summaryrefslogtreecommitdiff
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
parentb3f60112edcb85b459e60f66c44a55138b1cef49 (diff)
downloadgcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.gz
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.
-rw-r--r--Makefile.def8
-rw-r--r--Makefile.in3307
-rw-r--r--Makefile.tpl12
-rw-r--r--config/acx.m412
-rwxr-xr-xconfigure133
-rw-r--r--configure.ac31
-rw-r--r--gcc/Makefile.in4
-rwxr-xr-xgcc/configure107
-rw-r--r--gcc/configure.ac2
-rw-r--r--gcc/d/Make-lang.in132
-rw-r--r--gcc/d/config-lang.in2
-rw-r--r--gcc/d/d-attribs.cc1
-rw-r--r--gcc/d/d-builtins.cc52
-rw-r--r--gcc/d/d-codegen.cc78
-rw-r--r--gcc/d/d-compiler.cc49
-rw-r--r--gcc/d/d-convert.cc86
-rw-r--r--gcc/d/d-diagnostic.cc75
-rw-r--r--gcc/d/d-frontend.cc119
-rw-r--r--gcc/d/d-frontend.h37
-rw-r--r--gcc/d/d-incpath.cc5
-rw-r--r--gcc/d/d-lang.cc287
-rw-r--r--gcc/d/d-longdouble.cc6
-rw-r--r--gcc/d/d-system.h52
-rw-r--r--gcc/d/d-target.cc112
-rw-r--r--gcc/d/d-tree.h1
-rw-r--r--gcc/d/decl.cc166
-rw-r--r--gcc/d/dmd/MERGE2
-rw-r--r--gcc/d/dmd/README.md259
-rw-r--r--gcc/d/dmd/VERSION1
-rw-r--r--gcc/d/dmd/access.c560
-rw-r--r--gcc/d/dmd/access.d410
-rw-r--r--gcc/d/dmd/aggregate.d769
-rw-r--r--gcc/d/dmd/aggregate.h189
-rw-r--r--gcc/d/dmd/aliasthis.c94
-rw-r--r--gcc/d/dmd/aliasthis.d202
-rw-r--r--gcc/d/dmd/aliasthis.h10
-rw-r--r--gcc/d/dmd/apply.c149
-rw-r--r--gcc/d/dmd/apply.d189
-rw-r--r--gcc/d/dmd/arrayop.c634
-rw-r--r--gcc/d/dmd/arrayop.d387
-rw-r--r--gcc/d/dmd/arraytypes.d57
-rw-r--r--gcc/d/dmd/arraytypes.h9
-rw-r--r--gcc/d/dmd/ast_node.d26
-rw-r--r--gcc/d/dmd/astcodegen.d102
-rw-r--r--gcc/d/dmd/astenums.d391
-rw-r--r--gcc/d/dmd/attrib.c1320
-rw-r--r--gcc/d/dmd/attrib.d1518
-rw-r--r--gcc/d/dmd/attrib.h86
-rw-r--r--gcc/d/dmd/blockexit.c506
-rw-r--r--gcc/d/dmd/blockexit.d537
-rw-r--r--gcc/d/dmd/builtin.d33
-rw-r--r--gcc/d/dmd/canthrow.c316
-rw-r--r--gcc/d/dmd/canthrow.d244
-rw-r--r--gcc/d/dmd/chkformat.c985
-rw-r--r--gcc/d/dmd/chkformat.d1364
-rw-r--r--gcc/d/dmd/clone.c1179
-rw-r--r--gcc/d/dmd/clone.d1695
-rw-r--r--gcc/d/dmd/compiler.d57
-rw-r--r--gcc/d/dmd/compiler.h6
-rw-r--r--gcc/d/dmd/complex.d112
-rw-r--r--gcc/d/dmd/complex_t.h4
-rw-r--r--gcc/d/dmd/cond.c738
-rw-r--r--gcc/d/dmd/cond.d1004
-rw-r--r--gcc/d/dmd/cond.h31
-rw-r--r--gcc/d/dmd/constfold.c1922
-rw-r--r--gcc/d/dmd/constfold.d1825
-rw-r--r--gcc/d/dmd/cparse.d4249
-rw-r--r--gcc/d/dmd/cppmangle.c1168
-rw-r--r--gcc/d/dmd/cppmangle.d2540
-rw-r--r--gcc/d/dmd/ctfe.h222
-rw-r--r--gcc/d/dmd/ctfeexpr.c2127
-rw-r--r--gcc/d/dmd/ctfeexpr.d2096
-rw-r--r--gcc/d/dmd/ctorflow.d225
-rw-r--r--gcc/d/dmd/dcast.c3566
-rw-r--r--gcc/d/dmd/dcast.d3741
-rw-r--r--gcc/d/dmd/dclass.c1041
-rw-r--r--gcc/d/dmd/dclass.d1139
-rw-r--r--gcc/d/dmd/declaration.c1575
-rw-r--r--gcc/d/dmd/declaration.d2323
-rw-r--r--gcc/d/dmd/declaration.h582
-rw-r--r--gcc/d/dmd/delegatize.c208
-rw-r--r--gcc/d/dmd/delegatize.d305
-rw-r--r--gcc/d/dmd/denum.c388
-rw-r--r--gcc/d/dmd/denum.d333
-rw-r--r--gcc/d/dmd/dimport.c320
-rw-r--r--gcc/d/dmd/dimport.d358
-rw-r--r--gcc/d/dmd/dinterpret.c7017
-rw-r--r--gcc/d/dmd/dinterpret.d7487
-rw-r--r--gcc/d/dmd/dmacro.c458
-rw-r--r--gcc/d/dmd/dmacro.d435
-rw-r--r--gcc/d/dmd/dmangle.c1122
-rw-r--r--gcc/d/dmd/dmangle.d1297
-rw-r--r--gcc/d/dmd/dmodule.c1276
-rw-r--r--gcc/d/dmd/dmodule.d1608
-rw-r--r--gcc/d/dmd/doc.c2807
-rw-r--r--gcc/d/dmd/doc.d5388
-rw-r--r--gcc/d/dmd/doc.h6
-rw-r--r--gcc/d/dmd/dscope.c646
-rw-r--r--gcc/d/dmd/dscope.d768
-rw-r--r--gcc/d/dmd/dstruct.c1303
-rw-r--r--gcc/d/dmd/dstruct.d610
-rw-r--r--gcc/d/dmd/dsymbol.c1803
-rw-r--r--gcc/d/dmd/dsymbol.d2386
-rw-r--r--gcc/d/dmd/dsymbol.h142
-rw-r--r--gcc/d/dmd/dsymbolsem.c5620
-rw-r--r--gcc/d/dmd/dsymbolsem.d6654
-rw-r--r--gcc/d/dmd/dtemplate.c7581
-rw-r--r--gcc/d/dmd/dtemplate.d8415
-rw-r--r--gcc/d/dmd/dtoh.d3225
-rw-r--r--gcc/d/dmd/dversion.c187
-rw-r--r--gcc/d/dmd/dversion.d215
-rw-r--r--gcc/d/dmd/entity.c2390
-rw-r--r--gcc/d/dmd/entity.d2395
-rw-r--r--gcc/d/dmd/enum.h23
-rw-r--r--gcc/d/dmd/errors.d446
-rw-r--r--gcc/d/dmd/errors.h8
-rw-r--r--gcc/d/dmd/escape.c1234
-rw-r--r--gcc/d/dmd/escape.d2290
-rw-r--r--gcc/d/dmd/expression.c5706
-rw-r--r--gcc/d/dmd/expression.d6985
-rw-r--r--gcc/d/dmd/expression.h579
-rw-r--r--gcc/d/dmd/expressionsem.c10740
-rw-r--r--gcc/d/dmd/expressionsem.d13058
-rw-r--r--gcc/d/dmd/foreachvar.d323
-rw-r--r--gcc/d/dmd/func.c3161
-rw-r--r--gcc/d/dmd/func.d4102
-rw-r--r--gcc/d/dmd/globals.d640
-rw-r--r--gcc/d/dmd/globals.h255
-rw-r--r--gcc/d/dmd/gluelayer.d90
-rw-r--r--gcc/d/dmd/hdrgen.c3591
-rw-r--r--gcc/d/dmd/hdrgen.d3956
-rw-r--r--gcc/d/dmd/hdrgen.h43
-rw-r--r--gcc/d/dmd/iasm.c44
-rw-r--r--gcc/d/dmd/iasm.d59
-rw-r--r--gcc/d/dmd/iasmgcc.c379
-rw-r--r--gcc/d/dmd/iasmgcc.d537
-rw-r--r--gcc/d/dmd/id.d568
-rw-r--r--gcc/d/dmd/id.h16
-rw-r--r--gcc/d/dmd/identifier.c188
-rw-r--r--gcc/d/dmd/identifier.d362
-rw-r--r--gcc/d/dmd/identifier.h32
-rw-r--r--gcc/d/dmd/idgen.c560
-rw-r--r--gcc/d/dmd/impcnvgen.c598
-rw-r--r--gcc/d/dmd/impcnvtab.d379
-rw-r--r--gcc/d/dmd/imphint.c52
-rw-r--r--gcc/d/dmd/imphint.d91
-rw-r--r--gcc/d/dmd/import.h13
-rw-r--r--gcc/d/dmd/init.c282
-rw-r--r--gcc/d/dmd/init.d332
-rw-r--r--gcc/d/dmd/init.h69
-rw-r--r--gcc/d/dmd/initsem.c914
-rw-r--r--gcc/d/dmd/initsem.d1268
-rw-r--r--gcc/d/dmd/inline.d30
-rw-r--r--gcc/d/dmd/intrange.c839
-rw-r--r--gcc/d/dmd/intrange.d919
-rw-r--r--gcc/d/dmd/json.c888
-rw-r--r--gcc/d/dmd/json.d1085
-rw-r--r--gcc/d/dmd/json.h2
-rw-r--r--gcc/d/dmd/lambdacomp.d495
-rw-r--r--gcc/d/dmd/lexer.c2405
-rw-r--r--gcc/d/dmd/lexer.d3273
-rw-r--r--gcc/d/dmd/mangle.h6
-rw-r--r--gcc/d/dmd/module.h59
-rw-r--r--gcc/d/dmd/mtype.c8722
-rw-r--r--gcc/d/dmd/mtype.d7355
-rw-r--r--gcc/d/dmd/mtype.h444
-rw-r--r--gcc/d/dmd/nogc.c241
-rw-r--r--gcc/d/dmd/nogc.d266
-rw-r--r--gcc/d/dmd/nspace.c164
-rw-r--r--gcc/d/dmd/nspace.d170
-rw-r--r--gcc/d/dmd/nspace.h10
-rw-r--r--gcc/d/dmd/ob.d2680
-rw-r--r--gcc/d/dmd/objc.c84
-rw-r--r--gcc/d/dmd/objc.d953
-rw-r--r--gcc/d/dmd/objc.h55
-rw-r--r--gcc/d/dmd/opover.c1960
-rw-r--r--gcc/d/dmd/opover.d1843
-rw-r--r--gcc/d/dmd/optimize.c1230
-rw-r--r--gcc/d/dmd/optimize.d1186
-rw-r--r--gcc/d/dmd/parse.c8492
-rw-r--r--gcc/d/dmd/parse.d9365
-rw-r--r--gcc/d/dmd/parsetimevisitor.d297
-rw-r--r--gcc/d/dmd/permissivevisitor.d28
-rw-r--r--gcc/d/dmd/printast.d173
-rw-r--r--gcc/d/dmd/readme.txt13
-rw-r--r--gcc/d/dmd/res/default_ddoc_theme.ddoc825
-rw-r--r--gcc/d/dmd/root/README.md23
-rw-r--r--gcc/d/dmd/root/aav.c171
-rw-r--r--gcc/d/dmd/root/aav.d339
-rw-r--r--gcc/d/dmd/root/array.d1121
-rw-r--r--gcc/d/dmd/root/array.h52
-rw-r--r--gcc/d/dmd/root/bitarray.d192
-rw-r--r--gcc/d/dmd/root/bitarray.h4
-rw-r--r--gcc/d/dmd/root/checkedint.c238
-rw-r--r--gcc/d/dmd/root/ctfloat.d63
-rw-r--r--gcc/d/dmd/root/ctfloat.h6
-rw-r--r--gcc/d/dmd/root/dcompat.h12
-rw-r--r--gcc/d/dmd/root/file.c258
-rw-r--r--gcc/d/dmd/root/file.d814
-rw-r--r--gcc/d/dmd/root/file.h54
-rw-r--r--gcc/d/dmd/root/filename.c671
-rw-r--r--gcc/d/dmd/root/filename.d1273
-rw-r--r--gcc/d/dmd/root/filename.h15
-rw-r--r--gcc/d/dmd/root/hash.d83
-rw-r--r--gcc/d/dmd/root/longdouble.d140
-rw-r--r--gcc/d/dmd/root/object.h27
-rw-r--r--gcc/d/dmd/root/outbuffer.c417
-rw-r--r--gcc/d/dmd/root/outbuffer.d720
-rw-r--r--gcc/d/dmd/root/outbuffer.h31
-rw-r--r--gcc/d/dmd/root/port.d49
-rw-r--r--gcc/d/dmd/root/port.h11
-rw-r--r--gcc/d/dmd/root/region.d161
-rw-r--r--gcc/d/dmd/root/rmem.c191
-rw-r--r--gcc/d/dmd/root/rmem.d375
-rw-r--r--gcc/d/dmd/root/rmem.h17
-rw-r--r--gcc/d/dmd/root/root.h1
-rw-r--r--gcc/d/dmd/root/rootobject.c48
-rw-r--r--gcc/d/dmd/root/rootobject.d67
-rw-r--r--gcc/d/dmd/root/speller.c231
-rw-r--r--gcc/d/dmd/root/speller.d303
-rw-r--r--gcc/d/dmd/root/string.d293
-rw-r--r--gcc/d/dmd/root/stringtable.c196
-rw-r--r--gcc/d/dmd/root/stringtable.d411
-rw-r--r--gcc/d/dmd/safe.c168
-rw-r--r--gcc/d/dmd/safe.d228
-rw-r--r--gcc/d/dmd/sapply.c155
-rw-r--r--gcc/d/dmd/sapply.d180
-rw-r--r--gcc/d/dmd/scope.h122
-rw-r--r--gcc/d/dmd/semantic2.c430
-rw-r--r--gcc/d/dmd/semantic2.d774
-rw-r--r--gcc/d/dmd/semantic3.c1399
-rw-r--r--gcc/d/dmd/semantic3.d1624
-rw-r--r--gcc/d/dmd/sideeffect.c432
-rw-r--r--gcc/d/dmd/sideeffect.d418
-rw-r--r--gcc/d/dmd/statement.c1793
-rw-r--r--gcc/d/dmd/statement.d2053
-rw-r--r--gcc/d/dmd/statement.h354
-rw-r--r--gcc/d/dmd/statement_rewrite_walker.d194
-rw-r--r--gcc/d/dmd/statementsem.c3875
-rw-r--r--gcc/d/dmd/statementsem.d4995
-rw-r--r--gcc/d/dmd/staticassert.c55
-rw-r--r--gcc/d/dmd/staticassert.d66
-rw-r--r--gcc/d/dmd/staticassert.h6
-rw-r--r--gcc/d/dmd/staticcond.c96
-rw-r--r--gcc/d/dmd/staticcond.d424
-rw-r--r--gcc/d/dmd/stmtstate.d142
-rw-r--r--gcc/d/dmd/target.d438
-rw-r--r--gcc/d/dmd/target.h118
-rw-r--r--gcc/d/dmd/template.h146
-rw-r--r--gcc/d/dmd/templateparamsem.c116
-rw-r--r--gcc/d/dmd/templateparamsem.d190
-rw-r--r--gcc/d/dmd/tokens.c476
-rw-r--r--gcc/d/dmd/tokens.d1022
-rw-r--r--gcc/d/dmd/tokens.h69
-rw-r--r--gcc/d/dmd/traits.c1973
-rw-r--r--gcc/d/dmd/traits.d2202
-rw-r--r--gcc/d/dmd/transitivevisitor.d1207
-rw-r--r--gcc/d/dmd/typesem.c1462
-rw-r--r--gcc/d/dmd/typesem.d4896
-rw-r--r--gcc/d/dmd/typinf.d28
-rw-r--r--gcc/d/dmd/utf.c306
-rw-r--r--gcc/d/dmd/utf.d561
-rw-r--r--gcc/d/dmd/utils.c123
-rw-r--r--gcc/d/dmd/utils.d298
-rw-r--r--gcc/d/dmd/version.h18
-rw-r--r--gcc/d/dmd/visitor.d254
-rw-r--r--gcc/d/dmd/visitor.h481
-rw-r--r--gcc/d/expr.cc267
-rw-r--r--gcc/d/imports.cc8
-rw-r--r--gcc/d/intrinsics.cc10
-rw-r--r--gcc/d/intrinsics.def97
-rw-r--r--gcc/d/lang.opt165
-rw-r--r--gcc/d/modules.cc22
-rw-r--r--gcc/d/runtime.def30
-rw-r--r--gcc/d/toir.cc101
-rw-r--r--gcc/d/typeinfo.cc60
-rw-r--r--gcc/d/types.cc74
-rw-r--r--gcc/d/verstr.h1
-rw-r--r--gcc/po/EXCLUDES43
-rw-r--r--gcc/testsuite/gdc.dg/Wcastresult2.d2
-rw-r--r--gcc/testsuite/gdc.dg/asm1.d18
-rw-r--r--gcc/testsuite/gdc.dg/asm2.d2
-rw-r--r--gcc/testsuite/gdc.dg/asm3.d10
-rw-r--r--gcc/testsuite/gdc.dg/gdc282.d6
-rw-r--r--gcc/testsuite/gdc.dg/imports/gdc170.d8
-rw-r--r--gcc/testsuite/gdc.dg/intrinsics.d36
-rw-r--r--gcc/testsuite/gdc.dg/pr101672.d2
-rw-r--r--gcc/testsuite/gdc.dg/pr90650a.d2
-rw-r--r--gcc/testsuite/gdc.dg/pr90650b.d2
-rw-r--r--gcc/testsuite/gdc.dg/pr94777a.d2
-rw-r--r--gcc/testsuite/gdc.dg/pr94777c.d62
-rw-r--r--gcc/testsuite/gdc.dg/pr95250.d2
-rw-r--r--gcc/testsuite/gdc.dg/pr96156b.d17
-rw-r--r--gcc/testsuite/gdc.dg/pr96157c.d40
-rw-r--r--gcc/testsuite/gdc.dg/pr96869.d26
-rw-r--r--gcc/testsuite/gdc.dg/pr98277.d2
-rw-r--r--gcc/testsuite/gdc.dg/pr98457.d6
-rw-r--r--gcc/testsuite/gdc.dg/simd1.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2a.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2b.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2c.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2d.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2e.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2f.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2g.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2h.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2i.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd2j.d8
-rw-r--r--gcc/testsuite/gdc.dg/simd7951.d1
-rw-r--r--gcc/testsuite/gdc.dg/simd_ctfe.d87
-rw-r--r--gcc/testsuite/gdc.dg/torture/gdc309.d1
-rw-r--r--gcc/testsuite/gdc.dg/torture/pr94424.d16
-rw-r--r--gcc/testsuite/gdc.dg/torture/pr94777b.d135
-rw-r--r--gcc/testsuite/gdc.dg/torture/simd17344.d11
-rw-r--r--gcc/testsuite/gdc.dg/torture/simd20052.d17
-rw-r--r--gcc/testsuite/gdc.dg/torture/simd6.d26
-rw-r--r--gcc/testsuite/gdc.dg/torture/simd7.d18
-rw-r--r--gcc/testsuite/gdc.test/compilable/a3682.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/aliasassign.d41
-rw-r--r--gcc/testsuite/gdc.test/compilable/aliasdecl.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/art4769.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/b1215.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/b12504.d44
-rw-r--r--gcc/testsuite/gdc.test/compilable/b15206.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/b16360.d39
-rw-r--r--gcc/testsuite/gdc.test/compilable/b16697.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/b16967.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/b17111.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/b17651.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/b18197.d17
-rw-r--r--gcc/testsuite/gdc.test/compilable/b18242.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/b18489.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/b19432.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/b19442.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/b19775.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/b19829.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/b20045.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/b20067.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/b20758.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/b20780.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/b20833.d20
-rw-r--r--gcc/testsuite/gdc.test/compilable/b20885.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/b20938.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/b21285.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/b33.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/b6227.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/b6395.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/b6400.d37
-rw-r--r--gcc/testsuite/gdc.test/compilable/betterc.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/bug21196.d31
-rw-r--r--gcc/testsuite/gdc.test/compilable/callconv.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/ccompile.d36
-rw-r--r--gcc/testsuite/gdc.test/compilable/cdcmp.d148
-rw-r--r--gcc/testsuite/gdc.test/compilable/chkformat.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/commontype.d486
-rw-r--r--gcc/testsuite/gdc.test/compilable/compile1.d243
-rw-r--r--gcc/testsuite/gdc.test/compilable/cpp_abi_tag_unused.d21
-rw-r--r--gcc/testsuite/gdc.test/compilable/cppmangle.d958
-rw-r--r--gcc/testsuite/gdc.test/compilable/cppmangle2.d21
-rw-r--r--gcc/testsuite/gdc.test/compilable/cppmangle3.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/cppmangle_abitag.d106
-rw-r--r--gcc/testsuite/gdc.test/compilable/ctfe_math.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc1.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc10.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc10236.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc10236b.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc10325.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc10334.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc10366.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc10367.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc10869.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc10870.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc11.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc11479.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc11511.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc11823.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc12.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc12706.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc12745.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc13.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc13270.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc13645.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc14.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc14383.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc14413.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc14778.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc15475.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc17697.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc18361.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc198.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc19814.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc2.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc2273.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc4.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc4162.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc5.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc5446.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc6.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc648.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc6491.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc7.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc7555.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc7656.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc7715.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc7795.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc8.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc8271.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc8739.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9037.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9155.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9305.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9369.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9475.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9497a.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9497b.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9497c.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9497d.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9676a.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9676b.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9727.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9789.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc9903.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_breaks.d30
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_breaks_verbose.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_code.d46
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_code_verbose.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_emphasis.d45
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_emphasis_verbose.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_escapes.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_headings.d40
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_headings_verbose.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_links.d42
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_links_verbose.d17
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_lists.d68
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_lists_verbose.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_quote.d53
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_quote_verbose.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables.d42
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables_verbose.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddocbackticks.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ddocunittest.d50
-rw-r--r--gcc/testsuite/gdc.test/compilable/debugInference.d55
-rw-r--r--gcc/testsuite/gdc.test/compilable/defa.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/depmsg.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/depsOutput9948.d12
-rw-r--r--gcc/testsuite/gdc.test/compilable/dip22.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/dip22d.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/disable_new.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_21217.d91
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_AliasDeclaration.d217
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_AliasDeclaration_98.d56
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_AnonDeclaration.d106
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_CPPNamespaceDeclaration.d67
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_ClassDeclaration.d347
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_StructDeclaration.d286
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_TemplateDeclaration.d401
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_UnionDeclaration.d93
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_VarDeclaration.d116
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_cpp98_compat.d142
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_enum.d271
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_enum_cpp98.d244
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_expressions.d127
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_extern_type.d174
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_forwarding.d265
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_functions.d276
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_ignored.d147
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_invalid_identifiers.d170
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_names.d260
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_protection.d218
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_required_symbols.d225
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_special_enum.d90
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_unittest_block.d52
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtoh_verbose.d172
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtorfields.d52
-rw-r--r--gcc/testsuite/gdc.test/compilable/dtorfields_deprecation.d49
-rw-r--r--gcc/testsuite/gdc.test/compilable/extra-files/c6395.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/extra-files/depsOutput9948a.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/extra-files/dtoh_imports.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/extra-files/dtoh_imports2.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/extra-files/emptymain.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/extra-files/header1.d78
-rw-r--r--gcc/testsuite/gdc.test/compilable/extra-files/header17125.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/extra-files/header18365.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/extra-files/header2.d65
-rw-r--r--gcc/testsuite/gdc.test/compilable/fail137.d26
-rw-r--r--gcc/testsuite/gdc.test/compilable/fieldwise.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/filefullpath_18911.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/fix13165.d12
-rw-r--r--gcc/testsuite/gdc.test/compilable/fix17145.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/fix17349.d40
-rw-r--r--gcc/testsuite/gdc.test/compilable/fix20416.d36
-rw-r--r--gcc/testsuite/gdc.test/compilable/fix21647.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/fix21684.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/fix22180.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/forward1.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/future.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/futurexf.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/fwdref21063.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/header18364.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/header18365.d34
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice11054.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice11300.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice11925.d38
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice13403.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice13819.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice1524.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice20044.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice20415.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice6538.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/ice854.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/implicitconv.d33
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/a12511.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/a18911.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/cstuff3.c6
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/g313.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/imp16088.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/imp21832.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/imp22122.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/impfieldwise.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/pkg11847/mod11847.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/pkg11847/package.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/protectionimp.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test11563std_traits.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test13582.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test16709a.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test16709b.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test16709c.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test16709d.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test17441foo/bar.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test17441foo/package.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test17541_2.d20
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test17541_3.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test18651/algorithm.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test18651/b.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test18651/c.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test18651/datetime.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test18771a.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test18771b.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test18771c.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test18771d.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19187.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19344.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19656a.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19656b.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19656c.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19657b.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19657c.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19657d.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19657e.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19657f.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19657g.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19746a.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19746b.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19746c.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19746d.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19750a.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19750b.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19750c.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test19750d.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test21227/..foo/a.txt1
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test21227/a..b.txt1
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test21227/a.txt1
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test21464a.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/test63a.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/testcontracts.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/testlambda1.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/testlambda2.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/imports/u20958.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/interpret3.d1503
-rw-r--r--gcc/testsuite/gdc.test/compilable/interpret4.d31
-rw-r--r--gcc/testsuite/gdc.test/compilable/isZeroInit.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/isreturnonstack.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue12520.d21
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue15478.d55
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue15795.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue15818.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue18097.d12
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue19925.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue20362.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue20599.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue20704.d29
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue20705.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue20995.d12
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue21328.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue21378.d33
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue21662.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue21726.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue21880.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue21882.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue21905.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/issue9884.d25
-rw-r--r--gcc/testsuite/gdc.test/compilable/json.d153
-rw-r--r--gcc/testsuite/gdc.test/compilable/json20742.d69
-rw-r--r--gcc/testsuite/gdc.test/compilable/minimal.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/minimal2.d31
-rw-r--r--gcc/testsuite/gdc.test/compilable/mixin.d38
-rw-r--r--gcc/testsuite/gdc.test/compilable/mixinTemplateMangling.d35
-rw-r--r--gcc/testsuite/gdc.test/compilable/mixintempl.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/nestedtempl0.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/nestedtempl1.d25
-rw-r--r--gcc/testsuite/gdc.test/compilable/nogc.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/noreturn1.d49
-rw-r--r--gcc/testsuite/gdc.test/compilable/ob1.d149
-rw-r--r--gcc/testsuite/gdc.test/compilable/pr9374.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/pr9383.d25
-rw-r--r--gcc/testsuite/gdc.test/compilable/previewall.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/previewin.d116
-rw-r--r--gcc/testsuite/gdc.test/compilable/protattr.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/protection.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/quadratic.d42
-rw-r--r--gcc/testsuite/gdc.test/compilable/readmodify_structclass.d30
-rw-r--r--gcc/testsuite/gdc.test/compilable/reinterpretctfe.d34
-rw-r--r--gcc/testsuite/gdc.test/compilable/riia_ctor.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/rvalueref.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/scope.d247
-rw-r--r--gcc/testsuite/gdc.test/compilable/scopeinfer.d30
-rw-r--r--gcc/testsuite/gdc.test/compilable/shared.d125
-rw-r--r--gcc/testsuite/gdc.test/compilable/sharedopt.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/shortened_methods.d33
-rw-r--r--gcc/testsuite/gdc.test/compilable/staticforeach.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/sw_transition_complex.d118
-rw-r--r--gcc/testsuite/gdc.test/compilable/sw_transition_field.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/sw_transition_tls.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test1.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test10312.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test10375.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test10520.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/test10752.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test10981.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test10993.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test11169.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test11225a.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test11237.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/test11259.d25
-rw-r--r--gcc/testsuite/gdc.test/compilable/test11371.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test11563.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test11656.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test1170.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test11847.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test1238.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test12496.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test12511.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test12558.d39
-rw-r--r--gcc/testsuite/gdc.test/compilable/test12567c.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/test12567d.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test12807.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/test13226.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test13242.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test13512.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/test13582a.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test13582b.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test13858.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test13953.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test14114.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test14275.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test14528.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test14666.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test14740.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test14831.d60
-rw-r--r--gcc/testsuite/gdc.test/compilable/test14929.d85
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15019.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15150.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15225.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15292.d20
-rw-r--r--gcc/testsuite/gdc.test/compilable/test1537.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15389_x.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15389_y.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test1547.d20
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15490.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15519_x.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15519_y.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15780.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15785.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15856.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test15907.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16002.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16013a.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16013b.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16037.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16085.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16088.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16107.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16183.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16214a.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16273.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16460.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16492.d87
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16570.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16578a.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16578b.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16621.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16635.d56
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16657.d17
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16685.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16709.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test16798.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17057.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17143.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17146.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17351.d17
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17373.d32
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17419.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17441.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17512.d26
-rw-r--r--gcc/testsuite/gdc.test/compilable/test1754.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17541.d28
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17548.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17752.d12
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17782.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17793.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17807.d17
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17853.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17906.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17942.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test17970.d28
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18000.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18020.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18030.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18099.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18115.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18199.d87
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18251.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18385b.d29
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18430.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18468.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18474.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18572.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18578.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18584.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18645.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18651a.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18670.d12
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18694.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18737.d32
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18771.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18775.d20
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18821.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18905.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18936.d31
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18951a.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18951b.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test18976.d32
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19014.d12
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19066.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19081.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19097.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19108.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19187.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19203.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19224.d18
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19227.d29
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19315.d20
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19409.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19464.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19491.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19499.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19519.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19540.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19557.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19609.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19631.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19652.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19656.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19657a.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19713.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19728.d52
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19731.d78
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19746.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19750.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19754.d45
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19804.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19809.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19833.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19840.d21
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19895.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19936.d17
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19954.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test19970.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20000.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20021.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20039.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20051.d18
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20065.d12
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20100.d50
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20136.d18
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20138.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20181.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20296.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20318.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20326.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20367.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20388.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20406.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20410.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20417.d12
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20420.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20488.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20596.d31
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20653.d20
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20656.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20661.d17
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20695.d35
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20710.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20744.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20789.d34
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20795.d35
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20821.d17
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20835.d59
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20842.d33
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20868.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20906.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20909.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20923.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20958.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test20990.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21050.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21058.d25
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21227.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21255.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21282.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21299a.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21299b.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21330.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21372.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21398.d31
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21464.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21514.d20
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21543.d116
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21591.d46
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21659.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21661.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21668.d7
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21680.d9
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21743.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21753.d21
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21802.d38
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21806.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21828.d27
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21830.d25
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21831.d20
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21832.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21861.d38
-rw-r--r--gcc/testsuite/gdc.test/compilable/test21876.d17
-rw-r--r--gcc/testsuite/gdc.test/compilable/test22122.d53
-rw-r--r--gcc/testsuite/gdc.test/compilable/test22226.d15
-rw-r--r--gcc/testsuite/gdc.test/compilable/test25.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test2991.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test3004.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/test313a.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test313c.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test313d.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/test313e.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test313f.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test313g.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/test314.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test3775.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test4003.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/test4375.d5
-rw-r--r--gcc/testsuite/gdc.test/compilable/test50.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test5227.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/test55.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test59.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test5973.d41
-rw-r--r--gcc/testsuite/gdc.test/compilable/test6013.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test602.d8
-rw-r--r--gcc/testsuite/gdc.test/compilable/test61.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test62.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test63.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test6395.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test6541.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test66.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/test67.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test6777.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/test68.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/test69.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test6999.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test70.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test71.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test7172.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test72.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test7399.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/test7491.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test7524.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test7754.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/test8509.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/test8543.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test8696.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/test8922a.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test8922b.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test8922c.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test8922d.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test8922e.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test8922f.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9209.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9274.d26
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9276.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9278a.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9278b.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test930.d22
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9434.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9435.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9436.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9613.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9672.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9692.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9701.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9818.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/test9919.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/testAliasLookup.d60
-rw-r--r--gcc/testsuite/gdc.test/compilable/testCpCtor.d21
-rw-r--r--gcc/testsuite/gdc.test/compilable/testDIP37a.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/testInference.d54
-rw-r--r--gcc/testsuite/gdc.test/compilable/testVRP.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/testcontracts.d44
-rw-r--r--gcc/testsuite/gdc.test/compilable/testcstuff3.d4
-rw-r--r--gcc/testsuite/gdc.test/compilable/testdip1008.d21
-rw-r--r--gcc/testsuite/gdc.test/compilable/testexpression.d26
-rw-r--r--gcc/testsuite/gdc.test/compilable/testfwdref.d44
-rw-r--r--gcc/testsuite/gdc.test/compilable/testheader1.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/testheader12567a.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/testheader12567b.d16
-rw-r--r--gcc/testsuite/gdc.test/compilable/testheader17125.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/testheader1i.d13
-rw-r--r--gcc/testsuite/gdc.test/compilable/testheader2.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/testheader2i.d11
-rw-r--r--gcc/testsuite/gdc.test/compilable/testheader3.d31
-rw-r--r--gcc/testsuite/gdc.test/compilable/testheaderudamodule.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/testimport12242.d2
-rw-r--r--gcc/testsuite/gdc.test/compilable/testlambdacomp.d216
-rw-r--r--gcc/testsuite/gdc.test/compilable/testparse.d49
-rw-r--r--gcc/testsuite/gdc.test/compilable/testsctreturn.d19
-rw-r--r--gcc/testsuite/gdc.test/compilable/testtempl2.d24
-rw-r--r--gcc/testsuite/gdc.test/compilable/traits.d190
-rw-r--r--gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d120
-rw-r--r--gcc/testsuite/gdc.test/compilable/typeid_name.d14
-rw-r--r--gcc/testsuite/gdc.test/compilable/uda.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/udamodule1.d3
-rw-r--r--gcc/testsuite/gdc.test/compilable/udamodule2.d1
-rw-r--r--gcc/testsuite/gdc.test/compilable/union_initialization.d43
-rw-r--r--gcc/testsuite/gdc.test/compilable/vcg-ast.d25
-rw-r--r--gcc/testsuite/gdc.test/compilable/version.d23
-rw-r--r--gcc/testsuite/gdc.test/compilable/vgc1.d41
-rw-r--r--gcc/testsuite/gdc.test/compilable/vgc2.d34
-rw-r--r--gcc/testsuite/gdc.test/compilable/vgc3.d6
-rw-r--r--gcc/testsuite/gdc.test/compilable/vtemplates.d28
-rw-r--r--gcc/testsuite/gdc.test/compilable/vtemplates_list.d46
-rw-r--r--gcc/testsuite/gdc.test/compilable/warn3882.d10
-rw-r--r--gcc/testsuite/gdc.test/compilable/zerosize.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/aacmp10381.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/aliasassign.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/aliasassign1.d34
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/already_defined.d76
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b15069.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b15909.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b17918.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b19523.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b19685.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b19691.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b19691e.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b19717a.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b19730.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b20011.d40
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b20780.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b20875.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b3841.d45
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/b6227.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/betterc.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/bug15613.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/bug16165.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/bug18743.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/bug19569.d90
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/bug8891.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/bug9631.d47
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ccast.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/checkimports2.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/checkimports2a.d32
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/checkimports2b.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/checkimports2c.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/circ10280.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/class1.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/class2.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/commaexp.d57
-rwxr-xr-xgcc/testsuite/gdc.test/fail_compilation/constraints_aggr.d45
-rwxr-xr-xgcc/testsuite/gdc.test/fail_compilation/constraints_defs.d56
-rwxr-xr-xgcc/testsuite/gdc.test/fail_compilation/constraints_func1.d93
-rwxr-xr-xgcc/testsuite/gdc.test/fail_compilation/constraints_func2.d108
-rwxr-xr-xgcc/testsuite/gdc.test/fail_compilation/constraints_func3.d60
-rwxr-xr-xgcc/testsuite/gdc.test/fail_compilation/constraints_func4.d97
-rwxr-xr-xgcc/testsuite/gdc.test/fail_compilation/constraints_tmpl.d44
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/cpp_abi_tag.d57
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/cpp_abi_tag2.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/cppeh1.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/cppmangle.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/cppmangle2.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ctfe10989.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ctfe10995.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ctfe11467.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ctfe13612.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ctfe14207.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ctfe14465.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/dassert.d43
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ddoc_18083.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/debugCaseDeclaration.d39
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/dep_d1_ops.d191
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/dephexstrings.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/depmsg.d40
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/depmsg15814.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/depmsg15815.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/deprecate1553.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/deprecated6760.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/deprecatedImports.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/deprecatedTemplates.d63
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/deprecateopdot.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/deprecations.d66
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10089.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10099.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10141.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10169.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10319.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10405.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10415.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10688.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10768.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10783.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10792.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10805.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10862.d68
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10926.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag10984.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag11078.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag11132.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag11425.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag11727.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag11756.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag11769.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12063.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12124.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12280.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12312.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12380.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12480.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12487.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12678.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12777.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag12829.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag13028.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag13215.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag13320.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag13333.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag13528.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag13609b.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag13787.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag13884.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag13942.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag14102.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag14145.d38
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag14163.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag14235.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag14818.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag14875.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag14876.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag15209.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag15411.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag15669.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag15713.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag15974.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag16499.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag16977.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag1730.d57
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag18460.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag18574.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag19022.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag19225.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag20059.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag20518.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag21883.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag2452.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag3013.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag3438.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag3672.d66
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag3672a.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag3869.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag3913.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag4479.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag4528.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag4596.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag5385.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag5450.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag6373.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag6539.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag6677.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag6699.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag6707.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag7050a.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag7050b.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag7050c.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag7420.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag7477.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag7747.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag7998.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8044.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8101.d40
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8101b.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8178.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8318.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8425.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8510.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8559.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8648.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8684.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8697.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8714.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8777.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8787.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8894.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag8928.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9004.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9148.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9191.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9210a.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9247.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9312.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9357.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9358.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9398.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9451.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9620.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9635.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9679.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9831.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9861.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9880.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag9961.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag_class_alloc.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag_cstyle.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag_debug_conditional.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag_err1.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d40
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag_template_alias.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diag_template_this.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diagin.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/diaginref.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/dip22a.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/dip22b.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/dip22e.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/dip25.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/disable_new.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/dtor_attributes.d190
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/dtorfields_attributes.d43
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/e15876_1.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/e15876_2.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/e15876_3.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/e15876_4.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/e15876_5.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/e15876_6.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/enum9921.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/extra-files/a14446.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail100.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10082.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail101.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10102.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10115.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10254.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10277.d42
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10299.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail104.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10481.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail105.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10528.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10534.d32
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail106.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10630.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10666.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10806.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail109.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10905.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10947.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10964.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10968.d42
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail10980.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail110.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11038.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail111.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11125.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11151.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11163.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail113.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11355.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11375.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail114.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11445.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11453b.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11503c.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11503d.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11510.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11532.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11542.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11545.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11552.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11562.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11591b.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail116.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail117.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11714.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11717.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11720.d33
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail11748.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail118.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail120.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail122.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12236.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12255.d36
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail123.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12378.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12390.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail124.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12436.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail125.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail126.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12622.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12636.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail127.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12744.d36
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12749.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12764.d26
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12809.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail129.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12901.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12908.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail12932.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13064.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail131.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13116.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13120.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13187.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail132.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13203.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail133.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13336a.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13336b.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail134.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13424.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13435.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13498.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13574.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail136.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13601.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13701.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13756.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail139.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail13902.d70
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14089.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail142.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14249.d28
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail143.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14304.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail144.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14406.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14407.d47
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14416.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14486.d100
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail145.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14554.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14669.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14965.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail14997.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail150.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15068.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15292.d28
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail153.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail154.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail155.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15535.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15550.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail156.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15616a.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15616b.d32
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15626.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15667.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15691.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail15755.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail158.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail159.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail160.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail16001.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail161.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail162.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail16206a.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail16206b.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail163.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail16600.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail16689.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail169.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail16997.d59
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail170.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail172.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17275.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17354.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17491.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17492.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17502.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17518.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17570.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail176.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17602.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17612.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17625.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17646.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail177.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17722a.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17722b.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17842.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail179.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17927.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17955.d102
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17969.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail17976.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail180.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18057.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18093.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18228.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18236.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18243.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail183.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18417.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail185.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18620.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail187.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18719.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail188.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18892.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18938.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18970.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18979.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18985.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail18994.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail190.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail1900.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19038.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19076.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19098.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19099.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19103.d36
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19107.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19181.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail192.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19202.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19209.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail193.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19319a.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19319b.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail194.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19441.d49
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19447.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail195.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19609.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19687.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19729.d37
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19744.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19757_m32.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19757_m64.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail198.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19871.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19881.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19890a.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19890b.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19897.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19898a.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19898b.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19911b.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19911c.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19912a.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19912b.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19912c.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19912d.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19912e.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19913.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19914.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19915.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19917.d49
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19919.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19922.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19923.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19931.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail1995.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19955.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail19965.d37
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20000.d39
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20033.d54
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20040.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20073.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20084.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20108.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20163.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20164.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20183.d47
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20376.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20448.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20461.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20538.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20547.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20551.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20609.d45
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20637.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20638.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20658.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20691.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail207.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20714.d32
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20730a.d39
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20730b.d46
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20771.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20772.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20775.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20779.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail208.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20800.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail209.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail20965.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21091a.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21091b.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21092.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail212.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21275.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail213.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail215.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21508.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21508_2.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21547.d34
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail216.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail218.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21830.d34
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21831.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21832.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21849.d36
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21868b.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21885.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21928.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail21928b.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail2195.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail22035.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail22054.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail22075.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail22084.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail221.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail22118.d36
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail22121.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail22121/imports/test22121/package.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail22138.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail22157.d34
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail222.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail223.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail224.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail229.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail23.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail231.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail233.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail235.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail236.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail2361.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail237.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail238_m32.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail238_m64.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail24.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail240.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail241.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail243.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail244.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail245.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail2450.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail2456.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail246.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail247.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail248.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail249.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail25.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail250.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail251.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail253.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail256.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail259.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail261.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail262.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail263.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail265.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail267.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail270.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail272.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail273.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail275.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail278.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail2789.d109
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail282.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail284.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail288.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail291.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail296.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail2962.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail297.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail299.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail3.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail301.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail302.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail303.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail305.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail309.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail310.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail311.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail312.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail313.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail314.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail3144.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail315.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail317.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail318.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail319.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail320.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail324.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail325.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail328.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail329.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail330.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail331.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail332.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail333.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail336.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail337.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail34.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail340.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail341.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail343.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail347.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail349.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail35.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail351.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail354.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail355.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail36.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail3672.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail3703.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail3753.d48
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail37_m32.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail37_m64.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail38.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail3882.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail39.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail3990.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail40.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4082.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail41.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail42.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375a.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375b.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375c.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375d.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375e.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375f.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375g.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375h.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375i.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375j.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375k.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375l.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375m.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375o.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375r.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375s.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375t.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375u.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375v.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375w.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375x.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4375y.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail44.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4421.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4448.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail45.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4517.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4544.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail46.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4611.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail4923.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail50.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail51.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail5153.d28
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail52.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail54.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail59.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail61.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail6107.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail62.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail6242.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail63.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail6334.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail6453.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail66.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail6652.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail6795.d37
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail6889.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail7173.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail73.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail7352.d52
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail74.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail7443.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail75.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail76.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail7848.d43
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail7862.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail79.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail7903.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail8009.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail809.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail80_m32.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail80_m64.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail8217.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail8262.d33
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail8373.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail86.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail8631.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail8724.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9063.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9081.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail91.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9199.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail92.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9290.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail93.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9346.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9368.d49
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail94.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9413.d42
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9414a.d42
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9414b.d42
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9414c.d42
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9414d.d42
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail95.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9537.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9562.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail96.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9613.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9665a.d77
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9665b.d26
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail97.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9710.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9766.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9773.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9790.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail98.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9891.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9892.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail9936.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/failCopyCtor.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/failCopyCtor2.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_arrayop1.d53
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_arrayop2.d163
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3a.d28
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3b.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3c.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_casting.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_casting2.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_circular.d64
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_circular2.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_contracts3.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_opover.d28
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_pretty_errors.d36
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fail_scope.d32
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/failattr.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/faildeleteaa.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/faildottypeinfo.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/failmemalloc.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/failob1.d34
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/failob2.d67
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/failoffset.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fix17349.d36
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fix17635.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fix17751.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fix18575.d41
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fix19018.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fix19059.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fix19246.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fix5212.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fob1.d63
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/fob2.d178
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/gag4269f.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/goto1.d26
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/goto2.d143
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/goto3.d37
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10016.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10076.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10212.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10259.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10341.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10419.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10600.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10616.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10624.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10651.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10713.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10727a.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10727b.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10922.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10938.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice10949.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11086.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11404.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice1144.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11472.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11513a.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11513b.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11518.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11552.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11553.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11755.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11790.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11793.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11822.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11849b.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11850.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11856_0.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11856_1.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11919.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11922.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11925.d45
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice11944.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12040.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12158.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12174.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12235.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12350.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12362.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12534.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12539.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12574.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12727.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12827.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12841.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice12902.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13024.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13027.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13081.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13131.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13220.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13221.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13225.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13311.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13356.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13382.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13385.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13459.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13465a.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13465b.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice1358.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13788.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13816.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13835.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13921.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice13987.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14055.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14096.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14116.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14130.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14146.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14177.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14272.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14424.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14446.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14621.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14642.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14844.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14907.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice14929.d96
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice15092.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice15172.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice15332.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice15441.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice15688.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice15788.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice15816.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice15922.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice16035.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice16657.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice17074.d26
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice17831.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice18469.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice18753.d39
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice18803a.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice18803b.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice19295.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice19755.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice19762.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice19887.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice19950.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice20042.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice20056.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice20057.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice20264.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice20545.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice20709.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice21095.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice2843.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice4094.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice4983.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice6538.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice7645.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice8100.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice8255.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice8309.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice8711.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice8795.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9273a.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9273b.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9284.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9338.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9439.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9494.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9545.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9759.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9806.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/ice9865.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/impconv.d40
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imphint.d90
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/a14407.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/a17625.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/b17625.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/b17918a.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/b19762.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/c19762.d27
-rwxr-xr-xgcc/testsuite/gdc.test/fail_compilation/imports/constraints.d73
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/deprecatedImporta.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/deprecatedImportb.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/diag20518a.d0
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/diag20518a/b.d0
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/diag9210stdcomplex.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/fail20164.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/fail20637b.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/fail20638b.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/fail21275a.d34
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/i20057.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/imp17602.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/imp18554.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/imp18979.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/imp19661.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/imp20709.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/imp21832.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/import21508.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/issue21685.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/test18480a.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/test18480b.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/test18938a/cache.d0
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/test18938a/file.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/test18938b/file.d28
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/test19107a.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/test19107b.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/test20267.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/imports/test21246.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue15103.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue20422.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue20627.d67
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue20704.d39
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue21203.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue21295.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue21378.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue21685_main.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue21936.d32
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/issue3827.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/lexer1.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/lexer2.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/lexer3.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/lexer4.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/lookup.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/mangle1.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/mangle2.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/misc1.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d51
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/mixin.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/mixin_gc.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/nestedtempl0.d35
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/nestedtempl1.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/nestedtempl2.d38
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/nestedtempl3.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/no_Throwable.d26
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/no_TypeInfo.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/nogc1.d44
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/nogc2.d35
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/nogc3.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/noreturn.d118
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/notype.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/objc_class2.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/objc_class3.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/objc_non_objc_base.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/parse12967a.d32
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/parse12967b.d50
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/parse19277.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/parseStc2.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/parseStc3.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/parseStc4.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/parseStc5.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/pragmainline.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/pragmas.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/previewin.d42
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/protattr1.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/protattr2.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/protattr3.d1
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/pull12941.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/reserved_version.d206
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/retref2.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/retscope.d87
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/retscope2.d51
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/retscope3.d130
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/retscope4.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/retscope5.d26
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/retscope6.d233
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/scope_class.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/scope_type.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/shared.d227
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/skip.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/spell9644.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/staticarrayoverflow.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/staticforeach4.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/switches.d35
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test10.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test1021.d171
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test11006.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test11047.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test11176.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test12228.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test12385.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test12558.d57
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test12822.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test13152.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test13536.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test13537.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test13667.d101
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test13786.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test13867.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test14064.d15
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test14238.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test143.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test14496.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test14538.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15117.d30
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15177.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15191.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15306.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15373.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15399.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15544.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15660.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15672.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15703.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15704.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15785.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15785b.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15897.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15925.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test15989.d7
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16095.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16116.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16193.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16195.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16228.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16284.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16365.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16381.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16523.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16589.d26
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test16694.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17284.d20
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17380spec.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17422.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17423.d29
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17450.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17451.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17586.d14
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17868.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17868b.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17892.d32
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test17959.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18130.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18282.d89
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18385.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18385b.d47
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18480.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18484.d26
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18554.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18597.d27
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18607.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18644.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18708.d64
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test18736.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test19097.d56
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test19107.d25
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test19112.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test19176.d26
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test19193.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test19473.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test19646.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test19661.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test19971.d17
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20096.d28
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20149.d34
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20245.d43
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20267.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20324.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20383.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20515.d18
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20549.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20565.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20569.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20610.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20626.d22
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20696.d21
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test20719.d32
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21096.d13
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21198.d24
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21204.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21246.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21259.d52
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21319.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21518.d38
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21665.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21807.d54
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test21912.d54
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test22048.d10
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test22227.d16
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test314.d2
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test4838.d12
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test4946.d19
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test64.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test8556.d9
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test9150.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/test9176.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/testCols.d3
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/testInference.d33
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/testpull1810.d4
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/testscopestatic.d8
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/trait_loc_err.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/traits.d91
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/traits_child.d6
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/typeerrors.d31
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/union_initialization.d88
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/vararg2.d23
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/varargsstc.d11
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/verifyhookexist.d45
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/warn13679.d5
-rw-r--r--gcc/testsuite/gdc.test/fail_compilation/warn7444.d3
-rw-r--r--gcc/testsuite/gdc.test/runnable/A16.d11
-rw-r--r--gcc/testsuite/gdc.test/runnable/Same.d11
-rw-r--r--gcc/testsuite/gdc.test/runnable/a17.d10
-rw-r--r--gcc/testsuite/gdc.test/runnable/a18.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/a21.d14
-rw-r--r--gcc/testsuite/gdc.test/runnable/aliasassign.d31
-rw-r--r--gcc/testsuite/gdc.test/runnable/aliasthis.d222
-rw-r--r--gcc/testsuite/gdc.test/runnable/arrayop.d96
-rw-r--r--gcc/testsuite/gdc.test/runnable/auto1.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/b10562.d93
-rw-r--r--gcc/testsuite/gdc.test/runnable/b16360.d50
-rw-r--r--gcc/testsuite/gdc.test/runnable/b18034.d28
-rw-r--r--gcc/testsuite/gdc.test/runnable/b19584.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/b20470.d97
-rw-r--r--gcc/testsuite/gdc.test/runnable/b20890.d48
-rw-r--r--gcc/testsuite/gdc.test/runnable/b26.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/b6400.d69
-rw-r--r--gcc/testsuite/gdc.test/runnable/bench1.d14
-rw-r--r--gcc/testsuite/gdc.test/runnable/betterc.d202
-rw-r--r--gcc/testsuite/gdc.test/runnable/bettercUnittest.d38
-rw-r--r--gcc/testsuite/gdc.test/runnable/bitops.d58
-rw-r--r--gcc/testsuite/gdc.test/runnable/bug11155.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/bug19652.d22
-rw-r--r--gcc/testsuite/gdc.test/runnable/bug7068.d3
-rw-r--r--gcc/testsuite/gdc.test/runnable/builtin.d39
-rw-r--r--gcc/testsuite/gdc.test/runnable/cassert.d17
-rw-r--r--gcc/testsuite/gdc.test/runnable/casting.d53
-rw-r--r--gcc/testsuite/gdc.test/runnable/circular.d25
-rw-r--r--gcc/testsuite/gdc.test/runnable/closure.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/complex.d1180
-rw-r--r--gcc/testsuite/gdc.test/runnable/constfold.d96
-rw-r--r--gcc/testsuite/gdc.test/runnable/cppdtor.d143
-rw-r--r--gcc/testsuite/gdc.test/runnable/ctorpowtests.d72
-rw-r--r--gcc/testsuite/gdc.test/runnable/declaration.d28
-rw-r--r--gcc/testsuite/gdc.test/runnable/delegate.d45
-rw-r--r--gcc/testsuite/gdc.test/runnable/dhry.d931
-rw-r--r--gcc/testsuite/gdc.test/runnable/eh.d188
-rw-r--r--gcc/testsuite/gdc.test/runnable/entity1.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/evalorder.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/extra-files/lib13742a.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/extra-files/lib13742b.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/extra-files/minimal/object.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/extra-files/moreBettercUnittests.d11
-rw-r--r--gcc/testsuite/gdc.test/runnable/extra-files/test13742.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/extra-files/teststdio.txt6
-rw-r--r--gcc/testsuite/gdc.test/runnable/fix20466.d40
-rw-r--r--gcc/testsuite/gdc.test/runnable/fix22115.d70
-rw-r--r--gcc/testsuite/gdc.test/runnable/foreach.d31
-rw-r--r--gcc/testsuite/gdc.test/runnable/foreach2.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/foreach3.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/foreach4.d45
-rw-r--r--gcc/testsuite/gdc.test/runnable/foreach5.d128
-rw-r--r--gcc/testsuite/gdc.test/runnable/funclit.d95
-rw-r--r--gcc/testsuite/gdc.test/runnable/functype.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/hello.d4
-rw-r--r--gcc/testsuite/gdc.test/runnable/helloUTF8.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/ice15030.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/ice21696.d23
-rw-r--r--gcc/testsuite/gdc.test/runnable/ice21727.d46
-rw-r--r--gcc/testsuite/gdc.test/runnable/ifti.d53
-rw-r--r--gcc/testsuite/gdc.test/runnable/implicit.d35
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/A16a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/Other.d11
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/a12037.d37
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/a21a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/another_module_with_tests.d4
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/depsprot_default.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/depsprot_private.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/depsprot_public.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/module_with_tests.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/pubprivtmpla.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/std11file.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/std15017variant.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/template_ovs1.d9
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/template_ovs2.d9
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/template_ovs3.d5
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test11931a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test11931d.d19
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test13a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test15777a.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test15777b.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test17181a.d10
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test17181b.d3
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test17181c.d7
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test18868_a.d3
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test18868_fls.d33
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test19655b.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test19655c.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test19655d.d14
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test19655e.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test19655f.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test19655g.d3
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test24a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test24b.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test24c.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test27a.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test39a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test40a.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test45a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test45b.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/test49a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/imports/testmod2a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/inclusive_incontracts.d80
-rw-r--r--gcc/testsuite/gdc.test/runnable/inline.d71
-rw-r--r--gcc/testsuite/gdc.test/runnable/inner.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/interface.d9
-rw-r--r--gcc/testsuite/gdc.test/runnable/interface2.d28
-rw-r--r--gcc/testsuite/gdc.test/runnable/interface3.d10
-rw-r--r--gcc/testsuite/gdc.test/runnable/interpret.d151
-rw-r--r--gcc/testsuite/gdc.test/runnable/interpret2.d16
-rw-r--r--gcc/testsuite/gdc.test/runnable/issue16995.d42
-rw-r--r--gcc/testsuite/gdc.test/runnable/issue8671.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/lazy.d22
-rw-r--r--gcc/testsuite/gdc.test/runnable/lexer.d4
-rw-r--r--gcc/testsuite/gdc.test/runnable/link11069a.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/link11127.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/link12037.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/link12144.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/link13043.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/link13350.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/link13415.d14
-rw-r--r--gcc/testsuite/gdc.test/runnable/link14074a.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/link14074b.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/link14541.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/link14992.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/link15017.d7
-rw-r--r--gcc/testsuite/gdc.test/runnable/link6574.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/link7745.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/literal.d27
-rw-r--r--gcc/testsuite/gdc.test/runnable/loopunroll.d21
-rw-r--r--gcc/testsuite/gdc.test/runnable/m1.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/manboy.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/mangle.d48
-rw-r--r--gcc/testsuite/gdc.test/runnable/mars1.d1838
-rw-r--r--gcc/testsuite/gdc.test/runnable/minimal.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/minimal2.d46
-rw-r--r--gcc/testsuite/gdc.test/runnable/mixin1.d98
-rw-r--r--gcc/testsuite/gdc.test/runnable/mixin2.d30
-rw-r--r--gcc/testsuite/gdc.test/runnable/mod1.d10
-rw-r--r--gcc/testsuite/gdc.test/runnable/nan.d42
-rw-r--r--gcc/testsuite/gdc.test/runnable/nested.d121
-rw-r--r--gcc/testsuite/gdc.test/runnable/newdel.d70
-rw-r--r--gcc/testsuite/gdc.test/runnable/nogc.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/nulltype.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/opdisp.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/opover.d78
-rw-r--r--gcc/testsuite/gdc.test/runnable/opover2.d137
-rw-r--r--gcc/testsuite/gdc.test/runnable/opover3.d4
-rw-r--r--gcc/testsuite/gdc.test/runnable/overload.d101
-rw-r--r--gcc/testsuite/gdc.test/runnable/previewin.d189
-rw-r--r--gcc/testsuite/gdc.test/runnable/printargs.d4
-rw-r--r--gcc/testsuite/gdc.test/runnable/property.d4
-rw-r--r--gcc/testsuite/gdc.test/runnable/property2.d67
-rw-r--r--gcc/testsuite/gdc.test/runnable/pubprivtmpl.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/s2ir.d19
-rw-r--r--gcc/testsuite/gdc.test/runnable/sctor.d88
-rw-r--r--gcc/testsuite/gdc.test/runnable/sctor2.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/sdtor.d480
-rw-r--r--gcc/testsuite/gdc.test/runnable/statictor.d23
-rw-r--r--gcc/testsuite/gdc.test/runnable/stress.d194
-rw-r--r--gcc/testsuite/gdc.test/runnable/structlit.d86
-rw-r--r--gcc/testsuite/gdc.test/runnable/template1.d30
-rw-r--r--gcc/testsuite/gdc.test/runnable/template13478.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/template2.d40
-rw-r--r--gcc/testsuite/gdc.test/runnable/template4.d64
-rw-r--r--gcc/testsuite/gdc.test/runnable/template9.d526
-rw-r--r--gcc/testsuite/gdc.test/runnable/test10.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/test10378.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test10619.d38
-rw-r--r--gcc/testsuite/gdc.test/runnable/test10736.d11
-rw-r--r--gcc/testsuite/gdc.test/runnable/test10942.d16
-rw-r--r--gcc/testsuite/gdc.test/runnable/test11.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/test11447c.d3
-rw-r--r--gcc/testsuite/gdc.test/runnable/test11863.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test11934.d22
-rw-r--r--gcc/testsuite/gdc.test/runnable/test12.d70
-rw-r--r--gcc/testsuite/gdc.test/runnable/test12486.d18
-rw-r--r--gcc/testsuite/gdc.test/runnable/test14874.d76
-rw-r--r--gcc/testsuite/gdc.test/runnable/test15.d134
-rw-r--r--gcc/testsuite/gdc.test/runnable/test15079.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/test15373.d15
-rw-r--r--gcc/testsuite/gdc.test/runnable/test15568.d58
-rw-r--r--gcc/testsuite/gdc.test/runnable/test15624.d51
-rw-r--r--gcc/testsuite/gdc.test/runnable/test16047.d17
-rw-r--r--gcc/testsuite/gdc.test/runnable/test16115.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test16140.d32
-rw-r--r--gcc/testsuite/gdc.test/runnable/test16555.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17072.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17073.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17181.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17181b.d16
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17246.d50
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17258.d33
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17337.d23
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17338.d34
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17373.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17559.d84
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17684.d17
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17868.d45
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17868b.d52
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17885.d11
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17940.d46
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17943.d9
-rw-r--r--gcc/testsuite/gdc.test/runnable/test17965.d29
-rw-r--r--gcc/testsuite/gdc.test/runnable/test18296.d24
-rw-r--r--gcc/testsuite/gdc.test/runnable/test18534.d18
-rw-r--r--gcc/testsuite/gdc.test/runnable/test18545.d63
-rw-r--r--gcc/testsuite/gdc.test/runnable/test18746.d16
-rw-r--r--gcc/testsuite/gdc.test/runnable/test18868.d9
-rw-r--r--gcc/testsuite/gdc.test/runnable/test18868_2.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/test18868_3.d16
-rw-r--r--gcc/testsuite/gdc.test/runnable/test18880.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/test18916.d22
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19.d18
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19086.d64
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19122.d46
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19185.d22
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19192.d18
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19223.d38
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19251.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19317.d32
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19386.d36
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19393.d37
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19441.d24
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19476.d18
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19639.d22
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19655a.d10
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19672.d21
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19679.d21
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19688.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19729.d61
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19734.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19774.d43
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19782.d23
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19822.d29
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19825.d42
-rw-r--r--gcc/testsuite/gdc.test/runnable/test19891.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/test20.d109
-rw-r--r--gcc/testsuite/gdc.test/runnable/test20025.d21
-rw-r--r--gcc/testsuite/gdc.test/runnable/test20036.d10
-rw-r--r--gcc/testsuite/gdc.test/runnable/test20130.d44
-rw-r--r--gcc/testsuite/gdc.test/runnable/test20401.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/test20565.d18
-rw-r--r--gcc/testsuite/gdc.test/runnable/test20649.d15
-rw-r--r--gcc/testsuite/gdc.test/runnable/test20855.d26
-rw-r--r--gcc/testsuite/gdc.test/runnable/test20893.d21
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21040.d61
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21120.d27
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21357.d35
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21403.d72
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21424.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21479.d28
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21515.d81
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21586.d31
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21822.d18
-rw-r--r--gcc/testsuite/gdc.test/runnable/test21833.d30
-rw-r--r--gcc/testsuite/gdc.test/runnable/test22.d357
-rw-r--r--gcc/testsuite/gdc.test/runnable/test22209.d21
-rw-r--r--gcc/testsuite/gdc.test/runnable/test23.d177
-rw-r--r--gcc/testsuite/gdc.test/runnable/test24.d4
-rw-r--r--gcc/testsuite/gdc.test/runnable/test27.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/test28.d159
-rw-r--r--gcc/testsuite/gdc.test/runnable/test29.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/test3.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test30.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test34.d226
-rw-r--r--gcc/testsuite/gdc.test/runnable/test3449.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/test3574a.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test3574b.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test3574c.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test3574d.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test37.d9
-rw-r--r--gcc/testsuite/gdc.test/runnable/test38.d14
-rw-r--r--gcc/testsuite/gdc.test/runnable/test4.d166
-rw-r--r--gcc/testsuite/gdc.test/runnable/test40.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test42.d352
-rw-r--r--gcc/testsuite/gdc.test/runnable/test42a.d9
-rw-r--r--gcc/testsuite/gdc.test/runnable/test435.d14
-rw-r--r--gcc/testsuite/gdc.test/runnable/test45.d16
-rw-r--r--gcc/testsuite/gdc.test/runnable/test48.d14
-rw-r--r--gcc/testsuite/gdc.test/runnable/test49.d14
-rw-r--r--gcc/testsuite/gdc.test/runnable/test5.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/test52.d45
-rw-r--r--gcc/testsuite/gdc.test/runnable/test5305.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/test60.d23
-rw-r--r--gcc/testsuite/gdc.test/runnable/test61.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test6795.d26
-rw-r--r--gcc/testsuite/gdc.test/runnable/test711.d51
-rw-r--r--gcc/testsuite/gdc.test/runnable/test7452.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/test7511.d18
-rw-r--r--gcc/testsuite/gdc.test/runnable/test7932.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/test8.d115
-rw-r--r--gcc/testsuite/gdc.test/runnable/test809.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/test8544.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/test9259.d3
-rw-r--r--gcc/testsuite/gdc.test/runnable/test9271.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/testCopyCtor.d159
-rw-r--r--gcc/testsuite/gdc.test/runnable/test_dip1006.d38
-rw-r--r--gcc/testsuite/gdc.test/runnable/test_dip1006b.d35
-rw-r--r--gcc/testsuite/gdc.test/runnable/testaa.d91
-rw-r--r--gcc/testsuite/gdc.test/runnable/testaa2.d32
-rw-r--r--gcc/testsuite/gdc.test/runnable/testaa3.d7
-rw-r--r--gcc/testsuite/gdc.test/runnable/testabi.d926
-rw-r--r--gcc/testsuite/gdc.test/runnable/testaliascast.d63
-rw-r--r--gcc/testsuite/gdc.test/runnable/testappend.d16
-rw-r--r--gcc/testsuite/gdc.test/runnable/testassert.d380
-rw-r--r--gcc/testsuite/gdc.test/runnable/testassert_debug.d26
-rw-r--r--gcc/testsuite/gdc.test/runnable/testassign.d73
-rw-r--r--gcc/testsuite/gdc.test/runnable/testbitarray.d17
-rw-r--r--gcc/testsuite/gdc.test/runnable/testbounds.d123
-rw-r--r--gcc/testsuite/gdc.test/runnable/testbtst.d156
-rw-r--r--gcc/testsuite/gdc.test/runnable/testcgelem.d47
-rw-r--r--gcc/testsuite/gdc.test/runnable/testclass.d54
-rw-r--r--gcc/testsuite/gdc.test/runnable/testconst.d142
-rw-r--r--gcc/testsuite/gdc.test/runnable/testcontracts.d196
-rw-r--r--gcc/testsuite/gdc.test/runnable/testdefault_after_variadic.d98
-rw-r--r--gcc/testsuite/gdc.test/runnable/testdstress.d58
-rw-r--r--gcc/testsuite/gdc.test/runnable/testdt.d18
-rw-r--r--gcc/testsuite/gdc.test/runnable/testenum.d26
-rw-r--r--gcc/testsuite/gdc.test/runnable/testfile.d25
-rw-r--r--gcc/testsuite/gdc.test/runnable/testfloat.d239
-rw-r--r--gcc/testsuite/gdc.test/runnable/testformat.d126
-rw-r--r--gcc/testsuite/gdc.test/runnable/testgc2.d22
-rw-r--r--gcc/testsuite/gdc.test/runnable/testgc3.d11
-rw-r--r--gcc/testsuite/gdc.test/runnable/testinvariant.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/testkeyword.d4
-rw-r--r--gcc/testsuite/gdc.test/runnable/testline.d5
-rw-r--r--gcc/testsuite/gdc.test/runnable/testmain.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable/testminit.d13
-rw-r--r--gcc/testsuite/gdc.test/runnable/testmmfile.d120
-rw-r--r--gcc/testsuite/gdc.test/runnable/testmod2.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/testpic.d19
-rw-r--r--gcc/testsuite/gdc.test/runnable/testptrref.d29
-rw-r--r--gcc/testsuite/gdc.test/runnable/testptrref_gc.d3
-rw-r--r--gcc/testsuite/gdc.test/runnable/testreturn.d14
-rw-r--r--gcc/testsuite/gdc.test/runnable/testrightthis.d16
-rw-r--r--gcc/testsuite/gdc.test/runnable/testsafe.d20
-rw-r--r--gcc/testsuite/gdc.test/runnable/testscope.d55
-rw-r--r--gcc/testsuite/gdc.test/runnable/testscope2.d8
-rw-r--r--gcc/testsuite/gdc.test/runnable/testsignals.d114
-rw-r--r--gcc/testsuite/gdc.test/runnable/testsocket.d51
-rw-r--r--gcc/testsuite/gdc.test/runnable/teststdio.d34
-rw-r--r--gcc/testsuite/gdc.test/runnable/testswitch.d48
-rw-r--r--gcc/testsuite/gdc.test/runnable/testthread.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/testthread2.d39
-rw-r--r--gcc/testsuite/gdc.test/runnable/testtypeid.d102
-rw-r--r--gcc/testsuite/gdc.test/runnable/traits.d254
-rw-r--r--gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d7
-rw-r--r--gcc/testsuite/gdc.test/runnable/traits_getUnitTests.d2
-rw-r--r--gcc/testsuite/gdc.test/runnable/tuple_default_parameters.d64
-rw-r--r--gcc/testsuite/gdc.test/runnable/uda.d28
-rw-r--r--gcc/testsuite/gdc.test/runnable/ufcs.d127
-rw-r--r--gcc/testsuite/gdc.test/runnable/uniformctor.d22
-rw-r--r--gcc/testsuite/gdc.test/runnable/unique_typeinfo_names.d93
-rw-r--r--gcc/testsuite/gdc.test/runnable/variadic.d38
-rw-r--r--gcc/testsuite/gdc.test/runnable/version.d12
-rw-r--r--gcc/testsuite/gdc.test/runnable/warning1.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/wc.d4
-rw-r--r--gcc/testsuite/gdc.test/runnable/wc2.d6
-rw-r--r--gcc/testsuite/gdc.test/runnable/whetstone.d231
-rw-r--r--gcc/testsuite/gdc.test/runnable/xdtor.d26
-rw-r--r--gcc/testsuite/gdc.test/runnable/xpostblit.d59
-rw-r--r--gcc/testsuite/gdc.test/runnable/xtest46.d632
-rw-r--r--gcc/testsuite/gdc.test/runnable/xtest46_gc.d37
-rw-r--r--gcc/testsuite/gdc.test/runnable/xtest47.d67
-rw-r--r--gcc/testsuite/gdc.test/runnable/xtest55.d7
-rw-r--r--gcc/testsuite/gdc.test/runnable/xtestenum.d11
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/abi_tags.d139
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/cpp11.d70
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/cpp_abi_tests.d116
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/cpp_stdlib.d58
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/cppa.d497
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/externmangle.d57
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/externmangle2.d216
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/abi_tags.cpp146
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/c14203.cpp2
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp11.cpp35
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp19179.cpp15
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp_abi_tests.cpp103
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp_stdlib.cpp47
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/cppb.cpp262
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/cppb.h83
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/externmangle.cpp59
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/stdint.cpp8
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/test20652.cpp34
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/test21515.cpp18
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/extra-files/test6716.cpp13
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/stdint.d24
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/test14203.d22
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/test19179.d32
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/test20652.d23
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/test21515.d1
-rw-r--r--gcc/testsuite/gdc.test/runnable_cxx/test6716.d20
-rw-r--r--gcc/testsuite/lib/gdc-utils.exp81
-rw-r--r--libphobos/libdruntime/LICENSE26
-rw-r--r--libphobos/libdruntime/LICENSE.txt23
-rw-r--r--libphobos/libdruntime/MERGE2
-rw-r--r--libphobos/libdruntime/Makefile.am96
-rw-r--r--libphobos/libdruntime/Makefile.in508
-rw-r--r--libphobos/libdruntime/__entrypoint.di56
-rw-r--r--libphobos/libdruntime/core/atomic.d2428
-rw-r--r--libphobos/libdruntime/core/attribute.d188
-rw-r--r--libphobos/libdruntime/core/bitop.d19
-rw-r--r--libphobos/libdruntime/core/builtins.d19
-rw-r--r--libphobos/libdruntime/core/checkedint.d114
-rw-r--r--libphobos/libdruntime/core/demangle.d184
-rw-r--r--libphobos/libdruntime/core/exception.d347
-rw-r--r--libphobos/libdruntime/core/gc/config.d129
-rw-r--r--libphobos/libdruntime/core/gc/gcinterface.d198
-rw-r--r--libphobos/libdruntime/core/gc/registry.d87
-rw-r--r--libphobos/libdruntime/core/internal/abort.d20
-rw-r--r--libphobos/libdruntime/core/internal/array/appending.d222
-rw-r--r--libphobos/libdruntime/core/internal/array/capacity.d85
-rw-r--r--libphobos/libdruntime/core/internal/array/casting.d115
-rw-r--r--libphobos/libdruntime/core/internal/array/comparison.d242
-rw-r--r--libphobos/libdruntime/core/internal/array/concatenation.d75
-rw-r--r--libphobos/libdruntime/core/internal/array/construction.d307
-rw-r--r--libphobos/libdruntime/core/internal/array/equality.d237
-rw-r--r--libphobos/libdruntime/core/internal/array/operations.d670
-rw-r--r--libphobos/libdruntime/core/internal/array/utils.d121
-rw-r--r--libphobos/libdruntime/core/internal/arrayop.d451
-rw-r--r--libphobos/libdruntime/core/internal/atomic.d1141
-rw-r--r--libphobos/libdruntime/core/internal/container/array.d232
-rw-r--r--libphobos/libdruntime/core/internal/container/common.d63
-rw-r--r--libphobos/libdruntime/core/internal/container/hashtab.d330
-rw-r--r--libphobos/libdruntime/core/internal/container/treap.d368
-rw-r--r--libphobos/libdruntime/core/internal/convert.d56
-rw-r--r--libphobos/libdruntime/core/internal/dassert.d590
-rw-r--r--libphobos/libdruntime/core/internal/destruction.d47
-rw-r--r--libphobos/libdruntime/core/internal/entrypoint.d41
-rw-r--r--libphobos/libdruntime/core/internal/gc/bits.d493
-rw-r--r--libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d4836
-rw-r--r--libphobos/libdruntime/core/internal/gc/impl/manual/gc.d269
-rw-r--r--libphobos/libdruntime/core/internal/gc/impl/proto/gc.d248
-rw-r--r--libphobos/libdruntime/core/internal/gc/os.d308
-rw-r--r--libphobos/libdruntime/core/internal/gc/pooltable.d295
-rw-r--r--libphobos/libdruntime/core/internal/gc/proxy.d296
-rw-r--r--libphobos/libdruntime/core/internal/hash.d453
-rw-r--r--libphobos/libdruntime/core/internal/lifetime.d213
-rw-r--r--libphobos/libdruntime/core/internal/moving.d147
-rw-r--r--libphobos/libdruntime/core/internal/parseoptions.d422
-rw-r--r--libphobos/libdruntime/core/internal/postblit.d274
-rw-r--r--libphobos/libdruntime/core/internal/qsort.d196
-rw-r--r--libphobos/libdruntime/core/internal/spinlock.d23
-rw-r--r--libphobos/libdruntime/core/internal/string.d166
-rw-r--r--libphobos/libdruntime/core/internal/switch_.d190
-rw-r--r--libphobos/libdruntime/core/internal/traits.d609
-rw-r--r--libphobos/libdruntime/core/internal/utf.d938
-rw-r--r--libphobos/libdruntime/core/internal/util/array.d72
-rw-r--r--libphobos/libdruntime/core/internal/util/math.d53
-rw-r--r--libphobos/libdruntime/core/lifetime.d2201
-rw-r--r--libphobos/libdruntime/core/memory.d926
-rw-r--r--libphobos/libdruntime/core/runtime.d848
-rw-r--r--libphobos/libdruntime/core/stdc/math.d395
-rw-r--r--libphobos/libdruntime/core/stdc/stdint.d91
-rw-r--r--libphobos/libdruntime/core/stdcpp/allocator.d373
-rw-r--r--libphobos/libdruntime/core/stdcpp/array.d133
-rw-r--r--libphobos/libdruntime/core/stdcpp/exception.d161
-rw-r--r--libphobos/libdruntime/core/stdcpp/memory.d163
-rw-r--r--libphobos/libdruntime/core/stdcpp/new_.d186
-rw-r--r--libphobos/libdruntime/core/stdcpp/string.d2593
-rw-r--r--libphobos/libdruntime/core/stdcpp/string_view.d130
-rw-r--r--libphobos/libdruntime/core/stdcpp/type_traits.d50
-rw-r--r--libphobos/libdruntime/core/stdcpp/typeinfo.d87
-rw-r--r--libphobos/libdruntime/core/stdcpp/utility.d50
-rw-r--r--libphobos/libdruntime/core/stdcpp/vector.d850
-rw-r--r--libphobos/libdruntime/core/stdcpp/xutility.d427
-rw-r--r--libphobos/libdruntime/core/sync/barrier.d61
-rw-r--r--libphobos/libdruntime/core/sync/condition.d450
-rw-r--r--libphobos/libdruntime/core/sync/config.d19
-rw-r--r--libphobos/libdruntime/core/sync/event.d345
-rw-r--r--libphobos/libdruntime/core/sync/mutex.d16
-rw-r--r--libphobos/libdruntime/core/sync/rwmutex.d173
-rw-r--r--libphobos/libdruntime/core/sync/semaphore.d42
-rw-r--r--libphobos/libdruntime/core/sys/darwin/dlfcn.d5
-rw-r--r--libphobos/libdruntime/core/sys/dragonflybsd/sys/elf32.d2
-rw-r--r--libphobos/libdruntime/core/sys/dragonflybsd/sys/elf64.d2
-rw-r--r--libphobos/libdruntime/core/sys/freebsd/sys/elf32.d2
-rw-r--r--libphobos/libdruntime/core/sys/freebsd/sys/elf64.d2
-rw-r--r--libphobos/libdruntime/core/sys/linux/fs.d265
-rw-r--r--libphobos/libdruntime/core/sys/linux/io_uring.d414
-rw-r--r--libphobos/libdruntime/core/sys/linux/perf_event.d2515
-rw-r--r--libphobos/libdruntime/core/sys/linux/sys/procfs.d15
-rw-r--r--libphobos/libdruntime/core/sys/netbsd/sys/elf32.d2
-rw-r--r--libphobos/libdruntime/core/sys/netbsd/sys/elf64.d2
-rw-r--r--libphobos/libdruntime/core/sys/openbsd/execinfo.d147
-rw-r--r--libphobos/libdruntime/core/sys/openbsd/sys/elf32.d2
-rw-r--r--libphobos/libdruntime/core/sys/openbsd/sys/elf64.d2
-rw-r--r--libphobos/libdruntime/core/sys/posix/arpa/inet.d116
-rw-r--r--libphobos/libdruntime/core/sys/posix/fcntl.d16
-rw-r--r--libphobos/libdruntime/core/sys/posix/net/if_.d2
-rw-r--r--libphobos/libdruntime/core/sys/posix/semaphore.d2
-rw-r--r--libphobos/libdruntime/core/sys/posix/setjmp.d4
-rw-r--r--libphobos/libdruntime/core/sys/posix/stdio.d10
-rw-r--r--libphobos/libdruntime/core/sys/posix/string.d8
-rw-r--r--libphobos/libdruntime/core/sys/windows/basetsd.d2
-rw-r--r--libphobos/libdruntime/core/sys/windows/dll.d1
-rw-r--r--libphobos/libdruntime/core/sys/windows/sqlext.d2
-rw-r--r--libphobos/libdruntime/core/thread/fiber.d2
-rw-r--r--libphobos/libdruntime/core/thread/osthread.d34
-rw-r--r--libphobos/libdruntime/core/thread/threadbase.d12
-rw-r--r--libphobos/libdruntime/core/time.d1201
-rw-r--r--libphobos/libdruntime/gc/bits.d129
-rw-r--r--libphobos/libdruntime/gc/config.d291
-rw-r--r--libphobos/libdruntime/gc/gcinterface.d190
-rw-r--r--libphobos/libdruntime/gc/impl/conservative/gc.d3413
-rw-r--r--libphobos/libdruntime/gc/impl/manual/gc.d274
-rw-r--r--libphobos/libdruntime/gc/os.d214
-rw-r--r--libphobos/libdruntime/gc/pooltable.d285
-rw-r--r--libphobos/libdruntime/gc/proxy.d239
-rw-r--r--libphobos/libdruntime/gcc/deh.d22
-rw-r--r--libphobos/libdruntime/gcc/emutls.d3
-rw-r--r--libphobos/libdruntime/gcc/sections/elf.d6
-rw-r--r--libphobos/libdruntime/gcc/sections/macho.d6
-rw-r--r--libphobos/libdruntime/gcc/sections/pecoff.d6
-rw-r--r--libphobos/libdruntime/object.d3555
-rw-r--r--libphobos/libdruntime/rt/aApply.d6
-rw-r--r--libphobos/libdruntime/rt/aApplyR.d5
-rw-r--r--libphobos/libdruntime/rt/aaA.d272
-rw-r--r--libphobos/libdruntime/rt/adi.d306
-rw-r--r--libphobos/libdruntime/rt/arrayassign.d4
-rw-r--r--libphobos/libdruntime/rt/arraycast.d52
-rw-r--r--libphobos/libdruntime/rt/arraycat.d4
-rw-r--r--libphobos/libdruntime/rt/cast_.d51
-rw-r--r--libphobos/libdruntime/rt/config.d85
-rw-r--r--libphobos/libdruntime/rt/critical_.d3
-rw-r--r--libphobos/libdruntime/rt/deh.d36
-rw-r--r--libphobos/libdruntime/rt/dmain2.d333
-rw-r--r--libphobos/libdruntime/rt/dylib_fixes.c2
-rw-r--r--libphobos/libdruntime/rt/ehalloc.d125
-rw-r--r--libphobos/libdruntime/rt/invariant.d3
-rw-r--r--libphobos/libdruntime/rt/lifetime.d896
-rw-r--r--libphobos/libdruntime/rt/memory.d2
-rw-r--r--libphobos/libdruntime/rt/minfo.d10
-rw-r--r--libphobos/libdruntime/rt/monitor_.d10
-rw-r--r--libphobos/libdruntime/rt/obj.d35
-rw-r--r--libphobos/libdruntime/rt/profilegc.d170
-rw-r--r--libphobos/libdruntime/rt/qsort.d166
-rw-r--r--libphobos/libdruntime/rt/sections.d17
-rw-r--r--libphobos/libdruntime/rt/switch_.d424
-rw-r--r--libphobos/libdruntime/rt/tlsgc.d3
-rw-r--r--libphobos/libdruntime/rt/util/array.d72
-rw-r--r--libphobos/libdruntime/rt/util/container/array.d232
-rw-r--r--libphobos/libdruntime/rt/util/container/common.d66
-rw-r--r--libphobos/libdruntime/rt/util/container/hashtab.d329
-rw-r--r--libphobos/libdruntime/rt/util/container/treap.d338
-rw-r--r--libphobos/libdruntime/rt/util/random.d51
-rw-r--r--libphobos/libdruntime/rt/util/typeinfo.d304
-rw-r--r--libphobos/libdruntime/rt/util/utf.d920
-rw-r--r--libphobos/libdruntime/rt/util/utility.d44
-rw-r--r--libphobos/src/MERGE2
-rw-r--r--libphobos/src/Makefile.am47
-rw-r--r--libphobos/src/Makefile.in145
-rw-r--r--libphobos/src/etc/c/curl.d34
-rw-r--r--libphobos/src/etc/c/sqlite3.d2126
-rw-r--r--libphobos/src/etc/c/zlib.d9
-rw-r--r--libphobos/src/index.d22
-rw-r--r--libphobos/src/std/algorithm/comparison.d950
-rw-r--r--libphobos/src/std/algorithm/internal.d22
-rw-r--r--libphobos/src/std/algorithm/iteration.d4235
-rw-r--r--libphobos/src/std/algorithm/mutation.d1408
-rw-r--r--libphobos/src/std/algorithm/package.d13
-rw-r--r--libphobos/src/std/algorithm/searching.d1884
-rw-r--r--libphobos/src/std/algorithm/setops.d198
-rw-r--r--libphobos/src/std/algorithm/sorting.d1273
-rw-r--r--libphobos/src/std/array.d2036
-rw-r--r--libphobos/src/std/ascii.d188
-rw-r--r--libphobos/src/std/base64.d374
-rw-r--r--libphobos/src/std/bigint.d1045
-rw-r--r--libphobos/src/std/bitmanip.d2793
-rw-r--r--libphobos/src/std/compiler.d6
-rw-r--r--libphobos/src/std/complex.d1235
-rw-r--r--libphobos/src/std/concurrency.d695
-rw-r--r--libphobos/src/std/container/array.d467
-rw-r--r--libphobos/src/std/container/binaryheap.d98
-rw-r--r--libphobos/src/std/container/dlist.d177
-rw-r--r--libphobos/src/std/container/package.d519
-rw-r--r--libphobos/src/std/container/rbtree.d271
-rw-r--r--libphobos/src/std/container/slist.d268
-rw-r--r--libphobos/src/std/container/util.d8
-rw-r--r--libphobos/src/std/conv.d3078
-rw-r--r--libphobos/src/std/csv.d556
-rw-r--r--libphobos/src/std/datetime/date.d1202
-rw-r--r--libphobos/src/std/datetime/interval.d835
-rw-r--r--libphobos/src/std/datetime/package.d784
-rw-r--r--libphobos/src/std/datetime/stopwatch.d190
-rw-r--r--libphobos/src/std/datetime/systime.d2122
-rw-r--r--libphobos/src/std/datetime/timezone.d469
-rw-r--r--libphobos/src/std/demangle.d106
-rw-r--r--libphobos/src/std/digest/crc.d165
-rw-r--r--libphobos/src/std/digest/digest.d22
-rw-r--r--libphobos/src/std/digest/hmac.d60
-rw-r--r--libphobos/src/std/digest/md.d74
-rw-r--r--libphobos/src/std/digest/murmurhash.d2
-rw-r--r--libphobos/src/std/digest/package.d233
-rw-r--r--libphobos/src/std/digest/ripemd.d108
-rw-r--r--libphobos/src/std/digest/sha.d329
-rw-r--r--libphobos/src/std/encoding.d592
-rw-r--r--libphobos/src/std/exception.d920
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d195
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d699
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d424
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/ascending_page_allocator.d1007
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/bitmapped_block.d2480
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/bucketizer.d163
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d237
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/free_list.d385
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/free_tree.d82
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d193
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/null_allocator.d64
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/package.d145
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/quantizer.d184
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/region.d891
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d134
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/segregator.d241
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d339
-rw-r--r--libphobos/src/std/experimental/allocator/common.d192
-rw-r--r--libphobos/src/std/experimental/allocator/gc_allocator.d88
-rw-r--r--libphobos/src/std/experimental/allocator/mallocator.d175
-rw-r--r--libphobos/src/std/experimental/allocator/mmap_allocator.d92
-rw-r--r--libphobos/src/std/experimental/allocator/package.d1405
-rw-r--r--libphobos/src/std/experimental/allocator/showcase.d7
-rw-r--r--libphobos/src/std/experimental/allocator/typed.d14
-rw-r--r--libphobos/src/std/experimental/checkedint.d486
-rw-r--r--libphobos/src/std/experimental/logger/core.d444
-rw-r--r--libphobos/src/std/experimental/logger/filelogger.d71
-rw-r--r--libphobos/src/std/experimental/logger/multilogger.d55
-rw-r--r--libphobos/src/std/experimental/logger/nulllogger.d16
-rw-r--r--libphobos/src/std/experimental/logger/package.d153
-rw-r--r--libphobos/src/std/experimental/typecons.d43
-rw-r--r--libphobos/src/std/file.d2090
-rw-r--r--libphobos/src/std/format.d6028
-rw-r--r--libphobos/src/std/format/internal/floats.d2930
-rw-r--r--libphobos/src/std/format/internal/read.d410
-rw-r--r--libphobos/src/std/format/internal/write.d3980
-rw-r--r--libphobos/src/std/format/package.d1787
-rw-r--r--libphobos/src/std/format/read.d721
-rw-r--r--libphobos/src/std/format/spec.d949
-rw-r--r--libphobos/src/std/format/write.d1289
-rw-r--r--libphobos/src/std/functional.d688
-rw-r--r--libphobos/src/std/getopt.d371
-rw-r--r--libphobos/src/std/internal/attributes.d11
-rw-r--r--libphobos/src/std/internal/cstring.d318
-rw-r--r--libphobos/src/std/internal/math/biguintcore.d822
-rw-r--r--libphobos/src/std/internal/math/biguintnoasm.d18
-rw-r--r--libphobos/src/std/internal/math/errorfunction.d139
-rw-r--r--libphobos/src/std/internal/math/gammafunction.d303
-rw-r--r--libphobos/src/std/internal/memory.d58
-rw-r--r--libphobos/src/std/internal/scopebuffer.d29
-rw-r--r--libphobos/src/std/internal/test/dummyrange.d11
-rw-r--r--libphobos/src/std/internal/windows/advapi32.d4
-rw-r--r--libphobos/src/std/json.d1031
-rw-r--r--libphobos/src/std/math.d8586
-rw-r--r--libphobos/src/std/math/algebraic.d1072
-rw-r--r--libphobos/src/std/math/constants.d38
-rw-r--r--libphobos/src/std/math/exponential.d3439
-rw-r--r--libphobos/src/std/math/hardware.d1212
-rw-r--r--libphobos/src/std/math/operations.d1998
-rw-r--r--libphobos/src/std/math/package.d494
-rw-r--r--libphobos/src/std/math/remainder.d155
-rw-r--r--libphobos/src/std/math/rounding.d1004
-rw-r--r--libphobos/src/std/math/traits.d853
-rw-r--r--libphobos/src/std/math/trigonometry.d1425
-rw-r--r--libphobos/src/std/mathspecial.d35
-rw-r--r--libphobos/src/std/meta.d929
-rw-r--r--libphobos/src/std/mmfile.d146
-rw-r--r--libphobos/src/std/net/curl.d1101
-rw-r--r--libphobos/src/std/net/isemail.d135
-rw-r--r--libphobos/src/std/numeric.d1272
-rw-r--r--libphobos/src/std/outbuffer.d79
-rw-r--r--libphobos/src/std/package.d82
-rw-r--r--libphobos/src/std/parallelism.d741
-rw-r--r--libphobos/src/std/path.d993
-rw-r--r--libphobos/src/std/process.d2138
-rw-r--r--libphobos/src/std/random.d1905
-rw-r--r--libphobos/src/std/range/interfaces.d69
-rw-r--r--libphobos/src/std/range/package.d4583
-rw-r--r--libphobos/src/std/range/primitives.d767
-rw-r--r--libphobos/src/std/regex/internal/backtracking.d1388
-rw-r--r--libphobos/src/std/regex/internal/generator.d2
-rw-r--r--libphobos/src/std/regex/internal/ir.d565
-rw-r--r--libphobos/src/std/regex/internal/kickstart.d14
-rw-r--r--libphobos/src/std/regex/internal/parser.d792
-rw-r--r--libphobos/src/std/regex/internal/tests.d36
-rw-r--r--libphobos/src/std/regex/internal/tests2.d159
-rw-r--r--libphobos/src/std/regex/internal/thompson.d158
-rw-r--r--libphobos/src/std/regex/package.d558
-rw-r--r--libphobos/src/std/signals.d88
-rw-r--r--libphobos/src/std/socket.d695
-rw-r--r--libphobos/src/std/stdint.d6
-rw-r--r--libphobos/src/std/stdio.d1875
-rw-r--r--libphobos/src/std/string.d2046
-rw-r--r--libphobos/src/std/sumtype.d2500
-rw-r--r--libphobos/src/std/system.d20
-rw-r--r--libphobos/src/std/traits.d3180
-rw-r--r--libphobos/src/std/typecons.d3439
-rw-r--r--libphobos/src/std/typetuple.d5
-rw-r--r--libphobos/src/std/uni.d9768
-rw-r--r--libphobos/src/std/uni/package.d10637
-rw-r--r--libphobos/src/std/uri.d189
-rw-r--r--libphobos/src/std/utf.d1207
-rw-r--r--libphobos/src/std/uuid.d129
-rw-r--r--libphobos/src/std/variant.d973
-rw-r--r--libphobos/src/std/windows/charset.d12
-rw-r--r--libphobos/src/std/windows/registry.d122
-rw-r--r--libphobos/src/std/windows/syserror.d24
-rw-r--r--libphobos/src/std/xml.d312
-rw-r--r--libphobos/src/std/zip.d1303
-rw-r--r--libphobos/src/std/zlib.d270
-rw-r--r--libphobos/testsuite/lib/libphobos.exp60
-rw-r--r--libphobos/testsuite/libphobos.aa/test_aa.d79
-rw-r--r--libphobos/testsuite/libphobos.allocations/alloc_from_assert.d25
-rw-r--r--libphobos/testsuite/libphobos.betterc/betterc.exp27
-rw-r--r--libphobos/testsuite/libphobos.betterc/test18828.d10
-rw-r--r--libphobos/testsuite/libphobos.betterc/test19416.d14
-rw-r--r--libphobos/testsuite/libphobos.betterc/test19421.d13
-rw-r--r--libphobos/testsuite/libphobos.betterc/test19561.d16
-rw-r--r--libphobos/testsuite/libphobos.betterc/test19924.d15
-rw-r--r--libphobos/testsuite/libphobos.betterc/test20088.d14
-rw-r--r--libphobos/testsuite/libphobos.betterc/test20613.d18
-rw-r--r--libphobos/testsuite/libphobos.config/config.exp46
-rw-r--r--libphobos/testsuite/libphobos.config/test19433.d7
-rw-r--r--libphobos/testsuite/libphobos.config/test20459.d5
-rw-r--r--libphobos/testsuite/libphobos.druntime/druntime.exp2
-rw-r--r--libphobos/testsuite/libphobos.druntime_shared/druntime_shared.exp2
-rw-r--r--libphobos/testsuite/libphobos.exceptions/assert_fail.d564
-rw-r--r--libphobos/testsuite/libphobos.exceptions/catch_in_finally.d191
-rw-r--r--libphobos/testsuite/libphobos.exceptions/future_message.d71
-rw-r--r--libphobos/testsuite/libphobos.exceptions/long_backtrace_trunc.d37
-rw-r--r--libphobos/testsuite/libphobos.exceptions/refcounted.d96
-rw-r--r--libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions.d15
-rw-r--r--libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions_drt.d11
-rw-r--r--libphobos/testsuite/libphobos.exceptions/unknown_gc.d4
-rw-r--r--libphobos/testsuite/libphobos.gc/attributes.d30
-rw-r--r--libphobos/testsuite/libphobos.gc/forkgc.d36
-rw-r--r--libphobos/testsuite/libphobos.gc/forkgc2.d22
-rw-r--r--libphobos/testsuite/libphobos.gc/gc.exp27
-rw-r--r--libphobos/testsuite/libphobos.gc/nocollect.d15
-rw-r--r--libphobos/testsuite/libphobos.gc/precisegc.d126
-rw-r--r--libphobos/testsuite/libphobos.gc/recoverfree.d13
-rw-r--r--libphobos/testsuite/libphobos.gc/sigmaskgc.d42
-rw-r--r--libphobos/testsuite/libphobos.gc/startbackgc.d22
-rw-r--r--libphobos/testsuite/libphobos.hash/test_hash.d140
-rw-r--r--libphobos/testsuite/libphobos.imports/bug18193.d4
-rw-r--r--libphobos/testsuite/libphobos.imports/imports.exp29
-rw-r--r--libphobos/testsuite/libphobos.init_fini/custom_gc.d203
-rw-r--r--libphobos/testsuite/libphobos.init_fini/test18996.d13
-rw-r--r--libphobos/testsuite/libphobos.lifetime/large_aggregate_destroy_21097.d78
-rw-r--r--libphobos/testsuite/libphobos.lifetime/lifetime.exp27
-rw-r--r--libphobos/testsuite/libphobos.phobos/phobos.exp2
-rw-r--r--libphobos/testsuite/libphobos.phobos_shared/phobos_shared.exp2
-rw-r--r--libphobos/testsuite/libphobos.shared/host.c8
-rw-r--r--libphobos/testsuite/libphobos.shared/link_mod_collision.d5
-rw-r--r--libphobos/testsuite/libphobos.shared/load.d1
-rw-r--r--libphobos/testsuite/libphobos.shared/load_13414.d13
-rw-r--r--libphobos/testsuite/libphobos.shared/load_mod_collision.d14
-rw-r--r--libphobos/testsuite/libphobos.thread/external_threads.d50
-rw-r--r--libphobos/testsuite/libphobos.thread/fiber_guard_page.d4
-rw-r--r--libphobos/testsuite/libphobos.thread/join_detach.d20
-rw-r--r--libphobos/testsuite/libphobos.thread/test_import.d7
-rw-r--r--libphobos/testsuite/libphobos.thread/tlsgc_sections.d61
-rw-r--r--libphobos/testsuite/libphobos.thread/tlsstack.d38
-rw-r--r--libphobos/testsuite/libphobos.typeinfo/enum_.d21
-rw-r--r--libphobos/testsuite/libphobos.typeinfo/isbaseof.d46
-rw-r--r--libphobos/testsuite/libphobos.unittest/customhandler.d21
-rw-r--r--libphobos/testsuite/libphobos.unittest/unittest.exp53
-rwxr-xr-xlibphobos/testsuite/testsuite_flags.in2
2822 files changed, 347923 insertions, 202973 deletions
diff --git a/Makefile.def b/Makefile.def
index a504192e6d7..4e2087fbcfa 100644
--- a/Makefile.def
+++ b/Makefile.def
@@ -169,14 +169,14 @@ target_modules = { module= newlib; };
target_modules = { module= libgcc; bootstrap=true; no_check=true;
missing=TAGS;
missing=install-dvi; };
-target_modules = { module= libbacktrace; };
+target_modules = { module= libbacktrace; bootstrap=true; };
target_modules = { module= libquadmath; };
target_modules = { module= libgfortran; };
target_modules = { module= libobjc;
missing=TAGS;
missing=install-dvi; };
target_modules = { module= libgo; };
-target_modules = { module= libphobos;
+target_modules = { module= libphobos; bootstrap=true;
lib_path=src/.libs; };
target_modules = { module= libtermcap; no_check=true;
missing=mostlyclean;
@@ -186,12 +186,12 @@ target_modules = { module= libtermcap; no_check=true;
target_modules = { module= winsup; };
target_modules = { module= libgloss; no_check=true; };
target_modules = { module= libffi; no_install=true; };
-target_modules = { module= zlib; };
+target_modules = { module= zlib; bootstrap=true; };
target_modules = { module= rda; };
target_modules = { module= libada; };
target_modules = { module= libgomp; bootstrap= true; lib_path=.libs; };
target_modules = { module= libitm; lib_path=.libs; };
-target_modules = { module= libatomic; lib_path=.libs; };
+target_modules = { module= libatomic; bootstrap=true; lib_path=.libs; };
// These are (some of) the make targets to be done in each subdirectory.
// Not all; these are the ones which don't have special options.
diff --git a/Makefile.in b/Makefile.in
index 860cf8f067b..0f18c67120a 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -273,11 +273,14 @@ POSTSTAGE1_HOST_EXPORTS = \
$(POSTSTAGE1_CXX_EXPORT) \
$(LTO_EXPORTS) \
GDC="$$r/$(HOST_SUBDIR)/prev-gcc/gdc$(exeext) -B$$r/$(HOST_SUBDIR)/prev-gcc/ \
- -B$(build_tooldir)/bin/ $(GDC_FLAGS_FOR_TARGET) \
+ -B$(build_tooldir)/bin/ $(GDCFLAGS_FOR_TARGET) \
+ -B$$r/prev-$(TARGET_SUBDIR)/libphobos/libdruntime/gcc \
-B$$r/prev-$(TARGET_SUBDIR)/libphobos/src \
+ -B$$r/prev-$(TARGET_SUBDIR)/libphobos/src/.libs \
-I$$r/prev-$(TARGET_SUBDIR)/libphobos/libdruntime -I$$s/libphobos/libdruntime \
-L$$r/prev-$(TARGET_SUBDIR)/libphobos/src/.libs \
- -L$$r/prev-$(TARGET_SUBDIR)/libphobos/libdruntime/.libs"; \
+ -B$$r/prev-$(TARGET_SUBDIR)/libstdc++-v3/src/.libs \
+ -L$$r/prev-$(TARGET_SUBDIR)/libstdc++-v3/src/.libs"; \
export GDC; \
GDC_FOR_BUILD="$$GDC"; export GDC_FOR_BUILD; \
GNATBIND="$$r/$(HOST_SUBDIR)/prev-gcc/gnatbind"; export GNATBIND; \
@@ -564,6 +567,11 @@ STAGE1_CONFIGURE_FLAGS = --disable-intermodule $(STAGE1_CHECKING) \
--disable-coverage --enable-languages="$(STAGE1_LANGUAGES)" \
--disable-build-format-warnings
+@if target-libphobos-bootstrap
+STAGE1_CONFIGURE_FLAGS += --with-libphobos-druntime-only
+STAGE2_CONFIGURE_FLAGS += --with-libphobos-druntime-only
+@endif target-libphobos-bootstrap
+
# When using the slow stage1 compiler disable IL verification and forcefully
# enable it when using the stage2 compiler instead. As we later compare
# stage2 and stage3 we are merely avoid doing redundant work, plus we apply
@@ -1244,24 +1252,32 @@ all-target: maybe-all-target-newlib
@if target-libgcc-no-bootstrap
all-target: maybe-all-target-libgcc
@endif target-libgcc-no-bootstrap
+@if target-libbacktrace-no-bootstrap
all-target: maybe-all-target-libbacktrace
+@endif target-libbacktrace-no-bootstrap
all-target: maybe-all-target-libquadmath
all-target: maybe-all-target-libgfortran
all-target: maybe-all-target-libobjc
all-target: maybe-all-target-libgo
+@if target-libphobos-no-bootstrap
all-target: maybe-all-target-libphobos
+@endif target-libphobos-no-bootstrap
all-target: maybe-all-target-libtermcap
all-target: maybe-all-target-winsup
all-target: maybe-all-target-libgloss
all-target: maybe-all-target-libffi
+@if target-zlib-no-bootstrap
all-target: maybe-all-target-zlib
+@endif target-zlib-no-bootstrap
all-target: maybe-all-target-rda
all-target: maybe-all-target-libada
@if target-libgomp-no-bootstrap
all-target: maybe-all-target-libgomp
@endif target-libgomp-no-bootstrap
all-target: maybe-all-target-libitm
+@if target-libatomic-no-bootstrap
all-target: maybe-all-target-libatomic
+@endif target-libatomic-no-bootstrap
# Do a target for all the subdirectories. A ``make do-X'' will do a
# ``make X'' in all subdirectories (because, in general, there is a
@@ -50006,7 +50022,6 @@ configure-target-libbacktrace: stage_current
@if target-libbacktrace
maybe-configure-target-libbacktrace: configure-target-libbacktrace
configure-target-libbacktrace:
- @: $(MAKE); $(unstage)
@r=`${PWD_COMMAND}`; export r; \
s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
echo "Checking multilib configuration for libbacktrace..."; \
@@ -50044,6 +50059,412 @@ configure-target-libbacktrace:
+.PHONY: configure-stage1-target-libbacktrace maybe-configure-stage1-target-libbacktrace
+maybe-configure-stage1-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-configure-stage1-target-libbacktrace: configure-stage1-target-libbacktrace
+configure-stage1-target-libbacktrace:
+ @[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE1_TFLAGS)"; \
+ echo "Checking multilib configuration for libbacktrace..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libbacktrace/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/Makefile; \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libbacktrace/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 1 in $(TARGET_SUBDIR)/libbacktrace; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace; \
+ cd $(TARGET_SUBDIR)/libbacktrace || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libbacktrace/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libbacktrace; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ \
+ $(STAGE1_CONFIGURE_FLAGS)
+@endif target-libbacktrace-bootstrap
+
+.PHONY: configure-stage2-target-libbacktrace maybe-configure-stage2-target-libbacktrace
+maybe-configure-stage2-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-configure-stage2-target-libbacktrace: configure-stage2-target-libbacktrace
+configure-stage2-target-libbacktrace:
+ @[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE2_TFLAGS)"; \
+ echo "Checking multilib configuration for libbacktrace..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libbacktrace/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/Makefile; \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libbacktrace/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 2 in $(TARGET_SUBDIR)/libbacktrace; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace; \
+ cd $(TARGET_SUBDIR)/libbacktrace || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libbacktrace/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libbacktrace; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE2_CONFIGURE_FLAGS)
+@endif target-libbacktrace-bootstrap
+
+.PHONY: configure-stage3-target-libbacktrace maybe-configure-stage3-target-libbacktrace
+maybe-configure-stage3-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-configure-stage3-target-libbacktrace: configure-stage3-target-libbacktrace
+configure-stage3-target-libbacktrace:
+ @[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE3_TFLAGS)"; \
+ echo "Checking multilib configuration for libbacktrace..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libbacktrace/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/Makefile; \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libbacktrace/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 3 in $(TARGET_SUBDIR)/libbacktrace; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace; \
+ cd $(TARGET_SUBDIR)/libbacktrace || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libbacktrace/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libbacktrace; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE3_CONFIGURE_FLAGS)
+@endif target-libbacktrace-bootstrap
+
+.PHONY: configure-stage4-target-libbacktrace maybe-configure-stage4-target-libbacktrace
+maybe-configure-stage4-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-configure-stage4-target-libbacktrace: configure-stage4-target-libbacktrace
+configure-stage4-target-libbacktrace:
+ @[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE4_TFLAGS)"; \
+ echo "Checking multilib configuration for libbacktrace..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libbacktrace/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/Makefile; \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libbacktrace/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 4 in $(TARGET_SUBDIR)/libbacktrace; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace; \
+ cd $(TARGET_SUBDIR)/libbacktrace || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libbacktrace/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libbacktrace; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE4_CONFIGURE_FLAGS)
+@endif target-libbacktrace-bootstrap
+
+.PHONY: configure-stageprofile-target-libbacktrace maybe-configure-stageprofile-target-libbacktrace
+maybe-configure-stageprofile-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-configure-stageprofile-target-libbacktrace: configure-stageprofile-target-libbacktrace
+configure-stageprofile-target-libbacktrace:
+ @[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEprofile_TFLAGS)"; \
+ echo "Checking multilib configuration for libbacktrace..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libbacktrace/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/Makefile; \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libbacktrace/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage profile in $(TARGET_SUBDIR)/libbacktrace; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace; \
+ cd $(TARGET_SUBDIR)/libbacktrace || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libbacktrace/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libbacktrace; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEprofile_CONFIGURE_FLAGS)
+@endif target-libbacktrace-bootstrap
+
+.PHONY: configure-stagetrain-target-libbacktrace maybe-configure-stagetrain-target-libbacktrace
+maybe-configure-stagetrain-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-configure-stagetrain-target-libbacktrace: configure-stagetrain-target-libbacktrace
+configure-stagetrain-target-libbacktrace:
+ @[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEtrain_TFLAGS)"; \
+ echo "Checking multilib configuration for libbacktrace..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libbacktrace/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/Makefile; \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libbacktrace/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage train in $(TARGET_SUBDIR)/libbacktrace; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace; \
+ cd $(TARGET_SUBDIR)/libbacktrace || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libbacktrace/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libbacktrace; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEtrain_CONFIGURE_FLAGS)
+@endif target-libbacktrace-bootstrap
+
+.PHONY: configure-stagefeedback-target-libbacktrace maybe-configure-stagefeedback-target-libbacktrace
+maybe-configure-stagefeedback-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-configure-stagefeedback-target-libbacktrace: configure-stagefeedback-target-libbacktrace
+configure-stagefeedback-target-libbacktrace:
+ @[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+ echo "Checking multilib configuration for libbacktrace..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libbacktrace/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/Makefile; \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libbacktrace/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage feedback in $(TARGET_SUBDIR)/libbacktrace; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace; \
+ cd $(TARGET_SUBDIR)/libbacktrace || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libbacktrace/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libbacktrace; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEfeedback_CONFIGURE_FLAGS)
+@endif target-libbacktrace-bootstrap
+
+.PHONY: configure-stageautoprofile-target-libbacktrace maybe-configure-stageautoprofile-target-libbacktrace
+maybe-configure-stageautoprofile-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-configure-stageautoprofile-target-libbacktrace: configure-stageautoprofile-target-libbacktrace
+configure-stageautoprofile-target-libbacktrace:
+ @[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+ echo "Checking multilib configuration for libbacktrace..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libbacktrace/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/Makefile; \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libbacktrace/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage autoprofile in $(TARGET_SUBDIR)/libbacktrace; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace; \
+ cd $(TARGET_SUBDIR)/libbacktrace || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libbacktrace/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libbacktrace; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEautoprofile_CONFIGURE_FLAGS)
+@endif target-libbacktrace-bootstrap
+
+.PHONY: configure-stageautofeedback-target-libbacktrace maybe-configure-stageautofeedback-target-libbacktrace
+maybe-configure-stageautofeedback-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-configure-stageautofeedback-target-libbacktrace: configure-stageautofeedback-target-libbacktrace
+configure-stageautofeedback-target-libbacktrace:
+ @[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+ echo "Checking multilib configuration for libbacktrace..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libbacktrace/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libbacktrace/Makefile; \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libbacktrace/multilib.tmp $(TARGET_SUBDIR)/libbacktrace/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libbacktrace/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage autofeedback in $(TARGET_SUBDIR)/libbacktrace; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libbacktrace; \
+ cd $(TARGET_SUBDIR)/libbacktrace || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libbacktrace/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libbacktrace; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEautofeedback_CONFIGURE_FLAGS)
+@endif target-libbacktrace-bootstrap
+
+
+
.PHONY: all-target-libbacktrace maybe-all-target-libbacktrace
@@ -50055,7 +50476,6 @@ all-target-libbacktrace: stage_current
TARGET-target-libbacktrace=all
maybe-all-target-libbacktrace: all-target-libbacktrace
all-target-libbacktrace: configure-target-libbacktrace
- @: $(MAKE); $(unstage)
@r=`${PWD_COMMAND}`; export r; \
s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
$(NORMAL_TARGET_EXPORTS) \
@@ -50066,6 +50486,387 @@ all-target-libbacktrace: configure-target-libbacktrace
+.PHONY: all-stage1-target-libbacktrace maybe-all-stage1-target-libbacktrace
+.PHONY: clean-stage1-target-libbacktrace maybe-clean-stage1-target-libbacktrace
+maybe-all-stage1-target-libbacktrace:
+maybe-clean-stage1-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-all-stage1-target-libbacktrace: all-stage1-target-libbacktrace
+all-stage1: all-stage1-target-libbacktrace
+TARGET-stage1-target-libbacktrace = $(TARGET-target-libbacktrace)
+all-stage1-target-libbacktrace: configure-stage1-target-libbacktrace
+ @[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE1_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ \
+ TFLAGS="$(STAGE1_TFLAGS)" \
+ $(TARGET-stage1-target-libbacktrace)
+
+maybe-clean-stage1-target-libbacktrace: clean-stage1-target-libbacktrace
+clean-stage1: clean-stage1-target-libbacktrace
+clean-stage1-target-libbacktrace:
+ @if [ $(current_stage) = stage1 ]; then \
+ [ -f $(TARGET_SUBDIR)/libbacktrace/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage1-libbacktrace/Makefile ] || exit 0; \
+ $(MAKE) stage1-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) \
+ clean
+@endif target-libbacktrace-bootstrap
+
+
+.PHONY: all-stage2-target-libbacktrace maybe-all-stage2-target-libbacktrace
+.PHONY: clean-stage2-target-libbacktrace maybe-clean-stage2-target-libbacktrace
+maybe-all-stage2-target-libbacktrace:
+maybe-clean-stage2-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-all-stage2-target-libbacktrace: all-stage2-target-libbacktrace
+all-stage2: all-stage2-target-libbacktrace
+TARGET-stage2-target-libbacktrace = $(TARGET-target-libbacktrace)
+all-stage2-target-libbacktrace: configure-stage2-target-libbacktrace
+ @[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE2_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE2_TFLAGS)" \
+ $(TARGET-stage2-target-libbacktrace)
+
+maybe-clean-stage2-target-libbacktrace: clean-stage2-target-libbacktrace
+clean-stage2: clean-stage2-target-libbacktrace
+clean-stage2-target-libbacktrace:
+ @if [ $(current_stage) = stage2 ]; then \
+ [ -f $(TARGET_SUBDIR)/libbacktrace/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage2-libbacktrace/Makefile ] || exit 0; \
+ $(MAKE) stage2-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libbacktrace-bootstrap
+
+
+.PHONY: all-stage3-target-libbacktrace maybe-all-stage3-target-libbacktrace
+.PHONY: clean-stage3-target-libbacktrace maybe-clean-stage3-target-libbacktrace
+maybe-all-stage3-target-libbacktrace:
+maybe-clean-stage3-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-all-stage3-target-libbacktrace: all-stage3-target-libbacktrace
+all-stage3: all-stage3-target-libbacktrace
+TARGET-stage3-target-libbacktrace = $(TARGET-target-libbacktrace)
+all-stage3-target-libbacktrace: configure-stage3-target-libbacktrace
+ @[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE3_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE3_TFLAGS)" \
+ $(TARGET-stage3-target-libbacktrace)
+
+maybe-clean-stage3-target-libbacktrace: clean-stage3-target-libbacktrace
+clean-stage3: clean-stage3-target-libbacktrace
+clean-stage3-target-libbacktrace:
+ @if [ $(current_stage) = stage3 ]; then \
+ [ -f $(TARGET_SUBDIR)/libbacktrace/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage3-libbacktrace/Makefile ] || exit 0; \
+ $(MAKE) stage3-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libbacktrace-bootstrap
+
+
+.PHONY: all-stage4-target-libbacktrace maybe-all-stage4-target-libbacktrace
+.PHONY: clean-stage4-target-libbacktrace maybe-clean-stage4-target-libbacktrace
+maybe-all-stage4-target-libbacktrace:
+maybe-clean-stage4-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-all-stage4-target-libbacktrace: all-stage4-target-libbacktrace
+all-stage4: all-stage4-target-libbacktrace
+TARGET-stage4-target-libbacktrace = $(TARGET-target-libbacktrace)
+all-stage4-target-libbacktrace: configure-stage4-target-libbacktrace
+ @[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE4_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE4_TFLAGS)" \
+ $(TARGET-stage4-target-libbacktrace)
+
+maybe-clean-stage4-target-libbacktrace: clean-stage4-target-libbacktrace
+clean-stage4: clean-stage4-target-libbacktrace
+clean-stage4-target-libbacktrace:
+ @if [ $(current_stage) = stage4 ]; then \
+ [ -f $(TARGET_SUBDIR)/libbacktrace/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage4-libbacktrace/Makefile ] || exit 0; \
+ $(MAKE) stage4-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libbacktrace-bootstrap
+
+
+.PHONY: all-stageprofile-target-libbacktrace maybe-all-stageprofile-target-libbacktrace
+.PHONY: clean-stageprofile-target-libbacktrace maybe-clean-stageprofile-target-libbacktrace
+maybe-all-stageprofile-target-libbacktrace:
+maybe-clean-stageprofile-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-all-stageprofile-target-libbacktrace: all-stageprofile-target-libbacktrace
+all-stageprofile: all-stageprofile-target-libbacktrace
+TARGET-stageprofile-target-libbacktrace = $(TARGET-target-libbacktrace)
+all-stageprofile-target-libbacktrace: configure-stageprofile-target-libbacktrace
+ @[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEprofile_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEprofile_TFLAGS)" \
+ $(TARGET-stageprofile-target-libbacktrace)
+
+maybe-clean-stageprofile-target-libbacktrace: clean-stageprofile-target-libbacktrace
+clean-stageprofile: clean-stageprofile-target-libbacktrace
+clean-stageprofile-target-libbacktrace:
+ @if [ $(current_stage) = stageprofile ]; then \
+ [ -f $(TARGET_SUBDIR)/libbacktrace/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageprofile-libbacktrace/Makefile ] || exit 0; \
+ $(MAKE) stageprofile-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libbacktrace-bootstrap
+
+
+.PHONY: all-stagetrain-target-libbacktrace maybe-all-stagetrain-target-libbacktrace
+.PHONY: clean-stagetrain-target-libbacktrace maybe-clean-stagetrain-target-libbacktrace
+maybe-all-stagetrain-target-libbacktrace:
+maybe-clean-stagetrain-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-all-stagetrain-target-libbacktrace: all-stagetrain-target-libbacktrace
+all-stagetrain: all-stagetrain-target-libbacktrace
+TARGET-stagetrain-target-libbacktrace = $(TARGET-target-libbacktrace)
+all-stagetrain-target-libbacktrace: configure-stagetrain-target-libbacktrace
+ @[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEtrain_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEtrain_TFLAGS)" \
+ $(TARGET-stagetrain-target-libbacktrace)
+
+maybe-clean-stagetrain-target-libbacktrace: clean-stagetrain-target-libbacktrace
+clean-stagetrain: clean-stagetrain-target-libbacktrace
+clean-stagetrain-target-libbacktrace:
+ @if [ $(current_stage) = stagetrain ]; then \
+ [ -f $(TARGET_SUBDIR)/libbacktrace/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stagetrain-libbacktrace/Makefile ] || exit 0; \
+ $(MAKE) stagetrain-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libbacktrace-bootstrap
+
+
+.PHONY: all-stagefeedback-target-libbacktrace maybe-all-stagefeedback-target-libbacktrace
+.PHONY: clean-stagefeedback-target-libbacktrace maybe-clean-stagefeedback-target-libbacktrace
+maybe-all-stagefeedback-target-libbacktrace:
+maybe-clean-stagefeedback-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-all-stagefeedback-target-libbacktrace: all-stagefeedback-target-libbacktrace
+all-stagefeedback: all-stagefeedback-target-libbacktrace
+TARGET-stagefeedback-target-libbacktrace = $(TARGET-target-libbacktrace)
+all-stagefeedback-target-libbacktrace: configure-stagefeedback-target-libbacktrace
+ @[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEfeedback_TFLAGS)" \
+ $(TARGET-stagefeedback-target-libbacktrace)
+
+maybe-clean-stagefeedback-target-libbacktrace: clean-stagefeedback-target-libbacktrace
+clean-stagefeedback: clean-stagefeedback-target-libbacktrace
+clean-stagefeedback-target-libbacktrace:
+ @if [ $(current_stage) = stagefeedback ]; then \
+ [ -f $(TARGET_SUBDIR)/libbacktrace/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stagefeedback-libbacktrace/Makefile ] || exit 0; \
+ $(MAKE) stagefeedback-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libbacktrace-bootstrap
+
+
+.PHONY: all-stageautoprofile-target-libbacktrace maybe-all-stageautoprofile-target-libbacktrace
+.PHONY: clean-stageautoprofile-target-libbacktrace maybe-clean-stageautoprofile-target-libbacktrace
+maybe-all-stageautoprofile-target-libbacktrace:
+maybe-clean-stageautoprofile-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-all-stageautoprofile-target-libbacktrace: all-stageautoprofile-target-libbacktrace
+all-stageautoprofile: all-stageautoprofile-target-libbacktrace
+TARGET-stageautoprofile-target-libbacktrace = $(TARGET-target-libbacktrace)
+all-stageautoprofile-target-libbacktrace: configure-stageautoprofile-target-libbacktrace
+ @[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $$s/gcc/config/i386/$(AUTO_PROFILE) \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)" \
+ $(TARGET-stageautoprofile-target-libbacktrace)
+
+maybe-clean-stageautoprofile-target-libbacktrace: clean-stageautoprofile-target-libbacktrace
+clean-stageautoprofile: clean-stageautoprofile-target-libbacktrace
+clean-stageautoprofile-target-libbacktrace:
+ @if [ $(current_stage) = stageautoprofile ]; then \
+ [ -f $(TARGET_SUBDIR)/libbacktrace/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageautoprofile-libbacktrace/Makefile ] || exit 0; \
+ $(MAKE) stageautoprofile-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libbacktrace-bootstrap
+
+
+.PHONY: all-stageautofeedback-target-libbacktrace maybe-all-stageautofeedback-target-libbacktrace
+.PHONY: clean-stageautofeedback-target-libbacktrace maybe-clean-stageautofeedback-target-libbacktrace
+maybe-all-stageautofeedback-target-libbacktrace:
+maybe-clean-stageautofeedback-target-libbacktrace:
+@if target-libbacktrace-bootstrap
+maybe-all-stageautofeedback-target-libbacktrace: all-stageautofeedback-target-libbacktrace
+all-stageautofeedback: all-stageautofeedback-target-libbacktrace
+TARGET-stageautofeedback-target-libbacktrace = $(TARGET-target-libbacktrace)
+all-stageautofeedback-target-libbacktrace: configure-stageautofeedback-target-libbacktrace
+ @[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)" PERF_DATA=perf.data \
+ $(TARGET-stageautofeedback-target-libbacktrace)
+
+maybe-clean-stageautofeedback-target-libbacktrace: clean-stageautofeedback-target-libbacktrace
+clean-stageautofeedback: clean-stageautofeedback-target-libbacktrace
+clean-stageautofeedback-target-libbacktrace:
+ @if [ $(current_stage) = stageautofeedback ]; then \
+ [ -f $(TARGET_SUBDIR)/libbacktrace/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageautofeedback-libbacktrace/Makefile ] || exit 0; \
+ $(MAKE) stageautofeedback-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libbacktrace && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libbacktrace-bootstrap
+
+
+
+
.PHONY: check-target-libbacktrace maybe-check-target-libbacktrace
@@ -52398,7 +53199,6 @@ configure-target-libphobos: stage_current
@if target-libphobos
maybe-configure-target-libphobos: configure-target-libphobos
configure-target-libphobos:
- @: $(MAKE); $(unstage)
@r=`${PWD_COMMAND}`; export r; \
s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
echo "Checking multilib configuration for libphobos..."; \
@@ -52436,6 +53236,412 @@ configure-target-libphobos:
+.PHONY: configure-stage1-target-libphobos maybe-configure-stage1-target-libphobos
+maybe-configure-stage1-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-configure-stage1-target-libphobos: configure-stage1-target-libphobos
+configure-stage1-target-libphobos:
+ @[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE1_TFLAGS)"; \
+ echo "Checking multilib configuration for libphobos..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libphobos/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libphobos/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libphobos/Makefile; \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libphobos/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 1 in $(TARGET_SUBDIR)/libphobos; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos; \
+ cd $(TARGET_SUBDIR)/libphobos || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libphobos/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libphobos; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ \
+ $(STAGE1_CONFIGURE_FLAGS)
+@endif target-libphobos-bootstrap
+
+.PHONY: configure-stage2-target-libphobos maybe-configure-stage2-target-libphobos
+maybe-configure-stage2-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-configure-stage2-target-libphobos: configure-stage2-target-libphobos
+configure-stage2-target-libphobos:
+ @[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE2_TFLAGS)"; \
+ echo "Checking multilib configuration for libphobos..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libphobos/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libphobos/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libphobos/Makefile; \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libphobos/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 2 in $(TARGET_SUBDIR)/libphobos; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos; \
+ cd $(TARGET_SUBDIR)/libphobos || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libphobos/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libphobos; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE2_CONFIGURE_FLAGS)
+@endif target-libphobos-bootstrap
+
+.PHONY: configure-stage3-target-libphobos maybe-configure-stage3-target-libphobos
+maybe-configure-stage3-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-configure-stage3-target-libphobos: configure-stage3-target-libphobos
+configure-stage3-target-libphobos:
+ @[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE3_TFLAGS)"; \
+ echo "Checking multilib configuration for libphobos..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libphobos/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libphobos/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libphobos/Makefile; \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libphobos/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 3 in $(TARGET_SUBDIR)/libphobos; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos; \
+ cd $(TARGET_SUBDIR)/libphobos || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libphobos/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libphobos; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE3_CONFIGURE_FLAGS)
+@endif target-libphobos-bootstrap
+
+.PHONY: configure-stage4-target-libphobos maybe-configure-stage4-target-libphobos
+maybe-configure-stage4-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-configure-stage4-target-libphobos: configure-stage4-target-libphobos
+configure-stage4-target-libphobos:
+ @[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE4_TFLAGS)"; \
+ echo "Checking multilib configuration for libphobos..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libphobos/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libphobos/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libphobos/Makefile; \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libphobos/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 4 in $(TARGET_SUBDIR)/libphobos; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos; \
+ cd $(TARGET_SUBDIR)/libphobos || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libphobos/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libphobos; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE4_CONFIGURE_FLAGS)
+@endif target-libphobos-bootstrap
+
+.PHONY: configure-stageprofile-target-libphobos maybe-configure-stageprofile-target-libphobos
+maybe-configure-stageprofile-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-configure-stageprofile-target-libphobos: configure-stageprofile-target-libphobos
+configure-stageprofile-target-libphobos:
+ @[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEprofile_TFLAGS)"; \
+ echo "Checking multilib configuration for libphobos..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libphobos/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libphobos/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libphobos/Makefile; \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libphobos/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage profile in $(TARGET_SUBDIR)/libphobos; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos; \
+ cd $(TARGET_SUBDIR)/libphobos || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libphobos/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libphobos; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEprofile_CONFIGURE_FLAGS)
+@endif target-libphobos-bootstrap
+
+.PHONY: configure-stagetrain-target-libphobos maybe-configure-stagetrain-target-libphobos
+maybe-configure-stagetrain-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-configure-stagetrain-target-libphobos: configure-stagetrain-target-libphobos
+configure-stagetrain-target-libphobos:
+ @[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEtrain_TFLAGS)"; \
+ echo "Checking multilib configuration for libphobos..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libphobos/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libphobos/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libphobos/Makefile; \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libphobos/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage train in $(TARGET_SUBDIR)/libphobos; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos; \
+ cd $(TARGET_SUBDIR)/libphobos || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libphobos/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libphobos; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEtrain_CONFIGURE_FLAGS)
+@endif target-libphobos-bootstrap
+
+.PHONY: configure-stagefeedback-target-libphobos maybe-configure-stagefeedback-target-libphobos
+maybe-configure-stagefeedback-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-configure-stagefeedback-target-libphobos: configure-stagefeedback-target-libphobos
+configure-stagefeedback-target-libphobos:
+ @[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+ echo "Checking multilib configuration for libphobos..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libphobos/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libphobos/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libphobos/Makefile; \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libphobos/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage feedback in $(TARGET_SUBDIR)/libphobos; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos; \
+ cd $(TARGET_SUBDIR)/libphobos || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libphobos/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libphobos; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEfeedback_CONFIGURE_FLAGS)
+@endif target-libphobos-bootstrap
+
+.PHONY: configure-stageautoprofile-target-libphobos maybe-configure-stageautoprofile-target-libphobos
+maybe-configure-stageautoprofile-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-configure-stageautoprofile-target-libphobos: configure-stageautoprofile-target-libphobos
+configure-stageautoprofile-target-libphobos:
+ @[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+ echo "Checking multilib configuration for libphobos..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libphobos/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libphobos/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libphobos/Makefile; \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libphobos/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage autoprofile in $(TARGET_SUBDIR)/libphobos; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos; \
+ cd $(TARGET_SUBDIR)/libphobos || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libphobos/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libphobos; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEautoprofile_CONFIGURE_FLAGS)
+@endif target-libphobos-bootstrap
+
+.PHONY: configure-stageautofeedback-target-libphobos maybe-configure-stageautofeedback-target-libphobos
+maybe-configure-stageautofeedback-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-configure-stageautofeedback-target-libphobos: configure-stageautofeedback-target-libphobos
+configure-stageautofeedback-target-libphobos:
+ @[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+ echo "Checking multilib configuration for libphobos..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libphobos/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libphobos/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libphobos/Makefile; \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libphobos/multilib.tmp $(TARGET_SUBDIR)/libphobos/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libphobos/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage autofeedback in $(TARGET_SUBDIR)/libphobos; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libphobos; \
+ cd $(TARGET_SUBDIR)/libphobos || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libphobos/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libphobos; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEautofeedback_CONFIGURE_FLAGS)
+@endif target-libphobos-bootstrap
+
+
+
.PHONY: all-target-libphobos maybe-all-target-libphobos
@@ -52447,7 +53653,6 @@ all-target-libphobos: stage_current
TARGET-target-libphobos=all
maybe-all-target-libphobos: all-target-libphobos
all-target-libphobos: configure-target-libphobos
- @: $(MAKE); $(unstage)
@r=`${PWD_COMMAND}`; export r; \
s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
$(NORMAL_TARGET_EXPORTS) \
@@ -52458,6 +53663,387 @@ all-target-libphobos: configure-target-libphobos
+.PHONY: all-stage1-target-libphobos maybe-all-stage1-target-libphobos
+.PHONY: clean-stage1-target-libphobos maybe-clean-stage1-target-libphobos
+maybe-all-stage1-target-libphobos:
+maybe-clean-stage1-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-all-stage1-target-libphobos: all-stage1-target-libphobos
+all-stage1: all-stage1-target-libphobos
+TARGET-stage1-target-libphobos = $(TARGET-target-libphobos)
+all-stage1-target-libphobos: configure-stage1-target-libphobos
+ @[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE1_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ \
+ TFLAGS="$(STAGE1_TFLAGS)" \
+ $(TARGET-stage1-target-libphobos)
+
+maybe-clean-stage1-target-libphobos: clean-stage1-target-libphobos
+clean-stage1: clean-stage1-target-libphobos
+clean-stage1-target-libphobos:
+ @if [ $(current_stage) = stage1 ]; then \
+ [ -f $(TARGET_SUBDIR)/libphobos/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage1-libphobos/Makefile ] || exit 0; \
+ $(MAKE) stage1-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) \
+ clean
+@endif target-libphobos-bootstrap
+
+
+.PHONY: all-stage2-target-libphobos maybe-all-stage2-target-libphobos
+.PHONY: clean-stage2-target-libphobos maybe-clean-stage2-target-libphobos
+maybe-all-stage2-target-libphobos:
+maybe-clean-stage2-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-all-stage2-target-libphobos: all-stage2-target-libphobos
+all-stage2: all-stage2-target-libphobos
+TARGET-stage2-target-libphobos = $(TARGET-target-libphobos)
+all-stage2-target-libphobos: configure-stage2-target-libphobos
+ @[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE2_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE2_TFLAGS)" \
+ $(TARGET-stage2-target-libphobos)
+
+maybe-clean-stage2-target-libphobos: clean-stage2-target-libphobos
+clean-stage2: clean-stage2-target-libphobos
+clean-stage2-target-libphobos:
+ @if [ $(current_stage) = stage2 ]; then \
+ [ -f $(TARGET_SUBDIR)/libphobos/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage2-libphobos/Makefile ] || exit 0; \
+ $(MAKE) stage2-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libphobos-bootstrap
+
+
+.PHONY: all-stage3-target-libphobos maybe-all-stage3-target-libphobos
+.PHONY: clean-stage3-target-libphobos maybe-clean-stage3-target-libphobos
+maybe-all-stage3-target-libphobos:
+maybe-clean-stage3-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-all-stage3-target-libphobos: all-stage3-target-libphobos
+all-stage3: all-stage3-target-libphobos
+TARGET-stage3-target-libphobos = $(TARGET-target-libphobos)
+all-stage3-target-libphobos: configure-stage3-target-libphobos
+ @[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE3_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE3_TFLAGS)" \
+ $(TARGET-stage3-target-libphobos)
+
+maybe-clean-stage3-target-libphobos: clean-stage3-target-libphobos
+clean-stage3: clean-stage3-target-libphobos
+clean-stage3-target-libphobos:
+ @if [ $(current_stage) = stage3 ]; then \
+ [ -f $(TARGET_SUBDIR)/libphobos/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage3-libphobos/Makefile ] || exit 0; \
+ $(MAKE) stage3-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libphobos-bootstrap
+
+
+.PHONY: all-stage4-target-libphobos maybe-all-stage4-target-libphobos
+.PHONY: clean-stage4-target-libphobos maybe-clean-stage4-target-libphobos
+maybe-all-stage4-target-libphobos:
+maybe-clean-stage4-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-all-stage4-target-libphobos: all-stage4-target-libphobos
+all-stage4: all-stage4-target-libphobos
+TARGET-stage4-target-libphobos = $(TARGET-target-libphobos)
+all-stage4-target-libphobos: configure-stage4-target-libphobos
+ @[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE4_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE4_TFLAGS)" \
+ $(TARGET-stage4-target-libphobos)
+
+maybe-clean-stage4-target-libphobos: clean-stage4-target-libphobos
+clean-stage4: clean-stage4-target-libphobos
+clean-stage4-target-libphobos:
+ @if [ $(current_stage) = stage4 ]; then \
+ [ -f $(TARGET_SUBDIR)/libphobos/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage4-libphobos/Makefile ] || exit 0; \
+ $(MAKE) stage4-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libphobos-bootstrap
+
+
+.PHONY: all-stageprofile-target-libphobos maybe-all-stageprofile-target-libphobos
+.PHONY: clean-stageprofile-target-libphobos maybe-clean-stageprofile-target-libphobos
+maybe-all-stageprofile-target-libphobos:
+maybe-clean-stageprofile-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-all-stageprofile-target-libphobos: all-stageprofile-target-libphobos
+all-stageprofile: all-stageprofile-target-libphobos
+TARGET-stageprofile-target-libphobos = $(TARGET-target-libphobos)
+all-stageprofile-target-libphobos: configure-stageprofile-target-libphobos
+ @[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEprofile_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEprofile_TFLAGS)" \
+ $(TARGET-stageprofile-target-libphobos)
+
+maybe-clean-stageprofile-target-libphobos: clean-stageprofile-target-libphobos
+clean-stageprofile: clean-stageprofile-target-libphobos
+clean-stageprofile-target-libphobos:
+ @if [ $(current_stage) = stageprofile ]; then \
+ [ -f $(TARGET_SUBDIR)/libphobos/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageprofile-libphobos/Makefile ] || exit 0; \
+ $(MAKE) stageprofile-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libphobos-bootstrap
+
+
+.PHONY: all-stagetrain-target-libphobos maybe-all-stagetrain-target-libphobos
+.PHONY: clean-stagetrain-target-libphobos maybe-clean-stagetrain-target-libphobos
+maybe-all-stagetrain-target-libphobos:
+maybe-clean-stagetrain-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-all-stagetrain-target-libphobos: all-stagetrain-target-libphobos
+all-stagetrain: all-stagetrain-target-libphobos
+TARGET-stagetrain-target-libphobos = $(TARGET-target-libphobos)
+all-stagetrain-target-libphobos: configure-stagetrain-target-libphobos
+ @[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEtrain_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEtrain_TFLAGS)" \
+ $(TARGET-stagetrain-target-libphobos)
+
+maybe-clean-stagetrain-target-libphobos: clean-stagetrain-target-libphobos
+clean-stagetrain: clean-stagetrain-target-libphobos
+clean-stagetrain-target-libphobos:
+ @if [ $(current_stage) = stagetrain ]; then \
+ [ -f $(TARGET_SUBDIR)/libphobos/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stagetrain-libphobos/Makefile ] || exit 0; \
+ $(MAKE) stagetrain-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libphobos-bootstrap
+
+
+.PHONY: all-stagefeedback-target-libphobos maybe-all-stagefeedback-target-libphobos
+.PHONY: clean-stagefeedback-target-libphobos maybe-clean-stagefeedback-target-libphobos
+maybe-all-stagefeedback-target-libphobos:
+maybe-clean-stagefeedback-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-all-stagefeedback-target-libphobos: all-stagefeedback-target-libphobos
+all-stagefeedback: all-stagefeedback-target-libphobos
+TARGET-stagefeedback-target-libphobos = $(TARGET-target-libphobos)
+all-stagefeedback-target-libphobos: configure-stagefeedback-target-libphobos
+ @[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEfeedback_TFLAGS)" \
+ $(TARGET-stagefeedback-target-libphobos)
+
+maybe-clean-stagefeedback-target-libphobos: clean-stagefeedback-target-libphobos
+clean-stagefeedback: clean-stagefeedback-target-libphobos
+clean-stagefeedback-target-libphobos:
+ @if [ $(current_stage) = stagefeedback ]; then \
+ [ -f $(TARGET_SUBDIR)/libphobos/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stagefeedback-libphobos/Makefile ] || exit 0; \
+ $(MAKE) stagefeedback-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libphobos-bootstrap
+
+
+.PHONY: all-stageautoprofile-target-libphobos maybe-all-stageautoprofile-target-libphobos
+.PHONY: clean-stageautoprofile-target-libphobos maybe-clean-stageautoprofile-target-libphobos
+maybe-all-stageautoprofile-target-libphobos:
+maybe-clean-stageautoprofile-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-all-stageautoprofile-target-libphobos: all-stageautoprofile-target-libphobos
+all-stageautoprofile: all-stageautoprofile-target-libphobos
+TARGET-stageautoprofile-target-libphobos = $(TARGET-target-libphobos)
+all-stageautoprofile-target-libphobos: configure-stageautoprofile-target-libphobos
+ @[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $$s/gcc/config/i386/$(AUTO_PROFILE) \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)" \
+ $(TARGET-stageautoprofile-target-libphobos)
+
+maybe-clean-stageautoprofile-target-libphobos: clean-stageautoprofile-target-libphobos
+clean-stageautoprofile: clean-stageautoprofile-target-libphobos
+clean-stageautoprofile-target-libphobos:
+ @if [ $(current_stage) = stageautoprofile ]; then \
+ [ -f $(TARGET_SUBDIR)/libphobos/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageautoprofile-libphobos/Makefile ] || exit 0; \
+ $(MAKE) stageautoprofile-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libphobos-bootstrap
+
+
+.PHONY: all-stageautofeedback-target-libphobos maybe-all-stageautofeedback-target-libphobos
+.PHONY: clean-stageautofeedback-target-libphobos maybe-clean-stageautofeedback-target-libphobos
+maybe-all-stageautofeedback-target-libphobos:
+maybe-clean-stageautofeedback-target-libphobos:
+@if target-libphobos-bootstrap
+maybe-all-stageautofeedback-target-libphobos: all-stageautofeedback-target-libphobos
+all-stageautofeedback: all-stageautofeedback-target-libphobos
+TARGET-stageautofeedback-target-libphobos = $(TARGET-target-libphobos)
+all-stageautofeedback-target-libphobos: configure-stageautofeedback-target-libphobos
+ @[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)" PERF_DATA=perf.data \
+ $(TARGET-stageautofeedback-target-libphobos)
+
+maybe-clean-stageautofeedback-target-libphobos: clean-stageautofeedback-target-libphobos
+clean-stageautofeedback: clean-stageautofeedback-target-libphobos
+clean-stageautofeedback-target-libphobos:
+ @if [ $(current_stage) = stageautofeedback ]; then \
+ [ -f $(TARGET_SUBDIR)/libphobos/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageautofeedback-libphobos/Makefile ] || exit 0; \
+ $(MAKE) stageautofeedback-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libphobos && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libphobos-bootstrap
+
+
+
+
.PHONY: check-target-libphobos maybe-check-target-libphobos
@@ -54743,7 +56329,6 @@ configure-target-zlib: stage_current
@if target-zlib
maybe-configure-target-zlib: configure-target-zlib
configure-target-zlib:
- @: $(MAKE); $(unstage)
@r=`${PWD_COMMAND}`; export r; \
s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
echo "Checking multilib configuration for zlib..."; \
@@ -54781,6 +56366,412 @@ configure-target-zlib:
+.PHONY: configure-stage1-target-zlib maybe-configure-stage1-target-zlib
+maybe-configure-stage1-target-zlib:
+@if target-zlib-bootstrap
+maybe-configure-stage1-target-zlib: configure-stage1-target-zlib
+configure-stage1-target-zlib:
+ @[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE1_TFLAGS)"; \
+ echo "Checking multilib configuration for zlib..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/zlib/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/zlib/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/zlib/Makefile; \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/zlib/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 1 in $(TARGET_SUBDIR)/zlib; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib; \
+ cd $(TARGET_SUBDIR)/zlib || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/zlib/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=zlib; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ \
+ $(STAGE1_CONFIGURE_FLAGS)
+@endif target-zlib-bootstrap
+
+.PHONY: configure-stage2-target-zlib maybe-configure-stage2-target-zlib
+maybe-configure-stage2-target-zlib:
+@if target-zlib-bootstrap
+maybe-configure-stage2-target-zlib: configure-stage2-target-zlib
+configure-stage2-target-zlib:
+ @[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE2_TFLAGS)"; \
+ echo "Checking multilib configuration for zlib..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/zlib/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/zlib/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/zlib/Makefile; \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/zlib/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 2 in $(TARGET_SUBDIR)/zlib; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib; \
+ cd $(TARGET_SUBDIR)/zlib || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/zlib/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=zlib; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE2_CONFIGURE_FLAGS)
+@endif target-zlib-bootstrap
+
+.PHONY: configure-stage3-target-zlib maybe-configure-stage3-target-zlib
+maybe-configure-stage3-target-zlib:
+@if target-zlib-bootstrap
+maybe-configure-stage3-target-zlib: configure-stage3-target-zlib
+configure-stage3-target-zlib:
+ @[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE3_TFLAGS)"; \
+ echo "Checking multilib configuration for zlib..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/zlib/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/zlib/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/zlib/Makefile; \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/zlib/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 3 in $(TARGET_SUBDIR)/zlib; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib; \
+ cd $(TARGET_SUBDIR)/zlib || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/zlib/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=zlib; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE3_CONFIGURE_FLAGS)
+@endif target-zlib-bootstrap
+
+.PHONY: configure-stage4-target-zlib maybe-configure-stage4-target-zlib
+maybe-configure-stage4-target-zlib:
+@if target-zlib-bootstrap
+maybe-configure-stage4-target-zlib: configure-stage4-target-zlib
+configure-stage4-target-zlib:
+ @[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE4_TFLAGS)"; \
+ echo "Checking multilib configuration for zlib..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/zlib/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/zlib/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/zlib/Makefile; \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/zlib/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 4 in $(TARGET_SUBDIR)/zlib; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib; \
+ cd $(TARGET_SUBDIR)/zlib || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/zlib/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=zlib; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE4_CONFIGURE_FLAGS)
+@endif target-zlib-bootstrap
+
+.PHONY: configure-stageprofile-target-zlib maybe-configure-stageprofile-target-zlib
+maybe-configure-stageprofile-target-zlib:
+@if target-zlib-bootstrap
+maybe-configure-stageprofile-target-zlib: configure-stageprofile-target-zlib
+configure-stageprofile-target-zlib:
+ @[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEprofile_TFLAGS)"; \
+ echo "Checking multilib configuration for zlib..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/zlib/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/zlib/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/zlib/Makefile; \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/zlib/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage profile in $(TARGET_SUBDIR)/zlib; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib; \
+ cd $(TARGET_SUBDIR)/zlib || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/zlib/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=zlib; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEprofile_CONFIGURE_FLAGS)
+@endif target-zlib-bootstrap
+
+.PHONY: configure-stagetrain-target-zlib maybe-configure-stagetrain-target-zlib
+maybe-configure-stagetrain-target-zlib:
+@if target-zlib-bootstrap
+maybe-configure-stagetrain-target-zlib: configure-stagetrain-target-zlib
+configure-stagetrain-target-zlib:
+ @[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEtrain_TFLAGS)"; \
+ echo "Checking multilib configuration for zlib..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/zlib/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/zlib/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/zlib/Makefile; \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/zlib/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage train in $(TARGET_SUBDIR)/zlib; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib; \
+ cd $(TARGET_SUBDIR)/zlib || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/zlib/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=zlib; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEtrain_CONFIGURE_FLAGS)
+@endif target-zlib-bootstrap
+
+.PHONY: configure-stagefeedback-target-zlib maybe-configure-stagefeedback-target-zlib
+maybe-configure-stagefeedback-target-zlib:
+@if target-zlib-bootstrap
+maybe-configure-stagefeedback-target-zlib: configure-stagefeedback-target-zlib
+configure-stagefeedback-target-zlib:
+ @[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+ echo "Checking multilib configuration for zlib..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/zlib/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/zlib/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/zlib/Makefile; \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/zlib/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage feedback in $(TARGET_SUBDIR)/zlib; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib; \
+ cd $(TARGET_SUBDIR)/zlib || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/zlib/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=zlib; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEfeedback_CONFIGURE_FLAGS)
+@endif target-zlib-bootstrap
+
+.PHONY: configure-stageautoprofile-target-zlib maybe-configure-stageautoprofile-target-zlib
+maybe-configure-stageautoprofile-target-zlib:
+@if target-zlib-bootstrap
+maybe-configure-stageautoprofile-target-zlib: configure-stageautoprofile-target-zlib
+configure-stageautoprofile-target-zlib:
+ @[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+ echo "Checking multilib configuration for zlib..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/zlib/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/zlib/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/zlib/Makefile; \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/zlib/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage autoprofile in $(TARGET_SUBDIR)/zlib; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib; \
+ cd $(TARGET_SUBDIR)/zlib || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/zlib/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=zlib; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEautoprofile_CONFIGURE_FLAGS)
+@endif target-zlib-bootstrap
+
+.PHONY: configure-stageautofeedback-target-zlib maybe-configure-stageautofeedback-target-zlib
+maybe-configure-stageautofeedback-target-zlib:
+@if target-zlib-bootstrap
+maybe-configure-stageautofeedback-target-zlib: configure-stageautofeedback-target-zlib
+configure-stageautofeedback-target-zlib:
+ @[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+ echo "Checking multilib configuration for zlib..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/zlib/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/zlib/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/zlib/Makefile; \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/zlib/multilib.tmp $(TARGET_SUBDIR)/zlib/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/zlib/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage autofeedback in $(TARGET_SUBDIR)/zlib; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/zlib; \
+ cd $(TARGET_SUBDIR)/zlib || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/zlib/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=zlib; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEautofeedback_CONFIGURE_FLAGS)
+@endif target-zlib-bootstrap
+
+
+
.PHONY: all-target-zlib maybe-all-target-zlib
@@ -54792,7 +56783,6 @@ all-target-zlib: stage_current
TARGET-target-zlib=all
maybe-all-target-zlib: all-target-zlib
all-target-zlib: configure-target-zlib
- @: $(MAKE); $(unstage)
@r=`${PWD_COMMAND}`; export r; \
s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
$(NORMAL_TARGET_EXPORTS) \
@@ -54803,6 +56793,387 @@ all-target-zlib: configure-target-zlib
+.PHONY: all-stage1-target-zlib maybe-all-stage1-target-zlib
+.PHONY: clean-stage1-target-zlib maybe-clean-stage1-target-zlib
+maybe-all-stage1-target-zlib:
+maybe-clean-stage1-target-zlib:
+@if target-zlib-bootstrap
+maybe-all-stage1-target-zlib: all-stage1-target-zlib
+all-stage1: all-stage1-target-zlib
+TARGET-stage1-target-zlib = $(TARGET-target-zlib)
+all-stage1-target-zlib: configure-stage1-target-zlib
+ @[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE1_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ cd $(TARGET_SUBDIR)/zlib && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ \
+ TFLAGS="$(STAGE1_TFLAGS)" \
+ $(TARGET-stage1-target-zlib)
+
+maybe-clean-stage1-target-zlib: clean-stage1-target-zlib
+clean-stage1: clean-stage1-target-zlib
+clean-stage1-target-zlib:
+ @if [ $(current_stage) = stage1 ]; then \
+ [ -f $(TARGET_SUBDIR)/zlib/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage1-zlib/Makefile ] || exit 0; \
+ $(MAKE) stage1-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) \
+ clean
+@endif target-zlib-bootstrap
+
+
+.PHONY: all-stage2-target-zlib maybe-all-stage2-target-zlib
+.PHONY: clean-stage2-target-zlib maybe-clean-stage2-target-zlib
+maybe-all-stage2-target-zlib:
+maybe-clean-stage2-target-zlib:
+@if target-zlib-bootstrap
+maybe-all-stage2-target-zlib: all-stage2-target-zlib
+all-stage2: all-stage2-target-zlib
+TARGET-stage2-target-zlib = $(TARGET-target-zlib)
+all-stage2-target-zlib: configure-stage2-target-zlib
+ @[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE2_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/zlib && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE2_TFLAGS)" \
+ $(TARGET-stage2-target-zlib)
+
+maybe-clean-stage2-target-zlib: clean-stage2-target-zlib
+clean-stage2: clean-stage2-target-zlib
+clean-stage2-target-zlib:
+ @if [ $(current_stage) = stage2 ]; then \
+ [ -f $(TARGET_SUBDIR)/zlib/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage2-zlib/Makefile ] || exit 0; \
+ $(MAKE) stage2-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-zlib-bootstrap
+
+
+.PHONY: all-stage3-target-zlib maybe-all-stage3-target-zlib
+.PHONY: clean-stage3-target-zlib maybe-clean-stage3-target-zlib
+maybe-all-stage3-target-zlib:
+maybe-clean-stage3-target-zlib:
+@if target-zlib-bootstrap
+maybe-all-stage3-target-zlib: all-stage3-target-zlib
+all-stage3: all-stage3-target-zlib
+TARGET-stage3-target-zlib = $(TARGET-target-zlib)
+all-stage3-target-zlib: configure-stage3-target-zlib
+ @[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE3_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/zlib && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE3_TFLAGS)" \
+ $(TARGET-stage3-target-zlib)
+
+maybe-clean-stage3-target-zlib: clean-stage3-target-zlib
+clean-stage3: clean-stage3-target-zlib
+clean-stage3-target-zlib:
+ @if [ $(current_stage) = stage3 ]; then \
+ [ -f $(TARGET_SUBDIR)/zlib/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage3-zlib/Makefile ] || exit 0; \
+ $(MAKE) stage3-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-zlib-bootstrap
+
+
+.PHONY: all-stage4-target-zlib maybe-all-stage4-target-zlib
+.PHONY: clean-stage4-target-zlib maybe-clean-stage4-target-zlib
+maybe-all-stage4-target-zlib:
+maybe-clean-stage4-target-zlib:
+@if target-zlib-bootstrap
+maybe-all-stage4-target-zlib: all-stage4-target-zlib
+all-stage4: all-stage4-target-zlib
+TARGET-stage4-target-zlib = $(TARGET-target-zlib)
+all-stage4-target-zlib: configure-stage4-target-zlib
+ @[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE4_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/zlib && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE4_TFLAGS)" \
+ $(TARGET-stage4-target-zlib)
+
+maybe-clean-stage4-target-zlib: clean-stage4-target-zlib
+clean-stage4: clean-stage4-target-zlib
+clean-stage4-target-zlib:
+ @if [ $(current_stage) = stage4 ]; then \
+ [ -f $(TARGET_SUBDIR)/zlib/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage4-zlib/Makefile ] || exit 0; \
+ $(MAKE) stage4-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-zlib-bootstrap
+
+
+.PHONY: all-stageprofile-target-zlib maybe-all-stageprofile-target-zlib
+.PHONY: clean-stageprofile-target-zlib maybe-clean-stageprofile-target-zlib
+maybe-all-stageprofile-target-zlib:
+maybe-clean-stageprofile-target-zlib:
+@if target-zlib-bootstrap
+maybe-all-stageprofile-target-zlib: all-stageprofile-target-zlib
+all-stageprofile: all-stageprofile-target-zlib
+TARGET-stageprofile-target-zlib = $(TARGET-target-zlib)
+all-stageprofile-target-zlib: configure-stageprofile-target-zlib
+ @[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEprofile_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/zlib && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEprofile_TFLAGS)" \
+ $(TARGET-stageprofile-target-zlib)
+
+maybe-clean-stageprofile-target-zlib: clean-stageprofile-target-zlib
+clean-stageprofile: clean-stageprofile-target-zlib
+clean-stageprofile-target-zlib:
+ @if [ $(current_stage) = stageprofile ]; then \
+ [ -f $(TARGET_SUBDIR)/zlib/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageprofile-zlib/Makefile ] || exit 0; \
+ $(MAKE) stageprofile-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-zlib-bootstrap
+
+
+.PHONY: all-stagetrain-target-zlib maybe-all-stagetrain-target-zlib
+.PHONY: clean-stagetrain-target-zlib maybe-clean-stagetrain-target-zlib
+maybe-all-stagetrain-target-zlib:
+maybe-clean-stagetrain-target-zlib:
+@if target-zlib-bootstrap
+maybe-all-stagetrain-target-zlib: all-stagetrain-target-zlib
+all-stagetrain: all-stagetrain-target-zlib
+TARGET-stagetrain-target-zlib = $(TARGET-target-zlib)
+all-stagetrain-target-zlib: configure-stagetrain-target-zlib
+ @[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEtrain_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/zlib && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEtrain_TFLAGS)" \
+ $(TARGET-stagetrain-target-zlib)
+
+maybe-clean-stagetrain-target-zlib: clean-stagetrain-target-zlib
+clean-stagetrain: clean-stagetrain-target-zlib
+clean-stagetrain-target-zlib:
+ @if [ $(current_stage) = stagetrain ]; then \
+ [ -f $(TARGET_SUBDIR)/zlib/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stagetrain-zlib/Makefile ] || exit 0; \
+ $(MAKE) stagetrain-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-zlib-bootstrap
+
+
+.PHONY: all-stagefeedback-target-zlib maybe-all-stagefeedback-target-zlib
+.PHONY: clean-stagefeedback-target-zlib maybe-clean-stagefeedback-target-zlib
+maybe-all-stagefeedback-target-zlib:
+maybe-clean-stagefeedback-target-zlib:
+@if target-zlib-bootstrap
+maybe-all-stagefeedback-target-zlib: all-stagefeedback-target-zlib
+all-stagefeedback: all-stagefeedback-target-zlib
+TARGET-stagefeedback-target-zlib = $(TARGET-target-zlib)
+all-stagefeedback-target-zlib: configure-stagefeedback-target-zlib
+ @[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/zlib && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEfeedback_TFLAGS)" \
+ $(TARGET-stagefeedback-target-zlib)
+
+maybe-clean-stagefeedback-target-zlib: clean-stagefeedback-target-zlib
+clean-stagefeedback: clean-stagefeedback-target-zlib
+clean-stagefeedback-target-zlib:
+ @if [ $(current_stage) = stagefeedback ]; then \
+ [ -f $(TARGET_SUBDIR)/zlib/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stagefeedback-zlib/Makefile ] || exit 0; \
+ $(MAKE) stagefeedback-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-zlib-bootstrap
+
+
+.PHONY: all-stageautoprofile-target-zlib maybe-all-stageautoprofile-target-zlib
+.PHONY: clean-stageautoprofile-target-zlib maybe-clean-stageautoprofile-target-zlib
+maybe-all-stageautoprofile-target-zlib:
+maybe-clean-stageautoprofile-target-zlib:
+@if target-zlib-bootstrap
+maybe-all-stageautoprofile-target-zlib: all-stageautoprofile-target-zlib
+all-stageautoprofile: all-stageautoprofile-target-zlib
+TARGET-stageautoprofile-target-zlib = $(TARGET-target-zlib)
+all-stageautoprofile-target-zlib: configure-stageautoprofile-target-zlib
+ @[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $$s/gcc/config/i386/$(AUTO_PROFILE) \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)" \
+ $(TARGET-stageautoprofile-target-zlib)
+
+maybe-clean-stageautoprofile-target-zlib: clean-stageautoprofile-target-zlib
+clean-stageautoprofile: clean-stageautoprofile-target-zlib
+clean-stageautoprofile-target-zlib:
+ @if [ $(current_stage) = stageautoprofile ]; then \
+ [ -f $(TARGET_SUBDIR)/zlib/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageautoprofile-zlib/Makefile ] || exit 0; \
+ $(MAKE) stageautoprofile-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-zlib-bootstrap
+
+
+.PHONY: all-stageautofeedback-target-zlib maybe-all-stageautofeedback-target-zlib
+.PHONY: clean-stageautofeedback-target-zlib maybe-clean-stageautofeedback-target-zlib
+maybe-all-stageautofeedback-target-zlib:
+maybe-clean-stageautofeedback-target-zlib:
+@if target-zlib-bootstrap
+maybe-all-stageautofeedback-target-zlib: all-stageautofeedback-target-zlib
+all-stageautofeedback: all-stageautofeedback-target-zlib
+TARGET-stageautofeedback-target-zlib = $(TARGET-target-zlib)
+all-stageautofeedback-target-zlib: configure-stageautofeedback-target-zlib
+ @[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/zlib && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)" PERF_DATA=perf.data \
+ $(TARGET-stageautofeedback-target-zlib)
+
+maybe-clean-stageautofeedback-target-zlib: clean-stageautofeedback-target-zlib
+clean-stageautofeedback: clean-stageautofeedback-target-zlib
+clean-stageautofeedback-target-zlib:
+ @if [ $(current_stage) = stageautofeedback ]; then \
+ [ -f $(TARGET_SUBDIR)/zlib/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageautofeedback-zlib/Makefile ] || exit 0; \
+ $(MAKE) stageautofeedback-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/zlib && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-zlib-bootstrap
+
+
+
+
.PHONY: check-target-zlib maybe-check-target-zlib
@@ -57953,7 +60324,6 @@ configure-target-libatomic: stage_current
@if target-libatomic
maybe-configure-target-libatomic: configure-target-libatomic
configure-target-libatomic:
- @: $(MAKE); $(unstage)
@r=`${PWD_COMMAND}`; export r; \
s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
echo "Checking multilib configuration for libatomic..."; \
@@ -57991,6 +60361,412 @@ configure-target-libatomic:
+.PHONY: configure-stage1-target-libatomic maybe-configure-stage1-target-libatomic
+maybe-configure-stage1-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-configure-stage1-target-libatomic: configure-stage1-target-libatomic
+configure-stage1-target-libatomic:
+ @[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE1_TFLAGS)"; \
+ echo "Checking multilib configuration for libatomic..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libatomic/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libatomic/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libatomic/Makefile; \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libatomic/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 1 in $(TARGET_SUBDIR)/libatomic; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic; \
+ cd $(TARGET_SUBDIR)/libatomic || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libatomic/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libatomic; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ \
+ $(STAGE1_CONFIGURE_FLAGS)
+@endif target-libatomic-bootstrap
+
+.PHONY: configure-stage2-target-libatomic maybe-configure-stage2-target-libatomic
+maybe-configure-stage2-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-configure-stage2-target-libatomic: configure-stage2-target-libatomic
+configure-stage2-target-libatomic:
+ @[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE2_TFLAGS)"; \
+ echo "Checking multilib configuration for libatomic..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libatomic/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libatomic/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libatomic/Makefile; \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libatomic/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 2 in $(TARGET_SUBDIR)/libatomic; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic; \
+ cd $(TARGET_SUBDIR)/libatomic || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libatomic/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libatomic; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE2_CONFIGURE_FLAGS)
+@endif target-libatomic-bootstrap
+
+.PHONY: configure-stage3-target-libatomic maybe-configure-stage3-target-libatomic
+maybe-configure-stage3-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-configure-stage3-target-libatomic: configure-stage3-target-libatomic
+configure-stage3-target-libatomic:
+ @[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE3_TFLAGS)"; \
+ echo "Checking multilib configuration for libatomic..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libatomic/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libatomic/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libatomic/Makefile; \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libatomic/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 3 in $(TARGET_SUBDIR)/libatomic; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic; \
+ cd $(TARGET_SUBDIR)/libatomic || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libatomic/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libatomic; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE3_CONFIGURE_FLAGS)
+@endif target-libatomic-bootstrap
+
+.PHONY: configure-stage4-target-libatomic maybe-configure-stage4-target-libatomic
+maybe-configure-stage4-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-configure-stage4-target-libatomic: configure-stage4-target-libatomic
+configure-stage4-target-libatomic:
+ @[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE4_TFLAGS)"; \
+ echo "Checking multilib configuration for libatomic..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libatomic/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libatomic/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libatomic/Makefile; \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libatomic/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage 4 in $(TARGET_SUBDIR)/libatomic; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic; \
+ cd $(TARGET_SUBDIR)/libatomic || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libatomic/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libatomic; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGE4_CONFIGURE_FLAGS)
+@endif target-libatomic-bootstrap
+
+.PHONY: configure-stageprofile-target-libatomic maybe-configure-stageprofile-target-libatomic
+maybe-configure-stageprofile-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-configure-stageprofile-target-libatomic: configure-stageprofile-target-libatomic
+configure-stageprofile-target-libatomic:
+ @[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEprofile_TFLAGS)"; \
+ echo "Checking multilib configuration for libatomic..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libatomic/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libatomic/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libatomic/Makefile; \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libatomic/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage profile in $(TARGET_SUBDIR)/libatomic; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic; \
+ cd $(TARGET_SUBDIR)/libatomic || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libatomic/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libatomic; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEprofile_CONFIGURE_FLAGS)
+@endif target-libatomic-bootstrap
+
+.PHONY: configure-stagetrain-target-libatomic maybe-configure-stagetrain-target-libatomic
+maybe-configure-stagetrain-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-configure-stagetrain-target-libatomic: configure-stagetrain-target-libatomic
+configure-stagetrain-target-libatomic:
+ @[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEtrain_TFLAGS)"; \
+ echo "Checking multilib configuration for libatomic..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libatomic/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libatomic/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libatomic/Makefile; \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libatomic/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage train in $(TARGET_SUBDIR)/libatomic; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic; \
+ cd $(TARGET_SUBDIR)/libatomic || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libatomic/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libatomic; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEtrain_CONFIGURE_FLAGS)
+@endif target-libatomic-bootstrap
+
+.PHONY: configure-stagefeedback-target-libatomic maybe-configure-stagefeedback-target-libatomic
+maybe-configure-stagefeedback-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-configure-stagefeedback-target-libatomic: configure-stagefeedback-target-libatomic
+configure-stagefeedback-target-libatomic:
+ @[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+ echo "Checking multilib configuration for libatomic..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libatomic/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libatomic/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libatomic/Makefile; \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libatomic/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage feedback in $(TARGET_SUBDIR)/libatomic; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic; \
+ cd $(TARGET_SUBDIR)/libatomic || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libatomic/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libatomic; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEfeedback_CONFIGURE_FLAGS)
+@endif target-libatomic-bootstrap
+
+.PHONY: configure-stageautoprofile-target-libatomic maybe-configure-stageautoprofile-target-libatomic
+maybe-configure-stageautoprofile-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-configure-stageautoprofile-target-libatomic: configure-stageautoprofile-target-libatomic
+configure-stageautoprofile-target-libatomic:
+ @[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+ echo "Checking multilib configuration for libatomic..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libatomic/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libatomic/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libatomic/Makefile; \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libatomic/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage autoprofile in $(TARGET_SUBDIR)/libatomic; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic; \
+ cd $(TARGET_SUBDIR)/libatomic || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libatomic/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libatomic; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEautoprofile_CONFIGURE_FLAGS)
+@endif target-libatomic-bootstrap
+
+.PHONY: configure-stageautofeedback-target-libatomic maybe-configure-stageautofeedback-target-libatomic
+maybe-configure-stageautofeedback-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-configure-stageautofeedback-target-libatomic: configure-stageautofeedback-target-libatomic
+configure-stageautofeedback-target-libatomic:
+ @[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+ @$(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+ echo "Checking multilib configuration for libatomic..."; \
+ $(CC_FOR_TARGET) --print-multi-lib > $(TARGET_SUBDIR)/libatomic/multilib.tmp 2> /dev/null; \
+ if test -r $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ if cmp -s $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; then \
+ rm -f $(TARGET_SUBDIR)/libatomic/multilib.tmp; \
+ else \
+ rm -f $(TARGET_SUBDIR)/libatomic/Makefile; \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ else \
+ mv $(TARGET_SUBDIR)/libatomic/multilib.tmp $(TARGET_SUBDIR)/libatomic/multilib.out; \
+ fi; \
+ test ! -f $(TARGET_SUBDIR)/libatomic/Makefile || exit 0; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ CFLAGS="$(CFLAGS_FOR_TARGET)"; export CFLAGS; \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)"; export CXXFLAGS; \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)"; export LIBCFLAGS; \
+ echo Configuring stage autofeedback in $(TARGET_SUBDIR)/libatomic; \
+ $(SHELL) $(srcdir)/mkinstalldirs $(TARGET_SUBDIR)/libatomic; \
+ cd $(TARGET_SUBDIR)/libatomic || exit 1; \
+ case $(srcdir) in \
+ /* | [A-Za-z]:[\\/]*) topdir=$(srcdir) ;; \
+ *) topdir=`echo $(TARGET_SUBDIR)/libatomic/ | \
+ sed -e 's,\./,,g' -e 's,[^/]*/,../,g' `$(srcdir) ;; \
+ esac; \
+ module_srcdir=libatomic; \
+ $(SHELL) $$s/$$module_srcdir/configure \
+ --srcdir=$${topdir}/$$module_srcdir \
+ $(TARGET_CONFIGARGS) --build=${build_alias} --host=${target_alias} \
+ --target=${target_alias} \
+ --with-build-libsubdir=$(HOST_SUBDIR) \
+ $(STAGEautofeedback_CONFIGURE_FLAGS)
+@endif target-libatomic-bootstrap
+
+
+
.PHONY: all-target-libatomic maybe-all-target-libatomic
@@ -58002,7 +60778,6 @@ all-target-libatomic: stage_current
TARGET-target-libatomic=all
maybe-all-target-libatomic: all-target-libatomic
all-target-libatomic: configure-target-libatomic
- @: $(MAKE); $(unstage)
@r=`${PWD_COMMAND}`; export r; \
s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
$(NORMAL_TARGET_EXPORTS) \
@@ -58013,6 +60788,387 @@ all-target-libatomic: configure-target-libatomic
+.PHONY: all-stage1-target-libatomic maybe-all-stage1-target-libatomic
+.PHONY: clean-stage1-target-libatomic maybe-clean-stage1-target-libatomic
+maybe-all-stage1-target-libatomic:
+maybe-clean-stage1-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-all-stage1-target-libatomic: all-stage1-target-libatomic
+all-stage1: all-stage1-target-libatomic
+TARGET-stage1-target-libatomic = $(TARGET-target-libatomic)
+all-stage1-target-libatomic: configure-stage1-target-libatomic
+ @[ $(current_stage) = stage1 ] || $(MAKE) stage1-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE1_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ \
+ TFLAGS="$(STAGE1_TFLAGS)" \
+ $(TARGET-stage1-target-libatomic)
+
+maybe-clean-stage1-target-libatomic: clean-stage1-target-libatomic
+clean-stage1: clean-stage1-target-libatomic
+clean-stage1-target-libatomic:
+ @if [ $(current_stage) = stage1 ]; then \
+ [ -f $(TARGET_SUBDIR)/libatomic/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage1-libatomic/Makefile ] || exit 0; \
+ $(MAKE) stage1-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) \
+ clean
+@endif target-libatomic-bootstrap
+
+
+.PHONY: all-stage2-target-libatomic maybe-all-stage2-target-libatomic
+.PHONY: clean-stage2-target-libatomic maybe-clean-stage2-target-libatomic
+maybe-all-stage2-target-libatomic:
+maybe-clean-stage2-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-all-stage2-target-libatomic: all-stage2-target-libatomic
+all-stage2: all-stage2-target-libatomic
+TARGET-stage2-target-libatomic = $(TARGET-target-libatomic)
+all-stage2-target-libatomic: configure-stage2-target-libatomic
+ @[ $(current_stage) = stage2 ] || $(MAKE) stage2-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE2_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE2_TFLAGS)" \
+ $(TARGET-stage2-target-libatomic)
+
+maybe-clean-stage2-target-libatomic: clean-stage2-target-libatomic
+clean-stage2: clean-stage2-target-libatomic
+clean-stage2-target-libatomic:
+ @if [ $(current_stage) = stage2 ]; then \
+ [ -f $(TARGET_SUBDIR)/libatomic/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage2-libatomic/Makefile ] || exit 0; \
+ $(MAKE) stage2-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libatomic-bootstrap
+
+
+.PHONY: all-stage3-target-libatomic maybe-all-stage3-target-libatomic
+.PHONY: clean-stage3-target-libatomic maybe-clean-stage3-target-libatomic
+maybe-all-stage3-target-libatomic:
+maybe-clean-stage3-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-all-stage3-target-libatomic: all-stage3-target-libatomic
+all-stage3: all-stage3-target-libatomic
+TARGET-stage3-target-libatomic = $(TARGET-target-libatomic)
+all-stage3-target-libatomic: configure-stage3-target-libatomic
+ @[ $(current_stage) = stage3 ] || $(MAKE) stage3-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE3_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE3_TFLAGS)" \
+ $(TARGET-stage3-target-libatomic)
+
+maybe-clean-stage3-target-libatomic: clean-stage3-target-libatomic
+clean-stage3: clean-stage3-target-libatomic
+clean-stage3-target-libatomic:
+ @if [ $(current_stage) = stage3 ]; then \
+ [ -f $(TARGET_SUBDIR)/libatomic/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage3-libatomic/Makefile ] || exit 0; \
+ $(MAKE) stage3-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libatomic-bootstrap
+
+
+.PHONY: all-stage4-target-libatomic maybe-all-stage4-target-libatomic
+.PHONY: clean-stage4-target-libatomic maybe-clean-stage4-target-libatomic
+maybe-all-stage4-target-libatomic:
+maybe-clean-stage4-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-all-stage4-target-libatomic: all-stage4-target-libatomic
+all-stage4: all-stage4-target-libatomic
+TARGET-stage4-target-libatomic = $(TARGET-target-libatomic)
+all-stage4-target-libatomic: configure-stage4-target-libatomic
+ @[ $(current_stage) = stage4 ] || $(MAKE) stage4-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGE4_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGE4_TFLAGS)" \
+ $(TARGET-stage4-target-libatomic)
+
+maybe-clean-stage4-target-libatomic: clean-stage4-target-libatomic
+clean-stage4: clean-stage4-target-libatomic
+clean-stage4-target-libatomic:
+ @if [ $(current_stage) = stage4 ]; then \
+ [ -f $(TARGET_SUBDIR)/libatomic/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stage4-libatomic/Makefile ] || exit 0; \
+ $(MAKE) stage4-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libatomic-bootstrap
+
+
+.PHONY: all-stageprofile-target-libatomic maybe-all-stageprofile-target-libatomic
+.PHONY: clean-stageprofile-target-libatomic maybe-clean-stageprofile-target-libatomic
+maybe-all-stageprofile-target-libatomic:
+maybe-clean-stageprofile-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-all-stageprofile-target-libatomic: all-stageprofile-target-libatomic
+all-stageprofile: all-stageprofile-target-libatomic
+TARGET-stageprofile-target-libatomic = $(TARGET-target-libatomic)
+all-stageprofile-target-libatomic: configure-stageprofile-target-libatomic
+ @[ $(current_stage) = stageprofile ] || $(MAKE) stageprofile-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEprofile_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEprofile_TFLAGS)" \
+ $(TARGET-stageprofile-target-libatomic)
+
+maybe-clean-stageprofile-target-libatomic: clean-stageprofile-target-libatomic
+clean-stageprofile: clean-stageprofile-target-libatomic
+clean-stageprofile-target-libatomic:
+ @if [ $(current_stage) = stageprofile ]; then \
+ [ -f $(TARGET_SUBDIR)/libatomic/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageprofile-libatomic/Makefile ] || exit 0; \
+ $(MAKE) stageprofile-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libatomic-bootstrap
+
+
+.PHONY: all-stagetrain-target-libatomic maybe-all-stagetrain-target-libatomic
+.PHONY: clean-stagetrain-target-libatomic maybe-clean-stagetrain-target-libatomic
+maybe-all-stagetrain-target-libatomic:
+maybe-clean-stagetrain-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-all-stagetrain-target-libatomic: all-stagetrain-target-libatomic
+all-stagetrain: all-stagetrain-target-libatomic
+TARGET-stagetrain-target-libatomic = $(TARGET-target-libatomic)
+all-stagetrain-target-libatomic: configure-stagetrain-target-libatomic
+ @[ $(current_stage) = stagetrain ] || $(MAKE) stagetrain-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEtrain_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEtrain_TFLAGS)" \
+ $(TARGET-stagetrain-target-libatomic)
+
+maybe-clean-stagetrain-target-libatomic: clean-stagetrain-target-libatomic
+clean-stagetrain: clean-stagetrain-target-libatomic
+clean-stagetrain-target-libatomic:
+ @if [ $(current_stage) = stagetrain ]; then \
+ [ -f $(TARGET_SUBDIR)/libatomic/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stagetrain-libatomic/Makefile ] || exit 0; \
+ $(MAKE) stagetrain-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libatomic-bootstrap
+
+
+.PHONY: all-stagefeedback-target-libatomic maybe-all-stagefeedback-target-libatomic
+.PHONY: clean-stagefeedback-target-libatomic maybe-clean-stagefeedback-target-libatomic
+maybe-all-stagefeedback-target-libatomic:
+maybe-clean-stagefeedback-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-all-stagefeedback-target-libatomic: all-stagefeedback-target-libatomic
+all-stagefeedback: all-stagefeedback-target-libatomic
+TARGET-stagefeedback-target-libatomic = $(TARGET-target-libatomic)
+all-stagefeedback-target-libatomic: configure-stagefeedback-target-libatomic
+ @[ $(current_stage) = stagefeedback ] || $(MAKE) stagefeedback-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEfeedback_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEfeedback_TFLAGS)" \
+ $(TARGET-stagefeedback-target-libatomic)
+
+maybe-clean-stagefeedback-target-libatomic: clean-stagefeedback-target-libatomic
+clean-stagefeedback: clean-stagefeedback-target-libatomic
+clean-stagefeedback-target-libatomic:
+ @if [ $(current_stage) = stagefeedback ]; then \
+ [ -f $(TARGET_SUBDIR)/libatomic/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stagefeedback-libatomic/Makefile ] || exit 0; \
+ $(MAKE) stagefeedback-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libatomic-bootstrap
+
+
+.PHONY: all-stageautoprofile-target-libatomic maybe-all-stageautoprofile-target-libatomic
+.PHONY: clean-stageautoprofile-target-libatomic maybe-clean-stageautoprofile-target-libatomic
+maybe-all-stageautoprofile-target-libatomic:
+maybe-clean-stageautoprofile-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-all-stageautoprofile-target-libatomic: all-stageautoprofile-target-libatomic
+all-stageautoprofile: all-stageautoprofile-target-libatomic
+TARGET-stageautoprofile-target-libatomic = $(TARGET-target-libatomic)
+all-stageautoprofile-target-libatomic: configure-stageautoprofile-target-libatomic
+ @[ $(current_stage) = stageautoprofile ] || $(MAKE) stageautoprofile-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $$s/gcc/config/i386/$(AUTO_PROFILE) \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEautoprofile_TFLAGS)" \
+ $(TARGET-stageautoprofile-target-libatomic)
+
+maybe-clean-stageautoprofile-target-libatomic: clean-stageautoprofile-target-libatomic
+clean-stageautoprofile: clean-stageautoprofile-target-libatomic
+clean-stageautoprofile-target-libatomic:
+ @if [ $(current_stage) = stageautoprofile ]; then \
+ [ -f $(TARGET_SUBDIR)/libatomic/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageautoprofile-libatomic/Makefile ] || exit 0; \
+ $(MAKE) stageautoprofile-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libatomic-bootstrap
+
+
+.PHONY: all-stageautofeedback-target-libatomic maybe-all-stageautofeedback-target-libatomic
+.PHONY: clean-stageautofeedback-target-libatomic maybe-clean-stageautofeedback-target-libatomic
+maybe-all-stageautofeedback-target-libatomic:
+maybe-clean-stageautofeedback-target-libatomic:
+@if target-libatomic-bootstrap
+maybe-all-stageautofeedback-target-libatomic: all-stageautofeedback-target-libatomic
+all-stageautofeedback: all-stageautofeedback-target-libatomic
+TARGET-stageautofeedback-target-libatomic = $(TARGET-target-libatomic)
+all-stageautofeedback-target-libatomic: configure-stageautofeedback-target-libatomic
+ @[ $(current_stage) = stageautofeedback ] || $(MAKE) stageautofeedback-start
+ @r=`${PWD_COMMAND}`; export r; \
+ s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)"; \
+ $(NORMAL_TARGET_EXPORTS) \
+ \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ \
+ $(MAKE) $(BASE_FLAGS_TO_PASS) \
+ CFLAGS="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS="$(LIBCFLAGS_FOR_TARGET)" \
+ CFLAGS_FOR_TARGET="$(CFLAGS_FOR_TARGET)" \
+ CXXFLAGS_FOR_TARGET="$(CXXFLAGS_FOR_TARGET)" \
+ LIBCFLAGS_FOR_TARGET="$(LIBCFLAGS_FOR_TARGET)" \
+ $(EXTRA_TARGET_FLAGS) \
+ TFLAGS="$(STAGEautofeedback_TFLAGS)" PERF_DATA=perf.data \
+ $(TARGET-stageautofeedback-target-libatomic)
+
+maybe-clean-stageautofeedback-target-libatomic: clean-stageautofeedback-target-libatomic
+clean-stageautofeedback: clean-stageautofeedback-target-libatomic
+clean-stageautofeedback-target-libatomic:
+ @if [ $(current_stage) = stageautofeedback ]; then \
+ [ -f $(TARGET_SUBDIR)/libatomic/Makefile ] || exit 0; \
+ else \
+ [ -f $(TARGET_SUBDIR)/stageautofeedback-libatomic/Makefile ] || exit 0; \
+ $(MAKE) stageautofeedback-start; \
+ fi; \
+ cd $(TARGET_SUBDIR)/libatomic && \
+ $(MAKE) $(EXTRA_TARGET_FLAGS) clean
+@endif target-libatomic-bootstrap
+
+
+
+
.PHONY: check-target-libatomic maybe-check-target-libatomic
@@ -61936,17 +65092,41 @@ configure-stagetrain-target-libgcc: maybe-all-stagetrain-gcc
configure-stagefeedback-target-libgcc: maybe-all-stagefeedback-gcc
configure-stageautoprofile-target-libgcc: maybe-all-stageautoprofile-gcc
configure-stageautofeedback-target-libgcc: maybe-all-stageautofeedback-gcc
-configure-target-libbacktrace: stage_last
+configure-stage1-target-libbacktrace: maybe-all-stage1-gcc
+configure-stage2-target-libbacktrace: maybe-all-stage2-gcc
+configure-stage3-target-libbacktrace: maybe-all-stage3-gcc
+configure-stage4-target-libbacktrace: maybe-all-stage4-gcc
+configure-stageprofile-target-libbacktrace: maybe-all-stageprofile-gcc
+configure-stagetrain-target-libbacktrace: maybe-all-stagetrain-gcc
+configure-stagefeedback-target-libbacktrace: maybe-all-stagefeedback-gcc
+configure-stageautoprofile-target-libbacktrace: maybe-all-stageautoprofile-gcc
+configure-stageautofeedback-target-libbacktrace: maybe-all-stageautofeedback-gcc
configure-target-libquadmath: stage_last
configure-target-libgfortran: stage_last
configure-target-libobjc: stage_last
configure-target-libgo: stage_last
-configure-target-libphobos: stage_last
+configure-stage1-target-libphobos: maybe-all-stage1-gcc
+configure-stage2-target-libphobos: maybe-all-stage2-gcc
+configure-stage3-target-libphobos: maybe-all-stage3-gcc
+configure-stage4-target-libphobos: maybe-all-stage4-gcc
+configure-stageprofile-target-libphobos: maybe-all-stageprofile-gcc
+configure-stagetrain-target-libphobos: maybe-all-stagetrain-gcc
+configure-stagefeedback-target-libphobos: maybe-all-stagefeedback-gcc
+configure-stageautoprofile-target-libphobos: maybe-all-stageautoprofile-gcc
+configure-stageautofeedback-target-libphobos: maybe-all-stageautofeedback-gcc
configure-target-libtermcap: stage_last
configure-target-winsup: stage_last
configure-target-libgloss: stage_last
configure-target-libffi: stage_last
-configure-target-zlib: stage_last
+configure-stage1-target-zlib: maybe-all-stage1-gcc
+configure-stage2-target-zlib: maybe-all-stage2-gcc
+configure-stage3-target-zlib: maybe-all-stage3-gcc
+configure-stage4-target-zlib: maybe-all-stage4-gcc
+configure-stageprofile-target-zlib: maybe-all-stageprofile-gcc
+configure-stagetrain-target-zlib: maybe-all-stagetrain-gcc
+configure-stagefeedback-target-zlib: maybe-all-stagefeedback-gcc
+configure-stageautoprofile-target-zlib: maybe-all-stageautoprofile-gcc
+configure-stageautofeedback-target-zlib: maybe-all-stageautofeedback-gcc
configure-target-rda: stage_last
configure-target-libada: stage_last
configure-stage1-target-libgomp: maybe-all-stage1-gcc
@@ -61959,7 +65139,15 @@ configure-stagefeedback-target-libgomp: maybe-all-stagefeedback-gcc
configure-stageautoprofile-target-libgomp: maybe-all-stageautoprofile-gcc
configure-stageautofeedback-target-libgomp: maybe-all-stageautofeedback-gcc
configure-target-libitm: stage_last
-configure-target-libatomic: stage_last
+configure-stage1-target-libatomic: maybe-all-stage1-gcc
+configure-stage2-target-libatomic: maybe-all-stage2-gcc
+configure-stage3-target-libatomic: maybe-all-stage3-gcc
+configure-stage4-target-libatomic: maybe-all-stage4-gcc
+configure-stageprofile-target-libatomic: maybe-all-stageprofile-gcc
+configure-stagetrain-target-libatomic: maybe-all-stagetrain-gcc
+configure-stagefeedback-target-libatomic: maybe-all-stagefeedback-gcc
+configure-stageautoprofile-target-libatomic: maybe-all-stageautoprofile-gcc
+configure-stageautofeedback-target-libatomic: maybe-all-stageautofeedback-gcc
@endif gcc-bootstrap
@if gcc-no-bootstrap
@@ -63102,17 +66290,58 @@ all-flex: maybe-all-build-bison
all-flex: maybe-all-m4
all-flex: maybe-all-build-texinfo
all-m4: maybe-all-build-texinfo
-configure-target-fastjar: maybe-configure-target-zlib
-all-target-fastjar: maybe-all-target-zlib
configure-target-libgo: maybe-configure-target-libffi
-all-target-libgo: maybe-all-target-libbacktrace
all-target-libgo: maybe-all-target-libffi
-all-target-libgo: maybe-all-target-libatomic
configure-target-libphobos: maybe-configure-target-libbacktrace
+configure-stage1-target-libphobos: maybe-configure-stage1-target-libbacktrace
+configure-stage2-target-libphobos: maybe-configure-stage2-target-libbacktrace
+configure-stage3-target-libphobos: maybe-configure-stage3-target-libbacktrace
+configure-stage4-target-libphobos: maybe-configure-stage4-target-libbacktrace
+configure-stageprofile-target-libphobos: maybe-configure-stageprofile-target-libbacktrace
+configure-stagetrain-target-libphobos: maybe-configure-stagetrain-target-libbacktrace
+configure-stagefeedback-target-libphobos: maybe-configure-stagefeedback-target-libbacktrace
+configure-stageautoprofile-target-libphobos: maybe-configure-stageautoprofile-target-libbacktrace
+configure-stageautofeedback-target-libphobos: maybe-configure-stageautofeedback-target-libbacktrace
configure-target-libphobos: maybe-configure-target-zlib
+configure-stage1-target-libphobos: maybe-configure-stage1-target-zlib
+configure-stage2-target-libphobos: maybe-configure-stage2-target-zlib
+configure-stage3-target-libphobos: maybe-configure-stage3-target-zlib
+configure-stage4-target-libphobos: maybe-configure-stage4-target-zlib
+configure-stageprofile-target-libphobos: maybe-configure-stageprofile-target-zlib
+configure-stagetrain-target-libphobos: maybe-configure-stagetrain-target-zlib
+configure-stagefeedback-target-libphobos: maybe-configure-stagefeedback-target-zlib
+configure-stageautoprofile-target-libphobos: maybe-configure-stageautoprofile-target-zlib
+configure-stageautofeedback-target-libphobos: maybe-configure-stageautofeedback-target-zlib
all-target-libphobos: maybe-all-target-libbacktrace
+all-stage1-target-libphobos: maybe-all-stage1-target-libbacktrace
+all-stage2-target-libphobos: maybe-all-stage2-target-libbacktrace
+all-stage3-target-libphobos: maybe-all-stage3-target-libbacktrace
+all-stage4-target-libphobos: maybe-all-stage4-target-libbacktrace
+all-stageprofile-target-libphobos: maybe-all-stageprofile-target-libbacktrace
+all-stagetrain-target-libphobos: maybe-all-stagetrain-target-libbacktrace
+all-stagefeedback-target-libphobos: maybe-all-stagefeedback-target-libbacktrace
+all-stageautoprofile-target-libphobos: maybe-all-stageautoprofile-target-libbacktrace
+all-stageautofeedback-target-libphobos: maybe-all-stageautofeedback-target-libbacktrace
all-target-libphobos: maybe-all-target-zlib
+all-stage1-target-libphobos: maybe-all-stage1-target-zlib
+all-stage2-target-libphobos: maybe-all-stage2-target-zlib
+all-stage3-target-libphobos: maybe-all-stage3-target-zlib
+all-stage4-target-libphobos: maybe-all-stage4-target-zlib
+all-stageprofile-target-libphobos: maybe-all-stageprofile-target-zlib
+all-stagetrain-target-libphobos: maybe-all-stagetrain-target-zlib
+all-stagefeedback-target-libphobos: maybe-all-stagefeedback-target-zlib
+all-stageautoprofile-target-libphobos: maybe-all-stageautoprofile-target-zlib
+all-stageautofeedback-target-libphobos: maybe-all-stageautofeedback-target-zlib
all-target-libphobos: maybe-all-target-libatomic
+all-stage1-target-libphobos: maybe-all-stage1-target-libatomic
+all-stage2-target-libphobos: maybe-all-stage2-target-libatomic
+all-stage3-target-libphobos: maybe-all-stage3-target-libatomic
+all-stage4-target-libphobos: maybe-all-stage4-target-libatomic
+all-stageprofile-target-libphobos: maybe-all-stageprofile-target-libatomic
+all-stagetrain-target-libphobos: maybe-all-stagetrain-target-libatomic
+all-stagefeedback-target-libphobos: maybe-all-stagefeedback-target-libatomic
+all-stageautoprofile-target-libphobos: maybe-all-stageautoprofile-target-libatomic
+all-stageautofeedback-target-libphobos: maybe-all-stageautofeedback-target-libatomic
configure-target-libstdc++-v3: maybe-configure-target-libgomp
configure-stage1-target-libstdc++-v3: maybe-configure-stage1-target-libgomp
configure-stage2-target-libstdc++-v3: maybe-configure-stage2-target-libgomp
@@ -63169,7 +66398,6 @@ install-target-libstdc++-v3: maybe-install-target-libgcc
all-target-libgloss: maybe-all-target-newlib
all-target-winsup: maybe-all-target-libtermcap
configure-target-libgfortran: maybe-all-target-libquadmath
-configure-target-libgfortran: maybe-all-target-libbacktrace
@if gcc-bootstrap
@@ -63226,12 +66454,17 @@ all-fastjar: maybe-all-libiberty
all-bison: maybe-all-intl
all-flex: maybe-all-intl
all-m4: maybe-all-intl
+configure-target-fastjar: maybe-configure-target-zlib
+all-target-fastjar: maybe-all-target-zlib
configure-target-libgo: maybe-all-target-libstdc++-v3
configure-target-libffi: maybe-all-target-libstdc++-v3
+all-target-libgo: maybe-all-target-libbacktrace
+all-target-libgo: maybe-all-target-libatomic
configure-target-liboffloadmic: maybe-configure-target-libgomp
all-target-liboffloadmic: maybe-all-target-libgomp
configure-target-newlib: maybe-all-binutils
configure-target-newlib: maybe-all-ld
+configure-target-libgfortran: maybe-all-target-libbacktrace
@endunless gcc-bootstrap
# Dependencies for target modules on other target modules are
@@ -63267,6 +66500,33 @@ configure-stagetrain-target-libvtv: maybe-all-stagetrain-target-libgcc
configure-stagefeedback-target-libvtv: maybe-all-stagefeedback-target-libgcc
configure-stageautoprofile-target-libvtv: maybe-all-stageautoprofile-target-libgcc
configure-stageautofeedback-target-libvtv: maybe-all-stageautofeedback-target-libgcc
+configure-stage1-target-libbacktrace: maybe-all-stage1-target-libgcc
+configure-stage2-target-libbacktrace: maybe-all-stage2-target-libgcc
+configure-stage3-target-libbacktrace: maybe-all-stage3-target-libgcc
+configure-stage4-target-libbacktrace: maybe-all-stage4-target-libgcc
+configure-stageprofile-target-libbacktrace: maybe-all-stageprofile-target-libgcc
+configure-stagetrain-target-libbacktrace: maybe-all-stagetrain-target-libgcc
+configure-stagefeedback-target-libbacktrace: maybe-all-stagefeedback-target-libgcc
+configure-stageautoprofile-target-libbacktrace: maybe-all-stageautoprofile-target-libgcc
+configure-stageautofeedback-target-libbacktrace: maybe-all-stageautofeedback-target-libgcc
+configure-stage1-target-libphobos: maybe-all-stage1-target-libgcc
+configure-stage2-target-libphobos: maybe-all-stage2-target-libgcc
+configure-stage3-target-libphobos: maybe-all-stage3-target-libgcc
+configure-stage4-target-libphobos: maybe-all-stage4-target-libgcc
+configure-stageprofile-target-libphobos: maybe-all-stageprofile-target-libgcc
+configure-stagetrain-target-libphobos: maybe-all-stagetrain-target-libgcc
+configure-stagefeedback-target-libphobos: maybe-all-stagefeedback-target-libgcc
+configure-stageautoprofile-target-libphobos: maybe-all-stageautoprofile-target-libgcc
+configure-stageautofeedback-target-libphobos: maybe-all-stageautofeedback-target-libgcc
+configure-stage1-target-zlib: maybe-all-stage1-target-libgcc
+configure-stage2-target-zlib: maybe-all-stage2-target-libgcc
+configure-stage3-target-zlib: maybe-all-stage3-target-libgcc
+configure-stage4-target-zlib: maybe-all-stage4-target-libgcc
+configure-stageprofile-target-zlib: maybe-all-stageprofile-target-libgcc
+configure-stagetrain-target-zlib: maybe-all-stagetrain-target-libgcc
+configure-stagefeedback-target-zlib: maybe-all-stagefeedback-target-libgcc
+configure-stageautoprofile-target-zlib: maybe-all-stageautoprofile-target-libgcc
+configure-stageautofeedback-target-zlib: maybe-all-stageautofeedback-target-libgcc
configure-stage1-target-libgomp: maybe-all-stage1-target-libgcc
configure-stage2-target-libgomp: maybe-all-stage2-target-libgcc
configure-stage3-target-libgomp: maybe-all-stage3-target-libgcc
@@ -63276,6 +66536,15 @@ configure-stagetrain-target-libgomp: maybe-all-stagetrain-target-libgcc
configure-stagefeedback-target-libgomp: maybe-all-stagefeedback-target-libgcc
configure-stageautoprofile-target-libgomp: maybe-all-stageautoprofile-target-libgcc
configure-stageautofeedback-target-libgomp: maybe-all-stageautofeedback-target-libgcc
+configure-stage1-target-libatomic: maybe-all-stage1-target-libgcc
+configure-stage2-target-libatomic: maybe-all-stage2-target-libgcc
+configure-stage3-target-libatomic: maybe-all-stage3-target-libgcc
+configure-stage4-target-libatomic: maybe-all-stage4-target-libgcc
+configure-stageprofile-target-libatomic: maybe-all-stageprofile-target-libgcc
+configure-stagetrain-target-libatomic: maybe-all-stagetrain-target-libgcc
+configure-stagefeedback-target-libatomic: maybe-all-stagefeedback-target-libgcc
+configure-stageautoprofile-target-libatomic: maybe-all-stageautoprofile-target-libgcc
+configure-stageautofeedback-target-libatomic: maybe-all-stageautofeedback-target-libgcc
@endif gcc-bootstrap
@if gcc-no-bootstrap
diff --git a/Makefile.tpl b/Makefile.tpl
index 213052f8226..21fece89280 100644
--- a/Makefile.tpl
+++ b/Makefile.tpl
@@ -276,11 +276,14 @@ POSTSTAGE1_HOST_EXPORTS = \
$(POSTSTAGE1_CXX_EXPORT) \
$(LTO_EXPORTS) \
GDC="$$r/$(HOST_SUBDIR)/prev-gcc/gdc$(exeext) -B$$r/$(HOST_SUBDIR)/prev-gcc/ \
- -B$(build_tooldir)/bin/ $(GDC_FLAGS_FOR_TARGET) \
+ -B$(build_tooldir)/bin/ $(GDCFLAGS_FOR_TARGET) \
+ -B$$r/prev-$(TARGET_SUBDIR)/libphobos/libdruntime/gcc \
-B$$r/prev-$(TARGET_SUBDIR)/libphobos/src \
+ -B$$r/prev-$(TARGET_SUBDIR)/libphobos/src/.libs \
-I$$r/prev-$(TARGET_SUBDIR)/libphobos/libdruntime -I$$s/libphobos/libdruntime \
-L$$r/prev-$(TARGET_SUBDIR)/libphobos/src/.libs \
- -L$$r/prev-$(TARGET_SUBDIR)/libphobos/libdruntime/.libs"; \
+ -B$$r/prev-$(TARGET_SUBDIR)/libstdc++-v3/src/.libs \
+ -L$$r/prev-$(TARGET_SUBDIR)/libstdc++-v3/src/.libs"; \
export GDC; \
GDC_FOR_BUILD="$$GDC"; export GDC_FOR_BUILD; \
GNATBIND="$$r/$(HOST_SUBDIR)/prev-gcc/gnatbind"; export GNATBIND; \
@@ -487,6 +490,11 @@ STAGE1_CONFIGURE_FLAGS = --disable-intermodule $(STAGE1_CHECKING) \
--disable-coverage --enable-languages="$(STAGE1_LANGUAGES)" \
--disable-build-format-warnings
+@if target-libphobos-bootstrap
+STAGE1_CONFIGURE_FLAGS += --with-libphobos-druntime-only
+STAGE2_CONFIGURE_FLAGS += --with-libphobos-druntime-only
+@endif target-libphobos-bootstrap
+
# When using the slow stage1 compiler disable IL verification and forcefully
# enable it when using the stage2 compiler instead. As we later compare
# stage2 and stage3 we are merely avoid doing redundant work, plus we apply
diff --git a/config/acx.m4 b/config/acx.m4
index 87c1b5e2932..3c65d820628 100644
--- a/config/acx.m4
+++ b/config/acx.m4
@@ -420,6 +420,18 @@ else
fi
])
+# Test for D.
+AC_DEFUN([ACX_PROG_GDC],
+[AC_REQUIRE([AC_CHECK_TOOL_PREFIX])
+AC_REQUIRE([AC_PROG_CC])
+AC_CHECK_TOOL(GDC, gdc, no)
+if test "x$GDC" != xno; then
+ have_gdc=yes
+else
+ have_gdc=no
+fi
+])
+
dnl 'make compare' can be significantly faster, if cmp itself can
dnl skip bytes instead of using tail. The test being performed is
dnl "if cmp --ignore-initial=2 t1 t2 && ! cmp --ignore-initial=1 t1 t2"
diff --git a/configure b/configure
index 3062495da31..f8e6e2c3020 100755
--- a/configure
+++ b/configure
@@ -619,6 +619,7 @@ GFORTRAN_FOR_TARGET
GCC_FOR_TARGET
CXX_FOR_TARGET
CC_FOR_TARGET
+GDCFLAGS
READELF
OTOOL
OBJDUMP
@@ -702,6 +703,7 @@ gmplibs
HAVE_CXX11_FOR_BUILD
HAVE_CXX11
do_compare
+GDC
GNATMAKE
GNATBIND
ac_ct_CXX
@@ -5625,6 +5627,106 @@ else
have_gnat=no
fi
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gdc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gdc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_GDC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$GDC"; then
+ ac_cv_prog_GDC="$GDC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_GDC="${ac_tool_prefix}gdc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+GDC=$ac_cv_prog_GDC
+if test -n "$GDC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GDC" >&5
+$as_echo "$GDC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_GDC"; then
+ ac_ct_GDC=$GDC
+ # Extract the first word of "gdc", so it can be a program name with args.
+set dummy gdc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_GDC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_GDC"; then
+ ac_cv_prog_ac_ct_GDC="$ac_ct_GDC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_GDC="gdc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_GDC=$ac_cv_prog_ac_ct_GDC
+if test -n "$ac_ct_GDC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_GDC" >&5
+$as_echo "$ac_ct_GDC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_GDC" = x; then
+ GDC="no"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ GDC=$ac_ct_GDC
+ fi
+else
+ GDC="$ac_cv_prog_GDC"
+fi
+
+if test "x$GDC" != xno; then
+ have_gdc=yes
+else
+ have_gdc=no
+fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to compare bootstrapped objects" >&5
$as_echo_n "checking how to compare bootstrapped objects... " >&6; }
if ${gcc_cv_prog_cmp_skip+:} false; then :
@@ -8679,6 +8781,23 @@ $as_echo "$as_me: WARNING: GNAT is required to build $language" >&2;}
;;
esac
+ # Disable D if no preexisting GDC is available.
+ case ${add_this_lang}:${language}:${have_gdc} in
+ yes:d:no)
+ # Specifically requested language; tell them.
+ as_fn_error $? "GDC is required to build $language" "$LINENO" 5
+ ;;
+ all:d:no)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: GDC is required to build $language" >&5
+$as_echo "$as_me: WARNING: GDC is required to build $language" >&2;}
+ add_this_lang=unsupported
+ ;;
+ *:d:no)
+ # Silently disable.
+ add_this_lang=unsupported
+ ;;
+ esac
+
# Disable jit if -enable-host-shared not specified
# but not if building for Mingw. All code in Windows
# is position independent code (PIC).
@@ -8748,7 +8867,7 @@ $as_echo "$as_me: WARNING: ${language} not supported for this target" >&2;}
*) stage1_languages="${stage1_languages}${language}," ;;
esac
# We need to bootstrap any supporting libraries.
- bootstrap_target_libs="${bootstrap_target_libs}${target_libs},"
+ bootstrap_target_libs=`echo "${bootstrap_target_libs}${target_libs}," | sed "s/ /,/g"`
;;
esac
;;
@@ -9517,6 +9636,16 @@ if echo " ${target_configdirs} " | grep " libvtv " > /dev/null 2>&1 &&
bootstrap_target_libs=${bootstrap_target_libs}target-libvtv,
fi
+# If we are building libatomic and the list of enabled languages includes the
+# D frontend, bootstrap it.
+if echo " ${target_configdirs} " | grep " libatomic " > /dev/null 2>&1; then
+ case ,${enable_languages}, in
+ *,d,*)
+ bootstrap_target_libs=${bootstrap_target_libs}target-libatomic,
+ ;;
+ esac
+fi
+
# Determine whether gdb needs tk/tcl or not.
# Use 'maybe' since enable_gdbtk might be true even if tk isn't available
# and in that case we want gdb to be built without tk. Ugh!
@@ -12614,6 +12743,8 @@ fi
+
+
# Target tools.
# Check whether --with-build-time-tools was given.
diff --git a/configure.ac b/configure.ac
index bed60bcaf72..2de381fe863 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1406,6 +1406,7 @@ int main() {}])],
fi
ACX_PROG_GNAT
+ACX_PROG_GDC
ACX_PROG_CMP_IGNORE_INITIAL
AC_ARG_ENABLE([bootstrap],
@@ -2087,6 +2088,22 @@ if test -d ${srcdir}/gcc; then
;;
esac
+ # Disable D if no preexisting GDC is available.
+ case ${add_this_lang}:${language}:${have_gdc} in
+ yes:d:no)
+ # Specifically requested language; tell them.
+ AC_MSG_ERROR([GDC is required to build $language])
+ ;;
+ all:d:no)
+ AC_MSG_WARN([GDC is required to build $language])
+ add_this_lang=unsupported
+ ;;
+ *:d:no)
+ # Silently disable.
+ add_this_lang=unsupported
+ ;;
+ esac
+
# Disable jit if -enable-host-shared not specified
# but not if building for Mingw. All code in Windows
# is position independent code (PIC).
@@ -2154,7 +2171,7 @@ directories, to avoid imposing the performance cost of
*) stage1_languages="${stage1_languages}${language}," ;;
esac
# We need to bootstrap any supporting libraries.
- bootstrap_target_libs="${bootstrap_target_libs}${target_libs},"
+ bootstrap_target_libs=`echo "${bootstrap_target_libs}${target_libs}," | sed "s/ /,/g"`
;;
esac
;;
@@ -2837,6 +2854,16 @@ if echo " ${target_configdirs} " | grep " libvtv " > /dev/null 2>&1 &&
bootstrap_target_libs=${bootstrap_target_libs}target-libvtv,
fi
+# If we are building libatomic and the list of enabled languages includes the
+# D frontend, bootstrap it.
+if echo " ${target_configdirs} " | grep " libatomic " > /dev/null 2>&1; then
+ case ,${enable_languages}, in
+ *,d,*)
+ bootstrap_target_libs=${bootstrap_target_libs}target-libatomic,
+ ;;
+ esac
+fi
+
# Determine whether gdb needs tk/tcl or not.
# Use 'maybe' since enable_gdbtk might be true even if tk isn't available
# and in that case we want gdb to be built without tk. Ugh!
@@ -3505,6 +3532,8 @@ AC_SUBST(CC)
AC_SUBST(CXX)
AC_SUBST(CFLAGS)
AC_SUBST(CXXFLAGS)
+AC_SUBST(GDC)
+AC_SUBST(GDCFLAGS)
# Target tools.
AC_ARG_WITH([build-time-tools],
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 7054201db52..81ea8a73b99 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1092,6 +1092,10 @@ SYSLIBS = @GNAT_LIBEXC@
GNATBIND = @GNATBIND@
GNATMAKE = @GNATMAKE@
+# Used from d/Make-lang.in
+GDC = @GDC@
+GDCFLAGS = @GDCFLAGS@
+
# Libs needed (at present) just for jcf-dump.
LDEXP_LIB = @LDEXP_LIB@
diff --git a/gcc/configure b/gcc/configure
index fab76212f4c..a5160da83ec 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -858,6 +858,8 @@ EGREP
GREP
CXXCPP
PICFLAG_FOR_TARGET
+GDCFLAGS
+GDC
GNATMAKE
GNATBIND
ac_ct_CXX
@@ -5257,6 +5259,106 @@ else
fi
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gdc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gdc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_GDC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$GDC"; then
+ ac_cv_prog_GDC="$GDC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_GDC="${ac_tool_prefix}gdc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+GDC=$ac_cv_prog_GDC
+if test -n "$GDC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GDC" >&5
+$as_echo "$GDC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_GDC"; then
+ ac_ct_GDC=$GDC
+ # Extract the first word of "gdc", so it can be a program name with args.
+set dummy gdc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_GDC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_GDC"; then
+ ac_cv_prog_ac_ct_GDC="$ac_ct_GDC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_GDC="gdc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_GDC=$ac_cv_prog_ac_ct_GDC
+if test -n "$ac_ct_GDC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_GDC" >&5
+$as_echo "$ac_ct_GDC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_GDC" = x; then
+ GDC="no"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ GDC=$ac_ct_GDC
+ fi
+else
+ GDC="$ac_cv_prog_GDC"
+fi
+
+if test "x$GDC" != xno; then
+ have_gdc=yes
+else
+ have_gdc=no
+fi
+
+
# Do configure tests with the C++ compiler, since that's what we build with.
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
@@ -5275,6 +5377,7 @@ esac
+
# Determine PICFLAG for target gnatlib.
@@ -19458,7 +19561,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 19461 "configure"
+#line 19564 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -19564,7 +19667,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 19567 "configure"
+#line 19670 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
diff --git a/gcc/configure.ac b/gcc/configure.ac
index aa2f6d834ed..df4d2d8863c 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -435,6 +435,7 @@ rm -f a.out a.exe b.out
AC_PROG_CC
AC_PROG_CXX
ACX_PROG_GNAT([-I"$srcdir"/ada/libgnat])
+ACX_PROG_GDC([-I"$srcdir"/d])
# Do configure tests with the C++ compiler, since that's what we build with.
AC_LANG(C++)
@@ -448,6 +449,7 @@ case "$CC" in
esac
AC_SUBST(CFLAGS)
AC_SUBST(CXXFLAGS)
+AC_SUBST(GDCFLAGS)
# Determine PICFLAG for target gnatlib.
GCC_PICFLAG_FOR_TARGET
diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in
index 1ed813443b3..4c0a0321eba 100644
--- a/gcc/d/Make-lang.in
+++ b/gcc/d/Make-lang.in
@@ -46,30 +46,61 @@ gdc-cross$(exeext): gdc$(exeext)
-rm -f gdc-cross$(exeext)
cp gdc$(exeext) gdc-cross$(exeext)
-# Filter out pedantic and virtual overload warnings.
-d-warn = $(filter-out -pedantic -Woverloaded-virtual, $(STRICT_WARN))
-
-# Also filter out warnings for missing format attributes in the D Frontend.
-DMD_WARN_CXXFLAGS = $(filter-out -Wmissing-format-attribute, $(WARN_CXXFLAGS))
-DMD_COMPILE = $(subst $(WARN_CXXFLAGS), $(DMD_WARN_CXXFLAGS), $(COMPILE))
+# Use strict warnings.
+d-warn = $(STRICT_WARN)
+
+# D compiler and flags for building the front-end.
+ifeq ($(TREECHECKING),)
+CHECKING_DFLAGS = -frelease
+else
+CHECKING_DFLAGS =
+endif
+WARN_DFLAGS = -Wall -Wdeprecated $(NOCOMMON_FLAG)
+
+ALL_DFLAGS = $(DFLAGS-$@) $(GDCFLAGS) -fversion=IN_GCC $(CHECKING_DFLAGS) \
+ $(PICFLAG) $(ALIASING_FLAGS) $(COVERAGE_FLAGS) $(WARN_DFLAGS)
+
+DCOMPILE.base = $(GDC) $(NO_PIE_CFLAGS) -c $(ALL_DFLAGS) -o $@
+DCOMPILE = $(DCOMPILE.base) -MT $@ -MMD -MP -MF $(@D)/$(DEPDIR)/$(*F).TPo
+DPOSTCOMPILE = @mv $(@D)/$(DEPDIR)/$(*F).TPo $(@D)/$(DEPDIR)/$(*F).Po
+DLINKER = $(GDC) $(NO_PIE_FLAG) -lstdc++
+
+# Like LINKER, but use a mutex for serializing front end links.
+ifeq (@DO_LINK_MUTEX@,true)
+DLLINKER = $(SHELL) $(srcdir)/lock-and-run.sh linkfe.lck $(DLINKER)
+else
+DLLINKER = $(DLINKER)
+endif
# D Frontend object files.
D_FRONTEND_OBJS = \
d/aav.o \
d/access.o \
+ d/aggregate.o \
d/aliasthis.o \
d/apply.o \
+ d/array.o \
d/arrayop.o \
+ d/arraytypes.o \
d/attrib.o \
+ d/ast_node.o \
+ d/astcodegen.o \
+ d/astenums.o \
+ d/bitarray.o \
d/blockexit.o \
+ d/builtin.o \
d/canthrow.o \
- d/checkedint.o \
d/chkformat.o \
d/clone.o \
+ d/compiler.o \
+ d/complex.o \
d/cond.o \
d/constfold.o \
+ d/cparse.o \
d/cppmangle.o \
d/ctfeexpr.o \
+ d/ctfloat.o \
+ d/ctorflow.o \
d/dcast.o \
d/dclass.o \
d/declaration.o \
@@ -86,32 +117,49 @@ D_FRONTEND_OBJS = \
d/dsymbol.o \
d/dsymbolsem.o \
d/dtemplate.o \
+ d/dtoh.o \
d/dversion.o \
d/entity.o \
+ d/errors.o \
d/escape.o \
d/expression.o \
d/expressionsem.o \
d/file.o \
d/filename.o \
+ d/foreachvar.o \
d/func.o \
+ d/globals.o \
+ d/gluelayer.o \
+ d/hash.o \
d/hdrgen.o \
d/iasm.o \
d/iasmgcc.o \
+ d/id.o \
d/identifier.o \
+ d/impcnvtab.o \
d/imphint.o \
d/init.o \
d/initsem.o \
+ d/inline.o \
d/intrange.o \
d/json.o \
+ d/lambdacomp.o \
d/lexer.o \
+ d/longdouble.o \
d/mtype.o \
d/nogc.o \
d/nspace.o \
+ d/ob.o \
d/objc.o \
d/opover.o \
d/optimize.o \
d/outbuffer.o \
d/parse.o \
+ d/parsetimevisitor.o \
+ d/permissivevisitor.o \
+ d/port.o \
+ d/printast.o \
+ d/region.o \
d/rmem.o \
d/rootobject.o \
d/safe.o \
@@ -121,20 +169,23 @@ D_FRONTEND_OBJS = \
d/sideeffect.o \
d/speller.o \
d/statement.o \
+ d/statement_rewrite_walker.o \
d/statementsem.o \
d/staticassert.o \
d/staticcond.o \
+ d/stmtstate.o \
+ d/string.o \
d/stringtable.o \
+ d/target.o \
d/templateparamsem.o \
d/tokens.o \
d/traits.o \
+ d/transitivevisitor.o \
d/typesem.o \
+ d/typinf.o \
d/utf.o \
- d/utils.o
-
-# D Frontend generated files.
-D_GENERATED_SRCS = d/id.c d/id.h d/impcnvtab.c
-D_GENERATED_OBJS = d/id.o d/impcnvtab.o
+ d/utils.o \
+ d/visitor.o
# Language-specific object files for D.
D_OBJS = \
@@ -163,13 +214,13 @@ D_OBJS = \
d/types.o
# All language-specific object files for D.
-D_ALL_OBJS = $(D_FRONTEND_OBJS) $(D_GENERATED_OBJS) $(D_OBJS) $(D_TARGET_OBJS)
+D_ALL_OBJS = $(D_FRONTEND_OBJS) $(D_OBJS) $(D_TARGET_OBJS)
d_OBJS = $(D_ALL_OBJS) d/d-spec.o
d21$(exeext): $(D_ALL_OBJS) attribs.o $(BACKEND) $(LIBDEPS) $(d.prev)
@$(call LINK_PROGRESS,$(INDEX.d),start)
- +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
+ +$(DLLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -static-libphobos -o $@ \
$(D_ALL_OBJS) attribs.o $(BACKEND) $(LIBS) $(BACKENDLIBS)
@$(call LINK_PROGRESS,$(INDEX.d),end)
@@ -221,7 +272,7 @@ d.srcextra:
d.tags: force
cd $(srcdir)/d; \
- $(ETAGS) -o TAGS.sub *.c *.cc *.h dmd/*.c dmd/*.h dmd/root/*.h dmd/root/*.c; \
+ $(ETAGS) -o TAGS.sub *.c *.cc *.h dmd/*.h dmd/root/*.h; \
$(ETAGS) --include TAGS.sub --include ../TAGS.sub
d.man: doc/gdc.1
@@ -313,8 +364,6 @@ d.uninstall:
d.mostlyclean:
-rm -f d/*$(objext)
-rm -f d/*$(coverageexts)
- -rm -f $(D_GENERATED_SRCS)
- -rm -f d/idgen$(build_exeext) d/impcnvgen$(build_exeext)
-rm -f gdc$(exeext) gdc-cross$(exeext) d21$(exeext)
d.clean:
d.distclean:
@@ -337,48 +386,13 @@ d.stagefeedback: stagefeedback-start
-mv d/*$(objext) stagefeedback/d
# Include the dfrontend and build directories for headers.
-D_INCLUDES = -I$(srcdir)/d -I$(srcdir)/d/dmd -Id
-
-CFLAGS-d/id.o += $(D_INCLUDES)
-CFLAGS-d/impcnvtab.o += $(D_INCLUDES)
+D_INCLUDES = -I$(srcdir)/d -J$(srcdir)/d/dmd -J$(srcdir)/d/dmd/res
# Override build rules for D frontend.
-d/%.o: d/dmd/%.c $(D_GENERATED_SRCS)
- $(DMD_COMPILE) $(D_INCLUDES) $<
- $(POSTCOMPILE)
-
-d/%.o: d/dmd/root/%.c $(D_GENERATED_SRCS)
- $(DMD_COMPILE) $(D_INCLUDES) $<
- $(POSTCOMPILE)
-
-# Generated programs.
-d/idgen$(build_exeext): d/idgen.dmdgen.o $(BUILD_LIBDEPS)
- +$(LINKER_FOR_BUILD) $(BUILD_LINKERFLAGS) $(BUILD_LDFLAGS) -o $@ \
- $(filter-out $(BUILD_LIBDEPS), $^) $(BUILD_LIBS)
-
-d/impcnvgen$(build_exeext): d/impcnvgen.dmdgen.o $(BUILD_LIBDEPS)
- +$(LINKER_FOR_BUILD) $(BUILD_LINKERFLAGS) $(BUILD_LDFLAGS) -o $@ \
- $(filter-out $(BUILD_LIBDEPS), $^) $(BUILD_LIBS)
-
-# Generated sources.
-d/id.c: d/idgen$(build_exeext)
- cd d && ./idgen$(build_exeext)
-
-# idgen also generates id.h; just verify it exists.
-d/id.h: d/id.c
-
-d/impcnvtab.c: d/impcnvgen$(build_exeext)
- cd d && ./impcnvgen$(build_exeext)
-
-# Compile the generator programs.
-d/%.dmdgen.o: $(srcdir)/d/dmd/%.c
- $(COMPILER_FOR_BUILD) -c $(BUILD_COMPILERFLAGS) $(D_INCLUDES) \
- $(BUILD_CPPFLAGS) -o $@ $<
-
-# Header dependencies for the generator programs.
-D_SYSTEM_H = d/dmd/root/dsystem.h d/d-system.h
-
-d/idgen.dmdgen.o: d/dmd/idgen.c $(D_SYSTEM_H) $(BCONFIG_H) $(SYSTEM_H)
+d/%.o: d/dmd/%.d
+ $(DCOMPILE) $(D_INCLUDES) $<
+ $(DPOSTCOMPILE)
-d/impcnvgen.dmdgen.o: d/dmd/impcnvgen.c d/dmd/mtype.h $(D_SYSTEM_H) \
- $(BCONFIG_H) $(SYSTEM_H)
+d/%.o: d/dmd/root/%.d
+ $(DCOMPILE) $(D_INCLUDES) $<
+ $(DPOSTCOMPILE)
diff --git a/gcc/d/config-lang.in b/gcc/d/config-lang.in
index 0568b8da1de..00a71d6b8b5 100644
--- a/gcc/d/config-lang.in
+++ b/gcc/d/config-lang.in
@@ -19,10 +19,12 @@
# We define several parameters used by configure:
#
# language - name of language as it would appear in $(LANGUAGES)
+# boot_language - "yes" if we need to build this language in stage1
# compilers - value to add to $(COMPILERS)
language="d"
+boot_language=yes
compilers="d21\$(exeext)"
phobos_target_deps="target-zlib target-libbacktrace"
diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc
index b79cf96f55c..04b9791ab1b 100644
--- a/gcc/d/d-attribs.cc
+++ b/gcc/d/d-attribs.cc
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see
#include "dmd/attrib.h"
#include "dmd/declaration.h"
+#include "dmd/expression.h"
#include "dmd/module.h"
#include "dmd/mtype.h"
#include "dmd/template.h"
diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc
index 33347a14c67..ab3a950689f 100644
--- a/gcc/d/d-builtins.cc
+++ b/gcc/d/d-builtins.cc
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
#include "stor-layout.h"
#include "d-tree.h"
+#include "d-frontend.h"
#include "d-target.h"
@@ -98,7 +99,7 @@ build_frontend_type (tree type)
if (TYPE_MAIN_VARIANT (TREE_TYPE (type)) == char_type_node)
return Type::tchar->addMod (dtype->mod)->pointerTo ()->addMod (mod);
- if (dtype->ty == Tfunction)
+ if (dtype->ty == TY::Tfunction)
return (TypePointer::create (dtype))->addMod (mod);
return dtype->pointerTo ()->addMod (mod);
@@ -130,7 +131,7 @@ build_frontend_type (tree type)
/* For now, skip support for cent/ucent until the frontend
has better support for handling it. */
- for (size_t i = Tint8; i <= Tuns64; i++)
+ for (size_t i = (size_t) TY::Tint8; i <= (size_t) TY::Tuns64; i++)
{
dtype = Type::basic[i];
@@ -148,7 +149,7 @@ build_frontend_type (tree type)
{
unsigned size = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (type));
- for (size_t i = Tfloat32; i <= Tfloat80; i++)
+ for (size_t i = (size_t) TY::Tfloat32; i <= (size_t) TY::Tfloat80; i++)
{
dtype = Type::basic[i];
@@ -164,7 +165,8 @@ build_frontend_type (tree type)
case COMPLEX_TYPE:
{
unsigned size = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (type));
- for (size_t i = Tcomplex32; i <= Tcomplex80; i++)
+ for (size_t i = (size_t) TY::Tcomplex32; i <= (size_t) TY::Tcomplex80;
+ i++)
{
dtype = Type::basic[i];
@@ -235,7 +237,7 @@ build_frontend_type (tree type)
sdecl->structsize = int_size_in_bytes (type);
sdecl->alignsize = TYPE_ALIGN_UNIT (type);
sdecl->alignment = STRUCTALIGN_DEFAULT;
- sdecl->sizeok = SIZEOKdone;
+ sdecl->sizeok = Sizeok::done;
sdecl->type = (TypeStruct::create (sdecl))->addMod (mod);
sdecl->type->ctype = type;
sdecl->type->merge2 ();
@@ -243,7 +245,7 @@ build_frontend_type (tree type)
/* Add both named and anonymous fields as members of the struct.
Anonymous fields still need a name in D, so call them "__pad%u". */
unsigned anonfield_id = 0;
- sdecl->members = new Dsymbols;
+ sdecl->members = d_gc_malloc<Dsymbols> ();
for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
{
@@ -253,7 +255,6 @@ build_frontend_type (tree type)
/* Drop any field types that got cached before the conversion
of this record type failed. */
builtin_converted_decls.truncate (saved_builtin_decls_length);
- delete sdecl->members;
return NULL;
}
@@ -292,7 +293,7 @@ build_frontend_type (tree type)
tree parms = TYPE_ARG_TYPES (type);
VarArg varargs_p = VARARGvariadic;
- Parameters *args = new Parameters;
+ Parameters *args = d_gc_malloc<Parameters> ();
args->reserve (list_length (parms));
/* Attempt to convert all parameter types. */
@@ -318,7 +319,6 @@ build_frontend_type (tree type)
/* Drop any parameter types that got cached before the
conversion of this function type failed. */
builtin_converted_decls.truncate (saved_builtin_decls_length);
- delete args;
return NULL;
}
@@ -329,7 +329,7 @@ build_frontend_type (tree type)
have no named parameters, and so can't be represented in D. */
if (args->length != 0 || varargs_p == VARARGnone)
{
- dtype = TypeFunction::create (args, dtype, varargs_p, LINKc);
+ dtype = TypeFunction::create (args, dtype, varargs_p, LINK::c);
return dtype->addMod (mod);
}
}
@@ -386,7 +386,7 @@ d_eval_constant_expression (const Loc &loc, tree cst)
else if (code == VECTOR_CST)
{
dinteger_t nunits = VECTOR_CST_NELTS (cst).to_constant ();
- Expressions *elements = new Expressions;
+ Expressions *elements = d_gc_malloc<Expressions> ();
elements->setDim (nunits);
for (size_t i = 0; i < nunits; i++)
@@ -520,7 +520,7 @@ build_alias_declaration (const char *alias, Type *type)
void
d_build_builtins_module (Module *m)
{
- Dsymbols *members = new Dsymbols;
+ Dsymbols *members = d_gc_malloc<Dsymbols> ();
tree decl;
for (size_t i = 0; vec_safe_iterate (gcc_builtins_functions, i, &decl); ++i)
@@ -543,16 +543,16 @@ d_build_builtins_module (Module *m)
flag_unsafe_math_optimizations.
- Built-ins never use the GC or raise a D exception, and so are always
marked as `nothrow' and `@nogc'. */
- tf->purity = DECL_PURE_P (decl) ? PUREstrong
- : TREE_READONLY (decl) ? PUREconst
- : DECL_IS_NOVOPS (decl) ? PUREweak
- : !DECL_ASSEMBLER_NAME_SET_P (decl) ? PUREweak
- : PUREimpure;
- tf->trust = !DECL_ASSEMBLER_NAME_SET_P (decl) ? TRUSTsafe
- : TREE_NOTHROW (decl) ? TRUSTtrusted
- : TRUSTsystem;
- tf->isnothrow = true;
- tf->isnogc = true;
+ tf->purity = DECL_PURE_P (decl) ? PURE::strong
+ : TREE_READONLY (decl) ? PURE::const_
+ : DECL_IS_NOVOPS (decl) ? PURE::weak
+ : !DECL_ASSEMBLER_NAME_SET_P (decl) ? PURE::weak
+ : PURE::impure;
+ tf->trust = !DECL_ASSEMBLER_NAME_SET_P (decl) ? TRUST::safe
+ : TREE_NOTHROW (decl) ? TRUST::trusted
+ : TRUST::system;
+ tf->isnothrow (true);
+ tf->isnogc (true);
FuncDeclaration *func
= FuncDeclaration::create (Loc (), Loc (),
@@ -560,7 +560,7 @@ d_build_builtins_module (Module *m)
STCextern, tf);
DECL_LANG_SPECIFIC (decl) = build_lang_decl (func);
func->csym = decl;
- func->builtin = BUILTINgcc;
+ func->builtin = BUILTIN::gcc;
members->push (func);
}
@@ -660,7 +660,7 @@ d_build_builtins_module (Module *m)
members->push (build_alias_declaration ("__builtin_unwind_uint", t));
}
- m->members->push (LinkDeclaration::create (LINKc, members));
+ m->members->push (LinkDeclaration::create (Loc (), LINK::c, members));
}
/* Search for any `extern(C)' functions that match any known GCC library builtin
@@ -700,7 +700,7 @@ maybe_set_builtin_1 (Dsymbol *d)
/* Found a match, tell the frontend this is a builtin. */
DECL_LANG_SPECIFIC (t) = build_lang_decl (fd);
fd->csym = t;
- fd->builtin = BUILTINgcc;
+ fd->builtin = BUILTIN::gcc;
return;
}
}
@@ -858,7 +858,7 @@ d_build_d_type_nodes (void)
/* Calling build_ctype() links the front-end Type to the GCC node,
and sets the TYPE_NAME to the D language type. */
- for (unsigned ty = 0; ty < TMAX; ty++)
+ for (unsigned ty = 0; ty < (unsigned) TY::TMAX; ty++)
{
if (Type::basic[ty] != NULL)
build_ctype (Type::basic[ty]);
diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc
index e63365055d3..403e3c74377 100644
--- a/gcc/d/d-codegen.cc
+++ b/gcc/d/d-codegen.cc
@@ -76,7 +76,7 @@ d_decl_context (Dsymbol *dsym)
but only for extern(D) symbols. */
if (parent->isModule ())
{
- if ((decl != NULL && decl->linkage != LINKd)
+ if ((decl != NULL && decl->linkage != LINK::d)
|| (ad != NULL && ad->classKind != ClassKind::d))
return NULL_TREE;
@@ -131,7 +131,7 @@ declaration_reference_p (Declaration *decl)
Type *tb = decl->type->toBasetype ();
/* Declaration is a reference type. */
- if (tb->ty == Treference || decl->storage_class & (STCout | STCref))
+ if (tb->ty == TY::Treference || decl->storage_class & (STCout | STCref))
return true;
return false;
@@ -146,7 +146,7 @@ declaration_type (Declaration *decl)
if (decl->storage_class & STClazy)
{
TypeFunction *tf = TypeFunction::create (NULL, decl->type,
- VARARGnone, LINKd);
+ VARARGnone, LINK::d);
TypeDelegate *t = TypeDelegate::create (tf);
return build_ctype (t->merge2 ());
}
@@ -181,7 +181,7 @@ parameter_reference_p (Parameter *arg)
Type *tb = arg->type->toBasetype ();
/* Parameter is a reference type. */
- if (tb->ty == Treference || arg->storageClass & (STCout | STCref))
+ if (tb->ty == TY::Treference || arg->storageClass & (STCout | STCref))
return true;
return false;
@@ -196,7 +196,7 @@ parameter_type (Parameter *arg)
if (arg->storageClass & STClazy)
{
TypeFunction *tf = TypeFunction::create (NULL, arg->type,
- VARARGnone, LINKd);
+ VARARGnone, LINK::d);
TypeDelegate *t = TypeDelegate::create (tf);
return build_ctype (t->merge2 ());
}
@@ -319,10 +319,10 @@ get_array_length (tree exp, Type *type)
switch (tb->ty)
{
- case Tsarray:
+ case TY::Tsarray:
return size_int (tb->isTypeSArray ()->dim->toUInteger ());
- case Tarray:
+ case TY::Tarray:
return d_array_length (exp);
default:
@@ -411,7 +411,7 @@ build_delegate_cst (tree method, tree object, Type *type)
tree ctype;
Type *tb = type->toBasetype ();
- if (tb->ty == Tdelegate)
+ if (tb->ty == TY::Tdelegate)
ctype = build_ctype (type);
else
{
@@ -464,11 +464,11 @@ build_typeof_null_value (Type *type)
tree value;
/* For dynamic arrays, set length and pointer fields to zero. */
- if (tb->ty == Tarray)
+ if (tb->ty == TY::Tarray)
value = d_array_value (build_ctype (type), size_int (0), null_pointer_node);
/* For associative arrays, set the pointer field to null. */
- else if (tb->ty == Taarray)
+ else if (tb->ty == TY::Taarray)
{
tree ctype = build_ctype (type);
gcc_assert (TYPE_ASSOCIATIVE_ARRAY (ctype));
@@ -478,7 +478,7 @@ build_typeof_null_value (Type *type)
}
/* For delegates, set the frame and function pointer fields to null. */
- else if (tb->ty == Tdelegate)
+ else if (tb->ty == TY::Tdelegate)
value = build_delegate_cst (null_pointer_node, null_pointer_node, type);
/* Simple zero constant for all other types. */
@@ -882,7 +882,9 @@ identity_compare_p (StructDeclaration *sd)
}
/* Check for types that may have padding. */
- if ((tb->ty == Tcomplex80 || tb->ty == Tfloat80 || tb->ty == Timaginary80)
+ if ((tb->ty == TY::Tcomplex80
+ || tb->ty == TY::Tfloat80
+ || tb->ty == TY::Timaginary80)
&& target.realpad != 0)
return false;
@@ -960,12 +962,12 @@ lower_struct_comparison (tree_code code, StructDeclaration *sd,
/* Compare inner data structures. */
tcmp = lower_struct_comparison (code, ts->sym, t1ref, t2ref);
}
- else if (type->ty != Tvector && type->isintegral ())
+ else if (type->ty != TY::Tvector && type->isintegral ())
{
/* Integer comparison, no special handling required. */
tcmp = build_boolop (code, t1ref, t2ref);
}
- else if (type->ty != Tvector && type->isfloating ())
+ else if (type->ty != TY::Tvector && type->isfloating ())
{
/* Floating-point comparison, don't compare padding in type. */
if (!type->iscomplex ())
@@ -1839,7 +1841,7 @@ static tree
build_filename_from_loc (const Loc &loc)
{
const char *filename = loc.filename
- ? loc.filename : d_function_chain->module->srcfile->toChars ();
+ ? loc.filename : d_function_chain->module->srcfile.toChars ();
unsigned length = strlen (filename);
tree str = build_string (length, filename);
@@ -1862,7 +1864,6 @@ build_assert_call (const Loc &loc, libcall_fn libcall, tree msg)
{
case LIBCALL_ASSERT_MSG:
case LIBCALL_UNITTEST_MSG:
- case LIBCALL_SWITCH_ERROR:
/* File location is passed as a D string. */
if (loc.filename)
{
@@ -1912,7 +1913,7 @@ build_array_bounds_call (const Loc &loc)
/* Builds a bounds condition checking that INDEX is between 0 and LENGTH
in the index expression IE. The condition returns the INDEX if true, or
- throws a `RangeError`. */
+ throws a `ArrayIndexError`. */
tree
build_bounds_index_condition (IndexExp *ie, tree index, tree length)
@@ -1927,7 +1928,16 @@ build_bounds_index_condition (IndexExp *ie, tree index, tree length)
No need to check whether INDEX >= 0 as the front-end should
have already taken care of implicit casts to unsigned. */
tree condition = fold_build2 (GE_EXPR, d_bool_type, index, length);
- tree boundserr = build_array_bounds_call (ie->e2->loc);
+ tree boundserr;
+
+ if (checkaction_trap_p ())
+ boundserr = build_call_expr (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ boundserr = build_libcall (LIBCALL_ARRAYBOUNDS_INDEXP, Type::tvoid, 4,
+ build_filename_from_loc (ie->e2->loc),
+ size_int (ie->e2->loc.linnum), index, length);
+ }
return build_condition (TREE_TYPE (index), condition, boundserr, index);
}
@@ -1963,7 +1973,22 @@ build_bounds_slice_condition (SliceExp *se, tree lower, tree upper, tree length)
if (condition != NULL_TREE)
{
- tree boundserr = build_array_bounds_call (se->loc);
+ tree boundserr;
+
+ if (checkaction_trap_p ())
+ {
+ boundserr =
+ build_call_expr (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ }
+ else
+ {
+ boundserr = build_libcall (LIBCALL_ARRAYBOUNDS_SLICEP,
+ Type::tvoid, 5,
+ build_filename_from_loc (se->loc),
+ size_int (se->loc.linnum),
+ lower, upper, length);
+ }
+
upper = build_condition (TREE_TYPE (upper), condition,
boundserr, upper);
}
@@ -1993,9 +2018,9 @@ array_bounds_check (void)
case CHECKENABLEsafeonly:
/* For D2 safe functions only. */
fd = d_function_chain->function;
- if (fd && fd->type->ty == Tfunction)
+ if (fd && fd->type->ty == TY::Tfunction)
{
- if (fd->type->isTypeFunction ()->trust == TRUSTsafe)
+ if (fd->type->isTypeFunction ()->trust == TRUST::safe)
return true;
}
return false;
@@ -2014,6 +2039,7 @@ checkaction_trap_p (void)
switch (global.params.checkAction)
{
case CHECKACTION_D:
+ case CHECKACTION_context:
return false;
case CHECKACTION_C:
@@ -2032,11 +2058,11 @@ TypeFunction *
get_function_type (Type *t)
{
TypeFunction *tf = NULL;
- if (t->ty == Tpointer)
+ if (t->ty == TY::Tpointer)
t = t->nextOf ()->toBasetype ();
- if (t->ty == Tfunction)
+ if (t->ty == TY::Tfunction)
tf = t->isTypeFunction ();
- else if (t->ty == Tdelegate)
+ else if (t->ty == TY::Tdelegate)
tf = t->isTypeDelegate ()->next->isTypeFunction ();
return tf;
}
@@ -2096,7 +2122,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
gcc_assert (FUNC_OR_METHOD_TYPE_P (ctype));
gcc_assert (tf != NULL);
- gcc_assert (tf->ty == Tfunction);
+ gcc_assert (tf->ty == TY::Tfunction);
if (TREE_CODE (ctype) != FUNCTION_TYPE && object == NULL_TREE)
{
@@ -2195,7 +2221,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
SET_EXPR_LOCATION (result, input_location);
/* Enforce left to right evaluation. */
- if (tf->linkage == LINKd)
+ if (tf->linkage == LINK::d)
CALL_EXPR_ARGS_ORDERED (result) = 1;
result = maybe_expand_intrinsic (result);
diff --git a/gcc/d/d-compiler.cc b/gcc/d/d-compiler.cc
index 512ef4bab3e..3df40073ac5 100644
--- a/gcc/d/d-compiler.cc
+++ b/gcc/d/d-compiler.cc
@@ -20,7 +20,6 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "dmd/compiler.h"
-#include "dmd/scope.h"
#include "dmd/expression.h"
#include "dmd/identifier.h"
#include "dmd/module.h"
@@ -34,40 +33,6 @@ along with GCC; see the file COPYING3. If not see
/* Implements the Compiler interface used by the frontend. */
-/* Generate C main() in response to seeing D main(). This used to be in
- libdruntime, but contained a reference to _Dmain which didn't work when
- druntime was made into a shared library and was linked to a program, such
- as a C++ program, that didn't have a _Dmain. */
-
-void
-Compiler::genCmain (Scope *sc)
-{
- static bool initialized = false;
-
- if (initialized)
- return;
-
- /* The D code to be generated is provided by __entrypoint.di, try to load it,
- but don't fail if unfound. */
- unsigned errors = global.startGagging ();
- Module *m = Module::load (Loc (), NULL, Identifier::idPool ("__entrypoint"));
-
- if (global.endGagging (errors))
- m = NULL;
-
- if (m != NULL)
- {
- m->importedFrom = m;
- m->importAll (NULL);
- dsymbolSemantic (m, NULL);
- semantic2 (m, NULL);
- semantic3 (m, NULL);
- d_add_entrypoint_module (m, sc->_module);
- }
-
- initialized = true;
-}
-
/* Perform a reinterpret cast of EXPR to type TYPE for use in CTFE.
The front end should have already ensured that EXPR is a constant,
so we just lower the value to GCC and return the converted CST. */
@@ -123,7 +88,7 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type)
/* Encode CST to buffer. */
int len = native_encode_expr (cst, buffer, sizeof (buffer));
- if (tb->ty == Tsarray)
+ if (tb->ty == TY::Tsarray)
{
/* Interpret value as a vector of the same size,
then return the array literal. */
@@ -161,22 +126,22 @@ Compiler::onParseModule (Module *m)
{
ModuleDeclaration *md = m->md;
- if (!md || !md->id || !md->packages)
+ if (!md || !md->id|| md->packages.length == 0)
{
Identifier *id = (md && md->id) ? md->id : m->ident;
if (!strcmp (id->toChars (), "object"))
create_tinfo_types (m);
}
- else if (md->packages->length == 1)
+ else if (md->packages.length == 1)
{
- if (!strcmp ((*md->packages)[0]->toChars (), "gcc")
+ if (!strcmp (md->packages.ptr[0]->toChars (), "gcc")
&& !strcmp (md->id->toChars (), "builtins"))
d_build_builtins_module (m);
}
- else if (md->packages->length == 2)
+ else if (md->packages.length == 2)
{
- if (!strcmp ((*md->packages)[0]->toChars (), "core")
- && !strcmp ((*md->packages)[1]->toChars (), "stdc"))
+ if (!strcmp (md->packages.ptr[0]->toChars (), "core")
+ && !strcmp (md->packages.ptr[1]->toChars (), "stdc"))
d_add_builtin_module (m);
}
}
diff --git a/gcc/d/d-convert.cc b/gcc/d/d-convert.cc
index 3b4790298a5..25fd603edfa 100644
--- a/gcc/d/d-convert.cc
+++ b/gcc/d/d-convert.cc
@@ -361,14 +361,14 @@ convert_expr (tree exp, Type *etype, Type *totype)
switch (ebtype->ty)
{
- case Tdelegate:
- if (tbtype->ty == Tdelegate)
+ case TY::Tdelegate:
+ if (tbtype->ty == TY::Tdelegate)
{
exp = d_save_expr (exp);
return build_delegate_cst (delegate_method (exp),
delegate_object (exp), totype);
}
- else if (tbtype->ty == Tpointer)
+ else if (tbtype->ty == TY::Tpointer)
{
/* The front-end converts <delegate>.ptr to cast (void *)<delegate>.
Maybe should only allow void* ? */
@@ -382,8 +382,8 @@ convert_expr (tree exp, Type *etype, Type *totype)
}
break;
- case Tstruct:
- if (tbtype->ty == Tstruct)
+ case TY::Tstruct:
+ if (tbtype->ty == TY::Tstruct)
{
if (totype->size () == etype->size ())
{
@@ -400,8 +400,8 @@ convert_expr (tree exp, Type *etype, Type *totype)
/* else, default conversion, which should produce an error. */
break;
- case Tclass:
- if (tbtype->ty == Tclass)
+ case TY::Tclass:
+ if (tbtype->ty == TY::Tclass)
{
ClassDeclaration *cdfrom = ebtype->isClassHandle ();
ClassDeclaration *cdto = tbtype->isClassHandle ();
@@ -460,12 +460,12 @@ convert_expr (tree exp, Type *etype, Type *totype)
/* else default conversion. */
break;
- case Tsarray:
- if (tbtype->ty == Tpointer)
+ case TY::Tsarray:
+ if (tbtype->ty == TY::Tpointer)
{
result = build_nop (build_ctype (totype), build_address (exp));
}
- else if (tbtype->ty == Tarray)
+ else if (tbtype->ty == TY::Tarray)
{
dinteger_t dim = ebtype->isTypeSArray ()->dim->toInteger ();
dinteger_t esize = ebtype->nextOf ()->size ();
@@ -490,12 +490,12 @@ convert_expr (tree exp, Type *etype, Type *totype)
return d_array_value (build_ctype (totype), size_int (dim),
build_nop (ptrtype, build_address (exp)));
}
- else if (tbtype->ty == Tsarray)
+ else if (tbtype->ty == TY::Tsarray)
{
/* D allows casting a static array to any static array type. */
return build_nop (build_ctype (totype), exp);
}
- else if (tbtype->ty == Tstruct)
+ else if (tbtype->ty == TY::Tstruct)
{
/* And allows casting a static array to any struct type too.
Type sizes should have already been checked by the frontend. */
@@ -510,12 +510,12 @@ convert_expr (tree exp, Type *etype, Type *totype)
}
break;
- case Tarray:
- if (tbtype->ty == Tpointer)
+ case TY::Tarray:
+ if (tbtype->ty == TY::Tpointer)
{
return d_convert (build_ctype (totype), d_array_ptr (exp));
}
- else if (tbtype->ty == Tarray)
+ else if (tbtype->ty == TY::Tarray)
{
/* Assume tvoid->size() == 1. */
d_uns64 fsize = ebtype->nextOf ()->toBasetype ()->size ();
@@ -523,9 +523,18 @@ convert_expr (tree exp, Type *etype, Type *totype)
if (fsize != tsize)
{
- /* Conversion requires a reinterpret cast of array. */
- return build_libcall (LIBCALL_ARRAYCAST, totype, 3,
- size_int (tsize), size_int (fsize), exp);
+ /* Conversion requires a reinterpret cast of array.
+ This case should have been lowered in the semantic pass. */
+ if (tsize != 0 && fsize % tsize == 0)
+ {
+ /* Set array dimension to (length * (fsize / tsize)). */
+ tree newlength = size_mult_expr (d_array_length (exp),
+ size_int (fsize / tsize));
+ return d_array_value (build_ctype (totype), newlength,
+ d_array_ptr (exp));
+ }
+ else
+ gcc_unreachable ();
}
else
{
@@ -534,7 +543,7 @@ convert_expr (tree exp, Type *etype, Type *totype)
return build_vconvert (build_ctype (totype), exp);
}
}
- else if (tbtype->ty == Tsarray)
+ else if (tbtype->ty == TY::Tsarray)
{
/* Strings are treated as dynamic arrays in D2. */
if (ebtype->isString () && tbtype->isString ())
@@ -548,23 +557,23 @@ convert_expr (tree exp, Type *etype, Type *totype)
}
break;
- case Taarray:
- if (tbtype->ty == Taarray)
+ case TY::Taarray:
+ if (tbtype->ty == TY::Taarray)
return build_vconvert (build_ctype (totype), exp);
/* Can convert associative arrays to void pointers. */
- else if (tbtype->ty == Tpointer && tbtype->nextOf ()->ty == Tvoid)
+ else if (tbtype->ty == TY::Tpointer && tbtype->nextOf ()->ty == TY::Tvoid)
return build_vconvert (build_ctype (totype), exp);
/* Else, default conversion, which should product an error. */
break;
- case Tpointer:
+ case TY::Tpointer:
/* Can convert void pointers to associative arrays too. */
- if (tbtype->ty == Taarray && ebtype->nextOf ()->ty == Tvoid)
+ if (tbtype->ty == TY::Taarray && ebtype->nextOf ()->ty == TY::Tvoid)
return build_vconvert (build_ctype (totype), exp);
break;
- case Tnull:
- case Tnoreturn:
+ case TY::Tnull:
+ case TY::Tnoreturn:
/* Casting from `typeof(null)' for `null' expressions, or `typeof(*null)'
for `noreturn' expressions is represented as all zeros. */
result = build_typeof_null_value (totype);
@@ -574,8 +583,8 @@ convert_expr (tree exp, Type *etype, Type *totype)
result = compound_expr (exp, result);
break;
- case Tvector:
- if (tbtype->ty == Tsarray)
+ case TY::Tvector:
+ if (tbtype->ty == TY::Tsarray)
{
if (tbtype->size () == ebtype->size ())
return build_vconvert (build_ctype (totype), exp);
@@ -613,7 +622,7 @@ convert_for_rvalue (tree expr, Type *etype, Type *totype)
Type *ebtype = etype->toBasetype ();
Type *tbtype = totype->toBasetype ();
- if (ebtype->ty == Tbool)
+ if (ebtype->ty == TY::Tbool)
{
/* If casting from bool, the result is either 0 or 1, any other value
violates @safe code, so enforce that it is never invalid. */
@@ -651,7 +660,7 @@ convert_for_assignment (tree expr, Type *etype, Type *totype)
/* Assuming this only has to handle converting a non Tsarray type to
arbitrarily dimensioned Tsarrays. */
- if (tbtype->ty == Tsarray)
+ if (tbtype->ty == TY::Tsarray)
{
Type *telem = tbtype->nextOf ()->baseElemOf ();
@@ -685,7 +694,7 @@ convert_for_assignment (tree expr, Type *etype, Type *totype)
}
/* D Front end uses IntegerExp(0) to mean zero-init an array or structure. */
- if ((tbtype->ty == Tsarray || tbtype->ty == Tstruct)
+ if ((tbtype->ty == TY::Tsarray || tbtype->ty == TY::Tstruct)
&& ebtype->isintegral ())
{
if (!integer_zerop (expr))
@@ -736,12 +745,12 @@ convert_for_condition (tree expr, Type *type)
switch (type->toBasetype ()->ty)
{
- case Taarray:
+ case TY::Taarray:
/* Checks that aa.ptr !is null. */
result = component_ref (expr, TYPE_FIELDS (TREE_TYPE (expr)));
break;
- case Tarray:
+ case TY::Tarray:
{
/* Checks (arr.length || arr.ptr) (i.e arr !is null). */
expr = d_save_expr (expr);
@@ -762,7 +771,7 @@ convert_for_condition (tree expr, Type *type)
break;
}
- case Tdelegate:
+ case TY::Tdelegate:
{
/* Checks (function || object), but what good is it if there is
a null function pointer? */
@@ -783,7 +792,7 @@ convert_for_condition (tree expr, Type *type)
break;
}
- case Tnoreturn:
+ case TY::Tnoreturn:
/* Front-end allows conditionals that never return, represent the
conditional result value as all zeros. */
result = build_zero_cst (d_bool_type);
@@ -810,10 +819,10 @@ d_array_convert (Expression *exp)
{
Type *tb = exp->type->toBasetype ();
- if (tb->ty == Tarray)
+ if (tb->ty == TY::Tarray)
return build_expr (exp);
- if (tb->ty == Tsarray)
+ if (tb->ty == TY::Tsarray)
{
Type *totype = tb->nextOf ()->arrayOf ();
return convert_expr (build_expr (exp), exp->type, totype);
@@ -832,7 +841,8 @@ d_array_convert (Type *etype, Expression *exp)
{
Type *tb = exp->type->toBasetype ();
- if ((tb->ty != Tarray && tb->ty != Tsarray) || same_type_p (tb, etype))
+ if ((tb->ty != TY::Tarray && tb->ty != TY::Tsarray)
+ || same_type_p (tb, etype))
{
/* Convert single element to an array. */
tree expr = build_expr (exp);
diff --git a/gcc/d/d-diagnostic.cc b/gcc/d/d-diagnostic.cc
index 1982bd954a8..947b6e21d60 100644
--- a/gcc/d/d-diagnostic.cc
+++ b/gcc/d/d-diagnostic.cc
@@ -222,15 +222,6 @@ d_diagnostic_report_diagnostic (const Loc &loc, int opt, const char *format,
message prefix PREFIX1 and PREFIX2, increasing the global or gagged
error count. */
-void ATTRIBUTE_GCC_DIAG(2,3)
-error (const Loc &loc, const char *format, ...)
-{
- va_list ap;
- va_start (ap, format);
- verror (loc, format, ap);
- va_end (ap);
-}
-
void ATTRIBUTE_GCC_DIAG(2,0)
verror (const Loc &loc, const char *format, va_list ap,
const char *prefix1, const char *prefix2, const char *)
@@ -263,15 +254,6 @@ verror (const Loc &loc, const char *format, va_list ap,
/* Print supplementary message about the last error with explicit location LOC.
This doesn't increase the global error count. */
-void ATTRIBUTE_GCC_DIAG(2,3)
-errorSupplemental (const Loc &loc, const char *format, ...)
-{
- va_list ap;
- va_start (ap, format);
- verrorSupplemental (loc, format, ap);
- va_end (ap);
-}
-
void ATTRIBUTE_GCC_DIAG(2,0)
verrorSupplemental (const Loc &loc, const char *format, va_list ap)
{
@@ -284,15 +266,6 @@ verrorSupplemental (const Loc &loc, const char *format, va_list ap)
/* Print a warning message with explicit location LOC, increasing the
global warning count. */
-void ATTRIBUTE_GCC_DIAG(2,3)
-warning (const Loc &loc, const char *format, ...)
-{
- va_list ap;
- va_start (ap, format);
- vwarning (loc, format, ap);
- va_end (ap);
-}
-
void ATTRIBUTE_GCC_DIAG(2,0)
vwarning (const Loc &loc, const char *format, va_list ap)
{
@@ -311,15 +284,6 @@ vwarning (const Loc &loc, const char *format, va_list ap)
/* Print supplementary message about the last warning with explicit location
LOC. This doesn't increase the global warning count. */
-void ATTRIBUTE_GCC_DIAG(2,3)
-warningSupplemental (const Loc &loc, const char *format, ...)
-{
- va_list ap;
- va_start (ap, format);
- vwarningSupplemental (loc, format, ap);
- va_end (ap);
-}
-
void ATTRIBUTE_GCC_DIAG(2,0)
vwarningSupplemental (const Loc &loc, const char *format, va_list ap)
{
@@ -333,15 +297,6 @@ vwarningSupplemental (const Loc &loc, const char *format, va_list ap)
message prefix PREFIX1 and PREFIX2, increasing the global warning or
error count depending on how deprecations are treated. */
-void ATTRIBUTE_GCC_DIAG(2,3)
-deprecation (const Loc &loc, const char *format, ...)
-{
- va_list ap;
- va_start (ap, format);
- vdeprecation (loc, format, ap);
- va_end (ap);
-}
-
void ATTRIBUTE_GCC_DIAG(2,0)
vdeprecation (const Loc &loc, const char *format, va_list ap,
const char *prefix1, const char *prefix2)
@@ -372,15 +327,6 @@ vdeprecation (const Loc &loc, const char *format, va_list ap,
/* Print supplementary message about the last deprecation with explicit
location LOC. This does not increase the global error count. */
-void ATTRIBUTE_GCC_DIAG(2,3)
-deprecationSupplemental (const Loc &loc, const char *format, ...)
-{
- va_list ap;
- va_start (ap, format);
- vdeprecationSupplemental (loc, format, ap);
- va_end (ap);
-}
-
void ATTRIBUTE_GCC_DIAG(2,0)
vdeprecationSupplemental (const Loc &loc, const char *format, va_list ap)
{
@@ -392,30 +338,19 @@ vdeprecationSupplemental (const Loc &loc, const char *format, va_list ap)
/* Print a verbose message with explicit location LOC. */
-void ATTRIBUTE_GCC_DIAG(2, 3)
-message (const Loc &loc, const char *format, ...)
-{
- va_list ap;
- va_start (ap, format);
- vmessage (loc, format, ap);
- va_end (ap);
-}
-
void ATTRIBUTE_GCC_DIAG(2,0)
vmessage (const Loc &loc, const char *format, va_list ap)
{
d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, true);
}
-/* Same as above, but doesn't take a location argument. */
+/* Print a tip message with prefix and highlighing. */
-void ATTRIBUTE_GCC_DIAG(1, 2)
-message (const char *format, ...)
+void ATTRIBUTE_GCC_DIAG(1,0)
+vtip (const char *format, va_list ap)
{
- va_list ap;
- va_start (ap, format);
- vmessage (Loc (), format, ap);
- va_end (ap);
+ if (!global.gag)
+ d_diagnostic_report_diagnostic (Loc (), 0, format, ap, DK_DEBUG, true);
}
/* Call this after printing out fatal error messages to clean up and
diff --git a/gcc/d/d-frontend.cc b/gcc/d/d-frontend.cc
index 30fc6d435d0..522095f12c5 100644
--- a/gcc/d/d-frontend.cc
+++ b/gcc/d/d-frontend.cc
@@ -27,116 +27,11 @@ along with GCC; see the file COPYING3. If not see
#include "dmd/scope.h"
#include "tree.h"
-#include "options.h"
#include "fold-const.h"
#include "diagnostic.h"
#include "d-tree.h"
-
-/* Implements the Global interface defined by the frontend.
- Used for managing the state of the current compilation. */
-
-Global global;
-
-void
-Global::_init (void)
-{
- this->mars_ext = "d";
- this->hdr_ext = "di";
- this->doc_ext = "html";
- this->ddoc_ext = "ddoc";
- this->json_ext = "json";
- this->obj_ext = "o";
-
- this->run_noext = true;
- this->version = "v"
-#include "verstr.h"
- ;
-
- this->stdmsg = stderr;
-}
-
-/* Start gagging. Return the current number of gagged errors. */
-
-unsigned
-Global::startGagging (void)
-{
- this->gag++;
- return this->gaggedErrors;
-}
-
-/* End gagging, restoring the old gagged state. Return true if errors
- occured while gagged. */
-
-bool
-Global::endGagging (unsigned oldGagged)
-{
- bool anyErrs = (this->gaggedErrors != oldGagged);
- this->gag--;
-
- /* Restore the original state of gagged errors; set total errors
- to be original errors + new ungagged errors. */
- this->errors -= (this->gaggedErrors - oldGagged);
- this->gaggedErrors = oldGagged;
-
- return anyErrs;
-}
-
-/* Increment the error count to record that an error has occured in the
- current context. An error message may or may not have been printed. */
-
-void
-Global::increaseErrorCount (void)
-{
- if (gag)
- this->gaggedErrors++;
-
- this->errors++;
-}
-
-
-/* Implements the Loc interface defined by the frontend.
- Used for keeping track of current file/line position in code. */
-
-Loc::Loc (const char *filename, unsigned linnum, unsigned charnum)
-{
- this->linnum = linnum;
- this->charnum = charnum;
- this->filename = filename;
-}
-
-const char *
-Loc::toChars (void) const
-{
- OutBuffer buf;
-
- if (this->filename)
- buf.printf ("%s", this->filename);
-
- if (this->linnum)
- {
- buf.printf (":%u", this->linnum);
- if (this->charnum)
- buf.printf (":%u", this->charnum);
- }
-
- return buf.extractChars ();
-}
-
-bool
-Loc::equals (const Loc &loc)
-{
- if (this->linnum != loc.linnum || this->charnum != loc.charnum)
- return false;
-
- if (!FileName::equals (this->filename, loc.filename))
- return false;
-
- return true;
-}
-
-
/* Implements back-end specific interfaces used by the frontend. */
/* Determine if function FD is a builtin one that we can evaluate in CTFE. */
@@ -144,7 +39,7 @@ Loc::equals (const Loc &loc)
BUILTIN
isBuiltin (FuncDeclaration *fd)
{
- if (fd->builtin != BUILTINunknown)
+ if (fd->builtin != BUILTIN::unknown)
return fd->builtin;
maybe_set_intrinsic (fd);
@@ -158,7 +53,7 @@ isBuiltin (FuncDeclaration *fd)
Expression *
eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments)
{
- if (fd->builtin == BUILTINunimp)
+ if (fd->builtin == BUILTIN::unimp)
return NULL;
tree decl = get_symbol_decl (fd);
@@ -185,16 +80,8 @@ eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments)
Type *
getTypeInfoType (Loc loc, Type *type, Scope *sc)
{
- gcc_assert (type->ty != Terror);
+ gcc_assert (type->ty != TY::Terror);
check_typeinfo_type (loc, sc);
create_typeinfo (type, sc ? sc->_module->importedFrom : NULL);
return type->vtinfo->type;
}
-
-/* Return an inlined copy of a default argument for a function parameter. */
-
-Expression *
-inlineCopy (Expression *e, Scope *)
-{
- return e->copy ();
-}
diff --git a/gcc/d/d-frontend.h b/gcc/d/d-frontend.h
new file mode 100644
index 00000000000..3edf812212a
--- /dev/null
+++ b/gcc/d/d-frontend.h
@@ -0,0 +1,37 @@
+/* d-frontend.h -- D frontend interface to the gcc back-end.
+ Copyright (C) 2019 Free Software Foundation, Inc.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_D_FRONTEND_H
+#define GCC_D_FRONTEND_H
+
+/* These functions are defined in D runtime. */
+extern "C" int rt_init (void);
+extern "C" int rt_term (void);
+//extern "C" void gc_disable (void);
+extern "C" void *gc_malloc (size_t sz, unsigned ba = 0, const void *ti = NULL);
+extern "C" void gc_free (void *);
+extern "C" void gc_collect (void);
+
+template<typename T>
+inline T *
+d_gc_malloc (void)
+{
+ void *ptr = gc_malloc (sizeof (T));
+ return new(ptr) T ();
+}
+
+#endif
diff --git a/gcc/d/d-incpath.cc b/gcc/d/d-incpath.cc
index d86392903e3..9a65622229b 100644
--- a/gcc/d/d-incpath.cc
+++ b/gcc/d/d-incpath.cc
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "dmd/globals.h"
+#include "d-frontend.h"
#include "cppdefault.h"
@@ -71,7 +72,7 @@ add_globalpaths (Strings *paths)
if (paths)
{
if (!global.path)
- global.path = new Strings ();
+ global.path = d_gc_malloc<Strings> ();
for (size_t i = 0; i < paths->length; i++)
{
@@ -98,7 +99,7 @@ add_filepaths (Strings *paths)
if (paths)
{
if (!global.filePath)
- global.filePath = new Strings ();
+ global.filePath = d_gc_malloc<Strings> ();
for (size_t i = 0; i < paths->length; i++)
{
diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc
index be6330fbdfb..d20370e5d8a 100644
--- a/gcc/d/d-lang.cc
+++ b/gcc/d/d-lang.cc
@@ -26,12 +26,13 @@ along with GCC; see the file COPYING3. If not see
#include "dmd/errors.h"
#include "dmd/expression.h"
#include "dmd/hdrgen.h"
+#include "dmd/id.h"
#include "dmd/identifier.h"
#include "dmd/json.h"
#include "dmd/mangle.h"
-#include "dmd/mars.h"
#include "dmd/module.h"
#include "dmd/mtype.h"
+#include "dmd/root/file.h"
#include "dmd/target.h"
#include "opts.h"
@@ -53,7 +54,7 @@ along with GCC; see the file COPYING3. If not see
#include "input.h"
#include "d-tree.h"
-#include "id.h"
+#include "d-frontend.h"
/* Array of D frontend type/decl nodes. */
@@ -83,10 +84,6 @@ d_option;
/* List of modules being compiled. */
static Modules builtin_modules;
-/* Module where `C main' is defined, compiled in if needed. */
-static Module *entrypoint_module = NULL;
-static Module *entrypoint_root_module = NULL;
-
/* The current and global binding level in effect. */
struct binding_level *current_binding_level;
struct binding_level *global_binding_level;
@@ -202,7 +199,7 @@ deps_write (Module *module, obstack *buffer)
deps_write_string (d_option.deps_target[i], buffer, column);
}
else
- deps_write_string (module->objfile->name->str, buffer, column);
+ deps_write_string (module->objfile.toChars (), buffer, column);
obstack_1grow (buffer, ':');
column++;
@@ -212,7 +209,7 @@ deps_write (Module *module, obstack *buffer)
{
Module *depmod = modlist.pop ();
- const char *modstr = depmod->srcfile->name->str;
+ const char *modstr = depmod->srcfile.toChars ();
/* Skip modules that have already been looked at. */
if (seen_modules.add (modstr))
@@ -238,9 +235,7 @@ deps_write (Module *module, obstack *buffer)
Module *m = depmod->aimports[i];
/* Ignore compiler-generated modules. */
- if ((m->ident == Identifier::idPool ("__entrypoint")
- || m->ident == Identifier::idPool ("__main"))
- && m->parent == NULL)
+ if (m->ident == Identifier::idPool ("__main") && m->parent == NULL)
continue;
/* Don't search system installed modules, this includes
@@ -251,9 +246,9 @@ deps_write (Module *module, obstack *buffer)
&& m->parent == NULL)
continue;
- if (m->md && m->md->packages)
+ if (m->md && m->md->packages.length)
{
- Identifier *package = (*m->md->packages)[0];
+ Identifier *package = m->md->packages.ptr[0];
if (package == Identifier::idPool ("core")
|| package == Identifier::idPool ("std")
@@ -291,27 +286,15 @@ deps_write (Module *module, obstack *buffer)
static void
d_init_options (unsigned int, cl_decoded_option *decoded_options)
{
+ /* Initialize the D runtime. */
+ rt_init ();
+// gc_disable ();
+
/* Set default values. */
global._init ();
global.vendor = lang_hooks.name;
global.params.argv0 = xstrdup (decoded_options[0].arg);
- global.params.link = true;
- global.params.useAssert = CHECKENABLEdefault;
- global.params.useInvariants = CHECKENABLEdefault;
- global.params.useIn = CHECKENABLEdefault;
- global.params.useOut = CHECKENABLEdefault;
- global.params.useArrayBounds = CHECKENABLEdefault;
- global.params.useSwitchError = CHECKENABLEdefault;
- global.params.checkAction = CHECKACTION_D;
- global.params.useModuleInfo = true;
- global.params.useTypeInfo = true;
- global.params.useExceptions = true;
- global.params.useInline = false;
- global.params.obj = true;
- global.params.hdrStripPlainFunctions = true;
- global.params.betterC = false;
- global.params.allInst = false;
global.params.errorLimit = flag_max_errors;
/* Default extern(C++) mangling to C++14. */
@@ -320,9 +303,10 @@ d_init_options (unsigned int, cl_decoded_option *decoded_options)
/* Warnings and deprecations are disabled by default. */
global.params.useDeprecated = DIAGNOSTICinform;
global.params.warnings = DIAGNOSTICoff;
+ global.params.messageStyle = MESSAGESTYLEgnu;
- global.params.imppath = new Strings ();
- global.params.fileImppath = new Strings ();
+ global.params.imppath = d_gc_malloc<Strings> ();
+ global.params.fileImppath = d_gc_malloc<Strings> ();
/* Extra GDC-specific options. */
d_option.fonly = NULL;
@@ -462,6 +446,11 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
: (value == 1) ? CHECKENABLEsafeonly : CHECKENABLEoff;
break;
+ case OPT_fcheckaction_:
+ global.params.checkAction = (value == 0) ? CHECKACTION_D
+ : (value == 1) ? CHECKACTION_halt : CHECKACTION_context;
+ break;
+
case OPT_fdebug:
global.params.debuglevel = value ? 1 : 0;
break;
@@ -480,7 +469,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
if (Identifier::isValidIdentifier (CONST_CAST (char *, arg)))
{
if (!global.params.debugids)
- global.params.debugids = new Strings ();
+ global.params.debugids = d_gc_malloc<Strings> ();
global.params.debugids->push (arg);
break;
}
@@ -510,6 +499,16 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
global.params.betterC = !value;
break;
+ case OPT_fdump_c___spec_:
+ if (global.params.doCxxHdrGeneration == CxxHeaderMode::none)
+ global.params.doCxxHdrGeneration = CxxHeaderMode::silent;
+ global.params.cxxhdrname = arg;
+ break;
+
+ case OPT_fdump_c___spec_verbose:
+ global.params.doCxxHdrGeneration = CxxHeaderMode::verbose;
+ break;
+
case OPT_fdump_d_original:
global.params.vcg_ast = value;
break;
@@ -518,6 +517,22 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
global.params.useExceptions = value;
break;
+ case OPT_fextern_std_:
+ switch (value)
+ {
+ case CppStdRevisionCpp98:
+ case CppStdRevisionCpp11:
+ case CppStdRevisionCpp14:
+ case CppStdRevisionCpp17:
+ case CppStdRevisionCpp20:
+ global.params.cplusplus = (CppStdRevision) value;
+ break;
+
+ default:
+ error ("bad argument for %<-fextern-std%>: %qs", arg);
+ }
+ break;
+
case OPT_fignore_unknown_pragmas:
global.params.ignoreUnsupportedPragmas = value;
break;
@@ -552,35 +567,115 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
global.params.useIn = value ? CHECKENABLEon : CHECKENABLEoff;
break;
+ case OPT_fpreview_all:
+ global.params.ehnogc = value;
+ global.params.useDIP25 = FeatureState::enabled;
+ global.params.useDIP1000 = FeatureState::enabled;
+ global.params.useDIP1021 = value;
+ global.params.dtorFields = FeatureState::enabled;
+ global.params.fieldwise = value;
+ global.params.fixAliasThis = value;
+ global.params.previewIn = value;
+ global.params.fix16997 = value;
+ global.params.markdown = value;
+ global.params.noSharedAccess = value;
+ global.params.rvalueRefParam = value;
+ global.params.inclusiveInContracts = value;
+ global.params.shortenedMethods = value;
+ break;
+
+ case OPT_fpreview_dip1000:
+ global.params.useDIP1000 = FeatureState::enabled;
+ break;
+
+ case OPT_fpreview_dip1008:
+ global.params.ehnogc = value;
+ break;
+
+ case OPT_fpreview_dip1021:
+ global.params.useDIP1021 = value;
+ break;
+
+ case OPT_fpreview_dip25:
+ global.params.useDIP25 = FeatureState::enabled;
+ break;
+
+ case OPT_fpreview_dtorfields:
+ global.params.dtorFields = FeatureState::enabled;
+ break;
+
+ case OPT_fpreview_fieldwise:
+ global.params.fieldwise = value;
+ break;
+
+ case OPT_fpreview_fixaliasthis:
+ global.params.fixAliasThis = value;
+ break;
+
+ case OPT_fpreview_in:
+ global.params.previewIn = value;
+ break;
+
+ case OPT_fpreview_inclusiveincontracts:
+ global.params.inclusiveInContracts = value;
+ break;
+
+ case OPT_fpreview_intpromote:
+ global.params.fix16997 = value;
+ break;
+
+ case OPT_fpreview_nosharedaccess:
+ global.params.noSharedAccess = value;
+ break;
+
+ case OPT_fpreview_rvaluerefparam:
+ global.params.rvalueRefParam = value;
+ break;
+
+ case OPT_fpreview_shortenedmethods:
+ global.params.shortenedMethods = value;
+ break;
+
case OPT_frelease:
global.params.release = value;
break;
- case OPT_frtti:
- global.params.useTypeInfo = value;
+ case OPT_frevert_all:
+ global.params.useDIP25 = FeatureState::disabled;
+ global.params.markdown = !value;
+ global.params.dtorFields = FeatureState::disabled;
break;
- case OPT_fswitch_errors:
- global.params.useSwitchError = value ? CHECKENABLEon : CHECKENABLEoff;
+ case OPT_frevert_dip25:
+ global.params.useDIP25 = FeatureState::disabled;
break;
- case OPT_ftransition_all:
- global.params.vtls = value;
- global.params.vfield = value;
- global.params.vcomplex = value;
+ case OPT_frevert_dtorfields:
+ global.params.dtorFields = FeatureState::disabled;
+ break;
+
+ case OPT_frevert_markdown:
+ global.params.markdown = !value;
break;
- case OPT_ftransition_complex:
- global.params.vcomplex = value;
+ case OPT_frtti:
+ global.params.useTypeInfo = value;
break;
- case OPT_ftransition_dip1000:
- global.params.vsafe = value;
- global.params.useDIP25 = value;
+ case OPT_fsave_mixins_:
+ global.params.mixinFile = arg;
+ global.params.mixinOut = d_gc_malloc<OutBuffer> ();
break;
- case OPT_ftransition_dip25:
- global.params.useDIP25 = value;
+ case OPT_fswitch_errors:
+ global.params.useSwitchError = value ? CHECKENABLEon : CHECKENABLEoff;
+ break;
+
+ case OPT_ftransition_all:
+ global.params.vfield = value;
+ global.params.vgc = value;
+ global.params.vmarkdown= value;
+ global.params.vtls = value;
break;
case OPT_ftransition_field:
@@ -591,6 +686,14 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
global.params.vgc = value;
break;
+ case OPT_ftransition_vmarkdown:
+ global.params.vmarkdown = value;
+ break;
+
+ case OPT_ftransition_templates:
+ global.params.vtemplates = value;
+ break;
+
case OPT_ftransition_tls:
global.params.vtls = value;
break;
@@ -613,7 +716,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
if (Identifier::isValidIdentifier (CONST_CAST (char *, arg)))
{
if (!global.params.versionids)
- global.params.versionids = new Strings ();
+ global.params.versionids = d_gc_malloc<Strings> ();
global.params.versionids->push (arg);
break;
}
@@ -804,6 +907,14 @@ d_post_options (const char ** fn)
global.params.checkAction = CHECKACTION_C;
}
+ /* Enabling DIP1021 implies DIP1000. */
+ if (global.params.useDIP1021)
+ global.params.useDIP1000 = FeatureState::enabled;
+
+ /* Enabling DIP1000 implies DIP25. */
+ if (global.params.useDIP1000 == FeatureState::enabled)
+ global.params.useDIP25 = FeatureState::enabled;
+
/* Keep in sync with existing -fbounds-check flag. */
flag_bounds_check = (global.params.useArrayBounds == CHECKENABLEon);
@@ -828,6 +939,7 @@ d_post_options (const char ** fn)
global.params.symdebug = write_symbols != NO_DEBUG;
global.params.useInline = flag_inline_functions;
global.params.showColumns = flag_show_column;
+ global.params.printErrorContext = flag_diagnostics_show_caret;
if (global.params.useInline)
global.params.hdrStripPlainFunctions = false;
@@ -872,17 +984,6 @@ d_add_builtin_module (Module *m)
builtin_modules.push (m);
}
-/* Record the entrypoint module ENTRY which will be compiled in the current
- compilation. ROOT is the module scope where this was requested from. */
-
-void
-d_add_entrypoint_module (Module *entry, Module *root)
-{
- /* We are emitting this straight to object file. */
- entrypoint_module = entry;
- entrypoint_root_module = root;
-}
-
/* Implements the lang_hooks.parse_file routine for language D. */
static void
@@ -891,7 +992,7 @@ d_parse_file (void)
if (global.params.verbose)
{
message ("binary %s", global.params.argv0.ptr);
- message ("version %s", global.version.ptr);
+ message ("version %s", global.versionChars ());
if (global.versionids)
{
@@ -955,16 +1056,16 @@ d_parse_file (void)
/* Handling stdin, generate a unique name for the module. */
Module *m = Module::create (in_fnames[i],
- Identifier::generateId ("__stdin"),
+ Identifier::idPool ("__stdin"),
global.params.doDocComments,
global.params.doHdrGeneration);
modules.push (m);
/* Overwrite the source file for the module, the one created by
Module::create would have a forced a `.d' suffix. */
- m->srcfile = File::create ("<stdin>");
- m->srcfile->len = len;
- m->srcfile->buffer = buffer;
+ m->srcBuffer = FileBuffer::create ();
+ m->srcBuffer->data.length = len;
+ m->srcBuffer->data.ptr = buffer;
}
else
{
@@ -1011,15 +1112,16 @@ d_parse_file (void)
}
/* Load the module containing D main. */
+ Module *main_module = NULL;
if (global.params.addMain)
{
unsigned errors = global.startGagging ();
- Module *m = Module::load (Loc (), NULL, Identifier::idPool ("__main"));
+ main_module = Module::load (Loc (), NULL, Identifier::idPool ("__main"));
if (!global.endGagging (errors))
{
- m->importedFrom = m;
- modules.push (m);
+ main_module->importedFrom = main_module;
+ modules.push (main_module);
}
}
@@ -1038,7 +1140,7 @@ d_parse_file (void)
for (size_t i = 0; i < modules.length; i++)
{
Module *m = modules[i];
- if (d_option.fonly && m != Module::rootModule)
+ if (m->isHdrFile || (d_option.fonly && m != Module::rootModule))
continue;
if (global.params.verbose)
@@ -1203,7 +1305,7 @@ d_parse_file (void)
if (name && (name[0] != '-' || name[1] != '\0'))
{
const char *nameext
- = FileName::defaultExt (name, global.json_ext.ptr);
+ = FileName::defaultExt (name, json_ext.ptr);
json_stream = fopen (nameext, "w");
if (!json_stream)
{
@@ -1245,22 +1347,25 @@ d_parse_file (void)
}
}
+ /* Generate C++ header files. */
+ if (global.params.doCxxHdrGeneration != CxxHeaderMode::none)
+ genCppHdrFiles (modules);
+
+ if (global.errors)
+ goto had_errors;
+
for (size_t i = 0; i < modules.length; i++)
{
Module *m = modules[i];
- if (d_option.fonly && m != Module::rootModule)
+ if ((m->isHdrFile && m != main_module)
+ || (d_option.fonly && m != Module::rootModule))
continue;
if (global.params.verbose)
message ("code %s", m->toChars ());
if (!flag_syntax_only)
- {
- if ((entrypoint_module != NULL) && (m == entrypoint_root_module))
- build_decl_tree (entrypoint_module);
-
- build_decl_tree (m);
- }
+ build_decl_tree (m);
}
/* And end the main input file, if the debug writer wants it. */
@@ -1272,16 +1377,37 @@ d_parse_file (void)
exit with an error status. */
errorcount += (global.errors + global.warnings);
+ /* We want to write the mixin expansion file also on error. */
+ if (global.params.mixinOut)
+ {
+ FILE *mixin_stream = fopen (global.params.mixinFile, "w");
+
+ if (mixin_stream)
+ {
+ OutBuffer *buf = global.params.mixinOut;
+ fprintf (mixin_stream, "%s", buf->peekChars ());
+
+ if (ferror (mixin_stream) || fclose (mixin_stream))
+ fatal_error (input_location, "closing mixin file %s: %m",
+ global.params.mixinFile);
+ }
+ else
+ {
+ fatal_error (input_location, "opening mixin file %s: %m",
+ global.params.mixinFile);
+ }
+ }
+
/* Remove generated .di files on error. */
if (errorcount && dump_headers)
{
for (size_t i = 0; i < modules.length; i++)
{
Module *m = modules[i];
- if (d_option.fonly && m != Module::rootModule)
+ if (m->isHdrFile || (d_option.fonly && m != Module::rootModule))
continue;
- remove (m->hdrfile->toChars ());
+ remove (m->hdrfile.toChars ());
}
}
@@ -1406,7 +1532,7 @@ d_type_promotes_to (tree type)
/* Promotions are only applied on unnamed function arguments for declarations
with `extern(C)' or `extern(C++)' linkage. */
if (cfun && DECL_LANG_FRONTEND (cfun->decl)
- && DECL_LANG_FRONTEND (cfun->decl)->linkage != LINKd)
+ && DECL_LANG_FRONTEND (cfun->decl)->linkage != LINK::d)
{
/* In [type/integer-promotions], integer promotions are conversions of the
following types:
@@ -1559,7 +1685,8 @@ d_types_compatible_p (tree x, tree y)
return true;
/* Type system allows implicit conversion between. */
- if (tx->implicitConvTo (ty) || ty->implicitConvTo (tx))
+ if (tx->implicitConvTo (ty) != MATCH::nomatch
+ || ty->implicitConvTo (tx) != MATCH::nomatch)
return true;
}
diff --git a/gcc/d/d-longdouble.cc b/gcc/d/d-longdouble.cc
index 471544ef79a..27c8d17a065 100644
--- a/gcc/d/d-longdouble.cc
+++ b/gcc/d/d-longdouble.cc
@@ -30,12 +30,6 @@ along with GCC; see the file COPYING3. If not see
#include "longdouble.h"
-/* Constant real values 0, 1, -1 and 0.5. */
-real_t CTFloat::zero;
-real_t CTFloat::one;
-real_t CTFloat::minusone;
-real_t CTFloat::half;
-
/* Truncate longdouble to the highest precision supported by target. */
longdouble
diff --git a/gcc/d/d-system.h b/gcc/d/d-system.h
index a6a9fccc6b8..d7a0079ecf3 100644
--- a/gcc/d/d-system.h
+++ b/gcc/d/d-system.h
@@ -26,60 +26,8 @@
#endif
#include "system.h"
-/* Used by the dmd front-end to determine if we have POSIX-style IO. */
-#define POSIX (__linux__ || __GLIBC__ || __gnu_hurd__ || __APPLE__ \
- || __FreeBSD__ || __NetBSD__ || __OpenBSD__ || __DragonFly__ \
- || __sun || __unix__)
-
/* Forward assert invariants to gcc_assert. */
#undef assert
#define assert(EXPR) gcc_assert(EXPR)
-/* Use libiberty's lrealpath to avoid portability problems. */
-#undef realpath
-#define realpath(a, b) lrealpath((a))
-
-/* Forward ctype.h macros used by the dmd front-end to safe-ctype.h. */
-#undef isalpha
-#define isalpha(c) ISALPHA(c)
-#undef isalnum
-#define isalnum(c) ISALNUM(c)
-#undef isdigit
-#define isdigit(c) ISDIGIT(c)
-#undef islower
-#define islower(c) ISLOWER(c)
-#undef isprint
-#define isprint(c) ISPRINT(c)
-#undef isspace
-#define isspace(c) ISSPACE(c)
-#undef isupper
-#define isupper(c) ISUPPER(c)
-#undef isxdigit
-#define isxdigit(c) ISXDIGIT(c)
-#undef tolower
-#define tolower(c) TOLOWER(c)
-
-/* Forward _mkdir on MinGW to mkdir in system.h. */
-#ifdef _WIN32
-#undef _mkdir
-#define _mkdir(p) mkdir(p, 0)
-#endif
-
-/* Define any missing _MAX and _MIN macros. */
-#ifndef INT32_MAX
-# define INT32_MAX INTTYPE_MAXIMUM (int32_t)
-#endif
-#ifndef INT32_MIN
-# define INT32_MIN INTTYPE_MINIMUM (int32_t)
-#endif
-#ifndef INT64_MIN
-# define INT64_MIN INTTYPE_MINIMUM (int64_t)
-#endif
-#ifndef UINT32_MAX
-# define UINT32_MAX INTTYPE_MAXIMUM (uint32_t)
-#endif
-#ifndef UINT64_MAX
-# define UINT64_MAX INTTYPE_MAXIMUM (uint64_t)
-#endif
-
#endif /* GCC_D_SYSTEM_H */
diff --git a/gcc/d/d-target.cc b/gcc/d/d-target.cc
index 1488bcebb2c..21417dddf78 100644
--- a/gcc/d/d-target.cc
+++ b/gcc/d/d-target.cc
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "tm.h"
#include "tm_p.h"
#include "target.h"
+#include "calls.h"
#include "d-tree.h"
#include "d-target.h"
@@ -42,8 +43,6 @@ along with GCC; see the file COPYING3. If not see
/* Implements the Target interface defined by the front end.
Used for retrieving target-specific information. */
-Target target;
-
/* Internal key handlers for `__traits(getTargetInfo)'. */
static tree d_handle_target_cpp_std (void);
static tree d_handle_target_cpp_runtime_library (void);
@@ -89,9 +88,6 @@ define_float_constants (T &f, tree type)
/* Floating-point NaN. */
real_nan (&f.nan.rv (), "", 1, mode);
- /* Signalling floating-point NaN. */
- real_nan (&f.snan.rv (), "", 0, mode);
-
/* Floating-point +Infinity if the target supports infinities. */
real_inf (&f.infinity.rv ());
@@ -142,19 +138,19 @@ Target::_init (const Param &)
/* Define what type to use for size_t, ptrdiff_t. */
if (this->ptrsize == 8)
{
- global.params.isLP64 = true;
- Type::tsize_t = Type::basic[Tuns64];
- Type::tptrdiff_t = Type::basic[Tint64];
+ this->isLP64 = true;
+ Type::tsize_t = Type::basic[(int)TY::Tuns64];
+ Type::tptrdiff_t = Type::basic[(int)TY::Tint64];
}
else if (this->ptrsize == 4)
{
- Type::tsize_t = Type::basic[Tuns32];
- Type::tptrdiff_t = Type::basic[Tint32];
+ Type::tsize_t = Type::basic[(int)TY::Tuns32];
+ Type::tptrdiff_t = Type::basic[(int)TY::Tint32];
}
else if (this->ptrsize == 2)
{
- Type::tsize_t = Type::basic[Tuns16];
- Type::tptrdiff_t = Type::basic[Tint16];
+ Type::tsize_t = Type::basic[(int)TY::Tuns16];
+ Type::tptrdiff_t = Type::basic[(int)TY::Tint16];
}
else
sorry ("D does not support pointers on this target.");
@@ -164,15 +160,7 @@ Target::_init (const Param &)
/* Set-up target C ABI. */
this->c.longsize = int_size_in_bytes (long_integer_type_node);
this->c.long_doublesize = int_size_in_bytes (long_double_type_node);
-
- /* Define what type to use for wchar_t. We don't want to support wide
- characters less than "short" in D. */
- if (WCHAR_TYPE_SIZE == 32)
- this->c.twchar_t = Type::basic[Tdchar];
- else if (WCHAR_TYPE_SIZE == 16)
- this->c.twchar_t = Type::basic[Twchar];
- else
- sorry ("D does not support wide characters on this target.");
+ this->c.wchar_tsize = (WCHAR_TYPE_SIZE / BITS_PER_UNIT);
/* Set-up target C++ ABI. */
this->cpp.reverseOverloads = false;
@@ -182,6 +170,12 @@ Target::_init (const Param &)
/* Set-up target Objective-C ABI. */
this->objc.supported = false;
+ /* Set-up environmental settings. */
+ this->obj_ext = "o";
+ this->lib_ext = "a";
+ this->dll_ext = "so";
+ this->run_noext = true;
+
/* Initialize all compile-time properties for floating-point types.
Should ensure that our real_t type is able to represent real_value. */
gcc_assert (sizeof (real_t) >= sizeof (real_value));
@@ -273,7 +267,7 @@ Target::isVectorTypeSupported (int sz, Type *type)
type = Type::tuns8;
/* No support for non-trivial types, complex types, or booleans. */
- if (!type->isTypeBasic () || type->iscomplex () || type->ty == Tbool)
+ if (!type->isTypeBasic () || type->iscomplex () || type->ty == TY::Tbool)
return 2;
/* In [simd/vector extensions], which vector types are supported depends on
@@ -293,9 +287,9 @@ Target::isVectorTypeSupported (int sz, Type *type)
Returns true if the operation is supported or type is not a vector. */
bool
-Target::isVectorOpSupported (Type *type, TOK op, Type *)
+Target::isVectorOpSupported (Type *type, unsigned op, Type *)
{
- if (type->ty != Tvector)
+ if (type->ty != TY::Tvector)
return true;
/* Don't support if type is non-scalar, such as __vector(void[]). */
@@ -322,18 +316,10 @@ Target::isVectorOpSupported (Type *type, TOK op, Type *)
/* Logical operators must have a result type of bool. */
return false;
- case TOKue:
- case TOKlg:
- case TOKule:
- case TOKul:
- case TOKuge:
- case TOKug:
case TOKle:
case TOKlt:
case TOKge:
case TOKgt:
- case TOKleg:
- case TOKunord:
case TOKequal:
case TOKnotequal:
case TOKidentity:
@@ -379,7 +365,8 @@ TargetCPP::thunkMangle (FuncDeclaration *fd, int offset)
const char *
TargetCPP::typeMangle (Type *type)
{
- if (type->isTypeBasic () || type->ty == Tvector || type->ty == Tstruct)
+ if (type->isTypeBasic () || type->ty == TY::Tvector
+ || type->ty == TY::Tstruct)
{
tree ctype = build_ctype (type);
return targetm.mangle_type (ctype);
@@ -400,14 +387,14 @@ TargetCPP::parameterType (Parameter *arg)
else if (arg->storageClass & STClazy)
{
/* Mangle as delegate. */
- Type *td = TypeFunction::create (NULL, t, VARARGnone, LINKd);
- td = TypeDelegate::create (td);
- t = t->merge2 ();
+ TypeFunction *tf = TypeFunction::create (NULL, t, VARARGnone, LINK::d);
+ TypeDelegate *td = TypeDelegate::create (tf);
+ t = td->merge2 ();
}
/* Could be a va_list, which we mangle as a pointer. */
Type *tvalist = target.va_listType (Loc (), NULL);
- if (t->ty == Tsarray && tvalist->ty == Tsarray)
+ if (t->ty == TY::Tsarray && tvalist->ty == TY::Tsarray)
{
Type *tb = t->toBasetype ()->mutableOf ();
if (tb == tvalist)
@@ -450,10 +437,10 @@ Target::systemLinkage (void)
/* In [attribute/linkage], `System' is the same as `Windows' on Windows
platforms, and `C' on other platforms. */
if (link_system)
- return LINKwindows;
+ return LINK::windows;
}
- return LINKc;
+ return LINK::c;
}
/* Generate a TypeTuple of the equivalent types used to determine if a
@@ -477,12 +464,12 @@ Target::isReturnOnStack (TypeFunction *tf, bool)
/* Need the back-end type to determine this, but this is called from the
frontend before semantic processing is finished. An accurate value
is not currently needed anyway. */
- if (tf->isref)
+ if (tf->isref ())
return false;
Type *tn = tf->next->toBasetype ();
- return (tn->ty == Tstruct || tn->ty == Tsarray);
+ return (tn->ty == TY::Tstruct || tn->ty == TY::Tsarray);
}
/* Add all target info in HANDLERS to D_TARGET_INFO_TABLE for use by
@@ -575,12 +562,49 @@ Target::getTargetInfo (const char *key, const Loc &loc)
return NULL;
}
-/**
- * Returns true if the implementation for object monitors is always defined
- * in the D runtime library (rt/monitor_.d). */
+/* Returns true if the callee invokes destructors for arguments. */
+
+bool
+Target::isCalleeDestroyingArgs (TypeFunction *tf)
+{
+ return tf->linkage == LINK::d;
+}
+
+/* Returns true if the implementation for object monitors is always defined
+ in the D runtime library (rt/monitor_.d). */
bool
Target::libraryObjectMonitors (FuncDeclaration *, Statement *)
{
return true;
}
+
+/* Decides whether an `in' parameter of the specified POD type PARAM_TYPE is to
+ be passed by reference or by valie. This is used only when compiling with
+ `-fpreview=in' enabled. */
+
+bool
+Target::preferPassByRef (Type *param_type)
+{
+ if (param_type->size () == SIZE_INVALID)
+ return false;
+
+ tree type = build_ctype (param_type);
+
+ /* Prefer a `ref' if the type is an aggregate, and its size is greater than
+ its alignment. */
+ if (AGGREGATE_TYPE_P (type)
+ && (!valid_constant_size_p (TYPE_SIZE_UNIT (type))
+ || compare_tree_int (TYPE_SIZE_UNIT (type), TYPE_ALIGN (type)) > 0))
+ return true;
+
+ /* If the back-end is always going to pass this by invisible reference. */
+ if (pass_by_reference (NULL, function_arg_info (type, true)))
+ return true;
+
+ /* If returning the parameter means the caller will do RVO. */
+ if (targetm.calls.return_in_memory (type, NULL_TREE))
+ return true;
+
+ return false;
+}
diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h
index 9b90ca530e6..328b6b861d2 100644
--- a/gcc/d/d-tree.h
+++ b/gcc/d/d-tree.h
@@ -617,7 +617,6 @@ extern void add_import_paths (const char *, const char *, bool);
/* In d-lang.cc. */
extern void d_add_builtin_module (Module *);
-extern void d_add_entrypoint_module (Module *, Module *);
extern d_tree_node_structure_enum d_tree_node_structure (lang_tree_node *);
extern struct lang_type *build_lang_type (Type *);
extern struct lang_decl *build_lang_decl (Declaration *);
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc
index 9c9205fa349..e28a581a7ec 100644
--- a/gcc/d/decl.cc
+++ b/gcc/d/decl.cc
@@ -106,9 +106,9 @@ gcc_attribute_p (Dsymbol *decl)
{
ModuleDeclaration *md = decl->getModule ()->md;
- if (md && md->packages && md->packages->length == 1)
+ if (md && md->packages.length == 1)
{
- if (!strcmp ((*md->packages)[0]->toChars (), "gcc")
+ if (!strcmp (md->packages.ptr[0]->toChars (), "gcc")
&& !strcmp (md->id->toChars (), "attributes"))
return true;
}
@@ -116,6 +116,59 @@ gcc_attribute_p (Dsymbol *decl)
return false;
}
+/* Subroutine of pragma declaration visitor for marking the function in the
+ defined in SYM as a global constructor or destructor. If ISCTOR is true,
+ then we're applying pragma(crt_constructor). */
+
+static int
+apply_pragma_crt (Dsymbol *sym, bool isctor)
+{
+ AttribDeclaration *ad = sym->isAttribDeclaration ();
+ if (ad != NULL)
+ {
+ int nested = 0;
+
+ /* Walk all declarations of the attribute scope. */
+ Dsymbols *ds = ad->include (NULL);
+ if (ds)
+ {
+ for (size_t i = 0; i < ds->length; i++)
+ nested += apply_pragma_crt ((*ds)[i], isctor);
+ }
+
+ return nested;
+ }
+
+ FuncDeclaration *fd = sym->isFuncDeclaration ();
+ if (fd != NULL)
+ {
+ tree decl = get_decl_tree (fd);
+
+ /* Apply flags to the function. */
+ if (isctor)
+ {
+ DECL_STATIC_CONSTRUCTOR (decl) = 1;
+ decl_init_priority_insert (decl, DEFAULT_INIT_PRIORITY);
+ }
+ else
+ {
+ DECL_STATIC_DESTRUCTOR (decl) = 1;
+ decl_fini_priority_insert (decl, DEFAULT_INIT_PRIORITY);
+ }
+
+ if (fd->linkage != LINK::c)
+ {
+ error_at (make_location_t (fd->loc),
+ "must be %<extern(C)%> for %<pragma(%s)%>",
+ isctor ? "crt_constructor" : "crt_destructor");
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
/* Implements the visitor interface to lower all Declaration AST classes
emitted from the D Front-end to GCC trees.
All visit methods accept one parameter D, which holds the frontend AST
@@ -246,19 +299,31 @@ public:
}
/* Pragmas are a way to pass special information to the compiler and to add
- vendor specific extensions to D. We don't do anything here, yet. */
+ vendor specific extensions to D. */
void visit (PragmaDeclaration *d)
{
- if (!global.params.ignoreUnsupportedPragmas)
+ if (d->ident == Identifier::idPool ("lib")
+ || d->ident == Identifier::idPool ("startaddress"))
{
- if (d->ident == Identifier::idPool ("lib")
- || d->ident == Identifier::idPool ("startaddress"))
+ if (!global.params.ignoreUnsupportedPragmas)
{
warning_at (make_location_t (d->loc), OPT_Wunknown_pragmas,
"pragma(%s) not implemented", d->ident->toChars ());
}
}
+ else if (d->ident == Identifier::idPool ("crt_constructor")
+ || d->ident == Identifier::idPool ("crt_destructor"))
+ {
+ /* Handle pragma(crt_constructor) and pragma(crt_destructor). Apply
+ flag to indicate that the functions enclosed should run automatically
+ at the beginning or end of execution. */
+ bool isctor = (d->ident == Identifier::idPool ("crt_constructor"));
+
+ if (apply_pragma_crt (d, isctor) > 1)
+ error_at (make_location_t (d->loc),
+ "can only apply to a single declaration");
+ }
visit ((AttribDeclaration *) d);
}
@@ -311,14 +376,14 @@ public:
nested members. Only applies to classes or structs. */
Type *tb = fd->type->nextOf ()->baseElemOf ();
- while (tb->ty == Tarray || tb->ty == Tpointer)
+ while (tb->ty == TY::Tarray || tb->ty == TY::Tpointer)
tb = tb->nextOf ()->baseElemOf ();
TemplateInstance *ti = NULL;
- if (tb->ty == Tstruct)
+ if (tb->ty == TY::Tstruct)
ti = tb->isTypeStruct ()->sym->isInstantiated ();
- else if (tb->ty == Tclass)
+ else if (tb->ty == TY::Tclass)
ti = tb->isTypeClass ()->sym->isInstantiated ();
/* Return type is instantiated from this template declaration, walk over
@@ -360,7 +425,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
- if (d->type->ty == Terror)
+ if (d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@@ -447,7 +512,8 @@ public:
if (fd2->isFuture ())
continue;
- if (fd->leastAsSpecialized (fd2) || fd2->leastAsSpecialized (fd))
+ if (fd->leastAsSpecialized (fd2) != MATCH::nomatch
+ || fd2->leastAsSpecialized (fd) != MATCH::nomatch)
{
error_at (make_location_t (fd->loc), "use of %qs",
fd->toPrettyChars ());
@@ -474,7 +540,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
- if (d->type->ty == Terror)
+ if (d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@@ -495,7 +561,8 @@ public:
/* Generate C symbols. */
d->csym = get_classinfo_decl (d);
- d->vtblsym = get_vtable_decl (d);
+ Dsymbol *vtblsym = d->vtblSymbol ();
+ vtblsym->csym = get_vtable_decl (d);
tree sinit = aggregate_initializer_decl (d);
/* Generate static initializer. */
@@ -527,9 +594,9 @@ public:
}
}
- DECL_INITIAL (d->vtblsym)
- = build_constructor (TREE_TYPE (d->vtblsym), elms);
- d_finish_decl (d->vtblsym);
+ DECL_INITIAL (vtblsym->csym)
+ = build_constructor (TREE_TYPE (vtblsym->csym), elms);
+ d_finish_decl (vtblsym->csym);
/* Add this decl to the current binding level. */
tree ctype = TREE_TYPE (build_ctype (d->type));
@@ -547,7 +614,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
- if (d->type->ty == Terror)
+ if (d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@@ -590,7 +657,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
- if (d->errors || d->type->ty == Terror)
+ if (d->errors || d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@@ -629,7 +696,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
- if (d->type->ty == Terror)
+ if (d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@@ -695,7 +762,7 @@ public:
/* Frontend should have already caught this. */
gcc_assert (!integer_zerop (size)
- || d->type->toBasetype ()->ty == Tsarray);
+ || d->type->toBasetype ()->ty == TY::Tsarray);
d_finish_decl (decl);
@@ -770,7 +837,7 @@ public:
/* Check if any errors occurred when running semantic. */
if (TypeFunction *tf = d->type->isTypeFunction ())
{
- if (tf->next == NULL || tf->next->ty == Terror)
+ if (tf->next == NULL || tf->next->ty == TY::Terror)
return;
}
@@ -1183,6 +1250,17 @@ get_symbol_decl (Declaration *decl)
}
else if (TREE_CODE (decl->csym) == FUNCTION_DECL)
{
+ /* Dual-context functions require the code generation to build an array
+ for the context pointer of the function, making the delicate task of
+ tracking which context to follow when encountering a non-local symbol,
+ and so are a not planned to be supported. */
+ if (fd->needThis () && !fd->isMember2 ())
+ {
+ fatal_error (make_location_t (fd->loc),
+ "function requires a dual-context, which is not yet "
+ "supported by GDC");
+ }
+
/* The real function type may differ from its declaration. */
tree fntype = TREE_TYPE (decl->csym);
tree newfntype = NULL_TREE;
@@ -1238,9 +1316,9 @@ get_symbol_decl (Declaration *decl)
/* In [pragma/inline], functions decorated with `pragma(inline)' affects
whether they are inlined or not. */
- if (fd->inlining == PINLINEalways)
+ if (fd->inlining == PINLINE::always)
DECL_DECLARED_INLINE_P (decl->csym) = 1;
- else if (fd->inlining == PINLINEnever)
+ else if (fd->inlining == PINLINE::never)
DECL_UNINLINABLE (decl->csym) = 1;
/* Function was declared `naked'. */
@@ -1254,13 +1332,6 @@ get_symbol_decl (Declaration *decl)
if (fd->generated)
DECL_ARTIFICIAL (decl->csym) = 1;
- /* Vector array operations are always compiler generated. */
- if (fd->isArrayOp)
- {
- DECL_ARTIFICIAL (decl->csym) = 1;
- DECL_DECLARED_INLINE_P (decl->csym) = 1;
- }
-
/* Ensure and require contracts are lexically nested in the function they
part of, but are always publicly callable. */
if (fd->ident == Identifier::idPool ("ensure")
@@ -1271,7 +1342,7 @@ get_symbol_decl (Declaration *decl)
DECL_FINAL_P (decl->csym) = 1;
/* Function is of type `noreturn' or `typeof(*null)'. */
- if (fd->type->nextOf ()->ty == Tnoreturn)
+ if (fd->type->nextOf ()->ty == TY::Tnoreturn)
TREE_THIS_VOLATILE (decl->csym) = 1;
/* Check whether this function is expanded by the frontend. */
@@ -1298,10 +1369,10 @@ get_symbol_decl (Declaration *decl)
if (decl->storage_class & STCvolatile)
TREE_THIS_VOLATILE (decl->csym) = 1;
- /* Protection attributes are used by the debugger. */
- if (decl->protection.kind == Prot::private_)
+ /* Visibility attributes are used by the debugger. */
+ if (decl->visibility.kind == Visibility::private_)
TREE_PRIVATE (decl->csym) = 1;
- else if (decl->protection.kind == Prot::protected_)
+ else if (decl->visibility.kind == Visibility::protected_)
TREE_PROTECTED (decl->csym) = 1;
/* Likewise, so could the deprecated attribute. */
@@ -1794,7 +1865,7 @@ make_thunk (FuncDeclaration *decl, int offset)
forcing a D local thunk to be emitted. */
const char *ident;
- if (decl->linkage == LINKcpp)
+ if (decl->linkage == LINK::cpp)
ident = target.cpp.thunkMangle (decl, offset);
else
{
@@ -1810,7 +1881,9 @@ make_thunk (FuncDeclaration *decl, int offset)
SET_DECL_ASSEMBLER_NAME (thunk, DECL_NAME (thunk));
d_keep (thunk);
- free (CONST_CAST (char *, ident));
+
+ if (decl->linkage != LINK::cpp)
+ free (CONST_CAST (char *, ident));
if (!DECL_EXTERNAL (function))
finish_thunk (thunk, function);
@@ -1989,26 +2062,27 @@ d_mark_needed (tree decl)
tree
get_vtable_decl (ClassDeclaration *decl)
{
- if (decl->vtblsym)
- return decl->vtblsym;
+ if (decl->vtblsym && decl->vtblsym->csym)
+ return decl->vtblsym->csym;
tree ident = mangle_internal_decl (decl, "__vtbl", "Z");
/* Note: Using a static array type for the VAR_DECL, the DECL_INITIAL value
will have a different type. However the back-end seems to accept this. */
tree type = build_ctype (Type::tvoidptr->sarrayOf (decl->vtbl.length));
- decl->vtblsym = declare_extern_var (ident, type);
- DECL_LANG_SPECIFIC (decl->vtblsym) = build_lang_decl (NULL);
+ Dsymbol *vtblsym = decl->vtblSymbol ();
+ vtblsym->csym = declare_extern_var (ident, type);
+ DECL_LANG_SPECIFIC (vtblsym->csym) = build_lang_decl (NULL);
/* Class is a reference, want the record type. */
- DECL_CONTEXT (decl->vtblsym) = TREE_TYPE (build_ctype (decl->type));
- TREE_READONLY (decl->vtblsym) = 1;
- DECL_VIRTUAL_P (decl->vtblsym) = 1;
+ DECL_CONTEXT (vtblsym->csym) = TREE_TYPE (build_ctype (decl->type));
+ TREE_READONLY (vtblsym->csym) = 1;
+ DECL_VIRTUAL_P (vtblsym->csym) = 1;
- SET_DECL_ALIGN (decl->vtblsym, TARGET_VTABLE_ENTRY_ALIGN);
- DECL_USER_ALIGN (decl->vtblsym) = true;
+ SET_DECL_ALIGN (vtblsym->csym, TARGET_VTABLE_ENTRY_ALIGN);
+ DECL_USER_ALIGN (vtblsym->csym) = true;
- return decl->vtblsym;
+ return vtblsym->csym;
}
/* Helper function of build_class_instance. Find the field inside aggregate
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index 2568993fbf4..129050b6780 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@
-27e388b4c4d292cac25811496aaf79341c05c940
+b8384668f28741ad5884fc055a2bdb9c05fd95ec
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md
new file mode 100644
index 00000000000..720d25683af
--- /dev/null
+++ b/gcc/d/dmd/README.md
@@ -0,0 +1,259 @@
+# DMD Source code
+
+This is the source code to the DMD compiler
+for the D Programming Language defined in the documents at
+http://dlang.org/
+
+These sources are free, they are redistributable and modifiable
+under the terms of the Boost Software License, Version 1.0.
+The terms of this license are in the file boostlicense.txt,
+or see http://www.boost.org/LICENSE_1_0.txt.
+
+If a particular file has a different license in it, that overrides
+this license for that file.
+
+-Walter Bright
+
+## Directory structure
+
+| Folder | Purpose |
+|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [dmd/](https://github.com/dlang/dmd/tree/master/src/dmd) | The dmd driver and front-end |
+| [dmd/backend/](https://github.com/dlang/dmd/tree/master/src/dmd/backend) | Code generation for x86 or x86-64. Shared by the [Digital Mars C compiler](https://github.com/DigitalMars/Compiler/), but not [LDC](https://github.com/ldc-developers/ldc) or [GDC](https://gdcproject.org/). |
+| [dmd/root/](https://github.com/dlang/dmd/tree/master/src/dmd/root) | Meant as a portable utility library, but ["it wasn't very good and the only project left using it is dmd"](https://github.com/dlang/dmd/pull/9844#issuecomment-498479516). |
+
+DMD has a mostly flat directory structure, so this section aims to divide all source files into logical groups for easier navigation.
+The groups are roughly ordered by how late they appear in the compilation process.
+Note that these groups have no strict meaning, the category assignments are a bit subjective.
+
+### Driver
+
+| File | Purpose |
+|-----------------------------------------------------------------------------|-----------------------------------------------------------------------|
+| [mars.d](https://github.com/dlang/dmd/blob/master/src/dmd/mars.d) | The entry point. Contains `main`. |
+| [cli.d](https://github.com/dlang/dmd/blob/master/src/dmd/cli.d) | Define the command line interface |
+| [globals.d](https://github.com/dlang/dmd/blob/master/src/dmd/globals.d) | Define a structure storing command line options |
+| [dinifile.d](https://github.com/dlang/dmd/blob/master/src/dmd/dinifile.d) | Parse settings from .ini file (`sc.ini` / `dmd.conf`) |
+| [vsoptions.d](https://github.com/dlang/dmd/blob/master/src/dmd/vsoptions.d) | Detect the Microsoft Visual Studio toolchain for linking |
+| [frontend.d](https://github.com/dlang/dmd/blob/master/src/dmd/frontend.d) | An interface for using DMD as a library |
+| [errors.d](https://github.com/dlang/dmd/blob/master/src/dmd/errors.d) | Error reporting functionality |
+| [target.d](https://github.com/dlang/dmd/blob/master/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) |
+| [compiler.d](https://github.com/dlang/dmd/blob/master/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions |
+
+### Lexing / parsing
+
+| File | Purpose |
+|-----------------------------------------------------------------------|----------------------------------------------------------------------|
+| [lexer.d](https://github.com/dlang/dmd/blob/master/src/dmd/lexer.d) | Convert source code into tokens for the D and ImportC parsers |
+| [entity.d](https://github.com/dlang/dmd/blob/master/src/dmd/entity.d) | Define "\\&Entity;" escape sequence for strings / character literals |
+| [tokens.d](https://github.com/dlang/dmd/blob/master/src/dmd/tokens.d) | Define lexical tokens. |
+| [parse.d](https://github.com/dlang/dmd/blob/master/src/dmd/parse.d) | D parser, converting tokens into an Abstract Syntax Tree (AST) |
+| [cparse.d](https://github.com/dlang/dmd/blob/master/src/dmd/cparse.d) | ImportC parser, converting tokens into an Abstract Syntax Tree (AST) |
+
+### Semantic analysis
+
+**Symbols and declarations**
+
+| File | Purpose |
+|---------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
+| [dsymbol.d](https://github.com/dlang/dmd/blob/master/src/dmd/dsymbol.d) | Base class for a D symbol, e.g. a variable, function, module, enum etc. |
+| [identifier.d](https://github.com/dlang/dmd/blob/master/src/dmd/identifier.d) | Represents the name of a `Dsymbol` |
+| [id.d](https://github.com/dlang/dmd/blob/master/src/dmd/id.d) | Define strings for pre-defined identifiers (e.g. `sizeof`, `string`) |
+| [dscope.d](https://github.com/dlang/dmd/blob/master/src/dmd/dscope.d) | Define a 'scope' on which symbol lookup can be performed |
+| [dtemplate.d](https://github.com/dlang/dmd/blob/master/src/dmd/dtemplate.d) | A template declaration or instance |
+| [dmodule.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmodule.d) | Define a package and module |
+| [mtype.d](https://github.com/dlang/dmd/blob/master/src/dmd/mtype.d) | Define expression types such as `int`, `char[]`, `void function()` |
+| [arraytypes.d](https://github.com/dlang/dmd/blob/master/src/dmd/arraytypes.d) | For certain Declaration nodes of type `T`, provides aliases for `Array!T` |
+| [declaration.d](https://github.com/dlang/dmd/blob/master/src/dmd/declaration.d) | Misc. declarations of `alias`, variables, type tuples, `ClassInfo` etc. |
+| [denum.d](https://github.com/dlang/dmd/blob/master/src/dmd/denum.d) | Defines `enum` declarations and enum members |
+| [attrib.d](https://github.com/dlang/dmd/blob/master/src/dmd/nogc.d) | Declarations of 'attributes' such as `private`, `pragma()`, `immutable`, `@UDA`, `align`, `extern(C++)` and more |
+| [func.d](https://github.com/dlang/dmd/blob/master/src/dmd/func.d) | Define a function declaration (includes function literals, `invariant`, `unittest`) |
+| [dversion.d](https://github.com/dlang/dmd/blob/master/src/dmd/dversion.d) | Defines a version symbol, e.g. `version = ident`, `debug = ident` |
+
+**AST nodes**
+
+| File | Purpose |
+|-----------------------------------------------------------------------------------|-------------------------------------------------------------|
+| [ast_node.d](https://github.com/dlang/dmd/blob/master/src/dmd/ast_node.d) | Define an abstract AST node class |
+| [astbase.d](https://github.com/dlang/dmd/blob/master/src/dmd/astbase.d) | Namespace of AST nodes that can be produced by the parser |
+| [astcodegen.d](https://github.com/dlang/dmd/blob/master/src/dmd/astcodegen.d) | Namespace of AST nodes of a AST ready for code generation |
+| [astenums.d](https://github.com/dlang/dmd/blob/master/src/dmd/astenums.d) | Enums common to DMD and AST |
+| [expression.d](https://github.com/dlang/dmd/blob/master/src/dmd/expression.d) | Define expression AST nodes |
+| [statement.d](https://github.com/dlang/dmd/blob/master/src/dmd/statement.d) | Define statement AST nodes |
+| [staticassert.d](https://github.com/dlang/dmd/blob/master/src/dmd/staticassert.d) | Define a `static assert` AST node |
+| [aggregate.d](https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d) | Define an aggregate (`struct`, `union` or `class`) AST node |
+| [dclass.d](https://github.com/dlang/dmd/blob/master/src/dmd/dclass.d) | Define a `class` AST node |
+| [dstruct.d](https://github.com/dlang/dmd/blob/master/src/dmd/dstruct.d) | Define a `struct` or `union` AST node |
+| [init.d](https://github.com/dlang/dmd/blob/master/src/dmd/init.d) | Define variable initializers |
+
+**AST visitors**
+
+| File | Purpose |
+|-----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
+| [parsetimevisitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/parsetimevisitor.d) | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes |
+| [permissivevisitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/permissivevisitor.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes |
+| [strictvisitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/strictvisitor.d) | Visitor that forces derived classes to implement `visit` for every possible node |
+| [visitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/visitor.d) | A visitor implementing `visit` for all nodes present in the compiler |
+| [transitivevisitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/transitivevisitor.d) | Provide a mixin template with visit methods for the parse time AST |
+| [apply.d](https://github.com/dlang/dmd/blob/master/src/dmd/apply.d) | Depth-first expression visitor |
+| [sapply.d](https://github.com/dlang/dmd/blob/master/src/dmd/sapply.d) | Depth-first statement visitor |
+| [statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/src/dmd/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node |
+
+**Semantic passes**
+
+| File | Purpose |
+|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
+| [dsymbolsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/dsymbolsem.d) | Do semantic 1 pass (symbol identifiers/types) |
+| [semantic2.d](https://github.com/dlang/dmd/blob/master/src/dmd/semantic2.d) | Do semantic 2 pass (symbol initializers) |
+| [semantic3.d](https://github.com/dlang/dmd/blob/master/src/dmd/semantic3.d) | Do semantic 3 pass (function bodies) |
+| [inline.d](https://github.com/dlang/dmd/blob/master/src/dmd/inline.d) | Do inline pass (optimization pass that dmd does in the front-end) |
+| [inlinecost.d](https://github.com/dlang/dmd/blob/master/src/dmd/inlinecost.d) | Compute the cost of inlining a function call. |
+| [expressionsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/expressionsem.d) | Do semantic analysis for expressions |
+| [statementsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/statementsem.d) | Do semantic analysis for statements |
+| [initsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/initsem.d) | Do semantic analysis for initializers |
+| [templateparamsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/templateparamsem.d) | Do semantic analysis for template parameters |
+| [typesem.d](https://github.com/dlang/dmd/blob/master/src/dmd/typesem.d) | Do semantic analysis for types |
+
+**Semantic helpers**
+
+| File | Purpose |
+|-------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
+| [opover.d](https://github.com/dlang/dmd/blob/master/src/dmd/opover.d) | Operator overloading |
+| [clone.d](https://github.com/dlang/dmd/blob/master/src/dmd/dsymbolsem.d) | Generate automatic `opEquals`, `opAssign` and constructors for structs |
+| [blockexit.d](https://github.com/dlang/dmd/blob/master/src/dmd/blockexit.d) | Find out in what ways control flow can exit a block |
+| [ctorflow.d](https://github.com/dlang/dmd/blob/master/src/dmd/ctorflow.d) | Control flow in constructors |
+| [constfold.d](https://github.com/dlang/dmd/blob/master/src/dmd/constfold.d) | Do constant folding of arithmetic expressions |
+| [optimize.d](https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d) | Do constant folding more generally |
+| [dcast.d](https://github.com/dlang/dmd/blob/master/src/dmd/dcast.d) | Implicit or explicit cast(), finding common types e.g. in `x ? a : b`, integral promotions |
+| [impcnvtab.d](https://github.com/dlang/dmd/blob/master/src/dmd/impcnvtab.d) | Define an implicit conversion table for basic types |
+| [sideeffect.d](https://github.com/dlang/dmd/blob/master/src/dmd/sideeffect.d) | Extract side-effects of expressions for certain lowerings. |
+
+**Compile Time Function Execution (CTFE)**
+
+| File | Purpose |
+|-------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
+| [dinterpret.d](https://github.com/dlang/dmd/blob/master/src/dmd/dinterpret.d) | CTFE entry point |
+| [ctfeexpr.d](https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d) | CTFE for expressions involving pointers, slices, array concatenation etc. |
+| [builtin.d](https://github.com/dlang/dmd/blob/master/src/dmd/builtin.d) | Allow CTFE of certain external functions (`core.math`, `std.math` and `core.bitop`) |
+
+### Specific language features
+
+**Attribute checks**
+
+| File | Purpose |
+|---------------------------------------------------------------------------|----------------------------------------|
+| [nogc.d](https://github.com/dlang/dmd/blob/master/src/dmd/nogc.d) | `@nogc` checks |
+| [safe.d](https://github.com/dlang/dmd/blob/master/src/dmd/safe.d) | `@safe` checks |
+| [canthrow.d](https://github.com/dlang/dmd/blob/master/src/dmd/canthrow.d) | `nothrow` checks |
+| [escape.d](https://github.com/dlang/dmd/blob/master/src/dmd/escape.d) | `scope` checks |
+| [access.d](https://github.com/dlang/dmd/blob/master/src/dmd/access.d) | `public` / `private` checks |
+| [ob.d](https://github.com/dlang/dmd/blob/master/src/dmd/ob.d) | Ownership / borrowing (`@live`) checks |
+
+**Inline Assembly**
+
+| File | Purpose |
+|-------------------------------------------------------------------------|-------------------------------------------|
+| [iasm.d](https://github.com/dlang/dmd/blob/master/src/dmd/iasm.d) | Inline assembly depending on the compiler |
+| [iasmdmd.d](https://github.com/dlang/dmd/blob/master/src/dmd/iasmdmd.d) | Inline assembly for DMD |
+| [iasmgcc.d](https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d) | Inline assembly for GDC |
+
+**Other**
+
+| File | Purpose |
+|-------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
+| [aliasthis.d](https://github.com/dlang/dmd/blob/master/src/dmd/aliasthis.d) | Resolve implicit conversions for `alias X this` |
+| [traits.d](https://github.com/dlang/dmd/blob/master/src/dmd/traits.d) | `__traits()` |
+| [lambdacomp.d](https://github.com/dlang/dmd/blob/master/src/dmd/lambdacomp.d) | `__traits(isSame, x => y, z => w)` |
+| [cond.d](https://github.com/dlang/dmd/blob/master/src/dmd/cond.d) | Evaluate `static if`, `version` `debug ` |
+| [staticcond.d](https://github.com/dlang/dmd/blob/master/src/dmd/staticcond.d) | Lazily evaluate static conditions for `static if`, `static assert` and template constraints |
+| [delegatize.d](https://github.com/dlang/dmd/blob/master/src/dmd/delegatize.d) | Converts expression to delegates for `lazy` parameters |
+| [eh.d](https://github.com/dlang/dmd/blob/master/src/dmd/eh.d) | Generate tables for exception handling |
+| [nspace.d](https://github.com/dlang/dmd/blob/master/src/dmd/nspace.d) | Namespace for `extern (C++, Module)` |
+| [intrange.d](https://github.com/dlang/dmd/blob/master/src/dmd/intrange.d) | [Value range propagation](https://digitalmars.com/articles/b62.html) |
+| [dimport.d](https://github.com/dlang/dmd/blob/master/src/dmd/dimport.d) | Renamed imports (`import aliasSymbol = pkg1.pkg2.symbol`) |
+| [arrayop.d](https://github.com/dlang/dmd/blob/master/src/dmd/arrayop.d) | Array operations (`a[] = b[] + c[]`) |
+| [typinf.d](https://github.com/dlang/dmd/blob/master/src/dmd/typinf.d) | Generate typeinfo for `typeid()` (as well as internals) |
+
+| File | Purpose |
+|-----------------------------------------------------------------------------|------------------------------------------------------------------------------------|
+| [chkformat.d](https://github.com/dlang/dmd/blob/master/src/dmd/chkformat.d) | Validate arguments with format specifiers for `printf` / `scanf` etc. |
+| [imphint.d](https://github.com/dlang/dmd/blob/master/src/dmd/imphint.d) | Give a suggestion to e.g. `import std.stdio` when `writeln` could not be resolved. |
+
+### Library files
+
+| File | Purpose |
+|-------------------------------------------------------------------------------|------------------------------------------------------|
+| [lib.d](https://github.com/dlang/dmd/blob/master/src/dmd/lib.d) | Abstract library class |
+| [libelf.d](https://github.com/dlang/dmd/blob/master/src/dmd/libelf.d) | Library in ELF format (Unix) |
+| [libmach.d](https://github.com/dlang/dmd/blob/master/src/dmd/libmach.d) | Library in Mach-O format (macOS) |
+| [libmscoff.d](https://github.com/dlang/dmd/blob/master/src/dmd/libmscoff.d) | Library in COFF format (32/64-bit Windows) |
+| [libomf.d](https://github.com/dlang/dmd/blob/master/src/dmd/libomf.d) | Library in OMF format (legacy 32-bit Windows) |
+| [scanelf.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanelf.d) | Extract symbol names from a library in ELF format |
+| [scanmach.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanmach.d) | Extract symbol names from a library in Mach-O format |
+| [scanmscoff.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanmscoff.d) | Extract symbol names from a library in COFF format |
+| [scanomf.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanomf.d) | Extract symbol names from a library in OMF format |
+
+### Code generation / back-end interfacing
+
+| File | Purpose |
+|---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
+| [dmsc.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmsc.d) | Configures and initializes the back-end |
+| [toobj.d](https://github.com/dlang/dmd/blob/master/src/dmd/toobj.d) | Convert an AST that went through all semantic phases into an object file |
+| [toir.d](https://github.com/dlang/dmd/blob/master/src/dmd/toir.d) | Convert Dsymbols intermediate representation |
+| [e2ir.d](https://github.com/dlang/dmd/blob/master/src/dmd/e2ir.d) | Convert Expressions to intermediate representation |
+| [s2ir.d](https://github.com/dlang/dmd/blob/master/src/dmd/s2ir.d) | Convert Statements to intermediate representation |
+| [stmtstate.d](https://github.com/dlang/dmd/blob/master/src/dmd/stmtstate.d) | Used to help transform statement AST into flow graph |
+| [toctype.d](https://github.com/dlang/dmd/blob/master/src/dmd/toctype.d) | Convert a D type to a type the back-end understands |
+| [tocsym.d](https://github.com/dlang/dmd/blob/master/src/dmd/tocsym.d) | Convert a D symbol to a symbol the linker understands (with mangled name) |
+| [argtypes_x86.d](https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_x86.d) | Convert a D type into simple (register) types for the 32-bit x86 ABI |
+| [argtypes_sysv_x64.d](https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_sysv_x64.d) | 'argtypes' for the x86_64 System V ABI |
+| [argtypes_aarch64.d](https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_aarch64.d) | 'argtypes' for the AArch64 ABI |
+| [glue.d](https://github.com/dlang/dmd/blob/master/src/dmd/glue.d) | Generate the object file for function declarations |
+| [gluelayer.d](https://github.com/dlang/dmd/blob/master/src/dmd/gluelayer.d) | Declarations for back-end functions that the front-end invokes |
+| [todt.d](https://github.com/dlang/dmd/blob/master/src/dmd/todt.d) | Convert initializers into structures that the back-end will add to the data segment |
+| [tocvdebug.d](https://github.com/dlang/dmd/blob/master/src/dmd/tovcdebug.d) | Generate debug info in the CV4 debug format. |
+| [objc.d](https://github.com/dlang/dmd/blob/master/src/dmd/objc.d) | Objective-C interfacing |
+| [objc_glue.d](https://github.com/dlang/dmd/blob/master/src/dmd/objc_glue.d) | Glue code for Objective-C interop. |
+
+**Name mangling**
+
+| File | Purpose |
+|-----------------------------------------------------------------------------------|------------------------------------------------------------------|
+| [cppmangle.d](https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d) | C++ name mangling |
+| [cppmanglewin.d](https://github.com/dlang/dmd/blob/master/src/dmd/cppmanglewin.d) | C++ name mangling for Windows |
+| [dmangle.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmangle.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) |
+
+### Linking
+
+| File | Purpose |
+|-------------------------------------------------------------------|-----------------------------------------|
+| [link.d](https://github.com/dlang/dmd/blob/master/src/dmd/link.d) | Invoke the linker as a separate process |
+
+### Special output
+
+| File | Purpose |
+|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
+| [doc.d](https://github.com/dlang/dmd/blob/master/src/dmd/doc.d) | [Documentation generation](https://dlang.org/spec/ddoc.html) |
+| [dmacro.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmacro.d) | DDoc macro processing |
+| [hdrgen.d](https://github.com/dlang/dmd/blob/master/src/dmd/hdrgen.d) | Convert an AST into D source code for `.di` header generation, as well as `-vcg-ast` and error messages |
+| [json.d](https://github.com/dlang/dmd/blob/master/src/dmd/json.d) | Describe the module in a `.json` file for the `-X` flag |
+| [dtoh.d](https://github.com/dlang/dmd/blob/master/src/dmd/dtoh.d) | C++ header generation from D source files |
+
+### Utility
+
+Note: many other utilities are in [dmd/root](https://github.com/dlang/dmd/tree/master/src/dmd/root).
+
+| File | Purpose |
+|-----------------------------------------------------------------------------|---------------------------------------------------|
+| [env.d](https://github.com/dlang/dmd/blob/master/src/dmd/env.d) | Modify environment variables |
+| [console.d](https://github.com/dlang/dmd/blob/master/src/dmd/console.d) | Print error messages in color |
+| [utf.d](https://github.com/dlang/dmd/blob/master/src/dmd/utf.d) | Encoding/decoding Unicode text |
+| [filecache.d](https://github.com/dlang/dmd/blob/master/src/dmd/filecache.d) | Keep file contents in memory |
+| [utils.d](https://github.com/dlang/dmd/blob/master/src/dmd/utils.d) | Utility functions related to files and file paths |
+| [complex.d](https://github.com/dlang/dmd/blob/master/src/dmd/complex.d) | A complex number type |
+
+| File | Purpose |
+|---------------------------------------------------------------------------------|---------------------------------------------------------------|
+| [asttypename.d](https://github.com/dlang/dmd/blob/master/src/dmd/asttypename.d) | Print the internal name of an AST node (for debugging only) |
+| [printast.d](https://github.com/dlang/dmd/blob/master/src/dmd/printast.d) | Print the AST data structure |
+| [foreachvar.d](https://github.com/dlang/dmd/blob/master/src/dmd/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression |
diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION
new file mode 100644
index 00000000000..64ce79a7c45
--- /dev/null
+++ b/gcc/d/dmd/VERSION
@@ -0,0 +1 @@
+v2.097.2
diff --git a/gcc/d/dmd/access.c b/gcc/d/dmd/access.c
deleted file mode 100644
index 11b26c5dc5c..00000000000
--- a/gcc/d/dmd/access.c
+++ /dev/null
@@ -1,560 +0,0 @@
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/access.c
- */
-
-#include "root/dsystem.h"
-#include "root/root.h"
-#include "root/rmem.h"
-
-#include "errors.h"
-#include "enum.h"
-#include "aggregate.h"
-#include "init.h"
-#include "attrib.h"
-#include "scope.h"
-#include "id.h"
-#include "mtype.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "expression.h"
-#include "module.h"
-#include "template.h"
-
-/* Code to do access checks
- */
-
-bool hasPackageAccess(Scope *sc, Dsymbol *s);
-bool hasPackageAccess(Module *mod, Dsymbol *s);
-bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember);
-bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd);
-static Dsymbol *mostVisibleOverload(Dsymbol *s);
-
-/****************************************
- * Return Prot access for Dsymbol smember in this declaration.
- */
-Prot getAccess(AggregateDeclaration *ad, Dsymbol *smember)
-{
- Prot access_ret = Prot(Prot::none);
-
- assert(ad->isStructDeclaration() || ad->isClassDeclaration());
- if (smember->toParent() == ad)
- {
- access_ret = smember->prot();
- }
- else if (smember->isDeclaration()->isStatic())
- {
- access_ret = smember->prot();
- }
- if (ClassDeclaration *cd = ad->isClassDeclaration())
- {
- for (size_t i = 0; i < cd->baseclasses->length; i++)
- {
- BaseClass *b = (*cd->baseclasses)[i];
-
- Prot access = getAccess(b->sym, smember);
- switch (access.kind)
- {
- case Prot::none:
- break;
-
- case Prot::private_:
- access_ret = Prot(Prot::none); // private members of base class not accessible
- break;
-
- case Prot::package_:
- case Prot::protected_:
- case Prot::public_:
- case Prot::export_:
- // If access is to be tightened
- if (Prot::public_ < access.kind)
- access = Prot(Prot::public_);
-
- // Pick path with loosest access
- if (access_ret.isMoreRestrictiveThan(access))
- access_ret = access;
- break;
-
- default:
- assert(0);
- }
- }
- }
-
- return access_ret;
-}
-
-/********************************************************
- * Helper function for checkAccess()
- * Returns:
- * false is not accessible
- * true is accessible
- */
-static bool isAccessible(
- Dsymbol *smember,
- Dsymbol *sfunc,
- AggregateDeclaration *dthis,
- AggregateDeclaration *cdscope)
-{
- assert(dthis);
-
- if (hasPrivateAccess(dthis, sfunc) ||
- isFriendOf(dthis, cdscope))
- {
- if (smember->toParent() == dthis)
- return true;
-
- if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
- {
- for (size_t i = 0; i < cdthis->baseclasses->length; i++)
- {
- BaseClass *b = (*cdthis->baseclasses)[i];
- Prot access = getAccess(b->sym, smember);
- if (access.kind >= Prot::protected_ ||
- isAccessible(smember, sfunc, b->sym, cdscope))
- {
- return true;
- }
- }
- }
- }
- else
- {
- if (smember->toParent() != dthis)
- {
- if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
- {
- for (size_t i = 0; i < cdthis->baseclasses->length; i++)
- {
- BaseClass *b = (*cdthis->baseclasses)[i];
- if (isAccessible(smember, sfunc, b->sym, cdscope))
- return true;
- }
- }
- }
- }
- return false;
-}
-
-/*******************************
- * Do access check for member of this class, this class being the
- * type of the 'this' pointer used to access smember.
- * Returns true if the member is not accessible.
- */
-bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember)
-{
- FuncDeclaration *f = sc->func;
- AggregateDeclaration *cdscope = sc->getStructClassScope();
-
- Dsymbol *smemberparent = smember->toParent();
- if (!smemberparent || !smemberparent->isAggregateDeclaration())
- {
- return false; // then it is accessible
- }
-
- // BUG: should enable this check
- //assert(smember->parent->isBaseOf(this, NULL));
-
- bool result;
- Prot access;
- if (smemberparent == ad)
- {
- access = smember->prot();
- result = access.kind >= Prot::public_ ||
- hasPrivateAccess(ad, f) ||
- isFriendOf(ad, cdscope) ||
- (access.kind == Prot::package_ && hasPackageAccess(sc, smember)) ||
- ad->getAccessModule() == sc->_module;
- }
- else if ((access = getAccess(ad, smember)).kind >= Prot::public_)
- {
- result = true;
- }
- else if (access.kind == Prot::package_ && hasPackageAccess(sc, ad))
- {
- result = true;
- }
- else
- {
- result = isAccessible(smember, f, ad, cdscope);
- }
- if (!result)
- {
- ad->error(loc, "member %s is not accessible", smember->toChars());
- //printf("smember = %s %s, prot = %d, semanticRun = %d\n",
- // smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun);
- return true;
- }
- return false;
-}
-
-/****************************************
- * Determine if this is the same or friend of cd.
- */
-bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd)
-{
- if (ad == cd)
- return true;
-
- // Friends if both are in the same module
- //if (toParent() == cd->toParent())
- if (cd && ad->getAccessModule() == cd->getAccessModule())
- {
- return true;
- }
-
- return false;
-}
-
-/****************************************
- * Determine if scope sc has package level access to s.
- */
-bool hasPackageAccess(Scope *sc, Dsymbol *s)
-{
- return hasPackageAccess(sc->_module, s);
-}
-
-bool hasPackageAccess(Module *mod, Dsymbol *s)
-{
- Package *pkg = NULL;
-
- if (s->prot().pkg)
- pkg = s->prot().pkg;
- else
- {
- // no explicit package for protection, inferring most qualified one
- for (; s; s = s->parent)
- {
- if (Module *m = s->isModule())
- {
- DsymbolTable *dst = Package::resolve(m->md ? m->md->packages : NULL, NULL, NULL);
- assert(dst);
- Dsymbol *s2 = dst->lookup(m->ident);
- assert(s2);
- Package *p = s2->isPackage();
- if (p && p->isPackageMod())
- {
- pkg = p;
- break;
- }
- }
- else if ((pkg = s->isPackage()) != NULL)
- break;
- }
- }
-
- if (pkg)
- {
- if (pkg == mod->parent)
- {
- return true;
- }
- if (pkg->isPackageMod() == mod)
- {
- return true;
- }
- Dsymbol* ancestor = mod->parent;
- for (; ancestor; ancestor = ancestor->parent)
- {
- if (ancestor == pkg)
- {
- return true;
- }
- }
- }
-
- return false;
-}
-
-/****************************************
- * Determine if scope sc has protected level access to cd.
- */
-bool hasProtectedAccess(Scope *sc, Dsymbol *s)
-{
- if (ClassDeclaration *cd = s->isClassMember()) // also includes interfaces
- {
- for (Scope *scx = sc; scx; scx = scx->enclosing)
- {
- if (!scx->scopesym)
- continue;
- ClassDeclaration *cd2 = scx->scopesym->isClassDeclaration();
- if (cd2 && cd->isBaseOf(cd2, NULL))
- return true;
- }
- }
- return sc->_module == s->getAccessModule();
-}
-
-/**********************************
- * Determine if smember has access to private members of this declaration.
- */
-bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember)
-{
- if (smember)
- {
- AggregateDeclaration *cd = NULL;
- Dsymbol *smemberparent = smember->toParent();
- if (smemberparent)
- cd = smemberparent->isAggregateDeclaration();
-
- if (ad == cd) // smember is a member of this class
- {
- return true; // so we get private access
- }
-
- // If both are members of the same module, grant access
- while (1)
- {
- Dsymbol *sp = smember->toParent();
- if (sp->isFuncDeclaration() && smember->isFuncDeclaration())
- smember = sp;
- else
- break;
- }
- if (!cd && ad->toParent() == smember->toParent())
- {
- return true;
- }
- if (!cd && ad->getAccessModule() == smember->getAccessModule())
- {
- return true;
- }
- }
- return false;
-}
-
-/****************************************
- * Check access to d for expression e.d
- * Returns true if the declaration is not accessible.
- */
-bool checkAccess(Loc loc, Scope *sc, Expression *e, Declaration *d)
-{
- if (sc->flags & SCOPEnoaccesscheck)
- return false;
-
- if (d->isUnitTestDeclaration())
- {
- // Unittests are always accessible.
- return false;
- }
- if (!e)
- return false;
-
- if (e->type->ty == Tclass)
- {
- // Do access check
- ClassDeclaration *cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym);
- if (e->op == TOKsuper)
- {
- ClassDeclaration *cd2 = sc->func->toParent()->isClassDeclaration();
- if (cd2)
- cd = cd2;
- }
- return checkAccess(cd, loc, sc, d);
- }
- else if (e->type->ty == Tstruct)
- {
- // Do access check
- StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym);
- return checkAccess(cd, loc, sc, d);
- }
- return false;
-}
-
-/****************************************
- * Check access to package/module `p` from scope `sc`.
- *
- * Params:
- * loc = source location for issued error message
- * sc = scope from which to access to a fully qualified package name
- * p = the package/module to check access for
- * Returns: true if the package is not accessible.
- *
- * Because a global symbol table tree is used for imported packages/modules,
- * access to them needs to be checked based on the imports in the scope chain
- * (see Bugzilla 313).
- *
- */
-bool checkAccess(Scope *sc, Package *p)
-{
- if (sc->_module == p)
- return false;
- for (; sc; sc = sc->enclosing)
- {
- if (sc->scopesym && sc->scopesym->isPackageAccessible(p, Prot(Prot::private_)))
- return false;
- }
-
- return true;
-}
-
-/**
- * Check whether symbols `s` is visible in `mod`.
- *
- * Params:
- * mod = lookup origin
- * s = symbol to check for visibility
- * Returns: true if s is visible in mod
- */
-bool symbolIsVisible(Module *mod, Dsymbol *s)
-{
- // should sort overloads by ascending protection instead of iterating here
- s = mostVisibleOverload(s);
-
- switch (s->prot().kind)
- {
- case Prot::undefined:
- return true;
- case Prot::none:
- return false; // no access
- case Prot::private_:
- return s->getAccessModule() == mod;
- case Prot::package_:
- return s->getAccessModule() == mod || hasPackageAccess(mod, s);
- case Prot::protected_:
- return s->getAccessModule() == mod;
- case Prot::public_:
- case Prot::export_:
- return true;
- default:
- assert(0);
- }
- return false;
-}
-
-/**
- * Same as above, but determines the lookup module from symbols `origin`.
- */
-bool symbolIsVisible(Dsymbol *origin, Dsymbol *s)
-{
- return symbolIsVisible(origin->getAccessModule(), s);
-}
-
-/**
- * Same as above but also checks for protected symbols visible from scope `sc`.
- * Used for qualified name lookup.
- *
- * Params:
- * sc = lookup scope
- * s = symbol to check for visibility
- * Returns: true if s is visible by origin
- */
-bool symbolIsVisible(Scope *sc, Dsymbol *s)
-{
- s = mostVisibleOverload(s);
-
- switch (s->prot().kind)
- {
- case Prot::undefined:
- return true;
- case Prot::none:
- return false; // no access
- case Prot::private_:
- return sc->_module == s->getAccessModule();
- case Prot::package_:
- return sc->_module == s->getAccessModule() || hasPackageAccess(sc->_module, s);
- case Prot::protected_:
- return hasProtectedAccess(sc, s);
- case Prot::public_:
- case Prot::export_:
- return true;
- default:
- assert(0);
- }
- return false;
-}
-
-/**
- * Use the most visible overload to check visibility. Later perform an access
- * check on the resolved overload. This function is similar to overloadApply,
- * but doesn't recurse nor resolve aliases because protection/visibility is an
- * attribute of the alias not the aliasee.
- */
-static Dsymbol *mostVisibleOverload(Dsymbol *s)
-{
- if (!s->isOverloadable())
- return s;
-
- Dsymbol *next = NULL;
- Dsymbol *fstart = s;
- Dsymbol *mostVisible = s;
- for (; s; s = next)
- {
- // void func() {}
- // private void func(int) {}
- if (FuncDeclaration *fd = s->isFuncDeclaration())
- next = fd->overnext;
- // template temp(T) {}
- // private template temp(T:int) {}
- else if (TemplateDeclaration *td = s->isTemplateDeclaration())
- next = td->overnext;
- // alias common = mod1.func1;
- // alias common = mod2.func2;
- else if (FuncAliasDeclaration *fa = s->isFuncAliasDeclaration())
- next = fa->overnext;
- // alias common = mod1.templ1;
- // alias common = mod2.templ2;
- else if (OverDeclaration *od = s->isOverDeclaration())
- next = od->overnext;
- // alias name = sym;
- // private void name(int) {}
- else if (AliasDeclaration *ad = s->isAliasDeclaration())
- {
- if (!ad->isOverloadable())
- {
- //printf("Non overloadable Aliasee in overload list\n");
- assert(0);
- }
- // Yet unresolved aliases store overloads in overnext.
- if (ad->semanticRun < PASSsemanticdone)
- next = ad->overnext;
- else
- {
- /* This is a bit messy due to the complicated implementation of
- * alias. Aliases aren't overloadable themselves, but if their
- * Aliasee is overloadable they can be converted to an overloadable
- * alias.
- *
- * This is done by replacing the Aliasee w/ FuncAliasDeclaration
- * (for functions) or OverDeclaration (for templates) which are
- * simply overloadable aliases w/ weird names.
- *
- * Usually aliases should not be resolved for visibility checking
- * b/c public aliases to private symbols are public. But for the
- * overloadable alias situation, the Alias (_ad_) has been moved
- * into it's own Aliasee, leaving a shell that we peel away here.
- */
- Dsymbol *aliasee = ad->toAlias();
- if (aliasee->isFuncAliasDeclaration() || aliasee->isOverDeclaration())
- next = aliasee;
- else
- {
- /* A simple alias can be at the end of a function or template overload chain.
- * It can't have further overloads b/c it would have been
- * converted to an overloadable alias.
- */
- if (ad->overnext)
- {
- //printf("Unresolved overload of alias\n");
- assert(0);
- }
- break;
- }
- }
-
- // handled by overloadApply for unknown reason
- assert(next != ad); // should not alias itself
- assert(next != fstart); // should not alias the overload list itself
- }
- else
- break;
-
- if (next && mostVisible->prot().isMoreRestrictiveThan(next->prot()))
- mostVisible = next;
- }
- return mostVisible;
-}
diff --git a/gcc/d/dmd/access.d b/gcc/d/dmd/access.d
new file mode 100644
index 00000000000..944c9d3e124
--- /dev/null
+++ b/gcc/d/dmd/access.d
@@ -0,0 +1,410 @@
+/**
+ * Enforce visibility contrains such as `public` and `private`.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/attribute.html#visibility_attributes, Visibility Attributes)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/access.d, _access.d)
+ * Documentation: https://dlang.org/phobos/dmd_access.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/access.d
+ */
+
+module dmd.access;
+
+import dmd.aggregate;
+import dmd.astenums;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.mtype;
+import dmd.tokens;
+
+private enum LOG = false;
+
+
+/*******************************
+ * Do access check for member of this class, this class being the
+ * type of the 'this' pointer used to access smember.
+ * Returns true if the member is not accessible.
+ */
+bool checkAccess(AggregateDeclaration ad, Loc loc, Scope* sc, Dsymbol smember)
+{
+ static if (LOG)
+ {
+ printf("AggregateDeclaration::checkAccess() for %s.%s in function %s() in scope %s\n", ad.toChars(), smember.toChars(), f ? f.toChars() : null, cdscope ? cdscope.toChars() : null);
+ }
+
+ const p = smember.toParent();
+ if (p && p.isTemplateInstance())
+ {
+ return false; // for backward compatibility
+ }
+
+ if (!symbolIsVisible(sc, smember))
+ {
+ // when in @safe code or with -preview=dip1000
+ if (sc.flags & SCOPE.onlysafeaccess)
+ {
+ // if there is a func. ask for it's opinion of safety, and if it considers the access @safe accept it.
+ if (sc.func && !sc.func.setUnsafe())
+ return false;
+ }
+
+ ad.error(loc, "%s `%s` is not accessible%s", smember.kind(), smember.toChars(), (sc.flags & SCOPE.onlysafeaccess) ? " from `@safe` code".ptr : "".ptr);
+ //printf("smember = %s %s, vis = %d, semanticRun = %d\n",
+ // smember.kind(), smember.toPrettyChars(), smember.visible() smember.semanticRun);
+ return true;
+ }
+ return false;
+}
+
+/****************************************
+ * Determine if scope sc has package level access to s.
+ */
+private bool hasPackageAccess(Scope* sc, Dsymbol s)
+{
+ return hasPackageAccess(sc._module, s);
+}
+
+private bool hasPackageAccess(Module mod, Dsymbol s)
+{
+ static if (LOG)
+ {
+ printf("hasPackageAccess(s = '%s', mod = '%s', s.visibility.pkg = '%s')\n", s.toChars(), mod.toChars(), s.visible().pkg ? s.visible().pkg.toChars() : "NULL");
+ }
+ Package pkg = null;
+ if (s.visible().pkg)
+ pkg = s.visible().pkg;
+ else
+ {
+ // no explicit package for visibility, inferring most qualified one
+ for (; s; s = s.parent)
+ {
+ if (auto m = s.isModule())
+ {
+ DsymbolTable dst = Package.resolve(m.md ? m.md.packages : null, null, null);
+ assert(dst);
+ Dsymbol s2 = dst.lookup(m.ident);
+ assert(s2);
+ Package p = s2.isPackage();
+ if (p && p.isPackageMod())
+ {
+ pkg = p;
+ break;
+ }
+ }
+ else if ((pkg = s.isPackage()) !is null)
+ break;
+ }
+ }
+ static if (LOG)
+ {
+ if (pkg)
+ printf("\tsymbol access binds to package '%s'\n", pkg.toChars());
+ }
+ if (pkg)
+ {
+ if (pkg == mod.parent)
+ {
+ static if (LOG)
+ {
+ printf("\tsc is in permitted package for s\n");
+ }
+ return true;
+ }
+ if (pkg.isPackageMod() == mod)
+ {
+ static if (LOG)
+ {
+ printf("\ts is in same package.d module as sc\n");
+ }
+ return true;
+ }
+ Dsymbol ancestor = mod.parent;
+ for (; ancestor; ancestor = ancestor.parent)
+ {
+ if (ancestor == pkg)
+ {
+ static if (LOG)
+ {
+ printf("\tsc is in permitted ancestor package for s\n");
+ }
+ return true;
+ }
+ }
+ }
+ static if (LOG)
+ {
+ printf("\tno package access\n");
+ }
+ return false;
+}
+
+/****************************************
+ * Determine if scope sc has protected level access to cd.
+ */
+private bool hasProtectedAccess(Scope *sc, Dsymbol s)
+{
+ if (auto cd = s.isClassMember()) // also includes interfaces
+ {
+ for (auto scx = sc; scx; scx = scx.enclosing)
+ {
+ if (!scx.scopesym)
+ continue;
+ auto cd2 = scx.scopesym.isClassDeclaration();
+ if (cd2 && cd.isBaseOf(cd2, null))
+ return true;
+ }
+ }
+ return sc._module == s.getAccessModule();
+}
+
+/****************************************
+ * Check access to d for expression e.d
+ * Returns true if the declaration is not accessible.
+ */
+bool checkAccess(Loc loc, Scope* sc, Expression e, Dsymbol d)
+{
+ if (sc.flags & SCOPE.noaccesscheck)
+ return false;
+ static if (LOG)
+ {
+ if (e)
+ {
+ printf("checkAccess(%s . %s)\n", e.toChars(), d.toChars());
+ printf("\te.type = %s\n", e.type.toChars());
+ }
+ else
+ {
+ printf("checkAccess(%s)\n", d.toPrettyChars());
+ }
+ }
+ if (d.isUnitTestDeclaration())
+ {
+ // Unittests are always accessible.
+ return false;
+ }
+
+ if (!e)
+ return false;
+
+ if (auto tc = e.type.isTypeClass())
+ {
+ // Do access check
+ ClassDeclaration cd = tc.sym;
+ if (e.op == TOK.super_)
+ {
+ if (ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration())
+ cd = cd2;
+ }
+ return checkAccess(cd, loc, sc, d);
+ }
+ else if (auto ts = e.type.isTypeStruct())
+ {
+ // Do access check
+ StructDeclaration cd = ts.sym;
+ return checkAccess(cd, loc, sc, d);
+ }
+ return false;
+}
+
+/****************************************
+ * Check access to package/module `p` from scope `sc`.
+ *
+ * Params:
+ * sc = scope from which to access to a fully qualified package name
+ * p = the package/module to check access for
+ * Returns: true if the package is not accessible.
+ *
+ * Because a global symbol table tree is used for imported packages/modules,
+ * access to them needs to be checked based on the imports in the scope chain
+ * (see https://issues.dlang.org/show_bug.cgi?id=313).
+ *
+ */
+bool checkAccess(Scope* sc, Package p)
+{
+ if (sc._module == p)
+ return false;
+ for (; sc; sc = sc.enclosing)
+ {
+ if (sc.scopesym && sc.scopesym.isPackageAccessible(p, Visibility(Visibility.Kind.private_)))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Check whether symbols `s` is visible in `mod`.
+ *
+ * Params:
+ * mod = lookup origin
+ * s = symbol to check for visibility
+ * Returns: true if s is visible in mod
+ */
+bool symbolIsVisible(Module mod, Dsymbol s)
+{
+ // should sort overloads by ascending visibility instead of iterating here
+ s = mostVisibleOverload(s);
+ final switch (s.visible().kind)
+ {
+ case Visibility.Kind.undefined: return true;
+ case Visibility.Kind.none: return false; // no access
+ case Visibility.Kind.private_: return s.getAccessModule() == mod;
+ case Visibility.Kind.package_: return s.getAccessModule() == mod || hasPackageAccess(mod, s);
+ case Visibility.Kind.protected_: return s.getAccessModule() == mod;
+ case Visibility.Kind.public_, Visibility.Kind.export_: return true;
+ }
+}
+
+/**
+ * Same as above, but determines the lookup module from symbols `origin`.
+ */
+bool symbolIsVisible(Dsymbol origin, Dsymbol s)
+{
+ return symbolIsVisible(origin.getAccessModule(), s);
+}
+
+/**
+ * Same as above but also checks for protected symbols visible from scope `sc`.
+ * Used for qualified name lookup.
+ *
+ * Params:
+ * sc = lookup scope
+ * s = symbol to check for visibility
+ * Returns: true if s is visible by origin
+ */
+bool symbolIsVisible(Scope *sc, Dsymbol s)
+{
+ s = mostVisibleOverload(s);
+ return checkSymbolAccess(sc, s);
+}
+
+/**
+ * Check if a symbol is visible from a given scope without taking
+ * into account the most visible overload.
+ *
+ * Params:
+ * sc = lookup scope
+ * s = symbol to check for visibility
+ * Returns: true if s is visible by origin
+ */
+bool checkSymbolAccess(Scope *sc, Dsymbol s)
+{
+ final switch (s.visible().kind)
+ {
+ case Visibility.Kind.undefined: return true;
+ case Visibility.Kind.none: return false; // no access
+ case Visibility.Kind.private_: return sc._module == s.getAccessModule();
+ case Visibility.Kind.package_: return sc._module == s.getAccessModule() || hasPackageAccess(sc._module, s);
+ case Visibility.Kind.protected_: return hasProtectedAccess(sc, s);
+ case Visibility.Kind.public_, Visibility.Kind.export_: return true;
+ }
+}
+
+/**
+ * Use the most visible overload to check visibility. Later perform an access
+ * check on the resolved overload. This function is similar to overloadApply,
+ * but doesn't recurse nor resolve aliases because visibility is an
+ * attribute of the alias not the aliasee.
+ */
+public Dsymbol mostVisibleOverload(Dsymbol s, Module mod = null)
+{
+ if (!s.isOverloadable())
+ return s;
+
+ Dsymbol next, fstart = s, mostVisible = s;
+ for (; s; s = next)
+ {
+ // void func() {}
+ // private void func(int) {}
+ if (auto fd = s.isFuncDeclaration())
+ next = fd.overnext;
+ // template temp(T) {}
+ // private template temp(T:int) {}
+ else if (auto td = s.isTemplateDeclaration())
+ next = td.overnext;
+ // alias common = mod1.func1;
+ // alias common = mod2.func2;
+ else if (auto fa = s.isFuncAliasDeclaration())
+ next = fa.overnext;
+ // alias common = mod1.templ1;
+ // alias common = mod2.templ2;
+ else if (auto od = s.isOverDeclaration())
+ next = od.overnext;
+ // alias name = sym;
+ // private void name(int) {}
+ else if (auto ad = s.isAliasDeclaration())
+ {
+ assert(ad.isOverloadable || ad.type && ad.type.ty == Terror,
+ "Non overloadable Aliasee in overload list");
+ // Yet unresolved aliases store overloads in overnext.
+ if (ad.semanticRun < PASS.semanticdone)
+ next = ad.overnext;
+ else
+ {
+ /* This is a bit messy due to the complicated implementation of
+ * alias. Aliases aren't overloadable themselves, but if their
+ * Aliasee is overloadable they can be converted to an overloadable
+ * alias.
+ *
+ * This is done by replacing the Aliasee w/ FuncAliasDeclaration
+ * (for functions) or OverDeclaration (for templates) which are
+ * simply overloadable aliases w/ weird names.
+ *
+ * Usually aliases should not be resolved for visibility checking
+ * b/c public aliases to private symbols are public. But for the
+ * overloadable alias situation, the Alias (_ad_) has been moved
+ * into its own Aliasee, leaving a shell that we peel away here.
+ */
+ auto aliasee = ad.toAlias();
+ if (aliasee.isFuncAliasDeclaration || aliasee.isOverDeclaration)
+ next = aliasee;
+ else
+ {
+ /* A simple alias can be at the end of a function or template overload chain.
+ * It can't have further overloads b/c it would have been
+ * converted to an overloadable alias.
+ */
+ assert(ad.overnext is null, "Unresolved overload of alias");
+ break;
+ }
+ }
+ // handled by dmd.func.overloadApply for unknown reason
+ assert(next !is ad); // should not alias itself
+ assert(next !is fstart); // should not alias the overload list itself
+ }
+ else
+ break;
+
+ /**
+ * Return the "effective" visibility attribute of a symbol when accessed in a module.
+ * The effective visibility attribute is the same as the regular visibility attribute,
+ * except package() is "private" if the module is outside the package;
+ * otherwise, "public".
+ */
+ static Visibility visibilitySeenFromModule(Dsymbol d, Module mod = null)
+ {
+ Visibility vis = d.visible();
+ if (mod && vis.kind == Visibility.Kind.package_)
+ {
+ return hasPackageAccess(mod, d) ? Visibility(Visibility.Kind.public_) : Visibility(Visibility.Kind.private_);
+ }
+ return vis;
+ }
+
+ if (next &&
+ visibilitySeenFromModule(mostVisible, mod) < visibilitySeenFromModule(next, mod))
+ mostVisible = next;
+ }
+ return mostVisible;
+}
diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d
new file mode 100644
index 00000000000..cff4b9feb45
--- /dev/null
+++ b/gcc/d/dmd/aggregate.d
@@ -0,0 +1,769 @@
+/**
+ * Defines a `Dsymbol` representing an aggregate, which is a `struct`, `union` or `class`.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions),
+ * $(LINK2 https://dlang.org/spec/class.html, Class).
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d, _aggregate.d)
+ * Documentation: https://dlang.org/phobos/dmd_aggregate.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aggregate.d
+ */
+
+module dmd.aggregate;
+
+import core.stdc.stdio;
+import core.checkedint;
+
+import dmd.aliasthis;
+import dmd.apply;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.tokens;
+import dmd.typesem : defaultInit;
+import dmd.visitor;
+
+/**
+ * The ClassKind enum is used in AggregateDeclaration AST nodes to
+ * specify the linkage type of the struct/class/interface or if it
+ * is an anonymous class. If the class is anonymous it is also
+ * considered to be a D class.
+ */
+enum ClassKind : ubyte
+{
+ /// the aggregate is a d(efault) class
+ d,
+ /// the aggregate is a C++ struct/class/interface
+ cpp,
+ /// the aggregate is an Objective-C class/interface
+ objc,
+ /// the aggregate is a C struct
+ c,
+}
+
+/**
+ * If an aggregate has a pargma(mangle, ...) this holds the information
+ * to mangle.
+ */
+struct MangleOverride
+{
+ Dsymbol agg; // The symbol to copy template parameters from (if any)
+ Identifier id; // the name to override the aggregate's with, defaults to agg.ident
+}
+
+/***********************************************************
+ * Abstract aggregate as a common ancestor for Class- and StructDeclaration.
+ */
+extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
+{
+ Type type; ///
+ StorageClass storage_class; ///
+ uint structsize; /// size of struct
+ uint alignsize; /// size of struct for alignment purposes
+ VarDeclarations fields; /// VarDeclaration fields
+ Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol
+
+ /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface
+ ClassKind classKind;
+ /// Specify whether to mangle the aggregate as a `class` or a `struct`
+ /// This information is used by the MSVC mangler
+ /// Only valid for class and struct. TODO: Merge with ClassKind ?
+ CPPMANGLE cppmangle;
+
+ /// overridden symbol with pragma(mangle, "...") if not null
+ MangleOverride* mangleOverride;
+
+ /**
+ * !=null if is nested
+ * pointing to the dsymbol that directly enclosing it.
+ * 1. The function that enclosing it (nested struct and class)
+ * 2. The class that enclosing it (nested class only)
+ * 3. If enclosing aggregate is template, its enclosing dsymbol.
+ *
+ * See AggregateDeclaraton::makeNested for the details.
+ */
+ Dsymbol enclosing;
+
+ VarDeclaration vthis; /// 'this' parameter if this aggregate is nested
+ VarDeclaration vthis2; /// 'this' parameter if this aggregate is a template and is nested
+
+ // Special member functions
+ FuncDeclarations invs; /// Array of invariants
+ FuncDeclaration inv; /// Merged invariant calling all members of invs
+
+ /// CtorDeclaration or TemplateDeclaration
+ Dsymbol ctor;
+
+ /// default constructor - should have no arguments, because
+ /// it would be stored in TypeInfo_Class.defaultConstructor
+ CtorDeclaration defaultCtor;
+
+ AliasThis aliasthis; /// forward unresolved lookups to aliasthis
+
+ DtorDeclarations dtors; /// Array of destructors
+ DtorDeclaration dtor; /// aggregate destructor calling dtors and member constructors
+ DtorDeclaration primaryDtor;/// non-deleting C++ destructor, same as dtor for D
+ DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI)
+ FuncDeclaration fieldDtor; /// aggregate destructor for just the fields
+
+ Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this)
+
+ ///
+ Visibility visibility;
+ bool noDefaultCtor; /// no default construction
+ bool disableNew; /// disallow allocations using `new`
+ Sizeok sizeok = Sizeok.none; /// set when structsize contains valid data
+
+ final extern (D) this(const ref Loc loc, Identifier id)
+ {
+ super(loc, id);
+ visibility = Visibility(Visibility.Kind.public_);
+ }
+
+ /***************************************
+ * Create a new scope from sc.
+ * semantic, semantic2 and semantic3 will use this for aggregate members.
+ */
+ Scope* newScope(Scope* sc)
+ {
+ auto sc2 = sc.push(this);
+ sc2.stc &= STC.flowThruAggregate;
+ sc2.parent = this;
+ sc2.inunion = isUnionDeclaration();
+ sc2.visibility = Visibility(Visibility.Kind.public_);
+ sc2.explicitVisibility = 0;
+ sc2.aligndecl = null;
+ sc2.userAttribDecl = null;
+ sc2.namespace = null;
+ return sc2;
+ }
+
+ override final void setScope(Scope* sc)
+ {
+ // Might need a scope to resolve forward references. The check for
+ // semanticRun prevents unnecessary setting of _scope during deferred
+ // setScope phases for aggregates which already finished semantic().
+ // See https://issues.dlang.org/show_bug.cgi?id=16607
+ if (semanticRun < PASS.semanticdone)
+ ScopeDsymbol.setScope(sc);
+ }
+
+ /***************************************
+ * Returns:
+ * The total number of fields minus the number of hidden fields.
+ */
+ final size_t nonHiddenFields()
+ {
+ return fields.dim - isNested() - (vthis2 !is null);
+ }
+
+ /***************************************
+ * Collect all instance fields, then determine instance size.
+ * Returns:
+ * false if failed to determine the size.
+ */
+ final bool determineSize(Loc loc)
+ {
+ //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
+
+ // The previous instance size finalizing had:
+ if (type.ty == Terror)
+ return false; // failed already
+ if (sizeok == Sizeok.done)
+ return true; // succeeded
+
+ if (!members)
+ {
+ error(loc, "unknown size");
+ return false;
+ }
+
+ if (_scope)
+ dsymbolSemantic(this, null);
+
+ // Determine the instance size of base class first.
+ if (auto cd = isClassDeclaration())
+ {
+ cd = cd.baseClass;
+ if (cd && !cd.determineSize(loc))
+ goto Lfail;
+ }
+
+ // Determine instance fields when sizeok == Sizeok.none
+ if (!this.determineFields())
+ goto Lfail;
+ if (sizeok != Sizeok.done)
+ finalizeSize();
+
+ // this aggregate type has:
+ if (type.ty == Terror)
+ return false; // marked as invalid during the finalizing.
+ if (sizeok == Sizeok.done)
+ return true; // succeeded to calculate instance size.
+
+ Lfail:
+ // There's unresolvable forward reference.
+ if (type != Type.terror)
+ error(loc, "no size because of forward reference");
+ // Don't cache errors from speculative semantic, might be resolvable later.
+ // https://issues.dlang.org/show_bug.cgi?id=16574
+ if (!global.gag)
+ {
+ type = Type.terror;
+ errors = true;
+ }
+ return false;
+ }
+
+ abstract void finalizeSize();
+
+ override final d_uns64 size(const ref Loc loc)
+ {
+ //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
+ bool ok = determineSize(loc);
+ //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
+ return ok ? structsize : SIZE_INVALID;
+ }
+
+ /***************************************
+ * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
+ * field initializers have unique memory space on instance.
+ * Returns:
+ * true if any errors happen.
+ */
+ extern (D) final bool checkOverlappedFields()
+ {
+ //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
+ assert(sizeok == Sizeok.done);
+ size_t nfields = fields.dim;
+ if (isNested())
+ {
+ auto cd = isClassDeclaration();
+ if (!cd || !cd.baseClass || !cd.baseClass.isNested())
+ nfields--;
+ if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
+ nfields--;
+ }
+ bool errors = false;
+
+ // Fill in missing any elements with default initializers
+ foreach (i; 0 .. nfields)
+ {
+ auto vd = fields[i];
+ if (vd.errors)
+ {
+ errors = true;
+ continue;
+ }
+
+ const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
+
+ // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
+ foreach (j; 0 .. nfields)
+ {
+ if (i == j)
+ continue;
+ auto v2 = fields[j];
+ if (v2.errors)
+ {
+ errors = true;
+ continue;
+ }
+ if (!vd.isOverlappedWith(v2))
+ continue;
+
+ // vd and v2 are overlapping.
+ vd.overlapped = true;
+ v2.overlapped = true;
+
+ if (!MODimplicitConv(vd.type.mod, v2.type.mod))
+ v2.overlapUnsafe = true;
+ if (!MODimplicitConv(v2.type.mod, vd.type.mod))
+ vd.overlapUnsafe = true;
+
+ if (i > j)
+ continue;
+
+ if (!v2._init)
+ continue;
+
+ if (v2._init.isVoidInitializer())
+ continue;
+
+ if (vd._init && !vdIsVoidInit && v2._init)
+ {
+ .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
+ errors = true;
+ }
+ else if (v2._init && i < j)
+ {
+ .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
+ v2.toChars(), v2._init.toChars(), vd.toChars());
+ errors = true;
+ }
+ }
+ }
+ return errors;
+ }
+
+ /***************************************
+ * Fill out remainder of elements[] with default initializers for fields[].
+ * Params:
+ * loc = location
+ * elements = explicit arguments which given to construct object.
+ * ctorinit = true if the elements will be used for default initialization.
+ * Returns:
+ * false if any errors occur.
+ * Otherwise, returns true and the missing arguments will be pushed in elements[].
+ */
+ final bool fill(Loc loc, Expressions* elements, bool ctorinit)
+ {
+ //printf("AggregateDeclaration::fill() %s\n", toChars());
+ assert(sizeok == Sizeok.done);
+ assert(elements);
+ const nfields = nonHiddenFields();
+ bool errors = false;
+
+ size_t dim = elements.dim;
+ elements.setDim(nfields);
+ foreach (size_t i; dim .. nfields)
+ (*elements)[i] = null;
+
+ // Fill in missing any elements with default initializers
+ foreach (i; 0 .. nfields)
+ {
+ if ((*elements)[i])
+ continue;
+
+ auto vd = fields[i];
+ auto vx = vd;
+ if (vd._init && vd._init.isVoidInitializer())
+ vx = null;
+
+ // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
+ size_t fieldi = i;
+ foreach (j; 0 .. nfields)
+ {
+ if (i == j)
+ continue;
+ auto v2 = fields[j];
+ if (!vd.isOverlappedWith(v2))
+ continue;
+
+ if ((*elements)[j])
+ {
+ vx = null;
+ break;
+ }
+ if (v2._init && v2._init.isVoidInitializer())
+ continue;
+
+ version (all)
+ {
+ /* Prefer first found non-void-initialized field
+ * union U { int a; int b = 2; }
+ * U u; // Error: overlapping initialization for field a and b
+ */
+ if (!vx)
+ {
+ vx = v2;
+ fieldi = j;
+ }
+ else if (v2._init)
+ {
+ .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
+ errors = true;
+ }
+ }
+ else
+ {
+ // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always
+
+ /* Prefer explicitly initialized field
+ * union U { int a; int b = 2; }
+ * U u; // OK (u.b == 2)
+ */
+ if (!vx || !vx._init && v2._init)
+ {
+ vx = v2;
+ fieldi = j;
+ }
+ else if (vx != vd && !vx.isOverlappedWith(v2))
+ {
+ // Both vx and v2 fills vd, but vx and v2 does not overlap
+ }
+ else if (vx._init && v2._init)
+ {
+ .error(loc, "overlapping default initialization for field `%s` and `%s`",
+ v2.toChars(), vd.toChars());
+ errors = true;
+ }
+ else
+ assert(vx._init || !vx._init && !v2._init);
+ }
+ }
+ if (vx)
+ {
+ Expression e;
+ if (vx.type.size() == 0)
+ {
+ e = null;
+ }
+ else if (vx._init)
+ {
+ assert(!vx._init.isVoidInitializer());
+ if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057
+ {
+ vx.error(loc, "recursive initialization of field");
+ errors = true;
+ }
+ else
+ e = vx.getConstInitializer(false);
+ }
+ else
+ {
+ if ((vx.storage_class & STC.nodefaultctor) && !ctorinit)
+ {
+ .error(loc, "field `%s.%s` must be initialized because it has no default constructor",
+ type.toChars(), vx.toChars());
+ errors = true;
+ }
+ /* https://issues.dlang.org/show_bug.cgi?id=12509
+ * Get the element of static array type.
+ */
+ Type telem = vx.type;
+ if (telem.ty == Tsarray)
+ {
+ /* We cannot use Type::baseElemOf() here.
+ * If the bottom of the Tsarray is an enum type, baseElemOf()
+ * will return the base of the enum, and its default initializer
+ * would be different from the enum's.
+ */
+ TypeSArray tsa;
+ while ((tsa = telem.toBasetype().isTypeSArray()) !is null)
+ telem = tsa.next;
+ if (telem.ty == Tvoid)
+ telem = Type.tuns8.addMod(telem.mod);
+ }
+ if (telem.needsNested() && ctorinit)
+ e = telem.defaultInit(loc);
+ else
+ e = telem.defaultInitLiteral(loc);
+ }
+ (*elements)[fieldi] = e;
+ }
+ }
+ foreach (e; *elements)
+ {
+ if (e && e.op == TOK.error)
+ return false;
+ }
+
+ return !errors;
+ }
+
+ /****************************
+ * Do byte or word alignment as necessary.
+ * Align sizes of 0, as we may not know array sizes yet.
+ * Params:
+ * alignment = struct alignment that is in effect
+ * size = alignment requirement of field
+ * poffset = pointer to offset to be aligned
+ */
+ extern (D) static void alignmember(structalign_t alignment, uint size, uint* poffset) pure nothrow @safe
+ {
+ //printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset);
+ switch (alignment)
+ {
+ case cast(structalign_t)1:
+ // No alignment
+ break;
+
+ case cast(structalign_t)STRUCTALIGN_DEFAULT:
+ // Alignment in Target::fieldalignsize must match what the
+ // corresponding C compiler's default alignment behavior is.
+ assert(size > 0 && !(size & (size - 1)));
+ *poffset = (*poffset + size - 1) & ~(size - 1);
+ break;
+
+ default:
+ // Align on alignment boundary, which must be a positive power of 2
+ assert(alignment > 0 && !(alignment & (alignment - 1)));
+ *poffset = (*poffset + alignment - 1) & ~(alignment - 1);
+ break;
+ }
+ }
+
+ /****************************************
+ * Place a member (mem) into an aggregate (agg), which can be a struct, union or class
+ * Returns:
+ * offset to place field at
+ *
+ * nextoffset: next location in aggregate
+ * memsize: size of member
+ * memalignsize: natural alignment of member
+ * alignment: alignment in effect for this member
+ * paggsize: size of aggregate (updated)
+ * paggalignsize: alignment of aggregate (updated)
+ * isunion: the aggregate is a union
+ */
+ extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize,
+ structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion)
+ {
+ uint ofs = *nextoffset;
+
+ const uint actualAlignment =
+ alignment == STRUCTALIGN_DEFAULT ? memalignsize : alignment;
+
+ // Ensure no overflow
+ bool overflow;
+ const sz = addu(memsize, actualAlignment, overflow);
+ addu(ofs, sz, overflow);
+ if (overflow) assert(0);
+
+ alignmember(alignment, memalignsize, &ofs);
+ uint memoffset = ofs;
+ ofs += memsize;
+ if (ofs > *paggsize)
+ *paggsize = ofs;
+ if (!isunion)
+ *nextoffset = ofs;
+
+ if (*paggalignsize < actualAlignment)
+ *paggalignsize = actualAlignment;
+
+ return memoffset;
+ }
+
+ override final Type getType()
+ {
+ return type;
+ }
+
+ // is aggregate deprecated?
+ override final bool isDeprecated() const
+ {
+ return !!(this.storage_class & STC.deprecated_);
+ }
+
+ /// Flag this aggregate as deprecated
+ final void setDeprecated()
+ {
+ this.storage_class |= STC.deprecated_;
+ }
+
+ /****************************************
+ * Returns true if there's an extra member which is the 'this'
+ * pointer to the enclosing context (enclosing aggregate or function)
+ */
+ final bool isNested() const
+ {
+ return enclosing !is null;
+ }
+
+ /* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
+ */
+ extern (D) final void makeNested()
+ {
+ if (enclosing) // if already nested
+ return;
+ if (sizeok == Sizeok.done)
+ return;
+ if (isUnionDeclaration() || isInterfaceDeclaration())
+ return;
+ if (storage_class & STC.static_)
+ return;
+
+ // If nested struct, add in hidden 'this' pointer to outer scope
+ auto s = toParentLocal();
+ if (!s)
+ s = toParent2();
+ if (!s)
+ return;
+ Type t = null;
+ if (auto fd = s.isFuncDeclaration())
+ {
+ enclosing = fd;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=14422
+ * If a nested class parent is a function, its
+ * context pointer (== `outer`) should be void* always.
+ */
+ t = Type.tvoidptr;
+ }
+ else if (auto ad = s.isAggregateDeclaration())
+ {
+ if (isClassDeclaration() && ad.isClassDeclaration())
+ {
+ enclosing = ad;
+ }
+ else if (isStructDeclaration())
+ {
+ if (auto ti = ad.parent.isTemplateInstance())
+ {
+ enclosing = ti.enclosing;
+ }
+ }
+ t = ad.handleType();
+ }
+ if (enclosing)
+ {
+ //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
+ assert(t);
+ if (t.ty == Tstruct)
+ t = Type.tvoidptr; // t should not be a ref type
+
+ assert(!vthis);
+ vthis = new ThisDeclaration(loc, t);
+ //vthis.storage_class |= STC.ref_;
+
+ // Emulate vthis.addMember()
+ members.push(vthis);
+
+ // Emulate vthis.dsymbolSemantic()
+ vthis.storage_class |= STC.field;
+ vthis.parent = this;
+ vthis.visibility = Visibility(Visibility.Kind.public_);
+ vthis.alignment = t.alignment();
+ vthis.semanticRun = PASS.semanticdone;
+
+ if (sizeok == Sizeok.fwd)
+ fields.push(vthis);
+
+ makeNested2();
+ }
+ }
+
+ /* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
+ */
+ extern (D) final void makeNested2()
+ {
+ if (vthis2)
+ return;
+ if (!vthis)
+ makeNested(); // can't add second before first
+ if (!vthis)
+ return;
+ if (sizeok == Sizeok.done)
+ return;
+ if (isUnionDeclaration() || isInterfaceDeclaration())
+ return;
+ if (storage_class & STC.static_)
+ return;
+
+ auto s0 = toParentLocal();
+ auto s = toParent2();
+ if (!s || !s0 || s == s0)
+ return;
+ auto cd = s.isClassDeclaration();
+ Type t = cd ? cd.type : Type.tvoidptr;
+
+ vthis2 = new ThisDeclaration(loc, t);
+ //vthis2.storage_class |= STC.ref_;
+
+ // Emulate vthis2.addMember()
+ members.push(vthis2);
+
+ // Emulate vthis2.dsymbolSemantic()
+ vthis2.storage_class |= STC.field;
+ vthis2.parent = this;
+ vthis2.visibility = Visibility(Visibility.Kind.public_);
+ vthis2.alignment = t.alignment();
+ vthis2.semanticRun = PASS.semanticdone;
+
+ if (sizeok == Sizeok.fwd)
+ fields.push(vthis2);
+ }
+
+ override final bool isExport() const
+ {
+ return visibility.kind == Visibility.Kind.export_;
+ }
+
+ /*******************************************
+ * Look for constructor declaration.
+ */
+ final Dsymbol searchCtor()
+ {
+ auto s = search(Loc.initial, Id.ctor);
+ if (s)
+ {
+ if (!(s.isCtorDeclaration() ||
+ s.isTemplateDeclaration() ||
+ s.isOverloadSet()))
+ {
+ s.error("is not a constructor; identifiers starting with `__` are reserved for the implementation");
+ errors = true;
+ s = null;
+ }
+ }
+ if (s && s.toParent() != this)
+ s = null; // search() looks through ancestor classes
+ if (s)
+ {
+ // Finish all constructors semantics to determine this.noDefaultCtor.
+ struct SearchCtor
+ {
+ extern (C++) static int fp(Dsymbol s, void* ctxt)
+ {
+ auto f = s.isCtorDeclaration();
+ if (f && f.semanticRun == PASS.init)
+ f.dsymbolSemantic(null);
+ return 0;
+ }
+ }
+
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ auto sm = (*members)[i];
+ sm.apply(&SearchCtor.fp, null);
+ }
+ }
+ return s;
+ }
+
+ override final Visibility visible() pure nothrow @nogc @safe
+ {
+ return visibility;
+ }
+
+ // 'this' type
+ final Type handleType()
+ {
+ return type;
+ }
+
+ // Does this class have an invariant function?
+ final bool hasInvariant()
+ {
+ return invs.length != 0;
+ }
+
+ // Back end
+ void* sinit; /// initializer symbol
+
+ override final inout(AggregateDeclaration) isAggregateDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h
index 4935e6a1f5d..f8d2f45706a 100644
--- a/gcc/d/dmd/aggregate.h
+++ b/gcc/d/dmd/aggregate.h
@@ -10,12 +10,10 @@
#pragma once
-#include "root/root.h"
-
#include "dsymbol.h"
-#include "declaration.h"
#include "objc.h"
+class AliasThis;
class Identifier;
class Type;
class TypeFunction;
@@ -23,65 +21,51 @@ class Expression;
class FuncDeclaration;
class CtorDeclaration;
class DtorDeclaration;
-class InvariantDeclaration;
-class NewDeclaration;
-class DeleteDeclaration;
class InterfaceDeclaration;
class TypeInfoClassDeclaration;
class VarDeclaration;
-enum Sizeok
+enum class Sizeok : uint8_t
{
- SIZEOKnone, // size of aggregate is not yet able to compute
- SIZEOKfwd, // size of aggregate is ready to compute
- SIZEOKdone // size of aggregate is set correctly
+ none, // size of aggregate is not yet able to compute
+ fwd, // size of aggregate is ready to compute
+ inProcess, // in the midst of computing the size
+ done // size of aggregate is set correctly
};
-enum Baseok
+enum class Baseok : uint8_t
{
- BASEOKnone, // base classes not computed yet
- BASEOKin, // in process of resolving base classes
- BASEOKdone, // all base classes are resolved
- BASEOKsemanticdone // all base classes semantic done
+ none, // base classes not computed yet
+ in, // in process of resolving base classes
+ done, // all base classes are resolved
+ semanticdone // all base classes semantic done
};
-enum StructPOD
+enum class ThreeState : uint8_t
{
- ISPODno, // struct is not POD
- ISPODyes, // struct is POD
- ISPODfwd // POD not yet computed
+ none, // value not yet computed
+ no, // value is false
+ yes, // value is true
};
-enum Abstract
+FuncDeclaration *search_toString(StructDeclaration *sd);
+
+enum class ClassKind : uint8_t
{
- ABSfwdref = 0, // whether an abstract class is not yet computed
- ABSyes, // is abstract class
- ABSno // is not abstract class
+ /// the aggregate is a d(efault) struct/class/interface
+ d,
+ /// the aggregate is a C++ struct/class/interface
+ cpp,
+ /// the aggregate is an Objective-C class/interface
+ objc,
+ /// the aggregate is a C struct
+ c,
};
-FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc);
-FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc);
-bool needOpEquals(StructDeclaration *sd);
-FuncDeclaration *buildOpEquals(StructDeclaration *sd, Scope *sc);
-FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc);
-FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc);
-FuncDeclaration *buildXtoHash(StructDeclaration *ad, Scope *sc);
-FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc);
-FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc);
-FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc);
-FuncDeclaration *search_toString(StructDeclaration *sd);
-
-struct ClassKind
+struct MangleOverride
{
- enum Type
- {
- /// the class is a d(efault) class
- d,
- /// the class is a C++ interface
- cpp,
- /// the class is an Objective-C class/interface
- objc,
- };
+ Dsymbol *agg;
+ Identifier *id;
};
class AggregateDeclaration : public ScopeDsymbol
@@ -89,16 +73,16 @@ class AggregateDeclaration : public ScopeDsymbol
public:
Type *type;
StorageClass storage_class;
- Prot protection;
unsigned structsize; // size of struct
unsigned alignsize; // size of struct for alignment purposes
VarDeclarations fields; // VarDeclaration fields
- Sizeok sizeok; // set when structsize contains valid data
Dsymbol *deferred; // any deferred semantic2() or semantic3() symbol
- bool isdeprecated; // true if deprecated
- ClassKind::Type classKind; // specifies the linkage type
+ ClassKind classKind; // specifies the linkage type
+ CPPMANGLE cppmangle;
+ // overridden symbol with pragma(mangle, "...")
+ MangleOverride *mangleOverride;
/* !=NULL if is nested
* pointing to the dsymbol that directly enclosing it.
* 1. The function that enclosing it (nested struct and class)
@@ -108,11 +92,10 @@ public:
*/
Dsymbol *enclosing;
VarDeclaration *vthis; // 'this' parameter if this aggregate is nested
+ VarDeclaration *vthis2; // 'this' parameter if this aggregate is a template and is nested
// Special member functions
FuncDeclarations invs; // Array of invariants
FuncDeclaration *inv; // invariant
- NewDeclaration *aggNew; // allocator
- DeleteDeclaration *aggDelete; // deallocator
Dsymbol *ctor; // CtorDeclaration or TemplateDeclaration
@@ -120,42 +103,44 @@ public:
// it would be stored in TypeInfo_Class.defaultConstructor
CtorDeclaration *defaultCtor;
- Dsymbol *aliasthis; // forward unresolved lookups to aliasthis
- bool noDefaultCtor; // no default construction
+ AliasThis *aliasthis; // forward unresolved lookups to aliasthis
- FuncDeclarations dtors; // Array of destructors
- FuncDeclaration *dtor; // aggregate destructor
+ DtorDeclarations dtors; // Array of destructors
+ DtorDeclaration *dtor; // aggregate destructor
+ DtorDeclaration *primaryDtor; // non-deleting C++ destructor, same as dtor for D
+ DtorDeclaration *tidtor; // aggregate destructor used in TypeInfo (must have extern(D) ABI)
+ FuncDeclaration *fieldDtor; // aggregate destructor for just the fields
Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this)
- AggregateDeclaration(Loc loc, Identifier *id);
+ Visibility visibility;
+ bool noDefaultCtor; // no default construction
+ bool disableNew; // disallow allocations using `new`
+ Sizeok sizeok; // set when structsize contains valid data
+
virtual Scope *newScope(Scope *sc);
void setScope(Scope *sc);
- bool determineFields();
+ size_t nonHiddenFields();
bool determineSize(Loc loc);
virtual void finalizeSize() = 0;
- d_uns64 size(Loc loc);
- bool checkOverlappedFields();
+ d_uns64 size(const Loc &loc);
bool fill(Loc loc, Expressions *elements, bool ctorinit);
- static void alignmember(structalign_t salign, unsigned size, unsigned *poffset);
- static unsigned placeField(unsigned *nextoffset,
- unsigned memsize, unsigned memalignsize, structalign_t memalign,
- unsigned *paggsize, unsigned *paggalignsize, bool isunion);
Type *getType();
- bool isDeprecated(); // is aggregate deprecated?
- bool isNested();
- void makeNested();
+ bool isDeprecated() const; // is aggregate deprecated?
+ void setDeprecated();
+ bool isNested() const;
bool isExport() const;
Dsymbol *searchCtor();
- Prot prot();
+ Visibility visible();
// 'this' type
Type *handleType() { return type; }
+ bool hasInvariant();
+
// Back end
- Symbol *stag; // tag symbol for debug data
- Symbol *sinit;
+ void *sinit;
AggregateDeclaration *isAggregateDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -163,8 +148,7 @@ public:
struct StructFlags
{
- typedef unsigned Type;
- enum Enum
+ enum Type
{
none = 0x0,
hasPointers = 0x1 // NB: should use noPointers as in ClassFlags
@@ -174,9 +158,17 @@ struct StructFlags
class StructDeclaration : public AggregateDeclaration
{
public:
- int zeroInit; // !=0 if initialize with 0 fill
+ bool zeroInit; // !=0 if initialize with 0 fill
bool hasIdentityAssign; // true if has identity opAssign
+ bool hasBlitAssign; // true if opAssign is a blit
bool hasIdentityEquals; // true if has identity opEquals
+ bool hasNoFields; // has no fields
+ bool hasCopyCtor; // copy constructor
+ // Even if struct is defined as non-root symbol, some built-in operations
+ // (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo.
+ // For those, today TypeInfo_Struct is generated in COMDAT.
+ bool requestTypeInfo;
+
FuncDeclarations postblits; // Array of postblit functions
FuncDeclaration *postblit; // aggregate postblit
@@ -187,36 +179,30 @@ public:
static FuncDeclaration *xerrcmp; // object.xopCmp
structalign_t alignment; // alignment applied outside of the struct
- StructPOD ispod; // if struct is POD
+ ThreeState ispod; // if struct is POD
- // For 64 bit Efl function call/return ABI
- Type *arg1type;
- Type *arg2type;
+ // ABI-specific type(s) if the struct can be passed in registers
+ TypeTuple *argTypes;
- // Even if struct is defined as non-root symbol, some built-in operations
- // (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo.
- // For those, today TypeInfo_Struct is generated in COMDAT.
- bool requestTypeInfo;
-
- StructDeclaration(Loc loc, Identifier *id, bool inObject);
static StructDeclaration *create(Loc loc, Identifier *id, bool inObject);
- Dsymbol *syntaxCopy(Dsymbol *s);
- void semanticTypeInfoMembers();
+ StructDeclaration *syntaxCopy(Dsymbol *s);
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
const char *kind() const;
void finalizeSize();
- bool fit(Loc loc, Scope *sc, Expressions *elements, Type *stype);
bool isPOD();
StructDeclaration *isStructDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
+
+ unsigned numArgTypes() const;
+ Type *argType(unsigned index);
+ bool hasRegularCtor(bool checkDisabled = false);
};
class UnionDeclaration : public StructDeclaration
{
public:
- UnionDeclaration(Loc loc, Identifier *id);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ UnionDeclaration *syntaxCopy(Dsymbol *s);
const char *kind() const;
UnionDeclaration *isUnionDeclaration() { return this; }
@@ -236,18 +222,14 @@ struct BaseClass
DArray<BaseClass> baseInterfaces; // if BaseClass is an interface, these
// are a copy of the InterfaceDeclaration::interfaces
- BaseClass();
- BaseClass(Type *type);
-
bool fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance);
- void copyBaseInterfaces(BaseClasses *);
};
struct ClassFlags
{
- typedef unsigned Type;
- enum Enum
+ enum Type
{
+ none = 0x0,
isCOMclass = 0x1,
noPointers = 0x2,
hasOffTi = 0x4,
@@ -286,15 +268,18 @@ public:
TypeInfoClassDeclaration *vclassinfo; // the ClassInfo object for this ClassDeclaration
bool com; // true if this is a COM class (meaning it derives from IUnknown)
- bool isscope; // true if this is a scope class
- Abstract isabstract; // 0: fwdref, 1: is abstract class, 2: not abstract
- int inuse; // to prevent recursive attempts
+ bool stack; // true if this is a scope class
+ int cppDtorVtblIndex; // slot reserved for the virtual destructor [extern(C++)]
+ bool inuse; // to prevent recursive attempts
+
+ ThreeState isabstract; // if abstract class
Baseok baseok; // set the progress of base classes resolving
+ ObjcClassDeclaration objc; // Data for a class declaration that is needed for the Objective-C integration
Symbol *cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr
- ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject = false);
static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ const char *toPrettyChars(bool QualifyTypes = false);
+ ClassDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
bool isBaseOf2(ClassDeclaration *cd);
@@ -318,9 +303,11 @@ public:
const char *kind() const;
void addLocalClass(ClassDeclarations *);
+ void addObjcSymbols(ClassDeclarations *classes, ClassDeclarations *categories);
// Back end
- Symbol *vtblsym;
+ Dsymbol *vtblsym;
+ Dsymbol *vtblSymbol();
ClassDeclaration *isClassDeclaration() { return (ClassDeclaration *)this; }
void accept(Visitor *v) { v->visit(this); }
@@ -329,11 +316,9 @@ public:
class InterfaceDeclaration : public ClassDeclaration
{
public:
- InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ InterfaceDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
bool isBaseOf(ClassDeclaration *cd, int *poffset);
- bool isBaseOf(BaseClass *bc, int *poffset);
const char *kind() const;
int vtblOffset() const;
bool isCPPinterface() const;
diff --git a/gcc/d/dmd/aliasthis.c b/gcc/d/dmd/aliasthis.c
deleted file mode 100644
index 458416f4950..00000000000
--- a/gcc/d/dmd/aliasthis.c
+++ /dev/null
@@ -1,94 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 2009-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/aliasthis.c
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "identifier.h"
-#include "aliasthis.h"
-#include "scope.h"
-#include "aggregate.h"
-#include "dsymbol.h"
-#include "mtype.h"
-#include "declaration.h"
-#include "expression.h"
-#include "tokens.h"
-
-Expression *resolveAliasThis(Scope *sc, Expression *e, bool gag)
-{
- AggregateDeclaration *ad = isAggregate(e->type);
-
- if (ad && ad->aliasthis)
- {
- unsigned olderrors = gag ? global.startGagging() : 0;
-
- Loc loc = e->loc;
- Type *tthis = (e->op == TOKtype ? e->type : NULL);
- e = new DotIdExp(loc, e, ad->aliasthis->ident);
- e = expressionSemantic(e, sc);
- if (tthis && ad->aliasthis->needThis())
- {
- if (e->op == TOKvar)
- {
- if (FuncDeclaration *fd = ((VarExp *)e)->var->isFuncDeclaration())
- {
- // Bugzilla 13009: Support better match for the overloaded alias this.
- bool hasOverloads = false;
- if (FuncDeclaration *f = fd->overloadModMatch(loc, tthis, hasOverloads))
- {
- if (!hasOverloads)
- fd = f; // use exact match
- e = new VarExp(loc, fd, hasOverloads);
- e->type = f->type;
- e = new CallExp(loc, e);
- goto L1;
- }
- }
- }
- /* non-@property function is not called inside typeof(),
- * so resolve it ahead.
- */
- {
- int save = sc->intypeof;
- sc->intypeof = 1; // bypass "need this" error check
- e = resolveProperties(sc, e);
- sc->intypeof = save;
- }
-
- L1:
- e = new TypeExp(loc, new TypeTypeof(loc, e));
- e = expressionSemantic(e, sc);
- }
- e = resolveProperties(sc, e);
-
- if (gag && global.endGagging(olderrors))
- e = NULL;
- }
-
- return e;
-}
-
-AliasThis::AliasThis(Loc loc, Identifier *ident)
- : Dsymbol(NULL) // it's anonymous (no identifier)
-{
- this->loc = loc;
- this->ident = ident;
-}
-
-Dsymbol *AliasThis::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new AliasThis(loc, ident);
-}
-
-const char *AliasThis::kind() const
-{
- return "alias this";
-}
diff --git a/gcc/d/dmd/aliasthis.d b/gcc/d/dmd/aliasthis.d
new file mode 100644
index 00000000000..81e0d7e64be
--- /dev/null
+++ b/gcc/d/dmd/aliasthis.d
@@ -0,0 +1,202 @@
+/**
+ * Implements the `alias this` symbol.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/class.html#alias-this, Alias This)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/aliasthis.d, _aliasthis.d)
+ * Documentation: https://dlang.org/phobos/dmd_aliasthis.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aliasthis.d
+ */
+
+module dmd.aliasthis;
+
+import core.stdc.stdio;
+import dmd.aggregate;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.globals;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.opover;
+import dmd.tokens;
+import dmd.visitor;
+
+/***********************************************************
+ * alias ident this;
+ */
+extern (C++) final class AliasThis : Dsymbol
+{
+ Identifier ident;
+ /// The symbol this `alias this` resolves to
+ Dsymbol sym;
+ /// Whether this `alias this` is deprecated or not
+ bool isDeprecated_;
+
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, null); // it's anonymous (no identifier)
+ this.ident = ident;
+ }
+
+ override AliasThis syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto at = new AliasThis(loc, ident);
+ at.comment = comment;
+ return at;
+ }
+
+ override const(char)* kind() const
+ {
+ return "alias this";
+ }
+
+ AliasThis isAliasThis()
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ override bool isDeprecated() const
+ {
+ return this.isDeprecated_;
+ }
+}
+
+Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false)
+{
+ for (AggregateDeclaration ad = isAggregate(e.type); ad;)
+ {
+ if (ad.aliasthis)
+ {
+ uint olderrors = gag ? global.startGagging() : 0;
+ Loc loc = e.loc;
+ Type tthis = (e.op == TOK.type ? e.type : null);
+ e = new DotIdExp(loc, e, ad.aliasthis.ident);
+ e = e.expressionSemantic(sc);
+ if (tthis && ad.aliasthis.sym.needThis())
+ {
+ if (e.op == TOK.variable)
+ {
+ if (auto fd = (cast(VarExp)e).var.isFuncDeclaration())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13009
+ // Support better match for the overloaded alias this.
+ bool hasOverloads;
+ if (auto f = fd.overloadModMatch(loc, tthis, hasOverloads))
+ {
+ if (!hasOverloads)
+ fd = f; // use exact match
+ e = new VarExp(loc, fd, hasOverloads);
+ e.type = f.type;
+ e = new CallExp(loc, e);
+ goto L1;
+ }
+ }
+ }
+ /* non-@property function is not called inside typeof(),
+ * so resolve it ahead.
+ */
+ {
+ int save = sc.intypeof;
+ sc.intypeof = 1; // bypass "need this" error check
+ e = resolveProperties(sc, e);
+ sc.intypeof = save;
+ }
+ L1:
+ e = new TypeExp(loc, new TypeTypeof(loc, e));
+ e = e.expressionSemantic(sc);
+ }
+ e = resolveProperties(sc, e);
+ if (!gag)
+ ad.aliasthis.checkDeprecatedAliasThis(loc, sc);
+ else if (global.endGagging(olderrors))
+ e = null;
+ }
+
+ import dmd.dclass : ClassDeclaration;
+ auto cd = ad.isClassDeclaration();
+ if ((!e || !ad.aliasthis) && cd && cd.baseClass && cd.baseClass != ClassDeclaration.object)
+ {
+ ad = cd.baseClass;
+ continue;
+ }
+ break;
+ }
+ return e;
+}
+
+/**
+ * Check if an `alias this` is deprecated
+ *
+ * Usually one would use `expression.checkDeprecated(scope, aliasthis)` to
+ * check if `expression` uses a deprecated `aliasthis`, but this calls
+ * `toPrettyChars` which lead to the following message:
+ * "Deprecation: alias this `fullyqualified.aggregate.__anonymous` is deprecated"
+ *
+ * Params:
+ * at = The `AliasThis` object to check
+ * loc = `Loc` of the expression triggering the access to `at`
+ * sc = `Scope` of the expression
+ * (deprecations do not trigger in deprecated scopes)
+ *
+ * Returns:
+ * Whether the alias this was reported as deprecated.
+ */
+bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc)
+{
+ import dmd.errors : deprecation, Classification;
+ import dmd.dsymbolsem : getMessage;
+
+ if (global.params.useDeprecated != DiagnosticReporting.off
+ && at.isDeprecated() && !sc.isDeprecated())
+ {
+ const(char)* message = null;
+ for (Dsymbol p = at; p; p = p.parent)
+ {
+ message = p.depdecl ? p.depdecl.getMessage() : null;
+ if (message)
+ break;
+ }
+ if (message)
+ deprecation(loc, "`alias %s this` is deprecated - %s",
+ at.sym.toChars(), message);
+ else
+ deprecation(loc, "`alias %s this` is deprecated",
+ at.sym.toChars());
+
+ if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
+ ti.printInstantiationTrace(Classification.deprecation);
+
+ return true;
+ }
+ return false;
+}
+
+/**************************************
+ * Check and set 'att' if 't' is a recursive 'alias this' type
+ * Params:
+ * att = type reference used to detect recursion
+ * t = 'alias this' type
+ *
+ * Returns:
+ * Whether the 'alias this' is recursive or not
+ */
+bool isRecursiveAliasThis(ref Type att, Type t)
+{
+ auto tb = t.toBasetype();
+ if (att && tb.equivalent(att))
+ return true;
+ else if (!att && tb.checkAliasThisRec())
+ att = tb;
+ return false;
+}
diff --git a/gcc/d/dmd/aliasthis.h b/gcc/d/dmd/aliasthis.h
index 15905e41710..de93a8e6ae4 100644
--- a/gcc/d/dmd/aliasthis.h
+++ b/gcc/d/dmd/aliasthis.h
@@ -5,11 +5,12 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/dlang/dmd/blob/master/src/aliasthis.h
+ * https://github.com/dlang/dmd/blob/master/src/dmd/aliasthis.h
*/
#pragma once
+#include "globals.h"
#include "dsymbol.h"
/**************************************************************/
@@ -19,11 +20,12 @@ class AliasThis : public Dsymbol
public:
// alias Identifier this;
Identifier *ident;
+ Dsymbol *sym;
+ bool isDeprecated_;
- AliasThis(Loc loc, Identifier *ident);
-
- Dsymbol *syntaxCopy(Dsymbol *);
+ AliasThis *syntaxCopy(Dsymbol *);
const char *kind() const;
AliasThis *isAliasThis() { return this; }
void accept(Visitor *v) { v->visit(this); }
+ bool isDeprecated() const { return this->isDeprecated_; }
};
diff --git a/gcc/d/dmd/apply.c b/gcc/d/dmd/apply.c
deleted file mode 100644
index 8a727ae66d1..00000000000
--- a/gcc/d/dmd/apply.c
+++ /dev/null
@@ -1,149 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/apply.c
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "expression.h"
-#include "template.h"
-#include "visitor.h"
-
-
-/**************************************
- * An Expression tree walker that will visit each Expression e in the tree,
- * in depth-first evaluation order, and call fp(e,param) on it.
- * fp() signals whether the walking continues with its return value:
- * Returns:
- * 0 continue
- * 1 done
- * It's a bit slower than using virtual functions, but more encapsulated and less brittle.
- * Creating an iterator for this would be much more complex.
- */
-
-class PostorderExpressionVisitor : public StoppableVisitor
-{
-public:
- StoppableVisitor *v;
- PostorderExpressionVisitor(StoppableVisitor *v) : v(v) {}
-
- bool doCond(Expression *e)
- {
- if (!stop && e)
- e->accept(this);
- return stop;
- }
- bool doCond(Expressions *e)
- {
- if (!e)
- return false;
- for (size_t i = 0; i < e->length && !stop; i++)
- doCond((*e)[i]);
- return stop;
- }
- bool applyTo(Expression *e)
- {
- e->accept(v);
- stop = v->stop;
- return true;
- }
-
- void visit(Expression *e)
- {
- applyTo(e);
- }
-
- void visit(NewExp *e)
- {
- //printf("NewExp::apply(): %s\n", toChars());
-
- doCond(e->thisexp) || doCond(e->newargs) || doCond(e->arguments) || applyTo(e);
- }
-
- void visit(NewAnonClassExp *e)
- {
- //printf("NewAnonClassExp::apply(): %s\n", toChars());
-
- doCond(e->thisexp) || doCond(e->newargs) || doCond(e->arguments) || applyTo(e);
- }
-
- void visit(TypeidExp *e)
- {
- doCond(isExpression(e->obj)) || applyTo(e);
- }
-
- void visit(UnaExp *e)
- {
- doCond(e->e1) || applyTo(e);
- }
-
- void visit(BinExp *e)
- {
- doCond(e->e1) || doCond(e->e2) || applyTo(e);
- }
-
- void visit(AssertExp *e)
- {
- //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
- doCond(e->e1) || doCond(e->msg) || applyTo(e);
- }
-
- void visit(CallExp *e)
- {
- //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
- doCond(e->e1) || doCond(e->arguments) || applyTo(e);
- }
-
- void visit(ArrayExp *e)
- {
- //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
- doCond(e->e1) || doCond(e->arguments) || applyTo(e);
- }
-
- void visit(SliceExp *e)
- {
- doCond(e->e1) || doCond(e->lwr) || doCond(e->upr) || applyTo(e);
- }
-
- void visit(ArrayLiteralExp *e)
- {
- doCond(e->basis) || doCond(e->elements) || applyTo(e);
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- doCond(e->keys) || doCond(e->values) || applyTo(e);
- }
-
- void visit(StructLiteralExp *e)
- {
- if (e->stageflags & stageApply) return;
- int old = e->stageflags;
- e->stageflags |= stageApply;
- doCond(e->elements) || applyTo(e);
- e->stageflags = old;
- }
-
- void visit(TupleExp *e)
- {
- doCond(e->e0) || doCond(e->exps) || applyTo(e);
- }
-
- void visit(CondExp *e)
- {
- doCond(e->econd) || doCond(e->e1) || doCond(e->e2) || applyTo(e);
- }
-};
-
-bool walkPostorder(Expression *e, StoppableVisitor *v)
-{
- PostorderExpressionVisitor pv(v);
- e->accept(&pv);
- return v->stop;
-}
diff --git a/gcc/d/dmd/apply.d b/gcc/d/dmd/apply.d
new file mode 100644
index 00000000000..ab427e885dc
--- /dev/null
+++ b/gcc/d/dmd/apply.d
@@ -0,0 +1,189 @@
+/**
+ * A depth-first visitor for expressions.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/apply.d, _apply.d)
+ * Documentation: https://dlang.org/phobos/dmd_apply.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/apply.d
+ */
+
+module dmd.apply;
+
+import dmd.arraytypes;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.expression;
+import dmd.visitor;
+
+bool walkPostorder(Expression e, StoppableVisitor v)
+{
+ scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v);
+ e.accept(pv);
+ return v.stop;
+}
+
+/*********************************
+ * Iterate this dsymbol or members of this scoped dsymbol, then
+ * call `fp` with the found symbol and `params`.
+ * Params:
+ * symbol = the dsymbol or parent of members to call fp on
+ * fp = function pointer to process the iterated symbol.
+ * If it returns nonzero, the iteration will be aborted.
+ * params = any parameters passed to fp.
+ * Returns:
+ * nonzero if the iteration is aborted by the return value of fp,
+ * or 0 if it's completed.
+ */
+int apply(FP, Params...)(Dsymbol symbol, FP fp, Params params)
+{
+ if (auto nd = symbol.isNspace())
+ {
+ return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, params); } );
+ }
+ if (auto ad = symbol.isAttribDeclaration())
+ {
+ return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, params); } );
+ }
+ if (auto tm = symbol.isTemplateMixin())
+ {
+ if (tm._scope) // if fwd reference
+ dsymbolSemantic(tm, null); // try to resolve it
+
+ return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, params); } );
+ }
+
+ return fp(symbol, params);
+}
+
+/**************************************
+ * An Expression tree walker that will visit each Expression e in the tree,
+ * in depth-first evaluation order, and call fp(e,param) on it.
+ * fp() signals whether the walking continues with its return value:
+ * Returns:
+ * 0 continue
+ * 1 done
+ * It's a bit slower than using virtual functions, but more encapsulated and less brittle.
+ * Creating an iterator for this would be much more complex.
+ */
+private extern (C++) final class PostorderExpressionVisitor : StoppableVisitor
+{
+ alias visit = typeof(super).visit;
+public:
+ StoppableVisitor v;
+
+ extern (D) this(StoppableVisitor v)
+ {
+ this.v = v;
+ }
+
+ bool doCond(Expression e)
+ {
+ if (!stop && e)
+ e.accept(this);
+ return stop;
+ }
+
+ bool doCond(Expressions* e)
+ {
+ if (!e)
+ return false;
+ for (size_t i = 0; i < e.dim && !stop; i++)
+ doCond((*e)[i]);
+ return stop;
+ }
+
+ bool applyTo(Expression e)
+ {
+ e.accept(v);
+ stop = v.stop;
+ return true;
+ }
+
+ override void visit(Expression e)
+ {
+ applyTo(e);
+ }
+
+ override void visit(NewExp e)
+ {
+ //printf("NewExp::apply(): %s\n", toChars());
+ doCond(e.thisexp) || doCond(e.newargs) || doCond(e.arguments) || applyTo(e);
+ }
+
+ override void visit(NewAnonClassExp e)
+ {
+ //printf("NewAnonClassExp::apply(): %s\n", toChars());
+ doCond(e.thisexp) || doCond(e.newargs) || doCond(e.arguments) || applyTo(e);
+ }
+
+ override void visit(TypeidExp e)
+ {
+ doCond(isExpression(e.obj)) || applyTo(e);
+ }
+
+ override void visit(UnaExp e)
+ {
+ doCond(e.e1) || applyTo(e);
+ }
+
+ override void visit(BinExp e)
+ {
+ doCond(e.e1) || doCond(e.e2) || applyTo(e);
+ }
+
+ override void visit(AssertExp e)
+ {
+ //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
+ doCond(e.e1) || doCond(e.msg) || applyTo(e);
+ }
+
+ override void visit(CallExp e)
+ {
+ //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
+ doCond(e.e1) || doCond(e.arguments) || applyTo(e);
+ }
+
+ override void visit(ArrayExp e)
+ {
+ //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
+ doCond(e.e1) || doCond(e.arguments) || applyTo(e);
+ }
+
+ override void visit(SliceExp e)
+ {
+ doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ doCond(e.basis) || doCond(e.elements) || applyTo(e);
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ doCond(e.keys) || doCond(e.values) || applyTo(e);
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ if (e.stageflags & stageApply)
+ return;
+ int old = e.stageflags;
+ e.stageflags |= stageApply;
+ doCond(e.elements) || applyTo(e);
+ e.stageflags = old;
+ }
+
+ override void visit(TupleExp e)
+ {
+ doCond(e.e0) || doCond(e.exps) || applyTo(e);
+ }
+
+ override void visit(CondExp e)
+ {
+ doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e);
+ }
+}
diff --git a/gcc/d/dmd/arrayop.c b/gcc/d/dmd/arrayop.c
deleted file mode 100644
index 52d596bb871..00000000000
--- a/gcc/d/dmd/arrayop.c
+++ /dev/null
@@ -1,634 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/arrayop.c
- */
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-#include "root/aav.h"
-
-#include "mars.h"
-#include "expression.h"
-#include "statement.h"
-#include "mtype.h"
-#include "declaration.h"
-#include "scope.h"
-#include "id.h"
-#include "module.h"
-#include "init.h"
-#include "tokens.h"
-
-void buildArrayIdent(Expression *e, OutBuffer *buf, Expressions *arguments);
-Expression *buildArrayLoop(Expression *e, Parameters *fparams);
-
-/**************************************
- * Hash table of array op functions already generated or known about.
- */
-
-AA *arrayfuncs;
-
-/**************************************
- * Structure to contain information needed to insert an array op call
- */
-
-FuncDeclaration *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc)
-{
- Parameters *fparams = new Parameters();
- Expression *loopbody = buildArrayLoop(exp, fparams);
-
- /* Construct the function body:
- * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++)
- * loopbody;
- * return p;
- */
-
- Parameter *p = (*fparams)[0];
- // foreach (i; 0 .. p.length)
- Statement *s1 = new ForeachRangeStatement(Loc(), TOKforeach,
- new Parameter(0, NULL, Id::p, NULL, NULL),
- new IntegerExp(Loc(), 0, Type::tsize_t),
- new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p->ident)),
- new ExpStatement(Loc(), loopbody),
- Loc());
- //printf("%s\n", s1->toChars());
- Statement *s2 = new ReturnStatement(Loc(), new IdentifierExp(Loc(), p->ident));
- //printf("s2: %s\n", s2->toChars());
- Statement *fbody = new CompoundStatement(Loc(), s1, s2);
-
- // Built-in array ops should be @trusted, pure, nothrow and nogc
- StorageClass stc = STCtrusted | STCpure | STCnothrow | STCnogc;
-
- /* Construct the function
- */
- TypeFunction *ftype = new TypeFunction(ParameterList(fparams), exp->e1->type, LINKc, stc);
- //printf("fd: %s %s\n", ident->toChars(), ftype->toChars());
- FuncDeclaration *fd = new FuncDeclaration(Loc(), Loc(), ident, STCundefined, ftype);
- fd->fbody = fbody;
- fd->protection = Prot(Prot::public_);
- fd->linkage = LINKc;
- fd->isArrayOp = 1;
-
- sc->_module->importedFrom->members->push(fd);
-
- sc = sc->push();
- sc->parent = sc->_module->importedFrom;
- sc->stc = 0;
- sc->linkage = LINKc;
- dsymbolSemantic(fd, sc);
- semantic2(fd, sc);
- unsigned errors = global.startGagging();
- semantic3(fd, sc);
- if (global.endGagging(errors))
- {
- fd->type = Type::terror;
- fd->errors = true;
- fd->fbody = NULL;
- }
- sc->pop();
-
- return fd;
-}
-
-/**********************************************
- * Check that there are no uses of arrays without [].
- */
-bool isArrayOpValid(Expression *e)
-{
- if (e->op == TOKslice)
- return true;
- if (e->op == TOKarrayliteral)
- {
- Type *t = e->type->toBasetype();
- while (t->ty == Tarray || t->ty == Tsarray)
- t = t->nextOf()->toBasetype();
- return (t->ty != Tvoid);
- }
- Type *tb = e->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (isUnaArrayOp(e->op))
- {
- return isArrayOpValid(((UnaExp *)e)->e1);
- }
- if (isBinArrayOp(e->op) ||
- isBinAssignArrayOp(e->op) ||
- e->op == TOKassign)
- {
- BinExp *be = (BinExp *)e;
- return isArrayOpValid(be->e1) && isArrayOpValid(be->e2);
- }
- if (e->op == TOKconstruct)
- {
- BinExp *be = (BinExp *)e;
- return be->e1->op == TOKslice && isArrayOpValid(be->e2);
- }
- if (e->op == TOKcall)
- {
- return false; // TODO: Decide if [] is required after arrayop calls.
- }
- else
- {
- return false;
- }
- }
- return true;
-}
-
-bool isNonAssignmentArrayOp(Expression *e)
-{
- if (e->op == TOKslice)
- return isNonAssignmentArrayOp(((SliceExp *)e)->e1);
-
- Type *tb = e->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- return (isUnaArrayOp(e->op) || isBinArrayOp(e->op));
- }
- return false;
-}
-
-bool checkNonAssignmentArrayOp(Expression *e, bool suggestion)
-{
- if (isNonAssignmentArrayOp(e))
- {
- const char *s = "";
- if (suggestion)
- s = " (possible missing [])";
- e->error("array operation %s without destination memory not allowed%s", e->toChars(), s);
- return true;
- }
- return false;
-}
-
-/***********************************
- * Construct the array operation expression.
- */
-
-Expression *arrayOp(BinExp *e, Scope *sc)
-{
- //printf("BinExp::arrayOp() %s\n", toChars());
-
- Type *tb = e->type->toBasetype();
- assert(tb->ty == Tarray || tb->ty == Tsarray);
- Type *tbn = tb->nextOf()->toBasetype();
- if (tbn->ty == Tvoid)
- {
- e->error("cannot perform array operations on void[] arrays");
- return new ErrorExp();
- }
- if (!isArrayOpValid(e))
- {
- e->error("invalid array operation %s (possible missing [])", e->toChars());
- return new ErrorExp();
- }
-
- Expressions *arguments = new Expressions();
-
- /* The expression to generate an array operation for is mangled
- * into a name to use as the array operation function name.
- * Mangle in the operands and operators in RPN order, and type.
- */
- OutBuffer buf;
- buf.writestring("_array");
- buildArrayIdent(e, &buf, arguments);
- buf.writeByte('_');
-
- /* Append deco of array element type
- */
- buf.writestring(e->type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco);
-
- char *name = buf.peekChars();
- Identifier *ident = Identifier::idPool(name);
-
- FuncDeclaration **pFd = (FuncDeclaration **)dmd_aaGet(&arrayfuncs, (void *)ident);
- FuncDeclaration *fd = *pFd;
-
- if (!fd)
- fd = buildArrayOp(ident, e, sc);
-
- if (fd && fd->errors)
- {
- const char *fmt;
- if (tbn->ty == Tstruct || tbn->ty == Tclass)
- fmt = "invalid array operation '%s' because %s doesn't support necessary arithmetic operations";
- else if (!tbn->isscalar())
- fmt = "invalid array operation '%s' because %s is not a scalar type";
- else
- fmt = "invalid array operation '%s' for element type %s";
-
- e->error(fmt, e->toChars(), tbn->toChars());
- return new ErrorExp();
- }
-
- *pFd = fd;
-
- Expression *ev = new VarExp(e->loc, fd);
- Expression *ec = new CallExp(e->loc, ev, arguments);
-
- return expressionSemantic(ec, sc);
-}
-
-Expression *arrayOp(BinAssignExp *e, Scope *sc)
-{
- //printf("BinAssignExp::arrayOp() %s\n", toChars());
-
- /* Check that the elements of e1 can be assigned to
- */
- Type *tn = e->e1->type->toBasetype()->nextOf();
-
- if (tn && (!tn->isMutable() || !tn->isAssignable()))
- {
- e->error("slice %s is not mutable", e->e1->toChars());
- return new ErrorExp();
- }
- if (e->e1->op == TOKarrayliteral)
- {
- return e->e1->modifiableLvalue(sc, e->e1);
- }
-
- return arrayOp((BinExp *)e, sc);
-}
-
-/******************************************
- * Construct the identifier for the array operation function,
- * and build the argument list to pass to it.
- */
-
-void buildArrayIdent(Expression *e, OutBuffer *buf, Expressions *arguments)
-{
- class BuildArrayIdentVisitor : public Visitor
- {
- OutBuffer *buf;
- Expressions *arguments;
- public:
- BuildArrayIdentVisitor(OutBuffer *buf, Expressions *arguments)
- : buf(buf), arguments(arguments)
- {
- }
-
- void visit(Expression *e)
- {
- buf->writestring("Exp");
- arguments->shift(e);
- }
-
- void visit(CastExp *e)
- {
- Type *tb = e->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- e->e1->accept(this);
- }
- else
- visit((Expression *)e);
- }
-
- void visit(ArrayLiteralExp *e)
- {
- buf->writestring("Slice");
- arguments->shift(e);
- }
-
- void visit(SliceExp *e)
- {
- buf->writestring("Slice");
- arguments->shift(e);
- }
-
- void visit(AssignExp *e)
- {
- /* Evaluate assign expressions right to left
- */
- e->e2->accept(this);
- e->e1->accept(this);
- buf->writestring("Assign");
- }
-
- void visit(BinAssignExp *e)
- {
- /* Evaluate assign expressions right to left
- */
- e->e2->accept(this);
- e->e1->accept(this);
- const char *s;
- switch(e->op)
- {
- case TOKaddass: s = "Addass"; break;
- case TOKminass: s = "Minass"; break;
- case TOKmulass: s = "Mulass"; break;
- case TOKdivass: s = "Divass"; break;
- case TOKmodass: s = "Modass"; break;
- case TOKxorass: s = "Xorass"; break;
- case TOKandass: s = "Andass"; break;
- case TOKorass: s = "Orass"; break;
- case TOKpowass: s = "Powass"; break;
- default: assert(0);
- }
- buf->writestring(s);
- }
-
- void visit(NegExp *e)
- {
- e->e1->accept(this);
- buf->writestring("Neg");
- }
-
- void visit(ComExp *e)
- {
- e->e1->accept(this);
- buf->writestring("Com");
- }
-
- void visit(BinExp *e)
- {
- /* Evaluate assign expressions left to right
- */
- const char *s = NULL;
- switch(e->op)
- {
- case TOKadd: s = "Add"; break;
- case TOKmin: s = "Min"; break;
- case TOKmul: s = "Mul"; break;
- case TOKdiv: s = "Div"; break;
- case TOKmod: s = "Mod"; break;
- case TOKxor: s = "Xor"; break;
- case TOKand: s = "And"; break;
- case TOKor: s = "Or"; break;
- case TOKpow: s = "Pow"; break;
- default: break;
- }
- if (s)
- {
- Type *tb = e->type->toBasetype();
- Type *t1 = e->e1->type->toBasetype();
- Type *t2 = e->e2->type->toBasetype();
- e->e1->accept(this);
- if (t1->ty == Tarray &&
- ((t2->ty == Tarray && !t1->equivalent(tb)) ||
- (t2->ty != Tarray && !t1->nextOf()->equivalent(e->e2->type))))
- {
- // Bugzilla 12780: if A is narrower than B
- // A[] op B[]
- // A[] op B
- buf->writestring("Of");
- buf->writestring(t1->nextOf()->mutableOf()->deco);
- }
- e->e2->accept(this);
- if (t2->ty == Tarray &&
- ((t1->ty == Tarray && !t2->equivalent(tb)) ||
- (t1->ty != Tarray && !t2->nextOf()->equivalent(e->e1->type))))
- {
- // Bugzilla 12780: if B is narrower than A:
- // A[] op B[]
- // A op B[]
- buf->writestring("Of");
- buf->writestring(t2->nextOf()->mutableOf()->deco);
- }
- buf->writestring(s);
- }
- else
- visit((Expression *)e);
- }
- };
-
- BuildArrayIdentVisitor v(buf, arguments);
- e->accept(&v);
-}
-
-/******************************************
- * Construct the inner loop for the array operation function,
- * and build the parameter list.
- */
-
-Expression *buildArrayLoop(Expression *e, Parameters *fparams)
-{
- class BuildArrayLoopVisitor : public Visitor
- {
- Parameters *fparams;
- Expression *result;
-
- public:
- BuildArrayLoopVisitor(Parameters *fparams)
- : fparams(fparams), result(NULL)
- {
- }
-
- void visit(Expression *e)
- {
- Identifier *id = Identifier::generateId("c", fparams->length);
- Parameter *param = new Parameter(0, e->type, id, NULL, NULL);
- fparams->shift(param);
- result = new IdentifierExp(Loc(), id);
- }
-
- void visit(CastExp *e)
- {
- Type *tb = e->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- e->e1->accept(this);
- }
- else
- visit((Expression *)e);
- }
-
- void visit(ArrayLiteralExp *e)
- {
- Identifier *id = Identifier::generateId("p", fparams->length);
- Parameter *param = new Parameter(STCconst, e->type, id, NULL, NULL);
- fparams->shift(param);
- Expression *ie = new IdentifierExp(Loc(), id);
- Expression *index = new IdentifierExp(Loc(), Id::p);
- result = new ArrayExp(Loc(), ie, index);
- }
-
- void visit(SliceExp *e)
- {
- Identifier *id = Identifier::generateId("p", fparams->length);
- Parameter *param = new Parameter(STCconst, e->type, id, NULL, NULL);
- fparams->shift(param);
- Expression *ie = new IdentifierExp(Loc(), id);
- Expression *index = new IdentifierExp(Loc(), Id::p);
- result = new ArrayExp(Loc(), ie, index);
- }
-
- void visit(AssignExp *e)
- {
- /* Evaluate assign expressions right to left
- */
- Expression *ex2 = buildArrayLoop(e->e2);
- /* Need the cast because:
- * b = c + p[i];
- * where b is a byte fails because (c + p[i]) is an int
- * which cannot be implicitly cast to byte.
- */
- ex2 = new CastExp(Loc(), ex2, e->e1->type->nextOf());
- Expression *ex1 = buildArrayLoop(e->e1);
- Parameter *param = (*fparams)[0];
- param->storageClass = 0;
- result = new AssignExp(Loc(), ex1, ex2);
- }
-
- void visit(BinAssignExp *e)
- {
- /* Evaluate assign expressions right to left
- */
- Expression *ex2 = buildArrayLoop(e->e2);
- Expression *ex1 = buildArrayLoop(e->e1);
- Parameter *param = (*fparams)[0];
- param->storageClass = 0;
- switch(e->op)
- {
- case TOKaddass: result = new AddAssignExp(e->loc, ex1, ex2); return;
- case TOKminass: result = new MinAssignExp(e->loc, ex1, ex2); return;
- case TOKmulass: result = new MulAssignExp(e->loc, ex1, ex2); return;
- case TOKdivass: result = new DivAssignExp(e->loc, ex1, ex2); return;
- case TOKmodass: result = new ModAssignExp(e->loc, ex1, ex2); return;
- case TOKxorass: result = new XorAssignExp(e->loc, ex1, ex2); return;
- case TOKandass: result = new AndAssignExp(e->loc, ex1, ex2); return;
- case TOKorass: result = new OrAssignExp(e->loc, ex1, ex2); return;
- case TOKpowass: result = new PowAssignExp(e->loc, ex1, ex2); return;
- default:
- assert(0);
- }
- }
-
- void visit(NegExp *e)
- {
- Expression *ex1 = buildArrayLoop(e->e1);
- result = new NegExp(Loc(), ex1);
- }
-
- void visit(ComExp *e)
- {
- Expression *ex1 = buildArrayLoop(e->e1);
- result = new ComExp(Loc(), ex1);
- }
-
- void visit(BinExp *e)
- {
- if (isBinArrayOp(e->op))
- {
- /* Evaluate assign expressions left to right
- */
- BinExp *be = (BinExp *)e->copy();
- be->e1 = buildArrayLoop(be->e1);
- be->e2 = buildArrayLoop(be->e2);
- be->type = NULL;
- result = be;
- return;
- }
- else
- {
- visit((Expression *)e);
- return;
- }
- }
-
- Expression *buildArrayLoop(Expression *e)
- {
- e->accept(this);
- return result;
- }
- };
-
- BuildArrayLoopVisitor v(fparams);
- return v.buildArrayLoop(e);
-}
-
-/***********************************************
- * Test if expression is a unary array op.
- */
-
-bool isUnaArrayOp(TOK op)
-{
- switch (op)
- {
- case TOKneg:
- case TOKtilde:
- return true;
- default:
- break;
- }
- return false;
-}
-
-/***********************************************
- * Test if expression is a binary array op.
- */
-
-bool isBinArrayOp(TOK op)
-{
- switch (op)
- {
- case TOKadd:
- case TOKmin:
- case TOKmul:
- case TOKdiv:
- case TOKmod:
- case TOKxor:
- case TOKand:
- case TOKor:
- case TOKpow:
- return true;
- default:
- break;
- }
- return false;
-}
-
-/***********************************************
- * Test if expression is a binary assignment array op.
- */
-
-bool isBinAssignArrayOp(TOK op)
-{
- switch (op)
- {
- case TOKaddass:
- case TOKminass:
- case TOKmulass:
- case TOKdivass:
- case TOKmodass:
- case TOKxorass:
- case TOKandass:
- case TOKorass:
- case TOKpowass:
- return true;
- default:
- break;
- }
- return false;
-}
-
-/***********************************************
- * Test if operand is a valid array op operand.
- */
-
-bool isArrayOpOperand(Expression *e)
-{
- //printf("Expression::isArrayOpOperand() %s\n", e->toChars());
- if (e->op == TOKslice)
- return true;
- if (e->op == TOKarrayliteral)
- {
- Type *t = e->type->toBasetype();
- while (t->ty == Tarray || t->ty == Tsarray)
- t = t->nextOf()->toBasetype();
- return (t->ty != Tvoid);
- }
- Type *tb = e->type->toBasetype();
- if (tb->ty == Tarray)
- {
- return (isUnaArrayOp(e->op) ||
- isBinArrayOp(e->op) ||
- isBinAssignArrayOp(e->op) ||
- e->op == TOKassign);
- }
- return false;
-}
diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d
new file mode 100644
index 00000000000..66be73ea21f
--- /dev/null
+++ b/gcc/d/dmd/arrayop.d
@@ -0,0 +1,387 @@
+/**
+ * Implement array operations, such as `a[] = b[] + c[]`.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/arrays.html#array-operations, Array Operations)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/arrayop.d, _arrayop.d)
+ * Documentation: https://dlang.org/phobos/dmd_arrayop.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/arrayop.d
+ */
+
+module dmd.arrayop;
+
+import core.stdc.stdio;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.root.outbuffer;
+import dmd.statement;
+import dmd.tokens;
+import dmd.visitor;
+
+/**********************************************
+ * Check that there are no uses of arrays without [].
+ */
+bool isArrayOpValid(Expression e)
+{
+ //printf("isArrayOpValid() %s\n", e.toChars());
+ if (e.op == TOK.slice)
+ return true;
+ if (e.op == TOK.arrayLiteral)
+ {
+ Type t = e.type.toBasetype();
+ while (t.ty == Tarray || t.ty == Tsarray)
+ t = t.nextOf().toBasetype();
+ return (t.ty != Tvoid);
+ }
+ Type tb = e.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (isUnaArrayOp(e.op))
+ {
+ return isArrayOpValid((cast(UnaExp)e).e1);
+ }
+ if (isBinArrayOp(e.op) || isBinAssignArrayOp(e.op) || e.op == TOK.assign)
+ {
+ BinExp be = cast(BinExp)e;
+ return isArrayOpValid(be.e1) && isArrayOpValid(be.e2);
+ }
+ if (e.op == TOK.construct)
+ {
+ BinExp be = cast(BinExp)e;
+ return be.e1.op == TOK.slice && isArrayOpValid(be.e2);
+ }
+ // if (e.op == TOK.call)
+ // {
+ // TODO: Decide if [] is required after arrayop calls.
+ // }
+ return false;
+ }
+ return true;
+}
+
+bool isNonAssignmentArrayOp(Expression e)
+{
+ if (e.op == TOK.slice)
+ return isNonAssignmentArrayOp((cast(SliceExp)e).e1);
+
+ Type tb = e.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ return (isUnaArrayOp(e.op) || isBinArrayOp(e.op));
+ }
+ return false;
+}
+
+bool checkNonAssignmentArrayOp(Expression e, bool suggestion = false)
+{
+ if (isNonAssignmentArrayOp(e))
+ {
+ const(char)* s = "";
+ if (suggestion)
+ s = " (possible missing [])";
+ e.error("array operation `%s` without destination memory not allowed%s", e.toChars(), s);
+ return true;
+ }
+ return false;
+}
+
+/***********************************
+ * Construct the array operation expression, call object._arrayOp!(tiargs)(args).
+ *
+ * Encode operand types and operations into tiargs using reverse polish notation (RPN) to preserve precedence.
+ * Unary operations are prefixed with "u" (e.g. "u~").
+ * Pass operand values (slices or scalars) as args.
+ *
+ * Scalar expression sub-trees of `e` are evaluated before calling
+ * into druntime to hoist them out of the loop. This is a valid
+ * evaluation order as the actual array operations have no
+ * side-effect.
+ * References:
+ * https://github.com/dlang/druntime/blob/master/src/object.d#L3944
+ * https://github.com/dlang/druntime/blob/master/src/core/internal/array/operations.d
+ */
+Expression arrayOp(BinExp e, Scope* sc)
+{
+ //printf("BinExp.arrayOp() %s\n", e.toChars());
+ Type tb = e.type.toBasetype();
+ assert(tb.ty == Tarray || tb.ty == Tsarray);
+ Type tbn = tb.nextOf().toBasetype();
+ if (tbn.ty == Tvoid)
+ {
+ e.error("cannot perform array operations on `void[]` arrays");
+ return ErrorExp.get();
+ }
+ if (!isArrayOpValid(e))
+ return arrayOpInvalidError(e);
+
+ auto tiargs = new Objects();
+ auto args = new Expressions();
+ buildArrayOp(sc, e, tiargs, args);
+
+ import dmd.dtemplate : TemplateDeclaration;
+ __gshared TemplateDeclaration arrayOp;
+ if (arrayOp is null)
+ {
+ // Create .object._arrayOp
+ Identifier idArrayOp = Identifier.idPool("_arrayOp");
+ Expression id = new IdentifierExp(e.loc, Id.empty);
+ id = new DotIdExp(e.loc, id, Id.object);
+ id = new DotIdExp(e.loc, id, idArrayOp);
+
+ id = id.expressionSemantic(sc);
+ if (auto te = id.isTemplateExp())
+ arrayOp = te.td;
+ else
+ ObjectNotFound(idArrayOp); // fatal error
+ }
+
+ auto fd = resolveFuncCall(e.loc, sc, arrayOp, tiargs, null, args, FuncResolveFlag.standard);
+ if (!fd || fd.errors)
+ return ErrorExp.get();
+ return new CallExp(e.loc, new VarExp(e.loc, fd, false), args).expressionSemantic(sc);
+}
+
+/// ditto
+Expression arrayOp(BinAssignExp e, Scope* sc)
+{
+ //printf("BinAssignExp.arrayOp() %s\n", toChars());
+
+ /* Check that the elements of e1 can be assigned to
+ */
+ Type tn = e.e1.type.toBasetype().nextOf();
+
+ if (tn && (!tn.isMutable() || !tn.isAssignable()))
+ {
+ e.error("slice `%s` is not mutable", e.e1.toChars());
+ if (e.op == TOK.addAssign)
+ checkPossibleAddCatError!(AddAssignExp, CatAssignExp)(e.isAddAssignExp);
+ return ErrorExp.get();
+ }
+ if (e.e1.op == TOK.arrayLiteral)
+ {
+ return e.e1.modifiableLvalue(sc, e.e1);
+ }
+
+ return arrayOp(cast(BinExp)e, sc);
+}
+
+/******************************************
+ * Convert the expression tree e to template and function arguments,
+ * using reverse polish notation (RPN) to encode order of operations.
+ * Encode operations as string arguments, using a "u" prefix for unary operations.
+ */
+private void buildArrayOp(Scope* sc, Expression e, Objects* tiargs, Expressions* args)
+{
+ extern (C++) final class BuildArrayOpVisitor : Visitor
+ {
+ alias visit = Visitor.visit;
+ Scope* sc;
+ Objects* tiargs;
+ Expressions* args;
+
+ public:
+ extern (D) this(Scope* sc, Objects* tiargs, Expressions* args)
+ {
+ this.sc = sc;
+ this.tiargs = tiargs;
+ this.args = args;
+ }
+
+ override void visit(Expression e)
+ {
+ tiargs.push(e.type);
+ args.push(e);
+ }
+
+ override void visit(SliceExp e)
+ {
+ visit(cast(Expression) e);
+ }
+
+ override void visit(CastExp e)
+ {
+ visit(cast(Expression) e);
+ }
+
+ override void visit(UnaExp e)
+ {
+ Type tb = e.type.toBasetype();
+ if (tb.ty != Tarray && tb.ty != Tsarray) // hoist scalar expressions
+ {
+ visit(cast(Expression) e);
+ }
+ else
+ {
+ // RPN, prefix unary ops with u
+ OutBuffer buf;
+ buf.writestring("u");
+ buf.writestring(Token.toString(e.op));
+ e.e1.accept(this);
+ tiargs.push(new StringExp(Loc.initial, buf.extractSlice()).expressionSemantic(sc));
+ }
+ }
+
+ override void visit(BinExp e)
+ {
+ Type tb = e.type.toBasetype();
+ if (tb.ty != Tarray && tb.ty != Tsarray) // hoist scalar expressions
+ {
+ visit(cast(Expression) e);
+ }
+ else
+ {
+ // RPN
+ e.e1.accept(this);
+ e.e2.accept(this);
+ tiargs.push(new StringExp(Loc.initial, Token.toString(e.op)).expressionSemantic(sc));
+ }
+ }
+ }
+
+ scope v = new BuildArrayOpVisitor(sc, tiargs, args);
+ e.accept(v);
+}
+
+/***********************************************
+ * Some implicit casting can be performed by the _arrayOp template.
+ * Params:
+ * tfrom = type converting from
+ * tto = type converting to
+ * Returns:
+ * true if can be performed by _arrayOp
+ */
+bool isArrayOpImplicitCast(TypeDArray tfrom, TypeDArray tto)
+{
+ const tyf = tfrom.nextOf().toBasetype().ty;
+ const tyt = tto .nextOf().toBasetype().ty;
+ return tyf == tyt ||
+ tyf == Tint32 && tyt == Tfloat64;
+}
+
+/***********************************************
+ * Test if expression is a unary array op.
+ */
+bool isUnaArrayOp(TOK op)
+{
+ switch (op)
+ {
+ case TOK.negate:
+ case TOK.tilde:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/***********************************************
+ * Test if expression is a binary array op.
+ */
+bool isBinArrayOp(TOK op)
+{
+ switch (op)
+ {
+ case TOK.add:
+ case TOK.min:
+ case TOK.mul:
+ case TOK.div:
+ case TOK.mod:
+ case TOK.xor:
+ case TOK.and:
+ case TOK.or:
+ case TOK.pow:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/***********************************************
+ * Test if expression is a binary assignment array op.
+ */
+bool isBinAssignArrayOp(TOK op)
+{
+ switch (op)
+ {
+ case TOK.addAssign:
+ case TOK.minAssign:
+ case TOK.mulAssign:
+ case TOK.divAssign:
+ case TOK.modAssign:
+ case TOK.xorAssign:
+ case TOK.andAssign:
+ case TOK.orAssign:
+ case TOK.powAssign:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/***********************************************
+ * Test if operand is a valid array op operand.
+ */
+bool isArrayOpOperand(Expression e)
+{
+ //printf("Expression.isArrayOpOperand() %s\n", e.toChars());
+ if (e.op == TOK.slice)
+ return true;
+ if (e.op == TOK.arrayLiteral)
+ {
+ Type t = e.type.toBasetype();
+ while (t.ty == Tarray || t.ty == Tsarray)
+ t = t.nextOf().toBasetype();
+ return (t.ty != Tvoid);
+ }
+ Type tb = e.type.toBasetype();
+ if (tb.ty == Tarray)
+ {
+ return (isUnaArrayOp(e.op) ||
+ isBinArrayOp(e.op) ||
+ isBinAssignArrayOp(e.op) ||
+ e.op == TOK.assign);
+ }
+ return false;
+}
+
+
+/***************************************************
+ * Print error message about invalid array operation.
+ * Params:
+ * e = expression with the invalid array operation
+ * Returns:
+ * instance of ErrorExp
+ */
+
+ErrorExp arrayOpInvalidError(Expression e)
+{
+ e.error("invalid array operation `%s` (possible missing [])", e.toChars());
+ if (e.op == TOK.add)
+ checkPossibleAddCatError!(AddExp, CatExp)(e.isAddExp());
+ else if (e.op == TOK.addAssign)
+ checkPossibleAddCatError!(AddAssignExp, CatAssignExp)(e.isAddAssignExp());
+ return ErrorExp.get();
+}
+
+private void checkPossibleAddCatError(AddT, CatT)(AddT ae)
+{
+ if (!ae.e2.type || ae.e2.type.ty != Tarray || !ae.e2.type.implicitConvTo(ae.e1.type))
+ return;
+ CatT ce = new CatT(ae.loc, ae.e1, ae.e2);
+ ae.errorSupplemental("did you mean to concatenate (`%s`) instead ?", ce.toChars());
+}
diff --git a/gcc/d/dmd/arraytypes.d b/gcc/d/dmd/arraytypes.d
new file mode 100644
index 00000000000..b1f8d86a208
--- /dev/null
+++ b/gcc/d/dmd/arraytypes.d
@@ -0,0 +1,57 @@
+/**
+ * Provide aliases for arrays of certain declarations or statements.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/arraytypes.d, _arraytypes.d)
+ * Documentation: https://dlang.org/phobos/dmd_arraytypes.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/arraytypes.d
+ */
+
+module dmd.arraytypes;
+
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dmodule;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.expression;
+import dmd.func;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.root.array;
+import dmd.root.rootobject;
+import dmd.statement;
+
+alias Strings = Array!(const(char)*);
+alias Identifiers = Array!(Identifier);
+alias TemplateParameters = Array!(TemplateParameter);
+alias Expressions = Array!(Expression);
+alias Statements = Array!(Statement);
+alias BaseClasses = Array!(BaseClass*);
+alias ClassDeclarations = Array!(ClassDeclaration);
+alias Dsymbols = Array!(Dsymbol);
+alias Objects = Array!(RootObject);
+alias DtorDeclarations = Array!(DtorDeclaration);
+alias FuncDeclarations = Array!(FuncDeclaration);
+alias Parameters = Array!(Parameter);
+alias Initializers = Array!(Initializer);
+alias VarDeclarations = Array!(VarDeclaration);
+alias Types = Array!(Type);
+alias Catches = Array!(Catch);
+alias StaticDtorDeclarations = Array!(StaticDtorDeclaration);
+alias SharedStaticDtorDeclarations = Array!(SharedStaticDtorDeclaration);
+alias AliasDeclarations = Array!(AliasDeclaration);
+alias Modules = Array!(Module);
+alias CaseStatements = Array!(CaseStatement);
+alias ScopeStatements = Array!(ScopeStatement);
+alias GotoCaseStatements = Array!(GotoCaseStatement);
+alias ReturnStatements = Array!(ReturnStatement);
+alias GotoStatements = Array!(GotoStatement);
+alias TemplateInstances = Array!(TemplateInstance);
+alias Ensures = Array!(Ensure);
+alias Designators = Array!(Designator);
+alias DesigInits = Array!(DesigInit);
+
diff --git a/gcc/d/dmd/arraytypes.h b/gcc/d/dmd/arraytypes.h
index 0ecccf170bd..602d89059a0 100644
--- a/gcc/d/dmd/arraytypes.h
+++ b/gcc/d/dmd/arraytypes.h
@@ -27,6 +27,8 @@ typedef Array<class Dsymbol *> Dsymbols;
typedef Array<class RootObject *> Objects;
+typedef Array<class DtorDeclaration *> DtorDeclarations;
+
typedef Array<class FuncDeclaration *> FuncDeclarations;
typedef Array<class Parameter *> Parameters;
@@ -48,8 +50,6 @@ typedef Array<class AliasDeclaration *> AliasDeclarations;
typedef Array<class Module *> Modules;
-typedef Array<struct File *> Files;
-
typedef Array<class CaseStatement *> CaseStatements;
typedef Array<class ScopeStatement *> ScopeStatements;
@@ -63,3 +63,8 @@ typedef Array<class GotoStatement *> GotoStatements;
typedef Array<class TemplateInstance *> TemplateInstances;
typedef Array<struct Ensure> Ensures;
+
+typedef Array<struct Designator> Designators;
+
+typedef Array<struct DesigInit> DesigInits;
+
diff --git a/gcc/d/dmd/ast_node.d b/gcc/d/dmd/ast_node.d
new file mode 100644
index 00000000000..82d62a03657
--- /dev/null
+++ b/gcc/d/dmd/ast_node.d
@@ -0,0 +1,26 @@
+/**
+ * Defines the base class for all nodes which are part of the AST.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ast_node.d, _ast_node.d)
+ * Documentation: https://dlang.org/phobos/dmd_ast_node.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ast_node.d
+ */
+module dmd.ast_node;
+
+import dmd.root.rootobject : RootObject;
+import dmd.visitor : Visitor;
+
+/// The base class of all AST nodes.
+extern (C++) abstract class ASTNode : RootObject
+{
+ /**
+ * Visits this AST node using the given visitor.
+ *
+ * Params:
+ * v = the visitor to use when visiting this node
+ */
+ abstract void accept(Visitor v);
+}
diff --git a/gcc/d/dmd/astcodegen.d b/gcc/d/dmd/astcodegen.d
new file mode 100644
index 00000000000..d40f836faae
--- /dev/null
+++ b/gcc/d/dmd/astcodegen.d
@@ -0,0 +1,102 @@
+/**
+ * Defines AST nodes for the code generation stage.
+ *
+ * Documentation: https://dlang.org/phobos/dmd_astcodegen.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/astcodegen.d
+ */
+module dmd.astcodegen;
+
+
+struct ASTCodegen
+{
+ public import dmd.aggregate;
+ public import dmd.aliasthis;
+ public import dmd.arraytypes;
+ public import dmd.attrib;
+ public import dmd.cond;
+ public import dmd.dclass;
+ public import dmd.declaration;
+ public import dmd.denum;
+ public import dmd.dimport;
+ public import dmd.dmodule;
+ public import dmd.dstruct;
+ public import dmd.dsymbol;
+ public import dmd.dtemplate;
+ public import dmd.dversion;
+ public import dmd.expression;
+ public import dmd.func;
+ public import dmd.hdrgen;
+ public import dmd.init;
+ public import dmd.initsem;
+ public import dmd.mtype;
+ public import dmd.nspace;
+ public import dmd.statement;
+ public import dmd.staticassert;
+ public import dmd.typesem;
+ public import dmd.ctfeexpr;
+ public import dmd.init : Designator;
+
+
+ alias initializerToExpression = dmd.initsem.initializerToExpression;
+ alias typeToExpression = dmd.typesem.typeToExpression;
+ alias UserAttributeDeclaration = dmd.attrib.UserAttributeDeclaration;
+ alias Ensure = dmd.func.Ensure; // workaround for bug in older DMD frontends
+ alias ErrorExp = dmd.expression.ErrorExp;
+
+ alias MODFlags = dmd.mtype.MODFlags;
+ alias Type = dmd.mtype.Type;
+ alias Parameter = dmd.mtype.Parameter;
+ alias Tarray = dmd.mtype.Tarray;
+ alias Taarray = dmd.mtype.Taarray;
+ alias Tbool = dmd.mtype.Tbool;
+ alias Tchar = dmd.mtype.Tchar;
+ alias Tdchar = dmd.mtype.Tdchar;
+ alias Tdelegate = dmd.mtype.Tdelegate;
+ alias Tenum = dmd.mtype.Tenum;
+ alias Terror = dmd.mtype.Terror;
+ alias Tfloat32 = dmd.mtype.Tfloat32;
+ alias Tfloat64 = dmd.mtype.Tfloat64;
+ alias Tfloat80 = dmd.mtype.Tfloat80;
+ alias Tfunction = dmd.mtype.Tfunction;
+ alias Tpointer = dmd.mtype.Tpointer;
+ alias Treference = dmd.mtype.Treference;
+ alias Tident = dmd.mtype.Tident;
+ alias Tint8 = dmd.mtype.Tint8;
+ alias Tint16 = dmd.mtype.Tint16;
+ alias Tint32 = dmd.mtype.Tint32;
+ alias Tint64 = dmd.mtype.Tint64;
+ alias Tsarray = dmd.mtype.Tsarray;
+ alias Tstruct = dmd.mtype.Tstruct;
+ alias Tuns8 = dmd.mtype.Tuns8;
+ alias Tuns16 = dmd.mtype.Tuns16;
+ alias Tuns32 = dmd.mtype.Tuns32;
+ alias Tuns64 = dmd.mtype.Tuns64;
+ alias Tvoid = dmd.mtype.Tvoid;
+ alias Twchar = dmd.mtype.Twchar;
+ alias Tnoreturn = dmd.mtype.Tnoreturn;
+
+ alias Timaginary32 = dmd.mtype.Timaginary32;
+ alias Timaginary64 = dmd.mtype.Timaginary64;
+ alias Timaginary80 = dmd.mtype.Timaginary80;
+ alias Tcomplex32 = dmd.mtype.Tcomplex32;
+ alias Tcomplex64 = dmd.mtype.Tcomplex64;
+ alias Tcomplex80 = dmd.mtype.Tcomplex80;
+
+ alias ParameterList = dmd.mtype.ParameterList;
+ alias VarArg = dmd.mtype.VarArg;
+ alias STC = dmd.declaration.STC;
+ alias Dsymbol = dmd.dsymbol.Dsymbol;
+ alias Dsymbols = dmd.dsymbol.Dsymbols;
+ alias Visibility = dmd.dsymbol.Visibility;
+
+ alias stcToBuffer = dmd.hdrgen.stcToBuffer;
+ alias linkageToChars = dmd.hdrgen.linkageToChars;
+ alias visibilityToChars = dmd.hdrgen.visibilityToChars;
+
+ alias isType = dmd.dtemplate.isType;
+ alias isExpression = dmd.dtemplate.isExpression;
+ alias isTuple = dmd.dtemplate.isTuple;
+
+ alias IgnoreErrors = dmd.dsymbol.IgnoreErrors;
+ alias PASS = dmd.dsymbol.PASS;
+}
diff --git a/gcc/d/dmd/astenums.d b/gcc/d/dmd/astenums.d
new file mode 100644
index 00000000000..df88bb97bdf
--- /dev/null
+++ b/gcc/d/dmd/astenums.d
@@ -0,0 +1,391 @@
+/**
+ * Defines enums common to dmd and dmd as parse library.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/astenums.d, _astenums.d)
+ * Documentation: https://dlang.org/phobos/dmd_astenums.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/astenums.d
+ */
+
+module dmd.astenums;
+
+enum Sizeok : ubyte
+{
+ none, /// size of aggregate is not yet able to compute
+ fwd, /// size of aggregate is ready to compute
+ inProcess, /// in the midst of computing the size
+ done, /// size of aggregate is set correctly
+}
+
+enum Baseok : ubyte
+{
+ none, /// base classes not computed yet
+ start, /// in process of resolving base classes
+ done, /// all base classes are resolved
+ semanticdone, /// all base classes semantic done
+}
+
+enum MODFlags : int
+{
+ const_ = 1, // type is const
+ immutable_ = 4, // type is immutable
+ shared_ = 2, // type is shared
+ wild = 8, // type is wild
+ wildconst = (MODFlags.wild | MODFlags.const_), // type is wild const
+ mutable = 0x10, // type is mutable (only used in wildcard matching)
+}
+
+alias MOD = ubyte;
+
+enum STC : ulong // transfer changes to declaration.h
+{
+ undefined_ = 0,
+
+ static_ = 1, /// `static`
+ extern_ = 2, /// `extern`
+ const_ = 4, /// `const`
+ final_ = 8, /// `final`
+
+ abstract_ = 0x10, /// `abstract`
+ parameter = 0x20, /// is function parameter
+ field = 0x40, /// is field of struct, union or class
+ override_ = 0x80, /// `override`
+
+ auto_ = 0x100, /// `auto`
+ synchronized_ = 0x200, /// `synchronized`
+ deprecated_ = 0x400, /// `deprecated`
+ in_ = 0x800, /// `in` parameter
+
+ out_ = 0x1000, /// `out` parameter
+ lazy_ = 0x2000, /// `lazy` parameter
+ foreach_ = 0x4000, /// variable for foreach loop
+ variadic = 0x8000, /// the `variadic` parameter in: T foo(T a, U b, V variadic...)
+
+ ctorinit = 0x1_0000, /// can only be set inside constructor
+ templateparameter = 0x2_0000, /// template parameter
+ ref_ = 0x4_0000, /// `ref`
+ scope_ = 0x8_0000, /// `scope`
+
+ maybescope = 0x10_0000, /// parameter might be `scope`
+ scopeinferred = 0x20_0000, /// `scope` has been inferred and should not be part of mangling, `scope_` must also be set
+ return_ = 0x40_0000, /// 'return ref' or 'return scope' for function parameters
+ returnScope = 0x80_0000, /// if `ref return scope` then resolve to `ref` and `return scope`
+
+ returninferred = 0x100_0000, /// `return` has been inferred and should not be part of mangling, `return_` must also be set
+ immutable_ = 0x200_0000, /// `immutable`
+ init = 0x400_0000, /// has explicit initializer
+ manifest = 0x800_0000, /// manifest constant
+
+ nodtor = 0x1000_0000, /// do not run destructor
+ nothrow_ = 0x2000_0000, /// `nothrow` meaning never throws exceptions
+ pure_ = 0x4000_0000, /// `pure` function
+ tls = 0x8000_0000, /// thread local
+
+ alias_ = 0x1_0000_0000, /// `alias` parameter
+ shared_ = 0x2_0000_0000, /// accessible from multiple threads
+ gshared = 0x4_0000_0000, /// accessible from multiple threads, but not typed as `shared`
+ wild = 0x8_0000_0000, /// for wild type constructor
+
+ property = 0x10_0000_0000, /// `@property`
+ safe = 0x20_0000_0000, /// `@safe`
+ trusted = 0x40_0000_0000, /// `@trusted`
+ system = 0x80_0000_0000, /// `@system`
+
+ ctfe = 0x100_0000_0000, /// can be used in CTFE, even if it is static
+ disable = 0x200_0000_0000, /// for functions that are not callable
+ result = 0x400_0000_0000, /// for result variables passed to out contracts
+ nodefaultctor = 0x800_0000_0000, /// must be set inside constructor
+
+ temp = 0x1000_0000_0000, /// temporary variable
+ rvalue = 0x2000_0000_0000, /// force rvalue for variables
+ nogc = 0x4000_0000_0000, /// `@nogc`
+ autoref = 0x8000_0000_0000, /// Mark for the already deduced `auto ref` parameter
+
+ inference = 0x1_0000_0000_0000, /// do attribute inference
+ exptemp = 0x2_0000_0000_0000, /// temporary variable that has lifetime restricted to an expression
+ future = 0x4_0000_0000_0000, /// introducing new base class function
+ local = 0x8_0000_0000_0000, /// do not forward (see dmd.dsymbol.ForwardingScopeDsymbol).
+
+ live = 0x10_0000_0000_0000, /// function `@live` attribute
+ register = 0x20_0000_0000_0000, /// `register` storage class (ImportC)
+ volatile_ = 0x40_0000_0000_0000, /// destined for volatile in the back end
+
+ safeGroup = STC.safe | STC.trusted | STC.system,
+ IOR = STC.in_ | STC.ref_ | STC.out_,
+ TYPECTOR = (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild),
+ FUNCATTR = (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.property | STC.live |
+ safeGroup),
+
+ /* These are visible to the user, i.e. are expressed by the user
+ */
+ visibleStorageClasses =
+ (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.const_ | STC.final_ | STC.abstract_ | STC.synchronized_ |
+ STC.deprecated_ | STC.future | STC.override_ | STC.lazy_ | STC.alias_ | STC.out_ | STC.in_ | STC.manifest |
+ STC.immutable_ | STC.shared_ | STC.wild | STC.nothrow_ | STC.nogc | STC.pure_ | STC.ref_ | STC.return_ | STC.tls | STC.gshared |
+ STC.property | STC.safeGroup | STC.disable | STC.local | STC.live),
+
+ /* These storage classes "flow through" to the inner scope of a Dsymbol
+ */
+ flowThruAggregate = STC.safeGroup, /// for an AggregateDeclaration
+ flowThruFunction = ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.abstract_ | STC.deprecated_ | STC.override_ |
+ STC.TYPECTOR | STC.final_ | STC.tls | STC.gshared | STC.ref_ | STC.return_ | STC.property |
+ STC.nothrow_ | STC.pure_ | STC.safe | STC.trusted | STC.system), /// for a FuncDeclaration
+
+}
+
+/********
+ * Determine if it's the ambigous case of where `return` attaches to.
+ * Params:
+ * stc = STC flags
+ * Returns:
+ * true if (`ref` | `out`) and `scope` and `return`
+ */
+@safe pure @nogc nothrow
+bool isRefReturnScope(const ulong stc)
+{
+ return (stc & (STC.scope_ | STC.return_)) == (STC.scope_ | STC.return_) &&
+ stc & (STC.ref_ | STC.out_);
+}
+
+/* This is different from the one in declaration.d, make that fix a separate PR */
+static if (0)
+extern (C++) __gshared const(StorageClass) STCStorageClass =
+ (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.const_ | STC.final_ |
+ STC.abstract_ | STC.synchronized_ | STC.deprecated_ | STC.override_ | STC.lazy_ |
+ STC.alias_ | STC.out_ | STC.in_ | STC.manifest | STC.immutable_ | STC.shared_ |
+ STC.wild | STC.nothrow_ | STC.nogc | STC.pure_ | STC.ref_ | STC.return_ | STC.tls |
+ STC.gshared | STC.property | STC.live |
+ STC.safeGroup | STC.disable);
+
+enum TY : ubyte
+{
+ Tarray, // slice array, aka T[]
+ Tsarray, // static array, aka T[dimension]
+ Taarray, // associative array, aka T[type]
+ Tpointer,
+ Treference,
+ Tfunction,
+ Tident,
+ Tclass,
+ Tstruct,
+ Tenum,
+
+ Tdelegate,
+ Tnone,
+ Tvoid,
+ Tint8,
+ Tuns8,
+ Tint16,
+ Tuns16,
+ Tint32,
+ Tuns32,
+ Tint64,
+
+ Tuns64,
+ Tfloat32,
+ Tfloat64,
+ Tfloat80,
+ Timaginary32,
+ Timaginary64,
+ Timaginary80,
+ Tcomplex32,
+ Tcomplex64,
+ Tcomplex80,
+
+ Tbool,
+ Tchar,
+ Twchar,
+ Tdchar,
+ Terror,
+ Tinstance,
+ Ttypeof,
+ Ttuple,
+ Tslice,
+ Treturn,
+
+ Tnull,
+ Tvector,
+ Tint128,
+ Tuns128,
+ Ttraits,
+ Tmixin,
+ Tnoreturn,
+ Ttag,
+ TMAX
+}
+
+alias Tarray = TY.Tarray;
+alias Tsarray = TY.Tsarray;
+alias Taarray = TY.Taarray;
+alias Tpointer = TY.Tpointer;
+alias Treference = TY.Treference;
+alias Tfunction = TY.Tfunction;
+alias Tident = TY.Tident;
+alias Tclass = TY.Tclass;
+alias Tstruct = TY.Tstruct;
+alias Tenum = TY.Tenum;
+alias Tdelegate = TY.Tdelegate;
+alias Tnone = TY.Tnone;
+alias Tvoid = TY.Tvoid;
+alias Tint8 = TY.Tint8;
+alias Tuns8 = TY.Tuns8;
+alias Tint16 = TY.Tint16;
+alias Tuns16 = TY.Tuns16;
+alias Tint32 = TY.Tint32;
+alias Tuns32 = TY.Tuns32;
+alias Tint64 = TY.Tint64;
+alias Tuns64 = TY.Tuns64;
+alias Tfloat32 = TY.Tfloat32;
+alias Tfloat64 = TY.Tfloat64;
+alias Tfloat80 = TY.Tfloat80;
+alias Timaginary32 = TY.Timaginary32;
+alias Timaginary64 = TY.Timaginary64;
+alias Timaginary80 = TY.Timaginary80;
+alias Tcomplex32 = TY.Tcomplex32;
+alias Tcomplex64 = TY.Tcomplex64;
+alias Tcomplex80 = TY.Tcomplex80;
+alias Tbool = TY.Tbool;
+alias Tchar = TY.Tchar;
+alias Twchar = TY.Twchar;
+alias Tdchar = TY.Tdchar;
+alias Terror = TY.Terror;
+alias Tinstance = TY.Tinstance;
+alias Ttypeof = TY.Ttypeof;
+alias Ttuple = TY.Ttuple;
+alias Tslice = TY.Tslice;
+alias Treturn = TY.Treturn;
+alias Tnull = TY.Tnull;
+alias Tvector = TY.Tvector;
+alias Tint128 = TY.Tint128;
+alias Tuns128 = TY.Tuns128;
+alias Ttraits = TY.Ttraits;
+alias Tmixin = TY.Tmixin;
+alias Tnoreturn = TY.Tnoreturn;
+alias Ttag = TY.Ttag;
+alias TMAX = TY.TMAX;
+
+enum TFlags
+{
+ integral = 1,
+ floating = 2,
+ unsigned = 4,
+ real_ = 8,
+ imaginary = 0x10,
+ complex = 0x20,
+}
+
+enum PKG : int
+{
+ unknown, /// not yet determined whether it's a package.d or not
+ module_, /// already determined that's an actual package.d
+ package_, /// already determined that's an actual package
+}
+
+enum ThreeState : ubyte
+{
+ none, /// state is not yet computed
+ no, /// state is false
+ yes, /// state is true
+}
+
+enum TRUST : ubyte
+{
+ default_ = 0,
+ system = 1, // @system (same as TRUST.default)
+ trusted = 2, // @trusted
+ safe = 3, // @safe
+}
+
+enum PURE : ubyte
+{
+ impure = 0, // not pure at all
+ fwdref = 1, // it's pure, but not known which level yet
+ weak = 2, // no mutable globals are read or written
+ const_ = 3, // parameters are values or const
+ strong = 4, // parameters are values or immutable
+}
+
+// Whether alias this dependency is recursive or not
+enum AliasThisRec : int
+{
+ no = 0, // no alias this recursion
+ yes = 1, // alias this has recursive dependency
+ fwdref = 2, // not yet known
+ typeMask = 3, // mask to read no/yes/fwdref
+ tracing = 0x4, // mark in progress of implicitConvTo/deduceWild
+ tracingDT = 0x8, // mark in progress of deduceType
+}
+
+/***************
+ * Variadic argument lists
+ * https://dlang.org/spec/function.html#variadic
+ */
+enum VarArg : ubyte
+{
+ none = 0, /// fixed number of arguments
+ variadic = 1, /// (T t, ...) can be C-style (core.stdc.stdarg) or D-style (core.vararg)
+ typesafe = 2, /// (T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions
+ /// or https://dlang.org/spec/function.html#typesafe_variadic_functions
+}
+
+/*************************
+ * Identify Statement types with this enum rather than
+ * virtual functions
+ */
+enum STMT : ubyte
+{
+ Error,
+ Peel,
+ Exp, DtorExp,
+ Compile,
+ Compound, CompoundDeclaration, CompoundAsm,
+ UnrolledLoop,
+ Scope,
+ Forwarding,
+ While,
+ Do,
+ For,
+ Foreach,
+ ForeachRange,
+ If,
+ Conditional,
+ StaticForeach,
+ Pragma,
+ StaticAssert,
+ Switch,
+ Case,
+ CaseRange,
+ Default,
+ GotoDefault,
+ GotoCase,
+ SwitchError,
+ Return,
+ Break,
+ Continue,
+ Synchronized,
+ With,
+ TryCatch,
+ TryFinally,
+ ScopeGuard,
+ Throw,
+ Debug,
+ Goto,
+ Label,
+ Asm, InlineAsm, GccAsm,
+ Import,
+}
+
+/**********************
+ * Discriminant for which kind of initializer
+ */
+enum InitKind : ubyte
+{
+ void_,
+ error,
+ struct_,
+ array,
+ exp,
+ C_,
+}
+
diff --git a/gcc/d/dmd/attrib.c b/gcc/d/dmd/attrib.c
deleted file mode 100644
index a808b8a6c3f..00000000000
--- a/gcc/d/dmd/attrib.c
+++ /dev/null
@@ -1,1320 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/attrib.c
- */
-
-#include "root/dsystem.h" // memcmp()
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "init.h"
-#include "declaration.h"
-#include "attrib.h"
-#include "cond.h"
-#include "scope.h"
-#include "id.h"
-#include "expression.h"
-#include "dsymbol.h"
-#include "aggregate.h"
-#include "module.h"
-#include "parse.h"
-#include "target.h"
-#include "template.h"
-#include "utf.h"
-#include "mtype.h"
-
-bool definitelyValueParameter(Expression *e);
-Dsymbols *makeTupleForeachStaticDecl(Scope *sc, ForeachStatement *fs, Dsymbols *dbody, bool needExpansion);
-
-/********************************* AttribDeclaration ****************************/
-
-AttribDeclaration::AttribDeclaration(Dsymbols *decl)
- : Dsymbol()
-{
- this->decl = decl;
-}
-
-Dsymbols *AttribDeclaration::include(Scope *)
-{
- if (errors)
- return NULL;
-
- return decl;
-}
-
-int AttribDeclaration::apply(Dsymbol_apply_ft_t fp, void *param)
-{
- Dsymbols *d = include(_scope);
-
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- if (s)
- {
- if (s->apply(fp, param))
- return 1;
- }
- }
- }
- return 0;
-}
-
-/****************************************
- * Create a new scope if one or more given attributes
- * are different from the sc's.
- * If the returned scope != sc, the caller should pop
- * the scope after it used.
- */
-Scope *AttribDeclaration::createNewScope(Scope *sc,
- StorageClass stc, LINK linkage, CPPMANGLE cppmangle, Prot protection,
- int explicitProtection, AlignDeclaration *aligndecl, PINLINE inlining)
-{
- Scope *sc2 = sc;
- if (stc != sc->stc ||
- linkage != sc->linkage ||
- cppmangle != sc->cppmangle ||
- explicitProtection != sc->explicitProtection ||
- !(protection == sc->protection) ||
- aligndecl != sc->aligndecl ||
- inlining != sc->inlining)
- {
- // create new one for changes
- sc2 = sc->copy();
- sc2->stc = stc;
- sc2->linkage = linkage;
- sc2->cppmangle = cppmangle;
- sc2->protection = protection;
- sc2->explicitProtection = explicitProtection;
- sc2->aligndecl = aligndecl;
- sc2->inlining = inlining;
- }
- return sc2;
-}
-
-/****************************************
- * A hook point to supply scope for members.
- * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this.
- */
-Scope *AttribDeclaration::newScope(Scope *sc)
-{
- return sc;
-}
-
-void AttribDeclaration::addMember(Scope *sc, ScopeDsymbol *sds)
-{
- Dsymbols *d = include(sc);
-
- if (d)
- {
- Scope *sc2 = newScope(sc);
-
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- //printf("\taddMember %s to %s\n", s->toChars(), sds->toChars());
- s->addMember(sc2, sds);
- }
-
- if (sc2 != sc)
- sc2->pop();
- }
-}
-
-void AttribDeclaration::setScope(Scope *sc)
-{
- Dsymbols *d = include(sc);
-
- //printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d);
- if (d)
- {
- Scope *sc2 = newScope(sc);
-
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->setScope(sc2);
- }
-
- if (sc2 != sc)
- sc2->pop();
- }
-}
-
-void AttribDeclaration::importAll(Scope *sc)
-{
- Dsymbols *d = include(sc);
-
- //printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d);
- if (d)
- {
- Scope *sc2 = newScope(sc);
-
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->importAll(sc2);
- }
-
- if (sc2 != sc)
- sc2->pop();
- }
-}
-
-void AttribDeclaration::addComment(const utf8_t *comment)
-{
- //printf("AttribDeclaration::addComment %s\n", comment);
- if (comment)
- {
- Dsymbols *d = include(NULL);
-
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- //printf("AttribDeclaration::addComment %s\n", s->toChars());
- s->addComment(comment);
- }
- }
- }
-}
-
-void AttribDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion)
-{
- Dsymbols *d = include(NULL);
-
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->setFieldOffset(ad, poffset, isunion);
- }
- }
-}
-
-bool AttribDeclaration::hasPointers()
-{
- Dsymbols *d = include(NULL);
-
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- if (s->hasPointers())
- return true;
- }
- }
- return false;
-}
-
-bool AttribDeclaration::hasStaticCtorOrDtor()
-{
- Dsymbols *d = include(NULL);
-
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- if (s->hasStaticCtorOrDtor())
- return true;
- }
- }
- return false;
-}
-
-const char *AttribDeclaration::kind() const
-{
- return "attribute";
-}
-
-bool AttribDeclaration::oneMember(Dsymbol **ps, Identifier *ident)
-{
- Dsymbols *d = include(NULL);
-
- return Dsymbol::oneMembers(d, ps, ident);
-}
-
-void AttribDeclaration::checkCtorConstInit()
-{
- Dsymbols *d = include(NULL);
-
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->checkCtorConstInit();
- }
- }
-}
-
-/****************************************
- */
-
-void AttribDeclaration::addLocalClass(ClassDeclarations *aclasses)
-{
- Dsymbols *d = include(NULL);
-
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->addLocalClass(aclasses);
- }
- }
-}
-
-/************************* StorageClassDeclaration ****************************/
-
-StorageClassDeclaration::StorageClassDeclaration(StorageClass stc, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- this->stc = stc;
-}
-
-Dsymbol *StorageClassDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new StorageClassDeclaration(stc, Dsymbol::arraySyntaxCopy(decl));
-}
-
-bool StorageClassDeclaration::oneMember(Dsymbol **ps, Identifier *ident)
-{
- bool t = Dsymbol::oneMembers(decl, ps, ident);
- if (t && *ps)
- {
- /* This is to deal with the following case:
- * struct Tick {
- * template to(T) { const T to() { ... } }
- * }
- * For eponymous function templates, the 'const' needs to get attached to 'to'
- * before the semantic analysis of 'to', so that template overloading based on the
- * 'this' pointer can be successful.
- */
-
- FuncDeclaration *fd = (*ps)->isFuncDeclaration();
- if (fd)
- {
- /* Use storage_class2 instead of storage_class otherwise when we do .di generation
- * we'll wind up with 'const const' rather than 'const'.
- */
- /* Don't think we need to worry about mutually exclusive storage classes here
- */
- fd->storage_class2 |= stc;
- }
- }
- return t;
-}
-
-void StorageClassDeclaration::addMember(Scope *sc, ScopeDsymbol *sds)
-{
- Dsymbols *d = include(sc);
- if (d)
- {
- Scope *sc2 = newScope(sc);
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- //printf("\taddMember %s to %s\n", s->toChars(), sds->toChars());
- // STClocal needs to be attached before the member is added to the scope (because it influences the parent symbol)
- if (Declaration *decl = s->isDeclaration())
- {
- decl->storage_class |= stc & STClocal;
- if (StorageClassDeclaration *sdecl = s->isStorageClassDeclaration())
- {
- sdecl->stc |= stc & STClocal;
- }
- }
- s->addMember(sc2, sds);
- }
- if (sc2 != sc)
- sc2->pop();
- }
-}
-
-Scope *StorageClassDeclaration::newScope(Scope *sc)
-{
- StorageClass scstc = sc->stc;
-
- /* These sets of storage classes are mutually exclusive,
- * so choose the innermost or most recent one.
- */
- if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest))
- scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest);
- if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared))
- scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared);
- if (stc & (STCconst | STCimmutable | STCmanifest))
- scstc &= ~(STCconst | STCimmutable | STCmanifest);
- if (stc & (STCgshared | STCshared | STCtls))
- scstc &= ~(STCgshared | STCshared | STCtls);
- if (stc & (STCsafe | STCtrusted | STCsystem))
- scstc &= ~(STCsafe | STCtrusted | STCsystem);
- scstc |= stc;
- //printf("scstc = x%llx\n", scstc);
-
- return createNewScope(sc, scstc, sc->linkage, sc->cppmangle,
- sc->protection, sc->explicitProtection, sc->aligndecl,
- sc->inlining);
-}
-
-/********************************* DeprecatedDeclaration ****************************/
-
-DeprecatedDeclaration::DeprecatedDeclaration(Expression *msg, Dsymbols *decl)
- : StorageClassDeclaration(STCdeprecated, decl)
-{
- this->msg = msg;
- this->msgstr = NULL;
-}
-
-Dsymbol *DeprecatedDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new DeprecatedDeclaration(msg->syntaxCopy(), Dsymbol::arraySyntaxCopy(decl));
-}
-
-/**
- * Provides a new scope with `STCdeprecated` and `Scope.depdecl` set
- *
- * Calls `StorageClassDeclaration.newScope` (as it must be called or copied
- * in any function overriding `newScope`), then set the `Scope`'s depdecl.
- *
- * Returns:
- * Always a new scope, to use for this `DeprecatedDeclaration`'s members.
- */
-Scope *DeprecatedDeclaration::newScope(Scope *sc)
-{
- Scope *scx = StorageClassDeclaration::newScope(sc);
- // The enclosing scope is deprecated as well
- if (scx == sc)
- scx = sc->push();
- scx->depdecl = this;
- return scx;
-}
-
-void DeprecatedDeclaration::setScope(Scope *sc)
-{
- //printf("DeprecatedDeclaration::setScope() %p\n", this);
- if (decl)
- Dsymbol::setScope(sc); // for forward reference
- return AttribDeclaration::setScope(sc);
-}
-
-const char *DeprecatedDeclaration::getMessage()
-{
- if (Scope *sc = _scope)
- {
- _scope = NULL;
-
- sc = sc->startCTFE();
- msg = expressionSemantic(msg, sc);
- msg = resolveProperties(sc, msg);
- sc = sc->endCTFE();
- msg = msg->ctfeInterpret();
-
- if (StringExp *se = msg->toStringExp())
- msgstr = (char *)se->string;
- else
- msg->error("compile time constant expected, not `%s`", msg->toChars());
- }
- return msgstr;
-}
-
-/********************************* LinkDeclaration ****************************/
-
-LinkDeclaration::LinkDeclaration(LINK p, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- //printf("LinkDeclaration(linkage = %d, decl = %p)\n", p, decl);
- linkage = (p == LINKsystem) ? target.systemLinkage() : p;
-}
-
-LinkDeclaration *LinkDeclaration::create(LINK p, Dsymbols *decl)
-{
- return new LinkDeclaration(p, decl);
-}
-
-Dsymbol *LinkDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new LinkDeclaration(linkage, Dsymbol::arraySyntaxCopy(decl));
-}
-
-Scope *LinkDeclaration::newScope(Scope *sc)
-{
- return createNewScope(sc, sc->stc, this->linkage, sc->cppmangle,
- sc->protection, sc->explicitProtection, sc->aligndecl,
- sc->inlining);
-}
-
-const char *LinkDeclaration::toChars()
-{
- return "extern ()";
-}
-
-/********************************* CPPMangleDeclaration ****************************/
-
-CPPMangleDeclaration::CPPMangleDeclaration(CPPMANGLE p, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- //printf("CPPMangleDeclaration(cppmangle = %d, decl = %p)\n", p, decl);
- cppmangle = p;
-}
-
-Dsymbol *CPPMangleDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new CPPMangleDeclaration(cppmangle, Dsymbol::arraySyntaxCopy(decl));
-}
-
-Scope *CPPMangleDeclaration::newScope(Scope *sc)
-{
- return createNewScope(sc, sc->stc, LINKcpp, this->cppmangle,
- sc->protection, sc->explicitProtection, sc->aligndecl,
- sc->inlining);
-}
-
-const char *CPPMangleDeclaration::toChars()
-{
- return "extern ()";
-}
-
-/********************************* ProtDeclaration ****************************/
-
-/**
- * Params:
- * loc = source location of attribute token
- * p = protection attribute data
- * decl = declarations which are affected by this protection attribute
- */
-ProtDeclaration::ProtDeclaration(Loc loc, Prot p, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- this->loc = loc;
- this->protection = p;
- this->pkg_identifiers = NULL;
- //printf("decl = %p\n", decl);
-}
-
-/**
- * Params:
- * loc = source location of attribute token
- * pkg_identifiers = list of identifiers for a qualified package name
- * decl = declarations which are affected by this protection attribute
- */
-ProtDeclaration::ProtDeclaration(Loc loc, Identifiers* pkg_identifiers, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- this->loc = loc;
- this->protection.kind = Prot::package_;
- this->protection.pkg = NULL;
- this->pkg_identifiers = pkg_identifiers;
-}
-
-Dsymbol *ProtDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- if (protection.kind == Prot::package_)
- return new ProtDeclaration(this->loc, pkg_identifiers, Dsymbol::arraySyntaxCopy(decl));
- else
- return new ProtDeclaration(this->loc, protection, Dsymbol::arraySyntaxCopy(decl));
-}
-
-Scope *ProtDeclaration::newScope(Scope *sc)
-{
- if (pkg_identifiers)
- dsymbolSemantic(this, sc);
- return createNewScope(sc, sc->stc, sc->linkage, sc->cppmangle,
- this->protection, 1, sc->aligndecl,
- sc->inlining);
-}
-
-void ProtDeclaration::addMember(Scope *sc, ScopeDsymbol *sds)
-{
- if (pkg_identifiers)
- {
- Dsymbol* tmp;
- Package::resolve(pkg_identifiers, &tmp, NULL);
- protection.pkg = tmp ? tmp->isPackage() : NULL;
- pkg_identifiers = NULL;
- }
-
- if (protection.kind == Prot::package_ && protection.pkg && sc->_module)
- {
- Module *m = sc->_module;
-
- // While isAncestorPackageOf does an equality check, the fix for issue 17441 adds a check to see if
- // each package's .isModule() properites are equal.
- //
- // Properties generated from `package(foo)` i.e. protection.pkg have .isModule() == null.
- // This breaks package declarations of the package in question if they are declared in
- // the same package.d file, which _do_ have a module associated with them, and hence a non-null
- // isModule()
- if (!m->isPackage() || !protection.pkg->ident->equals(m->isPackage()->ident))
- {
- Package* pkg = m->parent ? m->parent->isPackage() : NULL;
- if (!pkg || !protection.pkg->isAncestorPackageOf(pkg))
- error("does not bind to one of ancestor packages of module `%s`",
- m->toPrettyChars(true));
- }
- }
-
- return AttribDeclaration::addMember(sc, sds);
-}
-
-const char *ProtDeclaration::kind() const
-{
- return "protection attribute";
-}
-
-const char *ProtDeclaration::toPrettyChars(bool)
-{
- assert(protection.kind > Prot::undefined);
-
- OutBuffer buf;
- buf.writeByte('\'');
- protectionToBuffer(&buf, protection);
- buf.writeByte('\'');
- return buf.extractChars();
-}
-
-/********************************* AlignDeclaration ****************************/
-
-AlignDeclaration::AlignDeclaration(Loc loc, Expression *ealign, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- this->loc = loc;
- this->ealign = ealign;
- this->salign = 0;
-}
-
-Dsymbol *AlignDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new AlignDeclaration(loc,
- ealign ? ealign->syntaxCopy() : NULL,
- Dsymbol::arraySyntaxCopy(decl));
-}
-
-Scope *AlignDeclaration::newScope(Scope *sc)
-{
- return createNewScope(sc, sc->stc, sc->linkage, sc->cppmangle,
- sc->protection, sc->explicitProtection, this,
- sc->inlining);
-}
-
-structalign_t AlignDeclaration::getAlignment(Scope *sc)
-{
- if (salign != 0)
- return salign;
-
- if (!ealign)
- return salign = STRUCTALIGN_DEFAULT;
-
- sc = sc->startCTFE();
- ealign = expressionSemantic(ealign, sc);
- ealign = resolveProperties(sc, ealign);
- sc = sc->endCTFE();
- ealign = ealign->ctfeInterpret();
-
- if (ealign->op == TOKerror)
- return salign = STRUCTALIGN_DEFAULT;
-
- Type *tb = ealign->type->toBasetype();
- sinteger_t n = ealign->toInteger();
-
- if (n < 1 || n & (n - 1) || STRUCTALIGN_DEFAULT < n || !tb->isintegral())
- {
- ::error(loc, "alignment must be an integer positive power of 2, not %s", ealign->toChars());
- return salign = STRUCTALIGN_DEFAULT;
- }
-
- return salign = (structalign_t)n;
-}
-
-/********************************* AnonDeclaration ****************************/
-
-AnonDeclaration::AnonDeclaration(Loc loc, bool isunion, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- this->loc = loc;
- this->isunion = isunion;
- this->sem = 0;
- this->anonoffset = 0;
- this->anonstructsize = 0;
- this->anonalignsize = 0;
-}
-
-Dsymbol *AnonDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new AnonDeclaration(loc, isunion, Dsymbol::arraySyntaxCopy(decl));
-}
-
-void AnonDeclaration::setScope(Scope *sc)
-{
- //printf("AnonDeclaration::setScope() %p\n", this);
- if (decl)
- Dsymbol::setScope(sc);
- AttribDeclaration::setScope(sc);
-}
-
-void AnonDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion)
-{
- //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this);
-
- if (decl)
- {
- /* This works by treating an AnonDeclaration as an aggregate 'member',
- * so in order to place that member we need to compute the member's
- * size and alignment.
- */
-
- size_t fieldstart = ad->fields.length;
-
- /* Hackishly hijack ad's structsize and alignsize fields
- * for use in our fake anon aggregate member.
- */
- unsigned savestructsize = ad->structsize;
- unsigned savealignsize = ad->alignsize;
- ad->structsize = 0;
- ad->alignsize = 0;
-
- unsigned offset = 0;
- for (size_t i = 0; i < decl->length; i++)
- {
- Dsymbol *s = (*decl)[i];
- s->setFieldOffset(ad, &offset, this->isunion);
- if (this->isunion)
- offset = 0;
- }
-
- /* Bugzilla 13613: If the fields in this->members had been already
- * added in ad->fields, just update *poffset for the subsequent
- * field offset calculation.
- */
- if (fieldstart == ad->fields.length)
- {
- ad->structsize = savestructsize;
- ad->alignsize = savealignsize;
- *poffset = ad->structsize;
- return;
- }
-
- anonstructsize = ad->structsize;
- anonalignsize = ad->alignsize;
- ad->structsize = savestructsize;
- ad->alignsize = savealignsize;
-
- // 0 sized structs are set to 1 byte
- // TODO: is this corect hebavior?
- if (anonstructsize == 0)
- {
- anonstructsize = 1;
- anonalignsize = 1;
- }
-
- assert(_scope);
- structalign_t alignment = _scope->alignment();
-
- /* Given the anon 'member's size and alignment,
- * go ahead and place it.
- */
- anonoffset = AggregateDeclaration::placeField(
- poffset,
- anonstructsize, anonalignsize, alignment,
- &ad->structsize, &ad->alignsize,
- isunion);
-
- // Add to the anon fields the base offset of this anonymous aggregate
- //printf("anon fields, anonoffset = %d\n", anonoffset);
- for (size_t i = fieldstart; i < ad->fields.length; i++)
- {
- VarDeclaration *v = ad->fields[i];
- //printf("\t[%d] %s %d\n", i, v->toChars(), v->offset);
- v->offset += anonoffset;
- }
- }
-}
-
-const char *AnonDeclaration::kind() const
-{
- return (isunion ? "anonymous union" : "anonymous struct");
-}
-
-/********************************* PragmaDeclaration ****************************/
-
-PragmaDeclaration::PragmaDeclaration(Loc loc, Identifier *ident, Expressions *args, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- this->loc = loc;
- this->ident = ident;
- this->args = args;
-}
-
-Dsymbol *PragmaDeclaration::syntaxCopy(Dsymbol *s)
-{
- //printf("PragmaDeclaration::syntaxCopy(%s)\n", toChars());
- assert(!s);
- return new PragmaDeclaration(loc, ident,
- Expression::arraySyntaxCopy(args),
- Dsymbol::arraySyntaxCopy(decl));
-}
-
-Scope *PragmaDeclaration::newScope(Scope *sc)
-{
- if (ident == Id::Pinline)
- {
- PINLINE inlining = PINLINEdefault;
- if (!args || args->length == 0)
- inlining = PINLINEdefault;
- else if (args->length != 1)
- {
- error("one boolean expression expected for pragma(inline), not %d", args->length);
- args->setDim(1);
- (*args)[0] = new ErrorExp();
- }
- else
- {
- Expression *e = (*args)[0];
-
- if (e->op != TOKint64 || !e->type->equals(Type::tbool))
- {
- if (e->op != TOKerror)
- {
- error("pragma(inline, true or false) expected, not %s", e->toChars());
- (*args)[0] = new ErrorExp();
- }
- }
- else if (e->isBool(true))
- inlining = PINLINEalways;
- else if (e->isBool(false))
- inlining = PINLINEnever;
- }
-
- return createNewScope(sc, sc->stc, sc->linkage, sc->cppmangle,
- sc->protection, sc->explicitProtection, sc->aligndecl,
- inlining);
- }
- if (ident == Id::printf || ident == Id::scanf)
- {
- Scope *sc2 = sc->push();
-
- if (ident == Id::printf)
- // Override previous setting, never let both be set
- sc2->flags = (sc2->flags & ~SCOPEscanf) | SCOPEprintf;
- else
- sc2->flags = (sc2->flags & ~SCOPEprintf) | SCOPEscanf;
-
- return sc2;
- }
- return sc;
-}
-
-const char *PragmaDeclaration::kind() const
-{
- return "pragma";
-}
-
-/********************************* ConditionalDeclaration ****************************/
-
-ConditionalDeclaration::ConditionalDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl)
- : AttribDeclaration(decl)
-{
- //printf("ConditionalDeclaration::ConditionalDeclaration()\n");
- this->condition = condition;
- this->elsedecl = elsedecl;
-}
-
-Dsymbol *ConditionalDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new ConditionalDeclaration(condition->syntaxCopy(),
- Dsymbol::arraySyntaxCopy(decl),
- Dsymbol::arraySyntaxCopy(elsedecl));
-}
-
-bool ConditionalDeclaration::oneMember(Dsymbol **ps, Identifier *ident)
-{
- //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition->inc);
- if (condition->inc)
- {
- Dsymbols *d = condition->include(NULL) ? decl : elsedecl;
- return Dsymbol::oneMembers(d, ps, ident);
- }
- else
- {
- bool res = (Dsymbol::oneMembers( decl, ps, ident) && *ps == NULL &&
- Dsymbol::oneMembers(elsedecl, ps, ident) && *ps == NULL);
- *ps = NULL;
- return res;
- }
-}
-
-// Decide if 'then' or 'else' code should be included
-
-Dsymbols *ConditionalDeclaration::include(Scope *sc)
-{
- //printf("ConditionalDeclaration::include(sc = %p) _scope = %p\n", sc, _scope);
-
- if (errors)
- return NULL;
-
- assert(condition);
- return condition->include(_scope ? _scope : sc) ? decl : elsedecl;
-}
-
-void ConditionalDeclaration::setScope(Scope *sc)
-{
- Dsymbols *d = include(sc);
-
- //printf("\tConditionalDeclaration::setScope '%s', d = %p\n",toChars(), d);
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->setScope(sc);
- }
- }
-}
-
-void ConditionalDeclaration::addComment(const utf8_t *comment)
-{
- /* Because addComment is called by the parser, if we called
- * include() it would define a version before it was used.
- * But it's no problem to drill down to both decl and elsedecl,
- * so that's the workaround.
- */
-
- if (comment)
- {
- Dsymbols *d = decl;
-
- for (int j = 0; j < 2; j++)
- {
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- //printf("ConditionalDeclaration::addComment %s\n", s->toChars());
- s->addComment(comment);
- }
- }
- d = elsedecl;
- }
- }
-}
-
-/***************************** StaticIfDeclaration ****************************/
-
-StaticIfDeclaration::StaticIfDeclaration(Condition *condition,
- Dsymbols *decl, Dsymbols *elsedecl)
- : ConditionalDeclaration(condition, decl, elsedecl)
-{
- //printf("StaticIfDeclaration::StaticIfDeclaration()\n");
- scopesym = NULL;
- addisdone = false;
- onStack = false;
-}
-
-Dsymbol *StaticIfDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new StaticIfDeclaration(condition->syntaxCopy(),
- Dsymbol::arraySyntaxCopy(decl),
- Dsymbol::arraySyntaxCopy(elsedecl));
-}
-
-/****************************************
- * Different from other AttribDeclaration subclasses, include() call requires
- * the completion of addMember and setScope phases.
- */
-Dsymbols *StaticIfDeclaration::include(Scope *sc)
-{
- //printf("StaticIfDeclaration::include(sc = %p) _scope = %p\n", sc, _scope);
-
- if (errors || onStack)
- return NULL;
- onStack = true;
- Dsymbols *d;
-
- if (condition->inc == 0)
- {
- assert(scopesym); // addMember is already done
- assert(_scope); // setScope is already done
-
- d = ConditionalDeclaration::include(_scope);
-
- if (d && !addisdone)
- {
- // Add members lazily.
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->addMember(_scope, scopesym);
- }
-
- // Set the member scopes lazily.
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->setScope(_scope);
- }
-
- addisdone = true;
- }
- onStack = false;
- return d;
- }
- else
- {
- d = ConditionalDeclaration::include(sc);
- onStack = false;
- return d;
- }
-}
-
-void StaticIfDeclaration::addMember(Scope *, ScopeDsymbol *sds)
-{
- //printf("StaticIfDeclaration::addMember() '%s'\n", toChars());
- /* This is deferred until the condition evaluated later (by the include() call),
- * so that expressions in the condition can refer to declarations
- * in the same scope, such as:
- *
- * template Foo(int i)
- * {
- * const int j = i + 1;
- * static if (j == 3)
- * const int k;
- * }
- */
- this->scopesym = sds;
-}
-
-void StaticIfDeclaration::importAll(Scope *)
-{
- // do not evaluate condition before semantic pass
-}
-
-void StaticIfDeclaration::setScope(Scope *sc)
-{
- // do not evaluate condition before semantic pass
-
- // But do set the scope, in case we need it for forward referencing
- Dsymbol::setScope(sc);
-}
-
-const char *StaticIfDeclaration::kind() const
-{
- return "static if";
-}
-
-/***************************** StaticForeachDeclaration ***********************/
-
-/* Static foreach at declaration scope, like:
- * static foreach (i; [0, 1, 2]){ }
- */
-
-StaticForeachDeclaration::StaticForeachDeclaration(StaticForeach *sfe, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- this->sfe = sfe;
- this->scopesym = NULL;
- this->onStack = false;
- this->cached = false;
- this->cache = NULL;
-}
-
-Dsymbol *StaticForeachDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new StaticForeachDeclaration(
- sfe->syntaxCopy(),
- Dsymbol::arraySyntaxCopy(decl));
-}
-
-bool StaticForeachDeclaration::oneMember(Dsymbol **ps, Identifier *ident)
-{
- // Required to support IFTI on a template that contains a
- // `static foreach` declaration. `super.oneMember` calls
- // include with a `null` scope. As `static foreach` requires
- // the scope for expansion, `oneMember` can only return a
- // precise result once `static foreach` has been expanded.
- if (cached)
- {
- return AttribDeclaration::oneMember(ps, ident);
- }
- *ps = NULL; // a `static foreach` declaration may in general expand to multiple symbols
- return false;
-}
-
-Dsymbols *StaticForeachDeclaration::include(Scope *)
-{
- if (errors || onStack)
- return NULL;
- if (cached)
- {
- assert(!onStack);
- return cache;
- }
- onStack = true;
-
- if (_scope)
- {
- staticForeachPrepare(sfe, _scope); // lower static foreach aggregate
- }
- if (!staticForeachReady(sfe))
- {
- onStack = false;
- return NULL; // TODO: ok?
- }
-
- // expand static foreach
- Dsymbols *d = makeTupleForeachStaticDecl(_scope, sfe->aggrfe, decl, sfe->needExpansion);
- if (d) // process generated declarations
- {
- // Add members lazily.
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->addMember(_scope, scopesym);
- }
- // Set the member scopes lazily.
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->setScope(_scope);
- }
- }
- onStack = false;
- cached = true;
- cache = d;
- return d;
-}
-
-void StaticForeachDeclaration::addMember(Scope *, ScopeDsymbol *sds)
-{
- // used only for caching the enclosing symbol
- this->scopesym = sds;
-}
-
-void StaticForeachDeclaration::addComment(const utf8_t *)
-{
- // do nothing
- // change this to give semantics to documentation comments on static foreach declarations
-}
-
-void StaticForeachDeclaration::setScope(Scope *sc)
-{
- // do not evaluate condition before semantic pass
- // But do set the scope, in case we need it for forward referencing
- Dsymbol::setScope(sc);
-}
-
-void StaticForeachDeclaration::importAll(Scope *)
-{
- // do not evaluate aggregate before semantic pass
-}
-
-const char *StaticForeachDeclaration::kind() const
-{
- return "static foreach";
-}
-
-/***********************************************************
- * Collection of declarations that stores foreach index variables in a
- * local symbol table. Other symbols declared within are forwarded to
- * another scope, like:
- *
- * static foreach (i; 0 .. 10) // loop variables for different indices do not conflict.
- * { // this body is expanded into 10 ForwardingAttribDeclarations, where `i` has storage class STClocal
- * mixin("enum x" ~ to!string(i) ~ " = i"); // ok, can access current loop variable
- * }
- *
- * static foreach (i; 0.. 10)
- * {
- * pragma(msg, mixin("x" ~ to!string(i))); // ok, all 10 symbols are visible as they were forwarded to the global scope
- * }
- *
- * static assert (!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop
- *
- * A StaticForeachDeclaration generates one
- * ForwardingAttribDeclaration for each expansion of its body. The
- * AST of the ForwardingAttribDeclaration contains both the `static
- * foreach` variables and the respective copy of the `static foreach`
- * body. The functionality is achieved by using a
- * ForwardingScopeDsymbol as the parent symbol for the generated
- * declarations.
- */
-
-ForwardingAttribDeclaration::ForwardingAttribDeclaration(Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- sym = new ForwardingScopeDsymbol(NULL);
- sym->symtab = new DsymbolTable();
-}
-
-/**************************************
- * Use the ForwardingScopeDsymbol as the parent symbol for members.
- */
-Scope *ForwardingAttribDeclaration::newScope(Scope *sc)
-{
- return sc->push(sym);
-}
-
-/***************************************
- * Lazily initializes the scope to forward to.
- */
-void ForwardingAttribDeclaration::addMember(Scope *sc, ScopeDsymbol *sds)
-{
- parent = sym->parent = sym->forward = sds;
- return AttribDeclaration::addMember(sc, sym);
-}
-
-/***************************** CompileDeclaration *****************************/
-
-// These are mixin declarations, like mixin("int x");
-
-CompileDeclaration::CompileDeclaration(Loc loc, Expressions *exps)
- : AttribDeclaration(NULL)
-{
- //printf("CompileDeclaration(loc = %d)\n", loc.linnum);
- this->loc = loc;
- this->exps = exps;
- this->scopesym = NULL;
- this->compiled = false;
-}
-
-Dsymbol *CompileDeclaration::syntaxCopy(Dsymbol *)
-{
- //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars());
- return new CompileDeclaration(loc, Expression::arraySyntaxCopy(exps));
-}
-
-void CompileDeclaration::addMember(Scope *, ScopeDsymbol *sds)
-{
- //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum);
- this->scopesym = sds;
-}
-
-void CompileDeclaration::setScope(Scope *sc)
-{
- Dsymbol::setScope(sc);
-}
-
-const char *CompileDeclaration::kind() const
-{
- return "mixin";
-}
-
-/***************************** UserAttributeDeclaration *****************************/
-
-UserAttributeDeclaration::UserAttributeDeclaration(Expressions *atts, Dsymbols *decl)
- : AttribDeclaration(decl)
-{
- //printf("UserAttributeDeclaration()\n");
- this->atts = atts;
-}
-
-Dsymbol *UserAttributeDeclaration::syntaxCopy(Dsymbol *s)
-{
- //printf("UserAttributeDeclaration::syntaxCopy('%s')\n", toChars());
- assert(!s);
- return new UserAttributeDeclaration(
- Expression::arraySyntaxCopy(this->atts),
- Dsymbol::arraySyntaxCopy(decl));
-}
-
-Scope *UserAttributeDeclaration::newScope(Scope *sc)
-{
- Scope *sc2 = sc;
- if (atts && atts->length)
- {
- // create new one for changes
- sc2 = sc->copy();
- sc2->userAttribDecl = this;
- }
- return sc2;
-}
-
-void UserAttributeDeclaration::setScope(Scope *sc)
-{
- //printf("UserAttributeDeclaration::setScope() %p\n", this);
- if (decl)
- Dsymbol::setScope(sc); // for forward reference of UDAs
-
- return AttribDeclaration::setScope(sc);
-}
-
-void udaExpressionEval(Scope *sc, Expressions *exps)
-{
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *e = (*exps)[i];
- if (e)
- {
- e = expressionSemantic(e, sc);
- if (definitelyValueParameter(e))
- e = e->ctfeInterpret();
- if (e->op == TOKtuple)
- {
- TupleExp *te = (TupleExp *)e;
- udaExpressionEval(sc, te->exps);
- }
- (*exps)[i] = e;
- }
- }
-}
-
-Expressions *UserAttributeDeclaration::concat(Expressions *udas1, Expressions *udas2)
-{
- Expressions *udas;
- if (!udas1 || udas1->length == 0)
- udas = udas2;
- else if (!udas2 || udas2->length == 0)
- udas = udas1;
- else
- {
- /* Create a new tuple that combines them
- * (do not append to left operand, as this is a copy-on-write operation)
- */
- udas = new Expressions();
- udas->push(new TupleExp(Loc(), udas1));
- udas->push(new TupleExp(Loc(), udas2));
- }
- return udas;
-}
-
-Expressions *UserAttributeDeclaration::getAttributes()
-{
- if (Scope *sc = _scope)
- {
- _scope = NULL;
- arrayExpressionSemantic(atts, sc);
- }
-
- Expressions *exps = new Expressions();
- if (userAttribDecl)
- exps->push(new TupleExp(Loc(), userAttribDecl->getAttributes()));
- if (atts && atts->length)
- exps->push(new TupleExp(Loc(), atts));
-
- return exps;
-}
-
-const char *UserAttributeDeclaration::kind() const
-{
- return "UserAttribute";
-}
diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d
new file mode 100644
index 00000000000..ae8f65b6daa
--- /dev/null
+++ b/gcc/d/dmd/attrib.d
@@ -0,0 +1,1518 @@
+/**
+ * Defines declarations of various attributes.
+ *
+ * The term 'attribute' refers to things that can apply to a larger scope than a single declaration.
+ * Among them are:
+ * - Alignment (`align(8)`)
+ * - User defined attributes (`@UDA`)
+ * - Function Attributes (`@safe`)
+ * - Storage classes (`static`, `__gshared`)
+ * - Mixin declarations (`mixin("int x;")`)
+ * - Conditional compilation (`static if`, `static foreach`)
+ * - Linkage (`extern(C)`)
+ * - Anonymous structs / unions
+ * - Protection (`private`, `public`)
+ * - Deprecated declarations (`@deprecated`)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/attrib.d, _attrib.d)
+ * Documentation: https://dlang.org/phobos/dmd_attrib.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/attrib.d
+ */
+
+module dmd.attrib;
+
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.cond;
+import dmd.declaration;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem : dsymbolSemantic;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen : visibilityToBuffer;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.objc; // for objc.addSymbols
+import dmd.root.outbuffer;
+import dmd.target; // for target.systemLinkage
+import dmd.tokens;
+import dmd.visitor;
+
+/***********************************************************
+ * Abstract attribute applied to Dsymbol's used as a common
+ * ancestor for storage classes (StorageClassDeclaration),
+ * linkage (LinkageDeclaration) and others.
+ */
+extern (C++) abstract class AttribDeclaration : Dsymbol
+{
+ Dsymbols* decl; /// Dsymbol's affected by this AttribDeclaration
+
+ extern (D) this(Dsymbols* decl)
+ {
+ this.decl = decl;
+ }
+
+ extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl)
+ {
+ super(loc, ident);
+ this.decl = decl;
+ }
+
+ Dsymbols* include(Scope* sc)
+ {
+ if (errors)
+ return null;
+
+ return decl;
+ }
+
+ /****************************************
+ * Create a new scope if one or more given attributes
+ * are different from the sc's.
+ * If the returned scope != sc, the caller should pop
+ * the scope after it used.
+ */
+ extern (D) static Scope* createNewScope(Scope* sc, StorageClass stc, LINK linkage,
+ CPPMANGLE cppmangle, Visibility visibility, int explicitVisibility,
+ AlignDeclaration aligndecl, PragmaDeclaration inlining)
+ {
+ Scope* sc2 = sc;
+ if (stc != sc.stc ||
+ linkage != sc.linkage ||
+ cppmangle != sc.cppmangle ||
+ explicitVisibility != sc.explicitVisibility ||
+ visibility != sc.visibility ||
+ aligndecl !is sc.aligndecl ||
+ inlining != sc.inlining)
+ {
+ // create new one for changes
+ sc2 = sc.copy();
+ sc2.stc = stc;
+ sc2.linkage = linkage;
+ sc2.cppmangle = cppmangle;
+ sc2.visibility = visibility;
+ sc2.explicitVisibility = explicitVisibility;
+ sc2.aligndecl = aligndecl;
+ sc2.inlining = inlining;
+ }
+ return sc2;
+ }
+
+ /****************************************
+ * A hook point to supply scope for members.
+ * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this.
+ */
+ Scope* newScope(Scope* sc)
+ {
+ return sc;
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ Dsymbols* d = include(sc);
+ if (d)
+ {
+ Scope* sc2 = newScope(sc);
+ d.foreachDsymbol( s => s.addMember(sc2, sds) );
+ if (sc2 != sc)
+ sc2.pop();
+ }
+ }
+
+ override void setScope(Scope* sc)
+ {
+ Dsymbols* d = include(sc);
+ //printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d);
+ if (d)
+ {
+ Scope* sc2 = newScope(sc);
+ d.foreachDsymbol( s => s.setScope(sc2) );
+ if (sc2 != sc)
+ sc2.pop();
+ }
+ }
+
+ override void importAll(Scope* sc)
+ {
+ Dsymbols* d = include(sc);
+ //printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d);
+ if (d)
+ {
+ Scope* sc2 = newScope(sc);
+ d.foreachDsymbol( s => s.importAll(sc2) );
+ if (sc2 != sc)
+ sc2.pop();
+ }
+ }
+
+ override void addComment(const(char)* comment)
+ {
+ //printf("AttribDeclaration::addComment %s\n", comment);
+ if (comment)
+ {
+ include(null).foreachDsymbol( s => s.addComment(comment) );
+ }
+ }
+
+ override const(char)* kind() const
+ {
+ return "attribute";
+ }
+
+ override bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ Dsymbols* d = include(null);
+ return Dsymbol.oneMembers(d, ps, ident);
+ }
+
+ override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
+ {
+ include(null).foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) );
+ }
+
+ override final bool hasPointers()
+ {
+ return include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
+ }
+
+ override final bool hasStaticCtorOrDtor()
+ {
+ return include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0;
+ }
+
+ override final void checkCtorConstInit()
+ {
+ include(null).foreachDsymbol( s => s.checkCtorConstInit() );
+ }
+
+ /****************************************
+ */
+ override final void addLocalClass(ClassDeclarations* aclasses)
+ {
+ include(null).foreachDsymbol( s => s.addLocalClass(aclasses) );
+ }
+
+ override final void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
+ {
+ objc.addSymbols(this, classes, categories);
+ }
+
+ override final inout(AttribDeclaration) isAttribDeclaration() inout pure @safe
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Storage classes applied to Dsymbols, e.g. `const int i;`
+ *
+ * <stc> <decl...>
+ */
+extern (C++) class StorageClassDeclaration : AttribDeclaration
+{
+ StorageClass stc;
+
+ extern (D) this(StorageClass stc, Dsymbols* decl)
+ {
+ super(decl);
+ this.stc = stc;
+ }
+
+ override StorageClassDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new StorageClassDeclaration(stc, Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ override Scope* newScope(Scope* sc)
+ {
+ StorageClass scstc = sc.stc;
+ /* These sets of storage classes are mutually exclusive,
+ * so choose the innermost or most recent one.
+ */
+ if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest))
+ scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest);
+ if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.tls | STC.manifest | STC.gshared))
+ scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.tls | STC.manifest | STC.gshared);
+ if (stc & (STC.const_ | STC.immutable_ | STC.manifest))
+ scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest);
+ if (stc & (STC.gshared | STC.shared_ | STC.tls))
+ scstc &= ~(STC.gshared | STC.shared_ | STC.tls);
+ if (stc & (STC.safe | STC.trusted | STC.system))
+ scstc &= ~(STC.safe | STC.trusted | STC.system);
+ scstc |= stc;
+ //printf("scstc = x%llx\n", scstc);
+ return createNewScope(sc, scstc, sc.linkage, sc.cppmangle,
+ sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining);
+ }
+
+ override final bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ bool t = Dsymbol.oneMembers(decl, ps, ident);
+ if (t && *ps)
+ {
+ /* This is to deal with the following case:
+ * struct Tick {
+ * template to(T) { const T to() { ... } }
+ * }
+ * For eponymous function templates, the 'const' needs to get attached to 'to'
+ * before the semantic analysis of 'to', so that template overloading based on the
+ * 'this' pointer can be successful.
+ */
+ FuncDeclaration fd = (*ps).isFuncDeclaration();
+ if (fd)
+ {
+ /* Use storage_class2 instead of storage_class otherwise when we do .di generation
+ * we'll wind up with 'const const' rather than 'const'.
+ */
+ /* Don't think we need to worry about mutually exclusive storage classes here
+ */
+ fd.storage_class2 |= stc;
+ }
+ }
+ return t;
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ Dsymbols* d = include(sc);
+ if (d)
+ {
+ Scope* sc2 = newScope(sc);
+
+ d.foreachDsymbol( (s)
+ {
+ //printf("\taddMember %s to %s\n", s.toChars(), sds.toChars());
+ // STC.local needs to be attached before the member is added to the scope (because it influences the parent symbol)
+ if (auto decl = s.isDeclaration())
+ {
+ decl.storage_class |= stc & STC.local;
+ if (auto sdecl = s.isStorageClassDeclaration()) // TODO: why is this not enough to deal with the nested case?
+ {
+ sdecl.stc |= stc & STC.local;
+ }
+ }
+ s.addMember(sc2, sds);
+ });
+
+ if (sc2 != sc)
+ sc2.pop();
+ }
+
+ }
+
+ override inout(StorageClassDeclaration) isStorageClassDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Deprecation with an additional message applied to Dsymbols,
+ * e.g. `deprecated("Superseeded by foo") int bar;`.
+ * (Note that `deprecated int bar;` is currently represented as a
+ * StorageClassDeclaration with STC.deprecated_)
+ *
+ * `deprecated(<msg>) <decl...>`
+ */
+extern (C++) final class DeprecatedDeclaration : StorageClassDeclaration
+{
+ Expression msg; /// deprecation message
+ const(char)* msgstr; /// cached string representation of msg
+
+ extern (D) this(Expression msg, Dsymbols* decl)
+ {
+ super(STC.deprecated_, decl);
+ this.msg = msg;
+ }
+
+ override DeprecatedDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new DeprecatedDeclaration(msg.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ /**
+ * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set
+ *
+ * Calls `StorageClassDeclaration.newScope` (as it must be called or copied
+ * in any function overriding `newScope`), then set the `Scope`'s depdecl.
+ *
+ * Returns:
+ * Always a new scope, to use for this `DeprecatedDeclaration`'s members.
+ */
+ override Scope* newScope(Scope* sc)
+ {
+ auto scx = super.newScope(sc);
+ // The enclosing scope is deprecated as well
+ if (scx == sc)
+ scx = sc.push();
+ scx.depdecl = this;
+ return scx;
+ }
+
+ override void setScope(Scope* sc)
+ {
+ //printf("DeprecatedDeclaration::setScope() %p\n", this);
+ if (decl)
+ Dsymbol.setScope(sc); // for forward reference
+ return AttribDeclaration.setScope(sc);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Linkage attribute applied to Dsymbols, e.g.
+ * `extern(C) void foo()`.
+ *
+ * `extern(<linkage>) <decl...>`
+ */
+extern (C++) final class LinkDeclaration : AttribDeclaration
+{
+ LINK linkage; /// either explicitly set or `default_`
+
+ extern (D) this(const ref Loc loc, LINK linkage, Dsymbols* decl)
+ {
+ super(loc, null, decl);
+ //printf("LinkDeclaration(linkage = %d, decl = %p)\n", linkage, decl);
+ this.linkage = (linkage == LINK.system) ? target.systemLinkage() : linkage;
+ }
+
+ static LinkDeclaration create(const ref Loc loc, LINK p, Dsymbols* decl)
+ {
+ return new LinkDeclaration(loc, p, decl);
+ }
+
+ override LinkDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new LinkDeclaration(loc, linkage, Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ override Scope* newScope(Scope* sc)
+ {
+ return createNewScope(sc, sc.stc, this.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility,
+ sc.aligndecl, sc.inlining);
+ }
+
+ override const(char)* toChars() const
+ {
+ return toString().ptr;
+ }
+
+ extern(D) override const(char)[] toString() const
+ {
+ return "extern ()";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Attribute declaring whether an external aggregate should be mangled as
+ * a struct or class in C++, e.g. `extern(C++, struct) class C { ... }`.
+ * This is required for correct name mangling on MSVC targets,
+ * see cppmanglewin.d for details.
+ *
+ * `extern(C++, <cppmangle>) <decl...>`
+ */
+extern (C++) final class CPPMangleDeclaration : AttribDeclaration
+{
+ CPPMANGLE cppmangle;
+
+ extern (D) this(const ref Loc loc, CPPMANGLE cppmangle, Dsymbols* decl)
+ {
+ super(loc, null, decl);
+ //printf("CPPMangleDeclaration(cppmangle = %d, decl = %p)\n", cppmangle, decl);
+ this.cppmangle = cppmangle;
+ }
+
+ override CPPMangleDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new CPPMangleDeclaration(loc, cppmangle, Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ override Scope* newScope(Scope* sc)
+ {
+ return createNewScope(sc, sc.stc, LINK.cpp, cppmangle, sc.visibility, sc.explicitVisibility,
+ sc.aligndecl, sc.inlining);
+ }
+
+ override void setScope(Scope* sc)
+ {
+ if (decl)
+ Dsymbol.setScope(sc); // for forward reference
+ return AttribDeclaration.setScope(sc);
+ }
+
+ override const(char)* toChars() const
+ {
+ return toString().ptr;
+ }
+
+ extern(D) override const(char)[] toString() const
+ {
+ return "extern ()";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/**
+ * A node to represent an `extern(C++)` namespace attribute
+ *
+ * There are two ways to declarate a symbol as member of a namespace:
+ * `Nspace` and `CPPNamespaceDeclaration`.
+ * The former creates a scope for the symbol, and inject them in the
+ * parent scope at the same time.
+ * The later, this class, has no semantic implications and is only
+ * used for mangling.
+ * Additionally, this class allows one to use reserved identifiers
+ * (D keywords) in the namespace.
+ *
+ * A `CPPNamespaceDeclaration` can be created from an `Identifier`
+ * (already resolved) or from an `Expression`, which is CTFE-ed
+ * and can be either a `TupleExp`, in which can additional
+ * `CPPNamespaceDeclaration` nodes are created, or a `StringExp`.
+ *
+ * Note that this class, like `Nspace`, matches only one identifier
+ * part of a namespace. For the namespace `"foo::bar"`,
+ * the will be a `CPPNamespaceDeclaration` with its `ident`
+ * set to `"bar"`, and its `namespace` field pointing to another
+ * `CPPNamespaceDeclaration` with its `ident` set to `"foo"`.
+ */
+extern (C++) final class CPPNamespaceDeclaration : AttribDeclaration
+{
+ /// CTFE-able expression, resolving to `TupleExp` or `StringExp`
+ Expression exp;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl)
+ {
+ super(loc, ident, decl);
+ }
+
+ extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl)
+ {
+ super(loc, null, decl);
+ this.exp = exp;
+ }
+
+ extern (D) this(const ref Loc loc, Identifier ident, Expression exp, Dsymbols* decl,
+ CPPNamespaceDeclaration parent)
+ {
+ super(loc, ident, decl);
+ this.exp = exp;
+ this.cppnamespace = parent;
+ }
+
+ override CPPNamespaceDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new CPPNamespaceDeclaration(
+ this.loc, this.ident, this.exp, Dsymbol.arraySyntaxCopy(this.decl), this.cppnamespace);
+ }
+
+ /**
+ * Returns:
+ * A copy of the parent scope, with `this` as `namespace` and C++ linkage
+ */
+ override Scope* newScope(Scope* sc)
+ {
+ auto scx = sc.copy();
+ scx.linkage = LINK.cpp;
+ scx.namespace = this;
+ return scx;
+ }
+
+ override const(char)* toChars() const
+ {
+ return toString().ptr;
+ }
+
+ extern(D) override const(char)[] toString() const
+ {
+ return "extern (C++, `namespace`)";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ override inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return this; }
+}
+
+/***********************************************************
+ * Visibility declaration for Dsymbols, e.g. `public int i;`
+ *
+ * `<visibility> <decl...>` or
+ * `package(<pkg_identifiers>) <decl...>` if `pkg_identifiers !is null`
+ */
+extern (C++) final class VisibilityDeclaration : AttribDeclaration
+{
+ Visibility visibility; /// the visibility
+ Identifier[] pkg_identifiers; /// identifiers for `package(foo.bar)` or null
+
+ /**
+ * Params:
+ * loc = source location of attribute token
+ * visibility = visibility attribute data
+ * decl = declarations which are affected by this visibility attribute
+ */
+ extern (D) this(const ref Loc loc, Visibility visibility, Dsymbols* decl)
+ {
+ super(loc, null, decl);
+ this.visibility = visibility;
+ //printf("decl = %p\n", decl);
+ }
+
+ /**
+ * Params:
+ * loc = source location of attribute token
+ * pkg_identifiers = list of identifiers for a qualified package name
+ * decl = declarations which are affected by this visibility attribute
+ */
+ extern (D) this(const ref Loc loc, Identifier[] pkg_identifiers, Dsymbols* decl)
+ {
+ super(loc, null, decl);
+ this.visibility.kind = Visibility.Kind.package_;
+ this.pkg_identifiers = pkg_identifiers;
+ if (pkg_identifiers.length > 0)
+ {
+ Dsymbol tmp;
+ Package.resolve(pkg_identifiers, &tmp, null);
+ visibility.pkg = tmp ? tmp.isPackage() : null;
+ }
+ }
+
+ override VisibilityDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+
+ if (visibility.kind == Visibility.Kind.package_)
+ return new VisibilityDeclaration(this.loc, pkg_identifiers, Dsymbol.arraySyntaxCopy(decl));
+ else
+ return new VisibilityDeclaration(this.loc, visibility, Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ override Scope* newScope(Scope* sc)
+ {
+ if (pkg_identifiers)
+ dsymbolSemantic(this, sc);
+ return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, this.visibility, 1, sc.aligndecl, sc.inlining);
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ if (pkg_identifiers)
+ {
+ Dsymbol tmp;
+ Package.resolve(pkg_identifiers, &tmp, null);
+ visibility.pkg = tmp ? tmp.isPackage() : null;
+ pkg_identifiers = null;
+ }
+ if (visibility.kind == Visibility.Kind.package_ && visibility.pkg && sc._module)
+ {
+ Module m = sc._module;
+
+ // While isAncestorPackageOf does an equality check, the fix for issue 17441 adds a check to see if
+ // each package's .isModule() properites are equal.
+ //
+ // Properties generated from `package(foo)` i.e. visibility.pkg have .isModule() == null.
+ // This breaks package declarations of the package in question if they are declared in
+ // the same package.d file, which _do_ have a module associated with them, and hence a non-null
+ // isModule()
+ if (!m.isPackage() || !visibility.pkg.ident.equals(m.isPackage().ident))
+ {
+ Package pkg = m.parent ? m.parent.isPackage() : null;
+ if (!pkg || !visibility.pkg.isAncestorPackageOf(pkg))
+ error("does not bind to one of ancestor packages of module `%s`", m.toPrettyChars(true));
+ }
+ }
+ return AttribDeclaration.addMember(sc, sds);
+ }
+
+ override const(char)* kind() const
+ {
+ return "visibility attribute";
+ }
+
+ override const(char)* toPrettyChars(bool)
+ {
+ assert(visibility.kind > Visibility.Kind.undefined);
+ OutBuffer buf;
+ visibilityToBuffer(&buf, visibility);
+ return buf.extractChars();
+ }
+
+ override inout(VisibilityDeclaration) isVisibilityDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Alignment attribute for aggregates, members and variables.
+ *
+ * `align(<ealign>) <decl...>` or
+ * `align <decl...>` if `ealign` is null
+ */
+extern (C++) final class AlignDeclaration : AttribDeclaration
+{
+ Expressions* exps; /// Expression(s) yielding the desired alignment,
+ /// the largest value wins
+ enum structalign_t UNKNOWN = 0; /// alignment not yet computed
+ static assert(STRUCTALIGN_DEFAULT != UNKNOWN);
+
+ /// the actual alignment, `UNKNOWN` until it's either set to the value of `ealign`
+ /// or `STRUCTALIGN_DEFAULT` if `ealign` is null ( / an error ocurred)
+ structalign_t salign = UNKNOWN;
+
+
+ extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl)
+ {
+ super(loc, null, decl);
+ if (exp)
+ {
+ if (!exps)
+ exps = new Expressions();
+ exps.push(exp);
+ }
+ }
+
+ extern (D) this(const ref Loc loc, Expressions* exps, Dsymbols* decl)
+ {
+ super(loc, null, decl);
+ this.exps = exps;
+ }
+
+ override AlignDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new AlignDeclaration(loc,
+ Expression.arraySyntaxCopy(exps),
+ Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ override Scope* newScope(Scope* sc)
+ {
+ return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, this, sc.inlining);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * An anonymous struct/union (defined by `isunion`).
+ */
+extern (C++) final class AnonDeclaration : AttribDeclaration
+{
+ bool isunion; /// whether it's a union
+ int sem; /// 1 if successful semantic()
+ uint anonoffset; /// offset of anonymous struct
+ uint anonstructsize; /// size of anonymous struct
+ uint anonalignsize; /// size of anonymous struct for alignment purposes
+
+ extern (D) this(const ref Loc loc, bool isunion, Dsymbols* decl)
+ {
+ super(loc, null, decl);
+ this.isunion = isunion;
+ }
+
+ override AnonDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new AnonDeclaration(loc, isunion, Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ override void setScope(Scope* sc)
+ {
+ if (decl)
+ Dsymbol.setScope(sc);
+ return AttribDeclaration.setScope(sc);
+ }
+
+ override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
+ {
+ //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this);
+ if (decl)
+ {
+ /* This works by treating an AnonDeclaration as an aggregate 'member',
+ * so in order to place that member we need to compute the member's
+ * size and alignment.
+ */
+ size_t fieldstart = ad.fields.dim;
+
+ /* Hackishly hijack ad's structsize and alignsize fields
+ * for use in our fake anon aggregate member.
+ */
+ uint savestructsize = ad.structsize;
+ uint savealignsize = ad.alignsize;
+ ad.structsize = 0;
+ ad.alignsize = 0;
+
+ FieldState fs;
+ decl.foreachDsymbol( (s)
+ {
+ s.setFieldOffset(ad, fs, this.isunion);
+ if (this.isunion)
+ fs.offset = 0;
+ });
+
+ /* https://issues.dlang.org/show_bug.cgi?id=13613
+ * If the fields in this.members had been already
+ * added in ad.fields, just update *poffset for the subsequent
+ * field offset calculation.
+ */
+ if (fieldstart == ad.fields.dim)
+ {
+ ad.structsize = savestructsize;
+ ad.alignsize = savealignsize;
+ fieldState.offset = ad.structsize;
+ return;
+ }
+
+ anonstructsize = ad.structsize;
+ anonalignsize = ad.alignsize;
+ ad.structsize = savestructsize;
+ ad.alignsize = savealignsize;
+
+ // 0 sized structs are set to 1 byte
+ if (anonstructsize == 0)
+ {
+ anonstructsize = 1;
+ anonalignsize = 1;
+ }
+
+ assert(_scope);
+ auto alignment = _scope.alignment();
+
+ /* Given the anon 'member's size and alignment,
+ * go ahead and place it.
+ */
+ anonoffset = AggregateDeclaration.placeField(
+ &fieldState.offset,
+ anonstructsize, anonalignsize, alignment,
+ &ad.structsize, &ad.alignsize,
+ isunion);
+
+ // Add to the anon fields the base offset of this anonymous aggregate
+ //printf("anon fields, anonoffset = %d\n", anonoffset);
+ foreach (const i; fieldstart .. ad.fields.dim)
+ {
+ VarDeclaration v = ad.fields[i];
+ //printf("\t[%d] %s %d\n", i, v.toChars(), v.offset);
+ v.offset += anonoffset;
+ }
+ }
+ }
+
+ override const(char)* kind() const
+ {
+ return (isunion ? "anonymous union" : "anonymous struct");
+ }
+
+ override inout(AnonDeclaration) isAnonDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Pragma applied to Dsymbols, e.g. `pragma(inline, true) void foo`,
+ * but not PragmaStatement's like `pragma(msg, "hello");`.
+ *
+ * pragma(<ident>, <args>)
+ */
+extern (C++) final class PragmaDeclaration : AttribDeclaration
+{
+ Expressions* args; /// parameters of this pragma
+
+ extern (D) this(const ref Loc loc, Identifier ident, Expressions* args, Dsymbols* decl)
+ {
+ super(loc, ident, decl);
+ this.args = args;
+ }
+
+ override PragmaDeclaration syntaxCopy(Dsymbol s)
+ {
+ //printf("PragmaDeclaration::syntaxCopy(%s)\n", toChars());
+ assert(!s);
+ return new PragmaDeclaration(loc, ident, Expression.arraySyntaxCopy(args), Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ override Scope* newScope(Scope* sc)
+ {
+ if (ident == Id.Pinline)
+ {
+ // We keep track of this pragma inside scopes,
+ // then it's evaluated on demand in function semantic
+ return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, this);
+ }
+ if (ident == Id.printf || ident == Id.scanf)
+ {
+ auto sc2 = sc.push();
+
+ if (ident == Id.printf)
+ // Override previous setting, never let both be set
+ sc2.flags = (sc2.flags & ~SCOPE.scanf) | SCOPE.printf;
+ else
+ sc2.flags = (sc2.flags & ~SCOPE.printf) | SCOPE.scanf;
+
+ return sc2;
+ }
+ return sc;
+ }
+
+ PINLINE evalPragmaInline(Scope* sc)
+ {
+ if (!args || args.dim == 0)
+ return PINLINE.default_;
+
+ Expression e = (*args)[0];
+ if (!e.type)
+ {
+
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = e.ctfeInterpret();
+ e = e.toBoolean(sc);
+ if (e.isErrorExp())
+ error("pragma(`inline`, `true` or `false`) expected, not `%s`", (*args)[0].toChars());
+ (*args)[0] = e;
+ }
+
+ if (e.isBool(true))
+ return PINLINE.always;
+ else if (e.isBool(false))
+ return PINLINE.never;
+ else
+ return PINLINE.default_;
+ }
+
+ override const(char)* kind() const
+ {
+ return "pragma";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * A conditional compilation declaration, used for `version`
+ * / `debug` and specialized for `static if`.
+ *
+ * <condition> { <decl...> } else { <elsedecl> }
+ */
+extern (C++) class ConditionalDeclaration : AttribDeclaration
+{
+ Condition condition; /// condition deciding whether decl or elsedecl applies
+ Dsymbols* elsedecl; /// array of Dsymbol's for else block
+
+ extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl)
+ {
+ super(loc, null, decl);
+ //printf("ConditionalDeclaration::ConditionalDeclaration()\n");
+ this.condition = condition;
+ this.elsedecl = elsedecl;
+ }
+
+ override ConditionalDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new ConditionalDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl));
+ }
+
+ override final bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition.inc);
+ if (condition.inc != Include.notComputed)
+ {
+ Dsymbols* d = condition.include(null) ? decl : elsedecl;
+ return Dsymbol.oneMembers(d, ps, ident);
+ }
+ else
+ {
+ bool res = (Dsymbol.oneMembers(decl, ps, ident) && *ps is null && Dsymbol.oneMembers(elsedecl, ps, ident) && *ps is null);
+ *ps = null;
+ return res;
+ }
+ }
+
+ // Decide if 'then' or 'else' code should be included
+ override Dsymbols* include(Scope* sc)
+ {
+ //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, scope);
+
+ if (errors)
+ return null;
+
+ assert(condition);
+ return condition.include(_scope ? _scope : sc) ? decl : elsedecl;
+ }
+
+ override final void addComment(const(char)* comment)
+ {
+ /* Because addComment is called by the parser, if we called
+ * include() it would define a version before it was used.
+ * But it's no problem to drill down to both decl and elsedecl,
+ * so that's the workaround.
+ */
+ if (comment)
+ {
+ decl .foreachDsymbol( s => s.addComment(comment) );
+ elsedecl.foreachDsymbol( s => s.addComment(comment) );
+ }
+ }
+
+ override void setScope(Scope* sc)
+ {
+ include(sc).foreachDsymbol( s => s.setScope(sc) );
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * `<scopesym> {
+ * static if (<condition>) { <decl> } else { <elsedecl> }
+ * }`
+ */
+extern (C++) final class StaticIfDeclaration : ConditionalDeclaration
+{
+ ScopeDsymbol scopesym; /// enclosing symbol (e.g. module) where symbols will be inserted
+ private bool addisdone = false; /// true if members have been added to scope
+ private bool onStack = false; /// true if a call to `include` is currently active
+
+ extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl)
+ {
+ super(loc, condition, decl, elsedecl);
+ //printf("StaticIfDeclaration::StaticIfDeclaration()\n");
+ }
+
+ override StaticIfDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new StaticIfDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl));
+ }
+
+ /****************************************
+ * Different from other AttribDeclaration subclasses, include() call requires
+ * the completion of addMember and setScope phases.
+ */
+ override Dsymbols* include(Scope* sc)
+ {
+ //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, scope);
+
+ if (errors || onStack)
+ return null;
+ onStack = true;
+ scope(exit) onStack = false;
+
+ if (sc && condition.inc == Include.notComputed)
+ {
+ assert(scopesym); // addMember is already done
+ assert(_scope); // setScope is already done
+ Dsymbols* d = ConditionalDeclaration.include(_scope);
+ if (d && !addisdone)
+ {
+ // Add members lazily.
+ d.foreachDsymbol( s => s.addMember(_scope, scopesym) );
+
+ // Set the member scopes lazily.
+ d.foreachDsymbol( s => s.setScope(_scope) );
+
+ addisdone = true;
+ }
+ return d;
+ }
+ else
+ {
+ return ConditionalDeclaration.include(sc);
+ }
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ //printf("StaticIfDeclaration::addMember() '%s'\n", toChars());
+ /* This is deferred until the condition evaluated later (by the include() call),
+ * so that expressions in the condition can refer to declarations
+ * in the same scope, such as:
+ *
+ * template Foo(int i)
+ * {
+ * const int j = i + 1;
+ * static if (j == 3)
+ * const int k;
+ * }
+ */
+ this.scopesym = sds;
+ }
+
+ override void setScope(Scope* sc)
+ {
+ // do not evaluate condition before semantic pass
+ // But do set the scope, in case we need it for forward referencing
+ Dsymbol.setScope(sc);
+ }
+
+ override void importAll(Scope* sc)
+ {
+ // do not evaluate condition before semantic pass
+ }
+
+ override const(char)* kind() const
+ {
+ return "static if";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Static foreach at declaration scope, like:
+ * static foreach (i; [0, 1, 2]){ }
+ */
+
+extern (C++) final class StaticForeachDeclaration : AttribDeclaration
+{
+ StaticForeach sfe; /// contains `static foreach` expansion logic
+
+ ScopeDsymbol scopesym; /// cached enclosing scope (mimics `static if` declaration)
+
+ /++
+ `include` can be called multiple times, but a `static foreach`
+ should be expanded at most once. Achieved by caching the result
+ of the first call. We need both `cached` and `cache`, because
+ `null` is a valid value for `cache`.
+ +/
+ bool onStack = false;
+ bool cached = false;
+ Dsymbols* cache = null;
+
+ extern (D) this(StaticForeach sfe, Dsymbols* decl)
+ {
+ super(sfe.loc, null, decl);
+ this.sfe = sfe;
+ }
+
+ override StaticForeachDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new StaticForeachDeclaration(
+ sfe.syntaxCopy(),
+ Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ override bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ // Required to support IFTI on a template that contains a
+ // `static foreach` declaration. `super.oneMember` calls
+ // include with a `null` scope. As `static foreach` requires
+ // the scope for expansion, `oneMember` can only return a
+ // precise result once `static foreach` has been expanded.
+ if (cached)
+ {
+ return super.oneMember(ps, ident);
+ }
+ *ps = null; // a `static foreach` declaration may in general expand to multiple symbols
+ return false;
+ }
+
+ override Dsymbols* include(Scope* sc)
+ {
+ if (errors || onStack)
+ return null;
+ if (cached)
+ {
+ assert(!onStack);
+ return cache;
+ }
+ onStack = true;
+ scope(exit) onStack = false;
+
+ if (_scope)
+ {
+ sfe.prepare(_scope); // lower static foreach aggregate
+ }
+ if (!sfe.ready())
+ {
+ return null; // TODO: ok?
+ }
+
+ // expand static foreach
+ import dmd.statementsem: makeTupleForeach;
+ Dsymbols* d = makeTupleForeach!(true,true)(_scope, sfe.aggrfe, decl, sfe.needExpansion);
+ if (d) // process generated declarations
+ {
+ // Add members lazily.
+ d.foreachDsymbol( s => s.addMember(_scope, scopesym) );
+
+ // Set the member scopes lazily.
+ d.foreachDsymbol( s => s.setScope(_scope) );
+ }
+ cached = true;
+ cache = d;
+ return d;
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ // used only for caching the enclosing symbol
+ this.scopesym = sds;
+ }
+
+ override void addComment(const(char)* comment)
+ {
+ // do nothing
+ // change this to give semantics to documentation comments on static foreach declarations
+ }
+
+ override void setScope(Scope* sc)
+ {
+ // do not evaluate condition before semantic pass
+ // But do set the scope, in case we need it for forward referencing
+ Dsymbol.setScope(sc);
+ }
+
+ override void importAll(Scope* sc)
+ {
+ // do not evaluate aggregate before semantic pass
+ }
+
+ override const(char)* kind() const
+ {
+ return "static foreach";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Collection of declarations that stores foreach index variables in a
+ * local symbol table. Other symbols declared within are forwarded to
+ * another scope, like:
+ *
+ * static foreach (i; 0 .. 10) // loop variables for different indices do not conflict.
+ * { // this body is expanded into 10 ForwardingAttribDeclarations, where `i` has storage class STC.local
+ * mixin("enum x" ~ to!string(i) ~ " = i"); // ok, can access current loop variable
+ * }
+ *
+ * static foreach (i; 0.. 10)
+ * {
+ * pragma(msg, mixin("x" ~ to!string(i))); // ok, all 10 symbols are visible as they were forwarded to the global scope
+ * }
+ *
+ * static assert (!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop
+ *
+ * A StaticForeachDeclaration generates one
+ * ForwardingAttribDeclaration for each expansion of its body. The
+ * AST of the ForwardingAttribDeclaration contains both the `static
+ * foreach` variables and the respective copy of the `static foreach`
+ * body. The functionality is achieved by using a
+ * ForwardingScopeDsymbol as the parent symbol for the generated
+ * declarations.
+ */
+
+extern(C++) final class ForwardingAttribDeclaration: AttribDeclaration
+{
+ ForwardingScopeDsymbol sym = null;
+
+ this(Dsymbols* decl)
+ {
+ super(decl);
+ sym = new ForwardingScopeDsymbol(null);
+ sym.symtab = new DsymbolTable();
+ }
+
+ /**************************************
+ * Use the ForwardingScopeDsymbol as the parent symbol for members.
+ */
+ override Scope* newScope(Scope* sc)
+ {
+ return sc.push(sym);
+ }
+
+ /***************************************
+ * Lazily initializes the scope to forward to.
+ */
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ parent = sym.parent = sym.forward = sds;
+ return super.addMember(sc, sym);
+ }
+
+ override inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+
+/***********************************************************
+ * Mixin declarations, like:
+ * mixin("int x");
+ * https://dlang.org/spec/module.html#mixin-declaration
+ */
+extern (C++) final class CompileDeclaration : AttribDeclaration
+{
+ Expressions* exps;
+ ScopeDsymbol scopesym;
+ bool compiled;
+
+ extern (D) this(const ref Loc loc, Expressions* exps)
+ {
+ super(loc, null, null);
+ //printf("CompileDeclaration(loc = %d)\n", loc.linnum);
+ this.exps = exps;
+ }
+
+ override CompileDeclaration syntaxCopy(Dsymbol s)
+ {
+ //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars());
+ return new CompileDeclaration(loc, Expression.arraySyntaxCopy(exps));
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum);
+ this.scopesym = sds;
+ }
+
+ override void setScope(Scope* sc)
+ {
+ Dsymbol.setScope(sc);
+ }
+
+ override const(char)* kind() const
+ {
+ return "mixin";
+ }
+
+ override inout(CompileDeclaration) isCompileDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * User defined attributes look like:
+ * @foo(args, ...)
+ * @(args, ...)
+ */
+extern (C++) final class UserAttributeDeclaration : AttribDeclaration
+{
+ Expressions* atts;
+
+ extern (D) this(Expressions* atts, Dsymbols* decl)
+ {
+ super(decl);
+ this.atts = atts;
+ }
+
+ override UserAttributeDeclaration syntaxCopy(Dsymbol s)
+ {
+ //printf("UserAttributeDeclaration::syntaxCopy('%s')\n", toChars());
+ assert(!s);
+ return new UserAttributeDeclaration(Expression.arraySyntaxCopy(this.atts), Dsymbol.arraySyntaxCopy(decl));
+ }
+
+ override Scope* newScope(Scope* sc)
+ {
+ Scope* sc2 = sc;
+ if (atts && atts.dim)
+ {
+ // create new one for changes
+ sc2 = sc.copy();
+ sc2.userAttribDecl = this;
+ }
+ return sc2;
+ }
+
+ override void setScope(Scope* sc)
+ {
+ //printf("UserAttributeDeclaration::setScope() %p\n", this);
+ if (decl)
+ Dsymbol.setScope(sc); // for forward reference of UDAs
+ return AttribDeclaration.setScope(sc);
+ }
+
+ extern (D) static Expressions* concat(Expressions* udas1, Expressions* udas2)
+ {
+ Expressions* udas;
+ if (!udas1 || udas1.dim == 0)
+ udas = udas2;
+ else if (!udas2 || udas2.dim == 0)
+ udas = udas1;
+ else
+ {
+ /* Create a new tuple that combines them
+ * (do not append to left operand, as this is a copy-on-write operation)
+ */
+ udas = new Expressions(2);
+ (*udas)[0] = new TupleExp(Loc.initial, udas1);
+ (*udas)[1] = new TupleExp(Loc.initial, udas2);
+ }
+ return udas;
+ }
+
+ Expressions* getAttributes()
+ {
+ if (auto sc = _scope)
+ {
+ _scope = null;
+ arrayExpressionSemantic(atts, sc);
+ }
+ auto exps = new Expressions();
+ if (userAttribDecl && userAttribDecl !is this)
+ exps.push(new TupleExp(Loc.initial, userAttribDecl.getAttributes()));
+ if (atts && atts.dim)
+ exps.push(new TupleExp(Loc.initial, atts));
+ return exps;
+ }
+
+ override const(char)* kind() const
+ {
+ return "UserAttribute";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ /**
+ * Check if the provided expression references `core.attribute.gnuAbiTag`
+ *
+ * This should be called after semantic has been run on the expression.
+ * Semantic on UDA happens in semantic2 (see `dmd.semantic2`).
+ *
+ * Params:
+ * e = Expression to check (usually from `UserAttributeDeclaration.atts`)
+ *
+ * Returns:
+ * `true` if the expression references the compiler-recognized `gnuAbiTag`
+ */
+ static bool isGNUABITag(Expression e)
+ {
+ if (global.params.cplusplus < CppStdRevision.cpp11)
+ return false;
+
+ auto ts = e.type ? e.type.isTypeStruct() : null;
+ if (!ts)
+ return false;
+ if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent)
+ return false;
+ // Can only be defined in druntime
+ Module m = ts.sym.parent.isModule();
+ if (!m || !m.isCoreModule(Id.attribute))
+ return false;
+ return true;
+ }
+
+ /**
+ * Called from a symbol's semantic to check if `gnuAbiTag` UDA
+ * can be applied to them
+ *
+ * Directly emits an error if the UDA doesn't work with this symbol
+ *
+ * Params:
+ * sym = symbol to check for `gnuAbiTag`
+ * linkage = Linkage of the symbol (Declaration.link or sc.link)
+ */
+ static void checkGNUABITag(Dsymbol sym, LINK linkage)
+ {
+ if (global.params.cplusplus < CppStdRevision.cpp11)
+ return;
+
+ // Avoid `if` at the call site
+ if (sym.userAttribDecl is null || sym.userAttribDecl.atts is null)
+ return;
+
+ foreach (exp; *sym.userAttribDecl.atts)
+ {
+ if (isGNUABITag(exp))
+ {
+ if (sym.isCPPNamespaceDeclaration() || sym.isNspace())
+ {
+ exp.error("`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars());
+ sym.errors = true;
+ }
+ else if (linkage != LINK.cpp)
+ {
+ exp.error("`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars());
+ sym.errors = true;
+ }
+ // Only one `@gnuAbiTag` is allowed by semantic2
+ return;
+ }
+ }
+ }
+}
diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h
index 174d3c1ad5b..e63c80be3c6 100644
--- a/gcc/d/dmd/attrib.h
+++ b/gcc/d/dmd/attrib.h
@@ -10,13 +10,10 @@
#pragma once
+#include "root/port.h"
#include "dsymbol.h"
class Expression;
-class Statement;
-class LabelDsymbol;
-class Initializer;
-class Module;
class Condition;
class StaticForeach;
@@ -27,12 +24,7 @@ class AttribDeclaration : public Dsymbol
public:
Dsymbols *decl; // array of Dsymbol's
- AttribDeclaration(Dsymbols *decl);
virtual Dsymbols *include(Scope *sc);
- int apply(Dsymbol_apply_ft_t fp, void *param);
- static Scope *createNewScope(Scope *sc,
- StorageClass newstc, LINK linkage, CPPMANGLE cppmangle, Prot protection,
- int explicitProtection, AlignDeclaration *aligndecl, PINLINE inlining);
virtual Scope *newScope(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope *sc);
@@ -40,7 +32,7 @@ public:
void addComment(const utf8_t *comment);
const char *kind() const;
bool oneMember(Dsymbol **ps, Identifier *ident);
- void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
+ void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion);
bool hasPointers();
bool hasStaticCtorOrDtor();
void checkCtorConstInit();
@@ -55,8 +47,7 @@ class StorageClassDeclaration : public AttribDeclaration
public:
StorageClass stc;
- StorageClassDeclaration(StorageClass stc, Dsymbols *decl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ StorageClassDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
bool oneMember(Dsymbol **ps, Identifier *ident);
void addMember(Scope *sc, ScopeDsymbol *sds);
@@ -71,11 +62,9 @@ public:
Expression *msg;
const char *msgstr;
- DeprecatedDeclaration(Expression *msg, Dsymbols *decl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ DeprecatedDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
void setScope(Scope *sc);
- const char *getMessage();
void accept(Visitor *v) { v->visit(this); }
};
@@ -84,11 +73,10 @@ class LinkDeclaration : public AttribDeclaration
public:
LINK linkage;
- LinkDeclaration(LINK p, Dsymbols *decl);
- static LinkDeclaration *create(LINK p, Dsymbols *decl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ static LinkDeclaration *create(const Loc &loc, LINK p, Dsymbols *decl);
+ LinkDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
- const char *toChars();
+ const char *toChars() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -97,40 +85,48 @@ class CPPMangleDeclaration : public AttribDeclaration
public:
CPPMANGLE cppmangle;
- CPPMangleDeclaration(CPPMANGLE p, Dsymbols *decl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ CPPMangleDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
- const char *toChars();
+ void setScope(Scope *sc);
+ const char *toChars() const;
void accept(Visitor *v) { v->visit(this); }
};
-class ProtDeclaration : public AttribDeclaration
+class CPPNamespaceDeclaration : public AttribDeclaration
{
public:
- Prot protection;
- Identifiers* pkg_identifiers;
+ Expression *exp;
- ProtDeclaration(Loc loc, Prot p, Dsymbols *decl);
- ProtDeclaration(Loc loc, Identifiers* pkg_identifiers, Dsymbols *decl);
+ CPPNamespaceDeclaration *syntaxCopy(Dsymbol *s);
+ Scope *newScope(Scope *sc);
+ const char *toChars() const;
+ void accept(Visitor *v) { v->visit(this); }
+};
+
+class VisibilityDeclaration : public AttribDeclaration
+{
+public:
+ Visibility visibility;
+ DArray<Identifier*> pkg_identifiers;
- Dsymbol *syntaxCopy(Dsymbol *s);
+ VisibilityDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
const char *kind() const;
const char *toPrettyChars(bool unused);
+ VisibilityDeclaration *isVisibilityDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
class AlignDeclaration : public AttribDeclaration
{
public:
- Expression *ealign;
+ Expressions *alignExps;
structalign_t salign;
- AlignDeclaration(Loc loc, Expression *ealign, Dsymbols *decl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ AlignDeclaration(const Loc &loc, Expression *ealign, Dsymbols *decl);
+ AlignDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
- structalign_t getAlignment(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -143,10 +139,9 @@ public:
unsigned anonstructsize; // size of anonymous struct
unsigned anonalignsize; // size of anonymous struct for alignment purposes
- AnonDeclaration(Loc loc, bool isunion, Dsymbols *decl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ AnonDeclaration *syntaxCopy(Dsymbol *s);
void setScope(Scope *sc);
- void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
+ void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion);
const char *kind() const;
AnonDeclaration *isAnonDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -157,9 +152,9 @@ class PragmaDeclaration : public AttribDeclaration
public:
Expressions *args; // array of Expression's
- PragmaDeclaration(Loc loc, Identifier *ident, Expressions *args, Dsymbols *decl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ PragmaDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
+ PINLINE evalPragmaInline(Scope* sc);
const char *kind() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -170,8 +165,7 @@ public:
Condition *condition;
Dsymbols *elsedecl; // array of Dsymbol's for else block
- ConditionalDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ ConditionalDeclaration *syntaxCopy(Dsymbol *s);
bool oneMember(Dsymbol **ps, Identifier *ident);
Dsymbols *include(Scope *sc);
void addComment(const utf8_t *comment);
@@ -186,8 +180,7 @@ public:
bool addisdone;
bool onStack;
- StaticIfDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ StaticIfDeclaration *syntaxCopy(Dsymbol *s);
Dsymbols *include(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope *sc);
@@ -205,8 +198,7 @@ public:
bool cached;
Dsymbols *cache;
- StaticForeachDeclaration(StaticForeach *sfe, Dsymbols *decl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ StaticForeachDeclaration *syntaxCopy(Dsymbol *s);
bool oneMember(Dsymbol **ps, Identifier *ident);
Dsymbols *include(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
@@ -222,7 +214,6 @@ class ForwardingAttribDeclaration : public AttribDeclaration
public:
ForwardingScopeDsymbol *sym;
- ForwardingAttribDeclaration(Dsymbols *decl);
Scope *newScope(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
ForwardingAttribDeclaration *isForwardingAttribDeclaration() { return this; }
@@ -239,8 +230,7 @@ public:
ScopeDsymbol *scopesym;
bool compiled;
- CompileDeclaration(Loc loc, Expressions *exps);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ CompileDeclaration *syntaxCopy(Dsymbol *s);
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope *sc);
const char *kind() const;
@@ -256,11 +246,9 @@ class UserAttributeDeclaration : public AttribDeclaration
public:
Expressions *atts;
- UserAttributeDeclaration(Expressions *atts, Dsymbols *decl);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ UserAttributeDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
void setScope(Scope *sc);
- static Expressions *concat(Expressions *udas1, Expressions *udas2);
Expressions *getAttributes();
const char *kind() const;
void accept(Visitor *v) { v->visit(this); }
diff --git a/gcc/d/dmd/blockexit.c b/gcc/d/dmd/blockexit.c
deleted file mode 100644
index 1895d36fb1e..00000000000
--- a/gcc/d/dmd/blockexit.c
+++ /dev/null
@@ -1,506 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "statement.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "id.h"
-
-/* Only valid after semantic analysis
- * If 'mustNotThrow' is true, generate an error if it throws
- */
-int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow)
-{
- class BlockExit : public Visitor
- {
- public:
- FuncDeclaration *func;
- bool mustNotThrow;
- int result;
-
- BlockExit(FuncDeclaration *func, bool mustNotThrow)
- : func(func), mustNotThrow(mustNotThrow)
- {
- result = BEnone;
- }
-
- void visit(Statement *s)
- {
- printf("Statement::blockExit(%p)\n", s);
- printf("%s\n", s->toChars());
- assert(0);
- result = BEany;
- }
-
- void visit(ErrorStatement *)
- {
- result = BEany;
- }
-
- void visit(ExpStatement *s)
- {
- result = BEfallthru;
- if (s->exp)
- {
- if (s->exp->op == TOKhalt)
- {
- result = BEhalt;
- return;
- }
- if (s->exp->op == TOKassert)
- {
- AssertExp *a = (AssertExp *)s->exp;
- if (a->e1->isBool(false)) // if it's an assert(0)
- {
- result = BEhalt;
- return;
- }
- }
- if (s->exp->type->toBasetype()->isTypeNoreturn())
- result = BEhalt;
- if (canThrow(s->exp, func, mustNotThrow))
- result |= BEthrow;
- }
- }
-
- void visit(CompileStatement *)
- {
- assert(global.errors);
- result = BEfallthru;
- }
-
- void visit(CompoundStatement *cs)
- {
- //printf("CompoundStatement::blockExit(%p) %d result = x%X\n", cs, cs->statements->length, result);
- result = BEfallthru;
- Statement *slast = NULL;
- for (size_t i = 0; i < cs->statements->length; i++)
- {
- Statement *s = (*cs->statements)[i];
- if (s)
- {
- //printf("result = x%x\n", result);
- //printf("s: %s\n", s->toChars());
- if (result & BEfallthru && slast)
- {
- slast = slast->last();
- if (slast && (slast->isCaseStatement() || slast->isDefaultStatement()) &&
- (s->isCaseStatement() || s->isDefaultStatement()))
- {
- // Allow if last case/default was empty
- CaseStatement *sc = slast->isCaseStatement();
- DefaultStatement *sd = slast->isDefaultStatement();
- if (sc && (!sc->statement->hasCode() || sc->statement->isCaseStatement() || sc->statement->isErrorStatement()))
- ;
- else if (sd && (!sd->statement->hasCode() || sd->statement->isCaseStatement() || sd->statement->isErrorStatement()))
- ;
- else
- {
- const char *gototype = s->isCaseStatement() ? "case" : "default";
- s->deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype);
- }
- }
- }
-
- if (!(result & BEfallthru) && !s->comeFrom())
- {
- if (blockExit(s, func, mustNotThrow) != BEhalt && s->hasCode())
- s->warning("statement is not reachable");
- }
- else
- {
- result &= ~BEfallthru;
- result |= blockExit(s, func, mustNotThrow);
- }
- slast = s;
- }
- }
- }
-
- void visit(UnrolledLoopStatement *uls)
- {
- result = BEfallthru;
- for (size_t i = 0; i < uls->statements->length; i++)
- {
- Statement *s = (*uls->statements)[i];
- if (s)
- {
- int r = blockExit(s, func, mustNotThrow);
- result |= r & ~(BEbreak | BEcontinue | BEfallthru);
- if ((r & (BEfallthru | BEcontinue | BEbreak)) == 0)
- result &= ~BEfallthru;
- }
- }
- }
-
- void visit(ScopeStatement *s)
- {
- //printf("ScopeStatement::blockExit(%p)\n", s->statement);
- result = s->statement ? blockExit(s->statement, func, mustNotThrow) : BEfallthru;
- }
-
- void visit(WhileStatement *)
- {
- assert(global.errors);
- result = BEfallthru;
- }
-
- void visit(DoStatement *s)
- {
- if (s->_body)
- {
- result = blockExit(s->_body, func, mustNotThrow);
- if (result == BEbreak)
- {
- result = BEfallthru;
- return;
- }
- if (result & BEcontinue)
- result |= BEfallthru;
- }
- else
- result = BEfallthru;
- if (result & BEfallthru)
- {
- if (canThrow(s->condition, func, mustNotThrow))
- result |= BEthrow;
- if (!(result & BEbreak) && s->condition->isBool(true))
- result &= ~BEfallthru;
- }
- result &= ~(BEbreak | BEcontinue);
- }
-
- void visit(ForStatement *s)
- {
- result = BEfallthru;
- if (s->_init)
- {
- result = blockExit(s->_init, func, mustNotThrow);
- if (!(result & BEfallthru))
- return;
- }
- if (s->condition)
- {
- if (canThrow(s->condition, func, mustNotThrow))
- result |= BEthrow;
- if (s->condition->isBool(true))
- result &= ~BEfallthru;
- else if (s->condition->isBool(false))
- return;
- }
- else
- result &= ~BEfallthru; // the body must do the exiting
- if (s->_body)
- {
- int r = blockExit(s->_body, func, mustNotThrow);
- if (r & (BEbreak | BEgoto))
- result |= BEfallthru;
- result |= r & ~(BEfallthru | BEbreak | BEcontinue);
- }
- if (s->increment && canThrow(s->increment, func, mustNotThrow))
- result |= BEthrow;
- }
-
- void visit(ForeachStatement *s)
- {
- result = BEfallthru;
- if (canThrow(s->aggr, func, mustNotThrow))
- result |= BEthrow;
- if (s->_body)
- result |= blockExit(s->_body, func, mustNotThrow) & ~(BEbreak | BEcontinue);
- }
-
- void visit(ForeachRangeStatement *)
- {
- assert(global.errors);
- result = BEfallthru;
- }
-
- void visit(IfStatement *s)
- {
- //printf("IfStatement::blockExit(%p)\n", s);
-
- result = BEnone;
- if (canThrow(s->condition, func, mustNotThrow))
- result |= BEthrow;
- if (s->condition->isBool(true))
- {
- if (s->ifbody)
- result |= blockExit(s->ifbody, func, mustNotThrow);
- else
- result |= BEfallthru;
- }
- else if (s->condition->isBool(false))
- {
- if (s->elsebody)
- result |= blockExit(s->elsebody, func, mustNotThrow);
- else
- result |= BEfallthru;
- }
- else
- {
- if (s->ifbody)
- result |= blockExit(s->ifbody, func, mustNotThrow);
- else
- result |= BEfallthru;
- if (s->elsebody)
- result |= blockExit(s->elsebody, func, mustNotThrow);
- else
- result |= BEfallthru;
- }
- //printf("IfStatement::blockExit(%p) = x%x\n", s, result);
- }
-
- void visit(ConditionalStatement *s)
- {
- result = blockExit(s->ifbody, func, mustNotThrow);
- if (s->elsebody)
- result |= blockExit(s->elsebody, func, mustNotThrow);
- }
-
- void visit(PragmaStatement *)
- {
- result = BEfallthru;
- }
-
- void visit(StaticAssertStatement *)
- {
- result = BEfallthru;
- }
-
- void visit(SwitchStatement *s)
- {
- result = BEnone;
- if (canThrow(s->condition, func, mustNotThrow))
- result |= BEthrow;
- if (s->_body)
- {
- result |= blockExit(s->_body, func, mustNotThrow);
- if (result & BEbreak)
- {
- result |= BEfallthru;
- result &= ~BEbreak;
- }
- }
- else
- result |= BEfallthru;
- }
-
- void visit(CaseStatement *s)
- {
- result = blockExit(s->statement, func, mustNotThrow);
- }
-
- void visit(DefaultStatement *s)
- {
- result = blockExit(s->statement, func, mustNotThrow);
- }
-
- void visit(GotoDefaultStatement *)
- {
- result = BEgoto;
- }
-
- void visit(GotoCaseStatement *)
- {
- result = BEgoto;
- }
-
- void visit(SwitchErrorStatement *)
- {
- // Switch errors are non-recoverable
- result = BEhalt;
- }
-
- void visit(ReturnStatement *s)
- {
- result = BEreturn;
- if (s->exp && canThrow(s->exp, func, mustNotThrow))
- result |= BEthrow;
- }
-
- void visit(BreakStatement *s)
- {
- //printf("BreakStatement::blockExit(%p) = x%x\n", s, s->ident ? BEgoto : BEbreak);
- result = s->ident ? BEgoto : BEbreak;
- }
-
- void visit(ContinueStatement *s)
- {
- result = s->ident ? BEgoto : BEcontinue;
- }
-
- void visit(SynchronizedStatement *s)
- {
- result = s->_body ? blockExit(s->_body, func, mustNotThrow) : BEfallthru;
- }
-
- void visit(WithStatement *s)
- {
- result = BEnone;
- if (canThrow(s->exp, func, mustNotThrow))
- result = BEthrow;
- if (s->_body)
- result |= blockExit(s->_body, func, mustNotThrow);
- else
- result |= BEfallthru;
- }
-
- void visit(TryCatchStatement *s)
- {
- assert(s->_body);
- result = blockExit(s->_body, func, false);
-
- int catchresult = 0;
- for (size_t i = 0; i < s->catches->length; i++)
- {
- Catch *c = (*s->catches)[i];
- if (c->type == Type::terror)
- continue;
-
- int cresult;
- if (c->handler)
- cresult = blockExit(c->handler, func, mustNotThrow);
- else
- cresult = BEfallthru;
-
- /* If we're catching Object, then there is no throwing
- */
- Identifier *id = c->type->toBasetype()->isClassHandle()->ident;
- if (c->internalCatch && (cresult & BEfallthru))
- {
- // Bugzilla 11542: leave blockExit flags of the body
- cresult &= ~BEfallthru;
- }
- else if (id == Id::Object || id == Id::Throwable)
- {
- result &= ~(BEthrow | BEerrthrow);
- }
- else if (id == Id::Exception)
- {
- result &= ~BEthrow;
- }
- catchresult |= cresult;
- }
- if (mustNotThrow && (result & BEthrow))
- {
- // now explain why this is nothrow
- blockExit(s->_body, func, mustNotThrow);
- }
- result |= catchresult;
- }
-
- void visit(TryFinallyStatement *s)
- {
- result = BEfallthru;
- if (s->_body)
- result = blockExit(s->_body, func, false);
-
- // check finally body as well, it may throw (bug #4082)
- int finalresult = BEfallthru;
- if (s->finalbody)
- finalresult = blockExit(s->finalbody, func, false);
-
- // If either body or finalbody halts
- if (result == BEhalt)
- finalresult = BEnone;
- if (finalresult == BEhalt)
- result = BEnone;
-
- if (mustNotThrow)
- {
- // now explain why this is nothrow
- if (s->_body && (result & BEthrow))
- blockExit(s->_body, func, mustNotThrow);
- if (s->finalbody && (finalresult & BEthrow))
- blockExit(s->finalbody, func, mustNotThrow);
- }
-
- #if 0
- // Bugzilla 13201: Mask to prevent spurious warnings for
- // destructor call, exit of synchronized statement, etc.
- if (result == BEhalt && finalresult != BEhalt && s->finalbody &&
- s->finalbody->hasCode())
- {
- s->finalbody->warning("statement is not reachable");
- }
- #endif
-
- if (!(finalresult & BEfallthru))
- result &= ~BEfallthru;
- result |= finalresult & ~BEfallthru;
- }
-
- void visit(ScopeGuardStatement *)
- {
- // At this point, this statement is just an empty placeholder
- result = BEfallthru;
- }
-
- void visit(ThrowStatement *s)
- {
- if (s->internalThrow)
- {
- // Bugzilla 8675: Allow throwing 'Throwable' object even if mustNotThrow.
- result = BEfallthru;
- return;
- }
-
- Type *t = s->exp->type->toBasetype();
- ClassDeclaration *cd = t->isClassHandle();
- assert(cd);
-
- if (cd == ClassDeclaration::errorException ||
- ClassDeclaration::errorException->isBaseOf(cd, NULL))
- {
- result = BEerrthrow;
- return;
- }
- if (mustNotThrow)
- s->error("%s is thrown but not caught", s->exp->type->toChars());
-
- result = BEthrow;
- }
-
- void visit(GotoStatement *)
- {
- //printf("GotoStatement::blockExit(%p)\n", s);
- result = BEgoto;
- }
-
- void visit(LabelStatement *s)
- {
- //printf("LabelStatement::blockExit(%p)\n", s);
- result = s->statement ? blockExit(s->statement, func, mustNotThrow) : BEfallthru;
- if (s->breaks)
- result |= BEfallthru;
- }
-
- void visit(CompoundAsmStatement *s)
- {
- if (mustNotThrow && !(s->stc & STCnothrow))
- s->deprecation("asm statement is assumed to throw - mark it with `nothrow` if it does not");
-
- // Assume the worst
- result = BEfallthru | BEreturn | BEgoto | BEhalt;
- if (!(s->stc & STCnothrow)) result |= BEthrow;
- }
-
- void visit(ImportStatement *)
- {
- result = BEfallthru;
- }
- };
-
- if (!s)
- return BEfallthru;
- BlockExit be(func, mustNotThrow);
- s->accept(&be);
- return be.result;
-}
diff --git a/gcc/d/dmd/blockexit.d b/gcc/d/dmd/blockexit.d
new file mode 100644
index 00000000000..1fd90051830
--- /dev/null
+++ b/gcc/d/dmd/blockexit.d
@@ -0,0 +1,537 @@
+/**
+ * Find out in what ways control flow can exit a statement block.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/blockexit.d, _blockexit.d)
+ * Documentation: https://dlang.org/phobos/dmd_blockexit.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/blockexit.d
+ */
+
+module dmd.blockexit;
+
+import core.stdc.stdio;
+
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.canthrow;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.statement;
+import dmd.tokens;
+import dmd.visitor;
+
+/**
+ * BE stands for BlockExit.
+ *
+ * It indicates if a statement does transfer control to another block.
+ * A block is a sequence of statements enclosed in { }
+ */
+enum BE : int
+{
+ none = 0,
+ fallthru = 1,
+ throw_ = 2,
+ return_ = 4,
+ goto_ = 8,
+ halt = 0x10,
+ break_ = 0x20,
+ continue_ = 0x40,
+ errthrow = 0x80,
+ any = (fallthru | throw_ | return_ | goto_ | halt),
+}
+
+
+/*********************************************
+ * Determine mask of ways that a statement can exit.
+ *
+ * Only valid after semantic analysis.
+ * Params:
+ * s = statement to check for block exit status
+ * func = function that statement s is in
+ * mustNotThrow = generate an error if it throws
+ * Returns:
+ * BE.xxxx
+ */
+int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
+{
+ extern (C++) final class BlockExit : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ FuncDeclaration func;
+ bool mustNotThrow;
+ int result;
+
+ extern (D) this(FuncDeclaration func, bool mustNotThrow)
+ {
+ this.func = func;
+ this.mustNotThrow = mustNotThrow;
+ result = BE.none;
+ }
+
+ override void visit(Statement s)
+ {
+ printf("Statement::blockExit(%p)\n", s);
+ printf("%s\n", s.toChars());
+ assert(0);
+ }
+
+ override void visit(ErrorStatement s)
+ {
+ result = BE.none;
+ }
+
+ override void visit(ExpStatement s)
+ {
+ result = BE.fallthru;
+ if (s.exp)
+ {
+ if (s.exp.op == TOK.halt)
+ {
+ result = BE.halt;
+ return;
+ }
+ if (s.exp.op == TOK.assert_)
+ {
+ AssertExp a = cast(AssertExp)s.exp;
+ if (a.e1.isBool(false)) // if it's an assert(0)
+ {
+ result = BE.halt;
+ return;
+ }
+ }
+ if (s.exp.type.toBasetype().isTypeNoreturn())
+ result = BE.halt;
+ if (canThrow(s.exp, func, mustNotThrow))
+ result |= BE.throw_;
+ }
+ }
+
+ override void visit(CompileStatement s)
+ {
+ assert(global.errors);
+ result = BE.fallthru;
+ }
+
+ override void visit(CompoundStatement cs)
+ {
+ //printf("CompoundStatement.blockExit(%p) %d result = x%X\n", cs, cs.statements.dim, result);
+ result = BE.fallthru;
+ Statement slast = null;
+ foreach (s; *cs.statements)
+ {
+ if (s)
+ {
+ //printf("result = x%x\n", result);
+ //printf("s: %s\n", s.toChars());
+ if (result & BE.fallthru && slast)
+ {
+ slast = slast.last();
+ if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement()))
+ {
+ // Allow if last case/default was empty
+ CaseStatement sc = slast.isCaseStatement();
+ DefaultStatement sd = slast.isDefaultStatement();
+ if (sc && (!sc.statement.hasCode() || sc.statement.isCaseStatement() || sc.statement.isErrorStatement()))
+ {
+ }
+ else if (sd && (!sd.statement.hasCode() || sd.statement.isCaseStatement() || sd.statement.isErrorStatement()))
+ {
+ }
+ else
+ {
+ const(char)* gototype = s.isCaseStatement() ? "case" : "default";
+ s.deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype);
+ }
+ }
+ }
+
+ if (!(result & BE.fallthru) && !s.comeFrom())
+ {
+ if (blockExit(s, func, mustNotThrow) != BE.halt && s.hasCode() &&
+ s.loc != Loc.initial) // don't emit warning for generated code
+ s.warning("statement is not reachable");
+ }
+ else
+ {
+ result &= ~BE.fallthru;
+ result |= blockExit(s, func, mustNotThrow);
+ }
+ slast = s;
+ }
+ }
+ }
+
+ override void visit(UnrolledLoopStatement uls)
+ {
+ result = BE.fallthru;
+ foreach (s; *uls.statements)
+ {
+ if (s)
+ {
+ int r = blockExit(s, func, mustNotThrow);
+ result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru);
+ if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0)
+ result &= ~BE.fallthru;
+ }
+ }
+ }
+
+ override void visit(ScopeStatement s)
+ {
+ //printf("ScopeStatement::blockExit(%p)\n", s.statement);
+ result = blockExit(s.statement, func, mustNotThrow);
+ }
+
+ override void visit(WhileStatement s)
+ {
+ assert(global.errors);
+ result = BE.fallthru;
+ }
+
+ override void visit(DoStatement s)
+ {
+ if (s._body)
+ {
+ result = blockExit(s._body, func, mustNotThrow);
+ if (result == BE.break_)
+ {
+ result = BE.fallthru;
+ return;
+ }
+ if (result & BE.continue_)
+ result |= BE.fallthru;
+ }
+ else
+ result = BE.fallthru;
+ if (result & BE.fallthru)
+ {
+ if (canThrow(s.condition, func, mustNotThrow))
+ result |= BE.throw_;
+ if (!(result & BE.break_) && s.condition.isBool(true))
+ result &= ~BE.fallthru;
+ }
+ result &= ~(BE.break_ | BE.continue_);
+ }
+
+ override void visit(ForStatement s)
+ {
+ result = BE.fallthru;
+ if (s._init)
+ {
+ result = blockExit(s._init, func, mustNotThrow);
+ if (!(result & BE.fallthru))
+ return;
+ }
+ if (s.condition)
+ {
+ if (canThrow(s.condition, func, mustNotThrow))
+ result |= BE.throw_;
+ if (s.condition.isBool(true))
+ result &= ~BE.fallthru;
+ else if (s.condition.isBool(false))
+ return;
+ }
+ else
+ result &= ~BE.fallthru; // the body must do the exiting
+ if (s._body)
+ {
+ int r = blockExit(s._body, func, mustNotThrow);
+ if (r & (BE.break_ | BE.goto_))
+ result |= BE.fallthru;
+ result |= r & ~(BE.fallthru | BE.break_ | BE.continue_);
+ }
+ if (s.increment && canThrow(s.increment, func, mustNotThrow))
+ result |= BE.throw_;
+ }
+
+ override void visit(ForeachStatement s)
+ {
+ result = BE.fallthru;
+ if (canThrow(s.aggr, func, mustNotThrow))
+ result |= BE.throw_;
+ if (s._body)
+ result |= blockExit(s._body, func, mustNotThrow) & ~(BE.break_ | BE.continue_);
+ }
+
+ override void visit(ForeachRangeStatement s)
+ {
+ assert(global.errors);
+ result = BE.fallthru;
+ }
+
+ override void visit(IfStatement s)
+ {
+ //printf("IfStatement::blockExit(%p)\n", s);
+ result = BE.none;
+ if (canThrow(s.condition, func, mustNotThrow))
+ result |= BE.throw_;
+ if (s.condition.isBool(true))
+ {
+ result |= blockExit(s.ifbody, func, mustNotThrow);
+ }
+ else if (s.condition.isBool(false))
+ {
+ result |= blockExit(s.elsebody, func, mustNotThrow);
+ }
+ else
+ {
+ result |= blockExit(s.ifbody, func, mustNotThrow);
+ result |= blockExit(s.elsebody, func, mustNotThrow);
+ }
+ //printf("IfStatement::blockExit(%p) = x%x\n", s, result);
+ }
+
+ override void visit(ConditionalStatement s)
+ {
+ result = blockExit(s.ifbody, func, mustNotThrow);
+ if (s.elsebody)
+ result |= blockExit(s.elsebody, func, mustNotThrow);
+ }
+
+ override void visit(PragmaStatement s)
+ {
+ result = BE.fallthru;
+ }
+
+ override void visit(StaticAssertStatement s)
+ {
+ result = BE.fallthru;
+ }
+
+ override void visit(SwitchStatement s)
+ {
+ result = BE.none;
+ if (canThrow(s.condition, func, mustNotThrow))
+ result |= BE.throw_;
+ if (s._body)
+ {
+ result |= blockExit(s._body, func, mustNotThrow);
+ if (result & BE.break_)
+ {
+ result |= BE.fallthru;
+ result &= ~BE.break_;
+ }
+ }
+ else
+ result |= BE.fallthru;
+ }
+
+ override void visit(CaseStatement s)
+ {
+ result = blockExit(s.statement, func, mustNotThrow);
+ }
+
+ override void visit(DefaultStatement s)
+ {
+ result = blockExit(s.statement, func, mustNotThrow);
+ }
+
+ override void visit(GotoDefaultStatement s)
+ {
+ result = BE.goto_;
+ }
+
+ override void visit(GotoCaseStatement s)
+ {
+ result = BE.goto_;
+ }
+
+ override void visit(SwitchErrorStatement s)
+ {
+ // Switch errors are non-recoverable
+ result = BE.halt;
+ }
+
+ override void visit(ReturnStatement s)
+ {
+ result = BE.return_;
+ if (s.exp && canThrow(s.exp, func, mustNotThrow))
+ result |= BE.throw_;
+ }
+
+ override void visit(BreakStatement s)
+ {
+ //printf("BreakStatement::blockExit(%p) = x%x\n", s, s.ident ? BE.goto_ : BE.break_);
+ result = s.ident ? BE.goto_ : BE.break_;
+ }
+
+ override void visit(ContinueStatement s)
+ {
+ result = s.ident ? BE.continue_ | BE.goto_ : BE.continue_;
+ }
+
+ override void visit(SynchronizedStatement s)
+ {
+ result = blockExit(s._body, func, mustNotThrow);
+ }
+
+ override void visit(WithStatement s)
+ {
+ result = BE.none;
+ if (canThrow(s.exp, func, mustNotThrow))
+ result = BE.throw_;
+ result |= blockExit(s._body, func, mustNotThrow);
+ }
+
+ override void visit(TryCatchStatement s)
+ {
+ assert(s._body);
+ result = blockExit(s._body, func, false);
+
+ int catchresult = 0;
+ foreach (c; *s.catches)
+ {
+ if (c.type == Type.terror)
+ continue;
+
+ int cresult = blockExit(c.handler, func, mustNotThrow);
+
+ /* If we're catching Object, then there is no throwing
+ */
+ Identifier id = c.type.toBasetype().isClassHandle().ident;
+ if (c.internalCatch && (cresult & BE.fallthru))
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=11542
+ // leave blockExit flags of the body
+ cresult &= ~BE.fallthru;
+ }
+ else if (id == Id.Object || id == Id.Throwable)
+ {
+ result &= ~(BE.throw_ | BE.errthrow);
+ }
+ else if (id == Id.Exception)
+ {
+ result &= ~BE.throw_;
+ }
+ catchresult |= cresult;
+ }
+ if (mustNotThrow && (result & BE.throw_))
+ {
+ // now explain why this is nothrow
+ blockExit(s._body, func, mustNotThrow);
+ }
+ result |= catchresult;
+ }
+
+ override void visit(TryFinallyStatement s)
+ {
+ result = BE.fallthru;
+ if (s._body)
+ result = blockExit(s._body, func, false);
+
+ // check finally body as well, it may throw (bug #4082)
+ int finalresult = BE.fallthru;
+ if (s.finalbody)
+ finalresult = blockExit(s.finalbody, func, false);
+
+ // If either body or finalbody halts
+ if (result == BE.halt)
+ finalresult = BE.none;
+ if (finalresult == BE.halt)
+ result = BE.none;
+
+ if (mustNotThrow)
+ {
+ // now explain why this is nothrow
+ if (s._body && (result & BE.throw_))
+ blockExit(s._body, func, mustNotThrow);
+ if (s.finalbody && (finalresult & BE.throw_))
+ blockExit(s.finalbody, func, mustNotThrow);
+ }
+
+ version (none)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13201
+ // Mask to prevent spurious warnings for
+ // destructor call, exit of synchronized statement, etc.
+ if (result == BE.halt && finalresult != BE.halt && s.finalbody && s.finalbody.hasCode())
+ {
+ s.finalbody.warning("statement is not reachable");
+ }
+ }
+
+ if (!(finalresult & BE.fallthru))
+ result &= ~BE.fallthru;
+ result |= finalresult & ~BE.fallthru;
+ }
+
+ override void visit(ScopeGuardStatement s)
+ {
+ // At this point, this statement is just an empty placeholder
+ result = BE.fallthru;
+ }
+
+ override void visit(ThrowStatement s)
+ {
+ if (s.internalThrow)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=8675
+ // Allow throwing 'Throwable' object even if mustNotThrow.
+ result = BE.fallthru;
+ return;
+ }
+
+ Type t = s.exp.type.toBasetype();
+ ClassDeclaration cd = t.isClassHandle();
+ assert(cd);
+
+ if (cd == ClassDeclaration.errorException || ClassDeclaration.errorException.isBaseOf(cd, null))
+ {
+ result = BE.errthrow;
+ return;
+ }
+ if (mustNotThrow)
+ s.error("`%s` is thrown but not caught", s.exp.type.toChars());
+
+ result = BE.throw_;
+ }
+
+ override void visit(GotoStatement s)
+ {
+ //printf("GotoStatement::blockExit(%p)\n", s);
+ result = BE.goto_;
+ }
+
+ override void visit(LabelStatement s)
+ {
+ //printf("LabelStatement::blockExit(%p)\n", s);
+ result = blockExit(s.statement, func, mustNotThrow);
+ if (s.breaks)
+ result |= BE.fallthru;
+ }
+
+ override void visit(CompoundAsmStatement s)
+ {
+ // Assume the worst
+ result = BE.fallthru | BE.return_ | BE.goto_ | BE.halt;
+ if (!(s.stc & STC.nothrow_))
+ {
+ if (mustNotThrow && !(s.stc & STC.nothrow_))
+ s.deprecation("`asm` statement is assumed to throw - mark it with `nothrow` if it does not");
+ else
+ result |= BE.throw_;
+ }
+ }
+
+ override void visit(ImportStatement s)
+ {
+ result = BE.fallthru;
+ }
+ }
+
+ if (!s)
+ return BE.fallthru;
+ scope BlockExit be = new BlockExit(func, mustNotThrow);
+ s.accept(be);
+ return be.result;
+}
+
diff --git a/gcc/d/dmd/builtin.d b/gcc/d/dmd/builtin.d
new file mode 100644
index 00000000000..b99f690e156
--- /dev/null
+++ b/gcc/d/dmd/builtin.d
@@ -0,0 +1,33 @@
+/**
+ * Implement CTFE for intrinsic (builtin) functions.
+ *
+ * Currently includes functions from `std.math`, `core.math` and `core.bitop`.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/builtin.d, _builtin.d)
+ * Documentation: https://dlang.org/phobos/dmd_builtin.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/builtin.d
+ */
+
+module dmd.builtin;
+
+import core.stdc.math;
+import core.stdc.string;
+import dmd.arraytypes;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+
+/**********************************
+ * Determine if function is a builtin one that we can
+ * evaluate at compile time.
+ */
+public extern (C++) BUILTIN isBuiltin(FuncDeclaration fd);
+
+/**************************************
+ * Evaluate builtin function.
+ * Return result; NULL if cannot evaluate it.
+ */
+public extern (C++) Expression eval_builtin(Loc loc, FuncDeclaration fd, Expressions* arguments);
diff --git a/gcc/d/dmd/canthrow.c b/gcc/d/dmd/canthrow.c
deleted file mode 100644
index 5d180f54367..00000000000
--- a/gcc/d/dmd/canthrow.c
+++ /dev/null
@@ -1,316 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/canthrow.c
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "init.h"
-#include "expression.h"
-#include "template.h"
-#include "statement.h"
-#include "mtype.h"
-#include "utf.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "scope.h"
-#include "attrib.h"
-#include "tokens.h"
-
-bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow);
-bool walkPostorder(Expression *e, StoppableVisitor *v);
-
-/********************************************
- * Returns true if the expression may throw exceptions.
- * If 'mustNotThrow' is true, generate an error if it throws
- */
-
-bool canThrow(Expression *e, FuncDeclaration *func, bool mustNotThrow)
-{
- //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
-
- // stop walking if we determine this expression can throw
- class CanThrow : public StoppableVisitor
- {
- FuncDeclaration *func;
- bool mustNotThrow;
-
- public:
- CanThrow(FuncDeclaration *func, bool mustNotThrow)
- : func(func), mustNotThrow(mustNotThrow)
- {
- }
-
- void visit(Expression *)
- {
- }
-
- void visit(DeclarationExp *de)
- {
- stop = Dsymbol_canThrow(de->declaration, func, mustNotThrow);
- }
-
- void visit(CallExp *ce)
- {
- if (global.errors && !ce->e1->type)
- return; // error recovery
-
- /* If calling a function or delegate that is typed as nothrow,
- * then this expression cannot throw.
- * Note that pure functions can throw.
- */
- Type *t = ce->e1->type->toBasetype();
- if (ce->f && ce->f == func)
- return;
- if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow)
- return;
- if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow)
- return;
-
- if (mustNotThrow)
- {
- if (ce->f)
- {
- ce->error("%s `%s` is not nothrow",
- ce->f->kind(), ce->f->toPrettyChars());
- }
- else
- {
- Expression *e1 = ce->e1;
- if (e1->op == TOKstar) // print 'fp' if e1 is (*fp)
- e1 = ((PtrExp *)e1)->e1;
- ce->error("`%s` is not nothrow", e1->toChars());
- }
- }
- stop = true;
- }
-
- void visit(NewExp *ne)
- {
- if (ne->member)
- {
- if (ne->allocator)
- {
- // Bugzilla 14407
- Type *t = ne->allocator->type->toBasetype();
- if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
- {
- if (mustNotThrow)
- {
- ne->error("%s `%s` is not nothrow",
- ne->allocator->kind(), ne->allocator->toPrettyChars());
- }
- stop = true;
- }
- }
- // See if constructor call can throw
- Type *t = ne->member->type->toBasetype();
- if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
- {
- if (mustNotThrow)
- {
- ne->error("%s `%s` is not nothrow",
- ne->member->kind(), ne->member->toPrettyChars());
- }
- stop = true;
- }
- }
- // regard storage allocation failures as not recoverable
- }
-
- void visit(DeleteExp *de)
- {
- Type *tb = de->e1->type->toBasetype();
- AggregateDeclaration *ad = NULL;
- switch (tb->ty)
- {
- case Tclass:
- ad = ((TypeClass *)tb)->sym;
- break;
-
- case Tpointer:
- tb = ((TypePointer *)tb)->next->toBasetype();
- if (tb->ty == Tstruct)
- ad = ((TypeStruct *)tb)->sym;
- break;
-
- case Tarray:
- {
- Type *tv = tb->nextOf()->baseElemOf();
- if (tv->ty == Tstruct)
- {
- ad = ((TypeStruct *)tv)->sym;
- break;
- }
- }
-
- default:
- break;
- }
- if (!ad)
- return;
-
- if (ad->dtor)
- {
- Type *t = ad->dtor->type->toBasetype();
- if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
- {
- if (mustNotThrow)
- {
- de->error("%s `%s` is not nothrow",
- ad->dtor->kind(), ad->dtor->toPrettyChars());
- }
- stop = true;
- }
- }
- if (ad->aggDelete && tb->ty != Tarray)
- {
- Type *t = ad->aggDelete->type;
- if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
- {
- if (mustNotThrow)
- {
- de->error("%s `%s` is not nothrow",
- ad->aggDelete->kind(), ad->aggDelete->toPrettyChars());
- }
- stop = true;
- }
- }
- }
-
- void visit(AssignExp *ae)
- {
- // blit-init cannot throw
- if (ae->op == TOKblit)
- return;
-
- /* Element-wise assignment could invoke postblits.
- */
- Type *t;
- if (ae->type->toBasetype()->ty == Tsarray)
- {
- if (!ae->e2->isLvalue())
- return;
- t = ae->type;
- }
- else if (ae->e1->op == TOKslice)
- t = ((SliceExp *)ae->e1)->e1->type;
- else
- return;
-
- Type *tv = t->baseElemOf();
- if (tv->ty != Tstruct)
- return;
- StructDeclaration *sd = ((TypeStruct *)tv)->sym;
- if (!sd->postblit || sd->postblit->type->ty != Tfunction)
- return;
-
- if (((TypeFunction *)sd->postblit->type)->isnothrow)
- ;
- else
- {
- if (mustNotThrow)
- {
- ae->error("%s `%s` is not nothrow",
- sd->postblit->kind(), sd->postblit->toPrettyChars());
- }
- stop = true;
- }
- }
-
- void visit(NewAnonClassExp *)
- {
- assert(0); // should have been lowered by semantic()
- }
- };
-
- CanThrow ct(func, mustNotThrow);
- return walkPostorder(e, &ct);
-}
-
-/**************************************
- * Does symbol, when initialized, throw?
- * Mirrors logic in Dsymbol_toElem().
- */
-
-bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow)
-{
- AttribDeclaration *ad;
- VarDeclaration *vd;
- TemplateMixin *tm;
- TupleDeclaration *td;
-
- //printf("Dsymbol_toElem() %s\n", s->toChars());
- ad = s->isAttribDeclaration();
- if (ad)
- {
- Dsymbols *decl = ad->include(NULL);
- if (decl && decl->length)
- {
- for (size_t i = 0; i < decl->length; i++)
- {
- s = (*decl)[i];
- if (Dsymbol_canThrow(s, func, mustNotThrow))
- return true;
- }
- }
- }
- else if ((vd = s->isVarDeclaration()) != NULL)
- {
- s = s->toAlias();
- if (s != vd)
- return Dsymbol_canThrow(s, func, mustNotThrow);
- if (vd->storage_class & STCmanifest)
- ;
- else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared))
- ;
- else
- {
- if (vd->_init)
- {
- ExpInitializer *ie = vd->_init->isExpInitializer();
- if (ie && canThrow(ie->exp, func, mustNotThrow))
- return true;
- }
- if (vd->needsScopeDtor())
- return canThrow(vd->edtor, func, mustNotThrow);
- }
- }
- else if ((tm = s->isTemplateMixin()) != NULL)
- {
- //printf("%s\n", tm->toChars());
- if (tm->members)
- {
- for (size_t i = 0; i < tm->members->length; i++)
- {
- Dsymbol *sm = (*tm->members)[i];
- if (Dsymbol_canThrow(sm, func, mustNotThrow))
- return true;
- }
- }
- }
- else if ((td = s->isTupleDeclaration()) != NULL)
- {
- for (size_t i = 0; i < td->objects->length; i++)
- {
- RootObject *o = (*td->objects)[i];
- if (o->dyncast() == DYNCAST_EXPRESSION)
- {
- Expression *eo = (Expression *)o;
- if (eo->op == TOKdsymbol)
- {
- DsymbolExp *se = (DsymbolExp *)eo;
- if (Dsymbol_canThrow(se->s, func, mustNotThrow))
- return true;
- }
- }
- }
- }
- return false;
-}
diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d
new file mode 100644
index 00000000000..ed05af6ac7c
--- /dev/null
+++ b/gcc/d/dmd/canthrow.d
@@ -0,0 +1,244 @@
+/**
+ * Perform checks for `nothrow`.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/function.html#nothrow-functions, Nothrow Functions)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/canthrow.d, _canthrow.d)
+ * Documentation: https://dlang.org/phobos/dmd_canthrow.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/canthrow.d
+ */
+
+module dmd.canthrow;
+
+import dmd.aggregate;
+import dmd.apply;
+import dmd.arraytypes;
+import dmd.attrib;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dsymbol;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.init;
+import dmd.mtype;
+import dmd.root.rootobject;
+import dmd.tokens;
+import dmd.visitor;
+
+/********************************************
+ * Returns true if the expression may throw exceptions.
+ * If 'mustNotThrow' is true, generate an error if it throws
+ */
+extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow)
+{
+ //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
+ // stop walking if we determine this expression can throw
+ extern (C++) final class CanThrow : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ FuncDeclaration func;
+ bool mustNotThrow;
+
+ public:
+ extern (D) this(FuncDeclaration func, bool mustNotThrow)
+ {
+ this.func = func;
+ this.mustNotThrow = mustNotThrow;
+ }
+
+ void checkFuncThrows(Expression e, FuncDeclaration f)
+ {
+ auto tf = f.type.toBasetype().isTypeFunction();
+ if (tf && !tf.isnothrow)
+ {
+ if (mustNotThrow)
+ {
+ e.error("%s `%s` is not `nothrow`",
+ f.kind(), f.toPrettyChars());
+
+ e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
+ }
+ stop = true; // if any function throws, then the whole expression throws
+ }
+ }
+
+ override void visit(Expression)
+ {
+ }
+
+ override void visit(DeclarationExp de)
+ {
+ stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow);
+ }
+
+ override void visit(CallExp ce)
+ {
+ if (ce.inDebugStatement)
+ return;
+
+ if (global.errors && !ce.e1.type)
+ return; // error recovery
+ /* If calling a function or delegate that is typed as nothrow,
+ * then this expression cannot throw.
+ * Note that pure functions can throw.
+ */
+ if (ce.f && ce.f == func)
+ return;
+ Type t = ce.e1.type.toBasetype();
+ auto tf = t.isTypeFunction();
+ if (tf && tf.isnothrow)
+ return;
+ else
+ {
+ auto td = t.isTypeDelegate();
+ if (td && td.nextOf().isTypeFunction().isnothrow)
+ return;
+ }
+
+ if (ce.f)
+ checkFuncThrows(ce, ce.f);
+ else if (mustNotThrow)
+ {
+ auto e1 = ce.e1;
+ if (auto pe = e1.isPtrExp()) // print 'fp' if e1 is (*fp)
+ e1 = pe.e1;
+ ce.error("`%s` is not `nothrow`", e1.toChars());
+ }
+ stop = true;
+ }
+
+ override void visit(NewExp ne)
+ {
+ if (ne.member)
+ {
+ // See if constructor call can throw
+ checkFuncThrows(ne, ne.member);
+ }
+ // regard storage allocation failures as not recoverable
+ }
+
+ override void visit(DeleteExp de)
+ {
+ Type tb = de.e1.type.toBasetype();
+ AggregateDeclaration ad = null;
+ switch (tb.ty)
+ {
+ case Tclass:
+ ad = tb.isTypeClass().sym;
+ break;
+
+ case Tpointer:
+ case Tarray:
+ auto ts = tb.nextOf().baseElemOf().isTypeStruct();
+ if (!ts)
+ return;
+ ad = ts.sym;
+ break;
+
+ default:
+ assert(0); // error should have been detected by semantic()
+ }
+
+ if (ad.dtor)
+ checkFuncThrows(de, ad.dtor);
+ }
+
+ override void visit(AssignExp ae)
+ {
+ // blit-init cannot throw
+ if (ae.op == TOK.blit)
+ return;
+ /* Element-wise assignment could invoke postblits.
+ */
+ Type t;
+ if (ae.type.toBasetype().ty == Tsarray)
+ {
+ if (!ae.e2.isLvalue())
+ return;
+ t = ae.type;
+ }
+ else if (auto se = ae.e1.isSliceExp())
+ t = se.e1.type;
+ else
+ return;
+
+ if (auto ts = t.baseElemOf().isTypeStruct())
+ if (auto postblit = ts.sym.postblit)
+ checkFuncThrows(ae, postblit);
+ }
+
+ override void visit(NewAnonClassExp)
+ {
+ assert(0); // should have been lowered by semantic()
+ }
+ }
+
+ scope CanThrow ct = new CanThrow(func, mustNotThrow);
+ return walkPostorder(e, ct);
+}
+
+/**************************************
+ * Does symbol, when initialized, throw?
+ * Mirrors logic in Dsymbol_toElem().
+ */
+private bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow)
+{
+ int symbolDg(Dsymbol s)
+ {
+ return Dsymbol_canThrow(s, func, mustNotThrow);
+ }
+
+ //printf("Dsymbol_toElem() %s\n", s.toChars());
+ if (auto vd = s.isVarDeclaration())
+ {
+ s = s.toAlias();
+ if (s != vd)
+ return Dsymbol_canThrow(s, func, mustNotThrow);
+ if (vd.storage_class & STC.manifest)
+ {
+ }
+ else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.tls | STC.gshared))
+ {
+ }
+ else
+ {
+ if (vd._init)
+ {
+ if (auto ie = vd._init.isExpInitializer())
+ if (canThrow(ie.exp, func, mustNotThrow))
+ return true;
+ }
+ if (vd.needsScopeDtor())
+ return canThrow(vd.edtor, func, mustNotThrow);
+ }
+ }
+ else if (auto ad = s.isAttribDeclaration())
+ {
+ return ad.include(null).foreachDsymbol(&symbolDg) != 0;
+ }
+ else if (auto tm = s.isTemplateMixin())
+ {
+ return tm.members.foreachDsymbol(&symbolDg) != 0;
+ }
+ else if (auto td = s.isTupleDeclaration())
+ {
+ for (size_t i = 0; i < td.objects.dim; i++)
+ {
+ RootObject o = (*td.objects)[i];
+ if (o.dyncast() == DYNCAST.expression)
+ {
+ Expression eo = cast(Expression)o;
+ if (auto se = eo.isDsymbolExp())
+ {
+ if (Dsymbol_canThrow(se.s, func, mustNotThrow))
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
diff --git a/gcc/d/dmd/chkformat.c b/gcc/d/dmd/chkformat.c
deleted file mode 100644
index a4a97c9bf50..00000000000
--- a/gcc/d/dmd/chkformat.c
+++ /dev/null
@@ -1,985 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-// Check the arguments to `printf` and `scanf` against the `format` string.
-
-#include "root/dsystem.h"
-#include "root/dcompat.h"
-
-#include "arraytypes.h"
-#include "cond.h"
-#include "errors.h"
-#include "expression.h"
-#include "globals.h"
-#include "identifier.h"
-#include "mtype.h"
-#include "target.h"
-
-
-/* Different kinds of formatting specifications, variations we don't
- care about are merged. (Like we don't care about the difference between
- f, e, g, a, etc.)
-
- For `scanf`, every format is a pointer.
- */
-enum Format
-{
- Format_d, // int
- Format_hhd, // signed char
- Format_hd, // short int
- Format_ld, // long int
- Format_lld, // long long int
- Format_jd, // intmax_t
- Format_zd, // size_t
- Format_td, // ptrdiff_t
- Format_u, // unsigned int
- Format_hhu, // unsigned char
- Format_hu, // unsigned short int
- Format_lu, // unsigned long int
- Format_llu, // unsigned long long int
- Format_ju, // uintmax_t
- Format_g, // float (scanf) / double (printf)
- Format_lg, // double (scanf)
- Format_Lg, // long double (both)
- Format_s, // char string (both)
- Format_ls, // wchar_t string (both)
- Format_c, // char (printf)
- Format_lc, // wint_t (printf)
- Format_p, // pointer
- Format_n, // pointer to int
- Format_hhn, // pointer to signed char
- Format_hn, // pointer to short
- Format_ln, // pointer to long int
- Format_lln, // pointer to long long int
- Format_jn, // pointer to intmax_t
- Format_zn, // pointer to size_t
- Format_tn, // pointer to ptrdiff_t
- Format_GNU_a, // GNU ext. : address to a string with no maximum size (scanf)
- Format_GNU_m, // GNU ext. : string corresponding to the error code in errno (printf) / length modifier (scanf)
- Format_percent, // %% (i.e. no argument)
- Format_error, // invalid format specification
-};
-
-/**************************************
- * Parse the *length specifier* and the *specifier* of the following form:
- * `[length]specifier`
- *
- * Params:
- * format = format string
- * idx = index of of start of format specifier,
- * which gets updated to index past the end of it,
- * even if `Format_error` is returned
- * genSpecifier = Generic specifier. For instance, it will be set to `d` if the
- * format is `hdd`.
- * Returns:
- * Format
- */
-static Format parseGenericFormatSpecifier(const char *format,
- size_t &idx, char &genSpecifier, bool useGNUExts =
- findCondition(global.versionids, Identifier::idPool("CRuntime_Glibc")))
-{
- genSpecifier = 0;
-
- const size_t length = strlen(format);
-
- /* Read the `length modifier`
- */
- const char lm = format[idx];
- bool lm1= false; // if jztL
- bool lm2= false; // if `hh` or `ll`
- if (lm == 'j' ||
- lm == 'z' ||
- lm == 't' ||
- lm == 'L')
- {
- ++idx;
- if (idx == length)
- return Format_error;
- lm1 = true;
- }
- else if (lm == 'h' || lm == 'l')
- {
- ++idx;
- if (idx == length)
- return Format_error;
- lm2 = lm == format[idx];
- if (lm2)
- {
- ++idx;
- if (idx == length)
- return Format_error;
- }
- }
-
- /* Read the `specifier`
- */
- Format specifier;
- const char sc = format[idx];
- genSpecifier = sc;
- switch (sc)
- {
- case 'd':
- case 'i':
- if (lm == 'L')
- specifier = Format_error;
- else
- specifier = lm == 'h' && lm2 ? Format_hhd :
- lm == 'h' ? Format_hd :
- lm == 'l' && lm2 ? Format_lld :
- lm == 'l' ? Format_ld :
- lm == 'j' ? Format_jd :
- lm == 'z' ? Format_zd :
- lm == 't' ? Format_td :
- Format_d;
- break;
-
- case 'u':
- case 'o':
- case 'x':
- case 'X':
- if (lm == 'L')
- specifier = Format_error;
- else
- specifier = lm == 'h' && lm2 ? Format_hhu :
- lm == 'h' ? Format_hu :
- lm == 'l' && lm2 ? Format_llu :
- lm == 'l' ? Format_lu :
- lm == 'j' ? Format_ju :
- lm == 'z' ? Format_zd :
- lm == 't' ? Format_td :
- Format_u;
- break;
-
- case 'a':
- if (useGNUExts)
- {
- // https://www.gnu.org/software/libc/manual/html_node/Dynamic-String-Input.html
- specifier = Format_GNU_a;
- break;
- }
- /* fall through */
-
- case 'f':
- case 'F':
- case 'e':
- case 'E':
- case 'g':
- case 'G':
- case 'A':
- if (lm == 'L')
- specifier = Format_Lg;
- else if (lm1 || lm2 || lm == 'h')
- specifier = Format_error;
- else
- specifier = lm == 'l' ? Format_lg : Format_g;
- break;
-
- case 'c':
- if (lm1 || lm2 || lm == 'h')
- specifier = Format_error;
- else
- specifier = lm == 'l' ? Format_lc : Format_c;
- break;
-
- case 's':
- if (lm1 || lm2 || lm == 'h')
- specifier = Format_error;
- else
- specifier = lm == 'l' ? Format_ls : Format_s;
- break;
-
- case 'p':
- if (lm1 || lm2 || lm == 'h' || lm == 'l')
- specifier = Format_error;
- else
- specifier = Format_p;
- break;
-
- case 'n':
- if (lm == 'L')
- specifier = Format_error;
- else
- specifier = lm == 'l' && lm2 ? Format_lln :
- lm == 'l' ? Format_ln :
- lm == 'h' && lm2 ? Format_hhn :
- lm == 'h' ? Format_hn :
- lm == 'j' ? Format_jn :
- lm == 'z' ? Format_zn :
- lm == 't' ? Format_tn :
- Format_n;
- break;
-
- case 'm':
- if (useGNUExts)
- {
- // http://www.gnu.org/software/libc/manual/html_node/Other-Output-Conversions.html
- specifier = Format_GNU_m;
- break;
- }
- goto Ldefault;
-
- default:
- Ldefault:
- specifier = Format_error;
- break;
- }
-
- ++idx;
- return specifier; // success
-}
-
-Format formatError(size_t &idx, size_t i)
-{
- idx = i;
- return Format_error;
-}
-
-/**************************************
- * Parse the *format specifier* which is of the form:
- *
- * `%[*][width][length]specifier`
- *
- * Params:
- * format = format string
- * idx = index of `%` of start of format specifier,
- * which gets updated to index past the end of it,
- * even if `Format_error` is returned
- * asterisk = set if there is a `*` sub-specifier
- * Returns:
- * Format
- */
-static Format parseScanfFormatSpecifier(const char *format, size_t &idx,
- bool &asterisk)
-{
- asterisk = false;
-
- size_t i = idx;
- assert(format[i] == '%');
- const size_t length = strlen(format);
-
- ++i;
- if (i == length)
- return formatError(idx, i);
-
- if (format[i] == '%')
- {
- idx = i + 1;
- return Format_percent;
- }
-
- // * sub-specifier
- if (format[i] == '*')
- {
- ++i;
- if (i == length)
- return formatError(idx, i);
- asterisk = true;
- }
-
- // fieldWidth
- while (isdigit(format[i]))
- {
- i++;
- if (i == length)
- return formatError(idx, i);
- }
-
- /* Read the scanset
- * A scanset can be anything, so we just check that it is paired
- */
- if (format[i] == '[')
- {
- while (i < length)
- {
- if (format[i] == ']')
- break;
- ++i;
- }
-
- // no `]` found
- if (i == length)
- return formatError(idx, i);
-
- ++i;
- // no specifier after `]`
- // it could be mixed with the one above, but then idx won't have the right index
- if (i == length)
- return formatError(idx, i);
- }
-
- /* Read the specifier
- */
- char genSpec;
- Format specifier = parseGenericFormatSpecifier(format, i, genSpec);
- if (specifier == Format_error)
- return formatError(idx, i);
-
- idx = i;
- return specifier; // success
-}
-
-/**************************************
- * Parse the *format specifier* which is of the form:
- *
- * `%[flags][field width][.precision][length modifier]specifier`
- *
- * Params:
- * format = format string
- * idx = index of `%` of start of format specifier,
- * which gets updated to index past the end of it,
- * even if `Format_error` is returned
- * widthStar = set if * for width
- * precisionStar = set if * for precision
- * Returns:
- * Format
- */
-static Format parsePrintfFormatSpecifier(const char *format, size_t &idx,
- bool &widthStar, bool &precisionStar)
-{
- widthStar = false;
- precisionStar = false;
-
- size_t i = idx;
- assert(format[i] == '%');
- const size_t format_length = strlen(format);
- const size_t length = format_length;
- bool hash = false;
- bool zero = false;
- bool flags = false;
- bool width = false;
- bool precision = false;
-
- ++i;
- if (i == length)
- return formatError(idx, i);
-
- if (format[i] == '%')
- {
- idx = i + 1;
- return Format_percent;
- }
-
- /* Read the `flags`
- */
- while (1)
- {
- const char c = format[i];
- if (c == '-' ||
- c == '+' ||
- c == ' ')
- {
- flags = true;
- }
- else if (c == '#')
- {
- hash = true;
- }
- else if (c == '0')
- {
- zero = true;
- }
- else
- break;
- ++i;
- if (i == length)
- return formatError(idx, i);
- }
-
- /* Read the `field width`
- */
- {
- const char c = format[i];
- if (c == '*')
- {
- width = true;
- widthStar = true;
- ++i;
- if (i == length)
- return formatError(idx, i);
- }
- else if ('1' <= c && c <= '9')
- {
- width = true;
- ++i;
- if (i == length)
- return formatError(idx, i);
- while ('0' <= format[i] && format[i] <= '9')
- {
- ++i;
- if (i == length)
- return formatError(idx, i);
- }
- }
- }
-
- /* Read the `precision`
- */
- if (format[i] == '.')
- {
- precision = true;
- ++i;
- if (i == length)
- return formatError(idx, i);
- const char c = format[i];
- if (c == '*')
- {
- precisionStar = true;
- ++i;
- if (i == length)
- return formatError(idx, i);
- }
- else if ('0' <= c && c <= '9')
- {
- ++i;
- if (i == length)
- return formatError(idx, i);
- while ('0' <= format[i] && format[i] <= '9')
- {
- ++i;
- if (i == length)
- return formatError(idx, i);
- }
- }
- }
-
- /* Read the specifier
- */
- char genSpec;
- Format specifier = parseGenericFormatSpecifier(format, i, genSpec);
- if (specifier == Format_error)
- return formatError(idx, i);
-
- switch (genSpec)
- {
- case 'c':
- case 's':
- if (hash || zero)
- return formatError(idx, i);
- break;
-
- case 'd':
- case 'i':
- if (hash)
- return formatError(idx, i);
- break;
-
- case 'n':
- if (hash || zero || precision || width || flags)
- return formatError(idx, i);
- break;
-
- default:
- break;
- }
-
- idx = i;
- return specifier; // success
-}
-
-/*******************************************/
-
-static Expression *getNextPrintfArg(const Loc &loc, Expressions &args, size_t &n,
- size_t gnu_m_count, bool &skip)
-{
- if (n == args.length)
- {
- if (args.length < (n + 1) - gnu_m_count)
- deprecation(loc, "more format specifiers than %d arguments", (int)n);
- else
- skip = true;
- return NULL;
- }
- return args[n++];
-}
-
-static void errorPrintfFormat(const char *prefix, DString &slice, Expression *arg,
- const char *texpect, Type *tactual)
-{
- deprecation(arg->loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`",
- prefix ? prefix : "", arg->toChars(), (int)slice.length, slice.ptr, texpect, tactual->toChars());
-}
-
-/******************************************
- * Check that arguments to a printf format string are compatible
- * with that string. Issue errors for incompatibilities.
- *
- * Follows the C99 specification for printf.
- *
- * Takes a generous, rather than strict, view of compatiblity.
- * For example, an unsigned value can be formatted with a signed specifier.
- *
- * Diagnosed incompatibilities are:
- *
- * 1. incompatible sizes which will cause argument misalignment
- * 2. deferencing arguments that are not pointers
- * 3. insufficient number of arguments
- * 4. struct arguments
- * 5. array and slice arguments
- * 6. non-pointer arguments to `s` specifier
- * 7. non-standard formats
- * 8. undefined behavior per C99
- *
- * Per the C Standard, extra arguments are ignored.
- *
- * No attempt is made to fix the arguments or the format string.
- *
- * Params:
- * loc = location for error messages
- * format = format string
- * args = arguments to match with format string
- * isVa_list = if a "v" function (format check only)
- *
- * Returns:
- * `true` if errors occurred
- * References:
- * C99 7.19.6.1
- * http://www.cplusplus.com/reference/cstdio/printf/
- */
-bool checkPrintfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list)
-{
- //printf("checkPrintFormat('%s')\n", format);
- size_t n = 0; // index in args
- size_t gnu_m_count = 0; // number of Format_GNU_m
- const size_t format_length = strlen(format);
- for (size_t i = 0; i < format_length;)
- {
- if (format[i] != '%')
- {
- ++i;
- continue;
- }
- bool widthStar = false;
- bool precisionStar = false;
- size_t j = i;
- const Format fmt = parsePrintfFormatSpecifier(format, j, widthStar, precisionStar);
- DString slice = DString(j - i, format + i);
- i = j;
-
- if (fmt == Format_percent)
- continue; // "%%", no arguments
-
- if (isVa_list)
- {
- // format check only
- if (fmt == Format_error)
- deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr);
- continue;
- }
-
- if (fmt == Format_GNU_m)
- ++gnu_m_count;
-
- if (widthStar)
- {
- bool skip = false;
- Expression *e = getNextPrintfArg(loc, args, n, gnu_m_count, skip);
- if (skip)
- continue;
- if (!e)
- return true;
- Type *t = e->type->toBasetype();
- if (t->ty != Tint32 && t->ty != Tuns32)
- errorPrintfFormat("width ", slice, e, "int", t);
- }
-
- if (precisionStar)
- {
- bool skip = false;
- Expression *e = getNextPrintfArg(loc, args, n, gnu_m_count, skip);
- if (skip)
- continue;
- if (!e)
- return true;
- Type *t = e->type->toBasetype();
- if (t->ty != Tint32 && t->ty != Tuns32)
- errorPrintfFormat("precision ", slice, e, "int", t);
- }
-
- bool skip = false;
- Expression *e = getNextPrintfArg(loc, args, n, gnu_m_count, skip);
- if (skip)
- continue;
- if (!e)
- return true;
- Type *t = e->type->toBasetype();
- Type *tnext = t->nextOf();
- const unsigned c_longsize = target.c.longsize;
- const unsigned ptrsize = target.ptrsize;
-
- // Types which are promoted to int are allowed.
- // Spec: C99 6.5.2.2.7
- switch (fmt)
- {
- case Format_u: // unsigned int
- case Format_d: // int
- if (t->ty != Tint32 && t->ty != Tuns32)
- errorPrintfFormat(NULL, slice, e, fmt == Format_u ? "uint" : "int", t);
- break;
-
- case Format_hhu: // unsigned char
- case Format_hhd: // signed char
- if (t->ty != Tint32 && t->ty != Tuns32 && t->ty != Tint8 && t->ty != Tuns8)
- errorPrintfFormat(NULL, slice, e, fmt == Format_hhu ? "ubyte" : "byte", t);
- break;
-
- case Format_hu: // unsigned short int
- case Format_hd: // short int
- if (t->ty != Tint32 && t->ty != Tuns32 && t->ty != Tint16 && t->ty != Tuns16)
- errorPrintfFormat(NULL, slice, e, fmt == Format_hu ? "ushort" : "short", t);
- break;
-
- case Format_lu: // unsigned long int
- case Format_ld: // long int
- if (!(t->isintegral() && t->size() == c_longsize))
- {
- if (fmt == Format_lu)
- errorPrintfFormat(NULL, slice, e, (c_longsize == 4 ? "uint" : "ulong"), t);
- else
- errorPrintfFormat(NULL, slice, e, (c_longsize == 4 ? "int" : "long"), t);
- }
- break;
-
- case Format_llu: // unsigned long long int
- case Format_lld: // long long int
- if (t->ty != Tint64 && t->ty != Tuns64)
- errorPrintfFormat(NULL, slice, e, fmt == Format_llu ? "ulong" : "long", t);
- break;
-
- case Format_ju: // uintmax_t
- case Format_jd: // intmax_t
- if (t->ty != Tint64 && t->ty != Tuns64)
- {
- if (fmt == Format_ju)
- errorPrintfFormat(NULL, slice, e, "core.stdc.stdint.uintmax_t", t);
- else
- errorPrintfFormat(NULL, slice, e, "core.stdc.stdint.intmax_t", t);
- }
- break;
-
- case Format_zd: // size_t
- if (!(t->isintegral() && t->size() == ptrsize))
- errorPrintfFormat(NULL, slice, e, "size_t", t);
- break;
-
- case Format_td: // ptrdiff_t
- if (!(t->isintegral() && t->size() == ptrsize))
- errorPrintfFormat(NULL, slice, e, "ptrdiff_t", t);
- break;
-
- case Format_GNU_a: // Format_GNU_a is only for scanf
- case Format_lg:
- case Format_g: // double
- if (t->ty != Tfloat64 && t->ty != Timaginary64)
- errorPrintfFormat(NULL, slice, e, "double", t);
- break;
-
- case Format_Lg: // long double
- if (t->ty != Tfloat80 && t->ty != Timaginary80)
- errorPrintfFormat(NULL, slice, e, "real", t);
- break;
-
- case Format_p: // pointer
- if (t->ty != Tpointer && t->ty != Tnull && t->ty != Tclass && t->ty != Tdelegate && t->ty != Taarray)
- errorPrintfFormat(NULL, slice, e, "void*", t);
- break;
-
- case Format_n: // pointer to int
- if (!(t->ty == Tpointer && tnext->ty == Tint32))
- errorPrintfFormat(NULL, slice, e, "int*", t);
- break;
-
- case Format_ln: // pointer to long int
- if (!(t->ty == Tpointer && tnext->isintegral() && !tnext->isunsigned() && tnext->size() == c_longsize))
- errorPrintfFormat(NULL, slice, e, (c_longsize == 4 ? "int*" : "long*"), t);
- break;
-
- case Format_lln: // pointer to long long int
- if (!(t->ty == Tpointer && tnext->ty == Tint64))
- errorPrintfFormat(NULL, slice, e, "long*", t);
- break;
-
- case Format_hn: // pointer to short
- if (!(t->ty == Tpointer && tnext->ty == Tint16))
- errorPrintfFormat(NULL, slice, e, "short*", t);
- break;
-
- case Format_hhn: // pointer to signed char
- if (!(t->ty == Tpointer && tnext->ty == Tint16))
- errorPrintfFormat(NULL, slice, e, "byte*", t);
- break;
-
- case Format_jn: // pointer to intmax_t
- if (!(t->ty == Tpointer && tnext->ty == Tint64))
- errorPrintfFormat(NULL, slice, e, "core.stdc.stdint.intmax_t*", t);
- break;
-
- case Format_zn: // pointer to size_t
- if (!(t->ty == Tpointer && tnext->isintegral() && tnext->isunsigned() && tnext->size() == ptrsize))
- errorPrintfFormat(NULL, slice, e, "size_t*", t);
- break;
-
- case Format_tn: // pointer to ptrdiff_t
- if (!(t->ty == Tpointer && tnext->isintegral() && !tnext->isunsigned() && tnext->size() == ptrsize))
- errorPrintfFormat(NULL, slice, e, "ptrdiff_t*", t);
- break;
-
- case Format_c: // char
- if (t->ty != Tint32 && t->ty != Tuns32)
- errorPrintfFormat(NULL, slice, e, "char", t);
- break;
-
- case Format_lc: // wint_t
- if (t->ty != Tint32 && t->ty != Tuns32)
- errorPrintfFormat(NULL, slice, e, "wchar_t", t);
- break;
-
- case Format_s: // pointer to char string
- if (!(t->ty == Tpointer && (tnext->ty == Tchar || tnext->ty == Tint8 || tnext->ty == Tuns8)))
- errorPrintfFormat(NULL, slice, e, "char*", t);
- break;
-
- case Format_ls: // pointer to wchar_t string
- {
- if (!(t->ty == Tpointer && tnext == target.c.twchar_t))
- errorPrintfFormat(NULL, slice, e, "wchar_t*", t);
- break;
- }
- case Format_error:
- deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr);
- break;
-
- case Format_GNU_m:
- break; // not assert(0) because it may go through it if there are extra arguments
-
- case Format_percent:
- default:
- assert(0);
- }
- }
- return false;
-}
-
-/*******************************************/
-
-static Expression *getNextScanfArg(const Loc &loc, Expressions &args, size_t &n, bool asterisk)
-{
- if (n == args.length)
- {
- if (!asterisk)
- deprecation(loc, "more format specifiers than %d arguments", (int)n);
- return NULL;
- }
- return args[n++];
-}
-
-static void errorScanfFormat(const char *prefix, DString &slice,
- Expression *arg, const char *texpect, Type *tactual)
-{
- deprecation(arg->loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`",
- prefix ? prefix : "", arg->toChars(), (int)slice.length, slice.ptr, texpect, tactual->toChars());
-}
-
-/******************************************
- * Check that arguments to a scanf format string are compatible
- * with that string. Issue errors for incompatibilities.
- *
- * Follows the C99 specification for scanf.
- *
- * Takes a generous, rather than strict, view of compatiblity.
- * For example, an unsigned value can be formatted with a signed specifier.
- *
- * Diagnosed incompatibilities are:
- *
- * 1. incompatible sizes which will cause argument misalignment
- * 2. deferencing arguments that are not pointers
- * 3. insufficient number of arguments
- * 4. struct arguments
- * 5. array and slice arguments
- * 6. non-standard formats
- * 7. undefined behavior per C99
- *
- * Per the C Standard, extra arguments are ignored.
- *
- * No attempt is made to fix the arguments or the format string.
- *
- * Params:
- * loc = location for error messages
- * format = format string
- * args = arguments to match with format string
- * isVa_list = if a "v" function (format check only)
- *
- * Returns:
- * `true` if errors occurred
- * References:
- * C99 7.19.6.2
- * http://www.cplusplus.com/reference/cstdio/scanf/
- */
-bool checkScanfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list)
-{
- size_t n = 0;
- const size_t format_length = strlen(format);
- for (size_t i = 0; i < format_length;)
- {
- if (format[i] != '%')
- {
- ++i;
- continue;
- }
- bool asterisk = false;
- size_t j = i;
- const Format fmt = parseScanfFormatSpecifier(format, j, asterisk);
- DString slice = DString(j - i, format + i);
- i = j;
-
- if (fmt == Format_percent || asterisk)
- continue; // "%%", "%*": no arguments
-
- if (isVa_list)
- {
- // format check only
- if (fmt == Format_error)
- deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr);
- continue;
- }
-
- Expression *e = getNextScanfArg(loc, args, n, asterisk);
- if (!e)
- return true;
-
- Type *t = e->type->toBasetype();
- Type *tnext = t->nextOf();
- const unsigned c_longsize = target.c.longsize;
- const unsigned ptrsize = target.ptrsize;
-
- switch (fmt)
- {
- case Format_n:
- case Format_d: // pointer to int
- if (!(t->ty == Tpointer && tnext->ty == Tint32))
- errorScanfFormat(NULL, slice, e, "int*", t);
- break;
-
- case Format_hhn:
- case Format_hhd: // pointer to signed char
- if (!(t->ty == Tpointer && tnext->ty == Tint16))
- errorScanfFormat(NULL, slice, e, "byte*", t);
- break;
-
- case Format_hn:
- case Format_hd: // pointer to short
- if (!(t->ty == Tpointer && tnext->ty == Tint16))
- errorScanfFormat(NULL, slice, e, "short*", t);
- break;
-
- case Format_ln:
- case Format_ld: // pointer to long int
- if (!(t->ty == Tpointer && tnext->isintegral() && tnext->size() == c_longsize))
- errorScanfFormat(NULL, slice, e, (c_longsize == 4 ? "int*" : "long*"), t);
- break;
-
- case Format_lln:
- case Format_lld: // pointer to long long int
- if (!(t->ty == Tpointer && tnext->ty == Tint64))
- errorScanfFormat(NULL, slice, e, "long*", t);
- break;
-
- case Format_jn:
- case Format_jd: // pointer to intmax_t
- if (!(t->ty == Tpointer && tnext->ty == Tint64))
- errorScanfFormat(NULL, slice, e, "core.stdc.stdint.intmax_t*", t);
- break;
-
- case Format_zn:
- case Format_zd: // pointer to size_t
- if (!(t->ty == Tpointer && tnext->isintegral() && tnext->isunsigned() && tnext->size() == ptrsize))
- errorScanfFormat(NULL, slice, e, "size_t*", t);
- break;
-
- case Format_tn:
- case Format_td: // pointer to ptrdiff_t
- if (!(t->ty == Tpointer && tnext->isintegral() && !tnext->isunsigned() && tnext->size() == ptrsize))
- errorScanfFormat(NULL, slice, e, "ptrdiff_t*", t);
- break;
-
- case Format_u: // pointer to unsigned int
- if (!(t->ty == Tpointer && tnext->ty == Tuns32))
- errorScanfFormat(NULL, slice, e, "uint*", t);
- break;
-
- case Format_hhu: // pointer to unsigned char
- if (!(t->ty == Tpointer && tnext->ty == Tuns8))
- errorScanfFormat(NULL, slice, e, "ubyte*", t);
- break;
-
- case Format_hu: // pointer to unsigned short int
- if (!(t->ty == Tpointer && tnext->ty == Tuns16))
- errorScanfFormat(NULL, slice, e, "ushort*", t);
- break;
-
- case Format_lu: // pointer to unsigned long int
- if (!(t->ty == Tpointer && tnext->isintegral() && tnext->isunsigned() && tnext->size() == c_longsize))
- errorScanfFormat(NULL, slice, e, (c_longsize == 4 ? "uint*" : "ulong*"), t);
- break;
-
- case Format_llu: // pointer to unsigned long long int
- if (!(t->ty == Tpointer && tnext->ty == Tuns64))
- errorScanfFormat(NULL, slice, e, "ulong*", t);
- break;
-
- case Format_ju: // pointer to uintmax_t
- if (!(t->ty == Tpointer && tnext->ty == Tuns64))
- errorScanfFormat(NULL, slice, e, "ulong*", t);
- break;
-
- case Format_g: // pointer to float
- if (!(t->ty == Tpointer && tnext->ty == Tfloat32))
- errorScanfFormat(NULL, slice, e, "float*", t);
- break;
-
- case Format_lg: // pointer to double
- if (!(t->ty == Tpointer && tnext->ty == Tfloat64))
- errorScanfFormat(NULL, slice, e, "double*", t);
- break;
-
- case Format_Lg: // pointer to long double
- if (!(t->ty == Tpointer && tnext->ty == Tfloat80))
- errorScanfFormat(NULL, slice, e, "real*", t);
- break;
-
- case Format_GNU_a:
- case Format_GNU_m:
- case Format_c:
- case Format_s: // pointer to char string
- if (!(t->ty == Tpointer && (tnext->ty == Tchar || tnext->ty == Tint8 || tnext->ty == Tuns8)))
- errorScanfFormat(NULL, slice, e, "char*", t);
- break;
-
- case Format_lc:
- case Format_ls: // pointer to wchar_t string
- {
- if (!(t->ty == Tpointer && tnext == target.c.twchar_t))
- errorScanfFormat(NULL, slice, e, "wchar_t*", t);
- break;
- }
- case Format_p: // double pointer
- if (!(t->ty == Tpointer && tnext->ty == Tpointer))
- errorScanfFormat(NULL, slice, e, "void**", t);
- break;
-
- case Format_error:
- deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr);
- break;
-
- case Format_percent:
- default:
- assert(0);
- }
- }
- return false;
-}
diff --git a/gcc/d/dmd/chkformat.d b/gcc/d/dmd/chkformat.d
new file mode 100644
index 00000000000..97adc5ad8ac
--- /dev/null
+++ b/gcc/d/dmd/chkformat.d
@@ -0,0 +1,1364 @@
+/**
+ * Check the arguments to `printf` and `scanf` against the `format` string.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/chkformat.d, _chkformat.d)
+ * Documentation: https://dlang.org/phobos/dmd_chkformat.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/chkformat.d
+ */
+module dmd.chkformat;
+
+//import core.stdc.stdio : printf, scanf;
+import core.stdc.ctype : isdigit;
+
+import dmd.astenums;
+import dmd.cond;
+import dmd.errors;
+import dmd.expression;
+import dmd.globals;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.target;
+
+
+/******************************************
+ * Check that arguments to a printf format string are compatible
+ * with that string. Issue errors for incompatibilities.
+ *
+ * Follows the C99 specification for printf.
+ *
+ * Takes a generous, rather than strict, view of compatiblity.
+ * For example, an unsigned value can be formatted with a signed specifier.
+ *
+ * Diagnosed incompatibilities are:
+ *
+ * 1. incompatible sizes which will cause argument misalignment
+ * 2. deferencing arguments that are not pointers
+ * 3. insufficient number of arguments
+ * 4. struct arguments
+ * 5. array and slice arguments
+ * 6. non-pointer arguments to `s` specifier
+ * 7. non-standard formats
+ * 8. undefined behavior per C99
+ *
+ * Per the C Standard, extra arguments are ignored.
+ *
+ * No attempt is made to fix the arguments or the format string.
+ *
+ * Params:
+ * loc = location for error messages
+ * format = format string
+ * args = arguments to match with format string
+ * isVa_list = if a "v" function (format check only)
+ *
+ * Returns:
+ * `true` if errors occurred
+ * References:
+ * C99 7.19.6.1
+ * http://www.cplusplus.com/reference/cstdio/printf/
+ */
+bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list)
+{
+ //printf("checkPrintFormat('%.*s')\n", cast(int)format.length, format.ptr);
+ size_t n, gnu_m_count; // index in args / number of Format.GNU_m
+ for (size_t i = 0; i < format.length;)
+ {
+ if (format[i] != '%')
+ {
+ ++i;
+ continue;
+ }
+ bool widthStar;
+ bool precisionStar;
+ size_t j = i;
+ const fmt = parsePrintfFormatSpecifier(format, j, widthStar, precisionStar);
+ const slice = format[i .. j];
+ i = j;
+
+ if (fmt == Format.percent)
+ continue; // "%%", no arguments
+
+ if (isVa_list)
+ {
+ // format check only
+ if (fmt == Format.error)
+ deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast(int)slice.length, slice.ptr);
+ continue;
+ }
+
+ if (fmt == Format.GNU_m)
+ ++gnu_m_count;
+
+ Expression getNextArg(ref bool skip)
+ {
+ if (n == args.length)
+ {
+ if (args.length < (n + 1) - gnu_m_count)
+ deprecation(loc, "more format specifiers than %d arguments", cast(int)n);
+ else
+ skip = true;
+ return null;
+ }
+ return args[n++];
+ }
+
+ void errorMsg(const char* prefix, Expression arg, const char* texpect, Type tactual)
+ {
+ deprecation(arg.loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`",
+ prefix ? prefix : "", arg.toChars(), cast(int)slice.length, slice.ptr, texpect, tactual.toChars());
+ }
+
+ if (widthStar)
+ {
+ bool skip;
+ auto e = getNextArg(skip);
+ if (skip)
+ continue;
+ if (!e)
+ return true;
+ auto t = e.type.toBasetype();
+ if (t.ty != Tint32 && t.ty != Tuns32)
+ errorMsg("width ", e, "int", t);
+ }
+
+ if (precisionStar)
+ {
+ bool skip;
+ auto e = getNextArg(skip);
+ if (skip)
+ continue;
+ if (!e)
+ return true;
+ auto t = e.type.toBasetype();
+ if (t.ty != Tint32 && t.ty != Tuns32)
+ errorMsg("precision ", e, "int", t);
+ }
+
+ bool skip;
+ auto e = getNextArg(skip);
+ if (skip)
+ continue;
+ if (!e)
+ return true;
+ auto t = e.type.toBasetype();
+ auto tnext = t.nextOf();
+ const c_longsize = target.c.longsize;
+ const ptrsize = target.ptrsize;
+
+ // Types which are promoted to int are allowed.
+ // Spec: C99 6.5.2.2.7
+ final switch (fmt)
+ {
+ case Format.u: // unsigned int
+ case Format.d: // int
+ if (t.ty != Tint32 && t.ty != Tuns32)
+ errorMsg(null, e, fmt == Format.u ? "uint" : "int", t);
+ break;
+
+ case Format.hhu: // unsigned char
+ case Format.hhd: // signed char
+ if (t.ty != Tint32 && t.ty != Tuns32 && t.ty != Tint8 && t.ty != Tuns8)
+ errorMsg(null, e, fmt == Format.hhu ? "ubyte" : "byte", t);
+ break;
+
+ case Format.hu: // unsigned short int
+ case Format.hd: // short int
+ if (t.ty != Tint32 && t.ty != Tuns32 && t.ty != Tint16 && t.ty != Tuns16)
+ errorMsg(null, e, fmt == Format.hu ? "ushort" : "short", t);
+ break;
+
+ case Format.lu: // unsigned long int
+ case Format.ld: // long int
+ if (!(t.isintegral() && t.size() == c_longsize))
+ {
+ if (fmt == Format.lu)
+ errorMsg(null, e, (c_longsize == 4 ? "uint" : "ulong"), t);
+ else
+ errorMsg(null, e, (c_longsize == 4 ? "int" : "long"), t);
+ }
+ break;
+
+ case Format.llu: // unsigned long long int
+ case Format.lld: // long long int
+ if (t.ty != Tint64 && t.ty != Tuns64)
+ errorMsg(null, e, fmt == Format.llu ? "ulong" : "long", t);
+ break;
+
+ case Format.ju: // uintmax_t
+ case Format.jd: // intmax_t
+ if (t.ty != Tint64 && t.ty != Tuns64)
+ {
+ if (fmt == Format.ju)
+ errorMsg(null, e, "core.stdc.stdint.uintmax_t", t);
+ else
+ errorMsg(null, e, "core.stdc.stdint.intmax_t", t);
+ }
+ break;
+
+ case Format.zd: // size_t
+ if (!(t.isintegral() && t.size() == ptrsize))
+ errorMsg(null, e, "size_t", t);
+ break;
+
+ case Format.td: // ptrdiff_t
+ if (!(t.isintegral() && t.size() == ptrsize))
+ errorMsg(null, e, "ptrdiff_t", t);
+ break;
+
+ case Format.GNU_a: // Format.GNU_a is only for scanf
+ case Format.lg:
+ case Format.g: // double
+ if (t.ty != Tfloat64 && t.ty != Timaginary64)
+ errorMsg(null, e, "double", t);
+ break;
+
+ case Format.Lg: // long double
+ if (t.ty != Tfloat80 && t.ty != Timaginary80)
+ errorMsg(null, e, "real", t);
+ break;
+
+ case Format.p: // pointer
+ if (t.ty != Tpointer && t.ty != Tnull && t.ty != Tclass && t.ty != Tdelegate && t.ty != Taarray)
+ errorMsg(null, e, "void*", t);
+ break;
+
+ case Format.n: // pointer to int
+ if (!(t.ty == Tpointer && tnext.ty == Tint32))
+ errorMsg(null, e, "int*", t);
+ break;
+
+ case Format.ln: // pointer to long int
+ if (!(t.ty == Tpointer && tnext.isintegral() && tnext.size() == c_longsize))
+ errorMsg(null, e, (c_longsize == 4 ? "int*" : "long*"), t);
+ break;
+
+ case Format.lln: // pointer to long long int
+ if (!(t.ty == Tpointer && tnext.ty == Tint64))
+ errorMsg(null, e, "long*", t);
+ break;
+
+ case Format.hn: // pointer to short
+ if (!(t.ty == Tpointer && tnext.ty == Tint16))
+ errorMsg(null, e, "short*", t);
+ break;
+
+ case Format.hhn: // pointer to signed char
+ if (!(t.ty == Tpointer && tnext.ty == Tint16))
+ errorMsg(null, e, "byte*", t);
+ break;
+
+ case Format.jn: // pointer to intmax_t
+ if (!(t.ty == Tpointer && tnext.ty == Tint64))
+ errorMsg(null, e, "core.stdc.stdint.intmax_t*", t);
+ break;
+
+ case Format.zn: // pointer to size_t
+ if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == ptrsize))
+ errorMsg(null, e, "size_t*", t);
+ break;
+
+ case Format.tn: // pointer to ptrdiff_t
+ if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == ptrsize))
+ errorMsg(null, e, "ptrdiff_t*", t);
+ break;
+
+ case Format.c: // char
+ if (t.ty != Tint32 && t.ty != Tuns32)
+ errorMsg(null, e, "char", t);
+ break;
+
+ case Format.lc: // wint_t
+ if (t.ty != Tint32 && t.ty != Tuns32)
+ errorMsg(null, e, "wchar_t", t);
+ break;
+
+ case Format.s: // pointer to char string
+ if (!(t.ty == Tpointer && (tnext.ty == Tchar || tnext.ty == Tint8 || tnext.ty == Tuns8)))
+ errorMsg(null, e, "char*", t);
+ break;
+
+ case Format.ls: // pointer to wchar_t string
+ if (!(t.ty == Tpointer && tnext.ty.isSomeChar && tnext.size() == target.c.wchar_tsize))
+ errorMsg(null, e, "wchar_t*", t);
+ break;
+
+ case Format.error:
+ deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast(int)slice.length, slice.ptr);
+ break;
+
+ case Format.GNU_m:
+ break; // not assert(0) because it may go through it if there are extra arguments
+
+ case Format.percent:
+ assert(0);
+ }
+ }
+ return false;
+}
+
+/******************************************
+ * Check that arguments to a scanf format string are compatible
+ * with that string. Issue errors for incompatibilities.
+ *
+ * Follows the C99 specification for scanf.
+ *
+ * Takes a generous, rather than strict, view of compatiblity.
+ * For example, an unsigned value can be formatted with a signed specifier.
+ *
+ * Diagnosed incompatibilities are:
+ *
+ * 1. incompatible sizes which will cause argument misalignment
+ * 2. deferencing arguments that are not pointers
+ * 3. insufficient number of arguments
+ * 4. struct arguments
+ * 5. array and slice arguments
+ * 6. non-standard formats
+ * 7. undefined behavior per C99
+ *
+ * Per the C Standard, extra arguments are ignored.
+ *
+ * No attempt is made to fix the arguments or the format string.
+ *
+ * Params:
+ * loc = location for error messages
+ * format = format string
+ * args = arguments to match with format string
+ * isVa_list = if a "v" function (format check only)
+ *
+ * Returns:
+ * `true` if errors occurred
+ * References:
+ * C99 7.19.6.2
+ * http://www.cplusplus.com/reference/cstdio/scanf/
+ */
+bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list)
+{
+ size_t n = 0;
+ for (size_t i = 0; i < format.length;)
+ {
+ if (format[i] != '%')
+ {
+ ++i;
+ continue;
+ }
+ bool asterisk;
+ size_t j = i;
+ const fmt = parseScanfFormatSpecifier(format, j, asterisk);
+ const slice = format[i .. j];
+ i = j;
+
+ if (fmt == Format.percent || asterisk)
+ continue; // "%%", "%*": no arguments
+
+ if (isVa_list)
+ {
+ // format check only
+ if (fmt == Format.error)
+ deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast(int)slice.length, slice.ptr);
+ continue;
+ }
+
+ Expression getNextArg()
+ {
+ if (n == args.length)
+ {
+ if (!asterisk)
+ deprecation(loc, "more format specifiers than %d arguments", cast(int)n);
+ return null;
+ }
+ return args[n++];
+ }
+
+ void errorMsg(const char* prefix, Expression arg, const char* texpect, Type tactual)
+ {
+ deprecation(arg.loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`",
+ prefix ? prefix : "", arg.toChars(), cast(int)slice.length, slice.ptr, texpect, tactual.toChars());
+ }
+
+ auto e = getNextArg();
+ if (!e)
+ return true;
+
+ auto t = e.type.toBasetype();
+ auto tnext = t.nextOf();
+ const c_longsize = target.c.longsize;
+ const ptrsize = target.ptrsize;
+
+ final switch (fmt)
+ {
+ case Format.n:
+ case Format.d: // pointer to int
+ if (!(t.ty == Tpointer && tnext.ty == Tint32))
+ errorMsg(null, e, "int*", t);
+ break;
+
+ case Format.hhn:
+ case Format.hhd: // pointer to signed char
+ if (!(t.ty == Tpointer && tnext.ty == Tint16))
+ errorMsg(null, e, "byte*", t);
+ break;
+
+ case Format.hn:
+ case Format.hd: // pointer to short
+ if (!(t.ty == Tpointer && tnext.ty == Tint16))
+ errorMsg(null, e, "short*", t);
+ break;
+
+ case Format.ln:
+ case Format.ld: // pointer to long int
+ if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == c_longsize))
+ errorMsg(null, e, (c_longsize == 4 ? "int*" : "long*"), t);
+ break;
+
+ case Format.lln:
+ case Format.lld: // pointer to long long int
+ if (!(t.ty == Tpointer && tnext.ty == Tint64))
+ errorMsg(null, e, "long*", t);
+ break;
+
+ case Format.jn:
+ case Format.jd: // pointer to intmax_t
+ if (!(t.ty == Tpointer && tnext.ty == Tint64))
+ errorMsg(null, e, "core.stdc.stdint.intmax_t*", t);
+ break;
+
+ case Format.zn:
+ case Format.zd: // pointer to size_t
+ if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == ptrsize))
+ errorMsg(null, e, "size_t*", t);
+ break;
+
+ case Format.tn:
+ case Format.td: // pointer to ptrdiff_t
+ if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == ptrsize))
+ errorMsg(null, e, "ptrdiff_t*", t);
+ break;
+
+ case Format.u: // pointer to unsigned int
+ if (!(t.ty == Tpointer && tnext.ty == Tuns32))
+ errorMsg(null, e, "uint*", t);
+ break;
+
+ case Format.hhu: // pointer to unsigned char
+ if (!(t.ty == Tpointer && tnext.ty == Tuns8))
+ errorMsg(null, e, "ubyte*", t);
+ break;
+
+ case Format.hu: // pointer to unsigned short int
+ if (!(t.ty == Tpointer && tnext.ty == Tuns16))
+ errorMsg(null, e, "ushort*", t);
+ break;
+
+ case Format.lu: // pointer to unsigned long int
+ if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == c_longsize))
+ errorMsg(null, e, (c_longsize == 4 ? "uint*" : "ulong*"), t);
+ break;
+
+ case Format.llu: // pointer to unsigned long long int
+ if (!(t.ty == Tpointer && tnext.ty == Tuns64))
+ errorMsg(null, e, "ulong*", t);
+ break;
+
+ case Format.ju: // pointer to uintmax_t
+ if (!(t.ty == Tpointer && tnext.ty == Tuns64))
+ errorMsg(null, e, "core.stdc.stdint.uintmax_t*", t);
+ break;
+
+ case Format.g: // pointer to float
+ if (!(t.ty == Tpointer && tnext.ty == Tfloat32))
+ errorMsg(null, e, "float*", t);
+ break;
+
+ case Format.lg: // pointer to double
+ if (!(t.ty == Tpointer && tnext.ty == Tfloat64))
+ errorMsg(null, e, "double*", t);
+ break;
+
+ case Format.Lg: // pointer to long double
+ if (!(t.ty == Tpointer && tnext.ty == Tfloat80))
+ errorMsg(null, e, "real*", t);
+ break;
+
+ case Format.GNU_a:
+ case Format.GNU_m:
+ case Format.c:
+ case Format.s: // pointer to char string
+ if (!(t.ty == Tpointer && (tnext.ty == Tchar || tnext.ty == Tint8 || tnext.ty == Tuns8)))
+ errorMsg(null, e, "char*", t);
+ break;
+
+ case Format.lc:
+ case Format.ls: // pointer to wchar_t string
+ if (!(t.ty == Tpointer && tnext.ty.isSomeChar && tnext.size() == target.c.wchar_tsize))
+ errorMsg(null, e, "wchar_t*", t);
+ break;
+
+ case Format.p: // double pointer
+ if (!(t.ty == Tpointer && tnext.ty == Tpointer))
+ errorMsg(null, e, "void**", t);
+ break;
+
+ case Format.error:
+ deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast(int)slice.length, slice.ptr);
+ break;
+
+ case Format.percent:
+ assert(0);
+ }
+ }
+ return false;
+}
+
+private:
+
+/**************************************
+ * Parse the *format specifier* which is of the form:
+ *
+ * `%[*][width][length]specifier`
+ *
+ * Params:
+ * format = format string
+ * idx = index of `%` of start of format specifier,
+ * which gets updated to index past the end of it,
+ * even if `Format.error` is returned
+ * asterisk = set if there is a `*` sub-specifier
+ * Returns:
+ * Format
+ */
+Format parseScanfFormatSpecifier(scope const char[] format, ref size_t idx,
+ out bool asterisk) nothrow pure @safe
+{
+ auto i = idx;
+ assert(format[i] == '%');
+ const length = format.length;
+
+ Format error()
+ {
+ idx = i;
+ return Format.error;
+ }
+
+ ++i;
+ if (i == length)
+ return error();
+
+ if (format[i] == '%')
+ {
+ idx = i + 1;
+ return Format.percent;
+ }
+
+ // * sub-specifier
+ if (format[i] == '*')
+ {
+ ++i;
+ if (i == length)
+ return error();
+ asterisk = true;
+ }
+
+ // fieldWidth
+ while (isdigit(format[i]))
+ {
+ i++;
+ if (i == length)
+ return error();
+ }
+
+ /* Read the scanset
+ * A scanset can be anything, so we just check that it is paired
+ */
+ if (format[i] == '[')
+ {
+ while (i < length)
+ {
+ if (format[i] == ']')
+ break;
+ ++i;
+ }
+
+ // no `]` found
+ if (i == length)
+ return error();
+
+ ++i;
+ // no specifier after `]`
+ // it could be mixed with the one above, but then idx won't have the right index
+ if (i == length)
+ return error();
+ }
+
+ /* Read the specifier
+ */
+ char genSpec;
+ Format specifier = parseGenericFormatSpecifier(format, i, genSpec);
+ if (specifier == Format.error)
+ return error();
+
+ idx = i;
+ return specifier; // success
+}
+
+/**************************************
+ * Parse the *format specifier* which is of the form:
+ *
+ * `%[flags][field width][.precision][length modifier]specifier`
+ *
+ * Params:
+ * format = format string
+ * idx = index of `%` of start of format specifier,
+ * which gets updated to index past the end of it,
+ * even if `Format.error` is returned
+ * widthStar = set if * for width
+ * precisionStar = set if * for precision
+ * Returns:
+ * Format
+ */
+Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx,
+ out bool widthStar, out bool precisionStar) nothrow pure @safe
+{
+ auto i = idx;
+ assert(format[i] == '%');
+ const length = format.length;
+ bool hash;
+ bool zero;
+ bool flags;
+ bool width;
+ bool precision;
+
+ Format error()
+ {
+ idx = i;
+ return Format.error;
+ }
+
+ ++i;
+ if (i == length)
+ return error();
+
+ if (format[i] == '%')
+ {
+ idx = i + 1;
+ return Format.percent;
+ }
+
+ /* Read the `flags`
+ */
+ while (1)
+ {
+ const c = format[i];
+ if (c == '-' ||
+ c == '+' ||
+ c == ' ')
+ {
+ flags = true;
+ }
+ else if (c == '#')
+ {
+ hash = true;
+ }
+ else if (c == '0')
+ {
+ zero = true;
+ }
+ else
+ break;
+ ++i;
+ if (i == length)
+ return error();
+ }
+
+ /* Read the `field width`
+ */
+ {
+ const c = format[i];
+ if (c == '*')
+ {
+ width = true;
+ widthStar = true;
+ ++i;
+ if (i == length)
+ return error();
+ }
+ else if ('1' <= c && c <= '9')
+ {
+ width = true;
+ ++i;
+ if (i == length)
+ return error();
+ while ('0' <= format[i] && format[i] <= '9')
+ {
+ ++i;
+ if (i == length)
+ return error();
+ }
+ }
+ }
+
+ /* Read the `precision`
+ */
+ if (format[i] == '.')
+ {
+ precision = true;
+ ++i;
+ if (i == length)
+ return error();
+ const c = format[i];
+ if (c == '*')
+ {
+ precisionStar = true;
+ ++i;
+ if (i == length)
+ return error();
+ }
+ else if ('0' <= c && c <= '9')
+ {
+ ++i;
+ if (i == length)
+ return error();
+ while ('0' <= format[i] && format[i] <= '9')
+ {
+ ++i;
+ if (i == length)
+ return error();
+ }
+ }
+ }
+
+ /* Read the specifier
+ */
+ char genSpec;
+ Format specifier = parseGenericFormatSpecifier(format, i, genSpec);
+ if (specifier == Format.error)
+ return error();
+
+ switch (genSpec)
+ {
+ case 'c':
+ case 's':
+ if (hash || zero)
+ return error();
+ break;
+
+ case 'd':
+ case 'i':
+ if (hash)
+ return error();
+ break;
+
+ case 'n':
+ if (hash || zero || precision || width || flags)
+ return error();
+ break;
+
+ default:
+ break;
+ }
+
+ idx = i;
+ return specifier; // success
+}
+
+/* Different kinds of formatting specifications, variations we don't
+ care about are merged. (Like we don't care about the difference between
+ f, e, g, a, etc.)
+
+ For `scanf`, every format is a pointer.
+ */
+enum Format
+{
+ d, // int
+ hhd, // signed char
+ hd, // short int
+ ld, // long int
+ lld, // long long int
+ jd, // intmax_t
+ zd, // size_t
+ td, // ptrdiff_t
+ u, // unsigned int
+ hhu, // unsigned char
+ hu, // unsigned short int
+ lu, // unsigned long int
+ llu, // unsigned long long int
+ ju, // uintmax_t
+ g, // float (scanf) / double (printf)
+ lg, // double (scanf)
+ Lg, // long double (both)
+ s, // char string (both)
+ ls, // wchar_t string (both)
+ c, // char (printf)
+ lc, // wint_t (printf)
+ p, // pointer
+ n, // pointer to int
+ hhn, // pointer to signed char
+ hn, // pointer to short
+ ln, // pointer to long int
+ lln, // pointer to long long int
+ jn, // pointer to intmax_t
+ zn, // pointer to size_t
+ tn, // pointer to ptrdiff_t
+ GNU_a, // GNU ext. : address to a string with no maximum size (scanf)
+ GNU_m, // GNU ext. : string corresponding to the error code in errno (printf) / length modifier (scanf)
+ percent, // %% (i.e. no argument)
+ error, // invalid format specification
+}
+
+/**************************************
+ * Parse the *length specifier* and the *specifier* of the following form:
+ * `[length]specifier`
+ *
+ * Params:
+ * format = format string
+ * idx = index of of start of format specifier,
+ * which gets updated to index past the end of it,
+ * even if `Format.error` is returned
+ * genSpecifier = Generic specifier. For instance, it will be set to `d` if the
+ * format is `hdd`.
+ * Returns:
+ * Format
+ */
+Format parseGenericFormatSpecifier(scope const char[] format,
+ ref size_t idx, out char genSpecifier, bool useGNUExts =
+ findCondition(global.versionids, Identifier.idPool("CRuntime_Glibc"))) nothrow pure @trusted
+{
+ const length = format.length;
+
+ /* Read the `length modifier`
+ */
+ const lm = format[idx];
+ bool lm1; // if jztL
+ bool lm2; // if `hh` or `ll`
+ if (lm == 'j' ||
+ lm == 'z' ||
+ lm == 't' ||
+ lm == 'L')
+ {
+ ++idx;
+ if (idx == length)
+ return Format.error;
+ lm1 = true;
+ }
+ else if (lm == 'h' || lm == 'l')
+ {
+ ++idx;
+ if (idx == length)
+ return Format.error;
+ lm2 = lm == format[idx];
+ if (lm2)
+ {
+ ++idx;
+ if (idx == length)
+ return Format.error;
+ }
+ }
+
+ /* Read the `specifier`
+ */
+ Format specifier;
+ const sc = format[idx];
+ genSpecifier = sc;
+ switch (sc)
+ {
+ case 'd':
+ case 'i':
+ if (lm == 'L')
+ specifier = Format.error;
+ else
+ specifier = lm == 'h' && lm2 ? Format.hhd :
+ lm == 'h' ? Format.hd :
+ lm == 'l' && lm2 ? Format.lld :
+ lm == 'l' ? Format.ld :
+ lm == 'j' ? Format.jd :
+ lm == 'z' ? Format.zd :
+ lm == 't' ? Format.td :
+ Format.d;
+ break;
+
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ if (lm == 'L')
+ specifier = Format.error;
+ else
+ specifier = lm == 'h' && lm2 ? Format.hhu :
+ lm == 'h' ? Format.hu :
+ lm == 'l' && lm2 ? Format.llu :
+ lm == 'l' ? Format.lu :
+ lm == 'j' ? Format.ju :
+ lm == 'z' ? Format.zd :
+ lm == 't' ? Format.td :
+ Format.u;
+ break;
+
+ case 'a':
+ if (useGNUExts)
+ {
+ // https://www.gnu.org/software/libc/manual/html_node/Dynamic-String-Input.html
+ specifier = Format.GNU_a;
+ break;
+ }
+ goto case;
+
+ case 'f':
+ case 'F':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ case 'A':
+ if (lm == 'L')
+ specifier = Format.Lg;
+ else if (lm1 || lm2 || lm == 'h')
+ specifier = Format.error;
+ else
+ specifier = lm == 'l' ? Format.lg : Format.g;
+ break;
+
+ case 'c':
+ if (lm1 || lm2 || lm == 'h')
+ specifier = Format.error;
+ else
+ specifier = lm == 'l' ? Format.lc : Format.c;
+ break;
+
+ case 's':
+ if (lm1 || lm2 || lm == 'h')
+ specifier = Format.error;
+ else
+ specifier = lm == 'l' ? Format.ls : Format.s;
+ break;
+
+ case 'p':
+ if (lm1 || lm2 || lm == 'h' || lm == 'l')
+ specifier = Format.error;
+ else
+ specifier = Format.p;
+ break;
+
+ case 'n':
+ if (lm == 'L')
+ specifier = Format.error;
+ else
+ specifier = lm == 'l' && lm2 ? Format.lln :
+ lm == 'l' ? Format.ln :
+ lm == 'h' && lm2 ? Format.hhn :
+ lm == 'h' ? Format.hn :
+ lm == 'j' ? Format.jn :
+ lm == 'z' ? Format.zn :
+ lm == 't' ? Format.tn :
+ Format.n;
+ break;
+
+ case 'm':
+ if (useGNUExts)
+ {
+ // http://www.gnu.org/software/libc/manual/html_node/Other-Output-Conversions.html
+ specifier = Format.GNU_m;
+ break;
+ }
+ goto default;
+
+ default:
+ specifier = Format.error;
+ break;
+ }
+
+ ++idx;
+ return specifier; // success
+}
+
+unittest
+{
+ /* parseGenericFormatSpecifier
+ */
+
+ char genSpecifier;
+ size_t idx;
+
+ assert(parseGenericFormatSpecifier("hhd", idx, genSpecifier) == Format.hhd);
+ assert(genSpecifier == 'd');
+
+ idx = 0;
+ assert(parseGenericFormatSpecifier("hn", idx, genSpecifier) == Format.hn);
+ assert(genSpecifier == 'n');
+
+ idx = 0;
+ assert(parseGenericFormatSpecifier("ji", idx, genSpecifier) == Format.jd);
+ assert(genSpecifier == 'i');
+
+ idx = 0;
+ assert(parseGenericFormatSpecifier("lu", idx, genSpecifier) == Format.lu);
+ assert(genSpecifier == 'u');
+
+ idx = 0;
+ assert(parseGenericFormatSpecifier("k", idx, genSpecifier) == Format.error);
+
+ /* parsePrintfFormatSpecifier
+ */
+
+ bool widthStar;
+ bool precisionStar;
+
+ // one for each Format
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%d", idx, widthStar, precisionStar) == Format.d);
+ assert(idx == 2);
+ assert(!widthStar && !precisionStar);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%ld", idx, widthStar, precisionStar) == Format.ld);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%lld", idx, widthStar, precisionStar) == Format.lld);
+ assert(idx == 4);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%jd", idx, widthStar, precisionStar) == Format.jd);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%zd", idx, widthStar, precisionStar) == Format.zd);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%td", idx, widthStar, precisionStar) == Format.td);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%g", idx, widthStar, precisionStar) == Format.g);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%Lg", idx, widthStar, precisionStar) == Format.Lg);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%p", idx, widthStar, precisionStar) == Format.p);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%n", idx, widthStar, precisionStar) == Format.n);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%ln", idx, widthStar, precisionStar) == Format.ln);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%lln", idx, widthStar, precisionStar) == Format.lln);
+ assert(idx == 4);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%hn", idx, widthStar, precisionStar) == Format.hn);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%hhn", idx, widthStar, precisionStar) == Format.hhn);
+ assert(idx == 4);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%jn", idx, widthStar, precisionStar) == Format.jn);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%zn", idx, widthStar, precisionStar) == Format.zn);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%tn", idx, widthStar, precisionStar) == Format.tn);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%c", idx, widthStar, precisionStar) == Format.c);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%lc", idx, widthStar, precisionStar) == Format.lc);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%s", idx, widthStar, precisionStar) == Format.s);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%ls", idx, widthStar, precisionStar) == Format.ls);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%%", idx, widthStar, precisionStar) == Format.percent);
+ assert(idx == 2);
+
+ // Synonyms
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%i", idx, widthStar, precisionStar) == Format.d);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%u", idx, widthStar, precisionStar) == Format.u);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%o", idx, widthStar, precisionStar) == Format.u);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%x", idx, widthStar, precisionStar) == Format.u);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%X", idx, widthStar, precisionStar) == Format.u);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%f", idx, widthStar, precisionStar) == Format.g);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%F", idx, widthStar, precisionStar) == Format.g);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%G", idx, widthStar, precisionStar) == Format.g);
+ assert(idx == 2);
+
+ idx = 0;
+ Format g = parsePrintfFormatSpecifier("%a", idx, widthStar, precisionStar);
+ assert(g == Format.g || g == Format.GNU_a);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%A", idx, widthStar, precisionStar) == Format.g);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%lg", idx, widthStar, precisionStar) == Format.lg);
+ assert(idx == 3);
+
+ // width, precision
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%*d", idx, widthStar, precisionStar) == Format.d);
+ assert(idx == 3);
+ assert(widthStar && !precisionStar);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%.*d", idx, widthStar, precisionStar) == Format.d);
+ assert(idx == 4);
+ assert(!widthStar && precisionStar);
+
+ idx = 0;
+ assert(parsePrintfFormatSpecifier("%*.*d", idx, widthStar, precisionStar) == Format.d);
+ assert(idx == 5);
+ assert(widthStar && precisionStar);
+
+ // Too short formats
+ {
+ foreach (s; ["%", "%-", "%+", "% ", "%#", "%0", "%*", "%1", "%19", "%.", "%.*", "%.1", "%.12",
+ "%j", "%z", "%t", "%l", "%h", "%ll", "%hh"])
+ {
+ idx = 0;
+ assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar) == Format.error);
+ assert(idx == s.length);
+ }
+ }
+
+ // Undefined format combinations
+ {
+ foreach (s; ["%#d", "%llg", "%jg", "%zg", "%tg", "%hg", "%hhg",
+ "%#c", "%0c", "%jc", "%zc", "%tc", "%Lc", "%hc", "%hhc", "%llc",
+ "%#s", "%0s", "%js", "%zs", "%ts", "%Ls", "%hs", "%hhs", "%lls",
+ "%jp", "%zp", "%tp", "%Lp", "%hp", "%lp", "%hhp", "%llp",
+ "%-n", "%+n", "% n", "%#n", "%0n", "%*n", "%1n", "%19n", "%.n", "%.*n", "%.1n", "%.12n", "%Ln", "%K"])
+ {
+ idx = 0;
+ assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar) == Format.error);
+ assert(idx == s.length);
+ }
+ }
+
+ /* parseScanfFormatSpecifier
+ */
+
+ bool asterisk;
+
+ // one for each Format
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%d", idx, asterisk) == Format.d);
+ assert(idx == 2);
+ assert(!asterisk);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%hhd", idx, asterisk) == Format.hhd);
+ assert(idx == 4);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%hd", idx, asterisk) == Format.hd);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%ld", idx, asterisk) == Format.ld);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%lld", idx, asterisk) == Format.lld);
+ assert(idx == 4);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%jd", idx, asterisk) == Format.jd);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%zd", idx, asterisk) == Format.zd);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%td", idx, asterisk,) == Format.td);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%u", idx, asterisk) == Format.u);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%hhu", idx, asterisk,) == Format.hhu);
+ assert(idx == 4);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%hu", idx, asterisk) == Format.hu);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%lu", idx, asterisk) == Format.lu);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%llu", idx, asterisk) == Format.llu);
+ assert(idx == 4);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%ju", idx, asterisk) == Format.ju);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%g", idx, asterisk) == Format.g);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%lg", idx, asterisk) == Format.lg);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%Lg", idx, asterisk) == Format.Lg);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%p", idx, asterisk) == Format.p);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%s", idx, asterisk) == Format.s);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%ls", idx, asterisk,) == Format.ls);
+ assert(idx == 3);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%%", idx, asterisk) == Format.percent);
+ assert(idx == 2);
+
+ // Synonyms
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%i", idx, asterisk) == Format.d);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%n", idx, asterisk) == Format.n);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%o", idx, asterisk) == Format.u);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%x", idx, asterisk) == Format.u);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%f", idx, asterisk) == Format.g);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%e", idx, asterisk) == Format.g);
+ assert(idx == 2);
+
+ idx = 0;
+ g = parseScanfFormatSpecifier("%a", idx, asterisk);
+ assert(g == Format.g || g == Format.GNU_a);
+ assert(idx == 2);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%c", idx, asterisk) == Format.c);
+ assert(idx == 2);
+
+ // asterisk
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%*d", idx, asterisk) == Format.d);
+ assert(idx == 3);
+ assert(asterisk);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%9ld", idx, asterisk) == Format.ld);
+ assert(idx == 4);
+ assert(!asterisk);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%*25984hhd", idx, asterisk) == Format.hhd);
+ assert(idx == 10);
+ assert(asterisk);
+
+ // scansets
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%[a-zA-Z]s", idx, asterisk) == Format.s);
+ assert(idx == 10);
+ assert(!asterisk);
+
+ idx = 0;
+ assert(parseScanfFormatSpecifier("%*25[a-z]hhd", idx, asterisk) == Format.hhd);
+ assert(idx == 12);
+ assert(asterisk);
+
+ // Too short formats
+ foreach (s; ["%", "% ", "%#", "%0", "%*", "%1", "%19",
+ "%j", "%z", "%t", "%l", "%h", "%ll", "%hh", "%K"])
+ {
+ idx = 0;
+ assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error);
+ assert(idx == s.length);
+ }
+
+
+ // Undefined format combinations
+ foreach (s; ["%Ld", "%llg", "%jg", "%zg", "%tg", "%hg", "%hhg",
+ "%jc", "%zc", "%tc", "%Lc", "%hc", "%hhc", "%llc",
+ "%jp", "%zp", "%tp", "%Lp", "%hp", "%lp", "%hhp", "%llp",
+ "%-", "%+", "%#", "%0", "%.", "%Ln"])
+ {
+ idx = 0;
+ assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error);
+ assert(idx == s.length);
+
+ }
+
+ // Invalid scansets
+ foreach (s; ["%[]", "%[s", "%[0-9lld", "%[", "%[a-z]"])
+ {
+ idx = 0;
+ assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error);
+ assert(idx == s.length);
+ }
+
+}
diff --git a/gcc/d/dmd/clone.c b/gcc/d/dmd/clone.c
deleted file mode 100644
index eb09076b2d2..00000000000
--- a/gcc/d/dmd/clone.c
+++ /dev/null
@@ -1,1179 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/clone.c
- */
-
-#include "root/dsystem.h"
-#include "root/root.h"
-
-#include "aggregate.h"
-#include "scope.h"
-#include "mtype.h"
-#include "declaration.h"
-#include "module.h"
-#include "id.h"
-#include "expression.h"
-#include "statement.h"
-#include "init.h"
-#include "template.h"
-#include "tokens.h"
-
-/*******************************************
- * Merge function attributes pure, nothrow, @safe, @nogc, and @disable
- */
-StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f)
-{
- if (!f)
- return s1;
-
- StorageClass s2 = (f->storage_class & STCdisable);
- TypeFunction *tf = (TypeFunction *)f->type;
- if (tf->trust == TRUSTsafe)
- s2 |= STCsafe;
- else if (tf->trust == TRUSTsystem)
- s2 |= STCsystem;
- else if (tf->trust == TRUSTtrusted)
- s2 |= STCtrusted;
- if (tf->purity != PUREimpure)
- s2 |= STCpure;
- if (tf->isnothrow)
- s2 |= STCnothrow;
- if (tf->isnogc)
- s2 |= STCnogc;
-
- StorageClass stc = 0;
- StorageClass sa = s1 & s2;
- StorageClass so = s1 | s2;
-
- if (so & STCsystem)
- stc |= STCsystem;
- else if (sa & STCtrusted)
- stc |= STCtrusted;
- else if ((so & (STCtrusted | STCsafe)) == (STCtrusted | STCsafe))
- stc |= STCtrusted;
- else if (sa & STCsafe)
- stc |= STCsafe;
-
- if (sa & STCpure)
- stc |= STCpure;
-
- if (sa & STCnothrow)
- stc |= STCnothrow;
-
- if (sa & STCnogc)
- stc |= STCnogc;
-
- if (so & STCdisable)
- stc |= STCdisable;
-
- return stc;
-}
-
-/*******************************************
- * Check given aggregate actually has an identity opAssign or not.
- * Params:
- * ad = struct or class
- * sc = current scope
- * Returns:
- * if found, returns FuncDeclaration of opAssign, otherwise null
- */
-FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc)
-{
- Dsymbol *assign = search_function(ad, Id::assign);
- if (assign)
- {
- /* check identity opAssign exists
- */
- UnionExp er; new(&er) NullExp(ad->loc, ad->type); // dummy rvalue
- UnionExp el; new(&el) IdentifierExp(ad->loc, Id::p); // dummy lvalue
- el.exp()->type = ad->type;
- Expressions a;
- a.setDim(1);
-
- unsigned errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
- sc = sc->push();
- sc->tinst = NULL;
- sc->minst = NULL;
-
- a[0] = er.exp();
- FuncDeclaration *f = resolveFuncCall(ad->loc, sc, assign, NULL, ad->type, &a, 1);
- if (!f)
- {
- a[0] = el.exp();
- f = resolveFuncCall(ad->loc, sc, assign, NULL, ad->type, &a, 1);
- }
-
- sc = sc->pop();
- global.endGagging(errors);
-
- if (f)
- {
- if (f->errors)
- return NULL;
- ParameterList fparams = f->getParameterList();
- if (fparams.length())
- {
- Parameter *fparam0 = fparams[0];
- if (fparam0->type->toDsymbol(NULL) != ad)
- f = NULL;
- }
- }
- // BUGS: This detection mechanism cannot find some opAssign-s like follows:
- // struct S { void opAssign(ref immutable S) const; }
- return f;
- }
- return NULL;
-}
-
-/*******************************************
- * We need an opAssign for the struct if
- * it has a destructor or a postblit.
- * We need to generate one if a user-specified one does not exist.
- */
-bool needOpAssign(StructDeclaration *sd)
-{
- //printf("StructDeclaration::needOpAssign() %s\n", sd->toChars());
- if (sd->isUnionDeclaration())
- return false;
-
- if (sd->hasIdentityAssign)
- goto Lneed; // because has identity==elaborate opAssign
-
- if (sd->dtor || sd->postblit)
- goto Lneed;
-
- /* If any of the fields need an opAssign, then we
- * need it too.
- */
- for (size_t i = 0; i < sd->fields.length; i++)
- {
- VarDeclaration *v = sd->fields[i];
- if (v->storage_class & STCref)
- continue;
- if (v->overlapped) // if field of a union
- continue; // user must handle it themselves
- Type *tv = v->type->baseElemOf();
- if (tv->ty == Tstruct)
- {
- TypeStruct *ts = (TypeStruct *)tv;
- if (ts->sym->isUnionDeclaration())
- continue;
- if (needOpAssign(ts->sym))
- goto Lneed;
- }
- }
- //printf("\tdontneed\n");
- return false;
-
-Lneed:
- //printf("\tneed\n");
- return true;
-}
-
-/******************************************
- * Build opAssign for struct.
- * ref S opAssign(S s) { ... }
- *
- * Note that s will be constructed onto the stack, and probably
- * copy-constructed in caller site.
- *
- * If S has copy copy construction and/or destructor,
- * the body will make bit-wise object swap:
- * S __swap = this; // bit copy
- * this = s; // bit copy
- * __swap.dtor();
- * Instead of running the destructor on s, run it on tmp instead.
- *
- * Otherwise, the body will make member-wise assignments:
- * Then, the body is:
- * this.field1 = s.field1;
- * this.field2 = s.field2;
- * ...;
- */
-FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc)
-{
- if (FuncDeclaration *f = hasIdentityOpAssign(sd, sc))
- {
- sd->hasIdentityAssign = true;
- return f;
- }
- // Even if non-identity opAssign is defined, built-in identity opAssign
- // will be defined.
-
- if (!needOpAssign(sd))
- return NULL;
-
- //printf("StructDeclaration::buildOpAssign() %s\n", sd->toChars());
- StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc;
- Loc declLoc = sd->loc;
- Loc loc = Loc(); // internal code should have no loc to prevent coverage
-
- // One of our sub-field might have `@disable opAssign` so we need to
- // check for it.
- // In this event, it will be reflected by having `stc` (opAssign's
- // storage class) include `STCdisabled`.
- for (size_t i = 0; i < sd->fields.length; i++)
- {
- VarDeclaration *v = sd->fields[i];
- if (v->storage_class & STCref)
- continue;
- if (v->overlapped)
- continue;
- Type *tv = v->type->baseElemOf();
- if (tv->ty != Tstruct)
- continue;
-
- StructDeclaration *sdv = ((TypeStruct *)tv)->sym;
- stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc));
- }
-
- if (sd->dtor || sd->postblit)
- {
- if (!sd->type->isAssignable()) // Bugzilla 13044
- return NULL;
- stc = mergeFuncAttrs(stc, sd->dtor);
- if (stc & STCsafe)
- stc = (stc & ~STCsafe) | STCtrusted;
- }
-
- Parameters *fparams = new Parameters;
- fparams->push(new Parameter(STCnodtor, sd->type, Id::p, NULL, NULL));
- TypeFunction *tf = new TypeFunction(ParameterList(fparams), sd->handleType(), LINKd, stc | STCref);
-
- FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), Id::assign, stc, tf);
- fop->storage_class |= STCinference;
- fop->generated = true;
- Expression *e = NULL;
- if (stc & STCdisable)
- {
- }
- else if (sd->dtor || sd->postblit)
- {
- /* Do swap this and rhs.
- * __swap = this; this = s; __swap.dtor();
- */
- //printf("\tswap copy\n");
- Identifier *idtmp = Identifier::generateId("__swap");
- VarDeclaration *tmp = NULL;
- AssignExp *ec = NULL;
- if (sd->dtor)
- {
- tmp = new VarDeclaration(loc, sd->type, idtmp, new VoidInitializer(loc));
- tmp->storage_class |= STCnodtor | STCtemp | STCctfe;
- e = new DeclarationExp(loc, tmp);
- ec = new BlitExp(loc, new VarExp(loc, tmp), new ThisExp(loc));
- e = Expression::combine(e, ec);
- }
- ec = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id::p));
- e = Expression::combine(e, ec);
- if (sd->dtor)
- {
- /* Instead of running the destructor on s, run it
- * on tmp. This avoids needing to copy tmp back in to s.
- */
- Expression *ec2 = new DotVarExp(loc, new VarExp(loc, tmp), sd->dtor, false);
- ec2 = new CallExp(loc, ec2);
- e = Expression::combine(e, ec2);
- }
- }
- else
- {
- /* Do memberwise copy.
- *
- * If sd is a nested struct, its vthis field assignment is:
- * 1. If it's nested in a class, it's a rebind of class reference.
- * 2. If it's nested in a function or struct, it's an update of void*.
- * In both cases, it will change the parent context.
- */
- //printf("\tmemberwise copy\n");
- for (size_t i = 0; i < sd->fields.length; i++)
- {
- VarDeclaration *v = sd->fields[i];
- // this.v = s.v;
- AssignExp *ec = new AssignExp(loc,
- new DotVarExp(loc, new ThisExp(loc), v),
- new DotVarExp(loc, new IdentifierExp(loc, Id::p), v));
- e = Expression::combine(e, ec);
- }
- }
- if (e)
- {
- Statement *s1 = new ExpStatement(loc, e);
-
- /* Add:
- * return this;
- */
- e = new ThisExp(loc);
- Statement *s2 = new ReturnStatement(loc, e);
-
- fop->fbody = new CompoundStatement(loc, s1, s2);
- tf->isreturn = true;
- }
-
- sd->members->push(fop);
- fop->addMember(sc, sd);
- sd->hasIdentityAssign = true; // temporary mark identity assignable
-
- unsigned errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
- Scope *sc2 = sc->push();
- sc2->stc = 0;
- sc2->linkage = LINKd;
-
- dsymbolSemantic(fop, sc2);
- semantic2(fop, sc2);
- // Bugzilla 15044: fop->semantic3 isn't run here for lazy forward reference resolution.
-
- sc2->pop();
- if (global.endGagging(errors)) // if errors happened
- {
- // Disable generated opAssign, because some members forbid identity assignment.
- fop->storage_class |= STCdisable;
- fop->fbody = NULL; // remove fbody which contains the error
- }
-
- //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd->toChars(), (fop->storage_class & STCdisable) != 0);
-
- return fop;
-}
-
-/*******************************************
- * We need an opEquals for the struct if
- * any fields has an opEquals.
- * Generate one if a user-specified one does not exist.
- */
-bool needOpEquals(StructDeclaration *sd)
-{
- //printf("StructDeclaration::needOpEquals() %s\n", sd->toChars());
- if (sd->isUnionDeclaration())
- goto Ldontneed;
-
- if (sd->hasIdentityEquals)
- goto Lneed;
-
- /* If any of the fields has an opEquals, then we
- * need it too.
- */
- for (size_t i = 0; i < sd->fields.length; i++)
- {
- VarDeclaration *v = sd->fields[i];
- if (v->storage_class & STCref)
- continue;
- if (v->overlapped)
- continue;
- Type *tv = v->type->toBasetype();
- Type *tvbase = tv->baseElemOf();
- if (tvbase->ty == Tstruct)
- {
- TypeStruct *ts = (TypeStruct *)tvbase;
- if (ts->sym->isUnionDeclaration())
- continue;
- if (needOpEquals(ts->sym))
- goto Lneed;
- if (ts->sym->aliasthis) // Bugzilla 14806
- goto Lneed;
- }
- if (tv->isfloating())
- {
- // This is necessray for:
- // 1. comparison of +0.0 and -0.0 should be true.
- // 2. comparison of NANs should be false always.
- goto Lneed;
- }
- if (tv->ty == Tarray)
- goto Lneed;
- if (tv->ty == Taarray)
- goto Lneed;
- if (tv->ty == Tclass)
- goto Lneed;
- }
-Ldontneed:
- //printf("\tdontneed\n");
- return false;
-
-Lneed:
- //printf("\tneed\n");
- return true;
-}
-
-/*******************************************
- * Check given aggregate actually has an identity opEquals or not.
- */
-FuncDeclaration *hasIdentityOpEquals(AggregateDeclaration *ad, Scope *sc)
-{
- Dsymbol *eq = search_function(ad, Id::eq);
- if (eq)
- {
- /* check identity opEquals exists
- */
- UnionExp er; new(&er) NullExp(ad->loc, NULL); // dummy rvalue
- UnionExp el; new(&el) IdentifierExp(ad->loc, Id::p); // dummy lvalue
- Expressions a;
- a.setDim(1);
- for (size_t i = 0; i < 5; i++)
- {
- Type *tthis = NULL; // dead-store to prevent spurious warning
- switch (i)
- {
- case 0: tthis = ad->type; break;
- case 1: tthis = ad->type->constOf(); break;
- case 2: tthis = ad->type->immutableOf(); break;
- case 3: tthis = ad->type->sharedOf(); break;
- case 4: tthis = ad->type->sharedConstOf(); break;
- default: assert(0);
- }
- FuncDeclaration *f = NULL;
-
- unsigned errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
- sc = sc->push();
- sc->tinst = NULL;
- sc->minst = NULL;
-
- for (size_t j = 0; j < 2; j++)
- {
- a[0] = (j == 0 ? er.exp() : el.exp());
- a[0]->type = tthis;
- f = resolveFuncCall(ad->loc, sc, eq, NULL, tthis, &a, 1);
- if (f)
- break;
- }
-
- sc = sc->pop();
- global.endGagging(errors);
-
- if (f)
- {
- if (f->errors)
- return NULL;
- return f;
- }
- }
- }
- return NULL;
-}
-
-/******************************************
- * Build opEquals for struct.
- * const bool opEquals(const S s) { ... }
- *
- * By fixing bugzilla 3789, opEquals is changed to be never implicitly generated.
- * Now, struct objects comparison s1 == s2 is translated to:
- * s1.tupleof == s2.tupleof
- * to calculate structural equality. See EqualExp::op_overload.
- */
-FuncDeclaration *buildOpEquals(StructDeclaration *sd, Scope *sc)
-{
- if (hasIdentityOpEquals(sd, sc))
- {
- sd->hasIdentityEquals = true;
- }
- return NULL;
-}
-
-/******************************************
- * Build __xopEquals for TypeInfo_Struct
- * static bool __xopEquals(ref const S p, ref const S q)
- * {
- * return p == q;
- * }
- *
- * This is called by TypeInfo.equals(p1, p2). If the struct does not support
- * const objects comparison, it will throw "not implemented" Error in runtime.
- */
-FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc)
-{
- if (!needOpEquals(sd))
- return NULL; // bitwise comparison would work
-
- //printf("StructDeclaration::buildXopEquals() %s\n", sd->toChars());
- if (Dsymbol *eq = search_function(sd, Id::eq))
- {
- if (FuncDeclaration *fd = eq->isFuncDeclaration())
- {
- TypeFunction *tfeqptr;
- {
- Scope scx;
-
- /* const bool opEquals(ref const S s);
- */
- Parameters *parameters = new Parameters;
- parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL, NULL));
- tfeqptr = new TypeFunction(ParameterList(parameters), Type::tbool, LINKd);
- tfeqptr->mod = MODconst;
- tfeqptr = (TypeFunction *)typeSemantic(tfeqptr, Loc(), &scx);
- }
- fd = fd->overloadExactMatch(tfeqptr);
- if (fd)
- return fd;
- }
- }
-
- if (!sd->xerreq)
- {
- // object._xopEquals
- Identifier *id = Identifier::idPool("_xopEquals");
- Expression *e = new IdentifierExp(sd->loc, Id::empty);
- e = new DotIdExp(sd->loc, e, Id::object);
- e = new DotIdExp(sd->loc, e, id);
- e = expressionSemantic(e, sc);
- Dsymbol *s = getDsymbol(e);
- assert(s);
- sd->xerreq = s->isFuncDeclaration();
- }
-
- Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly
- Loc loc = Loc(); // loc is unnecessary so errors are gagged
-
- Parameters *parameters = new Parameters;
- parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL, NULL));
- parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL, NULL));
- TypeFunction *tf = new TypeFunction(ParameterList(parameters), Type::tbool, LINKd);
-
- Identifier *id = Id::xopEquals;
- FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf);
- fop->generated = true;
- Expression *e1 = new IdentifierExp(loc, Id::p);
- Expression *e2 = new IdentifierExp(loc, Id::q);
- Expression *e = new EqualExp(TOKequal, loc, e1, e2);
-
- fop->fbody = new ReturnStatement(loc, e);
-
- unsigned errors = global.startGagging(); // Do not report errors
- Scope *sc2 = sc->push();
- sc2->stc = 0;
- sc2->linkage = LINKd;
-
- dsymbolSemantic(fop, sc2);
- semantic2(fop, sc2);
-
- sc2->pop();
- if (global.endGagging(errors)) // if errors happened
- fop = sd->xerreq;
-
- return fop;
-}
-
-/******************************************
- * Build __xopCmp for TypeInfo_Struct
- * static bool __xopCmp(ref const S p, ref const S q)
- * {
- * return p.opCmp(q);
- * }
- *
- * This is called by TypeInfo.compare(p1, p2). If the struct does not support
- * const objects comparison, it will throw "not implemented" Error in runtime.
- */
-FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc)
-{
- //printf("StructDeclaration::buildXopCmp() %s\n", toChars());
- if (Dsymbol *cmp = search_function(sd, Id::cmp))
- {
- if (FuncDeclaration *fd = cmp->isFuncDeclaration())
- {
- TypeFunction *tfcmpptr;
- {
- Scope scx;
-
- /* const int opCmp(ref const S s);
- */
- Parameters *parameters = new Parameters;
- parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL, NULL));
- tfcmpptr = new TypeFunction(ParameterList(parameters), Type::tint32, LINKd);
- tfcmpptr->mod = MODconst;
- tfcmpptr = (TypeFunction *)typeSemantic(tfcmpptr, Loc(), &scx);
- }
- fd = fd->overloadExactMatch(tfcmpptr);
- if (fd)
- return fd;
- }
- }
- else
- {
- // FIXME: doesn't work for recursive alias this
- return NULL;
- }
-
- if (!sd->xerrcmp)
- {
- // object._xopCmp
- Identifier *id = Identifier::idPool("_xopCmp");
- Expression *e = new IdentifierExp(sd->loc, Id::empty);
- e = new DotIdExp(sd->loc, e, Id::object);
- e = new DotIdExp(sd->loc, e, id);
- e = expressionSemantic(e, sc);
- Dsymbol *s = getDsymbol(e);
- assert(s);
- sd->xerrcmp = s->isFuncDeclaration();
- }
-
- Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly
- Loc loc = Loc(); // loc is unnecessary so errors are gagged
-
- Parameters *parameters = new Parameters;
- parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL, NULL));
- parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL, NULL));
- TypeFunction *tf = new TypeFunction(ParameterList(parameters), Type::tint32, LINKd);
-
- Identifier *id = Id::xopCmp;
- FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf);
- fop->generated = true;
- Expression *e1 = new IdentifierExp(loc, Id::p);
- Expression *e2 = new IdentifierExp(loc, Id::q);
-#ifdef IN_GCC
- Expression *e = new CallExp(loc, new DotIdExp(loc, e1, Id::cmp), e2);
-#else
- Expression *e = new CallExp(loc, new DotIdExp(loc, e2, Id::cmp), e1);
-#endif
-
- fop->fbody = new ReturnStatement(loc, e);
-
- unsigned errors = global.startGagging(); // Do not report errors
- Scope *sc2 = sc->push();
- sc2->stc = 0;
- sc2->linkage = LINKd;
-
- dsymbolSemantic(fop, sc2);
- semantic2(fop, sc2);
-
- sc2->pop();
- if (global.endGagging(errors)) // if errors happened
- fop = sd->xerrcmp;
-
- return fop;
-}
-
-/*******************************************
- * We need a toHash for the struct if
- * any fields has a toHash.
- * Generate one if a user-specified one does not exist.
- */
-bool needToHash(StructDeclaration *sd)
-{
- //printf("StructDeclaration::needToHash() %s\n", sd->toChars());
- if (sd->isUnionDeclaration())
- goto Ldontneed;
-
- if (sd->xhash)
- goto Lneed;
-
- /* If any of the fields has an opEquals, then we
- * need it too.
- */
- for (size_t i = 0; i < sd->fields.length; i++)
- {
- VarDeclaration *v = sd->fields[i];
- if (v->storage_class & STCref)
- continue;
- if (v->overlapped)
- continue;
- Type *tv = v->type->toBasetype();
- Type *tvbase = tv->baseElemOf();
- if (tvbase->ty == Tstruct)
- {
- TypeStruct *ts = (TypeStruct *)tvbase;
- if (ts->sym->isUnionDeclaration())
- continue;
- if (needToHash(ts->sym))
- goto Lneed;
- if (ts->sym->aliasthis) // Bugzilla 14948
- goto Lneed;
- }
- if (tv->isfloating())
- {
- // This is necessray for:
- // 1. comparison of +0.0 and -0.0 should be true.
- goto Lneed;
- }
- if (tv->ty == Tarray)
- goto Lneed;
- if (tv->ty == Taarray)
- goto Lneed;
- if (tv->ty == Tclass)
- goto Lneed;
- }
-Ldontneed:
- //printf("\tdontneed\n");
- return false;
-
-Lneed:
- //printf("\tneed\n");
- return true;
-}
-
-/******************************************
- * Build __xtoHash for non-bitwise hashing
- * static hash_t xtoHash(ref const S p) nothrow @trusted;
- */
-FuncDeclaration *buildXtoHash(StructDeclaration *sd, Scope *sc)
-{
- if (Dsymbol *s = search_function(sd, Id::tohash))
- {
- static TypeFunction *tftohash;
- if (!tftohash)
- {
- tftohash = new TypeFunction(ParameterList(), Type::thash_t, LINKd);
- tftohash->mod = MODconst;
- tftohash = (TypeFunction *)tftohash->merge();
- }
-
- if (FuncDeclaration *fd = s->isFuncDeclaration())
- {
- fd = fd->overloadExactMatch(tftohash);
- if (fd)
- return fd;
- }
- }
-
- if (!needToHash(sd))
- return NULL;
-
- //printf("StructDeclaration::buildXtoHash() %s\n", sd->toPrettyChars());
- Loc declLoc = Loc(); // loc is unnecessary so __xtoHash is never called directly
- Loc loc = Loc(); // internal code should have no loc to prevent coverage
-
- Parameters *parameters = new Parameters();
- parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL, NULL));
- TypeFunction *tf = new TypeFunction(ParameterList(parameters), Type::thash_t,
- LINKd, STCnothrow | STCtrusted);
-
- Identifier *id = Id::xtoHash;
- FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf);
- fop->generated = true;
-
- /* Do memberwise hashing.
- *
- * If sd is a nested struct, and if it's nested in a class, the calculated
- * hash value will also contain the result of parent class's toHash().
- */
- const char *code =
- "size_t h = 0;"
- "foreach (i, T; typeof(p.tupleof))"
- " h += typeid(T).getHash(cast(const void*)&p.tupleof[i]);"
- "return h;";
- fop->fbody = new CompileStatement(loc, new StringExp(loc, const_cast<char *>(code)));
-
- Scope *sc2 = sc->push();
- sc2->stc = 0;
- sc2->linkage = LINKd;
-
- dsymbolSemantic(fop, sc2);
- semantic2(fop, sc2);
-
- sc2->pop();
-
- //printf("%s fop = %s %s\n", sd->toChars(), fop->toChars(), fop->type->toChars());
- return fop;
-}
-
-/*****************************************
- * Create inclusive postblit for struct by aggregating
- * all the postblits in postblits[] with the postblits for
- * all the members.
- * Note the close similarity with AggregateDeclaration::buildDtor(),
- * and the ordering changes (runs forward instead of backwards).
- */
-FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc)
-{
- //printf("StructDeclaration::buildPostBlit() %s\n", sd->toChars());
- if (sd->isUnionDeclaration())
- return NULL;
-
- StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc;
- Loc declLoc = sd->postblits.length ? sd->postblits[0]->loc : sd->loc;
- Loc loc = Loc(); // internal code should have no loc to prevent coverage
-
- for (size_t i = 0; i < sd->postblits.length; i++)
- {
- stc |= sd->postblits[i]->storage_class & STCdisable;
- }
-
- Statements *a = new Statements();
- for (size_t i = 0; i < sd->fields.length && !(stc & STCdisable); i++)
- {
- VarDeclaration *v = sd->fields[i];
- if (v->storage_class & STCref)
- continue;
- if (v->overlapped)
- continue;
- Type *tv = v->type->baseElemOf();
- if (tv->ty != Tstruct)
- continue;
- StructDeclaration *sdv = ((TypeStruct *)tv)->sym;
- if (!sdv->postblit)
- continue;
- assert(!sdv->isUnionDeclaration());
- sdv->postblit->functionSemantic();
-
- stc = mergeFuncAttrs(stc, sdv->postblit);
- stc = mergeFuncAttrs(stc, sdv->dtor);
- if (stc & STCdisable)
- {
- a->setDim(0);
- break;
- }
-
- Expression *ex = NULL;
- tv = v->type->toBasetype();
- if (tv->ty == Tstruct)
- {
- // this.v.__xpostblit()
-
- ex = new ThisExp(loc);
- ex = new DotVarExp(loc, ex, v);
-
- // This is a hack so we can call postblits on const/immutable objects.
- ex = new AddrExp(loc, ex);
- ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo());
- ex = new PtrExp(loc, ex);
- if (stc & STCsafe)
- stc = (stc & ~STCsafe) | STCtrusted;
-
- ex = new DotVarExp(loc, ex, sdv->postblit, false);
- ex = new CallExp(loc, ex);
- }
- else
- {
- // __ArrayPostblit((cast(S*)this.v.ptr)[0 .. n])
-
- uinteger_t n = tv->numberOfElems(loc);
- if (n == 0)
- continue;
-
- ex = new ThisExp(loc);
- ex = new DotVarExp(loc, ex, v);
-
- // This is a hack so we can call postblits on const/immutable objects.
- ex = new DotIdExp(loc, ex, Id::ptr);
- ex = new CastExp(loc, ex, sdv->type->pointerTo());
- if (stc & STCsafe)
- stc = (stc & ~STCsafe) | STCtrusted;
-
- ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t),
- new IntegerExp(loc, n, Type::tsize_t));
- // Prevent redundant bounds check
- ((SliceExp *)ex)->upperIsInBounds = true;
- ((SliceExp *)ex)->lowerIsLessThanUpper = true;
-
- ex = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayPostblit), ex);
- }
- a->push(new ExpStatement(loc, ex)); // combine in forward order
-
- /* Bugzilla 10972: When the following field postblit calls fail,
- * this field should be destructed for Exception Safety.
- */
- if (!sdv->dtor)
- continue;
- sdv->dtor->functionSemantic();
-
- tv = v->type->toBasetype();
- if (v->type->toBasetype()->ty == Tstruct)
- {
- // this.v.__xdtor()
-
- ex = new ThisExp(loc);
- ex = new DotVarExp(loc, ex, v);
-
- // This is a hack so we can call destructors on const/immutable objects.
- ex = new AddrExp(loc, ex);
- ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo());
- ex = new PtrExp(loc, ex);
- if (stc & STCsafe)
- stc = (stc & ~STCsafe) | STCtrusted;
-
- ex = new DotVarExp(loc, ex, sdv->dtor, false);
- ex = new CallExp(loc, ex);
- }
- else
- {
- // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
-
- uinteger_t n = tv->numberOfElems(loc);
- //if (n == 0)
- // continue;
-
- ex = new ThisExp(loc);
- ex = new DotVarExp(loc, ex, v);
-
- // This is a hack so we can call destructors on const/immutable objects.
- ex = new DotIdExp(loc, ex, Id::ptr);
- ex = new CastExp(loc, ex, sdv->type->pointerTo());
- if (stc & STCsafe)
- stc = (stc & ~STCsafe) | STCtrusted;
-
- ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t),
- new IntegerExp(loc, n, Type::tsize_t));
- // Prevent redundant bounds check
- ((SliceExp *)ex)->upperIsInBounds = true;
- ((SliceExp *)ex)->lowerIsLessThanUpper = true;
-
- ex = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayDtor), ex);
- }
- a->push(new ScopeGuardStatement(loc, TOKon_scope_failure, new ExpStatement(loc, ex)));
- }
-
- // Build our own "postblit" which executes a, but only if needed.
- if (a->length || (stc & STCdisable))
- {
- //printf("Building __fieldPostBlit()\n");
- PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__fieldPostblit);
- dd->generated = true;
- dd->storage_class |= STCinference;
- dd->fbody = (stc & STCdisable) ? NULL : new CompoundStatement(loc, a);
- sd->postblits.shift(dd);
- sd->members->push(dd);
- dsymbolSemantic(dd, sc);
- }
-
- FuncDeclaration *xpostblit = NULL;
- switch (sd->postblits.length)
- {
- case 0:
- break;
-
- case 1:
- xpostblit = sd->postblits[0];
- break;
-
- default:
- Expression *e = NULL;
- stc = STCsafe | STCnothrow | STCpure | STCnogc;
- for (size_t i = 0; i < sd->postblits.length; i++)
- {
- FuncDeclaration *fd = sd->postblits[i];
- stc = mergeFuncAttrs(stc, fd);
- if (stc & STCdisable)
- {
- e = NULL;
- break;
- }
- Expression *ex = new ThisExp(loc);
- ex = new DotVarExp(loc, ex, fd, false);
- ex = new CallExp(loc, ex);
- e = Expression::combine(e, ex);
- }
- PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__aggrPostblit);
- dd->storage_class |= STCinference;
- dd->fbody = new ExpStatement(loc, e);
- sd->members->push(dd);
- dsymbolSemantic(dd, sc);
- xpostblit = dd;
- break;
- }
- // Add an __xpostblit alias to make the inclusive postblit accessible
- if (xpostblit)
- {
- AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xpostblit, xpostblit);
- dsymbolSemantic(alias, sc);
- sd->members->push(alias);
- alias->addMember(sc, sd); // add to symbol table
- }
- return xpostblit;
-}
-
-/*****************************************
- * Create inclusive destructor for struct/class by aggregating
- * all the destructors in dtors[] with the destructors for
- * all the members.
- * Note the close similarity with StructDeclaration::buildPostBlit(),
- * and the ordering changes (runs backward instead of forwards).
- */
-FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc)
-{
- //printf("AggregateDeclaration::buildDtor() %s\n", ad->toChars());
- if (ad->isUnionDeclaration())
- return NULL;
-
- StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc;
- Loc declLoc = ad->dtors.length ? ad->dtors[0]->loc : ad->loc;
- Loc loc = Loc(); // internal code should have no loc to prevent coverage
-
- Expression *e = NULL;
- for (size_t i = 0; i < ad->fields.length; i++)
- {
- VarDeclaration *v = ad->fields[i];
- if (v->storage_class & STCref)
- continue;
- if (v->overlapped)
- continue;
- Type *tv = v->type->baseElemOf();
- if (tv->ty != Tstruct)
- continue;
- StructDeclaration *sdv = ((TypeStruct *)tv)->sym;
- if (!sdv->dtor)
- continue;
- sdv->dtor->functionSemantic();
-
- stc = mergeFuncAttrs(stc, sdv->dtor);
- if (stc & STCdisable)
- {
- e = NULL;
- break;
- }
-
- Expression *ex = NULL;
- tv = v->type->toBasetype();
- if (tv->ty == Tstruct)
- {
- // this.v.__xdtor()
-
- ex = new ThisExp(loc);
- ex = new DotVarExp(loc, ex, v);
-
- // This is a hack so we can call destructors on const/immutable objects.
- ex = new AddrExp(loc, ex);
- ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo());
- ex = new PtrExp(loc, ex);
- if (stc & STCsafe)
- stc = (stc & ~STCsafe) | STCtrusted;
-
- ex = new DotVarExp(loc, ex, sdv->dtor, false);
- ex = new CallExp(loc, ex);
- }
- else
- {
- // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
-
- uinteger_t n = tv->numberOfElems(loc);
- if (n == 0)
- continue;
-
- ex = new ThisExp(loc);
- ex = new DotVarExp(loc, ex, v);
-
- // This is a hack so we can call destructors on const/immutable objects.
- ex = new DotIdExp(loc, ex, Id::ptr);
- ex = new CastExp(loc, ex, sdv->type->pointerTo());
- if (stc & STCsafe)
- stc = (stc & ~STCsafe) | STCtrusted;
-
- ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t),
- new IntegerExp(loc, n, Type::tsize_t));
- // Prevent redundant bounds check
- ((SliceExp *)ex)->upperIsInBounds = true;
- ((SliceExp *)ex)->lowerIsLessThanUpper = true;
-
- ex = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayDtor), ex);
- }
- e = Expression::combine(ex, e); // combine in reverse order
- }
-
- /* Build our own "destructor" which executes e
- */
- if (e || (stc & STCdisable))
- {
- //printf("Building __fieldDtor()\n");
- DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__fieldDtor);
- dd->generated = true;
- dd->storage_class |= STCinference;
- dd->fbody = new ExpStatement(loc, e);
- ad->dtors.shift(dd);
- ad->members->push(dd);
- dsymbolSemantic(dd, sc);
- }
-
- FuncDeclaration *xdtor = NULL;
- switch (ad->dtors.length)
- {
- case 0:
- break;
-
- case 1:
- xdtor = ad->dtors[0];
- break;
-
- default:
- e = NULL;
- stc = STCsafe | STCnothrow | STCpure | STCnogc;
- for (size_t i = 0; i < ad->dtors.length; i++)
- {
- FuncDeclaration *fd = ad->dtors[i];
- stc = mergeFuncAttrs(stc, fd);
- if (stc & STCdisable)
- {
- e = NULL;
- break;
- }
- Expression *ex = new ThisExp(loc);
- ex = new DotVarExp(loc, ex, fd, false);
- ex = new CallExp(loc, ex);
- e = Expression::combine(ex, e);
- }
- DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__aggrDtor);
- dd->generated = true;
- dd->storage_class |= STCinference;
- dd->fbody = new ExpStatement(loc, e);
- ad->members->push(dd);
- dsymbolSemantic(dd, sc);
- xdtor = dd;
- break;
- }
- // Add an __xdtor alias to make the inclusive dtor accessible
- if (xdtor)
- {
- AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xdtor, xdtor);
- dsymbolSemantic(alias, sc);
- ad->members->push(alias);
- alias->addMember(sc, ad); // add to symbol table
- }
- return xdtor;
-}
-
-/******************************************
- * Create inclusive invariant for struct/class by aggregating
- * all the invariants in invs[].
- * void __invariant() const [pure nothrow @trusted]
- * {
- * invs[0](), invs[1](), ...;
- * }
- */
-FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc)
-{
- StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc;
- Loc declLoc = ad->loc;
- Loc loc = Loc(); // internal code should have no loc to prevent coverage
-
- switch (ad->invs.length)
- {
- case 0:
- return NULL;
-
- case 1:
- // Don't return invs[0] so it has uniquely generated name.
- /* fall through */
-
- default:
- Expression *e = NULL;
- StorageClass stcx = 0;
- for (size_t i = 0; i < ad->invs.length; i++)
- {
- stc = mergeFuncAttrs(stc, ad->invs[i]);
- if (stc & STCdisable)
- {
- // What should do?
- }
- StorageClass stcy = (ad->invs[i]->storage_class & STCsynchronized) |
- (ad->invs[i]->type->mod & MODshared ? STCshared : 0);
- if (i == 0)
- stcx = stcy;
- else if (stcx ^ stcy)
- {
- #if 1 // currently rejects
- ad->error(ad->invs[i]->loc, "mixing invariants with shared/synchronized differene is not supported");
- e = NULL;
- break;
- #endif
- }
- e = Expression::combine(e, new CallExp(loc, new VarExp(loc, ad->invs[i], false)));
- }
- InvariantDeclaration *inv;
- inv = new InvariantDeclaration(declLoc, Loc(), stc | stcx, Id::classInvariant);
- inv->fbody = new ExpStatement(loc, e);
- ad->members->push(inv);
- dsymbolSemantic(inv, sc);
- return inv;
- }
-}
diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d
new file mode 100644
index 00000000000..d3006170be7
--- /dev/null
+++ b/gcc/d/dmd/clone.d
@@ -0,0 +1,1695 @@
+/**
+ * Builds struct member functions if needed and not defined by the user.
+ * Includes `opEquals`, `opAssign`, post blit, copy constructor and destructor.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/clone.d, _clone.d)
+ * Documentation: https://dlang.org/phobos/dmd_clone.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/clone.d
+ */
+
+module dmd.clone;
+
+import core.stdc.stdio;
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.opover;
+import dmd.semantic2;
+import dmd.semantic3;
+import dmd.statement;
+import dmd.target;
+import dmd.typesem;
+import dmd.tokens;
+
+/*******************************************
+ * Merge function attributes pure, nothrow, @safe, @nogc, and @disable
+ * from f into s1.
+ * Params:
+ * s1 = storage class to merge into
+ * f = function
+ * Returns:
+ * merged storage class
+ */
+StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure
+{
+ if (!f)
+ return s1;
+ StorageClass s2 = (f.storage_class & STC.disable);
+
+ TypeFunction tf = cast(TypeFunction)f.type;
+ if (tf.trust == TRUST.safe)
+ s2 |= STC.safe;
+ else if (tf.trust == TRUST.system)
+ s2 |= STC.system;
+ else if (tf.trust == TRUST.trusted)
+ s2 |= STC.trusted;
+
+ if (tf.purity != PURE.impure)
+ s2 |= STC.pure_;
+ if (tf.isnothrow)
+ s2 |= STC.nothrow_;
+ if (tf.isnogc)
+ s2 |= STC.nogc;
+
+ const sa = s1 & s2;
+ const so = s1 | s2;
+
+ StorageClass stc = (sa & (STC.pure_ | STC.nothrow_ | STC.nogc)) | (so & STC.disable);
+
+ if (so & STC.system)
+ stc |= STC.system;
+ else if (sa & STC.trusted)
+ stc |= STC.trusted;
+ else if ((so & (STC.trusted | STC.safe)) == (STC.trusted | STC.safe))
+ stc |= STC.trusted;
+ else if (sa & STC.safe)
+ stc |= STC.safe;
+
+ return stc;
+}
+
+/*******************************************
+ * Check given aggregate actually has an identity opAssign or not.
+ * Params:
+ * ad = struct or class
+ * sc = current scope
+ * Returns:
+ * if found, returns FuncDeclaration of opAssign, otherwise null
+ */
+FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc)
+{
+ Dsymbol assign = search_function(ad, Id.assign);
+ if (assign)
+ {
+ /* check identity opAssign exists
+ */
+ scope er = new NullExp(ad.loc, ad.type); // dummy rvalue
+ scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
+ el.type = ad.type;
+ Expressions a;
+ a.setDim(1);
+ const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
+ sc = sc.push();
+ sc.tinst = null;
+ sc.minst = null;
+
+ a[0] = er;
+ auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, &a, FuncResolveFlag.quiet);
+ if (!f)
+ {
+ a[0] = el;
+ f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, &a, FuncResolveFlag.quiet);
+ }
+
+ sc = sc.pop();
+ global.endGagging(errors);
+ if (f)
+ {
+ if (f.errors)
+ return null;
+ auto fparams = f.getParameterList();
+ if (fparams.length)
+ {
+ auto fparam0 = fparams[0];
+ if (fparam0.type.toDsymbol(null) != ad)
+ f = null;
+ }
+ }
+ // BUGS: This detection mechanism cannot find some opAssign-s like follows:
+ // struct S { void opAssign(ref immutable S) const; }
+ return f;
+ }
+ return null;
+}
+
+/*******************************************
+ * We need an opAssign for the struct if
+ * it has a destructor or a postblit.
+ * We need to generate one if a user-specified one does not exist.
+ */
+private bool needOpAssign(StructDeclaration sd)
+{
+ //printf("StructDeclaration::needOpAssign() %s\n", sd.toChars());
+
+ static bool isNeeded()
+ {
+ //printf("\tneed\n");
+ return true;
+ }
+
+ if (sd.isUnionDeclaration())
+ return !isNeeded();
+
+ if (sd.hasIdentityAssign || // because has identity==elaborate opAssign
+ sd.dtor ||
+ sd.postblit)
+ return isNeeded();
+
+ /* If any of the fields need an opAssign, then we
+ * need it too.
+ */
+ foreach (v; sd.fields)
+ {
+ if (v.storage_class & STC.ref_)
+ continue;
+ if (v.overlapped) // if field of a union
+ continue; // user must handle it themselves
+ Type tv = v.type.baseElemOf();
+ if (tv.ty == Tstruct)
+ {
+ TypeStruct ts = cast(TypeStruct)tv;
+ if (ts.sym.isUnionDeclaration())
+ continue;
+ if (needOpAssign(ts.sym))
+ return isNeeded();
+ }
+ }
+ return !isNeeded();
+}
+
+/******************************************
+ * Build opAssign for a `struct`.
+ *
+ * The generated `opAssign` function has the following signature:
+ *---
+ *ref S opAssign(S s) // S is the name of the `struct`
+ *---
+ *
+ * The opAssign function will be built for a struct `S` if the
+ * following constraints are met:
+ *
+ * 1. `S` does not have an identity `opAssign` defined.
+ *
+ * 2. `S` has at least one of the following members: a postblit (user-defined or
+ * generated for fields that have a defined postblit), a destructor
+ * (user-defined or generated for fields that have a defined destructor)
+ * or at least one field that has a defined `opAssign`.
+ *
+ * 3. `S` does not have any non-mutable fields.
+ *
+ * If `S` has a disabled destructor or at least one field that has a disabled
+ * `opAssign`, `S.opAssign` is going to be generated, but marked with `@disable`
+ *
+ * If `S` defines a destructor, the generated code for `opAssign` is:
+ *
+ *---
+ *S __swap = void;
+ *__swap = this; // bit copy
+ *this = s; // bit copy
+ *__swap.dtor();
+ *---
+ *
+ * Otherwise, if `S` defines a postblit, the generated code for `opAssign` is:
+ *
+ *---
+ *this = s;
+ *---
+ *
+ * Note that the parameter to the generated `opAssign` is passed by value, which means
+ * that the postblit is going to be called (if it is defined) in both of the above
+ * situations before entering the body of `opAssign`. The assignments in the above generated
+ * function bodies are blit expressions, so they can be regarded as `memcpy`s
+ * (`opAssign` is not called as this will result in an infinite recursion; the postblit
+ * is not called because it has already been called when the parameter was passed by value).
+ *
+ * If `S` does not have a postblit or a destructor, but contains at least one field that defines
+ * an `opAssign` function (which is not disabled), then the body will make member-wise
+ * assignments:
+ *
+ *---
+ *this.field1 = s.field1;
+ *this.field2 = s.field2;
+ *...;
+ *---
+ *
+ * In this situation, the assignemnts are actual assign expressions (`opAssign` is used
+ * if defined).
+ *
+ * References:
+ * https://dlang.org/spec/struct.html#assign-overload
+ * Params:
+ * sd = struct to generate opAssign for
+ * sc = context
+ * Returns:
+ * generated `opAssign` function
+ */
+FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc)
+{
+ if (FuncDeclaration f = hasIdentityOpAssign(sd, sc))
+ {
+ sd.hasIdentityAssign = true;
+ return f;
+ }
+ // Even if non-identity opAssign is defined, built-in identity opAssign
+ // will be defined.
+ if (!needOpAssign(sd))
+ return null;
+
+ //printf("StructDeclaration::buildOpAssign() %s\n", sd.toChars());
+ StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
+ Loc declLoc = sd.loc;
+ Loc loc; // internal code should have no loc to prevent coverage
+
+ // One of our sub-field might have `@disable opAssign` so we need to
+ // check for it.
+ // In this event, it will be reflected by having `stc` (opAssign's
+ // storage class) include `STC.disabled`.
+ foreach (v; sd.fields)
+ {
+ if (v.storage_class & STC.ref_)
+ continue;
+ if (v.overlapped)
+ continue;
+ Type tv = v.type.baseElemOf();
+ if (tv.ty != Tstruct)
+ continue;
+ StructDeclaration sdv = (cast(TypeStruct)tv).sym;
+ stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc));
+ }
+
+ if (sd.dtor || sd.postblit)
+ {
+ // if the type is not assignable, we cannot generate opAssign
+ if (!sd.type.isAssignable()) // https://issues.dlang.org/show_bug.cgi?id=13044
+ return null;
+ stc = mergeFuncAttrs(stc, sd.dtor);
+ if (stc & STC.safe)
+ stc = (stc & ~STC.safe) | STC.trusted;
+ }
+
+ auto fparams = new Parameters();
+ fparams.push(new Parameter(STC.nodtor, sd.type, Id.p, null, null));
+ auto tf = new TypeFunction(ParameterList(fparams), sd.handleType(), LINK.d, stc | STC.ref_);
+ auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.assign, stc, tf);
+ fop.storage_class |= STC.inference;
+ fop.generated = true;
+ Expression e;
+ if (stc & STC.disable)
+ {
+ e = null;
+ }
+ /* Do swap this and rhs.
+ * __swap = this; this = s; __swap.dtor();
+ */
+ else if (sd.dtor)
+ {
+ //printf("\tswap copy\n");
+ TypeFunction tdtor = cast(TypeFunction)sd.dtor.type;
+ assert(tdtor.ty == Tfunction);
+
+ auto idswap = Identifier.generateId("__swap");
+ auto swap = new VarDeclaration(loc, sd.type, idswap, new VoidInitializer(loc));
+ swap.storage_class |= STC.nodtor | STC.temp | STC.ctfe;
+ if (tdtor.isScopeQual)
+ swap.storage_class |= STC.scope_;
+ auto e1 = new DeclarationExp(loc, swap);
+
+ auto e2 = new BlitExp(loc, new VarExp(loc, swap), new ThisExp(loc));
+ auto e3 = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p));
+
+ /* Instead of running the destructor on s, run it
+ * on swap. This avoids needing to copy swap back in to s.
+ */
+ auto e4 = new CallExp(loc, new DotVarExp(loc, new VarExp(loc, swap), sd.dtor, false));
+
+ e = Expression.combine(e1, e2, e3, e4);
+ }
+ /* postblit was called when the value was passed to opAssign, we just need to blit the result */
+ else if (sd.postblit)
+ {
+ e = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id.p));
+ sd.hasBlitAssign = true;
+ }
+ else
+ {
+ /* Do memberwise copy.
+ *
+ * If sd is a nested struct, its vthis field assignment is:
+ * 1. If it's nested in a class, it's a rebind of class reference.
+ * 2. If it's nested in a function or struct, it's an update of void*.
+ * In both cases, it will change the parent context.
+ */
+ //printf("\tmemberwise copy\n");
+ e = null;
+ foreach (v; sd.fields)
+ {
+ // this.v = s.v;
+ auto ec = new AssignExp(loc,
+ new DotVarExp(loc, new ThisExp(loc), v),
+ new DotVarExp(loc, new IdentifierExp(loc, Id.p), v));
+ e = Expression.combine(e, ec);
+ }
+ }
+ if (e)
+ {
+ Statement s1 = new ExpStatement(loc, e);
+ /* Add:
+ * return this;
+ */
+ auto er = new ThisExp(loc);
+ Statement s2 = new ReturnStatement(loc, er);
+ fop.fbody = new CompoundStatement(loc, s1, s2);
+ tf.isreturn = true;
+ }
+ sd.members.push(fop);
+ fop.addMember(sc, sd);
+ sd.hasIdentityAssign = true; // temporary mark identity assignable
+ const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
+ Scope* sc2 = sc.push();
+ sc2.stc = 0;
+ sc2.linkage = LINK.d;
+ fop.dsymbolSemantic(sc2);
+ fop.semantic2(sc2);
+ // https://issues.dlang.org/show_bug.cgi?id=15044
+ //semantic3(fop, sc2); // isn't run here for lazy forward reference resolution.
+
+ sc2.pop();
+ if (global.endGagging(errors)) // if errors happened
+ {
+ // Disable generated opAssign, because some members forbid identity assignment.
+ fop.storage_class |= STC.disable;
+ fop.fbody = null; // remove fbody which contains the error
+ }
+
+ //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd.toChars(), (fop.storage_class & STC.disable) != 0);
+ //printf("fop.type: %s\n", fop.type.toPrettyChars());
+ return fop;
+}
+
+/*******************************************
+ * We need an opEquals for the struct if
+ * any fields has an opEquals.
+ * Generate one if a user-specified one does not exist.
+ */
+bool needOpEquals(StructDeclaration sd)
+{
+ //printf("StructDeclaration::needOpEquals() %s\n", sd.toChars());
+ if (sd.isUnionDeclaration())
+ goto Ldontneed;
+ if (sd.hasIdentityEquals)
+ goto Lneed;
+ /* If any of the fields has an opEquals, then we
+ * need it too.
+ */
+ foreach (VarDeclaration v; sd.fields)
+ {
+ if (v.storage_class & STC.ref_)
+ continue;
+ if (v.overlapped)
+ continue;
+ Type tv = v.type.toBasetype();
+ auto tvbase = tv.baseElemOf();
+ if (tvbase.ty == Tstruct)
+ {
+ TypeStruct ts = cast(TypeStruct)tvbase;
+ if (ts.sym.isUnionDeclaration())
+ continue;
+ if (needOpEquals(ts.sym))
+ goto Lneed;
+ }
+ if (tvbase.isfloating())
+ {
+ // This is necessray for:
+ // 1. comparison of +0.0 and -0.0 should be true.
+ // 2. comparison of NANs should be false always.
+ goto Lneed;
+ }
+ if (tvbase.ty == Tarray)
+ goto Lneed;
+ if (tvbase.ty == Taarray)
+ goto Lneed;
+ if (tvbase.ty == Tclass)
+ goto Lneed;
+ }
+Ldontneed:
+ //printf("\tdontneed\n");
+ return false;
+Lneed:
+ //printf("\tneed\n");
+ return true;
+}
+
+/*******************************************
+ * Check given aggregate actually has an identity opEquals or not.
+ */
+private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc)
+{
+ FuncDeclaration f;
+ if (Dsymbol eq = search_function(ad, Id.eq))
+ {
+ /* check identity opEquals exists
+ */
+ scope er = new NullExp(ad.loc, null); // dummy rvalue
+ scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
+ Expressions a;
+ a.setDim(1);
+
+ bool hasIt(Type tthis)
+ {
+ const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it
+ sc = sc.push();
+ sc.tinst = null;
+ sc.minst = null;
+
+ FuncDeclaration rfc(Expression e)
+ {
+ a[0] = e;
+ a[0].type = tthis;
+ return resolveFuncCall(ad.loc, sc, eq, null, tthis, &a, FuncResolveFlag.quiet);
+ }
+
+ f = rfc(er);
+ if (!f)
+ f = rfc(el);
+
+ sc = sc.pop();
+ global.endGagging(errors);
+
+ return f !is null;
+ }
+
+ if (hasIt(ad.type) ||
+ hasIt(ad.type.constOf()) ||
+ hasIt(ad.type.immutableOf()) ||
+ hasIt(ad.type.sharedOf()) ||
+ hasIt(ad.type.sharedConstOf()))
+ {
+ if (f.errors)
+ return null;
+ }
+ }
+ return f;
+}
+
+/******************************************
+ * Build opEquals for struct.
+ * const bool opEquals(const S s) { ... }
+ *
+ * By fixing https://issues.dlang.org/show_bug.cgi?id=3789
+ * opEquals is changed to be never implicitly generated.
+ * Now, struct objects comparison s1 == s2 is translated to:
+ * s1.tupleof == s2.tupleof
+ * to calculate structural equality. See EqualExp.op_overload.
+ */
+FuncDeclaration buildOpEquals(StructDeclaration sd, Scope* sc)
+{
+ if (hasIdentityOpEquals(sd, sc))
+ {
+ sd.hasIdentityEquals = true;
+ }
+ return null;
+}
+
+/******************************************
+ * Build __xopEquals for TypeInfo_Struct
+ * static bool __xopEquals(ref const S p, ref const S q)
+ * {
+ * return p == q;
+ * }
+ *
+ * This is called by TypeInfo.equals(p1, p2). If the struct does not support
+ * const objects comparison, it will throw "not implemented" Error in runtime.
+ */
+FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc)
+{
+ if (!needOpEquals(sd))
+ return null; // bitwise comparison would work
+
+ //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars());
+ if (Dsymbol eq = search_function(sd, Id.eq))
+ {
+ if (FuncDeclaration fd = eq.isFuncDeclaration())
+ {
+ TypeFunction tfeqptr;
+ {
+ Scope scx;
+ /* const bool opEquals(ref const S s);
+ */
+ auto parameters = new Parameters();
+ parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null));
+ tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d);
+ tfeqptr.mod = MODFlags.const_;
+ tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx);
+ }
+ fd = fd.overloadExactMatch(tfeqptr);
+ if (fd)
+ return fd;
+ }
+ }
+ if (!sd.xerreq)
+ {
+ // object._xopEquals
+ Identifier id = Identifier.idPool("_xopEquals");
+ Expression e = new IdentifierExp(sd.loc, Id.empty);
+ e = new DotIdExp(sd.loc, e, Id.object);
+ e = new DotIdExp(sd.loc, e, id);
+ e = e.expressionSemantic(sc);
+ Dsymbol s = getDsymbol(e);
+ assert(s);
+ sd.xerreq = s.isFuncDeclaration();
+ }
+ Loc declLoc; // loc is unnecessary so __xopEquals is never called directly
+ Loc loc; // loc is unnecessary so errors are gagged
+ auto parameters = new Parameters();
+ parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null))
+ .push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null, null));
+ auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d);
+ Identifier id = Id.xopEquals;
+ auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
+ fop.generated = true;
+ Expression e1 = new IdentifierExp(loc, Id.p);
+ Expression e2 = new IdentifierExp(loc, Id.q);
+ Expression e = new EqualExp(TOK.equal, loc, e1, e2);
+ fop.fbody = new ReturnStatement(loc, e);
+ uint errors = global.startGagging(); // Do not report errors
+ Scope* sc2 = sc.push();
+ sc2.stc = 0;
+ sc2.linkage = LINK.d;
+ fop.dsymbolSemantic(sc2);
+ fop.semantic2(sc2);
+ sc2.pop();
+ if (global.endGagging(errors)) // if errors happened
+ fop = sd.xerreq;
+ return fop;
+}
+
+/******************************************
+ * Build __xopCmp for TypeInfo_Struct
+ * static bool __xopCmp(ref const S p, ref const S q)
+ * {
+ * return p.opCmp(q);
+ * }
+ *
+ * This is called by TypeInfo.compare(p1, p2). If the struct does not support
+ * const objects comparison, it will throw "not implemented" Error in runtime.
+ */
+FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc)
+{
+ //printf("StructDeclaration::buildXopCmp() %s\n", toChars());
+ if (Dsymbol cmp = search_function(sd, Id.cmp))
+ {
+ if (FuncDeclaration fd = cmp.isFuncDeclaration())
+ {
+ TypeFunction tfcmpptr;
+ {
+ Scope scx;
+ /* const int opCmp(ref const S s);
+ */
+ auto parameters = new Parameters();
+ parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, null, null, null));
+ tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d);
+ tfcmpptr.mod = MODFlags.const_;
+ tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx);
+ }
+ fd = fd.overloadExactMatch(tfcmpptr);
+ if (fd)
+ return fd;
+ }
+ }
+ else
+ {
+ version (none) // FIXME: doesn't work for recursive alias this
+ {
+ /* Check opCmp member exists.
+ * Consider 'alias this', but except opDispatch.
+ */
+ Expression e = new DsymbolExp(sd.loc, sd);
+ e = new DotIdExp(sd.loc, e, Id.cmp);
+ Scope* sc2 = sc.push();
+ e = e.trySemantic(sc2);
+ sc2.pop();
+ if (e)
+ {
+ Dsymbol s = null;
+ switch (e.op)
+ {
+ case TOK.overloadSet:
+ s = (cast(OverExp)e).vars;
+ break;
+ case TOK.scope_:
+ s = (cast(ScopeExp)e).sds;
+ break;
+ case TOK.variable:
+ s = (cast(VarExp)e).var;
+ break;
+ default:
+ break;
+ }
+ if (!s || s.ident != Id.cmp)
+ e = null; // there's no valid member 'opCmp'
+ }
+ if (!e)
+ return null; // bitwise comparison would work
+ /* Essentially, a struct which does not define opCmp is not comparable.
+ * At this time, typeid(S).compare might be correct that throwing "not implement" Error.
+ * But implementing it would break existing code, such as:
+ *
+ * struct S { int value; } // no opCmp
+ * int[S] aa; // Currently AA key uses bitwise comparison
+ * // (It's default behavior of TypeInfo_Strust.compare).
+ *
+ * Not sure we should fix this inconsistency, so just keep current behavior.
+ */
+ }
+ else
+ {
+ return null;
+ }
+ }
+ if (!sd.xerrcmp)
+ {
+ // object._xopCmp
+ Identifier id = Identifier.idPool("_xopCmp");
+ Expression e = new IdentifierExp(sd.loc, Id.empty);
+ e = new DotIdExp(sd.loc, e, Id.object);
+ e = new DotIdExp(sd.loc, e, id);
+ e = e.expressionSemantic(sc);
+ Dsymbol s = getDsymbol(e);
+ assert(s);
+ sd.xerrcmp = s.isFuncDeclaration();
+ }
+ Loc declLoc; // loc is unnecessary so __xopCmp is never called directly
+ Loc loc; // loc is unnecessary so errors are gagged
+ auto parameters = new Parameters();
+ parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null));
+ parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.q, null, null));
+ auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d);
+ Identifier id = Id.xopCmp;
+ auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
+ fop.generated = true;
+ Expression e1 = new IdentifierExp(loc, Id.p);
+ Expression e2 = new IdentifierExp(loc, Id.q);
+ version (IN_GCC)
+ Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2);
+ else
+ Expression e = new CallExp(loc, new DotIdExp(loc, e2, Id.cmp), e1);
+ fop.fbody = new ReturnStatement(loc, e);
+ uint errors = global.startGagging(); // Do not report errors
+ Scope* sc2 = sc.push();
+ sc2.stc = 0;
+ sc2.linkage = LINK.d;
+ fop.dsymbolSemantic(sc2);
+ fop.semantic2(sc2);
+ sc2.pop();
+ if (global.endGagging(errors)) // if errors happened
+ fop = sd.xerrcmp;
+ return fop;
+}
+
+/*******************************************
+ * We need a toHash for the struct if
+ * any fields has a toHash.
+ * Generate one if a user-specified one does not exist.
+ */
+private bool needToHash(StructDeclaration sd)
+{
+ //printf("StructDeclaration::needToHash() %s\n", sd.toChars());
+ if (sd.isUnionDeclaration())
+ goto Ldontneed;
+ if (sd.xhash)
+ goto Lneed;
+
+ /* If any of the fields has an toHash, then we
+ * need it too.
+ */
+ foreach (VarDeclaration v; sd.fields)
+ {
+ if (v.storage_class & STC.ref_)
+ continue;
+ if (v.overlapped)
+ continue;
+ Type tv = v.type.toBasetype();
+ auto tvbase = tv.baseElemOf();
+ if (tvbase.ty == Tstruct)
+ {
+ TypeStruct ts = cast(TypeStruct)tvbase;
+ if (ts.sym.isUnionDeclaration())
+ continue;
+ if (needToHash(ts.sym))
+ goto Lneed;
+ }
+ if (tvbase.isfloating())
+ {
+ /* This is necessary because comparison of +0.0 and -0.0 should be true,
+ * i.e. not a bit compare.
+ */
+ goto Lneed;
+ }
+ if (tvbase.ty == Tarray)
+ goto Lneed;
+ if (tvbase.ty == Taarray)
+ goto Lneed;
+ if (tvbase.ty == Tclass)
+ goto Lneed;
+ }
+Ldontneed:
+ //printf("\tdontneed\n");
+ return false;
+Lneed:
+ //printf("\tneed\n");
+ return true;
+}
+
+/******************************************
+ * Build __xtoHash for non-bitwise hashing
+ * static hash_t xtoHash(ref const S p) nothrow @trusted;
+ */
+FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
+{
+ if (Dsymbol s = search_function(sd, Id.tohash))
+ {
+ __gshared TypeFunction tftohash;
+ if (!tftohash)
+ {
+ tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d);
+ tftohash.mod = MODFlags.const_;
+ tftohash = cast(TypeFunction)tftohash.merge();
+ }
+ if (FuncDeclaration fd = s.isFuncDeclaration())
+ {
+ fd = fd.overloadExactMatch(tftohash);
+ if (fd)
+ return fd;
+ }
+ }
+ if (!needToHash(sd))
+ return null;
+
+ //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
+ Loc declLoc; // loc is unnecessary so __xtoHash is never called directly
+ Loc loc; // internal code should have no loc to prevent coverage
+ auto parameters = new Parameters();
+ parameters.push(new Parameter(STC.ref_ | STC.const_, sd.type, Id.p, null, null));
+ auto tf = new TypeFunction(ParameterList(parameters), Type.thash_t, LINK.d, STC.nothrow_ | STC.trusted);
+ Identifier id = Id.xtoHash;
+ auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
+ fop.generated = true;
+
+ /* Do memberwise hashing.
+ *
+ * If sd is a nested struct, and if it's nested in a class, the calculated
+ * hash value will also contain the result of parent class's toHash().
+ */
+ const(char)[] code =
+ ".object.size_t h = 0;" ~
+ "foreach (i, T; typeof(p.tupleof))" ~
+ // workaround https://issues.dlang.org/show_bug.cgi?id=17968
+ " static if(is(T* : const(.object.Object)*)) " ~
+ " h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~
+ " else " ~
+ " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
+ "return h;";
+ fop.fbody = new CompileStatement(loc, new StringExp(loc, code));
+ Scope* sc2 = sc.push();
+ sc2.stc = 0;
+ sc2.linkage = LINK.d;
+ fop.dsymbolSemantic(sc2);
+ fop.semantic2(sc2);
+ sc2.pop();
+
+ //printf("%s fop = %s %s\n", sd.toChars(), fop.toChars(), fop.type.toChars());
+ return fop;
+}
+
+/*****************************************
+ * Create inclusive destructor for struct/class by aggregating
+ * all the destructors in dtors[] with the destructors for
+ * all the members.
+ * Params:
+ * ad = struct or class to build destructor for
+ * sc = context
+ * Returns:
+ * generated function, null if none needed
+ * Note:
+ * Close similarity with StructDeclaration::buildPostBlit(),
+ * and the ordering changes (runs backward instead of forwards).
+ */
+DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
+{
+ //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars());
+ if (ad.isUnionDeclaration())
+ return null; // unions don't have destructors
+
+ StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
+ Loc declLoc = ad.dtors.dim ? ad.dtors[0].loc : ad.loc;
+ Loc loc; // internal code should have no loc to prevent coverage
+ FuncDeclaration xdtor_fwd = null;
+
+ // if the dtor is an extern(C++) prototype, then we expect it performs a full-destruction; we don't need to build a full-dtor
+ const bool dtorIsCppPrototype = ad.dtors.dim == 1 && ad.dtors[0].linkage == LINK.cpp && !ad.dtors[0].fbody;
+ if (!dtorIsCppPrototype)
+ {
+ Expression e = null;
+ for (size_t i = 0; i < ad.fields.dim; i++)
+ {
+ auto v = ad.fields[i];
+ if (v.storage_class & STC.ref_)
+ continue;
+ if (v.overlapped)
+ continue;
+ auto tv = v.type.baseElemOf();
+ if (tv.ty != Tstruct)
+ continue;
+ auto sdv = (cast(TypeStruct)tv).sym;
+ if (!sdv.dtor)
+ continue;
+
+ // fix: https://issues.dlang.org/show_bug.cgi?id=17257
+ // braces for shrink wrapping scope of a
+ {
+ xdtor_fwd = sdv.dtor; // this dtor is temporary it could be anything
+ auto a = new AliasDeclaration(Loc.initial, Id.__xdtor, xdtor_fwd);
+ a.addMember(sc, ad); // temporarily add to symbol table
+ }
+
+ sdv.dtor.functionSemantic();
+
+ stc = mergeFuncAttrs(stc, sdv.dtor);
+ if (stc & STC.disable)
+ {
+ e = null;
+ break;
+ }
+
+ Expression ex;
+ tv = v.type.toBasetype();
+ if (tv.ty == Tstruct)
+ {
+ // this.v.__xdtor()
+
+ ex = new ThisExp(loc);
+ ex = new DotVarExp(loc, ex, v);
+
+ // This is a hack so we can call destructors on const/immutable objects.
+ // Do it as a type 'paint'.
+ ex = new CastExp(loc, ex, v.type.mutableOf());
+ if (stc & STC.safe)
+ stc = (stc & ~STC.safe) | STC.trusted;
+
+ ex = new DotVarExp(loc, ex, sdv.dtor, false);
+ ex = new CallExp(loc, ex);
+ }
+ else
+ {
+ // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
+
+ const n = tv.numberOfElems(loc);
+ if (n == 0)
+ continue;
+
+ ex = new ThisExp(loc);
+ ex = new DotVarExp(loc, ex, v);
+
+ // This is a hack so we can call destructors on const/immutable objects.
+ ex = new DotIdExp(loc, ex, Id.ptr);
+ ex = new CastExp(loc, ex, sdv.type.pointerTo());
+ if (stc & STC.safe)
+ stc = (stc & ~STC.safe) | STC.trusted;
+
+ ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
+ new IntegerExp(loc, n, Type.tsize_t));
+ // Prevent redundant bounds check
+ (cast(SliceExp)ex).upperIsInBounds = true;
+ (cast(SliceExp)ex).lowerIsLessThanUpper = true;
+
+ ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), ex);
+ }
+ e = Expression.combine(ex, e); // combine in reverse order
+ }
+
+ /* extern(C++) destructors call into super to destruct the full hierarchy
+ */
+ ClassDeclaration cldec = ad.isClassDeclaration();
+ if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.primaryDtor)
+ {
+ // WAIT BUT: do I need to run `cldec.baseClass.dtor` semantic? would it have been run before?
+ cldec.baseClass.dtor.functionSemantic();
+
+ stc = mergeFuncAttrs(stc, cldec.baseClass.primaryDtor);
+ if (!(stc & STC.disable))
+ {
+ // super.__xdtor()
+
+ Expression ex = new SuperExp(loc);
+
+ // This is a hack so we can call destructors on const/immutable objects.
+ // Do it as a type 'paint'.
+ ex = new CastExp(loc, ex, cldec.baseClass.type.mutableOf());
+ if (stc & STC.safe)
+ stc = (stc & ~STC.safe) | STC.trusted;
+
+ ex = new DotVarExp(loc, ex, cldec.baseClass.primaryDtor, false);
+ ex = new CallExp(loc, ex);
+
+ e = Expression.combine(e, ex); // super dtor last
+ }
+ }
+
+ /* Build our own "destructor" which executes e
+ */
+ if (e || (stc & STC.disable))
+ {
+ //printf("Building __fieldDtor(), %s\n", e.toChars());
+ auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__fieldDtor);
+ dd.generated = true;
+ dd.storage_class |= STC.inference;
+ dd.fbody = new ExpStatement(loc, e);
+ ad.dtors.shift(dd);
+ ad.members.push(dd);
+ dd.dsymbolSemantic(sc);
+ ad.fieldDtor = dd;
+ }
+ }
+
+ DtorDeclaration xdtor = null;
+ switch (ad.dtors.dim)
+ {
+ case 0:
+ break;
+
+ case 1:
+ xdtor = ad.dtors[0];
+ break;
+
+ default:
+ assert(!dtorIsCppPrototype);
+ Expression e = null;
+ e = null;
+ stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
+ foreach (FuncDeclaration fd; ad.dtors)
+ {
+ stc = mergeFuncAttrs(stc, fd);
+ if (stc & STC.disable)
+ {
+ e = null;
+ break;
+ }
+ Expression ex = new ThisExp(loc);
+ ex = new DotVarExp(loc, ex, fd, false);
+ ex = new CallExp(loc, ex);
+ e = Expression.combine(ex, e);
+ }
+ auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__aggrDtor);
+ dd.generated = true;
+ dd.storage_class |= STC.inference;
+ dd.fbody = new ExpStatement(loc, e);
+ ad.members.push(dd);
+ dd.dsymbolSemantic(sc);
+ xdtor = dd;
+ break;
+ }
+
+ ad.primaryDtor = xdtor;
+
+ if (xdtor && xdtor.linkage == LINK.cpp && !target.cpp.twoDtorInVtable)
+ xdtor = buildWindowsCppDtor(ad, xdtor, sc);
+
+ // Add an __xdtor alias to make the inclusive dtor accessible
+ if (xdtor)
+ {
+ auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, xdtor);
+ _alias.dsymbolSemantic(sc);
+ ad.members.push(_alias);
+ if (xdtor_fwd)
+ ad.symtab.update(_alias); // update forward dtor to correct one
+ else
+ _alias.addMember(sc, ad); // add to symbol table
+ }
+
+ return xdtor;
+}
+
+/**
+ * build a shim function around the compound dtor that accepts an argument
+ * that is used to implement the deleting C++ destructor
+ *
+ * Params:
+ * ad = the aggregate that contains the destructor to wrap
+ * dtor = the destructor to wrap
+ * sc = the scope in which to analyze the new function
+ *
+ * Returns:
+ * the shim destructor, semantically analyzed and added to the class as a member
+ */
+private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclaration dtor, Scope* sc)
+{
+ auto cldec = ad.isClassDeclaration();
+ if (!cldec || cldec.cppDtorVtblIndex == -1) // scalar deleting dtor not built for non-virtual dtors
+ return dtor;
+
+ // generate deleting C++ destructor corresponding to:
+ // void* C::~C(int del)
+ // {
+ // this->~C();
+ // // TODO: if (del) delete (char*)this;
+ // return (void*) this;
+ // }
+ Parameter delparam = new Parameter(STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null);
+ Parameters* params = new Parameters;
+ params.push(delparam);
+ auto ftype = new TypeFunction(ParameterList(params), Type.tvoidptr, LINK.cpp, dtor.storage_class);
+ auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.cppdtor);
+ func.type = ftype;
+ if (dtor.fbody)
+ {
+ const loc = dtor.loc;
+ auto stmts = new Statements;
+ auto call = new CallExp(loc, dtor, null);
+ call.directcall = true;
+ stmts.push(new ExpStatement(loc, call));
+ stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr)));
+ func.fbody = new CompoundStatement(loc, stmts);
+ func.generated = true;
+ }
+
+ auto sc2 = sc.push();
+ sc2.stc &= ~STC.static_; // not a static destructor
+ sc2.linkage = LINK.cpp;
+
+ ad.members.push(func);
+ func.addMember(sc2, ad);
+ func.dsymbolSemantic(sc2);
+
+ sc2.pop();
+ return func;
+}
+
+/**
+ * build a shim function around the compound dtor that translates
+ * a C++ destructor to a destructor with extern(D) calling convention
+ *
+ * Params:
+ * ad = the aggregate that contains the destructor to wrap
+ * sc = the scope in which to analyze the new function
+ *
+ * Returns:
+ * the shim destructor, semantically analyzed and added to the class as a member
+ */
+DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc)
+{
+ auto dtor = ad.primaryDtor;
+ if (!dtor)
+ return null;
+
+ // Generate shim only when ABI incompatible on target platform
+ if (ad.classKind != ClassKind.cpp || !target.cpp.wrapDtorInExternD)
+ return dtor;
+
+ // generate member function that adjusts calling convention
+ // (EAX used for 'this' instead of ECX on Windows/stack on others):
+ // extern(D) void __ticppdtor()
+ // {
+ // Class.__dtor();
+ // }
+ auto ftype = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dtor.storage_class);
+ auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.ticppdtor);
+ func.type = ftype;
+
+ auto call = new CallExp(dtor.loc, dtor, null);
+ call.directcall = true; // non-virtual call Class.__dtor();
+ func.fbody = new ExpStatement(dtor.loc, call);
+ func.generated = true;
+ func.storage_class |= STC.inference;
+
+ auto sc2 = sc.push();
+ sc2.stc &= ~STC.static_; // not a static destructor
+ sc2.linkage = LINK.d;
+
+ ad.members.push(func);
+ func.addMember(sc2, ad);
+ func.dsymbolSemantic(sc2);
+ func.functionSemantic(); // to infer attributes
+
+ sc2.pop();
+ return func;
+}
+
+/******************************************
+ * Create inclusive invariant for struct/class by aggregating
+ * all the invariants in invs[].
+ * ---
+ * void __invariant() const [pure nothrow @trusted]
+ * {
+ * invs[0](), invs[1](), ...;
+ * }
+ * ---
+ */
+FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc)
+{
+ switch (ad.invs.dim)
+ {
+ case 0:
+ return null;
+
+ case 1:
+ // Don't return invs[0] so it has uniquely generated name.
+ goto default;
+
+ default:
+ Expression e = null;
+ StorageClass stcx = 0;
+ StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
+ foreach (i, inv; ad.invs)
+ {
+ stc = mergeFuncAttrs(stc, inv);
+ if (stc & STC.disable)
+ {
+ // What should do?
+ }
+ const stcy = (inv.storage_class & STC.synchronized_) |
+ (inv.type.mod & MODFlags.shared_ ? STC.shared_ : 0);
+ if (i == 0)
+ stcx = stcy;
+ else if (stcx ^ stcy)
+ {
+ version (all)
+ {
+ // currently rejects
+ ad.error(inv.loc, "mixing invariants with different `shared`/`synchronized` qualifiers is not supported");
+ e = null;
+ break;
+ }
+ }
+ e = Expression.combine(e, new CallExp(Loc.initial, new VarExp(Loc.initial, inv, false)));
+ }
+ auto inv = new InvariantDeclaration(ad.loc, Loc.initial, stc | stcx,
+ Id.classInvariant, new ExpStatement(Loc.initial, e));
+ ad.members.push(inv);
+ inv.dsymbolSemantic(sc);
+ return inv;
+ }
+}
+
+/*****************************************
+ * Create inclusive postblit for struct by aggregating
+ * all the postblits in postblits[] with the postblits for
+ * all the members.
+ * Note the close similarity with AggregateDeclaration::buildDtor(),
+ * and the ordering changes (runs forward instead of backwards).
+ */
+FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
+{
+ //printf("buildPostBlit() %s\n", sd.toChars());
+ if (sd.isUnionDeclaration())
+ return null;
+
+ const hasUserDefinedPosblit = sd.postblits.dim && !sd.postblits[0].isDisabled ? true : false;
+
+ // by default, the storage class of the created postblit
+ StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
+ Loc declLoc = sd.postblits.dim ? sd.postblits[0].loc : sd.loc;
+ Loc loc; // internal code should have no loc to prevent coverage
+
+ // if any of the postblits are disabled, then the generated postblit
+ // will be disabled
+ foreach (postblit; sd.postblits)
+ stc |= postblit.storage_class & STC.disable;
+
+ VarDeclaration[] fieldsToDestroy;
+ auto postblitCalls = new Statements();
+ // iterate through all the struct fields that are not disabled
+ for (size_t i = 0; i < sd.fields.dim && !(stc & STC.disable); i++)
+ {
+ auto structField = sd.fields[i];
+ if (structField.storage_class & STC.ref_)
+ continue;
+ if (structField.overlapped)
+ continue;
+ // if it's a struct declaration or an array of structs
+ Type tv = structField.type.baseElemOf();
+ if (tv.ty != Tstruct)
+ continue;
+ auto sdv = (cast(TypeStruct)tv).sym;
+ // which has a postblit declaration
+ if (!sdv.postblit)
+ continue;
+ assert(!sdv.isUnionDeclaration());
+
+ // if this field's postblit is not `nothrow`, add a `scope(failure)`
+ // block to destroy any prior successfully postblitted fields should
+ // this field's postblit fail
+ if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow)
+ {
+ // create a list of destructors that need to be called
+ Expression[] dtorCalls;
+ foreach(sf; fieldsToDestroy)
+ {
+ Expression ex;
+ tv = sf.type.toBasetype();
+ if (tv.ty == Tstruct)
+ {
+ // this.v.__xdtor()
+
+ ex = new ThisExp(loc);
+ ex = new DotVarExp(loc, ex, sf);
+
+ // This is a hack so we can call destructors on const/immutable objects.
+ ex = new AddrExp(loc, ex);
+ ex = new CastExp(loc, ex, sf.type.mutableOf().pointerTo());
+ ex = new PtrExp(loc, ex);
+ if (stc & STC.safe)
+ stc = (stc & ~STC.safe) | STC.trusted;
+
+ auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym;
+
+ ex = new DotVarExp(loc, ex, sfv.dtor, false);
+ ex = new CallExp(loc, ex);
+
+ dtorCalls ~= ex;
+ }
+ else
+ {
+ // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n])
+
+ const length = tv.numberOfElems(loc);
+
+ ex = new ThisExp(loc);
+ ex = new DotVarExp(loc, ex, sf);
+
+ // This is a hack so we can call destructors on const/immutable objects.
+ ex = new DotIdExp(loc, ex, Id.ptr);
+ ex = new CastExp(loc, ex, sdv.type.pointerTo());
+ if (stc & STC.safe)
+ stc = (stc & ~STC.safe) | STC.trusted;
+
+ auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
+ new IntegerExp(loc, length, Type.tsize_t));
+ // Prevent redundant bounds check
+ se.upperIsInBounds = true;
+ se.lowerIsLessThanUpper = true;
+
+ ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se);
+
+ dtorCalls ~= ex;
+ }
+ }
+ fieldsToDestroy = [];
+
+ // aggregate the destructor calls
+ auto dtors = new Statements();
+ foreach_reverse(dc; dtorCalls)
+ {
+ dtors.push(new ExpStatement(loc, dc));
+ }
+
+ // put destructor calls in a `scope(failure)` block
+ postblitCalls.push(new ScopeGuardStatement(loc, TOK.onScopeFailure, new CompoundStatement(loc, dtors)));
+ }
+
+ // perform semantic on the member postblit in order to
+ // be able to aggregate it later on with the rest of the
+ // postblits
+ sdv.postblit.functionSemantic();
+
+ stc = mergeFuncAttrs(stc, sdv.postblit);
+ stc = mergeFuncAttrs(stc, sdv.dtor);
+
+ // if any of the struct member fields has disabled
+ // its postblit, then `sd` is not copyable, so no
+ // postblit is generated
+ if (stc & STC.disable)
+ {
+ postblitCalls.setDim(0);
+ break;
+ }
+
+ Expression ex;
+ tv = structField.type.toBasetype();
+ if (tv.ty == Tstruct)
+ {
+ // this.v.__xpostblit()
+
+ ex = new ThisExp(loc);
+ ex = new DotVarExp(loc, ex, structField);
+
+ // This is a hack so we can call postblits on const/immutable objects.
+ ex = new AddrExp(loc, ex);
+ ex = new CastExp(loc, ex, structField.type.mutableOf().pointerTo());
+ ex = new PtrExp(loc, ex);
+ if (stc & STC.safe)
+ stc = (stc & ~STC.safe) | STC.trusted;
+
+ ex = new DotVarExp(loc, ex, sdv.postblit, false);
+ ex = new CallExp(loc, ex);
+ }
+ else
+ {
+ // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n])
+
+ const length = tv.numberOfElems(loc);
+ if (length == 0)
+ continue;
+
+ ex = new ThisExp(loc);
+ ex = new DotVarExp(loc, ex, structField);
+
+ // This is a hack so we can call postblits on const/immutable objects.
+ ex = new DotIdExp(loc, ex, Id.ptr);
+ ex = new CastExp(loc, ex, sdv.type.pointerTo());
+ if (stc & STC.safe)
+ stc = (stc & ~STC.safe) | STC.trusted;
+
+ auto se = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type.tsize_t),
+ new IntegerExp(loc, length, Type.tsize_t));
+ // Prevent redundant bounds check
+ se.upperIsInBounds = true;
+ se.lowerIsLessThanUpper = true;
+ ex = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayPostblit), se);
+ }
+ postblitCalls.push(new ExpStatement(loc, ex)); // combine in forward order
+
+ /* https://issues.dlang.org/show_bug.cgi?id=10972
+ * When subsequent field postblit calls fail,
+ * this field should be destructed for Exception Safety.
+ */
+ if (sdv.dtor)
+ {
+ sdv.dtor.functionSemantic();
+
+ // keep a list of fields that need to be destroyed in case
+ // of a future postblit failure
+ fieldsToDestroy ~= structField;
+ }
+ }
+
+ void checkShared()
+ {
+ if (sd.type.isShared())
+ stc |= STC.shared_;
+ }
+
+ // Build our own "postblit" which executes a, but only if needed.
+ if (postblitCalls.dim || (stc & STC.disable))
+ {
+ //printf("Building __fieldPostBlit()\n");
+ checkShared();
+ auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__fieldPostblit);
+ dd.generated = true;
+ dd.storage_class |= STC.inference | STC.scope_;
+ dd.fbody = (stc & STC.disable) ? null : new CompoundStatement(loc, postblitCalls);
+ sd.postblits.shift(dd);
+ sd.members.push(dd);
+ dd.dsymbolSemantic(sc);
+ }
+
+ // create __xpostblit, which is the generated postblit
+ FuncDeclaration xpostblit = null;
+ switch (sd.postblits.dim)
+ {
+ case 0:
+ break;
+
+ case 1:
+ xpostblit = sd.postblits[0];
+ break;
+
+ default:
+ Expression e = null;
+ stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
+ foreach (fd; sd.postblits)
+ {
+ stc = mergeFuncAttrs(stc, fd);
+ if (stc & STC.disable)
+ {
+ e = null;
+ break;
+ }
+ Expression ex = new ThisExp(loc);
+ ex = new DotVarExp(loc, ex, fd, false);
+ ex = new CallExp(loc, ex);
+ e = Expression.combine(e, ex);
+ }
+
+ checkShared();
+ auto dd = new PostBlitDeclaration(declLoc, Loc.initial, stc, Id.__aggrPostblit);
+ dd.generated = true;
+ dd.storage_class |= STC.inference;
+ dd.fbody = new ExpStatement(loc, e);
+ sd.members.push(dd);
+ dd.dsymbolSemantic(sc);
+ xpostblit = dd;
+ break;
+ }
+
+ // Add an __xpostblit alias to make the inclusive postblit accessible
+ if (xpostblit)
+ {
+ auto _alias = new AliasDeclaration(Loc.initial, Id.__xpostblit, xpostblit);
+ _alias.dsymbolSemantic(sc);
+ sd.members.push(_alias);
+ _alias.addMember(sc, sd); // add to symbol table
+ }
+
+ if (sd.hasCopyCtor)
+ {
+ // we have user defined postblit, so we prioritize it
+ if (hasUserDefinedPosblit)
+ {
+ sd.hasCopyCtor = false;
+ return xpostblit;
+ }
+ // we have fields with postblits, so print deprecations
+ if (xpostblit && !xpostblit.isDisabled())
+ {
+ deprecation(sd.loc, "`struct %s` implicitly-generated postblit hides copy constructor.", sd.toChars);
+ deprecationSupplemental(sd.loc, "The field postblit will have priority over the copy constructor.");
+ deprecationSupplemental(sd.loc, "To change this, the postblit should be disabled for `struct %s`", sd.toChars());
+ sd.hasCopyCtor = false;
+ }
+ else
+ xpostblit = null;
+ }
+
+ return xpostblit;
+}
+
+/**
+ * Generates a copy constructor declaration with the specified storage
+ * class for the parameter and the function.
+ *
+ * Params:
+ * sd = the `struct` that contains the copy constructor
+ * paramStc = the storage class of the copy constructor parameter
+ * funcStc = the storage class for the copy constructor declaration
+ *
+ * Returns:
+ * The copy constructor declaration for struct `sd`.
+ */
+private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc)
+{
+ auto fparams = new Parameters();
+ auto structType = sd.type;
+ fparams.push(new Parameter(paramStc | STC.ref_ | STC.return_ | STC.scope_, structType, Id.p, null, null));
+ ParameterList pList = ParameterList(fparams);
+ auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_);
+ auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, true);
+ ccd.storage_class |= funcStc;
+ ccd.storage_class |= STC.inference;
+ ccd.generated = true;
+ return ccd;
+}
+
+/**
+ * Generates a trivial copy constructor body that simply does memberwise
+ * initialization:
+ *
+ * this.field1 = rhs.field1;
+ * this.field2 = rhs.field2;
+ * ...
+ *
+ * Params:
+ * sd = the `struct` declaration that contains the copy constructor
+ *
+ * Returns:
+ * A `CompoundStatement` containing the body of the copy constructor.
+ */
+private Statement generateCopyCtorBody(StructDeclaration sd)
+{
+ Loc loc;
+ Expression e;
+ foreach (v; sd.fields)
+ {
+ auto ec = new AssignExp(loc,
+ new DotVarExp(loc, new ThisExp(loc), v),
+ new DotVarExp(loc, new IdentifierExp(loc, Id.p), v));
+ e = Expression.combine(e, ec);
+ //printf("e.toChars = %s\n", e.toChars());
+ }
+ Statement s1 = new ExpStatement(loc, e);
+ return new CompoundStatement(loc, s1);
+}
+
+/**
+ * Determine if a copy constructor is needed for struct sd,
+ * if the following conditions are met:
+ *
+ * 1. sd does not define a copy constructor
+ * 2. at least one field of sd defines a copy constructor
+ *
+ * Params:
+ * sd = the `struct` for which the copy constructor is generated
+ * hasCpCtor = set to true if a copy constructor is already present
+ *
+ * Returns:
+ * `true` if one needs to be generated
+ * `false` otherwise
+ */
+private bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
+{
+ if (global.errors)
+ return false;
+
+ auto ctor = sd.search(sd.loc, Id.ctor);
+ if (ctor)
+ {
+ if (ctor.isOverloadSet())
+ return false;
+ if (auto td = ctor.isTemplateDeclaration())
+ ctor = td.funcroot;
+ }
+
+ CtorDeclaration cpCtor;
+ CtorDeclaration rvalueCtor;
+
+ if (!ctor)
+ goto LcheckFields;
+
+ overloadApply(ctor, (Dsymbol s)
+ {
+ if (s.isTemplateDeclaration())
+ return 0;
+ auto ctorDecl = s.isCtorDeclaration();
+ assert(ctorDecl);
+ if (ctorDecl.isCpCtor)
+ {
+ if (!cpCtor)
+ cpCtor = ctorDecl;
+ return 0;
+ }
+
+ auto tf = ctorDecl.type.toTypeFunction();
+ const dim = tf.parameterList.length;
+ if (dim == 1)
+ {
+ auto param = tf.parameterList[0];
+ if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
+ {
+ rvalueCtor = ctorDecl;
+ }
+ }
+ return 0;
+ });
+
+ if (cpCtor)
+ {
+ if (rvalueCtor)
+ {
+ .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars());
+ errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
+ errorSupplemental(cpCtor.loc, "copy constructor defined here");
+ }
+ hasCpCtor = true;
+ return false;
+ }
+
+LcheckFields:
+ VarDeclaration fieldWithCpCtor;
+ // see if any struct members define a copy constructor
+ foreach (v; sd.fields)
+ {
+ if (v.storage_class & STC.ref_)
+ continue;
+ if (v.overlapped)
+ continue;
+
+ auto ts = v.type.baseElemOf().isTypeStruct();
+ if (!ts)
+ continue;
+ if (ts.sym.hasCopyCtor)
+ {
+ fieldWithCpCtor = v;
+ break;
+ }
+ }
+
+ if (fieldWithCpCtor && rvalueCtor)
+ {
+ .error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd.toChars());
+ errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
+ errorSupplemental(fieldWithCpCtor.loc, "field with copy constructor defined here");
+ return false;
+ }
+ else if (!fieldWithCpCtor)
+ return false;
+ return true;
+}
+
+/**
+ * Generates a copy constructor if needCopyCtor() returns true.
+ * The generated copy constructor will be of the form:
+ * this(ref return scope inout(S) rhs) inout
+ * {
+ * this.field1 = rhs.field1;
+ * this.field2 = rhs.field2;
+ * ...
+ * }
+ *
+ * Params:
+ * sd = the `struct` for which the copy constructor is generated
+ * sc = the scope where the copy constructor is generated
+ *
+ * Returns:
+ * `true` if `struct` sd defines a copy constructor (explicitly or generated),
+ * `false` otherwise.
+ */
+bool buildCopyCtor(StructDeclaration sd, Scope* sc)
+{
+ bool hasCpCtor;
+ if (!needCopyCtor(sd, hasCpCtor))
+ return hasCpCtor;
+
+ //printf("generating copy constructor for %s\n", sd.toChars());
+ const MOD paramMod = MODFlags.wild;
+ const MOD funcMod = MODFlags.wild;
+ auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod));
+ auto copyCtorBody = generateCopyCtorBody(sd);
+ ccd.fbody = copyCtorBody;
+ sd.members.push(ccd);
+ ccd.addMember(sc, sd);
+ const errors = global.startGagging();
+ Scope* sc2 = sc.push();
+ sc2.stc = 0;
+ sc2.linkage = LINK.d;
+ ccd.dsymbolSemantic(sc2);
+ ccd.semantic2(sc2);
+ ccd.semantic3(sc2);
+ //printf("ccd semantic: %s\n", ccd.type.toChars());
+ sc2.pop();
+ if (global.endGagging(errors) || sd.isUnionDeclaration())
+ {
+ ccd.storage_class |= STC.disable;
+ ccd.fbody = null;
+ }
+ return true;
+}
+
+
diff --git a/gcc/d/dmd/compiler.d b/gcc/d/dmd/compiler.d
new file mode 100644
index 00000000000..28f9ba6fdc4
--- /dev/null
+++ b/gcc/d/dmd/compiler.d
@@ -0,0 +1,57 @@
+/**
+ * Describes a back-end compiler and implements compiler-specific actions.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/compiler.d, _compiler.d)
+ * Documentation: https://dlang.org/phobos/dmd_compiler.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/compiler.d
+ */
+
+module dmd.compiler;
+
+import dmd.arraytypes;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.expression;
+import dmd.mtype;
+import dmd.root.array;
+
+extern (C++) __gshared
+{
+ bool includeImports = false;
+ // array of module patterns used to include/exclude imported modules
+ Array!(const(char)*) includeModulePatterns;
+ Modules compiledImports;
+}
+
+
+/**
+ * A data structure that describes a back-end compiler and implements
+ * compiler-specific actions.
+ */
+extern (C++) struct Compiler
+{
+ /******************************
+ * Encode the given expression, which is assumed to be an rvalue literal
+ * as another type for use in CTFE.
+ * This corresponds roughly to the idiom *(Type *)&e.
+ */
+ extern (C++) static Expression paintAsType(UnionExp* pue, Expression e, Type type);
+
+ /******************************
+ * For the given module, perform any post parsing analysis.
+ * Certain compiler backends (ie: GDC) have special placeholder
+ * modules whose source are empty, but code gets injected
+ * immediately after loading.
+ */
+ extern (C++) static void onParseModule(Module m);
+
+ /**
+ * A callback function that is called once an imported module is
+ * parsed. If the callback returns true, then it tells the
+ * frontend that the driver intends on compiling the import.
+ */
+ extern (C++) static bool onImport(Module m);
+}
diff --git a/gcc/d/dmd/compiler.h b/gcc/d/dmd/compiler.h
index e7ef5a4ccf7..27e87b692a8 100644
--- a/gcc/d/dmd/compiler.h
+++ b/gcc/d/dmd/compiler.h
@@ -22,11 +22,6 @@ class Type;
struct Scope;
struct UnionExp;
-// DMD-generated module `__entrypoint` where the C main resides
-extern Module *entrypoint;
-// Module in which the D main is
-extern Module *rootHasMain;
-
extern bool includeImports;
// array of module patterns used to include/exclude imported modules
extern Array<const char*> includeModulePatterns;
@@ -37,7 +32,6 @@ struct Compiler
// CTFE support for cross-compilation.
static Expression *paintAsType(UnionExp *, Expression *, Type *);
// Backend
- static void genCmain(Scope *);
static bool onImport(Module *);
static void onParseModule(Module *);
};
diff --git a/gcc/d/dmd/complex.d b/gcc/d/dmd/complex.d
new file mode 100644
index 00000000000..84bf5e9763a
--- /dev/null
+++ b/gcc/d/dmd/complex.d
@@ -0,0 +1,112 @@
+/**
+ * Implements a complex number type.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/complex.d, _complex.d)
+ * Documentation: https://dlang.org/phobos/dmd_complex.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/complex.d
+ */
+
+module dmd.complex;
+
+import dmd.root.ctfloat;
+
+extern (C++) struct complex_t
+{
+ real_t re;
+ real_t im;
+
+ this() @disable;
+
+ this(real_t re)
+ {
+ this(re, CTFloat.zero);
+ }
+
+ this(real_t re, real_t im)
+ {
+ this.re = re;
+ this.im = im;
+ }
+
+ extern (D) complex_t opBinary(string op)(complex_t y)
+ if (op == "+")
+ {
+ return complex_t(re + y.re, im + y.im);
+ }
+
+ extern (D) complex_t opBinary(string op)(complex_t y)
+ if (op == "-")
+ {
+ return complex_t(re - y.re, im - y.im);
+ }
+
+ extern (D) complex_t opUnary(string op)()
+ if (op == "-")
+ {
+ return complex_t(-re, -im);
+ }
+
+ extern (D) complex_t opBinary(string op)(complex_t y)
+ if (op == "*")
+ {
+ return complex_t(re * y.re - im * y.im, im * y.re + re * y.im);
+ }
+
+ extern (D) complex_t opBinaryRight(string op)(real_t x)
+ if (op == "*")
+ {
+ return complex_t(x) * this;
+ }
+
+ extern (D) complex_t opBinary(string op)(real_t y)
+ if (op == "*")
+ {
+ return this * complex_t(y);
+ }
+
+ extern (D) complex_t opBinary(string op)(real_t y)
+ if (op == "/")
+ {
+ return this / complex_t(y);
+ }
+
+ extern (D) complex_t opBinary(string op)(complex_t y)
+ if (op == "/")
+ {
+ if (CTFloat.fabs(y.re) < CTFloat.fabs(y.im))
+ {
+ const r = y.re / y.im;
+ const den = y.im + r * y.re;
+ return complex_t((re * r + im) / den, (im * r - re) / den);
+ }
+ else
+ {
+ const r = y.im / y.re;
+ const den = y.re + r * y.im;
+ return complex_t((re + r * im) / den, (im - r * re) / den);
+ }
+ }
+
+ extern (D) bool opCast(T : bool)() const
+ {
+ return re || im;
+ }
+
+ int opEquals(complex_t y) const
+ {
+ return re == y.re && im == y.im;
+ }
+}
+
+extern (C++) real_t creall(complex_t x)
+{
+ return x.re;
+}
+
+extern (C++) real_t cimagl(complex_t x)
+{
+ return x.im;
+}
diff --git a/gcc/d/dmd/complex_t.h b/gcc/d/dmd/complex_t.h
index 7f174606422..335917135c2 100644
--- a/gcc/d/dmd/complex_t.h
+++ b/gcc/d/dmd/complex_t.h
@@ -20,7 +20,7 @@ struct complex_t
real_t re;
real_t im;
- complex_t(real_t re) : re(re), im(ldouble(0)) {}
+ complex_t(real_t re) : re(re), im(CTFloat::zero) {}
complex_t(real_t re, real_t im) : re(re), im(im) {}
complex_t operator + (complex_t y) { return complex_t(re + y.re, im + y.im); }
@@ -52,7 +52,7 @@ struct complex_t
int operator != (complex_t y) { return re != y.re || im != y.im; }
private:
- complex_t() : re(ldouble(0)), im(ldouble(0)) {}
+ complex_t() : re(CTFloat::zero), im(CTFloat::zero) {}
};
inline complex_t operator * (real_t x, complex_t y) { return complex_t(x) * y; }
diff --git a/gcc/d/dmd/cond.c b/gcc/d/dmd/cond.c
deleted file mode 100644
index 6c7dc9eb1a3..00000000000
--- a/gcc/d/dmd/cond.c
+++ /dev/null
@@ -1,738 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/cond.c
- */
-
-#include "root/dsystem.h" // strcmp()
-
-#include "mars.h"
-#include "id.h"
-#include "init.h"
-#include "aggregate.h"
-#include "declaration.h"
-#include "identifier.h"
-#include "expression.h"
-#include "cond.h"
-#include "module.h"
-#include "template.h"
-#include "mtype.h"
-#include "scope.h"
-#include "statement.h"
-#include "arraytypes.h"
-#include "tokens.h"
-
-bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors);
-
-int findCondition(Identifiers *ids, Identifier *ident)
-{
- if (ids)
- {
- for (size_t i = 0; i < ids->length; i++)
- {
- Identifier *id = (*ids)[i];
-
- if (id == ident)
- return true;
- }
- }
-
- return false;
-}
-
-/* ============================================================ */
-
-Condition::Condition(Loc loc)
-{
- this->loc = loc;
- inc = 0;
-}
-
-/* ============================================================ */
-
-StaticForeach::StaticForeach(Loc loc, ForeachStatement *aggrfe, ForeachRangeStatement *rangefe)
-{
- assert(!!aggrfe ^ !!rangefe);
- this->loc = loc;
- this->aggrfe = aggrfe;
- this->rangefe = rangefe;
- this->needExpansion = false;
-}
-
-StaticForeach *StaticForeach::syntaxCopy()
-{
- return new StaticForeach(
- loc,
- aggrfe ? (ForeachStatement *)aggrfe->syntaxCopy() : NULL,
- rangefe ? (ForeachRangeStatement *)rangefe->syntaxCopy() : NULL
- );
-}
-
-/*****************************************
- * Turn an aggregate which is an array into an expression tuple
- * of its elements. I.e., lower
- * static foreach (x; [1, 2, 3, 4]) { ... }
- * to
- * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... }
- */
-
-static void lowerArrayAggregate(StaticForeach *sfe, Scope *sc)
-{
- Expression *aggr = sfe->aggrfe->aggr;
- Expression *el = new ArrayLengthExp(aggr->loc, aggr);
- sc = sc->startCTFE();
- el = expressionSemantic(el, sc);
- sc = sc->endCTFE();
- el = el->optimize(WANTvalue);
- el = el->ctfeInterpret();
- if (el->op == TOKint64)
- {
- Expressions *es;
- if (ArrayLiteralExp *ale = aggr->isArrayLiteralExp())
- {
- // Directly use the elements of the array for the TupleExp creation
- es = ale->elements;
- }
- else
- {
- size_t length = (size_t)el->toInteger();
- es = new Expressions();
- es->setDim(length);
- for (size_t i = 0; i < length; i++)
- {
- IntegerExp *index = new IntegerExp(sfe->loc, i, Type::tsize_t);
- Expression *value = new IndexExp(aggr->loc, aggr, index);
- (*es)[i] = value;
- }
- }
- sfe->aggrfe->aggr = new TupleExp(aggr->loc, es);
- sfe->aggrfe->aggr = expressionSemantic(sfe->aggrfe->aggr, sc);
- sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue);
- sfe->aggrfe->aggr = sfe->aggrfe->aggr->ctfeInterpret();
- }
- else
- {
- sfe->aggrfe->aggr = new ErrorExp();
- }
-}
-
-/*****************************************
- * Wrap a statement into a function literal and call it.
- *
- * Params:
- * loc = The source location.
- * s = The statement.
- * Returns:
- * AST of the expression `(){ s; }()` with location loc.
- */
-
-static Expression *wrapAndCall(Loc loc, Statement *s)
-{
- TypeFunction *tf = new TypeFunction(ParameterList(), NULL, LINKdefault, 0);
- FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, loc, tf, TOKreserved, NULL);
- fd->fbody = s;
- FuncExp *fe = new FuncExp(loc, fd);
- Expression *ce = new CallExp(loc, fe, new Expressions());
- return ce;
-}
-
-/*****************************************
- * Create a `foreach` statement from `aggrefe/rangefe` with given
- * `foreach` variables and body `s`.
- *
- * Params:
- * loc = The source location.
- * parameters = The foreach variables.
- * s = The `foreach` body.
- * Returns:
- * `foreach (parameters; aggregate) s;` or
- * `foreach (parameters; lower .. upper) s;`
- * Where aggregate/lower, upper are as for the current StaticForeach.
- */
-
-static Statement *createForeach(StaticForeach *sfe, Loc loc, Parameters *parameters, Statement *s)
-{
- if (sfe->aggrfe)
- {
- return new ForeachStatement(loc, sfe->aggrfe->op, parameters, sfe->aggrfe->aggr->syntaxCopy(), s, loc);
- }
- else
- {
- assert(sfe->rangefe && parameters->length == 1);
- return new ForeachRangeStatement(loc, sfe->rangefe->op, (*parameters)[0],
- sfe->rangefe->lwr->syntaxCopy(),
- sfe->rangefe->upr->syntaxCopy(), s, loc);
- }
-}
-
-/*****************************************
- * For a `static foreach` with multiple loop variables, the
- * aggregate is lowered to an array of tuples. As D does not have
- * built-in tuples, we need a suitable tuple type. This generates
- * a `struct` that serves as the tuple type. This type is only
- * used during CTFE and hence its typeinfo will not go to the
- * object file.
- *
- * Params:
- * loc = The source location.
- * e = The expressions we wish to store in the tuple.
- * sc = The current scope.
- * Returns:
- * A struct type of the form
- * struct Tuple
- * {
- * typeof(AliasSeq!(e)) tuple;
- * }
- */
-
-static TypeStruct *createTupleType(Loc loc, Expressions *e)
-{ // TODO: move to druntime?
- Identifier *sid = Identifier::generateId("Tuple");
- StructDeclaration *sdecl = new StructDeclaration(loc, sid, false);
- sdecl->storage_class |= STCstatic;
- sdecl->members = new Dsymbols();
- Identifier *fid = Identifier::idPool("tuple");
- Type *ty = new TypeTypeof(loc, new TupleExp(loc, e));
- sdecl->members->push(new VarDeclaration(loc, ty, fid, NULL));
- TypeStruct *r = (TypeStruct *)sdecl->type;
- if (global.params.useTypeInfo && Type::dtypeinfo)
- r->vtinfo = TypeInfoStructDeclaration::create(r); // prevent typeinfo from going to object file
- return r;
-}
-
-/*****************************************
- * Create the AST for an instantiation of a suitable tuple type.
- *
- * Params:
- * loc = The source location.
- * type = A Tuple type, created with createTupleType.
- * e = The expressions we wish to store in the tuple.
- * Returns:
- * An AST for the expression `Tuple(e)`.
- */
-
-static Expression *createTuple(Loc loc, TypeStruct *type, Expressions *e)
-{ // TODO: move to druntime?
- return new CallExp(loc, new TypeExp(loc, type), e);
-}
-
-/*****************************************
- * Lower any aggregate that is not an array to an array using a
- * regular foreach loop within CTFE. If there are multiple
- * `static foreach` loop variables, an array of tuples is
- * generated. In thise case, the field `needExpansion` is set to
- * true to indicate that the static foreach loop expansion will
- * need to expand the tuples into multiple variables.
- *
- * For example, `static foreach (x; range) { ... }` is lowered to:
- *
- * static foreach (x; {
- * typeof({
- * foreach (x; range) return x;
- * }())[] __res;
- * foreach (x; range) __res ~= x;
- * return __res;
- * }()) { ... }
- *
- * Finally, call `lowerArrayAggregate` to turn the produced
- * array into an expression tuple.
- *
- * Params:
- * sc = The current scope.
- */
-
-static void lowerNonArrayAggregate(StaticForeach *sfe, Scope *sc)
-{
- size_t nvars = sfe->aggrfe ? sfe->aggrfe->parameters->length : 1;
- Loc aloc = sfe->aggrfe ? sfe->aggrfe->aggr->loc : sfe->rangefe->lwr->loc;
- // We need three sets of foreach loop variables because the
- // lowering contains three foreach loops.
- Parameters *pparams[3] = {new Parameters(), new Parameters(), new Parameters()};
- for (size_t i = 0; i < nvars; i++)
- {
- for (size_t j = 0; j < 3; j++)
- {
- Parameters *params = pparams[j];
- Parameter *p = sfe->aggrfe ? (*sfe->aggrfe->parameters)[i] : sfe->rangefe->prm;
- params->push(new Parameter(p->storageClass, p->type, p->ident, NULL, NULL));
- }
- }
- Expression *res[2];
- TypeStruct *tplty = NULL;
- if (nvars == 1) // only one `static foreach` variable, generate identifiers.
- {
- for (size_t i = 0; i < 2; i++)
- {
- res[i] = new IdentifierExp(aloc, (*pparams[i])[0]->ident);
- }
- }
- else // multiple `static foreach` variables, generate tuples.
- {
- for (size_t i = 0; i < 2; i++)
- {
- Expressions *e = new Expressions();
- for (size_t j = 0; j < pparams[0]->length; j++)
- {
- Parameter *p = (*pparams[i])[j];
- e->push(new IdentifierExp(aloc, p->ident));
- }
- if (!tplty)
- {
- tplty = createTupleType(aloc, e);
- }
- res[i] = createTuple(aloc, tplty, e);
- }
- sfe->needExpansion = true; // need to expand the tuples later
- }
- // generate remaining code for the new aggregate which is an
- // array (see documentation comment).
- if (sfe->rangefe)
- {
- sc = sc->startCTFE();
- sfe->rangefe->lwr = expressionSemantic(sfe->rangefe->lwr, sc);
- sfe->rangefe->lwr = resolveProperties(sc, sfe->rangefe->lwr);
- sfe->rangefe->upr = expressionSemantic(sfe->rangefe->upr, sc);
- sfe->rangefe->upr = resolveProperties(sc, sfe->rangefe->upr);
- sc = sc->endCTFE();
- sfe->rangefe->lwr = sfe->rangefe->lwr->optimize(WANTvalue);
- sfe->rangefe->lwr = sfe->rangefe->lwr->ctfeInterpret();
- sfe->rangefe->upr = sfe->rangefe->upr->optimize(WANTvalue);
- sfe->rangefe->upr = sfe->rangefe->upr->ctfeInterpret();
- }
- Statements *s1 = new Statements();
- Statements *sfebody = new Statements();
- if (tplty) sfebody->push(new ExpStatement(sfe->loc, tplty->sym));
- sfebody->push(new ReturnStatement(aloc, res[0]));
- s1->push(createForeach(sfe, aloc, pparams[0], new CompoundStatement(aloc, sfebody)));
- s1->push(new ExpStatement(aloc, new AssertExp(aloc, new IntegerExp(aloc, 0, Type::tint32))));
- Type *ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1)));
- Type *aty = ety->arrayOf();
- Identifier *idres = Identifier::generateId("__res");
- VarDeclaration *vard = new VarDeclaration(aloc, aty, idres, NULL);
- Statements *s2 = new Statements();
-
- // Run 'typeof' gagged to avoid duplicate errors and if it fails just create
- // an empty foreach to expose them.
- unsigned olderrors = global.startGagging();
- ety = typeSemantic(ety, aloc, sc);
- if (global.endGagging(olderrors))
- s2->push(createForeach(sfe, aloc, pparams[1], NULL));
- else
- {
- s2->push(new ExpStatement(aloc, vard));
- Expression *catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]);
- s2->push(createForeach(sfe, aloc, pparams[1], new ExpStatement(aloc, catass)));
- s2->push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres)));
- }
-
- Expression *aggr;
- Type *indexty;
-
- if (sfe->rangefe && (indexty = ety)->isintegral())
- {
- sfe->rangefe->lwr->type = indexty;
- sfe->rangefe->upr->type = indexty;
- IntRange lwrRange = getIntRange(sfe->rangefe->lwr);
- IntRange uprRange = getIntRange(sfe->rangefe->upr);
-
- const dinteger_t lwr = sfe->rangefe->lwr->toInteger();
- dinteger_t upr = sfe->rangefe->upr->toInteger();
- size_t length = 0;
-
- if (lwrRange.imin <= uprRange.imax)
- length = (size_t)(upr - lwr);
-
- Expressions *exps = new Expressions();
- exps->setDim(length);
-
- if (sfe->rangefe->op == TOKforeach)
- {
- for (size_t i = 0; i < length; i++)
- (*exps)[i] = new IntegerExp(aloc, lwr + i, indexty);
- }
- else
- {
- --upr;
- for (size_t i = 0; i < length; i++)
- (*exps)[i] = new IntegerExp(aloc, upr - i, indexty);
- }
- aggr = new ArrayLiteralExp(aloc, indexty->arrayOf(), exps);
- }
- else
- {
- aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2));
- sc = sc->startCTFE();
- aggr = expressionSemantic(aggr, sc);
- aggr = resolveProperties(sc, aggr);
- sc = sc->endCTFE();
- aggr = aggr->optimize(WANTvalue);
- aggr = aggr->ctfeInterpret();
- }
-
- assert(!!sfe->aggrfe ^ !!sfe->rangefe);
- sfe->aggrfe = new ForeachStatement(sfe->loc, TOKforeach, pparams[2], aggr,
- sfe->aggrfe ? sfe->aggrfe->_body : sfe->rangefe->_body,
- sfe->aggrfe ? sfe->aggrfe->endloc : sfe->rangefe->endloc);
- sfe->rangefe = NULL;
- lowerArrayAggregate(sfe, sc); // finally, turn generated array into expression tuple
-}
-
-/*****************************************
- * Perform `static foreach` lowerings that are necessary in order
- * to finally expand the `static foreach` using
- * `ddmd.statementsem.makeTupleForeach`.
- */
-
-void staticForeachPrepare(StaticForeach *sfe, Scope *sc)
-{
- assert(sc);
- if (sfe->aggrfe)
- {
- sc = sc->startCTFE();
- sfe->aggrfe->aggr = expressionSemantic(sfe->aggrfe->aggr, sc);
- sc = sc->endCTFE();
- sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue);
- }
-
- if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Terror)
- {
- return;
- }
-
- if (!staticForeachReady(sfe))
- {
- if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Tarray)
- {
- lowerArrayAggregate(sfe, sc);
- }
- else
- {
- lowerNonArrayAggregate(sfe, sc);
- }
- }
-}
-
-/*****************************************
- * Returns:
- * `true` iff ready to call `ddmd.statementsem.makeTupleForeach`.
- */
-
-bool staticForeachReady(StaticForeach *sfe)
-{
- return sfe->aggrfe && sfe->aggrfe->aggr && sfe->aggrfe->aggr->type &&
- sfe->aggrfe->aggr->type->toBasetype()->ty == Ttuple;
-}
-
-/* ============================================================ */
-
-DVCondition::DVCondition(Module *mod, unsigned level, Identifier *ident)
- : Condition(Loc())
-{
- this->mod = mod;
- this->level = level;
- this->ident = ident;
-}
-
-Condition *DVCondition::syntaxCopy()
-{
- return this; // don't need to copy
-}
-
-/* ============================================================ */
-
-void DebugCondition::addGlobalIdent(const char *ident)
-{
- if (!global.debugids)
- global.debugids = new Identifiers();
- global.debugids->push(Identifier::idPool(ident));
-}
-
-
-DebugCondition::DebugCondition(Module *mod, unsigned level, Identifier *ident)
- : DVCondition(mod, level, ident)
-{
-}
-
-// Helper for printing dependency information
-void printDepsConditional(Scope *sc, DVCondition* condition, const char* depType)
-{
- if (!global.params.moduleDeps || global.params.moduleDepsFile.length)
- return;
- OutBuffer *ob = global.params.moduleDeps;
- Module* imod = sc ? sc->instantiatingModule() : condition->mod;
- if (!imod)
- return;
- ob->writestring(depType);
- ob->writestring(imod->toPrettyChars());
- ob->writestring(" (");
- escapePath(ob, imod->srcfile->toChars());
- ob->writestring(") : ");
- if (condition->ident)
- ob->printf("%s\n", condition->ident->toChars());
- else
- ob->printf("%d\n", condition->level);
-}
-
-
-int DebugCondition::include(Scope *sc)
-{
- //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel);
- if (inc == 0)
- {
- inc = 2;
- bool definedInModule = false;
- if (ident)
- {
- if (findCondition(mod->debugids, ident))
- {
- inc = 1;
- definedInModule = true;
- }
- else if (findCondition(global.debugids, ident))
- inc = 1;
- else
- { if (!mod->debugidsNot)
- mod->debugidsNot = new Identifiers();
- mod->debugidsNot->push(ident);
- }
- }
- else if (level <= global.params.debuglevel || level <= mod->debuglevel)
- inc = 1;
- if (!definedInModule)
- printDepsConditional(sc, this, "depsDebug ");
- }
- return (inc == 1);
-}
-
-/* ============================================================ */
-
-static bool isReserved(const char *ident)
-{
- static const char* reserved[] =
- {
- "DigitalMars",
- "GNU",
- "LDC",
- "SDC",
- "Windows",
- "Win32",
- "Win64",
- "linux",
- "OSX",
- "FreeBSD",
- "OpenBSD",
- "NetBSD",
- "DragonFlyBSD",
- "BSD",
- "Solaris",
- "Posix",
- "AIX",
- "Haiku",
- "SkyOS",
- "SysV3",
- "SysV4",
- "Hurd",
- "Android",
- "PlayStation",
- "PlayStation4",
- "Cygwin",
- "MinGW",
- "FreeStanding",
- "X86",
- "X86_64",
- "ARM",
- "ARM_Thumb",
- "ARM_SoftFloat",
- "ARM_SoftFP",
- "ARM_HardFloat",
- "AArch64",
- "Epiphany",
- "PPC",
- "PPC_SoftFloat",
- "PPC_HardFloat",
- "PPC64",
- "IA64",
- "MIPS32",
- "MIPS64",
- "MIPS_O32",
- "MIPS_N32",
- "MIPS_O64",
- "MIPS_N64",
- "MIPS_EABI",
- "MIPS_SoftFloat",
- "MIPS_HardFloat",
- "MSP430",
- "NVPTX",
- "NVPTX64",
- "RISCV32",
- "RISCV64",
- "SPARC",
- "SPARC_V8Plus",
- "SPARC_SoftFloat",
- "SPARC_HardFloat",
- "SPARC64",
- "S390",
- "S390X",
- "HPPA",
- "HPPA64",
- "SH",
- "Alpha",
- "Alpha_SoftFloat",
- "Alpha_HardFloat",
- "LittleEndian",
- "BigEndian",
- "ELFv1",
- "ELFv2",
- "CRuntime_Digitalmars",
- "CRuntime_Glibc",
- "CRuntime_Microsoft",
- "CRuntime_Musl",
- "CRuntime_UClibc",
- "CppRuntime_Clang",
- "CppRuntime_DigitalMars",
- "CppRuntime_Gcc",
- "CppRuntime_Microsoft",
- "CppRuntime_Sun",
- "D_Coverage",
- "D_Ddoc",
- "D_InlineAsm_X86",
- "D_InlineAsm_X86_64",
- "D_LP64",
- "D_X32",
- "D_HardFloat",
- "D_SoftFloat",
- "D_PIC",
- "D_SIMD",
- "D_Version2",
- "D_NoBoundsChecks",
- "unittest",
- "assert",
- "all",
- "none",
- NULL
- };
-
- for (unsigned i = 0; reserved[i]; i++)
- {
- if (strcmp(ident, reserved[i]) == 0)
- return true;
- }
-
- if (ident[0] == 'D' && ident[1] == '_')
- return true;
- return false;
-}
-
-void checkReserved(Loc loc, const char *ident)
-{
- if (isReserved(ident))
- error(loc, "version identifier `%s` is reserved and cannot be set", ident);
-}
-
-void VersionCondition::addGlobalIdent(const char *ident)
-{
- checkReserved(Loc(), ident);
- addPredefinedGlobalIdent(ident);
-}
-
-void VersionCondition::addPredefinedGlobalIdent(const char *ident)
-{
- if (!global.versionids)
- global.versionids = new Identifiers();
- global.versionids->push(Identifier::idPool(ident));
-}
-
-
-VersionCondition::VersionCondition(Module *mod, unsigned level, Identifier *ident)
- : DVCondition(mod, level, ident)
-{
-}
-
-int VersionCondition::include(Scope *sc)
-{
- //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel);
- //if (ident) printf("\tident = '%s'\n", ident->toChars());
- if (inc == 0)
- {
- inc = 2;
- bool definedInModule=false;
- if (ident)
- {
- if (findCondition(mod->versionids, ident))
- {
- inc = 1;
- definedInModule = true;
- }
- else if (findCondition(global.versionids, ident))
- inc = 1;
- else
- {
- if (!mod->versionidsNot)
- mod->versionidsNot = new Identifiers();
- mod->versionidsNot->push(ident);
- }
- }
- else if (level <= global.params.versionlevel || level <= mod->versionlevel)
- inc = 1;
- if (!definedInModule && (!ident || (!isReserved(ident->toChars()) && ident != Id::_unittest && ident != Id::_assert)))
- printDepsConditional(sc, this, "depsVersion ");
- }
- return (inc == 1);
-}
-
-/**************************** StaticIfCondition *******************************/
-
-StaticIfCondition::StaticIfCondition(Loc loc, Expression *exp)
- : Condition(loc)
-{
- this->exp = exp;
-}
-
-Condition *StaticIfCondition::syntaxCopy()
-{
- return new StaticIfCondition(loc, exp->syntaxCopy());
-}
-
-int StaticIfCondition::include(Scope *sc)
-{
- if (inc == 0)
- {
- if (!sc)
- {
- error(loc, "static if conditional cannot be at global scope");
- inc = 2;
- return 0;
- }
-
- sc = sc->push(sc->scopesym);
-
- bool errors = false;
-
- if (!exp)
- goto Lerror;
-
- bool result = evalStaticCondition(sc, exp, exp, errors);
- sc->pop();
-
- // Prevent repeated condition evaluation.
- // See: fail_compilation/fail7815.d
- if (inc != 0)
- return (inc == 1);
- if (errors)
- goto Lerror;
- if (result)
- inc = 1;
- else
- inc = 2;
- }
- return (inc == 1);
-
-Lerror:
- if (!global.gag)
- inc = 2; // so we don't see the error message again
- return 0;
-}
diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d
new file mode 100644
index 00000000000..d4a8b136d43
--- /dev/null
+++ b/gcc/d/dmd/cond.d
@@ -0,0 +1,1004 @@
+/**
+ * Evaluate compile-time conditionals, such as `static if` `version` and `debug`.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/version.html, Conditional Compilation)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cond.d, _cond.d)
+ * Documentation: https://dlang.org/phobos/dmd_cond.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cond.d
+ */
+
+module dmd.cond;
+
+import core.stdc.string;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.ast_node;
+import dmd.dcast;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.globals;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.typesem;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.tokens;
+import dmd.utils;
+import dmd.visitor;
+import dmd.id;
+import dmd.statement;
+import dmd.declaration;
+import dmd.dstruct;
+import dmd.func;
+
+/***********************************************************
+ */
+
+enum Include : ubyte
+{
+ notComputed, /// not computed yet
+ yes, /// include the conditional code
+ no, /// do not include the conditional code
+}
+
+extern (C++) abstract class Condition : ASTNode
+{
+ Loc loc;
+
+ Include inc;
+
+ override final DYNCAST dyncast() const
+ {
+ return DYNCAST.condition;
+ }
+
+ extern (D) this(const ref Loc loc)
+ {
+ this.loc = loc;
+ }
+
+ abstract Condition syntaxCopy();
+
+ abstract int include(Scope* sc);
+
+ inout(DebugCondition) isDebugCondition() inout
+ {
+ return null;
+ }
+
+ inout(VersionCondition) isVersionCondition() inout
+ {
+ return null;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Implements common functionality for StaticForeachDeclaration and
+ * StaticForeachStatement This performs the necessary lowerings before
+ * dmd.statementsem.makeTupleForeach can be used to expand the
+ * corresponding `static foreach` declaration or statement.
+ */
+
+extern (C++) final class StaticForeach : RootObject
+{
+ extern(D) static immutable tupleFieldName = "tuple"; // used in lowering
+
+ Loc loc;
+
+ /***************
+ * Not `null` iff the `static foreach` is over an aggregate. In
+ * this case, it contains the corresponding ForeachStatement. For
+ * StaticForeachDeclaration, the body is `null`.
+ */
+ ForeachStatement aggrfe;
+ /***************
+ * Not `null` iff the `static foreach` is over a range. Exactly
+ * one of the `aggrefe` and `rangefe` fields is not null. See
+ * `aggrfe` field for more details.
+ */
+ ForeachRangeStatement rangefe;
+
+ /***************
+ * true if it is necessary to expand a tuple into multiple
+ * variables (see lowerNonArrayAggregate).
+ */
+ bool needExpansion = false;
+
+ extern (D) this(const ref Loc loc, ForeachStatement aggrfe, ForeachRangeStatement rangefe)
+ {
+ assert(!!aggrfe ^ !!rangefe);
+
+ this.loc = loc;
+ this.aggrfe = aggrfe;
+ this.rangefe = rangefe;
+ }
+
+ StaticForeach syntaxCopy()
+ {
+ return new StaticForeach(
+ loc,
+ aggrfe ? aggrfe.syntaxCopy() : null,
+ rangefe ? rangefe.syntaxCopy() : null
+ );
+ }
+
+ /*****************************************
+ * Turn an aggregate which is an array into an expression tuple
+ * of its elements. I.e., lower
+ * static foreach (x; [1, 2, 3, 4]) { ... }
+ * to
+ * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... }
+ */
+ private extern(D) void lowerArrayAggregate(Scope* sc)
+ {
+ auto aggr = aggrfe.aggr;
+ Expression el = new ArrayLengthExp(aggr.loc, aggr);
+ sc = sc.startCTFE();
+ el = el.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ el = el.optimize(WANTvalue);
+ el = el.ctfeInterpret();
+ if (el.op == TOK.int64)
+ {
+ Expressions *es = void;
+ if (auto ale = aggr.isArrayLiteralExp())
+ {
+ // Directly use the elements of the array for the TupleExp creation
+ es = ale.elements;
+ }
+ else
+ {
+ const length = cast(size_t)el.toInteger();
+ es = new Expressions(length);
+ foreach (i; 0 .. length)
+ {
+ auto index = new IntegerExp(loc, i, Type.tsize_t);
+ auto value = new IndexExp(aggr.loc, aggr, index);
+ (*es)[i] = value;
+ }
+ }
+ aggrfe.aggr = new TupleExp(aggr.loc, es);
+ aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc);
+ aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue);
+ aggrfe.aggr = aggrfe.aggr.ctfeInterpret();
+ }
+ else
+ {
+ aggrfe.aggr = ErrorExp.get();
+ }
+ }
+
+ /*****************************************
+ * Wrap a statement into a function literal and call it.
+ *
+ * Params:
+ * loc = The source location.
+ * s = The statement.
+ * Returns:
+ * AST of the expression `(){ s; }()` with location loc.
+ */
+ private extern(D) Expression wrapAndCall(const ref Loc loc, Statement s)
+ {
+ auto tf = new TypeFunction(ParameterList(), null, LINK.default_, 0);
+ auto fd = new FuncLiteralDeclaration(loc, loc, tf, TOK.reserved, null);
+ fd.fbody = s;
+ auto fe = new FuncExp(loc, fd);
+ auto ce = new CallExp(loc, fe, new Expressions());
+ return ce;
+ }
+
+ /*****************************************
+ * Create a `foreach` statement from `aggrefe/rangefe` with given
+ * `foreach` variables and body `s`.
+ *
+ * Params:
+ * loc = The source location.
+ * parameters = The foreach variables.
+ * s = The `foreach` body.
+ * Returns:
+ * `foreach (parameters; aggregate) s;` or
+ * `foreach (parameters; lower .. upper) s;`
+ * Where aggregate/lower, upper are as for the current StaticForeach.
+ */
+ private extern(D) Statement createForeach(const ref Loc loc, Parameters* parameters, Statement s)
+ {
+ if (aggrfe)
+ {
+ return new ForeachStatement(loc, aggrfe.op, parameters, aggrfe.aggr.syntaxCopy(), s, loc);
+ }
+ else
+ {
+ assert(rangefe && parameters.dim == 1);
+ return new ForeachRangeStatement(loc, rangefe.op, (*parameters)[0], rangefe.lwr.syntaxCopy(), rangefe.upr.syntaxCopy(), s, loc);
+ }
+ }
+
+ /*****************************************
+ * For a `static foreach` with multiple loop variables, the
+ * aggregate is lowered to an array of tuples. As D does not have
+ * built-in tuples, we need a suitable tuple type. This generates
+ * a `struct` that serves as the tuple type. This type is only
+ * used during CTFE and hence its typeinfo will not go to the
+ * object file.
+ *
+ * Params:
+ * loc = The source location.
+ * e = The expressions we wish to store in the tuple.
+ * sc = The current scope.
+ * Returns:
+ * A struct type of the form
+ * struct Tuple
+ * {
+ * typeof(AliasSeq!(e)) tuple;
+ * }
+ */
+
+ private extern(D) TypeStruct createTupleType(const ref Loc loc, Expressions* e, Scope* sc)
+ { // TODO: move to druntime?
+ auto sid = Identifier.generateId("Tuple");
+ auto sdecl = new StructDeclaration(loc, sid, false);
+ sdecl.storage_class |= STC.static_;
+ sdecl.members = new Dsymbols();
+ auto fid = Identifier.idPool(tupleFieldName.ptr, tupleFieldName.length);
+ auto ty = new TypeTypeof(loc, new TupleExp(loc, e));
+ sdecl.members.push(new VarDeclaration(loc, ty, fid, null, 0));
+ auto r = cast(TypeStruct)sdecl.type;
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ r.vtinfo = TypeInfoStructDeclaration.create(r); // prevent typeinfo from going to object file
+ return r;
+ }
+
+ /*****************************************
+ * Create the AST for an instantiation of a suitable tuple type.
+ *
+ * Params:
+ * loc = The source location.
+ * type = A Tuple type, created with createTupleType.
+ * e = The expressions we wish to store in the tuple.
+ * Returns:
+ * An AST for the expression `Tuple(e)`.
+ */
+
+ private extern(D) Expression createTuple(const ref Loc loc, TypeStruct type, Expressions* e)
+ { // TODO: move to druntime?
+ return new CallExp(loc, new TypeExp(loc, type), e);
+ }
+
+
+ /*****************************************
+ * Lower any aggregate that is not an array to an array using a
+ * regular foreach loop within CTFE. If there are multiple
+ * `static foreach` loop variables, an array of tuples is
+ * generated. In thise case, the field `needExpansion` is set to
+ * true to indicate that the static foreach loop expansion will
+ * need to expand the tuples into multiple variables.
+ *
+ * For example, `static foreach (x; range) { ... }` is lowered to:
+ *
+ * static foreach (x; {
+ * typeof({
+ * foreach (x; range) return x;
+ * }())[] __res;
+ * foreach (x; range) __res ~= x;
+ * return __res;
+ * }()) { ... }
+ *
+ * Finally, call `lowerArrayAggregate` to turn the produced
+ * array into an expression tuple.
+ *
+ * Params:
+ * sc = The current scope.
+ */
+
+ private void lowerNonArrayAggregate(Scope* sc)
+ {
+ auto nvars = aggrfe ? aggrfe.parameters.dim : 1;
+ auto aloc = aggrfe ? aggrfe.aggr.loc : rangefe.lwr.loc;
+ // We need three sets of foreach loop variables because the
+ // lowering contains three foreach loops.
+ Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()];
+ foreach (i; 0 .. nvars)
+ {
+ foreach (params; pparams)
+ {
+ auto p = aggrfe ? (*aggrfe.parameters)[i] : rangefe.prm;
+ params.push(new Parameter(p.storageClass, p.type, p.ident, null, null));
+ }
+ }
+ Expression[2] res;
+ TypeStruct tplty = null;
+ if (nvars == 1) // only one `static foreach` variable, generate identifiers.
+ {
+ foreach (i; 0 .. 2)
+ {
+ res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident);
+ }
+ }
+ else // multiple `static foreach` variables, generate tuples.
+ {
+ foreach (i; 0 .. 2)
+ {
+ auto e = new Expressions(pparams[0].dim);
+ foreach (j, ref elem; *e)
+ {
+ auto p = (*pparams[i])[j];
+ elem = new IdentifierExp(aloc, p.ident);
+ }
+ if (!tplty)
+ {
+ tplty = createTupleType(aloc, e, sc);
+ }
+ res[i] = createTuple(aloc, tplty, e);
+ }
+ needExpansion = true; // need to expand the tuples later
+ }
+ // generate remaining code for the new aggregate which is an
+ // array (see documentation comment).
+ if (rangefe)
+ {
+ sc = sc.startCTFE();
+ rangefe.lwr = rangefe.lwr.expressionSemantic(sc);
+ rangefe.lwr = resolveProperties(sc, rangefe.lwr);
+ rangefe.upr = rangefe.upr.expressionSemantic(sc);
+ rangefe.upr = resolveProperties(sc, rangefe.upr);
+ sc = sc.endCTFE();
+ rangefe.lwr = rangefe.lwr.optimize(WANTvalue);
+ rangefe.lwr = rangefe.lwr.ctfeInterpret();
+ rangefe.upr = rangefe.upr.optimize(WANTvalue);
+ rangefe.upr = rangefe.upr.ctfeInterpret();
+ }
+ auto s1 = new Statements();
+ auto sfe = new Statements();
+ if (tplty) sfe.push(new ExpStatement(loc, tplty.sym));
+ sfe.push(new ReturnStatement(aloc, res[0]));
+ s1.push(createForeach(aloc, pparams[0], new CompoundStatement(aloc, sfe)));
+ s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0)));
+ Type ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1)));
+ auto aty = ety.arrayOf();
+ auto idres = Identifier.generateId("__res");
+ auto vard = new VarDeclaration(aloc, aty, idres, null);
+ auto s2 = new Statements();
+
+ // Run 'typeof' gagged to avoid duplicate errors and if it fails just create
+ // an empty foreach to expose them.
+ uint olderrors = global.startGagging();
+ ety = ety.typeSemantic(aloc, sc);
+ if (global.endGagging(olderrors))
+ s2.push(createForeach(aloc, pparams[1], null));
+ else
+ {
+ s2.push(new ExpStatement(aloc, vard));
+ auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]);
+ s2.push(createForeach(aloc, pparams[1], new ExpStatement(aloc, catass)));
+ s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres)));
+ }
+
+ Expression aggr = void;
+ Type indexty = void;
+
+ if (rangefe && (indexty = ety).isintegral())
+ {
+ rangefe.lwr.type = indexty;
+ rangefe.upr.type = indexty;
+ auto lwrRange = getIntRange(rangefe.lwr);
+ auto uprRange = getIntRange(rangefe.upr);
+
+ const lwr = rangefe.lwr.toInteger();
+ auto upr = rangefe.upr.toInteger();
+ size_t length = 0;
+
+ if (lwrRange.imin <= uprRange.imax)
+ length = cast(size_t) (upr - lwr);
+
+ auto exps = new Expressions(length);
+
+ if (rangefe.op == TOK.foreach_)
+ {
+ foreach (i; 0 .. length)
+ (*exps)[i] = new IntegerExp(aloc, lwr + i, indexty);
+ }
+ else
+ {
+ --upr;
+ foreach (i; 0 .. length)
+ (*exps)[i] = new IntegerExp(aloc, upr - i, indexty);
+ }
+ aggr = new ArrayLiteralExp(aloc, indexty.arrayOf(), exps);
+ }
+ else
+ {
+ aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2));
+ sc = sc.startCTFE();
+ aggr = aggr.expressionSemantic(sc);
+ aggr = resolveProperties(sc, aggr);
+ sc = sc.endCTFE();
+ aggr = aggr.optimize(WANTvalue);
+ aggr = aggr.ctfeInterpret();
+ }
+
+ assert(!!aggrfe ^ !!rangefe);
+ aggrfe = new ForeachStatement(loc, TOK.foreach_, pparams[2], aggr,
+ aggrfe ? aggrfe._body : rangefe._body,
+ aggrfe ? aggrfe.endloc : rangefe.endloc);
+ rangefe = null;
+ lowerArrayAggregate(sc); // finally, turn generated array into expression tuple
+ }
+
+ /*****************************************
+ * Perform `static foreach` lowerings that are necessary in order
+ * to finally expand the `static foreach` using
+ * `dmd.statementsem.makeTupleForeach`.
+ */
+ extern(D) void prepare(Scope* sc)
+ {
+ assert(sc);
+
+ if (aggrfe)
+ {
+ sc = sc.startCTFE();
+ aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue);
+ }
+
+ if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Terror)
+ {
+ return;
+ }
+
+ if (!ready())
+ {
+ if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Tarray)
+ {
+ lowerArrayAggregate(sc);
+ }
+ else
+ {
+ lowerNonArrayAggregate(sc);
+ }
+ }
+ }
+
+ /*****************************************
+ * Returns:
+ * `true` iff ready to call `dmd.statementsem.makeTupleForeach`.
+ */
+ extern(D) bool ready()
+ {
+ return aggrfe && aggrfe.aggr && aggrfe.aggr.type && aggrfe.aggr.type.toBasetype().ty == Ttuple;
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class DVCondition : Condition
+{
+ uint level;
+ Identifier ident;
+ Module mod;
+
+ extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident)
+ {
+ super(loc);
+ this.mod = mod;
+ this.level = level;
+ this.ident = ident;
+ }
+
+ override final DVCondition syntaxCopy()
+ {
+ return this; // don't need to copy
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DebugCondition : DVCondition
+{
+ /**
+ * Add an user-supplied identifier to the list of global debug identifiers
+ *
+ * Can be called from either the driver or a `debug = Ident;` statement.
+ * Unlike version identifier, there isn't any reserved debug identifier
+ * so no validation takes place.
+ *
+ * Params:
+ * ident = identifier to add
+ */
+ deprecated("Kept for C++ compat - Use the string overload instead")
+ static void addGlobalIdent(const(char)* ident)
+ {
+ addGlobalIdent(ident[0 .. ident.strlen]);
+ }
+
+ /// Ditto
+ extern(D) static void addGlobalIdent(string ident)
+ {
+ // Overload necessary for string literals
+ addGlobalIdent(cast(const(char)[])ident);
+ }
+
+
+ /// Ditto
+ extern(D) static void addGlobalIdent(const(char)[] ident)
+ {
+ if (!global.debugids)
+ global.debugids = new Identifiers();
+ global.debugids.push(Identifier.idPool(ident));
+ }
+
+
+ /**
+ * Instantiate a new `DebugCondition`
+ *
+ * Params:
+ * mod = Module this node belongs to
+ * level = Minimum global level this condition needs to pass.
+ * Only used if `ident` is `null`.
+ * ident = Identifier required for this condition to pass.
+ * If `null`, this conditiion will use an integer level.
+ * loc = Location in the source file
+ */
+ extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident)
+ {
+ super(loc, mod, level, ident);
+ }
+
+ override int include(Scope* sc)
+ {
+ //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel);
+ if (inc == Include.notComputed)
+ {
+ inc = Include.no;
+ bool definedInModule = false;
+ if (ident)
+ {
+ if (findCondition(mod.debugids, ident))
+ {
+ inc = Include.yes;
+ definedInModule = true;
+ }
+ else if (findCondition(global.debugids, ident))
+ inc = Include.yes;
+ else
+ {
+ if (!mod.debugidsNot)
+ mod.debugidsNot = new Identifiers();
+ mod.debugidsNot.push(ident);
+ }
+ }
+ else if (level <= global.params.debuglevel || level <= mod.debuglevel)
+ inc = Include.yes;
+ if (!definedInModule)
+ printDepsConditional(sc, this, "depsDebug ");
+ }
+ return (inc == Include.yes);
+ }
+
+ override inout(DebugCondition) isDebugCondition() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ override const(char)* toChars() const
+ {
+ return ident ? ident.toChars() : "debug".ptr;
+ }
+}
+
+/**
+ * Node to represent a version condition
+ *
+ * A version condition is of the form:
+ * ---
+ * version (Identifier)
+ * ---
+ * In user code.
+ * This class also provides means to add version identifier
+ * to the list of global (cross module) identifiers.
+ */
+extern (C++) final class VersionCondition : DVCondition
+{
+ /**
+ * Check if a given version identifier is reserved.
+ *
+ * Params:
+ * ident = identifier being checked
+ *
+ * Returns:
+ * `true` if it is reserved, `false` otherwise
+ */
+ extern(D) private static bool isReserved(const(char)[] ident)
+ {
+ // This list doesn't include "D_*" versions, see the last return
+ switch (ident)
+ {
+ case "AArch64":
+ case "AIX":
+ case "all":
+ case "Alpha":
+ case "Alpha_HardFloat":
+ case "Alpha_SoftFloat":
+ case "Android":
+ case "ARM":
+ case "ARM_HardFloat":
+ case "ARM_SoftFloat":
+ case "ARM_SoftFP":
+ case "ARM_Thumb":
+ case "AsmJS":
+ case "assert":
+ case "AVR":
+ case "BigEndian":
+ case "BSD":
+ case "CppRuntime_Clang":
+ case "CppRuntime_DigitalMars":
+ case "CppRuntime_Gcc":
+ case "CppRuntime_Microsoft":
+ case "CppRuntime_Sun":
+ case "CRuntime_Bionic":
+ case "CRuntime_DigitalMars":
+ case "CRuntime_Glibc":
+ case "CRuntime_Microsoft":
+ case "CRuntime_Musl":
+ case "CRuntime_Newlib":
+ case "CRuntime_UClibc":
+ case "CRuntime_WASI":
+ case "Cygwin":
+ case "DigitalMars":
+ case "DragonFlyBSD":
+ case "Emscripten":
+ case "ELFv1":
+ case "ELFv2":
+ case "Epiphany":
+ case "FreeBSD":
+ case "FreeStanding":
+ case "GNU":
+ case "Haiku":
+ case "HPPA":
+ case "HPPA64":
+ case "Hurd":
+ case "IA64":
+ case "iOS":
+ case "LDC":
+ case "linux":
+ case "LittleEndian":
+ case "MinGW":
+ case "MIPS32":
+ case "MIPS64":
+ case "MIPS_EABI":
+ case "MIPS_HardFloat":
+ case "MIPS_N32":
+ case "MIPS_N64":
+ case "MIPS_O32":
+ case "MIPS_O64":
+ case "MIPS_SoftFloat":
+ case "MSP430":
+ case "NetBSD":
+ case "none":
+ case "NVPTX":
+ case "NVPTX64":
+ case "OpenBSD":
+ case "OSX":
+ case "PlayStation":
+ case "PlayStation4":
+ case "Posix":
+ case "PPC":
+ case "PPC64":
+ case "PPC_HardFloat":
+ case "PPC_SoftFloat":
+ case "RISCV32":
+ case "RISCV64":
+ case "S390":
+ case "S390X":
+ case "SDC":
+ case "SH":
+ case "SkyOS":
+ case "Solaris":
+ case "SPARC":
+ case "SPARC64":
+ case "SPARC_HardFloat":
+ case "SPARC_SoftFloat":
+ case "SPARC_V8Plus":
+ case "SystemZ":
+ case "SysV3":
+ case "SysV4":
+ case "TVOS":
+ case "unittest":
+ case "WASI":
+ case "WatchOS":
+ case "WebAssembly":
+ case "Win32":
+ case "Win64":
+ case "Windows":
+ case "X86":
+ case "X86_64":
+ return true;
+
+ default:
+ // Anything that starts with "D_" is reserved
+ return (ident.length >= 2 && ident[0 .. 2] == "D_");
+ }
+ }
+
+ /**
+ * Raises an error if a version identifier is reserved.
+ *
+ * Called when setting a version identifier, e.g. `-version=identifier`
+ * parameter to the compiler or `version = Foo` in user code.
+ *
+ * Params:
+ * loc = Where the identifier is set
+ * ident = identifier being checked (ident[$] must be '\0')
+ */
+ extern(D) static void checkReserved(const ref Loc loc, const(char)[] ident)
+ {
+ if (isReserved(ident))
+ error(loc, "version identifier `%s` is reserved and cannot be set",
+ ident.ptr);
+ }
+
+ /**
+ * Add an user-supplied global identifier to the list
+ *
+ * Only called from the driver for `-version=Ident` parameters.
+ * Will raise an error if the identifier is reserved.
+ *
+ * Params:
+ * ident = identifier to add
+ */
+ deprecated("Kept for C++ compat - Use the string overload instead")
+ static void addGlobalIdent(const(char)* ident)
+ {
+ addGlobalIdent(ident[0 .. ident.strlen]);
+ }
+
+ /// Ditto
+ extern(D) static void addGlobalIdent(string ident)
+ {
+ // Overload necessary for string literals
+ addGlobalIdent(cast(const(char)[])ident);
+ }
+
+
+ /// Ditto
+ extern(D) static void addGlobalIdent(const(char)[] ident)
+ {
+ checkReserved(Loc.initial, ident);
+ addPredefinedGlobalIdent(ident);
+ }
+
+ /**
+ * Add any global identifier to the list, without checking
+ * if it's predefined
+ *
+ * Only called from the driver after platform detection,
+ * and internally.
+ *
+ * Params:
+ * ident = identifier to add (ident[$] must be '\0')
+ */
+ deprecated("Kept for C++ compat - Use the string overload instead")
+ static void addPredefinedGlobalIdent(const(char)* ident)
+ {
+ addPredefinedGlobalIdent(ident.toDString());
+ }
+
+ /// Ditto
+ extern(D) static void addPredefinedGlobalIdent(string ident)
+ {
+ // Forward: Overload necessary for string literal
+ addPredefinedGlobalIdent(cast(const(char)[])ident);
+ }
+
+
+ /// Ditto
+ extern(D) static void addPredefinedGlobalIdent(const(char)[] ident)
+ {
+ if (!global.versionids)
+ global.versionids = new Identifiers();
+ global.versionids.push(Identifier.idPool(ident));
+ }
+
+ /**
+ * Instantiate a new `VersionCondition`
+ *
+ * Params:
+ * mod = Module this node belongs to
+ * level = Minimum global level this condition needs to pass.
+ * Only used if `ident` is `null`.
+ * ident = Identifier required for this condition to pass.
+ * If `null`, this conditiion will use an integer level.
+ * loc = Location in the source file
+ */
+ extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident)
+ {
+ super(loc, mod, level, ident);
+ }
+
+ override int include(Scope* sc)
+ {
+ //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel);
+ //if (ident) printf("\tident = '%s'\n", ident.toChars());
+ if (inc == Include.notComputed)
+ {
+ inc = Include.no;
+ bool definedInModule = false;
+ if (ident)
+ {
+ if (findCondition(mod.versionids, ident))
+ {
+ inc = Include.yes;
+ definedInModule = true;
+ }
+ else if (findCondition(global.versionids, ident))
+ inc = Include.yes;
+ else
+ {
+ if (!mod.versionidsNot)
+ mod.versionidsNot = new Identifiers();
+ mod.versionidsNot.push(ident);
+ }
+ }
+ else if (level <= global.params.versionlevel || level <= mod.versionlevel)
+ inc = Include.yes;
+ if (!definedInModule &&
+ (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert)))
+ {
+ printDepsConditional(sc, this, "depsVersion ");
+ }
+ }
+ return (inc == Include.yes);
+ }
+
+ override inout(VersionCondition) isVersionCondition() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ override const(char)* toChars() const
+ {
+ return ident ? ident.toChars() : "version".ptr;
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class StaticIfCondition : Condition
+{
+ Expression exp;
+
+ extern (D) this(const ref Loc loc, Expression exp)
+ {
+ super(loc);
+ this.exp = exp;
+ }
+
+ override StaticIfCondition syntaxCopy()
+ {
+ return new StaticIfCondition(loc, exp.syntaxCopy());
+ }
+
+ override int include(Scope* sc)
+ {
+ // printf("StaticIfCondition::include(sc = %p) this=%p inc = %d\n", sc, this, inc);
+
+ int errorReturn()
+ {
+ if (!global.gag)
+ inc = Include.no; // so we don't see the error message again
+ return 0;
+ }
+
+ if (inc == Include.notComputed)
+ {
+ if (!sc)
+ {
+ error(loc, "`static if` conditional cannot be at global scope");
+ inc = Include.no;
+ return 0;
+ }
+
+ import dmd.staticcond;
+ bool errors;
+
+ if (!exp)
+ return errorReturn();
+
+ bool result = evalStaticCondition(sc, exp, exp, errors);
+
+ // Prevent repeated condition evaluation.
+ // See: fail_compilation/fail7815.d
+ if (inc != Include.notComputed)
+ return (inc == Include.yes);
+ if (errors)
+ return errorReturn();
+ if (result)
+ inc = Include.yes;
+ else
+ inc = Include.no;
+ }
+ return (inc == Include.yes);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ override const(char)* toChars() const
+ {
+ return exp ? exp.toChars() : "static if".ptr;
+ }
+}
+
+
+/****************************************
+ * Find `ident` in an array of identifiers.
+ * Params:
+ * ids = array of identifiers
+ * ident = identifier to search for
+ * Returns:
+ * true if found
+ */
+bool findCondition(Identifiers* ids, Identifier ident) @safe nothrow pure
+{
+ if (ids)
+ {
+ foreach (id; *ids)
+ {
+ if (id == ident)
+ return true;
+ }
+ }
+ return false;
+}
+
+// Helper for printing dependency information
+private void printDepsConditional(Scope* sc, DVCondition condition, const(char)[] depType)
+{
+ if (!global.params.moduleDeps || global.params.moduleDepsFile)
+ return;
+ OutBuffer* ob = global.params.moduleDeps;
+ Module imod = sc ? sc._module : condition.mod;
+ if (!imod)
+ return;
+ ob.writestring(depType);
+ ob.writestring(imod.toPrettyChars());
+ ob.writestring(" (");
+ escapePath(ob, imod.srcfile.toChars());
+ ob.writestring(") : ");
+ if (condition.ident)
+ ob.writestring(condition.ident.toString());
+ else
+ ob.print(condition.level);
+ ob.writeByte('\n');
+}
diff --git a/gcc/d/dmd/cond.h b/gcc/d/dmd/cond.h
index 593a98d10b4..4f261162ebb 100644
--- a/gcc/d/dmd/cond.h
+++ b/gcc/d/dmd/cond.h
@@ -16,26 +16,24 @@
class Expression;
class Identifier;
-struct OutBuffer;
class Module;
struct Scope;
-class ScopeDsymbol;
class DebugCondition;
class ForeachStatement;
class ForeachRangeStatement;
-int findCondition(Identifiers *ids, Identifier *ident);
+enum Include
+{
+ INCLUDEnotComputed, /// not computed yet
+ INCLUDEyes, /// include the conditional code
+ INCLUDEno /// do not include the conditional code
+};
class Condition : public ASTNode
{
public:
Loc loc;
- // 0: not computed yet
- // 1: include
- // 2: do not include
- int inc;
-
- Condition(Loc loc);
+ Include inc;
virtual Condition *syntaxCopy() = 0;
virtual int include(Scope *sc) = 0;
@@ -54,13 +52,9 @@ public:
bool needExpansion;
- StaticForeach(Loc loc, ForeachStatement *aggrfe, ForeachRangeStatement *rangefe);
StaticForeach *syntaxCopy();
};
-void staticForeachPrepare(StaticForeach *sfe, Scope *sc);
-bool staticForeachReady(StaticForeach *sfe);
-
class DVCondition : public Condition
{
public:
@@ -68,9 +62,7 @@ public:
Identifier *ident;
Module *mod;
- DVCondition(Module *mod, unsigned level, Identifier *ident);
-
- Condition *syntaxCopy();
+ DVCondition *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -79,8 +71,6 @@ class DebugCondition : public DVCondition
public:
static void addGlobalIdent(const char *ident);
- DebugCondition(Module *mod, unsigned level, Identifier *ident);
-
int include(Scope *sc);
DebugCondition *isDebugCondition() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -92,8 +82,6 @@ public:
static void addGlobalIdent(const char *ident);
static void addPredefinedGlobalIdent(const char *ident);
- VersionCondition(Module *mod, unsigned level, Identifier *ident);
-
int include(Scope *sc);
VersionCondition *isVersionCondition() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -104,8 +92,7 @@ class StaticIfCondition : public Condition
public:
Expression *exp;
- StaticIfCondition(Loc loc, Expression *exp);
- Condition *syntaxCopy();
+ StaticIfCondition *syntaxCopy();
int include(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
diff --git a/gcc/d/dmd/constfold.c b/gcc/d/dmd/constfold.c
deleted file mode 100644
index 8cfeac54e20..00000000000
--- a/gcc/d/dmd/constfold.c
+++ /dev/null
@@ -1,1922 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/constfold.c
- */
-
-#include "root/dsystem.h" // mem{cpy|set|cmp}()
-
-#ifndef IN_GCC
-#include <math.h>
-#endif
-
-#include "root/rmem.h"
-#include "root/root.h"
-#include "root/port.h"
-
-#include "errors.h"
-#include "mtype.h"
-#include "expression.h"
-#include "aggregate.h"
-#include "declaration.h"
-#include "utf.h"
-#include "ctfe.h"
-#include "target.h"
-
-int RealEquals(real_t x1, real_t x2);
-
-Expression *expType(Type *type, Expression *e)
-{
- if (type != e->type)
- {
- e = e->copy();
- e->type = type;
- }
- return e;
-}
-
-/* ================================== isConst() ============================== */
-
-int isConst(Expression *e)
-{
- //printf("Expression::isConst(): %s\n", e->toChars());
- switch (e->op)
- {
- case TOKint64:
- case TOKfloat64:
- case TOKcomplex80:
- return 1;
- case TOKnull:
- return 0;
- case TOKsymoff:
- return 2;
- default:
- return 0;
- }
- assert(0);
- return 0;
-}
-
-/* =============================== constFold() ============================== */
-
-/* The constFold() functions were redundant with the optimize() ones,
- * and so have been folded in with them.
- */
-
-/* ========================================================================== */
-
-UnionExp Neg(Type *type, Expression *e1)
-{
- UnionExp ue;
- Loc loc = e1->loc;
-
- if (e1->type->isreal())
- {
- new(&ue) RealExp(loc, -e1->toReal(), type);
- }
- else if (e1->type->isimaginary())
- {
- new(&ue) RealExp(loc, -e1->toImaginary(), type);
- }
- else if (e1->type->iscomplex())
- {
- new(&ue) ComplexExp(loc, -e1->toComplex(), type);
- }
- else
- {
- new(&ue) IntegerExp(loc, -e1->toInteger(), type);
- }
- return ue;
-}
-
-UnionExp Com(Type *type, Expression *e1)
-{
- UnionExp ue;
- Loc loc = e1->loc;
-
- new(&ue) IntegerExp(loc, ~e1->toInteger(), type);
- return ue;
-}
-
-UnionExp Not(Type *type, Expression *e1)
-{
- UnionExp ue;
- Loc loc = e1->loc;
-
- new(&ue) IntegerExp(loc, e1->isBool(false) ? 1 : 0, type);
- return ue;
-}
-
-UnionExp Bool(Type *type, Expression *e1)
-{
- UnionExp ue;
- Loc loc = e1->loc;
-
- new(&ue) IntegerExp(loc, e1->isBool(true) ? 1 : 0, type);
- return ue;
-}
-
-UnionExp Add(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
-
- if (type->isreal())
- {
- new(&ue) RealExp(loc, e1->toReal() + e2->toReal(), type);
- }
- else if (type->isimaginary())
- {
- new(&ue) RealExp(loc, e1->toImaginary() + e2->toImaginary(), type);
- }
- else if (type->iscomplex())
- {
- // This rigamarole is necessary so that -0.0 doesn't get
- // converted to +0.0 by doing an extraneous add with +0.0
- complex_t c1 = complex_t(CTFloat::zero);
- real_t r1 = CTFloat::zero;
- real_t i1 = CTFloat::zero;
-
- complex_t c2 = complex_t(CTFloat::zero);
- real_t r2 = CTFloat::zero;
- real_t i2 = CTFloat::zero;
-
- complex_t v = complex_t(CTFloat::zero);
- int x;
-
- if (e1->type->isreal())
- {
- r1 = e1->toReal();
- x = 0;
- }
- else if (e1->type->isimaginary())
- {
- i1 = e1->toImaginary();
- x = 3;
- }
- else
- {
- c1 = e1->toComplex();
- x = 6;
- }
-
- if (e2->type->isreal())
- {
- r2 = e2->toReal();
- }
- else if (e2->type->isimaginary())
- {
- i2 = e2->toImaginary();
- x += 1;
- }
- else
- {
- c2 = e2->toComplex();
- x += 2;
- }
-
- switch (x)
- {
- case 0 + 0:
- v = complex_t(r1 + r2);
- break;
- case 0 + 1:
- v = complex_t(r1, i2);
- break;
- case 0 + 2:
- v = complex_t(r1 + creall(c2), cimagl(c2));
- break;
- case 3 + 0:
- v = complex_t(r2, i1);
- break;
- case 3 + 1:
- v = complex_t(CTFloat::zero, i1 + i2);
- break;
- case 3 + 2:
- v = complex_t(creall(c2), i1 + cimagl(c2));
- break;
- case 6 + 0:
- v = complex_t(creall(c1) + r2, cimagl(c2));
- break;
- case 6 + 1:
- v = complex_t(creall(c1), cimagl(c1) + i2);
- break;
- case 6 + 2:
- v = c1 + c2;
- break;
- default:
- assert(0);
- }
- new(&ue) ComplexExp(loc, v, type);
- }
- else if (e1->op == TOKsymoff)
- {
- SymOffExp *soe = (SymOffExp *)e1;
- new(&ue) SymOffExp(loc, soe->var, soe->offset + e2->toInteger());
- ue.exp()->type = type;
- }
- else if (e2->op == TOKsymoff)
- {
- SymOffExp *soe = (SymOffExp *)e2;
- new(&ue) SymOffExp(loc, soe->var, soe->offset + e1->toInteger());
- ue.exp()->type = type;
- }
- else
- new(&ue) IntegerExp(loc, e1->toInteger() + e2->toInteger(), type);
- return ue;
-}
-
-
-UnionExp Min(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
-
- if (type->isreal())
- {
- new(&ue) RealExp(loc, e1->toReal() - e2->toReal(), type);
- }
- else if (type->isimaginary())
- {
- new(&ue) RealExp(loc, e1->toImaginary() - e2->toImaginary(), type);
- }
- else if (type->iscomplex())
- {
- // This rigamarole is necessary so that -0.0 doesn't get
- // converted to +0.0 by doing an extraneous add with +0.0
- complex_t c1 = complex_t(CTFloat::zero);
- real_t r1 = CTFloat::zero;
- real_t i1 = CTFloat::zero;
-
- complex_t c2 = complex_t(CTFloat::zero);
- real_t r2 = CTFloat::zero;
- real_t i2 = CTFloat::zero;
-
- complex_t v = complex_t(CTFloat::zero);
- int x;
-
- if (e1->type->isreal())
- {
- r1 = e1->toReal();
- x = 0;
- }
- else if (e1->type->isimaginary())
- {
- i1 = e1->toImaginary();
- x = 3;
- }
- else
- {
- c1 = e1->toComplex();
- x = 6;
- }
-
- if (e2->type->isreal())
- {
- r2 = e2->toReal();
- }
- else if (e2->type->isimaginary())
- {
- i2 = e2->toImaginary();
- x += 1;
- }
- else
- {
- c2 = e2->toComplex();
- x += 2;
- }
-
- switch (x)
- {
- case 0 + 0:
- v = complex_t(r1 - r2);
- break;
- case 0 + 1:
- v = complex_t(r1, -i2);
- break;
- case 0 + 2:
- v = complex_t(r1 - creall(c2), -cimagl(c2));
- break;
- case 3 + 0:
- v = complex_t(-r2, i1);
- break;
- case 3 + 1:
- v = complex_t(CTFloat::zero, i1 - i2);
- break;
- case 3 + 2:
- v = complex_t(-creall(c2), i1 - cimagl(c2));
- break;
- case 6 + 0:
- v = complex_t(creall(c1) - r2, cimagl(c1));
- break;
- case 6 + 1:
- v = complex_t(creall(c1), cimagl(c1) - i2);
- break;
- case 6 + 2:
- v = c1 - c2;
- break;
- default:
- assert(0);
- }
- new(&ue) ComplexExp(loc, v, type);
- }
- else if (e1->op == TOKsymoff)
- {
- SymOffExp *soe = (SymOffExp *)e1;
- new(&ue) SymOffExp(loc, soe->var, soe->offset - e2->toInteger());
- ue.exp()->type = type;
- }
- else
- {
- new(&ue) IntegerExp(loc, e1->toInteger() - e2->toInteger(), type);
- }
- return ue;
-}
-
-UnionExp Mul(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
-
- if (type->isfloating())
- {
- complex_t c = complex_t(CTFloat::zero);
- real_t r;
-
- if (e1->type->isreal())
- {
- r = e1->toReal();
- c = e2->toComplex();
- c = complex_t(r * creall(c), r * cimagl(c));
- }
- else if (e1->type->isimaginary())
- {
- r = e1->toImaginary();
- c = e2->toComplex();
- c = complex_t(-r * cimagl(c), r * creall(c));
- }
- else if (e2->type->isreal())
- {
- r = e2->toReal();
- c = e1->toComplex();
- c = complex_t(r * creall(c), r * cimagl(c));
- }
- else if (e2->type->isimaginary())
- {
- r = e2->toImaginary();
- c = e1->toComplex();
- c = complex_t(-r * cimagl(c), r * creall(c));
- }
- else
- c = e1->toComplex() * e2->toComplex();
-
- if (type->isreal())
- new(&ue) RealExp(loc, creall(c), type);
- else if (type->isimaginary())
- new(&ue) RealExp(loc, cimagl(c), type);
- else if (type->iscomplex())
- new(&ue) ComplexExp(loc, c, type);
- else
- assert(0);
- }
- else
- {
- new(&ue) IntegerExp(loc, e1->toInteger() * e2->toInteger(), type);
- }
- return ue;
-}
-
-UnionExp Div(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
-
- if (type->isfloating())
- {
- complex_t c = complex_t(CTFloat::zero);
- real_t r;
-
- //e1->type->print();
- //e2->type->print();
- if (e2->type->isreal())
- {
- if (e1->type->isreal())
- {
- new(&ue) RealExp(loc, e1->toReal() / e2->toReal(), type);
- return ue;
- }
- r = e2->toReal();
- c = e1->toComplex();
- c = complex_t(creall(c) / r, cimagl(c) / r);
- }
- else if (e2->type->isimaginary())
- {
- r = e2->toImaginary();
- c = e1->toComplex();
- c = complex_t(cimagl(c) / r, -creall(c) / r);
- }
- else
- {
- c = e1->toComplex() / e2->toComplex();
- }
-
- if (type->isreal())
- new(&ue) RealExp(loc, creall(c), type);
- else if (type->isimaginary())
- new(&ue) RealExp(loc, cimagl(c), type);
- else if (type->iscomplex())
- new(&ue) ComplexExp(loc, c, type);
- else
- assert(0);
- }
- else
- {
- sinteger_t n1;
- sinteger_t n2;
- sinteger_t n;
-
- n1 = e1->toInteger();
- n2 = e2->toInteger();
- if (n2 == 0)
- {
- e2->error("divide by 0");
- new(&ue) ErrorExp();
- return ue;
- }
- if (n2 == -1 && !type->isunsigned())
- {
- // Check for int.min / -1
- if ((dinteger_t)n1 == 0xFFFFFFFF80000000ULL && type->toBasetype()->ty != Tint64)
- {
- e2->error("integer overflow: int.min / -1");
- new(&ue) ErrorExp();
- return ue;
- }
- else if ((dinteger_t)n1 == 0x8000000000000000LL) // long.min / -1
- {
- e2->error("integer overflow: long.min / -1");
- new(&ue) ErrorExp();
- return ue;
- }
- }
- if (e1->type->isunsigned() || e2->type->isunsigned())
- n = ((dinteger_t) n1) / ((dinteger_t) n2);
- else
- n = n1 / n2;
- new(&ue) IntegerExp(loc, n, type);
- }
- return ue;
-}
-
-UnionExp Mod(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
-
- if (type->isfloating())
- {
- complex_t c = complex_t(CTFloat::zero);
-
- if (e2->type->isreal())
- {
- real_t r2 = e2->toReal();
-
-#ifdef IN_GCC
- c = complex_t(e1->toReal() % r2, e1->toImaginary() % r2);
-#else
- c = complex_t(::fmodl(e1->toReal(), r2), ::fmodl(e1->toImaginary(), r2));
-#endif
- }
- else if (e2->type->isimaginary())
- {
- real_t i2 = e2->toImaginary();
-
-#ifdef IN_GCC
- c = complex_t(e1->toReal() % i2, e1->toImaginary() % i2);
-#else
- c = complex_t(::fmodl(e1->toReal(), i2), ::fmodl(e1->toImaginary(), i2));
-#endif
- }
- else
- assert(0);
-
- if (type->isreal())
- new(&ue) RealExp(loc, creall(c), type);
- else if (type->isimaginary())
- new(&ue) RealExp(loc, cimagl(c), type);
- else if (type->iscomplex())
- new(&ue) ComplexExp(loc, c, type);
- else
- assert(0);
- }
- else
- {
- sinteger_t n1;
- sinteger_t n2;
- sinteger_t n;
-
- n1 = e1->toInteger();
- n2 = e2->toInteger();
- if (n2 == 0)
- {
- e2->error("divide by 0");
- new(&ue) ErrorExp();
- return ue;
- }
- if (n2 == -1 && !type->isunsigned())
- {
- // Check for int.min % -1
- if ((dinteger_t)n1 == 0xFFFFFFFF80000000ULL && type->toBasetype()->ty != Tint64)
- {
- e2->error("integer overflow: int.min %% -1");
- new(&ue) ErrorExp();
- return ue;
- }
- else if ((dinteger_t)n1 == 0x8000000000000000LL) // long.min % -1
- {
- e2->error("integer overflow: long.min %% -1");
- new(&ue) ErrorExp();
- return ue;
- }
- }
- if (e1->type->isunsigned() || e2->type->isunsigned())
- n = ((dinteger_t) n1) % ((dinteger_t) n2);
- else
- n = n1 % n2;
- new(&ue) IntegerExp(loc, n, type);
- }
- return ue;
-}
-
-UnionExp Pow(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
-
- // Handle integer power operations.
- if (e2->type->isintegral())
- {
- dinteger_t n = e2->toInteger();
- bool neg;
-
- if (!e2->type->isunsigned() && (sinteger_t)n < 0)
- {
- if (e1->type->isintegral())
- {
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
-
- // Don't worry about overflow, from now on n is unsigned.
- neg = true;
- n = -n;
- }
- else
- neg = false;
-
- UnionExp ur, uv;
- if (e1->type->iscomplex())
- {
- new(&ur) ComplexExp(loc, e1->toComplex(), e1->type);
- new(&uv) ComplexExp(loc, complex_t(CTFloat::one), e1->type);
- }
- else if (e1->type->isfloating())
- {
- new(&ur) RealExp(loc, e1->toReal(), e1->type);
- new(&uv) RealExp(loc, CTFloat::one, e1->type);
- }
- else
- {
- new(&ur) IntegerExp(loc, e1->toInteger(), e1->type);
- new(&uv) IntegerExp(loc, 1, e1->type);
- }
-
- Expression* r = ur.exp();
- Expression* v = uv.exp();
- while (n != 0)
- {
- if (n & 1)
- {
- // v = v * r;
- uv = Mul(loc, v->type, v, r);
- }
- n >>= 1;
- // r = r * r
- ur = Mul(loc, r->type, r, r);
- }
-
- if (neg)
- {
- // ue = 1.0 / v
- UnionExp one;
- new(&one) RealExp(loc, CTFloat::one, v->type);
- uv = Div(loc, v->type, one.exp(), v);
- }
-
- if (type->iscomplex())
- new(&ue) ComplexExp(loc, v->toComplex(), type);
- else if (type->isintegral())
- new(&ue) IntegerExp(loc, v->toInteger(), type);
- else
- new(&ue) RealExp(loc, v->toReal(), type);
- }
- else if (e2->type->isfloating())
- {
- // x ^^ y for x < 0 and y not an integer is not defined; so set result as NaN
- if (e1->toReal() < CTFloat::zero)
- {
- new(&ue) RealExp(loc, target.RealProperties.nan, type);
- }
- else
- new(&ue) CTFEExp(TOKcantexp);
- }
- else
- new(&ue) CTFEExp(TOKcantexp);
-
- return ue;
-}
-
-UnionExp Shl(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- new(&ue) IntegerExp(loc, e1->toInteger() << e2->toInteger(), type);
- return ue;
-}
-
-UnionExp Shr(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- dinteger_t value = e1->toInteger();
- dinteger_t dcount = e2->toInteger();
- assert(dcount <= 0xFFFFFFFF);
- unsigned count = (unsigned)dcount;
- switch (e1->type->toBasetype()->ty)
- {
- case Tint8:
- value = (d_int8)(value) >> count;
- break;
-
- case Tuns8:
- case Tchar:
- value = (d_uns8)(value) >> count;
- break;
-
- case Tint16:
- value = (d_int16)(value) >> count;
- break;
-
- case Tuns16:
- case Twchar:
- value = (d_uns16)(value) >> count;
- break;
-
- case Tint32:
- value = (d_int32)(value) >> count;
- break;
-
- case Tuns32:
- case Tdchar:
- value = (d_uns32)(value) >> count;
- break;
-
- case Tint64:
- value = (d_int64)(value) >> count;
- break;
-
- case Tuns64:
- value = (d_uns64)(value) >> count;
- break;
-
- case Terror:
- new(&ue) ErrorExp();
- return ue;
-
- default:
- assert(0);
- }
- new(&ue) IntegerExp(loc, value, type);
- return ue;
-}
-
-UnionExp Ushr(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- dinteger_t value = e1->toInteger();
- dinteger_t dcount = e2->toInteger();
- assert(dcount <= 0xFFFFFFFF);
- unsigned count = (unsigned)dcount;
- switch (e1->type->toBasetype()->ty)
- {
- case Tint8:
- case Tuns8:
- case Tchar:
- // Possible only with >>>=. >>> always gets promoted to int.
- value = (value & 0xFF) >> count;
- break;
-
- case Tint16:
- case Tuns16:
- case Twchar:
- // Possible only with >>>=. >>> always gets promoted to int.
- value = (value & 0xFFFF) >> count;
- break;
-
- case Tint32:
- case Tuns32:
- case Tdchar:
- value = (value & 0xFFFFFFFF) >> count;
- break;
-
- case Tint64:
- case Tuns64:
- value = (d_uns64)(value) >> count;
- break;
-
- case Terror:
- new(&ue) ErrorExp();
- return ue;
-
- default:
- assert(0);
- }
- new(&ue) IntegerExp(loc, value, type);
- return ue;
-}
-
-UnionExp And(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- new(&ue) IntegerExp(loc, e1->toInteger() & e2->toInteger(), type);
- return ue;
-}
-
-UnionExp Or(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- new(&ue) IntegerExp(loc, e1->toInteger() | e2->toInteger(), type);
- return ue;
-}
-
-UnionExp Xor(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- new(&ue) IntegerExp(loc, e1->toInteger() ^ e2->toInteger(), type);
- return ue;
-}
-
-/* Also returns TOKcantexp if cannot be computed.
- */
-UnionExp Equal(TOK op, Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- int cmp = 0;
- real_t r1;
- real_t r2;
-
- //printf("Equal(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars());
-
- assert(op == TOKequal || op == TOKnotequal);
-
- if (e1->op == TOKnull)
- {
- if (e2->op == TOKnull)
- cmp = 1;
- else if (e2->op == TOKstring)
- {
- StringExp *es2 = (StringExp *)e2;
- cmp = (0 == es2->len);
- }
- else if (e2->op == TOKarrayliteral)
- {
- ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;
- cmp = !es2->elements || (0 == es2->elements->length);
- }
- else
- {
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
- }
- else if (e2->op == TOKnull)
- {
- if (e1->op == TOKstring)
- {
- StringExp *es1 = (StringExp *)e1;
- cmp = (0 == es1->len);
- }
- else if (e1->op == TOKarrayliteral)
- {
- ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1;
- cmp = !es1->elements || (0 == es1->elements->length);
- }
- else
- {
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
- }
- else if (e1->op == TOKstring && e2->op == TOKstring)
- {
- StringExp *es1 = (StringExp *)e1;
- StringExp *es2 = (StringExp *)e2;
-
- if (es1->sz != es2->sz)
- {
- assert(global.errors);
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
- if (es1->len == es2->len &&
- memcmp(es1->string, es2->string, es1->sz * es1->len) == 0)
- cmp = 1;
- else
- cmp = 0;
- }
- else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral)
- {
- ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1;
- ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;
-
- if ((!es1->elements || !es1->elements->length) &&
- (!es2->elements || !es2->elements->length))
- cmp = 1; // both arrays are empty
- else if (!es1->elements || !es2->elements)
- cmp = 0;
- else if (es1->elements->length != es2->elements->length)
- cmp = 0;
- else
- {
- for (size_t i = 0; i < es1->elements->length; i++)
- {
- Expression *ee1 = es1->getElement(i);
- Expression *ee2 = es2->getElement(i);
- ue = Equal(TOKequal, loc, Type::tint32, ee1, ee2);
- if (CTFEExp::isCantExp(ue.exp()))
- return ue;
- cmp = (int)ue.exp()->toInteger();
- if (cmp == 0)
- break;
- }
- }
- }
- else if (e1->op == TOKarrayliteral && e2->op == TOKstring)
- {
- // Swap operands and use common code
- Expression *etmp = e1;
- e1 = e2;
- e2 = etmp;
- goto Lsa;
- }
- else if (e1->op == TOKstring && e2->op == TOKarrayliteral)
- {
- Lsa:
- StringExp *es1 = (StringExp *)e1;
- ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;
- size_t dim1 = es1->len;
- size_t dim2 = es2->elements ? es2->elements->length : 0;
- if (dim1 != dim2)
- cmp = 0;
- else
- {
- cmp = 1; // if dim1 winds up being 0
- for (size_t i = 0; i < dim1; i++)
- {
- uinteger_t c = es1->charAt(i);
- Expression *ee2 = es2->getElement(i);
- if (ee2->isConst() != 1)
- {
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
- cmp = (c == ee2->toInteger());
- if (cmp == 0)
- break;
- }
- }
- }
- else if (e1->op == TOKstructliteral && e2->op == TOKstructliteral)
- {
- StructLiteralExp *es1 = (StructLiteralExp *)e1;
- StructLiteralExp *es2 = (StructLiteralExp *)e2;
-
- if (es1->sd != es2->sd)
- cmp = 0;
- else if ((!es1->elements || !es1->elements->length) &&
- (!es2->elements || !es2->elements->length))
- cmp = 1; // both arrays are empty
- else if (!es1->elements || !es2->elements)
- cmp = 0;
- else if (es1->elements->length != es2->elements->length)
- cmp = 0;
- else
- {
- cmp = 1;
- for (size_t i = 0; i < es1->elements->length; i++)
- {
- Expression *ee1 = (*es1->elements)[i];
- Expression *ee2 = (*es2->elements)[i];
-
- if (ee1 == ee2)
- continue;
- if (!ee1 || !ee2)
- {
- cmp = 0;
- break;
- }
- ue = Equal(TOKequal, loc, Type::tint32, ee1, ee2);
- if (ue.exp()->op == TOKcantexp)
- return ue;
- cmp = (int)ue.exp()->toInteger();
- if (cmp == 0)
- break;
- }
- }
- }
- else if (e1->isConst() != 1 || e2->isConst() != 1)
- {
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
- else if (e1->type->isreal())
- {
- r1 = e1->toReal();
- r2 = e2->toReal();
- goto L1;
- }
- else if (e1->type->isimaginary())
- {
- r1 = e1->toImaginary();
- r2 = e2->toImaginary();
- L1:
- if (CTFloat::isNaN(r1) || CTFloat::isNaN(r2)) // if unordered
- {
- cmp = 0;
- }
- else
- {
- cmp = (r1 == r2);
- }
- }
- else if (e1->type->iscomplex())
- {
- cmp = e1->toComplex() == e2->toComplex();
- }
- else if (e1->type->isintegral() || e1->type->toBasetype()->ty == Tpointer)
- {
- cmp = (e1->toInteger() == e2->toInteger());
- }
- else
- {
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
-
- if (op == TOKnotequal)
- cmp ^= 1;
- new(&ue) IntegerExp(loc, cmp, type);
- return ue;
-}
-
-UnionExp Identity(TOK op, Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- int cmp;
-
- if (e1->op == TOKnull)
- {
- cmp = (e2->op == TOKnull);
- }
- else if (e2->op == TOKnull)
- {
- cmp = 0;
- }
- else if (e1->op == TOKsymoff && e2->op == TOKsymoff)
- {
- SymOffExp *es1 = (SymOffExp *)e1;
- SymOffExp *es2 = (SymOffExp *)e2;
-
- cmp = (es1->var == es2->var && es1->offset == es2->offset);
- }
- else
- {
- if (e1->type->isreal())
- {
- cmp = RealEquals(e1->toReal(), e2->toReal());
- }
- else if (e1->type->isimaginary())
- {
- cmp = RealEquals(e1->toImaginary(), e2->toImaginary());
- }
- else if (e1->type->iscomplex())
- {
- complex_t v1 = e1->toComplex();
- complex_t v2 = e2->toComplex();
- cmp = RealEquals(creall(v1), creall(v2)) &&
- RealEquals(cimagl(v1), cimagl(v1));
- }
- else
- {
- ue = Equal((op == TOKidentity) ? TOKequal : TOKnotequal, loc, type, e1, e2);
- return ue;
- }
- }
- if (op == TOKnotidentity)
- cmp ^= 1;
- new(&ue) IntegerExp(loc, cmp, type);
- return ue;
-}
-
-
-UnionExp Cmp(TOK op, Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- dinteger_t n;
- real_t r1;
- real_t r2;
-
- //printf("Cmp(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars());
-
- if (e1->op == TOKstring && e2->op == TOKstring)
- {
- StringExp *es1 = (StringExp *)e1;
- StringExp *es2 = (StringExp *)e2;
- size_t sz = es1->sz;
- assert(sz == es2->sz);
-
- size_t len = es1->len;
- if (es2->len < len)
- len = es2->len;
-
- int rawCmp = memcmp(es1->string, es2->string, sz * len);
- if (rawCmp == 0)
- rawCmp = (int)(es1->len - es2->len);
- n = specificCmp(op, rawCmp);
- }
- else if (e1->isConst() != 1 || e2->isConst() != 1)
- {
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
- else if (e1->type->isreal())
- {
- r1 = e1->toReal();
- r2 = e2->toReal();
- goto L1;
- }
- else if (e1->type->isimaginary())
- {
- r1 = e1->toImaginary();
- r2 = e2->toImaginary();
- L1:
- n = realCmp(op, r1, r2);
- }
- else if (e1->type->iscomplex())
- {
- assert(0);
- }
- else
- {
- sinteger_t n1;
- sinteger_t n2;
-
- n1 = e1->toInteger();
- n2 = e2->toInteger();
- if (e1->type->isunsigned() || e2->type->isunsigned())
- n = intUnsignedCmp(op, n1, n2);
- else
- n = intSignedCmp(op, n1, n2);
- }
- new(&ue) IntegerExp(loc, n, type);
- return ue;
-}
-
-/* Also returns TOKcantexp if cannot be computed.
- * to: type to cast to
- * type: type to paint the result
- */
-
-UnionExp Cast(Loc loc, Type *type, Type *to, Expression *e1)
-{
- UnionExp ue;
- Type *tb = to->toBasetype();
- Type *typeb = type->toBasetype();
-
- //printf("Cast(type = %s, to = %s, e1 = %s)\n", type->toChars(), to->toChars(), e1->toChars());
- //printf("\te1->type = %s\n", e1->type->toChars());
- if (e1->type->equals(type) && type->equals(to))
- {
- new(&ue) UnionExp(e1);
- return ue;
- }
-
- if (e1->op == TOKvector && ((TypeVector *)e1->type)->basetype->equals(type) && type->equals(to))
- {
- Expression *ex = ((VectorExp *)e1)->e1;
- new(&ue) UnionExp(ex);
- return ue;
- }
-
- if (e1->type->implicitConvTo(to) >= MATCHconst ||
- to->implicitConvTo(e1->type) >= MATCHconst)
- {
- goto L1;
- }
-
- // Allow covariant converions of delegates
- // (Perhaps implicit conversion from pure to impure should be a MATCHconst,
- // then we wouldn't need this extra check.)
- if (e1->type->toBasetype()->ty == Tdelegate &&
- e1->type->implicitConvTo(to) == MATCHconvert)
- {
- goto L1;
- }
-
- /* Allow casting from one string type to another
- */
- if (e1->op == TOKstring)
- {
- if (tb->ty == Tarray && typeb->ty == Tarray &&
- tb->nextOf()->size() == typeb->nextOf()->size())
- {
- goto L1;
- }
- }
-
- if (e1->op == TOKarrayliteral && typeb == tb)
- {
-L1:
- Expression *ex = expType(to, e1);
- new(&ue) UnionExp(ex);
- return ue;
- }
-
- if (e1->isConst() != 1)
- {
- new(&ue) CTFEExp(TOKcantexp);
- }
- else if (tb->ty == Tbool)
- {
- new(&ue) IntegerExp(loc, e1->toInteger() != 0, type);
- }
- else if (type->isintegral())
- {
- if (e1->type->isfloating())
- {
- dinteger_t result;
- real_t r = e1->toReal();
-
- switch (typeb->ty)
- {
- case Tint8:
- result = (d_int8)(sinteger_t)r;
- break;
- case Tchar:
- case Tuns8:
- result = (d_uns8)(dinteger_t)r;
- break;
- case Tint16:
- result = (d_int16)(sinteger_t)r;
- break;
- case Twchar:
- case Tuns16:
- result = (d_uns16)(dinteger_t)r;
- break;
- case Tint32:
- result = (d_int32)r;
- break;
- case Tdchar:
- case Tuns32:
- result = (d_uns32)r;
- break;
- case Tint64:
- result = (d_int64)r;
- break;
- case Tuns64:
- result = (d_uns64)r;
- break;
- default:
- assert(0);
- }
-
- new(&ue) IntegerExp(loc, result, type);
- }
- else if (type->isunsigned())
- new(&ue) IntegerExp(loc, e1->toUInteger(), type);
- else
- new(&ue) IntegerExp(loc, e1->toInteger(), type);
- }
- else if (tb->isreal())
- {
- real_t value = e1->toReal();
-
- new(&ue) RealExp(loc, value, type);
- }
- else if (tb->isimaginary())
- {
- real_t value = e1->toImaginary();
-
- new(&ue) RealExp(loc, value, type);
- }
- else if (tb->iscomplex())
- {
- complex_t value = e1->toComplex();
-
- new(&ue) ComplexExp(loc, value, type);
- }
- else if (tb->isscalar())
- {
- new(&ue) IntegerExp(loc, e1->toInteger(), type);
- }
- else if (tb->ty == Tvoid)
- {
- new(&ue) CTFEExp(TOKcantexp);
- }
- else if (tb->ty == Tstruct && e1->op == TOKint64)
- {
- // Struct = 0;
- StructDeclaration *sd = tb->toDsymbol(NULL)->isStructDeclaration();
- assert(sd);
- Expressions *elements = new Expressions;
- for (size_t i = 0; i < sd->fields.length; i++)
- {
- VarDeclaration *v = sd->fields[i];
- UnionExp zero;
- new(&zero) IntegerExp(0);
- ue = Cast(loc, v->type, v->type, zero.exp());
- if (ue.exp()->op == TOKcantexp)
- return ue;
- elements->push(ue.exp()->copy());
- }
- new(&ue) StructLiteralExp(loc, sd, elements);
- ue.exp()->type = type;
- }
- else
- {
- if (type != Type::terror)
- {
- // have to change to Internal Compiler Error
- // all invalid casts should be handled already in Expression::castTo().
- error(loc, "cannot cast %s to %s", e1->type->toChars(), type->toChars());
- }
- new(&ue) ErrorExp();
- }
- return ue;
-}
-
-
-UnionExp ArrayLength(Type *type, Expression *e1)
-{
- UnionExp ue;
- Loc loc = e1->loc;
-
- if (e1->op == TOKstring)
- {
- StringExp *es1 = (StringExp *)e1;
-
- new(&ue) IntegerExp(loc, es1->len, type);
- }
- else if (e1->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e1;
- size_t dim = ale->elements ? ale->elements->length : 0;
-
- new(&ue) IntegerExp(loc, dim, type);
- }
- else if (e1->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e1;
- size_t dim = ale->keys->length;
-
- new(&ue) IntegerExp(loc, dim, type);
- }
- else if (e1->type->toBasetype()->ty == Tsarray)
- {
- Expression *e = ((TypeSArray *)e1->type->toBasetype())->dim;
- new(&ue) UnionExp(e);
- }
- else
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
-}
-
-/* Also return TOKcantexp if this fails
- */
-UnionExp Index(Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- Loc loc = e1->loc;
-
- //printf("Index(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars());
- assert(e1->type);
- if (e1->op == TOKstring && e2->op == TOKint64)
- {
- StringExp *es1 = (StringExp *)e1;
- uinteger_t i = e2->toInteger();
-
- if (i >= es1->len)
- {
- e1->error("string index %llu is out of bounds [0 .. %llu]", i, (ulonglong)es1->len);
- new(&ue) ErrorExp();
- }
- else
- {
- new(&ue) IntegerExp(loc, es1->charAt(i), type);
- }
- }
- else if (e1->type->toBasetype()->ty == Tsarray && e2->op == TOKint64)
- {
- TypeSArray *tsa = (TypeSArray *)e1->type->toBasetype();
- uinteger_t length = tsa->dim->toInteger();
- uinteger_t i = e2->toInteger();
-
- if (i >= length)
- {
- e1->error("array index %llu is out of bounds %s[0 .. %llu]", i, e1->toChars(), length);
- new(&ue) ErrorExp();
- }
- else if (e1->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e1;
- Expression *e = ale->getElement((size_t)i);
- e->type = type;
- e->loc = loc;
- if (hasSideEffect(e))
- new(&ue) CTFEExp(TOKcantexp);
- else
- new(&ue) UnionExp(e);
- }
- else
- new(&ue) CTFEExp(TOKcantexp);
- }
- else if (e1->type->toBasetype()->ty == Tarray && e2->op == TOKint64)
- {
- uinteger_t i = e2->toInteger();
-
- if (e1->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e1;
- if (i >= ale->elements->length)
- {
- e1->error("array index %llu is out of bounds %s[0 .. %u]", i, e1->toChars(), ale->elements->length);
- new(&ue) ErrorExp();
- }
- else
- {
- Expression *e = ale->getElement((size_t)i);
- e->type = type;
- e->loc = loc;
- if (hasSideEffect(e))
- new(&ue) CTFEExp(TOKcantexp);
- else
- new(&ue) UnionExp(e);
- }
- }
- else
- new(&ue) CTFEExp(TOKcantexp);
- }
- else if (e1->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e1;
- /* Search the keys backwards, in case there are duplicate keys
- */
- for (size_t i = ae->keys->length; i;)
- {
- i--;
- Expression *ekey = (*ae->keys)[i];
- ue = Equal(TOKequal, loc, Type::tbool, ekey, e2);
- if (CTFEExp::isCantExp(ue.exp()))
- return ue;
- if (ue.exp()->isBool(true))
- {
- Expression *e = (*ae->values)[i];
- e->type = type;
- e->loc = loc;
- if (hasSideEffect(e))
- new(&ue) CTFEExp(TOKcantexp);
- else
- new(&ue) UnionExp(e);
- return ue;
- }
- }
- new(&ue) CTFEExp(TOKcantexp);
- }
- else
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
-}
-
-/* Also return TOKcantexp if this fails
- */
-UnionExp Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr)
-{
- UnionExp ue;
- Loc loc = e1->loc;
-
- if (e1->op == TOKstring && lwr->op == TOKint64 && upr->op == TOKint64)
- {
- StringExp *es1 = (StringExp *)e1;
- uinteger_t ilwr = lwr->toInteger();
- uinteger_t iupr = upr->toInteger();
-
- if (iupr > es1->len || ilwr > iupr)
- {
- e1->error("string slice [%llu .. %llu] is out of bounds", ilwr, iupr);
- new(&ue) ErrorExp();
- }
- else
- {
- size_t len = (size_t)(iupr - ilwr);
- unsigned char sz = es1->sz;
-
- void *s = mem.xmalloc((len + 1) * sz);
- memcpy((char *)s, (char *)es1->string + ilwr * sz, len * sz);
- memset((char *)s + len * sz, 0, sz);
-
- new(&ue) StringExp(loc, s, len, es1->postfix);
- StringExp *es = (StringExp *)ue.exp();
- es->sz = sz;
- es->committed = es1->committed;
- es->type = type;
- }
- }
- else if (e1->op == TOKarrayliteral &&
- lwr->op == TOKint64 && upr->op == TOKint64 &&
- !hasSideEffect(e1))
- {
- ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1;
- uinteger_t ilwr = lwr->toInteger();
- uinteger_t iupr = upr->toInteger();
-
- if (iupr > es1->elements->length || ilwr > iupr)
- {
- e1->error("array slice [%llu .. %llu] is out of bounds", ilwr, iupr);
- new(&ue) ErrorExp();
- }
- else
- {
- Expressions *elements = new Expressions();
- elements->setDim((size_t)(iupr - ilwr));
- memcpy(elements->tdata(),
- es1->elements->tdata() + ilwr,
- (size_t)(iupr - ilwr) * sizeof((*es1->elements)[0]));
- new(&ue) ArrayLiteralExp(e1->loc, type, elements);
- }
- }
- else
- new(&ue) CTFEExp(TOKcantexp);
- assert(ue.exp()->type);
- return ue;
-}
-
-/* Set a slice of char/integer array literal 'existingAE' from a string 'newval'.
- * existingAE[firstIndex..firstIndex+newval.length] = newval.
- */
-void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, size_t firstIndex)
-{
- size_t newlen = newval->len;
- size_t sz = newval->sz;
- void *s = newval->string;
- Type *elemType = existingAE->type->nextOf();
- for (size_t j = 0; j < newlen; j++)
- {
- dinteger_t val;
- switch (sz)
- {
- case 1: val = (( utf8_t *)s)[j]; break;
- case 2: val = ((utf16_t *)s)[j]; break;
- case 4: val = ((utf32_t *)s)[j]; break;
- default: assert(0); break;
- }
- (*existingAE->elements)[j + firstIndex]
- = new IntegerExp(newval->loc, val, elemType);
- }
-}
-
-/* Set a slice of string 'existingSE' from a char array literal 'newae'.
- * existingSE[firstIndex..firstIndex+newae.length] = newae.
- */
-void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, size_t firstIndex)
-{
- void *s = existingSE->string;
- for (size_t j = 0; j < newae->elements->length; j++)
- {
- unsigned val = (unsigned)newae->getElement(j)->toInteger();
- switch (existingSE->sz)
- {
- case 1: (( utf8_t *)s)[j + firstIndex] = ( utf8_t)val; break;
- case 2: ((utf16_t *)s)[j + firstIndex] = (utf16_t)val; break;
- case 4: ((utf32_t *)s)[j + firstIndex] = (utf32_t)val; break;
- default: assert(0); break;
- }
- }
-}
-
-/* Set a slice of string 'existingSE' from a string 'newstr'.
- * existingSE[firstIndex..firstIndex+newstr.length] = newstr.
- */
-void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, size_t firstIndex)
-{
- void *s = existingSE->string;
- size_t sz = existingSE->sz;
- assert(sz == newstr->sz);
- memcpy((char *)s + firstIndex * sz, newstr->string, sz * newstr->len);
-}
-
-/* Compare a string slice with another string slice.
- * Conceptually equivalent to memcmp( se1[lo1..lo1+len], se2[lo2..lo2+len])
- */
-int sliceCmpStringWithString(StringExp *se1, StringExp *se2, size_t lo1, size_t lo2, size_t len)
-{
- void *s1 = se1->string;
- void *s2 = se2->string;
- size_t sz = se1->sz;
- assert(sz == se2->sz);
- return memcmp((char *)s1 + sz * lo1, (char *)s2 + sz * lo2, sz * len);
-}
-
-/* Compare a string slice with an array literal slice
- * Conceptually equivalent to memcmp( se1[lo1..lo1+len], ae2[lo2..lo2+len])
- */
-int sliceCmpStringWithArray(StringExp *se1, ArrayLiteralExp *ae2, size_t lo1, size_t lo2, size_t len)
-{
- void *s = se1->string;
- size_t sz = se1->sz;
-
- for (size_t j = 0; j < len; j++)
- {
- unsigned val2 = (unsigned)ae2->getElement(j + lo2)->toInteger();
- unsigned val1;
- switch (sz)
- {
- case 1: val1 = (( utf8_t *)s)[j + lo1]; break;
- case 2: val1 = ((utf16_t *)s)[j + lo1]; break;
- case 4: val1 = ((utf32_t *)s)[j + lo1]; break;
- default: assert(0); break;
- }
- int c = val1 - val2;
- if (c)
- return c;
- }
- return 0;
-}
-
-/* Also return TOKcantexp if this fails
- */
-UnionExp Cat(Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- Expression *e = CTFEExp::cantexp;
- Loc loc = e1->loc;
- Type *t;
- Type *t1 = e1->type->toBasetype();
- Type *t2 = e2->type->toBasetype();
-
- //printf("Cat(e1 = %s, e2 = %s)\n", e1->toChars(), e2->toChars());
- //printf("\tt1 = %s, t2 = %s, type = %s\n", t1->toChars(), t2->toChars(), type->toChars());
-
- if (e1->op == TOKnull && (e2->op == TOKint64 || e2->op == TOKstructliteral))
- {
- e = e2;
- t = t1;
- goto L2;
- }
- else if ((e1->op == TOKint64 || e1->op == TOKstructliteral) && e2->op == TOKnull)
- {
- e = e1;
- t = t2;
- L2:
- Type *tn = e->type->toBasetype();
- if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar)
- {
- // Create a StringExp
- if (t->nextOf())
- t = t->nextOf()->toBasetype();
- unsigned char sz = (unsigned char)t->size();
-
- dinteger_t v = e->toInteger();
-
- size_t len = (t->ty == tn->ty) ? 1 : utf_codeLength(sz, (dchar_t)v);
- void *s = mem.xmalloc((len + 1) * sz);
- if (t->ty == tn->ty)
- Port::valcpy(s, v, sz);
- else
- utf_encode(sz, s, (dchar_t)v);
-
- // Add terminating 0
- memset((char *)s + len * sz, 0, sz);
-
- new(&ue) StringExp(loc, s, len);
- StringExp *es = (StringExp *)ue.exp();
- es->type = type;
- es->sz = sz;
- es->committed = 1;
- }
- else
- {
- // Create an ArrayLiteralExp
- Expressions *elements = new Expressions();
- elements->push(e);
- new(&ue) ArrayLiteralExp(e->loc, type, elements);
- }
- assert(ue.exp()->type);
- return ue;
- }
- else if (e1->op == TOKnull && e2->op == TOKnull)
- {
- if (type == e1->type)
- {
- // Handle null ~= null
- if (t1->ty == Tarray && t2 == t1->nextOf())
- {
- new(&ue) ArrayLiteralExp(e1->loc, type, e2);
- assert(ue.exp()->type);
- return ue;
- }
- else
- {
- new(&ue) UnionExp(e1);
- assert(ue.exp()->type);
- return ue;
- }
- }
- if (type == e2->type)
- {
- new(&ue) UnionExp(e2);
- assert(ue.exp()->type);
- return ue;
- }
- new(&ue) NullExp(e1->loc, type);
- assert(ue.exp()->type);
- return ue;
- }
- else if (e1->op == TOKstring && e2->op == TOKstring)
- {
- // Concatenate the strings
- StringExp *es1 = (StringExp *)e1;
- StringExp *es2 = (StringExp *)e2;
- size_t len = es1->len + es2->len;
- unsigned char sz = es1->sz;
-
- if (sz != es2->sz)
- {
- /* Can happen with:
- * auto s = "foo"d ~ "bar"c;
- */
- assert(global.errors);
- new(&ue) CTFEExp(TOKcantexp);
- assert(ue.exp()->type);
- return ue;
- }
- void *s = mem.xmalloc((len + 1) * sz);
- memcpy((char *)s, es1->string, es1->len * sz);
- memcpy((char *)s + es1->len * sz, es2->string, es2->len * sz);
-
- // Add terminating 0
- memset((char *)s + len * sz, 0, sz);
-
- new(&ue) StringExp(loc, s, len);
- StringExp *es = (StringExp *)ue.exp();
- es->sz = sz;
- es->committed = es1->committed | es2->committed;
- es->type = type;
- assert(ue.exp()->type);
- return ue;
- }
- else if (e2->op == TOKstring && e1->op == TOKarrayliteral &&
- t1->nextOf()->isintegral())
- {
- // [chars] ~ string --> [chars]
- StringExp *es = (StringExp *)e2;
- ArrayLiteralExp *ea = (ArrayLiteralExp *)e1;
- size_t len = es->len + ea->elements->length;
- Expressions * elems = new Expressions;
- elems->setDim(len);
- for (size_t i= 0; i < ea->elements->length; ++i)
- {
- (*elems)[i] = ea->getElement(i);
- }
- new(&ue) ArrayLiteralExp(e1->loc, type, elems);
- ArrayLiteralExp *dest = (ArrayLiteralExp *)ue.exp();
- sliceAssignArrayLiteralFromString(dest, es, ea->elements->length);
- assert(ue.exp()->type);
- return ue;
- }
- else if (e1->op == TOKstring && e2->op == TOKarrayliteral &&
- t2->nextOf()->isintegral())
- {
- // string ~ [chars] --> [chars]
- StringExp *es = (StringExp *)e1;
- ArrayLiteralExp *ea = (ArrayLiteralExp *)e2;
- size_t len = es->len + ea->elements->length;
- Expressions * elems = new Expressions;
- elems->setDim(len);
- for (size_t i= 0; i < ea->elements->length; ++i)
- {
- (*elems)[es->len + i] = ea->getElement(i);
- }
- new(&ue) ArrayLiteralExp(e1->loc, type, elems);
- ArrayLiteralExp *dest = (ArrayLiteralExp *)ue.exp();
- sliceAssignArrayLiteralFromString(dest, es, 0);
- assert(ue.exp()->type);
- return ue;
- }
- else if (e1->op == TOKstring && e2->op == TOKint64)
- {
- // string ~ char --> string
- StringExp *es1 = (StringExp *)e1;
- StringExp *es;
- unsigned char sz = es1->sz;
- dinteger_t v = e2->toInteger();
-
- // Is it a concatentation of homogenous types?
- // (char[] ~ char, wchar[]~wchar, or dchar[]~dchar)
- bool homoConcat = (sz == t2->size());
- size_t len = es1->len;
- len += homoConcat ? 1 : utf_codeLength(sz, (dchar_t)v);
-
- void *s = mem.xmalloc((len + 1) * sz);
- memcpy(s, es1->string, es1->len * sz);
- if (homoConcat)
- Port::valcpy((char *)s + (sz * es1->len), v, sz);
- else
- utf_encode(sz, (char *)s + (sz * es1->len), (dchar_t)v);
-
- // Add terminating 0
- memset((char *)s + len * sz, 0, sz);
-
- new(&ue) StringExp(loc, s, len);
- es = (StringExp *)ue.exp();
- es->sz = sz;
- es->committed = es1->committed;
- es->type = type;
- assert(ue.exp()->type);
- return ue;
- }
- else if (e1->op == TOKint64 && e2->op == TOKstring)
- {
- // [w|d]?char ~ string --> string
- // We assume that we only ever prepend one char of the same type
- // (wchar,dchar) as the string's characters.
- StringExp *es2 = (StringExp *)e2;
- size_t len = 1 + es2->len;
- unsigned char sz = es2->sz;
- dinteger_t v = e1->toInteger();
-
- void *s = mem.xmalloc((len + 1) * sz);
- Port::valcpy((char *)s, v, sz);
- memcpy((char *)s + sz, es2->string, es2->len * sz);
-
- // Add terminating 0
- memset((char *)s + len * sz, 0, sz);
-
- new(&ue) StringExp(loc, s, len);
- StringExp *es = (StringExp *)ue.exp();
- es->sz = sz;
- es->committed = es2->committed;
- es->type = type;
- assert(ue.exp()->type);
- return ue;
- }
- else if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral &&
- t1->nextOf()->equals(t2->nextOf()))
- {
- // Concatenate the arrays
- Expressions *elems = ArrayLiteralExp::copyElements(e1, e2);
-
- new(&ue) ArrayLiteralExp(e1->loc, NULL, elems);
-
- e = ue.exp();
- if (type->toBasetype()->ty == Tsarray)
- {
- e->type = t1->nextOf()->sarrayOf(elems->length);
- }
- else
- e->type = type;
- assert(ue.exp()->type);
- return ue;
- }
- else if (e1->op == TOKarrayliteral && e2->op == TOKnull &&
- t1->nextOf()->equals(t2->nextOf()))
- {
- e = e1;
- goto L3;
- }
- else if (e1->op == TOKnull && e2->op == TOKarrayliteral &&
- t1->nextOf()->equals(t2->nextOf()))
- {
- e = e2;
- L3:
- // Concatenate the array with null
- Expressions *elems = ArrayLiteralExp::copyElements(e);
-
- new(&ue) ArrayLiteralExp(e->loc, NULL, elems);
-
- e = ue.exp();
- if (type->toBasetype()->ty == Tsarray)
- {
- e->type = t1->nextOf()->sarrayOf(elems->length);
- }
- else
- e->type = type;
- assert(ue.exp()->type);
- return ue;
- }
- else if ((e1->op == TOKarrayliteral || e1->op == TOKnull) &&
- e1->type->toBasetype()->nextOf() &&
- e1->type->toBasetype()->nextOf()->equals(e2->type))
- {
- Expressions *elems = (e1->op == TOKarrayliteral)
- ? ArrayLiteralExp::copyElements(e1) : new Expressions();
- elems->push(e2);
-
- new(&ue) ArrayLiteralExp(e1->loc, NULL, elems);
-
- e = ue.exp();
- if (type->toBasetype()->ty == Tsarray)
- {
- e->type = e2->type->sarrayOf(elems->length);
- }
- else
- e->type = type;
- assert(ue.exp()->type);
- return ue;
- }
- else if (e2->op == TOKarrayliteral &&
- e2->type->toBasetype()->nextOf()->equals(e1->type))
- {
- Expressions *elems = ArrayLiteralExp::copyElements(e1, e2);
-
- new(&ue) ArrayLiteralExp(e2->loc, NULL, elems);
-
- e = ue.exp();
- if (type->toBasetype()->ty == Tsarray)
- {
- e->type = e1->type->sarrayOf(elems->length);
- }
- else
- e->type = type;
- assert(ue.exp()->type);
- return ue;
- }
- else if (e1->op == TOKnull && e2->op == TOKstring)
- {
- t = e1->type;
- e = e2;
- goto L1;
- }
- else if (e1->op == TOKstring && e2->op == TOKnull)
- {
- e = e1;
- t = e2->type;
- L1:
- Type *tb = t->toBasetype();
- if (tb->ty == Tarray && tb->nextOf()->equivalent(e->type))
- {
- Expressions *expressions = new Expressions();
- expressions->push(e);
- new(&ue) ArrayLiteralExp(loc, t, expressions);
- e = ue.exp();
- }
- else
- {
- new(&ue) UnionExp(e);
- e = ue.exp();
- }
- if (!e->type->equals(type))
- {
- StringExp *se = (StringExp *)e->copy();
- e = se->castTo(NULL, type);
- new(&ue) UnionExp(e);
- e = ue.exp();
- }
- }
- else
- new(&ue) CTFEExp(TOKcantexp);
- assert(ue.exp()->type);
- return ue;
-}
-
-UnionExp Ptr(Type *type, Expression *e1)
-{
- //printf("Ptr(e1 = %s)\n", e1->toChars());
- UnionExp ue;
- if (e1->op == TOKadd)
- {
- AddExp *ae = (AddExp *)e1;
- if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64)
- {
- AddrExp *ade = (AddrExp *)ae->e1;
- if (ade->e1->op == TOKstructliteral)
- {
- StructLiteralExp *se = (StructLiteralExp *)ade->e1;
- unsigned offset = (unsigned)ae->e2->toInteger();
- Expression *e = se->getField(type, offset);
- if (e)
- {
- new(&ue) UnionExp(e);
- return ue;
- }
- }
- }
- }
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
-}
diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d
new file mode 100644
index 00000000000..1dada60de3b
--- /dev/null
+++ b/gcc/d/dmd/constfold.d
@@ -0,0 +1,1825 @@
+/**
+ * Perform constant folding of arithmetic expressions.
+ *
+ * The routines in this module are called from `optimize.d`.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/float.html#fp_const_folding, Floating Point Constant Folding)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/constfold.d, _constfold.d)
+ * Documentation: https://dlang.org/phobos/dmd_constfold.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/constfold.d
+ */
+
+module dmd.constfold;
+
+import core.stdc.string;
+import core.stdc.stdio;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.complex;
+import dmd.ctfeexpr;
+import dmd.declaration;
+import dmd.dstruct;
+import dmd.errors;
+import dmd.expression;
+import dmd.globals;
+import dmd.mtype;
+import dmd.root.ctfloat;
+import dmd.root.port;
+import dmd.root.rmem;
+import dmd.sideeffect;
+import dmd.target;
+import dmd.tokens;
+import dmd.utf;
+
+private enum LOG = false;
+
+private Expression expType(Type type, Expression e)
+{
+ if (type != e.type)
+ {
+ e = e.copy();
+ e.type = type;
+ }
+ return e;
+}
+
+/************************************
+ * Returns:
+ * true if e is a constant
+ */
+int isConst(Expression e)
+{
+ //printf("Expression::isConst(): %s\n", e.toChars());
+ switch (e.op)
+ {
+ case TOK.int64:
+ case TOK.float64:
+ case TOK.complex80:
+ return 1;
+ case TOK.null_:
+ return 0;
+ case TOK.symbolOffset:
+ return 2;
+ default:
+ return 0;
+ }
+ assert(0);
+}
+
+/**********************************
+ * Initialize a TOK.cantExpression Expression.
+ * Params:
+ * ue = where to write it
+ */
+void cantExp(out UnionExp ue)
+{
+ emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+}
+
+/* =============================== constFold() ============================== */
+/* The constFold() functions were redundant with the optimize() ones,
+ * and so have been folded in with them.
+ */
+/* ========================================================================== */
+UnionExp Neg(Type type, Expression e1)
+{
+ UnionExp ue = void;
+ Loc loc = e1.loc;
+ if (e1.type.isreal())
+ {
+ emplaceExp!(RealExp)(&ue, loc, -e1.toReal(), type);
+ }
+ else if (e1.type.isimaginary())
+ {
+ emplaceExp!(RealExp)(&ue, loc, -e1.toImaginary(), type);
+ }
+ else if (e1.type.iscomplex())
+ {
+ emplaceExp!(ComplexExp)(&ue, loc, -e1.toComplex(), type);
+ }
+ else
+ {
+ emplaceExp!(IntegerExp)(&ue, loc, -e1.toInteger(), type);
+ }
+ return ue;
+}
+
+UnionExp Com(Type type, Expression e1)
+{
+ UnionExp ue = void;
+ Loc loc = e1.loc;
+ emplaceExp!(IntegerExp)(&ue, loc, ~e1.toInteger(), type);
+ return ue;
+}
+
+UnionExp Not(Type type, Expression e1)
+{
+ UnionExp ue = void;
+ Loc loc = e1.loc;
+ emplaceExp!(IntegerExp)(&ue, loc, e1.isBool(false) ? 1 : 0, type);
+ return ue;
+}
+
+private UnionExp Bool(Type type, Expression e1)
+{
+ UnionExp ue = void;
+ Loc loc = e1.loc;
+ emplaceExp!(IntegerExp)(&ue, loc, e1.isBool(true) ? 1 : 0, type);
+ return ue;
+}
+
+UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ static if (LOG)
+ {
+ printf("Add(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars());
+ }
+ if (type.isreal())
+ {
+ emplaceExp!(RealExp)(&ue, loc, e1.toReal() + e2.toReal(), type);
+ }
+ else if (type.isimaginary())
+ {
+ emplaceExp!(RealExp)(&ue, loc, e1.toImaginary() + e2.toImaginary(), type);
+ }
+ else if (type.iscomplex())
+ {
+ // This rigamarole is necessary so that -0.0 doesn't get
+ // converted to +0.0 by doing an extraneous add with +0.0
+ auto c1 = complex_t(CTFloat.zero);
+ real_t r1 = CTFloat.zero;
+ real_t i1 = CTFloat.zero;
+ auto c2 = complex_t(CTFloat.zero);
+ real_t r2 = CTFloat.zero;
+ real_t i2 = CTFloat.zero;
+ auto v = complex_t(CTFloat.zero);
+ int x;
+ if (e1.type.isreal())
+ {
+ r1 = e1.toReal();
+ x = 0;
+ }
+ else if (e1.type.isimaginary())
+ {
+ i1 = e1.toImaginary();
+ x = 3;
+ }
+ else
+ {
+ c1 = e1.toComplex();
+ x = 6;
+ }
+ if (e2.type.isreal())
+ {
+ r2 = e2.toReal();
+ }
+ else if (e2.type.isimaginary())
+ {
+ i2 = e2.toImaginary();
+ x += 1;
+ }
+ else
+ {
+ c2 = e2.toComplex();
+ x += 2;
+ }
+ switch (x)
+ {
+ case 0 + 0:
+ v = complex_t(r1 + r2);
+ break;
+ case 0 + 1:
+ v = complex_t(r1, i2);
+ break;
+ case 0 + 2:
+ v = complex_t(r1 + creall(c2), cimagl(c2));
+ break;
+ case 3 + 0:
+ v = complex_t(r2, i1);
+ break;
+ case 3 + 1:
+ v = complex_t(CTFloat.zero, i1 + i2);
+ break;
+ case 3 + 2:
+ v = complex_t(creall(c2), i1 + cimagl(c2));
+ break;
+ case 6 + 0:
+ v = complex_t(creall(c1) + r2, cimagl(c2));
+ break;
+ case 6 + 1:
+ v = complex_t(creall(c1), cimagl(c1) + i2);
+ break;
+ case 6 + 2:
+ v = c1 + c2;
+ break;
+ default:
+ assert(0);
+ }
+ emplaceExp!(ComplexExp)(&ue, loc, v, type);
+ }
+ else if (e1.op == TOK.symbolOffset)
+ {
+ SymOffExp soe = cast(SymOffExp)e1;
+ emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset + e2.toInteger());
+ ue.exp().type = type;
+ }
+ else if (e2.op == TOK.symbolOffset)
+ {
+ SymOffExp soe = cast(SymOffExp)e2;
+ emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset + e1.toInteger());
+ ue.exp().type = type;
+ }
+ else
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() + e2.toInteger(), type);
+ return ue;
+}
+
+UnionExp Min(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ if (type.isreal())
+ {
+ emplaceExp!(RealExp)(&ue, loc, e1.toReal() - e2.toReal(), type);
+ }
+ else if (type.isimaginary())
+ {
+ emplaceExp!(RealExp)(&ue, loc, e1.toImaginary() - e2.toImaginary(), type);
+ }
+ else if (type.iscomplex())
+ {
+ // This rigamarole is necessary so that -0.0 doesn't get
+ // converted to +0.0 by doing an extraneous add with +0.0
+ auto c1 = complex_t(CTFloat.zero);
+ real_t r1 = CTFloat.zero;
+ real_t i1 = CTFloat.zero;
+ auto c2 = complex_t(CTFloat.zero);
+ real_t r2 = CTFloat.zero;
+ real_t i2 = CTFloat.zero;
+ auto v = complex_t(CTFloat.zero);
+ int x;
+ if (e1.type.isreal())
+ {
+ r1 = e1.toReal();
+ x = 0;
+ }
+ else if (e1.type.isimaginary())
+ {
+ i1 = e1.toImaginary();
+ x = 3;
+ }
+ else
+ {
+ c1 = e1.toComplex();
+ x = 6;
+ }
+ if (e2.type.isreal())
+ {
+ r2 = e2.toReal();
+ }
+ else if (e2.type.isimaginary())
+ {
+ i2 = e2.toImaginary();
+ x += 1;
+ }
+ else
+ {
+ c2 = e2.toComplex();
+ x += 2;
+ }
+ switch (x)
+ {
+ case 0 + 0:
+ v = complex_t(r1 - r2);
+ break;
+ case 0 + 1:
+ v = complex_t(r1, -i2);
+ break;
+ case 0 + 2:
+ v = complex_t(r1 - creall(c2), -cimagl(c2));
+ break;
+ case 3 + 0:
+ v = complex_t(-r2, i1);
+ break;
+ case 3 + 1:
+ v = complex_t(CTFloat.zero, i1 - i2);
+ break;
+ case 3 + 2:
+ v = complex_t(-creall(c2), i1 - cimagl(c2));
+ break;
+ case 6 + 0:
+ v = complex_t(creall(c1) - r2, cimagl(c1));
+ break;
+ case 6 + 1:
+ v = complex_t(creall(c1), cimagl(c1) - i2);
+ break;
+ case 6 + 2:
+ v = c1 - c2;
+ break;
+ default:
+ assert(0);
+ }
+ emplaceExp!(ComplexExp)(&ue, loc, v, type);
+ }
+ else if (e1.op == TOK.symbolOffset)
+ {
+ SymOffExp soe = cast(SymOffExp)e1;
+ emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset - e2.toInteger());
+ ue.exp().type = type;
+ }
+ else
+ {
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() - e2.toInteger(), type);
+ }
+ return ue;
+}
+
+UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ if (type.isfloating())
+ {
+ auto c = complex_t(CTFloat.zero);
+ real_t r = CTFloat.zero;
+ if (e1.type.isreal())
+ {
+ r = e1.toReal();
+ c = e2.toComplex();
+ c = complex_t(r * creall(c), r * cimagl(c));
+ }
+ else if (e1.type.isimaginary())
+ {
+ r = e1.toImaginary();
+ c = e2.toComplex();
+ c = complex_t(-r * cimagl(c), r * creall(c));
+ }
+ else if (e2.type.isreal())
+ {
+ r = e2.toReal();
+ c = e1.toComplex();
+ c = complex_t(r * creall(c), r * cimagl(c));
+ }
+ else if (e2.type.isimaginary())
+ {
+ r = e2.toImaginary();
+ c = e1.toComplex();
+ c = complex_t(-r * cimagl(c), r * creall(c));
+ }
+ else
+ c = e1.toComplex() * e2.toComplex();
+ if (type.isreal())
+ emplaceExp!(RealExp)(&ue, loc, creall(c), type);
+ else if (type.isimaginary())
+ emplaceExp!(RealExp)(&ue, loc, cimagl(c), type);
+ else if (type.iscomplex())
+ emplaceExp!(ComplexExp)(&ue, loc, c, type);
+ else
+ assert(0);
+ }
+ else
+ {
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() * e2.toInteger(), type);
+ }
+ return ue;
+}
+
+UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ if (type.isfloating())
+ {
+ auto c = complex_t(CTFloat.zero);
+ if (e2.type.isreal())
+ {
+ if (e1.type.isreal())
+ {
+ emplaceExp!(RealExp)(&ue, loc, e1.toReal() / e2.toReal(), type);
+ return ue;
+ }
+ const r = e2.toReal();
+ c = e1.toComplex();
+ c = complex_t(creall(c) / r, cimagl(c) / r);
+ }
+ else if (e2.type.isimaginary())
+ {
+ const r = e2.toImaginary();
+ c = e1.toComplex();
+ c = complex_t(cimagl(c) / r, -creall(c) / r);
+ }
+ else
+ {
+ c = e1.toComplex() / e2.toComplex();
+ }
+
+ if (type.isreal())
+ emplaceExp!(RealExp)(&ue, loc, creall(c), type);
+ else if (type.isimaginary())
+ emplaceExp!(RealExp)(&ue, loc, cimagl(c), type);
+ else if (type.iscomplex())
+ emplaceExp!(ComplexExp)(&ue, loc, c, type);
+ else
+ assert(0);
+ }
+ else
+ {
+ sinteger_t n1;
+ sinteger_t n2;
+ sinteger_t n;
+ n1 = e1.toInteger();
+ n2 = e2.toInteger();
+ if (n2 == 0)
+ {
+ e2.error("divide by 0");
+ emplaceExp!(ErrorExp)(&ue);
+ return ue;
+ }
+ if (n2 == -1 && !type.isunsigned())
+ {
+ // Check for int.min / -1
+ if (n1 == 0xFFFFFFFF80000000UL && type.toBasetype().ty != Tint64)
+ {
+ e2.error("integer overflow: `int.min / -1`");
+ emplaceExp!(ErrorExp)(&ue);
+ return ue;
+ }
+ else if (n1 == 0x8000000000000000L) // long.min / -1
+ {
+ e2.error("integer overflow: `long.min / -1L`");
+ emplaceExp!(ErrorExp)(&ue);
+ return ue;
+ }
+ }
+ if (e1.type.isunsigned() || e2.type.isunsigned())
+ n = (cast(dinteger_t)n1) / (cast(dinteger_t)n2);
+ else
+ n = n1 / n2;
+ emplaceExp!(IntegerExp)(&ue, loc, n, type);
+ }
+ return ue;
+}
+
+UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ if (type.isfloating())
+ {
+ auto c = complex_t(CTFloat.zero);
+ if (e2.type.isreal())
+ {
+ const r2 = e2.toReal();
+ c = complex_t(e1.toReal() % r2, e1.toImaginary() % r2);
+ }
+ else if (e2.type.isimaginary())
+ {
+ const i2 = e2.toImaginary();
+ c = complex_t(e1.toReal() % i2, e1.toImaginary() % i2);
+ }
+ else
+ assert(0);
+ if (type.isreal())
+ emplaceExp!(RealExp)(&ue, loc, creall(c), type);
+ else if (type.isimaginary())
+ emplaceExp!(RealExp)(&ue, loc, cimagl(c), type);
+ else if (type.iscomplex())
+ emplaceExp!(ComplexExp)(&ue, loc, c, type);
+ else
+ assert(0);
+ }
+ else
+ {
+ sinteger_t n1;
+ sinteger_t n2;
+ sinteger_t n;
+ n1 = e1.toInteger();
+ n2 = e2.toInteger();
+ if (n2 == 0)
+ {
+ e2.error("divide by 0");
+ emplaceExp!(ErrorExp)(&ue);
+ return ue;
+ }
+ if (n2 == -1 && !type.isunsigned())
+ {
+ // Check for int.min % -1
+ if (n1 == 0xFFFFFFFF80000000UL && type.toBasetype().ty != Tint64)
+ {
+ e2.error("integer overflow: `int.min %% -1`");
+ emplaceExp!(ErrorExp)(&ue);
+ return ue;
+ }
+ else if (n1 == 0x8000000000000000L) // long.min % -1
+ {
+ e2.error("integer overflow: `long.min %% -1L`");
+ emplaceExp!(ErrorExp)(&ue);
+ return ue;
+ }
+ }
+ if (e1.type.isunsigned() || e2.type.isunsigned())
+ n = (cast(dinteger_t)n1) % (cast(dinteger_t)n2);
+ else
+ n = n1 % n2;
+ emplaceExp!(IntegerExp)(&ue, loc, n, type);
+ }
+ return ue;
+}
+
+UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ //printf("Pow()\n");
+ UnionExp ue;
+ // Handle integer power operations.
+ if (e2.type.isintegral())
+ {
+ dinteger_t n = e2.toInteger();
+ bool neg;
+ if (!e2.type.isunsigned() && cast(sinteger_t)n < 0)
+ {
+ if (e1.type.isintegral())
+ {
+ cantExp(ue);
+ return ue;
+ }
+ // Don't worry about overflow, from now on n is unsigned.
+ neg = true;
+ n = -n;
+ }
+ else
+ neg = false;
+ UnionExp ur, uv;
+ if (e1.type.iscomplex())
+ {
+ emplaceExp!(ComplexExp)(&ur, loc, e1.toComplex(), e1.type);
+ emplaceExp!(ComplexExp)(&uv, loc, complex_t(CTFloat.one), e1.type);
+ }
+ else if (e1.type.isfloating())
+ {
+ emplaceExp!(RealExp)(&ur, loc, e1.toReal(), e1.type);
+ emplaceExp!(RealExp)(&uv, loc, CTFloat.one, e1.type);
+ }
+ else
+ {
+ emplaceExp!(IntegerExp)(&ur, loc, e1.toInteger(), e1.type);
+ emplaceExp!(IntegerExp)(&uv, loc, 1, e1.type);
+ }
+ Expression r = ur.exp();
+ Expression v = uv.exp();
+ while (n != 0)
+ {
+ if (n & 1)
+ {
+ // v = v * r;
+ uv = Mul(loc, v.type, v, r);
+ }
+ n >>= 1;
+ // r = r * r
+ ur = Mul(loc, r.type, r, r);
+ }
+ if (neg)
+ {
+ // ue = 1.0 / v
+ UnionExp one;
+ emplaceExp!(RealExp)(&one, loc, CTFloat.one, v.type);
+ uv = Div(loc, v.type, one.exp(), v);
+ }
+ if (type.iscomplex())
+ emplaceExp!(ComplexExp)(&ue, loc, v.toComplex(), type);
+ else if (type.isintegral())
+ emplaceExp!(IntegerExp)(&ue, loc, v.toInteger(), type);
+ else
+ emplaceExp!(RealExp)(&ue, loc, v.toReal(), type);
+ }
+ else if (e2.type.isfloating())
+ {
+ // x ^^ y for x < 0 and y not an integer is not defined; so set result as NaN
+ if (e1.toReal() < CTFloat.zero)
+ {
+ emplaceExp!(RealExp)(&ue, loc, target.RealProperties.nan, type);
+ }
+ else
+ cantExp(ue);
+ }
+ else
+ cantExp(ue);
+ return ue;
+}
+
+UnionExp Shl(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() << e2.toInteger(), type);
+ return ue;
+}
+
+UnionExp Shr(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ dinteger_t value = e1.toInteger();
+ dinteger_t dcount = e2.toInteger();
+ assert(dcount <= 0xFFFFFFFF);
+ uint count = cast(uint)dcount;
+ switch (e1.type.toBasetype().ty)
+ {
+ case Tint8:
+ value = cast(d_int8)value >> count;
+ break;
+ case Tuns8:
+ case Tchar:
+ value = cast(d_uns8)value >> count;
+ break;
+ case Tint16:
+ value = cast(d_int16)value >> count;
+ break;
+ case Tuns16:
+ case Twchar:
+ value = cast(d_uns16)value >> count;
+ break;
+ case Tint32:
+ value = cast(d_int32)value >> count;
+ break;
+ case Tuns32:
+ case Tdchar:
+ value = cast(d_uns32)value >> count;
+ break;
+ case Tint64:
+ value = cast(d_int64)value >> count;
+ break;
+ case Tuns64:
+ value = cast(d_uns64)value >> count;
+ break;
+ case Terror:
+ emplaceExp!(ErrorExp)(&ue);
+ return ue;
+ default:
+ assert(0);
+ }
+ emplaceExp!(IntegerExp)(&ue, loc, value, type);
+ return ue;
+}
+
+UnionExp Ushr(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ dinteger_t value = e1.toInteger();
+ dinteger_t dcount = e2.toInteger();
+ assert(dcount <= 0xFFFFFFFF);
+ uint count = cast(uint)dcount;
+ switch (e1.type.toBasetype().ty)
+ {
+ case Tint8:
+ case Tuns8:
+ case Tchar:
+ // Possible only with >>>=. >>> always gets promoted to int.
+ value = (value & 0xFF) >>> count;
+ break;
+ case Tint16:
+ case Tuns16:
+ case Twchar:
+ // Possible only with >>>=. >>> always gets promoted to int.
+ value = (value & 0xFFFF) >>> count;
+ break;
+ case Tint32:
+ case Tuns32:
+ case Tdchar:
+ value = (value & 0xFFFFFFFF) >>> count;
+ break;
+ case Tint64:
+ case Tuns64:
+ value = value >>> count;
+ break;
+ case Terror:
+ emplaceExp!(ErrorExp)(&ue);
+ return ue;
+ default:
+ assert(0);
+ }
+ emplaceExp!(IntegerExp)(&ue, loc, value, type);
+ return ue;
+}
+
+UnionExp And(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() & e2.toInteger(), type);
+ return ue;
+}
+
+UnionExp Or(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() | e2.toInteger(), type);
+ return ue;
+}
+
+UnionExp Xor(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ //printf("Xor(linnum = %d, e1 = %s, e2 = %s)\n", loc.linnum, e1.toChars(), e2.toChars());
+ UnionExp ue = void;
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() ^ e2.toInteger(), type);
+ return ue;
+}
+
+/* Also returns TOK.cantExpression if cannot be computed.
+ */
+UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ int cmp = 0;
+ real_t r1 = CTFloat.zero;
+ real_t r2 = CTFloat.zero;
+ //printf("Equal(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars());
+ assert(op == TOK.equal || op == TOK.notEqual);
+ if (e1.op == TOK.null_)
+ {
+ if (e2.op == TOK.null_)
+ cmp = 1;
+ else if (e2.op == TOK.string_)
+ {
+ StringExp es2 = cast(StringExp)e2;
+ cmp = (0 == es2.len);
+ }
+ else if (e2.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
+ cmp = !es2.elements || (0 == es2.elements.dim);
+ }
+ else
+ {
+ cantExp(ue);
+ return ue;
+ }
+ }
+ else if (e2.op == TOK.null_)
+ {
+ if (e1.op == TOK.string_)
+ {
+ StringExp es1 = cast(StringExp)e1;
+ cmp = (0 == es1.len);
+ }
+ else if (e1.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
+ cmp = !es1.elements || (0 == es1.elements.dim);
+ }
+ else
+ {
+ cantExp(ue);
+ return ue;
+ }
+ }
+ else if (e1.op == TOK.string_ && e2.op == TOK.string_)
+ {
+ StringExp es1 = cast(StringExp)e1;
+ StringExp es2 = cast(StringExp)e2;
+ if (es1.sz != es2.sz)
+ {
+ assert(global.errors);
+ cantExp(ue);
+ return ue;
+ }
+ const data1 = es1.peekData();
+ const data2 = es2.peekData();
+ if (es1.len == es2.len && memcmp(data1.ptr, data2.ptr, es1.sz * es1.len) == 0)
+ cmp = 1;
+ else
+ cmp = 0;
+ }
+ else if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
+ ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
+ if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim))
+ cmp = 1; // both arrays are empty
+ else if (!es1.elements || !es2.elements)
+ cmp = 0;
+ else if (es1.elements.dim != es2.elements.dim)
+ cmp = 0;
+ else
+ {
+ for (size_t i = 0; i < es1.elements.dim; i++)
+ {
+ auto ee1 = es1[i];
+ auto ee2 = es2[i];
+ ue = Equal(TOK.equal, loc, Type.tint32, ee1, ee2);
+ if (CTFEExp.isCantExp(ue.exp()))
+ return ue;
+ cmp = cast(int)ue.exp().toInteger();
+ if (cmp == 0)
+ break;
+ }
+ }
+ }
+ else if (e1.op == TOK.arrayLiteral && e2.op == TOK.string_)
+ {
+ // Swap operands and use common code
+ Expression etmp = e1;
+ e1 = e2;
+ e2 = etmp;
+ goto Lsa;
+ }
+ else if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral)
+ {
+ Lsa:
+ StringExp es1 = cast(StringExp)e1;
+ ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
+ size_t dim1 = es1.len;
+ size_t dim2 = es2.elements ? es2.elements.dim : 0;
+ if (dim1 != dim2)
+ cmp = 0;
+ else
+ {
+ cmp = 1; // if dim1 winds up being 0
+ for (size_t i = 0; i < dim1; i++)
+ {
+ uinteger_t c = es1.charAt(i);
+ auto ee2 = es2[i];
+ if (ee2.isConst() != 1)
+ {
+ cantExp(ue);
+ return ue;
+ }
+ cmp = (c == ee2.toInteger());
+ if (cmp == 0)
+ break;
+ }
+ }
+ }
+ else if (e1.op == TOK.structLiteral && e2.op == TOK.structLiteral)
+ {
+ StructLiteralExp es1 = cast(StructLiteralExp)e1;
+ StructLiteralExp es2 = cast(StructLiteralExp)e2;
+ if (es1.sd != es2.sd)
+ cmp = 0;
+ else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim))
+ cmp = 1; // both arrays are empty
+ else if (!es1.elements || !es2.elements)
+ cmp = 0;
+ else if (es1.elements.dim != es2.elements.dim)
+ cmp = 0;
+ else
+ {
+ cmp = 1;
+ for (size_t i = 0; i < es1.elements.dim; i++)
+ {
+ Expression ee1 = (*es1.elements)[i];
+ Expression ee2 = (*es2.elements)[i];
+ if (ee1 == ee2)
+ continue;
+ if (!ee1 || !ee2)
+ {
+ cmp = 0;
+ break;
+ }
+ ue = Equal(TOK.equal, loc, Type.tint32, ee1, ee2);
+ if (ue.exp().op == TOK.cantExpression)
+ return ue;
+ cmp = cast(int)ue.exp().toInteger();
+ if (cmp == 0)
+ break;
+ }
+ }
+ }
+ else if (e1.isConst() != 1 || e2.isConst() != 1)
+ {
+ cantExp(ue);
+ return ue;
+ }
+ else if (e1.type.isreal())
+ {
+ r1 = e1.toReal();
+ r2 = e2.toReal();
+ goto L1;
+ }
+ else if (e1.type.isimaginary())
+ {
+ r1 = e1.toImaginary();
+ r2 = e2.toImaginary();
+ L1:
+ if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
+ {
+ cmp = 0;
+ }
+ else
+ {
+ cmp = (r1 == r2);
+ }
+ }
+ else if (e1.type.iscomplex())
+ {
+ cmp = e1.toComplex() == e2.toComplex();
+ }
+ else if (e1.type.isintegral() || e1.type.toBasetype().ty == Tpointer)
+ {
+ cmp = (e1.toInteger() == e2.toInteger());
+ }
+ else
+ {
+ cantExp(ue);
+ return ue;
+ }
+ if (op == TOK.notEqual)
+ cmp ^= 1;
+ emplaceExp!(IntegerExp)(&ue, loc, cmp, type);
+ return ue;
+}
+
+UnionExp Identity(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ int cmp;
+ if (e1.op == TOK.null_)
+ {
+ cmp = (e2.op == TOK.null_);
+ }
+ else if (e2.op == TOK.null_)
+ {
+ cmp = 0;
+ }
+ else if (e1.op == TOK.symbolOffset && e2.op == TOK.symbolOffset)
+ {
+ SymOffExp es1 = cast(SymOffExp)e1;
+ SymOffExp es2 = cast(SymOffExp)e2;
+ cmp = (es1.var == es2.var && es1.offset == es2.offset);
+ }
+ else
+ {
+ if (e1.type.isreal())
+ {
+ cmp = RealIdentical(e1.toReal(), e2.toReal());
+ }
+ else if (e1.type.isimaginary())
+ {
+ cmp = RealIdentical(e1.toImaginary(), e2.toImaginary());
+ }
+ else if (e1.type.iscomplex())
+ {
+ complex_t v1 = e1.toComplex();
+ complex_t v2 = e2.toComplex();
+ cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1));
+ }
+ else
+ {
+ ue = Equal((op == TOK.identity) ? TOK.equal : TOK.notEqual, loc, type, e1, e2);
+ return ue;
+ }
+ }
+ if (op == TOK.notIdentity)
+ cmp ^= 1;
+ emplaceExp!(IntegerExp)(&ue, loc, cmp, type);
+ return ue;
+}
+
+UnionExp Cmp(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ dinteger_t n;
+ real_t r1 = CTFloat.zero;
+ real_t r2 = CTFloat.zero;
+ //printf("Cmp(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars());
+ if (e1.op == TOK.string_ && e2.op == TOK.string_)
+ {
+ StringExp es1 = cast(StringExp)e1;
+ StringExp es2 = cast(StringExp)e2;
+ size_t sz = es1.sz;
+ assert(sz == es2.sz);
+ size_t len = es1.len;
+ if (es2.len < len)
+ len = es2.len;
+ const data1 = es1.peekData();
+ const data2 = es1.peekData();
+ int rawCmp = memcmp(data1.ptr, data2.ptr, sz * len);
+ if (rawCmp == 0)
+ rawCmp = cast(int)(es1.len - es2.len);
+ n = specificCmp(op, rawCmp);
+ }
+ else if (e1.isConst() != 1 || e2.isConst() != 1)
+ {
+ cantExp(ue);
+ return ue;
+ }
+ else if (e1.type.isreal())
+ {
+ r1 = e1.toReal();
+ r2 = e2.toReal();
+ goto L1;
+ }
+ else if (e1.type.isimaginary())
+ {
+ r1 = e1.toImaginary();
+ r2 = e2.toImaginary();
+ L1:
+ n = realCmp(op, r1, r2);
+ }
+ else if (e1.type.iscomplex())
+ {
+ assert(0);
+ }
+ else
+ {
+ sinteger_t n1;
+ sinteger_t n2;
+ n1 = e1.toInteger();
+ n2 = e2.toInteger();
+ if (e1.type.isunsigned() || e2.type.isunsigned())
+ n = intUnsignedCmp(op, n1, n2);
+ else
+ n = intSignedCmp(op, n1, n2);
+ }
+ emplaceExp!(IntegerExp)(&ue, loc, n, type);
+ return ue;
+}
+
+/* Also returns TOK.cantExpression if cannot be computed.
+ * to: type to cast to
+ * type: type to paint the result
+ */
+UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1)
+{
+ UnionExp ue = void;
+ Type tb = to.toBasetype();
+ Type typeb = type.toBasetype();
+ //printf("Cast(type = %s, to = %s, e1 = %s)\n", type.toChars(), to.toChars(), e1.toChars());
+ //printf("\te1.type = %s\n", e1.type.toChars());
+ if (e1.type.equals(type) && type.equals(to))
+ {
+ emplaceExp!(UnionExp)(&ue, e1);
+ return ue;
+ }
+ if (e1.op == TOK.vector && (cast(TypeVector)e1.type).basetype.equals(type) && type.equals(to))
+ {
+ Expression ex = (cast(VectorExp)e1).e1;
+ emplaceExp!(UnionExp)(&ue, ex);
+ return ue;
+ }
+ if (e1.type.implicitConvTo(to) >= MATCH.constant || to.implicitConvTo(e1.type) >= MATCH.constant)
+ {
+ goto L1;
+ }
+ // Allow covariant converions of delegates
+ // (Perhaps implicit conversion from pure to impure should be a MATCH.constant,
+ // then we wouldn't need this extra check.)
+ if (e1.type.toBasetype().ty == Tdelegate && e1.type.implicitConvTo(to) == MATCH.convert)
+ {
+ goto L1;
+ }
+ /* Allow casting from one string type to another
+ */
+ if (e1.op == TOK.string_)
+ {
+ if (tb.ty == Tarray && typeb.ty == Tarray && tb.nextOf().size() == typeb.nextOf().size())
+ {
+ goto L1;
+ }
+ }
+ if (e1.op == TOK.arrayLiteral && typeb == tb)
+ {
+ L1:
+ Expression ex = expType(to, e1);
+ emplaceExp!(UnionExp)(&ue, ex);
+ return ue;
+ }
+ if (e1.isConst() != 1)
+ {
+ cantExp(ue);
+ }
+ else if (tb.ty == Tbool)
+ {
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() != 0, type);
+ }
+ else if (type.isintegral())
+ {
+ if (e1.type.isfloating())
+ {
+ dinteger_t result;
+ real_t r = e1.toReal();
+ switch (typeb.ty)
+ {
+ case Tint8:
+ result = cast(d_int8)cast(sinteger_t)r;
+ break;
+ case Tchar:
+ case Tuns8:
+ result = cast(d_uns8)cast(dinteger_t)r;
+ break;
+ case Tint16:
+ result = cast(d_int16)cast(sinteger_t)r;
+ break;
+ case Twchar:
+ case Tuns16:
+ result = cast(d_uns16)cast(dinteger_t)r;
+ break;
+ case Tint32:
+ result = cast(d_int32)r;
+ break;
+ case Tdchar:
+ case Tuns32:
+ result = cast(d_uns32)r;
+ break;
+ case Tint64:
+ result = cast(d_int64)r;
+ break;
+ case Tuns64:
+ result = cast(d_uns64)r;
+ break;
+ default:
+ assert(0);
+ }
+ emplaceExp!(IntegerExp)(&ue, loc, result, type);
+ }
+ else if (type.isunsigned())
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toUInteger(), type);
+ else
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type);
+ }
+ else if (tb.isreal())
+ {
+ real_t value = e1.toReal();
+ emplaceExp!(RealExp)(&ue, loc, value, type);
+ }
+ else if (tb.isimaginary())
+ {
+ real_t value = e1.toImaginary();
+ emplaceExp!(RealExp)(&ue, loc, value, type);
+ }
+ else if (tb.iscomplex())
+ {
+ complex_t value = e1.toComplex();
+ emplaceExp!(ComplexExp)(&ue, loc, value, type);
+ }
+ else if (tb.isscalar())
+ {
+ emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type);
+ }
+ else if (tb.ty == Tvoid)
+ {
+ cantExp(ue);
+ }
+ else if (tb.ty == Tstruct && e1.op == TOK.int64)
+ {
+ // Struct = 0;
+ StructDeclaration sd = tb.toDsymbol(null).isStructDeclaration();
+ assert(sd);
+ auto elements = new Expressions();
+ for (size_t i = 0; i < sd.fields.dim; i++)
+ {
+ VarDeclaration v = sd.fields[i];
+ UnionExp zero;
+ emplaceExp!(IntegerExp)(&zero, 0);
+ ue = Cast(loc, v.type, v.type, zero.exp());
+ if (ue.exp().op == TOK.cantExpression)
+ return ue;
+ elements.push(ue.exp().copy());
+ }
+ emplaceExp!(StructLiteralExp)(&ue, loc, sd, elements);
+ ue.exp().type = type;
+ }
+ else
+ {
+ if (type != Type.terror)
+ {
+ // have to change to Internal Compiler Error
+ // all invalid casts should be handled already in Expression::castTo().
+ error(loc, "cannot cast `%s` to `%s`", e1.type.toChars(), type.toChars());
+ }
+ emplaceExp!(ErrorExp)(&ue);
+ }
+ return ue;
+}
+
+UnionExp ArrayLength(Type type, Expression e1)
+{
+ UnionExp ue = void;
+ Loc loc = e1.loc;
+ if (e1.op == TOK.string_)
+ {
+ StringExp es1 = cast(StringExp)e1;
+ emplaceExp!(IntegerExp)(&ue, loc, es1.len, type);
+ }
+ else if (e1.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp ale = cast(ArrayLiteralExp)e1;
+ size_t dim = ale.elements ? ale.elements.dim : 0;
+ emplaceExp!(IntegerExp)(&ue, loc, dim, type);
+ }
+ else if (e1.op == TOK.assocArrayLiteral)
+ {
+ AssocArrayLiteralExp ale = cast(AssocArrayLiteralExp)e1;
+ size_t dim = ale.keys.dim;
+ emplaceExp!(IntegerExp)(&ue, loc, dim, type);
+ }
+ else if (e1.type.toBasetype().ty == Tsarray)
+ {
+ Expression e = (cast(TypeSArray)e1.type.toBasetype()).dim;
+ emplaceExp!(UnionExp)(&ue, e);
+ }
+ else
+ cantExp(ue);
+ return ue;
+}
+
+/* Also return TOK.cantExpression if this fails
+ */
+UnionExp Index(Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ Loc loc = e1.loc;
+ //printf("Index(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars());
+ assert(e1.type);
+ if (e1.op == TOK.string_ && e2.op == TOK.int64)
+ {
+ StringExp es1 = cast(StringExp)e1;
+ uinteger_t i = e2.toInteger();
+ if (i >= es1.len)
+ {
+ e1.error("string index %llu is out of bounds `[0 .. %llu]`", i, cast(ulong)es1.len);
+ emplaceExp!(ErrorExp)(&ue);
+ }
+ else
+ {
+ emplaceExp!(IntegerExp)(&ue, loc, es1.charAt(i), type);
+ }
+ }
+ else if (e1.type.toBasetype().ty == Tsarray && e2.op == TOK.int64)
+ {
+ TypeSArray tsa = cast(TypeSArray)e1.type.toBasetype();
+ uinteger_t length = tsa.dim.toInteger();
+ uinteger_t i = e2.toInteger();
+ if (i >= length)
+ {
+ e1.error("array index %llu is out of bounds `%s[0 .. %llu]`", i, e1.toChars(), length);
+ emplaceExp!(ErrorExp)(&ue);
+ }
+ else if (e1.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp ale = cast(ArrayLiteralExp)e1;
+ auto e = ale[cast(size_t)i];
+ e.type = type;
+ e.loc = loc;
+ if (hasSideEffect(e))
+ cantExp(ue);
+ else
+ emplaceExp!(UnionExp)(&ue, e);
+ }
+ else
+ cantExp(ue);
+ }
+ else if (e1.type.toBasetype().ty == Tarray && e2.op == TOK.int64)
+ {
+ uinteger_t i = e2.toInteger();
+ if (e1.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp ale = cast(ArrayLiteralExp)e1;
+ if (i >= ale.elements.dim)
+ {
+ e1.error("array index %llu is out of bounds `%s[0 .. %llu]`", i, e1.toChars(), cast(ulong) ale.elements.dim);
+ emplaceExp!(ErrorExp)(&ue);
+ }
+ else
+ {
+ auto e = ale[cast(size_t)i];
+ e.type = type;
+ e.loc = loc;
+ if (hasSideEffect(e))
+ cantExp(ue);
+ else
+ emplaceExp!(UnionExp)(&ue, e);
+ }
+ }
+ else
+ cantExp(ue);
+ }
+ else if (e1.op == TOK.assocArrayLiteral)
+ {
+ AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e1;
+ /* Search the keys backwards, in case there are duplicate keys
+ */
+ for (size_t i = ae.keys.dim; i;)
+ {
+ i--;
+ Expression ekey = (*ae.keys)[i];
+ ue = Equal(TOK.equal, loc, Type.tbool, ekey, e2);
+ if (CTFEExp.isCantExp(ue.exp()))
+ return ue;
+ if (ue.exp().isBool(true))
+ {
+ Expression e = (*ae.values)[i];
+ e.type = type;
+ e.loc = loc;
+ if (hasSideEffect(e))
+ cantExp(ue);
+ else
+ emplaceExp!(UnionExp)(&ue, e);
+ return ue;
+ }
+ }
+ cantExp(ue);
+ }
+ else
+ cantExp(ue);
+ return ue;
+}
+
+/* Also return TOK.cantExpression if this fails
+ */
+UnionExp Slice(Type type, Expression e1, Expression lwr, Expression upr)
+{
+ UnionExp ue = void;
+ Loc loc = e1.loc;
+ static if (LOG)
+ {
+ printf("Slice()\n");
+ if (lwr)
+ {
+ printf("\te1 = %s\n", e1.toChars());
+ printf("\tlwr = %s\n", lwr.toChars());
+ printf("\tupr = %s\n", upr.toChars());
+ }
+ }
+
+ static bool sliceBoundsCheck(uinteger_t lwr, uinteger_t upr, uinteger_t newlwr, uinteger_t newupr) pure
+ {
+ assert(lwr <= upr);
+ return !(newlwr <= newupr &&
+ lwr <= newlwr &&
+ newupr <= upr);
+ }
+
+ if (e1.op == TOK.string_ && lwr.op == TOK.int64 && upr.op == TOK.int64)
+ {
+ StringExp es1 = cast(StringExp)e1;
+ const uinteger_t ilwr = lwr.toInteger();
+ const uinteger_t iupr = upr.toInteger();
+ if (sliceBoundsCheck(0, es1.len, ilwr, iupr))
+ cantExp(ue); // https://issues.dlang.org/show_bug.cgi?id=18115
+ else
+ {
+ const len = cast(size_t)(iupr - ilwr);
+ const sz = es1.sz;
+ void* s = mem.xmalloc(len * sz);
+ const data1 = es1.peekData();
+ memcpy(s, data1.ptr + ilwr * sz, len * sz);
+ emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz, es1.postfix);
+ StringExp es = cast(StringExp)ue.exp();
+ es.committed = es1.committed;
+ es.type = type;
+ }
+ }
+ else if (e1.op == TOK.arrayLiteral && lwr.op == TOK.int64 && upr.op == TOK.int64 && !hasSideEffect(e1))
+ {
+ ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
+ const uinteger_t ilwr = lwr.toInteger();
+ const uinteger_t iupr = upr.toInteger();
+ if (sliceBoundsCheck(0, es1.elements.dim, ilwr, iupr))
+ cantExp(ue);
+ else
+ {
+ auto elements = new Expressions(cast(size_t)(iupr - ilwr));
+ memcpy(elements.tdata(), es1.elements.tdata() + ilwr, cast(size_t)(iupr - ilwr) * ((*es1.elements)[0]).sizeof);
+ emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, elements);
+ }
+ }
+ else
+ cantExp(ue);
+ return ue;
+}
+
+/* Set a slice of char/integer array literal 'existingAE' from a string 'newval'.
+ * existingAE[firstIndex..firstIndex+newval.length] = newval.
+ */
+void sliceAssignArrayLiteralFromString(ArrayLiteralExp existingAE, const StringExp newval, size_t firstIndex)
+{
+ const len = newval.len;
+ Type elemType = existingAE.type.nextOf();
+ foreach (j; 0 .. len)
+ {
+ const val = newval.getCodeUnit(j);
+ (*existingAE.elements)[j + firstIndex] = new IntegerExp(newval.loc, val, elemType);
+ }
+}
+
+/* Set a slice of string 'existingSE' from a char array literal 'newae'.
+ * existingSE[firstIndex..firstIndex+newae.length] = newae.
+ */
+void sliceAssignStringFromArrayLiteral(StringExp existingSE, ArrayLiteralExp newae, size_t firstIndex)
+{
+ assert(existingSE.ownedByCtfe != OwnedBy.code);
+ foreach (j; 0 .. newae.elements.dim)
+ {
+ existingSE.setCodeUnit(firstIndex + j, cast(dchar)newae[j].toInteger());
+ }
+}
+
+/* Set a slice of string 'existingSE' from a string 'newstr'.
+ * existingSE[firstIndex..firstIndex+newstr.length] = newstr.
+ */
+void sliceAssignStringFromString(StringExp existingSE, const StringExp newstr, size_t firstIndex)
+{
+ assert(existingSE.ownedByCtfe != OwnedBy.code);
+ size_t sz = existingSE.sz;
+ assert(sz == newstr.sz);
+ auto data1 = existingSE.borrowData();
+ const data2 = newstr.peekData();
+ memcpy(data1.ptr + firstIndex * sz, data2.ptr, data2.length);
+}
+
+/* Compare a string slice with another string slice.
+ * Conceptually equivalent to memcmp( se1[lo1..lo1+len], se2[lo2..lo2+len])
+ */
+int sliceCmpStringWithString(const StringExp se1, const StringExp se2, size_t lo1, size_t lo2, size_t len)
+{
+ size_t sz = se1.sz;
+ assert(sz == se2.sz);
+ const data1 = se1.peekData();
+ const data2 = se2.peekData();
+ return memcmp(data1.ptr + sz * lo1, data2.ptr + sz * lo2, sz * len);
+}
+
+/* Compare a string slice with an array literal slice
+ * Conceptually equivalent to memcmp( se1[lo1..lo1+len], ae2[lo2..lo2+len])
+ */
+int sliceCmpStringWithArray(const StringExp se1, ArrayLiteralExp ae2, size_t lo1, size_t lo2, size_t len)
+{
+ foreach (j; 0 .. len)
+ {
+ const val2 = cast(dchar)ae2[j + lo2].toInteger();
+ const val1 = se1.getCodeUnit(j + lo1);
+ const int c = val1 - val2;
+ if (c)
+ return c;
+ }
+ return 0;
+}
+
+/** Copy element `Expressions` in the parameters when they're `ArrayLiteralExp`s.
+ * Params:
+ * e1 = If it's ArrayLiteralExp, its `elements` will be copied.
+ * Otherwise, `e1` itself will be pushed into the new `Expressions`.
+ * e2 = If it's not `null`, it will be pushed/appended to the new
+ * `Expressions` by the same way with `e1`.
+ * Returns:
+ * Newly allocated `Expressions`. Note that it points to the original
+ * `Expression` values in e1 and e2.
+ */
+private Expressions* copyElements(Expression e1, Expression e2 = null)
+{
+ auto elems = new Expressions();
+
+ void append(ArrayLiteralExp ale)
+ {
+ if (!ale.elements)
+ return;
+ auto d = elems.dim;
+ elems.append(ale.elements);
+ foreach (ref el; (*elems)[d .. elems.dim])
+ {
+ if (!el)
+ el = ale.basis;
+ }
+ }
+
+ if (e1.op == TOK.arrayLiteral)
+ append(cast(ArrayLiteralExp)e1);
+ else
+ elems.push(e1);
+
+ if (e2)
+ {
+ if (e2.op == TOK.arrayLiteral)
+ append(cast(ArrayLiteralExp)e2);
+ else
+ elems.push(e2);
+ }
+
+ return elems;
+}
+
+/* Also return TOK.cantExpression if this fails
+ */
+UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ Expression e = CTFEExp.cantexp;
+ Type t;
+ Type t1 = e1.type.toBasetype();
+ Type t2 = e2.type.toBasetype();
+ //printf("Cat(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars());
+ //printf("\tt1 = %s, t2 = %s, type = %s\n", t1.toChars(), t2.toChars(), type.toChars());
+ if (e1.op == TOK.null_ && (e2.op == TOK.int64 || e2.op == TOK.structLiteral))
+ {
+ e = e2;
+ t = t1;
+ goto L2;
+ }
+ else if ((e1.op == TOK.int64 || e1.op == TOK.structLiteral) && e2.op == TOK.null_)
+ {
+ e = e1;
+ t = t2;
+ L2:
+ Type tn = e.type.toBasetype();
+ if (tn.ty.isSomeChar)
+ {
+ // Create a StringExp
+ if (t.nextOf())
+ t = t.nextOf().toBasetype();
+ const sz = cast(ubyte)t.size();
+ dinteger_t v = e.toInteger();
+ const len = (t.ty == tn.ty) ? 1 : utf_codeLength(sz, cast(dchar)v);
+ void* s = mem.xmalloc(len * sz);
+ if (t.ty == tn.ty)
+ Port.valcpy(s, v, sz);
+ else
+ utf_encode(sz, s, cast(dchar)v);
+ emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
+ StringExp es = cast(StringExp)ue.exp();
+ es.type = type;
+ es.committed = 1;
+ }
+ else
+ {
+ // Create an ArrayLiteralExp
+ auto elements = new Expressions();
+ elements.push(e);
+ emplaceExp!(ArrayLiteralExp)(&ue, e.loc, type, elements);
+ }
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e1.op == TOK.null_ && e2.op == TOK.null_)
+ {
+ if (type == e1.type)
+ {
+ // Handle null ~= null
+ if (t1.ty == Tarray && t2 == t1.nextOf())
+ {
+ emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, e2);
+ assert(ue.exp().type);
+ return ue;
+ }
+ else
+ {
+ emplaceExp!(UnionExp)(&ue, e1);
+ assert(ue.exp().type);
+ return ue;
+ }
+ }
+ if (type == e2.type)
+ {
+ emplaceExp!(UnionExp)(&ue, e2);
+ assert(ue.exp().type);
+ return ue;
+ }
+ emplaceExp!(NullExp)(&ue, e1.loc, type);
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e1.op == TOK.string_ && e2.op == TOK.string_)
+ {
+ // Concatenate the strings
+ StringExp es1 = cast(StringExp)e1;
+ StringExp es2 = cast(StringExp)e2;
+ size_t len = es1.len + es2.len;
+ ubyte sz = es1.sz;
+ if (sz != es2.sz)
+ {
+ /* Can happen with:
+ * auto s = "foo"d ~ "bar"c;
+ */
+ assert(global.errors);
+ cantExp(ue);
+ assert(ue.exp().type);
+ return ue;
+ }
+ void* s = mem.xmalloc(len * sz);
+ const data1 = es1.peekData();
+ const data2 = es2.peekData();
+ memcpy(cast(char*)s, data1.ptr, es1.len * sz);
+ memcpy(cast(char*)s + es1.len * sz, data2.ptr, es2.len * sz);
+ emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
+ StringExp es = cast(StringExp)ue.exp();
+ es.committed = es1.committed | es2.committed;
+ es.type = type;
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e2.op == TOK.string_ && e1.op == TOK.arrayLiteral && t1.nextOf().isintegral())
+ {
+ // [chars] ~ string --> [chars]
+ StringExp es = cast(StringExp)e2;
+ ArrayLiteralExp ea = cast(ArrayLiteralExp)e1;
+ size_t len = es.len + ea.elements.dim;
+ auto elems = new Expressions(len);
+ for (size_t i = 0; i < ea.elements.dim; ++i)
+ {
+ (*elems)[i] = ea[i];
+ }
+ emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, elems);
+ ArrayLiteralExp dest = cast(ArrayLiteralExp)ue.exp();
+ sliceAssignArrayLiteralFromString(dest, es, ea.elements.dim);
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral && t2.nextOf().isintegral())
+ {
+ // string ~ [chars] --> [chars]
+ StringExp es = cast(StringExp)e1;
+ ArrayLiteralExp ea = cast(ArrayLiteralExp)e2;
+ size_t len = es.len + ea.elements.dim;
+ auto elems = new Expressions(len);
+ for (size_t i = 0; i < ea.elements.dim; ++i)
+ {
+ (*elems)[es.len + i] = ea[i];
+ }
+ emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, type, elems);
+ ArrayLiteralExp dest = cast(ArrayLiteralExp)ue.exp();
+ sliceAssignArrayLiteralFromString(dest, es, 0);
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e1.op == TOK.string_ && e2.op == TOK.int64)
+ {
+ // string ~ char --> string
+ StringExp es1 = cast(StringExp)e1;
+ StringExp es;
+ const sz = es1.sz;
+ dinteger_t v = e2.toInteger();
+ // Is it a concatenation of homogenous types?
+ // (char[] ~ char, wchar[]~wchar, or dchar[]~dchar)
+ bool homoConcat = (sz == t2.size());
+ const len = es1.len + (homoConcat ? 1 : utf_codeLength(sz, cast(dchar)v));
+ void* s = mem.xmalloc(len * sz);
+ const data1 = es1.peekData();
+ memcpy(s, data1.ptr, data1.length);
+ if (homoConcat)
+ Port.valcpy(cast(char*)s + (sz * es1.len), v, sz);
+ else
+ utf_encode(sz, cast(char*)s + (sz * es1.len), cast(dchar)v);
+ emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
+ es = cast(StringExp)ue.exp();
+ es.committed = es1.committed;
+ es.type = type;
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e1.op == TOK.int64 && e2.op == TOK.string_)
+ {
+ // [w|d]?char ~ string --> string
+ // We assume that we only ever prepend one char of the same type
+ // (wchar,dchar) as the string's characters.
+ StringExp es2 = cast(StringExp)e2;
+ const len = 1 + es2.len;
+ const sz = es2.sz;
+ dinteger_t v = e1.toInteger();
+ void* s = mem.xmalloc(len * sz);
+ Port.valcpy(cast(char*)s, v, sz);
+ const data2 = es2.peekData();
+ memcpy(cast(char*)s + sz, data2.ptr, data2.length);
+ emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
+ StringExp es = cast(StringExp)ue.exp();
+ es.sz = sz;
+ es.committed = es2.committed;
+ es.type = type;
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
+ {
+ // Concatenate the arrays
+ auto elems = copyElements(e1, e2);
+
+ emplaceExp!(ArrayLiteralExp)(&ue, e1.loc, cast(Type)null, elems);
+
+ e = ue.exp();
+ if (type.toBasetype().ty == Tsarray)
+ {
+ e.type = t1.nextOf().sarrayOf(elems.dim);
+ }
+ else
+ e.type = type;
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf()))
+ {
+ e = e1;
+ goto L3;
+ }
+ else if (e1.op == TOK.null_ && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
+ {
+ e = e2;
+ L3:
+ // Concatenate the array with null
+ auto elems = copyElements(e);
+
+ emplaceExp!(ArrayLiteralExp)(&ue, e.loc, cast(Type)null, elems);
+
+ e = ue.exp();
+ if (type.toBasetype().ty == Tsarray)
+ {
+ e.type = t1.nextOf().sarrayOf(elems.dim);
+ }
+ else
+ e.type = type;
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if ((e1.op == TOK.arrayLiteral || e1.op == TOK.null_) && e1.type.toBasetype().nextOf() && e1.type.toBasetype().nextOf().equals(e2.type))
+ {
+ auto elems = (e1.op == TOK.arrayLiteral)
+ ? copyElements(e1) : new Expressions();
+ elems.push(e2);
+
+ emplaceExp!(ArrayLiteralExp)(&ue, loc, cast(Type)null, elems);
+
+ e = ue.exp();
+ if (type.toBasetype().ty == Tsarray)
+ {
+ e.type = e2.type.sarrayOf(elems.dim);
+ }
+ else
+ e.type = type;
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e2.op == TOK.arrayLiteral && e2.type.toBasetype().nextOf().equals(e1.type))
+ {
+ auto elems = copyElements(e1, e2);
+
+ emplaceExp!(ArrayLiteralExp)(&ue, loc, cast(Type)null, elems);
+
+ e = ue.exp();
+ if (type.toBasetype().ty == Tsarray)
+ {
+ e.type = e1.type.sarrayOf(elems.dim);
+ }
+ else
+ e.type = type;
+ assert(ue.exp().type);
+ return ue;
+ }
+ else if (e1.op == TOK.null_ && e2.op == TOK.string_)
+ {
+ t = e1.type;
+ e = e2;
+ goto L1;
+ }
+ else if (e1.op == TOK.string_ && e2.op == TOK.null_)
+ {
+ e = e1;
+ t = e2.type;
+ L1:
+ Type tb = t.toBasetype();
+ if (tb.ty == Tarray && tb.nextOf().equivalent(e.type))
+ {
+ auto expressions = new Expressions();
+ expressions.push(e);
+ emplaceExp!(ArrayLiteralExp)(&ue, loc, t, expressions);
+ e = ue.exp();
+ }
+ else
+ {
+ emplaceExp!(UnionExp)(&ue, e);
+ e = ue.exp();
+ }
+ if (!e.type.equals(type))
+ {
+ StringExp se = cast(StringExp)e.copy();
+ e = se.castTo(null, type);
+ emplaceExp!(UnionExp)(&ue, e);
+ e = ue.exp();
+ }
+ }
+ else
+ cantExp(ue);
+ assert(ue.exp().type);
+ return ue;
+}
+
+UnionExp Ptr(Type type, Expression e1)
+{
+ //printf("Ptr(e1 = %s)\n", e1.toChars());
+ UnionExp ue = void;
+ if (e1.op == TOK.add)
+ {
+ AddExp ae = cast(AddExp)e1;
+ if (ae.e1.op == TOK.address && ae.e2.op == TOK.int64)
+ {
+ AddrExp ade = cast(AddrExp)ae.e1;
+ if (ade.e1.op == TOK.structLiteral)
+ {
+ StructLiteralExp se = cast(StructLiteralExp)ade.e1;
+ uint offset = cast(uint)ae.e2.toInteger();
+ Expression e = se.getField(type, offset);
+ if (e)
+ {
+ emplaceExp!(UnionExp)(&ue, e);
+ return ue;
+ }
+ }
+ }
+ }
+ cantExp(ue);
+ return ue;
+}
diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d
new file mode 100644
index 00000000000..bb40649dc0b
--- /dev/null
+++ b/gcc/d/dmd/cparse.d
@@ -0,0 +1,4249 @@
+/**
+ * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
+ *
+ * Specification: C11
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cparse.d, _cparse.d)
+ * Documentation: https://dlang.org/phobos/dmd_cparse.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cparse.d
+ */
+
+module dmd.cparse;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.astenums;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.lexer;
+import dmd.parse;
+import dmd.errors;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.tokens;
+
+/***********************************************************
+ */
+final class CParser(AST) : Parser!AST
+{
+ AST.Dsymbols* symbols; // symbols declared in current scope
+
+ bool addFuncName; /// add declaration of __func__ to function symbol table
+
+ extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment,
+ const ref TARGET target)
+ {
+ super(_module, input, doDocComment);
+
+ //printf("CParser.this()\n");
+ mod = _module;
+ linkage = LINK.c;
+ Ccompile = true;
+
+ // Configure sizes for C `long`, `long double`, `wchar_t`
+ this.longsize = target.longsize;
+ this.long_doublesize = target.long_doublesize;
+ this.wchar_tsize = target.wchar_tsize;
+
+ // C `char` is always unsigned in ImportC
+ }
+
+ /********************************************
+ * Parse translation unit.
+ * C11 6.9
+ * translation-unit:
+ * external-declaration
+ * translation-unit external-declaration
+ *
+ * external-declaration:
+ * function-definition
+ * declaration
+ * Returns:
+ * array of Dsymbols that were declared
+ */
+ override AST.Dsymbols* parseModule()
+ {
+ //printf("cparseTranslationUnit()\n");
+ symbols = new AST.Dsymbols();
+ while (1)
+ {
+ if (token.value == TOK.endOfFile)
+ {
+ // wrap the symbols in `extern (C) { symbols }`
+ auto wrap = new AST.Dsymbols();
+ auto ld = new AST.LinkDeclaration(token.loc, LINK.c, symbols);
+ wrap.push(ld);
+
+ return wrap;
+ }
+
+ cparseDeclaration(LVL.global);
+ }
+ }
+
+ /******************************************************************************/
+ /********************************* Statement Parser ***************************/
+ //{
+
+ /**********************
+ * C11 6.8
+ * statement:
+ * labeled-statement
+ * compound-statement
+ * expression-statement
+ * selection-statement
+ * iteration-statement
+ * jump-statement
+ *
+ * Params:
+ * flags = PSxxxx
+ * endPtr = store location of closing brace
+ * pEndloc = if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
+ * Returns:
+ * parsed statement
+ */
+ AST.Statement cparseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
+ {
+ AST.Statement s;
+ const loc = token.loc;
+
+ //printf("cparseStatement()\n");
+
+ auto symbolsSave = symbols;
+ if (!(flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope)))
+ symbols = new AST.Dsymbols();
+
+ switch (token.value)
+ {
+ case TOK.identifier:
+ /* A leading identifier can be a declaration, label, or expression.
+ * A quick check of the next token can disambiguate most cases.
+ */
+ switch (peekNext())
+ {
+ case TOK.colon:
+ {
+ // It's a label
+ auto ident = token.ident;
+ nextToken(); // advance to `:`
+ nextToken(); // advance past `:`
+ if (token.value == TOK.rightCurly)
+ s = null;
+ else if (token.value == TOK.leftCurly)
+ s = cparseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
+ else
+ s = cparseStatement(ParseStatementFlags.semiOk);
+ s = new AST.LabelStatement(loc, ident, s);
+ break;
+ }
+
+ case TOK.dot:
+ case TOK.arrow:
+ case TOK.plusPlus:
+ case TOK.minusMinus:
+ case TOK.leftBracket:
+ case TOK.question:
+ case TOK.assign:
+ case TOK.addAssign:
+ case TOK.minAssign:
+ case TOK.mulAssign:
+ case TOK.divAssign:
+ case TOK.modAssign:
+ case TOK.andAssign:
+ case TOK.orAssign:
+ case TOK.xorAssign:
+ case TOK.leftShiftAssign:
+ case TOK.rightShiftAssign:
+ goto Lexp;
+
+ case TOK.leftParenthesis:
+ {
+ /* If tokens look like a function call, assume it is one,
+ * As any type-name won't be resolved until semantic, this
+ * could be rewritten later.
+ */
+ auto tk = &token;
+ if (isFunctionCall(tk))
+ goto Lexp;
+ goto default;
+ }
+
+ default:
+ {
+ /* If tokens look like a declaration, assume it is one
+ */
+ auto tk = &token;
+ if (isCDeclaration(tk))
+ goto Ldeclaration;
+ goto Lexp;
+ }
+ }
+ break;
+
+ case TOK.int32Literal:
+ case TOK.uns32Literal:
+ case TOK.int64Literal:
+ case TOK.uns64Literal:
+ case TOK.int128Literal:
+ case TOK.uns128Literal:
+ case TOK.float32Literal:
+ case TOK.float64Literal:
+ case TOK.float80Literal:
+ case TOK.imaginary32Literal:
+ case TOK.imaginary64Literal:
+ case TOK.imaginary80Literal:
+ case TOK.leftParenthesis:
+ case TOK.and:
+ case TOK.mul:
+ case TOK.min:
+ case TOK.add:
+ case TOK.tilde:
+ case TOK.not:
+ case TOK.plusPlus:
+ case TOK.minusMinus:
+ case TOK.sizeof_:
+ Lexp:
+ auto exp = cparseExpression();
+ if (token.value == TOK.identifier && exp.op == TOK.identifier)
+ {
+ error("found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
+ nextToken();
+ }
+ else
+ check(TOK.semicolon, "statement");
+ s = new AST.ExpStatement(loc, exp);
+ break;
+
+ // type-specifiers
+ case TOK.void_:
+ case TOK.char_:
+ case TOK.int16:
+ case TOK.int32:
+ case TOK.int64:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.signed:
+ case TOK.unsigned:
+ case TOK._Bool:
+ //case TOK._Imaginary:
+ case TOK._Complex:
+ case TOK.struct_:
+ case TOK.union_:
+ case TOK.enum_:
+
+ // storage-class-specifiers
+ case TOK.typedef_:
+ case TOK.extern_:
+ case TOK.static_:
+ case TOK._Thread_local:
+ case TOK.auto_:
+ case TOK.register:
+
+ // function-specifiers
+ case TOK.inline:
+ case TOK._Noreturn:
+
+ // type-qualifiers
+ case TOK.const_:
+ case TOK.volatile:
+ case TOK.restrict:
+
+ // alignment-specifier
+ case TOK._Alignas:
+
+ // atomic-type-specifier or type_qualifier
+ case TOK._Atomic:
+
+ Ldeclaration:
+ {
+ cparseDeclaration(LVL.local);
+ if (symbols.length > 1)
+ {
+ auto as = new AST.Statements();
+ as.reserve(symbols.length);
+ foreach (d; (*symbols)[])
+ {
+ s = new AST.ExpStatement(loc, d);
+ as.push(s);
+ }
+ s = new AST.CompoundDeclarationStatement(loc, as);
+ symbols.setDim(0);
+ }
+ else if (symbols.length == 1)
+ {
+ auto d = (*symbols)[0];
+ s = new AST.ExpStatement(loc, d);
+ symbols.setDim(0);
+ }
+ else
+ s = new AST.ExpStatement(loc, cast(AST.Expression)null);
+ if (flags & ParseStatementFlags.scope_)
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ break;
+ }
+
+ case TOK._Static_assert: // _Static_assert ( constant-expression, string-literal ) ;
+ s = new AST.StaticAssertStatement(cparseStaticAssert());
+ break;
+
+ case TOK.leftCurly:
+ {
+ /* C11 6.8.2
+ * compound-statement:
+ * { block-item-list (opt) }
+ *
+ * block-item-list:
+ * block-item
+ * block-item-list block-item
+ *
+ * block-item:
+ * declaration
+ * statement
+ */
+ nextToken();
+ auto statements = new AST.Statements();
+ while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
+ {
+ statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
+ }
+ if (endPtr)
+ *endPtr = token.ptr;
+ endloc = token.loc;
+ if (pEndloc)
+ {
+ *pEndloc = token.loc;
+ pEndloc = null; // don't set it again
+ }
+ s = new AST.CompoundStatement(loc, statements);
+ if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ check(TOK.rightCurly, "compound statement");
+ break;
+ }
+
+ case TOK.while_:
+ {
+ nextToken();
+ check(TOK.leftParenthesis);
+ auto condition = cparseExpression();
+ check(TOK.rightParenthesis);
+ Loc endloc;
+ auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
+ s = new AST.WhileStatement(loc, condition, _body, endloc, null);
+ break;
+ }
+
+ case TOK.semicolon:
+ /* C11 6.8.3 null statement
+ */
+ nextToken();
+ s = new AST.ExpStatement(loc, cast(AST.Expression)null);
+ break;
+
+ case TOK.do_:
+ {
+ nextToken();
+ auto _body = cparseStatement(ParseStatementFlags.scope_);
+ check(TOK.while_);
+ check(TOK.leftParenthesis);
+ auto condition = cparseExpression();
+ check(TOK.rightParenthesis);
+ check(TOK.semicolon, "terminating `;` required after do-while statement");
+ s = new AST.DoStatement(loc, _body, condition, token.loc);
+ break;
+ }
+
+ case TOK.for_:
+ {
+ AST.Statement _init;
+ AST.Expression condition;
+ AST.Expression increment;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ if (token.value == TOK.semicolon)
+ {
+ _init = null;
+ nextToken();
+ }
+ else
+ {
+ _init = cparseStatement(0);
+ }
+ if (token.value == TOK.semicolon)
+ {
+ condition = null;
+ nextToken();
+ }
+ else
+ {
+ condition = cparseExpression();
+ check(TOK.semicolon, "`for` condition");
+ }
+ if (token.value == TOK.rightParenthesis)
+ {
+ increment = null;
+ nextToken();
+ }
+ else
+ {
+ increment = cparseExpression();
+ check(TOK.rightParenthesis);
+ }
+ Loc endloc;
+ auto _body = cparseStatement(ParseStatementFlags.scope_, null, &endloc);
+ s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
+ break;
+ }
+
+ case TOK.if_:
+ {
+ nextToken();
+ check(TOK.leftParenthesis);
+ auto condition = cparseExpression();
+ check(TOK.rightParenthesis);
+ auto ifbody = cparseStatement(ParseStatementFlags.scope_);
+ AST.Statement elsebody;
+ if (token.value == TOK.else_)
+ {
+ nextToken();
+ elsebody = cparseStatement(ParseStatementFlags.scope_);
+ }
+ else
+ elsebody = null;
+ if (condition && ifbody)
+ s = new AST.IfStatement(loc, null, condition, ifbody, elsebody, token.loc);
+ else
+ s = null; // don't propagate parsing errors
+ break;
+ }
+
+ case TOK.else_:
+ error("found `else` without a corresponding `if` statement");
+ goto Lerror;
+
+ case TOK.switch_:
+ {
+ nextToken();
+ check(TOK.leftParenthesis);
+ auto condition = cparseExpression();
+ check(TOK.rightParenthesis);
+ auto _body = cparseStatement(ParseStatementFlags.scope_);
+ s = new AST.SwitchStatement(loc, condition, _body, false);
+ break;
+ }
+
+ case TOK.case_:
+ {
+
+ nextToken();
+ auto exp = cparseAssignExp();
+ check(TOK.colon);
+
+ if (flags & ParseStatementFlags.curlyScope)
+ {
+ auto statements = new AST.Statements();
+ while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
+ {
+ auto cur = cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
+ statements.push(cur);
+
+ // https://issues.dlang.org/show_bug.cgi?id=21739
+ // Stop at the last break s.t. the following non-case statements are
+ // not merged into the current case. This can happen for
+ // case 1: ... break;
+ // debug { case 2: ... }
+ if (cur && cur.isBreakStatement())
+ break;
+ }
+ s = new AST.CompoundStatement(loc, statements);
+ }
+ else
+ {
+ s = cparseStatement(ParseStatementFlags.semi);
+ }
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ s = new AST.CaseStatement(loc, exp, s);
+ break;
+ }
+
+ case TOK.default_:
+ {
+ nextToken();
+ check(TOK.colon);
+
+ if (flags & ParseStatementFlags.curlyScope)
+ {
+ auto statements = new AST.Statements();
+ while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
+ {
+ statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
+ }
+ s = new AST.CompoundStatement(loc, statements);
+ }
+ else
+ s = cparseStatement(ParseStatementFlags.semi);
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ s = new AST.DefaultStatement(loc, s);
+ break;
+ }
+
+ case TOK.return_:
+ {
+ /* return ;
+ * return expression ;
+ */
+ nextToken();
+ auto exp = token.value == TOK.semicolon ? null : cparseExpression();
+ check(TOK.semicolon, "`return` statement");
+ s = new AST.ReturnStatement(loc, exp);
+ break;
+ }
+
+ case TOK.break_:
+ nextToken();
+ check(TOK.semicolon, "`break` statement");
+ s = new AST.BreakStatement(loc, null);
+ break;
+
+ case TOK.continue_:
+ nextToken();
+ check(TOK.semicolon, "`continue` statement");
+ s = new AST.ContinueStatement(loc, null);
+ break;
+
+ case TOK.goto_:
+ {
+ Identifier ident;
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `goto`");
+ ident = null;
+ }
+ else
+ {
+ ident = token.ident;
+ nextToken();
+ }
+ s = new AST.GotoStatement(loc, ident);
+ check(TOK.semicolon, "`goto` statement");
+ break;
+ }
+
+ case TOK.asm_:
+ s = parseAsm();
+ break;
+
+ default:
+ error("found `%s` instead of statement", token.toChars());
+ goto Lerror;
+
+ Lerror:
+ panic();
+ if (token.value == TOK.semicolon)
+ nextToken();
+ s = null;
+ break;
+ }
+ if (pEndloc)
+ *pEndloc = prevloc;
+ symbols = symbolsSave;
+ return s;
+ }
+
+ //}
+ /*******************************************************************************/
+ /********************************* Expression Parser ***************************/
+ //{
+
+ /**************
+ * C11 6.5.17
+ * expression:
+ * assignment-expression
+ * expression , assignment-expression
+ */
+ AST.Expression cparseExpression()
+ {
+ auto loc = token.loc;
+
+ //printf("cparseExpression() loc = %d\n", loc.linnum);
+ auto e = cparseAssignExp();
+ while (token.value == TOK.comma)
+ {
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.CommaExp(loc, e, e2, false);
+ loc = token.loc;
+ }
+ return e;
+ }
+
+
+ /*********************
+ * C11 6.5.1
+ * primary-expression:
+ * identifier
+ * constant
+ * string-literal
+ * ( expression )
+ * generic-selection
+ */
+ AST.Expression cparsePrimaryExp()
+ {
+ AST.Expression e;
+ const loc = token.loc;
+
+ //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
+ switch (token.value)
+ {
+ case TOK.identifier:
+ if (token.ident is Id.__func__)
+ {
+ addFuncName = true; // implicitly declare __func__
+ }
+ e = new AST.IdentifierExp(loc, token.ident);
+ nextToken();
+ break;
+
+ case TOK.int32Literal:
+ e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
+ nextToken();
+ break;
+
+ case TOK.uns32Literal:
+ e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
+ nextToken();
+ break;
+
+ case TOK.int64Literal:
+ e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
+ nextToken();
+ break;
+
+ case TOK.uns64Literal:
+ e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
+ nextToken();
+ break;
+
+ case TOK.float32Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
+ nextToken();
+ break;
+
+ case TOK.float64Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
+ nextToken();
+ break;
+
+ case TOK.float80Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
+ nextToken();
+ break;
+
+ case TOK.imaginary32Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
+ nextToken();
+ break;
+
+ case TOK.imaginary64Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
+ nextToken();
+ break;
+
+ case TOK.imaginary80Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
+ nextToken();
+ break;
+
+ case TOK.string_:
+ {
+ // cat adjacent strings
+ auto s = token.ustring;
+ auto len = token.len;
+ auto postfix = token.postfix;
+ while (1)
+ {
+ nextToken();
+ if (token.value == TOK.string_)
+ {
+ if (token.postfix)
+ {
+ if (token.postfix != postfix)
+ error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
+ postfix = token.postfix;
+ }
+
+ const len1 = len;
+ const len2 = token.len;
+ len = len1 + len2;
+ auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
+ memcpy(s2, s, len1 * char.sizeof);
+ memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
+ s = s2;
+ }
+ else
+ break;
+ }
+ e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
+ break;
+ }
+
+ case TOK.leftParenthesis:
+ nextToken();
+ e = cparseExpression();
+ check(TOK.rightParenthesis);
+ break;
+
+ case TOK._Generic:
+ e = cparseGenericSelection();
+ break;
+
+ default:
+ error("expression expected, not `%s`", token.toChars());
+ // Anything for e, as long as it's not NULL
+ e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
+ nextToken();
+ break;
+ }
+ return e;
+ }
+
+ /*********************************
+ * C11 6.5.2
+ * postfix-expression:
+ * primary-expression
+ * postfix-expression [ expression ]
+ * postfix-expression ( argument-expression-list (opt) )
+ * postfix-expression . identifier
+ * postfix-expression -> identifier
+ * postfix-expression ++
+ * postfix-expression --
+ * ( type-name ) { initializer-list }
+ * ( type-name ) { initializer-list , }
+ *
+ * argument-expression-list:
+ * assignment-expression
+ * argument-expression-list , assignment-expression
+ */
+ private AST.Expression cparsePostfixExp(AST.Expression e)
+ {
+ e = cparsePrimaryExp();
+ return cparsePostfixOperators(e);
+ }
+
+ /********************************
+ * C11 6.5.2
+ * Parse a series of operators for a postfix expression after already parsing
+ * a primary-expression or compound literal expression.
+ * Params:
+ * e = parsed primary or compound literal expression
+ * Returns:
+ * parsed postfix expression
+ */
+ private AST.Expression cparsePostfixOperators(AST.Expression e)
+ {
+ while (1)
+ {
+ const loc = token.loc;
+ switch (token.value)
+ {
+ case TOK.dot:
+ case TOK.arrow:
+ nextToken();
+ if (token.value == TOK.identifier)
+ {
+ Identifier id = token.ident;
+ e = new AST.DotIdExp(loc, e, id);
+ break;
+ }
+ error("identifier expected following `.`, not `%s`", token.toChars());
+ break;
+
+ case TOK.plusPlus:
+ e = new AST.PostExp(TOK.plusPlus, loc, e);
+ break;
+
+ case TOK.minusMinus:
+ e = new AST.PostExp(TOK.minusMinus, loc, e);
+ break;
+
+ case TOK.leftParenthesis:
+ e = new AST.CallExp(loc, e, cparseArguments());
+ continue;
+
+ case TOK.leftBracket:
+ {
+ // array dereferences:
+ // array[index]
+ AST.Expression index;
+ auto arguments = new AST.Expressions();
+
+ inBrackets++;
+ nextToken();
+ index = cparseAssignExp();
+ arguments.push(index);
+ check(TOK.rightBracket);
+ inBrackets--;
+ e = new AST.ArrayExp(loc, e, arguments);
+ continue;
+ }
+ default:
+ return e;
+ }
+ nextToken();
+ }
+ }
+
+ /************************
+ * C11 6.5.3
+ * unary-expression:
+ * postfix-expression
+ * ++ unary-expression
+ * -- unary-expression
+ * unary-operator cast-expression
+ * sizeof unary-expression
+ * sizeof ( type-name )
+ * _Alignof ( type-name )
+ *
+ * unary-operator:
+ * & * + - ~ !
+ */
+ private AST.Expression cparseUnaryExp()
+ {
+ AST.Expression e;
+ const loc = token.loc;
+
+ switch (token.value)
+ {
+ case TOK.plusPlus:
+ nextToken();
+ // Parse `++` as an unary operator so that cast expressions only give
+ // an error for being non-lvalues.
+ e = cparseCastExp();
+ e = new AST.PreExp(TOK.prePlusPlus, loc, e);
+ break;
+
+ case TOK.minusMinus:
+ nextToken();
+ // Parse `--` as an unary operator, same as prefix increment.
+ e = cparseCastExp();
+ e = new AST.PreExp(TOK.preMinusMinus, loc, e);
+ break;
+
+ case TOK.and:
+ nextToken();
+ e = cparseCastExp();
+ e = new AST.AddrExp(loc, e);
+ break;
+
+ case TOK.mul:
+ nextToken();
+ e = cparseCastExp();
+ e = new AST.PtrExp(loc, e);
+ break;
+
+ case TOK.min:
+ nextToken();
+ e = cparseCastExp();
+ e = new AST.NegExp(loc, e);
+ break;
+
+ case TOK.add:
+ nextToken();
+ e = cparseCastExp();
+ e = new AST.UAddExp(loc, e);
+ break;
+
+ case TOK.not:
+ nextToken();
+ e = cparseCastExp();
+ e = new AST.NotExp(loc, e);
+ break;
+
+ case TOK.tilde:
+ nextToken();
+ e = cparseCastExp();
+ e = new AST.ComExp(loc, e);
+ break;
+
+ case TOK.sizeof_:
+ {
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ {
+ auto tk = peek(&token);
+ if (isTypeName(tk))
+ {
+ /* Expression may be either be requesting the sizeof a type-name
+ * or a compound literal, which requires checking whether
+ * the next token is leftCurly
+ */
+ nextToken();
+ auto t = cparseTypeName();
+ check(TOK.rightParenthesis);
+ if (token.value == TOK.leftCurly)
+ {
+ // ( type-name ) { initializer-list }
+ auto ci = cparseInitializer();
+ e = new AST.CompoundLiteralExp(loc, t, ci);
+ e = cparsePostfixOperators(e);
+ }
+ else
+ {
+ // ( type-name )
+ e = new AST.TypeExp(loc, t);
+ }
+ e = new AST.DotIdExp(loc, e, Id.__sizeof);
+ break;
+ }
+ }
+ e = cparseUnaryExp();
+ e = new AST.DotIdExp(loc, e, Id.__sizeof);
+ break;
+ }
+
+ case TOK._Alignof:
+ {
+ nextToken();
+ check(TOK.leftParenthesis);
+ auto t = cparseTypeName();
+ check(TOK.rightParenthesis);
+ e = new AST.TypeExp(loc, t);
+ e = new AST.DotIdExp(loc, e, Id.__xalignof);
+ break;
+ }
+
+ default:
+ e = cparsePostfixExp(e);
+ break;
+ }
+ assert(e);
+ return e;
+ }
+
+ /**************
+ * C11 6.5.4
+ * cast-expression
+ * unary-expression
+ * ( type-name ) cast-expression
+ */
+ private AST.Expression cparseCastExp()
+ {
+ if (token.value == TOK.leftParenthesis)
+ {
+ // If ( type-name )
+ auto pt = &token;
+ if (isCastExpression(pt))
+ {
+ // Expression may be either a cast or a compound literal, which
+ // requires checking whether the next token is leftCurly
+ const loc = token.loc;
+ nextToken();
+ auto t = cparseTypeName();
+ check(TOK.rightParenthesis);
+
+ if (token.value == TOK.leftCurly)
+ {
+ // C11 6.5.2.5 ( type-name ) { initializer-list }
+ auto ci = cparseInitializer();
+ auto ce = new AST.CompoundLiteralExp(loc, t, ci);
+ return cparsePostfixOperators(ce);
+ }
+ else
+ {
+ // ( type-name ) cast-expression
+ auto ce = cparseCastExp();
+ return new AST.CastExp(loc, ce, t);
+ }
+ }
+ }
+ return cparseUnaryExp();
+ }
+
+ /**************
+ * C11 6.5.5
+ * multiplicative-expression
+ * cast-expression
+ * multiplicative-expression * cast-expression
+ * multiplicative-expression / cast-expression
+ * multiplicative-expression % cast-expression
+ */
+ private AST.Expression cparseMulExp()
+ {
+ const loc = token.loc;
+ auto e = cparseCastExp();
+
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.mul:
+ nextToken();
+ auto e2 = cparseCastExp();
+ e = new AST.MulExp(loc, e, e2);
+ continue;
+
+ case TOK.div:
+ nextToken();
+ auto e2 = cparseCastExp();
+ e = new AST.DivExp(loc, e, e2);
+ continue;
+
+ case TOK.mod:
+ nextToken();
+ auto e2 = cparseCastExp();
+ e = new AST.ModExp(loc, e, e2);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.6
+ * additive-expression
+ * multiplicative-expression
+ * additive-expression + multiplicative-expression
+ * additive-expression - multiplicative-expression
+ */
+ private AST.Expression cparseAddExp()
+ {
+ const loc = token.loc;
+ auto e = cparseMulExp();
+
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.add:
+ nextToken();
+ auto e2 = cparseMulExp();
+ e = new AST.AddExp(loc, e, e2);
+ continue;
+
+ case TOK.min:
+ nextToken();
+ auto e2 = cparseMulExp();
+ e = new AST.MinExp(loc, e, e2);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.7
+ * shift-expression
+ * additive-expression
+ * shift-expression << additive-expression
+ * shift-expression >> additive-expression
+ */
+ private AST.Expression cparseShiftExp()
+ {
+ const loc = token.loc;
+ auto e = cparseAddExp();
+
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.leftShift:
+ nextToken();
+ auto e2 = cparseAddExp();
+ e = new AST.ShlExp(loc, e, e2);
+ continue;
+
+ case TOK.rightShift:
+ nextToken();
+ auto e2 = cparseAddExp();
+ e = new AST.ShrExp(loc, e, e2);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.8
+ * relational-expression
+ * shift-expression
+ * relational-expression < shift-expression
+ * relational-expression > shift-expression
+ * relational-expression <= shift-expression
+ * relational-expression >= shift-expression
+ */
+ private AST.Expression cparseRelationalExp()
+ {
+ const loc = token.loc;
+
+ auto e = cparseShiftExp();
+ TOK op = token.value;
+
+ switch (op)
+ {
+ case TOK.lessThan:
+ case TOK.lessOrEqual:
+ case TOK.greaterThan:
+ case TOK.greaterOrEqual:
+ nextToken();
+ auto e2 = cparseShiftExp();
+ e = new AST.CmpExp(op, loc, e, e2);
+ break;
+
+ default:
+ break;
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.9
+ * equality-expression
+ * relational-expression
+ * equality-expression == relational-expression
+ * equality-expression != relational-expression
+ */
+ private AST.Expression cparseEqualityExp()
+ {
+ const loc = token.loc;
+
+ auto e = cparseRelationalExp();
+ const TOK op = token.value;
+
+ switch (op)
+ {
+ case TOK.equal:
+ case TOK.notEqual:
+ nextToken();
+ auto e2 = cparseRelationalExp();
+ e = new AST.EqualExp(op, loc, e, e2);
+ break;
+
+ default:
+ break;
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.10
+ * AND-expression
+ * equality-expression
+ * AND-expression & equality-expression
+ */
+ private AST.Expression cparseAndExp()
+ {
+ Loc loc = token.loc;
+ auto e = cparseEqualityExp();
+ while (token.value == TOK.and)
+ {
+ nextToken();
+ auto e2 = cparseEqualityExp();
+ e = new AST.AndExp(loc, e, e2);
+ loc = token.loc;
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.11
+ * exclusive-OR-expression
+ * AND-expression
+ * exclusive-OR-expression ^ AND-expression
+ */
+ private AST.Expression cparseXorExp()
+ {
+ const loc = token.loc;
+
+ auto e = cparseAndExp();
+ while (token.value == TOK.xor)
+ {
+ nextToken();
+ auto e2 = cparseAndExp();
+ e = new AST.XorExp(loc, e, e2);
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.12
+ * inclusive-OR-expression
+ * exclusive-OR-expression
+ * inclusive-OR-expression | exclusive-OR-expression
+ */
+ private AST.Expression cparseOrExp()
+ {
+ const loc = token.loc;
+
+ auto e = cparseXorExp();
+ while (token.value == TOK.or)
+ {
+ nextToken();
+ auto e2 = cparseXorExp();
+ e = new AST.OrExp(loc, e, e2);
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.13
+ * logical-AND-expression
+ * inclusive-OR-expression
+ * logical-AND-expression && inclusive-OR-expression
+ */
+ private AST.Expression cparseAndAndExp()
+ {
+ const loc = token.loc;
+
+ auto e = cparseOrExp();
+ while (token.value == TOK.andAnd)
+ {
+ nextToken();
+ auto e2 = cparseOrExp();
+ e = new AST.LogicalExp(loc, TOK.andAnd, e, e2);
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.14
+ * logical-OR-expression
+ * logical-AND-expression
+ * logical-OR-expression || logical-AND-expression
+ */
+ private AST.Expression cparseOrOrExp()
+ {
+ const loc = token.loc;
+
+ auto e = cparseAndAndExp();
+ while (token.value == TOK.orOr)
+ {
+ nextToken();
+ auto e2 = cparseAndAndExp();
+ e = new AST.LogicalExp(loc, TOK.orOr, e, e2);
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.15
+ * conditional-expression:
+ * logical-OR-expression
+ * logical-OR-expression ? expression : conditional-expression
+ */
+ private AST.Expression cparseCondExp()
+ {
+ const loc = token.loc;
+
+ auto e = cparseOrOrExp();
+ if (token.value == TOK.question)
+ {
+ nextToken();
+ auto e1 = cparseExpression();
+ check(TOK.colon);
+ auto e2 = cparseCondExp();
+ e = new AST.CondExp(loc, e, e1, e2);
+ }
+ return e;
+ }
+
+ /**************
+ * C11 6.5.16
+ * assignment-expression:
+ * conditional-expression
+ * unary-expression assignment-operator assignment-expression
+ *
+ * assignment-operator:
+ * = *= /= %= += -= <<= >>= &= ^= |=
+ */
+ AST.Expression cparseAssignExp()
+ {
+ AST.Expression e;
+ e = cparseCondExp(); // constrain it to being unary-expression in semantic pass
+ if (e is null)
+ return e;
+
+ const loc = token.loc;
+ switch (token.value)
+ {
+ case TOK.assign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.AssignExp(loc, e, e2);
+ break;
+
+ case TOK.addAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.AddAssignExp(loc, e, e2);
+ break;
+
+ case TOK.minAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.MinAssignExp(loc, e, e2);
+ break;
+
+ case TOK.mulAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.MulAssignExp(loc, e, e2);
+ break;
+
+ case TOK.divAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.DivAssignExp(loc, e, e2);
+ break;
+
+ case TOK.modAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.ModAssignExp(loc, e, e2);
+ break;
+
+ case TOK.andAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.AndAssignExp(loc, e, e2);
+ break;
+
+ case TOK.orAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.OrAssignExp(loc, e, e2);
+ break;
+
+ case TOK.xorAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.XorAssignExp(loc, e, e2);
+ break;
+
+ case TOK.leftShiftAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.ShlAssignExp(loc, e, e2);
+ break;
+
+ case TOK.rightShiftAssign:
+ nextToken();
+ auto e2 = cparseAssignExp();
+ e = new AST.ShrAssignExp(loc, e, e2);
+ break;
+
+ default:
+ break;
+ }
+
+ return e;
+ }
+
+ /***********************
+ * C11 6.5.1.1
+ * _Generic ( assignment-expression, generic-assoc-list )
+ *
+ * generic-assoc-list:
+ * generic-association
+ * generic-assoc-list generic-association
+ *
+ * generic-association:
+ * type-name : assignment-expression
+ * default : assignment-expression
+ */
+ private AST.Expression cparseGenericSelection()
+ {
+ const loc = token.loc;
+ nextToken();
+ check(TOK.leftParenthesis);
+ auto cntlExp = cparseAssignExp();
+ check(TOK.comma);
+ auto types = new AST.Types();
+ auto exps = new AST.Expressions();
+ bool sawDefault;
+ while (1)
+ {
+ AST.Type t;
+ if (token.value == TOK.default_)
+ {
+ nextToken();
+ if (sawDefault)
+ error("only one `default` allowed in generic-assoc-list");
+ sawDefault = true;
+ t = null;
+ }
+ else
+ t = cparseTypeName();
+ types.push(t);
+
+ check(TOK.colon);
+ auto e = cparseAssignExp();
+ exps.push(e);
+ if (token.value == TOK.rightParenthesis || token.value == TOK.endOfFile)
+ break;
+ check(TOK.comma);
+ }
+ check(TOK.rightParenthesis);
+ return new AST.GenericExp(loc, cntlExp, types, exps);
+ }
+
+ /***********************
+ * C11 6.6 Constant expressions
+ * constant-expression:
+ * conditional-expression
+ */
+ private AST.Expression cparseConstantExp()
+ {
+ return cparseAssignExp();
+ }
+
+ //}
+ /********************************************************************************/
+ /********************************* Declaration Parser ***************************/
+ //{
+
+ /*************************************
+ * C11 6.7
+ * declaration:
+ * declaration-specifiers init-declarator-list (opt) ;
+ * static_assert-declaration
+ *
+ * init-declarator-list:
+ * init-declarator
+ * init-declarator-list , init-declarator
+ *
+ * init-declarator:
+ * declarator
+ * declarator = initializer
+ *
+ * Params:
+ * level = declaration context
+ */
+ void cparseDeclaration(LVL level)
+ {
+ //printf("cparseDeclaration(level = %d)\n", level);
+ if (token.value == TOK._Static_assert)
+ {
+ auto s = cparseStaticAssert();
+ symbols.push(s);
+ return;
+ }
+
+ auto symbolsSave = symbols;
+ Specifier specifier;
+ auto tspec = cparseDeclarationSpecifiers(level, specifier);
+
+ /* If a declarator does not follow, it is unnamed
+ */
+ if (token.value == TOK.semicolon && tspec)
+ {
+ nextToken();
+ auto tt = tspec.isTypeTag();
+ if (!tt || !tt.id)
+ return; // legal but meaningless empty declaration, ignore it
+
+ /* `struct tag;` and `struct tag { ... };`
+ * always result in a declaration in the current scope
+ */
+ auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) :
+ (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) :
+ new AST.EnumDeclaration(tt.loc, tt.id, AST.Type.tint32);
+ stag.members = tt.members;
+ if (!symbols)
+ symbols = new AST.Dsymbols();
+ auto stags = applySpecifier(stag, specifier);
+ symbols.push(stags);
+
+ if (tt.tok == TOK.enum_)
+ {
+ if (!tt.members)
+ error(tt.loc, "`enum %s` has no members", stag.toChars());
+ }
+ return;
+ }
+
+ if (tspec && specifier.mod & MOD.xconst)
+ {
+ tspec = toConst(tspec);
+ specifier.mod = MOD.xnone; // 'used' it
+ }
+
+ bool first = true;
+ while (1)
+ {
+ Identifier id;
+ AST.Expression asmname;
+ auto dt = cparseDeclarator(DTR.xdirect, tspec, id);
+ if (!dt)
+ {
+ panic();
+ nextToken();
+ break; // error recovery
+ }
+
+ /* GNU Extensions
+ * init-declarator:
+ * declarator simple-asm-expr (opt) gnu-attributes (opt)
+ * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer
+ */
+ switch (token.value)
+ {
+ case TOK.assign:
+ case TOK.comma:
+ case TOK.semicolon:
+ case TOK.asm_:
+ case TOK.__attribute__:
+ /* This is a data definition, there cannot now be a
+ * function definition.
+ */
+ first = false;
+ if (token.value == TOK.asm_)
+ asmname = cparseSimpleAsmExpr();
+ if (token.value == TOK.__attribute__)
+ {
+ cparseGnuAttributes(specifier);
+ if (token.value == TOK.leftCurly)
+ {
+ error("attributes should be specified before the function definition");
+ auto t = &token;
+ if (skipBraces(t))
+ {
+ token = *t;
+ return;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (specifier.alignExps && dt.isTypeFunction())
+ error("no alignment-specifier for function declaration"); // C11 6.7.5-2
+ if (specifier.alignExps && specifier.scw == SCW.xregister)
+ error("no alignment-specifier for `register` storage class"); // C11 6.7.5-2
+
+ /* C11 6.9.1 Function Definitions
+ * function-definition:
+ * declaration-specifiers declarator declaration-list (opt) compound-statement
+ *
+ * declaration-list:
+ * declaration
+ * declaration-list declaration
+ */
+ auto t = &token;
+ if (first && // first declarator
+ id &&
+ dt.isTypeFunction() && // function type not inherited from a typedef
+ isDeclarationList(t) && // optional declaration-list
+ level == LVL.global && // function definitions only at global scope
+ t.value == TOK.leftCurly) // start of compound-statement
+ {
+ auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier);
+ symbols = symbolsSave;
+ symbols.push(s);
+ return;
+ }
+ AST.Dsymbol s = null;
+ symbols = symbolsSave;
+ if (!symbols)
+ symbols = new AST.Dsymbols; // lazilly create it
+
+ if (level != LVL.global && !tspec && !specifier.scw && !specifier.mod)
+ error("declaration-specifier-seq required");
+ else if (specifier.scw == SCW.xtypedef)
+ {
+ if (token.value == TOK.assign)
+ error("no initializer for typedef declaration");
+ if (specifier.alignExps)
+ error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
+
+ bool isalias = true;
+ if (auto ts = dt.isTypeStruct())
+ {
+ if (ts.sym.isAnonymous())
+ {
+ // This is a typedef for an anonymous struct-or-union.
+ // Directly set the ident for the struct-or-union.
+ ts.sym.ident = id;
+ isalias = false;
+ }
+ }
+ else if (auto te = dt.isTypeEnum())
+ {
+ if (te.sym.isAnonymous())
+ {
+ // This is a typedef for an anonymous enum.
+ te.sym.ident = id;
+ isalias = false;
+ }
+ }
+ if (isalias)
+ s = new AST.AliasDeclaration(token.loc, id, dt);
+ }
+ else if (id)
+ {
+ if (level == LVL.prototype)
+ break; // declared later as Parameter, not VarDeclaration
+
+ if (dt.ty == AST.Tvoid)
+ error("`void` has no value");
+
+ AST.Initializer initializer;
+ bool hasInitializer;
+ if (token.value == TOK.assign)
+ {
+ nextToken();
+ hasInitializer = true;
+ initializer = cparseInitializer();
+ }
+ // declare the symbol
+ assert(id);
+ if (dt.isTypeFunction())
+ {
+ if (hasInitializer)
+ error("no initializer for function declaration");
+ if (specifier.scw & SCW.x_Thread_local)
+ error("functions cannot be `_Thread_local`"); // C11 6.7.1-4
+ auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, specifiersToSTC(level, specifier), dt, specifier.noreturn);
+ s = fd;
+ }
+ else
+ {
+ // Give non-extern variables an implicit void initializer
+ // if one has not been explicitly set.
+ if (!hasInitializer && !(specifier.scw & SCW.xextern))
+ initializer = new AST.VoidInitializer(token.loc);
+ s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier));
+ }
+ }
+ if (s !is null)
+ {
+ s = applySpecifier(s, specifier);
+ if (level == LVL.local)
+ {
+ // Wrap the declaration in `extern (C) { declaration }`
+ // Necessary for function pointers, but harmless to apply to all.
+ auto decls = new AST.Dsymbols(1);
+ (*decls)[0] = s;
+ s = new AST.LinkDeclaration(s.loc, linkage, decls);
+ }
+ // Saw `asm("name")` in the function, type, or variable definition.
+ // This maps directly to `pragma(mangle, "name")`
+ if (asmname)
+ {
+ auto args = new AST.Expressions(1);
+ (*args)[0] = asmname;
+ auto decls = new AST.Dsymbols(1);
+ (*decls)[0] = s;
+ s = new AST.PragmaDeclaration(asmname.loc, Id.mangle, args, decls);
+ }
+ symbols.push(s);
+ }
+ first = false;
+
+ switch (token.value)
+ {
+ case TOK.identifier:
+ error("missing comma");
+ goto default;
+
+ case TOK.semicolon:
+ nextToken();
+ return;
+
+ case TOK.comma:
+ nextToken();
+ break;
+
+ default:
+ error("`=`, `;` or `,` expected");
+ while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
+ nextToken();
+ nextToken();
+ return;
+ }
+ }
+ }
+
+ /***************************************
+ * C11 Function Definitions
+ * function-definition
+ * declaration-specifiers declarator declaration-list (opt) compound-statement
+ *
+ * declaration-list:
+ * declaration
+ * declaration-list declaration
+ *
+ * It's already been parsed up to the declaration-list (opt).
+ * Pick it up from there.
+ * Params:
+ * id = function identifier
+ * ft = function type
+ * specifier = function specifiers
+ * Returns:
+ * Dsymbol for the function
+ */
+ AST.Dsymbol cparseFunctionDefinition(Identifier id, AST.TypeFunction ft, ref Specifier specifier)
+ {
+ if (token.value != TOK.leftCurly) // if not start of a compound-statement
+ {
+ // Do declaration-list
+ do
+ {
+ cparseDeclaration(LVL.parameter);
+ } while (token.value != TOK.leftCurly);
+
+ /* Since there were declarations, the parameter-list must have been
+ * an identifier-list.
+ */
+ auto pl = ft.parameterList;
+ pl.hasIdentifierList = true; // semantic needs to know to adjust parameter types
+ if (pl.varargs != AST.VarArg.none)
+ error("function identifier-list cannot end with `...`");
+ auto plLength = pl.length;
+ if (symbols.length != plLength)
+ error("%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
+
+ /* Transfer the types and storage classes from symbols[] to pl[]
+ */
+ foreach (i; 0 .. plLength)
+ {
+ auto p = pl[i]; // yes, quadratic
+
+ // Convert typedef-identifier to identifier
+ if (p.type)
+ {
+ if (auto t = p.type.isTypeIdentifier())
+ {
+ p.ident = t.ident;
+ p.type = null;
+ }
+ }
+
+ if (p.type || !(p.storageClass & STC.parameter))
+ error("storage class and type are not allowed in identifier-list");
+ foreach (s; (*symbols)[]) // yes, quadratic
+ {
+ auto d = s.isDeclaration();
+ if (d && p.ident == d.ident && d.type)
+ {
+ p.type = d.type;
+ p.storageClass = d.storage_class;
+ d.type = null; // don't reuse
+ break;
+ }
+ }
+ if (!p.type)
+ error("no declaration for identifier `%s`", p.ident.toChars());
+ }
+ }
+
+ addFuncName = false; // gets set to true if somebody references __func__ in this function
+ const locFunc = token.loc;
+
+ auto body = cparseStatement(ParseStatementFlags.curly); // don't start a new scope; continue with parameter scope
+ auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, specifiersToSTC(LVL.global, specifier), ft, specifier.noreturn);
+
+ if (addFuncName)
+ {
+ auto s = createFuncName(locFunc, id);
+ body = new AST.CompoundStatement(locFunc, s, body);
+ }
+ fd.fbody = body;
+
+ // TODO add `symbols` to the function's local symbol table `sc2` in FuncDeclaration::semantic3()
+
+ return fd;
+ }
+
+ /***************************************
+ * C11 Initialization
+ * initializer:
+ * assignment-expression
+ * { initializer-list }
+ * { initializer-list , }
+ *
+ * initializer-list:
+ * designation (opt) initializer
+ * initializer-list , designation (opt) initializer
+ *
+ * designation:
+ * designator-list =
+ *
+ * designator-list:
+ * designator
+ * designator-list designator
+ *
+ * designator:
+ * [ constant-expression ]
+ * . identifier
+ * Returns:
+ * initializer
+ */
+ AST.Initializer cparseInitializer()
+ {
+ if (token.value != TOK.leftCurly)
+ {
+ auto ae = cparseAssignExp(); // assignment-expression
+ return new AST.ExpInitializer(token.loc, ae);
+ }
+ nextToken();
+ const loc = token.loc;
+
+ /* Collect one or more `designation (opt) initializer`
+ * into ci.initializerList, but lazily create ci
+ */
+ AST.CInitializer ci;
+ while (1)
+ {
+ /* There can be 0 or more designators preceding an initializer.
+ * Collect them in desigInit
+ */
+ AST.DesigInit desigInit;
+ while (1)
+ {
+ if (token.value == TOK.leftBracket) // [ constant-expression ]
+ {
+ nextToken();
+ auto e = cparseConstantExp();
+ check(TOK.rightBracket);
+ if (!desigInit.designatorList)
+ desigInit.designatorList = new AST.Designators;
+ desigInit.designatorList.push(AST.Designator(e));
+ }
+ else if (token.value == TOK.dot) // . identifier
+ {
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `.` designator");
+ break;
+ }
+ if (!desigInit.designatorList)
+ desigInit.designatorList = new AST.Designators;
+ desigInit.designatorList.push(AST.Designator(token.ident));
+ nextToken();
+ }
+ else
+ {
+ if (desigInit.designatorList)
+ check(TOK.assign);
+ break;
+ }
+ }
+
+ desigInit.initializer = cparseInitializer();
+ if (!ci)
+ ci = new AST.CInitializer(loc);
+ ci.initializerList.push(desigInit);
+ if (token.value == TOK.comma)
+ {
+ nextToken();
+ if (token.value != TOK.rightCurly)
+ continue;
+ }
+ break;
+ }
+ check(TOK.rightCurly);
+ //printf("ci: %s\n", ci.toChars());
+ return ci;
+ }
+
+ /*************************************
+ * C11 6.7
+ * declaration-specifier:
+ * storage-class-specifier declaration-specifiers (opt)
+ * type-specifier declaration-specifiers (opt)
+ * type-qualifier declaration-specifiers (opt)
+ * function-specifier declaration-specifiers (opt)
+ * alignment-specifier declaration-specifiers (opt)
+ * Params:
+ * level = declaration context
+ * specifier = specifiers in and out
+ * Returns:
+ * resulting type, null if not specified
+ */
+ private AST.Type cparseDeclarationSpecifiers(LVL level, ref Specifier specifier)
+ {
+ enum TKW : uint
+ {
+ xnone = 0,
+ xchar = 1,
+ xsigned = 2,
+ xunsigned = 4,
+ xshort = 8,
+ xint = 0x10,
+ xlong = 0x20,
+ xllong = 0x40,
+ xfloat = 0x80,
+ xdouble = 0x100,
+ xldouble = 0x200,
+ xtag = 0x400,
+ xident = 0x800,
+ xvoid = 0x1000,
+ xbool = 0x4000,
+ ximaginary = 0x8000,
+ xcomplex = 0x10000,
+ x_Atomic = 0x20000,
+ }
+
+ AST.Type t;
+ Loc loc;
+ //printf("parseDeclarationSpecifiers()\n");
+
+ TKW tkw;
+ SCW scw = specifier.scw & SCW.xtypedef;
+ MOD mod;
+ Identifier id;
+ Identifier previd;
+
+ Lwhile:
+ while (1)
+ {
+ //printf("token %s\n", token.toChars());
+ TKW tkwx;
+ SCW scwx;
+ MOD modx;
+ switch (token.value)
+ {
+ // Storage class specifiers
+ case TOK.static_: scwx = SCW.xstatic; break;
+ case TOK.extern_: scwx = SCW.xextern; break;
+ case TOK.auto_: scwx = SCW.xauto; break;
+ case TOK.register: scwx = SCW.xregister; break;
+ case TOK.typedef_: scwx = SCW.xtypedef; break;
+ case TOK.inline: scwx = SCW.xinline; break;
+ case TOK._Noreturn: scwx = SCW.x_Noreturn; break;
+ case TOK._Thread_local: scwx = SCW.x_Thread_local; break;
+
+ // Type qualifiers
+ case TOK.const_: modx = MOD.xconst; break;
+ case TOK.volatile: modx = MOD.xvolatile; break;
+ case TOK.restrict: modx = MOD.xrestrict; break;
+
+ // Type specifiers
+ case TOK.char_: tkwx = TKW.xchar; break;
+ case TOK.signed: tkwx = TKW.xsigned; break;
+ case TOK.unsigned: tkwx = TKW.xunsigned; break;
+ case TOK.int16: tkwx = TKW.xshort; break;
+ case TOK.int32: tkwx = TKW.xint; break;
+ case TOK.int64: tkwx = TKW.xlong; break;
+ case TOK.float32: tkwx = TKW.xfloat; break;
+ case TOK.float64: tkwx = TKW.xdouble; break;
+ case TOK.void_: tkwx = TKW.xvoid; break;
+ case TOK._Bool: tkwx = TKW.xbool; break;
+ case TOK._Imaginary: tkwx = TKW.ximaginary; break;
+ case TOK._Complex: tkwx = TKW.xcomplex; break;
+
+ case TOK.identifier:
+ tkwx = TKW.xident;
+ id = token.ident;
+ break;
+
+ case TOK.struct_:
+ case TOK.union_:
+ {
+ const structOrUnion = token.value;
+ const sloc = token.loc;
+ nextToken();
+
+ /* GNU Extensions
+ * struct-or-union-specifier:
+ * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt)
+ * struct-or-union gnu-attribute (opt) identifier
+ */
+ if (token.value == TOK.__attribute__)
+ cparseGnuAttributes(specifier);
+
+ t = cparseStruct(sloc, structOrUnion, symbols);
+ tkwx = TKW.xtag;
+ break;
+ }
+
+ case TOK.enum_:
+ t = cparseEnum(symbols);
+ tkwx = TKW.xtag;
+ break;
+
+ case TOK._Atomic:
+ {
+ // C11 6.7.2.4
+ // type-specifier if followed by `( type-name )`
+ auto tk = peek(&token);
+ if (tk.value == TOK.leftParenthesis)
+ {
+ tk = peek(tk);
+ if (isTypeName(tk) && tk.value == TOK.rightParenthesis)
+ {
+ nextToken();
+ t = cparseTypeName();
+ // TODO - implement the "atomic" part of t
+ tkwx = TKW.x_Atomic;
+ break;
+ }
+ }
+ // C11 6.7.3 type-qualifier if not
+ modx = MOD.x_Atomic;
+ break;
+ }
+
+ case TOK._Alignas:
+ {
+ /* C11 6.7.5
+ * _Alignas ( type-name )
+ * _Alignas ( constant-expression )
+ */
+
+ if (level & (LVL.parameter | LVL.prototype))
+ error("no alignment-specifier for parameters"); // C11 6.7.5-2
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ AST.Expression exp;
+ auto tk = &token;
+ if (isTypeName(tk)) // _Alignas ( type-name )
+ {
+ auto talign = cparseTypeName();
+ /* Convert type to expression: `talign.alignof`
+ */
+ auto e = new AST.TypeExp(loc, talign);
+ exp = new AST.DotIdExp(loc, e, Id.__xalignof);
+ }
+ else // _Alignas ( constant-expression )
+ {
+ exp = cparseConstantExp();
+ }
+
+ if (!specifier.alignExps)
+ specifier.alignExps = new AST.Expressions(0);
+ specifier.alignExps.push(exp);
+
+ check(TOK.rightParenthesis);
+ break;
+ }
+
+ case TOK.__attribute__:
+ {
+ /* GNU Extensions
+ * declaration-specifiers:
+ * gnu-attributes declaration-specifiers (opt)
+ */
+ cparseGnuAttributes(specifier);
+ break;
+ }
+
+ default:
+ break Lwhile;
+ }
+
+ if (tkwx)
+ {
+ if (tkw & TKW.xlong && tkwx & TKW.xlong)
+ {
+ tkw &= ~TKW.xlong;
+ tkwx = TKW.xllong;
+ }
+ if (tkw && tkwx & TKW.xident)
+ {
+ // 2nd identifier can't be a typedef
+ break Lwhile; // leave parser on the identifier for the following declarator
+ }
+ else if (tkwx & TKW.xident)
+ {
+ // 1st identifier, save it for TypeIdentifier
+ previd = id;
+ }
+ if (tkw & TKW.xident && tkwx || // typedef-name followed by type-specifier
+ tkw & tkwx) // duplicate type-specifiers
+ {
+ error("illegal combination of type specifiers");
+ tkwx = TKW.init;
+ }
+ tkw |= tkwx;
+ if (!(tkwx & TKW.xtag)) // if parser already advanced
+ nextToken();
+ continue;
+ }
+
+ if (modx)
+ {
+ mod |= modx;
+ nextToken();
+ continue;
+ }
+
+ if (scwx)
+ {
+ if (scw & scwx)
+ error("duplicate storage class");
+ scw |= scwx;
+ const scw2 = scw & (SCW.xstatic | SCW.xextern | SCW.xauto | SCW.xregister | SCW.xtypedef);
+ if (scw2 & (scw2 - 1) ||
+ scw & (SCW.xauto | SCW.xregister) && scw & (SCW.xinline | SCW.x_Noreturn))
+ {
+ error("conflicting storage class");
+ scw &= ~scwx;
+ }
+ if (level & (LVL.parameter | LVL.prototype) &&
+ scw & ~SCW.xregister)
+ {
+ error("only `register` storage class allowed for function parameters");
+ scw &= ~scwx;
+ }
+ if (level == LVL.global &&
+ scw & (SCW.xauto | SCW.xregister))
+ {
+ error("`auto` and `register` storage class not allowed for global");
+ scw &= ~scwx;
+ }
+ nextToken();
+ continue;
+ }
+ }
+
+ specifier.scw = scw;
+ specifier.mod = mod;
+
+ // Convert TKW bits to type t
+ switch (tkw)
+ {
+ case TKW.xnone: t = null; break;
+
+ case TKW.xchar: t = AST.Type.tchar; break;
+ case TKW.xsigned | TKW.xchar: t = AST.Type.tint8; break;
+ case TKW.xunsigned | TKW.xchar: t = AST.Type.tuns8; break;
+
+ case TKW.xshort:
+ case TKW.xsigned | TKW.xshort:
+ case TKW.xsigned | TKW.xshort | TKW.xint:
+ case TKW.xshort | TKW.xint: t = AST.Type.tint16; break;
+
+ case TKW.xunsigned | TKW.xshort | TKW.xint:
+ case TKW.xunsigned | TKW.xshort: t = AST.Type.tuns16; break;
+
+ case TKW.xint:
+ case TKW.xsigned:
+ case TKW.xsigned | TKW.xint: t = AST.Type.tint32; break;
+
+ case TKW.xunsigned:
+ case TKW.xunsigned | TKW.xint: t = AST.Type.tuns32; break;
+
+ case TKW.xlong:
+ case TKW.xsigned | TKW.xlong:
+ case TKW.xsigned | TKW.xlong | TKW.xint:
+ case TKW.xlong | TKW.xint: t = longsize == 4 ? AST.Type.tint32 : AST.Type.tint64; break;
+
+ case TKW.xunsigned | TKW.xlong | TKW.xint:
+ case TKW.xunsigned | TKW.xlong: t = longsize == 4 ? AST.Type.tuns32 : AST.Type.tuns64; break;
+
+ case TKW.xllong:
+ case TKW.xsigned | TKW.xllong:
+ case TKW.xsigned | TKW.xllong | TKW.xint:
+ case TKW.xllong | TKW.xint: t = AST.Type.tint64; break;
+
+ case TKW.xunsigned | TKW.xllong | TKW.xint:
+ case TKW.xunsigned | TKW.xllong: t = AST.Type.tuns64; break;
+
+ case TKW.xvoid: t = AST.Type.tvoid; break;
+ case TKW.xbool: t = AST.Type.tbool; break;
+
+ case TKW.xfloat: t = AST.Type.tfloat32; break;
+ case TKW.xdouble: t = AST.Type.tfloat64; break;
+ case TKW.xlong | TKW.xdouble: t = realType(RTFlags.realfloat); break;
+
+ case TKW.ximaginary | TKW.xfloat: t = AST.Type.timaginary32; break;
+ case TKW.ximaginary | TKW.xdouble: t = AST.Type.timaginary64; break;
+ case TKW.ximaginary | TKW.xlong | TKW.xdouble: t = realType(RTFlags.imaginary); break;
+
+ case TKW.xcomplex | TKW.xfloat: t = AST.Type.tcomplex32; break;
+ case TKW.xcomplex | TKW.xdouble: t = AST.Type.tcomplex64; break;
+ case TKW.xcomplex | TKW.xlong | TKW.xdouble: t = realType(RTFlags.complex); break;
+
+ case TKW.xident: t = new AST.TypeIdentifier(loc, previd);
+ break;
+
+ case TKW.xtag:
+ break; // t is already set
+
+ default:
+ error("illegal type combination");
+ t = AST.Type.terror;
+ break;
+ }
+
+ return t;
+ }
+
+ /********************************
+ * C11 6.7.6
+ * Parse a declarator (including function definitions).
+ * declarator:
+ * pointer (opt) direct-declarator
+ *
+ * direct-declarator :
+ * identifier
+ * ( declarator )
+ * direct-declarator [ type-qualifier-list (opt) assignment-expression (opt) ]
+ * direct-declarator [ static type-qualifier-list (opt) assignment-expression ]
+ * direct-declarator [ type-qualifier-list static assignment-expression (opt) ]
+ * direct-declarator [ type-qualifier-list (opt) * ]
+ * direct-declarator ( parameter-type-list )
+ * direct-declarator ( identifier-list (opt) )
+ *
+ * pointer :
+ * * type-qualifier-list (opt)
+ * * type-qualifier-list (opt) pointer
+ *
+ * type-qualifier-list :
+ * type-qualifier
+ * type-qualifier-list type-qualifier
+ *
+ * parameter-type-list :
+ * parameter-list
+ * parameter-list , ...
+ *
+ * parameter-list :
+ * parameter-declaration
+ * parameter-list , parameter-declaration
+ *
+ * parameter-declaration :
+ * declaration-specifiers declarator
+ * declaration-specifiers abstract-declarator (opt)
+ *
+ * identifier-list :
+ * identifier
+ * identifier-list , identifier
+ *
+ * Params:
+ * declarator = declarator kind
+ * t = base type to start with
+ * pident = set to Identifier if there is one, null if not
+ * storageClass = any storage classes seen so far that apply to a function
+ * Returns:
+ * type declared. If a TypeFunction is returned, this.symbols is the
+ * symbol table for the parameter-type-list, which will contain any
+ * declared struct, union or enum tags.
+ */
+ private AST.Type cparseDeclarator(DTR declarator, AST.Type t,
+ out Identifier pident, StorageClass storageClass = 0)
+ {
+ //printf("cparseDeclarator(%d)\n", declarator);
+ AST.Types constTypes; // all the Types that will need `const` applied to them
+ constTypes.setDim(0);
+
+ AST.Type parseDecl(AST.Type t)
+ {
+ AST.Type ts;
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.identifier: // identifier
+ //printf("identifier %s\n", token.ident.toChars());
+ if (declarator == DTR.xabstract)
+ error("identifier not allowed in abstract-declarator");
+ pident = token.ident;
+ ts = t;
+ nextToken();
+ break;
+
+ case TOK.leftParenthesis: // ( declarator )
+ /* like: T (*fp)();
+ * T ((*fp))();
+ */
+ nextToken();
+ ts = parseDecl(t);
+ check(TOK.rightParenthesis);
+ break;
+
+ case TOK.mul: // pointer
+ t = new AST.TypePointer(t);
+ nextToken();
+ // add post fixes const/volatile/restrict/_Atomic
+ const mod = cparseTypeQualifierList();
+ if (mod & MOD.xconst)
+ constTypes.push(t);
+ continue;
+
+ default:
+ if (declarator == DTR.xdirect)
+ {
+ error("identifier or `(` expected"); // )
+ panic();
+ }
+ ts = t;
+ break;
+ }
+ break;
+ }
+
+ // parse DeclaratorSuffixes
+ while (1)
+ {
+ /* Insert tx -> t into
+ * ts -> ... -> t
+ * so that
+ * ts -> ... -> tx -> t
+ */
+ static void insertTx(ref AST.Type ts, AST.Type tx, AST.Type t)
+ {
+ AST.Type* pt;
+ for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
+ {
+ }
+ *pt = tx;
+ }
+
+ switch (token.value)
+ {
+ case TOK.leftBracket:
+ {
+ // post [] syntax, pick up any leading type qualifiers, `static` and `*`
+ AST.Type ta;
+ nextToken();
+
+ auto mod = cparseTypeQualifierList(); // const/volatile/restrict/_Atomic
+
+ bool isStatic;
+ bool isVLA;
+ if (token.value == TOK.static_)
+ {
+ isStatic = true; // `static`
+ nextToken();
+ if (!mod) // type qualifiers after `static`
+ mod = cparseTypeQualifierList();
+ }
+ else if (token.value == TOK.mul)
+ {
+ if (peekNext() == TOK.rightBracket)
+ {
+ isVLA = true; // `*`
+ nextToken();
+ }
+ }
+
+ if (isStatic || token.value != TOK.rightBracket)
+ {
+ //printf("It's a static array\n");
+ AST.Expression e = cparseAssignExp(); // [ expression ]
+ ta = new AST.TypeSArray(t, e);
+ }
+ else
+ {
+ // An array of unknown size, fake it with a DArray
+ ta = new AST.TypeDArray(t); // []
+ }
+ check(TOK.rightBracket);
+
+ // Issue errors for unsupported types.
+ if (isVLA) // C11 6.7.6.2
+ {
+ error("variable length arrays are not supported");
+ }
+ if (isStatic) // C11 6.7.6.3
+ {
+ error("static array parameters are not supported");
+ }
+ if (declarator != DTR.xparameter)
+ {
+ /* C11 6.7.6.2-4: '*' can only be used with function prototype scope.
+ */
+ if (isVLA)
+ error("variable length array used outside of function prototype");
+ /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
+ * in a declaration of a function parameter with an array type.
+ */
+ if (isStatic || mod)
+ error("static or type qualifier used outside of function prototype");
+ }
+ if (ts.isTypeSArray() || ts.isTypeDArray())
+ {
+ /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear
+ * in the outermost array type derivation.
+ */
+ if (isStatic || mod)
+ error("static or type qualifier used in non-outermost array type derivation");
+ /* C11 6.7.6.2-1: the element type shall not be an incomplete or
+ * function type.
+ */
+ if (ta.isTypeDArray() && !isVLA)
+ error("array type has incomplete element type `%s`", ta.toChars());
+ }
+
+ // Apply type qualifiers to the constructed type.
+ if (mod & MOD.xconst) // ignore the other bits
+ ta = toConst(ta);
+ insertTx(ts, ta, t); // ts -> ... -> ta -> t
+ continue;
+ }
+
+ case TOK.leftParenthesis:
+ {
+ // New symbol table for parameter-list
+ auto symbolsSave = this.symbols;
+ this.symbols = null;
+
+ auto parameterList = cparseParameterList();
+ AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, 0);
+ // tf = tf.addSTC(storageClass); // TODO
+ insertTx(ts, tf, t); // ts -> ... -> tf -> t
+
+ if (ts != tf)
+ this.symbols = symbolsSave;
+ break;
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
+ return ts;
+ }
+
+ t = parseDecl(t);
+
+ /* Because const is transitive, cannot assemble types from
+ * fragments. Instead, types to be annotated with const are put
+ * in constTypes[], and a bottom up scan of t is done to apply
+ * const
+ */
+ if (constTypes.length)
+ {
+ AST.Type constApply(AST.Type t)
+ {
+ if (t.nextOf())
+ {
+ auto tn = cast(AST.TypeNext)t; // t.nextOf() should return a ref instead of this
+ tn.next = constApply(tn.next);
+ }
+ foreach (tc; constTypes[])
+ {
+ if (tc is t)
+ {
+ return toConst(t);
+ }
+ }
+ return t;
+ }
+
+ t = constApply(t);
+ }
+
+ //printf("result: %s\n", t.toChars());
+ return t;
+ }
+
+ /******************************
+ * C11 6.7.3
+ * type-qualifier:
+ * const
+ * restrict
+ * volatile
+ * _Atomic
+ */
+ MOD cparseTypeQualifierList()
+ {
+ MOD mod;
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.const_: mod |= MOD.xconst; break;
+ case TOK.volatile: mod |= MOD.xvolatile; break;
+ case TOK.restrict: mod |= MOD.xrestrict; break;
+ case TOK._Atomic: mod |= MOD.x_Atomic; break;
+
+ default:
+ return mod;
+ }
+ nextToken();
+ }
+ }
+
+ /***********************************
+ * C11 6.7.7
+ */
+ AST.Type cparseTypeName()
+ {
+ Specifier specifier;
+ auto tspec = cparseSpecifierQualifierList(LVL.global, specifier);
+ Identifier id;
+ return cparseDeclarator(DTR.xabstract, tspec, id);
+ }
+
+ /***********************************
+ * C11 6.7.2.1
+ * specifier-qualifier-list:
+ * type-specifier specifier-qualifier-list (opt)
+ * type-qualifier specifier-qualifier-list (opt)
+ * Params:
+ * level = declaration context
+ * specifier = specifiers in and out
+ * Returns:
+ * resulting type, null if not specified
+ */
+ AST.Type cparseSpecifierQualifierList(LVL level, ref Specifier specifier)
+ {
+ auto t = cparseDeclarationSpecifiers(level, specifier);
+ if (specifier.scw)
+ error("storage class not allowed in specifier-qualified-list");
+ return t;
+ }
+
+ /***********************************
+ * C11 6.7.6.3
+ * ( parameter-type-list )
+ * ( identifier-list (opt) )
+ */
+ AST.ParameterList cparseParameterList()
+ {
+ auto parameters = new AST.Parameters();
+ AST.VarArg varargs = AST.VarArg.none;
+ StorageClass varargsStc;
+
+ check(TOK.leftParenthesis);
+ if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis)
+ {
+ nextToken();
+ nextToken();
+ return AST.ParameterList(parameters, varargs, varargsStc);
+ }
+
+ /* The check for identifier-list comes later,
+ * when doing the trailing declaration-list (opt)
+ */
+ while (1)
+ {
+ if (token.value == TOK.rightParenthesis)
+ break;
+ if (token.value == TOK.dotDotDot)
+ {
+ varargs = AST.VarArg.variadic; // C-style variadics
+ nextToken();
+ check(TOK.rightParenthesis);
+ return AST.ParameterList(parameters, varargs, varargsStc);
+ }
+
+ Specifier specifier;
+ auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
+
+ Identifier id;
+ auto t = cparseDeclarator(DTR.xparameter, tspec, id);
+ if (specifier.mod & MOD.xconst)
+ t = toConst(t);
+ auto param = new AST.Parameter(STC.parameter, t, id, null, null);
+ parameters.push(param);
+ if (token.value == TOK.rightParenthesis)
+ break;
+ check(TOK.comma);
+ }
+ nextToken();
+ return AST.ParameterList(parameters, varargs, varargsStc);
+ }
+
+ /***********************************
+ * C11 6.7.10
+ * _Static_assert ( constant-expression , string-literal ) ;
+ */
+ private AST.StaticAssert cparseStaticAssert()
+ {
+ const loc = token.loc;
+
+ //printf("cparseStaticAssert()\n");
+ nextToken();
+ check(TOK.leftParenthesis);
+ auto exp = cparseConstantExp();
+ check(TOK.comma);
+ if (token.value != TOK.string_)
+ error("string literal expected");
+ auto msg = cparsePrimaryExp();
+ check(TOK.rightParenthesis);
+ check(TOK.semicolon);
+ return new AST.StaticAssert(loc, exp, msg);
+ }
+
+ /*************************
+ * Collect argument list.
+ * Parser is on opening parenthesis.
+ * Returns:
+ * the arguments
+ */
+ private AST.Expressions* cparseArguments()
+ {
+ nextToken();
+ auto arguments = new AST.Expressions();
+ while (token.value != TOK.rightParenthesis && token.value != TOK.endOfFile)
+ {
+ auto arg = cparseAssignExp();
+ arguments.push(arg);
+ if (token.value != TOK.comma)
+ break;
+
+ nextToken(); // consume comma
+ }
+
+ check(TOK.rightParenthesis);
+
+ return arguments;
+ }
+
+ /*************************
+ * __declspec parser
+ * https://docs.microsoft.com/en-us/cpp/cpp/declspec
+ * decl-specifier:
+ * __declspec ( extended-decl-modifier-seq )
+ *
+ * extended-decl-modifier-seq:
+ * extended-decl-modifier (opt)
+ * extended-decl-modifier extended-decl-modifier-seq
+ *
+ * extended-decl-modifier:
+ * dllimport
+ * dllexport
+ */
+ private void cparseDeclspec()
+ {
+ /* Check for dllexport, dllimport
+ * Ignore the rest
+ */
+ bool dllimport; // TODO implement
+ bool dllexport; // TODO implement
+ nextToken(); // move past __declspec
+ check(TOK.leftParenthesis);
+ while (1)
+ {
+ if (token.value == TOK.rightParenthesis)
+ {
+ nextToken();
+ break;
+ }
+ else if (token.value == TOK.endOfFile)
+ break;
+ else if (token.value == TOK.identifier)
+ {
+ if (token.ident == Id.dllimport)
+ {
+ dllimport = true;
+ nextToken();
+ }
+ else if (token.ident == Id.dllexport)
+ {
+ dllexport = true;
+ nextToken();
+ }
+ else
+ {
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ cparseParens();
+ }
+ }
+ else
+ {
+ error("extended-decl-modifier expected");
+ }
+ break;
+ }
+ }
+
+ /*************************
+ * Simple asm parser
+ * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html
+ * simple-asm-expr:
+ * asm ( asm-string-literal )
+ *
+ * asm-string-literal:
+ * string-literal
+ */
+ private AST.Expression cparseSimpleAsmExpr()
+ {
+ nextToken(); // move past asm
+ check(TOK.leftParenthesis);
+ if (token.value != TOK.string_)
+ error("string literal expected");
+ auto label = cparsePrimaryExp();
+ check(TOK.rightParenthesis);
+ return label;
+ }
+
+ /*************************
+ * __attribute__ parser
+ * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
+ * gnu-attributes:
+ * gnu-attributes gnu-attribute-specifier
+ *
+ * gnu-attribute-specifier:
+ * __attribute__ (( gnu-attribute-list ))
+ *
+ * gnu-attribute-list:
+ * gnu-attribute (opt)
+ * gnu-attribute-list , gnu-attribute
+ *
+ * Params:
+ * specifier = filled in with the attribute(s)
+ */
+ private void cparseGnuAttributes(ref Specifier specifier)
+ {
+ while (token.value == TOK.__attribute__)
+ {
+ nextToken(); // move past __attribute__
+ check(TOK.leftParenthesis);
+ check(TOK.leftParenthesis);
+
+ if (token.value != TOK.rightParenthesis)
+ {
+ while (1)
+ {
+ cparseGnuAttribute(specifier);
+ if (token.value != TOK.comma)
+ break;
+ nextToken();
+ }
+ }
+
+ check(TOK.rightParenthesis);
+ check(TOK.rightParenthesis);
+ }
+ }
+
+ /*************************
+ * Parse a single GNU attribute
+ * gnu-attribute:
+ * gnu-attribute-name
+ * gnu-attribute-name ( identifier )
+ * gnu-attribute-name ( identifier , expression-list )
+ * gnu-attribute-name ( expression-list (opt) )
+ *
+ * gnu-attribute-name:
+ * keyword
+ * identifier
+ *
+ * expression-list:
+ * constant-expression
+ * expression-list , constant-expression
+ *
+ * Params:
+ * specifier = filled in with the attribute(s)
+ */
+ private void cparseGnuAttribute(ref Specifier specifier)
+ {
+ /* Check for dllimport, dllexport, vector_size(bytes)
+ * Ignore the rest
+ */
+ bool dllimport; // TODO implement
+ bool dllexport; // TODO implement
+
+ if (!isGnuAttributeName())
+ return;
+
+ if (token.value == TOK.identifier)
+ {
+ if (token.ident == Id.dllimport)
+ {
+ dllimport = true;
+ nextToken();
+ }
+ else if (token.ident == Id.dllexport)
+ {
+ dllexport = true;
+ nextToken();
+ }
+ else if (token.ident == Id.noreturn)
+ {
+ specifier.noreturn = true;
+ nextToken();
+ }
+ else if (token.ident == Id.vector_size)
+ {
+ nextToken();
+ check(TOK.leftParenthesis);
+ cparseConstantExp(); // TODO implement
+ check(TOK.rightParenthesis);
+ }
+ else
+ {
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ cparseParens();
+ }
+ }
+ else
+ {
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ cparseParens();
+ }
+ }
+
+ /*************************
+ * See if match for GNU attribute name, which may be any identifier,
+ * storage-class-specifier, type-specifier, or type-qualifier.
+ * Returns:
+ * true if a valid GNU attribute name
+ */
+ private bool isGnuAttributeName()
+ {
+ switch (token.value)
+ {
+ case TOK.identifier:
+ case TOK.static_:
+ case TOK.unsigned:
+ case TOK.int64:
+ case TOK.const_:
+ case TOK.extern_:
+ case TOK.register:
+ case TOK.typedef_:
+ case TOK.int16:
+ case TOK.inline:
+ case TOK._Noreturn:
+ case TOK.volatile:
+ case TOK.signed:
+ case TOK.auto_:
+ case TOK.restrict:
+ case TOK._Complex:
+ case TOK._Thread_local:
+ case TOK.int32:
+ case TOK.char_:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.void_:
+ case TOK._Bool:
+ case TOK._Atomic:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ /***************************
+ * Like skipParens(), but consume the tokens.
+ */
+ private void cparseParens()
+ {
+ check(TOK.leftParenthesis);
+ int parens = 1;
+
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.leftParenthesis:
+ ++parens;
+ break;
+
+ case TOK.rightParenthesis:
+ --parens;
+ if (parens < 0)
+ {
+ error("extra right parenthesis");
+ return;
+ }
+ if (parens == 0)
+ {
+ nextToken();
+ return;
+ }
+ break;
+
+ case TOK.endOfFile:
+ error("end of file found before right parenthesis");
+ return;
+
+ default:
+ break;
+ }
+ nextToken();
+ }
+ }
+
+ //}
+ /******************************************************************************/
+ /***************************** Struct & Enum Parser ***************************/
+ //{
+
+ /*************************************
+ * C11 6.7.2.2
+ * enum-specifier:
+ * enum identifier (opt) { enumerator-list }
+ * enum identifier (opt) { enumerator-list , }
+ * enum identifier
+ *
+ * enumerator-list:
+ * enumerator
+ * enumerator-list , enumerator
+ *
+ * enumerator:
+ * enumeration-constant
+ * enumeration-constant = constant-expression
+ *
+ * enumeration-constant:
+ * identifier
+ *
+ * Params:
+ * symbols = symbols to add enum declaration to
+ * Returns:
+ * type of the enum
+ */
+ private AST.Type cparseEnum(ref AST.Dsymbols* symbols)
+ {
+ const loc = token.loc;
+ nextToken();
+
+ /* GNU Extensions
+ * enum-specifier:
+ * enum gnu-attributes (opt) identifier (opt) { enumerator-list } gnu-attributes (opt)
+ * enum gnu-attributes (opt) identifier (opt) { enumerator-list , } gnu-attributes (opt)
+ * enum gnu-attributes (opt) identifier
+ */
+ Specifier specifier;
+ if (token.value == TOK.__attribute__)
+ cparseGnuAttributes(specifier);
+
+ Identifier tag;
+ if (token.value == TOK.identifier)
+ {
+ tag = token.ident;
+ nextToken();
+ }
+
+ AST.Dsymbols* members;
+ if (token.value == TOK.leftCurly)
+ {
+ nextToken();
+ members = new AST.Dsymbols();
+
+ if (token.value == TOK.rightCurly) // C11 6.7.2.2-1
+ {
+ if (tag)
+ error("no members for `enum %s`", tag.toChars());
+ else
+ error("no members for anonymous enum");
+ }
+
+ while (token.value == TOK.identifier)
+ {
+ auto ident = token.ident; // enumeration-constant
+ nextToken();
+ auto mloc = token.loc;
+
+ AST.Expression value;
+ if (token.value == TOK.assign)
+ {
+ nextToken();
+ value = cparseConstantExp();
+ // TODO C11 6.7.2.2-2 value must fit into an int
+ }
+
+ auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null);
+ members.push(em);
+
+ if (token.value == TOK.comma)
+ {
+ nextToken();
+ continue;
+ }
+ break;
+ }
+ check(TOK.rightCurly);
+
+ /* GNU Extensions
+ * Parse the postfix gnu-attributes (opt)
+ */
+ if (token.value == TOK.__attribute__)
+ cparseGnuAttributes(specifier);
+ }
+ else if (!tag)
+ error("missing `identifier` after `enum`");
+
+ /* Need semantic information to determine if this is a declaration,
+ * redeclaration, or reference to existing declaration.
+ * Defer to the semantic() pass with a TypeTag.
+ */
+ return new AST.TypeTag(loc, TOK.enum_, tag, members);
+ }
+
+ /*************************************
+ * C11 6.7.2.1
+ * Parse struct and union specifiers.
+ * Parser is advanced to the tag identifier or brace.
+ * struct-or-union-specifier:
+ * struct-or-union identifier (opt) { struct-declaration-list }
+ * struct-or-union identifier
+ *
+ * struct-or-union:
+ * struct
+ * union
+ *
+ * struct-declaration-list:
+ * struct-declaration
+ * struct-declaration-list struct-declaration
+ *
+ * Params:
+ * loc = location of `struct` or `union`
+ * structOrUnion = TOK.struct_ or TOK.union_
+ * symbols = symbols to add struct-or-union declaration to
+ * Returns:
+ * type of the struct
+ */
+ private AST.Type cparseStruct(Loc loc, TOK structOrUnion, ref AST.Dsymbols* symbols)
+ {
+ Identifier tag;
+
+ if (token.value == TOK.identifier)
+ {
+ tag = token.ident;
+ nextToken();
+ }
+
+ AST.Dsymbols* members;
+ if (token.value == TOK.leftCurly)
+ {
+ nextToken();
+ auto symbolsSave = symbols;
+ symbols = new AST.Dsymbols();
+ while (token.value != TOK.rightCurly)
+ {
+ cparseStructDeclaration();
+
+ if (token.value == TOK.endOfFile)
+ break;
+ }
+ members = symbols; // `members` will be non-null even with 0 members
+ symbols = symbolsSave;
+ check(TOK.rightCurly);
+
+ if ((*members).length == 0) // C11 6.7.2.1-8
+ /* TODO: not strict enough, should really be contains "no named members",
+ * not just "no members".
+ * I.e. an unnamed bit field, _Static_assert, etc, are not named members,
+ * but will pass this check.
+ * Be careful to detect named members that come anonymous structs.
+ * Correctly doing this will likely mean moving it to typesem.d.
+ */
+ error("empty struct-declaration-list for `%s %s`", Token.toChars(structOrUnion), tag ? tag.toChars() : "Anonymous".ptr);
+ }
+ else if (!tag)
+ error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion));
+
+ /* Need semantic information to determine if this is a declaration,
+ * redeclaration, or reference to existing declaration.
+ * Defer to the semantic() pass with a TypeTag.
+ */
+ return new AST.TypeTag(loc, structOrUnion, tag, members);
+ }
+
+ /*************************************
+ * C11 6.7.2.1
+ * Parse a struct declaration member.
+ * struct-declaration:
+ * specifier-qualifier-list struct-declarator-list (opt) ;
+ * static_assert-declaration
+ *
+ * struct-declarator-list:
+ * struct-declarator
+ * struct-declarator-list , struct-declarator
+ *
+ * struct-declarator:
+ * declarator
+ * declarator (opt) : constant-expression
+ */
+ void cparseStructDeclaration()
+ {
+ //printf("cparseStructDeclaration()\n");
+ if (token.value == TOK._Static_assert)
+ {
+ auto s = cparseStaticAssert();
+ symbols.push(s);
+ return;
+ }
+
+ auto symbolsSave = symbols;
+ Specifier specifier;
+ auto tspec = cparseSpecifierQualifierList(LVL.member, specifier);
+
+ /* If a declarator does not follow, it is unnamed
+ */
+ if (token.value == TOK.semicolon && tspec)
+ {
+ nextToken();
+ auto tt = tspec.isTypeTag();
+ if (!tt)
+ return; // legal but meaningless empty declaration
+
+ /* If anonymous struct declaration
+ * struct { ... members ... };
+ * C11 6.7.2.1-13
+ */
+ if (!tt.id && tt.members)
+ {
+ /* members of anonymous struct are considered members of
+ * the containing struct
+ */
+ auto ad = new AST.AnonDeclaration(tt.loc, tt.tok == TOK.union_, tt.members);
+ if (!symbols)
+ symbols = new AST.Dsymbols();
+ auto s = applySpecifier(ad, specifier);
+ symbols.push(s);
+ return;
+ }
+ if (!tt.id && !tt.members)
+ return; // already gave error in cparseStruct()
+
+ /* `struct tag;` and `struct tag { ... };`
+ * always result in a declaration in the current scope
+ */
+ // TODO: merge in specifier
+ auto stag = (tt.tok == TOK.struct_)
+ ? new AST.StructDeclaration(tt.loc, tt.id, false)
+ : new AST.UnionDeclaration(tt.loc, tt.id);
+ stag.members = tt.members;
+ if (!symbols)
+ symbols = new AST.Dsymbols();
+ auto s = applySpecifier(stag, specifier);
+ symbols.push(s);
+ return;
+ }
+
+ while (1)
+ {
+ Identifier id;
+ AST.Type dt;
+ if (token.value == TOK.colon)
+ {
+ // C11 6.7.2.1-12 unnamed bit-field
+ id = Identifier.generateAnonymousId("BitField");
+ dt = tspec;
+ }
+ else
+ dt = cparseDeclarator(DTR.xdirect, tspec, id);
+ if (!dt)
+ {
+ panic();
+ nextToken();
+ break; // error recovery
+ }
+
+ AST.Expression width;
+ if (token.value == TOK.colon)
+ {
+ // C11 6.7.2.1-10 bit-field
+ nextToken();
+ width = cparseConstantExp();
+ }
+
+ if (specifier.mod & MOD.xconst)
+ dt = toConst(dt);
+
+ /* GNU Extensions
+ * struct-declarator:
+ * declarator gnu-attributes (opt)
+ * declarator (opt) : constant-expression gnu-attributes (opt)
+ */
+ if (token.value == TOK.__attribute__)
+ cparseGnuAttributes(specifier);
+
+ AST.Dsymbol s = null;
+ symbols = symbolsSave;
+ if (!symbols)
+ symbols = new AST.Dsymbols; // lazilly create it
+
+ if (!tspec && !specifier.scw && !specifier.mod)
+ error("specifier-qualifier-list required");
+ else if (width)
+ {
+ if (specifier.alignExps)
+ error("no alignment-specifier for bit field declaration"); // C11 6.7.5-2
+ s = new AST.BitFieldDeclaration(width.loc, dt, id, width);
+ }
+ else if (id)
+ {
+ if (dt.ty == AST.Tvoid)
+ error("`void` has no value");
+
+ // declare the symbol
+ // Give member variables an implicit void initializer
+ auto initializer = new AST.VoidInitializer(token.loc);
+ s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(LVL.member, specifier));
+ s = applySpecifier(s, specifier);
+ }
+ if (s !is null)
+ symbols.push(s);
+
+ switch (token.value)
+ {
+ case TOK.identifier:
+ error("missing comma");
+ goto default;
+
+ case TOK.semicolon:
+ nextToken();
+ return;
+
+ case TOK.comma:
+ nextToken();
+ break;
+
+ default:
+ error("`;` or `,` expected");
+ while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
+ nextToken();
+ nextToken();
+ return;
+ }
+ }
+ }
+
+ //}
+ /******************************************************************************/
+ /********************************* Lookahead Parser ***************************/
+ //{
+
+ /************************************
+ * Determine if the scanner is sitting on the start of a declaration.
+ * Params:
+ * t = current token of the scanner
+ * needId = flag with additional requirements for a declaration
+ * endtok = ending token
+ * pt = will be set ending token (if not null)
+ * Returns:
+ * true at start of a declaration
+ */
+ private bool isCDeclaration(ref Token* pt)
+ {
+ //printf("isCDeclaration()\n");
+ auto t = pt;
+ if (!isDeclarationSpecifiers(t))
+ return false;
+
+ while (1)
+ {
+ if (t.value == TOK.semicolon)
+ {
+ t = peek(t);
+ pt = t;
+ return true;
+ }
+ if (!isCDeclarator(t, DTR.xdirect))
+ return false;
+ if (t.value == TOK.asm_)
+ {
+ t = peek(t);
+ if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
+ return false;
+ }
+ if (t.value == TOK.__attribute__)
+ {
+ t = peek(t);
+ if (t.value != TOK.leftParenthesis || !skipParens(t, &t))
+ return false;
+ }
+ if (t.value == TOK.assign)
+ {
+ t = peek(t);
+ if (!isInitializer(t))
+ return false;
+ }
+ switch (t.value)
+ {
+ case TOK.comma:
+ t = peek(t);
+ break;
+
+ case TOK.semicolon:
+ t = peek(t);
+ pt = t;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ /********************************
+ * See if match for initializer.
+ * Params:
+ * pt = starting token, updated to one past end of initializer if true
+ * Returns:
+ * true if initializer
+ */
+ private bool isInitializer(ref Token* pt)
+ {
+ //printf("isInitializer()\n");
+ auto t = pt;
+
+ if (t.value == TOK.leftCurly)
+ {
+ if (!skipBraces(t))
+ return false;
+ pt = t;
+ return true;
+ }
+
+ // skip over assignment-expression, ending before comma or semiColon or EOF
+ if (!isAssignmentExpression(t))
+ return false;
+ pt = t;
+ return true;
+ }
+
+ /********************************
+ * See if match for:
+ * postfix-expression ( argument-expression-list(opt) )
+ * Params:
+ * pt = starting token, updated to one past end of initializer if true
+ * Returns:
+ * true if function call
+ */
+ private bool isFunctionCall(ref Token* pt)
+ {
+ //printf("isFunctionCall()\n");
+ auto t = pt;
+
+ if (!isPrimaryExpression(t))
+ return false;
+ if (t.value != TOK.leftParenthesis)
+ return false;
+ t = peek(t);
+ while (1)
+ {
+ if (!isAssignmentExpression(t))
+ return false;
+ if (t.value == TOK.comma)
+ {
+ t = peek(t);
+ continue;
+ }
+ if (t.value == TOK.rightParenthesis)
+ {
+ t = peek(t);
+ break;
+ }
+ return false;
+ }
+ if (t.value != TOK.semicolon)
+ return false;
+ pt = t;
+ return true;
+ }
+
+ /********************************
+ * See if match for assignment-expression.
+ * Params:
+ * pt = starting token, updated to one past end of assignment-expression if true
+ * Returns:
+ * true if assignment-expression
+ */
+ private bool isAssignmentExpression(ref Token* pt)
+ {
+ //printf("isAssignmentExpression()\n");
+ auto t = pt;
+
+ /* This doesn't actually check for grammar matching an
+ * assignment-expression. It just matches ( ) [ ] looking for
+ * an ending token that would terminate one.
+ */
+ bool any;
+ while (1)
+ {
+ switch (t.value)
+ {
+ case TOK.comma:
+ case TOK.semicolon:
+ case TOK.rightParenthesis:
+ case TOK.rightBracket:
+ case TOK.endOfFile:
+ if (!any)
+ return false;
+ break;
+
+ case TOK.leftParenthesis:
+ if (!skipParens(t, &t))
+ return false;
+ continue;
+
+ case TOK.leftBracket:
+ if (!skipBrackets(t))
+ return false;
+ continue;
+
+ default:
+ any = true; // assume token was part of an a-e
+ t = peek(t);
+ continue;
+ }
+ pt = t;
+ return true;
+ }
+ }
+
+ /********************************
+ * See if match for constant-expression.
+ * Params:
+ * pt = starting token, updated to one past end of constant-expression if true
+ * Returns:
+ * true if constant-expression
+ */
+ private bool isConstantExpression(ref Token* pt)
+ {
+ return isAssignmentExpression(pt);
+ }
+
+ /********************************
+ * See if match for declaration-specifiers.
+ * No errors are diagnosed.
+ * Params:
+ * pt = starting token, updated to one past end of declaration-specifiers if true
+ * Returns:
+ * true if declaration-specifiers
+ */
+ private bool isDeclarationSpecifiers(ref Token* pt)
+ {
+ //printf("isDeclarationSpecifiers()\n");
+
+ auto t = pt;
+
+ bool any;
+ while (1)
+ {
+ switch (t.value)
+ {
+ // type-specifiers
+ case TOK.void_:
+ case TOK.char_:
+ case TOK.int16:
+ case TOK.int32:
+ case TOK.int64:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.signed:
+ case TOK.unsigned:
+ case TOK._Bool:
+ //case TOK._Imaginary:
+ case TOK._Complex:
+ case TOK.identifier: // typedef-name
+ t = peek(t);
+ any = true;
+ break;
+
+ case TOK.struct_:
+ case TOK.union_:
+ case TOK.enum_:
+ t = peek(t);
+ if (t.value == TOK.identifier)
+ {
+ t = peek(t);
+ if (t.value == TOK.leftCurly)
+ {
+ if (!skipBraces(t))
+ return false;
+ }
+ }
+ else if (t.value == TOK.leftCurly)
+ {
+ if (!skipBraces(t))
+ return false;
+ }
+ else
+ return false;
+ any = true;
+ continue;
+
+ // storage-class-specifiers
+ case TOK.typedef_:
+ case TOK.extern_:
+ case TOK.static_:
+ case TOK._Thread_local:
+ case TOK.auto_:
+ case TOK.register:
+
+ // function-specifiers
+ case TOK.inline:
+ case TOK._Noreturn:
+
+ // type-qualifiers
+ case TOK.const_:
+ case TOK.volatile:
+ case TOK.restrict:
+ t = peek(t);
+ any = true;
+ continue;
+
+ case TOK._Alignas: // alignment-specifier
+ case TOK.__declspec: // decl-specifier
+ case TOK.__attribute__: // attribute-specifier
+ t = peek(t);
+ if (!skipParens(t, &t))
+ return false;
+ any = true;
+ continue;
+
+ // either atomic-type-specifier or type_qualifier
+ case TOK._Atomic: // TODO _Atomic ( type-name )
+ t = peek(t);
+ if (t.value == TOK.leftParenthesis) // maybe atomic-type-specifier
+ {
+ auto tsave = t;
+ t = peek(t);
+ if (!isTypeName(t) || t.value != TOK.rightParenthesis)
+ { // it's a type-qualifier
+ t = tsave; // back up parser
+ any = true;
+ continue;
+ }
+ t = peek(t); // move past right parenthesis of atomic-type-specifier
+ }
+ any = true;
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ if (any)
+ {
+ pt = t;
+ return true;
+ }
+ return false;
+ }
+
+ /**************************************
+ * See if declaration-list is present.
+ * Returns:
+ * true if declaration-list is present, even an empty one
+ */
+ bool isDeclarationList(ref Token* pt)
+ {
+ auto t = pt;
+ while (1)
+ {
+ if (t.value == TOK.leftCurly)
+ {
+ pt = t;
+ return true;
+ }
+ if (!isCDeclaration(t))
+ return false;
+ }
+ }
+
+ /*******************************************
+ * Skip braces.
+ * Params:
+ * pt = enters on left brace, set to token past right bracket on true
+ * Returns:
+ * true if successful
+ */
+ private bool skipBraces(ref Token* pt)
+ {
+ auto t = pt;
+ if (t.value != TOK.leftCurly)
+ return false;
+
+ int braces = 0;
+
+ while (1)
+ {
+ switch (t.value)
+ {
+ case TOK.leftCurly:
+ ++braces;
+ t = peek(t);
+ continue;
+
+ case TOK.rightCurly:
+ --braces;
+ if (braces == 0)
+ {
+ pt = peek(t);
+ return true;
+ }
+ if (braces < 0)
+ return false;
+
+ t = peek(t);
+ continue;
+
+ case TOK.endOfFile:
+ return false;
+
+ default:
+ t = peek(t);
+ continue;
+ }
+ }
+ }
+
+ /*******************************************
+ * Skip brackets.
+ * Params:
+ * pt = enters on left bracket, set to token past right bracket on true
+ * Returns:
+ * true if successful
+ */
+ private bool skipBrackets(ref Token* pt)
+ {
+ auto t = pt;
+ if (t.value != TOK.leftBracket)
+ return false;
+
+ int brackets = 0;
+
+ while (1)
+ {
+ switch (t.value)
+ {
+ case TOK.leftBracket:
+ ++brackets;
+ t = peek(t);
+ continue;
+
+ case TOK.rightBracket:
+ --brackets;
+ if (brackets == 0)
+ {
+ pt = peek(t);
+ return true;
+ }
+ if (brackets < 0)
+ return false;
+
+ t = peek(t);
+ continue;
+
+ case TOK.endOfFile:
+ return false;
+
+ default:
+ t = peek(t);
+ continue;
+ }
+ }
+ }
+
+ /*********************************
+ * Check to see if tokens starting with *pt form a declarator.
+ * Params:
+ * pt = pointer to starting token, updated to point past declarator if true is returned
+ * declarator = declarator kind
+ * Returns:
+ * true if it does
+ */
+ private bool isCDeclarator(ref Token* pt, DTR declarator)
+ {
+ auto t = pt;
+ while (1)
+ {
+ if (t.value == TOK.mul) // pointer
+ {
+ t = peek(t);
+ if (!isTypeQualifierList(t))
+ return false;
+ }
+ else
+ break;
+ }
+
+ if (t.value == TOK.identifier)
+ {
+ if (declarator == DTR.xabstract)
+ return false;
+ t = peek(t);
+ }
+ else if (t.value == TOK.leftParenthesis)
+ {
+ t = peek(t);
+ if (!isCDeclarator(t, declarator))
+ return false;
+ if (t.value != TOK.rightParenthesis)
+ return false;
+ t = peek(t);
+ }
+ else if (declarator == DTR.xdirect)
+ {
+ return false;
+ }
+
+ while (1)
+ {
+ if (t.value == TOK.leftBracket)
+ {
+ if (!skipBrackets(t))
+ return false;
+ }
+ else if (t.value == TOK.leftParenthesis)
+ {
+ if (!skipParens(t, &t))
+ return false;
+ }
+ else
+ break;
+ }
+ pt = t;
+ return true;
+ }
+
+ /***************************
+ * Is this the start of a type-qualifier-list?
+ * (Can be empty.)
+ * Params:
+ * pt = first token; updated with past end of type-qualifier-list if true
+ * Returns:
+ * true if start of type-qualifier-list
+ */
+ private bool isTypeQualifierList(ref Token* pt)
+ {
+ auto t = pt;
+ while (1)
+ {
+ switch (t.value)
+ {
+ case TOK.const_:
+ case TOK.restrict:
+ case TOK.volatile:
+ case TOK._Atomic:
+ t = peek(t);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ pt = t;
+ return true;
+ }
+
+ /***************************
+ * Is this the start of a type-name?
+ * Params:
+ * pt = first token; updated with past end of type-name if true
+ * Returns:
+ * true if start of type-name
+ */
+ private bool isTypeName(ref Token* pt)
+ {
+ auto t = pt;
+ //printf("isTypeName() %s\n", t.toChars());
+ if (!isSpecifierQualifierList(t))
+ return false;
+ if (!isCDeclarator(t, DTR.xabstract))
+ return false;
+ if (t.value != TOK.rightParenthesis)
+ return false;
+ pt = t;
+ return true;
+ }
+
+ /***************************
+ * Is this the start of a specifier-qualifier-list?
+ * Params:
+ * pt = first token; updated with past end of specifier-qualifier-list if true
+ * Returns:
+ * true if start of specifier-qualifier-list
+ */
+ private bool isSpecifierQualifierList(ref Token* pt)
+ {
+ auto t = pt;
+ bool result;
+ while (1)
+ {
+ switch (t.value)
+ {
+ // Type Qualifiers
+ case TOK.const_:
+ case TOK.restrict:
+ case TOK.volatile:
+
+ // Type Specifiers
+ case TOK.char_:
+ case TOK.signed:
+ case TOK.unsigned:
+ case TOK.int16:
+ case TOK.int32:
+ case TOK.int64:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.void_:
+ case TOK._Bool:
+ //case TOK._Imaginary: // ? missing in Spec
+ case TOK._Complex:
+
+ // typedef-name
+ case TOK.identifier: // will not know until semantic if typedef
+ t = peek(t);
+ break;
+
+ // struct-or-union-specifier
+ // enum-specifier
+ case TOK.struct_:
+ case TOK.union_:
+ case TOK.enum_:
+ t = peek(t);
+ if (t.value == TOK.identifier)
+ {
+ t = peek(t);
+ if (t.value == TOK.leftCurly)
+ {
+ if (!skipBraces(t))
+ return false;
+ }
+ }
+ else if (t.value == TOK.leftCurly)
+ {
+ if (!skipBraces(t))
+ return false;
+ }
+ else
+ return false;
+ break;
+
+ // atomic-type-specifier
+ case TOK._Atomic:
+ t = peek(t);
+ if (t.value != TOK.leftParenthesis ||
+ !skipParens(t, &t))
+ return false;
+ break;
+
+ default:
+ if (result)
+ pt = t;
+ return result;
+ }
+ result = true;
+ }
+ }
+
+ /************************************
+ * Looking at the leading left parenthesis, and determine if it is
+ * either of the following:
+ * ( type-name ) cast-expression
+ * ( type-name ) { initializer-list }
+ * as opposed to:
+ * ( expression )
+ * Params:
+ * pt = starting token, updated to one past end of constant-expression if true
+ * afterParenType = true if already seen ( type-name )
+ * Returns:
+ * true if matches ( type-name ) ...
+ */
+ private bool isCastExpression(ref Token* pt, bool afterParenType = false)
+ {
+ auto t = pt;
+ switch (t.value)
+ {
+ case TOK.leftParenthesis:
+ auto tk = peek(t); // move past left parenthesis
+ if (!isTypeName(tk) || tk.value != TOK.rightParenthesis)
+ {
+ if (afterParenType)
+ goto default; // could be ( type-name ) ( unary-expression )
+ return false;
+ }
+ tk = peek(tk); // move past right parenthesis
+
+ if (tk.value == TOK.leftCurly)
+ {
+ // ( type-name ) { initializer-list }
+ if (!isInitializer(tk))
+ return false;
+ t = tk;
+ break;
+ }
+ if (!isCastExpression(tk, true))
+ {
+ if (afterParenType) // could be ( type-name ) ( unary-expression )
+ goto default; // where unary-expression also matched type-name
+ return false;
+ }
+ // ( type-name ) cast-expression
+ t = tk;
+ break;
+
+ default:
+ if (!afterParenType || !isUnaryExpression(t, afterParenType))
+ return false;
+ // if we've already seen ( type-name ), then this is a cast
+ break;
+ }
+ pt = t;
+ return true;
+ }
+
+ /********************************
+ * See if match for unary-expression.
+ * Params:
+ * pt = starting token, updated to one past end of constant-expression if true
+ * afterParenType = true if already seen ( type-name ) of a cast-expression
+ * Returns:
+ * true if unary-expression
+ */
+ private bool isUnaryExpression(ref Token* pt, bool afterParenType = false)
+ {
+ auto t = pt;
+ switch (t.value)
+ {
+ case TOK.plusPlus:
+ case TOK.minusMinus:
+ t = peek(t);
+ if (!isUnaryExpression(t, afterParenType))
+ return false;
+ break;
+
+ case TOK.and:
+ case TOK.mul:
+ case TOK.min:
+ case TOK.add:
+ case TOK.not:
+ case TOK.tilde:
+ t = peek(t);
+ if (!isCastExpression(t, afterParenType))
+ return false;
+ break;
+
+ case TOK.sizeof_:
+ t = peek(t);
+ if (t.value == TOK.leftParenthesis)
+ {
+ auto tk = peek(t);
+ if (isTypeName(tk))
+ {
+ if (tk.value != TOK.rightParenthesis)
+ return false;
+ t = peek(tk);
+ break;
+ }
+ }
+ if (!isUnaryExpression(t, afterParenType))
+ return false;
+ break;
+
+ case TOK._Alignof:
+ t = peek(t);
+ if (t.value != TOK.leftParenthesis)
+ return false;
+ t = peek(t);
+ if (!isTypeName(t) || t.value != TOK.rightParenthesis)
+ return false;
+ break;
+
+ default:
+ // Compound literals are handled by cast and sizeof expressions,
+ // so be content with just seeing a primary expression.
+ if (!isPrimaryExpression(t))
+ return false;
+ break;
+ }
+ pt = t;
+ return true;
+ }
+
+ /********************************
+ * See if match for primary-expression.
+ * Params:
+ * pt = starting token, updated to one past end of constant-expression if true
+ * Returns:
+ * true if primary-expression
+ */
+ private bool isPrimaryExpression(ref Token* pt)
+ {
+ auto t = pt;
+ switch (t.value)
+ {
+ case TOK.identifier:
+ case TOK.int32Literal:
+ case TOK.uns32Literal:
+ case TOK.int64Literal:
+ case TOK.uns64Literal:
+ case TOK.float32Literal:
+ case TOK.float64Literal:
+ case TOK.float80Literal:
+ case TOK.imaginary32Literal:
+ case TOK.imaginary64Literal:
+ case TOK.imaginary80Literal:
+ case TOK.string_:
+ t = peek(t);
+ break;
+
+ case TOK.leftParenthesis:
+ // ( expression )
+ if (!skipParens(t, &t))
+ return false;
+ break;
+
+ case TOK._Generic:
+ t = peek(t);
+ if (!skipParens(t, &t))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+ pt = t;
+ return true;
+ }
+
+ //}
+ /******************************************************************************/
+ /********************************* More ***************************************/
+ //{
+
+ /**************
+ * Declaration context
+ */
+ enum LVL
+ {
+ global = 1, /// global
+ parameter = 2, /// function parameter (declarations for function identifier-list)
+ prototype = 4, /// function prototype
+ local = 8, /// local
+ member = 0x10, /// struct member
+ }
+
+ /// Types of declarator to parse
+ enum DTR
+ {
+ xdirect = 1, /// C11 6.7.6 direct-declarator
+ xabstract = 2, /// C11 6.7.7 abstract-declarator
+ xparameter = 3, /// parameter declarator may be either direct or abstract
+ }
+
+ /// C11 6.7.1 Storage-class specifiers
+ enum SCW : uint
+ {
+ xnone = 0,
+ xtypedef = 1,
+ xextern = 2,
+ xstatic = 4,
+ x_Thread_local = 8,
+ xauto = 0x10,
+ xregister = 0x20,
+ // C11 6.7.4 Function specifiers
+ xinline = 0x40,
+ x_Noreturn = 0x80,
+ }
+
+ /// C11 6.7.3 Type qualifiers
+ enum MOD : uint
+ {
+ xnone = 0,
+ xconst = 1,
+ xvolatile = 2,
+ xrestrict = 4,
+ x_Atomic = 8,
+ }
+
+ /**********************************
+ * Aggregate for all the various specifiers
+ */
+ struct Specifier
+ {
+ bool noreturn; /// noreturn attribute
+ SCW scw; /// storage-class specifiers
+ MOD mod; /// type qualifiers
+ AST.Expressions* alignExps; /// alignment
+ }
+
+ /***********************
+ * Convert from C specifiers to D storage class
+ * Params:
+ * level = declaration context
+ * specifier = specifiers, context, etc.
+ * Returns:
+ * corresponding D storage class
+ */
+ StorageClass specifiersToSTC(LVL level, const ref Specifier specifier)
+ {
+ StorageClass stc;
+ if (specifier.scw & SCW.x_Thread_local)
+ {
+ if (level == LVL.global)
+ {
+ if (specifier.scw & SCW.xextern)
+ stc = AST.STC.extern_;
+ }
+ else if (level == LVL.local)
+ {
+ if (specifier.scw & SCW.xextern)
+ stc = AST.STC.extern_;
+ else if (specifier.scw & SCW.xstatic)
+ stc = AST.STC.static_;
+ }
+ else if (level == LVL.member)
+ {
+ if (specifier.scw & SCW.xextern)
+ stc = AST.STC.extern_;
+ else if (specifier.scw & SCW.xstatic)
+ stc = AST.STC.static_;
+ }
+ }
+ else
+ {
+ if (level == LVL.global)
+ {
+ if (specifier.scw & SCW.xextern)
+ stc = AST.STC.extern_ | AST.STC.gshared;
+ else
+ stc = AST.STC.gshared;
+ }
+ else if (level == LVL.local)
+ {
+ if (specifier.scw & SCW.xextern)
+ stc = AST.STC.extern_ | AST.STC.gshared;
+ else if (specifier.scw & SCW.xstatic)
+ stc = AST.STC.gshared;
+ }
+ else if (level == LVL.member)
+ {
+ if (specifier.scw & SCW.xextern)
+ stc = AST.STC.extern_ | AST.STC.gshared;
+ else if (specifier.scw & SCW.xstatic)
+ stc = AST.STC.gshared;
+ }
+ }
+ return stc;
+ }
+
+ /***********************
+ * Return suitable D float type for C `long double`
+ * Params:
+ * flags = kind of float to return (real, imaginary, complex).
+ * Returns:
+ * corresponding D type
+ */
+ private AST.Type realType(RTFlags flags)
+ {
+ if (long_doublesize == AST.Type.tfloat80.size())
+ {
+ // On GDC and LDC, D `real` types map to C `long double`, so never
+ // return a double type when real.sizeof == double.sizeof.
+ final switch (flags)
+ {
+ case RTFlags.realfloat: return AST.Type.tfloat80;
+ case RTFlags.imaginary: return AST.Type.timaginary80;
+ case RTFlags.complex: return AST.Type.tcomplex80;
+ }
+ }
+ else
+ {
+ final switch (flags)
+ {
+ case RTFlags.realfloat: return long_doublesize == 8 ? AST.Type.tfloat64 : AST.Type.tfloat80;
+ case RTFlags.imaginary: return long_doublesize == 8 ? AST.Type.timaginary64 : AST.Type.timaginary80;
+ case RTFlags.complex: return long_doublesize == 8 ? AST.Type.tcomplex64 : AST.Type.tcomplex80;
+ }
+ }
+ }
+
+ /**************
+ * Flags for realType
+ */
+ private enum RTFlags
+ {
+ realfloat,
+ imaginary,
+ complex,
+ }
+
+ /********************
+ * C11 6.4.2.2 Create declaration to predefine __func__
+ * `static const char __func__[] = " function-name ";`
+ * Params:
+ * loc = location for this declaration
+ * id = identifier of function
+ * Returns:
+ * statement representing the declaration of __func__
+ */
+ private AST.Statement createFuncName(Loc loc, Identifier id)
+ {
+ const fn = id.toString(); // function-name
+ auto efn = new AST.StringExp(loc, fn, fn.length, 1, 'c');
+ auto ifn = new AST.ExpInitializer(loc, efn);
+ auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0
+ auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn);
+ efn.type = tfn.immutableOf();
+ efn.committed = 1;
+ auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_);
+ auto e = new AST.DeclarationExp(loc, sfn);
+ return new AST.ExpStatement(loc, e);
+ }
+
+ /************************
+ * After encountering an error, scan forward until a right brace or ; is found
+ * or the end of the file.
+ */
+ void panic()
+ {
+ while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
+ nextToken();
+ }
+
+ /**************************
+ * Apply `const` to a type.
+ * Params:
+ * t = type to add const to
+ * Returns:
+ * resulting type
+ */
+ private AST.Type toConst(AST.Type t)
+ {
+ // `const` is always applied to the return type, not the
+ // type function itself.
+ if (auto tf = t.isTypeFunction())
+ tf.next = tf.next.addSTC(STC.const_);
+ else
+ t = t.addSTC(STC.const_);
+ return t;
+ }
+
+ /***************************
+ * Apply specifier to a Dsymbol.
+ * Params:
+ * s = Dsymbol
+ * specifier = specifiers to apply
+ * Returns:
+ * Dsymbol with specifiers applied
+ */
+ private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
+ {
+ if (specifier.alignExps)
+ {
+ // Wrap declaration in an AlignDeclaration
+ auto decls = new AST.Dsymbols(1);
+ (*decls)[0] = s;
+ s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
+ }
+ return s;
+ }
+
+ //}
+}
diff --git a/gcc/d/dmd/cppmangle.c b/gcc/d/dmd/cppmangle.c
deleted file mode 100644
index baf64c5653e..00000000000
--- a/gcc/d/dmd/cppmangle.c
+++ /dev/null
@@ -1,1168 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/cppmangle.c
- */
-
-/**
- * Do mangling for C++ linkage.
- *
- * References:
- * Follows Itanium C++ ABI 1.86 section 5.1
- * http://refspecs.linux-foundation.org/cxxabi-1.86.html#mangling
- * which is where the grammar comments come from.
- *
- * Bugs:
- * https://issues.dlang.org/query.cgi
- * enter `C++, mangling` as the keywords.
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "dsymbol.h"
-#include "mtype.h"
-#include "scope.h"
-#include "init.h"
-#include "expression.h"
-#include "attrib.h"
-#include "declaration.h"
-#include "template.h"
-#include "id.h"
-#include "enum.h"
-#include "import.h"
-#include "aggregate.h"
-#include "target.h"
-
-typedef int (*ForeachDg)(void *ctx, size_t paramidx, Parameter *param);
-int Parameter_foreach(Parameters *parameters, ForeachDg dg, void *ctx, size_t *pn = NULL);
-
-class CppMangleVisitor : public Visitor
-{
- Objects components; // array of components available for substitution
- OutBuffer *buf; // append the mangling to buf[]
- public:
- Loc loc; // location for use in error messages
-
- // Write <seq-id> to buf
- void write_seq_id(size_t i)
- {
- if (i >= 36)
- {
- write_seq_id(i / 36);
- i %= 36;
- }
- i += (i < 10) ? '0' : 'A' - 10;
- buf->writeByte((char)i);
- }
-
- bool substitute(RootObject *p)
- {
- //printf("substitute %s\n", p ? p->toChars() : NULL);
- int i = find(p);
- if (i >= 0)
- {
- //printf("\tmatch\n");
- /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ...
- */
- buf->writeByte('S');
- if (i)
- {
- write_seq_id(i - 1);
- }
- buf->writeByte('_');
- return true;
- }
- return false;
- }
-
- /******
- * See if `p` exists in components[]
- * Returns:
- * index if found, -1 if not
- */
- int find(RootObject *p)
- {
- //printf("find %p %d %s\n", p, p.dyncast(), p ? p.toChars() : NULL);
- for (size_t i = 0; i < components.length; i++)
- {
- if (p == components[i])
- return (int)i;
- }
- return -1;
- }
-
- /*********************
- * Append p to components[]
- */
- void append(RootObject *p)
- {
- //printf("append %p %d %s\n", p, p.dyncast(), p ? p.toChars() : "null");
- components.push(p);
- }
-
- /************************
- * Determine if symbol is indeed the global ::std namespace.
- * Params:
- * s = symbol to check
- * Returns:
- * true if it is ::std
- */
- static bool isStd(Dsymbol *s)
- {
- return (s &&
- s->ident == Id::std && // the right name
- s->isNspace() && // g++ disallows global "std" for other than a namespace
- !getQualifier(s)); // at global level
- }
-
- /************************
- * Determine if type is a C++ fundamental type.
- * Params:
- * t = type to check
- * Returns:
- * true if it is a fundamental type
- */
- static bool isFundamentalType(Type *t)
- {
- // First check the target whether some specific ABI is being followed.
- bool isFundamental;
- if (target.cpp.fundamentalType(t, isFundamental))
- return isFundamental;
- if (t->ty == Tenum)
- {
- // Peel off enum type from special types.
- TypeEnum *te = (TypeEnum *)t;
- if (te->sym->isSpecial())
- t = te->sym->getMemtype(Loc());
- }
-
- // Fundamental arithmetic types:
- // 1. integral types: bool, char, int, ...
- // 2. floating point types: float, double, real
- // 3. void
- // 4. null pointer: std::nullptr_t (since C++11)
- if (t->ty == Tvoid || t->ty == Tbool)
- return true;
- else if (t->ty == Tnull && global.params.cplusplus >= CppStdRevisionCpp11)
- return true;
- else
- return t->isTypeBasic() && (t->isintegral() || t->isreal());
- }
-
- /******************************
- * Write the mangled representation of the template arguments.
- * Params:
- * ti = the template instance
- */
- void template_args(TemplateInstance *ti)
- {
- /* <template-args> ::= I <template-arg>+ E
- */
- if (!ti) // could happen if std::basic_string is not a template
- return;
- buf->writeByte('I');
- for (size_t i = 0; i < ti->tiargs->length; i++)
- {
- RootObject *o = (*ti->tiargs)[i];
- TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration();
- assert(td);
- TemplateParameter *tp = (*td->parameters)[i];
-
- /*
- * <template-arg> ::= <type> # type or template
- * ::= X <expression> E # expression
- * ::= <expr-primary> # simple expressions
- * ::= I <template-arg>* E # argument pack
- */
- if (tp->isTemplateTupleParameter())
- {
- buf->writeByte('I'); // argument pack
-
- // mangle the rest of the arguments as types
- for (size_t j = i; j < ti->tiargs->length; j++)
- {
- Type *t = isType((*ti->tiargs)[j]);
- assert(t);
- t->accept(this);
- }
-
- buf->writeByte('E');
- break;
- }
- if (tp->isTemplateTypeParameter())
- {
- Type *t = isType(o);
- assert(t);
- t->accept(this);
- }
- else if (TemplateValueParameter *tv = tp->isTemplateValueParameter())
- {
- // <expr-primary> ::= L <type> <value number> E # integer literal
- if (tv->valType->isintegral())
- {
- Expression *e = isExpression(o);
- assert(e);
- buf->writeByte('L');
- tv->valType->accept(this);
- uinteger_t val = e->toUInteger();
- if (!tv->valType->isunsigned() && (sinteger_t)val < 0)
- {
- val = -val;
- buf->writeByte('n');
- }
- buf->printf("%llu", val);
- buf->writeByte('E');
- }
- else
- {
- ti->error("Internal Compiler Error: C++ `%s` template value parameter is not supported", tv->valType->toChars());
- fatal();
- }
- }
- else if (tp->isTemplateAliasParameter())
- {
- Dsymbol *d = isDsymbol(o);
- Expression *e = isExpression(o);
- if (d && d->isFuncDeclaration())
- {
- bool is_nested = d->toParent3() &&
- !d->toParent3()->isModule() &&
- ((TypeFunction*)d->isFuncDeclaration()->type)->linkage == LINKcpp;
- if (is_nested)
- buf->writeByte('X');
- buf->writeByte('L');
- mangle_function(d->isFuncDeclaration());
- buf->writeByte('E');
- if (is_nested)
- buf->writeByte('E');
- }
- else if (e && e->op == TOKvar && ((VarExp*)e)->var->isVarDeclaration())
- {
- VarDeclaration *vd = ((VarExp*)e)->var->isVarDeclaration();
- buf->writeByte('L');
- mangle_variable(vd, true);
- buf->writeByte('E');
- }
- else if (d && d->isTemplateDeclaration() && d->isTemplateDeclaration()->onemember)
- {
- if (!substitute(d))
- {
- cpp_mangle_name(d, false);
- }
- }
- else
- {
- ti->error("Internal Compiler Error: `%s` is unsupported parameter for C++ template", o->toChars());
- fatal();
- }
- }
- else if (tp->isTemplateThisParameter())
- {
- ti->error("Internal Compiler Error: C++ `%s` template this parameter is not supported", o->toChars());
- fatal();
- }
- else
- {
- assert(0);
- }
- }
- buf->writeByte('E');
- }
-
- void source_name(Dsymbol *s)
- {
- //printf("source_name(%s)\n", s->toChars());
- if (TemplateInstance *ti = s->isTemplateInstance())
- {
- if (!substitute(ti->tempdecl))
- {
- append(ti->tempdecl);
- const char *name = ti->tempdecl->toAlias()->ident->toChars();
- buf->printf("%d", strlen(name));
- buf->writestring(name);
- }
- template_args(ti);
- }
- else
- {
- const char *name = s->ident->toChars();
- buf->printf("%d", strlen(name));
- buf->writestring(name);
- }
- }
-
- /********
- * See if s is actually an instance of a template
- * Params:
- * s = symbol
- * Returns:
- * if s is instance of a template, return the instance, otherwise return s
- */
- Dsymbol *getInstance(Dsymbol *s)
- {
- Dsymbol *p = s->toParent3();
- if (p)
- {
- if (TemplateInstance *ti = p->isTemplateInstance())
- return ti;
- }
- return s;
- }
-
- /********
- * Get qualifier for `s`, meaning the symbol
- * that s is in the symbol table of.
- * The module does not count as a qualifier, because C++
- * does not have modules.
- * Params:
- * s = symbol that may have a qualifier
- * Returns:
- * qualifier, NULL if none
- */
- static Dsymbol *getQualifier(Dsymbol *s)
- {
- Dsymbol *p = s->toParent3();
- return (p && !p->isModule()) ? p : NULL;
- }
-
- // Detect type char
- static bool isChar(RootObject *o)
- {
- Type *t = isType(o);
- return (t && t->equals(Type::tchar));
- }
-
- // Detect type ::std::char_traits<char>
- static bool isChar_traits_char(RootObject *o)
- {
- return isIdent_char(Id::char_traits, o);
- }
-
- // Detect type ::std::allocator<char>
- static bool isAllocator_char(RootObject *o)
- {
- return isIdent_char(Id::allocator, o);
- }
-
- // Detect type ::std::ident<char>
- static bool isIdent_char(Identifier *ident, RootObject *o)
- {
- Type *t = isType(o);
- if (!t || t->ty != Tstruct)
- return false;
- Dsymbol *s = ((TypeStruct*)t)->toDsymbol(NULL);
- if (s->ident != ident)
- return false;
- Dsymbol *p = s->toParent3();
- if (!p)
- return false;
- TemplateInstance *ti = p->isTemplateInstance();
- if (!ti)
- return false;
- Dsymbol *q = getQualifier(ti);
- return isStd(q) && ti->tiargs->length == 1 && isChar((*ti->tiargs)[0]);
- }
-
- /***
- * Detect template args <char, ::std::char_traits<char>>
- * and write st if found.
- * Returns:
- * true if found
- */
- bool char_std_char_traits_char(TemplateInstance *ti, const char *st)
- {
- if (ti->tiargs->length == 2 &&
- isChar((*ti->tiargs)[0]) &&
- isChar_traits_char((*ti->tiargs)[1]))
- {
- buf->writestring(st);
- return true;
- }
- return false;
- }
-
-
- void prefix_name(Dsymbol *s)
- {
- //printf("prefix_name(%s)\n", s->toChars());
- if (!substitute(s))
- {
- Dsymbol *si = getInstance(s);
- Dsymbol *p = getQualifier(si);
- if (p)
- {
- if (isStd(p))
- {
- TemplateInstance *ti = si->isTemplateInstance();
- if (ti)
- {
- if (s->ident == Id::allocator)
- {
- buf->writestring("Sa");
- template_args(ti);
- append(ti);
- return;
- }
- if (s->ident == Id::basic_string)
- {
- // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
- if (ti->tiargs->length == 3 &&
- isChar((*ti->tiargs)[0]) &&
- isChar_traits_char((*ti->tiargs)[1]) &&
- isAllocator_char((*ti->tiargs)[2]))
-
- {
- buf->writestring("Ss");
- return;
- }
- buf->writestring("Sb"); // ::std::basic_string
- template_args(ti);
- append(ti);
- return;
- }
-
- // ::std::basic_istream<char, ::std::char_traits<char>>
- if (s->ident == Id::basic_istream &&
- char_std_char_traits_char(ti, "Si"))
- return;
-
- // ::std::basic_ostream<char, ::std::char_traits<char>>
- if (s->ident == Id::basic_ostream &&
- char_std_char_traits_char(ti, "So"))
- return;
-
- // ::std::basic_iostream<char, ::std::char_traits<char>>
- if (s->ident == Id::basic_iostream &&
- char_std_char_traits_char(ti, "Sd"))
- return;
- }
- buf->writestring("St");
- }
- else
- prefix_name(p);
- }
- source_name(si);
- if (!isStd(si))
- {
- /* Do this after the source_name() call to keep components[]
- * in the right order.
- * https://issues.dlang.org/show_bug.cgi?id=17947
- */
- append(si);
- }
- }
- }
-
- void cpp_mangle_name(Dsymbol *s, bool qualified)
- {
- //printf("cpp_mangle_name(%s, %d)\n", s->toChars(), qualified);
- Dsymbol *p = s->toParent3();
- Dsymbol *se = s;
- bool write_prefix = true;
- if (p && p->isTemplateInstance())
- {
- se = p;
- if (find(p->isTemplateInstance()->tempdecl) >= 0)
- write_prefix = false;
- p = p->toParent3();
- }
-
- if (p && !p->isModule())
- {
- /* The N..E is not required if:
- * 1. the parent is 'std'
- * 2. 'std' is the initial qualifier
- * 3. there is no CV-qualifier or a ref-qualifier for a member function
- * ABI 5.1.8
- */
- if (isStd(p) && !qualified)
- {
- TemplateInstance *ti = se->isTemplateInstance();
- if (s->ident == Id::allocator)
- {
- buf->writestring("Sa"); // "Sa" is short for ::std::allocator
- template_args(ti);
- }
- else if (s->ident == Id::basic_string)
- {
- // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
- if (ti->tiargs->length == 3 &&
- isChar((*ti->tiargs)[0]) &&
- isChar_traits_char((*ti->tiargs)[1]) &&
- isAllocator_char((*ti->tiargs)[2]))
-
- {
- buf->writestring("Ss");
- return;
- }
- buf->writestring("Sb"); // ::std::basic_string
- template_args(ti);
- }
- else
- {
- // ::std::basic_istream<char, ::std::char_traits<char>>
- if (s->ident == Id::basic_istream)
- {
- if (char_std_char_traits_char(ti, "Si"))
- return;
- }
- else if (s->ident == Id::basic_ostream)
- {
- if (char_std_char_traits_char(ti, "So"))
- return;
- }
- else if (s->ident == Id::basic_iostream)
- {
- if (char_std_char_traits_char(ti, "Sd"))
- return;
- }
- buf->writestring("St");
- source_name(se);
- }
- }
- else
- {
- buf->writeByte('N');
- if (write_prefix)
- prefix_name(p);
- source_name(se);
- buf->writeByte('E');
- }
- }
- else
- source_name(se);
- append(s);
- }
-
- void CV_qualifiers(Type *t)
- {
- // CV-qualifiers are 'r': restrict, 'V': volatile, 'K': const
- if (t->isConst())
- buf->writeByte('K');
- }
-
- void mangle_variable(VarDeclaration *d, bool is_temp_arg_ref)
- {
- // fake mangling for fields to fix https://issues.dlang.org/show_bug.cgi?id=16525
- if (!(d->storage_class & (STCextern | STCfield | STCgshared)))
- {
- d->error("Internal Compiler Error: C++ static non-`__gshared` non-`extern` variables not supported");
- fatal();
- }
-
- Dsymbol *p = d->toParent3();
- if (p && !p->isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE"
- {
- buf->writestring("_ZN");
- prefix_name(p);
- source_name(d);
- buf->writeByte('E');
- }
- else //char beta[6] should mangle as "beta"
- {
- if (!is_temp_arg_ref)
- {
- buf->writestring(d->ident->toChars());
- }
- else
- {
- buf->writestring("_Z");
- source_name(d);
- }
- }
- }
-
- void mangle_function(FuncDeclaration *d)
- {
- //printf("mangle_function(%s)\n", d->toChars());
- /*
- * <mangled-name> ::= _Z <encoding>
- */
- buf->writestring("_Z");
- this->mangle_function_encoding(d);
- }
-
- void mangle_function_encoding(FuncDeclaration *d)
- {
- //printf("mangle_function_encoding(%s)\n", d->toChars());
- /*
- * <encoding> ::= <function name> <bare-function-type>
- * ::= <data name>
- * ::= <special-name>
- */
- TypeFunction *tf = (TypeFunction *)d->type;
-
- if (getFuncTemplateDecl(d))
- {
- /* It's an instance of a function template
- */
- TemplateInstance *ti = d->parent->isTemplateInstance();
- assert(ti);
- Dsymbol *p = ti->toParent3();
- if (p && !p->isModule() && tf->linkage == LINKcpp)
- {
- buf->writeByte('N');
- CV_qualifiers(d->type);
- prefix_name(p);
- if (d->isCtorDeclaration())
- buf->writestring("C1");
- else if (d->isDtorDeclaration())
- buf->writestring("D1");
- else
- source_name(ti);
- buf->writeByte('E');
- }
- else
- source_name(ti);
- headOfType(tf->nextOf()); // mangle return type
- }
- else
- {
- Dsymbol *p = d->toParent3();
- if (p && !p->isModule() && tf->linkage == LINKcpp)
- {
- /* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
- * ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
- */
- buf->writeByte('N');
- CV_qualifiers(d->type);
-
- /* <prefix> ::= <prefix> <unqualified-name>
- * ::= <template-prefix> <template-args>
- * ::= <template-param>
- * ::= # empty
- * ::= <substitution>
- * ::= <prefix> <data-member-prefix>
- */
- prefix_name(p);
- //printf("p: %s\n", buf.peekChars());
-
- if (d->isCtorDeclaration())
- {
- buf->writestring("C1");
- }
- else if (d->isDtorDeclaration())
- {
- buf->writestring("D1");
- }
- else
- {
- source_name(d);
- }
- buf->writeByte('E');
- }
- else
- {
- source_name(d);
- }
- }
-
- if (tf->linkage == LINKcpp) //Template args accept extern "C" symbols with special mangling
- {
- assert(tf->ty == Tfunction);
- mangleFunctionParameters(tf->parameterList.parameters,
- tf->parameterList.varargs);
- }
- }
-
- void mangleFunctionParameters(Parameters *parameters, int varargs)
- {
- struct ParamsCppMangle
- {
- int numparams;
- CppMangleVisitor *mangler;
-
- static int dg(void *ctx, size_t, Parameter *fparam)
- {
- ParamsCppMangle *p = (ParamsCppMangle *)ctx;
- CppMangleVisitor *mangler = p->mangler;
- Type *t = target.cpp.parameterType(fparam);
- if (t->ty == Tsarray)
- {
- // Static arrays in D are passed by value; no counterpart in C++
- t->error(mangler->loc, "Internal Compiler Error: unable to pass static array `%s` to extern(C++) function, use pointer instead",
- t->toChars());
- fatal();
- }
- mangler->headOfType(t);
- p->numparams++;
- return 0;
- }
- };
-
- ParamsCppMangle p;
- p.numparams = 0;
- p.mangler = this;
-
- if (parameters)
- Parameter_foreach(parameters, &ParamsCppMangle::dg, (void*)&p);
-
- if (varargs)
- buf->writeByte('z');
- else if (!p.numparams)
- buf->writeByte('v'); // encode (void) parameters
- }
-
-public:
- CppMangleVisitor(OutBuffer *buf, Loc loc)
- : components(), buf(buf), loc(loc)
- {
- }
-
- /*****
- * Entry point. Append mangling to buf[]
- * Params:
- * s = symbol to mangle
- */
- void mangleOf(Dsymbol *s)
- {
- if (VarDeclaration *vd = s->isVarDeclaration())
- {
- mangle_variable(vd, false);
- }
- else if (FuncDeclaration *fd = s->isFuncDeclaration())
- {
- mangle_function(fd);
- }
- else
- {
- assert(0);
- }
- }
-
- /****** The rest is type mangling ************/
-
- void error(Type *t)
- {
- const char *p;
- if (t->isImmutable())
- p = "`immutable` ";
- else if (t->isShared())
- p = "`shared` ";
- else
- p = "";
- t->error(loc, "Internal Compiler Error: %stype `%s` can not be mapped to C++", p, t->toChars());
- fatal(); //Fatal, because this error should be handled in frontend
- }
-
- /****************************
- * Mangle a type,
- * treating it as a Head followed by a Tail.
- * Params:
- * t = Head of a type
- */
- void headOfType(Type *t)
- {
- if (t->ty == Tclass)
- {
- mangleTypeClass((TypeClass*)t, true);
- }
- else
- {
- // For value types, strip const/immutable/shared from the head of the type
- t->mutableOf()->unSharedOf()->accept(this);
- }
- }
-
- void visit(Type *t)
- {
- error(t);
- }
-
- /******
- * Write out 1 or 2 character basic type mangling.
- * Handle const and substitutions.
- * Params:
- * t = type to mangle
- * p = if not 0, then character prefix
- * c = mangling character
- */
- void writeBasicType(Type *t, char p, char c)
- {
- // Only do substitutions for non-fundamental types.
- if (!isFundamentalType(t) || t->isConst())
- {
- if (substitute(t))
- return;
- else
- append(t);
- }
- CV_qualifiers(t);
- if (p)
- buf->writeByte(p);
- buf->writeByte(c);
- }
-
- void visit(TypeNull *t)
- {
- if (t->isImmutable() || t->isShared())
- return error(t);
-
- writeBasicType(t, 'D', 'n');
- }
-
- void visit(TypeNoreturn *t)
- {
- if (t->isImmutable() || t->isShared())
- return error(t);
-
- writeBasicType(t, 0, 'v'); // mangle like `void`
- }
-
- void visit(TypeBasic *t)
- {
- if (t->isImmutable() || t->isShared())
- return error(t);
-
- // Handle any target-specific basic types.
- if (const char *tm = target.cpp.typeMangle(t))
- {
- // Only do substitutions for non-fundamental types.
- if (!isFundamentalType(t) || t->isConst())
- {
- if (substitute(t))
- return;
- else
- append(t);
- }
- CV_qualifiers(t);
- buf->writestring(tm);
- return;
- }
-
- /* <builtin-type>:
- * v void
- * w wchar_t
- * b bool
- * c char
- * a signed char
- * h unsigned char
- * s short
- * t unsigned short
- * i int
- * j unsigned int
- * l long
- * m unsigned long
- * x long long, __int64
- * y unsigned long long, __int64
- * n __int128
- * o unsigned __int128
- * f float
- * d double
- * e long double, __float80
- * g __float128
- * z ellipsis
- * Dd 64 bit IEEE 754r decimal floating point
- * De 128 bit IEEE 754r decimal floating point
- * Df 32 bit IEEE 754r decimal floating point
- * Dh 16 bit IEEE 754r half-precision floating point
- * Di char32_t
- * Ds char16_t
- * u <source-name> # vendor extended type
- */
-
- char c;
- char p = 0;
- switch (t->ty)
- {
- case Tvoid: c = 'v'; break;
- case Tint8: c = 'a'; break;
- case Tuns8: c = 'h'; break;
- case Tint16: c = 's'; break;
- case Tuns16: c = 't'; break;
- case Tint32: c = 'i'; break;
- case Tuns32: c = 'j'; break;
- case Tfloat32: c = 'f'; break;
- case Tint64:
- c = (target.c.longsize == 8 ? 'l' : 'x');
- break;
- case Tuns64:
- c = (target.c.longsize == 8 ? 'm' : 'y');
- break;
- case Tint128: c = 'n'; break;
- case Tuns128: c = 'o'; break;
- case Tfloat64: c = 'd'; break;
- case Tfloat80: c = 'e'; break;
- case Tbool: c = 'b'; break;
- case Tchar: c = 'c'; break;
- case Twchar: c = 't'; break; // unsigned short (perhaps use 'Ds' ?
- case Tdchar: c = 'w'; break; // wchar_t (UTF-32) (perhaps use 'Di' ?
- case Timaginary32: p = 'G'; c = 'f'; break; // 'G' means imaginary
- case Timaginary64: p = 'G'; c = 'd'; break;
- case Timaginary80: p = 'G'; c = 'e'; break;
- case Tcomplex32: p = 'C'; c = 'f'; break; // 'C' means complex
- case Tcomplex64: p = 'C'; c = 'd'; break;
- case Tcomplex80: p = 'C'; c = 'e'; break;
-
- default:
- return error(t);
- }
- writeBasicType(t, p, c);
- }
-
- void visit(TypeVector *t)
- {
- if (t->isImmutable() || t->isShared())
- return error(t);
-
- if (substitute(t))
- return;
- append(t);
- CV_qualifiers(t);
-
- // Handle any target-specific vector types.
- if (const char *tm = target.cpp.typeMangle(t))
- {
- buf->writestring(tm);
- }
- else
- {
- assert(t->basetype && t->basetype->ty == Tsarray);
- assert(((TypeSArray *)t->basetype)->dim);
- buf->writestring("U8__vector"); //-- Gnu ABI v.3
- t->basetype->nextOf()->accept(this);
- }
- }
-
- void visit(TypeSArray *t)
- {
- if (t->isImmutable() || t->isShared())
- return error(t);
-
- if (!substitute(t))
- append(t);
- CV_qualifiers(t);
- buf->writeByte('A');
- buf->printf("%llu", t->dim ? t->dim->toInteger() : 0);
- buf->writeByte('_');
- t->next->accept(this);
- }
-
- void visit(TypePointer *t)
- {
- if (t->isImmutable() || t->isShared())
- return error(t);
-
- if (substitute(t))
- return;
- CV_qualifiers(t);
- buf->writeByte('P');
- t->next->accept(this);
- append(t);
- }
-
- void visit(TypeReference *t)
- {
- //printf("TypeReference %s\n", t->toChars());
- if (substitute(t))
- return;
- buf->writeByte('R');
- t->next->accept(this);
- append(t);
- }
-
- void visit(TypeFunction *t)
- {
- /*
- * <function-type> ::= F [Y] <bare-function-type> E
- * <bare-function-type> ::= <signature type>+
- * # types are possible return type, then parameter types
- */
-
- /* ABI says:
- "The type of a non-static member function is considered to be different,
- for the purposes of substitution, from the type of a namespace-scope or
- static member function whose type appears similar. The types of two
- non-static member functions are considered to be different, for the
- purposes of substitution, if the functions are members of different
- classes. In other words, for the purposes of substitution, the class of
- which the function is a member is considered part of the type of
- function."
-
- BUG: Right now, types of functions are never merged, so our simplistic
- component matcher always finds them to be different.
- We should use Type::equals on these, and use different
- TypeFunctions for non-static member functions, and non-static
- member functions of different classes.
- */
- if (substitute(t))
- return;
- buf->writeByte('F');
- if (t->linkage == LINKc)
- buf->writeByte('Y');
- Type *tn = t->next;
- if (t->isref)
- tn = tn->referenceTo();
- tn->accept(this);
- mangleFunctionParameters(t->parameterList.parameters,
- t->parameterList.varargs);
- buf->writeByte('E');
- append(t);
- }
-
- void visit(TypeStruct *t)
- {
- if (t->isImmutable() || t->isShared())
- return error(t);
-
- //printf("TypeStruct %s\n", t->toChars());
- doSymbol(t);
- }
-
-
- void visit(TypeEnum *t)
- {
- if (t->isImmutable() || t->isShared())
- return error(t);
-
- /* __c_(u)long(long) and others get special mangling
- */
- Identifier *id = t->sym->ident;
- //printf("enum id = '%s'\n", id->toChars());
- if (id == Id::__c_long)
- return writeBasicType(t, 0, 'l');
- else if (id == Id::__c_ulong)
- return writeBasicType(t, 0, 'm');
- else if (id == Id::__c_wchar_t)
- return writeBasicType(t, 0, 'w');
- else if (id == Id::__c_longlong)
- return writeBasicType(t, 0, 'x');
- else if (id == Id::__c_ulonglong)
- return writeBasicType(t, 0, 'y');
- else if (id == Id::__c_complex_float)
- return writeBasicType(t, 'C', 'f');
- else if (id == Id::__c_complex_double)
- return writeBasicType(t, 'C', 'd');
- else if (id == Id::__c_complex_real)
- return writeBasicType(t, 'C', 'e');
-
- doSymbol(t);
- }
-
- /****************
- * Write structs and enums.
- * Params:
- * t = TypeStruct or TypeEnum
- */
- void doSymbol(Type *t)
- {
- if (substitute(t))
- return;
- CV_qualifiers(t);
-
- // Handle any target-specific struct types.
- if (const char *tm = target.cpp.typeMangle(t))
- {
- buf->writestring(tm);
- }
- else
- {
- Dsymbol *s = t->toDsymbol(NULL);
- Dsymbol *p = s->toParent3();
- if (p && p->isTemplateInstance())
- {
- /* https://issues.dlang.org/show_bug.cgi?id=17947
- * Substitute the template instance symbol, not the struct/enum symbol
- */
- if (substitute(p))
- return;
- }
- if (!substitute(s))
- {
- cpp_mangle_name(s, t->isConst());
- }
- }
- if (t->isConst())
- append(t);
- }
-
- void visit(TypeClass *t)
- {
- mangleTypeClass(t, false);
- }
-
- /************************
- * Mangle a class type.
- * If it's the head, treat the initial pointer as a value type.
- * Params:
- * t = class type
- * head = true for head of a type
- */
- void mangleTypeClass(TypeClass *t, bool head)
- {
- if (t->isImmutable() || t->isShared())
- return error(t);
-
- /* Mangle as a <pointer to><struct>
- */
- if (substitute(t))
- return;
- if (!head)
- CV_qualifiers(t);
- buf->writeByte('P');
-
- CV_qualifiers(t);
-
- {
- Dsymbol *s = t->toDsymbol(NULL);
- Dsymbol *p = s->toParent3();
- if (p && p->isTemplateInstance())
- {
- /* https://issues.dlang.org/show_bug.cgi?id=17947
- * Substitute the template instance symbol, not the class symbol
- */
- if (substitute(p))
- return;
- }
- }
-
- if (!substitute(t->sym))
- {
- cpp_mangle_name(t->sym, t->isConst());
- }
- if (t->isConst())
- append(NULL); // C++ would have an extra type here
- append(t);
- }
-
- const char *mangle_typeinfo(Dsymbol *s)
- {
- buf->writestring("_ZTI");
- cpp_mangle_name(s, false);
- return buf->extractChars();
- }
-};
-
-const char *toCppMangleItanium(Dsymbol *s)
-{
- //printf("toCppMangleItanium(%s)\n", s->toChars());
- OutBuffer buf;
- CppMangleVisitor v(&buf, s->loc);
- v.mangleOf(s);
- return buf.extractChars();
-}
-
-const char *cppTypeInfoMangleItanium(Dsymbol *s)
-{
- //printf("cppTypeInfoMangleItanium(%s)\n", s->toChars());
- OutBuffer buf;
- buf.writestring("_ZTI"); // "TI" means typeinfo structure
- CppMangleVisitor v(&buf, s->loc);
- v.cpp_mangle_name(s, false);
- return buf.extractChars();
-}
-
-const char *cppThunkMangleItanium(FuncDeclaration *fd, int offset)
-{
- //printf("cppThunkMangleItanium(%s)\n", fd.toChars());
- OutBuffer buf;
- buf.printf("_ZThn%u_", offset); // "Th" means thunk, "n%u" is the call offset
- CppMangleVisitor v(&buf, fd->loc);
- v.mangle_function_encoding(fd);
- return buf.extractChars();
-}
diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/cppmangle.d
new file mode 100644
index 00000000000..0381f9ad6a4
--- /dev/null
+++ b/gcc/d/dmd/cppmangle.d
@@ -0,0 +1,2540 @@
+/**
+ * Do mangling for C++ linkage.
+ *
+ * This is the POSIX side of the implementation.
+ * It exports two functions to C++, `toCppMangleItanium` and `cppTypeInfoMangleItanium`.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d, _cppmangle.d)
+ * Documentation: https://dlang.org/phobos/dmd_cppmangle.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cppmangle.d
+ *
+ * References:
+ * Follows Itanium C++ ABI 1.86 section 5.1
+ * http://refspecs.linux-foundation.org/cxxabi-1.86.html#mangling
+ * which is where the grammar comments come from.
+ *
+ * Bugs:
+ * https://issues.dlang.org/query.cgi
+ * enter `C++, mangling` as the keywords.
+ */
+
+module dmd.cppmangle;
+
+import core.stdc.string;
+import core.stdc.stdio;
+
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.declaration;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.nspace;
+import dmd.root.array;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.target;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.visitor;
+
+
+// helper to check if an identifier is a C++ operator
+enum CppOperator { Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign, Unknown }
+package CppOperator isCppOperator(Identifier id)
+{
+ __gshared const(Identifier)[] operators = null;
+ if (!operators)
+ operators = [Id._cast, Id.assign, Id.eq, Id.index, Id.call, Id.opUnary, Id.opBinary, Id.opOpAssign];
+ foreach (i, op; operators)
+ {
+ if (op == id)
+ return cast(CppOperator)i;
+ }
+ return CppOperator.Unknown;
+}
+
+///
+extern(C++) const(char)* toCppMangleItanium(Dsymbol s)
+{
+ //printf("toCppMangleItanium(%s)\n", s.toChars());
+ OutBuffer buf;
+ scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc);
+ v.mangleOf(s);
+ return buf.extractChars();
+}
+
+///
+extern(C++) const(char)* cppTypeInfoMangleItanium(Dsymbol s)
+{
+ //printf("cppTypeInfoMangle(%s)\n", s.toChars());
+ OutBuffer buf;
+ buf.writestring("_ZTI"); // "TI" means typeinfo structure
+ scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc);
+ v.cpp_mangle_name(s, false);
+ return buf.extractChars();
+}
+
+///
+extern(C++) const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset)
+{
+ //printf("cppThunkMangleItanium(%s)\n", fd.toChars());
+ OutBuffer buf;
+ buf.printf("_ZThn%u_", offset); // "Th" means thunk, "n%u" is the call offset
+ scope CppMangleVisitor v = new CppMangleVisitor(&buf, fd.loc);
+ v.mangle_function_encoding(fd);
+ return buf.extractChars();
+}
+
+/******************************
+ * Determine if sym is the 'primary' destructor, that is,
+ * the most-aggregate destructor (the one that is defined as __xdtor)
+ * Params:
+ * sym = Dsymbol
+ * Returns:
+ * true if sym is the primary destructor for an aggregate
+ */
+bool isPrimaryDtor(const Dsymbol sym)
+{
+ const dtor = sym.isDtorDeclaration();
+ if (!dtor)
+ return false;
+ const ad = dtor.isMember();
+ assert(ad);
+ return dtor == ad.primaryDtor;
+}
+
+/// Context used when processing pre-semantic AST
+private struct Context
+{
+ /// Template instance of the function being mangled
+ TemplateInstance ti;
+ /// Function declaration we're mangling
+ FuncDeclaration fd;
+ /// Current type / expression being processed (semantically analyzed)
+ RootObject res;
+
+ @disable ref Context opAssign(ref Context other);
+ @disable ref Context opAssign(Context other);
+
+ /**
+ * Helper function to track `res`
+ *
+ * Params:
+ * next = Value to set `this.res` to.
+ * If `this.res` is `null`, the expression is not evalutated.
+ * This allow this code to be used even when no context is needed.
+ *
+ * Returns:
+ * The previous state of this `Context` object
+ */
+ private Context push(lazy RootObject next)
+ {
+ auto r = this.res;
+ if (r !is null)
+ this.res = next;
+ return Context(this.ti, this.fd, r);
+ }
+
+ /**
+ * Reset the context to a previous one, making any adjustment necessary
+ */
+ private void pop(ref Context prev)
+ {
+ this.res = prev.res;
+ }
+}
+
+private final class CppMangleVisitor : Visitor
+{
+ /// Context used when processing pre-semantic AST
+ private Context context;
+
+ ABITagContainer abiTags; /// Container for already-written ABI tags
+ Objects components; /// array of components available for substitution
+ OutBuffer* buf; /// append the mangling to buf[]
+ Loc loc; /// location for use in error messages
+
+ /**
+ * Constructor
+ *
+ * Params:
+ * buf = `OutBuffer` to write the mangling to
+ * loc = `Loc` of the symbol being mangled
+ */
+ this(OutBuffer* buf, Loc loc)
+ {
+ this.buf = buf;
+ this.loc = loc;
+ }
+
+ /*****
+ * Entry point. Append mangling to buf[]
+ * Params:
+ * s = symbol to mangle
+ */
+ void mangleOf(Dsymbol s)
+ {
+ if (VarDeclaration vd = s.isVarDeclaration())
+ {
+ mangle_variable(vd, vd.cppnamespace !is null);
+ }
+ else if (FuncDeclaration fd = s.isFuncDeclaration())
+ {
+ mangle_function(fd);
+ }
+ else
+ {
+ assert(0);
+ }
+ }
+
+ /**
+ * Mangle the return type of a function
+ *
+ * This is called on a templated function type.
+ * Context is set to the `FuncDeclaration`.
+ *
+ * Params:
+ * preSemantic = the `FuncDeclaration`'s `originalType`
+ */
+ void mangleReturnType(TypeFunction preSemantic)
+ {
+ auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type;
+ Type rt = preSemantic.nextOf();
+ if (tf.isref)
+ rt = rt.referenceTo();
+ auto prev = this.context.push(tf.nextOf());
+ scope (exit) this.context.pop(prev);
+ this.headOfType(rt);
+ }
+
+ /**
+ * Write a seq-id from an index number, excluding the terminating '_'
+ *
+ * Params:
+ * idx = the index in a substitution list.
+ * Note that index 0 has no value, and `S0_` would be the
+ * substitution at index 1 in the list.
+ *
+ * See-Also:
+ * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id
+ */
+ private void writeSequenceFromIndex(size_t idx)
+ {
+ if (idx)
+ {
+ void write_seq_id(size_t i)
+ {
+ if (i >= 36)
+ {
+ write_seq_id(i / 36);
+ i %= 36;
+ }
+ i += (i < 10) ? '0' : 'A' - 10;
+ buf.writeByte(cast(char)i);
+ }
+
+ write_seq_id(idx - 1);
+ }
+ }
+
+ /**
+ * Attempt to perform substitution on `p`
+ *
+ * If `p` already appeared in the mangling, it is stored as
+ * a 'part', and short references in the form of `SX_` can be used.
+ * Note that `p` can be anything: template declaration, struct declaration,
+ * class declaration, namespace...
+ *
+ * Params:
+ * p = The object to attempt to substitute
+ * nested = Whether or not `p` is to be considered nested.
+ * When `true`, `N` will be prepended before the substitution.
+ *
+ * Returns:
+ * Whether `p` already appeared in the mangling,
+ * and substitution has been written to `this.buf`.
+ */
+ bool substitute(RootObject p, bool nested = false)
+ {
+ //printf("substitute %s\n", p ? p.toChars() : null);
+ auto i = find(p);
+ if (i < 0)
+ return false;
+
+ //printf("\tmatch\n");
+ /* Sequence is S_, S0_, .., S9_, SA_, ..., SZ_, S10_, ...
+ */
+ if (nested)
+ buf.writeByte('N');
+ buf.writeByte('S');
+ writeSequenceFromIndex(i);
+ buf.writeByte('_');
+ return true;
+ }
+
+ /******
+ * See if `p` exists in components[]
+ *
+ * Note that components can contain `null` entries,
+ * as the index used in mangling is based on the index in the array.
+ *
+ * If called with an object whose dynamic type is `Nspace`,
+ * calls the `find(Nspace)` overload.
+ *
+ * Returns:
+ * index if found, -1 if not
+ */
+ int find(RootObject p)
+ {
+ //printf("find %p %d %s\n", p, p.dyncast(), p ? p.toChars() : null);
+ scope v = new ComponentVisitor(p);
+ foreach (i, component; components)
+ {
+ if (component)
+ component.visitObject(v);
+ if (v.result)
+ return cast(int)i;
+ }
+ return -1;
+ }
+
+ /*********************
+ * Append p to components[]
+ */
+ void append(RootObject p)
+ {
+ //printf("append %p %d %s\n", p, p.dyncast(), p ? p.toChars() : "null");
+ components.push(p);
+ }
+
+ /**
+ * Write an identifier preceded by its length
+ *
+ * Params:
+ * ident = `Identifier` to write to `this.buf`
+ */
+ void writeIdentifier(const ref Identifier ident)
+ {
+ const name = ident.toString();
+ this.buf.print(name.length);
+ this.buf.writestring(name);
+ }
+
+ /**
+ * Insert the leftover ABI tags to the buffer
+ *
+ * This inset ABI tags that hasn't already been written
+ * after the mangled name of the function.
+ * For more details, see the `abiTags` variable.
+ *
+ * Params:
+ * off = Offset to insert at
+ * fd = Type of the function to mangle the return type of
+ */
+ void writeRemainingTags(size_t off, TypeFunction tf)
+ {
+ scope remainingVisitor = new LeftoverVisitor(&this.abiTags.written);
+ tf.next.accept(remainingVisitor);
+ OutBuffer b2;
+ foreach (se; remainingVisitor.toWrite)
+ {
+ auto tag = se.peekString();
+ // We can only insert a slice, and each insert is a memmove,
+ // so use a temporary buffer to keep it efficient.
+ b2.reset();
+ b2.writestring("B");
+ b2.print(tag.length);
+ b2.writestring(tag);
+ this.buf.insert(off, b2[]);
+ off += b2.length;
+ }
+ }
+
+ /************************
+ * Determine if symbol is indeed the global ::std namespace.
+ * Params:
+ * s = symbol to check
+ * Returns:
+ * true if it is ::std
+ */
+ static bool isStd(Dsymbol s)
+ {
+ if (!s)
+ return false;
+
+ if (auto cnd = s.isCPPNamespaceDeclaration())
+ return isStd(cnd);
+
+ return (s.ident == Id.std && // the right name
+ s.isNspace() && // g++ disallows global "std" for other than a namespace
+ !getQualifier(s)); // at global level
+ }
+
+ /// Ditto
+ static bool isStd(CPPNamespaceDeclaration s)
+ {
+ return s && s.cppnamespace is null && s.ident == Id.std;
+ }
+
+ /************************
+ * Determine if type is a C++ fundamental type.
+ * Params:
+ * t = type to check
+ * Returns:
+ * true if it is a fundamental type
+ */
+ static bool isFundamentalType(Type t)
+ {
+ // First check the target whether some specific ABI is being followed.
+ bool isFundamental = void;
+ if (target.cpp.fundamentalType(t, isFundamental))
+ return isFundamental;
+
+ if (auto te = t.isTypeEnum())
+ {
+ // Peel off enum type from special types.
+ if (te.sym.isSpecial())
+ t = te.memType();
+ }
+
+ // Fundamental arithmetic types:
+ // 1. integral types: bool, char, int, ...
+ // 2. floating point types: float, double, real
+ // 3. void
+ // 4. null pointer: std::nullptr_t (since C++11)
+ if (t.ty == Tvoid || t.ty == Tbool)
+ return true;
+ else if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11)
+ return true;
+ else
+ return t.isTypeBasic() && (t.isintegral() || t.isreal());
+ }
+
+ /******************************
+ * Write the mangled representation of a template argument.
+ * Params:
+ * ti = the template instance
+ * arg = the template argument index
+ */
+ void template_arg(TemplateInstance ti, size_t arg)
+ {
+ TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
+ assert(td);
+ TemplateParameter tp = (*td.parameters)[arg];
+ RootObject o = (*ti.tiargs)[arg];
+
+ auto prev = this.context.push({
+ TemplateInstance parentti;
+ if (this.context.res.dyncast() == DYNCAST.dsymbol)
+ parentti = this.context.res.asFuncDecl().parent.isTemplateInstance();
+ else
+ parentti = this.context.res.asType().toDsymbol(null).parent.isTemplateInstance();
+ return (*parentti.tiargs)[arg];
+ }());
+ scope (exit) this.context.pop(prev);
+
+ if (tp.isTemplateTypeParameter())
+ {
+ Type t = isType(o);
+ assert(t);
+ t.accept(this);
+ }
+ else if (TemplateValueParameter tv = tp.isTemplateValueParameter())
+ {
+ // <expr-primary> ::= L <type> <value number> E # integer literal
+ if (tv.valType.isintegral())
+ {
+ Expression e = isExpression(o);
+ assert(e);
+ buf.writeByte('L');
+ tv.valType.accept(this);
+ auto val = e.toUInteger();
+ if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0)
+ {
+ val = -val;
+ buf.writeByte('n');
+ }
+ buf.print(val);
+ buf.writeByte('E');
+ }
+ else
+ {
+ ti.error("Internal Compiler Error: C++ `%s` template value parameter is not supported", tv.valType.toChars());
+ fatal();
+ }
+ }
+ else if (tp.isTemplateAliasParameter())
+ {
+ // Passing a function as alias parameter is the same as passing
+ // `&function`
+ Dsymbol d = isDsymbol(o);
+ Expression e = isExpression(o);
+ if (d && d.isFuncDeclaration())
+ {
+ // X .. E => template parameter is an expression
+ // 'ad' => unary operator ('&')
+ // L .. E => is a <expr-primary>
+ buf.writestring("XadL");
+ mangle_function(d.isFuncDeclaration());
+ buf.writestring("EE");
+ }
+ else if (e && e.op == TOK.variable && (cast(VarExp)e).var.isVarDeclaration())
+ {
+ VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration();
+ buf.writeByte('L');
+ mangle_variable(vd, true);
+ buf.writeByte('E');
+ }
+ else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember)
+ {
+ if (!substitute(d))
+ {
+ cpp_mangle_name(d, false);
+ }
+ }
+ else
+ {
+ ti.error("Internal Compiler Error: C++ `%s` template alias parameter is not supported", o.toChars());
+ fatal();
+ }
+ }
+ else if (tp.isTemplateThisParameter())
+ {
+ ti.error("Internal Compiler Error: C++ `%s` template this parameter is not supported", o.toChars());
+ fatal();
+ }
+ else
+ {
+ assert(0);
+ }
+ }
+
+ /******************************
+ * Write the mangled representation of the template arguments.
+ * Params:
+ * ti = the template instance
+ * firstArg = index of the first template argument to mangle
+ * (used for operator overloading)
+ * Returns:
+ * true if any arguments were written
+ */
+ bool template_args(TemplateInstance ti, int firstArg = 0)
+ {
+ /* <template-args> ::= I <template-arg>+ E
+ */
+ if (!ti || ti.tiargs.dim <= firstArg) // could happen if std::basic_string is not a template
+ return false;
+ buf.writeByte('I');
+ foreach (i; firstArg .. ti.tiargs.dim)
+ {
+ TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
+ assert(td);
+ TemplateParameter tp = (*td.parameters)[i];
+
+ /*
+ * <template-arg> ::= <type> # type or template
+ * ::= X <expression> E # expression
+ * ::= <expr-primary> # simple expressions
+ * ::= J <template-arg>* E # argument pack
+ *
+ * Reference: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.template-arg
+ */
+ if (TemplateTupleParameter tt = tp.isTemplateTupleParameter())
+ {
+ buf.writeByte('J'); // argument pack
+
+ // mangle the rest of the arguments as types
+ foreach (j; i .. (*ti.tiargs).dim)
+ {
+ Type t = isType((*ti.tiargs)[j]);
+ assert(t);
+ t.accept(this);
+ }
+
+ buf.writeByte('E');
+ break;
+ }
+
+ template_arg(ti, i);
+ }
+ buf.writeByte('E');
+ return true;
+ }
+
+ /**
+ * Write the symbol `p` if not null, then execute the delegate
+ *
+ * Params:
+ * p = Symbol to write
+ * dg = Delegate to execute
+ */
+ void writeChained(Dsymbol p, scope void delegate() dg)
+ {
+ if (p && !p.isModule())
+ {
+ buf.writestring("N");
+ source_name(p, true);
+ dg();
+ buf.writestring("E");
+ }
+ else
+ dg();
+ }
+
+ /**
+ * Write the name of `s` to the buffer
+ *
+ * Params:
+ * s = Symbol to write the name of
+ * haveNE = Whether `N..E` is already part of the mangling
+ * Because `Nspace` and `CPPNamespaceAttribute` can be
+ * mixed, this is a mandatory hack.
+ */
+ void source_name(Dsymbol s, bool haveNE = false)
+ {
+ version (none)
+ {
+ printf("source_name(%s)\n", s.toChars());
+ auto sl = this.buf.peekSlice();
+ assert(sl.length == 0 || haveNE || s.cppnamespace is null || sl != "_ZN");
+ }
+ auto ti = s.isTemplateInstance();
+
+ if (!ti)
+ {
+ auto ag = s.isAggregateDeclaration();
+ const ident = (ag && ag.mangleOverride) ? ag.mangleOverride.id : s.ident;
+ this.writeNamespace(s.cppnamespace, () {
+ this.writeIdentifier(ident);
+ this.abiTags.writeSymbol(s, this);
+ },
+ haveNE);
+ return;
+ }
+
+ bool needsTa = false;
+
+ // https://issues.dlang.org/show_bug.cgi?id=20413
+ // N..E is not needed when substituting members of the std namespace.
+ // This is observed in the GCC and Clang implementations.
+ // The Itanium specification is not clear enough on this specific case.
+ // References:
+ // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.name
+ // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression
+ Dsymbol q = getQualifier(ti.tempdecl);
+ Dsymbol ns = ti.tempdecl.cppnamespace;
+ const inStd = ns && isStd(ns) || q && isStd(q);
+ const isNested = !inStd && (ns || q);
+
+ if (substitute(ti.tempdecl, !haveNE && isNested))
+ {
+ template_args(ti);
+ if (!haveNE && isNested)
+ buf.writeByte('E');
+ return;
+ }
+ else if (this.writeStdSubstitution(ti, needsTa))
+ {
+ this.abiTags.writeSymbol(ti, this);
+ if (needsTa)
+ template_args(ti);
+ return;
+ }
+
+ auto ag = ti.aliasdecl ? ti.aliasdecl.isAggregateDeclaration() : null;
+ if (ag && ag.mangleOverride)
+ {
+ this.writeNamespace(
+ ti.toAlias().cppnamespace, () {
+ this.writeIdentifier(ag.mangleOverride.id);
+ if (ag.mangleOverride.agg && ag.mangleOverride.agg.isInstantiated())
+ {
+ auto to = ag.mangleOverride.agg.isInstantiated();
+ append(to);
+ this.abiTags.writeSymbol(to.tempdecl, this);
+ template_args(to);
+ }
+ }, haveNE);
+ }
+ else
+ {
+ this.writeNamespace(
+ s.cppnamespace, () {
+ this.writeIdentifier(ti.tempdecl.toAlias().ident);
+ append(ti.tempdecl);
+ this.abiTags.writeSymbol(ti.tempdecl, this);
+ template_args(ti);
+ }, haveNE);
+ }
+ }
+
+ /********
+ * See if s is actually an instance of a template
+ * Params:
+ * s = symbol
+ * Returns:
+ * if s is instance of a template, return the instance, otherwise return s
+ */
+ static Dsymbol getInstance(Dsymbol s)
+ {
+ Dsymbol p = s.toParent();
+ if (p)
+ {
+ if (TemplateInstance ti = p.isTemplateInstance())
+ return ti;
+ }
+ return s;
+ }
+
+ /// Get the namespace of a template instance
+ CPPNamespaceDeclaration getTiNamespace(TemplateInstance ti)
+ {
+ // If we receive a pre-semantic `TemplateInstance`,
+ // `cppnamespace` is always `null`
+ return ti.tempdecl ? ti.cppnamespace
+ : this.context.res.asType().toDsymbol(null).cppnamespace;
+ }
+
+ /********
+ * Get qualifier for `s`, meaning the symbol
+ * that s is in the symbol table of.
+ * The module does not count as a qualifier, because C++
+ * does not have modules.
+ * Params:
+ * s = symbol that may have a qualifier
+ * s is rewritten to be TemplateInstance if s is one
+ * Returns:
+ * qualifier, null if none
+ */
+ static Dsymbol getQualifier(Dsymbol s)
+ {
+ Dsymbol p = s.toParent();
+ return (p && !p.isModule()) ? p : null;
+ }
+
+ // Detect type char
+ static bool isChar(RootObject o)
+ {
+ Type t = isType(o);
+ return (t && t.equals(Type.tchar));
+ }
+
+ // Detect type ::std::char_traits<char>
+ bool isChar_traits_char(RootObject o)
+ {
+ return isIdent_char(Id.char_traits, o);
+ }
+
+ // Detect type ::std::allocator<char>
+ bool isAllocator_char(RootObject o)
+ {
+ return isIdent_char(Id.allocator, o);
+ }
+
+ // Detect type ::std::ident<char>
+ bool isIdent_char(Identifier ident, RootObject o)
+ {
+ Type t = isType(o);
+ if (!t || t.ty != Tstruct)
+ return false;
+ Dsymbol s = (cast(TypeStruct)t).toDsymbol(null);
+ if (s.ident != ident)
+ return false;
+ Dsymbol p = s.toParent();
+ if (!p)
+ return false;
+ TemplateInstance ti = p.isTemplateInstance();
+ if (!ti)
+ return false;
+ Dsymbol q = getQualifier(ti);
+ const bool inStd = isStd(q) || isStd(this.getTiNamespace(ti));
+ return inStd && ti.tiargs.dim == 1 && isChar((*ti.tiargs)[0]);
+ }
+
+ /***
+ * Detect template args <char, ::std::char_traits<char>>
+ * and write st if found.
+ * Returns:
+ * true if found
+ */
+ bool char_std_char_traits_char(TemplateInstance ti, string st)
+ {
+ if (ti.tiargs.dim == 2 &&
+ isChar((*ti.tiargs)[0]) &&
+ isChar_traits_char((*ti.tiargs)[1]))
+ {
+ buf.writestring(st.ptr);
+ return true;
+ }
+ return false;
+ }
+
+
+ void prefix_name(Dsymbol s)
+ {
+ //printf("prefix_name(%s)\n", s.toChars());
+ if (substitute(s))
+ return;
+ if (isStd(s))
+ return buf.writestring("St");
+
+ auto si = getInstance(s);
+ Dsymbol p = getQualifier(si);
+ if (p)
+ {
+ if (isStd(p))
+ {
+ bool needsTa;
+ auto ti = si.isTemplateInstance();
+ if (this.writeStdSubstitution(ti, needsTa))
+ {
+ this.abiTags.writeSymbol(ti, this);
+ if (needsTa)
+ {
+ template_args(ti);
+ append(ti);
+ }
+ return;
+ }
+ buf.writestring("St");
+ }
+ else
+ prefix_name(p);
+ }
+ source_name(si, true);
+ if (!isStd(si))
+ /* Do this after the source_name() call to keep components[]
+ * in the right order.
+ * https://issues.dlang.org/show_bug.cgi?id=17947
+ */
+ append(si);
+ }
+
+ /**
+ * Write common substitution for standard types, such as std::allocator
+ *
+ * This function assumes that the symbol `ti` is in the namespace `std`.
+ *
+ * Params:
+ * ti = Template instance to consider
+ * needsTa = If this function returns `true`, this value indicates
+ * if additional template argument mangling is needed
+ *
+ * Returns:
+ * `true` if a special std symbol was found
+ */
+ bool writeStdSubstitution(TemplateInstance ti, out bool needsTa)
+ {
+ if (!ti)
+ return false;
+ if (!isStd(this.getTiNamespace(ti)) && !isStd(getQualifier(ti)))
+ return false;
+
+ if (ti.name == Id.allocator)
+ {
+ buf.writestring("Sa");
+ needsTa = true;
+ return true;
+ }
+ if (ti.name == Id.basic_string)
+ {
+ // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
+ if (ti.tiargs.dim == 3 &&
+ isChar((*ti.tiargs)[0]) &&
+ isChar_traits_char((*ti.tiargs)[1]) &&
+ isAllocator_char((*ti.tiargs)[2]))
+
+ {
+ buf.writestring("Ss");
+ return true;
+ }
+ buf.writestring("Sb"); // ::std::basic_string
+ needsTa = true;
+ return true;
+ }
+
+ // ::std::basic_istream<char, ::std::char_traits<char>>
+ if (ti.name == Id.basic_istream &&
+ char_std_char_traits_char(ti, "Si"))
+ return true;
+
+ // ::std::basic_ostream<char, ::std::char_traits<char>>
+ if (ti.name == Id.basic_ostream &&
+ char_std_char_traits_char(ti, "So"))
+ return true;
+
+ // ::std::basic_iostream<char, ::std::char_traits<char>>
+ if (ti.name == Id.basic_iostream &&
+ char_std_char_traits_char(ti, "Sd"))
+ return true;
+
+ return false;
+ }
+
+ void cpp_mangle_name(Dsymbol s, bool qualified)
+ {
+ //printf("cpp_mangle_name(%s, %d)\n", s.toChars(), qualified);
+ Dsymbol p = s.toParent();
+ Dsymbol se = s;
+ bool write_prefix = true;
+ if (p && p.isTemplateInstance())
+ {
+ se = p;
+ if (find(p.isTemplateInstance().tempdecl) >= 0)
+ write_prefix = false;
+ p = p.toParent();
+ }
+ if (!p || p.isModule())
+ {
+ source_name(se, false);
+ append(s);
+ return;
+ }
+
+ if (!isStd(p) || qualified)
+ {
+ buf.writeByte('N');
+ if (write_prefix)
+ {
+ if (isStd(p))
+ buf.writestring("St");
+ else
+ prefix_name(p);
+ }
+ source_name(se, true);
+ buf.writeByte('E');
+ append(s);
+ return;
+ }
+ /* The N..E is not required if:
+ * 1. the parent is 'std'
+ * 2. 'std' is the initial qualifier
+ * 3. there is no CV-qualifier or a ref-qualifier for a member function
+ * ABI 5.1.8
+ */
+ TemplateInstance ti = se.isTemplateInstance();
+ if (s.ident == Id.allocator)
+ {
+ buf.writestring("Sa"); // "Sa" is short for ::std::allocator
+ template_args(ti);
+ }
+ else if (s.ident == Id.basic_string)
+ {
+ // ::std::basic_string<char, ::std::char_traits<char>, ::std::allocator<char>>
+ if (ti.tiargs.dim == 3 &&
+ isChar((*ti.tiargs)[0]) &&
+ isChar_traits_char((*ti.tiargs)[1]) &&
+ isAllocator_char((*ti.tiargs)[2]))
+ {
+ buf.writestring("Ss");
+ return;
+ }
+ buf.writestring("Sb"); // ::std::basic_string
+ template_args(ti);
+ }
+ else
+ {
+ // ::std::basic_istream<char, ::std::char_traits<char>>
+ if (s.ident == Id.basic_istream)
+ {
+ if (char_std_char_traits_char(ti, "Si"))
+ return;
+ }
+ else if (s.ident == Id.basic_ostream)
+ {
+ if (char_std_char_traits_char(ti, "So"))
+ return;
+ }
+ else if (s.ident == Id.basic_iostream)
+ {
+ if (char_std_char_traits_char(ti, "Sd"))
+ return;
+ }
+ buf.writestring("St");
+ source_name(se, true);
+ }
+ append(s);
+ }
+
+ /**
+ * Write CV-qualifiers to the buffer
+ *
+ * CV-qualifiers are 'r': restrict (unused in D), 'V': volatile, 'K': const
+ *
+ * See_Also:
+ * https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.CV-qualifiers
+ */
+ void CV_qualifiers(const Type t)
+ {
+ if (t.isConst())
+ buf.writeByte('K');
+ }
+
+ /**
+ * Mangles a variable
+ *
+ * Params:
+ * d = Variable declaration to mangle
+ * isNested = Whether this variable is nested, e.g. a template parameter
+ * or within a namespace
+ */
+ void mangle_variable(VarDeclaration d, bool isNested)
+ {
+ // fake mangling for fields to fix https://issues.dlang.org/show_bug.cgi?id=16525
+ if (!(d.storage_class & (STC.extern_ | STC.field | STC.gshared)))
+ {
+ d.error("Internal Compiler Error: C++ static non-`__gshared` non-`extern` variables not supported");
+ fatal();
+ }
+ Dsymbol p = d.toParent();
+ if (p && !p.isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE"
+ {
+ buf.writestring("_ZN");
+ prefix_name(p);
+ source_name(d, true);
+ buf.writeByte('E');
+ }
+ else if (isNested)
+ {
+ buf.writestring("_Z");
+ source_name(d, false);
+ }
+ else
+ {
+ if (auto varTags = ABITagContainer.forSymbol(d))
+ {
+ buf.writestring("_Z");
+ source_name(d, false);
+ return;
+ }
+ if (auto typeTags = ABITagContainer.forSymbol(d.type.toDsymbol(null)))
+ {
+ buf.writestring("_Z");
+ source_name(d, false);
+ this.abiTags.write(*this.buf, typeTags);
+ return;
+ }
+ //char beta[6] should mangle as "beta"
+ buf.writestring(d.ident.toString());
+ }
+ }
+
+ void mangle_function(FuncDeclaration d)
+ {
+ //printf("mangle_function(%s)\n", d.toChars());
+ /*
+ * <mangled-name> ::= _Z <encoding>
+ */
+ buf.writestring("_Z");
+ this.mangle_function_encoding(d);
+ }
+
+ void mangle_function_encoding(FuncDeclaration d)
+ {
+ //printf("mangle_function_encoding(%s)\n", d.toChars());
+ /*
+ * <encoding> ::= <function name> <bare-function-type>
+ * ::= <data name>
+ * ::= <special-name>
+ */
+ TypeFunction tf = cast(TypeFunction)d.type;
+
+ if (TemplateDeclaration ftd = getFuncTemplateDecl(d))
+ {
+ /* It's an instance of a function template
+ */
+ TemplateInstance ti = d.parent.isTemplateInstance();
+ assert(ti);
+ this.mangleTemplatedFunction(d, tf, ftd, ti);
+ return;
+ }
+
+ Dsymbol p = d.toParent();
+ if (p && !p.isModule() && tf.linkage == LINK.cpp)
+ {
+ this.mangleNestedFuncPrefix(tf, p);
+
+ if (auto ctor = d.isCtorDeclaration())
+ buf.writestring(ctor.isCpCtor ? "C2" : "C1");
+ else if (d.isPrimaryDtor())
+ buf.writestring("D1");
+ else if (d.ident && d.ident == Id.assign)
+ buf.writestring("aS");
+ else if (d.ident && d.ident == Id.eq)
+ buf.writestring("eq");
+ else if (d.ident && d.ident == Id.index)
+ buf.writestring("ix");
+ else if (d.ident && d.ident == Id.call)
+ buf.writestring("cl");
+ else
+ source_name(d, true);
+ buf.writeByte('E');
+ }
+ else
+ {
+ source_name(d, false);
+ }
+
+ // Save offset for potentially writing tags
+ const size_t off = this.buf.length();
+
+ // Template args accept extern "C" symbols with special mangling
+ if (tf.linkage == LINK.cpp)
+ mangleFunctionParameters(tf.parameterList);
+
+ if (!tf.next.isTypeBasic())
+ this.writeRemainingTags(off, tf);
+ }
+
+ /**
+ * Recursively mangles a non-scoped namespace
+ *
+ * Parameters:
+ * ns = Namespace to mangle
+ * dg = A delegate to write the identifier in this namespace
+ * haveNE = When `false` (the default), surround the namespace / dg
+ * call with nested name qualifier (`N..E`).
+ * Otherwise, they are already present (e.g. `Nspace` was used).
+ */
+ void writeNamespace(CPPNamespaceDeclaration ns, scope void delegate() dg,
+ bool haveNE = false)
+ {
+ void runDg () { if (dg !is null) dg(); }
+
+ if (ns is null || ns.ident is null)
+ return runDg();
+
+ if (isStd(ns))
+ {
+ if (!substitute(ns))
+ buf.writestring("St");
+ runDg();
+ }
+ else if (dg !is null)
+ {
+ if (!haveNE)
+ buf.writestring("N");
+ if (!substitute(ns))
+ {
+ this.writeNamespace(ns.cppnamespace, null);
+ this.writeIdentifier(ns.ident);
+ append(ns);
+ }
+ dg();
+ if (!haveNE)
+ buf.writestring("E");
+ }
+ else if (!substitute(ns))
+ {
+ this.writeNamespace(ns.cppnamespace, null);
+ this.writeIdentifier(ns.ident);
+ append(ns);
+ }
+ }
+
+ /**
+ * Mangles a function template to C++
+ *
+ * Params:
+ * d = Function declaration
+ * tf = Function type (casted d.type)
+ * ftd = Template declaration (ti.templdecl)
+ * ti = Template instance (d.parent)
+ */
+ void mangleTemplatedFunction(FuncDeclaration d, TypeFunction tf,
+ TemplateDeclaration ftd, TemplateInstance ti)
+ {
+ Dsymbol p = ti.toParent();
+ // Check if this function is *not* nested
+ if (!p || p.isModule() || tf.linkage != LINK.cpp)
+ {
+ this.context.ti = ti;
+ this.context.fd = d;
+ this.context.res = d;
+ TypeFunction preSemantic = cast(TypeFunction)d.originalType;
+ auto nspace = ti.toParent();
+ if (nspace && nspace.isNspace())
+ this.writeChained(ti.toParent(), () => source_name(ti, true));
+ else
+ source_name(ti, false);
+ this.mangleReturnType(preSemantic);
+ this.mangleFunctionParameters(ParameterList(preSemantic.parameterList.parameters, tf.parameterList.varargs));
+ return;
+ }
+
+ // It's a nested function (e.g. a member of an aggregate)
+ this.mangleNestedFuncPrefix(tf, p);
+
+ if (d.isCtorDeclaration())
+ {
+ buf.writestring("C1");
+ mangleFunctionParameters(tf.parameterList);
+ return;
+ }
+ else if (d.isPrimaryDtor())
+ {
+ buf.writestring("D1");
+ mangleFunctionParameters(tf.parameterList);
+ return;
+ }
+
+ int firstTemplateArg = 0;
+ bool appendReturnType = true;
+ bool isConvertFunc = false;
+ string symName;
+
+ // test for special symbols
+ CppOperator whichOp = isCppOperator(ti.name);
+ final switch (whichOp)
+ {
+ case CppOperator.Unknown:
+ break;
+ case CppOperator.Cast:
+ symName = "cv";
+ firstTemplateArg = 1;
+ isConvertFunc = true;
+ appendReturnType = false;
+ break;
+ case CppOperator.Assign:
+ symName = "aS";
+ break;
+ case CppOperator.Eq:
+ symName = "eq";
+ break;
+ case CppOperator.Index:
+ symName = "ix";
+ break;
+ case CppOperator.Call:
+ symName = "cl";
+ break;
+ case CppOperator.Unary:
+ case CppOperator.Binary:
+ case CppOperator.OpAssign:
+ TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
+ assert(td);
+ assert(ti.tiargs.dim >= 1);
+ TemplateParameter tp = (*td.parameters)[0];
+ TemplateValueParameter tv = tp.isTemplateValueParameter();
+ if (!tv || !tv.valType.isString())
+ break; // expecting a string argument to operators!
+ Expression exp = (*ti.tiargs)[0].isExpression();
+ StringExp str = exp.toStringExp();
+ switch (whichOp)
+ {
+ case CppOperator.Unary:
+ switch (str.peekString())
+ {
+ case "*": symName = "de"; goto continue_template;
+ case "++": symName = "pp"; goto continue_template;
+ case "--": symName = "mm"; goto continue_template;
+ case "-": symName = "ng"; goto continue_template;
+ case "+": symName = "ps"; goto continue_template;
+ case "~": symName = "co"; goto continue_template;
+ default: break;
+ }
+ break;
+ case CppOperator.Binary:
+ switch (str.peekString())
+ {
+ case ">>": symName = "rs"; goto continue_template;
+ case "<<": symName = "ls"; goto continue_template;
+ case "*": symName = "ml"; goto continue_template;
+ case "-": symName = "mi"; goto continue_template;
+ case "+": symName = "pl"; goto continue_template;
+ case "&": symName = "an"; goto continue_template;
+ case "/": symName = "dv"; goto continue_template;
+ case "%": symName = "rm"; goto continue_template;
+ case "^": symName = "eo"; goto continue_template;
+ case "|": symName = "or"; goto continue_template;
+ default: break;
+ }
+ break;
+ case CppOperator.OpAssign:
+ switch (str.peekString())
+ {
+ case "*": symName = "mL"; goto continue_template;
+ case "+": symName = "pL"; goto continue_template;
+ case "-": symName = "mI"; goto continue_template;
+ case "/": symName = "dV"; goto continue_template;
+ case "%": symName = "rM"; goto continue_template;
+ case ">>": symName = "rS"; goto continue_template;
+ case "<<": symName = "lS"; goto continue_template;
+ case "&": symName = "aN"; goto continue_template;
+ case "|": symName = "oR"; goto continue_template;
+ case "^": symName = "eO"; goto continue_template;
+ default: break;
+ }
+ break;
+ default:
+ assert(0);
+ continue_template:
+ firstTemplateArg = 1;
+ break;
+ }
+ break;
+ }
+ if (symName.length == 0)
+ source_name(ti, true);
+ else
+ {
+ buf.writestring(symName);
+ if (isConvertFunc)
+ template_arg(ti, 0);
+ appendReturnType = template_args(ti, firstTemplateArg) && appendReturnType;
+ }
+ buf.writeByte('E');
+ if (appendReturnType)
+ headOfType(tf.nextOf()); // mangle return type
+ mangleFunctionParameters(tf.parameterList);
+ }
+
+ /**
+ * Mangle the parameters of a function
+ *
+ * For templated functions, `context.res` is set to the `FuncDeclaration`
+ *
+ * Params:
+ * parameters = Array of `Parameter` to mangle
+ * varargs = if != 0, this function has varargs parameters
+ */
+ void mangleFunctionParameters(ParameterList parameterList)
+ {
+ int numparams = 0;
+
+ foreach (n, fparam; parameterList)
+ {
+ Type t = target.cpp.parameterType(fparam);
+ if (t.ty == Tsarray)
+ {
+ // Static arrays in D are passed by value; no counterpart in C++
+ .error(loc, "Internal Compiler Error: unable to pass static array `%s` to extern(C++) function, use pointer instead",
+ t.toChars());
+ fatal();
+ }
+ auto prev = this.context.push({
+ TypeFunction tf;
+ if (isDsymbol(this.context.res))
+ tf = cast(TypeFunction)this.context.res.asFuncDecl().type;
+ else
+ tf = this.context.res.asType().isTypeFunction();
+ assert(tf);
+ return (*tf.parameterList.parameters)[n].type;
+ }());
+ scope (exit) this.context.pop(prev);
+
+ if (this.context.ti && global.params.cplusplus >= CppStdRevision.cpp11)
+ handleParamPack(t, this.context.ti.tempdecl.isTemplateDeclaration().parameters);
+
+ headOfType(t);
+ ++numparams;
+ }
+
+ if (parameterList.varargs == VarArg.variadic)
+ buf.writeByte('z');
+ else if (!numparams)
+ buf.writeByte('v'); // encode (void) parameters
+ }
+
+ /****** The rest is type mangling ************/
+
+ void error(Type t)
+ {
+ const(char)* p;
+ if (t.isImmutable())
+ p = "`immutable` ";
+ else if (t.isShared())
+ p = "`shared` ";
+ else
+ p = "";
+ .error(loc, "Internal Compiler Error: %stype `%s` cannot be mapped to C++\n", p, t.toChars());
+ fatal(); //Fatal, because this error should be handled in frontend
+ }
+
+ /****************************
+ * Mangle a type,
+ * treating it as a Head followed by a Tail.
+ * Params:
+ * t = Head of a type
+ */
+ void headOfType(Type t)
+ {
+ if (t.ty == Tclass)
+ {
+ mangleTypeClass(cast(TypeClass)t, true);
+ }
+ else
+ {
+ // For value types, strip const/immutable/shared from the head of the type
+ auto prev = this.context.push(this.context.res.asType().mutableOf().unSharedOf());
+ scope (exit) this.context.pop(prev);
+ t.mutableOf().unSharedOf().accept(this);
+ }
+ }
+
+ /******
+ * Write out 1 or 2 character basic type mangling.
+ * Handle const and substitutions.
+ * Params:
+ * t = type to mangle
+ * p = if not 0, then character prefix
+ * c = mangling character
+ */
+ void writeBasicType(Type t, char p, char c)
+ {
+ // Only do substitutions for non-fundamental types.
+ if (!isFundamentalType(t) || t.isConst())
+ {
+ if (substitute(t))
+ return;
+ else
+ append(t);
+ }
+ CV_qualifiers(t);
+ if (p)
+ buf.writeByte(p);
+ buf.writeByte(c);
+ }
+
+
+ /****************
+ * Write structs and enums.
+ * Params:
+ * t = TypeStruct or TypeEnum
+ */
+ void doSymbol(Type t)
+ {
+ if (substitute(t))
+ return;
+ CV_qualifiers(t);
+
+ // Handle any target-specific struct types.
+ if (auto tm = target.cpp.typeMangle(t))
+ {
+ buf.writestring(tm);
+ }
+ else
+ {
+ Dsymbol s = t.toDsymbol(null);
+ Dsymbol p = s.toParent();
+ if (p && p.isTemplateInstance())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=17947
+ * Substitute the template instance symbol, not the struct/enum symbol
+ */
+ if (substitute(p))
+ return;
+ }
+ if (!substitute(s))
+ cpp_mangle_name(s, false);
+ }
+ if (t.isConst())
+ append(t);
+ }
+
+
+
+ /************************
+ * Mangle a class type.
+ * If it's the head, treat the initial pointer as a value type.
+ * Params:
+ * t = class type
+ * head = true for head of a type
+ */
+ void mangleTypeClass(TypeClass t, bool head)
+ {
+ if (t.isImmutable() || t.isShared())
+ return error(t);
+
+ /* Mangle as a <pointer to><struct>
+ */
+ if (substitute(t))
+ return;
+ if (!head)
+ CV_qualifiers(t);
+ buf.writeByte('P');
+
+ CV_qualifiers(t);
+
+ {
+ Dsymbol s = t.toDsymbol(null);
+ Dsymbol p = s.toParent();
+ if (p && p.isTemplateInstance())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=17947
+ * Substitute the template instance symbol, not the class symbol
+ */
+ if (substitute(p))
+ return;
+ }
+ }
+
+ if (!substitute(t.sym))
+ {
+ cpp_mangle_name(t.sym, false);
+ }
+ if (t.isConst())
+ append(null); // C++ would have an extra type here
+ append(t);
+ }
+
+ /**
+ * Mangle the prefix of a nested (e.g. member) function
+ *
+ * Params:
+ * tf = Type of the nested function
+ * parent = Parent in which the function is nested
+ */
+ void mangleNestedFuncPrefix(TypeFunction tf, Dsymbol parent)
+ {
+ /* <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
+ * ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
+ */
+ buf.writeByte('N');
+ CV_qualifiers(tf);
+
+ /* <prefix> ::= <prefix> <unqualified-name>
+ * ::= <template-prefix> <template-args>
+ * ::= <template-param>
+ * ::= # empty
+ * ::= <substitution>
+ * ::= <prefix> <data-member-prefix>
+ */
+ prefix_name(parent);
+ }
+
+ /**
+ * Write `Dp` (C++11 function parameter pack prefix) if 't' is a TemplateSequenceParameter (T...).
+ *
+ * Params:
+ * t = Parameter type
+ * params = Template parameters of the function
+ */
+ private void handleParamPack(Type t, TemplateParameters* params)
+ {
+ if (t.isTypeReference())
+ t = t.nextOf();
+ auto ti = t.isTypeIdentifier();
+ if (!ti)
+ return;
+
+ auto idx = templateParamIndex(ti.ident, params);
+ if (idx < params.length && (*params)[idx].isTemplateTupleParameter())
+ buf.writestring("Dp");
+ }
+
+ /**
+ * Helper function to write a `T..._` template index.
+ *
+ * Params:
+ * idx = Index of `param` in the template argument list
+ * param = Template parameter to mangle
+ */
+ private void writeTemplateArgIndex(size_t idx, TemplateParameter param)
+ {
+ // expressions are mangled in <X..E>
+ if (param.isTemplateValueParameter())
+ buf.writeByte('X');
+ buf.writeByte('T');
+ writeSequenceFromIndex(idx);
+ buf.writeByte('_');
+ if (param.isTemplateValueParameter())
+ buf.writeByte('E');
+ }
+
+ /**
+ * Given an array of template parameters and an identifier,
+ * returns the index of the identifier in that array.
+ *
+ * Params:
+ * ident = Identifier for which substitution is attempted
+ * (e.g. `void func(T)(T param)` => `T` from `T param`)
+ * params = `TemplateParameters` of the enclosing symbol
+ * (in the previous example, `func`'s template parameters)
+ *
+ * Returns:
+ * The index of the identifier match in `params`,
+ * or `params.length` if there wasn't any match.
+ */
+ private static size_t templateParamIndex(
+ const ref Identifier ident, TemplateParameters* params)
+ {
+ foreach (idx, param; *params)
+ if (param.ident == ident)
+ return idx;
+ return params.length;
+ }
+
+ /**
+ * Given a template instance `t`, write its qualified name
+ * without the template parameter list
+ *
+ * Params:
+ * t = Post-parsing `TemplateInstance` pointing to the symbol
+ * to mangle (one level deep)
+ * dg = Delegate to execute after writing the qualified symbol
+ *
+ */
+ private void writeQualified(TemplateInstance t, scope void delegate() dg)
+ {
+ auto type = isType(this.context.res);
+ if (!type)
+ {
+ this.writeIdentifier(t.name);
+ return dg();
+ }
+ auto sym1 = type.toDsymbol(null);
+ if (!sym1)
+ {
+ this.writeIdentifier(t.name);
+ return dg();
+ }
+ // Get the template instance
+ auto sym = getQualifier(sym1);
+ auto sym2 = getQualifier(sym);
+ if (sym2 && isStd(sym2)) // Nspace path
+ {
+ bool unused;
+ assert(sym.isTemplateInstance());
+ if (this.writeStdSubstitution(sym.isTemplateInstance(), unused))
+ return dg();
+ // std names don't require `N..E`
+ buf.writestring("St");
+ this.writeIdentifier(t.name);
+ this.append(t);
+ return dg();
+ }
+ else if (sym2)
+ {
+ buf.writestring("N");
+ if (!this.substitute(sym2))
+ sym2.accept(this);
+ }
+ this.writeNamespace(
+ sym1.cppnamespace, () {
+ this.writeIdentifier(t.name);
+ this.append(t);
+ dg();
+ });
+ if (sym2)
+ buf.writestring("E");
+ }
+
+extern(C++):
+
+ alias visit = Visitor.visit;
+
+ override void visit(TypeNull t)
+ {
+ if (t.isImmutable() || t.isShared())
+ return error(t);
+
+ writeBasicType(t, 'D', 'n');
+ }
+
+ override void visit(TypeNoreturn t)
+ {
+ if (t.isImmutable() || t.isShared())
+ return error(t);
+
+ writeBasicType(t, 0, 'v'); // mangle like `void`
+ }
+
+ override void visit(TypeBasic t)
+ {
+ if (t.isImmutable() || t.isShared())
+ return error(t);
+
+ // Handle any target-specific basic types.
+ if (auto tm = target.cpp.typeMangle(t))
+ {
+ // Only do substitutions for non-fundamental types.
+ if (!isFundamentalType(t) || t.isConst())
+ {
+ if (substitute(t))
+ return;
+ else
+ append(t);
+ }
+ CV_qualifiers(t);
+ buf.writestring(tm);
+ return;
+ }
+
+ /* <builtin-type>:
+ * v void
+ * w wchar_t
+ * b bool
+ * c char
+ * a signed char
+ * h unsigned char
+ * s short
+ * t unsigned short
+ * i int
+ * j unsigned int
+ * l long
+ * m unsigned long
+ * x long long, __int64
+ * y unsigned long long, __int64
+ * n __int128
+ * o unsigned __int128
+ * f float
+ * d double
+ * e long double, __float80
+ * g __float128
+ * z ellipsis
+ * Dd 64 bit IEEE 754r decimal floating point
+ * De 128 bit IEEE 754r decimal floating point
+ * Df 32 bit IEEE 754r decimal floating point
+ * Dh 16 bit IEEE 754r half-precision floating point
+ * Di char32_t
+ * Ds char16_t
+ * u <source-name> # vendor extended type
+ */
+ char c;
+ char p = 0;
+ switch (t.ty)
+ {
+ case Tvoid: c = 'v'; break;
+ case Tint8: c = 'a'; break;
+ case Tuns8: c = 'h'; break;
+ case Tint16: c = 's'; break;
+ case Tuns16: c = 't'; break;
+ case Tint32: c = 'i'; break;
+ case Tuns32: c = 'j'; break;
+ case Tfloat32: c = 'f'; break;
+ case Tint64:
+ c = target.c.longsize == 8 ? 'l' : 'x';
+ break;
+ case Tuns64:
+ c = target.c.longsize == 8 ? 'm' : 'y';
+ break;
+ case Tint128: c = 'n'; break;
+ case Tuns128: c = 'o'; break;
+ case Tfloat64: c = 'd'; break;
+ case Tfloat80: c = 'e'; break;
+ case Tbool: c = 'b'; break;
+ case Tchar: c = 'c'; break;
+ case Twchar: p = 'D'; c = 's'; break; // since C++11
+ case Tdchar: p = 'D'; c = 'i'; break; // since C++11
+ case Timaginary32: p = 'G'; c = 'f'; break; // 'G' means imaginary
+ case Timaginary64: p = 'G'; c = 'd'; break;
+ case Timaginary80: p = 'G'; c = 'e'; break;
+ case Tcomplex32: p = 'C'; c = 'f'; break; // 'C' means complex
+ case Tcomplex64: p = 'C'; c = 'd'; break;
+ case Tcomplex80: p = 'C'; c = 'e'; break;
+
+ default:
+ return error(t);
+ }
+ writeBasicType(t, p, c);
+ }
+
+ override void visit(TypeVector t)
+ {
+ if (t.isImmutable() || t.isShared())
+ return error(t);
+
+ if (substitute(t))
+ return;
+ append(t);
+ CV_qualifiers(t);
+
+ // Handle any target-specific vector types.
+ if (auto tm = target.cpp.typeMangle(t))
+ {
+ buf.writestring(tm);
+ }
+ else
+ {
+ assert(t.basetype && t.basetype.ty == Tsarray);
+ auto tsa = t.basetype.isTypeSArray();
+ assert(tsa.dim);
+ buf.writestring("Dv"); // -- Gnu ABI v.4
+ buf.print(tsa.dim.toInteger());
+ buf.writeByte('_');
+ t.basetype.nextOf().accept(this);
+ }
+ }
+
+ override void visit(TypeSArray t)
+ {
+ if (t.isImmutable() || t.isShared())
+ return error(t);
+
+ if (!substitute(t))
+ append(t);
+ CV_qualifiers(t);
+ buf.writeByte('A');
+ buf.print(t.dim ? t.dim.toInteger() : 0);
+ buf.writeByte('_');
+ t.next.accept(this);
+ }
+
+ override void visit(TypePointer t)
+ {
+ if (t.isImmutable() || t.isShared())
+ return error(t);
+
+ // Check for const - Since we cannot represent C++'s `char* const`,
+ // and `const char* const` (a.k.a `const(char*)` in D) is mangled
+ // the same as `const char*` (`const(char)*` in D), we need to add
+ // an extra `K` if `nextOf()` is `const`, before substitution
+ CV_qualifiers(t);
+ if (substitute(t))
+ return;
+ buf.writeByte('P');
+ auto prev = this.context.push(this.context.res.asType().nextOf());
+ scope (exit) this.context.pop(prev);
+ t.next.accept(this);
+ append(t);
+ }
+
+ override void visit(TypeReference t)
+ {
+ if (substitute(t))
+ return;
+ buf.writeByte('R');
+ CV_qualifiers(t.nextOf());
+ headOfType(t.nextOf());
+ if (t.nextOf().isConst())
+ append(t.nextOf());
+ append(t);
+ }
+
+ override void visit(TypeFunction t)
+ {
+ /*
+ * <function-type> ::= F [Y] <bare-function-type> E
+ * <bare-function-type> ::= <signature type>+
+ * # types are possible return type, then parameter types
+ */
+ /* ABI says:
+ "The type of a non-static member function is considered to be different,
+ for the purposes of substitution, from the type of a namespace-scope or
+ static member function whose type appears similar. The types of two
+ non-static member functions are considered to be different, for the
+ purposes of substitution, if the functions are members of different
+ classes. In other words, for the purposes of substitution, the class of
+ which the function is a member is considered part of the type of
+ function."
+
+ BUG: Right now, types of functions are never merged, so our simplistic
+ component matcher always finds them to be different.
+ We should use Type.equals on these, and use different
+ TypeFunctions for non-static member functions, and non-static
+ member functions of different classes.
+ */
+ if (substitute(t))
+ return;
+ buf.writeByte('F');
+ if (t.linkage == LINK.c)
+ buf.writeByte('Y');
+ Type tn = t.next;
+ if (t.isref)
+ tn = tn.referenceTo();
+ tn.accept(this);
+ mangleFunctionParameters(t.parameterList);
+ buf.writeByte('E');
+ append(t);
+ }
+
+ override void visit(TypeStruct t)
+ {
+ if (t.isImmutable() || t.isShared())
+ return error(t);
+ //printf("TypeStruct %s\n", t.toChars());
+ doSymbol(t);
+ }
+
+ override void visit(TypeEnum t)
+ {
+ if (t.isImmutable() || t.isShared())
+ return error(t);
+
+ /* __c_(u)long(long) and others get special mangling
+ */
+ const id = t.sym.ident;
+ //printf("enum id = '%s'\n", id.toChars());
+ if (id == Id.__c_long)
+ return writeBasicType(t, 0, 'l');
+ else if (id == Id.__c_ulong)
+ return writeBasicType(t, 0, 'm');
+ else if (id == Id.__c_wchar_t)
+ return writeBasicType(t, 0, 'w');
+ else if (id == Id.__c_longlong)
+ return writeBasicType(t, 0, 'x');
+ else if (id == Id.__c_ulonglong)
+ return writeBasicType(t, 0, 'y');
+ else if (id == Id.__c_complex_float)
+ return writeBasicType(t, 'C', 'f');
+ else if (id == Id.__c_complex_double)
+ return writeBasicType(t, 'C', 'd');
+ else if (id == Id.__c_complex_real)
+ return writeBasicType(t, 'C', 'e');
+
+ doSymbol(t);
+ }
+
+ override void visit(TypeClass t)
+ {
+ mangleTypeClass(t, false);
+ }
+
+ /**
+ * Performs template parameter substitution
+ *
+ * Mangling is performed on a copy of the post-parsing AST before
+ * any semantic pass is run.
+ * There is no easy way to link a type to the template parameters
+ * once semantic has run, because:
+ * - the `TemplateInstance` installs aliases in its scope to its params
+ * - `AliasDeclaration`s are resolved in many places
+ * - semantic passes are destructive, so the `TypeIdentifier` gets lost
+ *
+ * As a result, the best approach with the current architecture is to:
+ * - Run the visitor on the `originalType` of the function,
+ * looking up any `TypeIdentifier` at the template scope when found.
+ * - Fallback to the post-semantic `TypeFunction` when the identifier is
+ * not a template parameter.
+ */
+ override void visit(TypeIdentifier t)
+ {
+ auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl;
+ assert(decl.parameters !is null);
+ auto idx = templateParamIndex(t.ident, decl.parameters);
+ // If not found, default to the post-semantic type
+ if (idx >= decl.parameters.length)
+ return this.context.res.visitObject(this);
+
+ auto param = (*decl.parameters)[idx];
+ if (auto type = this.context.res.isType())
+ CV_qualifiers(type);
+ // Otherwise, attempt substitution (`S_` takes precedence on `T_`)
+ if (this.substitute(param))
+ return;
+
+ // If substitution failed, write `TX_` where `X` is the index
+ this.writeTemplateArgIndex(idx, param);
+ this.append(param);
+ // Write the ABI tags, if any
+ if (auto sym = this.context.res.isDsymbol())
+ this.abiTags.writeSymbol(sym, this);
+ }
+
+ /// Ditto
+ override void visit(TypeInstance t)
+ {
+ assert(t.tempinst !is null);
+ t.tempinst.accept(this);
+ }
+
+ /**
+ * Mangles a `TemplateInstance`
+ *
+ * A `TemplateInstance` can be found either in the parameter,
+ * or the return value.
+ * Arguments to the template instance needs to be mangled but the template
+ * can be partially substituted, so for example the following:
+ * `Container!(T, Val) func16479_12 (alias Container, T, int Val) ()`
+ * will mangle the return value part to "T_IT0_XT1_EE"
+ */
+ override void visit(TemplateInstance t)
+ {
+ // Template names are substituted, but args still need to be written
+ void writeArgs ()
+ {
+ buf.writeByte('I');
+ // When visiting the arguments, the context will be set to the
+ // resolved type
+ auto analyzed_ti = this.context.res.asType().toDsymbol(null).isInstantiated();
+ auto prev = this.context;
+ scope (exit) this.context.pop(prev);
+ foreach (idx, RootObject o; *t.tiargs)
+ {
+ this.context.res = (*analyzed_ti.tiargs)[idx];
+ o.visitObject(this);
+ }
+ if (analyzed_ti.tiargs.dim > t.tiargs.dim)
+ {
+ // If the resolved AST has more args than the parse one,
+ // we have default arguments
+ auto oparams = (cast(TemplateDeclaration)analyzed_ti.tempdecl).origParameters;
+ foreach (idx, arg; (*oparams)[t.tiargs.dim .. $])
+ {
+ this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.dim];
+
+ if (auto ttp = arg.isTemplateTypeParameter())
+ ttp.defaultType.accept(this);
+ else if (auto tvp = arg.isTemplateValueParameter())
+ tvp.defaultValue.accept(this);
+ else if (auto tvp = arg.isTemplateThisParameter())
+ tvp.defaultType.accept(this);
+ else if (auto tvp = arg.isTemplateAliasParameter())
+ tvp.defaultAlias.visitObject(this);
+ else
+ assert(0, arg.toString());
+ }
+ }
+ buf.writeByte('E');
+ }
+
+ // `name` is used, not `ident`
+ assert(t.name !is null);
+ assert(t.tiargs !is null);
+
+ bool needsTa;
+ auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl;
+ // Attempt to substitute the template itself
+ auto idx = templateParamIndex(t.name, decl.parameters);
+ if (idx < decl.parameters.length)
+ {
+ auto param = (*decl.parameters)[idx];
+ if (auto type = t.getType())
+ CV_qualifiers(type);
+ if (this.substitute(param))
+ return;
+ this.writeTemplateArgIndex(idx, param);
+ this.append(param);
+ writeArgs();
+ }
+ else if (this.writeStdSubstitution(t, needsTa))
+ {
+ if (needsTa)
+ writeArgs();
+ }
+ else if (!this.substitute(t))
+ this.writeQualified(t, &writeArgs);
+ }
+
+ /// Ditto
+ override void visit(IntegerExp t)
+ {
+ this.buf.writeByte('L');
+ t.type.accept(this);
+ this.buf.print(t.getInteger());
+ this.buf.writeByte('E');
+ }
+
+ override void visit(Nspace t)
+ {
+ if (auto p = getQualifier(t))
+ p.accept(this);
+
+ if (isStd(t))
+ buf.writestring("St");
+ else
+ {
+ this.writeIdentifier(t.ident);
+ this.append(t);
+ }
+ }
+
+ override void visit(Type t)
+ {
+ error(t);
+ }
+
+ void visit(Tuple t)
+ {
+ assert(0);
+ }
+}
+
+/// Helper code to visit `RootObject`, as it doesn't define `accept`,
+/// only its direct subtypes do.
+private void visitObject(V : Visitor)(RootObject o, V this_)
+{
+ assert(o !is null);
+ if (Type ta = isType(o))
+ ta.accept(this_);
+ else if (Expression ea = isExpression(o))
+ ea.accept(this_);
+ else if (Dsymbol sa = isDsymbol(o))
+ sa.accept(this_);
+ else if (TemplateParameter t = isTemplateParameter(o))
+ t.accept(this_);
+ else if (Tuple t = isTuple(o))
+ // `Tuple` inherits `RootObject` and does not define accept
+ // For this reason, this uses static dispatch on the visitor
+ this_.visit(t);
+ else
+ assert(0, o.toString());
+}
+
+/// Helper function to safely get a type out of a `RootObject`
+private Type asType(RootObject o)
+{
+ Type ta = isType(o);
+ // When called with context.res as argument, it can be `FuncDeclaration`
+ if (!ta && o.asFuncDecl())
+ ta = (cast(FuncDeclaration)o).type;
+ assert(ta !is null, o.toString());
+ return ta;
+}
+
+/// Helper function to safely get a `FuncDeclaration` out of a `RootObject`
+private FuncDeclaration asFuncDecl(RootObject o)
+{
+ Dsymbol d = isDsymbol(o);
+ assert(d !is null);
+ auto fd = d.isFuncDeclaration();
+ assert(fd !is null);
+ return fd;
+}
+
+/// Helper class to compare entries in components
+private extern(C++) final class ComponentVisitor : Visitor
+{
+ /// Only one of the following is not `null`, it's always
+ /// the most specialized type, set from the ctor
+ private Nspace namespace;
+
+ /// Ditto
+ private CPPNamespaceDeclaration namespace2;
+
+ /// Ditto
+ private TypePointer tpointer;
+
+ /// Ditto
+ private TypeReference tref;
+
+ /// Ditto
+ private TypeIdentifier tident;
+
+ /// Least specialized type
+ private RootObject object;
+
+ /// Set to the result of the comparison
+ private bool result;
+
+ public this(RootObject base)
+ {
+ switch (base.dyncast())
+ {
+ case DYNCAST.dsymbol:
+ if (auto ns = (cast(Dsymbol)base).isNspace())
+ this.namespace = ns;
+ else if (auto ns = (cast(Dsymbol)base).isCPPNamespaceDeclaration())
+ this.namespace2 = ns;
+ else
+ goto default;
+ break;
+
+ case DYNCAST.type:
+ auto t = cast(Type)base;
+ if (t.ty == Tpointer)
+ this.tpointer = cast(TypePointer)t;
+ else if (t.ty == Treference)
+ this.tref = cast(TypeReference)t;
+ else if (t.ty == Tident)
+ this.tident = cast(TypeIdentifier)t;
+ else
+ goto default;
+ break;
+
+ // Note: ABI tags are also handled here (they are TupleExp of StringExp)
+ default:
+ this.object = base;
+ }
+ }
+
+ /// Introduce base class overloads
+ alias visit = Visitor.visit;
+
+ /// Least specialized overload of each direct child of `RootObject`
+ public override void visit(Dsymbol o)
+ {
+ this.result = this.object && this.object == o;
+ }
+
+ /// Ditto
+ public override void visit(Expression o)
+ {
+ this.result = this.object && this.object == o;
+ }
+
+ /// Ditto
+ public void visit(Tuple o)
+ {
+ this.result = this.object && this.object == o;
+ }
+
+ /// Ditto
+ public override void visit(Type o)
+ {
+ this.result = this.object && this.object == o;
+ }
+
+ /// Ditto
+ public override void visit(TemplateParameter o)
+ {
+ this.result = this.object && this.object == o;
+ }
+
+ /**
+ * This overload handles composed types including template parameters
+ *
+ * Components for substitutions include "next" type.
+ * For example, if `ref T` is present, `ref T` and `T` will be present
+ * in the substitution array.
+ * But since we don't have the final/merged type, we cannot rely on
+ * object comparison, and need to recurse instead.
+ */
+ public override void visit(TypeReference o)
+ {
+ if (!this.tref)
+ return;
+ if (this.tref == o)
+ this.result = true;
+ else
+ {
+ // It might be a reference to a template parameter that we already
+ // saw, so we need to recurse
+ scope v = new ComponentVisitor(this.tref.next);
+ o.next.visitObject(v);
+ this.result = v.result;
+ }
+ }
+
+ /// Ditto
+ public override void visit(TypePointer o)
+ {
+ if (!this.tpointer)
+ return;
+ if (this.tpointer == o)
+ this.result = true;
+ else
+ {
+ // It might be a pointer to a template parameter that we already
+ // saw, so we need to recurse
+ scope v = new ComponentVisitor(this.tpointer.next);
+ o.next.visitObject(v);
+ this.result = v.result;
+ }
+ }
+
+ /// Ditto
+ public override void visit(TypeIdentifier o)
+ {
+ /// Since we know they are at the same level, scope resolution will
+ /// give us the same symbol, thus we can just compare ident.
+ this.result = (this.tident && (this.tident.ident == o.ident));
+ }
+
+ /**
+ * Overload which accepts a Namespace
+ *
+ * It is very common for large C++ projects to have multiple files sharing
+ * the same `namespace`. If any D project adopts the same approach
+ * (e.g. separating data structures from functions), it will lead to two
+ * `Nspace` objects being instantiated, with different addresses.
+ * At the same time, we cannot compare just any Dsymbol via identifier,
+ * because it messes with templates.
+ *
+ * See_Also:
+ * https://issues.dlang.org/show_bug.cgi?id=18922
+ *
+ * Params:
+ * ns = C++ namespace to do substitution for
+ */
+ public override void visit(Nspace ns)
+ {
+ this.result = isNamespaceEqual(this.namespace, ns)
+ || isNamespaceEqual(this.namespace2, ns);
+ }
+
+ /// Ditto
+ public override void visit(CPPNamespaceDeclaration ns)
+ {
+ this.result = isNamespaceEqual(this.namespace, ns)
+ || isNamespaceEqual(this.namespace2, ns);
+ }
+}
+
+/// Transitional functions for `CPPNamespaceDeclaration` / `Nspace`
+/// Remove when `Nspace` is removed.
+private bool isNamespaceEqual (Nspace a, Nspace b)
+{
+ if (a is null || b is null)
+ return false;
+ return a.equals(b);
+}
+
+/// Ditto
+private bool isNamespaceEqual (Nspace a, CPPNamespaceDeclaration b)
+{
+ return isNamespaceEqual(b, a);
+}
+
+/// Ditto
+private bool isNamespaceEqual (CPPNamespaceDeclaration a, Nspace b, size_t idx = 0)
+{
+ if ((a is null) != (b is null))
+ return false;
+ if (!a.ident.equals(b.ident))
+ return false;
+
+ // We need to see if there's more ident enclosing
+ if (auto pb = b.toParent().isNspace())
+ return isNamespaceEqual(a.cppnamespace, pb);
+ else
+ return a.cppnamespace is null;
+}
+
+/// Returns:
+/// Whether two `CPPNamespaceDeclaration` are equals
+private bool isNamespaceEqual (CPPNamespaceDeclaration a, CPPNamespaceDeclaration b)
+{
+ if (a is null || b is null)
+ return false;
+
+ if ((a.cppnamespace is null) != (b.cppnamespace is null))
+ return false;
+ if (a.ident != b.ident)
+ return false;
+ return a.cppnamespace is null ? true : isNamespaceEqual(a.cppnamespace, b.cppnamespace);
+}
+
+/**
+ * A container for ABI tags
+ *
+ * At its hearth, there is a sorted array of ABI tags having been written
+ * already. ABI tags can be present on parameters, template parameters,
+ * return value, and varaible. ABI tags for a given type needs to be written
+ * sorted. When a function returns a type that has ABI tags, only the tags that
+ * haven't been printed as part of the mangling (e.g. arguments) are written
+ * directly after the function name.
+ *
+ * This means that:
+ * ---
+ * /++ C++ type definitions:
+ * struct [[gnu::abi_tag("tag1")]] Struct1 {};
+ * struct [[gnu::abi_tag("tag2")]] Struct2 {};
+ * // Can also be: "tag2", "tag1", since tags are sorted.
+ * struct [[gnu::abi_tag("tag1", "tag2")]] Struct3 {};
+ * +/
+ * // Functions definitions:
+ * Struct3 func1 (Struct1);
+ * Struct3 func2 (Struct2);
+ * Struct3 func3 (Struct2, Struct1);
+ * ---
+ * Will be respectively pseudo-mangled (part of interest between stars) as:
+ * "_Z4 func1 *B4tag2* ParamsMangling" (ParamsMangling includes tag1),
+ * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes tag2),
+ * "_Z4 func2 *B4tag1* ParamsMangling" (ParamsMangling includes both).
+ *
+ * This is why why need to keep a list of tags that were written,
+ * and insert the missing one after parameter mangling has been written.
+ * Since there's a lot of operations that are not easily doable in DMD
+ * (since we can't use Phobos), this special container is implemented.
+ */
+private struct ABITagContainer
+{
+ private Array!StringExp written;
+
+ static ArrayLiteralExp forSymbol (Dsymbol s)
+ {
+ if (!s)
+ return null;
+ // If this is a template instance, we want the declaration,
+ // as that's where the UDAs are
+ if (auto ti = s.isTemplateInstance())
+ s = ti.tempdecl;
+ if (!s.userAttribDecl || !s.userAttribDecl.atts)
+ return null;
+
+ foreach (exp; *s.userAttribDecl.atts)
+ {
+ if (UserAttributeDeclaration.isGNUABITag(exp))
+ return (*exp.isStructLiteralExp().elements)[0]
+ .isArrayLiteralExp();
+ }
+ return null;
+ }
+
+ void writeSymbol(Dsymbol s, CppMangleVisitor self)
+ {
+ auto tale = forSymbol(s);
+ if (!tale) return;
+ if (self.substitute(tale))
+ return;
+ this.write(*self.buf, tale);
+ }
+
+ /**
+ * Write an ArrayLiteralExp (expected to be an ABI tag) to the buffer
+ *
+ * Params:
+ * buf = Buffer to write mangling to
+ * ale = GNU ABI tag array literal expression, semantically analyzed
+ */
+ void write (ref OutBuffer buf, ArrayLiteralExp ale, bool skipKnown = false)
+ {
+ void writeElem (StringExp exp)
+ {
+ const tag = exp.peekString();
+ buf.writestring("B");
+ buf.print(tag.length);
+ buf.writestring(tag);
+ }
+
+ bool match;
+ foreach (exp; *ale.elements)
+ {
+ auto elem = exp.toStringExp();
+ auto idx = closestIndex(this.written[], elem, match);
+ if (!match)
+ {
+ writeElem(elem);
+ this.written.insert(idx, elem);
+ }
+ else if (!skipKnown)
+ writeElem(elem);
+ }
+ }
+}
+
+/**
+ * Returns the closest index to to `exp` in `slice`
+ *
+ * Performs a binary search on `slice` (assumes `slice` is sorted),
+ * and returns either `exp`'s index in `slice` if `exact` is `true`,
+ * or the index at which `exp` can be inserted in `slice` if `exact is `false`.
+ * Inserting `exp` at the return value will keep the array sorted.
+ *
+ * Params:
+ * slice = The sorted slice to search into
+ * exp = The string expression to search for
+ * exact = If `true` on return, `exp` was found in `slice`
+ *
+ * Returns:
+ * Either the index to insert `exp` at (if `exact == false`),
+ * or the index of `exp` in `slice`.
+ */
+private size_t closestIndex (const(StringExp)[] slice, StringExp exp, out bool exact)
+{
+ if (!slice.length) return 0;
+
+ const StringExp* first = slice.ptr;
+ while (true)
+ {
+ int res = dstrcmp(exp.peekString(), slice[$ / 2].peekString());
+ if (res == 0)
+ {
+ exact = true;
+ return (&slice[$/2] - first);
+ }
+
+ if (slice.length == 1)
+ return (slice.ptr - first) + (res > 0);
+ slice = slice[(res > 0 ? $ / 2 : 0) .. (res > 0 ? $ : $ / 2)];
+ }
+}
+
+//
+unittest
+{
+ bool match;
+ auto s1 = new StringExp(Loc.initial, "Amande");
+ auto s2 = new StringExp(Loc.initial, "Baguette");
+ auto s3 = new StringExp(Loc.initial, "Croissant");
+ auto s4 = new StringExp(Loc.initial, "Framboises");
+ auto s5 = new StringExp(Loc.initial, "Proscuitto");
+
+ // Found, odd size
+ assert(closestIndex([s1, s2, s3, s4, s5], s1, match) == 0 && match);
+ assert(closestIndex([s1, s2, s3, s4, s5], s2, match) == 1 && match);
+ assert(closestIndex([s1, s2, s3, s4, s5], s3, match) == 2 && match);
+ assert(closestIndex([s1, s2, s3, s4, s5], s4, match) == 3 && match);
+ assert(closestIndex([s1, s2, s3, s4, s5], s5, match) == 4 && match);
+
+ // Not found, even size
+ assert(closestIndex([s2, s3, s4, s5], s1, match) == 0 && !match);
+ assert(closestIndex([s1, s3, s4, s5], s2, match) == 1 && !match);
+ assert(closestIndex([s1, s2, s4, s5], s3, match) == 2 && !match);
+ assert(closestIndex([s1, s2, s3, s5], s4, match) == 3 && !match);
+ assert(closestIndex([s1, s2, s3, s4], s5, match) == 4 && !match);
+
+ // Found, even size
+ assert(closestIndex([s1, s2, s3, s4], s1, match) == 0 && match);
+ assert(closestIndex([s1, s2, s3, s4], s2, match) == 1 && match);
+ assert(closestIndex([s1, s2, s3, s4], s3, match) == 2 && match);
+ assert(closestIndex([s1, s2, s3, s4], s4, match) == 3 && match);
+ assert(closestIndex([s1, s3, s4, s5], s5, match) == 3 && match);
+
+ // Not found, odd size
+ assert(closestIndex([s2, s4, s5], s1, match) == 0 && !match);
+ assert(closestIndex([s1, s4, s5], s2, match) == 1 && !match);
+ assert(closestIndex([s1, s2, s4], s3, match) == 2 && !match);
+ assert(closestIndex([s1, s3, s5], s4, match) == 2 && !match);
+ assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match);
+}
+
+/**
+ * Visits the return type of a function and writes leftover ABI tags
+ */
+extern(C++) private final class LeftoverVisitor : Visitor
+{
+ /// List of tags to write
+ private Array!StringExp toWrite;
+ /// List of tags to ignore
+ private const(Array!StringExp)* ignore;
+
+ ///
+ public this(const(Array!StringExp)* previous)
+ {
+ this.ignore = previous;
+ }
+
+ /// Reintroduce base class overloads
+ public alias visit = Visitor.visit;
+
+ /// Least specialized overload of each direct child of `RootObject`
+ public override void visit(Dsymbol o)
+ {
+ auto ale = ABITagContainer.forSymbol(o);
+ if (!ale) return;
+
+ bool match;
+ foreach (elem; *ale.elements)
+ {
+ auto se = elem.toStringExp();
+ closestIndex((*this.ignore)[], se, match);
+ if (match) continue;
+ auto idx = closestIndex(this.toWrite[], se, match);
+ if (!match)
+ this.toWrite.insert(idx, se);
+ }
+ }
+
+ /// Ditto
+ public override void visit(Type o)
+ {
+ if (auto sym = o.toDsymbol(null))
+ sym.accept(this);
+ }
+
+ /// Composite type
+ public override void visit(TypePointer o)
+ {
+ o.next.accept(this);
+ }
+
+ public override void visit(TypeReference o)
+ {
+ o.next.accept(this);
+ }
+}
diff --git a/gcc/d/dmd/ctfe.h b/gcc/d/dmd/ctfe.h
index 359739e24a0..242dd552b94 100644
--- a/gcc/d/dmd/ctfe.h
+++ b/gcc/d/dmd/ctfe.h
@@ -5,31 +5,15 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/dlang/dmd/blob/master/src/ctfe.h
+ * https://github.com/dlang/dmd/blob/master/src/dmd/ctfe.h
*/
#pragma once
-#include "arraytypes.h"
#include "tokens.h"
#include "expression.h"
/**
- Global status of the CTFE engine. Mostly used for performance diagnostics
- */
-struct CtfeStatus
-{
- static int callDepth; // current number of recursive calls
- /* When printing a stack trace,
- * suppress this number of calls
- */
- static int stackTraceCallsToSuppress;
- static int maxCallDepth; // highest number of recursive calls
- static int numArrayAllocs; // Number of allocated arrays
- static int numAssignments; // total number of assignments executed
-};
-
-/**
A reference to a class, or an interface. We need this when we
point to a base class (we must record what the type is).
*/
@@ -37,55 +21,35 @@ class ClassReferenceExp : public Expression
{
public:
StructLiteralExp *value;
- ClassReferenceExp(Loc loc, StructLiteralExp *lit, Type *type);
ClassDeclaration *originalClass();
/// Return index of the field, or -1 if not found
- int getFieldIndex(Type *fieldtype, unsigned fieldoffset);
- /// Return index of the field, or -1 if not found
/// Same as getFieldIndex, but checks for a direct match with the VarDeclaration
int findFieldIndexByName(VarDeclaration *v);
void accept(Visitor *v) { v->visit(this); }
};
-// The various functions are used only to detect compiler CTFE bugs
-Expression *getValue(VarDeclaration *vd);
-bool hasValue(VarDeclaration *vd);
-void setValueNull(VarDeclaration *vd);
-void setValueWithoutChecking(VarDeclaration *vd, Expression *newval);
-void setValue(VarDeclaration *vd, Expression *newval);
-
-/// Return index of the field, or -1 if not found
-/// Same as getFieldIndex, but checks for a direct match with the VarDeclaration
-int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v);
-
-
-/** An uninitialized value
+/**
+ An uninitialized value
*/
class VoidInitExp : public Expression
{
public:
VarDeclaration *var;
- VoidInitExp(VarDeclaration *var, Type *type);
- const char *toChars();
+ const char *toChars() const;
void accept(Visitor *v) { v->visit(this); }
};
-// Create an appropriate void initializer
-UnionExp voidInitLiteral(Type *t, VarDeclaration *var);
-
-/** Fake class which holds the thrown exception.
- Used for implementing exception handling.
+/**
+ Fake class which holds the thrown exception.
+ Used for implementing exception handling.
*/
class ThrownExceptionExp : public Expression
{
public:
ClassReferenceExp *thrown; // the thing being tossed
- ThrownExceptionExp(Loc loc, ClassReferenceExp *victim);
- const char *toChars();
- /// Generate an error message when this exception is not caught
- void generateUncaughtError();
+ const char *toChars() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -96,173 +60,5 @@ public:
class CTFEExp : public Expression
{
public:
- CTFEExp(TOK tok);
-
- const char *toChars();
-
- // Handy instances to share
- static CTFEExp *cantexp;
- static CTFEExp *voidexp;
- static CTFEExp *breakexp;
- static CTFEExp *continueexp;
- static CTFEExp *gotoexp;
-
- static bool isCantExp(Expression *e) { return e && e->op == TOKcantexp; }
- static bool isGotoExp(Expression *e) { return e && e->op == TOKgoto; }
+ const char *toChars() const;
};
-
-/****************************************************************/
-
-
-/// True if 'e' is TOKcantexp, or an exception
-bool exceptionOrCantInterpret(Expression *e);
-
-// Used for debugging only
-void showCtfeExpr(Expression *e, int level = 0);
-
-/// Return true if this is a valid CTFE expression
-bool isCtfeValueValid(Expression *newval);
-bool isCtfeReferenceValid(Expression *newval);
-
-/// Given expr, which evaluates to an array/AA/string literal,
-/// return true if it needs to be copied
-bool needToCopyLiteral(Expression *expr);
-
-/// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
-/// This value will be used for in-place modification.
-UnionExp copyLiteral(Expression *e);
-
-/// Set this literal to the given type, copying it if necessary
-Expression *paintTypeOntoLiteral(Type *type, Expression *lit);
-Expression *paintTypeOntoLiteral(UnionExp *pue, Type *type, Expression *lit);
-UnionExp paintTypeOntoLiteralCopy(Type *type, Expression *lit);
-
-/// Convert from a CTFE-internal slice, into a normal Expression
-Expression *resolveSlice(Expression *e, UnionExp *pue = NULL);
-
-/// Determine the array length, without interpreting the expression.
-uinteger_t resolveArrayLength(Expression *e);
-
-/// Create an array literal consisting of 'elem' duplicated 'dim' times.
-ArrayLiteralExp *createBlockDuplicatedArrayLiteral(UnionExp *pue, Loc loc, Type *type,
- Expression *elem, size_t dim);
-
-/// Create a string literal consisting of 'value' duplicated 'dim' times.
-StringExp *createBlockDuplicatedStringLiteral(UnionExp *pue, Loc loc, Type *type,
- unsigned value, size_t dim, unsigned char sz);
-
-
-/* Set dest = src, where both dest and src are container value literals
- * (ie, struct literals, or static arrays (can be an array literal or a string)
- * Assignment is recursively in-place.
- * Purpose: any reference to a member of 'dest' will remain valid after the
- * assignment.
- */
-void assignInPlace(Expression *dest, Expression *src);
-
-/// Duplicate the elements array, then set field 'indexToChange' = newelem.
-Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expression *newelem);
-
-/// Given an AA literal aae, set arr[index] = newval and return the new array.
-Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae,
- Expression *index, Expression *newval);
-
-/// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
-/// oldlen, change its length to newlen. If the newlen is longer than oldlen,
-/// all new elements will be set to the default initializer for the element type.
-UnionExp changeArrayLiteralLength(Loc loc, TypeArray *arrayType,
- Expression *oldval, size_t oldlen, size_t newlen);
-
-
-
-/// Return true if t is a pointer (not a function pointer)
-bool isPointer(Type *t);
-
-// For CTFE only. Returns true if 'e' is TRUE or a non-null pointer.
-bool isTrueBool(Expression *e);
-
-/// Is it safe to convert from srcPointee* to destPointee* ?
-/// srcPointee is the genuine type (never void).
-/// destPointee may be void.
-bool isSafePointerCast(Type *srcPointee, Type *destPointee);
-
-/// Given pointer e, return the memory block expression it points to,
-/// and set ofs to the offset within that memory block.
-Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs);
-
-/// Return true if agg1 and agg2 are pointers to the same memory block
-bool pointToSameMemoryBlock(Expression *agg1, Expression *agg2);
-
-// return e1 - e2 as an integer, or error if not possible
-UnionExp pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2);
-
-/// Return 1 if true, 0 if false
-/// -1 if comparison is illegal because they point to non-comparable memory blocks
-int comparePointers(TOK op, Expression *agg1, dinteger_t ofs1, Expression *agg2, dinteger_t ofs2);
-
-// Return eptr op e2, where eptr is a pointer, e2 is an integer,
-// and op is TOKadd or TOKmin
-UnionExp pointerArithmetic(Loc loc, TOK op, Type *type,
- Expression *eptr, Expression *e2);
-
-// True if conversion from type 'from' to 'to' involves a reinterpret_cast
-// floating point -> integer or integer -> floating point
-bool isFloatIntPaint(Type *to, Type *from);
-
-// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
-Expression *paintFloatInt(UnionExp *pue, Expression *fromVal, Type *to);
-
-/// Return true if t is an AA
-bool isAssocArray(Type *t);
-
-/// Given a template AA type, extract the corresponding built-in AA type
-TypeAArray *toBuiltinAAType(Type *t);
-
-/* Given an AA literal 'ae', and a key 'e2':
- * Return ae[e2] if present, or NULL if not found.
- * Return TOKcantexp on error.
- */
-Expression *findKeyInAA(Loc loc, AssocArrayLiteralExp *ae, Expression *e2);
-
-/// True if type is TypeInfo_Class
-bool isTypeInfo_Class(Type *type);
-
-
-/***********************************************
- COW const-folding operations
-***********************************************/
-
-/// Return true if non-pointer expression e can be compared
-/// with >,is, ==, etc, using ctfeCmp, ctfeEquals, ctfeIdentity
-bool isCtfeComparable(Expression *e);
-
-/// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1
-int ctfeEqual(Loc loc, TOK op, Expression *e1, Expression *e2);
-
-/// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1
-int ctfeIdentity(Loc loc, TOK op, Expression *e1, Expression *e2);
-
-/// Returns rawCmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-int specificCmp(TOK op, int rawCmp);
-
-/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-int intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2);
-
-/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-int intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2);
-
-/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-int realCmp(TOK op, real_t r1, real_t r2);
-
-/// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
-int ctfeCmp(Loc loc, TOK op, Expression *e1, Expression *e2);
-
-/// Returns e1 ~ e2. Resolves slices before concatenation.
-UnionExp ctfeCat(Loc loc, Type *type, Expression *e1, Expression *e2);
-
-/// Same as for constfold.Index, except that it only works for static arrays,
-/// dynamic arrays, and strings.
-Expression *ctfeIndex(Loc loc, Type *type, Expression *e1, uinteger_t indx);
-
-/// Cast 'e' of type 'type' to type 'to'.
-Expression *ctfeCast(UnionExp *pue, Loc loc, Type *type, Type *to, Expression *e);
diff --git a/gcc/d/dmd/ctfeexpr.c b/gcc/d/dmd/ctfeexpr.c
deleted file mode 100644
index a8e97833ad0..00000000000
--- a/gcc/d/dmd/ctfeexpr.c
+++ /dev/null
@@ -1,2127 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/ctfeexpr.c
- */
-
-#include "root/dsystem.h" // mem{cpy|set}()
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "expression.h"
-#include "declaration.h"
-#include "aggregate.h"
-// for AssocArray
-#include "id.h"
-#include "utf.h"
-#include "template.h"
-#include "ctfe.h"
-
-int RealEquals(real_t x1, real_t x2);
-
-/************** ClassReferenceExp ********************************************/
-
-ClassReferenceExp::ClassReferenceExp(Loc loc, StructLiteralExp *lit, Type *type)
- : Expression(loc, TOKclassreference, sizeof(ClassReferenceExp))
-{
- assert(lit && lit->sd && lit->sd->isClassDeclaration());
- this->value = lit;
- this->type = type;
-}
-
-ClassDeclaration *ClassReferenceExp::originalClass()
-{
- return value->sd->isClassDeclaration();
-}
-
-// Return index of the field, or -1 if not found
-int ClassReferenceExp::getFieldIndex(Type *fieldtype, unsigned fieldoffset)
-{
- ClassDeclaration *cd = originalClass();
- unsigned fieldsSoFar = 0;
- for (size_t j = 0; j < value->elements->length; j++)
- {
- while (j - fieldsSoFar >= cd->fields.length)
- {
- fieldsSoFar += cd->fields.length;
- cd = cd->baseClass;
- }
- VarDeclaration *v2 = cd->fields[j - fieldsSoFar];
- if (fieldoffset == v2->offset &&
- fieldtype->size() == v2->type->size())
- {
- return (int)(value->elements->length - fieldsSoFar - cd->fields.length + (j-fieldsSoFar));
- }
- }
- return -1;
-}
-
-// Return index of the field, or -1 if not found
-// Same as getFieldIndex, but checks for a direct match with the VarDeclaration
-int ClassReferenceExp::findFieldIndexByName(VarDeclaration *v)
-{
- ClassDeclaration *cd = originalClass();
- size_t fieldsSoFar = 0;
- for (size_t j = 0; j < value->elements->length; j++)
- {
- while (j - fieldsSoFar >= cd->fields.length)
- {
- fieldsSoFar += cd->fields.length;
- cd = cd->baseClass;
- }
- VarDeclaration *v2 = cd->fields[j - fieldsSoFar];
- if (v == v2)
- {
- return (int)(value->elements->length - fieldsSoFar - cd->fields.length + (j-fieldsSoFar));
- }
- }
- return -1;
-}
-
-/************** VoidInitExp ********************************************/
-
-VoidInitExp::VoidInitExp(VarDeclaration *var, Type *)
- : Expression(var->loc, TOKvoid, sizeof(VoidInitExp))
-{
- this->var = var;
- this->type = var->type;
-}
-
-const char *VoidInitExp::toChars()
-{
- return "void";
-}
-
-// Return index of the field, or -1 if not found
-// Same as getFieldIndex, but checks for a direct match with the VarDeclaration
-int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v)
-{
- for (size_t i = 0; i < sd->fields.length; ++i)
- {
- if (sd->fields[i] == v)
- return (int)i;
- }
- return -1;
-}
-
-/************** ThrownExceptionExp ********************************************/
-
-ThrownExceptionExp::ThrownExceptionExp(Loc loc, ClassReferenceExp *victim) : Expression(loc, TOKthrownexception, sizeof(ThrownExceptionExp))
-{
- this->thrown = victim;
- this->type = victim->type;
-}
-
-const char *ThrownExceptionExp::toChars()
-{
- return "CTFE ThrownException";
-}
-
-// Generate an error message when this exception is not caught
-void ThrownExceptionExp::generateUncaughtError()
-{
- UnionExp ue;
- Expression *e = resolveSlice((*thrown->value->elements)[0], &ue);
- StringExp *se = e->toStringExp();
- thrown->error("uncaught CTFE exception %s(%s)", thrown->type->toChars(), se ? se->toChars() : e->toChars());
-
- /* Also give the line where the throw statement was. We won't have it
- * in the case where the ThrowStatement is generated internally
- * (eg, in ScopeStatement)
- */
- if (loc.filename && !loc.equals(thrown->loc))
- errorSupplemental(loc, "thrown from here");
-}
-
-// True if 'e' is CTFEExp::cantexp, or an exception
-bool exceptionOrCantInterpret(Expression *e)
-{
- return e && (e->op == TOKcantexp || e->op == TOKthrownexception);
-}
-
-/********************** CTFEExp ******************************************/
-
-CTFEExp *CTFEExp::cantexp;
-CTFEExp *CTFEExp::voidexp;
-CTFEExp *CTFEExp::breakexp;
-CTFEExp *CTFEExp::continueexp;
-CTFEExp *CTFEExp::gotoexp;
-
-CTFEExp::CTFEExp(TOK tok)
- : Expression(Loc(), tok, sizeof(CTFEExp))
-{
- type = Type::tvoid;
-}
-
-const char *CTFEExp::toChars()
-{
- switch (op)
- {
- case TOKcantexp: return "<cant>";
- case TOKvoidexp: return "cast(void)0";
- case TOKbreak: return "<break>";
- case TOKcontinue: return "<continue>";
- case TOKgoto: return "<goto>";
- default: assert(0); return NULL;
- }
-}
-
-Expression *UnionExp::copy()
-{
- Expression *e = exp();
- //if (e->size > sizeof(u)) printf("%s\n", Token::toChars(e->op));
- assert(e->size <= sizeof(u));
- if (e->op == TOKcantexp) return CTFEExp::cantexp;
- if (e->op == TOKvoidexp) return CTFEExp::voidexp;
- if (e->op == TOKbreak) return CTFEExp::breakexp;
- if (e->op == TOKcontinue) return CTFEExp::continueexp;
- if (e->op == TOKgoto) return CTFEExp::gotoexp;
- return e->copy();
-}
-
-/************** Aggregate literals (AA/string/array/struct) ******************/
-
-// Given expr, which evaluates to an array/AA/string literal,
-// return true if it needs to be copied
-bool needToCopyLiteral(Expression *expr)
-{
- for (;;)
- {
- switch (expr->op)
- {
- case TOKarrayliteral:
- return ((ArrayLiteralExp *)expr)->ownedByCtfe == OWNEDcode;
- case TOKassocarrayliteral:
- return ((AssocArrayLiteralExp *)expr)->ownedByCtfe == OWNEDcode;
- case TOKstructliteral:
- return ((StructLiteralExp *)expr)->ownedByCtfe == OWNEDcode;
- case TOKstring:
- case TOKthis:
- case TOKvar:
- return false;
- case TOKassign:
- return false;
- case TOKindex:
- case TOKdotvar:
- case TOKslice:
- case TOKcast:
- expr = ((UnaExp *)expr)->e1;
- continue;
- case TOKcat:
- return needToCopyLiteral(((BinExp *)expr)->e1) ||
- needToCopyLiteral(((BinExp *)expr)->e2);
- case TOKcatass:
- expr = ((BinExp *)expr)->e2;
- continue;
- default:
- return false;
- }
- }
-}
-
-Expressions *copyLiteralArray(Expressions *oldelems, Expression *basis = NULL)
-{
- if (!oldelems)
- return oldelems;
- CtfeStatus::numArrayAllocs++;
- Expressions *newelems = new Expressions();
- newelems->setDim(oldelems->length);
- for (size_t i = 0; i < oldelems->length; i++)
- {
- Expression *el = (*oldelems)[i];
- if (!el)
- el = basis;
- (*newelems)[i] = copyLiteral(el).copy();
- }
- return newelems;
-}
-
-// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
-// This value will be used for in-place modification.
-UnionExp copyLiteral(Expression *e)
-{
- UnionExp ue;
- if (e->op == TOKstring) // syntaxCopy doesn't make a copy for StringExp!
- {
- StringExp *se = (StringExp *)e;
- utf8_t *s = (utf8_t *)mem.xcalloc(se->len + 1, se->sz);
- memcpy(s, se->string, se->len * se->sz);
- new(&ue) StringExp(se->loc, s, se->len);
- StringExp *se2 = (StringExp *)ue.exp();
- se2->committed = se->committed;
- se2->postfix = se->postfix;
- se2->type = se->type;
- se2->sz = se->sz;
- se2->ownedByCtfe = OWNEDctfe;
- return ue;
- }
- if (e->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
- Expressions *elements = copyLiteralArray(ale->elements, ale->basis);
-
- new(&ue) ArrayLiteralExp(e->loc, e->type, elements);
-
- ArrayLiteralExp *r = (ArrayLiteralExp *)ue.exp();
- r->ownedByCtfe = OWNEDctfe;
- return ue;
- }
- if (e->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e;
- new(&ue) AssocArrayLiteralExp(e->loc, copyLiteralArray(aae->keys), copyLiteralArray(aae->values));
- AssocArrayLiteralExp *r = (AssocArrayLiteralExp *)ue.exp();
- r->type = e->type;
- r->ownedByCtfe = OWNEDctfe;
- return ue;
- }
- if (e->op == TOKstructliteral)
- {
- /* syntaxCopy doesn't work for struct literals, because of a nasty special
- * case: block assignment is permitted inside struct literals, eg,
- * an int[4] array can be initialized with a single int.
- */
- StructLiteralExp *sle = (StructLiteralExp *)e;
- Expressions *oldelems = sle->elements;
- Expressions * newelems = new Expressions();
- newelems->setDim(oldelems->length);
- for (size_t i = 0; i < newelems->length; i++)
- {
- // We need the struct definition to detect block assignment
- VarDeclaration *v = sle->sd->fields[i];
- Expression *m = (*oldelems)[i];
-
- // If it is a void assignment, use the default initializer
- if (!m)
- m = voidInitLiteral(v->type, v).copy();
-
- if (v->type->ty == Tarray || v->type->ty == Taarray)
- {
- // Don't have to copy array references
- }
- else
- {
- // Buzilla 15681: Copy the source element always.
- m = copyLiteral(m).copy();
-
- // Block assignment from inside struct literals
- if (v->type->ty != m->type->ty && v->type->ty == Tsarray)
- {
- TypeSArray *tsa = (TypeSArray *)v->type;
- size_t len = (size_t)tsa->dim->toInteger();
- UnionExp uex;
- m = createBlockDuplicatedArrayLiteral(&uex, e->loc, v->type, m, len);
- if (m == uex.exp())
- m = uex.copy();
- }
- }
- (*newelems)[i] = m;
- }
- new(&ue) StructLiteralExp(e->loc, sle->sd, newelems, sle->stype);
- StructLiteralExp *r = (StructLiteralExp *)ue.exp();
- r->type = e->type;
- r->ownedByCtfe = OWNEDctfe;
- r->origin = ((StructLiteralExp *)e)->origin;
- return ue;
- }
- if (e->op == TOKfunction || e->op == TOKdelegate ||
- e->op == TOKsymoff || e->op == TOKnull ||
- e->op == TOKvar || e->op == TOKdotvar ||
- e->op == TOKint64 || e->op == TOKfloat64 ||
- e->op == TOKchar || e->op == TOKcomplex80 ||
- e->op == TOKvoid || e->op == TOKvector ||
- e->op == TOKtypeid)
- {
- // Simple value types
- // Keep e1 for DelegateExp and DotVarExp
- new(&ue) UnionExp(e);
- Expression *r = ue.exp();
- r->type = e->type;
- return ue;
- }
- if (e->op == TOKslice)
- {
- SliceExp *se = (SliceExp *)e;
- if (se->type->toBasetype()->ty == Tsarray)
- {
- // same with resolveSlice()
- if (se->e1->op == TOKnull)
- {
- new(&ue) NullExp(se->loc, se->type);
- return ue;
- }
- ue = Slice(se->type, se->e1, se->lwr, se->upr);
- assert(ue.exp()->op == TOKarrayliteral);
- ArrayLiteralExp *r = (ArrayLiteralExp *)ue.exp();
- r->elements = copyLiteralArray(r->elements);
- r->ownedByCtfe = OWNEDctfe;
- return ue;
- }
- else
- {
- // Array slices only do a shallow copy
- new(&ue) SliceExp(e->loc, se->e1, se->lwr, se->upr);
- Expression *r = ue.exp();
- r->type = e->type;
- return ue;
- }
- }
- if (isPointer(e->type))
- {
- // For pointers, we only do a shallow copy.
- if (e->op == TOKaddress)
- new(&ue) AddrExp(e->loc, ((AddrExp *)e)->e1);
- else if (e->op == TOKindex)
- new(&ue) IndexExp(e->loc, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2);
- else if (e->op == TOKdotvar)
- {
- new(&ue) DotVarExp(e->loc, ((DotVarExp *)e)->e1,
- ((DotVarExp *)e)->var, ((DotVarExp *)e)->hasOverloads);
- }
- else
- assert(0);
- Expression *r = ue.exp();
- r->type = e->type;
- return ue;
- }
- if (e->op == TOKclassreference)
- {
- new(&ue) ClassReferenceExp(e->loc, ((ClassReferenceExp *)e)->value, e->type);
- return ue;
- }
- if (e->op == TOKerror)
- {
- new(&ue) UnionExp(e);
- return ue;
- }
- e->error("CTFE internal error: literal %s", e->toChars());
- assert(0);
- return ue;
-}
-
-/* Deal with type painting.
- * Type painting is a major nuisance: we can't just set
- * e->type = type, because that would change the original literal.
- * But, we can't simply copy the literal either, because that would change
- * the values of any pointers.
- */
-Expression *paintTypeOntoLiteral(Type *type, Expression *lit)
-{
- if (lit->type->equals(type))
- return lit;
- return paintTypeOntoLiteralCopy(type, lit).copy();
-}
-
-Expression *paintTypeOntoLiteral(UnionExp *pue, Type *type, Expression *lit)
-{
- if (lit->type->equals(type))
- return lit;
- *pue = paintTypeOntoLiteralCopy(type, lit);
- return pue->exp();
-}
-
-UnionExp paintTypeOntoLiteralCopy(Type *type, Expression *lit)
-{
- UnionExp ue;
-
- if (lit->type->equals(type))
- {
- new(&ue) UnionExp(lit);
- return ue;
- }
-
- // If it is a cast to inout, retain the original type of the referenced part.
- if (type->hasWild() && type->hasPointers())
- {
- new(&ue) UnionExp(lit);
- ue.exp()->type = type;
- return ue;
- }
-
- if (lit->op == TOKslice)
- {
- SliceExp *se = (SliceExp *)lit;
- new(&ue) SliceExp(lit->loc, se->e1, se->lwr, se->upr);
- }
- else if (lit->op == TOKindex)
- {
- IndexExp *ie = (IndexExp *)lit;
- new(&ue) IndexExp(lit->loc, ie->e1, ie->e2);
- }
- else if (lit->op == TOKarrayliteral)
- {
- new(&ue) SliceExp(lit->loc, lit,
- new IntegerExp(Loc(), 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit).copy());
- }
- else if (lit->op == TOKstring)
- {
- // For strings, we need to introduce another level of indirection
- new(&ue) SliceExp(lit->loc, lit,
- new IntegerExp(Loc(), 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit).copy());
- }
- else if (lit->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)lit;
- // TODO: we should be creating a reference to this AAExp, not
- // just a ref to the keys and values.
- OwnedBy wasOwned = aae->ownedByCtfe;
- new(&ue) AssocArrayLiteralExp(lit->loc, aae->keys, aae->values);
- aae = (AssocArrayLiteralExp *)ue.exp();
- aae->ownedByCtfe = wasOwned;
- }
- else
- {
- // Can't type paint from struct to struct*; this needs another
- // level of indirection
- if (lit->op == TOKstructliteral && isPointer(type))
- lit->error("CTFE internal error: painting %s", type->toChars());
- ue = copyLiteral(lit);
- }
- ue.exp()->type = type;
- return ue;
-}
-
-/*************************************
- * If e is a SliceExp, constant fold it.
- * Params:
- * e = expression to resolve
- * pue = if not null, store resulting expression here
- * Returns:
- * resulting expression
- */
-Expression *resolveSlice(Expression *e, UnionExp *pue)
-{
- if (e->op != TOKslice)
- return e;
- SliceExp *se = (SliceExp *)e;
- if (se->e1->op == TOKnull)
- return se->e1;
- if (pue)
- {
- *pue = Slice(e->type, se->e1, se->lwr, se->upr);
- return pue->exp();
- }
- else
- return Slice(e->type, se->e1, se->lwr, se->upr).copy();
-}
-
-/* Determine the array length, without interpreting it.
- * e must be an array literal, or a slice
- * It's very wasteful to resolve the slice when we only
- * need the length.
- */
-uinteger_t resolveArrayLength(Expression *e)
-{
- if (e->op == TOKvector)
- return ((VectorExp *)e)->dim;
-
- if (e->op == TOKnull)
- return 0;
- if (e->op == TOKslice)
- {
- uinteger_t ilo = ((SliceExp *)e)->lwr->toInteger();
- uinteger_t iup = ((SliceExp *)e)->upr->toInteger();
- return iup - ilo;
- }
- if (e->op == TOKstring)
- {
- return ((StringExp *)e)->len;
- }
- if (e->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
- return ale->elements ? ale->elements->length : 0;
- }
- if (e->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e;
- return ale->keys->length;
- }
- assert(0);
- return 0;
-}
-
-/******************************
- * Helper for NewExp
- * Create an array literal consisting of 'elem' duplicated 'dim' times.
- * Params:
- * pue = where to store result
- * loc = source location where the interpretation occurs
- * type = target type of the result
- * elem = the source of array element, it will be owned by the result
- * dim = element number of the result
- * Returns:
- * Constructed ArrayLiteralExp
- */
-ArrayLiteralExp *createBlockDuplicatedArrayLiteral(UnionExp *pue, Loc loc, Type *type,
- Expression *elem, size_t dim)
-{
- if (type->ty == Tsarray && type->nextOf()->ty == Tsarray && elem->type->ty != Tsarray)
- {
- // If it is a multidimensional array literal, do it recursively
- TypeSArray *tsa = (TypeSArray *)type->nextOf();
- size_t len = (size_t)tsa->dim->toInteger();
- UnionExp ue;
- elem = createBlockDuplicatedArrayLiteral(&ue, loc, type->nextOf(), elem, len);
- if (elem == ue.exp())
- elem = ue.copy();
- }
-
- // Buzilla 15681
- Type *tb = elem->type->toBasetype();
- const bool mustCopy = tb->ty == Tstruct || tb->ty == Tsarray;
-
- Expressions *elements = new Expressions();
- elements->setDim(dim);
- for (size_t i = 0; i < dim; i++)
- {
- (*elements)[i] = mustCopy ? copyLiteral(elem).copy() : elem;
- }
- new(pue) ArrayLiteralExp(loc, type, elements);
- ArrayLiteralExp *ale = (ArrayLiteralExp *)pue->exp();
- ale->ownedByCtfe = OWNEDctfe;
- return ale;
-}
-
-/******************************
- * Helper for NewExp
- * Create a string literal consisting of 'value' duplicated 'dim' times.
- */
-StringExp *createBlockDuplicatedStringLiteral(UnionExp *pue, Loc loc, Type *type,
- unsigned value, size_t dim, unsigned char sz)
-{
- utf8_t *s = (utf8_t *)mem.xcalloc(dim + 1, sz);
- for (size_t elemi = 0; elemi < dim; ++elemi)
- {
- switch (sz)
- {
- case 1: s[elemi] = (utf8_t)value; break;
- case 2: ((unsigned short *)s)[elemi] = (unsigned short)value; break;
- case 4: ((unsigned *)s)[elemi] = value; break;
- default: assert(0);
- }
- }
- new(pue) StringExp(loc, s, dim);
- StringExp *se = (StringExp *)pue->exp();
- se->type = type;
- se->sz = sz;
- se->committed = true;
- se->ownedByCtfe = OWNEDctfe;
- return se;
-}
-
-// Return true if t is an AA
-bool isAssocArray(Type *t)
-{
- t = t->toBasetype();
- if (t->ty == Taarray)
- return true;
- return false;
-}
-
-// Given a template AA type, extract the corresponding built-in AA type
-TypeAArray *toBuiltinAAType(Type *t)
-{
- t = t->toBasetype();
- if (t->ty == Taarray)
- return (TypeAArray *)t;
- assert(0);
- return NULL;
-}
-
-/************** TypeInfo operations ************************************/
-
-// Return true if type is TypeInfo_Class
-bool isTypeInfo_Class(Type *type)
-{
- return type->ty == Tclass &&
- (Type::dtypeinfo == ((TypeClass *)type)->sym ||
- Type::dtypeinfo->isBaseOf(((TypeClass *)type)->sym, NULL));
-}
-
-/************** Pointer operations ************************************/
-
-// Return true if t is a pointer (not a function pointer)
-bool isPointer(Type *t)
-{
- Type * tb = t->toBasetype();
- return tb->ty == Tpointer && tb->nextOf()->ty != Tfunction;
-}
-
-// For CTFE only. Returns true if 'e' is true or a non-null pointer.
-bool isTrueBool(Expression *e)
-{
- return e->isBool(true) ||
- ((e->type->ty == Tpointer || e->type->ty == Tclass) && e->op != TOKnull);
-}
-
-/* Is it safe to convert from srcPointee* to destPointee* ?
- * srcPointee is the genuine type (never void).
- * destPointee may be void.
- */
-bool isSafePointerCast(Type *srcPointee, Type *destPointee)
-{
- // It's safe to cast S** to D** if it's OK to cast S* to D*
- while (srcPointee->ty == Tpointer && destPointee->ty == Tpointer)
- {
- srcPointee = srcPointee->nextOf();
- destPointee = destPointee->nextOf();
- }
-
- // It's OK if both are the same (modulo const)
- if (srcPointee->constConv(destPointee))
- return true;
-
- // It's OK if function pointers differ only in safe/pure/nothrow
- if (srcPointee->ty == Tfunction && destPointee->ty == Tfunction)
- return srcPointee->covariant(destPointee) == 1;
-
- // it's OK to cast to void*
- if (destPointee->ty == Tvoid)
- return true;
-
- // It's OK to cast from V[K] to void*
- if (srcPointee->ty == Taarray && destPointee == Type::tvoidptr)
- return true;
-
- // It's OK if they are the same size (static array of) integers, eg:
- // int* --> uint*
- // int[5][] --> uint[5][]
- if (srcPointee->ty == Tsarray && destPointee->ty == Tsarray)
- {
- if (srcPointee->size() != destPointee->size())
- return false;
- srcPointee = srcPointee->baseElemOf();
- destPointee = destPointee->baseElemOf();
- }
- return srcPointee->isintegral() &&
- destPointee->isintegral() &&
- srcPointee->size() == destPointee->size();
-}
-
-Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs)
-{
- *ofs = 0;
- if (e->op == TOKaddress)
- e = ((AddrExp *)e)->e1;
- if (e->op == TOKsymoff)
- *ofs = ((SymOffExp *)e)->offset;
- if (e->op == TOKdotvar)
- {
- Expression *ex = ((DotVarExp *)e)->e1;
- VarDeclaration *v = ((DotVarExp *)e)->var->isVarDeclaration();
- assert(v);
- StructLiteralExp *se = ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value : (StructLiteralExp *)ex;
- // We can't use getField, because it makes a copy
- unsigned i;
- if (ex->op == TOKclassreference)
- i = ((ClassReferenceExp *)ex)->getFieldIndex(e->type, v->offset);
- else
- i = se->getFieldIndex(e->type, v->offset);
- e = (*se->elements)[i];
- }
- if (e->op == TOKindex)
- {
- IndexExp *ie = (IndexExp *)e;
- // Note that each AA element is part of its own memory block
- if ((ie->e1->type->ty == Tarray ||
- ie->e1->type->ty == Tsarray ||
- ie->e1->op == TOKstring ||
- ie->e1->op == TOKarrayliteral) &&
- ie->e2->op == TOKint64)
- {
- *ofs = ie->e2->toInteger();
- return ie->e1;
- }
- }
- if (e->op == TOKslice && e->type->toBasetype()->ty == Tsarray)
- {
- SliceExp *se = (SliceExp *)e;
- if ((se->e1->type->ty == Tarray ||
- se->e1->type->ty == Tsarray ||
- se->e1->op == TOKstring ||
- se->e1->op == TOKarrayliteral) &&
- se->lwr->op == TOKint64)
- {
- *ofs = se->lwr->toInteger();
- return se->e1;
- }
- }
- return e;
-}
-
-/** Return true if agg1 and agg2 are pointers to the same memory block
-*/
-bool pointToSameMemoryBlock(Expression *agg1, Expression *agg2)
-{
- if (agg1 == agg2)
- return true;
-
- // For integers cast to pointers, we regard them as non-comparable
- // unless they are identical. (This may be overly strict).
- if (agg1->op == TOKint64 && agg2->op == TOKint64 &&
- agg1->toInteger() == agg2->toInteger())
- {
- return true;
- }
-
- // Note that type painting can occur with VarExp, so we
- // must compare the variables being pointed to.
- if (agg1->op == TOKvar && agg2->op == TOKvar &&
- ((VarExp *)agg1)->var == ((VarExp *)agg2)->var)
- {
- return true;
- }
- if (agg1->op == TOKsymoff && agg2->op == TOKsymoff &&
- ((SymOffExp *)agg1)->var == ((SymOffExp *)agg2)->var)
- {
- return true;
- }
-
- return false;
-}
-
-// return e1 - e2 as an integer, or error if not possible
-UnionExp pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- UnionExp ue;
- dinteger_t ofs1, ofs2;
- Expression *agg1 = getAggregateFromPointer(e1, &ofs1);
- Expression *agg2 = getAggregateFromPointer(e2, &ofs2);
- if (agg1 == agg2)
- {
- Type *pointee = ((TypePointer *)agg1->type)->next;
- dinteger_t sz = pointee->size();
- new(&ue) IntegerExp(loc, (ofs1 - ofs2) * sz, type);
- }
- else if (agg1->op == TOKstring && agg2->op == TOKstring)
- {
- if (((StringExp *)agg1)->string == ((StringExp *)agg2)->string)
- {
- Type *pointee = ((TypePointer *)agg1->type)->next;
- dinteger_t sz = pointee->size();
- new(&ue) IntegerExp(loc, (ofs1 - ofs2) * sz, type);
- }
- }
- else if (agg1->op == TOKsymoff && agg2->op == TOKsymoff &&
- ((SymOffExp *)agg1)->var == ((SymOffExp *)agg2)->var)
- {
- new(&ue) IntegerExp(loc, ofs1 - ofs2, type);
- }
- else
- {
- error(loc, "%s - %s cannot be interpreted at compile time: cannot subtract "
- "pointers to two different memory blocks",
- e1->toChars(), e2->toChars());
- new(&ue) CTFEExp(TOKcantexp);
- }
- return ue;
-}
-
-// Return eptr op e2, where eptr is a pointer, e2 is an integer,
-// and op is TOKadd or TOKmin
-UnionExp pointerArithmetic(Loc loc, TOK op, Type *type,
- Expression *eptr, Expression *e2)
-{
- UnionExp ue;
-
- if (eptr->type->nextOf()->ty == Tvoid)
- {
- error(loc, "cannot perform arithmetic on void* pointers at compile time");
- Lcant:
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
-
- dinteger_t ofs1;
- if (eptr->op == TOKaddress)
- eptr = ((AddrExp *)eptr)->e1;
- Expression *agg1 = getAggregateFromPointer(eptr, &ofs1);
- if (agg1->op == TOKsymoff)
- {
- if (((SymOffExp *)agg1)->var->type->ty != Tsarray)
- {
- error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
- goto Lcant;
- }
- }
- else if (agg1->op != TOKstring && agg1->op != TOKarrayliteral)
- {
- error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
- goto Lcant;
- }
- dinteger_t ofs2 = e2->toInteger();
-
- Type *pointee = ((TypeNext *)agg1->type->toBasetype())->next;
- dinteger_t sz = pointee->size();
-
- sinteger_t indx;
- dinteger_t len;
- if (agg1->op == TOKsymoff)
- {
- indx = ofs1 / sz;
- len = ((TypeSArray *)((SymOffExp *)agg1)->var->type)->dim->toInteger();
- }
- else
- {
- Expression *dollar = ArrayLength(Type::tsize_t, agg1).copy();
- assert(!CTFEExp::isCantExp(dollar));
- indx = ofs1;
- len = dollar->toInteger();
- }
- if (op == TOKadd || op == TOKaddass || op == TOKplusplus)
- indx += ofs2 / sz;
- else if (op == TOKmin || op == TOKminass || op == TOKminusminus)
- indx -= ofs2 / sz;
- else
- {
- error(loc, "CTFE internal error: bad pointer operation");
- goto Lcant;
- }
-
- if (indx < 0 || len < (dinteger_t)indx)
- {
- error(loc, "cannot assign pointer to index %lld inside memory block [0..%lld]", (ulonglong)indx, (ulonglong)len);
- goto Lcant;
- }
-
- if (agg1->op == TOKsymoff)
- {
- new(&ue) SymOffExp(loc, ((SymOffExp *)agg1)->var, indx * sz);
- SymOffExp *se = (SymOffExp *)ue.exp();
- se->type = type;
- return ue;
- }
-
- if (agg1->op != TOKarrayliteral && agg1->op != TOKstring)
- {
- error(loc, "CTFE internal error: pointer arithmetic %s", agg1->toChars());
- goto Lcant;
- }
-
- if (eptr->type->toBasetype()->ty == Tsarray)
- {
- dinteger_t dim = ((TypeSArray *)eptr->type->toBasetype())->dim->toInteger();
-
- // Create a CTFE pointer &agg1[indx .. indx+dim]
- SliceExp *se = new SliceExp(loc, agg1,
- new IntegerExp(loc, indx, Type::tsize_t),
- new IntegerExp(loc, indx + dim, Type::tsize_t));
- se->type = type->toBasetype()->nextOf();
- new(&ue) AddrExp(loc, se);
- ue.exp()->type = type;
- return ue;
- }
-
- // Create a CTFE pointer &agg1[indx]
- IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t);
- Expression *ie = new IndexExp(loc, agg1, ofs);
- ie->type = type->toBasetype()->nextOf(); // Bugzilla 13992
- new(&ue) AddrExp(loc, ie);
- ue.exp()->type = type;
- return ue;
-}
-
-// Return 1 if true, 0 if false
-// -1 if comparison is illegal because they point to non-comparable memory blocks
-int comparePointers(TOK op, Expression *agg1, dinteger_t ofs1, Expression *agg2, dinteger_t ofs2)
-{
- if (pointToSameMemoryBlock(agg1, agg2))
- {
- int n;
- switch (op)
- {
- case TOKlt: n = (ofs1 < ofs2); break;
- case TOKle: n = (ofs1 <= ofs2); break;
- case TOKgt: n = (ofs1 > ofs2); break;
- case TOKge: n = (ofs1 >= ofs2); break;
- case TOKidentity:
- case TOKequal: n = (ofs1 == ofs2); break;
- case TOKnotidentity:
- case TOKnotequal: n = (ofs1 != ofs2); break;
- default:
- assert(0);
- }
- return n;
- }
- bool null1 = (agg1->op == TOKnull);
- bool null2 = (agg2->op == TOKnull);
-
- int cmp;
- if (null1 || null2)
- {
- switch (op)
- {
- case TOKlt: cmp = null1 && !null2; break;
- case TOKgt: cmp = !null1 && null2; break;
- case TOKle: cmp = null1; break;
- case TOKge: cmp = null2; break;
- case TOKidentity:
- case TOKequal:
- case TOKnotidentity: // 'cmp' gets inverted below
- case TOKnotequal:
- cmp = (null1 == null2);
- break;
- default:
- assert(0);
- }
- }
- else
- {
- switch (op)
- {
- case TOKidentity:
- case TOKequal:
- case TOKnotidentity: // 'cmp' gets inverted below
- case TOKnotequal:
- cmp = 0;
- break;
- default:
- return -1; // memory blocks are different
- }
- }
- if (op == TOKnotidentity || op == TOKnotequal)
- cmp ^= 1;
- return cmp;
-}
-
-// True if conversion from type 'from' to 'to' involves a reinterpret_cast
-// floating point -> integer or integer -> floating point
-bool isFloatIntPaint(Type *to, Type *from)
-{
- return from->size() == to->size() &&
- ((from->isintegral() && to->isfloating()) ||
- (from->isfloating() && to->isintegral()));
-}
-
-// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
-Expression *paintFloatInt(UnionExp *pue, Expression *fromVal, Type *to)
-{
- if (exceptionOrCantInterpret(fromVal))
- return fromVal;
-
- assert(to->size() == 4 || to->size() == 8);
- return Compiler::paintAsType(pue, fromVal, to);
-}
-
-/******** Constant folding, with support for CTFE ***************************/
-
-/// Return true if non-pointer expression e can be compared
-/// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
-bool isCtfeComparable(Expression *e)
-{
- if (e->op == TOKslice)
- e = ((SliceExp *)e)->e1;
-
- if (e->isConst() != 1)
- {
- if (e->op == TOKnull ||
- e->op == TOKstring ||
- e->op == TOKfunction ||
- e->op == TOKdelegate ||
- e->op == TOKarrayliteral ||
- e->op == TOKstructliteral ||
- e->op == TOKassocarrayliteral ||
- e->op == TOKclassreference)
- {
- return true;
- }
- // Bugzilla 14123: TypeInfo object is comparable in CTFE
- if (e->op == TOKtypeid)
- return true;
-
- return false;
- }
- return true;
-}
-
-/// Map TOK comparison ops
-template <typename N>
-static bool numCmp(TOK op, N n1, N n2)
-{
- switch (op)
- {
- case TOKlt:
- return n1 < n2;
- case TOKle:
- return n1 <= n2;
- case TOKgt:
- return n1 > n2;
- case TOKge:
- return n1 >= n2;
-
- default:
- assert(0);
- }
- return false;
-}
-
-/// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-int specificCmp(TOK op, int rawCmp)
-{
- return numCmp<int>(op, rawCmp, 0);
-}
-
-/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-int intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2)
-{
- return numCmp<dinteger_t>(op, n1, n2);
-}
-
-/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-int intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2)
-{
- return numCmp<sinteger_t>(op, n1, n2);
-}
-
-/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-int realCmp(TOK op, real_t r1, real_t r2)
-{
- // Don't rely on compiler, handle NAN arguments separately
- if (CTFloat::isNaN(r1) || CTFloat::isNaN(r2)) // if unordered
- {
- switch (op)
- {
- case TOKlt:
- case TOKle:
- case TOKgt:
- case TOKge:
- break;
-
- default:
- assert(0);
- }
- return 0;
- }
- else
- {
- return numCmp<real_t>(op, r1, r2);
- }
-}
-
-int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2);
-
-/* Conceptually the same as memcmp(e1, e2).
- * e1 and e2 may be strings, arrayliterals, or slices.
- * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
- * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
- */
-int ctfeCmpArrays(Loc loc, Expression *e1, Expression *e2, uinteger_t len)
-{
- // Resolve slices, if necessary
- uinteger_t lo1 = 0;
- uinteger_t lo2 = 0;
-
- Expression *x = e1;
- if (x->op == TOKslice)
- {
- lo1 = ((SliceExp *)x)->lwr->toInteger();
- x = ((SliceExp *)x)->e1;
- }
- StringExp *se1 = (x->op == TOKstring) ? (StringExp *)x : NULL;
- ArrayLiteralExp *ae1 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : NULL;
-
- x = e2;
- if (x->op == TOKslice)
- {
- lo2 = ((SliceExp *)x)->lwr->toInteger();
- x = ((SliceExp *)x)->e1;
- }
- StringExp *se2 = (x->op == TOKstring) ? (StringExp *)x : NULL;
- ArrayLiteralExp *ae2 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : NULL;
-
- // Now both must be either TOKarrayliteral or TOKstring
- if (se1 && se2)
- return sliceCmpStringWithString(se1, se2, (size_t)lo1, (size_t)lo2, (size_t)len);
- if (se1 && ae2)
- return sliceCmpStringWithArray(se1, ae2, (size_t)lo1, (size_t)lo2, (size_t)len);
- if (se2 && ae1)
- return -sliceCmpStringWithArray(se2, ae1, (size_t)lo2, (size_t)lo1, (size_t)len);
-
- assert (ae1 && ae2);
- // Comparing two array literals. This case is potentially recursive.
- // If they aren't strings, we just need an equality check rather than
- // a full cmp.
- bool needCmp = ae1->type->nextOf()->isintegral();
- for (size_t i = 0; i < (size_t)len; i++)
- {
- Expression *ee1 = (*ae1->elements)[(size_t)(lo1 + i)];
- Expression *ee2 = (*ae2->elements)[(size_t)(lo2 + i)];
- if (needCmp)
- {
- sinteger_t c = ee1->toInteger() - ee2->toInteger();
- if (c > 0)
- return 1;
- if (c < 0)
- return -1;
- }
- else
- {
- if (ctfeRawCmp(loc, ee1, ee2))
- return 1;
- }
- }
- return 0;
-}
-
-/* Given a delegate expression e, return .funcptr.
- * If e is NullExp, return NULL.
- */
-FuncDeclaration *funcptrOf(Expression *e)
-{
- assert(e->type->ty == Tdelegate);
-
- if (e->op == TOKdelegate)
- return ((DelegateExp *)e)->func;
- if (e->op == TOKfunction)
- return ((FuncExp *)e)->fd;
- assert(e->op == TOKnull);
- return NULL;
-}
-
-bool isArray(Expression *e)
-{
- return e->op == TOKarrayliteral || e->op == TOKstring ||
- e->op == TOKslice || e->op == TOKnull;
-}
-
-/* For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
- * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
- */
-int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2)
-{
- if (e1->op == TOKclassreference || e2->op == TOKclassreference)
- {
- if (e1->op == TOKclassreference && e2->op == TOKclassreference &&
- ((ClassReferenceExp *)e1)->value == ((ClassReferenceExp *)e2)->value)
- return 0;
- return 1;
- }
- if (e1->op == TOKtypeid && e2->op == TOKtypeid)
- {
- // printf("e1: %s\n", e1->toChars());
- // printf("e2: %s\n", e2->toChars());
- Type *t1 = isType(((TypeidExp *)e1)->obj);
- Type *t2 = isType(((TypeidExp *)e2)->obj);
- assert(t1);
- assert(t2);
- return t1 != t2;
- }
-
- // null == null, regardless of type
-
- if (e1->op == TOKnull && e2->op == TOKnull)
- return 0;
-
- if (e1->type->ty == Tpointer && e2->type->ty == Tpointer)
- {
- // Can only be an equality test.
-
- dinteger_t ofs1, ofs2;
- Expression *agg1 = getAggregateFromPointer(e1, &ofs1);
- Expression *agg2 = getAggregateFromPointer(e2, &ofs2);
- if ((agg1 == agg2) || (agg1->op == TOKvar && agg2->op == TOKvar &&
- ((VarExp *)agg1)->var == ((VarExp *)agg2)->var))
- {
- if (ofs1 == ofs2)
- return 0;
- }
- return 1;
- }
- if (e1->type->ty == Tdelegate && e2->type->ty == Tdelegate)
- {
- // If .funcptr isn't the same, they are not equal
-
- if (funcptrOf(e1) != funcptrOf(e2))
- return 1;
-
- // If both are delegate literals, assume they have the
- // same closure pointer. TODO: We don't support closures yet!
- if (e1->op == TOKfunction && e2->op == TOKfunction)
- return 0;
- assert(e1->op == TOKdelegate && e2->op == TOKdelegate);
-
- // Same .funcptr. Do they have the same .ptr?
- Expression * ptr1 = ((DelegateExp *)e1)->e1;
- Expression * ptr2 = ((DelegateExp *)e2)->e1;
-
- dinteger_t ofs1, ofs2;
- Expression *agg1 = getAggregateFromPointer(ptr1, &ofs1);
- Expression *agg2 = getAggregateFromPointer(ptr2, &ofs2);
- // If they are TOKvar, it means they are FuncDeclarations
- if ((agg1 == agg2 && ofs1 == ofs2) ||
- (agg1->op == TOKvar && agg2->op == TOKvar &&
- ((VarExp *)agg1)->var == ((VarExp *)agg2)->var))
- {
- return 0;
- }
- return 1;
- }
- if (isArray(e1) && isArray(e2))
- {
- uinteger_t len1 = resolveArrayLength(e1);
- uinteger_t len2 = resolveArrayLength(e2);
- // workaround for dmc optimizer bug calculating wrong len for
- // uinteger_t len = (len1 < len2 ? len1 : len2);
- // if (len == 0) ...
- if (len1 > 0 && len2 > 0)
- {
- uinteger_t len = (len1 < len2 ? len1 : len2);
- int res = ctfeCmpArrays(loc, e1, e2, len);
- if (res != 0)
- return res;
- }
- return (int)(len1 - len2);
- }
- if (e1->type->isintegral())
- {
- return e1->toInteger() != e2->toInteger();
- }
- real_t r1;
- real_t r2;
- if (e1->type->isreal())
- {
- r1 = e1->toReal();
- r2 = e2->toReal();
- goto L1;
- }
- else if (e1->type->isimaginary())
- {
- r1 = e1->toImaginary();
- r2 = e2->toImaginary();
- L1:
- if (CTFloat::isNaN(r1) || CTFloat::isNaN(r2)) // if unordered
- {
- return 1;
- }
- else
- {
- return (r1 != r2);
- }
- }
- else if (e1->type->iscomplex())
- {
- return e1->toComplex() != e2->toComplex();
- }
-
- if (e1->op == TOKstructliteral && e2->op == TOKstructliteral)
- {
- StructLiteralExp *es1 = (StructLiteralExp *)e1;
- StructLiteralExp *es2 = (StructLiteralExp *)e2;
- // For structs, we only need to return 0 or 1 (< and > aren't legal).
-
- if (es1->sd != es2->sd)
- return 1;
- else if ((!es1->elements || !es1->elements->length) &&
- (!es2->elements || !es2->elements->length))
- return 0; // both arrays are empty
- else if (!es1->elements || !es2->elements)
- return 1;
- else if (es1->elements->length != es2->elements->length)
- return 1;
- else
- {
- for (size_t i = 0; i < es1->elements->length; i++)
- {
- Expression *ee1 = (*es1->elements)[i];
- Expression *ee2 = (*es2->elements)[i];
-
- if (ee1 == ee2)
- continue;
- if (!ee1 || !ee2)
- return 1;
- int cmp = ctfeRawCmp(loc, ee1, ee2);
- if (cmp)
- return 1;
- }
- return 0; // All elements are equal
- }
- }
- if (e1->op == TOKassocarrayliteral && e2->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *es1 = (AssocArrayLiteralExp *)e1;
- AssocArrayLiteralExp *es2 = (AssocArrayLiteralExp *)e2;
-
- size_t dim = es1->keys->length;
- if (es2->keys->length != dim)
- return 1;
-
- bool *used = (bool *)mem.xmalloc(sizeof(bool) * dim);
- memset(used, 0, sizeof(bool) * dim);
-
- for (size_t i = 0; i < dim; ++i)
- {
- Expression *k1 = (*es1->keys)[i];
- Expression *v1 = (*es1->values)[i];
- Expression *v2 = NULL;
- for (size_t j = 0; j < dim; ++j)
- {
- if (used[j])
- continue;
- Expression *k2 = (*es2->keys)[j];
-
- if (ctfeRawCmp(loc, k1, k2))
- continue;
- used[j] = true;
- v2 = (*es2->values)[j];
- break;
- }
- if (!v2 || ctfeRawCmp(loc, v1, v2))
- {
- mem.xfree(used);
- return 1;
- }
- }
- mem.xfree(used);
- return 0;
- }
- error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1->toChars(), e2->toChars());
- assert(0);
- return 0;
-}
-
-/// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1
-int ctfeEqual(Loc loc, TOK op, Expression *e1, Expression *e2)
-{
- int cmp = !ctfeRawCmp(loc, e1, e2);
- if (op == TOKnotequal)
- cmp ^= 1;
- return cmp;
-}
-
-/// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1
-int ctfeIdentity(Loc loc, TOK op, Expression *e1, Expression *e2)
-{
- //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", Token::toChars(op),
- // Token::toChars(e1->op), e1->toChars(), Token::toChars(e2->op), e1->toChars());
- int cmp;
- if (e1->op == TOKnull)
- {
- cmp = (e2->op == TOKnull);
- }
- else if (e2->op == TOKnull)
- {
- cmp = 0;
- }
- else if (e1->op == TOKsymoff && e2->op == TOKsymoff)
- {
- SymOffExp *es1 = (SymOffExp *)e1;
- SymOffExp *es2 = (SymOffExp *)e2;
- cmp = (es1->var == es2->var && es1->offset == es2->offset);
- }
- else if (e1->type->isreal())
- cmp = RealEquals(e1->toReal(), e2->toReal());
- else if (e1->type->isimaginary())
- cmp = RealEquals(e1->toImaginary(), e2->toImaginary());
- else if (e1->type->iscomplex())
- {
- complex_t v1 = e1->toComplex();
- complex_t v2 = e2->toComplex();
- cmp = RealEquals(creall(v1), creall(v2)) &&
- RealEquals(cimagl(v1), cimagl(v1));
- }
- else
- cmp = !ctfeRawCmp(loc, e1, e2);
-
- if (op == TOKnotidentity || op == TOKnotequal)
- cmp ^= 1;
- return cmp;
-}
-
-/// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
-int ctfeCmp(Loc loc, TOK op, Expression *e1, Expression *e2)
-{
- Type *t1 = e1->type->toBasetype();
- Type *t2 = e2->type->toBasetype();
-
- if (t1->isString() && t2->isString())
- return specificCmp(op, ctfeRawCmp(loc, e1, e2));
- else if (t1->isreal())
- return realCmp(op, e1->toReal(), e2->toReal());
- else if (t1->isimaginary())
- return realCmp(op, e1->toImaginary(), e2->toImaginary());
- else if (t1->isunsigned() || t2->isunsigned())
- return intUnsignedCmp(op, e1->toInteger(), e2->toInteger());
- else
- return intSignedCmp(op, e1->toInteger(), e2->toInteger());
-}
-
-UnionExp ctfeCat(Loc loc, Type *type, Expression *e1, Expression *e2)
-{
- Type *t1 = e1->type->toBasetype();
- Type *t2 = e2->type->toBasetype();
- UnionExp ue;
- if (e2->op == TOKstring && e1->op == TOKarrayliteral &&
- t1->nextOf()->isintegral())
- {
- // [chars] ~ string => string (only valid for CTFE)
- StringExp *es1 = (StringExp *)e2;
- ArrayLiteralExp *es2 = (ArrayLiteralExp *)e1;
- size_t len = es1->len + es2->elements->length;
- unsigned char sz = es1->sz;
-
- void *s = mem.xmalloc((len + 1) * sz);
- memcpy((char *)s + sz * es2->elements->length, es1->string, es1->len * sz);
- for (size_t i = 0; i < es2->elements->length; i++)
- {
- Expression *es2e = (*es2->elements)[i];
- if (es2e->op != TOKint64)
- {
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
- dinteger_t v = es2e->toInteger();
- Port::valcpy((utf8_t *)s + i * sz, v, sz);
- }
-
- // Add terminating 0
- memset((utf8_t *)s + len * sz, 0, sz);
-
- new(&ue) StringExp(loc, s, len);
- StringExp *es = (StringExp *)ue.exp();
- es->sz = sz;
- es->committed = 0;
- es->type = type;
- return ue;
- }
- if (e1->op == TOKstring && e2->op == TOKarrayliteral &&
- t2->nextOf()->isintegral())
- {
- // string ~ [chars] => string (only valid for CTFE)
- // Concatenate the strings
- StringExp *es1 = (StringExp *)e1;
- ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;
- size_t len = es1->len + es2->elements->length;
- unsigned char sz = es1->sz;
-
- void *s = mem.xmalloc((len + 1) * sz);
- memcpy(s, es1->string, es1->len * sz);
- for (size_t i = 0; i < es2->elements->length; i++)
- {
- Expression *es2e = (*es2->elements)[i];
- if (es2e->op != TOKint64)
- {
- new(&ue) CTFEExp(TOKcantexp);
- return ue;
- }
- dinteger_t v = es2e->toInteger();
- Port::valcpy((utf8_t *)s + (es1->len + i) * sz, v, sz);
- }
-
- // Add terminating 0
- memset((utf8_t *)s + len * sz, 0, sz);
-
- new(&ue) StringExp(loc, s, len);
- StringExp *es = (StringExp *)ue.exp();
- es->sz = sz;
- es->committed = 0; //es1->committed;
- es->type = type;
- return ue;
- }
- if (e1->op == TOKarrayliteral && e2->op == TOKarrayliteral &&
- t1->nextOf()->equals(t2->nextOf()))
- {
- // [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
- ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1;
- ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2;
-
- new(&ue) ArrayLiteralExp(es1->loc, type, copyLiteralArray(es1->elements));
- es1 = (ArrayLiteralExp *)ue.exp();
- es1->elements->insert(es1->elements->length, copyLiteralArray(es2->elements));
- return ue;
- }
- if (e1->op == TOKarrayliteral && e2->op == TOKnull &&
- t1->nextOf()->equals(t2->nextOf()))
- {
- // [ e1 ] ~ null ----> [ e1 ].dup
- ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy());
- return ue;
- }
- if (e1->op == TOKnull && e2->op == TOKarrayliteral &&
- t1->nextOf()->equals(t2->nextOf()))
- {
- // null ~ [ e2 ] ----> [ e2 ].dup
- ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy());
- return ue;
- }
- ue = Cat(type, e1, e2);
- return ue;
-}
-
-/* Given an AA literal 'ae', and a key 'e2':
- * Return ae[e2] if present, or NULL if not found.
- */
-Expression *findKeyInAA(Loc loc, AssocArrayLiteralExp *ae, Expression *e2)
-{
- /* Search the keys backwards, in case there are duplicate keys
- */
- for (size_t i = ae->keys->length; i;)
- {
- i--;
- Expression *ekey = (*ae->keys)[i];
- int eq = ctfeEqual(loc, TOKequal, ekey, e2);
- if (eq)
- {
- return (*ae->values)[i];
- }
- }
- return NULL;
-}
-
-/* Same as for constfold.Index, except that it only works for static arrays,
- * dynamic arrays, and strings. We know that e1 is an
- * interpreted CTFE expression, so it cannot have side-effects.
- */
-Expression *ctfeIndex(Loc loc, Type *type, Expression *e1, uinteger_t indx)
-{
- //printf("ctfeIndex(e1 = %s)\n", e1->toChars());
- assert(e1->type);
- if (e1->op == TOKstring)
- {
- StringExp *es1 = (StringExp *)e1;
- if (indx >= es1->len)
- {
- error(loc, "string index %llu is out of bounds [0 .. %llu]", (ulonglong)indx, (ulonglong)es1->len);
- return CTFEExp::cantexp;
- }
- return new IntegerExp(loc, es1->charAt(indx), type);
- }
- assert(e1->op == TOKarrayliteral);
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e1;
- if (indx >= ale->elements->length)
- {
- error(loc, "array index %llu is out of bounds %s[0 .. %llu]", (ulonglong)indx, e1->toChars(), (ulonglong)ale->elements->length);
- return CTFEExp::cantexp;
- }
- Expression *e = (*ale->elements)[(size_t)indx];
- return paintTypeOntoLiteral(type, e);
- }
-}
-
-Expression *ctfeCast(UnionExp *pue, Loc loc, Type *type, Type *to, Expression *e)
-{
- if (e->op == TOKnull)
- return paintTypeOntoLiteral(pue, to, e);
-
- if (e->op == TOKclassreference)
- {
- // Disallow reinterpreting class casts. Do this by ensuring that
- // the original class can implicitly convert to the target class
- ClassDeclaration *originalClass = ((ClassReferenceExp *)e)->originalClass();
- if (originalClass->type->implicitConvTo(to->mutableOf()))
- return paintTypeOntoLiteral(pue, to, e);
- else
- {
- new(pue) NullExp(loc, to);
- return pue->exp();
- }
- }
-
- // Allow TypeInfo type painting
- if (isTypeInfo_Class(e->type) && e->type->implicitConvTo(to))
- return paintTypeOntoLiteral(pue, to, e);
-
- // Allow casting away const for struct literals
- if (e->op == TOKstructliteral &&
- e->type->toBasetype()->castMod(0) == to->toBasetype()->castMod(0))
- return paintTypeOntoLiteral(pue, to, e);
-
- Expression *r;
- if (e->type->equals(type) && type->equals(to))
- {
- // necessary not to change e's address for pointer comparisons
- r = e;
- }
- else if (to->toBasetype()->ty == Tarray &&
- type->toBasetype()->ty == Tarray &&
- to->toBasetype()->nextOf()->size() == type->toBasetype()->nextOf()->size())
- {
- // Bugzilla 12495: Array reinterpret casts: eg. string to immutable(ubyte)[]
- return paintTypeOntoLiteral(pue, to, e);
- }
- else
- {
- *pue = Cast(loc, type, to, e);
- r = pue->exp();
- }
-
- if (CTFEExp::isCantExp(r))
- error(loc, "cannot cast %s to %s at compile time", e->toChars(), to->toChars());
-
- if (e->op == TOKarrayliteral)
- ((ArrayLiteralExp *)e)->ownedByCtfe = OWNEDctfe;
-
- if (e->op == TOKstring)
- ((StringExp *)e)->ownedByCtfe = OWNEDctfe;
-
- return r;
-}
-
-/******** Assignment helper functions ***************************/
-
-/* Set dest = src, where both dest and src are container value literals
- * (ie, struct literals, or static arrays (can be an array literal or a string))
- * Assignment is recursively in-place.
- * Purpose: any reference to a member of 'dest' will remain valid after the
- * assignment.
- */
-void assignInPlace(Expression *dest, Expression *src)
-{
- assert(dest->op == TOKstructliteral ||
- dest->op == TOKarrayliteral ||
- dest->op == TOKstring);
- Expressions *oldelems;
- Expressions *newelems;
- if (dest->op == TOKstructliteral)
- {
- assert(dest->op == src->op);
- oldelems = ((StructLiteralExp *)dest)->elements;
- newelems = ((StructLiteralExp *)src)->elements;
- if (((StructLiteralExp *)dest)->sd->isNested() && oldelems->length == newelems->length - 1)
- oldelems->push(NULL);
- }
- else if (dest->op == TOKarrayliteral && src->op==TOKarrayliteral)
- {
- oldelems = ((ArrayLiteralExp *)dest)->elements;
- newelems = ((ArrayLiteralExp *)src)->elements;
- }
- else if (dest->op == TOKstring && src->op == TOKstring)
- {
- sliceAssignStringFromString((StringExp *)dest, (StringExp *)src, 0);
- return;
- }
- else if (dest->op == TOKarrayliteral && src->op == TOKstring)
- {
- sliceAssignArrayLiteralFromString((ArrayLiteralExp *)dest, (StringExp *)src, 0);
- return;
- }
- else if (src->op == TOKarrayliteral && dest->op == TOKstring)
- {
- sliceAssignStringFromArrayLiteral((StringExp *)dest, (ArrayLiteralExp *)src, 0);
- return;
- }
- else
- assert(0);
-
- assert(oldelems->length == newelems->length);
-
- for (size_t i= 0; i < oldelems->length; ++i)
- {
- Expression *e = (*newelems)[i];
- Expression *o = (*oldelems)[i];
- if (e->op == TOKstructliteral)
- {
- assert(o->op == e->op);
- assignInPlace(o, e);
- }
- else if (e->type->ty == Tsarray && e->op != TOKvoid &&
- o->type->ty == Tsarray)
- {
- assignInPlace(o, e);
- }
- else
- {
- (*oldelems)[i] = (*newelems)[i];
- }
- }
-}
-
-// Duplicate the elements array, then set field 'indexToChange' = newelem.
-Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expression *newelem)
-{
- Expressions *expsx = new Expressions();
- ++CtfeStatus::numArrayAllocs;
- expsx->setDim(oldelems->length);
- for (size_t j = 0; j < expsx->length; j++)
- {
- if (j == indexToChange)
- (*expsx)[j] = newelem;
- else
- (*expsx)[j] = (*oldelems)[j];
- }
- return expsx;
-}
-
-// Given an AA literal aae, set aae[index] = newval and return newval.
-Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae,
- Expression *index, Expression *newval)
-{
- /* Create new associative array literal reflecting updated key/value
- */
- Expressions *keysx = aae->keys;
- Expressions *valuesx = aae->values;
- int updated = 0;
- for (size_t j = valuesx->length; j; )
- {
- j--;
- Expression *ekey = (*aae->keys)[j];
- int eq = ctfeEqual(loc, TOKequal, ekey, index);
- if (eq)
- {
- (*valuesx)[j] = newval;
- updated = 1;
- }
- }
- if (!updated)
- {
- // Append index/newval to keysx[]/valuesx[]
- valuesx->push(newval);
- keysx->push(index);
- }
- return newval;
-}
-
-/// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
-/// oldlen, change its length to newlen. If the newlen is longer than oldlen,
-/// all new elements will be set to the default initializer for the element type.
-UnionExp changeArrayLiteralLength(Loc loc, TypeArray *arrayType,
- Expression *oldval, size_t oldlen, size_t newlen)
-{
- UnionExp ue;
- Type *elemType = arrayType->next;
- assert(elemType);
- Expression *defaultElem = elemType->defaultInitLiteral(loc);
- Expressions *elements = new Expressions();
- elements->setDim(newlen);
-
- // Resolve slices
- size_t indxlo = 0;
- if (oldval->op == TOKslice)
- {
- indxlo = (size_t)((SliceExp *)oldval)->lwr->toInteger();
- oldval = ((SliceExp *)oldval)->e1;
- }
- size_t copylen = oldlen < newlen ? oldlen : newlen;
- if (oldval->op == TOKstring)
- {
- StringExp *oldse = (StringExp *)oldval;
- void *s = mem.xcalloc(newlen + 1, oldse->sz);
- memcpy(s, oldse->string, copylen * oldse->sz);
- unsigned defaultValue = (unsigned)(defaultElem->toInteger());
- for (size_t elemi = copylen; elemi < newlen; ++elemi)
- {
- switch (oldse->sz)
- {
- case 1: (( utf8_t *)s)[(size_t)(indxlo + elemi)] = ( utf8_t)defaultValue; break;
- case 2: ((utf16_t *)s)[(size_t)(indxlo + elemi)] = (utf16_t)defaultValue; break;
- case 4: ((utf32_t *)s)[(size_t)(indxlo + elemi)] = (utf32_t)defaultValue; break;
- default: assert(0);
- }
- }
- new(&ue) StringExp(loc, s, newlen);
- StringExp *se = (StringExp *)ue.exp();
- se->type = arrayType;
- se->sz = oldse->sz;
- se->committed = oldse->committed;
- se->ownedByCtfe = OWNEDctfe;
- }
- else
- {
- if (oldlen != 0)
- {
- assert(oldval->op == TOKarrayliteral);
- ArrayLiteralExp *ae = (ArrayLiteralExp *)oldval;
- for (size_t i = 0; i < copylen; i++)
- (*elements)[i] = (*ae->elements)[indxlo + i];
- }
- if (elemType->ty == Tstruct || elemType->ty == Tsarray)
- {
- /* If it is an aggregate literal representing a value type,
- * we need to create a unique copy for each element
- */
- for (size_t i = copylen; i < newlen; i++)
- (*elements)[i] = copyLiteral(defaultElem).copy();
- }
- else
- {
- for (size_t i = copylen; i < newlen; i++)
- (*elements)[i] = defaultElem;
- }
- new(&ue) ArrayLiteralExp(loc, arrayType, elements);
- ArrayLiteralExp *aae = (ArrayLiteralExp *)ue.exp();
- aae->ownedByCtfe = OWNEDctfe;
- }
- return ue;
-}
-
-/*************************** CTFE Sanity Checks ***************************/
-
-bool isCtfeValueValid(Expression *newval)
-{
- Type *tb = newval->type->toBasetype();
-
- if (newval->op == TOKint64 ||
- newval->op == TOKfloat64 ||
- newval->op == TOKchar ||
- newval->op == TOKcomplex80)
- {
- return tb->isscalar();
- }
- if (newval->op == TOKnull)
- {
- return tb->ty == Tnull ||
- tb->ty == Tpointer ||
- tb->ty == Tarray ||
- tb->ty == Taarray ||
- tb->ty == Tclass ||
- tb->ty == Tdelegate;
- }
-
- if (newval->op == TOKstring)
- return true; // CTFE would directly use the StringExp in AST.
- if (newval->op == TOKarrayliteral)
- return true; //((ArrayLiteralExp *)newval)->ownedByCtfe;
- if (newval->op == TOKassocarrayliteral)
- return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe;
- if (newval->op == TOKstructliteral)
- return true; //((StructLiteralExp *)newval)->ownedByCtfe;
- if (newval->op == TOKclassreference)
- return true;
-
- if (newval->op == TOKvector)
- return true; // vector literal
-
- if (newval->op == TOKfunction)
- return true; // function literal or delegate literal
- if (newval->op == TOKdelegate)
- {
- // &struct.func or &clasinst.func
- // &nestedfunc
- Expression *ethis = ((DelegateExp *)newval)->e1;
- return (ethis->op == TOKstructliteral ||
- ethis->op == TOKclassreference ||
- (ethis->op == TOKvar && ((VarExp *)ethis)->var == ((DelegateExp *)newval)->func));
- }
- if (newval->op == TOKsymoff)
- {
- // function pointer, or pointer to static variable
- Declaration *d = ((SymOffExp *)newval)->var;
- return d->isFuncDeclaration() || d->isDataseg();
- }
- if (newval->op == TOKtypeid)
- {
- // always valid
- return true;
- }
- if (newval->op == TOKaddress)
- {
- // e1 should be a CTFE reference
- Expression *e1 = ((AddrExp *)newval)->e1;
- return tb->ty == Tpointer &&
- (((e1->op == TOKstructliteral || e1->op == TOKarrayliteral) && isCtfeValueValid(e1)) ||
- (e1->op == TOKvar) ||
- (e1->op == TOKdotvar && isCtfeReferenceValid(e1)) ||
- (e1->op == TOKindex && isCtfeReferenceValid(e1)) ||
- (e1->op == TOKslice && e1->type->toBasetype()->ty == Tsarray));
- }
- if (newval->op == TOKslice)
- {
- // e1 should be an array aggregate
- SliceExp *se = (SliceExp *)newval;
- assert(se->lwr && se->lwr->op == TOKint64);
- assert(se->upr && se->upr->op == TOKint64);
- return (tb->ty == Tarray ||
- tb->ty == Tsarray) &&
- (se->e1->op == TOKstring ||
- se->e1->op == TOKarrayliteral);
- }
-
- if (newval->op == TOKvoid)
- return true; // uninitialized value
-
- newval->error("CTFE internal error: illegal CTFE value %s", newval->toChars());
- return false;
-}
-
-bool isCtfeReferenceValid(Expression *newval)
-{
- if (newval->op == TOKthis)
- return true;
- if (newval->op == TOKvar)
- {
- VarDeclaration *v = ((VarExp *)newval)->var->isVarDeclaration();
- assert(v);
- // Must not be a reference to a reference
- return true;
- }
- if (newval->op == TOKindex)
- {
- Expression *eagg = ((IndexExp *)newval)->e1;
- return eagg->op == TOKstring ||
- eagg->op == TOKarrayliteral ||
- eagg->op == TOKassocarrayliteral;
- }
- if (newval->op == TOKdotvar)
- {
- Expression *eagg = ((DotVarExp *)newval)->e1;
- return (eagg->op == TOKstructliteral || eagg->op == TOKclassreference) &&
- isCtfeValueValid(eagg);
- }
-
- // Internally a ref variable may directly point a stack memory.
- // e.g. ref int v = 1;
- return isCtfeValueValid(newval);
-}
-
-// Used for debugging only
-void showCtfeExpr(Expression *e, int level)
-{
- for (int i = level; i > 0; --i) printf(" ");
- Expressions *elements = NULL;
- // We need the struct definition to detect block assignment
- StructDeclaration *sd = NULL;
- ClassDeclaration *cd = NULL;
- if (e->op == TOKstructliteral)
- {
- elements = ((StructLiteralExp *)e)->elements;
- sd = ((StructLiteralExp *)e)->sd;
- printf("STRUCT type = %s %p:\n", e->type->toChars(),
- e);
- }
- else if (e->op == TOKclassreference)
- {
- elements = ((ClassReferenceExp *)e)->value->elements;
- cd = ((ClassReferenceExp *)e)->originalClass();
- printf("CLASS type = %s %p:\n", e->type->toChars(),
- ((ClassReferenceExp *)e)->value);
- }
- else if (e->op == TOKarrayliteral)
- {
- elements = ((ArrayLiteralExp *)e)->elements;
- printf("ARRAY LITERAL type=%s %p:\n", e->type->toChars(),
- e);
- }
- else if (e->op == TOKassocarrayliteral)
- {
- printf("AA LITERAL type=%s %p:\n", e->type->toChars(),
- e);
- }
- else if (e->op == TOKstring)
- {
- printf("STRING %s %p\n", e->toChars(),
- ((StringExp *)e)->string);
- }
- else if (e->op == TOKslice)
- {
- printf("SLICE %p: %s\n", e, e->toChars());
- showCtfeExpr(((SliceExp *)e)->e1, level + 1);
- }
- else if (e->op == TOKvar)
- {
- printf("VAR %p %s\n", e, e->toChars());
- VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration();
- if (v && getValue(v))
- showCtfeExpr(getValue(v), level + 1);
- }
- else if (e->op == TOKaddress)
- {
- // This is potentially recursive. We mustn't try to print the thing we're pointing to.
- printf("POINTER %p to %p: %s\n", e, ((AddrExp *)e)->e1, e->toChars());
- }
- else
- printf("VALUE %p: %s\n", e, e->toChars());
-
- if (elements)
- {
- size_t fieldsSoFar = 0;
- for (size_t i = 0; i < elements->length; i++)
- {
- Expression *z = NULL;
- VarDeclaration *v = NULL;
- if (i > 15)
- {
- printf("...(total %d elements)\n", (int)elements->length);
- return;
- }
- if (sd)
- {
- v = sd->fields[i];
- z = (*elements)[i];
- }
- else if (cd)
- {
- while (i - fieldsSoFar >= cd->fields.length)
- {
- fieldsSoFar += cd->fields.length;
- cd = cd->baseClass;
- for (int j = level; j > 0; --j) printf(" ");
- printf(" BASE CLASS: %s\n", cd->toChars());
- }
- v = cd->fields[i - fieldsSoFar];
- assert((elements->length + i) >= (fieldsSoFar + cd->fields.length));
- size_t indx = (elements->length - fieldsSoFar)- cd->fields.length + i;
- assert(indx < elements->length);
- z = (*elements)[indx];
- }
- if (!z)
- {
- for (int j = level; j > 0; --j) printf(" ");
- printf(" void\n");
- continue;
- }
-
- if (v)
- {
- // If it is a void assignment, use the default initializer
- if ((v->type->ty != z->type->ty) && v->type->ty == Tsarray)
- {
- for (int j = level; --j; ) printf(" ");
- printf(" field: block initalized static array\n");
- continue;
- }
- }
- showCtfeExpr(z, level + 1);
- }
- }
-}
-
-/*************************** Void initialization ***************************/
-
-UnionExp voidInitLiteral(Type *t, VarDeclaration *var)
-{
- UnionExp ue;
- if (t->ty == Tsarray)
- {
- TypeSArray *tsa = (TypeSArray *)t;
- Expression *elem = voidInitLiteral(tsa->next, var).copy();
-
- // For aggregate value types (structs, static arrays) we must
- // create an a separate copy for each element.
- bool mustCopy = (elem->op == TOKarrayliteral || elem->op == TOKstructliteral);
-
- Expressions *elements = new Expressions();
- size_t d = (size_t)tsa->dim->toInteger();
- elements->setDim(d);
- for (size_t i = 0; i < d; i++)
- {
- if (mustCopy && i > 0)
- elem = copyLiteral(elem).copy();
- (*elements)[i] = elem;
- }
- new(&ue) ArrayLiteralExp(var->loc, tsa, elements);
- ArrayLiteralExp *ae = (ArrayLiteralExp *)ue.exp();
- ae->ownedByCtfe = OWNEDctfe;
- }
- else if (t->ty == Tstruct)
- {
- TypeStruct *ts = (TypeStruct *)t;
- Expressions *exps = new Expressions();
- exps->setDim(ts->sym->fields.length);
- for (size_t i = 0; i < ts->sym->fields.length; i++)
- {
- (*exps)[i] = voidInitLiteral(ts->sym->fields[i]->type, ts->sym->fields[i]).copy();
- }
- new(&ue) StructLiteralExp(var->loc, ts->sym, exps);
- StructLiteralExp *se = (StructLiteralExp *)ue.exp();
- se->type = ts;
- se->ownedByCtfe = OWNEDctfe;
- }
- else
- new(&ue) VoidInitExp(var, t);
- return ue;
-}
diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d
new file mode 100644
index 00000000000..22633a869d4
--- /dev/null
+++ b/gcc/d/dmd/ctfeexpr.d
@@ -0,0 +1,2096 @@
+/**
+ * CTFE for expressions involving pointers, slices, array concatenation etc.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d, _ctfeexpr.d)
+ * Documentation: https://dlang.org/phobos/dmd_ctfeexpr.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctfeexpr.d
+ */
+
+module dmd.ctfeexpr;
+
+import core.stdc.stdio;
+import core.stdc.stdlib;
+import core.stdc.string;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.complex;
+import dmd.constfold;
+import dmd.compiler;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dinterpret;
+import dmd.dstruct;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.mtype;
+import dmd.root.ctfloat;
+import dmd.root.port;
+import dmd.root.rmem;
+import dmd.tokens;
+import dmd.visitor;
+
+
+/***********************************************************
+ * A reference to a class, or an interface. We need this when we
+ * point to a base class (we must record what the type is).
+ */
+extern (C++) final class ClassReferenceExp : Expression
+{
+ StructLiteralExp value;
+
+ extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type)
+ {
+ super(loc, TOK.classReference, __traits(classInstanceSize, ClassReferenceExp));
+ assert(lit && lit.sd && lit.sd.isClassDeclaration());
+ this.value = lit;
+ this.type = type;
+ }
+
+ ClassDeclaration originalClass()
+ {
+ return value.sd.isClassDeclaration();
+ }
+
+ // Return index of the field, or -1 if not found
+ private int getFieldIndex(Type fieldtype, uint fieldoffset)
+ {
+ ClassDeclaration cd = originalClass();
+ uint fieldsSoFar = 0;
+ for (size_t j = 0; j < value.elements.dim; j++)
+ {
+ while (j - fieldsSoFar >= cd.fields.dim)
+ {
+ fieldsSoFar += cd.fields.dim;
+ cd = cd.baseClass;
+ }
+ VarDeclaration v2 = cd.fields[j - fieldsSoFar];
+ if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
+ {
+ return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar));
+ }
+ }
+ return -1;
+ }
+
+ // Return index of the field, or -1 if not found
+ // Same as getFieldIndex, but checks for a direct match with the VarDeclaration
+ int findFieldIndexByName(VarDeclaration v)
+ {
+ ClassDeclaration cd = originalClass();
+ size_t fieldsSoFar = 0;
+ for (size_t j = 0; j < value.elements.dim; j++)
+ {
+ while (j - fieldsSoFar >= cd.fields.dim)
+ {
+ fieldsSoFar += cd.fields.dim;
+ cd = cd.baseClass;
+ }
+ VarDeclaration v2 = cd.fields[j - fieldsSoFar];
+ if (v == v2)
+ {
+ return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar));
+ }
+ }
+ return -1;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/*************************
+ * Same as getFieldIndex, but checks for a direct match with the VarDeclaration
+ * Returns:
+ * index of the field, or -1 if not found
+ */
+int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pure
+{
+ foreach (i, field; sd.fields)
+ {
+ if (field == v)
+ return cast(int)i;
+ }
+ return -1;
+}
+
+/***********************************************************
+ * Fake class which holds the thrown exception.
+ * Used for implementing exception handling.
+ */
+extern (C++) final class ThrownExceptionExp : Expression
+{
+ ClassReferenceExp thrown; // the thing being tossed
+
+ extern (D) this(const ref Loc loc, ClassReferenceExp victim)
+ {
+ super(loc, TOK.thrownException, __traits(classInstanceSize, ThrownExceptionExp));
+ this.thrown = victim;
+ this.type = victim.type;
+ }
+
+ override const(char)* toChars() const
+ {
+ return "CTFE ThrownException";
+ }
+
+ // Generate an error message when this exception is not caught
+ extern (D) void generateUncaughtError()
+ {
+ UnionExp ue = void;
+ Expression e = resolveSlice((*thrown.value.elements)[0], &ue);
+ StringExp se = e.toStringExp();
+ thrown.error("uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars());
+ /* Also give the line where the throw statement was. We won't have it
+ * in the case where the ThrowStatement is generated internally
+ * (eg, in ScopeStatement)
+ */
+ if (loc.isValid() && !loc.equals(thrown.loc))
+ .errorSupplemental(loc, "thrown from here");
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * This type is only used by the interpreter.
+ */
+extern (C++) final class CTFEExp : Expression
+{
+ extern (D) this(TOK tok)
+ {
+ super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp));
+ type = Type.tvoid;
+ }
+
+ override const(char)* toChars() const
+ {
+ switch (op)
+ {
+ case TOK.cantExpression:
+ return "<cant>";
+ case TOK.voidExpression:
+ return "cast(void)0";
+ case TOK.showCtfeContext:
+ return "<error>";
+ case TOK.break_:
+ return "<break>";
+ case TOK.continue_:
+ return "<continue>";
+ case TOK.goto_:
+ return "<goto>";
+ default:
+ assert(0);
+ }
+ }
+
+ extern (D) __gshared CTFEExp cantexp;
+ extern (D) __gshared CTFEExp voidexp;
+ extern (D) __gshared CTFEExp breakexp;
+ extern (D) __gshared CTFEExp continueexp;
+ extern (D) __gshared CTFEExp gotoexp;
+ /* Used when additional information is needed regarding
+ * a ctfe error.
+ */
+ extern (D) __gshared CTFEExp showcontext;
+
+ extern (D) static bool isCantExp(const Expression e)
+ {
+ return e && e.op == TOK.cantExpression;
+ }
+
+ extern (D) static bool isGotoExp(const Expression e)
+ {
+ return e && e.op == TOK.goto_;
+ }
+}
+
+// True if 'e' is CTFEExp::cantexp, or an exception
+bool exceptionOrCantInterpret(const Expression e)
+{
+ return e && (e.op == TOK.cantExpression || e.op == TOK.thrownException || e.op == TOK.showCtfeContext);
+}
+
+/************** Aggregate literals (AA/string/array/struct) ******************/
+// Given expr, which evaluates to an array/AA/string literal,
+// return true if it needs to be copied
+bool needToCopyLiteral(const Expression expr)
+{
+ Expression e = cast()expr;
+ for (;;)
+ {
+ switch (e.op)
+ {
+ case TOK.arrayLiteral:
+ return (cast(ArrayLiteralExp)e).ownedByCtfe == OwnedBy.code;
+ case TOK.assocArrayLiteral:
+ return (cast(AssocArrayLiteralExp)e).ownedByCtfe == OwnedBy.code;
+ case TOK.structLiteral:
+ return (cast(StructLiteralExp)e).ownedByCtfe == OwnedBy.code;
+ case TOK.string_:
+ case TOK.this_:
+ case TOK.variable:
+ return false;
+ case TOK.assign:
+ return false;
+ case TOK.index:
+ case TOK.dotVariable:
+ case TOK.slice:
+ case TOK.cast_:
+ e = (cast(UnaExp)e).e1;
+ continue;
+ case TOK.concatenate:
+ return needToCopyLiteral((cast(BinExp)e).e1) || needToCopyLiteral((cast(BinExp)e).e2);
+ case TOK.concatenateAssign:
+ case TOK.concatenateElemAssign:
+ case TOK.concatenateDcharAssign:
+ e = (cast(BinExp)e).e2;
+ continue;
+ default:
+ return false;
+ }
+ }
+}
+
+private Expressions* copyLiteralArray(Expressions* oldelems, Expression basis = null)
+{
+ if (!oldelems)
+ return oldelems;
+ incArrayAllocs();
+ auto newelems = new Expressions(oldelems.dim);
+ foreach (i, el; *oldelems)
+ {
+ (*newelems)[i] = copyLiteral(el ? el : basis).copy();
+ }
+ return newelems;
+}
+
+// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
+// This value will be used for in-place modification.
+UnionExp copyLiteral(Expression e)
+{
+ UnionExp ue = void;
+ if (auto se = e.isStringExp()) // syntaxCopy doesn't make a copy for StringExp!
+ {
+ char* s = cast(char*)mem.xcalloc(se.len + 1, se.sz);
+ const slice = se.peekData();
+ memcpy(s, slice.ptr, slice.length);
+ emplaceExp!(StringExp)(&ue, se.loc, s[0 .. se.len * se.sz], se.len, se.sz);
+ StringExp se2 = cast(StringExp)ue.exp();
+ se2.committed = se.committed;
+ se2.postfix = se.postfix;
+ se2.type = se.type;
+ se2.ownedByCtfe = OwnedBy.ctfe;
+ return ue;
+ }
+ if (auto ale = e.isArrayLiteralExp())
+ {
+ auto elements = copyLiteralArray(ale.elements, ale.basis);
+
+ emplaceExp!(ArrayLiteralExp)(&ue, e.loc, e.type, elements);
+
+ ArrayLiteralExp r = cast(ArrayLiteralExp)ue.exp();
+ r.ownedByCtfe = OwnedBy.ctfe;
+ return ue;
+ }
+ if (auto aae = e.isAssocArrayLiteralExp())
+ {
+ emplaceExp!(AssocArrayLiteralExp)(&ue, e.loc, copyLiteralArray(aae.keys), copyLiteralArray(aae.values));
+ AssocArrayLiteralExp r = cast(AssocArrayLiteralExp)ue.exp();
+ r.type = e.type;
+ r.ownedByCtfe = OwnedBy.ctfe;
+ return ue;
+ }
+ if (auto sle = e.isStructLiteralExp())
+ {
+ /* syntaxCopy doesn't work for struct literals, because of a nasty special
+ * case: block assignment is permitted inside struct literals, eg,
+ * an int[4] array can be initialized with a single int.
+ */
+ auto oldelems = sle.elements;
+ auto newelems = new Expressions(oldelems.dim);
+ foreach (i, ref el; *newelems)
+ {
+ // We need the struct definition to detect block assignment
+ auto v = sle.sd.fields[i];
+ auto m = (*oldelems)[i];
+
+ // If it is a void assignment, use the default initializer
+ if (!m)
+ m = voidInitLiteral(v.type, v).copy();
+
+ if (v.type.ty == Tarray || v.type.ty == Taarray)
+ {
+ // Don't have to copy array references
+ }
+ else
+ {
+ // Buzilla 15681: Copy the source element always.
+ m = copyLiteral(m).copy();
+
+ // Block assignment from inside struct literals
+ if (v.type.ty != m.type.ty && v.type.ty == Tsarray)
+ {
+ auto tsa = v.type.isTypeSArray();
+ auto len = cast(size_t)tsa.dim.toInteger();
+ UnionExp uex = void;
+ m = createBlockDuplicatedArrayLiteral(&uex, e.loc, v.type, m, len);
+ if (m == uex.exp())
+ m = uex.copy();
+ }
+ }
+ el = m;
+ }
+ emplaceExp!(StructLiteralExp)(&ue, e.loc, sle.sd, newelems, sle.stype);
+ auto r = ue.exp().isStructLiteralExp();
+ r.type = e.type;
+ r.ownedByCtfe = OwnedBy.ctfe;
+ r.origin = sle.origin;
+ return ue;
+ }
+ if (e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.symbolOffset || e.op == TOK.null_ || e.op == TOK.variable || e.op == TOK.dotVariable || e.op == TOK.int64 || e.op == TOK.float64 || e.op == TOK.char_ || e.op == TOK.complex80 || e.op == TOK.void_ || e.op == TOK.vector || e.op == TOK.typeid_)
+ {
+ // Simple value types
+ // Keep e1 for DelegateExp and DotVarExp
+ emplaceExp!(UnionExp)(&ue, e);
+ Expression r = ue.exp();
+ r.type = e.type;
+ return ue;
+ }
+ if (auto se = e.isSliceExp())
+ {
+ if (se.type.toBasetype().ty == Tsarray)
+ {
+ // same with resolveSlice()
+ if (se.e1.op == TOK.null_)
+ {
+ emplaceExp!(NullExp)(&ue, se.loc, se.type);
+ return ue;
+ }
+ ue = Slice(se.type, se.e1, se.lwr, se.upr);
+ auto r = ue.exp().isArrayLiteralExp();
+ r.elements = copyLiteralArray(r.elements);
+ r.ownedByCtfe = OwnedBy.ctfe;
+ return ue;
+ }
+ else
+ {
+ // Array slices only do a shallow copy
+ emplaceExp!(SliceExp)(&ue, e.loc, se.e1, se.lwr, se.upr);
+ Expression r = ue.exp();
+ r.type = e.type;
+ return ue;
+ }
+ }
+ if (isPointer(e.type))
+ {
+ // For pointers, we only do a shallow copy.
+ if (auto ae = e.isAddrExp())
+ emplaceExp!(AddrExp)(&ue, e.loc, ae.e1);
+ else if (auto ie = e.isIndexExp())
+ emplaceExp!(IndexExp)(&ue, e.loc, ie.e1, ie.e2);
+ else if (auto dve = e.isDotVarExp())
+ {
+ emplaceExp!(DotVarExp)(&ue, e.loc, dve.e1, dve.var, dve.hasOverloads);
+ }
+ else
+ assert(0);
+
+ Expression r = ue.exp();
+ r.type = e.type;
+ return ue;
+ }
+ if (auto cre = e.isClassReferenceExp())
+ {
+ emplaceExp!(ClassReferenceExp)(&ue, e.loc, cre.value, e.type);
+ return ue;
+ }
+ if (e.op == TOK.error)
+ {
+ emplaceExp!(UnionExp)(&ue, e);
+ return ue;
+ }
+ e.error("CTFE internal error: literal `%s`", e.toChars());
+ assert(0);
+}
+
+/* Deal with type painting.
+ * Type painting is a major nuisance: we can't just set
+ * e.type = type, because that would change the original literal.
+ * But, we can't simply copy the literal either, because that would change
+ * the values of any pointers.
+ */
+Expression paintTypeOntoLiteral(Type type, Expression lit)
+{
+ if (lit.type.equals(type))
+ return lit;
+ return paintTypeOntoLiteralCopy(type, lit).copy();
+}
+
+Expression paintTypeOntoLiteral(UnionExp* pue, Type type, Expression lit)
+{
+ if (lit.type.equals(type))
+ return lit;
+ *pue = paintTypeOntoLiteralCopy(type, lit);
+ return pue.exp();
+}
+
+private UnionExp paintTypeOntoLiteralCopy(Type type, Expression lit)
+{
+ UnionExp ue;
+ if (lit.type.equals(type))
+ {
+ emplaceExp!(UnionExp)(&ue, lit);
+ return ue;
+ }
+ // If it is a cast to inout, retain the original type of the referenced part.
+ if (type.hasWild())
+ {
+ emplaceExp!(UnionExp)(&ue, lit);
+ ue.exp().type = type;
+ return ue;
+ }
+ if (auto se = lit.isSliceExp())
+ {
+ emplaceExp!(SliceExp)(&ue, lit.loc, se.e1, se.lwr, se.upr);
+ }
+ else if (auto ie = lit.isIndexExp())
+ {
+ emplaceExp!(IndexExp)(&ue, lit.loc, ie.e1, ie.e2);
+ }
+ else if (lit.op == TOK.arrayLiteral)
+ {
+ emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy());
+ }
+ else if (lit.op == TOK.string_)
+ {
+ // For strings, we need to introduce another level of indirection
+ emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy());
+ }
+ else if (auto aae = lit.isAssocArrayLiteralExp())
+ {
+ // TODO: we should be creating a reference to this AAExp, not
+ // just a ref to the keys and values.
+ OwnedBy wasOwned = aae.ownedByCtfe;
+ emplaceExp!(AssocArrayLiteralExp)(&ue, lit.loc, aae.keys, aae.values);
+ aae = cast(AssocArrayLiteralExp)ue.exp();
+ aae.ownedByCtfe = wasOwned;
+ }
+ else
+ {
+ // Can't type paint from struct to struct*; this needs another
+ // level of indirection
+ if (lit.op == TOK.structLiteral && isPointer(type))
+ lit.error("CTFE internal error: painting `%s`", type.toChars());
+ ue = copyLiteral(lit);
+ }
+ ue.exp().type = type;
+ return ue;
+}
+
+/*************************************
+ * If e is a SliceExp, constant fold it.
+ * Params:
+ * e = expression to resolve
+ * pue = if not null, store resulting expression here
+ * Returns:
+ * resulting expression
+ */
+Expression resolveSlice(Expression e, UnionExp* pue = null)
+{
+ SliceExp se = e.isSliceExp();
+ if (!se)
+ return e;
+ if (se.e1.op == TOK.null_)
+ return se.e1;
+ if (pue)
+ {
+ *pue = Slice(e.type, se.e1, se.lwr, se.upr);
+ return pue.exp();
+ }
+ else
+ return Slice(e.type, se.e1, se.lwr, se.upr).copy();
+}
+
+/* Determine the array length, without interpreting it.
+ * e must be an array literal, or a slice
+ * It's very wasteful to resolve the slice when we only
+ * need the length.
+ */
+uinteger_t resolveArrayLength(const Expression e)
+{
+ switch (e.op)
+ {
+ case TOK.vector:
+ return e.isVectorExp().dim;
+
+ case TOK.null_:
+ return 0;
+
+ case TOK.slice:
+ {
+ auto se = cast(SliceExp)e;
+ const ilo = se.lwr.toInteger();
+ const iup = se.upr.toInteger();
+ return iup - ilo;
+ }
+
+ case TOK.string_:
+ return e.isStringExp().len;
+
+ case TOK.arrayLiteral:
+ {
+ const ale = e.isArrayLiteralExp();
+ return ale.elements ? ale.elements.dim : 0;
+ }
+
+ case TOK.assocArrayLiteral:
+ {
+ return e.isAssocArrayLiteralExp().keys.dim;
+ }
+
+ default:
+ assert(0);
+ }
+}
+
+/******************************
+ * Helper for NewExp
+ * Create an array literal consisting of 'elem' duplicated 'dim' times.
+ * Params:
+ * pue = where to store result
+ * loc = source location where the interpretation occurs
+ * type = target type of the result
+ * elem = the source of array element, it will be owned by the result
+ * dim = element number of the result
+ * Returns:
+ * Constructed ArrayLiteralExp
+ */
+ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc loc, Type type, Expression elem, size_t dim)
+{
+ if (type.ty == Tsarray && type.nextOf().ty == Tsarray && elem.type.ty != Tsarray)
+ {
+ // If it is a multidimensional array literal, do it recursively
+ auto tsa = type.nextOf().isTypeSArray();
+ const len = cast(size_t)tsa.dim.toInteger();
+ UnionExp ue = void;
+ elem = createBlockDuplicatedArrayLiteral(&ue, loc, type.nextOf(), elem, len);
+ if (elem == ue.exp())
+ elem = ue.copy();
+ }
+
+ // Buzilla 15681
+ const tb = elem.type.toBasetype();
+ const mustCopy = tb.ty == Tstruct || tb.ty == Tsarray;
+
+ auto elements = new Expressions(dim);
+ foreach (i, ref el; *elements)
+ {
+ el = mustCopy && i ? copyLiteral(elem).copy() : elem;
+ }
+ emplaceExp!(ArrayLiteralExp)(pue, loc, type, elements);
+ auto ale = pue.exp().isArrayLiteralExp();
+ ale.ownedByCtfe = OwnedBy.ctfe;
+ return ale;
+}
+
+/******************************
+ * Helper for NewExp
+ * Create a string literal consisting of 'value' duplicated 'dim' times.
+ */
+StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, const ref Loc loc, Type type, dchar value, size_t dim, ubyte sz)
+{
+ auto s = cast(char*)mem.xcalloc(dim, sz);
+ foreach (elemi; 0 .. dim)
+ {
+ switch (sz)
+ {
+ case 1:
+ s[elemi] = cast(char)value;
+ break;
+ case 2:
+ (cast(wchar*)s)[elemi] = cast(wchar)value;
+ break;
+ case 4:
+ (cast(dchar*)s)[elemi] = value;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ emplaceExp!(StringExp)(pue, loc, s[0 .. dim * sz], dim, sz);
+ auto se = pue.exp().isStringExp();
+ se.type = type;
+ se.committed = true;
+ se.ownedByCtfe = OwnedBy.ctfe;
+ return se;
+}
+
+// Return true if t is an AA
+bool isAssocArray(Type t)
+{
+ return t.toBasetype().isTypeAArray() !is null;
+}
+
+// Given a template AA type, extract the corresponding built-in AA type
+TypeAArray toBuiltinAAType(Type t)
+{
+ return t.toBasetype().isTypeAArray();
+}
+
+/************** TypeInfo operations ************************************/
+// Return true if type is TypeInfo_Class
+bool isTypeInfo_Class(const Type type)
+{
+ auto tc = cast()type.isTypeClass();
+ return tc && (Type.dtypeinfo == tc.sym || Type.dtypeinfo.isBaseOf(tc.sym, null));
+}
+
+/************** Pointer operations ************************************/
+// Return true if t is a pointer (not a function pointer)
+bool isPointer(Type t)
+{
+ Type tb = t.toBasetype();
+ return tb.ty == Tpointer && tb.nextOf().ty != Tfunction;
+}
+
+// For CTFE only. Returns true if 'e' is true or a non-null pointer.
+bool isTrueBool(Expression e)
+{
+ return e.isBool(true) || ((e.type.ty == Tpointer || e.type.ty == Tclass) && e.op != TOK.null_);
+}
+
+/* Is it safe to convert from srcPointee* to destPointee* ?
+ * srcPointee is the genuine type (never void).
+ * destPointee may be void.
+ */
+bool isSafePointerCast(Type srcPointee, Type destPointee)
+{
+ // It's safe to cast S** to D** if it's OK to cast S* to D*
+ while (srcPointee.ty == Tpointer && destPointee.ty == Tpointer)
+ {
+ srcPointee = srcPointee.nextOf();
+ destPointee = destPointee.nextOf();
+ }
+ // It's OK if both are the same (modulo const)
+ if (srcPointee.constConv(destPointee))
+ return true;
+ // It's OK if function pointers differ only in safe/pure/nothrow
+ if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction)
+ return srcPointee.covariant(destPointee) == Covariant.yes ||
+ destPointee.covariant(srcPointee) == Covariant.yes;
+ // it's OK to cast to void*
+ if (destPointee.ty == Tvoid)
+ return true;
+ // It's OK to cast from V[K] to void*
+ if (srcPointee.ty == Taarray && destPointee == Type.tvoidptr)
+ return true;
+ // It's OK if they are the same size (static array of) integers, eg:
+ // int* --> uint*
+ // int[5][] --> uint[5][]
+ if (srcPointee.ty == Tsarray && destPointee.ty == Tsarray)
+ {
+ if (srcPointee.size() != destPointee.size())
+ return false;
+ srcPointee = srcPointee.baseElemOf();
+ destPointee = destPointee.baseElemOf();
+ }
+ return srcPointee.isintegral() && destPointee.isintegral() && srcPointee.size() == destPointee.size();
+}
+
+Expression getAggregateFromPointer(Expression e, dinteger_t* ofs)
+{
+ *ofs = 0;
+ if (auto ae = e.isAddrExp())
+ e = ae.e1;
+ if (auto soe = e.isSymOffExp())
+ *ofs = soe.offset;
+ if (auto dve = e.isDotVarExp())
+ {
+ const ex = dve.e1;
+ const v = dve.var.isVarDeclaration();
+ assert(v);
+ StructLiteralExp se = (ex.op == TOK.classReference)
+ ? (cast(ClassReferenceExp)ex).value
+ : cast(StructLiteralExp)ex;
+
+ // We can't use getField, because it makes a copy
+ const i = (ex.op == TOK.classReference)
+ ? (cast(ClassReferenceExp)ex).getFieldIndex(e.type, v.offset)
+ : se.getFieldIndex(e.type, v.offset);
+ e = (*se.elements)[i];
+ }
+ if (auto ie = e.isIndexExp())
+ {
+ // Note that each AA element is part of its own memory block
+ if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == TOK.string_ || ie.e1.op == TOK.arrayLiteral) && ie.e2.op == TOK.int64)
+ {
+ *ofs = ie.e2.toInteger();
+ return ie.e1;
+ }
+ }
+ if (auto se = e.isSliceExp())
+ {
+ if (se && e.type.toBasetype().ty == Tsarray &&
+ (se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral) && se.lwr.op == TOK.int64)
+ {
+ *ofs = se.lwr.toInteger();
+ return se.e1;
+ }
+ }
+
+ // It can be a `null` disguised as a cast, e.g. `cast(void*)0`.
+ if (auto ie = e.isIntegerExp())
+ if (ie.type.ty == Tpointer && ie.getInteger() == 0)
+ return new NullExp(ie.loc, e.type.nextOf());
+ // Those casts are invalid, but let the rest of the code handle it,
+ // as it could be something like `x !is null`, which doesn't need
+ // to dereference the pointer, even if the pointer is `cast(void*)420`.
+
+ return e;
+}
+
+/** Return true if agg1 and agg2 are pointers to the same memory block
+ */
+bool pointToSameMemoryBlock(Expression agg1, Expression agg2)
+{
+ if (agg1 == agg2)
+ return true;
+ // For integers cast to pointers, we regard them as non-comparable
+ // unless they are identical. (This may be overly strict).
+ if (agg1.op == TOK.int64 && agg2.op == TOK.int64 && agg1.toInteger() == agg2.toInteger())
+ {
+ return true;
+ }
+ // Note that type painting can occur with VarExp, so we
+ // must compare the variables being pointed to.
+ if (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)
+ {
+ return true;
+ }
+ if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var)
+ {
+ return true;
+ }
+ return false;
+}
+
+// return e1 - e2 as an integer, or error if not possible
+UnionExp pointerDifference(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ UnionExp ue = void;
+ dinteger_t ofs1, ofs2;
+ Expression agg1 = getAggregateFromPointer(e1, &ofs1);
+ Expression agg2 = getAggregateFromPointer(e2, &ofs2);
+ if (agg1 == agg2)
+ {
+ Type pointee = (cast(TypePointer)agg1.type).next;
+ const sz = pointee.size();
+ emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type);
+ }
+ else if (agg1.op == TOK.string_ && agg2.op == TOK.string_ &&
+ (cast(StringExp)agg1).peekString().ptr == (cast(StringExp)agg2).peekString().ptr)
+ {
+ Type pointee = (cast(TypePointer)agg1.type).next;
+ const sz = pointee.size();
+ emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type);
+ }
+ else if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset &&
+ (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var)
+ {
+ emplaceExp!(IntegerExp)(&ue, loc, ofs1 - ofs2, type);
+ }
+ else
+ {
+ error(loc, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1.toChars(), e2.toChars());
+ emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+ }
+ return ue;
+}
+
+// Return eptr op e2, where eptr is a pointer, e2 is an integer,
+// and op is TOK.add or TOK.min
+UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr, Expression e2)
+{
+ UnionExp ue;
+ if (eptr.type.nextOf().ty == Tvoid)
+ {
+ error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
+ Lcant:
+ emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+ return ue;
+ }
+ if (eptr.op == TOK.address)
+ eptr = (cast(AddrExp)eptr).e1;
+ dinteger_t ofs1;
+ Expression agg1 = getAggregateFromPointer(eptr, &ofs1);
+ if (agg1.op == TOK.symbolOffset)
+ {
+ if ((cast(SymOffExp)agg1).var.type.ty != Tsarray)
+ {
+ error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
+ goto Lcant;
+ }
+ }
+ else if (agg1.op != TOK.string_ && agg1.op != TOK.arrayLiteral)
+ {
+ error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
+ goto Lcant;
+ }
+ dinteger_t ofs2 = e2.toInteger();
+ Type pointee = (cast(TypeNext)agg1.type.toBasetype()).next;
+ dinteger_t sz = pointee.size();
+ sinteger_t indx;
+ dinteger_t len;
+ if (agg1.op == TOK.symbolOffset)
+ {
+ indx = ofs1 / sz;
+ len = (cast(TypeSArray)(cast(SymOffExp)agg1).var.type).dim.toInteger();
+ }
+ else
+ {
+ Expression dollar = ArrayLength(Type.tsize_t, agg1).copy();
+ assert(!CTFEExp.isCantExp(dollar));
+ indx = ofs1;
+ len = dollar.toInteger();
+ }
+ if (op == TOK.add || op == TOK.addAssign || op == TOK.plusPlus)
+ indx += ofs2 / sz;
+ else if (op == TOK.min || op == TOK.minAssign || op == TOK.minusMinus)
+ indx -= ofs2 / sz;
+ else
+ {
+ error(loc, "CTFE internal error: bad pointer operation");
+ goto Lcant;
+ }
+ if (indx < 0 || len < indx)
+ {
+ error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len);
+ goto Lcant;
+ }
+ if (agg1.op == TOK.symbolOffset)
+ {
+ emplaceExp!(SymOffExp)(&ue, loc, (cast(SymOffExp)agg1).var, indx * sz);
+ SymOffExp se = cast(SymOffExp)ue.exp();
+ se.type = type;
+ return ue;
+ }
+ if (agg1.op != TOK.arrayLiteral && agg1.op != TOK.string_)
+ {
+ error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars());
+ goto Lcant;
+ }
+ if (eptr.type.toBasetype().ty == Tsarray)
+ {
+ dinteger_t dim = (cast(TypeSArray)eptr.type.toBasetype()).dim.toInteger();
+ // Create a CTFE pointer &agg1[indx .. indx+dim]
+ auto se = ctfeEmplaceExp!SliceExp(loc, agg1,
+ ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t),
+ ctfeEmplaceExp!IntegerExp(loc, indx + dim, Type.tsize_t));
+ se.type = type.toBasetype().nextOf();
+ emplaceExp!(AddrExp)(&ue, loc, se);
+ ue.exp().type = type;
+ return ue;
+ }
+ // Create a CTFE pointer &agg1[indx]
+ auto ofs = ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t);
+ Expression ie = ctfeEmplaceExp!IndexExp(loc, agg1, ofs);
+ ie.type = type.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992
+ emplaceExp!(AddrExp)(&ue, loc, ie);
+ ue.exp().type = type;
+ return ue;
+}
+
+// Return 1 if true, 0 if false
+// -1 if comparison is illegal because they point to non-comparable memory blocks
+int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2)
+{
+ if (pointToSameMemoryBlock(agg1, agg2))
+ {
+ int n;
+ switch (op)
+ {
+ case TOK.lessThan:
+ n = (ofs1 < ofs2);
+ break;
+ case TOK.lessOrEqual:
+ n = (ofs1 <= ofs2);
+ break;
+ case TOK.greaterThan:
+ n = (ofs1 > ofs2);
+ break;
+ case TOK.greaterOrEqual:
+ n = (ofs1 >= ofs2);
+ break;
+ case TOK.identity:
+ case TOK.equal:
+ n = (ofs1 == ofs2);
+ break;
+ case TOK.notIdentity:
+ case TOK.notEqual:
+ n = (ofs1 != ofs2);
+ break;
+ default:
+ assert(0);
+ }
+ return n;
+ }
+ const null1 = (agg1.op == TOK.null_);
+ const null2 = (agg2.op == TOK.null_);
+ int cmp;
+ if (null1 || null2)
+ {
+ switch (op)
+ {
+ case TOK.lessThan:
+ cmp = null1 && !null2;
+ break;
+ case TOK.greaterThan:
+ cmp = !null1 && null2;
+ break;
+ case TOK.lessOrEqual:
+ cmp = null1;
+ break;
+ case TOK.greaterOrEqual:
+ cmp = null2;
+ break;
+ case TOK.identity:
+ case TOK.equal:
+ case TOK.notIdentity: // 'cmp' gets inverted below
+ case TOK.notEqual:
+ cmp = (null1 == null2);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ else
+ {
+ switch (op)
+ {
+ case TOK.identity:
+ case TOK.equal:
+ case TOK.notIdentity: // 'cmp' gets inverted below
+ case TOK.notEqual:
+ cmp = 0;
+ break;
+ default:
+ return -1; // memory blocks are different
+ }
+ }
+ if (op == TOK.notIdentity || op == TOK.notEqual)
+ cmp ^= 1;
+ return cmp;
+}
+
+// True if conversion from type 'from' to 'to' involves a reinterpret_cast
+// floating point -> integer or integer -> floating point
+bool isFloatIntPaint(Type to, Type from)
+{
+ return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral());
+}
+
+// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
+Expression paintFloatInt(UnionExp* pue, Expression fromVal, Type to)
+{
+ if (exceptionOrCantInterpret(fromVal))
+ return fromVal;
+ assert(to.size() == 4 || to.size() == 8);
+ return Compiler.paintAsType(pue, fromVal, to);
+}
+
+/******** Constant folding, with support for CTFE ***************************/
+/// Return true if non-pointer expression e can be compared
+/// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
+bool isCtfeComparable(Expression e)
+{
+ if (e.op == TOK.slice)
+ e = (cast(SliceExp)e).e1;
+ if (e.isConst() != 1)
+ {
+ if (e.op == TOK.null_ || e.op == TOK.string_ || e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.arrayLiteral || e.op == TOK.structLiteral || e.op == TOK.assocArrayLiteral || e.op == TOK.classReference)
+ {
+ return true;
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=14123
+ // TypeInfo object is comparable in CTFE
+ if (e.op == TOK.typeid_)
+ return true;
+ return false;
+ }
+ return true;
+}
+
+/// Map TOK comparison ops
+private bool numCmp(N)(TOK op, N n1, N n2)
+{
+ switch (op)
+ {
+ case TOK.lessThan:
+ return n1 < n2;
+ case TOK.lessOrEqual:
+ return n1 <= n2;
+ case TOK.greaterThan:
+ return n1 > n2;
+ case TOK.greaterOrEqual:
+ return n1 >= n2;
+
+ default:
+ assert(0);
+ }
+}
+
+/// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
+bool specificCmp(TOK op, int rawCmp)
+{
+ return numCmp!int(op, rawCmp, 0);
+}
+
+/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
+bool intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2)
+{
+ return numCmp!dinteger_t(op, n1, n2);
+}
+
+/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
+bool intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2)
+{
+ return numCmp!sinteger_t(op, n1, n2);
+}
+
+/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
+bool realCmp(TOK op, real_t r1, real_t r2)
+{
+ // Don't rely on compiler, handle NAN arguments separately
+ if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
+ {
+ switch (op)
+ {
+ case TOK.lessThan:
+ case TOK.lessOrEqual:
+ case TOK.greaterThan:
+ case TOK.greaterOrEqual:
+ return false;
+
+ default:
+ assert(0);
+ }
+ }
+ else
+ {
+ return numCmp!real_t(op, r1, r2);
+ }
+}
+
+/* Conceptually the same as memcmp(e1, e2).
+ * e1 and e2 may be strings, arrayliterals, or slices.
+ * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
+ * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
+ * Returns:
+ * -1,0,1
+ */
+private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len)
+{
+ // Resolve slices, if necessary
+ uinteger_t lo1 = 0;
+ uinteger_t lo2 = 0;
+
+ Expression x1 = e1;
+ if (auto sle1 = x1.isSliceExp())
+ {
+ lo1 = sle1.lwr.toInteger();
+ x1 = sle1.e1;
+ }
+ auto se1 = x1.isStringExp();
+ auto ae1 = x1.isArrayLiteralExp();
+
+ Expression x2 = e2;
+ if (auto sle2 = x2.isSliceExp())
+ {
+ lo2 = sle2.lwr.toInteger();
+ x2 = sle2.e1;
+ }
+ auto se2 = x2.isStringExp();
+ auto ae2 = x2.isArrayLiteralExp();
+
+ // Now both must be either TOK.arrayLiteral or TOK.string_
+ if (se1 && se2)
+ return sliceCmpStringWithString(se1, se2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
+ if (se1 && ae2)
+ return sliceCmpStringWithArray(se1, ae2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
+ if (se2 && ae1)
+ return -sliceCmpStringWithArray(se2, ae1, cast(size_t)lo2, cast(size_t)lo1, cast(size_t)len);
+ assert(ae1 && ae2);
+ // Comparing two array literals. This case is potentially recursive.
+ // If they aren't strings, we just need an equality check rather than
+ // a full cmp.
+ const bool needCmp = ae1.type.nextOf().isintegral();
+ foreach (size_t i; 0 .. cast(size_t)len)
+ {
+ Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)];
+ Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)];
+ if (needCmp)
+ {
+ const sinteger_t c = ee1.toInteger() - ee2.toInteger();
+ if (c > 0)
+ return 1;
+ if (c < 0)
+ return -1;
+ }
+ else
+ {
+ if (ctfeRawCmp(loc, ee1, ee2))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Given a delegate expression e, return .funcptr.
+ * If e is NullExp, return NULL.
+ */
+private FuncDeclaration funcptrOf(Expression e)
+{
+ assert(e.type.ty == Tdelegate);
+ if (auto de = e.isDelegateExp())
+ return de.func;
+ if (auto fe = e.isFuncExp())
+ return fe.fd;
+ assert(e.op == TOK.null_);
+ return null;
+}
+
+private bool isArray(const Expression e)
+{
+ return e.op == TOK.arrayLiteral || e.op == TOK.string_ || e.op == TOK.slice || e.op == TOK.null_;
+}
+
+/*****
+ * Params:
+ * loc = source file location
+ * e1 = left operand
+ * e2 = right operand
+ * identity = true for `is` identity comparisons
+ * Returns:
+ * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
+ * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
+ */
+private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false)
+{
+ if (e1.op == TOK.classReference || e2.op == TOK.classReference)
+ {
+ if (e1.op == TOK.classReference && e2.op == TOK.classReference &&
+ (cast(ClassReferenceExp)e1).value == (cast(ClassReferenceExp)e2).value)
+ return 0;
+ return 1;
+ }
+ if (e1.op == TOK.typeid_ && e2.op == TOK.typeid_)
+ {
+ // printf("e1: %s\n", e1.toChars());
+ // printf("e2: %s\n", e2.toChars());
+ Type t1 = isType((cast(TypeidExp)e1).obj);
+ Type t2 = isType((cast(TypeidExp)e2).obj);
+ assert(t1);
+ assert(t2);
+ return t1 != t2;
+ }
+ // null == null, regardless of type
+ if (e1.op == TOK.null_ && e2.op == TOK.null_)
+ return 0;
+ if (e1.type.ty == Tpointer && e2.type.ty == Tpointer)
+ {
+ // Can only be an equality test.
+ dinteger_t ofs1, ofs2;
+ Expression agg1 = getAggregateFromPointer(e1, &ofs1);
+ Expression agg2 = getAggregateFromPointer(e2, &ofs2);
+ if ((agg1 == agg2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
+ {
+ if (ofs1 == ofs2)
+ return 0;
+ }
+ return 1;
+ }
+ if (e1.type.ty == Tdelegate && e2.type.ty == Tdelegate)
+ {
+ // If .funcptr isn't the same, they are not equal
+ if (funcptrOf(e1) != funcptrOf(e2))
+ return 1;
+ // If both are delegate literals, assume they have the
+ // same closure pointer. TODO: We don't support closures yet!
+ if (e1.op == TOK.function_ && e2.op == TOK.function_)
+ return 0;
+ assert(e1.op == TOK.delegate_ && e2.op == TOK.delegate_);
+ // Same .funcptr. Do they have the same .ptr?
+ Expression ptr1 = (cast(DelegateExp)e1).e1;
+ Expression ptr2 = (cast(DelegateExp)e2).e1;
+ dinteger_t ofs1, ofs2;
+ Expression agg1 = getAggregateFromPointer(ptr1, &ofs1);
+ Expression agg2 = getAggregateFromPointer(ptr2, &ofs2);
+ // If they are TOK.variable, it means they are FuncDeclarations
+ if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
+ {
+ return 0;
+ }
+ return 1;
+ }
+ if (isArray(e1) && isArray(e2))
+ {
+ const uinteger_t len1 = resolveArrayLength(e1);
+ const uinteger_t len2 = resolveArrayLength(e2);
+ // workaround for dmc optimizer bug calculating wrong len for
+ // uinteger_t len = (len1 < len2 ? len1 : len2);
+ // if (len == 0) ...
+ if (len1 > 0 && len2 > 0)
+ {
+ const uinteger_t len = (len1 < len2 ? len1 : len2);
+ const int res = ctfeCmpArrays(loc, e1, e2, len);
+ if (res != 0)
+ return res;
+ }
+ return cast(int)(len1 - len2);
+ }
+ if (e1.type.isintegral())
+ {
+ return e1.toInteger() != e2.toInteger();
+ }
+ if (e1.type.isreal() || e1.type.isimaginary())
+ {
+ real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary();
+ real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary();
+ if (identity)
+ return !RealIdentical(r1, r2);
+ if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
+ {
+ return 1; // they are not equal
+ }
+ else
+ {
+ return (r1 != r2);
+ }
+ }
+ else if (e1.type.iscomplex())
+ {
+ auto c1 = e1.toComplex();
+ auto c2 = e2.toComplex();
+ if (identity)
+ {
+ return !RealIdentical(c1.re, c2.re) && !RealIdentical(c1.im, c2.im);
+ }
+ return c1 != c2;
+ }
+ if (e1.op == TOK.structLiteral && e2.op == TOK.structLiteral)
+ {
+ StructLiteralExp es1 = cast(StructLiteralExp)e1;
+ StructLiteralExp es2 = cast(StructLiteralExp)e2;
+ // For structs, we only need to return 0 or 1 (< and > aren't legal).
+ if (es1.sd != es2.sd)
+ return 1;
+ else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim))
+ return 0; // both arrays are empty
+ else if (!es1.elements || !es2.elements)
+ return 1;
+ else if (es1.elements.dim != es2.elements.dim)
+ return 1;
+ else
+ {
+ foreach (size_t i; 0 .. es1.elements.dim)
+ {
+ Expression ee1 = (*es1.elements)[i];
+ Expression ee2 = (*es2.elements)[i];
+
+ // https://issues.dlang.org/show_bug.cgi?id=16284
+ if (ee1.op == TOK.void_ && ee2.op == TOK.void_) // if both are VoidInitExp
+ continue;
+
+ if (ee1 == ee2)
+ continue;
+ if (!ee1 || !ee2)
+ return 1;
+ const int cmp = ctfeRawCmp(loc, ee1, ee2, identity);
+ if (cmp)
+ return 1;
+ }
+ return 0; // All elements are equal
+ }
+ }
+ if (e1.op == TOK.assocArrayLiteral && e2.op == TOK.assocArrayLiteral)
+ {
+ AssocArrayLiteralExp es1 = cast(AssocArrayLiteralExp)e1;
+ AssocArrayLiteralExp es2 = cast(AssocArrayLiteralExp)e2;
+ size_t dim = es1.keys.dim;
+ if (es2.keys.dim != dim)
+ return 1;
+ bool* used = cast(bool*)mem.xmalloc(bool.sizeof * dim);
+ memset(used, 0, bool.sizeof * dim);
+ foreach (size_t i; 0 .. dim)
+ {
+ Expression k1 = (*es1.keys)[i];
+ Expression v1 = (*es1.values)[i];
+ Expression v2 = null;
+ foreach (size_t j; 0 .. dim)
+ {
+ if (used[j])
+ continue;
+ Expression k2 = (*es2.keys)[j];
+ if (ctfeRawCmp(loc, k1, k2, identity))
+ continue;
+ used[j] = true;
+ v2 = (*es2.values)[j];
+ break;
+ }
+ if (!v2 || ctfeRawCmp(loc, v1, v2, identity))
+ {
+ mem.xfree(used);
+ return 1;
+ }
+ }
+ mem.xfree(used);
+ return 0;
+ }
+ error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1.toChars(), e2.toChars());
+ assert(0);
+}
+
+/// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1
+bool ctfeEqual(const ref Loc loc, TOK op, Expression e1, Expression e2)
+{
+ return !ctfeRawCmp(loc, e1, e2) ^ (op == TOK.notEqual);
+}
+
+/// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1
+bool ctfeIdentity(const ref Loc loc, TOK op, Expression e1, Expression e2)
+{
+ //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars());
+ //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", Token::toChars(op),
+ // Token::toChars(e1.op), e1.toChars(), Token::toChars(e2.op), e1.toChars());
+ bool cmp;
+ if (e1.op == TOK.null_)
+ {
+ cmp = (e2.op == TOK.null_);
+ }
+ else if (e2.op == TOK.null_)
+ {
+ cmp = false;
+ }
+ else if (e1.op == TOK.symbolOffset && e2.op == TOK.symbolOffset)
+ {
+ SymOffExp es1 = cast(SymOffExp)e1;
+ SymOffExp es2 = cast(SymOffExp)e2;
+ cmp = (es1.var == es2.var && es1.offset == es2.offset);
+ }
+ else if (e1.type.isreal())
+ cmp = RealIdentical(e1.toReal(), e2.toReal());
+ else if (e1.type.isimaginary())
+ cmp = RealIdentical(e1.toImaginary(), e2.toImaginary());
+ else if (e1.type.iscomplex())
+ {
+ complex_t v1 = e1.toComplex();
+ complex_t v2 = e2.toComplex();
+ cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1));
+ }
+ else
+ {
+ cmp = !ctfeRawCmp(loc, e1, e2, true);
+ }
+ if (op == TOK.notIdentity || op == TOK.notEqual)
+ cmp ^= true;
+ return cmp;
+}
+
+/// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
+bool ctfeCmp(const ref Loc loc, TOK op, Expression e1, Expression e2)
+{
+ Type t1 = e1.type.toBasetype();
+ Type t2 = e2.type.toBasetype();
+
+ if (t1.isString() && t2.isString())
+ return specificCmp(op, ctfeRawCmp(loc, e1, e2));
+ else if (t1.isreal())
+ return realCmp(op, e1.toReal(), e2.toReal());
+ else if (t1.isimaginary())
+ return realCmp(op, e1.toImaginary(), e2.toImaginary());
+ else if (t1.isunsigned() || t2.isunsigned())
+ return intUnsignedCmp(op, e1.toInteger(), e2.toInteger());
+ else
+ return intSignedCmp(op, e1.toInteger(), e2.toInteger());
+}
+
+UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
+{
+ Type t1 = e1.type.toBasetype();
+ Type t2 = e2.type.toBasetype();
+ UnionExp ue;
+ if (e2.op == TOK.string_ && e1.op == TOK.arrayLiteral && t1.nextOf().isintegral())
+ {
+ // [chars] ~ string => string (only valid for CTFE)
+ StringExp es1 = cast(StringExp)e2;
+ ArrayLiteralExp es2 = cast(ArrayLiteralExp)e1;
+ const len = es1.len + es2.elements.dim;
+ const sz = es1.sz;
+ void* s = mem.xmalloc((len + 1) * sz);
+ const data1 = es1.peekData();
+ memcpy(cast(char*)s + sz * es2.elements.dim, data1.ptr, data1.length);
+ foreach (size_t i; 0 .. es2.elements.dim)
+ {
+ Expression es2e = (*es2.elements)[i];
+ if (es2e.op != TOK.int64)
+ {
+ emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+ return ue;
+ }
+ dinteger_t v = es2e.toInteger();
+ Port.valcpy(cast(char*)s + i * sz, v, sz);
+ }
+ // Add terminating 0
+ memset(cast(char*)s + len * sz, 0, sz);
+ emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
+ StringExp es = cast(StringExp)ue.exp();
+ es.committed = 0;
+ es.type = type;
+ return ue;
+ }
+ if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral && t2.nextOf().isintegral())
+ {
+ // string ~ [chars] => string (only valid for CTFE)
+ // Concatenate the strings
+ StringExp es1 = cast(StringExp)e1;
+ ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
+ const len = es1.len + es2.elements.dim;
+ const sz = es1.sz;
+ void* s = mem.xmalloc((len + 1) * sz);
+ auto slice = es1.peekData();
+ memcpy(s, slice.ptr, slice.length);
+ foreach (size_t i; 0 .. es2.elements.dim)
+ {
+ Expression es2e = (*es2.elements)[i];
+ if (es2e.op != TOK.int64)
+ {
+ emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+ return ue;
+ }
+ const v = es2e.toInteger();
+ Port.valcpy(cast(char*)s + (es1.len + i) * sz, v, sz);
+ }
+ // Add terminating 0
+ memset(cast(char*)s + len * sz, 0, sz);
+ emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz);
+ StringExp es = cast(StringExp)ue.exp();
+ es.sz = sz;
+ es.committed = 0; //es1.committed;
+ es.type = type;
+ return ue;
+ }
+ if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
+ {
+ // [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
+ ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
+ ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
+ emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements));
+ es1 = cast(ArrayLiteralExp)ue.exp();
+ es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements));
+ return ue;
+ }
+ if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf()))
+ {
+ // [ e1 ] ~ null ----> [ e1 ].dup
+ ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy());
+ return ue;
+ }
+ if (e1.op == TOK.null_ && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
+ {
+ // null ~ [ e2 ] ----> [ e2 ].dup
+ ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy());
+ return ue;
+ }
+ ue = Cat(loc, type, e1, e2);
+ return ue;
+}
+
+/* Given an AA literal 'ae', and a key 'e2':
+ * Return ae[e2] if present, or NULL if not found.
+ */
+Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2)
+{
+ /* Search the keys backwards, in case there are duplicate keys
+ */
+ for (size_t i = ae.keys.dim; i;)
+ {
+ --i;
+ Expression ekey = (*ae.keys)[i];
+ const int eq = ctfeEqual(loc, TOK.equal, ekey, e2);
+ if (eq)
+ {
+ return (*ae.values)[i];
+ }
+ }
+ return null;
+}
+
+/* Same as for constfold.Index, except that it only works for static arrays,
+ * dynamic arrays, and strings. We know that e1 is an
+ * interpreted CTFE expression, so it cannot have side-effects.
+ */
+Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx)
+{
+ //printf("ctfeIndex(e1 = %s)\n", e1.toChars());
+ assert(e1.type);
+ if (auto es1 = e1.isStringExp())
+ {
+ if (indx >= es1.len)
+ {
+ error(loc, "string index %llu is out of bounds `[0 .. %zu]`", indx, es1.len);
+ return CTFEExp.cantexp;
+ }
+ emplaceExp!IntegerExp(pue, loc, es1.charAt(indx), type);
+ return pue.exp();
+ }
+
+ if (auto ale = e1.isArrayLiteralExp())
+ {
+ if (indx >= ale.elements.dim)
+ {
+ error(loc, "array index %llu is out of bounds `%s[0 .. %zu]`", indx, e1.toChars(), ale.elements.dim);
+ return CTFEExp.cantexp;
+ }
+ Expression e = (*ale.elements)[cast(size_t)indx];
+ return paintTypeOntoLiteral(pue, type, e);
+ }
+
+ assert(0);
+}
+
+Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e)
+{
+ Expression paint()
+ {
+ return paintTypeOntoLiteral(pue, to, e);
+ }
+
+ if (e.op == TOK.null_)
+ return paint();
+
+ if (e.op == TOK.classReference)
+ {
+ // Disallow reinterpreting class casts. Do this by ensuring that
+ // the original class can implicitly convert to the target class
+ ClassDeclaration originalClass = (cast(ClassReferenceExp)e).originalClass();
+ if (originalClass.type.implicitConvTo(to.mutableOf()))
+ return paint();
+ else
+ {
+ emplaceExp!(NullExp)(pue, loc, to);
+ return pue.exp();
+ }
+ }
+
+ // Allow TypeInfo type painting
+ if (isTypeInfo_Class(e.type) && e.type.implicitConvTo(to))
+ return paint();
+
+ // Allow casting away const for struct literals
+ if (e.op == TOK.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0))
+ return paint();
+
+ Expression r;
+ if (e.type.equals(type) && type.equals(to))
+ {
+ // necessary not to change e's address for pointer comparisons
+ r = e;
+ }
+ else if (to.toBasetype().ty == Tarray &&
+ type.toBasetype().ty == Tarray &&
+ to.toBasetype().nextOf().size() == type.toBasetype().nextOf().size())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=12495
+ // Array reinterpret casts: eg. string to immutable(ubyte)[]
+ return paint();
+ }
+ else
+ {
+ *pue = Cast(loc, type, to, e);
+ r = pue.exp();
+ }
+
+ if (CTFEExp.isCantExp(r))
+ error(loc, "cannot cast `%s` to `%s` at compile time", e.toChars(), to.toChars());
+
+ if (auto ae = e.isArrayLiteralExp())
+ ae.ownedByCtfe = OwnedBy.ctfe;
+
+ if (auto se = e.isStringExp())
+ se.ownedByCtfe = OwnedBy.ctfe;
+
+ return r;
+}
+
+/******** Assignment helper functions ***************************/
+/* Set dest = src, where both dest and src are container value literals
+ * (ie, struct literals, or static arrays (can be an array literal or a string))
+ * Assignment is recursively in-place.
+ * Purpose: any reference to a member of 'dest' will remain valid after the
+ * assignment.
+ */
+void assignInPlace(Expression dest, Expression src)
+{
+ if (!(dest.op == TOK.structLiteral || dest.op == TOK.arrayLiteral || dest.op == TOK.string_))
+ {
+ printf("invalid op %d %d\n", src.op, dest.op);
+ assert(0);
+ }
+ Expressions* oldelems;
+ Expressions* newelems;
+ if (dest.op == TOK.structLiteral)
+ {
+ assert(dest.op == src.op);
+ oldelems = (cast(StructLiteralExp)dest).elements;
+ newelems = (cast(StructLiteralExp)src).elements;
+ auto sd = (cast(StructLiteralExp)dest).sd;
+ const nfields = sd.nonHiddenFields();
+ const nvthis = sd.fields.dim - nfields;
+ if (nvthis && oldelems.dim >= nfields && oldelems.dim < newelems.dim)
+ foreach (_; 0 .. newelems.dim - oldelems.dim)
+ oldelems.push(null);
+ }
+ else if (dest.op == TOK.arrayLiteral && src.op == TOK.arrayLiteral)
+ {
+ oldelems = (cast(ArrayLiteralExp)dest).elements;
+ newelems = (cast(ArrayLiteralExp)src).elements;
+ }
+ else if (dest.op == TOK.string_ && src.op == TOK.string_)
+ {
+ sliceAssignStringFromString(cast(StringExp)dest, cast(StringExp)src, 0);
+ return;
+ }
+ else if (dest.op == TOK.arrayLiteral && src.op == TOK.string_)
+ {
+ sliceAssignArrayLiteralFromString(cast(ArrayLiteralExp)dest, cast(StringExp)src, 0);
+ return;
+ }
+ else if (src.op == TOK.arrayLiteral && dest.op == TOK.string_)
+ {
+ sliceAssignStringFromArrayLiteral(cast(StringExp)dest, cast(ArrayLiteralExp)src, 0);
+ return;
+ }
+ else
+ {
+ printf("invalid op %d %d\n", src.op, dest.op);
+ assert(0);
+ }
+ assert(oldelems.dim == newelems.dim);
+ foreach (size_t i; 0 .. oldelems.dim)
+ {
+ Expression e = (*newelems)[i];
+ Expression o = (*oldelems)[i];
+ if (e.op == TOK.structLiteral)
+ {
+ assert(o.op == e.op);
+ assignInPlace(o, e);
+ }
+ else if (e.type.ty == Tsarray && e.op != TOK.void_ && o.type.ty == Tsarray)
+ {
+ assignInPlace(o, e);
+ }
+ else
+ {
+ (*oldelems)[i] = (*newelems)[i];
+ }
+ }
+}
+
+// Given an AA literal aae, set aae[index] = newval and return newval.
+Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval)
+{
+ /* Create new associative array literal reflecting updated key/value
+ */
+ Expressions* keysx = aae.keys;
+ Expressions* valuesx = aae.values;
+ int updated = 0;
+ for (size_t j = valuesx.dim; j;)
+ {
+ j--;
+ Expression ekey = (*aae.keys)[j];
+ int eq = ctfeEqual(loc, TOK.equal, ekey, index);
+ if (eq)
+ {
+ (*valuesx)[j] = newval;
+ updated = 1;
+ }
+ }
+ if (!updated)
+ {
+ // Append index/newval to keysx[]/valuesx[]
+ valuesx.push(newval);
+ keysx.push(index);
+ }
+ return newval;
+}
+
+/// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
+/// oldlen, change its length to newlen. If the newlen is longer than oldlen,
+/// all new elements will be set to the default initializer for the element type.
+UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen)
+{
+ UnionExp ue;
+ Type elemType = arrayType.next;
+ assert(elemType);
+ Expression defaultElem = elemType.defaultInitLiteral(loc);
+ auto elements = new Expressions(newlen);
+ // Resolve slices
+ size_t indxlo = 0;
+ if (oldval.op == TOK.slice)
+ {
+ indxlo = cast(size_t)(cast(SliceExp)oldval).lwr.toInteger();
+ oldval = (cast(SliceExp)oldval).e1;
+ }
+ size_t copylen = oldlen < newlen ? oldlen : newlen;
+ if (oldval.op == TOK.string_)
+ {
+ StringExp oldse = cast(StringExp)oldval;
+ void* s = mem.xcalloc(newlen + 1, oldse.sz);
+ const data = oldse.peekData();
+ memcpy(s, data.ptr, copylen * oldse.sz);
+ const defaultValue = cast(uint)defaultElem.toInteger();
+ foreach (size_t elemi; copylen .. newlen)
+ {
+ switch (oldse.sz)
+ {
+ case 1:
+ (cast(char*)s)[cast(size_t)(indxlo + elemi)] = cast(char)defaultValue;
+ break;
+ case 2:
+ (cast(wchar*)s)[cast(size_t)(indxlo + elemi)] = cast(wchar)defaultValue;
+ break;
+ case 4:
+ (cast(dchar*)s)[cast(size_t)(indxlo + elemi)] = cast(dchar)defaultValue;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ emplaceExp!(StringExp)(&ue, loc, s[0 .. newlen * oldse.sz], newlen, oldse.sz);
+ StringExp se = cast(StringExp)ue.exp();
+ se.type = arrayType;
+ se.sz = oldse.sz;
+ se.committed = oldse.committed;
+ se.ownedByCtfe = OwnedBy.ctfe;
+ }
+ else
+ {
+ if (oldlen != 0)
+ {
+ assert(oldval.op == TOK.arrayLiteral);
+ ArrayLiteralExp ae = cast(ArrayLiteralExp)oldval;
+ foreach (size_t i; 0 .. copylen)
+ (*elements)[i] = (*ae.elements)[indxlo + i];
+ }
+ if (elemType.ty == Tstruct || elemType.ty == Tsarray)
+ {
+ /* If it is an aggregate literal representing a value type,
+ * we need to create a unique copy for each element
+ */
+ foreach (size_t i; copylen .. newlen)
+ (*elements)[i] = copyLiteral(defaultElem).copy();
+ }
+ else
+ {
+ foreach (size_t i; copylen .. newlen)
+ (*elements)[i] = defaultElem;
+ }
+ emplaceExp!(ArrayLiteralExp)(&ue, loc, arrayType, elements);
+ ArrayLiteralExp aae = cast(ArrayLiteralExp)ue.exp();
+ aae.ownedByCtfe = OwnedBy.ctfe;
+ }
+ return ue;
+}
+
+/*************************** CTFE Sanity Checks ***************************/
+
+bool isCtfeValueValid(Expression newval)
+{
+ Type tb = newval.type.toBasetype();
+ switch (newval.op)
+ {
+ case TOK.int64:
+ case TOK.float64:
+ case TOK.char_:
+ case TOK.complex80:
+ return tb.isscalar();
+
+ case TOK.null_:
+ return tb.ty == Tnull ||
+ tb.ty == Tpointer ||
+ tb.ty == Tarray ||
+ tb.ty == Taarray ||
+ tb.ty == Tclass ||
+ tb.ty == Tdelegate;
+
+ case TOK.string_:
+ return true; // CTFE would directly use the StringExp in AST.
+
+ case TOK.arrayLiteral:
+ return true; //((ArrayLiteralExp *)newval)->ownedByCtfe;
+
+ case TOK.assocArrayLiteral:
+ return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe;
+
+ case TOK.structLiteral:
+ return true; //((StructLiteralExp *)newval)->ownedByCtfe;
+
+ case TOK.classReference:
+ return true;
+
+ case TOK.type:
+ return true;
+
+ case TOK.vector:
+ return true; // vector literal
+
+ case TOK.function_:
+ return true; // function literal or delegate literal
+
+ case TOK.delegate_:
+ {
+ // &struct.func or &clasinst.func
+ // &nestedfunc
+ Expression ethis = (cast(DelegateExp)newval).e1;
+ return (ethis.op == TOK.structLiteral || ethis.op == TOK.classReference || ethis.op == TOK.variable && (cast(VarExp)ethis).var == (cast(DelegateExp)newval).func);
+ }
+
+ case TOK.symbolOffset:
+ {
+ // function pointer, or pointer to static variable
+ Declaration d = (cast(SymOffExp)newval).var;
+ return d.isFuncDeclaration() || d.isDataseg();
+ }
+
+ case TOK.typeid_:
+ {
+ // always valid
+ return true;
+ }
+
+ case TOK.address:
+ {
+ // e1 should be a CTFE reference
+ Expression e1 = (cast(AddrExp)newval).e1;
+ return tb.ty == Tpointer &&
+ (
+ (e1.op == TOK.structLiteral || e1.op == TOK.arrayLiteral) && isCtfeValueValid(e1) ||
+ e1.op == TOK.variable ||
+ e1.op == TOK.dotVariable && isCtfeReferenceValid(e1) ||
+ e1.op == TOK.index && isCtfeReferenceValid(e1) ||
+ e1.op == TOK.slice && e1.type.toBasetype().ty == Tsarray
+ );
+ }
+
+ case TOK.slice:
+ {
+ // e1 should be an array aggregate
+ const SliceExp se = cast(SliceExp)newval;
+ assert(se.lwr && se.lwr.op == TOK.int64);
+ assert(se.upr && se.upr.op == TOK.int64);
+ return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral);
+ }
+
+ case TOK.void_:
+ return true; // uninitialized value
+
+ default:
+ newval.error("CTFE internal error: illegal CTFE value `%s`", newval.toChars());
+ return false;
+ }
+}
+
+bool isCtfeReferenceValid(Expression newval)
+{
+ switch (newval.op)
+ {
+ case TOK.this_:
+ return true;
+
+ case TOK.variable:
+ {
+ const VarDeclaration v = (cast(VarExp)newval).var.isVarDeclaration();
+ assert(v);
+ // Must not be a reference to a reference
+ return true;
+ }
+
+ case TOK.index:
+ {
+ const Expression eagg = (cast(IndexExp)newval).e1;
+ return eagg.op == TOK.string_ || eagg.op == TOK.arrayLiteral || eagg.op == TOK.assocArrayLiteral;
+ }
+
+ case TOK.dotVariable:
+ {
+ Expression eagg = (cast(DotVarExp)newval).e1;
+ return (eagg.op == TOK.structLiteral || eagg.op == TOK.classReference) && isCtfeValueValid(eagg);
+ }
+
+ default:
+ // Internally a ref variable may directly point a stack memory.
+ // e.g. ref int v = 1;
+ return isCtfeValueValid(newval);
+ }
+}
+
+// Used for debugging only
+void showCtfeExpr(Expression e, int level = 0)
+{
+ for (int i = level; i > 0; --i)
+ printf(" ");
+ Expressions* elements = null;
+ // We need the struct definition to detect block assignment
+ StructDeclaration sd = null;
+ ClassDeclaration cd = null;
+ if (e.op == TOK.structLiteral)
+ {
+ elements = (cast(StructLiteralExp)e).elements;
+ sd = (cast(StructLiteralExp)e).sd;
+ printf("STRUCT type = %s %p:\n", e.type.toChars(), e);
+ }
+ else if (e.op == TOK.classReference)
+ {
+ elements = (cast(ClassReferenceExp)e).value.elements;
+ cd = (cast(ClassReferenceExp)e).originalClass();
+ printf("CLASS type = %s %p:\n", e.type.toChars(), (cast(ClassReferenceExp)e).value);
+ }
+ else if (e.op == TOK.arrayLiteral)
+ {
+ elements = (cast(ArrayLiteralExp)e).elements;
+ printf("ARRAY LITERAL type=%s %p:\n", e.type.toChars(), e);
+ }
+ else if (e.op == TOK.assocArrayLiteral)
+ {
+ printf("AA LITERAL type=%s %p:\n", e.type.toChars(), e);
+ }
+ else if (e.op == TOK.string_)
+ {
+ printf("STRING %s %p\n", e.toChars(), e.isStringExp.peekString.ptr);
+ }
+ else if (e.op == TOK.slice)
+ {
+ printf("SLICE %p: %s\n", e, e.toChars());
+ showCtfeExpr((cast(SliceExp)e).e1, level + 1);
+ }
+ else if (e.op == TOK.variable)
+ {
+ printf("VAR %p %s\n", e, e.toChars());
+ VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
+ if (v && getValue(v))
+ showCtfeExpr(getValue(v), level + 1);
+ }
+ else if (e.op == TOK.address)
+ {
+ // This is potentially recursive. We mustn't try to print the thing we're pointing to.
+ printf("POINTER %p to %p: %s\n", e, (cast(AddrExp)e).e1, e.toChars());
+ }
+ else
+ printf("VALUE %p: %s\n", e, e.toChars());
+ if (elements)
+ {
+ size_t fieldsSoFar = 0;
+ for (size_t i = 0; i < elements.dim; i++)
+ {
+ Expression z = null;
+ VarDeclaration v = null;
+ if (i > 15)
+ {
+ printf("...(total %d elements)\n", cast(int)elements.dim);
+ return;
+ }
+ if (sd)
+ {
+ v = sd.fields[i];
+ z = (*elements)[i];
+ }
+ else if (cd)
+ {
+ while (i - fieldsSoFar >= cd.fields.dim)
+ {
+ fieldsSoFar += cd.fields.dim;
+ cd = cd.baseClass;
+ for (int j = level; j > 0; --j)
+ printf(" ");
+ printf(" BASE CLASS: %s\n", cd.toChars());
+ }
+ v = cd.fields[i - fieldsSoFar];
+ assert((elements.dim + i) >= (fieldsSoFar + cd.fields.dim));
+ size_t indx = (elements.dim - fieldsSoFar) - cd.fields.dim + i;
+ assert(indx < elements.dim);
+ z = (*elements)[indx];
+ }
+ if (!z)
+ {
+ for (int j = level; j > 0; --j)
+ printf(" ");
+ printf(" void\n");
+ continue;
+ }
+ if (v)
+ {
+ // If it is a void assignment, use the default initializer
+ if ((v.type.ty != z.type.ty) && v.type.ty == Tsarray)
+ {
+ for (int j = level; --j;)
+ printf(" ");
+ printf(" field: block initialized static array\n");
+ continue;
+ }
+ }
+ showCtfeExpr(z, level + 1);
+ }
+ }
+}
+
+/*************************** Void initialization ***************************/
+UnionExp voidInitLiteral(Type t, VarDeclaration var)
+{
+ UnionExp ue;
+ if (t.ty == Tsarray)
+ {
+ TypeSArray tsa = cast(TypeSArray)t;
+ Expression elem = voidInitLiteral(tsa.next, var).copy();
+ // For aggregate value types (structs, static arrays) we must
+ // create an a separate copy for each element.
+ const mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral);
+ const d = cast(size_t)tsa.dim.toInteger();
+ auto elements = new Expressions(d);
+ foreach (i; 0 .. d)
+ {
+ if (mustCopy && i > 0)
+ elem = copyLiteral(elem).copy();
+ (*elements)[i] = elem;
+ }
+ emplaceExp!(ArrayLiteralExp)(&ue, var.loc, tsa, elements);
+ ArrayLiteralExp ae = cast(ArrayLiteralExp)ue.exp();
+ ae.ownedByCtfe = OwnedBy.ctfe;
+ }
+ else if (t.ty == Tstruct)
+ {
+ TypeStruct ts = cast(TypeStruct)t;
+ auto exps = new Expressions(ts.sym.fields.dim);
+ foreach (size_t i; 0 .. ts.sym.fields.dim)
+ {
+ (*exps)[i] = voidInitLiteral(ts.sym.fields[i].type, ts.sym.fields[i]).copy();
+ }
+ emplaceExp!(StructLiteralExp)(&ue, var.loc, ts.sym, exps);
+ StructLiteralExp se = cast(StructLiteralExp)ue.exp();
+ se.type = ts;
+ se.ownedByCtfe = OwnedBy.ctfe;
+ }
+ else
+ emplaceExp!(VoidInitExp)(&ue, var);
+ return ue;
+}
diff --git a/gcc/d/dmd/ctorflow.d b/gcc/d/dmd/ctorflow.d
new file mode 100644
index 00000000000..c8b61be44a5
--- /dev/null
+++ b/gcc/d/dmd/ctorflow.d
@@ -0,0 +1,225 @@
+/**
+ * Manage flow analysis for constructors.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctorflow.d, _ctorflow.d)
+ * Documentation: https://dlang.org/phobos/dmd_ctorflow.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctorflow.d
+ */
+
+module dmd.ctorflow;
+
+import core.stdc.stdio;
+
+import dmd.root.rmem;
+import dmd.globals : Loc;
+
+enum CSX : ushort
+{
+ none = 0,
+ this_ctor = 0x01, /// called this()
+ super_ctor = 0x02, /// called super()
+ label = 0x04, /// seen a label
+ return_ = 0x08, /// seen a return statement
+ any_ctor = 0x10, /// either this() or super() was called
+ halt = 0x20, /// assert(0)
+}
+
+/// Individual field in the Ctor with information about its callees and location.
+struct FieldInit
+{
+ CSX csx; /// information about the field's callees
+ Loc loc; /// location of the field initialization
+}
+
+/***********
+ * Primitive flow analysis for constructors
+ */
+struct CtorFlow
+{
+ CSX callSuper; /// state of calling other constructors
+
+ FieldInit[] fieldinit; /// state of field initializations
+
+ void allocFieldinit(size_t dim)
+ {
+ fieldinit = (cast(FieldInit*)mem.xcalloc(FieldInit.sizeof, dim))[0 .. dim];
+ }
+
+ void freeFieldinit()
+ {
+ if (fieldinit.ptr)
+ mem.xfree(fieldinit.ptr);
+
+ fieldinit = null;
+ }
+
+ /***********************
+ * Create a deep copy of `this`
+ * Returns:
+ * a copy
+ */
+ CtorFlow clone()
+ {
+ return CtorFlow(callSuper, fieldinit.arraydup);
+ }
+
+ /**********************************
+ * Set CSX bits in flow analysis state
+ * Params:
+ * csx = bits to set
+ */
+ void orCSX(CSX csx) nothrow pure
+ {
+ callSuper |= csx;
+ foreach (ref u; fieldinit)
+ u.csx |= csx;
+ }
+
+ /******************************
+ * OR CSX bits to `this`
+ * Params:
+ * ctorflow = bits to OR in
+ */
+ void OR(const ref CtorFlow ctorflow) pure nothrow
+ {
+ callSuper |= ctorflow.callSuper;
+ if (fieldinit.length && ctorflow.fieldinit.length)
+ {
+ assert(fieldinit.length == ctorflow.fieldinit.length);
+ foreach (i, u; ctorflow.fieldinit)
+ {
+ auto fi = &fieldinit[i];
+ fi.csx |= u.csx;
+ if (fi.loc is Loc.init)
+ fi.loc = u.loc;
+ }
+ }
+ }
+}
+
+
+/****************************************
+ * Merge `b` flow analysis results into `a`.
+ * Params:
+ * a = the path to merge `b` into
+ * b = the other path
+ * Returns:
+ * false means one of the paths skips construction
+ */
+bool mergeCallSuper(ref CSX a, const CSX b) pure nothrow
+{
+ // This does a primitive flow analysis to support the restrictions
+ // regarding when and how constructors can appear.
+ // It merges the results of two paths.
+ // The two paths are `a` and `b`; the result is merged into `a`.
+ if (b == a)
+ return true;
+
+ // Have ALL branches called a constructor?
+ const aAll = (a & (CSX.this_ctor | CSX.super_ctor)) != 0;
+ const bAll = (b & (CSX.this_ctor | CSX.super_ctor)) != 0;
+ // Have ANY branches called a constructor?
+ const aAny = (a & CSX.any_ctor) != 0;
+ const bAny = (b & CSX.any_ctor) != 0;
+ // Have any branches returned?
+ const aRet = (a & CSX.return_) != 0;
+ const bRet = (b & CSX.return_) != 0;
+ // Have any branches halted?
+ const aHalt = (a & CSX.halt) != 0;
+ const bHalt = (b & CSX.halt) != 0;
+ if (aHalt && bHalt)
+ {
+ a = CSX.halt;
+ }
+ else if ((!bHalt && bRet && !bAny && aAny) || (!aHalt && aRet && !aAny && bAny))
+ {
+ // If one has returned without a constructor call, there must not
+ // be ctor calls in the other.
+ return false;
+ }
+ else if (bHalt || bRet && bAll)
+ {
+ // If one branch has called a ctor and then exited, anything the
+ // other branch has done is OK (except returning without a
+ // ctor call, but we already checked that).
+ a |= b & (CSX.any_ctor | CSX.label);
+ }
+ else if (aHalt || aRet && aAll)
+ {
+ a = cast(CSX)(b | (a & (CSX.any_ctor | CSX.label)));
+ }
+ else if (aAll != bAll) // both branches must have called ctors, or both not
+ return false;
+ else
+ {
+ // If one returned without a ctor, remember that
+ if (bRet && !bAny)
+ a |= CSX.return_;
+ a |= b & (CSX.any_ctor | CSX.label);
+ }
+ return true;
+}
+
+
+/****************************************
+ * Merge `b` flow analysis results into `a`.
+ * Params:
+ * a = the path to merge `b` into
+ * b = the other path
+ * Returns:
+ * false means either `a` or `b` skips initialization
+ */
+bool mergeFieldInit(ref CSX a, const CSX b) pure nothrow
+{
+ if (b == a)
+ return true;
+
+ // Have any branches returned?
+ const aRet = (a & CSX.return_) != 0;
+ const bRet = (b & CSX.return_) != 0;
+ // Have any branches halted?
+ const aHalt = (a & CSX.halt) != 0;
+ const bHalt = (b & CSX.halt) != 0;
+
+ if (aHalt && bHalt)
+ {
+ a = CSX.halt;
+ return true;
+ }
+
+ // The logic here is to prefer the branch that neither halts nor returns.
+ bool ok;
+ if (!bHalt && bRet)
+ {
+ // Branch b returns, no merging required.
+ ok = (b & CSX.this_ctor);
+ }
+ else if (!aHalt && aRet)
+ {
+ // Branch a returns, but b doesn't, b takes precedence.
+ ok = (a & CSX.this_ctor);
+ a = b;
+ }
+ else if (bHalt)
+ {
+ // Branch b halts, no merging required.
+ ok = (a & CSX.this_ctor);
+ }
+ else if (aHalt)
+ {
+ // Branch a halts, but b doesn't, b takes precedence.
+ ok = (b & CSX.this_ctor);
+ a = b;
+ }
+ else
+ {
+ // Neither branch returns nor halts, merge flags.
+ ok = !((a ^ b) & CSX.this_ctor);
+ a |= b;
+ }
+ return ok;
+}
+
diff --git a/gcc/d/dmd/dcast.c b/gcc/d/dmd/dcast.c
deleted file mode 100644
index d84ab7ffc21..00000000000
--- a/gcc/d/dmd/dcast.c
+++ /dev/null
@@ -1,3566 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/cast.c
- */
-
-#include "root/dsystem.h" // mem{set|cpy}()
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "expression.h"
-#include "mtype.h"
-#include "utf.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "template.h"
-#include "scope.h"
-#include "id.h"
-#include "init.h"
-#include "tokens.h"
-
-FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL);
-bool isCommutative(TOK op);
-MOD MODmerge(MOD mod1, MOD mod2);
-void toAutoQualChars(const char **result, Type *t1, Type *t2);
-
-/* ==================== implicitCast ====================== */
-
-/**************************************
- * Do an implicit cast.
- * Issue error if it can't be done.
- */
-
-
-Expression *implicitCastTo(Expression *e, Scope *sc, Type *t)
-{
- class ImplicitCastTo : public Visitor
- {
- public:
- Type *t;
- Scope *sc;
- Expression *result;
-
- ImplicitCastTo(Scope *sc, Type *t)
- : t(t), sc(sc)
- {
- result = NULL;
- }
-
- void visit(Expression *e)
- {
- //printf("Expression::implicitCastTo(%s of type %s) => %s\n", e->toChars(), e->type->toChars(), t->toChars());
-
- MATCH match = e->implicitConvTo(t);
- if (match)
- {
- if (match == MATCHconst &&
- (e->type->constConv(t) ||
- (!e->isLvalue() && e->type->equivalent(t))))
- {
- /* Do not emit CastExp for const conversions and
- * unique conversions on rvalue.
- */
- result = e->copy();
- result->type = t;
- return;
- }
- result = e->castTo(sc, t);
- return;
- }
-
- result = e->optimize(WANTvalue);
- if (result != e)
- {
- result->accept(this);
- return;
- }
-
- if (t->ty != Terror && e->type->ty != Terror)
- {
- if (!t->deco)
- {
- e->error("forward reference to type %s", t->toChars());
- }
- else
- {
- //printf("type %p ty %d deco %p\n", type, type->ty, type->deco);
- //type = type->semantic(loc, sc);
- //printf("type %s t %s\n", type->deco, t->deco);
- const char *ts[2];
- toAutoQualChars(ts, e->type, t);
- e->error("cannot implicitly convert expression (%s) of type %s to %s",
- e->toChars(), ts[0], ts[1]);
- }
- }
- result = new ErrorExp();
- }
-
- void visit(StringExp *e)
- {
- //printf("StringExp::implicitCastTo(%s of type %s) => %s\n", e->toChars(), e->type->toChars(), t->toChars());
- visit((Expression *)e);
- if (result->op == TOKstring)
- {
- // Retain polysemous nature if it started out that way
- ((StringExp *)result)->committed = e->committed;
- }
- }
-
- void visit(ErrorExp *e)
- {
- result = e;
- }
-
- void visit(FuncExp *e)
- {
- //printf("FuncExp::implicitCastTo type = %p %s, t = %s\n", e->type, e->type ? e->type->toChars() : NULL, t->toChars());
- FuncExp *fe;
- if (e->matchType(t, sc, &fe) > MATCHnomatch)
- {
- result = fe;
- return;
- }
- visit((Expression *)e);
- }
-
- void visit(ArrayLiteralExp *e)
- {
- visit((Expression *)e);
-
- Type *tb = result->type->toBasetype();
- if (tb->ty == Tarray && global.params.useTypeInfo && Type::dtypeinfo)
- semanticTypeInfo(sc, ((TypeDArray *)tb)->next);
- }
-
- void visit(SliceExp *e)
- {
- visit((Expression *)e);
- if (result->op != TOKslice)
- return;
-
- e = (SliceExp *)result;
- if (e->e1->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e->e1;
- Type *tb = t->toBasetype();
- Type *tx;
- if (tb->ty == Tsarray)
- tx = tb->nextOf()->sarrayOf(ale->elements ? ale->elements->length : 0);
- else
- tx = tb->nextOf()->arrayOf();
- e->e1 = ale->implicitCastTo(sc, tx);
- }
- }
- };
-
- ImplicitCastTo v(sc, t);
- e->accept(&v);
- return v.result;
-}
-
-/*******************************************
- * Return MATCH level of implicitly converting e to type t.
- * Don't do the actual cast; don't change e.
- */
-
-MATCH implicitConvTo(Expression *e, Type *t)
-{
- class ImplicitConvTo : public Visitor
- {
- public:
- Type *t;
- MATCH result;
-
- ImplicitConvTo(Type *t)
- : t(t)
- {
- result = MATCHnomatch;
- }
-
- void visit(Expression *e)
- {
- //static int nest; if (++nest == 10) halt();
- if (t == Type::terror)
- return;
- if (!e->type)
- {
- e->error("%s is not an expression", e->toChars());
- e->type = Type::terror;
- }
- Expression *ex = e->optimize(WANTvalue);
- if (ex->type->equals(t))
- {
- result = MATCHexact;
- return;
- }
- if (ex != e)
- {
- //printf("\toptimized to %s of type %s\n", e->toChars(), e->type->toChars());
- result = ex->implicitConvTo(t);
- return;
- }
- MATCH match = e->type->implicitConvTo(t);
- if (match != MATCHnomatch)
- {
- result = match;
- return;
- }
-
- /* See if we can do integral narrowing conversions
- */
- if (e->type->isintegral() && t->isintegral() &&
- e->type->isTypeBasic() && t->isTypeBasic())
- {
- IntRange src = getIntRange(e);
- IntRange target = IntRange::fromType(t);
- if (target.contains(src))
- {
- result = MATCHconvert;
- return;
- }
- }
- }
-
- /******
- * Given expression e of type t, see if we can implicitly convert e
- * to type tprime, where tprime is type t with mod bits added.
- * Returns:
- * match level
- */
- static MATCH implicitMod(Expression *e, Type *t, MOD mod)
- {
- Type *tprime;
- if (t->ty == Tpointer)
- tprime = t->nextOf()->castMod(mod)->pointerTo();
- else if (t->ty == Tarray)
- tprime = t->nextOf()->castMod(mod)->arrayOf();
- else if (t->ty == Tsarray)
- tprime = t->nextOf()->castMod(mod)->sarrayOf(t->size() / t->nextOf()->size());
- else
- tprime = t->castMod(mod);
-
- return e->implicitConvTo(tprime);
- }
-
- static MATCH implicitConvToAddMin(BinExp *e, Type *t)
- {
- /* Is this (ptr +- offset)? If so, then ask ptr
- * if the conversion can be done.
- * This is to support doing things like implicitly converting a mutable unique
- * pointer to an immutable pointer.
- */
-
- Type *typeb = e->type->toBasetype();
- Type *tb = t->toBasetype();
- if (typeb->ty != Tpointer || tb->ty != Tpointer)
- return MATCHnomatch;
-
- Type *t1b = e->e1->type->toBasetype();
- Type *t2b = e->e2->type->toBasetype();
- if (t1b->ty == Tpointer && t2b->isintegral() &&
- t1b->equivalent(tb))
- {
- // ptr + offset
- // ptr - offset
- MATCH m = e->e1->implicitConvTo(t);
- return (m > MATCHconst) ? MATCHconst : m;
- }
- if (t2b->ty == Tpointer && t1b->isintegral() &&
- t2b->equivalent(tb))
- {
- // offset + ptr
- MATCH m = e->e2->implicitConvTo(t);
- return (m > MATCHconst) ? MATCHconst : m;
- }
-
- return MATCHnomatch;
- }
-
- void visit(AddExp *e)
- {
- visit((Expression *)e);
- if (result == MATCHnomatch)
- result = implicitConvToAddMin(e, t);
- }
-
- void visit(MinExp *e)
- {
- visit((Expression *)e);
- if (result == MATCHnomatch)
- result = implicitConvToAddMin(e, t);
- }
-
- void visit(IntegerExp *e)
- {
- MATCH m = e->type->implicitConvTo(t);
- if (m >= MATCHconst)
- {
- result = m;
- return;
- }
-
- TY ty = e->type->toBasetype()->ty;
- TY toty = t->toBasetype()->ty;
- TY oldty = ty;
-
- if (m == MATCHnomatch && t->ty == Tenum)
- return;
-
- if (t->ty == Tvector)
- {
- TypeVector *tv = (TypeVector *)t;
- TypeBasic *tb = tv->elementType();
- if (tb->ty == Tvoid)
- return;
- toty = tb->ty;
- }
-
- switch (ty)
- {
- case Tbool:
- case Tint8:
- case Tchar:
- case Tuns8:
- case Tint16:
- case Tuns16:
- case Twchar:
- ty = Tint32;
- break;
-
- case Tdchar:
- ty = Tuns32;
- break;
-
- default:
- break;
- }
-
- // Only allow conversion if no change in value
- dinteger_t value = e->toInteger();
- switch (toty)
- {
- case Tbool:
- if ((value & 1) != value)
- return;
- break;
-
- case Tint8:
- if (ty == Tuns64 && value & ~0x7FUL)
- return;
- else if ((signed char)value != (sinteger_t)value)
- return;
- break;
-
- case Tchar:
- if ((oldty == Twchar || oldty == Tdchar) && value > 0x7F)
- return;
- /* fall through */
- case Tuns8:
- //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value);
- if ((unsigned char)value != value)
- return;
- break;
-
- case Tint16:
- if (ty == Tuns64 && value & ~0x7FFFUL)
- return;
- else if ((short)value != (sinteger_t)value)
- return;
- break;
-
- case Twchar:
- if (oldty == Tdchar && value > 0xD7FF && value < 0xE000)
- return;
- /* fall through */
- case Tuns16:
- if ((unsigned short)value != value)
- return;
- break;
-
- case Tint32:
- if (ty == Tuns32)
- {
- }
- else if (ty == Tuns64 && value & ~0x7FFFFFFFUL)
- return;
- else if ((int)value != (sinteger_t)value)
- return;
- break;
-
- case Tuns32:
- if (ty == Tint32)
- {
- }
- else if ((unsigned)value != value)
- return;
- break;
-
- case Tdchar:
- if (value > 0x10FFFFUL)
- return;
- break;
-
- case Tfloat32:
- {
- volatile float f;
- if (e->type->isunsigned())
- {
- f = (float)value;
- if (f != value)
- return;
- }
- else
- {
- f = (float)(sinteger_t)value;
- if (f != (sinteger_t)value)
- return;
- }
- break;
- }
-
- case Tfloat64:
- {
- volatile double f;
- if (e->type->isunsigned())
- {
- f = (double)value;
- if (f != value)
- return;
- }
- else
- {
- f = (double)(sinteger_t)value;
- if (f != (sinteger_t)value)
- return;
- }
- break;
- }
-
- case Tfloat80:
- {
- volatile_longdouble f;
- if (e->type->isunsigned())
- {
- f = ldouble(value);
- if ((dinteger_t)f != value) // isn't this a noop, because the compiler prefers ld
- return;
- }
- else
- {
- f = ldouble((sinteger_t)value);
- if ((sinteger_t)f != (sinteger_t)value)
- return;
- }
- break;
- }
-
- case Tpointer:
- //printf("type = %s\n", type->toBasetype()->toChars());
- //printf("t = %s\n", t->toBasetype()->toChars());
- if (ty == Tpointer &&
- e->type->toBasetype()->nextOf()->ty == t->toBasetype()->nextOf()->ty)
- {
- /* Allow things like:
- * const char* P = cast(char *)3;
- * char* q = P;
- */
- break;
- }
- /* fall through */
-
- default:
- visit((Expression *)e);
- return;
- }
-
- //printf("MATCHconvert\n");
- result = MATCHconvert;
- }
-
- void visit(ErrorExp *)
- {
- // no match
- }
-
- void visit(NullExp *e)
- {
- if (e->type->equals(t))
- {
- result = MATCHexact;
- return;
- }
-
- /* Allow implicit conversions from immutable to mutable|const,
- * and mutable to immutable. It works because, after all, a null
- * doesn't actually point to anything.
- */
- if (t->equivalent(e->type))
- {
- result = MATCHconst;
- return;
- }
-
- visit((Expression *)e);
- }
-
- void visit(StructLiteralExp *e)
- {
- visit((Expression *)e);
- if (result != MATCHnomatch)
- return;
- if (e->type->ty == t->ty && e->type->ty == Tstruct &&
- ((TypeStruct *)e->type)->sym == ((TypeStruct *)t)->sym)
- {
- result = MATCHconst;
- for (size_t i = 0; i < e->elements->length; i++)
- {
- Expression *el = (*e->elements)[i];
- if (!el)
- continue;
- Type *te = el->type;
- te = e->sd->fields[i]->type->addMod(t->mod);
- MATCH m2 = el->implicitConvTo(te);
- //printf("\t%s => %s, match = %d\n", el->toChars(), te->toChars(), m2);
- if (m2 < result)
- result = m2;
- }
- }
- }
-
- void visit(StringExp *e)
- {
- if (!e->committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid)
- return;
-
- if (e->type->ty == Tsarray || e->type->ty == Tarray || e->type->ty == Tpointer)
- {
- TY tyn = e->type->nextOf()->ty;
- if (tyn == Tchar || tyn == Twchar || tyn == Tdchar)
- {
- switch (t->ty)
- {
- case Tsarray:
- if (e->type->ty == Tsarray)
- {
- TY tynto = t->nextOf()->ty;
- if (tynto == tyn)
- {
- if (((TypeSArray *)e->type)->dim->toInteger() ==
- ((TypeSArray *)t)->dim->toInteger())
- {
- result = MATCHexact;
- }
- return;
- }
- if (tynto == Tchar || tynto == Twchar || tynto == Tdchar)
- {
- if (e->committed && tynto != tyn)
- return;
- size_t fromlen = e->numberOfCodeUnits(tynto);
- size_t tolen = (size_t)((TypeSArray *)t)->dim->toInteger();
- if (tolen < fromlen)
- return;
- if (tolen != fromlen)
- {
- // implicit length extending
- result = MATCHconvert;
- return;
- }
- }
- if (!e->committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar))
- {
- result = MATCHexact;
- return;
- }
- }
- else if (e->type->ty == Tarray)
- {
- TY tynto = t->nextOf()->ty;
- if (tynto == Tchar || tynto == Twchar || tynto == Tdchar)
- {
- if (e->committed && tynto != tyn)
- return;
- size_t fromlen = e->numberOfCodeUnits(tynto);
- size_t tolen = (size_t)((TypeSArray *)t)->dim->toInteger();
- if (tolen < fromlen)
- return;
- if (tolen != fromlen)
- {
- // implicit length extending
- result = MATCHconvert;
- return;
- }
- }
- if (tynto == tyn)
- {
- result = MATCHexact;
- return;
- }
- if (!e->committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar))
- {
- result = MATCHexact;
- return;
- }
- }
- /* fall through */
- case Tarray:
- case Tpointer:
- Type *tn = t->nextOf();
- MATCH m = MATCHexact;
- if (e->type->nextOf()->mod != tn->mod)
- {
- if (!tn->isConst())
- return;
- m = MATCHconst;
- }
- if (!e->committed)
- {
- switch (tn->ty)
- {
- case Tchar:
- if (e->postfix == 'w' || e->postfix == 'd')
- m = MATCHconvert;
- result = m;
- return;
- case Twchar:
- if (e->postfix != 'w')
- m = MATCHconvert;
- result = m;
- return;
- case Tdchar:
- if (e->postfix != 'd')
- m = MATCHconvert;
- result = m;
- return;
- }
- }
- break;
- }
- }
- }
-
- visit((Expression *)e);
- }
-
- void visit(ArrayLiteralExp *e)
- {
- Type *typeb = e->type->toBasetype();
- Type *tb = t->toBasetype();
- if ((tb->ty == Tarray || tb->ty == Tsarray) &&
- (typeb->ty == Tarray || typeb->ty == Tsarray))
- {
- result = MATCHexact;
- Type *typen = typeb->nextOf()->toBasetype();
-
- if (tb->ty == Tsarray)
- {
- TypeSArray *tsa = (TypeSArray *)tb;
- if (e->elements->length != tsa->dim->toInteger())
- result = MATCHnomatch;
- }
-
- Type *telement = tb->nextOf();
- if (!e->elements->length)
- {
- if (typen->ty != Tvoid)
- result = typen->implicitConvTo(telement);
- }
- else
- {
- if (e->basis)
- {
- MATCH m = e->basis->implicitConvTo(telement);
- if (m < result)
- result = m;
- }
- for (size_t i = 0; i < e->elements->length; i++)
- {
- Expression *el = (*e->elements)[i];
- if (result == MATCHnomatch)
- break;
- if (!el)
- continue;
- MATCH m = el->implicitConvTo(telement);
- if (m < result)
- result = m; // remember worst match
- }
- }
-
- if (!result)
- result = e->type->implicitConvTo(t);
-
- return;
- }
- else if (tb->ty == Tvector &&
- (typeb->ty == Tarray || typeb->ty == Tsarray))
- {
- result = MATCHexact;
- // Convert array literal to vector type
- TypeVector *tv = (TypeVector *)tb;
- TypeSArray *tbase = (TypeSArray *)tv->basetype;
- assert(tbase->ty == Tsarray);
- const size_t edim = e->elements->length;
- const size_t tbasedim = tbase->dim->toInteger();
- if (edim > tbasedim)
- {
- result = MATCHnomatch;
- return;
- }
-
- Type *telement = tv->elementType();
- if (edim < tbasedim)
- {
- Expression *el = typeb->nextOf()->defaultInitLiteral(e->loc);
- MATCH m = el->implicitConvTo(telement);
- if (m < result)
- result = m; // remember worst match
- }
- for (size_t i = 0; i < edim; i++)
- {
- Expression *el = (*e->elements)[i];
- MATCH m = el->implicitConvTo(telement);
- if (m < result)
- result = m; // remember worst match
- if (result == MATCHnomatch)
- break; // no need to check for worse
- }
- return;
- }
-
- visit((Expression *)e);
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- Type *typeb = e->type->toBasetype();
- Type *tb = t->toBasetype();
- if (tb->ty == Taarray && typeb->ty == Taarray)
- {
- result = MATCHexact;
- for (size_t i = 0; i < e->keys->length; i++)
- {
- Expression *el = (*e->keys)[i];
- MATCH m = el->implicitConvTo(((TypeAArray *)tb)->index);
- if (m < result)
- result = m; // remember worst match
- if (result == MATCHnomatch)
- break; // no need to check for worse
- el = (*e->values)[i];
- m = el->implicitConvTo(tb->nextOf());
- if (m < result)
- result = m; // remember worst match
- if (result == MATCHnomatch)
- break; // no need to check for worse
- }
- return;
- }
- else
- visit((Expression *)e);
- }
-
- void visit(CallExp *e)
- {
- visit((Expression *)e);
- if (result != MATCHnomatch)
- return;
-
- /* Allow the result of strongly pure functions to
- * convert to immutable
- */
- if (e->f && e->f->isolateReturn())
- {
- result = e->type->immutableOf()->implicitConvTo(t);
- if (result > MATCHconst) // Match level is MATCHconst at best.
- result = MATCHconst;
- return;
- }
-
- /* Conversion is 'const' conversion if:
- * 1. function is pure (weakly pure is ok)
- * 2. implicit conversion only fails because of mod bits
- * 3. each function parameter can be implicitly converted to the mod bits
- */
- Type *tx = e->f ? e->f->type : e->e1->type;
- tx = tx->toBasetype();
- if (tx->ty != Tfunction)
- return;
- TypeFunction *tf = (TypeFunction *)tx;
-
- if (tf->purity == PUREimpure)
- return;
- if (e->f && e->f->isNested())
- return;
-
- /* See if fail only because of mod bits.
- *
- * Bugzilla 14155: All pure functions can access global immutable data.
- * So the returned pointer may refer an immutable global data,
- * and then the returned pointer that points non-mutable object
- * cannot be unique pointer.
- *
- * Example:
- * immutable g;
- * static this() { g = 1; }
- * const(int*) foo() pure { return &g; }
- * void test() {
- * immutable(int*) ip = foo(); // OK
- * int* mp = foo(); // should be disallowed
- * }
- */
- if (e->type->immutableOf()->implicitConvTo(t) < MATCHconst &&
- e->type->addMod(MODshared)->implicitConvTo(t) < MATCHconst &&
- e->type->implicitConvTo(t->addMod(MODshared)) < MATCHconst)
- {
- return;
- }
- // Allow a conversion to immutable type, or
- // conversions of mutable types between thread-local and shared.
-
- /* Get mod bits of what we're converting to
- */
- Type *tb = t->toBasetype();
- MOD mod = tb->mod;
- if (tf->isref)
- ;
- else
- {
- Type *ti = getIndirection(t);
- if (ti)
- mod = ti->mod;
- }
- if (mod & MODwild)
- return; // not sure what to do with this
-
- /* Apply mod bits to each function parameter,
- * and see if we can convert the function argument to the modded type
- */
-
- size_t nparams = tf->parameterList.length();
- size_t j = tf->isDstyleVariadic(); // if TypeInfoArray was prepended
- if (e->e1->op == TOKdotvar)
- {
- /* Treat 'this' as just another function argument
- */
- DotVarExp *dve = (DotVarExp *)e->e1;
- Type *targ = dve->e1->type;
- if (targ->constConv(targ->castMod(mod)) == MATCHnomatch)
- return;
- }
- for (size_t i = j; i < e->arguments->length; ++i)
- {
- Expression *earg = (*e->arguments)[i];
- Type *targ = earg->type->toBasetype();
- if (i - j < nparams)
- {
- Parameter *fparam = tf->parameterList[i - j];
- if (fparam->storageClass & STClazy)
- return; // not sure what to do with this
- Type *tparam = fparam->type;
- if (!tparam)
- continue;
- if (fparam->storageClass & (STCout | STCref))
- {
- if (targ->constConv(tparam->castMod(mod)) == MATCHnomatch)
- return;
- continue;
- }
- }
-
- if (implicitMod(earg, targ, mod) == MATCHnomatch)
- return;
- }
-
- /* Success
- */
- result = MATCHconst;
- }
-
- void visit(AddrExp *e)
- {
- result = e->type->implicitConvTo(t);
- //printf("\tresult = %d\n", result);
-
- if (result != MATCHnomatch)
- return;
-
- // Look for pointers to functions where the functions are overloaded.
-
- t = t->toBasetype();
-
- if (e->e1->op == TOKoverloadset &&
- (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction)
- {
- OverExp *eo = (OverExp *)e->e1;
- FuncDeclaration *f = NULL;
- for (size_t i = 0; i < eo->vars->a.length; i++)
- {
- Dsymbol *s = eo->vars->a[i];
- FuncDeclaration *f2 = s->isFuncDeclaration();
- assert(f2);
- if (f2->overloadExactMatch(t->nextOf()))
- {
- if (f)
- {
- /* Error if match in more than one overload set,
- * even if one is a 'better' match than the other.
- */
- ScopeDsymbol::multiplyDefined(e->loc, f, f2);
- }
- else
- f = f2;
- result = MATCHexact;
- }
- }
- }
-
- if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction &&
- t->ty == Tpointer && t->nextOf()->ty == Tfunction &&
- e->e1->op == TOKvar)
- {
- /* I don't think this can ever happen -
- * it should have been
- * converted to a SymOffExp.
- */
- assert(0);
- }
-
- //printf("\tresult = %d\n", result);
- }
-
- void visit(SymOffExp *e)
- {
- result = e->type->implicitConvTo(t);
- //printf("\tresult = %d\n", result);
- if (result != MATCHnomatch)
- return;
-
- // Look for pointers to functions where the functions are overloaded.
- t = t->toBasetype();
- if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction &&
- (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction)
- {
- if (FuncDeclaration *f = e->var->isFuncDeclaration())
- {
- f = f->overloadExactMatch(t->nextOf());
- if (f)
- {
- if ((t->ty == Tdelegate && (f->needThis() || f->isNested())) ||
- (t->ty == Tpointer && !(f->needThis() || f->isNested())))
- {
- result = MATCHexact;
- }
- }
- }
- }
- //printf("\tresult = %d\n", result);
- }
-
- void visit(DelegateExp *e)
- {
- result = e->type->implicitConvTo(t);
- if (result != MATCHnomatch)
- return;
-
- // Look for pointers to functions where the functions are overloaded.
- t = t->toBasetype();
- if (e->type->ty == Tdelegate &&
- t->ty == Tdelegate)
- {
- if (e->func && e->func->overloadExactMatch(t->nextOf()))
- result = MATCHexact;
- }
- }
-
- void visit(FuncExp *e)
- {
- //printf("FuncExp::implicitConvTo type = %p %s, t = %s\n", e->type, e->type ? e->type->toChars() : NULL, t->toChars());
- MATCH m = e->matchType(t, NULL, NULL, 1);
- if (m > MATCHnomatch)
- {
- result = m;
- return;
- }
- visit((Expression *)e);
- }
-
- void visit(AndExp *e)
- {
- visit((Expression *)e);
- if (result != MATCHnomatch)
- return;
-
- MATCH m1 = e->e1->implicitConvTo(t);
- MATCH m2 = e->e2->implicitConvTo(t);
-
- // Pick the worst match
- result = (m1 < m2) ? m1 : m2;
- }
-
- void visit(OrExp *e)
- {
- visit((Expression *)e);
- if (result != MATCHnomatch)
- return;
-
- MATCH m1 = e->e1->implicitConvTo(t);
- MATCH m2 = e->e2->implicitConvTo(t);
-
- // Pick the worst match
- result = (m1 < m2) ? m1 : m2;
- }
-
- void visit(XorExp *e)
- {
- visit((Expression *)e);
- if (result != MATCHnomatch)
- return;
-
- MATCH m1 = e->e1->implicitConvTo(t);
- MATCH m2 = e->e2->implicitConvTo(t);
-
- // Pick the worst match
- result = (m1 < m2) ? m1 : m2;
- }
-
- void visit(CondExp *e)
- {
- MATCH m1 = e->e1->implicitConvTo(t);
- MATCH m2 = e->e2->implicitConvTo(t);
- //printf("CondExp: m1 %d m2 %d\n", m1, m2);
-
- // Pick the worst match
- result = (m1 < m2) ? m1 : m2;
- }
-
- void visit(CommaExp *e)
- {
- e->e2->accept(this);
- }
-
- void visit(CastExp *e)
- {
- result = e->type->implicitConvTo(t);
- if (result != MATCHnomatch)
- return;
-
- if (t->isintegral() &&
- e->e1->type->isintegral() &&
- e->e1->implicitConvTo(t) != MATCHnomatch)
- result = MATCHconvert;
- else
- visit((Expression *)e);
- }
-
- void visit(NewExp *e)
- {
- visit((Expression *)e);
- if (result != MATCHnomatch)
- return;
-
- /* Calling new() is like calling a pure function. We can implicitly convert the
- * return from new() to t using the same algorithm as in CallExp, with the function
- * 'arguments' being:
- * thisexp
- * newargs
- * arguments
- * .init
- * 'member' and 'allocator' need to be pure.
- */
-
- /* See if fail only because of mod bits
- */
- if (e->type->immutableOf()->implicitConvTo(t->immutableOf()) == MATCHnomatch)
- return;
-
- /* Get mod bits of what we're converting to
- */
- Type *tb = t->toBasetype();
- MOD mod = tb->mod;
- if (Type *ti = getIndirection(t))
- mod = ti->mod;
- if (mod & MODwild)
- return; // not sure what to do with this
-
- /* Apply mod bits to each argument,
- * and see if we can convert the argument to the modded type
- */
-
- if (e->thisexp)
- {
- /* Treat 'this' as just another function argument
- */
- Type *targ = e->thisexp->type;
- if (targ->constConv(targ->castMod(mod)) == MATCHnomatch)
- return;
- }
-
- /* Check call to 'allocator', then 'member'
- */
- FuncDeclaration *fd = e->allocator;
- for (int count = 0; count < 2; ++count, (fd = e->member))
- {
- if (!fd)
- continue;
- if (fd->errors || fd->type->ty != Tfunction)
- return; // error
- TypeFunction *tf = (TypeFunction *)fd->type;
- if (tf->purity == PUREimpure)
- return; // impure
-
- if (fd == e->member)
- {
- if (e->type->immutableOf()->implicitConvTo(t) < MATCHconst &&
- e->type->addMod(MODshared)->implicitConvTo(t) < MATCHconst &&
- e->type->implicitConvTo(t->addMod(MODshared)) < MATCHconst)
- {
- return;
- }
- // Allow a conversion to immutable type, or
- // conversions of mutable types between thread-local and shared.
- }
-
- Expressions *args = (fd == e->allocator) ? e->newargs : e->arguments;
-
- size_t nparams = tf->parameterList.length();
- size_t j = tf->isDstyleVariadic(); // if TypeInfoArray was prepended
- for (size_t i = j; i < e->arguments->length; ++i)
- {
- Expression *earg = (*args)[i];
- Type *targ = earg->type->toBasetype();
- if (i - j < nparams)
- {
- Parameter *fparam = tf->parameterList[i - j];
- if (fparam->storageClass & STClazy)
- return; // not sure what to do with this
- Type *tparam = fparam->type;
- if (!tparam)
- continue;
- if (fparam->storageClass & (STCout | STCref))
- {
- if (targ->constConv(tparam->castMod(mod)) == MATCHnomatch)
- return;
- continue;
- }
- }
-
- if (implicitMod(earg, targ, mod) == MATCHnomatch)
- return;
- }
- }
-
- /* If no 'member', then construction is by simple assignment,
- * and just straight check 'arguments'
- */
- if (!e->member && e->arguments)
- {
- for (size_t i = 0; i < e->arguments->length; ++i)
- {
- Expression *earg = (*e->arguments)[i];
- if (!earg) // Bugzilla 14853: if it's on overlapped field
- continue;
- Type *targ = earg->type->toBasetype();
- if (implicitMod(earg, targ, mod) == MATCHnomatch)
- return;
- }
- }
-
- /* Consider the .init expression as an argument
- */
- Type *ntb = e->newtype->toBasetype();
- if (ntb->ty == Tarray)
- ntb = ntb->nextOf()->toBasetype();
- if (ntb->ty == Tstruct)
- {
- // Don't allow nested structs - uplevel reference may not be convertible
- StructDeclaration *sd = ((TypeStruct *)ntb)->sym;
- sd->size(e->loc); // resolve any forward references
- if (sd->isNested())
- return;
- }
- if (ntb->isZeroInit(e->loc))
- {
- /* Zeros are implicitly convertible, except for special cases.
- */
- if (ntb->ty == Tclass)
- {
- /* With new() must look at the class instance initializer.
- */
- ClassDeclaration *cd = ((TypeClass *)ntb)->sym;
-
- cd->size(e->loc); // resolve any forward references
-
- if (cd->isNested())
- return; // uplevel reference may not be convertible
-
- assert(!cd->isInterfaceDeclaration());
-
- struct ClassCheck
- {
- static bool convertible(Loc loc, ClassDeclaration *cd, MOD mod)
- {
- for (size_t i = 0; i < cd->fields.length; i++)
- {
- VarDeclaration *v = cd->fields[i];
- Initializer *init = v->_init;
- if (init)
- {
- if (init->isVoidInitializer())
- ;
- else if (ExpInitializer *ei = init->isExpInitializer())
- {
- Type *tb = v->type->toBasetype();
- if (implicitMod(ei->exp, tb, mod) == MATCHnomatch)
- return false;
- }
- else
- {
- /* Enhancement: handle StructInitializer and ArrayInitializer
- */
- return false;
- }
- }
- else if (!v->type->isZeroInit(loc))
- return false;
- }
- return cd->baseClass ? convertible(loc, cd->baseClass, mod) : true;
- }
- };
-
- if (!ClassCheck::convertible(e->loc, cd, mod))
- return;
- }
- }
- else
- {
- Expression *earg = e->newtype->defaultInitLiteral(e->loc);
- Type *targ = e->newtype->toBasetype();
-
- if (implicitMod(earg, targ, mod) == MATCHnomatch)
- return;
- }
-
- /* Success
- */
- result = MATCHconst;
- }
-
- void visit(SliceExp *e)
- {
- //printf("SliceExp::implicitConvTo e = %s, type = %s\n", e->toChars(), e->type->toChars());
- visit((Expression *)e);
- if (result != MATCHnomatch)
- return;
-
- Type *tb = t->toBasetype();
- Type *typeb = e->type->toBasetype();
- if (tb->ty == Tsarray && typeb->ty == Tarray)
- {
- typeb = toStaticArrayType(e);
- if (typeb)
- result = typeb->implicitConvTo(t);
- return;
- }
-
- /* If the only reason it won't convert is because of the mod bits,
- * then test for conversion by seeing if e1 can be converted with those
- * same mod bits.
- */
- Type *t1b = e->e1->type->toBasetype();
- if (tb->ty == Tarray && typeb->equivalent(tb))
- {
- Type *tbn = tb->nextOf();
- Type *tx = NULL;
-
- /* If e->e1 is dynamic array or pointer, the uniqueness of e->e1
- * is equivalent with the uniqueness of the referred data. And in here
- * we can have arbitrary typed reference for that.
- */
- if (t1b->ty == Tarray)
- tx = tbn->arrayOf();
- if (t1b->ty == Tpointer)
- tx = tbn->pointerTo();
-
- /* If e->e1 is static array, at least it should be an rvalue.
- * If not, e->e1 is a reference, and its uniqueness does not link
- * to the uniqueness of the referred data.
- */
- if (t1b->ty == Tsarray && !e->e1->isLvalue())
- tx = tbn->sarrayOf(t1b->size() / tbn->size());
-
- if (tx)
- {
- result = e->e1->implicitConvTo(tx);
- if (result > MATCHconst) // Match level is MATCHconst at best.
- result = MATCHconst;
- }
- }
-
- // Enhancement 10724
- if (tb->ty == Tpointer && e->e1->op == TOKstring)
- e->e1->accept(this);
- }
- };
-
- ImplicitConvTo v(t);
- e->accept(&v);
- return v.result;
-}
-
-Type *toStaticArrayType(SliceExp *e)
-{
- if (e->lwr && e->upr)
- {
- // For the following code to work, e should be optimized beforehand.
- // (eg. $ in lwr and upr should be already resolved, if possible)
- Expression *lwr = e->lwr->optimize(WANTvalue);
- Expression *upr = e->upr->optimize(WANTvalue);
- if (lwr->isConst() && upr->isConst())
- {
- size_t len = (size_t)(upr->toUInteger() - lwr->toUInteger());
- return e->type->toBasetype()->nextOf()->sarrayOf(len);
- }
- }
- else
- {
- Type *t1b = e->e1->type->toBasetype();
- if (t1b->ty == Tsarray)
- return t1b;
- }
- return NULL;
-}
-
-/* ==================== castTo ====================== */
-
-/**************************************
- * Do an explicit cast.
- * Assume that the 'this' expression does not have any indirections.
- */
-
-Expression *castTo(Expression *e, Scope *sc, Type *t)
-{
- class CastTo : public Visitor
- {
- public:
- Type *t;
- Scope *sc;
- Expression *result;
-
- CastTo(Scope *sc, Type *t)
- : t(t), sc(sc)
- {
- result = NULL;
- }
-
- void visit(Expression *e)
- {
- //printf("Expression::castTo(this=%s, t=%s)\n", e->toChars(), t->toChars());
- if (e->type->equals(t))
- {
- result = e;
- return;
- }
- if (e->op == TOKvar)
- {
- VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration();
- if (v && v->storage_class & STCmanifest)
- {
- result = e->ctfeInterpret();
- result = result->castTo(sc, t);
- return;
- }
- }
-
- Type *tob = t->toBasetype();
- Type *t1b = e->type->toBasetype();
- if (tob->equals(t1b))
- {
- result = e->copy(); // because of COW for assignment to e->type
- result->type = t;
- return;
- }
-
- /* Make semantic error against invalid cast between concrete types.
- * Assume that 'e' is never be any placeholder expressions.
- * The result of these checks should be consistent with CastExp::toElem().
- */
-
- // Fat Value types
- const bool tob_isFV = (tob->ty == Tstruct || tob->ty == Tsarray || tob->ty == Tvector);
- const bool t1b_isFV = (t1b->ty == Tstruct || t1b->ty == Tsarray || t1b->ty == Tvector);
-
- // Fat Reference types
- const bool tob_isFR = (tob->ty == Tarray || tob->ty == Tdelegate);
- const bool t1b_isFR = (t1b->ty == Tarray || t1b->ty == Tdelegate);
-
- // Reference types
- const bool tob_isR = (tob_isFR || tob->ty == Tpointer || tob->ty == Taarray || tob->ty == Tclass);
- const bool t1b_isR = (t1b_isFR || t1b->ty == Tpointer || t1b->ty == Taarray || t1b->ty == Tclass);
-
- // Arithmetic types (== valueable basic types)
- const bool tob_isA = ((tob->isintegral() || tob->isfloating()) && tob->ty != Tvector);
- const bool t1b_isA = ((t1b->isintegral() || t1b->isfloating()) && t1b->ty != Tvector);
-
- if (AggregateDeclaration *t1ad = isAggregate(t1b))
- {
- AggregateDeclaration *toad = isAggregate(tob);
- if (t1ad != toad && t1ad->aliasthis)
- {
- if (t1b->ty == Tclass && tob->ty == Tclass)
- {
- ClassDeclaration *t1cd = t1b->isClassHandle();
- ClassDeclaration *tocd = tob->isClassHandle();
- int offset;
- if (tocd->isBaseOf(t1cd, &offset))
- goto Lok;
- }
-
- /* Forward the cast to our alias this member, rewrite to:
- * cast(to)e1.aliasthis
- */
- result = resolveAliasThis(sc, e);
- result = result->castTo(sc, t);
- return;
- }
- }
- else if (tob->ty == Tvector && t1b->ty != Tvector)
- {
- //printf("test1 e = %s, e->type = %s, tob = %s\n", e->toChars(), e->type->toChars(), tob->toChars());
- TypeVector *tv = (TypeVector *)tob;
- result = new CastExp(e->loc, e, tv->elementType());
- result = new VectorExp(e->loc, result, tob);
- result = expressionSemantic(result, sc);
- return;
- }
- else if (tob->ty != Tvector && t1b->ty == Tvector)
- {
- // T[n] <-- __vector(U[m])
- if (tob->ty == Tsarray)
- {
- if (t1b->size(e->loc) == tob->size(e->loc))
- goto Lok;
- }
- goto Lfail;
- }
- else if (t1b->implicitConvTo(tob) == MATCHconst && t->equals(e->type->constOf()))
- {
- result = e->copy();
- result->type = t;
- return;
- }
-
- // arithmetic values vs. other arithmetic values
- // arithmetic values vs. T*
- if ((tob_isA && (t1b_isA || t1b->ty == Tpointer)) ||
- (t1b_isA && (tob_isA || tob->ty == Tpointer)))
- {
- goto Lok;
- }
-
- // arithmetic values vs. references or fat values
- if ((tob_isA && (t1b_isR || t1b_isFV)) ||
- (t1b_isA && (tob_isR || tob_isFV)))
- {
- goto Lfail;
- }
-
- // Bugzlla 3133: A cast between fat values is possible only when the sizes match.
- if (tob_isFV && t1b_isFV)
- {
- if (t1b->size(e->loc) == tob->size(e->loc))
- goto Lok;
- e->error("cannot cast expression %s of type %s to %s because of different sizes",
- e->toChars(), e->type->toChars(), t->toChars());
- result = new ErrorExp();
- return;
- }
-
- // Fat values vs. null or references
- if ((tob_isFV && (t1b->ty == Tnull || t1b_isR)) ||
- (t1b_isFV && (tob->ty == Tnull || tob_isR)))
- {
- if (tob->ty == Tpointer && t1b->ty == Tsarray)
- {
- // T[n] sa;
- // cast(U*)sa; // ==> cast(U*)sa.ptr;
- result = new AddrExp(e->loc, e, t);
- return;
- }
- if (tob->ty == Tarray && t1b->ty == Tsarray)
- {
- // T[n] sa;
- // cast(U[])sa; // ==> cast(U[])sa[];
- d_uns64 fsize = t1b->nextOf()->size();
- d_uns64 tsize = tob->nextOf()->size();
- if (fsize != tsize)
- {
- dinteger_t dim = ((TypeSArray *)t1b)->dim->toInteger();
- if (tsize == 0 || (dim * fsize) % tsize != 0)
- {
- e->error("cannot cast expression `%s` of type `%s` to `%s` since sizes don't line up",
- e->toChars(), e->type->toChars(), t->toChars());
- result = new ErrorExp();
- return;
- }
- }
- goto Lok;
- }
- goto Lfail;
- }
-
- /* For references, any reinterpret casts are allowed to same 'ty' type.
- * T* to U*
- * R1 function(P1) to R2 function(P2)
- * R1 delegate(P1) to R2 delegate(P2)
- * T[] to U[]
- * V1[K1] to V2[K2]
- * class/interface A to B (will be a dynamic cast if possible)
- */
- if (tob->ty == t1b->ty && tob_isR && t1b_isR)
- goto Lok;
-
- // typeof(null) <-- non-null references or values
- if (tob->ty == Tnull && t1b->ty != Tnull)
- goto Lfail; // Bugzilla 14629
- // typeof(null) --> non-null references or arithmetic values
- if (t1b->ty == Tnull && tob->ty != Tnull)
- goto Lok;
-
- // Check size mismatch of references.
- // Tarray and Tdelegate are (void*).sizeof*2, but others have (void*).sizeof.
- if ((tob_isFR && t1b_isR) || (t1b_isFR && tob_isR))
- {
- if (tob->ty == Tpointer && t1b->ty == Tarray)
- {
- // T[] da;
- // cast(U*)da; // ==> cast(U*)da.ptr;
- goto Lok;
- }
- if (tob->ty == Tpointer && t1b->ty == Tdelegate)
- {
- // void delegate() dg;
- // cast(U*)dg; // ==> cast(U*)dg.ptr;
- // Note that it happens even when U is a Tfunction!
- e->deprecation("casting from %s to %s is deprecated", e->type->toChars(), t->toChars());
- goto Lok;
- }
- goto Lfail;
- }
-
- if (t1b->ty == Tvoid && tob->ty != Tvoid)
- {
- Lfail:
- e->error("cannot cast expression %s of type %s to %s",
- e->toChars(), e->type->toChars(), t->toChars());
- result = new ErrorExp();
- return;
- }
-
- Lok:
- result = new CastExp(e->loc, e, t);
- result->type = t; // Don't call semantic()
- //printf("Returning: %s\n", result->toChars());
- }
-
- void visit(ErrorExp *e)
- {
- result = e;
- }
-
- void visit(RealExp *e)
- {
- if (!e->type->equals(t))
- {
- if ((e->type->isreal() && t->isreal()) ||
- (e->type->isimaginary() && t->isimaginary())
- )
- {
- result = e->copy();
- result->type = t;
- }
- else
- visit((Expression *)e);
- return;
- }
- result = e;
- }
-
- void visit(ComplexExp *e)
- {
- if (!e->type->equals(t))
- {
- if (e->type->iscomplex() && t->iscomplex())
- {
- result = e->copy();
- result->type = t;
- }
- else
- visit((Expression *)e);
- return;
- }
- result = e;
- }
-
- void visit(NullExp *e)
- {
- //printf("NullExp::castTo(t = %s) %s\n", t->toChars(), toChars());
- visit((Expression *)e);
- if (result->op == TOKnull)
- {
- NullExp *ex = (NullExp *)result;
- ex->committed = 1;
- return;
- }
- }
-
- void visit(StructLiteralExp *e)
- {
- visit((Expression *)e);
- if (result->op == TOKstructliteral)
- ((StructLiteralExp *)result)->stype = t; // commit type
- }
-
- void visit(StringExp *e)
- {
- /* This follows copy-on-write; any changes to 'this'
- * will result in a copy.
- * The this->string member is considered immutable.
- */
- int copied = 0;
-
- //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t->toChars(), e->toChars(), e->committed);
-
- if (!e->committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid)
- {
- e->error("cannot convert string literal to void*");
- result = new ErrorExp();
- return;
- }
-
- StringExp *se = e;
- if (!e->committed)
- {
- se = (StringExp *)e->copy();
- se->committed = 1;
- copied = 1;
- }
-
- if (e->type->equals(t))
- {
- result = se;
- return;
- }
-
- Type *tb = t->toBasetype();
- //printf("\ttype = %s\n", e->type->toChars());
- if (tb->ty == Tdelegate && e->type->toBasetype()->ty != Tdelegate)
- {
- visit((Expression *)e);
- return;
- }
-
- Type *typeb = e->type->toBasetype();
- if (typeb->equals(tb))
- {
- if (!copied)
- {
- se = (StringExp *)e->copy();
- copied = 1;
- }
- se->type = t;
- result = se;
- return;
- }
-
- /* Handle reinterpret casts:
- * cast(wchar[3])"abcd"c --> [\u6261, \u6463, \u0000]
- * cast(wchar[2])"abcd"c --> [\u6261, \u6463]
- * cast(wchar[1])"abcd"c --> [\u6261]
- */
- if (e->committed && tb->ty == Tsarray && typeb->ty == Tarray)
- {
- se = (StringExp *)e->copy();
- d_uns64 szx = tb->nextOf()->size();
- assert(szx <= 255);
- se->sz = (unsigned char)szx;
- se->len = (size_t)((TypeSArray *)tb)->dim->toInteger();
- se->committed = 1;
- se->type = t;
-
- /* Assure space for terminating 0
- */
- if ((se->len + 1) * se->sz > (e->len + 1) * e->sz)
- {
- void *s = (void *)mem.xmalloc((se->len + 1) * se->sz);
- memcpy(s, se->string, se->len * se->sz);
- memset((char *)s + se->len * se->sz, 0, se->sz);
- se->string = s;
- }
- result = se;
- return;
- }
-
- if (tb->ty != Tsarray && tb->ty != Tarray && tb->ty != Tpointer)
- {
- if (!copied)
- {
- se = (StringExp *)e->copy();
- copied = 1;
- }
- goto Lcast;
- }
- if (typeb->ty != Tsarray && typeb->ty != Tarray && typeb->ty != Tpointer)
- {
- if (!copied)
- {
- se = (StringExp *)e->copy();
- copied = 1;
- }
- goto Lcast;
- }
-
- if (typeb->nextOf()->size() == tb->nextOf()->size())
- {
- if (!copied)
- {
- se = (StringExp *)e->copy();
- copied = 1;
- }
- if (tb->ty == Tsarray)
- goto L2; // handle possible change in static array dimension
- se->type = t;
- result = se;
- return;
- }
-
- if (e->committed)
- goto Lcast;
-
- #define X(tf,tt) ((int)(tf) * 256 + (int)(tt))
- {
- OutBuffer buffer;
- size_t newlen = 0;
- int tfty = typeb->nextOf()->toBasetype()->ty;
- int ttty = tb->nextOf()->toBasetype()->ty;
- switch (X(tfty, ttty))
- {
- case X(Tchar, Tchar):
- case X(Twchar,Twchar):
- case X(Tdchar,Tdchar):
- break;
-
- case X(Tchar, Twchar):
- for (size_t u = 0; u < e->len;)
- {
- unsigned c;
- const char *p = utf_decodeChar((utf8_t *)se->string, e->len, &u, &c);
- if (p)
- e->error("%s", p);
- else
- buffer.writeUTF16(c);
- }
- newlen = buffer.length() / 2;
- buffer.writeUTF16(0);
- goto L1;
-
- case X(Tchar, Tdchar):
- for (size_t u = 0; u < e->len;)
- {
- unsigned c;
- const char *p = utf_decodeChar((utf8_t *)se->string, e->len, &u, &c);
- if (p)
- e->error("%s", p);
- buffer.write4(c);
- newlen++;
- }
- buffer.write4(0);
- goto L1;
-
- case X(Twchar,Tchar):
- for (size_t u = 0; u < e->len;)
- {
- unsigned c;
- const char *p = utf_decodeWchar((unsigned short *)se->string, e->len, &u, &c);
- if (p)
- e->error("%s", p);
- else
- buffer.writeUTF8(c);
- }
- newlen = buffer.length();
- buffer.writeUTF8(0);
- goto L1;
-
- case X(Twchar,Tdchar):
- for (size_t u = 0; u < e->len;)
- {
- unsigned c;
- const char *p = utf_decodeWchar((unsigned short *)se->string, e->len, &u, &c);
- if (p)
- e->error("%s", p);
- buffer.write4(c);
- newlen++;
- }
- buffer.write4(0);
- goto L1;
-
- case X(Tdchar,Tchar):
- for (size_t u = 0; u < e->len; u++)
- {
- unsigned c = ((unsigned *)se->string)[u];
- if (!utf_isValidDchar(c))
- e->error("invalid UCS-32 char \\U%08x", c);
- else
- buffer.writeUTF8(c);
- newlen++;
- }
- newlen = buffer.length();
- buffer.writeUTF8(0);
- goto L1;
-
- case X(Tdchar,Twchar):
- for (size_t u = 0; u < e->len; u++)
- {
- unsigned c = ((unsigned *)se->string)[u];
- if (!utf_isValidDchar(c))
- e->error("invalid UCS-32 char \\U%08x", c);
- else
- buffer.writeUTF16(c);
- newlen++;
- }
- newlen = buffer.length() / 2;
- buffer.writeUTF16(0);
- goto L1;
-
- L1:
- if (!copied)
- {
- se = (StringExp *)e->copy();
- copied = 1;
- }
- se->string = buffer.extractData();
- se->len = newlen;
-
- {
- d_uns64 szx = tb->nextOf()->size();
- assert(szx <= 255);
- se->sz = (unsigned char)szx;
- }
- break;
-
- default:
- assert(typeb->nextOf()->size() != tb->nextOf()->size());
- goto Lcast;
- }
- }
- #undef X
- L2:
- assert(copied);
-
- // See if need to truncate or extend the literal
- if (tb->ty == Tsarray)
- {
- size_t dim2 = (size_t)((TypeSArray *)tb)->dim->toInteger();
-
- //printf("dim from = %d, to = %d\n", (int)se->len, (int)dim2);
-
- // Changing dimensions
- if (dim2 != se->len)
- {
- // Copy when changing the string literal
- size_t newsz = se->sz;
- size_t d = (dim2 < se->len) ? dim2 : se->len;
- void *s = (void *)mem.xmalloc((dim2 + 1) * newsz);
- memcpy(s, se->string, d * newsz);
- // Extend with 0, add terminating 0
- memset((char *)s + d * newsz, 0, (dim2 + 1 - d) * newsz);
- se->string = s;
- se->len = dim2;
- }
- }
- se->type = t;
- result = se;
- return;
-
- Lcast:
- result = new CastExp(e->loc, se, t);
- result->type = t; // so semantic() won't be run on e
- }
-
- void visit(AddrExp *e)
- {
- Type *tb;
-
- result = e;
-
- tb = t->toBasetype();
- e->type = e->type->toBasetype();
- if (!tb->equals(e->type))
- {
- // Look for pointers to functions where the functions are overloaded.
-
- if (e->e1->op == TOKoverloadset &&
- (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction)
- {
- OverExp *eo = (OverExp *)e->e1;
- FuncDeclaration *f = NULL;
- for (size_t i = 0; i < eo->vars->a.length; i++)
- {
- Dsymbol *s = eo->vars->a[i];
- FuncDeclaration *f2 = s->isFuncDeclaration();
- assert(f2);
- if (f2->overloadExactMatch(t->nextOf()))
- {
- if (f)
- {
- /* Error if match in more than one overload set,
- * even if one is a 'better' match than the other.
- */
- ScopeDsymbol::multiplyDefined(e->loc, f, f2);
- }
- else
- f = f2;
- }
- }
- if (f)
- {
- f->tookAddressOf++;
- SymOffExp *se = new SymOffExp(e->loc, f, 0, false);
- expressionSemantic(se, sc);
- // Let SymOffExp::castTo() do the heavy lifting
- visit(se);
- return;
- }
- }
-
- if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction &&
- tb->ty == Tpointer && tb->nextOf()->ty == Tfunction &&
- e->e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)e->e1;
- FuncDeclaration *f = ve->var->isFuncDeclaration();
- if (f)
- {
- assert(f->isImportedSymbol());
- f = f->overloadExactMatch(tb->nextOf());
- if (f)
- {
- result = new VarExp(e->loc, f, false);
- result->type = f->type;
- result = new AddrExp(e->loc, result, t);
- return;
- }
- }
- }
-
- if (FuncDeclaration *f = isFuncAddress(e))
- {
- if (f->checkForwardRef(e->loc))
- {
- result = new ErrorExp();
- return;
- }
- }
-
- visit((Expression *)e);
- }
- result->type = t;
- }
-
- void visit(TupleExp *e)
- {
- if (e->type->equals(t))
- {
- result = e;
- return;
- }
-
- TupleExp *te = (TupleExp *)e->copy();
- te->e0 = e->e0 ? e->e0->copy() : NULL;
- te->exps = (Expressions *)e->exps->copy();
- for (size_t i = 0; i < te->exps->length; i++)
- {
- Expression *ex = (*te->exps)[i];
- ex = ex->castTo(sc, t);
- (*te->exps)[i] = ex;
- }
- result = te;
-
- /* Questionable behavior: In here, result->type is not set to t.
- * Therefoe:
- * TypeTuple!(int, int) values;
- * auto values2 = cast(long)values;
- * // typeof(values2) == TypeTuple!(int, int) !!
- *
- * Only when the casted tuple is immediately expanded, it would work.
- * auto arr = [cast(long)values];
- * // typeof(arr) == long[]
- */
- }
-
- void visit(ArrayLiteralExp *e)
- {
- if (e->type == t)
- {
- result = e;
- return;
- }
- ArrayLiteralExp *ae = e;
- Type *typeb = e->type->toBasetype();
- Type *tb = t->toBasetype();
- if ((tb->ty == Tarray || tb->ty == Tsarray) &&
- (typeb->ty == Tarray || typeb->ty == Tsarray))
- {
- if (tb->nextOf()->toBasetype()->ty == Tvoid && typeb->nextOf()->toBasetype()->ty != Tvoid)
- {
- // Don't do anything to cast non-void[] to void[]
- }
- else if (typeb->ty == Tsarray && typeb->nextOf()->toBasetype()->ty == Tvoid)
- {
- // Don't do anything for casting void[n] to others
- }
- else
- {
- if (tb->ty == Tsarray)
- {
- TypeSArray *tsa = (TypeSArray *)tb;
- if (e->elements->length != tsa->dim->toInteger())
- goto L1;
- }
-
- ae = (ArrayLiteralExp *)e->copy();
- if (e->basis)
- ae->basis = e->basis->castTo(sc, tb->nextOf());
- ae->elements = e->elements->copy();
- for (size_t i = 0; i < e->elements->length; i++)
- {
- Expression *ex = (*e->elements)[i];
- if (!ex)
- continue;
- ex = ex->castTo(sc, tb->nextOf());
- (*ae->elements)[i] = ex;
- }
- ae->type = t;
- result = ae;
- return;
- }
- }
- else if (tb->ty == Tpointer && typeb->ty == Tsarray)
- {
- Type *tp = typeb->nextOf()->pointerTo();
- if (!tp->equals(ae->type))
- {
- ae = (ArrayLiteralExp *)e->copy();
- ae->type = tp;
- }
- }
- else if (tb->ty == Tvector &&
- (typeb->ty == Tarray || typeb->ty == Tsarray))
- {
- // Convert array literal to vector type
- TypeVector *tv = (TypeVector *)tb;
- TypeSArray *tbase = (TypeSArray *)tv->basetype;
- assert(tbase->ty == Tsarray);
- const size_t edim = e->elements->length;
- const size_t tbasedim = tbase->dim->toInteger();
- if (edim > tbasedim)
- goto L1;
-
- ae = (ArrayLiteralExp *)e->copy();
- ae->type = tbase; // Bugzilla 12642
- ae->elements = e->elements->copy();
- Type *telement = tv->elementType();
- for (size_t i = 0; i < edim; i++)
- {
- Expression *ex = (*e->elements)[i];
- ex = ex->castTo(sc, telement);
- (*ae->elements)[i] = ex;
- }
- // Fill in the rest with the default initializer
- ae->elements->setDim(tbasedim);
- for (size_t i = edim; i < tbasedim; i++)
- {
- Expression *ex = typeb->nextOf()->defaultInitLiteral(e->loc);
- ex = ex->castTo(sc, telement);
- (*ae->elements)[i] = ex;
- }
- Expression *ev = new VectorExp(e->loc, ae, tb);
- ev = expressionSemantic(ev, sc);
- result = ev;
- return;
- }
- L1:
- visit((Expression *)ae);
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- if (e->type == t)
- {
- result = e;
- return;
- }
- Type *typeb = e->type->toBasetype();
- Type *tb = t->toBasetype();
- if (tb->ty == Taarray && typeb->ty == Taarray &&
- tb->nextOf()->toBasetype()->ty != Tvoid)
- {
- AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e->copy();
- ae->keys = e->keys->copy();
- ae->values = e->values->copy();
- assert(e->keys->length == e->values->length);
- for (size_t i = 0; i < e->keys->length; i++)
- {
- Expression *ex = (*e->values)[i];
- ex = ex->castTo(sc, tb->nextOf());
- (*ae->values)[i] = ex;
-
- ex = (*e->keys)[i];
- ex = ex->castTo(sc, ((TypeAArray *)tb)->index);
- (*ae->keys)[i] = ex;
- }
- ae->type = t;
- result = ae;
- return;
- }
- visit((Expression *)e);
- }
-
- void visit(SymOffExp *e)
- {
- if (e->type == t && !e->hasOverloads)
- {
- result = e;
- return;
- }
- Type *tb = t->toBasetype();
- Type *typeb = e->type->toBasetype();
-
- if (tb->equals(typeb))
- {
- result = e->copy();
- result->type = t;
- ((SymOffExp *)result)->hasOverloads = false;
- return;
- }
-
- // Look for pointers to functions where the functions are overloaded.
- if (e->hasOverloads &&
- typeb->ty == Tpointer && typeb->nextOf()->ty == Tfunction &&
- (tb->ty == Tpointer || tb->ty == Tdelegate) && tb->nextOf()->ty == Tfunction)
- {
- FuncDeclaration *f = e->var->isFuncDeclaration();
- f = f ? f->overloadExactMatch(tb->nextOf()) : NULL;
- if (f)
- {
- if (tb->ty == Tdelegate)
- {
- if (f->needThis() && hasThis(sc))
- {
- result = new DelegateExp(e->loc, new ThisExp(e->loc), f, false);
- result = expressionSemantic(result, sc);
- }
- else if (f->isNested())
- {
- result = new DelegateExp(e->loc, new IntegerExp(0), f, false);
- result = expressionSemantic(result, sc);
- }
- else if (f->needThis())
- {
- e->error("no `this` to create delegate for %s", f->toChars());
- result = new ErrorExp();
- return;
- }
- else
- {
- e->error("cannot cast from function pointer to delegate");
- result = new ErrorExp();
- return;
- }
- }
- else
- {
- result = new SymOffExp(e->loc, f, 0, false);
- result->type = t;
- }
- f->tookAddressOf++;
- return;
- }
- }
-
- if (FuncDeclaration *f = isFuncAddress(e))
- {
- if (f->checkForwardRef(e->loc))
- {
- result = new ErrorExp();
- return;
- }
- }
-
- visit((Expression *)e);
- }
-
- void visit(DelegateExp *e)
- {
- static const char msg[] = "cannot form delegate due to covariant return type";
-
- Type *tb = t->toBasetype();
- Type *typeb = e->type->toBasetype();
- if (!tb->equals(typeb) || e->hasOverloads)
- {
- // Look for delegates to functions where the functions are overloaded.
- if (typeb->ty == Tdelegate &&
- tb->ty == Tdelegate)
- {
- if (e->func)
- {
- FuncDeclaration *f = e->func->overloadExactMatch(tb->nextOf());
- if (f)
- {
- int offset;
- if (f->tintro && f->tintro->nextOf()->isBaseOf(f->type->nextOf(), &offset) && offset)
- e->error("%s", msg);
- if (f != e->func) // if address not already marked as taken
- f->tookAddressOf++;
- result = new DelegateExp(e->loc, e->e1, f, false);
- result->type = t;
- return;
- }
- if (e->func->tintro)
- e->error("%s", msg);
- }
- }
-
- if (FuncDeclaration *f = isFuncAddress(e))
- {
- if (f->checkForwardRef(e->loc))
- {
- result = new ErrorExp();
- return;
- }
- }
-
- visit((Expression *)e);
- }
- else
- {
- int offset;
- e->func->tookAddressOf++;
- if (e->func->tintro && e->func->tintro->nextOf()->isBaseOf(e->func->type->nextOf(), &offset) && offset)
- e->error("%s", msg);
- result = e->copy();
- result->type = t;
- }
- }
-
- void visit(FuncExp *e)
- {
- //printf("FuncExp::castTo type = %s, t = %s\n", e->type->toChars(), t->toChars());
- FuncExp *fe;
- if (e->matchType(t, sc, &fe, 1) > MATCHnomatch)
- {
- result = fe;
- return;
- }
- visit((Expression *)e);
- }
-
- void visit(CondExp *e)
- {
- if (!e->type->equals(t))
- {
- result = new CondExp(e->loc, e->econd, e->e1->castTo(sc, t), e->e2->castTo(sc, t));
- result->type = t;
- return;
- }
- result = e;
- }
-
- void visit(CommaExp *e)
- {
- Expression *e2c = e->e2->castTo(sc, t);
-
- if (e2c != e->e2)
- {
- result = new CommaExp(e->loc, e->e1, e2c);
- result->type = e2c->type;
- }
- else
- {
- result = e;
- result->type = e->e2->type;
- }
- }
-
- void visit(SliceExp *e)
- {
- //printf("SliceExp::castTo e = %s, type = %s, t = %s\n", e->toChars(), e->type->toChars(), t->toChars());
- Type *typeb = e->type->toBasetype();
- Type *tb = t->toBasetype();
- if (e->type->equals(t) || typeb->ty != Tarray ||
- (tb->ty != Tarray && tb->ty != Tsarray))
- {
- visit((Expression *)e);
- return;
- }
-
- if (tb->ty == Tarray)
- {
- if (typeb->nextOf()->equivalent(tb->nextOf()))
- {
- // T[] to const(T)[]
- result = e->copy();
- result->type = t;
- }
- else
- {
- visit((Expression *)e);
- }
- return;
- }
-
- // Handle the cast from Tarray to Tsarray with CT-known slicing
-
- TypeSArray *tsa = (TypeSArray *)toStaticArrayType(e);
- if (tsa && tsa->size(e->loc) == tb->size(e->loc))
- {
- /* Match if the sarray sizes are equal:
- * T[a .. b] to const(T)[b-a]
- * T[a .. b] to U[dim] if (T.sizeof*(b-a) == U.sizeof*dim)
- *
- * If a SliceExp has Tsarray, it will become lvalue.
- * That's handled in SliceExp::isLvalue and toLvalue
- */
- result = e->copy();
- result->type = t;
- return;
- }
- if (tsa && tsa->dim->equals(((TypeSArray *)tb)->dim))
- {
- /* Match if the dimensions are equal
- * with the implicit conversion of e->e1:
- * cast(float[2]) [2.0, 1.0, 0.0][0..2];
- */
- Type *t1b = e->e1->type->toBasetype();
- if (t1b->ty == Tsarray)
- t1b = tb->nextOf()->sarrayOf(((TypeSArray *)t1b)->dim->toInteger());
- else if (t1b->ty == Tarray)
- t1b = tb->nextOf()->arrayOf();
- else if (t1b->ty == Tpointer)
- t1b = tb->nextOf()->pointerTo();
- else
- assert(0);
- if (e->e1->implicitConvTo(t1b) > MATCHnomatch)
- {
- Expression *e1x = e->e1->implicitCastTo(sc, t1b);
- assert(e1x->op != TOKerror);
- e = (SliceExp *)e->copy();
- e->e1 = e1x;
- e->type = t;
- result = e;
- return;
- }
- }
- e->error("cannot cast expression %s of type %s to %s",
- e->toChars(), tsa ? tsa->toChars() : e->type->toChars(),
- t->toChars());
- result = new ErrorExp();
- }
- };
-
- CastTo v(sc, t);
- e->accept(&v);
- return v.result;
-}
-
-/* ==================== inferType ====================== */
-
-/****************************************
- * Set type inference target
- * t Target type
- * flag 1: don't put an error when inference fails
- */
-
-Expression *inferType(Expression *e, Type *t, int flag)
-{
- class InferType : public Visitor
- {
- public:
- Type *t;
- int flag;
- Expression *result;
-
- InferType(Type *t, int flag)
- : t(t), flag(flag)
- {
- result = NULL;
- }
-
-
- void visit(Expression *e)
- {
- result = e;
- }
-
- void visit(ArrayLiteralExp *ale)
- {
- Type *tb = t->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- Type *tn = tb->nextOf();
- if (ale->basis)
- ale->basis = inferType(ale->basis, tn, flag);
- for (size_t i = 0; i < ale->elements->length; i++)
- {
- Expression *e = (*ale->elements)[i];
- if (e)
- {
- e = inferType(e, tn, flag);
- (*ale->elements)[i] = e;
- }
- }
- }
- result = ale;
- }
-
- void visit(AssocArrayLiteralExp *aale)
- {
- Type *tb = t->toBasetype();
- if (tb->ty == Taarray)
- {
- TypeAArray *taa = (TypeAArray *)tb;
- Type *ti = taa->index;
- Type *tv = taa->nextOf();
- for (size_t i = 0; i < aale->keys->length; i++)
- {
- Expression *e = (*aale->keys)[i];
- if (e)
- {
- e = inferType(e, ti, flag);
- (*aale->keys)[i] = e;
- }
- }
- for (size_t i = 0; i < aale->values->length; i++)
- {
- Expression *e = (*aale->values)[i];
- if (e)
- {
- e = inferType(e, tv, flag);
- (*aale->values)[i] = e;
- }
- }
- }
- result = aale;
- }
-
- void visit(FuncExp *fe)
- {
- //printf("FuncExp::inferType('%s'), to=%s\n", fe->type ? fe->type->toChars() : "null", t->toChars());
- if (t->ty == Tdelegate ||
- (t->ty == Tpointer && t->nextOf()->ty == Tfunction))
- {
- fe->fd->treq = t;
- }
- result = fe;
- }
-
- void visit(CondExp *ce)
- {
- Type *tb = t->toBasetype();
- ce->e1 = inferType(ce->e1, tb, flag);
- ce->e2 = inferType(ce->e2, tb, flag);
- result = ce;
- }
- };
-
- if (!t)
- return e;
-
- InferType v(t, flag);
- e->accept(&v);
- return v.result;
-}
-
-/* ==================== ====================== */
-
-/****************************************
- * Scale addition/subtraction to/from pointer.
- */
-
-Expression *scaleFactor(BinExp *be, Scope *sc)
-{
- Type *t1b = be->e1->type->toBasetype();
- Type *t2b = be->e2->type->toBasetype();
- Expression *eoff;
-
- if (t1b->ty == Tpointer && t2b->isintegral())
- {
- // Need to adjust operator by the stride
- // Replace (ptr + int) with (ptr + (int * stride))
- Type *t = Type::tptrdiff_t;
-
- d_uns64 stride = t1b->nextOf()->size(be->loc);
- if (!t->equals(t2b))
- be->e2 = be->e2->castTo(sc, t);
- eoff = be->e2;
- be->e2 = new MulExp(be->loc, be->e2, new IntegerExp(Loc(), stride, t));
- be->e2->type = t;
- be->type = be->e1->type;
- }
- else if (t2b->ty == Tpointer && t1b->isintegral())
- {
- // Need to adjust operator by the stride
- // Replace (int + ptr) with (ptr + (int * stride))
- Type *t = Type::tptrdiff_t;
- Expression *e;
-
- d_uns64 stride = t2b->nextOf()->size(be->loc);
- if (!t->equals(t1b))
- e = be->e1->castTo(sc, t);
- else
- e = be->e1;
- eoff = e;
- e = new MulExp(be->loc, e, new IntegerExp(Loc(), stride, t));
- e->type = t;
- be->type = be->e2->type;
- be->e1 = be->e2;
- be->e2 = e;
- }
- else
- assert(0);
-
- if (sc->func && !sc->intypeof)
- {
- eoff = eoff->optimize(WANTvalue);
- if (eoff->op == TOKint64 && eoff->toInteger() == 0)
- ;
- else if (sc->func->setUnsafe())
- {
- be->error("pointer arithmetic not allowed in @safe functions");
- return new ErrorExp();
- }
- }
-
- return be;
-}
-
-/**************************************
- * Return true if e is an empty array literal with dimensionality
- * equal to or less than type of other array.
- * [], [[]], [[[]]], etc.
- * I.e., make sure that [1,2] is compatible with [],
- * [[1,2]] is compatible with [[]], etc.
- */
-bool isVoidArrayLiteral(Expression *e, Type *other)
-{
- while (e->op == TOKarrayliteral && e->type->ty == Tarray
- && (((ArrayLiteralExp *)e)->elements->length == 1))
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
- e = ale->getElement(0);
- if (other->ty == Tsarray || other->ty == Tarray)
- other = other->nextOf();
- else
- return false;
- }
- if (other->ty != Tsarray && other->ty != Tarray)
- return false;
- Type *t = e->type;
- return (e->op == TOKarrayliteral && t->ty == Tarray &&
- t->nextOf()->ty == Tvoid &&
- ((ArrayLiteralExp *)e)->elements->length == 0);
-}
-
-// used by deduceType()
-Type *rawTypeMerge(Type *t1, Type *t2)
-{
- if (t1->equals(t2))
- return t1;
- if (t1->equivalent(t2))
- return t1->castMod(MODmerge(t1->mod, t2->mod));
-
- Type *t1b = t1->toBasetype();
- Type *t2b = t2->toBasetype();
- if (t1b->equals(t2b))
- return t1b;
- if (t1b->equivalent(t2b))
- return t1b->castMod(MODmerge(t1b->mod, t2b->mod));
-
- TY ty = (TY)impcnvResult[t1b->ty][t2b->ty];
- if (ty != Terror)
- return Type::basic[ty];
-
- return NULL;
-}
-
-/**************************************
- * Combine types.
- * Output:
- * *pt merged type, if *pt is not NULL
- * *pe1 rewritten e1
- * *pe2 rewritten e2
- * Returns:
- * true success
- * false failed
- */
-
-bool typeMerge(Scope *sc, TOK op, Type **pt, Expression **pe1, Expression **pe2)
-{
- //printf("typeMerge() %s op %s\n", (*pe1)->toChars(), (*pe2)->toChars());
-
- MATCH m;
- Expression *e1 = *pe1;
- Expression *e2 = *pe2;
- Type *t1b = e1->type->toBasetype();
- Type *t2b = e2->type->toBasetype();
-
- if (op != TOKquestion ||
- (t1b->ty != t2b->ty && (t1b->isTypeBasic() && t2b->isTypeBasic())))
- {
- e1 = integralPromotions(e1, sc);
- e2 = integralPromotions(e2, sc);
- }
-
- Type *t1 = e1->type;
- Type *t2 = e2->type;
- assert(t1);
- Type *t = t1;
-
- /* The start type of alias this type recursion.
- * In following case, we should save A, and stop recursion
- * if it appears again.
- * X -> Y -> [A] -> B -> A -> B -> ...
- */
- Type *att1 = NULL;
- Type *att2 = NULL;
-
- //if (t1) printf("\tt1 = %s\n", t1->toChars());
- //if (t2) printf("\tt2 = %s\n", t2->toChars());
- assert(t2);
-
- if (t1->mod != t2->mod &&
- t1->ty == Tenum && t2->ty == Tenum &&
- ((TypeEnum *)t1)->sym == ((TypeEnum *)t2)->sym)
- {
- unsigned char mod = MODmerge(t1->mod, t2->mod);
- t1 = t1->castMod(mod);
- t2 = t2->castMod(mod);
- }
-
-Lagain:
- t1b = t1->toBasetype();
- t2b = t2->toBasetype();
-
- TY ty = (TY)impcnvResult[t1b->ty][t2b->ty];
- if (ty != Terror)
- {
- TY ty1 = (TY)impcnvType1[t1b->ty][t2b->ty];
- TY ty2 = (TY)impcnvType2[t1b->ty][t2b->ty];
-
- if (t1b->ty == ty1) // if no promotions
- {
- if (t1->equals(t2))
- {
- t = t1;
- goto Lret;
- }
-
- if (t1b->equals(t2b))
- {
- t = t1b;
- goto Lret;
- }
- }
-
- t = Type::basic[ty];
-
- t1 = Type::basic[ty1];
- t2 = Type::basic[ty2];
- e1 = e1->castTo(sc, t1);
- e2 = e2->castTo(sc, t2);
- //printf("after typeCombine():\n");
- //print();
- //printf("ty = %d, ty1 = %d, ty2 = %d\n", ty, ty1, ty2);
- goto Lret;
- }
-
- t1 = t1b;
- t2 = t2b;
-
- if (t1->ty == Ttuple || t2->ty == Ttuple)
- goto Lincompatible;
-
- if (t1->equals(t2))
- {
- // merging can not result in new enum type
- if (t->ty == Tenum)
- t = t1b;
- }
- else if ((t1->ty == Tpointer && t2->ty == Tpointer) ||
- (t1->ty == Tdelegate && t2->ty == Tdelegate))
- {
- // Bring pointers to compatible type
- Type *t1n = t1->nextOf();
- Type *t2n = t2->nextOf();
-
- if (t1n->equals(t2n))
- ;
- else if (t1n->ty == Tvoid) // pointers to void are always compatible
- t = t2;
- else if (t2n->ty == Tvoid)
- ;
- else if (t1->implicitConvTo(t2))
- {
- goto Lt2;
- }
- else if (t2->implicitConvTo(t1))
- {
- goto Lt1;
- }
- else if (t1n->ty == Tfunction && t2n->ty == Tfunction)
- {
- TypeFunction *tf1 = (TypeFunction *)t1n;
- TypeFunction *tf2 = (TypeFunction *)t2n;
- tf1->purityLevel();
- tf2->purityLevel();
-
- TypeFunction *d = (TypeFunction *)tf1->syntaxCopy();
-
- if (tf1->purity != tf2->purity)
- d->purity = PUREimpure;
- assert(d->purity != PUREfwdref);
-
- d->isnothrow = (tf1->isnothrow && tf2->isnothrow);
- d->isnogc = (tf1->isnogc && tf2->isnogc);
-
- if (tf1->trust == tf2->trust)
- d->trust = tf1->trust;
- else if (tf1->trust <= TRUSTsystem || tf2->trust <= TRUSTsystem)
- d->trust = TRUSTsystem;
- else
- d->trust = TRUSTtrusted;
-
- Type *tx = NULL;
- if (t1->ty == Tdelegate)
- {
- tx = new TypeDelegate(d);
- }
- else
- tx = d->pointerTo();
-
- tx = typeSemantic(tx, e1->loc, sc);
-
- if (t1->implicitConvTo(tx) && t2->implicitConvTo(tx))
- {
- t = tx;
- e1 = e1->castTo(sc, t);
- e2 = e2->castTo(sc, t);
- goto Lret;
- }
- goto Lincompatible;
- }
- else if (t1n->mod != t2n->mod)
- {
- if (!t1n->isImmutable() && !t2n->isImmutable() && t1n->isShared() != t2n->isShared())
- goto Lincompatible;
- unsigned char mod = MODmerge(t1n->mod, t2n->mod);
- t1 = t1n->castMod(mod)->pointerTo();
- t2 = t2n->castMod(mod)->pointerTo();
- t = t1;
- goto Lagain;
- }
- else if (t1n->ty == Tclass && t2n->ty == Tclass)
- {
- ClassDeclaration *cd1 = t1n->isClassHandle();
- ClassDeclaration *cd2 = t2n->isClassHandle();
- int offset;
-
- if (cd1->isBaseOf(cd2, &offset))
- {
- if (offset)
- e2 = e2->castTo(sc, t);
- }
- else if (cd2->isBaseOf(cd1, &offset))
- {
- t = t2;
- if (offset)
- e1 = e1->castTo(sc, t);
- }
- else
- goto Lincompatible;
- }
- else
- {
- t1 = t1n->constOf()->pointerTo();
- t2 = t2n->constOf()->pointerTo();
- if (t1->implicitConvTo(t2))
- {
- goto Lt2;
- }
- else if (t2->implicitConvTo(t1))
- {
- goto Lt1;
- }
- goto Lincompatible;
- }
- }
- else if ((t1->ty == Tsarray || t1->ty == Tarray) &&
- ((e2->op == TOKnull && t2->ty == Tpointer && t2->nextOf()->ty == Tvoid) ||
- (e2->op == TOKarrayliteral && t2->ty == Tsarray && t2->nextOf()->ty == Tvoid && ((TypeSArray *)t2)->dim->toInteger() == 0) ||
- (isVoidArrayLiteral(e2, t1)))
- )
- {
- /* (T[n] op void*) => T[]
- * (T[] op void*) => T[]
- * (T[n] op void[0]) => T[]
- * (T[] op void[0]) => T[]
- * (T[n] op void[]) => T[]
- * (T[] op void[]) => T[]
- */
- goto Lx1;
- }
- else if ((t2->ty == Tsarray || t2->ty == Tarray) &&
- ((e1->op == TOKnull && t1->ty == Tpointer && t1->nextOf()->ty == Tvoid) ||
- (e1->op == TOKarrayliteral && t1->ty == Tsarray && t1->nextOf()->ty == Tvoid && ((TypeSArray *)t1)->dim->toInteger() == 0) ||
- (isVoidArrayLiteral(e1, t2)))
- )
- {
- /* (void* op T[n]) => T[]
- * (void* op T[]) => T[]
- * (void[0] op T[n]) => T[]
- * (void[0] op T[]) => T[]
- * (void[] op T[n]) => T[]
- * (void[] op T[]) => T[]
- */
- goto Lx2;
- }
- else if ((t1->ty == Tsarray || t1->ty == Tarray) &&
- (m = t1->implicitConvTo(t2)) != MATCHnomatch)
- {
- // Bugzilla 7285: Tsarray op [x, y, ...] should to be Tsarray
- // Bugzilla 14737: Tsarray ~ [x, y, ...] should to be Tarray
- if (t1->ty == Tsarray && e2->op == TOKarrayliteral && op != TOKcat)
- goto Lt1;
- if (m == MATCHconst &&
- (op == TOKaddass || op == TOKminass || op == TOKmulass ||
- op == TOKdivass || op == TOKmodass || op == TOKpowass ||
- op == TOKandass || op == TOKorass || op == TOKxorass)
- )
- {
- // Don't make the lvalue const
- t = t2;
- goto Lret;
- }
- goto Lt2;
- }
- else if ((t2->ty == Tsarray || t2->ty == Tarray) && t2->implicitConvTo(t1))
- {
- // Bugzilla 7285 & 14737
- if (t2->ty == Tsarray && e1->op == TOKarrayliteral && op != TOKcat)
- goto Lt2;
- goto Lt1;
- }
- else if ((t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Tpointer) &&
- (t2->ty == Tsarray || t2->ty == Tarray || t2->ty == Tpointer) &&
- t1->nextOf()->mod != t2->nextOf()->mod
- )
- {
- /* If one is mutable and the other invariant, then retry
- * with both of them as const
- */
- Type *t1n = t1->nextOf();
- Type *t2n = t2->nextOf();
- unsigned char mod;
- if (e1->op == TOKnull && e2->op != TOKnull)
- mod = t2n->mod;
- else if (e1->op != TOKnull && e2->op == TOKnull)
- mod = t1n->mod;
- else if (!t1n->isImmutable() && !t2n->isImmutable() && t1n->isShared() != t2n->isShared())
- goto Lincompatible;
- else
- mod = MODmerge(t1n->mod, t2n->mod);
-
- if (t1->ty == Tpointer)
- t1 = t1n->castMod(mod)->pointerTo();
- else
- t1 = t1n->castMod(mod)->arrayOf();
-
- if (t2->ty == Tpointer)
- t2 = t2n->castMod(mod)->pointerTo();
- else
- t2 = t2n->castMod(mod)->arrayOf();
- t = t1;
- goto Lagain;
- }
- else if (t1->ty == Tclass && t2->ty == Tclass)
- {
- if (t1->mod != t2->mod)
- {
- unsigned char mod;
- if (e1->op == TOKnull && e2->op != TOKnull)
- mod = t2->mod;
- else if (e1->op != TOKnull && e2->op == TOKnull)
- mod = t1->mod;
- else if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared())
- goto Lincompatible;
- else
- mod = MODmerge(t1->mod, t2->mod);
- t1 = t1->castMod(mod);
- t2 = t2->castMod(mod);
- t = t1;
- goto Lagain;
- }
- goto Lcc;
- }
- else if (t1->ty == Tclass || t2->ty == Tclass)
- {
-Lcc:
- while (1)
- {
- MATCH i1 = e2->implicitConvTo(t1);
- MATCH i2 = e1->implicitConvTo(t2);
-
- if (i1 && i2)
- {
- // We have the case of class vs. void*, so pick class
- if (t1->ty == Tpointer)
- i1 = MATCHnomatch;
- else if (t2->ty == Tpointer)
- i2 = MATCHnomatch;
- }
-
- if (i2)
- {
- e2 = e2->castTo(sc, t2);
- goto Lt2;
- }
- else if (i1)
- {
- e1 = e1->castTo(sc, t1);
- goto Lt1;
- }
- else if (t1->ty == Tclass && t2->ty == Tclass)
- {
- TypeClass *tc1 = (TypeClass *)t1;
- TypeClass *tc2 = (TypeClass *)t2;
-
- /* Pick 'tightest' type
- */
- ClassDeclaration *cd1 = tc1->sym->baseClass;
- ClassDeclaration *cd2 = tc2->sym->baseClass;
-
- if (cd1 && cd2)
- {
- t1 = cd1->type->castMod(t1->mod);
- t2 = cd2->type->castMod(t2->mod);
- }
- else if (cd1)
- t1 = cd1->type;
- else if (cd2)
- t2 = cd2->type;
- else
- goto Lincompatible;
- }
- else if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis)
- {
- if (att1 && e1->type == att1)
- goto Lincompatible;
- if (!att1 && e1->type->checkAliasThisRec())
- att1 = e1->type;
- //printf("att tmerge(c || c) e1 = %s\n", e1->type->toChars());
- e1 = resolveAliasThis(sc, e1);
- t1 = e1->type;
- continue;
- }
- else if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis)
- {
- if (att2 && e2->type == att2)
- goto Lincompatible;
- if (!att2 && e2->type->checkAliasThisRec())
- att2 = e2->type;
- //printf("att tmerge(c || c) e2 = %s\n", e2->type->toChars());
- e2 = resolveAliasThis(sc, e2);
- t2 = e2->type;
- continue;
- }
- else
- goto Lincompatible;
- }
- }
- else if (t1->ty == Tstruct && t2->ty == Tstruct)
- {
- if (t1->mod != t2->mod)
- {
- if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared())
- goto Lincompatible;
- unsigned char mod = MODmerge(t1->mod, t2->mod);
- t1 = t1->castMod(mod);
- t2 = t2->castMod(mod);
- t = t1;
- goto Lagain;
- }
-
- TypeStruct *ts1 = (TypeStruct *)t1;
- TypeStruct *ts2 = (TypeStruct *)t2;
- if (ts1->sym != ts2->sym)
- {
- if (!ts1->sym->aliasthis && !ts2->sym->aliasthis)
- goto Lincompatible;
-
- MATCH i1 = MATCHnomatch;
- MATCH i2 = MATCHnomatch;
-
- Expression *e1b = NULL;
- Expression *e2b = NULL;
- if (ts2->sym->aliasthis)
- {
- if (att2 && e2->type == att2)
- goto Lincompatible;
- if (!att2 && e2->type->checkAliasThisRec())
- att2 = e2->type;
- //printf("att tmerge(s && s) e2 = %s\n", e2->type->toChars());
- e2b = resolveAliasThis(sc, e2);
- i1 = e2b->implicitConvTo(t1);
- }
- if (ts1->sym->aliasthis)
- {
- if (att1 && e1->type == att1)
- goto Lincompatible;
- if (!att1 && e1->type->checkAliasThisRec())
- att1 = e1->type;
- //printf("att tmerge(s && s) e1 = %s\n", e1->type->toChars());
- e1b = resolveAliasThis(sc, e1);
- i2 = e1b->implicitConvTo(t2);
- }
- if (i1 && i2)
- goto Lincompatible;
-
- if (i1)
- goto Lt1;
- else if (i2)
- goto Lt2;
-
- if (e1b)
- {
- e1 = e1b;
- t1 = e1b->type->toBasetype();
- }
- if (e2b)
- {
- e2 = e2b;
- t2 = e2b->type->toBasetype();
- }
- t = t1;
- goto Lagain;
- }
- }
- else if (t1->ty == Tstruct || t2->ty == Tstruct)
- {
- if (t1->ty == Tstruct && ((TypeStruct *)t1)->sym->aliasthis)
- {
- if (att1 && e1->type == att1)
- goto Lincompatible;
- if (!att1 && e1->type->checkAliasThisRec())
- att1 = e1->type;
- //printf("att tmerge(s || s) e1 = %s\n", e1->type->toChars());
- e1 = resolveAliasThis(sc, e1);
- t1 = e1->type;
- t = t1;
- goto Lagain;
- }
- if (t2->ty == Tstruct && ((TypeStruct *)t2)->sym->aliasthis)
- {
- if (att2 && e2->type == att2)
- goto Lincompatible;
- if (!att2 && e2->type->checkAliasThisRec())
- att2 = e2->type;
- //printf("att tmerge(s || s) e2 = %s\n", e2->type->toChars());
- e2 = resolveAliasThis(sc, e2);
- t2 = e2->type;
- t = t2;
- goto Lagain;
- }
- goto Lincompatible;
- }
- else if ((e1->op == TOKstring || e1->op == TOKnull) && e1->implicitConvTo(t2))
- {
- goto Lt2;
- }
- else if ((e2->op == TOKstring || e2->op == TOKnull) && e2->implicitConvTo(t1))
- {
- goto Lt1;
- }
- else if (t1->ty == Tsarray && t2->ty == Tsarray &&
- e2->implicitConvTo(t1->nextOf()->arrayOf()))
- {
- Lx1:
- t = t1->nextOf()->arrayOf(); // T[]
- e1 = e1->castTo(sc, t);
- e2 = e2->castTo(sc, t);
- }
- else if (t1->ty == Tsarray && t2->ty == Tsarray &&
- e1->implicitConvTo(t2->nextOf()->arrayOf()))
- {
- Lx2:
- t = t2->nextOf()->arrayOf();
- e1 = e1->castTo(sc, t);
- e2 = e2->castTo(sc, t);
- }
- else if (t1->ty == Tvector && t2->ty == Tvector)
- {
- // Bugzilla 13841, all vector types should have no common types between
- // different vectors, even though their sizes are same.
- TypeVector *tv1 = (TypeVector *)t1;
- TypeVector *tv2 = (TypeVector *)t2;
- if (!tv1->basetype->equals(tv2->basetype))
- goto Lincompatible;
-
- goto LmodCompare;
- }
- else if (t1->ty == Tvector && t2->ty != Tvector &&
- e2->implicitConvTo(t1))
- {
- e2 = e2->castTo(sc, t1);
- t2 = t1;
- t = t1;
- goto Lagain;
- }
- else if (t2->ty == Tvector && t1->ty != Tvector &&
- e1->implicitConvTo(t2))
- {
- e1 = e1->castTo(sc, t2);
- t1 = t2;
- t = t1;
- goto Lagain;
- }
- else if (t1->isintegral() && t2->isintegral())
- {
- if (t1->ty != t2->ty)
- {
- if (t1->ty == Tvector || t2->ty == Tvector)
- goto Lincompatible;
- e1 = integralPromotions(e1, sc);
- e2 = integralPromotions(e2, sc);
- t1 = e1->type;
- t2 = e2->type;
- goto Lagain;
- }
- assert(t1->ty == t2->ty);
-LmodCompare:
- if (!t1->isImmutable() && !t2->isImmutable() && t1->isShared() != t2->isShared())
- goto Lincompatible;
- unsigned char mod = MODmerge(t1->mod, t2->mod);
-
- t1 = t1->castMod(mod);
- t2 = t2->castMod(mod);
- t = t1;
- e1 = e1->castTo(sc, t);
- e2 = e2->castTo(sc, t);
- goto Lagain;
- }
- else if (t1->ty == Tnull && t2->ty == Tnull)
- {
- unsigned char mod = MODmerge(t1->mod, t2->mod);
-
- t = t1->castMod(mod);
- e1 = e1->castTo(sc, t);
- e2 = e2->castTo(sc, t);
- goto Lret;
- }
- else if (t2->ty == Tnull &&
- (t1->ty == Tpointer || t1->ty == Taarray || t1->ty == Tarray))
- {
- goto Lt1;
- }
- else if (t1->ty == Tnull &&
- (t2->ty == Tpointer || t2->ty == Taarray || t2->ty == Tarray))
- {
- goto Lt2;
- }
- else if (t1->ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e1))
- {
- if (e2->implicitConvTo(t1->nextOf()))
- {
- // T[] op T
- // T[] op cast(T)U
- e2 = e2->castTo(sc, t1->nextOf());
- t = t1->nextOf()->arrayOf();
- }
- else if (t1->nextOf()->implicitConvTo(e2->type))
- {
- // (cast(T)U)[] op T (Bugzilla 12780)
- // e1 is left as U[], it will be handled in arrayOp() later.
- t = e2->type->arrayOf();
- }
- else if (t2->ty == Tarray && isArrayOpOperand(e2))
- {
- if (t1->nextOf()->implicitConvTo(t2->nextOf()))
- {
- // (cast(T)U)[] op T[] (Bugzilla 12780)
- // e1 is left as U[], it will be handled in arrayOp() later.
- t = t2->nextOf()->arrayOf();
- }
- else if (t2->nextOf()->implicitConvTo(t1->nextOf()))
- {
- // T[] op (cast(T)U)[] (Bugzilla 12780)
- // e2 is left as U[], it will be handled in arrayOp() later.
- t = t1->nextOf()->arrayOf();
- }
- else
- goto Lincompatible;
- }
- else
- goto Lincompatible;
- }
- else if (t2->ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e2))
- {
- if (e1->implicitConvTo(t2->nextOf()))
- {
- // T op T[]
- // cast(T)U op T[]
- e1 = e1->castTo(sc, t2->nextOf());
- t = t2->nextOf()->arrayOf();
- }
- else if (t2->nextOf()->implicitConvTo(e1->type))
- {
- // T op (cast(T)U)[] (Bugzilla 12780)
- // e2 is left as U[], it will be handled in arrayOp() later.
- t = e1->type->arrayOf();
- }
- else
- goto Lincompatible;
-
- //printf("test %s\n", Token::toChars(op));
- e1 = e1->optimize(WANTvalue);
- if (isCommutative(op) && e1->isConst())
- {
- /* Swap operands to minimize number of functions generated
- */
- //printf("swap %s\n", Token::toChars(op));
- Expression *tmp = e1;
- e1 = e2;
- e2 = tmp;
- }
- }
- else
- {
- Lincompatible:
- return false;
- }
-Lret:
- if (!*pt)
- *pt = t;
- *pe1 = e1;
- *pe2 = e2;
- //print();
- return true;
-
-
-Lt1:
- e2 = e2->castTo(sc, t1);
- t = t1;
- goto Lret;
-
-Lt2:
- e1 = e1->castTo(sc, t2);
- t = t2;
- goto Lret;
-}
-
-/************************************
- * Bring leaves to common type.
- * Returns ErrorExp if error occurs. otherwise returns NULL.
- */
-
-Expression *typeCombine(BinExp *be, Scope *sc)
-{
- Type *t1 = be->e1->type->toBasetype();
- Type *t2 = be->e2->type->toBasetype();
-
- if (be->op == TOKmin || be->op == TOKadd)
- {
- // struct+struct, and class+class are errors
- if (t1->ty == Tstruct && t2->ty == Tstruct)
- goto Lerror;
- else if (t1->ty == Tclass && t2->ty == Tclass)
- goto Lerror;
- else if (t1->ty == Taarray && t2->ty == Taarray)
- goto Lerror;
- }
-
- if (!typeMerge(sc, be->op, &be->type, &be->e1, &be->e2))
- goto Lerror;
- // If the types have no value, return an error
- if (be->e1->op == TOKerror)
- return be->e1;
- if (be->e2->op == TOKerror)
- return be->e2;
- return NULL;
-
-Lerror:
- Expression *ex = be->incompatibleTypes();
- if (ex->op == TOKerror)
- return ex;
- return new ErrorExp();
-}
-
-/***********************************
- * Do integral promotions (convertchk).
- * Don't convert <array of> to <pointer to>
- */
-
-Expression *integralPromotions(Expression *e, Scope *sc)
-{
- //printf("integralPromotions %s %s\n", e->toChars(), e->type->toChars());
- switch (e->type->toBasetype()->ty)
- {
- case Tvoid:
- e->error("void has no value");
- return new ErrorExp();
-
- case Tint8:
- case Tuns8:
- case Tint16:
- case Tuns16:
- case Tbool:
- case Tchar:
- case Twchar:
- e = e->castTo(sc, Type::tint32);
- break;
-
- case Tdchar:
- e = e->castTo(sc, Type::tuns32);
- break;
- default:
- break;
- }
- return e;
-}
-
-/***********************************
- * See if both types are arrays that can be compared
- * for equality. Return true if so.
- * If they are arrays, but incompatible, issue error.
- * This is to enable comparing things like an immutable
- * array with a mutable one.
- */
-
-bool arrayTypeCompatible(Loc loc, Type *t1, Type *t2)
-{
- t1 = t1->toBasetype()->merge2();
- t2 = t2->toBasetype()->merge2();
-
- if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) &&
- (t2->ty == Tarray || t2->ty == Tsarray || t2->ty == Tpointer))
- {
- if (t1->nextOf()->implicitConvTo(t2->nextOf()) < MATCHconst &&
- t2->nextOf()->implicitConvTo(t1->nextOf()) < MATCHconst &&
- (t1->nextOf()->ty != Tvoid && t2->nextOf()->ty != Tvoid))
- {
- error(loc, "array equality comparison type mismatch, %s vs %s", t1->toChars(), t2->toChars());
- }
- return true;
- }
- return false;
-}
-
-/***********************************
- * See if both types are arrays that can be compared
- * for equality without any casting. Return true if so.
- * This is to enable comparing things like an immutable
- * array with a mutable one.
- */
-bool arrayTypeCompatibleWithoutCasting(Type *t1, Type *t2)
-{
- t1 = t1->toBasetype();
- t2 = t2->toBasetype();
-
- if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) &&
- t2->ty == t1->ty)
- {
- if (t1->nextOf()->implicitConvTo(t2->nextOf()) >= MATCHconst ||
- t2->nextOf()->implicitConvTo(t1->nextOf()) >= MATCHconst)
- return true;
- }
- return false;
-}
-
-/******************************************************************/
-
-/* Determine the integral ranges of an expression.
- * This is used to determine if implicit narrowing conversions will
- * be allowed.
- */
-
-IntRange getIntRange(Expression *e)
-{
- class IntRangeVisitor : public Visitor
- {
- public:
- IntRange range;
-
- void visit(Expression *e)
- {
- range = IntRange::fromType(e->type);
- }
-
- void visit(IntegerExp *e)
- {
- range = IntRange(SignExtendedNumber(e->getInteger())).cast(e->type);
- }
-
- void visit(CastExp *e)
- {
- range = getIntRange(e->e1).cast(e->type);
- }
-
- void visit(AddExp *e)
- {
- IntRange ir1 = getIntRange(e->e1);
- IntRange ir2 = getIntRange(e->e2);
- range = (ir1 + ir2).cast(e->type);
- }
-
- void visit(MinExp *e)
- {
- IntRange ir1 = getIntRange(e->e1);
- IntRange ir2 = getIntRange(e->e2);
- range = (ir1 - ir2).cast(e->type);
- }
-
- void visit(DivExp *e)
- {
- IntRange ir1 = getIntRange(e->e1);
- IntRange ir2 = getIntRange(e->e2);
-
- range = (ir1 / ir2).cast(e->type);
- }
-
- void visit(MulExp *e)
- {
- IntRange ir1 = getIntRange(e->e1);
- IntRange ir2 = getIntRange(e->e2);
-
- range = (ir1 * ir2).cast(e->type);
- }
-
- void visit(ModExp *e)
- {
- IntRange ir1 = getIntRange(e->e1);
- IntRange ir2 = getIntRange(e->e2);
-
- // Modding on 0 is invalid anyway.
- if (!ir2.absNeg().imin.negative)
- {
- visit((Expression *)e);
- return;
- }
- range = (ir1 % ir2).cast(e->type);
- }
-
- void visit(AndExp *e)
- {
- IntRange result;
- bool hasResult = false;
- result.unionOrAssign(getIntRange(e->e1) & getIntRange(e->e2), hasResult);
-
- assert(hasResult);
- range = result.cast(e->type);
- }
-
- void visit(OrExp *e)
- {
- IntRange result;
- bool hasResult = false;
- result.unionOrAssign(getIntRange(e->e1) | getIntRange(e->e2), hasResult);
-
- assert(hasResult);
- range = result.cast(e->type);
- }
-
- void visit(XorExp *e)
- {
- IntRange result;
- bool hasResult = false;
- result.unionOrAssign(getIntRange(e->e1) ^ getIntRange(e->e2), hasResult);
-
- assert(hasResult);
- range = result.cast(e->type);
- }
-
- void visit(ShlExp *e)
- {
- IntRange ir1 = getIntRange(e->e1);
- IntRange ir2 = getIntRange(e->e2);
-
- range = (ir1 << ir2).cast(e->type);
- }
-
- void visit(ShrExp *e)
- {
- IntRange ir1 = getIntRange(e->e1);
- IntRange ir2 = getIntRange(e->e2);
-
- range = (ir1 >> ir2).cast(e->type);
- }
-
- void visit(UshrExp *e)
- {
- IntRange ir1 = getIntRange(e->e1).castUnsigned(e->e1->type);
- IntRange ir2 = getIntRange(e->e2);
-
- range = (ir1 >> ir2).cast(e->type);
- }
-
- void visit(AssignExp *e)
- {
- range = getIntRange(e->e2).cast(e->type);
- }
-
- void visit(CondExp *e)
- {
- // No need to check e->econd; assume caller has called optimize()
- IntRange ir1 = getIntRange(e->e1);
- IntRange ir2 = getIntRange(e->e2);
- range = ir1.unionWith(ir2).cast(e->type);
- }
-
- void visit(VarExp *e)
- {
- Expression *ie;
- VarDeclaration* vd = e->var->isVarDeclaration();
- if (vd && vd->range)
- range = vd->range->cast(e->type);
- else if (vd && vd->_init && !vd->type->isMutable() &&
- (ie = vd->getConstInitializer()) != NULL)
- ie->accept(this);
- else
- visit((Expression *)e);
- }
-
- void visit(CommaExp *e)
- {
- e->e2->accept(this);
- }
-
- void visit(ComExp *e)
- {
- IntRange ir = getIntRange(e->e1);
- range = IntRange(SignExtendedNumber(~ir.imax.value, !ir.imax.negative),
- SignExtendedNumber(~ir.imin.value, !ir.imin.negative)).cast(e->type);
- }
-
- void visit(NegExp *e)
- {
- IntRange ir = getIntRange(e->e1);
- range = (-ir).cast(e->type);
- }
- };
-
- IntRangeVisitor v;
- e->accept(&v);
- return v.range;
-}
diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d
new file mode 100644
index 00000000000..4c70565e5c2
--- /dev/null
+++ b/gcc/d/dmd/dcast.d
@@ -0,0 +1,3741 @@
+/**
+ * Semantic analysis for cast-expressions.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dcast.d, _dcast.d)
+ * Documentation: https://dlang.org/phobos/dmd_dcast.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dcast.d
+ */
+
+module dmd.dcast;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arrayop;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.impcnvtab;
+import dmd.id;
+import dmd.init;
+import dmd.intrange;
+import dmd.mtype;
+import dmd.opover;
+import dmd.root.ctfloat;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.utf;
+import dmd.visitor;
+
+enum LOG = false;
+
+/**
+ * Attempt to implicitly cast the expression into type `t`.
+ *
+ * This routine will change `e`. To check the matching level,
+ * use `implicitConvTo`.
+ *
+ * Params:
+ * e = Expression that is to be casted
+ * sc = Current scope
+ * t = Expected resulting type
+ *
+ * Returns:
+ * The resulting casted expression (mutating `e`), or `ErrorExp`
+ * if such an implicit conversion is not possible.
+ */
+Expression implicitCastTo(Expression e, Scope* sc, Type t)
+{
+ extern (C++) final class ImplicitCastTo : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Type t;
+ Scope* sc;
+ Expression result;
+
+ extern (D) this(Scope* sc, Type t)
+ {
+ this.sc = sc;
+ this.t = t;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars());
+
+ if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t))
+ {
+ if (match == MATCH.constant && (e.type.constConv(t) || !e.isLvalue() && e.type.equivalent(t)))
+ {
+ /* Do not emit CastExp for const conversions and
+ * unique conversions on rvalue.
+ */
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+
+ auto ad = isAggregate(e.type);
+ if (ad && ad.aliasthis)
+ {
+ auto ts = ad.type.isTypeStruct();
+ const adMatch = ts
+ ? ts.implicitConvToWithoutAliasThis(t)
+ : ad.type.isTypeClass().implicitConvToWithoutAliasThis(t);
+
+ if (!adMatch)
+ {
+ Type tob = t.toBasetype();
+ Type t1b = e.type.toBasetype();
+ if (ad != isAggregate(tob))
+ {
+ if (t1b.ty == Tclass && tob.ty == Tclass)
+ {
+ ClassDeclaration t1cd = t1b.isClassHandle();
+ ClassDeclaration tocd = tob.isClassHandle();
+ int offset;
+ if (tocd.isBaseOf(t1cd, &offset))
+ {
+ result = new CastExp(e.loc, e, t);
+ result.type = t;
+ return;
+ }
+ }
+
+ /* Forward the cast to our alias this member, rewrite to:
+ * cast(to)e1.aliasthis
+ */
+ result = resolveAliasThis(sc, e);
+ result = result.castTo(sc, t);
+ return;
+ }
+ }
+ }
+
+ result = e.castTo(sc, t);
+ return;
+ }
+
+ result = e.optimize(WANTvalue);
+ if (result != e)
+ {
+ result.accept(this);
+ return;
+ }
+
+ if (t.ty != Terror && e.type.ty != Terror)
+ {
+ if (!t.deco)
+ {
+ e.error("forward reference to type `%s`", t.toChars());
+ }
+ else
+ {
+ //printf("type %p ty %d deco %p\n", type, type.ty, type.deco);
+ //type = type.typeSemantic(loc, sc);
+ //printf("type %s t %s\n", type.deco, t.deco);
+ auto ts = toAutoQualChars(e.type, t);
+ e.error("cannot implicitly convert expression `%s` of type `%s` to `%s`",
+ e.toChars(), ts[0], ts[1]);
+ }
+ }
+ result = ErrorExp.get();
+ }
+
+ override void visit(StringExp e)
+ {
+ //printf("StringExp::implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars());
+ visit(cast(Expression)e);
+ if (auto se = result.isStringExp())
+ {
+ // Retain polysemous nature if it started out that way
+ se.committed = e.committed;
+ }
+ }
+
+ override void visit(ErrorExp e)
+ {
+ result = e;
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp::implicitCastTo type = %p %s, t = %s\n", e.type, e.type ? e.type.toChars() : NULL, t.toChars());
+ FuncExp fe;
+ if (e.matchType(t, sc, &fe) > MATCH.nomatch)
+ {
+ result = fe;
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ visit(cast(Expression)e);
+
+ Type tb = result.type.toBasetype();
+ if (auto ta = tb.isTypeDArray())
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ semanticTypeInfo(sc, ta.next);
+ }
+
+ override void visit(SliceExp e)
+ {
+ visit(cast(Expression)e);
+
+ if (auto se = result.isSliceExp())
+ if (auto ale = se.e1.isArrayLiteralExp())
+ {
+ Type tb = t.toBasetype();
+ Type tx = (tb.ty == Tsarray)
+ ? tb.nextOf().sarrayOf(ale.elements ? ale.elements.dim : 0)
+ : tb.nextOf().arrayOf();
+ se.e1 = ale.implicitCastTo(sc, tx);
+ }
+ }
+ }
+
+ scope ImplicitCastTo v = new ImplicitCastTo(sc, t);
+ e.accept(v);
+ return v.result;
+}
+
+/**
+ * Checks whether or not an expression can be implicitly converted
+ * to type `t`.
+ *
+ * Unlike `implicitCastTo`, this routine does not perform the actual cast,
+ * but only checks up to what `MATCH` level the conversion would be possible.
+ *
+ * Params:
+ * e = Expression that is to be casted
+ * t = Expected resulting type
+ *
+ * Returns:
+ * The `MATCH` level between `e.type` and `t`.
+ */
+MATCH implicitConvTo(Expression e, Type t)
+{
+ extern (C++) final class ImplicitConvTo : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Type t;
+ MATCH result;
+
+ extern (D) this(Type t)
+ {
+ this.t = t;
+ result = MATCH.nomatch;
+ }
+
+ override void visit(Expression e)
+ {
+ version (none)
+ {
+ printf("Expression::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ //static int nest; if (++nest == 10) assert(0);
+ if (t == Type.terror)
+ return;
+ if (!e.type)
+ {
+ e.error("`%s` is not an expression", e.toChars());
+ e.type = Type.terror;
+ }
+
+ Expression ex = e.optimize(WANTvalue);
+ if (ex.type.equals(t))
+ {
+ result = MATCH.exact;
+ return;
+ }
+ if (ex != e)
+ {
+ //printf("\toptimized to %s of type %s\n", e.toChars(), e.type.toChars());
+ result = ex.implicitConvTo(t);
+ return;
+ }
+
+ MATCH match = e.type.implicitConvTo(t);
+ if (match != MATCH.nomatch)
+ {
+ result = match;
+ return;
+ }
+
+ /* See if we can do integral narrowing conversions
+ */
+ if (e.type.isintegral() && t.isintegral() && e.type.isTypeBasic() && t.isTypeBasic())
+ {
+ IntRange src = getIntRange(e);
+ IntRange target = IntRange.fromType(t);
+ if (target.contains(src))
+ {
+ result = MATCH.convert;
+ return;
+ }
+ }
+ }
+
+ /******
+ * Given expression e of type t, see if we can implicitly convert e
+ * to type tprime, where tprime is type t with mod bits added.
+ * Returns:
+ * match level
+ */
+ static MATCH implicitMod(Expression e, Type t, MOD mod)
+ {
+ Type tprime;
+ if (t.ty == Tpointer)
+ tprime = t.nextOf().castMod(mod).pointerTo();
+ else if (t.ty == Tarray)
+ tprime = t.nextOf().castMod(mod).arrayOf();
+ else if (t.ty == Tsarray)
+ tprime = t.nextOf().castMod(mod).sarrayOf(t.size() / t.nextOf().size());
+ else
+ tprime = t.castMod(mod);
+
+ return e.implicitConvTo(tprime);
+ }
+
+ static MATCH implicitConvToAddMin(BinExp e, Type t)
+ {
+ /* Is this (ptr +- offset)? If so, then ask ptr
+ * if the conversion can be done.
+ * This is to support doing things like implicitly converting a mutable unique
+ * pointer to an immutable pointer.
+ */
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (typeb.ty != Tpointer || tb.ty != Tpointer)
+ return MATCH.nomatch;
+
+ Type t1b = e.e1.type.toBasetype();
+ Type t2b = e.e2.type.toBasetype();
+ if (t1b.ty == Tpointer && t2b.isintegral() && t1b.equivalent(tb))
+ {
+ // ptr + offset
+ // ptr - offset
+ MATCH m = e.e1.implicitConvTo(t);
+ return (m > MATCH.constant) ? MATCH.constant : m;
+ }
+ if (t2b.ty == Tpointer && t1b.isintegral() && t2b.equivalent(tb))
+ {
+ // offset + ptr
+ MATCH m = e.e2.implicitConvTo(t);
+ return (m > MATCH.constant) ? MATCH.constant : m;
+ }
+
+ return MATCH.nomatch;
+ }
+
+ override void visit(AddExp e)
+ {
+ version (none)
+ {
+ printf("AddExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ visit(cast(Expression)e);
+ if (result == MATCH.nomatch)
+ result = implicitConvToAddMin(e, t);
+ }
+
+ override void visit(MinExp e)
+ {
+ version (none)
+ {
+ printf("MinExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ visit(cast(Expression)e);
+ if (result == MATCH.nomatch)
+ result = implicitConvToAddMin(e, t);
+ }
+
+ override void visit(IntegerExp e)
+ {
+ version (none)
+ {
+ printf("IntegerExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ MATCH m = e.type.implicitConvTo(t);
+ if (m >= MATCH.constant)
+ {
+ result = m;
+ return;
+ }
+
+ TY ty = e.type.toBasetype().ty;
+ TY toty = t.toBasetype().ty;
+ TY oldty = ty;
+
+ if (m == MATCH.nomatch && t.ty == Tenum)
+ return;
+
+ if (auto tv = t.isTypeVector())
+ {
+ TypeBasic tb = tv.elementType();
+ if (tb.ty == Tvoid)
+ return;
+ toty = tb.ty;
+ }
+
+ switch (ty)
+ {
+ case Tbool:
+ case Tint8:
+ case Tchar:
+ case Tuns8:
+ case Tint16:
+ case Tuns16:
+ case Twchar:
+ ty = Tint32;
+ break;
+
+ case Tdchar:
+ ty = Tuns32;
+ break;
+
+ default:
+ break;
+ }
+
+ // Only allow conversion if no change in value
+ immutable dinteger_t value = e.toInteger();
+
+ bool isLosslesslyConvertibleToFP(T)()
+ {
+ if (e.type.isunsigned())
+ {
+ const f = cast(T) value;
+ return cast(dinteger_t) f == value;
+ }
+
+ const f = cast(T) cast(sinteger_t) value;
+ return cast(sinteger_t) f == cast(sinteger_t) value;
+ }
+
+ switch (toty)
+ {
+ case Tbool:
+ if ((value & 1) != value)
+ return;
+ break;
+
+ case Tint8:
+ if (ty == Tuns64 && value & ~0x7FU)
+ return;
+ else if (cast(byte)value != value)
+ return;
+ break;
+
+ case Tchar:
+ if ((oldty == Twchar || oldty == Tdchar) && value > 0x7F)
+ return;
+ goto case Tuns8;
+ case Tuns8:
+ //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value);
+ if (cast(ubyte)value != value)
+ return;
+ break;
+
+ case Tint16:
+ if (ty == Tuns64 && value & ~0x7FFFU)
+ return;
+ else if (cast(short)value != value)
+ return;
+ break;
+
+ case Twchar:
+ if (oldty == Tdchar && value > 0xD7FF && value < 0xE000)
+ return;
+ goto case Tuns16;
+ case Tuns16:
+ if (cast(ushort)value != value)
+ return;
+ break;
+
+ case Tint32:
+ if (ty == Tuns32)
+ {
+ }
+ else if (ty == Tuns64 && value & ~0x7FFFFFFFU)
+ return;
+ else if (cast(int)value != value)
+ return;
+ break;
+
+ case Tuns32:
+ if (ty == Tint32)
+ {
+ }
+ else if (cast(uint)value != value)
+ return;
+ break;
+
+ case Tdchar:
+ if (value > 0x10FFFFU)
+ return;
+ break;
+
+ case Tfloat32:
+ if (!isLosslesslyConvertibleToFP!float)
+ return;
+ break;
+
+ case Tfloat64:
+ if (!isLosslesslyConvertibleToFP!double)
+ return;
+ break;
+
+ case Tfloat80:
+ if (!isLosslesslyConvertibleToFP!real_t)
+ return;
+ break;
+
+ case Tpointer:
+ //printf("type = %s\n", type.toBasetype()->toChars());
+ //printf("t = %s\n", t.toBasetype()->toChars());
+ if (ty == Tpointer && e.type.toBasetype().nextOf().ty == t.toBasetype().nextOf().ty)
+ {
+ /* Allow things like:
+ * const char* P = cast(char *)3;
+ * char* q = P;
+ */
+ break;
+ }
+ goto default;
+
+ default:
+ visit(cast(Expression)e);
+ return;
+ }
+
+ //printf("MATCH.convert\n");
+ result = MATCH.convert;
+ }
+
+ override void visit(ErrorExp e)
+ {
+ // no match
+ }
+
+ override void visit(NullExp e)
+ {
+ version (none)
+ {
+ printf("NullExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ if (e.type.equals(t))
+ {
+ result = MATCH.exact;
+ return;
+ }
+
+ /* Allow implicit conversions from immutable to mutable|const,
+ * and mutable to immutable. It works because, after all, a null
+ * doesn't actually point to anything.
+ */
+ if (t.equivalent(e.type))
+ {
+ result = MATCH.constant;
+ return;
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ version (none)
+ {
+ printf("StructLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+ if (e.type.ty == t.ty && e.type.isTypeStruct() && e.type.isTypeStruct().sym == t.isTypeStruct().sym)
+ {
+ result = MATCH.constant;
+ foreach (i, el; (*e.elements)[])
+ {
+ if (!el)
+ continue;
+ Type te = e.sd.fields[i].type.addMod(t.mod);
+ MATCH m2 = el.implicitConvTo(te);
+ //printf("\t%s => %s, match = %d\n", el.toChars(), te.toChars(), m2);
+ if (m2 < result)
+ result = m2;
+ }
+ }
+ }
+
+ override void visit(StringExp e)
+ {
+ version (none)
+ {
+ printf("StringExp::implicitConvTo(this=%s, committed=%d, type=%s, t=%s)\n", e.toChars(), e.committed, e.type.toChars(), t.toChars());
+ }
+ if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid)
+ return;
+
+ if (!(e.type.ty == Tsarray || e.type.ty == Tarray || e.type.ty == Tpointer))
+ return visit(cast(Expression)e);
+
+ TY tyn = e.type.nextOf().ty;
+
+ if (!tyn.isSomeChar)
+ return visit(cast(Expression)e);
+
+ switch (t.ty)
+ {
+ case Tsarray:
+ if (e.type.ty == Tsarray)
+ {
+ TY tynto = t.nextOf().ty;
+ if (tynto == tyn)
+ {
+ if (e.type.isTypeSArray().dim.toInteger() == t.isTypeSArray().dim.toInteger())
+ {
+ result = MATCH.exact;
+ }
+ return;
+ }
+ if (tynto.isSomeChar)
+ {
+ if (e.committed && tynto != tyn)
+ return;
+ size_t fromlen = e.numberOfCodeUnits(tynto);
+ size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger();
+ if (tolen < fromlen)
+ return;
+ if (tolen != fromlen)
+ {
+ // implicit length extending
+ result = MATCH.convert;
+ return;
+ }
+ }
+ if (!e.committed && tynto.isSomeChar)
+ {
+ result = MATCH.exact;
+ return;
+ }
+ }
+ else if (e.type.ty == Tarray)
+ {
+ TY tynto = t.nextOf().ty;
+ if (tynto.isSomeChar)
+ {
+ if (e.committed && tynto != tyn)
+ return;
+ size_t fromlen = e.numberOfCodeUnits(tynto);
+ size_t tolen = cast(size_t)t.isTypeSArray().dim.toInteger();
+ if (tolen < fromlen)
+ return;
+ if (tolen != fromlen)
+ {
+ // implicit length extending
+ result = MATCH.convert;
+ return;
+ }
+ }
+ if (tynto == tyn)
+ {
+ result = MATCH.exact;
+ return;
+ }
+ if (!e.committed && tynto.isSomeChar)
+ {
+ result = MATCH.exact;
+ return;
+ }
+ }
+ goto case; /+ fall through +/
+ case Tarray:
+ case Tpointer:
+ Type tn = t.nextOf();
+ MATCH m = MATCH.exact;
+ if (e.type.nextOf().mod != tn.mod)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=16183
+ if (!tn.isConst() && !tn.isImmutable())
+ return;
+ m = MATCH.constant;
+ }
+ if (!e.committed)
+ {
+ switch (tn.ty)
+ {
+ case Tchar:
+ if (e.postfix == 'w' || e.postfix == 'd')
+ m = MATCH.convert;
+ result = m;
+ return;
+ case Twchar:
+ if (e.postfix != 'w')
+ m = MATCH.convert;
+ result = m;
+ return;
+ case Tdchar:
+ if (e.postfix != 'd')
+ m = MATCH.convert;
+ result = m;
+ return;
+ case Tenum:
+ if (tn.isTypeEnum().sym.isSpecial())
+ {
+ /* Allow string literal -> const(wchar_t)[]
+ */
+ if (TypeBasic tob = tn.toBasetype().isTypeBasic())
+ result = tn.implicitConvTo(tob);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ version (none)
+ {
+ printf("ArrayLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if ((tb.ty == Tarray || tb.ty == Tsarray) &&
+ (typeb.ty == Tarray || typeb.ty == Tsarray))
+ {
+ result = MATCH.exact;
+ Type typen = typeb.nextOf().toBasetype();
+
+ if (auto tsa = tb.isTypeSArray())
+ {
+ if (e.elements.dim != tsa.dim.toInteger())
+ result = MATCH.nomatch;
+ }
+
+ Type telement = tb.nextOf();
+ if (!e.elements.dim)
+ {
+ if (typen.ty != Tvoid)
+ result = typen.implicitConvTo(telement);
+ }
+ else
+ {
+ if (e.basis)
+ {
+ MATCH m = e.basis.implicitConvTo(telement);
+ if (m < result)
+ result = m;
+ }
+ for (size_t i = 0; i < e.elements.dim; i++)
+ {
+ Expression el = (*e.elements)[i];
+ if (result == MATCH.nomatch)
+ break;
+ if (!el)
+ continue;
+ MATCH m = el.implicitConvTo(telement);
+ if (m < result)
+ result = m; // remember worst match
+ }
+ }
+
+ if (!result)
+ result = e.type.implicitConvTo(t);
+
+ return;
+ }
+ else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray))
+ {
+ result = MATCH.exact;
+ // Convert array literal to vector type
+ TypeVector tv = tb.isTypeVector();
+ TypeSArray tbase = tv.basetype.isTypeSArray();
+ assert(tbase);
+ const edim = e.elements.dim;
+ const tbasedim = tbase.dim.toInteger();
+ if (edim > tbasedim)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+
+ Type telement = tv.elementType();
+ if (edim < tbasedim)
+ {
+ Expression el = typeb.nextOf.defaultInitLiteral(e.loc);
+ MATCH m = el.implicitConvTo(telement);
+ if (m < result)
+ result = m; // remember worst match
+ }
+ foreach (el; (*e.elements)[])
+ {
+ MATCH m = el.implicitConvTo(telement);
+ if (m < result)
+ result = m; // remember worst match
+ if (result == MATCH.nomatch)
+ break; // no need to check for worse
+ }
+ return;
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ auto taa = t.toBasetype().isTypeAArray();
+ Type typeb = e.type.toBasetype();
+
+ if (!(taa && typeb.ty == Taarray))
+ return visit(cast(Expression)e);
+
+ result = MATCH.exact;
+ foreach (i, el; (*e.keys)[])
+ {
+ MATCH m = el.implicitConvTo(taa.index);
+ if (m < result)
+ result = m; // remember worst match
+ if (result == MATCH.nomatch)
+ break; // no need to check for worse
+ el = (*e.values)[i];
+ m = el.implicitConvTo(taa.nextOf());
+ if (m < result)
+ result = m; // remember worst match
+ if (result == MATCH.nomatch)
+ break; // no need to check for worse
+ }
+ }
+
+ override void visit(CallExp e)
+ {
+ enum LOG = false;
+ static if (LOG)
+ {
+ printf("CallExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ /* Allow the result of strongly pure functions to
+ * convert to immutable
+ */
+ if (e.f &&
+ (global.params.useDIP1000 != FeatureState.enabled || // lots of legacy code breaks with the following purity check
+ e.f.isPure() >= PURE.strong ||
+ // Special case exemption for Object.dup() which we assume is implemented correctly
+ e.f.ident == Id.dup &&
+ e.f.toParent2() == ClassDeclaration.object.toParent()) &&
+ e.f.isReturnIsolated() // check isReturnIsolated last, because it is potentially expensive.
+ )
+ {
+ result = e.type.immutableOf().implicitConvTo(t);
+ if (result > MATCH.constant) // Match level is MATCH.constant at best.
+ result = MATCH.constant;
+ return;
+ }
+
+ /* Conversion is 'const' conversion if:
+ * 1. function is pure (weakly pure is ok)
+ * 2. implicit conversion only fails because of mod bits
+ * 3. each function parameter can be implicitly converted to the mod bits
+ */
+ auto tf = (e.f ? e.f.type : e.e1.type).toBasetype().isTypeFunction();
+ if (!tf)
+ return;
+
+ if (tf.purity == PURE.impure)
+ return;
+ if (e.f && e.f.isNested())
+ return;
+
+ /* See if fail only because of mod bits.
+ *
+ * https://issues.dlang.org/show_bug.cgi?id=14155
+ * All pure functions can access global immutable data.
+ * So the returned pointer may refer an immutable global data,
+ * and then the returned pointer that points non-mutable object
+ * cannot be unique pointer.
+ *
+ * Example:
+ * immutable g;
+ * static this() { g = 1; }
+ * const(int*) foo() pure { return &g; }
+ * void test() {
+ * immutable(int*) ip = foo(); // OK
+ * int* mp = foo(); // should be disallowed
+ * }
+ */
+ if (e.type.immutableOf().implicitConvTo(t) < MATCH.constant && e.type.addMod(MODFlags.shared_).implicitConvTo(t) < MATCH.constant && e.type.implicitConvTo(t.addMod(MODFlags.shared_)) < MATCH.constant)
+ {
+ return;
+ }
+ // Allow a conversion to immutable type, or
+ // conversions of mutable types between thread-local and shared.
+
+ /* Get mod bits of what we're converting to
+ */
+ Type tb = t.toBasetype();
+ MOD mod = tb.mod;
+ if (tf.isref)
+ {
+ }
+ else
+ {
+ if (Type ti = getIndirection(t))
+ mod = ti.mod;
+ }
+ static if (LOG)
+ {
+ printf("mod = x%x\n", mod);
+ }
+ if (mod & MODFlags.wild)
+ return; // not sure what to do with this
+
+ /* Apply mod bits to each function parameter,
+ * and see if we can convert the function argument to the modded type
+ */
+
+ size_t nparams = tf.parameterList.length;
+ size_t j = tf.isDstyleVariadic(); // if TypeInfoArray was prepended
+ if (auto dve = e.e1.isDotVarExp())
+ {
+ /* Treat 'this' as just another function argument
+ */
+ Type targ = dve.e1.type;
+ if (targ.constConv(targ.castMod(mod)) == MATCH.nomatch)
+ return;
+ }
+ foreach (const i; j .. e.arguments.dim)
+ {
+ Expression earg = (*e.arguments)[i];
+ Type targ = earg.type.toBasetype();
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targ: %s\n", cast(int)i, earg.toChars(), targ.toChars());
+ }
+ if (i - j < nparams)
+ {
+ Parameter fparam = tf.parameterList[i - j];
+ if (fparam.storageClass & STC.lazy_)
+ return; // not sure what to do with this
+ Type tparam = fparam.type;
+ if (!tparam)
+ continue;
+ if (fparam.isReference())
+ {
+ if (targ.constConv(tparam.castMod(mod)) == MATCH.nomatch)
+ return;
+ continue;
+ }
+ }
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targm: %s\n", cast(int)i, earg.toChars(), targ.addMod(mod).toChars());
+ }
+ if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+ return;
+ }
+
+ /* Success
+ */
+ result = MATCH.constant;
+ }
+
+ override void visit(AddrExp e)
+ {
+ version (none)
+ {
+ printf("AddrExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e.type.implicitConvTo(t);
+ //printf("\tresult = %d\n", result);
+
+ if (result != MATCH.nomatch)
+ return;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (e.e1.op == TOK.overloadSet &&
+ (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
+ {
+ OverExp eo = e.e1.isOverExp();
+ FuncDeclaration f = null;
+ foreach (s; eo.vars.a[])
+ {
+ FuncDeclaration f2 = s.isFuncDeclaration();
+ assert(f2);
+ if (f2.overloadExactMatch(tb.nextOf()))
+ {
+ if (f)
+ {
+ /* Error if match in more than one overload set,
+ * even if one is a 'better' match than the other.
+ */
+ ScopeDsymbol.multiplyDefined(e.loc, f, f2);
+ }
+ else
+ f = f2;
+ result = MATCH.exact;
+ }
+ }
+ }
+
+ if (e.e1.op == TOK.variable &&
+ typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
+ tb.ty == Tpointer && tb.nextOf().ty == Tfunction)
+ {
+ /* I don't think this can ever happen -
+ * it should have been
+ * converted to a SymOffExp.
+ */
+ assert(0);
+ }
+
+ //printf("\tresult = %d\n", result);
+ }
+
+ override void visit(SymOffExp e)
+ {
+ version (none)
+ {
+ printf("SymOffExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e.type.implicitConvTo(t);
+ //printf("\tresult = %d\n", result);
+ if (result != MATCH.nomatch)
+ return;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
+ (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
+ {
+ if (FuncDeclaration f = e.var.isFuncDeclaration())
+ {
+ f = f.overloadExactMatch(tb.nextOf());
+ if (f)
+ {
+ if ((tb.ty == Tdelegate && (f.needThis() || f.isNested())) ||
+ (tb.ty == Tpointer && !(f.needThis() || f.isNested())))
+ {
+ result = MATCH.exact;
+ }
+ }
+ }
+ }
+ //printf("\tresult = %d\n", result);
+ }
+
+ override void visit(DelegateExp e)
+ {
+ version (none)
+ {
+ printf("DelegateExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e.type.implicitConvTo(t);
+ if (result != MATCH.nomatch)
+ return;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (typeb.ty == Tdelegate && tb.ty == Tdelegate)
+ {
+ if (e.func && e.func.overloadExactMatch(tb.nextOf()))
+ result = MATCH.exact;
+ }
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp::implicitConvTo type = %p %s, t = %s\n", e.type, e.type ? e.type.toChars() : NULL, t.toChars());
+ MATCH m = e.matchType(t, null, null, 1);
+ if (m > MATCH.nomatch)
+ {
+ result = m;
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(AndExp e)
+ {
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ MATCH m1 = e.e1.implicitConvTo(t);
+ MATCH m2 = e.e2.implicitConvTo(t);
+
+ // Pick the worst match
+ result = (m1 < m2) ? m1 : m2;
+ }
+
+ override void visit(OrExp e)
+ {
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ MATCH m1 = e.e1.implicitConvTo(t);
+ MATCH m2 = e.e2.implicitConvTo(t);
+
+ // Pick the worst match
+ result = (m1 < m2) ? m1 : m2;
+ }
+
+ override void visit(XorExp e)
+ {
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ MATCH m1 = e.e1.implicitConvTo(t);
+ MATCH m2 = e.e2.implicitConvTo(t);
+
+ // Pick the worst match
+ result = (m1 < m2) ? m1 : m2;
+ }
+
+ override void visit(CondExp e)
+ {
+ MATCH m1 = e.e1.implicitConvTo(t);
+ MATCH m2 = e.e2.implicitConvTo(t);
+ //printf("CondExp: m1 %d m2 %d\n", m1, m2);
+
+ // Pick the worst match
+ result = (m1 < m2) ? m1 : m2;
+ }
+
+ override void visit(CommaExp e)
+ {
+ e.e2.accept(this);
+ }
+
+ override void visit(CastExp e)
+ {
+ version (none)
+ {
+ printf("CastExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e.type.implicitConvTo(t);
+ if (result != MATCH.nomatch)
+ return;
+
+ if (t.isintegral() && e.e1.type.isintegral() && e.e1.implicitConvTo(t) != MATCH.nomatch)
+ result = MATCH.convert;
+ else
+ visit(cast(Expression)e);
+ }
+
+ override void visit(NewExp e)
+ {
+ version (none)
+ {
+ printf("NewExp::implicitConvTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ /* Calling new() is like calling a pure function. We can implicitly convert the
+ * return from new() to t using the same algorithm as in CallExp, with the function
+ * 'arguments' being:
+ * thisexp
+ * newargs
+ * arguments
+ * .init
+ * 'member' need to be pure.
+ */
+
+ /* See if fail only because of mod bits
+ */
+ if (e.type.immutableOf().implicitConvTo(t.immutableOf()) == MATCH.nomatch)
+ return;
+
+ /* Get mod bits of what we're converting to
+ */
+ Type tb = t.toBasetype();
+ MOD mod = tb.mod;
+ if (Type ti = getIndirection(t))
+ mod = ti.mod;
+ static if (LOG)
+ {
+ printf("mod = x%x\n", mod);
+ }
+ if (mod & MODFlags.wild)
+ return; // not sure what to do with this
+
+ /* Apply mod bits to each argument,
+ * and see if we can convert the argument to the modded type
+ */
+
+ if (e.thisexp)
+ {
+ /* Treat 'this' as just another function argument
+ */
+ Type targ = e.thisexp.type;
+ if (targ.constConv(targ.castMod(mod)) == MATCH.nomatch)
+ return;
+ }
+
+ /* Check call to 'member'
+ */
+ if (e.member)
+ {
+ FuncDeclaration fd = e.member;
+ if (fd.errors || fd.type.ty != Tfunction)
+ return; // error
+ TypeFunction tf = fd.type.isTypeFunction();
+ if (tf.purity == PURE.impure)
+ return; // impure
+
+ if (e.type.immutableOf().implicitConvTo(t) < MATCH.constant && e.type.addMod(MODFlags.shared_).implicitConvTo(t) < MATCH.constant && e.type.implicitConvTo(t.addMod(MODFlags.shared_)) < MATCH.constant)
+ {
+ return;
+ }
+ // Allow a conversion to immutable type, or
+ // conversions of mutable types between thread-local and shared.
+
+ Expressions* args = e.arguments;
+
+ size_t nparams = tf.parameterList.length;
+ // if TypeInfoArray was prepended
+ size_t j = tf.isDstyleVariadic();
+ for (size_t i = j; i < e.arguments.dim; ++i)
+ {
+ Expression earg = (*args)[i];
+ Type targ = earg.type.toBasetype();
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targ: %s\n", cast(int)i, earg.toChars(), targ.toChars());
+ }
+ if (i - j < nparams)
+ {
+ Parameter fparam = tf.parameterList[i - j];
+ if (fparam.storageClass & STC.lazy_)
+ return; // not sure what to do with this
+ Type tparam = fparam.type;
+ if (!tparam)
+ continue;
+ if (fparam.isReference())
+ {
+ if (targ.constConv(tparam.castMod(mod)) == MATCH.nomatch)
+ return;
+ continue;
+ }
+ }
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targm: %s\n", cast(int)i, earg.toChars(), targ.addMod(mod).toChars());
+ }
+ if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+ return;
+ }
+ }
+
+ /* If no 'member', then construction is by simple assignment,
+ * and just straight check 'arguments'
+ */
+ if (!e.member && e.arguments)
+ {
+ for (size_t i = 0; i < e.arguments.dim; ++i)
+ {
+ Expression earg = (*e.arguments)[i];
+ if (!earg) // https://issues.dlang.org/show_bug.cgi?id=14853
+ // if it's on overlapped field
+ continue;
+ Type targ = earg.type.toBasetype();
+ static if (LOG)
+ {
+ printf("[%d] earg: %s, targ: %s\n", cast(int)i, earg.toChars(), targ.toChars());
+ printf("[%d] earg: %s, targm: %s\n", cast(int)i, earg.toChars(), targ.addMod(mod).toChars());
+ }
+ if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+ return;
+ }
+ }
+
+ /* Consider the .init expression as an argument
+ */
+ Type ntb = e.newtype.toBasetype();
+ if (ntb.ty == Tarray)
+ ntb = ntb.nextOf().toBasetype();
+ if (auto ts = ntb.isTypeStruct())
+ {
+ // Don't allow nested structs - uplevel reference may not be convertible
+ StructDeclaration sd = ts.sym;
+ sd.size(e.loc); // resolve any forward references
+ if (sd.isNested())
+ return;
+ }
+ if (ntb.isZeroInit(e.loc))
+ {
+ /* Zeros are implicitly convertible, except for special cases.
+ */
+ if (auto tc = ntb.isTypeClass())
+ {
+ /* With new() must look at the class instance initializer.
+ */
+ ClassDeclaration cd = tc.sym;
+
+ cd.size(e.loc); // resolve any forward references
+
+ if (cd.isNested())
+ return; // uplevel reference may not be convertible
+
+ assert(!cd.isInterfaceDeclaration());
+
+ struct ClassCheck
+ {
+ extern (C++) static bool convertible(Expression e, ClassDeclaration cd, MOD mod)
+ {
+ for (size_t i = 0; i < cd.fields.dim; i++)
+ {
+ VarDeclaration v = cd.fields[i];
+ Initializer _init = v._init;
+ if (_init)
+ {
+ if (_init.isVoidInitializer())
+ {
+ }
+ else if (ExpInitializer ei = _init.isExpInitializer())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=21319
+ // This is to prevent re-analyzing the same expression
+ // over and over again.
+ if (ei.exp == e)
+ return false;
+ Type tb = v.type.toBasetype();
+ if (implicitMod(ei.exp, tb, mod) == MATCH.nomatch)
+ return false;
+ }
+ else
+ {
+ /* Enhancement: handle StructInitializer and ArrayInitializer
+ */
+ return false;
+ }
+ }
+ else if (!v.type.isZeroInit(e.loc))
+ return false;
+ }
+ return cd.baseClass ? convertible(e, cd.baseClass, mod) : true;
+ }
+ }
+
+ if (!ClassCheck.convertible(e, cd, mod))
+ return;
+ }
+ }
+ else
+ {
+ Expression earg = e.newtype.defaultInitLiteral(e.loc);
+ Type targ = e.newtype.toBasetype();
+
+ if (implicitMod(earg, targ, mod) == MATCH.nomatch)
+ return;
+ }
+
+ /* Success
+ */
+ result = MATCH.constant;
+ }
+
+ override void visit(SliceExp e)
+ {
+ //printf("SliceExp::implicitConvTo e = %s, type = %s\n", e.toChars(), e.type.toChars());
+ visit(cast(Expression)e);
+ if (result != MATCH.nomatch)
+ return;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.ty == Tsarray && typeb.ty == Tarray)
+ {
+ typeb = toStaticArrayType(e);
+ if (typeb)
+ {
+ // Try: T[] -> T[dim]
+ // (Slice with compile-time known boundaries to static array)
+ result = typeb.implicitConvTo(t);
+ if (result > MATCH.convert)
+ result = MATCH.convert; // match with implicit conversion at most
+ }
+ return;
+ }
+
+ /* If the only reason it won't convert is because of the mod bits,
+ * then test for conversion by seeing if e1 can be converted with those
+ * same mod bits.
+ */
+ Type t1b = e.e1.type.toBasetype();
+ if (tb.ty == Tarray && typeb.equivalent(tb))
+ {
+ Type tbn = tb.nextOf();
+ Type tx = null;
+
+ /* If e.e1 is dynamic array or pointer, the uniqueness of e.e1
+ * is equivalent with the uniqueness of the referred data. And in here
+ * we can have arbitrary typed reference for that.
+ */
+ if (t1b.ty == Tarray)
+ tx = tbn.arrayOf();
+ if (t1b.ty == Tpointer)
+ tx = tbn.pointerTo();
+
+ /* If e.e1 is static array, at least it should be an rvalue.
+ * If not, e.e1 is a reference, and its uniqueness does not link
+ * to the uniqueness of the referred data.
+ */
+ if (t1b.ty == Tsarray && !e.e1.isLvalue())
+ tx = tbn.sarrayOf(t1b.size() / tbn.size());
+
+ if (tx)
+ {
+ result = e.e1.implicitConvTo(tx);
+ if (result > MATCH.constant) // Match level is MATCH.constant at best.
+ result = MATCH.constant;
+ }
+ }
+
+ // Enhancement 10724
+ if (tb.ty == Tpointer && e.e1.op == TOK.string_)
+ e.e1.accept(this);
+ }
+ }
+
+ scope ImplicitConvTo v = new ImplicitConvTo(t);
+ e.accept(v);
+ return v.result;
+}
+
+/**
+ * Same as implicitConvTo(); except follow C11 rules, which are quite a bit
+ * more permissive than D.
+ * C11 6.3 and 6.5.16.1
+ * Params:
+ * e = Expression that is to be casted
+ * t = Expected resulting type
+ * Returns:
+ * The `MATCH` level between `e.type` and `t`.
+ */
+MATCH cimplicitConvTo(Expression e, Type t)
+{
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.equals(typeb))
+ return MATCH.exact;
+ if ((typeb.isintegral() || typeb.isfloating()) &&
+ (tb.isintegral() || tb.isfloating()))
+ return MATCH.convert;
+ if (tb.ty == Tpointer && typeb.isintegral()) // C11 6.3.2.3-5
+ return MATCH.convert;
+ if (tb.isintegral() && typeb.ty == Tpointer) // C11 6.3.2.3-6
+ return MATCH.convert;
+ if (tb.ty == Tpointer && typeb.ty == Tpointer)
+ {
+ if (tb.isTypePointer().next.ty == Tvoid ||
+ typeb.isTypePointer().next.ty == Tvoid)
+ return MATCH.convert; // convert to/from void* C11 6.3.2.3-1
+ }
+
+ return implicitConvTo(e, t);
+}
+
+/*****************************************
+ */
+Type toStaticArrayType(SliceExp e)
+{
+ if (e.lwr && e.upr)
+ {
+ // For the following code to work, e should be optimized beforehand.
+ // (eg. $ in lwr and upr should be already resolved, if possible)
+ Expression lwr = e.lwr.optimize(WANTvalue);
+ Expression upr = e.upr.optimize(WANTvalue);
+ if (lwr.isConst() && upr.isConst())
+ {
+ size_t len = cast(size_t)(upr.toUInteger() - lwr.toUInteger());
+ return e.type.toBasetype().nextOf().sarrayOf(len);
+ }
+ }
+ else
+ {
+ Type t1b = e.e1.type.toBasetype();
+ if (t1b.ty == Tsarray)
+ return t1b;
+ }
+ return null;
+}
+
+/**************************************
+ * Do an explicit cast.
+ * Assume that the expression `e` does not have any indirections.
+ * (Parameter 'att' is used to stop 'alias this' recursion)
+ */
+Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
+{
+ extern (C++) final class CastTo : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Type t;
+ Scope* sc;
+ Expression result;
+
+ extern (D) this(Scope* sc, Type t)
+ {
+ this.sc = sc;
+ this.t = t;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression::castTo(this=%s, t=%s)\n", e.toChars(), t.toChars());
+ version (none)
+ {
+ printf("Expression::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ if (e.type.equals(t))
+ {
+ result = e;
+ return;
+ }
+ if (e.op == TOK.variable)
+ {
+ VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
+ if (v && v.storage_class & STC.manifest)
+ {
+ result = e.ctfeInterpret();
+ /* https://issues.dlang.org/show_bug.cgi?id=18236
+ *
+ * The expression returned by ctfeInterpret points
+ * to the line where the manifest constant was declared
+ * so we need to update the location before trying to cast
+ */
+ result.loc = e.loc;
+ result = result.castTo(sc, t);
+ return;
+ }
+ }
+
+ Type tob = t.toBasetype();
+ Type t1b = e.type.toBasetype();
+ if (tob.equals(t1b))
+ {
+ result = e.copy(); // because of COW for assignment to e.type
+ result.type = t;
+ return;
+ }
+
+ /* Make semantic error against invalid cast between concrete types.
+ * Assume that 'e' is never be any placeholder expressions.
+ * The result of these checks should be consistent with CastExp::toElem().
+ */
+
+ // Fat Value types
+ const(bool) tob_isFV = (tob.ty == Tstruct || tob.ty == Tsarray || tob.ty == Tvector);
+ const(bool) t1b_isFV = (t1b.ty == Tstruct || t1b.ty == Tsarray || t1b.ty == Tvector);
+
+ // Fat Reference types
+ const(bool) tob_isFR = (tob.ty == Tarray || tob.ty == Tdelegate);
+ const(bool) t1b_isFR = (t1b.ty == Tarray || t1b.ty == Tdelegate);
+
+ // Reference types
+ const(bool) tob_isR = (tob_isFR || tob.ty == Tpointer || tob.ty == Taarray || tob.ty == Tclass);
+ const(bool) t1b_isR = (t1b_isFR || t1b.ty == Tpointer || t1b.ty == Taarray || t1b.ty == Tclass);
+
+ // Arithmetic types (== valueable basic types)
+ const(bool) tob_isA = ((tob.isintegral() || tob.isfloating()) && tob.ty != Tvector);
+ const(bool) t1b_isA = ((t1b.isintegral() || t1b.isfloating()) && t1b.ty != Tvector);
+
+ // Try casting the alias this member.
+ // Return the expression if it succeeds, null otherwise.
+ Expression tryAliasThisCast()
+ {
+ if (isRecursiveAliasThis(att, t1b))
+ return null;
+
+ /* Forward the cast to our alias this member, rewrite to:
+ * cast(to)e1.aliasthis
+ */
+ auto exp = resolveAliasThis(sc, e);
+ const errors = global.startGagging();
+ exp = castTo(exp, sc, t, att);
+ return global.endGagging(errors) ? null : exp;
+ }
+
+ bool hasAliasThis;
+ if (AggregateDeclaration t1ad = isAggregate(t1b))
+ {
+ AggregateDeclaration toad = isAggregate(tob);
+ if (t1ad != toad && t1ad.aliasthis)
+ {
+ if (t1b.ty == Tclass && tob.ty == Tclass)
+ {
+ ClassDeclaration t1cd = t1b.isClassHandle();
+ ClassDeclaration tocd = tob.isClassHandle();
+ int offset;
+ if (tocd.isBaseOf(t1cd, &offset))
+ goto Lok;
+ }
+ hasAliasThis = true;
+ }
+ }
+ else if (tob.ty == Tvector && t1b.ty != Tvector)
+ {
+ //printf("test1 e = %s, e.type = %s, tob = %s\n", e.toChars(), e.type.toChars(), tob.toChars());
+ TypeVector tv = tob.isTypeVector();
+ result = new CastExp(e.loc, e, tv.elementType());
+ result = new VectorExp(e.loc, result, tob);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+ else if (tob.ty != Tvector && t1b.ty == Tvector)
+ {
+ // T[n] <-- __vector(U[m])
+ if (tob.ty == Tsarray)
+ {
+ if (t1b.size(e.loc) == tob.size(e.loc))
+ goto Lok;
+ }
+ goto Lfail;
+ }
+ else if (t1b.implicitConvTo(tob) == MATCH.constant && t.equals(e.type.constOf()))
+ {
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+
+ // arithmetic values vs. other arithmetic values
+ // arithmetic values vs. T*
+ if (tob_isA && (t1b_isA || t1b.ty == Tpointer) || t1b_isA && (tob_isA || tob.ty == Tpointer))
+ {
+ goto Lok;
+ }
+
+ // arithmetic values vs. references or fat values
+ if (tob_isA && (t1b_isR || t1b_isFV) || t1b_isA && (tob_isR || tob_isFV))
+ {
+ goto Lfail;
+ }
+
+ // Bugzlla 3133: A cast between fat values is possible only when the sizes match.
+ if (tob_isFV && t1b_isFV)
+ {
+ if (hasAliasThis)
+ {
+ result = tryAliasThisCast();
+ if (result)
+ return;
+ }
+
+ if (t1b.size(e.loc) == tob.size(e.loc))
+ goto Lok;
+
+ auto ts = toAutoQualChars(e.type, t);
+ e.error("cannot cast expression `%s` of type `%s` to `%s` because of different sizes",
+ e.toChars(), ts[0], ts[1]);
+ result = ErrorExp.get();
+ return;
+ }
+
+ // Fat values vs. null or references
+ if (tob_isFV && (t1b.ty == Tnull || t1b_isR) || t1b_isFV && (tob.ty == Tnull || tob_isR))
+ {
+ if (tob.ty == Tpointer && t1b.ty == Tsarray)
+ {
+ // T[n] sa;
+ // cast(U*)sa; // ==> cast(U*)sa.ptr;
+ result = new AddrExp(e.loc, e, t);
+ return;
+ }
+ if (tob.ty == Tarray && t1b.ty == Tsarray)
+ {
+ // T[n] sa;
+ // cast(U[])sa; // ==> cast(U[])sa[];
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (auto v = expToVariable(e))
+ {
+ if (e.type.hasPointers() && !checkAddressVar(sc, e, v))
+ goto Lfail;
+ }
+ }
+ const fsize = t1b.nextOf().size();
+ const tsize = tob.nextOf().size();
+ if (fsize != tsize)
+ {
+ const dim = t1b.isTypeSArray().dim.toInteger();
+ if (tsize == 0 || (dim * fsize) % tsize != 0)
+ {
+ e.error("cannot cast expression `%s` of type `%s` to `%s` since sizes don't line up",
+ e.toChars(), e.type.toChars(), t.toChars());
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ goto Lok;
+ }
+ goto Lfail;
+ }
+
+ /* For references, any reinterpret casts are allowed to same 'ty' type.
+ * T* to U*
+ * R1 function(P1) to R2 function(P2)
+ * R1 delegate(P1) to R2 delegate(P2)
+ * T[] to U[]
+ * V1[K1] to V2[K2]
+ * class/interface A to B (will be a dynamic cast if possible)
+ */
+ if (tob.ty == t1b.ty && tob_isR && t1b_isR)
+ goto Lok;
+
+ // typeof(null) <-- non-null references or values
+ if (tob.ty == Tnull && t1b.ty != Tnull)
+ goto Lfail; // https://issues.dlang.org/show_bug.cgi?id=14629
+ // typeof(null) --> non-null references or arithmetic values
+ if (t1b.ty == Tnull && tob.ty != Tnull)
+ goto Lok;
+
+ // Check size mismatch of references.
+ // Tarray and Tdelegate are (void*).sizeof*2, but others have (void*).sizeof.
+ if (tob_isFR && t1b_isR || t1b_isFR && tob_isR)
+ {
+ if (tob.ty == Tpointer && t1b.ty == Tarray)
+ {
+ // T[] da;
+ // cast(U*)da; // ==> cast(U*)da.ptr;
+ goto Lok;
+ }
+ if (tob.ty == Tpointer && t1b.ty == Tdelegate)
+ {
+ // void delegate() dg;
+ // cast(U*)dg; // ==> cast(U*)dg.ptr;
+ // Note that it happens even when U is a Tfunction!
+ e.deprecation("casting from %s to %s is deprecated", e.type.toChars(), t.toChars());
+ goto Lok;
+ }
+ goto Lfail;
+ }
+
+ if (t1b.ty == Tvoid && tob.ty != Tvoid)
+ {
+ Lfail:
+ /* if the cast cannot be performed, maybe there is an alias
+ * this that can be used for casting.
+ */
+ if (hasAliasThis)
+ {
+ result = tryAliasThisCast();
+ if (result)
+ return;
+ }
+ e.error("cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars());
+ result = ErrorExp.get();
+ return;
+ }
+
+ Lok:
+ result = new CastExp(e.loc, e, t);
+ result.type = t; // Don't call semantic()
+ //printf("Returning: %s\n", result.toChars());
+ }
+
+ override void visit(ErrorExp e)
+ {
+ result = e;
+ }
+
+ override void visit(RealExp e)
+ {
+ if (!e.type.equals(t))
+ {
+ if ((e.type.isreal() && t.isreal()) || (e.type.isimaginary() && t.isimaginary()))
+ {
+ result = e.copy();
+ result.type = t;
+ }
+ else
+ visit(cast(Expression)e);
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(ComplexExp e)
+ {
+ if (!e.type.equals(t))
+ {
+ if (e.type.iscomplex() && t.iscomplex())
+ {
+ result = e.copy();
+ result.type = t;
+ }
+ else
+ visit(cast(Expression)e);
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ visit(cast(Expression)e);
+ if (result.op == TOK.structLiteral)
+ (cast(StructLiteralExp)result).stype = t; // commit type
+ }
+
+ override void visit(StringExp e)
+ {
+ /* This follows copy-on-write; any changes to 'this'
+ * will result in a copy.
+ * The this.string member is considered immutable.
+ */
+ int copied = 0;
+
+ //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t.toChars(), e.toChars(), e.committed);
+
+ if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid)
+ {
+ e.error("cannot convert string literal to `void*`");
+ result = ErrorExp.get();
+ return;
+ }
+
+ StringExp se = e;
+ if (!e.committed)
+ {
+ se = cast(StringExp)e.copy();
+ se.committed = 1;
+ copied = 1;
+ }
+
+ if (e.type.equals(t))
+ {
+ result = se;
+ return;
+ }
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ //printf("\ttype = %s\n", e.type.toChars());
+ if (tb.ty == Tdelegate && typeb.ty != Tdelegate)
+ {
+ visit(cast(Expression)e);
+ return;
+ }
+
+ if (typeb.equals(tb))
+ {
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+ se.type = t;
+ result = se;
+ return;
+ }
+
+ /* Handle reinterpret casts:
+ * cast(wchar[3])"abcd"c --> [\u6261, \u6463, \u0000]
+ * cast(wchar[2])"abcd"c --> [\u6261, \u6463]
+ * cast(wchar[1])"abcd"c --> [\u6261]
+ * cast(char[4])"a" --> ['a', 0, 0, 0]
+ */
+ if (e.committed && tb.ty == Tsarray && typeb.ty == Tarray)
+ {
+ se = cast(StringExp)e.copy();
+ d_uns64 szx = tb.nextOf().size();
+ assert(szx <= 255);
+ se.sz = cast(ubyte)szx;
+ se.len = cast(size_t)tb.isTypeSArray().dim.toInteger();
+ se.committed = 1;
+ se.type = t;
+
+ /* If larger than source, pad with zeros.
+ */
+ const fullSize = (se.len + 1) * se.sz; // incl. terminating 0
+ if (fullSize > (e.len + 1) * e.sz)
+ {
+ void* s = mem.xmalloc(fullSize);
+ const srcSize = e.len * e.sz;
+ const data = se.peekData();
+ memcpy(s, data.ptr, srcSize);
+ memset(s + srcSize, 0, fullSize - srcSize);
+ se.setData(s, se.len, se.sz);
+ }
+ result = se;
+ return;
+ }
+
+ if (tb.ty != Tsarray && tb.ty != Tarray && tb.ty != Tpointer)
+ {
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+ goto Lcast;
+ }
+ if (typeb.ty != Tsarray && typeb.ty != Tarray && typeb.ty != Tpointer)
+ {
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+ goto Lcast;
+ }
+
+ if (typeb.nextOf().size() == tb.nextOf().size())
+ {
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+ if (tb.ty == Tsarray)
+ goto L2; // handle possible change in static array dimension
+ se.type = t;
+ result = se;
+ return;
+ }
+
+ if (e.committed)
+ goto Lcast;
+
+ auto X(T, U)(T tf, U tt)
+ {
+ return (cast(int)tf * 256 + cast(int)tt);
+ }
+
+ {
+ OutBuffer buffer;
+ size_t newlen = 0;
+ int tfty = typeb.nextOf().toBasetype().ty;
+ int ttty = tb.nextOf().toBasetype().ty;
+ switch (X(tfty, ttty))
+ {
+ case X(Tchar, Tchar):
+ case X(Twchar, Twchar):
+ case X(Tdchar, Tdchar):
+ break;
+
+ case X(Tchar, Twchar):
+ for (size_t u = 0; u < e.len;)
+ {
+ dchar c;
+ if (const s = utf_decodeChar(se.peekString(), u, c))
+ e.error("%.*s", cast(int)s.length, s.ptr);
+ else
+ buffer.writeUTF16(c);
+ }
+ newlen = buffer.length / 2;
+ buffer.writeUTF16(0);
+ goto L1;
+
+ case X(Tchar, Tdchar):
+ for (size_t u = 0; u < e.len;)
+ {
+ dchar c;
+ if (const s = utf_decodeChar(se.peekString(), u, c))
+ e.error("%.*s", cast(int)s.length, s.ptr);
+ buffer.write4(c);
+ newlen++;
+ }
+ buffer.write4(0);
+ goto L1;
+
+ case X(Twchar, Tchar):
+ for (size_t u = 0; u < e.len;)
+ {
+ dchar c;
+ if (const s = utf_decodeWchar(se.peekWstring(), u, c))
+ e.error("%.*s", cast(int)s.length, s.ptr);
+ else
+ buffer.writeUTF8(c);
+ }
+ newlen = buffer.length;
+ buffer.writeUTF8(0);
+ goto L1;
+
+ case X(Twchar, Tdchar):
+ for (size_t u = 0; u < e.len;)
+ {
+ dchar c;
+ if (const s = utf_decodeWchar(se.peekWstring(), u, c))
+ e.error("%.*s", cast(int)s.length, s.ptr);
+ buffer.write4(c);
+ newlen++;
+ }
+ buffer.write4(0);
+ goto L1;
+
+ case X(Tdchar, Tchar):
+ for (size_t u = 0; u < e.len; u++)
+ {
+ uint c = se.peekDstring()[u];
+ if (!utf_isValidDchar(c))
+ e.error("invalid UCS-32 char \\U%08x", c);
+ else
+ buffer.writeUTF8(c);
+ newlen++;
+ }
+ newlen = buffer.length;
+ buffer.writeUTF8(0);
+ goto L1;
+
+ case X(Tdchar, Twchar):
+ for (size_t u = 0; u < e.len; u++)
+ {
+ uint c = se.peekDstring()[u];
+ if (!utf_isValidDchar(c))
+ e.error("invalid UCS-32 char \\U%08x", c);
+ else
+ buffer.writeUTF16(c);
+ newlen++;
+ }
+ newlen = buffer.length / 2;
+ buffer.writeUTF16(0);
+ goto L1;
+
+ L1:
+ if (!copied)
+ {
+ se = cast(StringExp)e.copy();
+ copied = 1;
+ }
+
+ {
+ d_uns64 szx = tb.nextOf().size();
+ assert(szx <= 255);
+ se.setData(buffer.extractSlice().ptr, newlen, cast(ubyte)szx);
+ }
+ break;
+
+ default:
+ assert(typeb.nextOf().size() != tb.nextOf().size());
+ goto Lcast;
+ }
+ }
+ L2:
+ assert(copied);
+
+ // See if need to truncate or extend the literal
+ if (auto tsa = tb.isTypeSArray())
+ {
+ size_t dim2 = cast(size_t)tsa.dim.toInteger();
+ //printf("dim from = %d, to = %d\n", (int)se.len, (int)dim2);
+
+ // Changing dimensions
+ if (dim2 != se.len)
+ {
+ // Copy when changing the string literal
+ const newsz = se.sz;
+ const d = (dim2 < se.len) ? dim2 : se.len;
+ void* s = mem.xmalloc((dim2 + 1) * newsz);
+ memcpy(s, se.peekData().ptr, d * newsz);
+ // Extend with 0, add terminating 0
+ memset(s + d * newsz, 0, (dim2 + 1 - d) * newsz);
+ se.setData(s, dim2, newsz);
+ }
+ }
+ se.type = t;
+ result = se;
+ return;
+
+ Lcast:
+ result = new CastExp(e.loc, se, t);
+ result.type = t; // so semantic() won't be run on e
+ }
+
+ override void visit(AddrExp e)
+ {
+ version (none)
+ {
+ printf("AddrExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ result = e;
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.equals(typeb))
+ {
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (e.e1.op == TOK.overloadSet &&
+ (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
+ {
+ OverExp eo = cast(OverExp)e.e1;
+ FuncDeclaration f = null;
+ for (size_t i = 0; i < eo.vars.a.dim; i++)
+ {
+ auto s = eo.vars.a[i];
+ auto f2 = s.isFuncDeclaration();
+ assert(f2);
+ if (f2.overloadExactMatch(tb.nextOf()))
+ {
+ if (f)
+ {
+ /* Error if match in more than one overload set,
+ * even if one is a 'better' match than the other.
+ */
+ ScopeDsymbol.multiplyDefined(e.loc, f, f2);
+ }
+ else
+ f = f2;
+ }
+ }
+ if (f)
+ {
+ f.tookAddressOf++;
+ auto se = new SymOffExp(e.loc, f, 0, false);
+ auto se2 = se.expressionSemantic(sc);
+ // Let SymOffExp::castTo() do the heavy lifting
+ visit(se2);
+ return;
+ }
+ }
+
+ if (e.e1.op == TOK.variable &&
+ typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
+ tb.ty == Tpointer && tb.nextOf().ty == Tfunction)
+ {
+ auto ve = cast(VarExp)e.e1;
+ auto f = ve.var.isFuncDeclaration();
+ if (f)
+ {
+ assert(f.isImportedSymbol());
+ f = f.overloadExactMatch(tb.nextOf());
+ if (f)
+ {
+ result = new VarExp(e.loc, f, false);
+ result.type = f.type;
+ result = new AddrExp(e.loc, result, t);
+ return;
+ }
+ }
+ }
+
+ if (auto f = isFuncAddress(e))
+ {
+ if (f.checkForwardRef(e.loc))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(TupleExp e)
+ {
+ if (e.type.equals(t))
+ {
+ result = e;
+ return;
+ }
+
+ TupleExp te = e.copy().isTupleExp();
+ te.e0 = e.e0 ? e.e0.copy() : null;
+ te.exps = e.exps.copy();
+ for (size_t i = 0; i < te.exps.dim; i++)
+ {
+ Expression ex = (*te.exps)[i];
+ ex = ex.castTo(sc, t);
+ (*te.exps)[i] = ex;
+ }
+ result = te;
+
+ /* Questionable behavior: In here, result.type is not set to t.
+ * Therefoe:
+ * TypeTuple!(int, int) values;
+ * auto values2 = cast(long)values;
+ * // typeof(values2) == TypeTuple!(int, int) !!
+ *
+ * Only when the casted tuple is immediately expanded, it would work.
+ * auto arr = [cast(long)values];
+ * // typeof(arr) == long[]
+ */
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ version (none)
+ {
+ printf("ArrayLiteralExp::castTo(this=%s, type=%s, => %s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+
+ ArrayLiteralExp ae = e;
+
+ Type tb = t.toBasetype();
+ if (tb.ty == Tarray && global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (checkArrayLiteralEscape(sc, ae, false))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ if (e.type == t)
+ {
+ result = e;
+ return;
+ }
+ Type typeb = e.type.toBasetype();
+
+ if ((tb.ty == Tarray || tb.ty == Tsarray) &&
+ (typeb.ty == Tarray || typeb.ty == Tsarray))
+ {
+ if (tb.nextOf().toBasetype().ty == Tvoid && typeb.nextOf().toBasetype().ty != Tvoid)
+ {
+ // Don't do anything to cast non-void[] to void[]
+ }
+ else if (typeb.ty == Tsarray && typeb.nextOf().toBasetype().ty == Tvoid)
+ {
+ // Don't do anything for casting void[n] to others
+ }
+ else
+ {
+ if (auto tsa = tb.isTypeSArray())
+ {
+ if (e.elements.dim != tsa.dim.toInteger())
+ goto L1;
+ }
+
+ ae = cast(ArrayLiteralExp)e.copy();
+ if (e.basis)
+ ae.basis = e.basis.castTo(sc, tb.nextOf());
+ ae.elements = e.elements.copy();
+ for (size_t i = 0; i < e.elements.dim; i++)
+ {
+ Expression ex = (*e.elements)[i];
+ if (!ex)
+ continue;
+ ex = ex.castTo(sc, tb.nextOf());
+ (*ae.elements)[i] = ex;
+ }
+ ae.type = t;
+ result = ae;
+ return;
+ }
+ }
+ else if (tb.ty == Tpointer && typeb.ty == Tsarray)
+ {
+ Type tp = typeb.nextOf().pointerTo();
+ if (!tp.equals(ae.type))
+ {
+ ae = cast(ArrayLiteralExp)e.copy();
+ ae.type = tp;
+ }
+ }
+ else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray))
+ {
+ // Convert array literal to vector type
+ TypeVector tv = tb.isTypeVector();
+ TypeSArray tbase = tv.basetype.isTypeSArray();
+ assert(tbase.ty == Tsarray);
+ const edim = e.elements.dim;
+ const tbasedim = tbase.dim.toInteger();
+ if (edim > tbasedim)
+ goto L1;
+
+ ae = e.copy().isArrayLiteralExp();
+ ae.type = tbase; // https://issues.dlang.org/show_bug.cgi?id=12642
+ ae.elements = e.elements.copy();
+ Type telement = tv.elementType();
+ foreach (i; 0 .. edim)
+ {
+ Expression ex = (*e.elements)[i];
+ ex = ex.castTo(sc, telement);
+ (*ae.elements)[i] = ex;
+ }
+ // Fill in the rest with the default initializer
+ ae.elements.setDim(cast(size_t)tbasedim);
+ foreach (i; edim .. cast(size_t)tbasedim)
+ {
+ Expression ex = typeb.nextOf.defaultInitLiteral(e.loc);
+ ex = ex.castTo(sc, telement);
+ (*ae.elements)[i] = ex;
+ }
+ Expression ev = new VectorExp(e.loc, ae, tb);
+ ev = ev.expressionSemantic(sc);
+ result = ev;
+ return;
+ }
+ L1:
+ visit(cast(Expression)ae);
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ //printf("AssocArrayLiteralExp::castTo(this=%s, type=%s, => %s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ if (e.type == t)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.ty == Taarray && typeb.ty == Taarray &&
+ tb.nextOf().toBasetype().ty != Tvoid)
+ {
+ AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e.copy();
+ ae.keys = e.keys.copy();
+ ae.values = e.values.copy();
+ assert(e.keys.dim == e.values.dim);
+ for (size_t i = 0; i < e.keys.dim; i++)
+ {
+ Expression ex = (*e.values)[i];
+ ex = ex.castTo(sc, tb.nextOf());
+ (*ae.values)[i] = ex;
+
+ ex = (*e.keys)[i];
+ ex = ex.castTo(sc, tb.isTypeAArray().index);
+ (*ae.keys)[i] = ex;
+ }
+ ae.type = t;
+ result = ae;
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(SymOffExp e)
+ {
+ version (none)
+ {
+ printf("SymOffExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ if (e.type == t && !e.hasOverloads)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.equals(typeb))
+ {
+ result = e.copy();
+ result.type = t;
+ (cast(SymOffExp)result).hasOverloads = false;
+ return;
+ }
+
+ // Look for pointers to functions where the functions are overloaded.
+ if (e.hasOverloads &&
+ typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
+ (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
+ {
+ FuncDeclaration f = e.var.isFuncDeclaration();
+ f = f ? f.overloadExactMatch(tb.nextOf()) : null;
+ if (f)
+ {
+ if (tb.ty == Tdelegate)
+ {
+ if (f.needThis() && hasThis(sc))
+ {
+ result = new DelegateExp(e.loc, new ThisExp(e.loc), f, false);
+ result = result.expressionSemantic(sc);
+ }
+ else if (f.needThis())
+ {
+ e.error("no `this` to create delegate for `%s`", f.toChars());
+ result = ErrorExp.get();
+ return;
+ }
+ else if (f.isNested())
+ {
+ result = new DelegateExp(e.loc, IntegerExp.literal!0, f, false);
+ result = result.expressionSemantic(sc);
+ }
+ else
+ {
+ e.error("cannot cast from function pointer to delegate");
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ else
+ {
+ result = new SymOffExp(e.loc, f, 0, false);
+ result.type = t;
+ }
+ f.tookAddressOf++;
+ return;
+ }
+ }
+
+ if (auto f = isFuncAddress(e))
+ {
+ if (f.checkForwardRef(e.loc))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(DelegateExp e)
+ {
+ version (none)
+ {
+ printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars());
+ }
+ __gshared const(char)* msg = "cannot form delegate due to covariant return type";
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (tb.equals(typeb) && !e.hasOverloads)
+ {
+ int offset;
+ e.func.tookAddressOf++;
+ if (e.func.tintro && e.func.tintro.nextOf().isBaseOf(e.func.type.nextOf(), &offset) && offset)
+ e.error("%s", msg);
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+
+ // Look for delegates to functions where the functions are overloaded.
+ if (typeb.ty == Tdelegate && tb.ty == Tdelegate)
+ {
+ if (e.func)
+ {
+ auto f = e.func.overloadExactMatch(tb.nextOf());
+ if (f)
+ {
+ int offset;
+ if (f.tintro && f.tintro.nextOf().isBaseOf(f.type.nextOf(), &offset) && offset)
+ e.error("%s", msg);
+ if (f != e.func) // if address not already marked as taken
+ f.tookAddressOf++;
+ result = new DelegateExp(e.loc, e.e1, f, false, e.vthis2);
+ result.type = t;
+ return;
+ }
+ if (e.func.tintro)
+ e.error("%s", msg);
+ }
+ }
+
+ if (auto f = isFuncAddress(e))
+ {
+ if (f.checkForwardRef(e.loc))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ visit(cast(Expression)e);
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp::castTo type = %s, t = %s\n", e.type.toChars(), t.toChars());
+ FuncExp fe;
+ if (e.matchType(t, sc, &fe, 1) > MATCH.nomatch)
+ {
+ result = fe;
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(CondExp e)
+ {
+ if (!e.type.equals(t))
+ {
+ result = new CondExp(e.loc, e.econd, e.e1.castTo(sc, t), e.e2.castTo(sc, t));
+ result.type = t;
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(CommaExp e)
+ {
+ Expression e2c = e.e2.castTo(sc, t);
+
+ if (e2c != e.e2)
+ {
+ result = new CommaExp(e.loc, e.e1, e2c);
+ result.type = e2c.type;
+ }
+ else
+ {
+ result = e;
+ result.type = e.e2.type;
+ }
+ }
+
+ override void visit(SliceExp e)
+ {
+ //printf("SliceExp::castTo e = %s, type = %s, t = %s\n", e.toChars(), e.type.toChars(), t.toChars());
+
+ Type tb = t.toBasetype();
+ Type typeb = e.type.toBasetype();
+
+ if (e.type.equals(t) || typeb.ty != Tarray ||
+ (tb.ty != Tarray && tb.ty != Tsarray))
+ {
+ visit(cast(Expression)e);
+ return;
+ }
+
+ if (tb.ty == Tarray)
+ {
+ if (typeb.nextOf().equivalent(tb.nextOf()))
+ {
+ // T[] to const(T)[]
+ result = e.copy();
+ result.type = t;
+ }
+ else
+ {
+ visit(cast(Expression)e);
+ }
+ return;
+ }
+
+ // Handle the cast from Tarray to Tsarray with CT-known slicing
+
+ TypeSArray tsa = toStaticArrayType(e).isTypeSArray();
+ if (tsa && tsa.size(e.loc) == tb.size(e.loc))
+ {
+ /* Match if the sarray sizes are equal:
+ * T[a .. b] to const(T)[b-a]
+ * T[a .. b] to U[dim] if (T.sizeof*(b-a) == U.sizeof*dim)
+ *
+ * If a SliceExp has Tsarray, it will become lvalue.
+ * That's handled in SliceExp::isLvalue and toLvalue
+ */
+ result = e.copy();
+ result.type = t;
+ return;
+ }
+ if (tsa && tsa.dim.equals(tb.isTypeSArray().dim))
+ {
+ /* Match if the dimensions are equal
+ * with the implicit conversion of e.e1:
+ * cast(float[2]) [2.0, 1.0, 0.0][0..2];
+ */
+ Type t1b = e.e1.type.toBasetype();
+ if (t1b.ty == Tsarray)
+ t1b = tb.nextOf().sarrayOf(t1b.isTypeSArray().dim.toInteger());
+ else if (t1b.ty == Tarray)
+ t1b = tb.nextOf().arrayOf();
+ else if (t1b.ty == Tpointer)
+ t1b = tb.nextOf().pointerTo();
+ else
+ assert(0);
+ if (e.e1.implicitConvTo(t1b) > MATCH.nomatch)
+ {
+ Expression e1x = e.e1.implicitCastTo(sc, t1b);
+ assert(e1x.op != TOK.error);
+ e = cast(SliceExp)e.copy();
+ e.e1 = e1x;
+ e.type = t;
+ result = e;
+ return;
+ }
+ }
+ auto ts = toAutoQualChars(tsa ? tsa : e.type, t);
+ e.error("cannot cast expression `%s` of type `%s` to `%s`",
+ e.toChars(), ts[0], ts[1]);
+ result = ErrorExp.get();
+ }
+ }
+
+ // Casting to noreturn isn't an actual cast
+ // Rewrite cast(<qual> noreturn) <exp>
+ // as <exp>, assert(false)
+ if (t.isTypeNoreturn())
+ {
+ // Don't generate an unreachable assert(false) if e will abort
+ if (e.type.isTypeNoreturn())
+ {
+ // Paint e to accomodate for different type qualifiers
+ e.type = t;
+ return e;
+ }
+
+ auto ini = t.defaultInitLiteral(e.loc);
+ return Expression.combine(e, ini);
+ }
+
+ scope CastTo v = new CastTo(sc, t);
+ e.accept(v);
+ return v.result;
+}
+
+/****************************************
+ * Set type inference target
+ * t Target type
+ * flag 1: don't put an error when inference fails
+ */
+Expression inferType(Expression e, Type t, int flag = 0)
+{
+ Expression visitAle(ArrayLiteralExp ale)
+ {
+ Type tb = t.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ Type tn = tb.nextOf();
+ if (ale.basis)
+ ale.basis = inferType(ale.basis, tn, flag);
+ for (size_t i = 0; i < ale.elements.dim; i++)
+ {
+ if (Expression e = (*ale.elements)[i])
+ {
+ e = inferType(e, tn, flag);
+ (*ale.elements)[i] = e;
+ }
+ }
+ }
+ return ale;
+ }
+
+ Expression visitAar(AssocArrayLiteralExp aale)
+ {
+ Type tb = t.toBasetype();
+ if (auto taa = tb.isTypeAArray())
+ {
+ Type ti = taa.index;
+ Type tv = taa.nextOf();
+ for (size_t i = 0; i < aale.keys.dim; i++)
+ {
+ if (Expression e = (*aale.keys)[i])
+ {
+ e = inferType(e, ti, flag);
+ (*aale.keys)[i] = e;
+ }
+ }
+ for (size_t i = 0; i < aale.values.dim; i++)
+ {
+ if (Expression e = (*aale.values)[i])
+ {
+ e = inferType(e, tv, flag);
+ (*aale.values)[i] = e;
+ }
+ }
+ }
+ return aale;
+ }
+
+ Expression visitFun(FuncExp fe)
+ {
+ //printf("FuncExp::inferType('%s'), to=%s\n", fe.type ? fe.type.toChars() : "null", t.toChars());
+ if (t.ty == Tdelegate || t.ty == Tpointer && t.nextOf().ty == Tfunction)
+ {
+ fe.fd.treq = t;
+ }
+ return fe;
+ }
+
+ Expression visitTer(CondExp ce)
+ {
+ Type tb = t.toBasetype();
+ ce.e1 = inferType(ce.e1, tb, flag);
+ ce.e2 = inferType(ce.e2, tb, flag);
+ return ce;
+ }
+
+ if (t) switch (e.op)
+ {
+ case TOK.arrayLiteral: return visitAle(cast(ArrayLiteralExp) e);
+ case TOK.assocArrayLiteral: return visitAar(cast(AssocArrayLiteralExp) e);
+ case TOK.function_: return visitFun(cast(FuncExp) e);
+ case TOK.question: return visitTer(cast(CondExp) e);
+ default:
+ }
+ return e;
+}
+
+/****************************************
+ * Scale addition/subtraction to/from pointer.
+ */
+Expression scaleFactor(BinExp be, Scope* sc)
+{
+ Type t1b = be.e1.type.toBasetype();
+ Type t2b = be.e2.type.toBasetype();
+ Expression eoff;
+
+ if (t1b.ty == Tpointer && t2b.isintegral())
+ {
+ // Need to adjust operator by the stride
+ // Replace (ptr + int) with (ptr + (int * stride))
+ Type t = Type.tptrdiff_t;
+
+ d_uns64 stride = t1b.nextOf().size(be.loc);
+ if (!t.equals(t2b))
+ be.e2 = be.e2.castTo(sc, t);
+ eoff = be.e2;
+ be.e2 = new MulExp(be.loc, be.e2, new IntegerExp(Loc.initial, stride, t));
+ be.e2.type = t;
+ be.type = be.e1.type;
+ }
+ else if (t2b.ty == Tpointer && t1b.isintegral())
+ {
+ // Need to adjust operator by the stride
+ // Replace (int + ptr) with (ptr + (int * stride))
+ Type t = Type.tptrdiff_t;
+ Expression e;
+
+ d_uns64 stride = t2b.nextOf().size(be.loc);
+ if (!t.equals(t1b))
+ e = be.e1.castTo(sc, t);
+ else
+ e = be.e1;
+ eoff = e;
+ e = new MulExp(be.loc, e, new IntegerExp(Loc.initial, stride, t));
+ e.type = t;
+ be.type = be.e2.type;
+ be.e1 = be.e2;
+ be.e2 = e;
+ }
+ else
+ assert(0);
+
+ if (sc.func && !sc.intypeof)
+ {
+ eoff = eoff.optimize(WANTvalue);
+ if (eoff.op == TOK.int64 && eoff.toInteger() == 0)
+ {
+ }
+ else if (sc.func.setUnsafe())
+ {
+ be.error("pointer arithmetic not allowed in @safe functions");
+ return ErrorExp.get();
+ }
+ }
+
+ return be;
+}
+
+/**************************************
+ * Return true if e is an empty array literal with dimensionality
+ * equal to or less than type of other array.
+ * [], [[]], [[[]]], etc.
+ * I.e., make sure that [1,2] is compatible with [],
+ * [[1,2]] is compatible with [[]], etc.
+ */
+private bool isVoidArrayLiteral(Expression e, Type other)
+{
+ while (e.op == TOK.arrayLiteral && e.type.ty == Tarray && ((cast(ArrayLiteralExp)e).elements.dim == 1))
+ {
+ auto ale = cast(ArrayLiteralExp)e;
+ e = ale[0];
+ if (other.ty == Tsarray || other.ty == Tarray)
+ other = other.nextOf();
+ else
+ return false;
+ }
+ if (other.ty != Tsarray && other.ty != Tarray)
+ return false;
+ Type t = e.type;
+ return (e.op == TOK.arrayLiteral && t.ty == Tarray && t.nextOf().ty == Tvoid && (cast(ArrayLiteralExp)e).elements.dim == 0);
+}
+
+/**
+ * Merge types of `e1` and `e2` into a common subset
+ *
+ * Parameters `e1` and `e2` will be rewritten in place as needed.
+ *
+ * Params:
+ * sc = Current scope
+ * op = Operator such as `e1 op e2`. In practice, either TOK.question
+ * or one of the binary operator.
+ * pe1 = The LHS of the operation, will be rewritten
+ * pe2 = The RHS of the operation, will be rewritten
+ *
+ * Returns:
+ * The resulting type in case of success, `null` in case of error
+ */
+Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
+{
+ //printf("typeMerge() %s op %s\n", e1.toChars(), e2.toChars());
+
+ Expression e1 = pe1;
+ Expression e2 = pe2;
+
+ Type Lret(Type result)
+ {
+ pe1 = e1;
+ pe2 = e2;
+
+ version (none)
+ {
+ printf("-typeMerge() %s op %s\n", e1.toChars(), e2.toChars());
+ if (e1.type)
+ printf("\tt1 = %s\n", e1.type.toChars());
+ if (e2.type)
+ printf("\tt2 = %s\n", e2.type.toChars());
+ printf("\ttype = %s\n", result.toChars());
+ }
+ return result;
+ }
+
+ /// Converts one of the expression too the other
+ Type convert(ref Expression from, Type to)
+ {
+ from = from.castTo(sc, to);
+ return Lret(to);
+ }
+
+ /// Converts both expression to a third type
+ Type coerce(Type towards)
+ {
+ e1 = e1.castTo(sc, towards);
+ e2 = e2.castTo(sc, towards);
+ return Lret(towards);
+ }
+
+ Type t1b = e1.type.toBasetype();
+ Type t2b = e2.type.toBasetype();
+
+ if (op != TOK.question || t1b.ty != t2b.ty && (t1b.isTypeBasic() && t2b.isTypeBasic()))
+ {
+ if (op == TOK.question && t1b.ty.isSomeChar() && t2b.ty.isSomeChar())
+ {
+ e1 = e1.castTo(sc, Type.tdchar);
+ e2 = e2.castTo(sc, Type.tdchar);
+ }
+ else
+ {
+ e1 = integralPromotions(e1, sc);
+ e2 = integralPromotions(e2, sc);
+ }
+ }
+
+ MATCH m;
+ Type t1 = e1.type;
+ Type t2 = e2.type;
+ assert(t1);
+ Type t = t1;
+
+ /* The start type of alias this type recursion.
+ * In following case, we should save A, and stop recursion
+ * if it appears again.
+ * X -> Y -> [A] -> B -> A -> B -> ...
+ */
+ Type att1 = null;
+ Type att2 = null;
+
+ if (t1.mod != t2.mod &&
+ t1.ty == Tenum && t2.ty == Tenum &&
+ t1.isTypeEnum().sym == t2.isTypeEnum().sym)
+ {
+ ubyte mod = MODmerge(t1.mod, t2.mod);
+ t1 = t1.castMod(mod);
+ t2 = t2.castMod(mod);
+ }
+
+Lagain:
+ t1b = t1.toBasetype();
+ t2b = t2.toBasetype();
+
+ const ty = implicitConvCommonTy(t1b.ty, t2b.ty);
+ if (ty != Terror)
+ {
+ const ty1 = implicitConvTy1(t1b.ty, t2b.ty);
+ const ty2 = implicitConvTy1(t2b.ty, t1b.ty);
+
+ if (t1b.ty == ty1) // if no promotions
+ {
+ if (t1.equals(t2))
+ return Lret(t1);
+
+ if (t1b.equals(t2b))
+ return Lret(t1b);
+ }
+
+ t1 = Type.basic[ty1];
+ t2 = Type.basic[ty2];
+ e1 = e1.castTo(sc, t1);
+ e2 = e2.castTo(sc, t2);
+ return Lret(Type.basic[ty]);
+ }
+
+ t1 = t1b;
+ t2 = t2b;
+
+ if (t1.ty == Ttuple || t2.ty == Ttuple)
+ return null;
+
+ if (t1.equals(t2))
+ {
+ // merging can not result in new enum type
+ if (t.ty == Tenum)
+ return Lret(t1b);
+ return Lret(t);
+ }
+
+ if ((t1.ty == Tpointer && t2.ty == Tpointer) || (t1.ty == Tdelegate && t2.ty == Tdelegate))
+ {
+ // Bring pointers to compatible type
+ Type t1n = t1.nextOf();
+ Type t2n = t2.nextOf();
+
+ if (t1n.equals(t2n))
+ return Lret(t);
+
+ if (t1n.ty == Tvoid) // pointers to void are always compatible
+ return Lret(t2);
+
+ if (t2n.ty == Tvoid)
+ return Lret(t);
+
+ if (t1.implicitConvTo(t2))
+ return convert(e1, t2);
+
+ if (t2.implicitConvTo(t1))
+ return convert(e2, t1);
+
+ if (t1n.ty == Tfunction && t2n.ty == Tfunction)
+ {
+ TypeFunction tf1 = t1n.isTypeFunction();
+ TypeFunction tf2 = t2n.isTypeFunction();
+ tf1.purityLevel();
+ tf2.purityLevel();
+
+ TypeFunction d = tf1.syntaxCopy();
+
+ if (tf1.purity != tf2.purity)
+ d.purity = PURE.impure;
+ assert(d.purity != PURE.fwdref);
+
+ d.isnothrow = (tf1.isnothrow && tf2.isnothrow);
+ d.isnogc = (tf1.isnogc && tf2.isnogc);
+
+ if (tf1.trust == tf2.trust)
+ d.trust = tf1.trust;
+ else if (tf1.trust <= TRUST.system || tf2.trust <= TRUST.system)
+ d.trust = TRUST.system;
+ else
+ d.trust = TRUST.trusted;
+
+ Type tx = (t1.ty == Tdelegate) ? new TypeDelegate(d) : d.pointerTo();
+ tx = tx.typeSemantic(e1.loc, sc);
+
+ if (t1.implicitConvTo(tx) && t2.implicitConvTo(tx))
+ return coerce(tx);
+ return null;
+ }
+
+ if (t1n.mod != t2n.mod)
+ {
+ if (!t1n.isImmutable() && !t2n.isImmutable() && t1n.isShared() != t2n.isShared())
+ return null;
+ ubyte mod = MODmerge(t1n.mod, t2n.mod);
+ t1 = t1n.castMod(mod).pointerTo();
+ t2 = t2n.castMod(mod).pointerTo();
+ t = t1;
+ goto Lagain;
+ }
+
+ if (t1n.ty == Tclass && t2n.ty == Tclass)
+ {
+ ClassDeclaration cd1 = t1n.isClassHandle();
+ ClassDeclaration cd2 = t2n.isClassHandle();
+ int offset;
+ if (cd1.isBaseOf(cd2, &offset))
+ {
+ if (offset)
+ e2 = e2.castTo(sc, t);
+ return Lret(t);
+ }
+
+ if (cd2.isBaseOf(cd1, &offset))
+ {
+ if (offset)
+ e1 = e1.castTo(sc, t2);
+ return Lret(t2);
+ }
+
+ return null;
+ }
+
+ t1 = t1n.constOf().pointerTo();
+ t2 = t2n.constOf().pointerTo();
+ if (t1.implicitConvTo(t2))
+ return convert(e1, t2);
+ if (t2.implicitConvTo(t1))
+ return convert(e2, t1);
+ return null;
+ }
+
+ if ((t1.ty == Tsarray || t1.ty == Tarray) && (e2.op == TOK.null_ && t2.ty == Tpointer && t2.nextOf().ty == Tvoid || e2.op == TOK.arrayLiteral && t2.ty == Tsarray && t2.nextOf().ty == Tvoid && t2.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e2, t1)))
+ {
+ /* (T[n] op void*) => T[]
+ * (T[] op void*) => T[]
+ * (T[n] op void[0]) => T[]
+ * (T[] op void[0]) => T[]
+ * (T[n] op void[]) => T[]
+ * (T[] op void[]) => T[]
+ */
+ return coerce(t1.nextOf().arrayOf());
+ }
+
+ if ((t2.ty == Tsarray || t2.ty == Tarray) && (e1.op == TOK.null_ && t1.ty == Tpointer && t1.nextOf().ty == Tvoid || e1.op == TOK.arrayLiteral && t1.ty == Tsarray && t1.nextOf().ty == Tvoid && t1.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e1, t2)))
+ {
+ /* (void* op T[n]) => T[]
+ * (void* op T[]) => T[]
+ * (void[0] op T[n]) => T[]
+ * (void[0] op T[]) => T[]
+ * (void[] op T[n]) => T[]
+ * (void[] op T[]) => T[]
+ */
+ return coerce(t2.nextOf().arrayOf());
+ }
+
+ if ((t1.ty == Tsarray || t1.ty == Tarray) && (m = t1.implicitConvTo(t2)) != MATCH.nomatch)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=7285
+ // Tsarray op [x, y, ...] should to be Tsarray
+ // https://issues.dlang.org/show_bug.cgi?id=14737
+ // Tsarray ~ [x, y, ...] should to be Tarray
+ if (t1.ty == Tsarray && e2.op == TOK.arrayLiteral && op != TOK.concatenate)
+ return convert(e2, t1);
+ if (m == MATCH.constant && (op == TOK.addAssign || op == TOK.minAssign || op == TOK.mulAssign || op == TOK.divAssign || op == TOK.modAssign || op == TOK.powAssign || op == TOK.andAssign || op == TOK.orAssign || op == TOK.xorAssign))
+ {
+ // Don't make the lvalue const
+ return Lret(t2);
+ }
+ return convert(e1, t2);
+ }
+
+ if ((t2.ty == Tsarray || t2.ty == Tarray) && t2.implicitConvTo(t1))
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=7285
+ // https://issues.dlang.org/show_bug.cgi?id=14737
+ if (t2.ty == Tsarray && e1.op == TOK.arrayLiteral && op != TOK.concatenate)
+ return convert(e1, t2);
+ return convert(e2, t1);
+ }
+
+ if ((t1.ty == Tsarray || t1.ty == Tarray || t1.ty == Tpointer) && (t2.ty == Tsarray || t2.ty == Tarray || t2.ty == Tpointer) && t1.nextOf().mod != t2.nextOf().mod)
+ {
+ /* If one is mutable and the other immutable, then retry
+ * with both of them as const
+ */
+ Type t1n = t1.nextOf();
+ Type t2n = t2.nextOf();
+ ubyte mod;
+ if (e1.op == TOK.null_ && e2.op != TOK.null_)
+ mod = t2n.mod;
+ else if (e1.op != TOK.null_ && e2.op == TOK.null_)
+ mod = t1n.mod;
+ else if (!t1n.isImmutable() && !t2n.isImmutable() && t1n.isShared() != t2n.isShared())
+ return null;
+ else
+ mod = MODmerge(t1n.mod, t2n.mod);
+
+ if (t1.ty == Tpointer)
+ t1 = t1n.castMod(mod).pointerTo();
+ else
+ t1 = t1n.castMod(mod).arrayOf();
+
+ if (t2.ty == Tpointer)
+ t2 = t2n.castMod(mod).pointerTo();
+ else
+ t2 = t2n.castMod(mod).arrayOf();
+ t = t1;
+ goto Lagain;
+ }
+
+ if (t1.ty == Tclass && t2.ty == Tclass)
+ {
+ if (t1.mod != t2.mod)
+ {
+ ubyte mod;
+ if (e1.op == TOK.null_ && e2.op != TOK.null_)
+ mod = t2.mod;
+ else if (e1.op != TOK.null_ && e2.op == TOK.null_)
+ mod = t1.mod;
+ else if (!t1.isImmutable() && !t2.isImmutable() && t1.isShared() != t2.isShared())
+ return null;
+ else
+ mod = MODmerge(t1.mod, t2.mod);
+ t1 = t1.castMod(mod);
+ t2 = t2.castMod(mod);
+ t = t1;
+ goto Lagain;
+ }
+ goto Lcc;
+ }
+
+ if (t1.ty == Tclass || t2.ty == Tclass)
+ {
+ Lcc:
+ while (1)
+ {
+ MATCH i1 = e2.implicitConvTo(t1);
+ MATCH i2 = e1.implicitConvTo(t2);
+
+ if (i1 && i2)
+ {
+ // We have the case of class vs. void*, so pick class
+ if (t1.ty == Tpointer)
+ i1 = MATCH.nomatch;
+ else if (t2.ty == Tpointer)
+ i2 = MATCH.nomatch;
+ }
+
+ if (i2)
+ return coerce(t2);
+ if (i1)
+ return coerce(t1);
+
+ if (t1.ty == Tclass && t2.ty == Tclass)
+ {
+ TypeClass tc1 = t1.isTypeClass();
+ TypeClass tc2 = t2.isTypeClass();
+
+ /* Pick 'tightest' type
+ */
+ ClassDeclaration cd1 = tc1.sym.baseClass;
+ ClassDeclaration cd2 = tc2.sym.baseClass;
+ if (cd1 && cd2)
+ {
+ t1 = cd1.type.castMod(t1.mod);
+ t2 = cd2.type.castMod(t2.mod);
+ }
+ else if (cd1)
+ t1 = cd1.type;
+ else if (cd2)
+ t2 = cd2.type;
+ else
+ return null;
+ }
+ else if (t1.ty == Tstruct && t1.isTypeStruct().sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att1, e1.type))
+ return null;
+ //printf("att tmerge(c || c) e1 = %s\n", e1.type.toChars());
+ e1 = resolveAliasThis(sc, e1);
+ t1 = e1.type;
+ continue;
+ }
+ else if (t2.ty == Tstruct && t2.isTypeStruct().sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att2, e2.type))
+ return null;
+ //printf("att tmerge(c || c) e2 = %s\n", e2.type.toChars());
+ e2 = resolveAliasThis(sc, e2);
+ t2 = e2.type;
+ continue;
+ }
+ else
+ return null;
+ }
+ }
+
+ if (t1.ty == Tstruct && t2.ty == Tstruct)
+ {
+ if (t1.mod != t2.mod)
+ {
+ if (!t1.isImmutable() && !t2.isImmutable() && t1.isShared() != t2.isShared())
+ return null;
+ ubyte mod = MODmerge(t1.mod, t2.mod);
+ t1 = t1.castMod(mod);
+ t2 = t2.castMod(mod);
+ t = t1;
+ goto Lagain;
+ }
+
+ TypeStruct ts1 = t1.isTypeStruct();
+ TypeStruct ts2 = t2.isTypeStruct();
+ if (ts1.sym != ts2.sym)
+ {
+ if (!ts1.sym.aliasthis && !ts2.sym.aliasthis)
+ return null;
+
+ MATCH i1 = MATCH.nomatch;
+ MATCH i2 = MATCH.nomatch;
+
+ Expression e1b = null;
+ Expression e2b = null;
+ if (ts2.sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att2, e2.type))
+ return null;
+ //printf("att tmerge(s && s) e2 = %s\n", e2.type.toChars());
+ e2b = resolveAliasThis(sc, e2);
+ i1 = e2b.implicitConvTo(t1);
+ }
+ if (ts1.sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att1, e1.type))
+ return null;
+ //printf("att tmerge(s && s) e1 = %s\n", e1.type.toChars());
+ e1b = resolveAliasThis(sc, e1);
+ i2 = e1b.implicitConvTo(t2);
+ }
+ if (i1 && i2)
+ return null;
+
+ if (i1)
+ return convert(e2, t1);
+ if (i2)
+ return convert(e1, t2);
+
+ if (e1b)
+ {
+ e1 = e1b;
+ t1 = e1b.type.toBasetype();
+ }
+ if (e2b)
+ {
+ e2 = e2b;
+ t2 = e2b.type.toBasetype();
+ }
+ t = t1;
+ goto Lagain;
+ }
+ }
+
+ if (t1.ty == Tstruct || t2.ty == Tstruct)
+ {
+ if (t1.ty == Tstruct && t1.isTypeStruct().sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att1, e1.type))
+ return null;
+ //printf("att tmerge(s || s) e1 = %s\n", e1.type.toChars());
+ e1 = resolveAliasThis(sc, e1);
+ t1 = e1.type;
+ t = t1;
+ goto Lagain;
+ }
+ if (t2.ty == Tstruct && t2.isTypeStruct().sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att2, e2.type))
+ return null;
+ //printf("att tmerge(s || s) e2 = %s\n", e2.type.toChars());
+ e2 = resolveAliasThis(sc, e2);
+ t2 = e2.type;
+ t = t2;
+ goto Lagain;
+ }
+ return null;
+ }
+
+ if ((e1.op == TOK.string_ || e1.op == TOK.null_) && e1.implicitConvTo(t2))
+ return convert(e1, t2);
+ if ((e2.op == TOK.string_ || e2.op == TOK.null_) && e2.implicitConvTo(t1))
+ return convert(e2, t1);
+ if (t1.ty == Tsarray && t2.ty == Tsarray && e2.implicitConvTo(t1.nextOf().arrayOf()))
+ return coerce(t1.nextOf().arrayOf());
+ if (t1.ty == Tsarray && t2.ty == Tsarray && e1.implicitConvTo(t2.nextOf().arrayOf()))
+ return coerce(t2.nextOf().arrayOf());
+
+ if (t1.ty == Tvector && t2.ty == Tvector)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13841
+ // all vector types should have no common types between
+ // different vectors, even though their sizes are same.
+ auto tv1 = t1.isTypeVector();
+ auto tv2 = t2.isTypeVector();
+ if (!tv1.basetype.equals(tv2.basetype))
+ return null;
+
+ goto LmodCompare;
+ }
+
+ if (t1.ty == Tvector && t2.ty != Tvector && e2.implicitConvTo(t1))
+ {
+ e2 = e2.castTo(sc, t1);
+ t2 = t1;
+ t = t1;
+ goto Lagain;
+ }
+
+ if (t2.ty == Tvector && t1.ty != Tvector && e1.implicitConvTo(t2))
+ {
+ e1 = e1.castTo(sc, t2);
+ t1 = t2;
+ t = t1;
+ goto Lagain;
+ }
+
+ if (t1.isintegral() && t2.isintegral())
+ {
+ if (t1.ty != t2.ty)
+ {
+ if (t1.ty == Tvector || t2.ty == Tvector)
+ return null;
+ e1 = integralPromotions(e1, sc);
+ e2 = integralPromotions(e2, sc);
+ t1 = e1.type;
+ t2 = e2.type;
+ goto Lagain;
+ }
+ assert(t1.ty == t2.ty);
+LmodCompare:
+ if (!t1.isImmutable() && !t2.isImmutable() && t1.isShared() != t2.isShared())
+ return null;
+ ubyte mod = MODmerge(t1.mod, t2.mod);
+
+ t1 = t1.castMod(mod);
+ t2 = t2.castMod(mod);
+ t = t1;
+ e1 = e1.castTo(sc, t);
+ e2 = e2.castTo(sc, t);
+ goto Lagain;
+ }
+
+ if (t1.ty == Tnull && t2.ty == Tnull)
+ {
+ ubyte mod = MODmerge(t1.mod, t2.mod);
+ return coerce(t1.castMod(mod));
+ }
+
+ if (t2.ty == Tnull && (t1.ty == Tpointer || t1.ty == Taarray || t1.ty == Tarray))
+ return convert(e2, t1);
+ if (t1.ty == Tnull && (t2.ty == Tpointer || t2.ty == Taarray || t2.ty == Tarray))
+ return convert(e1, t2);
+
+ if (t1.ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e1))
+ {
+ if (e2.implicitConvTo(t1.nextOf()))
+ {
+ // T[] op T
+ // T[] op cast(T)U
+ e2 = e2.castTo(sc, t1.nextOf());
+ return Lret(t1.nextOf().arrayOf());
+ }
+ if (t1.nextOf().implicitConvTo(e2.type))
+ {
+ // (cast(T)U)[] op T (https://issues.dlang.org/show_bug.cgi?id=12780)
+ // e1 is left as U[], it will be handled in arrayOp() later.
+ return Lret(e2.type.arrayOf());
+ }
+ if (t2.ty == Tarray && isArrayOpOperand(e2))
+ {
+ if (t1.nextOf().implicitConvTo(t2.nextOf()))
+ {
+ // (cast(T)U)[] op T[] (https://issues.dlang.org/show_bug.cgi?id=12780)
+ t = t2.nextOf().arrayOf();
+ // if cast won't be handled in arrayOp() later
+ if (!isArrayOpImplicitCast(t1.isTypeDArray(), t2.isTypeDArray()))
+ e1 = e1.castTo(sc, t);
+ return Lret(t);
+ }
+ if (t2.nextOf().implicitConvTo(t1.nextOf()))
+ {
+ // T[] op (cast(T)U)[] (https://issues.dlang.org/show_bug.cgi?id=12780)
+ // e2 is left as U[], it will be handled in arrayOp() later.
+ t = t1.nextOf().arrayOf();
+ // if cast won't be handled in arrayOp() later
+ if (!isArrayOpImplicitCast(t2.isTypeDArray(), t1.isTypeDArray()))
+ e2 = e2.castTo(sc, t);
+ return Lret(t);
+ }
+ return null;
+ }
+ return null;
+ }
+ else if (t2.ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e2))
+ {
+ if (e1.implicitConvTo(t2.nextOf()))
+ {
+ // T op T[]
+ // cast(T)U op T[]
+ e1 = e1.castTo(sc, t2.nextOf());
+ t = t2.nextOf().arrayOf();
+ }
+ else if (t2.nextOf().implicitConvTo(e1.type))
+ {
+ // T op (cast(T)U)[] (https://issues.dlang.org/show_bug.cgi?id=12780)
+ // e2 is left as U[], it will be handled in arrayOp() later.
+ t = e1.type.arrayOf();
+ }
+ else
+ return null;
+
+ //printf("test %s\n", Token::toChars(op));
+ e1 = e1.optimize(WANTvalue);
+ if (isCommutative(op) && e1.isConst())
+ {
+ /* Swap operands to minimize number of functions generated
+ */
+ //printf("swap %s\n", Token::toChars(op));
+ Expression tmp = e1;
+ e1 = e2;
+ e2 = tmp;
+ }
+ return Lret(t);
+ }
+
+ return null;
+}
+
+/************************************
+ * Bring leaves to common type.
+ * Returns:
+ * null on success, ErrorExp if error occurs
+ */
+Expression typeCombine(BinExp be, Scope* sc)
+{
+ Expression errorReturn()
+ {
+ Expression ex = be.incompatibleTypes();
+ if (ex.op == TOK.error)
+ return ex;
+ return ErrorExp.get();
+ }
+
+ Type t1 = be.e1.type.toBasetype();
+ Type t2 = be.e2.type.toBasetype();
+
+ if (be.op == TOK.min || be.op == TOK.add)
+ {
+ // struct+struct, and class+class are errors
+ if (t1.ty == Tstruct && t2.ty == Tstruct)
+ return errorReturn();
+ else if (t1.ty == Tclass && t2.ty == Tclass)
+ return errorReturn();
+ else if (t1.ty == Taarray && t2.ty == Taarray)
+ return errorReturn();
+ }
+
+ if (auto result = typeMerge(sc, be.op, be.e1, be.e2))
+ {
+ if (be.type is null)
+ be.type = result;
+ }
+ else
+ return errorReturn();
+
+ // If the types have no value, return an error
+ if (be.e1.op == TOK.error)
+ return be.e1;
+ if (be.e2.op == TOK.error)
+ return be.e2;
+ return null;
+}
+
+/***********************************
+ * Do integral promotions (convertchk).
+ * Don't convert <array of> to <pointer to>
+ */
+Expression integralPromotions(Expression e, Scope* sc)
+{
+ //printf("integralPromotions %s %s\n", e.toChars(), e.type.toChars());
+ switch (e.type.toBasetype().ty)
+ {
+ case Tvoid:
+ e.error("void has no value");
+ return ErrorExp.get();
+
+ case Tint8:
+ case Tuns8:
+ case Tint16:
+ case Tuns16:
+ case Tbool:
+ case Tchar:
+ case Twchar:
+ e = e.castTo(sc, Type.tint32);
+ break;
+
+ case Tdchar:
+ e = e.castTo(sc, Type.tuns32);
+ break;
+
+ default:
+ break;
+ }
+ return e;
+}
+
+/******************************************************
+ * This provides a transition from the non-promoting behavior
+ * of unary + - ~ to the C-like integral promotion behavior.
+ * Params:
+ * sc = context
+ * ue = NegExp, UAddExp, or ComExp which is revised per rules
+ * References:
+ * https://issues.dlang.org/show_bug.cgi?id=16997
+ */
+
+void fix16997(Scope* sc, UnaExp ue)
+{
+ if (global.params.fix16997 || sc.flags & SCOPE.Cfile)
+ ue.e1 = integralPromotions(ue.e1, sc); // desired C-like behavor
+ else
+ {
+ switch (ue.e1.type.toBasetype.ty)
+ {
+ case Tint8:
+ case Tuns8:
+ case Tint16:
+ case Tuns16:
+ //case Tbool: // these operations aren't allowed on bool anyway
+ case Tchar:
+ case Twchar:
+ case Tdchar:
+ ue.deprecation("integral promotion not done for `%s`, use '-preview=intpromote' switch or `%scast(int)(%s)`",
+ ue.toChars(), Token.toChars(ue.op), ue.e1.toChars());
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/***********************************
+ * See if both types are arrays that can be compared
+ * for equality without any casting. Return true if so.
+ * This is to enable comparing things like an immutable
+ * array with a mutable one.
+ */
+extern (C++) bool arrayTypeCompatibleWithoutCasting(Type t1, Type t2)
+{
+ t1 = t1.toBasetype();
+ t2 = t2.toBasetype();
+
+ if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && t2.ty == t1.ty)
+ {
+ if (t1.nextOf().implicitConvTo(t2.nextOf()) >= MATCH.constant || t2.nextOf().implicitConvTo(t1.nextOf()) >= MATCH.constant)
+ return true;
+ }
+ return false;
+}
+
+/******************************************************************/
+/* Determine the integral ranges of an expression.
+ * This is used to determine if implicit narrowing conversions will
+ * be allowed.
+ */
+IntRange getIntRange(Expression e)
+{
+ extern (C++) final class IntRangeVisitor : Visitor
+ {
+ alias visit = Visitor.visit;
+
+ public:
+ IntRange range;
+
+ override void visit(Expression e)
+ {
+ range = IntRange.fromType(e.type);
+ }
+
+ override void visit(IntegerExp e)
+ {
+ range = IntRange(SignExtendedNumber(e.getInteger()))._cast(e.type);
+ }
+
+ override void visit(CastExp e)
+ {
+ range = getIntRange(e.e1)._cast(e.type);
+ }
+
+ override void visit(AddExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+ range = (ir1 + ir2)._cast(e.type);
+ }
+
+ override void visit(MinExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+ range = (ir1 - ir2)._cast(e.type);
+ }
+
+ override void visit(DivExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 / ir2)._cast(e.type);
+ }
+
+ override void visit(MulExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 * ir2)._cast(e.type);
+ }
+
+ override void visit(ModExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ // Modding on 0 is invalid anyway.
+ if (!ir2.absNeg().imin.negative)
+ {
+ visit(cast(Expression)e);
+ return;
+ }
+ range = (ir1 % ir2)._cast(e.type);
+ }
+
+ override void visit(AndExp e)
+ {
+ IntRange result;
+ bool hasResult = false;
+ result.unionOrAssign(getIntRange(e.e1) & getIntRange(e.e2), hasResult);
+
+ assert(hasResult);
+ range = result._cast(e.type);
+ }
+
+ override void visit(OrExp e)
+ {
+ IntRange result;
+ bool hasResult = false;
+ result.unionOrAssign(getIntRange(e.e1) | getIntRange(e.e2), hasResult);
+
+ assert(hasResult);
+ range = result._cast(e.type);
+ }
+
+ override void visit(XorExp e)
+ {
+ IntRange result;
+ bool hasResult = false;
+ result.unionOrAssign(getIntRange(e.e1) ^ getIntRange(e.e2), hasResult);
+
+ assert(hasResult);
+ range = result._cast(e.type);
+ }
+
+ override void visit(ShlExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 << ir2)._cast(e.type);
+ }
+
+ override void visit(ShrExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 >> ir2)._cast(e.type);
+ }
+
+ override void visit(UshrExp e)
+ {
+ IntRange ir1 = getIntRange(e.e1).castUnsigned(e.e1.type);
+ IntRange ir2 = getIntRange(e.e2);
+
+ range = (ir1 >>> ir2)._cast(e.type);
+ }
+
+ override void visit(AssignExp e)
+ {
+ range = getIntRange(e.e2)._cast(e.type);
+ }
+
+ override void visit(CondExp e)
+ {
+ // No need to check e.econd; assume caller has called optimize()
+ IntRange ir1 = getIntRange(e.e1);
+ IntRange ir2 = getIntRange(e.e2);
+ range = ir1.unionWith(ir2)._cast(e.type);
+ }
+
+ override void visit(VarExp e)
+ {
+ Expression ie;
+ VarDeclaration vd = e.var.isVarDeclaration();
+ if (vd && vd.range)
+ range = vd.range._cast(e.type);
+ else if (vd && vd._init && !vd.type.isMutable() && (ie = vd.getConstInitializer()) !is null)
+ ie.accept(this);
+ else
+ visit(cast(Expression)e);
+ }
+
+ override void visit(CommaExp e)
+ {
+ e.e2.accept(this);
+ }
+
+ override void visit(ComExp e)
+ {
+ IntRange ir = getIntRange(e.e1);
+ range = IntRange(SignExtendedNumber(~ir.imax.value, !ir.imax.negative), SignExtendedNumber(~ir.imin.value, !ir.imin.negative))._cast(e.type);
+ }
+
+ override void visit(NegExp e)
+ {
+ IntRange ir = getIntRange(e.e1);
+ range = (-ir)._cast(e.type);
+ }
+ }
+
+ scope IntRangeVisitor v = new IntRangeVisitor();
+ e.accept(v);
+ return v.range;
+}
diff --git a/gcc/d/dmd/dclass.c b/gcc/d/dmd/dclass.c
deleted file mode 100644
index 3f33014da9f..00000000000
--- a/gcc/d/dmd/dclass.c
+++ /dev/null
@@ -1,1041 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/class.c
- */
-
-#include "root/dsystem.h" // mem{cpy|set}()
-#include "root/root.h"
-#include "root/rmem.h"
-
-#include "errors.h"
-#include "enum.h"
-#include "init.h"
-#include "attrib.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "id.h"
-#include "mtype.h"
-#include "scope.h"
-#include "module.h"
-#include "expression.h"
-#include "statement.h"
-#include "template.h"
-#include "target.h"
-#include "objc.h"
-
-bool symbolIsVisible(Dsymbol *origin, Dsymbol *s);
-Objc *objc();
-
-
-/********************************* ClassDeclaration ****************************/
-
-ClassDeclaration *ClassDeclaration::object;
-ClassDeclaration *ClassDeclaration::throwable;
-ClassDeclaration *ClassDeclaration::exception;
-ClassDeclaration *ClassDeclaration::errorException;
-ClassDeclaration *ClassDeclaration::cpp_type_info_ptr; // Object.__cpp_type_info_ptr
-
-ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject)
- : AggregateDeclaration(loc, id ? id : Identifier::generateId("__anonclass"))
-{
- static const char msg[] = "only object.d can define this reserved class name";
-
- if (baseclasses)
- {
- // Actually, this is a transfer
- this->baseclasses = baseclasses;
- }
- else
- this->baseclasses = new BaseClasses();
-
- this->members = members;
-
- baseClass = NULL;
-
- interfaces.length = 0;
- interfaces.ptr = NULL;
-
- vtblInterfaces = NULL;
-
- //printf("ClassDeclaration(%s), dim = %d\n", id->toChars(), this->baseclasses->length);
-
- // For forward references
- type = new TypeClass(this);
-
- staticCtor = NULL;
- staticDtor = NULL;
-
- vtblsym = NULL;
- vclassinfo = NULL;
-
- if (id)
- {
- // Look for special class names
-
- if (id == Id::__sizeof || id == Id::__xalignof || id == Id::_mangleof)
- error("illegal class name");
-
- // BUG: What if this is the wrong TypeInfo, i.e. it is nested?
- if (id->toChars()[0] == 'T')
- {
- if (id == Id::TypeInfo)
- {
- if (!inObject)
- error("%s", msg);
- Type::dtypeinfo = this;
- }
-
- if (id == Id::TypeInfo_Class)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfoclass = this;
- }
-
- if (id == Id::TypeInfo_Interface)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfointerface = this;
- }
-
- if (id == Id::TypeInfo_Struct)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfostruct = this;
- }
-
- if (id == Id::TypeInfo_Pointer)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfopointer = this;
- }
-
- if (id == Id::TypeInfo_Array)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfoarray = this;
- }
-
- if (id == Id::TypeInfo_StaticArray)
- {
- //if (!inObject)
- // Type::typeinfostaticarray->error("%s", msg);
- Type::typeinfostaticarray = this;
- }
-
- if (id == Id::TypeInfo_AssociativeArray)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfoassociativearray = this;
- }
-
- if (id == Id::TypeInfo_Enum)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfoenum = this;
- }
-
- if (id == Id::TypeInfo_Function)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfofunction = this;
- }
-
- if (id == Id::TypeInfo_Delegate)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfodelegate = this;
- }
-
- if (id == Id::TypeInfo_Tuple)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfotypelist = this;
- }
-
- if (id == Id::TypeInfo_Const)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfoconst = this;
- }
-
- if (id == Id::TypeInfo_Invariant)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfoinvariant = this;
- }
-
- if (id == Id::TypeInfo_Shared)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfoshared = this;
- }
-
- if (id == Id::TypeInfo_Wild)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfowild = this;
- }
-
- if (id == Id::TypeInfo_Vector)
- {
- if (!inObject)
- error("%s", msg);
- Type::typeinfovector = this;
- }
- }
-
- if (id == Id::Object)
- {
- if (!inObject)
- error("%s", msg);
- object = this;
- }
-
- if (id == Id::Throwable)
- {
- if (!inObject)
- error("%s", msg);
- throwable = this;
- }
-
- if (id == Id::Exception)
- {
- if (!inObject)
- error("%s", msg);
- exception = this;
- }
-
- if (id == Id::Error)
- {
- if (!inObject)
- error("%s", msg);
- errorException = this;
- }
-
- if (id == Id::cpp_type_info_ptr)
- {
- if (!inObject)
- error("%s", msg);
- cpp_type_info_ptr = this;
- }
- }
-
- com = false;
- isscope = false;
- isabstract = ABSfwdref;
- inuse = 0;
- baseok = BASEOKnone;
- cpp_type_info_ptr_sym = NULL;
-}
-
-ClassDeclaration *ClassDeclaration::create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject)
-{
- return new ClassDeclaration(loc, id, baseclasses, members, inObject);
-}
-
-Dsymbol *ClassDeclaration::syntaxCopy(Dsymbol *s)
-{
- //printf("ClassDeclaration::syntaxCopy('%s')\n", toChars());
- ClassDeclaration *cd =
- s ? (ClassDeclaration *)s
- : new ClassDeclaration(loc, ident, NULL, NULL, false);
-
- cd->storage_class |= storage_class;
-
- cd->baseclasses->setDim(this->baseclasses->length);
- for (size_t i = 0; i < cd->baseclasses->length; i++)
- {
- BaseClass *b = (*this->baseclasses)[i];
- BaseClass *b2 = new BaseClass(b->type->syntaxCopy());
- (*cd->baseclasses)[i] = b2;
- }
-
- return ScopeDsymbol::syntaxCopy(cd);
-}
-
-Scope *ClassDeclaration::newScope(Scope *sc)
-{
- Scope *sc2 = AggregateDeclaration::newScope(sc);
- if (isCOMclass())
- {
- /* This enables us to use COM objects under Linux and
- * work with things like XPCOM
- */
- sc2->linkage = target.systemLinkage();
- }
- return sc2;
-}
-
-/*********************************************
- * Determine if 'this' is a base class of cd.
- * This is used to detect circular inheritance only.
- */
-
-bool ClassDeclaration::isBaseOf2(ClassDeclaration *cd)
-{
- if (!cd)
- return false;
- //printf("ClassDeclaration::isBaseOf2(this = '%s', cd = '%s')\n", toChars(), cd->toChars());
- for (size_t i = 0; i < cd->baseclasses->length; i++)
- {
- BaseClass *b = (*cd->baseclasses)[i];
- if (b->sym == this || isBaseOf2(b->sym))
- return true;
- }
- return false;
-}
-
-/*******************************************
- * Determine if 'this' is a base class of cd.
- */
-
-bool ClassDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset)
-{
- //printf("ClassDeclaration::isBaseOf(this = '%s', cd = '%s')\n", toChars(), cd->toChars());
- if (poffset)
- *poffset = 0;
- while (cd)
- {
- /* cd->baseClass might not be set if cd is forward referenced.
- */
- if (!cd->baseClass && cd->semanticRun < PASSsemanticdone && !cd->isInterfaceDeclaration())
- {
- dsymbolSemantic(cd, NULL);
- if (!cd->baseClass && cd->semanticRun < PASSsemanticdone)
- cd->error("base class is forward referenced by %s", toChars());
- }
-
- if (this == cd->baseClass)
- return true;
-
- cd = cd->baseClass;
- }
- return false;
-}
-
-/*********************************************
- * Determine if 'this' has complete base class information.
- * This is used to detect forward references in covariant overloads.
- */
-
-bool ClassDeclaration::isBaseInfoComplete()
-{
- return baseok >= BASEOKdone;
-}
-
-Dsymbol *ClassDeclaration::search(const Loc &loc, Identifier *ident, int flags)
-{
- //printf("%s.ClassDeclaration::search('%s', flags=x%x)\n", toChars(), ident->toChars(), flags);
-
- //if (_scope) printf("%s baseok = %d\n", toChars(), baseok);
- if (_scope && baseok < BASEOKdone)
- {
- if (!inuse)
- {
- // must semantic on base class/interfaces
- ++inuse;
- dsymbolSemantic(this, NULL);
- --inuse;
- }
- }
-
- if (!members || !symtab) // opaque or addMember is not yet done
- {
- error("is forward referenced when looking for `%s`", ident->toChars());
- //*(char*)0=0;
- return NULL;
- }
-
- Dsymbol *s = ScopeDsymbol::search(loc, ident, flags);
-
- // don't search imports of base classes
- if (flags & SearchImportsOnly)
- return s;
-
- if (!s)
- {
- // Search bases classes in depth-first, left to right order
-
- for (size_t i = 0; i < baseclasses->length; i++)
- {
- BaseClass *b = (*baseclasses)[i];
-
- if (b->sym)
- {
- if (!b->sym->symtab)
- error("base %s is forward referenced", b->sym->ident->toChars());
- else
- {
- s = b->sym->search(loc, ident, flags);
- if (!s)
- continue;
- else if (s == this) // happens if s is nested in this and derives from this
- s = NULL;
- else if (!(flags & IgnoreSymbolVisibility) && !(s->prot().kind == Prot::protected_) && !symbolIsVisible(this, s))
- s = NULL;
- else
- break;
- }
- }
- }
- }
- return s;
-}
-
-/************************************
- * Search base classes in depth-first, left-to-right order for
- * a class or interface named 'ident'.
- * Stops at first found. Does not look for additional matches.
- * Params:
- * ident = identifier to search for
- * Returns:
- * ClassDeclaration if found, null if not
- */
-ClassDeclaration *ClassDeclaration::searchBase(Identifier *ident)
-{
- for (size_t i = 0; i < baseclasses->length; i++)
- {
- BaseClass *b = (*baseclasses)[i];
- ClassDeclaration *cdb = b->type->isClassHandle();
- if (!cdb) // Bugzilla 10616
- return NULL;
- if (cdb->ident->equals(ident))
- return cdb;
- cdb = cdb->searchBase(ident);
- if (cdb)
- return cdb;
- }
- return NULL;
-}
-
-/****
- * Runs through the inheritance graph to set the BaseClass.offset fields.
- * Recursive in order to account for the size of the interface classes, if they are
- * more than just interfaces.
- * Params:
- * cd = interface to look at
- * baseOffset = offset of where cd will be placed
- * Returns:
- * subset of instantiated size used by cd for interfaces
- */
-static unsigned membersPlace(BaseClasses *vtblInterfaces, size_t &bi, ClassDeclaration *cd, unsigned baseOffset)
-{
- //printf(" membersPlace(%s, %d)\n", cd->toChars(), baseOffset);
- unsigned offset = baseOffset;
-
- for (size_t i = 0; i < cd->interfaces.length; i++)
- {
- BaseClass *b = cd->interfaces.ptr[i];
- if (b->sym->sizeok != SIZEOKdone)
- b->sym->finalizeSize();
- assert(b->sym->sizeok == SIZEOKdone);
-
- if (!b->sym->alignsize)
- b->sym->alignsize = target.ptrsize;
- cd->alignmember(b->sym->alignsize, b->sym->alignsize, &offset);
- assert(bi < vtblInterfaces->length);
- BaseClass *bv = (*vtblInterfaces)[bi];
- if (b->sym->interfaces.length == 0)
- {
- //printf("\tvtblInterfaces[%d] b=%p b->sym = %s, offset = %d\n", bi, bv, bv->sym->toChars(), offset);
- bv->offset = offset;
- ++bi;
- // All the base interfaces down the left side share the same offset
- for (BaseClass *b2 = bv; b2->baseInterfaces.length; )
- {
- b2 = &b2->baseInterfaces.ptr[0];
- b2->offset = offset;
- //printf("\tvtblInterfaces[%d] b=%p sym = %s, offset = %d\n", bi, b2, b2->sym->toChars(), b2->offset);
- }
- }
- membersPlace(vtblInterfaces, bi, b->sym, offset);
- //printf(" %s size = %d\n", b->sym->toChars(), b->sym->structsize);
- offset += b->sym->structsize;
- if (cd->alignsize < b->sym->alignsize)
- cd->alignsize = b->sym->alignsize;
- }
- return offset - baseOffset;
-}
-
-void ClassDeclaration::finalizeSize()
-{
- assert(sizeok != SIZEOKdone);
-
- // Set the offsets of the fields and determine the size of the class
- if (baseClass)
- {
- assert(baseClass->sizeok == SIZEOKdone);
-
- alignsize = baseClass->alignsize;
- if (classKind == ClassKind::cpp)
- structsize = target.cpp.derivedClassOffset(baseClass);
- else
- structsize = baseClass->structsize;
- }
- else if (isInterfaceDeclaration())
- {
- if (interfaces.length == 0)
- {
- alignsize = target.ptrsize;
- structsize = target.ptrsize; // allow room for __vptr
- }
- }
- else
- {
- alignsize = target.ptrsize;
- structsize = target.ptrsize; // allow room for __vptr
- if (hasMonitor())
- structsize += target.ptrsize; // allow room for __monitor
- }
-
- //printf("finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
- size_t bi = 0; // index into vtblInterfaces[]
-
- // Add vptr's for any interfaces implemented by this class
- structsize += membersPlace(vtblInterfaces, bi, this, structsize);
-
- if (isInterfaceDeclaration())
- {
- sizeok = SIZEOKdone;
- return;
- }
-
- // FIXME: Currently setFieldOffset functions need to increase fields
- // to calculate each variable offsets. It can be improved later.
- fields.setDim(0);
-
- unsigned offset = structsize;
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- s->setFieldOffset(this, &offset, false);
- }
-
- sizeok = SIZEOKdone;
-
- // Calculate fields[i]->overlapped
- checkOverlappedFields();
-}
-
-/**************
- * Returns: true if there's a __monitor field
- */
-bool ClassDeclaration::hasMonitor()
-{
- return classKind == ClassKind::d;
-}
-
-/**********************************************************
- * fd is in the vtbl[] for this class.
- * Return 1 if function is hidden (not findable through search).
- */
-
-int isf(void *param, Dsymbol *s)
-{
- FuncDeclaration *fd = s->isFuncDeclaration();
- if (!fd)
- return 0;
- //printf("param = %p, fd = %p %s\n", param, fd, fd->toChars());
- return (RootObject *)param == fd;
-}
-
-bool ClassDeclaration::isFuncHidden(FuncDeclaration *fd)
-{
- //printf("ClassDeclaration::isFuncHidden(class = %s, fd = %s)\n", toChars(), fd->toPrettyChars());
- Dsymbol *s = search(Loc(), fd->ident, IgnoreAmbiguous | IgnoreErrors);
- if (!s)
- {
- //printf("not found\n");
- /* Because, due to a hack, if there are multiple definitions
- * of fd->ident, NULL is returned.
- */
- return false;
- }
- s = s->toAlias();
- OverloadSet *os = s->isOverloadSet();
- if (os)
- {
- for (size_t i = 0; i < os->a.length; i++)
- {
- Dsymbol *s2 = os->a[i];
- FuncDeclaration *f2 = s2->isFuncDeclaration();
- if (f2 && overloadApply(f2, (void *)fd, &isf))
- return false;
- }
- return true;
- }
- else
- {
- FuncDeclaration *fdstart = s->isFuncDeclaration();
- //printf("%s fdstart = %p\n", s->kind(), fdstart);
- if (overloadApply(fdstart, (void *)fd, &isf))
- return false;
-
- return !fd->parent->isTemplateMixin();
- }
-}
-
-/****************
- * Find virtual function matching identifier and type.
- * Used to build virtual function tables for interface implementations.
- */
-
-FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf)
-{
- //printf("ClassDeclaration::findFunc(%s, %s) %s\n", ident->toChars(), tf->toChars(), toChars());
- FuncDeclaration *fdmatch = NULL;
- FuncDeclaration *fdambig = NULL;
-
- ClassDeclaration *cd = this;
- Dsymbols *vtbl = &cd->vtbl;
- while (1)
- {
- for (size_t i = 0; i < vtbl->length; i++)
- {
- FuncDeclaration *fd = (*vtbl)[i]->isFuncDeclaration();
- if (!fd)
- continue; // the first entry might be a ClassInfo
-
- //printf("\t[%d] = %s\n", i, fd->toChars());
- if (ident == fd->ident &&
- fd->type->covariant(tf) == 1)
- {
- //printf("fd->parent->isClassDeclaration() = %p\n", fd->parent->isClassDeclaration());
- if (!fdmatch)
- goto Lfd;
- if (fd == fdmatch)
- goto Lfdmatch;
-
- {
- // Function type matcing: exact > covariant
- MATCH m1 = tf->equals(fd ->type) ? MATCHexact : MATCHnomatch;
- MATCH m2 = tf->equals(fdmatch->type) ? MATCHexact : MATCHnomatch;
- if (m1 > m2)
- goto Lfd;
- else if (m1 < m2)
- goto Lfdmatch;
- }
-
- {
- MATCH m1 = (tf->mod == fd ->type->mod) ? MATCHexact : MATCHnomatch;
- MATCH m2 = (tf->mod == fdmatch->type->mod) ? MATCHexact : MATCHnomatch;
- if (m1 > m2)
- goto Lfd;
- else if (m1 < m2)
- goto Lfdmatch;
- }
-
- {
- // The way of definition: non-mixin > mixin
- MATCH m1 = fd ->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch;
- MATCH m2 = fdmatch->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch;
- if (m1 > m2)
- goto Lfd;
- else if (m1 < m2)
- goto Lfdmatch;
- }
-
- fdambig = fd;
- //printf("Lambig fdambig = %s %s [%s]\n", fdambig->toChars(), fdambig->type->toChars(), fdambig->loc.toChars());
- continue;
-
- Lfd:
- fdmatch = fd;
- fdambig = NULL;
- //printf("Lfd fdmatch = %s %s [%s]\n", fdmatch->toChars(), fdmatch->type->toChars(), fdmatch->loc.toChars());
- continue;
-
- Lfdmatch:
- continue;
- }
- //else printf("\t\t%d\n", fd->type->covariant(tf));
- }
- if (!cd)
- break;
- vtbl = &cd->vtblFinal;
- cd = cd->baseClass;
- }
-
- if (fdambig)
- error("ambiguous virtual function %s", fdambig->toChars());
- return fdmatch;
-}
-
-/****************************************
- */
-
-bool ClassDeclaration::isCOMclass() const
-{
- return com;
-}
-
-bool ClassDeclaration::isCOMinterface() const
-{
- return false;
-}
-
-bool ClassDeclaration::isCPPclass() const
-{
- return classKind == ClassKind::cpp;
-}
-
-bool ClassDeclaration::isCPPinterface() const
-{
- return false;
-}
-
-
-/****************************************
- */
-
-bool ClassDeclaration::isAbstract()
-{
- if (isabstract != ABSfwdref)
- return isabstract == ABSyes;
-
- /* Bugzilla 11169: Resolve forward references to all class member functions,
- * and determine whether this class is abstract.
- */
- struct SearchAbstract
- {
- static int fp(Dsymbol *s, void *)
- {
- FuncDeclaration *fd = s->isFuncDeclaration();
- if (!fd)
- return 0;
- if (fd->storage_class & STCstatic)
- return 0;
-
- if (fd->_scope)
- dsymbolSemantic(fd, NULL);
-
- if (fd->isAbstract())
- return 1;
- return 0;
- }
- };
-
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- if (s->apply(&SearchAbstract::fp, this))
- {
- isabstract = ABSyes;
- return true;
- }
- }
-
- /* Iterate inherited member functions and check their abstract attribute.
- */
- for (size_t i = 1; i < vtbl.length; i++)
- {
- FuncDeclaration *fd = vtbl[i]->isFuncDeclaration();
- //if (fd) printf("\tvtbl[%d] = [%s] %s\n", i, fd->loc.toChars(), fd->toChars());
- if (!fd || fd->isAbstract())
- {
- isabstract = ABSyes;
- return true;
- }
- }
-
- isabstract = ABSno;
- return false;
-}
-
-
-/****************************************
- * Determine if slot 0 of the vtbl[] is reserved for something else.
- * For class objects, yes, this is where the classinfo ptr goes.
- * For COM interfaces, no.
- * For non-COM interfaces, yes, this is where the Interface ptr goes.
- * Returns:
- * 0 vtbl[0] is first virtual function pointer
- * 1 vtbl[0] is classinfo/interfaceinfo pointer
- */
-
-int ClassDeclaration::vtblOffset() const
-{
- return classKind == ClassKind::cpp ? 0 : 1;
-}
-
-/****************************************
- */
-
-const char *ClassDeclaration::kind() const
-{
- return "class";
-}
-
-/****************************************
- */
-
-void ClassDeclaration::addLocalClass(ClassDeclarations *aclasses)
-{
- aclasses->push(this);
-}
-
-/********************************* InterfaceDeclaration ****************************/
-
-InterfaceDeclaration::InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses)
- : ClassDeclaration(loc, id, baseclasses, NULL, false)
-{
- if (id == Id::IUnknown) // IUnknown is the root of all COM interfaces
- {
- com = true;
- classKind = ClassKind::cpp; // IUnknown is also a C++ interface
- }
-}
-
-Dsymbol *InterfaceDeclaration::syntaxCopy(Dsymbol *s)
-{
- InterfaceDeclaration *id =
- s ? (InterfaceDeclaration *)s
- : new InterfaceDeclaration(loc, ident, NULL);
- return ClassDeclaration::syntaxCopy(id);
-}
-
-Scope *InterfaceDeclaration::newScope(Scope *sc)
-{
- Scope *sc2 = ClassDeclaration::newScope(sc);
- if (com)
- sc2->linkage = LINKwindows;
- else if (classKind == ClassKind::cpp)
- sc2->linkage = LINKcpp;
- else if (classKind == ClassKind::objc)
- sc2->linkage = LINKobjc;
- return sc2;
-}
-
-/*******************************************
- * Determine if 'this' is a base class of cd.
- * (Actually, if it is an interface supported by cd)
- * Output:
- * *poffset offset to start of class
- * OFFSET_RUNTIME must determine offset at runtime
- * Returns:
- * false not a base
- * true is a base
- */
-
-bool InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset)
-{
- //printf("%s.InterfaceDeclaration::isBaseOf(cd = '%s')\n", toChars(), cd->toChars());
- assert(!baseClass);
- for (size_t j = 0; j < cd->interfaces.length; j++)
- {
- BaseClass *b = cd->interfaces.ptr[j];
-
- //printf("\tX base %s\n", b->sym->toChars());
- if (this == b->sym)
- {
- //printf("\tfound at offset %d\n", b->offset);
- if (poffset)
- {
- // don't return incorrect offsets https://issues.dlang.org/show_bug.cgi?id=16980
- *poffset = cd->sizeok == SIZEOKdone ? b->offset : OFFSET_FWDREF;
- }
- //printf("\tfound at offset %d\n", b->offset);
- return true;
- }
- if (isBaseOf(b, poffset))
- return true;
- }
-
- if (cd->baseClass && isBaseOf(cd->baseClass, poffset))
- return true;
-
- if (poffset)
- *poffset = 0;
- return false;
-}
-
-bool InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset)
-{
- //printf("%s.InterfaceDeclaration::isBaseOf(bc = '%s')\n", toChars(), bc->sym->toChars());
- for (size_t j = 0; j < bc->baseInterfaces.length; j++)
- {
- BaseClass *b = &bc->baseInterfaces.ptr[j];
-
- //printf("\tY base %s\n", b->sym->toChars());
- if (this == b->sym)
- {
- //printf("\tfound at offset %d\n", b->offset);
- if (poffset)
- {
- *poffset = b->offset;
- }
- return true;
- }
- if (isBaseOf(b, poffset))
- {
- return true;
- }
- }
- if (poffset)
- *poffset = 0;
- return false;
-}
-
-/****************************************
- * Determine if slot 0 of the vtbl[] is reserved for something else.
- * For class objects, yes, this is where the ClassInfo ptr goes.
- * For COM interfaces, no.
- * For non-COM interfaces, yes, this is where the Interface ptr goes.
- */
-
-int InterfaceDeclaration::vtblOffset() const
-{
- if (isCOMinterface() || isCPPinterface())
- return 0;
- return 1;
-}
-
-bool InterfaceDeclaration::isCOMinterface() const
-{
- return com;
-}
-
-bool InterfaceDeclaration::isCPPinterface() const
-{
- return classKind == ClassKind::cpp;
-}
-
-/*******************************************
- */
-
-const char *InterfaceDeclaration::kind() const
-{
- return "interface";
-}
-
-
-/******************************** BaseClass *****************************/
-
-BaseClass::BaseClass()
-{
- this->type = NULL;
- this->sym = NULL;
- this->offset = 0;
-
- this->baseInterfaces.length = 0;
- this->baseInterfaces.ptr = NULL;
-}
-
-BaseClass::BaseClass(Type *type)
-{
- //printf("BaseClass(this = %p, '%s')\n", this, type->toChars());
- this->type = type;
- this->sym = NULL;
- this->offset = 0;
-
- this->baseInterfaces.length = 0;
- this->baseInterfaces.ptr = NULL;
-}
-
-/****************************************
- * Fill in vtbl[] for base class based on member functions of class cd.
- * Input:
- * vtbl if !=NULL, fill it in
- * newinstance !=0 means all entries must be filled in by members
- * of cd, not members of any base classes of cd.
- * Returns:
- * true if any entries were filled in by members of cd (not exclusively
- * by base classes)
- */
-
-bool BaseClass::fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance)
-{
- bool result = false;
-
- //printf("BaseClass::fillVtbl(this='%s', cd='%s')\n", sym->toChars(), cd->toChars());
- if (vtbl)
- vtbl->setDim(sym->vtbl.length);
-
- // first entry is ClassInfo reference
- for (size_t j = sym->vtblOffset(); j < sym->vtbl.length; j++)
- {
- FuncDeclaration *ifd = sym->vtbl[j]->isFuncDeclaration();
- FuncDeclaration *fd;
- TypeFunction *tf;
-
- //printf(" vtbl[%d] is '%s'\n", j, ifd ? ifd->toChars() : "null");
-
- assert(ifd);
- // Find corresponding function in this class
- tf = ifd->type->toTypeFunction();
- fd = cd->findFunc(ifd->ident, tf);
- if (fd && !fd->isAbstract())
- {
- //printf(" found\n");
- // Check that calling conventions match
- if (fd->linkage != ifd->linkage)
- fd->error("linkage doesn't match interface function");
-
- // Check that it is current
- //printf("newinstance = %d fd->toParent() = %s ifd->toParent() = %s\n",
- //newinstance, fd->toParent()->toChars(), ifd->toParent()->toChars());
- if (newinstance && fd->toParent() != cd && ifd->toParent() == sym)
- cd->error("interface function `%s` is not implemented", ifd->toFullSignature());
-
- if (fd->toParent() == cd)
- result = true;
- }
- else
- {
- //printf(" not found %p\n", fd);
- // BUG: should mark this class as abstract?
- if (!cd->isAbstract())
- cd->error("interface function `%s` is not implemented", ifd->toFullSignature());
-
- fd = NULL;
- }
- if (vtbl)
- (*vtbl)[j] = fd;
- }
-
- return result;
-}
-
-void BaseClass::copyBaseInterfaces(BaseClasses *vtblInterfaces)
-{
- //printf("+copyBaseInterfaces(), %s\n", sym->toChars());
-// if (baseInterfaces.length)
-// return;
-
- baseInterfaces.length = sym->interfaces.length;
- baseInterfaces.ptr = (BaseClass *)mem.xcalloc(baseInterfaces.length, sizeof(BaseClass));
-
- //printf("%s.copyBaseInterfaces()\n", sym->toChars());
- for (size_t i = 0; i < baseInterfaces.length; i++)
- {
- void *pb = &baseInterfaces.ptr[i];
- BaseClass *b2 = sym->interfaces.ptr[i];
-
- assert(b2->vtbl.length == 0); // should not be filled yet
- BaseClass *b = (BaseClass *)memcpy(pb, b2, sizeof(BaseClass));
-
- if (i) // single inheritance is i==0
- vtblInterfaces->push(b); // only need for M.I.
- b->copyBaseInterfaces(vtblInterfaces);
- }
- //printf("-copyBaseInterfaces\n");
-}
diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d
new file mode 100644
index 00000000000..b065251e652
--- /dev/null
+++ b/gcc/d/dmd/dclass.d
@@ -0,0 +1,1139 @@
+/**
+ * Defines a `class` declaration.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/class.html, Classes)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dclass.d, _dclass.d)
+ * Documentation: https://dlang.org/phobos/dmd_dclass.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dclass.d
+ */
+
+module dmd.dclass;
+
+import core.stdc.stdio;
+import core.stdc.string;
+
+import dmd.aggregate;
+import dmd.apply;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.gluelayer;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.objc;
+import dmd.root.rmem;
+import dmd.target;
+import dmd.visitor;
+
+/***********************************************************
+ */
+extern (C++) struct BaseClass
+{
+ Type type; // (before semantic processing)
+
+ ClassDeclaration sym;
+ uint offset; // 'this' pointer offset
+
+ // for interfaces: Array of FuncDeclaration's making up the vtbl[]
+ FuncDeclarations vtbl;
+
+ // if BaseClass is an interface, these
+ // are a copy of the InterfaceDeclaration.interfaces
+ BaseClass[] baseInterfaces;
+
+ extern (D) this(Type type)
+ {
+ //printf("BaseClass(this = %p, '%s')\n", this, type.toChars());
+ this.type = type;
+ }
+
+ /****************************************
+ * Fill in vtbl[] for base class based on member functions of class cd.
+ * Input:
+ * vtbl if !=NULL, fill it in
+ * newinstance !=0 means all entries must be filled in by members
+ * of cd, not members of any base classes of cd.
+ * Returns:
+ * true if any entries were filled in by members of cd (not exclusively
+ * by base classes)
+ */
+ extern (C++) bool fillVtbl(ClassDeclaration cd, FuncDeclarations* vtbl, int newinstance)
+ {
+ bool result = false;
+
+ //printf("BaseClass.fillVtbl(this='%s', cd='%s')\n", sym.toChars(), cd.toChars());
+ if (vtbl)
+ vtbl.setDim(sym.vtbl.dim);
+
+ // first entry is ClassInfo reference
+ for (size_t j = sym.vtblOffset(); j < sym.vtbl.dim; j++)
+ {
+ FuncDeclaration ifd = sym.vtbl[j].isFuncDeclaration();
+
+ //printf(" vtbl[%d] is '%s'\n", j, ifd ? ifd.toChars() : "null");
+ assert(ifd);
+
+ // Find corresponding function in this class
+ auto tf = ifd.type.toTypeFunction();
+ auto fd = cd.findFunc(ifd.ident, tf);
+ if (fd && !fd.isAbstract())
+ {
+ if (fd.toParent() == cd)
+ result = true;
+ }
+ else
+ fd = null;
+ if (vtbl)
+ (*vtbl)[j] = fd;
+ }
+ return result;
+ }
+
+ extern (D) void copyBaseInterfaces(BaseClasses* vtblInterfaces)
+ {
+ //printf("+copyBaseInterfaces(), %s\n", sym.toChars());
+ // if (baseInterfaces.length)
+ // return;
+ auto bc = cast(BaseClass*)mem.xcalloc(sym.interfaces.length, BaseClass.sizeof);
+ baseInterfaces = bc[0 .. sym.interfaces.length];
+ //printf("%s.copyBaseInterfaces()\n", sym.toChars());
+ for (size_t i = 0; i < baseInterfaces.length; i++)
+ {
+ BaseClass* b = &baseInterfaces[i];
+ BaseClass* b2 = sym.interfaces[i];
+
+ assert(b2.vtbl.dim == 0); // should not be filled yet
+ memcpy(b, b2, BaseClass.sizeof);
+
+ if (i) // single inheritance is i==0
+ vtblInterfaces.push(b); // only need for M.I.
+ b.copyBaseInterfaces(vtblInterfaces);
+ }
+ //printf("-copyBaseInterfaces\n");
+ }
+}
+
+enum ClassFlags : uint
+{
+ none = 0x0,
+ isCOMclass = 0x1,
+ noPointers = 0x2,
+ hasOffTi = 0x4,
+ hasCtor = 0x8,
+ hasGetMembers = 0x10,
+ hasTypeInfo = 0x20,
+ isAbstract = 0x40,
+ isCPPclass = 0x80,
+ hasDtor = 0x100,
+}
+
+/***********************************************************
+ */
+extern (C++) class ClassDeclaration : AggregateDeclaration
+{
+ extern (C++) __gshared
+ {
+ // Names found by reading object.d in druntime
+ ClassDeclaration object;
+ ClassDeclaration throwable;
+ ClassDeclaration exception;
+ ClassDeclaration errorException;
+ ClassDeclaration cpp_type_info_ptr; // Object.__cpp_type_info_ptr
+ }
+
+ ClassDeclaration baseClass; // NULL only if this is Object
+ FuncDeclaration staticCtor;
+ FuncDeclaration staticDtor;
+ Dsymbols vtbl; // Array of FuncDeclaration's making up the vtbl[]
+ Dsymbols vtblFinal; // More FuncDeclaration's that aren't in vtbl[]
+
+ // Array of BaseClass's; first is super, rest are Interface's
+ BaseClasses* baseclasses;
+
+ /* Slice of baseclasses[] that does not include baseClass
+ */
+ BaseClass*[] interfaces;
+
+ // array of base interfaces that have their own vtbl[]
+ BaseClasses* vtblInterfaces;
+
+ // the ClassInfo object for this ClassDeclaration
+ TypeInfoClassDeclaration vclassinfo;
+
+ // true if this is a COM class
+ bool com;
+
+ /// true if this is a scope class
+ bool stack;
+
+ /// if this is a C++ class, this is the slot reserved for the virtual destructor
+ int cppDtorVtblIndex = -1;
+
+ /// to prevent recursive attempts
+ private bool inuse;
+
+ ThreeState isabstract;
+
+ /// set the progress of base classes resolving
+ Baseok baseok;
+
+ /**
+ * Data for a class declaration that is needed for the Objective-C
+ * integration.
+ */
+ ObjcClassDeclaration objc;
+
+ Symbol* cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr
+
+ final extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
+ {
+ objc = ObjcClassDeclaration(this);
+
+ if (!id)
+ id = Identifier.generateAnonymousId("class");
+
+ super(loc, id);
+
+ __gshared const(char)* msg = "only object.d can define this reserved class name";
+
+ if (baseclasses)
+ {
+ // Actually, this is a transfer
+ this.baseclasses = baseclasses;
+ }
+ else
+ this.baseclasses = new BaseClasses();
+
+ this.members = members;
+
+ //printf("ClassDeclaration(%s), dim = %d\n", ident.toChars(), this.baseclasses.dim);
+
+ // For forward references
+ type = new TypeClass(this);
+
+ // Look for special class names
+ if (id == Id.__sizeof || id == Id.__xalignof || id == Id._mangleof)
+ error("illegal class name");
+
+ // BUG: What if this is the wrong TypeInfo, i.e. it is nested?
+ if (id.toChars()[0] == 'T')
+ {
+ if (id == Id.TypeInfo)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.dtypeinfo = this;
+ }
+ if (id == Id.TypeInfo_Class)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfoclass = this;
+ }
+ if (id == Id.TypeInfo_Interface)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfointerface = this;
+ }
+ if (id == Id.TypeInfo_Struct)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfostruct = this;
+ }
+ if (id == Id.TypeInfo_Pointer)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfopointer = this;
+ }
+ if (id == Id.TypeInfo_Array)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfoarray = this;
+ }
+ if (id == Id.TypeInfo_StaticArray)
+ {
+ //if (!inObject)
+ // Type.typeinfostaticarray.error("%s", msg);
+ Type.typeinfostaticarray = this;
+ }
+ if (id == Id.TypeInfo_AssociativeArray)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfoassociativearray = this;
+ }
+ if (id == Id.TypeInfo_Enum)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfoenum = this;
+ }
+ if (id == Id.TypeInfo_Function)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfofunction = this;
+ }
+ if (id == Id.TypeInfo_Delegate)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfodelegate = this;
+ }
+ if (id == Id.TypeInfo_Tuple)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfotypelist = this;
+ }
+ if (id == Id.TypeInfo_Const)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfoconst = this;
+ }
+ if (id == Id.TypeInfo_Invariant)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfoinvariant = this;
+ }
+ if (id == Id.TypeInfo_Shared)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfoshared = this;
+ }
+ if (id == Id.TypeInfo_Wild)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfowild = this;
+ }
+ if (id == Id.TypeInfo_Vector)
+ {
+ if (!inObject)
+ error("%s", msg);
+ Type.typeinfovector = this;
+ }
+ }
+
+ if (id == Id.Object)
+ {
+ if (!inObject)
+ error("%s", msg);
+ object = this;
+ }
+
+ if (id == Id.Throwable)
+ {
+ if (!inObject)
+ error("%s", msg);
+ throwable = this;
+ }
+ if (id == Id.Exception)
+ {
+ if (!inObject)
+ error("%s", msg);
+ exception = this;
+ }
+ if (id == Id.Error)
+ {
+ if (!inObject)
+ error("%s", msg);
+ errorException = this;
+ }
+ if (id == Id.cpp_type_info_ptr)
+ {
+ if (!inObject)
+ error("%s", msg);
+ cpp_type_info_ptr = this;
+ }
+
+ baseok = Baseok.none;
+ }
+
+ static ClassDeclaration create(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
+ {
+ return new ClassDeclaration(loc, id, baseclasses, members, inObject);
+ }
+
+ override const(char)* toPrettyChars(bool qualifyTypes = false)
+ {
+ if (objc.isMeta)
+ return .objc.toPrettyChars(this, qualifyTypes);
+
+ return super.toPrettyChars(qualifyTypes);
+ }
+
+ override ClassDeclaration syntaxCopy(Dsymbol s)
+ {
+ //printf("ClassDeclaration.syntaxCopy('%s')\n", toChars());
+ ClassDeclaration cd =
+ s ? cast(ClassDeclaration)s
+ : new ClassDeclaration(loc, ident, null, null, false);
+
+ cd.storage_class |= storage_class;
+
+ cd.baseclasses.setDim(this.baseclasses.dim);
+ for (size_t i = 0; i < cd.baseclasses.dim; i++)
+ {
+ BaseClass* b = (*this.baseclasses)[i];
+ auto b2 = new BaseClass(b.type.syntaxCopy());
+ (*cd.baseclasses)[i] = b2;
+ }
+
+ ScopeDsymbol.syntaxCopy(cd);
+ return cd;
+ }
+
+ override Scope* newScope(Scope* sc)
+ {
+ auto sc2 = super.newScope(sc);
+ if (isCOMclass())
+ {
+ /* This enables us to use COM objects under Linux and
+ * work with things like XPCOM
+ */
+ sc2.linkage = target.systemLinkage();
+ }
+ return sc2;
+ }
+
+ /*********************************************
+ * Determine if 'this' is a base class of cd.
+ * This is used to detect circular inheritance only.
+ */
+ final bool isBaseOf2(ClassDeclaration cd) pure nothrow @nogc
+ {
+ if (!cd)
+ return false;
+ //printf("ClassDeclaration.isBaseOf2(this = '%s', cd = '%s')\n", toChars(), cd.toChars());
+ for (size_t i = 0; i < cd.baseclasses.dim; i++)
+ {
+ BaseClass* b = (*cd.baseclasses)[i];
+ if (b.sym == this || isBaseOf2(b.sym))
+ return true;
+ }
+ return false;
+ }
+
+ enum OFFSET_RUNTIME = 0x76543210;
+ enum OFFSET_FWDREF = 0x76543211;
+
+ /*******************************************
+ * Determine if 'this' is a base class of cd.
+ */
+ bool isBaseOf(ClassDeclaration cd, int* poffset) pure nothrow @nogc
+ {
+ //printf("ClassDeclaration.isBaseOf(this = '%s', cd = '%s')\n", toChars(), cd.toChars());
+ if (poffset)
+ *poffset = 0;
+ while (cd)
+ {
+ if (this == cd.baseClass)
+ return true;
+
+ cd = cd.baseClass;
+ }
+ return false;
+ }
+
+ /*********************************************
+ * Determine if 'this' has complete base class information.
+ * This is used to detect forward references in covariant overloads.
+ */
+ final bool isBaseInfoComplete() const
+ {
+ return baseok >= Baseok.done;
+ }
+
+ override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ //printf("%s.ClassDeclaration.search('%s', flags=x%x)\n", toChars(), ident.toChars(), flags);
+ //if (_scope) printf("%s baseok = %d\n", toChars(), baseok);
+ if (_scope && baseok < Baseok.done)
+ {
+ if (!inuse)
+ {
+ // must semantic on base class/interfaces
+ inuse = true;
+ dsymbolSemantic(this, null);
+ inuse = false;
+ }
+ }
+
+ if (!members || !symtab) // opaque or addMember is not yet done
+ {
+ // .stringof is always defined (but may be hidden by some other symbol)
+ if (ident != Id.stringof && !(flags & IgnoreErrors) && semanticRun < PASS.semanticdone)
+ error("is forward referenced when looking for `%s`", ident.toChars());
+ //*(char*)0=0;
+ return null;
+ }
+
+ auto s = ScopeDsymbol.search(loc, ident, flags);
+
+ // don't search imports of base classes
+ if (flags & SearchImportsOnly)
+ return s;
+
+ if (s)
+ return s;
+
+ // Search bases classes in depth-first, left to right order
+ foreach (b; (*baseclasses)[])
+ {
+ if (!b.sym)
+ continue;
+
+ if (!b.sym.symtab)
+ {
+ error("base `%s` is forward referenced", b.sym.ident.toChars());
+ continue;
+ }
+
+ import dmd.access : symbolIsVisible;
+
+ s = b.sym.search(loc, ident, flags);
+ if (!s)
+ continue;
+ else if (s == this) // happens if s is nested in this and derives from this
+ s = null;
+ else if (!(flags & IgnoreSymbolVisibility) && !(s.visible().kind == Visibility.Kind.protected_) && !symbolIsVisible(this, s))
+ s = null;
+ else
+ break;
+ }
+
+ return s;
+ }
+
+ /************************************
+ * Search base classes in depth-first, left-to-right order for
+ * a class or interface named 'ident'.
+ * Stops at first found. Does not look for additional matches.
+ * Params:
+ * ident = identifier to search for
+ * Returns:
+ * ClassDeclaration if found, null if not
+ */
+ final ClassDeclaration searchBase(Identifier ident)
+ {
+ foreach (b; *baseclasses)
+ {
+ auto cdb = b.type.isClassHandle();
+ if (!cdb) // https://issues.dlang.org/show_bug.cgi?id=10616
+ return null;
+ if (cdb.ident.equals(ident))
+ return cdb;
+ auto result = cdb.searchBase(ident);
+ if (result)
+ return result;
+ }
+ return null;
+ }
+
+ final override void finalizeSize()
+ {
+ assert(sizeok != Sizeok.done);
+
+ // Set the offsets of the fields and determine the size of the class
+ if (baseClass)
+ {
+ assert(baseClass.sizeok == Sizeok.done);
+
+ alignsize = baseClass.alignsize;
+ if (classKind == ClassKind.cpp)
+ structsize = target.cpp.derivedClassOffset(baseClass);
+ else
+ structsize = baseClass.structsize;
+ }
+ else if (classKind == ClassKind.objc)
+ structsize = 0; // no hidden member for an Objective-C class
+ else if (isInterfaceDeclaration())
+ {
+ if (interfaces.length == 0)
+ {
+ alignsize = target.ptrsize;
+ structsize = target.ptrsize; // allow room for __vptr
+ }
+ }
+ else
+ {
+ alignsize = target.ptrsize;
+ structsize = target.ptrsize; // allow room for __vptr
+ if (hasMonitor())
+ structsize += target.ptrsize; // allow room for __monitor
+ }
+
+ //printf("finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
+ size_t bi = 0; // index into vtblInterfaces[]
+
+ /****
+ * Runs through the inheritance graph to set the BaseClass.offset fields.
+ * Recursive in order to account for the size of the interface classes, if they are
+ * more than just interfaces.
+ * Params:
+ * cd = interface to look at
+ * baseOffset = offset of where cd will be placed
+ * Returns:
+ * subset of instantiated size used by cd for interfaces
+ */
+ uint membersPlace(ClassDeclaration cd, uint baseOffset)
+ {
+ //printf(" membersPlace(%s, %d)\n", cd.toChars(), baseOffset);
+ uint offset = baseOffset;
+
+ foreach (BaseClass* b; cd.interfaces)
+ {
+ if (b.sym.sizeok != Sizeok.done)
+ b.sym.finalizeSize();
+ assert(b.sym.sizeok == Sizeok.done);
+
+ if (!b.sym.alignsize)
+ b.sym.alignsize = target.ptrsize;
+ alignmember(b.sym.alignsize, b.sym.alignsize, &offset);
+ assert(bi < vtblInterfaces.dim);
+
+ BaseClass* bv = (*vtblInterfaces)[bi];
+ if (b.sym.interfaces.length == 0)
+ {
+ //printf("\tvtblInterfaces[%d] b=%p b.sym = %s, offset = %d\n", bi, bv, bv.sym.toChars(), offset);
+ bv.offset = offset;
+ ++bi;
+ // All the base interfaces down the left side share the same offset
+ for (BaseClass* b2 = bv; b2.baseInterfaces.length; )
+ {
+ b2 = &b2.baseInterfaces[0];
+ b2.offset = offset;
+ //printf("\tvtblInterfaces[%d] b=%p sym = %s, offset = %d\n", bi, b2, b2.sym.toChars(), b2.offset);
+ }
+ }
+ membersPlace(b.sym, offset);
+ //printf(" %s size = %d\n", b.sym.toChars(), b.sym.structsize);
+ offset += b.sym.structsize;
+ if (alignsize < b.sym.alignsize)
+ alignsize = b.sym.alignsize;
+ }
+ return offset - baseOffset;
+ }
+
+ structsize += membersPlace(this, structsize);
+
+ if (isInterfaceDeclaration())
+ {
+ sizeok = Sizeok.done;
+ return;
+ }
+
+ // FIXME: Currently setFieldOffset functions need to increase fields
+ // to calculate each variable offsets. It can be improved later.
+ fields.setDim(0);
+
+ FieldState fieldState;
+ fieldState.offset = structsize;
+ foreach (s; *members)
+ {
+ s.setFieldOffset(this, fieldState, false);
+ }
+
+ sizeok = Sizeok.done;
+
+ // Calculate fields[i].overlapped
+ checkOverlappedFields();
+ }
+
+ /**************
+ * Returns: true if there's a __monitor field
+ */
+ final bool hasMonitor()
+ {
+ return classKind == ClassKind.d;
+ }
+
+ final bool isFuncHidden(FuncDeclaration fd)
+ {
+ //printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars());
+ Dsymbol s = search(Loc.initial, fd.ident, IgnoreAmbiguous | IgnoreErrors);
+ if (!s)
+ {
+ //printf("not found\n");
+ /* Because, due to a hack, if there are multiple definitions
+ * of fd.ident, NULL is returned.
+ */
+ return false;
+ }
+ s = s.toAlias();
+ if (auto os = s.isOverloadSet())
+ {
+ foreach (sm; os.a)
+ {
+ auto fm = sm.isFuncDeclaration();
+ if (overloadApply(fm, s => fd == s.isFuncDeclaration()))
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ auto f = s.isFuncDeclaration();
+ //printf("%s fdstart = %p\n", s.kind(), fdstart);
+ if (overloadApply(f, s => fd == s.isFuncDeclaration()))
+ return false;
+ return !fd.parent.isTemplateMixin();
+ }
+ }
+
+ /****************
+ * Find virtual function matching identifier and type.
+ * Used to build virtual function tables for interface implementations.
+ * Params:
+ * ident = function's identifier
+ * tf = function's type
+ * Returns:
+ * function symbol if found, null if not
+ * Errors:
+ * prints error message if more than one match
+ */
+ final FuncDeclaration findFunc(Identifier ident, TypeFunction tf)
+ {
+ //printf("ClassDeclaration.findFunc(%s, %s) %s\n", ident.toChars(), tf.toChars(), toChars());
+ FuncDeclaration fdmatch = null;
+ FuncDeclaration fdambig = null;
+
+ void updateBestMatch(FuncDeclaration fd)
+ {
+ fdmatch = fd;
+ fdambig = null;
+ //printf("Lfd fdmatch = %s %s [%s]\n", fdmatch.toChars(), fdmatch.type.toChars(), fdmatch.loc.toChars());
+ }
+
+ void searchVtbl(ref Dsymbols vtbl)
+ {
+ foreach (s; vtbl)
+ {
+ auto fd = s.isFuncDeclaration();
+ if (!fd)
+ continue;
+
+ // the first entry might be a ClassInfo
+ //printf("\t[%d] = %s\n", i, fd.toChars());
+ if (ident != fd.ident || fd.type.covariant(tf) != Covariant.yes)
+ {
+ //printf("\t\t%d\n", fd.type.covariant(tf));
+ continue;
+ }
+
+ //printf("fd.parent.isClassDeclaration() = %p\n", fd.parent.isClassDeclaration());
+ if (!fdmatch)
+ {
+ updateBestMatch(fd);
+ continue;
+ }
+ if (fd == fdmatch)
+ continue;
+
+ {
+ // Function type matching: exact > covariant
+ MATCH m1 = tf.equals(fd.type) ? MATCH.exact : MATCH.nomatch;
+ MATCH m2 = tf.equals(fdmatch.type) ? MATCH.exact : MATCH.nomatch;
+ if (m1 > m2)
+ {
+ updateBestMatch(fd);
+ continue;
+ }
+ else if (m1 < m2)
+ continue;
+ }
+ {
+ MATCH m1 = (tf.mod == fd.type.mod) ? MATCH.exact : MATCH.nomatch;
+ MATCH m2 = (tf.mod == fdmatch.type.mod) ? MATCH.exact : MATCH.nomatch;
+ if (m1 > m2)
+ {
+ updateBestMatch(fd);
+ continue;
+ }
+ else if (m1 < m2)
+ continue;
+ }
+ {
+ // The way of definition: non-mixin > mixin
+ MATCH m1 = fd.parent.isClassDeclaration() ? MATCH.exact : MATCH.nomatch;
+ MATCH m2 = fdmatch.parent.isClassDeclaration() ? MATCH.exact : MATCH.nomatch;
+ if (m1 > m2)
+ {
+ updateBestMatch(fd);
+ continue;
+ }
+ else if (m1 < m2)
+ continue;
+ }
+
+ fdambig = fd;
+ //printf("Lambig fdambig = %s %s [%s]\n", fdambig.toChars(), fdambig.type.toChars(), fdambig.loc.toChars());
+ }
+ }
+
+ searchVtbl(vtbl);
+ for (auto cd = this; cd; cd = cd.baseClass)
+ {
+ searchVtbl(cd.vtblFinal);
+ }
+
+ if (fdambig)
+ error("ambiguous virtual function `%s`", fdambig.toChars());
+
+ return fdmatch;
+ }
+
+ /****************************************
+ */
+ final bool isCOMclass() const
+ {
+ return com;
+ }
+
+ bool isCOMinterface() const
+ {
+ return false;
+ }
+
+ final bool isCPPclass() const
+ {
+ return classKind == ClassKind.cpp;
+ }
+
+ bool isCPPinterface() const
+ {
+ return false;
+ }
+
+ /****************************************
+ */
+ final bool isAbstract()
+ {
+ enum log = false;
+ if (isabstract != ThreeState.none)
+ return isabstract == ThreeState.yes;
+
+ if (log) printf("isAbstract(%s)\n", toChars());
+
+ bool no() { if (log) printf("no\n"); isabstract = ThreeState.no; return false; }
+ bool yes() { if (log) printf("yes\n"); isabstract = ThreeState.yes; return true; }
+
+ if (storage_class & STC.abstract_ || _scope && _scope.stc & STC.abstract_)
+ return yes();
+
+ if (errors)
+ return no();
+
+ /* https://issues.dlang.org/show_bug.cgi?id=11169
+ * Resolve forward references to all class member functions,
+ * and determine whether this class is abstract.
+ */
+ static int func(Dsymbol s)
+ {
+ auto fd = s.isFuncDeclaration();
+ if (!fd)
+ return 0;
+ if (fd.storage_class & STC.static_)
+ return 0;
+
+ if (fd.isAbstract())
+ return 1;
+ return 0;
+ }
+
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ auto s = (*members)[i];
+ if (s.apply(&func))
+ {
+ return yes();
+ }
+ }
+
+ /* If the base class is not abstract, then this class cannot
+ * be abstract.
+ */
+ if (!isInterfaceDeclaration() && (!baseClass || !baseClass.isAbstract()))
+ return no();
+
+ /* If any abstract functions are inherited, but not overridden,
+ * then the class is abstract. Do this by checking the vtbl[].
+ * Need to do semantic() on class to fill the vtbl[].
+ */
+ this.dsymbolSemantic(null);
+
+ /* The next line should work, but does not because when ClassDeclaration.dsymbolSemantic()
+ * is called recursively it can set PASS.semanticdone without finishing it.
+ */
+ //if (semanticRun < PASS.semanticdone)
+ {
+ /* Could not complete semantic(). Try running semantic() on
+ * each of the virtual functions,
+ * which will fill in the vtbl[] overrides.
+ */
+ static int virtualSemantic(Dsymbol s)
+ {
+ auto fd = s.isFuncDeclaration();
+ if (fd && !(fd.storage_class & STC.static_) && !fd.isUnitTestDeclaration())
+ fd.dsymbolSemantic(null);
+ return 0;
+ }
+
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ auto s = (*members)[i];
+ s.apply(&virtualSemantic);
+ }
+ }
+
+ /* Finally, check the vtbl[]
+ */
+ foreach (i; 1 .. vtbl.dim)
+ {
+ auto fd = vtbl[i].isFuncDeclaration();
+ //if (fd) printf("\tvtbl[%d] = [%s] %s\n", i, fd.loc.toChars(), fd.toPrettyChars());
+ if (!fd || fd.isAbstract())
+ {
+ return yes();
+ }
+ }
+
+ return no();
+ }
+
+ /****************************************
+ * Determine if slot 0 of the vtbl[] is reserved for something else.
+ * For class objects, yes, this is where the classinfo ptr goes.
+ * For COM interfaces, no.
+ * For non-COM interfaces, yes, this is where the Interface ptr goes.
+ * Returns:
+ * 0 vtbl[0] is first virtual function pointer
+ * 1 vtbl[0] is classinfo/interfaceinfo pointer
+ */
+ int vtblOffset() const
+ {
+ return classKind == ClassKind.cpp ? 0 : 1;
+ }
+
+ /****************************************
+ */
+ override const(char)* kind() const
+ {
+ return "class";
+ }
+
+ /****************************************
+ */
+ override final void addLocalClass(ClassDeclarations* aclasses)
+ {
+ if (classKind != ClassKind.objc)
+ aclasses.push(this);
+ }
+
+ override final void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
+ {
+ .objc.addSymbols(this, classes, categories);
+ }
+
+ // Back end
+ Dsymbol vtblsym;
+
+ final Dsymbol vtblSymbol()
+ {
+ if (!vtblsym)
+ {
+ auto vtype = Type.tvoidptr.immutableOf().sarrayOf(vtbl.dim);
+ auto var = new VarDeclaration(loc, vtype, Identifier.idPool("__vtbl"), null, STC.immutable_ | STC.static_);
+ var.addMember(null, this);
+ var.isdataseg = 1;
+ var.linkage = LINK.d;
+ var.semanticRun = PASS.semanticdone; // no more semantic wanted
+ vtblsym = var;
+ }
+ return vtblsym;
+ }
+
+ override final inout(ClassDeclaration) isClassDeclaration() inout @nogc nothrow pure @safe
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class InterfaceDeclaration : ClassDeclaration
+{
+ extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses)
+ {
+ super(loc, id, baseclasses, null, false);
+ if (id == Id.IUnknown) // IUnknown is the root of all COM interfaces
+ {
+ com = true;
+ classKind = ClassKind.cpp; // IUnknown is also a C++ interface
+ }
+ }
+
+ override InterfaceDeclaration syntaxCopy(Dsymbol s)
+ {
+ InterfaceDeclaration id =
+ s ? cast(InterfaceDeclaration)s
+ : new InterfaceDeclaration(loc, ident, null);
+ ClassDeclaration.syntaxCopy(id);
+ return id;
+ }
+
+
+ override Scope* newScope(Scope* sc)
+ {
+ auto sc2 = super.newScope(sc);
+ if (com)
+ sc2.linkage = LINK.windows;
+ else if (classKind == ClassKind.cpp)
+ sc2.linkage = LINK.cpp;
+ else if (classKind == ClassKind.objc)
+ sc2.linkage = LINK.objc;
+ return sc2;
+ }
+
+ /*******************************************
+ * Determine if 'this' is a base class of cd.
+ * (Actually, if it is an interface supported by cd)
+ * Output:
+ * *poffset offset to start of class
+ * OFFSET_RUNTIME must determine offset at runtime
+ * Returns:
+ * false not a base
+ * true is a base
+ */
+ override bool isBaseOf(ClassDeclaration cd, int* poffset) pure nothrow @nogc
+ {
+ //printf("%s.InterfaceDeclaration.isBaseOf(cd = '%s')\n", toChars(), cd.toChars());
+ assert(!baseClass);
+ foreach (b; cd.interfaces)
+ {
+ //printf("\tX base %s\n", b.sym.toChars());
+ if (this == b.sym)
+ {
+ //printf("\tfound at offset %d\n", b.offset);
+ if (poffset)
+ {
+ // don't return incorrect offsets
+ // https://issues.dlang.org/show_bug.cgi?id=16980
+ *poffset = cd.sizeok == Sizeok.done ? b.offset : OFFSET_FWDREF;
+ }
+ // printf("\tfound at offset %d\n", b.offset);
+ return true;
+ }
+ if (baseClassImplementsInterface(this, b, poffset))
+ return true;
+ }
+ if (cd.baseClass && isBaseOf(cd.baseClass, poffset))
+ return true;
+
+ if (poffset)
+ *poffset = 0;
+ return false;
+ }
+
+ /*******************************************
+ */
+ override const(char)* kind() const
+ {
+ return "interface";
+ }
+
+ /****************************************
+ * Determine if slot 0 of the vtbl[] is reserved for something else.
+ * For class objects, yes, this is where the ClassInfo ptr goes.
+ * For COM interfaces, no.
+ * For non-COM interfaces, yes, this is where the Interface ptr goes.
+ */
+ override int vtblOffset() const
+ {
+ if (isCOMinterface() || isCPPinterface())
+ return 0;
+ return 1;
+ }
+
+ override bool isCPPinterface() const
+ {
+ return classKind == ClassKind.cpp;
+ }
+
+ override bool isCOMinterface() const
+ {
+ return com;
+ }
+
+ override inout(InterfaceDeclaration) isInterfaceDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/**
+ * Returns whether `bc` implements `id`, including indirectly (`bc` implements an interfaces
+ * that inherits from `id`)
+ *
+ * Params:
+ * id = the interface
+ * bc = the base class
+ * poffset = out parameter, offset of the interface in an object
+ *
+ * Returns:
+ * true if the `bc` implements `id`, false otherwise
+ **/
+private bool baseClassImplementsInterface(InterfaceDeclaration id, BaseClass* bc, int* poffset) pure nothrow @nogc
+{
+ //printf("%s.InterfaceDeclaration.isBaseOf(bc = '%s')\n", id.toChars(), bc.sym.toChars());
+ for (size_t j = 0; j < bc.baseInterfaces.length; j++)
+ {
+ BaseClass* b = &bc.baseInterfaces[j];
+ //printf("\tY base %s\n", b.sym.toChars());
+ if (id == b.sym)
+ {
+ //printf("\tfound at offset %d\n", b.offset);
+ if (poffset)
+ {
+ *poffset = b.offset;
+ }
+ return true;
+ }
+ if (baseClassImplementsInterface(id, b, poffset))
+ {
+ return true;
+ }
+ }
+
+ if (poffset)
+ *poffset = 0;
+ return false;
+}
diff --git a/gcc/d/dmd/declaration.c b/gcc/d/dmd/declaration.c
deleted file mode 100644
index a9394dcce7f..00000000000
--- a/gcc/d/dmd/declaration.c
+++ /dev/null
@@ -1,1575 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/declaration.c
- */
-
-#include "root/dsystem.h"
-#include "root/checkedint.h"
-
-#include "errors.h"
-#include "init.h"
-#include "declaration.h"
-#include "attrib.h"
-#include "mtype.h"
-#include "template.h"
-#include "scope.h"
-#include "aggregate.h"
-#include "module.h"
-#include "import.h"
-#include "id.h"
-#include "expression.h"
-#include "statement.h"
-#include "ctfe.h"
-#include "target.h"
-#include "hdrgen.h"
-
-bool checkNestedRef(Dsymbol *s, Dsymbol *p);
-
-/************************************
- * Check to see the aggregate type is nested and its context pointer is
- * accessible from the current scope.
- * Returns true if error occurs.
- */
-bool checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad, size_t iStart = 0)
-{
- Dsymbol *sparent = ad->toParent2();
- Dsymbol *s = sc->func;
- if (ad->isNested() && s)
- {
- //printf("ad = %p %s [%s], parent:%p\n", ad, ad->toChars(), ad->loc.toChars(), ad->parent);
- //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent->toChars(), sparent->loc.toChars(), sparent->parent->toChars());
- if (checkNestedRef(s, sparent))
- {
- error(loc, "cannot access frame pointer of %s", ad->toPrettyChars());
- return true;
- }
- }
-
- bool result = false;
- for (size_t i = iStart; i < ad->fields.length; i++)
- {
- VarDeclaration *vd = ad->fields[i];
- Type *tb = vd->type->baseElemOf();
- if (tb->ty == Tstruct)
- {
- result |= checkFrameAccess(loc, sc, ((TypeStruct *)tb)->sym);
- }
- }
- return result;
-}
-
-/********************************* Declaration ****************************/
-
-Declaration::Declaration(Identifier *id)
- : Dsymbol(id)
-{
- type = NULL;
- originalType = NULL;
- storage_class = STCundefined;
- protection = Prot(Prot::undefined);
- linkage = LINKdefault;
- inuse = 0;
- mangleOverride = NULL;
-}
-
-const char *Declaration::kind() const
-{
- return "declaration";
-}
-
-d_uns64 Declaration::size(Loc)
-{
- assert(type);
- return type->size();
-}
-
-bool Declaration::isDelete()
-{
- return false;
-}
-
-bool Declaration::isDataseg()
-{
- return false;
-}
-
-bool Declaration::isThreadlocal()
-{
- return false;
-}
-
-bool Declaration::isCodeseg() const
-{
- return false;
-}
-
-Prot Declaration::prot()
-{
- return protection;
-}
-
-/*************************************
- * Check to see if declaration can be modified in this context (sc).
- * Issue error if not.
- */
-
-int Declaration::checkModify(Loc loc, Scope *sc, Type *, Expression *e1, int flag)
-{
- VarDeclaration *v = isVarDeclaration();
- if (v && v->canassign)
- return 2;
-
- if (isParameter() || isResult())
- {
- for (Scope *scx = sc; scx; scx = scx->enclosing)
- {
- if (scx->func == parent && (scx->flags & SCOPEcontract))
- {
- const char *s = isParameter() && parent->ident != Id::ensure ? "parameter" : "result";
- if (!flag) error(loc, "cannot modify %s `%s` in contract", s, toChars());
- return 2; // do not report type related errors
- }
- }
- }
-
- if (e1 && e1->op == TOKthis && isField())
- {
- VarDeclaration *vthis = e1->isThisExp()->var;
- for (Scope *scx = sc; scx; scx = scx->enclosing)
- {
- if (scx->func == vthis->parent && (scx->flags & SCOPEcontract))
- {
- if (!flag)
- error(loc, "cannot modify parameter `this` in contract");
- return 2; // do not report type related errors
- }
- }
- }
-
- if (v && (isCtorinit() || isField()))
- {
- // It's only modifiable if inside the right constructor
- if ((storage_class & (STCforeach | STCref)) == (STCforeach | STCref))
- return 2;
- return modifyFieldVar(loc, sc, v, e1) ? 2 : 1;
- }
- return 1;
-}
-
-/**
- * Issue an error if an attempt to call a disabled method is made
- *
- * If the declaration is disabled but inside a disabled function,
- * returns `true` but do not issue an error message.
- *
- * Params:
- * loc = Location information of the call
- * sc = Scope in which the call occurs
- * isAliasedDeclaration = if `true` searches overload set
- *
- * Returns:
- * `true` if this `Declaration` is `@disable`d, `false` otherwise.
- */
-bool Declaration::checkDisabled(Loc loc, Scope *sc, bool isAliasedDeclaration)
-{
- if (!(storage_class & STCdisable))
- return false;
-
- if (sc->func && (sc->func->storage_class & STCdisable))
- return true;
-
- Dsymbol *p = toParent();
- if (p && isPostBlitDeclaration())
- {
- p->error(loc, "is not copyable because it is annotated with `@disable`");
- return true;
- }
-
- // if the function is @disabled, maybe there
- // is an overload in the overload set that isn't
- if (isAliasedDeclaration)
- {
- FuncDeclaration *fd = isFuncDeclaration();
- if (fd)
- {
- for (FuncDeclaration *ovl = fd; ovl; ovl = (FuncDeclaration *)ovl->overnext)
- if (!(ovl->storage_class & STCdisable))
- return false;
- }
- }
- error(loc, "cannot be used because it is annotated with `@disable`");
- return true;
-}
-
-Dsymbol *Declaration::search(const Loc &loc, Identifier *ident, int flags)
-{
- Dsymbol *s = Dsymbol::search(loc, ident, flags);
- if (!s && type)
- {
- s = type->toDsymbol(_scope);
- if (s)
- s = s->search(loc, ident, flags);
- }
- return s;
-}
-
-
-/********************************* TupleDeclaration ****************************/
-
-TupleDeclaration::TupleDeclaration(Loc loc, Identifier *id, Objects *objects)
- : Declaration(id)
-{
- this->loc = loc;
- this->type = NULL;
- this->objects = objects;
- this->isexp = false;
- this->tupletype = NULL;
-}
-
-Dsymbol *TupleDeclaration::syntaxCopy(Dsymbol *)
-{
- assert(0);
- return NULL;
-}
-
-const char *TupleDeclaration::kind() const
-{
- return "tuple";
-}
-
-Type *TupleDeclaration::getType()
-{
- /* If this tuple represents a type, return that type
- */
-
- //printf("TupleDeclaration::getType() %s\n", toChars());
- if (isexp)
- return NULL;
- if (!tupletype)
- {
- /* It's only a type tuple if all the Object's are types
- */
- for (size_t i = 0; i < objects->length; i++)
- {
- RootObject *o = (*objects)[i];
- if (o->dyncast() != DYNCAST_TYPE)
- {
- //printf("\tnot[%d], %p, %d\n", i, o, o->dyncast());
- return NULL;
- }
- }
-
- /* We know it's a type tuple, so build the TypeTuple
- */
- Types *types = (Types *)objects;
- Parameters *args = new Parameters();
- args->setDim(objects->length);
- OutBuffer buf;
- int hasdeco = 1;
- for (size_t i = 0; i < types->length; i++)
- {
- Type *t = (*types)[i];
- //printf("type = %s\n", t->toChars());
- Parameter *arg = new Parameter(0, t, NULL, NULL, NULL);
- (*args)[i] = arg;
- if (!t->deco)
- hasdeco = 0;
- }
-
- tupletype = new TypeTuple(args);
- if (hasdeco)
- return typeSemantic(tupletype, Loc(), NULL);
- }
-
- return tupletype;
-}
-
-Dsymbol *TupleDeclaration::toAlias2()
-{
- //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects->toChars());
-
- for (size_t i = 0; i < objects->length; i++)
- {
- RootObject *o = (*objects)[i];
- if (Dsymbol *s = isDsymbol(o))
- {
- s = s->toAlias2();
- (*objects)[i] = s;
- }
- }
- return this;
-}
-
-bool TupleDeclaration::needThis()
-{
- //printf("TupleDeclaration::needThis(%s)\n", toChars());
- for (size_t i = 0; i < objects->length; i++)
- {
- RootObject *o = (*objects)[i];
- if (o->dyncast() == DYNCAST_EXPRESSION)
- {
- Expression *e = (Expression *)o;
- if (e->op == TOKdsymbol)
- {
- DsymbolExp *ve = (DsymbolExp *)e;
- Declaration *d = ve->s->isDeclaration();
- if (d && d->needThis())
- {
- return true;
- }
- }
- }
- }
- return false;
-}
-
-/********************************* AliasDeclaration ****************************/
-
-AliasDeclaration::AliasDeclaration(Loc loc, Identifier *id, Type *type)
- : Declaration(id)
-{
- //printf("AliasDeclaration(id = '%s', type = %p)\n", id->toChars(), type);
- //printf("type = '%s'\n", type->toChars());
- this->loc = loc;
- this->type = type;
- this->aliassym = NULL;
- this->_import = NULL;
- this->overnext = NULL;
- assert(type);
-}
-
-AliasDeclaration::AliasDeclaration(Loc loc, Identifier *id, Dsymbol *s)
- : Declaration(id)
-{
- //printf("AliasDeclaration(id = '%s', s = %p)\n", id->toChars(), s);
- assert(s != this);
- this->loc = loc;
- this->type = NULL;
- this->aliassym = s;
- this->_import = NULL;
- this->overnext = NULL;
- assert(s);
-}
-
-AliasDeclaration *AliasDeclaration::create(Loc loc, Identifier *id, Type *type)
-{
- return new AliasDeclaration(loc, id, type);
-}
-
-Dsymbol *AliasDeclaration::syntaxCopy(Dsymbol *s)
-{
- //printf("AliasDeclaration::syntaxCopy()\n");
- assert(!s);
- AliasDeclaration *sa =
- type ? new AliasDeclaration(loc, ident, type->syntaxCopy())
- : new AliasDeclaration(loc, ident, aliassym->syntaxCopy(NULL));
- sa->storage_class = storage_class;
- return sa;
-}
-
-bool AliasDeclaration::overloadInsert(Dsymbol *s)
-{
- //printf("[%s] AliasDeclaration::overloadInsert('%s') s = %s %s @ [%s]\n",
- // loc.toChars(), toChars(), s->kind(), s->toChars(), s->loc.toChars());
-
- /** Aliases aren't overloadable themselves, but if their Aliasee is
- * overloadable they are converted to an overloadable Alias (either
- * FuncAliasDeclaration or OverDeclaration).
- *
- * This is done by moving the Aliasee into such an overloadable alias
- * which is then used to replace the existing Aliasee. The original
- * Alias (_this_) remains a useless shell.
- *
- * This is a horrible mess. It was probably done to avoid replacing
- * existing AST nodes and references, but it needs a major
- * simplification b/c it's too complex to maintain.
- *
- * A simpler approach might be to merge any colliding symbols into a
- * simple Overload class (an array) and then later have that resolve
- * all collisions.
- */
- if (semanticRun >= PASSsemanticdone)
- {
- /* Semantic analysis is already finished, and the aliased entity
- * is not overloadable.
- */
- if (type)
- return false;
-
- /* When s is added in member scope by static if, mixin("code") or others,
- * aliassym is determined already. See the case in: test/compilable/test61.d
- */
- Dsymbol *sa = aliassym->toAlias();
- if (FuncDeclaration *fd = sa->isFuncDeclaration())
- {
- FuncAliasDeclaration *fa = new FuncAliasDeclaration(ident, fd);
- fa->protection = protection;
- fa->parent = parent;
- aliassym = fa;
- return aliassym->overloadInsert(s);
- }
- if (TemplateDeclaration *td = sa->isTemplateDeclaration())
- {
- OverDeclaration *od = new OverDeclaration(ident, td);
- od->protection = protection;
- od->parent = parent;
- aliassym = od;
- return aliassym->overloadInsert(s);
- }
- if (OverDeclaration *od = sa->isOverDeclaration())
- {
- if (sa->ident != ident || sa->parent != parent)
- {
- od = new OverDeclaration(ident, od);
- od->protection = protection;
- od->parent = parent;
- aliassym = od;
- }
- return od->overloadInsert(s);
- }
- if (OverloadSet *os = sa->isOverloadSet())
- {
- if (sa->ident != ident || sa->parent != parent)
- {
- os = new OverloadSet(ident, os);
- // TODO: protection is lost here b/c OverloadSets have no protection attribute
- // Might no be a practical issue, b/c the code below fails to resolve the overload anyhow.
- // ----
- // module os1;
- // import a, b;
- // private alias merged = foo; // private alias to overload set of a.foo and b.foo
- // ----
- // module os2;
- // import a, b;
- // public alias merged = bar; // public alias to overload set of a.bar and b.bar
- // ----
- // module bug;
- // import os1, os2;
- // void test() { merged(123); } // should only look at os2.merged
- //
- // os.protection = protection;
- os->parent = parent;
- aliassym = os;
- }
- os->push(s);
- return true;
- }
- return false;
- }
-
- /* Don't know yet what the aliased symbol is, so assume it can
- * be overloaded and check later for correctness.
- */
- if (overnext)
- return overnext->overloadInsert(s);
- if (s == this)
- return true;
- overnext = s;
- return true;
-}
-
-const char *AliasDeclaration::kind() const
-{
- return "alias";
-}
-
-Type *AliasDeclaration::getType()
-{
- if (type)
- return type;
- return toAlias()->getType();
-}
-
-Dsymbol *AliasDeclaration::toAlias()
-{
- //printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s', inuse = %d)\n",
- // loc.toChars(), toChars(), this, aliassym, aliassym ? aliassym->kind() : "", inuse);
- assert(this != aliassym);
- //static int count; if (++count == 10) *(char*)0=0;
- if (inuse == 1 && type && _scope)
- {
- inuse = 2;
- unsigned olderrors = global.errors;
- Dsymbol *s = type->toDsymbol(_scope);
- //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type->toChars(), s, this);
- if (global.errors != olderrors)
- goto Lerr;
- if (s)
- {
- s = s->toAlias();
- if (global.errors != olderrors)
- goto Lerr;
- aliassym = s;
- inuse = 0;
- }
- else
- {
- Type *t = typeSemantic(type, loc, _scope);
- if (t->ty == Terror)
- goto Lerr;
- if (global.errors != olderrors)
- goto Lerr;
- //printf("t = %s\n", t->toChars());
- inuse = 0;
- }
- }
- if (inuse)
- {
- error("recursive alias declaration");
-
- Lerr:
- // Avoid breaking "recursive alias" state during errors gagged
- if (global.gag)
- return this;
-
- aliassym = new AliasDeclaration(loc, ident, Type::terror);
- type = Type::terror;
- return aliassym;
- }
-
- if (semanticRun >= PASSsemanticdone)
- {
- // semantic is already done.
-
- // Do not see aliassym !is null, because of lambda aliases.
-
- // Do not see type.deco !is null, even so "alias T = const int;` needs
- // semantic analysis to take the storage class `const` as type qualifier.
- }
- else
- {
- if (_import && _import->_scope)
- {
- /* If this is an internal alias for selective/renamed import,
- * load the module first.
- */
- dsymbolSemantic(_import, NULL);
- }
- if (_scope)
- {
- aliasSemantic(this, _scope);
- }
- }
-
- inuse = 1;
- Dsymbol *s = aliassym ? aliassym->toAlias() : this;
- inuse = 0;
- return s;
-}
-
-Dsymbol *AliasDeclaration::toAlias2()
-{
- if (inuse)
- {
- error("recursive alias declaration");
- return this;
- }
- inuse = 1;
- Dsymbol *s = aliassym ? aliassym->toAlias2() : this;
- inuse = 0;
- return s;
-}
-
-bool AliasDeclaration::isOverloadable()
-{
- // assume overloadable until alias is resolved
- return semanticRun < PASSsemanticdone ||
- (aliassym && aliassym->isOverloadable());
-}
-
-/****************************** OverDeclaration **************************/
-
-OverDeclaration::OverDeclaration(Identifier *ident, Dsymbol *s, bool hasOverloads)
- : Declaration(ident)
-{
- this->overnext = NULL;
- this->aliassym = s;
-
- this->hasOverloads = hasOverloads;
- if (hasOverloads)
- {
- if (OverDeclaration *od = aliassym->isOverDeclaration())
- this->hasOverloads = od->hasOverloads;
- }
- else
- {
- // for internal use
- assert(!aliassym->isOverDeclaration());
- }
-}
-
-const char *OverDeclaration::kind() const
-{
- return "overload alias"; // todo
-}
-
-bool OverDeclaration::equals(RootObject *o)
-{
- if (this == o)
- return true;
-
- Dsymbol *s = isDsymbol(o);
- if (!s)
- return false;
-
- OverDeclaration *od1 = this;
- if (OverDeclaration *od2 = s->isOverDeclaration())
- {
- return od1->aliassym->equals(od2->aliassym) &&
- od1->hasOverloads == od2->hasOverloads;
- }
- if (aliassym == s)
- {
- if (hasOverloads)
- return true;
- if (FuncDeclaration *fd = s->isFuncDeclaration())
- {
- return fd->isUnique() != NULL;
- }
- if (TemplateDeclaration *td = s->isTemplateDeclaration())
- {
- return td->overnext == NULL;
- }
- }
- return false;
-}
-
-bool OverDeclaration::overloadInsert(Dsymbol *s)
-{
- //printf("OverDeclaration::overloadInsert('%s') aliassym = %p, overnext = %p\n", s->toChars(), aliassym, overnext);
- if (overnext)
- return overnext->overloadInsert(s);
- if (s == this)
- return true;
- overnext = s;
- return true;
-}
-
-Dsymbol *OverDeclaration::toAlias()
-{
- return this;
-}
-
-bool OverDeclaration::isOverloadable()
-{
- return true;
-}
-
-Dsymbol *OverDeclaration::isUnique()
-{
- if (!hasOverloads)
- {
- if (aliassym->isFuncDeclaration() ||
- aliassym->isTemplateDeclaration())
- {
- return aliassym;
- }
- }
-
- struct ParamUniqueSym
- {
- static int fp(void *param, Dsymbol *s)
- {
- Dsymbol **ps = (Dsymbol **)param;
- if (*ps)
- {
- *ps = NULL;
- return 1; // ambiguous, done
- }
- else
- {
- *ps = s;
- return 0;
- }
- }
- };
- Dsymbol *result = NULL;
- overloadApply(aliassym, &result, &ParamUniqueSym::fp);
- return result;
-}
-
-/********************************* VarDeclaration ****************************/
-
-VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer *init)
- : Declaration(id)
-{
- //printf("VarDeclaration('%s')\n", id->toChars());
- assert(id);
- assert(type || init);
- this->type = type;
- this->_init = init;
- this->loc = loc;
- offset = 0;
- isargptr = false;
- alignment = 0;
- ctorinit = 0;
- aliassym = NULL;
- onstack = false;
- mynew = false;
- canassign = 0;
- overlapped = false;
- overlapUnsafe = false;
- doNotInferScope = false;
- isdataseg = 0;
- lastVar = NULL;
- endlinnum = 0;
- ctfeAdrOnStack = -1;
- edtor = NULL;
- range = NULL;
-
- static unsigned nextSequenceNumber = 0;
- this->sequenceNumber = ++nextSequenceNumber;
-}
-
-VarDeclaration *VarDeclaration::create(Loc loc, Type *type, Identifier *id, Initializer *init)
-{
- return new VarDeclaration(loc, type, id, init);
-}
-
-Dsymbol *VarDeclaration::syntaxCopy(Dsymbol *s)
-{
- //printf("VarDeclaration::syntaxCopy(%s)\n", toChars());
- assert(!s);
- VarDeclaration *v = new VarDeclaration(loc,
- type ? type->syntaxCopy() : NULL,
- ident,
- _init ? _init->syntaxCopy() : NULL);
- v->storage_class = storage_class;
- return v;
-}
-
-void VarDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion)
-{
- //printf("VarDeclaration::setFieldOffset(ad = %s) %s\n", ad->toChars(), toChars());
-
- if (aliassym)
- {
- // If this variable was really a tuple, set the offsets for the tuple fields
- TupleDeclaration *v2 = aliassym->isTupleDeclaration();
- assert(v2);
- for (size_t i = 0; i < v2->objects->length; i++)
- {
- RootObject *o = (*v2->objects)[i];
- assert(o->dyncast() == DYNCAST_EXPRESSION);
- Expression *e = (Expression *)o;
- assert(e->op == TOKdsymbol);
- DsymbolExp *se = (DsymbolExp *)e;
- se->s->setFieldOffset(ad, poffset, isunion);
- }
- return;
- }
-
- if (!isField())
- return;
- assert(!(storage_class & (STCstatic | STCextern | STCparameter | STCtls)));
-
- //printf("+VarDeclaration::setFieldOffset(ad = %s) %s\n", ad->toChars(), toChars());
-
- /* Fields that are tuples appear both as part of TupleDeclarations and
- * as members. That means ignore them if they are already a field.
- */
- if (offset)
- {
- // already a field
- *poffset = ad->structsize; // Bugzilla 13613
- return;
- }
- for (size_t i = 0; i < ad->fields.length; i++)
- {
- if (ad->fields[i] == this)
- {
- // already a field
- *poffset = ad->structsize; // Bugzilla 13613
- return;
- }
- }
-
- // Check for forward referenced types which will fail the size() call
- Type *t = type->toBasetype();
- if (storage_class & STCref)
- {
- // References are the size of a pointer
- t = Type::tvoidptr;
- }
- Type *tv = t->baseElemOf();
- if (tv->ty == Tstruct)
- {
- TypeStruct *ts = (TypeStruct *)tv;
- assert(ts->sym != ad); // already checked in ad->determineFields()
- if (!ts->sym->determineSize(loc))
- {
- type = Type::terror;
- errors = true;
- return;
- }
- }
-
- // List in ad->fields. Even if the type is error, it's necessary to avoid
- // pointless error diagnostic "more initializers than fields" on struct literal.
- ad->fields.push(this);
-
- if (t->ty == Terror)
- return;
-
- const d_uns64 sz = t->size(loc);
- assert(sz != SIZE_INVALID && sz < UINT32_MAX);
- unsigned memsize = (unsigned)sz; // size of member
- unsigned memalignsize = target.fieldalign(t); // size of member for alignment purposes
-
- offset = AggregateDeclaration::placeField(poffset, memsize, memalignsize, alignment,
- &ad->structsize, &ad->alignsize, isunion);
-
- //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize);
-
- //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad->toChars(), offset, memsize);
-}
-
-const char *VarDeclaration::kind() const
-{
- return "variable";
-}
-
-Dsymbol *VarDeclaration::toAlias()
-{
- //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
- if ((!type || !type->deco) && _scope)
- dsymbolSemantic(this, _scope);
-
- assert(this != aliassym);
- Dsymbol *s = aliassym ? aliassym->toAlias() : this;
- return s;
-}
-
-AggregateDeclaration *VarDeclaration::isThis()
-{
- AggregateDeclaration *ad = NULL;
-
- if (!(storage_class & (STCstatic | STCextern | STCmanifest | STCtemplateparameter |
- STCtls | STCgshared | STCctfe)))
- {
- for (Dsymbol *s = this; s; s = s->parent)
- {
- ad = s->isMember();
- if (ad)
- break;
- if (!s->parent || !s->parent->isTemplateMixin()) break;
- }
- }
- return ad;
-}
-
-bool VarDeclaration::needThis()
-{
- //printf("VarDeclaration::needThis(%s, x%x)\n", toChars(), storage_class);
- return isField();
-}
-
-bool VarDeclaration::isExport() const
-{
- return protection.kind == Prot::export_;
-}
-
-bool VarDeclaration::isImportedSymbol() const
-{
- if (protection.kind == Prot::export_ && !_init &&
- (storage_class & STCstatic || parent->isModule()))
- return true;
- return false;
-}
-
-/*******************************************
- * Helper function for the expansion of manifest constant.
- */
-Expression *VarDeclaration::expandInitializer(Loc loc)
-{
- assert((storage_class & STCmanifest) && _init);
-
- Expression *e = getConstInitializer();
- if (!e)
- {
- ::error(loc, "cannot make expression out of initializer for %s", toChars());
- return new ErrorExp();
- }
-
- e = e->copy();
- e->loc = loc; // for better error message
- return e;
-}
-
-void VarDeclaration::checkCtorConstInit()
-{
-#if 0 /* doesn't work if more than one static ctor */
- if (ctorinit == 0 && isCtorinit() && !isField())
- error("missing initializer in static constructor for const variable");
-#endif
-}
-
-bool lambdaCheckForNestedRef(Expression *e, Scope *sc);
-
-/************************************
- * Check to see if this variable is actually in an enclosing function
- * rather than the current one.
- * Returns true if error occurs.
- */
-bool VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
-{
- //printf("VarDeclaration::checkNestedReference() %s\n", toChars());
- if (sc->intypeof == 1 || (sc->flags & SCOPEctfe))
- return false;
- if (!parent || parent == sc->parent)
- return false;
- if (isDataseg() || (storage_class & STCmanifest))
- return false;
-
- // The current function
- FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
- if (!fdthis)
- return false; // out of function scope
-
- Dsymbol *p = toParent2();
-
- // Function literals from fdthis to p must be delegates
- checkNestedRef(fdthis, p);
-
- // The function that this variable is in
- FuncDeclaration *fdv = p->isFuncDeclaration();
- if (!fdv || fdv == fdthis)
- return false;
-
- // Add fdthis to nestedrefs[] if not already there
- if (!nestedrefs.contains(fdthis))
- nestedrefs.push(fdthis);
-
- /* __require and __ensure will always get called directly,
- * so they never make outer functions closure.
- */
- if (fdthis->ident == Id::require || fdthis->ident == Id::ensure)
- return false;
-
- //printf("\tfdv = %s\n", fdv->toChars());
- //printf("\tfdthis = %s\n", fdthis->toChars());
- if (loc.filename)
- {
- int lv = fdthis->getLevel(loc, sc, fdv);
- if (lv == -2) // error
- return true;
- }
-
- // Add this to fdv->closureVars[] if not already there
- if (!sc->intypeof && !(sc->flags & SCOPEcompile))
- {
- if (!fdv->closureVars.contains(this))
- fdv->closureVars.push(this);
- }
-
- //printf("fdthis is %s\n", fdthis->toChars());
- //printf("var %s in function %s is nested ref\n", toChars(), fdv->toChars());
- // __dollar creates problems because it isn't a real variable Bugzilla 3326
- if (ident == Id::dollar)
- {
- ::error(loc, "cannnot use $ inside a function literal");
- return true;
- }
-
- if (ident == Id::withSym) // Bugzilla 1759
- {
- ExpInitializer *ez = _init->isExpInitializer();
- assert(ez);
- Expression *e = ez->exp;
- if (e->op == TOKconstruct || e->op == TOKblit)
- e = ((AssignExp *)e)->e2;
- return lambdaCheckForNestedRef(e, sc);
- }
-
- return false;
-}
-
-/*******************************************
- * If variable has a constant expression initializer, get it.
- * Otherwise, return NULL.
- */
-
-Expression *VarDeclaration::getConstInitializer(bool needFullType)
-{
- assert(type && _init);
-
- // Ungag errors when not speculative
- unsigned oldgag = global.gag;
- if (global.gag)
- {
- Dsymbol *sym = toParent()->isAggregateDeclaration();
- if (sym && !sym->isSpeculative())
- global.gag = 0;
- }
-
- if (_scope)
- {
- inuse++;
- _init = initializerSemantic(_init, _scope, type, INITinterpret);
- _scope = NULL;
- inuse--;
- }
- Expression *e = initializerToExpression(_init, needFullType ? type : NULL);
-
- global.gag = oldgag;
- return e;
-}
-
-/*************************************
- * Return true if we can take the address of this variable.
- */
-
-bool VarDeclaration::canTakeAddressOf()
-{
- return !(storage_class & STCmanifest);
-}
-
-
-/*******************************
- * Does symbol go into data segment?
- * Includes extern variables.
- */
-
-bool VarDeclaration::isDataseg()
-{
- if (isdataseg == 0) // the value is not cached
- {
- isdataseg = 2; // The Variables does not go into the datasegment
-
- if (!canTakeAddressOf())
- {
- return false;
- }
-
- Dsymbol *parent = toParent();
- if (!parent && !(storage_class & STCstatic))
- {
- error("forward referenced");
- type = Type::terror;
- }
- else if (storage_class & (STCstatic | STCextern | STCtls | STCgshared) ||
- parent->isModule() || parent->isTemplateInstance() || parent->isNspace())
- {
- assert(!isParameter() && !isResult());
- isdataseg = 1; // It is in the DataSegment
- }
- }
-
- return (isdataseg == 1);
-}
-
-/************************************
- * Does symbol go into thread local storage?
- */
-
-bool VarDeclaration::isThreadlocal()
-{
- //printf("VarDeclaration::isThreadlocal(%p, '%s')\n", this, toChars());
- /* Data defaults to being thread-local. It is not thread-local
- * if it is immutable, const or shared.
- */
- bool i = isDataseg() &&
- !(storage_class & (STCimmutable | STCconst | STCshared | STCgshared));
- //printf("\treturn %d\n", i);
- return i;
-}
-
-/********************************************
- * Can variable be read and written by CTFE?
- */
-
-bool VarDeclaration::isCTFE()
-{
- return (storage_class & STCctfe) != 0; // || !isDataseg();
-}
-
-bool VarDeclaration::isOverlappedWith(VarDeclaration *v)
-{
- const d_uns64 vsz = v->type->size();
- const d_uns64 tsz = type->size();
- assert(vsz != SIZE_INVALID && tsz != SIZE_INVALID);
- return offset < v->offset + vsz &&
- v->offset < offset + tsz;
-}
-
-bool VarDeclaration::hasPointers()
-{
- //printf("VarDeclaration::hasPointers() %s, ty = %d\n", toChars(), type->ty);
- return (!isDataseg() && type->hasPointers());
-}
-
-/******************************************
- * Return true if variable needs to call the destructor.
- */
-
-bool VarDeclaration::needsScopeDtor()
-{
- //printf("VarDeclaration::needsScopeDtor() %s\n", toChars());
- return edtor && !(storage_class & STCnodtor);
-}
-
-
-/******************************************
- * If a variable has a scope destructor call, return call for it.
- * Otherwise, return NULL.
- */
-
-Expression *VarDeclaration::callScopeDtor(Scope *)
-{
- //printf("VarDeclaration::callScopeDtor() %s\n", toChars());
-
- // Destruction of STCfield's is handled by buildDtor()
- if (storage_class & (STCnodtor | STCref | STCout | STCfield))
- {
- return NULL;
- }
-
- Expression *e = NULL;
-
- // Destructors for structs and arrays of structs
- Type *tv = type->baseElemOf();
- if (tv->ty == Tstruct)
- {
- StructDeclaration *sd = ((TypeStruct *)tv)->sym;
- if (!sd->dtor || sd->errors)
- return NULL;
-
- const d_uns64 sz = type->size();
- assert(sz != SIZE_INVALID);
- if (!sz)
- return NULL;
-
- if (type->toBasetype()->ty == Tstruct)
- {
- // v.__xdtor()
- e = new VarExp(loc, this);
-
- /* This is a hack so we can call destructors on const/immutable objects.
- * Need to add things like "const ~this()" and "immutable ~this()" to
- * fix properly.
- */
- e->type = e->type->mutableOf();
-
- // Enable calling destructors on shared objects.
- // The destructor is always a single, non-overloaded function,
- // and must serve both shared and non-shared objects.
- e->type = e->type->unSharedOf();
-
- e = new DotVarExp(loc, e, sd->dtor, false);
- e = new CallExp(loc, e);
- }
- else
- {
- // __ArrayDtor(v[0 .. n])
- e = new VarExp(loc, this);
-
- const d_uns64 sdsz = sd->type->size();
- assert(sdsz != SIZE_INVALID && sdsz != 0);
- const d_uns64 n = sz / sdsz;
- e = new SliceExp(loc, e, new IntegerExp(loc, 0, Type::tsize_t),
- new IntegerExp(loc, n, Type::tsize_t));
- // Prevent redundant bounds check
- ((SliceExp *)e)->upperIsInBounds = true;
- ((SliceExp *)e)->lowerIsLessThanUpper = true;
-
- // This is a hack so we can call destructors on const/immutable objects.
- e->type = sd->type->arrayOf();
-
- e = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayDtor), e);
- }
- return e;
- }
-
- // Destructors for classes
- if (storage_class & (STCauto | STCscope) && !(storage_class & STCparameter))
- {
- for (ClassDeclaration *cd = type->isClassHandle();
- cd;
- cd = cd->baseClass)
- {
- /* We can do better if there's a way with onstack
- * classes to determine if there's no way the monitor
- * could be set.
- */
- //if (cd->isInterfaceDeclaration())
- //error("interface %s cannot be scope", cd->toChars());
-
- // Destroying C++ scope classes crashes currently. Since C++ class dtors are not currently supported, simply do not run dtors for them.
- // See https://issues.dlang.org/show_bug.cgi?id=13182
- if (cd->isCPPclass())
- {
- break;
- }
- if (mynew || onstack) // if any destructors
- {
- // delete this;
- Expression *ec;
-
- ec = new VarExp(loc, this);
- e = new DeleteExp(loc, ec, true);
- e->type = Type::tvoid;
- break;
- }
- }
- }
- return e;
-}
-
-/**********************************
- * Determine if `this` has a lifetime that lasts past
- * the destruction of `v`
- * Params:
- * v = variable to test against
- * Returns:
- * true if it does
- */
-bool VarDeclaration::enclosesLifetimeOf(VarDeclaration *v) const
-{
- return sequenceNumber < v->sequenceNumber;
-}
-
-/******************************************
- */
-
-void ObjectNotFound(Identifier *id)
-{
- Type::error(Loc(), "%s not found. object.d may be incorrectly installed or corrupt.", id->toChars());
- fatal();
-}
-
-/******************************** SymbolDeclaration ********************************/
-
-SymbolDeclaration::SymbolDeclaration(Loc loc, StructDeclaration *dsym)
- : Declaration(dsym->ident)
-{
- this->loc = loc;
- this->dsym = dsym;
- storage_class |= STCconst;
-}
-
-/********************************* TypeInfoDeclaration ****************************/
-
-TypeInfoDeclaration::TypeInfoDeclaration(Type *tinfo)
- : VarDeclaration(Loc(), Type::dtypeinfo->type, tinfo->getTypeInfoIdent(), NULL)
-{
- this->tinfo = tinfo;
- storage_class = STCstatic | STCgshared;
- protection = Prot(Prot::public_);
- linkage = LINKc;
- alignment = target.ptrsize;
-}
-
-TypeInfoDeclaration *TypeInfoDeclaration::create(Type *tinfo)
-{
- return new TypeInfoDeclaration(tinfo);
-}
-
-Dsymbol *TypeInfoDeclaration::syntaxCopy(Dsymbol *)
-{
- assert(0); // should never be produced by syntax
- return NULL;
-}
-
-const char *TypeInfoDeclaration::toChars()
-{
- //printf("TypeInfoDeclaration::toChars() tinfo = %s\n", tinfo->toChars());
- OutBuffer buf;
- buf.writestring("typeid(");
- buf.writestring(tinfo->toChars());
- buf.writeByte(')');
- return buf.extractChars();
-}
-
-/***************************** TypeInfoConstDeclaration **********************/
-
-TypeInfoConstDeclaration::TypeInfoConstDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfoconst)
- {
- ObjectNotFound(Id::TypeInfo_Const);
- }
- type = Type::typeinfoconst->type;
-}
-
-TypeInfoConstDeclaration *TypeInfoConstDeclaration::create(Type *tinfo)
-{
- return new TypeInfoConstDeclaration(tinfo);
-}
-
-/***************************** TypeInfoInvariantDeclaration **********************/
-
-TypeInfoInvariantDeclaration::TypeInfoInvariantDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfoinvariant)
- {
- ObjectNotFound(Id::TypeInfo_Invariant);
- }
- type = Type::typeinfoinvariant->type;
-}
-
-TypeInfoInvariantDeclaration *TypeInfoInvariantDeclaration::create(Type *tinfo)
-{
- return new TypeInfoInvariantDeclaration(tinfo);
-}
-
-/***************************** TypeInfoSharedDeclaration **********************/
-
-TypeInfoSharedDeclaration::TypeInfoSharedDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfoshared)
- {
- ObjectNotFound(Id::TypeInfo_Shared);
- }
- type = Type::typeinfoshared->type;
-}
-
-TypeInfoSharedDeclaration *TypeInfoSharedDeclaration::create(Type *tinfo)
-{
- return new TypeInfoSharedDeclaration(tinfo);
-}
-
-/***************************** TypeInfoWildDeclaration **********************/
-
-TypeInfoWildDeclaration::TypeInfoWildDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfowild)
- {
- ObjectNotFound(Id::TypeInfo_Wild);
- }
- type = Type::typeinfowild->type;
-}
-
-TypeInfoWildDeclaration *TypeInfoWildDeclaration::create(Type *tinfo)
-{
- return new TypeInfoWildDeclaration(tinfo);
-}
-
-/***************************** TypeInfoStructDeclaration **********************/
-
-TypeInfoStructDeclaration::TypeInfoStructDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfostruct)
- {
- ObjectNotFound(Id::TypeInfo_Struct);
- }
- type = Type::typeinfostruct->type;
-}
-
-TypeInfoStructDeclaration *TypeInfoStructDeclaration::create(Type *tinfo)
-{
- return new TypeInfoStructDeclaration(tinfo);
-}
-
-/***************************** TypeInfoClassDeclaration ***********************/
-
-TypeInfoClassDeclaration::TypeInfoClassDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfoclass)
- {
- ObjectNotFound(Id::TypeInfo_Class);
- }
- type = Type::typeinfoclass->type;
-}
-
-TypeInfoClassDeclaration *TypeInfoClassDeclaration::create(Type *tinfo)
-{
- return new TypeInfoClassDeclaration(tinfo);
-}
-
-/***************************** TypeInfoInterfaceDeclaration *******************/
-
-TypeInfoInterfaceDeclaration::TypeInfoInterfaceDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfointerface)
- {
- ObjectNotFound(Id::TypeInfo_Interface);
- }
- type = Type::typeinfointerface->type;
-}
-
-TypeInfoInterfaceDeclaration *TypeInfoInterfaceDeclaration::create(Type *tinfo)
-{
- return new TypeInfoInterfaceDeclaration(tinfo);
-}
-
-/***************************** TypeInfoPointerDeclaration *********************/
-
-TypeInfoPointerDeclaration::TypeInfoPointerDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfopointer)
- {
- ObjectNotFound(Id::TypeInfo_Pointer);
- }
- type = Type::typeinfopointer->type;
-}
-
-TypeInfoPointerDeclaration *TypeInfoPointerDeclaration::create(Type *tinfo)
-{
- return new TypeInfoPointerDeclaration(tinfo);
-}
-
-/***************************** TypeInfoArrayDeclaration ***********************/
-
-TypeInfoArrayDeclaration::TypeInfoArrayDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfoarray)
- {
- ObjectNotFound(Id::TypeInfo_Array);
- }
- type = Type::typeinfoarray->type;
-}
-
-TypeInfoArrayDeclaration *TypeInfoArrayDeclaration::create(Type *tinfo)
-{
- return new TypeInfoArrayDeclaration(tinfo);
-}
-
-/***************************** TypeInfoStaticArrayDeclaration *****************/
-
-TypeInfoStaticArrayDeclaration::TypeInfoStaticArrayDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfostaticarray)
- {
- ObjectNotFound(Id::TypeInfo_StaticArray);
- }
- type = Type::typeinfostaticarray->type;
-}
-
-TypeInfoStaticArrayDeclaration *TypeInfoStaticArrayDeclaration::create(Type *tinfo)
-{
- return new TypeInfoStaticArrayDeclaration(tinfo);
-}
-
-/***************************** TypeInfoAssociativeArrayDeclaration ************/
-
-TypeInfoAssociativeArrayDeclaration::TypeInfoAssociativeArrayDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfoassociativearray)
- {
- ObjectNotFound(Id::TypeInfo_AssociativeArray);
- }
- type = Type::typeinfoassociativearray->type;
-}
-
-TypeInfoAssociativeArrayDeclaration *TypeInfoAssociativeArrayDeclaration::create(Type *tinfo)
-{
- return new TypeInfoAssociativeArrayDeclaration(tinfo);
-}
-
-/***************************** TypeInfoVectorDeclaration ***********************/
-
-TypeInfoVectorDeclaration::TypeInfoVectorDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfovector)
- {
- ObjectNotFound(Id::TypeInfo_Vector);
- }
- type = Type::typeinfovector->type;
-}
-
-TypeInfoVectorDeclaration *TypeInfoVectorDeclaration::create(Type *tinfo)
-{
- return new TypeInfoVectorDeclaration(tinfo);
-}
-
-/***************************** TypeInfoEnumDeclaration ***********************/
-
-TypeInfoEnumDeclaration::TypeInfoEnumDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfoenum)
- {
- ObjectNotFound(Id::TypeInfo_Enum);
- }
- type = Type::typeinfoenum->type;
-}
-
-TypeInfoEnumDeclaration *TypeInfoEnumDeclaration::create(Type *tinfo)
-{
- return new TypeInfoEnumDeclaration(tinfo);
-}
-
-/***************************** TypeInfoFunctionDeclaration ********************/
-
-TypeInfoFunctionDeclaration::TypeInfoFunctionDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfofunction)
- {
- ObjectNotFound(Id::TypeInfo_Function);
- }
- type = Type::typeinfofunction->type;
-}
-
-TypeInfoFunctionDeclaration *TypeInfoFunctionDeclaration::create(Type *tinfo)
-{
- return new TypeInfoFunctionDeclaration(tinfo);
-}
-
-/***************************** TypeInfoDelegateDeclaration ********************/
-
-TypeInfoDelegateDeclaration::TypeInfoDelegateDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfodelegate)
- {
- ObjectNotFound(Id::TypeInfo_Delegate);
- }
- type = Type::typeinfodelegate->type;
-}
-
-TypeInfoDelegateDeclaration *TypeInfoDelegateDeclaration::create(Type *tinfo)
-{
- return new TypeInfoDelegateDeclaration(tinfo);
-}
-
-/***************************** TypeInfoTupleDeclaration **********************/
-
-TypeInfoTupleDeclaration::TypeInfoTupleDeclaration(Type *tinfo)
- : TypeInfoDeclaration(tinfo)
-{
- if (!Type::typeinfotypelist)
- {
- ObjectNotFound(Id::TypeInfo_Tuple);
- }
- type = Type::typeinfotypelist->type;
-}
-
-TypeInfoTupleDeclaration *TypeInfoTupleDeclaration::create(Type *tinfo)
-{
- return new TypeInfoTupleDeclaration(tinfo);
-}
-
-/********************************* ThisDeclaration ****************************/
-
-// For the "this" parameter to member functions
-
-ThisDeclaration::ThisDeclaration(Loc loc, Type *t)
- : VarDeclaration(loc, t, Id::This, NULL)
-{
- storage_class |= STCnodtor;
-}
-
-Dsymbol *ThisDeclaration::syntaxCopy(Dsymbol *)
-{
- assert(0); // should never be produced by syntax
- return NULL;
-}
-
diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d
new file mode 100644
index 00000000000..0f40c1142c7
--- /dev/null
+++ b/gcc/d/dmd/declaration.d
@@ -0,0 +1,2323 @@
+/**
+ * Miscellaneous declarations, including typedef, alias, variable declarations including the
+ * implicit this declaration, type tuples, ClassInfo, ModuleInfo and various TypeInfos.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/declaration.d, _declaration.d)
+ * Documentation: https://dlang.org/phobos/dmd_declaration.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/declaration.d
+ */
+
+module dmd.declaration;
+
+import core.stdc.stdio;
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.ctorflow;
+import dmd.dclass;
+import dmd.delegatize;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.initsem;
+import dmd.intrange;
+import dmd.mtype;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.target;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.visitor;
+
+/************************************
+ * Check to see the aggregate type is nested and its context pointer is
+ * accessible from the current scope.
+ * Returns true if error occurs.
+ */
+bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0)
+{
+ Dsymbol sparent = ad.toParentLocal();
+ Dsymbol sparent2 = ad.toParent2();
+ Dsymbol s = sc.func;
+ if (ad.isNested() && s)
+ {
+ //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent);
+ //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars());
+ //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars());
+ if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2))
+ {
+ error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars());
+ return true;
+ }
+ }
+
+ bool result = false;
+ for (size_t i = iStart; i < ad.fields.dim; i++)
+ {
+ VarDeclaration vd = ad.fields[i];
+ Type tb = vd.type.baseElemOf();
+ if (tb.ty == Tstruct)
+ {
+ result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym);
+ }
+ }
+ return result;
+}
+
+/***********************************************
+ * Mark variable v as modified if it is inside a constructor that var
+ * is a field in.
+ */
+bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1)
+{
+ //printf("modifyFieldVar(var = %s)\n", var.toChars());
+ Dsymbol s = sc.func;
+ while (1)
+ {
+ FuncDeclaration fd = null;
+ if (s)
+ fd = s.isFuncDeclaration();
+ if (fd &&
+ ((fd.isCtorDeclaration() && var.isField()) ||
+ (fd.isStaticCtorDeclaration() && !var.isField())) &&
+ fd.toParentDecl() == var.toParent2() &&
+ (!e1 || e1.op == TOK.this_))
+ {
+ bool result = true;
+
+ var.ctorinit = true;
+ //printf("setting ctorinit\n");
+
+ if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof)
+ {
+ assert(e1);
+ auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 ||
+ var.type.needsNested());
+
+ const dim = sc.ctorflow.fieldinit.length;
+ auto ad = fd.isMemberDecl();
+ assert(ad);
+ size_t i;
+ for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ?
+ {
+ if (ad.fields[i] == var)
+ break;
+ }
+ assert(i < dim);
+ auto fieldInit = &sc.ctorflow.fieldinit[i];
+ const fi = fieldInit.csx;
+
+ if (fi & CSX.this_ctor)
+ {
+ if (var.type.isMutable() && e1.type.isMutable())
+ result = false;
+ else
+ {
+ const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod);
+ .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars());
+ .errorSupplemental(fieldInit.loc, "Previous initialization is here.");
+ }
+ }
+ else if (sc.inLoop || (fi & CSX.label))
+ {
+ if (!mustInit && var.type.isMutable() && e1.type.isMutable())
+ result = false;
+ else
+ {
+ const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod);
+ .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars());
+ }
+ }
+
+ fieldInit.csx |= CSX.this_ctor;
+ fieldInit.loc = e1.loc;
+ if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258
+ {
+ foreach (j, v; ad.fields)
+ {
+ if (v is var || !var.isOverlappedWith(v))
+ continue;
+ v.ctorinit = true;
+ sc.ctorflow.fieldinit[j].csx = CSX.this_ctor;
+ }
+ }
+ }
+ else if (fd != sc.func)
+ {
+ if (var.type.isMutable())
+ result = false;
+ else if (sc.func.fes)
+ {
+ const(char)* p = var.isField() ? "field" : var.kind();
+ .error(loc, "%s %s `%s` initialization is not allowed in foreach loop",
+ MODtoChars(var.type.mod), p, var.toChars());
+ }
+ else
+ {
+ const(char)* p = var.isField() ? "field" : var.kind();
+ .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`",
+ MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars());
+ }
+ }
+ else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() &&
+ var.type.isImmutable())
+ {
+ .error(loc, "%s %s `%s` initialization is not allowed in `static this`",
+ MODtoChars(var.type.mod), var.kind(), var.toChars());
+ errorSupplemental(loc, "Use `shared static this` instead.");
+ }
+ return result;
+ }
+ else
+ {
+ if (s)
+ {
+ s = s.toParentP(var.toParent2());
+ continue;
+ }
+ }
+ break;
+ }
+ return false;
+}
+
+/******************************************
+ */
+extern (C++) void ObjectNotFound(Identifier id)
+{
+ error(Loc.initial, "`%s` not found. object.d may be incorrectly installed or corrupt.", id.toChars());
+ fatal();
+}
+
+/* Accumulator for successive matches.
+ */
+struct MatchAccumulator
+{
+ int count; // number of matches found so far
+ MATCH last = MATCH.nomatch; // match level of lastf
+ FuncDeclaration lastf; // last matching function we found
+ FuncDeclaration nextf; // if ambiguous match, this is the "other" function
+}
+
+/***********************************************************
+ */
+extern (C++) abstract class Declaration : Dsymbol
+{
+ Type type;
+ Type originalType; // before semantic analysis
+ StorageClass storage_class = STC.undefined_;
+ Visibility visibility;
+ LINK linkage = LINK.default_;
+ short inuse; // used to detect cycles
+
+ ubyte adFlags; // control re-assignment of AliasDeclaration (put here for packing reasons)
+ enum wasRead = 1; // set if AliasDeclaration was read
+ enum ignoreRead = 2; // ignore any reads of AliasDeclaration
+
+ // overridden symbol with pragma(mangle, "...")
+ const(char)[] mangleOverride;
+
+ final extern (D) this(Identifier ident)
+ {
+ super(ident);
+ visibility = Visibility(Visibility.Kind.undefined);
+ }
+
+ final extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, ident);
+ visibility = Visibility(Visibility.Kind.undefined);
+ }
+
+ override const(char)* kind() const
+ {
+ return "declaration";
+ }
+
+ override final d_uns64 size(const ref Loc loc)
+ {
+ assert(type);
+ return type.size();
+ }
+
+ /**
+ * Issue an error if an attempt to call a disabled method is made
+ *
+ * If the declaration is disabled but inside a disabled function,
+ * returns `true` but do not issue an error message.
+ *
+ * Params:
+ * loc = Location information of the call
+ * sc = Scope in which the call occurs
+ * isAliasedDeclaration = if `true` searches overload set
+ *
+ * Returns:
+ * `true` if this `Declaration` is `@disable`d, `false` otherwise.
+ */
+ extern (D) final bool checkDisabled(Loc loc, Scope* sc, bool isAliasedDeclaration = false)
+ {
+ if (!(storage_class & STC.disable))
+ return false;
+
+ if (sc.func && sc.func.storage_class & STC.disable)
+ return true;
+
+ if (auto p = toParent())
+ {
+ if (auto postblit = isPostBlitDeclaration())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=21885
+ *
+ * If the generated postblit is disabled, it
+ * means that one of the fields has a disabled
+ * postblit. Print the first field that has
+ * a disabled postblit.
+ */
+ if (postblit.generated)
+ {
+ auto sd = p.isStructDeclaration();
+ assert(sd);
+ for (size_t i = 0; i < sd.fields.dim; i++)
+ {
+ auto structField = sd.fields[i];
+ if (structField.overlapped)
+ continue;
+ Type tv = structField.type.baseElemOf();
+ if (tv.ty != Tstruct)
+ continue;
+ auto sdv = (cast(TypeStruct)tv).sym;
+ if (!sdv.postblit)
+ continue;
+ if (sdv.postblit.isDisabled())
+ {
+ p.error(loc, "is not copyable because field `%s` is not copyable", structField.toChars());
+ return true;
+ }
+ }
+ }
+ p.error(loc, "is not copyable because it has a disabled postblit");
+ return true;
+ }
+ }
+
+ // if the function is @disabled, maybe there
+ // is an overload in the overload set that isn't
+ if (isAliasedDeclaration)
+ {
+ FuncDeclaration fd = isFuncDeclaration();
+ if (fd)
+ {
+ for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext)
+ if (!(ovl.storage_class & STC.disable))
+ return false;
+ }
+ }
+
+ if (auto ctor = isCtorDeclaration())
+ {
+ if (ctor.isCpCtor && ctor.generated)
+ {
+ .error(loc, "Generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", parent.toPrettyChars());
+ return true;
+ }
+ }
+ error(loc, "cannot be used because it is annotated with `@disable`");
+ return true;
+ }
+
+ /*************************************
+ * Check to see if declaration can be modified in this context (sc).
+ * Issue error if not.
+ * Params:
+ * loc = location for error messages
+ * e1 = `null` or `this` expression when this declaration is a field
+ * sc = context
+ * flag = if the first bit is set it means do not issue error message for
+ * invalid modification; if the second bit is set, it means that
+ this declaration is a field and a subfield of it is modified.
+ * Returns:
+ * Modifiable.yes or Modifiable.initialization
+ */
+ extern (D) final Modifiable checkModify(Loc loc, Scope* sc, Expression e1, ModifyFlags flag)
+ {
+ VarDeclaration v = isVarDeclaration();
+ if (v && v.canassign)
+ return Modifiable.initialization;
+
+ if (isParameter() || isResult())
+ {
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (scx.func == parent && (scx.flags & SCOPE.contract))
+ {
+ const(char)* s = isParameter() && parent.ident != Id.ensure ? "parameter" : "result";
+ if (!(flag & ModifyFlags.noError))
+ error(loc, "cannot modify %s `%s` in contract", s, toChars());
+ return Modifiable.initialization; // do not report type related errors
+ }
+ }
+ }
+
+ if (e1 && e1.op == TOK.this_ && isField())
+ {
+ VarDeclaration vthis = (cast(ThisExp)e1).var;
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (scx.func == vthis.parent && (scx.flags & SCOPE.contract))
+ {
+ if (!(flag & ModifyFlags.noError))
+ error(loc, "cannot modify parameter `this` in contract");
+ return Modifiable.initialization; // do not report type related errors
+ }
+ }
+ }
+
+ if (v && (isCtorinit() || isField()))
+ {
+ // It's only modifiable if inside the right constructor
+ if ((storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_))
+ return Modifiable.initialization;
+ if (flag & ModifyFlags.fieldAssign)
+ return Modifiable.yes;
+ return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes;
+ }
+ return Modifiable.yes;
+ }
+
+ override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ Dsymbol s = Dsymbol.search(loc, ident, flags);
+ if (!s && type)
+ {
+ s = type.toDsymbol(_scope);
+ if (s)
+ s = s.search(loc, ident, flags);
+ }
+ return s;
+ }
+
+ final bool isStatic() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.static_) != 0;
+ }
+
+ bool isDelete()
+ {
+ return false;
+ }
+
+ bool isDataseg()
+ {
+ return false;
+ }
+
+ bool isThreadlocal()
+ {
+ return false;
+ }
+
+ bool isCodeseg() const pure nothrow @nogc @safe
+ {
+ return false;
+ }
+
+ final bool isCtorinit() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.ctorinit) != 0;
+ }
+
+ final bool isFinal() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.final_) != 0;
+ }
+
+ bool isAbstract()
+ {
+ return (storage_class & STC.abstract_) != 0;
+ }
+
+ final bool isConst() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.const_) != 0;
+ }
+
+ final bool isImmutable() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.immutable_) != 0;
+ }
+
+ final bool isWild() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.wild) != 0;
+ }
+
+ final bool isAuto() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.auto_) != 0;
+ }
+
+ final bool isScope() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.scope_) != 0;
+ }
+
+ final bool isSynchronized() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.synchronized_) != 0;
+ }
+
+ final bool isParameter() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.parameter) != 0;
+ }
+
+ override final bool isDeprecated() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.deprecated_) != 0;
+ }
+
+ final bool isDisabled() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.disable) != 0;
+ }
+
+ final bool isOverride() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.override_) != 0;
+ }
+
+ final bool isResult() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.result) != 0;
+ }
+
+ final bool isField() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.field) != 0;
+ }
+
+ final bool isIn() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.in_) != 0;
+ }
+
+ final bool isOut() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.out_) != 0;
+ }
+
+ final bool isRef() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.ref_) != 0;
+ }
+
+ /// Returns: Whether the variable is a reference, annotated with `out` or `ref`
+ final bool isReference() const pure nothrow @nogc @safe
+ {
+ return (storage_class & (STC.ref_ | STC.out_)) != 0;
+ }
+
+ final bool isFuture() const pure nothrow @nogc @safe
+ {
+ return (storage_class & STC.future) != 0;
+ }
+
+ override final Visibility visible() pure nothrow @nogc @safe
+ {
+ return visibility;
+ }
+
+ override final inout(Declaration) isDeclaration() inout pure nothrow @nogc @safe
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TupleDeclaration : Declaration
+{
+ Objects* objects;
+ bool isexp; // true: expression tuple
+ TypeTuple tupletype; // !=null if this is a type tuple
+
+ extern (D) this(const ref Loc loc, Identifier ident, Objects* objects)
+ {
+ super(loc, ident);
+ this.objects = objects;
+ }
+
+ override TupleDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(0);
+ }
+
+ override const(char)* kind() const
+ {
+ return "tuple";
+ }
+
+ override Type getType()
+ {
+ /* If this tuple represents a type, return that type
+ */
+
+ //printf("TupleDeclaration::getType() %s\n", toChars());
+ if (isexp)
+ return null;
+ if (!tupletype)
+ {
+ /* It's only a type tuple if all the Object's are types
+ */
+ for (size_t i = 0; i < objects.dim; i++)
+ {
+ RootObject o = (*objects)[i];
+ if (o.dyncast() != DYNCAST.type)
+ {
+ //printf("\tnot[%d], %p, %d\n", i, o, o.dyncast());
+ return null;
+ }
+ }
+
+ /* We know it's a type tuple, so build the TypeTuple
+ */
+ Types* types = cast(Types*)objects;
+ auto args = new Parameters(objects.dim);
+ OutBuffer buf;
+ int hasdeco = 1;
+ for (size_t i = 0; i < types.dim; i++)
+ {
+ Type t = (*types)[i];
+ //printf("type = %s\n", t.toChars());
+ version (none)
+ {
+ buf.printf("_%s_%d", ident.toChars(), i);
+ const len = buf.offset;
+ const name = buf.extractSlice().ptr;
+ auto id = Identifier.idPool(name, len);
+ auto arg = new Parameter(STC.in_, t, id, null);
+ }
+ else
+ {
+ auto arg = new Parameter(0, t, null, null, null);
+ }
+ (*args)[i] = arg;
+ if (!t.deco)
+ hasdeco = 0;
+ }
+
+ tupletype = new TypeTuple(args);
+ if (hasdeco)
+ return tupletype.typeSemantic(Loc.initial, null);
+ }
+ return tupletype;
+ }
+
+ override Dsymbol toAlias2()
+ {
+ //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars());
+ for (size_t i = 0; i < objects.dim; i++)
+ {
+ RootObject o = (*objects)[i];
+ if (Dsymbol s = isDsymbol(o))
+ {
+ s = s.toAlias2();
+ (*objects)[i] = s;
+ }
+ }
+ return this;
+ }
+
+ override bool needThis()
+ {
+ //printf("TupleDeclaration::needThis(%s)\n", toChars());
+ for (size_t i = 0; i < objects.dim; i++)
+ {
+ RootObject o = (*objects)[i];
+ if (o.dyncast() == DYNCAST.expression)
+ {
+ Expression e = cast(Expression)o;
+ if (e.op == TOK.dSymbol)
+ {
+ DsymbolExp ve = cast(DsymbolExp)e;
+ Declaration d = ve.s.isDeclaration();
+ if (d && d.needThis())
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ override inout(TupleDeclaration) isTupleDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class AliasDeclaration : Declaration
+{
+ Dsymbol aliassym;
+ Dsymbol overnext; // next in overload list
+ Dsymbol _import; // !=null if unresolved internal alias for selective import
+
+ extern (D) this(const ref Loc loc, Identifier ident, Type type)
+ {
+ super(loc, ident);
+ //printf("AliasDeclaration(id = '%s', type = %p)\n", id.toChars(), type);
+ //printf("type = '%s'\n", type.toChars());
+ this.type = type;
+ assert(type);
+ }
+
+ extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s)
+ {
+ super(loc, ident);
+ //printf("AliasDeclaration(id = '%s', s = %p)\n", id.toChars(), s);
+ assert(s != this);
+ this.aliassym = s;
+ assert(s);
+ }
+
+ static AliasDeclaration create(Loc loc, Identifier id, Type type)
+ {
+ return new AliasDeclaration(loc, id, type);
+ }
+
+ override AliasDeclaration syntaxCopy(Dsymbol s)
+ {
+ //printf("AliasDeclaration::syntaxCopy()\n");
+ assert(!s);
+ AliasDeclaration sa = type ? new AliasDeclaration(loc, ident, type.syntaxCopy()) : new AliasDeclaration(loc, ident, aliassym.syntaxCopy(null));
+ sa.comment = comment;
+ sa.storage_class = storage_class;
+ return sa;
+ }
+
+ override bool overloadInsert(Dsymbol s)
+ {
+ //printf("[%s] AliasDeclaration::overloadInsert('%s') s = %s %s @ [%s]\n",
+ // loc.toChars(), toChars(), s.kind(), s.toChars(), s.loc.toChars());
+
+ /** Aliases aren't overloadable themselves, but if their Aliasee is
+ * overloadable they are converted to an overloadable Alias (either
+ * FuncAliasDeclaration or OverDeclaration).
+ *
+ * This is done by moving the Aliasee into such an overloadable alias
+ * which is then used to replace the existing Aliasee. The original
+ * Alias (_this_) remains a useless shell.
+ *
+ * This is a horrible mess. It was probably done to avoid replacing
+ * existing AST nodes and references, but it needs a major
+ * simplification b/c it's too complex to maintain.
+ *
+ * A simpler approach might be to merge any colliding symbols into a
+ * simple Overload class (an array) and then later have that resolve
+ * all collisions.
+ */
+ if (semanticRun >= PASS.semanticdone)
+ {
+ /* Semantic analysis is already finished, and the aliased entity
+ * is not overloadable.
+ */
+ if (type)
+ return false;
+
+ /* When s is added in member scope by static if, mixin("code") or others,
+ * aliassym is determined already. See the case in: test/compilable/test61.d
+ */
+ auto sa = aliassym.toAlias();
+
+ if (auto td = s.toAlias().isTemplateDeclaration())
+ s = td.funcroot ? td.funcroot : td;
+
+ if (auto fd = sa.isFuncDeclaration())
+ {
+ auto fa = new FuncAliasDeclaration(ident, fd);
+ fa.visibility = visibility;
+ fa.parent = parent;
+ aliassym = fa;
+ return aliassym.overloadInsert(s);
+ }
+ if (auto td = sa.isTemplateDeclaration())
+ {
+ auto od = new OverDeclaration(ident, td.funcroot ? td.funcroot : td);
+ od.visibility = visibility;
+ od.parent = parent;
+ aliassym = od;
+ return aliassym.overloadInsert(s);
+ }
+ if (auto od = sa.isOverDeclaration())
+ {
+ if (sa.ident != ident || sa.parent != parent)
+ {
+ od = new OverDeclaration(ident, od);
+ od.visibility = visibility;
+ od.parent = parent;
+ aliassym = od;
+ }
+ return od.overloadInsert(s);
+ }
+ if (auto os = sa.isOverloadSet())
+ {
+ if (sa.ident != ident || sa.parent != parent)
+ {
+ os = new OverloadSet(ident, os);
+ // TODO: visibility is lost here b/c OverloadSets have no visibility attribute
+ // Might no be a practical issue, b/c the code below fails to resolve the overload anyhow.
+ // ----
+ // module os1;
+ // import a, b;
+ // private alias merged = foo; // private alias to overload set of a.foo and b.foo
+ // ----
+ // module os2;
+ // import a, b;
+ // public alias merged = bar; // public alias to overload set of a.bar and b.bar
+ // ----
+ // module bug;
+ // import os1, os2;
+ // void test() { merged(123); } // should only look at os2.merged
+ //
+ // os.visibility = visibility;
+ os.parent = parent;
+ aliassym = os;
+ }
+ os.push(s);
+ return true;
+ }
+ return false;
+ }
+
+ /* Don't know yet what the aliased symbol is, so assume it can
+ * be overloaded and check later for correctness.
+ */
+ if (overnext)
+ return overnext.overloadInsert(s);
+ if (s is this)
+ return true;
+ overnext = s;
+ return true;
+ }
+
+ override const(char)* kind() const
+ {
+ return "alias";
+ }
+
+ override Type getType()
+ {
+ if (type)
+ return type;
+ return toAlias().getType();
+ }
+
+ override Dsymbol toAlias()
+ {
+ //printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s', inuse = %d)\n",
+ // loc.toChars(), toChars(), this, aliassym, aliassym ? aliassym.kind() : "", inuse);
+ assert(this != aliassym);
+ //static int count; if (++count == 10) *(char*)0=0;
+
+ // Reading the AliasDeclaration
+ if (!(adFlags & ignoreRead))
+ adFlags |= wasRead; // can never assign to this AliasDeclaration again
+
+ if (inuse == 1 && type && _scope)
+ {
+ inuse = 2;
+ uint olderrors = global.errors;
+ Dsymbol s = type.toDsymbol(_scope);
+ //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
+ if (global.errors != olderrors)
+ goto Lerr;
+ if (s)
+ {
+ s = s.toAlias();
+ if (global.errors != olderrors)
+ goto Lerr;
+ aliassym = s;
+ inuse = 0;
+ }
+ else
+ {
+ Type t = type.typeSemantic(loc, _scope);
+ if (t.ty == Terror)
+ goto Lerr;
+ if (global.errors != olderrors)
+ goto Lerr;
+ //printf("t = %s\n", t.toChars());
+ inuse = 0;
+ }
+ }
+ if (inuse)
+ {
+ error("recursive alias declaration");
+
+ Lerr:
+ // Avoid breaking "recursive alias" state during errors gagged
+ if (global.gag)
+ return this;
+ aliassym = new AliasDeclaration(loc, ident, Type.terror);
+ type = Type.terror;
+ return aliassym;
+ }
+
+ if (semanticRun >= PASS.semanticdone)
+ {
+ // semantic is already done.
+
+ // Do not see aliassym !is null, because of lambda aliases.
+
+ // Do not see type.deco !is null, even so "alias T = const int;` needs
+ // semantic analysis to take the storage class `const` as type qualifier.
+ }
+ else
+ {
+ if (_import && _import._scope)
+ {
+ /* If this is an internal alias for selective/renamed import,
+ * load the module first.
+ */
+ _import.dsymbolSemantic(null);
+ }
+ if (_scope)
+ {
+ aliasSemantic(this, _scope);
+ }
+ }
+
+ inuse = 1;
+ Dsymbol s = aliassym ? aliassym.toAlias() : this;
+ inuse = 0;
+ return s;
+ }
+
+ override Dsymbol toAlias2()
+ {
+ if (inuse)
+ {
+ error("recursive alias declaration");
+ return this;
+ }
+ inuse = 1;
+ Dsymbol s = aliassym ? aliassym.toAlias2() : this;
+ inuse = 0;
+ return s;
+ }
+
+ override bool isOverloadable() const
+ {
+ // assume overloadable until alias is resolved
+ return semanticRun < PASS.semanticdone ||
+ aliassym && aliassym.isOverloadable();
+ }
+
+ override inout(AliasDeclaration) isAliasDeclaration() inout
+ {
+ return this;
+ }
+
+ /** Returns: `true` if this instance was created to make a template parameter
+ visible in the scope of a template body, `false` otherwise */
+ extern (D) bool isAliasedTemplateParameter() const
+ {
+ return !!(storage_class & STC.templateparameter);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class OverDeclaration : Declaration
+{
+ Dsymbol overnext; // next in overload list
+ Dsymbol aliassym;
+
+ extern (D) this(Identifier ident, Dsymbol s)
+ {
+ super(ident);
+ this.aliassym = s;
+ }
+
+ override const(char)* kind() const
+ {
+ return "overload alias"; // todo
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+
+ auto s = isDsymbol(o);
+ if (!s)
+ return false;
+
+ if (auto od2 = s.isOverDeclaration())
+ return this.aliassym.equals(od2.aliassym);
+ return this.aliassym == s;
+ }
+
+ override bool overloadInsert(Dsymbol s)
+ {
+ //printf("OverDeclaration::overloadInsert('%s') aliassym = %p, overnext = %p\n", s.toChars(), aliassym, overnext);
+ if (overnext)
+ return overnext.overloadInsert(s);
+ if (s == this)
+ return true;
+ overnext = s;
+ return true;
+ }
+
+ override bool isOverloadable() const
+ {
+ return true;
+ }
+
+ Dsymbol isUnique()
+ {
+ Dsymbol result = null;
+ overloadApply(aliassym, (Dsymbol s)
+ {
+ if (result)
+ {
+ result = null;
+ return 1; // ambiguous, done
+ }
+ else
+ {
+ result = s;
+ return 0;
+ }
+ });
+ return result;
+ }
+
+ override inout(OverDeclaration) isOverDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class VarDeclaration : Declaration
+{
+ Initializer _init;
+ FuncDeclarations nestedrefs; // referenced by these lexically nested functions
+ Dsymbol aliassym; // if redone as alias to another symbol
+ VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection
+ Expression edtor; // if !=null, does the destruction of the variable
+ IntRange* range; // if !=null, the variable is known to be within the range
+ VarDeclarations* maybes; // STC.maybescope variables that are assigned to this STC.maybescope variable
+
+ uint endlinnum; // line number of end of scope that this var lives in
+ uint offset;
+ uint sequenceNumber; // order the variables are declared
+ __gshared uint nextSequenceNumber; // the counter for sequenceNumber
+ structalign_t alignment;
+
+ // When interpreting, these point to the value (NULL if value not determinable)
+ // The index of this variable on the CTFE stack, AdrOnStackNone if not allocated
+ enum AdrOnStackNone = ~0u;
+ uint ctfeAdrOnStack;
+
+ bool isargptr; // if parameter that _argptr points to
+ bool ctorinit; // it has been initialized in a ctor
+ bool iscatchvar; // this is the exception object variable in catch() clause
+ bool isowner; // this is an Owner, despite it being `scope`
+
+ // Both these mean the var is not rebindable once assigned,
+ // and the destructor gets run when it goes out of scope
+ bool onstack; // it is a class that was allocated on the stack
+ bool mynew; // it is a class new'd with custom operator new
+
+ byte canassign; // it can be assigned to
+ bool overlapped; // if it is a field and has overlapping
+ bool overlapUnsafe; // if it is an overlapping field and the overlaps are unsafe
+ bool doNotInferScope; // do not infer 'scope' for this variable
+ bool doNotInferReturn; // do not infer 'return' for this variable
+ ubyte isdataseg; // private data for isDataseg 0 unset, 1 true, 2 false
+
+ bool isArgDtorVar; // temporary created to handle scope destruction of a function argument
+
+ final extern (D) this(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_)
+ in
+ {
+ assert(ident);
+ }
+ do
+ {
+ //printf("VarDeclaration('%s')\n", ident.toChars());
+ super(loc, ident);
+ debug
+ {
+ if (!type && !_init)
+ {
+ //printf("VarDeclaration('%s')\n", ident.toChars());
+ //*(char*)0=0;
+ }
+ }
+
+ assert(type || _init);
+ this.type = type;
+ this._init = _init;
+ ctfeAdrOnStack = AdrOnStackNone;
+ this.storage_class = storage_class;
+ sequenceNumber = ++nextSequenceNumber;
+ }
+
+ static VarDeclaration create(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_)
+ {
+ return new VarDeclaration(loc, type, ident, _init, storage_class);
+ }
+
+ override VarDeclaration syntaxCopy(Dsymbol s)
+ {
+ //printf("VarDeclaration::syntaxCopy(%s)\n", toChars());
+ assert(!s);
+ auto v = new VarDeclaration(loc, type ? type.syntaxCopy() : null, ident, _init ? _init.syntaxCopy() : null, storage_class);
+ v.comment = comment;
+ return v;
+ }
+
+ override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
+ {
+ //printf("VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars());
+
+ if (aliassym)
+ {
+ // If this variable was really a tuple, set the offsets for the tuple fields
+ TupleDeclaration v2 = aliassym.isTupleDeclaration();
+ assert(v2);
+ for (size_t i = 0; i < v2.objects.dim; i++)
+ {
+ RootObject o = (*v2.objects)[i];
+ assert(o.dyncast() == DYNCAST.expression);
+ Expression e = cast(Expression)o;
+ assert(e.op == TOK.dSymbol);
+ DsymbolExp se = cast(DsymbolExp)e;
+ se.s.setFieldOffset(ad, fieldState, isunion);
+ }
+ return;
+ }
+
+ if (!isField())
+ return;
+ assert(!(storage_class & (STC.static_ | STC.extern_ | STC.parameter | STC.tls)));
+
+ //printf("+VarDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars());
+
+ /* Fields that are tuples appear both as part of TupleDeclarations and
+ * as members. That means ignore them if they are already a field.
+ */
+ if (offset)
+ {
+ // already a field
+ fieldState.offset = ad.structsize; // https://issues.dlang.org/show_bug.cgi?id=13613
+ return;
+ }
+ for (size_t i = 0; i < ad.fields.dim; i++)
+ {
+ if (ad.fields[i] == this)
+ {
+ // already a field
+ fieldState.offset = ad.structsize; // https://issues.dlang.org/show_bug.cgi?id=13613
+ return;
+ }
+ }
+
+ // Check for forward referenced types which will fail the size() call
+ Type t = type.toBasetype();
+ if (storage_class & STC.ref_)
+ {
+ // References are the size of a pointer
+ t = Type.tvoidptr;
+ }
+ Type tv = t.baseElemOf();
+ if (tv.ty == Tstruct)
+ {
+ auto ts = cast(TypeStruct)tv;
+ assert(ts.sym != ad); // already checked in ad.determineFields()
+ if (!ts.sym.determineSize(loc))
+ {
+ type = Type.terror;
+ errors = true;
+ return;
+ }
+ }
+
+ // List in ad.fields. Even if the type is error, it's necessary to avoid
+ // pointless error diagnostic "more initializers than fields" on struct literal.
+ ad.fields.push(this);
+
+ if (t.ty == Terror)
+ return;
+
+ /* If coming after a bit field in progress,
+ * advance past the field
+ */
+ if (fieldState.inFlight)
+ {
+ fieldState.inFlight = false;
+ if (0 && target.os & Target.OS.Posix)
+ fieldState.offset += (fieldState.bitOffset + 7) / 8;
+ else if (0 &&target.os == Target.OS.Windows)
+ fieldState.offset += fieldState.fieldSize;
+ }
+
+ const sz = t.size(loc);
+ assert(sz != SIZE_INVALID && sz < uint.max);
+ uint memsize = cast(uint)sz; // size of member
+ uint memalignsize = target.fieldalign(t); // size of member for alignment purposes
+ offset = AggregateDeclaration.placeField(
+ &fieldState.offset,
+ memsize, memalignsize, alignment,
+ &ad.structsize, &ad.alignsize,
+ isunion);
+
+ //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize);
+ //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize);
+ }
+
+ override const(char)* kind() const
+ {
+ return "variable";
+ }
+
+ override final inout(AggregateDeclaration) isThis() inout
+ {
+ if (!(storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.tls | STC.gshared | STC.ctfe)))
+ {
+ /* The casting is necessary because `s = s.parent` is otherwise rejected
+ */
+ for (auto s = cast(Dsymbol)this; s; s = s.parent)
+ {
+ auto ad = (cast(inout)s).isMember();
+ if (ad)
+ return ad;
+ if (!s.parent || !s.parent.isTemplateMixin())
+ break;
+ }
+ }
+ return null;
+ }
+
+ override final bool needThis()
+ {
+ //printf("VarDeclaration::needThis(%s, x%x)\n", toChars(), storage_class);
+ return isField();
+ }
+
+ override final bool isExport() const
+ {
+ return visibility.kind == Visibility.Kind.export_;
+ }
+
+ override final bool isImportedSymbol() const
+ {
+ if (visibility.kind == Visibility.Kind.export_ && !_init && (storage_class & STC.static_ || parent.isModule()))
+ return true;
+ return false;
+ }
+
+ /*******************************
+ * Does symbol go into data segment?
+ * Includes extern variables.
+ */
+ override final bool isDataseg()
+ {
+ version (none)
+ {
+ printf("VarDeclaration::isDataseg(%p, '%s')\n", this, toChars());
+ printf("%llx, isModule: %p, isTemplateInstance: %p, isNspace: %p\n",
+ storage_class & (STC.static_ | STC.const_), parent.isModule(), parent.isTemplateInstance(), parent.isNspace());
+ printf("parent = '%s'\n", parent.toChars());
+ }
+
+ if (isdataseg == 0) // the value is not cached
+ {
+ isdataseg = 2; // The Variables does not go into the datasegment
+
+ if (!canTakeAddressOf())
+ {
+ return false;
+ }
+
+ Dsymbol parent = toParent();
+ if (!parent && !(storage_class & STC.static_))
+ {
+ error("forward referenced");
+ type = Type.terror;
+ }
+ else if (storage_class & (STC.static_ | STC.extern_ | STC.tls | STC.gshared) ||
+ parent.isModule() || parent.isTemplateInstance() || parent.isNspace())
+ {
+ assert(!isParameter() && !isResult());
+ isdataseg = 1; // It is in the DataSegment
+ }
+ }
+
+ return (isdataseg == 1);
+ }
+ /************************************
+ * Does symbol go into thread local storage?
+ */
+ override final bool isThreadlocal()
+ {
+ //printf("VarDeclaration::isThreadlocal(%p, '%s')\n", this, toChars());
+ /* Data defaults to being thread-local. It is not thread-local
+ * if it is immutable, const or shared.
+ */
+ bool i = isDataseg() && !(storage_class & (STC.immutable_ | STC.const_ | STC.shared_ | STC.gshared));
+ //printf("\treturn %d\n", i);
+ return i;
+ }
+
+ /********************************************
+ * Can variable be read and written by CTFE?
+ */
+ final bool isCTFE()
+ {
+ return (storage_class & STC.ctfe) != 0; // || !isDataseg();
+ }
+
+ final bool isOverlappedWith(VarDeclaration v)
+ {
+ const vsz = v.type.size();
+ const tsz = type.size();
+ assert(vsz != SIZE_INVALID && tsz != SIZE_INVALID);
+
+ // Overlap is checked by comparing bit offsets
+ auto bitoffset = offset * 8;
+ auto vbitoffset = v.offset * 8;
+
+ // Bitsize of types are overridden by any bit-field widths.
+ ulong tbitsize = void;
+ if (auto bf = isBitFieldDeclaration())
+ {
+ bitoffset += bf.bitOffset;
+ tbitsize = bf.fieldWidth;
+ }
+ else
+ tbitsize = tsz * 8;
+
+ ulong vbitsize = void;
+ if (auto vbf = v.isBitFieldDeclaration())
+ {
+ vbitoffset += vbf.bitOffset;
+ vbitsize = vbf.fieldWidth;
+ }
+ else
+ vbitsize = vsz * 8;
+
+ return bitoffset < vbitoffset + vbitsize &&
+ vbitoffset < bitoffset + tbitsize;
+ }
+
+ override final bool hasPointers()
+ {
+ //printf("VarDeclaration::hasPointers() %s, ty = %d\n", toChars(), type.ty);
+ return (!isDataseg() && type.hasPointers());
+ }
+
+ /*************************************
+ * Return true if we can take the address of this variable.
+ */
+ final bool canTakeAddressOf()
+ {
+ return !(storage_class & STC.manifest);
+ }
+
+ /******************************************
+ * Return true if variable needs to call the destructor.
+ */
+ final bool needsScopeDtor()
+ {
+ //printf("VarDeclaration::needsScopeDtor() %s\n", toChars());
+ return edtor && !(storage_class & STC.nodtor);
+ }
+
+ /******************************************
+ * If a variable has a scope destructor call, return call for it.
+ * Otherwise, return NULL.
+ */
+ extern (D) final Expression callScopeDtor(Scope* sc)
+ {
+ //printf("VarDeclaration::callScopeDtor() %s\n", toChars());
+
+ // Destruction of STC.field's is handled by buildDtor()
+ if (storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field))
+ {
+ return null;
+ }
+
+ if (iscatchvar)
+ return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here
+
+ Expression e = null;
+ // Destructors for structs and arrays of structs
+ Type tv = type.baseElemOf();
+ if (tv.ty == Tstruct)
+ {
+ StructDeclaration sd = (cast(TypeStruct)tv).sym;
+ if (!sd.dtor || sd.errors)
+ return null;
+
+ const sz = type.size();
+ assert(sz != SIZE_INVALID);
+ if (!sz)
+ return null;
+
+ if (type.toBasetype().ty == Tstruct)
+ {
+ // v.__xdtor()
+ e = new VarExp(loc, this);
+
+ /* This is a hack so we can call destructors on const/immutable objects.
+ * Need to add things like "const ~this()" and "immutable ~this()" to
+ * fix properly.
+ */
+ e.type = e.type.mutableOf();
+
+ // Enable calling destructors on shared objects.
+ // The destructor is always a single, non-overloaded function,
+ // and must serve both shared and non-shared objects.
+ e.type = e.type.unSharedOf;
+
+ e = new DotVarExp(loc, e, sd.dtor, false);
+ e = new CallExp(loc, e);
+ }
+ else
+ {
+ // __ArrayDtor(v[0 .. n])
+ e = new VarExp(loc, this);
+
+ const sdsz = sd.type.size();
+ assert(sdsz != SIZE_INVALID && sdsz != 0);
+ const n = sz / sdsz;
+ e = new SliceExp(loc, e, new IntegerExp(loc, 0, Type.tsize_t), new IntegerExp(loc, n, Type.tsize_t));
+
+ // Prevent redundant bounds check
+ (cast(SliceExp)e).upperIsInBounds = true;
+ (cast(SliceExp)e).lowerIsLessThanUpper = true;
+
+ // This is a hack so we can call destructors on const/immutable objects.
+ e.type = sd.type.arrayOf();
+
+ e = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), e);
+ }
+ return e;
+ }
+ // Destructors for classes
+ if (storage_class & (STC.auto_ | STC.scope_) && !(storage_class & STC.parameter))
+ {
+ for (ClassDeclaration cd = type.isClassHandle(); cd; cd = cd.baseClass)
+ {
+ /* We can do better if there's a way with onstack
+ * classes to determine if there's no way the monitor
+ * could be set.
+ */
+ //if (cd.isInterfaceDeclaration())
+ // error("interface `%s` cannot be scope", cd.toChars());
+
+ if (mynew || onstack) // if any destructors
+ {
+ // delete'ing C++ classes crashes (and delete is deprecated anyway)
+ if (cd.classKind == ClassKind.cpp)
+ {
+ // Don't call non-existant dtor
+ if (!cd.dtor)
+ break;
+
+ e = new VarExp(loc, this);
+ e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances
+ e = new DotVarExp(loc, e, cd.dtor, false);
+ e = new CallExp(loc, e);
+ break;
+ }
+
+ // delete this;
+ Expression ec;
+ ec = new VarExp(loc, this);
+ e = new DeleteExp(loc, ec, true);
+ e.type = Type.tvoid;
+ break;
+ }
+ }
+ }
+ return e;
+ }
+
+ /*******************************************
+ * If variable has a constant expression initializer, get it.
+ * Otherwise, return null.
+ */
+ extern (D) final Expression getConstInitializer(bool needFullType = true)
+ {
+ assert(type && _init);
+
+ // Ungag errors when not speculative
+ uint oldgag = global.gag;
+ if (global.gag)
+ {
+ Dsymbol sym = toParent().isAggregateDeclaration();
+ if (sym && !sym.isSpeculative())
+ global.gag = 0;
+ }
+
+ if (_scope)
+ {
+ inuse++;
+ _init = _init.initializerSemantic(_scope, type, INITinterpret);
+ _scope = null;
+ inuse--;
+ }
+
+ Expression e = _init.initializerToExpression(needFullType ? type : null);
+ global.gag = oldgag;
+ return e;
+ }
+
+ /*******************************************
+ * Helper function for the expansion of manifest constant.
+ */
+ extern (D) final Expression expandInitializer(Loc loc)
+ {
+ assert((storage_class & STC.manifest) && _init);
+
+ auto e = getConstInitializer();
+ if (!e)
+ {
+ .error(loc, "cannot make expression out of initializer for `%s`", toChars());
+ return ErrorExp.get();
+ }
+
+ e = e.copy();
+ e.loc = loc; // for better error message
+ return e;
+ }
+
+ override final void checkCtorConstInit()
+ {
+ version (none)
+ {
+ /* doesn't work if more than one static ctor */
+ if (ctorinit == 0 && isCtorinit() && !isField())
+ error("missing initializer in static constructor for const variable");
+ }
+ }
+
+ /************************************
+ * Check to see if this variable is actually in an enclosing function
+ * rather than the current one.
+ * Update nestedrefs[], closureVars[] and outerVars[].
+ * Returns: true if error occurs.
+ */
+ extern (D) final bool checkNestedReference(Scope* sc, Loc loc)
+ {
+ //printf("VarDeclaration::checkNestedReference() %s\n", toChars());
+ if (sc.intypeof == 1 || (sc.flags & SCOPE.ctfe))
+ return false;
+ if (!parent || parent == sc.parent)
+ return false;
+ if (isDataseg() || (storage_class & STC.manifest))
+ return false;
+
+ // The current function
+ FuncDeclaration fdthis = sc.parent.isFuncDeclaration();
+ if (!fdthis)
+ return false; // out of function scope
+
+ Dsymbol p = toParent2();
+
+ // Function literals from fdthis to p must be delegates
+ ensureStaticLinkTo(fdthis, p);
+
+ // The function that this variable is in
+ FuncDeclaration fdv = p.isFuncDeclaration();
+ if (!fdv || fdv == fdthis)
+ return false;
+
+ // Add fdthis to nestedrefs[] if not already there
+ if (!nestedrefs.contains(fdthis))
+ nestedrefs.push(fdthis);
+
+ //printf("\tfdv = %s\n", fdv.toChars());
+ //printf("\tfdthis = %s\n", fdthis.toChars());
+ if (loc.isValid())
+ {
+ if (fdthis.getLevelAndCheck(loc, sc, fdv, this) == fdthis.LevelError)
+ return true;
+ }
+
+ // Add this VarDeclaration to fdv.closureVars[] if not already there
+ if (!sc.intypeof && !(sc.flags & SCOPE.compile) &&
+ // https://issues.dlang.org/show_bug.cgi?id=17605
+ (fdv.flags & FUNCFLAG.compileTimeOnly || !(fdthis.flags & FUNCFLAG.compileTimeOnly))
+ )
+ {
+ if (!fdv.closureVars.contains(this))
+ fdv.closureVars.push(this);
+ }
+
+ if (!fdthis.outerVars.contains(this))
+ fdthis.outerVars.push(this);
+
+ //printf("fdthis is %s\n", fdthis.toChars());
+ //printf("var %s in function %s is nested ref\n", toChars(), fdv.toChars());
+ // __dollar creates problems because it isn't a real variable
+ // https://issues.dlang.org/show_bug.cgi?id=3326
+ if (ident == Id.dollar)
+ {
+ .error(loc, "cannnot use `$` inside a function literal");
+ return true;
+ }
+ if (ident == Id.withSym) // https://issues.dlang.org/show_bug.cgi?id=1759
+ {
+ ExpInitializer ez = _init.isExpInitializer();
+ assert(ez);
+ Expression e = ez.exp;
+ if (e.op == TOK.construct || e.op == TOK.blit)
+ e = (cast(AssignExp)e).e2;
+ return lambdaCheckForNestedRef(e, sc);
+ }
+
+ return false;
+ }
+
+ override final Dsymbol toAlias()
+ {
+ //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
+ if ((!type || !type.deco) && _scope)
+ dsymbolSemantic(this, _scope);
+
+ assert(this != aliassym);
+ Dsymbol s = aliassym ? aliassym.toAlias() : this;
+ return s;
+ }
+
+ // Eliminate need for dynamic_cast
+ override final inout(VarDeclaration) isVarDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ /**********************************
+ * Determine if `this` has a lifetime that lasts past
+ * the destruction of `v`
+ * Params:
+ * v = variable to test against
+ * Returns:
+ * true if it does
+ */
+ final bool enclosesLifetimeOf(VarDeclaration v) const pure
+ {
+ // VarDeclaration's with these STC's need special treatment
+ enum special = STC.temp | STC.foreach_;
+
+ // Sequence numbers work when there are no special VarDeclaration's involved
+ if (!((this.storage_class | v.storage_class) & special))
+ {
+ // FIXME: VarDeclaration's for parameters are created in semantic3, so
+ // they will have a greater sequence number than local variables.
+ // Hence reverse the result for mixed comparisons.
+ const exp = this.isParameter() == v.isParameter();
+
+ return (this.sequenceNumber < v.sequenceNumber) == exp;
+ }
+
+ // Assume that semantic produces temporaries according to their lifetime
+ // (It won't create a temporary before the actual content)
+ if ((this.storage_class & special) && (v.storage_class & special))
+ return this.sequenceNumber < v.sequenceNumber;
+
+ // Fall back to lexical order
+ assert(this.loc != Loc.initial);
+ assert(v.loc != Loc.initial);
+
+ if (auto ld = this.loc.linnum - v.loc.linnum)
+ return ld < 0;
+
+ if (auto cd = this.loc.charnum - v.loc.charnum)
+ return cd < 0;
+
+ // Default fallback
+ return this.sequenceNumber < v.sequenceNumber;
+ }
+
+ /***************************************
+ * Add variable to maybes[].
+ * When a maybescope variable `v` is assigned to a maybescope variable `this`,
+ * we cannot determine if `this` is actually scope until the semantic
+ * analysis for the function is completed. Thus, we save the data
+ * until then.
+ * Params:
+ * v = an STC.maybescope variable that was assigned to `this`
+ */
+ final void addMaybe(VarDeclaration v)
+ {
+ //printf("add %s to %s's list of dependencies\n", v.toChars(), toChars());
+ if (!maybes)
+ maybes = new VarDeclarations();
+ maybes.push(v);
+ }
+}
+
+/*******************************************************
+ * C11 6.7.2.1-4 bit fields
+ */
+extern (C++) class BitFieldDeclaration : VarDeclaration
+{
+ Expression width;
+
+ uint fieldWidth;
+ uint bitOffset;
+
+ final extern (D) this(const ref Loc loc, Type type, Identifier ident, Expression width)
+ {
+ super(loc, type, ident, null);
+
+ this.width = width;
+ this.storage_class |= STC.field;
+ }
+
+ override BitFieldDeclaration syntaxCopy(Dsymbol s)
+ {
+ //printf("BitFieldDeclaration::syntaxCopy(%s)\n", toChars());
+ assert(!s);
+ auto bf = new BitFieldDeclaration(loc, type ? type.syntaxCopy() : null, ident, width.syntaxCopy());
+ bf.comment = comment;
+ return bf;
+ }
+
+ override final inout(BitFieldDeclaration) isBitFieldDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ override final void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
+ {
+ //printf("BitFieldDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars());
+
+ Type t = type.toBasetype();
+
+ // List in ad.fields. Even if the type is error, it's necessary to avoid
+ // pointless error diagnostic "more initializers than fields" on struct literal.
+ if (!isAnonymous())
+ ad.fields.push(this);
+
+ if (t.ty == Terror)
+ return;
+
+ const sz = t.size(loc);
+ assert(sz != SIZE_INVALID && sz < uint.max);
+ uint memsize = cast(uint)sz; // size of member
+ uint memalignsize = target.fieldalign(t); // size of member for alignment purposes
+
+ if (fieldWidth == 0 && !isAnonymous())
+ error(loc, "named bit fields cannot have 0 width");
+ if (fieldWidth > memsize * 8)
+ error(loc, "bit field width %d is larger than type", fieldWidth);
+
+ void startNewField()
+ {
+ offset = AggregateDeclaration.placeField(
+ &fieldState.offset,
+ memsize, memalignsize, alignment,
+ &ad.structsize, &ad.alignsize,
+ isunion);
+
+ fieldState.inFlight = true;
+ fieldState.fieldOffset = offset;
+ fieldState.bitOffset = 0;
+ fieldState.fieldSize = memsize;
+ }
+
+ if (!fieldState.inFlight || fieldWidth == 0)
+ {
+ startNewField();
+ }
+
+ if (0 && target.os & Target.OS.Posix)
+ {
+ if ((fieldState.offset%4 * 8) + fieldState.bitOffset + fieldWidth > int.sizeof * 8)
+ {
+ startNewField();
+ }
+ }
+ else if (1 || target.os == Target.OS.Windows)
+ {
+ if (memsize != fieldState.fieldSize ||
+ fieldState.bitOffset + fieldWidth > fieldState.fieldSize * 8)
+ {
+ startNewField();
+ }
+ }
+
+ offset = fieldState.fieldOffset;
+ bitOffset = fieldState.bitOffset;
+ if (0 && target.os & Target.OS.Posix)
+ {
+ while (bitOffset > memsize * 8)
+ {
+ bitOffset -= 8;
+ offset += 1;
+ }
+ }
+
+ //fieldState.fieldSize = memsize;
+ if (!isunion)
+ {
+ fieldState.bitOffset += fieldWidth;
+ }
+
+ //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize);
+ //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize);
+ }
+}
+
+/***********************************************************
+ * This is a shell around a back end symbol
+ */
+extern (C++) final class SymbolDeclaration : Declaration
+{
+ StructDeclaration dsym;
+
+ extern (D) this(const ref Loc loc, StructDeclaration dsym)
+ {
+ super(loc, dsym.ident);
+ this.dsym = dsym;
+ storage_class |= STC.const_;
+ }
+
+ // Eliminate need for dynamic_cast
+ override inout(SymbolDeclaration) isSymbolDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class TypeInfoDeclaration : VarDeclaration
+{
+ Type tinfo;
+
+ final extern (D) this(Type tinfo)
+ {
+ super(Loc.initial, Type.dtypeinfo.type, tinfo.getTypeInfoIdent(), null);
+ this.tinfo = tinfo;
+ storage_class = STC.static_ | STC.gshared;
+ visibility = Visibility(Visibility.Kind.public_);
+ linkage = LINK.c;
+ alignment = target.ptrsize;
+ }
+
+ static TypeInfoDeclaration create(Type tinfo)
+ {
+ return new TypeInfoDeclaration(tinfo);
+ }
+
+ override final TypeInfoDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(0); // should never be produced by syntax
+ }
+
+ override final const(char)* toChars() const
+ {
+ //printf("TypeInfoDeclaration::toChars() tinfo = %s\n", tinfo.toChars());
+ OutBuffer buf;
+ buf.writestring("typeid(");
+ buf.writestring(tinfo.toChars());
+ buf.writeByte(')');
+ return buf.extractChars();
+ }
+
+ override final inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout @nogc nothrow pure @safe
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoStructDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfostruct)
+ {
+ ObjectNotFound(Id.TypeInfo_Struct);
+ }
+ type = Type.typeinfostruct.type;
+ }
+
+ static TypeInfoStructDeclaration create(Type tinfo)
+ {
+ return new TypeInfoStructDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoClassDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfoclass)
+ {
+ ObjectNotFound(Id.TypeInfo_Class);
+ }
+ type = Type.typeinfoclass.type;
+ }
+
+ static TypeInfoClassDeclaration create(Type tinfo)
+ {
+ return new TypeInfoClassDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoInterfaceDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfointerface)
+ {
+ ObjectNotFound(Id.TypeInfo_Interface);
+ }
+ type = Type.typeinfointerface.type;
+ }
+
+ static TypeInfoInterfaceDeclaration create(Type tinfo)
+ {
+ return new TypeInfoInterfaceDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoPointerDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfopointer)
+ {
+ ObjectNotFound(Id.TypeInfo_Pointer);
+ }
+ type = Type.typeinfopointer.type;
+ }
+
+ static TypeInfoPointerDeclaration create(Type tinfo)
+ {
+ return new TypeInfoPointerDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoArrayDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfoarray)
+ {
+ ObjectNotFound(Id.TypeInfo_Array);
+ }
+ type = Type.typeinfoarray.type;
+ }
+
+ static TypeInfoArrayDeclaration create(Type tinfo)
+ {
+ return new TypeInfoArrayDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfostaticarray)
+ {
+ ObjectNotFound(Id.TypeInfo_StaticArray);
+ }
+ type = Type.typeinfostaticarray.type;
+ }
+
+ static TypeInfoStaticArrayDeclaration create(Type tinfo)
+ {
+ return new TypeInfoStaticArrayDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfoassociativearray)
+ {
+ ObjectNotFound(Id.TypeInfo_AssociativeArray);
+ }
+ type = Type.typeinfoassociativearray.type;
+ }
+
+ static TypeInfoAssociativeArrayDeclaration create(Type tinfo)
+ {
+ return new TypeInfoAssociativeArrayDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoEnumDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfoenum)
+ {
+ ObjectNotFound(Id.TypeInfo_Enum);
+ }
+ type = Type.typeinfoenum.type;
+ }
+
+ static TypeInfoEnumDeclaration create(Type tinfo)
+ {
+ return new TypeInfoEnumDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoFunctionDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfofunction)
+ {
+ ObjectNotFound(Id.TypeInfo_Function);
+ }
+ type = Type.typeinfofunction.type;
+ }
+
+ static TypeInfoFunctionDeclaration create(Type tinfo)
+ {
+ return new TypeInfoFunctionDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoDelegateDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfodelegate)
+ {
+ ObjectNotFound(Id.TypeInfo_Delegate);
+ }
+ type = Type.typeinfodelegate.type;
+ }
+
+ static TypeInfoDelegateDeclaration create(Type tinfo)
+ {
+ return new TypeInfoDelegateDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoTupleDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfotypelist)
+ {
+ ObjectNotFound(Id.TypeInfo_Tuple);
+ }
+ type = Type.typeinfotypelist.type;
+ }
+
+ static TypeInfoTupleDeclaration create(Type tinfo)
+ {
+ return new TypeInfoTupleDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoConstDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfoconst)
+ {
+ ObjectNotFound(Id.TypeInfo_Const);
+ }
+ type = Type.typeinfoconst.type;
+ }
+
+ static TypeInfoConstDeclaration create(Type tinfo)
+ {
+ return new TypeInfoConstDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoInvariantDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfoinvariant)
+ {
+ ObjectNotFound(Id.TypeInfo_Invariant);
+ }
+ type = Type.typeinfoinvariant.type;
+ }
+
+ static TypeInfoInvariantDeclaration create(Type tinfo)
+ {
+ return new TypeInfoInvariantDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoSharedDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfoshared)
+ {
+ ObjectNotFound(Id.TypeInfo_Shared);
+ }
+ type = Type.typeinfoshared.type;
+ }
+
+ static TypeInfoSharedDeclaration create(Type tinfo)
+ {
+ return new TypeInfoSharedDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoWildDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfowild)
+ {
+ ObjectNotFound(Id.TypeInfo_Wild);
+ }
+ type = Type.typeinfowild.type;
+ }
+
+ static TypeInfoWildDeclaration create(Type tinfo)
+ {
+ return new TypeInfoWildDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeInfoVectorDeclaration : TypeInfoDeclaration
+{
+ extern (D) this(Type tinfo)
+ {
+ super(tinfo);
+ if (!Type.typeinfovector)
+ {
+ ObjectNotFound(Id.TypeInfo_Vector);
+ }
+ type = Type.typeinfovector.type;
+ }
+
+ static TypeInfoVectorDeclaration create(Type tinfo)
+ {
+ return new TypeInfoVectorDeclaration(tinfo);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * For the "this" parameter to member functions
+ */
+extern (C++) final class ThisDeclaration : VarDeclaration
+{
+ extern (D) this(const ref Loc loc, Type t)
+ {
+ super(loc, t, Id.This, null);
+ storage_class |= STC.nodtor;
+ }
+
+ override ThisDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(0); // should never be produced by syntax
+ }
+
+ override inout(ThisDeclaration) isThisDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h
index 55c814288e0..1c56defd3ee 100644
--- a/gcc/d/dmd/declaration.h
+++ b/gcc/d/dmd/declaration.h
@@ -13,115 +13,98 @@
#include "dsymbol.h"
#include "mtype.h"
#include "objc.h"
+#include "tokens.h"
class Expression;
class Statement;
class LabelDsymbol;
class Initializer;
-class Module;
class ForeachStatement;
struct Ensure
{
Identifier *id;
Statement *ensure;
-
- Ensure();
- Ensure(Identifier *id, Statement *ensure);
- Ensure syntaxCopy();
- static Ensures *arraySyntaxCopy(Ensures *a);
};
-class AliasDeclaration;
class FuncDeclaration;
-class ExpInitializer;
class StructDeclaration;
-struct InterState;
-struct CompiledCtfeFunction;
-struct ObjcSelector;
struct IntRange;
-enum LINK;
-enum TOK;
-enum MATCH;
-enum PURE;
-enum PINLINE;
-
-#define STCundefined 0LL
-#define STCstatic 1LL
-#define STCextern 2LL
-#define STCconst 4LL
-#define STCfinal 8LL
-#define STCabstract 0x10LL
-#define STCparameter 0x20LL
-#define STCfield 0x40LL
-#define STCoverride 0x80LL
-#define STCauto 0x100LL
-#define STCsynchronized 0x200LL
-#define STCdeprecated 0x400LL
-#define STCin 0x800LL // in parameter
-#define STCout 0x1000LL // out parameter
-#define STClazy 0x2000LL // lazy parameter
-#define STCforeach 0x4000LL // variable for foreach loop
-#define STCvariadic 0x10000LL // the 'variadic' parameter in: T foo(T a, U b, V variadic...)
-#define STCctorinit 0x20000LL // can only be set inside constructor
-#define STCtemplateparameter 0x40000LL // template parameter
-#define STCscope 0x80000LL
-#define STCimmutable 0x100000LL
-#define STCref 0x200000LL
-#define STCinit 0x400000LL // has explicit initializer
-#define STCmanifest 0x800000LL // manifest constant
-#define STCnodtor 0x1000000LL // don't run destructor
-#define STCnothrow 0x2000000LL // never throws exceptions
-#define STCpure 0x4000000LL // pure function
-#define STCtls 0x8000000LL // thread local
-#define STCalias 0x10000000LL // alias parameter
-#define STCshared 0x20000000LL // accessible from multiple threads
-// accessible from multiple threads
-// but not typed as "shared"
-#define STCgshared 0x40000000LL
-#define STCwild 0x80000000LL // for "wild" type constructor
+//enum STC : ulong from astenums.d:
+
+ #define STCundefined 0ULL
+
+ #define STCstatic 1ULL /// `static`
+ #define STCextern 2ULL /// `extern`
+ #define STCconst 4ULL /// `const`
+ #define STCfinal 8ULL /// `final`
+
+ #define STCabstract 0x10ULL /// `abstract`
+ #define STCparameter 0x20ULL /// is function parameter
+ #define STCfield 0x40ULL /// is field of struct, union or class
+ #define STCoverride 0x80ULL /// `override`
+
+ #define STCauto 0x100ULL /// `auto`
+ #define STCsynchronized 0x200ULL /// `synchronized`
+ #define STCdeprecated 0x400ULL /// `deprecated`
+ #define STCin 0x800ULL /// `in` parameter
+
+ #define STCout 0x1000ULL /// `out` parameter
+ #define STClazy 0x2000ULL /// `lazy` parameter
+ #define STCforeach 0x4000ULL /// variable for foreach loop
+ #define STCvariadic 0x8000ULL /// the `variadic` parameter in: T foo(T a, U b, V variadic...)
+
+ #define STCctorinit 0x10000ULL /// can only be set inside constructor
+ #define STCtemplateparameter 0x20000ULL /// template parameter
+ #define STCref 0x40000ULL /// `ref`
+ #define STCscope 0x80000ULL /// `scope`
+
+ #define STCmaybescope 0x100000ULL /// parameter might be `scope`
+ #define STCscopeinferred 0x200000ULL /// `scope` has been inferred and should not be part of mangling, `scope` must also be set
+ #define STCreturn 0x400000ULL /// 'return ref' or 'return scope' for function parameters
+ #define STCreturnScope 0x800000ULL /// if `ref return scope` then resolve to `ref` and `return scope`
+
+ #define STCreturninferred 0x1000000ULL /// `return` has been inferred and should not be part of mangling, `return` must also be set
+ #define STCimmutable 0x2000000ULL /// `immutable`
+ #define STCinit 0x4000000ULL /// has explicit initializer
+ #define STCmanifest 0x8000000ULL /// manifest constant
+
+ #define STCnodtor 0x10000000ULL /// do not run destructor
+ #define STCnothrow 0x20000000ULL /// `nothrow` meaning never throws exceptions
+ #define STCpure 0x40000000ULL /// `pure` function
+ #define STCtls 0x80000000ULL /// thread local
+
+ #define STCalias 0x100000000ULL /// `alias` parameter
+ #define STCshared 0x200000000ULL /// accessible from multiple threads
+ #define STCgshared 0x400000000ULL /// accessible from multiple threads, but not typed as `shared`
+ #define STCwild 0x800000000ULL /// for wild type constructor
+
+ #define STCproperty 0x1000000000ULL /// `@property`
+ #define STCsafe 0x2000000000ULL /// `@safe`
+ #define STCtrusted 0x4000000000ULL /// `@trusted`
+ #define STCsystem 0x8000000000ULL /// `@system`
+
+ #define STCctfe 0x10000000000ULL /// can be used in CTFE, even if it is static
+ #define STCdisable 0x20000000000ULL /// for functions that are not callable
+ #define STCresult 0x40000000000ULL /// for result variables passed to out contracts
+ #define STCnodefaultctor 0x80000000000ULL /// must be set inside constructor
+
+ #define STCtemp 0x100000000000ULL /// temporary variable
+ #define STCrvalue 0x200000000000ULL /// force rvalue for variables
+ #define STCnogc 0x400000000000ULL /// `@nogc`
+ #define STCautoref 0x800000000000ULL /// Mark for the already deduced `auto ref` parameter
+
+ #define STCinference 0x1000000000000ULL /// do attribute inference
+ #define STCexptemp 0x2000000000000ULL /// temporary variable that has lifetime restricted to an expression
+ #define STCfuture 0x4000000000000ULL /// introducing new base class function
+ #define STClocal 0x8000000000000ULL /// do not forward (see dmd.dsymbol.ForwardingScopeDsymbol).
+
+ #define STClive 0x10000000000000ULL /// function `@live` attribute
+ #define STCregister 0x20000000000000ULL /// `register` storage class (ImportC)
+ #define STCvolatile 0x40000000000000ULL /// destined for volatile in the back end
+
#define STC_TYPECTOR (STCconst | STCimmutable | STCshared | STCwild)
#define STC_FUNCATTR (STCref | STCnothrow | STCnogc | STCpure | STCproperty | STCsafe | STCtrusted | STCsystem)
-#define STCproperty 0x100000000LL
-#define STCsafe 0x200000000LL
-#define STCtrusted 0x400000000LL
-#define STCsystem 0x800000000LL
-#define STCctfe 0x1000000000LL // can be used in CTFE, even if it is static
-#define STCdisable 0x2000000000LL // for functions that are not callable
-#define STCresult 0x4000000000LL // for result variables passed to out contracts
-#define STCnodefaultctor 0x8000000000LL // must be set inside constructor
-#define STCtemp 0x10000000000LL // temporary variable
-#define STCrvalue 0x20000000000LL // force rvalue for variables
-#define STCnogc 0x40000000000LL // @nogc
-#define STCvolatile 0x80000000000LL // destined for volatile in the back end
-#define STCreturn 0x100000000000LL // 'return ref' or 'return scope' for function parameters
-#define STCautoref 0x200000000000LL // Mark for the already deduced 'auto ref' parameter
-#define STCinference 0x400000000000LL // do attribute inference
-#define STCexptemp 0x800000000000LL // temporary variable that has lifetime restricted to an expression
-#define STCmaybescope 0x1000000000000LL // parameter might be 'scope'
-#define STCscopeinferred 0x2000000000000LL // 'scope' has been inferred and should not be part of mangling
-#define STCfuture 0x4000000000000LL // introducing new base class function
-#define STClocal 0x8000000000000LL // do not forward (see ddmd.dsymbol.ForwardingScopeDsymbol).
-
-const StorageClass STCStorageClass = (STCauto | STCscope | STCstatic | STCextern | STCconst | STCfinal |
- STCabstract | STCsynchronized | STCdeprecated | STCfuture | STCoverride | STClazy | STCalias |
- STCout | STCin |
- STCmanifest | STCimmutable | STCshared | STCwild | STCnothrow | STCnogc | STCpure | STCref | STCtls |
- STCgshared | STCproperty | STCsafe | STCtrusted | STCsystem | STCdisable | STClocal);
-
-struct Match
-{
- int count; // number of matches found
- MATCH last; // match level of lastf
- FuncDeclaration *lastf; // last matching function we found
- FuncDeclaration *nextf; // current matching function
- FuncDeclaration *anyf; // pick a func, any func, to use for error recovery
-};
-
-void functionResolve(Match *m, Dsymbol *fd, Loc loc, Scope *sc, Objects *tiargs, Type *tthis, Expressions *fargs, const char **pMessage = NULL);
-int overloadApply(Dsymbol *fstart, void *param, int (*fp)(void *, Dsymbol *));
-void aliasSemantic(AliasDeclaration *ds, Scope *sc);
-
void ObjectNotFound(Identifier *id);
/**************************************************************/
@@ -132,47 +115,45 @@ public:
Type *type;
Type *originalType; // before semantic analysis
StorageClass storage_class;
- Prot protection;
+ Visibility visibility;
LINK linkage;
- int inuse; // used to detect cycles
+ short inuse; // used to detect cycles
+ uint8_t adFlags;
DString mangleOverride; // overridden symbol with pragma(mangle, "...")
- Declaration(Identifier *id);
const char *kind() const;
- d_uns64 size(Loc loc);
- bool checkDisabled(Loc loc, Scope *sc, bool isAliasedDeclaration = false);
- int checkModify(Loc loc, Scope *sc, Type *t, Expression *e1, int flag);
+ d_uns64 size(const Loc &loc);
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
- bool isStatic() { return (storage_class & STCstatic) != 0; }
+ bool isStatic() const { return (storage_class & STCstatic) != 0; }
virtual bool isDelete();
virtual bool isDataseg();
virtual bool isThreadlocal();
virtual bool isCodeseg() const;
- bool isCtorinit() { return (storage_class & STCctorinit) != 0; }
- bool isFinal() { return (storage_class & STCfinal) != 0; }
- bool isAbstract() { return (storage_class & STCabstract) != 0; }
- bool isConst() { return (storage_class & STCconst) != 0; }
- bool isImmutable() { return (storage_class & STCimmutable) != 0; }
- bool isWild() { return (storage_class & STCwild) != 0; }
- bool isAuto() { return (storage_class & STCauto) != 0; }
- bool isScope() { return (storage_class & STCscope) != 0; }
- bool isSynchronized() { return (storage_class & STCsynchronized) != 0; }
- bool isParameter() { return (storage_class & STCparameter) != 0; }
- bool isDeprecated() { return (storage_class & STCdeprecated) != 0; }
- bool isDisabled() { return (storage_class & STCdisable) != 0; }
- bool isOverride() { return (storage_class & STCoverride) != 0; }
- bool isResult() { return (storage_class & STCresult) != 0; }
- bool isField() { return (storage_class & STCfield) != 0; }
-
- bool isIn() { return (storage_class & STCin) != 0; }
- bool isOut() { return (storage_class & STCout) != 0; }
- bool isRef() { return (storage_class & STCref) != 0; }
-
- bool isFuture() { return (storage_class & STCfuture) != 0; }
-
- Prot prot();
+ bool isCtorinit() const { return (storage_class & STCctorinit) != 0; }
+ bool isFinal() const { return (storage_class & STCfinal) != 0; }
+ virtual bool isAbstract() { return (storage_class & STCabstract) != 0; }
+ bool isConst() const { return (storage_class & STCconst) != 0; }
+ bool isImmutable() const { return (storage_class & STCimmutable) != 0; }
+ bool isWild() const { return (storage_class & STCwild) != 0; }
+ bool isAuto() const { return (storage_class & STCauto) != 0; }
+ bool isScope() const { return (storage_class & STCscope) != 0; }
+ bool isSynchronized() const { return (storage_class & STCsynchronized) != 0; }
+ bool isParameter() const { return (storage_class & STCparameter) != 0; }
+ bool isDeprecated() const { return (storage_class & STCdeprecated) != 0; }
+ bool isOverride() const { return (storage_class & STCoverride) != 0; }
+ bool isResult() const { return (storage_class & STCresult) != 0; }
+ bool isField() const { return (storage_class & STCfield) != 0; }
+
+ bool isIn() const { return (storage_class & STCin) != 0; }
+ bool isOut() const { return (storage_class & STCout) != 0; }
+ bool isRef() const { return (storage_class & STCref) != 0; }
+ bool isReference() const { return (storage_class & (STCref | STCout)) != 0; }
+
+ bool isFuture() const { return (storage_class & STCfuture) != 0; }
+
+ Visibility visible();
Declaration *isDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -188,8 +169,7 @@ public:
TypeTuple *tupletype; // !=NULL if this is a type tuple
- TupleDeclaration(Loc loc, Identifier *ident, Objects *objects);
- Dsymbol *syntaxCopy(Dsymbol *);
+ TupleDeclaration *syntaxCopy(Dsymbol *);
const char *kind() const;
Type *getType();
Dsymbol *toAlias2();
@@ -208,16 +188,14 @@ public:
Dsymbol *overnext; // next in overload list
Dsymbol *_import; // !=NULL if unresolved internal alias for selective import
- AliasDeclaration(Loc loc, Identifier *ident, Type *type);
- AliasDeclaration(Loc loc, Identifier *ident, Dsymbol *s);
static AliasDeclaration *create(Loc loc, Identifier *id, Type *type);
- Dsymbol *syntaxCopy(Dsymbol *);
+ AliasDeclaration *syntaxCopy(Dsymbol *);
bool overloadInsert(Dsymbol *s);
const char *kind() const;
Type *getType();
Dsymbol *toAlias();
Dsymbol *toAlias2();
- bool isOverloadable();
+ bool isOverloadable() const;
AliasDeclaration *isAliasDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -230,16 +208,14 @@ class OverDeclaration : public Declaration
public:
Dsymbol *overnext; // next in overload list
Dsymbol *aliassym;
- bool hasOverloads;
- OverDeclaration(Identifier *ident, Dsymbol *s, bool hasOverloads = true);
const char *kind() const;
- bool equals(RootObject *o);
+ bool equals(const RootObject *o) const;
bool overloadInsert(Dsymbol *s);
Dsymbol *toAlias();
Dsymbol *isUnique();
- bool isOverloadable();
+ bool isOverloadable() const;
OverDeclaration *isOverDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -251,33 +227,40 @@ class VarDeclaration : public Declaration
{
public:
Initializer *_init;
+ FuncDeclarations nestedrefs; // referenced by these lexically nested functions
+ Dsymbol *aliassym; // if redone as alias to another symbol
+ VarDeclaration *lastVar; // Linked list of variables for goto-skips-init detection
+ Expression *edtor; // if !=NULL, does the destruction of the variable
+ IntRange *range; // if !NULL, the variable is known to be within the range
+ VarDeclarations *maybes; // STCmaybescope variables that are assigned to this STCmaybescope variable
+
+ unsigned endlinnum; // line number of end of scope that this var lives in
unsigned offset;
unsigned sequenceNumber; // order the variables are declared
- FuncDeclarations nestedrefs; // referenced by these lexically nested functions
- bool isargptr; // if parameter that _argptr points to
structalign_t alignment;
+
+ // When interpreting, these point to the value (NULL if value not determinable)
+ // The index of this variable on the CTFE stack, ~0u if not allocated
+ unsigned ctfeAdrOnStack;
+
+ bool isargptr; // if parameter that _argptr points to
bool ctorinit; // it has been initialized in a ctor
+ bool iscatchvar; // this is the exception object variable in catch() clause
+ bool isowner; // this is an Owner, despite it being `scope`
bool onstack; // it is a class that was allocated on the stack
bool mynew; // it is a class new'd with custom operator new
- int canassign; // it can be assigned to
+ char canassign; // it can be assigned to
bool overlapped; // if it is a field and has overlapping
bool overlapUnsafe; // if it is an overlapping field and the overlaps are unsafe
bool doNotInferScope; // do not infer 'scope' for this variable
+ bool doNotInferReturn; // do not infer 'return' for this variable
unsigned char isdataseg; // private data for isDataseg
- Dsymbol *aliassym; // if redone as alias to another symbol
- VarDeclaration *lastVar; // Linked list of variables for goto-skips-init detection
- unsigned endlinnum; // line number of end of scope that this var lives in
-
- // When interpreting, these point to the value (NULL if value not determinable)
- // The index of this variable on the CTFE stack, -1 if not allocated
- int ctfeAdrOnStack;
- Expression *edtor; // if !=NULL, does the destruction of the variable
- IntRange *range; // if !NULL, the variable is known to be within the range
+ bool isArgDtorVar; // temporary created to handle scope destruction of a function argument
- VarDeclaration(Loc loc, Type *t, Identifier *id, Initializer *init);
- static VarDeclaration *create(Loc loc, Type *t, Identifier *id, Initializer *init);
- Dsymbol *syntaxCopy(Dsymbol *);
- void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
+public:
+ static VarDeclaration *create(const Loc &loc, Type *t, Identifier *id, Initializer *init, StorageClass storage_class = STCundefined);
+ VarDeclaration *syntaxCopy(Dsymbol *);
+ void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion);
const char *kind() const;
AggregateDeclaration *isThis();
bool needThis();
@@ -291,11 +274,7 @@ public:
bool canTakeAddressOf();
bool needsScopeDtor();
bool enclosesLifetimeOf(VarDeclaration *v) const;
- Expression *callScopeDtor(Scope *sc);
- Expression *getConstInitializer(bool needFullType = true);
- Expression *expandInitializer(Loc loc);
void checkCtorConstInit();
- bool checkNestedReference(Scope *sc, Loc loc);
Dsymbol *toAlias();
// Eliminate need for dynamic_cast
VarDeclaration *isVarDeclaration() { return (VarDeclaration *)this; }
@@ -304,6 +283,21 @@ public:
/**************************************************************/
+class BitFieldDeclaration : public VarDeclaration
+{
+public:
+ Expression *width;
+
+ unsigned fieldWidth;
+ unsigned bitOffset;
+
+ BitFieldDeclaration *syntaxCopy(Dsymbol*);
+ BitFieldDeclaration *isBitFieldDeclaration() { return this; }
+ void accept(Visitor *v) { v->visit(this); }
+};
+
+/**************************************************************/
+
// This is a shell around a back end symbol
class SymbolDeclaration : public Declaration
@@ -311,8 +305,6 @@ class SymbolDeclaration : public Declaration
public:
StructDeclaration *dsym;
- SymbolDeclaration(Loc loc, StructDeclaration *dsym);
-
// Eliminate need for dynamic_cast
SymbolDeclaration *isSymbolDeclaration() { return (SymbolDeclaration *)this; }
void accept(Visitor *v) { v->visit(this); }
@@ -323,10 +315,9 @@ class TypeInfoDeclaration : public VarDeclaration
public:
Type *tinfo;
- TypeInfoDeclaration(Type *tinfo);
static TypeInfoDeclaration *create(Type *tinfo);
- Dsymbol *syntaxCopy(Dsymbol *);
- const char *toChars();
+ TypeInfoDeclaration *syntaxCopy(Dsymbol *);
+ const char *toChars() const;
TypeInfoDeclaration *isTypeInfoDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -335,7 +326,6 @@ public:
class TypeInfoStructDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoStructDeclaration(Type *tinfo);
static TypeInfoStructDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -344,7 +334,6 @@ public:
class TypeInfoClassDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoClassDeclaration(Type *tinfo);
static TypeInfoClassDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -353,7 +342,6 @@ public:
class TypeInfoInterfaceDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoInterfaceDeclaration(Type *tinfo);
static TypeInfoInterfaceDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -362,7 +350,6 @@ public:
class TypeInfoPointerDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoPointerDeclaration(Type *tinfo);
static TypeInfoPointerDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -371,7 +358,6 @@ public:
class TypeInfoArrayDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoArrayDeclaration(Type *tinfo);
static TypeInfoArrayDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -380,7 +366,6 @@ public:
class TypeInfoStaticArrayDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoStaticArrayDeclaration(Type *tinfo);
static TypeInfoStaticArrayDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -389,7 +374,6 @@ public:
class TypeInfoAssociativeArrayDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoAssociativeArrayDeclaration(Type *tinfo);
static TypeInfoAssociativeArrayDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -398,7 +382,6 @@ public:
class TypeInfoEnumDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoEnumDeclaration(Type *tinfo);
static TypeInfoEnumDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -407,7 +390,6 @@ public:
class TypeInfoFunctionDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoFunctionDeclaration(Type *tinfo);
static TypeInfoFunctionDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -416,7 +398,6 @@ public:
class TypeInfoDelegateDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoDelegateDeclaration(Type *tinfo);
static TypeInfoDelegateDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -425,7 +406,6 @@ public:
class TypeInfoTupleDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoTupleDeclaration(Type *tinfo);
static TypeInfoTupleDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -434,7 +414,6 @@ public:
class TypeInfoConstDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoConstDeclaration(Type *tinfo);
static TypeInfoConstDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -443,7 +422,6 @@ public:
class TypeInfoInvariantDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoInvariantDeclaration(Type *tinfo);
static TypeInfoInvariantDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -452,7 +430,6 @@ public:
class TypeInfoSharedDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoSharedDeclaration(Type *tinfo);
static TypeInfoSharedDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -461,7 +438,6 @@ public:
class TypeInfoWildDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoWildDeclaration(Type *tinfo);
static TypeInfoWildDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -470,7 +446,6 @@ public:
class TypeInfoVectorDeclaration : public TypeInfoDeclaration
{
public:
- TypeInfoVectorDeclaration(Type *tinfo);
static TypeInfoVectorDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@@ -481,13 +456,12 @@ public:
class ThisDeclaration : public VarDeclaration
{
public:
- ThisDeclaration(Loc loc, Type *t);
- Dsymbol *syntaxCopy(Dsymbol *);
+ ThisDeclaration *syntaxCopy(Dsymbol *);
ThisDeclaration *isThisDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
-enum ILS
+enum class ILS : unsigned char
{
ILSuninitialized, // not computed yet
ILSno, // cannot inline
@@ -496,68 +470,53 @@ enum ILS
/**************************************************************/
-enum BUILTIN
-{
- BUILTINunknown = 255, /// not known if this is a builtin
- BUILTINunimp = 0, /// this is not a builtin
- BUILTINgcc, /// this is a GCC builtin
- BUILTINllvm, /// this is an LLVM builtin
- BUILTINsin,
- BUILTINcos,
- BUILTINtan,
- BUILTINsqrt,
- BUILTINfabs,
- BUILTINldexp,
- BUILTINlog,
- BUILTINlog2,
- BUILTINlog10,
- BUILTINexp,
- BUILTINexpm1,
- BUILTINexp2,
- BUILTINround,
- BUILTINfloor,
- BUILTINceil,
- BUILTINtrunc,
- BUILTINcopysign,
- BUILTINpow,
- BUILTINfmin,
- BUILTINfmax,
- BUILTINfma,
- BUILTINisnan,
- BUILTINisinfinity,
- BUILTINisfinite,
- BUILTINbsf,
- BUILTINbsr,
- BUILTINbswap,
- BUILTINpopcnt,
- BUILTINyl2x,
- BUILTINyl2xp1,
- BUILTINtoPrecFloat,
- BUILTINtoPrecDouble,
- BUILTINtoPrecReal
+enum class BUILTIN : unsigned char
+{
+ unknown = 255, /// not known if this is a builtin
+ unimp = 0, /// this is not a builtin
+ gcc, /// this is a GCC builtin
+ llvm, /// this is an LLVM builtin
+ sin,
+ cos,
+ tan,
+ sqrt,
+ fabs,
+ ldexp,
+ log,
+ log2,
+ log10,
+ exp,
+ expm1,
+ exp2,
+ round,
+ floor,
+ ceil,
+ trunc,
+ copysign,
+ pow,
+ fmin,
+ fmax,
+ fma,
+ isnan,
+ isinfinity,
+ isfinite,
+ bsf,
+ bsr,
+ bswap,
+ popcnt,
+ yl2x,
+ yl2xp1,
+ toPrecFloat,
+ toPrecDouble,
+ toPrecReal
};
Expression *eval_builtin(Loc loc, FuncDeclaration *fd, Expressions *arguments);
BUILTIN isBuiltin(FuncDeclaration *fd);
-typedef Expression *(*builtin_fp)(Loc loc, FuncDeclaration *fd, Expressions *arguments);
-void add_builtin(const char *mangle, builtin_fp fp);
-void builtin_init();
-
-#define FUNCFLAGpurityInprocess 1 // working on determining purity
-#define FUNCFLAGsafetyInprocess 2 // working on determining safety
-#define FUNCFLAGnothrowInprocess 4 // working on determining nothrow
-#define FUNCFLAGnogcInprocess 8 // working on determining @nogc
-#define FUNCFLAGreturnInprocess 0x10 // working on inferring 'return' for parameters
-#define FUNCFLAGinlineScanned 0x20 // function has been scanned for inline possibilities
-#define FUNCFLAGinferScope 0x40 // infer 'scope' for parameters
-#define FUNCFLAGprintf 0x200 // is a printf-like function
-#define FUNCFLAGscanf 0x400 // is a scanf-like function
-
class FuncDeclaration : public Declaration
{
public:
- Types *fthrows; // Array of Type's of exceptions (not used)
Statements *frequires; // in contracts
Ensures *fensures; // out contracts
Statement *frequire; // lowered in contract
@@ -568,6 +527,9 @@ public:
FuncDeclaration *fdrequire; // function that does the in contract
FuncDeclaration *fdensure; // function that does the out contract
+ Expressions *fdrequireParams; // argument list for __require
+ Expressions *fdensureParams; // argument list for __ensure
+
const char *mangleString; // mangled symbol created from mangleExact()
VarDeclaration *vresult; // result variable for out contracts
@@ -577,8 +539,9 @@ public:
// scopes from having the same name
DsymbolTable *localsymtab;
VarDeclaration *vthis; // 'this' parameter (member and nested)
+ bool isThis2; // has a dual-context 'this' parameter
VarDeclaration *v_arguments; // '_arguments' parameter
- ObjcSelector* selector; // Objective-C method selector (member function only)
+
VarDeclaration *v_argptr; // '_argptr' variable
VarDeclarations *parameters; // Array of VarDeclaration's for parameters
DsymbolTable *labtab; // statement label symbol table
@@ -589,13 +552,16 @@ public:
bool naked; // true if naked
bool generated; // true if function was generated by the compiler rather than
// supplied by the user
+ bool hasAlwaysInlines; // contains references to functions that must be inlined
+ unsigned char isCrtCtorDtor; // has attribute pragma(crt_constructor(1)/crt_destructor(2))
+ // not set before the glue layer
ILS inlineStatusStmt;
ILS inlineStatusExp;
PINLINE inlining;
- CompiledCtfeFunction *ctfeCode; // Compiled code for interpreter
int inlineNest; // !=0 if nested inline
- bool isArrayOp; // true if array operation
+ bool eh_none; /// true if no exception unwinding is needed
+
// true if errors in semantic3 this function's frame ptr
bool semantic3Errors;
ForeachStatement *fes; // if foreach body, this is the foreach
@@ -635,6 +601,12 @@ public:
// local variables in this function which are referenced by nested functions
VarDeclarations closureVars;
+
+ /** Outer variables which are referenced by this nested function
+ * (the inverse of closureVars)
+ */
+ VarDeclarations outerVars;
+
// Sibling nested functions which called this one
FuncDeclarations siblingCallers;
@@ -642,76 +614,62 @@ public:
unsigned flags; // FUNCFLAGxxxxx
- FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type);
- static FuncDeclaration *create(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type);
- Dsymbol *syntaxCopy(Dsymbol *);
+ // Data for a function declaration that is needed for the Objective-C
+ // integration.
+ ObjcFuncDeclaration objc;
+
+ static FuncDeclaration *create(const Loc &loc, const Loc &endloc, Identifier *id, StorageClass storage_class, Type *type, bool noreturn = false);
+ FuncDeclaration *syntaxCopy(Dsymbol *);
bool functionSemantic();
bool functionSemantic3();
- bool checkForwardRef(Loc loc);
- // called from semantic3
- VarDeclaration *declareThis(Scope *sc, AggregateDeclaration *ad);
- bool equals(RootObject *o);
+ bool equals(const RootObject *o) const;
int overrides(FuncDeclaration *fd);
- int findVtblIndex(Dsymbols *vtbl, int dim, bool fix17349 = true);
+ int findVtblIndex(Dsymbols *vtbl, int dim);
BaseClass *overrideInterface();
bool overloadInsert(Dsymbol *s);
- FuncDeclaration *overloadExactMatch(Type *t);
- FuncDeclaration *overloadModMatch(Loc loc, Type *tthis, bool &hasOverloads);
- TemplateDeclaration *findTemplateDeclRoot();
bool inUnittest();
MATCH leastAsSpecialized(FuncDeclaration *g);
- LabelDsymbol *searchLabel(Identifier *ident);
- int getLevel(Loc loc, Scope *sc, FuncDeclaration *fd); // lexical nesting level difference
+ LabelDsymbol *searchLabel(Identifier *ident, const Loc &loc);
+ int getLevel(FuncDeclaration *fd, int intypeof); // lexical nesting level difference
+ int getLevelAndCheck(const Loc &loc, Scope *sc, FuncDeclaration *fd);
const char *toPrettyChars(bool QualifyTypes = false);
const char *toFullSignature(); // for diagnostics, e.g. 'int foo(int x, int y) pure'
- bool isMain();
- bool isCMain();
- bool isWinMain();
- bool isDllMain();
+ bool isMain() const;
+ bool isCMain() const;
+ bool isWinMain() const;
+ bool isDllMain() const;
bool isExport() const;
bool isImportedSymbol() const;
bool isCodeseg() const;
- bool isOverloadable();
+ bool isOverloadable() const;
+ bool isAbstract();
PURE isPure();
PURE isPureBypassingInference();
- bool setImpure();
bool isSafe();
bool isSafeBypassingInference();
bool isTrusted();
- bool setUnsafe();
bool isNogc();
bool isNogcBypassingInference();
- bool setGC();
- void printGCUsage(Loc loc, const char *warn);
- bool isolateReturn();
- bool parametersIntersect(Type *t);
- virtual bool isNested();
+ virtual bool isNested() const;
AggregateDeclaration *isThis();
bool needThis();
bool isVirtualMethod();
- virtual bool isVirtual();
- virtual bool isFinalFunc();
+ virtual bool isVirtual() const;
+ bool isFinalFunc() const;
virtual bool addPreInvariant();
virtual bool addPostInvariant();
const char *kind() const;
- FuncDeclaration *isUnique();
- bool checkNestedReference(Scope *sc, Loc loc);
+ bool isUnique();
bool needsClosure();
- bool checkClosure();
bool hasNestedFrameRefs();
- void buildResultVar(Scope *sc, Type *tret);
- Statement *mergeFrequire(Statement *);
- static bool needsFensure(FuncDeclaration *fd);
- void buildEnsureRequire();
- Statement *mergeFensure(Statement *, Identifier *oid);
ParameterList getParameterList();
static FuncDeclaration *genCfunc(Parameters *args, Type *treturn, const char *name, StorageClass stc=0);
static FuncDeclaration *genCfunc(Parameters *args, Type *treturn, Identifier *id, StorageClass stc=0);
- void checkDmain();
+
bool checkNRVO();
FuncDeclaration *isFuncDeclaration() { return this; }
@@ -720,20 +678,12 @@ public:
void accept(Visitor *v) { v->visit(this); }
};
-FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s,
- Objects *tiargs,
- Type *tthis,
- Expressions *arguments,
- int flags = 0);
-
class FuncAliasDeclaration : public FuncDeclaration
{
public:
FuncDeclaration *funcalias;
bool hasOverloads;
- FuncAliasDeclaration(Identifier *ident, FuncDeclaration *funcalias, bool hasOverloads = true);
-
FuncAliasDeclaration *isFuncAliasDeclaration() { return this; }
const char *kind() const;
@@ -750,12 +700,10 @@ public:
// backend
bool deferToObj;
- FuncLiteralDeclaration(Loc loc, Loc endloc, Type *type, TOK tok,
- ForeachStatement *fes, Identifier *id = NULL);
- Dsymbol *syntaxCopy(Dsymbol *);
- bool isNested();
+ FuncLiteralDeclaration *syntaxCopy(Dsymbol *);
+ bool isNested() const;
AggregateDeclaration *isThis();
- bool isVirtual();
+ bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
@@ -770,11 +718,11 @@ public:
class CtorDeclaration : public FuncDeclaration
{
public:
- CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type);
- Dsymbol *syntaxCopy(Dsymbol *);
+ bool isCpCtor;
+ CtorDeclaration *syntaxCopy(Dsymbol *);
const char *kind() const;
- const char *toChars();
- bool isVirtual();
+ const char *toChars() const;
+ bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
@@ -785,9 +733,8 @@ public:
class PostBlitDeclaration : public FuncDeclaration
{
public:
- PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id);
- Dsymbol *syntaxCopy(Dsymbol *);
- bool isVirtual();
+ PostBlitDeclaration *syntaxCopy(Dsymbol *);
+ bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
bool overloadInsert(Dsymbol *s);
@@ -799,12 +746,10 @@ public:
class DtorDeclaration : public FuncDeclaration
{
public:
- DtorDeclaration(Loc loc, Loc endloc);
- DtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id);
- Dsymbol *syntaxCopy(Dsymbol *);
+ DtorDeclaration *syntaxCopy(Dsymbol *);
const char *kind() const;
- const char *toChars();
- bool isVirtual();
+ const char *toChars() const;
+ bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
bool overloadInsert(Dsymbol *s);
@@ -816,11 +761,9 @@ public:
class StaticCtorDeclaration : public FuncDeclaration
{
public:
- StaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc);
- StaticCtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc);
- Dsymbol *syntaxCopy(Dsymbol *);
+ StaticCtorDeclaration *syntaxCopy(Dsymbol *);
AggregateDeclaration *isThis();
- bool isVirtual();
+ bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
bool hasStaticCtorOrDtor();
@@ -832,8 +775,7 @@ public:
class SharedStaticCtorDeclaration : public StaticCtorDeclaration
{
public:
- SharedStaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc);
- Dsymbol *syntaxCopy(Dsymbol *);
+ SharedStaticCtorDeclaration *syntaxCopy(Dsymbol *);
SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -844,11 +786,9 @@ class StaticDtorDeclaration : public FuncDeclaration
public:
VarDeclaration *vgate; // 'gate' variable
- StaticDtorDeclaration(Loc loc, Loc endloc, StorageClass stc);
- StaticDtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc);
- Dsymbol *syntaxCopy(Dsymbol *);
+ StaticDtorDeclaration *syntaxCopy(Dsymbol *);
AggregateDeclaration *isThis();
- bool isVirtual();
+ bool isVirtual() const;
bool hasStaticCtorOrDtor();
bool addPreInvariant();
bool addPostInvariant();
@@ -860,8 +800,7 @@ public:
class SharedStaticDtorDeclaration : public StaticDtorDeclaration
{
public:
- SharedStaticDtorDeclaration(Loc loc, Loc endloc, StorageClass stc);
- Dsymbol *syntaxCopy(Dsymbol *);
+ SharedStaticDtorDeclaration *syntaxCopy(Dsymbol *);
SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -870,9 +809,8 @@ public:
class InvariantDeclaration : public FuncDeclaration
{
public:
- InvariantDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id = NULL);
- Dsymbol *syntaxCopy(Dsymbol *);
- bool isVirtual();
+ InvariantDeclaration *syntaxCopy(Dsymbol *);
+ bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
@@ -888,10 +826,9 @@ public:
// toObjFile() these nested functions after this one
FuncDeclarations deferredNested;
- UnitTestDeclaration(Loc loc, Loc endloc, StorageClass stc, char *codedoc);
- Dsymbol *syntaxCopy(Dsymbol *);
+ UnitTestDeclaration *syntaxCopy(Dsymbol *);
AggregateDeclaration *isThis();
- bool isVirtual();
+ bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
@@ -905,31 +842,12 @@ public:
Parameters *parameters;
VarArg varargs;
- NewDeclaration(Loc loc, Loc endloc, StorageClass stc, Parameters *arguments, VarArg varargs);
- Dsymbol *syntaxCopy(Dsymbol *);
+ NewDeclaration *syntaxCopy(Dsymbol *);
const char *kind() const;
- bool isVirtual();
+ bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
NewDeclaration *isNewDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
-
-
-class DeleteDeclaration : public FuncDeclaration
-{
-public:
- Parameters *parameters;
-
- DeleteDeclaration(Loc loc, Loc endloc, StorageClass stc, Parameters *arguments);
- Dsymbol *syntaxCopy(Dsymbol *);
- const char *kind() const;
- bool isDelete();
- bool isVirtual();
- bool addPreInvariant();
- bool addPostInvariant();
-
- DeleteDeclaration *isDeleteDeclaration() { return this; }
- void accept(Visitor *v) { v->visit(this); }
-};
diff --git a/gcc/d/dmd/delegatize.c b/gcc/d/dmd/delegatize.c
deleted file mode 100644
index b3019aae4b8..00000000000
--- a/gcc/d/dmd/delegatize.c
+++ /dev/null
@@ -1,208 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/delegatize.c
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "expression.h"
-#include "statement.h"
-#include "mtype.h"
-#include "utf.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "scope.h"
-#include "init.h"
-#include "tokens.h"
-
-
-bool walkPostorder(Expression *e, StoppableVisitor *v);
-void lambdaSetParent(Expression *e, Scope *sc);
-bool lambdaCheckForNestedRef(Expression *e, Scope *sc);
-
-/********************************************
- * Convert from expression to delegate that returns the expression,
- * i.e. convert:
- * expr
- * to:
- * typeof(expr) delegate() { return expr; }
- */
-Expression *toDelegate(Expression *e, Type* t, Scope *sc)
-{
- //printf("Expression::toDelegate(t = %s) %s\n", t->toChars(), e->toChars());
- Loc loc = e->loc;
-
- TypeFunction *tf = new TypeFunction(ParameterList(), t, LINKd);
- if (t->hasWild())
- tf->mod = MODwild;
- FuncLiteralDeclaration *fld =
- new FuncLiteralDeclaration(loc, loc, tf, TOKdelegate, NULL);
-
- sc = sc->push();
- sc->parent = fld; // set current function to be the delegate
- lambdaSetParent(e, sc);
- bool r = lambdaCheckForNestedRef(e, sc);
- sc = sc->pop();
-
- if (r)
- return new ErrorExp();
-
- Statement *s;
- if (t->ty == Tvoid)
- s = new ExpStatement(loc, e);
- else
- s = new ReturnStatement(loc, e);
- fld->fbody = s;
-
- e = new FuncExp(loc, fld);
- e = expressionSemantic(e, sc);
- return e;
-}
-
-/******************************************
- * Patch the parent of declarations to be the new function literal.
- */
-void lambdaSetParent(Expression *e, Scope *sc)
-{
- class LambdaSetParent : public StoppableVisitor
- {
- Scope *sc;
- public:
- LambdaSetParent(Scope *sc) : sc(sc) {}
-
- void visit(Expression *)
- {
- }
-
- void visit(DeclarationExp *e)
- {
- e->declaration->parent = sc->parent;
- }
-
- void visit(IndexExp *e)
- {
- if (e->lengthVar)
- {
- //printf("lengthVar\n");
- e->lengthVar->parent = sc->parent;
- }
- }
-
- void visit(SliceExp *e)
- {
- if (e->lengthVar)
- {
- //printf("lengthVar\n");
- e->lengthVar->parent = sc->parent;
- }
- }
- };
-
- LambdaSetParent lsp(sc);
- walkPostorder(e, &lsp);
-}
-
-/*******************************************
- * Look for references to variables in a scope enclosing the new function literal.
- * Returns true if error occurs.
- */
-bool lambdaCheckForNestedRef(Expression *e, Scope *sc)
-{
- class LambdaCheckForNestedRef : public StoppableVisitor
- {
- public:
- Scope *sc;
- bool result;
-
- LambdaCheckForNestedRef(Scope *sc)
- : sc(sc), result(false)
- {
- }
-
- void visit(Expression *)
- {
- }
-
- void visit(SymOffExp *e)
- {
- VarDeclaration *v = e->var->isVarDeclaration();
- if (v)
- result = v->checkNestedReference(sc, Loc());
- }
-
- void visit(VarExp *e)
- {
- VarDeclaration *v = e->var->isVarDeclaration();
- if (v)
- result = v->checkNestedReference(sc, Loc());
- }
-
- void visit(ThisExp *e)
- {
- if (e->var)
- result = e->var->checkNestedReference(sc, Loc());
- }
-
- void visit(DeclarationExp *e)
- {
- VarDeclaration *v = e->declaration->isVarDeclaration();
- if (v)
- {
- result = v->checkNestedReference(sc, Loc());
- if (result)
- return;
-
- /* Some expressions cause the frontend to create a temporary.
- * For example, structs with cpctors replace the original
- * expression e with:
- * __cpcttmp = __cpcttmp.cpctor(e);
- *
- * In this instance, we need to ensure that the original
- * expression e does not have any nested references by
- * checking the declaration initializer too.
- */
- if (v->_init && v->_init->isExpInitializer())
- {
- Expression *ie = initializerToExpression(v->_init);
- result = lambdaCheckForNestedRef(ie, sc);
- }
- }
- }
- };
-
- LambdaCheckForNestedRef v(sc);
- walkPostorder(e, &v);
- return v.result;
-}
-
-bool checkNestedRef(Dsymbol *s, Dsymbol *p)
-{
- while (s)
- {
- if (s == p) // hit!
- return false;
-
- if (FuncDeclaration *fd = s->isFuncDeclaration())
- {
- if (!fd->isThis() && !fd->isNested())
- break;
-
- // Bugzilla 15332: change to delegate if fd is actually nested.
- if (FuncLiteralDeclaration *fld = fd->isFuncLiteralDeclaration())
- fld->tok = TOKdelegate;
- }
- if (AggregateDeclaration *ad = s->isAggregateDeclaration())
- {
- if (ad->storage_class & STCstatic)
- break;
- }
- s = s->toParent2();
- }
- return true;
-}
diff --git a/gcc/d/dmd/delegatize.d b/gcc/d/dmd/delegatize.d
new file mode 100644
index 00000000000..07c1bbda650
--- /dev/null
+++ b/gcc/d/dmd/delegatize.d
@@ -0,0 +1,305 @@
+/**
+ * Implements conversion from expressions to delegates for lazy parameters.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/function.html#lazy-params, Lazy Parameters)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/delegatize.d, _delegatize.d)
+ * Documentation: https://dlang.org/phobos/dmd_delegatize.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/delegatize.d
+ */
+
+module dmd.delegatize;
+
+import core.stdc.stdio;
+import dmd.apply;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.init;
+import dmd.initsem;
+import dmd.mtype;
+import dmd.statement;
+import dmd.tokens;
+import dmd.visitor;
+
+
+/*********************************
+ * Convert expression into a delegate.
+ *
+ * Used to convert the argument to a lazy parameter.
+ *
+ * Params:
+ * e = argument to convert to a delegate
+ * t = the type to be returned by the delegate
+ * sc = context
+ * Returns:
+ * A delegate literal
+ */
+Expression toDelegate(Expression e, Type t, Scope* sc)
+{
+ //printf("Expression::toDelegate(t = %s) %s\n", t.toChars(), e.toChars());
+ Loc loc = e.loc;
+ auto tf = new TypeFunction(ParameterList(), t, LINK.d);
+ if (t.hasWild())
+ tf.mod = MODFlags.wild;
+ auto fld = new FuncLiteralDeclaration(loc, loc, tf, TOK.delegate_, null);
+ lambdaSetParent(e, fld);
+
+ sc = sc.push();
+ sc.parent = fld; // set current function to be the delegate
+ bool r = lambdaCheckForNestedRef(e, sc);
+ sc = sc.pop();
+ if (r)
+ return ErrorExp.get();
+
+ Statement s;
+ if (t.ty == Tvoid)
+ s = new ExpStatement(loc, e);
+ else
+ s = new ReturnStatement(loc, e);
+ fld.fbody = s;
+ e = new FuncExp(loc, fld);
+ e = e.expressionSemantic(sc);
+ return e;
+}
+
+/******************************************
+ * Patch the parent of declarations to be the new function literal.
+ *
+ * Since the expression is going to be moved into a function literal,
+ * the parent for declarations in the expression needs to be
+ * reset to that function literal.
+ * Params:
+ * e = expression to check
+ * fd = function literal symbol (the new parent)
+ */
+private void lambdaSetParent(Expression e, FuncDeclaration fd)
+{
+ extern (C++) final class LambdaSetParent : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ FuncDeclaration fd;
+
+ private void setParent(Dsymbol s)
+ {
+ VarDeclaration vd = s.isVarDeclaration();
+ FuncDeclaration pfd = s.parent ? s.parent.isFuncDeclaration() : null;
+ s.parent = fd;
+ if (!vd || !pfd)
+ return;
+ // move to fd's closure when applicable
+ foreach (i; 0 .. pfd.closureVars.dim)
+ {
+ if (vd == pfd.closureVars[i])
+ {
+ pfd.closureVars.remove(i);
+ fd.closureVars.push(vd);
+ break;
+ }
+ }
+ }
+
+ public:
+ extern (D) this(FuncDeclaration fd)
+ {
+ this.fd = fd;
+ }
+
+ override void visit(Expression)
+ {
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ setParent(e.declaration);
+ e.declaration.accept(this);
+ }
+
+ override void visit(IndexExp e)
+ {
+ if (e.lengthVar)
+ {
+ //printf("lengthVar\n");
+ setParent(e.lengthVar);
+ e.lengthVar.accept(this);
+ }
+ }
+
+ override void visit(SliceExp e)
+ {
+ if (e.lengthVar)
+ {
+ //printf("lengthVar\n");
+ setParent(e.lengthVar);
+ e.lengthVar.accept(this);
+ }
+ }
+
+ override void visit(Dsymbol)
+ {
+ }
+
+ override void visit(VarDeclaration v)
+ {
+ if (v._init)
+ v._init.accept(this);
+ }
+
+ override void visit(Initializer)
+ {
+ }
+
+ override void visit(ExpInitializer ei)
+ {
+ walkPostorder(ei.exp ,this);
+ }
+
+ override void visit(StructInitializer si)
+ {
+ foreach (i, const id; si.field)
+ if (Initializer iz = si.value[i])
+ iz.accept(this);
+ }
+
+ override void visit(ArrayInitializer ai)
+ {
+ foreach (i, ex; ai.index)
+ {
+ if (ex)
+ walkPostorder(ex, this);
+ if (Initializer iz = ai.value[i])
+ iz.accept(this);
+ }
+ }
+ }
+
+ scope LambdaSetParent lsp = new LambdaSetParent(fd);
+ walkPostorder(e, lsp);
+}
+
+/*******************************************
+ * Look for references to variables in a scope enclosing the new function literal.
+ *
+ * Essentially just calls `checkNestedReference() for each variable reference in `e`.
+ * Params:
+ * sc = context
+ * e = expression to check
+ * Returns:
+ * true if error occurs.
+ */
+bool lambdaCheckForNestedRef(Expression e, Scope* sc)
+{
+ extern (C++) final class LambdaCheckForNestedRef : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ public:
+ Scope* sc;
+ bool result;
+
+ extern (D) this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ override void visit(Expression)
+ {
+ }
+
+ override void visit(SymOffExp e)
+ {
+ VarDeclaration v = e.var.isVarDeclaration();
+ if (v)
+ result = v.checkNestedReference(sc, Loc.initial);
+ }
+
+ override void visit(VarExp e)
+ {
+ VarDeclaration v = e.var.isVarDeclaration();
+ if (v)
+ result = v.checkNestedReference(sc, Loc.initial);
+ }
+
+ override void visit(ThisExp e)
+ {
+ if (e.var)
+ result = e.var.checkNestedReference(sc, Loc.initial);
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ VarDeclaration v = e.declaration.isVarDeclaration();
+ if (v)
+ {
+ result = v.checkNestedReference(sc, Loc.initial);
+ if (result)
+ return;
+ /* Some expressions cause the frontend to create a temporary.
+ * For example, structs with cpctors replace the original
+ * expression e with:
+ * __cpcttmp = __cpcttmp.cpctor(e);
+ *
+ * In this instance, we need to ensure that the original
+ * expression e does not have any nested references by
+ * checking the declaration initializer too.
+ */
+ if (v._init && v._init.isExpInitializer())
+ {
+ Expression ie = v._init.initializerToExpression();
+ result = lambdaCheckForNestedRef(ie, sc);
+ }
+ }
+ }
+ }
+
+ scope LambdaCheckForNestedRef v = new LambdaCheckForNestedRef(sc);
+ walkPostorder(e, v);
+ return v.result;
+}
+
+/*****************************************
+ * See if context `s` is nested within context `p`, meaning
+ * it `p` is reachable at runtime by walking the static links.
+ * If any of the intervening contexts are function literals,
+ * make sure they are delegates.
+ * Params:
+ * s = inner context
+ * p = outer context
+ * Returns:
+ * true means it is accessible by walking the context pointers at runtime
+ * References:
+ * for static links see https://en.wikipedia.org/wiki/Call_stack#Functions_of_the_call_stack
+ */
+bool ensureStaticLinkTo(Dsymbol s, Dsymbol p)
+{
+ while (s)
+ {
+ if (s == p) // hit!
+ return true;
+
+ if (auto fd = s.isFuncDeclaration())
+ {
+ if (!fd.isThis() && !fd.isNested())
+ break;
+
+ // https://issues.dlang.org/show_bug.cgi?id=15332
+ // change to delegate if fd is actually nested.
+ if (auto fld = fd.isFuncLiteralDeclaration())
+ fld.tok = TOK.delegate_;
+ }
+ if (auto ad = s.isAggregateDeclaration())
+ {
+ if (ad.storage_class & STC.static_)
+ break;
+ }
+ s = s.toParentP(p);
+ }
+ return false;
+}
diff --git a/gcc/d/dmd/denum.c b/gcc/d/dmd/denum.c
deleted file mode 100644
index b00eaa02f9d..00000000000
--- a/gcc/d/dmd/denum.c
+++ /dev/null
@@ -1,388 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/enum.c
- */
-
-#include "root/dsystem.h"
-#include "root/root.h"
-
-#include "errors.h"
-#include "enum.h"
-#include "attrib.h"
-#include "mtype.h"
-#include "scope.h"
-#include "id.h"
-#include "expression.h"
-#include "module.h"
-#include "declaration.h"
-#include "init.h"
-
-bool isSpecialEnumIdent(const Identifier *ident)
-{
- return ident == Id::__c_long ||
- ident == Id::__c_ulong ||
- ident == Id::__c_longlong ||
- ident == Id::__c_ulonglong ||
- ident == Id::__c_long_double ||
- ident == Id::__c_wchar_t ||
- ident == Id::__c_complex_float ||
- ident == Id::__c_complex_double ||
- ident == Id::__c_complex_real;
-}
-
-/********************************* EnumDeclaration ****************************/
-
-EnumDeclaration::EnumDeclaration(Loc loc, Identifier *id, Type *memtype)
- : ScopeDsymbol(id)
-{
- //printf("EnumDeclaration() %s\n", toChars());
- this->loc = loc;
- type = new TypeEnum(this);
- this->memtype = memtype;
- maxval = NULL;
- minval = NULL;
- defaultval = NULL;
- sinit = NULL;
- isdeprecated = false;
- protection = Prot(Prot::undefined);
- parent = NULL;
- added = false;
- inuse = 0;
-}
-
-Dsymbol *EnumDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- EnumDeclaration *ed = new EnumDeclaration(loc, ident,
- memtype ? memtype->syntaxCopy() : NULL);
- return ScopeDsymbol::syntaxCopy(ed);
-}
-
-void EnumDeclaration::setScope(Scope *sc)
-{
- if (semanticRun > PASSinit)
- return;
- ScopeDsymbol::setScope(sc);
-}
-
-void EnumDeclaration::addMember(Scope *sc, ScopeDsymbol *sds)
-{
- /* Anonymous enum members get added to enclosing scope.
- */
- ScopeDsymbol *scopesym = isAnonymous() ? sds : this;
-
- if (!isAnonymous())
- {
- ScopeDsymbol::addMember(sc, sds);
-
- if (!symtab)
- symtab = new DsymbolTable();
- }
-
- if (members)
- {
- for (size_t i = 0; i < members->length; i++)
- {
- EnumMember *em = (*members)[i]->isEnumMember();
- em->ed = this;
- //printf("add %s to scope %s\n", em->toChars(), scopesym->toChars());
- em->addMember(sc, isAnonymous() ? scopesym : this);
- }
- }
- added = true;
-}
-
-/******************************
- * Get the value of the .max/.min property as an Expression
- * Input:
- * id Id::max or Id::min
- */
-
-Expression *EnumDeclaration::getMaxMinValue(Loc loc, Identifier *id)
-{
- //printf("EnumDeclaration::getMaxValue()\n");
- bool first = true;
-
- Expression **pval = (id == Id::max) ? &maxval : &minval;
-
- if (inuse)
- {
- error(loc, "recursive definition of .%s property", id->toChars());
- goto Lerrors;
- }
- if (*pval)
- goto Ldone;
-
- if (_scope)
- dsymbolSemantic(this, _scope);
- if (errors)
- goto Lerrors;
- if (!members)
- {
- if (isSpecial())
- {
- /* Allow these special enums to not need a member list
- */
- return memtype->getProperty(loc, id, 0);
- }
-
- error(loc, "is opaque and has no `.%s`", id->toChars());
- goto Lerrors;
- }
- if (!(memtype && memtype->isintegral()))
- {
- error(loc, "has no .%s property because base type %s is not an integral type",
- id->toChars(),
- memtype ? memtype->toChars() : "");
- goto Lerrors;
- }
-
- for (size_t i = 0; i < members->length; i++)
- {
- EnumMember *em = (*members)[i]->isEnumMember();
- if (!em)
- continue;
- if (em->errors)
- {
- errors = true;
- continue;
- }
-
- if (em->semanticRun < PASSsemanticdone)
- {
- em->error("is forward referenced looking for `.%s`", id->toChars());
- errors = true;
- continue;
- }
-
- if (first)
- {
- *pval = em->value();
- first = false;
- }
- else
- {
- /* In order to work successfully with UDTs,
- * build expressions to do the comparisons,
- * and let the semantic analyzer and constant
- * folder give us the result.
- */
-
- /* Compute:
- * if (e > maxval)
- * maxval = e;
- */
- Expression *e = em->value();
- Expression *ec = new CmpExp(id == Id::max ? TOKgt : TOKlt, em->loc, e, *pval);
- inuse++;
- ec = expressionSemantic(ec, em->_scope);
- inuse--;
- ec = ec->ctfeInterpret();
- if (ec->op == TOKerror)
- {
- errors = true;
- continue;
- }
- if (ec->toInteger())
- *pval = e;
- }
- }
- if (errors)
- goto Lerrors;
-Ldone:
- {
- Expression *e = *pval;
- if (e->op != TOKerror)
- {
- e = e->copy();
- e->loc = loc;
- }
- return e;
- }
-
-Lerrors:
- *pval = new ErrorExp();
- return *pval;
-}
-
-/****************
- * Determine if enum is a 'special' one.
- * Returns:
- * true if special
- */
-bool EnumDeclaration::isSpecial() const
-{
- return isSpecialEnumIdent(ident) && memtype;
-}
-
-Expression *EnumDeclaration::getDefaultValue(Loc loc)
-{
- //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars());
- if (defaultval)
- return defaultval;
-
- if (_scope)
- dsymbolSemantic(this, _scope);
- if (errors)
- goto Lerrors;
- if (!members)
- {
- if (isSpecial())
- {
- /* Allow these special enums to not need a member list
- */
- defaultval = memtype->defaultInit(loc);
- return defaultval;
- }
-
- error(loc, "is opaque and has no default initializer");
- goto Lerrors;
- }
-
- for (size_t i = 0; i < members->length; i++)
- {
- EnumMember *em = (*members)[i]->isEnumMember();
- if (em)
- {
- if (em->semanticRun < PASSsemanticdone)
- {
- error(loc, "forward reference of `%s.init`", toChars());
- goto Lerrors;
- }
-
- defaultval = em->value();
- return defaultval;
- }
- }
-
-Lerrors:
- defaultval = new ErrorExp();
- return defaultval;
-}
-
-Type *EnumDeclaration::getMemtype(Loc loc)
-{
- if (loc.linnum == 0)
- loc = this->loc;
- if (_scope)
- {
- /* Enum is forward referenced. We don't need to resolve the whole thing,
- * just the base type
- */
- if (memtype)
- memtype = typeSemantic(memtype, loc, _scope);
- }
- if (!memtype)
- {
- if (!isAnonymous() && (members || semanticRun >= PASSsemanticdone))
- memtype = Type::tint32;
- else
- {
- error(loc, "is forward referenced looking for base type");
- return Type::terror;
- }
- }
- return memtype;
-}
-
-bool EnumDeclaration::oneMember(Dsymbol **ps, Identifier *ident)
-{
- if (isAnonymous())
- return Dsymbol::oneMembers(members, ps, ident);
- return Dsymbol::oneMember(ps, ident);
-}
-
-Type *EnumDeclaration::getType()
-{
- return type;
-}
-
-const char *EnumDeclaration::kind() const
-{
- return "enum";
-}
-
-bool EnumDeclaration::isDeprecated()
-{
- return isdeprecated;
-}
-
-Prot EnumDeclaration::prot()
-{
- return protection;
-}
-
-Dsymbol *EnumDeclaration::search(const Loc &loc, Identifier *ident, int flags)
-{
- //printf("%s.EnumDeclaration::search('%s')\n", toChars(), ident->toChars());
- if (_scope)
- {
- // Try one last time to resolve this enum
- dsymbolSemantic(this, _scope);
- }
-
- Dsymbol *s = ScopeDsymbol::search(loc, ident, flags);
- return s;
-}
-
-/********************************* EnumMember ****************************/
-
-EnumMember::EnumMember(Loc loc, Identifier *id, Expression *value, Type *origType)
- : VarDeclaration(loc, NULL, id ? id : Id::empty, new ExpInitializer(loc, value))
-{
- this->ed = NULL;
- this->origValue = value;
- this->origType = origType;
-}
-
-EnumMember::EnumMember(Loc loc, Identifier *id, Expression *value, Type *memType,
- StorageClass stc, UserAttributeDeclaration *uad, DeprecatedDeclaration *dd)
- : VarDeclaration(loc, NULL, id ? id : Id::empty, new ExpInitializer(loc, value))
-{
- this->ed = NULL;
- this->origValue = value;
- this->origType = memType;
- this->storage_class = stc;
- this->userAttribDecl = uad;
- this->depdecl = dd;
-}
-
-Expression *&EnumMember::value()
-{
- return ((ExpInitializer*)_init)->exp;
-}
-
-Dsymbol *EnumMember::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new EnumMember(loc, ident,
- value() ? value()->syntaxCopy() : NULL,
- origType ? origType->syntaxCopy() : NULL);
-}
-
-const char *EnumMember::kind() const
-{
- return "enum member";
-}
-
-Expression *EnumMember::getVarExp(Loc loc, Scope *sc)
-{
- dsymbolSemantic(this, sc);
- if (errors)
- return new ErrorExp();
- checkDisabled(loc, sc);
-
- if (depdecl && !depdecl->_scope)
- depdecl->_scope = sc;
- checkDeprecated(loc, sc);
-
- if (errors)
- return new ErrorExp();
- Expression *e = new VarExp(loc, this);
- return expressionSemantic(e, sc);
-}
diff --git a/gcc/d/dmd/denum.d b/gcc/d/dmd/denum.d
new file mode 100644
index 00000000000..54467d8efd4
--- /dev/null
+++ b/gcc/d/dmd/denum.d
@@ -0,0 +1,333 @@
+/**
+ * Define `enum` declarations and `enum` members.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/enum.html, Enums)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/denum.d, _denum.d)
+ * Documentation: https://dlang.org/phobos/dmd_denum.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/denum.d
+ * References: https://dlang.org/spec/enum.html
+ */
+
+module dmd.denum;
+
+import core.stdc.stdio;
+
+import dmd.attrib;
+import dmd.gluelayer;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.expression;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.visitor;
+
+/***********************************************************
+ * AST node for `EnumDeclaration`
+ * https://dlang.org/spec/enum.html#EnumDeclaration
+ */
+extern (C++) final class EnumDeclaration : ScopeDsymbol
+{
+ /* The separate, and distinct, cases are:
+ * 1. enum { ... }
+ * 2. enum : memtype { ... }
+ * 3. enum id { ... }
+ * 4. enum id : memtype { ... }
+ * 5. enum id : memtype;
+ * 6. enum id;
+ */
+ Type type; // the TypeEnum
+ Type memtype; // type of the members
+
+ Visibility visibility;
+ Expression maxval;
+ Expression minval;
+ Expression defaultval; // default initializer
+ bool isdeprecated;
+ bool added;
+ int inuse;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Type memtype)
+ {
+ super(loc, ident);
+ //printf("EnumDeclaration() %s\n", toChars());
+ type = new TypeEnum(this);
+ this.memtype = memtype;
+ visibility = Visibility(Visibility.Kind.undefined);
+ }
+
+ override EnumDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto ed = new EnumDeclaration(loc, ident, memtype ? memtype.syntaxCopy() : null);
+ ScopeDsymbol.syntaxCopy(ed);
+ return ed;
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ version (none)
+ {
+ printf("EnumDeclaration::addMember() %s\n", toChars());
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ EnumMember em = (*members)[i].isEnumMember();
+ printf(" member %s\n", em.toChars());
+ }
+ }
+ if (!isAnonymous())
+ {
+ ScopeDsymbol.addMember(sc, sds);
+ }
+
+ addEnumMembers(this, sc, sds);
+ }
+
+ override void setScope(Scope* sc)
+ {
+ if (semanticRun > PASS.init)
+ return;
+ ScopeDsymbol.setScope(sc);
+ }
+
+ override bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ if (isAnonymous())
+ return Dsymbol.oneMembers(members, ps, ident);
+ return Dsymbol.oneMember(ps, ident);
+ }
+
+ override Type getType()
+ {
+ return type;
+ }
+
+ override const(char)* kind() const
+ {
+ return "enum";
+ }
+
+ override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ //printf("%s.EnumDeclaration::search('%s')\n", toChars(), ident.toChars());
+ if (_scope)
+ {
+ // Try one last time to resolve this enum
+ dsymbolSemantic(this, _scope);
+ }
+
+ Dsymbol s = ScopeDsymbol.search(loc, ident, flags);
+ return s;
+ }
+
+ // is Dsymbol deprecated?
+ override bool isDeprecated() const
+ {
+ return isdeprecated;
+ }
+
+ override Visibility visible() pure nothrow @nogc @safe
+ {
+ return visibility;
+ }
+
+
+ /****************
+ * Determine if enum is a special one.
+ * Returns:
+ * `true` if special
+ */
+ bool isSpecial() const nothrow @nogc
+ {
+ return isSpecialEnumIdent(ident) && memtype;
+ }
+
+ Expression getDefaultValue(const ref Loc loc)
+ {
+ Expression handleErrors(){
+ defaultval = ErrorExp.get();
+ return defaultval;
+ }
+ //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars());
+ if (defaultval)
+ return defaultval;
+
+ if (_scope)
+ dsymbolSemantic(this, _scope);
+ if (errors)
+ return handleErrors();
+ if (!members)
+ {
+ if (isSpecial())
+ {
+ /* Allow these special enums to not need a member list
+ */
+ return defaultval = memtype.defaultInit(loc);
+ }
+
+ error(loc, "is opaque and has no default initializer");
+ return handleErrors();
+ }
+
+ foreach (const i; 0 .. members.dim)
+ {
+ EnumMember em = (*members)[i].isEnumMember();
+ if (em)
+ {
+ if (em.semanticRun < PASS.semanticdone)
+ {
+ error(loc, "forward reference of `%s.init`", toChars());
+ return handleErrors();
+ }
+
+ defaultval = em.value;
+ return defaultval;
+ }
+ }
+ return handleErrors();
+ }
+
+ Type getMemtype(const ref Loc loc)
+ {
+ if (_scope)
+ {
+ /* Enum is forward referenced. We don't need to resolve the whole thing,
+ * just the base type
+ */
+ if (memtype)
+ {
+ Loc locx = loc.isValid() ? loc : this.loc;
+ memtype = memtype.typeSemantic(locx, _scope);
+ }
+ else
+ {
+ // Run semantic to get the type from a possible first member value
+ dsymbolSemantic(this, _scope);
+ }
+ }
+ if (!memtype)
+ {
+ if (!isAnonymous() && (members || semanticRun >= PASS.semanticdone))
+ memtype = Type.tint32;
+ else
+ {
+ Loc locx = loc.isValid() ? loc : this.loc;
+ error(locx, "is forward referenced looking for base type");
+ return Type.terror;
+ }
+ }
+ return memtype;
+ }
+
+ override inout(EnumDeclaration) isEnumDeclaration() inout
+ {
+ return this;
+ }
+
+ Symbol* sinit;
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * AST node representing a member of an enum.
+ * https://dlang.org/spec/enum.html#EnumMember
+ * https://dlang.org/spec/enum.html#AnonymousEnumMember
+ */
+extern (C++) final class EnumMember : VarDeclaration
+{
+ /* Can take the following forms:
+ * 1. id
+ * 2. id = value
+ * 3. type id = value
+ */
+ @property ref value() { return (cast(ExpInitializer)_init).exp; }
+
+ // A cast() is injected to 'value' after dsymbolSemantic(),
+ // but 'origValue' will preserve the original value,
+ // or previous value + 1 if none was specified.
+ Expression origValue;
+
+ Type origType;
+
+ EnumDeclaration ed;
+
+ extern (D) this(const ref Loc loc, Identifier id, Expression value, Type origType)
+ {
+ super(loc, null, id ? id : Id.empty, new ExpInitializer(loc, value));
+ this.origValue = value;
+ this.origType = origType;
+ }
+
+ extern(D) this(Loc loc, Identifier id, Expression value, Type memtype,
+ StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd)
+ {
+ this(loc, id, value, memtype);
+ storage_class = stc;
+ userAttribDecl = uad;
+ depdecl = dd;
+ }
+
+ override EnumMember syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new EnumMember(
+ loc, ident,
+ value ? value.syntaxCopy() : null,
+ origType ? origType.syntaxCopy() : null,
+ storage_class,
+ userAttribDecl ? userAttribDecl.syntaxCopy(s) : null,
+ depdecl ? depdecl.syntaxCopy(s) : null);
+ }
+
+ override const(char)* kind() const
+ {
+ return "enum member";
+ }
+
+ override inout(EnumMember) isEnumMember() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/******************************************
+ * Check for special enum names.
+ *
+ * Special enum names are used by the C++ name mangler to represent
+ * C++ types that are not basic D types.
+ * Params:
+ * ident = identifier to check for specialness
+ * Returns:
+ * `true` if it is special
+ */
+bool isSpecialEnumIdent(const Identifier ident) @nogc nothrow
+{
+ return ident == Id.__c_long ||
+ ident == Id.__c_ulong ||
+ ident == Id.__c_longlong ||
+ ident == Id.__c_ulonglong ||
+ ident == Id.__c_long_double ||
+ ident == Id.__c_wchar_t ||
+ ident == Id.__c_complex_float ||
+ ident == Id.__c_complex_double ||
+ ident == Id.__c_complex_real;
+}
diff --git a/gcc/d/dmd/dimport.c b/gcc/d/dmd/dimport.c
deleted file mode 100644
index 7b63a186552..00000000000
--- a/gcc/d/dmd/dimport.c
+++ /dev/null
@@ -1,320 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/import.c
- */
-
-#include "root/dsystem.h"
-#include "root/root.h"
-
-#include "mars.h"
-#include "dsymbol.h"
-#include "import.h"
-#include "identifier.h"
-#include "module.h"
-#include "scope.h"
-#include "mtype.h"
-#include "declaration.h"
-#include "id.h"
-#include "attrib.h"
-#include "hdrgen.h"
-
-/********************************* Import ****************************/
-
-Import::Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId,
- int isstatic)
- : Dsymbol(NULL)
-{
- assert(id);
- this->loc = loc;
- this->packages = packages;
- this->id = id;
- this->aliasId = aliasId;
- this->isstatic = isstatic;
- this->protection = Prot(Prot::private_); // default to private
- this->pkg = NULL;
- this->mod = NULL;
-
- // Set symbol name (bracketed)
- if (aliasId)
- {
- // import [cstdio] = std.stdio;
- this->ident = aliasId;
- }
- else if (packages && packages->length)
- {
- // import [std].stdio;
- this->ident = (*packages)[0];
- }
- else
- {
- // import [foo];
- this->ident = id;
- }
-}
-
-void Import::addAlias(Identifier *name, Identifier *alias)
-{
- if (isstatic)
- error("cannot have an import bind list");
-
- if (!aliasId)
- this->ident = NULL; // make it an anonymous import
-
- names.push(name);
- aliases.push(alias);
-}
-
-const char *Import::kind() const
-{
- return isstatic ? "static import" : "import";
-}
-
-Prot Import::prot()
-{
- return protection;
-}
-
-Dsymbol *Import::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
-
- Import *si = new Import(loc, packages, id, aliasId, isstatic);
-
- for (size_t i = 0; i < names.length; i++)
- {
- si->addAlias(names[i], aliases[i]);
- }
-
- return si;
-}
-
-void Import::load(Scope *sc)
-{
- //printf("Import::load('%s') %p\n", toPrettyChars(), this);
-
- // See if existing module
- DsymbolTable *dst = Package::resolve(packages, NULL, &pkg);
- Dsymbol *s = dst->lookup(id);
- if (s)
- {
- if (s->isModule())
- mod = (Module *)s;
- else
- {
- if (s->isAliasDeclaration())
- {
- ::error(loc, "%s %s conflicts with %s", s->kind(), s->toPrettyChars(), id->toChars());
- }
- else if (Package *p = s->isPackage())
- {
- if (p->isPkgMod == PKGunknown)
- {
- mod = Module::load(loc, packages, id);
- if (!mod)
- p->isPkgMod = PKGpackage;
- else
- {
- // mod is a package.d, or a normal module which conflicts with the package name.
- assert(mod->isPackageFile == (p->isPkgMod == PKGmodule));
- if (mod->isPackageFile)
- mod->tag = p->tag; // reuse the same package tag
- }
- }
- else
- {
- mod = p->isPackageMod();
- }
- if (!mod)
- {
- ::error(loc, "can only import from a module, not from package %s.%s",
- p->toPrettyChars(), id->toChars());
- }
- }
- else if (pkg)
- {
- ::error(loc, "can only import from a module, not from package %s.%s",
- pkg->toPrettyChars(), id->toChars());
- }
- else
- {
- ::error(loc, "can only import from a module, not from package %s",
- id->toChars());
- }
- }
- }
-
- if (!mod)
- {
- // Load module
- mod = Module::load(loc, packages, id);
- if (mod)
- {
- dst->insert(id, mod); // id may be different from mod->ident,
- // if so then insert alias
- }
- }
- if (mod && !mod->importedFrom)
- mod->importedFrom = sc ? sc->_module->importedFrom : Module::rootModule;
- if (!pkg)
- {
- if (mod && mod->isPackageFile)
- {
- // one level depth package.d file (import pkg; ./pkg/package.d)
- // it's necessary to use the wrapping Package already created
- pkg = mod->pkg;
- }
- else
- pkg = mod;
- }
-
- //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg);
-}
-
-void Import::importAll(Scope *sc)
-{
- if (mod) return; // Already done
- load(sc);
- if (!mod) return; // Failed
-
- if (sc->stc & STCstatic)
- isstatic = true;
- mod->importAll(NULL);
- if (mod->md && mod->md->isdeprecated)
- {
- Expression *msg = mod->md->msg;
- if (StringExp *se = msg ? msg->toStringExp() : NULL)
- mod->deprecation(loc, "is deprecated - %s", se->string);
- else
- mod->deprecation(loc, "is deprecated");
- }
- if (sc->explicitProtection)
- protection = sc->protection;
- if (!isstatic && !aliasId && !names.length)
- sc->scopesym->importScope(mod, protection);
- // Enable access to pkgs/mod as soon as posible, because compiler
- // can traverse them before the import gets semantic (Issue: 21501)
- if (!aliasId && !names.length)
- addPackageAccess(sc->scopesym);
-}
-
-/*******************************
- * Mark the imported packages as accessible from the current
- * scope. This access check is necessary when using FQN b/c
- * we're using a single global package tree.
- * https://issues.dlang.org/show_bug.cgi?id=313
- */
-void Import::addPackageAccess(ScopeDsymbol *scopesym)
-{
- //printf("Import::addPackageAccess('%s') %p\n", toPrettyChars(), this);
- if (packages)
- {
- // import a.b.c.d;
- Package *p = pkg; // a
- scopesym->addAccessiblePackage(p, protection);
- for (size_t i = 1; i < packages->length; i++) // [b, c]
- {
- Identifier *id = (*packages)[i];
- p = (Package *) p->symtab->lookup(id);
- // https://issues.dlang.org/show_bug.cgi?id=17991
- // An import of truly empty file/package can happen
- // https://issues.dlang.org/show_bug.cgi?id=20151
- // Package in the path conflicts with a module name
- if (p == NULL)
- return;
- scopesym->addAccessiblePackage(p, protection);
- }
- }
- scopesym->addAccessiblePackage(mod, protection); // d
-}
-
-Dsymbol *Import::toAlias()
-{
- if (aliasId)
- return mod;
- return this;
-}
-
-/*****************************
- * Add import to sd's symbol table.
- */
-
-void Import::addMember(Scope *sc, ScopeDsymbol *sd)
-{
- //printf("Import::addMember(this=%s, sd=%s, sc=%p)\n", toChars(), sd->toChars(), sc);
- if (names.length == 0)
- return Dsymbol::addMember(sc, sd);
-
- if (aliasId)
- Dsymbol::addMember(sc, sd);
-
- /* Instead of adding the import to sd's symbol table,
- * add each of the alias=name pairs
- */
- for (size_t i = 0; i < names.length; i++)
- {
- Identifier *name = names[i];
- Identifier *alias = aliases[i];
-
- if (!alias)
- alias = name;
-
- TypeIdentifier *tname = new TypeIdentifier(loc, name);
- AliasDeclaration *ad = new AliasDeclaration(loc, alias, tname);
- ad->_import = this;
- ad->addMember(sc, sd);
-
- aliasdecls.push(ad);
- }
-}
-
-void Import::setScope(Scope *sc)
-{
- Dsymbol::setScope(sc);
- if (aliasdecls.length)
- {
- if (!mod)
- importAll(sc);
-
- sc = sc->push(mod);
- sc->protection = protection;
- for (size_t i = 0; i < aliasdecls.length; i++)
- {
- AliasDeclaration *ad = aliasdecls[i];
- ad->setScope(sc);
- }
- sc = sc->pop();
- }
-}
-
-Dsymbol *Import::search(const Loc &loc, Identifier *ident, int flags)
-{
- //printf("%s.Import::search(ident = '%s', flags = x%x)\n", toChars(), ident->toChars(), flags);
-
- if (!pkg)
- {
- load(NULL);
- mod->importAll(NULL);
- dsymbolSemantic(mod, NULL);
- }
-
- // Forward it to the package/module
- return pkg->search(loc, ident, flags);
-}
-
-bool Import::overloadInsert(Dsymbol *s)
-{
- /* Allow multiple imports with the same package base, but disallow
- * alias collisions (Bugzilla 5412).
- */
- assert(ident && ident == s->ident);
- Import *imp;
- if (!aliasId && (imp = s->isImport()) != NULL && !imp->aliasId)
- return true;
- else
- return false;
-}
diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d
new file mode 100644
index 00000000000..8cd436422ee
--- /dev/null
+++ b/gcc/d/dmd/dimport.d
@@ -0,0 +1,358 @@
+/**
+ * A `Dsymbol` representing a renamed import.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dimport.d, _dimport.d)
+ * Documentation: https://dlang.org/phobos/dmd_dimport.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dimport.d
+ */
+
+module dmd.dimport;
+
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.errors;
+import dmd.expression;
+import dmd.globals;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.visitor;
+
+/***********************************************************
+ */
+extern (C++) final class Import : Dsymbol
+{
+ /* static import aliasId = pkg1.pkg2.id : alias1 = name1, alias2 = name2;
+ */
+ Identifier[] packages; // array of Identifier's representing packages
+ Identifier id; // module Identifier
+ Identifier aliasId;
+ int isstatic; // !=0 if static import
+ Visibility visibility;
+
+ // Pairs of alias=name to bind into current namespace
+ Identifiers names;
+ Identifiers aliases;
+
+ Module mod;
+ Package pkg; // leftmost package/module
+
+ // corresponding AliasDeclarations for alias=name pairs
+ AliasDeclarations aliasdecls;
+
+ extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Identifier aliasId, int isstatic)
+ {
+ Identifier selectIdent()
+ {
+ // select Dsymbol identifier (bracketed)
+ if (aliasId)
+ {
+ // import [aliasId] = std.stdio;
+ return aliasId;
+ }
+ else if (packages.length > 0)
+ {
+ // import [std].stdio;
+ return packages[0];
+ }
+ else
+ {
+ // import [id];
+ return id;
+ }
+ }
+
+ super(loc, selectIdent());
+
+ assert(id);
+ version (none)
+ {
+ printf("Import::Import(");
+ foreach (id; packages)
+ {
+ printf("%s.", id.toChars());
+ }
+ printf("%s)\n", id.toChars());
+ }
+ this.packages = packages;
+ this.id = id;
+ this.aliasId = aliasId;
+ this.isstatic = isstatic;
+ this.visibility = Visibility.Kind.private_; // default to private
+ }
+
+ extern (D) void addAlias(Identifier name, Identifier _alias)
+ {
+ if (isstatic)
+ error("cannot have an import bind list");
+ if (!aliasId)
+ this.ident = null; // make it an anonymous import
+ names.push(name);
+ aliases.push(_alias);
+ }
+
+ override const(char)* kind() const
+ {
+ return isstatic ? "static import" : "import";
+ }
+
+ override Visibility visible() pure nothrow @nogc @safe
+ {
+ return visibility;
+ }
+
+ // copy only syntax trees
+ override Import syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto si = new Import(loc, packages, id, aliasId, isstatic);
+ si.comment = comment;
+ for (size_t i = 0; i < names.dim; i++)
+ {
+ si.addAlias(names[i], aliases[i]);
+ }
+ return si;
+ }
+
+ /*******************************
+ * Load this module.
+ * Returns:
+ * true for errors, false for success
+ */
+ bool load(Scope* sc)
+ {
+ //printf("Import::load('%s') %p\n", toPrettyChars(), this);
+ // See if existing module
+ const errors = global.errors;
+ DsymbolTable dst = Package.resolve(packages, null, &pkg);
+ version (none)
+ {
+ if (pkg && pkg.isModule())
+ {
+ .error(loc, "can only import from a module, not from a member of module `%s`. Did you mean `import %s : %s`?", pkg.toChars(), pkg.toPrettyChars(), id.toChars());
+ mod = pkg.isModule(); // Error recovery - treat as import of that module
+ return true;
+ }
+ }
+ Dsymbol s = dst.lookup(id);
+ if (s)
+ {
+ if (s.isModule())
+ mod = cast(Module)s;
+ else
+ {
+ if (s.isAliasDeclaration())
+ {
+ .error(loc, "%s `%s` conflicts with `%s`", s.kind(), s.toPrettyChars(), id.toChars());
+ }
+ else if (Package p = s.isPackage())
+ {
+ if (p.isPkgMod == PKG.unknown)
+ {
+ uint preverrors = global.errors;
+ mod = Module.load(loc, packages, id);
+ if (!mod)
+ p.isPkgMod = PKG.package_;
+ else
+ {
+ // mod is a package.d, or a normal module which conflicts with the package name.
+ if (mod.isPackageFile)
+ mod.tag = p.tag; // reuse the same package tag
+ else
+ {
+ // show error if Module.load does not
+ if (preverrors == global.errors)
+ .error(loc, "%s `%s` from file %s conflicts with %s `%s`", mod.kind(), mod.toPrettyChars(), mod.srcfile.toChars, p.kind(), p.toPrettyChars());
+ return true;
+ }
+ }
+ }
+ else
+ {
+ mod = p.isPackageMod();
+ }
+ if (!mod)
+ {
+ .error(loc, "can only import from a module, not from package `%s.%s`", p.toPrettyChars(), id.toChars());
+ }
+ }
+ else if (pkg)
+ {
+ .error(loc, "can only import from a module, not from package `%s.%s`", pkg.toPrettyChars(), id.toChars());
+ }
+ else
+ {
+ .error(loc, "can only import from a module, not from package `%s`", id.toChars());
+ }
+ }
+ }
+ if (!mod)
+ {
+ // Load module
+ mod = Module.load(loc, packages, id);
+ if (mod)
+ {
+ // id may be different from mod.ident, if so then insert alias
+ dst.insert(id, mod);
+ }
+ }
+ if (mod && !mod.importedFrom)
+ mod.importedFrom = sc ? sc._module.importedFrom : Module.rootModule;
+ if (!pkg)
+ {
+ if (mod && mod.isPackageFile)
+ {
+ // one level depth package.d file (import pkg; ./pkg/package.d)
+ // it's necessary to use the wrapping Package already created
+ pkg = mod.pkg;
+ }
+ else
+ pkg = mod;
+ }
+ //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg);
+ return global.errors != errors;
+ }
+
+ override void importAll(Scope* sc)
+ {
+ if (mod) return; // Already done
+ load(sc);
+ if (!mod) return; // Failed
+
+ if (sc.stc & STC.static_)
+ isstatic = true;
+ mod.importAll(null);
+ mod.checkImportDeprecation(loc, sc);
+ if (sc.explicitVisibility)
+ visibility = sc.visibility;
+ if (!isstatic && !aliasId && !names.dim)
+ sc.scopesym.importScope(mod, visibility);
+ // Enable access to pkgs/mod as soon as posible, because compiler
+ // can traverse them before the import gets semantic (Issue: 21501)
+ if (!aliasId && !names.dim)
+ addPackageAccess(sc.scopesym);
+ }
+
+ /*******************************
+ * Mark the imported packages as accessible from the current
+ * scope. This access check is necessary when using FQN b/c
+ * we're using a single global package tree.
+ * https://issues.dlang.org/show_bug.cgi?id=313
+ */
+ extern (D) void addPackageAccess(ScopeDsymbol scopesym)
+ {
+ //printf("Import::addPackageAccess('%s') %p\n", toPrettyChars(), this);
+ if (packages.length > 0)
+ {
+ // import a.b.c.d;
+ auto p = pkg; // a
+ scopesym.addAccessiblePackage(p, visibility);
+ foreach (id; packages[1 .. $]) // [b, c]
+ {
+ p = cast(Package) p.symtab.lookup(id);
+ // https://issues.dlang.org/show_bug.cgi?id=17991
+ // An import of truly empty file/package can happen
+ // https://issues.dlang.org/show_bug.cgi?id=20151
+ // Package in the path conflicts with a module name
+ if (p is null)
+ break;
+ scopesym.addAccessiblePackage(p, visibility);
+ }
+ }
+ scopesym.addAccessiblePackage(mod, visibility); // d
+ }
+
+ override Dsymbol toAlias()
+ {
+ if (aliasId)
+ return mod;
+ return this;
+ }
+
+ /*****************************
+ * Add import to sd's symbol table.
+ */
+ override void addMember(Scope* sc, ScopeDsymbol sd)
+ {
+ //printf("Import.addMember(this=%s, sd=%s, sc=%p)\n", toChars(), sd.toChars(), sc);
+ if (names.dim == 0)
+ return Dsymbol.addMember(sc, sd);
+ if (aliasId)
+ Dsymbol.addMember(sc, sd);
+ /* Instead of adding the import to sd's symbol table,
+ * add each of the alias=name pairs
+ */
+ for (size_t i = 0; i < names.dim; i++)
+ {
+ Identifier name = names[i];
+ Identifier _alias = aliases[i];
+ if (!_alias)
+ _alias = name;
+ auto tname = new TypeIdentifier(loc, name);
+ auto ad = new AliasDeclaration(loc, _alias, tname);
+ ad._import = this;
+ ad.addMember(sc, sd);
+ aliasdecls.push(ad);
+ }
+ }
+
+ override void setScope(Scope* sc)
+ {
+ Dsymbol.setScope(sc);
+ if (aliasdecls.dim)
+ {
+ if (!mod)
+ importAll(sc);
+
+ sc = sc.push(mod);
+ sc.visibility = visibility;
+ foreach (ad; aliasdecls)
+ ad.setScope(sc);
+ sc = sc.pop();
+ }
+ }
+
+ override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ //printf("%s.Import.search(ident = '%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
+ if (!pkg)
+ {
+ load(null);
+ mod.importAll(null);
+ mod.dsymbolSemantic(null);
+ }
+ // Forward it to the package/module
+ return pkg.search(loc, ident, flags);
+ }
+
+ override bool overloadInsert(Dsymbol s)
+ {
+ /* Allow multiple imports with the same package base, but disallow
+ * alias collisions
+ * https://issues.dlang.org/show_bug.cgi?id=5412
+ */
+ assert(ident && ident == s.ident);
+ Import imp;
+ if (!aliasId && (imp = s.isImport()) !is null && !imp.aliasId)
+ return true;
+ else
+ return false;
+ }
+
+ override inout(Import) isImport() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
diff --git a/gcc/d/dmd/dinterpret.c b/gcc/d/dmd/dinterpret.c
deleted file mode 100644
index ab9d88c660c..00000000000
--- a/gcc/d/dmd/dinterpret.c
+++ /dev/null
@@ -1,7017 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/interpret.c
- */
-
-#include "root/dsystem.h" // mem{cpy|set}()
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "statement.h"
-#include "expression.h"
-#include "cond.h"
-#include "init.h"
-#include "staticassert.h"
-#include "mtype.h"
-#include "scope.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "id.h"
-#include "utf.h"
-#include "attrib.h" // for AttribDeclaration
-
-#include "template.h"
-#include "ctfe.h"
-
-/* Interpreter: what form of return value expression is required?
- */
-enum CtfeGoal
-{
- ctfeNeedRvalue, // Must return an Rvalue (== CTFE value)
- ctfeNeedLvalue, // Must return an Lvalue (== CTFE reference)
- ctfeNeedNothing // The return value is not required
-};
-
-bool walkPostorder(Expression *e, StoppableVisitor *v);
-Expression *interpret(Statement *s, InterState *istate);
-Expression *interpret(Expression *e, InterState *istate, CtfeGoal goal = ctfeNeedRvalue);
-
-static Expression *interpret(UnionExp *pue, Expression *e, InterState *istate, CtfeGoal goal = ctfeNeedRvalue);
-static Expression *interpret(UnionExp *pue, Statement *s, InterState *istate);
-
-#define SHOWPERFORMANCE 0
-
-// Maximum allowable recursive function calls in CTFE
-#define CTFE_RECURSION_LIMIT 1000
-
-/**
- The values of all CTFE variables
-*/
-struct CtfeStack
-{
-private:
- /* The stack. Every declaration we encounter is pushed here,
- together with the VarDeclaration, and the previous
- stack address of that variable, so that we can restore it
- when we leave the stack frame.
- Note that when a function is forward referenced, the interpreter must
- run semantic3, and that may start CTFE again with a NULL istate. Thus
- the stack might not be empty when CTFE begins.
-
- Ctfe Stack addresses are just 0-based integers, but we save
- them as 'void *' because Array can only do pointers.
- */
- Expressions values; // values on the stack
- VarDeclarations vars; // corresponding variables
- Array<void *> savedId; // id of the previous state of that var
-
- Array<void *> frames; // all previous frame pointers
- Expressions savedThis; // all previous values of localThis
-
- /* Global constants get saved here after evaluation, so we never
- * have to redo them. This saves a lot of time and memory.
- */
- Expressions globalValues; // values of global constants
-
- size_t framepointer; // current frame pointer
- size_t maxStackPointer; // most stack we've ever used
- Expression *localThis; // value of 'this', or NULL if none
-public:
- CtfeStack();
-
- size_t stackPointer();
-
- // The current value of 'this', or NULL if none
- Expression *getThis();
-
- // Largest number of stack positions we've used
- size_t maxStackUsage();
- // Start a new stack frame, using the provided 'this'.
- void startFrame(Expression *thisexp);
- void endFrame();
- bool isInCurrentFrame(VarDeclaration *v);
- Expression *getValue(VarDeclaration *v);
- void setValue(VarDeclaration *v, Expression *e);
- void push(VarDeclaration *v);
- void pop(VarDeclaration *v);
- void popAll(size_t stackpointer);
- void saveGlobalConstant(VarDeclaration *v, Expression *e);
-};
-
-struct InterState
-{
- InterState *caller; // calling function's InterState
- FuncDeclaration *fd; // function being interpreted
- Statement *start; // if !=NULL, start execution at this statement
- /* target of CTFEExp result; also
- * target of labelled CTFEExp or
- * CTFEExp. (NULL if no label).
- */
- Statement *gotoTarget;
-
- InterState();
-};
-
-/************** CtfeStack ********************************************/
-
-CtfeStack ctfeStack;
-
-CtfeStack::CtfeStack() : framepointer(0), maxStackPointer(0)
-{
-}
-
-size_t CtfeStack::stackPointer()
-{
- return values.length;
-}
-
-Expression *CtfeStack::getThis()
-{
- return localThis;
-}
-
-// Largest number of stack positions we've used
-size_t CtfeStack::maxStackUsage()
-{
- return maxStackPointer;
-}
-
-void CtfeStack::startFrame(Expression *thisexp)
-{
- frames.push((void *)(size_t)(framepointer));
- savedThis.push(localThis);
- framepointer = stackPointer();
- localThis = thisexp;
-}
-
-void CtfeStack::endFrame()
-{
- size_t oldframe = (size_t)(frames[frames.length-1]);
- localThis = savedThis[savedThis.length-1];
- popAll(framepointer);
- framepointer = oldframe;
- frames.setDim(frames.length - 1);
- savedThis.setDim(savedThis.length -1);
-}
-
-bool CtfeStack::isInCurrentFrame(VarDeclaration *v)
-{
- if (v->isDataseg() && !v->isCTFE())
- return false; // It's a global
- return v->ctfeAdrOnStack >= (int)framepointer;
-}
-
-Expression *CtfeStack::getValue(VarDeclaration *v)
-{
- if ((v->isDataseg() || v->storage_class & STCmanifest) && !v->isCTFE())
- {
- assert(v->ctfeAdrOnStack >= 0 &&
- v->ctfeAdrOnStack < (int)globalValues.length);
- return globalValues[v->ctfeAdrOnStack];
- }
- assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < (int)stackPointer());
- return values[v->ctfeAdrOnStack];
-}
-
-void CtfeStack::setValue(VarDeclaration *v, Expression *e)
-{
- assert(!v->isDataseg() || v->isCTFE());
- assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < (int)stackPointer());
- values[v->ctfeAdrOnStack] = e;
-}
-
-void CtfeStack::push(VarDeclaration *v)
-{
- assert(!v->isDataseg() || v->isCTFE());
- if (v->ctfeAdrOnStack != -1 &&
- v->ctfeAdrOnStack >= (int)framepointer)
- {
- // Already exists in this frame, reuse it.
- values[v->ctfeAdrOnStack] = NULL;
- return;
- }
- savedId.push((void *)(size_t)(v->ctfeAdrOnStack));
- v->ctfeAdrOnStack = (int)values.length;
- vars.push(v);
- values.push(NULL);
-}
-
-void CtfeStack::pop(VarDeclaration *v)
-{
- assert(!v->isDataseg() || v->isCTFE());
- assert(!(v->storage_class & (STCref | STCout)));
- int oldid = v->ctfeAdrOnStack;
- v->ctfeAdrOnStack = (int)(size_t)(savedId[oldid]);
- if (v->ctfeAdrOnStack == (int)values.length - 1)
- {
- values.pop();
- vars.pop();
- savedId.pop();
- }
-}
-
-void CtfeStack::popAll(size_t stackpointer)
-{
- if (stackPointer() > maxStackPointer)
- maxStackPointer = stackPointer();
- assert(values.length >= stackpointer);
- for (size_t i = stackpointer; i < values.length; ++i)
- {
- VarDeclaration *v = vars[i];
- v->ctfeAdrOnStack = (int)(size_t)(savedId[i]);
- }
- values.setDim(stackpointer);
- vars.setDim(stackpointer);
- savedId.setDim(stackpointer);
-}
-
-void CtfeStack::saveGlobalConstant(VarDeclaration *v, Expression *e)
-{
- assert(v->_init && (v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) && !v->isCTFE());
- v->ctfeAdrOnStack = (int)globalValues.length;
- globalValues.push(e);
-}
-
-/************** InterState ********************************************/
-
-InterState::InterState()
-{
- memset(this, 0, sizeof(InterState));
-}
-
-/************** CtfeStatus ********************************************/
-
-int CtfeStatus::callDepth = 0;
-int CtfeStatus::stackTraceCallsToSuppress = 0;
-int CtfeStatus::maxCallDepth = 0;
-int CtfeStatus::numArrayAllocs = 0;
-int CtfeStatus::numAssignments = 0;
-
-// CTFE diagnostic information
-void printCtfePerformanceStats()
-{
-#if SHOWPERFORMANCE
- printf(" ---- CTFE Performance ----\n");
- printf("max call depth = %d\tmax stack = %d\n", CtfeStatus::maxCallDepth, ctfeStack.maxStackUsage());
- printf("array allocs = %d\tassignments = %d\n\n", CtfeStatus::numArrayAllocs, CtfeStatus::numAssignments);
-#endif
-}
-
-static Expression *evaluateIfBuiltin(UnionExp *pue, InterState *istate, Loc loc,
- FuncDeclaration *fd, Expressions *arguments, Expression *pthis);
-static Expression *evaluatePostblit(InterState *istate, Expression *e);
-static Expression *evaluateDtor(InterState *istate, Expression *e);
-
-static bool isEntirelyVoid(Expressions* elems);
-static Expression *scrubArray(Loc loc, Expressions *elems, bool structlit = false);
-static Expression *scrubStructLiteral(Loc loc, StructLiteralExp *sle);
-static Expression *scrubReturnValue(Loc loc, Expression *e);
-static Expression *scrubArrayCache(Expressions *elems);
-static Expression *scrubStructLiteralCache(StructLiteralExp *sle);
-static Expression *scrubCacheValue(Expression *e);
-
-
-/*************************************
- * CTFE-object code for a single function
- *
- * Currently only counts the number of local variables in the function
- */
-struct CompiledCtfeFunction
-{
- FuncDeclaration *func; // Function being compiled, NULL if global scope
- int numVars; // Number of variables declared in this function
- Loc callingloc;
-
- CompiledCtfeFunction(FuncDeclaration *f)
- {
- func = f;
- numVars = 0;
- }
-
- void onDeclaration(VarDeclaration *)
- {
- //printf("%s CTFE declare %s\n", v->loc.toChars(), v->toChars());
- ++numVars;
- }
-
- void onExpression(Expression *e)
- {
- class VarWalker : public StoppableVisitor
- {
- public:
- CompiledCtfeFunction *ccf;
-
- VarWalker(CompiledCtfeFunction *ccf)
- : ccf(ccf)
- {
- }
-
- void visit(Expression *)
- {
- }
-
- void visit(ErrorExp *e)
- {
- // Currently there's a front-end bug: silent errors
- // can occur inside delegate literals inside is(typeof()).
- // Suppress the check in this case.
- if (global.gag && ccf->func)
- {
- stop = 1;
- return;
- }
-
- ::error(e->loc, "CTFE internal error: ErrorExp in %s\n", ccf->func ? ccf->func->loc.toChars() : ccf->callingloc.toChars());
- assert(0);
- }
-
- void visit(DeclarationExp *e)
- {
- VarDeclaration *v = e->declaration->isVarDeclaration();
- if (!v)
- return;
- TupleDeclaration *td = v->toAlias()->isTupleDeclaration();
- if (td)
- {
- if (!td->objects)
- return;
- for (size_t i= 0; i < td->objects->length; ++i)
- {
- RootObject *o = td->objects->tdata()[i];
- Expression *ex = isExpression(o);
- DsymbolExp *s = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL;
- assert(s);
- VarDeclaration *v2 = s->s->isVarDeclaration();
- assert(v2);
- if (!v2->isDataseg() || v2->isCTFE())
- ccf->onDeclaration(v2);
- }
- }
- else if (!(v->isDataseg() || v->storage_class & STCmanifest) || v->isCTFE())
- ccf->onDeclaration(v);
- Dsymbol *s = v->toAlias();
- if (s == v && !v->isStatic() && v->_init)
- {
- ExpInitializer *ie = v->_init->isExpInitializer();
- if (ie)
- ccf->onExpression(ie->exp);
- }
- }
-
- void visit(IndexExp *e)
- {
- if (e->lengthVar)
- ccf->onDeclaration(e->lengthVar);
- }
-
- void visit(SliceExp *e)
- {
- if (e->lengthVar)
- ccf->onDeclaration(e->lengthVar);
- }
- };
-
- VarWalker v(this);
- walkPostorder(e, &v);
- }
-};
-
-class CtfeCompiler : public Visitor
-{
-public:
- CompiledCtfeFunction *ccf;
-
- CtfeCompiler(CompiledCtfeFunction *ccf)
- : ccf(ccf)
- {
- }
-
- void visit(Statement *)
- {
- assert(0);
- }
-
- void visit(ExpStatement *s)
- {
- if (s->exp)
- ccf->onExpression(s->exp);
- }
-
- void visit(CompoundStatement *s)
- {
- for (size_t i = 0; i < s->statements->length; i++)
- {
- Statement *sx = (*s->statements)[i];
- if (sx)
- ctfeCompile(sx);
- }
- }
-
- void visit(UnrolledLoopStatement *s)
- {
- for (size_t i = 0; i < s->statements->length; i++)
- {
- Statement *sx = (*s->statements)[i];
- if (sx)
- ctfeCompile(sx);
- }
- }
-
- void visit(IfStatement *s)
- {
- ccf->onExpression(s->condition);
- if (s->ifbody)
- ctfeCompile(s->ifbody);
- if (s->elsebody)
- ctfeCompile(s->elsebody);
- }
-
- void visit(ScopeStatement *s)
- {
- if (s->statement)
- ctfeCompile(s->statement);
- }
-
- void visit(ScopeGuardStatement *)
- {
- // rewritten to try/catch/finally
- assert(0);
- }
-
- void visit(DoStatement *s)
- {
- ccf->onExpression(s->condition);
- if (s->_body)
- ctfeCompile(s->_body);
- }
-
- void visit(WhileStatement *)
- {
- // rewritten to ForStatement
- assert(0);
- }
-
- void visit(ForStatement *s)
- {
- if (s->_init)
- ctfeCompile(s->_init);
- if (s->condition)
- ccf->onExpression(s->condition);
- if (s->increment)
- ccf->onExpression(s->increment);
- if (s->_body)
- ctfeCompile(s->_body);
- }
-
- void visit(ForeachStatement *)
- {
- // rewritten for ForStatement
- assert(0);
- }
-
- void visit(SwitchStatement *s)
- {
- ccf->onExpression(s->condition);
- // Note that the body contains the the Case and Default
- // statements, so we only need to compile the expressions
- for (size_t i = 0; i < s->cases->length; i++)
- {
- ccf->onExpression((*s->cases)[i]->exp);
- }
- if (s->_body)
- ctfeCompile(s->_body);
- }
-
- void visit(CaseStatement *s)
- {
- if (s->statement)
- ctfeCompile(s->statement);
- }
-
- void visit(DefaultStatement *s)
- {
- if (s->statement)
- ctfeCompile(s->statement);
- }
-
- void visit(GotoDefaultStatement *)
- {
- }
-
- void visit(GotoCaseStatement *)
- {
- }
-
- void visit(SwitchErrorStatement *)
- {
- }
-
- void visit(ReturnStatement *s)
- {
- if (s->exp)
- ccf->onExpression(s->exp);
- }
-
- void visit(BreakStatement *)
- {
- }
-
- void visit(ContinueStatement *)
- {
- }
-
- void visit(WithStatement *s)
- {
- // If it is with(Enum) {...}, just execute the body.
- if (s->exp->op == TOKscope || s->exp->op == TOKtype)
- {
- }
- else
- {
- ccf->onDeclaration(s->wthis);
- ccf->onExpression(s->exp);
- }
- if (s->_body)
- ctfeCompile(s->_body);
- }
-
- void visit(TryCatchStatement *s)
- {
- if (s->_body)
- ctfeCompile(s->_body);
- for (size_t i = 0; i < s->catches->length; i++)
- {
- Catch *ca = (*s->catches)[i];
- if (ca->var)
- ccf->onDeclaration(ca->var);
- if (ca->handler)
- ctfeCompile(ca->handler);
- }
- }
-
- void visit(TryFinallyStatement *s)
- {
- if (s->_body)
- ctfeCompile(s->_body);
- if (s->finalbody)
- ctfeCompile(s->finalbody);
- }
-
- void visit(ThrowStatement *s)
- {
- ccf->onExpression(s->exp);
- }
-
- void visit(GotoStatement *)
- {
- }
-
- void visit(LabelStatement *s)
- {
- if (s->statement)
- ctfeCompile(s->statement);
- }
-
- void visit(ImportStatement *)
- {
- // Contains no variables or executable code
- }
-
- void visit(ForeachRangeStatement *)
- {
- // rewritten for ForStatement
- assert(0);
- }
-
- void visit(AsmStatement *)
- {
- // we can't compile asm statements
- }
-
- void ctfeCompile(Statement *s)
- {
- s->accept(this);
- }
-};
-
-/*************************************
- * Compile this function for CTFE.
- * At present, this merely allocates variables.
- */
-void ctfeCompile(FuncDeclaration *fd)
-{
- assert(!fd->ctfeCode);
- assert(!fd->semantic3Errors);
- assert(fd->semanticRun == PASSsemantic3done);
-
- fd->ctfeCode = new CompiledCtfeFunction(fd);
- if (fd->parameters)
- {
- Type *tb = fd->type->toBasetype();
- assert(tb->ty == Tfunction);
- for (size_t i = 0; i < fd->parameters->length; i++)
- {
- VarDeclaration *v = (*fd->parameters)[i];
- fd->ctfeCode->onDeclaration(v);
- }
- }
- if (fd->vresult)
- fd->ctfeCode->onDeclaration(fd->vresult);
- CtfeCompiler v(fd->ctfeCode);
- v.ctfeCompile(fd->fbody);
-}
-
-/*************************************
- * Entry point for CTFE.
- * A compile-time result is required. Give an error if not possible.
- *
- * `e` must be semantically valid expression. In other words, it should not
- * contain any `ErrorExp`s in it. But, CTFE interpretation will cross over
- * functions and may invoke a function that contains `ErrorStatement` in its body.
- * If that, the "CTFE failed because of previous errors" error is raised.
- */
-Expression *ctfeInterpret(Expression *e)
-{
- switch (e->op)
- {
- case TOKint64:
- case TOKfloat64:
- case TOKcomplex80:
- case TOKnull:
- case TOKvoid:
- case TOKstring:
- case TOKthis:
- case TOKsuper:
- case TOKtype:
- case TOKtypeid:
- case TOKtemplate: // non-eponymous template/instance
- case TOKscope: // ditto
- case TOKdottd: // ditto, e.e1 doesn't matter here
- case TOKdot: // ditto
- if (e->type->ty == Terror)
- return new ErrorExp();
- /* fall through */
-
- case TOKerror:
- return e;
-
- default:
- break;
- }
-
- assert(e->type); // Bugzilla 14642
- //assert(e->type->ty != Terror); // FIXME
- if (e->type->ty == Terror)
- return new ErrorExp();
-
- // This code is outside a function, but still needs to be compiled
- // (there are compiler-generated temporary variables such as __dollar).
- // However, this will only be run once and can then be discarded.
- CompiledCtfeFunction ctfeCodeGlobal(NULL);
- ctfeCodeGlobal.callingloc = e->loc;
- ctfeCodeGlobal.onExpression(e);
-
- Expression *result = interpret(e, NULL);
- if (!CTFEExp::isCantExp(result))
- result = scrubReturnValue(e->loc, result);
- if (CTFEExp::isCantExp(result))
- result = new ErrorExp();
- return result;
-}
-
-/* Run CTFE on the expression, but allow the expression to be a TypeExp
- * or a tuple containing a TypeExp. (This is required by pragma(msg)).
- */
-Expression *ctfeInterpretForPragmaMsg(Expression *e)
-{
- if (e->op == TOKerror || e->op == TOKtype)
- return e;
-
- // It's also OK for it to be a function declaration (happens only with
- // __traits(getOverloads))
- if (e->op == TOKvar && ((VarExp *)e)->var->isFuncDeclaration())
- {
- return e;
- }
-
- if (e->op != TOKtuple)
- return e->ctfeInterpret();
-
- // Tuples need to be treated seperately, since they are
- // allowed to contain a TypeExp in this case.
-
- TupleExp *tup = (TupleExp *)e;
- Expressions *expsx = NULL;
- for (size_t i = 0; i < tup->exps->length; ++i)
- {
- Expression *g = (*tup->exps)[i];
- Expression *h = g;
- h = ctfeInterpretForPragmaMsg(g);
- if (h != g)
- {
- if (!expsx)
- {
- expsx = new Expressions();
- expsx->setDim(tup->exps->length);
- for (size_t j = 0; j < tup->exps->length; j++)
- (*expsx)[j] = (*tup->exps)[j];
- }
- (*expsx)[i] = h;
- }
- }
- if (expsx)
- {
- TupleExp *te = new TupleExp(e->loc, expsx);
- expandTuples(te->exps);
- te->type = new TypeTuple(te->exps);
- return te;
- }
- return e;
-}
-
-/*************************************
- * Attempt to interpret a function given the arguments.
- * Input:
- * pue storage for result
- * fd function being called
- * istate state for calling function (NULL if none)
- * arguments function arguments
- * thisarg 'this', if a needThis() function, NULL if not.
- *
- * Return result expression if successful, TOKcantexp if not,
- * or CTFEExp if function returned void.
- */
-
-static Expression *interpretFunction(UnionExp *pue, FuncDeclaration *fd, InterState *istate, Expressions *arguments, Expression *thisarg)
-{
- assert(pue);
- if (fd->semanticRun == PASSsemantic3)
- {
- fd->error("circular dependency. Functions cannot be interpreted while being compiled");
- return CTFEExp::cantexp;
- }
- if (!fd->functionSemantic3())
- return CTFEExp::cantexp;
- if (fd->semanticRun < PASSsemantic3done)
- return CTFEExp::cantexp;
-
- // CTFE-compile the function
- if (!fd->ctfeCode)
- ctfeCompile(fd);
-
- Type *tb = fd->type->toBasetype();
- assert(tb->ty == Tfunction);
- TypeFunction *tf = (TypeFunction *)tb;
- if (tf->parameterList.varargs != VARARGnone && arguments &&
- ((fd->parameters && arguments->length != fd->parameters->length) || (!fd->parameters && arguments->length)))
- {
- fd->error("C-style variadic functions are not yet implemented in CTFE");
- return CTFEExp::cantexp;
- }
-
- // Nested functions always inherit the 'this' pointer from the parent,
- // except for delegates. (Note that the 'this' pointer may be null).
- // Func literals report isNested() even if they are in global scope,
- // so we need to check that the parent is a function.
- if (fd->isNested() && fd->toParent2()->isFuncDeclaration() && !thisarg && istate)
- thisarg = ctfeStack.getThis();
-
- if (fd->needThis() && !thisarg)
- {
- // error, no this. Prevent segfault.
- // Here should be unreachable by the strict 'this' check in front-end.
- fd->error("need `this` to access member %s", fd->toChars());
- return CTFEExp::cantexp;
- }
-
- // Place to hold all the arguments to the function while
- // we are evaluating them.
- Expressions eargs;
- size_t dim = arguments ? arguments->length : 0;
- assert((fd->parameters ? fd->parameters->length : 0) == dim);
-
- /* Evaluate all the arguments to the function,
- * store the results in eargs[]
- */
- eargs.setDim(dim);
- for (size_t i = 0; i < dim; i++)
- {
- Expression *earg = (*arguments)[i];
- Parameter *fparam = tf->parameterList[i];
-
- if (fparam->storageClass & (STCout | STCref))
- {
- if (!istate && (fparam->storageClass & STCout))
- {
- // initializing an out parameter involves writing to it.
- earg->error("global %s cannot be passed as an `out` parameter at compile time", earg->toChars());
- return CTFEExp::cantexp;
- }
- // Convert all reference arguments into lvalue references
- earg = interpret(earg, istate, ctfeNeedLvalue);
- if (CTFEExp::isCantExp(earg))
- return earg;
- }
- else if (fparam->storageClass & STClazy)
- {
- }
- else
- {
- /* Value parameters
- */
- Type *ta = fparam->type->toBasetype();
- if (ta->ty == Tsarray && earg->op == TOKaddress)
- {
- /* Static arrays are passed by a simple pointer.
- * Skip past this to get at the actual arg.
- */
- earg = ((AddrExp *)earg)->e1;
- }
- earg = interpret(earg, istate);
- if (CTFEExp::isCantExp(earg))
- return earg;
- /* Struct literals are passed by value, but we don't need to
- * copy them if they are passed as const
- */
- if (earg->op == TOKstructliteral && !(fparam->storageClass & (STCconst | STCimmutable)))
- earg = copyLiteral(earg).copy();
- }
- if (earg->op == TOKthrownexception)
- {
- if (istate)
- return earg;
- ((ThrownExceptionExp *)earg)->generateUncaughtError();
- return CTFEExp::cantexp;
- }
- eargs[i] = earg;
- }
-
- // Now that we've evaluated all the arguments, we can start the frame
- // (this is the moment when the 'call' actually takes place).
- InterState istatex;
- istatex.caller = istate;
- istatex.fd = fd;
- ctfeStack.startFrame(thisarg);
- if (fd->vthis && thisarg)
- {
- ctfeStack.push(fd->vthis);
- setValue(fd->vthis, thisarg);
- }
-
- for (size_t i = 0; i < dim; i++)
- {
- Expression *earg = eargs[i];
- Parameter *fparam = tf->parameterList[i];
- VarDeclaration *v = (*fd->parameters)[i];
- ctfeStack.push(v);
-
- if ((fparam->storageClass & (STCout | STCref)) &&
- earg->op == TOKvar && ((VarExp *)earg)->var->toParent2() == fd)
- {
- VarDeclaration *vx = ((VarExp *)earg)->var->isVarDeclaration();
- if (!vx)
- {
- fd->error("cannot interpret %s as a ref parameter", earg->toChars());
- return CTFEExp::cantexp;
- }
-
- /* vx is a variable that is declared in fd.
- * It means that fd is recursively called. e.g.
- *
- * void fd(int n, ref int v = dummy) {
- * int vx;
- * if (n == 1) fd(2, vx);
- * }
- * fd(1);
- *
- * The old value of vx on the stack in fd(1)
- * should be saved at the start of fd(2, vx) call.
- */
- int oldadr = vx->ctfeAdrOnStack;
-
- ctfeStack.push(vx);
- assert(!hasValue(vx)); // vx is made uninitialized
-
- // Bugzilla 14299: v->ctfeAdrOnStack should be saved already
- // in the stack before the overwrite.
- v->ctfeAdrOnStack = oldadr;
- assert(hasValue(v)); // ref parameter v should refer existing value.
- }
- else
- {
- // Value parameters and non-trivial references
- setValueWithoutChecking(v, earg);
- }
- }
-
- if (fd->vresult)
- ctfeStack.push(fd->vresult);
-
- // Enter the function
- ++CtfeStatus::callDepth;
- if (CtfeStatus::callDepth > CtfeStatus::maxCallDepth)
- CtfeStatus::maxCallDepth = CtfeStatus::callDepth;
-
- Expression *e = NULL;
- while (1)
- {
- if (CtfeStatus::callDepth > CTFE_RECURSION_LIMIT)
- {
- // This is a compiler error. It must not be suppressed.
- global.gag = 0;
- fd->error("CTFE recursion limit exceeded");
- e = CTFEExp::cantexp;
- break;
- }
- e = interpret(pue, fd->fbody, &istatex);
-
- if (istatex.start)
- {
- fd->error("CTFE internal error: failed to resume at statement %s", istatex.start->toChars());
- return CTFEExp::cantexp;
- }
-
- /* This is how we deal with a recursive statement AST
- * that has arbitrary goto statements in it.
- * Bubble up a 'result' which is the target of the goto
- * statement, then go recursively down the AST looking
- * for that statement, then execute starting there.
- */
- if (CTFEExp::isGotoExp(e))
- {
- istatex.start = istatex.gotoTarget; // set starting statement
- istatex.gotoTarget = NULL;
- }
- else
- {
- assert(!e || (e->op != TOKcontinue && e->op != TOKbreak));
- break;
- }
- }
- // If fell off the end of a void function, return void
- if (!e && tf->next->ty == Tvoid)
- e = CTFEExp::voidexp;
- if (tf->isref && e->op == TOKvar && ((VarExp *)e)->var == fd->vthis)
- e = thisarg;
- assert(e != NULL);
-
- // Leave the function
- --CtfeStatus::callDepth;
-
- ctfeStack.endFrame();
-
- // If it generated an uncaught exception, report error.
- if (!istate && e->op == TOKthrownexception)
- {
- if (e == pue->exp())
- e = pue->copy();
- ((ThrownExceptionExp *)e)->generateUncaughtError();
- e = CTFEExp::cantexp;
- }
-
- return e;
-}
-
-class Interpreter : public Visitor
-{
-public:
- InterState *istate;
- CtfeGoal goal;
-
- Expression *result;
- UnionExp *pue; // storage for `result`
-
- Interpreter(UnionExp *pue, InterState *istate, CtfeGoal goal)
- : istate(istate), goal(goal), pue(pue)
- {
- result = NULL;
- }
-
- // If e is TOKthrowexception or TOKcantexp,
- // set it to 'result' and returns true.
- bool exceptionOrCant(Expression *e)
- {
- if (exceptionOrCantInterpret(e))
- {
- // Make sure e is not pointing to a stack temporary
- result = (e->op == TOKcantexp) ? CTFEExp::cantexp : e;
- return true;
- }
- return false;
- }
-
- static Expressions *copyArrayOnWrite(Expressions *exps, Expressions *original)
- {
- if (exps == original)
- {
- if (!original)
- exps = new Expressions();
- else
- exps = original->copy();
- ++CtfeStatus::numArrayAllocs;
- }
- return exps;
- }
-
- /******************************** Statement ***************************/
-
- void visit(Statement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- s->error("statement %s cannot be interpreted at compile time", s->toChars());
- result = CTFEExp::cantexp;
- }
-
- void visit(ExpStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- Expression *e = interpret(pue, s->exp, istate, ctfeNeedNothing);
- if (exceptionOrCant(e))
- return;
- }
-
- void visit(CompoundStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
-
- const size_t dim = s->statements ? s->statements->length : 0;
- for (size_t i = 0; i < dim; i++)
- {
- Statement *sx = (*s->statements)[i];
- result = interpret(pue, sx, istate);
- if (result)
- break;
- }
- }
-
- void visit(UnrolledLoopStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
-
- const size_t dim = s->statements ? s->statements->length : 0;
- for (size_t i = 0; i < dim; i++)
- {
- Statement *sx = (*s->statements)[i];
- Expression *e = interpret(pue, sx, istate);
- if (!e) // suceeds to interpret, or goto target
- continue; // was not fonnd when istate->start != NULL
- if (exceptionOrCant(e))
- return;
- if (e->op == TOKbreak)
- {
- if (istate->gotoTarget && istate->gotoTarget != s)
- {
- result = e; // break at a higher level
- return;
- }
- istate->gotoTarget = NULL;
- result = NULL;
- return;
- }
- if (e->op == TOKcontinue)
- {
- if (istate->gotoTarget && istate->gotoTarget != s)
- {
- result = e; // continue at a higher level
- return;
- }
- istate->gotoTarget = NULL;
- continue;
- }
-
- // expression from return statement, or thrown exception
- result = e;
- break;
- }
- }
-
- void visit(IfStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
- if (istate->start)
- {
- Expression *e = NULL;
- e = interpret(s->ifbody, istate);
- if (!e && istate->start)
- e = interpret(s->elsebody, istate);
- result = e;
- return;
- }
-
- UnionExp ue;
- Expression *e = interpret(&ue, s->condition, istate);
- assert(e);
- if (exceptionOrCant(e))
- return;
-
- if (isTrueBool(e))
- result = interpret(pue, s->ifbody, istate);
- else if (e->isBool(false))
- result = interpret(pue, s->elsebody, istate);
- else
- {
- // no error, or assert(0)?
- result = CTFEExp::cantexp;
- }
- }
-
- void visit(ScopeStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
-
- result = interpret(pue, s->statement, istate);
- }
-
- /**
- Given an expression e which is about to be returned from the current
- function, generate an error if it contains pointers to local variables.
-
- Only checks expressions passed by value (pointers to local variables
- may already be stored in members of classes, arrays, or AAs which
- were passed as mutable function parameters).
- Returns:
- true if it is safe to return, false if an error was generated.
- */
-
- static bool stopPointersEscaping(Loc loc, Expression *e)
- {
- if (!e->type->hasPointers())
- return true;
- if (isPointer(e->type))
- {
- Expression *x = e;
- if (e->op == TOKaddress)
- x = ((AddrExp *)e)->e1;
- VarDeclaration *v;
- while (x->op == TOKvar &&
- (v = ((VarExp *)x)->var->isVarDeclaration()) != NULL)
- {
- if (v->storage_class & STCref)
- {
- x = getValue(v);
- if (e->op == TOKaddress)
- ((AddrExp *)e)->e1 = x;
- continue;
- }
- if (ctfeStack.isInCurrentFrame(v))
- {
- error(loc, "returning a pointer to a local stack variable");
- return false;
- }
- else
- break;
- }
- // TODO: If it is a TOKdotvar or TOKindex, we should check that it is not
- // pointing to a local struct or static array.
- }
- if (e->op == TOKstructliteral)
- {
- StructLiteralExp *se = (StructLiteralExp *)e;
- return stopPointersEscapingFromArray(loc, se->elements);
- }
- if (e->op == TOKarrayliteral)
- {
- return stopPointersEscapingFromArray(loc, ((ArrayLiteralExp *)e)->elements);
- }
- if (e->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e;
- if (!stopPointersEscapingFromArray(loc, aae->keys))
- return false;
- return stopPointersEscapingFromArray(loc, aae->values);
- }
- return true;
- }
-
- // Check all members of an array for escaping local variables. Return false if error
- static bool stopPointersEscapingFromArray(Loc loc, Expressions *elems)
- {
- for (size_t i = 0; i < elems->length; i++)
- {
- Expression *m = (*elems)[i];
- if (!m)
- continue;
- if (!stopPointersEscaping(loc, m))
- return false;
- }
- return true;
- }
-
- void visit(ReturnStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- if (!s->exp)
- {
- result = CTFEExp::voidexp;
- return;
- }
-
- assert(istate && istate->fd && istate->fd->type && istate->fd->type->ty == Tfunction);
- TypeFunction *tf = (TypeFunction *)istate->fd->type;
-
- /* If the function returns a ref AND it's been called from an assignment,
- * we need to return an lvalue. Otherwise, just do an (rvalue) interpret.
- */
- if (tf->isref)
- {
- result = interpret(pue, s->exp, istate, ctfeNeedLvalue);
- return;
- }
- if (tf->next && tf->next->ty == Tdelegate && istate->fd->closureVars.length > 0)
- {
- // To support this, we need to copy all the closure vars
- // into the delegate literal.
- s->error("closures are not yet supported in CTFE");
- result = CTFEExp::cantexp;
- return;
- }
-
- // We need to treat pointers specially, because TOKsymoff can be used to
- // return a value OR a pointer
- Expression *e = interpret(pue, s->exp, istate);
- if (exceptionOrCant(e))
- return;
-
- // Disallow returning pointers to stack-allocated variables (bug 7876)
- if (!stopPointersEscaping(s->loc, e))
- {
- result = CTFEExp::cantexp;
- return;
- }
-
- if (needToCopyLiteral(e))
- e = copyLiteral(e).copy();
- result = e;
- }
-
- static Statement *findGotoTarget(InterState *istate, Identifier *ident)
- {
- Statement *target = NULL;
- if (ident)
- {
- LabelDsymbol *label = istate->fd->searchLabel(ident);
- assert(label && label->statement);
- LabelStatement *ls = label->statement;
- target = ls->gotoTarget ? ls->gotoTarget : ls->statement;
- }
- return target;
- }
-
- void visit(BreakStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- istate->gotoTarget = findGotoTarget(istate, s->ident);
- result = CTFEExp::breakexp;
- }
-
- void visit(ContinueStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- istate->gotoTarget = findGotoTarget(istate, s->ident);
- result = CTFEExp::continueexp;
- }
-
- void visit(WhileStatement *)
- {
- assert(0); // rewritten to ForStatement
- }
-
- void visit(DoStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
-
- while (1)
- {
- Expression *e = interpret(s->_body, istate);
- if (!e && istate->start) // goto target was not found
- return;
- assert(!istate->start);
-
- if (exceptionOrCant(e))
- return;
- if (e && e->op == TOKbreak)
- {
- if (istate->gotoTarget && istate->gotoTarget != s)
- {
- result = e; // break at a higher level
- return;
- }
- istate->gotoTarget = NULL;
- break;
- }
- if (e && e->op == TOKcontinue)
- {
- if (istate->gotoTarget && istate->gotoTarget != s)
- {
- result = e; // continue at a higher level
- return;
- }
- istate->gotoTarget = NULL;
- e = NULL;
- }
- if (e)
- {
- result = e; // bubbled up from ReturnStatement
- return;
- }
-
- UnionExp ue;
- e = interpret(&ue, s->condition, istate);
- if (exceptionOrCant(e))
- return;
- if (!e->isConst())
- {
- result = CTFEExp::cantexp;
- return;
- }
- if (e->isBool(false))
- break;
- assert(isTrueBool(e));
- }
- assert(result == NULL);
- }
-
- void visit(ForStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
-
- UnionExp ueinit;
- Expression *ei = interpret(&ueinit, s->_init, istate);
- if (exceptionOrCant(ei))
- return;
- assert(!ei); // s->init never returns from function, or jumps out from it
-
- while (1)
- {
- if (s->condition && !istate->start)
- {
- UnionExp ue;
- Expression *e = interpret(&ue, s->condition, istate);
- if (exceptionOrCant(e))
- return;
- if (e->isBool(false))
- break;
- assert(isTrueBool(e));
- }
-
- Expression *e = interpret(pue, s->_body, istate);
- if (!e && istate->start) // goto target was not found
- return;
- assert(!istate->start);
-
- if (exceptionOrCant(e))
- return;
- if (e && e->op == TOKbreak)
- {
- if (istate->gotoTarget && istate->gotoTarget != s)
- {
- result = e; // break at a higher level
- return;
- }
- istate->gotoTarget = NULL;
- break;
- }
- if (e && e->op == TOKcontinue)
- {
- if (istate->gotoTarget && istate->gotoTarget != s)
- {
- result = e; // continue at a higher level
- return;
- }
- istate->gotoTarget = NULL;
- e = NULL;
- }
- if (e)
- {
- result = e; // bubbled up from ReturnStatement
- return;
- }
-
- UnionExp uei;
- e = interpret(&uei, s->increment, istate, ctfeNeedNothing);
- if (exceptionOrCant(e))
- return;
- }
- assert(result == NULL);
- }
-
- void visit(ForeachStatement *)
- {
- assert(0); // rewritten to ForStatement
- }
-
- void visit(ForeachRangeStatement *)
- {
- assert(0); // rewritten to ForStatement
- }
-
- void visit(SwitchStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
- if (istate->start)
- {
- Expression *e = interpret(s->_body, istate);
- if (istate->start) // goto target was not found
- return;
- if (exceptionOrCant(e))
- return;
- if (e && e->op == TOKbreak)
- {
- if (istate->gotoTarget && istate->gotoTarget != s)
- {
- result = e; // break at a higher level
- return;
- }
- istate->gotoTarget = NULL;
- e = NULL;
- }
- result = e;
- return;
- }
-
- UnionExp uecond;
- Expression *econdition = interpret(&uecond, s->condition, istate);
- if (exceptionOrCant(econdition))
- return;
-
- Statement *scase = NULL;
- size_t dim = s->cases ? s->cases->length : 0;
- for (size_t i = 0; i < dim; i++)
- {
- CaseStatement *cs = (*s->cases)[i];
- UnionExp uecase;
- Expression *ecase = interpret(&uecase, cs->exp, istate);
- if (exceptionOrCant(ecase))
- return;
- if (ctfeEqual(cs->exp->loc, TOKequal, econdition, ecase))
- {
- scase = cs;
- break;
- }
- }
- if (!scase)
- {
- if (s->hasNoDefault)
- s->error("no default or case for %s in switch statement", econdition->toChars());
- scase = s->sdefault;
- }
-
- assert(scase);
-
- /* Jump to scase
- */
- istate->start = scase;
- Expression *e = interpret(pue, s->_body, istate);
- assert(!istate->start); // jump must not fail
- if (e && e->op == TOKbreak)
- {
- if (istate->gotoTarget && istate->gotoTarget != s)
- {
- result = e; // break at a higher level
- return;
- }
- istate->gotoTarget = NULL;
- e = NULL;
- }
- result = e;
- }
-
- void visit(CaseStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
-
- result = interpret(pue, s->statement, istate);
- }
-
- void visit(DefaultStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
-
- result = interpret(pue, s->statement, istate);
- }
-
- void visit(GotoStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- assert(s->label && s->label->statement);
- istate->gotoTarget = s->label->statement;
- result = CTFEExp::gotoexp;
- }
-
- void visit(GotoCaseStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- assert(s->cs);
- istate->gotoTarget = s->cs;
- result = CTFEExp::gotoexp;
- }
-
- void visit(GotoDefaultStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- assert(s->sw && s->sw->sdefault);
- istate->gotoTarget = s->sw->sdefault;
- result = CTFEExp::gotoexp;
- }
-
- void visit(LabelStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
-
- result = interpret(pue, s->statement, istate);
- }
-
- void visit(TryCatchStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
- if (istate->start)
- {
- Expression *e = NULL;
- e = interpret(pue, s->_body, istate);
- for (size_t i = 0; i < s->catches->length; i++)
- {
- if (e || !istate->start) // goto target was found
- break;
- Catch *ca = (*s->catches)[i];
- e = interpret(ca->handler, istate);
- }
- result = e;
- return;
- }
-
- Expression *e = interpret(pue, s->_body, istate);
-
- // An exception was thrown
- if (e && e->op == TOKthrownexception)
- {
- ThrownExceptionExp *ex = (ThrownExceptionExp *)e;
- Type *extype = ex->thrown->originalClass()->type;
-
- // Search for an appropriate catch clause.
- for (size_t i = 0; i < s->catches->length; i++)
- {
- Catch *ca = (*s->catches)[i];
- Type *catype = ca->type;
- if (!catype->equals(extype) && !catype->isBaseOf(extype, NULL))
- continue;
-
- // Execute the handler
- if (ca->var)
- {
- ctfeStack.push(ca->var);
- setValue(ca->var, ex->thrown);
- }
- e = interpret(ca->handler, istate);
- if (CTFEExp::isGotoExp(e))
- {
- /* This is an optimization that relies on the locality of the jump target.
- * If the label is in the same catch handler, the following scan
- * would find it quickly and can reduce jump cost.
- * Otherwise, the catch block may be unnnecessary scanned again
- * so it would make CTFE speed slower.
- */
- InterState istatex = *istate;
- istatex.start = istate->gotoTarget; // set starting statement
- istatex.gotoTarget = NULL;
- Expression *eh = interpret(ca->handler, &istatex);
- if (!istatex.start)
- {
- istate->gotoTarget = NULL;
- e = eh;
- }
- }
- break;
- }
- }
- result = e;
- }
-
- static bool isAnErrorException(ClassDeclaration *cd)
- {
- return cd == ClassDeclaration::errorException || ClassDeclaration::errorException->isBaseOf(cd, NULL);
- }
-
- static ThrownExceptionExp *chainExceptions(ThrownExceptionExp *oldest, ThrownExceptionExp *newest)
- {
- // Little sanity check to make sure it's really a Throwable
- ClassReferenceExp *boss = oldest->thrown;
- const int next = 4; // index of Throwable.next
- assert((*boss->value->elements)[next]->type->ty == Tclass); // Throwable.next
- ClassReferenceExp *collateral = newest->thrown;
- if ( isAnErrorException(collateral->originalClass()) &&
- !isAnErrorException(boss->originalClass()))
- {
- /* Find the index of the Error.bypassException field
- */
- int bypass = next + 1;
- if ((*collateral->value->elements)[bypass]->type->ty == Tuns32)
- bypass += 1; // skip over _refcount field
- assert((*collateral->value->elements)[bypass]->type->ty == Tclass);
-
- // The new exception bypass the existing chain
- (*collateral->value->elements)[bypass] = boss;
- return newest;
- }
- while ((*boss->value->elements)[next]->op == TOKclassreference)
- {
- boss = (ClassReferenceExp *)(*boss->value->elements)[next];
- }
- (*boss->value->elements)[next] = collateral;
- return oldest;
- }
-
- void visit(TryFinallyStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
- if (istate->start)
- {
- Expression *e = NULL;
- e = interpret(pue, s->_body, istate);
- // Jump into/out from finalbody is disabled in semantic analysis.
- // and jump inside will be handled by the ScopeStatement == finalbody.
- result = e;
- return;
- }
-
- Expression *ex = interpret(s->_body, istate);
- if (CTFEExp::isCantExp(ex))
- {
- result = ex;
- return;
- }
- while (CTFEExp::isGotoExp(ex))
- {
- // If the goto target is within the body, we must not interpret the finally statement,
- // because that will call destructors for objects within the scope, which we should not do.
- InterState istatex = *istate;
- istatex.start = istate->gotoTarget; // set starting statement
- istatex.gotoTarget = NULL;
- Expression *bex = interpret(s->_body, &istatex);
- if (istatex.start)
- {
- // The goto target is outside the current scope.
- break;
- }
- // The goto target was within the body.
- if (CTFEExp::isCantExp(bex))
- {
- result = bex;
- return;
- }
- *istate = istatex;
- ex = bex;
- }
- Expression *ey = interpret(s->finalbody, istate);
- if (CTFEExp::isCantExp(ey))
- {
- result = ey;
- return;
- }
- if (ey && ey->op == TOKthrownexception)
- {
- // Check for collided exceptions
- if (ex && ex->op == TOKthrownexception)
- ex = chainExceptions((ThrownExceptionExp *)ex, (ThrownExceptionExp *)ey);
- else
- ex = ey;
- }
- result = ex;
- }
-
- void visit(ThrowStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- Expression *e = interpret(s->exp, istate);
- if (exceptionOrCant(e))
- return;
-
- assert(e->op == TOKclassreference);
- result = new ThrownExceptionExp(s->loc, (ClassReferenceExp *)e);
- }
-
- void visit(ScopeGuardStatement *)
- {
- assert(0);
- }
-
- void visit(WithStatement *s)
- {
- if (istate->start == s)
- istate->start = NULL;
- if (istate->start)
- {
- result = s->_body ? interpret(s->_body, istate) : NULL;
- return;
- }
-
- // If it is with(Enum) {...}, just execute the body.
- if (s->exp->op == TOKscope || s->exp->op == TOKtype)
- {
- result = interpret(pue, s->_body, istate);
- return;
- }
-
- Expression *e = interpret(s->exp, istate);
- if (exceptionOrCant(e))
- return;
-
- if (s->wthis->type->ty == Tpointer && s->exp->type->ty != Tpointer)
- {
- e = new AddrExp(s->loc, e, s->wthis->type);
- }
- ctfeStack.push(s->wthis);
- setValue(s->wthis, e);
- e = interpret(s->_body, istate);
- if (CTFEExp::isGotoExp(e))
- {
- /* This is an optimization that relies on the locality of the jump target.
- * If the label is in the same WithStatement, the following scan
- * would find it quickly and can reduce jump cost.
- * Otherwise, the statement body may be unnnecessary scanned again
- * so it would make CTFE speed slower.
- */
- InterState istatex = *istate;
- istatex.start = istate->gotoTarget; // set starting statement
- istatex.gotoTarget = NULL;
- Expression *ex = interpret(s->_body, &istatex);
- if (!istatex.start)
- {
- istate->gotoTarget = NULL;
- e = ex;
- }
- }
- ctfeStack.pop(s->wthis);
- result = e;
- }
-
- void visit(AsmStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
-
- s->error("asm statements cannot be interpreted at compile time");
- result = CTFEExp::cantexp;
- }
-
- void visit(ImportStatement *s)
- {
- if (istate->start)
- {
- if (istate->start != s)
- return;
- istate->start = NULL;
- }
- }
-
- /******************************** Expression ***************************/
-
- void visit(Expression *e)
- {
- e->error("cannot interpret %s at compile time", e->toChars());
- result = CTFEExp::cantexp;
- }
-
- void visit(ThisExp *e)
- {
- if (goal == ctfeNeedLvalue)
- {
- // We might end up here with istate being zero (see bugzilla 16382)
- if (istate && istate->fd->vthis)
- {
- result = new VarExp(e->loc, istate->fd->vthis);
- result->type = e->type;
- }
- else
- result = e;
- return;
- }
-
- result = ctfeStack.getThis();
- if (result)
- {
- assert(result->op == TOKstructliteral ||
- result->op == TOKclassreference);
- return;
- }
- e->error("value of `this` is not known at compile time");
- result = CTFEExp::cantexp;
- }
-
- void visit(NullExp *e)
- {
- result = e;
- }
-
- void visit(IntegerExp *e)
- {
- result = e;
- }
-
- void visit(RealExp *e)
- {
- result = e;
- }
-
- void visit(ComplexExp *e)
- {
- result = e;
- }
-
- void visit(StringExp *e)
- {
- /* Attempts to modify string literals are prevented
- * in BinExp::interpretAssignCommon.
- */
- result = e;
- }
-
- void visit(FuncExp *e)
- {
- result = e;
- }
-
- void visit(SymOffExp *e)
- {
- if (e->var->isFuncDeclaration() && e->offset == 0)
- {
- result = e;
- return;
- }
- if (isTypeInfo_Class(e->type) && e->offset == 0)
- {
- result = e;
- return;
- }
- if (e->type->ty != Tpointer)
- {
- // Probably impossible
- e->error("cannot interpret %s at compile time", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- Type *pointee = ((TypePointer *)e->type)->next;
- if (e->var->isThreadlocal())
- {
- e->error("cannot take address of thread-local variable %s at compile time", e->var->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- // Check for taking an address of a shared variable.
- // If the shared variable is an array, the offset might not be zero.
- Type *fromType = NULL;
- if (e->var->type->ty == Tarray || e->var->type->ty == Tsarray)
- {
- fromType = ((TypeArray *)(e->var->type))->next;
- }
- if (e->var->isDataseg() &&
- ((e->offset == 0 && isSafePointerCast(e->var->type, pointee)) ||
- (fromType && isSafePointerCast(fromType, pointee))))
- {
- result = e;
- return;
- }
- Expression *val = getVarExp(e->loc, istate, e->var, goal);
- if (exceptionOrCant(val))
- return;
- if (val->type->ty == Tarray || val->type->ty == Tsarray)
- {
- // Check for unsupported type painting operations
- Type *elemtype = ((TypeArray *)(val->type))->next;
- d_uns64 elemsize = elemtype->size();
-
- // It's OK to cast from fixed length to fixed length array, eg &int[n] to int[d]*.
- if (val->type->ty == Tsarray && pointee->ty == Tsarray &&
- elemsize == pointee->nextOf()->size())
- {
- size_t d = (size_t)((TypeSArray *)pointee)->dim->toInteger();
- Expression *elwr = new IntegerExp(e->loc, e->offset / elemsize, Type::tsize_t);
- Expression *eupr = new IntegerExp(e->loc, e->offset / elemsize + d, Type::tsize_t);
-
- // Create a CTFE pointer &val[ofs..ofs+d]
- SliceExp *se = new SliceExp(e->loc, val, elwr, eupr);
- se->type = pointee;
- new(pue) AddrExp(e->loc, se, e->type);
- result = pue->exp();
- return;
- }
-
- if (!isSafePointerCast(elemtype, pointee))
- {
- // It's also OK to cast from &string to string*.
- if (e->offset == 0 && isSafePointerCast(e->var->type, pointee))
- {
- // Create a CTFE pointer &var
- VarExp *ve = new VarExp(e->loc, e->var);
- ve->type = elemtype;
- new(pue) AddrExp(e->loc, ve, e->type);
- result = pue->exp();
- return;
- }
- e->error("reinterpreting cast from %s to %s is not supported in CTFE",
- val->type->toChars(), e->type->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- const dinteger_t sz = pointee->size();
- dinteger_t indx = e->offset / sz;
- assert(sz * indx == e->offset);
- Expression *aggregate = NULL;
- if (val->op == TOKarrayliteral || val->op == TOKstring)
- {
- aggregate = val;
- }
- else if (val->op == TOKslice)
- {
- aggregate = ((SliceExp *)val)->e1;
- UnionExp uelwr;
- Expression *lwr = interpret(&uelwr, ((SliceExp *)val)->lwr, istate);
- indx += lwr->toInteger();
- }
- if (aggregate)
- {
- // Create a CTFE pointer &aggregate[ofs]
- IntegerExp *ofs = new IntegerExp(e->loc, indx, Type::tsize_t);
- IndexExp *ei = new IndexExp(e->loc, aggregate, ofs);
- ei->type = elemtype;
- new(pue) AddrExp(e->loc, ei, e->type);
- result = pue->exp();
- return;
- }
- }
- else if (e->offset == 0 && isSafePointerCast(e->var->type, pointee))
- {
- // Create a CTFE pointer &var
- VarExp *ve = new VarExp(e->loc, e->var);
- ve->type = e->var->type;
- new(pue) AddrExp(e->loc, ve, e->type);
- result = pue->exp();
- return;
- }
-
- e->error("cannot convert &%s to %s at compile time", e->var->type->toChars(), e->type->toChars());
- result = CTFEExp::cantexp;
- }
-
- void visit(AddrExp *e)
- {
- if (e->e1->op == TOKvar && ((VarExp *)e->e1)->var->isDataseg())
- {
- // Normally this is already done by optimize()
- // Do it here in case optimize(WANTvalue) wasn't run before CTFE
- new(pue) SymOffExp(e->loc, ((VarExp *)e->e1)->var, 0);
- result = pue->exp();
- result->type = e->type;
- return;
- }
- Expression *er = interpret(e->e1, istate, ctfeNeedLvalue);
- if (er->op == TOKvar && ((VarExp *)er)->var == istate->fd->vthis)
- er = interpret(er, istate);
- if (exceptionOrCant(er))
- return;
-
- // Return a simplified address expression
- new(pue) AddrExp(e->loc, er, e->type);
- result = pue->exp();
- }
-
- void visit(DelegateExp *e)
- {
- // TODO: Really we should create a CTFE-only delegate expression
- // of a pointer and a funcptr.
-
- // If it is &nestedfunc, just return it
- // TODO: We should save the context pointer
- if (e->e1->op == TOKvar && ((VarExp *)e->e1)->var == e->func)
- {
- result = e;
- return;
- }
-
- Expression *er = interpret(pue, e->e1, istate);
- if (exceptionOrCant(er))
- return;
- if (er == e->e1)
- {
- // If it has already been CTFE'd, just return it
- result = e;
- }
- else
- {
- er = (er == pue->exp()) ? pue->copy() : er;
- new(pue) DelegateExp(e->loc, er, e->func, false);
- result = pue->exp();
- result->type = e->type;
- }
- }
-
- static Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal)
- {
- Expression *e = CTFEExp::cantexp;
- if (VarDeclaration *v = d->isVarDeclaration())
- {
- /* Magic variable __ctfe always returns true when interpreting
- */
- if (v->ident == Id::ctfe)
- return new IntegerExp(loc, 1, Type::tbool);
-
- if (!v->originalType && v->semanticRun < PASSsemanticdone) // semantic() not yet run
- {
- dsymbolSemantic(v, NULL);
- if (v->type->ty == Terror)
- return CTFEExp::cantexp;
- }
-
- if ((v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) &&
- !hasValue(v) &&
- v->_init && !v->isCTFE())
- {
- if (v->inuse)
- {
- error(loc, "circular initialization of %s `%s`", v->kind(), v->toPrettyChars());
- return CTFEExp::cantexp;
- }
- if (v->_scope)
- {
- v->inuse++;
- v->_init = initializerSemantic(v->_init, v->_scope, v->type, INITinterpret); // might not be run on aggregate members
- v->inuse--;
- }
- e = initializerToExpression(v->_init, v->type);
- if (!e)
- return CTFEExp::cantexp;
- assert(e->type);
-
- if (e->op == TOKconstruct || e->op == TOKblit)
- {
- AssignExp *ae = (AssignExp *)e;
- e = ae->e2;
- }
-
- if (e->op == TOKerror)
- {
- // FIXME: Ultimately all errors should be detected in prior semantic analysis stage.
- }
- else if (v->isDataseg() || (v->storage_class & STCmanifest))
- {
- /* Bugzilla 14304: e is a value that is not yet owned by CTFE.
- * Mark as "cached", and use it directly during interpretation.
- */
- e = scrubCacheValue(e);
- ctfeStack.saveGlobalConstant(v, e);
- }
- else
- {
- v->inuse++;
- e = interpret(e, istate);
- v->inuse--;
- if (CTFEExp::isCantExp(e) && !global.gag && !CtfeStatus::stackTraceCallsToSuppress)
- errorSupplemental(loc, "while evaluating %s.init", v->toChars());
- if (exceptionOrCantInterpret(e))
- return e;
- }
- }
- else if (v->isCTFE() && !hasValue(v))
- {
- if (v->_init && v->type->size() != 0)
- {
- if (v->_init->isVoidInitializer())
- {
- // var should have been initialized when it was created
- error(loc, "CTFE internal error: trying to access uninitialized var");
- assert(0);
- return CTFEExp::cantexp;
- }
- e = initializerToExpression(v->_init);
- }
- else
- e = v->type->defaultInitLiteral(e->loc);
-
- e = interpret(e, istate);
- }
- else if (!(v->isDataseg() || v->storage_class & STCmanifest) && !v->isCTFE() && !istate)
- {
- error(loc, "variable %s cannot be read at compile time", v->toChars());
- return CTFEExp::cantexp;
- }
- else
- {
- e = hasValue(v) ? getValue(v) : NULL;
- if (!e && !v->isCTFE() && v->isDataseg())
- {
- error(loc, "static variable %s cannot be read at compile time", v->toChars());
- return CTFEExp::cantexp;
- }
- if (!e)
- {
- assert(!(v->_init && v->_init->isVoidInitializer()));
- // CTFE initiated from inside a function
- error(loc, "variable %s cannot be read at compile time", v->toChars());
- return CTFEExp::cantexp;
- }
- if (e->op == TOKvoid)
- {
- VoidInitExp *ve = (VoidInitExp *)e;
- error(loc, "cannot read uninitialized variable %s in ctfe", v->toPrettyChars());
- errorSupplemental(ve->var->loc, "%s was uninitialized and used before set", ve->var->toChars());
- return CTFEExp::cantexp;
- }
- if (goal != ctfeNeedLvalue && (v->isRef() || v->isOut()))
- e = interpret(e, istate, goal);
- }
- if (!e)
- e = CTFEExp::cantexp;
- }
- else if (SymbolDeclaration *s = d->isSymbolDeclaration())
- {
- // Struct static initializers, for example
- e = s->dsym->type->defaultInitLiteral(loc);
- if (e->op == TOKerror)
- error(loc, "CTFE failed because of previous errors in %s.init", s->toChars());
- e = expressionSemantic(e, NULL);
- if (e->op == TOKerror)
- e = CTFEExp::cantexp;
- else // Convert NULL to CTFEExp
- e = interpret(e, istate, goal);
- }
- else
- error(loc, "cannot interpret declaration %s at compile time", d->toChars());
- return e;
- }
-
- void visit(VarExp *e)
- {
- if (e->var->isFuncDeclaration())
- {
- result = e;
- return;
- }
-
- if (goal == ctfeNeedLvalue)
- {
- VarDeclaration *v = e->var->isVarDeclaration();
- if (v && !v->isDataseg() && !v->isCTFE() && !istate)
- {
- e->error("variable %s cannot be read at compile time", v->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (v && !hasValue(v))
- {
- if (!v->isCTFE() && v->isDataseg())
- e->error("static variable %s cannot be read at compile time", v->toChars());
- else // CTFE initiated from inside a function
- e->error("variable %s cannot be read at compile time", v->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (v && (v->storage_class & (STCout | STCref)) && hasValue(v))
- {
- // Strip off the nest of ref variables
- Expression *ev = getValue(v);
- if (ev->op == TOKvar || ev->op == TOKindex || ev->op == TOKdotvar)
- {
- result = interpret(pue, ev, istate, goal);
- return;
- }
- }
- result = e;
- return;
- }
- result = getVarExp(e->loc, istate, e->var, goal);
- if (exceptionOrCant(result))
- return;
- if ((e->var->storage_class & (STCref | STCout)) == 0 &&
- e->type->baseElemOf()->ty != Tstruct)
- {
- /* Ultimately, STCref|STCout check should be enough to see the
- * necessity of type repainting. But currently front-end paints
- * non-ref struct variables by the const type.
- *
- * auto foo(ref const S cs);
- * S s;
- * foo(s); // VarExp('s') will have const(S)
- */
- // A VarExp may include an implicit cast. It must be done explicitly.
- result = paintTypeOntoLiteral(pue, e->type, result);
- }
- }
-
- void visit(DeclarationExp *e)
- {
- Dsymbol *s = e->declaration;
- if (VarDeclaration *v = s->isVarDeclaration())
- {
- if (TupleDeclaration *td = v->toAlias()->isTupleDeclaration())
- {
- result = NULL;
-
- // Reserve stack space for all tuple members
- if (!td->objects)
- return;
- for (size_t i = 0; i < td->objects->length; ++i)
- {
- RootObject * o = (*td->objects)[i];
- Expression *ex = isExpression(o);
- DsymbolExp *ds = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL;
- VarDeclaration *v2 = ds ? ds->s->isVarDeclaration() : NULL;
- assert(v2);
- if (v2->isDataseg() && !v2->isCTFE())
- continue;
-
- ctfeStack.push(v2);
- if (v2->_init)
- {
- Expression *einit;
- if (ExpInitializer *ie = v2->_init->isExpInitializer())
- {
- einit = interpret(ie->exp, istate, goal);
- if (exceptionOrCant(einit))
- return;
- }
- else if (v2->_init->isVoidInitializer())
- {
- einit = voidInitLiteral(v2->type, v2).copy();
- }
- else
- {
- e->error("declaration %s is not yet implemented in CTFE", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- setValue(v2, einit);
- }
- }
- return;
- }
- if (v->isStatic())
- {
- // Just ignore static variables which aren't read or written yet
- result = NULL;
- return;
- }
- if (!(v->isDataseg() || v->storage_class & STCmanifest) || v->isCTFE())
- ctfeStack.push(v);
- if (v->_init)
- {
- if (ExpInitializer *ie = v->_init->isExpInitializer())
- {
- result = interpret(ie->exp, istate, goal);
- }
- else if (v->_init->isVoidInitializer())
- {
- result = voidInitLiteral(v->type, v).copy();
- // There is no AssignExp for void initializers,
- // so set it here.
- setValue(v, result);
- }
- else
- {
- e->error("declaration %s is not yet implemented in CTFE", e->toChars());
- result = CTFEExp::cantexp;
- }
- }
- else if (v->type->size() == 0)
- {
- // Zero-length arrays don't need an initializer
- result = v->type->defaultInitLiteral(e->loc);
- }
- else
- {
- e->error("variable %s cannot be modified at compile time", v->toChars());
- result = CTFEExp::cantexp;
- }
- return;
- }
- if (s->isAttribDeclaration() ||
- s->isTemplateMixin() ||
- s->isTupleDeclaration())
- {
- // Check for static struct declarations, which aren't executable
- AttribDeclaration *ad = e->declaration->isAttribDeclaration();
- if (ad && ad->decl && ad->decl->length == 1)
- {
- Dsymbol *sparent = (*ad->decl)[0];
- if (sparent->isAggregateDeclaration() ||
- sparent->isTemplateDeclaration() ||
- sparent->isAliasDeclaration())
- {
- result = NULL;
- return; // static (template) struct declaration. Nothing to do.
- }
- }
-
- // These can be made to work, too lazy now
- e->error("declaration %s is not yet implemented in CTFE", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- // Others should not contain executable code, so are trivial to evaluate
- result = NULL;
- }
-
- void visit(TypeidExp *e)
- {
- if (isType(e->obj))
- {
- result = e;
- return;
- }
- if (Expression *ex = isExpression(e->obj))
- {
- result = interpret(pue, ex, istate);
- if (exceptionOrCant(ex))
- return;
-
- if (result->op == TOKnull)
- {
- e->error("null pointer dereference evaluating typeid. `%s` is null", ex->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (result->op != TOKclassreference)
- {
- e->error("CTFE internal error: determining classinfo");
- result = CTFEExp::cantexp;
- return;
- }
-
- ClassDeclaration *cd = ((ClassReferenceExp *)result)->originalClass();
- assert(cd);
-
- new(pue) TypeidExp(e->loc, cd->type);
- result = pue->exp();
- result->type = e->type;
- return;
- }
- visit((Expression *)e);
- }
-
- void visit(TupleExp *e)
- {
- if (exceptionOrCant(interpret(e->e0, istate, ctfeNeedNothing)))
- return;
-
- Expressions *expsx = e->exps;
- for (size_t i = 0; i < expsx->length; i++)
- {
- Expression *exp = (*expsx)[i];
- Expression *ex = interpret(exp, istate);
- if (exceptionOrCant(ex))
- return;
-
- // A tuple of assignments can contain void (Bug 5676).
- if (goal == ctfeNeedNothing)
- continue;
- if (ex->op == TOKvoidexp)
- {
- e->error("CTFE internal error: void element %s in tuple", exp->toChars());
- assert(0);
- }
-
- /* If any changes, do Copy On Write
- */
- if (ex != exp)
- {
- expsx = copyArrayOnWrite(expsx, e->exps);
- (*expsx)[i] = ex;
- }
- }
- if (expsx != e->exps)
- {
- expandTuples(expsx);
- new(pue) TupleExp(e->loc, expsx);
- result = pue->exp();
- result->type = new TypeTuple(expsx);
- }
- else
- result = e;
- }
-
- void visit(ArrayLiteralExp *e)
- {
- if (e->ownedByCtfe >= OWNEDctfe) // We've already interpreted all the elements
- {
- result = e;
- return;
- }
-
- Type *tn = e->type->toBasetype()->nextOf()->toBasetype();
- bool wantCopy = (tn->ty == Tsarray || tn->ty == Tstruct);
-
- Expression *basis = interpret(e->basis, istate);
- if (exceptionOrCant(basis))
- return;
-
- Expressions *expsx = e->elements;
- size_t dim = expsx ? expsx->length : 0;
- for (size_t i = 0; i < dim; i++)
- {
- Expression *exp = (*expsx)[i];
- Expression *ex;
- if (!exp)
- {
- ex = copyLiteral(basis).copy();
- }
- else
- {
- // segfault bug 6250
- assert(exp->op != TOKindex || ((IndexExp *)exp)->e1 != e);
-
- ex = interpret(exp, istate);
- if (exceptionOrCant(ex))
- return;
-
- /* Each elements should have distinct CFE memory.
- * int[1] z = 7;
- * int[1][] pieces = [z,z]; // here
- */
- if (wantCopy)
- ex = copyLiteral(ex).copy();
- }
-
- /* If any changes, do Copy On Write
- */
- if (ex != exp)
- {
- expsx = copyArrayOnWrite(expsx, e->elements);
- (*expsx)[i] = ex;
- }
- }
-
- if (expsx != e->elements)
- {
- // todo: all tuple expansions should go in semantic phase.
- expandTuples(expsx);
- if (expsx->length != dim)
- {
- e->error("CTFE internal error: invalid array literal");
- result = CTFEExp::cantexp;
- return;
- }
- new(pue) ArrayLiteralExp(e->loc, e->type, basis, expsx);
- ArrayLiteralExp *ale = (ArrayLiteralExp *)pue->exp();
- ale->ownedByCtfe = OWNEDctfe;
- result = ale;
- }
- else if (((TypeNext *)e->type)->next->mod & (MODconst | MODimmutable))
- {
- // If it's immutable, we don't need to dup it
- result = e;
- }
- else
- {
- *pue = copyLiteral(e);
- result = pue->exp();
- }
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- if (e->ownedByCtfe >= OWNEDctfe) // We've already interpreted all the elements
- {
- result = e;
- return;
- }
-
- Expressions *keysx = e->keys;
- Expressions *valuesx = e->values;
- for (size_t i = 0; i < keysx->length; i++)
- {
- Expression *ekey = (*keysx)[i];
- Expression *evalue = (*valuesx)[i];
-
- Expression *ek = interpret(ekey, istate);
- if (exceptionOrCant(ek))
- return;
- Expression *ev = interpret(evalue, istate);
- if (exceptionOrCant(ev))
- return;
-
- /* If any changes, do Copy On Write
- */
- if (ek != ekey ||
- ev != evalue)
- {
- keysx = copyArrayOnWrite(keysx, e->keys);
- valuesx = copyArrayOnWrite(valuesx, e->values);
- (*keysx)[i] = ek;
- (*valuesx)[i] = ev;
- }
- }
- if (keysx != e->keys)
- expandTuples(keysx);
- if (valuesx != e->values)
- expandTuples(valuesx);
- if (keysx->length != valuesx->length)
- {
- e->error("CTFE internal error: invalid AA");
- result = CTFEExp::cantexp;
- return;
- }
-
- /* Remove duplicate keys
- */
- for (size_t i = 1; i < keysx->length; i++)
- {
- Expression *ekey = (*keysx)[i - 1];
- for (size_t j = i; j < keysx->length; j++)
- {
- Expression *ekey2 = (*keysx)[j];
- if (!ctfeEqual(e->loc, TOKequal, ekey, ekey2))
- continue;
-
- // Remove ekey
- keysx = copyArrayOnWrite(keysx, e->keys);
- valuesx = copyArrayOnWrite(valuesx, e->values);
- keysx->remove(i - 1);
- valuesx->remove(i - 1);
-
- i -= 1; // redo the i'th iteration
- break;
- }
- }
-
- if (keysx != e->keys ||
- valuesx != e->values)
- {
- assert(keysx != e->keys &&
- valuesx != e->values);
- AssocArrayLiteralExp *ae = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
- ae->type = e->type;
- ae->ownedByCtfe = OWNEDctfe;
- result = ae;
- }
- else
- {
- *pue = copyLiteral(e);
- result = pue->exp();
- }
- }
-
- void visit(StructLiteralExp *e)
- {
- if (e->ownedByCtfe >= OWNEDctfe)
- {
- result = e;
- return;
- }
-
- size_t dim = e->elements ? e->elements->length : 0;
- Expressions *expsx = e->elements;
-
- if (dim != e->sd->fields.length)
- {
- // guaranteed by AggregateDeclaration.fill and TypeStruct.defaultInitLiteral
- assert(e->sd->isNested() && dim == e->sd->fields.length - 1);
-
- /* If a nested struct has no initialized hidden pointer,
- * set it to null to match the runtime behaviour.
- */
- NullExp *ne = new NullExp(e->loc);
- ne->type = e->sd->vthis->type;
-
- expsx = copyArrayOnWrite(expsx, e->elements);
- expsx->push(ne);
- ++dim;
- }
- assert(dim == e->sd->fields.length);
-
- for (size_t i = 0; i < dim; i++)
- {
- VarDeclaration *v = e->sd->fields[i];
- Expression *exp = (*expsx)[i];
- Expression *ex = NULL;
- if (!exp)
- {
- ex = voidInitLiteral(v->type, v).copy();
- }
- else
- {
- ex = interpret(exp, istate);
- if (exceptionOrCant(ex))
- return;
- if ((v->type->ty != ex->type->ty) && v->type->ty == Tsarray)
- {
- // Block assignment from inside struct literals
- TypeSArray *tsa = (TypeSArray *)v->type;
- size_t len = (size_t)tsa->dim->toInteger();
- UnionExp ue;
- ex = createBlockDuplicatedArrayLiteral(&ue, ex->loc, v->type, ex, len);
- if (ex == ue.exp())
- ex = ue.copy();
- }
- }
-
- /* If any changes, do Copy On Write
- */
- if (ex != exp)
- {
- expsx = copyArrayOnWrite(expsx, e->elements);
- (*expsx)[i] = ex;
- }
- }
-
- if (expsx != e->elements)
- {
- expandTuples(expsx);
- if (expsx->length != e->sd->fields.length)
- {
- e->error("CTFE internal error: invalid struct literal");
- result = CTFEExp::cantexp;
- return;
- }
- new(pue) StructLiteralExp(e->loc, e->sd, expsx);
- StructLiteralExp *sle = (StructLiteralExp *)pue->exp();
- sle->type = e->type;
- sle->ownedByCtfe = OWNEDctfe;
- sle->origin = e->origin;
- result = sle;
- }
- else
- {
- *pue = copyLiteral(e);
- result = pue->exp();
- }
- }
-
- // Create an array literal of type 'newtype' with dimensions given by
- // 'arguments'[argnum..$]
- static Expression *recursivelyCreateArrayLiteral(UnionExp *pue, Loc loc, Type *newtype, InterState *istate,
- Expressions *arguments, int argnum)
- {
- Expression *lenExpr = interpret(pue, (*arguments)[argnum], istate);
- if (exceptionOrCantInterpret(lenExpr))
- return lenExpr;
- size_t len = (size_t)(lenExpr->toInteger());
- Type *elemType = ((TypeArray *)newtype)->next;
- if (elemType->ty == Tarray && argnum < (int)arguments->length - 1)
- {
- Expression *elem = recursivelyCreateArrayLiteral(pue, loc, elemType, istate,
- arguments, argnum + 1);
- if (exceptionOrCantInterpret(elem))
- return elem;
-
- Expressions *elements = new Expressions();
- elements->setDim(len);
- for (size_t i = 0; i < len; i++)
- (*elements)[i] = copyLiteral(elem).copy();
- new(pue) ArrayLiteralExp(loc, newtype, elements);
- ArrayLiteralExp *ae = (ArrayLiteralExp *)pue->exp();
- ae->ownedByCtfe = OWNEDctfe;
- return ae;
- }
- assert(argnum == (int)arguments->length - 1);
- if (elemType->ty == Tchar || elemType->ty == Twchar || elemType->ty == Tdchar)
- {
- const unsigned ch = (unsigned)elemType->defaultInitLiteral(loc)->toInteger();
- const unsigned char sz = (unsigned char)elemType->size();
- return createBlockDuplicatedStringLiteral(pue, loc, newtype, ch, len, sz);
- }
- else
- {
- Expression *el = interpret(elemType->defaultInitLiteral(loc), istate);
- return createBlockDuplicatedArrayLiteral(pue, loc, newtype, el, len);
- }
- }
-
- void visit(NewExp *e)
- {
- if (e->allocator)
- {
- e->error("member allocators not supported by CTFE");
- result = CTFEExp::cantexp;
- return;
- }
-
- Expression *epre = interpret(pue, e->argprefix, istate, ctfeNeedNothing);
- if (exceptionOrCant(epre))
- return;
-
- if (e->newtype->ty == Tarray && e->arguments)
- {
- result = recursivelyCreateArrayLiteral(pue, e->loc, e->newtype, istate, e->arguments, 0);
- return;
- }
- if (e->newtype->toBasetype()->ty == Tstruct)
- {
- if (e->member)
- {
- Expression *se = e->newtype->defaultInitLiteral(e->loc);
- se = interpret(se, istate);
- if (exceptionOrCant(se))
- return;
- result = interpretFunction(pue, e->member, istate, e->arguments, se);
-
- // Repaint as same as CallExp::interpret() does.
- result->loc = e->loc;
- }
- else
- {
- StructDeclaration *sd = ((TypeStruct *)e->newtype->toBasetype())->sym;
- Expressions *exps = new Expressions();
- exps->reserve(sd->fields.length);
- if (e->arguments)
- {
- exps->setDim(e->arguments->length);
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *ex = (*e->arguments)[i];
- ex = interpret(ex, istate);
- if (exceptionOrCant(ex))
- return;
- (*exps)[i] = ex;
- }
- }
- sd->fill(e->loc, exps, false);
-
- StructLiteralExp *se = new StructLiteralExp(e->loc, sd, exps, e->newtype);
- se->type = e->newtype;
- se->ownedByCtfe = OWNEDctfe;
- result = interpret(pue, se, istate);
- }
- if (exceptionOrCant(result))
- return;
- Expression *ev = (result == pue->exp()) ? pue->copy() : result;
- new(pue) AddrExp(e->loc, ev, e->type);
- result = pue->exp();
- return;
- }
- if (e->newtype->toBasetype()->ty == Tclass)
- {
- ClassDeclaration *cd = ((TypeClass *)e->newtype->toBasetype())->sym;
- size_t totalFieldCount = 0;
- for (ClassDeclaration *c = cd; c; c = c->baseClass)
- totalFieldCount += c->fields.length;
- Expressions *elems = new Expressions;
- elems->setDim(totalFieldCount);
- size_t fieldsSoFar = totalFieldCount;
- for (ClassDeclaration *c = cd; c; c = c->baseClass)
- {
- fieldsSoFar -= c->fields.length;
- for (size_t i = 0; i < c->fields.length; i++)
- {
- VarDeclaration *v = c->fields[i];
- if (v->inuse)
- {
- e->error("circular reference to `%s`", v->toPrettyChars());
- result = CTFEExp::cantexp;
- return;
- }
- Expression *m;
- if (v->_init)
- {
- if (v->_init->isVoidInitializer())
- m = voidInitLiteral(v->type, v).copy();
- else
- m = v->getConstInitializer(true);
- }
- else
- m = v->type->defaultInitLiteral(e->loc);
- if (exceptionOrCant(m))
- return;
- (*elems)[fieldsSoFar+i] = copyLiteral(m).copy();
- }
- }
- // Hack: we store a ClassDeclaration instead of a StructDeclaration.
- // We probably won't get away with this.
- StructLiteralExp *se = new StructLiteralExp(e->loc, (StructDeclaration *)cd, elems, e->newtype);
- se->ownedByCtfe = OWNEDctfe;
- new(pue) ClassReferenceExp(e->loc, se, e->type);
- Expression *eref = pue->exp();
- if (e->member)
- {
- // Call constructor
- if (!e->member->fbody)
- {
- Expression *ctorfail = evaluateIfBuiltin(pue, istate, e->loc, e->member, e->arguments, eref);
- if (ctorfail)
- {
- if (exceptionOrCant(ctorfail))
- return;
- result = eref;
- return;
- }
- e->member->error("%s cannot be constructed at compile time, because the constructor has no available source code", e->newtype->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- UnionExp ue;
- Expression *ctorfail = interpretFunction(&ue, e->member, istate, e->arguments, eref);
- if (exceptionOrCant(ctorfail))
- return;
-
- /* Bugzilla 14465: Repaint the loc, because a super() call
- * in the constructor modifies the loc of ClassReferenceExp
- * in CallExp::interpret().
- */
- eref->loc = e->loc;
- }
- result = eref;
- return;
- }
- if (e->newtype->toBasetype()->isscalar())
- {
- Expression *newval;
- if (e->arguments && e->arguments->length)
- newval = (*e->arguments)[0];
- else
- newval = e->newtype->defaultInitLiteral(e->loc);
- newval = interpret(newval, istate);
- if (exceptionOrCant(newval))
- return;
-
- // Create a CTFE pointer &[newval][0]
- Expressions *elements = new Expressions();
- elements->setDim(1);
- (*elements)[0] = newval;
- ArrayLiteralExp *ae = new ArrayLiteralExp(e->loc, e->newtype->arrayOf(), elements);
- ae->ownedByCtfe = OWNEDctfe;
-
- IndexExp *ei = new IndexExp(e->loc, ae, new IntegerExp(Loc(), 0, Type::tsize_t));
- ei->type = e->newtype;
- new(pue) AddrExp(e->loc, ei, e->type);
- result = pue->exp();
- return;
- }
- e->error("cannot interpret %s at compile time", e->toChars());
- result = CTFEExp::cantexp;
- }
-
- void visit(UnaExp *e)
- {
- UnionExp ue;
- Expression *e1 = interpret(&ue, e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- switch (e->op)
- {
- case TOKneg: *pue = Neg(e->type, e1); break;
- case TOKtilde: *pue = Com(e->type, e1); break;
- case TOKnot: *pue = Not(e->type, e1); break;
- default: assert(0);
- }
- result = (*pue).exp();
- }
-
- void visit(DotTypeExp *e)
- {
- UnionExp ue;
- Expression *e1 = interpret(&ue, e->e1, istate);
- if (exceptionOrCant(e1))
- return;
-
- if (e1 == e->e1)
- result = e; // optimize: reuse this CTFE reference
- else
- {
- DotTypeExp *edt = (DotTypeExp *)e->copy();
- edt->e1 = (e1 == ue.exp()) ? e1->copy() : e1; // don't return pointer to ue
- result = edt;
- }
- }
-
- bool evalOperand(UnionExp *pue, Expression *e, Expression *ex, Expression *&er)
- {
- er = interpret(pue, ex, istate);
- if (exceptionOrCant(er))
- return false;
- if (er->isConst() != 1)
- {
- if (er->op == TOKarrayliteral)
- // Until we get it to work, issue a reasonable error message
- e->error("cannot interpret array literal expression %s at compile time", e->toChars());
- else
- e->error("CTFE internal error: non-constant value %s", ex->toChars());
- result = CTFEExp::cantexp;
- return false;
- }
- return true;
- }
-
- void interpretCommon(BinExp *e, fp_t fp)
- {
- if (e->e1->type->ty == Tpointer && e->e2->type->ty == Tpointer && e->op == TOKmin)
- {
- UnionExp ue1;
- Expression *e1 = interpret(&ue1, e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- UnionExp ue2;
- Expression *e2 = interpret(&ue2, e->e2, istate);
- if (exceptionOrCant(e2))
- return;
- *pue = pointerDifference(e->loc, e->type, e1, e2);
- result = (*pue).exp();
- return;
- }
- if (e->e1->type->ty == Tpointer && e->e2->type->isintegral())
- {
- UnionExp ue1;
- Expression *e1 = interpret(&ue1, e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- UnionExp ue2;
- Expression *e2 = interpret(&ue2, e->e2, istate);
- if (exceptionOrCant(e2))
- return;
- *pue = pointerArithmetic(e->loc, e->op, e->type, e1, e2);
- result = (*pue).exp();
- return;
- }
- if (e->e2->type->ty == Tpointer && e->e1->type->isintegral() && e->op == TOKadd)
- {
- UnionExp ue1;
- Expression *e1 = interpret(&ue1, e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- UnionExp ue2;
- Expression *e2 = interpret(&ue2, e->e2, istate);
- if (exceptionOrCant(e2))
- return;
- *pue = pointerArithmetic(e->loc, e->op, e->type, e2, e1);
- result = (*pue).exp();
- return;
- }
- if (e->e1->type->ty == Tpointer || e->e2->type->ty == Tpointer)
- {
- e->error("pointer expression %s cannot be interpreted at compile time", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- UnionExp ue1;
- Expression *e1;
- if (!evalOperand(&ue1, e, e->e1, e1))
- return;
- UnionExp ue2;
- Expression *e2;
- if (!evalOperand(&ue2, e, e->e2, e2))
- return;
-
- if (e->op == TOKshr || e->op == TOKshl || e->op == TOKushr)
- {
- const sinteger_t i2 = e2->toInteger();
- const d_uns64 sz = e1->type->size() * 8;
- if (i2 < 0 || (d_uns64)i2 >= sz)
- {
- e->error("shift by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1);
- result = CTFEExp::cantexp;
- return;
- }
- }
- *pue = (*fp)(e->loc, e->type, e1, e2);
- result = (*pue).exp();
- if (CTFEExp::isCantExp(result))
- e->error("%s cannot be interpreted at compile time", e->toChars());
- }
-
- void interpretCompareCommon(BinExp *e, fp2_t fp)
- {
- UnionExp ue1;
- UnionExp ue2;
- if (e->e1->type->ty == Tpointer && e->e2->type->ty == Tpointer)
- {
- Expression *e1 = interpret(&ue1, e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- Expression *e2 = interpret(&ue2, e->e2, istate);
- if (exceptionOrCant(e2))
- return;
- //printf("e1 = %s %s, e2 = %s %s\n", e1->type->toChars(), e1->toChars(), e2->type->toChars(), e2->toChars());
- dinteger_t ofs1, ofs2;
- Expression *agg1 = getAggregateFromPointer(e1, &ofs1);
- Expression *agg2 = getAggregateFromPointer(e2, &ofs2);
- //printf("agg1 = %p %s, agg2 = %p %s\n", agg1, agg1->toChars(), agg2, agg2->toChars());
- const int cmp = comparePointers(e->op, agg1, ofs1, agg2, ofs2);
- if (cmp == -1)
- {
- char dir = (e->op == TOKgt || e->op == TOKge) ? '<' : '>';
- e->error("the ordering of pointers to unrelated memory blocks is indeterminate in CTFE."
- " To check if they point to the same memory block, use both > and < inside && or ||, "
- "eg (%s && %s %c= %s + 1)",
- e->toChars(), e->e1->toChars(), dir, e->e2->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- new(pue) IntegerExp(e->loc, cmp, e->type);
- result = (*pue).exp();
- return;
- }
- Expression *e1 = interpret(&ue1, e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- if (!isCtfeComparable(e1))
- {
- e->error("cannot compare %s at compile time", e1->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- Expression *e2 = interpret(&ue2, e->e2, istate);
- if (exceptionOrCant(e2))
- return;
- if (!isCtfeComparable(e2))
- {
- e->error("cannot compare %s at compile time", e2->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- const int cmp = (*fp)(e->loc, e->op, e1, e2);
- new(pue) IntegerExp(e->loc, cmp, e->type);
- result = (*pue).exp();
- }
-
- void visit(BinExp *e)
- {
- switch (e->op)
- {
- case TOKadd: interpretCommon(e, &Add); return;
- case TOKmin: interpretCommon(e, &Min); return;
- case TOKmul: interpretCommon(e, &Mul); return;
- case TOKdiv: interpretCommon(e, &Div); return;
- case TOKmod: interpretCommon(e, &Mod); return;
- case TOKshl: interpretCommon(e, &Shl); return;
- case TOKshr: interpretCommon(e, &Shr); return;
- case TOKushr: interpretCommon(e, &Ushr); return;
- case TOKand: interpretCommon(e, &And); return;
- case TOKor: interpretCommon(e, &Or); return;
- case TOKxor: interpretCommon(e, &Xor); return;
- case TOKpow: interpretCommon(e, &Pow); return;
- case TOKequal:
- case TOKnotequal:
- interpretCompareCommon(e, &ctfeEqual);
- return;
- case TOKidentity:
- case TOKnotidentity:
- interpretCompareCommon(e, &ctfeIdentity);
- return;
- case TOKlt:
- case TOKle:
- case TOKgt:
- case TOKge:
- interpretCompareCommon(e, &ctfeCmp);
- return;
- default:
- printf("be = '%s' %s at [%s]\n", Token::toChars(e->op), e->toChars(), e->loc.toChars());
- assert(0);
- return;
- }
- }
-
- /* Helper functions for BinExp::interpretAssignCommon
- */
-
- // Returns the variable which is eventually modified, or NULL if an rvalue.
- // thisval is the current value of 'this'.
- static VarDeclaration *findParentVar(Expression *e)
- {
- for (;;)
- {
- if (e->op == TOKvar)
- break;
- if (e->op == TOKindex)
- e = ((IndexExp *)e)->e1;
- else if (e->op == TOKdotvar)
- e = ((DotVarExp *)e)->e1;
- else if (e->op == TOKdotti)
- e = ((DotTemplateInstanceExp *)e)->e1;
- else if (e->op == TOKslice)
- e = ((SliceExp *)e)->e1;
- else
- return NULL;
- }
- VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration();
- assert(v);
- return v;
- }
-
- void interpretAssignCommon(BinExp *e, fp_t fp, int post = 0)
- {
- result = CTFEExp::cantexp;
- Expression *e1 = e->e1;
- if (!istate)
- {
- e->error("value of %s is not known at compile time", e1->toChars());
- return;
- }
-
- ++CtfeStatus::numAssignments;
-
- /* Before we begin, we need to know if this is a reference assignment
- * (dynamic array, AA, or class) or a value assignment.
- * Determining this for slice assignments are tricky: we need to know
- * if it is a block assignment (a[] = e) rather than a direct slice
- * assignment (a[] = b[]). Note that initializers of multi-dimensional
- * static arrays can have 2D block assignments (eg, int[7][7] x = 6;).
- * So we need to recurse to determine if it is a block assignment.
- */
- bool isBlockAssignment = false;
- if (e1->op == TOKslice)
- {
- // a[] = e can have const e. So we compare the naked types.
- Type *tdst = e1->type->toBasetype();
- Type *tsrc = e->e2->type->toBasetype();
- while (tdst->ty == Tsarray || tdst->ty == Tarray)
- {
- tdst = ((TypeArray *)tdst)->next->toBasetype();
- if (tsrc->equivalent(tdst))
- {
- isBlockAssignment = true;
- break;
- }
- }
- }
-
- // ---------------------------------------
- // Deal with reference assignment
- // ---------------------------------------
- // If it is a construction of a ref variable, it is a ref assignment
- if ((e->op == TOKconstruct || e->op == TOKblit) &&
- (((AssignExp *)e)->memset & referenceInit))
- {
- assert(!fp);
-
- Expression *newval = interpret(e->e2, istate, ctfeNeedLvalue);
- if (exceptionOrCant(newval))
- return;
-
- VarDeclaration *v = ((VarExp *)e1)->var->isVarDeclaration();
- setValue(v, newval);
-
- // Get the value to return. Note that 'newval' is an Lvalue,
- // so if we need an Rvalue, we have to interpret again.
- if (goal == ctfeNeedRvalue)
- result = interpret(newval, istate);
- else
- result = e1; // VarExp is a CTFE reference
- return;
- }
-
- if (fp)
- {
- while (e1->op == TOKcast)
- {
- CastExp *ce = (CastExp *)e1;
- e1 = ce->e1;
- }
- }
-
- // ---------------------------------------
- // Interpret left hand side
- // ---------------------------------------
- AssocArrayLiteralExp *existingAA = NULL;
- Expression *lastIndex = NULL;
- Expression *oldval = NULL;
- if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
- {
- // ---------------------------------------
- // Deal with AA index assignment
- // ---------------------------------------
- /* This needs special treatment if the AA doesn't exist yet.
- * There are two special cases:
- * (1) If the AA is itself an index of another AA, we may need to create
- * multiple nested AA literals before we can insert the new value.
- * (2) If the ultimate AA is null, no insertion happens at all. Instead,
- * we create nested AA literals, and change it into a assignment.
- */
- IndexExp *ie = (IndexExp *)e1;
- int depth = 0; // how many nested AA indices are there?
- while (ie->e1->op == TOKindex &&
- ((IndexExp *)ie->e1)->e1->type->toBasetype()->ty == Taarray)
- {
- assert(ie->modifiable);
- ie = (IndexExp *)ie->e1;
- ++depth;
- }
-
- // Get the AA value to be modified.
- Expression *aggregate = interpret(ie->e1, istate);
- if (exceptionOrCant(aggregate))
- return;
- if (aggregate->op == TOKassocarrayliteral)
- {
- existingAA = (AssocArrayLiteralExp *)aggregate;
-
- // Normal case, ultimate parent AA already exists
- // We need to walk from the deepest index up, checking that an AA literal
- // already exists on each level.
- lastIndex = interpret(((IndexExp *)e1)->e2, istate);
- lastIndex = resolveSlice(lastIndex); // only happens with AA assignment
- if (exceptionOrCant(lastIndex))
- return;
-
- while (depth > 0)
- {
- // Walk the syntax tree to find the indexExp at this depth
- IndexExp *xe = (IndexExp *)e1;
- for (int d= 0; d < depth; ++d)
- xe = (IndexExp *)xe->e1;
-
- Expression *ekey = interpret(xe->e2, istate);
- if (exceptionOrCant(ekey))
- return;
- UnionExp ekeyTmp;
- ekey = resolveSlice(ekey, &ekeyTmp); // only happens with AA assignment
-
- // Look up this index in it up in the existing AA, to get the next level of AA.
- AssocArrayLiteralExp *newAA = (AssocArrayLiteralExp *)findKeyInAA(e->loc, existingAA, ekey);
- if (exceptionOrCant(newAA))
- return;
- if (!newAA)
- {
- // Doesn't exist yet, create an empty AA...
- Expressions *keysx = new Expressions();
- Expressions *valuesx = new Expressions();
- newAA = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
- newAA->type = xe->type;
- newAA->ownedByCtfe = OWNEDctfe;
- //... and insert it into the existing AA.
- existingAA->keys->push(ekey);
- existingAA->values->push(newAA);
- }
- existingAA = newAA;
- --depth;
- }
-
- if (fp)
- {
- oldval = findKeyInAA(e->loc, existingAA, lastIndex);
- if (!oldval)
- oldval = copyLiteral(e->e1->type->defaultInitLiteral(e->loc)).copy();
- }
- }
- else
- {
- /* The AA is currently null. 'aggregate' is actually a reference to
- * whatever contains it. It could be anything: var, dotvarexp, ...
- * We rewrite the assignment from:
- * aa[i][j] op= newval;
- * into:
- * aa = [i:[j:T.init]];
- * aa[j] op= newval;
- */
- oldval = copyLiteral(e->e1->type->defaultInitLiteral(e->loc)).copy();
-
- Expression *newaae = oldval;
- while (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
- {
- Expression *ekey = interpret(((IndexExp *)e1)->e2, istate);
- if (exceptionOrCant(ekey))
- return;
- ekey = resolveSlice(ekey); // only happens with AA assignment
- Expressions *keysx = new Expressions();
- Expressions *valuesx = new Expressions();
- keysx->push(ekey);
- valuesx->push(newaae);
- AssocArrayLiteralExp *aae = new AssocArrayLiteralExp(e->loc, keysx, valuesx);
- aae->type = ((IndexExp *)e1)->e1->type;
- aae->ownedByCtfe = OWNEDctfe;
- if (!existingAA)
- {
- existingAA = aae;
- lastIndex = ekey;
- }
- newaae = aae;
- e1 = ((IndexExp *)e1)->e1;
- }
-
- // We must set to aggregate with newaae
- e1 = interpret(e1, istate, ctfeNeedLvalue);
- if (exceptionOrCant(e1))
- return;
- e1 = assignToLvalue(e, e1, newaae);
- if (exceptionOrCant(e1))
- return;
- }
- assert(existingAA && lastIndex);
- e1 = NULL; // stomp
- }
- else if (e1->op == TOKarraylength)
- {
- oldval = interpret(e1, istate);
- if (exceptionOrCant(oldval))
- return;
- }
- else if (e->op == TOKconstruct || e->op == TOKblit)
- {
- // Unless we have a simple var assignment, we're
- // only modifying part of the variable. So we need to make sure
- // that the parent variable exists.
- VarDeclaration *ultimateVar = findParentVar(e1);
- if (e1->op == TOKvar)
- {
- VarDeclaration *v = ((VarExp *)e1)->var->isVarDeclaration();
- assert(v);
- if (v->storage_class & STCout)
- goto L1;
- }
- else if (ultimateVar && !getValue(ultimateVar))
- {
- Expression *ex = interpret(ultimateVar->type->defaultInitLiteral(e->loc), istate);
- if (exceptionOrCant(ex))
- return;
- setValue(ultimateVar, ex);
- }
- else
- goto L1;
- }
- else
- {
- L1:
- e1 = interpret(e1, istate, ctfeNeedLvalue);
- if (exceptionOrCant(e1))
- return;
-
- if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray)
- {
- IndexExp *ie = (IndexExp *)e1;
- assert(ie->e1->op == TOKassocarrayliteral);
- existingAA = (AssocArrayLiteralExp *)ie->e1;
- lastIndex = ie->e2;
- }
- }
-
- // ---------------------------------------
- // Interpret right hand side
- // ---------------------------------------
- Expression *newval = interpret(e->e2, istate);
- if (exceptionOrCant(newval))
- return;
- if (e->op == TOKblit && newval->op == TOKint64)
- {
- Type *tbn = e->type->baseElemOf();
- if (tbn->ty == Tstruct)
- {
- /* Look for special case of struct being initialized with 0.
- */
- newval = e->type->defaultInitLiteral(e->loc);
- if (newval->op == TOKerror)
- {
- result = CTFEExp::cantexp;
- return;
- }
- newval = interpret(newval, istate); // copy and set ownedByCtfe flag
- if (exceptionOrCant(newval))
- return;
- }
- }
-
- // ----------------------------------------------------
- // Deal with read-modify-write assignments.
- // Set 'newval' to the final assignment value
- // Also determine the return value (except for slice
- // assignments, which are more complicated)
- // ----------------------------------------------------
- if (fp)
- {
- if (!oldval)
- {
- // Load the left hand side after interpreting the right hand side.
- oldval = interpret(e1, istate);
- if (exceptionOrCant(oldval))
- return;
- }
-
- if (e->e1->type->ty != Tpointer)
- {
- // ~= can create new values (see bug 6052)
- if (e->op == TOKcatass)
- {
- // We need to dup it and repaint the type. For a dynamic array
- // we can skip duplication, because it gets copied later anyway.
- if (newval->type->ty != Tarray)
- {
- newval = copyLiteral(newval).copy();
- newval->type = e->e2->type; // repaint type
- }
- else
- {
- newval = paintTypeOntoLiteral(e->e2->type, newval);
- newval = resolveSlice(newval);
- }
- }
- oldval = resolveSlice(oldval);
-
- newval = (*fp)(e->loc, e->type, oldval, newval).copy();
- }
- else if (e->e2->type->isintegral() &&
- (e->op == TOKaddass ||
- e->op == TOKminass ||
- e->op == TOKplusplus ||
- e->op == TOKminusminus))
- {
- newval = pointerArithmetic(e->loc, e->op, e->type, oldval, newval).copy();
- }
- else
- {
- e->error("pointer expression %s cannot be interpreted at compile time", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (exceptionOrCant(newval))
- {
- if (CTFEExp::isCantExp(newval))
- e->error("cannot interpret %s at compile time", e->toChars());
- return;
- }
- }
-
- if (existingAA)
- {
- if (existingAA->ownedByCtfe != OWNEDctfe)
- {
- e->error("cannot modify read-only constant %s", existingAA->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- //printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n",
- // __LINE__, existingAA->toChars(), lastIndex->toChars(), oldval ? oldval->toChars() : NULL, newval->toChars());
- assignAssocArrayElement(e->loc, existingAA, lastIndex, newval);
-
- // Determine the return value
- result = ctfeCast(pue, e->loc, e->type, e->type, fp && post ? oldval : newval);
- return;
- }
- if (e1->op == TOKarraylength)
- {
- /* Change the assignment from:
- * arr.length = n;
- * into:
- * arr = new_length_array; (result is n)
- */
-
- // Determine the return value
- result = ctfeCast(pue, e->loc, e->type, e->type, fp && post ? oldval : newval);
- if (exceptionOrCant(result))
- return;
-
- if (result == pue->exp())
- result = pue->copy();
-
- size_t oldlen = (size_t)oldval->toInteger();
- size_t newlen = (size_t)newval->toInteger();
- if (oldlen == newlen) // no change required -- we're done!
- return;
-
- // We have changed it into a reference assignment
- // Note that returnValue is still the new length.
- e1 = ((ArrayLengthExp *)e1)->e1;
- Type *t = e1->type->toBasetype();
- if (t->ty != Tarray)
- {
- e->error("%s is not yet supported at compile time", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- e1 = interpret(e1, istate, ctfeNeedLvalue);
- if (exceptionOrCant(e1))
- return;
-
- if (oldlen != 0) // Get the old array literal.
- oldval = interpret(e1, istate);
- newval = changeArrayLiteralLength(e->loc, (TypeArray *)t, oldval,
- oldlen, newlen).copy();
-
- e1 = assignToLvalue(e, e1, newval);
- if (exceptionOrCant(e1))
- return;
-
- return;
- }
-
- if (!isBlockAssignment)
- {
- newval = ctfeCast(pue, e->loc, e->type, e->type, newval);
- if (exceptionOrCant(newval))
- return;
- if (newval == pue->exp())
- newval = pue->copy();
-
- // Determine the return value
- if (goal == ctfeNeedLvalue) // Bugzilla 14371
- result = e1;
- else
- {
- result = ctfeCast(pue, e->loc, e->type, e->type, fp && post ? oldval : newval);
- if (result == pue->exp())
- result = pue->copy();
- }
- if (exceptionOrCant(result))
- return;
- }
- if (exceptionOrCant(newval))
- return;
-
- /* Block assignment or element-wise assignment.
- */
- if (e1->op == TOKslice ||
- e1->op == TOKvector ||
- e1->op == TOKarrayliteral ||
- e1->op == TOKstring ||
- (e1->op == TOKnull && e1->type->toBasetype()->ty == Tarray))
- {
- // Note that slice assignments don't support things like ++, so
- // we don't need to remember 'returnValue'.
- result = interpretAssignToSlice(pue, e, e1, newval, isBlockAssignment);
- if (exceptionOrCant(result))
- return;
- if (e->e1->op == TOKslice)
- {
- Expression *e1x = interpret(((SliceExp*)e->e1)->e1, istate, ctfeNeedLvalue);
- if (e1x->op == TOKdotvar)
- {
- DotVarExp *dve = (DotVarExp*)e1x;
- Expression *ex = dve->e1;
- StructLiteralExp *sle = ex->op == TOKstructliteral ? ((StructLiteralExp *)ex)
- : ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value
- : NULL;
- VarDeclaration *v = dve->var->isVarDeclaration();
- if (!sle || !v)
- {
- e->error("CTFE internal error: dotvar slice assignment");
- result = CTFEExp::cantexp;
- return;
- }
- stompOverlappedFields(sle, v);
- }
- }
- return;
- }
-
- assert(result);
-
- /* Assignment to a CTFE reference.
- */
- if (Expression *ex = assignToLvalue(e, e1, newval))
- result = ex;
-
- return;
- }
-
- /* Set all sibling fields which overlap with v to VoidExp.
- */
- void stompOverlappedFields(StructLiteralExp *sle, VarDeclaration *v)
- {
- if (!v->overlapped)
- return;
-
- for (size_t i = 0; i < sle->sd->fields.length; i++)
- {
- VarDeclaration *v2 = sle->sd->fields[i];
- if (v == v2 || !v->isOverlappedWith(v2))
- continue;
- Expression *e = (*sle->elements)[i];
- if (e->op != TOKvoid)
- (*sle->elements)[i] = voidInitLiteral(e->type, v).copy();
- }
- }
-
- Expression *assignToLvalue(BinExp *e, Expression *e1, Expression *newval)
- {
- VarDeclaration *vd = NULL;
- Expression **payload = NULL; // dead-store to prevent spurious warning
- Expression *oldval;
-
- if (e1->op == TOKvar)
- {
- vd = ((VarExp *)e1)->var->isVarDeclaration();
- oldval = getValue(vd);
- }
- else if (e1->op == TOKdotvar)
- {
- /* Assignment to member variable of the form:
- * e.v = newval
- */
- Expression *ex = ((DotVarExp *)e1)->e1;
- StructLiteralExp *sle =
- ex->op == TOKstructliteral ? ((StructLiteralExp *)ex):
- ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value
- : NULL;
- VarDeclaration *v = ((DotVarExp *)e1)->var->isVarDeclaration();
- if (!sle || !v)
- {
- e->error("CTFE internal error: dotvar assignment");
- return CTFEExp::cantexp;
- }
- if (sle->ownedByCtfe != OWNEDctfe)
- {
- e->error("cannot modify read-only constant %s", sle->toChars());
- return CTFEExp::cantexp;
- }
-
- int fieldi = ex->op == TOKstructliteral
- ? findFieldIndexByName(sle->sd, v)
- : ((ClassReferenceExp *)ex)->findFieldIndexByName(v);
- if (fieldi == -1)
- {
- e->error("CTFE internal error: cannot find field %s in %s", v->toChars(), ex->toChars());
- return CTFEExp::cantexp;
- }
- assert(0 <= fieldi && fieldi < (int)sle->elements->length);
-
- // If it's a union, set all other members of this union to void
- stompOverlappedFields(sle, v);
-
- payload = &(*sle->elements)[fieldi];
- oldval = *payload;
- }
- else if (e1->op == TOKindex)
- {
- IndexExp *ie = (IndexExp *)e1;
- assert(ie->e1->type->toBasetype()->ty != Taarray);
-
- Expression *aggregate;
- uinteger_t indexToModify;
- if (!resolveIndexing(ie, istate, &aggregate, &indexToModify, true))
- {
- return CTFEExp::cantexp;
- }
- size_t index = (size_t)indexToModify;
-
- if (aggregate->op == TOKstring)
- {
- StringExp *existingSE = (StringExp *)aggregate;
- if (existingSE->ownedByCtfe != OWNEDctfe)
- {
- e->error("cannot modify read-only string literal %s", ie->e1->toChars());
- return CTFEExp::cantexp;
- }
- void *s = existingSE->string;
- dinteger_t value = newval->toInteger();
- switch (existingSE->sz)
- {
- case 1: (( utf8_t *)s)[index] = ( utf8_t)value; break;
- case 2: ((utf16_t *)s)[index] = (utf16_t)value; break;
- case 4: ((utf32_t *)s)[index] = (utf32_t)value; break;
- default: assert(0); break;
- }
- return NULL;
- }
- if (aggregate->op != TOKarrayliteral)
- {
- e->error("index assignment %s is not yet supported in CTFE ", e->toChars());
- return CTFEExp::cantexp;
- }
-
- ArrayLiteralExp *existingAE = (ArrayLiteralExp *)aggregate;
- if (existingAE->ownedByCtfe != OWNEDctfe)
- {
- e->error("cannot modify read-only constant %s", existingAE->toChars());
- return CTFEExp::cantexp;
- }
-
- payload = &(*existingAE->elements)[index];
- oldval = *payload;
- }
- else
- {
- e->error("%s cannot be evaluated at compile time", e->toChars());
- return CTFEExp::cantexp;
- }
-
- Type *t1b = e1->type->toBasetype();
- bool wantCopy = t1b->baseElemOf()->ty == Tstruct;
-
- if (newval->op == TOKstructliteral && oldval)
- {
- newval = copyLiteral(newval).copy();
- assignInPlace(oldval, newval);
- }
- else if (wantCopy && e->op == TOKassign)
- {
- // Currently postblit/destructor calls on static array are done
- // in the druntime internal functions so they don't appear in AST.
- // Therefore interpreter should handle them specially.
-
- assert(oldval);
- #if 1 // todo: instead we can directly access to each elements of the slice
- newval = resolveSlice(newval);
- if (CTFEExp::isCantExp(newval))
- {
- e->error("CTFE internal error: assignment %s", e->toChars());
- return CTFEExp::cantexp;
- }
- #endif
- assert(oldval->op == TOKarrayliteral);
- assert(newval->op == TOKarrayliteral);
-
- Expressions *oldelems = ((ArrayLiteralExp *)oldval)->elements;
- Expressions *newelems = ((ArrayLiteralExp *)newval)->elements;
- assert(oldelems->length == newelems->length);
-
- Type *elemtype = oldval->type->nextOf();
- for (size_t i = 0; i < newelems->length; i++)
- {
- Expression *oldelem = (*oldelems)[i];
- Expression *newelem = paintTypeOntoLiteral(elemtype, (*newelems)[i]);
- // Bugzilla 9245
- if (e->e2->isLvalue())
- {
- if (Expression *ex = evaluatePostblit(istate, newelem))
- return ex;
- }
- // Bugzilla 13661
- if (Expression *ex = evaluateDtor(istate, oldelem))
- return ex;
- (*oldelems)[i] = newelem;
- }
- }
- else
- {
- // e1 has its own payload, so we have to create a new literal.
- if (wantCopy)
- newval = copyLiteral(newval).copy();
-
- if (t1b->ty == Tsarray && e->op == TOKconstruct && e->e2->isLvalue())
- {
- // Bugzilla 9245
- if (Expression *ex = evaluatePostblit(istate, newval))
- return ex;
- }
-
- oldval = newval;
- }
-
- if (vd)
- setValue(vd, oldval);
- else
- *payload = oldval;
-
- // Blit assignment should return the newly created value.
- if (e->op == TOKblit)
- return oldval;
-
- return NULL;
- }
-
- /*************
- * Deal with assignments of the form:
- * dest[] = newval
- * dest[low..upp] = newval
- * where newval has already been interpreted
- *
- * This could be a slice assignment or a block assignment, and
- * dest could be either an array literal, or a string.
- *
- * Returns TOKcantexp on failure. If there are no errors,
- * it returns aggregate[low..upp], except that as an optimisation,
- * if goal == ctfeNeedNothing, it will return NULL
- */
- Expression *interpretAssignToSlice(UnionExp *pue, BinExp *e,
- Expression *e1, Expression *newval, bool isBlockAssignment)
- {
- dinteger_t lowerbound;
- dinteger_t upperbound;
-
- Expression *aggregate;
- dinteger_t firstIndex;
-
- if (e1->op == TOKslice)
- {
- // ------------------------------
- // aggregate[] = newval
- // aggregate[low..upp] = newval
- // ------------------------------
-
- SliceExp *se = (SliceExp *)e1;
- #if 1 // should be move in interpretAssignCommon as the evaluation of e1
- Expression *oldval = interpret(se->e1, istate);
-
- // Set the $ variable
- uinteger_t dollar = resolveArrayLength(oldval);
- if (se->lengthVar)
- {
- Expression *dollarExp = new IntegerExp(e1->loc, dollar, Type::tsize_t);
- ctfeStack.push(se->lengthVar);
- setValue(se->lengthVar, dollarExp);
- }
- Expression *lwr = interpret(se->lwr, istate);
- if (exceptionOrCantInterpret(lwr))
- {
- if (se->lengthVar)
- ctfeStack.pop(se->lengthVar);
- return lwr;
- }
- Expression *upr = interpret(se->upr, istate);
- if (exceptionOrCantInterpret(upr))
- {
- if (se->lengthVar)
- ctfeStack.pop(se->lengthVar);
- return upr;
- }
- if (se->lengthVar)
- ctfeStack.pop(se->lengthVar); // $ is defined only in [L..U]
-
- unsigned dim = (unsigned)dollar;
- lowerbound = (int)(lwr ? lwr->toInteger() : 0);
- upperbound = (size_t)(upr ? upr->toInteger() : dim);
-
- if ((int)lowerbound < 0 || dim < upperbound)
- {
- e->error("array bounds [0..%d] exceeded in slice [%d..%d]",
- dim, lowerbound, upperbound);
- return CTFEExp::cantexp;
- }
- #endif
- aggregate = oldval;
- firstIndex = lowerbound;
-
- if (aggregate->op == TOKslice)
- {
- // Slice of a slice --> change the bounds
- SliceExp *oldse = (SliceExp *)aggregate;
- if (oldse->upr->toInteger() < upperbound + oldse->lwr->toInteger())
- {
- e->error("slice [%d..%d] exceeds array bounds [0..%lld]",
- lowerbound, upperbound,
- oldse->upr->toInteger() - oldse->lwr->toInteger());
- return CTFEExp::cantexp;
- }
- aggregate = oldse->e1;
- firstIndex = lowerbound + oldse->lwr->toInteger();
- }
- }
- else
- {
- if (e1->op == TOKarrayliteral)
- {
- lowerbound = 0;
- upperbound = ((ArrayLiteralExp *)e1)->elements->length;
- }
- else if (e1->op == TOKstring)
- {
- lowerbound = 0;
- upperbound = ((StringExp *)e1)->len;
- }
- else if (e1->op == TOKnull)
- {
- lowerbound = 0;
- upperbound = 0;
- }
- else
- assert(0);
-
- aggregate = e1;
- firstIndex = lowerbound;
- }
- if (upperbound == lowerbound)
- return newval;
-
- // For slice assignment, we check that the lengths match.
- if (!isBlockAssignment)
- {
- size_t srclen = (size_t)resolveArrayLength(newval);
- if (srclen != (upperbound - lowerbound))
- {
- e->error("array length mismatch assigning [0..%d] to [%d..%d]",
- srclen, lowerbound, upperbound);
- return CTFEExp::cantexp;
- }
- }
-
- if (aggregate->op == TOKstring)
- {
- StringExp *existingSE = (StringExp *)aggregate;
- if (existingSE->ownedByCtfe != OWNEDctfe)
- {
- e->error("cannot modify read-only string literal %s", existingSE->toChars());
- return CTFEExp::cantexp;
- }
-
- if (newval->op == TOKslice)
- {
- SliceExp *se = (SliceExp *)newval;
- Expression *aggr2 = se->e1;
- const dinteger_t srclower = se->lwr->toInteger();
- const dinteger_t srcupper = se->upr->toInteger();
-
- if (aggregate == aggr2 &&
- lowerbound < srcupper && srclower < upperbound)
- {
- e->error("overlapping slice assignment [%d..%d] = [%llu..%llu]",
- lowerbound, upperbound, srclower, srcupper);
- return CTFEExp::cantexp;
- }
- #if 1 // todo: instead we can directly access to each elements of the slice
- Expression *orignewval = newval;
- newval = resolveSlice(newval);
- if (CTFEExp::isCantExp(newval))
- {
- e->error("CTFE internal error: slice %s", orignewval->toChars());
- return CTFEExp::cantexp;
- }
- #endif
- assert(newval->op != TOKslice);
- }
- if (newval->op == TOKstring)
- {
- sliceAssignStringFromString((StringExp *)existingSE, (StringExp *)newval, (size_t)firstIndex);
- return newval;
- }
- if (newval->op == TOKarrayliteral)
- {
- /* Mixed slice: it was initialized as a string literal.
- * Now a slice of it is being set with an array literal.
- */
- sliceAssignStringFromArrayLiteral(existingSE, (ArrayLiteralExp *)newval, (size_t)firstIndex);
- return newval;
- }
-
- // String literal block slice assign
- dinteger_t value = newval->toInteger();
- void *s = existingSE->string;
- for (size_t i = 0; i < upperbound - lowerbound; i++)
- {
- switch (existingSE->sz)
- {
- case 1: (( utf8_t *)s)[(size_t)(i + firstIndex)] = ( utf8_t)value; break;
- case 2: ((utf16_t *)s)[(size_t)(i + firstIndex)] = (utf16_t)value; break;
- case 4: ((utf32_t *)s)[(size_t)(i + firstIndex)] = (utf32_t)value; break;
- default: assert(0); break;
- }
- }
- if (goal == ctfeNeedNothing)
- return NULL; // avoid creating an unused literal
- SliceExp *retslice = new SliceExp(e->loc, existingSE,
- new IntegerExp(e->loc, firstIndex, Type::tsize_t),
- new IntegerExp(e->loc, firstIndex + upperbound - lowerbound, Type::tsize_t));
- retslice->type = e->type;
- return interpret(pue, retslice, istate);
- }
- if (aggregate->op == TOKarrayliteral)
- {
- ArrayLiteralExp *existingAE = (ArrayLiteralExp *)aggregate;
- if (existingAE->ownedByCtfe != OWNEDctfe)
- {
- e->error("cannot modify read-only constant %s", existingAE->toChars());
- return CTFEExp::cantexp;
- }
-
- if (newval->op == TOKslice && !isBlockAssignment)
- {
- SliceExp *se = (SliceExp *)newval;
- Expression *aggr2 = se->e1;
- const dinteger_t srclower = se->lwr->toInteger();
- const dinteger_t srcupper = se->upr->toInteger();
- const bool wantCopy = (newval->type->toBasetype()->nextOf()->baseElemOf()->ty == Tstruct);
-
- //printf("oldval = %p %s[%d..%u]\nnewval = %p %s[%llu..%llu] wantCopy = %d\n",
- // aggregate, aggregate->toChars(), lowerbound, upperbound,
- // aggr2, aggr2->toChars(), srclower, srcupper, wantCopy);
- if (wantCopy)
- {
- // Currently overlapping for struct array is allowed.
- // The order of elements processing depends on the overlapping.
- // See bugzilla 14024.
- assert(aggr2->op == TOKarrayliteral);
- Expressions *oldelems = existingAE->elements;
- Expressions *newelems = ((ArrayLiteralExp *)aggr2)->elements;
-
- Type *elemtype = aggregate->type->nextOf();
- bool needsPostblit = e->e2->isLvalue();
-
- if (aggregate == aggr2 &&
- srclower < lowerbound && lowerbound < srcupper)
- {
- // reverse order
- for (size_t i = upperbound - lowerbound; 0 < i--; )
- {
- Expression *oldelem = (*oldelems)[(size_t)(i + firstIndex)];
- Expression *newelem = (*newelems)[(size_t)(i + srclower)];
- newelem = copyLiteral(newelem).copy();
- newelem->type = elemtype;
- if (needsPostblit)
- {
- if (Expression *x = evaluatePostblit(istate, newelem))
- return x;
- }
- if (Expression *x = evaluateDtor(istate, oldelem))
- return x;
- (*oldelems)[lowerbound + i] = newelem;
- }
- }
- else
- {
- // normal order
- for (size_t i = 0; i < upperbound - lowerbound; i++)
- {
- Expression *oldelem = (*oldelems)[(size_t)(i + firstIndex)];
- Expression *newelem = (*newelems)[(size_t)(i + srclower)];
- newelem = copyLiteral(newelem).copy();
- newelem->type = elemtype;
- if (needsPostblit)
- {
- if (Expression *x = evaluatePostblit(istate, newelem))
- return x;
- }
- if (Expression *x = evaluateDtor(istate, oldelem))
- return x;
- (*oldelems)[lowerbound + i] = newelem;
- }
- }
-
- //assert(0);
- return newval; // oldval?
- }
- if (aggregate == aggr2 &&
- lowerbound < srcupper && srclower < upperbound)
- {
- e->error("overlapping slice assignment [%d..%d] = [%llu..%llu]",
- lowerbound, upperbound, srclower, srcupper);
- return CTFEExp::cantexp;
- }
- #if 1 // todo: instead we can directly access to each elements of the slice
- Expression *orignewval = newval;
- newval = resolveSlice(newval);
- if (CTFEExp::isCantExp(newval))
- {
- e->error("CTFE internal error: slice %s", orignewval->toChars());
- return CTFEExp::cantexp;
- }
- #endif
- // no overlapping
- //length?
- assert(newval->op != TOKslice);
- }
- if (newval->op == TOKstring && !isBlockAssignment)
- {
- /* Mixed slice: it was initialized as an array literal of chars/integers.
- * Now a slice of it is being set with a string.
- */
- sliceAssignArrayLiteralFromString(existingAE, (StringExp *)newval, (size_t)firstIndex);
- return newval;
- }
- if (newval->op == TOKarrayliteral && !isBlockAssignment)
- {
- Expressions *oldelems = existingAE->elements;
- Expressions *newelems = ((ArrayLiteralExp *)newval)->elements;
- Type *elemtype = existingAE->type->nextOf();
- bool needsPostblit = e->op != TOKblit && e->e2->isLvalue();
- for (size_t j = 0; j < newelems->length; j++)
- {
- Expression *newelem = (*newelems)[j];
- newelem = paintTypeOntoLiteral(elemtype, newelem);
- if (needsPostblit)
- {
- Expression *x = evaluatePostblit(istate, newelem);
- if (exceptionOrCantInterpret(x))
- return x;
- }
- (*oldelems)[(size_t)(j + firstIndex)] = newelem;
- }
- return newval;
- }
-
- /* Block assignment, initialization of static arrays
- * x[] = newval
- * x may be a multidimensional static array. (Note that this
- * only happens with array literals, never with strings).
- */
- struct RecursiveBlock
- {
- InterState *istate;
- Expression *newval;
- bool refCopy;
- bool needsPostblit;
- bool needsDtor;
-
- Expression *assignTo(ArrayLiteralExp *ae)
- {
- return assignTo(ae, 0, ae->elements->length);
- }
-
- Expression *assignTo(ArrayLiteralExp *ae, size_t lwr, size_t upr)
- {
- Expressions *w = ae->elements;
-
- assert(ae->type->ty == Tsarray ||
- ae->type->ty == Tarray);
- bool directblk = ((TypeArray *)ae->type)->next->equivalent(newval->type);
-
- for (size_t k = lwr; k < upr; k++)
- {
- if (!directblk && (*w)[k]->op == TOKarrayliteral)
- {
- // Multidimensional array block assign
- if (Expression *ex = assignTo((ArrayLiteralExp *)(*w)[k]))
- return ex;
- }
- else if (refCopy)
- {
- (*w)[k] = newval;
- }
- else if (!needsPostblit && !needsDtor)
- {
- assignInPlace((*w)[k], newval);
- }
- else
- {
- Expression *oldelem = (*w)[k];
- Expression *tmpelem = needsDtor ? copyLiteral(oldelem).copy() : NULL;
-
- assignInPlace(oldelem, newval);
-
- if (needsPostblit)
- {
- if (Expression *ex = evaluatePostblit(istate, oldelem))
- return ex;
- }
- if (needsDtor)
- {
- // Bugzilla 14860
- if (Expression *ex = evaluateDtor(istate, tmpelem))
- return ex;
- }
- }
- }
- return NULL;
- }
- };
-
- Type *tn = newval->type->toBasetype();
- bool wantRef = (tn->ty == Tarray || isAssocArray(tn) ||tn->ty == Tclass);
- bool cow = newval->op != TOKstructliteral &&
- newval->op != TOKarrayliteral &&
- newval->op != TOKstring;
- Type *tb = tn->baseElemOf();
- StructDeclaration *sd = (tb->ty == Tstruct ? ((TypeStruct *)tb)->sym : NULL);
-
- RecursiveBlock rb;
- rb.istate = istate;
- rb.newval = newval;
- rb.refCopy = wantRef || cow;
- rb.needsPostblit = sd && sd->postblit && e->op != TOKblit && e->e2->isLvalue();
- rb.needsDtor = sd && sd->dtor && e->op == TOKassign;
-
- if (Expression *ex = rb.assignTo(existingAE, lowerbound, upperbound))
- return ex;
-
- if (goal == ctfeNeedNothing)
- return NULL; // avoid creating an unused literal
- SliceExp *retslice = new SliceExp(e->loc, existingAE,
- new IntegerExp(e->loc, firstIndex, Type::tsize_t),
- new IntegerExp(e->loc, firstIndex + upperbound - lowerbound, Type::tsize_t));
- retslice->type = e->type;
- return interpret(pue, retslice, istate);
- }
-
- e->error("slice operation %s = %s cannot be evaluated at compile time",
- e1->toChars(), newval->toChars());
- return CTFEExp::cantexp;
- }
-
- void visit(AssignExp *e)
- {
- interpretAssignCommon(e, NULL);
- }
-
- void visit(BinAssignExp *e)
- {
- switch (e->op)
- {
- case TOKaddass: interpretAssignCommon(e, &Add); return;
- case TOKminass: interpretAssignCommon(e, &Min); return;
- case TOKcatass: interpretAssignCommon(e, &ctfeCat); return;
- case TOKmulass: interpretAssignCommon(e, &Mul); return;
- case TOKdivass: interpretAssignCommon(e, &Div); return;
- case TOKmodass: interpretAssignCommon(e, &Mod); return;
- case TOKshlass: interpretAssignCommon(e, &Shl); return;
- case TOKshrass: interpretAssignCommon(e, &Shr); return;
- case TOKushrass: interpretAssignCommon(e, &Ushr); return;
- case TOKandass: interpretAssignCommon(e, &And); return;
- case TOKorass: interpretAssignCommon(e, &Or); return;
- case TOKxorass: interpretAssignCommon(e, &Xor); return;
- case TOKpowass: interpretAssignCommon(e, &Pow); return;
- default:
- assert(0);
- return;
- }
- }
-
- void visit(PostExp *e)
- {
- if (e->op == TOKplusplus)
- interpretAssignCommon(e, &Add, 1);
- else
- interpretAssignCommon(e, &Min, 1);
- }
-
- /* Return 1 if e is a p1 > p2 or p1 >= p2 pointer comparison;
- * -1 if e is a p1 < p2 or p1 <= p2 pointer comparison;
- * 0 otherwise
- */
- static int isPointerCmpExp(Expression *e, Expression **p1, Expression **p2)
- {
- int ret = 1;
- while (e->op == TOKnot)
- {
- ret *= -1;
- e = ((NotExp *)e)->e1;
- }
- switch (e->op)
- {
- case TOKlt:
- case TOKle:
- ret *= -1;
- /* fall through */
- case TOKgt:
- case TOKge:
- *p1 = ((BinExp *)e)->e1;
- *p2 = ((BinExp *)e)->e2;
- if (!(isPointer((*p1)->type) && isPointer((*p2)->type)))
- ret = 0;
- break;
- default:
- ret = 0;
- break;
- }
- return ret;
- }
-
- /** Negate a relational operator, eg >= becomes <
- */
- static TOK reverseRelation(TOK op)
- {
- switch (op)
- {
- case TOKge: return TOKlt;
- case TOKgt: return TOKle;
- case TOKle: return TOKgt;
- case TOKlt: return TOKge;
- default:
- return assert(0), TOKreserved;
- }
- }
-
- /** If this is a four pointer relation, evaluate it, else return NULL.
- *
- * This is an expression of the form (p1 > q1 && p2 < q2) or (p1 < q1 || p2 > q2)
- * where p1, p2 are expressions yielding pointers to memory block p,
- * and q1, q2 are expressions yielding pointers to memory block q.
- * This expression is valid even if p and q are independent memory
- * blocks and are therefore not normally comparable; the && form returns true
- * if [p1..p2] lies inside [q1..q2], and false otherwise; the || form returns
- * true if [p1..p2] lies outside [q1..q2], and false otherwise.
- *
- * Within the expression, any ordering of p1, p2, q1, q2 is permissible;
- * the comparison operators can be any of >, <, <=, >=, provided that
- * both directions (p > q and p < q) are checked. Additionally the
- * relational sub-expressions can be negated, eg
- * (!(q1 < p1) && p2 <= q2) is valid.
- */
- void interpretFourPointerRelation(UnionExp *pue, BinExp *e)
- {
- assert(e->op == TOKandand || e->op == TOKoror);
-
- /* It can only be an isInside expression, if both e1 and e2 are
- * directional pointer comparisons.
- * Note that this check can be made statically; it does not depends on
- * any runtime values. This allows a JIT implementation to compile a
- * special AndAndPossiblyInside, keeping the normal AndAnd case efficient.
- */
-
- // Save the pointer expressions and the comparison directions,
- // so we can use them later.
- Expression *p1 = NULL;
- Expression *p2 = NULL;
- Expression *p3 = NULL;
- Expression *p4 = NULL;
- int dir1 = isPointerCmpExp(e->e1, &p1, &p2);
- int dir2 = isPointerCmpExp(e->e2, &p3, &p4);
- if (dir1 == 0 || dir2 == 0)
- {
- result = NULL;
- return;
- }
-
- //printf("FourPointerRelation %s\n", toChars());
- UnionExp ue1;
- UnionExp ue2;
- UnionExp ue3;
- UnionExp ue4;
-
- // Evaluate the first two pointers
- p1 = interpret(&ue1, p1, istate);
- if (exceptionOrCant(p1))
- return;
- p2 = interpret(&ue2, p2, istate);
- if (exceptionOrCant(p2))
- return;
- dinteger_t ofs1, ofs2;
- Expression *agg1 = getAggregateFromPointer(p1, &ofs1);
- Expression *agg2 = getAggregateFromPointer(p2, &ofs2);
-
- if (!pointToSameMemoryBlock(agg1, agg2) &&
- agg1->op != TOKnull &&
- agg2->op != TOKnull)
- {
- // Here it is either CANT_INTERPRET,
- // or an IsInside comparison returning false.
- p3 = interpret(&ue3, p3, istate);
- if (CTFEExp::isCantExp(p3))
- return;
- // Note that it is NOT legal for it to throw an exception!
- Expression *except = NULL;
- if (exceptionOrCantInterpret(p3))
- except = p3;
- else
- {
- p4 = interpret(&ue4, p4, istate);
- if (CTFEExp::isCantExp(p4))
- {
- result = p4;
- return;
- }
- if (exceptionOrCantInterpret(p4))
- except = p4;
- }
- if (except)
- {
- e->error("comparison %s of pointers to unrelated memory blocks remains "
- "indeterminate at compile time "
- "because exception %s was thrown while evaluating %s",
- e->e1->toChars(), except->toChars(), e->e2->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- dinteger_t ofs3, ofs4;
- Expression *agg3 = getAggregateFromPointer(p3, &ofs3);
- Expression *agg4 = getAggregateFromPointer(p4, &ofs4);
- // The valid cases are:
- // p1 > p2 && p3 > p4 (same direction, also for < && <)
- // p1 > p2 && p3 < p4 (different direction, also < && >)
- // Changing any > into >= doesnt affect the result
- if ((dir1 == dir2 && pointToSameMemoryBlock(agg1, agg4) && pointToSameMemoryBlock(agg2, agg3)) ||
- (dir1 != dir2 && pointToSameMemoryBlock(agg1, agg3) && pointToSameMemoryBlock(agg2, agg4)))
- {
- // it's a legal two-sided comparison
- new(pue) IntegerExp(e->loc, (e->op == TOKandand) ? 0 : 1, e->type);
- result = pue->exp();
- return;
- }
- // It's an invalid four-pointer comparison. Either the second
- // comparison is in the same direction as the first, or else
- // more than two memory blocks are involved (either two independent
- // invalid comparisons are present, or else agg3 == agg4).
- e->error("comparison %s of pointers to unrelated memory blocks is "
- "indeterminate at compile time, even when combined with %s.",
- e->e1->toChars(), e->e2->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- // The first pointer expression didn't need special treatment, so we
- // we need to interpret the entire expression exactly as a normal && or ||.
- // This is easy because we haven't evaluated e2 at all yet, and we already
- // know it will return a bool.
- // But we mustn't evaluate the pointer expressions in e1 again, in case
- // they have side-effects.
- bool nott = false;
- Expression *ex = e->e1;
- while (ex->op == TOKnot)
- {
- nott = !nott;
- ex = ((NotExp *)ex)->e1;
- }
- const TOK cmpop = nott ? reverseRelation(ex->op) : ex->op;
- const int cmp = comparePointers(cmpop, agg1, ofs1, agg2, ofs2);
- // We already know this is a valid comparison.
- assert(cmp >= 0);
- if ((e->op == TOKandand && cmp == 1) ||
- (e->op == TOKoror && cmp == 0))
- {
- result = interpret(pue, e->e2, istate);
- return;
- }
- new(pue) IntegerExp(e->loc, (e->op == TOKandand) ? 0 : 1, e->type);
- result = pue->exp();
- }
-
- void visit(LogicalExp *e)
- {
- // Check for an insidePointer expression, evaluate it if so
- interpretFourPointerRelation(pue, e);
- if (result)
- return;
-
- result = interpret(e->e1, istate);
- if (exceptionOrCant(result))
- return;
-
- int res;
- const bool andand = e->op == TOKandand;
- if (andand ? result->isBool(false) : isTrueBool(result))
- res = !andand;
- else if (andand ? isTrueBool(result) : result->isBool(false))
- {
- UnionExp ue2;
- result = interpret(&ue2, e->e2, istate);
- if (exceptionOrCant(result))
- return;
- if (result->op == TOKvoidexp)
- {
- assert(e->type->ty == Tvoid);
- result = NULL;
- return;
- }
- if (result->isBool(false))
- res = 0;
- else if (isTrueBool(result))
- res = 1;
- else
- {
- result->error("`%s` does not evaluate to a boolean", result->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- }
- else
- {
- result->error("`%s` cannot be interpreted as a boolean", result->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (goal != ctfeNeedNothing)
- {
- new(pue) IntegerExp(e->loc, res, e->type);
- result = pue->exp();
- }
- }
-
- // Print a stack trace, starting from callingExp which called fd.
- // To shorten the stack trace, try to detect recursion.
- void showCtfeBackTrace(CallExp * callingExp, FuncDeclaration *fd)
- {
- if (CtfeStatus::stackTraceCallsToSuppress > 0)
- {
- --CtfeStatus::stackTraceCallsToSuppress;
- return;
- }
- errorSupplemental(callingExp->loc, "called from here: %s", callingExp->toChars());
- // Quit if it's not worth trying to compress the stack trace
- if (CtfeStatus::callDepth < 6 || global.params.verbose)
- return;
- // Recursion happens if the current function already exists in the call stack.
- int numToSuppress = 0;
- int recurseCount = 0;
- int depthSoFar = 0;
- InterState *lastRecurse = istate;
- for (InterState * cur = istate; cur; cur = cur->caller)
- {
- if (cur->fd == fd)
- {
- ++recurseCount;
- numToSuppress = depthSoFar;
- lastRecurse = cur;
- }
- ++depthSoFar;
- }
- // We need at least three calls to the same function, to make compression worthwhile
- if (recurseCount < 2)
- return;
- // We found a useful recursion. Print all the calls involved in the recursion
- errorSupplemental(fd->loc, "%d recursive calls to function %s", recurseCount, fd->toChars());
- for (InterState *cur = istate; cur->fd != fd; cur = cur->caller)
- {
- errorSupplemental(cur->fd->loc, "recursively called from function %s", cur->fd->toChars());
- }
- // We probably didn't enter the recursion in this function.
- // Go deeper to find the real beginning.
- InterState * cur = istate;
- while (lastRecurse->caller && cur->fd == lastRecurse->caller->fd)
- {
- cur = cur->caller;
- lastRecurse = lastRecurse->caller;
- ++numToSuppress;
- }
- CtfeStatus::stackTraceCallsToSuppress = numToSuppress;
- }
-
- void visit(CallExp *e)
- {
- Expression *pthis = NULL;
- FuncDeclaration *fd = NULL;
-
- Expression *ecall = interpret(e->e1, istate);
- if (exceptionOrCant(ecall))
- return;
-
- if (ecall->op == TOKdotvar)
- {
- DotVarExp *dve = (DotVarExp *)ecall;
-
- // Calling a member function
- pthis = dve->e1;
- fd = dve->var->isFuncDeclaration();
- assert(fd);
-
- if (pthis->op == TOKdottype)
- pthis = ((DotTypeExp *)dve->e1)->e1;
- }
- else if (ecall->op == TOKvar)
- {
- fd = ((VarExp *)ecall)->var->isFuncDeclaration();
- assert(fd);
-
- if (fd->ident == Id::__ArrayPostblit ||
- fd->ident == Id::__ArrayDtor)
- {
- assert(e->arguments->length == 1);
- Expression *ea = (*e->arguments)[0];
- //printf("1 ea = %s %s\n", ea->type->toChars(), ea->toChars());
- if (ea->op == TOKslice)
- ea = ((SliceExp *)ea)->e1;
- if (ea->op == TOKcast)
- ea = ((CastExp *)ea)->e1;
-
- //printf("2 ea = %s, %s %s\n", ea->type->toChars(), Token::toChars(ea->op), ea->toChars());
- if (ea->op == TOKvar || ea->op == TOKsymoff)
- result = getVarExp(e->loc, istate, ((SymbolExp *)ea)->var, ctfeNeedRvalue);
- else if (ea->op == TOKaddress)
- result = interpret(((AddrExp *)ea)->e1, istate);
- // https://issues.dlang.org/show_bug.cgi?id=18871
- // https://issues.dlang.org/show_bug.cgi?id=18819
- else if (ea->op == TOKarrayliteral)
- result = interpret((ArrayLiteralExp *)ea, istate);
- else
- assert(0);
- if (CTFEExp::isCantExp(result))
- return;
-
- if (fd->ident == Id::__ArrayPostblit)
- result = evaluatePostblit(istate, result);
- else
- result = evaluateDtor(istate, result);
- if (!result)
- result = CTFEExp::voidexp;
- return;
- }
- }
- else if (ecall->op == TOKsymoff)
- {
- SymOffExp *soe = (SymOffExp *)ecall;
- fd = soe->var->isFuncDeclaration();
- assert(fd && soe->offset == 0);
- }
- else if (ecall->op == TOKdelegate)
- {
- // Calling a delegate
- fd = ((DelegateExp *)ecall)->func;
- pthis = ((DelegateExp *)ecall)->e1;
-
- // Special handling for: &nestedfunc --> DelegateExp(VarExp(nestedfunc), nestedfunc)
- if (pthis->op == TOKvar && ((VarExp *)pthis)->var == fd)
- pthis = NULL; // context is not necessary for CTFE
- }
- else if (ecall->op == TOKfunction)
- {
- // Calling a delegate literal
- fd = ((FuncExp *)ecall)->fd;
- }
- else
- {
- // delegate.funcptr()
- // others
- e->error("cannot call %s at compile time", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- if (!fd)
- {
- e->error("CTFE internal error: cannot evaluate %s at compile time", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (pthis)
- {
- // Member function call
-
- // Currently this is satisfied because closure is not yet supported.
- assert(!fd->isNested());
-
- if (pthis->op == TOKtypeid)
- {
- pthis->error("static variable %s cannot be read at compile time", pthis->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- assert(pthis);
-
- if (pthis->op == TOKnull)
- {
- assert(pthis->type->toBasetype()->ty == Tclass);
- e->error("function call through null class reference %s", pthis->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- assert(pthis->op == TOKstructliteral || pthis->op == TOKclassreference);
-
- if (fd->isVirtual() && !e->directcall)
- {
- // Make a virtual function call.
- // Get the function from the vtable of the original class
- assert(pthis->op == TOKclassreference);
- ClassDeclaration *cd = ((ClassReferenceExp *)pthis)->originalClass();
-
- // We can't just use the vtable index to look it up, because
- // vtables for interfaces don't get populated until the glue layer.
- fd = cd->findFunc(fd->ident, (TypeFunction *)fd->type);
- assert(fd);
- }
- }
-
- if (fd && fd->semanticRun >= PASSsemantic3done && fd->semantic3Errors)
- {
- e->error("CTFE failed because of previous errors in %s", fd->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- // Check for built-in functions
- result = evaluateIfBuiltin(pue, istate, e->loc, fd, e->arguments, pthis);
- if (result)
- return;
-
- if (!fd->fbody)
- {
- e->error("%s cannot be interpreted at compile time,"
- " because it has no available source code", fd->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- result = interpretFunction(pue, fd, istate, e->arguments, pthis);
- if (result->op == TOKvoidexp)
- return;
- if (!exceptionOrCantInterpret(result))
- {
- if (goal != ctfeNeedLvalue) // Peel off CTFE reference if it's unnecessary
- {
- if (result == pue->exp())
- result = pue->copy();
- result = interpret(pue, result, istate);
- }
- }
- if (!exceptionOrCantInterpret(result))
- {
- result = paintTypeOntoLiteral(e->type, result);
- result->loc = e->loc;
- }
- else if (CTFEExp::isCantExp(result) && !global.gag)
- showCtfeBackTrace(e, fd); // Print a stack trace.
- }
-
- void endTempStackFrame(InterState *pistateComma)
- {
- // If we created a temporary stack frame, end it now.
- if (istate == pistateComma)
- ctfeStack.endFrame();
- }
-
- void visit(CommaExp *e)
- {
- CommaExp *firstComma = e;
- while (firstComma->e1->op == TOKcomma)
- firstComma = (CommaExp *)firstComma->e1;
-
- // If it creates a variable, and there's no context for
- // the variable to be created in, we need to create one now.
- InterState istateComma;
- if (!istate && firstComma->e1->op == TOKdeclaration)
- {
- ctfeStack.startFrame(NULL);
- istate = &istateComma;
- }
-
- result = CTFEExp::cantexp;
-
- // If the comma returns a temporary variable, it needs to be an lvalue
- // (this is particularly important for struct constructors)
- if (e->e1->op == TOKdeclaration && e->e2->op == TOKvar &&
- ((DeclarationExp *)e->e1)->declaration == ((VarExp*)e->e2)->var &&
- ((VarExp*)e->e2)->var->storage_class & STCctfe) // same as Expression::isTemp
- {
- VarExp *ve = (VarExp *)e->e2;
- VarDeclaration *v = ve->var->isVarDeclaration();
- ctfeStack.push(v);
- if (!v->_init && !getValue(v))
- {
- setValue(v, copyLiteral(v->type->defaultInitLiteral(e->loc)).copy());
- }
- if (!getValue(v))
- {
- Expression *newval = initializerToExpression(v->_init);
- // Bug 4027. Copy constructors are a weird case where the
- // initializer is a void function (the variable is modified
- // through a reference parameter instead).
- newval = interpret(newval, istate);
- if (exceptionOrCant(newval))
- return endTempStackFrame(&istateComma);
- if (newval->op != TOKvoidexp)
- {
- // v isn't necessarily null.
- setValueWithoutChecking(v, copyLiteral(newval).copy());
- }
- }
- }
- else
- {
- UnionExp ue;
- Expression *e1 = interpret(&ue, e->e1, istate, ctfeNeedNothing);
- if (exceptionOrCant(e1))
- return endTempStackFrame(&istateComma);
- }
- result = interpret(pue, e->e2, istate, goal);
- return endTempStackFrame(&istateComma);
- }
-
- void visit(CondExp *e)
- {
- UnionExp uecond;
- Expression *econd;
- econd = interpret(&uecond, e->econd, istate);
- if (exceptionOrCant(econd))
- return;
-
- if (isPointer(e->econd->type))
- {
- if (econd->op != TOKnull)
- {
- new(&uecond) IntegerExp(e->loc, 1, Type::tbool);
- econd = uecond.exp();
- }
- }
-
- if (isTrueBool(econd))
- result = interpret(pue, e->e1, istate, goal);
- else if (econd->isBool(false))
- result = interpret(pue, e->e2, istate, goal);
- else
- {
- e->error("%s does not evaluate to boolean result at compile time", e->econd->toChars());
- result = CTFEExp::cantexp;
- }
- }
-
- void visit(ArrayLengthExp *e)
- {
- UnionExp ue1;
- Expression *e1 = interpret(&ue1, e->e1, istate);
- assert(e1);
- if (exceptionOrCant(e1))
- return;
- if (e1->op != TOKstring &&
- e1->op != TOKarrayliteral &&
- e1->op != TOKslice &&
- e1->op != TOKnull)
- {
- e->error("%s cannot be evaluated at compile time", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- new(pue) IntegerExp(e->loc, resolveArrayLength(e1), e->type);
- result = pue->exp();
- }
-
- /**
- * Interpret the vector expression as an array literal.
- * Params:
- * pue = non-null pointer to temporary storage that can be used to store the return value
- * e = Expression to interpret
- * Returns:
- * resulting array literal or 'e' if unable to interpret
- */
- static Expression *interpretVectorToArray(UnionExp *pue, VectorExp *e)
- {
- if (e->e1->op == TOKarrayliteral)
- return (ArrayLiteralExp *)e->e1;
- if (e->e1->op == TOKint64 || e->e1->op == TOKfloat64)
- {
- // Convert literal __vector(int) -> __vector([array])
- Expressions *elements = new Expressions();
- elements->setDim(e->dim);
- for (size_t i = 0; i < elements->length; i++)
- (*elements)[i] = copyLiteral(e->e1).copy();
- TypeSArray *type = NULL;
- if (e->type->ty == Tvector)
- {
- TypeVector *tv = (TypeVector *)e->type;
- if (tv->basetype->ty == Tsarray)
- type = (TypeSArray *)tv->basetype;
- }
- else if (e->type->ty == Tsarray)
- type = (TypeSArray *)e->type;
- assert(type);
- new(pue) ArrayLiteralExp(e->loc, type, elements);
- ArrayLiteralExp *ale = (ArrayLiteralExp *)pue->exp();
- ale->ownedByCtfe = OWNEDctfe;
- return ale;
- }
- return e;
- }
-
- void visit(VectorExp *e)
- {
- if (e->ownedByCtfe >= OWNEDctfe) // We've already interpreted all the elements
- {
- result = e;
- return;
- }
- Expression *e1 = interpret(pue, e->e1, istate);
- assert(e1);
- if (exceptionOrCant(e1))
- return;
- if (e1->op != TOKarrayliteral && e1->op != TOKint64 && e1->op != TOKfloat64)
- {
- e->error("`%s` cannot be evaluated at compile time", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (e1 == pue->exp())
- e1 = pue->copy();
- new(pue) VectorExp(e->loc, e1, e->to);
- VectorExp *ve = (VectorExp *)pue->exp();
- ve->type = e->type;
- ve->dim = e->dim;
- ve->ownedByCtfe = OWNEDctfe;
- result = ve;
- }
-
- void visit(VectorArrayExp *e)
- {
- Expression *e1 = interpret(pue, e->e1, istate);
- assert(e1);
- if (exceptionOrCant(e1))
- return;
- if (e1->op == TOKvector)
- {
- VectorExp *ve = (VectorExp *)e1;
- result = interpretVectorToArray(pue, ve);
- if (result->op != TOKvector)
- return;
- }
- e->error("`%s` cannot be evaluated at compile time", e->toChars());
- result = CTFEExp::cantexp;
- }
-
- void visit(DelegatePtrExp *e)
- {
- Expression *e1 = interpret(pue, e->e1, istate);
- assert(e1);
- if (exceptionOrCant(e1))
- return;
- e->error("%s cannot be evaluated at compile time", e->toChars());
- result = CTFEExp::cantexp;
- }
-
- void visit(DelegateFuncptrExp *e)
- {
- Expression *e1 = interpret(pue, e->e1, istate);
- assert(e1);
- if (exceptionOrCant(e1))
- return;
- e->error("%s cannot be evaluated at compile time", e->toChars());
- result = CTFEExp::cantexp;
- }
-
- static bool resolveIndexing(IndexExp *e, InterState *istate, Expression **pagg, uinteger_t *pidx, bool modify)
- {
- assert(e->e1->type->toBasetype()->ty != Taarray);
-
- if (e->e1->type->toBasetype()->ty == Tpointer)
- {
- // Indexing a pointer. Note that there is no $ in this case.
- Expression *e1 = interpret(e->e1, istate);
- if (exceptionOrCantInterpret(e1))
- return false;
-
- Expression *e2 = interpret(e->e2, istate);
- if (exceptionOrCantInterpret(e2))
- return false;
- sinteger_t indx = e2->toInteger();
-
- dinteger_t ofs;
- Expression *agg = getAggregateFromPointer(e1, &ofs);
-
- if (agg->op == TOKnull)
- {
- e->error("cannot index through null pointer %s", e->e1->toChars());
- return false;
- }
- if (agg->op == TOKint64)
- {
- e->error("cannot index through invalid pointer %s of value %s",
- e->e1->toChars(), e1->toChars());
- return false;
- }
- // Pointer to a non-array variable
- if (agg->op == TOKsymoff)
- {
- e->error("mutable variable %s cannot be %s at compile time, even through a pointer",
- (modify ? "modified" : "read"), ((SymOffExp *)agg)->var->toChars());
- return false;
- }
-
- if (agg->op == TOKarrayliteral || agg->op == TOKstring)
- {
- dinteger_t len = resolveArrayLength(agg);
- if (ofs + indx >= len)
- {
- e->error("pointer index [%lld] exceeds allocated memory block [0..%lld]",
- ofs + indx, len);
- return false;
- }
- }
- else
- {
- if (ofs + indx != 0)
- {
- e->error("pointer index [%lld] lies outside memory block [0..1]",
- ofs + indx);
- return false;
- }
- }
- *pagg = agg;
- *pidx = ofs + indx;
- return true;
- }
-
- Expression *e1 = interpret(e->e1, istate);
- if (exceptionOrCantInterpret(e1))
- return false;
- if (e1->op == TOKnull)
- {
- e->error("cannot index null array %s", e->e1->toChars());
- return false;
- }
- if (e1->op == TOKvector)
- {
- UnionExp ue;
- e1 = interpretVectorToArray(&ue, (VectorExp *)e1);
- e1 = (e1 == ue.exp()) ? ue.copy() : e1;
- }
-
- // Set the $ variable, and find the array literal to modify
- if (e1->op != TOKarrayliteral &&
- e1->op != TOKstring &&
- e1->op != TOKslice &&
- e1->op != TOKvector)
- {
- e->error("cannot determine length of %s at compile time",
- e->e1->toChars());
- return false;
- }
-
- dinteger_t len = resolveArrayLength(e1);
- if (e->lengthVar)
- {
- Expression *dollarExp = new IntegerExp(e->loc, len, Type::tsize_t);
- ctfeStack.push(e->lengthVar);
- setValue(e->lengthVar, dollarExp);
- }
- Expression *e2 = interpret(e->e2, istate);
- if (e->lengthVar)
- ctfeStack.pop(e->lengthVar); // $ is defined only inside []
- if (exceptionOrCantInterpret(e2))
- return false;
- if (e2->op != TOKint64)
- {
- e->error("CTFE internal error: non-integral index [%s]", e->e2->toChars());
- return false;
- }
-
- if (e1->op == TOKslice)
- {
- // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx']
- uinteger_t index = e2->toInteger();
- uinteger_t ilwr = ((SliceExp *)e1)->lwr->toInteger();
- uinteger_t iupr = ((SliceExp *)e1)->upr->toInteger();
-
- if (index > iupr - ilwr)
- {
- e->error("index %llu exceeds array length %llu", index, iupr - ilwr);
- return false;
- }
- *pagg = ((SliceExp *)e1)->e1;
- *pidx = index + ilwr;
- }
- else
- {
- *pagg = e1;
- *pidx = e2->toInteger();
- if (len <= *pidx)
- {
- e->error("array index %lld is out of bounds [0..%lld]",
- *pidx, len);
- return false;
- }
- }
- return true;
- }
-
- void visit(IndexExp *e)
- {
- if (e->e1->type->toBasetype()->ty == Tpointer)
- {
- Expression *agg;
- uinteger_t indexToAccess;
- if (!resolveIndexing(e, istate, &agg, &indexToAccess, false))
- {
- result = CTFEExp::cantexp;
- return;
- }
- if (agg->op == TOKarrayliteral || agg->op == TOKstring)
- {
- if (goal == ctfeNeedLvalue)
- {
- // if we need a reference, IndexExp shouldn't be interpreting
- // the expression to a value, it should stay as a reference
- new(pue) IndexExp(e->loc, agg, new IntegerExp(e->e2->loc, indexToAccess, e->e2->type));
- result = pue->exp();
- result->type = e->type;
- return;
- }
- result = ctfeIndex(e->loc, e->type, agg, indexToAccess);
- return;
- }
- else
- {
- assert(indexToAccess == 0);
- result = interpret(agg, istate, goal);
- if (exceptionOrCant(result))
- return;
- result = paintTypeOntoLiteral(e->type, result);
- return;
- }
- }
-
- if (e->e1->type->toBasetype()->ty == Taarray)
- {
- Expression *e1 = interpret(e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- if (e1->op == TOKnull)
- {
- if (goal == ctfeNeedLvalue && e1->type->ty == Taarray && e->modifiable)
- {
- assert(0); // does not reach here?
- return;
- }
- e->error("cannot index null array %s", e->e1->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- Expression *e2 = interpret(e->e2, istate);
- if (exceptionOrCant(e2))
- return;
-
- if (goal == ctfeNeedLvalue)
- {
- // Pointer or reference of a scalar type
- if (e1 == e->e1 && e2 == e->e2)
- result = e;
- else
- {
- new(pue) IndexExp(e->loc, e1, e2);
- result = pue->exp();
- result->type = e->type;
- }
- return;
- }
-
- assert(e1->op == TOKassocarrayliteral);
- UnionExp e2tmp;
- e2 = resolveSlice(e2, &e2tmp);
- result = findKeyInAA(e->loc, (AssocArrayLiteralExp *)e1, e2);
- if (!result)
- {
- e->error("key %s not found in associative array %s", e2->toChars(), e->e1->toChars());
- result = CTFEExp::cantexp;
- }
- return;
- }
-
- Expression *agg;
- uinteger_t indexToAccess;
- if (!resolveIndexing(e, istate, &agg, &indexToAccess, false))
- {
- result = CTFEExp::cantexp;
- return;
- }
-
- if (goal == ctfeNeedLvalue)
- {
- Expression *e2 = new IntegerExp(e->e2->loc, indexToAccess, Type::tsize_t);
- new(pue) IndexExp(e->loc, agg, e2);
- result = pue->exp();
- result->type = e->type;
- return;
- }
-
- result = ctfeIndex(e->loc, e->type, agg, indexToAccess);
- if (exceptionOrCant(result))
- return;
- if (result->op == TOKvoid)
- {
- e->error("%s is used before initialized", e->toChars());
- errorSupplemental(result->loc, "originally uninitialized here");
- result = CTFEExp::cantexp;
- return;
- }
- result = paintTypeOntoLiteral(e->type, result);
- }
-
- void visit(SliceExp *e)
- {
- if (e->e1->type->toBasetype()->ty == Tpointer)
- {
- // Slicing a pointer. Note that there is no $ in this case.
- Expression *e1 = interpret(e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- if (e1->op == TOKint64)
- {
- e->error("cannot slice invalid pointer %s of value %s", e->e1->toChars(), e1->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- /* Evaluate lower and upper bounds of slice
- */
- Expression *lwr = interpret(e->lwr, istate);
- if (exceptionOrCant(lwr))
- return;
- Expression *upr = interpret(e->upr, istate);
- if (exceptionOrCant(upr))
- return;
- uinteger_t ilwr = lwr->toInteger();
- uinteger_t iupr = upr->toInteger();
-
- dinteger_t ofs;
- Expression *agg = getAggregateFromPointer(e1, &ofs);
- ilwr += ofs;
- iupr += ofs;
- if (agg->op == TOKnull)
- {
- if (iupr == ilwr)
- {
- result = new NullExp(e->loc);
- result->type = e->type;
- return;
- }
- e->error("cannot slice null pointer %s", e->e1->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (agg->op == TOKsymoff)
- {
- e->error("slicing pointers to static variables is not supported in CTFE");
- result = CTFEExp::cantexp;
- return;
- }
- if (agg->op != TOKarrayliteral && agg->op != TOKstring)
- {
- e->error("pointer %s cannot be sliced at compile time (it does not point to an array)", e->e1->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- assert(agg->op == TOKarrayliteral || agg->op == TOKstring);
- dinteger_t len = ArrayLength(Type::tsize_t, agg).exp()->toInteger();
- //Type *pointee = ((TypePointer *)agg->type)->next;
- if (iupr > (len + 1) || iupr < ilwr)
- {
- e->error("pointer slice [%lld..%lld] exceeds allocated memory block [0..%lld]", ilwr, iupr, len);
- result = CTFEExp::cantexp;
- return;
- }
- if (ofs != 0)
- {
- lwr = new IntegerExp(e->loc, ilwr, lwr->type);
- upr = new IntegerExp(e->loc, iupr, upr->type);
- }
- new(pue) SliceExp(e->loc, agg, lwr, upr);
- result = pue->exp();
- result->type = e->type;
- return;
- }
-
- Expression *e1 = interpret(e->e1, istate);
- if (exceptionOrCant(e1))
- return;
-
- if (!e->lwr)
- {
- result = paintTypeOntoLiteral(e->type, e1);
- return;
- }
-
- if (e1->op == TOKvector)
- {
- e1 = interpretVectorToArray(pue, (VectorExp *)e1);
- e1 = (e1 == pue->exp()) ? pue->copy() : e1;
- }
-
- /* Set the $ variable
- */
- if (e1->op != TOKarrayliteral && e1->op != TOKstring && e1->op != TOKnull && e1->op != TOKslice && e1->op != TOKvector)
- {
- e->error("cannot determine length of %s at compile time", e1->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- uinteger_t dollar = resolveArrayLength(e1);
- if (e->lengthVar)
- {
- IntegerExp *dollarExp = new IntegerExp(e->loc, dollar, Type::tsize_t);
- ctfeStack.push(e->lengthVar);
- setValue(e->lengthVar, dollarExp);
- }
-
- /* Evaluate lower and upper bounds of slice
- */
- Expression *lwr = interpret(e->lwr, istate);
- if (exceptionOrCant(lwr))
- {
- if (e->lengthVar)
- ctfeStack.pop(e->lengthVar);
- return;
- }
- Expression *upr = interpret(e->upr, istate);
- if (exceptionOrCant(upr))
- {
- if (e->lengthVar)
- ctfeStack.pop(e->lengthVar);
- return;
- }
- if (e->lengthVar)
- ctfeStack.pop(e->lengthVar); // $ is defined only inside [L..U]
-
- uinteger_t ilwr = lwr->toInteger();
- uinteger_t iupr = upr->toInteger();
- if (e1->op == TOKnull)
- {
- if (ilwr == 0 && iupr == 0)
- {
- result = e1;
- return;
- }
- e1->error("slice [%llu..%llu] is out of bounds", ilwr, iupr);
- result = CTFEExp::cantexp;
- return;
- }
- if (e1->op == TOKslice)
- {
- SliceExp *se = (SliceExp *)e1;
- // Simplify slice of slice:
- // aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr']
- uinteger_t lo1 = se->lwr->toInteger();
- uinteger_t up1 = se->upr->toInteger();
- if (ilwr > iupr || iupr > up1 - lo1)
- {
- e->error("slice[%llu..%llu] exceeds array bounds[%llu..%llu]", ilwr, iupr, lo1, up1);
- result = CTFEExp::cantexp;
- return;
- }
- ilwr += lo1;
- iupr += lo1;
- new(pue) SliceExp(e->loc, se->e1, new IntegerExp(e->loc, ilwr, lwr->type), new IntegerExp(e->loc, iupr, upr->type));
- result = pue->exp();
- result->type = e->type;
- return;
- }
- if (e1->op == TOKarrayliteral || e1->op == TOKstring)
- {
- if (iupr < ilwr || dollar < iupr)
- {
- e->error("slice [%lld..%lld] exceeds array bounds [0..%lld]", ilwr, iupr, dollar);
- result = CTFEExp::cantexp;
- return;
- }
- }
- new(pue) SliceExp(e->loc, e1, lwr, upr);
- result = pue->exp();
- result->type = e->type;
- }
-
- void visit(InExp *e)
- {
- Expression *e1 = interpret(e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- Expression *e2 = interpret(e->e2, istate);
- if (exceptionOrCant(e2))
- return;
- if (e2->op == TOKnull)
- {
- new(pue) NullExp(e->loc, e->type);
- result = pue->exp();
- return;
- }
- if (e2->op != TOKassocarrayliteral)
- {
- e->error("%s cannot be interpreted at compile time", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- e1 = resolveSlice(e1);
- result = findKeyInAA(e->loc, (AssocArrayLiteralExp *)e2, e1);
- if (exceptionOrCant(result))
- return;
- if (!result)
- {
- new(pue) NullExp(e->loc, e->type);
- result = pue->exp();
- }
- else
- {
- // Create a CTFE pointer &aa[index]
- result = new IndexExp(e->loc, e2, e1);
- result->type = e->type->nextOf();
- new(pue) AddrExp(e->loc, result, e->type);
- result = pue->exp();
- }
- }
-
- void visit(CatExp *e)
- {
- UnionExp ue1;
- Expression *e1 = interpret(&ue1, e->e1, istate);
- if (exceptionOrCant(e1))
- return;
-
- UnionExp ue2;
- Expression *e2 = interpret(&ue2, e->e2, istate);
- if (exceptionOrCant(e2))
- return;
-
- UnionExp e1tmp;
- e1 = resolveSlice(e1, &e1tmp);
-
- UnionExp e2tmp;
- e2 = resolveSlice(e2, &e2tmp);
-
- /* e1 and e2 can't go on the stack because of x~[y] and [x]~y will
- * result in [x,y] and then x or y is on the stack.
- * But if they are both strings, we can, because it isn't the x~[y] case.
- */
- if (!(e1->op == TOKstring && e2->op == TOKstring))
- {
- if (e1 == ue1.exp())
- e1 = ue1.copy();
- if (e2 == ue2.exp())
- e2 = ue2.copy();
- }
-
- *pue = ctfeCat(e->loc, e->type, e1, e2);
- result = pue->exp();
-
- if (CTFEExp::isCantExp(result))
- {
- e->error("%s cannot be interpreted at compile time", e->toChars());
- return;
- }
- // We know we still own it, because we interpreted both e1 and e2
- if (result->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)result;
- ale->ownedByCtfe = OWNEDctfe;
-
- // Bugzilla 14686
- for (size_t i = 0; i < ale->elements->length; i++)
- {
- Expression *ex = evaluatePostblit(istate, (*ale->elements)[i]);
- if (exceptionOrCant(ex))
- return;
- }
- }
- if (result->op == TOKstring)
- ((StringExp *)result)->ownedByCtfe = OWNEDctfe;
- }
-
- void visit(DeleteExp *e)
- {
- result = interpret(e->e1, istate);
- if (exceptionOrCant(result))
- return;
-
- if (result->op == TOKnull)
- {
- result = CTFEExp::voidexp;
- return;
- }
-
- Type *tb = e->e1->type->toBasetype();
- switch (tb->ty)
- {
- case Tclass:
- {
- if (result->op != TOKclassreference)
- {
- e->error("delete on invalid class reference `%s`", result->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- ClassReferenceExp *cre = (ClassReferenceExp *)result;
- ClassDeclaration *cd = cre->originalClass();
- if (cd->aggDelete)
- {
- e->error("member deallocators not supported by CTFE");
- result = CTFEExp::cantexp;
- return;
- }
-
- if (cd->dtor)
- {
- result = interpretFunction(pue, cd->dtor, istate, NULL, cre);
- if (exceptionOrCant(result))
- return;
- }
- break;
- }
-
- case Tpointer:
- {
- tb = ((TypePointer *)tb)->next->toBasetype();
- if (tb->ty == Tstruct)
- {
- if (result->op != TOKaddress ||
- ((AddrExp *)result)->e1->op != TOKstructliteral)
- {
- e->error("delete on invalid struct pointer `%s`", result->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- StructDeclaration *sd = ((TypeStruct *)tb)->sym;
- StructLiteralExp *sle = (StructLiteralExp *)((AddrExp *)result)->e1;
- if (sd->aggDelete)
- {
- e->error("member deallocators not supported by CTFE");
- result = CTFEExp::cantexp;
- return;
- }
-
- if (sd->dtor)
- {
- result = interpretFunction(pue, sd->dtor, istate, NULL, sle);
- if (exceptionOrCant(result))
- return;
- }
- }
- break;
- }
-
- case Tarray:
- {
- Type *tv = tb->nextOf()->baseElemOf();
- if (tv->ty == Tstruct)
- {
- if (result->op != TOKarrayliteral)
- {
- e->error("delete on invalid struct array `%s`", result->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- StructDeclaration *sd = ((TypeStruct *)tv)->sym;
- if (sd->aggDelete)
- {
- e->error("member deallocators not supported by CTFE");
- result = CTFEExp::cantexp;
- return;
- }
-
- if (sd->dtor)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)result;
- for (size_t i = 0; i < ale->elements->length; i++)
- {
- Expression *el = (*ale->elements)[i];
- result = interpretFunction(pue, sd->dtor, istate, NULL, el);
- if (exceptionOrCant(result))
- return;
- }
- }
- }
- break;
- }
-
- default:
- assert(0);
- }
- result = CTFEExp::voidexp;
- }
-
- void visit(CastExp *e)
- {
- Expression *e1 = interpret(e->e1, istate, goal);
- if (exceptionOrCant(e1))
- return;
- // If the expression has been cast to void, do nothing.
- if (e->to->ty == Tvoid)
- {
- result = CTFEExp::voidexp;
- return;
- }
- if (e->to->ty == Tpointer && e1->op != TOKnull)
- {
- Type *pointee = ((TypePointer *)e->type)->next;
- // Implement special cases of normally-unsafe casts
- if (e1->op == TOKint64)
- {
- // Happens with Windows HANDLEs, for example.
- result = paintTypeOntoLiteral(pue, e->to, e1);
- return;
- }
- bool castToSarrayPointer = false;
- bool castBackFromVoid = false;
- if (e1->type->ty == Tarray || e1->type->ty == Tsarray || e1->type->ty == Tpointer)
- {
- // Check for unsupported type painting operations
- // For slices, we need the type being sliced,
- // since it may have already been type painted
- Type *elemtype = e1->type->nextOf();
- if (e1->op == TOKslice)
- elemtype = ((SliceExp *)e1)->e1->type->nextOf();
- // Allow casts from X* to void *, and X** to void** for any X.
- // But don't allow cast from X* to void**.
- // So, we strip all matching * from source and target to find X.
- // Allow casts to X* from void* only if the 'void' was originally an X;
- // we check this later on.
- Type *ultimatePointee = pointee;
- Type *ultimateSrc = elemtype;
- while (ultimatePointee->ty == Tpointer && ultimateSrc->ty == Tpointer)
- {
- ultimatePointee = ultimatePointee->nextOf();
- ultimateSrc = ultimateSrc->nextOf();
- }
- if (ultimatePointee->ty == Tsarray && ultimatePointee->nextOf()->equivalent(ultimateSrc))
- {
- castToSarrayPointer = true;
- }
- else if (ultimatePointee->ty != Tvoid && ultimateSrc->ty != Tvoid &&
- !isSafePointerCast(elemtype, pointee))
- {
- e->error("reinterpreting cast from %s* to %s* is not supported in CTFE",
- elemtype->toChars(), pointee->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (ultimateSrc->ty == Tvoid)
- castBackFromVoid = true;
- }
-
- if (e1->op == TOKslice)
- {
- SliceExp *se = (SliceExp *)e1;
- if (se->e1->op == TOKnull)
- {
- result = paintTypeOntoLiteral(pue, e->type, se->e1);
- return;
- }
- // Create a CTFE pointer &aggregate[1..2]
- IndexExp *ei = new IndexExp(e->loc, se->e1, se->lwr);
- ei->type = e->type->nextOf();
- new(pue) AddrExp(e->loc, ei, e->type);
- result = pue->exp();
- return;
- }
- if (e1->op == TOKarrayliteral || e1->op == TOKstring)
- {
- // Create a CTFE pointer &[1,2,3][0] or &"abc"[0]
- IndexExp *ei = new IndexExp(e->loc, e1, new IntegerExp(e->loc, 0, Type::tsize_t));
- ei->type = e->type->nextOf();
- new(pue) AddrExp(e->loc, ei, e->type);
- result = pue->exp();
- return;
- }
- if (e1->op == TOKindex && !((IndexExp *)e1)->e1->type->equals(e1->type))
- {
- // type painting operation
- IndexExp *ie = (IndexExp *)e1;
- if (castBackFromVoid)
- {
- // get the original type. For strings, it's just the type...
- Type *origType = ie->e1->type->nextOf();
- // ..but for arrays of type void*, it's the type of the element
- if (ie->e1->op == TOKarrayliteral && ie->e2->op == TOKint64)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)ie->e1;
- const size_t indx = (size_t)ie->e2->toInteger();
- if (indx < ale->elements->length)
- {
- Expression *xx = (*ale->elements)[indx];
- if (xx)
- {
- if (xx->op == TOKindex)
- origType = ((IndexExp *)xx)->e1->type->nextOf();
- else if (xx->op == TOKaddress)
- origType= ((AddrExp *)xx)->e1->type;
- else if (xx->op == TOKvar)
- origType = ((VarExp *)xx)->var->type;
- }
- }
- }
- if (!isSafePointerCast(origType, pointee))
- {
- e->error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", origType->toChars(), pointee->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- }
- new(pue) IndexExp(e1->loc, ie->e1, ie->e2);
- result = pue->exp();
- result->type = e->type;
- return;
- }
- if (e1->op == TOKaddress)
- {
- AddrExp *ae = (AddrExp *)e1;
- Type *origType = ae->e1->type;
- if (isSafePointerCast(origType, pointee))
- {
- new(pue) AddrExp(e->loc, ae->e1, e->type);
- result = pue->exp();
- return;
- }
- if (castToSarrayPointer && pointee->toBasetype()->ty == Tsarray && ae->e1->op == TOKindex)
- {
- // &val[idx]
- dinteger_t dim = ((TypeSArray *)pointee->toBasetype())->dim->toInteger();
- IndexExp *ie = (IndexExp *)ae->e1;
- Expression *lwr = ie->e2;
- Expression *upr = new IntegerExp(ie->e2->loc, ie->e2->toInteger() + dim, Type::tsize_t);
-
- // Create a CTFE pointer &val[idx..idx+dim]
- SliceExp *er = new SliceExp(e->loc, ie->e1, lwr, upr);
- er->type = pointee;
- new(pue) AddrExp(e->loc, er, e->type);
- result = pue->exp();
- return;
- }
- }
- if (e1->op == TOKvar || e1->op == TOKsymoff)
- {
- // type painting operation
- Type *origType = ((SymbolExp *)e1)->var->type;
- if (castBackFromVoid && !isSafePointerCast(origType, pointee))
- {
- e->error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", origType->toChars(), pointee->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (e1->op == TOKvar)
- new(pue) VarExp(e->loc, ((VarExp *)e1)->var);
- else
- new(pue) SymOffExp(e->loc, ((SymOffExp *)e1)->var, ((SymOffExp *)e1)->offset);
- result = pue->exp();
- result->type = e->to;
- return;
- }
-
- // Check if we have a null pointer (eg, inside a struct)
- e1 = interpret(e1, istate);
- if (e1->op != TOKnull)
- {
- e->error("pointer cast from %s to %s is not supported at compile time", e1->type->toChars(), e->to->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- }
- if (e->to->ty == Tsarray && e->e1->type->ty == Tvector)
- {
- // Special handling for: cast(float[4])__vector([w, x, y, z])
- e1 = interpret(e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- assert(e1->op == TOKvector);
- e1 = interpretVectorToArray(pue, (VectorExp *)e1);
- }
- if (e->to->ty == Tarray && e1->op == TOKslice)
- {
- // Note that the slice may be void[], so when checking for dangerous
- // casts, we need to use the original type, which is se->e1.
- SliceExp *se = (SliceExp *)e1;
- if (!isSafePointerCast(se->e1->type->nextOf(), e->to->nextOf()))
- {
- e->error("array cast from %s to %s is not supported at compile time", se->e1->type->toChars(), e->to->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- new(pue) SliceExp(e1->loc, se->e1, se->lwr, se->upr);
- result = pue->exp();
- result->type = e->to;
- return;
- }
- // Disallow array type painting, except for conversions between built-in
- // types of identical size.
- if ((e->to->ty == Tsarray || e->to->ty == Tarray) && (e1->type->ty == Tsarray || e1->type->ty == Tarray) && !isSafePointerCast(e1->type->nextOf(), e->to->nextOf()))
- {
- e->error("array cast from %s to %s is not supported at compile time", e1->type->toChars(), e->to->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (e->to->ty == Tsarray)
- e1 = resolveSlice(e1);
- if (e->to->toBasetype()->ty == Tbool && e1->type->ty == Tpointer)
- {
- new(pue) IntegerExp(e->loc, e1->op != TOKnull, e->to);
- result = pue->exp();
- return;
- }
- result = ctfeCast(pue, e->loc, e->type, e->to, e1);
- }
-
- void visit(AssertExp *e)
- {
- Expression *e1 = interpret(pue, e->e1, istate);
- if (exceptionOrCant(e1))
- return;
- if (isTrueBool(e1))
- {
- }
- else if (e1->isBool(false))
- {
- if (e->msg)
- {
- UnionExp ue;
- result = interpret(&ue, e->msg, istate);
- if (exceptionOrCant(result))
- return;
- e->error("%s", result->toChars());
- }
- else
- e->error("%s failed", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- else
- {
- e->error("%s is not a compile time boolean expression", e1->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- result = e1;
- return;
- }
-
- void visit(PtrExp *e)
- {
- // Check for int<->float and long<->double casts.
- if (e->e1->op == TOKsymoff)
- {
- SymOffExp *soe = (SymOffExp *)e->e1;
- if (soe->offset == 0 && soe->var->isVarDeclaration() && isFloatIntPaint(e->type, soe->var->type))
- {
- // *(cast(int*)&v), where v is a float variable
- result = paintFloatInt(pue, getVarExp(e->loc, istate, soe->var, ctfeNeedRvalue), e->type);
- return;
- }
- }
-
- if (e->e1->op == TOKcast)
- {
- CastExp *ce1 = (CastExp *)e->e1;
- if (ce1->e1->op == TOKaddress)
- {
- AddrExp *ae11 = (AddrExp *)ce1->e1;
- // *(cast(int*)&x), where x is a float expression
- Expression *x = ae11->e1;
- if (isFloatIntPaint(e->type, x->type))
- {
- result = paintFloatInt(pue, interpret(x, istate), e->type);
- return;
- }
- }
- }
-
- // Constant fold *(&structliteral + offset)
- if (e->e1->op == TOKadd)
- {
- AddExp *ae = (AddExp *)e->e1;
- if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64)
- {
- AddrExp *ade = (AddrExp *)ae->e1;
- Expression *ex = interpret(ade->e1, istate);
- if (exceptionOrCant(ex))
- return;
- if (ex->op == TOKstructliteral)
- {
- StructLiteralExp *se = (StructLiteralExp *)ex;
- dinteger_t offset = ae->e2->toInteger();
- result = se->getField(e->type, (unsigned)offset);
- if (result)
- return;
- }
- }
- }
-
- // It's possible we have an array bounds error. We need to make sure it
- // errors with this line number, not the one where the pointer was set.
- result = interpret(e->e1, istate);
- if (exceptionOrCant(result))
- return;
-
- if (result->op == TOKfunction)
- return;
- if (result->op == TOKsymoff)
- {
- SymOffExp *soe = (SymOffExp *)result;
- if (soe->offset == 0 && soe->var->isFuncDeclaration())
- return;
- e->error("cannot dereference pointer to static variable %s at compile time", soe->var->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- if (result->op != TOKaddress)
- {
- if (result->op == TOKnull)
- e->error("dereference of null pointer `%s`", e->e1->toChars());
- else
- e->error("dereference of invalid pointer `%s`", result->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- // *(&x) ==> x
- result = ((AddrExp *)result)->e1;
-
- if (result->op == TOKslice && e->type->toBasetype()->ty == Tsarray)
- {
- /* aggr[lwr..upr]
- * upr may exceed the upper boundary of aggr, but the check is deferred
- * until those out-of-bounds elements will be touched.
- */
- return;
- }
- result = interpret(pue, result, istate, goal);
- if (exceptionOrCant(result))
- return;
- }
-
- void visit(DotVarExp *e)
- {
- Expression *ex = interpret(e->e1, istate);
- if (exceptionOrCant(ex))
- return;
-
- if (FuncDeclaration *f = e->var->isFuncDeclaration())
- {
- if (ex == e->e1)
- result = e; // optimize: reuse this CTFE reference
- else
- {
- new(pue) DotVarExp(e->loc, ex, f, false);
- result = pue->exp();
- result->type = e->type;
- }
- return;
- }
-
- VarDeclaration *v = e->var->isVarDeclaration();
- if (!v)
- {
- e->error("CTFE internal error: %s", e->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- if (ex->op == TOKnull)
- {
- if (ex->type->toBasetype()->ty == Tclass)
- e->error("class `%s` is null and cannot be dereferenced", e->e1->toChars());
- else
- e->error("CTFE internal error: null this `%s`", e->e1->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- if (ex->op != TOKstructliteral && ex->op != TOKclassreference)
- {
- e->error("%s.%s is not yet implemented at compile time", e->e1->toChars(), e->var->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- StructLiteralExp *se;
- int i;
-
- // We can't use getField, because it makes a copy
- if (ex->op == TOKclassreference)
- {
- se = ((ClassReferenceExp *)ex)->value;
- i = ((ClassReferenceExp *)ex)->findFieldIndexByName(v);
- }
- else
- {
- se = (StructLiteralExp *)ex;
- i = findFieldIndexByName(se->sd, v);
- }
- if (i == -1)
- {
- e->error("couldn't find field %s of type %s in %s", v->toChars(), e->type->toChars(), se->toChars());
- result = CTFEExp::cantexp;
- return;
- }
-
- if (goal == ctfeNeedLvalue)
- {
- Expression *ev = (*se->elements)[i];
- if (!ev || ev->op == TOKvoid)
- (*se->elements)[i] = voidInitLiteral(e->type, v).copy();
- // just return the (simplified) dotvar expression as a CTFE reference
- if (e->e1 == ex)
- result = e;
- else
- {
- new(pue) DotVarExp(e->loc, ex, v);
- result = pue->exp();
- result->type = e->type;
- }
- return;
- }
-
- result = (*se->elements)[i];
- if (!result)
- {
- // https://issues.dlang.org/show_bug.cgi?id=19897
- // Zero-length fields don't have an initializer.
- if (v->type->size() == 0)
- result = voidInitLiteral(e->type, v).copy();
- else
- {
- e->error("Internal Compiler Error: null field %s", v->toChars());
- result = CTFEExp::cantexp;
- return;
- }
- }
- if (result->op == TOKvoid)
- {
- VoidInitExp *ve = (VoidInitExp *)result;
- const char *s = ve->var->toChars();
- if (v->overlapped)
- {
- e->error("reinterpretation through overlapped field %s is not allowed in CTFE", s);
- result = CTFEExp::cantexp;
- return;
- }
- e->error("cannot read uninitialized variable %s in CTFE", s);
- result = CTFEExp::cantexp;
- return;
- }
-
- if (v->type->ty != result->type->ty && v->type->ty == Tsarray)
- {
- // Block assignment from inside struct literals
- TypeSArray *tsa = (TypeSArray *)v->type;
- size_t len = (size_t)tsa->dim->toInteger();
- UnionExp ue;
- result = createBlockDuplicatedArrayLiteral(&ue, ex->loc, v->type, ex, len);
- if (result == ue.exp())
- result = ue.copy();
- (*se->elements)[i] = result;
- }
- }
-
- void visit(RemoveExp *e)
- {
- Expression *agg = interpret(e->e1, istate);
- if (exceptionOrCant(agg))
- return;
- Expression *index = interpret(e->e2, istate);
- if (exceptionOrCant(index))
- return;
- if (agg->op == TOKnull)
- {
- result = CTFEExp::voidexp;
- return;
- }
- assert(agg->op == TOKassocarrayliteral);
- AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)agg;
- Expressions *keysx = aae->keys;
- Expressions *valuesx = aae->values;
- size_t removed = 0;
- for (size_t j = 0; j < valuesx->length; ++j)
- {
- Expression *ekey = (*keysx)[j];
- int eq = ctfeEqual(e->loc, TOKequal, ekey, index);
- if (eq)
- ++removed;
- else if (removed != 0)
- {
- (*keysx)[j - removed] = ekey;
- (*valuesx)[j - removed] = (*valuesx)[j];
- }
- }
- valuesx->length = valuesx->length - removed;
- keysx->length = keysx->length - removed;
- new(pue) IntegerExp(e->loc, removed ? 1 : 0, Type::tbool);
- result = pue->exp();
- }
-
- void visit(ClassReferenceExp *e)
- {
- //printf("ClassReferenceExp::interpret() %s\n", e->value->toChars());
- result = e;
- }
-
- void visit(VoidInitExp *e)
- {
- e->error("CTFE internal error: trying to read uninitialized variable");
- assert(0);
- result = CTFEExp::cantexp;
- }
-
- void visit(ThrownExceptionExp *e)
- {
- assert(0); // This should never be interpreted
- result = e;
- }
-
-};
-
-/********************************************
- * Interpret the expression.
- * Params:
- * pue = non-null pointer to temporary storage that can be used to store the return value
- * e = Expression to interpret
- * istate = context
- * goal = what the result will be used for
- * Returns:
- * resulting expression
- */
-
-static Expression *interpret(UnionExp *pue, Expression *e, InterState *istate, CtfeGoal goal)
-{
- if (!e)
- return NULL;
- Interpreter v(pue, istate, goal);
- e->accept(&v);
- Expression *ex = v.result;
- assert(goal == ctfeNeedNothing || ex != NULL);
- return ex;
-}
-
-///
-Expression *interpret(Expression *e, InterState *istate, CtfeGoal goal)
-{
- UnionExp ue;
- Expression *result = interpret(&ue, e, istate, goal);
- if (result == ue.exp())
- result = ue.copy();
- return result;
-}
-
-/***********************************
- * Interpret the statement.
- * Params:
- * pue = non-null pointer to temporary storage that can be used to store the return value
- * s = Statement to interpret
- * istate = context
- * Returns:
- * NULL continue to next statement
- * TOKcantexp cannot interpret statement at compile time
- * !NULL expression from return statement, or thrown exception
- */
-
-static Expression *interpret(UnionExp *pue, Statement *s, InterState *istate)
-{
- if (!s)
- return NULL;
- Interpreter v(pue, istate, ctfeNeedNothing);
- s->accept(&v);
- return v.result;
-}
-
-Expression *interpret(Statement *s, InterState *istate)
-{
- UnionExp ue;
- Expression *result = interpret(&ue, s, istate);
- if (result == ue.exp())
- result = ue.copy();
- return result;
-}
-
-/**
- * All results destined for use outside of CTFE need to have their CTFE-specific
- * features removed.
- * In particular,
- * 1. all slices must be resolved.
- * 2. all .ownedByCtfe set to OWNEDcode
- */
-Expression *scrubReturnValue(Loc loc, Expression *e)
-{
- if (e->op == TOKclassreference)
- {
- StructLiteralExp *sle = ((ClassReferenceExp*)e)->value;
- if (Expression *ex = scrubStructLiteral(loc, sle))
- return ex;
- }
- else if (e->op == TOKvoid)
- {
- error(loc, "uninitialized variable `%s` cannot be returned from CTFE", ((VoidInitExp *)e)->var->toChars());
- return new ErrorExp();
- }
-
- e = resolveSlice(e);
-
- if (e->op == TOKstructliteral)
- {
- StructLiteralExp *sle = (StructLiteralExp *)e;
- if (Expression *ex = scrubStructLiteral(loc, sle))
- return ex;
- }
- else if (e->op == TOKstring)
- {
- ((StringExp *)e)->ownedByCtfe = OWNEDcode;
- }
- else if (e->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
- ale->ownedByCtfe = OWNEDcode;
- if (Expression *ex = scrubArray(loc, ale->elements))
- return ex;
- }
- else if (e->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e;
- aae->ownedByCtfe = OWNEDcode;
- if (Expression *ex = scrubArray(loc, aae->keys))
- return ex;
- if (Expression *ex = scrubArray(loc, aae->values))
- return ex;
- aae->type = toBuiltinAAType(aae->type);
- }
- else if (e->op == TOKvector)
- {
- VectorExp *ve = (VectorExp *)e;
- ve->ownedByCtfe = OWNEDcode;
- if (ve->e1->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)ve->e1;
- ale->ownedByCtfe = OWNEDcode;
- if (Expression *ex = scrubArray(loc, ale->elements))
- return ex;
- }
- }
- return e;
-}
-
-/* Returns: true if e is void,
- * or is an array literal or struct literal of void elements.
- */
-static bool isVoid(Expression *e, bool checkArray = false)
-{
- if (e->op == TOKvoid)
- return true;
-
- if (checkArray && e->type->ty != Tsarray)
- return false;
-
- if (e->op == TOKarrayliteral)
- return isEntirelyVoid(((ArrayLiteralExp *)e)->elements);
-
- if (e->op == TOKstructliteral)
- return isEntirelyVoid(((StructLiteralExp *)e)->elements);
-
- return false;
-}
-
-// Return true if every element is either void,
-// or is an array literal or struct literal of void elements.
-bool isEntirelyVoid(Expressions *elems)
-{
- for (size_t i = 0; i < elems->length; i++)
- {
- Expression *e = (*elems)[i];
- // It can be NULL for performance reasons,
- // see StructLiteralExp::interpret().
- if (e && !isVoid(e))
- return false;
- }
- return true;
-}
-
-// Scrub all members of an array. Return false if error
-Expression *scrubArray(Loc loc, Expressions *elems, bool structlit)
-{
- for (size_t i = 0; i < elems->length; i++)
- {
- Expression *e = (*elems)[i];
- // It can be NULL for performance reasons,
- // see StructLiteralExp::interpret().
- if (!e)
- continue;
-
- // A struct .init may contain void members.
- // Static array members are a weird special case (bug 10994).
- if (structlit && isVoid(e, true))
- {
- e = NULL;
- }
- else
- {
- e = scrubReturnValue(loc, e);
- if (CTFEExp::isCantExp(e) || e->op == TOKerror)
- return e;
- }
- (*elems)[i] = e;
- }
- return NULL;
-}
-
-Expression *scrubStructLiteral(Loc loc, StructLiteralExp *sle)
-{
- sle->ownedByCtfe = OWNEDcode;
- if (!(sle->stageflags & stageScrub))
- {
- const int old = sle->stageflags;
- sle->stageflags |= stageScrub; // prevent infinite recursion
- if (Expression *ex = scrubArray(loc, sle->elements, true))
- return ex;
- sle->stageflags = old;
- }
- return NULL;
-}
-
-/**************************************
- * Transitively set all .ownedByCtfe to OWNEDcache
- */
-Expression *scrubCacheValue(Expression *e)
-{
- if (!e)
- return e;
-
- if (e->op == TOKclassreference)
- {
- StructLiteralExp *sle = ((ClassReferenceExp*)e)->value;
- if (Expression *ex = scrubStructLiteralCache(sle))
- return ex;
- }
- else if (e->op == TOKstructliteral)
- {
- StructLiteralExp *sle = (StructLiteralExp *)e;
- if (Expression *ex = scrubStructLiteralCache(sle))
- return ex;
- }
- else if (e->op == TOKstring)
- {
- ((StringExp *)e)->ownedByCtfe = OWNEDcache;
- }
- else if (e->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
- ale->ownedByCtfe = OWNEDcache;
- if (Expression *ex = scrubArrayCache(ale->elements))
- return ex;
- }
- else if (e->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e;
- aae->ownedByCtfe = OWNEDcache;
- if (Expression *ex = scrubArrayCache(aae->keys))
- return ex;
- if (Expression *ex = scrubArrayCache(aae->values))
- return ex;
- }
- else if (e->op == TOKvector)
- {
- VectorExp *ve = (VectorExp *)e;
- ve->ownedByCtfe = OWNEDcache;
- if (ve->e1->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)ve->e1;
- ale->ownedByCtfe = OWNEDcache;
- if (Expression *ex = scrubArrayCache(ale->elements))
- return ex;
- }
- }
- return e;
-}
-
-Expression *scrubArrayCache(Expressions *elems)
-{
- for (size_t i = 0; i < elems->length; i++)
- {
- Expression *e = (*elems)[i];
- (*elems)[i] = scrubCacheValue(e);
- }
- return NULL;
-}
-
-Expression *scrubStructLiteralCache(StructLiteralExp *sle)
-{
- sle->ownedByCtfe = OWNEDcache;
- if (!(sle->stageflags & stageScrub))
- {
- const int old = sle->stageflags;
- sle->stageflags |= stageScrub; // prevent infinite recursion
- if (Expression *ex = scrubArrayCache(sle->elements))
- return ex;
- sle->stageflags = old;
- }
- return NULL;
-}
-
-/******************************* Special Functions ***************************/
-
-static Expression *interpret_length(UnionExp *pue, InterState *istate, Expression *earg)
-{
- //printf("interpret_length()\n");
- earg = interpret(pue, earg, istate);
- if (exceptionOrCantInterpret(earg))
- return earg;
- dinteger_t len = 0;
- if (earg->op == TOKassocarrayliteral)
- len = ((AssocArrayLiteralExp *)earg)->keys->length;
- else
- assert(earg->op == TOKnull);
- new(pue) IntegerExp(earg->loc, len, Type::tsize_t);
- return pue->exp();
-}
-
-static Expression *interpret_keys(UnionExp *pue, InterState *istate, Expression *earg, Type *returnType)
-{
- earg = interpret(pue, earg, istate);
- if (exceptionOrCantInterpret(earg))
- return earg;
- if (earg->op == TOKnull)
- {
- new(pue) NullExp(earg->loc, earg->type);
- return pue->exp();
- }
- if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray)
- return NULL;
- assert(earg->op == TOKassocarrayliteral);
- AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg;
- ArrayLiteralExp *ae = new ArrayLiteralExp(aae->loc, returnType, aae->keys);
- ae->ownedByCtfe = aae->ownedByCtfe;
- *pue = copyLiteral(ae);
- return pue->exp();
-}
-
-static Expression *interpret_values(UnionExp *pue, InterState *istate, Expression *earg, Type *returnType)
-{
- earg = interpret(pue, earg, istate);
- if (exceptionOrCantInterpret(earg))
- return earg;
- if (earg->op == TOKnull)
- {
- new(pue) NullExp(earg->loc, earg->type);
- return pue->exp();
- }
- if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray)
- return NULL;
- assert(earg->op == TOKassocarrayliteral);
- AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg;
- ArrayLiteralExp *ae = new ArrayLiteralExp(aae->loc, returnType, aae->values);
- ae->ownedByCtfe = aae->ownedByCtfe;
- //printf("result is %s\n", e->toChars());
- *pue = copyLiteral(ae);
- return pue->exp();
-}
-
-Expression *interpret_dup(UnionExp *pue, InterState *istate, Expression *earg)
-{
- earg = interpret(pue, earg, istate);
- if (exceptionOrCantInterpret(earg))
- return earg;
- if (earg->op == TOKnull)
- {
- new(pue) NullExp(earg->loc, earg->type);
- return pue->exp();
- }
- if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray)
- return NULL;
- assert(earg->op == TOKassocarrayliteral);
- AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)copyLiteral(earg).copy();
- for (size_t i = 0; i < aae->keys->length; i++)
- {
- if (Expression *e = evaluatePostblit(istate, (*aae->keys)[i]))
- return e;
- if (Expression *e = evaluatePostblit(istate, (*aae->values)[i]))
- return e;
- }
- aae->type = earg->type->mutableOf(); // repaint type from const(int[int]) to const(int)[int]
- //printf("result is %s\n", aae->toChars());
- return aae;
-}
-
-// signature is int delegate(ref Value) OR int delegate(ref Key, ref Value)
-Expression *interpret_aaApply(UnionExp *pue, InterState *istate, Expression *aa, Expression *deleg)
-{
- aa = interpret(aa, istate);
- if (exceptionOrCantInterpret(aa))
- return aa;
- if (aa->op != TOKassocarrayliteral)
- {
- new(pue) IntegerExp(deleg->loc, 0, Type::tsize_t);
- return pue->exp();
- }
-
- FuncDeclaration *fd = NULL;
- Expression *pthis = NULL;
- if (deleg->op == TOKdelegate)
- {
- fd = ((DelegateExp *)deleg)->func;
- pthis = ((DelegateExp *)deleg)->e1;
- }
- else if (deleg->op == TOKfunction)
- fd = ((FuncExp*)deleg)->fd;
-
- assert(fd && fd->fbody);
- assert(fd->parameters);
- size_t numParams = fd->parameters->length;
- assert(numParams == 1 || numParams == 2);
-
- Parameter *fparam = ((TypeFunction *)fd->type)->parameterList[numParams - 1];
- bool wantRefValue = 0 != (fparam->storageClass & (STCout | STCref));
-
- Expressions args;
- args.setDim(numParams);
-
- AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)aa;
- if (!ae->keys || ae->keys->length == 0)
- return new IntegerExp(deleg->loc, 0, Type::tsize_t);
- Expression *eresult;
-
- for (size_t i = 0; i < ae->keys->length; ++i)
- {
- Expression *ekey = (*ae->keys)[i];
- Expression *evalue = (*ae->values)[i];
- if (wantRefValue)
- {
- Type *t = evalue->type;
- evalue = new IndexExp(deleg->loc, ae, ekey);
- evalue->type = t;
- }
- args[numParams - 1] = evalue;
- if (numParams == 2)
- args[0] = ekey;
-
- UnionExp ue;
- eresult = interpretFunction(&ue, fd, istate, &args, pthis);
- if (eresult == ue.exp())
- eresult = ue.copy();
- if (exceptionOrCantInterpret(eresult))
- return eresult;
-
- assert(eresult->op == TOKint64);
- if (((IntegerExp *)eresult)->getInteger() != 0)
- return eresult;
- }
- return eresult;
-}
-
-/* Decoding UTF strings for foreach loops. Duplicates the functionality of
- * the twelve _aApplyXXn functions in aApply.d in the runtime.
- */
-static Expression *foreachApplyUtf(UnionExp *pue, InterState *istate, Expression *str, Expression *deleg, bool rvs)
-{
- FuncDeclaration *fd = NULL;
- Expression *pthis = NULL;
- if (deleg->op == TOKdelegate)
- {
- fd = ((DelegateExp *)deleg)->func;
- pthis = ((DelegateExp *)deleg)->e1;
- }
- else if (deleg->op == TOKfunction)
- fd = ((FuncExp*)deleg)->fd;
-
- assert(fd && fd->fbody);
- assert(fd->parameters);
- size_t numParams = fd->parameters->length;
- assert(numParams == 1 || numParams == 2);
- Type *charType = (*fd->parameters)[numParams-1]->type;
- Type *indexType = numParams == 2 ? (*fd->parameters)[0]->type
- : Type::tsize_t;
- size_t len = (size_t)resolveArrayLength(str);
- if (len == 0)
- {
- new(pue) IntegerExp(deleg->loc, 0, indexType);
- return pue->exp();
- }
-
- str = resolveSlice(str);
-
- StringExp *se = NULL;
- ArrayLiteralExp *ale = NULL;
- if (str->op == TOKstring)
- se = (StringExp *) str;
- else if (str->op == TOKarrayliteral)
- ale = (ArrayLiteralExp *)str;
- else
- {
- str->error("CTFE internal error: cannot foreach %s", str->toChars());
- return CTFEExp::cantexp;
- }
- Expressions args;
- args.setDim(numParams);
-
- Expression *eresult = NULL; // ded-store to prevent spurious warning
-
- // Buffers for encoding; also used for decoding array literals
- utf8_t utf8buf[4];
- unsigned short utf16buf[2];
-
- size_t start = rvs ? len : 0;
- size_t end = rvs ? 0: len;
- for (size_t indx = start; indx != end;)
- {
- // Step 1: Decode the next dchar from the string.
-
- const char *errmsg = NULL; // Used for reporting decoding errors
- dchar_t rawvalue; // Holds the decoded dchar
- size_t currentIndex = indx; // The index of the decoded character
-
- if (ale)
- {
- // If it is an array literal, copy the code points into the buffer
- size_t buflen = 1; // #code points in the buffer
- size_t n = 1; // #code points in this char
- size_t sz = (size_t)ale->type->nextOf()->size();
-
- switch (sz)
- {
- case 1:
- if (rvs)
- {
- // find the start of the string
- --indx;
- buflen = 1;
- while (indx > 0 && buflen < 4)
- {
- Expression * r = (*ale->elements)[indx];
- assert(r->op == TOKint64);
- utf8_t x = (utf8_t)(((IntegerExp *)r)->getInteger());
- if ((x & 0xC0) != 0x80)
- break;
- ++buflen;
- }
- }
- else
- buflen = (indx + 4 > len) ? len - indx : 4;
- for (size_t i = 0; i < buflen; ++i)
- {
- Expression * r = (*ale->elements)[indx + i];
- assert(r->op == TOKint64);
- utf8buf[i] = (utf8_t)(((IntegerExp *)r)->getInteger());
- }
- n = 0;
- errmsg = utf_decodeChar(&utf8buf[0], buflen, &n, &rawvalue);
- break;
- case 2:
- if (rvs)
- {
- // find the start of the string
- --indx;
- buflen = 1;
- Expression * r = (*ale->elements)[indx];
- assert(r->op == TOKint64);
- unsigned short x = (unsigned short)(((IntegerExp *)r)->getInteger());
- if (indx > 0 && x >= 0xDC00 && x <= 0xDFFF)
- {
- --indx;
- ++buflen;
- }
- }
- else
- buflen = (indx + 2 > len) ? len - indx : 2;
- for (size_t i=0; i < buflen; ++i)
- {
- Expression * r = (*ale->elements)[indx + i];
- assert(r->op == TOKint64);
- utf16buf[i] = (unsigned short)(((IntegerExp *)r)->getInteger());
- }
- n = 0;
- errmsg = utf_decodeWchar(&utf16buf[0], buflen, &n, &rawvalue);
- break;
- case 4:
- {
- if (rvs)
- --indx;
-
- Expression * r = (*ale->elements)[indx];
- assert(r->op == TOKint64);
- rawvalue = (dchar_t)((IntegerExp *)r)->getInteger();
- n = 1;
- }
- break;
- default:
- assert(0);
- }
- if (!rvs)
- indx += n;
- }
- else
- {
- // String literals
- size_t saveindx; // used for reverse iteration
-
- switch (se->sz)
- {
- case 1:
- if (rvs)
- {
- // find the start of the string
- utf8_t *s = (utf8_t *)se->string;
- --indx;
- while (indx > 0 && ((s[indx]&0xC0) == 0x80))
- --indx;
- saveindx = indx;
- }
- errmsg = utf_decodeChar((utf8_t *)se->string, se->len, &indx, &rawvalue);
- if (rvs)
- indx = saveindx;
- break;
- case 2:
- if (rvs)
- {
- // find the start
- unsigned short *s = (unsigned short *)se->string;
- --indx;
- if (s[indx] >= 0xDC00 && s[indx]<= 0xDFFF)
- --indx;
- saveindx = indx;
- }
- errmsg = utf_decodeWchar((unsigned short *)se->string, se->len, &indx, &rawvalue);
- if (rvs)
- indx = saveindx;
- break;
- case 4:
- if (rvs)
- --indx;
- rawvalue = ((unsigned *)(se->string))[indx];
- if (!rvs)
- ++indx;
- break;
- default:
- assert(0);
- }
- }
- if (errmsg)
- {
- deleg->error("%s", errmsg);
- return CTFEExp::cantexp;
- }
-
- // Step 2: encode the dchar in the target encoding
-
- int charlen = 1; // How many codepoints are involved?
- switch (charType->size())
- {
- case 1:
- charlen = utf_codeLengthChar(rawvalue);
- utf_encodeChar(&utf8buf[0], rawvalue);
- break;
- case 2:
- charlen = utf_codeLengthWchar(rawvalue);
- utf_encodeWchar(&utf16buf[0], rawvalue);
- break;
- case 4:
- break;
- default:
- assert(0);
- }
- if (rvs)
- currentIndex = indx;
-
- // Step 3: call the delegate once for each code point
-
- // The index only needs to be set once
- if (numParams == 2)
- args[0] = new IntegerExp(deleg->loc, currentIndex, indexType);
-
- Expression *val = NULL;
-
- for (int k= 0; k < charlen; ++k)
- {
- dchar_t codepoint;
- switch (charType->size())
- {
- case 1:
- codepoint = utf8buf[k];
- break;
- case 2:
- codepoint = utf16buf[k];
- break;
- case 4:
- codepoint = rawvalue;
- break;
- default:
- assert(0);
- }
- val = new IntegerExp(str->loc, codepoint, charType);
-
- args[numParams - 1] = val;
-
- UnionExp ue;
- eresult = interpretFunction(&ue, fd, istate, &args, pthis);
- if (eresult == ue.exp())
- eresult = ue.copy();
- if (exceptionOrCantInterpret(eresult))
- return eresult;
- assert(eresult->op == TOKint64);
- if (((IntegerExp *)eresult)->getInteger() != 0)
- return eresult;
- }
- }
- return eresult;
-}
-
-/* If this is a built-in function, return the interpreted result,
- * Otherwise, return NULL.
- */
-Expression *evaluateIfBuiltin(UnionExp *pue, InterState *istate, Loc loc,
- FuncDeclaration *fd, Expressions *arguments, Expression *pthis)
-{
- Expression *e = NULL;
- size_t nargs = arguments ? arguments->length : 0;
- if (!pthis)
- {
- if (isBuiltin(fd) != BUILTINunimp)
- {
- Expressions args;
- args.setDim(nargs);
- for (size_t i = 0; i < args.length; i++)
- {
- Expression *earg = (*arguments)[i];
- earg = interpret(earg, istate);
- if (exceptionOrCantInterpret(earg))
- return earg;
- args[i] = earg;
- }
- e = eval_builtin(loc, fd, &args);
- if (!e)
- {
- error(loc, "cannot evaluate unimplemented builtin %s at compile time", fd->toChars());
- e = CTFEExp::cantexp;
- }
- }
- }
- if (!pthis)
- {
- Expression *firstarg = nargs > 0 ? (*arguments)[0] : NULL;
- if (firstarg && firstarg->type->toBasetype()->ty == Taarray)
- {
- TypeAArray *firstAAtype = (TypeAArray *)firstarg->type;
- const Identifier *id = fd->ident;
- if (nargs == 1)
- {
- if (fd->ident == Id::aaLen)
- return interpret_length(pue, istate, firstarg);
-
- if (fd->toParent2()->ident == Id::object)
- {
- if (id == Id::keys)
- return interpret_keys(pue, istate, firstarg, firstAAtype->index->arrayOf());
- if (id == Id::values)
- return interpret_values(pue, istate, firstarg, firstAAtype->nextOf()->arrayOf());
- if (id == Id::rehash)
- return interpret(pue, firstarg, istate);
- if (id == Id::dup)
- return interpret_dup(pue, istate, firstarg);
- }
- }
- else // (nargs == 3)
- {
- if (id == Id::_aaApply)
- return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
- if (id == Id::_aaApply2)
- return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
- }
- }
- }
- if (pthis && !fd->fbody && fd->isCtorDeclaration() && fd->parent && fd->parent->parent && fd->parent->parent->ident == Id::object)
- {
- if (pthis->op == TOKclassreference && fd->parent->ident == Id::Throwable)
- {
- // At present, the constructors just copy their arguments into the struct.
- // But we might need some magic if stack tracing gets added to druntime.
- StructLiteralExp *se = ((ClassReferenceExp *)pthis)->value;
- assert(arguments->length <= se->elements->length);
- for (size_t i = 0; i < arguments->length; ++i)
- {
- e = interpret((*arguments)[i], istate);
- if (exceptionOrCantInterpret(e))
- return e;
- (*se->elements)[i] = e;
- }
- return CTFEExp::voidexp;
- }
- }
- if (nargs == 1 && !pthis &&
- (fd->ident == Id::criticalenter || fd->ident == Id::criticalexit))
- {
- // Support synchronized{} as a no-op
- return CTFEExp::voidexp;
- }
- if (!pthis)
- {
- const char *id = fd->ident->toChars();
- size_t idlen = strlen(id);
- if (nargs == 2 && (idlen == 10 || idlen == 11) &&
- !strncmp(id, "_aApply", 7))
- {
- // Functions from aApply.d and aApplyR.d in the runtime
- bool rvs = (idlen == 11); // true if foreach_reverse
- char c = id[idlen-3]; // char width: 'c', 'w', or 'd'
- char s = id[idlen-2]; // string width: 'c', 'w', or 'd'
- char n = id[idlen-1]; // numParams: 1 or 2.
- // There are 12 combinations
- if ((n == '1' || n == '2') &&
- (c == 'c' || c == 'w' || c == 'd') &&
- (s == 'c' || s == 'w' || s == 'd') && c != s)
- {
- Expression *str = (*arguments)[0];
- str = interpret(str, istate);
- if (exceptionOrCantInterpret(str))
- return str;
- return foreachApplyUtf(pue, istate, str, (*arguments)[1], rvs);
- }
- }
- }
- return e;
-}
-
-Expression *evaluatePostblit(InterState *istate, Expression *e)
-{
- Type *tb = e->type->baseElemOf();
- if (tb->ty != Tstruct)
- return NULL;
- StructDeclaration *sd = ((TypeStruct *)tb)->sym;
- if (!sd->postblit)
- return NULL;
-
- if (e->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e;
- for (size_t i = 0; i < ale->elements->length; i++)
- {
- e = evaluatePostblit(istate, (*ale->elements)[i]);
- if (e)
- return e;
- }
- return NULL;
- }
- if (e->op == TOKstructliteral)
- {
- // e.__postblit()
- UnionExp ue;
- e = interpretFunction(&ue, sd->postblit, istate, NULL, e);
- if (e == ue.exp())
- e = ue.copy();
- if (exceptionOrCantInterpret(e))
- return e;
- return NULL;
- }
- assert(0);
- return NULL;
-}
-
-Expression *evaluateDtor(InterState *istate, Expression *e)
-{
- Type *tb = e->type->baseElemOf();
- if (tb->ty != Tstruct)
- return NULL;
- StructDeclaration *sd = ((TypeStruct *)tb)->sym;
- if (!sd->dtor)
- return NULL;
-
- UnionExp ue;
- if (e->op == TOKarrayliteral)
- {
- ArrayLiteralExp *alex = (ArrayLiteralExp *)e;
- for (size_t i = alex->elements->length; 0 < i--; )
- e = evaluateDtor(istate, (*alex->elements)[i]);
- }
- else if (e->op == TOKstructliteral)
- {
- // e.__dtor()
- e = interpretFunction(&ue, sd->dtor, istate, NULL, e);
- }
- else
- assert(0);
- if (exceptionOrCantInterpret(e))
- {
- if (e == ue.exp())
- e = ue.copy();
- return e;
- }
- return NULL;
-}
-
-/*************************** CTFE Sanity Checks ***************************/
-
-/* Setter functions for CTFE variable values.
- * These functions exist to check for compiler CTFE bugs.
- */
-bool hasValue(VarDeclaration *vd)
-{
- if (vd->ctfeAdrOnStack == -1)
- return false;
- return NULL != getValue(vd);
-}
-
-Expression *getValue(VarDeclaration *vd)
-{
- return ctfeStack.getValue(vd);
-}
-
-void setValueNull(VarDeclaration *vd)
-{
- ctfeStack.setValue(vd, NULL);
-}
-
-// Don't check for validity
-void setValueWithoutChecking(VarDeclaration *vd, Expression *newval)
-{
- ctfeStack.setValue(vd, newval);
-}
-
-void setValue(VarDeclaration *vd, Expression *newval)
-{
- assert((vd->storage_class & (STCout | STCref))
- ? isCtfeReferenceValid(newval)
- : isCtfeValueValid(newval));
- ctfeStack.setValue(vd, newval);
-}
diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d
new file mode 100644
index 00000000000..541fac7dfb4
--- /dev/null
+++ b/gcc/d/dmd/dinterpret.d
@@ -0,0 +1,7487 @@
+/**
+ * The entry point for CTFE.
+ *
+ * Specification: ($LINK2 https://dlang.org/spec/function.html#interpretation, Compile Time Function Execution (CTFE))
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dinterpret.d, _dinterpret.d)
+ * Documentation: https://dlang.org/phobos/dmd_dinterpret.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dinterpret.d
+ */
+
+module dmd.dinterpret;
+
+import core.stdc.stdio;
+import core.stdc.stdlib;
+import core.stdc.string;
+import dmd.apply;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.builtin;
+import dmd.constfold;
+import dmd.ctfeexpr;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.initsem;
+import dmd.mtype;
+import dmd.printast;
+import dmd.root.rmem;
+import dmd.root.array;
+import dmd.root.region;
+import dmd.root.rootobject;
+import dmd.statement;
+import dmd.tokens;
+import dmd.utf;
+import dmd.visitor;
+
+/*************************************
+ * Entry point for CTFE.
+ * A compile-time result is required. Give an error if not possible.
+ *
+ * `e` must be semantically valid expression. In other words, it should not
+ * contain any `ErrorExp`s in it. But, CTFE interpretation will cross over
+ * functions and may invoke a function that contains `ErrorStatement` in its body.
+ * If that, the "CTFE failed because of previous errors" error is raised.
+ */
+public Expression ctfeInterpret(Expression e)
+{
+ switch (e.op)
+ {
+ case TOK.int64:
+ case TOK.float64:
+ case TOK.complex80:
+ case TOK.null_:
+ case TOK.void_:
+ case TOK.string_:
+ case TOK.this_:
+ case TOK.super_:
+ case TOK.type:
+ case TOK.typeid_:
+ case TOK.template_: // non-eponymous template/instance
+ case TOK.scope_: // ditto
+ case TOK.dotTemplateDeclaration: // ditto, e.e1 doesn't matter here
+ case TOK.dot: // ditto
+ if (e.type.ty == Terror)
+ return ErrorExp.get();
+ goto case TOK.error;
+
+ case TOK.error:
+ return e;
+
+ default:
+ break;
+ }
+
+ assert(e.type); // https://issues.dlang.org/show_bug.cgi?id=14642
+ //assert(e.type.ty != Terror); // FIXME
+ if (e.type.ty == Terror)
+ return ErrorExp.get();
+
+ auto rgnpos = ctfeGlobals.region.savePos();
+
+ Expression result = interpret(e, null);
+
+ result = copyRegionExp(result);
+
+ if (!CTFEExp.isCantExp(result))
+ result = scrubReturnValue(e.loc, result);
+ if (CTFEExp.isCantExp(result))
+ result = ErrorExp.get();
+
+ ctfeGlobals.region.release(rgnpos);
+
+ return result;
+}
+
+/* Run CTFE on the expression, but allow the expression to be a TypeExp
+ * or a tuple containing a TypeExp. (This is required by pragma(msg)).
+ */
+public Expression ctfeInterpretForPragmaMsg(Expression e)
+{
+ if (e.op == TOK.error || e.op == TOK.type)
+ return e;
+
+ // It's also OK for it to be a function declaration (happens only with
+ // __traits(getOverloads))
+ if (auto ve = e.isVarExp())
+ if (ve.var.isFuncDeclaration())
+ {
+ return e;
+ }
+
+ auto tup = e.isTupleExp();
+ if (!tup)
+ return e.ctfeInterpret();
+
+ // Tuples need to be treated separately, since they are
+ // allowed to contain a TypeExp in this case.
+
+ Expressions* expsx = null;
+ foreach (i, g; *tup.exps)
+ {
+ auto h = ctfeInterpretForPragmaMsg(g);
+ if (h != g)
+ {
+ if (!expsx)
+ {
+ expsx = tup.exps.copy();
+ }
+ (*expsx)[i] = h;
+ }
+ }
+ if (expsx)
+ {
+ auto te = new TupleExp(e.loc, expsx);
+ expandTuples(te.exps);
+ te.type = new TypeTuple(te.exps);
+ return te;
+ }
+ return e;
+}
+
+public extern (C++) Expression getValue(VarDeclaration vd)
+{
+ return ctfeGlobals.stack.getValue(vd);
+}
+
+/*************************************************
+ * Allocate an Expression in the ctfe region.
+ * Params:
+ * T = type of Expression to allocate
+ * args = arguments to Expression's constructor
+ * Returns:
+ * allocated Expression
+ */
+T ctfeEmplaceExp(T : Expression, Args...)(Args args)
+{
+ if (mem.isGCEnabled)
+ return new T(args);
+ auto p = ctfeGlobals.region.malloc(__traits(classInstanceSize, T));
+ emplaceExp!T(p, args);
+ return cast(T)p;
+}
+
+// CTFE diagnostic information
+public extern (C++) void printCtfePerformanceStats()
+{
+ debug (SHOWPERFORMANCE)
+ {
+ printf(" ---- CTFE Performance ----\n");
+ printf("max call depth = %d\tmax stack = %d\n", ctfeGlobals.maxCallDepth, ctfeGlobals.stack.maxStackUsage());
+ printf("array allocs = %d\tassignments = %d\n\n", ctfeGlobals.numArrayAllocs, ctfeGlobals.numAssignments);
+ }
+}
+
+/**************************
+ */
+
+void incArrayAllocs()
+{
+ ++ctfeGlobals.numArrayAllocs;
+}
+
+/* ================================================ Implementation ======================================= */
+
+private:
+
+/***************
+ * Collect together globals used by CTFE
+ */
+struct CtfeGlobals
+{
+ Region region;
+
+ CtfeStack stack;
+
+ int callDepth = 0; // current number of recursive calls
+
+ // When printing a stack trace, suppress this number of calls
+ int stackTraceCallsToSuppress = 0;
+
+ int maxCallDepth = 0; // highest number of recursive calls
+ int numArrayAllocs = 0; // Number of allocated arrays
+ int numAssignments = 0; // total number of assignments executed
+}
+
+__gshared CtfeGlobals ctfeGlobals;
+
+enum CTFEGoal : int
+{
+ RValue, /// Must return an Rvalue (== CTFE value)
+ LValue, /// Must return an Lvalue (== CTFE reference)
+ Nothing, /// The return value is not required
+}
+
+//debug = LOG;
+//debug = LOGASSIGN;
+//debug = LOGCOMPILE;
+//debug = SHOWPERFORMANCE;
+
+// Maximum allowable recursive function calls in CTFE
+enum CTFE_RECURSION_LIMIT = 1000;
+
+/**
+ The values of all CTFE variables
+ */
+struct CtfeStack
+{
+private:
+ /* The stack. Every declaration we encounter is pushed here,
+ * together with the VarDeclaration, and the previous
+ * stack address of that variable, so that we can restore it
+ * when we leave the stack frame.
+ * Note that when a function is forward referenced, the interpreter must
+ * run semantic3, and that may start CTFE again with a NULL istate. Thus
+ * the stack might not be empty when CTFE begins.
+ *
+ * Ctfe Stack addresses are just 0-based integers, but we save
+ * them as 'void *' because Array can only do pointers.
+ */
+ Expressions values; // values on the stack
+ VarDeclarations vars; // corresponding variables
+ Array!(void*) savedId; // id of the previous state of that var
+
+ Array!(void*) frames; // all previous frame pointers
+ Expressions savedThis; // all previous values of localThis
+
+ /* Global constants get saved here after evaluation, so we never
+ * have to redo them. This saves a lot of time and memory.
+ */
+ Expressions globalValues; // values of global constants
+
+ size_t framepointer; // current frame pointer
+ size_t maxStackPointer; // most stack we've ever used
+ Expression localThis; // value of 'this', or NULL if none
+
+public:
+ extern (C++) size_t stackPointer()
+ {
+ return values.dim;
+ }
+
+ // The current value of 'this', or NULL if none
+ extern (C++) Expression getThis()
+ {
+ return localThis;
+ }
+
+ // Largest number of stack positions we've used
+ extern (C++) size_t maxStackUsage()
+ {
+ return maxStackPointer;
+ }
+
+ // Start a new stack frame, using the provided 'this'.
+ extern (C++) void startFrame(Expression thisexp)
+ {
+ frames.push(cast(void*)cast(size_t)framepointer);
+ savedThis.push(localThis);
+ framepointer = stackPointer();
+ localThis = thisexp;
+ }
+
+ extern (C++) void endFrame()
+ {
+ size_t oldframe = cast(size_t)frames[frames.dim - 1];
+ localThis = savedThis[savedThis.dim - 1];
+ popAll(framepointer);
+ framepointer = oldframe;
+ frames.setDim(frames.dim - 1);
+ savedThis.setDim(savedThis.dim - 1);
+ }
+
+ extern (C++) bool isInCurrentFrame(VarDeclaration v)
+ {
+ if (v.isDataseg() && !v.isCTFE())
+ return false; // It's a global
+ return v.ctfeAdrOnStack >= framepointer;
+ }
+
+ extern (C++) Expression getValue(VarDeclaration v)
+ {
+ //printf("getValue() %s\n", v.toChars());
+ if ((v.isDataseg() || v.storage_class & STC.manifest) && !v.isCTFE())
+ {
+ assert(v.ctfeAdrOnStack < globalValues.dim);
+ return globalValues[v.ctfeAdrOnStack];
+ }
+ assert(v.ctfeAdrOnStack < stackPointer());
+ return values[v.ctfeAdrOnStack];
+ }
+
+ extern (C++) void setValue(VarDeclaration v, Expression e)
+ {
+ //printf("setValue() %s : %s\n", v.toChars(), e.toChars());
+ assert(!v.isDataseg() || v.isCTFE());
+ assert(v.ctfeAdrOnStack < stackPointer());
+ values[v.ctfeAdrOnStack] = e;
+ }
+
+ extern (C++) void push(VarDeclaration v)
+ {
+ //printf("push() %s\n", v.toChars());
+ assert(!v.isDataseg() || v.isCTFE());
+ if (v.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone && v.ctfeAdrOnStack >= framepointer)
+ {
+ // Already exists in this frame, reuse it.
+ values[v.ctfeAdrOnStack] = null;
+ return;
+ }
+ savedId.push(cast(void*)cast(size_t)v.ctfeAdrOnStack);
+ v.ctfeAdrOnStack = cast(uint)values.dim;
+ vars.push(v);
+ values.push(null);
+ }
+
+ extern (C++) void pop(VarDeclaration v)
+ {
+ assert(!v.isDataseg() || v.isCTFE());
+ assert(!v.isReference());
+ const oldid = v.ctfeAdrOnStack;
+ v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[oldid];
+ if (v.ctfeAdrOnStack == values.dim - 1)
+ {
+ values.pop();
+ vars.pop();
+ savedId.pop();
+ }
+ }
+
+ extern (C++) void popAll(size_t stackpointer)
+ {
+ if (stackPointer() > maxStackPointer)
+ maxStackPointer = stackPointer();
+ assert(values.dim >= stackpointer);
+ for (size_t i = stackpointer; i < values.dim; ++i)
+ {
+ VarDeclaration v = vars[i];
+ v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[i];
+ }
+ values.setDim(stackpointer);
+ vars.setDim(stackpointer);
+ savedId.setDim(stackpointer);
+ }
+
+ extern (C++) void saveGlobalConstant(VarDeclaration v, Expression e)
+ {
+ assert(v._init && (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest) && !v.isCTFE());
+ v.ctfeAdrOnStack = cast(uint)globalValues.dim;
+ globalValues.push(copyRegionExp(e));
+ }
+}
+
+private struct InterState
+{
+ InterState* caller; // calling function's InterState
+ FuncDeclaration fd; // function being interpreted
+ Statement start; // if !=NULL, start execution at this statement
+
+ /* target of CTFEExp result; also
+ * target of labelled CTFEExp or
+ * CTFEExp. (null if no label).
+ */
+ Statement gotoTarget;
+}
+
+/*************************************
+ * Attempt to interpret a function given the arguments.
+ * Params:
+ * pue = storage for result
+ * fd = function being called
+ * istate = state for calling function (NULL if none)
+ * arguments = function arguments
+ * thisarg = 'this', if a needThis() function, NULL if not.
+ *
+ * Returns:
+ * result expression if successful, TOK.cantExpression if not,
+ * or CTFEExp if function returned void.
+ */
+private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterState* istate, Expressions* arguments, Expression thisarg)
+{
+ debug (LOG)
+ {
+ printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", fd.loc.toChars(), istate, fd.toChars());
+ }
+ assert(pue);
+ if (fd.semanticRun == PASS.semantic3)
+ {
+ fd.error("circular dependency. Functions cannot be interpreted while being compiled");
+ return CTFEExp.cantexp;
+ }
+ if (!fd.functionSemantic3())
+ return CTFEExp.cantexp;
+ if (fd.semanticRun < PASS.semantic3done)
+ {
+ fd.error("circular dependency. Functions cannot be interpreted while being compiled");
+ return CTFEExp.cantexp;
+ }
+
+ auto tf = fd.type.toBasetype().isTypeFunction();
+ if (tf.parameterList.varargs != VarArg.none && arguments &&
+ ((fd.parameters && arguments.dim != fd.parameters.dim) || (!fd.parameters && arguments.dim)))
+ {
+ fd.error("C-style variadic functions are not yet implemented in CTFE");
+ return CTFEExp.cantexp;
+ }
+
+ // Nested functions always inherit the 'this' pointer from the parent,
+ // except for delegates. (Note that the 'this' pointer may be null).
+ // Func literals report isNested() even if they are in global scope,
+ // so we need to check that the parent is a function.
+ if (fd.isNested() && fd.toParentLocal().isFuncDeclaration() && !thisarg && istate)
+ thisarg = ctfeGlobals.stack.getThis();
+
+ if (fd.needThis() && !thisarg)
+ {
+ // error, no this. Prevent segfault.
+ // Here should be unreachable by the strict 'this' check in front-end.
+ fd.error("need `this` to access member `%s`", fd.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ // Place to hold all the arguments to the function while
+ // we are evaluating them.
+ size_t dim = arguments ? arguments.dim : 0;
+ assert((fd.parameters ? fd.parameters.dim : 0) == dim);
+
+ /* Evaluate all the arguments to the function,
+ * store the results in eargs[]
+ */
+ Expressions eargs = Expressions(dim);
+ for (size_t i = 0; i < dim; i++)
+ {
+ Expression earg = (*arguments)[i];
+ Parameter fparam = tf.parameterList[i];
+
+ if (fparam.isReference())
+ {
+ if (!istate && (fparam.storageClass & STC.out_))
+ {
+ // initializing an out parameter involves writing to it.
+ earg.error("global `%s` cannot be passed as an `out` parameter at compile time", earg.toChars());
+ return CTFEExp.cantexp;
+ }
+ // Convert all reference arguments into lvalue references
+ earg = interpretRegion(earg, istate, CTFEGoal.LValue);
+ if (CTFEExp.isCantExp(earg))
+ return earg;
+ }
+ else if (fparam.storageClass & STC.lazy_)
+ {
+ }
+ else
+ {
+ /* Value parameters
+ */
+ Type ta = fparam.type.toBasetype();
+ if (ta.ty == Tsarray)
+ if (auto eaddr = earg.isAddrExp())
+ {
+ /* Static arrays are passed by a simple pointer.
+ * Skip past this to get at the actual arg.
+ */
+ earg = eaddr.e1;
+ }
+
+ earg = interpretRegion(earg, istate);
+ if (CTFEExp.isCantExp(earg))
+ return earg;
+
+ /* Struct literals are passed by value, but we don't need to
+ * copy them if they are passed as const
+ */
+ if (earg.op == TOK.structLiteral && !(fparam.storageClass & (STC.const_ | STC.immutable_)))
+ earg = copyLiteral(earg).copy();
+ }
+ if (auto tee = earg.isThrownExceptionExp())
+ {
+ if (istate)
+ return tee;
+ tee.generateUncaughtError();
+ return CTFEExp.cantexp;
+ }
+ eargs[i] = earg;
+ }
+
+ // Now that we've evaluated all the arguments, we can start the frame
+ // (this is the moment when the 'call' actually takes place).
+ InterState istatex;
+ istatex.caller = istate;
+ istatex.fd = fd;
+
+ if (fd.isThis2)
+ {
+ Expression arg0 = thisarg;
+ if (arg0 && arg0.type.ty == Tstruct)
+ {
+ Type t = arg0.type.pointerTo();
+ arg0 = ctfeEmplaceExp!AddrExp(arg0.loc, arg0);
+ arg0.type = t;
+ }
+ auto elements = new Expressions(2);
+ (*elements)[0] = arg0;
+ (*elements)[1] = ctfeGlobals.stack.getThis();
+ Type t2 = Type.tvoidptr.sarrayOf(2);
+ const loc = thisarg ? thisarg.loc : fd.loc;
+ thisarg = ctfeEmplaceExp!ArrayLiteralExp(loc, t2, elements);
+ thisarg = ctfeEmplaceExp!AddrExp(loc, thisarg);
+ thisarg.type = t2.pointerTo();
+ }
+
+ ctfeGlobals.stack.startFrame(thisarg);
+ if (fd.vthis && thisarg)
+ {
+ ctfeGlobals.stack.push(fd.vthis);
+ setValue(fd.vthis, thisarg);
+ }
+
+ for (size_t i = 0; i < dim; i++)
+ {
+ Expression earg = eargs[i];
+ Parameter fparam = tf.parameterList[i];
+ VarDeclaration v = (*fd.parameters)[i];
+ debug (LOG)
+ {
+ printf("arg[%zu] = %s\n", i, earg.toChars());
+ }
+ ctfeGlobals.stack.push(v);
+
+ if (fparam.isReference() && earg.op == TOK.variable &&
+ earg.isVarExp().var.toParent2() == fd)
+ {
+ VarDeclaration vx = earg.isVarExp().var.isVarDeclaration();
+ if (!vx)
+ {
+ fd.error("cannot interpret `%s` as a `ref` parameter", earg.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ /* vx is a variable that is declared in fd.
+ * It means that fd is recursively called. e.g.
+ *
+ * void fd(int n, ref int v = dummy) {
+ * int vx;
+ * if (n == 1) fd(2, vx);
+ * }
+ * fd(1);
+ *
+ * The old value of vx on the stack in fd(1)
+ * should be saved at the start of fd(2, vx) call.
+ */
+ const oldadr = vx.ctfeAdrOnStack;
+
+ ctfeGlobals.stack.push(vx);
+ assert(!hasValue(vx)); // vx is made uninitialized
+
+ // https://issues.dlang.org/show_bug.cgi?id=14299
+ // v.ctfeAdrOnStack should be saved already
+ // in the stack before the overwrite.
+ v.ctfeAdrOnStack = oldadr;
+ assert(hasValue(v)); // ref parameter v should refer existing value.
+ }
+ else
+ {
+ // Value parameters and non-trivial references
+ setValueWithoutChecking(v, earg);
+ }
+ debug (LOG)
+ {
+ printf("interpreted arg[%zu] = %s\n", i, earg.toChars());
+ showCtfeExpr(earg);
+ }
+ debug (LOGASSIGN)
+ {
+ printf("interpreted arg[%zu] = %s\n", i, earg.toChars());
+ showCtfeExpr(earg);
+ }
+ }
+
+ if (fd.vresult)
+ ctfeGlobals.stack.push(fd.vresult);
+
+ // Enter the function
+ ++ctfeGlobals.callDepth;
+ if (ctfeGlobals.callDepth > ctfeGlobals.maxCallDepth)
+ ctfeGlobals.maxCallDepth = ctfeGlobals.callDepth;
+
+ Expression e = null;
+ while (1)
+ {
+ if (ctfeGlobals.callDepth > CTFE_RECURSION_LIMIT)
+ {
+ // This is a compiler error. It must not be suppressed.
+ global.gag = 0;
+ fd.error("CTFE recursion limit exceeded");
+ e = CTFEExp.cantexp;
+ break;
+ }
+ e = interpret(pue, fd.fbody, &istatex);
+ if (CTFEExp.isCantExp(e))
+ {
+ debug (LOG)
+ {
+ printf("function body failed to interpret\n");
+ }
+ }
+
+ if (istatex.start)
+ {
+ fd.error("CTFE internal error: failed to resume at statement `%s`", istatex.start.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ /* This is how we deal with a recursive statement AST
+ * that has arbitrary goto statements in it.
+ * Bubble up a 'result' which is the target of the goto
+ * statement, then go recursively down the AST looking
+ * for that statement, then execute starting there.
+ */
+ if (CTFEExp.isGotoExp(e))
+ {
+ istatex.start = istatex.gotoTarget; // set starting statement
+ istatex.gotoTarget = null;
+ }
+ else
+ {
+ assert(!e || (e.op != TOK.continue_ && e.op != TOK.break_));
+ break;
+ }
+ }
+ // If fell off the end of a void function, return void
+ if (!e && tf.next.ty == Tvoid)
+ e = CTFEExp.voidexp;
+ if (tf.isref && e.op == TOK.variable && e.isVarExp().var == fd.vthis)
+ e = thisarg;
+ if (tf.isref && fd.isThis2 && e.op == TOK.index)
+ {
+ auto ie = e.isIndexExp();
+ auto pe = ie.e1.isPtrExp();
+ auto ve = !pe ? null : pe.e1.isVarExp();
+ if (ve && ve.var == fd.vthis)
+ {
+ auto ne = ie.e2.isIntegerExp();
+ assert(ne);
+ auto ale = thisarg.isAddrExp().e1.isArrayLiteralExp();
+ e = (*ale.elements)[cast(size_t)ne.getInteger()];
+ if (auto ae = e.isAddrExp())
+ {
+ e = ae.e1;
+ }
+ }
+ }
+ assert(e !is null);
+
+ // Leave the function
+ --ctfeGlobals.callDepth;
+
+ ctfeGlobals.stack.endFrame();
+
+ // If it generated an uncaught exception, report error.
+ if (!istate && e.isThrownExceptionExp())
+ {
+ if (e == pue.exp())
+ e = pue.copy();
+ e.isThrownExceptionExp().generateUncaughtError();
+ e = CTFEExp.cantexp;
+ }
+
+ return e;
+}
+
+/// used to collect coverage information in ctfe
+void incUsageCtfe(InterState* istate, const ref Loc loc)
+{
+ if (global.params.ctfe_cov && istate)
+ {
+ auto line = loc.linnum;
+ auto mod = istate.fd.getModule();
+
+ ++mod.ctfe_cov[line];
+ }
+}
+
+private extern (C++) final class Interpreter : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ InterState* istate;
+ CTFEGoal goal;
+ Expression result;
+ UnionExp* pue; // storage for `result`
+
+ extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal)
+ {
+ this.pue = pue;
+ this.istate = istate;
+ this.goal = goal;
+ }
+
+ // If e is TOK.throw_exception or TOK.cantExpression,
+ // set it to 'result' and returns true.
+ bool exceptionOrCant(Expression e)
+ {
+ if (exceptionOrCantInterpret(e))
+ {
+ // Make sure e is not pointing to a stack temporary
+ result = (e.op == TOK.cantExpression) ? CTFEExp.cantexp : e;
+ return true;
+ }
+ return false;
+ }
+
+ static Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original)
+ {
+ if (exps is original)
+ {
+ if (!original)
+ exps = new Expressions();
+ else
+ exps = original.copy();
+ ++ctfeGlobals.numArrayAllocs;
+ }
+ return exps;
+ }
+
+ /******************************** Statement ***************************/
+
+ override void visit(Statement s)
+ {
+ debug (LOG)
+ {
+ printf("%s Statement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+
+ s.error("statement `%s` cannot be interpreted at compile time", s.toChars());
+ result = CTFEExp.cantexp;
+ }
+
+ override void visit(ExpStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s ExpStatement::interpret(%s)\n", s.loc.toChars(), s.exp ? s.exp.toChars() : "");
+ }
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+ if (s.exp && s.exp.hasCode)
+ incUsageCtfe(istate, s.loc);
+
+ Expression e = interpret(pue, s.exp, istate, CTFEGoal.Nothing);
+ if (exceptionOrCant(e))
+ return;
+ }
+
+ override void visit(CompoundStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s CompoundStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start == s)
+ istate.start = null;
+
+ const dim = s.statements ? s.statements.dim : 0;
+ foreach (i; 0 .. dim)
+ {
+ Statement sx = (*s.statements)[i];
+ result = interpret(pue, sx, istate);
+ if (result)
+ break;
+ }
+ debug (LOG)
+ {
+ printf("%s -CompoundStatement::interpret() %p\n", s.loc.toChars(), result);
+ }
+ }
+
+ override void visit(UnrolledLoopStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s UnrolledLoopStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start == s)
+ istate.start = null;
+
+ const dim = s.statements ? s.statements.dim : 0;
+ foreach (i; 0 .. dim)
+ {
+ Statement sx = (*s.statements)[i];
+ Expression e = interpret(pue, sx, istate);
+ if (!e) // succeeds to interpret, or goto target was not found
+ continue;
+ if (exceptionOrCant(e))
+ return;
+ if (e.op == TOK.break_)
+ {
+ if (istate.gotoTarget && istate.gotoTarget != s)
+ {
+ result = e; // break at a higher level
+ return;
+ }
+ istate.gotoTarget = null;
+ result = null;
+ return;
+ }
+ if (e.op == TOK.continue_)
+ {
+ if (istate.gotoTarget && istate.gotoTarget != s)
+ {
+ result = e; // continue at a higher level
+ return;
+ }
+ istate.gotoTarget = null;
+ continue;
+ }
+
+ // expression from return statement, or thrown exception
+ result = e;
+ break;
+ }
+ }
+
+ override void visit(IfStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s IfStatement::interpret(%s)\n", s.loc.toChars(), s.condition.toChars());
+ }
+ incUsageCtfe(istate, s.loc);
+ if (istate.start == s)
+ istate.start = null;
+ if (istate.start)
+ {
+ Expression e = null;
+ e = interpret(s.ifbody, istate);
+ if (!e && istate.start)
+ e = interpret(s.elsebody, istate);
+ result = e;
+ return;
+ }
+
+ UnionExp ue = void;
+ Expression e = interpret(&ue, s.condition, istate);
+ assert(e);
+ if (exceptionOrCant(e))
+ return;
+
+ if (isTrueBool(e))
+ result = interpret(pue, s.ifbody, istate);
+ else if (e.isBool(false))
+ result = interpret(pue, s.elsebody, istate);
+ else
+ {
+ // no error, or assert(0)?
+ result = CTFEExp.cantexp;
+ }
+ }
+
+ override void visit(ScopeStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s ScopeStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start == s)
+ istate.start = null;
+
+ result = interpret(pue, s.statement, istate);
+ }
+
+ /**
+ Given an expression e which is about to be returned from the current
+ function, generate an error if it contains pointers to local variables.
+
+ Only checks expressions passed by value (pointers to local variables
+ may already be stored in members of classes, arrays, or AAs which
+ were passed as mutable function parameters).
+ Returns:
+ true if it is safe to return, false if an error was generated.
+ */
+ static bool stopPointersEscaping(const ref Loc loc, Expression e)
+ {
+ if (!e.type.hasPointers())
+ return true;
+ if (isPointer(e.type))
+ {
+ Expression x = e;
+ if (auto eaddr = e.isAddrExp())
+ x = eaddr.e1;
+ VarDeclaration v;
+ while (x.op == TOK.variable && (v = (cast(VarExp)x).var.isVarDeclaration()) !is null)
+ {
+ if (v.storage_class & STC.ref_)
+ {
+ x = getValue(v);
+ if (auto eaddr = e.isAddrExp())
+ eaddr.e1 = x;
+ continue;
+ }
+ if (ctfeGlobals.stack.isInCurrentFrame(v))
+ {
+ error(loc, "returning a pointer to a local stack variable");
+ return false;
+ }
+ else
+ break;
+ }
+ // TODO: If it is a TOK.dotVariable or TOK.index, we should check that it is not
+ // pointing to a local struct or static array.
+ }
+ if (auto se = e.isStructLiteralExp())
+ {
+ return stopPointersEscapingFromArray(loc, se.elements);
+ }
+ if (auto ale = e.isArrayLiteralExp())
+ {
+ return stopPointersEscapingFromArray(loc, ale.elements);
+ }
+ if (auto aae = e.isAssocArrayLiteralExp())
+ {
+ if (!stopPointersEscapingFromArray(loc, aae.keys))
+ return false;
+ return stopPointersEscapingFromArray(loc, aae.values);
+ }
+ return true;
+ }
+
+ // Check all elements of an array for escaping local variables. Return false if error
+ static bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems)
+ {
+ foreach (e; *elems)
+ {
+ if (e && !stopPointersEscaping(loc, e))
+ return false;
+ }
+ return true;
+ }
+
+ override void visit(ReturnStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s ReturnStatement::interpret(%s)\n", s.loc.toChars(), s.exp ? s.exp.toChars() : "");
+ }
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+
+ if (!s.exp)
+ {
+ result = CTFEExp.voidexp;
+ return;
+ }
+
+ incUsageCtfe(istate, s.loc);
+ assert(istate && istate.fd && istate.fd.type && istate.fd.type.ty == Tfunction);
+ TypeFunction tf = cast(TypeFunction)istate.fd.type;
+
+ /* If the function returns a ref AND it's been called from an assignment,
+ * we need to return an lvalue. Otherwise, just do an (rvalue) interpret.
+ */
+ if (tf.isref)
+ {
+ result = interpret(pue, s.exp, istate, CTFEGoal.LValue);
+ return;
+ }
+ if (tf.next && tf.next.ty == Tdelegate && istate.fd.closureVars.dim > 0)
+ {
+ // To support this, we need to copy all the closure vars
+ // into the delegate literal.
+ s.error("closures are not yet supported in CTFE");
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ // We need to treat pointers specially, because TOK.symbolOffset can be used to
+ // return a value OR a pointer
+ Expression e = interpret(pue, s.exp, istate);
+ if (exceptionOrCant(e))
+ return;
+
+ // Disallow returning pointers to stack-allocated variables (bug 7876)
+ if (!stopPointersEscaping(s.loc, e))
+ {
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ if (needToCopyLiteral(e))
+ e = copyLiteral(e).copy();
+ debug (LOGASSIGN)
+ {
+ printf("RETURN %s\n", s.loc.toChars());
+ showCtfeExpr(e);
+ }
+ result = e;
+ }
+
+ static Statement findGotoTarget(InterState* istate, Identifier ident)
+ {
+ Statement target = null;
+ if (ident)
+ {
+ LabelDsymbol label = istate.fd.searchLabel(ident);
+ assert(label && label.statement);
+ LabelStatement ls = label.statement;
+ target = ls.gotoTarget ? ls.gotoTarget : ls.statement;
+ }
+ return target;
+ }
+
+ override void visit(BreakStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s BreakStatement::interpret()\n", s.loc.toChars());
+ }
+ incUsageCtfe(istate, s.loc);
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+
+ istate.gotoTarget = findGotoTarget(istate, s.ident);
+ result = CTFEExp.breakexp;
+ }
+
+ override void visit(ContinueStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s ContinueStatement::interpret()\n", s.loc.toChars());
+ }
+ incUsageCtfe(istate, s.loc);
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+
+ istate.gotoTarget = findGotoTarget(istate, s.ident);
+ result = CTFEExp.continueexp;
+ }
+
+ override void visit(WhileStatement s)
+ {
+ debug (LOG)
+ {
+ printf("WhileStatement::interpret()\n");
+ }
+ assert(0); // rewritten to ForStatement
+ }
+
+ override void visit(DoStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s DoStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start == s)
+ istate.start = null;
+
+ while (1)
+ {
+ Expression e = interpret(s._body, istate);
+ if (!e && istate.start) // goto target was not found
+ return;
+ assert(!istate.start);
+
+ if (exceptionOrCant(e))
+ return;
+ if (e && e.op == TOK.break_)
+ {
+ if (istate.gotoTarget && istate.gotoTarget != s)
+ {
+ result = e; // break at a higher level
+ return;
+ }
+ istate.gotoTarget = null;
+ break;
+ }
+ if (e && e.op == TOK.continue_)
+ {
+ if (istate.gotoTarget && istate.gotoTarget != s)
+ {
+ result = e; // continue at a higher level
+ return;
+ }
+ istate.gotoTarget = null;
+ e = null;
+ }
+ if (e)
+ {
+ result = e; // bubbled up from ReturnStatement
+ return;
+ }
+
+ UnionExp ue = void;
+ incUsageCtfe(istate, s.condition.loc);
+ e = interpret(&ue, s.condition, istate);
+ if (exceptionOrCant(e))
+ return;
+ if (!e.isConst())
+ {
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (e.isBool(false))
+ break;
+ assert(isTrueBool(e));
+ }
+ assert(result is null);
+ }
+
+ override void visit(ForStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s ForStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start == s)
+ istate.start = null;
+
+ UnionExp ueinit = void;
+ Expression ei = interpret(&ueinit, s._init, istate);
+ if (exceptionOrCant(ei))
+ return;
+ assert(!ei); // s.init never returns from function, or jumps out from it
+
+ while (1)
+ {
+ if (s.condition && !istate.start)
+ {
+ UnionExp ue = void;
+ incUsageCtfe(istate, s.condition.loc);
+ Expression e = interpret(&ue, s.condition, istate);
+ if (exceptionOrCant(e))
+ return;
+ if (e.isBool(false))
+ break;
+ assert(isTrueBool(e));
+ }
+
+ Expression e = interpret(pue, s._body, istate);
+ if (!e && istate.start) // goto target was not found
+ return;
+ assert(!istate.start);
+
+ if (exceptionOrCant(e))
+ return;
+ if (e && e.op == TOK.break_)
+ {
+ if (istate.gotoTarget && istate.gotoTarget != s)
+ {
+ result = e; // break at a higher level
+ return;
+ }
+ istate.gotoTarget = null;
+ break;
+ }
+ if (e && e.op == TOK.continue_)
+ {
+ if (istate.gotoTarget && istate.gotoTarget != s)
+ {
+ result = e; // continue at a higher level
+ return;
+ }
+ istate.gotoTarget = null;
+ e = null;
+ }
+ if (e)
+ {
+ result = e; // bubbled up from ReturnStatement
+ return;
+ }
+
+ UnionExp uei = void;
+ if (s.increment)
+ incUsageCtfe(istate, s.increment.loc);
+ e = interpret(&uei, s.increment, istate, CTFEGoal.Nothing);
+ if (exceptionOrCant(e))
+ return;
+ }
+ assert(result is null);
+ }
+
+ override void visit(ForeachStatement s)
+ {
+ assert(0); // rewritten to ForStatement
+ }
+
+ override void visit(ForeachRangeStatement s)
+ {
+ assert(0); // rewritten to ForStatement
+ }
+
+ override void visit(SwitchStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s SwitchStatement::interpret()\n", s.loc.toChars());
+ }
+ incUsageCtfe(istate, s.loc);
+ if (istate.start == s)
+ istate.start = null;
+ if (istate.start)
+ {
+ Expression e = interpret(s._body, istate);
+ if (istate.start) // goto target was not found
+ return;
+ if (exceptionOrCant(e))
+ return;
+ if (e && e.op == TOK.break_)
+ {
+ if (istate.gotoTarget && istate.gotoTarget != s)
+ {
+ result = e; // break at a higher level
+ return;
+ }
+ istate.gotoTarget = null;
+ e = null;
+ }
+ result = e;
+ return;
+ }
+
+ UnionExp uecond = void;
+ Expression econdition = interpret(&uecond, s.condition, istate);
+ if (exceptionOrCant(econdition))
+ return;
+
+ Statement scase = null;
+ if (s.cases)
+ foreach (cs; *s.cases)
+ {
+ UnionExp uecase = void;
+ Expression ecase = interpret(&uecase, cs.exp, istate);
+ if (exceptionOrCant(ecase))
+ return;
+ if (ctfeEqual(cs.exp.loc, TOK.equal, econdition, ecase))
+ {
+ scase = cs;
+ break;
+ }
+ }
+ if (!scase)
+ {
+ if (s.hasNoDefault)
+ s.error("no `default` or `case` for `%s` in `switch` statement", econdition.toChars());
+ scase = s.sdefault;
+ }
+
+ assert(scase);
+
+ /* Jump to scase
+ */
+ istate.start = scase;
+ Expression e = interpret(pue, s._body, istate);
+ assert(!istate.start); // jump must not fail
+ if (e && e.op == TOK.break_)
+ {
+ if (istate.gotoTarget && istate.gotoTarget != s)
+ {
+ result = e; // break at a higher level
+ return;
+ }
+ istate.gotoTarget = null;
+ e = null;
+ }
+ result = e;
+ }
+
+ override void visit(CaseStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s CaseStatement::interpret(%s) this = %p\n", s.loc.toChars(), s.exp.toChars(), s);
+ }
+ incUsageCtfe(istate, s.loc);
+ if (istate.start == s)
+ istate.start = null;
+
+ result = interpret(pue, s.statement, istate);
+ }
+
+ override void visit(DefaultStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s DefaultStatement::interpret()\n", s.loc.toChars());
+ }
+ incUsageCtfe(istate, s.loc);
+ if (istate.start == s)
+ istate.start = null;
+
+ result = interpret(pue, s.statement, istate);
+ }
+
+ override void visit(GotoStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s GotoStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+ incUsageCtfe(istate, s.loc);
+
+ assert(s.label && s.label.statement);
+ istate.gotoTarget = s.label.statement;
+ result = CTFEExp.gotoexp;
+ }
+
+ override void visit(GotoCaseStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s GotoCaseStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+ incUsageCtfe(istate, s.loc);
+
+ assert(s.cs);
+ istate.gotoTarget = s.cs;
+ result = CTFEExp.gotoexp;
+ }
+
+ override void visit(GotoDefaultStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s GotoDefaultStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+ incUsageCtfe(istate, s.loc);
+
+ assert(s.sw && s.sw.sdefault);
+ istate.gotoTarget = s.sw.sdefault;
+ result = CTFEExp.gotoexp;
+ }
+
+ override void visit(LabelStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s LabelStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start == s)
+ istate.start = null;
+
+ result = interpret(pue, s.statement, istate);
+ }
+
+ override void visit(TryCatchStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s TryCatchStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start == s)
+ istate.start = null;
+ if (istate.start)
+ {
+ Expression e = null;
+ e = interpret(pue, s._body, istate);
+ foreach (ca; *s.catches)
+ {
+ if (e || !istate.start) // goto target was found
+ break;
+ e = interpret(pue, ca.handler, istate);
+ }
+ result = e;
+ return;
+ }
+
+ Expression e = interpret(s._body, istate);
+
+ // An exception was thrown
+ if (e && e.isThrownExceptionExp())
+ {
+ ThrownExceptionExp ex = e.isThrownExceptionExp();
+ Type extype = ex.thrown.originalClass().type;
+
+ // Search for an appropriate catch clause.
+ foreach (ca; *s.catches)
+ {
+ Type catype = ca.type;
+ if (!catype.equals(extype) && !catype.isBaseOf(extype, null))
+ continue;
+
+ // Execute the handler
+ if (ca.var)
+ {
+ ctfeGlobals.stack.push(ca.var);
+ setValue(ca.var, ex.thrown);
+ }
+ e = interpret(ca.handler, istate);
+ if (CTFEExp.isGotoExp(e))
+ {
+ /* This is an optimization that relies on the locality of the jump target.
+ * If the label is in the same catch handler, the following scan
+ * would find it quickly and can reduce jump cost.
+ * Otherwise, the catch block may be unnnecessary scanned again
+ * so it would make CTFE speed slower.
+ */
+ InterState istatex = *istate;
+ istatex.start = istate.gotoTarget; // set starting statement
+ istatex.gotoTarget = null;
+ Expression eh = interpret(ca.handler, &istatex);
+ if (!istatex.start)
+ {
+ istate.gotoTarget = null;
+ e = eh;
+ }
+ }
+ break;
+ }
+ }
+ result = e;
+ }
+
+ static bool isAnErrorException(ClassDeclaration cd)
+ {
+ return cd == ClassDeclaration.errorException || ClassDeclaration.errorException.isBaseOf(cd, null);
+ }
+
+ static ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest)
+ {
+ debug (LOG)
+ {
+ printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars());
+ }
+ // Little sanity check to make sure it's really a Throwable
+ ClassReferenceExp boss = oldest.thrown;
+ const next = 4; // index of Throwable.next
+ assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next
+ ClassReferenceExp collateral = newest.thrown;
+ if (isAnErrorException(collateral.originalClass()) && !isAnErrorException(boss.originalClass()))
+ {
+ /* Find the index of the Error.bypassException field
+ */
+ auto bypass = next + 1;
+ if ((*collateral.value.elements)[bypass].type.ty == Tuns32)
+ bypass += 1; // skip over _refcount field
+ assert((*collateral.value.elements)[bypass].type.ty == Tclass);
+
+ // The new exception bypass the existing chain
+ (*collateral.value.elements)[bypass] = boss;
+ return newest;
+ }
+ while ((*boss.value.elements)[next].op == TOK.classReference)
+ {
+ boss = cast(ClassReferenceExp)(*boss.value.elements)[next];
+ }
+ (*boss.value.elements)[next] = collateral;
+ return oldest;
+ }
+
+ override void visit(TryFinallyStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s TryFinallyStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start == s)
+ istate.start = null;
+ if (istate.start)
+ {
+ Expression e = null;
+ e = interpret(pue, s._body, istate);
+ // Jump into/out from finalbody is disabled in semantic analysis.
+ // and jump inside will be handled by the ScopeStatement == finalbody.
+ result = e;
+ return;
+ }
+
+ Expression ex = interpret(s._body, istate);
+ if (CTFEExp.isCantExp(ex))
+ {
+ result = ex;
+ return;
+ }
+ while (CTFEExp.isGotoExp(ex))
+ {
+ // If the goto target is within the body, we must not interpret the finally statement,
+ // because that will call destructors for objects within the scope, which we should not do.
+ InterState istatex = *istate;
+ istatex.start = istate.gotoTarget; // set starting statement
+ istatex.gotoTarget = null;
+ Expression bex = interpret(s._body, &istatex);
+ if (istatex.start)
+ {
+ // The goto target is outside the current scope.
+ break;
+ }
+ // The goto target was within the body.
+ if (CTFEExp.isCantExp(bex))
+ {
+ result = bex;
+ return;
+ }
+ *istate = istatex;
+ ex = bex;
+ }
+
+ Expression ey = interpret(s.finalbody, istate);
+ if (CTFEExp.isCantExp(ey))
+ {
+ result = ey;
+ return;
+ }
+ if (ey && ey.isThrownExceptionExp())
+ {
+ // Check for collided exceptions
+ if (ex && ex.isThrownExceptionExp())
+ ex = chainExceptions(ex.isThrownExceptionExp(), ey.isThrownExceptionExp());
+ else
+ ex = ey;
+ }
+ result = ex;
+ }
+
+ override void visit(ThrowStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s ThrowStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+
+ incUsageCtfe(istate, s.loc);
+
+ Expression e = interpretRegion(s.exp, istate);
+ if (exceptionOrCant(e))
+ return;
+
+ assert(e.op == TOK.classReference);
+ result = ctfeEmplaceExp!ThrownExceptionExp(s.loc, e.isClassReferenceExp());
+ }
+
+ override void visit(ScopeGuardStatement s)
+ {
+ assert(0);
+ }
+
+ override void visit(WithStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s WithStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start == s)
+ istate.start = null;
+ if (istate.start)
+ {
+ result = s._body ? interpret(s._body, istate) : null;
+ return;
+ }
+
+ // If it is with(Enum) {...}, just execute the body.
+ if (s.exp.op == TOK.scope_ || s.exp.op == TOK.type)
+ {
+ result = interpret(pue, s._body, istate);
+ return;
+ }
+
+ incUsageCtfe(istate, s.loc);
+
+ Expression e = interpret(s.exp, istate);
+ if (exceptionOrCant(e))
+ return;
+
+ if (s.wthis.type.ty == Tpointer && s.exp.type.ty != Tpointer)
+ {
+ e = ctfeEmplaceExp!AddrExp(s.loc, e, s.wthis.type);
+ }
+ ctfeGlobals.stack.push(s.wthis);
+ setValue(s.wthis, e);
+ e = interpret(s._body, istate);
+ if (CTFEExp.isGotoExp(e))
+ {
+ /* This is an optimization that relies on the locality of the jump target.
+ * If the label is in the same WithStatement, the following scan
+ * would find it quickly and can reduce jump cost.
+ * Otherwise, the statement body may be unnnecessary scanned again
+ * so it would make CTFE speed slower.
+ */
+ InterState istatex = *istate;
+ istatex.start = istate.gotoTarget; // set starting statement
+ istatex.gotoTarget = null;
+ Expression ex = interpret(s._body, &istatex);
+ if (!istatex.start)
+ {
+ istate.gotoTarget = null;
+ e = ex;
+ }
+ }
+ ctfeGlobals.stack.pop(s.wthis);
+ result = e;
+ }
+
+ override void visit(AsmStatement s)
+ {
+ debug (LOG)
+ {
+ printf("%s AsmStatement::interpret()\n", s.loc.toChars());
+ }
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+ s.error("`asm` statements cannot be interpreted at compile time");
+ result = CTFEExp.cantexp;
+ }
+
+ override void visit(ImportStatement s)
+ {
+ debug (LOG)
+ {
+ printf("ImportStatement::interpret()\n");
+ }
+ if (istate.start)
+ {
+ if (istate.start != s)
+ return;
+ istate.start = null;
+ }
+ }
+
+ /******************************** Expression ***************************/
+
+ override void visit(Expression e)
+ {
+ debug (LOG)
+ {
+ printf("%s Expression::interpret() '%s' %s\n", e.loc.toChars(), Token.toChars(e.op), e.toChars());
+ printf("type = %s\n", e.type.toChars());
+ showCtfeExpr(e);
+ }
+ e.error("cannot interpret `%s` at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ }
+
+ override void visit(TypeExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s TypeExp.interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ result = e;
+ }
+
+ override void visit(ThisExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s ThisExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (goal == CTFEGoal.LValue)
+ {
+ // We might end up here with istate being zero
+ // https://issues.dlang.org/show_bug.cgi?id=16382
+ if (istate && istate.fd.vthis)
+ {
+ result = ctfeEmplaceExp!VarExp(e.loc, istate.fd.vthis);
+ if (istate.fd.isThis2)
+ {
+ result = ctfeEmplaceExp!PtrExp(e.loc, result);
+ result.type = Type.tvoidptr.sarrayOf(2);
+ result = ctfeEmplaceExp!IndexExp(e.loc, result, IntegerExp.literal!0);
+ }
+ result.type = e.type;
+ }
+ else
+ result = e;
+ return;
+ }
+
+ result = ctfeGlobals.stack.getThis();
+ if (result)
+ {
+ if (istate && istate.fd.isThis2)
+ {
+ assert(result.op == TOK.address);
+ result = (cast(AddrExp)result).e1;
+ assert(result.op == TOK.arrayLiteral);
+ result = (*(cast(ArrayLiteralExp)result).elements)[0];
+ if (e.type.ty == Tstruct)
+ {
+ result = (cast(AddrExp)result).e1;
+ }
+ return;
+ }
+ assert(result.op == TOK.structLiteral || result.op == TOK.classReference || result.op == TOK.type);
+ return;
+ }
+ e.error("value of `this` is not known at compile time");
+ result = CTFEExp.cantexp;
+ }
+
+ override void visit(NullExp e)
+ {
+ result = e;
+ }
+
+ override void visit(IntegerExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s IntegerExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ result = e;
+ }
+
+ override void visit(RealExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s RealExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ result = e;
+ }
+
+ override void visit(ComplexExp e)
+ {
+ result = e;
+ }
+
+ override void visit(StringExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s StringExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ /* Attempts to modify string literals are prevented
+ * in BinExp::interpretAssignCommon.
+ */
+ result = e;
+ }
+
+ override void visit(FuncExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s FuncExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ result = e;
+ }
+
+ override void visit(SymOffExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s SymOffExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (e.var.isFuncDeclaration() && e.offset == 0)
+ {
+ result = e;
+ return;
+ }
+ if (isTypeInfo_Class(e.type) && e.offset == 0)
+ {
+ result = e;
+ return;
+ }
+ if (e.type.ty != Tpointer)
+ {
+ // Probably impossible
+ e.error("cannot interpret `%s` at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ Type pointee = (cast(TypePointer)e.type).next;
+ if (e.var.isThreadlocal())
+ {
+ e.error("cannot take address of thread-local variable %s at compile time", e.var.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ // Check for taking an address of a shared variable.
+ // If the shared variable is an array, the offset might not be zero.
+ Type fromType = null;
+ if (e.var.type.ty == Tarray || e.var.type.ty == Tsarray)
+ {
+ fromType = (cast(TypeArray)e.var.type).next;
+ }
+ if (e.var.isDataseg() && ((e.offset == 0 && isSafePointerCast(e.var.type, pointee)) || (fromType && isSafePointerCast(fromType, pointee))))
+ {
+ result = e;
+ return;
+ }
+
+ Expression val = getVarExp(e.loc, istate, e.var, goal);
+ if (exceptionOrCant(val))
+ return;
+ if (val.type.ty == Tarray || val.type.ty == Tsarray)
+ {
+ // Check for unsupported type painting operations
+ Type elemtype = (cast(TypeArray)val.type).next;
+ d_uns64 elemsize = elemtype.size();
+
+ // It's OK to cast from fixed length to fixed length array, eg &int[n] to int[d]*.
+ if (val.type.ty == Tsarray && pointee.ty == Tsarray && elemsize == pointee.nextOf().size())
+ {
+ size_t d = cast(size_t)(cast(TypeSArray)pointee).dim.toInteger();
+ Expression elwr = ctfeEmplaceExp!IntegerExp(e.loc, e.offset / elemsize, Type.tsize_t);
+ Expression eupr = ctfeEmplaceExp!IntegerExp(e.loc, e.offset / elemsize + d, Type.tsize_t);
+
+ // Create a CTFE pointer &val[ofs..ofs+d]
+ auto se = ctfeEmplaceExp!SliceExp(e.loc, val, elwr, eupr);
+ se.type = pointee;
+ emplaceExp!(AddrExp)(pue, e.loc, se, e.type);
+ result = pue.exp();
+ return;
+ }
+
+ if (!isSafePointerCast(elemtype, pointee))
+ {
+ // It's also OK to cast from &string to string*.
+ if (e.offset == 0 && isSafePointerCast(e.var.type, pointee))
+ {
+ // Create a CTFE pointer &var
+ auto ve = ctfeEmplaceExp!VarExp(e.loc, e.var);
+ ve.type = elemtype;
+ emplaceExp!(AddrExp)(pue, e.loc, ve, e.type);
+ result = pue.exp();
+ return;
+ }
+ e.error("reinterpreting cast from `%s` to `%s` is not supported in CTFE", val.type.toChars(), e.type.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ const dinteger_t sz = pointee.size();
+ dinteger_t indx = e.offset / sz;
+ assert(sz * indx == e.offset);
+ Expression aggregate = null;
+ if (val.op == TOK.arrayLiteral || val.op == TOK.string_)
+ {
+ aggregate = val;
+ }
+ else if (auto se = val.isSliceExp())
+ {
+ aggregate = se.e1;
+ UnionExp uelwr = void;
+ Expression lwr = interpret(&uelwr, se.lwr, istate);
+ indx += lwr.toInteger();
+ }
+ if (aggregate)
+ {
+ // Create a CTFE pointer &aggregate[ofs]
+ auto ofs = ctfeEmplaceExp!IntegerExp(e.loc, indx, Type.tsize_t);
+ auto ei = ctfeEmplaceExp!IndexExp(e.loc, aggregate, ofs);
+ ei.type = elemtype;
+ emplaceExp!(AddrExp)(pue, e.loc, ei, e.type);
+ result = pue.exp();
+ return;
+ }
+ }
+ else if (e.offset == 0 && isSafePointerCast(e.var.type, pointee))
+ {
+ // Create a CTFE pointer &var
+ auto ve = ctfeEmplaceExp!VarExp(e.loc, e.var);
+ ve.type = e.var.type;
+ emplaceExp!(AddrExp)(pue, e.loc, ve, e.type);
+ result = pue.exp();
+ return;
+ }
+
+ e.error("cannot convert `&%s` to `%s` at compile time", e.var.type.toChars(), e.type.toChars());
+ result = CTFEExp.cantexp;
+ }
+
+ override void visit(AddrExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s AddrExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (auto ve = e.e1.isVarExp())
+ {
+ Declaration decl = ve.var;
+
+ // We cannot take the address of an imported symbol at compile time
+ if (decl.isImportedSymbol()) {
+ e.error("cannot take address of imported symbol `%s` at compile time", decl.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ if (decl.isDataseg()) {
+ // Normally this is already done by optimize()
+ // Do it here in case optimize(WANTvalue) wasn't run before CTFE
+ emplaceExp!(SymOffExp)(pue, e.loc, (cast(VarExp)e.e1).var, 0);
+ result = pue.exp();
+ result.type = e.type;
+ return;
+ }
+ }
+ auto er = interpret(e.e1, istate, CTFEGoal.LValue);
+ if (auto ve = er.isVarExp())
+ if (ve.var == istate.fd.vthis)
+ er = interpret(er, istate);
+
+ if (exceptionOrCant(er))
+ return;
+
+ // Return a simplified address expression
+ emplaceExp!(AddrExp)(pue, e.loc, er, e.type);
+ result = pue.exp();
+ }
+
+ override void visit(DelegateExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s DelegateExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ // TODO: Really we should create a CTFE-only delegate expression
+ // of a pointer and a funcptr.
+
+ // If it is &nestedfunc, just return it
+ // TODO: We should save the context pointer
+ if (auto ve1 = e.e1.isVarExp())
+ if (ve1.var == e.func)
+ {
+ result = e;
+ return;
+ }
+
+ auto er = interpret(pue, e.e1, istate);
+ if (exceptionOrCant(er))
+ return;
+ if (er == e.e1)
+ {
+ // If it has already been CTFE'd, just return it
+ result = e;
+ }
+ else
+ {
+ er = (er == pue.exp()) ? pue.copy() : er;
+ emplaceExp!(DelegateExp)(pue, e.loc, er, e.func, false);
+ result = pue.exp();
+ result.type = e.type;
+ }
+ }
+
+ static Expression getVarExp(const ref Loc loc, InterState* istate, Declaration d, CTFEGoal goal)
+ {
+ Expression e = CTFEExp.cantexp;
+ if (VarDeclaration v = d.isVarDeclaration())
+ {
+ /* Magic variable __ctfe always returns true when interpreting
+ */
+ if (v.ident == Id.ctfe)
+ return IntegerExp.createBool(true);
+
+ if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run
+ {
+ v.dsymbolSemantic(null);
+ if (v.type.ty == Terror)
+ return CTFEExp.cantexp;
+ }
+
+ if ((v.isConst() || v.isImmutable() || v.storage_class & STC.manifest) && !hasValue(v) && v._init && !v.isCTFE())
+ {
+ if (v.inuse)
+ {
+ error(loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
+ return CTFEExp.cantexp;
+ }
+ if (v._scope)
+ {
+ v.inuse++;
+ v._init = v._init.initializerSemantic(v._scope, v.type, INITinterpret); // might not be run on aggregate members
+ v.inuse--;
+ }
+ e = v._init.initializerToExpression(v.type);
+ if (!e)
+ return CTFEExp.cantexp;
+ assert(e.type);
+
+ if (e.op == TOK.construct || e.op == TOK.blit)
+ {
+ AssignExp ae = cast(AssignExp)e;
+ e = ae.e2;
+ }
+
+ if (e.op == TOK.error)
+ {
+ // FIXME: Ultimately all errors should be detected in prior semantic analysis stage.
+ }
+ else if (v.isDataseg() || (v.storage_class & STC.manifest))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=14304
+ * e is a value that is not yet owned by CTFE.
+ * Mark as "cached", and use it directly during interpretation.
+ */
+ e = scrubCacheValue(e);
+ ctfeGlobals.stack.saveGlobalConstant(v, e);
+ }
+ else
+ {
+ v.inuse++;
+ e = interpret(e, istate);
+ v.inuse--;
+ if (CTFEExp.isCantExp(e) && !global.gag && !ctfeGlobals.stackTraceCallsToSuppress)
+ errorSupplemental(loc, "while evaluating %s.init", v.toChars());
+ if (exceptionOrCantInterpret(e))
+ return e;
+ }
+ }
+ else if (v.isCTFE() && !hasValue(v))
+ {
+ if (v._init && v.type.size() != 0)
+ {
+ if (v._init.isVoidInitializer())
+ {
+ // var should have been initialized when it was created
+ error(loc, "CTFE internal error: trying to access uninitialized var");
+ assert(0);
+ }
+ e = v._init.initializerToExpression();
+ }
+ else
+ // Zero-length arrays don't have an initializer
+ e = v.type.defaultInitLiteral(e.loc);
+
+ e = interpret(e, istate);
+ }
+ else if (!(v.isDataseg() || v.storage_class & STC.manifest) && !v.isCTFE() && !istate)
+ {
+ error(loc, "variable `%s` cannot be read at compile time", v.toChars());
+ return CTFEExp.cantexp;
+ }
+ else
+ {
+ e = hasValue(v) ? getValue(v) : null;
+ if (!e)
+ {
+ // Zero-length arrays don't have an initializer
+ if (v.type.size() == 0)
+ e = v.type.defaultInitLiteral(loc);
+ else if (!v.isCTFE() && v.isDataseg())
+ {
+ error(loc, "static variable `%s` cannot be read at compile time", v.toChars());
+ return CTFEExp.cantexp;
+ }
+ else
+ {
+ assert(!(v._init && v._init.isVoidInitializer()));
+ // CTFE initiated from inside a function
+ error(loc, "variable `%s` cannot be read at compile time", v.toChars());
+ return CTFEExp.cantexp;
+ }
+ }
+ if (auto vie = e.isVoidInitExp())
+ {
+ error(loc, "cannot read uninitialized variable `%s` in ctfe", v.toPrettyChars());
+ errorSupplemental(vie.var.loc, "`%s` was uninitialized and used before set", vie.var.toChars());
+ return CTFEExp.cantexp;
+ }
+ if (goal != CTFEGoal.LValue && v.isReference())
+ e = interpret(e, istate, goal);
+ }
+ if (!e)
+ e = CTFEExp.cantexp;
+ }
+ else if (SymbolDeclaration s = d.isSymbolDeclaration())
+ {
+ // Struct static initializers, for example
+ e = s.dsym.type.defaultInitLiteral(loc);
+ if (e.op == TOK.error)
+ error(loc, "CTFE failed because of previous errors in `%s.init`", s.toChars());
+ e = e.expressionSemantic(null);
+ if (e.op == TOK.error)
+ e = CTFEExp.cantexp;
+ else // Convert NULL to CTFEExp
+ e = interpret(e, istate, goal);
+ }
+ else
+ error(loc, "cannot interpret declaration `%s` at compile time", d.toChars());
+ return e;
+ }
+
+ override void visit(VarExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s VarExp::interpret() `%s`, goal = %d\n", e.loc.toChars(), e.toChars(), goal);
+ }
+ if (e.var.isFuncDeclaration())
+ {
+ result = e;
+ return;
+ }
+
+ // Note: This is a workaround for
+ // https://issues.dlang.org/show_bug.cgi?id=17351
+ // The aforementioned bug triggers when passing manifest constant by `ref`.
+ // If there was not a previous reference to them, they are
+ // not cached and trigger a "cannot be read at compile time".
+ // This fix is a crude solution to get it to work. A more proper
+ // approach would be to resolve the forward reference, but that is
+ // much more involved.
+ if (goal == CTFEGoal.LValue && e.var.type.isMutable())
+ {
+ if (auto v = e.var.isVarDeclaration())
+ {
+ if (!v.isDataseg() && !v.isCTFE() && !istate)
+ {
+ e.error("variable `%s` cannot be read at compile time", v.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (!hasValue(v))
+ {
+ if (!v.isCTFE() && v.isDataseg())
+ e.error("static variable `%s` cannot be read at compile time", v.toChars());
+ else // CTFE initiated from inside a function
+ e.error("variable `%s` cannot be read at compile time", v.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ if (v.storage_class & (STC.out_ | STC.ref_))
+ {
+ // Strip off the nest of ref variables
+ Expression ev = getValue(v);
+ if (ev.op == TOK.variable ||
+ ev.op == TOK.index ||
+ ev.op == TOK.slice ||
+ ev.op == TOK.dotVariable)
+ {
+ result = interpret(pue, ev, istate, goal);
+ return;
+ }
+ }
+ }
+ result = e;
+ return;
+ }
+ result = getVarExp(e.loc, istate, e.var, goal);
+ if (exceptionOrCant(result))
+ return;
+
+ // Visit the default initializer for noreturn variables
+ // (Custom initializers would abort the current function call and exit above)
+ if (result.type.ty == Tnoreturn)
+ {
+ result.accept(this);
+ return;
+ }
+
+ if ((e.var.storage_class & (STC.ref_ | STC.out_)) == 0 && e.type.baseElemOf().ty != Tstruct)
+ {
+ /* Ultimately, STC.ref_|STC.out_ check should be enough to see the
+ * necessity of type repainting. But currently front-end paints
+ * non-ref struct variables by the const type.
+ *
+ * auto foo(ref const S cs);
+ * S s;
+ * foo(s); // VarExp('s') will have const(S)
+ */
+ // A VarExp may include an implicit cast. It must be done explicitly.
+ result = paintTypeOntoLiteral(pue, e.type, result);
+ }
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s DeclarationExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ Dsymbol s = e.declaration;
+ while (s.isAttribDeclaration())
+ {
+ auto ad = cast(AttribDeclaration)s;
+ assert(ad.decl && ad.decl.dim == 1); // Currently, only one allowed when parsing
+ s = (*ad.decl)[0];
+ }
+ if (VarDeclaration v = s.isVarDeclaration())
+ {
+ if (TupleDeclaration td = v.toAlias().isTupleDeclaration())
+ {
+ result = null;
+
+ // Reserve stack space for all tuple members
+ if (!td.objects)
+ return;
+ foreach (o; *td.objects)
+ {
+ Expression ex = isExpression(o);
+ DsymbolExp ds = ex ? ex.isDsymbolExp() : null;
+ VarDeclaration v2 = ds ? ds.s.isVarDeclaration() : null;
+ assert(v2);
+ if (v2.isDataseg() && !v2.isCTFE())
+ continue;
+
+ ctfeGlobals.stack.push(v2);
+ if (v2._init)
+ {
+ Expression einit;
+ if (ExpInitializer ie = v2._init.isExpInitializer())
+ {
+ einit = interpretRegion(ie.exp, istate, goal);
+ if (exceptionOrCant(einit))
+ return;
+ }
+ else if (v2._init.isVoidInitializer())
+ {
+ einit = voidInitLiteral(v2.type, v2).copy();
+ }
+ else
+ {
+ e.error("declaration `%s` is not yet implemented in CTFE", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ setValue(v2, einit);
+ }
+ }
+ return;
+ }
+ if (v.isStatic())
+ {
+ // Just ignore static variables which aren't read or written yet
+ result = null;
+ return;
+ }
+ if (!(v.isDataseg() || v.storage_class & STC.manifest) || v.isCTFE())
+ ctfeGlobals.stack.push(v);
+ if (v._init)
+ {
+ if (ExpInitializer ie = v._init.isExpInitializer())
+ {
+ result = interpretRegion(ie.exp, istate, goal);
+ }
+ else if (v._init.isVoidInitializer())
+ {
+ result = voidInitLiteral(v.type, v).copy();
+ // There is no AssignExp for void initializers,
+ // so set it here.
+ setValue(v, result);
+ }
+ else
+ {
+ e.error("declaration `%s` is not yet implemented in CTFE", e.toChars());
+ result = CTFEExp.cantexp;
+ }
+ }
+ else if (v.type.size() == 0)
+ {
+ // Zero-length arrays don't need an initializer
+ result = v.type.defaultInitLiteral(e.loc);
+ }
+ else
+ {
+ e.error("variable `%s` cannot be modified at compile time", v.toChars());
+ result = CTFEExp.cantexp;
+ }
+ return;
+ }
+ if (s.isTemplateMixin() || s.isTupleDeclaration())
+ {
+ // These can be made to work, too lazy now
+ e.error("declaration `%s` is not yet implemented in CTFE", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ // Others should not contain executable code, so are trivial to evaluate
+ result = null;
+ debug (LOG)
+ {
+ printf("-DeclarationExp::interpret(%s): %p\n", e.toChars(), result);
+ }
+ }
+
+ override void visit(TypeidExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s TypeidExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (Type t = isType(e.obj))
+ {
+ result = e;
+ return;
+ }
+ if (Expression ex = isExpression(e.obj))
+ {
+ result = interpret(pue, ex, istate);
+ if (exceptionOrCant(ex))
+ return;
+
+ if (result.op == TOK.null_)
+ {
+ e.error("null pointer dereference evaluating typeid. `%s` is `null`", ex.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (result.op != TOK.classReference)
+ {
+ e.error("CTFE internal error: determining classinfo");
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ ClassDeclaration cd = (cast(ClassReferenceExp)result).originalClass();
+ assert(cd);
+
+ emplaceExp!(TypeidExp)(pue, e.loc, cd.type);
+ result = pue.exp();
+ result.type = e.type;
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(TupleExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s TupleExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (exceptionOrCant(interpretRegion(e.e0, istate, CTFEGoal.Nothing)))
+ return;
+
+ auto expsx = e.exps;
+ foreach (i, exp; *expsx)
+ {
+ Expression ex = interpretRegion(exp, istate);
+ if (exceptionOrCant(ex))
+ return;
+
+ // A tuple of assignments can contain void (Bug 5676).
+ if (goal == CTFEGoal.Nothing)
+ continue;
+ if (ex.op == TOK.voidExpression)
+ {
+ e.error("CTFE internal error: void element `%s` in tuple", exp.toChars());
+ assert(0);
+ }
+
+ /* If any changes, do Copy On Write
+ */
+ if (ex !is exp)
+ {
+ expsx = copyArrayOnWrite(expsx, e.exps);
+ (*expsx)[i] = copyRegionExp(ex);
+ }
+ }
+
+ if (expsx !is e.exps)
+ {
+ expandTuples(expsx);
+ emplaceExp!(TupleExp)(pue, e.loc, expsx);
+ result = pue.exp();
+ result.type = new TypeTuple(expsx);
+ }
+ else
+ result = e;
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s ArrayLiteralExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements
+ {
+ result = e;
+ return;
+ }
+
+ Type tn = e.type.toBasetype().nextOf().toBasetype();
+ bool wantCopy = (tn.ty == Tsarray || tn.ty == Tstruct);
+
+ auto basis = interpretRegion(e.basis, istate);
+ if (exceptionOrCant(basis))
+ return;
+
+ auto expsx = e.elements;
+ size_t dim = expsx ? expsx.dim : 0;
+ for (size_t i = 0; i < dim; i++)
+ {
+ Expression exp = (*expsx)[i];
+ Expression ex;
+ if (!exp)
+ {
+ ex = copyLiteral(basis).copy();
+ }
+ else
+ {
+ // segfault bug 6250
+ assert(exp.op != TOK.index || (cast(IndexExp)exp).e1 != e);
+
+ ex = interpretRegion(exp, istate);
+ if (exceptionOrCant(ex))
+ return;
+
+ /* Each elements should have distinct CTFE memory.
+ * int[1] z = 7;
+ * int[1][] pieces = [z,z]; // here
+ */
+ if (wantCopy)
+ ex = copyLiteral(ex).copy();
+ }
+
+ /* If any changes, do Copy On Write
+ */
+ if (ex !is exp)
+ {
+ expsx = copyArrayOnWrite(expsx, e.elements);
+ (*expsx)[i] = ex;
+ }
+ }
+
+ if (expsx !is e.elements)
+ {
+ // todo: all tuple expansions should go in semantic phase.
+ expandTuples(expsx);
+ if (expsx.dim != dim)
+ {
+ e.error("CTFE internal error: invalid array literal");
+ result = CTFEExp.cantexp;
+ return;
+ }
+ emplaceExp!(ArrayLiteralExp)(pue, e.loc, e.type, basis, expsx);
+ auto ale = cast(ArrayLiteralExp)pue.exp();
+ ale.ownedByCtfe = OwnedBy.ctfe;
+ result = ale;
+ }
+ else if ((cast(TypeNext)e.type).next.mod & (MODFlags.const_ | MODFlags.immutable_))
+ {
+ // If it's immutable, we don't need to dup it
+ result = e;
+ }
+ else
+ {
+ *pue = copyLiteral(e);
+ result = pue.exp();
+ }
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s AssocArrayLiteralExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements
+ {
+ result = e;
+ return;
+ }
+
+ auto keysx = e.keys;
+ auto valuesx = e.values;
+ foreach (i, ekey; *keysx)
+ {
+ auto evalue = (*valuesx)[i];
+
+ auto ek = interpretRegion(ekey, istate);
+ if (exceptionOrCant(ek))
+ return;
+ auto ev = interpretRegion(evalue, istate);
+ if (exceptionOrCant(ev))
+ return;
+
+ /* If any changes, do Copy On Write
+ */
+ if (ek !is ekey ||
+ ev !is evalue)
+ {
+ keysx = copyArrayOnWrite(keysx, e.keys);
+ valuesx = copyArrayOnWrite(valuesx, e.values);
+ (*keysx)[i] = ek;
+ (*valuesx)[i] = ev;
+ }
+ }
+ if (keysx !is e.keys)
+ expandTuples(keysx);
+ if (valuesx !is e.values)
+ expandTuples(valuesx);
+ if (keysx.dim != valuesx.dim)
+ {
+ e.error("CTFE internal error: invalid AA");
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ /* Remove duplicate keys
+ */
+ for (size_t i = 1; i < keysx.dim; i++)
+ {
+ auto ekey = (*keysx)[i - 1];
+ for (size_t j = i; j < keysx.dim; j++)
+ {
+ auto ekey2 = (*keysx)[j];
+ if (!ctfeEqual(e.loc, TOK.equal, ekey, ekey2))
+ continue;
+
+ // Remove ekey
+ keysx = copyArrayOnWrite(keysx, e.keys);
+ valuesx = copyArrayOnWrite(valuesx, e.values);
+ keysx.remove(i - 1);
+ valuesx.remove(i - 1);
+
+ i -= 1; // redo the i'th iteration
+ break;
+ }
+ }
+
+ if (keysx !is e.keys ||
+ valuesx !is e.values)
+ {
+ assert(keysx !is e.keys &&
+ valuesx !is e.values);
+ auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
+ aae.type = e.type;
+ aae.ownedByCtfe = OwnedBy.ctfe;
+ result = aae;
+ }
+ else
+ {
+ *pue = copyLiteral(e);
+ result = pue.exp();
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s StructLiteralExp::interpret() %s ownedByCtfe = %d\n", e.loc.toChars(), e.toChars(), e.ownedByCtfe);
+ }
+ if (e.ownedByCtfe >= OwnedBy.ctfe)
+ {
+ result = e;
+ return;
+ }
+
+ size_t dim = e.elements ? e.elements.dim : 0;
+ auto expsx = e.elements;
+
+ if (dim != e.sd.fields.dim)
+ {
+ // guaranteed by AggregateDeclaration.fill and TypeStruct.defaultInitLiteral
+ const nvthis = e.sd.fields.dim - e.sd.nonHiddenFields();
+ assert(e.sd.fields.dim - dim == nvthis);
+
+ /* If a nested struct has no initialized hidden pointer,
+ * set it to null to match the runtime behaviour.
+ */
+ foreach (const i; 0 .. nvthis)
+ {
+ auto ne = ctfeEmplaceExp!NullExp(e.loc);
+ auto vthis = i == 0 ? e.sd.vthis : e.sd.vthis2;
+ ne.type = vthis.type;
+
+ expsx = copyArrayOnWrite(expsx, e.elements);
+ expsx.push(ne);
+ ++dim;
+ }
+ }
+ assert(dim == e.sd.fields.dim);
+
+ foreach (i; 0 .. dim)
+ {
+ auto v = e.sd.fields[i];
+ Expression exp = (*expsx)[i];
+ Expression ex;
+ if (!exp)
+ {
+ ex = voidInitLiteral(v.type, v).copy();
+ }
+ else
+ {
+ ex = interpretRegion(exp, istate);
+ if (exceptionOrCant(ex))
+ return;
+ if ((v.type.ty != ex.type.ty) && v.type.ty == Tsarray)
+ {
+ // Block assignment from inside struct literals
+ auto tsa = cast(TypeSArray)v.type;
+ auto len = cast(size_t)tsa.dim.toInteger();
+ UnionExp ue = void;
+ ex = createBlockDuplicatedArrayLiteral(&ue, ex.loc, v.type, ex, len);
+ if (ex == ue.exp())
+ ex = ue.copy();
+ }
+ }
+
+ /* If any changes, do Copy On Write
+ */
+ if (ex !is exp)
+ {
+ expsx = copyArrayOnWrite(expsx, e.elements);
+ (*expsx)[i] = ex;
+ }
+ }
+
+ if (expsx !is e.elements)
+ {
+ expandTuples(expsx);
+ if (expsx.dim != e.sd.fields.dim)
+ {
+ e.error("CTFE internal error: invalid struct literal");
+ result = CTFEExp.cantexp;
+ return;
+ }
+ emplaceExp!(StructLiteralExp)(pue, e.loc, e.sd, expsx);
+ auto sle = cast(StructLiteralExp)pue.exp();
+ sle.type = e.type;
+ sle.ownedByCtfe = OwnedBy.ctfe;
+ sle.origin = e.origin;
+ result = sle;
+ }
+ else
+ {
+ *pue = copyLiteral(e);
+ result = pue.exp();
+ }
+ }
+
+ // Create an array literal of type 'newtype' with dimensions given by
+ // 'arguments'[argnum..$]
+ static Expression recursivelyCreateArrayLiteral(UnionExp* pue, const ref Loc loc, Type newtype, InterState* istate, Expressions* arguments, int argnum)
+ {
+ Expression lenExpr = interpret(pue, (*arguments)[argnum], istate);
+ if (exceptionOrCantInterpret(lenExpr))
+ return lenExpr;
+ size_t len = cast(size_t)lenExpr.toInteger();
+ Type elemType = (cast(TypeArray)newtype).next;
+ if (elemType.ty == Tarray && argnum < arguments.dim - 1)
+ {
+ Expression elem = recursivelyCreateArrayLiteral(pue, loc, elemType, istate, arguments, argnum + 1);
+ if (exceptionOrCantInterpret(elem))
+ return elem;
+
+ auto elements = new Expressions(len);
+ foreach (ref element; *elements)
+ element = copyLiteral(elem).copy();
+ emplaceExp!(ArrayLiteralExp)(pue, loc, newtype, elements);
+ auto ae = cast(ArrayLiteralExp)pue.exp();
+ ae.ownedByCtfe = OwnedBy.ctfe;
+ return ae;
+ }
+ assert(argnum == arguments.dim - 1);
+ if (elemType.ty.isSomeChar)
+ {
+ const ch = cast(dchar)elemType.defaultInitLiteral(loc).toInteger();
+ const sz = cast(ubyte)elemType.size();
+ return createBlockDuplicatedStringLiteral(pue, loc, newtype, ch, len, sz);
+ }
+ else
+ {
+ auto el = interpret(elemType.defaultInitLiteral(loc), istate);
+ return createBlockDuplicatedArrayLiteral(pue, loc, newtype, el, len);
+ }
+ }
+
+ override void visit(NewExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s NewExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+
+ Expression epre = interpret(pue, e.argprefix, istate, CTFEGoal.Nothing);
+ if (exceptionOrCant(epre))
+ return;
+
+ if (e.newtype.ty == Tarray && e.arguments)
+ {
+ result = recursivelyCreateArrayLiteral(pue, e.loc, e.newtype, istate, e.arguments, 0);
+ return;
+ }
+ if (auto ts = e.newtype.toBasetype().isTypeStruct())
+ {
+ if (e.member)
+ {
+ Expression se = e.newtype.defaultInitLiteral(e.loc);
+ se = interpret(se, istate);
+ if (exceptionOrCant(se))
+ return;
+ result = interpretFunction(pue, e.member, istate, e.arguments, se);
+
+ // Repaint as same as CallExp::interpret() does.
+ result.loc = e.loc;
+ }
+ else
+ {
+ StructDeclaration sd = ts.sym;
+ auto exps = new Expressions();
+ exps.reserve(sd.fields.dim);
+ if (e.arguments)
+ {
+ exps.setDim(e.arguments.dim);
+ foreach (i, ex; *e.arguments)
+ {
+ ex = interpretRegion(ex, istate);
+ if (exceptionOrCant(ex))
+ return;
+ (*exps)[i] = ex;
+ }
+ }
+ sd.fill(e.loc, exps, false);
+
+ auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, sd, exps, e.newtype);
+ se.origin = se;
+ se.type = e.newtype;
+ se.ownedByCtfe = OwnedBy.ctfe;
+ result = interpret(pue, se, istate);
+ }
+ if (exceptionOrCant(result))
+ return;
+ Expression ev = (result == pue.exp()) ? pue.copy() : result;
+ emplaceExp!(AddrExp)(pue, e.loc, ev, e.type);
+ result = pue.exp();
+ return;
+ }
+ if (auto tc = e.newtype.toBasetype().isTypeClass())
+ {
+ ClassDeclaration cd = tc.sym;
+ size_t totalFieldCount = 0;
+ for (ClassDeclaration c = cd; c; c = c.baseClass)
+ totalFieldCount += c.fields.dim;
+ auto elems = new Expressions(totalFieldCount);
+ size_t fieldsSoFar = totalFieldCount;
+ for (ClassDeclaration c = cd; c; c = c.baseClass)
+ {
+ fieldsSoFar -= c.fields.dim;
+ foreach (i, v; c.fields)
+ {
+ if (v.inuse)
+ {
+ e.error("circular reference to `%s`", v.toPrettyChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ Expression m;
+ if (v._init)
+ {
+ if (v._init.isVoidInitializer())
+ m = voidInitLiteral(v.type, v).copy();
+ else
+ m = v.getConstInitializer(true);
+ }
+ else
+ m = v.type.defaultInitLiteral(e.loc);
+ if (exceptionOrCant(m))
+ return;
+ (*elems)[fieldsSoFar + i] = copyLiteral(m).copy();
+ }
+ }
+ // Hack: we store a ClassDeclaration instead of a StructDeclaration.
+ // We probably won't get away with this.
+// auto se = new StructLiteralExp(e.loc, cast(StructDeclaration)cd, elems, e.newtype);
+ auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, cast(StructDeclaration)cd, elems, e.newtype);
+ se.origin = se;
+ se.ownedByCtfe = OwnedBy.ctfe;
+ emplaceExp!(ClassReferenceExp)(pue, e.loc, se, e.type);
+ Expression eref = pue.exp();
+ if (e.member)
+ {
+ // Call constructor
+ if (!e.member.fbody)
+ {
+ Expression ctorfail = evaluateIfBuiltin(pue, istate, e.loc, e.member, e.arguments, eref);
+ if (ctorfail)
+ {
+ if (exceptionOrCant(ctorfail))
+ return;
+ result = eref;
+ return;
+ }
+ e.member.error("`%s` cannot be constructed at compile time, because the constructor has no available source code", e.newtype.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ UnionExp ue = void;
+ Expression ctorfail = interpretFunction(&ue, e.member, istate, e.arguments, eref);
+ if (exceptionOrCant(ctorfail))
+ return;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=14465
+ * Repaint the loc, because a super() call
+ * in the constructor modifies the loc of ClassReferenceExp
+ * in CallExp::interpret().
+ */
+ eref.loc = e.loc;
+ }
+ result = eref;
+ return;
+ }
+ if (e.newtype.toBasetype().isscalar())
+ {
+ Expression newval;
+ if (e.arguments && e.arguments.dim)
+ newval = (*e.arguments)[0];
+ else
+ newval = e.newtype.defaultInitLiteral(e.loc);
+ newval = interpretRegion(newval, istate);
+ if (exceptionOrCant(newval))
+ return;
+
+ // Create a CTFE pointer &[newval][0]
+ auto elements = new Expressions(1);
+ (*elements)[0] = newval;
+ auto ae = ctfeEmplaceExp!ArrayLiteralExp(e.loc, e.newtype.arrayOf(), elements);
+ ae.ownedByCtfe = OwnedBy.ctfe;
+
+ auto ei = ctfeEmplaceExp!IndexExp(e.loc, ae, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t));
+ ei.type = e.newtype;
+ emplaceExp!(AddrExp)(pue, e.loc, ei, e.type);
+ result = pue.exp();
+ return;
+ }
+ e.error("cannot interpret `%s` at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ }
+
+ override void visit(UnaExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s UnaExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ UnionExp ue = void;
+ Expression e1 = interpret(&ue, e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ switch (e.op)
+ {
+ case TOK.negate:
+ *pue = Neg(e.type, e1);
+ break;
+
+ case TOK.tilde:
+ *pue = Com(e.type, e1);
+ break;
+
+ case TOK.not:
+ *pue = Not(e.type, e1);
+ break;
+
+ default:
+ assert(0);
+ }
+ result = (*pue).exp();
+ }
+
+ override void visit(DotTypeExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s DotTypeExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ UnionExp ue = void;
+ Expression e1 = interpret(&ue, e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ if (e1 == e.e1)
+ result = e; // optimize: reuse this CTFE reference
+ else
+ {
+ auto edt = cast(DotTypeExp)e.copy();
+ edt.e1 = (e1 == ue.exp()) ? e1.copy() : e1; // don't return pointer to ue
+ result = edt;
+ }
+ }
+
+ extern (D) private void interpretCommon(BinExp e, fp_t fp)
+ {
+ debug (LOG)
+ {
+ printf("%s BinExp::interpretCommon() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer && e.op == TOK.min)
+ {
+ UnionExp ue1 = void;
+ Expression e1 = interpret(&ue1, e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ UnionExp ue2 = void;
+ Expression e2 = interpret(&ue2, e.e2, istate);
+ if (exceptionOrCant(e2))
+ return;
+ *pue = pointerDifference(e.loc, e.type, e1, e2);
+ result = (*pue).exp();
+ return;
+ }
+ if (e.e1.type.ty == Tpointer && e.e2.type.isintegral())
+ {
+ UnionExp ue1 = void;
+ Expression e1 = interpret(&ue1, e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ UnionExp ue2 = void;
+ Expression e2 = interpret(&ue2, e.e2, istate);
+ if (exceptionOrCant(e2))
+ return;
+ *pue = pointerArithmetic(e.loc, e.op, e.type, e1, e2);
+ result = (*pue).exp();
+ return;
+ }
+ if (e.e2.type.ty == Tpointer && e.e1.type.isintegral() && e.op == TOK.add)
+ {
+ UnionExp ue1 = void;
+ Expression e1 = interpret(&ue1, e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ UnionExp ue2 = void;
+ Expression e2 = interpret(&ue2, e.e2, istate);
+ if (exceptionOrCant(e2))
+ return;
+ *pue = pointerArithmetic(e.loc, e.op, e.type, e2, e1);
+ result = (*pue).exp();
+ return;
+ }
+ if (e.e1.type.ty == Tpointer || e.e2.type.ty == Tpointer)
+ {
+ e.error("pointer expression `%s` cannot be interpreted at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ bool evalOperand(UnionExp* pue, Expression ex, out Expression er)
+ {
+ er = interpret(pue, ex, istate);
+ if (exceptionOrCant(er))
+ return false;
+ return true;
+ }
+
+ UnionExp ue1 = void;
+ Expression e1;
+ if (!evalOperand(&ue1, e.e1, e1))
+ return;
+
+ UnionExp ue2 = void;
+ Expression e2;
+ if (!evalOperand(&ue2, e.e2, e2))
+ return;
+
+ if (e.op == TOK.rightShift || e.op == TOK.leftShift || e.op == TOK.unsignedRightShift)
+ {
+ const sinteger_t i2 = e2.toInteger();
+ const d_uns64 sz = e1.type.size() * 8;
+ if (i2 < 0 || i2 >= sz)
+ {
+ e.error("shift by %lld is outside the range 0..%llu", i2, cast(ulong)sz - 1);
+ result = CTFEExp.cantexp;
+ return;
+ }
+ }
+
+ /******************************************
+ * Perform the operation fp on operands e1 and e2.
+ */
+ UnionExp evaluate(Loc loc, Type type, Expression e1, Expression e2)
+ {
+ UnionExp ue = void;
+ auto ae1 = e1.isArrayLiteralExp();
+ auto ae2 = e2.isArrayLiteralExp();
+ if (ae1 || ae2)
+ {
+ /* Cases:
+ * 1. T[] op T[]
+ * 2. T op T[]
+ * 3. T[] op T
+ */
+ if (ae1 && e2.implicitConvTo(e1.type.toBasetype().nextOf())) // case 3
+ ae2 = null;
+ else if (ae2 && e1.implicitConvTo(e2.type.toBasetype().nextOf())) // case 2
+ ae1 = null;
+ // else case 1
+
+ auto aex = ae1 ? ae1 : ae2;
+ if (!aex.elements)
+ {
+ emplaceExp!ArrayLiteralExp(&ue, loc, type, cast(Expressions*) null);
+ return ue;
+ }
+ const length = aex.elements.length;
+ Expressions* elements = new Expressions(length);
+
+ emplaceExp!ArrayLiteralExp(&ue, loc, type, elements);
+ foreach (i; 0 .. length)
+ {
+ Expression e1x = ae1 ? ae1[i] : e1;
+ Expression e2x = ae2 ? ae2[i] : e2;
+ UnionExp uex = evaluate(loc, e1x.type, e1x, e2x);
+ // This can be made more efficient by making use of ue.basis
+ (*elements)[i] = uex.copy();
+ }
+ return ue;
+ }
+
+ if (e1.isConst() != 1)
+ {
+ // The following should really be an assert()
+ e1.error("CTFE internal error: non-constant value `%s`", e1.toChars());
+ emplaceExp!CTFEExp(&ue, TOK.cantExpression);
+ return ue;
+ }
+ if (e2.isConst() != 1)
+ {
+ e2.error("CTFE internal error: non-constant value `%s`", e2.toChars());
+ emplaceExp!CTFEExp(&ue, TOK.cantExpression);
+ return ue;
+ }
+
+ return (*fp)(loc, type, e1, e2);
+ }
+
+ *pue = evaluate(e.loc, e.type, e1, e2);
+ result = (*pue).exp();
+ if (CTFEExp.isCantExp(result))
+ e.error("`%s` cannot be interpreted at compile time", e.toChars());
+ }
+
+ extern (D) private void interpretCompareCommon(BinExp e, fp2_t fp)
+ {
+ debug (LOG)
+ {
+ printf("%s BinExp::interpretCompareCommon() %s\n", e.loc.toChars(), e.toChars());
+ }
+ UnionExp ue1 = void;
+ UnionExp ue2 = void;
+ if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer)
+ {
+ Expression e1 = interpret(&ue1, e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ Expression e2 = interpret(&ue2, e.e2, istate);
+ if (exceptionOrCant(e2))
+ return;
+ //printf("e1 = %s %s, e2 = %s %s\n", e1.type.toChars(), e1.toChars(), e2.type.toChars(), e2.toChars());
+ dinteger_t ofs1, ofs2;
+ Expression agg1 = getAggregateFromPointer(e1, &ofs1);
+ Expression agg2 = getAggregateFromPointer(e2, &ofs2);
+ //printf("agg1 = %p %s, agg2 = %p %s\n", agg1, agg1.toChars(), agg2, agg2.toChars());
+ const cmp = comparePointers(e.op, agg1, ofs1, agg2, ofs2);
+ if (cmp == -1)
+ {
+ char dir = (e.op == TOK.greaterThan || e.op == TOK.greaterOrEqual) ? '<' : '>';
+ e.error("the ordering of pointers to unrelated memory blocks is indeterminate in CTFE. To check if they point to the same memory block, use both `>` and `<` inside `&&` or `||`, eg `%s && %s %c= %s + 1`", e.toChars(), e.e1.toChars(), dir, e.e2.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (e.type.equals(Type.tbool))
+ result = IntegerExp.createBool(cmp != 0);
+ else
+ {
+ emplaceExp!(IntegerExp)(pue, e.loc, cmp, e.type);
+ result = (*pue).exp();
+ }
+ return;
+ }
+ Expression e1 = interpret(&ue1, e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ if (!isCtfeComparable(e1))
+ {
+ e.error("cannot compare `%s` at compile time", e1.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ Expression e2 = interpret(&ue2, e.e2, istate);
+ if (exceptionOrCant(e2))
+ return;
+ if (!isCtfeComparable(e2))
+ {
+ e.error("cannot compare `%s` at compile time", e2.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ const cmp = (*fp)(e.loc, e.op, e1, e2);
+ if (e.type.equals(Type.tbool))
+ result = IntegerExp.createBool(cmp);
+ else
+ {
+ emplaceExp!(IntegerExp)(pue, e.loc, cmp, e.type);
+ result = (*pue).exp();
+ }
+ }
+
+ override void visit(BinExp e)
+ {
+ switch (e.op)
+ {
+ case TOK.add:
+ interpretCommon(e, &Add);
+ return;
+
+ case TOK.min:
+ interpretCommon(e, &Min);
+ return;
+
+ case TOK.mul:
+ interpretCommon(e, &Mul);
+ return;
+
+ case TOK.div:
+ interpretCommon(e, &Div);
+ return;
+
+ case TOK.mod:
+ interpretCommon(e, &Mod);
+ return;
+
+ case TOK.leftShift:
+ interpretCommon(e, &Shl);
+ return;
+
+ case TOK.rightShift:
+ interpretCommon(e, &Shr);
+ return;
+
+ case TOK.unsignedRightShift:
+ interpretCommon(e, &Ushr);
+ return;
+
+ case TOK.and:
+ interpretCommon(e, &And);
+ return;
+
+ case TOK.or:
+ interpretCommon(e, &Or);
+ return;
+
+ case TOK.xor:
+ interpretCommon(e, &Xor);
+ return;
+
+ case TOK.pow:
+ interpretCommon(e, &Pow);
+ return;
+
+ case TOK.equal:
+ case TOK.notEqual:
+ interpretCompareCommon(e, &ctfeEqual);
+ return;
+
+ case TOK.identity:
+ case TOK.notIdentity:
+ interpretCompareCommon(e, &ctfeIdentity);
+ return;
+
+ case TOK.lessThan:
+ case TOK.lessOrEqual:
+ case TOK.greaterThan:
+ case TOK.greaterOrEqual:
+ interpretCompareCommon(e, &ctfeCmp);
+ return;
+
+ default:
+ printf("be = '%s' %s at [%s]\n", Token.toChars(e.op), e.toChars(), e.loc.toChars());
+ assert(0);
+ }
+ }
+
+ /* Helper functions for BinExp::interpretAssignCommon
+ */
+ // Returns the variable which is eventually modified, or NULL if an rvalue.
+ // thisval is the current value of 'this'.
+ static VarDeclaration findParentVar(Expression e)
+ {
+ for (;;)
+ {
+ if (auto ve = e.isVarExp())
+ {
+ VarDeclaration v = ve.var.isVarDeclaration();
+ assert(v);
+ return v;
+ }
+ if (auto ie = e.isIndexExp())
+ e = ie.e1;
+ else if (auto dve = e.isDotVarExp())
+ e = dve.e1;
+ else if (auto dtie = e.isDotTemplateInstanceExp())
+ e = dtie.e1;
+ else if (auto se = e.isSliceExp())
+ e = se.e1;
+ else
+ return null;
+ }
+ }
+
+ extern (D) private void interpretAssignCommon(BinExp e, fp_t fp, int post = 0)
+ {
+ debug (LOG)
+ {
+ printf("%s BinExp::interpretAssignCommon() %s\n", e.loc.toChars(), e.toChars());
+ }
+ result = CTFEExp.cantexp;
+
+ Expression e1 = e.e1;
+ if (!istate)
+ {
+ e.error("value of `%s` is not known at compile time", e1.toChars());
+ return;
+ }
+
+ ++ctfeGlobals.numAssignments;
+
+ /* Before we begin, we need to know if this is a reference assignment
+ * (dynamic array, AA, or class) or a value assignment.
+ * Determining this for slice assignments are tricky: we need to know
+ * if it is a block assignment (a[] = e) rather than a direct slice
+ * assignment (a[] = b[]). Note that initializers of multi-dimensional
+ * static arrays can have 2D block assignments (eg, int[7][7] x = 6;).
+ * So we need to recurse to determine if it is a block assignment.
+ */
+ bool isBlockAssignment = false;
+ if (e1.op == TOK.slice)
+ {
+ // a[] = e can have const e. So we compare the naked types.
+ Type tdst = e1.type.toBasetype();
+ Type tsrc = e.e2.type.toBasetype();
+ while (tdst.ty == Tsarray || tdst.ty == Tarray)
+ {
+ tdst = (cast(TypeArray)tdst).next.toBasetype();
+ if (tsrc.equivalent(tdst))
+ {
+ isBlockAssignment = true;
+ break;
+ }
+ }
+ }
+
+ // ---------------------------------------
+ // Deal with reference assignment
+ // ---------------------------------------
+ // If it is a construction of a ref variable, it is a ref assignment
+ if ((e.op == TOK.construct || e.op == TOK.blit) &&
+ ((cast(AssignExp)e).memset == MemorySet.referenceInit))
+ {
+ assert(!fp);
+
+ Expression newval = interpretRegion(e.e2, istate, CTFEGoal.LValue);
+ if (exceptionOrCant(newval))
+ return;
+
+ VarDeclaration v = (cast(VarExp)e1).var.isVarDeclaration();
+ setValue(v, newval);
+
+ // Get the value to return. Note that 'newval' is an Lvalue,
+ // so if we need an Rvalue, we have to interpret again.
+ if (goal == CTFEGoal.RValue)
+ result = interpretRegion(newval, istate);
+ else
+ result = e1; // VarExp is a CTFE reference
+ return;
+ }
+
+ if (fp)
+ {
+ while (e1.op == TOK.cast_)
+ {
+ CastExp ce = cast(CastExp)e1;
+ e1 = ce.e1;
+ }
+ }
+
+ // ---------------------------------------
+ // Interpret left hand side
+ // ---------------------------------------
+ AssocArrayLiteralExp existingAA = null;
+ Expression lastIndex = null;
+ Expression oldval = null;
+ if (e1.op == TOK.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
+ {
+ // ---------------------------------------
+ // Deal with AA index assignment
+ // ---------------------------------------
+ /* This needs special treatment if the AA doesn't exist yet.
+ * There are two special cases:
+ * (1) If the AA is itself an index of another AA, we may need to create
+ * multiple nested AA literals before we can insert the new value.
+ * (2) If the ultimate AA is null, no insertion happens at all. Instead,
+ * we create nested AA literals, and change it into a assignment.
+ */
+ IndexExp ie = cast(IndexExp)e1;
+ int depth = 0; // how many nested AA indices are there?
+ while (ie.e1.op == TOK.index && (cast(IndexExp)ie.e1).e1.type.toBasetype().ty == Taarray)
+ {
+ assert(ie.modifiable);
+ ie = cast(IndexExp)ie.e1;
+ ++depth;
+ }
+
+ // Get the AA value to be modified.
+ Expression aggregate = interpretRegion(ie.e1, istate);
+ if (exceptionOrCant(aggregate))
+ return;
+ if ((existingAA = aggregate.isAssocArrayLiteralExp()) !is null)
+ {
+ // Normal case, ultimate parent AA already exists
+ // We need to walk from the deepest index up, checking that an AA literal
+ // already exists on each level.
+ lastIndex = interpretRegion((cast(IndexExp)e1).e2, istate);
+ lastIndex = resolveSlice(lastIndex); // only happens with AA assignment
+ if (exceptionOrCant(lastIndex))
+ return;
+
+ while (depth > 0)
+ {
+ // Walk the syntax tree to find the indexExp at this depth
+ IndexExp xe = cast(IndexExp)e1;
+ foreach (d; 0 .. depth)
+ xe = cast(IndexExp)xe.e1;
+
+ Expression ekey = interpretRegion(xe.e2, istate);
+ if (exceptionOrCant(ekey))
+ return;
+ UnionExp ekeyTmp = void;
+ ekey = resolveSlice(ekey, &ekeyTmp); // only happens with AA assignment
+
+ // Look up this index in it up in the existing AA, to get the next level of AA.
+ AssocArrayLiteralExp newAA = cast(AssocArrayLiteralExp)findKeyInAA(e.loc, existingAA, ekey);
+ if (exceptionOrCant(newAA))
+ return;
+ if (!newAA)
+ {
+ // Doesn't exist yet, create an empty AA...
+ auto keysx = new Expressions();
+ auto valuesx = new Expressions();
+ newAA = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
+ newAA.type = xe.type;
+ newAA.ownedByCtfe = OwnedBy.ctfe;
+ //... and insert it into the existing AA.
+ existingAA.keys.push(ekey);
+ existingAA.values.push(newAA);
+ }
+ existingAA = newAA;
+ --depth;
+ }
+
+ if (fp)
+ {
+ oldval = findKeyInAA(e.loc, existingAA, lastIndex);
+ if (!oldval)
+ oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy();
+ }
+ }
+ else
+ {
+ /* The AA is currently null. 'aggregate' is actually a reference to
+ * whatever contains it. It could be anything: var, dotvarexp, ...
+ * We rewrite the assignment from:
+ * aa[i][j] op= newval;
+ * into:
+ * aa = [i:[j:T.init]];
+ * aa[j] op= newval;
+ */
+ oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy();
+
+ Expression newaae = oldval;
+ while (e1.op == TOK.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
+ {
+ Expression ekey = interpretRegion((cast(IndexExp)e1).e2, istate);
+ if (exceptionOrCant(ekey))
+ return;
+ ekey = resolveSlice(ekey); // only happens with AA assignment
+
+ auto keysx = new Expressions();
+ auto valuesx = new Expressions();
+ keysx.push(ekey);
+ valuesx.push(newaae);
+
+ auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
+ aae.type = (cast(IndexExp)e1).e1.type;
+ aae.ownedByCtfe = OwnedBy.ctfe;
+ if (!existingAA)
+ {
+ existingAA = aae;
+ lastIndex = ekey;
+ }
+ newaae = aae;
+ e1 = (cast(IndexExp)e1).e1;
+ }
+
+ // We must set to aggregate with newaae
+ e1 = interpretRegion(e1, istate, CTFEGoal.LValue);
+ if (exceptionOrCant(e1))
+ return;
+ e1 = assignToLvalue(e, e1, newaae);
+ if (exceptionOrCant(e1))
+ return;
+ }
+ assert(existingAA && lastIndex);
+ e1 = null; // stomp
+ }
+ else if (e1.op == TOK.arrayLength)
+ {
+ oldval = interpretRegion(e1, istate);
+ if (exceptionOrCant(oldval))
+ return;
+ }
+ else if (e.op == TOK.construct || e.op == TOK.blit)
+ {
+ // Unless we have a simple var assignment, we're
+ // only modifying part of the variable. So we need to make sure
+ // that the parent variable exists.
+ VarDeclaration ultimateVar = findParentVar(e1);
+ if (auto ve = e1.isVarExp())
+ {
+ VarDeclaration v = ve.var.isVarDeclaration();
+ assert(v);
+ if (v.storage_class & STC.out_)
+ goto L1;
+ }
+ else if (ultimateVar && !getValue(ultimateVar))
+ {
+ Expression ex = interpretRegion(ultimateVar.type.defaultInitLiteral(e.loc), istate);
+ if (exceptionOrCant(ex))
+ return;
+ setValue(ultimateVar, ex);
+ }
+ else
+ goto L1;
+ }
+ else
+ {
+ L1:
+ e1 = interpretRegion(e1, istate, CTFEGoal.LValue);
+ if (exceptionOrCant(e1))
+ return;
+
+ if (e1.op == TOK.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
+ {
+ IndexExp ie = cast(IndexExp)e1;
+ assert(ie.e1.op == TOK.assocArrayLiteral);
+ existingAA = cast(AssocArrayLiteralExp)ie.e1;
+ lastIndex = ie.e2;
+ }
+ }
+
+ // ---------------------------------------
+ // Interpret right hand side
+ // ---------------------------------------
+ Expression newval = interpretRegion(e.e2, istate);
+ if (exceptionOrCant(newval))
+ return;
+ if (e.op == TOK.blit && newval.op == TOK.int64)
+ {
+ Type tbn = e.type.baseElemOf();
+ if (tbn.ty == Tstruct)
+ {
+ /* Look for special case of struct being initialized with 0.
+ */
+ newval = e.type.defaultInitLiteral(e.loc);
+ if (newval.op == TOK.error)
+ {
+ result = CTFEExp.cantexp;
+ return;
+ }
+ newval = interpretRegion(newval, istate); // copy and set ownedByCtfe flag
+ if (exceptionOrCant(newval))
+ return;
+ }
+ }
+
+ // ----------------------------------------------------
+ // Deal with read-modify-write assignments.
+ // Set 'newval' to the final assignment value
+ // Also determine the return value (except for slice
+ // assignments, which are more complicated)
+ // ----------------------------------------------------
+ if (fp)
+ {
+ if (!oldval)
+ {
+ // Load the left hand side after interpreting the right hand side.
+ oldval = interpretRegion(e1, istate);
+ if (exceptionOrCant(oldval))
+ return;
+ }
+
+ if (e.e1.type.ty != Tpointer)
+ {
+ // ~= can create new values (see bug 6052)
+ if (e.op == TOK.concatenateAssign || e.op == TOK.concatenateElemAssign || e.op == TOK.concatenateDcharAssign)
+ {
+ // We need to dup it and repaint the type. For a dynamic array
+ // we can skip duplication, because it gets copied later anyway.
+ if (newval.type.ty != Tarray)
+ {
+ newval = copyLiteral(newval).copy();
+ newval.type = e.e2.type; // repaint type
+ }
+ else
+ {
+ newval = paintTypeOntoLiteral(e.e2.type, newval);
+ newval = resolveSlice(newval);
+ }
+ }
+ oldval = resolveSlice(oldval);
+
+ newval = (*fp)(e.loc, e.type, oldval, newval).copy();
+ }
+ else if (e.e2.type.isintegral() &&
+ (e.op == TOK.addAssign ||
+ e.op == TOK.minAssign ||
+ e.op == TOK.plusPlus ||
+ e.op == TOK.minusMinus))
+ {
+ newval = pointerArithmetic(e.loc, e.op, e.type, oldval, newval).copy();
+ }
+ else
+ {
+ e.error("pointer expression `%s` cannot be interpreted at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (exceptionOrCant(newval))
+ {
+ if (CTFEExp.isCantExp(newval))
+ e.error("cannot interpret `%s` at compile time", e.toChars());
+ return;
+ }
+ }
+
+ if (existingAA)
+ {
+ if (existingAA.ownedByCtfe != OwnedBy.ctfe)
+ {
+ e.error("cannot modify read-only constant `%s`", existingAA.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ //printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n",
+ // __LINE__, existingAA.toChars(), lastIndex.toChars(), oldval ? oldval.toChars() : NULL, newval.toChars());
+ assignAssocArrayElement(e.loc, existingAA, lastIndex, newval);
+
+ // Determine the return value
+ result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval);
+ return;
+ }
+ if (e1.op == TOK.arrayLength)
+ {
+ /* Change the assignment from:
+ * arr.length = n;
+ * into:
+ * arr = new_length_array; (result is n)
+ */
+
+ // Determine the return value
+ result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval);
+ if (exceptionOrCant(result))
+ return;
+
+ if (result == pue.exp())
+ result = pue.copy();
+
+ size_t oldlen = cast(size_t)oldval.toInteger();
+ size_t newlen = cast(size_t)newval.toInteger();
+ if (oldlen == newlen) // no change required -- we're done!
+ return;
+
+ // We have changed it into a reference assignment
+ // Note that returnValue is still the new length.
+ e1 = (cast(ArrayLengthExp)e1).e1;
+ Type t = e1.type.toBasetype();
+ if (t.ty != Tarray)
+ {
+ e.error("`%s` is not yet supported at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ e1 = interpretRegion(e1, istate, CTFEGoal.LValue);
+ if (exceptionOrCant(e1))
+ return;
+
+ if (oldlen != 0) // Get the old array literal.
+ oldval = interpretRegion(e1, istate);
+ UnionExp utmp = void;
+ oldval = resolveSlice(oldval, &utmp);
+
+ newval = changeArrayLiteralLength(e.loc, cast(TypeArray)t, oldval, oldlen, newlen).copy();
+
+ e1 = assignToLvalue(e, e1, newval);
+ if (exceptionOrCant(e1))
+ return;
+
+ return;
+ }
+
+ if (!isBlockAssignment)
+ {
+ newval = ctfeCast(pue, e.loc, e.type, e.type, newval);
+ if (exceptionOrCant(newval))
+ return;
+ if (newval == pue.exp())
+ newval = pue.copy();
+
+ // Determine the return value
+ if (goal == CTFEGoal.LValue) // https://issues.dlang.org/show_bug.cgi?id=14371
+ result = e1;
+ else
+ {
+ result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval);
+ if (result == pue.exp())
+ result = pue.copy();
+ }
+ if (exceptionOrCant(result))
+ return;
+ }
+ if (exceptionOrCant(newval))
+ return;
+
+ debug (LOGASSIGN)
+ {
+ printf("ASSIGN: %s=%s\n", e1.toChars(), newval.toChars());
+ showCtfeExpr(newval);
+ }
+
+ /* Block assignment or element-wise assignment.
+ */
+ if (e1.op == TOK.slice ||
+ e1.op == TOK.vector ||
+ e1.op == TOK.arrayLiteral ||
+ e1.op == TOK.string_ ||
+ e1.op == TOK.null_ && e1.type.toBasetype().ty == Tarray)
+ {
+ // Note that slice assignments don't support things like ++, so
+ // we don't need to remember 'returnValue'.
+ result = interpretAssignToSlice(pue, e, e1, newval, isBlockAssignment);
+ if (exceptionOrCant(result))
+ return;
+ if (auto se = e.e1.isSliceExp())
+ {
+ Expression e1x = interpretRegion(se.e1, istate, CTFEGoal.LValue);
+ if (auto dve = e1x.isDotVarExp())
+ {
+ auto ex = dve.e1;
+ auto sle = ex.op == TOK.structLiteral ? (cast(StructLiteralExp)ex)
+ : ex.op == TOK.classReference ? (cast(ClassReferenceExp)ex).value
+ : null;
+ auto v = dve.var.isVarDeclaration();
+ if (!sle || !v)
+ {
+ e.error("CTFE internal error: dotvar slice assignment");
+ result = CTFEExp.cantexp;
+ return;
+ }
+ stompOverlappedFields(sle, v);
+ }
+ }
+ return;
+ }
+ assert(result);
+
+ /* Assignment to a CTFE reference.
+ */
+ if (Expression ex = assignToLvalue(e, e1, newval))
+ result = ex;
+
+ return;
+ }
+
+ /* Set all sibling fields which overlap with v to VoidExp.
+ */
+ private void stompOverlappedFields(StructLiteralExp sle, VarDeclaration v)
+ {
+ if (!v.overlapped)
+ return;
+ foreach (size_t i, v2; sle.sd.fields)
+ {
+ if (v is v2 || !v.isOverlappedWith(v2))
+ continue;
+ auto e = (*sle.elements)[i];
+ if (e.op != TOK.void_)
+ (*sle.elements)[i] = voidInitLiteral(e.type, v).copy();
+ }
+ }
+
+ private Expression assignToLvalue(BinExp e, Expression e1, Expression newval)
+ {
+ //printf("assignToLvalue() e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars());
+ VarDeclaration vd = null;
+ Expression* payload = null; // dead-store to prevent spurious warning
+ Expression oldval;
+
+ if (auto ve = e1.isVarExp())
+ {
+ vd = ve.var.isVarDeclaration();
+ oldval = getValue(vd);
+ }
+ else if (auto dve = e1.isDotVarExp())
+ {
+ /* Assignment to member variable of the form:
+ * e.v = newval
+ */
+ auto ex = dve.e1;
+ auto sle = ex.op == TOK.structLiteral ? (cast(StructLiteralExp)ex)
+ : ex.op == TOK.classReference ? (cast(ClassReferenceExp)ex).value
+ : null;
+ auto v = (cast(DotVarExp)e1).var.isVarDeclaration();
+ if (!sle || !v)
+ {
+ e.error("CTFE internal error: dotvar assignment");
+ return CTFEExp.cantexp;
+ }
+ if (sle.ownedByCtfe != OwnedBy.ctfe)
+ {
+ e.error("cannot modify read-only constant `%s`", sle.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ int fieldi = ex.op == TOK.structLiteral ? findFieldIndexByName(sle.sd, v)
+ : (cast(ClassReferenceExp)ex).findFieldIndexByName(v);
+ if (fieldi == -1)
+ {
+ e.error("CTFE internal error: cannot find field `%s` in `%s`", v.toChars(), ex.toChars());
+ return CTFEExp.cantexp;
+ }
+ assert(0 <= fieldi && fieldi < sle.elements.dim);
+
+ // If it's a union, set all other members of this union to void
+ stompOverlappedFields(sle, v);
+
+ payload = &(*sle.elements)[fieldi];
+ oldval = *payload;
+ }
+ else if (auto ie = e1.isIndexExp())
+ {
+ assert(ie.e1.type.toBasetype().ty != Taarray);
+
+ Expression aggregate;
+ uinteger_t indexToModify;
+ if (!resolveIndexing(ie, istate, &aggregate, &indexToModify, true))
+ {
+ return CTFEExp.cantexp;
+ }
+ size_t index = cast(size_t)indexToModify;
+
+ if (auto existingSE = aggregate.isStringExp())
+ {
+ if (existingSE.ownedByCtfe != OwnedBy.ctfe)
+ {
+ e.error("cannot modify read-only string literal `%s`", ie.e1.toChars());
+ return CTFEExp.cantexp;
+ }
+ existingSE.setCodeUnit(index, cast(dchar)newval.toInteger());
+ return null;
+ }
+ if (aggregate.op != TOK.arrayLiteral)
+ {
+ e.error("index assignment `%s` is not yet supported in CTFE ", e.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ ArrayLiteralExp existingAE = cast(ArrayLiteralExp)aggregate;
+ if (existingAE.ownedByCtfe != OwnedBy.ctfe)
+ {
+ e.error("cannot modify read-only constant `%s`", existingAE.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ payload = &(*existingAE.elements)[index];
+ oldval = *payload;
+ }
+ else
+ {
+ e.error("`%s` cannot be evaluated at compile time", e.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ Type t1b = e1.type.toBasetype();
+ bool wantCopy = t1b.baseElemOf().ty == Tstruct;
+
+ if (auto ve = newval.isVectorExp())
+ {
+ // Ensure ve is an array literal, and not a broadcast
+ if (ve.e1.op == TOK.int64 || ve.e1.op == TOK.float64) // if broadcast
+ {
+ UnionExp ue = void;
+ Expression ex = interpretVectorToArray(&ue, ve);
+ ve.e1 = (ex == ue.exp()) ? ue.copy() : ex;
+ }
+ }
+
+ if (newval.op == TOK.structLiteral && oldval)
+ {
+ assert(oldval.op == TOK.structLiteral || oldval.op == TOK.arrayLiteral || oldval.op == TOK.string_);
+ newval = copyLiteral(newval).copy();
+ assignInPlace(oldval, newval);
+ }
+ else if (wantCopy && e.op == TOK.assign)
+ {
+ // Currently postblit/destructor calls on static array are done
+ // in the druntime internal functions so they don't appear in AST.
+ // Therefore interpreter should handle them specially.
+
+ assert(oldval);
+ version (all) // todo: instead we can directly access to each elements of the slice
+ {
+ newval = resolveSlice(newval);
+ if (CTFEExp.isCantExp(newval))
+ {
+ e.error("CTFE internal error: assignment `%s`", e.toChars());
+ return CTFEExp.cantexp;
+ }
+ }
+ assert(oldval.op == TOK.arrayLiteral);
+ assert(newval.op == TOK.arrayLiteral);
+
+ Expressions* oldelems = (cast(ArrayLiteralExp)oldval).elements;
+ Expressions* newelems = (cast(ArrayLiteralExp)newval).elements;
+ assert(oldelems.dim == newelems.dim);
+
+ Type elemtype = oldval.type.nextOf();
+ foreach (i, ref oldelem; *oldelems)
+ {
+ Expression newelem = paintTypeOntoLiteral(elemtype, (*newelems)[i]);
+ // https://issues.dlang.org/show_bug.cgi?id=9245
+ if (e.e2.isLvalue())
+ {
+ if (Expression ex = evaluatePostblit(istate, newelem))
+ return ex;
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=13661
+ if (Expression ex = evaluateDtor(istate, oldelem))
+ return ex;
+ oldelem = newelem;
+ }
+ }
+ else
+ {
+ // e1 has its own payload, so we have to create a new literal.
+ if (wantCopy)
+ newval = copyLiteral(newval).copy();
+
+ if (t1b.ty == Tsarray && e.op == TOK.construct && e.e2.isLvalue())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=9245
+ if (Expression ex = evaluatePostblit(istate, newval))
+ return ex;
+ }
+
+ oldval = newval;
+ }
+
+ if (vd)
+ setValue(vd, oldval);
+ else
+ *payload = oldval;
+
+ // Blit assignment should return the newly created value.
+ if (e.op == TOK.blit)
+ return oldval;
+
+ return null;
+ }
+
+ /*************
+ * Deal with assignments of the form:
+ * dest[] = newval
+ * dest[low..upp] = newval
+ * where newval has already been interpreted
+ *
+ * This could be a slice assignment or a block assignment, and
+ * dest could be either an array literal, or a string.
+ *
+ * Returns TOK.cantExpression on failure. If there are no errors,
+ * it returns aggregate[low..upp], except that as an optimisation,
+ * if goal == CTFEGoal.Nothing, it will return NULL
+ */
+ private Expression interpretAssignToSlice(UnionExp* pue, BinExp e, Expression e1, Expression newval, bool isBlockAssignment)
+ {
+ dinteger_t lowerbound;
+ dinteger_t upperbound;
+ dinteger_t firstIndex;
+
+ Expression aggregate;
+
+ if (auto se = e1.isSliceExp())
+ {
+ // ------------------------------
+ // aggregate[] = newval
+ // aggregate[low..upp] = newval
+ // ------------------------------
+ version (all) // should be move in interpretAssignCommon as the evaluation of e1
+ {
+ Expression oldval = interpretRegion(se.e1, istate);
+
+ // Set the $ variable
+ uinteger_t dollar = resolveArrayLength(oldval);
+ if (se.lengthVar)
+ {
+ Expression dollarExp = ctfeEmplaceExp!IntegerExp(e1.loc, dollar, Type.tsize_t);
+ ctfeGlobals.stack.push(se.lengthVar);
+ setValue(se.lengthVar, dollarExp);
+ }
+ Expression lwr = interpretRegion(se.lwr, istate);
+ if (exceptionOrCantInterpret(lwr))
+ {
+ if (se.lengthVar)
+ ctfeGlobals.stack.pop(se.lengthVar);
+ return lwr;
+ }
+ Expression upr = interpretRegion(se.upr, istate);
+ if (exceptionOrCantInterpret(upr))
+ {
+ if (se.lengthVar)
+ ctfeGlobals.stack.pop(se.lengthVar);
+ return upr;
+ }
+ if (se.lengthVar)
+ ctfeGlobals.stack.pop(se.lengthVar); // $ is defined only in [L..U]
+
+ const dim = dollar;
+ lowerbound = lwr ? lwr.toInteger() : 0;
+ upperbound = upr ? upr.toInteger() : dim;
+
+ if (lowerbound < 0 || dim < upperbound)
+ {
+ e.error("array bounds `[0..%llu]` exceeded in slice `[%llu..%llu]`",
+ ulong(dim), ulong(lowerbound), ulong(upperbound));
+ return CTFEExp.cantexp;
+ }
+ }
+ aggregate = oldval;
+ firstIndex = lowerbound;
+
+ if (auto oldse = aggregate.isSliceExp())
+ {
+ // Slice of a slice --> change the bounds
+ if (oldse.upr.toInteger() < upperbound + oldse.lwr.toInteger())
+ {
+ e.error("slice `[%llu..%llu]` exceeds array bounds `[0..%llu]`",
+ ulong(lowerbound), ulong(upperbound), oldse.upr.toInteger() - oldse.lwr.toInteger());
+ return CTFEExp.cantexp;
+ }
+ aggregate = oldse.e1;
+ firstIndex = lowerbound + oldse.lwr.toInteger();
+ }
+ }
+ else
+ {
+ if (auto ale = e1.isArrayLiteralExp())
+ {
+ lowerbound = 0;
+ upperbound = ale.elements.dim;
+ }
+ else if (auto se = e1.isStringExp())
+ {
+ lowerbound = 0;
+ upperbound = se.len;
+ }
+ else if (e1.op == TOK.null_)
+ {
+ lowerbound = 0;
+ upperbound = 0;
+ }
+ else if (VectorExp ve = e1.isVectorExp())
+ {
+ // ve is not handled but a proper error message is returned
+ // this is to prevent https://issues.dlang.org/show_bug.cgi?id=20042
+ lowerbound = 0;
+ upperbound = ve.dim;
+ }
+ else
+ assert(0);
+
+ aggregate = e1;
+ firstIndex = lowerbound;
+ }
+ if (upperbound == lowerbound)
+ return newval;
+
+ // For slice assignment, we check that the lengths match.
+ if (!isBlockAssignment)
+ {
+ const srclen = resolveArrayLength(newval);
+ if (srclen != (upperbound - lowerbound))
+ {
+ e.error("array length mismatch assigning `[0..%llu]` to `[%llu..%llu]`",
+ ulong(srclen), ulong(lowerbound), ulong(upperbound));
+ return CTFEExp.cantexp;
+ }
+ }
+
+ if (auto existingSE = aggregate.isStringExp())
+ {
+ if (existingSE.ownedByCtfe != OwnedBy.ctfe)
+ {
+ e.error("cannot modify read-only string literal `%s`", existingSE.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ if (auto se = newval.isSliceExp())
+ {
+ auto aggr2 = se.e1;
+ const srclower = se.lwr.toInteger();
+ const srcupper = se.upr.toInteger();
+
+ if (aggregate == aggr2 &&
+ lowerbound < srcupper && srclower < upperbound)
+ {
+ e.error("overlapping slice assignment `[%llu..%llu] = [%llu..%llu]`",
+ ulong(lowerbound), ulong(upperbound), ulong(srclower), ulong(srcupper));
+ return CTFEExp.cantexp;
+ }
+ version (all) // todo: instead we can directly access to each elements of the slice
+ {
+ Expression orignewval = newval;
+ newval = resolveSlice(newval);
+ if (CTFEExp.isCantExp(newval))
+ {
+ e.error("CTFE internal error: slice `%s`", orignewval.toChars());
+ return CTFEExp.cantexp;
+ }
+ }
+ assert(newval.op != TOK.slice);
+ }
+ if (auto se = newval.isStringExp())
+ {
+ sliceAssignStringFromString(existingSE, se, cast(size_t)firstIndex);
+ return newval;
+ }
+ if (auto ale = newval.isArrayLiteralExp())
+ {
+ /* Mixed slice: it was initialized as a string literal.
+ * Now a slice of it is being set with an array literal.
+ */
+ sliceAssignStringFromArrayLiteral(existingSE, ale, cast(size_t)firstIndex);
+ return newval;
+ }
+
+ // String literal block slice assign
+ const value = cast(dchar)newval.toInteger();
+ foreach (i; 0 .. upperbound - lowerbound)
+ {
+ existingSE.setCodeUnit(cast(size_t)(i + firstIndex), value);
+ }
+ if (goal == CTFEGoal.Nothing)
+ return null; // avoid creating an unused literal
+ auto retslice = ctfeEmplaceExp!SliceExp(e.loc, existingSE,
+ ctfeEmplaceExp!IntegerExp(e.loc, firstIndex, Type.tsize_t),
+ ctfeEmplaceExp!IntegerExp(e.loc, firstIndex + upperbound - lowerbound, Type.tsize_t));
+ retslice.type = e.type;
+ return interpret(pue, retslice, istate);
+ }
+ if (auto existingAE = aggregate.isArrayLiteralExp())
+ {
+ if (existingAE.ownedByCtfe != OwnedBy.ctfe)
+ {
+ e.error("cannot modify read-only constant `%s`", existingAE.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ if (newval.op == TOK.slice && !isBlockAssignment)
+ {
+ auto se = cast(SliceExp)newval;
+ auto aggr2 = se.e1;
+ const srclower = se.lwr.toInteger();
+ const srcupper = se.upr.toInteger();
+ const wantCopy = (newval.type.toBasetype().nextOf().baseElemOf().ty == Tstruct);
+
+ //printf("oldval = %p %s[%d..%u]\nnewval = %p %s[%llu..%llu] wantCopy = %d\n",
+ // aggregate, aggregate.toChars(), lowerbound, upperbound,
+ // aggr2, aggr2.toChars(), srclower, srcupper, wantCopy);
+ if (wantCopy)
+ {
+ // Currently overlapping for struct array is allowed.
+ // The order of elements processing depends on the overlapping.
+ // https://issues.dlang.org/show_bug.cgi?id=14024
+ assert(aggr2.op == TOK.arrayLiteral);
+ Expressions* oldelems = existingAE.elements;
+ Expressions* newelems = (cast(ArrayLiteralExp)aggr2).elements;
+
+ Type elemtype = aggregate.type.nextOf();
+ bool needsPostblit = e.e2.isLvalue();
+
+ if (aggregate == aggr2 && srclower < lowerbound && lowerbound < srcupper)
+ {
+ // reverse order
+ for (auto i = upperbound - lowerbound; 0 < i--;)
+ {
+ Expression oldelem = (*oldelems)[cast(size_t)(i + firstIndex)];
+ Expression newelem = (*newelems)[cast(size_t)(i + srclower)];
+ newelem = copyLiteral(newelem).copy();
+ newelem.type = elemtype;
+ if (needsPostblit)
+ {
+ if (Expression x = evaluatePostblit(istate, newelem))
+ return x;
+ }
+ if (Expression x = evaluateDtor(istate, oldelem))
+ return x;
+ (*oldelems)[cast(size_t)(lowerbound + i)] = newelem;
+ }
+ }
+ else
+ {
+ // normal order
+ for (auto i = 0; i < upperbound - lowerbound; i++)
+ {
+ Expression oldelem = (*oldelems)[cast(size_t)(i + firstIndex)];
+ Expression newelem = (*newelems)[cast(size_t)(i + srclower)];
+ newelem = copyLiteral(newelem).copy();
+ newelem.type = elemtype;
+ if (needsPostblit)
+ {
+ if (Expression x = evaluatePostblit(istate, newelem))
+ return x;
+ }
+ if (Expression x = evaluateDtor(istate, oldelem))
+ return x;
+ (*oldelems)[cast(size_t)(lowerbound + i)] = newelem;
+ }
+ }
+
+ //assert(0);
+ return newval; // oldval?
+ }
+ if (aggregate == aggr2 &&
+ lowerbound < srcupper && srclower < upperbound)
+ {
+ e.error("overlapping slice assignment `[%llu..%llu] = [%llu..%llu]`",
+ ulong(lowerbound), ulong(upperbound), ulong(srclower), ulong(srcupper));
+ return CTFEExp.cantexp;
+ }
+ version (all) // todo: instead we can directly access to each elements of the slice
+ {
+ Expression orignewval = newval;
+ newval = resolveSlice(newval);
+ if (CTFEExp.isCantExp(newval))
+ {
+ e.error("CTFE internal error: slice `%s`", orignewval.toChars());
+ return CTFEExp.cantexp;
+ }
+ }
+ // no overlapping
+ //length?
+ assert(newval.op != TOK.slice);
+ }
+ if (newval.op == TOK.string_ && !isBlockAssignment)
+ {
+ /* Mixed slice: it was initialized as an array literal of chars/integers.
+ * Now a slice of it is being set with a string.
+ */
+ sliceAssignArrayLiteralFromString(existingAE, cast(StringExp)newval, cast(size_t)firstIndex);
+ return newval;
+ }
+ if (newval.op == TOK.arrayLiteral && !isBlockAssignment)
+ {
+ Expressions* oldelems = existingAE.elements;
+ Expressions* newelems = (cast(ArrayLiteralExp)newval).elements;
+ Type elemtype = existingAE.type.nextOf();
+ bool needsPostblit = e.op != TOK.blit && e.e2.isLvalue();
+ foreach (j, newelem; *newelems)
+ {
+ newelem = paintTypeOntoLiteral(elemtype, newelem);
+ if (needsPostblit)
+ {
+ Expression x = evaluatePostblit(istate, newelem);
+ if (exceptionOrCantInterpret(x))
+ return x;
+ }
+ (*oldelems)[cast(size_t)(j + firstIndex)] = newelem;
+ }
+ return newval;
+ }
+
+ /* Block assignment, initialization of static arrays
+ * x[] = newval
+ * x may be a multidimensional static array. (Note that this
+ * only happens with array literals, never with strings).
+ */
+ struct RecursiveBlock
+ {
+ InterState* istate;
+ Expression newval;
+ bool refCopy;
+ bool needsPostblit;
+ bool needsDtor;
+
+ extern (C++) Expression assignTo(ArrayLiteralExp ae)
+ {
+ return assignTo(ae, 0, ae.elements.dim);
+ }
+
+ extern (C++) Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr)
+ {
+ Expressions* w = ae.elements;
+ assert(ae.type.ty == Tsarray || ae.type.ty == Tarray);
+ bool directblk = (cast(TypeArray)ae.type).next.equivalent(newval.type);
+ for (size_t k = lwr; k < upr; k++)
+ {
+ if (!directblk && (*w)[k].op == TOK.arrayLiteral)
+ {
+ // Multidimensional array block assign
+ if (Expression ex = assignTo(cast(ArrayLiteralExp)(*w)[k]))
+ return ex;
+ }
+ else if (refCopy)
+ {
+ (*w)[k] = newval;
+ }
+ else if (!needsPostblit && !needsDtor)
+ {
+ assignInPlace((*w)[k], newval);
+ }
+ else
+ {
+ Expression oldelem = (*w)[k];
+ Expression tmpelem = needsDtor ? copyLiteral(oldelem).copy() : null;
+ assignInPlace(oldelem, newval);
+ if (needsPostblit)
+ {
+ if (Expression ex = evaluatePostblit(istate, oldelem))
+ return ex;
+ }
+ if (needsDtor)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14860
+ if (Expression ex = evaluateDtor(istate, tmpelem))
+ return ex;
+ }
+ }
+ }
+ return null;
+ }
+ }
+
+ Type tn = newval.type.toBasetype();
+ bool wantRef = (tn.ty == Tarray || isAssocArray(tn) || tn.ty == Tclass);
+ bool cow = newval.op != TOK.structLiteral && newval.op != TOK.arrayLiteral && newval.op != TOK.string_;
+ Type tb = tn.baseElemOf();
+ StructDeclaration sd = (tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null);
+
+ RecursiveBlock rb;
+ rb.istate = istate;
+ rb.newval = newval;
+ rb.refCopy = wantRef || cow;
+ rb.needsPostblit = sd && sd.postblit && e.op != TOK.blit && e.e2.isLvalue();
+ rb.needsDtor = sd && sd.dtor && e.op == TOK.assign;
+ if (Expression ex = rb.assignTo(existingAE, cast(size_t)lowerbound, cast(size_t)upperbound))
+ return ex;
+
+ if (goal == CTFEGoal.Nothing)
+ return null; // avoid creating an unused literal
+ auto retslice = ctfeEmplaceExp!SliceExp(e.loc, existingAE,
+ ctfeEmplaceExp!IntegerExp(e.loc, firstIndex, Type.tsize_t),
+ ctfeEmplaceExp!IntegerExp(e.loc, firstIndex + upperbound - lowerbound, Type.tsize_t));
+ retslice.type = e.type;
+ return interpret(pue, retslice, istate);
+ }
+
+ e.error("slice operation `%s = %s` cannot be evaluated at compile time", e1.toChars(), newval.toChars());
+ return CTFEExp.cantexp;
+ }
+
+ override void visit(AssignExp e)
+ {
+ interpretAssignCommon(e, null);
+ }
+
+ override void visit(BinAssignExp e)
+ {
+ switch (e.op)
+ {
+ case TOK.addAssign:
+ interpretAssignCommon(e, &Add);
+ return;
+
+ case TOK.minAssign:
+ interpretAssignCommon(e, &Min);
+ return;
+
+ case TOK.concatenateAssign:
+ case TOK.concatenateElemAssign:
+ case TOK.concatenateDcharAssign:
+ interpretAssignCommon(e, &ctfeCat);
+ return;
+
+ case TOK.mulAssign:
+ interpretAssignCommon(e, &Mul);
+ return;
+
+ case TOK.divAssign:
+ interpretAssignCommon(e, &Div);
+ return;
+
+ case TOK.modAssign:
+ interpretAssignCommon(e, &Mod);
+ return;
+
+ case TOK.leftShiftAssign:
+ interpretAssignCommon(e, &Shl);
+ return;
+
+ case TOK.rightShiftAssign:
+ interpretAssignCommon(e, &Shr);
+ return;
+
+ case TOK.unsignedRightShiftAssign:
+ interpretAssignCommon(e, &Ushr);
+ return;
+
+ case TOK.andAssign:
+ interpretAssignCommon(e, &And);
+ return;
+
+ case TOK.orAssign:
+ interpretAssignCommon(e, &Or);
+ return;
+
+ case TOK.xorAssign:
+ interpretAssignCommon(e, &Xor);
+ return;
+
+ case TOK.powAssign:
+ interpretAssignCommon(e, &Pow);
+ return;
+
+ default:
+ assert(0);
+ }
+ }
+
+ override void visit(PostExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s PostExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (e.op == TOK.plusPlus)
+ interpretAssignCommon(e, &Add, 1);
+ else
+ interpretAssignCommon(e, &Min, 1);
+ debug (LOG)
+ {
+ if (CTFEExp.isCantExp(result))
+ printf("PostExp::interpret() CANT\n");
+ }
+ }
+
+ /* Return 1 if e is a p1 > p2 or p1 >= p2 pointer comparison;
+ * -1 if e is a p1 < p2 or p1 <= p2 pointer comparison;
+ * 0 otherwise
+ */
+ static int isPointerCmpExp(Expression e, Expression* p1, Expression* p2)
+ {
+ int ret = 1;
+ while (e.op == TOK.not)
+ {
+ ret *= -1;
+ e = (cast(NotExp)e).e1;
+ }
+ switch (e.op)
+ {
+ case TOK.lessThan:
+ case TOK.lessOrEqual:
+ ret *= -1;
+ goto case; /+ fall through +/
+ case TOK.greaterThan:
+ case TOK.greaterOrEqual:
+ *p1 = (cast(BinExp)e).e1;
+ *p2 = (cast(BinExp)e).e2;
+ if (!(isPointer((*p1).type) && isPointer((*p2).type)))
+ ret = 0;
+ break;
+
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+ }
+
+ /** If this is a four pointer relation, evaluate it, else return NULL.
+ *
+ * This is an expression of the form (p1 > q1 && p2 < q2) or (p1 < q1 || p2 > q2)
+ * where p1, p2 are expressions yielding pointers to memory block p,
+ * and q1, q2 are expressions yielding pointers to memory block q.
+ * This expression is valid even if p and q are independent memory
+ * blocks and are therefore not normally comparable; the && form returns true
+ * if [p1..p2] lies inside [q1..q2], and false otherwise; the || form returns
+ * true if [p1..p2] lies outside [q1..q2], and false otherwise.
+ *
+ * Within the expression, any ordering of p1, p2, q1, q2 is permissible;
+ * the comparison operators can be any of >, <, <=, >=, provided that
+ * both directions (p > q and p < q) are checked. Additionally the
+ * relational sub-expressions can be negated, eg
+ * (!(q1 < p1) && p2 <= q2) is valid.
+ */
+ private void interpretFourPointerRelation(UnionExp* pue, BinExp e)
+ {
+ assert(e.op == TOK.andAnd || e.op == TOK.orOr);
+
+ /* It can only be an isInside expression, if both e1 and e2 are
+ * directional pointer comparisons.
+ * Note that this check can be made statically; it does not depends on
+ * any runtime values. This allows a JIT implementation to compile a
+ * special AndAndPossiblyInside, keeping the normal AndAnd case efficient.
+ */
+
+ // Save the pointer expressions and the comparison directions,
+ // so we can use them later.
+ Expression p1 = null;
+ Expression p2 = null;
+ Expression p3 = null;
+ Expression p4 = null;
+ int dir1 = isPointerCmpExp(e.e1, &p1, &p2);
+ int dir2 = isPointerCmpExp(e.e2, &p3, &p4);
+ if (dir1 == 0 || dir2 == 0)
+ {
+ result = null;
+ return;
+ }
+
+ //printf("FourPointerRelation %s\n", toChars());
+
+ UnionExp ue1 = void;
+ UnionExp ue2 = void;
+ UnionExp ue3 = void;
+ UnionExp ue4 = void;
+
+ // Evaluate the first two pointers
+ p1 = interpret(&ue1, p1, istate);
+ if (exceptionOrCant(p1))
+ return;
+ p2 = interpret(&ue2, p2, istate);
+ if (exceptionOrCant(p2))
+ return;
+ dinteger_t ofs1, ofs2;
+ Expression agg1 = getAggregateFromPointer(p1, &ofs1);
+ Expression agg2 = getAggregateFromPointer(p2, &ofs2);
+
+ if (!pointToSameMemoryBlock(agg1, agg2) && agg1.op != TOK.null_ && agg2.op != TOK.null_)
+ {
+ // Here it is either CANT_INTERPRET,
+ // or an IsInside comparison returning false.
+ p3 = interpret(&ue3, p3, istate);
+ if (CTFEExp.isCantExp(p3))
+ return;
+ // Note that it is NOT legal for it to throw an exception!
+ Expression except = null;
+ if (exceptionOrCantInterpret(p3))
+ except = p3;
+ else
+ {
+ p4 = interpret(&ue4, p4, istate);
+ if (CTFEExp.isCantExp(p4))
+ {
+ result = p4;
+ return;
+ }
+ if (exceptionOrCantInterpret(p4))
+ except = p4;
+ }
+ if (except)
+ {
+ e.error("comparison `%s` of pointers to unrelated memory blocks remains indeterminate at compile time because exception `%s` was thrown while evaluating `%s`", e.e1.toChars(), except.toChars(), e.e2.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ dinteger_t ofs3, ofs4;
+ Expression agg3 = getAggregateFromPointer(p3, &ofs3);
+ Expression agg4 = getAggregateFromPointer(p4, &ofs4);
+ // The valid cases are:
+ // p1 > p2 && p3 > p4 (same direction, also for < && <)
+ // p1 > p2 && p3 < p4 (different direction, also < && >)
+ // Changing any > into >= doesn't affect the result
+ if ((dir1 == dir2 && pointToSameMemoryBlock(agg1, agg4) && pointToSameMemoryBlock(agg2, agg3)) ||
+ (dir1 != dir2 && pointToSameMemoryBlock(agg1, agg3) && pointToSameMemoryBlock(agg2, agg4)))
+ {
+ // it's a legal two-sided comparison
+ emplaceExp!(IntegerExp)(pue, e.loc, (e.op == TOK.andAnd) ? 0 : 1, e.type);
+ result = pue.exp();
+ return;
+ }
+ // It's an invalid four-pointer comparison. Either the second
+ // comparison is in the same direction as the first, or else
+ // more than two memory blocks are involved (either two independent
+ // invalid comparisons are present, or else agg3 == agg4).
+ e.error("comparison `%s` of pointers to unrelated memory blocks is indeterminate at compile time, even when combined with `%s`.", e.e1.toChars(), e.e2.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ // The first pointer expression didn't need special treatment, so we
+ // we need to interpret the entire expression exactly as a normal && or ||.
+ // This is easy because we haven't evaluated e2 at all yet, and we already
+ // know it will return a bool.
+ // But we mustn't evaluate the pointer expressions in e1 again, in case
+ // they have side-effects.
+ bool nott = false;
+ Expression ex = e.e1;
+ while (1)
+ {
+ if (auto ne = ex.isNotExp())
+ {
+ nott = !nott;
+ ex = ne.e1;
+ }
+ else
+ break;
+ }
+
+ /** Negate relational operator, eg >= becomes <
+ * Params:
+ * op = comparison operator to negate
+ * Returns:
+ * negate operator
+ */
+ static TOK negateRelation(TOK op) pure
+ {
+ switch (op)
+ {
+ case TOK.greaterOrEqual: op = TOK.lessThan; break;
+ case TOK.greaterThan: op = TOK.lessOrEqual; break;
+ case TOK.lessOrEqual: op = TOK.greaterThan; break;
+ case TOK.lessThan: op = TOK.greaterOrEqual; break;
+ default: assert(0);
+ }
+ return op;
+ }
+
+ const TOK cmpop = nott ? negateRelation(ex.op) : ex.op;
+ const cmp = comparePointers(cmpop, agg1, ofs1, agg2, ofs2);
+ // We already know this is a valid comparison.
+ assert(cmp >= 0);
+ if (e.op == TOK.andAnd && cmp == 1 || e.op == TOK.orOr && cmp == 0)
+ {
+ result = interpret(pue, e.e2, istate);
+ return;
+ }
+ emplaceExp!(IntegerExp)(pue, e.loc, (e.op == TOK.andAnd) ? 0 : 1, e.type);
+ result = pue.exp();
+ }
+
+ override void visit(LogicalExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s LogicalExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ // Check for an insidePointer expression, evaluate it if so
+ interpretFourPointerRelation(pue, e);
+ if (result)
+ return;
+
+ UnionExp ue1 = void;
+ result = interpret(&ue1, e.e1, istate);
+ if (exceptionOrCant(result))
+ return;
+
+ bool res;
+ const andand = e.op == TOK.andAnd;
+ if (andand ? result.isBool(false) : isTrueBool(result))
+ res = !andand;
+ else if (andand ? isTrueBool(result) : result.isBool(false))
+ {
+ UnionExp ue2 = void;
+ result = interpret(&ue2, e.e2, istate);
+ if (exceptionOrCant(result))
+ return;
+ if (result.op == TOK.voidExpression)
+ {
+ assert(e.type.ty == Tvoid);
+ result = null;
+ return;
+ }
+ if (result.isBool(false))
+ res = false;
+ else if (isTrueBool(result))
+ res = true;
+ else
+ {
+ e.error("`%s` does not evaluate to a `bool`", result.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ }
+ else
+ {
+ e.error("`%s` cannot be interpreted as a `bool`", result.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ incUsageCtfe(istate, e.e2.loc);
+
+ if (goal != CTFEGoal.Nothing)
+ {
+ if (e.type.equals(Type.tbool))
+ result = IntegerExp.createBool(res);
+ else
+ {
+ emplaceExp!(IntegerExp)(pue, e.loc, res, e.type);
+ result = pue.exp();
+ }
+ }
+ }
+
+
+ // Print a stack trace, starting from callingExp which called fd.
+ // To shorten the stack trace, try to detect recursion.
+ private void showCtfeBackTrace(CallExp callingExp, FuncDeclaration fd)
+ {
+ if (ctfeGlobals.stackTraceCallsToSuppress > 0)
+ {
+ --ctfeGlobals.stackTraceCallsToSuppress;
+ return;
+ }
+ errorSupplemental(callingExp.loc, "called from here: `%s`", callingExp.toChars());
+ // Quit if it's not worth trying to compress the stack trace
+ if (ctfeGlobals.callDepth < 6 || global.params.verbose)
+ return;
+ // Recursion happens if the current function already exists in the call stack.
+ int numToSuppress = 0;
+ int recurseCount = 0;
+ int depthSoFar = 0;
+ InterState* lastRecurse = istate;
+ for (InterState* cur = istate; cur; cur = cur.caller)
+ {
+ if (cur.fd == fd)
+ {
+ ++recurseCount;
+ numToSuppress = depthSoFar;
+ lastRecurse = cur;
+ }
+ ++depthSoFar;
+ }
+ // We need at least three calls to the same function, to make compression worthwhile
+ if (recurseCount < 2)
+ return;
+ // We found a useful recursion. Print all the calls involved in the recursion
+ errorSupplemental(fd.loc, "%d recursive calls to function `%s`", recurseCount, fd.toChars());
+ for (InterState* cur = istate; cur.fd != fd; cur = cur.caller)
+ {
+ errorSupplemental(cur.fd.loc, "recursively called from function `%s`", cur.fd.toChars());
+ }
+ // We probably didn't enter the recursion in this function.
+ // Go deeper to find the real beginning.
+ InterState* cur = istate;
+ while (lastRecurse.caller && cur.fd == lastRecurse.caller.fd)
+ {
+ cur = cur.caller;
+ lastRecurse = lastRecurse.caller;
+ ++numToSuppress;
+ }
+ ctfeGlobals.stackTraceCallsToSuppress = numToSuppress;
+ }
+
+ override void visit(CallExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s CallExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ Expression pthis = null;
+ FuncDeclaration fd = null;
+
+ Expression ecall = interpretRegion(e.e1, istate);
+ if (exceptionOrCant(ecall))
+ return;
+
+ if (auto dve = ecall.isDotVarExp())
+ {
+ // Calling a member function
+ pthis = dve.e1;
+ fd = dve.var.isFuncDeclaration();
+ assert(fd);
+
+ if (auto dte = pthis.isDotTypeExp())
+ pthis = dte.e1;
+ }
+ else if (auto ve = ecall.isVarExp())
+ {
+ fd = ve.var.isFuncDeclaration();
+ assert(fd);
+
+ // If `_d_HookTraceImpl` is found, resolve the underlying hook and replace `e` and `fd` with it.
+ removeHookTraceImpl(e, fd);
+
+ if (fd.ident == Id.__ArrayPostblit || fd.ident == Id.__ArrayDtor)
+ {
+ assert(e.arguments.dim == 1);
+ Expression ea = (*e.arguments)[0];
+ // printf("1 ea = %s %s\n", ea.type.toChars(), ea.toChars());
+ if (auto se = ea.isSliceExp())
+ ea = se.e1;
+ if (auto ce = ea.isCastExp())
+ ea = ce.e1;
+
+ // printf("2 ea = %s, %s %s\n", ea.type.toChars(), Token.toChars(ea.op), ea.toChars());
+ if (ea.op == TOK.variable || ea.op == TOK.symbolOffset)
+ result = getVarExp(e.loc, istate, (cast(SymbolExp)ea).var, CTFEGoal.RValue);
+ else if (auto ae = ea.isAddrExp())
+ result = interpretRegion(ae.e1, istate);
+
+ // https://issues.dlang.org/show_bug.cgi?id=18871
+ // https://issues.dlang.org/show_bug.cgi?id=18819
+ else if (auto ale = ea.isArrayLiteralExp())
+ result = interpretRegion(ale, istate);
+
+ else
+ assert(0);
+ if (CTFEExp.isCantExp(result))
+ return;
+
+ if (fd.ident == Id.__ArrayPostblit)
+ result = evaluatePostblit(istate, result);
+ else
+ result = evaluateDtor(istate, result);
+ if (!result)
+ result = CTFEExp.voidexp;
+ return;
+ }
+ else if (fd.ident == Id._d_arraysetlengthT)
+ {
+ // In expressionsem.d `ea.length = eb;` got lowered to `_d_arraysetlengthT(ea, eb);`.
+ // The following code will rewrite it back to `ea.length = eb` and then interpret that expression.
+ assert(e.arguments.dim == 2);
+
+ Expression ea = (*e.arguments)[0];
+ Expression eb = (*e.arguments)[1];
+
+ auto ale = ctfeEmplaceExp!ArrayLengthExp(e.loc, ea);
+ ale.type = Type.tsize_t;
+ AssignExp ae = ctfeEmplaceExp!AssignExp(e.loc, ale, eb);
+ ae.type = ea.type;
+
+ // if (global.params.verbose)
+ // message("interpret %s =>\n %s", e.toChars(), ae.toChars());
+ result = interpretRegion(ae, istate);
+ return;
+ }
+ }
+ else if (auto soe = ecall.isSymOffExp())
+ {
+ fd = soe.var.isFuncDeclaration();
+ assert(fd && soe.offset == 0);
+ }
+ else if (auto de = ecall.isDelegateExp())
+ {
+ // Calling a delegate
+ fd = de.func;
+ pthis = de.e1;
+
+ // Special handling for: &nestedfunc --> DelegateExp(VarExp(nestedfunc), nestedfunc)
+ if (auto ve = pthis.isVarExp())
+ if (ve.var == fd)
+ pthis = null; // context is not necessary for CTFE
+ }
+ else if (auto fe = ecall.isFuncExp())
+ {
+ // Calling a delegate literal
+ fd = fe.fd;
+ }
+ else
+ {
+ // delegate.funcptr()
+ // others
+ e.error("cannot call `%s` at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (!fd)
+ {
+ e.error("CTFE internal error: cannot evaluate `%s` at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (pthis)
+ {
+ // Member function call
+
+ // Currently this is satisfied because closure is not yet supported.
+ assert(!fd.isNested() || fd.needThis());
+
+ if (pthis.op == TOK.typeid_)
+ {
+ pthis.error("static variable `%s` cannot be read at compile time", pthis.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ assert(pthis);
+
+ if (pthis.op == TOK.null_)
+ {
+ assert(pthis.type.toBasetype().ty == Tclass);
+ e.error("function call through null class reference `%s`", pthis.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ assert(pthis.op == TOK.structLiteral || pthis.op == TOK.classReference || pthis.op == TOK.type);
+
+ if (fd.isVirtual() && !e.directcall)
+ {
+ // Make a virtual function call.
+ // Get the function from the vtable of the original class
+ ClassDeclaration cd = pthis.isClassReferenceExp().originalClass();
+
+ // We can't just use the vtable index to look it up, because
+ // vtables for interfaces don't get populated until the glue layer.
+ fd = cd.findFunc(fd.ident, fd.type.isTypeFunction());
+ assert(fd);
+ }
+ }
+
+ if (fd && fd.semanticRun >= PASS.semantic3done && fd.semantic3Errors)
+ {
+ e.error("CTFE failed because of previous errors in `%s`", fd.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ // Check for built-in functions
+ result = evaluateIfBuiltin(pue, istate, e.loc, fd, e.arguments, pthis);
+ if (result)
+ return;
+
+ if (!fd.fbody)
+ {
+ e.error("`%s` cannot be interpreted at compile time, because it has no available source code", fd.toChars());
+ result = CTFEExp.showcontext;
+ return;
+ }
+
+ result = interpretFunction(pue, fd, istate, e.arguments, pthis);
+ if (result.op == TOK.voidExpression)
+ return;
+ if (!exceptionOrCantInterpret(result))
+ {
+ if (goal != CTFEGoal.LValue) // Peel off CTFE reference if it's unnecessary
+ {
+ if (result == pue.exp())
+ result = pue.copy();
+ result = interpret(pue, result, istate);
+ }
+ }
+ if (!exceptionOrCantInterpret(result))
+ {
+ result = paintTypeOntoLiteral(pue, e.type, result);
+ result.loc = e.loc;
+ }
+ else if (CTFEExp.isCantExp(result) && !global.gag)
+ showCtfeBackTrace(e, fd); // Print a stack trace.
+ }
+
+ override void visit(CommaExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s CommaExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+
+ // If it creates a variable, and there's no context for
+ // the variable to be created in, we need to create one now.
+ InterState istateComma;
+ if (!istate && firstComma(e.e1).op == TOK.declaration)
+ {
+ ctfeGlobals.stack.startFrame(null);
+ istate = &istateComma;
+ }
+
+ void endTempStackFrame()
+ {
+ // If we created a temporary stack frame, end it now.
+ if (istate == &istateComma)
+ ctfeGlobals.stack.endFrame();
+ }
+
+ result = CTFEExp.cantexp;
+
+ // If the comma returns a temporary variable, it needs to be an lvalue
+ // (this is particularly important for struct constructors)
+ if (e.e1.op == TOK.declaration &&
+ e.e2.op == TOK.variable &&
+ e.e1.isDeclarationExp().declaration == e.e2.isVarExp().var &&
+ e.e2.isVarExp().var.storage_class & STC.ctfe)
+ {
+ VarExp ve = e.e2.isVarExp();
+ VarDeclaration v = ve.var.isVarDeclaration();
+ ctfeGlobals.stack.push(v);
+ if (!v._init && !getValue(v))
+ {
+ setValue(v, copyLiteral(v.type.defaultInitLiteral(e.loc)).copy());
+ }
+ if (!getValue(v))
+ {
+ Expression newval = v._init.initializerToExpression();
+ // Bug 4027. Copy constructors are a weird case where the
+ // initializer is a void function (the variable is modified
+ // through a reference parameter instead).
+ newval = interpretRegion(newval, istate);
+ if (exceptionOrCant(newval))
+ return endTempStackFrame();
+ if (newval.op != TOK.voidExpression)
+ {
+ // v isn't necessarily null.
+ setValueWithoutChecking(v, copyLiteral(newval).copy());
+ }
+ }
+ }
+ else
+ {
+ UnionExp ue = void;
+ auto e1 = interpret(&ue, e.e1, istate, CTFEGoal.Nothing);
+ if (exceptionOrCant(e1))
+ return endTempStackFrame();
+ }
+ result = interpret(pue, e.e2, istate, goal);
+ return endTempStackFrame();
+ }
+
+ override void visit(CondExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s CondExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ UnionExp uecond = void;
+ Expression econd;
+ econd = interpret(&uecond, e.econd, istate);
+ if (exceptionOrCant(econd))
+ return;
+
+ if (isPointer(e.econd.type))
+ {
+ if (econd.op != TOK.null_)
+ {
+ econd = IntegerExp.createBool(true);
+ }
+ }
+
+ if (isTrueBool(econd))
+ {
+ result = interpret(pue, e.e1, istate, goal);
+ incUsageCtfe(istate, e.e1.loc);
+ }
+ else if (econd.isBool(false))
+ {
+ result = interpret(pue, e.e2, istate, goal);
+ incUsageCtfe(istate, e.e2.loc);
+ }
+ else
+ {
+ e.error("`%s` does not evaluate to boolean result at compile time", e.econd.toChars());
+ result = CTFEExp.cantexp;
+ }
+ }
+
+ override void visit(ArrayLengthExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s ArrayLengthExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ UnionExp ue1;
+ Expression e1 = interpret(&ue1, e.e1, istate);
+ assert(e1);
+ if (exceptionOrCant(e1))
+ return;
+ if (e1.op != TOK.string_ && e1.op != TOK.arrayLiteral && e1.op != TOK.slice && e1.op != TOK.null_)
+ {
+ e.error("`%s` cannot be evaluated at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ emplaceExp!(IntegerExp)(pue, e.loc, resolveArrayLength(e1), e.type);
+ result = pue.exp();
+ }
+
+ /**
+ * Interpret the vector expression as an array literal.
+ * Params:
+ * pue = non-null pointer to temporary storage that can be used to store the return value
+ * e = Expression to interpret
+ * Returns:
+ * resulting array literal or 'e' if unable to interpret
+ */
+ static Expression interpretVectorToArray(UnionExp* pue, VectorExp e)
+ {
+ if (auto ale = e.e1.isArrayLiteralExp())
+ return ale; // it's already an array literal
+ if (e.e1.op == TOK.int64 || e.e1.op == TOK.float64)
+ {
+ // Convert literal __vector(int) -> __vector([array])
+ auto elements = new Expressions(e.dim);
+ foreach (ref element; *elements)
+ element = copyLiteral(e.e1).copy();
+ auto type = (e.type.ty == Tvector) ? e.type.isTypeVector().basetype : e.type.isTypeSArray();
+ assert(type);
+ emplaceExp!(ArrayLiteralExp)(pue, e.loc, type, elements);
+ auto ale = pue.exp().isArrayLiteralExp();
+ ale.ownedByCtfe = OwnedBy.ctfe;
+ return ale;
+ }
+ return e;
+ }
+
+ override void visit(VectorExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s VectorExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements
+ {
+ result = e;
+ return;
+ }
+ Expression e1 = interpret(pue, e.e1, istate);
+ assert(e1);
+ if (exceptionOrCant(e1))
+ return;
+ if (e1.op != TOK.arrayLiteral && e1.op != TOK.int64 && e1.op != TOK.float64)
+ {
+ e.error("`%s` cannot be evaluated at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (e1 == pue.exp())
+ e1 = pue.copy();
+ emplaceExp!(VectorExp)(pue, e.loc, e1, e.to);
+ auto ve = pue.exp().isVectorExp();
+ ve.type = e.type;
+ ve.dim = e.dim;
+ ve.ownedByCtfe = OwnedBy.ctfe;
+ result = ve;
+ }
+
+ override void visit(VectorArrayExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s VectorArrayExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ Expression e1 = interpret(pue, e.e1, istate);
+ assert(e1);
+ if (exceptionOrCant(e1))
+ return;
+ if (auto ve = e1.isVectorExp())
+ {
+ result = interpretVectorToArray(pue, ve);
+ if (result.op != TOK.vector)
+ return;
+ }
+ e.error("`%s` cannot be evaluated at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ }
+
+ override void visit(DelegatePtrExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s DelegatePtrExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ Expression e1 = interpret(pue, e.e1, istate);
+ assert(e1);
+ if (exceptionOrCant(e1))
+ return;
+ e.error("`%s` cannot be evaluated at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ }
+
+ override void visit(DelegateFuncptrExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s DelegateFuncptrExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ Expression e1 = interpret(pue, e.e1, istate);
+ assert(e1);
+ if (exceptionOrCant(e1))
+ return;
+ e.error("`%s` cannot be evaluated at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ }
+
+ static bool resolveIndexing(IndexExp e, InterState* istate, Expression* pagg, uinteger_t* pidx, bool modify)
+ {
+ assert(e.e1.type.toBasetype().ty != Taarray);
+
+ if (e.e1.type.toBasetype().ty == Tpointer)
+ {
+ // Indexing a pointer. Note that there is no $ in this case.
+ Expression e1 = interpretRegion(e.e1, istate);
+ if (exceptionOrCantInterpret(e1))
+ return false;
+
+ Expression e2 = interpretRegion(e.e2, istate);
+ if (exceptionOrCantInterpret(e2))
+ return false;
+ sinteger_t indx = e2.toInteger();
+
+ dinteger_t ofs;
+ Expression agg = getAggregateFromPointer(e1, &ofs);
+
+ if (agg.op == TOK.null_)
+ {
+ e.error("cannot index through null pointer `%s`", e.e1.toChars());
+ return false;
+ }
+ if (agg.op == TOK.int64)
+ {
+ e.error("cannot index through invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars());
+ return false;
+ }
+ // Pointer to a non-array variable
+ if (agg.op == TOK.symbolOffset)
+ {
+ e.error("mutable variable `%s` cannot be %s at compile time, even through a pointer", cast(char*)(modify ? "modified" : "read"), (cast(SymOffExp)agg).var.toChars());
+ return false;
+ }
+
+ if (agg.op == TOK.arrayLiteral || agg.op == TOK.string_)
+ {
+ dinteger_t len = resolveArrayLength(agg);
+ if (ofs + indx >= len)
+ {
+ e.error("pointer index `[%lld]` exceeds allocated memory block `[0..%lld]`", ofs + indx, len);
+ return false;
+ }
+ }
+ else
+ {
+ if (ofs + indx != 0)
+ {
+ e.error("pointer index `[%lld]` lies outside memory block `[0..1]`", ofs + indx);
+ return false;
+ }
+ }
+ *pagg = agg;
+ *pidx = ofs + indx;
+ return true;
+ }
+
+ Expression e1 = interpretRegion(e.e1, istate);
+ if (exceptionOrCantInterpret(e1))
+ return false;
+ if (e1.op == TOK.null_)
+ {
+ e.error("cannot index null array `%s`", e.e1.toChars());
+ return false;
+ }
+ if (auto ve = e1.isVectorExp())
+ {
+ UnionExp ue = void;
+ e1 = interpretVectorToArray(&ue, ve);
+ e1 = (e1 == ue.exp()) ? ue.copy() : e1;
+ }
+
+ // Set the $ variable, and find the array literal to modify
+ dinteger_t len;
+ if (e1.op == TOK.variable && e1.type.toBasetype().ty == Tsarray)
+ len = e1.type.toBasetype().isTypeSArray().dim.toInteger();
+ else
+ {
+ if (e1.op != TOK.arrayLiteral && e1.op != TOK.string_ && e1.op != TOK.slice && e1.op != TOK.vector)
+ {
+ e.error("cannot determine length of `%s` at compile time", e.e1.toChars());
+ return false;
+ }
+ len = resolveArrayLength(e1);
+ }
+
+ if (e.lengthVar)
+ {
+ Expression dollarExp = ctfeEmplaceExp!IntegerExp(e.loc, len, Type.tsize_t);
+ ctfeGlobals.stack.push(e.lengthVar);
+ setValue(e.lengthVar, dollarExp);
+ }
+ Expression e2 = interpretRegion(e.e2, istate);
+ if (e.lengthVar)
+ ctfeGlobals.stack.pop(e.lengthVar); // $ is defined only inside []
+ if (exceptionOrCantInterpret(e2))
+ return false;
+ if (e2.op != TOK.int64)
+ {
+ e.error("CTFE internal error: non-integral index `[%s]`", e.e2.toChars());
+ return false;
+ }
+
+ if (auto se = e1.isSliceExp())
+ {
+ // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx']
+ uinteger_t index = e2.toInteger();
+ uinteger_t ilwr = se.lwr.toInteger();
+ uinteger_t iupr = se.upr.toInteger();
+
+ if (index > iupr - ilwr)
+ {
+ e.error("index %llu exceeds array length %llu", index, iupr - ilwr);
+ return false;
+ }
+ *pagg = (cast(SliceExp)e1).e1;
+ *pidx = index + ilwr;
+ }
+ else
+ {
+ *pagg = e1;
+ *pidx = e2.toInteger();
+ if (len <= *pidx)
+ {
+ e.error("array index %lld is out of bounds `[0..%lld]`", *pidx, len);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ override void visit(IndexExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s IndexExp::interpret() %s, goal = %d\n", e.loc.toChars(), e.toChars(), goal);
+ }
+ if (e.e1.type.toBasetype().ty == Tpointer)
+ {
+ Expression agg;
+ uinteger_t indexToAccess;
+ if (!resolveIndexing(e, istate, &agg, &indexToAccess, false))
+ {
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (agg.op == TOK.arrayLiteral || agg.op == TOK.string_)
+ {
+ if (goal == CTFEGoal.LValue)
+ {
+ // if we need a reference, IndexExp shouldn't be interpreting
+ // the expression to a value, it should stay as a reference
+ emplaceExp!(IndexExp)(pue, e.loc, agg, ctfeEmplaceExp!IntegerExp(e.e2.loc, indexToAccess, e.e2.type));
+ result = pue.exp();
+ result.type = e.type;
+ return;
+ }
+ result = ctfeIndex(pue, e.loc, e.type, agg, indexToAccess);
+ return;
+ }
+ else
+ {
+ assert(indexToAccess == 0);
+ result = interpretRegion(agg, istate, goal);
+ if (exceptionOrCant(result))
+ return;
+ result = paintTypeOntoLiteral(pue, e.type, result);
+ return;
+ }
+ }
+
+ if (e.e1.type.toBasetype().ty == Taarray)
+ {
+ Expression e1 = interpretRegion(e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ if (e1.op == TOK.null_)
+ {
+ if (goal == CTFEGoal.LValue && e1.type.ty == Taarray && e.modifiable)
+ {
+ assert(0); // does not reach here?
+ }
+ e.error("cannot index null array `%s`", e.e1.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ Expression e2 = interpretRegion(e.e2, istate);
+ if (exceptionOrCant(e2))
+ return;
+
+ if (goal == CTFEGoal.LValue)
+ {
+ // Pointer or reference of a scalar type
+ if (e1 == e.e1 && e2 == e.e2)
+ result = e;
+ else
+ {
+ emplaceExp!(IndexExp)(pue, e.loc, e1, e2);
+ result = pue.exp();
+ result.type = e.type;
+ }
+ return;
+ }
+
+ assert(e1.op == TOK.assocArrayLiteral);
+ UnionExp e2tmp = void;
+ e2 = resolveSlice(e2, &e2tmp);
+ result = findKeyInAA(e.loc, cast(AssocArrayLiteralExp)e1, e2);
+ if (!result)
+ {
+ e.error("key `%s` not found in associative array `%s`", e2.toChars(), e.e1.toChars());
+ result = CTFEExp.cantexp;
+ }
+ return;
+ }
+
+ Expression agg;
+ uinteger_t indexToAccess;
+ if (!resolveIndexing(e, istate, &agg, &indexToAccess, false))
+ {
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ if (goal == CTFEGoal.LValue)
+ {
+ Expression e2 = ctfeEmplaceExp!IntegerExp(e.e2.loc, indexToAccess, Type.tsize_t);
+ emplaceExp!(IndexExp)(pue, e.loc, agg, e2);
+ result = pue.exp();
+ result.type = e.type;
+ return;
+ }
+
+ result = ctfeIndex(pue, e.loc, e.type, agg, indexToAccess);
+ if (exceptionOrCant(result))
+ return;
+ if (result.op == TOK.void_)
+ {
+ e.error("`%s` is used before initialized", e.toChars());
+ errorSupplemental(result.loc, "originally uninitialized here");
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (result == pue.exp())
+ result = result.copy();
+ }
+
+ override void visit(SliceExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s SliceExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ if (e.e1.type.toBasetype().ty == Tpointer)
+ {
+ // Slicing a pointer. Note that there is no $ in this case.
+ Expression e1 = interpretRegion(e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ if (e1.op == TOK.int64)
+ {
+ e.error("cannot slice invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ /* Evaluate lower and upper bounds of slice
+ */
+ Expression lwr = interpretRegion(e.lwr, istate);
+ if (exceptionOrCant(lwr))
+ return;
+ Expression upr = interpretRegion(e.upr, istate);
+ if (exceptionOrCant(upr))
+ return;
+ uinteger_t ilwr = lwr.toInteger();
+ uinteger_t iupr = upr.toInteger();
+
+ dinteger_t ofs;
+ Expression agg = getAggregateFromPointer(e1, &ofs);
+ ilwr += ofs;
+ iupr += ofs;
+ if (agg.op == TOK.null_)
+ {
+ if (iupr == ilwr)
+ {
+ result = ctfeEmplaceExp!NullExp(e.loc);
+ result.type = e.type;
+ return;
+ }
+ e.error("cannot slice null pointer `%s`", e.e1.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (agg.op == TOK.symbolOffset)
+ {
+ e.error("slicing pointers to static variables is not supported in CTFE");
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (agg.op != TOK.arrayLiteral && agg.op != TOK.string_)
+ {
+ e.error("pointer `%s` cannot be sliced at compile time (it does not point to an array)", e.e1.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ assert(agg.op == TOK.arrayLiteral || agg.op == TOK.string_);
+ dinteger_t len = ArrayLength(Type.tsize_t, agg).exp().toInteger();
+ //Type *pointee = ((TypePointer *)agg.type)->next;
+ if (iupr > (len + 1) || iupr < ilwr)
+ {
+ e.error("pointer slice `[%lld..%lld]` exceeds allocated memory block `[0..%lld]`", ilwr, iupr, len);
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (ofs != 0)
+ {
+ lwr = ctfeEmplaceExp!IntegerExp(e.loc, ilwr, lwr.type);
+ upr = ctfeEmplaceExp!IntegerExp(e.loc, iupr, upr.type);
+ }
+ emplaceExp!(SliceExp)(pue, e.loc, agg, lwr, upr);
+ result = pue.exp();
+ result.type = e.type;
+ return;
+ }
+
+ CTFEGoal goal1 = CTFEGoal.RValue;
+ if (goal == CTFEGoal.LValue)
+ {
+ if (e.e1.type.toBasetype().ty == Tsarray)
+ if (auto ve = e.e1.isVarExp())
+ if (auto vd = ve.var.isVarDeclaration())
+ if (vd.storage_class & STC.ref_)
+ goal1 = CTFEGoal.LValue;
+ }
+ Expression e1 = interpret(e.e1, istate, goal1);
+ if (exceptionOrCant(e1))
+ return;
+
+ if (!e.lwr)
+ {
+ result = paintTypeOntoLiteral(pue, e.type, e1);
+ return;
+ }
+ if (auto ve = e1.isVectorExp())
+ {
+ e1 = interpretVectorToArray(pue, ve);
+ e1 = (e1 == pue.exp()) ? pue.copy() : e1;
+ }
+
+ /* Set dollar to the length of the array
+ */
+ uinteger_t dollar;
+ if ((e1.op == TOK.variable || e1.op == TOK.dotVariable) && e1.type.toBasetype().ty == Tsarray)
+ dollar = e1.type.toBasetype().isTypeSArray().dim.toInteger();
+ else
+ {
+ if (e1.op != TOK.arrayLiteral && e1.op != TOK.string_ && e1.op != TOK.null_ && e1.op != TOK.slice && e1.op != TOK.vector)
+ {
+ e.error("cannot determine length of `%s` at compile time", e1.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ dollar = resolveArrayLength(e1);
+ }
+
+ /* Set the $ variable
+ */
+ if (e.lengthVar)
+ {
+ auto dollarExp = ctfeEmplaceExp!IntegerExp(e.loc, dollar, Type.tsize_t);
+ ctfeGlobals.stack.push(e.lengthVar);
+ setValue(e.lengthVar, dollarExp);
+ }
+
+ /* Evaluate lower and upper bounds of slice
+ */
+ Expression lwr = interpretRegion(e.lwr, istate);
+ if (exceptionOrCant(lwr))
+ {
+ if (e.lengthVar)
+ ctfeGlobals.stack.pop(e.lengthVar);
+ return;
+ }
+ Expression upr = interpretRegion(e.upr, istate);
+ if (exceptionOrCant(upr))
+ {
+ if (e.lengthVar)
+ ctfeGlobals.stack.pop(e.lengthVar);
+ return;
+ }
+ if (e.lengthVar)
+ ctfeGlobals.stack.pop(e.lengthVar); // $ is defined only inside [L..U]
+
+ uinteger_t ilwr = lwr.toInteger();
+ uinteger_t iupr = upr.toInteger();
+ if (e1.op == TOK.null_)
+ {
+ if (ilwr == 0 && iupr == 0)
+ {
+ result = e1;
+ return;
+ }
+ e1.error("slice `[%llu..%llu]` is out of bounds", ilwr, iupr);
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (auto se = e1.isSliceExp())
+ {
+ // Simplify slice of slice:
+ // aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr']
+ uinteger_t lo1 = se.lwr.toInteger();
+ uinteger_t up1 = se.upr.toInteger();
+ if (ilwr > iupr || iupr > up1 - lo1)
+ {
+ e.error("slice `[%llu..%llu]` exceeds array bounds `[%llu..%llu]`", ilwr, iupr, lo1, up1);
+ result = CTFEExp.cantexp;
+ return;
+ }
+ ilwr += lo1;
+ iupr += lo1;
+ emplaceExp!(SliceExp)(pue, e.loc, se.e1,
+ ctfeEmplaceExp!IntegerExp(e.loc, ilwr, lwr.type),
+ ctfeEmplaceExp!IntegerExp(e.loc, iupr, upr.type));
+ result = pue.exp();
+ result.type = e.type;
+ return;
+ }
+ if (e1.op == TOK.arrayLiteral || e1.op == TOK.string_)
+ {
+ if (iupr < ilwr || dollar < iupr)
+ {
+ e.error("slice `[%lld..%lld]` exceeds array bounds `[0..%lld]`", ilwr, iupr, dollar);
+ result = CTFEExp.cantexp;
+ return;
+ }
+ }
+ emplaceExp!(SliceExp)(pue, e.loc, e1, lwr, upr);
+ result = pue.exp();
+ result.type = e.type;
+ }
+
+ override void visit(InExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s InExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ Expression e1 = interpretRegion(e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ Expression e2 = interpretRegion(e.e2, istate);
+ if (exceptionOrCant(e2))
+ return;
+ if (e2.op == TOK.null_)
+ {
+ emplaceExp!(NullExp)(pue, e.loc, e.type);
+ result = pue.exp();
+ return;
+ }
+ if (e2.op != TOK.assocArrayLiteral)
+ {
+ e.error("`%s` cannot be interpreted at compile time", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ e1 = resolveSlice(e1);
+ result = findKeyInAA(e.loc, cast(AssocArrayLiteralExp)e2, e1);
+ if (exceptionOrCant(result))
+ return;
+ if (!result)
+ {
+ emplaceExp!(NullExp)(pue, e.loc, e.type);
+ result = pue.exp();
+ }
+ else
+ {
+ // Create a CTFE pointer &aa[index]
+ result = ctfeEmplaceExp!IndexExp(e.loc, e2, e1);
+ result.type = e.type.nextOf();
+ emplaceExp!(AddrExp)(pue, e.loc, result, e.type);
+ result = pue.exp();
+ }
+ }
+
+ override void visit(CatExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s CatExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+
+ UnionExp ue1 = void;
+ Expression e1 = interpret(&ue1, e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+
+ UnionExp ue2 = void;
+ Expression e2 = interpret(&ue2, e.e2, istate);
+ if (exceptionOrCant(e2))
+ return;
+
+ UnionExp e1tmp = void;
+ e1 = resolveSlice(e1, &e1tmp);
+
+ UnionExp e2tmp = void;
+ e2 = resolveSlice(e2, &e2tmp);
+
+ /* e1 and e2 can't go on the stack because of x~[y] and [x]~y will
+ * result in [x,y] and then x or y is on the stack.
+ * But if they are both strings, we can, because it isn't the x~[y] case.
+ */
+ if (!(e1.op == TOK.string_ && e2.op == TOK.string_))
+ {
+ if (e1 == ue1.exp())
+ e1 = ue1.copy();
+ if (e2 == ue2.exp())
+ e2 = ue2.copy();
+ }
+
+ *pue = ctfeCat(e.loc, e.type, e1, e2);
+ result = pue.exp();
+
+ if (CTFEExp.isCantExp(result))
+ {
+ e.error("`%s` cannot be interpreted at compile time", e.toChars());
+ return;
+ }
+ // We know we still own it, because we interpreted both e1 and e2
+ if (auto ale = result.isArrayLiteralExp())
+ {
+ ale.ownedByCtfe = OwnedBy.ctfe;
+
+ // https://issues.dlang.org/show_bug.cgi?id=14686
+ foreach (elem; *ale.elements)
+ {
+ Expression ex = evaluatePostblit(istate, elem);
+ if (exceptionOrCant(ex))
+ return;
+ }
+ }
+ else if (auto se = result.isStringExp())
+ se.ownedByCtfe = OwnedBy.ctfe;
+ }
+
+ override void visit(DeleteExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s DeleteExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ result = interpretRegion(e.e1, istate);
+ if (exceptionOrCant(result))
+ return;
+
+ if (result.op == TOK.null_)
+ {
+ result = CTFEExp.voidexp;
+ return;
+ }
+
+ auto tb = e.e1.type.toBasetype();
+ switch (tb.ty)
+ {
+ case Tclass:
+ if (result.op != TOK.classReference)
+ {
+ e.error("`delete` on invalid class reference `%s`", result.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ auto cre = cast(ClassReferenceExp)result;
+ auto cd = cre.originalClass();
+
+ // Find dtor(s) in inheritance chain
+ do
+ {
+ if (cd.dtor)
+ {
+ result = interpretFunction(pue, cd.dtor, istate, null, cre);
+ if (exceptionOrCant(result))
+ return;
+
+ // Dtors of Non-extern(D) classes use implicit chaining (like structs)
+ import dmd.aggregate : ClassKind;
+ if (cd.classKind != ClassKind.d)
+ break;
+ }
+
+ // Emulate manual chaining as done in rt_finalize2
+ cd = cd.baseClass;
+
+ } while (cd); // Stop after Object
+
+ break;
+
+ case Tpointer:
+ tb = (cast(TypePointer)tb).next.toBasetype();
+ if (tb.ty == Tstruct)
+ {
+ if (result.op != TOK.address ||
+ (cast(AddrExp)result).e1.op != TOK.structLiteral)
+ {
+ e.error("`delete` on invalid struct pointer `%s`", result.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ auto sd = (cast(TypeStruct)tb).sym;
+ auto sle = cast(StructLiteralExp)(cast(AddrExp)result).e1;
+
+ if (sd.dtor)
+ {
+ result = interpretFunction(pue, sd.dtor, istate, null, sle);
+ if (exceptionOrCant(result))
+ return;
+ }
+ }
+ break;
+
+ case Tarray:
+ auto tv = tb.nextOf().baseElemOf();
+ if (tv.ty == Tstruct)
+ {
+ if (result.op != TOK.arrayLiteral)
+ {
+ e.error("`delete` on invalid struct array `%s`", result.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ auto sd = (cast(TypeStruct)tv).sym;
+
+ if (sd.dtor)
+ {
+ auto ale = cast(ArrayLiteralExp)result;
+ foreach (el; *ale.elements)
+ {
+ result = interpretFunction(pue, sd.dtor, istate, null, el);
+ if (exceptionOrCant(result))
+ return;
+ }
+ }
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ result = CTFEExp.voidexp;
+ }
+
+ override void visit(CastExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s CastExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ Expression e1 = interpretRegion(e.e1, istate, goal);
+ if (exceptionOrCant(e1))
+ return;
+ // If the expression has been cast to void, do nothing.
+ if (e.to.ty == Tvoid)
+ {
+ result = CTFEExp.voidexp;
+ return;
+ }
+ if (e.to.ty == Tpointer && e1.op != TOK.null_)
+ {
+ Type pointee = (cast(TypePointer)e.type).next;
+ // Implement special cases of normally-unsafe casts
+ if (e1.op == TOK.int64)
+ {
+ // Happens with Windows HANDLEs, for example.
+ result = paintTypeOntoLiteral(pue, e.to, e1);
+ return;
+ }
+
+ bool castToSarrayPointer = false;
+ bool castBackFromVoid = false;
+ if (e1.type.ty == Tarray || e1.type.ty == Tsarray || e1.type.ty == Tpointer)
+ {
+ // Check for unsupported type painting operations
+ // For slices, we need the type being sliced,
+ // since it may have already been type painted
+ Type elemtype = e1.type.nextOf();
+ if (auto se = e1.isSliceExp())
+ elemtype = se.e1.type.nextOf();
+
+ // Allow casts from X* to void *, and X** to void** for any X.
+ // But don't allow cast from X* to void**.
+ // So, we strip all matching * from source and target to find X.
+ // Allow casts to X* from void* only if the 'void' was originally an X;
+ // we check this later on.
+ Type ultimatePointee = pointee;
+ Type ultimateSrc = elemtype;
+ while (ultimatePointee.ty == Tpointer && ultimateSrc.ty == Tpointer)
+ {
+ ultimatePointee = ultimatePointee.nextOf();
+ ultimateSrc = ultimateSrc.nextOf();
+ }
+ if (ultimatePointee.ty == Tsarray && ultimatePointee.nextOf().equivalent(ultimateSrc))
+ {
+ castToSarrayPointer = true;
+ }
+ else if (ultimatePointee.ty != Tvoid && ultimateSrc.ty != Tvoid && !isSafePointerCast(elemtype, pointee))
+ {
+ e.error("reinterpreting cast from `%s*` to `%s*` is not supported in CTFE", elemtype.toChars(), pointee.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (ultimateSrc.ty == Tvoid)
+ castBackFromVoid = true;
+ }
+
+ if (auto se = e1.isSliceExp())
+ {
+ if (se.e1.op == TOK.null_)
+ {
+ result = paintTypeOntoLiteral(pue, e.type, se.e1);
+ return;
+ }
+ // Create a CTFE pointer &aggregate[1..2]
+ auto ei = ctfeEmplaceExp!IndexExp(e.loc, se.e1, se.lwr);
+ ei.type = e.type.nextOf();
+ emplaceExp!(AddrExp)(pue, e.loc, ei, e.type);
+ result = pue.exp();
+ return;
+ }
+ if (e1.op == TOK.arrayLiteral || e1.op == TOK.string_)
+ {
+ // Create a CTFE pointer &[1,2,3][0] or &"abc"[0]
+ auto ei = ctfeEmplaceExp!IndexExp(e.loc, e1, ctfeEmplaceExp!IntegerExp(e.loc, 0, Type.tsize_t));
+ ei.type = e.type.nextOf();
+ emplaceExp!(AddrExp)(pue, e.loc, ei, e.type);
+ result = pue.exp();
+ return;
+ }
+ if (e1.op == TOK.index && !(cast(IndexExp)e1).e1.type.equals(e1.type))
+ {
+ // type painting operation
+ IndexExp ie = cast(IndexExp)e1;
+ if (castBackFromVoid)
+ {
+ // get the original type. For strings, it's just the type...
+ Type origType = ie.e1.type.nextOf();
+ // ..but for arrays of type void*, it's the type of the element
+ if (ie.e1.op == TOK.arrayLiteral && ie.e2.op == TOK.int64)
+ {
+ ArrayLiteralExp ale = cast(ArrayLiteralExp)ie.e1;
+ const indx = cast(size_t)ie.e2.toInteger();
+ if (indx < ale.elements.dim)
+ {
+ if (Expression xx = (*ale.elements)[indx])
+ {
+ if (auto iex = xx.isIndexExp())
+ origType = iex.e1.type.nextOf();
+ else if (auto ae = xx.isAddrExp())
+ origType = ae.e1.type;
+ else if (auto ve = xx.isVarExp())
+ origType = ve.var.type;
+ }
+ }
+ }
+ if (!isSafePointerCast(origType, pointee))
+ {
+ e.error("using `void*` to reinterpret cast from `%s*` to `%s*` is not supported in CTFE", origType.toChars(), pointee.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ }
+ emplaceExp!(IndexExp)(pue, e1.loc, ie.e1, ie.e2);
+ result = pue.exp();
+ result.type = e.type;
+ return;
+ }
+
+ if (auto ae = e1.isAddrExp())
+ {
+ Type origType = ae.e1.type;
+ if (isSafePointerCast(origType, pointee))
+ {
+ emplaceExp!(AddrExp)(pue, e.loc, ae.e1, e.type);
+ result = pue.exp();
+ return;
+ }
+
+ if (castToSarrayPointer && pointee.toBasetype().ty == Tsarray && ae.e1.op == TOK.index)
+ {
+ // &val[idx]
+ dinteger_t dim = (cast(TypeSArray)pointee.toBasetype()).dim.toInteger();
+ IndexExp ie = cast(IndexExp)ae.e1;
+ Expression lwr = ie.e2;
+ Expression upr = ctfeEmplaceExp!IntegerExp(ie.e2.loc, ie.e2.toInteger() + dim, Type.tsize_t);
+
+ // Create a CTFE pointer &val[idx..idx+dim]
+ auto er = ctfeEmplaceExp!SliceExp(e.loc, ie.e1, lwr, upr);
+ er.type = pointee;
+ emplaceExp!(AddrExp)(pue, e.loc, er, e.type);
+ result = pue.exp();
+ return;
+ }
+ }
+
+ if (e1.op == TOK.variable || e1.op == TOK.symbolOffset)
+ {
+ // type painting operation
+ Type origType = (cast(SymbolExp)e1).var.type;
+ if (castBackFromVoid && !isSafePointerCast(origType, pointee))
+ {
+ e.error("using `void*` to reinterpret cast from `%s*` to `%s*` is not supported in CTFE", origType.toChars(), pointee.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (auto ve = e1.isVarExp())
+ emplaceExp!(VarExp)(pue, e.loc, ve.var);
+ else
+ emplaceExp!(SymOffExp)(pue, e.loc, (cast(SymOffExp)e1).var, (cast(SymOffExp)e1).offset);
+ result = pue.exp();
+ result.type = e.to;
+ return;
+ }
+
+ // Check if we have a null pointer (eg, inside a struct)
+ e1 = interpretRegion(e1, istate);
+ if (e1.op != TOK.null_)
+ {
+ e.error("pointer cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ }
+ if (e.to.ty == Tsarray && e.e1.type.ty == Tvector)
+ {
+ // Special handling for: cast(float[4])__vector([w, x, y, z])
+ e1 = interpretRegion(e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ assert(e1.op == TOK.vector);
+ e1 = interpretVectorToArray(pue, e1.isVectorExp());
+ }
+ if (e.to.ty == Tarray && e1.op == TOK.slice)
+ {
+ // Note that the slice may be void[], so when checking for dangerous
+ // casts, we need to use the original type, which is se.e1.
+ SliceExp se = cast(SliceExp)e1;
+ if (!isSafePointerCast(se.e1.type.nextOf(), e.to.nextOf()))
+ {
+ e.error("array cast from `%s` to `%s` is not supported at compile time", se.e1.type.toChars(), e.to.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ emplaceExp!(SliceExp)(pue, e1.loc, se.e1, se.lwr, se.upr);
+ result = pue.exp();
+ result.type = e.to;
+ return;
+ }
+ // Disallow array type painting, except for conversions between built-in
+ // types of identical size.
+ if ((e.to.ty == Tsarray || e.to.ty == Tarray) && (e1.type.ty == Tsarray || e1.type.ty == Tarray) && !isSafePointerCast(e1.type.nextOf(), e.to.nextOf()))
+ {
+ e.error("array cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (e.to.ty == Tsarray)
+ e1 = resolveSlice(e1);
+ if (e.to.toBasetype().ty == Tbool && e1.type.ty == Tpointer)
+ {
+ emplaceExp!(IntegerExp)(pue, e.loc, e1.op != TOK.null_, e.to);
+ result = pue.exp();
+ return;
+ }
+ result = ctfeCast(pue, e.loc, e.type, e.to, e1);
+ }
+
+ override void visit(AssertExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s AssertExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ Expression e1 = interpret(pue, e.e1, istate);
+ if (exceptionOrCant(e1))
+ return;
+ if (isTrueBool(e1))
+ {
+ }
+ else if (e1.isBool(false))
+ {
+ if (e.msg)
+ {
+ UnionExp ue = void;
+ result = interpret(&ue, e.msg, istate);
+ if (exceptionOrCant(result))
+ return;
+ e.error("`%s`", result.toChars());
+ }
+ else
+ e.error("`%s` failed", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ else
+ {
+ e.error("`%s` is not a compile time boolean expression", e1.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ result = e1;
+ return;
+ }
+
+ override void visit(PtrExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s PtrExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ // Check for int<->float and long<->double casts.
+ if (auto soe1 = e.e1.isSymOffExp())
+ if (soe1.offset == 0 && soe1.var.isVarDeclaration() && isFloatIntPaint(e.type, soe1.var.type))
+ {
+ // *(cast(int*)&v), where v is a float variable
+ result = paintFloatInt(pue, getVarExp(e.loc, istate, soe1.var, CTFEGoal.RValue), e.type);
+ return;
+ }
+
+ if (auto ce1 = e.e1.isCastExp())
+ if (auto ae11 = ce1.e1.isAddrExp())
+ {
+ // *(cast(int*)&x), where x is a float expression
+ Expression x = ae11.e1;
+ if (isFloatIntPaint(e.type, x.type))
+ {
+ result = paintFloatInt(pue, interpretRegion(x, istate), e.type);
+ return;
+ }
+ }
+
+ // Constant fold *(&structliteral + offset)
+ if (auto ae = e.e1.isAddExp())
+ {
+ if (ae.e1.op == TOK.address && ae.e2.op == TOK.int64)
+ {
+ AddrExp ade = cast(AddrExp)ae.e1;
+ Expression ex = interpretRegion(ade.e1, istate);
+ if (exceptionOrCant(ex))
+ return;
+ if (auto se = ex.isStructLiteralExp())
+ {
+ dinteger_t offset = ae.e2.toInteger();
+ result = se.getField(e.type, cast(uint)offset);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ // It's possible we have an array bounds error. We need to make sure it
+ // errors with this line number, not the one where the pointer was set.
+ result = interpretRegion(e.e1, istate);
+ if (exceptionOrCant(result))
+ return;
+
+ if (result.op == TOK.function_)
+ return;
+ if (auto soe = result.isSymOffExp())
+ {
+ if (soe.offset == 0 && soe.var.isFuncDeclaration())
+ return;
+ e.error("cannot dereference pointer to static variable `%s` at compile time", soe.var.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ if (result.isStringExp())
+ return;
+
+ if (result.op != TOK.address)
+ {
+ if (result.op == TOK.null_)
+ e.error("dereference of null pointer `%s`", e.e1.toChars());
+ else
+ e.error("dereference of invalid pointer `%s`", result.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ // *(&x) ==> x
+ result = (cast(AddrExp)result).e1;
+
+ if (result.op == TOK.slice && e.type.toBasetype().ty == Tsarray)
+ {
+ /* aggr[lwr..upr]
+ * upr may exceed the upper boundary of aggr, but the check is deferred
+ * until those out-of-bounds elements will be touched.
+ */
+ return;
+ }
+ result = interpret(pue, result, istate, goal);
+ if (exceptionOrCant(result))
+ return;
+
+ debug (LOG)
+ {
+ if (CTFEExp.isCantExp(result))
+ printf("PtrExp::interpret() %s = CTFEExp::cantexp\n", e.toChars());
+ }
+ }
+
+ override void visit(DotVarExp e)
+ {
+ void notImplementedYet()
+ {
+ e.error("`%s.%s` is not yet implemented at compile time", e.e1.toChars(), e.var.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ debug (LOG)
+ {
+ printf("%s DotVarExp::interpret() %s, goal = %d\n", e.loc.toChars(), e.toChars(), goal);
+ }
+ Expression ex = interpretRegion(e.e1, istate);
+ if (exceptionOrCant(ex))
+ return;
+
+ if (FuncDeclaration f = e.var.isFuncDeclaration())
+ {
+ if (ex == e.e1)
+ result = e; // optimize: reuse this CTFE reference
+ else
+ {
+ emplaceExp!(DotVarExp)(pue, e.loc, ex, f, false);
+ result = pue.exp();
+ result.type = e.type;
+ }
+ return;
+ }
+
+ VarDeclaration v = e.var.isVarDeclaration();
+ if (!v)
+ {
+ e.error("CTFE internal error: `%s`", e.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ if (ex.op == TOK.null_)
+ {
+ if (ex.type.toBasetype().ty == Tclass)
+ e.error("class `%s` is `null` and cannot be dereferenced", e.e1.toChars());
+ else
+ e.error("CTFE internal error: null this `%s`", e.e1.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ StructLiteralExp se;
+ int i;
+
+ if (ex.op != TOK.structLiteral && ex.op != TOK.classReference && ex.op != TOK.typeid_)
+ {
+ return notImplementedYet();
+ }
+
+ // We can't use getField, because it makes a copy
+ if (ex.op == TOK.classReference)
+ {
+ se = (cast(ClassReferenceExp)ex).value;
+ i = (cast(ClassReferenceExp)ex).findFieldIndexByName(v);
+ }
+ else if (ex.op == TOK.typeid_)
+ {
+ if (v.ident == Identifier.idPool("name"))
+ {
+ if (auto t = isType(ex.isTypeidExp().obj))
+ {
+ auto sym = t.toDsymbol(null);
+ if (auto ident = (sym ? sym.ident : null))
+ {
+ result = new StringExp(e.loc, ident.toString());
+ result.expressionSemantic(null);
+ return ;
+ }
+ }
+ }
+ return notImplementedYet();
+ }
+ else
+ {
+ se = cast(StructLiteralExp)ex;
+ i = findFieldIndexByName(se.sd, v);
+ }
+ if (i == -1)
+ {
+ e.error("couldn't find field `%s` of type `%s` in `%s`", v.toChars(), e.type.toChars(), se.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19897
+ // https://issues.dlang.org/show_bug.cgi?id=20710
+ // Zero-elements fields don't have an initializer. See: scrubArray function
+ if ((*se.elements)[i] is null)
+ (*se.elements)[i] = voidInitLiteral(e.type, v).copy();
+
+ if (goal == CTFEGoal.LValue)
+ {
+ // just return the (simplified) dotvar expression as a CTFE reference
+ if (e.e1 == ex)
+ result = e;
+ else
+ {
+ emplaceExp!(DotVarExp)(pue, e.loc, ex, v);
+ result = pue.exp();
+ result.type = e.type;
+ }
+ return;
+ }
+
+ result = (*se.elements)[i];
+ if (!result)
+ {
+ e.error("Internal Compiler Error: null field `%s`", v.toChars());
+ result = CTFEExp.cantexp;
+ return;
+ }
+ if (auto vie = result.isVoidInitExp())
+ {
+ const s = vie.var.toChars();
+ if (v.overlapped)
+ {
+ e.error("reinterpretation through overlapped field `%s` is not allowed in CTFE", s);
+ result = CTFEExp.cantexp;
+ return;
+ }
+ e.error("cannot read uninitialized variable `%s` in CTFE", s);
+ result = CTFEExp.cantexp;
+ return;
+ }
+
+ if (v.type.ty != result.type.ty && v.type.ty == Tsarray)
+ {
+ // Block assignment from inside struct literals
+ auto tsa = cast(TypeSArray)v.type;
+ auto len = cast(size_t)tsa.dim.toInteger();
+ UnionExp ue = void;
+ result = createBlockDuplicatedArrayLiteral(&ue, ex.loc, v.type, ex, len);
+ if (result == ue.exp())
+ result = ue.copy();
+ (*se.elements)[i] = result;
+ }
+ debug (LOG)
+ {
+ if (CTFEExp.isCantExp(result))
+ printf("DotVarExp::interpret() %s = CTFEExp::cantexp\n", e.toChars());
+ }
+ }
+
+ override void visit(RemoveExp e)
+ {
+ debug (LOG)
+ {
+ printf("%s RemoveExp::interpret() %s\n", e.loc.toChars(), e.toChars());
+ }
+ Expression agg = interpret(e.e1, istate);
+ if (exceptionOrCant(agg))
+ return;
+ Expression index = interpret(e.e2, istate);
+ if (exceptionOrCant(index))
+ return;
+ if (agg.op == TOK.null_)
+ {
+ result = CTFEExp.voidexp;
+ return;
+ }
+
+ AssocArrayLiteralExp aae = agg.isAssocArrayLiteralExp();
+ Expressions* keysx = aae.keys;
+ Expressions* valuesx = aae.values;
+ size_t removed = 0;
+ foreach (j, evalue; *valuesx)
+ {
+ Expression ekey = (*keysx)[j];
+ int eq = ctfeEqual(e.loc, TOK.equal, ekey, index);
+ if (eq)
+ ++removed;
+ else if (removed != 0)
+ {
+ (*keysx)[j - removed] = ekey;
+ (*valuesx)[j - removed] = evalue;
+ }
+ }
+ valuesx.dim = valuesx.dim - removed;
+ keysx.dim = keysx.dim - removed;
+ result = IntegerExp.createBool(removed != 0);
+ }
+
+ override void visit(ClassReferenceExp e)
+ {
+ //printf("ClassReferenceExp::interpret() %s\n", e.value.toChars());
+ result = e;
+ }
+
+ override void visit(VoidInitExp e)
+ {
+ e.error("CTFE internal error: trying to read uninitialized variable");
+ assert(0);
+ }
+
+ override void visit(ThrownExceptionExp e)
+ {
+ assert(0); // This should never be interpreted
+ }
+}
+
+/********************************************
+ * Interpret the expression.
+ * Params:
+ * pue = non-null pointer to temporary storage that can be used to store the return value
+ * e = Expression to interpret
+ * istate = context
+ * goal = what the result will be used for
+ * Returns:
+ * resulting expression
+ */
+
+Expression interpret(UnionExp* pue, Expression e, InterState* istate, CTFEGoal goal = CTFEGoal.RValue)
+{
+ if (!e)
+ return null;
+ scope Interpreter v = new Interpreter(pue, istate, goal);
+ e.accept(v);
+ Expression ex = v.result;
+ assert(goal == CTFEGoal.Nothing || ex !is null);
+ return ex;
+}
+
+///
+Expression interpret(Expression e, InterState* istate, CTFEGoal goal = CTFEGoal.RValue)
+{
+ UnionExp ue = void;
+ auto result = interpret(&ue, e, istate, goal);
+ if (result == ue.exp())
+ result = ue.copy();
+ return result;
+}
+
+/*****************************
+ * Same as interpret(), but return result allocated in Region.
+ * Params:
+ * e = Expression to interpret
+ * istate = context
+ * goal = what the result will be used for
+ * Returns:
+ * resulting expression
+ */
+Expression interpretRegion(Expression e, InterState* istate, CTFEGoal goal = CTFEGoal.RValue)
+{
+ UnionExp ue = void;
+ auto result = interpret(&ue, e, istate, goal);
+ auto uexp = ue.exp();
+ if (result != uexp)
+ return result;
+ if (mem.isGCEnabled)
+ return ue.copy();
+
+ // mimicking UnionExp.copy, but with region allocation
+ switch (uexp.op)
+ {
+ case TOK.cantExpression: return CTFEExp.cantexp;
+ case TOK.voidExpression: return CTFEExp.voidexp;
+ case TOK.break_: return CTFEExp.breakexp;
+ case TOK.continue_: return CTFEExp.continueexp;
+ case TOK.goto_: return CTFEExp.gotoexp;
+ default: break;
+ }
+ auto p = ctfeGlobals.region.malloc(uexp.size);
+ return cast(Expression)memcpy(p, cast(void*)uexp, uexp.size);
+}
+
+/***********************************
+ * Interpret the statement.
+ * Params:
+ * pue = non-null pointer to temporary storage that can be used to store the return value
+ * s = Statement to interpret
+ * istate = context
+ * Returns:
+ * NULL continue to next statement
+ * TOK.cantExpression cannot interpret statement at compile time
+ * !NULL expression from return statement, or thrown exception
+ */
+Expression interpret(UnionExp* pue, Statement s, InterState* istate)
+{
+ if (!s)
+ return null;
+ scope Interpreter v = new Interpreter(pue, istate, CTFEGoal.Nothing);
+ s.accept(v);
+ return v.result;
+}
+
+///
+Expression interpret(Statement s, InterState* istate)
+{
+ UnionExp ue = void;
+ auto result = interpret(&ue, s, istate);
+ if (result == ue.exp())
+ result = ue.copy();
+ return result;
+}
+
+/**
+ * All results destined for use outside of CTFE need to have their CTFE-specific
+ * features removed.
+ * In particular,
+ * 1. all slices must be resolved.
+ * 2. all .ownedByCtfe set to OwnedBy.code
+ */
+private Expression scrubReturnValue(const ref Loc loc, Expression e)
+{
+ /* Returns: true if e is void,
+ * or is an array literal or struct literal of void elements.
+ */
+ static bool isVoid(const Expression e, bool checkArrayType = false) pure
+ {
+ if (e.op == TOK.void_)
+ return true;
+
+ static bool isEntirelyVoid(const Expressions* elems)
+ {
+ foreach (e; *elems)
+ {
+ // It can be NULL for performance reasons,
+ // see StructLiteralExp::interpret().
+ if (e && !isVoid(e))
+ return false;
+ }
+ return true;
+ }
+
+ if (auto sle = e.isStructLiteralExp())
+ return isEntirelyVoid(sle.elements);
+
+ if (checkArrayType && e.type.ty != Tsarray)
+ return false;
+
+ if (auto ale = e.isArrayLiteralExp())
+ return isEntirelyVoid(ale.elements);
+
+ return false;
+ }
+
+
+ /* Scrub all elements of elems[].
+ * Returns: null for success, error Expression for failure
+ */
+ Expression scrubArray(Expressions* elems, bool structlit = false)
+ {
+ foreach (ref e; *elems)
+ {
+ // It can be NULL for performance reasons,
+ // see StructLiteralExp::interpret().
+ if (!e)
+ continue;
+
+ // A struct .init may contain void members.
+ // Static array members are a weird special case https://issues.dlang.org/show_bug.cgi?id=10994
+ if (structlit && isVoid(e, true))
+ {
+ e = null;
+ }
+ else
+ {
+ e = scrubReturnValue(loc, e);
+ if (CTFEExp.isCantExp(e) || e.op == TOK.error)
+ return e;
+ }
+ }
+ return null;
+ }
+
+ Expression scrubSE(StructLiteralExp sle)
+ {
+ sle.ownedByCtfe = OwnedBy.code;
+ if (!(sle.stageflags & stageScrub))
+ {
+ const old = sle.stageflags;
+ sle.stageflags |= stageScrub; // prevent infinite recursion
+ if (auto ex = scrubArray(sle.elements, true))
+ return ex;
+ sle.stageflags = old;
+ }
+ return null;
+ }
+
+ if (e.op == TOK.classReference)
+ {
+ StructLiteralExp sle = (cast(ClassReferenceExp)e).value;
+ if (auto ex = scrubSE(sle))
+ return ex;
+ }
+ else if (auto vie = e.isVoidInitExp())
+ {
+ error(loc, "uninitialized variable `%s` cannot be returned from CTFE", vie.var.toChars());
+ return ErrorExp.get();
+ }
+
+ e = resolveSlice(e);
+
+ if (auto sle = e.isStructLiteralExp())
+ {
+ if (auto ex = scrubSE(sle))
+ return ex;
+ }
+ else if (auto se = e.isStringExp())
+ {
+ se.ownedByCtfe = OwnedBy.code;
+ }
+ else if (auto ale = e.isArrayLiteralExp())
+ {
+ ale.ownedByCtfe = OwnedBy.code;
+ if (auto ex = scrubArray(ale.elements))
+ return ex;
+ }
+ else if (auto aae = e.isAssocArrayLiteralExp())
+ {
+ aae.ownedByCtfe = OwnedBy.code;
+ if (auto ex = scrubArray(aae.keys))
+ return ex;
+ if (auto ex = scrubArray(aae.values))
+ return ex;
+ aae.type = toBuiltinAAType(aae.type);
+ }
+ else if (auto ve = e.isVectorExp())
+ {
+ ve.ownedByCtfe = OwnedBy.code;
+ if (auto ale = ve.e1.isArrayLiteralExp())
+ {
+ ale.ownedByCtfe = OwnedBy.code;
+ if (auto ex = scrubArray(ale.elements))
+ return ex;
+ }
+ }
+ return e;
+}
+
+/**************************************
+ * Transitively set all .ownedByCtfe to OwnedBy.cache
+ */
+private Expression scrubCacheValue(Expression e)
+{
+ if (!e)
+ return e;
+
+ Expression scrubArrayCache(Expressions* elems)
+ {
+ foreach (ref e; *elems)
+ e = scrubCacheValue(e);
+ return null;
+ }
+
+ Expression scrubSE(StructLiteralExp sle)
+ {
+ sle.ownedByCtfe = OwnedBy.cache;
+ if (!(sle.stageflags & stageScrub))
+ {
+ const old = sle.stageflags;
+ sle.stageflags |= stageScrub; // prevent infinite recursion
+ if (auto ex = scrubArrayCache(sle.elements))
+ return ex;
+ sle.stageflags = old;
+ }
+ return null;
+ }
+
+ if (e.op == TOK.classReference)
+ {
+ if (auto ex = scrubSE((cast(ClassReferenceExp)e).value))
+ return ex;
+ }
+ else if (auto sle = e.isStructLiteralExp())
+ {
+ if (auto ex = scrubSE(sle))
+ return ex;
+ }
+ else if (auto se = e.isStringExp())
+ {
+ se.ownedByCtfe = OwnedBy.cache;
+ }
+ else if (auto ale = e.isArrayLiteralExp())
+ {
+ ale.ownedByCtfe = OwnedBy.cache;
+ if (Expression ex = scrubArrayCache(ale.elements))
+ return ex;
+ }
+ else if (auto aae = e.isAssocArrayLiteralExp())
+ {
+ aae.ownedByCtfe = OwnedBy.cache;
+ if (auto ex = scrubArrayCache(aae.keys))
+ return ex;
+ if (auto ex = scrubArrayCache(aae.values))
+ return ex;
+ }
+ else if (auto ve = e.isVectorExp())
+ {
+ ve.ownedByCtfe = OwnedBy.cache;
+ if (auto ale = ve.e1.isArrayLiteralExp())
+ {
+ ale.ownedByCtfe = OwnedBy.cache;
+ if (auto ex = scrubArrayCache(ale.elements))
+ return ex;
+ }
+ }
+ return e;
+}
+
+/********************************************
+ * Transitively replace all Expressions allocated in ctfeGlobals.region
+ * with Mem owned copies.
+ * Params:
+ * e = possible ctfeGlobals.region owned expression
+ * Returns:
+ * Mem owned expression
+ */
+private Expression copyRegionExp(Expression e)
+{
+ if (!e)
+ return e;
+
+ static void copyArray(Expressions* elems)
+ {
+ foreach (ref e; *elems)
+ {
+ auto ex = e;
+ e = null;
+ e = copyRegionExp(ex);
+ }
+ }
+
+ static void copySE(StructLiteralExp sle)
+ {
+ if (1 || !(sle.stageflags & stageScrub))
+ {
+ const old = sle.stageflags;
+ sle.stageflags |= stageScrub; // prevent infinite recursion
+ copyArray(sle.elements);
+ sle.stageflags = old;
+ }
+ }
+
+ switch (e.op)
+ {
+ case TOK.classReference:
+ {
+ auto cre = e.isClassReferenceExp();
+ cre.value = copyRegionExp(cre.value).isStructLiteralExp();
+ break;
+ }
+
+ case TOK.structLiteral:
+ {
+ auto sle = e.isStructLiteralExp();
+
+ /* The following is to take care of updating sle.origin correctly,
+ * which may have multiple objects pointing to it.
+ */
+ if (sle.isOriginal && !ctfeGlobals.region.contains(cast(void*)sle.origin))
+ {
+ /* This means sle has already been moved out of the region,
+ * and sle.origin is the new location.
+ */
+ return sle.origin;
+ }
+ copySE(sle);
+ sle.isOriginal = sle is sle.origin;
+
+ auto slec = ctfeGlobals.region.contains(cast(void*)e)
+ ? e.copy().isStructLiteralExp() // move sle out of region to slec
+ : sle;
+
+ if (ctfeGlobals.region.contains(cast(void*)sle.origin))
+ {
+ auto sleo = sle.origin == sle ? slec : sle.origin.copy().isStructLiteralExp();
+ sle.origin = sleo;
+ slec.origin = sleo;
+ }
+ return slec;
+ }
+
+ case TOK.arrayLiteral:
+ {
+ auto ale = e.isArrayLiteralExp();
+ ale.basis = copyRegionExp(ale.basis);
+ copyArray(ale.elements);
+ break;
+ }
+
+ case TOK.assocArrayLiteral:
+ copyArray(e.isAssocArrayLiteralExp().keys);
+ copyArray(e.isAssocArrayLiteralExp().values);
+ break;
+
+ case TOK.slice:
+ {
+ auto se = e.isSliceExp();
+ se.e1 = copyRegionExp(se.e1);
+ se.upr = copyRegionExp(se.upr);
+ se.lwr = copyRegionExp(se.lwr);
+ break;
+ }
+
+ case TOK.tuple:
+ {
+ auto te = e.isTupleExp();
+ te.e0 = copyRegionExp(te.e0);
+ copyArray(te.exps);
+ break;
+ }
+
+ case TOK.address:
+ case TOK.delegate_:
+ case TOK.vector:
+ case TOK.dotVariable:
+ {
+ UnaExp ue = cast(UnaExp)e;
+ ue.e1 = copyRegionExp(ue.e1);
+ break;
+ }
+
+ case TOK.index:
+ {
+ BinExp be = cast(BinExp)e;
+ be.e1 = copyRegionExp(be.e1);
+ be.e2 = copyRegionExp(be.e2);
+ break;
+ }
+
+ case TOK.this_:
+ case TOK.super_:
+ case TOK.variable:
+ case TOK.type:
+ case TOK.function_:
+ case TOK.typeid_:
+ case TOK.string_:
+ case TOK.int64:
+ case TOK.error:
+ case TOK.float64:
+ case TOK.complex80:
+ case TOK.null_:
+ case TOK.void_:
+ case TOK.symbolOffset:
+ case TOK.char_:
+ break;
+
+ case TOK.cantExpression:
+ case TOK.voidExpression:
+ case TOK.showCtfeContext:
+ return e;
+
+ default:
+ printf("e: %s, %s\n", Token.toChars(e.op), e.toChars());
+ assert(0);
+ }
+
+ if (ctfeGlobals.region.contains(cast(void*)e))
+ {
+ return e.copy();
+ }
+ return e;
+}
+
+/******************************* Special Functions ***************************/
+
+private Expression interpret_length(UnionExp* pue, InterState* istate, Expression earg)
+{
+ //printf("interpret_length()\n");
+ earg = interpret(pue, earg, istate);
+ if (exceptionOrCantInterpret(earg))
+ return earg;
+ dinteger_t len = 0;
+ if (auto aae = earg.isAssocArrayLiteralExp())
+ len = aae.keys.dim;
+ else
+ assert(earg.op == TOK.null_);
+ emplaceExp!(IntegerExp)(pue, earg.loc, len, Type.tsize_t);
+ return pue.exp();
+}
+
+private Expression interpret_keys(UnionExp* pue, InterState* istate, Expression earg, Type returnType)
+{
+ debug (LOG)
+ {
+ printf("interpret_keys()\n");
+ }
+ earg = interpret(pue, earg, istate);
+ if (exceptionOrCantInterpret(earg))
+ return earg;
+ if (earg.op == TOK.null_)
+ {
+ emplaceExp!(NullExp)(pue, earg.loc, earg.type);
+ return pue.exp();
+ }
+ if (earg.op != TOK.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
+ return null;
+ AssocArrayLiteralExp aae = earg.isAssocArrayLiteralExp();
+ auto ae = ctfeEmplaceExp!ArrayLiteralExp(aae.loc, returnType, aae.keys);
+ ae.ownedByCtfe = aae.ownedByCtfe;
+ *pue = copyLiteral(ae);
+ return pue.exp();
+}
+
+private Expression interpret_values(UnionExp* pue, InterState* istate, Expression earg, Type returnType)
+{
+ debug (LOG)
+ {
+ printf("interpret_values()\n");
+ }
+ earg = interpret(pue, earg, istate);
+ if (exceptionOrCantInterpret(earg))
+ return earg;
+ if (earg.op == TOK.null_)
+ {
+ emplaceExp!(NullExp)(pue, earg.loc, earg.type);
+ return pue.exp();
+ }
+ if (earg.op != TOK.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
+ return null;
+ auto aae = earg.isAssocArrayLiteralExp();
+ auto ae = ctfeEmplaceExp!ArrayLiteralExp(aae.loc, returnType, aae.values);
+ ae.ownedByCtfe = aae.ownedByCtfe;
+ //printf("result is %s\n", e.toChars());
+ *pue = copyLiteral(ae);
+ return pue.exp();
+}
+
+private Expression interpret_dup(UnionExp* pue, InterState* istate, Expression earg)
+{
+ debug (LOG)
+ {
+ printf("interpret_dup()\n");
+ }
+ earg = interpret(pue, earg, istate);
+ if (exceptionOrCantInterpret(earg))
+ return earg;
+ if (earg.op == TOK.null_)
+ {
+ emplaceExp!(NullExp)(pue, earg.loc, earg.type);
+ return pue.exp();
+ }
+ if (earg.op != TOK.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
+ return null;
+ auto aae = copyLiteral(earg).copy().isAssocArrayLiteralExp();
+ for (size_t i = 0; i < aae.keys.dim; i++)
+ {
+ if (Expression e = evaluatePostblit(istate, (*aae.keys)[i]))
+ return e;
+ if (Expression e = evaluatePostblit(istate, (*aae.values)[i]))
+ return e;
+ }
+ aae.type = earg.type.mutableOf(); // repaint type from const(int[int]) to const(int)[int]
+ //printf("result is %s\n", aae.toChars());
+ return aae;
+}
+
+// signature is int delegate(ref Value) OR int delegate(ref Key, ref Value)
+private Expression interpret_aaApply(UnionExp* pue, InterState* istate, Expression aa, Expression deleg)
+{
+ aa = interpret(aa, istate);
+ if (exceptionOrCantInterpret(aa))
+ return aa;
+ if (aa.op != TOK.assocArrayLiteral)
+ {
+ emplaceExp!(IntegerExp)(pue, deleg.loc, 0, Type.tsize_t);
+ return pue.exp();
+ }
+
+ FuncDeclaration fd = null;
+ Expression pthis = null;
+ if (auto de = deleg.isDelegateExp())
+ {
+ fd = de.func;
+ pthis = de.e1;
+ }
+ else if (auto fe = deleg.isFuncExp())
+ fd = fe.fd;
+
+ assert(fd && fd.fbody);
+ assert(fd.parameters);
+ size_t numParams = fd.parameters.dim;
+ assert(numParams == 1 || numParams == 2);
+
+ Parameter fparam = fd.type.isTypeFunction().parameterList[numParams - 1];
+ const wantRefValue = fparam.isReference();
+
+ Expressions args = Expressions(numParams);
+
+ AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)aa;
+ if (!ae.keys || ae.keys.dim == 0)
+ return ctfeEmplaceExp!IntegerExp(deleg.loc, 0, Type.tsize_t);
+ Expression eresult;
+
+ for (size_t i = 0; i < ae.keys.dim; ++i)
+ {
+ Expression ekey = (*ae.keys)[i];
+ Expression evalue = (*ae.values)[i];
+ if (wantRefValue)
+ {
+ Type t = evalue.type;
+ evalue = ctfeEmplaceExp!IndexExp(deleg.loc, ae, ekey);
+ evalue.type = t;
+ }
+ args[numParams - 1] = evalue;
+ if (numParams == 2)
+ args[0] = ekey;
+
+ UnionExp ue = void;
+ eresult = interpretFunction(&ue, fd, istate, &args, pthis);
+ if (eresult == ue.exp())
+ eresult = ue.copy();
+ if (exceptionOrCantInterpret(eresult))
+ return eresult;
+
+ if (eresult.isIntegerExp().getInteger() != 0)
+ return eresult;
+ }
+ return eresult;
+}
+
+/* Decoding UTF strings for foreach loops. Duplicates the functionality of
+ * the twelve _aApplyXXn functions in aApply.d in the runtime.
+ */
+private Expression foreachApplyUtf(UnionExp* pue, InterState* istate, Expression str, Expression deleg, bool rvs)
+{
+ debug (LOG)
+ {
+ printf("foreachApplyUtf(%s, %s)\n", str.toChars(), deleg.toChars());
+ }
+ FuncDeclaration fd = null;
+ Expression pthis = null;
+ if (auto de = deleg.isDelegateExp())
+ {
+ fd = de.func;
+ pthis = de.e1;
+ }
+ else if (auto fe = deleg.isFuncExp())
+ fd = fe.fd;
+
+ assert(fd && fd.fbody);
+ assert(fd.parameters);
+ size_t numParams = fd.parameters.dim;
+ assert(numParams == 1 || numParams == 2);
+ Type charType = (*fd.parameters)[numParams - 1].type;
+ Type indexType = numParams == 2 ? (*fd.parameters)[0].type : Type.tsize_t;
+ size_t len = cast(size_t)resolveArrayLength(str);
+ if (len == 0)
+ {
+ emplaceExp!(IntegerExp)(pue, deleg.loc, 0, indexType);
+ return pue.exp();
+ }
+
+ UnionExp strTmp = void;
+ str = resolveSlice(str, &strTmp);
+
+ auto se = str.isStringExp();
+ auto ale = str.isArrayLiteralExp();
+ if (!se && !ale)
+ {
+ str.error("CTFE internal error: cannot foreach `%s`", str.toChars());
+ return CTFEExp.cantexp;
+ }
+ Expressions args = Expressions(numParams);
+
+ Expression eresult = null; // ded-store to prevent spurious warning
+
+ // Buffers for encoding; also used for decoding array literals
+ char[4] utf8buf = void;
+ wchar[2] utf16buf = void;
+
+ size_t start = rvs ? len : 0;
+ size_t end = rvs ? 0 : len;
+ for (size_t indx = start; indx != end;)
+ {
+ // Step 1: Decode the next dchar from the string.
+
+ string errmsg = null; // Used for reporting decoding errors
+ dchar rawvalue; // Holds the decoded dchar
+ size_t currentIndex = indx; // The index of the decoded character
+
+ if (ale)
+ {
+ // If it is an array literal, copy the code points into the buffer
+ size_t buflen = 1; // #code points in the buffer
+ size_t n = 1; // #code points in this char
+ size_t sz = cast(size_t)ale.type.nextOf().size();
+
+ switch (sz)
+ {
+ case 1:
+ if (rvs)
+ {
+ // find the start of the string
+ --indx;
+ buflen = 1;
+ while (indx > 0 && buflen < 4)
+ {
+ Expression r = (*ale.elements)[indx];
+ char x = cast(char)r.isIntegerExp().getInteger();
+ if ((x & 0xC0) != 0x80)
+ break;
+ --indx;
+ ++buflen;
+ }
+ }
+ else
+ buflen = (indx + 4 > len) ? len - indx : 4;
+ for (size_t i = 0; i < buflen; ++i)
+ {
+ Expression r = (*ale.elements)[indx + i];
+ utf8buf[i] = cast(char)r.isIntegerExp().getInteger();
+ }
+ n = 0;
+ errmsg = utf_decodeChar(utf8buf[0 .. buflen], n, rawvalue);
+ break;
+
+ case 2:
+ if (rvs)
+ {
+ // find the start of the string
+ --indx;
+ buflen = 1;
+ Expression r = (*ale.elements)[indx];
+ ushort x = cast(ushort)r.isIntegerExp().getInteger();
+ if (indx > 0 && x >= 0xDC00 && x <= 0xDFFF)
+ {
+ --indx;
+ ++buflen;
+ }
+ }
+ else
+ buflen = (indx + 2 > len) ? len - indx : 2;
+ for (size_t i = 0; i < buflen; ++i)
+ {
+ Expression r = (*ale.elements)[indx + i];
+ utf16buf[i] = cast(ushort)r.isIntegerExp().getInteger();
+ }
+ n = 0;
+ errmsg = utf_decodeWchar(utf16buf[0 .. buflen], n, rawvalue);
+ break;
+
+ case 4:
+ {
+ if (rvs)
+ --indx;
+ Expression r = (*ale.elements)[indx];
+ rawvalue = cast(dchar)r.isIntegerExp().getInteger();
+ n = 1;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ if (!rvs)
+ indx += n;
+ }
+ else
+ {
+ // String literals
+ size_t saveindx; // used for reverse iteration
+
+ switch (se.sz)
+ {
+ case 1:
+ {
+ if (rvs)
+ {
+ // find the start of the string
+ --indx;
+ while (indx > 0 && ((se.getCodeUnit(indx) & 0xC0) == 0x80))
+ --indx;
+ saveindx = indx;
+ }
+ auto slice = se.peekString();
+ errmsg = utf_decodeChar(slice, indx, rawvalue);
+ if (rvs)
+ indx = saveindx;
+ break;
+ }
+
+ case 2:
+ if (rvs)
+ {
+ // find the start
+ --indx;
+ auto wc = se.getCodeUnit(indx);
+ if (wc >= 0xDC00 && wc <= 0xDFFF)
+ --indx;
+ saveindx = indx;
+ }
+ const slice = se.peekWstring();
+ errmsg = utf_decodeWchar(slice, indx, rawvalue);
+ if (rvs)
+ indx = saveindx;
+ break;
+
+ case 4:
+ if (rvs)
+ --indx;
+ rawvalue = se.getCodeUnit(indx);
+ if (!rvs)
+ ++indx;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ if (errmsg)
+ {
+ deleg.error("`%.*s`", cast(int)errmsg.length, errmsg.ptr);
+ return CTFEExp.cantexp;
+ }
+
+ // Step 2: encode the dchar in the target encoding
+
+ int charlen = 1; // How many codepoints are involved?
+ switch (charType.size())
+ {
+ case 1:
+ charlen = utf_codeLengthChar(rawvalue);
+ utf_encodeChar(&utf8buf[0], rawvalue);
+ break;
+ case 2:
+ charlen = utf_codeLengthWchar(rawvalue);
+ utf_encodeWchar(&utf16buf[0], rawvalue);
+ break;
+ case 4:
+ break;
+ default:
+ assert(0);
+ }
+ if (rvs)
+ currentIndex = indx;
+
+ // Step 3: call the delegate once for each code point
+
+ // The index only needs to be set once
+ if (numParams == 2)
+ args[0] = ctfeEmplaceExp!IntegerExp(deleg.loc, currentIndex, indexType);
+
+ Expression val = null;
+
+ foreach (k; 0 .. charlen)
+ {
+ dchar codepoint;
+ switch (charType.size())
+ {
+ case 1:
+ codepoint = utf8buf[k];
+ break;
+ case 2:
+ codepoint = utf16buf[k];
+ break;
+ case 4:
+ codepoint = rawvalue;
+ break;
+ default:
+ assert(0);
+ }
+ val = ctfeEmplaceExp!IntegerExp(str.loc, codepoint, charType);
+
+ args[numParams - 1] = val;
+
+ UnionExp ue = void;
+ eresult = interpretFunction(&ue, fd, istate, &args, pthis);
+ if (eresult == ue.exp())
+ eresult = ue.copy();
+ if (exceptionOrCantInterpret(eresult))
+ return eresult;
+ if (eresult.isIntegerExp().getInteger() != 0)
+ return eresult;
+ }
+ }
+ return eresult;
+}
+
+/* If this is a built-in function, return the interpreted result,
+ * Otherwise, return NULL.
+ */
+private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, const ref Loc loc, FuncDeclaration fd, Expressions* arguments, Expression pthis)
+{
+ Expression e = null;
+ size_t nargs = arguments ? arguments.dim : 0;
+ if (!pthis)
+ {
+ if (isBuiltin(fd) != BUILTIN.unimp)
+ {
+ Expressions args = Expressions(nargs);
+ foreach (i, ref arg; args)
+ {
+ Expression earg = (*arguments)[i];
+ earg = interpret(earg, istate);
+ if (exceptionOrCantInterpret(earg))
+ return earg;
+ arg = earg;
+ }
+ e = eval_builtin(loc, fd, &args);
+ if (!e)
+ {
+ error(loc, "cannot evaluate unimplemented builtin `%s` at compile time", fd.toChars());
+ e = CTFEExp.cantexp;
+ }
+ }
+ }
+ if (!pthis)
+ {
+ if (nargs == 1 || nargs == 3)
+ {
+ Expression firstarg = (*arguments)[0];
+ if (auto firstAAtype = firstarg.type.toBasetype().isTypeAArray())
+ {
+ const id = fd.ident;
+ if (nargs == 1)
+ {
+ if (id == Id.aaLen)
+ return interpret_length(pue, istate, firstarg);
+
+ if (fd.toParent2().ident == Id.object)
+ {
+ if (id == Id.keys)
+ return interpret_keys(pue, istate, firstarg, firstAAtype.index.arrayOf());
+ if (id == Id.values)
+ return interpret_values(pue, istate, firstarg, firstAAtype.nextOf().arrayOf());
+ if (id == Id.rehash)
+ return interpret(pue, firstarg, istate);
+ if (id == Id.dup)
+ return interpret_dup(pue, istate, firstarg);
+ }
+ }
+ else // (nargs == 3)
+ {
+ if (id == Id._aaApply)
+ return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
+ if (id == Id._aaApply2)
+ return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
+ }
+ }
+ }
+ }
+ if (pthis && !fd.fbody && fd.isCtorDeclaration() && fd.parent && fd.parent.parent && fd.parent.parent.ident == Id.object)
+ {
+ if (pthis.op == TOK.classReference && fd.parent.ident == Id.Throwable)
+ {
+ // At present, the constructors just copy their arguments into the struct.
+ // But we might need some magic if stack tracing gets added to druntime.
+ StructLiteralExp se = (cast(ClassReferenceExp)pthis).value;
+ assert(arguments.dim <= se.elements.dim);
+ foreach (i, arg; *arguments)
+ {
+ auto elem = interpret(arg, istate);
+ if (exceptionOrCantInterpret(elem))
+ return elem;
+ (*se.elements)[i] = elem;
+ }
+ return CTFEExp.voidexp;
+ }
+ }
+ if (nargs == 1 && !pthis && (fd.ident == Id.criticalenter || fd.ident == Id.criticalexit))
+ {
+ // Support synchronized{} as a no-op
+ return CTFEExp.voidexp;
+ }
+ if (!pthis)
+ {
+ const idlen = fd.ident.toString().length;
+ const id = fd.ident.toChars();
+ if (nargs == 2 && (idlen == 10 || idlen == 11) && !strncmp(id, "_aApply", 7))
+ {
+ // Functions from aApply.d and aApplyR.d in the runtime
+ bool rvs = (idlen == 11); // true if foreach_reverse
+ char c = id[idlen - 3]; // char width: 'c', 'w', or 'd'
+ char s = id[idlen - 2]; // string width: 'c', 'w', or 'd'
+ char n = id[idlen - 1]; // numParams: 1 or 2.
+ // There are 12 combinations
+ if ((n == '1' || n == '2') &&
+ (c == 'c' || c == 'w' || c == 'd') &&
+ (s == 'c' || s == 'w' || s == 'd') &&
+ c != s)
+ {
+ Expression str = (*arguments)[0];
+ str = interpret(str, istate);
+ if (exceptionOrCantInterpret(str))
+ return str;
+ return foreachApplyUtf(pue, istate, str, (*arguments)[1], rvs);
+ }
+ }
+ }
+ return e;
+}
+
+private Expression evaluatePostblit(InterState* istate, Expression e)
+{
+ auto ts = e.type.baseElemOf().isTypeStruct();
+ if (!ts)
+ return null;
+ StructDeclaration sd = ts.sym;
+ if (!sd.postblit)
+ return null;
+
+ if (auto ale = e.isArrayLiteralExp())
+ {
+ foreach (elem; *ale.elements)
+ {
+ if (auto ex = evaluatePostblit(istate, elem))
+ return ex;
+ }
+ return null;
+ }
+ if (e.op == TOK.structLiteral)
+ {
+ // e.__postblit()
+ UnionExp ue = void;
+ e = interpretFunction(&ue, sd.postblit, istate, null, e);
+ if (e == ue.exp())
+ e = ue.copy();
+ if (exceptionOrCantInterpret(e))
+ return e;
+ return null;
+ }
+ assert(0);
+}
+
+private Expression evaluateDtor(InterState* istate, Expression e)
+{
+ auto ts = e.type.baseElemOf().isTypeStruct();
+ if (!ts)
+ return null;
+ StructDeclaration sd = ts.sym;
+ if (!sd.dtor)
+ return null;
+
+ UnionExp ue = void;
+ if (auto ale = e.isArrayLiteralExp())
+ {
+ foreach_reverse (elem; *ale.elements)
+ e = evaluateDtor(istate, elem);
+ }
+ else if (e.op == TOK.structLiteral)
+ {
+ // e.__dtor()
+ e = interpretFunction(&ue, sd.dtor, istate, null, e);
+ }
+ else
+ assert(0);
+ if (exceptionOrCantInterpret(e))
+ {
+ if (e == ue.exp())
+ e = ue.copy();
+ return e;
+ }
+ return null;
+}
+
+/*************************** CTFE Sanity Checks ***************************/
+/* Setter functions for CTFE variable values.
+ * These functions exist to check for compiler CTFE bugs.
+ */
+private bool hasValue(VarDeclaration vd)
+{
+ return vd.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone &&
+ getValue(vd) !is null;
+}
+
+// Don't check for validity
+private void setValueWithoutChecking(VarDeclaration vd, Expression newval)
+{
+ ctfeGlobals.stack.setValue(vd, newval);
+}
+
+private void setValue(VarDeclaration vd, Expression newval)
+{
+ //printf("setValue() vd: %s newval: %s\n", vd.toChars(), newval.toChars());
+ version (none)
+ {
+ if (!((vd.storage_class & (STC.out_ | STC.ref_)) ? isCtfeReferenceValid(newval) : isCtfeValueValid(newval)))
+ {
+ printf("[%s] vd = %s %s, newval = %s\n", vd.loc.toChars(), vd.type.toChars(), vd.toChars(), newval.toChars());
+ }
+ }
+ assert((vd.storage_class & (STC.out_ | STC.ref_)) ? isCtfeReferenceValid(newval) : isCtfeValueValid(newval));
+ ctfeGlobals.stack.setValue(vd, newval);
+}
+
+/**
+ * Removes `_d_HookTraceImpl` if found from `ce` and `fd`.
+ * This is needed for the CTFE interception code to be able to find hooks that are called though the hook's `*Trace`
+ * wrapper.
+ *
+ * This is done by replacing `_d_HookTraceImpl!(T, Hook, errMsg)(..., parameters)` with `Hook(parameters)`.
+ * Parameters:
+ * ce = The CallExp that possible will be be replaced
+ * fd = Fully resolve function declaration that `ce` would call
+ */
+private void removeHookTraceImpl(ref CallExp ce, ref FuncDeclaration fd)
+{
+ if (fd.ident != Id._d_HookTraceImpl)
+ return;
+
+ auto oldCE = ce;
+
+ // Get the Hook from the second template parameter
+ TemplateInstance templateInstance = fd.parent.isTemplateInstance;
+ RootObject hook = (*templateInstance.tiargs)[1];
+ assert(hook.dyncast() == DYNCAST.dsymbol, "Expected _d_HookTraceImpl's second template parameter to be an alias to the hook!");
+ fd = (cast(Dsymbol)hook).isFuncDeclaration;
+
+ // Remove the first three trace parameters
+ auto arguments = new Expressions();
+ arguments.reserve(ce.arguments.dim - 3);
+ arguments.pushSlice((*ce.arguments)[3 .. $]);
+
+ ce = ctfeEmplaceExp!CallExp(ce.loc, ctfeEmplaceExp!VarExp(ce.loc, fd, false), arguments);
+
+ if (global.params.verbose)
+ message("strip %s =>\n %s", oldCE.toChars(), ce.toChars());
+}
diff --git a/gcc/d/dmd/dmacro.c b/gcc/d/dmd/dmacro.c
deleted file mode 100644
index 91cbe50713c..00000000000
--- a/gcc/d/dmd/dmacro.c
+++ /dev/null
@@ -1,458 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/macro.c
- */
-
-/* Simple macro text processor.
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "errors.h"
-#include "root/rmem.h"
-#include "root/root.h"
-
-#include "macro.h"
-
-bool isIdStart(const utf8_t *p);
-bool isIdTail(const utf8_t *p);
-int utfStride(const utf8_t *p);
-
-utf8_t *memdup(const utf8_t *p, size_t len)
-{
- return (utf8_t *)memcpy(mem.xmalloc(len), p, len);
-}
-
-Macro::Macro(const utf8_t *name, size_t namelen, const utf8_t *text, size_t textlen)
-{
- next = NULL;
-
- this->name = name;
- this->namelen = namelen;
-
- this->text = text;
- this->textlen = textlen;
- inuse = 0;
-}
-
-
-Macro *Macro::search(const utf8_t *name, size_t namelen)
-{ Macro *table;
-
- //printf("Macro::search(%.*s)\n", namelen, name);
- for (table = this; table; table = table->next)
- {
- if (table->namelen == namelen &&
- memcmp(table->name, name, namelen) == 0)
- {
- //printf("\tfound %d\n", table->textlen);
- break;
- }
- }
- return table;
-}
-
-Macro *Macro::define(Macro **ptable, const utf8_t *name, size_t namelen, const utf8_t *text, size_t textlen)
-{
- //printf("Macro::define('%.*s' = '%.*s')\n", namelen, name, textlen, text);
-
- Macro *table;
-
- //assert(ptable);
- for (table = *ptable; table; table = table->next)
- {
- if (table->namelen == namelen &&
- memcmp(table->name, name, namelen) == 0)
- {
- table->text = text;
- table->textlen = textlen;
- return table;
- }
- }
- table = new Macro(name, namelen, text, textlen);
- table->next = *ptable;
- *ptable = table;
- return table;
-}
-
-/**********************************************************
- * Given buffer p[0..end], extract argument marg[0..marglen].
- * Params:
- * n 0: get entire argument
- * 1..9: get nth argument
- * -1: get 2nd through end
- */
-
-size_t extractArgN(const utf8_t *p, size_t end, const utf8_t **pmarg, size_t *pmarglen, int n)
-{
- /* Scan forward for matching right parenthesis.
- * Nest parentheses.
- * Skip over "..." and '...' strings inside HTML tags.
- * Skip over <!-- ... --> comments.
- * Skip over previous macro insertions
- * Set marglen.
- */
- unsigned parens = 1;
- unsigned char instring = 0;
- unsigned incomment = 0;
- unsigned intag = 0;
- unsigned inexp = 0;
- int argn = 0;
-
- size_t v = 0;
-
- Largstart:
- // Skip first space, if any, to find the start of the macro argument
- if (n != 1 && v < end && isspace(p[v]))
- v++;
- *pmarg = p + v;
-
- for (; v < end; v++)
- { utf8_t c = p[v];
-
- switch (c)
- {
- case ',':
- if (!inexp && !instring && !incomment && parens == 1)
- {
- argn++;
- if (argn == 1 && n == -1)
- { v++;
- goto Largstart;
- }
- if (argn == n)
- break;
- if (argn + 1 == n)
- { v++;
- goto Largstart;
- }
- }
- continue;
-
- case '(':
- if (!inexp && !instring && !incomment)
- parens++;
- continue;
-
- case ')':
- if (!inexp && !instring && !incomment && --parens == 0)
- {
- break;
- }
- continue;
-
- case '"':
- case '\'':
- if (!inexp && !incomment && intag)
- {
- if (c == instring)
- instring = 0;
- else if (!instring)
- instring = c;
- }
- continue;
-
- case '<':
- if (!inexp && !instring && !incomment)
- {
- if (v + 6 < end &&
- p[v + 1] == '!' &&
- p[v + 2] == '-' &&
- p[v + 3] == '-')
- {
- incomment = 1;
- v += 3;
- }
- else if (v + 2 < end &&
- isalpha(p[v + 1]))
- intag = 1;
- }
- continue;
-
- case '>':
- if (!inexp)
- intag = 0;
- continue;
-
- case '-':
- if (!inexp &&
- !instring &&
- incomment &&
- v + 2 < end &&
- p[v + 1] == '-' &&
- p[v + 2] == '>')
- {
- incomment = 0;
- v += 2;
- }
- continue;
-
- case 0xFF:
- if (v + 1 < end)
- {
- if (p[v + 1] == '{')
- inexp++;
- else if (p[v + 1] == '}')
- inexp--;
- }
- continue;
-
- default:
- continue;
- }
- break;
- }
- if (argn == 0 && n == -1)
- *pmarg = p + v;
- *pmarglen = p + v - *pmarg;
- //printf("extractArg%d('%.*s') = '%.*s'\n", n, end, p, *pmarglen, *pmarg);
- return v;
-}
-
-
-/*****************************************************
- * Expand macro in place in buf.
- * Only look at the text in buf from start to end.
- */
-
-void Macro::expand(OutBuffer *buf, size_t start, size_t *pend,
- const utf8_t *arg, size_t arglen)
-{
- // limit recursive expansion
- static int nest;
- if (nest > global.recursionLimit)
- {
- error(Loc(), "DDoc macro expansion limit exceeded; more than %d expansions.",
- global.recursionLimit);
- return;
- }
- nest++;
-
- size_t end = *pend;
- assert(start <= end);
- assert(end <= buf->length());
-
- /* First pass - replace $0
- */
- arg = memdup(arg, arglen);
- for (size_t u = start; u + 1 < end; )
- {
- utf8_t *p = (utf8_t *)buf->slice().ptr; // buf->slice().ptr is not loop invariant
-
- /* Look for $0, but not $$0, and replace it with arg.
- */
- if (p[u] == '$' && (isdigit(p[u + 1]) || p[u + 1] == '+'))
- {
- if (u > start && p[u - 1] == '$')
- { // Don't expand $$0, but replace it with $0
- buf->remove(u - 1, 1);
- end--;
- u += 1; // now u is one past the closing '1'
- continue;
- }
-
- utf8_t c = p[u + 1];
- int n = (c == '+') ? -1 : c - '0';
-
- const utf8_t *marg;
- size_t marglen;
- if (n == 0)
- {
- marg = arg;
- marglen = arglen;
- }
- else
- extractArgN(arg, arglen, &marg, &marglen, n);
- if (marglen == 0)
- { // Just remove macro invocation
- //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg);
- buf->remove(u, 2);
- end -= 2;
- }
- else if (c == '+')
- {
- // Replace '$+' with 'arg'
- //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg);
- buf->remove(u, 2);
- buf->insert(u, marg, marglen);
- end += marglen - 2;
-
- // Scan replaced text for further expansion
- size_t mend = u + marglen;
- expand(buf, u, &mend, NULL, 0);
- end += mend - (u + marglen);
- u = mend;
- }
- else
- {
- // Replace '$1' with '\xFF{arg\xFF}'
- //printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], marglen, marg);
- buf->slice().ptr[u] = 0xFF;
- buf->slice().ptr[u + 1] = '{';
- buf->insert(u + 2, marg, marglen);
- buf->insert(u + 2 + marglen, (const char *)"\xFF}", 2);
- end += -2 + 2 + marglen + 2;
-
- // Scan replaced text for further expansion
- size_t mend = u + 2 + marglen;
- expand(buf, u + 2, &mend, NULL, 0);
- end += mend - (u + 2 + marglen);
- u = mend;
- }
- //printf("u = %d, end = %d\n", u, end);
- //printf("#%.*s#\n", end, &buf->slice().ptr[0]);
- continue;
- }
-
- u++;
- }
-
- /* Second pass - replace other macros
- */
- for (size_t u = start; u + 4 < end; )
- {
- utf8_t *p = (utf8_t *)buf->slice().ptr; // buf->slice().ptr is not loop invariant
-
- /* A valid start of macro expansion is $(c, where c is
- * an id start character, and not $$(c.
- */
- if (p[u] == '$' && p[u + 1] == '(' && isIdStart(p+u+2))
- {
- //printf("\tfound macro start '%c'\n", p[u + 2]);
- utf8_t *name = p + u + 2;
- size_t namelen = 0;
-
- const utf8_t *marg;
- size_t marglen;
-
- size_t v;
- /* Scan forward to find end of macro name and
- * beginning of macro argument (marg).
- */
- for (v = u + 2; v < end; v+=utfStride(p+v))
- {
-
- if (!isIdTail(p+v))
- { // We've gone past the end of the macro name.
- namelen = v - (u + 2);
- break;
- }
- }
-
- v += extractArgN(p + v, end - v, &marg, &marglen, 0);
- assert(v <= end);
-
- if (v < end)
- { // v is on the closing ')'
- if (u > start && p[u - 1] == '$')
- { // Don't expand $$(NAME), but replace it with $(NAME)
- buf->remove(u - 1, 1);
- end--;
- u = v; // now u is one past the closing ')'
- continue;
- }
-
- Macro *m = search(name, namelen);
-
- if (!m)
- {
- static const char undef[] = "DDOC_UNDEFINED_MACRO";
- m = search((const utf8_t *)undef, strlen(undef));
- if (m)
- {
- // Macro was not defined, so this is an expansion of
- // DDOC_UNDEFINED_MACRO. Prepend macro name to args.
- // marg = name[ ] ~ "," ~ marg[ ];
- if (marglen)
- {
- utf8_t *q = (utf8_t *)mem.xmalloc(namelen + 1 + marglen);
- assert(q);
- memcpy(q, name, namelen);
- q[namelen] = ',';
- memcpy(q + namelen + 1, marg, marglen);
- marg = q;
- marglen += namelen + 1;
- }
- else
- {
- marg = name;
- marglen = namelen;
- }
- }
- }
-
- if (m)
- {
- if (m->inuse && marglen == 0)
- { // Remove macro invocation
- buf->remove(u, v + 1 - u);
- end -= v + 1 - u;
- }
- else if (m->inuse &&
- ((arglen == marglen && memcmp(arg, marg, arglen) == 0) ||
- (arglen + 4 == marglen &&
- marg[0] == 0xFF &&
- marg[1] == '{' &&
- memcmp(arg, marg + 2, arglen) == 0 &&
- marg[marglen - 2] == 0xFF &&
- marg[marglen - 1] == '}'
- )
- )
- )
- {
- /* Recursive expansion:
- * marg is same as arg (with blue paint added)
- * Just leave in place.
- */
- }
- else
- {
- //printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", m->namelen, m->name, marglen, marg, m->textlen, m->text);
- marg = memdup(marg, marglen);
- // Insert replacement text
- buf->spread(v + 1, 2 + m->textlen + 2);
- buf->slice().ptr[v + 1] = 0xFF;
- buf->slice().ptr[v + 2] = '{';
- memcpy(buf->slice().ptr + v + 3, m->text, m->textlen);
- buf->slice().ptr[v + 3 + m->textlen] = 0xFF;
- buf->slice().ptr[v + 3 + m->textlen + 1] = '}';
-
- end += 2 + m->textlen + 2;
-
- // Scan replaced text for further expansion
- m->inuse++;
- size_t mend = v + 1 + 2+m->textlen+2;
- expand(buf, v + 1, &mend, marg, marglen);
- end += mend - (v + 1 + 2+m->textlen+2);
- m->inuse--;
-
- buf->remove(u, v + 1 - u);
- end -= v + 1 - u;
- u += mend - (v + 1);
- mem.xfree(const_cast<utf8_t *>(marg));
- //printf("u = %d, end = %d\n", u, end);
- //printf("#%.*s#\n", end - u, &buf->slice().ptr[u]);
- continue;
- }
- }
- else
- {
- // Replace $(NAME) with nothing
- buf->remove(u, v + 1 - u);
- end -= (v + 1 - u);
- continue;
- }
- }
- }
- u++;
- }
- mem.xfree(const_cast<utf8_t *>(arg));
- *pend = end;
- nest--;
-}
diff --git a/gcc/d/dmd/dmacro.d b/gcc/d/dmd/dmacro.d
new file mode 100644
index 00000000000..ddfee2ccab0
--- /dev/null
+++ b/gcc/d/dmd/dmacro.d
@@ -0,0 +1,435 @@
+/**
+ * Text macro processor for Ddoc.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmacro.d, _dmacro.d)
+ * Documentation: https://dlang.org/phobos/dmd_dmacro.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmacro.d
+ */
+
+module dmd.dmacro;
+
+import core.stdc.ctype;
+import core.stdc.string;
+import dmd.doc;
+import dmd.errors;
+import dmd.globals;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+
+extern (C++) struct MacroTable
+{
+ /**********************************
+ * Define name=text macro.
+ * If macro `name` already exists, replace the text for it.
+ * Params:
+ * name = name of macro
+ * text = text of macro
+ */
+ extern (D) void define(const(char)[] name, const(char)[] text)
+ {
+ //printf("MacroTable::define('%.*s' = '%.*s')\n", cast(int)name.length, name.ptr, text.length, text.ptr);
+ Macro* table;
+ for (table = mactab; table; table = table.next)
+ {
+ if (table.name == name)
+ {
+ table.text = text;
+ return;
+ }
+ }
+ table = new Macro(name, text);
+ table.next = mactab;
+ mactab = table;
+ }
+
+ /*****************************************************
+ * Look for macros in buf and expand them in place.
+ * Only look at the text in buf from start to pend.
+ */
+ extern (D) void expand(ref OutBuffer buf, size_t start, ref size_t pend, const(char)[] arg)
+ {
+ version (none)
+ {
+ printf("Macro::expand(buf[%d..%d], arg = '%.*s')\n", start, pend, cast(int)arg.length, arg.ptr);
+ printf("Buf is: '%.*s'\n", cast(int)(pend - start), buf.data + start);
+ }
+ // limit recursive expansion
+ __gshared int nest;
+ if (nest > global.recursionLimit)
+ {
+ error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.",
+ global.recursionLimit);
+ return;
+ }
+ nest++;
+ size_t end = pend;
+ assert(start <= end);
+ assert(end <= buf.length);
+ /* First pass - replace $0
+ */
+ arg = memdup(arg);
+ for (size_t u = start; u + 1 < end;)
+ {
+ char* p = cast(char*)buf[].ptr; // buf.data is not loop invariant
+ /* Look for $0, but not $$0, and replace it with arg.
+ */
+ if (p[u] == '$' && (isdigit(p[u + 1]) || p[u + 1] == '+'))
+ {
+ if (u > start && p[u - 1] == '$')
+ {
+ // Don't expand $$0, but replace it with $0
+ buf.remove(u - 1, 1);
+ end--;
+ u += 1; // now u is one past the closing '1'
+ continue;
+ }
+ char c = p[u + 1];
+ int n = (c == '+') ? -1 : c - '0';
+ const(char)[] marg;
+ if (n == 0)
+ {
+ marg = arg;
+ }
+ else
+ extractArgN(arg, marg, n);
+ if (marg.length == 0)
+ {
+ // Just remove macro invocation
+ //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], cast(int)marg.length, marg.ptr);
+ buf.remove(u, 2);
+ end -= 2;
+ }
+ else if (c == '+')
+ {
+ // Replace '$+' with 'arg'
+ //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], cast(int)marg.length, marg.ptr);
+ buf.remove(u, 2);
+ buf.insert(u, marg);
+ end += marg.length - 2;
+ // Scan replaced text for further expansion
+ size_t mend = u + marg.length;
+ expand(buf, u, mend, null);
+ end += mend - (u + marg.length);
+ u = mend;
+ }
+ else
+ {
+ // Replace '$1' with '\xFF{arg\xFF}'
+ //printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], cast(int)marg.length, marg.ptr);
+ ubyte[] slice = cast(ubyte[])buf[];
+ slice[u] = 0xFF;
+ slice[u + 1] = '{';
+ buf.insert(u + 2, marg);
+ buf.insert(u + 2 + marg.length, "\xFF}");
+ end += -2 + 2 + marg.length + 2;
+ // Scan replaced text for further expansion
+ size_t mend = u + 2 + marg.length;
+ expand(buf, u + 2, mend, null);
+ end += mend - (u + 2 + marg.length);
+ u = mend;
+ }
+ //printf("u = %d, end = %d\n", u, end);
+ //printf("#%.*s#\n", cast(int)end, &buf.data[0]);
+ continue;
+ }
+ u++;
+ }
+ /* Second pass - replace other macros
+ */
+ for (size_t u = start; u + 4 < end;)
+ {
+ char* p = cast(char*)buf[].ptr; // buf.data is not loop invariant
+ /* A valid start of macro expansion is $(c, where c is
+ * an id start character, and not $$(c.
+ */
+ if (p[u] == '$' && p[u + 1] == '(' && isIdStart(p + u + 2))
+ {
+ //printf("\tfound macro start '%c'\n", p[u + 2]);
+ char* name = p + u + 2;
+ size_t namelen = 0;
+ const(char)[] marg;
+ size_t v;
+ /* Scan forward to find end of macro name and
+ * beginning of macro argument (marg).
+ */
+ for (v = u + 2; v < end; v += utfStride(p + v))
+ {
+ if (!isIdTail(p + v))
+ {
+ // We've gone past the end of the macro name.
+ namelen = v - (u + 2);
+ break;
+ }
+ }
+ v += extractArgN(p[v .. end], marg, 0);
+ assert(v <= end);
+ if (v < end)
+ {
+ // v is on the closing ')'
+ if (u > start && p[u - 1] == '$')
+ {
+ // Don't expand $$(NAME), but replace it with $(NAME)
+ buf.remove(u - 1, 1);
+ end--;
+ u = v; // now u is one past the closing ')'
+ continue;
+ }
+ Macro* m = search(name[0 .. namelen]);
+ if (!m)
+ {
+ immutable undef = "DDOC_UNDEFINED_MACRO";
+ m = search(undef);
+ if (m)
+ {
+ // Macro was not defined, so this is an expansion of
+ // DDOC_UNDEFINED_MACRO. Prepend macro name to args.
+ // marg = name[ ] ~ "," ~ marg[ ];
+ if (marg.length)
+ {
+ char* q = cast(char*)mem.xmalloc(namelen + 1 + marg.length);
+ assert(q);
+ memcpy(q, name, namelen);
+ q[namelen] = ',';
+ memcpy(q + namelen + 1, marg.ptr, marg.length);
+ marg = q[0 .. marg.length + namelen + 1];
+ }
+ else
+ {
+ marg = name[0 .. namelen];
+ }
+ }
+ }
+ if (m)
+ {
+ if (m.inuse && marg.length == 0)
+ {
+ // Remove macro invocation
+ buf.remove(u, v + 1 - u);
+ end -= v + 1 - u;
+ }
+ else if (m.inuse && ((arg.length == marg.length && memcmp(arg.ptr, marg.ptr, arg.length) == 0) ||
+ (arg.length + 4 == marg.length && marg[0] == 0xFF && marg[1] == '{' && memcmp(arg.ptr, marg.ptr + 2, arg.length) == 0 && marg[marg.length - 2] == 0xFF && marg[marg.length - 1] == '}')))
+ {
+ /* Recursive expansion:
+ * marg is same as arg (with blue paint added)
+ * Just leave in place.
+ */
+ }
+ else
+ {
+ //printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", cast(int)m.namelen, m.name, cast(int)marg.length, marg.ptr, cast(int)m.textlen, m.text);
+ marg = memdup(marg);
+ // Insert replacement text
+ buf.spread(v + 1, 2 + m.text.length + 2);
+ ubyte[] slice = cast(ubyte[])buf[];
+ slice[v + 1] = 0xFF;
+ slice[v + 2] = '{';
+ slice[v + 3 .. v + 3 + m.text.length] = cast(ubyte[])m.text[];
+ slice[v + 3 + m.text.length] = 0xFF;
+ slice[v + 3 + m.text.length + 1] = '}';
+ end += 2 + m.text.length + 2;
+ // Scan replaced text for further expansion
+ m.inuse++;
+ size_t mend = v + 1 + 2 + m.text.length + 2;
+ expand(buf, v + 1, mend, marg);
+ end += mend - (v + 1 + 2 + m.text.length + 2);
+ m.inuse--;
+ buf.remove(u, v + 1 - u);
+ end -= v + 1 - u;
+ u += mend - (v + 1);
+ mem.xfree(cast(char*)marg.ptr);
+ //printf("u = %d, end = %d\n", u, end);
+ //printf("#%.*s#\n", cast(int)(end - u), &buf.data[u]);
+ continue;
+ }
+ }
+ else
+ {
+ // Replace $(NAME) with nothing
+ buf.remove(u, v + 1 - u);
+ end -= (v + 1 - u);
+ continue;
+ }
+ }
+ }
+ u++;
+ }
+ mem.xfree(cast(char*)arg);
+ pend = end;
+ nest--;
+ }
+
+ private:
+
+ extern (D) Macro* search(const(char)[] name)
+ {
+ Macro* table;
+ //printf("Macro::search(%.*s)\n", cast(int)name.length, name.ptr);
+ for (table = mactab; table; table = table.next)
+ {
+ if (table.name == name)
+ {
+ //printf("\tfound %d\n", table.textlen);
+ break;
+ }
+ }
+ return table;
+ }
+
+ Macro* mactab;
+}
+
+/* ************************************************************************ */
+
+private:
+
+struct Macro
+{
+ Macro* next; // next in list
+ const(char)[] name; // macro name
+ const(char)[] text; // macro replacement text
+ int inuse; // macro is in use (don't expand)
+
+ this(const(char)[] name, const(char)[] text)
+ {
+ this.name = name;
+ this.text = text;
+ }
+}
+
+/************************
+ * Make mutable copy of slice p.
+ * Params:
+ * p = slice
+ * Returns:
+ * copy allocated with mem.xmalloc()
+ */
+
+char[] memdup(const(char)[] p)
+{
+ size_t len = p.length;
+ return (cast(char*)memcpy(mem.xmalloc(len), p.ptr, len))[0 .. len];
+}
+
+/**********************************************************
+ * Given buffer buf[], extract argument marg[].
+ * Params:
+ * buf = source string
+ * marg = set to slice of buf[]
+ * n = 0: get entire argument
+ * 1..9: get nth argument
+ * -1: get 2nd through end
+ */
+size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n)
+{
+ /* Scan forward for matching right parenthesis.
+ * Nest parentheses.
+ * Skip over "..." and '...' strings inside HTML tags.
+ * Skip over <!-- ... --> comments.
+ * Skip over previous macro insertions
+ * Set marg.
+ */
+ uint parens = 1;
+ ubyte instring = 0;
+ uint incomment = 0;
+ uint intag = 0;
+ uint inexp = 0;
+ uint argn = 0;
+ size_t v = 0;
+ const p = buf.ptr;
+ const end = buf.length;
+Largstart:
+ // Skip first space, if any, to find the start of the macro argument
+ if (n != 1 && v < end && isspace(p[v]))
+ v++;
+ size_t vstart = v;
+ for (; v < end; v++)
+ {
+ char c = p[v];
+ switch (c)
+ {
+ case ',':
+ if (!inexp && !instring && !incomment && parens == 1)
+ {
+ argn++;
+ if (argn == 1 && n == -1)
+ {
+ v++;
+ goto Largstart;
+ }
+ if (argn == n)
+ break;
+ if (argn + 1 == n)
+ {
+ v++;
+ goto Largstart;
+ }
+ }
+ continue;
+ case '(':
+ if (!inexp && !instring && !incomment)
+ parens++;
+ continue;
+ case ')':
+ if (!inexp && !instring && !incomment && --parens == 0)
+ {
+ break;
+ }
+ continue;
+ case '"':
+ case '\'':
+ if (!inexp && !incomment && intag)
+ {
+ if (c == instring)
+ instring = 0;
+ else if (!instring)
+ instring = c;
+ }
+ continue;
+ case '<':
+ if (!inexp && !instring && !incomment)
+ {
+ if (v + 6 < end && p[v + 1] == '!' && p[v + 2] == '-' && p[v + 3] == '-')
+ {
+ incomment = 1;
+ v += 3;
+ }
+ else if (v + 2 < end && isalpha(p[v + 1]))
+ intag = 1;
+ }
+ continue;
+ case '>':
+ if (!inexp)
+ intag = 0;
+ continue;
+ case '-':
+ if (!inexp && !instring && incomment && v + 2 < end && p[v + 1] == '-' && p[v + 2] == '>')
+ {
+ incomment = 0;
+ v += 2;
+ }
+ continue;
+ case 0xFF:
+ if (v + 1 < end)
+ {
+ if (p[v + 1] == '{')
+ inexp++;
+ else if (p[v + 1] == '}')
+ inexp--;
+ }
+ continue;
+ default:
+ continue;
+ }
+ break;
+ }
+ if (argn == 0 && n == -1)
+ marg = p[v .. v];
+ else
+ marg = p[vstart .. v];
+ //printf("extractArg%d('%.*s') = '%.*s'\n", n, cast(int)end, p, cast(int)marg.length, marg.ptr);
+ return v;
+}
diff --git a/gcc/d/dmd/dmangle.c b/gcc/d/dmd/dmangle.c
deleted file mode 100644
index 83f4c18bee8..00000000000
--- a/gcc/d/dmd/dmangle.c
+++ /dev/null
@@ -1,1122 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/mangle.c
- */
-
-#include "root/dsystem.h"
-#include "root/root.h"
-#include "root/aav.h"
-
-#include "mangle.h"
-#include "init.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "mtype.h"
-#include "attrib.h"
-#include "target.h"
-#include "template.h"
-#include "id.h"
-#include "module.h"
-#include "enum.h"
-#include "expression.h"
-#include "utf.h"
-
-typedef int (*ForeachDg)(void *ctx, size_t paramidx, Parameter *param);
-int Parameter_foreach(Parameters *parameters, ForeachDg dg, void *ctx, size_t *pn = NULL);
-
-static const char *mangleChar[TMAX];
-
-void initTypeMangle()
-{
- mangleChar[Tarray] = "A";
- mangleChar[Tsarray] = "G";
- mangleChar[Taarray] = "H";
- mangleChar[Tpointer] = "P";
- mangleChar[Treference] = "R";
- mangleChar[Tfunction] = "F";
- mangleChar[Tident] = "I";
- mangleChar[Tclass] = "C";
- mangleChar[Tstruct] = "S";
- mangleChar[Tenum] = "E";
- mangleChar[Tdelegate] = "D";
-
- mangleChar[Tnone] = "n";
- mangleChar[Tvoid] = "v";
- mangleChar[Tint8] = "g";
- mangleChar[Tuns8] = "h";
- mangleChar[Tint16] = "s";
- mangleChar[Tuns16] = "t";
- mangleChar[Tint32] = "i";
- mangleChar[Tuns32] = "k";
- mangleChar[Tint64] = "l";
- mangleChar[Tuns64] = "m";
- mangleChar[Tint128] = "zi";
- mangleChar[Tuns128] = "zk";
- mangleChar[Tfloat32] = "f";
- mangleChar[Tfloat64] = "d";
- mangleChar[Tfloat80] = "e";
-
- mangleChar[Timaginary32] = "o";
- mangleChar[Timaginary64] = "p";
- mangleChar[Timaginary80] = "j";
- mangleChar[Tcomplex32] = "q";
- mangleChar[Tcomplex64] = "r";
- mangleChar[Tcomplex80] = "c";
-
- mangleChar[Tbool] = "b";
- mangleChar[Tchar] = "a";
- mangleChar[Twchar] = "u";
- mangleChar[Tdchar] = "w";
-
- // '@' shouldn't appear anywhere in the deco'd names
- mangleChar[Tinstance] = "@";
- mangleChar[Terror] = "@";
- mangleChar[Ttypeof] = "@";
- mangleChar[Ttuple] = "B";
- mangleChar[Tslice] = "@";
- mangleChar[Treturn] = "@";
- mangleChar[Tvector] = "@";
- mangleChar[Ttraits] = "@";
- mangleChar[Tmixin] = "@";
- mangleChar[Tnoreturn] = "@"; // becomes 'Nn'
-
- mangleChar[Tnull] = "n"; // same as TypeNone
-
- for (size_t i = 0; i < TMAX; i++)
- {
- if (!mangleChar[i])
- fprintf(stderr, "ty = %llu\n", (ulonglong)i);
- assert(mangleChar[i]);
- }
-}
-
-/*********************************
- * Mangling for mod.
- */
-void MODtoDecoBuffer(OutBuffer *buf, MOD mod)
-{
- switch (mod)
- {
- case 0:
- break;
- case MODconst:
- buf->writeByte('x');
- break;
- case MODimmutable:
- buf->writeByte('y');
- break;
- case MODshared:
- buf->writeByte('O');
- break;
- case MODshared | MODconst:
- buf->writestring("Ox");
- break;
- case MODwild:
- buf->writestring("Ng");
- break;
- case MODwildconst:
- buf->writestring("Ngx");
- break;
- case MODshared | MODwild:
- buf->writestring("ONg");
- break;
- case MODshared | MODwildconst:
- buf->writestring("ONgx");
- break;
- default:
- assert(0);
- }
-}
-
-class Mangler : public Visitor
-{
-public:
- AA *types;
- AA *idents;
- OutBuffer *buf;
-
- Mangler(OutBuffer *buf)
- {
- this->types = NULL;
- this->idents = NULL;
- this->buf = buf;
- }
-
- /**
- * writes a back reference with the relative position encoded with base 26
- * using upper case letters for all digits but the last digit which uses
- * a lower case letter.
- * The decoder has to look up the referenced position to determine
- * whether the back reference is an identifier (starts with a digit)
- * or a type (starts with a letter).
- *
- * Params:
- * pos = relative position to encode
- */
- void writeBackRef(size_t pos)
- {
- buf->writeByte('Q');
- const size_t base = 26;
- size_t mul = 1;
- while (pos >= mul * base)
- mul *= base;
- while (mul >= base)
- {
- unsigned char dig = (unsigned char)(pos / mul);
- buf->writeByte('A' + dig);
- pos -= dig * mul;
- mul /= base;
- }
- buf->writeByte('a' + (unsigned char)pos);
- }
-
- /**
- * Back references a non-basic type
- *
- * The encoded mangling is
- * 'Q' <relative position of first occurrence of type>
- *
- * Params:
- * t = the type to encode via back referencing
- *
- * Returns:
- * true if the type was found. A back reference has been encoded.
- * false if the type was not found. The current position is saved for later back references.
- */
- bool backrefType(Type *t)
- {
- if (!t->isTypeBasic())
- {
- size_t *p = (size_t *)dmd_aaGet(&types, (void *)t);
- if (*p)
- {
- writeBackRef(buf->length() - *p);
- return true;
- }
- *p = buf->length();
- }
- return false;
- }
-
- /**
- * Back references a single identifier
- *
- * The encoded mangling is
- * 'Q' <relative position of first occurrence of type>
- *
- * Params:
- * id = the identifier to encode via back referencing
- *
- * Returns:
- * true if the identifier was found. A back reference has been encoded.
- * false if the identifier was not found. The current position is saved for later back references.
- */
- bool backrefIdentifier(Identifier *id)
- {
- size_t *p = (size_t *)dmd_aaGet(&idents, (void *)id);
- if (*p)
- {
- writeBackRef(buf->length() - *p);
- return true;
- }
- *p = buf->length();
- return false;
- }
-
- void mangleSymbol(Dsymbol *s)
- {
- s->accept(this);
- }
-
- void mangleType(Type *t)
- {
- if (!backrefType(t))
- t->accept(this);
- }
-
- void mangleIdentifier(Identifier *id, Dsymbol *s)
- {
- if (!backrefIdentifier(id))
- toBuffer(id->toChars(), s);
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- /**************************************************
- * Type mangling
- */
-
- void visitWithMask(Type *t, unsigned char modMask)
- {
- if (modMask != t->mod)
- {
- MODtoDecoBuffer(buf, t->mod);
- }
- mangleType(t);
- }
-
- void visit(Type *t)
- {
- buf->writestring(mangleChar[t->ty]);
- }
-
- void visit(TypeNext *t)
- {
- visit((Type *)t);
- visitWithMask(t->next, t->mod);
- }
-
- void visit(TypeVector *t)
- {
- buf->writestring("Nh");
- visitWithMask(t->basetype, t->mod);
- }
-
- void visit(TypeSArray *t)
- {
- visit((Type *)t);
- if (t->dim)
- buf->print(t->dim->toInteger());
- if (t->next)
- visitWithMask(t->next, t->mod);
- }
-
- void visit(TypeDArray *t)
- {
- visit((Type *)t);
- if (t->next)
- visitWithMask(t->next, t->mod);
- }
-
- void visit(TypeAArray *t)
- {
- visit((Type *)t);
- visitWithMask(t->index, 0);
- visitWithMask(t->next, t->mod);
- }
-
- void visit(TypeFunction *t)
- {
- //printf("TypeFunction::toDecoBuffer() t = %p %s\n", t, t->toChars());
- //static int nest; if (++nest == 50) *(char*)0=0;
-
- mangleFuncType(t, t, t->mod, t->next);
- }
-
- void mangleFuncType(TypeFunction *t, TypeFunction *ta, unsigned char modMask, Type *tret)
- {
- //printf("mangleFuncType() %s\n", t->toChars());
- if (t->inuse && tret)
- {
- // printf("TypeFunction.mangleFuncType() t = %s inuse\n", t->toChars());
- t->inuse = 2; // flag error to caller
- return;
- }
- t->inuse++;
-
- if (modMask != t->mod)
- MODtoDecoBuffer(buf, t->mod);
-
- unsigned char mc;
- switch (t->linkage)
- {
- case LINKd: mc = 'F'; break;
- case LINKc: mc = 'U'; break;
- case LINKwindows: mc = 'W'; break;
- case LINKcpp: mc = 'R'; break;
- case LINKobjc: mc = 'Y'; break;
- default:
- assert(0);
- }
- buf->writeByte(mc);
-
- if (ta->purity || ta->isnothrow || ta->isnogc || ta->isproperty || ta->isref || ta->trust || ta->isreturn || ta->isscope)
- {
- if (ta->purity)
- buf->writestring("Na");
- if (ta->isnothrow)
- buf->writestring("Nb");
- if (ta->isref)
- buf->writestring("Nc");
- if (ta->isproperty)
- buf->writestring("Nd");
- if (ta->isnogc)
- buf->writestring("Ni");
- if (ta->isreturn)
- buf->writestring("Nj");
- if (ta->isscope && !ta->isreturn && !ta->isscopeinferred)
- buf->writestring("Nl");
- switch (ta->trust)
- {
- case TRUSTtrusted:
- buf->writestring("Ne");
- break;
- case TRUSTsafe:
- buf->writestring("Nf");
- break;
- default:
- break;
- }
- }
-
- // Write argument types
- paramsToDecoBuffer(t->parameterList.parameters);
- //if (buf->slice().ptr[buf->length() - 1] == '@') halt();
- buf->writeByte('Z' - t->parameterList.varargs); // mark end of arg list
- if (tret != NULL)
- visitWithMask(tret, 0);
-
- t->inuse--;
- }
-
- void visit(TypeIdentifier *t)
- {
- visit((Type *)t);
- const char *name = t->ident->toChars();
- size_t len = strlen(name);
- buf->print(len);
- buf->writestring(name);
- }
-
- void visit(TypeEnum *t)
- {
- visit((Type *)t);
- mangleSymbol(t->sym);
- }
-
- void visit(TypeStruct *t)
- {
- //printf("TypeStruct::toDecoBuffer('%s') = '%s'\n", t->toChars(), name);
- visit((Type *)t);
- mangleSymbol(t->sym);
- }
-
- void visit(TypeClass *t)
- {
- //printf("TypeClass::toDecoBuffer('%s' mod=%x) = '%s'\n", t->toChars(), mod, name);
- visit((Type *)t);
- mangleSymbol(t->sym);
- }
-
- void visit(TypeTuple *t)
- {
- //printf("TypeTuple::toDecoBuffer() t = %p, %s\n", t, t->toChars());
- visit((Type *)t);
- paramsToDecoBuffer(t->arguments);
- buf->writeByte('Z');
- }
-
- void visit(TypeNull *t)
- {
- visit((Type *)t);
- }
-
- void visit(TypeNoreturn *)
- {
- buf->writestring("Nn");
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- void mangleDecl(Declaration *sthis)
- {
- mangleParent(sthis);
-
- assert(sthis->ident);
- mangleIdentifier(sthis->ident, sthis);
- if (FuncDeclaration *fd = sthis->isFuncDeclaration())
- {
- mangleFunc(fd, false);
- }
- else if (sthis->type)
- {
- visitWithMask(sthis->type, 0);
- }
- else
- assert(0);
- }
-
- void mangleParent(Dsymbol *s)
- {
- Dsymbol *p;
- if (TemplateInstance *ti = s->isTemplateInstance())
- p = ti->isTemplateMixin() ? ti->parent : ti->tempdecl->parent;
- else
- p = s->parent;
-
- if (p)
- {
- mangleParent(p);
- TemplateInstance *ti = p->isTemplateInstance();
- if (ti && !ti->isTemplateMixin())
- {
- mangleTemplateInstance(ti);
- }
- else if (p->getIdent())
- {
- mangleIdentifier(p->ident, s);
- if (FuncDeclaration *f = p->isFuncDeclaration())
- mangleFunc(f, true);
- }
- else
- buf->writeByte('0');
- }
- }
-
- void mangleFunc(FuncDeclaration *fd, bool inParent)
- {
- //printf("deco = '%s'\n", fd->type->deco ? fd->type->deco : "null");
- //printf("fd->type = %s\n", fd->type->toChars());
- if (fd->needThis() || fd->isNested())
- buf->writeByte('M');
- if (inParent)
- {
- TypeFunction *tf = (TypeFunction *)fd->type;
- TypeFunction *tfo = (TypeFunction *)fd->originalType;
- mangleFuncType(tf, tfo, 0, NULL);
- }
- else if (fd->type)
- {
- visitWithMask(fd->type, 0);
- }
- else
- {
- printf("[%s] %s no type\n", fd->loc.toChars(), fd->toChars());
- assert(0); // don't mangle function until semantic3 done.
- }
- }
-
- /************************************************************
- * Write length prefixed string to buf.
- */
- void toBuffer(const char *id, Dsymbol *s)
- {
- size_t len = strlen(id);
- if (buf->length() + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone
- s->error("excessive length %llu for symbol, possible recursive expansion?", buf->length() + len);
- else
- {
- buf->print(len);
- buf->write(id, len);
- }
- }
-
- static const char *externallyMangledIdentifier(Declaration *d)
- {
- if (!d->parent || d->parent->isModule() || d->linkage == LINKcpp) // if at global scope
- {
- switch (d->linkage)
- {
- case LINKd:
- break;
- case LINKc:
- case LINKwindows:
- case LINKobjc:
- return d->ident->toChars();
- case LINKcpp:
- return target.cpp.toMangle(d);
- case LINKdefault:
- d->error("forward declaration");
- return d->ident->toChars();
- default:
- fprintf(stderr, "'%s', linkage = %d\n", d->toChars(), d->linkage);
- assert(0);
- }
- }
- return NULL;
- }
-
- void visit(Declaration *d)
- {
- //printf("Declaration::mangle(this = %p, '%s', parent = '%s', linkage = %d)\n",
- // d, d->toChars(), d->parent ? d->parent->toChars() : "null", d->linkage);
- if (const char *id = externallyMangledIdentifier(d))
- {
- buf->writestring(id);
- return;
- }
- buf->writestring("_D");
- mangleDecl(d);
- }
-
- /******************************************************************************
- * Normally FuncDeclaration and FuncAliasDeclaration have overloads.
- * If and only if there is no overloads, mangle() could return
- * exact mangled name.
- *
- * module test;
- * void foo(long) {} // _D4test3fooFlZv
- * void foo(string) {} // _D4test3fooFAyaZv
- *
- * // from FuncDeclaration::mangle().
- * pragma(msg, foo.mangleof); // prints unexact mangled name "4test3foo"
- * // by calling Dsymbol::mangle()
- *
- * // from FuncAliasDeclaration::mangle()
- * pragma(msg, __traits(getOverloads, test, "foo")[0].mangleof); // "_D4test3fooFlZv"
- * pragma(msg, __traits(getOverloads, test, "foo")[1].mangleof); // "_D4test3fooFAyaZv"
- *
- * If a function has no overloads, .mangleof property still returns exact mangled name.
- *
- * void bar() {}
- * pragma(msg, bar.mangleof); // still prints "_D4test3barFZv"
- * // by calling FuncDeclaration::mangleExact().
- */
- void visit(FuncDeclaration *fd)
- {
- if (fd->isUnique())
- mangleExact(fd);
- else
- visit((Dsymbol *)fd);
- }
-
- // ditto
- void visit(FuncAliasDeclaration *fd)
- {
- FuncDeclaration *f = fd->toAliasFunc();
- FuncAliasDeclaration *fa = f->isFuncAliasDeclaration();
- if (!fd->hasOverloads && !fa)
- {
- mangleExact(f);
- return;
- }
- if (fa)
- {
- mangleSymbol(fa);
- return;
- }
- visit((Dsymbol *)fd);
- }
-
- void visit(OverDeclaration *od)
- {
- if (od->overnext)
- {
- visit((Dsymbol *)od);
- return;
- }
-
- if (FuncDeclaration *fd = od->aliassym->isFuncDeclaration())
- {
- if (!od->hasOverloads || fd->isUnique())
- {
- mangleExact(fd);
- return;
- }
- }
- if (TemplateDeclaration *td = od->aliassym->isTemplateDeclaration())
- {
- if (!od->hasOverloads || td->overnext == NULL)
- {
- mangleSymbol(td);
- return;
- }
- }
- visit((Dsymbol *)od);
- }
-
- void mangleExact(FuncDeclaration *fd)
- {
- assert(!fd->isFuncAliasDeclaration());
-
- if (fd->mangleOverride.length)
- {
- buf->writestring(fd->mangleOverride.ptr);
- return;
- }
-
- if (fd->isMain())
- {
- buf->writestring("_Dmain");
- return;
- }
-
- if (fd->isWinMain() || fd->isDllMain() || fd->ident == Id::tls_get_addr)
- {
- buf->writestring(fd->ident->toChars());
- return;
- }
-
- visit((Declaration *)fd);
- }
-
- void visit(VarDeclaration *vd)
- {
- if (vd->mangleOverride.length)
- {
- buf->writestring(vd->mangleOverride.ptr);
- return;
- }
-
- visit((Declaration *)vd);
- }
-
- void visit(AggregateDeclaration *ad)
- {
- ClassDeclaration *cd = ad->isClassDeclaration();
- Dsymbol *parentsave = ad->parent;
- if (cd)
- {
- /* These are reserved to the compiler, so keep simple
- * names for them.
- */
- if ((cd->ident == Id::Exception && cd->parent->ident == Id::object) ||
- cd->ident == Id::TypeInfo ||
- cd->ident == Id::TypeInfo_Struct ||
- cd->ident == Id::TypeInfo_Class ||
- cd->ident == Id::TypeInfo_Tuple ||
- cd == ClassDeclaration::object ||
- cd == Type::typeinfoclass ||
- cd == Module::moduleinfo ||
- strncmp(cd->ident->toChars(), "TypeInfo_", 9) == 0)
- {
- // Don't mangle parent
- ad->parent = NULL;
- }
- }
-
- visit((Dsymbol *)ad);
-
- ad->parent = parentsave;
- }
-
- void visit(TemplateInstance *ti)
- {
- if (!ti->tempdecl)
- ti->error("is not defined");
- else
- mangleParent(ti);
-
- if (ti->isTemplateMixin() && ti->ident)
- mangleIdentifier(ti->ident, ti);
- else
- mangleTemplateInstance(ti);
- }
-
- void mangleTemplateInstance(TemplateInstance *ti)
- {
- TemplateDeclaration *tempdecl = ti->tempdecl->isTemplateDeclaration();
- assert(tempdecl);
-
- // Use "__U" for the symbols declared inside template constraint.
- const char T = ti->members ? 'T' : 'U';
- buf->printf("__%c", T);
- mangleIdentifier(tempdecl->ident, tempdecl);
-
- Objects *args = ti->tiargs;
- size_t nparams = tempdecl->parameters->length - (tempdecl->isVariadic() ? 1 : 0);
- for (size_t i = 0; i < args->length; i++)
- {
- RootObject *o = (*args)[i];
- Type *ta = isType(o);
- Expression *ea = isExpression(o);
- Dsymbol *sa = isDsymbol(o);
- Tuple *va = isTuple(o);
- //printf("\to [%d] %p ta %p ea %p sa %p va %p\n", i, o, ta, ea, sa, va);
- if (i < nparams && (*tempdecl->parameters)[i]->specialization())
- buf->writeByte('H'); // https://issues.dlang.org/show_bug.cgi?id=6574
- if (ta)
- {
- buf->writeByte('T');
- visitWithMask(ta, 0);
- }
- else if (ea)
- {
- // Don't interpret it yet, it might actually be an alias template parameter.
- // Only constfold manifest constants, not const/immutable lvalues, see https://issues.dlang.org/show_bug.cgi?id=17339.
- const bool keepLvalue = true;
- ea = ea->optimize(WANTvalue, keepLvalue);
- if (ea->op == TOKvar)
- {
- sa = ((VarExp *)ea)->var;
- ea = NULL;
- goto Lsa;
- }
- if (ea->op == TOKthis)
- {
- sa = ((ThisExp *)ea)->var;
- ea = NULL;
- goto Lsa;
- }
- if (ea->op == TOKfunction)
- {
- if (((FuncExp *)ea)->td)
- sa = ((FuncExp *)ea)->td;
- else
- sa = ((FuncExp *)ea)->fd;
- ea = NULL;
- goto Lsa;
- }
- buf->writeByte('V');
- if (ea->op == TOKtuple)
- {
- ea->error("tuple is not a valid template value argument");
- continue;
- }
- // Now that we know it is not an alias, we MUST obtain a value
- unsigned olderr = global.errors;
- ea = ea->ctfeInterpret();
- if (ea->op == TOKerror || olderr != global.errors)
- continue;
-
- /* Use type mangling that matches what it would be for a function parameter
- */
- visitWithMask(ea->type, 0);
- ea->accept(this);
- }
- else if (sa)
- {
- Lsa:
- sa = sa->toAlias();
- if (Declaration *d = sa->isDeclaration())
- {
- if (FuncAliasDeclaration *fad = d->isFuncAliasDeclaration())
- d = fad->toAliasFunc();
- if (d->mangleOverride.length)
- {
- buf->writeByte('X');
- toBuffer(d->mangleOverride.ptr, d);
- continue;
- }
- if (const char *id = externallyMangledIdentifier(d))
- {
- buf->writeByte('X');
- toBuffer(id, d);
- continue;
- }
- if (!d->type || !d->type->deco)
- {
- ti->error("forward reference of %s %s", d->kind(), d->toChars());
- continue;
- }
- }
- buf->writeByte('S');
- mangleSymbol(sa);
- }
- else if (va)
- {
- assert(i + 1 == args->length); // must be last one
- args = &va->objects;
- i = -(size_t)1;
- }
- else
- assert(0);
- }
- buf->writeByte('Z');
- }
-
- void visit(Dsymbol *s)
- {
- mangleParent(s);
- if (s->ident)
- mangleIdentifier(s->ident, s);
- else
- toBuffer(s->toChars(), s);
- //printf("Dsymbol::mangle() %s = %s\n", s->toChars(), id);
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- void visit(Expression *e)
- {
- e->error("expression %s is not a valid template value argument", e->toChars());
- }
-
- void visit(IntegerExp *e)
- {
- if ((sinteger_t)e->value < 0)
- {
- buf->writeByte('N');
- buf->print(-e->value);
- }
- else
- {
- buf->writeByte('i');
- buf->print(e->value);
- }
- }
-
- void visit(RealExp *e)
- {
- buf->writeByte('e');
- realToMangleBuffer(e->value);
- }
-
- void realToMangleBuffer(real_t value)
- {
- /* Rely on %A to get portable mangling.
- * Must munge result to get only identifier characters.
- *
- * Possible values from %A => mangled result
- * NAN => NAN
- * -INF => NINF
- * INF => INF
- * -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79
- * 0X1.9P+2 => 19P2
- */
-
- if (CTFloat::isNaN(value))
- buf->writestring("NAN"); // no -NAN bugs
- else if (CTFloat::isInfinity(value))
- buf->writestring(value < CTFloat::zero ? "NINF" : "INF");
- else
- {
- const size_t BUFFER_LEN = 36;
- char buffer[BUFFER_LEN];
- size_t n = CTFloat::sprint(buffer, 'A', value);
- assert(n < BUFFER_LEN);
- for (size_t i = 0; i < n; i++)
- {
- char c = buffer[i];
- switch (c)
- {
- case '-':
- buf->writeByte('N');
- break;
-
- case '+':
- case 'X':
- case '.':
- break;
-
- case '0':
- if (i < 2)
- break; // skip leading 0X
- /* fall through */
- default:
- buf->writeByte(c);
- break;
- }
- }
- }
- }
-
- void visit(ComplexExp *e)
- {
- buf->writeByte('c');
- realToMangleBuffer(e->toReal());
- buf->writeByte('c'); // separate the two
- realToMangleBuffer(e->toImaginary());
- }
-
- void visit(NullExp *)
- {
- buf->writeByte('n');
- }
-
- void visit(StringExp *e)
- {
- char m;
- OutBuffer tmp;
- utf8_t *q;
- size_t qlen;
-
- /* Write string in UTF-8 format
- */
- switch (e->sz)
- {
- case 1:
- m = 'a';
- q = (utf8_t *)e->string;
- qlen = e->len;
- break;
-
- case 2:
- m = 'w';
- for (size_t u = 0; u < e->len; )
- {
- unsigned c;
- const char *p = utf_decodeWchar((unsigned short *)e->string, e->len, &u, &c);
- if (p)
- e->error("%s", p);
- else
- tmp.writeUTF8(c);
- }
- q = (utf8_t *)tmp.slice().ptr;
- qlen = tmp.length();
- break;
-
- case 4:
- m = 'd';
- for (size_t u = 0; u < e->len; u++)
- {
- unsigned c = ((unsigned *)e->string)[u];
- if (!utf_isValidDchar(c))
- e->error("invalid UCS-32 char \\U%08x", c);
- else
- tmp.writeUTF8(c);
- }
- q = (utf8_t *)tmp.slice().ptr;
- qlen = tmp.length();
- break;
-
- default:
- assert(0);
- }
- buf->reserve(1 + 11 + 2 * qlen);
- buf->writeByte(m);
- buf->print(qlen);
- buf->writeByte('_'); // nbytes <= 11
-
- for (utf8_t *p = (utf8_t *)buf->slice().ptr + buf->length(), *pend = p + 2 * qlen;
- p < pend; p += 2, ++q)
- {
- utf8_t hi = *q >> 4 & 0xF;
- p[0] = (utf8_t)(hi < 10 ? hi + '0' : hi - 10 + 'a');
- utf8_t lo = *q & 0xF;
- p[1] = (utf8_t)(lo < 10 ? lo + '0' : lo - 10 + 'a');
- }
- buf->setsize(buf->length() + 2 * qlen);
- }
-
- void visit(ArrayLiteralExp *e)
- {
- size_t dim = e->elements ? e->elements->length : 0;
- buf->writeByte('A');
- buf->print(dim);
- for (size_t i = 0; i < dim; i++)
- {
- e->getElement(i)->accept(this);
- }
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- size_t dim = e->keys->length;
- buf->writeByte('A');
- buf->print(dim);
- for (size_t i = 0; i < dim; i++)
- {
- (*e->keys)[i]->accept(this);
- (*e->values)[i]->accept(this);
- }
- }
-
- void visit(StructLiteralExp *e)
- {
- size_t dim = e->elements ? e->elements->length : 0;
- buf->writeByte('S');
- buf->print(dim);
- for (size_t i = 0; i < dim; i++)
- {
- Expression *ex = (*e->elements)[i];
- if (ex)
- ex->accept(this);
- else
- buf->writeByte('v'); // 'v' for void
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- void paramsToDecoBuffer(Parameters *parameters)
- {
- //printf("Parameter::paramsToDecoBuffer()\n");
- Parameter_foreach(parameters, &paramsToDecoBufferDg, (void *)this);
- }
-
- static int paramsToDecoBufferDg(void *ctx, size_t, Parameter *p)
- {
- p->accept((Visitor *)ctx);
- return 0;
- }
-
- void visit(Parameter *p)
- {
- if (p->storageClass & STCscope && !(p->storageClass & STCscopeinferred))
- buf->writeByte('M');
- // 'return inout ref' is the same as 'inout ref'
- if ((p->storageClass & (STCreturn | STCwild)) == STCreturn)
- buf->writestring("Nk");
- switch (p->storageClass & (STCin | STCout | STCref | STClazy))
- {
- case 0:
- case STCin:
- break;
- case STCout:
- buf->writeByte('J');
- break;
- case STCref:
- buf->writeByte('K');
- break;
- case STClazy:
- buf->writeByte('L');
- break;
- default:
- assert(0);
- }
- visitWithMask(p->type, 0);
- }
-};
-
-/******************************************************************************
- * Returns exact mangled name of function.
- */
-const char *mangleExact(FuncDeclaration *fd)
-{
- if (!fd->mangleString)
- {
- OutBuffer buf;
- Mangler v(&buf);
- v.mangleExact(fd);
- fd->mangleString = buf.extractChars();
- }
- return fd->mangleString;
-}
-
-void mangleToBuffer(Type *t, OutBuffer *buf)
-{
- Mangler v(buf);
- v.visitWithMask(t, 0);
-}
-
-void mangleToBuffer(Expression *e, OutBuffer *buf)
-{
- Mangler v(buf);
- e->accept(&v);
-}
-
-void mangleToBuffer(Dsymbol *s, OutBuffer *buf)
-{
- Mangler v(buf);
- s->accept(&v);
-}
-
-void mangleToBuffer(TemplateInstance *ti, OutBuffer *buf)
-{
- Mangler v(buf);
- v.mangleTemplateInstance(ti);
-}
-
-/**********************************************
- * Convert a string representing a type (the deco) and
- * return its equivalent Type.
- * Params:
- * deco = string containing the deco
- * Returns:
- * null for failed to convert
- * Type for succeeded
- */
-
-Type *decoToType(const char *deco)
-{
- if (!deco)
- return NULL;
-
- //printf("decoToType(): %s\n", deco)
- if (StringValue *sv = Type::stringtable.lookup(deco, strlen(deco)))
- {
- if (sv->ptrvalue)
- {
- Type *t = (Type *)sv->ptrvalue;
- assert(t->deco);
- return t;
- }
- }
- return NULL;
-}
diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d
new file mode 100644
index 00000000000..71b8c7a6098
--- /dev/null
+++ b/gcc/d/dmd/dmangle.d
@@ -0,0 +1,1297 @@
+/**
+ * Does name mangling for `extern(D)` symbols.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/abi.html#name_mangling, Name Mangling)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmangle.d, _dmangle.d)
+ * Documentation: https://dlang.org/phobos/dmd_dmangle.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmangle.d
+ * References: https://dlang.org/blog/2017/12/20/ds-newfangled-name-mangling/
+ */
+
+module dmd.dmangle;
+
+import dmd.astenums;
+
+/******************************************************************************
+ * Returns exact mangled name of function.
+ */
+extern (C++) const(char)* mangleExact(FuncDeclaration fd)
+{
+ if (!fd.mangleString)
+ {
+ OutBuffer buf;
+ scope Mangler v = new Mangler(&buf);
+ v.mangleExact(fd);
+ fd.mangleString = buf.extractChars();
+ }
+ return fd.mangleString;
+}
+
+extern (C++) void mangleToBuffer(Type t, OutBuffer* buf)
+{
+ if (t.deco)
+ buf.writestring(t.deco);
+ else
+ {
+ scope Mangler v = new Mangler(buf, t);
+ v.visitWithMask(t, 0);
+ }
+}
+
+extern (C++) void mangleToBuffer(Expression e, OutBuffer* buf)
+{
+ scope Mangler v = new Mangler(buf);
+ e.accept(v);
+}
+
+extern (C++) void mangleToBuffer(Dsymbol s, OutBuffer* buf)
+{
+ scope Mangler v = new Mangler(buf);
+ s.accept(v);
+}
+
+extern (C++) void mangleToBuffer(TemplateInstance ti, OutBuffer* buf)
+{
+ scope Mangler v = new Mangler(buf);
+ v.mangleTemplateInstance(ti);
+}
+
+/// Returns: `true` if the given character is a valid mangled character
+package bool isValidMangling(dchar c) nothrow
+{
+ return
+ c >= 'A' && c <= 'Z' ||
+ c >= 'a' && c <= 'z' ||
+ c >= '0' && c <= '9' ||
+ c != 0 && strchr("$%().:?@[]_", c) ||
+ isUniAlpha(c);
+}
+
+// valid mangled characters
+unittest
+{
+ assert('a'.isValidMangling);
+ assert('B'.isValidMangling);
+ assert('2'.isValidMangling);
+ assert('@'.isValidMangling);
+ assert('_'.isValidMangling);
+}
+
+// invalid mangled characters
+unittest
+{
+ assert(!'-'.isValidMangling);
+ assert(!0.isValidMangling);
+ assert(!'/'.isValidMangling);
+ assert(!'\\'.isValidMangling);
+}
+
+/**********************************************
+ * Convert a string representing a type (the deco) and
+ * return its equivalent Type.
+ * Params:
+ * deco = string containing the deco
+ * Returns:
+ * null for failed to convert
+ * Type for succeeded
+ */
+
+public Type decoToType(const(char)[] deco)
+{
+ //printf("decoToType(): %.*s\n", cast(int)deco.length, deco.ptr);
+ if (auto sv = Type.stringtable.lookup(deco))
+ {
+ if (sv.value)
+ {
+ Type t = cast(Type)sv.value;
+ assert(t.deco);
+ return t;
+ }
+ }
+ return null;
+}
+
+
+/***************************************** private ***************************************/
+
+private:
+
+
+import core.stdc.ctype;
+import core.stdc.stdio;
+import core.stdc.string;
+
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dmodule;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.root.ctfloat;
+import dmd.root.outbuffer;
+import dmd.root.aav;
+import dmd.root.string;
+import dmd.root.stringtable;
+import dmd.target;
+import dmd.tokens;
+import dmd.utf;
+import dmd.visitor;
+
+private immutable char[TMAX] mangleChar =
+[
+ Tchar : 'a',
+ Tbool : 'b',
+ Tcomplex80 : 'c',
+ Tfloat64 : 'd',
+ Tfloat80 : 'e',
+ Tfloat32 : 'f',
+ Tint8 : 'g',
+ Tuns8 : 'h',
+ Tint32 : 'i',
+ Timaginary80 : 'j',
+ Tuns32 : 'k',
+ Tint64 : 'l',
+ Tuns64 : 'm',
+ Tnull : 'n',
+ Timaginary32 : 'o',
+ Timaginary64 : 'p',
+ Tcomplex32 : 'q',
+ Tcomplex64 : 'r',
+ Tint16 : 's',
+ Tuns16 : 't',
+ Twchar : 'u',
+ Tvoid : 'v',
+ Tdchar : 'w',
+ // x // const
+ // y // immutable
+ Tint128 : 'z', // zi
+ Tuns128 : 'z', // zk
+
+ Tarray : 'A',
+ Ttuple : 'B',
+ Tclass : 'C',
+ Tdelegate : 'D',
+ Tenum : 'E',
+ Tfunction : 'F', // D function
+ Tsarray : 'G',
+ Taarray : 'H',
+ // I // in
+ // J // out
+ // K // ref
+ // L // lazy
+ // M // has this, or scope
+ // N // Nh:vector Ng:wild Nn:noreturn
+ // O // shared
+ Tpointer : 'P',
+ // Q // Type/symbol/identifier backward reference
+ Treference : 'R',
+ Tstruct : 'S',
+ // T // Ttypedef
+ // U // C function
+ // W // Windows function
+ // X // variadic T t...)
+ // Y // variadic T t,...)
+ // Z // not variadic, end of parameters
+
+ // '@' shouldn't appear anywhere in the deco'd names
+ Tnone : '@',
+ Tident : '@',
+ Tinstance : '@',
+ Terror : '@',
+ Ttypeof : '@',
+ Tslice : '@',
+ Treturn : '@',
+ Tvector : '@',
+ Ttraits : '@',
+ Tmixin : '@',
+ Ttag : '@',
+ Tnoreturn : '@', // becomes 'Nn'
+];
+
+unittest
+{
+ foreach (i, mangle; mangleChar)
+ {
+ if (mangle == char.init)
+ {
+ fprintf(stderr, "ty = %u\n", cast(uint)i);
+ assert(0);
+ }
+ }
+}
+
+/***********************
+ * Mangle basic type ty to buf.
+ */
+
+private void tyToDecoBuffer(OutBuffer* buf, int ty)
+{
+ const c = mangleChar[ty];
+ buf.writeByte(c);
+ if (c == 'z')
+ buf.writeByte(ty == Tint128 ? 'i' : 'k');
+}
+
+/*********************************
+ * Mangling for mod.
+ */
+private void MODtoDecoBuffer(OutBuffer* buf, MOD mod)
+{
+ switch (mod)
+ {
+ case 0:
+ break;
+ case MODFlags.const_:
+ buf.writeByte('x');
+ break;
+ case MODFlags.immutable_:
+ buf.writeByte('y');
+ break;
+ case MODFlags.shared_:
+ buf.writeByte('O');
+ break;
+ case MODFlags.shared_ | MODFlags.const_:
+ buf.writestring("Ox");
+ break;
+ case MODFlags.wild:
+ buf.writestring("Ng");
+ break;
+ case MODFlags.wildconst:
+ buf.writestring("Ngx");
+ break;
+ case MODFlags.shared_ | MODFlags.wild:
+ buf.writestring("ONg");
+ break;
+ case MODFlags.shared_ | MODFlags.wildconst:
+ buf.writestring("ONgx");
+ break;
+ default:
+ assert(0);
+ }
+}
+
+private extern (C++) final class Mangler : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ static assert(Key.sizeof == size_t.sizeof);
+ AssocArray!(Type, size_t) types; // Type => (offset+1) in buf
+ AssocArray!(Identifier, size_t) idents; // Identifier => (offset+1) in buf
+ OutBuffer* buf;
+ Type rootType;
+
+ extern (D) this(OutBuffer* buf, Type rootType = null)
+ {
+ this.buf = buf;
+ this.rootType = rootType;
+ }
+
+ /**
+ * writes a back reference with the relative position encoded with base 26
+ * using upper case letters for all digits but the last digit which uses
+ * a lower case letter.
+ * The decoder has to look up the referenced position to determine
+ * whether the back reference is an identifier (starts with a digit)
+ * or a type (starts with a letter).
+ *
+ * Params:
+ * pos = relative position to encode
+ */
+ void writeBackRef(size_t pos)
+ {
+ buf.writeByte('Q');
+ enum base = 26;
+ size_t mul = 1;
+ while (pos >= mul * base)
+ mul *= base;
+ while (mul >= base)
+ {
+ auto dig = cast(ubyte)(pos / mul);
+ buf.writeByte('A' + dig);
+ pos -= dig * mul;
+ mul /= base;
+ }
+ buf.writeByte('a' + cast(ubyte)pos);
+ }
+
+ /**
+ * Back references a non-basic type
+ *
+ * The encoded mangling is
+ * 'Q' <relative position of first occurrence of type>
+ *
+ * Params:
+ * t = the type to encode via back referencing
+ *
+ * Returns:
+ * true if the type was found. A back reference has been encoded.
+ * false if the type was not found. The current position is saved for later back references.
+ */
+ bool backrefType(Type t)
+ {
+ if (t.isTypeBasic())
+ return false;
+
+ /**
+ * https://issues.dlang.org/show_bug.cgi?id=21591
+ *
+ * Special case for unmerged TypeFunctions: use the generic merged
+ * function type as backref cache key to avoid missed backrefs.
+ *
+ * Merging is based on mangling, so we need to avoid an infinite
+ * recursion by excluding the case where `t` is the root type passed to
+ * `mangleToBuffer()`.
+ */
+ if (t != rootType)
+ {
+ if (t.isFunction_Delegate_PtrToFunction())
+ {
+ t = t.merge2();
+ }
+ }
+
+ return backrefImpl(types, t);
+ }
+
+ /**
+ * Back references a single identifier
+ *
+ * The encoded mangling is
+ * 'Q' <relative position of first occurrence of type>
+ *
+ * Params:
+ * id = the identifier to encode via back referencing
+ *
+ * Returns:
+ * true if the identifier was found. A back reference has been encoded.
+ * false if the identifier was not found. The current position is saved for later back references.
+ */
+ bool backrefIdentifier(Identifier id)
+ {
+ return backrefImpl(idents, id);
+ }
+
+ private extern(D) bool backrefImpl(T)(ref AssocArray!(T, size_t) aa, T key)
+ {
+ auto p = aa.getLvalue(key);
+ if (*p)
+ {
+ const offset = *p - 1;
+ writeBackRef(buf.length - offset);
+ return true;
+ }
+ *p = buf.length + 1;
+ return false;
+ }
+
+ void mangleSymbol(Dsymbol s)
+ {
+ s.accept(this);
+ }
+
+ void mangleType(Type t)
+ {
+ if (!backrefType(t))
+ t.accept(this);
+ }
+
+ void mangleIdentifier(Identifier id, Dsymbol s)
+ {
+ if (!backrefIdentifier(id))
+ toBuffer(id.toString(), s);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ /**************************************************
+ * Type mangling
+ */
+ void visitWithMask(Type t, ubyte modMask)
+ {
+ if (modMask != t.mod)
+ {
+ MODtoDecoBuffer(buf, t.mod);
+ }
+ mangleType(t);
+ }
+
+ override void visit(Type t)
+ {
+ tyToDecoBuffer(buf, t.ty);
+ }
+
+ override void visit(TypeNext t)
+ {
+ visit(cast(Type)t);
+ visitWithMask(t.next, t.mod);
+ }
+
+ override void visit(TypeVector t)
+ {
+ buf.writestring("Nh");
+ visitWithMask(t.basetype, t.mod);
+ }
+
+ override void visit(TypeSArray t)
+ {
+ visit(cast(Type)t);
+ if (t.dim)
+ buf.print(t.dim.toInteger());
+ if (t.next)
+ visitWithMask(t.next, t.mod);
+ }
+
+ override void visit(TypeDArray t)
+ {
+ visit(cast(Type)t);
+ if (t.next)
+ visitWithMask(t.next, t.mod);
+ }
+
+ override void visit(TypeAArray t)
+ {
+ visit(cast(Type)t);
+ visitWithMask(t.index, 0);
+ visitWithMask(t.next, t.mod);
+ }
+
+ override void visit(TypeFunction t)
+ {
+ //printf("TypeFunction.toDecoBuffer() t = %p %s\n", t, t.toChars());
+ //static int nest; if (++nest == 50) *(char*)0=0;
+ mangleFuncType(t, t, t.mod, t.next);
+ }
+
+ void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret)
+ {
+ //printf("mangleFuncType() %s\n", t.toChars());
+ if (t.inuse && tret)
+ {
+ // printf("TypeFunction.mangleFuncType() t = %s inuse\n", t.toChars());
+ t.inuse = 2; // flag error to caller
+ return;
+ }
+ t.inuse++;
+ if (modMask != t.mod)
+ MODtoDecoBuffer(buf, t.mod);
+
+ char mc;
+ final switch (t.linkage)
+ {
+ case LINK.default_:
+ case LINK.system:
+ case LINK.d:
+ mc = 'F';
+ break;
+ case LINK.c:
+ mc = 'U';
+ break;
+ case LINK.windows:
+ mc = 'W';
+ break;
+ case LINK.cpp:
+ mc = 'R';
+ break;
+ case LINK.objc:
+ mc = 'Y';
+ break;
+ }
+ buf.writeByte(mc);
+
+ if (ta.purity)
+ buf.writestring("Na");
+ if (ta.isnothrow)
+ buf.writestring("Nb");
+ if (ta.isref)
+ buf.writestring("Nc");
+ if (ta.isproperty)
+ buf.writestring("Nd");
+ if (ta.isnogc)
+ buf.writestring("Ni");
+
+ if (ta.isreturn && !ta.isreturninferred)
+ buf.writestring("Nj");
+ else if (ta.isScopeQual && !ta.isscopeinferred)
+ buf.writestring("Nl");
+
+ if (ta.islive)
+ buf.writestring("Nm");
+
+ switch (ta.trust)
+ {
+ case TRUST.trusted:
+ buf.writestring("Ne");
+ break;
+ case TRUST.safe:
+ buf.writestring("Nf");
+ break;
+ default:
+ break;
+ }
+
+ // Write argument types
+ foreach (idx, param; t.parameterList)
+ param.accept(this);
+ //if (buf.data[buf.length - 1] == '@') assert(0);
+ buf.writeByte('Z' - t.parameterList.varargs); // mark end of arg list
+ if (tret !is null)
+ visitWithMask(tret, 0);
+ t.inuse--;
+ }
+
+ override void visit(TypeIdentifier t)
+ {
+ visit(cast(Type)t);
+ auto name = t.ident.toString();
+ buf.print(cast(int)name.length);
+ buf.writestring(name);
+ }
+
+ override void visit(TypeEnum t)
+ {
+ visit(cast(Type)t);
+ mangleSymbol(t.sym);
+ }
+
+ override void visit(TypeStruct t)
+ {
+ //printf("TypeStruct.toDecoBuffer('%s') = '%s'\n", t.toChars(), name);
+ visit(cast(Type)t);
+ mangleSymbol(t.sym);
+ }
+
+ override void visit(TypeClass t)
+ {
+ //printf("TypeClass.toDecoBuffer('%s' mod=%x) = '%s'\n", t.toChars(), mod, name);
+ visit(cast(Type)t);
+ mangleSymbol(t.sym);
+ }
+
+ override void visit(TypeTuple t)
+ {
+ //printf("TypeTuple.toDecoBuffer() t = %p, %s\n", t, t.toChars());
+ visit(cast(Type)t);
+ Parameter._foreach(t.arguments, (idx, param) {
+ param.accept(this);
+ return 0;
+ });
+ buf.writeByte('Z');
+ }
+
+ override void visit(TypeNull t)
+ {
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeNoreturn t)
+ {
+ buf.writestring("Nn");
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ void mangleDecl(Declaration sthis)
+ {
+ mangleParent(sthis);
+ assert(sthis.ident);
+ mangleIdentifier(sthis.ident, sthis);
+ if (FuncDeclaration fd = sthis.isFuncDeclaration())
+ {
+ mangleFunc(fd, false);
+ }
+ else if (sthis.type)
+ {
+ visitWithMask(sthis.type, 0);
+ }
+ else
+ assert(0);
+ }
+
+ void mangleParent(Dsymbol s)
+ {
+ //printf("mangleParent() %s %s\n", s.kind(), s.toChars());
+ Dsymbol p;
+ if (TemplateInstance ti = s.isTemplateInstance())
+ p = ti.isTemplateMixin() ? ti.parent : ti.tempdecl.parent;
+ else
+ p = s.parent;
+ if (p)
+ {
+ uint localNum = s.localNum;
+ mangleParent(p);
+ auto ti = p.isTemplateInstance();
+ if (ti && !ti.isTemplateMixin())
+ {
+ localNum = ti.tempdecl.localNum;
+ mangleTemplateInstance(ti);
+ }
+ else if (p.getIdent())
+ {
+ mangleIdentifier(p.ident, s);
+ if (FuncDeclaration f = p.isFuncDeclaration())
+ mangleFunc(f, true);
+ }
+ else
+ buf.writeByte('0');
+
+ /* There can be multiple different declarations in the same
+ * function that have the same mangled name.
+ * This results in localNum having a non-zero number, which
+ * is used to add a fake parent of the form `__Sddd` to make
+ * the mangled names unique.
+ * https://issues.dlang.org/show_bug.cgi?id=20565
+ */
+ if (localNum)
+ {
+ uint ndigits = 1;
+ auto n = localNum;
+ while (n >= 10)
+ {
+ n /= 10;
+ ++ndigits;
+ }
+ buf.printf("%u__S%u", ndigits + 3, localNum);
+ }
+ }
+ }
+
+ void mangleFunc(FuncDeclaration fd, bool inParent)
+ {
+ //printf("deco = '%s'\n", fd.type.deco ? fd.type.deco : "null");
+ //printf("fd.type = %s\n", fd.type.toChars());
+ if (fd.needThis() || fd.isNested())
+ buf.writeByte('M');
+
+ if (!fd.type || fd.type.ty == Terror)
+ {
+ // never should have gotten here, but could be the result of
+ // failed speculative compilation
+ buf.writestring("9__error__FZ");
+
+ //printf("[%s] %s no type\n", fd.loc.toChars(), fd.toChars());
+ //assert(0); // don't mangle function until semantic3 done.
+ }
+ else if (inParent)
+ {
+ TypeFunction tf = fd.type.isTypeFunction();
+ TypeFunction tfo = fd.originalType.isTypeFunction();
+ mangleFuncType(tf, tfo, 0, null);
+ }
+ else
+ {
+ visitWithMask(fd.type, 0);
+ }
+ }
+
+ /************************************************************
+ * Write length prefixed string to buf.
+ */
+ extern (D) void toBuffer(const(char)[] id, Dsymbol s)
+ {
+ const len = id.length;
+ if (buf.length + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone
+ s.error("excessive length %llu for symbol, possible recursive expansion?", cast(ulong)(buf.length + len));
+ else
+ {
+ buf.print(len);
+ buf.writestring(id);
+ }
+ }
+
+ /************************************************************
+ * Try to obtain an externally mangled identifier from a declaration.
+ * If the declaration is at global scope or mixed in at global scope,
+ * the user might want to call it externally, so an externally mangled
+ * name is returned. Member functions or nested functions can't be called
+ * externally in C, so in that case null is returned. C++ does support
+ * namespaces, so extern(C++) always gives a C++ mangled name.
+ *
+ * See also: https://issues.dlang.org/show_bug.cgi?id=20012
+ *
+ * Params:
+ * d = declaration to mangle
+ *
+ * Returns:
+ * an externally mangled name or null if the declaration cannot be called externally
+ */
+ extern (D) static const(char)[] externallyMangledIdentifier(Declaration d)
+ {
+ const par = d.toParent(); //toParent() skips over mixin templates
+ if (!par || par.isModule() || d.linkage == LINK.cpp)
+ {
+ if (d.linkage != LINK.d && d.localNum)
+ d.error("the same declaration cannot be in multiple scopes with non-D linkage");
+ final switch (d.linkage)
+ {
+ case LINK.d:
+ break;
+ case LINK.c:
+ case LINK.windows:
+ case LINK.objc:
+ return d.ident.toString();
+ case LINK.cpp:
+ {
+ const p = target.cpp.toMangle(d);
+ return p.toDString();
+ }
+ case LINK.default_:
+ case LINK.system:
+ d.error("forward declaration");
+ return d.ident.toString();
+ }
+ }
+ return null;
+ }
+
+ override void visit(Declaration d)
+ {
+ //printf("Declaration.mangle(this = %p, '%s', parent = '%s', linkage = %d)\n",
+ // d, d.toChars(), d.parent ? d.parent.toChars() : "null", d.linkage);
+ if (const id = externallyMangledIdentifier(d))
+ {
+ buf.writestring(id);
+ return;
+ }
+ buf.writestring("_D");
+ mangleDecl(d);
+ debug
+ {
+ const slice = (*buf)[];
+ assert(slice.length);
+ for (size_t pos; pos < slice.length; )
+ {
+ dchar c;
+ auto ppos = pos;
+ const s = utf_decodeChar(slice, pos, c);
+ assert(s is null, s);
+ assert(c.isValidMangling, "The mangled name '" ~ slice ~ "' " ~
+ "contains an invalid character: " ~ slice[ppos..pos]);
+ }
+ }
+ }
+
+ /******************************************************************************
+ * Normally FuncDeclaration and FuncAliasDeclaration have overloads.
+ * If and only if there is no overloads, mangle() could return
+ * exact mangled name.
+ *
+ * module test;
+ * void foo(long) {} // _D4test3fooFlZv
+ * void foo(string) {} // _D4test3fooFAyaZv
+ *
+ * // from FuncDeclaration.mangle().
+ * pragma(msg, foo.mangleof); // prints unexact mangled name "4test3foo"
+ * // by calling Dsymbol.mangle()
+ *
+ * // from FuncAliasDeclaration.mangle()
+ * pragma(msg, __traits(getOverloads, test, "foo")[0].mangleof); // "_D4test3fooFlZv"
+ * pragma(msg, __traits(getOverloads, test, "foo")[1].mangleof); // "_D4test3fooFAyaZv"
+ *
+ * If a function has no overloads, .mangleof property still returns exact mangled name.
+ *
+ * void bar() {}
+ * pragma(msg, bar.mangleof); // still prints "_D4test3barFZv"
+ * // by calling FuncDeclaration.mangleExact().
+ */
+ override void visit(FuncDeclaration fd)
+ {
+ if (fd.isUnique())
+ mangleExact(fd);
+ else
+ visit(cast(Dsymbol)fd);
+ }
+
+ // ditto
+ override void visit(FuncAliasDeclaration fd)
+ {
+ FuncDeclaration f = fd.toAliasFunc();
+ FuncAliasDeclaration fa = f.isFuncAliasDeclaration();
+ if (!fd.hasOverloads && !fa)
+ {
+ mangleExact(f);
+ return;
+ }
+ if (fa)
+ {
+ mangleSymbol(fa);
+ return;
+ }
+ visit(cast(Dsymbol)fd);
+ }
+
+ override void visit(OverDeclaration od)
+ {
+ if (od.overnext)
+ {
+ visit(cast(Dsymbol)od);
+ return;
+ }
+ if (FuncDeclaration fd = od.aliassym.isFuncDeclaration())
+ {
+ if (fd.isUnique())
+ {
+ mangleExact(fd);
+ return;
+ }
+ }
+ if (TemplateDeclaration td = od.aliassym.isTemplateDeclaration())
+ {
+ if (td.overnext is null)
+ {
+ mangleSymbol(td);
+ return;
+ }
+ }
+ visit(cast(Dsymbol)od);
+ }
+
+ void mangleExact(FuncDeclaration fd)
+ {
+ assert(!fd.isFuncAliasDeclaration());
+ if (fd.mangleOverride)
+ {
+ buf.writestring(fd.mangleOverride);
+ return;
+ }
+ if (fd.isMain())
+ {
+ buf.writestring("_Dmain");
+ return;
+ }
+ if (fd.isWinMain() || fd.isDllMain())
+ {
+ buf.writestring(fd.ident.toString());
+ return;
+ }
+ visit(cast(Declaration)fd);
+ }
+
+ override void visit(VarDeclaration vd)
+ {
+ if (vd.mangleOverride)
+ {
+ buf.writestring(vd.mangleOverride);
+ return;
+ }
+ visit(cast(Declaration)vd);
+ }
+
+ override void visit(AggregateDeclaration ad)
+ {
+ ClassDeclaration cd = ad.isClassDeclaration();
+ Dsymbol parentsave = ad.parent;
+ if (cd)
+ {
+ /* These are reserved to the compiler, so keep simple
+ * names for them.
+ */
+ if (cd.ident == Id.Exception && cd.parent.ident == Id.object || cd.ident == Id.TypeInfo || cd.ident == Id.TypeInfo_Struct || cd.ident == Id.TypeInfo_Class || cd.ident == Id.TypeInfo_Tuple || cd == ClassDeclaration.object || cd == Type.typeinfoclass || cd == Module.moduleinfo || strncmp(cd.ident.toChars(), "TypeInfo_", 9) == 0)
+ {
+ // Don't mangle parent
+ ad.parent = null;
+ }
+ }
+ visit(cast(Dsymbol)ad);
+ ad.parent = parentsave;
+ }
+
+ override void visit(TemplateInstance ti)
+ {
+ version (none)
+ {
+ printf("TemplateInstance.mangle() %p %s", ti, ti.toChars());
+ if (ti.parent)
+ printf(" parent = %s %s", ti.parent.kind(), ti.parent.toChars());
+ printf("\n");
+ }
+ if (!ti.tempdecl)
+ ti.error("is not defined");
+ else
+ mangleParent(ti);
+
+ if (ti.isTemplateMixin() && ti.ident)
+ mangleIdentifier(ti.ident, ti);
+ else
+ mangleTemplateInstance(ti);
+ }
+
+ void mangleTemplateInstance(TemplateInstance ti)
+ {
+ TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ // Use "__U" for the symbols declared inside template constraint.
+ const char T = ti.members ? 'T' : 'U';
+ buf.printf("__%c", T);
+ mangleIdentifier(tempdecl.ident, tempdecl);
+
+ auto args = ti.tiargs;
+ size_t nparams = tempdecl.parameters.dim - (tempdecl.isVariadic() ? 1 : 0);
+ for (size_t i = 0; i < args.dim; i++)
+ {
+ auto o = (*args)[i];
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+ Tuple va = isTuple(o);
+ //printf("\to [%d] %p ta %p ea %p sa %p va %p\n", i, o, ta, ea, sa, va);
+ if (i < nparams && (*tempdecl.parameters)[i].specialization())
+ buf.writeByte('H'); // https://issues.dlang.org/show_bug.cgi?id=6574
+ if (ta)
+ {
+ buf.writeByte('T');
+ visitWithMask(ta, 0);
+ }
+ else if (ea)
+ {
+ // Don't interpret it yet, it might actually be an alias template parameter.
+ // Only constfold manifest constants, not const/immutable lvalues, see https://issues.dlang.org/show_bug.cgi?id=17339.
+ enum keepLvalue = true;
+ ea = ea.optimize(WANTvalue, keepLvalue);
+ if (auto ev = ea.isVarExp())
+ {
+ sa = ev.var;
+ ea = null;
+ goto Lsa;
+ }
+ if (auto et = ea.isThisExp())
+ {
+ sa = et.var;
+ ea = null;
+ goto Lsa;
+ }
+ if (auto ef = ea.isFuncExp())
+ {
+ if (ef.td)
+ sa = ef.td;
+ else
+ sa = ef.fd;
+ ea = null;
+ goto Lsa;
+ }
+ buf.writeByte('V');
+ if (ea.op == TOK.tuple)
+ {
+ ea.error("tuple is not a valid template value argument");
+ continue;
+ }
+ // Now that we know it is not an alias, we MUST obtain a value
+ uint olderr = global.errors;
+ ea = ea.ctfeInterpret();
+ if (ea.op == TOK.error || olderr != global.errors)
+ continue;
+
+ /* Use type mangling that matches what it would be for a function parameter
+ */
+ visitWithMask(ea.type, 0);
+ ea.accept(this);
+ }
+ else if (sa)
+ {
+ Lsa:
+ sa = sa.toAlias();
+ if (sa.isDeclaration() && !sa.isOverDeclaration())
+ {
+ Declaration d = sa.isDeclaration();
+
+ if (auto fad = d.isFuncAliasDeclaration())
+ d = fad.toAliasFunc();
+ if (d.mangleOverride)
+ {
+ buf.writeByte('X');
+ toBuffer(d.mangleOverride, d);
+ continue;
+ }
+ if (const id = externallyMangledIdentifier(d))
+ {
+ buf.writeByte('X');
+ toBuffer(id, d);
+ continue;
+ }
+ if (!d.type || !d.type.deco)
+ {
+ ti.error("forward reference of %s `%s`", d.kind(), d.toChars());
+ continue;
+ }
+ }
+ buf.writeByte('S');
+ mangleSymbol(sa);
+ }
+ else if (va)
+ {
+ assert(i + 1 == args.dim); // must be last one
+ args = &va.objects;
+ i = -cast(size_t)1;
+ }
+ else
+ assert(0);
+ }
+ buf.writeByte('Z');
+ }
+
+ override void visit(Dsymbol s)
+ {
+ version (none)
+ {
+ printf("Dsymbol.mangle() '%s'", s.toChars());
+ if (s.parent)
+ printf(" parent = %s %s", s.parent.kind(), s.parent.toChars());
+ printf("\n");
+ }
+ mangleParent(s);
+ if (s.ident)
+ mangleIdentifier(s.ident, s);
+ else
+ toBuffer(s.toString(), s);
+ //printf("Dsymbol.mangle() %s = %s\n", s.toChars(), id);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ override void visit(Expression e)
+ {
+ e.error("expression `%s` is not a valid template value argument", e.toChars());
+ }
+
+ override void visit(IntegerExp e)
+ {
+ const v = e.toInteger();
+ if (cast(sinteger_t)v < 0)
+ {
+ buf.writeByte('N');
+ buf.print(-v);
+ }
+ else
+ {
+ buf.writeByte('i');
+ buf.print(v);
+ }
+ }
+
+ override void visit(RealExp e)
+ {
+ buf.writeByte('e');
+ realToMangleBuffer(e.value);
+ }
+
+ void realToMangleBuffer(real_t value)
+ {
+ /* Rely on %A to get portable mangling.
+ * Must munge result to get only identifier characters.
+ *
+ * Possible values from %A => mangled result
+ * NAN => NAN
+ * -INF => NINF
+ * INF => INF
+ * -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79
+ * 0X1.9P+2 => 19P2
+ */
+ if (CTFloat.isNaN(value))
+ {
+ buf.writestring("NAN"); // no -NAN bugs
+ return;
+ }
+
+ if (value < CTFloat.zero)
+ {
+ buf.writeByte('N');
+ value = -value;
+ }
+
+ if (CTFloat.isInfinity(value))
+ {
+ buf.writestring("INF");
+ return;
+ }
+
+ char[36] buffer = void;
+ // 'A' format yields [-]0xh.hhhhp+-d
+ const n = CTFloat.sprint(buffer.ptr, 'A', value);
+ assert(n < buffer.length);
+ foreach (const c; buffer[2 .. n])
+ {
+ switch (c)
+ {
+ case '-':
+ buf.writeByte('N');
+ break;
+
+ case '+':
+ case '.':
+ break;
+
+ default:
+ buf.writeByte(c);
+ break;
+ }
+ }
+ }
+
+ override void visit(ComplexExp e)
+ {
+ buf.writeByte('c');
+ realToMangleBuffer(e.toReal());
+ buf.writeByte('c'); // separate the two
+ realToMangleBuffer(e.toImaginary());
+ }
+
+ override void visit(NullExp e)
+ {
+ buf.writeByte('n');
+ }
+
+ override void visit(StringExp e)
+ {
+ char m;
+ OutBuffer tmp;
+ const(char)[] q;
+ /* Write string in UTF-8 format
+ */
+ switch (e.sz)
+ {
+ case 1:
+ m = 'a';
+ q = e.peekString();
+ break;
+ case 2:
+ {
+ m = 'w';
+ const slice = e.peekWstring();
+ for (size_t u = 0; u < e.len;)
+ {
+ dchar c;
+ if (const s = utf_decodeWchar(slice, u, c))
+ e.error("%.*s", cast(int)s.length, s.ptr);
+ else
+ tmp.writeUTF8(c);
+ }
+ q = tmp[];
+ break;
+ }
+ case 4:
+ {
+ m = 'd';
+ const slice = e.peekDstring();
+ foreach (c; slice)
+ {
+ if (!utf_isValidDchar(c))
+ e.error("invalid UCS-32 char \\U%08x", c);
+ else
+ tmp.writeUTF8(c);
+ }
+ q = tmp[];
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+ buf.reserve(1 + 11 + 2 * q.length);
+ buf.writeByte(m);
+ buf.print(q.length);
+ buf.writeByte('_'); // nbytes <= 11
+ auto slice = buf.allocate(2 * q.length);
+ foreach (i, c; q)
+ {
+ char hi = (c >> 4) & 0xF;
+ slice[i * 2] = cast(char)(hi < 10 ? hi + '0' : hi - 10 + 'a');
+ char lo = c & 0xF;
+ slice[i * 2 + 1] = cast(char)(lo < 10 ? lo + '0' : lo - 10 + 'a');
+ }
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ const dim = e.elements ? e.elements.dim : 0;
+ buf.writeByte('A');
+ buf.print(dim);
+ foreach (i; 0 .. dim)
+ {
+ e[i].accept(this);
+ }
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ const dim = e.keys.dim;
+ buf.writeByte('A');
+ buf.print(dim);
+ foreach (i; 0 .. dim)
+ {
+ (*e.keys)[i].accept(this);
+ (*e.values)[i].accept(this);
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ const dim = e.elements ? e.elements.dim : 0;
+ buf.writeByte('S');
+ buf.print(dim);
+ foreach (i; 0 .. dim)
+ {
+ Expression ex = (*e.elements)[i];
+ if (ex)
+ ex.accept(this);
+ else
+ buf.writeByte('v'); // 'v' for void
+ }
+ }
+
+ override void visit(FuncExp e)
+ {
+ buf.writeByte('f');
+ if (e.td)
+ mangleSymbol(e.td);
+ else
+ mangleSymbol(e.fd);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ override void visit(Parameter p)
+ {
+ if (p.storageClass & STC.scope_ && !(p.storageClass & STC.scopeinferred))
+ buf.writeByte('M');
+
+ // 'return inout ref' is the same as 'inout ref'
+ if ((p.storageClass & (STC.return_ | STC.wild)) == STC.return_ &&
+ !(p.storageClass & STC.returninferred))
+ buf.writestring("Nk");
+ switch (p.storageClass & (STC.IOR | STC.lazy_))
+ {
+ case 0:
+ break;
+ case STC.in_:
+ buf.writeByte('I');
+ break;
+ case STC.in_ | STC.ref_:
+ buf.writestring("IK");
+ break;
+ case STC.out_:
+ buf.writeByte('J');
+ break;
+ case STC.ref_:
+ buf.writeByte('K');
+ break;
+ case STC.lazy_:
+ buf.writeByte('L');
+ break;
+ default:
+ debug
+ {
+ printf("storageClass = x%llx\n", p.storageClass & (STC.IOR | STC.lazy_));
+ }
+ assert(0);
+ }
+ visitWithMask(p.type, (p.storageClass & STC.in_) ? MODFlags.const_ : 0);
+ }
+}
diff --git a/gcc/d/dmd/dmodule.c b/gcc/d/dmd/dmodule.c
deleted file mode 100644
index 472b2b9e7f8..00000000000
--- a/gcc/d/dmd/dmodule.c
+++ /dev/null
@@ -1,1276 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/module.c
- */
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "module.h"
-#include "parse.h"
-#include "scope.h"
-#include "identifier.h"
-#include "id.h"
-#include "import.h"
-#include "dsymbol.h"
-#include "expression.h"
-#include "lexer.h"
-#include "attrib.h"
-
-AggregateDeclaration *Module::moduleinfo;
-
-Module *Module::rootModule;
-DsymbolTable *Module::modules;
-Modules Module::amodules;
-
-Dsymbols Module::deferred; // deferred Dsymbol's needing semantic() run on them
-Dsymbols Module::deferred2; // deferred Dsymbol's needing semantic2() run on them
-Dsymbols Module::deferred3; // deferred Dsymbol's needing semantic3() run on them
-unsigned Module::dprogress;
-
-void Module::_init()
-{
- modules = new DsymbolTable();
-}
-
-Module::Module(const char *filename, Identifier *ident, int doDocComment, int doHdrGen)
- : Package(ident)
-{
- const char *srcfilename;
-
-// printf("Module::Module(filename = '%s', ident = '%s')\n", filename, ident->toChars());
- this->arg = filename;
- md = NULL;
- errors = 0;
- numlines = 0;
- members = NULL;
- isDocFile = 0;
- isPackageFile = false;
- pkg = NULL;
- needmoduleinfo = 0;
- selfimports = 0;
- rootimports = 0;
- insearch = 0;
- searchCacheIdent = NULL;
- searchCacheSymbol = NULL;
- searchCacheFlags = 0;
- decldefs = NULL;
- sictor = NULL;
- sctor = NULL;
- sdtor = NULL;
- ssharedctor = NULL;
- sshareddtor = NULL;
- stest = NULL;
- sfilename = NULL;
- importedFrom = NULL;
- srcfile = NULL;
- docfile = NULL;
-
- debuglevel = 0;
- debugids = NULL;
- debugidsNot = NULL;
- versionlevel = 0;
- versionids = NULL;
- versionidsNot = NULL;
-
- macrotable = NULL;
- escapetable = NULL;
- doppelganger = 0;
- cov = NULL;
- covb = NULL;
-
- nameoffset = 0;
- namelen = 0;
-
- srcfilename = FileName::defaultExt(filename, global.mars_ext.ptr);
-
- if (global.run_noext && global.params.run &&
- !FileName::ext(filename) &&
- FileName::exists(srcfilename) == 0 &&
- FileName::exists(filename) == 1)
- {
- FileName::free(srcfilename);
- srcfilename = FileName::removeExt(filename); // just does a mem.strdup(filename)
- }
- else if (!FileName::equalsExt(srcfilename, global.mars_ext.ptr) &&
- !FileName::equalsExt(srcfilename, global.hdr_ext.ptr) &&
- !FileName::equalsExt(srcfilename, "dd"))
- {
- error("source file name '%s' must have .%s extension", srcfilename, global.mars_ext);
- fatal();
- }
- srcfile = new File(srcfilename);
- objfile = setOutfile(global.params.objname.ptr, global.params.objdir.ptr, filename, global.obj_ext.ptr);
-
- if (doDocComment)
- setDocfile();
-
- if (doHdrGen)
- hdrfile = setOutfile(global.params.hdrname.ptr, global.params.hdrdir.ptr, arg, global.hdr_ext.ptr);
-
- //objfile = new File(objfilename);
-}
-
-Module *Module::create(const char *filename, Identifier *ident, int doDocComment, int doHdrGen)
-{
- return new Module(filename, ident, doDocComment, doHdrGen);
-}
-
-void Module::setDocfile()
-{
- docfile = setOutfile(global.params.docname.ptr, global.params.docdir.ptr, arg, global.doc_ext.ptr);
-}
-
-/*********************************************
- * Combines things into output file name for .html and .di files.
- * Input:
- * name Command line name given for the file, NULL if none
- * dir Command line directory given for the file, NULL if none
- * arg Name of the source file
- * ext File name extension to use if 'name' is NULL
- * global.params.preservePaths get output path from arg
- * srcfile Input file - output file name must not match input file
- */
-
-File *Module::setOutfile(const char *name, const char *dir, const char *arg, const char *ext)
-{
- const char *docfilename;
-
- if (name)
- {
- docfilename = name;
- }
- else
- {
- const char *argdoc;
- if (global.params.preservePaths)
- argdoc = arg;
- else
- argdoc = FileName::name(arg);
-
- // If argdoc doesn't have an absolute path, make it relative to dir
- if (!FileName::absolute(argdoc))
- { //FileName::ensurePathExists(dir);
- argdoc = FileName::combine(dir, argdoc);
- }
- docfilename = FileName::forceExt(argdoc, ext);
- }
-
- if (FileName::equals(docfilename, srcfile->name->str))
- {
- error("source file and output file have same name '%s'", srcfile->name->str);
- fatal();
- }
-
- return new File(docfilename);
-}
-
-void Module::deleteObjFile()
-{
- if (global.params.obj)
- objfile->remove();
- if (docfile)
- docfile->remove();
-}
-
-const char *Module::kind() const
-{
- return "module";
-}
-
-static void checkModFileAlias(OutBuffer *buf, OutBuffer *dotmods,
- Array<const char *> *ms, size_t msdim, const char *p)
-{
- /* Check and replace the contents of buf[] with
- * an alias string from global.params.modFileAliasStrings[]
- */
- dotmods->writestring(p);
- for (size_t j = msdim; j--;)
- {
- const char *m = (*ms)[j];
- const char *q = strchr(m, '=');
- assert(q);
- if (dotmods->length() == (size_t)(q - m) && memcmp(dotmods->peekChars(), m, q - m) == 0)
- {
- buf->reset();
- size_t qlen = strlen(q + 1);
- if (qlen && (q[qlen] == '/' || q[qlen] == '\\'))
- --qlen; // remove trailing separator
- buf->write(q + 1, qlen);
- break; // last matching entry in ms[] wins
- }
- }
- dotmods->writeByte('.');
-}
-
-/**
- * Converts a chain of identifiers to the filename of the module
- *
- * Params:
- * packages = the names of the "parent" packages
- * ident = the name of the child package or module
- *
- * Returns:
- * the filename of the child package or module
- */
-static const char *getFilename(Identifiers *packages, Identifier *ident)
-{
- const char *filename = ident->toChars();
-
- if (packages == NULL || packages->length == 0)
- return filename;
-
- OutBuffer buf;
- OutBuffer dotmods;
- Array<const char *> *ms = &global.params.modFileAliasStrings;
- const size_t msdim = ms ? ms->length : 0;
-
- for (size_t i = 0; i < packages->length; i++)
- {
- Identifier *pid = (*packages)[i];
- const char *p = pid->toChars();
- buf.writestring(p);
- if (msdim)
- checkModFileAlias(&buf, &dotmods, ms, msdim, p);
-#if _WIN32
- buf.writeByte('\\');
-#else
- buf.writeByte('/');
-#endif
- }
- buf.writestring(filename);
- if (msdim)
- checkModFileAlias(&buf, &dotmods, ms, msdim, filename);
- buf.writeByte(0);
- filename = (char *)buf.extractData();
-
- return filename;
-}
-
-/********************************************
- * Look for the source file if it's different from filename.
- * Look for .di, .d, directory, and along global.path.
- * Does not open the file.
- * Input:
- * filename as supplied by the user
- * global.path
- * Returns:
- * NULL if it's not different from filename.
- */
-
-static const char *lookForSourceFile(const char *filename)
-{
- /* Search along global.path for .di file, then .d file.
- */
- const char *sdi = FileName::forceExt(filename, global.hdr_ext.ptr);
- if (FileName::exists(sdi) == 1)
- return sdi;
-
- const char *sd = FileName::forceExt(filename, global.mars_ext.ptr);
- if (FileName::exists(sd) == 1)
- return sd;
-
- if (FileName::exists(filename) == 2)
- {
- /* The filename exists and it's a directory.
- * Therefore, the result should be: filename/package.d
- * iff filename/package.d is a file
- */
- const char *ni = FileName::combine(filename, "package.di");
- if (FileName::exists(ni) == 1)
- return ni;
- FileName::free(ni);
- const char *n = FileName::combine(filename, "package.d");
- if (FileName::exists(n) == 1)
- return n;
- FileName::free(n);
- }
-
- if (FileName::absolute(filename))
- return NULL;
-
- if (!global.path)
- return NULL;
-
- for (size_t i = 0; i < global.path->length; i++)
- {
- const char *p = (*global.path)[i];
- const char *n = FileName::combine(p, sdi);
- if (FileName::exists(n) == 1)
- {
- return n;
- }
- FileName::free(n);
-
- n = FileName::combine(p, sd);
- if (FileName::exists(n) == 1)
- {
- return n;
- }
- FileName::free(n);
-
- const char *b = FileName::removeExt(filename);
- n = FileName::combine(p, b);
- FileName::free(b);
- if (FileName::exists(n) == 2)
- {
- const char *n2i = FileName::combine(n, "package.di");
- if (FileName::exists(n2i) == 1)
- return n2i;
- FileName::free(n2i);
- const char *n2 = FileName::combine(n, "package.d");
- if (FileName::exists(n2) == 1)
- {
- return n2;
- }
- FileName::free(n2);
- }
- FileName::free(n);
- }
- return NULL;
-}
-
-Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident)
-{
- //printf("Module::load(ident = '%s')\n", ident->toChars());
-
- // Build module filename by turning:
- // foo.bar.baz
- // into:
- // foo\bar\baz
- const char *filename = getFilename(packages, ident);
- // Look for the source file
- const char *result = lookForSourceFile(filename);
- if (result)
- filename = result;
-
- Module *m = new Module(filename, ident, 0, 0);
- m->loc = loc;
-
- if (!m->read(loc))
- return NULL;
-
- if (global.params.verbose)
- {
- OutBuffer buf;
- if (packages)
- {
- for (size_t i = 0; i < packages->length; i++)
- {
- Identifier *pid = (*packages)[i];
- buf.writestring(pid->toChars());
- buf.writeByte('.');
- }
- }
- buf.printf("%s\t(%s)", ident->toChars(), m->srcfile->toChars());
- message("import %s", buf.peekChars());
- }
-
- m = m->parse();
-
- // Call onImport here because if the module is going to be compiled then we
- // need to determine it early because it affects semantic analysis. This is
- // being done after parsing the module so the full module name can be taken
- // from whatever was declared in the file.
- if (!m->isRoot() && Compiler::onImport(m))
- {
- m->importedFrom = m;
- assert(m->isRoot());
- }
- return m;
-}
-
-bool Module::read(Loc loc)
-{
- //printf("Module::read('%s') file '%s'\n", toChars(), srcfile->toChars());
- if (srcfile->read())
- {
- if (!strcmp(srcfile->toChars(), "object.d"))
- {
- ::error(loc, "cannot find source code for runtime library file 'object.d'");
- errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
- const char *dmdConfFile = global.inifilename.length ? FileName::canonicalName(global.inifilename.ptr) : NULL;
- errorSupplemental(loc, "config file: %s", dmdConfFile ? dmdConfFile : "not found");
- }
- else
- {
- // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module
- bool isPackageMod = (strcmp(toChars(), "package") != 0) &&
- (strcmp(srcfile->name->name(), "package.d") == 0 || (strcmp(srcfile->name->name(), "package.di") == 0));
-
- if (isPackageMod)
- ::error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'",
- toChars(), srcfile->toChars());
- else
- error(loc, "is in file '%s' which cannot be read", srcfile->toChars());
- }
-
- if (!global.gag)
- {
- /* Print path
- */
- if (global.path)
- {
- for (size_t i = 0; i < global.path->length; i++)
- {
- const char *p = (*global.path)[i];
- fprintf(stderr, "import path[%llu] = %s\n", (ulonglong)i, p);
- }
- }
- else
- fprintf(stderr, "Specify path to file '%s' with -I switch\n", srcfile->toChars());
- fatal();
- }
- return false;
- }
- return true;
-}
-
-Module *Module::parse()
-{
- //printf("Module::parse(srcfile='%s') this=%p\n", srcfile->name->toChars(), this);
-
- const char *srcname = srcfile->name->toChars();
- //printf("Module::parse(srcname = '%s')\n", srcname);
-
- isPackageFile = (strcmp(srcfile->name->name(), "package.d") == 0 ||
- strcmp(srcfile->name->name(), "package.di") == 0);
-
- utf8_t *buf = (utf8_t *)srcfile->buffer;
- size_t buflen = srcfile->len;
-
- if (buflen >= 2)
- {
- /* Convert all non-UTF-8 formats to UTF-8.
- * BOM : http://www.unicode.org/faq/utf_bom.html
- * 00 00 FE FF UTF-32BE, big-endian
- * FF FE 00 00 UTF-32LE, little-endian
- * FE FF UTF-16BE, big-endian
- * FF FE UTF-16LE, little-endian
- * EF BB BF UTF-8
- */
-
- unsigned le;
- unsigned bom = 1; // assume there's a BOM
- if (buf[0] == 0xFF && buf[1] == 0xFE)
- {
- if (buflen >= 4 && buf[2] == 0 && buf[3] == 0)
- { // UTF-32LE
- le = 1;
-
- Lutf32:
- OutBuffer dbuf;
- unsigned *pu = (unsigned *)(buf);
- unsigned *pumax = &pu[buflen / 4];
-
- if (buflen & 3)
- { error("odd length of UTF-32 char source %u", buflen);
- fatal();
- }
-
- dbuf.reserve(buflen / 4);
- for (pu += bom; pu < pumax; pu++)
- { unsigned u;
-
- u = le ? Port::readlongLE(pu) : Port::readlongBE(pu);
- if (u & ~0x7F)
- {
- if (u > 0x10FFFF)
- { error("UTF-32 value %08x greater than 0x10FFFF", u);
- fatal();
- }
- dbuf.writeUTF8(u);
- }
- else
- dbuf.writeByte(u);
- }
- dbuf.writeByte(0); // add 0 as sentinel for scanner
- buflen = dbuf.length() - 1; // don't include sentinel in count
- buf = (utf8_t *) dbuf.extractData();
- }
- else
- { // UTF-16LE (X86)
- // Convert it to UTF-8
- le = 1;
-
- Lutf16:
- OutBuffer dbuf;
- unsigned short *pu = (unsigned short *)(buf);
- unsigned short *pumax = &pu[buflen / 2];
-
- if (buflen & 1)
- { error("odd length of UTF-16 char source %u", buflen);
- fatal();
- }
-
- dbuf.reserve(buflen / 2);
- for (pu += bom; pu < pumax; pu++)
- { unsigned u;
-
- u = le ? Port::readwordLE(pu) : Port::readwordBE(pu);
- if (u & ~0x7F)
- { if (u >= 0xD800 && u <= 0xDBFF)
- { unsigned u2;
-
- if (++pu > pumax)
- { error("surrogate UTF-16 high value %04x at EOF", u);
- fatal();
- }
- u2 = le ? Port::readwordLE(pu) : Port::readwordBE(pu);
- if (u2 < 0xDC00 || u2 > 0xDFFF)
- { error("surrogate UTF-16 low value %04x out of range", u2);
- fatal();
- }
- u = (u - 0xD7C0) << 10;
- u |= (u2 - 0xDC00);
- }
- else if (u >= 0xDC00 && u <= 0xDFFF)
- { error("unpaired surrogate UTF-16 value %04x", u);
- fatal();
- }
- else if (u == 0xFFFE || u == 0xFFFF)
- { error("illegal UTF-16 value %04x", u);
- fatal();
- }
- dbuf.writeUTF8(u);
- }
- else
- dbuf.writeByte(u);
- }
- dbuf.writeByte(0); // add 0 as sentinel for scanner
- buflen = dbuf.length() - 1; // don't include sentinel in count
- buf = (utf8_t *) dbuf.extractData();
- }
- }
- else if (buf[0] == 0xFE && buf[1] == 0xFF)
- { // UTF-16BE
- le = 0;
- goto Lutf16;
- }
- else if (buflen >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFE && buf[3] == 0xFF)
- { // UTF-32BE
- le = 0;
- goto Lutf32;
- }
- else if (buflen >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
- { // UTF-8
-
- buf += 3;
- buflen -= 3;
- }
- else
- {
- /* There is no BOM. Make use of Arcane Jill's insight that
- * the first char of D source must be ASCII to
- * figure out the encoding.
- */
-
- bom = 0;
- if (buflen >= 4)
- { if (buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
- { // UTF-32LE
- le = 1;
- goto Lutf32;
- }
- else if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0)
- { // UTF-32BE
- le = 0;
- goto Lutf32;
- }
- }
- if (buflen >= 2)
- {
- if (buf[1] == 0)
- { // UTF-16LE
- le = 1;
- goto Lutf16;
- }
- else if (buf[0] == 0)
- { // UTF-16BE
- le = 0;
- goto Lutf16;
- }
- }
-
- // It's UTF-8
- if (buf[0] >= 0x80)
- { error("source file must start with BOM or ASCII character, not \\x%02X", buf[0]);
- fatal();
- }
- }
- }
-
- /* If it starts with the string "Ddoc", then it's a documentation
- * source file.
- */
- if (buflen >= 4 && memcmp(buf, "Ddoc", 4) == 0)
- {
- comment = buf + 4;
- isDocFile = 1;
- if (!docfile)
- setDocfile();
- return this;
- }
- {
- Parser p(this, buf, buflen, docfile != NULL);
- p.nextToken();
- members = p.parseModule();
- md = p.md;
- numlines = p.scanloc.linnum;
- if (p.errors)
- ++global.errors;
- }
-
- if (srcfile->ref == 0)
- ::free(srcfile->buffer);
- srcfile->buffer = NULL;
- srcfile->len = 0;
-
- /* The symbol table into which the module is to be inserted.
- */
- DsymbolTable *dst;
-
- if (md)
- {
- /* A ModuleDeclaration, md, was provided.
- * The ModuleDeclaration sets the packages this module appears in, and
- * the name of this module.
- */
- this->ident = md->id;
- Package *ppack = NULL;
- dst = Package::resolve(md->packages, &this->parent, &ppack);
- assert(dst);
-
- Module *m = ppack ? ppack->isModule() : NULL;
- if (m && (strcmp(m->srcfile->name->name(), "package.d") != 0 &&
- strcmp(m->srcfile->name->name(), "package.di") != 0))
- {
- ::error(md->loc, "package name '%s' conflicts with usage as a module name in file %s",
- ppack->toPrettyChars(), m->srcfile->toChars());
- }
- }
- else
- {
- /* The name of the module is set to the source file name.
- * There are no packages.
- */
- dst = modules; // and so this module goes into global module symbol table
-
- /* Check to see if module name is a valid identifier
- */
- if (!Identifier::isValidIdentifier(this->ident->toChars()))
- error("has non-identifier characters in filename, use module declaration instead");
- }
-
- // Insert module into the symbol table
- Dsymbol *s = this;
- if (isPackageFile)
- {
- /* If the source tree is as follows:
- * pkg/
- * +- package.d
- * +- common.d
- * the 'pkg' will be incorporated to the internal package tree in two ways:
- * import pkg;
- * and:
- * import pkg.common;
- *
- * If both are used in one compilation, 'pkg' as a module (== pkg/package.d)
- * and a package name 'pkg' will conflict each other.
- *
- * To avoid the conflict:
- * 1. If preceding package name insertion had occurred by Package::resolve,
- * reuse the previous wrapping 'Package' if it exists
- * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here.
- *
- * Then change Package::isPkgMod to PKGmodule and set Package::mod.
- *
- * Note that the 'wrapping Package' is the Package that contains package.d and other submodules,
- * the one inserted to the symbol table.
- */
- Dsymbol *ps = dst->lookup(ident);
- Package *p = ps ? ps->isPackage() : NULL;
- if (p == NULL)
- {
- p = new Package(ident);
- p->tag = this->tag; // reuse the same package tag
- p->symtab = new DsymbolTable();
- }
- this->tag= p->tag; // reuse the 'older' package tag
- this->pkg = p;
- p->parent = this->parent;
- p->isPkgMod = PKGmodule;
- p->mod = this;
- s = p;
- }
- if (!dst->insert(s))
- {
- /* It conflicts with a name that is already in the symbol table.
- * Figure out what went wrong, and issue error message.
- */
- Dsymbol *prev = dst->lookup(ident);
- assert(prev);
- if (Module *mprev = prev->isModule())
- {
- if (FileName::compare(srcname, mprev->srcfile->toChars()) != 0)
- error(loc, "from file %s conflicts with another module %s from file %s",
- srcname, mprev->toChars(), mprev->srcfile->toChars());
- else if (isRoot() && mprev->isRoot())
- error(loc, "from file %s is specified twice on the command line",
- srcname);
- else
- error(loc, "from file %s must be imported with 'import %s;'",
- srcname, toPrettyChars());
-
- // Bugzilla 14446: Return previously parsed module to avoid AST duplication ICE.
- return mprev;
- }
- else if (Package *pkg = prev->isPackage())
- {
- // 'package.d' loaded after a previous 'Package' insertion
- if (isPackageFile)
- amodules.push(this); // Add to global array of all modules
- else
- error(md ? md->loc : loc, "from file %s conflicts with package name %s",
- srcname, pkg->toChars());
- }
- else
- assert(global.errors);
- }
- else
- {
- // Add to global array of all modules
- amodules.push(this);
- }
- Compiler::onParseModule(this);
- return this;
-}
-
-void Module::importAll(Scope *)
-{
- //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
-
- if (_scope)
- return; // already done
-
- if (isDocFile)
- {
- error("is a Ddoc file, cannot import it");
- return;
- }
-
- /* Note that modules get their own scope, from scratch.
- * This is so regardless of where in the syntax a module
- * gets imported, it is unaffected by context.
- * Ignore prevsc.
- */
- Scope *sc = Scope::createGlobal(this); // create root scope
-
- if (md && md->msg)
- md->msg = semanticString(sc, md->msg, "deprecation message");
-
- // Add import of "object", even for the "object" module.
- // If it isn't there, some compiler rewrites, like
- // classinst == classinst -> .object.opEquals(classinst, classinst)
- // would fail inside object.d.
- if (members->length == 0 || ((*members)[0])->ident != Id::object ||
- (*members)[0]->isImport() == NULL)
- {
- Import *im = new Import(Loc(), NULL, Id::object, NULL, 0);
- members->shift(im);
- }
-
- if (!symtab)
- {
- // Add all symbols into module's symbol table
- symtab = new DsymbolTable();
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- s->addMember(sc, sc->scopesym);
- }
- }
- // anything else should be run after addMember, so version/debug symbols are defined
-
- /* Set scope for the symbols so that if we forward reference
- * a symbol, it can possibly be resolved on the spot.
- * If this works out well, it can be extended to all modules
- * before any semantic() on any of them.
- */
- setScope(sc); // remember module scope for semantic
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- s->setScope(sc);
- }
-
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- s->importAll(sc);
- }
-
- sc = sc->pop();
- sc->pop(); // 2 pops because Scope::createGlobal() created 2
-}
-
-/**********************************
- * Determine if we need to generate an instance of ModuleInfo
- * for this Module.
- */
-
-int Module::needModuleInfo()
-{
- //printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
- return needmoduleinfo || global.params.cov;
-}
-
-Dsymbol *Module::search(const Loc &loc, Identifier *ident, int flags)
-{
- /* Since modules can be circularly referenced,
- * need to stop infinite recursive searches.
- * This is done with the cache.
- */
-
- //printf("%s Module::search('%s', flags = x%x) insearch = %d\n", toChars(), ident->toChars(), flags, insearch);
- if (insearch)
- return NULL;
-
- /* Qualified module searches always search their imports,
- * even if SearchLocalsOnly
- */
- if (!(flags & SearchUnqualifiedModule))
- flags &= ~(SearchUnqualifiedModule | SearchLocalsOnly);
-
- if (searchCacheIdent == ident && searchCacheFlags == flags)
- {
- //printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n",
- // toChars(), ident->toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol->toChars() : "null");
- return searchCacheSymbol;
- }
-
- unsigned int errors = global.errors;
-
- insearch = 1;
- Dsymbol *s = ScopeDsymbol::search(loc, ident, flags);
- insearch = 0;
-
- if (errors == global.errors)
- {
- // Bugzilla 10752: We can cache the result only when it does not cause
- // access error so the side-effect should be reproduced in later search.
- searchCacheIdent = ident;
- searchCacheSymbol = s;
- searchCacheFlags = flags;
- }
- return s;
-}
-
-bool Module::isPackageAccessible(Package *p, Prot protection, int flags)
-{
- if (insearch) // don't follow import cycles
- return false;
- if (flags & IgnorePrivateImports)
- protection = Prot(Prot::public_); // only consider public imports
- insearch = true;
- bool r = ScopeDsymbol::isPackageAccessible(p, protection);
- insearch = false;
- return r;
-}
-
-Dsymbol *Module::symtabInsert(Dsymbol *s)
-{
- searchCacheIdent = NULL; // symbol is inserted, so invalidate cache
- return Package::symtabInsert(s);
-}
-
-void Module::clearCache()
-{
- for (size_t i = 0; i < amodules.length; i++)
- {
- Module *m = amodules[i];
- m->searchCacheIdent = NULL;
- }
-}
-
-/*******************************************
- * Can't run semantic on s now, try again later.
- */
-
-void Module::addDeferredSemantic(Dsymbol *s)
-{
- // Don't add it if it is already there
- for (size_t i = 0; i < deferred.length; i++)
- {
- Dsymbol *sd = deferred[i];
-
- if (sd == s)
- return;
- }
-
- //printf("Module::addDeferredSemantic('%s')\n", s->toChars());
- deferred.push(s);
-}
-
-void Module::addDeferredSemantic2(Dsymbol *s)
-{
- //printf("Module::addDeferredSemantic2('%s')\n", s->toChars());
- deferred2.push(s);
-}
-
-void Module::addDeferredSemantic3(Dsymbol *s)
-{
- //printf("Module::addDeferredSemantic3('%s')\n", s->toChars());
- deferred3.push(s);
-}
-
-/******************************************
- * Run semantic() on deferred symbols.
- */
-
-void Module::runDeferredSemantic()
-{
- if (dprogress == 0)
- return;
-
- static int nested;
- if (nested)
- return;
- //if (deferred.length) printf("+Module::runDeferredSemantic(), len = %d\n", deferred.length);
- nested++;
-
- size_t len;
- do
- {
- dprogress = 0;
- len = deferred.length;
- if (!len)
- break;
-
- Dsymbol **todo;
- Dsymbol **todoalloc = NULL;
- Dsymbol *tmp;
- if (len == 1)
- {
- todo = &tmp;
- }
- else
- {
- todo = (Dsymbol **)mem.xmalloc(len * sizeof(Dsymbol *));
- todoalloc = todo;
- }
- memcpy(todo, deferred.tdata(), len * sizeof(Dsymbol *));
- deferred.setDim(0);
-
- for (size_t i = 0; i < len; i++)
- {
- Dsymbol *s = todo[i];
-
- dsymbolSemantic(s, NULL);
- //printf("deferred: %s, parent = %s\n", s->toChars(), s->parent->toChars());
- }
- //printf("\tdeferred.length = %d, len = %d, dprogress = %d\n", deferred.length, len, dprogress);
- if (todoalloc)
- free(todoalloc);
- } while (deferred.length < len || dprogress); // while making progress
- nested--;
- //printf("-Module::runDeferredSemantic(), len = %d\n", deferred.length);
-}
-
-void Module::runDeferredSemantic2()
-{
- Module::runDeferredSemantic();
-
- Dsymbols *a = &Module::deferred2;
- for (size_t i = 0; i < a->length; i++)
- {
- Dsymbol *s = (*a)[i];
- //printf("[%d] %s semantic2a\n", i, s->toPrettyChars());
- semantic2(s, NULL);
-
- if (global.errors)
- break;
- }
- a->setDim(0);
-}
-
-void Module::runDeferredSemantic3()
-{
- Module::runDeferredSemantic2();
-
- Dsymbols *a = &Module::deferred3;
- for (size_t i = 0; i < a->length; i++)
- {
- Dsymbol *s = (*a)[i];
- //printf("[%d] %s semantic3a\n", i, s->toPrettyChars());
-
- semantic3(s, NULL);
-
- if (global.errors)
- break;
- }
- a->setDim(0);
-}
-
-/************************************
- * Recursively look at every module this module imports,
- * return true if it imports m.
- * Can be used to detect circular imports.
- */
-
-int Module::imports(Module *m)
-{
- //printf("%s Module::imports(%s)\n", toChars(), m->toChars());
- for (size_t i = 0; i < aimports.length; i++)
- {
- Module *mi = aimports[i];
- if (mi == m)
- return true;
- if (!mi->insearch)
- {
- mi->insearch = 1;
- int r = mi->imports(m);
- if (r)
- return r;
- }
- }
- return false;
-}
-
-/*************************************
- * Return true if module imports itself.
- */
-
-bool Module::selfImports()
-{
- //printf("Module::selfImports() %s\n", toChars());
- if (selfimports == 0)
- {
- for (size_t i = 0; i < amodules.length; i++)
- amodules[i]->insearch = 0;
-
- selfimports = imports(this) + 1;
-
- for (size_t i = 0; i < amodules.length; i++)
- amodules[i]->insearch = 0;
- }
- return selfimports == 2;
-}
-
-/*************************************
- * Return true if module imports root module.
- */
-
-bool Module::rootImports()
-{
- //printf("Module::rootImports() %s\n", toChars());
- if (rootimports == 0)
- {
- for (size_t i = 0; i < amodules.length; i++)
- amodules[i]->insearch = 0;
-
- rootimports = 1;
- for (size_t i = 0; i < amodules.length; ++i)
- {
- Module *m = amodules[i];
- if (m->isRoot() && imports(m))
- {
- rootimports = 2;
- break;
- }
- }
-
- for (size_t i = 0; i < amodules.length; i++)
- amodules[i]->insearch = 0;
- }
- return rootimports == 2;
-}
-
-bool Module::isCoreModule(Identifier *ident)
-{
- return this->ident == ident && parent && parent->ident == Id::core && !parent->parent;
-}
-
-/* =========================== ModuleDeclaration ===================== */
-
-ModuleDeclaration::ModuleDeclaration(Loc loc, Identifiers *packages, Identifier *id)
-{
- this->loc = loc;
- this->packages = packages;
- this->id = id;
- this->isdeprecated = false;
- this->msg = NULL;
-}
-
-const char *ModuleDeclaration::toChars()
-{
- OutBuffer buf;
-
- if (packages && packages->length)
- {
- for (size_t i = 0; i < packages->length; i++)
- {
- Identifier *pid = (*packages)[i];
- buf.writestring(pid->toChars());
- buf.writeByte('.');
- }
- }
- buf.writestring(id->toChars());
- return buf.extractChars();
-}
-
-/* =========================== Package ===================== */
-
-Package::Package(Identifier *ident)
- : ScopeDsymbol(ident)
-{
- this->isPkgMod = PKGunknown;
- this->mod = NULL;
- static unsigned packageTag = 0;
- this->tag = packageTag++;
-}
-
-
-const char *Package::kind() const
-{
- return "package";
-}
-
-Module *Package::isPackageMod()
-{
- if (isPkgMod == PKGmodule)
- {
- return mod;
- }
- return NULL;
-}
-
-/**
- * Checks for the existence of a package.d to set isPkgMod appropriately
- * if isPkgMod == PKGunknown
- */
-void Package::resolvePKGunknown()
-{
- if (isModule())
- return;
- if (isPkgMod != PKGunknown)
- return;
-
- Identifiers packages;
- for (Dsymbol *s = this->parent; s; s = s->parent)
- packages.insert(0, s->ident);
-
- if (lookForSourceFile(getFilename(&packages, ident)))
- Module::load(Loc(), &packages, this->ident);
- else
- isPkgMod = PKGpackage;
-}
-
-/**
- * Checks if pkg is a sub-package of this
- *
- * For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3',
- * this function returns 'true'. If it is other way around or qualified
- * package paths conflict function returns 'false'.
- *
- * Params:
- * pkg = possible subpackage
- *
- * Returns:
- * see description
- */
-bool Package::isAncestorPackageOf(const Package * const pkg) const
-{
- if (this == pkg)
- return true;
- if (!pkg || !pkg->parent)
- return false;
- return isAncestorPackageOf(pkg->parent->isPackage());
-}
-
-/****************************************************
- * Input:
- * packages[] the pkg1.pkg2 of pkg1.pkg2.mod
- * Returns:
- * the symbol table that mod should be inserted into
- * Output:
- * *pparent the rightmost package, i.e. pkg2, or NULL if no packages
- * *ppkg the leftmost package, i.e. pkg1, or NULL if no packages
- */
-
-DsymbolTable *Package::resolve(Identifiers *packages, Dsymbol **pparent, Package **ppkg)
-{
- DsymbolTable *dst = Module::modules;
- Dsymbol *parent = NULL;
-
- //printf("Package::resolve()\n");
- if (ppkg)
- *ppkg = NULL;
-
- if (packages)
- {
- for (size_t i = 0; i < packages->length; i++)
- {
- Identifier *pid = (*packages)[i];
- Package *pkg;
- Dsymbol *p = dst->lookup(pid);
- if (!p)
- {
- pkg = new Package(pid);
- dst->insert(pkg);
- pkg->parent = parent;
- pkg->symtab = new DsymbolTable();
- }
- else
- {
- pkg = p->isPackage();
- assert(pkg);
- // It might already be a module, not a package, but that needs
- // to be checked at a higher level, where a nice error message
- // can be generated.
- // dot net needs modules and packages with same name
-
- // But we still need a symbol table for it
- if (!pkg->symtab)
- pkg->symtab = new DsymbolTable();
- }
- parent = pkg;
- dst = pkg->symtab;
- if (ppkg && !*ppkg)
- *ppkg = pkg;
- if (pkg->isModule())
- {
- // Return the module so that a nice error message can be generated
- if (ppkg)
- *ppkg = (Package *)p;
- break;
- }
- }
- }
- if (pparent)
- *pparent = parent;
- return dst;
-}
-
-Dsymbol *Package::search(const Loc &loc, Identifier *ident, int flags)
-{
- //printf("%s Package::search('%s', flags = x%x)\n", toChars(), ident->toChars(), flags);
- flags &= ~SearchLocalsOnly; // searching an import is always transitive
- if (!isModule() && mod)
- {
- // Prefer full package name.
- Dsymbol *s = symtab ? symtab->lookup(ident) : NULL;
- if (s)
- return s;
- //printf("[%s] through pkdmod: %s\n", loc.toChars(), toChars());
- return mod->search(loc, ident, flags);
- }
-
- return ScopeDsymbol::search(loc, ident, flags);
-}
diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d
new file mode 100644
index 00000000000..768eaa05a08
--- /dev/null
+++ b/gcc/d/dmd/dmodule.d
@@ -0,0 +1,1608 @@
+/**
+ * Defines a package and module.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/module.html, Modules)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmodule.d, _dmodule.d)
+ * Documentation: https://dlang.org/phobos/dmd_dmodule.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmodule.d
+ */
+
+module dmd.dmodule;
+
+import core.stdc.stdio;
+import core.stdc.stdlib;
+import core.stdc.string;
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.compiler;
+import dmd.gluelayer;
+import dmd.dimport;
+import dmd.dmacro;
+import dmd.doc;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.parse;
+import dmd.cparse;
+import dmd.root.array;
+import dmd.root.file;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.port;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.semantic2;
+import dmd.semantic3;
+import dmd.target;
+import dmd.utils;
+import dmd.visitor;
+
+enum package_d = "package." ~ mars_ext;
+enum package_di = "package." ~ hdr_ext;
+
+/********************************************
+ * Look for the source file if it's different from filename.
+ * Look for .di, .d, directory, and along global.path.
+ * Does not open the file.
+ * Params:
+ * filename = as supplied by the user
+ * path = path to look for filename
+ * Returns:
+ * the found file name or
+ * `null` if it is not different from filename.
+ */
+private const(char)[] lookForSourceFile(const char[] filename, const char*[] path)
+{
+ //printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr);
+ /* Search along path[] for .di file, then .d file, then .i file, then .c file.
+ */
+ const sdi = FileName.forceExt(filename, hdr_ext);
+ if (FileName.exists(sdi) == 1)
+ return sdi;
+ scope(exit) FileName.free(sdi.ptr);
+
+ const sd = FileName.forceExt(filename, mars_ext);
+ if (FileName.exists(sd) == 1)
+ return sd;
+ scope(exit) FileName.free(sd.ptr);
+
+ const si = FileName.forceExt(filename, i_ext);
+ if (FileName.exists(si) == 1)
+ return si;
+ scope(exit) FileName.free(si.ptr);
+
+ const sc = FileName.forceExt(filename, c_ext);
+ if (FileName.exists(sc) == 1)
+ return sc;
+ scope(exit) FileName.free(sc.ptr);
+
+ if (FileName.exists(filename) == 2)
+ {
+ /* The filename exists and it's a directory.
+ * Therefore, the result should be: filename/package.d
+ * iff filename/package.d is a file
+ */
+ const ni = FileName.combine(filename, package_di);
+ if (FileName.exists(ni) == 1)
+ return ni;
+ FileName.free(ni.ptr);
+
+ const n = FileName.combine(filename, package_d);
+ if (FileName.exists(n) == 1)
+ return n;
+ FileName.free(n.ptr);
+ }
+ if (FileName.absolute(filename))
+ return null;
+ if (!path.length)
+ return null;
+ foreach (entry; path)
+ {
+ const p = entry.toDString();
+
+ const(char)[] n = FileName.combine(p, sdi);
+ if (FileName.exists(n) == 1) {
+ return n;
+ }
+ FileName.free(n.ptr);
+
+ n = FileName.combine(p, sd);
+ if (FileName.exists(n) == 1) {
+ return n;
+ }
+ FileName.free(n.ptr);
+
+ n = FileName.combine(p, si);
+ if (FileName.exists(n) == 1) {
+ return n;
+ }
+ FileName.free(n.ptr);
+
+ n = FileName.combine(p, sc);
+ if (FileName.exists(n) == 1) {
+ return n;
+ }
+ FileName.free(n.ptr);
+
+ const b = FileName.removeExt(filename);
+ n = FileName.combine(p, b);
+ FileName.free(b.ptr);
+ if (FileName.exists(n) == 2)
+ {
+ const n2i = FileName.combine(n, package_di);
+ if (FileName.exists(n2i) == 1)
+ return n2i;
+ FileName.free(n2i.ptr);
+ const n2 = FileName.combine(n, package_d);
+ if (FileName.exists(n2) == 1) {
+ return n2;
+ }
+ FileName.free(n2.ptr);
+ }
+ FileName.free(n.ptr);
+ }
+ return null;
+}
+
+// function used to call semantic3 on a module's dependencies
+void semantic3OnDependencies(Module m)
+{
+ if (!m)
+ return;
+
+ if (m.semanticRun > PASS.semantic3)
+ return;
+
+ m.semantic3(null);
+
+ foreach (i; 1 .. m.aimports.dim)
+ semantic3OnDependencies(m.aimports[i]);
+}
+
+/**
+ * Remove generated .di files on error and exit
+ */
+void removeHdrFilesAndFail(ref Param params, ref Modules modules)
+{
+ if (params.doHdrGeneration)
+ {
+ foreach (m; modules)
+ {
+ if (m.isHdrFile)
+ continue;
+ File.remove(m.hdrfile.toChars());
+ }
+ }
+
+ fatal();
+}
+
+/**
+ * Converts a chain of identifiers to the filename of the module
+ *
+ * Params:
+ * packages = the names of the "parent" packages
+ * ident = the name of the child package or module
+ *
+ * Returns:
+ * the filename of the child package or module
+ */
+private const(char)[] getFilename(Identifier[] packages, Identifier ident)
+{
+ const(char)[] filename = ident.toString();
+
+ if (packages.length == 0)
+ return filename;
+
+ OutBuffer buf;
+ OutBuffer dotmods;
+ auto modAliases = &global.params.modFileAliasStrings;
+
+ void checkModFileAlias(const(char)[] p)
+ {
+ /* Check and replace the contents of buf[] with
+ * an alias string from global.params.modFileAliasStrings[]
+ */
+ dotmods.writestring(p);
+ foreach_reverse (const m; *modAliases)
+ {
+ const q = strchr(m, '=');
+ assert(q);
+ if (dotmods.length == q - m && memcmp(dotmods.peekChars(), m, q - m) == 0)
+ {
+ buf.setsize(0);
+ auto rhs = q[1 .. strlen(q)];
+ if (rhs.length > 0 && (rhs[$ - 1] == '/' || rhs[$ - 1] == '\\'))
+ rhs = rhs[0 .. $ - 1]; // remove trailing separator
+ buf.writestring(rhs);
+ break; // last matching entry in ms[] wins
+ }
+ }
+ dotmods.writeByte('.');
+ }
+
+ foreach (pid; packages)
+ {
+ const p = pid.toString();
+ buf.writestring(p);
+ if (modAliases.dim)
+ checkModFileAlias(p);
+ version (Windows)
+ enum FileSeparator = '\\';
+ else
+ enum FileSeparator = '/';
+ buf.writeByte(FileSeparator);
+ }
+ buf.writestring(filename);
+ if (modAliases.dim)
+ checkModFileAlias(filename);
+ buf.writeByte(0);
+ filename = buf.extractSlice()[0 .. $ - 1];
+
+ return filename;
+}
+
+/***********************************************************
+ */
+extern (C++) class Package : ScopeDsymbol
+{
+ PKG isPkgMod = PKG.unknown;
+ uint tag; // auto incremented tag, used to mask package tree in scopes
+ Module mod; // !=null if isPkgMod == PKG.module_
+
+ final extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, ident);
+ __gshared uint packageTag;
+ this.tag = packageTag++;
+ }
+
+ override const(char)* kind() const
+ {
+ return "package";
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ // custom 'equals' for bug 17441. "package a" and "module a" are not equal
+ if (this == o)
+ return true;
+ auto p = cast(Package)o;
+ return p && isModule() == p.isModule() && ident.equals(p.ident);
+ }
+
+ /****************************************************
+ * Input:
+ * packages[] the pkg1.pkg2 of pkg1.pkg2.mod
+ * Returns:
+ * the symbol table that mod should be inserted into
+ * Output:
+ * *pparent the rightmost package, i.e. pkg2, or NULL if no packages
+ * *ppkg the leftmost package, i.e. pkg1, or NULL if no packages
+ */
+ extern (D) static DsymbolTable resolve(Identifier[] packages, Dsymbol* pparent, Package* ppkg)
+ {
+ DsymbolTable dst = Module.modules;
+ Dsymbol parent = null;
+ //printf("Package::resolve()\n");
+ if (ppkg)
+ *ppkg = null;
+ foreach (pid; packages)
+ {
+ Package pkg;
+ Dsymbol p = dst.lookup(pid);
+ if (!p)
+ {
+ pkg = new Package(Loc.initial, pid);
+ dst.insert(pkg);
+ pkg.parent = parent;
+ pkg.symtab = new DsymbolTable();
+ }
+ else
+ {
+ pkg = p.isPackage();
+ assert(pkg);
+ // It might already be a module, not a package, but that needs
+ // to be checked at a higher level, where a nice error message
+ // can be generated.
+ // dot net needs modules and packages with same name
+ // But we still need a symbol table for it
+ if (!pkg.symtab)
+ pkg.symtab = new DsymbolTable();
+ }
+ parent = pkg;
+ dst = pkg.symtab;
+ if (ppkg && !*ppkg)
+ *ppkg = pkg;
+ if (pkg.isModule())
+ {
+ // Return the module so that a nice error message can be generated
+ if (ppkg)
+ *ppkg = cast(Package)p;
+ break;
+ }
+ }
+
+ if (pparent)
+ *pparent = parent;
+ return dst;
+ }
+
+ override final inout(Package) isPackage() inout
+ {
+ return this;
+ }
+
+ /**
+ * Checks if pkg is a sub-package of this
+ *
+ * For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3',
+ * this function returns 'true'. If it is other way around or qualified
+ * package paths conflict function returns 'false'.
+ *
+ * Params:
+ * pkg = possible subpackage
+ *
+ * Returns:
+ * see description
+ */
+ final bool isAncestorPackageOf(const Package pkg) const
+ {
+ if (this == pkg)
+ return true;
+ if (!pkg || !pkg.parent)
+ return false;
+ return isAncestorPackageOf(pkg.parent.isPackage());
+ }
+
+ override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ //printf("%s Package.search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
+ flags &= ~SearchLocalsOnly; // searching an import is always transitive
+ if (!isModule() && mod)
+ {
+ // Prefer full package name.
+ Dsymbol s = symtab ? symtab.lookup(ident) : null;
+ if (s)
+ return s;
+ //printf("[%s] through pkdmod: %s\n", loc.toChars(), toChars());
+ return mod.search(loc, ident, flags);
+ }
+ return ScopeDsymbol.search(loc, ident, flags);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ final Module isPackageMod()
+ {
+ if (isPkgMod == PKG.module_)
+ {
+ return mod;
+ }
+ return null;
+ }
+
+ /**
+ * Checks for the existence of a package.d to set isPkgMod appropriately
+ * if isPkgMod == PKG.unknown
+ */
+ final void resolvePKGunknown()
+ {
+ if (isModule())
+ return;
+ if (isPkgMod != PKG.unknown)
+ return;
+
+ Identifier[] packages;
+ for (Dsymbol s = this.parent; s; s = s.parent)
+ packages ~= s.ident;
+ reverse(packages);
+
+ if (lookForSourceFile(getFilename(packages, ident), global.path ? (*global.path)[] : null))
+ Module.load(Loc(), packages, this.ident);
+ else
+ isPkgMod = PKG.package_;
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class Module : Package
+{
+ extern (C++) __gshared Module rootModule;
+ extern (C++) __gshared DsymbolTable modules; // symbol table of all modules
+ extern (C++) __gshared Modules amodules; // array of all modules
+ extern (C++) __gshared Dsymbols deferred; // deferred Dsymbol's needing semantic() run on them
+ extern (C++) __gshared Dsymbols deferred2; // deferred Dsymbol's needing semantic2() run on them
+ extern (C++) __gshared Dsymbols deferred3; // deferred Dsymbol's needing semantic3() run on them
+ extern (C++) __gshared uint dprogress; // progress resolving the deferred list
+
+ static void _init()
+ {
+ modules = new DsymbolTable();
+ }
+
+ /**
+ * Deinitializes the global state of the compiler.
+ *
+ * This can be used to restore the state set by `_init` to its original
+ * state.
+ */
+ static void deinitialize()
+ {
+ modules = modules.init;
+ }
+
+ extern (C++) __gshared AggregateDeclaration moduleinfo;
+
+ const(char)[] arg; // original argument name
+ ModuleDeclaration* md; // if !=null, the contents of the ModuleDeclaration declaration
+ const FileName srcfile; // input source file
+ const FileName objfile; // output .obj file
+ const FileName hdrfile; // 'header' file
+ FileName docfile; // output documentation file
+ FileBuffer* srcBuffer; // set during load(), free'd in parse()
+ uint errors; // if any errors in file
+ uint numlines; // number of lines in source file
+ bool isHdrFile; // if it is a header (.di) file
+ bool isCFile; // if it is a C (.c) file
+ bool isDocFile; // if it is a documentation input file, not D source
+ bool hasAlwaysInlines; // contains references to functions that must be inlined
+ bool isPackageFile; // if it is a package.d
+ Package pkg; // if isPackageFile is true, the Package that contains this package.d
+ Strings contentImportedFiles; // array of files whose content was imported
+ int needmoduleinfo;
+ int selfimports; // 0: don't know, 1: does not, 2: does
+ Dsymbol[void*] tagSymTab; /// ImportC: tag symbols that conflict with other symbols used as the index
+
+ /*************************************
+ * Return true if module imports itself.
+ */
+ bool selfImports()
+ {
+ //printf("Module::selfImports() %s\n", toChars());
+ if (selfimports == 0)
+ {
+ foreach (Module m; amodules)
+ m.insearch = 0;
+ selfimports = imports(this) + 1;
+ foreach (Module m; amodules)
+ m.insearch = 0;
+ }
+ return selfimports == 2;
+ }
+
+ int rootimports; // 0: don't know, 1: does not, 2: does
+
+ /*************************************
+ * Return true if module imports root module.
+ */
+ bool rootImports()
+ {
+ //printf("Module::rootImports() %s\n", toChars());
+ if (rootimports == 0)
+ {
+ foreach (Module m; amodules)
+ m.insearch = 0;
+ rootimports = 1;
+ foreach (Module m; amodules)
+ {
+ if (m.isRoot() && imports(m))
+ {
+ rootimports = 2;
+ break;
+ }
+ }
+ foreach (Module m; amodules)
+ m.insearch = 0;
+ }
+ return rootimports == 2;
+ }
+
+ int insearch;
+ Identifier searchCacheIdent;
+ Dsymbol searchCacheSymbol; // cached value of search
+ int searchCacheFlags; // cached flags
+
+ /**
+ * A root module is one that will be compiled all the way to
+ * object code. This field holds the root module that caused
+ * this module to be loaded. If this module is a root module,
+ * then it will be set to `this`. This is used to determine
+ * ownership of template instantiation.
+ */
+ Module importedFrom;
+
+ Dsymbols* decldefs; // top level declarations for this Module
+
+ Modules aimports; // all imported modules
+
+ uint debuglevel; // debug level
+ Identifiers* debugids; // debug identifiers
+ Identifiers* debugidsNot; // forward referenced debug identifiers
+
+ uint versionlevel; // version level
+ Identifiers* versionids; // version identifiers
+ Identifiers* versionidsNot; // forward referenced version identifiers
+
+ MacroTable macrotable; // document comment macros
+ Escape* _escapetable; // document comment escapes
+
+ size_t nameoffset; // offset of module name from start of ModuleInfo
+ size_t namelen; // length of module name in characters
+
+ extern (D) this(const ref Loc loc, const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
+ {
+ super(loc, ident);
+ const(char)[] srcfilename;
+ //printf("Module::Module(filename = '%.*s', ident = '%s')\n", cast(int)filename.length, filename.ptr, ident.toChars());
+ this.arg = filename;
+ srcfilename = FileName.defaultExt(filename, mars_ext);
+ if (target.run_noext && global.params.run &&
+ !FileName.ext(filename) &&
+ FileName.exists(srcfilename) == 0 &&
+ FileName.exists(filename) == 1)
+ {
+ FileName.free(srcfilename.ptr);
+ srcfilename = FileName.removeExt(filename); // just does a mem.strdup(filename)
+ }
+ else if (!FileName.equalsExt(srcfilename, mars_ext) &&
+ !FileName.equalsExt(srcfilename, hdr_ext) &&
+ !FileName.equalsExt(srcfilename, c_ext) &&
+ !FileName.equalsExt(srcfilename, i_ext) &&
+ !FileName.equalsExt(srcfilename, dd_ext))
+ {
+
+ error("source file name '%.*s' must have .%.*s extension",
+ cast(int)srcfilename.length, srcfilename.ptr,
+ cast(int)mars_ext.length, mars_ext.ptr);
+ fatal();
+ }
+
+ srcfile = FileName(srcfilename);
+ objfile = setOutfilename(global.params.objname, global.params.objdir, filename, target.obj_ext);
+ if (doDocComment)
+ setDocfile();
+ if (doHdrGen)
+ hdrfile = setOutfilename(global.params.hdrname, global.params.hdrdir, arg, hdr_ext);
+ }
+
+ extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
+ {
+ this(Loc.initial, filename, ident, doDocComment, doHdrGen);
+ }
+
+ static Module create(const(char)* filename, Identifier ident, int doDocComment, int doHdrGen)
+ {
+ return create(filename.toDString, ident, doDocComment, doHdrGen);
+ }
+
+ extern (D) static Module create(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
+ {
+ return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen);
+ }
+
+ extern (C++) static Module load(Loc loc, Identifiers* packages, Identifier ident)
+ {
+ return load(loc, packages ? (*packages)[] : null, ident);
+ }
+
+ extern (D) static Module load(Loc loc, Identifier[] packages, Identifier ident)
+ {
+ //printf("Module::load(ident = '%s')\n", ident.toChars());
+ // Build module filename by turning:
+ // foo.bar.baz
+ // into:
+ // foo\bar\baz
+ const(char)[] filename = getFilename(packages, ident);
+ // Look for the source file
+ if (const result = lookForSourceFile(filename, global.path ? (*global.path)[] : null))
+ filename = result; // leaks
+
+ auto m = new Module(loc, filename, ident, 0, 0);
+
+ if (!m.read(loc))
+ return null;
+ if (global.params.verbose)
+ {
+ OutBuffer buf;
+ foreach (pid; packages)
+ {
+ buf.writestring(pid.toString());
+ buf.writeByte('.');
+ }
+ buf.printf("%s\t(%s)", ident.toChars(), m.srcfile.toChars());
+ message("import %s", buf.peekChars());
+ }
+ m = m.parse();
+
+ // Call onImport here because if the module is going to be compiled then we
+ // need to determine it early because it affects semantic analysis. This is
+ // being done after parsing the module so the full module name can be taken
+ // from whatever was declared in the file.
+ if (!m.isRoot() && Compiler.onImport(m))
+ {
+ m.importedFrom = m;
+ assert(m.isRoot());
+ }
+ return m;
+ }
+
+ override const(char)* kind() const
+ {
+ return "module";
+ }
+
+ /*********************************************
+ * Combines things into output file name for .html and .di files.
+ * Input:
+ * name Command line name given for the file, NULL if none
+ * dir Command line directory given for the file, NULL if none
+ * arg Name of the source file
+ * ext File name extension to use if 'name' is NULL
+ * global.params.preservePaths get output path from arg
+ * srcfile Input file - output file name must not match input file
+ */
+ extern(D) FileName setOutfilename(const(char)[] name, const(char)[] dir, const(char)[] arg, const(char)[] ext)
+ {
+ const(char)[] docfilename;
+ if (name)
+ {
+ docfilename = name;
+ }
+ else
+ {
+ const(char)[] argdoc;
+ OutBuffer buf;
+ if (arg == "__stdin.d")
+ {
+ version (Posix)
+ import core.sys.posix.unistd : getpid;
+ else version (Windows)
+ import core.sys.windows.winbase : getpid = GetCurrentProcessId;
+ buf.printf("__stdin_%d.d", getpid());
+ arg = buf[];
+ }
+ if (global.params.preservePaths)
+ argdoc = arg;
+ else
+ argdoc = FileName.name(arg);
+ // If argdoc doesn't have an absolute path, make it relative to dir
+ if (!FileName.absolute(argdoc))
+ {
+ //FileName::ensurePathExists(dir);
+ argdoc = FileName.combine(dir, argdoc);
+ }
+ docfilename = FileName.forceExt(argdoc, ext);
+ }
+ if (FileName.equals(docfilename, srcfile.toString()))
+ {
+ error("source file and output file have same name '%s'", srcfile.toChars());
+ fatal();
+ }
+ return FileName(docfilename);
+ }
+
+ extern (D) void setDocfile()
+ {
+ docfile = setOutfilename(global.params.docname, global.params.docdir, arg, doc_ext);
+ }
+
+ /**
+ * Loads the source buffer from the given read result into `this.srcBuffer`.
+ *
+ * Will take ownership of the buffer located inside `readResult`.
+ *
+ * Params:
+ * loc = the location
+ * readResult = the result of reading a file containing the source code
+ *
+ * Returns: `true` if successful
+ */
+ bool loadSourceBuffer(const ref Loc loc, ref File.ReadResult readResult)
+ {
+ //printf("Module::loadSourceBuffer('%s') file '%s'\n", toChars(), srcfile.toChars());
+ // take ownership of buffer
+ srcBuffer = new FileBuffer(readResult.extractSlice());
+ if (readResult.success)
+ return true;
+
+ if (FileName.equals(srcfile.toString(), "object.d"))
+ {
+ .error(loc, "cannot find source code for runtime library file 'object.d'");
+ errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
+ const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found";
+ errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr);
+ }
+ else
+ {
+ // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module
+ bool isPackageMod = (strcmp(toChars(), "package") != 0) && (strcmp(srcfile.name(), package_d) == 0 || (strcmp(srcfile.name(), package_di) == 0));
+ if (isPackageMod)
+ .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars());
+ else
+ error(loc, "is in file '%s' which cannot be read", srcfile.toChars());
+ }
+ if (!global.gag)
+ {
+ /* Print path
+ */
+ if (global.path)
+ {
+ foreach (i, p; *global.path)
+ fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p);
+ }
+ else
+ {
+ fprintf(stderr, "Specify path to file '%s' with -I switch\n", srcfile.toChars());
+ }
+
+ removeHdrFilesAndFail(global.params, Module.amodules);
+ }
+ return false;
+ }
+
+ /**
+ * Reads the file from `srcfile` and loads the source buffer.
+ *
+ * If makefile module dependency is requested, we add this module
+ * to the list of dependencies from here.
+ *
+ * Params:
+ * loc = the location
+ *
+ * Returns: `true` if successful
+ * See_Also: loadSourceBuffer
+ */
+ bool read(const ref Loc loc)
+ {
+ if (srcBuffer)
+ return true; // already read
+
+ //printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
+ auto readResult = File.read(srcfile.toChars());
+
+ if (global.params.emitMakeDeps)
+ {
+ global.params.makeDeps.push(srcfile.toChars());
+ }
+
+ return loadSourceBuffer(loc, readResult);
+ }
+
+ /// syntactic parse
+ Module parse()
+ {
+ return parseModule!ASTCodegen();
+ }
+
+ /// ditto
+ extern (D) Module parseModule(AST)()
+ {
+ enum Endian { little, big}
+ enum SourceEncoding { utf16, utf32}
+
+ /*
+ * Convert a buffer from UTF32 to UTF8
+ * Params:
+ * Endian = is the buffer big/little endian
+ * buf = buffer of UTF32 data
+ * Returns:
+ * input buffer reencoded as UTF8
+ */
+
+ char[] UTF32ToUTF8(Endian endian)(const(char)[] buf)
+ {
+ static if (endian == Endian.little)
+ alias readNext = Port.readlongLE;
+ else
+ alias readNext = Port.readlongBE;
+
+ if (buf.length & 3)
+ {
+ error("odd length of UTF-32 char source %llu", cast(ulong) buf.length);
+ fatal();
+ }
+
+ const (uint)[] eBuf = cast(const(uint)[])buf;
+
+ OutBuffer dbuf;
+ dbuf.reserve(eBuf.length);
+
+ foreach (i; 0 .. eBuf.length)
+ {
+ const u = readNext(&eBuf[i]);
+ if (u & ~0x7F)
+ {
+ if (u > 0x10FFFF)
+ {
+ error("UTF-32 value %08x greater than 0x10FFFF", u);
+ fatal();
+ }
+ dbuf.writeUTF8(u);
+ }
+ else
+ dbuf.writeByte(u);
+ }
+ dbuf.writeByte(0); //add null terminator
+ return dbuf.extractSlice();
+ }
+
+ /*
+ * Convert a buffer from UTF16 to UTF8
+ * Params:
+ * Endian = is the buffer big/little endian
+ * buf = buffer of UTF16 data
+ * Returns:
+ * input buffer reencoded as UTF8
+ */
+
+ char[] UTF16ToUTF8(Endian endian)(const(char)[] buf)
+ {
+ static if (endian == Endian.little)
+ alias readNext = Port.readwordLE;
+ else
+ alias readNext = Port.readwordBE;
+
+ if (buf.length & 1)
+ {
+ error("odd length of UTF-16 char source %llu", cast(ulong) buf.length);
+ fatal();
+ }
+
+ const (ushort)[] eBuf = cast(const(ushort)[])buf;
+
+ OutBuffer dbuf;
+ dbuf.reserve(eBuf.length);
+
+ //i will be incremented in the loop for high codepoints
+ foreach (ref i; 0 .. eBuf.length)
+ {
+ uint u = readNext(&eBuf[i]);
+ if (u & ~0x7F)
+ {
+ if (0xD800 <= u && u < 0xDC00)
+ {
+ i++;
+ if (i >= eBuf.length)
+ {
+ error("surrogate UTF-16 high value %04x at end of file", u);
+ fatal();
+ }
+ const u2 = readNext(&eBuf[i]);
+ if (u2 < 0xDC00 || 0xE000 <= u2)
+ {
+ error("surrogate UTF-16 low value %04x out of range", u2);
+ fatal();
+ }
+ u = (u - 0xD7C0) << 10;
+ u |= (u2 - 0xDC00);
+ }
+ else if (u >= 0xDC00 && u <= 0xDFFF)
+ {
+ error("unpaired surrogate UTF-16 value %04x", u);
+ fatal();
+ }
+ else if (u == 0xFFFE || u == 0xFFFF)
+ {
+ error("illegal UTF-16 value %04x", u);
+ fatal();
+ }
+ dbuf.writeUTF8(u);
+ }
+ else
+ dbuf.writeByte(u);
+ }
+ dbuf.writeByte(0); //add a terminating null byte
+ return dbuf.extractSlice();
+ }
+
+ const(char)* srcname = srcfile.toChars();
+ //printf("Module::parse(srcname = '%s')\n", srcname);
+ isPackageFile = (strcmp(srcfile.name(), package_d) == 0 ||
+ strcmp(srcfile.name(), package_di) == 0);
+ const(char)[] buf = cast(const(char)[]) srcBuffer.data;
+
+ bool needsReencoding = true;
+ bool hasBOM = true; //assume there's a BOM
+ Endian endian;
+ SourceEncoding sourceEncoding;
+
+ if (buf.length >= 2)
+ {
+ /* Convert all non-UTF-8 formats to UTF-8.
+ * BOM : http://www.unicode.org/faq/utf_bom.html
+ * 00 00 FE FF UTF-32BE, big-endian
+ * FF FE 00 00 UTF-32LE, little-endian
+ * FE FF UTF-16BE, big-endian
+ * FF FE UTF-16LE, little-endian
+ * EF BB BF UTF-8
+ */
+ if (buf[0] == 0xFF && buf[1] == 0xFE)
+ {
+ endian = Endian.little;
+
+ sourceEncoding = buf.length >= 4 && buf[2] == 0 && buf[3] == 0
+ ? SourceEncoding.utf32
+ : SourceEncoding.utf16;
+ }
+ else if (buf[0] == 0xFE && buf[1] == 0xFF)
+ {
+ endian = Endian.big;
+ sourceEncoding = SourceEncoding.utf16;
+ }
+ else if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFE && buf[3] == 0xFF)
+ {
+ endian = Endian.big;
+ sourceEncoding = SourceEncoding.utf32;
+ }
+ else if (buf.length >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
+ {
+ needsReencoding = false;//utf8 with BOM
+ }
+ else
+ {
+ /* There is no BOM. Make use of Arcane Jill's insight that
+ * the first char of D source must be ASCII to
+ * figure out the encoding.
+ */
+ hasBOM = false;
+ if (buf.length >= 4 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
+ {
+ endian = Endian.little;
+ sourceEncoding = SourceEncoding.utf32;
+ }
+ else if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0)
+ {
+ endian = Endian.big;
+ sourceEncoding = SourceEncoding.utf32;
+ }
+ else if (buf.length >= 2 && buf[1] == 0) //try to check for UTF-16
+ {
+ endian = Endian.little;
+ sourceEncoding = SourceEncoding.utf16;
+ }
+ else if (buf[0] == 0)
+ {
+ endian = Endian.big;
+ sourceEncoding = SourceEncoding.utf16;
+ }
+ else {
+ // It's UTF-8
+ needsReencoding = false;
+ if (buf[0] >= 0x80)
+ {
+ error("source file must start with BOM or ASCII character, not \\x%02X", buf[0]);
+ fatal();
+ }
+ }
+ }
+ //throw away BOM
+ if (hasBOM)
+ {
+ if (!needsReencoding) buf = buf[3..$];// utf-8 already
+ else if (sourceEncoding == SourceEncoding.utf32) buf = buf[4..$];
+ else buf = buf[2..$]; //utf 16
+ }
+ }
+ // Assume the buffer is from memory and has not be read from disk. Assume UTF-8.
+ else if (buf.length >= 1 && (buf[0] == '\0' || buf[0] == 0x1A))
+ needsReencoding = false;
+ //printf("%s, %d, %d, %d\n", srcfile.name.toChars(), needsReencoding, endian == Endian.little, sourceEncoding == SourceEncoding.utf16);
+ if (needsReencoding)
+ {
+ if (sourceEncoding == SourceEncoding.utf16)
+ {
+ buf = endian == Endian.little
+ ? UTF16ToUTF8!(Endian.little)(buf)
+ : UTF16ToUTF8!(Endian.big)(buf);
+ }
+ else
+ {
+ buf = endian == Endian.little
+ ? UTF32ToUTF8!(Endian.little)(buf)
+ : UTF32ToUTF8!(Endian.big)(buf);
+ }
+ }
+
+ /* If it starts with the string "Ddoc", then it's a documentation
+ * source file.
+ */
+ if (buf.length>= 4 && buf[0..4] == "Ddoc")
+ {
+ comment = buf.ptr + 4;
+ isDocFile = true;
+ if (!docfile)
+ setDocfile();
+ return this;
+ }
+ /* If it has the extension ".dd", it is also a documentation
+ * source file. Documentation source files may begin with "Ddoc"
+ * but do not have to if they have the .dd extension.
+ * https://issues.dlang.org/show_bug.cgi?id=15465
+ */
+ if (FileName.equalsExt(arg, dd_ext))
+ {
+ comment = buf.ptr; // the optional Ddoc, if present, is handled above.
+ isDocFile = true;
+ if (!docfile)
+ setDocfile();
+ return this;
+ }
+ /* If it has the extension ".di", it is a "header" file.
+ */
+ if (FileName.equalsExt(arg, hdr_ext))
+ {
+ isHdrFile = true;
+ }
+
+ /* If it has the extension ".c", it is a "C" file.
+ * If it has the extension ".i", it is a preprocessed "C" file.
+ */
+ if (FileName.equalsExt(arg, c_ext) || FileName.equalsExt(arg, i_ext))
+ {
+ isCFile = true;
+
+ scope p = new CParser!AST(this, buf, cast(bool) docfile, target.c);
+ p.nextToken();
+ members = p.parseModule();
+ md = p.md;
+ numlines = p.scanloc.linnum;
+ }
+ else
+ {
+ scope p = new Parser!AST(this, buf, cast(bool) docfile);
+ p.nextToken();
+ members = p.parseModule();
+ md = p.md;
+ numlines = p.scanloc.linnum;
+ }
+ srcBuffer.destroy();
+ srcBuffer = null;
+ /* The symbol table into which the module is to be inserted.
+ */
+ DsymbolTable dst;
+ if (md)
+ {
+ /* A ModuleDeclaration, md, was provided.
+ * The ModuleDeclaration sets the packages this module appears in, and
+ * the name of this module.
+ */
+ this.ident = md.id;
+ Package ppack = null;
+ dst = Package.resolve(md.packages, &this.parent, &ppack);
+
+ // Mark the package path as accessible from the current module
+ // https://issues.dlang.org/show_bug.cgi?id=21661
+ // Code taken from Import.addPackageAccess()
+ if (md.packages.length > 0)
+ {
+ // module a.b.c.d;
+ auto p = ppack; // a
+ addAccessiblePackage(p, Visibility(Visibility.Kind.private_));
+ foreach (id; md.packages[1 .. $]) // [b, c]
+ {
+ p = cast(Package) p.symtab.lookup(id);
+ if (p is null)
+ break;
+ addAccessiblePackage(p, Visibility(Visibility.Kind.private_));
+ }
+ }
+ assert(dst);
+ Module m = ppack ? ppack.isModule() : null;
+ if (m && (strcmp(m.srcfile.name(), package_d) != 0 &&
+ strcmp(m.srcfile.name(), package_di) != 0))
+ {
+ .error(md.loc, "package name '%s' conflicts with usage as a module name in file %s", ppack.toPrettyChars(), m.srcfile.toChars());
+ }
+ }
+ else
+ {
+ /* The name of the module is set to the source file name.
+ * There are no packages.
+ */
+ dst = modules; // and so this module goes into global module symbol table
+ /* Check to see if module name is a valid identifier
+ */
+ if (!Identifier.isValidIdentifier(this.ident.toChars()))
+ error("has non-identifier characters in filename, use module declaration instead");
+ }
+ // Insert module into the symbol table
+ Dsymbol s = this;
+ if (isPackageFile)
+ {
+ /* If the source tree is as follows:
+ * pkg/
+ * +- package.d
+ * +- common.d
+ * the 'pkg' will be incorporated to the internal package tree in two ways:
+ * import pkg;
+ * and:
+ * import pkg.common;
+ *
+ * If both are used in one compilation, 'pkg' as a module (== pkg/package.d)
+ * and a package name 'pkg' will conflict each other.
+ *
+ * To avoid the conflict:
+ * 1. If preceding package name insertion had occurred by Package::resolve,
+ * reuse the previous wrapping 'Package' if it exists
+ * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here.
+ *
+ * Then change Package::isPkgMod to PKG.module_ and set Package::mod.
+ *
+ * Note that the 'wrapping Package' is the Package that contains package.d and other submodules,
+ * the one inserted to the symbol table.
+ */
+ auto ps = dst.lookup(ident);
+ Package p = ps ? ps.isPackage() : null;
+ if (p is null)
+ {
+ p = new Package(Loc.initial, ident);
+ p.tag = this.tag; // reuse the same package tag
+ p.symtab = new DsymbolTable();
+ }
+ this.tag = p.tag; // reuse the 'older' package tag
+ this.pkg = p;
+ p.parent = this.parent;
+ p.isPkgMod = PKG.module_;
+ p.mod = this;
+ s = p;
+ }
+ if (!dst.insert(s))
+ {
+ /* It conflicts with a name that is already in the symbol table.
+ * Figure out what went wrong, and issue error message.
+ */
+ Dsymbol prev = dst.lookup(ident);
+ assert(prev);
+ if (Module mprev = prev.isModule())
+ {
+ if (!FileName.equals(srcname, mprev.srcfile.toChars()))
+ error(loc, "from file %s conflicts with another module %s from file %s", srcname, mprev.toChars(), mprev.srcfile.toChars());
+ else if (isRoot() && mprev.isRoot())
+ error(loc, "from file %s is specified twice on the command line", srcname);
+ else
+ error(loc, "from file %s must be imported with 'import %s;'", srcname, toPrettyChars());
+ // https://issues.dlang.org/show_bug.cgi?id=14446
+ // Return previously parsed module to avoid AST duplication ICE.
+ return mprev;
+ }
+ else if (Package pkg = prev.isPackage())
+ {
+ // 'package.d' loaded after a previous 'Package' insertion
+ if (isPackageFile)
+ amodules.push(this); // Add to global array of all modules
+ else
+ error(md ? md.loc : loc, "from file %s conflicts with package name %s", srcname, pkg.toChars());
+ }
+ else
+ assert(global.errors);
+ }
+ else
+ {
+ // Add to global array of all modules
+ amodules.push(this);
+ }
+ Compiler.onParseModule(this);
+ return this;
+ }
+
+ override void importAll(Scope* prevsc)
+ {
+ //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
+ if (_scope)
+ return; // already done
+ if (isDocFile)
+ {
+ error("is a Ddoc file, cannot import it");
+ return;
+ }
+
+ /* Note that modules get their own scope, from scratch.
+ * This is so regardless of where in the syntax a module
+ * gets imported, it is unaffected by context.
+ * Ignore prevsc.
+ */
+ Scope* sc = Scope.createGlobal(this); // create root scope
+
+ if (md && md.msg)
+ md.msg = semanticString(sc, md.msg, "deprecation message");
+
+ // Add import of "object", even for the "object" module.
+ // If it isn't there, some compiler rewrites, like
+ // classinst == classinst -> .object.opEquals(classinst, classinst)
+ // would fail inside object.d.
+ if (members.dim == 0 || (*members)[0].ident != Id.object ||
+ (*members)[0].isImport() is null)
+ {
+ auto im = new Import(Loc.initial, null, Id.object, null, 0);
+ members.shift(im);
+ }
+ if (!symtab)
+ {
+ // Add all symbols into module's symbol table
+ symtab = new DsymbolTable();
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ Dsymbol s = (*members)[i];
+ s.addMember(sc, sc.scopesym);
+ }
+ }
+ // anything else should be run after addMember, so version/debug symbols are defined
+ /* Set scope for the symbols so that if we forward reference
+ * a symbol, it can possibly be resolved on the spot.
+ * If this works out well, it can be extended to all modules
+ * before any semantic() on any of them.
+ */
+ setScope(sc); // remember module scope for semantic
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ Dsymbol s = (*members)[i];
+ s.setScope(sc);
+ }
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ Dsymbol s = (*members)[i];
+ s.importAll(sc);
+ }
+ sc = sc.pop();
+ sc.pop(); // 2 pops because Scope.createGlobal() created 2
+ }
+
+ /**********************************
+ * Determine if we need to generate an instance of ModuleInfo
+ * for this Module.
+ */
+ int needModuleInfo()
+ {
+ //printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
+ return needmoduleinfo || global.params.cov;
+ }
+
+ /*******************************************
+ * Print deprecation warning if we're deprecated, when
+ * this module is imported from scope sc.
+ *
+ * Params:
+ * sc = the scope into which we are imported
+ * loc = the location of the import statement
+ */
+ void checkImportDeprecation(const ref Loc loc, Scope* sc)
+ {
+ if (md && md.isdeprecated && !sc.isDeprecated)
+ {
+ Expression msg = md.msg;
+ if (StringExp se = msg ? msg.toStringExp() : null)
+ {
+ const slice = se.peekString();
+ deprecation(loc, "is deprecated - %.*s", cast(int)slice.length, slice.ptr);
+ }
+ else
+ deprecation(loc, "is deprecated");
+ }
+ }
+
+ override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ /* Since modules can be circularly referenced,
+ * need to stop infinite recursive searches.
+ * This is done with the cache.
+ */
+ //printf("%s Module.search('%s', flags = x%x) insearch = %d\n", toChars(), ident.toChars(), flags, insearch);
+ if (insearch)
+ return null;
+
+ /* Qualified module searches always search their imports,
+ * even if SearchLocalsOnly
+ */
+ if (!(flags & SearchUnqualifiedModule))
+ flags &= ~(SearchUnqualifiedModule | SearchLocalsOnly);
+
+ if (searchCacheIdent == ident && searchCacheFlags == flags)
+ {
+ //printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n",
+ // toChars(), ident.toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol.toChars() : "null");
+ return searchCacheSymbol;
+ }
+
+ uint errors = global.errors;
+
+ insearch = 1;
+ Dsymbol s = ScopeDsymbol.search(loc, ident, flags);
+ insearch = 0;
+
+ if (errors == global.errors)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=10752
+ // Can cache the result only when it does not cause
+ // access error so the side-effect should be reproduced in later search.
+ searchCacheIdent = ident;
+ searchCacheSymbol = s;
+ searchCacheFlags = flags;
+ }
+ return s;
+ }
+
+ override bool isPackageAccessible(Package p, Visibility visibility, int flags = 0)
+ {
+ if (insearch) // don't follow import cycles
+ return false;
+ insearch = true;
+ scope (exit)
+ insearch = false;
+ if (flags & IgnorePrivateImports)
+ visibility = Visibility(Visibility.Kind.public_); // only consider public imports
+ return super.isPackageAccessible(p, visibility);
+ }
+
+ override Dsymbol symtabInsert(Dsymbol s)
+ {
+ searchCacheIdent = null; // symbol is inserted, so invalidate cache
+ return Package.symtabInsert(s);
+ }
+
+ void deleteObjFile()
+ {
+ if (global.params.obj)
+ File.remove(objfile.toChars());
+ if (docfile)
+ File.remove(docfile.toChars());
+ }
+
+ /*******************************************
+ * Can't run semantic on s now, try again later.
+ */
+ extern (D) static void addDeferredSemantic(Dsymbol s)
+ {
+ //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
+ deferred.push(s);
+ }
+
+ extern (D) static void addDeferredSemantic2(Dsymbol s)
+ {
+ //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
+ deferred2.push(s);
+ }
+
+ extern (D) static void addDeferredSemantic3(Dsymbol s)
+ {
+ //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
+ deferred3.push(s);
+ }
+
+ /******************************************
+ * Run semantic() on deferred symbols.
+ */
+ static void runDeferredSemantic()
+ {
+ if (dprogress == 0)
+ return;
+
+ __gshared int nested;
+ if (nested)
+ return;
+ //if (deferred.dim) printf("+Module::runDeferredSemantic(), len = %d\n", deferred.dim);
+ nested++;
+
+ size_t len;
+ do
+ {
+ dprogress = 0;
+ len = deferred.dim;
+ if (!len)
+ break;
+
+ Dsymbol* todo;
+ Dsymbol* todoalloc = null;
+ Dsymbol tmp;
+ if (len == 1)
+ {
+ todo = &tmp;
+ }
+ else
+ {
+ todo = cast(Dsymbol*)Mem.check(malloc(len * Dsymbol.sizeof));
+ todoalloc = todo;
+ }
+ memcpy(todo, deferred.tdata(), len * Dsymbol.sizeof);
+ deferred.setDim(0);
+
+ foreach (i; 0..len)
+ {
+ Dsymbol s = todo[i];
+ s.dsymbolSemantic(null);
+ //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars());
+ }
+ //printf("\tdeferred.dim = %d, len = %d, dprogress = %d\n", deferred.dim, len, dprogress);
+ if (todoalloc)
+ free(todoalloc);
+ }
+ while (deferred.dim < len || dprogress); // while making progress
+ nested--;
+ //printf("-Module::runDeferredSemantic(), len = %d\n", deferred.dim);
+ }
+
+ static void runDeferredSemantic2()
+ {
+ Module.runDeferredSemantic();
+
+ Dsymbols* a = &Module.deferred2;
+ for (size_t i = 0; i < a.dim; i++)
+ {
+ Dsymbol s = (*a)[i];
+ //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
+ s.semantic2(null);
+
+ if (global.errors)
+ break;
+ }
+ a.setDim(0);
+ }
+
+ static void runDeferredSemantic3()
+ {
+ Module.runDeferredSemantic2();
+
+ Dsymbols* a = &Module.deferred3;
+ for (size_t i = 0; i < a.dim; i++)
+ {
+ Dsymbol s = (*a)[i];
+ //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
+ s.semantic3(null);
+
+ if (global.errors)
+ break;
+ }
+ a.setDim(0);
+ }
+
+ extern (D) static void clearCache()
+ {
+ foreach (Module m; amodules)
+ m.searchCacheIdent = null;
+ }
+
+ /************************************
+ * Recursively look at every module this module imports,
+ * return true if it imports m.
+ * Can be used to detect circular imports.
+ */
+ int imports(Module m)
+ {
+ //printf("%s Module::imports(%s)\n", toChars(), m.toChars());
+ version (none)
+ {
+ foreach (i, Module mi; aimports)
+ printf("\t[%d] %s\n", cast(int) i, mi.toChars());
+ }
+ foreach (Module mi; aimports)
+ {
+ if (mi == m)
+ return true;
+ if (!mi.insearch)
+ {
+ mi.insearch = 1;
+ int r = mi.imports(m);
+ if (r)
+ return r;
+ }
+ }
+ return false;
+ }
+
+ bool isRoot()
+ {
+ return this.importedFrom == this;
+ }
+
+ // true if the module source file is directly
+ // listed in command line.
+ bool isCoreModule(Identifier ident)
+ {
+ return this.ident == ident && parent && parent.ident == Id.core && !parent.parent;
+ }
+
+ // Back end
+ int doppelganger; // sub-module
+ Symbol* cov; // private uint[] __coverage;
+ uint* covb; // bit array of valid code line numbers
+ Symbol* sictor; // module order independent constructor
+ Symbol* sctor; // module constructor
+ Symbol* sdtor; // module destructor
+ Symbol* ssharedctor; // module shared constructor
+ Symbol* sshareddtor; // module shared destructor
+ Symbol* stest; // module unit test
+ Symbol* sfilename; // symbol for filename
+
+ uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line]
+
+ override inout(Module) isModule() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ /***********************************************
+ * Writes this module's fully-qualified name to buf
+ * Params:
+ * buf = The buffer to write to
+ */
+ void fullyQualifiedName(ref OutBuffer buf)
+ {
+ buf.writestring(ident.toString());
+
+ for (auto package_ = parent; package_ !is null; package_ = package_.parent)
+ {
+ buf.prependstring(".");
+ buf.prependstring(package_.ident.toChars());
+ }
+ }
+
+ /** Lazily initializes and returns the escape table.
+ Turns out it eats a lot of memory.
+ */
+ extern(D) Escape* escapetable()
+ {
+ if (!_escapetable)
+ _escapetable = new Escape();
+ return _escapetable;
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) struct ModuleDeclaration
+{
+ Loc loc;
+ Identifier id;
+ Identifier[] packages; // array of Identifier's representing packages
+ bool isdeprecated; // if it is a deprecated module
+ Expression msg;
+
+ extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Expression msg, bool isdeprecated)
+ {
+ this.loc = loc;
+ this.packages = packages;
+ this.id = id;
+ this.msg = msg;
+ this.isdeprecated = isdeprecated;
+ }
+
+ extern (C++) const(char)* toChars() const
+ {
+ OutBuffer buf;
+ foreach (pid; packages)
+ {
+ buf.writestring(pid.toString());
+ buf.writeByte('.');
+ }
+ buf.writestring(id.toString());
+ return buf.extractChars();
+ }
+
+ /// Provide a human readable representation
+ extern (D) const(char)[] toString() const
+ {
+ return this.toChars().toDString;
+ }
+}
diff --git a/gcc/d/dmd/doc.c b/gcc/d/dmd/doc.c
deleted file mode 100644
index 5d2da1ccdb9..00000000000
--- a/gcc/d/dmd/doc.c
+++ /dev/null
@@ -1,2807 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/doc.c
- */
-
-// This implements the Ddoc capability.
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-#include "root/root.h"
-#include "root/port.h"
-#include "root/aav.h"
-
-#include "attrib.h"
-#include "cond.h"
-#include "mars.h"
-#include "dsymbol.h"
-#include "macro.h"
-#include "template.h"
-#include "lexer.h"
-#include "aggregate.h"
-#include "declaration.h"
-#include "statement.h"
-#include "enum.h"
-#include "id.h"
-#include "module.h"
-#include "scope.h"
-#include "hdrgen.h"
-#include "doc.h"
-#include "mtype.h"
-#include "utf.h"
-
-void emitMemberComments(ScopeDsymbol *sds, OutBuffer *buf, Scope *sc);
-void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc);
-void emitComment(Dsymbol *s, OutBuffer *buf, Scope *sc);
-
-struct Escape
-{
- const char *strings[256];
-
- const char *escapeChar(unsigned c);
-};
-
-class Section
-{
-public:
- const utf8_t *name;
- size_t namelen;
-
- const utf8_t *body;
- size_t bodylen;
-
- int nooutput;
-
- virtual void write(Loc loc, DocComment *dc, Scope *sc, Dsymbols *a, OutBuffer *buf);
-};
-
-class ParamSection : public Section
-{
-public:
- void write(Loc loc, DocComment *dc, Scope *sc, Dsymbols *a, OutBuffer *buf);
-};
-
-class MacroSection : public Section
-{
-public:
- void write(Loc loc, DocComment *dc, Scope *sc, Dsymbols *a, OutBuffer *buf);
-};
-
-typedef Array<Section *> Sections;
-
-struct DocComment
-{
- Sections sections; // Section*[]
-
- Section *summary;
- Section *copyright;
- Section *macros;
- Macro **pmacrotable;
- Escape **pescapetable;
-
- Dsymbols a;
-
- DocComment() :
- summary(NULL), copyright(NULL), macros(NULL), pmacrotable(NULL), pescapetable(NULL)
- { }
-
- static DocComment *parse(Dsymbol *s, const utf8_t *comment);
- static void parseMacros(Escape **pescapetable, Macro **pmacrotable, const utf8_t *m, size_t mlen);
- static void parseEscapes(Escape **pescapetable, const utf8_t *textstart, size_t textlen);
-
- void parseSections(const utf8_t *comment);
- void writeSections(Scope *sc, Dsymbols *a, OutBuffer *buf);
-};
-
-
-int cmp(const char *stringz, const void *s, size_t slen);
-int icmp(const char *stringz, const void *s, size_t slen);
-bool isDitto(const utf8_t *comment);
-const utf8_t *skipwhitespace(const utf8_t *p);
-size_t skiptoident(OutBuffer *buf, size_t i);
-size_t skippastident(OutBuffer *buf, size_t i);
-size_t skippastURL(OutBuffer *buf, size_t i);
-void highlightText(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset);
-void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset);
-void highlightCode(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset);
-void highlightCode2(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset);
-void highlightCode3(Scope *sc, OutBuffer *buf, const utf8_t *p, const utf8_t *pend);
-TypeFunction *isTypeFunction(Dsymbol *s);
-Parameter *isFunctionParameter(Dsymbols *a, const utf8_t *p, size_t len);
-TemplateParameter *isTemplateParameter(Dsymbols *a, const utf8_t *p, size_t len);
-
-bool isIdStart(const utf8_t *p);
-bool isCVariadicArg(const utf8_t *p, size_t len);
-bool isIdTail(const utf8_t *p);
-bool isIndentWS(const utf8_t *p);
-int utfStride(const utf8_t *p);
-
-// Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one).
-bool isCVariadicParameter(Dsymbols *a, const utf8_t *p, size_t len)
-{
- for (size_t i = 0; i < a->length; i++)
- {
- TypeFunction *tf = isTypeFunction((*a)[i]);
- if (tf && tf->parameterList.varargs == VARARGvariadic && cmp("...", p, len) == 0)
- return true;
- }
- return false;
-}
-
-/****************************************************
- */
-static Parameter *isFunctionParameter(Dsymbol *s, const utf8_t *p, size_t len)
-{
- TypeFunction *tf = isTypeFunction(s);
- if (tf && tf->parameterList.parameters)
- {
- for (size_t k = 0; k < tf->parameterList.parameters->length; k++)
- {
- Parameter *fparam = (*tf->parameterList.parameters)[k];
- if (fparam->ident && cmp(fparam->ident->toChars(), p, len) == 0)
- {
- return fparam;
- }
- }
- }
- return NULL;
-}
-
-static Dsymbol *getEponymousMember(TemplateDeclaration *td)
-{
- if (!td->onemember)
- return NULL;
-
- if (AggregateDeclaration *ad = td->onemember->isAggregateDeclaration())
- return ad;
- if (FuncDeclaration *fd = td->onemember->isFuncDeclaration())
- return fd;
- if (td->onemember->isEnumMember())
- return NULL; // Keep backward compatibility. See compilable/ddoc9.d
- if (VarDeclaration *vd = td->onemember->isVarDeclaration())
- return td->constraint ? NULL : vd;
-
- return NULL;
-}
-
-/****************************************************
- */
-static Parameter *isEponymousFunctionParameter(Dsymbols *a, const utf8_t *p, size_t len)
-{
- for (size_t i = 0; i < a->length; i++)
- {
- TemplateDeclaration *td = (*a)[i]->isTemplateDeclaration();
- if (td && td->onemember)
- {
- /* Case 1: we refer to a template declaration inside the template
-
- /// ...ddoc...
- template case1(T) {
- void case1(R)() {}
- }
- */
- td = td->onemember->isTemplateDeclaration();
- }
- if (!td)
- {
- /* Case 2: we're an alias to a template declaration
-
- /// ...ddoc...
- alias case2 = case1!int;
- */
- AliasDeclaration *ad = (*a)[i]->isAliasDeclaration();
- if (ad && ad->aliassym)
- {
- td = ad->aliassym->isTemplateDeclaration();
- }
- }
- while (td)
- {
- Dsymbol *sym = getEponymousMember(td);
- if (sym)
- {
- Parameter *fparam = isFunctionParameter(sym, p, len);
- if (fparam)
- {
- return fparam;
- }
- }
- td = td->overnext;
- }
- }
- return NULL;
-}
-
-static TemplateDeclaration *getEponymousParent(Dsymbol *s)
-{
- if (!s->parent)
- return NULL;
- TemplateDeclaration *td = s->parent->isTemplateDeclaration();
- return (td && getEponymousMember(td)) ? td : NULL;
-}
-
-static const char ddoc_default[] = "\
-DDOC = <html><head>\n\
- <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
- <title>$(TITLE)</title>\n\
- </head><body>\n\
- <h1>$(TITLE)</h1>\n\
- $(BODY)\n\
- <hr>$(SMALL Page generated by $(LINK2 http://dlang.org/ddoc.html, Ddoc). $(COPYRIGHT))\n\
- </body></html>\n\
-\n\
-B = <b>$0</b>\n\
-I = <i>$0</i>\n\
-U = <u>$0</u>\n\
-P = <p>$0</p>\n\
-DL = <dl>$0</dl>\n\
-DT = <dt>$0</dt>\n\
-DD = <dd>$0</dd>\n\
-TABLE = <table>$0</table>\n\
-TR = <tr>$0</tr>\n\
-TH = <th>$0</th>\n\
-TD = <td>$0</td>\n\
-OL = <ol>$0</ol>\n\
-UL = <ul>$0</ul>\n\
-LI = <li>$0</li>\n\
-BIG = <big>$0</big>\n\
-SMALL = <small>$0</small>\n\
-BR = <br>\n\
-LINK = <a href=\"$0\">$0</a>\n\
-LINK2 = <a href=\"$1\">$+</a>\n\
-LPAREN= (\n\
-RPAREN= )\n\
-BACKTICK= `\n\
-DOLLAR= $\n\
-DEPRECATED= $0\n\
-\n\
-RED = <font color=red>$0</font>\n\
-BLUE = <font color=blue>$0</font>\n\
-GREEN = <font color=green>$0</font>\n\
-YELLOW =<font color=yellow>$0</font>\n\
-BLACK = <font color=black>$0</font>\n\
-WHITE = <font color=white>$0</font>\n\
-\n\
-D_CODE = <pre class=\"d_code\">$0</pre>\n\
-DDOC_BACKQUOTED = $(D_INLINECODE $0)\n\
-D_INLINECODE = <pre style=\"display:inline;\" class=\"d_inline_code\">$0</pre>\n\
-D_COMMENT = $(GREEN $0)\n\
-D_STRING = $(RED $0)\n\
-D_KEYWORD = $(BLUE $0)\n\
-D_PSYMBOL = $(U $0)\n\
-D_PARAM = $(I $0)\n\
-\n\
-DDOC_COMMENT = <!-- $0 -->\n\
-DDOC_DECL = $(DT $(BIG $0))\n\
-DDOC_DECL_DD = $(DD $0)\n\
-DDOC_DITTO = $(BR)$0\n\
-DDOC_SECTIONS = $0\n\
-DDOC_SUMMARY = $0$(BR)$(BR)\n\
-DDOC_DESCRIPTION = $0$(BR)$(BR)\n\
-DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\
-DDOC_SECTION_H = $(B $0)$(BR)\n\
-DDOC_SECTION = $0$(BR)$(BR)\n\
-DDOC_MEMBERS = $(DL $0)\n\
-DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\
-DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\
-DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\
-DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\
-DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\
-DDOC_ENUM_BASETYPE = $0\n\
-DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\
-DDOC_PARAM_ROW = $(TR $0)\n\
-DDOC_PARAM_ID = $(TD $0)\n\
-DDOC_PARAM_DESC = $(TD $0)\n\
-DDOC_BLANKLINE = $(BR)$(BR)\n\
-\n\
-DDOC_ANCHOR = <a name=\"$1\"></a>\n\
-DDOC_PSYMBOL = $(U $0)\n\
-DDOC_PSUPER_SYMBOL = $(U $0)\n\
-DDOC_KEYWORD = $(B $0)\n\
-DDOC_PARAM = $(I $0)\n\
-\n\
-ESCAPES = /</&lt;/\n\
- />/&gt;/\n\
- /&/&amp;/\n\
-";
-
-static const char ddoc_decl_s[] = "$(DDOC_DECL ";
-static const char ddoc_decl_e[] = ")\n";
-
-static const char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD ";
-static const char ddoc_decl_dd_e[] = ")\n";
-
-
-/****************************************************
- */
-
-void gendocfile(Module *m)
-{
- static OutBuffer mbuf;
- static int mbuf_done;
-
- OutBuffer buf;
-
- //printf("Module::gendocfile()\n");
-
- if (!mbuf_done) // if not already read the ddoc files
- {
- mbuf_done = 1;
-
- // Use our internal default
- mbuf.write(ddoc_default, strlen(ddoc_default));
-
- // Override with DDOCFILE specified in the sc.ini file
- char *p = getenv("DDOCFILE");
- if (p)
- global.params.ddocfiles.shift(p);
-
- // Override with the ddoc macro files from the command line
- for (size_t i = 0; i < global.params.ddocfiles.length; i++)
- {
- FileName f(global.params.ddocfiles[i]);
- File file(&f);
- readFile(m->loc, &file);
- // BUG: convert file contents to UTF-8 before use
-
- //printf("file: '%.*s'\n", file.len, file.buffer);
- mbuf.write(file.buffer, file.len);
- }
- }
- DocComment::parseMacros(&m->escapetable, &m->macrotable, (utf8_t *)mbuf.slice().ptr, mbuf.length());
-
- Scope *sc = Scope::createGlobal(m); // create root scope
-
- DocComment *dc = DocComment::parse(m, m->comment);
- dc->pmacrotable = &m->macrotable;
- dc->pescapetable = &m->escapetable;
- sc->lastdc = dc;
-
- // Generate predefined macros
-
- // Set the title to be the name of the module
- {
- const char *p = m->toPrettyChars();
- Macro::define(&m->macrotable, (const utf8_t *)"TITLE", 5, (const utf8_t *)p, strlen(p));
- }
-
- // Set time macros
- {
- time_t t;
- time(&t);
- char *p = ctime(&t);
- p = mem.xstrdup(p);
- Macro::define(&m->macrotable, (const utf8_t *)"DATETIME", 8, (const utf8_t *)p, strlen(p));
- Macro::define(&m->macrotable, (const utf8_t *)"YEAR", 4, (const utf8_t *)p + 20, 4);
- }
-
- const char *srcfilename = m->srcfile->toChars();
- Macro::define(&m->macrotable, (const utf8_t *)"SRCFILENAME", 11, (const utf8_t *)srcfilename, strlen(srcfilename));
-
- const char *docfilename = m->docfile->toChars();
- Macro::define(&m->macrotable, (const utf8_t *)"DOCFILENAME", 11, (const utf8_t *)docfilename, strlen(docfilename));
-
- if (dc->copyright)
- {
- dc->copyright->nooutput = 1;
- Macro::define(&m->macrotable, (const utf8_t *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen);
- }
-
- buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", m->srcfile->toChars());
- if (m->isDocFile)
- {
- Loc loc = m->md ? m->md->loc : m->loc;
- size_t commentlen = strlen((const char *)m->comment);
- Dsymbols a;
- // Bugzilla 9764: Don't push m in a, to prevent emphasize ddoc file name.
- if (dc->macros)
- {
- commentlen = dc->macros->name - m->comment;
- dc->macros->write(loc, dc, sc, &a, &buf);
- }
- buf.write(m->comment, commentlen);
- highlightText(sc, &a, &buf, 0);
- }
- else
- {
- Dsymbols a;
- a.push(m);
- dc->writeSections(sc, &a, &buf);
- emitMemberComments(m, &buf, sc);
- }
-
- //printf("BODY= '%.*s'\n", buf.length(), buf.slice().ptr);
- Macro::define(&m->macrotable, (const utf8_t *)"BODY", 4, (const utf8_t *)buf.slice().ptr, buf.length());
-
- OutBuffer buf2;
- buf2.writestring("$(DDOC)\n");
- size_t end = buf2.length();
- m->macrotable->expand(&buf2, 0, &end, NULL, 0);
-
- /* Remove all the escape sequences from buf2,
- * and make CR-LF the newline.
- */
- {
- buf.setsize(0);
- buf.reserve(buf2.length());
- utf8_t *p = (utf8_t *)buf2.slice().ptr;
- for (size_t j = 0; j < buf2.length(); j++)
- {
- utf8_t c = p[j];
- if (c == 0xFF && j + 1 < buf2.length())
- {
- j++;
- continue;
- }
- if (c == '\n')
- buf.writeByte('\r');
- else if (c == '\r')
- {
- buf.writestring("\r\n");
- if (j + 1 < buf2.length() && p[j + 1] == '\n')
- {
- j++;
- }
- continue;
- }
- buf.writeByte(c);
- }
- }
-
- // Transfer image to file
- assert(m->docfile);
- m->docfile->setbuffer(buf.slice().ptr, buf.length());
- m->docfile->ref = 1;
- ensurePathToNameExists(Loc(), m->docfile->toChars());
- writeFile(m->loc, m->docfile);
-}
-
-/****************************************************
- * Having unmatched parentheses can hose the output of Ddoc,
- * as the macros depend on properly nested parentheses.
- * This function replaces all ( with $(LPAREN) and ) with $(RPAREN)
- * to preserve text literally. This also means macros in the
- * text won't be expanded.
- */
-void escapeDdocString(OutBuffer *buf, size_t start)
-{
- for (size_t u = start; u < buf->length(); u++)
- {
- utf8_t c = buf->slice().ptr[u];
- switch(c)
- {
- case '$':
- buf->remove(u, 1);
- buf->insert(u, (const char *)"$(DOLLAR)", 9);
- u += 8;
- break;
-
- case '(':
- buf->remove(u, 1); //remove the (
- buf->insert(u, (const char *)"$(LPAREN)", 9); //insert this instead
- u += 8; //skip over newly inserted macro
- break;
-
- case ')':
- buf->remove(u, 1); //remove the )
- buf->insert(u, (const char *)"$(RPAREN)", 9); //insert this instead
- u += 8; //skip over newly inserted macro
- break;
- }
- }
-}
-
-/****************************************************
- * Having unmatched parentheses can hose the output of Ddoc,
- * as the macros depend on properly nested parentheses.
-
- * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN).
- */
-void escapeStrayParenthesis(Loc loc, OutBuffer *buf, size_t start)
-{
- unsigned par_open = 0;
-
- for (size_t u = start; u < buf->length(); u++)
- {
- utf8_t c = buf->slice().ptr[u];
- switch(c)
- {
- case '(':
- par_open++;
- break;
-
- case ')':
- if (par_open == 0)
- {
- //stray ')'
- warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output."
- " Use $(RPAREN) instead for unpaired right parentheses.");
- buf->remove(u, 1); //remove the )
- buf->insert(u, (const char *)"$(RPAREN)", 9); //insert this instead
- u += 8; //skip over newly inserted macro
- }
- else
- par_open--;
- break;
- }
- }
-
- if (par_open) // if any unmatched lparens
- {
- par_open = 0;
- for (size_t u = buf->length(); u > start;)
- {
- u--;
- utf8_t c = buf->slice().ptr[u];
- switch(c)
- {
- case ')':
- par_open++;
- break;
-
- case '(':
- if (par_open == 0)
- {
- //stray '('
- warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output."
- " Use $(LPAREN) instead for unpaired left parentheses.");
- buf->remove(u, 1); //remove the (
- buf->insert(u, (const char *)"$(LPAREN)", 9); //insert this instead
- }
- else
- par_open--;
- break;
- }
- }
- }
-}
-
-// Basically, this is to skip over things like private{} blocks in a struct or
-// class definition that don't add any components to the qualified name.
-static Scope *skipNonQualScopes(Scope *sc)
-{
- while (sc && !sc->scopesym)
- sc = sc->enclosing;
- return sc;
-}
-
-static bool emitAnchorName(OutBuffer *buf, Dsymbol *s, Scope *sc)
-{
- if (!s || s->isPackage() || s->isModule())
- return false;
-
- // Add parent names first
- bool dot = false;
- if (s->parent)
- dot = emitAnchorName(buf, s->parent, sc);
- else if (sc)
- dot = emitAnchorName(buf, sc->scopesym, skipNonQualScopes(sc->enclosing));
-
- // Eponymous template members can share the parent anchor name
- if (getEponymousParent(s))
- return dot;
- if (dot)
- buf->writeByte('.');
-
- // Use "this" not "__ctor"
- TemplateDeclaration *td;
- if (s->isCtorDeclaration() || ((td = s->isTemplateDeclaration()) != NULL &&
- td->onemember && td->onemember->isCtorDeclaration()))
- {
- buf->writestring("this");
- }
- else
- {
- /* We just want the identifier, not overloads like TemplateDeclaration::toChars.
- * We don't want the template parameter list and constraints. */
- buf->writestring(s->Dsymbol::toChars());
- }
- return true;
-}
-
-static void emitAnchor(OutBuffer *buf, Dsymbol *s, Scope *sc)
-{
- Identifier *ident;
- {
- OutBuffer anc;
- emitAnchorName(&anc, s, skipNonQualScopes(sc));
- ident = Identifier::idPool(anc.peekChars());
- }
- size_t *count = (size_t*)dmd_aaGet(&sc->anchorCounts, (void *)ident);
- TemplateDeclaration *td = getEponymousParent(s);
- // don't write an anchor for matching consecutive ditto symbols
- if (*count > 0 && sc->prevAnchor == ident &&
- sc->lastdc && (isDitto(s->comment) || (td && isDitto(td->comment))))
- return;
-
- (*count)++;
- // cache anchor name
- sc->prevAnchor = ident;
-
- buf->writestring("$(DDOC_ANCHOR ");
- buf->writestring(ident->toChars());
- // only append count once there's a duplicate
- if (*count != 1)
- buf->printf(".%u", *count);
- buf->writeByte(')');
-}
-
-/******************************* emitComment **********************************/
-
-/** Get leading indentation from 'src' which represents lines of code. */
-static size_t getCodeIndent(const char *src)
-{
- while (src && (*src == '\r' || *src == '\n'))
- ++src; // skip until we find the first non-empty line
-
- size_t codeIndent = 0;
- while (src && (*src == ' ' || *src == '\t'))
- {
- codeIndent++;
- src++;
- }
- return codeIndent;
-}
-
-/** Recursively expand template mixin member docs into the scope. */
-static void expandTemplateMixinComments(TemplateMixin *tm, OutBuffer *buf, Scope *sc)
-{
- if (!tm->semanticRun)
- dsymbolSemantic(tm, sc);
- TemplateDeclaration *td = (tm && tm->tempdecl) ?
- tm->tempdecl->isTemplateDeclaration() : NULL;
- if (td && td->members)
- {
- for (size_t i = 0; i < td->members->length; i++)
- {
- Dsymbol *sm = (*td->members)[i];
- TemplateMixin *tmc = sm->isTemplateMixin();
- if (tmc && tmc->comment)
- expandTemplateMixinComments(tmc, buf, sc);
- else
- emitComment(sm, buf, sc);
- }
- }
-}
-
-void emitMemberComments(ScopeDsymbol *sds, OutBuffer *buf, Scope *sc)
-{
- if (!sds->members)
- return;
-
- //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
-
- const char *m = "$(DDOC_MEMBERS ";
- if (sds->isTemplateDeclaration())
- m = "$(DDOC_TEMPLATE_MEMBERS ";
- else if (sds->isClassDeclaration())
- m = "$(DDOC_CLASS_MEMBERS ";
- else if (sds->isStructDeclaration())
- m = "$(DDOC_STRUCT_MEMBERS ";
- else if (sds->isEnumDeclaration())
- m = "$(DDOC_ENUM_MEMBERS ";
- else if (sds->isModule())
- m = "$(DDOC_MODULE_MEMBERS ";
-
- size_t offset1 = buf->length(); // save starting offset
- buf->writestring(m);
- size_t offset2 = buf->length(); // to see if we write anything
-
- sc = sc->push(sds);
-
- for (size_t i = 0; i < sds->members->length; i++)
- {
- Dsymbol *s = (*sds->members)[i];
- //printf("\ts = '%s'\n", s->toChars());
-
- // only expand if parent is a non-template (semantic won't work)
- if (s->comment && s->isTemplateMixin() && s->parent && !s->parent->isTemplateDeclaration())
- expandTemplateMixinComments((TemplateMixin *)s, buf, sc);
-
- emitComment(s, buf, sc);
- }
- emitComment(NULL, buf, sc);
-
- sc->pop();
-
- if (buf->length() == offset2)
- {
- /* Didn't write out any members, so back out last write
- */
- buf->setsize(offset1);
- }
- else
- buf->writestring(")\n");
-}
-
-void emitProtection(OutBuffer *buf, Prot prot)
-{
- if (prot.kind != Prot::undefined && prot.kind != Prot::public_)
- {
- protectionToBuffer(buf, prot);
- buf->writeByte(' ');
- }
-}
-
-void emitComment(Dsymbol *s, OutBuffer *buf, Scope *sc)
-{
- class EmitComment : public Visitor
- {
- public:
- OutBuffer *buf;
- Scope *sc;
-
- EmitComment(OutBuffer *buf, Scope *sc)
- : buf(buf), sc(sc)
- {
- }
-
- void visit(Dsymbol *) {}
- void visit(InvariantDeclaration *) {}
- void visit(UnitTestDeclaration *) {}
- void visit(PostBlitDeclaration *) {}
- void visit(DtorDeclaration *) {}
- void visit(StaticCtorDeclaration *) {}
- void visit(StaticDtorDeclaration *) {}
- void visit(TypeInfoDeclaration *) {}
-
- void emit(Scope *sc, Dsymbol *s, const utf8_t *com)
- {
- if (s && sc->lastdc && isDitto(com))
- {
- sc->lastdc->a.push(s);
- return;
- }
-
- // Put previous doc comment if exists
- if (DocComment *dc = sc->lastdc)
- {
- // Put the declaration signatures as the document 'title'
- buf->writestring(ddoc_decl_s);
- for (size_t i = 0; i < dc->a.length; i++)
- {
- Dsymbol *sx = dc->a[i];
-
- if (i == 0)
- {
- size_t o = buf->length();
- toDocBuffer(sx, buf, sc);
- highlightCode(sc, sx, buf, o);
- continue;
- }
-
- buf->writestring("$(DDOC_DITTO ");
- {
- size_t o = buf->length();
- toDocBuffer(sx, buf, sc);
- highlightCode(sc, sx, buf, o);
- }
- buf->writeByte(')');
- }
- buf->writestring(ddoc_decl_e);
-
- // Put the ddoc comment as the document 'description'
- buf->writestring(ddoc_decl_dd_s);
- {
- dc->writeSections(sc, &dc->a, buf);
- if (ScopeDsymbol *sds = dc->a[0]->isScopeDsymbol())
- emitMemberComments(sds, buf, sc);
- }
- buf->writestring(ddoc_decl_dd_e);
- //printf("buf.2 = [[%.*s]]\n", buf->length() - o0, buf->slice().ptr + o0);
- }
-
- if (s)
- {
- DocComment *dc = DocComment::parse(s, com);
- dc->pmacrotable = &sc->_module->macrotable;
- sc->lastdc = dc;
- }
- }
-
- void visit(Declaration *d)
- {
- //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", d, d->toChars(), d->comment);
- //printf("type = %p\n", d->type);
- const utf8_t *com = d->comment;
- if (TemplateDeclaration *td = getEponymousParent(d))
- {
- if (isDitto(td->comment))
- com = td->comment;
- else
- com = Lexer::combineComments(td->comment, com);
- }
- else
- {
- if (!d->ident)
- return;
- if (!d->type && !d->isCtorDeclaration() && !d->isAliasDeclaration())
- return;
- if (d->protection.kind == Prot::private_ || sc->protection.kind == Prot::private_)
- return;
- }
- if (!com)
- return;
-
- emit(sc, d, com);
- }
-
- void visit(AggregateDeclaration *ad)
- {
- //printf("AggregateDeclaration::emitComment() '%s'\n", ad->toChars());
- const utf8_t *com = ad->comment;
- if (TemplateDeclaration *td = getEponymousParent(ad))
- {
- if (isDitto(td->comment))
- com = td->comment;
- else
- com = Lexer::combineComments(td->comment, com);
- }
- else
- {
- if (ad->prot().kind == Prot::private_ || sc->protection.kind == Prot::private_)
- return;
- if (!ad->comment)
- return;
- }
- if (!com)
- return;
-
- emit(sc, ad, com);
- }
-
- void visit(TemplateDeclaration *td)
- {
- //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", td->toChars(), td->kind());
- if (td->prot().kind == Prot::private_ || sc->protection.kind == Prot::private_)
- return;
- if (!td->comment)
- return;
-
- if (Dsymbol *ss = getEponymousMember(td))
- {
- ss->accept(this);
- return;
- }
- emit(sc, td, td->comment);
- }
-
- void visit(EnumDeclaration *ed)
- {
- if (ed->prot().kind == Prot::private_ || sc->protection.kind == Prot::private_)
- return;
- if (ed->isAnonymous() && ed->members)
- {
- for (size_t i = 0; i < ed->members->length; i++)
- {
- Dsymbol *s = (*ed->members)[i];
- emitComment(s, buf, sc);
- }
- return;
- }
- if (!ed->comment)
- return;
- if (ed->isAnonymous())
- return;
-
- emit(sc, ed, ed->comment);
- }
-
- void visit(EnumMember *em)
- {
- //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", em, em->toChars(), em->comment);
- if (em->prot().kind == Prot::private_ || sc->protection.kind == Prot::private_)
- return;
- if (!em->comment)
- return;
-
- emit(sc, em, em->comment);
- }
-
- void visit(AttribDeclaration *ad)
- {
- //printf("AttribDeclaration::emitComment(sc = %p)\n", sc);
-
- /* A general problem with this, illustrated by BUGZILLA 2516,
- * is that attributes are not transmitted through to the underlying
- * member declarations for template bodies, because semantic analysis
- * is not done for template declaration bodies
- * (only template instantiations).
- * Hence, Ddoc omits attributes from template members.
- */
-
- Dsymbols *d = ad->include(NULL);
-
- if (d)
- {
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- //printf("AttribDeclaration::emitComment %s\n", s->toChars());
- emitComment(s, buf, sc);
- }
- }
- }
-
- void visit(ProtDeclaration *pd)
- {
- if (pd->decl)
- {
- Scope *scx = sc;
- sc = sc->copy();
- sc->protection = pd->protection;
- visit((AttribDeclaration *)pd);
- scx->lastdc = sc->lastdc;
- sc = sc->pop();
- }
- }
-
- void visit(ConditionalDeclaration *cd)
- {
- //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc);
- if (cd->condition->inc)
- {
- visit((AttribDeclaration *)cd);
- return;
- }
-
- /* If generating doc comment, be careful because if we're inside
- * a template, then include(NULL) will fail.
- */
- Dsymbols *d = cd->decl ? cd->decl : cd->elsedecl;
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- emitComment(s, buf, sc);
- }
- }
- };
-
- EmitComment v(buf, sc);
-
- if (!s)
- v.emit(sc, NULL, NULL);
- else
- s->accept(&v);
-}
-
-/******************************* toDocBuffer **********************************/
-
-void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc)
-{
- class ToDocBuffer : public Visitor
- {
- public:
- OutBuffer *buf;
- Scope *sc;
-
- ToDocBuffer(OutBuffer *buf, Scope *sc)
- : buf(buf), sc(sc)
- {
- }
-
- void visit(Dsymbol *s)
- {
- //printf("Dsymbol::toDocbuffer() %s\n", s->toChars());
- HdrGenState hgs;
- hgs.ddoc = true;
- ::toCBuffer(s, buf, &hgs);
- }
-
- void prefix(Dsymbol *s)
- {
- if (s->isDeprecated())
- buf->writestring("deprecated ");
-
- if (Declaration *d = s->isDeclaration())
- {
- emitProtection(buf, d->protection);
-
- if (d->isStatic())
- buf->writestring("static ");
- else if (d->isFinal())
- buf->writestring("final ");
- else if (d->isAbstract())
- buf->writestring("abstract ");
-
- if (!d->isFuncDeclaration()) // functionToBufferFull handles this
- {
- if (d->isConst())
- buf->writestring("const ");
- if (d->isImmutable())
- buf->writestring("immutable ");
- if (d->isSynchronized())
- buf->writestring("synchronized ");
-
- if (d->storage_class & STCmanifest)
- buf->writestring("enum ");
- }
- }
- }
-
- void visit(Declaration *d)
- {
- if (!d->ident)
- return;
-
- TemplateDeclaration *td = getEponymousParent(d);
- //printf("Declaration::toDocbuffer() %s, originalType = %s, td = %s\n", d->toChars(), d->originalType ? d->originalType->toChars() : "--", td ? td->toChars() : "--");
-
- HdrGenState hgs;
- hgs.ddoc = true;
-
- if (d->isDeprecated())
- buf->writestring("$(DEPRECATED ");
-
- prefix(d);
-
- if (d->type)
- {
- Type *origType = d->originalType ? d->originalType : d->type;
- if (origType->ty == Tfunction)
- {
- functionToBufferFull((TypeFunction *)origType, buf, d->ident, &hgs, td);
- }
- else
- ::toCBuffer(origType, buf, d->ident, &hgs);
- }
- else
- buf->writestring(d->ident->toChars());
-
- if (d->isVarDeclaration() && td)
- {
- buf->writeByte('(');
- if (td->origParameters && td->origParameters->length)
- {
- for (size_t i = 0; i < td->origParameters->length; i++)
- {
- if (i)
- buf->writestring(", ");
- toCBuffer((*td->origParameters)[i], buf, &hgs);
- }
- }
- buf->writeByte(')');
- }
-
- // emit constraints if declaration is a templated declaration
- if (td && td->constraint)
- {
- buf->writestring(" if (");
- ::toCBuffer(td->constraint, buf, &hgs);
- buf->writeByte(')');
- }
-
- if (d->isDeprecated())
- buf->writestring(")");
-
- buf->writestring(";\n");
- }
-
- void visit(AliasDeclaration *ad)
- {
- //printf("AliasDeclaration::toDocbuffer() %s\n", ad->toChars());
- if (!ad->ident)
- return;
-
- if (ad->isDeprecated())
- buf->writestring("deprecated ");
-
- emitProtection(buf, ad->protection);
- buf->printf("alias %s = ", ad->toChars());
-
- if (Dsymbol *sa = ad->aliassym) // ident alias
- {
- prettyPrintDsymbol(sa, ad->parent);
- }
- else if (Type *type = ad->getType()) // type alias
- {
- if (type->ty == Tclass || type->ty == Tstruct || type->ty == Tenum)
- {
- if (Dsymbol *s = type->toDsymbol(NULL)) // elaborate type
- prettyPrintDsymbol(s, ad->parent);
- else
- buf->writestring(type->toChars());
- }
- else
- {
- // simple type
- buf->writestring(type->toChars());
- }
- }
-
- buf->writestring(";\n");
- }
-
- void parentToBuffer(Dsymbol *s)
- {
- if (s && !s->isPackage() && !s->isModule())
- {
- parentToBuffer(s->parent);
- buf->writestring(s->toChars());
- buf->writestring(".");
- }
- }
-
- static bool inSameModule(Dsymbol *s, Dsymbol *p)
- {
- for ( ; s ; s = s->parent)
- {
- if (s->isModule())
- break;
- }
-
- for ( ; p ; p = p->parent)
- {
- if (p->isModule())
- break;
- }
-
- return s == p;
- }
-
- void prettyPrintDsymbol(Dsymbol *s, Dsymbol *parent)
- {
- if (s->parent && (s->parent == parent)) // in current scope -> naked name
- {
- buf->writestring(s->toChars());
- }
- else if (!inSameModule(s, parent)) // in another module -> full name
- {
- buf->writestring(s->toPrettyChars());
- }
- else // nested in a type in this module -> full name w/o module name
- {
- // if alias is nested in a user-type use module-scope lookup
- if (!parent->isModule() && !parent->isPackage())
- buf->writestring(".");
-
- parentToBuffer(s->parent);
- buf->writestring(s->toChars());
- }
- }
-
- void visit(AggregateDeclaration *ad)
- {
- if (!ad->ident)
- return;
-
- buf->printf("%s %s", ad->kind(), ad->toChars());
- buf->writestring(";\n");
- }
-
- void visit(StructDeclaration *sd)
- {
- //printf("StructDeclaration::toDocbuffer() %s\n", sd->toChars());
- if (!sd->ident)
- return;
-
- if (TemplateDeclaration *td = getEponymousParent(sd))
- {
- toDocBuffer(td, buf, sc);
- }
- else
- {
- buf->printf("%s %s", sd->kind(), sd->toChars());
- }
- buf->writestring(";\n");
- }
-
- void visit(ClassDeclaration *cd)
- {
- //printf("ClassDeclaration::toDocbuffer() %s\n", cd->toChars());
- if (!cd->ident)
- return;
-
- if (TemplateDeclaration *td = getEponymousParent(cd))
- {
- toDocBuffer(td, buf, sc);
- }
- else
- {
- if (!cd->isInterfaceDeclaration() && cd->isAbstract())
- buf->writestring("abstract ");
- buf->printf("%s %s", cd->kind(), cd->toChars());
- }
- int any = 0;
- for (size_t i = 0; i < cd->baseclasses->length; i++)
- {
- BaseClass *bc = (*cd->baseclasses)[i];
-
- if (bc->sym && bc->sym->ident == Id::Object)
- continue;
-
- if (any)
- buf->writestring(", ");
- else
- {
- buf->writestring(": ");
- any = 1;
- }
- emitProtection(buf, Prot(Prot::public_));
- if (bc->sym)
- {
- buf->printf("$(DDOC_PSUPER_SYMBOL %s)", bc->sym->toPrettyChars());
- }
- else
- {
- HdrGenState hgs;
- ::toCBuffer(bc->type, buf, NULL, &hgs);
- }
- }
- buf->writestring(";\n");
- }
-
- void visit(EnumDeclaration *ed)
- {
- if (!ed->ident)
- return;
-
- buf->printf("%s %s", ed->kind(), ed->toChars());
- if (ed->memtype)
- {
- buf->writestring(": $(DDOC_ENUM_BASETYPE ");
- HdrGenState hgs;
- ::toCBuffer(ed->memtype, buf, NULL, &hgs);
- buf->writestring(")");
- }
- buf->writestring(";\n");
- }
-
- void visit(EnumMember *em)
- {
- if (!em->ident)
- return;
-
- buf->writestring(em->toChars());
- }
- };
-
- ToDocBuffer v(buf, sc);
- s->accept(&v);
-}
-
-/********************************* DocComment *********************************/
-
-DocComment *DocComment::parse(Dsymbol *s, const utf8_t *comment)
-{
- //printf("parse(%s): '%s'\n", s->toChars(), comment);
- DocComment *dc = new DocComment();
- dc->a.push(s);
- if (!comment)
- return dc;
-
- dc->parseSections(comment);
-
- for (size_t i = 0; i < dc->sections.length; i++)
- {
- Section *sec = dc->sections[i];
-
- if (icmp("copyright", sec->name, sec->namelen) == 0)
- {
- dc->copyright = sec;
- }
- if (icmp("macros", sec->name, sec->namelen) == 0)
- {
- dc->macros = sec;
- }
- }
-
- return dc;
-}
-
-/*****************************************
- * Parse next paragraph out of *pcomment.
- * Update *pcomment to point past paragraph.
- * Returns NULL if no more paragraphs.
- * If paragraph ends in 'identifier:',
- * then (*pcomment)[0 .. idlen] is the identifier.
- */
-
-void DocComment::parseSections(const utf8_t *comment)
-{
- const utf8_t *p;
- const utf8_t *pstart;
- const utf8_t *pend;
- const utf8_t *idstart = NULL; // dead-store to prevent spurious warning
- size_t idlen;
-
- const utf8_t *name = NULL;
- size_t namelen = 0;
-
- //printf("parseSections('%s')\n", comment);
- p = comment;
- while (*p)
- {
- const utf8_t *pstart0 = p;
- p = skipwhitespace(p);
- pstart = p;
- pend = p;
-
- /* Find end of section, which is ended by one of:
- * 'identifier:' (but not inside a code section)
- * '\0'
- */
- idlen = 0;
- int inCode = 0;
- while (1)
- {
- // Check for start/end of a code section
- if (*p == '-')
- {
- if (!inCode)
- {
- // restore leading indentation
- while (pstart0 < pstart && isIndentWS(pstart-1)) --pstart;
- }
-
- int numdash = 0;
- while (*p == '-')
- {
- ++numdash;
- p++;
- }
- // BUG: handle UTF PS and LS too
- if ((!*p || *p == '\r' || *p == '\n') && numdash >= 3)
- inCode ^= 1;
- pend = p;
- }
-
- if (!inCode && isIdStart(p))
- {
- const utf8_t *q = p + utfStride(p);
- while (isIdTail(q))
- q += utfStride(q);
- // Detected tag ends it
- if (*q == ':' && isupper(*p)
- && (isspace(q[1]) || q[1] == 0))
- {
- idlen = q - p;
- idstart = p;
- for (pend = p; pend > pstart; pend--)
- {
- if (pend[-1] == '\n')
- break;
- }
- p = q + 1;
- break;
- }
- }
- while (1)
- {
- if (!*p)
- goto L1;
- if (*p == '\n')
- {
- p++;
- if (*p == '\n' && !summary && !namelen && !inCode)
- {
- pend = p;
- p++;
- goto L1;
- }
- break;
- }
- p++;
- pend = p;
- }
- p = skipwhitespace(p);
- }
- L1:
-
- if (namelen || pstart < pend)
- {
- Section *s;
- if (icmp("Params", name, namelen) == 0)
- s = new ParamSection();
- else if (icmp("Macros", name, namelen) == 0)
- s = new MacroSection();
- else
- s = new Section();
- s->name = name;
- s->namelen = namelen;
- s->body = pstart;
- s->bodylen = pend - pstart;
- s->nooutput = 0;
-
- //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body);
-
- sections.push(s);
-
- if (!summary && !namelen)
- summary = s;
- }
-
- if (idlen)
- {
- name = idstart;
- namelen = idlen;
- }
- else
- {
- name = NULL;
- namelen = 0;
- if (!*p)
- break;
- }
- }
-}
-
-void DocComment::writeSections(Scope *sc, Dsymbols *a, OutBuffer *buf)
-{
- assert(a->length);
-
- //printf("DocComment::writeSections()\n");
- Loc loc = (*a)[0]->loc;
- if (Module *m = (*a)[0]->isModule())
- {
- if (m->md)
- loc = m->md->loc;
- }
-
- size_t offset1 = buf->length();
- buf->writestring("$(DDOC_SECTIONS ");
- size_t offset2 = buf->length();
-
- for (size_t i = 0; i < sections.length; i++)
- {
- Section *sec = sections[i];
- if (sec->nooutput)
- continue;
-
- //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body);
- if (!sec->namelen && i == 0)
- {
- buf->writestring("$(DDOC_SUMMARY ");
- size_t o = buf->length();
- buf->write(sec->body, sec->bodylen);
- escapeStrayParenthesis(loc, buf, o);
- highlightText(sc, a, buf, o);
- buf->writestring(")\n");
- }
- else
- sec->write(loc, this, sc, a, buf);
- }
-
- for (size_t i = 0; i < a->length; i++)
- {
- Dsymbol *s = (*a)[i];
- if (Dsymbol *td = getEponymousParent(s))
- s = td;
-
- for (UnitTestDeclaration *utd = s->ddocUnittest; utd; utd = utd->ddocUnittest)
- {
- if (utd->protection.kind == Prot::private_ || !utd->comment || !utd->fbody)
- continue;
-
- // Strip whitespaces to avoid showing empty summary
- const utf8_t *c = utd->comment;
- while (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r') ++c;
-
- buf->writestring("$(DDOC_EXAMPLES ");
-
- size_t o = buf->length();
- buf->writestring((const char *)c);
-
- if (utd->codedoc)
- {
- size_t n = getCodeIndent(utd->codedoc);
- while (n--) buf->writeByte(' ');
- buf->writestring("----\n");
- buf->writestring(utd->codedoc);
- buf->writestring("----\n");
- highlightText(sc, a, buf, o);
- }
-
- buf->writestring(")");
- }
- }
-
- if (buf->length() == offset2)
- {
- /* Didn't write out any sections, so back out last write
- */
- buf->setsize(offset1);
- buf->writestring("$(DDOC_BLANKLINE)\n");
- }
- else
- buf->writestring(")\n");
-}
-
-/***************************************************
- */
-
-void Section::write(Loc loc, DocComment *, Scope *sc, Dsymbols *a, OutBuffer *buf)
-{
- assert(a->length);
-
- if (namelen)
- {
- static const char *table[] =
- {
- "AUTHORS", "BUGS", "COPYRIGHT", "DATE",
- "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE",
- "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS",
- "VERSION", NULL
- };
-
- for (size_t i = 0; table[i]; i++)
- {
- if (icmp(table[i], name, namelen) == 0)
- {
- buf->printf("$(DDOC_%s ", table[i]);
- goto L1;
- }
- }
-
- buf->writestring("$(DDOC_SECTION ");
-
- // Replace _ characters with spaces
- buf->writestring("$(DDOC_SECTION_H ");
- size_t o = buf->length();
- for (size_t u = 0; u < namelen; u++)
- {
- utf8_t c = name[u];
- buf->writeByte((c == '_') ? ' ' : c);
- }
- escapeStrayParenthesis(loc, buf, o);
- buf->writestring(":)\n");
- }
- else
- {
- buf->writestring("$(DDOC_DESCRIPTION ");
- }
- L1:
- size_t o = buf->length();
- buf->write(body, bodylen);
- escapeStrayParenthesis(loc, buf, o);
- highlightText(sc, a, buf, o);
- buf->writestring(")\n");
-}
-
-/***************************************************
- */
-
-void ParamSection::write(Loc loc, DocComment *, Scope *sc, Dsymbols *a, OutBuffer *buf)
-{
- assert(a->length);
- Dsymbol *s = (*a)[0]; // test
-
- const utf8_t *p = body;
- size_t len = bodylen;
- const utf8_t *pend = p + len;
-
- const utf8_t *tempstart = NULL;
- size_t templen = 0;
-
- const utf8_t *namestart = NULL;
- size_t namelen = 0; // !=0 if line continuation
-
- const utf8_t *textstart = NULL;
- size_t textlen = 0;
-
- size_t paramcount = 0;
-
- buf->writestring("$(DDOC_PARAMS ");
- while (p < pend)
- {
- // Skip to start of macro
- while (1)
- {
- switch (*p)
- {
- case ' ':
- case '\t':
- p++;
- continue;
-
- case '\n':
- p++;
- goto Lcont;
-
- default:
- if (isIdStart(p) || isCVariadicArg(p, pend - p))
- break;
- if (namelen)
- goto Ltext; // continuation of prev macro
- goto Lskipline;
- }
- break;
- }
- tempstart = p;
-
- while (isIdTail(p))
- p += utfStride(p);
- if (isCVariadicArg(p, pend - p))
- p += 3;
-
- templen = p - tempstart;
-
- while (*p == ' ' || *p == '\t')
- p++;
-
- if (*p != '=')
- {
- if (namelen)
- goto Ltext; // continuation of prev macro
- goto Lskipline;
- }
- p++;
-
- if (namelen)
- {
- // Output existing param
-
- L1:
- //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
- ++paramcount;
- HdrGenState hgs;
- buf->writestring("$(DDOC_PARAM_ROW ");
- {
- buf->writestring("$(DDOC_PARAM_ID ");
- {
- size_t o = buf->length();
- Parameter *fparam = isFunctionParameter(a, namestart, namelen);
- if (!fparam)
- {
- // Comments on a template might refer to function parameters within.
- // Search the parameters of nested eponymous functions (with the same name.)
- fparam = isEponymousFunctionParameter(a, namestart, namelen);
- }
- bool isCVariadic = isCVariadicParameter(a, namestart, namelen);
- if (isCVariadic)
- {
- buf->writestring("...");
- }
- else if (fparam && fparam->type && fparam->ident)
- {
- ::toCBuffer(fparam->type, buf, fparam->ident, &hgs);
- }
- else
- {
- if (isTemplateParameter(a, namestart, namelen))
- {
- // 10236: Don't count template parameters for params check
- --paramcount;
- }
- else if (!fparam)
- {
- warning(s->loc, "Ddoc: function declaration has no parameter '%.*s'", (int)namelen, namestart);
- }
- buf->write(namestart, namelen);
- }
- escapeStrayParenthesis(loc, buf, o);
- highlightCode(sc, a, buf, o);
- }
- buf->writestring(")\n");
-
- buf->writestring("$(DDOC_PARAM_DESC ");
- {
- size_t o = buf->length();
- buf->write(textstart, textlen);
- escapeStrayParenthesis(loc, buf, o);
- highlightText(sc, a, buf, o);
- }
- buf->writestring(")");
- }
- buf->writestring(")\n");
- namelen = 0;
- if (p >= pend)
- break;
- }
-
- namestart = tempstart;
- namelen = templen;
-
- while (*p == ' ' || *p == '\t')
- p++;
- textstart = p;
-
- Ltext:
- while (*p != '\n')
- p++;
- textlen = p - textstart;
- p++;
-
- Lcont:
- continue;
-
- Lskipline:
- // Ignore this line
- while (*p++ != '\n')
- ;
- }
- if (namelen)
- goto L1; // write out last one
- buf->writestring(")\n");
-
- TypeFunction *tf = a->length == 1 ? isTypeFunction(s) : NULL;
- if (tf)
- {
- size_t pcount = (tf->parameterList.parameters ? tf->parameterList.parameters->length : 0) +
- (int)(tf->parameterList.varargs == VARARGvariadic);
- if (pcount != paramcount)
- {
- warning(s->loc, "Ddoc: parameter count mismatch");
- }
- }
-}
-
-/***************************************************
- */
-
-void MacroSection::write(Loc, DocComment *dc, Scope *, Dsymbols *, OutBuffer *)
-{
- //printf("MacroSection::write()\n");
- DocComment::parseMacros(dc->pescapetable, dc->pmacrotable, body, bodylen);
-}
-
-/************************************************
- * Parse macros out of Macros: section.
- * Macros are of the form:
- * name1 = value1
- *
- * name2 = value2
- */
-
-void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, const utf8_t *m, size_t mlen)
-{
- const utf8_t *p = m;
- size_t len = mlen;
- const utf8_t *pend = p + len;
-
- const utf8_t *tempstart = NULL;
- size_t templen = 0;
-
- const utf8_t *namestart = NULL;
- size_t namelen = 0; // !=0 if line continuation
-
- const utf8_t *textstart = NULL;
- size_t textlen = 0;
-
- while (p < pend)
- {
- // Skip to start of macro
- while (1)
- {
- if (p >= pend)
- goto Ldone;
- switch (*p)
- {
- case ' ':
- case '\t':
- p++;
- continue;
-
- case '\r':
- case '\n':
- p++;
- goto Lcont;
-
- default:
- if (isIdStart(p))
- break;
- if (namelen)
- goto Ltext; // continuation of prev macro
- goto Lskipline;
- }
- break;
- }
- tempstart = p;
-
- while (1)
- {
- if (p >= pend)
- goto Ldone;
- if (!isIdTail(p))
- break;
- p += utfStride(p);
- }
- templen = p - tempstart;
-
- while (1)
- {
- if (p >= pend)
- goto Ldone;
- if (!(*p == ' ' || *p == '\t'))
- break;
- p++;
- }
-
- if (*p != '=')
- {
- if (namelen)
- goto Ltext; // continuation of prev macro
- goto Lskipline;
- }
- p++;
- if (p >= pend)
- goto Ldone;
-
- if (namelen)
- {
- // Output existing macro
- L1:
- //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
- if (icmp("ESCAPES", namestart, namelen) == 0)
- parseEscapes(pescapetable, textstart, textlen);
- else
- Macro::define(pmacrotable, namestart, namelen, textstart, textlen);
- namelen = 0;
- if (p >= pend)
- break;
- }
-
- namestart = tempstart;
- namelen = templen;
-
- while (p < pend && (*p == ' ' || *p == '\t'))
- p++;
- textstart = p;
-
- Ltext:
- while (p < pend && *p != '\r' && *p != '\n')
- p++;
- textlen = p - textstart;
-
- p++;
- //printf("p = %p, pend = %p\n", p, pend);
-
- Lcont:
- continue;
-
- Lskipline:
- // Ignore this line
- while (p < pend && *p != '\r' && *p != '\n')
- p++;
- }
-Ldone:
- if (namelen)
- goto L1; // write out last one
-}
-
-/**************************************
- * Parse escapes of the form:
- * /c/string/
- * where c is a single character.
- * Multiple escapes can be separated
- * by whitespace and/or commas.
- */
-
-void DocComment::parseEscapes(Escape **pescapetable, const utf8_t *textstart, size_t textlen)
-{
- Escape *escapetable = *pescapetable;
-
- if (!escapetable)
- {
- escapetable = new Escape;
- memset(escapetable, 0, sizeof(Escape));
- *pescapetable = escapetable;
- }
- //printf("parseEscapes('%.*s') pescapetable = %p\n", textlen, textstart, pescapetable);
- const utf8_t *p = textstart;
- const utf8_t *pend = p + textlen;
-
- while (1)
- {
- while (1)
- {
- if (p + 4 >= pend)
- return;
- if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ','))
- break;
- p++;
- }
- if (p[0] != '/' || p[2] != '/')
- return;
- utf8_t c = p[1];
- p += 3;
- const utf8_t *start = p;
- while (1)
- {
- if (p >= pend)
- return;
- if (*p == '/')
- break;
- p++;
- }
- size_t len = p - start;
- char *s = (char *)memcpy(mem.xmalloc(len + 1), start, len);
- s[len] = 0;
- escapetable->strings[c] = s;
- //printf("\t%c = '%s'\n", c, s);
- p++;
- }
-}
-
-
-/******************************************
- * Compare 0-terminated string with length terminated string.
- * Return < 0, ==0, > 0
- */
-
-int cmp(const char *stringz, const void *s, size_t slen)
-{
- size_t len1 = strlen(stringz);
-
- if (len1 != slen)
- return (int)(len1 - slen);
- return memcmp(stringz, s, slen);
-}
-
-int icmp(const char *stringz, const void *s, size_t slen)
-{
- size_t len1 = strlen(stringz);
-
- if (len1 != slen)
- return (int)(len1 - slen);
- return Port::memicmp(stringz, (const char *)s, slen);
-}
-
-/*****************************************
- * Return true if comment consists entirely of "ditto".
- */
-
-bool isDitto(const utf8_t *comment)
-{
- if (comment)
- {
- const utf8_t *p = skipwhitespace(comment);
-
- if (Port::memicmp((const char *)p, "ditto", 5) == 0 && *skipwhitespace(p + 5) == 0)
- return true;
- }
- return false;
-}
-
-/**********************************************
- * Skip white space.
- */
-
-const utf8_t *skipwhitespace(const utf8_t *p)
-{
- for (; 1; p++)
- {
- switch (*p)
- {
- case ' ':
- case '\t':
- case '\n':
- continue;
- }
- break;
- }
- return p;
-}
-
-
-/************************************************
- * Scan forward to one of:
- * start of identifier
- * beginning of next line
- * end of buf
- */
-
-size_t skiptoident(OutBuffer *buf, size_t i)
-{
- while (i < buf->length())
- {
- dchar_t c;
-
- size_t oi = i;
- if (utf_decodeChar((utf8_t *)buf->slice().ptr, buf->length(), &i, &c))
- {
- /* Ignore UTF errors, but still consume input
- */
- break;
- }
- if (c >= 0x80)
- {
- if (!isUniAlpha(c))
- continue;
- }
- else if (!(isalpha(c) || c == '_' || c == '\n'))
- continue;
- i = oi;
- break;
- }
- return i;
-}
-
-/************************************************
- * Scan forward past end of identifier.
- */
-
-size_t skippastident(OutBuffer *buf, size_t i)
-{
- while (i < buf->length())
- {
- dchar_t c;
-
- size_t oi = i;
- if (utf_decodeChar((utf8_t *)buf->slice().ptr, buf->length(), &i, &c))
- {
- /* Ignore UTF errors, but still consume input
- */
- break;
- }
- if (c >= 0x80)
- {
- if (isUniAlpha(c))
- continue;
- }
- else if (isalnum(c) || c == '_')
- continue;
- i = oi;
- break;
- }
- return i;
-}
-
-
-/************************************************
- * Scan forward past URL starting at i.
- * We don't want to highlight parts of a URL.
- * Returns:
- * i if not a URL
- * index just past it if it is a URL
- */
-
-size_t skippastURL(OutBuffer *buf, size_t i)
-{
- size_t length = buf->length() - i;
- utf8_t *p = (utf8_t *)&buf->slice().ptr[i];
- size_t j;
- unsigned sawdot = 0;
-
- if (length > 7 && Port::memicmp((char *)p, "http://", 7) == 0)
- {
- j = 7;
- }
- else if (length > 8 && Port::memicmp((char *)p, "https://", 8) == 0)
- {
- j = 8;
- }
- else
- goto Lno;
-
- for (; j < length; j++)
- {
- utf8_t c = p[j];
- if (isalnum(c))
- continue;
- if (c == '-' || c == '_' || c == '?' ||
- c == '=' || c == '%' || c == '&' ||
- c == '/' || c == '+' || c == '#' ||
- c == '~')
- continue;
- if (c == '.')
- {
- sawdot = 1;
- continue;
- }
- break;
- }
- if (sawdot)
- return i + j;
-
-Lno:
- return i;
-}
-
-
-/****************************************************
- */
-
-bool isIdentifier(Dsymbols *a, const utf8_t *p, size_t len)
-{
- for (size_t i = 0; i < a->length; i++)
- {
- const char *s = (*a)[i]->ident->toChars();
- if (cmp(s, p, len) == 0)
- return true;
- }
- return false;
-}
-
-/****************************************************
- */
-
-bool isKeyword(utf8_t *p, size_t len)
-{
- static const char *table[] = { "true", "false", "null", NULL };
-
- for (int i = 0; table[i]; i++)
- {
- if (cmp(table[i], p, len) == 0)
- return true;
- }
- return false;
-}
-
-/****************************************************
- */
-
-TypeFunction *isTypeFunction(Dsymbol *s)
-{
- FuncDeclaration *f = s->isFuncDeclaration();
-
- /* f->type may be NULL for template members.
- */
- if (f && f->type)
- {
- Type *t = f->originalType ? f->originalType : f->type;
- if (t->ty == Tfunction)
- return (TypeFunction *)t;
- }
- return NULL;
-}
-
-/****************************************************
- */
-
-Parameter *isFunctionParameter(Dsymbols *a, const utf8_t *p, size_t len)
-{
- for (size_t i = 0; i < a->length; i++)
- {
- Parameter *fparam = isFunctionParameter((*a)[i], p, len);
- if (fparam)
- {
- return fparam;
- }
- }
- return NULL;
-}
-
-/****************************************************
- */
-
-TemplateParameter *isTemplateParameter(Dsymbols *a, const utf8_t *p, size_t len)
-{
- for (size_t i = 0; i < a->length; i++)
- {
- TemplateDeclaration *td = (*a)[i]->isTemplateDeclaration();
- // Check for the parent, if the current symbol is not a template declaration.
- if (!td)
- td = getEponymousParent((*a)[i]);
- if (td && td->origParameters)
- {
- for (size_t k = 0; k < td->origParameters->length; k++)
- {
- TemplateParameter *tp = (*td->origParameters)[k];
- if (tp->ident && cmp(tp->ident->toChars(), p, len) == 0)
- {
- return tp;
- }
- }
- }
- }
- return NULL;
-}
-
-/****************************************************
- * Return true if str is a reserved symbol name
- * that starts with a double underscore.
- */
-
-bool isReservedName(utf8_t *str, size_t len)
-{
- static const char *table[] = {
- "__ctor", "__dtor", "__postblit", "__invariant", "__unitTest",
- "__require", "__ensure", "__dollar", "__ctfe", "__withSym", "__result",
- "__returnLabel", "__vptr", "__monitor", "__gate", "__xopEquals", "__xopCmp",
- "__LINE__", "__FILE__", "__MODULE__", "__FUNCTION__", "__PRETTY_FUNCTION__",
- "__DATE__", "__TIME__", "__TIMESTAMP__", "__VENDOR__", "__VERSION__",
- "__EOF__", "__LOCAL_SIZE", "___tls_get_addr", "__entrypoint", NULL };
-
- for (int i = 0; table[i]; i++)
- {
- if (cmp(table[i], str, len) == 0)
- return true;
- }
- return false;
-}
-
-/**************************************************
- * Highlight text section.
- */
-
-void highlightText(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset)
-{
- Dsymbol *s = a->length ? (*a)[0] : NULL; // test
-
- //printf("highlightText()\n");
-
- int leadingBlank = 1;
- int inCode = 0;
- int inBacktick = 0;
- //int inComment = 0; // in <!-- ... --> comment
- size_t iCodeStart = 0; // start of code section
- size_t codeIndent = 0;
-
- size_t iLineStart = offset;
-
- for (size_t i = offset; i < buf->length(); i++)
- {
- utf8_t c = buf->slice().ptr[i];
-
- Lcont:
- switch (c)
- {
- case ' ':
- case '\t':
- break;
-
- case '\n':
- if (inBacktick)
- {
- // `inline code` is only valid if contained on a single line
- // otherwise, the backticks should be output literally.
- //
- // This lets things like `output from the linker' display
- // unmolested while keeping the feature consistent with GitHub.
-
- inBacktick = false;
- inCode = false; // the backtick also assumes we're in code
-
- // Nothing else is necessary since the DDOC_BACKQUOTED macro is
- // inserted lazily at the close quote, meaning the rest of the
- // text is already OK.
- }
-
- if (!sc->_module->isDocFile &&
- !inCode && i == iLineStart && i + 1 < buf->length()) // if "\n\n"
- {
- static const char blankline[] = "$(DDOC_BLANKLINE)\n";
-
- i = buf->insert(i, blankline, strlen(blankline));
- }
- leadingBlank = 1;
- iLineStart = i + 1;
- break;
-
- case '<':
- {
- leadingBlank = 0;
- if (inCode)
- break;
- utf8_t *p = (utf8_t *)&buf->slice().ptr[i];
- const char *se = sc->_module->escapetable->escapeChar('<');
- if (se && strcmp(se, "&lt;") == 0)
- {
- // Generating HTML
- // Skip over comments
- if (p[1] == '!' && p[2] == '-' && p[3] == '-')
- {
- size_t j = i + 4;
- p += 4;
- while (1)
- {
- if (j == buf->length())
- goto L1;
- if (p[0] == '-' && p[1] == '-' && p[2] == '>')
- {
- i = j + 2; // place on closing '>'
- break;
- }
- j++;
- p++;
- }
- break;
- }
-
- // Skip over HTML tag
- if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2])))
- {
- size_t j = i + 2;
- p += 2;
- while (1)
- {
- if (j == buf->length())
- break;
- if (p[0] == '>')
- {
- i = j; // place on closing '>'
- break;
- }
- j++;
- p++;
- }
- break;
- }
- }
- L1:
- // Replace '<' with '&lt;' character entity
- if (se)
- {
- size_t len = strlen(se);
- buf->remove(i, 1);
- i = buf->insert(i, se, len);
- i--; // point to ';'
- }
- break;
- }
- case '>':
- {
- leadingBlank = 0;
- if (inCode)
- break;
- // Replace '>' with '&gt;' character entity
- const char *se = sc->_module->escapetable->escapeChar('>');
- if (se)
- {
- size_t len = strlen(se);
- buf->remove(i, 1);
- i = buf->insert(i, se, len);
- i--; // point to ';'
- }
- break;
- }
- case '&':
- {
- leadingBlank = 0;
- if (inCode)
- break;
- utf8_t *p = (utf8_t *)&buf->slice().ptr[i];
- if (p[1] == '#' || isalpha(p[1]))
- break; // already a character entity
- // Replace '&' with '&amp;' character entity
- const char *se = sc->_module->escapetable->escapeChar('&');
- if (se)
- {
- size_t len = strlen(se);
- buf->remove(i, 1);
- i = buf->insert(i, se, len);
- i--; // point to ';'
- }
- break;
- }
- case '`':
- {
- if (inBacktick)
- {
- inBacktick = 0;
- inCode = 0;
-
- OutBuffer codebuf;
-
- codebuf.write(buf->slice().ptr + iCodeStart + 1, i - (iCodeStart + 1));
-
- // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL
- highlightCode(sc, a, &codebuf, 0);
-
- buf->remove(iCodeStart, i - iCodeStart + 1); // also trimming off the current `
-
- static const char pre[] = "$(DDOC_BACKQUOTED ";
- i = buf->insert(iCodeStart, pre, strlen(pre));
- i = buf->insert(i, (char *)codebuf.slice().ptr, codebuf.length());
- i = buf->insert(i, ")", 1);
-
- i--; // point to the ending ) so when the for loop does i++, it will see the next character
-
- break;
- }
-
- if (inCode)
- break;
-
- inCode = 1;
- inBacktick = 1;
- codeIndent = 0; // inline code is not indented
-
- // All we do here is set the code flags and record
- // the location. The macro will be inserted lazily
- // so we can easily cancel the inBacktick if we come
- // across a newline character.
- iCodeStart = i;
-
- break;
- }
- case '-':
- /* A line beginning with --- delimits a code section.
- * inCode tells us if it is start or end of a code section.
- */
- if (leadingBlank)
- {
- size_t istart = i;
- size_t eollen = 0;
-
- leadingBlank = 0;
- while (1)
- {
- ++i;
- if (i >= buf->length())
- break;
- c = buf->slice().ptr[i];
- if (c == '\n')
- {
- eollen = 1;
- break;
- }
- if (c == '\r')
- {
- eollen = 1;
- if (i + 1 >= buf->length())
- break;
- if (buf->slice().ptr[i + 1] == '\n')
- {
- eollen = 2;
- break;
- }
- }
- // BUG: handle UTF PS and LS too
- if (c != '-')
- goto Lcont;
- }
- if (i - istart < 3)
- goto Lcont;
-
- // We have the start/end of a code section
-
- // Remove the entire --- line, including blanks and \n
- buf->remove(iLineStart, i - iLineStart + eollen);
- i = iLineStart;
-
- if (inCode && (i <= iCodeStart))
- {
- // Empty code section, just remove it completely.
- inCode = 0;
- break;
- }
-
- if (inCode)
- {
- inCode = 0;
- // The code section is from iCodeStart to i
- OutBuffer codebuf;
-
- codebuf.write(buf->slice().ptr + iCodeStart, i - iCodeStart);
- codebuf.writeByte(0);
-
- // Remove leading indentations from all lines
- bool lineStart = true;
- utf8_t *endp = (utf8_t *)codebuf.slice().ptr + codebuf.length();
- for (utf8_t *p = (utf8_t *)codebuf.slice().ptr; p < endp; )
- {
- if (lineStart)
- {
- size_t j = codeIndent;
- utf8_t *q = p;
- while (j-- > 0 && q < endp && isIndentWS(q))
- ++q;
- codebuf.remove(p - (utf8_t *)codebuf.slice().ptr, q - p);
- assert((utf8_t *)codebuf.slice().ptr <= p);
- assert(p < (utf8_t *)codebuf.slice().ptr + codebuf.length());
- lineStart = false;
- endp = (utf8_t *)codebuf.slice().ptr + codebuf.length(); // update
- continue;
- }
- if (*p == '\n')
- lineStart = true;
- ++p;
- }
-
- highlightCode2(sc, a, &codebuf, 0);
- buf->remove(iCodeStart, i - iCodeStart);
- i = buf->insert(iCodeStart, codebuf.slice().ptr, codebuf.length());
- i = buf->insert(i, (const char *)")\n", 2);
- i -= 2; // in next loop, c should be '\n'
- }
- else
- {
- static const char d_code[] = "$(D_CODE ";
-
- inCode = 1;
- codeIndent = istart - iLineStart; // save indent count
- i = buf->insert(i, d_code, strlen(d_code));
- iCodeStart = i;
- i--; // place i on >
- leadingBlank = true;
- }
- }
- break;
-
- default:
- leadingBlank = 0;
- if (sc->_module->isDocFile || inCode)
- break;
-
- utf8_t *start = (utf8_t *)buf->slice().ptr + i;
- if (isIdStart(start))
- {
- size_t j = skippastident(buf, i);
- if (i < j)
- {
- size_t k = skippastURL(buf, i);
- if (i < k)
- {
- i = k - 1;
- break;
- }
- }
- else
- break;
- size_t len = j - i;
-
- // leading '_' means no highlight unless it's a reserved symbol name
- if (c == '_' &&
- (i == 0 || !isdigit(*(start - 1))) &&
- (i == buf->length() - 1 || !isReservedName(start, len)))
- {
- buf->remove(i, 1);
- i = j - 1;
- break;
- }
- if (isIdentifier(a, start, len))
- {
- i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
- break;
- }
- if (isKeyword(start, len))
- {
- i = buf->bracket(i, "$(DDOC_KEYWORD ", j, ")") - 1;
- break;
- }
- if (isFunctionParameter(a, start, len))
- {
- //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
- i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1;
- break;
- }
-
- i = j - 1;
- }
- break;
- }
- }
- if (inCode)
- error(s ? s->loc : Loc(), "unmatched --- in DDoc comment");
-}
-
-/**************************************************
- * Highlight code for DDOC section.
- */
-
-void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset)
-{
- //printf("highlightCode(s = %s '%s')\n", s->kind(), s->toChars());
- OutBuffer ancbuf;
- emitAnchor(&ancbuf, s, sc);
- buf->insert(offset, (char *)ancbuf.slice().ptr, ancbuf.length());
- offset += ancbuf.length();
-
- Dsymbols a;
- a.push(s);
- highlightCode(sc, &a, buf, offset);
-}
-
-/****************************************************
- */
-
-void highlightCode(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset)
-{
- //printf("highlightCode(a = '%s')\n", a->toChars());
-
- for (size_t i = offset; i < buf->length(); i++)
- {
- utf8_t c = buf->slice().ptr[i];
- const char *se = sc->_module->escapetable->escapeChar(c);
- if (se)
- {
- size_t len = strlen(se);
- buf->remove(i, 1);
- i = buf->insert(i, se, len);
- i--; // point to ';'
- continue;
- }
-
- utf8_t *start = (utf8_t *)buf->slice().ptr + i;
- if (isIdStart(start))
- {
- size_t j = skippastident(buf, i);
- if (i < j)
- {
- size_t len = j - i;
- if (isIdentifier(a, start, len))
- {
- i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
- continue;
- }
- if (isFunctionParameter(a, start, len))
- {
- //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
- i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1;
- continue;
- }
- i = j - 1;
- }
- }
- }
-}
-
-/****************************************
- */
-
-void highlightCode3(Scope *sc, OutBuffer *buf, const utf8_t *p, const utf8_t *pend)
-{
- for (; p < pend; p++)
- {
- const char *s = sc->_module->escapetable->escapeChar(*p);
- if (s)
- buf->writestring(s);
- else
- buf->writeByte(*p);
- }
-}
-
-/**************************************************
- * Highlight code for CODE section.
- */
-
-void highlightCode2(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset)
-{
- unsigned errorsave = global.errors;
- Lexer lex(NULL, (utf8_t *)buf->slice().ptr, 0, buf->length() - 1, 0, 1);
- OutBuffer res;
- const utf8_t *lastp = (utf8_t *)buf->slice().ptr;
-
- //printf("highlightCode2('%.*s')\n", buf->length() - 1, buf->slice().ptr);
- res.reserve(buf->length());
- while (1)
- {
- Token tok;
- lex.scan(&tok);
- highlightCode3(sc, &res, lastp, tok.ptr);
-
- const char *highlight = NULL;
- switch (tok.value)
- {
- case TOKidentifier:
- {
- if (!sc)
- break;
- size_t len = lex.p - tok.ptr;
- if (isIdentifier(a, tok.ptr, len))
- {
- highlight = "$(D_PSYMBOL ";
- break;
- }
- if (isFunctionParameter(a, tok.ptr, len))
- {
- //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
- highlight = "$(D_PARAM ";
- break;
- }
- break;
- }
- case TOKcomment:
- highlight = "$(D_COMMENT ";
- break;
-
- case TOKstring:
- highlight = "$(D_STRING ";
- break;
-
- default:
- if (tok.isKeyword())
- highlight = "$(D_KEYWORD ";
- break;
- }
- if (highlight)
- {
- res.writestring(highlight);
- size_t o = res.length();
- highlightCode3(sc, &res, tok.ptr, lex.p);
- if (tok.value == TOKcomment || tok.value == TOKstring)
- escapeDdocString(&res, o); // Bugzilla 7656, 7715, and 10519
- res.writeByte(')');
- }
- else
- highlightCode3(sc, &res, tok.ptr, lex.p);
- if (tok.value == TOKeof)
- break;
- lastp = lex.p;
- }
- buf->setsize(offset);
- buf->write(&res);
- global.errors = errorsave;
-}
-
-/***************************************
- * Find character string to replace c with.
- */
-
-const char *Escape::escapeChar(unsigned c)
-{
- assert(c < 256);
- //printf("escapeChar('%c') => %p, %p\n", c, strings, strings[c]);
- return strings[c];
-}
-
-/****************************************
- * Determine if p points to the start of a "..." parameter identifier.
- */
-
-bool isCVariadicArg(const utf8_t *p, size_t len)
-{
- return len >= 3 && cmp("...", p, 3) == 0;
-}
-
-/****************************************
- * Determine if p points to the start of an identifier.
- */
-
-bool isIdStart(const utf8_t *p)
-{
- unsigned c = *p;
- if (isalpha(c) || c == '_')
- return true;
- if (c >= 0x80)
- {
- size_t i = 0;
- if (utf_decodeChar(p, 4, &i, &c))
- return false; // ignore errors
- if (isUniAlpha(c))
- return true;
- }
- return false;
-}
-
-/****************************************
- * Determine if p points to the rest of an identifier.
- */
-
-bool isIdTail(const utf8_t *p)
-{
- unsigned c = *p;
- if (isalnum(c) || c == '_')
- return true;
- if (c >= 0x80)
- {
- size_t i = 0;
- if (utf_decodeChar(p, 4, &i, &c))
- return false; // ignore errors
- if (isUniAlpha(c))
- return true;
- }
- return false;
-}
-
-/****************************************
- * Determine if p points to the indentation space.
- */
-
-bool isIndentWS(const utf8_t *p)
-{
- return (*p == ' ') || (*p == '\t');
-}
-
-/*****************************************
- * Return number of bytes in UTF character.
- */
-
-int utfStride(const utf8_t *p)
-{
- unsigned c = *p;
- if (c < 0x80)
- return 1;
- size_t i = 0;
- utf_decodeChar(p, 4, &i, &c); // ignore errors, but still consume input
- return (int)i;
-}
diff --git a/gcc/d/dmd/doc.d b/gcc/d/dmd/doc.d
new file mode 100644
index 00000000000..9b4329b8b0a
--- /dev/null
+++ b/gcc/d/dmd/doc.d
@@ -0,0 +1,5388 @@
+/**
+ * Ddoc documentation generation.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/ddoc.html, Documentation Generator)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/doc.d, _doc.d)
+ * Documentation: https://dlang.org/phobos/dmd_doc.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/doc.d
+ */
+
+module dmd.doc;
+
+import core.stdc.ctype;
+import core.stdc.stdlib;
+import core.stdc.stdio;
+import core.stdc.string;
+import core.stdc.time;
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.cond;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dmacro;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.lexer;
+import dmd.mtype;
+import dmd.root.array;
+import dmd.root.file;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.port;
+import dmd.root.rmem;
+import dmd.root.string;
+import dmd.tokens;
+import dmd.utf;
+import dmd.utils;
+import dmd.visitor;
+
+struct Escape
+{
+ const(char)[][char.max] strings;
+
+ /***************************************
+ * Find character string to replace c with.
+ */
+ const(char)[] escapeChar(char c)
+ {
+ version (all)
+ {
+ //printf("escapeChar('%c') => %p, %p\n", c, strings, strings[c].ptr);
+ return strings[c];
+ }
+ else
+ {
+ const(char)[] s;
+ switch (c)
+ {
+ case '<':
+ s = "&lt;";
+ break;
+ case '>':
+ s = "&gt;";
+ break;
+ case '&':
+ s = "&amp;";
+ break;
+ default:
+ s = null;
+ break;
+ }
+ return s;
+ }
+ }
+}
+
+/***********************************************************
+ */
+private class Section
+{
+ const(char)[] name;
+ const(char)[] body_;
+ int nooutput;
+
+ override string toString() const
+ {
+ assert(0);
+ }
+
+ void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, OutBuffer* buf)
+ {
+ assert(a.dim);
+ if (name.length)
+ {
+ static immutable table =
+ [
+ "AUTHORS",
+ "BUGS",
+ "COPYRIGHT",
+ "DATE",
+ "DEPRECATED",
+ "EXAMPLES",
+ "HISTORY",
+ "LICENSE",
+ "RETURNS",
+ "SEE_ALSO",
+ "STANDARDS",
+ "THROWS",
+ "VERSION",
+ ];
+ foreach (entry; table)
+ {
+ if (iequals(entry, name))
+ {
+ buf.printf("$(DDOC_%s ", entry.ptr);
+ goto L1;
+ }
+ }
+ buf.writestring("$(DDOC_SECTION ");
+ // Replace _ characters with spaces
+ buf.writestring("$(DDOC_SECTION_H ");
+ size_t o = buf.length;
+ foreach (char c; name)
+ buf.writeByte((c == '_') ? ' ' : c);
+ escapeStrayParenthesis(loc, buf, o, false);
+ buf.writestring(")");
+ }
+ else
+ {
+ buf.writestring("$(DDOC_DESCRIPTION ");
+ }
+ L1:
+ size_t o = buf.length;
+ buf.write(body_);
+ escapeStrayParenthesis(loc, buf, o, true);
+ highlightText(sc, a, loc, *buf, o);
+ buf.writestring(")");
+ }
+}
+
+/***********************************************************
+ */
+private final class ParamSection : Section
+{
+ override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, OutBuffer* buf)
+ {
+ assert(a.dim);
+ Dsymbol s = (*a)[0]; // test
+ const(char)* p = body_.ptr;
+ size_t len = body_.length;
+ const(char)* pend = p + len;
+ const(char)* tempstart = null;
+ size_t templen = 0;
+ const(char)* namestart = null;
+ size_t namelen = 0; // !=0 if line continuation
+ const(char)* textstart = null;
+ size_t textlen = 0;
+ size_t paramcount = 0;
+ buf.writestring("$(DDOC_PARAMS ");
+ while (p < pend)
+ {
+ // Skip to start of macro
+ while (1)
+ {
+ switch (*p)
+ {
+ case ' ':
+ case '\t':
+ p++;
+ continue;
+ case '\n':
+ p++;
+ goto Lcont;
+ default:
+ if (isIdStart(p) || isCVariadicArg(p[0 .. cast(size_t)(pend - p)]))
+ break;
+ if (namelen)
+ goto Ltext;
+ // continuation of prev macro
+ goto Lskipline;
+ }
+ break;
+ }
+ tempstart = p;
+ while (isIdTail(p))
+ p += utfStride(p);
+ if (isCVariadicArg(p[0 .. cast(size_t)(pend - p)]))
+ p += 3;
+ templen = p - tempstart;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p != '=')
+ {
+ if (namelen)
+ goto Ltext;
+ // continuation of prev macro
+ goto Lskipline;
+ }
+ p++;
+ if (namelen)
+ {
+ // Output existing param
+ L1:
+ //printf("param '%.*s' = '%.*s'\n", cast(int)namelen, namestart, cast(int)textlen, textstart);
+ ++paramcount;
+ HdrGenState hgs;
+ buf.writestring("$(DDOC_PARAM_ROW ");
+ {
+ buf.writestring("$(DDOC_PARAM_ID ");
+ {
+ size_t o = buf.length;
+ Parameter fparam = isFunctionParameter(a, namestart, namelen);
+ if (!fparam)
+ {
+ // Comments on a template might refer to function parameters within.
+ // Search the parameters of nested eponymous functions (with the same name.)
+ fparam = isEponymousFunctionParameter(a, namestart, namelen);
+ }
+ bool isCVariadic = isCVariadicParameter(a, namestart[0 .. namelen]);
+ if (isCVariadic)
+ {
+ buf.writestring("...");
+ }
+ else if (fparam && fparam.type && fparam.ident)
+ {
+ .toCBuffer(fparam.type, buf, fparam.ident, &hgs);
+ }
+ else
+ {
+ if (isTemplateParameter(a, namestart, namelen))
+ {
+ // 10236: Don't count template parameters for params check
+ --paramcount;
+ }
+ else if (!fparam)
+ {
+ warning(s.loc, "Ddoc: function declaration has no parameter '%.*s'", cast(int)namelen, namestart);
+ }
+ buf.write(namestart[0 .. namelen]);
+ }
+ escapeStrayParenthesis(loc, buf, o, true);
+ highlightCode(sc, a, *buf, o);
+ }
+ buf.writestring(")");
+ buf.writestring("$(DDOC_PARAM_DESC ");
+ {
+ size_t o = buf.length;
+ buf.write(textstart[0 .. textlen]);
+ escapeStrayParenthesis(loc, buf, o, true);
+ highlightText(sc, a, loc, *buf, o);
+ }
+ buf.writestring(")");
+ }
+ buf.writestring(")");
+ namelen = 0;
+ if (p >= pend)
+ break;
+ }
+ namestart = tempstart;
+ namelen = templen;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ textstart = p;
+ Ltext:
+ while (*p != '\n')
+ p++;
+ textlen = p - textstart;
+ p++;
+ Lcont:
+ continue;
+ Lskipline:
+ // Ignore this line
+ while (*p++ != '\n')
+ {
+ }
+ }
+ if (namelen)
+ goto L1;
+ // write out last one
+ buf.writestring(")");
+ TypeFunction tf = a.dim == 1 ? isTypeFunction(s) : null;
+ if (tf)
+ {
+ size_t pcount = (tf.parameterList.parameters ? tf.parameterList.parameters.dim : 0) +
+ cast(int)(tf.parameterList.varargs == VarArg.variadic);
+ if (pcount != paramcount)
+ {
+ warning(s.loc, "Ddoc: parameter count mismatch, expected %llu, got %llu",
+ cast(ulong) pcount, cast(ulong) paramcount);
+ if (paramcount == 0)
+ {
+ // Chances are someone messed up the format
+ warningSupplemental(s.loc, "Note that the format is `param = description`");
+ }
+ }
+ }
+ }
+}
+
+/***********************************************************
+ */
+private final class MacroSection : Section
+{
+ override void write(Loc loc, DocComment* dc, Scope* sc, Dsymbols* a, OutBuffer* buf)
+ {
+ //printf("MacroSection::write()\n");
+ DocComment.parseMacros(dc.escapetable, *dc.pmacrotable, body_);
+ }
+}
+
+private alias Sections = Array!(Section);
+
+// Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one).
+private bool isCVariadicParameter(Dsymbols* a, const(char)[] p)
+{
+ foreach (member; *a)
+ {
+ TypeFunction tf = isTypeFunction(member);
+ if (tf && tf.parameterList.varargs == VarArg.variadic && p == "...")
+ return true;
+ }
+ return false;
+}
+
+private Dsymbol getEponymousMember(TemplateDeclaration td)
+{
+ if (!td.onemember)
+ return null;
+ if (AggregateDeclaration ad = td.onemember.isAggregateDeclaration())
+ return ad;
+ if (FuncDeclaration fd = td.onemember.isFuncDeclaration())
+ return fd;
+ if (auto em = td.onemember.isEnumMember())
+ return null; // Keep backward compatibility. See compilable/ddoc9.d
+ if (VarDeclaration vd = td.onemember.isVarDeclaration())
+ return td.constraint ? null : vd;
+ return null;
+}
+
+private TemplateDeclaration getEponymousParent(Dsymbol s)
+{
+ if (!s.parent)
+ return null;
+ TemplateDeclaration td = s.parent.isTemplateDeclaration();
+ return (td && getEponymousMember(td)) ? td : null;
+}
+
+private immutable ddoc_default = import("default_ddoc_theme." ~ ddoc_ext);
+private immutable ddoc_decl_s = "$(DDOC_DECL ";
+private immutable ddoc_decl_e = ")\n";
+private immutable ddoc_decl_dd_s = "$(DDOC_DECL_DD ";
+private immutable ddoc_decl_dd_e = ")\n";
+
+/****************************************************
+ */
+extern(C++) void gendocfile(Module m)
+{
+ __gshared OutBuffer mbuf;
+ __gshared int mbuf_done;
+ OutBuffer buf;
+ //printf("Module::gendocfile()\n");
+ if (!mbuf_done) // if not already read the ddoc files
+ {
+ mbuf_done = 1;
+ // Use our internal default
+ mbuf.writestring(ddoc_default);
+ // Override with DDOCFILE specified in the sc.ini file
+ char* p = getenv("DDOCFILE");
+ if (p)
+ global.params.ddocfiles.shift(p);
+ // Override with the ddoc macro files from the command line
+ for (size_t i = 0; i < global.params.ddocfiles.dim; i++)
+ {
+ auto buffer = readFile(m.loc, global.params.ddocfiles[i]);
+ // BUG: convert file contents to UTF-8 before use
+ const data = buffer.data;
+ //printf("file: '%.*s'\n", cast(int)data.length, data.ptr);
+ mbuf.write(data);
+ }
+ }
+ DocComment.parseMacros(m.escapetable, m.macrotable, mbuf[]);
+ Scope* sc = Scope.createGlobal(m); // create root scope
+ DocComment* dc = DocComment.parse(m, m.comment);
+ dc.pmacrotable = &m.macrotable;
+ dc.escapetable = m.escapetable;
+ sc.lastdc = dc;
+ // Generate predefined macros
+ // Set the title to be the name of the module
+ {
+ const p = m.toPrettyChars().toDString;
+ m.macrotable.define("TITLE", p);
+ }
+ // Set time macros
+ {
+ time_t t;
+ time(&t);
+ char* p = ctime(&t);
+ p = mem.xstrdup(p);
+ m.macrotable.define("DATETIME", p.toDString());
+ m.macrotable.define("YEAR", p[20 .. 20 + 4]);
+ }
+ const srcfilename = m.srcfile.toString();
+ m.macrotable.define("SRCFILENAME", srcfilename);
+ const docfilename = m.docfile.toString();
+ m.macrotable.define("DOCFILENAME", docfilename);
+ if (dc.copyright)
+ {
+ dc.copyright.nooutput = 1;
+ m.macrotable.define("COPYRIGHT", dc.copyright.body_);
+ }
+ if (m.isDocFile)
+ {
+ const ploc = m.md ? &m.md.loc : &m.loc;
+ const loc = Loc(ploc.filename ? ploc.filename : srcfilename.ptr,
+ ploc.linnum,
+ ploc.charnum);
+
+ size_t commentlen = strlen(cast(char*)m.comment);
+ Dsymbols a;
+ // https://issues.dlang.org/show_bug.cgi?id=9764
+ // Don't push m in a, to prevent emphasize ddoc file name.
+ if (dc.macros)
+ {
+ commentlen = dc.macros.name.ptr - m.comment;
+ dc.macros.write(loc, dc, sc, &a, &buf);
+ }
+ buf.write(m.comment[0 .. commentlen]);
+ highlightText(sc, &a, loc, buf, 0);
+ }
+ else
+ {
+ Dsymbols a;
+ a.push(m);
+ dc.writeSections(sc, &a, &buf);
+ emitMemberComments(m, buf, sc);
+ }
+ //printf("BODY= '%.*s'\n", cast(int)buf.length, buf.data);
+ m.macrotable.define("BODY", buf[]);
+ OutBuffer buf2;
+ buf2.writestring("$(DDOC)");
+ size_t end = buf2.length;
+ m.macrotable.expand(buf2, 0, end, null);
+ version (all)
+ {
+ /* Remove all the escape sequences from buf2,
+ * and make CR-LF the newline.
+ */
+ {
+ const slice = buf2[];
+ buf.setsize(0);
+ buf.reserve(slice.length);
+ auto p = slice.ptr;
+ for (size_t j = 0; j < slice.length; j++)
+ {
+ char c = p[j];
+ if (c == 0xFF && j + 1 < slice.length)
+ {
+ j++;
+ continue;
+ }
+ if (c == '\n')
+ buf.writeByte('\r');
+ else if (c == '\r')
+ {
+ buf.writestring("\r\n");
+ if (j + 1 < slice.length && p[j + 1] == '\n')
+ {
+ j++;
+ }
+ continue;
+ }
+ buf.writeByte(c);
+ }
+ }
+ writeFile(m.loc, m.docfile.toString(), buf[]);
+ }
+ else
+ {
+ /* Remove all the escape sequences from buf2
+ */
+ {
+ size_t i = 0;
+ char* p = buf2.data;
+ for (size_t j = 0; j < buf2.length; j++)
+ {
+ if (p[j] == 0xFF && j + 1 < buf2.length)
+ {
+ j++;
+ continue;
+ }
+ p[i] = p[j];
+ i++;
+ }
+ buf2.setsize(i);
+ }
+ writeFile(m.loc, m.docfile.toString(), buf2[]);
+ }
+}
+
+/****************************************************
+ * Having unmatched parentheses can hose the output of Ddoc,
+ * as the macros depend on properly nested parentheses.
+ * This function replaces all ( with $(LPAREN) and ) with $(RPAREN)
+ * to preserve text literally. This also means macros in the
+ * text won't be expanded.
+ */
+void escapeDdocString(OutBuffer* buf, size_t start)
+{
+ for (size_t u = start; u < buf.length; u++)
+ {
+ char c = (*buf)[u];
+ switch (c)
+ {
+ case '$':
+ buf.remove(u, 1);
+ buf.insert(u, "$(DOLLAR)");
+ u += 8;
+ break;
+ case '(':
+ buf.remove(u, 1); //remove the (
+ buf.insert(u, "$(LPAREN)"); //insert this instead
+ u += 8; //skip over newly inserted macro
+ break;
+ case ')':
+ buf.remove(u, 1); //remove the )
+ buf.insert(u, "$(RPAREN)"); //insert this instead
+ u += 8; //skip over newly inserted macro
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/****************************************************
+ * Having unmatched parentheses can hose the output of Ddoc,
+ * as the macros depend on properly nested parentheses.
+ *
+ * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN).
+ *
+ * Params:
+ * loc = source location of start of text. It is a mutable copy to allow incrementing its linenum, for printing the correct line number when an error is encountered in a multiline block of ddoc.
+ * buf = an OutBuffer containing the DDoc
+ * start = the index within buf to start replacing unmatched parentheses
+ * respectBackslashEscapes = if true, always replace parentheses that are
+ * directly preceeded by a backslash with $(LPAREN) or $(RPAREN) instead of
+ * counting them as stray parentheses
+ */
+private void escapeStrayParenthesis(Loc loc, OutBuffer* buf, size_t start, bool respectBackslashEscapes)
+{
+ uint par_open = 0;
+ char inCode = 0;
+ bool atLineStart = true;
+ for (size_t u = start; u < buf.length; u++)
+ {
+ char c = (*buf)[u];
+ switch (c)
+ {
+ case '(':
+ if (!inCode)
+ par_open++;
+ atLineStart = false;
+ break;
+ case ')':
+ if (!inCode)
+ {
+ if (par_open == 0)
+ {
+ //stray ')'
+ warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output. Use $(RPAREN) instead for unpaired right parentheses.");
+ buf.remove(u, 1); //remove the )
+ buf.insert(u, "$(RPAREN)"); //insert this instead
+ u += 8; //skip over newly inserted macro
+ }
+ else
+ par_open--;
+ }
+ atLineStart = false;
+ break;
+ case '\n':
+ atLineStart = true;
+ version (none)
+ {
+ // For this to work, loc must be set to the beginning of the passed
+ // text which is currently not possible
+ // (loc is set to the Loc of the Dsymbol)
+ loc.linnum++;
+ }
+ break;
+ case ' ':
+ case '\r':
+ case '\t':
+ break;
+ case '-':
+ case '`':
+ case '~':
+ // Issue 15465: don't try to escape unbalanced parens inside code
+ // blocks.
+ int numdash = 1;
+ for (++u; u < buf.length && (*buf)[u] == c; ++u)
+ ++numdash;
+ --u;
+ if (c == '`' || (atLineStart && numdash >= 3))
+ {
+ if (inCode == c)
+ inCode = 0;
+ else if (!inCode)
+ inCode = c;
+ }
+ atLineStart = false;
+ break;
+ case '\\':
+ // replace backslash-escaped parens with their macros
+ if (!inCode && respectBackslashEscapes && u+1 < buf.length && global.params.markdown)
+ {
+ if ((*buf)[u+1] == '(' || (*buf)[u+1] == ')')
+ {
+ const paren = (*buf)[u+1] == '(' ? "$(LPAREN)" : "$(RPAREN)";
+ buf.remove(u, 2); //remove the \)
+ buf.insert(u, paren); //insert this instead
+ u += 8; //skip over newly inserted macro
+ }
+ else if ((*buf)[u+1] == '\\')
+ ++u;
+ }
+ break;
+ default:
+ atLineStart = false;
+ break;
+ }
+ }
+ if (par_open) // if any unmatched lparens
+ {
+ par_open = 0;
+ for (size_t u = buf.length; u > start;)
+ {
+ u--;
+ char c = (*buf)[u];
+ switch (c)
+ {
+ case ')':
+ par_open++;
+ break;
+ case '(':
+ if (par_open == 0)
+ {
+ //stray '('
+ warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output. Use $(LPAREN) instead for unpaired left parentheses.");
+ buf.remove(u, 1); //remove the (
+ buf.insert(u, "$(LPAREN)"); //insert this instead
+ }
+ else
+ par_open--;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+// Basically, this is to skip over things like private{} blocks in a struct or
+// class definition that don't add any components to the qualified name.
+private Scope* skipNonQualScopes(Scope* sc)
+{
+ while (sc && !sc.scopesym)
+ sc = sc.enclosing;
+ return sc;
+}
+
+private bool emitAnchorName(ref OutBuffer buf, Dsymbol s, Scope* sc, bool includeParent)
+{
+ if (!s || s.isPackage() || s.isModule())
+ return false;
+ // Add parent names first
+ bool dot = false;
+ auto eponymousParent = getEponymousParent(s);
+ if (includeParent && s.parent || eponymousParent)
+ dot = emitAnchorName(buf, s.parent, sc, includeParent);
+ else if (includeParent && sc)
+ dot = emitAnchorName(buf, sc.scopesym, skipNonQualScopes(sc.enclosing), includeParent);
+ // Eponymous template members can share the parent anchor name
+ if (eponymousParent)
+ return dot;
+ if (dot)
+ buf.writeByte('.');
+ // Use "this" not "__ctor"
+ TemplateDeclaration td;
+ if (s.isCtorDeclaration() || ((td = s.isTemplateDeclaration()) !is null && td.onemember && td.onemember.isCtorDeclaration()))
+ {
+ buf.writestring("this");
+ }
+ else
+ {
+ /* We just want the identifier, not overloads like TemplateDeclaration::toChars.
+ * We don't want the template parameter list and constraints. */
+ buf.writestring(s.Dsymbol.toChars());
+ }
+ return true;
+}
+
+private void emitAnchor(ref OutBuffer buf, Dsymbol s, Scope* sc, bool forHeader = false)
+{
+ Identifier ident;
+ {
+ OutBuffer anc;
+ emitAnchorName(anc, s, skipNonQualScopes(sc), true);
+ ident = Identifier.idPool(anc[]);
+ }
+
+ auto pcount = cast(void*)ident in sc.anchorCounts;
+ typeof(*pcount) count;
+ if (!forHeader)
+ {
+ if (pcount)
+ {
+ // Existing anchor,
+ // don't write an anchor for matching consecutive ditto symbols
+ TemplateDeclaration td = getEponymousParent(s);
+ if (sc.prevAnchor == ident && sc.lastdc && (isDitto(s.comment) || (td && isDitto(td.comment))))
+ return;
+
+ count = ++*pcount;
+ }
+ else
+ {
+ sc.anchorCounts[cast(void*)ident] = 1;
+ count = 1;
+ }
+ }
+
+ // cache anchor name
+ sc.prevAnchor = ident;
+ auto macroName = forHeader ? "DDOC_HEADER_ANCHOR" : "DDOC_ANCHOR";
+
+ if (auto imp = s.isImport())
+ {
+ // For example: `public import core.stdc.string : memcpy, memcmp;`
+ if (imp.aliases.dim > 0)
+ {
+ for(int i = 0; i < imp.aliases.dim; i++)
+ {
+ // Need to distinguish between
+ // `public import core.stdc.string : memcpy, memcmp;` and
+ // `public import core.stdc.string : copy = memcpy, compare = memcmp;`
+ auto a = imp.aliases[i];
+ auto id = a ? a : imp.names[i];
+ auto loc = Loc.init;
+ if (auto symFromId = sc.search(loc, id, null))
+ {
+ emitAnchor(buf, symFromId, sc, forHeader);
+ }
+ }
+ }
+ else
+ {
+ // For example: `public import str = core.stdc.string;`
+ if (imp.aliasId)
+ {
+ auto symbolName = imp.aliasId.toString();
+
+ buf.printf("$(%.*s %.*s", cast(int) macroName.length, macroName.ptr,
+ cast(int) symbolName.length, symbolName.ptr);
+
+ if (forHeader)
+ {
+ buf.printf(", %.*s", cast(int) symbolName.length, symbolName.ptr);
+ }
+ }
+ else
+ {
+ // The general case: `public import core.stdc.string;`
+
+ // fully qualify imports so `core.stdc.string` doesn't appear as `core`
+ void printFullyQualifiedImport()
+ {
+ foreach (const pid; imp.packages)
+ {
+ buf.printf("%s.", pid.toChars());
+ }
+ buf.writestring(imp.id.toString());
+ }
+
+ buf.printf("$(%.*s ", cast(int) macroName.length, macroName.ptr);
+ printFullyQualifiedImport();
+
+ if (forHeader)
+ {
+ buf.printf(", ");
+ printFullyQualifiedImport();
+ }
+ }
+
+ buf.writeByte(')');
+ }
+ }
+ else
+ {
+ auto symbolName = ident.toString();
+ buf.printf("$(%.*s %.*s", cast(int) macroName.length, macroName.ptr,
+ cast(int) symbolName.length, symbolName.ptr);
+
+ // only append count once there's a duplicate
+ if (count > 1)
+ buf.printf(".%u", count);
+
+ if (forHeader)
+ {
+ Identifier shortIdent;
+ {
+ OutBuffer anc;
+ emitAnchorName(anc, s, skipNonQualScopes(sc), false);
+ shortIdent = Identifier.idPool(anc[]);
+ }
+
+ auto shortName = shortIdent.toString();
+ buf.printf(", %.*s", cast(int) shortName.length, shortName.ptr);
+ }
+
+ buf.writeByte(')');
+ }
+}
+
+/******************************* emitComment **********************************/
+
+/** Get leading indentation from 'src' which represents lines of code. */
+private size_t getCodeIndent(const(char)* src)
+{
+ while (src && (*src == '\r' || *src == '\n'))
+ ++src; // skip until we find the first non-empty line
+ size_t codeIndent = 0;
+ while (src && (*src == ' ' || *src == '\t'))
+ {
+ codeIndent++;
+ src++;
+ }
+ return codeIndent;
+}
+
+/** Recursively expand template mixin member docs into the scope. */
+private void expandTemplateMixinComments(TemplateMixin tm, ref OutBuffer buf, Scope* sc)
+{
+ if (!tm.semanticRun)
+ tm.dsymbolSemantic(sc);
+ TemplateDeclaration td = (tm && tm.tempdecl) ? tm.tempdecl.isTemplateDeclaration() : null;
+ if (td && td.members)
+ {
+ for (size_t i = 0; i < td.members.dim; i++)
+ {
+ Dsymbol sm = (*td.members)[i];
+ TemplateMixin tmc = sm.isTemplateMixin();
+ if (tmc && tmc.comment)
+ expandTemplateMixinComments(tmc, buf, sc);
+ else
+ emitComment(sm, buf, sc);
+ }
+ }
+}
+
+private void emitMemberComments(ScopeDsymbol sds, ref OutBuffer buf, Scope* sc)
+{
+ if (!sds.members)
+ return;
+ //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
+ const(char)[] m = "$(DDOC_MEMBERS ";
+ if (sds.isTemplateDeclaration())
+ m = "$(DDOC_TEMPLATE_MEMBERS ";
+ else if (sds.isClassDeclaration())
+ m = "$(DDOC_CLASS_MEMBERS ";
+ else if (sds.isStructDeclaration())
+ m = "$(DDOC_STRUCT_MEMBERS ";
+ else if (sds.isEnumDeclaration())
+ m = "$(DDOC_ENUM_MEMBERS ";
+ else if (sds.isModule())
+ m = "$(DDOC_MODULE_MEMBERS ";
+ size_t offset1 = buf.length; // save starting offset
+ buf.writestring(m);
+ size_t offset2 = buf.length; // to see if we write anything
+ sc = sc.push(sds);
+ for (size_t i = 0; i < sds.members.dim; i++)
+ {
+ Dsymbol s = (*sds.members)[i];
+ //printf("\ts = '%s'\n", s.toChars());
+ // only expand if parent is a non-template (semantic won't work)
+ if (s.comment && s.isTemplateMixin() && s.parent && !s.parent.isTemplateDeclaration())
+ expandTemplateMixinComments(cast(TemplateMixin)s, buf, sc);
+ emitComment(s, buf, sc);
+ }
+ emitComment(null, buf, sc);
+ sc.pop();
+ if (buf.length == offset2)
+ {
+ /* Didn't write out any members, so back out last write
+ */
+ buf.setsize(offset1);
+ }
+ else
+ buf.writestring(")");
+}
+
+private void emitVisibility(ref OutBuffer buf, Import i)
+{
+ // imports are private by default, which is different from other declarations
+ // so they should explicitly show their visibility
+ emitVisibility(buf, i.visibility);
+}
+
+private void emitVisibility(ref OutBuffer buf, Declaration d)
+{
+ auto vis = d.visibility;
+ if (vis.kind != Visibility.Kind.undefined && vis.kind != Visibility.Kind.public_)
+ {
+ emitVisibility(buf, vis);
+ }
+}
+
+private void emitVisibility(ref OutBuffer buf, Visibility vis)
+{
+ visibilityToBuffer(&buf, vis);
+ buf.writeByte(' ');
+}
+
+private void emitComment(Dsymbol s, ref OutBuffer buf, Scope* sc)
+{
+ extern (C++) final class EmitComment : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ OutBuffer* buf;
+ Scope* sc;
+
+ extern (D) this(ref OutBuffer buf, Scope* sc)
+ {
+ this.buf = &buf;
+ this.sc = sc;
+ }
+
+ override void visit(Dsymbol)
+ {
+ }
+
+ override void visit(InvariantDeclaration)
+ {
+ }
+
+ override void visit(UnitTestDeclaration)
+ {
+ }
+
+ override void visit(PostBlitDeclaration)
+ {
+ }
+
+ override void visit(DtorDeclaration)
+ {
+ }
+
+ override void visit(StaticCtorDeclaration)
+ {
+ }
+
+ override void visit(StaticDtorDeclaration)
+ {
+ }
+
+ override void visit(TypeInfoDeclaration)
+ {
+ }
+
+ void emit(Scope* sc, Dsymbol s, const(char)* com)
+ {
+ if (s && sc.lastdc && isDitto(com))
+ {
+ sc.lastdc.a.push(s);
+ return;
+ }
+ // Put previous doc comment if exists
+ if (DocComment* dc = sc.lastdc)
+ {
+ assert(dc.a.dim > 0, "Expects at least one declaration for a" ~
+ "documentation comment");
+
+ auto symbol = dc.a[0];
+
+ buf.writestring("$(DDOC_MEMBER");
+ buf.writestring("$(DDOC_MEMBER_HEADER");
+ emitAnchor(*buf, symbol, sc, true);
+ buf.writeByte(')');
+
+ // Put the declaration signatures as the document 'title'
+ buf.writestring(ddoc_decl_s);
+ for (size_t i = 0; i < dc.a.dim; i++)
+ {
+ Dsymbol sx = dc.a[i];
+ // the added linebreaks in here make looking at multiple
+ // signatures more appealing
+ if (i == 0)
+ {
+ size_t o = buf.length;
+ toDocBuffer(sx, *buf, sc);
+ highlightCode(sc, sx, *buf, o);
+ buf.writestring("$(DDOC_OVERLOAD_SEPARATOR)");
+ continue;
+ }
+ buf.writestring("$(DDOC_DITTO ");
+ {
+ size_t o = buf.length;
+ toDocBuffer(sx, *buf, sc);
+ highlightCode(sc, sx, *buf, o);
+ }
+ buf.writestring("$(DDOC_OVERLOAD_SEPARATOR)");
+ buf.writeByte(')');
+ }
+ buf.writestring(ddoc_decl_e);
+ // Put the ddoc comment as the document 'description'
+ buf.writestring(ddoc_decl_dd_s);
+ {
+ dc.writeSections(sc, &dc.a, buf);
+ if (ScopeDsymbol sds = dc.a[0].isScopeDsymbol())
+ emitMemberComments(sds, *buf, sc);
+ }
+ buf.writestring(ddoc_decl_dd_e);
+ buf.writeByte(')');
+ //printf("buf.2 = [[%.*s]]\n", cast(int)(buf.length - o0), buf.data + o0);
+ }
+ if (s)
+ {
+ DocComment* dc = DocComment.parse(s, com);
+ dc.pmacrotable = &sc._module.macrotable;
+ sc.lastdc = dc;
+ }
+ }
+
+ override void visit(Import imp)
+ {
+ if (imp.visible().kind != Visibility.Kind.public_ && sc.visibility.kind != Visibility.Kind.export_)
+ return;
+
+ if (imp.comment)
+ emit(sc, imp, imp.comment);
+ }
+
+ override void visit(Declaration d)
+ {
+ //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", d, d.toChars(), d.comment);
+ //printf("type = %p\n", d.type);
+ const(char)* com = d.comment;
+ if (TemplateDeclaration td = getEponymousParent(d))
+ {
+ if (isDitto(td.comment))
+ com = td.comment;
+ else
+ com = Lexer.combineComments(td.comment.toDString(), com.toDString(), true);
+ }
+ else
+ {
+ if (!d.ident)
+ return;
+ if (!d.type)
+ {
+ if (!d.isCtorDeclaration() &&
+ !d.isAliasDeclaration() &&
+ !d.isVarDeclaration())
+ {
+ return;
+ }
+ }
+ if (d.visibility.kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
+ return;
+ }
+ if (!com)
+ return;
+ emit(sc, d, com);
+ }
+
+ override void visit(AggregateDeclaration ad)
+ {
+ //printf("AggregateDeclaration::emitComment() '%s'\n", ad.toChars());
+ const(char)* com = ad.comment;
+ if (TemplateDeclaration td = getEponymousParent(ad))
+ {
+ if (isDitto(td.comment))
+ com = td.comment;
+ else
+ com = Lexer.combineComments(td.comment.toDString(), com.toDString(), true);
+ }
+ else
+ {
+ if (ad.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
+ return;
+ if (!ad.comment)
+ return;
+ }
+ if (!com)
+ return;
+ emit(sc, ad, com);
+ }
+
+ override void visit(TemplateDeclaration td)
+ {
+ //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", td.toChars(), td.kind());
+ if (td.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
+ return;
+ if (!td.comment)
+ return;
+ if (Dsymbol ss = getEponymousMember(td))
+ {
+ ss.accept(this);
+ return;
+ }
+ emit(sc, td, td.comment);
+ }
+
+ override void visit(EnumDeclaration ed)
+ {
+ if (ed.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
+ return;
+ if (ed.isAnonymous() && ed.members)
+ {
+ for (size_t i = 0; i < ed.members.dim; i++)
+ {
+ Dsymbol s = (*ed.members)[i];
+ emitComment(s, *buf, sc);
+ }
+ return;
+ }
+ if (!ed.comment)
+ return;
+ if (ed.isAnonymous())
+ return;
+ emit(sc, ed, ed.comment);
+ }
+
+ override void visit(EnumMember em)
+ {
+ //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", em, em.toChars(), em.comment);
+ if (em.visible().kind == Visibility.Kind.private_ || sc.visibility.kind == Visibility.Kind.private_)
+ return;
+ if (!em.comment)
+ return;
+ emit(sc, em, em.comment);
+ }
+
+ override void visit(AttribDeclaration ad)
+ {
+ //printf("AttribDeclaration::emitComment(sc = %p)\n", sc);
+ /* A general problem with this,
+ * illustrated by https://issues.dlang.org/show_bug.cgi?id=2516
+ * is that attributes are not transmitted through to the underlying
+ * member declarations for template bodies, because semantic analysis
+ * is not done for template declaration bodies
+ * (only template instantiations).
+ * Hence, Ddoc omits attributes from template members.
+ */
+ Dsymbols* d = ad.include(null);
+ if (d)
+ {
+ for (size_t i = 0; i < d.dim; i++)
+ {
+ Dsymbol s = (*d)[i];
+ //printf("AttribDeclaration::emitComment %s\n", s.toChars());
+ emitComment(s, *buf, sc);
+ }
+ }
+ }
+
+ override void visit(VisibilityDeclaration pd)
+ {
+ if (pd.decl)
+ {
+ Scope* scx = sc;
+ sc = sc.copy();
+ sc.visibility = pd.visibility;
+ visit(cast(AttribDeclaration)pd);
+ scx.lastdc = sc.lastdc;
+ sc = sc.pop();
+ }
+ }
+
+ override void visit(ConditionalDeclaration cd)
+ {
+ //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc);
+ if (cd.condition.inc != Include.notComputed)
+ {
+ visit(cast(AttribDeclaration)cd);
+ return;
+ }
+ /* If generating doc comment, be careful because if we're inside
+ * a template, then include(null) will fail.
+ */
+ Dsymbols* d = cd.decl ? cd.decl : cd.elsedecl;
+ for (size_t i = 0; i < d.dim; i++)
+ {
+ Dsymbol s = (*d)[i];
+ emitComment(s, *buf, sc);
+ }
+ }
+ }
+
+ scope EmitComment v = new EmitComment(buf, sc);
+ if (!s)
+ v.emit(sc, null, null);
+ else
+ s.accept(v);
+}
+
+private void toDocBuffer(Dsymbol s, ref OutBuffer buf, Scope* sc)
+{
+ extern (C++) final class ToDocBuffer : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ OutBuffer* buf;
+ Scope* sc;
+
+ extern (D) this(ref OutBuffer buf, Scope* sc)
+ {
+ this.buf = &buf;
+ this.sc = sc;
+ }
+
+ override void visit(Dsymbol s)
+ {
+ //printf("Dsymbol::toDocbuffer() %s\n", s.toChars());
+ HdrGenState hgs;
+ hgs.ddoc = true;
+ .toCBuffer(s, buf, &hgs);
+ }
+
+ void prefix(Dsymbol s)
+ {
+ if (s.isDeprecated())
+ buf.writestring("deprecated ");
+ if (Declaration d = s.isDeclaration())
+ {
+ emitVisibility(*buf, d);
+ if (d.isStatic())
+ buf.writestring("static ");
+ else if (d.isFinal())
+ buf.writestring("final ");
+ else if (d.isAbstract())
+ buf.writestring("abstract ");
+
+ if (d.isFuncDeclaration()) // functionToBufferFull handles this
+ return;
+
+ if (d.isImmutable())
+ buf.writestring("immutable ");
+ if (d.storage_class & STC.shared_)
+ buf.writestring("shared ");
+ if (d.isWild())
+ buf.writestring("inout ");
+ if (d.isConst())
+ buf.writestring("const ");
+
+ if (d.isSynchronized())
+ buf.writestring("synchronized ");
+
+ if (d.storage_class & STC.manifest)
+ buf.writestring("enum ");
+
+ // Add "auto" for the untyped variable in template members
+ if (!d.type && d.isVarDeclaration() &&
+ !d.isImmutable() && !(d.storage_class & STC.shared_) && !d.isWild() && !d.isConst() &&
+ !d.isSynchronized())
+ {
+ buf.writestring("auto ");
+ }
+ }
+ }
+
+ override void visit(Import i)
+ {
+ HdrGenState hgs;
+ hgs.ddoc = true;
+ emitVisibility(*buf, i);
+ .toCBuffer(i, buf, &hgs);
+ }
+
+ override void visit(Declaration d)
+ {
+ if (!d.ident)
+ return;
+ TemplateDeclaration td = getEponymousParent(d);
+ //printf("Declaration::toDocbuffer() %s, originalType = %s, td = %s\n", d.toChars(), d.originalType ? d.originalType.toChars() : "--", td ? td.toChars() : "--");
+ HdrGenState hgs;
+ hgs.ddoc = true;
+ if (d.isDeprecated())
+ buf.writestring("$(DEPRECATED ");
+ prefix(d);
+ if (d.type)
+ {
+ Type origType = d.originalType ? d.originalType : d.type;
+ if (origType.ty == Tfunction)
+ {
+ functionToBufferFull(cast(TypeFunction)origType, buf, d.ident, &hgs, td);
+ }
+ else
+ .toCBuffer(origType, buf, d.ident, &hgs);
+ }
+ else
+ buf.writestring(d.ident.toString());
+ if (d.isVarDeclaration() && td)
+ {
+ buf.writeByte('(');
+ if (td.origParameters && td.origParameters.dim)
+ {
+ for (size_t i = 0; i < td.origParameters.dim; i++)
+ {
+ if (i)
+ buf.writestring(", ");
+ toCBuffer((*td.origParameters)[i], buf, &hgs);
+ }
+ }
+ buf.writeByte(')');
+ }
+ // emit constraints if declaration is a templated declaration
+ if (td && td.constraint)
+ {
+ bool noFuncDecl = td.isFuncDeclaration() is null;
+ if (noFuncDecl)
+ {
+ buf.writestring("$(DDOC_CONSTRAINT ");
+ }
+
+ .toCBuffer(td.constraint, buf, &hgs);
+
+ if (noFuncDecl)
+ {
+ buf.writestring(")");
+ }
+ }
+ if (d.isDeprecated())
+ buf.writestring(")");
+ buf.writestring(";\n");
+ }
+
+ override void visit(AliasDeclaration ad)
+ {
+ //printf("AliasDeclaration::toDocbuffer() %s\n", ad.toChars());
+ if (!ad.ident)
+ return;
+ if (ad.isDeprecated())
+ buf.writestring("deprecated ");
+ emitVisibility(*buf, ad);
+ buf.printf("alias %s = ", ad.toChars());
+ if (Dsymbol s = ad.aliassym) // ident alias
+ {
+ prettyPrintDsymbol(s, ad.parent);
+ }
+ else if (Type type = ad.getType()) // type alias
+ {
+ if (type.ty == Tclass || type.ty == Tstruct || type.ty == Tenum)
+ {
+ if (Dsymbol s = type.toDsymbol(null)) // elaborate type
+ prettyPrintDsymbol(s, ad.parent);
+ else
+ buf.writestring(type.toChars());
+ }
+ else
+ {
+ // simple type
+ buf.writestring(type.toChars());
+ }
+ }
+ buf.writestring(";\n");
+ }
+
+ void parentToBuffer(Dsymbol s)
+ {
+ if (s && !s.isPackage() && !s.isModule())
+ {
+ parentToBuffer(s.parent);
+ buf.writestring(s.toChars());
+ buf.writestring(".");
+ }
+ }
+
+ static bool inSameModule(Dsymbol s, Dsymbol p)
+ {
+ for (; s; s = s.parent)
+ {
+ if (s.isModule())
+ break;
+ }
+ for (; p; p = p.parent)
+ {
+ if (p.isModule())
+ break;
+ }
+ return s == p;
+ }
+
+ void prettyPrintDsymbol(Dsymbol s, Dsymbol parent)
+ {
+ if (s.parent && (s.parent == parent)) // in current scope -> naked name
+ {
+ buf.writestring(s.toChars());
+ }
+ else if (!inSameModule(s, parent)) // in another module -> full name
+ {
+ buf.writestring(s.toPrettyChars());
+ }
+ else // nested in a type in this module -> full name w/o module name
+ {
+ // if alias is nested in a user-type use module-scope lookup
+ if (!parent.isModule() && !parent.isPackage())
+ buf.writestring(".");
+ parentToBuffer(s.parent);
+ buf.writestring(s.toChars());
+ }
+ }
+
+ override void visit(AggregateDeclaration ad)
+ {
+ if (!ad.ident)
+ return;
+ version (none)
+ {
+ emitVisibility(buf, ad);
+ }
+ buf.printf("%s %s", ad.kind(), ad.toChars());
+ buf.writestring(";\n");
+ }
+
+ override void visit(StructDeclaration sd)
+ {
+ //printf("StructDeclaration::toDocbuffer() %s\n", sd.toChars());
+ if (!sd.ident)
+ return;
+ version (none)
+ {
+ emitVisibility(buf, sd);
+ }
+ if (TemplateDeclaration td = getEponymousParent(sd))
+ {
+ toDocBuffer(td, *buf, sc);
+ }
+ else
+ {
+ buf.printf("%s %s", sd.kind(), sd.toChars());
+ }
+ buf.writestring(";\n");
+ }
+
+ override void visit(ClassDeclaration cd)
+ {
+ //printf("ClassDeclaration::toDocbuffer() %s\n", cd.toChars());
+ if (!cd.ident)
+ return;
+ version (none)
+ {
+ emitVisibility(*buf, cd);
+ }
+ if (TemplateDeclaration td = getEponymousParent(cd))
+ {
+ toDocBuffer(td, *buf, sc);
+ }
+ else
+ {
+ if (!cd.isInterfaceDeclaration() && cd.isAbstract())
+ buf.writestring("abstract ");
+ buf.printf("%s %s", cd.kind(), cd.toChars());
+ }
+ int any = 0;
+ for (size_t i = 0; i < cd.baseclasses.dim; i++)
+ {
+ BaseClass* bc = (*cd.baseclasses)[i];
+ if (bc.sym && bc.sym.ident == Id.Object)
+ continue;
+ if (any)
+ buf.writestring(", ");
+ else
+ {
+ buf.writestring(": ");
+ any = 1;
+ }
+
+ if (bc.sym)
+ {
+ buf.printf("$(DDOC_PSUPER_SYMBOL %s)", bc.sym.toPrettyChars());
+ }
+ else
+ {
+ HdrGenState hgs;
+ .toCBuffer(bc.type, buf, null, &hgs);
+ }
+ }
+ buf.writestring(";\n");
+ }
+
+ override void visit(EnumDeclaration ed)
+ {
+ if (!ed.ident)
+ return;
+ buf.printf("%s %s", ed.kind(), ed.toChars());
+ if (ed.memtype)
+ {
+ buf.writestring(": $(DDOC_ENUM_BASETYPE ");
+ HdrGenState hgs;
+ .toCBuffer(ed.memtype, buf, null, &hgs);
+ buf.writestring(")");
+ }
+ buf.writestring(";\n");
+ }
+
+ override void visit(EnumMember em)
+ {
+ if (!em.ident)
+ return;
+ buf.writestring(em.toChars());
+ }
+ }
+
+ scope ToDocBuffer v = new ToDocBuffer(buf, sc);
+ s.accept(v);
+}
+
+/***********************************************************
+ */
+struct DocComment
+{
+ Sections sections; // Section*[]
+ Section summary;
+ Section copyright;
+ Section macros;
+ MacroTable* pmacrotable;
+ Escape* escapetable;
+ Dsymbols a;
+
+ static DocComment* parse(Dsymbol s, const(char)* comment)
+ {
+ //printf("parse(%s): '%s'\n", s.toChars(), comment);
+ auto dc = new DocComment();
+ dc.a.push(s);
+ if (!comment)
+ return dc;
+ dc.parseSections(comment);
+ for (size_t i = 0; i < dc.sections.dim; i++)
+ {
+ Section sec = dc.sections[i];
+ if (iequals("copyright", sec.name))
+ {
+ dc.copyright = sec;
+ }
+ if (iequals("macros", sec.name))
+ {
+ dc.macros = sec;
+ }
+ }
+ return dc;
+ }
+
+ /************************************************
+ * Parse macros out of Macros: section.
+ * Macros are of the form:
+ * name1 = value1
+ *
+ * name2 = value2
+ */
+ extern(D) static void parseMacros(
+ Escape* escapetable, ref MacroTable pmacrotable, const(char)[] m)
+ {
+ const(char)* p = m.ptr;
+ size_t len = m.length;
+ const(char)* pend = p + len;
+ const(char)* tempstart = null;
+ size_t templen = 0;
+ const(char)* namestart = null;
+ size_t namelen = 0; // !=0 if line continuation
+ const(char)* textstart = null;
+ size_t textlen = 0;
+ while (p < pend)
+ {
+ // Skip to start of macro
+ while (1)
+ {
+ if (p >= pend)
+ goto Ldone;
+ switch (*p)
+ {
+ case ' ':
+ case '\t':
+ p++;
+ continue;
+ case '\r':
+ case '\n':
+ p++;
+ goto Lcont;
+ default:
+ if (isIdStart(p))
+ break;
+ if (namelen)
+ goto Ltext; // continuation of prev macro
+ goto Lskipline;
+ }
+ break;
+ }
+ tempstart = p;
+ while (1)
+ {
+ if (p >= pend)
+ goto Ldone;
+ if (!isIdTail(p))
+ break;
+ p += utfStride(p);
+ }
+ templen = p - tempstart;
+ while (1)
+ {
+ if (p >= pend)
+ goto Ldone;
+ if (!(*p == ' ' || *p == '\t'))
+ break;
+ p++;
+ }
+ if (*p != '=')
+ {
+ if (namelen)
+ goto Ltext; // continuation of prev macro
+ goto Lskipline;
+ }
+ p++;
+ if (p >= pend)
+ goto Ldone;
+ if (namelen)
+ {
+ // Output existing macro
+ L1:
+ //printf("macro '%.*s' = '%.*s'\n", cast(int)namelen, namestart, cast(int)textlen, textstart);
+ if (iequals("ESCAPES", namestart[0 .. namelen]))
+ parseEscapes(escapetable, textstart[0 .. textlen]);
+ else
+ pmacrotable.define(namestart[0 .. namelen], textstart[0 .. textlen]);
+ namelen = 0;
+ if (p >= pend)
+ break;
+ }
+ namestart = tempstart;
+ namelen = templen;
+ while (p < pend && (*p == ' ' || *p == '\t'))
+ p++;
+ textstart = p;
+ Ltext:
+ while (p < pend && *p != '\r' && *p != '\n')
+ p++;
+ textlen = p - textstart;
+ p++;
+ //printf("p = %p, pend = %p\n", p, pend);
+ Lcont:
+ continue;
+ Lskipline:
+ // Ignore this line
+ while (p < pend && *p != '\r' && *p != '\n')
+ p++;
+ }
+ Ldone:
+ if (namelen)
+ goto L1; // write out last one
+ }
+
+ /**************************************
+ * Parse escapes of the form:
+ * /c/string/
+ * where c is a single character.
+ * Multiple escapes can be separated
+ * by whitespace and/or commas.
+ */
+ static void parseEscapes(Escape* escapetable, const(char)[] text)
+ {
+ if (!escapetable)
+ {
+ escapetable = new Escape();
+ memset(escapetable, 0, Escape.sizeof);
+ }
+ //printf("parseEscapes('%.*s') pescapetable = %p\n", cast(int)text.length, text.ptr, escapetable);
+ const(char)* p = text.ptr;
+ const(char)* pend = p + text.length;
+ while (1)
+ {
+ while (1)
+ {
+ if (p + 4 >= pend)
+ return;
+ if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ','))
+ break;
+ p++;
+ }
+ if (p[0] != '/' || p[2] != '/')
+ return;
+ char c = p[1];
+ p += 3;
+ const(char)* start = p;
+ while (1)
+ {
+ if (p >= pend)
+ return;
+ if (*p == '/')
+ break;
+ p++;
+ }
+ size_t len = p - start;
+ char* s = cast(char*)memcpy(mem.xmalloc(len + 1), start, len);
+ s[len] = 0;
+ escapetable.strings[c] = s[0 .. len];
+ //printf("\t%c = '%s'\n", c, s);
+ p++;
+ }
+ }
+
+ /*****************************************
+ * Parse next paragraph out of *pcomment.
+ * Update *pcomment to point past paragraph.
+ * Returns NULL if no more paragraphs.
+ * If paragraph ends in 'identifier:',
+ * then (*pcomment)[0 .. idlen] is the identifier.
+ */
+ void parseSections(const(char)* comment)
+ {
+ const(char)* p;
+ const(char)* pstart;
+ const(char)* pend;
+ const(char)* idstart = null; // dead-store to prevent spurious warning
+ size_t idlen;
+ const(char)* name = null;
+ size_t namelen = 0;
+ //printf("parseSections('%s')\n", comment);
+ p = comment;
+ while (*p)
+ {
+ const(char)* pstart0 = p;
+ p = skipwhitespace(p);
+ pstart = p;
+ pend = p;
+
+ // Undo indent if starting with a list item
+ if ((*p == '-' || *p == '+' || *p == '*') && (*(p+1) == ' ' || *(p+1) == '\t'))
+ pstart = pstart0;
+ else
+ {
+ const(char)* pitem = p;
+ while (*pitem >= '0' && *pitem <= '9')
+ ++pitem;
+ if (pitem > p && *pitem == '.' && (*(pitem+1) == ' ' || *(pitem+1) == '\t'))
+ pstart = pstart0;
+ }
+
+ /* Find end of section, which is ended by one of:
+ * 'identifier:' (but not inside a code section)
+ * '\0'
+ */
+ idlen = 0;
+ int inCode = 0;
+ while (1)
+ {
+ // Check for start/end of a code section
+ if (*p == '-' || *p == '`' || *p == '~')
+ {
+ char c = *p;
+ int numdash = 0;
+ while (*p == c)
+ {
+ ++numdash;
+ p++;
+ }
+ // BUG: handle UTF PS and LS too
+ if ((!*p || *p == '\r' || *p == '\n' || (!inCode && c != '-')) && numdash >= 3)
+ {
+ inCode = inCode == c ? false : c;
+ if (inCode)
+ {
+ // restore leading indentation
+ while (pstart0 < pstart && isIndentWS(pstart - 1))
+ --pstart;
+ }
+ }
+ pend = p;
+ }
+ if (!inCode && isIdStart(p))
+ {
+ const(char)* q = p + utfStride(p);
+ while (isIdTail(q))
+ q += utfStride(q);
+
+ // Detected tag ends it
+ if (*q == ':' && isupper(*p)
+ && (isspace(q[1]) || q[1] == 0))
+ {
+ idlen = q - p;
+ idstart = p;
+ for (pend = p; pend > pstart; pend--)
+ {
+ if (pend[-1] == '\n')
+ break;
+ }
+ p = q + 1;
+ break;
+ }
+ }
+ while (1)
+ {
+ if (!*p)
+ goto L1;
+ if (*p == '\n')
+ {
+ p++;
+ if (*p == '\n' && !summary && !namelen && !inCode)
+ {
+ pend = p;
+ p++;
+ goto L1;
+ }
+ break;
+ }
+ p++;
+ pend = p;
+ }
+ p = skipwhitespace(p);
+ }
+ L1:
+ if (namelen || pstart < pend)
+ {
+ Section s;
+ if (iequals("Params", name[0 .. namelen]))
+ s = new ParamSection();
+ else if (iequals("Macros", name[0 .. namelen]))
+ s = new MacroSection();
+ else
+ s = new Section();
+ s.name = name[0 .. namelen];
+ s.body_ = pstart[0 .. pend - pstart];
+ s.nooutput = 0;
+ //printf("Section: '%.*s' = '%.*s'\n", cast(int)s.namelen, s.name, cast(int)s.bodylen, s.body);
+ sections.push(s);
+ if (!summary && !namelen)
+ summary = s;
+ }
+ if (idlen)
+ {
+ name = idstart;
+ namelen = idlen;
+ }
+ else
+ {
+ name = null;
+ namelen = 0;
+ if (!*p)
+ break;
+ }
+ }
+ }
+
+ void writeSections(Scope* sc, Dsymbols* a, OutBuffer* buf)
+ {
+ assert(a.dim);
+ //printf("DocComment::writeSections()\n");
+ Loc loc = (*a)[0].loc;
+ if (Module m = (*a)[0].isModule())
+ {
+ if (m.md)
+ loc = m.md.loc;
+ }
+ size_t offset1 = buf.length;
+ buf.writestring("$(DDOC_SECTIONS ");
+ size_t offset2 = buf.length;
+ for (size_t i = 0; i < sections.dim; i++)
+ {
+ Section sec = sections[i];
+ if (sec.nooutput)
+ continue;
+ //printf("Section: '%.*s' = '%.*s'\n", cast(int)sec.namelen, sec.name, cast(int)sec.bodylen, sec.body);
+ if (!sec.name.length && i == 0)
+ {
+ buf.writestring("$(DDOC_SUMMARY ");
+ size_t o = buf.length;
+ buf.write(sec.body_);
+ escapeStrayParenthesis(loc, buf, o, true);
+ highlightText(sc, a, loc, *buf, o);
+ buf.writestring(")");
+ }
+ else
+ sec.write(loc, &this, sc, a, buf);
+ }
+ for (size_t i = 0; i < a.dim; i++)
+ {
+ Dsymbol s = (*a)[i];
+ if (Dsymbol td = getEponymousParent(s))
+ s = td;
+ for (UnitTestDeclaration utd = s.ddocUnittest; utd; utd = utd.ddocUnittest)
+ {
+ if (utd.visibility.kind == Visibility.Kind.private_ || !utd.comment || !utd.fbody)
+ continue;
+ // Strip whitespaces to avoid showing empty summary
+ const(char)* c = utd.comment;
+ while (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r')
+ ++c;
+ buf.writestring("$(DDOC_EXAMPLES ");
+ size_t o = buf.length;
+ buf.writestring(cast(char*)c);
+ if (utd.codedoc)
+ {
+ auto codedoc = utd.codedoc.stripLeadingNewlines;
+ size_t n = getCodeIndent(codedoc);
+ while (n--)
+ buf.writeByte(' ');
+ buf.writestring("----\n");
+ buf.writestring(codedoc);
+ buf.writestring("----\n");
+ highlightText(sc, a, loc, *buf, o);
+ }
+ buf.writestring(")");
+ }
+ }
+ if (buf.length == offset2)
+ {
+ /* Didn't write out any sections, so back out last write
+ */
+ buf.setsize(offset1);
+ buf.writestring("\n");
+ }
+ else
+ buf.writestring(")");
+ }
+}
+
+/*****************************************
+ * Return true if comment consists entirely of "ditto".
+ */
+private bool isDitto(const(char)* comment)
+{
+ if (comment)
+ {
+ const(char)* p = skipwhitespace(comment);
+ if (Port.memicmp(p, "ditto", 5) == 0 && *skipwhitespace(p + 5) == 0)
+ return true;
+ }
+ return false;
+}
+
+/**********************************************
+ * Skip white space.
+ */
+private const(char)* skipwhitespace(const(char)* p)
+{
+ return skipwhitespace(p.toDString).ptr;
+}
+
+/// Ditto
+private const(char)[] skipwhitespace(const(char)[] p)
+{
+ foreach (idx, char c; p)
+ {
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ continue;
+ default:
+ return p[idx .. $];
+ }
+ }
+ return p[$ .. $];
+}
+
+/************************************************
+ * Scan past all instances of the given characters.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` to start scanning from
+ * chars = the characters to skip; order is unimportant
+ * Returns: the index after skipping characters.
+ */
+private size_t skipChars(ref OutBuffer buf, size_t i, string chars)
+{
+ Outer:
+ foreach (j, c; buf[][i..$])
+ {
+ foreach (d; chars)
+ {
+ if (d == c)
+ continue Outer;
+ }
+ return i + j;
+ }
+ return buf.length;
+}
+
+unittest {
+ OutBuffer buf;
+ string data = "test ---\r\n\r\nend";
+ buf.write(data);
+
+ assert(skipChars(buf, 0, "-") == 0);
+ assert(skipChars(buf, 4, "-") == 4);
+ assert(skipChars(buf, 4, " -") == 8);
+ assert(skipChars(buf, 8, "\r\n") == 12);
+ assert(skipChars(buf, 12, "dne") == 15);
+}
+
+/****************************************************
+ * Replace all instances of `c` with `r` in the given string
+ * Params:
+ * s = the string to do replacements in
+ * c = the character to look for
+ * r = the string to replace `c` with
+ * Returns: `s` with `c` replaced with `r`
+ */
+private inout(char)[] replaceChar(inout(char)[] s, char c, string r) pure
+{
+ int count = 0;
+ foreach (char sc; s)
+ if (sc == c)
+ ++count;
+ if (count == 0)
+ return s;
+
+ char[] result;
+ result.reserve(s.length - count + (r.length * count));
+ size_t start = 0;
+ foreach (i, char sc; s)
+ {
+ if (sc == c)
+ {
+ result ~= s[start..i];
+ result ~= r;
+ start = i+1;
+ }
+ }
+ result ~= s[start..$];
+ return result;
+}
+
+///
+unittest
+{
+ assert("".replaceChar(',', "$(COMMA)") == "");
+ assert("ab".replaceChar(',', "$(COMMA)") == "ab");
+ assert("a,b".replaceChar(',', "$(COMMA)") == "a$(COMMA)b");
+ assert("a,,b".replaceChar(',', "$(COMMA)") == "a$(COMMA)$(COMMA)b");
+ assert(",ab".replaceChar(',', "$(COMMA)") == "$(COMMA)ab");
+ assert("ab,".replaceChar(',', "$(COMMA)") == "ab$(COMMA)");
+}
+
+/**
+ * Return a lowercased copy of a string.
+ * Params:
+ * s = the string to lowercase
+ * Returns: the lowercase version of the string or the original if already lowercase
+ */
+private string toLowercase(string s) pure
+{
+ string lower;
+ foreach (size_t i; 0..s.length)
+ {
+ char c = s[i];
+// TODO: maybe unicode lowercase, somehow
+ if (c >= 'A' && c <= 'Z')
+ {
+ if (!lower.length) {
+ lower.reserve(s.length);
+ }
+ lower ~= s[lower.length..i];
+ c += 'a' - 'A';
+ lower ~= c;
+ }
+ }
+ if (lower.length)
+ lower ~= s[lower.length..$];
+ else
+ lower = s;
+ return lower;
+}
+
+///
+unittest
+{
+ assert("".toLowercase == "");
+ assert("abc".toLowercase == "abc");
+ assert("ABC".toLowercase == "abc");
+ assert("aBc".toLowercase == "abc");
+}
+
+/************************************************
+ * Get the indent from one index to another, counting tab stops as four spaces wide
+ * per the Markdown spec.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * from = the index within `buf` to start counting from, inclusive
+ * to = the index within `buf` to stop counting at, exclusive
+ * Returns: the indent
+ */
+private int getMarkdownIndent(ref OutBuffer buf, size_t from, size_t to)
+{
+ const slice = buf[];
+ if (to > slice.length)
+ to = slice.length;
+ int indent = 0;
+ foreach (const c; slice[from..to])
+ indent += (c == '\t') ? 4 - (indent % 4) : 1;
+ return indent;
+}
+
+/************************************************
+ * Scan forward to one of:
+ * start of identifier
+ * beginning of next line
+ * end of buf
+ */
+size_t skiptoident(ref OutBuffer buf, size_t i)
+{
+ const slice = buf[];
+ while (i < slice.length)
+ {
+ dchar c;
+ size_t oi = i;
+ if (utf_decodeChar(slice, i, c))
+ {
+ /* Ignore UTF errors, but still consume input
+ */
+ break;
+ }
+ if (c >= 0x80)
+ {
+ if (!isUniAlpha(c))
+ continue;
+ }
+ else if (!(isalpha(c) || c == '_' || c == '\n'))
+ continue;
+ i = oi;
+ break;
+ }
+ return i;
+}
+
+/************************************************
+ * Scan forward past end of identifier.
+ */
+private size_t skippastident(ref OutBuffer buf, size_t i)
+{
+ const slice = buf[];
+ while (i < slice.length)
+ {
+ dchar c;
+ size_t oi = i;
+ if (utf_decodeChar(slice, i, c))
+ {
+ /* Ignore UTF errors, but still consume input
+ */
+ break;
+ }
+ if (c >= 0x80)
+ {
+ if (isUniAlpha(c))
+ continue;
+ }
+ else if (isalnum(c) || c == '_')
+ continue;
+ i = oi;
+ break;
+ }
+ return i;
+}
+
+/************************************************
+ * Scan forward past end of an identifier that might
+ * contain dots (e.g. `abc.def`)
+ */
+private size_t skipPastIdentWithDots(ref OutBuffer buf, size_t i)
+{
+ const slice = buf[];
+ bool lastCharWasDot;
+ while (i < slice.length)
+ {
+ dchar c;
+ size_t oi = i;
+ if (utf_decodeChar(slice, i, c))
+ {
+ /* Ignore UTF errors, but still consume input
+ */
+ break;
+ }
+ if (c == '.')
+ {
+ // We need to distinguish between `abc.def`, abc..def`, and `abc.`
+ // Only `abc.def` is a valid identifier
+
+ if (lastCharWasDot)
+ {
+ i = oi;
+ break;
+ }
+
+ lastCharWasDot = true;
+ continue;
+ }
+ else
+ {
+ if (c >= 0x80)
+ {
+ if (isUniAlpha(c))
+ {
+ lastCharWasDot = false;
+ continue;
+ }
+ }
+ else if (isalnum(c) || c == '_')
+ {
+ lastCharWasDot = false;
+ continue;
+ }
+ i = oi;
+ break;
+ }
+ }
+
+ // if `abc.`
+ if (lastCharWasDot)
+ return i - 1;
+
+ return i;
+}
+
+/************************************************
+ * Scan forward past URL starting at i.
+ * We don't want to highlight parts of a URL.
+ * Returns:
+ * i if not a URL
+ * index just past it if it is a URL
+ */
+private size_t skippastURL(ref OutBuffer buf, size_t i)
+{
+ const slice = buf[][i .. $];
+ size_t j;
+ bool sawdot = false;
+ if (slice.length > 7 && Port.memicmp(slice.ptr, "http://", 7) == 0)
+ {
+ j = 7;
+ }
+ else if (slice.length > 8 && Port.memicmp(slice.ptr, "https://", 8) == 0)
+ {
+ j = 8;
+ }
+ else
+ goto Lno;
+ for (; j < slice.length; j++)
+ {
+ const c = slice[j];
+ if (isalnum(c))
+ continue;
+ if (c == '-' || c == '_' || c == '?' || c == '=' || c == '%' ||
+ c == '&' || c == '/' || c == '+' || c == '#' || c == '~')
+ continue;
+ if (c == '.')
+ {
+ sawdot = true;
+ continue;
+ }
+ break;
+ }
+ if (sawdot)
+ return i + j;
+Lno:
+ return i;
+}
+
+/****************************************************
+ * Remove a previously-inserted blank line macro.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iAt = the index within `buf` of the start of the `$(DDOC_BLANKLINE)`
+ * macro. Upon function return its value is set to `0`.
+ * i = an index within `buf`. If `i` is after `iAt` then it gets
+ * reduced by the length of the removed macro.
+ */
+private void removeBlankLineMacro(ref OutBuffer buf, ref size_t iAt, ref size_t i)
+{
+ if (!iAt)
+ return;
+
+ enum macroLength = "$(DDOC_BLANKLINE)".length;
+ buf.remove(iAt, macroLength);
+ if (i > iAt)
+ i -= macroLength;
+ iAt = 0;
+}
+
+/****************************************************
+ * Attempt to detect and replace a Markdown thematic break (HR). These are three
+ * or more of the same delimiter, optionally with spaces or tabs between any of
+ * them, e.g. `\n- - -\n` becomes `\n$(HR)\n`
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` of the first character of a potential
+ * thematic break. If the replacement is made `i` changes to
+ * point to the closing parenthesis of the `$(HR)` macro.
+ * iLineStart = the index within `buf` that the thematic break's line starts at
+ * loc = the current location within the file
+ * Returns: whether a thematic break was replaced
+ */
+private bool replaceMarkdownThematicBreak(ref OutBuffer buf, ref size_t i, size_t iLineStart, const ref Loc loc)
+{
+ if (!global.params.markdown)
+ return false;
+
+ const slice = buf[];
+ const c = buf[i];
+ size_t j = i + 1;
+ int repeat = 1;
+ for (; j < slice.length; j++)
+ {
+ if (buf[j] == c)
+ ++repeat;
+ else if (buf[j] != ' ' && buf[j] != '\t')
+ break;
+ }
+ if (repeat >= 3)
+ {
+ if (j >= buf.length || buf[j] == '\n' || buf[j] == '\r')
+ {
+ if (global.params.vmarkdown)
+ {
+ const s = buf[][i..j];
+ message(loc, "Ddoc: converted '%.*s' to a thematic break", cast(int)s.length, s.ptr);
+ }
+
+ buf.remove(iLineStart, j - iLineStart);
+ i = buf.insert(iLineStart, "$(HR)") - 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+/****************************************************
+ * Detect the level of an ATX-style heading, e.g. `## This is a heading` would
+ * have a level of `2`.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` of the first `#` character
+ * Returns:
+ * the detected heading level from 1 to 6, or
+ * 0 if not at an ATX heading
+ */
+private int detectAtxHeadingLevel(ref OutBuffer buf, const size_t i)
+{
+ if (!global.params.markdown)
+ return 0;
+
+ const iHeadingStart = i;
+ const iAfterHashes = skipChars(buf, i, "#");
+ const headingLevel = cast(int) (iAfterHashes - iHeadingStart);
+ if (headingLevel > 6)
+ return 0;
+
+ const iTextStart = skipChars(buf, iAfterHashes, " \t");
+ const emptyHeading = buf[iTextStart] == '\r' || buf[iTextStart] == '\n';
+
+ // require whitespace
+ if (!emptyHeading && iTextStart == iAfterHashes)
+ return 0;
+
+ return headingLevel;
+}
+
+/****************************************************
+ * Remove any trailing `##` suffix from an ATX-style heading.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` to start looking for a suffix at
+ */
+private void removeAnyAtxHeadingSuffix(ref OutBuffer buf, size_t i)
+{
+ size_t j = i;
+ size_t iSuffixStart = 0;
+ size_t iWhitespaceStart = j;
+ const slice = buf[];
+ for (; j < slice.length; j++)
+ {
+ switch (slice[j])
+ {
+ case '#':
+ if (iWhitespaceStart && !iSuffixStart)
+ iSuffixStart = j;
+ continue;
+ case ' ':
+ case '\t':
+ if (!iWhitespaceStart)
+ iWhitespaceStart = j;
+ continue;
+ case '\r':
+ case '\n':
+ break;
+ default:
+ iSuffixStart = 0;
+ iWhitespaceStart = 0;
+ continue;
+ }
+ break;
+ }
+ if (iSuffixStart)
+ buf.remove(iWhitespaceStart, j - iWhitespaceStart);
+}
+
+/****************************************************
+ * Wrap text in a Markdown heading macro, e.g. `$(H2 heading text`).
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iStart = the index within `buf` that the Markdown heading starts at
+ * iEnd = the index within `buf` of the character after the last
+ * heading character. Is incremented by the length of the
+ * inserted heading macro when this function ends.
+ * loc = the location of the Ddoc within the file
+ * headingLevel = the level (1-6) of heading to end. Is set to `0` when this
+ * function ends.
+ */
+private void endMarkdownHeading(ref OutBuffer buf, size_t iStart, ref size_t iEnd, const ref Loc loc, ref int headingLevel)
+{
+ if (!global.params.markdown)
+ return;
+ if (global.params.vmarkdown)
+ {
+ const s = buf[][iStart..iEnd];
+ message(loc, "Ddoc: added heading '%.*s'", cast(int)s.length, s.ptr);
+ }
+
+ char[5] heading = "$(H0 ";
+ heading[3] = cast(char) ('0' + headingLevel);
+ buf.insert(iStart, heading);
+ iEnd += 5;
+ size_t iBeforeNewline = iEnd;
+ while (buf[iBeforeNewline-1] == '\r' || buf[iBeforeNewline-1] == '\n')
+ --iBeforeNewline;
+ buf.insert(iBeforeNewline, ")");
+ headingLevel = 0;
+}
+
+/****************************************************
+ * End all nested Markdown quotes, if inside any.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` of the character after the quote text.
+ * quoteLevel = the current quote level. Is set to `0` when this function ends.
+ * Returns: the amount that `i` was moved
+ */
+private size_t endAllMarkdownQuotes(ref OutBuffer buf, size_t i, ref int quoteLevel)
+{
+ const length = quoteLevel;
+ for (; quoteLevel > 0; --quoteLevel)
+ i = buf.insert(i, ")");
+ return length;
+}
+
+/****************************************************
+ * Convenience function to end all Markdown lists and quotes, if inside any, and
+ * set `quoteMacroLevel` to `0`.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` of the character after the list and/or
+ * quote text. Is adjusted when this function ends if any lists
+ * and/or quotes were ended.
+ * nestedLists = a set of nested lists. Upon return it will be empty.
+ * quoteLevel = the current quote level. Is set to `0` when this function ends.
+ * quoteMacroLevel = the macro level that the quote was started at. Is set to
+ * `0` when this function ends.
+ * Returns: the amount that `i` was moved
+ */
+private size_t endAllListsAndQuotes(ref OutBuffer buf, ref size_t i, ref MarkdownList[] nestedLists, ref int quoteLevel, out int quoteMacroLevel)
+{
+ quoteMacroLevel = 0;
+ const i0 = i;
+ i += MarkdownList.endAllNestedLists(buf, i, nestedLists);
+ i += endAllMarkdownQuotes(buf, i, quoteLevel);
+ return i - i0;
+}
+
+/****************************************************
+ * Replace Markdown emphasis with the appropriate macro,
+ * e.g. `*very* **nice**` becomes `$(EM very) $(STRONG nice)`.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * loc = the current location within the file
+ * inlineDelimiters = the collection of delimiters found within a paragraph. When this function returns its length will be reduced to `downToLevel`.
+ * downToLevel = the length within `inlineDelimiters`` to reduce emphasis to
+ * Returns: the number of characters added to the buffer by the replacements
+ */
+private size_t replaceMarkdownEmphasis(ref OutBuffer buf, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int downToLevel = 0)
+{
+ if (!global.params.markdown)
+ return 0;
+
+ size_t replaceEmphasisPair(ref MarkdownDelimiter start, ref MarkdownDelimiter end)
+ {
+ immutable count = start.count == 1 || end.count == 1 ? 1 : 2;
+
+ size_t iStart = start.iStart;
+ size_t iEnd = end.iStart;
+ end.count -= count;
+ start.count -= count;
+ iStart += start.count;
+
+ if (!start.count)
+ start.type = 0;
+ if (!end.count)
+ end.type = 0;
+
+ if (global.params.vmarkdown)
+ {
+ const s = buf[][iStart + count..iEnd];
+ message(loc, "Ddoc: emphasized text '%.*s'", cast(int)s.length, s.ptr);
+ }
+
+ buf.remove(iStart, count);
+ iEnd -= count;
+ buf.remove(iEnd, count);
+
+ string macroName = count >= 2 ? "$(STRONG " : "$(EM ";
+ buf.insert(iEnd, ")");
+ buf.insert(iStart, macroName);
+
+ const delta = 1 + macroName.length - (count + count);
+ end.iStart += count;
+ return delta;
+ }
+
+ size_t delta = 0;
+ int start = (cast(int) inlineDelimiters.length) - 1;
+ while (start >= downToLevel)
+ {
+ // find start emphasis
+ while (start >= downToLevel &&
+ (inlineDelimiters[start].type != '*' || !inlineDelimiters[start].leftFlanking))
+ --start;
+ if (start < downToLevel)
+ break;
+
+ // find the nearest end emphasis
+ int end = start + 1;
+ while (end < inlineDelimiters.length &&
+ (inlineDelimiters[end].type != inlineDelimiters[start].type ||
+ inlineDelimiters[end].macroLevel != inlineDelimiters[start].macroLevel ||
+ !inlineDelimiters[end].rightFlanking))
+ ++end;
+ if (end == inlineDelimiters.length)
+ {
+ // the start emphasis has no matching end; if it isn't an end itself then kill it
+ if (!inlineDelimiters[start].rightFlanking)
+ inlineDelimiters[start].type = 0;
+ --start;
+ continue;
+ }
+
+ // multiple-of-3 rule
+ if (((inlineDelimiters[start].leftFlanking && inlineDelimiters[start].rightFlanking) ||
+ (inlineDelimiters[end].leftFlanking && inlineDelimiters[end].rightFlanking)) &&
+ (inlineDelimiters[start].count + inlineDelimiters[end].count) % 3 == 0)
+ {
+ --start;
+ continue;
+ }
+
+ immutable delta0 = replaceEmphasisPair(inlineDelimiters[start], inlineDelimiters[end]);
+
+ for (; end < inlineDelimiters.length; ++end)
+ inlineDelimiters[end].iStart += delta0;
+ delta += delta0;
+ }
+
+ inlineDelimiters.length = downToLevel;
+ return delta;
+}
+
+/****************************************************
+ */
+private bool isIdentifier(Dsymbols* a, const(char)* p, size_t len)
+{
+ foreach (member; *a)
+ {
+ if (auto imp = member.isImport())
+ {
+ // For example: `public import str = core.stdc.string;`
+ // This checks if `p` is equal to `str`
+ if (imp.aliasId)
+ {
+ if (p[0 .. len] == imp.aliasId.toString())
+ return true;
+ }
+ else
+ {
+ // The general case: `public import core.stdc.string;`
+
+ // fully qualify imports so `core.stdc.string` doesn't appear as `core`
+ string fullyQualifiedImport;
+ foreach (const pid; imp.packages)
+ {
+ fullyQualifiedImport ~= pid.toString() ~ ".";
+ }
+ fullyQualifiedImport ~= imp.id.toString();
+
+ // Check if `p` == `core.stdc.string`
+ if (p[0 .. len] == fullyQualifiedImport)
+ return true;
+ }
+ }
+ else if (member.ident)
+ {
+ if (p[0 .. len] == member.ident.toString())
+ return true;
+ }
+
+ }
+ return false;
+}
+
+/****************************************************
+ */
+private bool isKeyword(const(char)* p, size_t len)
+{
+ immutable string[3] table = ["true", "false", "null"];
+ foreach (s; table)
+ {
+ if (p[0 .. len] == s)
+ return true;
+ }
+ return false;
+}
+
+/****************************************************
+ */
+private TypeFunction isTypeFunction(Dsymbol s)
+{
+ FuncDeclaration f = s.isFuncDeclaration();
+ /* f.type may be NULL for template members.
+ */
+ if (f && f.type)
+ {
+ Type t = f.originalType ? f.originalType : f.type;
+ if (t.ty == Tfunction)
+ return cast(TypeFunction)t;
+ }
+ return null;
+}
+
+/****************************************************
+ */
+private Parameter isFunctionParameter(Dsymbol s, const(char)* p, size_t len)
+{
+ TypeFunction tf = isTypeFunction(s);
+ if (tf && tf.parameterList.parameters)
+ {
+ foreach (fparam; *tf.parameterList.parameters)
+ {
+ if (fparam.ident && p[0 .. len] == fparam.ident.toString())
+ {
+ return fparam;
+ }
+ }
+ }
+ return null;
+}
+
+/****************************************************
+ */
+private Parameter isFunctionParameter(Dsymbols* a, const(char)* p, size_t len)
+{
+ for (size_t i = 0; i < a.dim; i++)
+ {
+ Parameter fparam = isFunctionParameter((*a)[i], p, len);
+ if (fparam)
+ {
+ return fparam;
+ }
+ }
+ return null;
+}
+
+/****************************************************
+ */
+private Parameter isEponymousFunctionParameter(Dsymbols *a, const(char) *p, size_t len)
+{
+ for (size_t i = 0; i < a.dim; i++)
+ {
+ TemplateDeclaration td = (*a)[i].isTemplateDeclaration();
+ if (td && td.onemember)
+ {
+ /* Case 1: we refer to a template declaration inside the template
+
+ /// ...ddoc...
+ template case1(T) {
+ void case1(R)() {}
+ }
+ */
+ td = td.onemember.isTemplateDeclaration();
+ }
+ if (!td)
+ {
+ /* Case 2: we're an alias to a template declaration
+
+ /// ...ddoc...
+ alias case2 = case1!int;
+ */
+ AliasDeclaration ad = (*a)[i].isAliasDeclaration();
+ if (ad && ad.aliassym)
+ {
+ td = ad.aliassym.isTemplateDeclaration();
+ }
+ }
+ while (td)
+ {
+ Dsymbol sym = getEponymousMember(td);
+ if (sym)
+ {
+ Parameter fparam = isFunctionParameter(sym, p, len);
+ if (fparam)
+ {
+ return fparam;
+ }
+ }
+ td = td.overnext;
+ }
+ }
+ return null;
+}
+
+/****************************************************
+ */
+private TemplateParameter isTemplateParameter(Dsymbols* a, const(char)* p, size_t len)
+{
+ for (size_t i = 0; i < a.dim; i++)
+ {
+ TemplateDeclaration td = (*a)[i].isTemplateDeclaration();
+ // Check for the parent, if the current symbol is not a template declaration.
+ if (!td)
+ td = getEponymousParent((*a)[i]);
+ if (td && td.origParameters)
+ {
+ foreach (tp; *td.origParameters)
+ {
+ if (tp.ident && p[0 .. len] == tp.ident.toString())
+ {
+ return tp;
+ }
+ }
+ }
+ }
+ return null;
+}
+
+/****************************************************
+ * Return true if str is a reserved symbol name
+ * that starts with a double underscore.
+ */
+private bool isReservedName(const(char)[] str)
+{
+ immutable string[] table =
+ [
+ "__ctor",
+ "__dtor",
+ "__postblit",
+ "__invariant",
+ "__unitTest",
+ "__require",
+ "__ensure",
+ "__dollar",
+ "__ctfe",
+ "__withSym",
+ "__result",
+ "__returnLabel",
+ "__vptr",
+ "__monitor",
+ "__gate",
+ "__xopEquals",
+ "__xopCmp",
+ "__LINE__",
+ "__FILE__",
+ "__MODULE__",
+ "__FUNCTION__",
+ "__PRETTY_FUNCTION__",
+ "__DATE__",
+ "__TIME__",
+ "__TIMESTAMP__",
+ "__VENDOR__",
+ "__VERSION__",
+ "__EOF__",
+ "__CXXLIB__",
+ "__LOCAL_SIZE",
+ "__entrypoint",
+ ];
+ foreach (s; table)
+ {
+ if (str == s)
+ return true;
+ }
+ return false;
+}
+
+/****************************************************
+ * A delimiter for Markdown inline content like emphasis and links.
+ */
+private struct MarkdownDelimiter
+{
+ size_t iStart; /// the index where this delimiter starts
+ int count; /// the length of this delimeter's start sequence
+ int macroLevel; /// the count of nested DDoc macros when the delimiter is started
+ bool leftFlanking; /// whether the delimiter is left-flanking, as defined by the CommonMark spec
+ bool rightFlanking; /// whether the delimiter is right-flanking, as defined by the CommonMark spec
+ bool atParagraphStart; /// whether the delimiter is at the start of a paragraph
+ char type; /// the type of delimiter, defined by its starting character
+
+ /// whether this describes a valid delimiter
+ @property bool isValid() const { return count != 0; }
+
+ /// flag this delimiter as invalid
+ void invalidate() { count = 0; }
+}
+
+/****************************************************
+ * Info about a Markdown list.
+ */
+private struct MarkdownList
+{
+ string orderedStart; /// an optional start number--if present then the list starts at this number
+ size_t iStart; /// the index where the list item starts
+ size_t iContentStart; /// the index where the content starts after the list delimiter
+ int delimiterIndent; /// the level of indent the list delimiter starts at
+ int contentIndent; /// the level of indent the content starts at
+ int macroLevel; /// the count of nested DDoc macros when the list is started
+ char type; /// the type of list, defined by its starting character
+
+ /// whether this describes a valid list
+ @property bool isValid() const { return type != type.init; }
+
+ /****************************************************
+ * Try to parse a list item, returning whether successful.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iLineStart = the index within `buf` of the first character of the line
+ * i = the index within `buf` of the potential list item
+ * Returns: the parsed list item. Its `isValid` property describes whether parsing succeeded.
+ */
+ static MarkdownList parseItem(ref OutBuffer buf, size_t iLineStart, size_t i)
+ {
+ if (!global.params.markdown)
+ return MarkdownList();
+
+ if (buf[i] == '+' || buf[i] == '-' || buf[i] == '*')
+ return parseUnorderedListItem(buf, iLineStart, i);
+ else
+ return parseOrderedListItem(buf, iLineStart, i);
+ }
+
+ /****************************************************
+ * Return whether the context is at a list item of the same type as this list.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iLineStart = the index within `buf` of the first character of the line
+ * i = the index within `buf` of the list item
+ * Returns: whether `i` is at a list item of the same type as this list
+ */
+ private bool isAtItemInThisList(ref OutBuffer buf, size_t iLineStart, size_t i)
+ {
+ MarkdownList item = (type == '.' || type == ')') ?
+ parseOrderedListItem(buf, iLineStart, i) :
+ parseUnorderedListItem(buf, iLineStart, i);
+ if (item.type == type)
+ return item.delimiterIndent < contentIndent && item.contentIndent > delimiterIndent;
+ return false;
+ }
+
+ /****************************************************
+ * Start a Markdown list item by creating/deleting nested lists and starting the item.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iLineStart = the index within `buf` of the first character of the line. If this function succeeds it will be adjuested to equal `i`.
+ * i = the index within `buf` of the list item. If this function succeeds `i` will be adjusted to fit the inserted macro.
+ * iPrecedingBlankLine = the index within `buf` of the preceeding blank line. If non-zero and a new list was started, the preceeding blank line is removed and this value is set to `0`.
+ * nestedLists = a set of nested lists. If this function succeeds it may contain a new nested list.
+ * loc = the location of the Ddoc within the file
+ * Returns: `true` if a list was created
+ */
+ bool startItem(ref OutBuffer buf, ref size_t iLineStart, ref size_t i, ref size_t iPrecedingBlankLine, ref MarkdownList[] nestedLists, const ref Loc loc)
+ {
+ buf.remove(iStart, iContentStart - iStart);
+
+ if (!nestedLists.length ||
+ delimiterIndent >= nestedLists[$-1].contentIndent ||
+ buf[iLineStart - 4..iLineStart] == "$(LI")
+ {
+ // start a list macro
+ nestedLists ~= this;
+ if (type == '.')
+ {
+ if (orderedStart.length)
+ {
+ iStart = buf.insert(iStart, "$(OL_START ");
+ iStart = buf.insert(iStart, orderedStart);
+ iStart = buf.insert(iStart, ",\n");
+ }
+ else
+ iStart = buf.insert(iStart, "$(OL\n");
+ }
+ else
+ iStart = buf.insert(iStart, "$(UL\n");
+
+ removeBlankLineMacro(buf, iPrecedingBlankLine, iStart);
+ }
+ else if (nestedLists.length)
+ {
+ nestedLists[$-1].delimiterIndent = delimiterIndent;
+ nestedLists[$-1].contentIndent = contentIndent;
+ }
+
+ iStart = buf.insert(iStart, "$(LI\n");
+ i = iStart - 1;
+ iLineStart = i;
+
+ if (global.params.vmarkdown)
+ {
+ size_t iEnd = iStart;
+ while (iEnd < buf.length && buf[iEnd] != '\r' && buf[iEnd] != '\n')
+ ++iEnd;
+ const s = buf[][iStart..iEnd];
+ message(loc, "Ddoc: starting list item '%.*s'", cast(int)s.length, s.ptr);
+ }
+
+ return true;
+ }
+
+ /****************************************************
+ * End all nested Markdown lists.
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` to end lists at.
+ * nestedLists = a set of nested lists. Upon return it will be empty.
+ * Returns: the amount that `i` changed
+ */
+ static size_t endAllNestedLists(ref OutBuffer buf, size_t i, ref MarkdownList[] nestedLists)
+ {
+ const iStart = i;
+ for (; nestedLists.length; --nestedLists.length)
+ i = buf.insert(i, ")\n)");
+ return i - iStart;
+ }
+
+ /****************************************************
+ * Look for a sibling list item or the end of nested list(s).
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` to end lists at. If there was a sibling or ending lists `i` will be adjusted to fit the macro endings.
+ * iParagraphStart = the index within `buf` to start the next paragraph at at. May be adjusted upon return.
+ * nestedLists = a set of nested lists. Some nested lists may have been removed from it upon return.
+ */
+ static void handleSiblingOrEndingList(ref OutBuffer buf, ref size_t i, ref size_t iParagraphStart, ref MarkdownList[] nestedLists)
+ {
+ size_t iAfterSpaces = skipChars(buf, i + 1, " \t");
+
+ if (nestedLists[$-1].isAtItemInThisList(buf, i + 1, iAfterSpaces))
+ {
+ // end a sibling list item
+ i = buf.insert(i, ")");
+ iParagraphStart = skipChars(buf, i, " \t\r\n");
+ }
+ else if (iAfterSpaces >= buf.length || (buf[iAfterSpaces] != '\r' && buf[iAfterSpaces] != '\n'))
+ {
+ // end nested lists that are indented more than this content
+ const indent = getMarkdownIndent(buf, i + 1, iAfterSpaces);
+ while (nestedLists.length && nestedLists[$-1].contentIndent > indent)
+ {
+ i = buf.insert(i, ")\n)");
+ --nestedLists.length;
+ iParagraphStart = skipChars(buf, i, " \t\r\n");
+
+ if (nestedLists.length && nestedLists[$-1].isAtItemInThisList(buf, i + 1, iParagraphStart))
+ {
+ i = buf.insert(i, ")");
+ ++iParagraphStart;
+ break;
+ }
+ }
+ }
+ }
+
+ /****************************************************
+ * Parse an unordered list item at the current position
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iLineStart = the index within `buf` of the first character of the line
+ * i = the index within `buf` of the list item
+ * Returns: the parsed list item, or a list item with type `.init` if no list item is available
+ */
+ private static MarkdownList parseUnorderedListItem(ref OutBuffer buf, size_t iLineStart, size_t i)
+ {
+ if (i+1 < buf.length &&
+ (buf[i] == '-' ||
+ buf[i] == '*' ||
+ buf[i] == '+') &&
+ (buf[i+1] == ' ' ||
+ buf[i+1] == '\t' ||
+ buf[i+1] == '\r' ||
+ buf[i+1] == '\n'))
+ {
+ const iContentStart = skipChars(buf, i + 1, " \t");
+ const delimiterIndent = getMarkdownIndent(buf, iLineStart, i);
+ const contentIndent = getMarkdownIndent(buf, iLineStart, iContentStart);
+ auto list = MarkdownList(null, iLineStart, iContentStart, delimiterIndent, contentIndent, 0, buf[i]);
+ return list;
+ }
+ return MarkdownList();
+ }
+
+ /****************************************************
+ * Parse an ordered list item at the current position
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iLineStart = the index within `buf` of the first character of the line
+ * i = the index within `buf` of the list item
+ * Returns: the parsed list item, or a list item with type `.init` if no list item is available
+ */
+ private static MarkdownList parseOrderedListItem(ref OutBuffer buf, size_t iLineStart, size_t i)
+ {
+ size_t iAfterNumbers = skipChars(buf, i, "0123456789");
+ if (iAfterNumbers - i > 0 &&
+ iAfterNumbers - i <= 9 &&
+ iAfterNumbers + 1 < buf.length &&
+ buf[iAfterNumbers] == '.' &&
+ (buf[iAfterNumbers+1] == ' ' ||
+ buf[iAfterNumbers+1] == '\t' ||
+ buf[iAfterNumbers+1] == '\r' ||
+ buf[iAfterNumbers+1] == '\n'))
+ {
+ const iContentStart = skipChars(buf, iAfterNumbers + 1, " \t");
+ const delimiterIndent = getMarkdownIndent(buf, iLineStart, i);
+ const contentIndent = getMarkdownIndent(buf, iLineStart, iContentStart);
+ size_t iNumberStart = skipChars(buf, i, "0");
+ if (iNumberStart == iAfterNumbers)
+ --iNumberStart;
+ auto orderedStart = buf[][iNumberStart .. iAfterNumbers];
+ if (orderedStart == "1")
+ orderedStart = null;
+ return MarkdownList(orderedStart.idup, iLineStart, iContentStart, delimiterIndent, contentIndent, 0, buf[iAfterNumbers]);
+ }
+ return MarkdownList();
+ }
+}
+
+/****************************************************
+ * A Markdown link.
+ */
+private struct MarkdownLink
+{
+ string href; /// the link destination
+ string title; /// an optional title for the link
+ string label; /// an optional label for the link
+ Dsymbol symbol; /// an optional symbol to link to
+
+ /****************************************************
+ * Replace a Markdown link or link definition in the form of:
+ * - Inline link: `[foo](url/ 'optional title')`
+ * - Reference link: `[foo][bar]`, `[foo][]` or `[foo]`
+ * - Link reference definition: `[bar]: url/ 'optional title'`
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the `]` character of the potential link.
+ * If this function succeeds it will be adjusted to fit the inserted link macro.
+ * loc = the current location within the file
+ * inlineDelimiters = previously parsed Markdown delimiters, including emphasis and link/image starts
+ * delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter
+ * linkReferences = previously parsed link references. When this function returns it may contain
+ * additional previously unparsed references.
+ * Returns: whether a reference link was found and replaced at `i`
+ */
+ static bool replaceLink(ref OutBuffer buf, ref size_t i, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences)
+ {
+ const delimiter = inlineDelimiters[delimiterIndex];
+ MarkdownLink link;
+
+ size_t iEnd = link.parseReferenceDefinition(buf, i, delimiter);
+ if (iEnd > i)
+ {
+ i = delimiter.iStart;
+ link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc);
+ inlineDelimiters.length = delimiterIndex;
+ return true;
+ }
+
+ iEnd = link.parseInlineLink(buf, i);
+ if (iEnd == i)
+ {
+ iEnd = link.parseReferenceLink(buf, i, delimiter);
+ if (iEnd > i)
+ {
+ const label = link.label;
+ link = linkReferences.lookupReference(label, buf, i, loc);
+ // check rightFlanking to avoid replacing things like int[string]
+ if (!link.href.length && !delimiter.rightFlanking)
+ link = linkReferences.lookupSymbol(label);
+ if (!link.href.length)
+ return false;
+ }
+ }
+
+ if (iEnd == i)
+ return false;
+
+ immutable delta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, delimiterIndex);
+ iEnd += delta;
+ i += delta;
+
+ if (global.params.vmarkdown)
+ {
+ const s = buf[][delimiter.iStart..iEnd];
+ message(loc, "Ddoc: linking '%.*s' to '%.*s'", cast(int)s.length, s.ptr, cast(int)link.href.length, link.href.ptr);
+ }
+
+ link.replaceLink(buf, i, iEnd, delimiter);
+ return true;
+ }
+
+ /****************************************************
+ * Replace a Markdown link definition in the form of `[bar]: url/ 'optional title'`
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the `]` character of the potential link.
+ * If this function succeeds it will be adjusted to fit the inserted link macro.
+ * inlineDelimiters = previously parsed Markdown delimiters, including emphasis and link/image starts
+ * delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter
+ * linkReferences = previously parsed link references. When this function returns it may contain
+ * additional previously unparsed references.
+ * loc = the current location in the file
+ * Returns: whether a reference link was found and replaced at `i`
+ */
+ static bool replaceReferenceDefinition(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences, const ref Loc loc)
+ {
+ const delimiter = inlineDelimiters[delimiterIndex];
+ MarkdownLink link;
+ size_t iEnd = link.parseReferenceDefinition(buf, i, delimiter);
+ if (iEnd == i)
+ return false;
+
+ i = delimiter.iStart;
+ link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc);
+ inlineDelimiters.length = delimiterIndex;
+ return true;
+ }
+
+ /****************************************************
+ * Parse a Markdown inline link in the form of `[foo](url/ 'optional title')`
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the `]` character of the inline link.
+ * Returns: the index at the end of parsing the link, or `i` if parsing failed.
+ */
+ private size_t parseInlineLink(ref OutBuffer buf, size_t i)
+ {
+ size_t iEnd = i + 1;
+ if (iEnd >= buf.length || buf[iEnd] != '(')
+ return i;
+ ++iEnd;
+
+ if (!parseHref(buf, iEnd))
+ return i;
+
+ iEnd = skipChars(buf, iEnd, " \t\r\n");
+ if (buf[iEnd] != ')')
+ {
+ if (parseTitle(buf, iEnd))
+ iEnd = skipChars(buf, iEnd, " \t\r\n");
+ }
+
+ if (buf[iEnd] != ')')
+ return i;
+
+ return iEnd + 1;
+ }
+
+ /****************************************************
+ * Parse a Markdown reference link in the form of `[foo][bar]`, `[foo][]` or `[foo]`
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the `]` character of the inline link.
+ * delimiter = the delimiter that starts this link
+ * Returns: the index at the end of parsing the link, or `i` if parsing failed.
+ */
+ private size_t parseReferenceLink(ref OutBuffer buf, size_t i, MarkdownDelimiter delimiter)
+ {
+ size_t iStart = i + 1;
+ size_t iEnd = iStart;
+ if (iEnd >= buf.length || buf[iEnd] != '[' || (iEnd+1 < buf.length && buf[iEnd+1] == ']'))
+ {
+ // collapsed reference [foo][] or shortcut reference [foo]
+ iStart = delimiter.iStart + delimiter.count - 1;
+ if (buf[iEnd] == '[')
+ iEnd += 2;
+ }
+
+ parseLabel(buf, iStart);
+ if (!label.length)
+ return i;
+
+ if (iEnd < iStart)
+ iEnd = iStart;
+ return iEnd;
+ }
+
+ /****************************************************
+ * Parse a Markdown reference definition in the form of `[bar]: url/ 'optional title'`
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the `]` character of the inline link.
+ * delimiter = the delimiter that starts this link
+ * Returns: the index at the end of parsing the link, or `i` if parsing failed.
+ */
+ private size_t parseReferenceDefinition(ref OutBuffer buf, size_t i, MarkdownDelimiter delimiter)
+ {
+ if (!delimiter.atParagraphStart || delimiter.type != '[' ||
+ i+1 >= buf.length || buf[i+1] != ':')
+ return i;
+
+ size_t iEnd = delimiter.iStart;
+ parseLabel(buf, iEnd);
+ if (label.length == 0 || iEnd != i + 1)
+ return i;
+
+ ++iEnd;
+ iEnd = skipChars(buf, iEnd, " \t");
+ skipOneNewline(buf, iEnd);
+
+ if (!parseHref(buf, iEnd) || href.length == 0)
+ return i;
+
+ iEnd = skipChars(buf, iEnd, " \t");
+ const requireNewline = !skipOneNewline(buf, iEnd);
+ const iBeforeTitle = iEnd;
+
+ if (parseTitle(buf, iEnd))
+ {
+ iEnd = skipChars(buf, iEnd, " \t");
+ if (iEnd < buf.length && buf[iEnd] != '\r' && buf[iEnd] != '\n')
+ {
+ // the title must end with a newline
+ title.length = 0;
+ iEnd = iBeforeTitle;
+ }
+ }
+
+ iEnd = skipChars(buf, iEnd, " \t");
+ if (requireNewline && iEnd < buf.length-1 && buf[iEnd] != '\r' && buf[iEnd] != '\n')
+ return i;
+
+ return iEnd;
+ }
+
+ /****************************************************
+ * Parse and normalize a Markdown reference label
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the `[` character at the start of the label.
+ * If this function returns a non-empty label then `i` will point just after the ']' at the end of the label.
+ * Returns: the parsed and normalized label, possibly empty
+ */
+ private bool parseLabel(ref OutBuffer buf, ref size_t i)
+ {
+ if (buf[i] != '[')
+ return false;
+
+ const slice = buf[];
+ size_t j = i + 1;
+
+ // Some labels have already been en-symboled; handle that
+ const inSymbol = j+15 < slice.length && slice[j..j+15] == "$(DDOC_PSYMBOL ";
+ if (inSymbol)
+ j += 15;
+
+ for (; j < slice.length; ++j)
+ {
+ const c = slice[j];
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ if (label.length && label[$-1] != ' ')
+ label ~= ' ';
+ break;
+ case ')':
+ if (inSymbol && j+1 < slice.length && slice[j+1] == ']')
+ {
+ ++j;
+ goto case ']';
+ }
+ goto default;
+ case '[':
+ if (slice[j-1] != '\\')
+ {
+ label.length = 0;
+ return false;
+ }
+ break;
+ case ']':
+ if (label.length && label[$-1] == ' ')
+ --label.length;
+ if (label.length)
+ {
+ i = j + 1;
+ return true;
+ }
+ return false;
+ default:
+ label ~= c;
+ break;
+ }
+ }
+ label.length = 0;
+ return false;
+ }
+
+ /****************************************************
+ * Parse and store a Markdown link URL, optionally enclosed in `<>` brackets
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the first character of the URL.
+ * If this function succeeds `i` will point just after the the end of the URL.
+ * Returns: whether a URL was found and parsed
+ */
+ private bool parseHref(ref OutBuffer buf, ref size_t i)
+ {
+ size_t j = skipChars(buf, i, " \t");
+
+ size_t iHrefStart = j;
+ size_t parenDepth = 1;
+ bool inPointy = false;
+ const slice = buf[];
+ for (; j < slice.length; j++)
+ {
+ switch (slice[j])
+ {
+ case '<':
+ if (!inPointy && j == iHrefStart)
+ {
+ inPointy = true;
+ ++iHrefStart;
+ }
+ break;
+ case '>':
+ if (inPointy && slice[j-1] != '\\')
+ goto LReturnHref;
+ break;
+ case '(':
+ if (!inPointy && slice[j-1] != '\\')
+ ++parenDepth;
+ break;
+ case ')':
+ if (!inPointy && slice[j-1] != '\\')
+ {
+ --parenDepth;
+ if (!parenDepth)
+ goto LReturnHref;
+ }
+ break;
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ if (inPointy)
+ {
+ // invalid link
+ return false;
+ }
+ goto LReturnHref;
+ default:
+ break;
+ }
+ }
+ if (inPointy)
+ return false;
+ LReturnHref:
+ auto href = slice[iHrefStart .. j].dup;
+ this.href = cast(string) percentEncode(removeEscapeBackslashes(href)).replaceChar(',', "$(COMMA)");
+ i = j;
+ if (inPointy)
+ ++i;
+ return true;
+ }
+
+ /****************************************************
+ * Parse and store a Markdown link title, enclosed in parentheses or `'` or `"` quotes
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the first character of the title.
+ * If this function succeeds `i` will point just after the the end of the title.
+ * Returns: whether a title was found and parsed
+ */
+ private bool parseTitle(ref OutBuffer buf, ref size_t i)
+ {
+ size_t j = skipChars(buf, i, " \t");
+ if (j >= buf.length)
+ return false;
+
+ char type = buf[j];
+ if (type != '"' && type != '\'' && type != '(')
+ return false;
+ if (type == '(')
+ type = ')';
+
+ const iTitleStart = j + 1;
+ size_t iNewline = 0;
+ const slice = buf[];
+ for (j = iTitleStart; j < slice.length; j++)
+ {
+ const c = slice[j];
+ switch (c)
+ {
+ case ')':
+ case '"':
+ case '\'':
+ if (type == c && slice[j-1] != '\\')
+ goto LEndTitle;
+ iNewline = 0;
+ break;
+ case ' ':
+ case '\t':
+ case '\r':
+ break;
+ case '\n':
+ if (iNewline)
+ {
+ // no blank lines in titles
+ return false;
+ }
+ iNewline = j;
+ break;
+ default:
+ iNewline = 0;
+ break;
+ }
+ }
+ return false;
+ LEndTitle:
+ auto title = slice[iTitleStart .. j].dup;
+ this.title = cast(string) removeEscapeBackslashes(title).
+ replaceChar(',', "$(COMMA)").
+ replaceChar('"', "$(QUOTE)");
+ i = j + 1;
+ return true;
+ }
+
+ /****************************************************
+ * Replace a Markdown link or image with the appropriate macro
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the `]` character of the inline link.
+ * When this function returns it will be adjusted to the end of the inserted macro.
+ * iLinkEnd = the index within `buf` that points just after the last character of the link
+ * delimiter = the Markdown delimiter that started the link or image
+ */
+ private void replaceLink(ref OutBuffer buf, ref size_t i, size_t iLinkEnd, MarkdownDelimiter delimiter)
+ {
+ size_t iAfterLink = i - delimiter.count;
+ string macroName;
+ if (symbol)
+ {
+ macroName = "$(SYMBOL_LINK ";
+ }
+ else if (title.length)
+ {
+ if (delimiter.type == '[')
+ macroName = "$(LINK_TITLE ";
+ else
+ macroName = "$(IMAGE_TITLE ";
+ }
+ else
+ {
+ if (delimiter.type == '[')
+ macroName = "$(LINK2 ";
+ else
+ macroName = "$(IMAGE ";
+ }
+ buf.remove(delimiter.iStart, delimiter.count);
+ buf.remove(i - delimiter.count, iLinkEnd - i);
+ iLinkEnd = buf.insert(delimiter.iStart, macroName);
+ iLinkEnd = buf.insert(iLinkEnd, href);
+ iLinkEnd = buf.insert(iLinkEnd, ", ");
+ iAfterLink += macroName.length + href.length + 2;
+ if (title.length)
+ {
+ iLinkEnd = buf.insert(iLinkEnd, title);
+ iLinkEnd = buf.insert(iLinkEnd, ", ");
+ iAfterLink += title.length + 2;
+
+ // Link macros with titles require escaping commas
+ for (size_t j = iLinkEnd; j < iAfterLink; ++j)
+ if (buf[j] == ',')
+ {
+ buf.remove(j, 1);
+ j = buf.insert(j, "$(COMMA)") - 1;
+ iAfterLink += 7;
+ }
+ }
+// TODO: if image, remove internal macros, leaving only text
+ buf.insert(iAfterLink, ")");
+ i = iAfterLink;
+ }
+
+ /****************************************************
+ * Store the Markdown link definition and remove it from `buf`
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` that points to the `[` character at the start of the link definition.
+ * When this function returns it will be adjusted to exclude the link definition.
+ * iEnd = the index within `buf` that points just after the end of the definition
+ * linkReferences = previously parsed link references. When this function returns it may contain
+ * an additional reference.
+ * loc = the current location in the file
+ */
+ private void storeAndReplaceDefinition(ref OutBuffer buf, ref size_t i, size_t iEnd, ref MarkdownLinkReferences linkReferences, const ref Loc loc)
+ {
+ if (global.params.vmarkdown)
+ message(loc, "Ddoc: found link reference '%.*s' to '%.*s'", cast(int)label.length, label.ptr, cast(int)href.length, href.ptr);
+
+ // Remove the definition and trailing whitespace
+ iEnd = skipChars(buf, iEnd, " \t\r\n");
+ buf.remove(i, iEnd - i);
+ i -= 2;
+
+ string lowercaseLabel = label.toLowercase();
+ if (lowercaseLabel !in linkReferences.references)
+ linkReferences.references[lowercaseLabel] = this;
+ }
+
+ /****************************************************
+ * Remove Markdown escaping backslashes from the given string
+ * Params:
+ * s = the string to remove escaping backslashes from
+ * Returns: `s` without escaping backslashes in it
+ */
+ private static char[] removeEscapeBackslashes(char[] s)
+ {
+ if (!s.length)
+ return s;
+
+ // avoid doing anything if there isn't anything to escape
+ size_t i;
+ for (i = 0; i < s.length-1; ++i)
+ if (s[i] == '\\' && ispunct(s[i+1]))
+ break;
+ if (i == s.length-1)
+ return s;
+
+ // copy characters backwards, then truncate
+ size_t j = i + 1;
+ s[i] = s[j];
+ for (++i, ++j; j < s.length; ++i, ++j)
+ {
+ if (j < s.length-1 && s[j] == '\\' && ispunct(s[j+1]))
+ ++j;
+ s[i] = s[j];
+ }
+ s.length -= (j - i);
+ return s;
+ }
+
+ ///
+ unittest
+ {
+ assert(removeEscapeBackslashes("".dup) == "");
+ assert(removeEscapeBackslashes(`\a`.dup) == `\a`);
+ assert(removeEscapeBackslashes(`.\`.dup) == `.\`);
+ assert(removeEscapeBackslashes(`\.\`.dup) == `.\`);
+ assert(removeEscapeBackslashes(`\.`.dup) == `.`);
+ assert(removeEscapeBackslashes(`\.\.`.dup) == `..`);
+ assert(removeEscapeBackslashes(`a\.b\.c`.dup) == `a.b.c`);
+ }
+
+ /****************************************************
+ * Percent-encode (AKA URL-encode) the given string
+ * Params:
+ * s = the string to percent-encode
+ * Returns: `s` with special characters percent-encoded
+ */
+ private static inout(char)[] percentEncode(inout(char)[] s) pure
+ {
+ static bool shouldEncode(char c)
+ {
+ return ((c < '0' && c != '!' && c != '#' && c != '$' && c != '%' && c != '&' && c != '\'' && c != '(' &&
+ c != ')' && c != '*' && c != '+' && c != ',' && c != '-' && c != '.' && c != '/')
+ || (c > '9' && c < 'A' && c != ':' && c != ';' && c != '=' && c != '?' && c != '@')
+ || (c > 'Z' && c < 'a' && c != '[' && c != ']' && c != '_')
+ || (c > 'z' && c != '~'));
+ }
+
+ for (size_t i = 0; i < s.length; ++i)
+ {
+ if (shouldEncode(s[i]))
+ {
+ immutable static hexDigits = "0123456789ABCDEF";
+ immutable encoded1 = hexDigits[s[i] >> 4];
+ immutable encoded2 = hexDigits[s[i] & 0x0F];
+ s = s[0..i] ~ '%' ~ encoded1 ~ encoded2 ~ s[i+1..$];
+ i += 2;
+ }
+ }
+ return s;
+ }
+
+ ///
+ unittest
+ {
+ assert(percentEncode("") == "");
+ assert(percentEncode("aB12-._~/?") == "aB12-._~/?");
+ assert(percentEncode("<\n>") == "%3C%0A%3E");
+ }
+
+ /**************************************************
+ * Skip a single newline at `i`
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` to start looking at.
+ * If this function succeeds `i` will point after the newline.
+ * Returns: whether a newline was skipped
+ */
+ private static bool skipOneNewline(ref OutBuffer buf, ref size_t i) pure
+ {
+ if (i < buf.length && buf[i] == '\r')
+ ++i;
+ if (i < buf.length && buf[i] == '\n')
+ {
+ ++i;
+ return true;
+ }
+ return false;
+ }
+}
+
+/**************************************************
+ * A set of Markdown link references.
+ */
+private struct MarkdownLinkReferences
+{
+ MarkdownLink[string] references; // link references keyed by normalized label
+ MarkdownLink[string] symbols; // link symbols keyed by name
+ Scope* _scope; // the current scope
+ bool extractedAll; // the index into the buffer of the last-parsed reference
+
+ /**************************************************
+ * Look up a reference by label, searching through the rest of the buffer if needed.
+ * Symbols in the current scope are searched for if the DDoc doesn't define the reference.
+ * Params:
+ * label = the label to find the reference for
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` to start searching for references at
+ * loc = the current location in the file
+ * Returns: a link. If the `href` member has a value then the reference is valid.
+ */
+ MarkdownLink lookupReference(string label, ref OutBuffer buf, size_t i, const ref Loc loc)
+ {
+ const lowercaseLabel = label.toLowercase();
+ if (lowercaseLabel !in references)
+ extractReferences(buf, i, loc);
+
+ if (lowercaseLabel in references)
+ return references[lowercaseLabel];
+
+ return MarkdownLink();
+ }
+
+ /**
+ * Look up the link for the D symbol with the given name.
+ * If found, the link is cached in the `symbols` member.
+ * Params:
+ * name = the name of the symbol
+ * Returns: the link for the symbol or a link with a `null` href
+ */
+ MarkdownLink lookupSymbol(string name)
+ {
+ if (name in symbols)
+ return symbols[name];
+
+ const ids = split(name, '.');
+
+ MarkdownLink link;
+ auto id = Identifier.lookup(ids[0].ptr, ids[0].length);
+ if (id)
+ {
+ auto loc = Loc();
+ auto symbol = _scope.search(loc, id, null, IgnoreErrors);
+ for (size_t i = 1; symbol && i < ids.length; ++i)
+ {
+ id = Identifier.lookup(ids[i].ptr, ids[i].length);
+ symbol = id !is null ? symbol.search(loc, id, IgnoreErrors) : null;
+ }
+ if (symbol)
+ link = MarkdownLink(createHref(symbol), null, name, symbol);
+ }
+
+ symbols[name] = link;
+ return link;
+ }
+
+ /**************************************************
+ * Remove and store all link references from the document, in the form of
+ * `[label]: href "optional title"`
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` to start looking at
+ * loc = the current location in the file
+ * Returns: whether a reference was extracted
+ */
+ private void extractReferences(ref OutBuffer buf, size_t i, const ref Loc loc)
+ {
+ static bool isFollowedBySpace(ref OutBuffer buf, size_t i)
+ {
+ return i+1 < buf.length && (buf[i+1] == ' ' || buf[i+1] == '\t');
+ }
+
+ if (extractedAll)
+ return;
+
+ bool leadingBlank = false;
+ int inCode = false;
+ bool newParagraph = true;
+ MarkdownDelimiter[] delimiters;
+ for (; i < buf.length; ++i)
+ {
+ const c = buf[i];
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ break;
+ case '\n':
+ if (leadingBlank && !inCode)
+ newParagraph = true;
+ leadingBlank = true;
+ break;
+ case '\\':
+ ++i;
+ break;
+ case '#':
+ if (leadingBlank && !inCode)
+ newParagraph = true;
+ leadingBlank = false;
+ break;
+ case '>':
+ if (leadingBlank && !inCode)
+ newParagraph = true;
+ break;
+ case '+':
+ if (leadingBlank && !inCode && isFollowedBySpace(buf, i))
+ newParagraph = true;
+ else
+ leadingBlank = false;
+ break;
+ case '0':
+ ..
+ case '9':
+ if (leadingBlank && !inCode)
+ {
+ i = skipChars(buf, i, "0123456789");
+ if (i < buf.length &&
+ (buf[i] == '.' || buf[i] == ')') &&
+ isFollowedBySpace(buf, i))
+ newParagraph = true;
+ else
+ leadingBlank = false;
+ }
+ break;
+ case '*':
+ if (leadingBlank && !inCode)
+ {
+ newParagraph = true;
+ if (!isFollowedBySpace(buf, i))
+ leadingBlank = false;
+ }
+ break;
+ case '`':
+ case '~':
+ if (leadingBlank && i+2 < buf.length && buf[i+1] == c && buf[i+2] == c)
+ {
+ inCode = inCode == c ? false : c;
+ i = skipChars(buf, i, [c]) - 1;
+ newParagraph = true;
+ }
+ leadingBlank = false;
+ break;
+ case '-':
+ if (leadingBlank && !inCode && isFollowedBySpace(buf, i))
+ goto case '+';
+ else
+ goto case '`';
+ case '[':
+ if (leadingBlank && !inCode && newParagraph)
+ delimiters ~= MarkdownDelimiter(i, 1, 0, false, false, true, c);
+ break;
+ case ']':
+ if (delimiters.length && !inCode &&
+ MarkdownLink.replaceReferenceDefinition(buf, i, delimiters, cast(int) delimiters.length - 1, this, loc))
+ --i;
+ break;
+ default:
+ if (leadingBlank)
+ newParagraph = false;
+ leadingBlank = false;
+ break;
+ }
+ }
+ extractedAll = true;
+ }
+
+ /**
+ * Split a string by a delimiter, excluding the delimiter.
+ * Params:
+ * s = the string to split
+ * delimiter = the character to split by
+ * Returns: the resulting array of strings
+ */
+ private static string[] split(string s, char delimiter) pure
+ {
+ string[] result;
+ size_t iStart = 0;
+ foreach (size_t i; 0..s.length)
+ if (s[i] == delimiter)
+ {
+ result ~= s[iStart..i];
+ iStart = i + 1;
+ }
+ result ~= s[iStart..$];
+ return result;
+ }
+
+ ///
+ unittest
+ {
+ assert(split("", ',') == [""]);
+ assert(split("ab", ',') == ["ab"]);
+ assert(split("a,b", ',') == ["a", "b"]);
+ assert(split("a,,b", ',') == ["a", "", "b"]);
+ assert(split(",ab", ',') == ["", "ab"]);
+ assert(split("ab,", ',') == ["ab", ""]);
+ }
+
+ /**
+ * Create a HREF for the given D symbol.
+ * The HREF is relative to the current location if possible.
+ * Params:
+ * symbol = the symbol to create a HREF for.
+ * Returns: the resulting href
+ */
+ private string createHref(Dsymbol symbol)
+ {
+ Dsymbol root = symbol;
+
+ const(char)[] lref;
+ while (symbol && symbol.ident && !symbol.isModule())
+ {
+ if (lref.length)
+ lref = '.' ~ lref;
+ lref = symbol.ident.toString() ~ lref;
+ symbol = symbol.parent;
+ }
+
+ const(char)[] path;
+ if (symbol && symbol.ident && symbol.isModule() != _scope._module)
+ {
+ do
+ {
+ root = symbol;
+
+ // If the module has a file name, we're done
+ if (const m = symbol.isModule())
+ if (m.docfile)
+ {
+ path = m.docfile.toString();
+ break;
+ }
+
+ if (path.length)
+ path = '_' ~ path;
+ path = symbol.ident.toString() ~ path;
+ symbol = symbol.parent;
+ } while (symbol && symbol.ident);
+
+ if (!symbol && path.length)
+ path ~= "$(DOC_EXTENSION)";
+ }
+
+ // Attempt an absolute URL if not in the same package
+ while (root.parent)
+ root = root.parent;
+ Dsymbol scopeRoot = _scope._module;
+ while (scopeRoot.parent)
+ scopeRoot = scopeRoot.parent;
+ if (scopeRoot != root)
+ {
+ path = "$(DOC_ROOT_" ~ root.ident.toString() ~ ')' ~ path;
+ lref = '.' ~ lref; // remote URIs like Phobos and Mir use .prefixes
+ }
+
+ return cast(string) (path ~ '#' ~ lref);
+ }
+}
+
+private enum TableColumnAlignment
+{
+ none,
+ left,
+ center,
+ right
+}
+
+/****************************************************
+ * Parse a Markdown table delimiter row in the form of `| -- | :-- | :--: | --: |`
+ * where the example text has four columns with the following alignments:
+ * default, left, center, and right. The first and last pipes are optional. If a
+ * delimiter row is found it will be removed from `buf`.
+ *
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iStart = the index within `buf` that the delimiter row starts at
+ * inQuote = whether the table is inside a quote
+ * columnAlignments = alignments to populate for each column
+ * Returns: the index of the end of the parsed delimiter, or `0` if not found
+ */
+private size_t parseTableDelimiterRow(ref OutBuffer buf, const size_t iStart, bool inQuote, ref TableColumnAlignment[] columnAlignments)
+{
+ size_t i = skipChars(buf, iStart, inQuote ? ">| \t" : "| \t");
+ while (i < buf.length && buf[i] != '\r' && buf[i] != '\n')
+ {
+ const leftColon = buf[i] == ':';
+ if (leftColon)
+ ++i;
+
+ if (i >= buf.length || buf[i] != '-')
+ break;
+ i = skipChars(buf, i, "-");
+
+ const rightColon = i < buf.length && buf[i] == ':';
+ i = skipChars(buf, i, ": \t");
+
+ if (i >= buf.length || (buf[i] != '|' && buf[i] != '\r' && buf[i] != '\n'))
+ break;
+ i = skipChars(buf, i, "| \t");
+
+ columnAlignments ~= (leftColon && rightColon) ? TableColumnAlignment.center :
+ leftColon ? TableColumnAlignment.left :
+ rightColon ? TableColumnAlignment.right :
+ TableColumnAlignment.none;
+ }
+
+ if (i < buf.length && buf[i] != '\r' && buf[i] != '\n' && buf[i] != ')')
+ {
+ columnAlignments.length = 0;
+ return 0;
+ }
+
+ if (i < buf.length && buf[i] == '\r') ++i;
+ if (i < buf.length && buf[i] == '\n') ++i;
+ return i;
+}
+
+/****************************************************
+ * Look for a table delimiter row, and if found parse the previous row as a
+ * table header row. If both exist with a matching number of columns, start a
+ * table.
+ *
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iStart = the index within `buf` that the table header row starts at, inclusive
+ * iEnd = the index within `buf` that the table header row ends at, exclusive
+ * loc = the current location in the file
+ * inQuote = whether the table is inside a quote
+ * inlineDelimiters = delimiters containing columns separators and any inline emphasis
+ * columnAlignments = the parsed alignments for each column
+ * Returns: the number of characters added by starting the table, or `0` if unchanged
+ */
+private size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, bool inQuote, ref MarkdownDelimiter[] inlineDelimiters, out TableColumnAlignment[] columnAlignments)
+{
+ const iDelimiterRowEnd = parseTableDelimiterRow(buf, iEnd + 1, inQuote, columnAlignments);
+ if (iDelimiterRowEnd)
+ {
+ const delta = replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, true);
+ if (delta)
+ {
+ buf.remove(iEnd + delta, iDelimiterRowEnd - iEnd);
+ buf.insert(iEnd + delta, "$(TBODY ");
+ buf.insert(iStart, "$(TABLE ");
+ return delta + 15;
+ }
+ }
+
+ columnAlignments.length = 0;
+ return 0;
+}
+
+/****************************************************
+ * Replace a Markdown table row in the form of table cells delimited by pipes:
+ * `| cell | cell | cell`. The first and last pipes are optional.
+ *
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iStart = the index within `buf` that the table row starts at, inclusive
+ * iEnd = the index within `buf` that the table row ends at, exclusive
+ * loc = the current location in the file
+ * inlineDelimiters = delimiters containing columns separators and any inline emphasis
+ * columnAlignments = alignments for each column
+ * headerRow = if `true` then the number of columns will be enforced to match
+ * `columnAlignments.length` and the row will be surrounded by a
+ * `THEAD` macro
+ * Returns: the number of characters added by replacing the row, or `0` if unchanged
+ */
+private size_t replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow)
+{
+ if (!columnAlignments.length || iStart == iEnd)
+ return 0;
+
+ iStart = skipChars(buf, iStart, " \t");
+ int cellCount = 0;
+ foreach (delimiter; inlineDelimiters)
+ if (delimiter.type == '|' && !delimiter.leftFlanking)
+ ++cellCount;
+ bool ignoreLast = inlineDelimiters.length > 0 && inlineDelimiters[$-1].type == '|';
+ if (ignoreLast)
+ {
+ const iLast = skipChars(buf, inlineDelimiters[$-1].iStart + inlineDelimiters[$-1].count, " \t");
+ ignoreLast = iLast >= iEnd;
+ }
+ if (!ignoreLast)
+ ++cellCount;
+
+ if (headerRow && cellCount != columnAlignments.length)
+ return 0;
+
+ if (headerRow && global.params.vmarkdown)
+ {
+ const s = buf[][iStart..iEnd];
+ message(loc, "Ddoc: formatting table '%.*s'", cast(int)s.length, s.ptr);
+ }
+
+ size_t delta = 0;
+
+ void replaceTableCell(size_t iCellStart, size_t iCellEnd, int cellIndex, int di)
+ {
+ const eDelta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, di);
+ delta += eDelta;
+ iCellEnd += eDelta;
+
+ // strip trailing whitespace and delimiter
+ size_t i = iCellEnd - 1;
+ while (i > iCellStart && (buf[i] == '|' || buf[i] == ' ' || buf[i] == '\t'))
+ --i;
+ ++i;
+ buf.remove(i, iCellEnd - i);
+ delta -= iCellEnd - i;
+ iCellEnd = i;
+
+ buf.insert(iCellEnd, ")");
+ ++delta;
+
+ // strip initial whitespace and delimiter
+ i = skipChars(buf, iCellStart, "| \t");
+ buf.remove(iCellStart, i - iCellStart);
+ delta -= i - iCellStart;
+
+ switch (columnAlignments[cellIndex])
+ {
+ case TableColumnAlignment.none:
+ buf.insert(iCellStart, headerRow ? "$(TH " : "$(TD ");
+ delta += 5;
+ break;
+ case TableColumnAlignment.left:
+ buf.insert(iCellStart, "left, ");
+ delta += 6;
+ goto default;
+ case TableColumnAlignment.center:
+ buf.insert(iCellStart, "center, ");
+ delta += 8;
+ goto default;
+ case TableColumnAlignment.right:
+ buf.insert(iCellStart, "right, ");
+ delta += 7;
+ goto default;
+ default:
+ buf.insert(iCellStart, headerRow ? "$(TH_ALIGN " : "$(TD_ALIGN ");
+ delta += 11;
+ break;
+ }
+ }
+
+ int cellIndex = cellCount - 1;
+ size_t iCellEnd = iEnd;
+ foreach_reverse (di, delimiter; inlineDelimiters)
+ {
+ if (delimiter.type == '|')
+ {
+ if (ignoreLast && di == inlineDelimiters.length-1)
+ {
+ ignoreLast = false;
+ continue;
+ }
+
+ if (cellIndex >= columnAlignments.length)
+ {
+ // kill any extra cells
+ buf.remove(delimiter.iStart, iEnd + delta - delimiter.iStart);
+ delta -= iEnd + delta - delimiter.iStart;
+ iCellEnd = iEnd + delta;
+ --cellIndex;
+ continue;
+ }
+
+ replaceTableCell(delimiter.iStart, iCellEnd, cellIndex, cast(int) di);
+ iCellEnd = delimiter.iStart;
+ --cellIndex;
+ }
+ }
+
+ // if no starting pipe, replace from the start
+ if (cellIndex >= 0)
+ replaceTableCell(iStart, iCellEnd, cellIndex, 0);
+
+ buf.insert(iEnd + delta, ")");
+ buf.insert(iStart, "$(TR ");
+ delta += 6;
+
+ if (headerRow)
+ {
+ buf.insert(iEnd + delta, ")");
+ buf.insert(iStart, "$(THEAD ");
+ delta += 9;
+ }
+
+ return delta;
+}
+
+/****************************************************
+ * End a table, if in one.
+ *
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * i = the index within `buf` to end the table at
+ * columnAlignments = alignments for each column; upon return is set to length `0`
+ * Returns: the number of characters added by ending the table, or `0` if unchanged
+ */
+private size_t endTable(ref OutBuffer buf, size_t i, ref TableColumnAlignment[] columnAlignments)
+{
+ if (!columnAlignments.length)
+ return 0;
+
+ buf.insert(i, "))");
+ columnAlignments.length = 0;
+ return 2;
+}
+
+/****************************************************
+ * End a table row and then the table itself.
+ *
+ * Params:
+ * buf = an OutBuffer containing the DDoc
+ * iStart = the index within `buf` that the table row starts at, inclusive
+ * iEnd = the index within `buf` that the table row ends at, exclusive
+ * loc = the current location in the file
+ * inlineDelimiters = delimiters containing columns separators and any inline emphasis
+ * columnAlignments = alignments for each column; upon return is set to length `0`
+ * Returns: the number of characters added by replacing the row, or `0` if unchanged
+ */
+private size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments)
+{
+ size_t delta = replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, false);
+ delta += endTable(buf, iEnd + delta, columnAlignments);
+ return delta;
+}
+
+/**************************************************
+ * Highlight text section.
+ *
+ * Params:
+ * scope = the current parse scope
+ * a = an array of D symbols at the current scope
+ * loc = source location of start of text. It is a mutable copy to allow incrementing its linenum, for printing the correct line number when an error is encountered in a multiline block of ddoc.
+ * buf = an OutBuffer containing the DDoc
+ * offset = the index within buf to start highlighting
+ */
+private void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t offset)
+{
+ const incrementLoc = loc.linnum == 0 ? 1 : 0;
+ loc.linnum += incrementLoc;
+ loc.charnum = 0;
+ //printf("highlightText()\n");
+ bool leadingBlank = true;
+ size_t iParagraphStart = offset;
+ size_t iPrecedingBlankLine = 0;
+ int headingLevel = 0;
+ int headingMacroLevel = 0;
+ int quoteLevel = 0;
+ bool lineQuoted = false;
+ int quoteMacroLevel = 0;
+ MarkdownList[] nestedLists;
+ MarkdownDelimiter[] inlineDelimiters;
+ MarkdownLinkReferences linkReferences;
+ TableColumnAlignment[] columnAlignments;
+ bool tableRowDetected = false;
+ int inCode = 0;
+ int inBacktick = 0;
+ int macroLevel = 0;
+ int previousMacroLevel = 0;
+ int parenLevel = 0;
+ size_t iCodeStart = 0; // start of code section
+ size_t codeFenceLength = 0;
+ size_t codeIndent = 0;
+ string codeLanguage;
+ size_t iLineStart = offset;
+ linkReferences._scope = sc;
+ for (size_t i = offset; i < buf.length; i++)
+ {
+ char c = buf[i];
+ Lcont:
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ break;
+ case '\n':
+ if (inBacktick)
+ {
+ // `inline code` is only valid if contained on a single line
+ // otherwise, the backticks should be output literally.
+ //
+ // This lets things like `output from the linker' display
+ // unmolested while keeping the feature consistent with GitHub.
+ inBacktick = false;
+ inCode = false; // the backtick also assumes we're in code
+ // Nothing else is necessary since the DDOC_BACKQUOTED macro is
+ // inserted lazily at the close quote, meaning the rest of the
+ // text is already OK.
+ }
+ if (headingLevel)
+ {
+ i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters);
+ endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel);
+ removeBlankLineMacro(buf, iPrecedingBlankLine, i);
+ ++i;
+ iParagraphStart = skipChars(buf, i, " \t\r\n");
+ }
+
+ if (tableRowDetected && !columnAlignments.length)
+ i += startTable(buf, iLineStart, i, loc, lineQuoted, inlineDelimiters, columnAlignments);
+ else if (columnAlignments.length)
+ {
+ const delta = replaceTableRow(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments, false);
+ if (delta)
+ i += delta;
+ else
+ i += endTable(buf, i, columnAlignments);
+ }
+
+ if (!inCode && nestedLists.length && !quoteLevel)
+ MarkdownList.handleSiblingOrEndingList(buf, i, iParagraphStart, nestedLists);
+
+ iPrecedingBlankLine = 0;
+ if (!inCode && i == iLineStart && i + 1 < buf.length) // if "\n\n"
+ {
+ i += endTable(buf, i, columnAlignments);
+ if (!lineQuoted && quoteLevel)
+ endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel);
+ i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters);
+
+ // if we don't already know about this paragraph break then
+ // insert a blank line and record the paragraph break
+ if (iParagraphStart <= i)
+ {
+ iPrecedingBlankLine = i;
+ i = buf.insert(i, "$(DDOC_BLANKLINE)");
+ iParagraphStart = i + 1;
+ }
+ }
+ else if (inCode &&
+ i == iLineStart &&
+ i + 1 < buf.length &&
+ !lineQuoted &&
+ quoteLevel) // if "\n\n" in quoted code
+ {
+ inCode = false;
+ i = buf.insert(i, ")");
+ i += endAllMarkdownQuotes(buf, i, quoteLevel);
+ quoteMacroLevel = 0;
+ }
+ leadingBlank = true;
+ lineQuoted = false;
+ tableRowDetected = false;
+ iLineStart = i + 1;
+ loc.linnum += incrementLoc;
+
+ // update the paragraph start if we just entered a macro
+ if (previousMacroLevel < macroLevel && iParagraphStart < iLineStart)
+ iParagraphStart = iLineStart;
+ previousMacroLevel = macroLevel;
+ break;
+
+ case '<':
+ {
+ leadingBlank = false;
+ if (inCode)
+ break;
+ const slice = buf[];
+ auto p = &slice[i];
+ const se = sc._module.escapetable.escapeChar('<');
+ if (se == "&lt;")
+ {
+ // Generating HTML
+ // Skip over comments
+ if (p[1] == '!' && p[2] == '-' && p[3] == '-')
+ {
+ size_t j = i + 4;
+ p += 4;
+ while (1)
+ {
+ if (j == slice.length)
+ goto L1;
+ if (p[0] == '-' && p[1] == '-' && p[2] == '>')
+ {
+ i = j + 2; // place on closing '>'
+ break;
+ }
+ j++;
+ p++;
+ }
+ break;
+ }
+ // Skip over HTML tag
+ if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2])))
+ {
+ size_t j = i + 2;
+ p += 2;
+ while (1)
+ {
+ if (j == slice.length)
+ break;
+ if (p[0] == '>')
+ {
+ i = j; // place on closing '>'
+ break;
+ }
+ j++;
+ p++;
+ }
+ break;
+ }
+ }
+ L1:
+ // Replace '<' with '&lt;' character entity
+ if (se.length)
+ {
+ buf.remove(i, 1);
+ i = buf.insert(i, se);
+ i--; // point to ';'
+ }
+ break;
+ }
+
+ case '>':
+ {
+ if (leadingBlank && (!inCode || quoteLevel) && global.params.markdown)
+ {
+ if (!quoteLevel && global.params.vmarkdown)
+ {
+ size_t iEnd = i + 1;
+ while (iEnd < buf.length && buf[iEnd] != '\n')
+ ++iEnd;
+ const s = buf[][i .. iEnd];
+ message(loc, "Ddoc: starting quote block with '%.*s'", cast(int)s.length, s.ptr);
+ }
+
+ lineQuoted = true;
+ int lineQuoteLevel = 1;
+ size_t iAfterDelimiters = i + 1;
+ for (; iAfterDelimiters < buf.length; ++iAfterDelimiters)
+ {
+ const c0 = buf[iAfterDelimiters];
+ if (c0 == '>')
+ ++lineQuoteLevel;
+ else if (c0 != ' ' && c0 != '\t')
+ break;
+ }
+ if (!quoteMacroLevel)
+ quoteMacroLevel = macroLevel;
+ buf.remove(i, iAfterDelimiters - i);
+
+ if (quoteLevel < lineQuoteLevel)
+ {
+ i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ if (nestedLists.length)
+ {
+ const indent = getMarkdownIndent(buf, iLineStart, i);
+ if (indent < nestedLists[$-1].contentIndent)
+ i += MarkdownList.endAllNestedLists(buf, i, nestedLists);
+ }
+
+ for (; quoteLevel < lineQuoteLevel; ++quoteLevel)
+ {
+ i = buf.insert(i, "$(BLOCKQUOTE\n");
+ iLineStart = iParagraphStart = i;
+ }
+ --i;
+ }
+ else
+ {
+ --i;
+ if (nestedLists.length)
+ MarkdownList.handleSiblingOrEndingList(buf, i, iParagraphStart, nestedLists);
+ }
+ break;
+ }
+
+ leadingBlank = false;
+ if (inCode)
+ break;
+ // Replace '>' with '&gt;' character entity
+ const se = sc._module.escapetable.escapeChar('>');
+ if (se.length)
+ {
+ buf.remove(i, 1);
+ i = buf.insert(i, se);
+ i--; // point to ';'
+ }
+ break;
+ }
+
+ case '&':
+ {
+ leadingBlank = false;
+ if (inCode)
+ break;
+ char* p = cast(char*)&buf[].ptr[i];
+ if (p[1] == '#' || isalpha(p[1]))
+ break;
+ // already a character entity
+ // Replace '&' with '&amp;' character entity
+ const se = sc._module.escapetable.escapeChar('&');
+ if (se)
+ {
+ buf.remove(i, 1);
+ i = buf.insert(i, se);
+ i--; // point to ';'
+ }
+ break;
+ }
+
+ case '`':
+ {
+ const iAfterDelimiter = skipChars(buf, i, "`");
+ const count = iAfterDelimiter - i;
+
+ if (inBacktick == count)
+ {
+ inBacktick = 0;
+ inCode = 0;
+ OutBuffer codebuf;
+ codebuf.write(buf[iCodeStart + count .. i]);
+ // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL
+ highlightCode(sc, a, codebuf, 0);
+ escapeStrayParenthesis(loc, &codebuf, 0, false);
+ buf.remove(iCodeStart, i - iCodeStart + count); // also trimming off the current `
+ immutable pre = "$(DDOC_BACKQUOTED ";
+ i = buf.insert(iCodeStart, pre);
+ i = buf.insert(i, codebuf[]);
+ i = buf.insert(i, ")");
+ i--; // point to the ending ) so when the for loop does i++, it will see the next character
+ break;
+ }
+
+ // Perhaps we're starting or ending a Markdown code block
+ if (leadingBlank && global.params.markdown && count >= 3)
+ {
+ bool moreBackticks = false;
+ for (size_t j = iAfterDelimiter; !moreBackticks && j < buf.length; ++j)
+ if (buf[j] == '`')
+ moreBackticks = true;
+ else if (buf[j] == '\r' || buf[j] == '\n')
+ break;
+ if (!moreBackticks)
+ goto case '-';
+ }
+
+ if (inCode)
+ {
+ if (inBacktick)
+ i = iAfterDelimiter - 1;
+ break;
+ }
+ inCode = c;
+ inBacktick = cast(int) count;
+ codeIndent = 0; // inline code is not indented
+ // All we do here is set the code flags and record
+ // the location. The macro will be inserted lazily
+ // so we can easily cancel the inBacktick if we come
+ // across a newline character.
+ iCodeStart = i;
+ i = iAfterDelimiter - 1;
+ break;
+ }
+
+ case '#':
+ {
+ /* A line beginning with # indicates an ATX-style heading. */
+ if (leadingBlank && !inCode)
+ {
+ leadingBlank = false;
+
+ headingLevel = detectAtxHeadingLevel(buf, i);
+ if (!headingLevel)
+ break;
+
+ i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ if (!lineQuoted && quoteLevel)
+ i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
+
+ // remove the ### prefix, including whitespace
+ i = skipChars(buf, i + headingLevel, " \t");
+ buf.remove(iLineStart, i - iLineStart);
+ i = iParagraphStart = iLineStart;
+
+ removeAnyAtxHeadingSuffix(buf, i);
+ --i;
+
+ headingMacroLevel = macroLevel;
+ }
+ break;
+ }
+
+ case '~':
+ {
+ if (leadingBlank && global.params.markdown)
+ {
+ // Perhaps we're starting or ending a Markdown code block
+ const iAfterDelimiter = skipChars(buf, i, "~");
+ if (iAfterDelimiter - i >= 3)
+ goto case '-';
+ }
+ leadingBlank = false;
+ break;
+ }
+
+ case '-':
+ /* A line beginning with --- delimits a code section.
+ * inCode tells us if it is start or end of a code section.
+ */
+ if (leadingBlank)
+ {
+ if (!inCode && c == '-')
+ {
+ const list = MarkdownList.parseItem(buf, iLineStart, i);
+ if (list.isValid)
+ {
+ if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc))
+ {
+ removeBlankLineMacro(buf, iPrecedingBlankLine, i);
+ iParagraphStart = skipChars(buf, i+1, " \t\r\n");
+ break;
+ }
+ else
+ goto case '+';
+ }
+ }
+
+ size_t istart = i;
+ size_t eollen = 0;
+ leadingBlank = false;
+ const c0 = c; // if we jumped here from case '`' or case '~'
+ size_t iInfoString = 0;
+ if (!inCode)
+ codeLanguage.length = 0;
+ while (1)
+ {
+ ++i;
+ if (i >= buf.length)
+ break;
+ c = buf[i];
+ if (c == '\n')
+ {
+ eollen = 1;
+ break;
+ }
+ if (c == '\r')
+ {
+ eollen = 1;
+ if (i + 1 >= buf.length)
+ break;
+ if (buf[i + 1] == '\n')
+ {
+ eollen = 2;
+ break;
+ }
+ }
+ // BUG: handle UTF PS and LS too
+ if (c != c0 || iInfoString)
+ {
+ if (global.params.markdown && !iInfoString && !inCode && i - istart >= 3)
+ {
+ // Start a Markdown info string, like ```ruby
+ codeFenceLength = i - istart;
+ i = iInfoString = skipChars(buf, i, " \t");
+ }
+ else if (iInfoString && c != '`')
+ {
+ if (!codeLanguage.length && (c == ' ' || c == '\t'))
+ codeLanguage = cast(string) buf[iInfoString..i].idup;
+ }
+ else
+ {
+ iInfoString = 0;
+ goto Lcont;
+ }
+ }
+ }
+ if (i - istart < 3 || (inCode && (inCode != c0 || (inCode != '-' && i - istart < codeFenceLength))))
+ goto Lcont;
+ if (iInfoString)
+ {
+ if (!codeLanguage.length)
+ codeLanguage = cast(string) buf[iInfoString..i].idup;
+ }
+ else
+ codeFenceLength = i - istart;
+
+ // We have the start/end of a code section
+ // Remove the entire --- line, including blanks and \n
+ buf.remove(iLineStart, i - iLineStart + eollen);
+ i = iLineStart;
+ if (eollen)
+ leadingBlank = true;
+ if (inCode && (i <= iCodeStart))
+ {
+ // Empty code section, just remove it completely.
+ inCode = 0;
+ break;
+ }
+ if (inCode)
+ {
+ inCode = 0;
+ // The code section is from iCodeStart to i
+ OutBuffer codebuf;
+ codebuf.write(buf[iCodeStart .. i]);
+ codebuf.writeByte(0);
+ // Remove leading indentations from all lines
+ bool lineStart = true;
+ char* endp = cast(char*)codebuf[].ptr + codebuf.length;
+ for (char* p = cast(char*)codebuf[].ptr; p < endp;)
+ {
+ if (lineStart)
+ {
+ size_t j = codeIndent;
+ char* q = p;
+ while (j-- > 0 && q < endp && isIndentWS(q))
+ ++q;
+ codebuf.remove(p - cast(char*)codebuf[].ptr, q - p);
+ assert(cast(char*)codebuf[].ptr <= p);
+ assert(p < cast(char*)codebuf[].ptr + codebuf.length);
+ lineStart = false;
+ endp = cast(char*)codebuf[].ptr + codebuf.length; // update
+ continue;
+ }
+ if (*p == '\n')
+ lineStart = true;
+ ++p;
+ }
+ if (!codeLanguage.length || codeLanguage == "dlang" || codeLanguage == "d")
+ highlightCode2(sc, a, codebuf, 0);
+ else
+ codebuf.remove(codebuf.length-1, 1); // remove the trailing 0 byte
+ escapeStrayParenthesis(loc, &codebuf, 0, false);
+ buf.remove(iCodeStart, i - iCodeStart);
+ i = buf.insert(iCodeStart, codebuf[]);
+ i = buf.insert(i, ")\n");
+ i -= 2; // in next loop, c should be '\n'
+ }
+ else
+ {
+ i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ if (!lineQuoted && quoteLevel)
+ {
+ const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
+ i += delta;
+ istart += delta;
+ }
+
+ inCode = c0;
+ codeIndent = istart - iLineStart; // save indent count
+ if (codeLanguage.length && codeLanguage != "dlang" && codeLanguage != "d")
+ {
+ // backslash-escape
+ for (size_t j; j < codeLanguage.length - 1; ++j)
+ if (codeLanguage[j] == '\\' && ispunct(codeLanguage[j + 1]))
+ codeLanguage = codeLanguage[0..j] ~ codeLanguage[j + 1..$];
+
+ if (global.params.vmarkdown)
+ message(loc, "Ddoc: adding code block for language '%.*s'", cast(int)codeLanguage.length, codeLanguage.ptr);
+
+ i = buf.insert(i, "$(OTHER_CODE ");
+ i = buf.insert(i, codeLanguage);
+ i = buf.insert(i, ",");
+ }
+ else
+ i = buf.insert(i, "$(D_CODE ");
+ iCodeStart = i;
+ i--; // place i on >
+ leadingBlank = true;
+ }
+ }
+ break;
+
+ case '_':
+ {
+ if (leadingBlank && !inCode && replaceMarkdownThematicBreak(buf, i, iLineStart, loc))
+ {
+ i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ if (!lineQuoted && quoteLevel)
+ i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
+ removeBlankLineMacro(buf, iPrecedingBlankLine, i);
+ iParagraphStart = skipChars(buf, i+1, " \t\r\n");
+ break;
+ }
+ goto default;
+ }
+
+ case '+':
+ case '0':
+ ..
+ case '9':
+ {
+ if (leadingBlank && !inCode)
+ {
+ MarkdownList list = MarkdownList.parseItem(buf, iLineStart, i);
+ if (list.isValid)
+ {
+ // Avoid starting a numbered list in the middle of a paragraph
+ if (!nestedLists.length && list.orderedStart.length &&
+ iParagraphStart < iLineStart)
+ {
+ i += list.orderedStart.length - 1;
+ break;
+ }
+
+ i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ if (!lineQuoted && quoteLevel)
+ {
+ const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
+ i += delta;
+ list.iStart += delta;
+ list.iContentStart += delta;
+ }
+
+ list.macroLevel = macroLevel;
+ list.startItem(buf, iLineStart, i, iPrecedingBlankLine, nestedLists, loc);
+ break;
+ }
+ }
+ leadingBlank = false;
+ break;
+ }
+
+ case '*':
+ {
+ if (inCode || inBacktick || !global.params.markdown)
+ {
+ leadingBlank = false;
+ break;
+ }
+
+ if (leadingBlank)
+ {
+ // Check for a thematic break
+ if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc))
+ {
+ i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ if (!lineQuoted && quoteLevel)
+ i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel);
+ removeBlankLineMacro(buf, iPrecedingBlankLine, i);
+ iParagraphStart = skipChars(buf, i+1, " \t\r\n");
+ break;
+ }
+
+ // An initial * indicates a Markdown list item
+ const list = MarkdownList.parseItem(buf, iLineStart, i);
+ if (list.isValid)
+ goto case '+';
+ }
+
+ // Markdown emphasis
+ const leftC = i > offset ? buf[i-1] : '\0';
+ size_t iAfterEmphasis = skipChars(buf, i+1, "*");
+ const rightC = iAfterEmphasis < buf.length ? buf[iAfterEmphasis] : '\0';
+ int count = cast(int) (iAfterEmphasis - i);
+ const leftFlanking = (rightC != '\0' && !isspace(rightC)) && (!ispunct(rightC) || leftC == '\0' || isspace(leftC) || ispunct(leftC));
+ const rightFlanking = (leftC != '\0' && !isspace(leftC)) && (!ispunct(leftC) || rightC == '\0' || isspace(rightC) || ispunct(rightC));
+ auto emphasis = MarkdownDelimiter(i, count, macroLevel, leftFlanking, rightFlanking, false, c);
+
+ if (!emphasis.leftFlanking && !emphasis.rightFlanking)
+ {
+ i = iAfterEmphasis - 1;
+ break;
+ }
+
+ inlineDelimiters ~= emphasis;
+ i += emphasis.count;
+ --i;
+ break;
+ }
+
+ case '!':
+ {
+ leadingBlank = false;
+
+ if (inCode || !global.params.markdown)
+ break;
+
+ if (i < buf.length-1 && buf[i+1] == '[')
+ {
+ const imageStart = MarkdownDelimiter(i, 2, macroLevel, false, false, false, c);
+ inlineDelimiters ~= imageStart;
+ ++i;
+ }
+ break;
+ }
+ case '[':
+ {
+ if (inCode || !global.params.markdown)
+ {
+ leadingBlank = false;
+ break;
+ }
+
+ const leftC = i > offset ? buf[i-1] : '\0';
+ const rightFlanking = leftC != '\0' && !isspace(leftC) && !ispunct(leftC);
+ const atParagraphStart = leadingBlank && iParagraphStart >= iLineStart;
+ const linkStart = MarkdownDelimiter(i, 1, macroLevel, false, rightFlanking, atParagraphStart, c);
+ inlineDelimiters ~= linkStart;
+ leadingBlank = false;
+ break;
+ }
+ case ']':
+ {
+ leadingBlank = false;
+
+ if (inCode || !global.params.markdown)
+ break;
+
+ for (int d = cast(int) inlineDelimiters.length - 1; d >= 0; --d)
+ {
+ const delimiter = inlineDelimiters[d];
+ if (delimiter.type == '[' || delimiter.type == '!')
+ {
+ if (delimiter.isValid &&
+ MarkdownLink.replaceLink(buf, i, loc, inlineDelimiters, d, linkReferences))
+ {
+ // if we removed a reference link then we're at line start
+ if (i <= delimiter.iStart)
+ leadingBlank = true;
+
+ // don't nest links
+ if (delimiter.type == '[')
+ for (--d; d >= 0; --d)
+ if (inlineDelimiters[d].type == '[')
+ inlineDelimiters[d].invalidate();
+ }
+ else
+ {
+ // nothing found, so kill the delimiter
+ inlineDelimiters = inlineDelimiters[0..d] ~ inlineDelimiters[d+1..$];
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case '|':
+ {
+ if (inCode || !global.params.markdown)
+ {
+ leadingBlank = false;
+ break;
+ }
+
+ tableRowDetected = true;
+ inlineDelimiters ~= MarkdownDelimiter(i, 1, macroLevel, leadingBlank, false, false, c);
+ leadingBlank = false;
+ break;
+ }
+
+ case '\\':
+ {
+ leadingBlank = false;
+ if (inCode || i+1 >= buf.length || !global.params.markdown)
+ break;
+
+ /* Escape Markdown special characters */
+ char c1 = buf[i+1];
+ if (ispunct(c1))
+ {
+ if (global.params.vmarkdown)
+ message(loc, "Ddoc: backslash-escaped %c", c1);
+
+ buf.remove(i, 1);
+
+ auto se = sc._module.escapetable.escapeChar(c1);
+ if (!se)
+ se = c1 == '$' ? "$(DOLLAR)" : c1 == ',' ? "$(COMMA)" : null;
+ if (se)
+ {
+ buf.remove(i, 1);
+ i = buf.insert(i, se);
+ i--; // point to escaped char
+ }
+ }
+ break;
+ }
+
+ case '$':
+ {
+ /* Look for the start of a macro, '$(Identifier'
+ */
+ leadingBlank = false;
+ if (inCode || inBacktick)
+ break;
+ const slice = buf[];
+ auto p = &slice[i];
+ if (p[1] == '(' && isIdStart(&p[2]))
+ ++macroLevel;
+ break;
+ }
+
+ case '(':
+ {
+ if (!inCode && i > offset && buf[i-1] != '$')
+ ++parenLevel;
+ break;
+ }
+
+ case ')':
+ { /* End of macro
+ */
+ leadingBlank = false;
+ if (inCode || inBacktick)
+ break;
+ if (parenLevel > 0)
+ --parenLevel;
+ else if (macroLevel)
+ {
+ int downToLevel = cast(int) inlineDelimiters.length;
+ while (downToLevel > 0 && inlineDelimiters[downToLevel - 1].macroLevel >= macroLevel)
+ --downToLevel;
+ if (headingLevel && headingMacroLevel >= macroLevel)
+ {
+ endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel);
+ removeBlankLineMacro(buf, iPrecedingBlankLine, i);
+ }
+ i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ while (nestedLists.length && nestedLists[$-1].macroLevel >= macroLevel)
+ {
+ i = buf.insert(i, ")\n)");
+ --nestedLists.length;
+ }
+ if (quoteLevel && quoteMacroLevel >= macroLevel)
+ i += endAllMarkdownQuotes(buf, i, quoteLevel);
+ i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters, downToLevel);
+
+ --macroLevel;
+ quoteMacroLevel = 0;
+ }
+ break;
+ }
+
+ default:
+ leadingBlank = false;
+ if (sc._module.isDocFile || inCode)
+ break;
+ const start = cast(char*)buf[].ptr + i;
+ if (isIdStart(start))
+ {
+ size_t j = skippastident(buf, i);
+ if (i < j)
+ {
+ size_t k = skippastURL(buf, i);
+ if (i < k)
+ {
+ /* The URL is buf[i..k]
+ */
+ if (macroLevel)
+ /* Leave alone if already in a macro
+ */
+ i = k - 1;
+ else
+ {
+ /* Replace URL with '$(DDOC_LINK_AUTODETECT URL)'
+ */
+ i = buf.bracket(i, "$(DDOC_LINK_AUTODETECT ", k, ")") - 1;
+ }
+ break;
+ }
+ }
+ else
+ break;
+ size_t len = j - i;
+ // leading '_' means no highlight unless it's a reserved symbol name
+ if (c == '_' && (i == 0 || !isdigit(*(start - 1))) && (i == buf.length - 1 || !isReservedName(start[0 .. len])))
+ {
+ buf.remove(i, 1);
+ i = buf.bracket(i, "$(DDOC_AUTO_PSYMBOL_SUPPRESS ", j - 1, ")") - 1;
+ break;
+ }
+ if (isIdentifier(a, start, len))
+ {
+ i = buf.bracket(i, "$(DDOC_AUTO_PSYMBOL ", j, ")") - 1;
+ break;
+ }
+ if (isKeyword(start, len))
+ {
+ i = buf.bracket(i, "$(DDOC_AUTO_KEYWORD ", j, ")") - 1;
+ break;
+ }
+ if (isFunctionParameter(a, start, len))
+ {
+ //printf("highlighting arg '%s', i = %d, j = %d\n", arg.ident.toChars(), i, j);
+ i = buf.bracket(i, "$(DDOC_AUTO_PARAM ", j, ")") - 1;
+ break;
+ }
+ i = j - 1;
+ }
+ break;
+ }
+ }
+
+ if (inCode == '-')
+ error(loc, "unmatched `---` in DDoc comment");
+ else if (inCode)
+ buf.insert(buf.length, ")");
+
+ size_t i = buf.length;
+ if (headingLevel)
+ {
+ endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel);
+ removeBlankLineMacro(buf, iPrecedingBlankLine, i);
+ }
+ i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments);
+ i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters);
+ endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel);
+}
+
+/**************************************************
+ * Highlight code for DDOC section.
+ */
+private void highlightCode(Scope* sc, Dsymbol s, ref OutBuffer buf, size_t offset)
+{
+ auto imp = s.isImport();
+ if (imp && imp.aliases.dim > 0)
+ {
+ // For example: `public import core.stdc.string : memcpy, memcmp;`
+ for(int i = 0; i < imp.aliases.dim; i++)
+ {
+ // Need to distinguish between
+ // `public import core.stdc.string : memcpy, memcmp;` and
+ // `public import core.stdc.string : copy = memcpy, compare = memcmp;`
+ auto a = imp.aliases[i];
+ auto id = a ? a : imp.names[i];
+ auto loc = Loc.init;
+ if (auto symFromId = sc.search(loc, id, null))
+ {
+ highlightCode(sc, symFromId, buf, offset);
+ }
+ }
+ }
+ else
+ {
+ OutBuffer ancbuf;
+ emitAnchor(ancbuf, s, sc);
+ buf.insert(offset, ancbuf[]);
+ offset += ancbuf.length;
+
+ Dsymbols a;
+ a.push(s);
+ highlightCode(sc, &a, buf, offset);
+ }
+}
+
+/****************************************************
+ */
+private void highlightCode(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t offset)
+{
+ //printf("highlightCode(a = '%s')\n", a.toChars());
+ bool resolvedTemplateParameters = false;
+
+ for (size_t i = offset; i < buf.length; i++)
+ {
+ char c = buf[i];
+ const se = sc._module.escapetable.escapeChar(c);
+ if (se.length)
+ {
+ buf.remove(i, 1);
+ i = buf.insert(i, se);
+ i--; // point to ';'
+ continue;
+ }
+ char* start = cast(char*)buf[].ptr + i;
+ if (isIdStart(start))
+ {
+ size_t j = skipPastIdentWithDots(buf, i);
+ if (i < j)
+ {
+ size_t len = j - i;
+ if (isIdentifier(a, start, len))
+ {
+ i = buf.bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
+ continue;
+ }
+ }
+
+ j = skippastident(buf, i);
+ if (i < j)
+ {
+ size_t len = j - i;
+ if (isIdentifier(a, start, len))
+ {
+ i = buf.bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
+ continue;
+ }
+ if (isFunctionParameter(a, start, len))
+ {
+ //printf("highlighting arg '%s', i = %d, j = %d\n", arg.ident.toChars(), i, j);
+ i = buf.bracket(i, "$(DDOC_PARAM ", j, ")") - 1;
+ continue;
+ }
+ i = j - 1;
+ }
+ }
+ else if (!resolvedTemplateParameters)
+ {
+ size_t previ = i;
+
+ // hunt for template declarations:
+ foreach (symi; 0 .. a.dim)
+ {
+ FuncDeclaration fd = (*a)[symi].isFuncDeclaration();
+
+ if (!fd || !fd.parent || !fd.parent.isTemplateDeclaration())
+ {
+ continue;
+ }
+
+ TemplateDeclaration td = fd.parent.isTemplateDeclaration();
+
+ // build the template parameters
+ Array!(size_t) paramLens;
+ paramLens.reserve(td.parameters.dim);
+
+ OutBuffer parametersBuf;
+ HdrGenState hgs;
+
+ parametersBuf.writeByte('(');
+
+ foreach (parami; 0 .. td.parameters.dim)
+ {
+ TemplateParameter tp = (*td.parameters)[parami];
+
+ if (parami)
+ parametersBuf.writestring(", ");
+
+ size_t lastOffset = parametersBuf.length;
+
+ .toCBuffer(tp, &parametersBuf, &hgs);
+
+ paramLens[parami] = parametersBuf.length - lastOffset;
+ }
+ parametersBuf.writeByte(')');
+
+ const templateParams = parametersBuf[];
+
+ //printf("templateDecl: %s\ntemplateParams: %s\nstart: %s\n", td.toChars(), templateParams, start);
+ if (start[0 .. templateParams.length] == templateParams)
+ {
+ immutable templateParamListMacro = "$(DDOC_TEMPLATE_PARAM_LIST ";
+ buf.bracket(i, templateParamListMacro.ptr, i + templateParams.length, ")");
+
+ // We have the parameter list. While we're here we might
+ // as well wrap the parameters themselves as well
+
+ // + 1 here to take into account the opening paren of the
+ // template param list
+ i += templateParamListMacro.length + 1;
+
+ foreach (const len; paramLens)
+ {
+ i = buf.bracket(i, "$(DDOC_TEMPLATE_PARAM ", i + len, ")");
+ // increment two here for space + comma
+ i += 2;
+ }
+
+ resolvedTemplateParameters = true;
+ // reset i to be positioned back before we found the template
+ // param list this assures that anything within the template
+ // param list that needs to be escaped or otherwise altered
+ // has an opportunity for that to happen outside of this context
+ i = previ;
+
+ continue;
+ }
+ }
+ }
+ }
+}
+
+/****************************************
+ */
+private void highlightCode3(Scope* sc, ref OutBuffer buf, const(char)* p, const(char)* pend)
+{
+ for (; p < pend; p++)
+ {
+ const se = sc._module.escapetable.escapeChar(*p);
+ if (se.length)
+ buf.writestring(se);
+ else
+ buf.writeByte(*p);
+ }
+}
+
+/**************************************************
+ * Highlight code for CODE section.
+ */
+private void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t offset)
+{
+ uint errorsave = global.startGagging();
+
+ scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1);
+ OutBuffer res;
+ const(char)* lastp = cast(char*)buf[].ptr;
+ //printf("highlightCode2('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr);
+ res.reserve(buf.length);
+ while (1)
+ {
+ Token tok;
+ lex.scan(&tok);
+ highlightCode3(sc, res, lastp, tok.ptr);
+ string highlight = null;
+ switch (tok.value)
+ {
+ case TOK.identifier:
+ {
+ if (!sc)
+ break;
+ size_t len = lex.p - tok.ptr;
+ if (isIdentifier(a, tok.ptr, len))
+ {
+ highlight = "$(D_PSYMBOL ";
+ break;
+ }
+ if (isFunctionParameter(a, tok.ptr, len))
+ {
+ //printf("highlighting arg '%s', i = %d, j = %d\n", arg.ident.toChars(), i, j);
+ highlight = "$(D_PARAM ";
+ break;
+ }
+ break;
+ }
+ case TOK.comment:
+ highlight = "$(D_COMMENT ";
+ break;
+ case TOK.string_:
+ highlight = "$(D_STRING ";
+ break;
+ default:
+ if (tok.isKeyword())
+ highlight = "$(D_KEYWORD ";
+ break;
+ }
+ if (highlight)
+ {
+ res.writestring(highlight);
+ size_t o = res.length;
+ highlightCode3(sc, res, tok.ptr, lex.p);
+ if (tok.value == TOK.comment || tok.value == TOK.string_)
+ /* https://issues.dlang.org/show_bug.cgi?id=7656
+ * https://issues.dlang.org/show_bug.cgi?id=7715
+ * https://issues.dlang.org/show_bug.cgi?id=10519
+ */
+ escapeDdocString(&res, o);
+ res.writeByte(')');
+ }
+ else
+ highlightCode3(sc, res, tok.ptr, lex.p);
+ if (tok.value == TOK.endOfFile)
+ break;
+ lastp = lex.p;
+ }
+ buf.setsize(offset);
+ buf.write(&res);
+ global.endGagging(errorsave);
+}
+
+/****************************************
+ * Determine if p points to the start of a "..." parameter identifier.
+ */
+private bool isCVariadicArg(const(char)[] p)
+{
+ return p.length >= 3 && p[0 .. 3] == "...";
+}
+
+/****************************************
+ * Determine if p points to the start of an identifier.
+ */
+bool isIdStart(const(char)* p)
+{
+ dchar c = *p;
+ if (isalpha(c) || c == '_')
+ return true;
+ if (c >= 0x80)
+ {
+ size_t i = 0;
+ if (utf_decodeChar(p[0 .. 4], i, c))
+ return false; // ignore errors
+ if (isUniAlpha(c))
+ return true;
+ }
+ return false;
+}
+
+/****************************************
+ * Determine if p points to the rest of an identifier.
+ */
+bool isIdTail(const(char)* p)
+{
+ dchar c = *p;
+ if (isalnum(c) || c == '_')
+ return true;
+ if (c >= 0x80)
+ {
+ size_t i = 0;
+ if (utf_decodeChar(p[0 .. 4], i, c))
+ return false; // ignore errors
+ if (isUniAlpha(c))
+ return true;
+ }
+ return false;
+}
+
+/****************************************
+ * Determine if p points to the indentation space.
+ */
+private bool isIndentWS(const(char)* p)
+{
+ return (*p == ' ') || (*p == '\t');
+}
+
+/*****************************************
+ * Return number of bytes in UTF character.
+ */
+int utfStride(const(char)* p)
+{
+ dchar c = *p;
+ if (c < 0x80)
+ return 1;
+ size_t i = 0;
+ utf_decodeChar(p[0 .. 4], i, c); // ignore errors, but still consume input
+ return cast(int)i;
+}
+
+private inout(char)* stripLeadingNewlines(inout(char)* s)
+{
+ while (s && *s == '\n' || *s == '\r')
+ s++;
+
+ return s;
+}
diff --git a/gcc/d/dmd/doc.h b/gcc/d/dmd/doc.h
index 6d13ab1c121..a144417daee 100644
--- a/gcc/d/dmd/doc.h
+++ b/gcc/d/dmd/doc.h
@@ -5,15 +5,11 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/doc.h
+ * https://github.com/dlang/dmd/blob/master/src/dmd/doc.h
*/
#pragma once
-#include "root/dsystem.h"
-
class Module;
-struct OutBuffer;
-void escapeDdocString(OutBuffer *buf, size_t start);
void gendocfile(Module *m);
diff --git a/gcc/d/dmd/dscope.c b/gcc/d/dmd/dscope.c
deleted file mode 100644
index e56f3936ee0..00000000000
--- a/gcc/d/dmd/dscope.c
+++ /dev/null
@@ -1,646 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/scope.c
- */
-
-#include "root/dsystem.h" // strlen()
-#include "root/root.h"
-#include "root/rmem.h"
-#include "root/speller.h"
-
-#include "mars.h"
-#include "init.h"
-#include "identifier.h"
-#include "scope.h"
-#include "attrib.h"
-#include "dsymbol.h"
-#include "declaration.h"
-#include "statement.h"
-#include "aggregate.h"
-#include "module.h"
-#include "id.h"
-#include "target.h"
-#include "template.h"
-
-Scope *Scope::freelist = NULL;
-
-void allocFieldinit(Scope *sc, size_t dim)
-{
- sc->fieldinit = (unsigned *)mem.xcalloc(sizeof(unsigned), dim);
- sc->fieldinit_dim = dim;
-}
-
-void freeFieldinit(Scope *sc)
-{
- if (sc->fieldinit)
- mem.xfree(sc->fieldinit);
- sc->fieldinit = NULL;
- sc->fieldinit_dim = 0;
-}
-
-Scope *Scope::alloc()
-{
- if (freelist)
- {
- Scope *s = freelist;
- freelist = s->enclosing;
- //printf("freelist %p\n", s);
- assert(s->flags & SCOPEfree);
- s->flags &= ~SCOPEfree;
- return s;
- }
-
- return new Scope();
-}
-
-Scope::Scope()
-{
- // Create root scope
-
- //printf("Scope::Scope() %p\n", this);
- this->_module = NULL;
- this->scopesym = NULL;
- this->enclosing = NULL;
- this->parent = NULL;
- this->sw = NULL;
- this->tf = NULL;
- this->os = NULL;
- this->tinst = NULL;
- this->minst = NULL;
- this->sbreak = NULL;
- this->scontinue = NULL;
- this->fes = NULL;
- this->callsc = NULL;
- this->aligndecl = NULL;
- this->func = NULL;
- this->slabel = NULL;
- this->linkage = LINKd;
- this->cppmangle = CPPMANGLEdefault;
- this->inlining = PINLINEdefault;
- this->protection = Prot(Prot::public_);
- this->explicitProtection = 0;
- this->stc = 0;
- this->depdecl = NULL;
- this->inunion = 0;
- this->nofree = 0;
- this->noctor = 0;
- this->intypeof = 0;
- this->lastVar = NULL;
- this->callSuper = 0;
- this->fieldinit = NULL;
- this->fieldinit_dim = 0;
- this->flags = 0;
- this->lastdc = NULL;
- this->anchorCounts = NULL;
- this->prevAnchor = NULL;
- this->userAttribDecl = NULL;
-}
-
-Scope *Scope::copy()
-{
- Scope *sc = Scope::alloc();
- *sc = *this; // memcpy
-
- /* Bugzilla 11777: The copied scope should not inherit fieldinit.
- */
- sc->fieldinit = NULL;
-
- return sc;
-}
-
-Scope *Scope::createGlobal(Module *_module)
-{
- Scope *sc = Scope::alloc();
- *sc = Scope(); // memset
-
- sc->aligndecl = NULL;
- sc->linkage = LINKd;
- sc->inlining = PINLINEdefault;
- sc->protection = Prot(Prot::public_);
-
- sc->_module = _module;
-
- sc->tinst = NULL;
- sc->minst = _module;
-
- sc->scopesym = new ScopeDsymbol();
- sc->scopesym->symtab = new DsymbolTable();
-
- // Add top level package as member of this global scope
- Dsymbol *m = _module;
- while (m->parent)
- m = m->parent;
- m->addMember(NULL, sc->scopesym);
- m->parent = NULL; // got changed by addMember()
-
- // Create the module scope underneath the global scope
- sc = sc->push(_module);
- sc->parent = _module;
- return sc;
-}
-
-Scope *Scope::push()
-{
- Scope *s = copy();
-
- //printf("Scope::push(this = %p) new = %p\n", this, s);
- assert(!(flags & SCOPEfree));
- s->scopesym = NULL;
- s->enclosing = this;
- s->slabel = NULL;
- s->nofree = 0;
- s->fieldinit = saveFieldInit();
- s->flags = (flags & (SCOPEcontract | SCOPEdebug | SCOPEctfe | SCOPEcompile | SCOPEconstraint |
- SCOPEnoaccesscheck | SCOPEignoresymbolvisibility |
- SCOPEprintf | SCOPEscanf));
- s->lastdc = NULL;
-
- assert(this != s);
- return s;
-}
-
-Scope *Scope::push(ScopeDsymbol *ss)
-{
- //printf("Scope::push(%s)\n", ss->toChars());
- Scope *s = push();
- s->scopesym = ss;
- return s;
-}
-
-Scope *Scope::pop()
-{
- //printf("Scope::pop() %p nofree = %d\n", this, nofree);
- Scope *enc = enclosing;
-
- if (enclosing)
- {
- enclosing->callSuper |= callSuper;
- if (fieldinit)
- {
- if (enclosing->fieldinit)
- {
- assert(fieldinit != enclosing->fieldinit);
- size_t dim = fieldinit_dim;
- for (size_t i = 0; i < dim; i++)
- enclosing->fieldinit[i] |= fieldinit[i];
- }
- freeFieldinit(this);
- }
- }
-
- if (!nofree)
- {
- enclosing = freelist;
- freelist = this;
- flags |= SCOPEfree;
- }
-
- return enc;
-}
-
-Scope *Scope::startCTFE()
-{
- Scope *sc = this->push();
- sc->flags = this->flags | SCOPEctfe;
- return sc;
-}
-
-Scope *Scope::endCTFE()
-{
- assert(flags & SCOPEctfe);
- return pop();
-}
-
-void Scope::mergeCallSuper(Loc loc, unsigned cs)
-{
- // This does a primitive flow analysis to support the restrictions
- // regarding when and how constructors can appear.
- // It merges the results of two paths.
- // The two paths are callSuper and cs; the result is merged into callSuper.
-
- if (cs != callSuper)
- {
- // Have ALL branches called a constructor?
- int aAll = (cs & (CSXthis_ctor | CSXsuper_ctor)) != 0;
- int bAll = (callSuper & (CSXthis_ctor | CSXsuper_ctor)) != 0;
-
- // Have ANY branches called a constructor?
- bool aAny = (cs & CSXany_ctor) != 0;
- bool bAny = (callSuper & CSXany_ctor) != 0;
-
- // Have any branches returned?
- bool aRet = (cs & CSXreturn) != 0;
- bool bRet = (callSuper & CSXreturn) != 0;
-
- // Have any branches halted?
- bool aHalt = (cs & CSXhalt) != 0;
- bool bHalt = (callSuper & CSXhalt) != 0;
-
- bool ok = true;
-
- if (aHalt && bHalt)
- {
- callSuper = CSXhalt;
- }
- else if ((!aHalt && aRet && !aAny && bAny) ||
- (!bHalt && bRet && !bAny && aAny))
- {
- // If one has returned without a constructor call, there must be never
- // have been ctor calls in the other.
- ok = false;
- }
- else if (aHalt || (aRet && aAll))
- {
- // If one branch has called a ctor and then exited, anything the
- // other branch has done is OK (except returning without a
- // ctor call, but we already checked that).
- callSuper |= cs & (CSXany_ctor | CSXlabel);
- }
- else if (bHalt || (bRet && bAll))
- {
- callSuper = cs | (callSuper & (CSXany_ctor | CSXlabel));
- }
- else
- {
- // Both branches must have called ctors, or both not.
- ok = (aAll == bAll);
- // If one returned without a ctor, we must remember that
- // (Don't bother if we've already found an error)
- if (ok && aRet && !aAny)
- callSuper |= CSXreturn;
- callSuper |= cs & (CSXany_ctor | CSXlabel);
- }
- if (!ok)
- error(loc, "one path skips constructor");
- }
-}
-
-unsigned *Scope::saveFieldInit()
-{
- unsigned *fi = NULL;
- if (fieldinit) // copy
- {
- size_t dim = fieldinit_dim;
- fi = (unsigned *)mem.xmalloc(sizeof(unsigned) * dim);
- for (size_t i = 0; i < dim; i++)
- fi[i] = fieldinit[i];
- }
- return fi;
-}
-
-/****************************************
- * Merge `b` flow analysis results into `a`.
- * Params:
- * a = the path to merge fi into
- * b = the other path
- * Returns:
- * false means either `a` or `b` skips initialization
- */
-static bool mergeFieldInit(unsigned &a, const unsigned b)
-{
- if (b == a)
- return true;
-
- // Have any branches returned?
- bool aRet = (a & CSXreturn) != 0;
- bool bRet = (b & CSXreturn) != 0;
-
- // Have any branches halted?
- bool aHalt = (a & CSXhalt) != 0;
- bool bHalt = (b & CSXhalt) != 0;
-
- if (aHalt && bHalt)
- {
- a = CSXhalt;
- return true;
- }
-
- // The logic here is to prefer the branch that neither halts nor returns.
- bool ok;
- if (!bHalt && bRet)
- {
- // Branch b returns, no merging required.
- ok = (b & CSXthis_ctor);
- }
- else if (!aHalt && aRet)
- {
- // Branch a returns, but b doesn't, b takes precedence.
- ok = (a & CSXthis_ctor);
- a = b;
- }
- else if (bHalt)
- {
- // Branch b halts, no merging required.
- ok = (a & CSXthis_ctor);
- }
- else if (aHalt)
- {
- // Branch a halts, but b doesn't, b takes precedence
- ok = (b & CSXthis_ctor);
- a = b;
- }
- else
- {
- // Neither branch returns nor halts, merge flags
- ok = !((a ^ b) & CSXthis_ctor);
- a |= b;
- }
- return ok;
-}
-
-void Scope::mergeFieldInit(Loc loc, unsigned *fies)
-{
- if (fieldinit && fies)
- {
- FuncDeclaration *f = func;
- if (fes) f = fes->func;
- AggregateDeclaration *ad = f->isMember2();
- assert(ad);
-
- for (size_t i = 0; i < ad->fields.length; i++)
- {
- VarDeclaration *v = ad->fields[i];
- bool mustInit = (v->storage_class & STCnodefaultctor ||
- v->type->needsNested());
-
- if (!::mergeFieldInit(fieldinit[i], fies[i]) && mustInit)
- {
- ::error(loc, "one path skips field %s", v->toChars());
- }
- }
- }
-}
-
-Module *Scope::instantiatingModule()
-{
- // TODO: in speculative context, returning 'module' is correct?
- return minst ? minst : _module;
-}
-
-static Dsymbol *searchScopes(Scope *scope, Loc loc, Identifier *ident, Dsymbol **pscopesym, int flags)
-{
- for (Scope *sc = scope; sc; sc = sc->enclosing)
- {
- assert(sc != sc->enclosing);
- if (!sc->scopesym)
- continue;
- //printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc->scopesym->toChars(), sc->scopesym->kind(), flags);
-
- if (sc->scopesym->isModule())
- flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
-
- if (Dsymbol *s = sc->scopesym->search(loc, ident, flags))
- {
- if (!(flags & (SearchImportsOnly | IgnoreErrors)) &&
- ident == Id::length && sc->scopesym->isArrayScopeSymbol() &&
- sc->enclosing && sc->enclosing->search(loc, ident, NULL, flags))
- {
- warning(s->loc, "array `length` hides other `length` name in outer scope");
- }
- if (pscopesym)
- *pscopesym = sc->scopesym;
- return s;
- }
- // Stop when we hit a module, but keep going if that is not just under the global scope
- if (sc->scopesym->isModule() && !(sc->enclosing && !sc->enclosing->enclosing))
- break;
- }
- return NULL;
-}
-
-/************************************
- * Perform unqualified name lookup by following the chain of scopes up
- * until found.
- *
- * Params:
- * loc = location to use for error messages
- * ident = name to look up
- * pscopesym = if supplied and name is found, set to scope that ident was found in
- * flags = modify search based on flags
- *
- * Returns:
- * symbol if found, null if not
- */
-Dsymbol *Scope::search(Loc loc, Identifier *ident, Dsymbol **pscopesym, int flags)
-{
- // This function is called only for unqualified lookup
- assert(!(flags & (SearchLocalsOnly | SearchImportsOnly)));
-
- /* If ident is "start at module scope", only look at module scope
- */
- if (ident == Id::empty)
- {
- // Look for module scope
- for (Scope *sc = this; sc; sc = sc->enclosing)
- {
- assert(sc != sc->enclosing);
- if (!sc->scopesym)
- continue;
-
- if (Dsymbol *s = sc->scopesym->isModule())
- {
- if (pscopesym)
- *pscopesym = sc->scopesym;
- return s;
- }
- }
- return NULL;
- }
-
- if (this->flags & SCOPEignoresymbolvisibility)
- flags |= IgnoreSymbolVisibility;
-
- // First look in local scopes
- Dsymbol *s = searchScopes(this, loc, ident, pscopesym, flags | SearchLocalsOnly);
- if (!s)
- {
- // Second look in imported modules
- s = searchScopes(this, loc, ident, pscopesym, flags | SearchImportsOnly);
- }
- return s;
-}
-
-Dsymbol *Scope::insert(Dsymbol *s)
-{
- if (VarDeclaration *vd = s->isVarDeclaration())
- {
- if (lastVar)
- vd->lastVar = lastVar;
- lastVar = vd;
- }
- else if (WithScopeSymbol *ss = s->isWithScopeSymbol())
- {
- if (VarDeclaration *wthis = ss->withstate->wthis)
- {
- if (lastVar)
- wthis->lastVar = lastVar;
- lastVar = wthis;
- }
- return NULL;
- }
- for (Scope *sc = this; sc; sc = sc->enclosing)
- {
- //printf("\tsc = %p\n", sc);
- if (sc->scopesym)
- {
- //printf("\t\tsc->scopesym = %p\n", sc->scopesym);
- if (!sc->scopesym->symtab)
- sc->scopesym->symtab = new DsymbolTable();
- return sc->scopesym->symtabInsert(s);
- }
- }
- assert(0);
- return NULL;
-}
-
-/********************************************
- * Search enclosing scopes for ClassDeclaration.
- */
-
-ClassDeclaration *Scope::getClassScope()
-{
- for (Scope *sc = this; sc; sc = sc->enclosing)
- {
- if (!sc->scopesym)
- continue;
-
- ClassDeclaration *cd = sc->scopesym->isClassDeclaration();
- if (cd)
- return cd;
- }
- return NULL;
-}
-
-/********************************************
- * Search enclosing scopes for ClassDeclaration.
- */
-
-AggregateDeclaration *Scope::getStructClassScope()
-{
- for (Scope *sc = this; sc; sc = sc->enclosing)
- {
- if (!sc->scopesym)
- continue;
-
- AggregateDeclaration *ad = sc->scopesym->isClassDeclaration();
- if (ad)
- return ad;
- ad = sc->scopesym->isStructDeclaration();
- if (ad)
- return ad;
- }
- return NULL;
-}
-
-/*******************************************
- * For TemplateDeclarations, we need to remember the Scope
- * where it was declared. So mark the Scope as not
- * to be free'd.
- */
-
-void Scope::setNoFree()
-{
- //int i = 0;
-
- //printf("Scope::setNoFree(this = %p)\n", this);
- for (Scope *sc = this; sc; sc = sc->enclosing)
- {
- //printf("\tsc = %p\n", sc);
- sc->nofree = 1;
-
- assert(!(flags & SCOPEfree));
- //assert(sc != sc->enclosing);
- //assert(!sc->enclosing || sc != sc->enclosing->enclosing);
- //if (++i == 10)
- //assert(0);
- }
-}
-
-structalign_t Scope::alignment()
-{
- if (aligndecl)
- return aligndecl->getAlignment(this);
- else
- return STRUCTALIGN_DEFAULT;
-}
-
-/************************************************
- * Given the failed search attempt, try to find
- * one with a close spelling.
- */
-
-static void *scope_search_fp(void *arg, const char *seed, int* cost)
-{
- //printf("scope_search_fp('%s')\n", seed);
-
- /* If not in the lexer's string table, it certainly isn't in the symbol table.
- * Doing this first is a lot faster.
- */
- size_t len = strlen(seed);
- if (!len)
- return NULL;
- Identifier *id = Identifier::lookup(seed, len);
- if (!id)
- return NULL;
-
- Scope *sc = (Scope *)arg;
- Module::clearCache();
- Dsymbol *scopesym = NULL;
- Dsymbol *s = sc->search(Loc(), id, &scopesym, IgnoreErrors);
- if (s)
- {
- for (*cost = 0; sc; sc = sc->enclosing, (*cost)++)
- if (sc->scopesym == scopesym)
- break;
- if (scopesym != s->parent)
- {
- (*cost)++; // got to the symbol through an import
- if (s->prot().kind == Prot::private_)
- return NULL;
- }
- }
- return (void*)s;
-}
-
-Dsymbol *Scope::search_correct(Identifier *ident)
-{
- if (global.gag)
- return NULL; // don't do it for speculative compiles; too time consuming
-
- Dsymbol *scopesym = NULL;
- // search for exact name first
- if (Dsymbol *s = search(Loc(), ident, &scopesym, IgnoreErrors))
- return s;
- return (Dsymbol *)speller(ident->toChars(), &scope_search_fp, this, idchars);
-}
-
-/************************************
- * Maybe `ident` was a C or C++ name. Check for that,
- * and suggest the D equivalent.
- * Params:
- * ident = unknown identifier
- * Returns:
- * D identifier string if found, null if not
- */
-const char *Scope::search_correct_C(Identifier *ident)
-{
- TOK tok;
- if (ident == Id::C_NULL)
- tok = TOKnull;
- else if (ident == Id::C_TRUE)
- tok = TOKtrue;
- else if (ident == Id::C_FALSE)
- tok = TOKfalse;
- else if (ident == Id::C_unsigned)
- tok = TOKuns32;
- else if (ident == Id::C_wchar_t)
- tok = target.c.twchar_t->ty == Twchar ? TOKwchar : TOKdchar;
- else
- return NULL;
- return Token::toChars(tok);
-}
diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d
new file mode 100644
index 00000000000..638fc7e5295
--- /dev/null
+++ b/gcc/d/dmd/dscope.d
@@ -0,0 +1,768 @@
+/**
+ * A scope as defined by curly braces `{}`.
+ *
+ * Not to be confused with the `scope` storage class.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dscope.d, _dscope.d)
+ * Documentation: https://dlang.org/phobos/dmd_dscope.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dscope.d
+ */
+
+module dmd.dscope;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.ctorflow;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dmodule;
+import dmd.doc;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.expression;
+import dmd.errors;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.root.speller;
+import dmd.statement;
+import dmd.target;
+import dmd.tokens;
+
+//version=LOGSEARCH;
+
+
+// List of flags that can be applied to this `Scope`
+enum SCOPE
+{
+ ctor = 0x0001, /// constructor type
+ noaccesscheck = 0x0002, /// don't do access checks
+ condition = 0x0004, /// inside static if/assert condition
+ debug_ = 0x0008, /// inside debug conditional
+ constraint = 0x0010, /// inside template constraint
+ invariant_ = 0x0020, /// inside invariant code
+ require = 0x0040, /// inside in contract code
+ ensure = 0x0060, /// inside out contract code
+ contract = 0x0060, /// [mask] we're inside contract code
+ ctfe = 0x0080, /// inside a ctfe-only expression
+ compile = 0x0100, /// inside __traits(compile)
+ ignoresymbolvisibility = 0x0200, /// ignore symbol visibility
+ /// https://issues.dlang.org/show_bug.cgi?id=15907
+ onlysafeaccess = 0x0400, /// unsafe access is not allowed for @safe code
+ Cfile = 0x0800, /// C semantics apply
+ free = 0x8000, /// is on free list
+
+ fullinst = 0x10000, /// fully instantiate templates
+ alias_ = 0x20000, /// inside alias declaration.
+
+ // The following are mutually exclusive
+ printf = 0x4_0000, /// printf-style function
+ scanf = 0x8_0000, /// scanf-style function
+}
+
+/// Flags that are carried along with a scope push()
+private enum PersistentFlags =
+ SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint |
+ SCOPE.noaccesscheck | SCOPE.onlysafeaccess | SCOPE.ignoresymbolvisibility |
+ SCOPE.printf | SCOPE.scanf | SCOPE.Cfile;
+
+struct Scope
+{
+ Scope* enclosing; /// enclosing Scope
+
+ Module _module; /// Root module
+ ScopeDsymbol scopesym; /// current symbol
+ FuncDeclaration func; /// function we are in
+ Dsymbol parent; /// parent to use
+ LabelStatement slabel; /// enclosing labelled statement
+ SwitchStatement sw; /// enclosing switch statement
+ Statement tryBody; /// enclosing _body of TryCatchStatement or TryFinallyStatement
+ TryFinallyStatement tf; /// enclosing try finally statement
+ ScopeGuardStatement os; /// enclosing scope(xxx) statement
+ Statement sbreak; /// enclosing statement that supports "break"
+ Statement scontinue; /// enclosing statement that supports "continue"
+ ForeachStatement fes; /// if nested function for ForeachStatement, this is it
+ Scope* callsc; /// used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
+ Dsymbol inunion; /// != null if processing members of a union
+ bool nofree; /// true if shouldn't free it
+ bool inLoop; /// true if inside a loop (where constructor calls aren't allowed)
+ int intypeof; /// in typeof(exp)
+ VarDeclaration lastVar; /// Previous symbol used to prevent goto-skips-init
+
+ /* If minst && !tinst, it's in definitely non-speculative scope (eg. module member scope).
+ * If !minst && !tinst, it's in definitely speculative scope (eg. template constraint).
+ * If minst && tinst, it's in instantiated code scope without speculation.
+ * If !minst && tinst, it's in instantiated code scope with speculation.
+ */
+ Module minst; /// root module where the instantiated templates should belong to
+ TemplateInstance tinst; /// enclosing template instance
+
+ CtorFlow ctorflow; /// flow analysis for constructors
+
+ /// alignment for struct members
+ AlignDeclaration aligndecl;
+
+ /// C++ namespace this symbol is in
+ CPPNamespaceDeclaration namespace;
+
+ /// linkage for external functions
+ LINK linkage = LINK.d;
+
+ /// mangle type
+ CPPMANGLE cppmangle = CPPMANGLE.def;
+
+ /// inlining strategy for functions
+ PragmaDeclaration inlining;
+
+ /// visibility for class members
+ Visibility visibility = Visibility(Visibility.Kind.public_);
+ int explicitVisibility; /// set if in an explicit visibility attribute
+
+ StorageClass stc; /// storage class
+
+ DeprecatedDeclaration depdecl; /// customized deprecation message
+
+ uint flags;
+
+ // user defined attributes
+ UserAttributeDeclaration userAttribDecl;
+
+ DocComment* lastdc; /// documentation comment for last symbol at this scope
+ uint[void*] anchorCounts; /// lookup duplicate anchor name count
+ Identifier prevAnchor; /// qualified symbol name of last doc anchor
+
+ AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value,
+ /// do not set wasRead for it
+
+ extern (D) __gshared Scope* freelist;
+
+ extern (D) static Scope* alloc()
+ {
+ if (freelist)
+ {
+ Scope* s = freelist;
+ freelist = s.enclosing;
+ //printf("freelist %p\n", s);
+ assert(s.flags & SCOPE.free);
+ s.flags &= ~SCOPE.free;
+ return s;
+ }
+ return new Scope();
+ }
+
+ extern (D) static Scope* createGlobal(Module _module)
+ {
+ Scope* sc = Scope.alloc();
+ *sc = Scope.init;
+ sc._module = _module;
+ sc.minst = _module;
+ sc.scopesym = new ScopeDsymbol();
+ sc.scopesym.symtab = new DsymbolTable();
+ // Add top level package as member of this global scope
+ Dsymbol m = _module;
+ while (m.parent)
+ m = m.parent;
+ m.addMember(null, sc.scopesym);
+ m.parent = null; // got changed by addMember()
+ if (_module.isCFile)
+ sc.flags |= SCOPE.Cfile;
+ // Create the module scope underneath the global scope
+ sc = sc.push(_module);
+ sc.parent = _module;
+ return sc;
+ }
+
+ extern (C++) Scope* copy()
+ {
+ Scope* sc = Scope.alloc();
+ *sc = this;
+ /* https://issues.dlang.org/show_bug.cgi?id=11777
+ * The copied scope should not inherit fieldinit.
+ */
+ sc.ctorflow.fieldinit = null;
+ return sc;
+ }
+
+ extern (C++) Scope* push()
+ {
+ Scope* s = copy();
+ //printf("Scope::push(this = %p) new = %p\n", this, s);
+ assert(!(flags & SCOPE.free));
+ s.scopesym = null;
+ s.enclosing = &this;
+ debug
+ {
+ if (enclosing)
+ assert(!(enclosing.flags & SCOPE.free));
+ if (s == enclosing)
+ {
+ printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing);
+ }
+ assert(s != enclosing);
+ }
+ s.slabel = null;
+ s.nofree = false;
+ s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup;
+ s.flags = (flags & PersistentFlags);
+ s.lastdc = null;
+ assert(&this != s);
+ return s;
+ }
+
+ extern (C++) Scope* push(ScopeDsymbol ss)
+ {
+ //printf("Scope::push(%s)\n", ss.toChars());
+ Scope* s = push();
+ s.scopesym = ss;
+ return s;
+ }
+
+ extern (C++) Scope* pop()
+ {
+ //printf("Scope::pop() %p nofree = %d\n", this, nofree);
+ if (enclosing)
+ enclosing.ctorflow.OR(ctorflow);
+ ctorflow.freeFieldinit();
+
+ Scope* enc = enclosing;
+ if (!nofree)
+ {
+ if (mem.isGCEnabled)
+ this = this.init;
+ enclosing = freelist;
+ freelist = &this;
+ flags |= SCOPE.free;
+ }
+ return enc;
+ }
+
+ /*************************
+ * Similar to pop(), but the results in `this` are not folded
+ * into `enclosing`.
+ */
+ extern (D) void detach()
+ {
+ ctorflow.freeFieldinit();
+ enclosing = null;
+ pop();
+ }
+
+ extern (C++) Scope* startCTFE()
+ {
+ Scope* sc = this.push();
+ sc.flags = this.flags | SCOPE.ctfe;
+ version (none)
+ {
+ /* TODO: Currently this is not possible, because we need to
+ * unspeculative some types and symbols if they are necessary for the
+ * final executable. Consider:
+ *
+ * struct S(T) {
+ * string toString() const { return "instantiated"; }
+ * }
+ * enum x = S!int();
+ * void main() {
+ * // To call x.toString in runtime, compiler should unspeculative S!int.
+ * assert(x.toString() == "instantiated");
+ * }
+ */
+ // If a template is instantiated from CT evaluated expression,
+ // compiler can elide its code generation.
+ sc.tinst = null;
+ sc.minst = null;
+ }
+ return sc;
+ }
+
+ extern (C++) Scope* endCTFE()
+ {
+ assert(flags & SCOPE.ctfe);
+ return pop();
+ }
+
+
+ /*******************************
+ * Merge results of `ctorflow` into `this`.
+ * Params:
+ * loc = for error messages
+ * ctorflow = flow results to merge in
+ */
+ extern (D) void merge(const ref Loc loc, const ref CtorFlow ctorflow)
+ {
+ if (!mergeCallSuper(this.ctorflow.callSuper, ctorflow.callSuper))
+ error(loc, "one path skips constructor");
+
+ const fies = ctorflow.fieldinit;
+ if (this.ctorflow.fieldinit.length && fies.length)
+ {
+ FuncDeclaration f = func;
+ if (fes)
+ f = fes.func;
+ auto ad = f.isMemberDecl();
+ assert(ad);
+ foreach (i, v; ad.fields)
+ {
+ bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
+ auto fieldInit = &this.ctorflow.fieldinit[i];
+ const fiesCurrent = fies[i];
+ if (fieldInit.loc is Loc.init)
+ fieldInit.loc = fiesCurrent.loc;
+ if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit)
+ {
+ error(loc, "one path skips field `%s`", v.toChars());
+ }
+ }
+ }
+ }
+
+ /************************************
+ * Perform unqualified name lookup by following the chain of scopes up
+ * until found.
+ *
+ * Params:
+ * loc = location to use for error messages
+ * ident = name to look up
+ * pscopesym = if supplied and name is found, set to scope that ident was found in
+ * flags = modify search based on flags
+ *
+ * Returns:
+ * symbol if found, null if not
+ */
+ extern (C++) Dsymbol search(const ref Loc loc, Identifier ident, Dsymbol* pscopesym, int flags = IgnoreNone)
+ {
+ version (LOGSEARCH)
+ {
+ printf("Scope.search(%p, '%s' flags=x%x)\n", &this, ident.toChars(), flags);
+ // Print scope chain
+ for (Scope* sc = &this; sc; sc = sc.enclosing)
+ {
+ if (!sc.scopesym)
+ continue;
+ printf("\tscope %s\n", sc.scopesym.toChars());
+ }
+
+ static void printMsg(string txt, Dsymbol s)
+ {
+ printf("%.*s %s.%s, kind = '%s'\n", cast(int)txt.length, txt.ptr,
+ s.parent ? s.parent.toChars() : "", s.toChars(), s.kind());
+ }
+ }
+
+ // This function is called only for unqualified lookup
+ assert(!(flags & (SearchLocalsOnly | SearchImportsOnly)));
+
+ /* If ident is "start at module scope", only look at module scope
+ */
+ if (ident == Id.empty)
+ {
+ // Look for module scope
+ for (Scope* sc = &this; sc; sc = sc.enclosing)
+ {
+ assert(sc != sc.enclosing);
+ if (!sc.scopesym)
+ continue;
+ if (Dsymbol s = sc.scopesym.isModule())
+ {
+ //printMsg("\tfound", s);
+ if (pscopesym)
+ *pscopesym = sc.scopesym;
+ return s;
+ }
+ }
+ return null;
+ }
+
+ Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, int flags, Expression* exp)
+ {
+ import dmd.mtype;
+ if (!ad || !ad.aliasthis)
+ return null;
+
+ Declaration decl = ad.aliasthis.sym.isDeclaration();
+ if (!decl)
+ return null;
+
+ Type t = decl.type;
+ ScopeDsymbol sds;
+ TypeClass tc;
+ TypeStruct ts;
+ switch(t.ty)
+ {
+ case Tstruct:
+ ts = cast(TypeStruct)t;
+ sds = ts.sym;
+ break;
+ case Tclass:
+ tc = cast(TypeClass)t;
+ sds = tc.sym;
+ break;
+ case Tinstance:
+ sds = (cast(TypeInstance)t).tempinst;
+ break;
+ case Tenum:
+ sds = (cast(TypeEnum)t).sym;
+ break;
+ default: break;
+ }
+
+ if (!sds)
+ return null;
+
+ Dsymbol ret = sds.search(loc, ident, flags);
+ if (ret)
+ {
+ *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
+ *exp = new DotIdExp(loc, *exp, ident);
+ return ret;
+ }
+
+ if (!ts && !tc)
+ return null;
+
+ Dsymbol s;
+ *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
+ if (ts && !(ts.att & AliasThisRec.tracing))
+ {
+ ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracing);
+ s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
+ ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracing);
+ }
+ else if(tc && !(tc.att & AliasThisRec.tracing))
+ {
+ tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracing);
+ s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
+ tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracing);
+ }
+ return s;
+ }
+
+ Dsymbol searchScopes(int flags)
+ {
+ for (Scope* sc = &this; sc; sc = sc.enclosing)
+ {
+ assert(sc != sc.enclosing);
+ if (!sc.scopesym)
+ continue;
+ //printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc.scopesym.toChars(), sc.scopesym.kind(), flags);
+
+ if (sc.scopesym.isModule())
+ flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
+
+ if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
+ {
+ if (flags & TagNameSpace)
+ {
+ // ImportC: if symbol is not a tag, look for it in tag table
+ if (!s.isScopeDsymbol())
+ {
+ auto ps = cast(void*)s in sc._module.tagSymTab;
+ if (!ps)
+ goto NotFound;
+ s = *ps;
+ }
+ }
+ if (!(flags & (SearchImportsOnly | IgnoreErrors)) &&
+ ident == Id.length && sc.scopesym.isArrayScopeSymbol() &&
+ sc.enclosing && sc.enclosing.search(loc, ident, null, flags))
+ {
+ warning(s.loc, "array `length` hides other `length` name in outer scope");
+ }
+ //printMsg("\tfound local", s);
+ if (pscopesym)
+ *pscopesym = sc.scopesym;
+ return s;
+ }
+
+ NotFound:
+ if (global.params.fixAliasThis)
+ {
+ Expression exp = new ThisExp(loc);
+ Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp);
+ if (aliasSym)
+ {
+ //printf("found aliassym: %s\n", aliasSym.toChars());
+ if (pscopesym)
+ *pscopesym = new ExpressionDsymbol(exp);
+ return aliasSym;
+ }
+ }
+
+ // Stop when we hit a module, but keep going if that is not just under the global scope
+ if (sc.scopesym.isModule() && !(sc.enclosing && !sc.enclosing.enclosing))
+ break;
+ }
+ return null;
+ }
+
+ if (this.flags & SCOPE.ignoresymbolvisibility)
+ flags |= IgnoreSymbolVisibility;
+
+ // First look in local scopes
+ Dsymbol s = searchScopes(flags | SearchLocalsOnly);
+ version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s);
+ if (!s)
+ {
+ // Second look in imported modules
+ s = searchScopes(flags | SearchImportsOnly);
+ version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s);
+ }
+ return s;
+ }
+
+ extern (D) Dsymbol search_correct(Identifier ident)
+ {
+ if (global.gag)
+ return null; // don't do it for speculative compiles; too time consuming
+
+ /************************************************
+ * Given the failed search attempt, try to find
+ * one with a close spelling.
+ * Params:
+ * seed = identifier to search for
+ * cost = set to the cost, which rises with each outer scope
+ * Returns:
+ * Dsymbol if found, null if not
+ */
+ extern (D) Dsymbol scope_search_fp(const(char)[] seed, out int cost)
+ {
+ //printf("scope_search_fp('%s')\n", seed);
+ /* If not in the lexer's string table, it certainly isn't in the symbol table.
+ * Doing this first is a lot faster.
+ */
+ if (!seed.length)
+ return null;
+ Identifier id = Identifier.lookup(seed);
+ if (!id)
+ return null;
+ Scope* sc = &this;
+ Module.clearCache();
+ Dsymbol scopesym = null;
+ Dsymbol s = sc.search(Loc.initial, id, &scopesym, IgnoreErrors);
+ if (!s)
+ return null;
+
+ // Do not show `@disable`d declarations
+ if (auto decl = s.isDeclaration())
+ if (decl.storage_class & STC.disable)
+ return null;
+ // Or `deprecated` ones if we're not in a deprecated scope
+ if (s.isDeprecated() && !sc.isDeprecated())
+ return null;
+
+ for (cost = 0; sc; sc = sc.enclosing, ++cost)
+ if (sc.scopesym == scopesym)
+ break;
+ if (scopesym != s.parent)
+ {
+ ++cost; // got to the symbol through an import
+ if (s.visible().kind == Visibility.Kind.private_)
+ return null;
+ }
+ return s;
+ }
+
+ Dsymbol scopesym = null;
+ // search for exact name first
+ if (auto s = search(Loc.initial, ident, &scopesym, IgnoreErrors))
+ return s;
+ return speller!scope_search_fp(ident.toString());
+ }
+
+ /************************************
+ * Maybe `ident` was a C or C++ name. Check for that,
+ * and suggest the D equivalent.
+ * Params:
+ * ident = unknown identifier
+ * Returns:
+ * D identifier string if found, null if not
+ */
+ extern (D) static const(char)* search_correct_C(Identifier ident)
+ {
+ import dmd.astenums : Twchar;
+ TOK tok;
+ if (ident == Id.NULL)
+ tok = TOK.null_;
+ else if (ident == Id.TRUE)
+ tok = TOK.true_;
+ else if (ident == Id.FALSE)
+ tok = TOK.false_;
+ else if (ident == Id.unsigned)
+ tok = TOK.uns32;
+ else if (ident == Id.wchar_t)
+ tok = target.c.wchar_tsize == 2 ? TOK.wchar_ : TOK.dchar_;
+ else
+ return null;
+ return Token.toChars(tok);
+ }
+
+ /***************************
+ * Find the innermost scope with a symbol table.
+ * Returns:
+ * innermost scope, null if none
+ */
+ extern (D) Scope* inner() return
+ {
+ for (Scope* sc = &this; sc; sc = sc.enclosing)
+ {
+ if (sc.scopesym)
+ return sc;
+ }
+ return null;
+ }
+
+ /******************************
+ * Add symbol s to innermost symbol table.
+ * Params:
+ * s = symbol to insert
+ * Returns:
+ * null if already in table, `s` if not
+ */
+ extern (D) Dsymbol insert(Dsymbol s)
+ {
+ //printf("insert() %s\n", s.toChars());
+ if (VarDeclaration vd = s.isVarDeclaration())
+ {
+ if (lastVar)
+ vd.lastVar = lastVar;
+ lastVar = vd;
+ }
+ else if (WithScopeSymbol ss = s.isWithScopeSymbol())
+ {
+ if (VarDeclaration vd = ss.withstate.wthis)
+ {
+ if (lastVar)
+ vd.lastVar = lastVar;
+ lastVar = vd;
+ }
+ return null;
+ }
+
+ auto scopesym = inner().scopesym;
+ //printf("\t\tscopesym = %p\n", scopesym);
+ if (!scopesym.symtab)
+ scopesym.symtab = new DsymbolTable();
+ if (!(flags & SCOPE.Cfile))
+ return scopesym.symtabInsert(s);
+
+ // ImportC insert
+ if (!scopesym.symtabInsert(s)) // if already in table
+ {
+ Dsymbol s2 = scopesym.symtabLookup(s, s.ident); // s2 is existing entry
+ return handleTagSymbols(this, s, s2, scopesym);
+ }
+ return s; // inserted
+ }
+
+ /********************************************
+ * Search enclosing scopes for ScopeDsymbol.
+ */
+ ScopeDsymbol getScopesym()
+ {
+ for (Scope* sc = &this; sc; sc = sc.enclosing)
+ {
+ if (sc.scopesym)
+ return sc.scopesym;
+ }
+ return null; // not found
+ }
+
+ /********************************************
+ * Search enclosing scopes for ClassDeclaration.
+ */
+ extern (C++) ClassDeclaration getClassScope()
+ {
+ for (Scope* sc = &this; sc; sc = sc.enclosing)
+ {
+ if (!sc.scopesym)
+ continue;
+ if (ClassDeclaration cd = sc.scopesym.isClassDeclaration())
+ return cd;
+ }
+ return null;
+ }
+
+ /********************************************
+ * Search enclosing scopes for ClassDeclaration.
+ */
+ extern (C++) AggregateDeclaration getStructClassScope()
+ {
+ for (Scope* sc = &this; sc; sc = sc.enclosing)
+ {
+ if (!sc.scopesym)
+ continue;
+ if (AggregateDeclaration ad = sc.scopesym.isClassDeclaration())
+ return ad;
+ if (AggregateDeclaration ad = sc.scopesym.isStructDeclaration())
+ return ad;
+ }
+ return null;
+ }
+
+ /*******************************************
+ * For TemplateDeclarations, we need to remember the Scope
+ * where it was declared. So mark the Scope as not
+ * to be free'd.
+ */
+ extern (D) void setNoFree()
+ {
+ //int i = 0;
+ //printf("Scope::setNoFree(this = %p)\n", this);
+ for (Scope* sc = &this; sc; sc = sc.enclosing)
+ {
+ //printf("\tsc = %p\n", sc);
+ sc.nofree = true;
+ assert(!(flags & SCOPE.free));
+ //assert(sc != sc.enclosing);
+ //assert(!sc.enclosing || sc != sc.enclosing.enclosing);
+ //if (++i == 10)
+ // assert(0);
+ }
+ }
+
+ structalign_t alignment()
+ {
+ if (aligndecl)
+ return aligndecl.getAlignment(&this);
+ else
+ return STRUCTALIGN_DEFAULT;
+ }
+
+ /**********************************
+ * Checks whether the current scope (or any of its parents) is deprecated.
+ *
+ * Returns: `true` if this or any parent scope is deprecated, `false` otherwise`
+ */
+ extern(C++) bool isDeprecated() @safe @nogc pure nothrow const
+ {
+ for (const(Dsymbol)* sp = &(this.parent); *sp; sp = &(sp.parent))
+ {
+ if (sp.isDeprecated())
+ return true;
+ }
+ for (const(Scope)* sc2 = &this; sc2; sc2 = sc2.enclosing)
+ {
+ if (sc2.scopesym && sc2.scopesym.isDeprecated())
+ return true;
+
+ // If inside a StorageClassDeclaration that is deprecated
+ if (sc2.stc & STC.deprecated_)
+ return true;
+ }
+ if (_module.md && _module.md.isdeprecated)
+ {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/gcc/d/dmd/dstruct.c b/gcc/d/dmd/dstruct.c
deleted file mode 100644
index 98621594bd2..00000000000
--- a/gcc/d/dmd/dstruct.c
+++ /dev/null
@@ -1,1303 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/struct.c
- */
-
-#include "root/dsystem.h"
-#include "root/root.h"
-
-#include "errors.h"
-#include "aggregate.h"
-#include "scope.h"
-#include "mtype.h"
-#include "init.h"
-#include "declaration.h"
-#include "module.h"
-#include "id.h"
-#include "statement.h"
-#include "template.h"
-#include "tokens.h"
-#include "target.h"
-#include "utf.h"
-#include "root/ctfloat.h"
-
-Type *getTypeInfoType(Loc loc, Type *t, Scope *sc);
-void unSpeculative(Scope *sc, RootObject *o);
-bool MODimplicitConv(MOD modfrom, MOD modto);
-Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
-
-FuncDeclaration *StructDeclaration::xerreq; // object.xopEquals
-FuncDeclaration *StructDeclaration::xerrcmp; // object.xopCmp
-
-/***************************************
- * Search toString member function for TypeInfo_Struct.
- * string toString();
- */
-FuncDeclaration *search_toString(StructDeclaration *sd)
-{
- Dsymbol *s = search_function(sd, Id::tostring);
- FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL;
- if (fd)
- {
- static TypeFunction *tftostring;
- if (!tftostring)
- {
- tftostring = new TypeFunction(ParameterList(), Type::tstring, LINKd);
- tftostring = tftostring->merge()->toTypeFunction();
- }
-
- fd = fd->overloadExactMatch(tftostring);
- }
- return fd;
-}
-
-/***************************************
- * Request additonal semantic analysis for TypeInfo generation.
- */
-void semanticTypeInfo(Scope *sc, Type *t)
-{
- class FullTypeInfoVisitor : public Visitor
- {
- public:
- Scope *sc;
-
- void visit(Type *t)
- {
- Type *tb = t->toBasetype();
- if (tb != t)
- tb->accept(this);
- }
- void visit(TypeNext *t)
- {
- if (t->next)
- t->next->accept(this);
- }
- void visit(TypeBasic *) { }
- void visit(TypeVector *t)
- {
- t->basetype->accept(this);
- }
- void visit(TypeAArray *t)
- {
- t->index->accept(this);
- visit((TypeNext *)t);
- }
- void visit(TypeFunction *t)
- {
- visit((TypeNext *)t);
- // Currently TypeInfo_Function doesn't store parameter types.
- }
- void visit(TypeStruct *t)
- {
- //printf("semanticTypeInfo::visit(TypeStruct = %s)\n", t->toChars());
- StructDeclaration *sd = t->sym;
-
- /* Step 1: create TypeInfoDeclaration
- */
- if (!sc) // inline may request TypeInfo.
- {
- Scope scx;
- scx._module = sd->getModule();
- getTypeInfoType(sd->loc, t, &scx);
- sd->requestTypeInfo = true;
- }
- else if (!sc->minst)
- {
- // don't yet have to generate TypeInfo instance if
- // the typeid(T) expression exists in speculative scope.
- }
- else
- {
- getTypeInfoType(sd->loc, t, sc);
- sd->requestTypeInfo = true;
-
- // Bugzilla 15149, if the typeid operand type comes from a
- // result of auto function, it may be yet speculative.
- // unSpeculative(sc, sd);
- }
-
- /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later.
- * This should be done even if typeid(T) exists in speculative scope.
- * Because it may appear later in non-speculative scope.
- */
- if (!sd->members)
- return; // opaque struct
- if (!sd->xeq && !sd->xcmp && !sd->postblit &&
- !sd->dtor && !sd->xhash && !search_toString(sd))
- return; // none of TypeInfo-specific members
-
- // If the struct is in a non-root module, run semantic3 to get
- // correct symbols for the member function.
- if (sd->semanticRun >= PASSsemantic3)
- {
- // semantic3 is already done
- }
- else if (TemplateInstance *ti = sd->isInstantiated())
- {
- if (ti->minst && !ti->minst->isRoot())
- Module::addDeferredSemantic3(sd);
- }
- else
- {
- if (sd->inNonRoot())
- {
- //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd->toChars(), sd->inNonRoot());
- Module::addDeferredSemantic3(sd);
- }
- }
- }
- void visit(TypeClass *) { }
- void visit(TypeTuple *t)
- {
- if (t->arguments)
- {
- for (size_t i = 0; i < t->arguments->length; i++)
- {
- Type *tprm = (*t->arguments)[i]->type;
- if (tprm)
- tprm->accept(this);
- }
- }
- }
- };
-
- if (sc)
- {
- if (!sc->func)
- return;
- if (sc->intypeof)
- return;
- if (sc->flags & (SCOPEctfe | SCOPEcompile))
- return;
- }
-
- FullTypeInfoVisitor v;
- v.sc = sc;
- t->accept(&v);
-}
-
-/********************************* AggregateDeclaration ****************************/
-
-AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id)
- : ScopeDsymbol(id)
-{
- this->loc = loc;
-
- storage_class = 0;
- protection = Prot(Prot::public_);
- type = NULL;
- structsize = 0; // size of struct
- alignsize = 0; // size of struct for alignment purposes
- sizeok = SIZEOKnone; // size not determined yet
- deferred = NULL;
- isdeprecated = false;
- classKind = ClassKind::d;
- inv = NULL;
- aggNew = NULL;
- aggDelete = NULL;
-
- stag = NULL;
- sinit = NULL;
- enclosing = NULL;
- vthis = NULL;
-
- ctor = NULL;
- defaultCtor = NULL;
- aliasthis = NULL;
- noDefaultCtor = false;
- dtor = NULL;
- getRTInfo = NULL;
-}
-
-Prot AggregateDeclaration::prot()
-{
- return protection;
-}
-
-/***************************************
- * Create a new scope from sc.
- * semantic, semantic2 and semantic3 will use this for aggregate members.
- */
-Scope *AggregateDeclaration::newScope(Scope *sc)
-{
- Scope *sc2 = sc->push(this);
- sc2->stc &= STCsafe | STCtrusted | STCsystem;
- sc2->parent = this;
- if (isUnionDeclaration())
- sc2->inunion = 1;
- sc2->protection = Prot(Prot::public_);
- sc2->explicitProtection = 0;
- sc2->aligndecl = NULL;
- sc2->userAttribDecl = NULL;
- return sc2;
-}
-
-void AggregateDeclaration::setScope(Scope *sc)
-{
- // Might need a scope to resolve forward references. The check for
- // semanticRun prevents unnecessary setting of _scope during deferred
- // setScope phases for aggregates which already finished semantic().
- // Also see https://issues.dlang.org/show_bug.cgi?id=16607
- if (semanticRun < PASSsemanticdone)
- ScopeDsymbol::setScope(sc);
-}
-
-/***************************************
- * Find all instance fields, then push them into `fields`.
- *
- * Runs semantic() for all instance field variables, but also
- * the field types can reamin yet not resolved forward references,
- * except direct recursive definitions.
- * After the process sizeok is set to SIZEOKfwd.
- *
- * Returns:
- * false if any errors occur.
- */
-bool AggregateDeclaration::determineFields()
-{
- if (_scope)
- dsymbolSemantic(this, NULL);
- if (sizeok != SIZEOKnone)
- return true;
-
- //printf("determineFields() %s, fields.length = %d\n", toChars(), fields.length);
- fields.setDim(0);
-
- struct SV
- {
- AggregateDeclaration *agg;
-
- static int func(Dsymbol *s, void *param)
- {
- VarDeclaration *v = s->isVarDeclaration();
- if (!v)
- return 0;
- if (v->storage_class & STCmanifest)
- return 0;
-
- AggregateDeclaration *ad = ((SV *)param)->agg;
-
- if (v->semanticRun < PASSsemanticdone)
- dsymbolSemantic(v, NULL);
- // Note: Aggregate fields or size could have determined during v->semantic.
- if (ad->sizeok != SIZEOKnone)
- return 1;
-
- if (v->aliassym)
- return 0; // If this variable was really a tuple, skip it.
-
- if (v->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCctfe | STCtemplateparameter))
- return 0;
- if (!v->isField() || v->semanticRun < PASSsemanticdone)
- return 1; // unresolvable forward reference
-
- ad->fields.push(v);
-
- if (v->storage_class & STCref)
- return 0;
- Type *tv = v->type->baseElemOf();
- if (tv->ty != Tstruct)
- return 0;
- if (ad == ((TypeStruct *)tv)->sym)
- {
- const char *psz = (v->type->toBasetype()->ty == Tsarray) ? "static array of " : "";
- ad->error("cannot have field %s with %ssame struct type", v->toChars(), psz);
- ad->type = Type::terror;
- ad->errors = true;
- return 1;
- }
- return 0;
- }
- };
- SV sv;
- sv.agg = this;
-
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- if (s->apply(&SV::func, &sv))
- {
- if (sizeok != SIZEOKnone)
- return true;
- return false;
- }
- }
-
- if (sizeok != SIZEOKdone)
- sizeok = SIZEOKfwd;
-
- return true;
-}
-
-/***************************************
- * Collect all instance fields, then determine instance size.
- * Returns:
- * false if failed to determine the size.
- */
-bool AggregateDeclaration::determineSize(Loc loc)
-{
- //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
-
- // The previous instance size finalizing had:
- if (type->ty == Terror)
- return false; // failed already
- if (sizeok == SIZEOKdone)
- return true; // succeeded
-
- if (!members)
- {
- error(loc, "unknown size");
- return false;
- }
-
- if (_scope)
- dsymbolSemantic(this, NULL);
-
- // Determine the instance size of base class first.
- if (ClassDeclaration *cd = isClassDeclaration())
- {
- cd = cd->baseClass;
- if (cd && !cd->determineSize(loc))
- goto Lfail;
- }
-
- // Determine instance fields when sizeok == SIZEOKnone
- if (!determineFields())
- goto Lfail;
- if (sizeok != SIZEOKdone)
- finalizeSize();
-
- // this aggregate type has:
- if (type->ty == Terror)
- return false; // marked as invalid during the finalizing.
- if (sizeok == SIZEOKdone)
- return true; // succeeded to calculate instance size.
-
-Lfail:
- // There's unresolvable forward reference.
- if (type != Type::terror)
- error(loc, "no size because of forward reference");
- // Don't cache errors from speculative semantic, might be resolvable later.
- // https://issues.dlang.org/show_bug.cgi?id=16574
- if (!global.gag)
- {
- type = Type::terror;
- errors = true;
- }
- return false;
-}
-
-void StructDeclaration::semanticTypeInfoMembers()
-{
- if (xeq &&
- xeq->_scope &&
- xeq->semanticRun < PASSsemantic3done)
- {
- unsigned errors = global.startGagging();
- semantic3(xeq, xeq->_scope);
- if (global.endGagging(errors))
- xeq = xerreq;
- }
-
- if (xcmp &&
- xcmp->_scope &&
- xcmp->semanticRun < PASSsemantic3done)
- {
- unsigned errors = global.startGagging();
- semantic3(xcmp, xcmp->_scope);
- if (global.endGagging(errors))
- xcmp = xerrcmp;
- }
-
- FuncDeclaration *ftostr = search_toString(this);
- if (ftostr &&
- ftostr->_scope &&
- ftostr->semanticRun < PASSsemantic3done)
- {
- semantic3(ftostr, ftostr->_scope);
- }
-
- if (xhash &&
- xhash->_scope &&
- xhash->semanticRun < PASSsemantic3done)
- {
- semantic3(xhash, xhash->_scope);
- }
-
- if (postblit &&
- postblit->_scope &&
- postblit->semanticRun < PASSsemantic3done)
- {
- semantic3(postblit, postblit->_scope);
- }
-
- if (dtor &&
- dtor->_scope &&
- dtor->semanticRun < PASSsemantic3done)
- {
- semantic3(dtor, dtor->_scope);
- }
-}
-
-d_uns64 AggregateDeclaration::size(Loc loc)
-{
- //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
- bool ok = determineSize(loc);
- //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
- return ok ? structsize : SIZE_INVALID;
-}
-
-Type *AggregateDeclaration::getType()
-{
- return type;
-}
-
-bool AggregateDeclaration::isDeprecated()
-{
- return isdeprecated;
-}
-
-bool AggregateDeclaration::isExport() const
-{
- return protection.kind == Prot::export_;
-}
-
-/***************************************
- * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
- * field initializers have unique memory space on instance.
- * Returns:
- * true if any errors happen.
- */
-
-bool AggregateDeclaration::checkOverlappedFields()
-{
- //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
- assert(sizeok == SIZEOKdone);
- size_t nfields = fields.length;
- if (isNested())
- {
- ClassDeclaration *cd = isClassDeclaration();
- if (!cd || !cd->baseClass || !cd->baseClass->isNested())
- nfields--;
- }
- bool errors = false;
-
- // Fill in missing any elements with default initializers
- for (size_t i = 0; i < nfields; i++)
- {
- VarDeclaration *vd = fields[i];
- if (vd->errors)
- {
- errors = true;
- continue;
- }
-
- VarDeclaration *vx = vd;
- if (vd->_init && vd->_init->isVoidInitializer())
- vx = NULL;
-
- // Find overlapped fields with the hole [vd->offset .. vd->offset->size()].
- for (size_t j = 0; j < nfields; j++)
- {
- if (i == j)
- continue;
- VarDeclaration *v2 = fields[j];
- if (v2->errors)
- {
- errors = true;
- continue;
- }
- if (!vd->isOverlappedWith(v2))
- continue;
-
- // vd and v2 are overlapping.
- vd->overlapped = true;
- v2->overlapped = true;
-
- if (!MODimplicitConv(vd->type->mod, v2->type->mod))
- v2->overlapUnsafe = true;
- if (!MODimplicitConv(v2->type->mod, vd->type->mod))
- vd->overlapUnsafe = true;
-
- if (!vx)
- continue;
- if (v2->_init && v2->_init->isVoidInitializer())
- continue;
-
- if (vx->_init && v2->_init)
- {
- ::error(loc, "overlapping default initialization for field %s and %s", v2->toChars(), vd->toChars());
- errors = true;
- }
- }
- }
- return errors;
-}
-
-/***************************************
- * Fill out remainder of elements[] with default initializers for fields[].
- * Input:
- * loc: location
- * elements: explicit arguments which given to construct object.
- * ctorinit: true if the elements will be used for default initialization.
- * Returns:
- * false if any errors occur.
- * Otherwise, returns true and the missing arguments will be pushed in elements[].
- */
-bool AggregateDeclaration::fill(Loc loc, Expressions *elements, bool ctorinit)
-{
- //printf("AggregateDeclaration::fill() %s\n", toChars());
- assert(sizeok == SIZEOKdone);
- assert(elements);
- size_t nfields = fields.length - isNested();
- bool errors = false;
-
- size_t dim = elements->length;
- elements->setDim(nfields);
- for (size_t i = dim; i < nfields; i++)
- (*elements)[i] = NULL;
-
- // Fill in missing any elements with default initializers
- for (size_t i = 0; i < nfields; i++)
- {
- if ((*elements)[i])
- continue;
-
- VarDeclaration *vd = fields[i];
- VarDeclaration *vx = vd;
- if (vd->_init && vd->_init->isVoidInitializer())
- vx = NULL;
-
- // Find overlapped fields with the hole [vd->offset .. vd->offset->size()].
- size_t fieldi = i;
- for (size_t j = 0; j < nfields; j++)
- {
- if (i == j)
- continue;
- VarDeclaration *v2 = fields[j];
- if (!vd->isOverlappedWith(v2))
- continue;
-
- if ((*elements)[j])
- {
- vx = NULL;
- break;
- }
- if (v2->_init && v2->_init->isVoidInitializer())
- continue;
-
- if (1)
- {
- /* Prefer first found non-void-initialized field
- * union U { int a; int b = 2; }
- * U u; // Error: overlapping initialization for field a and b
- */
- if (!vx)
- {
- vx = v2;
- fieldi = j;
- }
- else if (v2->_init)
- {
- ::error(loc, "overlapping initialization for field %s and %s",
- v2->toChars(), vd->toChars());
- errors = true;
- }
- }
- else
- {
- // Will fix Bugzilla 1432 by enabling this path always
-
- /* Prefer explicitly initialized field
- * union U { int a; int b = 2; }
- * U u; // OK (u.b == 2)
- */
- if (!vx || (!vx->_init && v2->_init))
- {
- vx = v2;
- fieldi = j;
- }
- else if (vx != vd && !vx->isOverlappedWith(v2))
- {
- // Both vx and v2 fills vd, but vx and v2 does not overlap
- }
- else if (vx->_init && v2->_init)
- {
- ::error(loc, "overlapping default initialization for field %s and %s",
- v2->toChars(), vd->toChars());
- errors = true;
- }
- else
- assert(vx->_init || (!vx->_init && !v2->_init));
- }
- }
- if (vx)
- {
- Expression *e;
- if (vx->type->size() == 0)
- {
- e = NULL;
- }
- else if (vx->_init)
- {
- assert(!vx->_init->isVoidInitializer());
- if (vx->inuse) // https://issues.dlang.org/show_bug.cgi?id=18057
- {
- vx->error(loc, "recursive initialization of field");
- errors = true;
- e = NULL;
- }
- else
- e = vx->getConstInitializer(false);
- }
- else
- {
- if ((vx->storage_class & STCnodefaultctor) && !ctorinit)
- {
- ::error(loc, "field %s.%s must be initialized because it has no default constructor",
- type->toChars(), vx->toChars());
- errors = true;
- }
-
- /* Bugzilla 12509: Get the element of static array type.
- */
- Type *telem = vx->type;
- if (telem->ty == Tsarray)
- {
- /* We cannot use Type::baseElemOf() here.
- * If the bottom of the Tsarray is an enum type, baseElemOf()
- * will return the base of the enum, and its default initializer
- * would be different from the enum's.
- */
- while (telem->toBasetype()->ty == Tsarray)
- telem = ((TypeSArray *)telem->toBasetype())->next;
-
- if (telem->ty == Tvoid)
- telem = Type::tuns8->addMod(telem->mod);
- }
- if (telem->needsNested() && ctorinit)
- e = telem->defaultInit(loc);
- else
- e = telem->defaultInitLiteral(loc);
- }
- (*elements)[fieldi] = e;
- }
- }
-
- for (size_t i = 0; i < elements->length; i++)
- {
- Expression *e = (*elements)[i];
- if (e && e->op == TOKerror)
- return false;
- }
-
- return !errors;
-}
-
-/****************************
- * Do byte or word alignment as necessary.
- * Align sizes of 0, as we may not know array sizes yet.
- *
- * alignment: struct alignment that is in effect
- * size: alignment requirement of field
- */
-
-void AggregateDeclaration::alignmember(
- structalign_t alignment,
- unsigned size,
- unsigned *poffset)
-{
- //printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset);
- switch (alignment)
- {
- case (structalign_t) 1:
- // No alignment
- break;
-
- case (structalign_t) STRUCTALIGN_DEFAULT:
- // Alignment in target.fieldalignsize must match what the
- // corresponding C compiler's default alignment behavior is.
- assert(size > 0 && !(size & (size - 1)));
- *poffset = (*poffset + size - 1) & ~(size - 1);
- break;
-
- default:
- // Align on alignment boundary, which must be a positive power of 2
- assert(alignment > 0 && !(alignment & (alignment - 1)));
- *poffset = (*poffset + alignment - 1) & ~(alignment - 1);
- break;
- }
-}
-
-/****************************************
- * Place a member (mem) into an aggregate (agg), which can be a struct, union or class
- * Returns:
- * offset to place field at
- *
- * nextoffset: next location in aggregate
- * memsize: size of member
- * memalignsize: natural alignment of member
- * alignment: alignment in effect for this member
- * paggsize: size of aggregate (updated)
- * paggalignsize: alignment of aggregate (updated)
- * isunion: the aggregate is a union
- */
-unsigned AggregateDeclaration::placeField(
- unsigned *nextoffset,
- unsigned memsize,
- unsigned memalignsize,
- structalign_t alignment,
- unsigned *paggsize,
- unsigned *paggalignsize,
- bool isunion
- )
-{
- unsigned ofs = *nextoffset;
-
- const unsigned actualAlignment =
- alignment == STRUCTALIGN_DEFAULT ? memalignsize : alignment;
-
- alignmember(alignment, memalignsize, &ofs);
- unsigned memoffset = ofs;
- ofs += memsize;
- if (ofs > *paggsize)
- *paggsize = ofs;
- if (!isunion)
- *nextoffset = ofs;
-
- if (*paggalignsize < actualAlignment)
- *paggalignsize = actualAlignment;
-
- return memoffset;
-}
-
-
-/****************************************
- * Returns true if there's an extra member which is the 'this'
- * pointer to the enclosing context (enclosing aggregate or function)
- */
-
-bool AggregateDeclaration::isNested()
-{
- return enclosing != NULL;
-}
-
-/* Append vthis field (this->tupleof[$-1]) to make this aggregate type nested.
- */
-void AggregateDeclaration::makeNested()
-{
- if (enclosing) // if already nested
- return;
- if (sizeok == SIZEOKdone)
- return;
- if (isUnionDeclaration() || isInterfaceDeclaration())
- return;
- if (storage_class & STCstatic)
- return;
-
- // If nested struct, add in hidden 'this' pointer to outer scope
- Dsymbol *s = toParent2();
- if (!s)
- return;
- Type *t = NULL;
- if (FuncDeclaration *fd = s->isFuncDeclaration())
- {
- enclosing = fd;
-
- /* Bugzilla 14422: If a nested class parent is a function, its
- * context pointer (== `outer`) should be void* always.
- */
- t = Type::tvoidptr;
- }
- else if (AggregateDeclaration *ad = s->isAggregateDeclaration())
- {
- if (isClassDeclaration() && ad->isClassDeclaration())
- {
- enclosing = ad;
- }
- else if (isStructDeclaration())
- {
- if (TemplateInstance *ti = ad->parent->isTemplateInstance())
- {
- enclosing = ti->enclosing;
- }
- }
-
- t = ad->handleType();
- }
- if (enclosing)
- {
- //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing->toChars());
- assert(t);
- if (t->ty == Tstruct)
- t = Type::tvoidptr; // t should not be a ref type
- assert(!vthis);
- vthis = new ThisDeclaration(loc, t);
- //vthis->storage_class |= STCref;
-
- // Emulate vthis->addMember()
- members->push(vthis);
-
- // Emulate vthis->semantic()
- vthis->storage_class |= STCfield;
- vthis->parent = this;
- vthis->protection = Prot(Prot::public_);
- vthis->alignment = t->alignment();
- vthis->semanticRun = PASSsemanticdone;
-
- if (sizeok == SIZEOKfwd)
- fields.push(vthis);
- }
-}
-
-/*******************************************
- * Look for constructor declaration.
- */
-Dsymbol *AggregateDeclaration::searchCtor()
-{
- Dsymbol *s = search(Loc(), Id::ctor);
- if (s)
- {
- if (!(s->isCtorDeclaration() ||
- s->isTemplateDeclaration() ||
- s->isOverloadSet()))
- {
- s->error("is not a constructor; identifiers starting with __ are reserved for the implementation");
- errors = true;
- s = NULL;
- }
- }
- if (s && s->toParent() != this)
- s = NULL; // search() looks through ancestor classes
- if (s)
- {
- // Finish all constructors semantics to determine this->noDefaultCtor.
- struct SearchCtor
- {
- static int fp(Dsymbol *s, void *)
- {
- CtorDeclaration *f = s->isCtorDeclaration();
- if (f && f->semanticRun == PASSinit)
- dsymbolSemantic(f, NULL);
- return 0;
- }
- };
-
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *sm = (*members)[i];
- sm->apply(&SearchCtor::fp, NULL);
- }
- }
- return s;
-}
-
-/********************************* StructDeclaration ****************************/
-
-StructDeclaration::StructDeclaration(Loc loc, Identifier *id, bool inObject)
- : AggregateDeclaration(loc, id)
-{
- zeroInit = 0; // assume false until we do semantic processing
- hasIdentityAssign = false;
- hasIdentityEquals = false;
- postblit = NULL;
-
- xeq = NULL;
- xcmp = NULL;
- xhash = NULL;
- alignment = 0;
- ispod = ISPODfwd;
- arg1type = NULL;
- arg2type = NULL;
- requestTypeInfo = false;
-
- // For forward references
- type = new TypeStruct(this);
-
- if (inObject)
- {
- if (id == Id::ModuleInfo && !Module::moduleinfo)
- Module::moduleinfo = this;
- }
-}
-
-StructDeclaration *StructDeclaration::create(Loc loc, Identifier *id, bool inObject)
-{
- return new StructDeclaration(loc, id, inObject);
-}
-
-Dsymbol *StructDeclaration::syntaxCopy(Dsymbol *s)
-{
- StructDeclaration *sd =
- s ? (StructDeclaration *)s
- : new StructDeclaration(loc, ident, false);
- return ScopeDsymbol::syntaxCopy(sd);
-}
-
-Dsymbol *StructDeclaration::search(const Loc &loc, Identifier *ident, int flags)
-{
- //printf("%s.StructDeclaration::search('%s', flags = x%x)\n", toChars(), ident->toChars(), flags);
-
- if (_scope && !symtab)
- dsymbolSemantic(this, _scope);
-
- if (!members || !symtab) // opaque or semantic() is not yet called
- {
- error("is forward referenced when looking for `%s`", ident->toChars());
- return NULL;
- }
-
- return ScopeDsymbol::search(loc, ident, flags);
-}
-
-/**********************************
- * Determine if exp is all binary zeros.
- * Params:
- * exp = expression to check
- * Returns:
- * true if it's all binary 0
- */
-static bool isZeroInit(Expression *exp)
-{
- switch (exp->op)
- {
- case TOKint64:
- return exp->toInteger() == 0;
-
- case TOKnull:
- case TOKfalse:
- return true;
-
- case TOKstructliteral:
- {
- StructLiteralExp *sle = (StructLiteralExp *) exp;
- for (size_t i = 0; i < sle->sd->fields.length; i++)
- {
- VarDeclaration *field = sle->sd->fields[i];
- if (field->type->size(field->loc))
- {
- Expression *e = (*sle->elements)[i];
- if (e ? !isZeroInit(e)
- : !field->type->isZeroInit(field->loc))
- return false;
- }
- }
- return true;
- }
-
- case TOKarrayliteral:
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *) exp;
-
- const size_t dim = ale->elements ? ale->elements->length : 0;
-
- if (ale->type->toBasetype()->ty == Tarray) // if initializing a dynamic array
- return dim == 0;
-
- for (size_t i = 0; i < dim; i++)
- {
- if (!isZeroInit(ale->getElement(i)))
- return false;
- }
- /* Note that true is returned for all T[0]
- */
- return true;
- }
-
- case TOKstring:
- {
- StringExp *se = exp->toStringExp();
-
- if (se->type->toBasetype()->ty == Tarray) // if initializing a dynamic array
- return se->len == 0;
-
- void *s = se->string;
- for (size_t i = 0; i < se->len; i++)
- {
- dinteger_t val;
- switch (se->sz)
- {
- case 1: val = (( utf8_t *)s)[i]; break;
- case 2: val = ((utf16_t *)s)[i]; break;
- case 4: val = ((utf32_t *)s)[i]; break;
- default: assert(0); break;
- }
- if (val)
- return false;
- }
- return true;
- }
-
- case TOKvector:
- {
- VectorExp *ve = (VectorExp *) exp;
- return isZeroInit(ve->e1);
- }
-
- case TOKfloat64:
- case TOKcomplex80:
- {
- return (exp->toReal() == CTFloat::zero) &&
- (exp->toImaginary() == CTFloat::zero);
- }
-
- default:
- return false;
- }
-}
-
-void StructDeclaration::finalizeSize()
-{
- //printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
- assert(sizeok != SIZEOKdone);
-
- //printf("+StructDeclaration::finalizeSize() %s, fields.length = %d, sizeok = %d\n", toChars(), fields.length, sizeok);
-
- fields.setDim(0); // workaround
-
- // Set the offsets of the fields and determine the size of the struct
- unsigned offset = 0;
- bool isunion = isUnionDeclaration() != NULL;
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- s->setFieldOffset(this, &offset, isunion);
- }
- if (type->ty == Terror)
- return;
-
- // 0 sized struct's are set to 1 byte
- if (structsize == 0)
- {
- structsize = 1;
- alignsize = 1;
- }
-
- // Round struct size up to next alignsize boundary.
- // This will ensure that arrays of structs will get their internals
- // aligned properly.
- if (alignment == STRUCTALIGN_DEFAULT)
- structsize = (structsize + alignsize - 1) & ~(alignsize - 1);
- else
- structsize = (structsize + alignment - 1) & ~(alignment - 1);
-
- sizeok = SIZEOKdone;
-
- //printf("-StructDeclaration::finalizeSize() %s, fields.length = %d, structsize = %d\n", toChars(), fields.length, structsize);
-
- if (errors)
- return;
-
- // Calculate fields[i]->overlapped
- if (checkOverlappedFields())
- {
- errors = true;
- return;
- }
-
- // Determine if struct is all zeros or not
- zeroInit = 1;
- for (size_t i = 0; i < fields.length; i++)
- {
- VarDeclaration *vd = fields[i];
- if (vd->_init)
- {
- if (vd->_init->isVoidInitializer())
- /* Treat as 0 for the purposes of putting the initializer
- * in the BSS segment, or doing a mass set to 0
- */
- continue;
-
- // Zero size fields are zero initialized
- if (vd->type->size(vd->loc) == 0)
- continue;
-
- // Examine init to see if it is all 0s.
- Expression *exp = vd->getConstInitializer();
- if (!exp || !isZeroInit(exp))
- {
- zeroInit = 0;
- break;
- }
- }
- else if (!vd->type->isZeroInit(loc))
- {
- zeroInit = 0;
- break;
- }
- }
-
- TypeTuple *tt = target.toArgTypes(type);
- size_t dim = tt ? tt->arguments->length : 0;
- if (dim >= 1)
- {
- assert(dim <= 2);
- arg1type = (*tt->arguments)[0]->type;
- if (dim == 2)
- arg2type = (*tt->arguments)[1]->type;
- }
-}
-
-/***************************************
- * Fit elements[] to the corresponding type of field[].
- * Input:
- * loc
- * sc
- * elements The explicit arguments that given to construct object.
- * stype The constructed object type.
- * Returns false if any errors occur.
- * Otherwise, returns true and elements[] are rewritten for the output.
- */
-bool StructDeclaration::fit(Loc loc, Scope *sc, Expressions *elements, Type *stype)
-{
- if (!elements)
- return true;
-
- size_t nfields = fields.length - isNested();
- size_t offset = 0;
- for (size_t i = 0; i < elements->length; i++)
- {
- Expression *e = (*elements)[i];
- if (!e)
- continue;
-
- e = resolveProperties(sc, e);
- if (i >= nfields)
- {
- if (i == fields.length - 1 && isNested() && e->op == TOKnull)
- {
- // CTFE sometimes creates null as hidden pointer; we'll allow this.
- continue;
- }
- ::error(loc, "more initializers than fields (%d) of %s", (int)nfields, toChars());
- return false;
- }
- VarDeclaration *v = fields[i];
- if (v->offset < offset)
- {
- ::error(loc, "overlapping initialization for %s", v->toChars());
- return false;
- }
- offset = (unsigned)(v->offset + v->type->size());
-
- Type *t = v->type;
- if (stype)
- t = t->addMod(stype->mod);
- Type *origType = t;
- Type *tb = t->toBasetype();
-
- /* Look for case of initializing a static array with a too-short
- * string literal, such as:
- * char[5] foo = "abc";
- * Allow this by doing an explicit cast, which will lengthen the string
- * literal.
- */
- if (e->op == TOKstring && tb->ty == Tsarray)
- {
- StringExp *se = (StringExp *)e;
- Type *typeb = se->type->toBasetype();
- TY tynto = tb->nextOf()->ty;
- if (!se->committed &&
- (typeb->ty == Tarray || typeb->ty == Tsarray) &&
- (tynto == Tchar || tynto == Twchar || tynto == Tdchar) &&
- se->numberOfCodeUnits(tynto) < ((TypeSArray *)tb)->dim->toInteger())
- {
- e = se->castTo(sc, t);
- goto L1;
- }
- }
-
- while (!e->implicitConvTo(t) && tb->ty == Tsarray)
- {
- /* Static array initialization, as in:
- * T[3][5] = e;
- */
- t = tb->nextOf();
- tb = t->toBasetype();
- }
- if (!e->implicitConvTo(t))
- t = origType; // restore type for better diagnostic
-
- e = e->implicitCastTo(sc, t);
- L1:
- if (e->op == TOKerror)
- return false;
-
- (*elements)[i] = doCopyOrMove(sc, e);
- }
- return true;
-}
-
-/***************************************
- * Return true if struct is POD (Plain Old Data).
- * This is defined as:
- * not nested
- * no postblits, destructors, or assignment operators
- * no 'ref' fields or fields that are themselves non-POD
- * The idea being these are compatible with C structs.
- */
-bool StructDeclaration::isPOD()
-{
- // If we've already determined whether this struct is POD.
- if (ispod != ISPODfwd)
- return (ispod == ISPODyes);
-
- ispod = ISPODyes;
-
- if (enclosing || postblit || dtor)
- ispod = ISPODno;
-
- // Recursively check all fields are POD.
- for (size_t i = 0; i < fields.length; i++)
- {
- VarDeclaration *v = fields[i];
- if (v->storage_class & STCref)
- {
- ispod = ISPODno;
- break;
- }
-
- Type *tv = v->type->baseElemOf();
- if (tv->ty == Tstruct)
- {
- TypeStruct *ts = (TypeStruct *)tv;
- StructDeclaration *sd = ts->sym;
- if (!sd->isPOD())
- {
- ispod = ISPODno;
- break;
- }
- }
- }
-
- return (ispod == ISPODyes);
-}
-
-const char *StructDeclaration::kind() const
-{
- return "struct";
-}
-
-/********************************* UnionDeclaration ****************************/
-
-UnionDeclaration::UnionDeclaration(Loc loc, Identifier *id)
- : StructDeclaration(loc, id, false)
-{
-}
-
-Dsymbol *UnionDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- UnionDeclaration *ud = new UnionDeclaration(loc, ident);
- return StructDeclaration::syntaxCopy(ud);
-}
-
-const char *UnionDeclaration::kind() const
-{
- return "union";
-}
diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d
new file mode 100644
index 00000000000..80ecd361135
--- /dev/null
+++ b/gcc/d/dmd/dstruct.d
@@ -0,0 +1,610 @@
+/**
+ * Struct and union declarations.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dstruct.d, _dstruct.d)
+ * Documentation: https://dlang.org/phobos/dmd_dstruct.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dstruct.d
+ */
+
+module dmd.dstruct;
+
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.opover;
+import dmd.target;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.typinf;
+import dmd.visitor;
+
+/***************************************
+ * Search sd for a member function of the form:
+ * `extern (D) string toString();`
+ * Params:
+ * sd = struct declaration to search
+ * Returns:
+ * FuncDeclaration of `toString()` if found, `null` if not
+ */
+extern (C++) FuncDeclaration search_toString(StructDeclaration sd)
+{
+ Dsymbol s = search_function(sd, Id.tostring);
+ FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
+ if (fd)
+ {
+ __gshared TypeFunction tftostring;
+ if (!tftostring)
+ {
+ tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d);
+ tftostring = tftostring.merge().toTypeFunction();
+ }
+ fd = fd.overloadExactMatch(tftostring);
+ }
+ return fd;
+}
+
+/***************************************
+ * Request additional semantic analysis for TypeInfo generation.
+ * Params:
+ * sc = context
+ * t = type that TypeInfo is being generated for
+ */
+extern (C++) void semanticTypeInfo(Scope* sc, Type t)
+{
+ if (sc)
+ {
+ if (sc.intypeof)
+ return;
+ if (sc.flags & (SCOPE.ctfe | SCOPE.compile))
+ return;
+ }
+
+ if (!t)
+ return;
+
+ void visitVector(TypeVector t)
+ {
+ semanticTypeInfo(sc, t.basetype);
+ }
+
+ void visitAArray(TypeAArray t)
+ {
+ semanticTypeInfo(sc, t.index);
+ semanticTypeInfo(sc, t.next);
+ }
+
+ void visitStruct(TypeStruct t)
+ {
+ //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars());
+ StructDeclaration sd = t.sym;
+
+ /* Step 1: create TypeInfoDeclaration
+ */
+ if (!sc) // inline may request TypeInfo.
+ {
+ Scope scx;
+ scx._module = sd.getModule();
+ getTypeInfoType(sd.loc, t, &scx);
+ sd.requestTypeInfo = true;
+ }
+ else if (!sc.minst)
+ {
+ // don't yet have to generate TypeInfo instance if
+ // the typeid(T) expression exists in speculative scope.
+ }
+ else
+ {
+ getTypeInfoType(sd.loc, t, sc);
+ sd.requestTypeInfo = true;
+
+ // https://issues.dlang.org/show_bug.cgi?id=15149
+ // if the typeid operand type comes from a
+ // result of auto function, it may be yet speculative.
+ // unSpeculative(sc, sd);
+ }
+
+ /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later.
+ * This should be done even if typeid(T) exists in speculative scope.
+ * Because it may appear later in non-speculative scope.
+ */
+ if (!sd.members)
+ return; // opaque struct
+ if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.dtor && !sd.xhash && !search_toString(sd))
+ return; // none of TypeInfo-specific members
+
+ // If the struct is in a non-root module, run semantic3 to get
+ // correct symbols for the member function.
+ if (sd.semanticRun >= PASS.semantic3)
+ {
+ // semantic3 is already done
+ }
+ else if (TemplateInstance ti = sd.isInstantiated())
+ {
+ if (ti.minst && !ti.minst.isRoot())
+ Module.addDeferredSemantic3(sd);
+ }
+ else
+ {
+ if (sd.inNonRoot())
+ {
+ //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot());
+ Module.addDeferredSemantic3(sd);
+ }
+ }
+ }
+
+ void visitTuple(TypeTuple t)
+ {
+ if (t.arguments)
+ {
+ foreach (arg; *t.arguments)
+ {
+ semanticTypeInfo(sc, arg.type);
+ }
+ }
+ }
+
+ /* Note structural similarity of this Type walker to that in isSpeculativeType()
+ */
+
+ Type tb = t.toBasetype();
+ switch (tb.ty)
+ {
+ case Tvector: visitVector(tb.isTypeVector()); break;
+ case Taarray: visitAArray(tb.isTypeAArray()); break;
+ case Tstruct: visitStruct(tb.isTypeStruct()); break;
+ case Ttuple: visitTuple (tb.isTypeTuple()); break;
+
+ case Tclass:
+ case Tenum: break;
+
+ default: semanticTypeInfo(sc, tb.nextOf()); break;
+ }
+}
+
+enum StructFlags : int
+{
+ none = 0x0,
+ hasPointers = 0x1, // NB: should use noPointers as in ClassFlags
+}
+
+/***********************************************************
+ * All `struct` declarations are an instance of this.
+ */
+extern (C++) class StructDeclaration : AggregateDeclaration
+{
+ bool zeroInit; // !=0 if initialize with 0 fill
+ bool hasIdentityAssign; // true if has identity opAssign
+ bool hasBlitAssign; // true if opAssign is a blit
+ bool hasIdentityEquals; // true if has identity opEquals
+ bool hasNoFields; // has no fields
+ bool hasCopyCtor; // copy constructor
+ // Even if struct is defined as non-root symbol, some built-in operations
+ // (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo.
+ // For those, today TypeInfo_Struct is generated in COMDAT.
+ bool requestTypeInfo;
+
+ FuncDeclarations postblits; // Array of postblit functions
+ FuncDeclaration postblit; // aggregate postblit
+
+ FuncDeclaration xeq; // TypeInfo_Struct.xopEquals
+ FuncDeclaration xcmp; // TypeInfo_Struct.xopCmp
+ FuncDeclaration xhash; // TypeInfo_Struct.xtoHash
+ extern (C++) __gshared FuncDeclaration xerreq; // object.xopEquals
+ extern (C++) __gshared FuncDeclaration xerrcmp; // object.xopCmp
+
+ structalign_t alignment; // alignment applied outside of the struct
+ ThreeState ispod; // if struct is POD
+
+ // ABI-specific type(s) if the struct can be passed in registers
+ TypeTuple argTypes;
+
+ extern (D) this(const ref Loc loc, Identifier id, bool inObject)
+ {
+ super(loc, id);
+ zeroInit = false; // assume false until we do semantic processing
+ ispod = ThreeState.none;
+ // For forward references
+ type = new TypeStruct(this);
+
+ if (inObject)
+ {
+ if (id == Id.ModuleInfo && !Module.moduleinfo)
+ Module.moduleinfo = this;
+ }
+ }
+
+ static StructDeclaration create(Loc loc, Identifier id, bool inObject)
+ {
+ return new StructDeclaration(loc, id, inObject);
+ }
+
+ override StructDeclaration syntaxCopy(Dsymbol s)
+ {
+ StructDeclaration sd =
+ s ? cast(StructDeclaration)s
+ : new StructDeclaration(loc, ident, false);
+ ScopeDsymbol.syntaxCopy(sd);
+ return sd;
+ }
+
+ override final Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ //printf("%s.StructDeclaration::search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
+ if (_scope && !symtab)
+ dsymbolSemantic(this, _scope);
+
+ if (!members || !symtab) // opaque or semantic() is not yet called
+ {
+ // .stringof is always defined (but may be hidden by some other symbol)
+ if(ident != Id.stringof && !(flags & IgnoreErrors) && semanticRun < PASS.semanticdone)
+ error("is forward referenced when looking for `%s`", ident.toChars());
+ return null;
+ }
+
+ return ScopeDsymbol.search(loc, ident, flags);
+ }
+
+ override const(char)* kind() const
+ {
+ return "struct";
+ }
+
+ override final void finalizeSize()
+ {
+ //printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
+ assert(sizeok != Sizeok.done);
+
+ if (sizeok == Sizeok.inProcess)
+ {
+ return;
+ }
+ sizeok = Sizeok.inProcess;
+
+ //printf("+StructDeclaration::finalizeSize() %s, fields.dim = %d, sizeok = %d\n", toChars(), fields.dim, sizeok);
+
+ fields.setDim(0); // workaround
+
+ // Set the offsets of the fields and determine the size of the struct
+ FieldState fieldState;
+ bool isunion = isUnionDeclaration() !is null;
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ Dsymbol s = (*members)[i];
+ s.setFieldOffset(this, fieldState, isunion);
+ }
+ if (type.ty == Terror)
+ {
+ errors = true;
+ return;
+ }
+
+ // 0 sized struct's are set to 1 byte
+ if (structsize == 0)
+ {
+ hasNoFields = true;
+ alignsize = 1;
+ if (classKind != classKind.c) // C gets a struct size of 0
+ structsize = 1;
+ }
+
+ // Round struct size up to next alignsize boundary.
+ // This will ensure that arrays of structs will get their internals
+ // aligned properly.
+ if (alignment == STRUCTALIGN_DEFAULT)
+ structsize = (structsize + alignsize - 1) & ~(alignsize - 1);
+ else
+ structsize = (structsize + alignment - 1) & ~(alignment - 1);
+
+ sizeok = Sizeok.done;
+
+ //printf("-StructDeclaration::finalizeSize() %s, fields.dim = %d, structsize = %d\n", toChars(), fields.dim, structsize);
+
+ if (errors)
+ return;
+
+ // Calculate fields[i].overlapped
+ if (checkOverlappedFields())
+ {
+ errors = true;
+ return;
+ }
+
+ // Determine if struct is all zeros or not
+ zeroInit = true;
+ foreach (vd; fields)
+ {
+ if (vd._init)
+ {
+ if (vd._init.isVoidInitializer())
+ /* Treat as 0 for the purposes of putting the initializer
+ * in the BSS segment, or doing a mass set to 0
+ */
+ continue;
+
+ // Zero size fields are zero initialized
+ if (vd.type.size(vd.loc) == 0)
+ continue;
+
+ // Examine init to see if it is all 0s.
+ auto exp = vd.getConstInitializer();
+ if (!exp || !_isZeroInit(exp))
+ {
+ zeroInit = false;
+ break;
+ }
+ }
+ else if (!vd.type.isZeroInit(loc))
+ {
+ zeroInit = false;
+ break;
+ }
+ }
+
+ argTypes = target.toArgTypes(type);
+ }
+
+ /***************************************
+ * Determine if struct is POD (Plain Old Data).
+ *
+ * POD is defined as:
+ * $(OL
+ * $(LI not nested)
+ * $(LI no postblits, destructors, or assignment operators)
+ * $(LI no `ref` fields or fields that are themselves non-POD)
+ * )
+ * The idea being these are compatible with C structs.
+ *
+ * Returns:
+ * true if struct is POD
+ */
+ final bool isPOD()
+ {
+ // If we've already determined whether this struct is POD.
+ if (ispod != ThreeState.none)
+ return (ispod == ThreeState.yes);
+
+ ispod = ThreeState.yes;
+
+ if (enclosing || postblit || dtor || hasCopyCtor)
+ {
+ ispod = ThreeState.no;
+ return false;
+ }
+
+ // Recursively check all fields are POD.
+ for (size_t i = 0; i < fields.dim; i++)
+ {
+ VarDeclaration v = fields[i];
+ if (v.storage_class & STC.ref_)
+ {
+ ispod = ThreeState.no;
+ return false;
+ }
+
+ Type tv = v.type.baseElemOf();
+ if (tv.ty == Tstruct)
+ {
+ TypeStruct ts = cast(TypeStruct)tv;
+ StructDeclaration sd = ts.sym;
+ if (!sd.isPOD())
+ {
+ ispod = ThreeState.no;
+ return false;
+ }
+ }
+ }
+
+ return (ispod == ThreeState.yes);
+ }
+
+ override final inout(StructDeclaration) isStructDeclaration() inout @nogc nothrow pure @safe
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ final uint numArgTypes() const
+ {
+ return argTypes && argTypes.arguments ? cast(uint) argTypes.arguments.dim : 0;
+ }
+
+ final Type argType(uint index)
+ {
+ return index < numArgTypes() ? (*argTypes.arguments)[index].type : null;
+ }
+
+
+ /***************************************
+ * Verifies whether the struct declaration has a
+ * constructor that is not a copy constructor.
+ * Optionally, it can check whether the struct
+ * declaration has a regular constructor, that
+ * is not disabled.
+ *
+ * Params:
+ * checkDisabled = if the struct has a regular
+ non-disabled constructor
+ * Returns:
+ * true, if the struct has a regular (optionally,
+ * not disabled) constructor, false otherwise.
+ */
+ final bool hasRegularCtor(bool checkDisabled = false)
+ {
+ if (!ctor)
+ return false;
+
+ bool result;
+ overloadApply(ctor, (Dsymbol s)
+ {
+ if (auto td = s.isTemplateDeclaration())
+ {
+ if (checkDisabled && td.onemember)
+ {
+ if (auto ctorDecl = td.onemember.isCtorDeclaration())
+ {
+ if (ctorDecl.storage_class & STC.disable)
+ return 0;
+ }
+ }
+ result = true;
+ return 1;
+ }
+ if (auto ctorDecl = s.isCtorDeclaration())
+ {
+ if (!ctorDecl.isCpCtor && (!checkDisabled || !(ctorDecl.storage_class & STC.disable)))
+ {
+ result = true;
+ return 1;
+ }
+ }
+ return 0;
+ });
+ return result;
+ }
+}
+
+/**********************************
+ * Determine if exp is all binary zeros.
+ * Params:
+ * exp = expression to check
+ * Returns:
+ * true if it's all binary 0
+ */
+private bool _isZeroInit(Expression exp)
+{
+ switch (exp.op)
+ {
+ case TOK.int64:
+ return exp.toInteger() == 0;
+
+ case TOK.null_:
+ case TOK.false_:
+ return true;
+
+ case TOK.structLiteral:
+ {
+ auto sle = cast(StructLiteralExp) exp;
+ foreach (i; 0 .. sle.sd.fields.dim)
+ {
+ auto field = sle.sd.fields[i];
+ if (field.type.size(field.loc))
+ {
+ auto e = (*sle.elements)[i];
+ if (e ? !_isZeroInit(e)
+ : !field.type.isZeroInit(field.loc))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case TOK.arrayLiteral:
+ {
+ auto ale = cast(ArrayLiteralExp)exp;
+
+ const dim = ale.elements ? ale.elements.dim : 0;
+
+ if (ale.type.toBasetype().ty == Tarray) // if initializing a dynamic array
+ return dim == 0;
+
+ foreach (i; 0 .. dim)
+ {
+ if (!_isZeroInit(ale[i]))
+ return false;
+ }
+
+ /* Note that true is returned for all T[0]
+ */
+ return true;
+ }
+
+ case TOK.string_:
+ {
+ StringExp se = cast(StringExp)exp;
+
+ if (se.type.toBasetype().ty == Tarray) // if initializing a dynamic array
+ return se.len == 0;
+
+ foreach (i; 0 .. se.len)
+ {
+ if (se.getCodeUnit(i))
+ return false;
+ }
+ return true;
+ }
+
+ case TOK.vector:
+ {
+ auto ve = cast(VectorExp) exp;
+ return _isZeroInit(ve.e1);
+ }
+
+ case TOK.float64:
+ case TOK.complex80:
+ {
+ import dmd.root.ctfloat : CTFloat;
+ return (exp.toReal() is CTFloat.zero) &&
+ (exp.toImaginary() is CTFloat.zero);
+ }
+
+ default:
+ return false;
+ }
+}
+
+/***********************************************************
+ * Unions are a variation on structs.
+ */
+extern (C++) final class UnionDeclaration : StructDeclaration
+{
+ extern (D) this(const ref Loc loc, Identifier id)
+ {
+ super(loc, id, false);
+ }
+
+ override UnionDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto ud = new UnionDeclaration(loc, ident);
+ StructDeclaration.syntaxCopy(ud);
+ return ud;
+ }
+
+ override const(char)* kind() const
+ {
+ return "union";
+ }
+
+ override inout(UnionDeclaration) isUnionDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
diff --git a/gcc/d/dmd/dsymbol.c b/gcc/d/dmd/dsymbol.c
deleted file mode 100644
index f0c1cf6d93c..00000000000
--- a/gcc/d/dmd/dsymbol.c
+++ /dev/null
@@ -1,1803 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/dsymbol.c
- */
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-#include "root/speller.h"
-#include "root/aav.h"
-
-#include "mars.h"
-#include "dsymbol.h"
-#include "aggregate.h"
-#include "identifier.h"
-#include "module.h"
-#include "mtype.h"
-#include "expression.h"
-#include "statement.h"
-#include "declaration.h"
-#include "id.h"
-#include "scope.h"
-#include "init.h"
-#include "import.h"
-#include "template.h"
-#include "attrib.h"
-#include "enum.h"
-#include "lexer.h"
-#include "nspace.h"
-
-bool symbolIsVisible(Dsymbol *origin, Dsymbol *s);
-typedef int (*ForeachDg)(void *ctx, size_t idx, Dsymbol *s);
-int ScopeDsymbol_foreach(Scope *sc, Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn = NULL);
-
-
-/****************************** Dsymbol ******************************/
-
-Dsymbol::Dsymbol()
-{
- //printf("Dsymbol::Dsymbol(%p)\n", this);
- this->ident = NULL;
- this->parent = NULL;
- this->csym = NULL;
- this->isym = NULL;
- this->loc = Loc();
- this->comment = NULL;
- this->_scope = NULL;
- this->prettystring = NULL;
- this->semanticRun = PASSinit;
- this->errors = false;
- this->depdecl = NULL;
- this->userAttribDecl = NULL;
- this->ddocUnittest = NULL;
-}
-
-Dsymbol::Dsymbol(Identifier *ident)
-{
- //printf("Dsymbol::Dsymbol(%p, ident)\n", this);
- this->ident = ident;
- this->parent = NULL;
- this->csym = NULL;
- this->isym = NULL;
- this->loc = Loc();
- this->comment = NULL;
- this->_scope = NULL;
- this->prettystring = NULL;
- this->semanticRun = PASSinit;
- this->errors = false;
- this->depdecl = NULL;
- this->userAttribDecl = NULL;
- this->ddocUnittest = NULL;
-}
-
-Dsymbol *Dsymbol::create(Identifier *ident)
-{
- return new Dsymbol(ident);
-}
-
-bool Dsymbol::equals(RootObject *o)
-{
- if (this == o)
- return true;
- Dsymbol *s = (Dsymbol *)(o);
- // Overload sets don't have an ident
- if (s && ident && s->ident && ident->equals(s->ident))
- return true;
- return false;
-}
-
-/**************************************
- * Copy the syntax.
- * Used for template instantiations.
- * If s is NULL, allocate the new object, otherwise fill it in.
- */
-
-Dsymbol *Dsymbol::syntaxCopy(Dsymbol *)
-{
- print();
- printf("%s %s\n", kind(), toChars());
- assert(0);
- return NULL;
-}
-
-/**************************************
- * Determine if this symbol is only one.
- * Returns:
- * false, *ps = NULL: There are 2 or more symbols
- * true, *ps = NULL: There are zero symbols
- * true, *ps = symbol: The one and only one symbol
- */
-
-bool Dsymbol::oneMember(Dsymbol **ps, Identifier *)
-{
- //printf("Dsymbol::oneMember()\n");
- *ps = this;
- return true;
-}
-
-/*****************************************
- * Same as Dsymbol::oneMember(), but look at an array of Dsymbols.
- */
-
-bool Dsymbol::oneMembers(Dsymbols *members, Dsymbol **ps, Identifier *ident)
-{
- //printf("Dsymbol::oneMembers() %d\n", members ? members->length : 0);
- Dsymbol *s = NULL;
-
- if (members)
- {
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *sx = (*members)[i];
- bool x = sx->oneMember(ps, ident);
- //printf("\t[%d] kind %s = %d, s = %p\n", i, sx->kind(), x, *ps);
- if (!x)
- {
- //printf("\tfalse 1\n");
- assert(*ps == NULL);
- return false;
- }
- if (*ps)
- {
- assert(ident);
- if (!(*ps)->ident || !(*ps)->ident->equals(ident))
- continue;
- if (!s)
- s = *ps;
- else if (s->isOverloadable() && (*ps)->isOverloadable())
- {
- // keep head of overload set
- FuncDeclaration *f1 = s->isFuncDeclaration();
- FuncDeclaration *f2 = (*ps)->isFuncDeclaration();
- if (f1 && f2)
- {
- assert(!f1->isFuncAliasDeclaration());
- assert(!f2->isFuncAliasDeclaration());
- for (; f1 != f2; f1 = f1->overnext0)
- {
- if (f1->overnext0 == NULL)
- {
- f1->overnext0 = f2;
- break;
- }
- }
- }
- }
- else // more than one symbol
- {
- *ps = NULL;
- //printf("\tfalse 2\n");
- return false;
- }
- }
- }
- }
- *ps = s; // s is the one symbol, NULL if none
- //printf("\ttrue\n");
- return true;
-}
-
-/*****************************************
- * Is Dsymbol a variable that contains pointers?
- */
-
-bool Dsymbol::hasPointers()
-{
- //printf("Dsymbol::hasPointers() %s\n", toChars());
- return false;
-}
-
-bool Dsymbol::hasStaticCtorOrDtor()
-{
- //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars());
- return false;
-}
-
-void Dsymbol::setFieldOffset(AggregateDeclaration *, unsigned *, bool)
-{
-}
-
-Identifier *Dsymbol::getIdent()
-{
- return ident;
-}
-
-const char *Dsymbol::toChars()
-{
- return ident ? ident->toChars() : "__anonymous";
-}
-
-const char *Dsymbol::toPrettyCharsHelper()
-{
- return toChars();
-}
-
-const char *Dsymbol::toPrettyChars(bool QualifyTypes)
-{
- if (prettystring && !QualifyTypes)
- return (const char *)prettystring;
-
- //printf("Dsymbol::toPrettyChars() '%s'\n", toChars());
- if (!parent)
- {
- const char *s = toChars();
- if (!QualifyTypes)
- prettystring = (const utf8_t *)s;
- return s;
- }
-
- // Computer number of components
- size_t complength = 0;
- for (Dsymbol *p = this; p; p = p->parent)
- ++complength;
-
- // Allocate temporary array comp[]
- const char **comp = (const char **)mem.xmalloc(complength * sizeof(char**));
-
- // Fill in comp[] and compute length of final result
- size_t length = 0;
- int i = 0;
- for (Dsymbol *p = this; p; p = p->parent)
- {
- const char *s = QualifyTypes ? p->toPrettyCharsHelper() : p->toChars();
- const size_t len = strlen(s);
- comp[i] = s;
- ++i;
- length += len + 1;
- }
-
- char *s = (char *)mem.xmalloc(length);
- char *q = s + length - 1;
- *q = 0;
- for (size_t j = 0; j < complength; j++)
- {
- const char *t = comp[j];
- const size_t len = strlen(t);
- q -= len;
- memcpy(q, t, len);
- if (q == s)
- break;
- *--q = '.';
- }
- free(comp);
- if (!QualifyTypes)
- prettystring = (utf8_t *)s;
- return s;
-}
-
-Loc& Dsymbol::getLoc()
-{
- if (!loc.filename) // avoid bug 5861.
- {
- Module *m = getModule();
-
- if (m && m->srcfile)
- loc.filename = m->srcfile->toChars();
- }
- return loc;
-}
-
-const char *Dsymbol::locToChars()
-{
- return getLoc().toChars();
-}
-
-const char *Dsymbol::kind() const
-{
- return "symbol";
-}
-
-/*********************************
- * If this symbol is really an alias for another,
- * return that other.
- * If needed, semantic() is invoked due to resolve forward reference.
- */
-Dsymbol *Dsymbol::toAlias()
-{
- return this;
-}
-
-/*********************************
- * Resolve recursive tuple expansion in eponymous template.
- */
-Dsymbol *Dsymbol::toAlias2()
-{
- return toAlias();
-}
-
-/**
- * `pastMixin` returns the enclosing symbol if this is a template mixin.
- *
- * `pastMixinAndNspace` does likewise, additionally skipping over Nspaces that
- * are mangleOnly.
- *
- * See also `parent`, `toParent`, `toParent2` and `toParent3`.
- */
-Dsymbol *Dsymbol::pastMixin()
-{
- //printf("Dsymbol::pastMixin() %s\n", toChars());
- if (!isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol())
- return this;
- if (!parent)
- return NULL;
- return parent->pastMixin();
-}
-
-/// ditto
-Dsymbol *Dsymbol::pastMixinAndNspace()
-{
- //printf("Dsymbol::pastMixinAndNspace() %s\n", toChars());
- Nspace *ns = isNspace();
- if (!(ns && ns->mangleOnly) &&
- !isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol())
- return this;
- if (!parent)
- return NULL;
- return parent->pastMixinAndNspace();
-}
-
-/**********************************
- * `parent` field returns a lexically enclosing scope symbol this is a member of.
- *
- * `toParent()` returns a logically enclosing scope symbol this is a member of.
- * It skips over TemplateMixin's and Nspaces that are mangleOnly.
- *
- * `toParent2()` returns an enclosing scope symbol this is living at runtime.
- * It skips over both TemplateInstance's and TemplateMixin's.
- * It's used when looking for the 'this' pointer of the enclosing function/class.
- *
- * `toParent3()` returns a logically enclosing scope symbol this is a member of.
- * It skips over TemplateMixin's.
- *
- * Examples:
- * module mod;
- * template Foo(alias a) { mixin Bar!(); }
- * mixin template Bar() {
- * public { // ProtDeclaration
- * void baz() { a = 2; }
- * }
- * }
- * void test() {
- * int v = 1;
- * alias foo = Foo!(v);
- * foo.baz();
- * assert(v == 2);
- * }
- *
- * // s == FuncDeclaration('mod.test.Foo!().Bar!().baz()')
- * // s.parent == TemplateMixin('mod.test.Foo!().Bar!()')
- * // s.toParent() == TemplateInstance('mod.test.Foo!()')
- * // s.toParent2() == FuncDeclaration('mod.test')
- */
-Dsymbol *Dsymbol::toParent()
-{
- return parent ? parent->pastMixinAndNspace() : NULL;
-}
-
-/// ditto
-Dsymbol *Dsymbol::toParent2()
-{
- if (!parent ||
- (!parent->isTemplateInstance() &&
- !parent->isForwardingAttribDeclaration() &&
- !parent->isForwardingScopeDsymbol()))
- return parent;
- return parent->toParent2();
-}
-
-/// ditto
-Dsymbol *Dsymbol::toParent3()
-{
- return parent ? parent->pastMixin() : NULL;
-}
-
-TemplateInstance *Dsymbol::isInstantiated()
-{
- for (Dsymbol *s = parent; s; s = s->parent)
- {
- TemplateInstance *ti = s->isTemplateInstance();
- if (ti && !ti->isTemplateMixin())
- return ti;
- }
- return NULL;
-}
-
-// Check if this function is a member of a template which has only been
-// instantiated speculatively, eg from inside is(typeof()).
-// Return the speculative template instance it is part of,
-// or NULL if not speculative.
-TemplateInstance *Dsymbol::isSpeculative()
-{
- Dsymbol *par = parent;
- while (par)
- {
- TemplateInstance *ti = par->isTemplateInstance();
- if (ti && ti->gagged)
- return ti;
- par = par->toParent();
- }
- return NULL;
-}
-
-Ungag Dsymbol::ungagSpeculative()
-{
- unsigned oldgag = global.gag;
-
- if (global.gag && !isSpeculative() && !toParent2()->isFuncDeclaration())
- global.gag = 0;
-
- return Ungag(oldgag);
-}
-
-bool Dsymbol::isAnonymous()
-{
- return ident == NULL;
-}
-
-/*************************************
- * Set scope for future semantic analysis so we can
- * deal better with forward references.
- */
-
-void Dsymbol::setScope(Scope *sc)
-{
- //printf("Dsymbol::setScope() %p %s, %p stc = %llx\n", this, toChars(), sc, sc->stc);
- if (!sc->nofree)
- sc->setNoFree(); // may need it even after semantic() finishes
- _scope = sc;
- if (sc->depdecl)
- depdecl = sc->depdecl;
-
- if (!userAttribDecl)
- userAttribDecl = sc->userAttribDecl;
-}
-
-void Dsymbol::importAll(Scope *)
-{
-}
-
-/*********************************************
- * Search for ident as member of s.
- * Params:
- * loc = location to print for error messages
- * ident = identifier to search for
- * flags = IgnoreXXXX
- * Returns:
- * NULL if not found
- */
-
-Dsymbol *Dsymbol::search(const Loc &, Identifier *, int)
-{
- //printf("Dsymbol::search(this=%p,%s, ident='%s')\n", this, toChars(), ident->toChars());
- return NULL;
-}
-
-/***************************************************
- * Search for symbol with correct spelling.
- */
-
-void *symbol_search_fp(void *arg, const char *seed, int *cost)
-{
- /* If not in the lexer's string table, it certainly isn't in the symbol table.
- * Doing this first is a lot faster.
- */
- size_t len = strlen(seed);
- if (!len)
- return NULL;
- Identifier *id = Identifier::lookup(seed, len);
- if (!id)
- return NULL;
-
- *cost = 0;
- Dsymbol *s = (Dsymbol *)arg;
- Module::clearCache();
- return (void *)s->search(Loc(), id, IgnoreErrors);
-}
-
-Dsymbol *Dsymbol::search_correct(Identifier *ident)
-{
- if (global.gag)
- return NULL; // don't do it for speculative compiles; too time consuming
- // search for exact name first
- if (Dsymbol *s = search(Loc(), ident, IgnoreErrors))
- return s;
- return (Dsymbol *)speller(ident->toChars(), &symbol_search_fp, (void *)this, idchars);
-}
-
-/***************************************
- * Search for identifier id as a member of 'this'.
- * id may be a template instance.
- * Returns:
- * symbol found, NULL if not
- */
-Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, RootObject *id, int flags)
-{
- //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident->toChars());
- Dsymbol *s = toAlias();
- Dsymbol *sm;
-
- if (Declaration *d = s->isDeclaration())
- {
- if (d->inuse)
- {
- ::error(loc, "circular reference to `%s`", d->toPrettyChars());
- return NULL;
- }
- }
-
- switch (id->dyncast())
- {
- case DYNCAST_IDENTIFIER:
- sm = s->search(loc, (Identifier *)id, flags);
- break;
-
- case DYNCAST_DSYMBOL:
- {
- // It's a template instance
- //printf("\ttemplate instance id\n");
- Dsymbol *st = (Dsymbol *)id;
- TemplateInstance *ti = st->isTemplateInstance();
- sm = s->search(loc, ti->name);
- if (!sm)
- {
- sm = s->search_correct(ti->name);
- if (sm)
- ::error(loc, "template identifier `%s` is not a member of %s `%s`, did you mean %s `%s`?",
- ti->name->toChars(), s->kind(), s->toPrettyChars(), sm->kind(), sm->toChars());
- else
- ::error(loc, "template identifier `%s` is not a member of %s `%s`",
- ti->name->toChars(), s->kind(), s->toPrettyChars());
- return NULL;
- }
- sm = sm->toAlias();
- TemplateDeclaration *td = sm->isTemplateDeclaration();
- if (!td)
- {
- ::error(loc, "%s.%s is not a template, it is a %s", s->toPrettyChars(), ti->name->toChars(), sm->kind());
- return NULL;
- }
- ti->tempdecl = td;
- if (!ti->semanticRun)
- dsymbolSemantic(ti, sc);
- sm = ti->toAlias();
- break;
- }
-
- case DYNCAST_TYPE:
- case DYNCAST_EXPRESSION:
- default:
- assert(0);
- }
- return sm;
-}
-
-bool Dsymbol::overloadInsert(Dsymbol *)
-{
- //printf("Dsymbol::overloadInsert('%s')\n", s->toChars());
- return false;
-}
-
-d_uns64 Dsymbol::size(Loc)
-{
- error("Dsymbol `%s` has no size", toChars());
- return SIZE_INVALID;
-}
-
-bool Dsymbol::isforwardRef()
-{
- return false;
-}
-
-AggregateDeclaration *Dsymbol::isThis()
-{
- return NULL;
-}
-
-bool Dsymbol::isExport() const
-{
- return false;
-}
-
-bool Dsymbol::isImportedSymbol() const
-{
- return false;
-}
-
-bool Dsymbol::isDeprecated()
-{
- return false;
-}
-
-bool Dsymbol::isOverloadable()
-{
- return false;
-}
-
-LabelDsymbol *Dsymbol::isLabel() // is this a LabelDsymbol()?
-{
- return NULL;
-}
-
-/// Returns an AggregateDeclaration when toParent() is that.
-AggregateDeclaration *Dsymbol::isMember()
-{
- //printf("Dsymbol::isMember() %s\n", toChars());
- Dsymbol *parent = toParent();
- //printf("parent is %s %s\n", parent->kind(), parent->toChars());
- return parent ? parent->isAggregateDeclaration() : NULL;
-}
-
-/// Returns an AggregateDeclaration when toParent2() is that.
-AggregateDeclaration *Dsymbol::isMember2()
-{
- //printf("Dsymbol::isMember2() %s\n", toChars());
- Dsymbol *parent = toParent2();
- //printf("parent is %s %s\n", parent->kind(), parent->toChars());
- return parent ? parent->isAggregateDeclaration() : NULL;
-}
-
-// is this a member of a ClassDeclaration?
-ClassDeclaration *Dsymbol::isClassMember()
-{
- AggregateDeclaration *ad = isMember();
- return ad ? ad->isClassDeclaration() : NULL;
-}
-
-Type *Dsymbol::getType()
-{
- return NULL;
-}
-
-bool Dsymbol::needThis()
-{
- return false;
-}
-
-/*********************************
- * Iterate this dsymbol or members of this scoped dsymbol, then
- * call `fp` with the found symbol and `param`.
- * Params:
- * fp = function pointer to process the iterated symbol.
- * If it returns nonzero, the iteration will be aborted.
- * param = a parameter passed to fp.
- * Returns:
- * nonzero if the iteration is aborted by the return value of fp,
- * or 0 if it's completed.
- */
-int Dsymbol::apply(Dsymbol_apply_ft_t fp, void *param)
-{
- return (*fp)(this, param);
-}
-
-void Dsymbol::addMember(Scope *, ScopeDsymbol *sds)
-{
- //printf("Dsymbol::addMember('%s')\n", toChars());
- //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sds->toChars());
- //printf("Dsymbol::addMember(this = %p, '%s' sds = %p, sds->symtab = %p)\n", this, toChars(), sds, sds->symtab);
- parent = sds;
- if (!isAnonymous()) // no name, so can't add it to symbol table
- {
- if (!sds->symtabInsert(this)) // if name is already defined
- {
- Dsymbol *s2 = sds->symtabLookup(this, ident);
- if (!s2->overloadInsert(this))
- {
- sds->multiplyDefined(Loc(), this, s2);
- errors = true;
- }
- }
- if (sds->isAggregateDeclaration() || sds->isEnumDeclaration())
- {
- if (ident == Id::__sizeof || ident == Id::__xalignof || ident == Id::_mangleof)
- {
- error(".%s property cannot be redefined", ident->toChars());
- errors = true;
- }
- }
- }
-}
-
-void Dsymbol::error(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::verror(getLoc(), format, ap, kind(), toPrettyChars());
- va_end(ap);
-}
-
-void Dsymbol::error(Loc loc, const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::verror(loc, format, ap, kind(), toPrettyChars());
- va_end(ap);
-}
-
-void Dsymbol::deprecation(Loc loc, const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::vdeprecation(loc, format, ap, kind(), toPrettyChars());
- va_end(ap);
-}
-
-void Dsymbol::deprecation(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::vdeprecation(getLoc(), format, ap, kind(), toPrettyChars());
- va_end(ap);
-}
-
-bool Dsymbol::checkDeprecated(Loc loc, Scope *sc)
-{
- if (global.params.useDeprecated != DIAGNOSTICoff && isDeprecated())
- {
- // Don't complain if we're inside a deprecated symbol's scope
- for (Dsymbol *sp = sc->parent; sp; sp = sp->parent)
- {
- if (sp->isDeprecated())
- return false;
- }
-
- for (Scope *sc2 = sc; sc2; sc2 = sc2->enclosing)
- {
- if (sc2->scopesym && sc2->scopesym->isDeprecated())
- return false;
-
- // If inside a StorageClassDeclaration that is deprecated
- if (sc2->stc & STCdeprecated)
- return false;
- }
-
- const char *message = NULL;
- for (Dsymbol *p = this; p; p = p->parent)
- {
- message = p->depdecl ? p->depdecl->getMessage() : NULL;
- if (message)
- break;
- }
-
- if (message)
- deprecation(loc, "is deprecated - %s", message);
- else
- deprecation(loc, "is deprecated");
-
- return true;
- }
-
- return false;
-}
-
-/**********************************
- * Determine which Module a Dsymbol is in.
- */
-
-Module *Dsymbol::getModule()
-{
- //printf("Dsymbol::getModule()\n");
- if (TemplateInstance *ti = isInstantiated())
- return ti->tempdecl->getModule();
-
- Dsymbol *s = this;
- while (s)
- {
- //printf("\ts = %s '%s'\n", s->kind(), s->toPrettyChars());
- Module *m = s->isModule();
- if (m)
- return m;
- s = s->parent;
- }
- return NULL;
-}
-
-/**********************************
- * Determine which Module a Dsymbol is in, as far as access rights go.
- */
-
-Module *Dsymbol::getAccessModule()
-{
- //printf("Dsymbol::getAccessModule()\n");
- if (TemplateInstance *ti = isInstantiated())
- return ti->tempdecl->getAccessModule();
-
- Dsymbol *s = this;
- while (s)
- {
- //printf("\ts = %s '%s'\n", s->kind(), s->toPrettyChars());
- Module *m = s->isModule();
- if (m)
- return m;
- TemplateInstance *ti = s->isTemplateInstance();
- if (ti && ti->enclosing)
- {
- /* Because of local template instantiation, the parent isn't where the access
- * rights come from - it's the template declaration
- */
- s = ti->tempdecl;
- }
- else
- s = s->parent;
- }
- return NULL;
-}
-
-/*************************************
- */
-
-Prot Dsymbol::prot()
-{
- return Prot(Prot::public_);
-}
-
-/*************************************
- * Do syntax copy of an array of Dsymbol's.
- */
-
-Dsymbols *Dsymbol::arraySyntaxCopy(Dsymbols *a)
-{
-
- Dsymbols *b = NULL;
- if (a)
- {
- b = a->copy();
- for (size_t i = 0; i < b->length; i++)
- {
- (*b)[i] = (*b)[i]->syntaxCopy(NULL);
- }
- }
- return b;
-}
-
-/****************************************
- * Add documentation comment to Dsymbol.
- * Ignore NULL comments.
- */
-
-void Dsymbol::addComment(const utf8_t *comment)
-{
- //if (comment)
- //printf("adding comment '%s' to symbol %p '%s'\n", comment, this, toChars());
-
- if (!this->comment)
- this->comment = comment;
- else if (comment && strcmp((const char *)comment, (const char *)this->comment) != 0)
- { // Concatenate the two
- this->comment = Lexer::combineComments(this->comment, comment);
- }
-}
-
-/****************************************
- * Returns true if this symbol is defined in a non-root module without instantiation.
- */
-bool Dsymbol::inNonRoot()
-{
- Dsymbol *s = parent;
- for (; s; s = s->toParent())
- {
- if (s->isTemplateInstance())
- {
- return false;
- }
- if (Module *m = s->isModule())
- {
- if (!m->isRoot())
- return true;
- break;
- }
- }
- return false;
-}
-
-/********************************* OverloadSet ****************************/
-
-OverloadSet::OverloadSet(Identifier *ident, OverloadSet *os)
- : Dsymbol(ident)
-{
- if (os)
- {
- for (size_t i = 0; i < os->a.length; i++)
- {
- a.push(os->a[i]);
- }
- }
-}
-
-void OverloadSet::push(Dsymbol *s)
-{
- a.push(s);
-}
-
-const char *OverloadSet::kind() const
-{
- return "overloadset";
-}
-
-
-/********************************* ForwardingScopeDsymbol ******************/
-
-ForwardingScopeDsymbol::ForwardingScopeDsymbol(ScopeDsymbol *forward)
- : ScopeDsymbol()
-{
- this->forward = forward;
-}
-
-Dsymbol *ForwardingScopeDsymbol::symtabInsert(Dsymbol *s)
-{
- assert(forward);
- if (Declaration *d = s->isDeclaration())
- {
- if (d->storage_class & STClocal)
- {
- // Symbols with storage class STClocal are not
- // forwarded, but stored in the local symbol
- // table. (Those are the `static foreach` variables.)
- if (!symtab)
- {
- symtab = new DsymbolTable();
- }
- return ScopeDsymbol::symtabInsert(s); // insert locally
- }
- }
- if (!forward->symtab)
- {
- forward->symtab = new DsymbolTable();
- }
- // Non-STClocal symbols are forwarded to `forward`.
- return forward->symtabInsert(s);
-}
-
-/************************
- * This override handles the following two cases:
- * static foreach (i, i; [0]) { ... }
- * and
- * static foreach (i; [0]) { enum i = 2; }
- */
-Dsymbol *ForwardingScopeDsymbol::symtabLookup(Dsymbol *s, Identifier *id)
-{
- assert(forward);
- // correctly diagnose clashing foreach loop variables.
- if (Declaration *d = s->isDeclaration())
- {
- if (d->storage_class & STClocal)
- {
- if (!symtab)
- {
- symtab = new DsymbolTable();
- }
- return ScopeDsymbol::symtabLookup(s,id);
- }
- }
- // Declarations within `static foreach` do not clash with
- // `static foreach` loop variables.
- if (!forward->symtab)
- {
- forward->symtab = new DsymbolTable();
- }
- return forward->symtabLookup(s,id);
-}
-
-void ForwardingScopeDsymbol::importScope(Dsymbol *s, Prot protection)
-{
- forward->importScope(s, protection);
-}
-
-const char *ForwardingScopeDsymbol::kind() const
-{
- return "local scope";
-}
-
-/********************************* ScopeDsymbol ****************************/
-
-ScopeDsymbol::ScopeDsymbol()
- : Dsymbol()
-{
- members = NULL;
- symtab = NULL;
- endlinnum = 0;
- importedScopes = NULL;
- prots = NULL;
-}
-
-ScopeDsymbol::ScopeDsymbol(Identifier *id)
- : Dsymbol(id)
-{
- members = NULL;
- symtab = NULL;
- endlinnum = 0;
- importedScopes = NULL;
- prots = NULL;
-}
-
-Dsymbol *ScopeDsymbol::syntaxCopy(Dsymbol *s)
-{
- //printf("ScopeDsymbol::syntaxCopy('%s')\n", toChars());
- ScopeDsymbol *sds = s ? (ScopeDsymbol *)s : new ScopeDsymbol(ident);
- sds->members = arraySyntaxCopy(members);
- sds->endlinnum = endlinnum;
- return sds;
-}
-
-/*****************************************
- * This function is #1 on the list of functions that eat cpu time.
- * Be very, very careful about slowing it down.
- */
-
-Dsymbol *ScopeDsymbol::search(const Loc &loc, Identifier *ident, int flags)
-{
- //printf("%s->ScopeDsymbol::search(ident='%s', flags=x%x)\n", toChars(), ident->toChars(), flags);
- //if (strcmp(ident->toChars(),"c") == 0) *(char*)0=0;
-
- // Look in symbols declared in this module
- if (symtab && !(flags & SearchImportsOnly))
- {
- //printf(" look in locals\n");
- Dsymbol *s1 = symtab->lookup(ident);
- if (s1)
- {
- //printf("\tfound in locals = '%s.%s'\n",toChars(),s1->toChars());
- return s1;
- }
- }
- //printf(" not found in locals\n");
-
- // Look in imported scopes
- if (importedScopes)
- {
- //printf(" look in imports\n");
- Dsymbol *s = NULL;
- OverloadSet *a = NULL;
-
- // Look in imported modules
- for (size_t i = 0; i < importedScopes->length; i++)
- {
- // If private import, don't search it
- if ((flags & IgnorePrivateImports) && prots[i] == Prot::private_)
- continue;
-
- int sflags = flags & (IgnoreErrors | IgnoreAmbiguous); // remember these in recursive searches
- Dsymbol *ss = (*importedScopes)[i];
-
- //printf("\tscanning import '%s', prots = %d, isModule = %p, isImport = %p\n", ss->toChars(), prots[i], ss->isModule(), ss->isImport());
-
- if (ss->isModule())
- {
- if (flags & SearchLocalsOnly)
- continue;
- }
- else // mixin template
- {
- if (flags & SearchImportsOnly)
- continue;
- sflags |= SearchLocalsOnly;
- }
-
- /* Don't find private members if ss is a module
- */
- Dsymbol *s2 = ss->search(loc, ident, sflags | (ss->isModule() ? IgnorePrivateImports : IgnoreNone));
- if (!s2 || (!(flags & IgnoreSymbolVisibility) && !symbolIsVisible(this, s2)))
- continue;
- if (!s)
- {
- s = s2;
- if (s && s->isOverloadSet())
- a = mergeOverloadSet(ident, a, s);
- }
- else if (s2 && s != s2)
- {
- if (s->toAlias() == s2->toAlias() ||
- (s->getType() == s2->getType() && s->getType()))
- {
- /* After following aliases, we found the same
- * symbol, so it's not an ambiguity. But if one
- * alias is deprecated or less accessible, prefer
- * the other.
- */
- if (s->isDeprecated() ||
- (s->prot().isMoreRestrictiveThan(s2->prot()) && s2->prot().kind != Prot::none))
- s = s2;
- }
- else
- {
- /* Two imports of the same module should be regarded as
- * the same.
- */
- Import *i1 = s->isImport();
- Import *i2 = s2->isImport();
- if (!(i1 && i2 &&
- (i1->mod == i2->mod ||
- (!i1->parent->isImport() && !i2->parent->isImport() &&
- i1->ident->equals(i2->ident))
- )
- )
- )
- {
- /* Bugzilla 8668:
- * Public selective import adds AliasDeclaration in module.
- * To make an overload set, resolve aliases in here and
- * get actual overload roots which accessible via s and s2.
- */
- s = s->toAlias();
- s2 = s2->toAlias();
-
- /* If both s2 and s are overloadable (though we only
- * need to check s once)
- */
- if ((s2->isOverloadSet() || s2->isOverloadable()) &&
- (a || s->isOverloadable()))
- {
- a = mergeOverloadSet(ident, a, s2);
- continue;
- }
- if (flags & IgnoreAmbiguous) // if return NULL on ambiguity
- return NULL;
- if (!(flags & IgnoreErrors))
- ScopeDsymbol::multiplyDefined(loc, s, s2);
- break;
- }
- }
- }
- }
-
- if (s)
- {
- /* Build special symbol if we had multiple finds
- */
- if (a)
- {
- if (!s->isOverloadSet())
- a = mergeOverloadSet(ident, a, s);
- s = a;
- }
- //printf("\tfound in imports %s.%s\n", toChars(), s.toChars());
- return s;
- }
- //printf(" not found in imports\n");
- }
-
- return NULL;
-}
-
-OverloadSet *ScopeDsymbol::mergeOverloadSet(Identifier *ident, OverloadSet *os, Dsymbol *s)
-{
- if (!os)
- {
- os = new OverloadSet(ident);
- os->parent = this;
- }
- if (OverloadSet *os2 = s->isOverloadSet())
- {
- // Merge the cross-module overload set 'os2' into 'os'
- if (os->a.length == 0)
- {
- os->a.setDim(os2->a.length);
- memcpy(os->a.tdata(), os2->a.tdata(), sizeof(os->a[0]) * os2->a.length);
- }
- else
- {
- for (size_t i = 0; i < os2->a.length; i++)
- {
- os = mergeOverloadSet(ident, os, os2->a[i]);
- }
- }
- }
- else
- {
- assert(s->isOverloadable());
-
- /* Don't add to os[] if s is alias of previous sym
- */
- for (size_t j = 0; j < os->a.length; j++)
- {
- Dsymbol *s2 = os->a[j];
- if (s->toAlias() == s2->toAlias())
- {
- if (s2->isDeprecated() ||
- (s2->prot().isMoreRestrictiveThan(s->prot()) &&
- s->prot().kind != Prot::none))
- {
- os->a[j] = s;
- }
- goto Lcontinue;
- }
- }
- os->push(s);
- Lcontinue:
- ;
- }
- return os;
-}
-
-void ScopeDsymbol::importScope(Dsymbol *s, Prot protection)
-{
- //printf("%s->ScopeDsymbol::importScope(%s, %d)\n", toChars(), s->toChars(), protection);
-
- // No circular or redundant import's
- if (s != this)
- {
- if (!importedScopes)
- importedScopes = new Dsymbols();
- else
- {
- for (size_t i = 0; i < importedScopes->length; i++)
- {
- Dsymbol *ss = (*importedScopes)[i];
- if (ss == s) // if already imported
- {
- if (protection.kind > prots[i])
- prots[i] = protection.kind; // upgrade access
- return;
- }
- }
- }
- importedScopes->push(s);
- prots = (Prot::Kind *)mem.xrealloc(prots, importedScopes->length * sizeof(prots[0]));
- prots[importedScopes->length - 1] = protection.kind;
- }
-}
-
-#define BITS_PER_INDEX (sizeof(size_t) * CHAR_BIT)
-
-static void bitArraySet(BitArray *array, size_t idx)
-{
- array->ptr[idx / BITS_PER_INDEX] |= 1ULL << (idx % BITS_PER_INDEX);
-}
-
-static bool bitArrayGet(BitArray *array, size_t idx)
-{
- const size_t boffset = idx % BITS_PER_INDEX;
- return (array->ptr[idx / BITS_PER_INDEX] & (1ULL << boffset)) >> boffset;
-}
-
-static void bitArrayLength(BitArray *array, size_t len)
-{
- if (array->len < len)
- {
- const size_t obytes = (array->len + BITS_PER_INDEX - 1) / BITS_PER_INDEX;
- const size_t nbytes = (len + BITS_PER_INDEX - 1) / BITS_PER_INDEX;
-
- if (!array->ptr)
- array->ptr = (size_t *)mem.xmalloc(nbytes * sizeof(size_t));
- else
- array->ptr = (size_t *)mem.xrealloc(array->ptr, nbytes * sizeof(size_t));
-
- for (size_t i = obytes; i < nbytes; i++)
- array->ptr[i] = 0;
-
- array->len = nbytes * BITS_PER_INDEX;
- }
-}
-
-void ScopeDsymbol::addAccessiblePackage(Package *p, Prot protection)
-{
- BitArray *pary = protection.kind == Prot::private_ ? &privateAccessiblePackages : &accessiblePackages;
- if (pary->len <= p->tag)
- bitArrayLength(pary, p->tag + 1);
- bitArraySet(pary, p->tag);
-}
-
-bool ScopeDsymbol::isPackageAccessible(Package *p, Prot protection, int)
-{
- if ((p->tag < accessiblePackages.len && bitArrayGet(&accessiblePackages, p->tag)) ||
- (protection.kind == Prot::private_ && p->tag < privateAccessiblePackages.len && bitArrayGet(&privateAccessiblePackages, p->tag)))
- return true;
- if (importedScopes)
- {
- for (size_t i = 0; i < importedScopes->length; i++)
- {
- // only search visible scopes && imported modules should ignore private imports
- Dsymbol *ss = (*importedScopes)[i];
- if (protection.kind <= prots[i] &&
- ss->isScopeDsymbol()->isPackageAccessible(p, protection, IgnorePrivateImports))
- return true;
- }
- }
- return false;
-}
-
-bool ScopeDsymbol::isforwardRef()
-{
- return (members == NULL);
-}
-
-void ScopeDsymbol::multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2)
-{
- if (loc.filename)
- { ::error(loc, "%s at %s conflicts with %s at %s",
- s1->toPrettyChars(),
- s1->locToChars(),
- s2->toPrettyChars(),
- s2->locToChars());
- }
- else
- {
- s1->error(s1->loc, "conflicts with %s %s at %s",
- s2->kind(),
- s2->toPrettyChars(),
- s2->locToChars());
- }
-}
-
-const char *ScopeDsymbol::kind() const
-{
- return "ScopeDsymbol";
-}
-
-Dsymbol *ScopeDsymbol::symtabInsert(Dsymbol *s)
-{
- return symtab->insert(s);
-}
-
-/****************************************
- * Look up identifier in symbol table.
- */
-
-Dsymbol *ScopeDsymbol::symtabLookup(Dsymbol *, Identifier *id)
-{
- return symtab->lookup(id);
-}
-
-/****************************************
- * Return true if any of the members are static ctors or static dtors, or if
- * any members have members that are.
- */
-
-bool ScopeDsymbol::hasStaticCtorOrDtor()
-{
- if (members)
- {
- for (size_t i = 0; i < members->length; i++)
- { Dsymbol *member = (*members)[i];
-
- if (member->hasStaticCtorOrDtor())
- return true;
- }
- }
- return false;
-}
-
-/***************************************
- * Determine number of Dsymbols, folding in AttribDeclaration members.
- */
-
-static int dimDg(void *ctx, size_t, Dsymbol *)
-{
- ++*(size_t *)ctx;
- return 0;
-}
-
-size_t ScopeDsymbol::dim(Dsymbols *members)
-{
- size_t n = 0;
- ScopeDsymbol_foreach(NULL, members, &dimDg, &n);
- return n;
-}
-
-/***************************************
- * Get nth Dsymbol, folding in AttribDeclaration members.
- * Returns:
- * Dsymbol* nth Dsymbol
- * NULL not found, *pn gets incremented by the number
- * of Dsymbols
- */
-
-struct GetNthSymbolCtx
-{
- size_t nth;
- Dsymbol *sym;
-};
-
-static int getNthSymbolDg(void *ctx, size_t n, Dsymbol *sym)
-{
- GetNthSymbolCtx *p = (GetNthSymbolCtx *)ctx;
- if (n == p->nth)
- { p->sym = sym;
- return 1;
- }
- return 0;
-}
-
-Dsymbol *ScopeDsymbol::getNth(Dsymbols *members, size_t nth, size_t *)
-{
- GetNthSymbolCtx ctx = { nth, NULL };
- int res = ScopeDsymbol_foreach(NULL, members, &getNthSymbolDg, &ctx);
- return res ? ctx.sym : NULL;
-}
-
-/***************************************
- * Expands attribute declarations in members in depth first
- * order. Calls dg(void *ctx, size_t symidx, Dsymbol *sym) for each
- * member.
- * If dg returns !=0, stops and returns that value else returns 0.
- * Use this function to avoid the O(N + N^2/2) complexity of
- * calculating dim and calling N times getNth.
- */
-
-int ScopeDsymbol_foreach(Scope *sc, Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn)
-{
- assert(dg);
- if (!members)
- return 0;
-
- size_t n = pn ? *pn : 0; // take over index
- int result = 0;
- for (size_t i = 0; i < members->length; i++)
- { Dsymbol *s = (*members)[i];
-
- if (AttribDeclaration *a = s->isAttribDeclaration())
- result = ScopeDsymbol_foreach(sc, a->include(sc), dg, ctx, &n);
- else if (TemplateMixin *tm = s->isTemplateMixin())
- result = ScopeDsymbol_foreach(sc, tm->members, dg, ctx, &n);
- else if (s->isTemplateInstance())
- ;
- else if (s->isUnitTestDeclaration())
- ;
- else
- result = dg(ctx, n++, s);
-
- if (result)
- break;
- }
-
- if (pn)
- *pn = n; // update index
- return result;
-}
-
-/*******************************************
- * Look for member of the form:
- * const(MemberInfo)[] getMembers(string);
- * Returns NULL if not found
- */
-
-FuncDeclaration *ScopeDsymbol::findGetMembers()
-{
- Dsymbol *s = search_function(this, Id::getmembers);
- FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL;
-
- if (fdx && fdx->isVirtual())
- fdx = NULL;
-
- return fdx;
-}
-
-
-/****************************** WithScopeSymbol ******************************/
-
-WithScopeSymbol::WithScopeSymbol(WithStatement *withstate)
- : ScopeDsymbol()
-{
- this->withstate = withstate;
-}
-
-Dsymbol *WithScopeSymbol::search(const Loc &loc, Identifier *ident, int flags)
-{
- //printf("WithScopeSymbol::search(%s)\n", ident->toChars());
- if (flags & SearchImportsOnly)
- return NULL;
-
- // Acts as proxy to the with class declaration
- Dsymbol *s = NULL;
- Expression *eold = NULL;
- for (Expression *e = withstate->exp; e != eold; e = resolveAliasThis(_scope, e))
- {
- if (e->op == TOKscope)
- {
- s = ((ScopeExp *)e)->sds;
- }
- else if (e->op == TOKtype)
- {
- s = e->type->toDsymbol(NULL);
- }
- else
- {
- Type *t = e->type->toBasetype();
- s = t->toDsymbol(NULL);
- }
- if (s)
- {
- s = s->search(loc, ident, flags);
- if (s)
- return s;
- }
- eold = e;
- }
- return NULL;
-}
-
-/****************************** ArrayScopeSymbol ******************************/
-
-ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, Expression *e)
- : ScopeDsymbol()
-{
- assert(e->op == TOKindex || e->op == TOKslice || e->op == TOKarray);
- exp = e;
- type = NULL;
- td = NULL;
- this->sc = sc;
-}
-
-ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, TypeTuple *t)
- : ScopeDsymbol()
-{
- exp = NULL;
- type = t;
- td = NULL;
- this->sc = sc;
-}
-
-ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, TupleDeclaration *s)
- : ScopeDsymbol()
-{
- exp = NULL;
- type = NULL;
- td = s;
- this->sc = sc;
-}
-
-Dsymbol *ArrayScopeSymbol::search(const Loc &loc, Identifier *ident, int)
-{
- //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident->toChars(), flags);
- if (ident == Id::dollar)
- {
- VarDeclaration **pvar;
- Expression *ce;
-
- L1:
- if (td)
- {
- /* $ gives the number of elements in the tuple
- */
- VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL);
- Expression *e = new IntegerExp(Loc(), td->objects->length, Type::tsize_t);
- v->_init = new ExpInitializer(Loc(), e);
- v->storage_class |= STCtemp | STCstatic | STCconst;
- dsymbolSemantic(v, sc);
- return v;
- }
-
- if (type)
- {
- /* $ gives the number of type entries in the type tuple
- */
- VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL);
- Expression *e = new IntegerExp(Loc(), type->arguments->length, Type::tsize_t);
- v->_init = new ExpInitializer(Loc(), e);
- v->storage_class |= STCtemp | STCstatic | STCconst;
- dsymbolSemantic(v, sc);
- return v;
- }
-
- if (exp->op == TOKindex)
- {
- /* array[index] where index is some function of $
- */
- IndexExp *ie = (IndexExp *)exp;
- pvar = &ie->lengthVar;
- ce = ie->e1;
- }
- else if (exp->op == TOKslice)
- {
- /* array[lwr .. upr] where lwr or upr is some function of $
- */
- SliceExp *se = (SliceExp *)exp;
- pvar = &se->lengthVar;
- ce = se->e1;
- }
- else if (exp->op == TOKarray)
- {
- /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $
- * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...)
- */
- ArrayExp *ae = (ArrayExp *)exp;
- pvar = &ae->lengthVar;
- ce = ae->e1;
- }
- else
- {
- /* Didn't find $, look in enclosing scope(s).
- */
- return NULL;
- }
-
- while (ce->op == TOKcomma)
- ce = ((CommaExp *)ce)->e2;
-
- /* If we are indexing into an array that is really a type
- * tuple, rewrite this as an index into a type tuple and
- * try again.
- */
- if (ce->op == TOKtype)
- {
- Type *t = ((TypeExp *)ce)->type;
- if (t->ty == Ttuple)
- {
- type = (TypeTuple *)t;
- goto L1;
- }
- }
-
- /* *pvar is lazily initialized, so if we refer to $
- * multiple times, it gets set only once.
- */
- if (!*pvar) // if not already initialized
- {
- /* Create variable v and set it to the value of $
- */
- VarDeclaration *v;
- Type *t;
- if (ce->op == TOKtuple)
- {
- /* It is for an expression tuple, so the
- * length will be a const.
- */
- Expression *e = new IntegerExp(Loc(), ((TupleExp *)ce)->exps->length, Type::tsize_t);
- v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, new ExpInitializer(Loc(), e));
- v->storage_class |= STCtemp | STCstatic | STCconst;
- }
- else if (ce->type && (t = ce->type->toBasetype()) != NULL &&
- (t->ty == Tstruct || t->ty == Tclass))
- {
- // Look for opDollar
- assert(exp->op == TOKarray || exp->op == TOKslice);
- AggregateDeclaration *ad = isAggregate(t);
- assert(ad);
-
- Dsymbol *s = ad->search(loc, Id::opDollar);
- if (!s) // no dollar exists -- search in higher scope
- return NULL;
- s = s->toAlias();
-
- Expression *e = NULL;
- // Check for multi-dimensional opDollar(dim) template.
- if (TemplateDeclaration *td = s->isTemplateDeclaration())
- {
- dinteger_t dim = 0;
- if (exp->op == TOKarray)
- {
- dim = ((ArrayExp *)exp)->currentDimension;
- }
- else if (exp->op == TOKslice)
- {
- dim = 0; // slices are currently always one-dimensional
- }
- else
- {
- assert(0);
- }
-
- Objects *tiargs = new Objects();
- Expression *edim = new IntegerExp(Loc(), dim, Type::tsize_t);
- edim = expressionSemantic(edim, sc);
- tiargs->push(edim);
- e = new DotTemplateInstanceExp(loc, ce, td->ident, tiargs);
- }
- else
- {
- /* opDollar exists, but it's not a template.
- * This is acceptable ONLY for single-dimension indexing.
- * Note that it's impossible to have both template & function opDollar,
- * because both take no arguments.
- */
- if (exp->op == TOKarray && ((ArrayExp *)exp)->arguments->length != 1)
- {
- exp->error("%s only defines opDollar for one dimension", ad->toChars());
- return NULL;
- }
- Declaration *d = s->isDeclaration();
- assert(d);
- e = new DotVarExp(loc, ce, d);
- }
- e = expressionSemantic(e, sc);
- if (!e->type)
- exp->error("%s has no value", e->toChars());
- t = e->type->toBasetype();
- if (t && t->ty == Tfunction)
- e = new CallExp(e->loc, e);
- v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(Loc(), e));
- v->storage_class |= STCtemp | STCctfe | STCrvalue;
- }
- else
- {
- /* For arrays, $ will either be a compile-time constant
- * (in which case its value in set during constant-folding),
- * or a variable (in which case an expression is created in
- * toir.c).
- */
- VoidInitializer *e = new VoidInitializer(Loc());
- e->type = Type::tsize_t;
- v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, e);
- v->storage_class |= STCtemp | STCctfe; // it's never a true static variable
- }
- *pvar = v;
- }
- dsymbolSemantic(*pvar, sc);
- return (*pvar);
- }
- return NULL;
-}
-
-
-/****************************** DsymbolTable ******************************/
-
-DsymbolTable::DsymbolTable()
-{
- tab = NULL;
-}
-
-Dsymbol *DsymbolTable::lookup(Identifier const * const ident)
-{
- //printf("DsymbolTable::lookup(%s)\n", (char*)ident->string);
- return (Dsymbol *)dmd_aaGetRvalue(tab, const_cast<void *>((const void *)ident));
-}
-
-Dsymbol *DsymbolTable::insert(Dsymbol *s)
-{
- //printf("DsymbolTable::insert(this = %p, '%s')\n", this, s->ident->toChars());
- Identifier *ident = s->ident;
- Dsymbol **ps = (Dsymbol **)dmd_aaGet(&tab, (void *)ident);
- if (*ps)
- return NULL; // already in table
- *ps = s;
- return s;
-}
-
-Dsymbol *DsymbolTable::insert(Identifier const * const ident, Dsymbol *s)
-{
- //printf("DsymbolTable::insert()\n");
- Dsymbol **ps = (Dsymbol **)dmd_aaGet(&tab, const_cast<void *>((const void *)ident));
- if (*ps)
- return NULL; // already in table
- *ps = s;
- return s;
-}
-
-Dsymbol *DsymbolTable::update(Dsymbol *s)
-{
- Identifier *ident = s->ident;
- Dsymbol **ps = (Dsymbol **)dmd_aaGet(&tab, (void *)ident);
- *ps = s;
- return s;
-}
-
-/****************************** Prot ******************************/
-
-Prot::Prot()
-{
- this->kind = Prot::undefined;
- this->pkg = NULL;
-}
-
-Prot::Prot(Prot::Kind kind)
-{
- this->kind = kind;
- this->pkg = NULL;
-}
-
-/**
- * Checks if `this` is superset of `other` restrictions.
- * For example, "protected" is more restrictive than "public".
- */
-bool Prot::isMoreRestrictiveThan(const Prot other) const
-{
- return this->kind < other.kind;
-}
-
-/**
- * Checks if `this` is absolutely identical protection attribute to `other`
- */
-bool Prot::operator==(const Prot& other) const
-{
- if (this->kind == other.kind)
- {
- if (this->kind == Prot::package_)
- return this->pkg == other.pkg;
- return true;
- }
- return false;
-}
diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d
new file mode 100644
index 00000000000..3a6dff2d44f
--- /dev/null
+++ b/gcc/d/dmd/dsymbol.d
@@ -0,0 +1,2386 @@
+/**
+ * The base class for a D symbol, which can be a module, variable, function, enum, etc.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dsymbol.d, _dsymbol.d)
+ * Documentation: https://dlang.org/phobos/dmd_dsymbol.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbol.d
+ */
+
+module dmd.dsymbol;
+
+import core.stdc.stdarg;
+import core.stdc.stdio;
+import core.stdc.string;
+import core.stdc.stdlib;
+
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arraytypes;
+import dmd.attrib;
+import dmd.astenums;
+import dmd.ast_node;
+import dmd.gluelayer;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dmodule;
+import dmd.dversion;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.lexer;
+import dmd.mtype;
+import dmd.nspace;
+import dmd.opover;
+import dmd.root.aav;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.root.speller;
+import dmd.root.string;
+import dmd.statement;
+import dmd.tokens;
+import dmd.visitor;
+
+/***************************************
+ * Calls dg(Dsymbol *sym) for each Dsymbol.
+ * If dg returns !=0, stops and returns that value else returns 0.
+ * Params:
+ * symbols = Dsymbols
+ * dg = delegate to call for each Dsymbol
+ * Returns:
+ * last value returned by dg()
+ *
+ * See_Also: $(REF each, dmd, root, array)
+ */
+int foreachDsymbol(Dsymbols* symbols, scope int delegate(Dsymbol) dg)
+{
+ assert(dg);
+ if (symbols)
+ {
+ /* Do not use foreach, as the size of the array may expand during iteration
+ */
+ for (size_t i = 0; i < symbols.dim; ++i)
+ {
+ Dsymbol s = (*symbols)[i];
+ const result = dg(s);
+ if (result)
+ return result;
+ }
+ }
+ return 0;
+}
+
+/***************************************
+ * Calls dg(Dsymbol *sym) for each Dsymbol.
+ * Params:
+ * symbols = Dsymbols
+ * dg = delegate to call for each Dsymbol
+ *
+ * See_Also: $(REF each, dmd, root, array)
+ */
+void foreachDsymbol(Dsymbols* symbols, scope void delegate(Dsymbol) dg)
+{
+ assert(dg);
+ if (symbols)
+ {
+ /* Do not use foreach, as the size of the array may expand during iteration
+ */
+ for (size_t i = 0; i < symbols.dim; ++i)
+ {
+ Dsymbol s = (*symbols)[i];
+ dg(s);
+ }
+ }
+}
+
+
+struct Ungag
+{
+ uint oldgag;
+
+ extern (D) this(uint old)
+ {
+ this.oldgag = old;
+ }
+
+ extern (C++) ~this()
+ {
+ global.gag = oldgag;
+ }
+}
+
+struct Visibility
+{
+ ///
+ enum Kind : ubyte
+ {
+ undefined,
+ none, // no access
+ private_,
+ package_,
+ protected_,
+ public_,
+ export_,
+ }
+
+ Kind kind;
+ Package pkg;
+
+ extern (D):
+
+ this(Visibility.Kind kind) pure nothrow @nogc @safe
+ {
+ this.kind = kind;
+ }
+
+ /**
+ * Checks if `this` is less or more visible than `other`
+ *
+ * Params:
+ * other = Visibility to compare `this` to.
+ *
+ * Returns:
+ * A value `< 0` if `this` is less visible than `other`,
+ * a value `> 0` if `this` is more visible than `other`,
+ * and `0` if they are at the same level.
+ * Note that `package` visibility with different packages
+ * will also return `0`.
+ */
+ int opCmp(const Visibility other) const pure nothrow @nogc @safe
+ {
+ return this.kind - other.kind;
+ }
+
+ ///
+ unittest
+ {
+ assert(Visibility(Visibility.Kind.public_) > Visibility(Visibility.Kind.private_));
+ assert(Visibility(Visibility.Kind.private_) < Visibility(Visibility.Kind.protected_));
+ assert(Visibility(Visibility.Kind.package_) >= Visibility(Visibility.Kind.package_));
+ }
+
+ /**
+ * Checks if `this` is absolutely identical visibility attribute to `other`
+ */
+ bool opEquals(ref const Visibility other) const
+ {
+ if (this.kind == other.kind)
+ {
+ if (this.kind == Visibility.Kind.package_)
+ return this.pkg == other.pkg;
+ return true;
+ }
+ return false;
+ }
+}
+
+enum PASS : int
+{
+ init, // initial state
+ semantic, // semantic() started
+ semanticdone, // semantic() done
+ semantic2, // semantic2() started
+ semantic2done, // semantic2() done
+ semantic3, // semantic3() started
+ semantic3done, // semantic3() done
+ inline, // inline started
+ inlinedone, // inline done
+ obj, // toObjFile() run
+}
+
+// Search options
+enum : int
+{
+ IgnoreNone = 0x00, // default
+ IgnorePrivateImports = 0x01, // don't search private imports
+ IgnoreErrors = 0x02, // don't give error messages
+ IgnoreAmbiguous = 0x04, // return NULL if ambiguous
+ SearchLocalsOnly = 0x08, // only look at locals (don't search imports)
+ SearchImportsOnly = 0x10, // only look in imports
+ SearchUnqualifiedModule = 0x20, // the module scope search is unqualified,
+ // meaning don't search imports in that scope,
+ // because qualified module searches search
+ // their imports
+ IgnoreSymbolVisibility = 0x80, // also find private and package protected symbols
+ TagNameSpace = 0x100, // search ImportC tag symbol table
+}
+
+/***********************************************************
+ * Struct/Class/Union field state.
+ * Used for transitory information when setting field offsets, such
+ * as bit fields.
+ */
+struct FieldState
+{
+ uint offset; /// offset for next field
+
+ uint fieldOffset; /// offset for the start of the bit field
+ uint bitOffset; /// bit offset for field
+ uint fieldSize; /// size of field in bytes
+ bool inFlight; /// bit field is in flight
+}
+
+/***********************************************************
+ */
+extern (C++) class Dsymbol : ASTNode
+{
+ Identifier ident;
+ Dsymbol parent;
+ /// C++ namespace this symbol belongs to
+ CPPNamespaceDeclaration cppnamespace;
+ Symbol* csym; // symbol for code generator
+ Symbol* isym; // import version of csym
+ const(char)* comment; // documentation comment for this Dsymbol
+ const Loc loc; // where defined
+ Scope* _scope; // !=null means context to use for semantic()
+ const(char)* prettystring; // cached value of toPrettyChars()
+ bool errors; // this symbol failed to pass semantic()
+ PASS semanticRun = PASS.init;
+ ushort localNum; /// perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab
+
+ DeprecatedDeclaration depdecl; // customized deprecation message
+ UserAttributeDeclaration userAttribDecl; // user defined attributes
+
+ // !=null means there's a ddoc unittest associated with this symbol
+ // (only use this with ddoc)
+ UnitTestDeclaration ddocUnittest;
+
+ final extern (D) this()
+ {
+ //printf("Dsymbol::Dsymbol(%p)\n", this);
+ loc = Loc(null, 0, 0);
+ }
+
+ final extern (D) this(Identifier ident)
+ {
+ //printf("Dsymbol::Dsymbol(%p, ident)\n", this);
+ this.loc = Loc(null, 0, 0);
+ this.ident = ident;
+ }
+
+ final extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ //printf("Dsymbol::Dsymbol(%p, ident)\n", this);
+ this.loc = loc;
+ this.ident = ident;
+ }
+
+ static Dsymbol create(Identifier ident)
+ {
+ return new Dsymbol(ident);
+ }
+
+ override const(char)* toChars() const
+ {
+ return ident ? ident.toChars() : "__anonymous";
+ }
+
+ // helper to print fully qualified (template) arguments
+ const(char)* toPrettyCharsHelper()
+ {
+ return toChars();
+ }
+
+ final const(Loc) getLoc()
+ {
+ if (!loc.isValid()) // avoid bug 5861.
+ if (const m = getModule())
+ return Loc(m.srcfile.toChars(), 0, 0);
+ return loc;
+ }
+
+ final const(char)* locToChars()
+ {
+ return getLoc().toChars();
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ if (o.dyncast() != DYNCAST.dsymbol)
+ return false;
+ auto s = cast(Dsymbol)o;
+ // Overload sets don't have an ident
+ // Function-local declarations may have identical names
+ // if they are declared in different scopes
+ if (s && ident && s.ident && ident.equals(s.ident) && localNum == s.localNum)
+ return true;
+ return false;
+ }
+
+ final bool isAnonymous() const
+ {
+ return ident is null || ident.isAnonymous;
+ }
+
+ extern(D) private const(char)[] prettyFormatHelper()
+ {
+ const cstr = toPrettyChars();
+ return '`' ~ cstr.toDString() ~ "`\0";
+ }
+
+ static if (__VERSION__ < 2092)
+ {
+ final void error(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .verror(loc, format, ap, kind(), prettyFormatHelper().ptr);
+ va_end(ap);
+ }
+
+ final void error(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ const loc = getLoc();
+ .verror(loc, format, ap, kind(), prettyFormatHelper().ptr);
+ va_end(ap);
+ }
+
+ final void deprecation(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr);
+ va_end(ap);
+ }
+
+ final void deprecation(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ const loc = getLoc();
+ .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr);
+ va_end(ap);
+ }
+ }
+ else
+ {
+ pragma(printf) final void error(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .verror(loc, format, ap, kind(), prettyFormatHelper().ptr);
+ va_end(ap);
+ }
+
+ pragma(printf) final void error(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ const loc = getLoc();
+ .verror(loc, format, ap, kind(), prettyFormatHelper().ptr);
+ va_end(ap);
+ }
+
+ pragma(printf) final void deprecation(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr);
+ va_end(ap);
+ }
+
+ pragma(printf) final void deprecation(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ const loc = getLoc();
+ .vdeprecation(loc, format, ap, kind(), prettyFormatHelper().ptr);
+ va_end(ap);
+ }
+ }
+
+ final bool checkDeprecated(const ref Loc loc, Scope* sc)
+ {
+ if (global.params.useDeprecated == DiagnosticReporting.off)
+ return false;
+ if (!this.isDeprecated())
+ return false;
+ // Don't complain if we're inside a deprecated symbol's scope
+ if (sc.isDeprecated())
+ return false;
+ // Don't complain if we're inside a template constraint
+ // https://issues.dlang.org/show_bug.cgi?id=21831
+ if (sc.flags & SCOPE.constraint)
+ return false;
+
+ const(char)* message = null;
+ for (Dsymbol p = this; p; p = p.parent)
+ {
+ message = p.depdecl ? p.depdecl.getMessage() : null;
+ if (message)
+ break;
+ }
+ if (message)
+ deprecation(loc, "is deprecated - %s", message);
+ else
+ deprecation(loc, "is deprecated");
+
+ if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
+ ti.printInstantiationTrace(Classification.deprecation);
+ else if (auto ti = sc.parent ? sc.parent.isTemplateInstance() : null)
+ ti.printInstantiationTrace(Classification.deprecation);
+
+ return true;
+ }
+
+ /**********************************
+ * Determine which Module a Dsymbol is in.
+ */
+ final Module getModule()
+ {
+ //printf("Dsymbol::getModule()\n");
+ if (TemplateInstance ti = isInstantiated())
+ return ti.tempdecl.getModule();
+ Dsymbol s = this;
+ while (s)
+ {
+ //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars());
+ Module m = s.isModule();
+ if (m)
+ return m;
+ s = s.parent;
+ }
+ return null;
+ }
+
+ /**********************************
+ * Determine which Module a Dsymbol is in, as far as access rights go.
+ */
+ final Module getAccessModule()
+ {
+ //printf("Dsymbol::getAccessModule()\n");
+ if (TemplateInstance ti = isInstantiated())
+ return ti.tempdecl.getAccessModule();
+ Dsymbol s = this;
+ while (s)
+ {
+ //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars());
+ Module m = s.isModule();
+ if (m)
+ return m;
+ TemplateInstance ti = s.isTemplateInstance();
+ if (ti && ti.enclosing)
+ {
+ /* Because of local template instantiation, the parent isn't where the access
+ * rights come from - it's the template declaration
+ */
+ s = ti.tempdecl;
+ }
+ else
+ s = s.parent;
+ }
+ return null;
+ }
+
+ /**
+ * `pastMixin` returns the enclosing symbol if this is a template mixin.
+ *
+ * `pastMixinAndNspace` does likewise, additionally skipping over Nspaces that
+ * are mangleOnly.
+ *
+ * See also `parent`, `toParent` and `toParent2`.
+ */
+ final inout(Dsymbol) pastMixin() inout
+ {
+ //printf("Dsymbol::pastMixin() %s\n", toChars());
+ if (!isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol())
+ return this;
+ if (!parent)
+ return null;
+ return parent.pastMixin();
+ }
+
+ /**********************************
+ * `parent` field returns a lexically enclosing scope symbol this is a member of.
+ *
+ * `toParent()` returns a logically enclosing scope symbol this is a member of.
+ * It skips over TemplateMixin's.
+ *
+ * `toParent2()` returns an enclosing scope symbol this is living at runtime.
+ * It skips over both TemplateInstance's and TemplateMixin's.
+ * It's used when looking for the 'this' pointer of the enclosing function/class.
+ *
+ * `toParentDecl()` similar to `toParent2()` but always follows the template declaration scope
+ * instead of the instantiation scope.
+ *
+ * `toParentLocal()` similar to `toParentDecl()` but follows the instantiation scope
+ * if a template declaration is non-local i.e. global or static.
+ *
+ * Examples:
+ * ---
+ * module mod;
+ * template Foo(alias a) { mixin Bar!(); }
+ * mixin template Bar() {
+ * public { // VisibilityDeclaration
+ * void baz() { a = 2; }
+ * }
+ * }
+ * void test() {
+ * int v = 1;
+ * alias foo = Foo!(v);
+ * foo.baz();
+ * assert(v == 2);
+ * }
+ *
+ * // s == FuncDeclaration('mod.test.Foo!().Bar!().baz()')
+ * // s.parent == TemplateMixin('mod.test.Foo!().Bar!()')
+ * // s.toParent() == TemplateInstance('mod.test.Foo!()')
+ * // s.toParent2() == FuncDeclaration('mod.test')
+ * // s.toParentDecl() == Module('mod')
+ * // s.toParentLocal() == FuncDeclaration('mod.test')
+ * ---
+ */
+ final inout(Dsymbol) toParent() inout
+ {
+ return parent ? parent.pastMixin() : null;
+ }
+
+ /// ditto
+ final inout(Dsymbol) toParent2() inout
+ {
+ if (!parent || !parent.isTemplateInstance && !parent.isForwardingAttribDeclaration() && !parent.isForwardingScopeDsymbol())
+ return parent;
+ return parent.toParent2;
+ }
+
+ /// ditto
+ final inout(Dsymbol) toParentDecl() inout
+ {
+ return toParentDeclImpl(false);
+ }
+
+ /// ditto
+ final inout(Dsymbol) toParentLocal() inout
+ {
+ return toParentDeclImpl(true);
+ }
+
+ private inout(Dsymbol) toParentDeclImpl(bool localOnly) inout
+ {
+ auto p = toParent();
+ if (!p || !p.isTemplateInstance())
+ return p;
+ auto ti = p.isTemplateInstance();
+ if (ti.tempdecl && (!localOnly || !(cast(TemplateDeclaration)ti.tempdecl).isstatic))
+ return ti.tempdecl.toParentDeclImpl(localOnly);
+ return parent.toParentDeclImpl(localOnly);
+ }
+
+ /**
+ * Returns the declaration scope scope of `this` unless any of the symbols
+ * `p1` or `p2` resides in its enclosing instantiation scope then the
+ * latter is returned.
+ */
+ final Dsymbol toParentP(Dsymbol p1, Dsymbol p2 = null)
+ {
+ return followInstantiationContext(p1, p2) ? toParent2() : toParentLocal();
+ }
+
+ final inout(TemplateInstance) isInstantiated() inout
+ {
+ if (!parent)
+ return null;
+ auto ti = parent.isTemplateInstance();
+ if (ti && !ti.isTemplateMixin())
+ return ti;
+ return parent.isInstantiated();
+ }
+
+ /***
+ * Returns true if any of the symbols `p1` or `p2` resides in the enclosing
+ * instantiation scope of `this`.
+ */
+ final bool followInstantiationContext(Dsymbol p1, Dsymbol p2 = null)
+ {
+ static bool has2This(Dsymbol s)
+ {
+ if (auto f = s.isFuncDeclaration())
+ return f.isThis2;
+ if (auto ad = s.isAggregateDeclaration())
+ return ad.vthis2 !is null;
+ return false;
+ }
+
+ if (has2This(this))
+ {
+ assert(p1);
+ auto outer = toParent();
+ while (outer)
+ {
+ auto ti = outer.isTemplateInstance();
+ if (!ti)
+ break;
+ foreach (oarg; *ti.tiargs)
+ {
+ auto sa = getDsymbol(oarg);
+ if (!sa)
+ continue;
+ sa = sa.toAlias().toParent2();
+ if (!sa)
+ continue;
+ if (sa == p1)
+ return true;
+ else if (p2 && sa == p2)
+ return true;
+ }
+ outer = ti.tempdecl.toParent();
+ }
+ return false;
+ }
+ return false;
+ }
+
+ // Check if this function is a member of a template which has only been
+ // instantiated speculatively, eg from inside is(typeof()).
+ // Return the speculative template instance it is part of,
+ // or NULL if not speculative.
+ final inout(TemplateInstance) isSpeculative() inout
+ {
+ if (!parent)
+ return null;
+ auto ti = parent.isTemplateInstance();
+ if (ti && ti.gagged)
+ return ti;
+ if (!parent.toParent())
+ return null;
+ return parent.isSpeculative();
+ }
+
+ final Ungag ungagSpeculative() const
+ {
+ uint oldgag = global.gag;
+ if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration())
+ global.gag = 0;
+ return Ungag(oldgag);
+ }
+
+ // kludge for template.isSymbol()
+ override final DYNCAST dyncast() const
+ {
+ return DYNCAST.dsymbol;
+ }
+
+ /*************************************
+ * Do syntax copy of an array of Dsymbol's.
+ */
+ extern (D) static Dsymbols* arraySyntaxCopy(Dsymbols* a)
+ {
+ Dsymbols* b = null;
+ if (a)
+ {
+ b = a.copy();
+ for (size_t i = 0; i < b.dim; i++)
+ {
+ (*b)[i] = (*b)[i].syntaxCopy(null);
+ }
+ }
+ return b;
+ }
+
+ Identifier getIdent()
+ {
+ return ident;
+ }
+
+ const(char)* toPrettyChars(bool QualifyTypes = false)
+ {
+ if (prettystring && !QualifyTypes)
+ return prettystring;
+
+ //printf("Dsymbol::toPrettyChars() '%s'\n", toChars());
+ if (!parent)
+ {
+ auto s = toChars();
+ if (!QualifyTypes)
+ prettystring = s;
+ return s;
+ }
+
+ // Computer number of components
+ size_t complength = 0;
+ for (Dsymbol p = this; p; p = p.parent)
+ ++complength;
+
+ // Allocate temporary array comp[]
+ alias T = const(char)[];
+ auto compptr = cast(T*)Mem.check(malloc(complength * T.sizeof));
+ auto comp = compptr[0 .. complength];
+
+ // Fill in comp[] and compute length of final result
+ size_t length = 0;
+ int i;
+ for (Dsymbol p = this; p; p = p.parent)
+ {
+ const s = QualifyTypes ? p.toPrettyCharsHelper() : p.toChars();
+ const len = strlen(s);
+ comp[i] = s[0 .. len];
+ ++i;
+ length += len + 1;
+ }
+
+ auto s = cast(char*)mem.xmalloc_noscan(length);
+ auto q = s + length - 1;
+ *q = 0;
+ foreach (j; 0 .. complength)
+ {
+ const t = comp[j].ptr;
+ const len = comp[j].length;
+ q -= len;
+ memcpy(q, t, len);
+ if (q == s)
+ break;
+ *--q = '.';
+ }
+ free(comp.ptr);
+ if (!QualifyTypes)
+ prettystring = s;
+ return s;
+ }
+
+ const(char)* kind() const pure nothrow @nogc @safe
+ {
+ return "symbol";
+ }
+
+ /*********************************
+ * If this symbol is really an alias for another,
+ * return that other.
+ * If needed, semantic() is invoked due to resolve forward reference.
+ */
+ Dsymbol toAlias()
+ {
+ return this;
+ }
+
+ /*********************************
+ * Resolve recursive tuple expansion in eponymous template.
+ */
+ Dsymbol toAlias2()
+ {
+ return toAlias();
+ }
+
+ void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ //printf("Dsymbol::addMember('%s')\n", toChars());
+ //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sds.toChars());
+ //printf("Dsymbol::addMember(this = %p, '%s' sds = %p, sds.symtab = %p)\n", this, toChars(), sds, sds.symtab);
+ parent = sds;
+ if (isAnonymous()) // no name, so can't add it to symbol table
+ return;
+
+ if (!sds.symtabInsert(this)) // if name is already defined
+ {
+ if (isAliasDeclaration() && !_scope)
+ setScope(sc);
+ Dsymbol s2 = sds.symtabLookup(this,ident);
+
+ // If using C tag/prototype/forward declaration rules
+ if (sc.flags & SCOPE.Cfile &&
+ handleTagSymbols(*sc, this, s2, sds))
+ return;
+
+ if (!s2.overloadInsert(this))
+ {
+ sds.multiplyDefined(Loc.initial, this, s2);
+ errors = true;
+ }
+ }
+ if (sds.isAggregateDeclaration() || sds.isEnumDeclaration())
+ {
+ if (ident == Id.__sizeof || ident == Id.__xalignof || ident == Id._mangleof)
+ {
+ error("`.%s` property cannot be redefined", ident.toChars());
+ errors = true;
+ }
+ }
+ }
+
+ /*************************************
+ * Set scope for future semantic analysis so we can
+ * deal better with forward references.
+ */
+ void setScope(Scope* sc)
+ {
+ //printf("Dsymbol::setScope() %p %s, %p stc = %llx\n", this, toChars(), sc, sc.stc);
+ if (!sc.nofree)
+ sc.setNoFree(); // may need it even after semantic() finishes
+ _scope = sc;
+ if (sc.depdecl)
+ depdecl = sc.depdecl;
+ if (!userAttribDecl)
+ userAttribDecl = sc.userAttribDecl;
+ }
+
+ void importAll(Scope* sc)
+ {
+ }
+
+ /*********************************************
+ * Search for ident as member of s.
+ * Params:
+ * loc = location to print for error messages
+ * ident = identifier to search for
+ * flags = IgnoreXXXX
+ * Returns:
+ * null if not found
+ */
+ Dsymbol search(const ref Loc loc, Identifier ident, int flags = IgnoreNone)
+ {
+ //printf("Dsymbol::search(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars());
+ return null;
+ }
+
+ extern (D) final Dsymbol search_correct(Identifier ident)
+ {
+ /***************************************************
+ * Search for symbol with correct spelling.
+ */
+ extern (D) Dsymbol symbol_search_fp(const(char)[] seed, out int cost)
+ {
+ /* If not in the lexer's string table, it certainly isn't in the symbol table.
+ * Doing this first is a lot faster.
+ */
+ if (!seed.length)
+ return null;
+ Identifier id = Identifier.lookup(seed);
+ if (!id)
+ return null;
+ cost = 0; // all the same cost
+ Dsymbol s = this;
+ Module.clearCache();
+ return s.search(Loc.initial, id, IgnoreErrors);
+ }
+
+ if (global.gag)
+ return null; // don't do it for speculative compiles; too time consuming
+ // search for exact name first
+ if (auto s = search(Loc.initial, ident, IgnoreErrors))
+ return s;
+ return speller!symbol_search_fp(ident.toString());
+ }
+
+ /***************************************
+ * Search for identifier id as a member of `this`.
+ * `id` may be a template instance.
+ *
+ * Params:
+ * loc = location to print the error messages
+ * sc = the scope where the symbol is located
+ * id = the id of the symbol
+ * flags = the search flags which can be `SearchLocalsOnly` or `IgnorePrivateImports`
+ *
+ * Returns:
+ * symbol found, NULL if not
+ */
+ extern (D) final Dsymbol searchX(const ref Loc loc, Scope* sc, RootObject id, int flags)
+ {
+ //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars());
+ Dsymbol s = toAlias();
+ Dsymbol sm;
+ if (Declaration d = s.isDeclaration())
+ {
+ if (d.inuse)
+ {
+ .error(loc, "circular reference to `%s`", d.toPrettyChars());
+ return null;
+ }
+ }
+ switch (id.dyncast())
+ {
+ case DYNCAST.identifier:
+ sm = s.search(loc, cast(Identifier)id, flags);
+ break;
+ case DYNCAST.dsymbol:
+ {
+ // It's a template instance
+ //printf("\ttemplate instance id\n");
+ Dsymbol st = cast(Dsymbol)id;
+ TemplateInstance ti = st.isTemplateInstance();
+ sm = s.search(loc, ti.name);
+ if (!sm)
+ {
+ sm = s.search_correct(ti.name);
+ if (sm)
+ .error(loc, "template identifier `%s` is not a member of %s `%s`, did you mean %s `%s`?", ti.name.toChars(), s.kind(), s.toPrettyChars(), sm.kind(), sm.toChars());
+ else
+ .error(loc, "template identifier `%s` is not a member of %s `%s`", ti.name.toChars(), s.kind(), s.toPrettyChars());
+ return null;
+ }
+ sm = sm.toAlias();
+ TemplateDeclaration td = sm.isTemplateDeclaration();
+ if (!td)
+ {
+ .error(loc, "`%s.%s` is not a template, it is a %s", s.toPrettyChars(), ti.name.toChars(), sm.kind());
+ return null;
+ }
+ ti.tempdecl = td;
+ if (!ti.semanticRun)
+ ti.dsymbolSemantic(sc);
+ sm = ti.toAlias();
+ break;
+ }
+ case DYNCAST.type:
+ case DYNCAST.expression:
+ default:
+ assert(0);
+ }
+ return sm;
+ }
+
+ bool overloadInsert(Dsymbol s)
+ {
+ //printf("Dsymbol::overloadInsert('%s')\n", s.toChars());
+ return false;
+ }
+
+ /*********************************
+ * Returns:
+ * SIZE_INVALID when the size cannot be determined
+ */
+ d_uns64 size(const ref Loc loc)
+ {
+ error("Dsymbol `%s` has no size", toChars());
+ return SIZE_INVALID;
+ }
+
+ bool isforwardRef()
+ {
+ return false;
+ }
+
+ // is a 'this' required to access the member
+ inout(AggregateDeclaration) isThis() inout
+ {
+ return null;
+ }
+
+ // is Dsymbol exported?
+ bool isExport() const
+ {
+ return false;
+ }
+
+ // is Dsymbol imported?
+ bool isImportedSymbol() const
+ {
+ return false;
+ }
+
+ // is Dsymbol deprecated?
+ bool isDeprecated() @safe @nogc pure nothrow const
+ {
+ return false;
+ }
+
+ bool isOverloadable() const
+ {
+ return false;
+ }
+
+ // is this a LabelDsymbol()?
+ LabelDsymbol isLabel()
+ {
+ return null;
+ }
+
+ /// Returns an AggregateDeclaration when toParent() is that.
+ final inout(AggregateDeclaration) isMember() inout
+ {
+ //printf("Dsymbol::isMember() %s\n", toChars());
+ auto p = toParent();
+ //printf("parent is %s %s\n", p.kind(), p.toChars());
+ return p ? p.isAggregateDeclaration() : null;
+ }
+
+ /// Returns an AggregateDeclaration when toParent2() is that.
+ final inout(AggregateDeclaration) isMember2() inout
+ {
+ //printf("Dsymbol::isMember2() '%s'\n", toChars());
+ auto p = toParent2();
+ //printf("parent is %s %s\n", p.kind(), p.toChars());
+ return p ? p.isAggregateDeclaration() : null;
+ }
+
+ /// Returns an AggregateDeclaration when toParentDecl() is that.
+ final inout(AggregateDeclaration) isMemberDecl() inout
+ {
+ //printf("Dsymbol::isMemberDecl() '%s'\n", toChars());
+ auto p = toParentDecl();
+ //printf("parent is %s %s\n", p.kind(), p.toChars());
+ return p ? p.isAggregateDeclaration() : null;
+ }
+
+ /// Returns an AggregateDeclaration when toParentLocal() is that.
+ final inout(AggregateDeclaration) isMemberLocal() inout
+ {
+ //printf("Dsymbol::isMemberLocal() '%s'\n", toChars());
+ auto p = toParentLocal();
+ //printf("parent is %s %s\n", p.kind(), p.toChars());
+ return p ? p.isAggregateDeclaration() : null;
+ }
+
+ // is this a member of a ClassDeclaration?
+ final ClassDeclaration isClassMember()
+ {
+ auto ad = isMember();
+ return ad ? ad.isClassDeclaration() : null;
+ }
+
+ // is this a type?
+ Type getType()
+ {
+ return null;
+ }
+
+ // need a 'this' pointer?
+ bool needThis()
+ {
+ return false;
+ }
+
+ /*************************************
+ */
+ Visibility visible() pure nothrow @nogc @safe
+ {
+ return Visibility(Visibility.Kind.public_);
+ }
+
+ /**************************************
+ * Copy the syntax.
+ * Used for template instantiations.
+ * If s is NULL, allocate the new object, otherwise fill it in.
+ */
+ Dsymbol syntaxCopy(Dsymbol s)
+ {
+ printf("%s %s\n", kind(), toChars());
+ assert(0);
+ }
+
+ /**************************************
+ * Determine if this symbol is only one.
+ * Returns:
+ * false, *ps = NULL: There are 2 or more symbols
+ * true, *ps = NULL: There are zero symbols
+ * true, *ps = symbol: The one and only one symbol
+ */
+ bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ //printf("Dsymbol::oneMember()\n");
+ *ps = this;
+ return true;
+ }
+
+ /*****************************************
+ * Same as Dsymbol::oneMember(), but look at an array of Dsymbols.
+ */
+ extern (D) static bool oneMembers(Dsymbols* members, Dsymbol* ps, Identifier ident)
+ {
+ //printf("Dsymbol::oneMembers() %d\n", members ? members.dim : 0);
+ Dsymbol s = null;
+ if (!members)
+ {
+ *ps = null;
+ return true;
+ }
+
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ Dsymbol sx = (*members)[i];
+ bool x = sx.oneMember(ps, ident);
+ //printf("\t[%d] kind %s = %d, s = %p\n", i, sx.kind(), x, *ps);
+ if (!x)
+ {
+ //printf("\tfalse 1\n");
+ assert(*ps is null);
+ return false;
+ }
+ if (*ps)
+ {
+ assert(ident);
+ if (!(*ps).ident || !(*ps).ident.equals(ident))
+ continue;
+ if (!s)
+ s = *ps;
+ else if (s.isOverloadable() && (*ps).isOverloadable())
+ {
+ // keep head of overload set
+ FuncDeclaration f1 = s.isFuncDeclaration();
+ FuncDeclaration f2 = (*ps).isFuncDeclaration();
+ if (f1 && f2)
+ {
+ assert(!f1.isFuncAliasDeclaration());
+ assert(!f2.isFuncAliasDeclaration());
+ for (; f1 != f2; f1 = f1.overnext0)
+ {
+ if (f1.overnext0 is null)
+ {
+ f1.overnext0 = f2;
+ break;
+ }
+ }
+ }
+ }
+ else // more than one symbol
+ {
+ *ps = null;
+ //printf("\tfalse 2\n");
+ return false;
+ }
+ }
+ }
+ *ps = s; // s is the one symbol, null if none
+ //printf("\ttrue\n");
+ return true;
+ }
+
+ void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
+ {
+ }
+
+ /*****************************************
+ * Is Dsymbol a variable that contains pointers?
+ */
+ bool hasPointers()
+ {
+ //printf("Dsymbol::hasPointers() %s\n", toChars());
+ return false;
+ }
+
+ bool hasStaticCtorOrDtor()
+ {
+ //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars());
+ return false;
+ }
+
+ void addLocalClass(ClassDeclarations*)
+ {
+ }
+
+ void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories)
+ {
+ }
+
+ void checkCtorConstInit()
+ {
+ }
+
+ /****************************************
+ * Add documentation comment to Dsymbol.
+ * Ignore NULL comments.
+ */
+ void addComment(const(char)* comment)
+ {
+ //if (comment)
+ // printf("adding comment '%s' to symbol %p '%s'\n", comment, this, toChars());
+ if (!this.comment)
+ this.comment = comment;
+ else if (comment && strcmp(cast(char*)comment, cast(char*)this.comment) != 0)
+ {
+ // Concatenate the two
+ this.comment = Lexer.combineComments(this.comment.toDString(), comment.toDString(), true);
+ }
+ }
+
+ /****************************************
+ * Returns true if this symbol is defined in a non-root module without instantiation.
+ */
+ final bool inNonRoot()
+ {
+ Dsymbol s = parent;
+ for (; s; s = s.toParent())
+ {
+ if (auto ti = s.isTemplateInstance())
+ {
+ return false;
+ }
+ if (auto m = s.isModule())
+ {
+ if (!m.isRoot())
+ return true;
+ break;
+ }
+ }
+ return false;
+ }
+
+ /************
+ */
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ pure nothrow @safe @nogc:
+
+ // Eliminate need for dynamic_cast
+ inout(Package) isPackage() inout { return null; }
+ inout(Module) isModule() inout { return null; }
+ inout(EnumMember) isEnumMember() inout { return null; }
+ inout(TemplateDeclaration) isTemplateDeclaration() inout { return null; }
+ inout(TemplateInstance) isTemplateInstance() inout { return null; }
+ inout(TemplateMixin) isTemplateMixin() inout { return null; }
+ inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout { return null; }
+ inout(Nspace) isNspace() inout { return null; }
+ inout(Declaration) isDeclaration() inout { return null; }
+ inout(StorageClassDeclaration) isStorageClassDeclaration() inout { return null; }
+ inout(ExpressionDsymbol) isExpressionDsymbol() inout { return null; }
+ inout(AliasAssign) isAliasAssign() inout { return null; }
+ inout(ThisDeclaration) isThisDeclaration() inout { return null; }
+ inout(BitFieldDeclaration) isBitFieldDeclaration() inout { return null; }
+ inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout { return null; }
+ inout(TupleDeclaration) isTupleDeclaration() inout { return null; }
+ inout(AliasDeclaration) isAliasDeclaration() inout { return null; }
+ inout(AggregateDeclaration) isAggregateDeclaration() inout { return null; }
+ inout(FuncDeclaration) isFuncDeclaration() inout { return null; }
+ inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout { return null; }
+ inout(OverDeclaration) isOverDeclaration() inout { return null; }
+ inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout { return null; }
+ inout(CtorDeclaration) isCtorDeclaration() inout { return null; }
+ inout(PostBlitDeclaration) isPostBlitDeclaration() inout { return null; }
+ inout(DtorDeclaration) isDtorDeclaration() inout { return null; }
+ inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout { return null; }
+ inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout { return null; }
+ inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout { return null; }
+ inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout { return null; }
+ inout(InvariantDeclaration) isInvariantDeclaration() inout { return null; }
+ inout(UnitTestDeclaration) isUnitTestDeclaration() inout { return null; }
+ inout(NewDeclaration) isNewDeclaration() inout { return null; }
+ inout(VarDeclaration) isVarDeclaration() inout { return null; }
+ inout(VersionSymbol) isVersionSymbol() inout { return null; }
+ inout(DebugSymbol) isDebugSymbol() inout { return null; }
+ inout(ClassDeclaration) isClassDeclaration() inout { return null; }
+ inout(StructDeclaration) isStructDeclaration() inout { return null; }
+ inout(UnionDeclaration) isUnionDeclaration() inout { return null; }
+ inout(InterfaceDeclaration) isInterfaceDeclaration() inout { return null; }
+ inout(ScopeDsymbol) isScopeDsymbol() inout { return null; }
+ inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout { return null; }
+ inout(WithScopeSymbol) isWithScopeSymbol() inout { return null; }
+ inout(ArrayScopeSymbol) isArrayScopeSymbol() inout { return null; }
+ inout(Import) isImport() inout { return null; }
+ inout(EnumDeclaration) isEnumDeclaration() inout { return null; }
+ inout(SymbolDeclaration) isSymbolDeclaration() inout { return null; }
+ inout(AttribDeclaration) isAttribDeclaration() inout { return null; }
+ inout(AnonDeclaration) isAnonDeclaration() inout { return null; }
+ inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return null; }
+ inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return null; }
+ inout(OverloadSet) isOverloadSet() inout { return null; }
+ inout(CompileDeclaration) isCompileDeclaration() inout { return null; }
+}
+
+/***********************************************************
+ * Dsymbol that generates a scope
+ */
+extern (C++) class ScopeDsymbol : Dsymbol
+{
+ Dsymbols* members; // all Dsymbol's in this scope
+ DsymbolTable symtab; // members[] sorted into table
+ uint endlinnum; // the linnumber of the statement after the scope (0 if unknown)
+
+private:
+ /// symbols whose members have been imported, i.e. imported modules and template mixins
+ Dsymbols* importedScopes;
+ Visibility.Kind* visibilities; // array of Visibility.Kind, one for each import
+
+ import dmd.root.bitarray;
+ BitArray accessiblePackages, privateAccessiblePackages;// whitelists of accessible (imported) packages
+
+public:
+ final extern (D) this()
+ {
+ }
+
+ final extern (D) this(Identifier ident)
+ {
+ super(ident);
+ }
+
+ final extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, ident);
+ }
+
+ override ScopeDsymbol syntaxCopy(Dsymbol s)
+ {
+ //printf("ScopeDsymbol::syntaxCopy('%s')\n", toChars());
+ ScopeDsymbol sds = s ? cast(ScopeDsymbol)s : new ScopeDsymbol(ident);
+ sds.comment = comment;
+ sds.members = arraySyntaxCopy(members);
+ sds.endlinnum = endlinnum;
+ return sds;
+ }
+
+ /*****************************************
+ * This function is #1 on the list of functions that eat cpu time.
+ * Be very, very careful about slowing it down.
+ */
+ override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ //printf("%s.ScopeDsymbol::search(ident='%s', flags=x%x)\n", toChars(), ident.toChars(), flags);
+ //if (strcmp(ident.toChars(),"c") == 0) *(char*)0=0;
+
+ // Look in symbols declared in this module
+ if (symtab && !(flags & SearchImportsOnly))
+ {
+ //printf(" look in locals\n");
+ auto s1 = symtab.lookup(ident);
+ if (s1)
+ {
+ //printf("\tfound in locals = '%s.%s'\n",toChars(),s1.toChars());
+ return s1;
+ }
+ }
+ //printf(" not found in locals\n");
+
+ // Look in imported scopes
+ if (!importedScopes)
+ return null;
+
+ //printf(" look in imports\n");
+ Dsymbol s = null;
+ OverloadSet a = null;
+ // Look in imported modules
+ for (size_t i = 0; i < importedScopes.dim; i++)
+ {
+ // If private import, don't search it
+ if ((flags & IgnorePrivateImports) && visibilities[i] == Visibility.Kind.private_)
+ continue;
+ int sflags = flags & (IgnoreErrors | IgnoreAmbiguous); // remember these in recursive searches
+ Dsymbol ss = (*importedScopes)[i];
+ //printf("\tscanning import '%s', visibilities = %d, isModule = %p, isImport = %p\n", ss.toChars(), visibilities[i], ss.isModule(), ss.isImport());
+
+ if (ss.isModule())
+ {
+ if (flags & SearchLocalsOnly)
+ continue;
+ }
+ else // mixin template
+ {
+ if (flags & SearchImportsOnly)
+ continue;
+
+ sflags |= SearchLocalsOnly;
+ }
+
+ /* Don't find private members if ss is a module
+ */
+ Dsymbol s2 = ss.search(loc, ident, sflags | (ss.isModule() ? IgnorePrivateImports : IgnoreNone));
+ import dmd.access : symbolIsVisible;
+ if (!s2 || !(flags & IgnoreSymbolVisibility) && !symbolIsVisible(this, s2))
+ continue;
+ if (!s)
+ {
+ s = s2;
+ if (s && s.isOverloadSet())
+ a = mergeOverloadSet(ident, a, s);
+ }
+ else if (s2 && s != s2)
+ {
+ if (s.toAlias() == s2.toAlias() || s.getType() == s2.getType() && s.getType())
+ {
+ /* After following aliases, we found the same
+ * symbol, so it's not an ambiguity. But if one
+ * alias is deprecated or less accessible, prefer
+ * the other.
+ */
+ if (s.isDeprecated() || s.visible() < s2.visible() && s2.visible().kind != Visibility.Kind.none)
+ s = s2;
+ }
+ else
+ {
+ /* Two imports of the same module should be regarded as
+ * the same.
+ */
+ Import i1 = s.isImport();
+ Import i2 = s2.isImport();
+ if (!(i1 && i2 && (i1.mod == i2.mod || (!i1.parent.isImport() && !i2.parent.isImport() && i1.ident.equals(i2.ident)))))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=8668
+ * Public selective import adds AliasDeclaration in module.
+ * To make an overload set, resolve aliases in here and
+ * get actual overload roots which accessible via s and s2.
+ */
+ s = s.toAlias();
+ s2 = s2.toAlias();
+ /* If both s2 and s are overloadable (though we only
+ * need to check s once)
+ */
+
+ auto so2 = s2.isOverloadSet();
+ if ((so2 || s2.isOverloadable()) && (a || s.isOverloadable()))
+ {
+ if (symbolIsVisible(this, s2))
+ {
+ a = mergeOverloadSet(ident, a, s2);
+ }
+ if (!symbolIsVisible(this, s))
+ s = s2;
+ continue;
+ }
+
+ /* Two different overflow sets can have the same members
+ * https://issues.dlang.org/show_bug.cgi?id=16709
+ */
+ auto so = s.isOverloadSet();
+ if (so && so2)
+ {
+ if (so.a.length == so2.a.length)
+ {
+ foreach (j; 0 .. so.a.length)
+ {
+ if (so.a[j] !is so2.a[j])
+ goto L1;
+ }
+ continue; // the same
+ L1:
+ { } // different
+ }
+ }
+
+ if (flags & IgnoreAmbiguous) // if return NULL on ambiguity
+ return null;
+ if (!(flags & IgnoreErrors))
+ ScopeDsymbol.multiplyDefined(loc, s, s2);
+ break;
+ }
+ }
+ }
+ }
+ if (s)
+ {
+ /* Build special symbol if we had multiple finds
+ */
+ if (a)
+ {
+ if (!s.isOverloadSet())
+ a = mergeOverloadSet(ident, a, s);
+ s = a;
+ }
+ //printf("\tfound in imports %s.%s\n", toChars(), s.toChars());
+ return s;
+ }
+ //printf(" not found in imports\n");
+ return null;
+ }
+
+ extern (D) private OverloadSet mergeOverloadSet(Identifier ident, OverloadSet os, Dsymbol s)
+ {
+ if (!os)
+ {
+ os = new OverloadSet(ident);
+ os.parent = this;
+ }
+ if (OverloadSet os2 = s.isOverloadSet())
+ {
+ // Merge the cross-module overload set 'os2' into 'os'
+ if (os.a.dim == 0)
+ {
+ os.a.setDim(os2.a.dim);
+ memcpy(os.a.tdata(), os2.a.tdata(), (os.a[0]).sizeof * os2.a.dim);
+ }
+ else
+ {
+ for (size_t i = 0; i < os2.a.dim; i++)
+ {
+ os = mergeOverloadSet(ident, os, os2.a[i]);
+ }
+ }
+ }
+ else
+ {
+ assert(s.isOverloadable());
+ /* Don't add to os[] if s is alias of previous sym
+ */
+ for (size_t j = 0; j < os.a.dim; j++)
+ {
+ Dsymbol s2 = os.a[j];
+ if (s.toAlias() == s2.toAlias())
+ {
+ if (s2.isDeprecated() || (s2.visible() < s.visible() && s.visible().kind != Visibility.Kind.none))
+ {
+ os.a[j] = s;
+ }
+ goto Lcontinue;
+ }
+ }
+ os.push(s);
+ Lcontinue:
+ }
+ return os;
+ }
+
+ void importScope(Dsymbol s, Visibility visibility)
+ {
+ //printf("%s.ScopeDsymbol::importScope(%s, %d)\n", toChars(), s.toChars(), visibility);
+ // No circular or redundant import's
+ if (s != this)
+ {
+ if (!importedScopes)
+ importedScopes = new Dsymbols();
+ else
+ {
+ for (size_t i = 0; i < importedScopes.dim; i++)
+ {
+ Dsymbol ss = (*importedScopes)[i];
+ if (ss == s) // if already imported
+ {
+ if (visibility.kind > visibilities[i])
+ visibilities[i] = visibility.kind; // upgrade access
+ return;
+ }
+ }
+ }
+ importedScopes.push(s);
+ visibilities = cast(Visibility.Kind*)mem.xrealloc(visibilities, importedScopes.dim * (visibilities[0]).sizeof);
+ visibilities[importedScopes.dim - 1] = visibility.kind;
+ }
+ }
+
+ extern (D) final void addAccessiblePackage(Package p, Visibility visibility)
+ {
+ auto pary = visibility.kind == Visibility.Kind.private_ ? &privateAccessiblePackages : &accessiblePackages;
+ if (pary.length <= p.tag)
+ pary.length = p.tag + 1;
+ (*pary)[p.tag] = true;
+ }
+
+ bool isPackageAccessible(Package p, Visibility visibility, int flags = 0)
+ {
+ if (p.tag < accessiblePackages.length && accessiblePackages[p.tag] ||
+ visibility.kind == Visibility.Kind.private_ && p.tag < privateAccessiblePackages.length && privateAccessiblePackages[p.tag])
+ return true;
+ foreach (i, ss; importedScopes ? (*importedScopes)[] : null)
+ {
+ // only search visible scopes && imported modules should ignore private imports
+ if (visibility.kind <= visibilities[i] &&
+ ss.isScopeDsymbol.isPackageAccessible(p, visibility, IgnorePrivateImports))
+ return true;
+ }
+ return false;
+ }
+
+ override final bool isforwardRef()
+ {
+ return (members is null);
+ }
+
+ static void multiplyDefined(const ref Loc loc, Dsymbol s1, Dsymbol s2)
+ {
+ version (none)
+ {
+ printf("ScopeDsymbol::multiplyDefined()\n");
+ printf("s1 = %p, '%s' kind = '%s', parent = %s\n", s1, s1.toChars(), s1.kind(), s1.parent ? s1.parent.toChars() : "");
+ printf("s2 = %p, '%s' kind = '%s', parent = %s\n", s2, s2.toChars(), s2.kind(), s2.parent ? s2.parent.toChars() : "");
+ }
+ if (loc.isValid())
+ {
+ .error(loc, "%s `%s` at %s conflicts with %s `%s` at %s",
+ s1.kind(), s1.toPrettyChars(), s1.locToChars(),
+ s2.kind(), s2.toPrettyChars(), s2.locToChars());
+
+ static if (0)
+ {
+ if (auto so = s1.isOverloadSet())
+ {
+ printf("first %p:\n", so);
+ foreach (s; so.a[])
+ {
+ printf(" %p %s `%s` at %s\n", s, s.kind(), s.toPrettyChars(), s.locToChars());
+ }
+ }
+ if (auto so = s2.isOverloadSet())
+ {
+ printf("second %p:\n", so);
+ foreach (s; so.a[])
+ {
+ printf(" %p %s `%s` at %s\n", s, s.kind(), s.toPrettyChars(), s.locToChars());
+ }
+ }
+ }
+ }
+ else
+ {
+ s1.error(s1.loc, "conflicts with %s `%s` at %s", s2.kind(), s2.toPrettyChars(), s2.locToChars());
+ }
+ }
+
+ override const(char)* kind() const
+ {
+ return "ScopeDsymbol";
+ }
+
+ /*******************************************
+ * Look for member of the form:
+ * const(MemberInfo)[] getMembers(string);
+ * Returns NULL if not found
+ */
+ final FuncDeclaration findGetMembers()
+ {
+ Dsymbol s = search_function(this, Id.getmembers);
+ FuncDeclaration fdx = s ? s.isFuncDeclaration() : null;
+ version (none)
+ {
+ // Finish
+ __gshared TypeFunction tfgetmembers;
+ if (!tfgetmembers)
+ {
+ Scope sc;
+ auto parameters = new Parameters();
+ Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
+ parameters.push(p);
+ Type tret = null;
+ tfgetmembers = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
+ tfgetmembers = cast(TypeFunction)tfgetmembers.dsymbolSemantic(Loc.initial, &sc);
+ }
+ if (fdx)
+ fdx = fdx.overloadExactMatch(tfgetmembers);
+ }
+ if (fdx && fdx.isVirtual())
+ fdx = null;
+ return fdx;
+ }
+
+ /********************************
+ * Insert Dsymbol in table.
+ * Params:
+ * s = symbol to add
+ * Returns:
+ * null if already in table, `s` if inserted
+ */
+ Dsymbol symtabInsert(Dsymbol s)
+ {
+ return symtab.insert(s);
+ }
+
+ /****************************************
+ * Look up identifier in symbol table.
+ * Params:
+ * s = symbol
+ * id = identifier to look up
+ * Returns:
+ * Dsymbol if found, null if not
+ */
+ Dsymbol symtabLookup(Dsymbol s, Identifier id)
+ {
+ return symtab.lookup(id);
+ }
+
+ /****************************************
+ * Return true if any of the members are static ctors or static dtors, or if
+ * any members have members that are.
+ */
+ override bool hasStaticCtorOrDtor()
+ {
+ if (members)
+ {
+ for (size_t i = 0; i < members.dim; i++)
+ {
+ Dsymbol member = (*members)[i];
+ if (member.hasStaticCtorOrDtor())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ extern (D) alias ForeachDg = int delegate(size_t idx, Dsymbol s);
+
+ /***************************************
+ * Expands attribute declarations in members in depth first
+ * order. Calls dg(size_t symidx, Dsymbol *sym) for each
+ * member.
+ * If dg returns !=0, stops and returns that value else returns 0.
+ * Use this function to avoid the O(N + N^2/2) complexity of
+ * calculating dim and calling N times getNth.
+ * Returns:
+ * last value returned by dg()
+ */
+ extern (D) static int _foreach(Scope* sc, Dsymbols* members, scope ForeachDg dg, size_t* pn = null)
+ {
+ assert(dg);
+ if (!members)
+ return 0;
+ size_t n = pn ? *pn : 0; // take over index
+ int result = 0;
+ foreach (size_t i; 0 .. members.dim)
+ {
+ Dsymbol s = (*members)[i];
+ if (AttribDeclaration a = s.isAttribDeclaration())
+ result = _foreach(sc, a.include(sc), dg, &n);
+ else if (TemplateMixin tm = s.isTemplateMixin())
+ result = _foreach(sc, tm.members, dg, &n);
+ else if (s.isTemplateInstance())
+ {
+ }
+ else if (s.isUnitTestDeclaration())
+ {
+ }
+ else
+ result = dg(n++, s);
+ if (result)
+ break;
+ }
+ if (pn)
+ *pn = n; // update index
+ return result;
+ }
+
+ override final inout(ScopeDsymbol) isScopeDsymbol() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * With statement scope
+ */
+extern (C++) final class WithScopeSymbol : ScopeDsymbol
+{
+ WithStatement withstate;
+
+ extern (D) this(WithStatement withstate)
+ {
+ this.withstate = withstate;
+ }
+
+ override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ //printf("WithScopeSymbol.search(%s)\n", ident.toChars());
+ if (flags & SearchImportsOnly)
+ return null;
+ // Acts as proxy to the with class declaration
+ Dsymbol s = null;
+ Expression eold = null;
+ for (Expression e = withstate.exp; e != eold; e = resolveAliasThis(_scope, e))
+ {
+ if (e.op == TOK.scope_)
+ {
+ s = (cast(ScopeExp)e).sds;
+ }
+ else if (e.op == TOK.type)
+ {
+ s = e.type.toDsymbol(null);
+ }
+ else
+ {
+ Type t = e.type.toBasetype();
+ s = t.toDsymbol(null);
+ }
+ if (s)
+ {
+ s = s.search(loc, ident, flags);
+ if (s)
+ return s;
+ }
+ eold = e;
+ }
+ return null;
+ }
+
+ override inout(WithScopeSymbol) isWithScopeSymbol() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Array Index/Slice scope
+ */
+extern (C++) final class ArrayScopeSymbol : ScopeDsymbol
+{
+ // either a SliceExp, an IndexExp, an ArrayExp, a TypeTuple or a TupleDeclaration.
+ // Discriminated using DYNCAST and, for expressions, also TOK
+ private RootObject arrayContent;
+ Scope* sc;
+
+ extern (D) this(Scope* sc, Expression exp)
+ {
+ super(exp.loc, null);
+ assert(exp.op == TOK.index || exp.op == TOK.slice || exp.op == TOK.array);
+ this.sc = sc;
+ this.arrayContent = exp;
+ }
+
+ extern (D) this(Scope* sc, TypeTuple type)
+ {
+ this.sc = sc;
+ this.arrayContent = type;
+ }
+
+ extern (D) this(Scope* sc, TupleDeclaration td)
+ {
+ this.sc = sc;
+ this.arrayContent = td;
+ }
+
+ /// This override is used to solve `$`
+ override Dsymbol search(const ref Loc loc, Identifier ident, int flags = IgnoreNone)
+ {
+ //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident.toChars(), flags);
+ if (ident != Id.dollar)
+ return null;
+
+ VarDeclaration* pvar;
+ Expression ce;
+
+ static Dsymbol dollarFromTypeTuple(const ref Loc loc, TypeTuple tt, Scope* sc)
+ {
+
+ /* $ gives the number of type entries in the type tuple
+ */
+ auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null);
+ Expression e = new IntegerExp(Loc.initial, tt.arguments.dim, Type.tsize_t);
+ v._init = new ExpInitializer(Loc.initial, e);
+ v.storage_class |= STC.temp | STC.static_ | STC.const_;
+ v.dsymbolSemantic(sc);
+ return v;
+ }
+
+ const DYNCAST kind = arrayContent.dyncast();
+ if (kind == DYNCAST.dsymbol)
+ {
+ TupleDeclaration td = cast(TupleDeclaration) arrayContent;
+ /* $ gives the number of elements in the tuple
+ */
+ auto v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, null);
+ Expression e = new IntegerExp(Loc.initial, td.objects.dim, Type.tsize_t);
+ v._init = new ExpInitializer(Loc.initial, e);
+ v.storage_class |= STC.temp | STC.static_ | STC.const_;
+ v.dsymbolSemantic(sc);
+ return v;
+ }
+ if (kind == DYNCAST.type)
+ {
+ return dollarFromTypeTuple(loc, cast(TypeTuple) arrayContent, sc);
+ }
+ Expression exp = cast(Expression) arrayContent;
+ if (auto ie = exp.isIndexExp())
+ {
+ /* array[index] where index is some function of $
+ */
+ pvar = &ie.lengthVar;
+ ce = ie.e1;
+ }
+ else if (auto se = exp.isSliceExp())
+ {
+ /* array[lwr .. upr] where lwr or upr is some function of $
+ */
+ pvar = &se.lengthVar;
+ ce = se.e1;
+ }
+ else if (auto ae = exp.isArrayExp())
+ {
+ /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $
+ * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...)
+ */
+ pvar = &ae.lengthVar;
+ ce = ae.e1;
+ }
+ else
+ {
+ /* Didn't find $, look in enclosing scope(s).
+ */
+ return null;
+ }
+ ce = ce.lastComma();
+ /* If we are indexing into an array that is really a type
+ * tuple, rewrite this as an index into a type tuple and
+ * try again.
+ */
+ if (auto te = ce.isTypeExp())
+ {
+ if (auto ttp = te.type.isTypeTuple())
+ return dollarFromTypeTuple(loc, ttp, sc);
+ }
+ /* *pvar is lazily initialized, so if we refer to $
+ * multiple times, it gets set only once.
+ */
+ if (!*pvar) // if not already initialized
+ {
+ /* Create variable v and set it to the value of $
+ */
+ VarDeclaration v;
+ Type t;
+ if (auto tupexp = ce.isTupleExp())
+ {
+ /* It is for an expression tuple, so the
+ * length will be a const.
+ */
+ Expression e = new IntegerExp(Loc.initial, tupexp.exps.dim, Type.tsize_t);
+ v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, new ExpInitializer(Loc.initial, e));
+ v.storage_class |= STC.temp | STC.static_ | STC.const_;
+ }
+ else if (ce.type && (t = ce.type.toBasetype()) !is null && (t.ty == Tstruct || t.ty == Tclass))
+ {
+ // Look for opDollar
+ assert(exp.op == TOK.array || exp.op == TOK.slice);
+ AggregateDeclaration ad = isAggregate(t);
+ assert(ad);
+ Dsymbol s = ad.search(loc, Id.opDollar);
+ if (!s) // no dollar exists -- search in higher scope
+ return null;
+ s = s.toAlias();
+ Expression e = null;
+ // Check for multi-dimensional opDollar(dim) template.
+ if (TemplateDeclaration td = s.isTemplateDeclaration())
+ {
+ dinteger_t dim = 0;
+ if (exp.op == TOK.array)
+ {
+ dim = (cast(ArrayExp)exp).currentDimension;
+ }
+ else if (exp.op == TOK.slice)
+ {
+ dim = 0; // slices are currently always one-dimensional
+ }
+ else
+ {
+ assert(0);
+ }
+ auto tiargs = new Objects();
+ Expression edim = new IntegerExp(Loc.initial, dim, Type.tsize_t);
+ edim = edim.expressionSemantic(sc);
+ tiargs.push(edim);
+ e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs);
+ }
+ else
+ {
+ /* opDollar exists, but it's not a template.
+ * This is acceptable ONLY for single-dimension indexing.
+ * Note that it's impossible to have both template & function opDollar,
+ * because both take no arguments.
+ */
+ if (exp.op == TOK.array && (cast(ArrayExp)exp).arguments.dim != 1)
+ {
+ exp.error("`%s` only defines opDollar for one dimension", ad.toChars());
+ return null;
+ }
+ Declaration d = s.isDeclaration();
+ assert(d);
+ e = new DotVarExp(loc, ce, d);
+ }
+ e = e.expressionSemantic(sc);
+ if (!e.type)
+ exp.error("`%s` has no value", e.toChars());
+ t = e.type.toBasetype();
+ if (t && t.ty == Tfunction)
+ e = new CallExp(e.loc, e);
+ v = new VarDeclaration(loc, null, Id.dollar, new ExpInitializer(Loc.initial, e));
+ v.storage_class |= STC.temp | STC.ctfe | STC.rvalue;
+ }
+ else
+ {
+ /* For arrays, $ will either be a compile-time constant
+ * (in which case its value in set during constant-folding),
+ * or a variable (in which case an expression is created in
+ * toir.c).
+ */
+ auto e = new VoidInitializer(Loc.initial);
+ e.type = Type.tsize_t;
+ v = new VarDeclaration(loc, Type.tsize_t, Id.dollar, e);
+ v.storage_class |= STC.temp | STC.ctfe; // it's never a true static variable
+ }
+ *pvar = v;
+ }
+ (*pvar).dsymbolSemantic(sc);
+ return (*pvar);
+ }
+
+ override inout(ArrayScopeSymbol) isArrayScopeSymbol() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Overload Sets
+ */
+extern (C++) final class OverloadSet : Dsymbol
+{
+ Dsymbols a; // array of Dsymbols
+
+ extern (D) this(Identifier ident, OverloadSet os = null)
+ {
+ super(ident);
+ if (os)
+ {
+ a.pushSlice(os.a[]);
+ }
+ }
+
+ void push(Dsymbol s)
+ {
+ a.push(s);
+ }
+
+ override inout(OverloadSet) isOverloadSet() inout
+ {
+ return this;
+ }
+
+ override const(char)* kind() const
+ {
+ return "overloadset";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Forwarding ScopeDsymbol. Used by ForwardingAttribDeclaration and
+ * ForwardingScopeDeclaration to forward symbol insertions to another
+ * scope. See `dmd.attrib.ForwardingAttribDeclaration` for more
+ * details.
+ */
+extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol
+{
+ /*************************
+ * Symbol to forward insertions to.
+ * Can be `null` before being lazily initialized.
+ */
+ ScopeDsymbol forward;
+ extern (D) this(ScopeDsymbol forward)
+ {
+ super(null);
+ this.forward = forward;
+ }
+ override Dsymbol symtabInsert(Dsymbol s)
+ {
+ assert(forward);
+ if (auto d = s.isDeclaration())
+ {
+ if (d.storage_class & STC.local)
+ {
+ // Symbols with storage class STC.local are not
+ // forwarded, but stored in the local symbol
+ // table. (Those are the `static foreach` variables.)
+ if (!symtab)
+ {
+ symtab = new DsymbolTable();
+ }
+ return super.symtabInsert(s); // insert locally
+ }
+ }
+ if (!forward.symtab)
+ {
+ forward.symtab = new DsymbolTable();
+ }
+ // Non-STC.local symbols are forwarded to `forward`.
+ return forward.symtabInsert(s);
+ }
+
+ /************************
+ * This override handles the following two cases:
+ * static foreach (i, i; [0]) { ... }
+ * and
+ * static foreach (i; [0]) { enum i = 2; }
+ */
+ override Dsymbol symtabLookup(Dsymbol s, Identifier id)
+ {
+ assert(forward);
+ // correctly diagnose clashing foreach loop variables.
+ if (auto d = s.isDeclaration())
+ {
+ if (d.storage_class & STC.local)
+ {
+ if (!symtab)
+ {
+ symtab = new DsymbolTable();
+ }
+ return super.symtabLookup(s,id);
+ }
+ }
+ // Declarations within `static foreach` do not clash with
+ // `static foreach` loop variables.
+ if (!forward.symtab)
+ {
+ forward.symtab = new DsymbolTable();
+ }
+ return forward.symtabLookup(s,id);
+ }
+
+ override void importScope(Dsymbol s, Visibility visibility)
+ {
+ forward.importScope(s, visibility);
+ }
+
+ override const(char)* kind()const{ return "local scope"; }
+
+ override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout
+ {
+ return this;
+ }
+
+}
+
+/**
+ * Class that holds an expression in a Dsymbol wrapper.
+ * This is not an AST node, but a class used to pass
+ * an expression as a function parameter of type Dsymbol.
+ */
+extern (C++) final class ExpressionDsymbol : Dsymbol
+{
+ Expression exp;
+ this(Expression exp)
+ {
+ super();
+ this.exp = exp;
+ }
+
+ override inout(ExpressionDsymbol) isExpressionDsymbol() inout
+ {
+ return this;
+ }
+}
+
+/**********************************************
+ * Encapsulate assigning to an alias:
+ * `identifier = type;`
+ * `identifier = symbol;`
+ * where `identifier` is an AliasDeclaration in scope.
+ */
+extern (C++) final class AliasAssign : Dsymbol
+{
+ Identifier ident; /// Dsymbol's ident will be null, as this class is anonymous
+ Type type; /// replace previous RHS of AliasDeclaration with `type`
+ Dsymbol aliassym; /// replace previous RHS of AliasDeclaration with `aliassym`
+ /// only one of type and aliassym can be != null
+
+ extern (D) this(const ref Loc loc, Identifier ident, Type type, Dsymbol aliassym)
+ {
+ super(loc, null);
+ this.ident = ident;
+ this.type = type;
+ this.aliassym = aliassym;
+ }
+
+ override AliasAssign syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ AliasAssign aa = new AliasAssign(loc, ident,
+ type ? type.syntaxCopy() : null,
+ aliassym ? aliassym.syntaxCopy(null) : null);
+ return aa;
+ }
+
+ override inout(AliasAssign) isAliasAssign() inout
+ {
+ return this;
+ }
+
+ override const(char)* kind() const
+ {
+ return "alias assignment";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Table of Dsymbol's
+ */
+extern (C++) final class DsymbolTable : RootObject
+{
+ AssocArray!(Identifier, Dsymbol) tab;
+
+ /***************************
+ * Look up Identifier in symbol table
+ * Params:
+ * ident = identifer to look up
+ * Returns:
+ * Dsymbol if found, null if not
+ */
+ Dsymbol lookup(const Identifier ident)
+ {
+ //printf("DsymbolTable::lookup(%s)\n", ident.toChars());
+ return tab[ident];
+ }
+
+ /**********
+ * Replace existing symbol in symbol table with `s`.
+ * If it's not there, add it.
+ * Params:
+ * s = replacement symbol with same identifier
+ */
+ void update(Dsymbol s)
+ {
+ *tab.getLvalue(s.ident) = s;
+ }
+
+ /**************************
+ * Insert Dsymbol in table.
+ * Params:
+ * s = symbol to add
+ * Returns:
+ * null if already in table, `s` if inserted
+ */
+ Dsymbol insert(Dsymbol s)
+ {
+ return insert(s.ident, s);
+ }
+
+ /**************************
+ * Insert Dsymbol in table.
+ * Params:
+ * ident = identifier to serve as index
+ * s = symbol to add
+ * Returns:
+ * null if already in table, `s` if inserted
+ */
+ Dsymbol insert(const Identifier ident, Dsymbol s)
+ {
+ //printf("DsymbolTable.insert(this = %p, '%s')\n", this, s.ident.toChars());
+ Dsymbol* ps = tab.getLvalue(ident);
+ if (*ps)
+ return null; // already in table
+ *ps = s;
+ return s;
+ }
+
+ /*****************
+ * Returns:
+ * number of symbols in symbol table
+ */
+ size_t length() const pure
+ {
+ return tab.length;
+ }
+}
+
+/**********************************************
+ * ImportC tag symbols sit in a parallel symbol table,
+ * so that this C code works:
+ * ---
+ * struct S { a; };
+ * int S;
+ * struct S s;
+ * ---
+ * But there are relatively few such tag symbols, so that would be
+ * a waste of memory and complexity. An additional problem is we'd like the D side
+ * to find the tag symbols with ordinary lookup, not lookup in both
+ * tables, if the tag symbol is not conflicting with an ordinary symbol.
+ * The solution is to put the tag symbols that conflict into an associative
+ * array, indexed by the address of the ordinary symbol that conflicts with it.
+ * C has no modules, so this associative array is tagSymTab[] in ModuleDeclaration.
+ * A side effect of our approach is that D code cannot access a tag symbol that is
+ * hidden by an ordinary symbol. This is more of a theoretical problem, as nobody
+ * has mentioned it when importing C headers. If someone wants to do it,
+ * too bad so sad. Change the C code.
+ * This function fixes up the symbol table when faced with adding a new symbol
+ * `s` when there is an existing symbol `s2` with the same name.
+ * C also allows forward and prototype declarations of tag symbols,
+ * this function merges those.
+ * Params:
+ * sc = context
+ * s = symbol to add to symbol table
+ * s2 = existing declaration
+ * sds = symbol table
+ * Returns:
+ * if s and s2 are successfully put in symbol table then return the merged symbol,
+ * null if they conflict
+ */
+Dsymbol handleTagSymbols(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds)
+{
+ enum log = false;
+ if (log) printf("handleTagSymbols('%s')\n", s.toChars());
+ auto sd = s.isScopeDsymbol(); // new declaration
+ auto sd2 = s2.isScopeDsymbol(); // existing declaration
+
+ if (!sd2)
+ {
+ /* Look in tag table
+ */
+ if (log) printf(" look in tag table\n");
+ if (auto p = cast(void*)s2 in sc._module.tagSymTab)
+ {
+ Dsymbol s2tag = *p;
+ sd2 = s2tag.isScopeDsymbol();
+ assert(sd2); // only tags allowed in tag symbol table
+ }
+ }
+
+ if (sd && sd2) // `s` is a tag, `sd2` is the same tag
+ {
+ if (log) printf(" tag is already defined\n");
+
+ if (sd.kind() != sd2.kind()) // being enum/struct/union must match
+ return null; // conflict
+
+ /* Not a redeclaration if one is a forward declaration.
+ * Move members to the first declared type, which is sd2.
+ */
+ if (sd2.members)
+ {
+ if (!sd.members)
+ return sd2; // ignore the sd redeclaration
+ }
+ else if (sd.members)
+ {
+ sd2.members = sd.members; // transfer definition to sd2
+ sd.members = null;
+ return sd2;
+ }
+ else
+ return sd2; // ignore redeclaration
+ }
+ else if (sd) // `s` is a tag, `s2` is not
+ {
+ if (log) printf(" s is tag, s2 is not\n");
+ /* add `s` as tag indexed by s2
+ */
+ sc._module.tagSymTab[cast(void*)s2] = s;
+ return s;
+ }
+ else if (s2 is sd2) // `s2` is a tag, `s` is not
+ {
+ if (log) printf(" s2 is tag, s is not\n");
+ /* replace `s2` in symbol table with `s`,
+ * then add `s2` as tag indexed by `s`
+ */
+ sds.symtab.update(s);
+ sc._module.tagSymTab[cast(void*)s] = s2;
+ return s;
+ }
+ if (log) printf(" collision\n");
+ return null;
+}
+
+
diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h
index ce0ce4564af..f43bc837992 100644
--- a/gcc/d/dmd/dsymbol.h
+++ b/gcc/d/dmd/dsymbol.h
@@ -11,17 +11,18 @@
#pragma once
#include "root/port.h"
-#include "root/stringtable.h"
#include "ast_node.h"
#include "globals.h"
#include "arraytypes.h"
#include "visitor.h"
+class CPPNamespaceDeclaration;
class Identifier;
struct Scope;
class DsymbolTable;
class Declaration;
class ThisDeclaration;
+class BitFieldDeclaration;
class TypeInfoDeclaration;
class TupleDeclaration;
class AliasDeclaration;
@@ -47,6 +48,7 @@ class UnitTestDeclaration;
class NewDeclaration;
class VarDeclaration;
class AttribDeclaration;
+class VisibilityDeclaration;
class Package;
class Module;
class Import;
@@ -66,7 +68,8 @@ class WithScopeSymbol;
class ArrayScopeSymbol;
class SymbolDeclaration;
class Expression;
-class DeleteDeclaration;
+class ExpressionDsymbol;
+class AliasAssign;
class OverloadSet;
struct AA;
#ifdef IN_GCC
@@ -84,10 +87,10 @@ struct Ungag
};
void dsymbolSemantic(Dsymbol *dsym, Scope *sc);
-void semantic2(Dsymbol *dsym, Scope* sc);
+void semantic2(Dsymbol *dsym, Scope *sc);
void semantic3(Dsymbol *dsym, Scope* sc);
-struct Prot
+struct Visibility
{
enum Kind
{
@@ -101,18 +104,8 @@ struct Prot
};
Kind kind;
Package *pkg;
-
- Prot();
- Prot(Kind kind);
-
- bool isMoreRestrictiveThan(const Prot other) const;
- bool operator==(const Prot& other) const;
};
-// in hdrgen.c
-void protectionToBuffer(OutBuffer *buf, Prot prot);
-const char *protectionToChars(Prot::Kind kind);
-
/* State of symbol in winding its way through the passes of the compiler
*/
enum PASS
@@ -143,16 +136,27 @@ enum
// meaning don't search imports in that scope,
// because qualified module searches search
// their imports
- IgnoreSymbolVisibility = 0x80 // also find private and package protected symbols
+ IgnoreSymbolVisibility = 0x80, // also find private and package protected symbols
+ TagNameSpace = 0x100, // search ImportC tag symbol table
};
-typedef int (*Dsymbol_apply_ft_t)(Dsymbol *, void *);
+struct FieldState
+{
+ unsigned offset;
+
+ unsigned fieldOffset;
+ unsigned bitOffset;
+ unsigned fieldSice;
+ bool inFlight;
+};
class Dsymbol : public ASTNode
{
public:
Identifier *ident;
Dsymbol *parent;
+ /// C++ namespace this symbol belongs to
+ CPPNamespaceDeclaration *namespace_;
Symbol *csym; // symbol for code generator
Symbol *isym; // import version of csym
const utf8_t *comment; // documentation comment for this Dsymbol
@@ -161,74 +165,72 @@ public:
const utf8_t *prettystring;
bool errors; // this symbol failed to pass semantic()
PASS semanticRun;
+ unsigned short localNum; // perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab
DeprecatedDeclaration *depdecl; // customized deprecation message
UserAttributeDeclaration *userAttribDecl; // user defined attributes
UnitTestDeclaration *ddocUnittest; // !=NULL means there's a ddoc unittest associated with this symbol (only use this with ddoc)
- Dsymbol();
- Dsymbol(Identifier *);
static Dsymbol *create(Identifier *);
- const char *toChars();
+ const char *toChars() const;
virtual const char *toPrettyCharsHelper(); // helper to print fully qualified (template) arguments
- Loc& getLoc();
+ Loc getLoc();
const char *locToChars();
- bool equals(RootObject *o);
- bool isAnonymous();
- void error(Loc loc, const char *format, ...);
+ bool equals(const RootObject *o) const;
+ bool isAnonymous() const;
+ void error(const Loc &loc, const char *format, ...);
void error(const char *format, ...);
- void deprecation(Loc loc, const char *format, ...);
+ void deprecation(const Loc &loc, const char *format, ...);
void deprecation(const char *format, ...);
- bool checkDeprecated(Loc loc, Scope *sc);
+ bool checkDeprecated(const Loc &loc, Scope *sc);
Module *getModule();
Module *getAccessModule();
Dsymbol *pastMixin();
- Dsymbol *pastMixinAndNspace();
Dsymbol *toParent();
Dsymbol *toParent2();
- Dsymbol *toParent3();
+ Dsymbol *toParentDecl();
+ Dsymbol *toParentLocal();
+ Dsymbol *toParentP(Dsymbol *p1, Dsymbol *p2 = NULL);
TemplateInstance *isInstantiated();
+ bool followInstantiationContext(Dsymbol *p1, Dsymbol *p2 = NULL);
TemplateInstance *isSpeculative();
Ungag ungagSpeculative();
// kludge for template.isSymbol()
- int dyncast() const { return DYNCAST_DSYMBOL; }
-
- static Dsymbols *arraySyntaxCopy(Dsymbols *a);
+ DYNCAST dyncast() const { return DYNCAST_DSYMBOL; }
virtual Identifier *getIdent();
virtual const char *toPrettyChars(bool QualifyTypes = false);
virtual const char *kind() const;
virtual Dsymbol *toAlias(); // resolve real symbol
virtual Dsymbol *toAlias2();
- virtual int apply(Dsymbol_apply_ft_t fp, void *param);
virtual void addMember(Scope *sc, ScopeDsymbol *sds);
virtual void setScope(Scope *sc);
virtual void importAll(Scope *sc);
virtual Dsymbol *search(const Loc &loc, Identifier *ident, int flags = IgnoreNone);
- Dsymbol *search_correct(Identifier *id);
- Dsymbol *searchX(Loc loc, Scope *sc, RootObject *id, int flags);
virtual bool overloadInsert(Dsymbol *s);
- virtual d_uns64 size(Loc loc);
+ virtual d_uns64 size(const Loc &loc);
virtual bool isforwardRef();
virtual AggregateDeclaration *isThis(); // is a 'this' required to access the member
virtual bool isExport() const; // is Dsymbol exported?
virtual bool isImportedSymbol() const; // is Dsymbol imported?
- virtual bool isDeprecated(); // is Dsymbol deprecated?
- virtual bool isOverloadable();
+ virtual bool isDeprecated() const; // is Dsymbol deprecated?
+ virtual bool isOverloadable() const;
virtual LabelDsymbol *isLabel(); // is this a LabelDsymbol?
- AggregateDeclaration *isMember(); // is this a member of an AggregateDeclaration?
- AggregateDeclaration *isMember2(); // is this a member of an AggregateDeclaration?
- ClassDeclaration *isClassMember(); // is this a member of a ClassDeclaration?
+ AggregateDeclaration *isMember(); // is toParent() an AggregateDeclaration?
+ AggregateDeclaration *isMember2(); // is toParent2() an AggregateDeclaration?
+ AggregateDeclaration *isMemberDecl(); // is toParentDecl() an AggregateDeclaration?
+ AggregateDeclaration *isMemberLocal(); // is toParentLocal() an AggregateDeclaration?
+ ClassDeclaration *isClassMember(); // isMember() is a ClassDeclaration?
virtual Type *getType(); // is this a type?
virtual bool needThis(); // need a 'this' pointer?
- virtual Prot prot();
+ virtual Visibility visible();
virtual Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees
virtual bool oneMember(Dsymbol **ps, Identifier *ident);
- static bool oneMembers(Dsymbols *members, Dsymbol **ps, Identifier *ident);
- virtual void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
+ virtual void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion);
virtual bool hasPointers();
virtual bool hasStaticCtorOrDtor();
virtual void addLocalClass(ClassDeclarations *) { }
+ virtual void addObjcSymbols(ClassDeclarations *, ClassDeclarations *) { }
virtual void checkCtorConstInit() { }
virtual void addComment(const utf8_t *comment);
@@ -246,7 +248,10 @@ public:
virtual Nspace *isNspace() { return NULL; }
virtual Declaration *isDeclaration() { return NULL; }
virtual StorageClassDeclaration *isStorageClassDeclaration(){ return NULL; }
+ virtual ExpressionDsymbol *isExpressionDsymbol() { return NULL; }
+ virtual AliasAssign *isAliasAssign() { return NULL; }
virtual ThisDeclaration *isThisDeclaration() { return NULL; }
+ virtual BitFieldDeclaration *isBitFieldDeclaration() { return NULL; }
virtual TypeInfoDeclaration *isTypeInfoDeclaration() { return NULL; }
virtual TupleDeclaration *isTupleDeclaration() { return NULL; }
virtual AliasDeclaration *isAliasDeclaration() { return NULL; }
@@ -278,11 +283,13 @@ public:
virtual ArrayScopeSymbol *isArrayScopeSymbol() { return NULL; }
virtual Import *isImport() { return NULL; }
virtual EnumDeclaration *isEnumDeclaration() { return NULL; }
- virtual DeleteDeclaration *isDeleteDeclaration() { return NULL; }
virtual SymbolDeclaration *isSymbolDeclaration() { return NULL; }
virtual AttribDeclaration *isAttribDeclaration() { return NULL; }
virtual AnonDeclaration *isAnonDeclaration() { return NULL; }
+ virtual CPPNamespaceDeclaration *isCPPNamespaceDeclaration() { return NULL; }
+ virtual VisibilityDeclaration *isVisibilityDeclaration() { return NULL; }
virtual OverloadSet *isOverloadSet() { return NULL; }
+ virtual CompileDeclaration *isCompileDeclaration() { return NULL; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -297,30 +304,23 @@ public:
private:
Dsymbols *importedScopes; // imported Dsymbol's
- Prot::Kind *prots; // array of PROTKIND, one for each import
+ Visibility::Kind *visibilities; // array of `Visibility.Kind`, one for each import
BitArray accessiblePackages, privateAccessiblePackages;
public:
- ScopeDsymbol();
- ScopeDsymbol(Identifier *id);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ ScopeDsymbol *syntaxCopy(Dsymbol *s);
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
- OverloadSet *mergeOverloadSet(Identifier *ident, OverloadSet *os, Dsymbol *s);
- virtual void importScope(Dsymbol *s, Prot protection);
- void addAccessiblePackage(Package *p, Prot protection);
- virtual bool isPackageAccessible(Package *p, Prot protection, int flags = 0);
+ virtual void importScope(Dsymbol *s, Visibility visibility);
+ virtual bool isPackageAccessible(Package *p, Visibility visibility, int flags = 0);
bool isforwardRef();
- static void multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2);
+ static void multiplyDefined(const Loc &loc, Dsymbol *s1, Dsymbol *s2);
const char *kind() const;
FuncDeclaration *findGetMembers();
virtual Dsymbol *symtabInsert(Dsymbol *s);
virtual Dsymbol *symtabLookup(Dsymbol *s, Identifier *id);
bool hasStaticCtorOrDtor();
- static size_t dim(Dsymbols *members);
- static Dsymbol *getNth(Dsymbols *members, size_t nth, size_t *pn = NULL);
-
ScopeDsymbol *isScopeDsymbol() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -332,7 +332,6 @@ class WithScopeSymbol : public ScopeDsymbol
public:
WithStatement *withstate;
- WithScopeSymbol(WithStatement *withstate);
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
WithScopeSymbol *isWithScopeSymbol() { return this; }
@@ -343,15 +342,11 @@ public:
class ArrayScopeSymbol : public ScopeDsymbol
{
+private:
+ RootObject *arrayContent;
public:
- Expression *exp; // IndexExp or SliceExp
- TypeTuple *type; // for tuple[length]
- TupleDeclaration *td; // for tuples of objects
Scope *sc;
- ArrayScopeSymbol(Scope *sc, Expression *e);
- ArrayScopeSymbol(Scope *sc, TypeTuple *t);
- ArrayScopeSymbol(Scope *sc, TupleDeclaration *td);
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = IgnoreNone);
ArrayScopeSymbol *isArrayScopeSymbol() { return this; }
@@ -365,7 +360,6 @@ class OverloadSet : public Dsymbol
public:
Dsymbols a; // array of Dsymbols
- OverloadSet(Identifier *ident, OverloadSet *os = NULL);
void push(Dsymbol *s);
OverloadSet *isOverloadSet() { return this; }
const char *kind() const;
@@ -379,15 +373,22 @@ class ForwardingScopeDsymbol : public ScopeDsymbol
public:
ScopeDsymbol *forward;
- ForwardingScopeDsymbol(ScopeDsymbol *forward);
Dsymbol *symtabInsert(Dsymbol *s);
Dsymbol *symtabLookup(Dsymbol *s, Identifier *id);
- void importScope(Dsymbol *s, Prot protection);
+ void importScope(Dsymbol *s, Visibility visibility);
const char *kind() const;
ForwardingScopeDsymbol *isForwardingScopeDsymbol() { return this; }
};
+class ExpressionDsymbol : public Dsymbol
+{
+public:
+ Expression *exp;
+
+ ExpressionDsymbol *isExpressionDsymbol() { return this; }
+};
+
// Table of Dsymbol's
class DsymbolTable : public RootObject
@@ -395,15 +396,16 @@ class DsymbolTable : public RootObject
public:
AA *tab;
- DsymbolTable();
-
// Look up Identifier. Return Dsymbol if found, NULL if not.
Dsymbol *lookup(Identifier const * const ident);
+ // Look for Dsymbol in table. If there, return it. If not, insert s and return that.
+ void update(Dsymbol *s);
+
// Insert Dsymbol in table. Return NULL if already there.
Dsymbol *insert(Dsymbol *s);
-
- // Look for Dsymbol in table. If there, return it. If not, insert s and return that.
- Dsymbol *update(Dsymbol *s);
Dsymbol *insert(Identifier const * const ident, Dsymbol *s); // when ident and s are not the same
+
+ // Number of symbols in symbol table
+ size_t length() const;
};
diff --git a/gcc/d/dmd/dsymbolsem.c b/gcc/d/dmd/dsymbolsem.c
deleted file mode 100644
index 7a44ed2c41d..00000000000
--- a/gcc/d/dmd/dsymbolsem.c
+++ /dev/null
@@ -1,5620 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "root/dsystem.h"
-#include "root/aav.h"
-
-#include "dsymbol.h"
-#include "aggregate.h"
-#include "aliasthis.h"
-#include "attrib.h"
-#include "cond.h"
-#include "declaration.h"
-#include "enum.h"
-#include "errors.h"
-#include "hdrgen.h"
-#include "id.h"
-#include "import.h"
-#include "init.h"
-#include "mars.h"
-#include "module.h"
-#include "nspace.h"
-#include "objc.h"
-#include "parse.h"
-#include "scope.h"
-#include "statement.h"
-#include "staticassert.h"
-#include "target.h"
-#include "template.h"
-#include "utf.h"
-#include "version.h"
-#include "visitor.h"
-
-bool allowsContractWithoutBody(FuncDeclaration *funcdecl);
-bool checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad, size_t istart = 0);
-VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e);
-Initializer *inferType(Initializer *init, Scope *sc);
-void MODtoBuffer(OutBuffer *buf, MOD mod);
-bool reliesOnTident(Type *t, TemplateParameters *tparams = NULL, size_t iStart = 0);
-bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps);
-bool symbolIsVisible(Scope *sc, Dsymbol *s);
-Objc *objc();
-
-static unsigned setMangleOverride(Dsymbol *s, char *sym)
-{
- AttribDeclaration *ad = s->isAttribDeclaration();
-
- if (ad)
- {
- Dsymbols *decls = ad->include(NULL);
- unsigned nestedCount = 0;
-
- if (decls && decls->length)
- for (size_t i = 0; i < decls->length; ++i)
- nestedCount += setMangleOverride((*decls)[i], sym);
-
- return nestedCount;
- }
- else if (s->isFuncDeclaration() || s->isVarDeclaration())
- {
- s->isDeclaration()->mangleOverride = sym;
- return 1;
- }
- else
- return 0;
-}
-
-/**********************************
- * Decide if attributes for this function can be inferred from examining
- * the function body.
- * Returns:
- * true if can
- */
-static bool canInferAttributes(FuncDeclaration *fd, Scope *sc)
-{
- if (!fd->fbody)
- return false;
-
- if (fd->isVirtualMethod())
- return false; // since they may be overridden
-
- if (sc->func &&
- /********** this is for backwards compatibility for the moment ********/
- (!fd->isMember() || (sc->func->isSafeBypassingInference() && !fd->isInstantiated())))
- return true;
-
- if (fd->isFuncLiteralDeclaration() || // externs are not possible with literals
- (fd->storage_class & STCinference) || // do attribute inference
- (fd->inferRetType && !fd->isCtorDeclaration()))
- return true;
-
- if (fd->isInstantiated())
- {
- TemplateInstance *ti = fd->parent->isTemplateInstance();
- if (ti == NULL || ti->isTemplateMixin() || ti->tempdecl->ident == fd->ident)
- return true;
- }
-
- return false;
-}
-
-/*****************************************
- * Initialize for inferring the attributes of this function.
- */
-static void initInferAttributes(FuncDeclaration *fd)
-{
- //printf("initInferAttributes() for %s\n", toPrettyChars());
- TypeFunction *tf = fd->type->toTypeFunction();
- if (tf->purity == PUREimpure) // purity not specified
- fd->flags |= FUNCFLAGpurityInprocess;
-
- if (tf->trust == TRUSTdefault)
- fd->flags |= FUNCFLAGsafetyInprocess;
-
- if (!tf->isnothrow)
- fd->flags |= FUNCFLAGnothrowInprocess;
-
- if (!tf->isnogc)
- fd->flags |= FUNCFLAGnogcInprocess;
-
- if (!fd->isVirtual() || fd->introducing)
- fd->flags |= FUNCFLAGreturnInprocess;
-
- // Initialize for inferring STCscope
- if (global.params.vsafe)
- fd->flags |= FUNCFLAGinferScope;
-}
-
-static void badObjectDotD(ClassDeclaration *cd)
-{
- cd->error("missing or corrupt object.d");
- fatal();
-}
-
-/* Bugzilla 12078, 12143 and 15733:
- * While resolving base classes and interfaces, a base may refer
- * the member of this derived class. In that time, if all bases of
- * this class can be determined, we can go forward the semantc process
- * beyond the Lancestorsdone. To do the recursive semantic analysis,
- * temporarily set and unset `_scope` around exp().
- */
-static Type *resolveBase(ClassDeclaration *cd, Scope *sc, Scope *&scx, Type *type)
-{
- if (!scx)
- {
- scx = sc->copy();
- scx->setNoFree();
- }
- cd->_scope = scx;
- Type *t = typeSemantic(type, cd->loc, sc);
- cd->_scope = NULL;
- return t;
-}
-
-static void resolveBase(ClassDeclaration *cd, Scope *sc, Scope *&scx, ClassDeclaration *sym)
-{
- if (!scx)
- {
- scx = sc->copy();
- scx->setNoFree();
- }
- cd->_scope = scx;
- dsymbolSemantic(sym, NULL);
- cd->_scope = NULL;
-}
-
-class DsymbolSemanticVisitor : public Visitor
-{
-public:
- Scope *sc;
-
- DsymbolSemanticVisitor(Scope *sc)
- {
- this->sc = sc;
- }
-
- void visit(Dsymbol *dsym)
- {
- dsym->error("%p has no semantic routine", dsym);
- }
-
- void visit(ScopeDsymbol *) { }
- void visit(Declaration *) { }
-
- void visit(AliasThis *dsym)
- {
- if (dsym->semanticRun != PASSinit)
- return;
-
- if (dsym->_scope)
- {
- sc = dsym->_scope;
- dsym->_scope = NULL;
- }
-
- if (!sc)
- return;
-
- dsym->semanticRun = PASSsemantic;
-
- Dsymbol *p = sc->parent->pastMixin();
- AggregateDeclaration *ad = p->isAggregateDeclaration();
- if (!ad)
- {
- error(dsym->loc, "alias this can only be a member of aggregate, not %s %s",
- p->kind(), p->toChars());
- return;
- }
-
- assert(ad->members);
- Dsymbol *s = ad->search(dsym->loc, dsym->ident);
- if (!s)
- {
- s = sc->search(dsym->loc, dsym->ident, NULL);
- if (s)
- error(dsym->loc, "%s is not a member of %s", s->toChars(), ad->toChars());
- else
- error(dsym->loc, "undefined identifier %s", dsym->ident->toChars());
- return;
- }
- else if (ad->aliasthis && s != ad->aliasthis)
- {
- error(dsym->loc, "there can be only one alias this");
- return;
- }
-
- if (ad->type->ty == Tstruct && ((TypeStruct *)ad->type)->sym != ad)
- {
- AggregateDeclaration *ad2 = ((TypeStruct *)ad->type)->sym;
- assert(ad2->type == Type::terror);
- ad->aliasthis = ad2->aliasthis;
- return;
- }
-
- /* disable the alias this conversion so the implicit conversion check
- * doesn't use it.
- */
- ad->aliasthis = NULL;
-
- Dsymbol *sx = s;
- if (sx->isAliasDeclaration())
- sx = sx->toAlias();
- Declaration *d = sx->isDeclaration();
- if (d && !d->isTupleDeclaration())
- {
- Type *t = d->type;
- assert(t);
- if (ad->type->implicitConvTo(t) > MATCHnomatch)
- {
- error(dsym->loc, "alias this is not reachable as %s already converts to %s", ad->toChars(), t->toChars());
- }
- }
-
- ad->aliasthis = s;
- dsym->semanticRun = PASSsemanticdone;
- }
-
- void visit(AliasDeclaration *dsym)
- {
- if (dsym->semanticRun >= PASSsemanticdone)
- return;
- assert(dsym->semanticRun <= PASSsemantic);
-
- dsym->storage_class |= sc->stc & STCdeprecated;
- dsym->protection = sc->protection;
- dsym->userAttribDecl = sc->userAttribDecl;
-
- if (!sc->func && dsym->inNonRoot())
- return;
-
- aliasSemantic(dsym, sc);
- }
-
- void visit(VarDeclaration *dsym)
- {
- //if (dsym->semanticRun > PASSinit)
- // return;
- //dsym->semanticRun = PASSsemantic;
-
- if (dsym->semanticRun >= PASSsemanticdone)
- return;
-
- Scope *scx = NULL;
- if (dsym->_scope)
- {
- sc = dsym->_scope;
- scx = sc;
- dsym->_scope = NULL;
- }
-
- if (!sc)
- return;
-
- dsym->semanticRun = PASSsemantic;
-
- /* Pick up storage classes from context, but except synchronized,
- * override, abstract, and final.
- */
- dsym->storage_class |= (sc->stc & ~(STCsynchronized | STCoverride | STCabstract | STCfinal));
- if (dsym->storage_class & STCextern && dsym->_init)
- dsym->error("extern symbols cannot have initializers");
-
- dsym->userAttribDecl = sc->userAttribDecl;
-
- AggregateDeclaration *ad = dsym->isThis();
- if (ad)
- dsym->storage_class |= ad->storage_class & STC_TYPECTOR;
-
- /* If auto type inference, do the inference
- */
- int inferred = 0;
- if (!dsym->type)
- {
- dsym->inuse++;
-
- // Infering the type requires running semantic,
- // so mark the scope as ctfe if required
- bool needctfe = (dsym->storage_class & (STCmanifest | STCstatic)) != 0;
- if (needctfe) sc = sc->startCTFE();
-
- //printf("inferring type for %s with init %s\n", dsym->toChars(), dsym->_init->toChars());
- dsym->_init = inferType(dsym->_init, sc);
- dsym->type = initializerToExpression(dsym->_init)->type;
-
- if (needctfe) sc = sc->endCTFE();
-
- dsym->inuse--;
- inferred = 1;
-
- /* This is a kludge to support the existing syntax for RAII
- * declarations.
- */
- dsym->storage_class &= ~STCauto;
- dsym->originalType = dsym->type->syntaxCopy();
- }
- else
- {
- if (!dsym->originalType)
- dsym->originalType = dsym->type->syntaxCopy();
-
- /* Prefix function attributes of variable declaration can affect
- * its type:
- * pure nothrow void function() fp;
- * static assert(is(typeof(fp) == void function() pure nothrow));
- */
- Scope *sc2 = sc->push();
- sc2->stc |= (dsym->storage_class & STC_FUNCATTR);
- dsym->inuse++;
- dsym->type = typeSemantic(dsym->type, dsym->loc, sc2);
- dsym->inuse--;
- sc2->pop();
- }
- //printf(" semantic type = %s\n", dsym->type ? dsym->type->toChars() : "null");
- if (dsym->type->ty == Terror)
- dsym->errors = true;
-
- dsym->type->checkDeprecated(dsym->loc, sc);
- dsym->linkage = sc->linkage;
- dsym->parent = sc->parent;
- //printf("this = %p, parent = %p, '%s'\n", dsym, dsym->parent, dsym->parent->toChars());
- dsym->protection = sc->protection;
-
- /* If scope's alignment is the default, use the type's alignment,
- * otherwise the scope overrrides.
- */
- dsym->alignment = sc->alignment();
- if (dsym->alignment == STRUCTALIGN_DEFAULT)
- dsym->alignment = dsym->type->alignment(); // use type's alignment
-
- //printf("sc->stc = %x\n", sc->stc);
- //printf("storage_class = x%x\n", dsym->storage_class);
-
- if (global.params.vcomplex)
- dsym->type->checkComplexTransition(dsym->loc);
-
- // Calculate type size + safety checks
- if (sc->func && !sc->intypeof)
- {
- if ((dsym->storage_class & STCgshared) && !dsym->isMember())
- {
- if (sc->func->setUnsafe())
- dsym->error("__gshared not allowed in safe functions; use shared");
- }
- }
-
- Dsymbol *parent = dsym->toParent();
-
- Type *tb = dsym->type->toBasetype();
- Type *tbn = tb->baseElemOf();
- if (tb->ty == Tvoid && !(dsym->storage_class & STClazy))
- {
- if (inferred)
- {
- dsym->error("type %s is inferred from initializer %s, and variables cannot be of type void",
- dsym->type->toChars(), dsym->_init->toChars());
- }
- else
- dsym->error("variables cannot be of type void");
- dsym->type = Type::terror;
- tb = dsym->type;
- }
- if (tb->ty == Tfunction)
- {
- dsym->error("cannot be declared to be a function");
- dsym->type = Type::terror;
- tb = dsym->type;
- }
- if (tb->ty == Tstruct)
- {
- TypeStruct *ts = (TypeStruct *)tb;
- if (!ts->sym->members)
- {
- dsym->error("no definition of struct `%s`", ts->toChars());
-
- // Explain why the definition is required when it's part of another type
- if (!dsym->type->isTypeStruct())
- {
- // Prefer Loc of the dependant type
- Dsymbol *s = dsym->type->toDsymbol(sc);
- Loc loc = s ? s->loc : dsym->loc;
- errorSupplemental(loc, "required by type `%s`", dsym->type->toChars());
- }
-
- // Flag variable as error to avoid invalid error messages due to unknown size
- dsym->type = Type::terror;
- }
- }
- if ((dsym->storage_class & STCauto) && !inferred)
- dsym->error("storage class `auto` has no effect if type is not inferred, did you mean `scope`?");
-
- if (tb->ty == Ttuple)
- {
- /* Instead, declare variables for each of the tuple elements
- * and add those.
- */
- TypeTuple *tt = (TypeTuple *)tb;
- size_t nelems = Parameter::dim(tt->arguments);
- Expression *ie = (dsym->_init && !dsym->_init->isVoidInitializer()) ? initializerToExpression(dsym->_init) : NULL;
- if (ie)
- ie = expressionSemantic(ie, sc);
-
- if (nelems > 0 && ie)
- {
- Expressions *iexps = new Expressions();
- iexps->push(ie);
-
- Expressions *exps = new Expressions();
-
- for (size_t pos = 0; pos < iexps->length; pos++)
- {
- Lexpand1:
- Expression *e = (*iexps)[pos];
- Parameter *arg = Parameter::getNth(tt->arguments, pos);
- arg->type = typeSemantic(arg->type, dsym->loc, sc);
- //printf("[%d] iexps->length = %d, ", pos, iexps->length);
- //printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars());
- //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars());
-
- if (e != ie)
- {
- if (iexps->length > nelems)
- goto Lnomatch;
- if (e->type->implicitConvTo(arg->type))
- continue;
- }
-
- if (e->op == TOKtuple)
- {
- TupleExp *te = (TupleExp *)e;
- if (iexps->length - 1 + te->exps->length > nelems)
- goto Lnomatch;
-
- iexps->remove(pos);
- iexps->insert(pos, te->exps);
- (*iexps)[pos] = Expression::combine(te->e0, (*iexps)[pos]);
- goto Lexpand1;
- }
- else if (isAliasThisTuple(e))
- {
- VarDeclaration *v = copyToTemp(0, "__tup", e);
- dsymbolSemantic(v, sc);
- VarExp *ve = new VarExp(dsym->loc, v);
- ve->type = e->type;
-
- exps->setDim(1);
- (*exps)[0] = ve;
- expandAliasThisTuples(exps, 0);
-
- for (size_t u = 0; u < exps->length ; u++)
- {
- Lexpand2:
- Expression *ee = (*exps)[u];
- arg = Parameter::getNth(tt->arguments, pos + u);
- arg->type = typeSemantic(arg->type, dsym->loc, sc);
- //printf("[%d+%d] exps->length = %d, ", pos, u, exps->length);
- //printf("ee = (%s %s, %s), ", Token::tochars[ee->op], ee->toChars(), ee->type->toChars());
- //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars());
-
- size_t iexps_dim = iexps->length - 1 + exps->length;
- if (iexps_dim > nelems)
- goto Lnomatch;
- if (ee->type->implicitConvTo(arg->type))
- continue;
-
- if (expandAliasThisTuples(exps, u) != -1)
- goto Lexpand2;
- }
-
- if ((*exps)[0] != ve)
- {
- Expression *e0 = (*exps)[0];
- (*exps)[0] = new CommaExp(dsym->loc, new DeclarationExp(dsym->loc, v), e0);
- (*exps)[0]->type = e0->type;
-
- iexps->remove(pos);
- iexps->insert(pos, exps);
- goto Lexpand1;
- }
- }
- }
- if (iexps->length < nelems)
- goto Lnomatch;
-
- ie = new TupleExp(dsym->_init->loc, iexps);
- }
- Lnomatch:
-
- if (ie && ie->op == TOKtuple)
- {
- TupleExp *te = (TupleExp *)ie;
- size_t tedim = te->exps->length;
- if (tedim != nelems)
- {
- error(dsym->loc, "tuple of %d elements cannot be assigned to tuple of %d elements", (int)tedim, (int)nelems);
- for (size_t u = tedim; u < nelems; u++) // fill dummy expression
- te->exps->push(new ErrorExp());
- }
- }
-
- Objects *exps = new Objects();
- exps->setDim(nelems);
- for (size_t i = 0; i < nelems; i++)
- {
- Parameter *arg = Parameter::getNth(tt->arguments, i);
-
- OutBuffer buf;
- buf.printf("__%s_field_%llu", dsym->ident->toChars(), (ulonglong)i);
- const char *name = buf.extractChars();
- Identifier *id = Identifier::idPool(name);
-
- Initializer *ti;
- if (ie)
- {
- Expression *einit = ie;
- if (ie->op == TOKtuple)
- {
- TupleExp *te = (TupleExp *)ie;
- einit = (*te->exps)[i];
- if (i == 0)
- einit = Expression::combine(te->e0, einit);
- }
- ti = new ExpInitializer(einit->loc, einit);
- }
- else
- ti = dsym->_init ? dsym->_init->syntaxCopy() : NULL;
-
- VarDeclaration *v = new VarDeclaration(dsym->loc, arg->type, id, ti);
- v->storage_class |= STCtemp | STClocal | dsym->storage_class;
- if (arg->storageClass & STCparameter)
- v->storage_class |= arg->storageClass;
- //printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars());
- dsymbolSemantic(v, sc);
-
- if (sc->scopesym)
- {
- //printf("adding %s to %s\n", v->toChars(), sc->scopesym->toChars());
- if (sc->scopesym->members)
- sc->scopesym->members->push(v);
- }
-
- Expression *e = new DsymbolExp(dsym->loc, v);
- (*exps)[i] = e;
- }
- TupleDeclaration *v2 = new TupleDeclaration(dsym->loc, dsym->ident, exps);
- v2->parent = dsym->parent;
- v2->isexp = true;
- dsym->aliassym = v2;
- dsym->semanticRun = PASSsemanticdone;
- return;
- }
-
- /* Storage class can modify the type
- */
- dsym->type = dsym->type->addStorageClass(dsym->storage_class);
-
- /* Adjust storage class to reflect type
- */
- if (dsym->type->isConst())
- {
- dsym->storage_class |= STCconst;
- if (dsym->type->isShared())
- dsym->storage_class |= STCshared;
- }
- else if (dsym->type->isImmutable())
- dsym->storage_class |= STCimmutable;
- else if (dsym->type->isShared())
- dsym->storage_class |= STCshared;
- else if (dsym->type->isWild())
- dsym->storage_class |= STCwild;
-
- if (StorageClass stc = dsym->storage_class & (STCsynchronized | STCoverride | STCabstract | STCfinal))
- {
- if (stc == STCfinal)
- dsym->error("cannot be final, perhaps you meant const?");
- else
- {
- OutBuffer buf;
- stcToBuffer(&buf, stc);
- dsym->error("cannot be %s", buf.peekChars());
- }
- dsym->storage_class &= ~stc; // strip off
- }
-
- if (dsym->storage_class & STCscope)
- {
- StorageClass stc = dsym->storage_class & (STCstatic | STCextern | STCmanifest | STCtls | STCgshared);
- if (stc)
- {
- OutBuffer buf;
- stcToBuffer(&buf, stc);
- dsym->error("cannot be `scope` and `%s`", buf.peekChars());
- }
- else if (dsym->isMember())
- {
- dsym->error("field cannot be `scope`");
- }
- else if (!dsym->type->hasPointers())
- {
- dsym->storage_class &= ~STCscope; // silently ignore; may occur in generic code
- }
- }
-
- if (dsym->storage_class & (STCstatic | STCextern | STCmanifest | STCtemplateparameter | STCtls | STCgshared | STCctfe))
- {
- }
- else
- {
- AggregateDeclaration *aad = parent->isAggregateDeclaration();
- if (aad)
- {
- if (global.params.vfield &&
- dsym->storage_class & (STCconst | STCimmutable) && dsym->_init && !dsym->_init->isVoidInitializer())
- {
- const char *s = (dsym->storage_class & STCimmutable) ? "immutable" : "const";
- message(dsym->loc, "`%s.%s` is `%s` field", ad->toPrettyChars(), dsym->toChars(), s);
- }
- dsym->storage_class |= STCfield;
- if (tbn->ty == Tstruct && ((TypeStruct *)tbn)->sym->noDefaultCtor)
- {
- if (!dsym->isThisDeclaration() && !dsym->_init)
- aad->noDefaultCtor = true;
- }
- }
-
- InterfaceDeclaration *id = parent->isInterfaceDeclaration();
- if (id)
- {
- dsym->error("field not allowed in interface");
- }
- else if (aad && aad->sizeok == SIZEOKdone)
- {
- dsym->error("cannot be further field because it will change the determined %s size", aad->toChars());
- }
-
- /* Templates cannot add fields to aggregates
- */
- TemplateInstance *ti = parent->isTemplateInstance();
- if (ti)
- {
- // Take care of nested templates
- while (1)
- {
- TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance();
- if (!ti2)
- break;
- ti = ti2;
- }
-
- // If it's a member template
- AggregateDeclaration *ad2 = ti->tempdecl->isMember();
- if (ad2 && dsym->storage_class != STCundefined)
- {
- dsym->error("cannot use template to add field to aggregate `%s`", ad2->toChars());
- }
- }
- }
-
- if ((dsym->storage_class & (STCref | STCparameter | STCforeach | STCtemp | STCresult)) == STCref && dsym->ident != Id::This)
- {
- dsym->error("only parameters or foreach declarations can be ref");
- }
-
- if (dsym->type->hasWild())
- {
- if (dsym->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCfield) ||
- dsym->isDataseg()
- )
- {
- dsym->error("only parameters or stack based variables can be inout");
- }
- FuncDeclaration *func = sc->func;
- if (func)
- {
- if (func->fes)
- func = func->fes->func;
- bool isWild = false;
- for (FuncDeclaration *fd = func; fd; fd = fd->toParent2()->isFuncDeclaration())
- {
- if (((TypeFunction *)fd->type)->iswild)
- {
- isWild = true;
- break;
- }
- }
- if (!isWild)
- {
- dsym->error("inout variables can only be declared inside inout functions");
- }
- }
- }
-
- if (!(dsym->storage_class & (STCctfe | STCref | STCresult)) && tbn->ty == Tstruct &&
- ((TypeStruct *)tbn)->sym->noDefaultCtor)
- {
- if (!dsym->_init)
- {
- if (dsym->isField())
- {
- /* For fields, we'll check the constructor later to make sure it is initialized
- */
- dsym->storage_class |= STCnodefaultctor;
- }
- else if (dsym->storage_class & STCparameter)
- ;
- else
- dsym->error("default construction is disabled for type %s", dsym->type->toChars());
- }
- }
-
- FuncDeclaration *fd = parent->isFuncDeclaration();
- if (dsym->type->isscope() && !(dsym->storage_class & STCnodtor))
- {
- if (dsym->storage_class & (STCfield | STCout | STCref | STCstatic | STCmanifest | STCtls | STCgshared) || !fd)
- {
- dsym->error("globals, statics, fields, manifest constants, ref and out parameters cannot be scope");
- }
-
- if (!(dsym->storage_class & STCscope))
- {
- if (!(dsym->storage_class & STCparameter) && dsym->ident != Id::withSym)
- dsym->error("reference to scope class must be scope");
- }
- }
-
- // Calculate type size + safety checks
- if (sc->func && !sc->intypeof)
- {
- if (dsym->_init && dsym->_init->isVoidInitializer() && dsym->type->hasPointers()) // get type size
- {
- if (sc->func->setUnsafe())
- dsym->error("void initializers for pointers not allowed in safe functions");
- }
- else if (!dsym->_init &&
- !(dsym->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCfield | STCparameter)) &&
- dsym->type->hasVoidInitPointers())
- {
- if (sc->func->setUnsafe())
- dsym->error("void initializers for pointers not allowed in safe functions");
- }
- }
-
- if (!dsym->_init && !fd)
- {
- // If not mutable, initializable by constructor only
- dsym->storage_class |= STCctorinit;
- }
-
- if (dsym->_init)
- dsym->storage_class |= STCinit; // remember we had an explicit initializer
- else if (dsym->storage_class & STCmanifest)
- dsym->error("manifest constants must have initializers");
-
- bool isBlit = false;
- d_uns64 sz = 0;
- if (!dsym->_init && !sc->inunion && !(dsym->storage_class & (STCstatic | STCgshared | STCextern)) && fd &&
- (!(dsym->storage_class & (STCfield | STCin | STCforeach | STCparameter | STCresult))
- || (dsym->storage_class & STCout)) &&
- (sz = dsym->type->size()) != 0)
- {
- // Provide a default initializer
- //printf("Providing default initializer for '%s'\n", dsym->toChars());
- if (sz == SIZE_INVALID && dsym->type->ty != Terror)
- dsym->error("size of type %s is invalid", dsym->type->toChars());
-
- Type *tv = dsym->type;
- while (tv->ty == Tsarray) // Don't skip Tenum
- tv = tv->nextOf();
- if (tv->needsNested())
- {
- /* Nested struct requires valid enclosing frame pointer.
- * In StructLiteralExp::toElem(), it's calculated.
- */
- assert(tv->toBasetype()->ty == Tstruct);
- checkFrameAccess(dsym->loc, sc, ((TypeStruct *)tbn)->sym);
-
- Expression *e = tv->defaultInitLiteral(dsym->loc);
- e = new BlitExp(dsym->loc, new VarExp(dsym->loc, dsym), e);
- e = expressionSemantic(e, sc);
- dsym->_init = new ExpInitializer(dsym->loc, e);
- goto Ldtor;
- }
- if (tv->ty == Tstruct && ((TypeStruct *)tv)->sym->zeroInit == 1)
- {
- /* If a struct is all zeros, as a special case
- * set it's initializer to the integer 0.
- * In AssignExp::toElem(), we check for this and issue
- * a memset() to initialize the struct.
- * Must do same check in interpreter.
- */
- Expression *e = new IntegerExp(dsym->loc, 0, Type::tint32);
- e = new BlitExp(dsym->loc, new VarExp(dsym->loc, dsym), e);
- e->type = dsym->type; // don't type check this, it would fail
- dsym->_init = new ExpInitializer(dsym->loc, e);
- goto Ldtor;
- }
- if (dsym->type->baseElemOf()->ty == Tvoid)
- {
- dsym->error("%s does not have a default initializer", dsym->type->toChars());
- }
- else if (Expression *e = dsym->type->defaultInit(dsym->loc))
- {
- dsym->_init = new ExpInitializer(dsym->loc, e);
- }
- // Default initializer is always a blit
- isBlit = true;
- }
-
- if (dsym->_init)
- {
- sc = sc->push();
- sc->stc &= ~(STC_TYPECTOR | STCpure | STCnothrow | STCnogc | STCref | STCdisable);
-
- ExpInitializer *ei = dsym->_init->isExpInitializer();
- if (ei) // Bugzilla 13424: Preset the required type to fail in FuncLiteralDeclaration::semantic3
- ei->exp = inferType(ei->exp, dsym->type);
-
- // If inside function, there is no semantic3() call
- if (sc->func || sc->intypeof == 1)
- {
- // If local variable, use AssignExp to handle all the various
- // possibilities.
- if (fd &&
- !(dsym->storage_class & (STCmanifest | STCstatic | STCtls | STCgshared | STCextern)) &&
- !dsym->_init->isVoidInitializer())
- {
- //printf("fd = '%s', var = '%s'\n", fd->toChars(), dsym->toChars());
- if (!ei)
- {
- ArrayInitializer *ai = dsym->_init->isArrayInitializer();
- Expression *e;
- if (ai && tb->ty == Taarray)
- e = ai->toAssocArrayLiteral();
- else
- e = initializerToExpression(dsym->_init);
- if (!e)
- {
- // Run semantic, but don't need to interpret
- dsym->_init = initializerSemantic(dsym->_init, sc, dsym->type, INITnointerpret);
- e = initializerToExpression(dsym->_init);
- if (!e)
- {
- dsym->error("is not a static and cannot have static initializer");
- e = new ErrorExp();
- }
- }
- ei = new ExpInitializer(dsym->_init->loc, e);
- dsym->_init = ei;
- }
-
- Expression *exp = ei->exp;
- Expression *e1 = new VarExp(dsym->loc, dsym);
- if (isBlit)
- exp = new BlitExp(dsym->loc, e1, exp);
- else
- exp = new ConstructExp(dsym->loc, e1, exp);
- dsym->canassign++;
- exp = expressionSemantic(exp, sc);
- dsym->canassign--;
- exp = exp->optimize(WANTvalue);
-
- if (exp->op == TOKerror)
- {
- dsym->_init = new ErrorInitializer();
- ei = NULL;
- }
- else
- ei->exp = exp;
-
- if (ei && dsym->isScope())
- {
- Expression *ex = ei->exp;
- while (ex->op == TOKcomma)
- ex = ((CommaExp *)ex)->e2;
- if (ex->op == TOKblit || ex->op == TOKconstruct)
- ex = ((AssignExp *)ex)->e2;
- if (ex->op == TOKnew)
- {
- // See if initializer is a NewExp that can be allocated on the stack
- NewExp *ne = (NewExp *)ex;
- if (dsym->type->toBasetype()->ty == Tclass)
- {
- if (ne->newargs && ne->newargs->length > 1)
- {
- dsym->mynew = true;
- }
- else
- {
- ne->onstack = true;
- dsym->onstack = true;
- }
- }
- }
- else if (ex->op == TOKfunction)
- {
- // or a delegate that doesn't escape a reference to the function
- FuncDeclaration *f = ((FuncExp *)ex)->fd;
- f->tookAddressOf--;
- }
- }
- }
- else
- {
- // Bugzilla 14166: Don't run CTFE for the temporary variables inside typeof
- dsym->_init = initializerSemantic(dsym->_init, sc, dsym->type, sc->intypeof == 1 ? INITnointerpret : INITinterpret);
- }
- }
- else if (parent->isAggregateDeclaration())
- {
- dsym->_scope = scx ? scx : sc->copy();
- dsym->_scope->setNoFree();
- }
- else if (dsym->storage_class & (STCconst | STCimmutable | STCmanifest) ||
- dsym->type->isConst() || dsym->type->isImmutable())
- {
- /* Because we may need the results of a const declaration in a
- * subsequent type, such as an array dimension, before semantic2()
- * gets ordinarily run, try to run semantic2() now.
- * Ignore failure.
- */
-
- if (!inferred)
- {
- unsigned errors = global.errors;
- dsym->inuse++;
- if (ei)
- {
- Expression *exp = ei->exp->syntaxCopy();
-
- bool needctfe = dsym->isDataseg() || (dsym->storage_class & STCmanifest);
- if (needctfe) sc = sc->startCTFE();
- exp = expressionSemantic(exp, sc);
- exp = resolveProperties(sc, exp);
- if (needctfe) sc = sc->endCTFE();
-
- Type *tb2 = dsym->type->toBasetype();
- Type *ti = exp->type->toBasetype();
-
- /* The problem is the following code:
- * struct CopyTest {
- * double x;
- * this(double a) { x = a * 10.0;}
- * this(this) { x += 2.0; }
- * }
- * const CopyTest z = CopyTest(5.3); // ok
- * const CopyTest w = z; // not ok, postblit not run
- * static assert(w.x == 55.0);
- * because the postblit doesn't get run on the initialization of w.
- */
- if (ti->ty == Tstruct)
- {
- StructDeclaration *sd = ((TypeStruct *)ti)->sym;
- /* Look to see if initializer involves a copy constructor
- * (which implies a postblit)
- */
- // there is a copy constructor
- // and exp is the same struct
- if (sd->postblit &&
- tb2->toDsymbol(NULL) == sd)
- {
- // The only allowable initializer is a (non-copy) constructor
- if (exp->isLvalue())
- dsym->error("of type struct %s uses this(this), which is not allowed in static initialization", tb2->toChars());
- }
- }
- ei->exp = exp;
- }
- dsym->_init = initializerSemantic(dsym->_init, sc, dsym->type, INITinterpret);
- dsym->inuse--;
- if (global.errors > errors)
- {
- dsym->_init = new ErrorInitializer();
- dsym->type = Type::terror;
- }
- }
- else
- {
- dsym->_scope = scx ? scx : sc->copy();
- dsym->_scope->setNoFree();
- }
- }
- sc = sc->pop();
- }
-
- Ldtor:
- /* Build code to execute destruction, if necessary
- */
- dsym->edtor = dsym->callScopeDtor(sc);
- if (dsym->edtor)
- {
- if (sc->func && dsym->storage_class & (STCstatic | STCgshared))
- dsym->edtor = expressionSemantic(dsym->edtor, sc->_module->_scope);
- else
- dsym->edtor = expressionSemantic(dsym->edtor, sc);
-
- #if 0 // currently disabled because of std.stdio.stdin, stdout and stderr
- if (dsym->isDataseg() && !(dsym->storage_class & STCextern))
- dsym->error("static storage variables cannot have destructors");
- #endif
- }
-
- dsym->semanticRun = PASSsemanticdone;
-
- if (dsym->type->toBasetype()->ty == Terror)
- dsym->errors = true;
-
- if (sc->scopesym && !sc->scopesym->isAggregateDeclaration())
- {
- for (ScopeDsymbol *sym = sc->scopesym; sym && dsym->endlinnum == 0;
- sym = sym->parent ? sym->parent->isScopeDsymbol() : NULL)
- dsym->endlinnum = sym->endlinnum;
- }
- }
-
- void visit(TypeInfoDeclaration *dsym)
- {
- assert(dsym->linkage == LINKc);
- }
-
- void visit(Import *imp)
- {
- //printf("Import::semantic('%s') %s\n", toPrettyChars(), imp->id->toChars());
- if (imp->semanticRun > PASSinit)
- return;
-
- if (imp->_scope)
- {
- sc = imp->_scope;
- imp->_scope = NULL;
- }
- if (!sc)
- return;
-
- imp->semanticRun = PASSsemantic;
-
- // Load if not already done so
- if (!imp->mod)
- {
- imp->load(sc);
- if (imp->mod)
- imp->mod->importAll(NULL);
- }
-
- if (imp->mod)
- {
- // Modules need a list of each imported module
- //printf("%s imports %s\n", sc->_module->toChars(), imp->mod->toChars());
- sc->_module->aimports.push(imp->mod);
-
- if (sc->explicitProtection)
- imp->protection = sc->protection;
-
- if (!imp->aliasId && !imp->names.length) // neither a selective nor a renamed import
- {
- ScopeDsymbol *scopesym = NULL;
- if (sc->explicitProtection)
- imp->protection = sc->protection.kind;
- for (Scope *scd = sc; scd; scd = scd->enclosing)
- {
- if (!scd->scopesym)
- continue;
- scopesym = scd->scopesym;
- break;
- }
-
- if (!imp->isstatic)
- {
- scopesym->importScope(imp->mod, imp->protection);
- }
-
- imp->addPackageAccess(scopesym);
- }
-
- dsymbolSemantic(imp->mod, NULL);
-
- if (imp->mod->needmoduleinfo)
- {
- //printf("module4 %s because of %s\n", sc->_module->toChars(), imp->mod->toChars());
- sc->_module->needmoduleinfo = 1;
- }
-
- sc = sc->push(imp->mod);
- sc->protection = imp->protection;
- for (size_t i = 0; i < imp->aliasdecls.length; i++)
- {
- AliasDeclaration *ad = imp->aliasdecls[i];
- //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), imp->aliases[i]->toChars(), imp->names[i]->toChars(), ad->_scope);
- Dsymbol *sym = imp->mod->search(imp->loc, imp->names[i], IgnorePrivateImports);
- if (sym)
- {
- if (!symbolIsVisible(sc, sym))
- imp->mod->error(imp->loc, "member `%s` is not visible from module `%s`",
- imp->names[i]->toChars(), sc->_module->toChars());
- dsymbolSemantic(ad, sc);
- // If the import declaration is in non-root module,
- // analysis of the aliased symbol is deferred.
- // Therefore, don't see the ad->aliassym or ad->type here.
- }
- else
- {
- Dsymbol *s = imp->mod->search_correct(imp->names[i]);
- if (s)
- imp->mod->error(imp->loc, "import `%s` not found, did you mean %s `%s`?", imp->names[i]->toChars(), s->kind(), s->toPrettyChars());
- else
- imp->mod->error(imp->loc, "import `%s` not found", imp->names[i]->toChars());
- ad->type = Type::terror;
- }
- }
- sc = sc->pop();
- }
-
- imp->semanticRun = PASSsemanticdone;
-
- // object self-imports itself, so skip that (Bugzilla 7547)
- // don't list pseudo modules __entrypoint.d, __main.d (Bugzilla 11117, 11164)
- if (global.params.moduleDeps != NULL &&
- !(imp->id == Id::object && sc->_module->ident == Id::object) &&
- sc->_module->ident != Id::entrypoint &&
- strcmp(sc->_module->ident->toChars(), "__main") != 0)
- {
- /* The grammar of the file is:
- * ImportDeclaration
- * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> "
- * ModuleAliasIdentifier ] "\n"
- *
- * BasicImportDeclaration
- * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string"
- * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")"
- *
- * FilePath
- * - any string with '(', ')' and '\' escaped with the '\' character
- */
-
- OutBuffer *ob = global.params.moduleDeps;
- Module* imod = sc->instantiatingModule();
- if (!global.params.moduleDepsFile.length)
- ob->writestring("depsImport ");
- ob->writestring(imod->toPrettyChars());
- ob->writestring(" (");
- escapePath(ob, imod->srcfile->toChars());
- ob->writestring(") : ");
-
- // use protection instead of sc->protection because it couldn't be
- // resolved yet, see the comment above
- protectionToBuffer(ob, imp->protection);
- ob->writeByte(' ');
- if (imp->isstatic)
- {
- stcToBuffer(ob, STCstatic);
- ob->writeByte(' ');
- }
- ob->writestring(": ");
-
- if (imp->packages)
- {
- for (size_t i = 0; i < imp->packages->length; i++)
- {
- Identifier *pid = (*imp->packages)[i];
- ob->printf("%s.", pid->toChars());
- }
- }
-
- ob->writestring(imp->id->toChars());
- ob->writestring(" (");
- if (imp->mod)
- escapePath(ob, imp->mod->srcfile->toChars());
- else
- ob->writestring("???");
- ob->writeByte(')');
-
- for (size_t i = 0; i < imp->names.length; i++)
- {
- if (i == 0)
- ob->writeByte(':');
- else
- ob->writeByte(',');
-
- Identifier *name = imp->names[i];
- Identifier *alias = imp->aliases[i];
-
- if (!alias)
- {
- ob->printf("%s", name->toChars());
- alias = name;
- }
- else
- ob->printf("%s=%s", alias->toChars(), name->toChars());
- }
-
- if (imp->aliasId)
- ob->printf(" -> %s", imp->aliasId->toChars());
-
- ob->writenl();
- }
-
- //printf("-Import::semantic('%s'), pkg = %p\n", imp->toChars(), imp->pkg);
- }
-
- void attribSemantic(AttribDeclaration *ad)
- {
- if (ad->semanticRun != PASSinit)
- return;
- ad->semanticRun = PASSsemantic;
- Dsymbols *d = ad->include(sc);
- //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
- if (d)
- {
- Scope *sc2 = ad->newScope(sc);
- bool errors = false;
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- dsymbolSemantic(s, sc2);
- errors |= s->errors;
- }
- ad->errors |= errors;
- if (sc2 != sc)
- sc2->pop();
- }
- ad->semanticRun = PASSsemanticdone;
- }
-
- void visit(AttribDeclaration *atd)
- {
- attribSemantic(atd);
- }
-
- void visit(AnonDeclaration *scd)
- {
- //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", scd);
- assert(sc->parent);
- Dsymbol *p = sc->parent->pastMixin();
- AggregateDeclaration *ad = p->isAggregateDeclaration();
- if (!ad)
- {
- error(scd->loc, "%s can only be a part of an aggregate, not %s %s",
- scd->kind(), p->kind(), p->toChars());
- scd->errors = true;
- return;
- }
-
- if (scd->decl)
- {
- sc = sc->push();
- sc->stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared);
- sc->inunion = scd->isunion;
- sc->flags = 0;
-
- for (size_t i = 0; i < scd->decl->length; i++)
- {
- Dsymbol *s = (*scd->decl)[i];
- dsymbolSemantic(s, sc);
- }
- sc = sc->pop();
- }
- }
-
- void visit(PragmaDeclaration *pd)
- {
- // Should be merged with PragmaStatement
- //printf("\tPragmaDeclaration::semantic '%s'\n",toChars());
- if (pd->ident == Id::msg)
- {
- if (pd->args)
- {
- for (size_t i = 0; i < pd->args->length; i++)
- {
- Expression *e = (*pd->args)[i];
-
- sc = sc->startCTFE();
- e = expressionSemantic(e, sc);
- e = resolveProperties(sc, e);
- sc = sc->endCTFE();
- e = ctfeInterpretForPragmaMsg(e);
- if (e->op == TOKerror)
- {
- errorSupplemental(pd->loc, "while evaluating pragma(msg, %s)", (*pd->args)[i]->toChars());
- return;
- }
- StringExp *se = e->toStringExp();
- if (se)
- {
- se = se->toUTF8(sc);
- fprintf(stderr, "%.*s", (int)se->len, (char *)se->string);
- }
- else
- fprintf(stderr, "%s", e->toChars());
- }
- fprintf(stderr, "\n");
- }
- goto Lnodecl;
- }
- else if (pd->ident == Id::lib)
- {
- if (!pd->args || pd->args->length != 1)
- pd->error("string expected for library name");
- else
- {
- StringExp *se = semanticString(sc, (*pd->args)[0], "library name");
- if (!se)
- goto Lnodecl;
- (*pd->args)[0] = se;
-
- char *name = (char *)mem.xmalloc(se->len + 1);
- memcpy(name, se->string, se->len);
- name[se->len] = 0;
- if (global.params.verbose)
- message("library %s", name);
- if (global.params.moduleDeps && !global.params.moduleDepsFile.length)
- {
- OutBuffer *ob = global.params.moduleDeps;
- Module *imod = sc->instantiatingModule();
- ob->writestring("depsLib ");
- ob->writestring(imod->toPrettyChars());
- ob->writestring(" (");
- escapePath(ob, imod->srcfile->toChars());
- ob->writestring(") : ");
- ob->writestring((char *) name);
- ob->writenl();
- }
- mem.xfree(name);
- }
- goto Lnodecl;
- }
- else if (pd->ident == Id::startaddress)
- {
- if (!pd->args || pd->args->length != 1)
- pd->error("function name expected for start address");
- else
- {
- /* Bugzilla 11980:
- * resolveProperties and ctfeInterpret call are not necessary.
- */
- Expression *e = (*pd->args)[0];
-
- sc = sc->startCTFE();
- e = expressionSemantic(e, sc);
- sc = sc->endCTFE();
-
- (*pd->args)[0] = e;
- Dsymbol *sa = getDsymbol(e);
- if (!sa || !sa->isFuncDeclaration())
- pd->error("function name expected for start address, not `%s`", e->toChars());
- }
- goto Lnodecl;
- }
- else if (pd->ident == Id::Pinline)
- {
- goto Ldecl;
- }
- else if (pd->ident == Id::mangle)
- {
- if (!pd->args)
- pd->args = new Expressions();
- if (pd->args->length != 1)
- {
- pd->error("string expected for mangled name");
- pd->args->setDim(1);
- (*pd->args)[0] = new ErrorExp(); // error recovery
- goto Ldecl;
- }
-
- StringExp *se = semanticString(sc, (*pd->args)[0], "mangled name");
- if (!se)
- goto Ldecl;
- (*pd->args)[0] = se; // Will be used for later
-
- if (!se->len)
- {
- pd->error("zero-length string not allowed for mangled name");
- goto Ldecl;
- }
- if (se->sz != 1)
- {
- pd->error("mangled name characters can only be of type char");
- goto Ldecl;
- }
-
- /* Note: D language specification should not have any assumption about backend
- * implementation. Ideally pragma(mangle) can accept a string of any content.
- *
- * Therefore, this validation is compiler implementation specific.
- */
- for (size_t i = 0; i < se->len; )
- {
- utf8_t *p = (utf8_t *)se->string;
- dchar_t c = p[i];
- if (c < 0x80)
- {
- if ((c >= 'A' && c <= 'Z') ||
- (c >= 'a' && c <= 'z') ||
- (c >= '0' && c <= '9') ||
- (c != 0 && strchr("$%().:?@[]_", c)))
- {
- ++i;
- continue;
- }
- else
- {
- pd->error("char 0x%02x not allowed in mangled name", c);
- break;
- }
- }
-
- if (const char* msg = utf_decodeChar((utf8_t *)se->string, se->len, &i, &c))
- {
- pd->error("%s", msg);
- break;
- }
-
- if (!isUniAlpha(c))
- {
- pd->error("char 0x%04x not allowed in mangled name", c);
- break;
- }
- }
- }
- else if (pd->ident == Id::printf || pd->ident == Id::scanf)
- {
- if (pd->args && pd->args->length != 0)
- pd->error("takes no argument");
- goto Ldecl;
- }
- else if (global.params.ignoreUnsupportedPragmas)
- {
- if (global.params.verbose)
- {
- /* Print unrecognized pragmas
- */
- OutBuffer buf;
- buf.writestring(pd->ident->toChars());
- if (pd->args)
- {
- for (size_t i = 0; i < pd->args->length; i++)
- {
- Expression *e = (*pd->args)[i];
-
- sc = sc->startCTFE();
- e = expressionSemantic(e, sc);
- e = resolveProperties(sc, e);
- sc = sc->endCTFE();
-
- e = e->ctfeInterpret();
- if (i == 0)
- buf.writestring(" (");
- else
- buf.writeByte(',');
- buf.writestring(e->toChars());
- }
- if (pd->args->length)
- buf.writeByte(')');
- }
- message("pragma %s", buf.peekChars());
- }
- goto Lnodecl;
- }
- else
- error(pd->loc, "unrecognized pragma(%s)", pd->ident->toChars());
-
- Ldecl:
- if (pd->decl)
- {
- Scope *sc2 = pd->newScope(sc);
-
- for (size_t i = 0; i < pd->decl->length; i++)
- {
- Dsymbol *s = (*pd->decl)[i];
-
- dsymbolSemantic(s, sc2);
-
- if (pd->ident == Id::mangle)
- {
- assert(pd->args && pd->args->length == 1);
- if (StringExp *se = (*pd->args)[0]->toStringExp())
- {
- char *name = (char *)mem.xmalloc(se->len + 1);
- memcpy(name, se->string, se->len);
- name[se->len] = 0;
-
- unsigned cnt = setMangleOverride(s, name);
- if (cnt > 1)
- pd->error("can only apply to a single declaration");
- }
- }
- }
-
- if (sc2 != sc)
- sc2->pop();
- }
- return;
-
- Lnodecl:
- if (pd->decl)
- {
- pd->error("pragma is missing closing `;`");
- goto Ldecl; // do them anyway, to avoid segfaults.
- }
- }
-
- void visit(StaticIfDeclaration *sid)
- {
- attribSemantic(sid);
- }
-
- void visit(StaticForeachDeclaration *sfd)
- {
- attribSemantic(sfd);
- }
-
- Dsymbols *compileIt(CompileDeclaration *cd)
- {
- //printf("CompileDeclaration::compileIt(loc = %d) %s\n", cd->loc.linnum, cd->exp->toChars());
- OutBuffer buf;
- if (expressionsToString(buf, sc, cd->exps))
- return NULL;
-
- unsigned errors = global.errors;
- const size_t len = buf.length();
- const char *str = buf.extractChars();
- Parser p(cd->loc, sc->_module, (const utf8_t *)str, len, false);
- p.nextToken();
-
- Dsymbols *d = p.parseDeclDefs(0);
- if (global.errors != errors)
- return NULL;
-
- if (p.token.value != TOKeof)
- {
- cd->error("incomplete mixin declaration (%s)", str);
- return NULL;
- }
- return d;
- }
-
- void visit(CompileDeclaration *cd)
- {
- //printf("CompileDeclaration::semantic()\n");
- if (!cd->compiled)
- {
- cd->decl = compileIt(cd);
- cd->AttribDeclaration::addMember(sc, cd->scopesym);
- cd->compiled = true;
-
- if (cd->_scope && cd->decl)
- {
- for (size_t i = 0; i < cd->decl->length; i++)
- {
- Dsymbol *s = (*cd->decl)[i];
- s->setScope(cd->_scope);
- }
- }
- }
- attribSemantic(cd);
- }
-
- void visit(UserAttributeDeclaration *uad)
- {
- //printf("UserAttributeDeclaration::semantic() %p\n", this);
- if (uad->decl && !uad->_scope)
- uad->Dsymbol::setScope(sc); // for function local symbols
-
- attribSemantic(uad);
- }
-
- void visit(StaticAssert *sa)
- {
- if (sa->semanticRun < PASSsemanticdone)
- sa->semanticRun = PASSsemanticdone;
- }
-
- void visit(DebugSymbol *ds)
- {
- //printf("DebugSymbol::semantic() %s\n", ds->toChars());
- if (ds->semanticRun < PASSsemanticdone)
- ds->semanticRun = PASSsemanticdone;
- }
-
- void visit(VersionSymbol *vs)
- {
- if (vs->semanticRun < PASSsemanticdone)
- vs->semanticRun = PASSsemanticdone;
- }
-
- void visit(Package *pkg)
- {
- if (pkg->semanticRun < PASSsemanticdone)
- pkg->semanticRun = PASSsemanticdone;
- }
-
- void visit(Module *m)
- {
- if (m->semanticRun != PASSinit)
- return;
-
- //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, m->toChars(), parent);
- m->semanticRun = PASSsemantic;
-
- // Note that modules get their own scope, from scratch.
- // This is so regardless of where in the syntax a module
- // gets imported, it is unaffected by context.
- Scope *sc = m->_scope; // see if already got one from importAll()
- if (!sc)
- {
- sc = Scope::createGlobal(m); // create root scope
- }
-
- //printf("Module = %p, linkage = %d\n", sc->scopesym, sc->linkage);
-
- // Pass 1 semantic routines: do public side of the definition
- for (size_t i = 0; i < m->members->length; i++)
- {
- Dsymbol *s = (*m->members)[i];
-
- //printf("\tModule('%s'): '%s'.semantic()\n", m->toChars(), s->toChars());
- dsymbolSemantic(s, sc);
- m->runDeferredSemantic();
- }
-
- if (m->userAttribDecl)
- {
- dsymbolSemantic(m->userAttribDecl, sc);
- }
-
- if (!m->_scope)
- {
- sc = sc->pop();
- sc->pop(); // 2 pops because Scope::createGlobal() created 2
- }
- m->semanticRun = PASSsemanticdone;
- //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", m, m->toChars(), parent);
- }
-
- void visit(EnumDeclaration *ed)
- {
- //printf("EnumDeclaration::semantic(sd = %p, '%s') %s\n", sc->scopesym, sc->scopesym->toChars(), ed->toChars());
- //printf("EnumDeclaration::semantic() %p %s\n", ed, ed->toChars());
- if (ed->semanticRun >= PASSsemanticdone)
- return; // semantic() already completed
- if (ed->semanticRun == PASSsemantic)
- {
- assert(ed->memtype);
- error(ed->loc, "circular reference to enum base type %s", ed->memtype->toChars());
- ed->errors = true;
- ed->semanticRun = PASSsemanticdone;
- return;
- }
- unsigned dprogress_save = Module::dprogress;
-
- Scope *scx = NULL;
- if (ed->_scope)
- {
- sc = ed->_scope;
- scx = ed->_scope; // save so we don't make redundant copies
- ed->_scope = NULL;
- }
-
- if (!sc)
- return;
-
- ed->parent = sc->parent;
- ed->type = typeSemantic(ed->type, ed->loc, sc);
-
- ed->protection = sc->protection;
- if (sc->stc & STCdeprecated)
- ed->isdeprecated = true;
- ed->userAttribDecl = sc->userAttribDecl;
-
- ed->semanticRun = PASSsemantic;
-
- if (!ed->members && !ed->memtype) // enum ident;
- {
- ed->semanticRun = PASSsemanticdone;
- return;
- }
-
- if (!ed->symtab)
- ed->symtab = new DsymbolTable();
-
- /* The separate, and distinct, cases are:
- * 1. enum { ... }
- * 2. enum : memtype { ... }
- * 3. enum ident { ... }
- * 4. enum ident : memtype { ... }
- * 5. enum ident : memtype;
- * 6. enum ident;
- */
-
- if (ed->memtype)
- {
- ed->memtype = typeSemantic(ed->memtype, ed->loc, sc);
-
- /* Check to see if memtype is forward referenced
- */
- if (ed->memtype->ty == Tenum)
- {
- EnumDeclaration *sym = (EnumDeclaration *)ed->memtype->toDsymbol(sc);
- if (!sym->memtype || !sym->members || !sym->symtab || sym->_scope)
- {
- // memtype is forward referenced, so try again later
- ed->_scope = scx ? scx : sc->copy();
- ed->_scope->setNoFree();
- Module::addDeferredSemantic(ed);
- Module::dprogress = dprogress_save;
- //printf("\tdeferring %s\n", ed->toChars());
- ed->semanticRun = PASSinit;
- return;
- }
- else
- // Ensure that semantic is run to detect. e.g. invalid forward references
- dsymbolSemantic(sym, sc);
- }
- if (ed->memtype->ty == Tvoid)
- {
- ed->error("base type must not be void");
- ed->memtype = Type::terror;
- }
- if (ed->memtype->ty == Terror)
- {
- ed->errors = true;
- if (ed->members)
- {
- for (size_t i = 0; i < ed->members->length; i++)
- {
- Dsymbol *s = (*ed->members)[i];
- s->errors = true; // poison all the members
- }
- }
- ed->semanticRun = PASSsemanticdone;
- return;
- }
- }
-
- ed->semanticRun = PASSsemanticdone;
-
- if (!ed->members) // enum ident : memtype;
- return;
-
- if (ed->members->length == 0)
- {
- ed->error("enum %s must have at least one member", ed->toChars());
- ed->errors = true;
- return;
- }
-
- Module::dprogress++;
-
- Scope *sce;
- if (ed->isAnonymous())
- sce = sc;
- else
- {
- sce = sc->push(ed);
- sce->parent = ed;
- }
- sce = sce->startCTFE();
- sce->setNoFree(); // needed for getMaxMinValue()
-
- /* Each enum member gets the sce scope
- */
- for (size_t i = 0; i < ed->members->length; i++)
- {
- EnumMember *em = (*ed->members)[i]->isEnumMember();
- if (em)
- em->_scope = sce;
- }
-
- if (!ed->added)
- {
- /* addMember() is not called when the EnumDeclaration appears as a function statement,
- * so we have to do what addMember() does and install the enum members in the right symbol
- * table
- */
- ScopeDsymbol *scopesym = NULL;
- if (ed->isAnonymous())
- {
- /* Anonymous enum members get added to enclosing scope.
- */
- for (Scope *sct = sce; 1; sct = sct->enclosing)
- {
- assert(sct);
- if (sct->scopesym)
- {
- scopesym = sct->scopesym;
- if (!sct->scopesym->symtab)
- sct->scopesym->symtab = new DsymbolTable();
- break;
- }
- }
- }
- else
- {
- // Otherwise enum members are in the EnumDeclaration's symbol table
- scopesym = ed;
- }
-
- for (size_t i = 0; i < ed->members->length; i++)
- {
- EnumMember *em = (*ed->members)[i]->isEnumMember();
- if (em)
- {
- em->ed = ed;
- em->addMember(sc, scopesym);
- }
- }
- }
-
- for (size_t i = 0; i < ed->members->length; i++)
- {
- EnumMember *em = (*ed->members)[i]->isEnumMember();
- if (em)
- dsymbolSemantic(em, em->_scope);
- }
- //printf("defaultval = %lld\n", defaultval);
-
- //if (defaultval) printf("defaultval: %s %s\n", defaultval->toChars(), defaultval->type->toChars());
- //printf("members = %s\n", ed->members->toChars());
- }
-
- void visit(EnumMember *em)
- {
- //printf("EnumMember::semantic() %s\n", em->toChars());
- if (em->errors || em->semanticRun >= PASSsemanticdone)
- return;
- if (em->semanticRun == PASSsemantic)
- {
- em->error("circular reference to enum member");
- Lerrors:
- em->errors = true;
- em->semanticRun = PASSsemanticdone;
- return;
- }
- assert(em->ed);
-
- dsymbolSemantic(em->ed, sc);
- if (em->ed->errors)
- goto Lerrors;
-
- if (em->errors || em->semanticRun >= PASSsemanticdone)
- return;
-
- if (em->_scope)
- sc = em->_scope;
- if (!sc)
- return;
-
- em->semanticRun = PASSsemantic;
-
- em->protection = em->ed->isAnonymous() ? em->ed->protection : Prot(Prot::public_);
- em->linkage = LINKd;
- em->storage_class |= STCmanifest;
-
- // https://issues.dlang.org/show_bug.cgi?id=9701
- if (em->ed->isAnonymous())
- {
- if (em->userAttribDecl)
- em->userAttribDecl->userAttribDecl = em->ed->userAttribDecl;
- else
- em->userAttribDecl = em->ed->userAttribDecl;
- }
-
- // The first enum member is special
- bool first = (em == (*em->ed->members)[0]);
-
- if (em->origType)
- {
- em->origType = typeSemantic(em->origType, em->loc, sc);
- em->type = em->origType;
- assert(em->value()); // "type id;" is not a valid enum member declaration
- }
-
- if (em->value())
- {
- Expression *e = em->value();
- assert(e->dyncast() == DYNCAST_EXPRESSION);
- e = expressionSemantic(e, sc);
- e = resolveProperties(sc, e);
- e = e->ctfeInterpret();
- if (e->op == TOKerror)
- goto Lerrors;
- if (first && !em->ed->memtype && !em->ed->isAnonymous())
- {
- em->ed->memtype = e->type;
- if (em->ed->memtype->ty == Terror)
- {
- em->ed->errors = true;
- goto Lerrors;
- }
- if (em->ed->memtype->ty != Terror)
- {
- /* Bugzilla 11746: All of named enum members should have same type
- * with the first member. If the following members were referenced
- * during the first member semantic, their types should be unified.
- */
- for (size_t i = 0; i < em->ed->members->length; i++)
- {
- EnumMember *enm = (*em->ed->members)[i]->isEnumMember();
- if (!enm || enm == em || enm->semanticRun < PASSsemanticdone || enm->origType)
- continue;
-
- //printf("[%d] enm = %s, enm->semanticRun = %d\n", i, enm->toChars(), enm->semanticRun);
- Expression *ev = enm->value();
- ev = ev->implicitCastTo(sc, em->ed->memtype);
- ev = ev->ctfeInterpret();
- ev = ev->castTo(sc, em->ed->type);
- if (ev->op == TOKerror)
- em->ed->errors = true;
- enm->value() = ev;
- }
- if (em->ed->errors)
- {
- em->ed->memtype = Type::terror;
- goto Lerrors;
- }
- }
- }
-
- if (em->ed->memtype && !em->origType)
- {
- e = e->implicitCastTo(sc, em->ed->memtype);
- e = e->ctfeInterpret();
-
- // save origValue for better json output
- em->origValue = e;
-
- if (!em->ed->isAnonymous())
- {
- e = e->castTo(sc, em->ed->type);
- e = e->ctfeInterpret();
- }
- }
- else if (em->origType)
- {
- e = e->implicitCastTo(sc, em->origType);
- e = e->ctfeInterpret();
- assert(em->ed->isAnonymous());
-
- // save origValue for better json output
- em->origValue = e;
- }
- em->value() = e;
- }
- else if (first)
- {
- Type *t;
- if (em->ed->memtype)
- t = em->ed->memtype;
- else
- {
- t = Type::tint32;
- if (!em->ed->isAnonymous())
- em->ed->memtype = t;
- }
- Expression *e = new IntegerExp(em->loc, 0, Type::tint32);
- e = e->implicitCastTo(sc, t);
- e = e->ctfeInterpret();
-
- // save origValue for better json output
- em->origValue = e;
-
- if (!em->ed->isAnonymous())
- {
- e = e->castTo(sc, em->ed->type);
- e = e->ctfeInterpret();
- }
- em->value() = e;
- }
- else
- {
- /* Find the previous enum member,
- * and set this to be the previous value + 1
- */
- EnumMember *emprev = NULL;
- for (size_t i = 0; i < em->ed->members->length; i++)
- {
- EnumMember *enm = (*em->ed->members)[i]->isEnumMember();
- if (enm)
- {
- if (enm == em)
- break;
- emprev = enm;
- }
- }
- assert(emprev);
- if (emprev->semanticRun < PASSsemanticdone) // if forward reference
- dsymbolSemantic(emprev, emprev->_scope); // resolve it
- if (emprev->errors)
- goto Lerrors;
-
- Expression *eprev = emprev->value();
- Type *tprev = eprev->type->equals(em->ed->type) ? em->ed->memtype : eprev->type;
-
- Expression *emax = tprev->getProperty(em->ed->loc, Id::max, 0);
- emax = expressionSemantic(emax, sc);
- emax = emax->ctfeInterpret();
-
- // Set value to (eprev + 1).
- // But first check that (eprev != emax)
- assert(eprev);
- Expression *e = new EqualExp(TOKequal, em->loc, eprev, emax);
- e = expressionSemantic(e, sc);
- e = e->ctfeInterpret();
- if (e->toInteger())
- {
- em->error("initialization with (%s.%s + 1) causes overflow for type `%s`", emprev->ed->toChars(), emprev->toChars(), em->ed->type->toBasetype()->toChars());
- goto Lerrors;
- }
-
- // Now set e to (eprev + 1)
- e = new AddExp(em->loc, eprev, new IntegerExp(em->loc, 1, Type::tint32));
- e = expressionSemantic(e, sc);
- e = e->castTo(sc, eprev->type);
- e = e->ctfeInterpret();
-
- // save origValue (without cast) for better json output
- if (e->op != TOKerror) // avoid duplicate diagnostics
- {
- assert(emprev->origValue);
- em->origValue = new AddExp(em->loc, emprev->origValue, new IntegerExp(em->loc, 1, Type::tint32));
- em->origValue = expressionSemantic(em->origValue, sc);
- em->origValue = em->origValue->ctfeInterpret();
- }
-
- if (e->op == TOKerror)
- goto Lerrors;
- if (e->type->isfloating())
- {
- // Check that e != eprev (not always true for floats)
- Expression *etest = new EqualExp(TOKequal, em->loc, e, eprev);
- etest = expressionSemantic(etest, sc);
- etest = etest->ctfeInterpret();
- if (etest->toInteger())
- {
- em->error("has inexact value, due to loss of precision");
- goto Lerrors;
- }
- }
- em->value() = e;
- }
- if (!em->origType)
- em->type = em->value()->type;
-
- assert(em->origValue);
- em->semanticRun = PASSsemanticdone;
- }
-
- void visit(TemplateDeclaration *tempdecl)
- {
- if (tempdecl->semanticRun != PASSinit)
- return; // semantic() already run
-
- // Remember templates defined in module object that we need to know about
- if (sc->_module && sc->_module->ident == Id::object)
- {
- if (tempdecl->ident == Id::RTInfo)
- Type::rtinfo = tempdecl;
- }
-
- /* Remember Scope for later instantiations, but make
- * a copy since attributes can change.
- */
- if (!tempdecl->_scope)
- {
- tempdecl->_scope = sc->copy();
- tempdecl->_scope->setNoFree();
- }
-
- tempdecl->semanticRun = PASSsemantic;
-
- tempdecl->parent = sc->parent;
- tempdecl->protection = sc->protection;
- tempdecl->isstatic = tempdecl->toParent()->isModule() || (tempdecl->_scope->stc & STCstatic);
-
- if (!tempdecl->isstatic)
- {
- if (AggregateDeclaration *ad = tempdecl->parent->pastMixin()->isAggregateDeclaration())
- ad->makeNested();
- }
-
- // Set up scope for parameters
- ScopeDsymbol *paramsym = new ScopeDsymbol();
- paramsym->parent = tempdecl->parent;
- Scope *paramscope = sc->push(paramsym);
- paramscope->stc = 0;
-
- if (global.params.doDocComments)
- {
- tempdecl->origParameters = new TemplateParameters();
- tempdecl->origParameters->setDim(tempdecl->parameters->length);
- for (size_t i = 0; i < tempdecl->parameters->length; i++)
- {
- TemplateParameter *tp = (*tempdecl->parameters)[i];
- (*tempdecl->origParameters)[i] = tp->syntaxCopy();
- }
- }
-
- for (size_t i = 0; i < tempdecl->parameters->length; i++)
- {
- TemplateParameter *tp = (*tempdecl->parameters)[i];
-
- if (!tp->declareParameter(paramscope))
- {
- error(tp->loc, "parameter `%s` multiply defined", tp->ident->toChars());
- tempdecl->errors = true;
- }
- if (!tpsemantic(tp, paramscope, tempdecl->parameters))
- {
- tempdecl->errors = true;
- }
- if (i + 1 != tempdecl->parameters->length && tp->isTemplateTupleParameter())
- {
- tempdecl->error("template tuple parameter must be last one");
- tempdecl->errors = true;
- }
- }
-
- /* Calculate TemplateParameter::dependent
- */
- TemplateParameters tparams;
- tparams.setDim(1);
- for (size_t i = 0; i < tempdecl->parameters->length; i++)
- {
- TemplateParameter *tp = (*tempdecl->parameters)[i];
- tparams[0] = tp;
-
- for (size_t j = 0; j < tempdecl->parameters->length; j++)
- {
- // Skip cases like: X(T : T)
- if (i == j)
- continue;
-
- if (TemplateTypeParameter *ttp = (*tempdecl->parameters)[j]->isTemplateTypeParameter())
- {
- if (reliesOnTident(ttp->specType, &tparams))
- tp->dependent = true;
- }
- else if (TemplateAliasParameter *tap = (*tempdecl->parameters)[j]->isTemplateAliasParameter())
- {
- if (reliesOnTident(tap->specType, &tparams) ||
- reliesOnTident(isType(tap->specAlias), &tparams))
- {
- tp->dependent = true;
- }
- }
- }
- }
-
- paramscope->pop();
-
- // Compute again
- tempdecl->onemember = NULL;
- if (tempdecl->members)
- {
- Dsymbol *s;
- if (Dsymbol::oneMembers(tempdecl->members, &s, tempdecl->ident) && s)
- {
- tempdecl->onemember = s;
- s->parent = tempdecl;
- }
- }
-
- /* BUG: should check:
- * o no virtual functions or non-static data members of classes
- */
- tempdecl->semanticRun = PASSsemanticdone;
- }
-
- void visit(TemplateInstance *ti)
- {
- templateInstanceSemantic(ti, sc, NULL);
- }
-
- void visit(TemplateMixin *tm)
- {
- if (tm->semanticRun != PASSinit)
- {
- // When a class/struct contains mixin members, and is done over
- // because of forward references, never reach here so semanticRun
- // has been reset to PASSinit.
- return;
- }
- tm->semanticRun = PASSsemantic;
-
- Scope *scx = NULL;
- if (tm->_scope)
- {
- sc = tm->_scope;
- scx = tm->_scope; // save so we don't make redundant copies
- tm->_scope = NULL;
- }
-
- /* Run semantic on each argument, place results in tiargs[],
- * then find best match template with tiargs
- */
- if (!tm->findTempDecl(sc) ||
- !tm->semanticTiargs(sc) ||
- !tm->findBestMatch(sc, NULL))
- {
- if (tm->semanticRun == PASSinit) // forward reference had occured
- {
- //printf("forward reference - deferring\n");
- tm->_scope = scx ? scx : sc->copy();
- tm->_scope->setNoFree();
- Module::addDeferredSemantic(tm);
- return;
- }
-
- tm->inst = tm;
- tm->errors = true;
- return; // error recovery
- }
- TemplateDeclaration *tempdecl = tm->tempdecl->isTemplateDeclaration();
- assert(tempdecl);
-
- if (!tm->ident)
- {
- /* Assign scope local unique identifier, as same as lambdas.
- */
- const char *s = "__mixin";
-
- if (FuncDeclaration *func = sc->parent->isFuncDeclaration())
- {
- tm->symtab = func->localsymtab;
- if (tm->symtab)
- {
- // Inside template constraint, symtab is not set yet.
- goto L1;
- }
- }
- else
- {
- tm->symtab = sc->parent->isScopeDsymbol()->symtab;
- L1:
- assert(tm->symtab);
- int num = (int)dmd_aaLen(tm->symtab->tab) + 1;
- tm->ident = Identifier::generateId(s, num);
- tm->symtab->insert(tm);
- }
- }
-
- tm->inst = tm;
- tm->parent = sc->parent;
-
- /* Detect recursive mixin instantiations.
- */
- for (Dsymbol *s = tm->parent; s; s = s->parent)
- {
- //printf("\ts = '%s'\n", s->toChars());
- TemplateMixin *tmix = s->isTemplateMixin();
- if (!tmix || tempdecl != tmix->tempdecl)
- continue;
-
- /* Different argument list lengths happen with variadic args
- */
- if (tm->tiargs->length != tmix->tiargs->length)
- continue;
-
- for (size_t i = 0; i < tm->tiargs->length; i++)
- {
- RootObject *o = (*tm->tiargs)[i];
- Type *ta = isType(o);
- Expression *ea = isExpression(o);
- Dsymbol *sa = isDsymbol(o);
- RootObject *tmo = (*tmix->tiargs)[i];
- if (ta)
- {
- Type *tmta = isType(tmo);
- if (!tmta)
- goto Lcontinue;
- if (!ta->equals(tmta))
- goto Lcontinue;
- }
- else if (ea)
- {
- Expression *tme = isExpression(tmo);
- if (!tme || !ea->equals(tme))
- goto Lcontinue;
- }
- else if (sa)
- {
- Dsymbol *tmsa = isDsymbol(tmo);
- if (sa != tmsa)
- goto Lcontinue;
- }
- else
- assert(0);
- }
- tm->error("recursive mixin instantiation");
- return;
-
- Lcontinue:
- continue;
- }
-
- // Copy the syntax trees from the TemplateDeclaration
- tm->members = Dsymbol::arraySyntaxCopy(tempdecl->members);
- if (!tm->members)
- return;
-
- tm->symtab = new DsymbolTable();
-
- for (Scope *sce = sc; 1; sce = sce->enclosing)
- {
- ScopeDsymbol *sds = (ScopeDsymbol *)sce->scopesym;
- if (sds)
- {
- sds->importScope(tm, Prot(Prot::public_));
- break;
- }
- }
-
- Scope *scy = sc->push(tm);
- scy->parent = tm;
-
- tm->argsym = new ScopeDsymbol();
- tm->argsym->parent = scy->parent;
- Scope *argscope = scy->push(tm->argsym);
-
- unsigned errorsave = global.errors;
-
- // Declare each template parameter as an alias for the argument type
- tm->declareParameters(argscope);
-
- // Add members to enclosing scope, as well as this scope
- for (size_t i = 0; i < tm->members->length; i++)
- {
- Dsymbol *s = (*tm->members)[i];
- s->addMember(argscope, tm);
- //printf("sc->parent = %p, sc->scopesym = %p\n", sc->parent, sc->scopesym);
- //printf("s->parent = %s\n", s->parent->toChars());
- }
-
- // Do semantic() analysis on template instance members
- Scope *sc2 = argscope->push(tm);
- //size_t deferred_dim = Module::deferred.length;
-
- static int nest;
- //printf("%d\n", nest);
- if (++nest > global.recursionLimit)
- {
- global.gag = 0; // ensure error message gets printed
- tm->error("recursive expansion");
- fatal();
- }
-
- for (size_t i = 0; i < tm->members->length; i++)
- {
- Dsymbol *s = (*tm->members)[i];
- s->setScope(sc2);
- }
-
- for (size_t i = 0; i < tm->members->length; i++)
- {
- Dsymbol *s = (*tm->members)[i];
- s->importAll(sc2);
- }
-
- for (size_t i = 0; i < tm->members->length; i++)
- {
- Dsymbol *s = (*tm->members)[i];
- dsymbolSemantic(s, sc2);
- }
-
- nest--;
-
- /* In DeclDefs scope, TemplateMixin does not have to handle deferred symbols.
- * Because the members would already call Module::addDeferredSemantic() for themselves.
- * See Struct, Class, Interface, and EnumDeclaration::semantic().
- */
- //if (!sc->func && Module::deferred.length > deferred_dim) {}
-
- AggregateDeclaration *ad = tm->toParent()->isAggregateDeclaration();
- if (sc->func && !ad)
- {
- semantic2(tm, sc2);
- semantic3(tm, sc2);
- }
-
- // Give additional context info if error occurred during instantiation
- if (global.errors != errorsave)
- {
- tm->error("error instantiating");
- tm->errors = true;
- }
-
- sc2->pop();
- argscope->pop();
- scy->pop();
- }
-
- void visit(Nspace *ns)
- {
- if (ns->semanticRun != PASSinit)
- return;
- if (ns->_scope)
- {
- sc = ns->_scope;
- ns->_scope = NULL;
- }
- if (!sc)
- return;
-
- ns->semanticRun = PASSsemantic;
- ns->parent = sc->parent;
- if (ns->members)
- {
- assert(sc);
- sc = sc->push(ns);
- sc->linkage = LINKcpp; // note that namespaces imply C++ linkage
- sc->parent = ns;
-
- for (size_t i = 0; i < ns->members->length; i++)
- {
- Dsymbol *s = (*ns->members)[i];
- s->importAll(sc);
- }
-
- for (size_t i = 0; i < ns->members->length; i++)
- {
- Dsymbol *s = (*ns->members)[i];
- dsymbolSemantic(s, sc);
- }
- sc->pop();
- }
- ns->semanticRun = PASSsemanticdone;
- }
-
-
-private:
- static bool isPointerToChar(Parameter *p)
- {
- if (TypePointer *tptr = p->type->isTypePointer())
- {
- return tptr->next->ty == Tchar;
- }
- return false;
- }
-
- static bool isVa_list(Parameter *p, FuncDeclaration *funcdecl, Scope *sc)
- {
- return p->type->equals(target.va_listType(funcdecl->loc, sc));
- }
-
-public:
- void funcDeclarationSemantic(FuncDeclaration *funcdecl)
- {
- TypeFunction *f;
- AggregateDeclaration *ad;
- InterfaceDeclaration *id;
-
- if (funcdecl->semanticRun != PASSinit && funcdecl->isFuncLiteralDeclaration())
- {
- /* Member functions that have return types that are
- * forward references can have semantic() run more than
- * once on them.
- * See test\interface2.d, test20
- */
- return;
- }
-
- if (funcdecl->semanticRun >= PASSsemanticdone)
- return;
- assert(funcdecl->semanticRun <= PASSsemantic);
- funcdecl->semanticRun = PASSsemantic;
-
- if (funcdecl->_scope)
- {
- sc = funcdecl->_scope;
- funcdecl->_scope = NULL;
- }
-
- if (!sc || funcdecl->errors)
- return;
-
- funcdecl->parent = sc->parent;
- Dsymbol *parent = funcdecl->toParent();
-
- funcdecl->foverrides.setDim(0); // reset in case semantic() is being retried for this function
-
- funcdecl->storage_class |= sc->stc & ~STCref;
- ad = funcdecl->isThis();
- // Don't nest structs b/c of generated methods which should not access the outer scopes.
- // https://issues.dlang.org/show_bug.cgi?id=16627
- if (ad && !funcdecl->generated)
- {
- funcdecl->storage_class |= ad->storage_class & (STC_TYPECTOR | STCsynchronized);
- ad->makeNested();
- }
- if (sc->func)
- funcdecl->storage_class |= sc->func->storage_class & STCdisable;
- // Remove prefix storage classes silently.
- if ((funcdecl->storage_class & STC_TYPECTOR) && !(ad || funcdecl->isNested()))
- funcdecl->storage_class &= ~STC_TYPECTOR;
-
- //printf("function storage_class = x%llx, sc->stc = x%llx, %x\n", funcdecl->storage_class, sc->stc, Declaration::isFinal());
-
- FuncLiteralDeclaration *fld = funcdecl->isFuncLiteralDeclaration();
- if (fld && fld->treq)
- {
- Type *treq = fld->treq;
- assert(treq->nextOf()->ty == Tfunction);
- if (treq->ty == Tdelegate)
- fld->tok = TOKdelegate;
- else if (treq->ty == Tpointer && treq->nextOf()->ty == Tfunction)
- fld->tok = TOKfunction;
- else
- assert(0);
- funcdecl->linkage = treq->nextOf()->toTypeFunction()->linkage;
- }
- else
- funcdecl->linkage = sc->linkage;
- funcdecl->inlining = sc->inlining;
- funcdecl->protection = sc->protection;
- funcdecl->userAttribDecl = sc->userAttribDecl;
-
- if (!funcdecl->originalType)
- funcdecl->originalType = funcdecl->type->syntaxCopy();
- if (funcdecl->type->ty != Tfunction)
- {
- if (funcdecl->type->ty != Terror)
- {
- funcdecl->error("%s must be a function instead of %s", funcdecl->toChars(), funcdecl->type->toChars());
- funcdecl->type = Type::terror;
- }
- funcdecl->errors = true;
- return;
- }
- if (!funcdecl->type->deco)
- {
- sc = sc->push();
- sc->stc |= funcdecl->storage_class & (STCdisable | STCdeprecated); // forward to function type
- TypeFunction *tf = funcdecl->type->toTypeFunction();
-
- if (sc->func)
- {
- /* If the nesting parent is pure without inference,
- * then this function defaults to pure too.
- *
- * auto foo() pure {
- * auto bar() {} // become a weak purity funciton
- * class C { // nested class
- * auto baz() {} // become a weak purity funciton
- * }
- *
- * static auto boo() {} // typed as impure
- * // Even though, boo cannot call any impure functions.
- * // See also Expression::checkPurity().
- * }
- */
- if (tf->purity == PUREimpure && (funcdecl->isNested() || funcdecl->isThis()))
- {
- FuncDeclaration *fd = NULL;
- for (Dsymbol *p = funcdecl->toParent2(); p; p = p->toParent2())
- {
- if (AggregateDeclaration *adx = p->isAggregateDeclaration())
- {
- if (adx->isNested())
- continue;
- break;
- }
- if ((fd = p->isFuncDeclaration()) != NULL)
- break;
- }
-
- /* If the parent's purity is inferred, then this function's purity needs
- * to be inferred first.
- */
- if (fd && fd->isPureBypassingInference() >= PUREweak &&
- !funcdecl->isInstantiated())
- {
- tf->purity = PUREfwdref; // default to pure
- }
- }
- }
-
- if (tf->isref) sc->stc |= STCref;
- if (tf->isscope) sc->stc |= STCscope;
- if (tf->isnothrow) sc->stc |= STCnothrow;
- if (tf->isnogc) sc->stc |= STCnogc;
- if (tf->isproperty) sc->stc |= STCproperty;
- if (tf->purity == PUREfwdref) sc->stc |= STCpure;
- if (tf->trust != TRUSTdefault)
- sc->stc &= ~(STCsafe | STCsystem | STCtrusted);
- if (tf->trust == TRUSTsafe) sc->stc |= STCsafe;
- if (tf->trust == TRUSTsystem) sc->stc |= STCsystem;
- if (tf->trust == TRUSTtrusted) sc->stc |= STCtrusted;
-
- if (funcdecl->isCtorDeclaration())
- {
- sc->flags |= SCOPEctor;
-
- Type *tret = ad->handleType();
- assert(tret);
- tret = tret->addStorageClass(funcdecl->storage_class | sc->stc);
- tret = tret->addMod(funcdecl->type->mod);
- tf->next = tret;
-
- if (ad->isStructDeclaration())
- sc->stc |= STCref;
- }
-
- // 'return' on a non-static class member function implies 'scope' as well
- if (ad && ad->isClassDeclaration() && (tf->isreturn || sc->stc & STCreturn) && !(sc->stc & STCstatic))
- sc->stc |= STCscope;
-
- // If 'this' has no pointers, remove 'scope' as it has no meaning
- if (sc->stc & STCscope && ad && ad->isStructDeclaration() && !ad->type->hasPointers())
- {
- sc->stc &= ~STCscope;
- tf->isscope = false;
- }
-
- sc->linkage = funcdecl->linkage;
-
- if (!tf->isNaked() && !(funcdecl->isThis() || funcdecl->isNested()))
- {
- OutBuffer buf;
- MODtoBuffer(&buf, tf->mod);
- funcdecl->error("without `this` cannot be %s", buf.peekChars());
- tf->mod = 0; // remove qualifiers
- }
-
- /* Apply const, immutable, wild and shared storage class
- * to the function type. Do this before type semantic.
- */
- StorageClass stc = funcdecl->storage_class;
- if (funcdecl->type->isImmutable())
- stc |= STCimmutable;
- if (funcdecl->type->isConst())
- stc |= STCconst;
- if (funcdecl->type->isShared() || funcdecl->storage_class & STCsynchronized)
- stc |= STCshared;
- if (funcdecl->type->isWild())
- stc |= STCwild;
- switch (stc & STC_TYPECTOR)
- {
- case STCimmutable:
- case STCimmutable | STCconst:
- case STCimmutable | STCwild:
- case STCimmutable | STCwild | STCconst:
- case STCimmutable | STCshared:
- case STCimmutable | STCshared | STCconst:
- case STCimmutable | STCshared | STCwild:
- case STCimmutable | STCshared | STCwild | STCconst:
- // Don't use immutableOf(), as that will do a merge()
- funcdecl->type = funcdecl->type->makeImmutable();
- break;
-
- case STCconst:
- funcdecl->type = funcdecl->type->makeConst();
- break;
-
- case STCwild:
- funcdecl->type = funcdecl->type->makeWild();
- break;
-
- case STCwild | STCconst:
- funcdecl->type = funcdecl->type->makeWildConst();
- break;
-
- case STCshared:
- funcdecl->type = funcdecl->type->makeShared();
- break;
-
- case STCshared | STCconst:
- funcdecl->type = funcdecl->type->makeSharedConst();
- break;
-
- case STCshared | STCwild:
- funcdecl->type = funcdecl->type->makeSharedWild();
- break;
-
- case STCshared | STCwild | STCconst:
- funcdecl->type = funcdecl->type->makeSharedWildConst();
- break;
-
- case 0:
- break;
-
- default:
- assert(0);
- }
-
- funcdecl->type = typeSemantic(funcdecl->type, funcdecl->loc, sc);
- sc = sc->pop();
- }
- if (funcdecl->type->ty != Tfunction)
- {
- if (funcdecl->type->ty != Terror)
- {
- funcdecl->error("%s must be a function instead of %s", funcdecl->toChars(), funcdecl->type->toChars());
- funcdecl->type = Type::terror;
- }
- funcdecl->errors = true;
- return;
- }
- else
- {
- // Merge back function attributes into 'originalType'.
- // It's used for mangling, ddoc, and json output.
- TypeFunction *tfo = funcdecl->originalType->toTypeFunction();
- TypeFunction *tfx = funcdecl->type->toTypeFunction();
- tfo->mod = tfx->mod;
- tfo->isscope = tfx->isscope;
- tfo->isscopeinferred = tfx->isscopeinferred;
- tfo->isref = tfx->isref;
- tfo->isnothrow = tfx->isnothrow;
- tfo->isnogc = tfx->isnogc;
- tfo->isproperty = tfx->isproperty;
- tfo->purity = tfx->purity;
- tfo->trust = tfx->trust;
-
- funcdecl->storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR);
- }
-
- f = (TypeFunction *)funcdecl->type;
-
- if ((funcdecl->storage_class & STCauto) && !f->isref && !funcdecl->inferRetType)
- funcdecl->error("storage class `auto` has no effect if return type is not inferred");
- /* Functions can only be 'scope' if they have a 'this'
- */
- if (f->isscope && !funcdecl->isNested() && !ad)
- {
- funcdecl->error("functions cannot be scope");
- }
-
- if (f->isreturn && !funcdecl->needThis() && !funcdecl->isNested())
- {
- /* Non-static nested functions have a hidden 'this' pointer to which
- * the 'return' applies
- */
- funcdecl->error("static member has no `this` to which `return` can apply");
- }
-
- if (funcdecl->isAbstract() && !funcdecl->isVirtual())
- {
- const char *sfunc;
- if (funcdecl->isStatic())
- sfunc = "static";
- else if (funcdecl->protection.kind == Prot::private_ || funcdecl->protection.kind == Prot::package_)
- sfunc = protectionToChars(funcdecl->protection.kind);
- else
- sfunc = "non-virtual";
- funcdecl->error("%s functions cannot be abstract", sfunc);
- }
-
- if (funcdecl->isOverride() && !funcdecl->isVirtual())
- {
- Prot::Kind kind = funcdecl->prot().kind;
- if ((kind == Prot::private_ || kind == Prot::package_) && funcdecl->isMember())
- funcdecl->error("%s method is not virtual and cannot override", protectionToChars(kind));
- else
- funcdecl->error("cannot override a non-virtual function");
- }
-
- if (funcdecl->isAbstract() && funcdecl->isFinalFunc())
- funcdecl->error("cannot be both final and abstract");
-
- if (const unsigned pors = sc->flags & (SCOPEprintf | SCOPEscanf))
- {
- /* printf/scanf-like functions must be of the form:
- * extern (C/C++) T printf([parameters...], const(char)* format, ...);
- * or:
- * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list);
- */
- const size_t nparams = f->parameterList.length();
- if ((f->linkage == LINKc || f->linkage == LINKcpp) &&
-
- ((f->parameterList.varargs == VARARGvariadic &&
- nparams >= 1 &&
- isPointerToChar(f->parameterList[nparams - 1])) ||
- (f->parameterList.varargs == VARARGnone &&
- nparams >= 2 &&
- isPointerToChar(f->parameterList[nparams - 2]) &&
- isVa_list(f->parameterList[nparams - 1], funcdecl, sc))
- )
- )
- {
- funcdecl->flags |= (pors == SCOPEprintf) ? FUNCFLAGprintf : FUNCFLAGscanf;
- }
- else
- {
- const char *p = (pors == SCOPEprintf ? Id::printf : Id::scanf)->toChars();
- if (f->parameterList.varargs == VARARGvariadic)
- {
- funcdecl->error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, ...)`"
- " not `%s`",
- p, f->next->toChars(), funcdecl->toChars(), funcdecl->type->toChars());
- }
- else
- {
- funcdecl->error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, va_list)`",
- p, f->next->toChars(), funcdecl->toChars());
- }
- }
- }
-
- id = parent->isInterfaceDeclaration();
- if (id)
- {
- funcdecl->storage_class |= STCabstract;
-
- if (funcdecl->isCtorDeclaration() ||
- funcdecl->isPostBlitDeclaration() ||
- funcdecl->isDtorDeclaration() ||
- funcdecl->isInvariantDeclaration() ||
- funcdecl->isNewDeclaration() || funcdecl->isDelete())
- funcdecl->error("constructors, destructors, postblits, invariants, new and delete functions are not allowed in interface %s", id->toChars());
- if (funcdecl->fbody && funcdecl->isVirtual())
- funcdecl->error("function body only allowed in final functions in interface %s", id->toChars());
- }
-
- if (UnionDeclaration *ud = parent->isUnionDeclaration())
- {
- if (funcdecl->isPostBlitDeclaration() ||
- funcdecl->isDtorDeclaration() ||
- funcdecl->isInvariantDeclaration())
- funcdecl->error("destructors, postblits and invariants are not allowed in union %s", ud->toChars());
- }
-
- if (parent->isStructDeclaration())
- {
- if (funcdecl->isCtorDeclaration())
- {
- goto Ldone;
- }
- }
-
- if (ClassDeclaration *cd = parent->isClassDeclaration())
- {
- if (funcdecl->isCtorDeclaration())
- {
- goto Ldone;
- }
-
- if (funcdecl->storage_class & STCabstract)
- cd->isabstract = ABSyes;
-
- // if static function, do not put in vtbl[]
- if (!funcdecl->isVirtual())
- {
- //printf("\tnot virtual\n");
- goto Ldone;
- }
- // Suppress further errors if the return type is an error
- if (funcdecl->type->nextOf() == Type::terror)
- goto Ldone;
-
- bool may_override = false;
- for (size_t i = 0; i < cd->baseclasses->length; i++)
- {
- BaseClass *b = (*cd->baseclasses)[i];
- ClassDeclaration *cbd = b->type->toBasetype()->isClassHandle();
- if (!cbd)
- continue;
- for (size_t j = 0; j < cbd->vtbl.length; j++)
- {
- FuncDeclaration *f2 = cbd->vtbl[j]->isFuncDeclaration();
- if (!f2 || f2->ident != funcdecl->ident)
- continue;
- if (cbd->parent && cbd->parent->isTemplateInstance())
- {
- if (!f2->functionSemantic())
- goto Ldone;
- }
- may_override = true;
- }
- }
- if (may_override && funcdecl->type->nextOf() == NULL)
- {
- /* If same name function exists in base class but 'this' is auto return,
- * cannot find index of base class's vtbl[] to override.
- */
- funcdecl->error("return type inference is not supported if may override base class function");
- }
-
- /* Find index of existing function in base class's vtbl[] to override
- * (the index will be the same as in cd's current vtbl[])
- */
- int vi = cd->baseClass ? funcdecl->findVtblIndex((Dsymbols*)&cd->baseClass->vtbl, (int)cd->baseClass->vtbl.length)
- : -1;
-
- bool doesoverride = false;
- switch (vi)
- {
- case -1:
- Lintro:
- /* Didn't find one, so
- * This is an 'introducing' function which gets a new
- * slot in the vtbl[].
- */
-
- // Verify this doesn't override previous final function
- if (cd->baseClass)
- {
- Dsymbol *s = cd->baseClass->search(funcdecl->loc, funcdecl->ident);
- if (s)
- {
- FuncDeclaration *f2 = s->isFuncDeclaration();
- if (f2)
- {
- f2 = f2->overloadExactMatch(funcdecl->type);
- if (f2 && f2->isFinalFunc() && f2->prot().kind != Prot::private_)
- funcdecl->error("cannot override final function %s", f2->toPrettyChars());
- }
- }
- }
-
- /* These quirky conditions mimic what VC++ appears to do
- */
- if (global.params.mscoff && cd->isCPPclass() &&
- cd->baseClass && cd->baseClass->vtbl.length)
- {
- /* if overriding an interface function, then this is not
- * introducing and don't put it in the class vtbl[]
- */
- funcdecl->interfaceVirtual = funcdecl->overrideInterface();
- if (funcdecl->interfaceVirtual)
- {
- //printf("\tinterface function %s\n", funcdecl->toChars());
- cd->vtblFinal.push(funcdecl);
- goto Linterfaces;
- }
- }
-
- if (funcdecl->isFinalFunc())
- {
- // Don't check here, as it may override an interface function
- //if (funcdecl->isOverride())
- //funcdecl->error("is marked as override, but does not override any function");
- cd->vtblFinal.push(funcdecl);
- }
- else
- {
- //printf("\tintroducing function %s\n", funcdecl->toChars());
- funcdecl->introducing = 1;
- if (cd->isCPPclass() && target.cpp.reverseOverloads)
- {
- // with dmc, overloaded functions are grouped and in reverse order
- funcdecl->vtblIndex = (int)cd->vtbl.length;
- for (int i = 0; i < (int)cd->vtbl.length; i++)
- {
- if (cd->vtbl[i]->ident == funcdecl->ident && cd->vtbl[i]->parent == parent)
- {
- funcdecl->vtblIndex = (int)i;
- break;
- }
- }
- // shift all existing functions back
- for (int i = (int)cd->vtbl.length; i > funcdecl->vtblIndex; i--)
- {
- FuncDeclaration *fd = cd->vtbl[i-1]->isFuncDeclaration();
- assert(fd);
- fd->vtblIndex++;
- }
- cd->vtbl.insert(funcdecl->vtblIndex, funcdecl);
- }
- else
- {
- // Append to end of vtbl[]
- vi = (int)cd->vtbl.length;
- cd->vtbl.push(funcdecl);
- funcdecl->vtblIndex = vi;
- }
- }
- break;
-
- case -2:
- // can't determine because of forward references
- funcdecl->errors = true;
- return;
-
- default:
- {
- FuncDeclaration *fdv = cd->baseClass->vtbl[vi]->isFuncDeclaration();
- FuncDeclaration *fdc = cd->vtbl[vi]->isFuncDeclaration();
- // This function is covariant with fdv
-
- if (fdc == funcdecl)
- {
- doesoverride = true;
- break;
- }
-
- if (fdc->toParent() == parent)
- {
- //printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n",
- // vi, funcdecl, funcdecl->toChars(), funcdecl->type->toChars(), funcdecl->loc.toChars(),
- // fdc, fdc ->toChars(), fdc ->type->toChars(), fdc ->loc.toChars(),
- // fdv, fdv ->toChars(), fdv ->type->toChars(), fdv ->loc.toChars());
-
- // fdc overrides fdv exactly, then this introduces new function.
- if (fdc->type->mod == fdv->type->mod && funcdecl->type->mod != fdv->type->mod)
- goto Lintro;
- }
-
- // This function overrides fdv
- if (fdv->isFinalFunc())
- funcdecl->error("cannot override final function %s", fdv->toPrettyChars());
-
- if (!funcdecl->isOverride())
- {
- if (fdv->isFuture())
- {
- ::deprecation(funcdecl->loc, "@__future base class method %s is being overridden by %s; rename the latter",
- fdv->toPrettyChars(), funcdecl->toPrettyChars());
- // Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[]
- goto Lintro;
- }
- else
- {
- int vi2 = funcdecl->findVtblIndex(&cd->baseClass->vtbl, (int)cd->baseClass->vtbl.length, false);
- if (vi2 < 0)
- // https://issues.dlang.org/show_bug.cgi?id=17349
- ::deprecation(funcdecl->loc, "cannot implicitly override base class method `%s` with `%s`; add `override` attribute",
- fdv->toPrettyChars(), funcdecl->toPrettyChars());
- else
- error(funcdecl->loc, "implicitly overriding base class method %s with %s deprecated; add `override` attribute",
- fdv->toPrettyChars(), funcdecl->toPrettyChars());
- }
- }
-
- doesoverride = true;
- if (fdc->toParent() == parent)
- {
- // If both are mixins, or both are not, then error.
- // If either is not, the one that is not overrides the other.
- bool thismixin = funcdecl->parent->isClassDeclaration() != NULL;
- bool fdcmixin = fdc->parent->isClassDeclaration() != NULL;
- if (thismixin == fdcmixin)
- {
- funcdecl->error("multiple overrides of same function");
- }
- else if (!thismixin) // fdc overrides fdv
- {
- // this doesn't override any function
- break;
- }
- }
- cd->vtbl[vi] = funcdecl;
- funcdecl->vtblIndex = vi;
-
- /* Remember which functions this overrides
- */
- funcdecl->foverrides.push(fdv);
-
- /* This works by whenever this function is called,
- * it actually returns tintro, which gets dynamically
- * cast to type. But we know that tintro is a base
- * of type, so we could optimize it by not doing a
- * dynamic cast, but just subtracting the isBaseOf()
- * offset if the value is != null.
- */
-
- if (fdv->tintro)
- funcdecl->tintro = fdv->tintro;
- else if (!funcdecl->type->equals(fdv->type))
- {
- /* Only need to have a tintro if the vptr
- * offsets differ
- */
- int offset;
- if (fdv->type->nextOf()->isBaseOf(funcdecl->type->nextOf(), &offset))
- {
- funcdecl->tintro = fdv->type;
- }
- }
- break;
- }
- }
-
- /* Go through all the interface bases.
- * If this function is covariant with any members of those interface
- * functions, set the tintro.
- */
- Linterfaces:
- for (size_t i = 0; i < cd->interfaces.length; i++)
- {
- BaseClass *b = cd->interfaces.ptr[i];
- vi = funcdecl->findVtblIndex((Dsymbols *)&b->sym->vtbl, (int)b->sym->vtbl.length);
- switch (vi)
- {
- case -1:
- break;
-
- case -2:
- // can't determine because of forward references
- funcdecl->errors = true;
- return;
-
- default:
- {
- FuncDeclaration *fdv = (FuncDeclaration *)b->sym->vtbl[vi];
- Type *ti = NULL;
-
- /* Remember which functions this overrides
- */
- funcdecl->foverrides.push(fdv);
-
- /* Should we really require 'override' when implementing
- * an interface function?
- */
- //if (!funcdecl->isOverride())
- //warning(funcdecl->loc, "overrides base class function %s, but is not marked with `override`", fdv->toPrettyChars());
-
- if (fdv->tintro)
- ti = fdv->tintro;
- else if (!funcdecl->type->equals(fdv->type))
- {
- /* Only need to have a tintro if the vptr
- * offsets differ
- */
- int offset;
- if (fdv->type->nextOf()->isBaseOf(funcdecl->type->nextOf(), &offset))
- {
- ti = fdv->type;
- }
- }
- if (ti)
- {
- if (funcdecl->tintro)
- {
- if (!funcdecl->tintro->nextOf()->equals(ti->nextOf()) &&
- !funcdecl->tintro->nextOf()->isBaseOf(ti->nextOf(), NULL) &&
- !ti->nextOf()->isBaseOf(funcdecl->tintro->nextOf(), NULL))
- {
- funcdecl->error("incompatible covariant types %s and %s", funcdecl->tintro->toChars(), ti->toChars());
- }
- }
- funcdecl->tintro = ti;
- }
- goto L2;
- }
- }
- }
-
- if (!doesoverride && funcdecl->isOverride() && (funcdecl->type->nextOf() || !may_override))
- {
- BaseClass *bc = NULL;
- Dsymbol *s = NULL;
- for (size_t i = 0; i < cd->baseclasses->length; i++)
- {
- bc = (*cd->baseclasses)[i];
- s = bc->sym->search_correct(funcdecl->ident);
- if (s) break;
- }
-
- if (s)
- funcdecl->error("does not override any function, did you mean to override `%s%s`?",
- bc->sym->isCPPclass() ? "extern (C++) " : "", s->toPrettyChars());
- else
- funcdecl->error("does not override any function");
- }
-
- L2: ;
-
- /* Go through all the interface bases.
- * Disallow overriding any final functions in the interface(s).
- */
- for (size_t i = 0; i < cd->interfaces.length; i++)
- {
- BaseClass *b = cd->interfaces.ptr[i];
- if (b->sym)
- {
- Dsymbol *s = search_function(b->sym, funcdecl->ident);
- if (s)
- {
- FuncDeclaration *f2 = s->isFuncDeclaration();
- if (f2)
- {
- f2 = f2->overloadExactMatch(funcdecl->type);
- if (f2 && f2->isFinalFunc() && f2->prot().kind != Prot::private_)
- funcdecl->error("cannot override final function %s.%s", b->sym->toChars(), f2->toPrettyChars());
- }
- }
- }
- }
-
- if (funcdecl->isOverride())
- {
- if (funcdecl->storage_class & STCdisable)
- funcdecl->deprecation("overridden functions cannot be annotated @disable");
- if (funcdecl->isDeprecated())
- funcdecl->deprecation("deprecated functions cannot be annotated @disable");
- }
- }
- else if (funcdecl->isOverride() && !parent->isTemplateInstance())
- funcdecl->error("override only applies to class member functions");
-
- // Reflect this->type to f because it could be changed by findVtblIndex
- f = funcdecl->type->toTypeFunction();
-
- Ldone:
- /* Contracts can only appear without a body when they are virtual interface functions
- */
- if (!funcdecl->fbody && !allowsContractWithoutBody(funcdecl))
- funcdecl->error("in and out contracts can only appear without a body when they are virtual interface functions or abstract");
-
- /* Do not allow template instances to add virtual functions
- * to a class.
- */
- if (funcdecl->isVirtual())
- {
- TemplateInstance *ti = parent->isTemplateInstance();
- if (ti)
- {
- // Take care of nested templates
- while (1)
- {
- TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance();
- if (!ti2)
- break;
- ti = ti2;
- }
-
- // If it's a member template
- ClassDeclaration *cd = ti->tempdecl->isClassMember();
- if (cd)
- {
- funcdecl->error("cannot use template to add virtual function to class `%s`", cd->toChars());
- }
- }
- }
-
- if (funcdecl->isMain())
- funcdecl->checkDmain(); // Check main() parameters and return type
-
- /* Purity and safety can be inferred for some functions by examining
- * the function body.
- */
- if (canInferAttributes(funcdecl, sc))
- initInferAttributes(funcdecl);
-
- Module::dprogress++;
- funcdecl->semanticRun = PASSsemanticdone;
-
- /* Save scope for possible later use (if we need the
- * function internals)
- */
- funcdecl->_scope = sc->copy();
- funcdecl->_scope->setNoFree();
-
- static bool printedMain = false; // semantic might run more than once
- if (global.params.verbose && !printedMain)
- {
- const char *type = funcdecl->isMain() ? "main" : funcdecl->isWinMain() ? "winmain" : funcdecl->isDllMain() ? "dllmain" : (const char *)NULL;
- Module *mod = sc->_module;
-
- if (type && mod)
- {
- printedMain = true;
- const char *name = mod->srcfile->toChars();
- const char *path = FileName::searchPath(global.path, name, true);
- message("entry %-10s\t%s", type, path ? path : name);
- }
- }
-
- if (funcdecl->fbody && funcdecl->isMain() && sc->_module->isRoot())
- Compiler::genCmain(sc);
-
- assert(funcdecl->type->ty != Terror || funcdecl->errors);
-
- // semantic for parameters' UDAs
- const size_t nparams = f->parameterList.length();
- for (size_t i = 0; i < nparams; i++)
- {
- Parameter *param = f->parameterList[i];
- if (param && param->userAttribDecl)
- dsymbolSemantic(param->userAttribDecl, sc);
- }
- }
-
- // Do the semantic analysis on the external interface to the function.
- void visit(FuncDeclaration *funcdecl)
- {
- funcDeclarationSemantic(funcdecl);
- }
-
- void visit(CtorDeclaration *ctd)
- {
- //printf("CtorDeclaration::semantic() %s\n", ctd->toChars());
- if (ctd->semanticRun >= PASSsemanticdone)
- return;
- if (ctd->_scope)
- {
- sc = ctd->_scope;
- ctd->_scope = NULL;
- }
-
- ctd->parent = sc->parent;
- Dsymbol *p = ctd->toParent2();
- AggregateDeclaration *ad = p->isAggregateDeclaration();
- if (!ad)
- {
- error(ctd->loc, "constructor can only be a member of aggregate, not %s %s",
- p->kind(), p->toChars());
- ctd->type = Type::terror;
- ctd->errors = true;
- return;
- }
-
- sc = sc->push();
- sc->stc &= ~STCstatic; // not a static constructor
- sc->flags |= SCOPEctor;
-
- funcDeclarationSemantic(ctd);
-
- sc->pop();
-
- if (ctd->errors)
- return;
-
- TypeFunction *tf = ctd->type->toTypeFunction();
-
- /* See if it's the default constructor
- * But, template constructor should not become a default constructor.
- */
- if (ad && (!ctd->parent->isTemplateInstance() || ctd->parent->isTemplateMixin()))
- {
- const size_t dim = tf->parameterList.length();
-
- if (StructDeclaration *sd = ad->isStructDeclaration())
- {
- if (dim == 0 && tf->parameterList.varargs == VARARGnone) // empty default ctor w/o any varargs
- {
- if (ctd->fbody || !(ctd->storage_class & STCdisable) || dim)
- {
- ctd->error("default constructor for structs only allowed "
- "with @disable, no body, and no parameters");
- ctd->storage_class |= STCdisable;
- ctd->fbody = NULL;
- }
- sd->noDefaultCtor = true;
- }
- else if (dim == 0 && tf->parameterList.varargs) // allow varargs only ctor
- {
- }
- else if (dim && tf->parameterList[0]->defaultArg)
- {
- // if the first parameter has a default argument, then the rest does as well
- if (ctd->storage_class & STCdisable)
- {
- ctd->deprecation("@disable'd constructor cannot have default "
- "arguments for all parameters.");
- deprecationSupplemental(ctd->loc, "Use @disable this(); if you want to disable default initialization.");
- }
- else
- ctd->deprecation("all parameters have default arguments, "
- "but structs cannot have default constructors.");
- }
-
- }
- else if (dim == 0 && tf->parameterList.varargs == VARARGnone)
- {
- ad->defaultCtor = ctd;
- }
- }
- }
-
- void visit(PostBlitDeclaration *pbd)
- {
- //printf("PostBlitDeclaration::semantic() %s\n", pbd->toChars());
- //printf("ident: %s, %s, %p, %p\n", pbd->ident->toChars(), Id::dtor->toChars(), pbd->ident, Id::dtor);
- //printf("stc = x%llx\n", sc->stc);
- if (pbd->semanticRun >= PASSsemanticdone)
- return;
- if (pbd->_scope)
- {
- sc = pbd->_scope;
- pbd->_scope = NULL;
- }
-
- pbd->parent = sc->parent;
- Dsymbol *p = pbd->toParent2();
- StructDeclaration *ad = p->isStructDeclaration();
- if (!ad)
- {
- error(pbd->loc, "postblit can only be a member of struct/union, not %s %s",
- p->kind(), p->toChars());
- pbd->type = Type::terror;
- pbd->errors = true;
- return;
- }
- if (pbd->ident == Id::postblit && pbd->semanticRun < PASSsemantic)
- ad->postblits.push(pbd);
- if (!pbd->type)
- pbd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, pbd->storage_class);
-
- sc = sc->push();
- sc->stc &= ~STCstatic; // not static
- sc->linkage = LINKd;
-
- funcDeclarationSemantic(pbd);
-
- sc->pop();
- }
-
- void visit(DtorDeclaration *dd)
- {
- //printf("DtorDeclaration::semantic() %s\n", dd->toChars());
- //printf("ident: %s, %s, %p, %p\n", dd->ident->toChars(), Id::dtor->toChars(), dd->ident, Id::dtor);
- if (dd->semanticRun >= PASSsemanticdone)
- return;
- if (dd->_scope)
- {
- sc = dd->_scope;
- dd->_scope = NULL;
- }
-
- dd->parent = sc->parent;
- Dsymbol *p = dd->toParent2();
- AggregateDeclaration *ad = p->isAggregateDeclaration();
- if (!ad)
- {
- error(dd->loc, "destructor can only be a member of aggregate, not %s %s",
- p->kind(), p->toChars());
- dd->type = Type::terror;
- dd->errors = true;
- return;
- }
- if (dd->ident == Id::dtor && dd->semanticRun < PASSsemantic)
- ad->dtors.push(dd);
- if (!dd->type)
- dd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, dd->storage_class);
-
- sc = sc->push();
- sc->stc &= ~STCstatic; // not a static destructor
- if (sc->linkage != LINKcpp)
- sc->linkage = LINKd;
-
- funcDeclarationSemantic(dd);
-
- sc->pop();
- }
-
- void visit(StaticCtorDeclaration *scd)
- {
- //printf("StaticCtorDeclaration::semantic()\n");
- if (scd->semanticRun >= PASSsemanticdone)
- return;
- if (scd->_scope)
- {
- sc = scd->_scope;
- scd->_scope = NULL;
- }
-
- scd->parent = sc->parent;
- Dsymbol *p = scd->parent->pastMixin();
- if (!p->isScopeDsymbol())
- {
- const char *s = (scd->isSharedStaticCtorDeclaration() ? "shared " : "");
- error(scd->loc, "%sstatic constructor can only be member of module/aggregate/template, not %s %s",
- s, p->kind(), p->toChars());
- scd->type = Type::terror;
- scd->errors = true;
- return;
- }
- if (!scd->type)
- scd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, scd->storage_class);
-
- /* If the static ctor appears within a template instantiation,
- * it could get called multiple times by the module constructors
- * for different modules. Thus, protect it with a gate.
- */
- if (scd->isInstantiated() && scd->semanticRun < PASSsemantic)
- {
- /* Add this prefix to the function:
- * static int gate;
- * if (++gate != 1) return;
- * Note that this is not thread safe; should not have threads
- * during static construction.
- */
- VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL);
- v->storage_class = STCtemp | (scd->isSharedStaticCtorDeclaration() ? STCstatic : STCtls);
- Statements *sa = new Statements();
- Statement *s = new ExpStatement(Loc(), v);
- sa->push(s);
- Expression *e = new IdentifierExp(Loc(), v->ident);
- e = new AddAssignExp(Loc(), e, new IntegerExp(1));
- e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(1));
- s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL, Loc());
- sa->push(s);
- if (scd->fbody)
- sa->push(scd->fbody);
- scd->fbody = new CompoundStatement(Loc(), sa);
- }
-
- funcDeclarationSemantic(scd);
-
- // We're going to need ModuleInfo
- Module *m = scd->getModule();
- if (!m)
- m = sc->_module;
- if (m)
- {
- m->needmoduleinfo = 1;
- //printf("module1 %s needs moduleinfo\n", m->toChars());
- }
- }
-
- void visit(StaticDtorDeclaration *sdd)
- {
- if (sdd->semanticRun >= PASSsemanticdone)
- return;
- if (sdd->_scope)
- {
- sc = sdd->_scope;
- sdd->_scope = NULL;
- }
-
- sdd->parent = sc->parent;
- Dsymbol *p = sdd->parent->pastMixin();
- if (!p->isScopeDsymbol())
- {
- const char *s = (sdd->isSharedStaticDtorDeclaration() ? "shared " : "");
- error(sdd->loc, "%sstatic destructor can only be member of module/aggregate/template, not %s %s",
- s, p->kind(), p->toChars());
- sdd->type = Type::terror;
- sdd->errors = true;
- return;
- }
- if (!sdd->type)
- sdd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, sdd->storage_class);
-
- /* If the static ctor appears within a template instantiation,
- * it could get called multiple times by the module constructors
- * for different modules. Thus, protect it with a gate.
- */
- if (sdd->isInstantiated() && sdd->semanticRun < PASSsemantic)
- {
- /* Add this prefix to the function:
- * static int gate;
- * if (--gate != 0) return;
- * Increment gate during constructor execution.
- * Note that this is not thread safe; should not have threads
- * during static destruction.
- */
- VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL);
- v->storage_class = STCtemp | (sdd->isSharedStaticDtorDeclaration() ? STCstatic : STCtls);
- Statements *sa = new Statements();
- Statement *s = new ExpStatement(Loc(), v);
- sa->push(s);
- Expression *e = new IdentifierExp(Loc(), v->ident);
- e = new AddAssignExp(Loc(), e, new IntegerExp(-1));
- e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(0));
- s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL, Loc());
- sa->push(s);
- if (sdd->fbody)
- sa->push(sdd->fbody);
- sdd->fbody = new CompoundStatement(Loc(), sa);
- sdd->vgate = v;
- }
-
- funcDeclarationSemantic(sdd);
-
- // We're going to need ModuleInfo
- Module *m = sdd->getModule();
- if (!m)
- m = sc->_module;
- if (m)
- {
- m->needmoduleinfo = 1;
- //printf("module2 %s needs moduleinfo\n", m->toChars());
- }
- }
-
- void visit(InvariantDeclaration *invd)
- {
- if (invd->semanticRun >= PASSsemanticdone)
- return;
- if (invd->_scope)
- {
- sc = invd->_scope;
- invd->_scope = NULL;
- }
-
- invd->parent = sc->parent;
- Dsymbol *p = invd->parent->pastMixin();
- AggregateDeclaration *ad = p->isAggregateDeclaration();
- if (!ad)
- {
- error(invd->loc, "invariant can only be a member of aggregate, not %s %s",
- p->kind(), p->toChars());
- invd->type = Type::terror;
- invd->errors = true;
- return;
- }
- if (invd->ident != Id::classInvariant &&
- invd->semanticRun < PASSsemantic &&
- !ad->isUnionDeclaration() // users are on their own with union fields
- )
- ad->invs.push(invd);
- if (!invd->type)
- invd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, invd->storage_class);
-
- sc = sc->push();
- sc->stc &= ~STCstatic; // not a static invariant
- sc->stc |= STCconst; // invariant() is always const
- sc->flags = (sc->flags & ~SCOPEcontract) | SCOPEinvariant;
- sc->linkage = LINKd;
-
- funcDeclarationSemantic(invd);
-
- sc->pop();
- }
-
- void visit(UnitTestDeclaration *utd)
- {
- if (utd->semanticRun >= PASSsemanticdone)
- return;
- if (utd->_scope)
- {
- sc = utd->_scope;
- utd->_scope = NULL;
- }
-
- utd->protection = sc->protection;
-
- utd->parent = sc->parent;
- Dsymbol *p = utd->parent->pastMixin();
- if (!p->isScopeDsymbol())
- {
- error(utd->loc, "unittest can only be a member of module/aggregate/template, not %s %s",
- p->kind(), p->toChars());
- utd->type = Type::terror;
- utd->errors = true;
- return;
- }
-
- if (global.params.useUnitTests)
- {
- if (!utd->type)
- utd->type = new TypeFunction(ParameterList(), Type::tvoid, LINKd, utd->storage_class);
- Scope *sc2 = sc->push();
- sc2->linkage = LINKd;
- funcDeclarationSemantic(utd);
- sc2->pop();
- }
- }
-
- void visit(NewDeclaration *nd)
- {
- //printf("NewDeclaration::semantic()\n");
- if (nd->semanticRun >= PASSsemanticdone)
- return;
- if (nd->_scope)
- {
- sc = nd->_scope;
- nd->_scope = NULL;
- }
-
- nd->parent = sc->parent;
- Dsymbol *p = nd->parent->pastMixin();
- if (!p->isAggregateDeclaration())
- {
- error(nd->loc, "allocator can only be a member of aggregate, not %s %s",
- p->kind(), p->toChars());
- nd->type = Type::terror;
- nd->errors = true;
- return;
- }
- Type *tret = Type::tvoid->pointerTo();
- if (!nd->type)
- nd->type = new TypeFunction(ParameterList(nd->parameters, nd->varargs), tret, LINKd, nd->storage_class);
-
- nd->type = typeSemantic(nd->type, nd->loc, sc);
-
- // Check that there is at least one argument of type size_t
- TypeFunction *tf = nd->type->toTypeFunction();
- if (tf->parameterList.length() < 1)
- {
- nd->error("at least one argument of type size_t expected");
- }
- else
- {
- Parameter *fparam = tf->parameterList[0];
- if (!fparam->type->equals(Type::tsize_t))
- nd->error("first argument must be type size_t, not %s", fparam->type->toChars());
- }
-
- funcDeclarationSemantic(nd);
- }
-
- void visit(DeleteDeclaration *deld)
- {
- //printf("DeleteDeclaration::semantic()\n");
- if (deld->semanticRun >= PASSsemanticdone)
- return;
- if (deld->_scope)
- {
- sc = deld->_scope;
- deld->_scope = NULL;
- }
-
- deld->parent = sc->parent;
- Dsymbol *p = deld->parent->pastMixin();
- if (!p->isAggregateDeclaration())
- {
- error(deld->loc, "deallocator can only be a member of aggregate, not %s %s",
- p->kind(), p->toChars());
- deld->type = Type::terror;
- deld->errors = true;
- return;
- }
- if (!deld->type)
- deld->type = new TypeFunction(ParameterList(deld->parameters), Type::tvoid, LINKd, deld->storage_class);
-
- deld->type = typeSemantic(deld->type, deld->loc, sc);
-
- // Check that there is only one argument of type void*
- TypeFunction *tf = deld->type->toTypeFunction();
- if (tf->parameterList.length() != 1)
- {
- deld->error("one argument of type void* expected");
- }
- else
- {
- Parameter *fparam = tf->parameterList[0];
- if (!fparam->type->equals(Type::tvoid->pointerTo()))
- deld->error("one argument of type void* expected, not %s", fparam->type->toChars());
- }
-
- funcDeclarationSemantic(deld);
- }
-
- void visit(StructDeclaration *sd)
- {
- //printf("StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", sd, sd->parent->toChars(), sd->toChars(), sizeok);
-
- //static int count; if (++count == 20) halt();
-
- if (sd->semanticRun >= PASSsemanticdone)
- return;
- unsigned errors = global.errors;
-
- //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", sd, sd->parent->toChars(), sd->toChars(), sizeok);
- Scope *scx = NULL;
- if (sd->_scope)
- {
- sc = sd->_scope;
- scx = sd->_scope; // save so we don't make redundant copies
- sd->_scope = NULL;
- }
-
- if (!sd->parent)
- {
- assert(sc->parent && sc->func);
- sd->parent = sc->parent;
- }
- assert(sd->parent && !sd->isAnonymous());
-
- if (sd->errors)
- sd->type = Type::terror;
- if (sd->semanticRun == PASSinit)
- sd->type = sd->type->addSTC(sc->stc | sd->storage_class);
- sd->type = typeSemantic(sd->type, sd->loc, sc);
-
- if (sd->type->ty == Tstruct && ((TypeStruct *)sd->type)->sym != sd)
- {
- TemplateInstance *ti = ((TypeStruct *)sd->type)->sym->isInstantiated();
- if (ti && isError(ti))
- ((TypeStruct *)sd->type)->sym = sd;
- }
-
- // Ungag errors when not speculative
- Ungag ungag = sd->ungagSpeculative();
-
- if (sd->semanticRun == PASSinit)
- {
- sd->protection = sc->protection;
-
- sd->alignment = sc->alignment();
-
- sd->storage_class |= sc->stc;
- if (sd->storage_class & STCdeprecated)
- sd->isdeprecated = true;
- if (sd->storage_class & STCabstract)
- sd->error("structs, unions cannot be abstract");
- sd->userAttribDecl = sc->userAttribDecl;
-
- if (sc->linkage == LINKcpp)
- sd->classKind = ClassKind::cpp;
- }
- else if (sd->symtab && !scx)
- {
- return;
- }
- sd->semanticRun = PASSsemantic;
-
- if (!sd->members) // if opaque declaration
- {
- sd->semanticRun = PASSsemanticdone;
- return;
- }
- if (!sd->symtab)
- {
- sd->symtab = new DsymbolTable();
-
- for (size_t i = 0; i < sd->members->length; i++)
- {
- Dsymbol *s = (*sd->members)[i];
- //printf("adding member '%s' to '%s'\n", s->toChars(), sd->toChars());
- s->addMember(sc, sd);
- }
- }
-
- Scope *sc2 = sd->newScope(sc);
-
- /* Set scope so if there are forward references, we still might be able to
- * resolve individual members like enums.
- */
- for (size_t i = 0; i < sd->members->length; i++)
- {
- Dsymbol *s = (*sd->members)[i];
- //printf("struct: setScope %s %s\n", s->kind(), s->toChars());
- s->setScope(sc2);
- }
-
- for (size_t i = 0; i < sd->members->length; i++)
- {
- Dsymbol *s = (*sd->members)[i];
- s->importAll(sc2);
- }
-
- for (size_t i = 0; i < sd->members->length; i++)
- {
- Dsymbol *s = (*sd->members)[i];
- dsymbolSemantic(s, sc2);
- }
-
- if (!sd->determineFields())
- {
- assert(sd->type->ty == Terror);
- sc2->pop();
- sd->semanticRun = PASSsemanticdone;
- return;
- }
-
- /* Following special member functions creation needs semantic analysis
- * completion of sub-structs in each field types. For example, buildDtor
- * needs to check existence of elaborate dtor in type of each fields.
- * See the case in compilable/test14838.d
- */
- for (size_t i = 0; i < sd->fields.length; i++)
- {
- VarDeclaration *v = sd->fields[i];
- Type *tb = v->type->baseElemOf();
- if (tb->ty != Tstruct)
- continue;
- StructDeclaration *sdec = ((TypeStruct *)tb)->sym;
- if (sdec->semanticRun >= PASSsemanticdone)
- continue;
-
- sc2->pop();
-
- sd->_scope = scx ? scx : sc->copy();
- sd->_scope->setNoFree();
- Module::addDeferredSemantic(sd);
-
- //printf("\tdeferring %s\n", sd->toChars());
- return;
- }
-
- /* Look for special member functions.
- */
- sd->aggNew = (NewDeclaration *)sd->search(Loc(), Id::classNew);
- sd->aggDelete = (DeleteDeclaration *)sd->search(Loc(), Id::classDelete);
-
- // Look for the constructor
- sd->ctor = sd->searchCtor();
-
- sd->dtor = buildDtor(sd, sc2);
- sd->postblit = buildPostBlit(sd, sc2);
-
- buildOpAssign(sd, sc2);
- buildOpEquals(sd, sc2);
-
- if (global.params.useTypeInfo && Type::dtypeinfo) // these functions are used for TypeInfo
- {
- sd->xeq = buildXopEquals(sd, sc2);
- sd->xcmp = buildXopCmp(sd, sc2);
- sd->xhash = buildXtoHash(sd, sc2);
- }
-
- sd->inv = buildInv(sd, sc2);
-
- Module::dprogress++;
- sd->semanticRun = PASSsemanticdone;
- //printf("-StructDeclaration::semantic(this=%p, '%s')\n", sd, sd->toChars());
-
- sc2->pop();
-
- if (sd->ctor)
- {
- Dsymbol *scall = sd->search(Loc(), Id::call);
- if (scall)
- {
- unsigned xerrors = global.startGagging();
- sc = sc->push();
- sc->tinst = NULL;
- sc->minst = NULL;
- FuncDeclaration *fcall = resolveFuncCall(sd->loc, sc, scall, NULL, NULL, NULL, 1);
- sc = sc->pop();
- global.endGagging(xerrors);
-
- if (fcall && fcall->isStatic())
- {
- sd->error(fcall->loc, "`static opCall` is hidden by constructors and can never be called");
- errorSupplemental(fcall->loc, "Please use a factory method instead, or replace all constructors with `static opCall`.");
- }
- }
- }
-
- if (sd->type->ty == Tstruct && ((TypeStruct *)sd->type)->sym != sd)
- {
- // https://issues.dlang.org/show_bug.cgi?id=19024
- StructDeclaration *sym = ((TypeStruct *)sd->type)->sym;
- sd->error("already exists at %s. Perhaps in another function with the same name?", sym->loc.toChars());
- }
-
- if (global.errors != errors)
- {
- // The type is no good.
- sd->type = Type::terror;
- sd->errors = true;
- if (sd->deferred)
- sd->deferred->errors = true;
- }
-
- if (sd->deferred && !global.gag)
- {
- semantic2(sd->deferred, sc);
- semantic3(sd->deferred, sc);
- }
- }
-
- void interfaceSemantic(ClassDeclaration *cd)
- {
- cd->vtblInterfaces = new BaseClasses();
- cd->vtblInterfaces->reserve(cd->interfaces.length);
-
- for (size_t i = 0; i < cd->interfaces.length; i++)
- {
- BaseClass *b = cd->interfaces.ptr[i];
- cd->vtblInterfaces->push(b);
- b->copyBaseInterfaces(cd->vtblInterfaces);
- }
- }
-
- void visit(ClassDeclaration *cldec)
- {
- //printf("ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", cldec->toChars(), cldec->type, sizeok, cldec);
- //printf("\tparent = %p, '%s'\n", sc->parent, sc->parent ? sc->parent->toChars() : "");
- //printf("sc->stc = %x\n", sc->stc);
-
- //{ static int n; if (++n == 20) *(char*)0=0; }
-
- if (cldec->semanticRun >= PASSsemanticdone)
- return;
- unsigned errors = global.errors;
-
- //printf("+ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", cldec->toChars(), cldec->type, sizeok, cldec);
-
- Scope *scx = NULL;
- if (cldec->_scope)
- {
- sc = cldec->_scope;
- scx = cldec->_scope; // save so we don't make redundant copies
- cldec->_scope = NULL;
- }
-
- if (!cldec->parent)
- {
- assert(sc->parent);
- cldec->parent = sc->parent;
- }
-
- if (cldec->errors)
- cldec->type = Type::terror;
- cldec->type = typeSemantic(cldec->type, cldec->loc, sc);
-
- if (cldec->type->ty == Tclass && ((TypeClass *)cldec->type)->sym != cldec)
- {
- TemplateInstance *ti = ((TypeClass *)cldec->type)->sym->isInstantiated();
- if (ti && isError(ti))
- ((TypeClass *)cldec->type)->sym = cldec;
- }
-
- // Ungag errors when not speculative
- Ungag ungag = cldec->ungagSpeculative();
-
- if (cldec->semanticRun == PASSinit)
- {
- cldec->protection = sc->protection;
-
- cldec->storage_class |= sc->stc;
- if (cldec->storage_class & STCdeprecated)
- cldec->isdeprecated = true;
- if (cldec->storage_class & STCauto)
- cldec->error("storage class `auto` is invalid when declaring a class, did you mean to use `scope`?");
- if (cldec->storage_class & STCscope)
- cldec->isscope = true;
- if (cldec->storage_class & STCabstract)
- cldec->isabstract = ABSyes;
-
- cldec->userAttribDecl = sc->userAttribDecl;
-
- if (sc->linkage == LINKcpp)
- cldec->classKind = ClassKind::cpp;
- if (sc->linkage == LINKobjc)
- objc()->setObjc(cldec);
- }
- else if (cldec->symtab && !scx)
- {
- return;
- }
- cldec->semanticRun = PASSsemantic;
-
- if (cldec->baseok < BASEOKdone)
- {
- cldec->baseok = BASEOKin;
-
- // Expand any tuples in baseclasses[]
- for (size_t i = 0; i < cldec->baseclasses->length; )
- {
- BaseClass *b = (*cldec->baseclasses)[i];
- b->type = resolveBase(cldec, sc, scx, b->type);
-
- Type *tb = b->type->toBasetype();
- if (tb->ty == Ttuple)
- {
- TypeTuple *tup = (TypeTuple *)tb;
- cldec->baseclasses->remove(i);
- size_t dim = Parameter::dim(tup->arguments);
- for (size_t j = 0; j < dim; j++)
- {
- Parameter *arg = Parameter::getNth(tup->arguments, j);
- b = new BaseClass(arg->type);
- cldec->baseclasses->insert(i + j, b);
- }
- }
- else
- i++;
- }
-
- if (cldec->baseok >= BASEOKdone)
- {
- //printf("%s already semantic analyzed, semanticRun = %d\n", cldec->toChars(), cldec->semanticRun);
- if (cldec->semanticRun >= PASSsemanticdone)
- return;
- goto Lancestorsdone;
- }
-
- // See if there's a base class as first in baseclasses[]
- if (cldec->baseclasses->length)
- {
- BaseClass *b = (*cldec->baseclasses)[0];
- Type *tb = b->type->toBasetype();
- TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL;
- if (!tc)
- {
- if (b->type != Type::terror)
- cldec->error("base type must be class or interface, not %s", b->type->toChars());
- cldec->baseclasses->remove(0);
- goto L7;
- }
-
- if (tc->sym->isDeprecated())
- {
- if (!cldec->isDeprecated())
- {
- // Deriving from deprecated class makes this one deprecated too
- cldec->isdeprecated = true;
-
- tc->checkDeprecated(cldec->loc, sc);
- }
- }
-
- if (tc->sym->isInterfaceDeclaration())
- goto L7;
-
- for (ClassDeclaration *cdb = tc->sym; cdb; cdb = cdb->baseClass)
- {
- if (cdb == cldec)
- {
- cldec->error("circular inheritance");
- cldec->baseclasses->remove(0);
- goto L7;
- }
- }
-
- /* Bugzilla 11034: Essentially, class inheritance hierarchy
- * and instance size of each classes are orthogonal information.
- * Therefore, even if tc->sym->sizeof == SIZEOKnone,
- * we need to set baseClass field for class covariance check.
- */
- cldec->baseClass = tc->sym;
- b->sym = cldec->baseClass;
-
- if (tc->sym->baseok < BASEOKdone)
- resolveBase(cldec, sc, scx, tc->sym); // Try to resolve forward reference
- if (tc->sym->baseok < BASEOKdone)
- {
- //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars());
- if (tc->sym->_scope)
- Module::addDeferredSemantic(tc->sym);
- cldec->baseok = BASEOKnone;
- }
- L7: ;
- }
-
- // Treat the remaining entries in baseclasses as interfaces
- // Check for errors, handle forward references
- for (size_t i = (cldec->baseClass ? 1 : 0); i < cldec->baseclasses->length; )
- {
- BaseClass *b = (*cldec->baseclasses)[i];
- Type *tb = b->type->toBasetype();
- TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL;
- if (!tc || !tc->sym->isInterfaceDeclaration())
- {
- if (b->type != Type::terror)
- cldec->error("base type must be interface, not %s", b->type->toChars());
- cldec->baseclasses->remove(i);
- continue;
- }
-
- // Check for duplicate interfaces
- for (size_t j = (cldec->baseClass ? 1 : 0); j < i; j++)
- {
- BaseClass *b2 = (*cldec->baseclasses)[j];
- if (b2->sym == tc->sym)
- {
- cldec->error("inherits from duplicate interface %s", b2->sym->toChars());
- cldec->baseclasses->remove(i);
- continue;
- }
- }
-
- if (tc->sym->isDeprecated())
- {
- if (!cldec->isDeprecated())
- {
- // Deriving from deprecated class makes this one deprecated too
- cldec->isdeprecated = true;
-
- tc->checkDeprecated(cldec->loc, sc);
- }
- }
-
- b->sym = tc->sym;
-
- if (tc->sym->baseok < BASEOKdone)
- resolveBase(cldec, sc, scx, tc->sym); // Try to resolve forward reference
- if (tc->sym->baseok < BASEOKdone)
- {
- //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars());
- if (tc->sym->_scope)
- Module::addDeferredSemantic(tc->sym);
- cldec->baseok = BASEOKnone;
- }
- i++;
- }
- if (cldec->baseok == BASEOKnone)
- {
- // Forward referencee of one or more bases, try again later
- cldec->_scope = scx ? scx : sc->copy();
- cldec->_scope->setNoFree();
- Module::addDeferredSemantic(cldec);
- //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, cldec->toChars());
- return;
- }
- cldec->baseok = BASEOKdone;
-
- // If no base class, and this is not an Object, use Object as base class
- if (!cldec->baseClass && cldec->ident != Id::Object && !cldec->isCPPclass())
- {
- if (!ClassDeclaration::object || ClassDeclaration::object->errors)
- badObjectDotD(cldec);
-
- Type *t = ClassDeclaration::object->type;
- t = typeSemantic(t, cldec->loc, sc)->toBasetype();
- if (t->ty == Terror)
- badObjectDotD(cldec);
- assert(t->ty == Tclass);
- TypeClass *tc = (TypeClass *)t;
-
- BaseClass *b = new BaseClass(tc);
- cldec->baseclasses->shift(b);
-
- cldec->baseClass = tc->sym;
- assert(!cldec->baseClass->isInterfaceDeclaration());
- b->sym = cldec->baseClass;
- }
- if (cldec->baseClass)
- {
- if (cldec->baseClass->storage_class & STCfinal)
- cldec->error("cannot inherit from final class %s", cldec->baseClass->toChars());
-
- // Inherit properties from base class
- if (cldec->baseClass->isCOMclass())
- cldec->com = true;
- if (cldec->baseClass->isCPPclass())
- cldec->classKind = ClassKind::cpp;
- if (cldec->baseClass->isscope)
- cldec->isscope = true;
- cldec->enclosing = cldec->baseClass->enclosing;
- cldec->storage_class |= cldec->baseClass->storage_class & STC_TYPECTOR;
- }
-
- cldec->interfaces.length = cldec->baseclasses->length - (cldec->baseClass ? 1 : 0);
- cldec->interfaces.ptr = cldec->baseclasses->tdata() + (cldec->baseClass ? 1 : 0);
-
- for (size_t i = 0; i < cldec->interfaces.length; i++)
- {
- BaseClass *b = cldec->interfaces.ptr[i];
- // If this is an interface, and it derives from a COM interface,
- // then this is a COM interface too.
- if (b->sym->isCOMinterface())
- cldec->com = true;
- if (cldec->isCPPclass() && !b->sym->isCPPinterface())
- {
- error(cldec->loc, "C++ class `%s` cannot implement D interface `%s`",
- cldec->toPrettyChars(), b->sym->toPrettyChars());
- }
- }
-
- interfaceSemantic(cldec);
- }
- Lancestorsdone:
- //printf("\tClassDeclaration::semantic(%s) baseok = %d\n", cldec->toChars(), cldec->baseok);
-
- if (!cldec->members) // if opaque declaration
- {
- cldec->semanticRun = PASSsemanticdone;
- return;
- }
- if (!cldec->symtab)
- {
- cldec->symtab = new DsymbolTable();
-
- /* Bugzilla 12152: The semantic analysis of base classes should be finished
- * before the members semantic analysis of this class, in order to determine
- * vtbl in this class. However if a base class refers the member of this class,
- * it can be resolved as a normal forward reference.
- * Call addMember() and setScope() to make this class members visible from the base classes.
- */
- for (size_t i = 0; i < cldec->members->length; i++)
- {
- Dsymbol *s = (*cldec->members)[i];
- s->addMember(sc, cldec);
- }
-
- Scope *sc2 = cldec->newScope(sc);
-
- /* Set scope so if there are forward references, we still might be able to
- * resolve individual members like enums.
- */
- for (size_t i = 0; i < cldec->members->length; i++)
- {
- Dsymbol *s = (*cldec->members)[i];
- //printf("[%d] setScope %s %s, sc2 = %p\n", i, s->kind(), s->toChars(), sc2);
- s->setScope(sc2);
- }
-
- sc2->pop();
- }
-
- for (size_t i = 0; i < cldec->baseclasses->length; i++)
- {
- BaseClass *b = (*cldec->baseclasses)[i];
- Type *tb = b->type->toBasetype();
- assert(tb->ty == Tclass);
- TypeClass *tc = (TypeClass *)tb;
-
- if (tc->sym->semanticRun < PASSsemanticdone)
- {
- // Forward referencee of one or more bases, try again later
- cldec->_scope = scx ? scx : sc->copy();
- cldec->_scope->setNoFree();
- if (tc->sym->_scope)
- Module::addDeferredSemantic(tc->sym);
- Module::addDeferredSemantic(cldec);
- //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, cldec->toChars());
- return;
- }
- }
-
- if (cldec->baseok == BASEOKdone)
- {
- cldec->baseok = BASEOKsemanticdone;
-
- // initialize vtbl
- if (cldec->baseClass)
- {
- if (cldec->isCPPclass() && cldec->baseClass->vtbl.length == 0)
- {
- cldec->error("C++ base class %s needs at least one virtual function", cldec->baseClass->toChars());
- }
-
- // Copy vtbl[] from base class
- cldec->vtbl.setDim(cldec->baseClass->vtbl.length);
- memcpy(cldec->vtbl.tdata(), cldec->baseClass->vtbl.tdata(), sizeof(void *) * cldec->vtbl.length);
-
- cldec->vthis = cldec->baseClass->vthis;
- }
- else
- {
- // No base class, so this is the root of the class hierarchy
- cldec->vtbl.setDim(0);
- if (cldec->vtblOffset())
- cldec->vtbl.push(cldec); // leave room for classinfo as first member
- }
-
- /* If this is a nested class, add the hidden 'this'
- * member which is a pointer to the enclosing scope.
- */
- if (cldec->vthis) // if inheriting from nested class
- {
- // Use the base class's 'this' member
- if (cldec->storage_class & STCstatic)
- cldec->error("static class cannot inherit from nested class %s", cldec->baseClass->toChars());
- if (cldec->toParent2() != cldec->baseClass->toParent2() &&
- (!cldec->toParent2() ||
- !cldec->baseClass->toParent2()->getType() ||
- !cldec->baseClass->toParent2()->getType()->isBaseOf(cldec->toParent2()->getType(), NULL)))
- {
- if (cldec->toParent2())
- {
- cldec->error("is nested within %s, but super class %s is nested within %s",
- cldec->toParent2()->toChars(),
- cldec->baseClass->toChars(),
- cldec->baseClass->toParent2()->toChars());
- }
- else
- {
- cldec->error("is not nested, but super class %s is nested within %s",
- cldec->baseClass->toChars(),
- cldec->baseClass->toParent2()->toChars());
- }
- cldec->enclosing = NULL;
- }
- }
- else
- cldec->makeNested();
- }
-
- Scope *sc2 = cldec->newScope(sc);
-
- for (size_t i = 0; i < cldec->members->length; i++)
- {
- Dsymbol *s = (*cldec->members)[i];
- s->importAll(sc2);
- }
-
- // Note that members.length can grow due to tuple expansion during semantic()
- for (size_t i = 0; i < cldec->members->length; i++)
- {
- Dsymbol *s = (*cldec->members)[i];
- dsymbolSemantic(s, sc2);
- }
-
- if (!cldec->determineFields())
- {
- assert(cldec->type == Type::terror);
- sc2->pop();
- return;
- }
-
- /* Following special member functions creation needs semantic analysis
- * completion of sub-structs in each field types.
- */
- for (size_t i = 0; i < cldec->fields.length; i++)
- {
- VarDeclaration *v = cldec->fields[i];
- Type *tb = v->type->baseElemOf();
- if (tb->ty != Tstruct)
- continue;
- StructDeclaration *sd = ((TypeStruct *)tb)->sym;
- if (sd->semanticRun >= PASSsemanticdone)
- continue;
-
- sc2->pop();
-
- cldec->_scope = scx ? scx : sc->copy();
- cldec->_scope->setNoFree();
- Module::addDeferredSemantic(cldec);
- //printf("\tdeferring %s\n", cldec->toChars());
- return;
- }
-
- /* Look for special member functions.
- * They must be in this class, not in a base class.
- */
-
- // Can be in base class
- cldec->aggNew = (NewDeclaration *)cldec->search(Loc(), Id::classNew);
- cldec->aggDelete = (DeleteDeclaration *)cldec->search(Loc(), Id::classDelete);
-
- // Look for the constructor
- cldec->ctor = cldec->searchCtor();
-
- if (!cldec->ctor && cldec->noDefaultCtor)
- {
- // A class object is always created by constructor, so this check is legitimate.
- for (size_t i = 0; i < cldec->fields.length; i++)
- {
- VarDeclaration *v = cldec->fields[i];
- if (v->storage_class & STCnodefaultctor)
- error(v->loc, "field %s must be initialized in constructor", v->toChars());
- }
- }
-
- // If this class has no constructor, but base class has a default
- // ctor, create a constructor:
- // this() { }
- if (!cldec->ctor && cldec->baseClass && cldec->baseClass->ctor)
- {
- FuncDeclaration *fd = resolveFuncCall(cldec->loc, sc2, cldec->baseClass->ctor, NULL, cldec->type, NULL, 1);
- if (!fd) // try shared base ctor instead
- fd = resolveFuncCall(cldec->loc, sc2, cldec->baseClass->ctor, NULL, cldec->type->sharedOf(), NULL, 1);
- if (fd && !fd->errors)
- {
- //printf("Creating default this(){} for class %s\n", cldec->toChars());
- TypeFunction *btf = fd->type->toTypeFunction();
- TypeFunction *tf = new TypeFunction(ParameterList(), NULL, LINKd, fd->storage_class);
- tf->mod = btf->mod;
- tf->purity = btf->purity;
- tf->isnothrow = btf->isnothrow;
- tf->isnogc = btf->isnogc;
- tf->trust = btf->trust;
-
- CtorDeclaration *ctor = new CtorDeclaration(cldec->loc, Loc(), 0, tf);
- ctor->fbody = new CompoundStatement(Loc(), new Statements());
-
- cldec->members->push(ctor);
- ctor->addMember(sc, cldec);
- dsymbolSemantic(ctor, sc2);
-
- cldec->ctor = ctor;
- cldec->defaultCtor = ctor;
- }
- else
- {
- cldec->error("cannot implicitly generate a default ctor when base class %s is missing a default ctor",
- cldec->baseClass->toPrettyChars());
- }
- }
-
- cldec->dtor = buildDtor(cldec, sc2);
-
- if (FuncDeclaration *f = hasIdentityOpAssign(cldec, sc2))
- {
- if (!(f->storage_class & STCdisable))
- cldec->error(f->loc, "identity assignment operator overload is illegal");
- }
-
- cldec->inv = buildInv(cldec, sc2);
-
- Module::dprogress++;
- cldec->semanticRun = PASSsemanticdone;
- //printf("-ClassDeclaration.semantic(%s), type = %p\n", cldec->toChars(), cldec->type);
- //members.print();
-
- sc2->pop();
-
- if (cldec->type->ty == Tclass && ((TypeClass *)cldec->type)->sym != cldec)
- {
- // https://issues.dlang.org/show_bug.cgi?id=17492
- ClassDeclaration *cd = ((TypeClass *)cldec->type)->sym;
- cldec->error("already exists at %s. Perhaps in another function with the same name?", cd->loc.toChars());
- }
-
- if (global.errors != errors)
- {
- // The type is no good.
- cldec->type = Type::terror;
- cldec->errors = true;
- if (cldec->deferred)
- cldec->deferred->errors = true;
- }
-
- // Verify fields of a synchronized class are not public
- if (cldec->storage_class & STCsynchronized)
- {
- for (size_t i = 0; i < cldec->fields.length; i++)
- {
- VarDeclaration *vd = cldec->fields[i];
- if (!vd->isThisDeclaration() &&
- !vd->prot().isMoreRestrictiveThan(Prot(Prot::public_)))
- {
- vd->error("Field members of a synchronized class cannot be %s",
- protectionToChars(vd->prot().kind));
- }
- }
- }
-
- if (cldec->deferred && !global.gag)
- {
- semantic2(cldec->deferred, sc);
- semantic3(cldec->deferred, sc);
- }
- //printf("-ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", cldec->toChars(), cldec->type, sizeok, cldec);
- }
-
- void visit(InterfaceDeclaration *idec)
- {
- //printf("InterfaceDeclaration::semantic(%s), type = %p\n", idec->toChars(), idec->type);
- if (idec->semanticRun >= PASSsemanticdone)
- return;
- unsigned errors = global.errors;
-
- //printf("+InterfaceDeclaration.semantic(%s), type = %p\n", idec->toChars(), idec->type);
-
- Scope *scx = NULL;
- if (idec->_scope)
- {
- sc = idec->_scope;
- scx = idec->_scope; // save so we don't make redundant copies
- idec->_scope = NULL;
- }
-
- if (!idec->parent)
- {
- assert(sc->parent && sc->func);
- idec->parent = sc->parent;
- }
- assert(idec->parent && !idec->isAnonymous());
-
- if (idec->errors)
- idec->type = Type::terror;
- idec->type = typeSemantic(idec->type, idec->loc, sc);
-
- if (idec->type->ty == Tclass && ((TypeClass *)idec->type)->sym != idec)
- {
- TemplateInstance *ti = ((TypeClass *)idec->type)->sym->isInstantiated();
- if (ti && isError(ti))
- ((TypeClass *)idec->type)->sym = idec;
- }
-
- // Ungag errors when not speculative
- Ungag ungag = idec->ungagSpeculative();
-
- if (idec->semanticRun == PASSinit)
- {
- idec->protection = sc->protection;
-
- idec->storage_class |= sc->stc;
- if (idec->storage_class & STCdeprecated)
- idec->isdeprecated = true;
-
- idec->userAttribDecl = sc->userAttribDecl;
- }
- else if (idec->symtab)
- {
- if (idec->sizeok == SIZEOKdone || !scx)
- {
- idec->semanticRun = PASSsemanticdone;
- return;
- }
- }
- idec->semanticRun = PASSsemantic;
-
- if (idec->baseok < BASEOKdone)
- {
- idec->baseok = BASEOKin;
-
- // Expand any tuples in baseclasses[]
- for (size_t i = 0; i < idec->baseclasses->length; )
- {
- BaseClass *b = (*idec->baseclasses)[i];
- b->type = resolveBase(idec, sc, scx, b->type);
-
- Type *tb = b->type->toBasetype();
- if (tb->ty == Ttuple)
- {
- TypeTuple *tup = (TypeTuple *)tb;
- idec->baseclasses->remove(i);
- size_t dim = Parameter::dim(tup->arguments);
- for (size_t j = 0; j < dim; j++)
- {
- Parameter *arg = Parameter::getNth(tup->arguments, j);
- b = new BaseClass(arg->type);
- idec->baseclasses->insert(i + j, b);
- }
- }
- else
- i++;
- }
-
- if (idec->baseok >= BASEOKdone)
- {
- //printf("%s already semantic analyzed, semanticRun = %d\n", idec->toChars(), idec->semanticRun);
- if (idec->semanticRun >= PASSsemanticdone)
- return;
- goto Lancestorsdone;
- }
-
- if (!idec->baseclasses->length && sc->linkage == LINKcpp)
- idec->classKind = ClassKind::cpp;
- if (sc->linkage == LINKobjc)
- objc()->setObjc(idec);
-
- // Check for errors, handle forward references
- for (size_t i = 0; i < idec->baseclasses->length; )
- {
- BaseClass *b = (*idec->baseclasses)[i];
- Type *tb = b->type->toBasetype();
- TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL;
- if (!tc || !tc->sym->isInterfaceDeclaration())
- {
- if (b->type != Type::terror)
- idec->error("base type must be interface, not %s", b->type->toChars());
- idec->baseclasses->remove(i);
- continue;
- }
-
- // Check for duplicate interfaces
- for (size_t j = 0; j < i; j++)
- {
- BaseClass *b2 = (*idec->baseclasses)[j];
- if (b2->sym == tc->sym)
- {
- idec->error("inherits from duplicate interface %s", b2->sym->toChars());
- idec->baseclasses->remove(i);
- continue;
- }
- }
-
- if (tc->sym == idec || idec->isBaseOf2(tc->sym))
- {
- idec->error("circular inheritance of interface");
- idec->baseclasses->remove(i);
- continue;
- }
-
- if (tc->sym->isDeprecated())
- {
- if (!idec->isDeprecated())
- {
- // Deriving from deprecated class makes this one deprecated too
- idec->isdeprecated = true;
-
- tc->checkDeprecated(idec->loc, sc);
- }
- }
-
- b->sym = tc->sym;
-
- if (tc->sym->baseok < BASEOKdone)
- resolveBase(idec, sc, scx, tc->sym); // Try to resolve forward reference
- if (tc->sym->baseok < BASEOKdone)
- {
- //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars());
- if (tc->sym->_scope)
- Module::addDeferredSemantic(tc->sym);
- idec->baseok = BASEOKnone;
- }
- i++;
- }
- if (idec->baseok == BASEOKnone)
- {
- // Forward referencee of one or more bases, try again later
- idec->_scope = scx ? scx : sc->copy();
- idec->_scope->setNoFree();
- Module::addDeferredSemantic(idec);
- return;
- }
- idec->baseok = BASEOKdone;
-
- idec->interfaces.length = idec->baseclasses->length;
- idec->interfaces.ptr = idec->baseclasses->tdata();
-
- for (size_t i = 0; i < idec->interfaces.length; i++)
- {
- BaseClass *b = idec->interfaces.ptr[i];
- // If this is an interface, and it derives from a COM interface,
- // then this is a COM interface too.
- if (b->sym->isCOMinterface())
- idec->com = true;
- if (b->sym->isCPPinterface())
- idec->classKind = ClassKind::cpp;
- }
-
- interfaceSemantic(idec);
- }
- Lancestorsdone:
-
- if (!idec->members) // if opaque declaration
- {
- idec->semanticRun = PASSsemanticdone;
- return;
- }
- if (!idec->symtab)
- idec->symtab = new DsymbolTable();
-
- for (size_t i = 0; i < idec->baseclasses->length; i++)
- {
- BaseClass *b = (*idec->baseclasses)[i];
- Type *tb = b->type->toBasetype();
- assert(tb->ty == Tclass);
- TypeClass *tc = (TypeClass *)tb;
-
- if (tc->sym->semanticRun < PASSsemanticdone)
- {
- // Forward referencee of one or more bases, try again later
- idec->_scope = scx ? scx : sc->copy();
- idec->_scope->setNoFree();
- if (tc->sym->_scope)
- Module::addDeferredSemantic(tc->sym);
- Module::addDeferredSemantic(idec);
- return;
- }
- }
-
- if (idec->baseok == BASEOKdone)
- {
- idec->baseok = BASEOKsemanticdone;
-
- // initialize vtbl
- if (idec->vtblOffset())
- idec->vtbl.push(idec); // leave room at vtbl[0] for classinfo
-
- // Cat together the vtbl[]'s from base cldec->interfaces
- for (size_t i = 0; i < idec->interfaces.length; i++)
- {
- BaseClass *b = idec->interfaces.ptr[i];
-
- // Skip if b has already appeared
- for (size_t k = 0; k < i; k++)
- {
- if (b == idec->interfaces.ptr[k])
- goto Lcontinue;
- }
-
- // Copy vtbl[] from base class
- if (b->sym->vtblOffset())
- {
- size_t d = b->sym->vtbl.length;
- if (d > 1)
- {
- idec->vtbl.reserve(d - 1);
- for (size_t j = 1; j < d; j++)
- idec->vtbl.push(b->sym->vtbl[j]);
- }
- }
- else
- {
- idec->vtbl.append(&b->sym->vtbl);
- }
-
- Lcontinue:
- ;
- }
- }
-
- for (size_t i = 0; i < idec->members->length; i++)
- {
- Dsymbol *s = (*idec->members)[i];
- s->addMember(sc, idec);
- }
-
- Scope *sc2 = idec->newScope(sc);
-
- /* Set scope so if there are forward references, we still might be able to
- * resolve individual members like enums.
- */
- for (size_t i = 0; i < idec->members->length; i++)
- {
- Dsymbol *s = (*idec->members)[i];
- //printf("setScope %s %s\n", s->kind(), s->toChars());
- s->setScope(sc2);
- }
-
- for (size_t i = 0; i < idec->members->length; i++)
- {
- Dsymbol *s = (*idec->members)[i];
- s->importAll(sc2);
- }
-
- for (size_t i = 0; i < idec->members->length; i++)
- {
- Dsymbol *s = (*idec->members)[i];
- dsymbolSemantic(s, sc2);
- }
-
- Module::dprogress++;
- idec->semanticRun = PASSsemanticdone;
- //printf("-InterfaceDeclaration.semantic(%s), type = %p\n", idec->toChars(), idec->type);
- //members->print();
-
- sc2->pop();
-
- if (global.errors != errors)
- {
- // The type is no good.
- idec->type = Type::terror;
- }
-
- assert(idec->type->ty != Tclass || ((TypeClass *)idec->type)->sym == idec);
- }
-};
-
-/******************************************************
- * Do template instance semantic for isAliasSeq templates.
- * This is a greatly simplified version of TemplateInstance::semantic().
- */
-static void aliasSeqInstanceSemantic(TemplateInstance *tempinst, Scope *sc, TemplateDeclaration *tempdecl)
-{
- //printf("[%s] aliasSeqInstanceSemantic('%s')\n", tempinst->loc.toChars(), tempinst->toChars());
- Scope *paramscope = sc->push();
- paramscope->stc = 0;
- paramscope->protection = Prot(Prot::public_);
-
- TemplateTupleParameter *ttp = (*tempdecl->parameters)[0]->isTemplateTupleParameter();
- Tuple *va = isTuple(tempinst->tdtypes[0]);
- Declaration *d = new TupleDeclaration(tempinst->loc, ttp->ident, &va->objects);
- d->storage_class |= STCtemplateparameter;
- dsymbolSemantic(d, sc);
-
- paramscope->pop();
-
- tempinst->aliasdecl = d;
-
- tempinst->semanticRun = PASSsemanticdone;
-}
-
-/******************************************************
- * Do template instance semantic for isAlias templates.
- * This is a greatly simplified version of TemplateInstance::semantic().
- */
-static void aliasInstanceSemantic(TemplateInstance *tempinst, Scope *sc, TemplateDeclaration *tempdecl)
-{
- //printf("[%s] aliasInstanceSemantic('%s')\n", tempinst->loc.toChars(), tempinst->toChars());
- Scope *paramscope = sc->push();
- paramscope->stc = 0;
- paramscope->protection = Prot(Prot::public_);
-
- TemplateTypeParameter *ttp = (*tempdecl->parameters)[0]->isTemplateTypeParameter();
- Type *ta = isType(tempinst->tdtypes[0]);
- AliasDeclaration *ad = tempdecl->onemember->isAliasDeclaration();
-
- // Note: qualifiers can be in both 'ad.type.mod' and 'ad.storage_class'
- Declaration *d = new AliasDeclaration(tempinst->loc, ttp->ident, ta->addMod(ad->type->mod));
- d->storage_class |= STCtemplateparameter | ad->storage_class;
- dsymbolSemantic(d, sc);
-
- paramscope->pop();
-
- tempinst->aliasdecl = d;
-
- tempinst->semanticRun = PASSsemanticdone;
-}
-
-void templateInstanceSemantic(TemplateInstance *tempinst, Scope *sc, Expressions *fargs)
-{
- //printf("[%s] TemplateInstance::semantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst->loc.toChars(), tempinst->toChars(), tempinst, global.gag, sc);
- if (tempinst->inst) // if semantic() was already run
- {
- return;
- }
- if (tempinst->semanticRun != PASSinit)
- {
- Ungag ungag(global.gag);
- if (!tempinst->gagged)
- global.gag = 0;
- tempinst->error(tempinst->loc, "recursive template expansion");
- if (tempinst->gagged)
- tempinst->semanticRun = PASSinit;
- else
- tempinst->inst = tempinst;
- tempinst->errors = true;
- return;
- }
-
- // Get the enclosing template instance from the scope tinst
- tempinst->tinst = sc->tinst;
-
- // Get the instantiating module from the scope minst
- tempinst->minst = sc->minst;
- // Bugzilla 10920: If the enclosing function is non-root symbol,
- // this instance should be speculative.
- if (!tempinst->tinst && sc->func && sc->func->inNonRoot())
- {
- tempinst->minst = NULL;
- }
-
- tempinst->gagged = (global.gag > 0);
-
- tempinst->semanticRun = PASSsemantic;
-
- /* Find template declaration first,
- * then run semantic on each argument (place results in tiargs[]),
- * last find most specialized template from overload list/set.
- */
- if (!tempinst->findTempDecl(sc, NULL) ||
- !tempinst->semanticTiargs(sc) ||
- !tempinst->findBestMatch(sc, fargs))
- {
-Lerror:
- if (tempinst->gagged)
- {
- // Bugzilla 13220: Rollback status for later semantic re-running.
- tempinst->semanticRun = PASSinit;
- }
- else
- tempinst->inst = tempinst;
- tempinst->errors = true;
- return;
- }
- TemplateDeclaration *tempdecl = tempinst->tempdecl->isTemplateDeclaration();
- assert(tempdecl);
-
- // If tempdecl is a mixin, disallow it
- if (tempdecl->ismixin)
- {
- tempinst->error("mixin templates are not regular templates");
- goto Lerror;
- }
-
- tempinst->hasNestedArgs(tempinst->tiargs, tempdecl->isstatic);
- if (tempinst->errors)
- goto Lerror;
-
- /* Greatly simplified semantic processing for AliasSeq templates
- */
- if (tempdecl->isTrivialAliasSeq)
- {
- tempinst->inst = tempinst;
- return aliasSeqInstanceSemantic(tempinst, sc, tempdecl);
- }
- /* Greatly simplified semantic processing for Alias templates
- */
- else if (tempdecl->isTrivialAlias)
- {
- tempinst->inst = tempinst;
- return aliasInstanceSemantic(tempinst, sc, tempdecl);
- }
-
- /* See if there is an existing TemplateInstantiation that already
- * implements the typeargs. If so, just refer to that one instead.
- */
- tempinst->inst = tempdecl->findExistingInstance(tempinst, fargs);
- TemplateInstance *errinst = NULL;
- if (!tempinst->inst)
- {
- // So, we need to implement 'this' instance.
- }
- else if (tempinst->inst->gagged && !tempinst->gagged && tempinst->inst->errors)
- {
- // If the first instantiation had failed, re-run semantic,
- // so that error messages are shown.
- errinst = tempinst->inst;
- }
- else
- {
- // It's a match
- tempinst->parent = tempinst->inst->parent;
- tempinst->errors = tempinst->inst->errors;
-
- // If both this and the previous instantiation were gagged,
- // use the number of errors that happened last time.
- global.errors += tempinst->errors;
- global.gaggedErrors += tempinst->errors;
-
- // If the first instantiation was gagged, but this is not:
- if (tempinst->inst->gagged)
- {
- // It had succeeded, mark it is a non-gagged instantiation,
- // and reuse it.
- tempinst->inst->gagged = tempinst->gagged;
- }
-
- tempinst->tnext = tempinst->inst->tnext;
- tempinst->inst->tnext = tempinst;
-
- /* A module can have explicit template instance and its alias
- * in module scope (e,g, `alias Base64 = Base64Impl!('+', '/');`).
- * If the first instantiation 'inst' had happened in non-root module,
- * compiler can assume that its instantiated code would be included
- * in the separately compiled obj/lib file (e.g. phobos.lib).
- *
- * However, if 'this' second instantiation happened in root module,
- * compiler might need to invoke its codegen (Bugzilla 2500 & 2644).
- * But whole import graph is not determined until all semantic pass finished,
- * so 'inst' should conservatively finish the semantic3 pass for the codegen.
- */
- if (tempinst->minst && tempinst->minst->isRoot() && !(tempinst->inst->minst && tempinst->inst->minst->isRoot()))
- {
- /* Swap the position of 'inst' and 'this' in the instantiation graph.
- * Then, the primary instance `inst` will be changed to a root instance,
- * along with all members of `inst` having their scopes updated.
- *
- * Before:
- * non-root -> A!() -> B!()[inst] -> C!() { members[non-root] }
- * |
- * root -> D!() -> B!()[this]
- *
- * After:
- * non-root -> A!() -> B!()[this]
- * |
- * root -> D!() -> B!()[inst] -> C!() { members[root] }
- */
- Module *mi = tempinst->minst;
- TemplateInstance *ti = tempinst->tinst;
- tempinst->minst = tempinst->inst->minst;
- tempinst->tinst = tempinst->inst->tinst;
- tempinst->inst->minst = mi;
- tempinst->inst->tinst = ti;
-
- /* https://issues.dlang.org/show_bug.cgi?id=21299
- `minst` has been updated on the primary instance `inst` so it is
- now coming from a root module, however all Dsymbol `inst.members`
- of the instance still have their `_scope.minst` pointing at the
- original non-root module. We must now propagate `minst` to all
- members so that forward referenced dependencies that get
- instantiated will also be appended to the root module, otherwise
- there will be undefined references at link-time. */
- class InstMemberWalker : public Visitor
- {
- public:
- TemplateInstance *inst;
-
- InstMemberWalker(TemplateInstance *inst)
- : inst(inst) { }
-
- void visit(Dsymbol *d)
- {
- if (d->_scope)
- d->_scope->minst = inst->minst;
- }
-
- void visit(ScopeDsymbol *sds)
- {
- if (!sds->members)
- return;
- for (size_t i = 0; i < sds->members->length; i++)
- {
- Dsymbol *s = (*sds->members)[i];
- s->accept(this);
- }
- visit((Dsymbol *)sds);
- }
-
- void visit(AttribDeclaration *ad)
- {
- Dsymbols *d = ad->include(NULL);
- if (!d)
- return;
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- s->accept(this);
- }
- visit((Dsymbol *)ad);
- }
-
- void visit(ConditionalDeclaration *cd)
- {
- if (cd->condition->inc)
- visit((AttribDeclaration *)cd);
- else
- visit((Dsymbol *)cd);
- }
- };
- InstMemberWalker v(tempinst->inst);
- tempinst->inst->accept(&v);
-
- if (tempinst->minst) // if inst was not speculative
- {
- /* Add 'inst' once again to the root module members[], then the
- * instance members will get codegen chances.
- */
- tempinst->inst->appendToModuleMember();
- }
- }
-
- return;
- }
- unsigned errorsave = global.errors;
-
- tempinst->inst = tempinst;
- tempinst->parent = tempinst->enclosing ? tempinst->enclosing : tempdecl->parent;
- //printf("parent = '%s'\n", tempinst->parent->kind());
-
- TemplateInstance *tempdecl_instance_idx = tempdecl->addInstance(tempinst);
-
- //getIdent();
-
- // Store the place we added it to in target_symbol_list(_idx) so we can
- // remove it later if we encounter an error.
- Dsymbols *target_symbol_list = tempinst->appendToModuleMember();
- size_t target_symbol_list_idx = target_symbol_list ? target_symbol_list->length - 1 : 0;
-
- // Copy the syntax trees from the TemplateDeclaration
- tempinst->members = Dsymbol::arraySyntaxCopy(tempdecl->members);
-
- // resolve TemplateThisParameter
- for (size_t i = 0; i < tempdecl->parameters->length; i++)
- {
- if ((*tempdecl->parameters)[i]->isTemplateThisParameter() == NULL)
- continue;
- Type *t = isType((*tempinst->tiargs)[i]);
- assert(t);
- if (StorageClass stc = ModToStc(t->mod))
- {
- //printf("t = %s, stc = x%llx\n", t->toChars(), stc);
- Dsymbols *s = new Dsymbols();
- s->push(new StorageClassDeclaration(stc, tempinst->members));
- tempinst->members = s;
- }
- break;
- }
-
- // Create our own scope for the template parameters
- Scope *scope = tempdecl->_scope;
- if (tempdecl->semanticRun == PASSinit)
- {
- tempinst->error("template instantiation %s forward references template declaration %s", tempinst->toChars(), tempdecl->toChars());
- return;
- }
-
- tempinst->argsym = new ScopeDsymbol();
- tempinst->argsym->parent = scope->parent;
- scope = scope->push(tempinst->argsym);
- scope->tinst = tempinst;
- scope->minst = tempinst->minst;
- //scope->stc = 0;
-
- // Declare each template parameter as an alias for the argument type
- Scope *paramscope = scope->push();
- paramscope->stc = 0;
- paramscope->protection = Prot(Prot::public_); // Bugzilla 14169: template parameters should be public
- tempinst->declareParameters(paramscope);
- paramscope->pop();
-
- // Add members of template instance to template instance symbol table
-// tempinst->parent = scope->scopesym;
- tempinst->symtab = new DsymbolTable();
- for (size_t i = 0; i < tempinst->members->length; i++)
- {
- Dsymbol *s = (*tempinst->members)[i];
- s->addMember(scope, tempinst);
- }
-
- /* See if there is only one member of template instance, and that
- * member has the same name as the template instance.
- * If so, this template instance becomes an alias for that member.
- */
- //printf("members->length = %d\n", tempinst->members->length);
- if (tempinst->members->length)
- {
- Dsymbol *s;
- if (Dsymbol::oneMembers(tempinst->members, &s, tempdecl->ident) && s)
- {
- //printf("tempdecl->ident = %s, s = '%s'\n", tempdecl->ident->toChars(), s->kind(), s->toPrettyChars());
- //printf("setting aliasdecl\n");
- tempinst->aliasdecl = s;
- }
- }
-
- /* If function template declaration
- */
- if (fargs && tempinst->aliasdecl)
- {
- FuncDeclaration *fd = tempinst->aliasdecl->isFuncDeclaration();
- if (fd)
- {
- /* Transmit fargs to type so that TypeFunction::semantic() can
- * resolve any "auto ref" storage classes.
- */
- TypeFunction *tf = (TypeFunction *)fd->type;
- if (tf && tf->ty == Tfunction)
- tf->fargs = fargs;
- }
- }
-
- // Do semantic() analysis on template instance members
- Scope *sc2;
- sc2 = scope->push(tempinst);
- //printf("enclosing = %d, sc->parent = %s\n", tempinst->enclosing, sc->parent->toChars());
- sc2->parent = tempinst;
- sc2->tinst = tempinst;
- sc2->minst = tempinst->minst;
-
- tempinst->tryExpandMembers(sc2);
-
- tempinst->semanticRun = PASSsemanticdone;
-
- /* ConditionalDeclaration may introduce eponymous declaration,
- * so we should find it once again after semantic.
- */
- if (tempinst->members->length)
- {
- Dsymbol *s;
- if (Dsymbol::oneMembers(tempinst->members, &s, tempdecl->ident) && s)
- {
- if (!tempinst->aliasdecl || tempinst->aliasdecl != s)
- {
- //printf("tempdecl->ident = %s, s = '%s'\n", tempdecl->ident->toChars(), s->kind(), s->toPrettyChars());
- //printf("setting aliasdecl 2\n");
- tempinst->aliasdecl = s;
- }
- }
- }
-
- if (global.errors != errorsave)
- goto Laftersemantic;
-
- /* If any of the instantiation members didn't get semantic() run
- * on them due to forward references, we cannot run semantic2()
- * or semantic3() yet.
- */
- {
- bool found_deferred_ad = false;
- for (size_t i = 0; i < Module::deferred.length; i++)
- {
- Dsymbol *sd = Module::deferred[i];
- AggregateDeclaration *ad = sd->isAggregateDeclaration();
- if (ad && ad->parent && ad->parent->isTemplateInstance())
- {
- //printf("deferred template aggregate: %s %s\n",
- // sd->parent->toChars(), sd->toChars());
- found_deferred_ad = true;
- if (ad->parent == tempinst)
- {
- ad->deferred = tempinst;
- break;
- }
- }
- }
- if (found_deferred_ad || Module::deferred.length)
- goto Laftersemantic;
- }
-
- /* The problem is when to parse the initializer for a variable.
- * Perhaps VarDeclaration::semantic() should do it like it does
- * for initializers inside a function.
- */
- //if (sc->parent->isFuncDeclaration())
- {
- /* BUG 782: this has problems if the classes this depends on
- * are forward referenced. Find a way to defer semantic()
- * on this template.
- */
- semantic2(tempinst, sc2);
- }
- if (global.errors != errorsave)
- goto Laftersemantic;
-
- if ((sc->func || (sc->flags & SCOPEfullinst)) && !tempinst->tinst)
- {
- /* If a template is instantiated inside function, the whole instantiation
- * should be done at that position. But, immediate running semantic3 of
- * dependent templates may cause unresolved forward reference (Bugzilla 9050).
- * To avoid the issue, don't run semantic3 until semantic and semantic2 done.
- */
- TemplateInstances deferred;
- tempinst->deferred = &deferred;
-
- //printf("Run semantic3 on %s\n", tempinst->toChars());
- tempinst->trySemantic3(sc2);
-
- for (size_t i = 0; i < deferred.length; i++)
- {
- //printf("+ run deferred semantic3 on %s\n", deferred[i]->toChars());
- semantic3(deferred[i], NULL);
- }
-
- tempinst->deferred = NULL;
- }
- else if (tempinst->tinst)
- {
- bool doSemantic3 = false;
- if (sc->func && tempinst->aliasdecl && tempinst->aliasdecl->toAlias()->isFuncDeclaration())
- {
- /* Template function instantiation should run semantic3 immediately
- * for attribute inference.
- */
- tempinst->trySemantic3(sc2);
- }
- else if (sc->func)
- {
- /* A lambda function in template arguments might capture the
- * instantiated scope context. For the correct context inference,
- * all instantiated functions should run the semantic3 immediately.
- * See also compilable/test14973.d
- */
- for (size_t i = 0; i < tempinst->tdtypes.length; i++)
- {
- RootObject *oarg = tempinst->tdtypes[i];
- Dsymbol *s = getDsymbol(oarg);
- if (!s)
- continue;
-
- if (TemplateDeclaration *td = s->isTemplateDeclaration())
- {
- if (!td->literal)
- continue;
- assert(td->members && td->members->length == 1);
- s = (*td->members)[0];
- }
- if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
- {
- if (fld->tok == TOKreserved)
- {
- doSemantic3 = true;
- break;
- }
- }
- }
- //printf("[%s] %s doSemantic3 = %d\n", tempinst->loc.toChars(), tempinst->toChars(), doSemantic3);
- }
- if (doSemantic3)
- tempinst->trySemantic3(sc2);
-
- TemplateInstance *ti = tempinst->tinst;
- int nest = 0;
- while (ti && !ti->deferred && ti->tinst)
- {
- ti = ti->tinst;
- if (++nest > global.recursionLimit)
- {
- global.gag = 0; // ensure error message gets printed
- tempinst->error("recursive expansion");
- fatal();
- }
- }
- if (ti && ti->deferred)
- {
- //printf("deferred semantic3 of %p %s, ti = %s, ti->deferred = %p\n", tempinst, tempinst->toChars(), ti->toChars());
- for (size_t i = 0; ; i++)
- {
- if (i == ti->deferred->length)
- {
- ti->deferred->push(tempinst);
- break;
- }
- if ((*ti->deferred)[i] == tempinst)
- break;
- }
- }
- }
-
- if (tempinst->aliasdecl)
- {
- /* Bugzilla 13816: AliasDeclaration tries to resolve forward reference
- * twice (See inuse check in AliasDeclaration::toAlias()). It's
- * necessary to resolve mutual references of instantiated symbols, but
- * it will left a true recursive alias in tuple declaration - an
- * AliasDeclaration A refers TupleDeclaration B, and B contains A
- * in its elements. To correctly make it an error, we strictly need to
- * resolve the alias of eponymous member.
- */
- tempinst->aliasdecl = tempinst->aliasdecl->toAlias2();
- }
-
- Laftersemantic:
- sc2->pop();
-
- scope->pop();
-
- // Give additional context info if error occurred during instantiation
- if (global.errors != errorsave)
- {
- if (!tempinst->errors)
- {
- if (!tempdecl->literal)
- tempinst->error(tempinst->loc, "error instantiating");
- if (tempinst->tinst)
- tempinst->tinst->printInstantiationTrace();
- }
- tempinst->errors = true;
- if (tempinst->gagged)
- {
- // Errors are gagged, so remove the template instance from the
- // instance/symbol lists we added it to and reset our state to
- // finish clean and so we can try to instantiate it again later
- // (see bugzilla 4302 and 6602).
- tempdecl->removeInstance(tempdecl_instance_idx);
- if (target_symbol_list)
- {
- // Because we added 'this' in the last position above, we
- // should be able to remove it without messing other indices up.
- assert((*target_symbol_list)[target_symbol_list_idx] == tempinst);
- target_symbol_list->remove(target_symbol_list_idx);
- tempinst->memberOf = NULL; // no longer a member
- }
- tempinst->semanticRun = PASSinit;
- tempinst->inst = NULL;
- tempinst->symtab = NULL;
- }
- }
- else if (errinst)
- {
- /* Bugzilla 14541: If the previous gagged instance had failed by
- * circular references, currrent "error reproduction instantiation"
- * might succeed, because of the difference of instantiated context.
- * On such case, the cached error instance needs to be overridden by the
- * succeeded instance.
- */
- //printf("replaceInstance()\n");
- TemplateInstances *tinstances = (TemplateInstances *)dmd_aaGetRvalue((AA *)tempdecl->instances, (void *)tempinst->hash);
- assert(tinstances);
- for (size_t i = 0; i < tinstances->length; i++)
- {
- TemplateInstance *ti = (*tinstances)[i];
- if (ti == errinst)
- {
- (*tinstances)[i] = tempinst; // override
- break;
- }
- }
- }
-}
-
-// function used to perform semantic on AliasDeclaration
-void aliasSemantic(AliasDeclaration *ds, Scope *sc)
-{
- //printf("AliasDeclaration::semantic() %s\n", ds->toChars());
-
- // as AliasDeclaration::semantic, in case we're called first.
- // see https://issues.dlang.org/show_bug.cgi?id=21001
- ds->storage_class |= sc->stc & STCdeprecated;
- ds->protection = sc->protection;
- ds->userAttribDecl = sc->userAttribDecl;
-
- // TypeTraits needs to know if it's located in an AliasDeclaration
- const unsigned oldflags = sc->flags;
- sc->flags |= SCOPEalias;
-
- if (ds->aliassym)
- {
- FuncDeclaration *fd = ds->aliassym->isFuncLiteralDeclaration();
- TemplateDeclaration *td = ds->aliassym->isTemplateDeclaration();
- if (fd || (td && td->literal))
- {
- if (fd && fd->semanticRun >= PASSsemanticdone)
- {
- sc->flags = oldflags;
- return;
- }
-
- Expression *e = new FuncExp(ds->loc, ds->aliassym);
- e = expressionSemantic(e, sc);
- if (e->op == TOKfunction)
- {
- FuncExp *fe = (FuncExp *)e;
- ds->aliassym = fe->td ? (Dsymbol *)fe->td : fe->fd;
- }
- else
- {
- ds->aliassym = NULL;
- ds->type = Type::terror;
- }
- sc->flags = oldflags;
- return;
- }
-
- if (ds->aliassym->isTemplateInstance())
- dsymbolSemantic(ds->aliassym, sc);
- sc->flags = oldflags;
- return;
- }
- ds->inuse = 1;
-
- // Given:
- // alias foo.bar.abc def;
- // it is not knowable from the syntax whether this is an alias
- // for a type or an alias for a symbol. It is up to the semantic()
- // pass to distinguish.
- // If it is a type, then type is set and getType() will return that
- // type. If it is a symbol, then aliassym is set and type is NULL -
- // toAlias() will return aliasssym.
-
- unsigned int errors = global.errors;
- Type *oldtype = ds->type;
-
- // Ungag errors when not instantiated DeclDefs scope alias
- Ungag ungag(global.gag);
- //printf("%s parent = %s, gag = %d, instantiated = %d\n", ds->toChars(), ds->parent, global.gag, ds->isInstantiated());
- if (ds->parent && global.gag && !ds->isInstantiated() && !ds->toParent2()->isFuncDeclaration())
- {
- //printf("%s type = %s\n", ds->toPrettyChars(), ds->type->toChars());
- global.gag = 0;
- }
-
- /* This section is needed because Type::resolve() will:
- * const x = 3;
- * alias y = x;
- * try to convert identifier x to 3.
- */
- Dsymbol *s = ds->type->toDsymbol(sc);
- if (errors != global.errors)
- {
- s = NULL;
- ds->type = Type::terror;
- }
- if (s && s == ds)
- {
- ds->error("cannot resolve");
- s = NULL;
- ds->type = Type::terror;
- }
- if (!s || !s->isEnumMember())
- {
- Type *t;
- Expression *e;
- Scope *sc2 = sc;
- if (ds->storage_class & (STCref | STCnothrow | STCnogc | STCpure | STCdisable))
- {
- // For 'ref' to be attached to function types, and picked
- // up by Type::resolve(), it has to go into sc.
- sc2 = sc->push();
- sc2->stc |= ds->storage_class & (STCref | STCnothrow | STCnogc | STCpure | STCshared | STCdisable);
- }
- ds->type = ds->type->addSTC(ds->storage_class);
- ds->type->resolve(ds->loc, sc2, &e, &t, &s);
- if (sc2 != sc)
- sc2->pop();
-
- if (e) // Try to convert Expression to Dsymbol
- {
- s = getDsymbol(e);
- if (!s)
- {
- if (e->op != TOKerror)
- ds->error("cannot alias an expression %s", e->toChars());
- t = Type::terror;
- }
- }
- ds->type = t;
- }
- if (s == ds)
- {
- assert(global.errors);
- ds->type = Type::terror;
- s = NULL;
- }
- if (!s) // it's a type alias
- {
- //printf("alias %s resolved to type %s\n", ds->toChars(), ds->type->toChars());
- ds->type = typeSemantic(ds->type, ds->loc, sc);
- ds->aliassym = NULL;
- }
- else // it's a symbolic alias
- {
- //printf("alias %s resolved to %s %s\n", ds->toChars(), s->kind(), s->toChars());
- ds->type = NULL;
- ds->aliassym = s;
- }
- if (global.gag && errors != global.errors)
- {
- ds->type = oldtype;
- ds->aliassym = NULL;
- }
- ds->inuse = 0;
- ds->semanticRun = PASSsemanticdone;
-
- if (Dsymbol *sx = ds->overnext)
- {
- ds->overnext = NULL;
-
- if (!ds->overloadInsert(sx))
- ScopeDsymbol::multiplyDefined(Loc(), sx, ds);
- }
- sc->flags = oldflags;
-}
-
-
-/*************************************
- * Does semantic analysis on the public face of declarations.
- */
-void dsymbolSemantic(Dsymbol *dsym, Scope *sc)
-{
- DsymbolSemanticVisitor v(sc);
- dsym->accept(&v);
-}
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
new file mode 100644
index 00000000000..eac20952eb5
--- /dev/null
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -0,0 +1,6654 @@
+/**
+ * Does the semantic 1 pass on the AST, which looks at symbol declarations but not initializers
+ * or function bodies.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dsymbolsem.d, _dsymbolsem.d)
+ * Documentation: https://dlang.org/phobos/dmd_dsymbolsem.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbolsem.d
+ */
+
+module dmd.dsymbolsem;
+
+import core.stdc.stdio;
+import core.stdc.string;
+
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.apply;
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.blockexit;
+import dmd.clone;
+import dmd.compiler;
+import dmd.dcast;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dinterpret;
+import dmd.dmangle;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.dversion;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.initsem;
+import dmd.hdrgen;
+import dmd.mtype;
+import dmd.nogc;
+import dmd.nspace;
+import dmd.objc;
+import dmd.opover;
+import dmd.parse;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.semantic2;
+import dmd.semantic3;
+import dmd.sideeffect;
+import dmd.statementsem;
+import dmd.staticassert;
+import dmd.tokens;
+import dmd.utf;
+import dmd.utils;
+import dmd.statement;
+import dmd.target;
+import dmd.templateparamsem;
+import dmd.typesem;
+import dmd.visitor;
+
+enum LOG = false;
+
+private uint setMangleOverride(Dsymbol s, const(char)[] sym)
+{
+ if (s.isFuncDeclaration() || s.isVarDeclaration())
+ {
+ s.isDeclaration().mangleOverride = sym;
+ return 1;
+ }
+
+ if (auto ad = s.isAttribDeclaration())
+ {
+ uint nestedCount = 0;
+
+ ad.include(null).foreachDsymbol( (s) { nestedCount += setMangleOverride(s, sym); } );
+
+ return nestedCount;
+ }
+ return 0;
+}
+
+/*************************************
+ * Does semantic analysis on the public face of declarations.
+ */
+extern(C++) void dsymbolSemantic(Dsymbol dsym, Scope* sc)
+{
+ scope v = new DsymbolSemanticVisitor(sc);
+ dsym.accept(v);
+}
+
+/***************************************************
+ * Determine the numerical value of the AlignmentDeclaration
+ * Params:
+ * ad = AlignmentDeclaration
+ * sc = context
+ * Returns:
+ * alignment as numerical value that is never 0.
+ * STRUCTALIGN_DEFAULT is used instead.
+ * STRUCTALIGN_DEFAULT is returned for errors
+ */
+structalign_t getAlignment(AlignDeclaration ad, Scope* sc)
+{
+ if (ad.salign != ad.UNKNOWN) // UNKNOWN is 0
+ return ad.salign;
+
+ if (!ad.exps)
+ return ad.salign = STRUCTALIGN_DEFAULT;
+
+ dinteger_t strictest = 0; // strictest alignment
+ bool errors;
+ foreach (ref exp; (*ad.exps)[])
+ {
+ sc = sc.startCTFE();
+ auto e = exp.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = e.ctfeInterpret();
+ exp = e; // could be re-evaluated if exps are assigned to more than one AlignDeclaration by CParser.applySpecifier(),
+ // e.g. `_Alignas(8) int a, b;`
+ if (e.op == TOK.error)
+ errors = true;
+ else
+ {
+ auto n = e.toInteger();
+ if (sc.flags & SCOPE.Cfile && n == 0) // C11 6.7.5-6 allows 0 for alignment
+ continue;
+
+ if (n < 1 || n & (n - 1) || structalign_t.max < n || !e.type.isintegral())
+ {
+ error(ad.loc, "alignment must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
+ errors = true;
+ }
+ if (n > strictest) // C11 6.7.5-6
+ strictest = n;
+ }
+ }
+
+ ad.salign = (errors || strictest == 0) // C11 6.7.5-6 says alignment of 0 means no effect
+ ? STRUCTALIGN_DEFAULT
+ : cast(structalign_t) strictest;
+ return ad.salign;
+}
+
+const(char)* getMessage(DeprecatedDeclaration dd)
+{
+ if (auto sc = dd._scope)
+ {
+ dd._scope = null;
+
+ sc = sc.startCTFE();
+ dd.msg = dd.msg.expressionSemantic(sc);
+ dd.msg = resolveProperties(sc, dd.msg);
+ sc = sc.endCTFE();
+ dd.msg = dd.msg.ctfeInterpret();
+
+ if (auto se = dd.msg.toStringExp())
+ dd.msgstr = se.toStringz().ptr;
+ else
+ dd.msg.error("compile time constant expected, not `%s`", dd.msg.toChars());
+ }
+ return dd.msgstr;
+}
+
+
+// Returns true if a contract can appear without a function body.
+package bool allowsContractWithoutBody(FuncDeclaration funcdecl)
+{
+ assert(!funcdecl.fbody);
+
+ /* Contracts can only appear without a body when they are virtual
+ * interface functions or abstract.
+ */
+ Dsymbol parent = funcdecl.toParent();
+ InterfaceDeclaration id = parent.isInterfaceDeclaration();
+
+ if (!funcdecl.isAbstract() &&
+ (funcdecl.fensures || funcdecl.frequires) &&
+ !(id && funcdecl.isVirtual()))
+ {
+ auto cd = parent.isClassDeclaration();
+ if (!(cd && cd.isAbstract()))
+ return false;
+ }
+ return true;
+}
+
+private extern(C++) final class DsymbolSemanticVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+
+ Scope* sc;
+ this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ // Save the scope and defer semantic analysis on the Dsymbol.
+ private void deferDsymbolSemantic(Dsymbol s, Scope *scx)
+ {
+ s._scope = scx ? scx : sc.copy();
+ s._scope.setNoFree();
+ Module.addDeferredSemantic(s);
+ }
+
+ override void visit(Dsymbol dsym)
+ {
+ dsym.error("%p has no semantic routine", dsym);
+ }
+
+ override void visit(ScopeDsymbol) { }
+ override void visit(Declaration) { }
+
+ override void visit(AliasThis dsym)
+ {
+ if (dsym.semanticRun != PASS.init)
+ return;
+
+ if (dsym._scope)
+ {
+ sc = dsym._scope;
+ dsym._scope = null;
+ }
+
+ if (!sc)
+ return;
+
+ dsym.semanticRun = PASS.semantic;
+ dsym.isDeprecated_ = !!(sc.stc & STC.deprecated_);
+
+ Dsymbol p = sc.parent.pastMixin();
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (!ad)
+ {
+ error(dsym.loc, "alias this can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
+ return;
+ }
+
+ assert(ad.members);
+ Dsymbol s = ad.search(dsym.loc, dsym.ident);
+ if (!s)
+ {
+ s = sc.search(dsym.loc, dsym.ident, null);
+ if (s)
+ error(dsym.loc, "`%s` is not a member of `%s`", s.toChars(), ad.toChars());
+ else
+ error(dsym.loc, "undefined identifier `%s`", dsym.ident.toChars());
+ return;
+ }
+ if (ad.aliasthis && s != ad.aliasthis)
+ {
+ error(dsym.loc, "there can be only one alias this");
+ return;
+ }
+
+ /* disable the alias this conversion so the implicit conversion check
+ * doesn't use it.
+ */
+ ad.aliasthis = null;
+
+ Dsymbol sx = s;
+ if (sx.isAliasDeclaration())
+ sx = sx.toAlias();
+ Declaration d = sx.isDeclaration();
+ if (d && !d.isTupleDeclaration())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=18429
+ *
+ * If the identifier in the AliasThis declaration
+ * is defined later and is a voldemort type, we must
+ * perform semantic on the declaration to deduce the type.
+ */
+ if (!d.type)
+ d.dsymbolSemantic(sc);
+
+ Type t = d.type;
+ assert(t);
+ if (ad.type.implicitConvTo(t) > MATCH.nomatch)
+ {
+ error(dsym.loc, "alias this is not reachable as `%s` already converts to `%s`", ad.toChars(), t.toChars());
+ }
+ }
+
+ dsym.sym = s;
+ // Restore alias this
+ ad.aliasthis = dsym;
+ dsym.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(AliasDeclaration dsym)
+ {
+ if (dsym.semanticRun >= PASS.semanticdone)
+ return;
+ assert(dsym.semanticRun <= PASS.semantic);
+
+ dsym.storage_class |= sc.stc & STC.deprecated_;
+ dsym.visibility = sc.visibility;
+ dsym.userAttribDecl = sc.userAttribDecl;
+
+ if (!sc.func && dsym.inNonRoot())
+ return;
+
+ aliasSemantic(dsym, sc);
+ }
+
+ override void visit(AliasAssign dsym)
+ {
+ //printf("visit(AliasAssign)\n");
+ if (dsym.semanticRun >= PASS.semanticdone)
+ return;
+ assert(dsym.semanticRun <= PASS.semantic);
+
+ if (!sc.func && dsym.inNonRoot())
+ return;
+
+ aliasAssignSemantic(dsym, sc);
+ }
+
+ override void visit(VarDeclaration dsym)
+ {
+ version (none)
+ {
+ printf("VarDeclaration::semantic('%s', parent = '%s') sem = %d\n",
+ dsym.toChars(), sc.parent ? sc.parent.toChars() : null, dsym.semanticRun);
+ printf(" type = %s\n", dsym.type ? dsym.type.toChars() : "null");
+ printf(" stc = x%llx\n", dsym.storage_class);
+ printf(" storage_class = x%llx\n", dsym.storage_class);
+ printf("linkage = %d\n", dsym.linkage);
+ //if (strcmp(toChars(), "mul") == 0) assert(0);
+ }
+ //if (semanticRun > PASS.init)
+ // return;
+ //semanticRun = PSSsemantic;
+
+ if (dsym.semanticRun >= PASS.semanticdone)
+ return;
+
+ if (sc && sc.inunion && sc.inunion.isAnonDeclaration())
+ dsym.overlapped = true;
+
+ Scope* scx = null;
+ if (dsym._scope)
+ {
+ sc = dsym._scope;
+ scx = sc;
+ dsym._scope = null;
+ }
+
+ if (!sc)
+ return;
+
+ dsym.semanticRun = PASS.semantic;
+
+ /* Pick up storage classes from context, but except synchronized,
+ * override, abstract, and final.
+ */
+ dsym.storage_class |= (sc.stc & ~(STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_));
+ if (dsym.storage_class & STC.extern_ && dsym._init)
+ dsym.error("extern symbols cannot have initializers");
+
+ dsym.userAttribDecl = sc.userAttribDecl;
+ dsym.cppnamespace = sc.namespace;
+
+ AggregateDeclaration ad = dsym.isThis();
+ if (ad)
+ dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
+
+ /* If auto type inference, do the inference
+ */
+ int inferred = 0;
+ if (!dsym.type)
+ {
+ dsym.inuse++;
+
+ // Infering the type requires running semantic,
+ // so mark the scope as ctfe if required
+ bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0;
+ if (needctfe)
+ {
+ sc.flags |= SCOPE.condition;
+ sc = sc.startCTFE();
+ }
+ //printf("inferring type for %s with init %s\n", dsym.toChars(), dsym._init.toChars());
+ dsym._init = dsym._init.inferType(sc);
+ dsym.type = dsym._init.initializerToExpression().type;
+ if (needctfe)
+ sc = sc.endCTFE();
+
+ dsym.inuse--;
+ inferred = 1;
+
+ /* This is a kludge to support the existing syntax for RAII
+ * declarations.
+ */
+ dsym.storage_class &= ~STC.auto_;
+ dsym.originalType = dsym.type.syntaxCopy();
+ }
+ else
+ {
+ if (!dsym.originalType)
+ dsym.originalType = dsym.type.syntaxCopy();
+
+ /* Prefix function attributes of variable declaration can affect
+ * its type:
+ * pure nothrow void function() fp;
+ * static assert(is(typeof(fp) == void function() pure nothrow));
+ */
+ Scope* sc2 = sc.push();
+ sc2.stc |= (dsym.storage_class & STC.FUNCATTR);
+ dsym.inuse++;
+ dsym.type = dsym.type.typeSemantic(dsym.loc, sc2);
+ dsym.inuse--;
+ sc2.pop();
+ }
+ //printf(" semantic type = %s\n", dsym.type ? dsym.type.toChars() : "null");
+ if (dsym.type.ty == Terror)
+ dsym.errors = true;
+
+ dsym.type.checkDeprecated(dsym.loc, sc);
+ dsym.linkage = sc.linkage;
+ dsym.parent = sc.parent;
+ //printf("this = %p, parent = %p, '%s'\n", dsym, dsym.parent, dsym.parent.toChars());
+ dsym.visibility = sc.visibility;
+
+ /* If scope's alignment is the default, use the type's alignment,
+ * otherwise the scope overrrides.
+ */
+ dsym.alignment = sc.alignment();
+ if (dsym.alignment == STRUCTALIGN_DEFAULT)
+ dsym.alignment = dsym.type.alignment(); // use type's alignment
+
+ //printf("sc.stc = %x\n", sc.stc);
+ //printf("storage_class = x%x\n", storage_class);
+
+ if (global.params.vcomplex)
+ dsym.type.checkComplexTransition(dsym.loc, sc);
+
+ // Calculate type size + safety checks
+ if (sc.func && !sc.intypeof)
+ {
+ if (dsym.storage_class & STC.gshared && !dsym.isMember())
+ {
+ if (sc.func.setUnsafe())
+ dsym.error("__gshared not allowed in safe functions; use shared");
+ }
+ }
+
+ Dsymbol parent = dsym.toParent();
+
+ Type tb = dsym.type.toBasetype();
+ Type tbn = tb.baseElemOf();
+ if (tb.ty == Tvoid && !(dsym.storage_class & STC.lazy_))
+ {
+ if (inferred)
+ {
+ dsym.error("type `%s` is inferred from initializer `%s`, and variables cannot be of type `void`", dsym.type.toChars(), dsym._init.toChars());
+ }
+ else
+ dsym.error("variables cannot be of type `void`");
+ dsym.type = Type.terror;
+ tb = dsym.type;
+ }
+ if (tb.ty == Tfunction)
+ {
+ dsym.error("cannot be declared to be a function");
+ dsym.type = Type.terror;
+ tb = dsym.type;
+ }
+ if (auto ts = tb.isTypeStruct())
+ {
+ // Require declarations, except when it's just a reference (as done for pointers)
+ // or when the variable is defined externally
+ if (!ts.sym.members && !(dsym.storage_class & (STC.ref_ | STC.extern_)))
+ {
+ dsym.error("no definition of struct `%s`", ts.toChars());
+
+ // Explain why the definition is required when it's part of another type
+ if (!dsym.type.isTypeStruct())
+ {
+ // Prefer Loc of the dependant type
+ const s = dsym.type.toDsymbol(sc);
+ const loc = (s ? s : dsym).loc;
+ loc.errorSupplemental("required by type `%s`", dsym.type.toChars());
+ }
+
+ // Flag variable as error to avoid invalid error messages due to unknown size
+ dsym.type = Type.terror;
+ }
+ }
+ if ((dsym.storage_class & STC.auto_) && !inferred)
+ dsym.error("storage class `auto` has no effect if type is not inferred, did you mean `scope`?");
+
+ if (auto tt = tb.isTypeTuple())
+ {
+ /* Instead, declare variables for each of the tuple elements
+ * and add those.
+ */
+ size_t nelems = Parameter.dim(tt.arguments);
+ Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression() : null;
+ if (ie)
+ ie = ie.expressionSemantic(sc);
+ if (nelems > 0 && ie)
+ {
+ auto iexps = new Expressions();
+ iexps.push(ie);
+ auto exps = new Expressions();
+ for (size_t pos = 0; pos < iexps.dim; pos++)
+ {
+ Lexpand1:
+ Expression e = (*iexps)[pos];
+ Parameter arg = Parameter.getNth(tt.arguments, pos);
+ arg.type = arg.type.typeSemantic(dsym.loc, sc);
+ //printf("[%d] iexps.dim = %d, ", pos, iexps.dim);
+ //printf("e = (%s %s, %s), ", Token::tochars[e.op], e.toChars(), e.type.toChars());
+ //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
+
+ if (e != ie)
+ {
+ if (iexps.dim > nelems)
+ goto Lnomatch;
+ if (e.type.implicitConvTo(arg.type))
+ continue;
+ }
+
+ if (auto te = e.isTupleExp())
+ {
+ if (iexps.dim - 1 + te.exps.dim > nelems)
+ goto Lnomatch;
+
+ iexps.remove(pos);
+ iexps.insert(pos, te.exps);
+ (*iexps)[pos] = Expression.combine(te.e0, (*iexps)[pos]);
+ goto Lexpand1;
+ }
+ else if (isAliasThisTuple(e))
+ {
+ auto v = copyToTemp(0, "__tup", e);
+ v.dsymbolSemantic(sc);
+ auto ve = new VarExp(dsym.loc, v);
+ ve.type = e.type;
+
+ exps.setDim(1);
+ (*exps)[0] = ve;
+ expandAliasThisTuples(exps, 0);
+
+ for (size_t u = 0; u < exps.dim; u++)
+ {
+ Lexpand2:
+ Expression ee = (*exps)[u];
+ arg = Parameter.getNth(tt.arguments, pos + u);
+ arg.type = arg.type.typeSemantic(dsym.loc, sc);
+ //printf("[%d+%d] exps.dim = %d, ", pos, u, exps.dim);
+ //printf("ee = (%s %s, %s), ", Token::tochars[ee.op], ee.toChars(), ee.type.toChars());
+ //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
+
+ size_t iexps_dim = iexps.dim - 1 + exps.dim;
+ if (iexps_dim > nelems)
+ goto Lnomatch;
+ if (ee.type.implicitConvTo(arg.type))
+ continue;
+
+ if (expandAliasThisTuples(exps, u) != -1)
+ goto Lexpand2;
+ }
+
+ if ((*exps)[0] != ve)
+ {
+ Expression e0 = (*exps)[0];
+ (*exps)[0] = new CommaExp(dsym.loc, new DeclarationExp(dsym.loc, v), e0);
+ (*exps)[0].type = e0.type;
+
+ iexps.remove(pos);
+ iexps.insert(pos, exps);
+ goto Lexpand1;
+ }
+ }
+ }
+ if (iexps.dim < nelems)
+ goto Lnomatch;
+
+ ie = new TupleExp(dsym._init.loc, iexps);
+ }
+ Lnomatch:
+
+ if (ie && ie.op == TOK.tuple)
+ {
+ auto te = ie.isTupleExp();
+ size_t tedim = te.exps.dim;
+ if (tedim != nelems)
+ {
+ error(dsym.loc, "tuple of %d elements cannot be assigned to tuple of %d elements", cast(int)tedim, cast(int)nelems);
+ for (size_t u = tedim; u < nelems; u++) // fill dummy expression
+ te.exps.push(ErrorExp.get());
+ }
+ }
+
+ auto exps = new Objects(nelems);
+ for (size_t i = 0; i < nelems; i++)
+ {
+ Parameter arg = Parameter.getNth(tt.arguments, i);
+
+ OutBuffer buf;
+ buf.printf("__%s_field_%llu", dsym.ident.toChars(), cast(ulong)i);
+ auto id = Identifier.idPool(buf[]);
+
+ Initializer ti;
+ if (ie)
+ {
+ Expression einit = ie;
+ if (auto te = ie.isTupleExp())
+ {
+ einit = (*te.exps)[i];
+ if (i == 0)
+ einit = Expression.combine(te.e0, einit);
+ }
+ ti = new ExpInitializer(einit.loc, einit);
+ }
+ else
+ ti = dsym._init ? dsym._init.syntaxCopy() : null;
+
+ StorageClass storage_class = STC.temp | STC.local | dsym.storage_class;
+ if ((dsym.storage_class & STC.parameter) && (arg.storageClass & STC.parameter))
+ storage_class |= arg.storageClass;
+ auto v = new VarDeclaration(dsym.loc, arg.type, id, ti, storage_class);
+ //printf("declaring field %s of type %s\n", v.toChars(), v.type.toChars());
+ v.dsymbolSemantic(sc);
+
+ if (sc.scopesym)
+ {
+ //printf("adding %s to %s\n", v.toChars(), sc.scopesym.toChars());
+ if (sc.scopesym.members)
+ // Note this prevents using foreach() over members, because the limits can change
+ sc.scopesym.members.push(v);
+ }
+
+ Expression e = new DsymbolExp(dsym.loc, v);
+ (*exps)[i] = e;
+ }
+ auto v2 = new TupleDeclaration(dsym.loc, dsym.ident, exps);
+ v2.parent = dsym.parent;
+ v2.isexp = true;
+ dsym.aliassym = v2;
+ dsym.semanticRun = PASS.semanticdone;
+ return;
+ }
+
+ /* Storage class can modify the type
+ */
+ dsym.type = dsym.type.addStorageClass(dsym.storage_class);
+
+ /* Adjust storage class to reflect type
+ */
+ if (dsym.type.isConst())
+ {
+ dsym.storage_class |= STC.const_;
+ if (dsym.type.isShared())
+ dsym.storage_class |= STC.shared_;
+ }
+ else if (dsym.type.isImmutable())
+ dsym.storage_class |= STC.immutable_;
+ else if (dsym.type.isShared())
+ dsym.storage_class |= STC.shared_;
+ else if (dsym.type.isWild())
+ dsym.storage_class |= STC.wild;
+
+ if (StorageClass stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_))
+ {
+ if (stc == STC.final_)
+ dsym.error("cannot be `final`, perhaps you meant `const`?");
+ else
+ {
+ OutBuffer buf;
+ stcToBuffer(&buf, stc);
+ dsym.error("cannot be `%s`", buf.peekChars());
+ }
+ dsym.storage_class &= ~stc; // strip off
+ }
+
+ // At this point we can add `scope` to the STC instead of `in`,
+ // because we are never going to use this variable's STC for user messages
+ if (dsym.storage_class & STC.in_ && global.params.previewIn)
+ dsym.storage_class |= STC.scope_;
+
+ if (dsym.storage_class & STC.scope_)
+ {
+ StorageClass stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.tls | STC.gshared);
+ if (stc)
+ {
+ OutBuffer buf;
+ stcToBuffer(&buf, stc);
+ dsym.error("cannot be `scope` and `%s`", buf.peekChars());
+ }
+ else if (dsym.isMember())
+ {
+ dsym.error("field cannot be `scope`");
+ }
+ else if (!dsym.type.hasPointers())
+ {
+ dsym.storage_class &= ~STC.scope_; // silently ignore; may occur in generic code
+ }
+ }
+
+ if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.templateparameter | STC.tls | STC.gshared | STC.ctfe))
+ {
+ }
+ else
+ {
+ AggregateDeclaration aad = parent.isAggregateDeclaration();
+ if (aad)
+ {
+ if (global.params.vfield && dsym.storage_class & (STC.const_ | STC.immutable_) && dsym._init && !dsym._init.isVoidInitializer())
+ {
+ const(char)* s = (dsym.storage_class & STC.immutable_) ? "immutable" : "const";
+ message(dsym.loc, "`%s.%s` is `%s` field", ad.toPrettyChars(), dsym.toChars(), s);
+ }
+ dsym.storage_class |= STC.field;
+ if (auto ts = tbn.isTypeStruct())
+ if (ts.sym.noDefaultCtor)
+ {
+ if (!dsym.isThisDeclaration() && !dsym._init)
+ aad.noDefaultCtor = true;
+ }
+ }
+
+ InterfaceDeclaration id = parent.isInterfaceDeclaration();
+ if (id)
+ {
+ dsym.error("field not allowed in interface");
+ }
+ else if (aad && aad.sizeok == Sizeok.done)
+ {
+ dsym.error("cannot be further field because it will change the determined %s size", aad.toChars());
+ }
+
+ /* Templates cannot add fields to aggregates
+ */
+ TemplateInstance ti = parent.isTemplateInstance();
+ if (ti)
+ {
+ // Take care of nested templates
+ while (1)
+ {
+ TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
+ if (!ti2)
+ break;
+ ti = ti2;
+ }
+ // If it's a member template
+ AggregateDeclaration ad2 = ti.tempdecl.isMember();
+ if (ad2 && dsym.storage_class != STC.undefined_)
+ {
+ dsym.error("cannot use template to add field to aggregate `%s`", ad2.toChars());
+ }
+ }
+ }
+
+ if ((dsym.storage_class & (STC.ref_ | STC.parameter | STC.foreach_ | STC.temp | STC.result)) == STC.ref_ && dsym.ident != Id.This)
+ {
+ dsym.error("only parameters or `foreach` declarations can be `ref`");
+ }
+
+ if (dsym.type.hasWild())
+ {
+ if (dsym.storage_class & (STC.static_ | STC.extern_ | STC.tls | STC.gshared | STC.manifest | STC.field) || dsym.isDataseg())
+ {
+ dsym.error("only parameters or stack based variables can be `inout`");
+ }
+ FuncDeclaration func = sc.func;
+ if (func)
+ {
+ if (func.fes)
+ func = func.fes.func;
+ bool isWild = false;
+ for (FuncDeclaration fd = func; fd; fd = fd.toParentDecl().isFuncDeclaration())
+ {
+ if (fd.type.isTypeFunction().iswild)
+ {
+ isWild = true;
+ break;
+ }
+ }
+ if (!isWild)
+ {
+ dsym.error("`inout` variables can only be declared inside `inout` functions");
+ }
+ }
+ }
+
+ if (!(dsym.storage_class & (STC.ctfe | STC.extern_ | STC.ref_ | STC.result)) &&
+ tbn.ty == Tstruct && tbn.isTypeStruct().sym.noDefaultCtor)
+ {
+ if (!dsym._init)
+ {
+ if (dsym.isField())
+ {
+ /* For fields, we'll check the constructor later to make sure it is initialized
+ */
+ dsym.storage_class |= STC.nodefaultctor;
+ }
+ else if (dsym.storage_class & STC.parameter)
+ {
+ }
+ else
+ dsym.error("default construction is disabled for type `%s`", dsym.type.toChars());
+ }
+ }
+
+ FuncDeclaration fd = parent.isFuncDeclaration();
+ if (dsym.type.isscope() && !(dsym.storage_class & STC.nodtor))
+ {
+ if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.tls | STC.gshared) || !fd)
+ {
+ dsym.error("globals, statics, fields, manifest constants, ref and out parameters cannot be `scope`");
+ }
+
+ // @@@DEPRECATED@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+ // Deprecated in 2.087
+ // Remove this when the feature is removed from the language
+ if (0 && // deprecation disabled for now to accommodate existing extensive use
+ !(dsym.storage_class & STC.scope_))
+ {
+ if (!(dsym.storage_class & STC.parameter) && dsym.ident != Id.withSym)
+ dsym.error("reference to `scope class` must be `scope`");
+ }
+ }
+
+ // Calculate type size + safety checks
+ if (sc.func && !sc.intypeof)
+ {
+ if (dsym._init && dsym._init.isVoidInitializer() &&
+ (dsym.type.hasPointers() || dsym.type.hasInvariant())) // also computes type size
+ {
+ if (sc.func.setUnsafe())
+ {
+ if (dsym.type.hasPointers())
+ dsym.error("`void` initializers for pointers not allowed in safe functions");
+ else
+ dsym.error("`void` initializers for structs with invariants are not allowed in safe functions");
+ }
+ }
+ else if (!dsym._init &&
+ !(dsym.storage_class & (STC.static_ | STC.extern_ | STC.tls | STC.gshared | STC.manifest | STC.field | STC.parameter)) &&
+ dsym.type.hasVoidInitPointers())
+ {
+ if (sc.func.setUnsafe())
+ dsym.error("`void` initializers for pointers not allowed in safe functions");
+ }
+ }
+
+ if ((!dsym._init || dsym._init.isVoidInitializer) && !fd)
+ {
+ // If not mutable, initializable by constructor only
+ dsym.storage_class |= STC.ctorinit;
+ }
+
+ if (dsym._init)
+ dsym.storage_class |= STC.init; // remember we had an explicit initializer
+ else if (dsym.storage_class & STC.manifest)
+ dsym.error("manifest constants must have initializers");
+
+ bool isBlit = false;
+ d_uns64 sz;
+ if (!dsym._init &&
+ !(dsym.storage_class & (STC.static_ | STC.gshared | STC.extern_)) &&
+ fd &&
+ (!(dsym.storage_class & (STC.field | STC.in_ | STC.foreach_ | STC.parameter | STC.result)) ||
+ (dsym.storage_class & STC.out_)) &&
+ (sz = dsym.type.size()) != 0)
+ {
+ // Provide a default initializer
+
+ //printf("Providing default initializer for '%s'\n", toChars());
+ if (sz == SIZE_INVALID && dsym.type.ty != Terror)
+ dsym.error("size of type `%s` is invalid", dsym.type.toChars());
+
+ Type tv = dsym.type;
+ while (tv.ty == Tsarray) // Don't skip Tenum
+ tv = tv.nextOf();
+ if (tv.needsNested())
+ {
+ /* Nested struct requires valid enclosing frame pointer.
+ * In StructLiteralExp::toElem(), it's calculated.
+ */
+ assert(tbn.ty == Tstruct);
+ checkFrameAccess(dsym.loc, sc, tbn.isTypeStruct().sym);
+
+ Expression e = tv.defaultInitLiteral(dsym.loc);
+ e = new BlitExp(dsym.loc, new VarExp(dsym.loc, dsym), e);
+ e = e.expressionSemantic(sc);
+ dsym._init = new ExpInitializer(dsym.loc, e);
+ goto Ldtor;
+ }
+ if (tv.ty == Tstruct && tv.isTypeStruct().sym.zeroInit)
+ {
+ /* If a struct is all zeros, as a special case
+ * set its initializer to the integer 0.
+ * In AssignExp::toElem(), we check for this and issue
+ * a memset() to initialize the struct.
+ * Must do same check in interpreter.
+ */
+ Expression e = IntegerExp.literal!0;
+ e = new BlitExp(dsym.loc, new VarExp(dsym.loc, dsym), e);
+ e.type = dsym.type; // don't type check this, it would fail
+ dsym._init = new ExpInitializer(dsym.loc, e);
+ goto Ldtor;
+ }
+ if (dsym.type.baseElemOf().ty == Tvoid)
+ {
+ dsym.error("`%s` does not have a default initializer", dsym.type.toChars());
+ }
+ else if (auto e = dsym.type.defaultInit(dsym.loc))
+ {
+ dsym._init = new ExpInitializer(dsym.loc, e);
+ }
+
+ // Default initializer is always a blit
+ isBlit = true;
+ }
+ if (dsym._init)
+ {
+ sc = sc.push();
+ sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable);
+
+ ExpInitializer ei = dsym._init.isExpInitializer();
+
+ if (ei) // https://issues.dlang.org/show_bug.cgi?id=13424
+ // Preset the required type to fail in FuncLiteralDeclaration::semantic3
+ ei.exp = inferType(ei.exp, dsym.type);
+
+ // If inside function, there is no semantic3() call
+ if (sc.func || sc.intypeof == 1)
+ {
+ // If local variable, use AssignExp to handle all the various
+ // possibilities.
+ if (fd && !(dsym.storage_class & (STC.manifest | STC.static_ | STC.tls | STC.gshared | STC.extern_)) && !dsym._init.isVoidInitializer())
+ {
+ //printf("fd = '%s', var = '%s'\n", fd.toChars(), toChars());
+ if (!ei)
+ {
+ ArrayInitializer ai = dsym._init.isArrayInitializer();
+ Expression e;
+ if (ai && tb.ty == Taarray)
+ e = ai.toAssocArrayLiteral();
+ else
+ e = dsym._init.initializerToExpression();
+ if (!e)
+ {
+ // Run semantic, but don't need to interpret
+ dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITnointerpret);
+ e = dsym._init.initializerToExpression();
+ if (!e)
+ {
+ dsym.error("is not a static and cannot have static initializer");
+ e = ErrorExp.get();
+ }
+ }
+ ei = new ExpInitializer(dsym._init.loc, e);
+ dsym._init = ei;
+ }
+
+ Expression exp = ei.exp;
+ Expression e1 = new VarExp(dsym.loc, dsym);
+ if (isBlit)
+ exp = new BlitExp(dsym.loc, e1, exp);
+ else
+ exp = new ConstructExp(dsym.loc, e1, exp);
+ dsym.canassign++;
+ exp = exp.expressionSemantic(sc);
+ dsym.canassign--;
+ exp = exp.optimize(WANTvalue);
+ if (exp.op == TOK.error)
+ {
+ dsym._init = new ErrorInitializer();
+ ei = null;
+ }
+ else
+ ei.exp = exp;
+
+ if (ei && dsym.isScope())
+ {
+ Expression ex = ei.exp.lastComma();
+ if (ex.op == TOK.blit || ex.op == TOK.construct)
+ ex = (cast(AssignExp)ex).e2;
+ if (auto ne = ex.isNewExp())
+ {
+ // See if initializer is a NewExp that can be allocated on the stack
+ if (dsym.type.toBasetype().ty == Tclass)
+ {
+ if (ne.newargs && ne.newargs.dim > 1)
+ {
+ dsym.mynew = true;
+ }
+ else
+ {
+ ne.onstack = 1;
+ dsym.onstack = true;
+ }
+ }
+ }
+ else if (auto fe = ex.isFuncExp())
+ {
+ // or a delegate that doesn't escape a reference to the function
+ FuncDeclaration f = fe.fd;
+ if (f.tookAddressOf)
+ f.tookAddressOf--;
+ }
+ }
+ }
+ else
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14166
+ // Don't run CTFE for the temporary variables inside typeof
+ dsym._init = dsym._init.initializerSemantic(sc, dsym.type, sc.intypeof == 1 ? INITnointerpret : INITinterpret);
+ const init_err = dsym._init.isExpInitializer();
+ if (init_err && init_err.exp.op == TOK.showCtfeContext)
+ {
+ errorSupplemental(dsym.loc, "compile time context created here");
+ }
+ }
+ }
+ else if (parent.isAggregateDeclaration())
+ {
+ dsym._scope = scx ? scx : sc.copy();
+ dsym._scope.setNoFree();
+ }
+ else if (dsym.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) ||
+ dsym.type.isConst() || dsym.type.isImmutable() ||
+ sc.flags & SCOPE.Cfile)
+ {
+ /* Because we may need the results of a const declaration in a
+ * subsequent type, such as an array dimension, before semantic2()
+ * gets ordinarily run, try to run semantic2() now.
+ * If a C array is of unknown size, the initializer can provide the size. Do this
+ * eagerly because C does it eagerly.
+ * Ignore failure.
+ */
+ if (!inferred)
+ {
+ uint errors = global.errors;
+ dsym.inuse++;
+ // Bug 20549. Don't try this on modules or packages, syntaxCopy
+ // could crash (inf. recursion) on a mod/pkg referencing itself
+ if (ei && (ei.exp.op != TOK.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
+ {
+ Expression exp = ei.exp.syntaxCopy();
+
+ bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest);
+ if (needctfe)
+ sc = sc.startCTFE();
+ exp = exp.expressionSemantic(sc);
+ exp = resolveProperties(sc, exp);
+ if (needctfe)
+ sc = sc.endCTFE();
+
+ Type tb2 = dsym.type.toBasetype();
+ Type ti = exp.type.toBasetype();
+
+ /* The problem is the following code:
+ * struct CopyTest {
+ * double x;
+ * this(double a) { x = a * 10.0;}
+ * this(this) { x += 2.0; }
+ * }
+ * const CopyTest z = CopyTest(5.3); // ok
+ * const CopyTest w = z; // not ok, postblit not run
+ * static assert(w.x == 55.0);
+ * because the postblit doesn't get run on the initialization of w.
+ */
+ if (auto ts = ti.isTypeStruct())
+ {
+ StructDeclaration sd = ts.sym;
+ /* Look to see if initializer involves a copy constructor
+ * (which implies a postblit)
+ */
+ // there is a copy constructor
+ // and exp is the same struct
+ if (sd.postblit && tb2.toDsymbol(null) == sd)
+ {
+ // The only allowable initializer is a (non-copy) constructor
+ if (exp.isLvalue())
+ dsym.error("of type struct `%s` uses `this(this)`, which is not allowed in static initialization", tb2.toChars());
+ }
+ }
+ ei.exp = exp;
+ }
+
+ dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
+ dsym.inuse--;
+ if (global.errors > errors)
+ {
+ dsym._init = new ErrorInitializer();
+ dsym.type = Type.terror;
+ }
+ }
+ else
+ {
+ dsym._scope = scx ? scx : sc.copy();
+ dsym._scope.setNoFree();
+ }
+ }
+ sc = sc.pop();
+ }
+
+ Ldtor:
+ /* Build code to execute destruction, if necessary
+ */
+ dsym.edtor = dsym.callScopeDtor(sc);
+ if (dsym.edtor)
+ {
+ /* If dsym is a local variable, who's type is a struct with a scope destructor,
+ * then make dsym scope, too.
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled &&
+ !(dsym.storage_class & (STC.parameter | STC.temp | STC.field | STC.in_ | STC.foreach_ | STC.result | STC.manifest)) &&
+ !dsym.isDataseg() &&
+ !dsym.doNotInferScope &&
+ dsym.type.hasPointers())
+ {
+ auto tv = dsym.type.baseElemOf();
+ if (tv.ty == Tstruct &&
+ tv.isTypeStruct().sym.dtor.storage_class & STC.scope_)
+ {
+ dsym.storage_class |= STC.scope_;
+ }
+ }
+
+ if (sc.func && dsym.storage_class & (STC.static_ | STC.gshared))
+ dsym.edtor = dsym.edtor.expressionSemantic(sc._module._scope);
+ else
+ dsym.edtor = dsym.edtor.expressionSemantic(sc);
+
+ version (none)
+ {
+ // currently disabled because of std.stdio.stdin, stdout and stderr
+ if (dsym.isDataseg() && !(dsym.storage_class & STC.extern_))
+ dsym.error("static storage variables cannot have destructors");
+ }
+ }
+
+ dsym.semanticRun = PASS.semanticdone;
+
+ if (dsym.type.toBasetype().ty == Terror)
+ dsym.errors = true;
+
+ if(sc.scopesym && !sc.scopesym.isAggregateDeclaration())
+ {
+ for (ScopeDsymbol sym = sc.scopesym; sym && dsym.endlinnum == 0;
+ sym = sym.parent ? sym.parent.isScopeDsymbol() : null)
+ dsym.endlinnum = sym.endlinnum;
+ }
+ }
+
+ override void visit(TypeInfoDeclaration dsym)
+ {
+ assert(dsym.linkage == LINK.c);
+ }
+
+ override void visit(BitFieldDeclaration dsym)
+ {
+ //printf("BitField::semantic('%s') %s\n", toPrettyChars(), id.toChars());
+ if (dsym.semanticRun >= PASS.semanticdone)
+ return;
+
+ visit(cast(VarDeclaration)dsym);
+ if (dsym.errors)
+ return;
+
+ sc = sc.startCTFE();
+ auto width = dsym.width.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ width = width.ctfeInterpret();
+ if (!dsym.type.isintegral())
+ {
+ // C11 6.7.2.1-5
+ width.error("bit-field type `%s` is not an integer type", dsym.type.toChars());
+ dsym.errors = true;
+ }
+ if (!width.isIntegerExp())
+ {
+ width.error("bit-field width `%s` is not an integer constant", dsym.width.toChars());
+ dsym.errors = true;
+ }
+ const uwidth = width.toInteger(); // uwidth is unsigned
+ if (uwidth == 0 && !dsym.isAnonymous())
+ {
+ width.error("bit-field `%s` has zero width", dsym.toChars());
+ dsym.errors = true;
+ }
+ const max_width = dsym.type.size() * 8;
+ if (uwidth > max_width)
+ {
+ width.error("width `%lld` of bit-field `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
+ dsym.errors = true;
+ }
+ dsym.fieldWidth = cast(uint)uwidth;
+ }
+
+ override void visit(Import imp)
+ {
+ //printf("Import::semantic('%s') %s\n", toPrettyChars(), id.toChars());
+ if (imp.semanticRun > PASS.init)
+ return;
+
+ if (imp._scope)
+ {
+ sc = imp._scope;
+ imp._scope = null;
+ }
+ if (!sc)
+ return;
+
+ imp.parent = sc.parent;
+
+ imp.semanticRun = PASS.semantic;
+
+ // Load if not already done so
+ bool loadErrored = false;
+ if (!imp.mod)
+ {
+ loadErrored = imp.load(sc);
+ if (imp.mod)
+ {
+ imp.mod.importAll(null);
+ imp.mod.checkImportDeprecation(imp.loc, sc);
+ }
+ }
+ if (imp.mod)
+ {
+ // Modules need a list of each imported module
+
+ // if inside a template instantiation, the instantianting
+ // module gets the import.
+ // https://issues.dlang.org/show_bug.cgi?id=17181
+ Module importer = sc._module;
+ if (sc.minst && sc.tinst)
+ {
+ importer = sc.minst;
+ if (!sc.tinst.importedModules.contains(imp.mod))
+ sc.tinst.importedModules.push(imp.mod);
+ }
+ //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars());
+ if (!importer.aimports.contains(imp.mod))
+ importer.aimports.push(imp.mod);
+
+ if (sc.explicitVisibility)
+ imp.visibility = sc.visibility;
+
+ if (!imp.aliasId && !imp.names.dim) // neither a selective nor a renamed import
+ {
+ ScopeDsymbol scopesym = sc.getScopesym();
+
+ if (!imp.isstatic)
+ {
+ scopesym.importScope(imp.mod, imp.visibility);
+ }
+
+
+ imp.addPackageAccess(scopesym);
+ }
+
+ if (!loadErrored)
+ {
+ imp.mod.dsymbolSemantic(null);
+ }
+
+ if (imp.mod.needmoduleinfo)
+ {
+ //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars());
+ importer.needmoduleinfo = 1;
+ }
+
+ sc = sc.push(imp.mod);
+ sc.visibility = imp.visibility;
+ for (size_t i = 0; i < imp.aliasdecls.dim; i++)
+ {
+ AliasDeclaration ad = imp.aliasdecls[i];
+ //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope);
+ Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], IgnorePrivateImports);
+ if (sym)
+ {
+ import dmd.access : symbolIsVisible;
+ if (!symbolIsVisible(sc, sym))
+ imp.mod.error(imp.loc, "member `%s` is not visible from module `%s`",
+ imp.names[i].toChars(), sc._module.toChars());
+ ad.dsymbolSemantic(sc);
+ // If the import declaration is in non-root module,
+ // analysis of the aliased symbol is deferred.
+ // Therefore, don't see the ad.aliassym or ad.type here.
+ }
+ else
+ {
+ Dsymbol s = imp.mod.search_correct(imp.names[i]);
+ if (s)
+ imp.mod.error(imp.loc, "import `%s` not found, did you mean %s `%s`?", imp.names[i].toChars(), s.kind(), s.toPrettyChars());
+ else
+ imp.mod.error(imp.loc, "import `%s` not found", imp.names[i].toChars());
+ ad.type = Type.terror;
+ }
+ }
+ sc = sc.pop();
+ }
+
+ imp.semanticRun = PASS.semanticdone;
+
+ // object self-imports itself, so skip that
+ // https://issues.dlang.org/show_bug.cgi?id=7547
+ // don't list pseudo modules __entrypoint.d, __main.d
+ // https://issues.dlang.org/show_bug.cgi?id=11117
+ // https://issues.dlang.org/show_bug.cgi?id=11164
+ if (global.params.moduleDeps !is null && !(imp.id == Id.object && sc._module.ident == Id.object) &&
+ strcmp(sc._module.ident.toChars(), "__main") != 0)
+ {
+ /* The grammar of the file is:
+ * ImportDeclaration
+ * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> "
+ * ModuleAliasIdentifier ] "\n"
+ *
+ * BasicImportDeclaration
+ * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string"
+ * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")"
+ *
+ * FilePath
+ * - any string with '(', ')' and '\' escaped with the '\' character
+ */
+ OutBuffer* ob = global.params.moduleDeps;
+ Module imod = sc._module;
+ if (!global.params.moduleDepsFile)
+ ob.writestring("depsImport ");
+ ob.writestring(imod.toPrettyChars());
+ ob.writestring(" (");
+ escapePath(ob, imod.srcfile.toChars());
+ ob.writestring(") : ");
+ // use visibility instead of sc.visibility because it couldn't be
+ // resolved yet, see the comment above
+ visibilityToBuffer(ob, imp.visibility);
+ ob.writeByte(' ');
+ if (imp.isstatic)
+ {
+ stcToBuffer(ob, STC.static_);
+ ob.writeByte(' ');
+ }
+ ob.writestring(": ");
+ foreach (pid; imp.packages)
+ {
+ ob.printf("%s.", pid.toChars());
+ }
+ ob.writestring(imp.id.toString());
+ ob.writestring(" (");
+ if (imp.mod)
+ escapePath(ob, imp.mod.srcfile.toChars());
+ else
+ ob.writestring("???");
+ ob.writeByte(')');
+ foreach (i, name; imp.names)
+ {
+ if (i == 0)
+ ob.writeByte(':');
+ else
+ ob.writeByte(',');
+ Identifier _alias = imp.aliases[i];
+ if (!_alias)
+ {
+ ob.printf("%s", name.toChars());
+ _alias = name;
+ }
+ else
+ ob.printf("%s=%s", _alias.toChars(), name.toChars());
+ }
+ if (imp.aliasId)
+ ob.printf(" -> %s", imp.aliasId.toChars());
+ ob.writenl();
+ }
+ //printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg);
+ }
+
+ void attribSemantic(AttribDeclaration ad)
+ {
+ if (ad.semanticRun != PASS.init)
+ return;
+ ad.semanticRun = PASS.semantic;
+ Dsymbols* d = ad.include(sc);
+ //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
+ if (d)
+ {
+ Scope* sc2 = ad.newScope(sc);
+ bool errors;
+ for (size_t i = 0; i < d.dim; i++)
+ {
+ Dsymbol s = (*d)[i];
+ s.dsymbolSemantic(sc2);
+ errors |= s.errors;
+ }
+ ad.errors |= errors;
+ if (sc2 != sc)
+ sc2.pop();
+ }
+ ad.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(AttribDeclaration atd)
+ {
+ attribSemantic(atd);
+ }
+
+ override void visit(AnonDeclaration scd)
+ {
+ //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this);
+ assert(sc.parent);
+ auto p = sc.parent.pastMixin();
+ auto ad = p.isAggregateDeclaration();
+ if (!ad)
+ {
+ error(scd.loc, "%s can only be a part of an aggregate, not %s `%s`", scd.kind(), p.kind(), p.toChars());
+ scd.errors = true;
+ return;
+ }
+
+ if (scd.decl)
+ {
+ sc = sc.push();
+ sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.tls | STC.gshared);
+ sc.inunion = scd.isunion ? scd : null;
+ sc.flags = 0;
+ for (size_t i = 0; i < scd.decl.dim; i++)
+ {
+ Dsymbol s = (*scd.decl)[i];
+ s.dsymbolSemantic(sc);
+ }
+ sc = sc.pop();
+ }
+ }
+
+ override void visit(PragmaDeclaration pd)
+ {
+ StringExp verifyMangleString(ref Expression e)
+ {
+ auto se = semanticString(sc, e, "mangled name");
+ if (!se)
+ return null;
+ e = se;
+ if (!se.len)
+ {
+ pd.error("zero-length string not allowed for mangled name");
+ return null;
+ }
+ if (se.sz != 1)
+ {
+ pd.error("mangled name characters can only be of type `char`");
+ return null;
+ }
+ version (all)
+ {
+ /* Note: D language specification should not have any assumption about backend
+ * implementation. Ideally pragma(mangle) can accept a string of any content.
+ *
+ * Therefore, this validation is compiler implementation specific.
+ */
+ auto slice = se.peekString();
+ for (size_t i = 0; i < se.len;)
+ {
+ dchar c = slice[i];
+ if (c < 0x80)
+ {
+ if (c.isValidMangling)
+ {
+ ++i;
+ continue;
+ }
+ else
+ {
+ pd.error("char 0x%02x not allowed in mangled name", c);
+ break;
+ }
+ }
+ if (const msg = utf_decodeChar(slice, i, c))
+ {
+ pd.error("%.*s", cast(int)msg.length, msg.ptr);
+ break;
+ }
+ if (!isUniAlpha(c))
+ {
+ pd.error("char `0x%04x` not allowed in mangled name", c);
+ break;
+ }
+ }
+ }
+ return se;
+ }
+ void declarations()
+ {
+ if (!pd.decl)
+ return;
+
+ Scope* sc2 = pd.newScope(sc);
+ scope(exit)
+ if (sc2 != sc)
+ sc2.pop();
+
+ foreach (s; (*pd.decl)[])
+ {
+ s.dsymbolSemantic(sc2);
+ if (pd.ident != Id.mangle)
+ continue;
+ assert(pd.args);
+ if (auto ad = s.isAggregateDeclaration())
+ {
+ Expression e = (*pd.args)[0];
+ sc2 = sc2.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc2, e);
+ sc2 = sc2.endCTFE();
+ AggregateDeclaration agg;
+ if (auto tc = e.type.isTypeClass())
+ agg = tc.sym;
+ else if (auto ts = e.type.isTypeStruct())
+ agg = ts.sym;
+ ad.mangleOverride = new MangleOverride;
+ void setString(ref Expression e)
+ {
+ if (auto se = verifyMangleString(e))
+ {
+ const name = (cast(const(char)[])se.peekData()).xarraydup;
+ ad.mangleOverride.id = Identifier.idPool(name);
+ e = se;
+ }
+ else
+ e.error("must be a string");
+ }
+ if (agg)
+ {
+ ad.mangleOverride.agg = agg;
+ if (pd.args.dim == 2)
+ {
+ setString((*pd.args)[1]);
+ }
+ else
+ ad.mangleOverride.id = agg.ident;
+ }
+ else
+ setString((*pd.args)[0]);
+ }
+ else if (auto td = s.isTemplateDeclaration())
+ {
+ pd.error("cannot apply to a template declaration");
+ errorSupplemental(pd.loc, "use `template Class(Args...){ pragma(mangle, \"other_name\") class Class {} }`");
+ }
+ else if (auto se = verifyMangleString((*pd.args)[0]))
+ {
+ const name = (cast(const(char)[])se.peekData()).xarraydup;
+ uint cnt = setMangleOverride(s, name);
+ if (cnt > 1)
+ pd.error("can only apply to a single declaration");
+ }
+ }
+ }
+
+ void noDeclarations()
+ {
+ if (pd.decl)
+ {
+ pd.error("is missing a terminating `;`");
+ declarations();
+ // do them anyway, to avoid segfaults.
+ }
+ }
+
+ // Should be merged with PragmaStatement
+ //printf("\tPragmaDeclaration::semantic '%s'\n", pd.toChars());
+ if (target.mscoff)
+ {
+ if (pd.ident == Id.linkerDirective)
+ {
+ if (!pd.args || pd.args.dim != 1)
+ pd.error("one string argument expected for pragma(linkerDirective)");
+ else
+ {
+ auto se = semanticString(sc, (*pd.args)[0], "linker directive");
+ if (!se)
+ return noDeclarations();
+ (*pd.args)[0] = se;
+ if (global.params.verbose)
+ message("linkopt %.*s", cast(int)se.len, se.peekString().ptr);
+ }
+ return noDeclarations();
+ }
+ }
+ if (pd.ident == Id.msg)
+ {
+ if (pd.args)
+ {
+ for (size_t i = 0; i < pd.args.dim; i++)
+ {
+ Expression e = (*pd.args)[i];
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = ctfeInterpretForPragmaMsg(e);
+ if (e.op == TOK.error)
+ {
+ errorSupplemental(pd.loc, "while evaluating `pragma(msg, %s)`", (*pd.args)[i].toChars());
+ return;
+ }
+ StringExp se = e.toStringExp();
+ if (se)
+ {
+ se = se.toUTF8(sc);
+ fprintf(stderr, "%.*s", cast(int)se.len, se.peekString().ptr);
+ }
+ else
+ fprintf(stderr, "%s", e.toChars());
+ }
+ fprintf(stderr, "\n");
+ }
+ return noDeclarations();
+ }
+ else if (pd.ident == Id.lib)
+ {
+ if (!pd.args || pd.args.dim != 1)
+ pd.error("string expected for library name");
+ else
+ {
+ auto se = semanticString(sc, (*pd.args)[0], "library name");
+ if (!se)
+ return noDeclarations();
+ (*pd.args)[0] = se;
+
+ auto name = se.peekString().xarraydup;
+ if (global.params.verbose)
+ message("library %s", name.ptr);
+ if (global.params.moduleDeps && !global.params.moduleDepsFile)
+ {
+ OutBuffer* ob = global.params.moduleDeps;
+ Module imod = sc._module;
+ ob.writestring("depsLib ");
+ ob.writestring(imod.toPrettyChars());
+ ob.writestring(" (");
+ escapePath(ob, imod.srcfile.toChars());
+ ob.writestring(") : ");
+ ob.writestring(name);
+ ob.writenl();
+ }
+ mem.xfree(name.ptr);
+ }
+ return noDeclarations();
+ }
+ else if (pd.ident == Id.startaddress)
+ {
+ if (!pd.args || pd.args.dim != 1)
+ pd.error("function name expected for start address");
+ else
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=11980
+ * resolveProperties and ctfeInterpret call are not necessary.
+ */
+ Expression e = (*pd.args)[0];
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ (*pd.args)[0] = e;
+ Dsymbol sa = getDsymbol(e);
+ if (!sa || !sa.isFuncDeclaration())
+ pd.error("function name expected for start address, not `%s`", e.toChars());
+ }
+ return noDeclarations();
+ }
+ else if (pd.ident == Id.Pinline)
+ {
+ if (pd.args && pd.args.dim > 1)
+ {
+ pd.error("one boolean expression expected for `pragma(inline)`, not %llu", cast(ulong) pd.args.dim);
+ pd.args.setDim(1);
+ (*pd.args)[0] = ErrorExp.get();
+ }
+
+ // this pragma now gets evaluated on demand in function semantic
+
+ return declarations();
+ }
+ else if (pd.ident == Id.mangle)
+ {
+ if (!pd.args)
+ pd.args = new Expressions();
+ if (pd.args.dim == 0 || pd.args.dim > 2)
+ {
+ pd.error(pd.args.dim == 0 ? "string expected for mangled name"
+ : "expected 1 or 2 arguments");
+ pd.args.setDim(1);
+ (*pd.args)[0] = ErrorExp.get(); // error recovery
+ }
+ return declarations();
+ }
+ else if (pd.ident == Id.crt_constructor || pd.ident == Id.crt_destructor)
+ {
+ if (pd.args && pd.args.dim != 0)
+ pd.error("takes no argument");
+ return declarations();
+ }
+ else if (pd.ident == Id.printf || pd.ident == Id.scanf)
+ {
+ if (pd.args && pd.args.dim != 0)
+ pd.error("takes no argument");
+ return declarations();
+ }
+ else if (!global.params.ignoreUnsupportedPragmas)
+ {
+ error(pd.loc, "unrecognized `pragma(%s)`", pd.ident.toChars());
+ return declarations();
+ }
+
+ if (!global.params.verbose)
+ return declarations();
+
+ /* Print unrecognized pragmas
+ */
+ OutBuffer buf;
+ buf.writestring(pd.ident.toString());
+ if (pd.args)
+ {
+ const errors_save = global.startGagging();
+ for (size_t i = 0; i < pd.args.dim; i++)
+ {
+ Expression e = (*pd.args)[i];
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = e.ctfeInterpret();
+ if (i == 0)
+ buf.writestring(" (");
+ else
+ buf.writeByte(',');
+ buf.writestring(e.toChars());
+ }
+ if (pd.args.dim)
+ buf.writeByte(')');
+ global.endGagging(errors_save);
+ }
+ message("pragma %s", buf.peekChars());
+ return declarations();
+ }
+
+ override void visit(StaticIfDeclaration sid)
+ {
+ attribSemantic(sid);
+ }
+
+ override void visit(StaticForeachDeclaration sfd)
+ {
+ attribSemantic(sfd);
+ }
+
+ private Dsymbols* compileIt(CompileDeclaration cd)
+ {
+ //printf("CompileDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars());
+ OutBuffer buf;
+ if (expressionsToString(buf, sc, cd.exps))
+ return null;
+
+ const errors = global.errors;
+ const len = buf.length;
+ buf.writeByte(0);
+ const str = buf.extractSlice()[0 .. len];
+ scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false);
+ p.nextToken();
+
+ auto d = p.parseDeclDefs(0);
+ if (global.errors != errors)
+ return null;
+
+ if (p.token.value != TOK.endOfFile)
+ {
+ cd.error("incomplete mixin declaration `%s`", str.ptr);
+ return null;
+ }
+ return d;
+ }
+
+ /***********************************************************
+ * https://dlang.org/spec/module.html#mixin-declaration
+ */
+ override void visit(CompileDeclaration cd)
+ {
+ //printf("CompileDeclaration::semantic()\n");
+ if (!cd.compiled)
+ {
+ cd.decl = compileIt(cd);
+ cd.AttribDeclaration.addMember(sc, cd.scopesym);
+ cd.compiled = true;
+
+ if (cd._scope && cd.decl)
+ {
+ for (size_t i = 0; i < cd.decl.dim; i++)
+ {
+ Dsymbol s = (*cd.decl)[i];
+ s.setScope(cd._scope);
+ }
+ }
+ }
+ attribSemantic(cd);
+ }
+
+ override void visit(CPPNamespaceDeclaration ns)
+ {
+ Identifier identFromSE (StringExp se)
+ {
+ const sident = se.toStringz();
+ if (!sident.length || !Identifier.isValidIdentifier(sident))
+ {
+ ns.exp.error("expected valid identifier for C++ namespace but got `%.*s`",
+ cast(int)sident.length, sident.ptr);
+ return null;
+ }
+ else
+ return Identifier.idPool(sident);
+ }
+
+ if (ns.ident is null)
+ {
+ ns.cppnamespace = sc.namespace;
+ sc = sc.startCTFE();
+ ns.exp = ns.exp.expressionSemantic(sc);
+ ns.exp = resolveProperties(sc, ns.exp);
+ sc = sc.endCTFE();
+ ns.exp = ns.exp.ctfeInterpret();
+ // Can be either a tuple of strings or a string itself
+ if (auto te = ns.exp.isTupleExp())
+ {
+ expandTuples(te.exps);
+ CPPNamespaceDeclaration current = ns.cppnamespace;
+ for (size_t d = 0; d < te.exps.dim; ++d)
+ {
+ auto exp = (*te.exps)[d];
+ auto prev = d ? current : ns.cppnamespace;
+ current = (d + 1) != te.exps.dim
+ ? new CPPNamespaceDeclaration(ns.loc, exp, null)
+ : ns;
+ current.exp = exp;
+ current.cppnamespace = prev;
+ if (auto se = exp.toStringExp())
+ {
+ current.ident = identFromSE(se);
+ if (current.ident is null)
+ return; // An error happened in `identFromSE`
+ }
+ else
+ ns.exp.error("`%s`: index %llu is not a string constant, it is a `%s`",
+ ns.exp.toChars(), cast(ulong) d, ns.exp.type.toChars());
+ }
+ }
+ else if (auto se = ns.exp.toStringExp())
+ ns.ident = identFromSE(se);
+ // Empty Tuple
+ else if (ns.exp.isTypeExp() && ns.exp.isTypeExp().type.toBasetype().isTypeTuple())
+ {
+ }
+ else
+ ns.exp.error("compile time string constant (or tuple) expected, not `%s`",
+ ns.exp.toChars());
+ }
+ attribSemantic(ns);
+ }
+
+ override void visit(UserAttributeDeclaration uad)
+ {
+ //printf("UserAttributeDeclaration::semantic() %p\n", this);
+ if (uad.decl && !uad._scope)
+ uad.Dsymbol.setScope(sc); // for function local symbols
+ arrayExpressionSemantic(uad.atts, sc, true);
+ return attribSemantic(uad);
+ }
+
+ override void visit(StaticAssert sa)
+ {
+ if (sa.semanticRun < PASS.semanticdone)
+ sa.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(DebugSymbol ds)
+ {
+ //printf("DebugSymbol::semantic() %s\n", toChars());
+ if (ds.semanticRun < PASS.semanticdone)
+ ds.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(VersionSymbol vs)
+ {
+ if (vs.semanticRun < PASS.semanticdone)
+ vs.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(Package pkg)
+ {
+ if (pkg.semanticRun < PASS.semanticdone)
+ pkg.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(Module m)
+ {
+ if (m.semanticRun != PASS.init)
+ return;
+ //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
+ m.semanticRun = PASS.semantic;
+ // Note that modules get their own scope, from scratch.
+ // This is so regardless of where in the syntax a module
+ // gets imported, it is unaffected by context.
+ Scope* sc = m._scope; // see if already got one from importAll()
+ if (!sc)
+ {
+ sc = Scope.createGlobal(m); // create root scope
+ }
+
+ //printf("Module = %p, linkage = %d\n", sc.scopesym, sc.linkage);
+ // Pass 1 semantic routines: do public side of the definition
+ m.members.foreachDsymbol( (s)
+ {
+ //printf("\tModule('%s'): '%s'.dsymbolSemantic()\n", toChars(), s.toChars());
+ s.dsymbolSemantic(sc);
+ m.runDeferredSemantic();
+ });
+
+ if (m.userAttribDecl)
+ {
+ m.userAttribDecl.dsymbolSemantic(sc);
+ }
+ if (!m._scope)
+ {
+ sc = sc.pop();
+ sc.pop(); // 2 pops because Scope.createGlobal() created 2
+ }
+ m.semanticRun = PASS.semanticdone;
+ //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
+ }
+
+ override void visit(EnumDeclaration ed)
+ {
+ //printf("EnumDeclaration::semantic(sd = %p, '%s') %s\n", sc.scopesym, sc.scopesym.toChars(), ed.toChars());
+ //printf("EnumDeclaration::semantic() %p %s\n", this, ed.toChars());
+ if (ed.semanticRun >= PASS.semanticdone)
+ return; // semantic() already completed
+ if (ed.semanticRun == PASS.semantic)
+ {
+ assert(ed.memtype);
+ error(ed.loc, "circular reference to enum base type `%s`", ed.memtype.toChars());
+ ed.errors = true;
+ ed.semanticRun = PASS.semanticdone;
+ return;
+ }
+ uint dprogress_save = Module.dprogress;
+
+ Scope* scx = null;
+ if (ed._scope)
+ {
+ sc = ed._scope;
+ scx = ed._scope; // save so we don't make redundant copies
+ ed._scope = null;
+ }
+
+ if (!sc)
+ return;
+
+ ed.parent = sc.parent;
+ ed.type = ed.type.typeSemantic(ed.loc, sc);
+
+ ed.visibility = sc.visibility;
+ if (sc.stc & STC.deprecated_)
+ ed.isdeprecated = true;
+ ed.userAttribDecl = sc.userAttribDecl;
+ ed.cppnamespace = sc.namespace;
+
+ ed.semanticRun = PASS.semantic;
+ UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage);
+
+ if (!ed.members && !ed.memtype) // enum ident;
+ {
+ ed.semanticRun = PASS.semanticdone;
+ return;
+ }
+
+ if (!ed.symtab)
+ ed.symtab = new DsymbolTable();
+
+ /* The separate, and distinct, cases are:
+ * 1. enum { ... }
+ * 2. enum : memtype { ... }
+ * 3. enum ident { ... }
+ * 4. enum ident : memtype { ... }
+ * 5. enum ident : memtype;
+ * 6. enum ident;
+ */
+
+ if (ed.memtype)
+ {
+ ed.memtype = ed.memtype.typeSemantic(ed.loc, sc);
+
+ /* Check to see if memtype is forward referenced
+ */
+ if (auto te = ed.memtype.isTypeEnum())
+ {
+ auto sym = te.toDsymbol(sc).isEnumDeclaration();
+ // Special enums like __c_[u]long[long] are fine to forward reference
+ // see https://issues.dlang.org/show_bug.cgi?id=20599
+ if (!sym.isSpecial() && (!sym.memtype || !sym.members || !sym.symtab || sym._scope))
+ {
+ // memtype is forward referenced, so try again later
+ deferDsymbolSemantic(ed, scx);
+ Module.dprogress = dprogress_save;
+ //printf("\tdeferring %s\n", toChars());
+ ed.semanticRun = PASS.init;
+ return;
+ }
+ else
+ // Ensure that semantic is run to detect. e.g. invalid forward references
+ sym.dsymbolSemantic(sc);
+ }
+ if (ed.memtype.ty == Tvoid)
+ {
+ ed.error("base type must not be `void`");
+ ed.memtype = Type.terror;
+ }
+ if (ed.memtype.ty == Terror)
+ {
+ ed.errors = true;
+ // poison all the members
+ ed.members.foreachDsymbol( (s) { s.errors = true; } );
+ ed.semanticRun = PASS.semanticdone;
+ return;
+ }
+ }
+
+ if (!ed.members) // enum ident : memtype;
+ {
+ ed.semanticRun = PASS.semanticdone;
+ return;
+ }
+
+ if (ed.members.dim == 0)
+ {
+ ed.error("enum `%s` must have at least one member", ed.toChars());
+ ed.errors = true;
+ ed.semanticRun = PASS.semanticdone;
+ return;
+ }
+
+ if (!(sc.flags & SCOPE.Cfile)) // C enum remains incomplete until members are done
+ ed.semanticRun = PASS.semanticdone;
+
+ Module.dprogress++;
+
+ Scope* sce;
+ if (ed.isAnonymous())
+ sce = sc;
+ else
+ {
+ sce = sc.push(ed);
+ sce.parent = ed;
+ }
+ sce = sce.startCTFE();
+ sce.setNoFree(); // needed for getMaxMinValue()
+
+ /* Each enum member gets the sce scope
+ */
+ ed.members.foreachDsymbol( (s)
+ {
+ EnumMember em = s.isEnumMember();
+ if (em)
+ em._scope = sce;
+ });
+
+ /* addMember() is not called when the EnumDeclaration appears as a function statement,
+ * so we have to do what addMember() does and install the enum members in the right symbol
+ * table
+ */
+ addEnumMembers(ed, sc, sc.getScopesym());
+
+ if (sc.flags & SCOPE.Cfile)
+ {
+ /* C11 6.7.2.2
+ */
+ ed.memtype = Type.tint32; // C11 6.7.2.2-4 implementation defined
+ int nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0
+
+ void emSemantic(EnumMember em, ref int nextValue)
+ {
+ static void errorReturn(EnumMember em)
+ {
+ em.errors = true;
+ em.semanticRun = PASS.semanticdone;
+ }
+
+ em.semanticRun = PASS.semantic;
+ em.type = Type.tint32;
+ em.linkage = LINK.c;
+ em.storage_class |= STC.manifest;
+ if (em.value)
+ {
+ Expression e = em.value;
+ assert(e.dyncast() == DYNCAST.expression);
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ e = e.integralPromotions(sc);
+ e = e.ctfeInterpret();
+ if (e.op == TOK.error)
+ return errorReturn(em);
+ auto ie = e.isIntegerExp();
+ if (!ie)
+ {
+ // C11 6.7.2.2-2
+ em.error("enum member must be an integral constant expression, not `%s` of type `%s`", e.toChars(), e.type.toChars());
+ return errorReturn(em);
+ }
+ const sinteger_t v = ie.toInteger();
+ if (v < int.min || v > uint.max)
+ {
+ // C11 6.7.2.2-2
+ em.error("enum member value `%s` does not fit in an `int`", e.toChars());
+ return errorReturn(em);
+ }
+ em.value = new IntegerExp(em.loc, cast(int)v, Type.tint32);
+ nextValue = cast(int)v;
+ }
+ else
+ {
+ em.value = new IntegerExp(em.loc, nextValue, Type.tint32);
+ }
+ ++nextValue; // C11 6.7.2.2-3 add 1 to value of previous enumeration constant
+ em.semanticRun = PASS.semanticdone;
+ }
+
+ ed.members.foreachDsymbol( (s)
+ {
+ if (EnumMember em = s.isEnumMember())
+ emSemantic(em, nextValue);
+ });
+ ed.semanticRun = PASS.semanticdone;
+ return;
+ }
+
+ ed.members.foreachDsymbol( (s)
+ {
+ if (EnumMember em = s.isEnumMember())
+ em.dsymbolSemantic(em._scope);
+ });
+ //printf("defaultval = %lld\n", defaultval);
+
+ //if (defaultval) printf("defaultval: %s %s\n", defaultval.toChars(), defaultval.type.toChars());
+ //printf("members = %s\n", members.toChars());
+ }
+
+ override void visit(EnumMember em)
+ {
+ //printf("EnumMember::semantic() %s\n", em.toChars());
+
+ void errorReturn()
+ {
+ em.errors = true;
+ em.semanticRun = PASS.semanticdone;
+ }
+
+ if (em.errors || em.semanticRun >= PASS.semanticdone)
+ return;
+ if (em.semanticRun == PASS.semantic)
+ {
+ em.error("circular reference to `enum` member");
+ return errorReturn();
+ }
+ assert(em.ed);
+
+ em.ed.dsymbolSemantic(sc);
+ if (em.ed.errors)
+ return errorReturn();
+ if (em.errors || em.semanticRun >= PASS.semanticdone)
+ return;
+
+ if (em._scope)
+ sc = em._scope;
+ if (!sc)
+ return;
+
+ em.semanticRun = PASS.semantic;
+
+ em.visibility = em.ed.isAnonymous() ? em.ed.visibility : Visibility(Visibility.Kind.public_);
+ em.linkage = LINK.d;
+ em.storage_class |= STC.manifest;
+
+ // https://issues.dlang.org/show_bug.cgi?id=9701
+ if (em.ed.isAnonymous())
+ {
+ if (em.userAttribDecl)
+ em.userAttribDecl.userAttribDecl = em.ed.userAttribDecl;
+ else
+ em.userAttribDecl = em.ed.userAttribDecl;
+ }
+
+ // Eval UDA in this same scope. Issues 19344, 20835, 21122
+ if (em.userAttribDecl)
+ {
+ // Set scope but avoid extra sc.uda attachment inside setScope()
+ auto inneruda = em.userAttribDecl.userAttribDecl;
+ em.userAttribDecl.setScope(sc);
+ em.userAttribDecl.userAttribDecl = inneruda;
+ }
+
+ // The first enum member is special
+ bool first = (em == (*em.ed.members)[0]);
+
+ if (em.origType)
+ {
+ em.origType = em.origType.typeSemantic(em.loc, sc);
+ em.type = em.origType;
+ assert(em.value); // "type id;" is not a valid enum member declaration
+ }
+
+ if (em.value)
+ {
+ Expression e = em.value;
+ assert(e.dyncast() == DYNCAST.expression);
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ e = e.ctfeInterpret();
+ if (e.op == TOK.error)
+ return errorReturn();
+ if (first && !em.ed.memtype && !em.ed.isAnonymous())
+ {
+ em.ed.memtype = e.type;
+ if (em.ed.memtype.ty == Terror)
+ {
+ em.ed.errors = true;
+ return errorReturn();
+ }
+ if (em.ed.memtype.ty != Terror)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=11746
+ * All of named enum members should have same type
+ * with the first member. If the following members were referenced
+ * during the first member semantic, their types should be unified.
+ */
+ em.ed.members.foreachDsymbol( (s)
+ {
+ EnumMember enm = s.isEnumMember();
+ if (!enm || enm == em || enm.semanticRun < PASS.semanticdone || enm.origType)
+ return;
+
+ //printf("[%d] em = %s, em.semanticRun = %d\n", i, toChars(), em.semanticRun);
+ Expression ev = enm.value;
+ ev = ev.implicitCastTo(sc, em.ed.memtype);
+ ev = ev.ctfeInterpret();
+ ev = ev.castTo(sc, em.ed.type);
+ if (ev.op == TOK.error)
+ em.ed.errors = true;
+ enm.value = ev;
+ });
+
+ if (em.ed.errors)
+ {
+ em.ed.memtype = Type.terror;
+ return errorReturn();
+ }
+ }
+ }
+
+ if (em.ed.memtype && !em.origType)
+ {
+ e = e.implicitCastTo(sc, em.ed.memtype);
+ e = e.ctfeInterpret();
+
+ // save origValue for better json output
+ em.origValue = e;
+
+ if (!em.ed.isAnonymous())
+ {
+ e = e.castTo(sc, em.ed.type.addMod(e.type.mod)); // https://issues.dlang.org/show_bug.cgi?id=12385
+ e = e.ctfeInterpret();
+ }
+ }
+ else if (em.origType)
+ {
+ e = e.implicitCastTo(sc, em.origType);
+ e = e.ctfeInterpret();
+ assert(em.ed.isAnonymous());
+
+ // save origValue for better json output
+ em.origValue = e;
+ }
+ em.value = e;
+ }
+ else if (first)
+ {
+ Type t;
+ if (em.ed.memtype)
+ t = em.ed.memtype;
+ else
+ {
+ t = Type.tint32;
+ if (!em.ed.isAnonymous())
+ em.ed.memtype = t;
+ }
+ Expression e = new IntegerExp(em.loc, 0, t);
+ e = e.ctfeInterpret();
+
+ // save origValue for better json output
+ em.origValue = e;
+
+ if (!em.ed.isAnonymous())
+ {
+ e = e.castTo(sc, em.ed.type);
+ e = e.ctfeInterpret();
+ }
+ em.value = e;
+ }
+ else
+ {
+ /* Find the previous enum member,
+ * and set this to be the previous value + 1
+ */
+ EnumMember emprev = null;
+ em.ed.members.foreachDsymbol( (s)
+ {
+ if (auto enm = s.isEnumMember())
+ {
+ if (enm == em)
+ return 1; // found
+ emprev = enm;
+ }
+ return 0; // continue
+ });
+
+ assert(emprev);
+ if (emprev.semanticRun < PASS.semanticdone) // if forward reference
+ emprev.dsymbolSemantic(emprev._scope); // resolve it
+ if (emprev.errors)
+ return errorReturn();
+
+ Expression eprev = emprev.value;
+ // .toHeadMutable() due to https://issues.dlang.org/show_bug.cgi?id=18645
+ Type tprev = eprev.type.toHeadMutable().equals(em.ed.type.toHeadMutable())
+ ? em.ed.memtype
+ : eprev.type;
+
+ Expression emax = tprev.getProperty(sc, em.ed.loc, Id.max, 0);
+ emax = emax.expressionSemantic(sc);
+ emax = emax.ctfeInterpret();
+
+ // Set value to (eprev + 1).
+ // But first check that (eprev != emax)
+ assert(eprev);
+ Expression e = new EqualExp(TOK.equal, em.loc, eprev, emax);
+ e = e.expressionSemantic(sc);
+ e = e.ctfeInterpret();
+ if (e.toInteger())
+ {
+ em.error("initialization with `%s.%s+1` causes overflow for type `%s`",
+ emprev.ed.toChars(), emprev.toChars(), em.ed.memtype.toChars());
+ return errorReturn();
+ }
+
+ // Now set e to (eprev + 1)
+ e = new AddExp(em.loc, eprev, IntegerExp.literal!1);
+ e = e.expressionSemantic(sc);
+ e = e.castTo(sc, eprev.type);
+ e = e.ctfeInterpret();
+
+ // save origValue (without cast) for better json output
+ if (e.op != TOK.error) // avoid duplicate diagnostics
+ {
+ assert(emprev.origValue);
+ em.origValue = new AddExp(em.loc, emprev.origValue, IntegerExp.literal!1);
+ em.origValue = em.origValue.expressionSemantic(sc);
+ em.origValue = em.origValue.ctfeInterpret();
+ }
+
+ if (e.op == TOK.error)
+ return errorReturn();
+ if (e.type.isfloating())
+ {
+ // Check that e != eprev (not always true for floats)
+ Expression etest = new EqualExp(TOK.equal, em.loc, e, eprev);
+ etest = etest.expressionSemantic(sc);
+ etest = etest.ctfeInterpret();
+ if (etest.toInteger())
+ {
+ em.error("has inexact value due to loss of precision");
+ return errorReturn();
+ }
+ }
+ em.value = e;
+ }
+ if (!em.origType)
+ em.type = em.value.type;
+
+ assert(em.origValue);
+ em.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(TemplateDeclaration tempdecl)
+ {
+ static if (LOG)
+ {
+ printf("TemplateDeclaration.dsymbolSemantic(this = %p, id = '%s')\n", this, tempdecl.ident.toChars());
+ printf("sc.stc = %llx\n", sc.stc);
+ printf("sc.module = %s\n", sc._module.toChars());
+ }
+ if (tempdecl.semanticRun != PASS.init)
+ return; // semantic() already run
+
+ if (tempdecl._scope)
+ {
+ sc = tempdecl._scope;
+ tempdecl._scope = null;
+ }
+ if (!sc)
+ return;
+
+ // Remember templates defined in module object that we need to know about
+ if (sc._module && sc._module.ident == Id.object)
+ {
+ if (tempdecl.ident == Id.RTInfo)
+ Type.rtinfo = tempdecl;
+ }
+
+ /* Remember Scope for later instantiations, but make
+ * a copy since attributes can change.
+ */
+ if (!tempdecl._scope)
+ {
+ tempdecl._scope = sc.copy();
+ tempdecl._scope.setNoFree();
+ }
+
+ tempdecl.semanticRun = PASS.semantic;
+
+ tempdecl.parent = sc.parent;
+ tempdecl.visibility = sc.visibility;
+ tempdecl.cppnamespace = sc.namespace;
+ tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_);
+ tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_);
+
+ UserAttributeDeclaration.checkGNUABITag(tempdecl, sc.linkage);
+
+ if (!tempdecl.isstatic)
+ {
+ if (auto ad = tempdecl.parent.pastMixin().isAggregateDeclaration())
+ ad.makeNested();
+ }
+
+ // Set up scope for parameters
+ auto paramsym = new ScopeDsymbol();
+ paramsym.parent = tempdecl.parent;
+ Scope* paramscope = sc.push(paramsym);
+ paramscope.stc = 0;
+
+ if (global.params.doDocComments)
+ {
+ tempdecl.origParameters = new TemplateParameters(tempdecl.parameters.dim);
+ for (size_t i = 0; i < tempdecl.parameters.dim; i++)
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ (*tempdecl.origParameters)[i] = tp.syntaxCopy();
+ }
+ }
+
+ for (size_t i = 0; i < tempdecl.parameters.dim; i++)
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ if (!tp.declareParameter(paramscope))
+ {
+ error(tp.loc, "parameter `%s` multiply defined", tp.ident.toChars());
+ tempdecl.errors = true;
+ }
+ if (!tp.tpsemantic(paramscope, tempdecl.parameters))
+ {
+ tempdecl.errors = true;
+ }
+ if (i + 1 != tempdecl.parameters.dim && tp.isTemplateTupleParameter())
+ {
+ tempdecl.error("template tuple parameter must be last one");
+ tempdecl.errors = true;
+ }
+ }
+
+ /* Calculate TemplateParameter.dependent
+ */
+ TemplateParameters tparams = TemplateParameters(1);
+ for (size_t i = 0; i < tempdecl.parameters.dim; i++)
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ tparams[0] = tp;
+
+ for (size_t j = 0; j < tempdecl.parameters.dim; j++)
+ {
+ // Skip cases like: X(T : T)
+ if (i == j)
+ continue;
+
+ if (TemplateTypeParameter ttp = (*tempdecl.parameters)[j].isTemplateTypeParameter())
+ {
+ if (reliesOnTident(ttp.specType, &tparams))
+ tp.dependent = true;
+ }
+ else if (TemplateAliasParameter tap = (*tempdecl.parameters)[j].isTemplateAliasParameter())
+ {
+ if (reliesOnTident(tap.specType, &tparams) ||
+ reliesOnTident(isType(tap.specAlias), &tparams))
+ {
+ tp.dependent = true;
+ }
+ }
+ }
+ }
+
+ paramscope.pop();
+
+ // Compute again
+ tempdecl.onemember = null;
+ if (tempdecl.members)
+ {
+ Dsymbol s;
+ if (Dsymbol.oneMembers(tempdecl.members, &s, tempdecl.ident) && s)
+ {
+ tempdecl.onemember = s;
+ s.parent = tempdecl;
+ }
+ }
+
+ /* BUG: should check:
+ * 1. template functions must not introduce virtual functions, as they
+ * cannot be accomodated in the vtbl[]
+ * 2. templates cannot introduce non-static data members (i.e. fields)
+ * as they would change the instance size of the aggregate.
+ */
+
+ tempdecl.semanticRun = PASS.semanticdone;
+ }
+
+ override void visit(TemplateInstance ti)
+ {
+ templateInstanceSemantic(ti, sc, null);
+ }
+
+ override void visit(TemplateMixin tm)
+ {
+ static if (LOG)
+ {
+ printf("+TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
+ fflush(stdout);
+ }
+ if (tm.semanticRun != PASS.init)
+ {
+ // When a class/struct contains mixin members, and is done over
+ // because of forward references, never reach here so semanticRun
+ // has been reset to PASS.init.
+ static if (LOG)
+ {
+ printf("\tsemantic done\n");
+ }
+ return;
+ }
+ tm.semanticRun = PASS.semantic;
+ static if (LOG)
+ {
+ printf("\tdo semantic\n");
+ }
+
+ Scope* scx = null;
+ if (tm._scope)
+ {
+ sc = tm._scope;
+ scx = tm._scope; // save so we don't make redundant copies
+ tm._scope = null;
+ }
+
+ /* Run semantic on each argument, place results in tiargs[],
+ * then find best match template with tiargs
+ */
+ if (!tm.findTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, null))
+ {
+ if (tm.semanticRun == PASS.init) // forward reference had occurred
+ {
+ //printf("forward reference - deferring\n");
+ return deferDsymbolSemantic(tm, scx);
+ }
+
+ tm.inst = tm;
+ tm.errors = true;
+ return; // error recovery
+ }
+
+ auto tempdecl = tm.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ if (!tm.ident)
+ {
+ /* Assign scope local unique identifier, as same as lambdas.
+ */
+ const(char)[] s = "__mixin";
+
+ if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+ {
+ tm.symtab = func.localsymtab;
+ if (tm.symtab)
+ {
+ // Inside template constraint, symtab is not set yet.
+ goto L1;
+ }
+ }
+ else
+ {
+ tm.symtab = sc.parent.isScopeDsymbol().symtab;
+ L1:
+ assert(tm.symtab);
+ tm.ident = Identifier.generateId(s, tm.symtab.length + 1);
+ tm.symtab.insert(tm);
+ }
+ }
+
+ tm.inst = tm;
+ tm.parent = sc.parent;
+
+ /* Detect recursive mixin instantiations.
+ */
+ for (Dsymbol s = tm.parent; s; s = s.parent)
+ {
+ //printf("\ts = '%s'\n", s.toChars());
+ TemplateMixin tmix = s.isTemplateMixin();
+ if (!tmix || tempdecl != tmix.tempdecl)
+ continue;
+
+ /* Different argument list lengths happen with variadic args
+ */
+ if (tm.tiargs.dim != tmix.tiargs.dim)
+ continue;
+
+ for (size_t i = 0; i < tm.tiargs.dim; i++)
+ {
+ RootObject o = (*tm.tiargs)[i];
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+ RootObject tmo = (*tmix.tiargs)[i];
+ if (ta)
+ {
+ Type tmta = isType(tmo);
+ if (!tmta)
+ goto Lcontinue;
+ if (!ta.equals(tmta))
+ goto Lcontinue;
+ }
+ else if (ea)
+ {
+ Expression tme = isExpression(tmo);
+ if (!tme || !ea.equals(tme))
+ goto Lcontinue;
+ }
+ else if (sa)
+ {
+ Dsymbol tmsa = isDsymbol(tmo);
+ if (sa != tmsa)
+ goto Lcontinue;
+ }
+ else
+ assert(0);
+ }
+ tm.error("recursive mixin instantiation");
+ return;
+
+ Lcontinue:
+ continue;
+ }
+
+ // Copy the syntax trees from the TemplateDeclaration
+ tm.members = Dsymbol.arraySyntaxCopy(tempdecl.members);
+ if (!tm.members)
+ return;
+
+ tm.symtab = new DsymbolTable();
+
+ sc.getScopesym().importScope(tm, Visibility(Visibility.Kind.public_));
+
+ static if (LOG)
+ {
+ printf("\tcreate scope for template parameters '%s'\n", tm.toChars());
+ }
+ Scope* scy = sc.push(tm);
+ scy.parent = tm;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=930
+ *
+ * If the template that is to be mixed in is in the scope of a template
+ * instance, we have to also declare the type aliases in the new mixin scope.
+ */
+ auto parentInstance = tempdecl.parent ? tempdecl.parent.isTemplateInstance() : null;
+ if (parentInstance)
+ parentInstance.declareParameters(scy);
+
+ tm.argsym = new ScopeDsymbol();
+ tm.argsym.parent = scy.parent;
+ Scope* argscope = scy.push(tm.argsym);
+
+ uint errorsave = global.errors;
+
+ // Declare each template parameter as an alias for the argument type
+ tm.declareParameters(argscope);
+
+ // Add members to enclosing scope, as well as this scope
+ tm.members.foreachDsymbol(s => s.addMember(argscope, tm));
+
+ // Do semantic() analysis on template instance members
+ static if (LOG)
+ {
+ printf("\tdo semantic() on template instance members '%s'\n", tm.toChars());
+ }
+ Scope* sc2 = argscope.push(tm);
+ //size_t deferred_dim = Module.deferred.dim;
+
+ __gshared int nest;
+ //printf("%d\n", nest);
+ if (++nest > global.recursionLimit)
+ {
+ global.gag = 0; // ensure error message gets printed
+ tm.error("recursive expansion");
+ fatal();
+ }
+
+ tm.members.foreachDsymbol( s => s.setScope(sc2) );
+
+ tm.members.foreachDsymbol( s => s.importAll(sc2) );
+
+ tm.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
+
+ nest--;
+
+ /* In DeclDefs scope, TemplateMixin does not have to handle deferred symbols.
+ * Because the members would already call Module.addDeferredSemantic() for themselves.
+ * See Struct, Class, Interface, and EnumDeclaration.dsymbolSemantic().
+ */
+ //if (!sc.func && Module.deferred.dim > deferred_dim) {}
+
+ AggregateDeclaration ad = tm.toParent().isAggregateDeclaration();
+ if (sc.func && !ad)
+ {
+ tm.semantic2(sc2);
+ tm.semantic3(sc2);
+ }
+
+ // Give additional context info if error occurred during instantiation
+ if (global.errors != errorsave)
+ {
+ tm.error("error instantiating");
+ tm.errors = true;
+ }
+
+ sc2.pop();
+ argscope.pop();
+ scy.pop();
+
+ static if (LOG)
+ {
+ printf("-TemplateMixin.dsymbolSemantic('%s', this=%p)\n", tm.toChars(), tm);
+ }
+ }
+
+ override void visit(Nspace ns)
+ {
+ if (ns.semanticRun != PASS.init)
+ return;
+ static if (LOG)
+ {
+ printf("+Nspace::semantic('%s')\n", ns.toChars());
+ }
+ if (ns._scope)
+ {
+ sc = ns._scope;
+ ns._scope = null;
+ }
+ if (!sc)
+ return;
+
+ bool repopulateMembers = false;
+ if (ns.identExp)
+ {
+ // resolve the namespace identifier
+ sc = sc.startCTFE();
+ Expression resolved = ns.identExp.expressionSemantic(sc);
+ resolved = resolveProperties(sc, resolved);
+ sc = sc.endCTFE();
+ resolved = resolved.ctfeInterpret();
+ StringExp name = resolved.toStringExp();
+ TupleExp tup = name ? null : resolved.toTupleExp();
+ if (!tup && !name)
+ {
+ error(ns.loc, "expected string expression for namespace name, got `%s`", ns.identExp.toChars());
+ return;
+ }
+ ns.identExp = resolved; // we don't need to keep the old AST around
+ if (name)
+ {
+ const(char)[] ident = name.toStringz();
+ if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
+ {
+ error(ns.loc, "expected valid identifier for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
+ return;
+ }
+ ns.ident = Identifier.idPool(ident);
+ }
+ else
+ {
+ // create namespace stack from the tuple
+ Nspace parentns = ns;
+ foreach (i, exp; *tup.exps)
+ {
+ name = exp.toStringExp();
+ if (!name)
+ {
+ error(ns.loc, "expected string expression for namespace name, got `%s`", exp.toChars());
+ return;
+ }
+ const(char)[] ident = name.toStringz();
+ if (ident.length == 0 || !Identifier.isValidIdentifier(ident))
+ {
+ error(ns.loc, "expected valid identifier for C++ namespace but got `%.*s`", cast(int)ident.length, ident.ptr);
+ return;
+ }
+ if (i == 0)
+ {
+ ns.ident = Identifier.idPool(ident);
+ }
+ else
+ {
+ // insert the new namespace
+ Nspace childns = new Nspace(ns.loc, Identifier.idPool(ident), null, parentns.members);
+ parentns.members = new Dsymbols;
+ parentns.members.push(childns);
+ parentns = childns;
+ repopulateMembers = true;
+ }
+ }
+ }
+ }
+
+ ns.semanticRun = PASS.semantic;
+ ns.parent = sc.parent;
+ // Link does not matter here, if the UDA is present it will error
+ UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp);
+
+ if (ns.members)
+ {
+ assert(sc);
+ sc = sc.push(ns);
+ sc.linkage = LINK.cpp; // note that namespaces imply C++ linkage
+ sc.parent = ns;
+ foreach (s; *ns.members)
+ {
+ if (repopulateMembers)
+ {
+ s.addMember(sc, sc.scopesym);
+ s.setScope(sc);
+ }
+ s.importAll(sc);
+ }
+ foreach (s; *ns.members)
+ {
+ static if (LOG)
+ {
+ printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind());
+ }
+ s.dsymbolSemantic(sc);
+ }
+ sc.pop();
+ }
+ ns.semanticRun = PASS.semanticdone;
+ static if (LOG)
+ {
+ printf("-Nspace::semantic('%s')\n", ns.toChars());
+ }
+ }
+
+ void funcDeclarationSemantic(FuncDeclaration funcdecl)
+ {
+ TypeFunction f;
+ AggregateDeclaration ad;
+ InterfaceDeclaration id;
+
+ version (none)
+ {
+ printf("FuncDeclaration::semantic(sc = %p, this = %p, '%s', linkage = %d)\n", sc, funcdecl, funcdecl.toPrettyChars(), sc.linkage);
+ if (funcdecl.isFuncLiteralDeclaration())
+ printf("\tFuncLiteralDeclaration()\n");
+ printf("sc.parent = %s, parent = %s\n", sc.parent.toChars(), funcdecl.parent ? funcdecl.parent.toChars() : "");
+ printf("type: %p, %s\n", funcdecl.type, funcdecl.type.toChars());
+ }
+
+ if (funcdecl.semanticRun != PASS.init && funcdecl.isFuncLiteralDeclaration())
+ {
+ /* Member functions that have return types that are
+ * forward references can have semantic() run more than
+ * once on them.
+ * See test\interface2.d, test20
+ */
+ return;
+ }
+
+ if (funcdecl.semanticRun >= PASS.semanticdone)
+ return;
+ assert(funcdecl.semanticRun <= PASS.semantic);
+ funcdecl.semanticRun = PASS.semantic;
+
+ if (funcdecl._scope)
+ {
+ sc = funcdecl._scope;
+ funcdecl._scope = null;
+ }
+
+ if (!sc || funcdecl.errors)
+ return;
+
+ funcdecl.cppnamespace = sc.namespace;
+ funcdecl.parent = sc.parent;
+ Dsymbol parent = funcdecl.toParent();
+
+ funcdecl.foverrides.setDim(0); // reset in case semantic() is being retried for this function
+
+ funcdecl.storage_class |= sc.stc & ~STC.ref_;
+ ad = funcdecl.isThis();
+ // Don't nest structs b/c of generated methods which should not access the outer scopes.
+ // https://issues.dlang.org/show_bug.cgi?id=16627
+ if (ad && !funcdecl.generated)
+ {
+ funcdecl.storage_class |= ad.storage_class & (STC.TYPECTOR | STC.synchronized_);
+ ad.makeNested();
+ }
+ if (sc.func)
+ funcdecl.storage_class |= sc.func.storage_class & STC.disable;
+ // Remove prefix storage classes silently.
+ if ((funcdecl.storage_class & STC.TYPECTOR) && !(ad || funcdecl.isNested()))
+ funcdecl.storage_class &= ~STC.TYPECTOR;
+
+ //printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration::isFinal());
+
+ if (sc.flags & SCOPE.compile)
+ funcdecl.flags |= FUNCFLAG.compileTimeOnly; // don't emit code for this function
+
+ FuncLiteralDeclaration fld = funcdecl.isFuncLiteralDeclaration();
+ if (fld && fld.treq)
+ {
+ Type treq = fld.treq;
+ assert(treq.nextOf().ty == Tfunction);
+ if (treq.ty == Tdelegate)
+ fld.tok = TOK.delegate_;
+ else if (treq.isPtrToFunction())
+ fld.tok = TOK.function_;
+ else
+ assert(0);
+ funcdecl.linkage = treq.nextOf().toTypeFunction().linkage;
+ }
+ else
+ funcdecl.linkage = sc.linkage;
+
+ // evaluate pragma(inline)
+ if (auto pragmadecl = sc.inlining)
+ funcdecl.inlining = pragmadecl.evalPragmaInline(sc);
+
+ funcdecl.visibility = sc.visibility;
+ funcdecl.userAttribDecl = sc.userAttribDecl;
+ UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl.linkage);
+
+ if (!funcdecl.originalType)
+ funcdecl.originalType = funcdecl.type.syntaxCopy();
+ if (funcdecl.type.ty != Tfunction)
+ {
+ if (funcdecl.type.ty != Terror)
+ {
+ funcdecl.error("`%s` must be a function instead of `%s`", funcdecl.toChars(), funcdecl.type.toChars());
+ funcdecl.type = Type.terror;
+ }
+ funcdecl.errors = true;
+ return;
+ }
+ if (!funcdecl.type.deco)
+ {
+ sc = sc.push();
+ sc.stc |= funcdecl.storage_class & (STC.disable | STC.deprecated_); // forward to function type
+
+ TypeFunction tf = funcdecl.type.toTypeFunction();
+ if (sc.func)
+ {
+ /* If the nesting parent is pure without inference,
+ * then this function defaults to pure too.
+ *
+ * auto foo() pure {
+ * auto bar() {} // become a weak purity function
+ * class C { // nested class
+ * auto baz() {} // become a weak purity function
+ * }
+ *
+ * static auto boo() {} // typed as impure
+ * // Even though, boo cannot call any impure functions.
+ * // See also Expression::checkPurity().
+ * }
+ */
+ if (tf.purity == PURE.impure && (funcdecl.isNested() || funcdecl.isThis()))
+ {
+ FuncDeclaration fd = null;
+ for (Dsymbol p = funcdecl.toParent2(); p; p = p.toParent2())
+ {
+ if (AggregateDeclaration adx = p.isAggregateDeclaration())
+ {
+ if (adx.isNested())
+ continue;
+ break;
+ }
+ if ((fd = p.isFuncDeclaration()) !is null)
+ break;
+ }
+
+ /* If the parent's purity is inferred, then this function's purity needs
+ * to be inferred first.
+ */
+ if (fd && fd.isPureBypassingInference() >= PURE.weak && !funcdecl.isInstantiated())
+ {
+ tf.purity = PURE.fwdref; // default to pure
+ }
+ }
+ }
+
+ if (tf.isref)
+ sc.stc |= STC.ref_;
+ if (tf.isScopeQual)
+ sc.stc |= STC.scope_;
+ if (tf.isnothrow)
+ sc.stc |= STC.nothrow_;
+ if (tf.isnogc)
+ sc.stc |= STC.nogc;
+ if (tf.isproperty)
+ sc.stc |= STC.property;
+ if (tf.purity == PURE.fwdref)
+ sc.stc |= STC.pure_;
+ if (tf.trust != TRUST.default_)
+ sc.stc &= ~STC.safeGroup;
+ if (tf.trust == TRUST.safe)
+ sc.stc |= STC.safe;
+ if (tf.trust == TRUST.system)
+ sc.stc |= STC.system;
+ if (tf.trust == TRUST.trusted)
+ sc.stc |= STC.trusted;
+
+ if (funcdecl.isCtorDeclaration())
+ {
+ tf.isctor = true;
+ Type tret = ad.handleType();
+ assert(tret);
+ tret = tret.addStorageClass(funcdecl.storage_class | sc.stc);
+ tret = tret.addMod(funcdecl.type.mod);
+ tf.next = tret;
+ if (ad.isStructDeclaration())
+ sc.stc |= STC.ref_;
+ }
+
+ // 'return' on a non-static class member function implies 'scope' as well
+ if (ad && ad.isClassDeclaration() && (tf.isreturn || sc.stc & STC.return_) && !(sc.stc & STC.static_))
+ sc.stc |= STC.scope_;
+
+ // If 'this' has no pointers, remove 'scope' as it has no meaning
+ if (sc.stc & STC.scope_ && ad && ad.isStructDeclaration() && !ad.type.hasPointers())
+ {
+ sc.stc &= ~STC.scope_;
+ tf.isScopeQual = false;
+ }
+
+ sc.linkage = funcdecl.linkage;
+
+ if (!tf.isNaked() && !(funcdecl.isThis() || funcdecl.isNested()))
+ {
+ OutBuffer buf;
+ MODtoBuffer(&buf, tf.mod);
+ funcdecl.error("without `this` cannot be `%s`", buf.peekChars());
+ tf.mod = 0; // remove qualifiers
+ }
+
+ /* Apply const, immutable, wild and shared storage class
+ * to the function type. Do this before type semantic.
+ */
+ auto stc = funcdecl.storage_class;
+ if (funcdecl.type.isImmutable())
+ stc |= STC.immutable_;
+ if (funcdecl.type.isConst())
+ stc |= STC.const_;
+ if (funcdecl.type.isShared() || funcdecl.storage_class & STC.synchronized_)
+ stc |= STC.shared_;
+ if (funcdecl.type.isWild())
+ stc |= STC.wild;
+ funcdecl.type = funcdecl.type.addSTC(stc);
+
+ funcdecl.type = funcdecl.type.typeSemantic(funcdecl.loc, sc);
+ sc = sc.pop();
+ }
+ if (funcdecl.type.ty != Tfunction)
+ {
+ if (funcdecl.type.ty != Terror)
+ {
+ funcdecl.error("`%s` must be a function instead of `%s`", funcdecl.toChars(), funcdecl.type.toChars());
+ funcdecl.type = Type.terror;
+ }
+ funcdecl.errors = true;
+ return;
+ }
+ else
+ {
+ // Merge back function attributes into 'originalType'.
+ // It's used for mangling, ddoc, and json output.
+ TypeFunction tfo = funcdecl.originalType.toTypeFunction();
+ TypeFunction tfx = funcdecl.type.toTypeFunction();
+ tfo.mod = tfx.mod;
+ tfo.isScopeQual = tfx.isScopeQual;
+ tfo.isreturninferred = tfx.isreturninferred;
+ tfo.isscopeinferred = tfx.isscopeinferred;
+ tfo.isref = tfx.isref;
+ tfo.isnothrow = tfx.isnothrow;
+ tfo.isnogc = tfx.isnogc;
+ tfo.isproperty = tfx.isproperty;
+ tfo.purity = tfx.purity;
+ tfo.trust = tfx.trust;
+
+ funcdecl.storage_class &= ~(STC.TYPECTOR | STC.FUNCATTR);
+ }
+
+ f = cast(TypeFunction)funcdecl.type;
+
+ if ((funcdecl.storage_class & STC.auto_) && !f.isref && !funcdecl.inferRetType)
+ funcdecl.error("storage class `auto` has no effect if return type is not inferred");
+
+ /* Functions can only be 'scope' if they have a 'this'
+ */
+ if (f.isScopeQual && !funcdecl.isNested() && !ad)
+ {
+ funcdecl.error("functions cannot be `scope`");
+ }
+
+ if (f.isreturn && !funcdecl.needThis() && !funcdecl.isNested())
+ {
+ /* Non-static nested functions have a hidden 'this' pointer to which
+ * the 'return' applies
+ */
+ if (sc.scopesym && sc.scopesym.isAggregateDeclaration())
+ funcdecl.error("`static` member has no `this` to which `return` can apply");
+ else
+ error(funcdecl.loc, "Top-level function `%s` has no `this` to which `return` can apply", funcdecl.toChars());
+ }
+
+ if (funcdecl.isAbstract() && !funcdecl.isVirtual())
+ {
+ const(char)* sfunc;
+ if (funcdecl.isStatic())
+ sfunc = "static";
+ else if (funcdecl.visibility.kind == Visibility.Kind.private_ || funcdecl.visibility.kind == Visibility.Kind.package_)
+ sfunc = visibilityToChars(funcdecl.visibility.kind);
+ else
+ sfunc = "final";
+ funcdecl.error("`%s` functions cannot be `abstract`", sfunc);
+ }
+
+ if (funcdecl.isOverride() && !funcdecl.isVirtual() && !funcdecl.isFuncLiteralDeclaration())
+ {
+ Visibility.Kind kind = funcdecl.visible().kind;
+ if ((kind == Visibility.Kind.private_ || kind == Visibility.Kind.package_) && funcdecl.isMember())
+ funcdecl.error("`%s` method is not virtual and cannot override", visibilityToChars(kind));
+ else
+ funcdecl.error("cannot override a non-virtual function");
+ }
+
+ if (funcdecl.isAbstract() && funcdecl.isFinalFunc())
+ funcdecl.error("cannot be both `final` and `abstract`");
+ version (none)
+ {
+ if (funcdecl.isAbstract() && funcdecl.fbody)
+ funcdecl.error("`abstract` functions cannot have bodies");
+ }
+
+ version (none)
+ {
+ if (funcdecl.isStaticConstructor() || funcdecl.isStaticDestructor())
+ {
+ if (!funcdecl.isStatic() || funcdecl.type.nextOf().ty != Tvoid)
+ funcdecl.error("static constructors / destructors must be `static void`");
+ if (f.arguments && f.arguments.dim)
+ funcdecl.error("static constructors / destructors must have empty parameter list");
+ // BUG: check for invalid storage classes
+ }
+ }
+
+ if (const pors = sc.flags & (SCOPE.printf | SCOPE.scanf))
+ {
+ /* printf/scanf-like functions must be of the form:
+ * extern (C/C++) T printf([parameters...], const(char)* format, ...);
+ * or:
+ * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list);
+ */
+
+ static bool isPointerToChar(Parameter p)
+ {
+ if (auto tptr = p.type.isTypePointer())
+ {
+ return tptr.next.ty == Tchar;
+ }
+ return false;
+ }
+
+ bool isVa_list(Parameter p)
+ {
+ return p.type.equals(target.va_listType(funcdecl.loc, sc));
+ }
+
+ const nparams = f.parameterList.length;
+ if ((f.linkage == LINK.c || f.linkage == LINK.cpp) &&
+
+ (f.parameterList.varargs == VarArg.variadic &&
+ nparams >= 1 &&
+ isPointerToChar(f.parameterList[nparams - 1]) ||
+
+ f.parameterList.varargs == VarArg.none &&
+ nparams >= 2 &&
+ isPointerToChar(f.parameterList[nparams - 2]) &&
+ isVa_list(f.parameterList[nparams - 1])
+ )
+ )
+ {
+ funcdecl.flags |= (pors == SCOPE.printf) ? FUNCFLAG.printf : FUNCFLAG.scanf;
+ }
+ else
+ {
+ const p = (pors == SCOPE.printf ? Id.printf : Id.scanf).toChars();
+ if (f.parameterList.varargs == VarArg.variadic)
+ {
+ funcdecl.error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, ...)`"
+ ~ " not `%s`",
+ p, f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars());
+ }
+ else
+ {
+ funcdecl.error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, va_list)`",
+ p, f.next.toChars(), funcdecl.toChars());
+ }
+ }
+ }
+
+ id = parent.isInterfaceDeclaration();
+ if (id)
+ {
+ funcdecl.storage_class |= STC.abstract_;
+ if (funcdecl.isCtorDeclaration() || funcdecl.isPostBlitDeclaration() || funcdecl.isDtorDeclaration() || funcdecl.isInvariantDeclaration() || funcdecl.isNewDeclaration() || funcdecl.isDelete())
+ funcdecl.error("constructors, destructors, postblits, invariants, new and delete functions are not allowed in interface `%s`", id.toChars());
+ if (funcdecl.fbody && funcdecl.isVirtual())
+ funcdecl.error("function body only allowed in `final` functions in interface `%s`", id.toChars());
+ }
+ if (UnionDeclaration ud = parent.isUnionDeclaration())
+ {
+ if (funcdecl.isPostBlitDeclaration() || funcdecl.isDtorDeclaration() || funcdecl.isInvariantDeclaration())
+ funcdecl.error("destructors, postblits and invariants are not allowed in union `%s`", ud.toChars());
+ }
+
+ if (StructDeclaration sd = parent.isStructDeclaration())
+ {
+ if (funcdecl.isCtorDeclaration())
+ {
+ goto Ldone;
+ }
+ }
+
+ if (ClassDeclaration cd = parent.isClassDeclaration())
+ {
+ parent = cd = objc.getParent(funcdecl, cd);
+
+ if (funcdecl.isCtorDeclaration())
+ {
+ goto Ldone;
+ }
+
+ if (funcdecl.storage_class & STC.abstract_)
+ cd.isabstract = ThreeState.yes;
+
+ // if static function, do not put in vtbl[]
+ if (!funcdecl.isVirtual())
+ {
+ //printf("\tnot virtual\n");
+ goto Ldone;
+ }
+ // Suppress further errors if the return type is an error
+ if (funcdecl.type.nextOf() == Type.terror)
+ goto Ldone;
+
+ bool may_override = false;
+ for (size_t i = 0; i < cd.baseclasses.dim; i++)
+ {
+ BaseClass* b = (*cd.baseclasses)[i];
+ ClassDeclaration cbd = b.type.toBasetype().isClassHandle();
+ if (!cbd)
+ continue;
+ for (size_t j = 0; j < cbd.vtbl.dim; j++)
+ {
+ FuncDeclaration f2 = cbd.vtbl[j].isFuncDeclaration();
+ if (!f2 || f2.ident != funcdecl.ident)
+ continue;
+ if (cbd.parent && cbd.parent.isTemplateInstance())
+ {
+ if (!f2.functionSemantic())
+ goto Ldone;
+ }
+ may_override = true;
+ }
+ }
+ if (may_override && funcdecl.type.nextOf() is null)
+ {
+ /* If same name function exists in base class but 'this' is auto return,
+ * cannot find index of base class's vtbl[] to override.
+ */
+ funcdecl.error("return type inference is not supported if may override base class function");
+ }
+
+ /* Find index of existing function in base class's vtbl[] to override
+ * (the index will be the same as in cd's current vtbl[])
+ */
+ int vi = cd.baseClass ? funcdecl.findVtblIndex(&cd.baseClass.vtbl, cast(int)cd.baseClass.vtbl.dim) : -1;
+
+ bool doesoverride = false;
+ switch (vi)
+ {
+ case -1:
+ Lintro:
+ /* Didn't find one, so
+ * This is an 'introducing' function which gets a new
+ * slot in the vtbl[].
+ */
+
+ // Verify this doesn't override previous final function
+ if (cd.baseClass)
+ {
+ Dsymbol s = cd.baseClass.search(funcdecl.loc, funcdecl.ident);
+ if (s)
+ {
+ FuncDeclaration f2 = s.isFuncDeclaration();
+ if (f2)
+ {
+ f2 = f2.overloadExactMatch(funcdecl.type);
+ if (f2 && f2.isFinalFunc() && f2.visible().kind != Visibility.Kind.private_)
+ funcdecl.error("cannot override `final` function `%s`", f2.toPrettyChars());
+ }
+ }
+ }
+
+ /* These quirky conditions mimic what VC++ appears to do
+ */
+ if (target.mscoff && cd.classKind == ClassKind.cpp &&
+ cd.baseClass && cd.baseClass.vtbl.dim)
+ {
+ /* if overriding an interface function, then this is not
+ * introducing and don't put it in the class vtbl[]
+ */
+ funcdecl.interfaceVirtual = funcdecl.overrideInterface();
+ if (funcdecl.interfaceVirtual)
+ {
+ //printf("\tinterface function %s\n", toChars());
+ cd.vtblFinal.push(funcdecl);
+ goto Linterfaces;
+ }
+ }
+
+ if (funcdecl.isFinalFunc())
+ {
+ // Don't check here, as it may override an interface function
+ //if (isOverride())
+ // error("is marked as override, but does not override any function");
+ cd.vtblFinal.push(funcdecl);
+ }
+ else
+ {
+ //printf("\tintroducing function %s\n", funcdecl.toChars());
+ funcdecl.introducing = 1;
+ if (cd.classKind == ClassKind.cpp && target.cpp.reverseOverloads)
+ {
+ /* Overloaded functions with same name are grouped and in reverse order.
+ * Search for first function of overload group, and insert
+ * funcdecl into vtbl[] immediately before it.
+ */
+ funcdecl.vtblIndex = cast(int)cd.vtbl.dim;
+ bool found;
+ foreach (const i, s; cd.vtbl)
+ {
+ if (found)
+ // the rest get shifted forward
+ ++s.isFuncDeclaration().vtblIndex;
+ else if (s.ident == funcdecl.ident && s.parent == parent)
+ {
+ // found first function of overload group
+ funcdecl.vtblIndex = cast(int)i;
+ found = true;
+ ++s.isFuncDeclaration().vtblIndex;
+ }
+ }
+ cd.vtbl.insert(funcdecl.vtblIndex, funcdecl);
+
+ debug foreach (const i, s; cd.vtbl)
+ {
+ // a C++ dtor gets its vtblIndex later (and might even be added twice to the vtbl),
+ // e.g. when compiling druntime with a debug compiler, namely with core.stdcpp.exception.
+ if (auto fd = s.isFuncDeclaration())
+ assert(fd.vtblIndex == i ||
+ (cd.classKind == ClassKind.cpp && fd.isDtorDeclaration) ||
+ funcdecl.parent.isInterfaceDeclaration); // interface functions can be in multiple vtbls
+ }
+ }
+ else
+ {
+ // Append to end of vtbl[]
+ vi = cast(int)cd.vtbl.dim;
+ cd.vtbl.push(funcdecl);
+ funcdecl.vtblIndex = vi;
+ }
+ }
+ break;
+
+ case -2:
+ // can't determine because of forward references
+ funcdecl.errors = true;
+ return;
+
+ default:
+ {
+ FuncDeclaration fdv = cd.baseClass.vtbl[vi].isFuncDeclaration();
+ FuncDeclaration fdc = cd.vtbl[vi].isFuncDeclaration();
+ // This function is covariant with fdv
+
+ if (fdc == funcdecl)
+ {
+ doesoverride = true;
+ break;
+ }
+
+ if (fdc.toParent() == parent)
+ {
+ //printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n",
+ // vi, this, this.toChars(), this.type.toChars(), this.loc.toChars(),
+ // fdc, fdc .toChars(), fdc .type.toChars(), fdc .loc.toChars(),
+ // fdv, fdv .toChars(), fdv .type.toChars(), fdv .loc.toChars());
+
+ // fdc overrides fdv exactly, then this introduces new function.
+ if (fdc.type.mod == fdv.type.mod && funcdecl.type.mod != fdv.type.mod)
+ goto Lintro;
+ }
+
+ if (fdv.isDeprecated)
+ deprecation(funcdecl.loc, "`%s` is overriding the deprecated method `%s`",
+ funcdecl.toPrettyChars, fdv.toPrettyChars);
+
+ // This function overrides fdv
+ if (fdv.isFinalFunc())
+ funcdecl.error("cannot override `final` function `%s`", fdv.toPrettyChars());
+
+ if (!funcdecl.isOverride())
+ {
+ if (fdv.isFuture())
+ {
+ deprecation(funcdecl.loc, "`@__future` base class method `%s` is being overridden by `%s`; rename the latter", fdv.toPrettyChars(), funcdecl.toPrettyChars());
+ // Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[]
+ goto Lintro;
+ }
+ else
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=17349
+ error(funcdecl.loc, "cannot implicitly override base class method `%s` with `%s`; add `override` attribute",
+ fdv.toPrettyChars(), funcdecl.toPrettyChars());
+ }
+ }
+ doesoverride = true;
+ if (fdc.toParent() == parent)
+ {
+ // If both are mixins, or both are not, then error.
+ // If either is not, the one that is not overrides the other.
+ bool thismixin = funcdecl.parent.isClassDeclaration() !is null;
+ bool fdcmixin = fdc.parent.isClassDeclaration() !is null;
+ if (thismixin == fdcmixin)
+ {
+ funcdecl.error("multiple overrides of same function");
+ }
+ /*
+ * https://issues.dlang.org/show_bug.cgi?id=711
+ *
+ * If an overriding method is introduced through a mixin,
+ * we need to update the vtbl so that both methods are
+ * present.
+ */
+ else if (thismixin)
+ {
+ /* if the mixin introduced the overriding method, then reintroduce it
+ * in the vtbl. The initial entry for the mixined method
+ * will be updated at the end of the enclosing `if` block
+ * to point to the current (non-mixined) function.
+ */
+ auto vitmp = cast(int)cd.vtbl.dim;
+ cd.vtbl.push(fdc);
+ fdc.vtblIndex = vitmp;
+ }
+ else if (fdcmixin)
+ {
+ /* if the current overriding function is coming from a
+ * mixined block, then push the current function in the
+ * vtbl, but keep the previous (non-mixined) function as
+ * the overriding one.
+ */
+ auto vitmp = cast(int)cd.vtbl.dim;
+ cd.vtbl.push(funcdecl);
+ funcdecl.vtblIndex = vitmp;
+ break;
+ }
+ else // fdc overrides fdv
+ {
+ // this doesn't override any function
+ break;
+ }
+ }
+ cd.vtbl[vi] = funcdecl;
+ funcdecl.vtblIndex = vi;
+
+ /* Remember which functions this overrides
+ */
+ funcdecl.foverrides.push(fdv);
+
+ /* This works by whenever this function is called,
+ * it actually returns tintro, which gets dynamically
+ * cast to type. But we know that tintro is a base
+ * of type, so we could optimize it by not doing a
+ * dynamic cast, but just subtracting the isBaseOf()
+ * offset if the value is != null.
+ */
+
+ if (fdv.tintro)
+ funcdecl.tintro = fdv.tintro;
+ else if (!funcdecl.type.equals(fdv.type))
+ {
+ /* Only need to have a tintro if the vptr
+ * offsets differ
+ */
+ int offset;
+ if (fdv.type.nextOf().isBaseOf(funcdecl.type.nextOf(), &offset))
+ {
+ funcdecl.tintro = fdv.type;
+ }
+ }
+ break;
+ }
+ }
+
+ /* Go through all the interface bases.
+ * If this function is covariant with any members of those interface
+ * functions, set the tintro.
+ */
+ Linterfaces:
+ bool foundVtblMatch = false;
+
+ for (ClassDeclaration bcd = cd; !foundVtblMatch && bcd; bcd = bcd.baseClass)
+ {
+ foreach (b; bcd.interfaces)
+ {
+ vi = funcdecl.findVtblIndex(&b.sym.vtbl, cast(int)b.sym.vtbl.dim);
+ switch (vi)
+ {
+ case -1:
+ break;
+
+ case -2:
+ // can't determine because of forward references
+ funcdecl.errors = true;
+ return;
+
+ default:
+ {
+ auto fdv = cast(FuncDeclaration)b.sym.vtbl[vi];
+ Type ti = null;
+
+ foundVtblMatch = true;
+
+ /* Remember which functions this overrides
+ */
+ funcdecl.foverrides.push(fdv);
+
+ /* Should we really require 'override' when implementing
+ * an interface function?
+ */
+ //if (!isOverride())
+ // warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv.toPrettyChars());
+
+ if (fdv.tintro)
+ ti = fdv.tintro;
+ else if (!funcdecl.type.equals(fdv.type))
+ {
+ /* Only need to have a tintro if the vptr
+ * offsets differ
+ */
+ int offset;
+ if (fdv.type.nextOf().isBaseOf(funcdecl.type.nextOf(), &offset))
+ {
+ ti = fdv.type;
+ }
+ }
+ if (ti)
+ {
+ if (funcdecl.tintro)
+ {
+ if (!funcdecl.tintro.nextOf().equals(ti.nextOf()) && !funcdecl.tintro.nextOf().isBaseOf(ti.nextOf(), null) && !ti.nextOf().isBaseOf(funcdecl.tintro.nextOf(), null))
+ {
+ funcdecl.error("incompatible covariant types `%s` and `%s`", funcdecl.tintro.toChars(), ti.toChars());
+ }
+ }
+ else
+ {
+ funcdecl.tintro = ti;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (foundVtblMatch)
+ {
+ goto L2;
+ }
+
+ if (!doesoverride && funcdecl.isOverride() && (funcdecl.type.nextOf() || !may_override))
+ {
+ BaseClass* bc = null;
+ Dsymbol s = null;
+ for (size_t i = 0; i < cd.baseclasses.dim; i++)
+ {
+ bc = (*cd.baseclasses)[i];
+ s = bc.sym.search_correct(funcdecl.ident);
+ if (s)
+ break;
+ }
+
+ if (s)
+ {
+ HdrGenState hgs;
+ OutBuffer buf;
+
+ auto fd = s.isFuncDeclaration();
+ functionToBufferFull(cast(TypeFunction)(funcdecl.type), &buf,
+ new Identifier(funcdecl.toPrettyChars()), &hgs, null);
+ const(char)* funcdeclToChars = buf.peekChars();
+
+ if (fd)
+ {
+ OutBuffer buf1;
+
+ if (fd.ident == funcdecl.ident)
+ hgs.fullQual = true;
+ functionToBufferFull(cast(TypeFunction)(fd.type), &buf1,
+ new Identifier(fd.toPrettyChars()), &hgs, null);
+
+ error(funcdecl.loc, "function `%s` does not override any function, did you mean to override `%s`?",
+ funcdeclToChars, buf1.peekChars());
+ }
+ else
+ {
+ error(funcdecl.loc, "function `%s` does not override any function, did you mean to override %s `%s`?",
+ funcdeclToChars, s.kind, s.toPrettyChars());
+ errorSupplemental(funcdecl.loc, "Functions are the only declarations that may be overriden");
+ }
+ }
+ else
+ funcdecl.error("does not override any function");
+ }
+
+ L2:
+ objc.setSelector(funcdecl, sc);
+ objc.checkLinkage(funcdecl);
+ objc.addToClassMethodList(funcdecl, cd);
+ objc.setAsOptional(funcdecl, sc);
+
+ /* Go through all the interface bases.
+ * Disallow overriding any final functions in the interface(s).
+ */
+ foreach (b; cd.interfaces)
+ {
+ if (b.sym)
+ {
+ Dsymbol s = search_function(b.sym, funcdecl.ident);
+ if (s)
+ {
+ FuncDeclaration f2 = s.isFuncDeclaration();
+ if (f2)
+ {
+ f2 = f2.overloadExactMatch(funcdecl.type);
+ if (f2 && f2.isFinalFunc() && f2.visible().kind != Visibility.Kind.private_)
+ funcdecl.error("cannot override `final` function `%s.%s`", b.sym.toChars(), f2.toPrettyChars());
+ }
+ }
+ }
+ }
+
+ if (funcdecl.isOverride)
+ {
+ if (funcdecl.storage_class & STC.disable)
+ deprecation(funcdecl.loc,
+ "`%s` cannot be annotated with `@disable` because it is overriding a function in the base class",
+ funcdecl.toPrettyChars);
+ if (funcdecl.isDeprecated)
+ deprecation(funcdecl.loc,
+ "`%s` cannot be marked as `deprecated` because it is overriding a function in the base class",
+ funcdecl.toPrettyChars);
+ }
+
+ }
+ else if (funcdecl.isOverride() && !parent.isTemplateInstance())
+ funcdecl.error("`override` only applies to class member functions");
+
+ if (auto ti = parent.isTemplateInstance)
+ {
+ objc.setSelector(funcdecl, sc);
+ objc.setAsOptional(funcdecl, sc);
+ }
+
+ objc.validateSelector(funcdecl);
+ objc.validateOptional(funcdecl);
+ // Reflect this.type to f because it could be changed by findVtblIndex
+ f = funcdecl.type.toTypeFunction();
+
+ Ldone:
+ if (!funcdecl.fbody && !funcdecl.allowsContractWithoutBody())
+ funcdecl.error("`in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract");
+
+ /* Do not allow template instances to add virtual functions
+ * to a class.
+ */
+ if (funcdecl.isVirtual())
+ {
+ TemplateInstance ti = parent.isTemplateInstance();
+ if (ti)
+ {
+ // Take care of nested templates
+ while (1)
+ {
+ TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
+ if (!ti2)
+ break;
+ ti = ti2;
+ }
+
+ // If it's a member template
+ ClassDeclaration cd = ti.tempdecl.isClassMember();
+ if (cd)
+ {
+ funcdecl.error("cannot use template to add virtual function to class `%s`", cd.toChars());
+ }
+ }
+ }
+
+ if (funcdecl.isMain())
+ funcdecl.checkDmain(); // Check main() parameters and return type
+
+ /* Purity and safety can be inferred for some functions by examining
+ * the function body.
+ */
+ if (funcdecl.canInferAttributes(sc))
+ funcdecl.initInferAttributes();
+
+ Module.dprogress++;
+ funcdecl.semanticRun = PASS.semanticdone;
+
+ /* Save scope for possible later use (if we need the
+ * function internals)
+ */
+ funcdecl._scope = sc.copy();
+ funcdecl._scope.setNoFree();
+
+ __gshared bool printedMain = false; // semantic might run more than once
+ if (global.params.verbose && !printedMain)
+ {
+ const(char)* type = funcdecl.isMain() ? "main" : funcdecl.isWinMain() ? "winmain" : funcdecl.isDllMain() ? "dllmain" : cast(const(char)*)null;
+ Module mod = sc._module;
+
+ if (type && mod)
+ {
+ printedMain = true;
+ auto name = mod.srcfile.toChars();
+ auto path = FileName.searchPath(global.path, name, true);
+ message("entry %-10s\t%s", type, path ? path : name);
+ }
+ }
+
+ if (funcdecl.fbody && funcdecl.isMain() && sc._module.isRoot())
+ {
+ // check if `_d_cmain` is defined
+ bool cmainTemplateExists()
+ {
+ auto rootSymbol = sc.search(funcdecl.loc, Id.empty, null);
+ if (auto moduleSymbol = rootSymbol.search(funcdecl.loc, Id.object))
+ if (moduleSymbol.search(funcdecl.loc, Id.CMain))
+ return true;
+
+ return false;
+ }
+
+ // Only mixin `_d_cmain` if it is defined
+ if (cmainTemplateExists())
+ {
+ // add `mixin _d_cmain!();` to the declaring module
+ auto tqual = new TypeIdentifier(funcdecl.loc, Id.CMain);
+ auto tm = new TemplateMixin(funcdecl.loc, null, tqual, null);
+ sc._module.members.push(tm);
+ }
+ }
+
+ assert(funcdecl.type.ty != Terror || funcdecl.errors);
+
+ // semantic for parameters' UDAs
+ foreach (i, param; f.parameterList)
+ {
+ if (param && param.userAttribDecl)
+ param.userAttribDecl.dsymbolSemantic(sc);
+ }
+ }
+
+ /// Do the semantic analysis on the external interface to the function.
+ override void visit(FuncDeclaration funcdecl)
+ {
+ funcDeclarationSemantic(funcdecl);
+ }
+
+ override void visit(CtorDeclaration ctd)
+ {
+ //printf("CtorDeclaration::semantic() %s\n", toChars());
+ if (ctd.semanticRun >= PASS.semanticdone)
+ return;
+ if (ctd._scope)
+ {
+ sc = ctd._scope;
+ ctd._scope = null;
+ }
+
+ ctd.parent = sc.parent;
+ Dsymbol p = ctd.toParentDecl();
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (!ad)
+ {
+ error(ctd.loc, "constructor can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
+ ctd.type = Type.terror;
+ ctd.errors = true;
+ return;
+ }
+
+ sc = sc.push();
+
+ if (sc.stc & STC.static_)
+ {
+ if (sc.stc & STC.shared_)
+ error(ctd.loc, "`shared static` has no effect on a constructor inside a `shared static` block. Use `shared static this()`");
+ else
+ error(ctd.loc, "`static` has no effect on a constructor inside a `static` block. Use `static this()`");
+ }
+
+ sc.stc &= ~STC.static_; // not a static constructor
+
+ funcDeclarationSemantic(ctd);
+
+ sc.pop();
+
+ if (ctd.errors)
+ return;
+
+ TypeFunction tf = ctd.type.toTypeFunction();
+
+ /* See if it's the default constructor
+ * But, template constructor should not become a default constructor.
+ */
+ if (ad && (!ctd.parent.isTemplateInstance() || ctd.parent.isTemplateMixin()))
+ {
+ immutable dim = tf.parameterList.length;
+
+ if (auto sd = ad.isStructDeclaration())
+ {
+ if (dim == 0 && tf.parameterList.varargs == VarArg.none) // empty default ctor w/o any varargs
+ {
+ if (ctd.fbody || !(ctd.storage_class & STC.disable))
+ {
+ ctd.error("default constructor for structs only allowed " ~
+ "with `@disable`, no body, and no parameters");
+ ctd.storage_class |= STC.disable;
+ ctd.fbody = null;
+ }
+ sd.noDefaultCtor = true;
+ }
+ else if (dim == 0 && tf.parameterList.varargs != VarArg.none) // allow varargs only ctor
+ {
+ }
+ else if (dim && tf.parameterList[0].defaultArg)
+ {
+ // if the first parameter has a default argument, then the rest does as well
+ if (ctd.storage_class & STC.disable)
+ {
+ ctd.error("is marked `@disable`, so it cannot have default "~
+ "arguments for all parameters.");
+ errorSupplemental(ctd.loc, "Use `@disable this();` if you want to disable default initialization.");
+ }
+ else
+ ctd.error("all parameters have default arguments, "~
+ "but structs cannot have default constructors.");
+ }
+ else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
+ {
+ //printf("tf: %s\n", tf.toChars());
+ auto param = tf.parameterList[0];
+ if (param.storageClass & STC.ref_ && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
+ {
+ //printf("copy constructor\n");
+ ctd.isCpCtor = true;
+ }
+ }
+ }
+ else if (dim == 0 && tf.parameterList.varargs == VarArg.none)
+ {
+ ad.defaultCtor = ctd;
+ }
+ }
+ }
+
+ override void visit(PostBlitDeclaration pbd)
+ {
+ //printf("PostBlitDeclaration::semantic() %s\n", toChars());
+ //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id::dtor.toChars(), ident, Id::dtor);
+ //printf("stc = x%llx\n", sc.stc);
+ if (pbd.semanticRun >= PASS.semanticdone)
+ return;
+ if (pbd._scope)
+ {
+ sc = pbd._scope;
+ pbd._scope = null;
+ }
+
+ pbd.parent = sc.parent;
+ Dsymbol p = pbd.toParent2();
+ StructDeclaration ad = p.isStructDeclaration();
+ if (!ad)
+ {
+ error(pbd.loc, "postblit can only be a member of struct, not %s `%s`", p.kind(), p.toChars());
+ pbd.type = Type.terror;
+ pbd.errors = true;
+ return;
+ }
+ if (pbd.ident == Id.postblit && pbd.semanticRun < PASS.semantic)
+ ad.postblits.push(pbd);
+ if (!pbd.type)
+ pbd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, pbd.storage_class);
+
+ sc = sc.push();
+ sc.stc &= ~STC.static_; // not static
+ sc.linkage = LINK.d;
+
+ funcDeclarationSemantic(pbd);
+
+ sc.pop();
+ }
+
+ override void visit(DtorDeclaration dd)
+ {
+ //printf("DtorDeclaration::semantic() %s\n", toChars());
+ //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id::dtor.toChars(), ident, Id::dtor);
+ if (dd.semanticRun >= PASS.semanticdone)
+ return;
+ if (dd._scope)
+ {
+ sc = dd._scope;
+ dd._scope = null;
+ }
+
+ dd.parent = sc.parent;
+ Dsymbol p = dd.toParent2();
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (!ad)
+ {
+ error(dd.loc, "destructor can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
+ dd.type = Type.terror;
+ dd.errors = true;
+ return;
+ }
+ if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic)
+ ad.dtors.push(dd);
+ if (!dd.type)
+ {
+ dd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dd.storage_class);
+ if (ad.classKind == ClassKind.cpp && dd.ident == Id.dtor)
+ {
+ if (auto cldec = ad.isClassDeclaration())
+ {
+ assert (cldec.cppDtorVtblIndex == -1); // double-call check already by dd.type
+ if (cldec.baseClass && cldec.baseClass.cppDtorVtblIndex != -1)
+ {
+ // override the base virtual
+ cldec.cppDtorVtblIndex = cldec.baseClass.cppDtorVtblIndex;
+ }
+ else if (!dd.isFinal())
+ {
+ // reserve the dtor slot for the destructor (which we'll create later)
+ cldec.cppDtorVtblIndex = cast(int)cldec.vtbl.dim;
+ cldec.vtbl.push(dd);
+ if (target.cpp.twoDtorInVtable)
+ cldec.vtbl.push(dd); // deleting destructor uses a second slot
+ }
+ }
+ }
+ }
+
+ sc = sc.push();
+ sc.stc &= ~STC.static_; // not a static destructor
+ if (sc.linkage != LINK.cpp)
+ sc.linkage = LINK.d;
+
+ funcDeclarationSemantic(dd);
+
+ sc.pop();
+ }
+
+ override void visit(StaticCtorDeclaration scd)
+ {
+ //printf("StaticCtorDeclaration::semantic()\n");
+ if (scd.semanticRun >= PASS.semanticdone)
+ return;
+ if (scd._scope)
+ {
+ sc = scd._scope;
+ scd._scope = null;
+ }
+
+ scd.parent = sc.parent;
+ Dsymbol p = scd.parent.pastMixin();
+ if (!p.isScopeDsymbol())
+ {
+ const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : "");
+ error(scd.loc, "`%sstatic` constructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars());
+ scd.type = Type.terror;
+ scd.errors = true;
+ return;
+ }
+ if (!scd.type)
+ scd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, scd.storage_class);
+
+ /* If the static ctor appears within a template instantiation,
+ * it could get called multiple times by the module constructors
+ * for different modules. Thus, protect it with a gate.
+ */
+ if (scd.isInstantiated() && scd.semanticRun < PASS.semantic)
+ {
+ /* Add this prefix to the function:
+ * static int gate;
+ * if (++gate != 1) return;
+ * Note that this is not thread safe; should not have threads
+ * during static construction.
+ */
+ auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
+ v.storage_class = STC.temp | (scd.isSharedStaticCtorDeclaration() ? STC.static_ : STC.tls);
+
+ auto sa = new Statements();
+ Statement s = new ExpStatement(Loc.initial, v);
+ sa.push(s);
+
+ Expression e = new IdentifierExp(Loc.initial, v.ident);
+ e = new AddAssignExp(Loc.initial, e, IntegerExp.literal!1);
+ e = new EqualExp(TOK.notEqual, Loc.initial, e, IntegerExp.literal!1);
+ s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
+
+ sa.push(s);
+ if (scd.fbody)
+ sa.push(scd.fbody);
+
+ scd.fbody = new CompoundStatement(Loc.initial, sa);
+ }
+
+ const LINK save = sc.linkage;
+ if (save != LINK.d)
+ {
+ const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : "");
+ deprecation(scd.loc, "`%sstatic` constructor can only be of D linkage", s);
+ // Just correct it
+ sc.linkage = LINK.d;
+ }
+ funcDeclarationSemantic(scd);
+ sc.linkage = save;
+
+ // We're going to need ModuleInfo
+ Module m = scd.getModule();
+ if (!m)
+ m = sc._module;
+ if (m)
+ {
+ m.needmoduleinfo = 1;
+ //printf("module1 %s needs moduleinfo\n", m.toChars());
+ }
+ }
+
+ override void visit(StaticDtorDeclaration sdd)
+ {
+ if (sdd.semanticRun >= PASS.semanticdone)
+ return;
+ if (sdd._scope)
+ {
+ sc = sdd._scope;
+ sdd._scope = null;
+ }
+
+ sdd.parent = sc.parent;
+ Dsymbol p = sdd.parent.pastMixin();
+ if (!p.isScopeDsymbol())
+ {
+ const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : "");
+ error(sdd.loc, "`%sstatic` destructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars());
+ sdd.type = Type.terror;
+ sdd.errors = true;
+ return;
+ }
+ if (!sdd.type)
+ sdd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sdd.storage_class);
+
+ /* If the static ctor appears within a template instantiation,
+ * it could get called multiple times by the module constructors
+ * for different modules. Thus, protect it with a gate.
+ */
+ if (sdd.isInstantiated() && sdd.semanticRun < PASS.semantic)
+ {
+ /* Add this prefix to the function:
+ * static int gate;
+ * if (--gate != 0) return;
+ * Increment gate during constructor execution.
+ * Note that this is not thread safe; should not have threads
+ * during static destruction.
+ */
+ auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
+ v.storage_class = STC.temp | (sdd.isSharedStaticDtorDeclaration() ? STC.static_ : STC.tls);
+
+ auto sa = new Statements();
+ Statement s = new ExpStatement(Loc.initial, v);
+ sa.push(s);
+
+ Expression e = new IdentifierExp(Loc.initial, v.ident);
+ e = new AddAssignExp(Loc.initial, e, IntegerExp.literal!(-1));
+ e = new EqualExp(TOK.notEqual, Loc.initial, e, IntegerExp.literal!0);
+ s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
+
+ sa.push(s);
+ if (sdd.fbody)
+ sa.push(sdd.fbody);
+
+ sdd.fbody = new CompoundStatement(Loc.initial, sa);
+
+ sdd.vgate = v;
+ }
+
+ const LINK save = sc.linkage;
+ if (save != LINK.d)
+ {
+ const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : "");
+ deprecation(sdd.loc, "`%sstatic` destructor can only be of D linkage", s);
+ // Just correct it
+ sc.linkage = LINK.d;
+ }
+ funcDeclarationSemantic(sdd);
+ sc.linkage = save;
+
+ // We're going to need ModuleInfo
+ Module m = sdd.getModule();
+ if (!m)
+ m = sc._module;
+ if (m)
+ {
+ m.needmoduleinfo = 1;
+ //printf("module2 %s needs moduleinfo\n", m.toChars());
+ }
+ }
+
+ override void visit(InvariantDeclaration invd)
+ {
+ if (invd.semanticRun >= PASS.semanticdone)
+ return;
+ if (invd._scope)
+ {
+ sc = invd._scope;
+ invd._scope = null;
+ }
+
+ invd.parent = sc.parent;
+ Dsymbol p = invd.parent.pastMixin();
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (!ad)
+ {
+ error(invd.loc, "`invariant` can only be a member of aggregate, not %s `%s`", p.kind(), p.toChars());
+ invd.type = Type.terror;
+ invd.errors = true;
+ return;
+ }
+ if (invd.ident != Id.classInvariant &&
+ invd.semanticRun < PASS.semantic &&
+ !ad.isUnionDeclaration() // users are on their own with union fields
+ )
+ ad.invs.push(invd);
+ if (!invd.type)
+ invd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, invd.storage_class);
+
+ sc = sc.push();
+ sc.stc &= ~STC.static_; // not a static invariant
+ sc.stc |= STC.const_; // invariant() is always const
+ sc.flags = (sc.flags & ~SCOPE.contract) | SCOPE.invariant_;
+ sc.linkage = LINK.d;
+
+ funcDeclarationSemantic(invd);
+
+ sc.pop();
+ }
+
+ override void visit(UnitTestDeclaration utd)
+ {
+ if (utd.semanticRun >= PASS.semanticdone)
+ return;
+ if (utd._scope)
+ {
+ sc = utd._scope;
+ utd._scope = null;
+ }
+
+ utd.visibility = sc.visibility;
+
+ utd.parent = sc.parent;
+ Dsymbol p = utd.parent.pastMixin();
+ if (!p.isScopeDsymbol())
+ {
+ error(utd.loc, "`unittest` can only be a member of module/aggregate/template, not %s `%s`", p.kind(), p.toChars());
+ utd.type = Type.terror;
+ utd.errors = true;
+ return;
+ }
+
+ if (global.params.useUnitTests)
+ {
+ if (!utd.type)
+ utd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, utd.storage_class);
+ Scope* sc2 = sc.push();
+ sc2.linkage = LINK.d;
+ funcDeclarationSemantic(utd);
+ sc2.pop();
+ }
+
+ version (none)
+ {
+ // We're going to need ModuleInfo even if the unit tests are not
+ // compiled in, because other modules may import this module and refer
+ // to this ModuleInfo.
+ // (This doesn't make sense to me?)
+ Module m = utd.getModule();
+ if (!m)
+ m = sc._module;
+ if (m)
+ {
+ //printf("module3 %s needs moduleinfo\n", m.toChars());
+ m.needmoduleinfo = 1;
+ }
+ }
+ }
+
+ override void visit(NewDeclaration nd)
+ {
+ //printf("NewDeclaration::semantic()\n");
+ if (nd.semanticRun >= PASS.semanticdone)
+ return;
+ if (!nd.type)
+ nd.type = new TypeFunction(ParameterList(), Type.tvoid.pointerTo(), LINK.d, nd.storage_class);
+
+ funcDeclarationSemantic(nd);
+ }
+
+ override void visit(StructDeclaration sd)
+ {
+ //printf("StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok);
+
+ //static int count; if (++count == 20) assert(0);
+
+ if (sd.semanticRun >= PASS.semanticdone)
+ return;
+ int errors = global.errors;
+
+ //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", this, sd.toPrettyChars(), sd.sizeok);
+ Scope* scx = null;
+ if (sd._scope)
+ {
+ sc = sd._scope;
+ scx = sd._scope; // save so we don't make redundant copies
+ sd._scope = null;
+ }
+
+ if (!sd.parent)
+ {
+ assert(sc.parent && sc.func);
+ sd.parent = sc.parent;
+ }
+ assert(sd.parent && !sd.isAnonymous());
+
+ if (sd.errors)
+ sd.type = Type.terror;
+ if (sd.semanticRun == PASS.init)
+ sd.type = sd.type.addSTC(sc.stc | sd.storage_class);
+ sd.type = sd.type.typeSemantic(sd.loc, sc);
+ if (auto ts = sd.type.isTypeStruct())
+ if (ts.sym != sd)
+ {
+ auto ti = ts.sym.isInstantiated();
+ if (ti && isError(ti))
+ ts.sym = sd;
+ }
+
+ // Ungag errors when not speculative
+ Ungag ungag = sd.ungagSpeculative();
+
+ if (sd.semanticRun == PASS.init)
+ {
+ sd.visibility = sc.visibility;
+
+ sd.alignment = sc.alignment();
+
+ sd.storage_class |= sc.stc;
+ if (sd.storage_class & STC.abstract_)
+ sd.error("structs, unions cannot be `abstract`");
+
+ sd.userAttribDecl = sc.userAttribDecl;
+
+ if (sc.linkage == LINK.cpp)
+ sd.classKind = ClassKind.cpp;
+ else if (sc.linkage == LINK.c)
+ sd.classKind = ClassKind.c;
+ sd.cppnamespace = sc.namespace;
+ sd.cppmangle = sc.cppmangle;
+ }
+ else if (sd.symtab && !scx)
+ return;
+
+ sd.semanticRun = PASS.semantic;
+ UserAttributeDeclaration.checkGNUABITag(sd, sc.linkage);
+
+ if (!sd.members) // if opaque declaration
+ {
+ sd.semanticRun = PASS.semanticdone;
+ return;
+ }
+ if (!sd.symtab)
+ {
+ sd.symtab = new DsymbolTable();
+
+ sd.members.foreachDsymbol( s => s.addMember(sc, sd) );
+ }
+
+ auto sc2 = sd.newScope(sc);
+
+ /* Set scope so if there are forward references, we still might be able to
+ * resolve individual members like enums.
+ */
+ sd.members.foreachDsymbol( s => s.setScope(sc2) );
+ sd.members.foreachDsymbol( s => s.importAll(sc2) );
+ sd.members.foreachDsymbol( (s) { s.dsymbolSemantic(sc2); sd.errors |= s.errors; } );
+
+ if (sd.errors)
+ sd.type = Type.terror;
+
+ if (!sd.determineFields())
+ {
+ if (sd.type.ty != Terror)
+ {
+ sd.error(sd.loc, "circular or forward reference");
+ sd.errors = true;
+ sd.type = Type.terror;
+ }
+
+ sc2.pop();
+ sd.semanticRun = PASS.semanticdone;
+ return;
+ }
+ /* Following special member functions creation needs semantic analysis
+ * completion of sub-structs in each field types. For example, buildDtor
+ * needs to check existence of elaborate dtor in type of each fields.
+ * See the case in compilable/test14838.d
+ */
+ foreach (v; sd.fields)
+ {
+ Type tb = v.type.baseElemOf();
+ if (tb.ty != Tstruct)
+ continue;
+ auto sdec = (cast(TypeStruct)tb).sym;
+ if (sdec.semanticRun >= PASS.semanticdone)
+ continue;
+
+ sc2.pop();
+
+ //printf("\tdeferring %s\n", toChars());
+ return deferDsymbolSemantic(sd, scx);
+ }
+
+ /* Look for special member functions.
+ */
+ sd.disableNew = sd.search(Loc.initial, Id.classNew) !is null;
+
+ // Look for the constructor
+ sd.ctor = sd.searchCtor();
+
+ sd.dtor = buildDtor(sd, sc2);
+ sd.tidtor = buildExternDDtor(sd, sc2);
+ sd.hasCopyCtor = buildCopyCtor(sd, sc2);
+ sd.postblit = buildPostBlit(sd, sc2);
+
+ buildOpAssign(sd, sc2);
+ buildOpEquals(sd, sc2);
+
+ if (global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo
+ {
+ sd.xeq = buildXopEquals(sd, sc2);
+ sd.xcmp = buildXopCmp(sd, sc2);
+ sd.xhash = buildXtoHash(sd, sc2);
+ }
+
+ sd.inv = buildInv(sd, sc2);
+
+ Module.dprogress++;
+ sd.semanticRun = PASS.semanticdone;
+ //printf("-StructDeclaration::semantic(this=%p, '%s')\n", sd, sd.toChars());
+
+ sc2.pop();
+
+ if (sd.ctor)
+ {
+ Dsymbol scall = sd.search(Loc.initial, Id.call);
+ if (scall)
+ {
+ uint xerrors = global.startGagging();
+ sc = sc.push();
+ sc.tinst = null;
+ sc.minst = null;
+ auto fcall = resolveFuncCall(sd.loc, sc, scall, null, null, null, FuncResolveFlag.quiet);
+ sc = sc.pop();
+ global.endGagging(xerrors);
+
+ if (fcall && fcall.isStatic())
+ {
+ sd.error(fcall.loc, "`static opCall` is hidden by constructors and can never be called");
+ errorSupplemental(fcall.loc, "Please use a factory method instead, or replace all constructors with `static opCall`.");
+ }
+ }
+ }
+
+ if (sd.type.ty == Tstruct && (cast(TypeStruct)sd.type).sym != sd)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=19024
+ StructDeclaration sym = (cast(TypeStruct)sd.type).sym;
+ version (none)
+ {
+ printf("this = %p %s\n", sd, sd.toChars());
+ printf("type = %d sym = %p, %s\n", sd.type.ty, sym, sym.toPrettyChars());
+ }
+ sd.error("already exists at %s. Perhaps in another function with the same name?", sym.loc.toChars());
+ }
+
+ if (global.errors != errors)
+ {
+ // The type is no good.
+ sd.type = Type.terror;
+ sd.errors = true;
+ if (sd.deferred)
+ sd.deferred.errors = true;
+ }
+
+ if (sd.deferred && !global.gag)
+ {
+ sd.deferred.semantic2(sc);
+ sd.deferred.semantic3(sc);
+ }
+ }
+
+ void interfaceSemantic(ClassDeclaration cd)
+ {
+ cd.vtblInterfaces = new BaseClasses();
+ cd.vtblInterfaces.reserve(cd.interfaces.length);
+ foreach (b; cd.interfaces)
+ {
+ cd.vtblInterfaces.push(b);
+ b.copyBaseInterfaces(cd.vtblInterfaces);
+ }
+ }
+
+ override void visit(ClassDeclaration cldec)
+ {
+ //printf("ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", cldec.toChars(), cldec.type, cldec.sizeok, this);
+ //printf("\tparent = %p, '%s'\n", sc.parent, sc.parent ? sc.parent.toChars() : "");
+ //printf("sc.stc = %x\n", sc.stc);
+
+ //{ static int n; if (++n == 20) *(char*)0=0; }
+
+ if (cldec.semanticRun >= PASS.semanticdone)
+ return;
+ int errors = global.errors;
+
+ //printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
+
+ Scope* scx = null;
+ if (cldec._scope)
+ {
+ sc = cldec._scope;
+ scx = cldec._scope; // save so we don't make redundant copies
+ cldec._scope = null;
+ }
+
+ if (!cldec.parent)
+ {
+ assert(sc.parent);
+ cldec.parent = sc.parent;
+ }
+
+ if (cldec.errors)
+ cldec.type = Type.terror;
+ cldec.type = cldec.type.typeSemantic(cldec.loc, sc);
+ if (auto tc = cldec.type.isTypeClass())
+ if (tc.sym != cldec)
+ {
+ auto ti = tc.sym.isInstantiated();
+ if (ti && isError(ti))
+ tc.sym = cldec;
+ }
+
+ // Ungag errors when not speculative
+ Ungag ungag = cldec.ungagSpeculative();
+
+ if (cldec.semanticRun == PASS.init)
+ {
+ cldec.visibility = sc.visibility;
+
+ cldec.storage_class |= sc.stc;
+ if (cldec.storage_class & STC.auto_)
+ cldec.error("storage class `auto` is invalid when declaring a class, did you mean to use `scope`?");
+ if (cldec.storage_class & STC.scope_)
+ cldec.stack = true;
+ if (cldec.storage_class & STC.abstract_)
+ cldec.isabstract = ThreeState.yes;
+
+ cldec.userAttribDecl = sc.userAttribDecl;
+
+ if (sc.linkage == LINK.cpp)
+ cldec.classKind = ClassKind.cpp;
+ cldec.cppnamespace = sc.namespace;
+ cldec.cppmangle = sc.cppmangle;
+ if (sc.linkage == LINK.objc)
+ objc.setObjc(cldec);
+ }
+ else if (cldec.symtab && !scx)
+ {
+ return;
+ }
+ cldec.semanticRun = PASS.semantic;
+ UserAttributeDeclaration.checkGNUABITag(cldec, sc.linkage);
+
+ if (cldec.baseok < Baseok.done)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=12078
+ * https://issues.dlang.org/show_bug.cgi?id=12143
+ * https://issues.dlang.org/show_bug.cgi?id=15733
+ * While resolving base classes and interfaces, a base may refer
+ * the member of this derived class. In that time, if all bases of
+ * this class can be determined, we can go forward the semantc process
+ * beyond the Lancestorsdone. To do the recursive semantic analysis,
+ * temporarily set and unset `_scope` around exp().
+ */
+ T resolveBase(T)(lazy T exp)
+ {
+ if (!scx)
+ {
+ scx = sc.copy();
+ scx.setNoFree();
+ }
+ static if (!is(T == void))
+ {
+ cldec._scope = scx;
+ auto r = exp();
+ cldec._scope = null;
+ return r;
+ }
+ else
+ {
+ cldec._scope = scx;
+ exp();
+ cldec._scope = null;
+ }
+ }
+
+ cldec.baseok = Baseok.start;
+
+ // Expand any tuples in baseclasses[]
+ for (size_t i = 0; i < cldec.baseclasses.dim;)
+ {
+ auto b = (*cldec.baseclasses)[i];
+ b.type = resolveBase(b.type.typeSemantic(cldec.loc, sc));
+
+ Type tb = b.type.toBasetype();
+ if (auto tup = tb.isTypeTuple())
+ {
+ cldec.baseclasses.remove(i);
+ size_t dim = Parameter.dim(tup.arguments);
+ for (size_t j = 0; j < dim; j++)
+ {
+ Parameter arg = Parameter.getNth(tup.arguments, j);
+ b = new BaseClass(arg.type);
+ cldec.baseclasses.insert(i + j, b);
+ }
+ }
+ else
+ i++;
+ }
+
+ if (cldec.baseok >= Baseok.done)
+ {
+ //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun);
+ if (cldec.semanticRun >= PASS.semanticdone)
+ return;
+ goto Lancestorsdone;
+ }
+
+ // See if there's a base class as first in baseclasses[]
+ if (cldec.baseclasses.dim)
+ {
+ BaseClass* b = (*cldec.baseclasses)[0];
+ Type tb = b.type.toBasetype();
+ TypeClass tc = tb.isTypeClass();
+ if (!tc)
+ {
+ if (b.type != Type.terror)
+ cldec.error("base type must be `class` or `interface`, not `%s`", b.type.toChars());
+ cldec.baseclasses.remove(0);
+ goto L7;
+ }
+ if (tc.sym.isDeprecated())
+ {
+ if (!cldec.isDeprecated())
+ {
+ // Deriving from deprecated class makes this one deprecated too
+ cldec.setDeprecated();
+ tc.checkDeprecated(cldec.loc, sc);
+ }
+ }
+ if (tc.sym.isInterfaceDeclaration())
+ goto L7;
+
+ for (ClassDeclaration cdb = tc.sym; cdb; cdb = cdb.baseClass)
+ {
+ if (cdb == cldec)
+ {
+ cldec.error("circular inheritance");
+ cldec.baseclasses.remove(0);
+ goto L7;
+ }
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=11034
+ * Class inheritance hierarchy
+ * and instance size of each classes are orthogonal information.
+ * Therefore, even if tc.sym.sizeof == Sizeok.none,
+ * we need to set baseClass field for class covariance check.
+ */
+ cldec.baseClass = tc.sym;
+ b.sym = cldec.baseClass;
+
+ if (tc.sym.baseok < Baseok.done)
+ resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
+ if (tc.sym.baseok < Baseok.done)
+ {
+ //printf("\ttry later, forward reference of base class %s\n", tc.sym.toChars());
+ if (tc.sym._scope)
+ Module.addDeferredSemantic(tc.sym);
+ cldec.baseok = Baseok.none;
+ }
+ L7:
+ }
+
+ // Treat the remaining entries in baseclasses as interfaces
+ // Check for errors, handle forward references
+ int multiClassError = cldec.baseClass is null ? 0 : 1;
+
+ BCLoop:
+ for (size_t i = (cldec.baseClass ? 1 : 0); i < cldec.baseclasses.dim;)
+ {
+ BaseClass* b = (*cldec.baseclasses)[i];
+ Type tb = b.type.toBasetype();
+ TypeClass tc = tb.isTypeClass();
+ if (!tc || !tc.sym.isInterfaceDeclaration())
+ {
+ // It's a class
+ if (tc)
+ {
+ if (multiClassError == 0)
+ {
+ error(cldec.loc,"`%s`: base class must be specified first, " ~
+ "before any interfaces.", cldec.toPrettyChars());
+ multiClassError += 1;
+ }
+ else if (multiClassError >= 1)
+ {
+ if(multiClassError == 1)
+ error(cldec.loc,"`%s`: multiple class inheritance is not supported." ~
+ " Use multiple interface inheritance and/or composition.", cldec.toPrettyChars());
+ multiClassError += 1;
+
+ if (tc.sym.fields.dim)
+ errorSupplemental(cldec.loc,"`%s` has fields, consider making it a member of `%s`",
+ b.type.toChars(), cldec.type.toChars());
+ else
+ errorSupplemental(cldec.loc,"`%s` has no fields, consider making it an `interface`",
+ b.type.toChars());
+ }
+ }
+ // It's something else: e.g. `int` in `class Foo : Bar, int { ... }`
+ else if (b.type != Type.terror)
+ {
+ error(cldec.loc,"`%s`: base type must be `interface`, not `%s`",
+ cldec.toPrettyChars(), b.type.toChars());
+ }
+ cldec.baseclasses.remove(i);
+ continue;
+ }
+
+ // Check for duplicate interfaces
+ for (size_t j = (cldec.baseClass ? 1 : 0); j < i; j++)
+ {
+ BaseClass* b2 = (*cldec.baseclasses)[j];
+ if (b2.sym == tc.sym)
+ {
+ cldec.error("inherits from duplicate interface `%s`", b2.sym.toChars());
+ cldec.baseclasses.remove(i);
+ continue BCLoop;
+ }
+ }
+ if (tc.sym.isDeprecated())
+ {
+ if (!cldec.isDeprecated())
+ {
+ // Deriving from deprecated class makes this one deprecated too
+ cldec.setDeprecated();
+ tc.checkDeprecated(cldec.loc, sc);
+ }
+ }
+
+ b.sym = tc.sym;
+
+ if (tc.sym.baseok < Baseok.done)
+ resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
+ if (tc.sym.baseok < Baseok.done)
+ {
+ //printf("\ttry later, forward reference of base %s\n", tc.sym.toChars());
+ if (tc.sym._scope)
+ Module.addDeferredSemantic(tc.sym);
+ cldec.baseok = Baseok.none;
+ }
+ i++;
+ }
+ if (cldec.baseok == Baseok.none)
+ {
+ // Forward referencee of one or more bases, try again later
+ //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars());
+ return deferDsymbolSemantic(cldec, scx);
+ }
+ cldec.baseok = Baseok.done;
+
+ if (cldec.classKind == ClassKind.objc || (cldec.baseClass && cldec.baseClass.classKind == ClassKind.objc))
+ cldec.classKind = ClassKind.objc; // Objective-C classes do not inherit from Object
+
+ // If no base class, and this is not an Object, use Object as base class
+ if (!cldec.baseClass && cldec.ident != Id.Object && cldec.object && cldec.classKind == ClassKind.d)
+ {
+ void badObjectDotD()
+ {
+ cldec.error("missing or corrupt object.d");
+ fatal();
+ }
+
+ if (!cldec.object || cldec.object.errors)
+ badObjectDotD();
+
+ Type t = cldec.object.type;
+ t = t.typeSemantic(cldec.loc, sc).toBasetype();
+ if (t.ty == Terror)
+ badObjectDotD();
+ TypeClass tc = t.isTypeClass();
+ assert(tc);
+
+ auto b = new BaseClass(tc);
+ cldec.baseclasses.shift(b);
+
+ cldec.baseClass = tc.sym;
+ assert(!cldec.baseClass.isInterfaceDeclaration());
+ b.sym = cldec.baseClass;
+ }
+ if (cldec.baseClass)
+ {
+ if (cldec.baseClass.storage_class & STC.final_)
+ cldec.error("cannot inherit from class `%s` because it is `final`", cldec.baseClass.toChars());
+
+ // Inherit properties from base class
+ if (cldec.baseClass.isCOMclass())
+ cldec.com = true;
+ if (cldec.baseClass.isCPPclass())
+ cldec.classKind = ClassKind.cpp;
+ if (cldec.baseClass.stack)
+ cldec.stack = true;
+ cldec.enclosing = cldec.baseClass.enclosing;
+ cldec.storage_class |= cldec.baseClass.storage_class & STC.TYPECTOR;
+ }
+
+ cldec.interfaces = cldec.baseclasses.tdata()[(cldec.baseClass ? 1 : 0) .. cldec.baseclasses.dim];
+ foreach (b; cldec.interfaces)
+ {
+ // If this is an interface, and it derives from a COM interface,
+ // then this is a COM interface too.
+ if (b.sym.isCOMinterface())
+ cldec.com = true;
+ if (cldec.classKind == ClassKind.cpp && !b.sym.isCPPinterface())
+ {
+ error(cldec.loc, "C++ class `%s` cannot implement D interface `%s`",
+ cldec.toPrettyChars(), b.sym.toPrettyChars());
+ }
+ }
+ interfaceSemantic(cldec);
+ }
+ Lancestorsdone:
+ //printf("\tClassDeclaration.dsymbolSemantic(%s) baseok = %d\n", toChars(), baseok);
+
+ if (!cldec.members) // if opaque declaration
+ {
+ cldec.semanticRun = PASS.semanticdone;
+ return;
+ }
+ if (!cldec.symtab)
+ {
+ cldec.symtab = new DsymbolTable();
+
+ /* https://issues.dlang.org/show_bug.cgi?id=12152
+ * The semantic analysis of base classes should be finished
+ * before the members semantic analysis of this class, in order to determine
+ * vtbl in this class. However if a base class refers the member of this class,
+ * it can be resolved as a normal forward reference.
+ * Call addMember() and setScope() to make this class members visible from the base classes.
+ */
+ cldec.members.foreachDsymbol( s => s.addMember(sc, cldec) );
+
+ auto sc2 = cldec.newScope(sc);
+
+ /* Set scope so if there are forward references, we still might be able to
+ * resolve individual members like enums.
+ */
+ cldec.members.foreachDsymbol( s => s.setScope(sc2) );
+
+ sc2.pop();
+ }
+
+ for (size_t i = 0; i < cldec.baseclasses.dim; i++)
+ {
+ BaseClass* b = (*cldec.baseclasses)[i];
+ Type tb = b.type.toBasetype();
+ TypeClass tc = tb.isTypeClass();
+ if (tc.sym.semanticRun < PASS.semanticdone)
+ {
+ // Forward referencee of one or more bases, try again later
+ if (tc.sym._scope)
+ Module.addDeferredSemantic(tc.sym);
+ //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars());
+ return deferDsymbolSemantic(cldec, scx);
+ }
+ }
+
+ if (cldec.baseok == Baseok.done)
+ {
+ cldec.baseok = Baseok.semanticdone;
+ objc.setMetaclass(cldec, sc);
+
+ // initialize vtbl
+ if (cldec.baseClass)
+ {
+ if (cldec.classKind == ClassKind.cpp && cldec.baseClass.vtbl.dim == 0)
+ {
+ cldec.error("C++ base class `%s` needs at least one virtual function", cldec.baseClass.toChars());
+ }
+
+ // Copy vtbl[] from base class
+ cldec.vtbl.setDim(cldec.baseClass.vtbl.dim);
+ memcpy(cldec.vtbl.tdata(), cldec.baseClass.vtbl.tdata(), (void*).sizeof * cldec.vtbl.dim);
+
+ cldec.vthis = cldec.baseClass.vthis;
+ cldec.vthis2 = cldec.baseClass.vthis2;
+ }
+ else
+ {
+ // No base class, so this is the root of the class hierarchy
+ cldec.vtbl.setDim(0);
+ if (cldec.vtblOffset())
+ cldec.vtbl.push(cldec); // leave room for classinfo as first member
+ }
+
+ /* If this is a nested class, add the hidden 'this'
+ * member which is a pointer to the enclosing scope.
+ */
+ if (cldec.vthis) // if inheriting from nested class
+ {
+ // Use the base class's 'this' member
+ if (cldec.storage_class & STC.static_)
+ cldec.error("static class cannot inherit from nested class `%s`", cldec.baseClass.toChars());
+ if (cldec.toParentLocal() != cldec.baseClass.toParentLocal() &&
+ (!cldec.toParentLocal() ||
+ !cldec.baseClass.toParentLocal().getType() ||
+ !cldec.baseClass.toParentLocal().getType().isBaseOf(cldec.toParentLocal().getType(), null)))
+ {
+ if (cldec.toParentLocal())
+ {
+ cldec.error("is nested within `%s`, but super class `%s` is nested within `%s`",
+ cldec.toParentLocal().toChars(),
+ cldec.baseClass.toChars(),
+ cldec.baseClass.toParentLocal().toChars());
+ }
+ else
+ {
+ cldec.error("is not nested, but super class `%s` is nested within `%s`",
+ cldec.baseClass.toChars(),
+ cldec.baseClass.toParentLocal().toChars());
+ }
+ cldec.enclosing = null;
+ }
+ if (cldec.vthis2)
+ {
+ if (cldec.toParent2() != cldec.baseClass.toParent2() &&
+ (!cldec.toParent2() ||
+ !cldec.baseClass.toParent2().getType() ||
+ !cldec.baseClass.toParent2().getType().isBaseOf(cldec.toParent2().getType(), null)))
+ {
+ if (cldec.toParent2() && cldec.toParent2() != cldec.toParentLocal())
+ {
+ cldec.error("needs the frame pointer of `%s`, but super class `%s` needs the frame pointer of `%s`",
+ cldec.toParent2().toChars(),
+ cldec.baseClass.toChars(),
+ cldec.baseClass.toParent2().toChars());
+ }
+ else
+ {
+ cldec.error("doesn't need a frame pointer, but super class `%s` needs the frame pointer of `%s`",
+ cldec.baseClass.toChars(),
+ cldec.baseClass.toParent2().toChars());
+ }
+ }
+ }
+ else
+ cldec.makeNested2();
+ }
+ else
+ cldec.makeNested();
+ }
+
+ auto sc2 = cldec.newScope(sc);
+
+ cldec.members.foreachDsymbol( s => s.importAll(sc2) );
+
+ // Note that members.dim can grow due to tuple expansion during semantic()
+ cldec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
+
+ if (!cldec.determineFields())
+ {
+ assert(cldec.type == Type.terror);
+ sc2.pop();
+ return;
+ }
+ /* Following special member functions creation needs semantic analysis
+ * completion of sub-structs in each field types.
+ */
+ foreach (v; cldec.fields)
+ {
+ Type tb = v.type.baseElemOf();
+ if (tb.ty != Tstruct)
+ continue;
+ auto sd = (cast(TypeStruct)tb).sym;
+ if (sd.semanticRun >= PASS.semanticdone)
+ continue;
+
+ sc2.pop();
+
+ //printf("\tdeferring %s\n", toChars());
+ return deferDsymbolSemantic(cldec, scx);
+ }
+
+ /* Look for special member functions.
+ * They must be in this class, not in a base class.
+ */
+ // Can be in base class
+ cldec.disableNew = cldec.search(Loc.initial, Id.classNew) !is null;
+
+ // Look for the constructor
+ cldec.ctor = cldec.searchCtor();
+
+ if (!cldec.ctor && cldec.noDefaultCtor)
+ {
+ // A class object is always created by constructor, so this check is legitimate.
+ foreach (v; cldec.fields)
+ {
+ if (v.storage_class & STC.nodefaultctor)
+ error(v.loc, "field `%s` must be initialized in constructor", v.toChars());
+ }
+ }
+
+ // If this class has no constructor, but base class has a default
+ // ctor, create a constructor:
+ // this() { }
+ if (!cldec.ctor && cldec.baseClass && cldec.baseClass.ctor)
+ {
+ auto fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type, null, FuncResolveFlag.quiet);
+ if (!fd) // try shared base ctor instead
+ fd = resolveFuncCall(cldec.loc, sc2, cldec.baseClass.ctor, null, cldec.type.sharedOf, null, FuncResolveFlag.quiet);
+ if (fd && !fd.errors)
+ {
+ //printf("Creating default this(){} for class %s\n", toChars());
+ auto btf = fd.type.toTypeFunction();
+ auto tf = new TypeFunction(ParameterList(), null, LINK.d, fd.storage_class);
+ tf.mod = btf.mod;
+ // Don't copy @safe, ... from the base class constructor and let it be inferred instead
+ // This is required if other lowerings add code to the generated constructor which
+ // is less strict (e.g. `preview=dtorfields` might introduce a call to a less qualified dtor)
+
+ auto ctor = new CtorDeclaration(cldec.loc, Loc.initial, 0, tf);
+ ctor.storage_class |= STC.inference;
+ ctor.generated = true;
+ ctor.fbody = new CompoundStatement(Loc.initial, new Statements());
+
+ cldec.members.push(ctor);
+ ctor.addMember(sc, cldec);
+ ctor.dsymbolSemantic(sc2);
+
+ cldec.ctor = ctor;
+ cldec.defaultCtor = ctor;
+ }
+ else
+ {
+ cldec.error("cannot implicitly generate a default constructor when base class `%s` is missing a default constructor",
+ cldec.baseClass.toPrettyChars());
+ }
+ }
+
+ cldec.dtor = buildDtor(cldec, sc2);
+ cldec.tidtor = buildExternDDtor(cldec, sc2);
+
+ if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1)
+ {
+ // now we've built the aggregate destructor, we'll make it virtual and assign it to the reserved vtable slot
+ cldec.dtor.vtblIndex = cldec.cppDtorVtblIndex;
+ cldec.vtbl[cldec.cppDtorVtblIndex] = cldec.dtor;
+
+ if (target.cpp.twoDtorInVtable)
+ {
+ // TODO: create a C++ compatible deleting destructor (call out to `operator delete`)
+ // for the moment, we'll call the non-deleting destructor and leak
+ cldec.vtbl[cldec.cppDtorVtblIndex + 1] = cldec.dtor;
+ }
+ }
+
+ if (auto f = hasIdentityOpAssign(cldec, sc2))
+ {
+ if (!(f.storage_class & STC.disable))
+ cldec.error(f.loc, "identity assignment operator overload is illegal");
+ }
+
+ cldec.inv = buildInv(cldec, sc2);
+
+ Module.dprogress++;
+ cldec.semanticRun = PASS.semanticdone;
+ //printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+
+ sc2.pop();
+
+ /* isAbstract() is undecidable in some cases because of circular dependencies.
+ * Now that semantic is finished, get a definitive result, and error if it is not the same.
+ */
+ if (cldec.isabstract != ThreeState.none) // if evaluated it before completion
+ {
+ const isabstractsave = cldec.isabstract;
+ cldec.isabstract = ThreeState.none;
+ cldec.isAbstract(); // recalculate
+ if (cldec.isabstract != isabstractsave)
+ {
+ cldec.error("cannot infer `abstract` attribute due to circular dependencies");
+ }
+ }
+
+ if (cldec.type.ty == Tclass && (cast(TypeClass)cldec.type).sym != cldec)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=17492
+ ClassDeclaration cd = (cast(TypeClass)cldec.type).sym;
+ version (none)
+ {
+ printf("this = %p %s\n", cldec, cldec.toPrettyChars());
+ printf("type = %d sym = %p, %s\n", cldec.type.ty, cd, cd.toPrettyChars());
+ }
+ cldec.error("already exists at %s. Perhaps in another function with the same name?", cd.loc.toChars());
+ }
+
+ if (global.errors != errors)
+ {
+ // The type is no good.
+ cldec.type = Type.terror;
+ cldec.errors = true;
+ if (cldec.deferred)
+ cldec.deferred.errors = true;
+ }
+
+ // Verify fields of a synchronized class are not public
+ if (cldec.storage_class & STC.synchronized_)
+ {
+ foreach (vd; cldec.fields)
+ {
+ if (!vd.isThisDeclaration() &&
+ vd.visible() >= Visibility(Visibility.Kind.public_))
+ {
+ vd.error("Field members of a `synchronized` class cannot be `%s`",
+ visibilityToChars(vd.visible().kind));
+ }
+ }
+ }
+
+ if (cldec.deferred && !global.gag)
+ {
+ cldec.deferred.semantic2(sc);
+ cldec.deferred.semantic3(sc);
+ }
+ //printf("-ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this);
+
+ // @@@DEPRECATED@@@ https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+ // Deprecated in 2.087
+ // Make an error in 2.091
+ // Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
+ if (0 && // deprecation disabled for now to accommodate existing extensive use
+ cldec.storage_class & STC.scope_)
+ deprecation(cldec.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site.");
+ }
+
+ override void visit(InterfaceDeclaration idec)
+ {
+ /// Returns: `true` is this is an anonymous Objective-C metaclass
+ static bool isAnonymousMetaclass(InterfaceDeclaration idec)
+ {
+ return idec.classKind == ClassKind.objc &&
+ idec.objc.isMeta &&
+ idec.isAnonymous;
+ }
+
+ //printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+ if (idec.semanticRun >= PASS.semanticdone)
+ return;
+ int errors = global.errors;
+
+ //printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+
+ Scope* scx = null;
+ if (idec._scope)
+ {
+ sc = idec._scope;
+ scx = idec._scope; // save so we don't make redundant copies
+ idec._scope = null;
+ }
+
+ if (!idec.parent)
+ {
+ assert(sc.parent && sc.func);
+ idec.parent = sc.parent;
+ }
+ // Objective-C metaclasses are anonymous
+ assert(idec.parent && !idec.isAnonymous || isAnonymousMetaclass(idec));
+
+ if (idec.errors)
+ idec.type = Type.terror;
+ idec.type = idec.type.typeSemantic(idec.loc, sc);
+ if (idec.type.ty == Tclass && (cast(TypeClass)idec.type).sym != idec)
+ {
+ auto ti = (cast(TypeClass)idec.type).sym.isInstantiated();
+ if (ti && isError(ti))
+ (cast(TypeClass)idec.type).sym = idec;
+ }
+
+ // Ungag errors when not speculative
+ Ungag ungag = idec.ungagSpeculative();
+
+ if (idec.semanticRun == PASS.init)
+ {
+ idec.visibility = sc.visibility;
+
+ idec.storage_class |= sc.stc;
+ idec.userAttribDecl = sc.userAttribDecl;
+ }
+ else if (idec.symtab)
+ {
+ if (idec.sizeok == Sizeok.done || !scx)
+ {
+ idec.semanticRun = PASS.semanticdone;
+ return;
+ }
+ }
+ idec.semanticRun = PASS.semantic;
+
+ if (idec.baseok < Baseok.done)
+ {
+ T resolveBase(T)(lazy T exp)
+ {
+ if (!scx)
+ {
+ scx = sc.copy();
+ scx.setNoFree();
+ }
+ static if (!is(T == void))
+ {
+ idec._scope = scx;
+ auto r = exp();
+ idec._scope = null;
+ return r;
+ }
+ else
+ {
+ idec._scope = scx;
+ exp();
+ idec._scope = null;
+ }
+ }
+
+ idec.baseok = Baseok.start;
+
+ // Expand any tuples in baseclasses[]
+ for (size_t i = 0; i < idec.baseclasses.dim;)
+ {
+ auto b = (*idec.baseclasses)[i];
+ b.type = resolveBase(b.type.typeSemantic(idec.loc, sc));
+
+ Type tb = b.type.toBasetype();
+ if (auto tup = tb.isTypeTuple())
+ {
+ idec.baseclasses.remove(i);
+ size_t dim = Parameter.dim(tup.arguments);
+ for (size_t j = 0; j < dim; j++)
+ {
+ Parameter arg = Parameter.getNth(tup.arguments, j);
+ b = new BaseClass(arg.type);
+ idec.baseclasses.insert(i + j, b);
+ }
+ }
+ else
+ i++;
+ }
+
+ if (idec.baseok >= Baseok.done)
+ {
+ //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun);
+ if (idec.semanticRun >= PASS.semanticdone)
+ return;
+ goto Lancestorsdone;
+ }
+
+ if (!idec.baseclasses.dim && sc.linkage == LINK.cpp)
+ idec.classKind = ClassKind.cpp;
+ idec.cppnamespace = sc.namespace;
+ UserAttributeDeclaration.checkGNUABITag(idec, sc.linkage);
+
+ if (sc.linkage == LINK.objc)
+ objc.setObjc(idec);
+
+ // Check for errors, handle forward references
+ BCLoop:
+ for (size_t i = 0; i < idec.baseclasses.dim;)
+ {
+ BaseClass* b = (*idec.baseclasses)[i];
+ Type tb = b.type.toBasetype();
+ TypeClass tc = (tb.ty == Tclass) ? cast(TypeClass)tb : null;
+ if (!tc || !tc.sym.isInterfaceDeclaration())
+ {
+ if (b.type != Type.terror)
+ idec.error("base type must be `interface`, not `%s`", b.type.toChars());
+ idec.baseclasses.remove(i);
+ continue;
+ }
+
+ // Check for duplicate interfaces
+ for (size_t j = 0; j < i; j++)
+ {
+ BaseClass* b2 = (*idec.baseclasses)[j];
+ if (b2.sym == tc.sym)
+ {
+ idec.error("inherits from duplicate interface `%s`", b2.sym.toChars());
+ idec.baseclasses.remove(i);
+ continue BCLoop;
+ }
+ }
+ if (tc.sym == idec || idec.isBaseOf2(tc.sym))
+ {
+ idec.error("circular inheritance of interface");
+ idec.baseclasses.remove(i);
+ continue;
+ }
+ if (tc.sym.isDeprecated())
+ {
+ if (!idec.isDeprecated())
+ {
+ // Deriving from deprecated interface makes this one deprecated too
+ idec.setDeprecated();
+ tc.checkDeprecated(idec.loc, sc);
+ }
+ }
+
+ b.sym = tc.sym;
+
+ if (tc.sym.baseok < Baseok.done)
+ resolveBase(tc.sym.dsymbolSemantic(null)); // Try to resolve forward reference
+ if (tc.sym.baseok < Baseok.done)
+ {
+ //printf("\ttry later, forward reference of base %s\n", tc.sym.toChars());
+ if (tc.sym._scope)
+ Module.addDeferredSemantic(tc.sym);
+ idec.baseok = Baseok.none;
+ }
+ i++;
+ }
+ if (idec.baseok == Baseok.none)
+ {
+ // Forward referencee of one or more bases, try again later
+ return deferDsymbolSemantic(idec, scx);
+ }
+ idec.baseok = Baseok.done;
+
+ idec.interfaces = idec.baseclasses.tdata()[0 .. idec.baseclasses.dim];
+ foreach (b; idec.interfaces)
+ {
+ // If this is an interface, and it derives from a COM interface,
+ // then this is a COM interface too.
+ if (b.sym.isCOMinterface())
+ idec.com = true;
+ if (b.sym.isCPPinterface())
+ idec.classKind = ClassKind.cpp;
+ }
+
+ interfaceSemantic(idec);
+ }
+ Lancestorsdone:
+
+ if (!idec.members) // if opaque declaration
+ {
+ idec.semanticRun = PASS.semanticdone;
+ return;
+ }
+ if (!idec.symtab)
+ idec.symtab = new DsymbolTable();
+
+ for (size_t i = 0; i < idec.baseclasses.dim; i++)
+ {
+ BaseClass* b = (*idec.baseclasses)[i];
+ Type tb = b.type.toBasetype();
+ TypeClass tc = tb.isTypeClass();
+ if (tc.sym.semanticRun < PASS.semanticdone)
+ {
+ // Forward referencee of one or more bases, try again later
+ if (tc.sym._scope)
+ Module.addDeferredSemantic(tc.sym);
+ return deferDsymbolSemantic(idec, scx);
+ }
+ }
+
+ if (idec.baseok == Baseok.done)
+ {
+ idec.baseok = Baseok.semanticdone;
+ objc.setMetaclass(idec, sc);
+
+ // initialize vtbl
+ if (idec.vtblOffset())
+ idec.vtbl.push(idec); // leave room at vtbl[0] for classinfo
+
+ // Cat together the vtbl[]'s from base interfaces
+ foreach (i, b; idec.interfaces)
+ {
+ // Skip if b has already appeared
+ for (size_t k = 0; k < i; k++)
+ {
+ if (b == idec.interfaces[k])
+ goto Lcontinue;
+ }
+
+ // Copy vtbl[] from base class
+ if (b.sym.vtblOffset())
+ {
+ size_t d = b.sym.vtbl.dim;
+ if (d > 1)
+ {
+ idec.vtbl.pushSlice(b.sym.vtbl[1 .. d]);
+ }
+ }
+ else
+ {
+ idec.vtbl.append(&b.sym.vtbl);
+ }
+
+ Lcontinue:
+ }
+ }
+
+ idec.members.foreachDsymbol( s => s.addMember(sc, idec) );
+
+ auto sc2 = idec.newScope(sc);
+
+ /* Set scope so if there are forward references, we still might be able to
+ * resolve individual members like enums.
+ */
+ idec.members.foreachDsymbol( s => s.setScope(sc2) );
+
+ idec.members.foreachDsymbol( s => s.importAll(sc2) );
+
+ idec.members.foreachDsymbol( s => s.dsymbolSemantic(sc2) );
+
+ Module.dprogress++;
+ idec.semanticRun = PASS.semanticdone;
+ //printf("-InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type);
+
+ sc2.pop();
+
+ if (global.errors != errors)
+ {
+ // The type is no good.
+ idec.type = Type.terror;
+ }
+
+ version (none)
+ {
+ if (type.ty == Tclass && (cast(TypeClass)idec.type).sym != idec)
+ {
+ printf("this = %p %s\n", idec, idec.toChars());
+ printf("type = %d sym = %p\n", idec.type.ty, (cast(TypeClass)idec.type).sym);
+ }
+ }
+ assert(idec.type.ty != Tclass || (cast(TypeClass)idec.type).sym == idec);
+
+ // @@@DEPRECATED@@@https://dlang.org/deprecate.html#scope%20as%20a%20type%20constraint
+ // Deprecated in 2.087
+ // Remove in 2.091
+ // Don't forget to remove code at https://github.com/dlang/dmd/blob/b2f8274ba76358607fc3297a1e9f361480f9bcf9/src/dmd/dsymbolsem.d#L1032-L1036
+ if (idec.storage_class & STC.scope_)
+ deprecation(idec.loc, "`scope` as a type constraint is deprecated. Use `scope` at the usage site.");
+ }
+}
+
+/*******************************************
+ * Add members of EnumDeclaration to the symbol table(s).
+ * Params:
+ * ed = EnumDeclaration
+ * sc = context of `ed`
+ * sds = symbol table that `ed` resides in
+ */
+void addEnumMembers(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds)
+{
+ if (ed.added)
+ return;
+ ed.added = true;
+
+ if (!ed.members)
+ return;
+
+ const bool isCEnum = (sc.flags & SCOPE.Cfile) != 0; // it's an ImportC enum
+ const bool isAnon = ed.isAnonymous();
+
+ if ((isCEnum || isAnon) && !sds.symtab)
+ sds.symtab = new DsymbolTable();
+
+ if ((isCEnum || !isAnon) && !ed.symtab)
+ ed.symtab = new DsymbolTable();
+
+ ed.members.foreachDsymbol( (s)
+ {
+ if (EnumMember em = s.isEnumMember())
+ {
+ em.ed = ed;
+ if (isCEnum)
+ {
+ em.addMember(sc, ed); // add em to ed's symbol table
+ em.addMember(sc, sds); // add em to symbol table that ed is in
+ em.parent = ed; // restore it after previous addMember() changed it
+ }
+ else
+ {
+ em.addMember(sc, isAnon ? sds : ed);
+ }
+ }
+ });
+}
+
+void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, Expressions* fargs)
+{
+ //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc);
+ version (none)
+ {
+ for (Dsymbol s = tempinst; s; s = s.parent)
+ {
+ printf("\t%s\n", s.toChars());
+ }
+ printf("Scope\n");
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ printf("\t%s parent %s\n", scx._module ? scx._module.toChars() : "null", scx.parent ? scx.parent.toChars() : "null");
+ }
+ }
+
+ static if (LOG)
+ {
+ printf("\n+TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
+ }
+ if (tempinst.inst) // if semantic() was already run
+ {
+ static if (LOG)
+ {
+ printf("-TemplateInstance.dsymbolSemantic('%s', this=%p) already run\n",
+ tempinst.inst.toChars(), tempinst.inst);
+ }
+ return;
+ }
+ if (tempinst.semanticRun != PASS.init)
+ {
+ static if (LOG)
+ {
+ printf("Recursive template expansion\n");
+ }
+ auto ungag = Ungag(global.gag);
+ if (!tempinst.gagged)
+ global.gag = 0;
+ tempinst.error(tempinst.loc, "recursive template expansion");
+ if (tempinst.gagged)
+ tempinst.semanticRun = PASS.init;
+ else
+ tempinst.inst = tempinst;
+ tempinst.errors = true;
+ return;
+ }
+
+ // Get the enclosing template instance from the scope tinst
+ tempinst.tinst = sc.tinst;
+
+ // Get the instantiating module from the scope minst
+ tempinst.minst = sc.minst;
+ // https://issues.dlang.org/show_bug.cgi?id=10920
+ // If the enclosing function is non-root symbol,
+ // this instance should be speculative.
+ if (!tempinst.tinst && sc.func && sc.func.inNonRoot())
+ {
+ tempinst.minst = null;
+ }
+
+ tempinst.gagged = (global.gag > 0);
+
+ tempinst.semanticRun = PASS.semantic;
+
+ static if (LOG)
+ {
+ printf("\tdo semantic\n");
+ }
+ /* Find template declaration first,
+ * then run semantic on each argument (place results in tiargs[]),
+ * last find most specialized template from overload list/set.
+ */
+ if (!tempinst.findTempDecl(sc, null) || !tempinst.semanticTiargs(sc) || !tempinst.findBestMatch(sc, fargs))
+ {
+ Lerror:
+ if (tempinst.gagged)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13220
+ // Roll back status for later semantic re-running
+ tempinst.semanticRun = PASS.init;
+ }
+ else
+ tempinst.inst = tempinst;
+ tempinst.errors = true;
+ return;
+ }
+ TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ TemplateStats.incInstance(tempdecl, tempinst);
+
+ tempdecl.checkDeprecated(tempinst.loc, sc);
+
+ // If tempdecl is a mixin, disallow it
+ if (tempdecl.ismixin)
+ {
+ tempinst.error("mixin templates are not regular templates");
+ goto Lerror;
+ }
+
+ tempinst.hasNestedArgs(tempinst.tiargs, tempdecl.isstatic);
+ if (tempinst.errors)
+ goto Lerror;
+
+ // Copy the tempdecl namespace (not the scope one)
+ tempinst.cppnamespace = tempdecl.cppnamespace;
+ if (tempinst.cppnamespace)
+ tempinst.cppnamespace.dsymbolSemantic(sc);
+
+ /* Greatly simplified semantic processing for AliasSeq templates
+ */
+ if (tempdecl.isTrivialAliasSeq)
+ {
+ tempinst.inst = tempinst;
+ return aliasSeqInstanceSemantic(tempinst, sc, tempdecl);
+ }
+
+ /* Greatly simplified semantic processing for Alias templates
+ */
+ else if (tempdecl.isTrivialAlias)
+ {
+ tempinst.inst = tempinst;
+ return aliasInstanceSemantic(tempinst, sc, tempdecl);
+ }
+
+ /* See if there is an existing TemplateInstantiation that already
+ * implements the typeargs. If so, just refer to that one instead.
+ */
+ tempinst.inst = tempdecl.findExistingInstance(tempinst, fargs);
+ TemplateInstance errinst = null;
+ if (!tempinst.inst)
+ {
+ // So, we need to implement 'this' instance.
+ }
+ else if (tempinst.inst.gagged && !tempinst.gagged && tempinst.inst.errors)
+ {
+ // If the first instantiation had failed, re-run semantic,
+ // so that error messages are shown.
+ errinst = tempinst.inst;
+ }
+ else
+ {
+ // It's a match
+ tempinst.parent = tempinst.inst.parent;
+ tempinst.errors = tempinst.inst.errors;
+
+ // If both this and the previous instantiation were gagged,
+ // use the number of errors that happened last time.
+ global.errors += tempinst.errors;
+ global.gaggedErrors += tempinst.errors;
+
+ // If the first instantiation was gagged, but this is not:
+ if (tempinst.inst.gagged)
+ {
+ // It had succeeded, mark it is a non-gagged instantiation,
+ // and reuse it.
+ tempinst.inst.gagged = tempinst.gagged;
+ }
+
+ tempinst.tnext = tempinst.inst.tnext;
+ tempinst.inst.tnext = tempinst;
+
+ /* A module can have explicit template instance and its alias
+ * in module scope (e,g, `alias Base64 = Base64Impl!('+', '/');`).
+ * If the first instantiation 'inst' had happened in non-root module,
+ * compiler can assume that its instantiated code would be included
+ * in the separately compiled obj/lib file (e.g. phobos.lib).
+ *
+ * However, if 'this' second instantiation happened in root module,
+ * compiler might need to invoke its codegen
+ * (https://issues.dlang.org/show_bug.cgi?id=2500 & https://issues.dlang.org/show_bug.cgi?id=2644).
+ * But whole import graph is not determined until all semantic pass finished,
+ * so 'inst' should conservatively finish the semantic3 pass for the codegen.
+ */
+ if (tempinst.minst && tempinst.minst.isRoot() && !(tempinst.inst.minst && tempinst.inst.minst.isRoot()))
+ {
+ /* Swap the position of 'inst' and 'this' in the instantiation graph.
+ * Then, the primary instance `inst` will be changed to a root instance,
+ * along with all members of `inst` having their scopes updated.
+ *
+ * Before:
+ * non-root -> A!() -> B!()[inst] -> C!() { members[non-root] }
+ * |
+ * root -> D!() -> B!()[this]
+ *
+ * After:
+ * non-root -> A!() -> B!()[this]
+ * |
+ * root -> D!() -> B!()[inst] -> C!() { members[root] }
+ */
+ Module mi = tempinst.minst;
+ TemplateInstance ti = tempinst.tinst;
+ tempinst.minst = tempinst.inst.minst;
+ tempinst.tinst = tempinst.inst.tinst;
+ tempinst.inst.minst = mi;
+ tempinst.inst.tinst = ti;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=21299
+ `minst` has been updated on the primary instance `inst` so it is
+ now coming from a root module, however all Dsymbol `inst.members`
+ of the instance still have their `_scope.minst` pointing at the
+ original non-root module. We must now propagate `minst` to all
+ members so that forward referenced dependencies that get
+ instantiated will also be appended to the root module, otherwise
+ there will be undefined references at link-time. */
+ extern (C++) final class InstMemberWalker : Visitor
+ {
+ alias visit = Visitor.visit;
+ TemplateInstance inst;
+
+ extern (D) this(TemplateInstance inst)
+ {
+ this.inst = inst;
+ }
+
+ override void visit(Dsymbol d)
+ {
+ if (d._scope)
+ d._scope.minst = inst.minst;
+ }
+
+ override void visit(ScopeDsymbol sds)
+ {
+ sds.members.foreachDsymbol( s => s.accept(this) );
+ visit(cast(Dsymbol)sds);
+ }
+
+ override void visit(AttribDeclaration ad)
+ {
+ ad.include(null).foreachDsymbol( s => s.accept(this) );
+ visit(cast(Dsymbol)ad);
+ }
+
+ override void visit(ConditionalDeclaration cd)
+ {
+ if (cd.condition.inc)
+ visit(cast(AttribDeclaration)cd);
+ else
+ visit(cast(Dsymbol)cd);
+ }
+ }
+ scope v = new InstMemberWalker(tempinst.inst);
+ tempinst.inst.accept(v);
+
+ if (tempinst.minst) // if inst was not speculative
+ {
+ /* Add 'inst' once again to the root module members[], then the
+ * instance members will get codegen chances.
+ */
+ tempinst.inst.appendToModuleMember();
+ }
+ }
+
+ // modules imported by an existing instance should be added to the module
+ // that instantiates the instance.
+ if (tempinst.minst)
+ foreach(imp; tempinst.inst.importedModules)
+ if (!tempinst.minst.aimports.contains(imp))
+ tempinst.minst.aimports.push(imp);
+
+ static if (LOG)
+ {
+ printf("\tit's a match with instance %p, %d\n", tempinst.inst, tempinst.inst.semanticRun);
+ }
+ return;
+ }
+ static if (LOG)
+ {
+ printf("\timplement template instance %s '%s'\n", tempdecl.parent.toChars(), tempinst.toChars());
+ printf("\ttempdecl %s\n", tempdecl.toChars());
+ }
+ uint errorsave = global.errors;
+
+ tempinst.inst = tempinst;
+ tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent;
+ //printf("parent = '%s'\n", parent.kind());
+
+ TemplateStats.incUnique(tempdecl, tempinst);
+
+ TemplateInstance tempdecl_instance_idx = tempdecl.addInstance(tempinst);
+
+ //getIdent();
+
+ // Store the place we added it to in target_symbol_list(_idx) so we can
+ // remove it later if we encounter an error.
+ Dsymbols* target_symbol_list = tempinst.appendToModuleMember();
+ size_t target_symbol_list_idx = target_symbol_list ? target_symbol_list.dim - 1 : 0;
+
+ // Copy the syntax trees from the TemplateDeclaration
+ tempinst.members = Dsymbol.arraySyntaxCopy(tempdecl.members);
+
+ // resolve TemplateThisParameter
+ for (size_t i = 0; i < tempdecl.parameters.dim; i++)
+ {
+ if ((*tempdecl.parameters)[i].isTemplateThisParameter() is null)
+ continue;
+ Type t = isType((*tempinst.tiargs)[i]);
+ assert(t);
+ if (StorageClass stc = ModToStc(t.mod))
+ {
+ //printf("t = %s, stc = x%llx\n", t.toChars(), stc);
+ auto s = new Dsymbols();
+ s.push(new StorageClassDeclaration(stc, tempinst.members));
+ tempinst.members = s;
+ }
+ break;
+ }
+
+ // Create our own scope for the template parameters
+ Scope* _scope = tempdecl._scope;
+ if (tempdecl.semanticRun == PASS.init)
+ {
+ tempinst.error("template instantiation `%s` forward references template declaration `%s`", tempinst.toChars(), tempdecl.toChars());
+ return;
+ }
+
+ static if (LOG)
+ {
+ printf("\tcreate scope for template parameters '%s'\n", tempinst.toChars());
+ }
+ tempinst.argsym = new ScopeDsymbol();
+ tempinst.argsym.parent = _scope.parent;
+ _scope = _scope.push(tempinst.argsym);
+ _scope.tinst = tempinst;
+ _scope.minst = tempinst.minst;
+ //scope.stc = 0;
+
+ // Declare each template parameter as an alias for the argument type
+ Scope* paramscope = _scope.push();
+ paramscope.stc = 0;
+ paramscope.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=14169
+ // template parameters should be public
+ tempinst.declareParameters(paramscope);
+ paramscope.pop();
+
+ // Add members of template instance to template instance symbol table
+ //parent = scope.scopesym;
+ tempinst.symtab = new DsymbolTable();
+
+ tempinst.members.foreachDsymbol( (s)
+ {
+ static if (LOG)
+ {
+ printf("\t adding member '%s' %p kind %s to '%s'\n", s.toChars(), s, s.kind(), tempinst.toChars());
+ }
+ s.addMember(_scope, tempinst);
+ });
+
+ static if (LOG)
+ {
+ printf("adding members done\n");
+ }
+
+ /* See if there is only one member of template instance, and that
+ * member has the same name as the template instance.
+ * If so, this template instance becomes an alias for that member.
+ */
+ //printf("members.dim = %d\n", tempinst.members.dim);
+ if (tempinst.members.dim)
+ {
+ Dsymbol s;
+ if (Dsymbol.oneMembers(tempinst.members, &s, tempdecl.ident) && s)
+ {
+ //printf("tempdecl.ident = %s, s = '%s'\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
+ //printf("setting aliasdecl\n");
+ tempinst.aliasdecl = s;
+ }
+ }
+
+ /* If function template declaration
+ */
+ if (fargs && tempinst.aliasdecl)
+ {
+ if (auto fd = tempinst.aliasdecl.isFuncDeclaration())
+ {
+ /* Transmit fargs to type so that TypeFunction.dsymbolSemantic() can
+ * resolve any "auto ref" storage classes.
+ */
+ if (fd.type)
+ if (auto tf = fd.type.isTypeFunction())
+ tf.fargs = fargs;
+ }
+ }
+
+ // Do semantic() analysis on template instance members
+ static if (LOG)
+ {
+ printf("\tdo semantic() on template instance members '%s'\n", tempinst.toChars());
+ }
+ Scope* sc2;
+ sc2 = _scope.push(tempinst);
+ //printf("enclosing = %d, sc.parent = %s\n", tempinst.enclosing, sc.parent.toChars());
+ sc2.parent = tempinst;
+ sc2.tinst = tempinst;
+ sc2.minst = tempinst.minst;
+ sc2.stc &= ~STC.deprecated_;
+ tempinst.tryExpandMembers(sc2);
+
+ tempinst.semanticRun = PASS.semanticdone;
+
+ /* ConditionalDeclaration may introduce eponymous declaration,
+ * so we should find it once again after semantic.
+ */
+ if (tempinst.members.dim)
+ {
+ Dsymbol s;
+ if (Dsymbol.oneMembers(tempinst.members, &s, tempdecl.ident) && s)
+ {
+ if (!tempinst.aliasdecl || tempinst.aliasdecl != s)
+ {
+ //printf("tempdecl.ident = %s, s = '%s'\n", tempdecl.ident.toChars(), s.kind(), s.toPrettyChars());
+ //printf("setting aliasdecl 2\n");
+ tempinst.aliasdecl = s;
+ }
+ }
+ }
+
+ if (global.errors != errorsave)
+ goto Laftersemantic;
+
+ /* If any of the instantiation members didn't get semantic() run
+ * on them due to forward references, we cannot run semantic2()
+ * or semantic3() yet.
+ */
+ {
+ bool found_deferred_ad = false;
+ for (size_t i = 0; i < Module.deferred.dim; i++)
+ {
+ Dsymbol sd = Module.deferred[i];
+ AggregateDeclaration ad = sd.isAggregateDeclaration();
+ if (ad && ad.parent && ad.parent.isTemplateInstance())
+ {
+ //printf("deferred template aggregate: %s %s\n",
+ // sd.parent.toChars(), sd.toChars());
+ found_deferred_ad = true;
+ if (ad.parent == tempinst)
+ {
+ ad.deferred = tempinst;
+ break;
+ }
+ }
+ }
+ if (found_deferred_ad || Module.deferred.dim)
+ goto Laftersemantic;
+ }
+
+ /* The problem is when to parse the initializer for a variable.
+ * Perhaps VarDeclaration.dsymbolSemantic() should do it like it does
+ * for initializers inside a function.
+ */
+ //if (sc.parent.isFuncDeclaration())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=782
+ * this has problems if the classes this depends on
+ * are forward referenced. Find a way to defer semantic()
+ * on this template.
+ */
+ tempinst.semantic2(sc2);
+ }
+ if (global.errors != errorsave)
+ goto Laftersemantic;
+
+ if ((sc.func || (sc.flags & SCOPE.fullinst)) && !tempinst.tinst)
+ {
+ /* If a template is instantiated inside function, the whole instantiation
+ * should be done at that position. But, immediate running semantic3 of
+ * dependent templates may cause unresolved forward reference.
+ * https://issues.dlang.org/show_bug.cgi?id=9050
+ * To avoid the issue, don't run semantic3 until semantic and semantic2 done.
+ */
+ TemplateInstances deferred;
+ tempinst.deferred = &deferred;
+
+ //printf("Run semantic3 on %s\n", toChars());
+ tempinst.trySemantic3(sc2);
+
+ for (size_t i = 0; i < deferred.dim; i++)
+ {
+ //printf("+ run deferred semantic3 on %s\n", deferred[i].toChars());
+ deferred[i].semantic3(null);
+ }
+
+ tempinst.deferred = null;
+ }
+ else if (tempinst.tinst)
+ {
+ bool doSemantic3 = false;
+ FuncDeclaration fd;
+ if (tempinst.aliasdecl)
+ fd = tempinst.aliasdecl.toAlias2().isFuncDeclaration();
+
+ if (fd)
+ {
+ /* Template function instantiation should run semantic3 immediately
+ * for attribute inference.
+ */
+ scope fld = fd.isFuncLiteralDeclaration();
+ if (fld && fld.tok == TOK.reserved)
+ doSemantic3 = true;
+ else if (sc.func)
+ doSemantic3 = true;
+ }
+ else if (sc.func)
+ {
+ /* A lambda function in template arguments might capture the
+ * instantiated scope context. For the correct context inference,
+ * all instantiated functions should run the semantic3 immediately.
+ * See also compilable/test14973.d
+ */
+ foreach (oarg; tempinst.tdtypes)
+ {
+ auto s = getDsymbol(oarg);
+ if (!s)
+ continue;
+
+ if (auto td = s.isTemplateDeclaration())
+ {
+ if (!td.literal)
+ continue;
+ assert(td.members && td.members.dim == 1);
+ s = (*td.members)[0];
+ }
+ if (auto fld = s.isFuncLiteralDeclaration())
+ {
+ if (fld.tok == TOK.reserved)
+ {
+ doSemantic3 = true;
+ break;
+ }
+ }
+ }
+ //printf("[%s] %s doSemantic3 = %d\n", tempinst.tinst.loc.toChars(), tempinst.tinst.toChars(), doSemantic3);
+ }
+ if (doSemantic3)
+ tempinst.trySemantic3(sc2);
+
+ TemplateInstance ti = tempinst.tinst;
+ int nest = 0;
+ while (ti && !ti.deferred && ti.tinst)
+ {
+ ti = ti.tinst;
+ if (++nest > global.recursionLimit)
+ {
+ global.gag = 0; // ensure error message gets printed
+ tempinst.error("recursive expansion");
+ fatal();
+ }
+ }
+ if (ti && ti.deferred)
+ {
+ //printf("deferred semantic3 of %p %s, ti = %s, ti.deferred = %p\n", this, toChars(), ti.toChars());
+ for (size_t i = 0;; i++)
+ {
+ if (i == ti.deferred.dim)
+ {
+ ti.deferred.push(tempinst);
+ break;
+ }
+ if ((*ti.deferred)[i] == tempinst)
+ break;
+ }
+ }
+ }
+
+ if (tempinst.aliasdecl)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=13816
+ * AliasDeclaration tries to resolve forward reference
+ * twice (See inuse check in AliasDeclaration.toAlias()). It's
+ * necessary to resolve mutual references of instantiated symbols, but
+ * it will left a true recursive alias in tuple declaration - an
+ * AliasDeclaration A refers TupleDeclaration B, and B contains A
+ * in its elements. To correctly make it an error, we strictly need to
+ * resolve the alias of eponymous member.
+ */
+ tempinst.aliasdecl = tempinst.aliasdecl.toAlias2();
+ }
+
+Laftersemantic:
+ sc2.pop();
+ _scope.pop();
+
+ // Give additional context info if error occurred during instantiation
+ if (global.errors != errorsave)
+ {
+ if (!tempinst.errors)
+ {
+ if (!tempdecl.literal)
+ tempinst.error(tempinst.loc, "error instantiating");
+ if (tempinst.tinst)
+ tempinst.tinst.printInstantiationTrace();
+ }
+ tempinst.errors = true;
+ if (tempinst.gagged)
+ {
+ // Errors are gagged, so remove the template instance from the
+ // instance/symbol lists we added it to and reset our state to
+ // finish clean and so we can try to instantiate it again later
+ // (see https://issues.dlang.org/show_bug.cgi?id=4302 and https://issues.dlang.org/show_bug.cgi?id=6602).
+ tempdecl.removeInstance(tempdecl_instance_idx);
+ if (target_symbol_list)
+ {
+ // Because we added 'this' in the last position above, we
+ // should be able to remove it without messing other indices up.
+ assert((*target_symbol_list)[target_symbol_list_idx] == tempinst);
+ target_symbol_list.remove(target_symbol_list_idx);
+ tempinst.memberOf = null; // no longer a member
+ }
+ tempinst.semanticRun = PASS.init;
+ tempinst.inst = null;
+ tempinst.symtab = null;
+ }
+ }
+ else if (errinst)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=14541
+ * If the previous gagged instance had failed by
+ * circular references, currrent "error reproduction instantiation"
+ * might succeed, because of the difference of instantiated context.
+ * On such case, the cached error instance needs to be overridden by the
+ * succeeded instance.
+ */
+ //printf("replaceInstance()\n");
+ assert(errinst.errors);
+ auto ti1 = TemplateInstanceBox(errinst);
+ tempdecl.instances.remove(ti1);
+
+ auto ti2 = TemplateInstanceBox(tempinst);
+ tempdecl.instances[ti2] = tempinst;
+ }
+
+ static if (LOG)
+ {
+ printf("-TemplateInstance.dsymbolSemantic('%s', this=%p)\n", tempinst.toChars(), tempinst);
+ }
+}
+
+/******************************************************
+ * Do template instance semantic for isAliasSeq templates.
+ * This is a greatly simplified version of templateInstanceSemantic().
+ */
+private
+void aliasSeqInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclaration tempdecl)
+{
+ //printf("[%s] aliasSeqInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars());
+ Scope* paramscope = sc.push();
+ paramscope.stc = 0;
+ paramscope.visibility = Visibility(Visibility.Kind.public_);
+
+ TemplateTupleParameter ttp = (*tempdecl.parameters)[0].isTemplateTupleParameter();
+ Tuple va = tempinst.tdtypes[0].isTuple();
+ Declaration d = new TupleDeclaration(tempinst.loc, ttp.ident, &va.objects);
+ d.storage_class |= STC.templateparameter;
+ d.dsymbolSemantic(sc);
+
+ paramscope.pop();
+
+ tempinst.aliasdecl = d;
+
+ tempinst.semanticRun = PASS.semanticdone;
+}
+
+/******************************************************
+ * Do template instance semantic for isAlias templates.
+ * This is a greatly simplified version of templateInstanceSemantic().
+ */
+private
+void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclaration tempdecl)
+{
+ //printf("[%s] aliasInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars());
+ Scope* paramscope = sc.push();
+ paramscope.stc = 0;
+ paramscope.visibility = Visibility(Visibility.Kind.public_);
+
+ TemplateTypeParameter ttp = (*tempdecl.parameters)[0].isTemplateTypeParameter();
+ Type ta = tempinst.tdtypes[0].isType();
+ auto ad = tempdecl.onemember.isAliasDeclaration();
+
+ // Note: qualifiers can be in both 'ad.type.mod' and 'ad.storage_class'
+ Declaration d = new AliasDeclaration(tempinst.loc, ttp.ident, ta.addMod(ad.type.mod));
+ d.storage_class |= STC.templateparameter | ad.storage_class;
+ d.dsymbolSemantic(sc);
+
+ paramscope.pop();
+
+ tempinst.aliasdecl = d;
+
+ tempinst.semanticRun = PASS.semanticdone;
+}
+
+// function used to perform semantic on AliasDeclaration
+void aliasSemantic(AliasDeclaration ds, Scope* sc)
+{
+ //printf("AliasDeclaration::semantic() %s\n", ds.toChars());
+
+ // as DsymbolSemanticVisitor::visit(AliasDeclaration), in case we're called first.
+ // see https://issues.dlang.org/show_bug.cgi?id=21001
+ ds.storage_class |= sc.stc & STC.deprecated_;
+ ds.visibility = sc.visibility;
+ ds.userAttribDecl = sc.userAttribDecl;
+
+ // TypeTraits needs to know if it's located in an AliasDeclaration
+ const oldflags = sc.flags;
+ sc.flags |= SCOPE.alias_;
+
+ void normalRet()
+ {
+ sc.flags = oldflags;
+ ds.inuse = 0;
+ ds.semanticRun = PASS.semanticdone;
+
+ if (auto sx = ds.overnext)
+ {
+ ds.overnext = null;
+ if (!ds.overloadInsert(sx))
+ ScopeDsymbol.multiplyDefined(Loc.initial, sx, ds);
+ }
+ }
+
+ void errorRet()
+ {
+ ds.aliassym = null;
+ ds.type = Type.terror;
+ ds.inuse = 0;
+ normalRet();
+ }
+
+ // preserve the original type
+ if (!ds.originalType && ds.type)
+ ds.originalType = ds.type.syntaxCopy();
+
+ if (ds.aliassym)
+ {
+ auto fd = ds.aliassym.isFuncLiteralDeclaration();
+ auto td = ds.aliassym.isTemplateDeclaration();
+ if (fd || td && td.literal)
+ {
+ if (fd && fd.semanticRun >= PASS.semanticdone)
+ return normalRet();
+
+ Expression e = new FuncExp(ds.loc, ds.aliassym);
+ e = e.expressionSemantic(sc);
+ if (auto fe = e.isFuncExp())
+ {
+ ds.aliassym = fe.td ? cast(Dsymbol)fe.td : fe.fd;
+ return normalRet();
+ }
+ else
+ return errorRet();
+ }
+
+ if (ds.aliassym.isTemplateInstance())
+ ds.aliassym.dsymbolSemantic(sc);
+ return normalRet();
+ }
+ ds.inuse = 1;
+
+ // Given:
+ // alias foo.bar.abc def;
+ // it is not knowable from the syntax whether `def` is an alias
+ // for type `foo.bar.abc` or an alias for symbol `foo.bar.abc`. It is up to the semantic()
+ // pass to distinguish.
+ // If it is a type, then `.type` is set and getType() will return that
+ // type. If it is a symbol, then `.aliassym` is set and type is `null` -
+ // toAlias() will return `.aliassym`
+
+ const errors = global.errors;
+ Type oldtype = ds.type;
+
+ // Ungag errors when not instantiated DeclDefs scope alias
+ auto ungag = Ungag(global.gag);
+ //printf("%s parent = %s, gag = %d, instantiated = %d\n", toChars(), parent, global.gag, isInstantiated());
+ if (ds.parent && global.gag && !ds.isInstantiated() && !ds.toParent2().isFuncDeclaration())
+ {
+ //printf("%s type = %s\n", toPrettyChars(), type.toChars());
+ global.gag = 0;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=18480
+ // Detect `alias sym = sym;` to prevent creating loops in overload overnext lists.
+ if (auto tident = ds.type.isTypeIdentifier())
+ {
+ // Selective imports are allowed to alias to the same name `import mod : sym=sym`.
+ if (!ds._import)
+ {
+ if (tident.ident is ds.ident && !tident.idents.dim)
+ {
+ error(ds.loc, "`alias %s = %s;` cannot alias itself, use a qualified name to create an overload set",
+ ds.ident.toChars(), tident.ident.toChars());
+ ds.type = Type.terror;
+ }
+ }
+ }
+ /* This section is needed because Type.resolve() will:
+ * const x = 3;
+ * alias y = x;
+ * try to convert identifier x to 3.
+ */
+ auto s = ds.type.toDsymbol(sc);
+ if (errors != global.errors)
+ return errorRet();
+ if (s == ds)
+ {
+ ds.error("cannot resolve");
+ return errorRet();
+ }
+ if (!s || !s.isEnumMember())
+ {
+ Type t;
+ Expression e;
+ Scope* sc2 = sc;
+ if (ds.storage_class & (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.disable))
+ {
+ // For 'ref' to be attached to function types, and picked
+ // up by Type.resolve(), it has to go into sc.
+ sc2 = sc.push();
+ sc2.stc |= ds.storage_class & (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable);
+ }
+ ds.type = ds.type.addSTC(ds.storage_class);
+ ds.type.resolve(ds.loc, sc2, e, t, s);
+ if (sc2 != sc)
+ sc2.pop();
+
+ if (e) // Try to convert Expression to Dsymbol
+ {
+ // TupleExp is naturally converted to a TupleDeclaration
+ if (auto te = e.isTupleExp())
+ s = new TupleDeclaration(te.loc, ds.ident, cast(Objects*)te.exps);
+ else
+ {
+ s = getDsymbol(e);
+ if (!s)
+ {
+ if (e.op != TOK.error)
+ ds.error("cannot alias an expression `%s`", e.toChars());
+ return errorRet();
+ }
+ }
+ }
+ ds.type = t;
+ }
+ if (s == ds)
+ {
+ assert(global.errors);
+ return errorRet();
+ }
+ if (s) // it's a symbolic alias
+ {
+ //printf("alias %s resolved to %s %s\n", toChars(), s.kind(), s.toChars());
+ ds.type = null;
+ ds.aliassym = s;
+ }
+ else // it's a type alias
+ {
+ //printf("alias %s resolved to type %s\n", toChars(), type.toChars());
+ ds.type = ds.type.typeSemantic(ds.loc, sc);
+ ds.aliassym = null;
+ }
+
+ if (global.gag && errors != global.errors)
+ return errorRet();
+
+ normalRet();
+}
+
+/********************
+ * Perform semantic on AliasAssignment.
+ * Has a lot of similarities to aliasSemantic(). Perhaps they should share code.
+ */
+private void aliasAssignSemantic(AliasAssign ds, Scope* sc)
+{
+ //printf("AliasAssign::semantic() %p, %s\n", ds, ds.ident.toChars());
+
+ void errorRet()
+ {
+ ds.errors = true;
+ ds.type = Type.terror;
+ ds.semanticRun = PASS.semanticdone;
+ return;
+ }
+
+ /* Find the AliasDeclaration corresponding to ds.
+ * Returns: AliasDeclaration if found, null if error
+ */
+ AliasDeclaration findAliasDeclaration(AliasAssign ds, Scope* sc)
+ {
+ Dsymbol scopesym;
+ Dsymbol as = sc.search(ds.loc, ds.ident, &scopesym);
+ if (!as)
+ {
+ ds.error("undefined identifier `%s`", ds.ident.toChars());
+ return null;
+ }
+ if (as.errors)
+ return null;
+
+ auto ad = as.isAliasDeclaration();
+ if (!ad)
+ {
+ ds.error("identifier `%s` must be an alias declaration", as.toChars());
+ return null;
+ }
+
+ if (ad.overnext)
+ {
+ ds.error("cannot reassign overloaded alias");
+ return null;
+ }
+
+ // Check constraints on the parent
+ auto adParent = ad.toParent();
+ if (adParent != ds.toParent())
+ {
+ if (!adParent)
+ adParent = ds.toParent();
+ error(ds.loc, "`%s` must have same parent `%s` as alias `%s`", ds.ident.toChars(), adParent.toChars(), ad.toChars());
+ return null;
+ }
+ if (!adParent.isTemplateInstance())
+ {
+ ds.error("must be a member of a template");
+ return null;
+ }
+
+ return ad;
+ }
+
+ auto aliassym = findAliasDeclaration(ds, sc);
+ if (!aliassym)
+ return errorRet();
+
+ if (aliassym.adFlags & Declaration.wasRead)
+ {
+ if (!aliassym.errors)
+ error(ds.loc, "%s was read, so cannot reassign", aliassym.toChars());
+ aliassym.errors = true;
+ return errorRet();
+ }
+
+ aliassym.adFlags |= Declaration.ignoreRead; // temporarilly allow reads of aliassym
+
+ const storage_class = sc.stc & (STC.deprecated_ | STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable);
+
+ if (ds.aliassym)
+ {
+ auto fd = ds.aliassym.isFuncLiteralDeclaration();
+ auto td = ds.aliassym.isTemplateDeclaration();
+ if (fd && fd.semanticRun >= PASS.semanticdone)
+ {
+ }
+ else if (fd || td && td.literal)
+ {
+
+ Expression e = new FuncExp(ds.loc, ds.aliassym);
+ e = e.expressionSemantic(sc);
+ auto fe = e.isFuncExp();
+ if (!fe)
+ return errorRet();
+ ds.aliassym = fe.td ? cast(Dsymbol)fe.td : fe.fd;
+ }
+ else if (ds.aliassym.isTemplateInstance())
+ ds.aliassym.dsymbolSemantic(sc);
+
+ aliassym.type = null;
+ aliassym.aliassym = ds.aliassym;
+ return;
+ }
+
+ /* Given:
+ * abc = def;
+ * it is not knownable from the syntax whether `def` is a type or a symbol.
+ * It appears here as `ds.type`. Do semantic analysis on `def` to disambiguate.
+ */
+
+ const errors = global.errors;
+
+ /* This section is needed because Type.resolve() will:
+ * const x = 3;
+ * alias y = x;
+ * try to convert identifier x to 3.
+ */
+ auto s = ds.type.toDsymbol(sc);
+ if (errors != global.errors)
+ return errorRet();
+ if (s == aliassym)
+ {
+ ds.error("cannot resolve");
+ return errorRet();
+ }
+
+ if (!s || !s.isEnumMember())
+ {
+ Type t;
+ Expression e;
+ Scope* sc2 = sc;
+ if (storage_class & (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable))
+ {
+ // For 'ref' to be attached to function types, and picked
+ // up by Type.resolve(), it has to go into sc.
+ sc2 = sc.push();
+ sc2.stc |= storage_class & (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable);
+ }
+ ds.type = ds.type.addSTC(storage_class);
+ ds.type.resolve(ds.loc, sc2, e, t, s);
+ if (sc2 != sc)
+ sc2.pop();
+
+ if (e) // Try to convert Expression to Dsymbol
+ {
+ // TupleExp is naturally converted to a TupleDeclaration
+ if (auto te = e.isTupleExp())
+ s = new TupleDeclaration(te.loc, ds.ident, cast(Objects*)te.exps);
+ else
+ {
+ s = getDsymbol(e);
+ if (!s)
+ {
+ if (e.op != TOK.error)
+ ds.error("cannot alias an expression `%s`", e.toChars());
+ return errorRet();
+ }
+ }
+ }
+ ds.type = t;
+ }
+ if (s == aliassym)
+ {
+ assert(global.errors);
+ return errorRet();
+ }
+
+ if (s) // it's a symbolic alias
+ {
+ //printf("alias %s resolved to %s %s\n", toChars(), s.kind(), s.toChars());
+ aliassym.type = null;
+ aliassym.aliassym = s;
+ aliassym.storage_class |= sc.stc & STC.deprecated_;
+ aliassym.visibility = sc.visibility;
+ aliassym.userAttribDecl = sc.userAttribDecl;
+ }
+ else // it's a type alias
+ {
+ //printf("alias %s resolved to type %s\n", toChars(), type.toChars());
+ aliassym.type = ds.type.typeSemantic(ds.loc, sc);
+ aliassym.aliassym = null;
+ }
+
+
+ aliassym.adFlags &= ~Declaration.ignoreRead;
+
+ if (aliassym.type && aliassym.type.ty == Terror ||
+ global.gag && errors != global.errors)
+ {
+ aliassym.type = Type.terror;
+ aliassym.aliassym = null;
+ return errorRet();
+ }
+
+ ds.semanticRun = PASS.semanticdone;
+}
+
+/***************************************
+ * Find all instance fields in `ad`, then push them into `fields`.
+ *
+ * Runs semantic() for all instance field variables, but also
+ * the field types can remain yet not resolved forward references,
+ * except direct recursive definitions.
+ * After the process sizeok is set to Sizeok.fwd.
+ *
+ * Params:
+ * ad = the AggregateDeclaration to examine
+ * Returns:
+ * false if any errors occur.
+ */
+bool determineFields(AggregateDeclaration ad)
+{
+ if (ad._scope)
+ dsymbolSemantic(ad, null);
+ if (ad.sizeok != Sizeok.none)
+ return true;
+
+ //printf("determineFields() %s, fields.dim = %d\n", toChars(), fields.dim);
+ // determineFields can be called recursively from one of the fields's v.semantic
+ ad.fields.setDim(0);
+
+ static int func(Dsymbol s, AggregateDeclaration ad)
+ {
+ auto v = s.isVarDeclaration();
+ if (!v)
+ return 0;
+ if (v.storage_class & STC.manifest)
+ return 0;
+
+ if (v.semanticRun < PASS.semanticdone)
+ v.dsymbolSemantic(null);
+ // Return in case a recursive determineFields triggered by v.semantic already finished
+ if (ad.sizeok != Sizeok.none)
+ return 1;
+
+ if (v.aliassym)
+ return 0; // If this variable was really a tuple, skip it.
+
+ if (v.storage_class & (STC.static_ | STC.extern_ | STC.tls | STC.gshared | STC.manifest | STC.ctfe | STC.templateparameter))
+ return 0;
+ if (!v.isField() || v.semanticRun < PASS.semanticdone)
+ return 1; // unresolvable forward reference
+
+ ad.fields.push(v);
+
+ if (v.storage_class & STC.ref_)
+ return 0;
+ auto tv = v.type.baseElemOf();
+ if (auto tvs = tv.isTypeStruct())
+ {
+ if (ad == tvs.sym)
+ {
+ const(char)* psz = (v.type.toBasetype().ty == Tsarray) ? "static array of " : "";
+ ad.error("cannot have field `%s` with %ssame struct type", v.toChars(), psz);
+ ad.type = Type.terror;
+ ad.errors = true;
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ if (ad.members)
+ {
+ for (size_t i = 0; i < ad.members.dim; i++)
+ {
+ auto s = (*ad.members)[i];
+ if (s.apply(&func, ad))
+ {
+ if (ad.sizeok != Sizeok.none)
+ {
+ // recursive determineFields already finished
+ return true;
+ }
+ return false;
+ }
+ }
+ }
+
+ if (ad.sizeok != Sizeok.done)
+ ad.sizeok = Sizeok.fwd;
+
+ return true;
+}
diff --git a/gcc/d/dmd/dtemplate.c b/gcc/d/dmd/dtemplate.c
deleted file mode 100644
index 20036f2dbff..00000000000
--- a/gcc/d/dmd/dtemplate.c
+++ /dev/null
@@ -1,7581 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/template.c
- */
-
-// Handle template implementation
-
-#include "root/dsystem.h"
-#include "root/root.h"
-#include "root/aav.h"
-#include "root/rmem.h"
-#include "root/stringtable.h"
-#include "root/hash.h"
-
-#include "mangle.h"
-#include "mtype.h"
-#include "template.h"
-#include "init.h"
-#include "expression.h"
-#include "scope.h"
-#include "module.h"
-#include "aggregate.h"
-#include "declaration.h"
-#include "dsymbol.h"
-#include "mars.h"
-#include "dsymbol.h"
-#include "identifier.h"
-#include "hdrgen.h"
-#include "id.h"
-#include "attrib.h"
-#include "cond.h"
-#include "tokens.h"
-
-#define IDX_NOTFOUND (0x12345678) // index is not found
-
-Type *rawTypeMerge(Type *t1, Type *t2);
-bool MODimplicitConv(MOD modfrom, MOD modto);
-MATCH MODmethodConv(MOD modfrom, MOD modto);
-MOD MODmerge(MOD mod1, MOD mod2);
-
-static size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters);
-static int arrayObjectMatch(Objects *oa1, Objects *oa2);
-static unsigned char deduceWildHelper(Type *t, Type **at, Type *tparam);
-static MATCH deduceTypeHelper(Type *t, Type **at, Type *tparam);
-bool reliesOnTident(Type *t, TemplateParameters *tparams = NULL, size_t iStart = 0);
-bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors);
-
-/********************************************
- * These functions substitute for dynamic_cast. dynamic_cast does not work
- * on earlier versions of gcc.
- */
-
-Expression *isExpression(RootObject *o)
-{
- //return dynamic_cast<Expression *>(o);
- if (!o || o->dyncast() != DYNCAST_EXPRESSION)
- return NULL;
- return (Expression *)o;
-}
-
-Dsymbol *isDsymbol(RootObject *o)
-{
- //return dynamic_cast<Dsymbol *>(o);
- if (!o || o->dyncast() != DYNCAST_DSYMBOL)
- return NULL;
- return (Dsymbol *)o;
-}
-
-Type *isType(RootObject *o)
-{
- //return dynamic_cast<Type *>(o);
- if (!o || o->dyncast() != DYNCAST_TYPE)
- return NULL;
- return (Type *)o;
-}
-
-Tuple *isTuple(RootObject *o)
-{
- //return dynamic_cast<Tuple *>(o);
- if (!o || o->dyncast() != DYNCAST_TUPLE)
- return NULL;
- return (Tuple *)o;
-}
-
-Parameter *isParameter(RootObject *o)
-{
- //return dynamic_cast<Parameter *>(o);
- if (!o || o->dyncast() != DYNCAST_PARAMETER)
- return NULL;
- return (Parameter *)o;
-}
-
-/**************************************
- * Is this Object an error?
- */
-bool isError(RootObject *o)
-{
- Type *t = isType(o);
- if (t)
- return (t->ty == Terror);
- Expression *e = isExpression(o);
- if (e)
- return (e->op == TOKerror || !e->type || e->type->ty == Terror);
- Tuple *v = isTuple(o);
- if (v)
- return arrayObjectIsError(&v->objects);
- Dsymbol *s = isDsymbol(o);
- assert(s);
- if (s->errors)
- return true;
- return s->parent ? isError(s->parent) : false;
-}
-
-/**************************************
- * Are any of the Objects an error?
- */
-bool arrayObjectIsError(Objects *args)
-{
- for (size_t i = 0; i < args->length; i++)
- {
- RootObject *o = (*args)[i];
- if (isError(o))
- return true;
- }
- return false;
-}
-
-/***********************
- * Try to get arg as a type.
- */
-
-Type *getType(RootObject *o)
-{
- Type *t = isType(o);
- if (!t)
- {
- Expression *e = isExpression(o);
- if (e)
- t = e->type;
- }
- return t;
-}
-
-Dsymbol *getDsymbol(RootObject *oarg)
-{
- //printf("getDsymbol()\n");
- //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg));
-
- Dsymbol *sa;
- Expression *ea = isExpression(oarg);
- if (ea)
- {
- // Try to convert Expression to symbol
- if (VarExp *ve = ea->isVarExp())
- sa = ve->var;
- else if (FuncExp *fe = ea->isFuncExp())
- sa = fe->td ? (Dsymbol *)fe->td : (Dsymbol *)fe->fd;
- else if (TemplateExp *te = ea->isTemplateExp())
- sa = te->td;
- else if (ScopeExp *se = ea->isScopeExp())
- sa = se->sds;
- else
- sa = NULL;
- }
- else
- {
- // Try to convert Type to symbol
- Type *ta = isType(oarg);
- if (ta)
- sa = ta->toDsymbol(NULL);
- else
- sa = isDsymbol(oarg); // if already a symbol
- }
- return sa;
-}
-
-/***********************
- * Try to get value from manifest constant
- */
-
-static Expression *getValue(Expression *e)
-{
- if (e && e->op == TOKvar)
- {
- VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration();
- if (v && v->storage_class & STCmanifest)
- {
- e = v->getConstInitializer();
- }
- }
- return e;
-}
-
-static Expression *getValue(Dsymbol *&s)
-{
- Expression *e = NULL;
- if (s)
- {
- VarDeclaration *v = s->isVarDeclaration();
- if (v && v->storage_class & STCmanifest)
- {
- e = v->getConstInitializer();
- }
- }
- return e;
-}
-
-/**********************************
- * Return true if e could be valid only as a template value parameter.
- * Return false if it might be an alias or tuple.
- * (Note that even in this case, it could still turn out to be a value).
- */
-bool definitelyValueParameter(Expression *e)
-{
- // None of these can be value parameters
- if (e->op == TOKtuple || e->op == TOKscope ||
- e->op == TOKtype || e->op == TOKdottype ||
- e->op == TOKtemplate || e->op == TOKdottd ||
- e->op == TOKfunction || e->op == TOKerror ||
- e->op == TOKthis || e->op == TOKsuper ||
- e->op == TOKdot)
- return false;
-
- if (e->op != TOKdotvar)
- return true;
-
- /* Template instantiations involving a DotVar expression are difficult.
- * In most cases, they should be treated as a value parameter, and interpreted.
- * But they might also just be a fully qualified name, which should be treated
- * as an alias.
- */
-
- // x.y.f cannot be a value
- FuncDeclaration *f = ((DotVarExp *)e)->var->isFuncDeclaration();
- if (f)
- return false;
-
- while (e->op == TOKdotvar)
- {
- e = ((DotVarExp *)e)->e1;
- }
- // this.x.y and super.x.y couldn't possibly be valid values.
- if (e->op == TOKthis || e->op == TOKsuper)
- return false;
-
- // e.type.x could be an alias
- if (e->op == TOKdottype)
- return false;
-
- // var.x.y is the only other possible form of alias
- if (e->op != TOKvar)
- return true;
-
- VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration();
-
- // func.x.y is not an alias
- if (!v)
- return true;
-
- // TODO: Should we force CTFE if it is a global constant?
-
- return false;
-}
-
-static Expression *getExpression(RootObject *o)
-{
- Dsymbol *s = isDsymbol(o);
- return s ? getValue(s) : getValue(isExpression(o));
-}
-
-/******************************
- * If o1 matches o2, return true.
- * Else, return false.
- */
-
-static bool match(RootObject *o1, RootObject *o2)
-{
- //printf("match() o1 = %p %s (%d), o2 = %p %s (%d)\n",
- // o1, o1->toChars(), o1->dyncast(), o2, o2->toChars(), o2->dyncast());
-
- /* A proper implementation of the various equals() overrides
- * should make it possible to just do o1->equals(o2), but
- * we'll do that another day.
- */
-
- /* Manifest constants should be compared by their values,
- * at least in template arguments.
- */
-
- if (Type *t1 = isType(o1))
- {
- Type *t2 = isType(o2);
- if (!t2)
- goto Lnomatch;
-
- //printf("\tt1 = %s\n", t1->toChars());
- //printf("\tt2 = %s\n", t2->toChars());
- if (!t1->equals(t2))
- goto Lnomatch;
-
- goto Lmatch;
- }
- if (Expression *e1 = getExpression(o1))
- {
- Expression *e2 = getExpression(o2);
- if (!e2)
- goto Lnomatch;
-
- //printf("\te1 = %s %s %s\n", e1->type->toChars(), Token::toChars(e1->op), e1->toChars());
- //printf("\te2 = %s %s %s\n", e2->type->toChars(), Token::toChars(e2->op), e2->toChars());
-
- // two expressions can be equal although they do not have the same
- // type; that happens when they have the same value. So check type
- // as well as expression equality to ensure templates are properly
- // matched.
- if (!e1->type->equals(e2->type) || !e1->equals(e2))
- goto Lnomatch;
-
- goto Lmatch;
- }
- if (Dsymbol *s1 = isDsymbol(o1))
- {
- Dsymbol *s2 = isDsymbol(o2);
- if (!s2)
- goto Lnomatch;
-
- //printf("\ts1 = %s\n", s1->toChars());
- //printf("\ts2 = %s\n", s2->toChars());
- if (!s1->equals(s2))
- goto Lnomatch;
- if (s1->parent != s2->parent && !s1->isFuncDeclaration() && !s2->isFuncDeclaration())
- goto Lnomatch;
-
- goto Lmatch;
- }
- if (Tuple *u1 = isTuple(o1))
- {
- Tuple *u2 = isTuple(o2);
- if (!u2)
- goto Lnomatch;
-
- //printf("\tu1 = %s\n", u1->toChars());
- //printf("\tu2 = %s\n", u2->toChars());
- if (!arrayObjectMatch(&u1->objects, &u2->objects))
- goto Lnomatch;
-
- goto Lmatch;
- }
-Lmatch:
- //printf("\t-> match\n");
- return true;
-
-Lnomatch:
- //printf("\t-> nomatch\n");
- return false;
-}
-
-
-/************************************
- * Match an array of them.
- */
-int arrayObjectMatch(Objects *oa1, Objects *oa2)
-{
- if (oa1 == oa2)
- return 1;
- if (oa1->length != oa2->length)
- return 0;
- for (size_t j = 0; j < oa1->length; j++)
- {
- RootObject *o1 = (*oa1)[j];
- RootObject *o2 = (*oa2)[j];
- if (!match(o1, o2))
- {
- return 0;
- }
- }
- return 1;
-}
-
-
-/************************************
- * Computes hash of expression.
- * Handles all Expression classes and MUST match their equals method,
- * i.e. e1->equals(e2) implies expressionHash(e1) == expressionHash(e2).
- */
-static hash_t expressionHash(Expression *e)
-{
- switch (e->op)
- {
- case TOKint64:
- return (size_t) ((IntegerExp *)e)->getInteger();
-
- case TOKfloat64:
- return CTFloat::hash(((RealExp *)e)->value);
-
- case TOKcomplex80:
- {
- ComplexExp *ce = (ComplexExp *)e;
- return mixHash(CTFloat::hash(ce->toReal()), CTFloat::hash(ce->toImaginary()));
- }
-
- case TOKidentifier:
- return (size_t)(void *) ((IdentifierExp *)e)->ident;
-
- case TOKnull:
- return (size_t)(void *) ((NullExp *)e)->type;
-
- case TOKstring:
- {
- StringExp *se = (StringExp *)e;
- return calcHash((const char *)se->string, se->len * se->sz);
- }
-
- case TOKtuple:
- {
- TupleExp *te = (TupleExp *)e;
- size_t hash = 0;
- hash += te->e0 ? expressionHash(te->e0) : 0;
- for (size_t i = 0; i < te->exps->length; i++)
- {
- Expression *elem = (*te->exps)[i];
- hash = mixHash(hash, expressionHash(elem));
- }
- return hash;
- }
-
- case TOKarrayliteral:
- {
- ArrayLiteralExp *ae = (ArrayLiteralExp *)e;
- size_t hash = 0;
- for (size_t i = 0; i < ae->elements->length; i++)
- hash = mixHash(hash, expressionHash(ae->getElement(i)));
- return hash;
- }
-
- case TOKassocarrayliteral:
- {
- AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e;
- size_t hash = 0;
- for (size_t i = 0; i < ae->keys->length; i++)
- // reduction needs associative op as keys are unsorted (use XOR)
- hash ^= mixHash(expressionHash((*ae->keys)[i]), expressionHash((*ae->values)[i]));
- return hash;
- }
-
- case TOKstructliteral:
- {
- StructLiteralExp *se = (StructLiteralExp *)e;
- size_t hash = 0;
- for (size_t i = 0; i < se->elements->length; i++)
- {
- Expression *elem = (*se->elements)[i];
- hash = mixHash(hash, elem ? expressionHash(elem) : 0);
- }
- return hash;
- }
-
- case TOKvar:
- return (size_t)(void *) ((VarExp *)e)->var;
-
- case TOKfunction:
- return (size_t)(void *) ((FuncExp *)e)->fd;
-
- default:
- // no custom equals for this expression
- // equals based on identity
- return (size_t)(void *) e;
- }
-}
-
-
-/************************************
- * Return hash of Objects.
- */
-static hash_t arrayObjectHash(Objects *oa1)
-{
- hash_t hash = 0;
- for (size_t j = 0; j < oa1->length; j++)
- {
- /* Must follow the logic of match()
- */
- RootObject *o1 = (*oa1)[j];
- if (Type *t1 = isType(o1))
- hash = mixHash(hash, (size_t)t1->deco);
- else if (Expression *e1 = getExpression(o1))
- hash = mixHash(hash, expressionHash(e1));
- else if (Dsymbol *s1 = isDsymbol(o1))
- {
- FuncAliasDeclaration *fa1 = s1->isFuncAliasDeclaration();
- if (fa1)
- s1 = fa1->toAliasFunc();
- hash = mixHash(hash, mixHash((size_t)(void *)s1->getIdent(), (size_t)(void *)s1->parent));
- }
- else if (Tuple *u1 = isTuple(o1))
- hash = mixHash(hash, arrayObjectHash(&u1->objects));
- }
- return hash;
-}
-
-RootObject *objectSyntaxCopy(RootObject *o)
-{
- if (!o)
- return NULL;
- if (Type *t = isType(o))
- return t->syntaxCopy();
- if (Expression *e = isExpression(o))
- return e->syntaxCopy();
- return o;
-}
-
-
-/* ======================== TemplateDeclaration ============================= */
-
-TemplateDeclaration::TemplateDeclaration(Loc loc, Identifier *id,
- TemplateParameters *parameters, Expression *constraint, Dsymbols *decldefs, bool ismixin, bool literal)
- : ScopeDsymbol(id)
-{
- this->loc = loc;
- this->parameters = parameters;
- this->origParameters = parameters;
- this->constraint = constraint;
- this->members = decldefs;
- this->overnext = NULL;
- this->overroot = NULL;
- this->funcroot = NULL;
- this->onemember = NULL;
- this->literal = literal;
- this->ismixin = ismixin;
- this->isstatic = true;
- this->isTrivialAliasSeq = false;
- this->isTrivialAlias = false;
- this->previous = NULL;
- this->protection = Prot(Prot::undefined);
- this->inuse = 0;
- this->instances = NULL;
-
- // Compute in advance for Ddoc's use
- // Bugzilla 11153: ident could be NULL if parsing fails.
- if (!members || !ident)
- return;
-
- Dsymbol *s;
- if (!Dsymbol::oneMembers(members, &s, ident) || !s)
- return;
-
- onemember = s;
- s->parent = this;
-
- /* Set isTrivialAliasSeq if this fits the pattern:
- * template AliasSeq(T...) { alias AliasSeq = T; }
- * or set isTrivialAlias if this fits the pattern:
- * template Alias(T) { alias Alias = qualifiers(T); }
- */
- if (!(parameters && parameters->length == 1))
- return;
-
- AliasDeclaration *ad = s->isAliasDeclaration();
- if (!ad || !ad->type)
- return;
-
- TypeIdentifier *ti = ad->type->isTypeIdentifier();
- if (!ti || ti->idents.length != 0)
- return;
-
- if (TemplateTupleParameter *ttp = (*parameters)[0]->isTemplateTupleParameter())
- {
- if (ti->ident == ttp->ident && ti->mod == 0)
- {
- //printf("found isAliasSeq %s %s\n", s->toChars(), ad->type->toChars());
- isTrivialAliasSeq = true;
- }
- }
- else if (TemplateTypeParameter *ttp = (*parameters)[0]->isTemplateTypeParameter())
- {
- if (ti->ident == ttp->ident)
- {
- //printf("found isAlias %s %s\n", s->toChars(), ad->type->toChars());
- isTrivialAlias = true;
- }
- }
-}
-
-Dsymbol *TemplateDeclaration::syntaxCopy(Dsymbol *)
-{
- //printf("TemplateDeclaration::syntaxCopy()\n");
- TemplateParameters *p = NULL;
- if (parameters)
- {
- p = new TemplateParameters();
- p->setDim(parameters->length);
- for (size_t i = 0; i < p->length; i++)
- (*p)[i] = (*parameters)[i]->syntaxCopy();
- }
- return new TemplateDeclaration(loc, ident, p,
- constraint ? constraint->syntaxCopy() : NULL,
- Dsymbol::arraySyntaxCopy(members), ismixin, literal);
-}
-
-const char *TemplateDeclaration::kind() const
-{
- return (onemember && onemember->isAggregateDeclaration())
- ? onemember->kind()
- : "template";
-}
-
-/**********************************
- * Overload existing TemplateDeclaration 'this' with the new one 's'.
- * Return true if successful; i.e. no conflict.
- */
-
-bool TemplateDeclaration::overloadInsert(Dsymbol *s)
-{
- FuncDeclaration *fd = s->isFuncDeclaration();
- if (fd)
- {
- if (funcroot)
- return funcroot->overloadInsert(fd);
- funcroot = fd;
- return funcroot->overloadInsert(this);
- }
-
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (!td)
- return false;
-
- TemplateDeclaration *pthis = this;
- TemplateDeclaration **ptd;
- for (ptd = &pthis; *ptd; ptd = &(*ptd)->overnext)
- {
- }
-
- td->overroot = this;
- *ptd = td;
- return true;
-}
-
-/****************************
- * Check to see if constraint is satisfied.
- */
-bool TemplateDeclaration::evaluateConstraint(
- TemplateInstance *ti, Scope *sc, Scope *paramscope,
- Objects *dedargs, FuncDeclaration *fd)
-{
- /* Detect recursive attempts to instantiate this template declaration,
- * Bugzilla 4072
- * void foo(T)(T x) if (is(typeof(foo(x)))) { }
- * static assert(!is(typeof(foo(7))));
- * Recursive attempts are regarded as a constraint failure.
- */
- /* There's a chicken-and-egg problem here. We don't know yet if this template
- * instantiation will be a local one (enclosing is set), and we won't know until
- * after selecting the correct template. Thus, function we're nesting inside
- * is not on the sc scope chain, and this can cause errors in FuncDeclaration::getLevel().
- * Workaround the problem by setting a flag to relax the checking on frame errors.
- */
-
- for (TemplatePrevious *p = previous; p; p = p->prev)
- {
- if (arrayObjectMatch(p->dedargs, dedargs))
- {
- //printf("recursive, no match p->sc=%p %p %s\n", p->sc, this, this->toChars());
- /* It must be a subscope of p->sc, other scope chains are not recursive
- * instantiations.
- */
- for (Scope *scx = sc; scx; scx = scx->enclosing)
- {
- if (scx == p->sc)
- return false;
- }
- }
- /* BUG: should also check for ref param differences
- */
- }
-
- TemplatePrevious pr;
- pr.prev = previous;
- pr.sc = paramscope;
- pr.dedargs = dedargs;
- previous = &pr; // add this to threaded list
-
- Scope *scx = paramscope->push(ti);
- scx->parent = ti;
- scx->tinst = NULL;
- scx->minst = NULL;
-
- assert(!ti->symtab);
- if (fd)
- {
- /* Declare all the function parameters as variables and add them to the scope
- * Making parameters is similar to FuncDeclaration::semantic3
- */
- TypeFunction *tf = (TypeFunction *)fd->type;
- assert(tf->ty == Tfunction);
-
- scx->parent = fd;
-
- Parameters *fparameters = tf->parameterList.parameters;
- VarArg fvarargs = tf->parameterList.varargs;
-
- size_t nfparams = Parameter::dim(fparameters);
- for (size_t i = 0; i < nfparams; i++)
- {
- Parameter *fparam = Parameter::getNth(fparameters, i);
- fparam->storageClass &= (STCin | STCout | STCref | STClazy | STCfinal | STC_TYPECTOR | STCnodtor);
- fparam->storageClass |= STCparameter;
- if (fvarargs == VARARGtypesafe && i + 1 == nfparams)
- fparam->storageClass |= STCvariadic;
- }
- for (size_t i = 0; i < fparameters->length; i++)
- {
- Parameter *fparam = (*fparameters)[i];
- if (!fparam->ident)
- continue; // don't add it, if it has no name
- VarDeclaration *v = new VarDeclaration(loc, fparam->type, fparam->ident, NULL);
- v->storage_class = fparam->storageClass;
- dsymbolSemantic(v, scx);
- if (!ti->symtab)
- ti->symtab = new DsymbolTable();
- if (!scx->insert(v))
- error("parameter %s.%s is already defined", toChars(), v->toChars());
- else
- v->parent = fd;
- }
- if (isstatic)
- fd->storage_class |= STCstatic;
-
- fd->vthis = fd->declareThis(scx, fd->isThis());
- }
-
- Expression *e = constraint->syntaxCopy();
-
- assert(ti->inst == NULL);
- ti->inst = ti; // temporary instantiation to enable genIdent()
-
- scx->flags |= SCOPEconstraint;
- bool errors = false;
- bool result = evalStaticCondition(scx, constraint, e, errors);
- ti->inst = NULL;
- ti->symtab = NULL;
- scx = scx->pop();
- previous = pr.prev; // unlink from threaded list
- if (errors)
- return false;
- return result;
-}
-
-/***************************************
- * Given that ti is an instance of this TemplateDeclaration,
- * deduce the types of the parameters to this, and store
- * those deduced types in dedtypes[].
- * Input:
- * flag 1: don't do semantic() because of dummy types
- * 2: don't change types in matchArg()
- * Output:
- * dedtypes deduced arguments
- * Return match level.
- */
-
-MATCH TemplateDeclaration::matchWithInstance(Scope *sc, TemplateInstance *ti,
- Objects *dedtypes, Expressions *fargs, int flag)
-{
- MATCH m;
- size_t dedtypes_dim = dedtypes->length;
-
- dedtypes->zero();
-
- if (errors)
- return MATCHnomatch;
-
- size_t parameters_dim = parameters->length;
- int variadic = isVariadic() != NULL;
-
- // If more arguments than parameters, no match
- if (ti->tiargs->length > parameters_dim && !variadic)
- {
- return MATCHnomatch;
- }
-
- assert(dedtypes_dim == parameters_dim);
- assert(dedtypes_dim >= ti->tiargs->length || variadic);
-
- assert(_scope);
-
- // Set up scope for template parameters
- ScopeDsymbol *paramsym = new ScopeDsymbol();
- paramsym->parent = _scope->parent;
- Scope *paramscope = _scope->push(paramsym);
- paramscope->tinst = ti;
- paramscope->minst = sc->minst;
- paramscope->callsc = sc;
- paramscope->stc = 0;
-
- // Attempt type deduction
- m = MATCHexact;
- for (size_t i = 0; i < dedtypes_dim; i++)
- {
- MATCH m2;
- TemplateParameter *tp = (*parameters)[i];
- Declaration *sparam;
-
- //printf("\targument [%d]\n", i);
- inuse++;
- m2 = tp->matchArg(ti->loc, paramscope, ti->tiargs, i, parameters, dedtypes, &sparam);
- inuse--;
- //printf("\tm2 = %d\n", m2);
-
- if (m2 == MATCHnomatch)
- {
- goto Lnomatch;
- }
-
- if (m2 < m)
- m = m2;
-
- if (!flag)
- dsymbolSemantic(sparam, paramscope);
- if (!paramscope->insert(sparam)) // TODO: This check can make more early
- goto Lnomatch; // in TemplateDeclaration::semantic, and
- // then we don't need to make sparam if flags == 0
- }
-
- if (!flag)
- {
- /* Any parameter left without a type gets the type of
- * its corresponding arg
- */
- for (size_t i = 0; i < dedtypes_dim; i++)
- {
- if (!(*dedtypes)[i])
- {
- assert(i < ti->tiargs->length);
- (*dedtypes)[i] = (Type *)(*ti->tiargs)[i];
- }
- }
- }
-
- if (m > MATCHnomatch && constraint && !flag)
- {
- if (ti->hasNestedArgs(ti->tiargs, this->isstatic)) // TODO: should gag error
- ti->parent = ti->enclosing;
- else
- ti->parent = this->parent;
-
- // Similar to doHeaderInstantiation
- FuncDeclaration *fd = onemember ? onemember->isFuncDeclaration() : NULL;
- if (fd)
- {
- assert(fd->type->ty == Tfunction);
- TypeFunction *tf = (TypeFunction *)fd->type->syntaxCopy();
-
- fd = new FuncDeclaration(fd->loc, fd->endloc, fd->ident, fd->storage_class, tf);
- fd->parent = ti;
- fd->inferRetType = true;
-
- // Shouldn't run semantic on default arguments and return type.
- for (size_t i = 0; i < tf->parameterList.parameters->length; i++)
- (*tf->parameterList.parameters)[i]->defaultArg = NULL;
- tf->next = NULL;
-
- // Resolve parameter types and 'auto ref's.
- tf->fargs = fargs;
- unsigned olderrors = global.startGagging();
- fd->type = typeSemantic(tf, loc, paramscope);
- if (global.endGagging(olderrors))
- {
- assert(fd->type->ty != Tfunction);
- goto Lnomatch;
- }
- assert(fd->type->ty == Tfunction);
- fd->originalType = fd->type; // for mangling
- }
-
- // TODO: dedtypes => ti->tiargs ?
- if (!evaluateConstraint(ti, sc, paramscope, dedtypes, fd))
- goto Lnomatch;
- }
-
- goto Lret;
-
-Lnomatch:
- m = MATCHnomatch;
-
-Lret:
- paramscope->pop();
- return m;
-}
-
-/********************************************
- * Determine partial specialization order of 'this' vs td2.
- * Returns:
- * match this is at least as specialized as td2
- * 0 td2 is more specialized than this
- */
-
-MATCH TemplateDeclaration::leastAsSpecialized(Scope *sc, TemplateDeclaration *td2, Expressions *fargs)
-{
- /* This works by taking the template parameters to this template
- * declaration and feeding them to td2 as if it were a template
- * instance.
- * If it works, then this template is at least as specialized
- * as td2.
- */
-
- TemplateInstance ti(Loc(), ident); // create dummy template instance
- // Set type arguments to dummy template instance to be types
- // generated from the parameters to this template declaration
- ti.tiargs = new Objects();
- ti.tiargs->reserve(parameters->length);
- for (size_t i = 0; i < parameters->length; i++)
- {
- TemplateParameter *tp = (*parameters)[i];
- if (tp->dependent)
- break;
- RootObject *p = (RootObject *)tp->dummyArg();
- if (!p)
- break;
-
- ti.tiargs->push(p);
- }
-
- // Temporary Array to hold deduced types
- Objects dedtypes;
- dedtypes.setDim(td2->parameters->length);
-
- // Attempt a type deduction
- MATCH m = td2->matchWithInstance(sc, &ti, &dedtypes, fargs, 1);
- if (m > MATCHnomatch)
- {
- /* A non-variadic template is more specialized than a
- * variadic one.
- */
- TemplateTupleParameter *tp = isVariadic();
- if (tp && !tp->dependent && !td2->isVariadic())
- goto L1;
-
- return m;
- }
- L1:
- return MATCHnomatch;
-}
-
-static Expression *emptyArrayElement = NULL;
-
-class TypeDeduced : public Type
-{
-public:
- Type *tded;
- Expressions argexps; // corresponding expressions
- Types tparams; // tparams[i]->mod
-
- TypeDeduced(Type *tt, Expression *e, Type *tparam)
- : Type(Tnone)
- {
- tded = tt;
- argexps.push(e);
- tparams.push(tparam);
- }
-
- virtual ~TypeDeduced()
- {
- }
-
- void update(Expression *e, Type *tparam)
- {
- argexps.push(e);
- tparams.push(tparam);
- }
- void update(Type *tt, Expression *e, Type *tparam)
- {
- tded = tt;
- argexps.push(e);
- tparams.push(tparam);
- }
- MATCH matchAll(Type *tt)
- {
- MATCH match = MATCHexact;
- for (size_t j = 0; j < argexps.length; j++)
- {
- Expression *e = argexps[j];
- assert(e);
- if (e == emptyArrayElement)
- continue;
-
- Type *t = tt->addMod(tparams[j]->mod)->substWildTo(MODconst);
-
- MATCH m = e->implicitConvTo(t);
- if (match > m)
- match = m;
- if (match <= MATCHnomatch)
- break;
- }
- return match;
- }
-};
-
-/*************************************************
- * Match function arguments against a specific template function.
- * Input:
- * ti
- * sc instantiation scope
- * fd
- * tthis 'this' argument if !NULL
- * fargs arguments to function
- * Output:
- * fd Partially instantiated function declaration
- * ti->tdtypes Expression/Type deduced template arguments
- * Returns:
- * match level
- * bit 0-3 Match template parameters by inferred template arguments
- * bit 4-7 Match template parameters by initial template arguments
- */
-
-MATCH TemplateDeclaration::deduceFunctionTemplateMatch(
- TemplateInstance *ti, Scope *sc,
- FuncDeclaration *&fd, Type *tthis, Expressions *fargs)
-{
- size_t nfparams;
- size_t nfargs;
- size_t ntargs; // array size of tiargs
- size_t fptupindex = IDX_NOTFOUND;
- MATCH match = MATCHexact;
- MATCH matchTiargs = MATCHexact;
- ParameterList fparameters; // function parameter list
- unsigned wildmatch = 0;
- size_t inferStart = 0;
-
- Loc instLoc = ti->loc;
- Objects *tiargs = ti->tiargs;
- Objects *dedargs = new Objects();
- Objects* dedtypes = &ti->tdtypes; // for T:T*, the dedargs is the T*, dedtypes is the T
-
- assert(_scope);
-
- dedargs->setDim(parameters->length);
- dedargs->zero();
-
- dedtypes->setDim(parameters->length);
- dedtypes->zero();
-
- if (errors || fd->errors)
- return MATCHnomatch;
-
- // Set up scope for parameters
- ScopeDsymbol *paramsym = new ScopeDsymbol();
- paramsym->parent = _scope->parent; // should use hasnestedArgs and enclosing?
- Scope *paramscope = _scope->push(paramsym);
- paramscope->tinst = ti;
- paramscope->minst = sc->minst;
- paramscope->callsc = sc;
- paramscope->stc = 0;
-
- TemplateTupleParameter *tp = isVariadic();
- Tuple *declaredTuple = NULL;
-
- ntargs = 0;
- if (tiargs)
- {
- // Set initial template arguments
- ntargs = tiargs->length;
- size_t n = parameters->length;
- if (tp)
- n--;
- if (ntargs > n)
- {
- if (!tp)
- goto Lnomatch;
-
- /* The extra initial template arguments
- * now form the tuple argument.
- */
- Tuple *t = new Tuple();
- assert(parameters->length);
- (*dedargs)[parameters->length - 1] = t;
-
- t->objects.setDim(ntargs - n);
- for (size_t i = 0; i < t->objects.length; i++)
- {
- t->objects[i] = (*tiargs)[n + i];
- }
- declareParameter(paramscope, tp, t);
- declaredTuple = t;
- }
- else
- n = ntargs;
-
- memcpy(dedargs->tdata(), tiargs->tdata(), n * sizeof(*dedargs->tdata()));
-
- for (size_t i = 0; i < n; i++)
- {
- assert(i < parameters->length);
- Declaration *sparam = NULL;
- MATCH m = (*parameters)[i]->matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, &sparam);
- //printf("\tdeduceType m = %d\n", m);
- if (m <= MATCHnomatch)
- goto Lnomatch;
- if (m < matchTiargs)
- matchTiargs = m;
-
- dsymbolSemantic(sparam, paramscope);
- if (!paramscope->insert(sparam))
- goto Lnomatch;
- }
- if (n < parameters->length && !declaredTuple)
- {
- inferStart = n;
- }
- else
- inferStart = parameters->length;
- //printf("tiargs matchTiargs = %d\n", matchTiargs);
- }
-
- fparameters = fd->getParameterList();
- nfparams = fparameters.length(); // number of function parameters
- nfargs = fargs ? fargs->length : 0; // number of function arguments
-
- /* Check for match of function arguments with variadic template
- * parameter, such as:
- *
- * void foo(T, A...)(T t, A a);
- * void main() { foo(1,2,3); }
- */
- if (tp) // if variadic
- {
- // TemplateTupleParameter always makes most lesser matching.
- matchTiargs = MATCHconvert;
-
- if (nfparams == 0 && nfargs != 0) // if no function parameters
- {
- if (!declaredTuple)
- {
- Tuple *t = new Tuple();
- //printf("t = %p\n", t);
- (*dedargs)[parameters->length - 1] = t;
- declareParameter(paramscope, tp, t);
- declaredTuple = t;
- }
- }
- else
- {
- /* Figure out which of the function parameters matches
- * the tuple template parameter. Do this by matching
- * type identifiers.
- * Set the index of this function parameter to fptupindex.
- */
- for (fptupindex = 0; fptupindex < nfparams; fptupindex++)
- {
- Parameter *fparam = (*fparameters.parameters)[fptupindex];
- if (fparam->type->ty != Tident)
- continue;
- TypeIdentifier *tid = (TypeIdentifier *)fparam->type;
- if (!tp->ident->equals(tid->ident) || tid->idents.length)
- continue;
-
- if (fparameters.varargs != VARARGnone) // variadic function doesn't
- goto Lnomatch; // go with variadic template
-
- goto L1;
- }
- fptupindex = IDX_NOTFOUND;
- L1:
- ;
- }
- }
-
- if (toParent()->isModule() || (_scope->stc & STCstatic))
- tthis = NULL;
- if (tthis)
- {
- bool hasttp = false;
-
- // Match 'tthis' to any TemplateThisParameter's
- for (size_t i = 0; i < parameters->length; i++)
- {
- TemplateThisParameter *ttp = (*parameters)[i]->isTemplateThisParameter();
- if (ttp)
- {
- hasttp = true;
-
- Type *t = new TypeIdentifier(Loc(), ttp->ident);
- MATCH m = deduceType(tthis, paramscope, t, parameters, dedtypes);
- if (m <= MATCHnomatch)
- goto Lnomatch;
- if (m < match)
- match = m; // pick worst match
- }
- }
-
- // Match attributes of tthis against attributes of fd
- if (fd->type && !fd->isCtorDeclaration())
- {
- StorageClass stc = _scope->stc | fd->storage_class2;
- // Propagate parent storage class (see bug 5504)
- Dsymbol *p = parent;
- while (p->isTemplateDeclaration() || p->isTemplateInstance())
- p = p->parent;
- AggregateDeclaration *ad = p->isAggregateDeclaration();
- if (ad)
- stc |= ad->storage_class;
-
- unsigned char mod = fd->type->mod;
- if (stc & STCimmutable)
- mod = MODimmutable;
- else
- {
- if (stc & (STCshared | STCsynchronized))
- mod |= MODshared;
- if (stc & STCconst)
- mod |= MODconst;
- if (stc & STCwild)
- mod |= MODwild;
- }
-
- unsigned char thismod = tthis->mod;
- if (hasttp)
- mod = MODmerge(thismod, mod);
- MATCH m = MODmethodConv(thismod, mod);
- if (m <= MATCHnomatch)
- goto Lnomatch;
- if (m < match)
- match = m;
- }
- }
-
- // Loop through the function parameters
- {
- //printf("%s\n\tnfargs = %d, nfparams = %d, tuple_dim = %d\n", toChars(), nfargs, nfparams, declaredTuple ? declaredTuple->objects.length : 0);
- //printf("\ttp = %p, fptupindex = %d, found = %d, declaredTuple = %s\n", tp, fptupindex, fptupindex != IDX_NOTFOUND, declaredTuple ? declaredTuple->toChars() : NULL);
- size_t argi = 0;
- size_t nfargs2 = nfargs; // nfargs + supplied defaultArgs
- for (size_t parami = 0; parami < nfparams; parami++)
- {
- Parameter *fparam = fparameters[parami];
-
- // Apply function parameter storage classes to parameter types
- Type *prmtype = fparam->type->addStorageClass(fparam->storageClass);
-
- Expression *farg;
-
- /* See function parameters which wound up
- * as part of a template tuple parameter.
- */
- if (fptupindex != IDX_NOTFOUND && parami == fptupindex)
- {
- assert(prmtype->ty == Tident);
- TypeIdentifier *tid = (TypeIdentifier *)prmtype;
- if (!declaredTuple)
- {
- /* The types of the function arguments
- * now form the tuple argument.
- */
- declaredTuple = new Tuple();
- (*dedargs)[parameters->length - 1] = declaredTuple;
-
- /* Count function parameters following a tuple parameter.
- * void foo(U, T...)(int y, T, U, int) {} // rem == 2 (U, int)
- */
- size_t rem = 0;
- for (size_t j = parami + 1; j < nfparams; j++)
- {
- Parameter *p = fparameters[j];
- if (!reliesOnTident(p->type, parameters, inferStart))
- {
- Type *pt = typeSemantic(p->type->syntaxCopy(), fd->loc, paramscope);
- rem += pt->ty == Ttuple ? ((TypeTuple *)pt)->arguments->length : 1;
- }
- else
- {
- ++rem;
- }
- }
-
- if (nfargs2 - argi < rem)
- goto Lnomatch;
- declaredTuple->objects.setDim(nfargs2 - argi - rem);
- for (size_t i = 0; i < declaredTuple->objects.length; i++)
- {
- farg = (*fargs)[argi + i];
-
- // Check invalid arguments to detect errors early.
- if (farg->op == TOKerror || farg->type->ty == Terror)
- goto Lnomatch;
-
- if (!(fparam->storageClass & STClazy) && farg->type->ty == Tvoid)
- goto Lnomatch;
-
- Type *tt;
- MATCH m;
- if (unsigned char wm = deduceWildHelper(farg->type, &tt, tid))
- {
- wildmatch |= wm;
- m = MATCHconst;
- }
- else
- {
- m = deduceTypeHelper(farg->type, &tt, tid);
- }
- if (m <= MATCHnomatch)
- goto Lnomatch;
- if (m < match)
- match = m;
-
- /* Remove top const for dynamic array types and pointer types
- */
- if ((tt->ty == Tarray || tt->ty == Tpointer) &&
- !tt->isMutable() &&
- (!(fparam->storageClass & STCref) ||
- ((fparam->storageClass & STCauto) && !farg->isLvalue())))
- {
- tt = tt->mutableOf();
- }
- declaredTuple->objects[i] = tt;
- }
- declareParameter(paramscope, tp, declaredTuple);
- }
- else
- {
- // Bugzilla 6810: If declared tuple is not a type tuple,
- // it cannot be function parameter types.
- for (size_t i = 0; i < declaredTuple->objects.length; i++)
- {
- if (!isType(declaredTuple->objects[i]))
- goto Lnomatch;
- }
- }
- assert(declaredTuple);
- argi += declaredTuple->objects.length;
- continue;
- }
-
- // If parameter type doesn't depend on inferred template parameters,
- // semantic it to get actual type.
- if (!reliesOnTident(prmtype, parameters, inferStart))
- {
- // should copy prmtype to avoid affecting semantic result
- prmtype = typeSemantic(prmtype->syntaxCopy(), fd->loc, paramscope);
-
- if (prmtype->ty == Ttuple)
- {
- TypeTuple *tt = (TypeTuple *)prmtype;
- size_t tt_dim = tt->arguments->length;
- for (size_t j = 0; j < tt_dim; j++, ++argi)
- {
- Parameter *p = (*tt->arguments)[j];
- if (j == tt_dim - 1 && fparameters.varargs == VARARGtypesafe &&
- parami + 1 == nfparams && argi < nfargs)
- {
- prmtype = p->type;
- goto Lvarargs;
- }
- if (argi >= nfargs)
- {
- if (p->defaultArg)
- continue;
- goto Lnomatch;
- }
- farg = (*fargs)[argi];
- if (!farg->implicitConvTo(p->type))
- goto Lnomatch;
- }
- continue;
- }
- }
-
- if (argi >= nfargs) // if not enough arguments
- {
- if (!fparam->defaultArg)
- goto Lvarargs;
-
- /* Bugzilla 2803: Before the starting of type deduction from the function
- * default arguments, set the already deduced parameters into paramscope.
- * It's necessary to avoid breaking existing acceptable code. Cases:
- *
- * 1. Already deduced template parameters can appear in fparam->defaultArg:
- * auto foo(A, B)(A a, B b = A.stringof);
- * foo(1);
- * // at fparam == 'B b = A.string', A is equivalent with the deduced type 'int'
- *
- * 2. If prmtype depends on default-specified template parameter, the
- * default type should be preferred.
- * auto foo(N = size_t, R)(R r, N start = 0)
- * foo([1,2,3]);
- * // at fparam `N start = 0`, N should be 'size_t' before
- * // the deduction result from fparam->defaultArg.
- */
- if (argi == nfargs)
- {
- for (size_t i = 0; i < dedtypes->length; i++)
- {
- Type *at = isType((*dedtypes)[i]);
- if (at && at->ty == Tnone)
- {
- TypeDeduced *xt = (TypeDeduced *)at;
- (*dedtypes)[i] = xt->tded; // 'unbox'
- delete xt;
- }
- }
- for (size_t i = ntargs; i < dedargs->length; i++)
- {
- TemplateParameter *tparam = (*parameters)[i];
-
- RootObject *oarg = (*dedargs)[i];
- RootObject *oded = (*dedtypes)[i];
- if (!oarg)
- {
- if (oded)
- {
- if (tparam->specialization() || !tparam->isTemplateTypeParameter())
- {
- /* The specialization can work as long as afterwards
- * the oded == oarg
- */
- (*dedargs)[i] = oded;
- MATCH m2 = tparam->matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, NULL);
- //printf("m2 = %d\n", m2);
- if (m2 <= MATCHnomatch)
- goto Lnomatch;
- if (m2 < matchTiargs)
- matchTiargs = m2; // pick worst match
- if (!(*dedtypes)[i]->equals(oded))
- error("specialization not allowed for deduced parameter %s", tparam->ident->toChars());
- }
- else
- {
- if (MATCHconvert < matchTiargs)
- matchTiargs = MATCHconvert;
- }
- (*dedargs)[i] = declareParameter(paramscope, tparam, oded);
- }
- else
- {
- inuse++;
- oded = tparam->defaultArg(instLoc, paramscope);
- inuse--;
- if (oded)
- (*dedargs)[i] = declareParameter(paramscope, tparam, oded);
- }
- }
- }
- }
- nfargs2 = argi + 1;
-
- /* If prmtype does not depend on any template parameters:
- *
- * auto foo(T)(T v, double x = 0);
- * foo("str");
- * // at fparam == 'double x = 0'
- *
- * or, if all template parameters in the prmtype are already deduced:
- *
- * auto foo(R)(R range, ElementType!R sum = 0);
- * foo([1,2,3]);
- * // at fparam == 'ElementType!R sum = 0'
- *
- * Deducing prmtype from fparam->defaultArg is not necessary.
- */
- if (prmtype->deco ||
- prmtype->syntaxCopy()->trySemantic(loc, paramscope))
- {
- ++argi;
- continue;
- }
-
- // Deduce prmtype from the defaultArg.
- farg = fparam->defaultArg->syntaxCopy();
- farg = expressionSemantic(farg, paramscope);
- farg = resolveProperties(paramscope, farg);
- }
- else
- {
- farg = (*fargs)[argi];
- }
- {
- // Check invalid arguments to detect errors early.
- if (farg->op == TOKerror || farg->type->ty == Terror)
- goto Lnomatch;
-
- Type *att = NULL;
- Lretry:
- Type *argtype = farg->type;
-
- if (!(fparam->storageClass & STClazy) && argtype->ty == Tvoid && farg->op != TOKfunction)
- goto Lnomatch;
-
- // Bugzilla 12876: optimize arugument to allow CT-known length matching
- farg = farg->optimize(WANTvalue, (fparam->storageClass & (STCref | STCout)) != 0);
- //printf("farg = %s %s\n", farg->type->toChars(), farg->toChars());
-
- RootObject *oarg = farg;
- if ((fparam->storageClass & STCref) &&
- (!(fparam->storageClass & STCauto) || farg->isLvalue()))
- {
- /* Allow expressions that have CT-known boundaries and type [] to match with [dim]
- */
- Type *taai;
- if (argtype->ty == Tarray &&
- (prmtype->ty == Tsarray ||
- (prmtype->ty == Taarray && (taai = ((TypeAArray *)prmtype)->index)->ty == Tident &&
- ((TypeIdentifier *)taai)->idents.length == 0)))
- {
- if (farg->op == TOKstring)
- {
- StringExp *se = (StringExp *)farg;
- argtype = se->type->nextOf()->sarrayOf(se->len);
- }
- else if (farg->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ae = (ArrayLiteralExp *)farg;
- argtype = ae->type->nextOf()->sarrayOf(ae->elements->length);
- }
- else if (farg->op == TOKslice)
- {
- SliceExp *se = (SliceExp *)farg;
- if (Type *tsa = toStaticArrayType(se))
- argtype = tsa;
- }
- }
-
- oarg = argtype;
- }
- else if ((fparam->storageClass & STCout) == 0 &&
- (argtype->ty == Tarray || argtype->ty == Tpointer) &&
- templateParameterLookup(prmtype, parameters) != IDX_NOTFOUND &&
- ((TypeIdentifier *)prmtype)->idents.length == 0)
- {
- /* The farg passing to the prmtype always make a copy. Therefore,
- * we can shrink the set of the deduced type arguments for prmtype
- * by adjusting top-qualifier of the argtype.
- *
- * prmtype argtype ta
- * T <- const(E)[] const(E)[]
- * T <- const(E[]) const(E)[]
- * qualifier(T) <- const(E)[] const(E[])
- * qualifier(T) <- const(E[]) const(E[])
- */
- Type *ta = argtype->castMod(prmtype->mod ? argtype->nextOf()->mod : 0);
- if (ta != argtype)
- {
- Expression *ea = farg->copy();
- ea->type = ta;
- oarg = ea;
- }
- }
-
- if (fparameters.varargs == VARARGtypesafe && parami + 1 == nfparams && argi + 1 < nfargs)
- goto Lvarargs;
-
- unsigned wm = 0;
- MATCH m = deduceType(oarg, paramscope, prmtype, parameters, dedtypes, &wm, inferStart);
- //printf("\tL%d deduceType m = %d, wm = x%x, wildmatch = x%x\n", __LINE__, m, wm, wildmatch);
- wildmatch |= wm;
-
- /* If no match, see if the argument can be matched by using
- * implicit conversions.
- */
- if (m == MATCHnomatch && prmtype->deco)
- m = farg->implicitConvTo(prmtype);
-
- if (m == MATCHnomatch)
- {
- AggregateDeclaration *ad = isAggregate(farg->type);
- if (ad && ad->aliasthis && argtype != att)
- {
- if (!att && argtype->checkAliasThisRec()) // Bugzilla 12537
- att = argtype;
-
- /* If a semantic error occurs while doing alias this,
- * eg purity(bug 7295), just regard it as not a match.
- */
- if (Expression *e = resolveAliasThis(sc, farg, true))
- {
- farg = e;
- goto Lretry;
- }
- }
- }
-
- if (m > MATCHnomatch && (fparam->storageClass & (STCref | STCauto)) == STCref)
- {
- if (!farg->isLvalue())
- {
- if ((farg->op == TOKstring || farg->op == TOKslice) &&
- (prmtype->ty == Tsarray || prmtype->ty == Taarray))
- {
- // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
- }
- else
- goto Lnomatch;
- }
- }
- if (m > MATCHnomatch && (fparam->storageClass & STCout))
- {
- if (!farg->isLvalue())
- goto Lnomatch;
- if (!farg->type->isMutable()) // Bugzilla 11916
- goto Lnomatch;
- }
- if (m == MATCHnomatch && (fparam->storageClass & STClazy) && prmtype->ty == Tvoid &&
- farg->type->ty != Tvoid)
- m = MATCHconvert;
-
- if (m != MATCHnomatch)
- {
- if (m < match)
- match = m; // pick worst match
- argi++;
- continue;
- }
- }
-
- Lvarargs:
- /* The following code for variadic arguments closely
- * matches TypeFunction::callMatch()
- */
- if (!(fparameters.varargs == VARARGtypesafe && parami + 1 == nfparams))
- goto Lnomatch;
-
- /* Check for match with function parameter T...
- */
- Type *tb = prmtype->toBasetype();
- switch (tb->ty)
- {
- // 6764 fix - TypeAArray may be TypeSArray have not yet run semantic().
- case Tsarray:
- case Taarray:
- // Perhaps we can do better with this, see TypeFunction::callMatch()
- if (tb->ty == Tsarray)
- {
- TypeSArray *tsa = (TypeSArray *)tb;
- dinteger_t sz = tsa->dim->toInteger();
- if (sz != nfargs - argi)
- goto Lnomatch;
- }
- else if (tb->ty == Taarray)
- {
- TypeAArray *taa = (TypeAArray *)tb;
- Expression *dim = new IntegerExp(instLoc, nfargs - argi, Type::tsize_t);
-
- size_t i = templateParameterLookup(taa->index, parameters);
- if (i == IDX_NOTFOUND)
- {
- Expression *e;
- Type *t;
- Dsymbol *s;
- Scope *sco;
-
- unsigned errors = global.startGagging();
- /* ref: https://issues.dlang.org/show_bug.cgi?id=11118
- * The parameter isn't part of the template
- * ones, let's try to find it in the
- * instantiation scope 'sc' and the one
- * belonging to the template itself. */
- sco = sc;
- taa->index->resolve(instLoc, sco, &e, &t, &s);
- if (!e)
- {
- sco = paramscope;
- taa->index->resolve(instLoc, sco, &e, &t, &s);
- }
- global.endGagging(errors);
-
- if (!e)
- {
- goto Lnomatch;
- }
-
- e = e->ctfeInterpret();
- e = e->implicitCastTo(sco, Type::tsize_t);
- e = e->optimize(WANTvalue);
- if (!dim->equals(e))
- goto Lnomatch;
- }
- else
- {
- // This code matches code in TypeInstance::deduceType()
- TemplateParameter *tprm = (*parameters)[i];
- TemplateValueParameter *tvp = tprm->isTemplateValueParameter();
- if (!tvp)
- goto Lnomatch;
- Expression *e = (Expression *)(*dedtypes)[i];
- if (e)
- {
- if (!dim->equals(e))
- goto Lnomatch;
- }
- else
- {
- Type *vt = typeSemantic(tvp->valType, Loc(), sc);
- MATCH m = (MATCH)dim->implicitConvTo(vt);
- if (m <= MATCHnomatch)
- goto Lnomatch;
- (*dedtypes)[i] = dim;
- }
- }
- }
- /* fall through */
- case Tarray:
- {
- TypeArray *ta = (TypeArray *)tb;
- Type *tret = fparam->isLazyArray();
- for (; argi < nfargs; argi++)
- {
- Expression *arg = (*fargs)[argi];
- assert(arg);
-
- MATCH m;
- /* If lazy array of delegates,
- * convert arg(s) to delegate(s)
- */
- if (tret)
- {
- if (ta->next->equals(arg->type))
- {
- m = MATCHexact;
- }
- else
- {
- m = arg->implicitConvTo(tret);
- if (m == MATCHnomatch)
- {
- if (tret->toBasetype()->ty == Tvoid)
- m = MATCHconvert;
- }
- }
- }
- else
- {
- unsigned wm = 0;
- m = deduceType(arg, paramscope, ta->next, parameters, dedtypes, &wm, inferStart);
- wildmatch |= wm;
- }
- if (m == MATCHnomatch)
- goto Lnomatch;
- if (m < match)
- match = m;
- }
- goto Lmatch;
- }
- case Tclass:
- case Tident:
- goto Lmatch;
-
- default:
- goto Lnomatch;
- }
- ++argi;
- }
- //printf("-> argi = %d, nfargs = %d, nfargs2 = %d\n", argi, nfargs, nfargs2);
- if (argi != nfargs2 && fparameters.varargs == VARARGnone)
- goto Lnomatch;
- }
-
-Lmatch:
-
- for (size_t i = 0; i < dedtypes->length; i++)
- {
- Type *at = isType((*dedtypes)[i]);
- if (at)
- {
- if (at->ty == Tnone)
- {
- TypeDeduced *xt = (TypeDeduced *)at;
- at = xt->tded; // 'unbox'
- delete xt;
- }
- (*dedtypes)[i] = at->merge2();
- }
- }
- for (size_t i = ntargs; i < dedargs->length; i++)
- {
- TemplateParameter *tparam = (*parameters)[i];
- //printf("tparam[%d] = %s\n", i, tparam->ident->toChars());
- /* For T:T*, the dedargs is the T*, dedtypes is the T
- * But for function templates, we really need them to match
- */
- RootObject *oarg = (*dedargs)[i];
- RootObject *oded = (*dedtypes)[i];
- //printf("1dedargs[%d] = %p, dedtypes[%d] = %p\n", i, oarg, i, oded);
- //if (oarg) printf("oarg: %s\n", oarg->toChars());
- //if (oded) printf("oded: %s\n", oded->toChars());
- if (!oarg)
- {
- if (oded)
- {
- if (tparam->specialization() || !tparam->isTemplateTypeParameter())
- {
- /* The specialization can work as long as afterwards
- * the oded == oarg
- */
- (*dedargs)[i] = oded;
- MATCH m2 = tparam->matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, NULL);
- //printf("m2 = %d\n", m2);
- if (m2 <= MATCHnomatch)
- goto Lnomatch;
- if (m2 < matchTiargs)
- matchTiargs = m2; // pick worst match
- if (!(*dedtypes)[i]->equals(oded))
- error("specialization not allowed for deduced parameter %s", tparam->ident->toChars());
- }
- else
- {
- if (MATCHconvert < matchTiargs)
- matchTiargs = MATCHconvert;
- }
- }
- else
- {
- inuse++;
- oded = tparam->defaultArg(instLoc, paramscope);
- inuse--;
- if (!oded)
- {
- // if tuple parameter and
- // tuple parameter was not in function parameter list and
- // we're one or more arguments short (i.e. no tuple argument)
- if (tparam == tp &&
- fptupindex == IDX_NOTFOUND &&
- ntargs <= dedargs->length - 1)
- {
- // make tuple argument an empty tuple
- oded = (RootObject *)new Tuple();
- }
- else
- goto Lnomatch;
- }
- if (isError(oded))
- goto Lerror;
- ntargs++;
-
- /* At the template parameter T, the picked default template argument
- * X!int should be matched to T in order to deduce dependent
- * template parameter A.
- * auto foo(T : X!A = X!int, A...)() { ... }
- * foo(); // T <-- X!int, A <-- (int)
- */
- if (tparam->specialization())
- {
- (*dedargs)[i] = oded;
- MATCH m2 = tparam->matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, NULL);
- //printf("m2 = %d\n", m2);
- if (m2 <= MATCHnomatch)
- goto Lnomatch;
- if (m2 < matchTiargs)
- matchTiargs = m2; // pick worst match
- if (!(*dedtypes)[i]->equals(oded))
- error("specialization not allowed for deduced parameter %s", tparam->ident->toChars());
- }
- }
- oded = declareParameter(paramscope, tparam, oded);
- (*dedargs)[i] = oded;
- }
- }
-
- /* Bugzilla 7469: As same as the code for 7469 in findBestMatch,
- * expand a Tuple in dedargs to normalize template arguments.
- */
- if (size_t d = dedargs->length)
- {
- if (Tuple *va = isTuple((*dedargs)[d - 1]))
- {
- if (va->objects.length)
- {
- dedargs->setDim(d - 1);
- dedargs->insert(d - 1, &va->objects);
- }
- }
- }
- ti->tiargs = dedargs; // update to the normalized template arguments.
-
- // Partially instantiate function for constraint and fd->leastAsSpecialized()
- {
- assert(paramsym);
- Scope *sc2 = _scope;
- sc2 = sc2->push(paramsym);
- sc2 = sc2->push(ti);
- sc2->parent = ti;
- sc2->tinst = ti;
- sc2->minst = sc->minst;
-
- fd = doHeaderInstantiation(ti, sc2, fd, tthis, fargs);
-
- sc2 = sc2->pop();
- sc2 = sc2->pop();
-
- if (!fd)
- goto Lnomatch;
- }
-
- if (constraint)
- {
- if (!evaluateConstraint(ti, sc, paramscope, dedargs, fd))
- goto Lnomatch;
- }
-
- paramscope->pop();
- //printf("\tmatch %d\n", match);
- return (MATCH)(match | (matchTiargs<<4));
-
-Lnomatch:
- paramscope->pop();
- //printf("\tnomatch\n");
- return MATCHnomatch;
-
-Lerror: // todo: for the future improvement
- paramscope->pop();
- //printf("\terror\n");
- return MATCHnomatch;
-}
-
-/**************************************************
- * Declare template parameter tp with value o, and install it in the scope sc.
- */
-
-RootObject *TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, RootObject *o)
-{
- //printf("TemplateDeclaration::declareParameter('%s', o = %p)\n", tp->ident->toChars(), o);
-
- Type *ta = isType(o);
- Expression *ea = isExpression(o);
- Dsymbol *sa = isDsymbol(o);
- Tuple *va = isTuple(o);
-
- Declaration *d;
- VarDeclaration *v = NULL;
-
- if (ea && ea->op == TOKtype)
- ta = ea->type;
- else if (ea && ea->op == TOKscope)
- sa = ((ScopeExp *)ea)->sds;
- else if (ea && (ea->op == TOKthis || ea->op == TOKsuper))
- sa = ((ThisExp *)ea)->var;
- else if (ea && ea->op == TOKfunction)
- {
- if (((FuncExp *)ea)->td)
- sa = ((FuncExp *)ea)->td;
- else
- sa = ((FuncExp *)ea)->fd;
- }
-
- if (ta)
- {
- //printf("type %s\n", ta->toChars());
- d = new AliasDeclaration(Loc(), tp->ident, ta);
- }
- else if (sa)
- {
- //printf("Alias %s %s;\n", sa->ident->toChars(), tp->ident->toChars());
- d = new AliasDeclaration(Loc(), tp->ident, sa);
- }
- else if (ea)
- {
- // tdtypes.data[i] always matches ea here
- Initializer *init = new ExpInitializer(loc, ea);
- TemplateValueParameter *tvp = tp->isTemplateValueParameter();
-
- Type *t = tvp ? tvp->valType : NULL;
-
- v = new VarDeclaration(loc, t, tp->ident, init);
- v->storage_class = STCmanifest | STCtemplateparameter;
- d = v;
- }
- else if (va)
- {
- //printf("\ttuple\n");
- d = new TupleDeclaration(loc, tp->ident, &va->objects);
- }
- else
- {
- assert(0);
- }
-
- d->storage_class |= STCtemplateparameter;
- if (ta)
- {
- Type *t = ta;
- // consistent with Type::checkDeprecated()
- while (t->ty != Tenum)
- {
- if (!t->nextOf()) break;
- t = ((TypeNext *)t)->next;
- }
- if (Dsymbol *s = t->toDsymbol(sc))
- {
- if (s->isDeprecated())
- d->storage_class |= STCdeprecated;
- }
- }
- else if (sa)
- {
- if (sa->isDeprecated())
- d->storage_class |= STCdeprecated;
- }
-
- if (!sc->insert(d))
- error("declaration %s is already defined", tp->ident->toChars());
- dsymbolSemantic(d, sc);
-
- /* So the caller's o gets updated with the result of semantic() being run on o
- */
- if (v)
- o = initializerToExpression(v->_init);
- return o;
-}
-
-/**************************************
- * Determine if TemplateDeclaration is variadic.
- */
-
-TemplateTupleParameter *isVariadic(TemplateParameters *parameters)
-{
- size_t dim = parameters->length;
- TemplateTupleParameter *tp = NULL;
-
- if (dim)
- tp = ((*parameters)[dim - 1])->isTemplateTupleParameter();
- return tp;
-}
-
-TemplateTupleParameter *TemplateDeclaration::isVariadic()
-{
- return ::isVariadic(parameters);
-}
-
-/***********************************
- * We can overload templates.
- */
-
-bool TemplateDeclaration::isOverloadable()
-{
- return true;
-}
-
-/*************************************************
- * Given function arguments, figure out which template function
- * to expand, and return matching result.
- * Params:
- * m = matching result
- * dstart = the root of overloaded function templates
- * loc = instantiation location
- * sc = instantiation scope
- * tiargs = initial list of template arguments
- * tthis = if !NULL, the 'this' pointer argument
- * fargs = arguments to function
- * pMessage = address to store error message, or null
- */
-
-void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc,
- Objects *tiargs, Type *tthis, Expressions *fargs, const char **pMessage)
-{
- struct ParamDeduce
- {
- // context
- Loc loc;
- Scope *sc;
- Type *tthis;
- Objects *tiargs;
- Expressions *fargs;
- const char **pMessage;
- // result
- Match *m;
- int property; // 0: unintialized
- // 1: seen @property
- // 2: not @property
- size_t ov_index;
- TemplateDeclaration *td_best;
- TemplateInstance *ti_best;
- MATCH ta_last;
- Type *tthis_best;
-
- static int fp(void *param, Dsymbol *s)
- {
- if (s->errors)
- return 0;
- if (FuncDeclaration *fd = s->isFuncDeclaration())
- return ((ParamDeduce *)param)->applyFunction(fd);
- if (TemplateDeclaration *td = s->isTemplateDeclaration())
- return ((ParamDeduce *)param)->applyTemplate(td);
- return 0;
- }
-
- int applyFunction(FuncDeclaration *fd)
- {
- // skip duplicates
- if (fd == m->lastf)
- return 0;
- // explicitly specified tiargs never match to non template function
- if (tiargs && tiargs->length > 0)
- return 0;
-
- // constructors need a valid scope in order to detect semantic errors
- if (!fd->isCtorDeclaration() &&
- fd->semanticRun < PASSsemanticdone)
- {
- Ungag ungag = fd->ungagSpeculative();
- dsymbolSemantic(fd, NULL);
- }
- if (fd->semanticRun < PASSsemanticdone)
- {
- ::error(loc, "forward reference to template %s", fd->toChars());
- return 1;
- }
- //printf("fd = %s %s, fargs = %s\n", fd->toChars(), fd->type->toChars(), fargs->toChars());
- m->anyf = fd;
- TypeFunction *tf = (TypeFunction *)fd->type;
-
- int prop = (tf->isproperty) ? 1 : 2;
- if (property == 0)
- property = prop;
- else if (property != prop)
- error(fd->loc, "cannot overload both property and non-property functions");
-
- /* For constructors, qualifier check will be opposite direction.
- * Qualified constructor always makes qualified object, then will be checked
- * that it is implicitly convertible to tthis.
- */
- Type *tthis_fd = fd->needThis() ? tthis : NULL;
- bool isCtorCall = tthis_fd && fd->isCtorDeclaration();
- if (isCtorCall)
- {
- //printf("%s tf->mod = x%x tthis_fd->mod = x%x %d\n", tf->toChars(),
- // tf->mod, tthis_fd->mod, fd->isolateReturn());
- if (MODimplicitConv(tf->mod, tthis_fd->mod) ||
- (tf->isWild() && tf->isShared() == tthis_fd->isShared()) ||
- fd->isolateReturn())
- {
- /* && tf->isShared() == tthis_fd->isShared()*/
- // Uniquely constructed object can ignore shared qualifier.
- // TODO: Is this appropriate?
- tthis_fd = NULL;
- }
- else
- return 0; // MATCHnomatch
- }
- MATCH mfa = tf->callMatch(tthis_fd, fargs, 0, pMessage);
- //printf("test1: mfa = %d\n", mfa);
- if (mfa > MATCHnomatch)
- {
- if (mfa > m->last) goto LfIsBetter;
- if (mfa < m->last) goto LlastIsBetter;
-
- /* See if one of the matches overrides the other.
- */
- assert(m->lastf);
- if (m->lastf->overrides(fd)) goto LlastIsBetter;
- if (fd->overrides(m->lastf)) goto LfIsBetter;
-
- /* Try to disambiguate using template-style partial ordering rules.
- * In essence, if f() and g() are ambiguous, if f() can call g(),
- * but g() cannot call f(), then pick f().
- * This is because f() is "more specialized."
- */
- {
- MATCH c1 = fd->leastAsSpecialized(m->lastf);
- MATCH c2 = m->lastf->leastAsSpecialized(fd);
- //printf("c1 = %d, c2 = %d\n", c1, c2);
- if (c1 > c2) goto LfIsBetter;
- if (c1 < c2) goto LlastIsBetter;
- }
-
- /* The 'overrides' check above does covariant checking only
- * for virtual member functions. It should do it for all functions,
- * but in order to not risk breaking code we put it after
- * the 'leastAsSpecialized' check.
- * In the future try moving it before.
- * I.e. a not-the-same-but-covariant match is preferred,
- * as it is more restrictive.
- */
- if (!m->lastf->type->equals(fd->type))
- {
- //printf("cov: %d %d\n", m->lastf->type->covariant(fd->type), fd->type->covariant(m->lastf->type));
- if (m->lastf->type->covariant(fd->type) == 1) goto LlastIsBetter;
- if (fd->type->covariant(m->lastf->type) == 1) goto LfIsBetter;
- }
-
- /* If the two functions are the same function, like:
- * int foo(int);
- * int foo(int x) { ... }
- * then pick the one with the body.
- */
- if (tf->equals(m->lastf->type) &&
- fd->storage_class == m->lastf->storage_class &&
- fd->parent == m->lastf->parent &&
- fd->protection == m->lastf->protection &&
- fd->linkage == m->lastf->linkage)
- {
- if ( fd->fbody && !m->lastf->fbody) goto LfIsBetter;
- if (!fd->fbody && m->lastf->fbody) goto LlastIsBetter;
- }
-
- // Bugzilla 14450: Prefer exact qualified constructor for the creating object type
- if (isCtorCall && tf->mod != m->lastf->type->mod)
- {
- if (tthis->mod == tf->mod) goto LfIsBetter;
- if (tthis->mod == m->lastf->type->mod) goto LlastIsBetter;
- }
-
- m->nextf = fd;
- m->count++;
- return 0;
-
- LlastIsBetter:
- return 0;
-
- LfIsBetter:
- td_best = NULL;
- ti_best = NULL;
- ta_last = MATCHexact;
- m->last = mfa;
- m->lastf = fd;
- tthis_best = tthis_fd;
- ov_index = 0;
- m->count = 1;
- return 0;
- }
- return 0;
- }
-
- int applyTemplate(TemplateDeclaration *td)
- {
- //printf("applyTemplate()\n");
- if (td->inuse)
- {
- td->error(loc, "recursive template expansion");
- return 1;
- }
- if (td == td_best) // skip duplicates
- return 0;
-
- if (!sc)
- sc = td->_scope; // workaround for Type::aliasthisOf
-
- if (td->semanticRun == PASSinit && td->_scope)
- {
- // Try to fix forward reference. Ungag errors while doing so.
- Ungag ungag = td->ungagSpeculative();
- dsymbolSemantic(td, td->_scope);
- }
- if (td->semanticRun == PASSinit)
- {
- ::error(loc, "forward reference to template %s", td->toChars());
- Lerror:
- m->lastf = NULL;
- m->count = 0;
- m->last = MATCHnomatch;
- return 1;
- }
- //printf("td = %s\n", td->toChars());
-
- FuncDeclaration *f;
- f = td->onemember ? td->onemember->isFuncDeclaration() : NULL;
- if (!f)
- {
- if (!tiargs)
- tiargs = new Objects();
- TemplateInstance *ti = new TemplateInstance(loc, td, tiargs);
- Objects dedtypes;
- dedtypes.setDim(td->parameters->length);
- assert(td->semanticRun != PASSinit);
- MATCH mta = td->matchWithInstance(sc, ti, &dedtypes, fargs, 0);
- //printf("matchWithInstance = %d\n", mta);
- if (mta <= MATCHnomatch || mta < ta_last) // no match or less match
- return 0;
-
- templateInstanceSemantic(ti, sc, fargs);
- if (!ti->inst) // if template failed to expand
- return 0;
-
- Dsymbol *s = ti->inst->toAlias();
- FuncDeclaration *fd;
- if (TemplateDeclaration *tdx = s->isTemplateDeclaration())
- {
- Objects dedtypesX; // empty tiargs
-
- // Bugzilla 11553: Check for recursive instantiation of tdx.
- for (TemplatePrevious *p = tdx->previous; p; p = p->prev)
- {
- if (arrayObjectMatch(p->dedargs, &dedtypesX))
- {
- //printf("recursive, no match p->sc=%p %p %s\n", p->sc, this, this->toChars());
- /* It must be a subscope of p->sc, other scope chains are not recursive
- * instantiations.
- */
- for (Scope *scx = sc; scx; scx = scx->enclosing)
- {
- if (scx == p->sc)
- {
- error(loc, "recursive template expansion while looking for %s.%s", ti->toChars(), tdx->toChars());
- goto Lerror;
- }
- }
- }
- /* BUG: should also check for ref param differences
- */
- }
-
- TemplatePrevious pr;
- pr.prev = tdx->previous;
- pr.sc = sc;
- pr.dedargs = &dedtypesX;
- tdx->previous = &pr; // add this to threaded list
-
- fd = resolveFuncCall(loc, sc, s, NULL, tthis, fargs, 1);
-
- tdx->previous = pr.prev; // unlink from threaded list
- }
- else if (s->isFuncDeclaration())
- {
- fd = resolveFuncCall(loc, sc, s, NULL, tthis, fargs, 1);
- }
- else
- goto Lerror;
-
- if (!fd)
- return 0;
-
- if (fd->type->ty != Tfunction)
- {
- m->lastf = fd; // to propagate "error match"
- m->count = 1;
- m->last = MATCHnomatch;
- return 1;
- }
-
- Type *tthis_fd = fd->needThis() && !fd->isCtorDeclaration() ? tthis : NULL;
-
- TypeFunction *tf = (TypeFunction *)fd->type;
- MATCH mfa = tf->callMatch(tthis_fd, fargs);
- if (mfa < m->last)
- return 0;
-
- if (mta < ta_last) goto Ltd_best2;
- if (mta > ta_last) goto Ltd2;
-
- if (mfa < m->last) goto Ltd_best2;
- if (mfa > m->last) goto Ltd2;
-
- //printf("Lambig2\n");
- m->nextf = fd;
- m->count++;
- return 0;
-
- Ltd_best2:
- return 0;
-
- Ltd2:
- // td is the new best match
- assert(td->_scope);
- td_best = td;
- ti_best = NULL;
- property = 0; // (backward compatibility)
- ta_last = mta;
- m->last = mfa;
- m->lastf = fd;
- tthis_best = tthis_fd;
- ov_index = 0;
- m->nextf = NULL;
- m->count = 1;
- return 0;
- }
-
- //printf("td = %s\n", td->toChars());
- for (size_t ovi = 0; f; f = f->overnext0, ovi++)
- {
- if (f->type->ty != Tfunction || f->errors)
- goto Lerror;
-
- /* This is a 'dummy' instance to evaluate constraint properly.
- */
- TemplateInstance *ti = new TemplateInstance(loc, td, tiargs);
- ti->parent = td->parent; // Maybe calculating valid 'enclosing' is unnecessary.
-
- FuncDeclaration *fd = f;
- int x = td->deduceFunctionTemplateMatch(ti, sc, fd, tthis, fargs);
- MATCH mta = (MATCH)(x >> 4);
- MATCH mfa = (MATCH)(x & 0xF);
- //printf("match:t/f = %d/%d\n", mta, mfa);
- if (!fd || mfa == MATCHnomatch)
- continue;
-
- Type *tthis_fd = fd->needThis() ? tthis : NULL;
-
- bool isCtorCall = tthis_fd && fd->isCtorDeclaration();
- if (isCtorCall)
- {
- // Constructor call requires additional check.
-
- TypeFunction *tf = (TypeFunction *)fd->type;
- assert(tf->next);
- if (MODimplicitConv(tf->mod, tthis_fd->mod) ||
- (tf->isWild() && tf->isShared() == tthis_fd->isShared()) ||
- fd->isolateReturn())
- {
- tthis_fd = NULL;
- }
- else
- continue; // MATCHnomatch
- }
-
- if (mta < ta_last) goto Ltd_best;
- if (mta > ta_last) goto Ltd;
-
- if (mfa < m->last) goto Ltd_best;
- if (mfa > m->last) goto Ltd;
-
- if (td_best)
- {
- // Disambiguate by picking the most specialized TemplateDeclaration
- MATCH c1 = td->leastAsSpecialized(sc, td_best, fargs);
- MATCH c2 = td_best->leastAsSpecialized(sc, td, fargs);
- //printf("1: c1 = %d, c2 = %d\n", c1, c2);
- if (c1 > c2) goto Ltd;
- if (c1 < c2) goto Ltd_best;
- }
- assert(fd && m->lastf);
- {
- // Disambiguate by tf->callMatch
- TypeFunction *tf1 = (TypeFunction *)fd->type;
- assert(tf1->ty == Tfunction);
- TypeFunction *tf2 = (TypeFunction *)m->lastf->type;
- assert(tf2->ty == Tfunction);
- MATCH c1 = tf1->callMatch(tthis_fd, fargs);
- MATCH c2 = tf2->callMatch(tthis_best, fargs);
- //printf("2: c1 = %d, c2 = %d\n", c1, c2);
- if (c1 > c2) goto Ltd;
- if (c1 < c2) goto Ltd_best;
- }
- {
- // Disambiguate by picking the most specialized FunctionDeclaration
- MATCH c1 = fd->leastAsSpecialized(m->lastf);
- MATCH c2 = m->lastf->leastAsSpecialized(fd);
- //printf("3: c1 = %d, c2 = %d\n", c1, c2);
- if (c1 > c2) goto Ltd;
- if (c1 < c2) goto Ltd_best;
- }
-
- // Bugzilla 14450: Prefer exact qualified constructor for the creating object type
- if (isCtorCall && fd->type->mod != m->lastf->type->mod)
- {
- if (tthis->mod == fd->type->mod) goto Ltd;
- if (tthis->mod == m->lastf->type->mod) goto Ltd_best;
- }
-
- m->nextf = fd;
- m->count++;
- continue;
-
- Ltd_best: // td_best is the best match so far
- //printf("Ltd_best\n");
- continue;
-
- Ltd: // td is the new best match
- //printf("Ltd\n");
- assert(td->_scope);
- td_best = td;
- ti_best = ti;
- property = 0; // (backward compatibility)
- ta_last = mta;
- m->last = mfa;
- m->lastf = fd;
- tthis_best = tthis_fd;
- ov_index = ovi;
- m->nextf = NULL;
- m->count = 1;
- continue;
- }
- return 0;
- }
- };
- ParamDeduce p;
- // context
- p.loc = loc;
- p.sc = sc;
- p.tthis = tthis;
- p.tiargs = tiargs;
- p.fargs = fargs;
- p.pMessage = pMessage;
-
- // result
- p.m = m;
- p.property = 0;
- p.ov_index = 0;
- p.td_best = NULL;
- p.ti_best = NULL;
- p.ta_last = m->last != MATCHnomatch ? MATCHexact : MATCHnomatch;
- p.tthis_best = NULL;
-
- TemplateDeclaration *td = dstart->isTemplateDeclaration();
- if (td && td->funcroot)
- dstart = td->funcroot;
-
- overloadApply(dstart, &p, &ParamDeduce::fp);
-
- //printf("td_best = %p, m->lastf = %p\n", p.td_best, m->lastf);
- if (p.td_best && p.ti_best && m->count == 1)
- {
- // Matches to template function
- assert(p.td_best->onemember && p.td_best->onemember->isFuncDeclaration());
-
- /* The best match is td_best with arguments tdargs.
- * Now instantiate the template.
- */
- assert(p.td_best->_scope);
- if (!sc)
- sc = p.td_best->_scope; // workaround for Type::aliasthisOf
-
- TemplateInstance *ti = new TemplateInstance(loc, p.td_best, p.ti_best->tiargs);
- templateInstanceSemantic(ti, sc, fargs);
-
- m->lastf = ti->toAlias()->isFuncDeclaration();
- if (!m->lastf)
- goto Lnomatch;
- if (ti->errors)
- {
- Lerror:
- m->count = 1;
- assert(m->lastf);
- m->last = MATCHnomatch;
- return;
- }
-
- // look forward instantiated overload function
- // Dsymbol::oneMembers is alredy called in TemplateInstance::semantic.
- // it has filled overnext0d
- while (p.ov_index--)
- {
- m->lastf = m->lastf->overnext0;
- assert(m->lastf);
- }
-
- p.tthis_best = m->lastf->needThis() && !m->lastf->isCtorDeclaration() ? tthis : NULL;
-
- TypeFunction *tf = (TypeFunction *)m->lastf->type;
- if (tf->ty == Terror)
- goto Lerror;
- assert(tf->ty == Tfunction);
- if (!tf->callMatch(p.tthis_best, fargs))
- goto Lnomatch;
-
- /* As Bugzilla 3682 shows, a template instance can be matched while instantiating
- * that same template. Thus, the function type can be incomplete. Complete it.
- *
- * Bugzilla 9208: For auto function, completion should be deferred to the end of
- * its semantic3. Should not complete it in here.
- */
- if (tf->next && !m->lastf->inferRetType)
- {
- m->lastf->type = typeSemantic(tf, loc, sc);
- }
- }
- else if (m->lastf)
- {
- // Matches to non template function,
- // or found matches were ambiguous.
- assert(m->count >= 1);
- }
- else
- {
- Lnomatch:
- m->count = 0;
- m->lastf = NULL;
- m->last = MATCHnomatch;
- }
-}
-
-/*************************************************
- * Limited function template instantiation for using fd->leastAsSpecialized()
- */
-FuncDeclaration *TemplateDeclaration::doHeaderInstantiation(
- TemplateInstance *ti, Scope *sc2,
- FuncDeclaration *fd, Type *tthis, Expressions *fargs)
-{
- assert(fd);
-
- // function body and contracts are not need
- if (fd->isCtorDeclaration())
- fd = new CtorDeclaration(fd->loc, fd->endloc, fd->storage_class, fd->type->syntaxCopy());
- else
- fd = new FuncDeclaration(fd->loc, fd->endloc, fd->ident, fd->storage_class, fd->type->syntaxCopy());
- fd->parent = ti;
-
- assert(fd->type->ty == Tfunction);
- TypeFunction *tf = (TypeFunction *)fd->type;
- tf->fargs = fargs;
-
- if (tthis)
- {
- // Match 'tthis' to any TemplateThisParameter's
- bool hasttp = false;
- for (size_t i = 0; i < parameters->length; i++)
- {
- TemplateParameter *tp = (*parameters)[i];
- TemplateThisParameter *ttp = tp->isTemplateThisParameter();
- if (ttp)
- hasttp = true;
- }
- if (hasttp)
- {
- tf = (TypeFunction *)tf->addSTC(ModToStc(tthis->mod));
- assert(!tf->deco);
- }
- }
-
- Scope *scx = sc2->push();
-
- // Shouldn't run semantic on default arguments and return type.
- for (size_t i = 0; i < tf->parameterList.parameters->length; i++)
- (*tf->parameterList.parameters)[i]->defaultArg = NULL;
- if (fd->isCtorDeclaration())
- {
- // For constructors, emitting return type is necessary for
- // isolateReturn() in functionResolve.
- scx->flags |= SCOPEctor;
-
- Dsymbol *parent = toParent2();
- Type *tret;
- AggregateDeclaration *ad = parent->isAggregateDeclaration();
- if (!ad || parent->isUnionDeclaration())
- {
- tret = Type::tvoid;
- }
- else
- {
- tret = ad->handleType();
- assert(tret);
- tret = tret->addStorageClass(fd->storage_class | scx->stc);
- tret = tret->addMod(tf->mod);
- }
- tf->next = tret;
- if (ad && ad->isStructDeclaration())
- tf->isref = 1;
- //printf("tf = %s\n", tf->toChars());
- }
- else
- tf->next = NULL;
- fd->type = tf;
- fd->type = fd->type->addSTC(scx->stc);
- fd->type = typeSemantic(fd->type, fd->loc, scx);
- scx = scx->pop();
-
- if (fd->type->ty != Tfunction)
- return NULL;
-
- fd->originalType = fd->type; // for mangling
- //printf("\t[%s] fd->type = %s, mod = %x, ", loc.toChars(), fd->type->toChars(), fd->type->mod);
- //printf("fd->needThis() = %d\n", fd->needThis());
-
- return fd;
-}
-
-bool TemplateDeclaration::hasStaticCtorOrDtor()
-{
- return false; // don't scan uninstantiated templates
-}
-
-const char *TemplateDeclaration::toChars()
-{
- if (literal)
- return Dsymbol::toChars();
-
- OutBuffer buf;
- HdrGenState hgs;
-
- buf.writestring(ident->toChars());
- buf.writeByte('(');
- for (size_t i = 0; i < parameters->length; i++)
- {
- TemplateParameter *tp = (*parameters)[i];
- if (i)
- buf.writestring(", ");
- ::toCBuffer(tp, &buf, &hgs);
- }
- buf.writeByte(')');
-
- if (onemember)
- {
- FuncDeclaration *fd = onemember->isFuncDeclaration();
- if (fd && fd->type)
- {
- TypeFunction *tf = (TypeFunction *)fd->type;
- buf.writestring(parametersTypeToChars(tf->parameterList));
- }
- }
-
- if (constraint)
- {
- buf.writestring(" if (");
- ::toCBuffer(constraint, &buf, &hgs);
- buf.writeByte(')');
- }
- return buf.extractChars();
-}
-
-Prot TemplateDeclaration::prot()
-{
- return protection;
-}
-
-/****************************************************
- * Given a new instance tithis of this TemplateDeclaration,
- * see if there already exists an instance.
- * If so, return that existing instance.
- */
-
-TemplateInstance *TemplateDeclaration::findExistingInstance(TemplateInstance *tithis, Expressions *fargs)
-{
- //printf("findExistingInstance(%p)\n", tithis);
- tithis->fargs = fargs;
- TemplateInstances *tinstances = (TemplateInstances *)dmd_aaGetRvalue((AA *)instances, (void *)tithis->toHash());
- if (tinstances)
- {
- for (size_t i = 0; i < tinstances->length; i++)
- {
- TemplateInstance *ti = (*tinstances)[i];
- if (tithis->compare(ti) == 0)
- return ti;
- }
- }
- return NULL;
-}
-
-/********************************************
- * Add instance ti to TemplateDeclaration's table of instances.
- * Return a handle we can use to later remove it if it fails instantiation.
- */
-
-TemplateInstance *TemplateDeclaration::addInstance(TemplateInstance *ti)
-{
- //printf("addInstance() %p %p\n", instances, ti);
- TemplateInstances **ptinstances = (TemplateInstances **)dmd_aaGet((AA **)&instances, (void *)ti->toHash());
- if (!*ptinstances)
- *ptinstances = new TemplateInstances();
- (*ptinstances)->push(ti);
- return ti;
-}
-
-/*******************************************
- * Remove TemplateInstance from table of instances.
- * Input:
- * handle returned by addInstance()
- */
-
-void TemplateDeclaration::removeInstance(TemplateInstance *handle)
-{
- //printf("removeInstance()\n");
- TemplateInstances *tinstances = (TemplateInstances *)dmd_aaGetRvalue((AA *)instances, (void *)handle->toHash());
- if (tinstances)
- {
- for (size_t i = 0; i < tinstances->length; i++)
- {
- TemplateInstance *ti = (*tinstances)[i];
- if (handle == ti)
- {
- tinstances->remove(i);
- break;
- }
- }
- }
-}
-
-/* ======================== Type ============================================ */
-
-/****
- * Given an identifier, figure out which TemplateParameter it is.
- * Return IDX_NOTFOUND if not found.
- */
-
-static size_t templateIdentifierLookup(Identifier *id, TemplateParameters *parameters)
-{
- for (size_t i = 0; i < parameters->length; i++)
- {
- TemplateParameter *tp = (*parameters)[i];
- if (tp->ident->equals(id))
- return i;
- }
- return IDX_NOTFOUND;
-}
-
-size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters)
-{
- if (tparam->ty == Tident)
- {
- TypeIdentifier *tident = (TypeIdentifier *)tparam;
- //printf("\ttident = '%s'\n", tident->toChars());
- return templateIdentifierLookup(tident->ident, parameters);
- }
- return IDX_NOTFOUND;
-}
-
-unsigned char deduceWildHelper(Type *t, Type **at, Type *tparam)
-{
- if ((tparam->mod & MODwild) == 0)
- return 0;
-
- *at = NULL;
-
- #define X(U,T) ((U) << 4) | (T)
- switch (X(tparam->mod, t->mod))
- {
- case X(MODwild, 0):
- case X(MODwild, MODconst):
- case X(MODwild, MODshared):
- case X(MODwild, MODshared | MODconst):
- case X(MODwild, MODimmutable):
- case X(MODwildconst, 0):
- case X(MODwildconst, MODconst):
- case X(MODwildconst, MODshared):
- case X(MODwildconst, MODshared | MODconst):
- case X(MODwildconst, MODimmutable):
- case X(MODshared | MODwild, MODshared):
- case X(MODshared | MODwild, MODshared | MODconst):
- case X(MODshared | MODwild, MODimmutable):
- case X(MODshared | MODwildconst, MODshared):
- case X(MODshared | MODwildconst, MODshared | MODconst):
- case X(MODshared | MODwildconst, MODimmutable):
- {
- unsigned char wm = (t->mod & ~MODshared);
- if (wm == 0)
- wm = MODmutable;
- unsigned char m = (t->mod & (MODconst | MODimmutable)) | (tparam->mod & t->mod & MODshared);
- *at = t->unqualify(m);
- return wm;
- }
-
- case X(MODwild, MODwild):
- case X(MODwild, MODwildconst):
- case X(MODwild, MODshared | MODwild):
- case X(MODwild, MODshared | MODwildconst):
- case X(MODwildconst, MODwild):
- case X(MODwildconst, MODwildconst):
- case X(MODwildconst, MODshared | MODwild):
- case X(MODwildconst, MODshared | MODwildconst):
- case X(MODshared | MODwild, MODshared | MODwild):
- case X(MODshared | MODwild, MODshared | MODwildconst):
- case X(MODshared | MODwildconst, MODshared | MODwild):
- case X(MODshared | MODwildconst, MODshared | MODwildconst):
- {
- *at = t->unqualify(tparam->mod & t->mod);
- return MODwild;
- }
-
- default:
- return 0;
- }
- #undef X
-}
-
-MATCH deduceTypeHelper(Type *t, Type **at, Type *tparam)
-{
- // 9*9 == 81 cases
-
- #define X(U,T) ((U) << 4) | (T)
- switch (X(tparam->mod, t->mod))
- {
- case X(0, 0):
- case X(0, MODconst):
- case X(0, MODwild):
- case X(0, MODwildconst):
- case X(0, MODshared):
- case X(0, MODshared | MODconst):
- case X(0, MODshared | MODwild):
- case X(0, MODshared | MODwildconst):
- case X(0, MODimmutable):
- // foo(U) T => T
- // foo(U) const(T) => const(T)
- // foo(U) inout(T) => inout(T)
- // foo(U) inout(const(T)) => inout(const(T))
- // foo(U) shared(T) => shared(T)
- // foo(U) shared(const(T)) => shared(const(T))
- // foo(U) shared(inout(T)) => shared(inout(T))
- // foo(U) shared(inout(const(T))) => shared(inout(const(T)))
- // foo(U) immutable(T) => immutable(T)
- {
- *at = t;
- return MATCHexact;
- }
-
- case X(MODconst, MODconst):
- case X(MODwild, MODwild):
- case X(MODwildconst, MODwildconst):
- case X(MODshared, MODshared):
- case X(MODshared | MODconst, MODshared | MODconst):
- case X(MODshared | MODwild, MODshared | MODwild):
- case X(MODshared | MODwildconst, MODshared | MODwildconst):
- case X(MODimmutable, MODimmutable):
- // foo(const(U)) const(T) => T
- // foo(inout(U)) inout(T) => T
- // foo(inout(const(U))) inout(const(T)) => T
- // foo(shared(U)) shared(T) => T
- // foo(shared(const(U))) shared(const(T)) => T
- // foo(shared(inout(U))) shared(inout(T)) => T
- // foo(shared(inout(const(U)))) shared(inout(const(T))) => T
- // foo(immutable(U)) immutable(T) => T
- {
- *at = t->mutableOf()->unSharedOf();
- return MATCHexact;
- }
-
- case X(MODconst, 0):
- case X(MODconst, MODwild):
- case X(MODconst, MODwildconst):
- case X(MODconst, MODshared | MODconst):
- case X(MODconst, MODshared | MODwild):
- case X(MODconst, MODshared | MODwildconst):
- case X(MODconst, MODimmutable):
- case X(MODwild, MODshared | MODwild):
- case X(MODwildconst, MODshared | MODwildconst):
- case X(MODshared | MODconst, MODimmutable):
- // foo(const(U)) T => T
- // foo(const(U)) inout(T) => T
- // foo(const(U)) inout(const(T)) => T
- // foo(const(U)) shared(const(T)) => shared(T)
- // foo(const(U)) shared(inout(T)) => shared(T)
- // foo(const(U)) shared(inout(const(T))) => shared(T)
- // foo(const(U)) immutable(T) => T
- // foo(inout(U)) shared(inout(T)) => shared(T)
- // foo(inout(const(U))) shared(inout(const(T))) => shared(T)
- // foo(shared(const(U))) immutable(T) => T
- {
- *at = t->mutableOf();
- return MATCHconst;
- }
-
- case X(MODconst, MODshared):
- // foo(const(U)) shared(T) => shared(T)
- {
- *at = t;
- return MATCHconst;
- }
-
- case X(MODshared, MODshared | MODconst):
- case X(MODshared, MODshared | MODwild):
- case X(MODshared, MODshared | MODwildconst):
- case X(MODshared | MODconst, MODshared):
- // foo(shared(U)) shared(const(T)) => const(T)
- // foo(shared(U)) shared(inout(T)) => inout(T)
- // foo(shared(U)) shared(inout(const(T))) => inout(const(T))
- // foo(shared(const(U))) shared(T) => T
- {
- *at = t->unSharedOf();
- return MATCHconst;
- }
-
- case X(MODwildconst, MODimmutable):
- case X(MODshared | MODconst, MODshared | MODwildconst):
- case X(MODshared | MODwildconst, MODimmutable):
- case X(MODshared | MODwildconst, MODshared | MODwild):
- // foo(inout(const(U))) immutable(T) => T
- // foo(shared(const(U))) shared(inout(const(T))) => T
- // foo(shared(inout(const(U)))) immutable(T) => T
- // foo(shared(inout(const(U)))) shared(inout(T)) => T
- {
- *at = t->unSharedOf()->mutableOf();
- return MATCHconst;
- }
-
- case X(MODshared | MODconst, MODshared | MODwild):
- // foo(shared(const(U))) shared(inout(T)) => T
- {
- *at = t->unSharedOf()->mutableOf();
- return MATCHconst;
- }
-
- case X(MODwild, 0):
- case X(MODwild, MODconst):
- case X(MODwild, MODwildconst):
- case X(MODwild, MODimmutable):
- case X(MODwild, MODshared):
- case X(MODwild, MODshared | MODconst):
- case X(MODwild, MODshared | MODwildconst):
- case X(MODwildconst, 0):
- case X(MODwildconst, MODconst):
- case X(MODwildconst, MODwild):
- case X(MODwildconst, MODshared):
- case X(MODwildconst, MODshared | MODconst):
- case X(MODwildconst, MODshared | MODwild):
- case X(MODshared, 0):
- case X(MODshared, MODconst):
- case X(MODshared, MODwild):
- case X(MODshared, MODwildconst):
- case X(MODshared, MODimmutable):
- case X(MODshared | MODconst, 0):
- case X(MODshared | MODconst, MODconst):
- case X(MODshared | MODconst, MODwild):
- case X(MODshared | MODconst, MODwildconst):
- case X(MODshared | MODwild, 0):
- case X(MODshared | MODwild, MODconst):
- case X(MODshared | MODwild, MODwild):
- case X(MODshared | MODwild, MODwildconst):
- case X(MODshared | MODwild, MODimmutable):
- case X(MODshared | MODwild, MODshared):
- case X(MODshared | MODwild, MODshared | MODconst):
- case X(MODshared | MODwild, MODshared | MODwildconst):
- case X(MODshared | MODwildconst, 0):
- case X(MODshared | MODwildconst, MODconst):
- case X(MODshared | MODwildconst, MODwild):
- case X(MODshared | MODwildconst, MODwildconst):
- case X(MODshared | MODwildconst, MODshared):
- case X(MODshared | MODwildconst, MODshared | MODconst):
- case X(MODimmutable, 0):
- case X(MODimmutable, MODconst):
- case X(MODimmutable, MODwild):
- case X(MODimmutable, MODwildconst):
- case X(MODimmutable, MODshared):
- case X(MODimmutable, MODshared | MODconst):
- case X(MODimmutable, MODshared | MODwild):
- case X(MODimmutable, MODshared | MODwildconst):
- // foo(inout(U)) T => nomatch
- // foo(inout(U)) const(T) => nomatch
- // foo(inout(U)) inout(const(T)) => nomatch
- // foo(inout(U)) immutable(T) => nomatch
- // foo(inout(U)) shared(T) => nomatch
- // foo(inout(U)) shared(const(T)) => nomatch
- // foo(inout(U)) shared(inout(const(T))) => nomatch
- // foo(inout(const(U))) T => nomatch
- // foo(inout(const(U))) const(T) => nomatch
- // foo(inout(const(U))) inout(T) => nomatch
- // foo(inout(const(U))) shared(T) => nomatch
- // foo(inout(const(U))) shared(const(T)) => nomatch
- // foo(inout(const(U))) shared(inout(T)) => nomatch
- // foo(shared(U)) T => nomatch
- // foo(shared(U)) const(T) => nomatch
- // foo(shared(U)) inout(T) => nomatch
- // foo(shared(U)) inout(const(T)) => nomatch
- // foo(shared(U)) immutable(T) => nomatch
- // foo(shared(const(U))) T => nomatch
- // foo(shared(const(U))) const(T) => nomatch
- // foo(shared(const(U))) inout(T) => nomatch
- // foo(shared(const(U))) inout(const(T)) => nomatch
- // foo(shared(inout(U))) T => nomatch
- // foo(shared(inout(U))) const(T) => nomatch
- // foo(shared(inout(U))) inout(T) => nomatch
- // foo(shared(inout(U))) inout(const(T)) => nomatch
- // foo(shared(inout(U))) immutable(T) => nomatch
- // foo(shared(inout(U))) shared(T) => nomatch
- // foo(shared(inout(U))) shared(const(T)) => nomatch
- // foo(shared(inout(U))) shared(inout(const(T))) => nomatch
- // foo(shared(inout(const(U)))) T => nomatch
- // foo(shared(inout(const(U)))) const(T) => nomatch
- // foo(shared(inout(const(U)))) inout(T) => nomatch
- // foo(shared(inout(const(U)))) inout(const(T)) => nomatch
- // foo(shared(inout(const(U)))) shared(T) => nomatch
- // foo(shared(inout(const(U)))) shared(const(T)) => nomatch
- // foo(immutable(U)) T => nomatch
- // foo(immutable(U)) const(T) => nomatch
- // foo(immutable(U)) inout(T) => nomatch
- // foo(immutable(U)) inout(const(T)) => nomatch
- // foo(immutable(U)) shared(T) => nomatch
- // foo(immutable(U)) shared(const(T)) => nomatch
- // foo(immutable(U)) shared(inout(T)) => nomatch
- // foo(immutable(U)) shared(inout(const(T))) => nomatch
- return MATCHnomatch;
-
- default:
- assert(0);
- return MATCHnomatch; // silence compiler warning about missing return
- }
- #undef X
-}
-
-/* These form the heart of template argument deduction.
- * Given 'this' being the type argument to the template instance,
- * it is matched against the template declaration parameter specialization
- * 'tparam' to determine the type to be used for the parameter.
- * Example:
- * template Foo(T:T*) // template declaration
- * Foo!(int*) // template instantiation
- * Input:
- * this = int*
- * tparam = T*
- * parameters = [ T:T* ] // Array of TemplateParameter's
- * Output:
- * dedtypes = [ int ] // Array of Expression/Type's
- */
-MATCH deduceType(RootObject *o, Scope *sc, Type *tparam, TemplateParameters *parameters,
- Objects *dedtypes, unsigned *wm, size_t inferStart)
-{
- class DeduceType : public Visitor
- {
- public:
- Scope *sc;
- Type *tparam;
- TemplateParameters *parameters;
- Objects *dedtypes;
- unsigned *wm;
- size_t inferStart;
- MATCH result;
-
- DeduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm, size_t inferStart)
- : sc(sc), tparam(tparam), parameters(parameters), dedtypes(dedtypes), wm(wm), inferStart(inferStart)
- {
- result = MATCHnomatch;
- }
-
- void visit(Type *t)
- {
- if (!tparam)
- goto Lnomatch;
-
- if (t == tparam)
- goto Lexact;
-
- if (tparam->ty == Tident)
- {
- // Determine which parameter tparam is
- size_t i = templateParameterLookup(tparam, parameters);
- if (i == IDX_NOTFOUND)
- {
- if (!sc)
- goto Lnomatch;
-
- /* Need a loc to go with the semantic routine.
- */
- Loc loc;
- if (parameters->length)
- {
- TemplateParameter *tp = (*parameters)[0];
- loc = tp->loc;
- }
-
- /* BUG: what if tparam is a template instance, that
- * has as an argument another Tident?
- */
- tparam = typeSemantic(tparam, loc, sc);
- assert(tparam->ty != Tident);
- result = deduceType(t, sc, tparam, parameters, dedtypes, wm);
- return;
- }
-
- TemplateParameter *tp = (*parameters)[i];
-
- TypeIdentifier *tident = (TypeIdentifier *)tparam;
- if (tident->idents.length > 0)
- {
- //printf("matching %s to %s\n", tparam->toChars(), t->toChars());
- Dsymbol *s = t->toDsymbol(sc);
- for (size_t j = tident->idents.length; j-- > 0; )
- {
- RootObject *id = tident->idents[j];
- if (id->dyncast() == DYNCAST_IDENTIFIER)
- {
- if (!s || !s->parent)
- goto Lnomatch;
- Dsymbol *s2 = s->parent->search(Loc(), (Identifier *)id);
- if (!s2)
- goto Lnomatch;
- s2 = s2->toAlias();
- //printf("[%d] s = %s %s, s2 = %s %s\n", j, s->kind(), s->toChars(), s2->kind(), s2->toChars());
- if (s != s2)
- {
- if (Type *tx = s2->getType())
- {
- if (s != tx->toDsymbol(sc))
- goto Lnomatch;
- }
- else
- goto Lnomatch;
- }
- s = s->parent;
- }
- else
- goto Lnomatch;
- }
- //printf("[e] s = %s\n", s?s->toChars():"(null)");
- if (tp->isTemplateTypeParameter())
- {
- Type *tt = s->getType();
- if (!tt)
- goto Lnomatch;
- Type *at = (Type *)(*dedtypes)[i];
- if (at && at->ty == Tnone)
- at = ((TypeDeduced *)at)->tded;
- if (!at || tt->equals(at))
- {
- (*dedtypes)[i] = tt;
- goto Lexact;
- }
- }
- if (tp->isTemplateAliasParameter())
- {
- Dsymbol *s2 = (Dsymbol *)(*dedtypes)[i];
- if (!s2 || s == s2)
- {
- (*dedtypes)[i] = s;
- goto Lexact;
- }
- }
- goto Lnomatch;
- }
-
- // Found the corresponding parameter tp
- if (!tp->isTemplateTypeParameter())
- goto Lnomatch;
-
- Type *at = (Type *)(*dedtypes)[i];
- Type *tt;
- if (unsigned char wx = wm ? deduceWildHelper(t, &tt, tparam) : 0)
- {
- // type vs (none)
- if (!at)
- {
- (*dedtypes)[i] = tt;
- *wm |= wx;
- result = MATCHconst;
- return;
- }
-
- // type vs expressions
- if (at->ty == Tnone)
- {
- TypeDeduced *xt = (TypeDeduced *)at;
- result = xt->matchAll(tt);
- if (result > MATCHnomatch)
- {
- (*dedtypes)[i] = tt;
- if (result > MATCHconst)
- result = MATCHconst; // limit level for inout matches
- delete xt;
- }
- return;
- }
-
- // type vs type
- if (tt->equals(at))
- {
- (*dedtypes)[i] = tt; // Prefer current type match
- goto Lconst;
- }
- if (tt->implicitConvTo(at->constOf()))
- {
- (*dedtypes)[i] = at->constOf()->mutableOf();
- *wm |= MODconst;
- goto Lconst;
- }
- if (at->implicitConvTo(tt->constOf()))
- {
- (*dedtypes)[i] = tt->constOf()->mutableOf();
- *wm |= MODconst;
- goto Lconst;
- }
- goto Lnomatch;
- }
- else if (MATCH m = deduceTypeHelper(t, &tt, tparam))
- {
- // type vs (none)
- if (!at)
- {
- (*dedtypes)[i] = tt;
- result = m;
- return;
- }
-
- // type vs expressions
- if (at->ty == Tnone)
- {
- TypeDeduced *xt = (TypeDeduced *)at;
- result = xt->matchAll(tt);
- if (result > MATCHnomatch)
- {
- (*dedtypes)[i] = tt;
- delete xt;
- }
- return;
- }
-
- // type vs type
- if (tt->equals(at))
- {
- goto Lexact;
- }
- if (tt->ty == Tclass && at->ty == Tclass)
- {
- result = tt->implicitConvTo(at);
- return;
- }
- if (tt->ty == Tsarray && at->ty == Tarray &&
- tt->nextOf()->implicitConvTo(at->nextOf()) >= MATCHconst)
- {
- goto Lexact;
- }
- }
- goto Lnomatch;
- }
-
- if (tparam->ty == Ttypeof)
- {
- /* Need a loc to go with the semantic routine.
- */
- Loc loc;
- if (parameters->length)
- {
- TemplateParameter *tp = (*parameters)[0];
- loc = tp->loc;
- }
-
- tparam = typeSemantic(tparam, loc, sc);
- }
- if (t->ty != tparam->ty)
- {
- if (Dsymbol *sym = t->toDsymbol(sc))
- {
- if (sym->isforwardRef() && !tparam->deco)
- goto Lnomatch;
- }
-
- MATCH m = t->implicitConvTo(tparam);
- if (m == MATCHnomatch)
- {
- if (t->ty == Tclass)
- {
- TypeClass *tc = (TypeClass *)t;
- if (tc->sym->aliasthis && !(tc->att & RECtracingDT))
- {
- tc->att = (AliasThisRec)(tc->att | RECtracingDT);
- m = deduceType(t->aliasthisOf(), sc, tparam, parameters, dedtypes, wm);
- tc->att = (AliasThisRec)(tc->att & ~RECtracingDT);
- }
- }
- else if (t->ty == Tstruct)
- {
- TypeStruct *ts = (TypeStruct *)t;
- if (ts->sym->aliasthis && !(ts->att & RECtracingDT))
- {
- ts->att = (AliasThisRec)(ts->att | RECtracingDT);
- m = deduceType(t->aliasthisOf(), sc, tparam, parameters, dedtypes, wm);
- ts->att = (AliasThisRec)(ts->att & ~RECtracingDT);
- }
- }
- }
- result = m;
- return;
- }
-
- if (t->nextOf())
- {
- if (tparam->deco && !tparam->hasWild())
- {
- result = t->implicitConvTo(tparam);
- return;
- }
-
- Type *tpn = tparam->nextOf();
- if (wm && t->ty == Taarray && tparam->isWild())
- {
- // Bugzilla 12403: In IFTI, stop inout matching on transitive part of AA types.
- tpn = tpn->substWildTo(MODmutable);
- }
-
- result = deduceType(t->nextOf(), sc, tpn, parameters, dedtypes, wm);
- return;
- }
-
- Lexact:
- result = MATCHexact;
- return;
-
- Lnomatch:
- result = MATCHnomatch;
- return;
-
- Lconst:
- result = MATCHconst;
- }
-
- void visit(TypeVector *t)
- {
- if (tparam->ty == Tvector)
- {
- TypeVector *tp = (TypeVector *)tparam;
- result = deduceType(t->basetype, sc, tp->basetype, parameters, dedtypes, wm);
- return;
- }
- visit((Type *)t);
- }
-
- void visit(TypeDArray *t)
- {
- visit((Type *)t);
- }
-
- void visit(TypeSArray *t)
- {
- // Extra check that array dimensions must match
- if (tparam)
- {
- if (tparam->ty == Tarray)
- {
- MATCH m = deduceType(t->next, sc, tparam->nextOf(), parameters, dedtypes, wm);
- result = (m >= MATCHconst) ? MATCHconvert : MATCHnomatch;
- return;
- }
-
- TemplateParameter *tp = NULL;
- Expression *edim = NULL;
- size_t i;
- if (tparam->ty == Tsarray)
- {
- TypeSArray *tsa = (TypeSArray *)tparam;
- if (tsa->dim->op == TOKvar &&
- ((VarExp *)tsa->dim)->var->storage_class & STCtemplateparameter)
- {
- Identifier *id = ((VarExp *)tsa->dim)->var->ident;
- i = templateIdentifierLookup(id, parameters);
- assert(i != IDX_NOTFOUND);
- tp = (*parameters)[i];
- }
- else
- edim = tsa->dim;
- }
- else if (tparam->ty == Taarray)
- {
- TypeAArray *taa = (TypeAArray *)tparam;
- i = templateParameterLookup(taa->index, parameters);
- if (i != IDX_NOTFOUND)
- tp = (*parameters)[i];
- else
- {
- Expression *e;
- Type *tx;
- Dsymbol *s;
- taa->index->resolve(Loc(), sc, &e, &tx, &s);
- edim = s ? getValue(s) : getValue(e);
- }
- }
- if ((tp && tp->matchArg(sc, t->dim, i, parameters, dedtypes, NULL)) ||
- (edim && edim->toInteger() == t->dim->toInteger()))
- {
- result = deduceType(t->next, sc, tparam->nextOf(), parameters, dedtypes, wm);
- return;
- }
- }
- visit((Type *)t);
- return;
-
- result = MATCHnomatch;
- }
-
- void visit(TypeAArray *t)
- {
- // Extra check that index type must match
- if (tparam && tparam->ty == Taarray)
- {
- TypeAArray *tp = (TypeAArray *)tparam;
- if (!deduceType(t->index, sc, tp->index, parameters, dedtypes))
- {
- result = MATCHnomatch;
- return;
- }
- }
- visit((Type *)t);
- }
-
- void visit(TypeFunction *t)
- {
- //printf("TypeFunction::deduceType()\n");
- //printf("\tthis = %d, ", t->ty); t->print();
- //printf("\ttparam = %d, ", tparam->ty); tparam->print();
-
- // Extra check that function characteristics must match
- if (tparam && tparam->ty == Tfunction)
- {
- TypeFunction *tp = (TypeFunction *)tparam;
- if (t->parameterList.varargs != tp->parameterList.varargs ||
- t->linkage != tp->linkage)
- {
- result = MATCHnomatch;
- return;
- }
-
- size_t nfargs = t->parameterList.length();
- size_t nfparams = tp->parameterList.length();
-
- // bug 2579 fix: Apply function parameter storage classes to parameter types
- for (size_t i = 0; i < nfparams; i++)
- {
- Parameter *fparam = tp->parameterList[i];
- fparam->type = fparam->type->addStorageClass(fparam->storageClass);
- fparam->storageClass &= ~(STC_TYPECTOR | STCin);
- }
- //printf("\t-> this = %d, ", t->ty); t->print();
- //printf("\t-> tparam = %d, ", tparam->ty); tparam->print();
-
- /* See if tuple match
- */
- if (nfparams > 0 && nfargs >= nfparams - 1)
- {
- /* See if 'A' of the template parameter matches 'A'
- * of the type of the last function parameter.
- */
- Parameter *fparam = tp->parameterList[nfparams - 1];
- assert(fparam);
- assert(fparam->type);
- if (fparam->type->ty != Tident)
- goto L1;
- TypeIdentifier *tid = (TypeIdentifier *)fparam->type;
- if (tid->idents.length)
- goto L1;
-
- /* Look through parameters to find tuple matching tid->ident
- */
- size_t tupi = 0;
- for (; 1; tupi++)
- {
- if (tupi == parameters->length)
- goto L1;
- TemplateParameter *tx = (*parameters)[tupi];
- TemplateTupleParameter *tup = tx->isTemplateTupleParameter();
- if (tup && tup->ident->equals(tid->ident))
- break;
- }
-
- /* The types of the function arguments [nfparams - 1 .. nfargs]
- * now form the tuple argument.
- */
- size_t tuple_dim = nfargs - (nfparams - 1);
-
- /* See if existing tuple, and whether it matches or not
- */
- RootObject *o = (*dedtypes)[tupi];
- if (o)
- {
- // Existing deduced argument must be a tuple, and must match
- Tuple *tup = isTuple(o);
- if (!tup || tup->objects.length != tuple_dim)
- {
- result = MATCHnomatch;
- return;
- }
- for (size_t i = 0; i < tuple_dim; i++)
- {
- Parameter *arg = t->parameterList[nfparams - 1 + i];
- if (!arg->type->equals(tup->objects[i]))
- {
- result = MATCHnomatch;
- return;
- }
- }
- }
- else
- {
- // Create new tuple
- Tuple *tup = new Tuple();
- tup->objects.setDim(tuple_dim);
- for (size_t i = 0; i < tuple_dim; i++)
- {
- Parameter *arg = t->parameterList[nfparams - 1 + i];
- tup->objects[i] = arg->type;
- }
- (*dedtypes)[tupi] = tup;
- }
- nfparams--; // don't consider the last parameter for type deduction
- goto L2;
- }
-
- L1:
- if (nfargs != nfparams)
- {
- result = MATCHnomatch;
- return;
- }
- L2:
- for (size_t i = 0; i < nfparams; i++)
- {
- Parameter *a = t->parameterList[i];
- Parameter *ap = tp->parameterList[i];
-
- if (!a->isCovariant(t->isref, ap) ||
- !deduceType(a->type, sc, ap->type, parameters, dedtypes))
- {
- result = MATCHnomatch;
- return;
- }
- }
- }
- visit((Type *)t);
- }
-
- void visit(TypeIdentifier *t)
- {
- // Extra check
- if (tparam && tparam->ty == Tident)
- {
- TypeIdentifier *tp = (TypeIdentifier *)tparam;
-
- for (size_t i = 0; i < t->idents.length; i++)
- {
- RootObject *id1 = t->idents[i];
- RootObject *id2 = tp->idents[i];
-
- if (!id1->equals(id2))
- {
- result = MATCHnomatch;
- return;
- }
- }
- }
- visit((Type *)t);
- }
-
- void visit(TypeInstance *t)
- {
- // Extra check
- if (tparam && tparam->ty == Tinstance && t->tempinst->tempdecl)
- {
- TemplateDeclaration *tempdecl = t->tempinst->tempdecl->isTemplateDeclaration();
- assert(tempdecl);
-
- TypeInstance *tp = (TypeInstance *)tparam;
-
- //printf("tempinst->tempdecl = %p\n", tempdecl);
- //printf("tp->tempinst->tempdecl = %p\n", tp->tempinst->tempdecl);
- if (!tp->tempinst->tempdecl)
- {
- //printf("tp->tempinst->name = '%s'\n", tp->tempinst->name->toChars());
-
- /* Handle case of:
- * template Foo(T : sa!(T), alias sa)
- */
- size_t i = templateIdentifierLookup(tp->tempinst->name, parameters);
- if (i == IDX_NOTFOUND)
- {
- /* Didn't find it as a parameter identifier. Try looking
- * it up and seeing if is an alias. See Bugzilla 1454
- */
- TypeIdentifier *tid = new TypeIdentifier(tp->loc, tp->tempinst->name);
- Type *tx;
- Expression *e;
- Dsymbol *s;
- tid->resolve(tp->loc, sc, &e, &tx, &s);
- if (tx)
- {
- s = tx->toDsymbol(sc);
- if (TemplateInstance *ti = s ? s->parent->isTemplateInstance() : NULL)
- {
- // Bugzilla 14290: Try to match with ti->tempecl,
- // only when ti is an enclosing instance.
- Dsymbol *p = sc->parent;
- while (p && p != ti)
- p = p->parent;
- if (p)
- s = ti->tempdecl;
- }
- }
- if (s)
- {
- s = s->toAlias();
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (td)
- {
- if (td->overroot)
- td = td->overroot;
- for (; td; td = td->overnext)
- {
- if (td == tempdecl)
- goto L2;
- }
- }
- }
- goto Lnomatch;
- }
- TemplateParameter *tpx = (*parameters)[i];
- if (!tpx->matchArg(sc, tempdecl, i, parameters, dedtypes, NULL))
- goto Lnomatch;
- }
- else if (tempdecl != tp->tempinst->tempdecl)
- goto Lnomatch;
-
- L2:
-
- for (size_t i = 0; 1; i++)
- {
- //printf("\ttest: tempinst->tiargs[%d]\n", i);
- RootObject *o1 = NULL;
- if (i < t->tempinst->tiargs->length)
- o1 = (*t->tempinst->tiargs)[i];
- else if (i < t->tempinst->tdtypes.length && i < tp->tempinst->tiargs->length)
- {
- // Pick up default arg
- o1 = t->tempinst->tdtypes[i];
- }
- else if (i >= tp->tempinst->tiargs->length)
- break;
-
- if (i >= tp->tempinst->tiargs->length)
- {
- size_t dim = tempdecl->parameters->length - (tempdecl->isVariadic() ? 1 : 0);
- while (i < dim && ((*tempdecl->parameters)[i]->dependent ||
- (*tempdecl->parameters)[i]->hasDefaultArg()))
- {
- i++;
- }
- if (i >= dim)
- break; // match if all remained parameters are dependent
- goto Lnomatch;
- }
-
- RootObject *o2 = (*tp->tempinst->tiargs)[i];
- Type *t2 = isType(o2);
-
- size_t j = (t2 && t2->ty == Tident && i == tp->tempinst->tiargs->length - 1)
- ? templateParameterLookup(t2, parameters) : IDX_NOTFOUND;
- if (j != IDX_NOTFOUND && j == parameters->length - 1 &&
- (*parameters)[j]->isTemplateTupleParameter())
- {
- /* Given:
- * struct A(B...) {}
- * alias A!(int, float) X;
- * static if (is(X Y == A!(Z), Z...)) {}
- * deduce that Z is a tuple(int, float)
- */
-
- /* Create tuple from remaining args
- */
- Tuple *vt = new Tuple();
- size_t vtdim = (tempdecl->isVariadic()
- ? t->tempinst->tiargs->length : t->tempinst->tdtypes.length) - i;
- vt->objects.setDim(vtdim);
- for (size_t k = 0; k < vtdim; k++)
- {
- RootObject *o;
- if (k < t->tempinst->tiargs->length)
- o = (*t->tempinst->tiargs)[i + k];
- else // Pick up default arg
- o = t->tempinst->tdtypes[i + k];
- vt->objects[k] = o;
- }
-
- Tuple *v = (Tuple *)(*dedtypes)[j];
- if (v)
- {
- if (!match(v, vt))
- goto Lnomatch;
- }
- else
- (*dedtypes)[j] = vt;
- break;
- }
- else if (!o1)
- break;
-
- Type *t1 = isType(o1);
- Dsymbol *s1 = isDsymbol(o1);
- Dsymbol *s2 = isDsymbol(o2);
- Expression *e1 = s1 ? getValue(s1) : getValue(isExpression(o1));
- Expression *e2 = isExpression(o2);
-
- if (t1 && t2)
- {
- if (!deduceType(t1, sc, t2, parameters, dedtypes))
- goto Lnomatch;
- }
- else if (e1 && e2)
- {
- Le:
- e1 = e1->ctfeInterpret();
-
- /* If it is one of the template parameters for this template,
- * we should not attempt to interpret it. It already has a value.
- */
- if (e2->op == TOKvar &&
- (((VarExp *)e2)->var->storage_class & STCtemplateparameter))
- {
- /*
- * (T:Number!(e2), int e2)
- */
- j = templateIdentifierLookup(((VarExp *)e2)->var->ident, parameters);
- if (j != IDX_NOTFOUND)
- goto L1;
- // The template parameter was not from this template
- // (it may be from a parent template, for example)
- }
-
- e2 = expressionSemantic(e2, sc); // Bugzilla 13417
- e2 = e2->ctfeInterpret();
-
- //printf("e1 = %s, type = %s %d\n", e1->toChars(), e1->type->toChars(), e1->type->ty);
- //printf("e2 = %s, type = %s %d\n", e2->toChars(), e2->type->toChars(), e2->type->ty);
- if (!e1->equals(e2))
- {
- if (!e2->implicitConvTo(e1->type))
- goto Lnomatch;
-
- e2 = e2->implicitCastTo(sc, e1->type);
- e2 = e2->ctfeInterpret();
- if (!e1->equals(e2))
- goto Lnomatch;
- }
- }
- else if (e1 && t2 && t2->ty == Tident)
- {
- j = templateParameterLookup(t2, parameters);
- L1:
- if (j == IDX_NOTFOUND)
- {
- t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2);
- if (e2)
- goto Le;
- goto Lnomatch;
- }
- if (!(*parameters)[j]->matchArg(sc, e1, j, parameters, dedtypes, NULL))
- goto Lnomatch;
- }
- else if (s1 && s2)
- {
- Ls:
- if (!s1->equals(s2))
- goto Lnomatch;
- }
- else if (s1 && t2 && t2->ty == Tident)
- {
- j = templateParameterLookup(t2, parameters);
- if (j == IDX_NOTFOUND)
- {
- t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2);
- if (s2)
- goto Ls;
- goto Lnomatch;
- }
- if (!(*parameters)[j]->matchArg(sc, s1, j, parameters, dedtypes, NULL))
- goto Lnomatch;
- }
- else
- goto Lnomatch;
- }
- }
- visit((Type *)t);
- return;
-
- Lnomatch:
- //printf("no match\n");
- result = MATCHnomatch;
- }
-
- void visit(TypeStruct *t)
- {
- /* If this struct is a template struct, and we're matching
- * it against a template instance, convert the struct type
- * to a template instance, too, and try again.
- */
- TemplateInstance *ti = t->sym->parent->isTemplateInstance();
-
- if (tparam && tparam->ty == Tinstance)
- {
- if (ti && ti->toAlias() == t->sym)
- {
- TypeInstance *tx = new TypeInstance(Loc(), ti);
- result = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
- return;
- }
-
- /* Match things like:
- * S!(T).foo
- */
- TypeInstance *tpi = (TypeInstance *)tparam;
- if (tpi->idents.length)
- {
- RootObject *id = tpi->idents[tpi->idents.length - 1];
- if (id->dyncast() == DYNCAST_IDENTIFIER && t->sym->ident->equals((Identifier *)id))
- {
- Type *tparent = t->sym->parent->getType();
- if (tparent)
- {
- /* Slice off the .foo in S!(T).foo
- */
- tpi->idents.length--;
- result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
- tpi->idents.length++;
- return;
- }
- }
- }
- }
-
- // Extra check
- if (tparam && tparam->ty == Tstruct)
- {
- TypeStruct *tp = (TypeStruct *)tparam;
-
- //printf("\t%d\n", (MATCH) t->implicitConvTo(tp));
- if (wm && t->deduceWild(tparam, false))
- {
- result = MATCHconst;
- return;
- }
- result = t->implicitConvTo(tp);
- return;
- }
- visit((Type *)t);
- }
-
- void visit(TypeEnum *t)
- {
- // Extra check
- if (tparam && tparam->ty == Tenum)
- {
- TypeEnum *tp = (TypeEnum *)tparam;
- if (t->sym == tp->sym)
- visit((Type *)t);
- else
- result = MATCHnomatch;
- return;
- }
- Type *tb = t->toBasetype();
- if (tb->ty == tparam->ty ||
- (tb->ty == Tsarray && tparam->ty == Taarray))
- {
- result = deduceType(tb, sc, tparam, parameters, dedtypes, wm);
- return;
- }
- visit((Type *)t);
- }
-
- /* Helper for TypeClass::deduceType().
- * Classes can match with implicit conversion to a base class or interface.
- * This is complicated, because there may be more than one base class which
- * matches. In such cases, one or more parameters remain ambiguous.
- * For example,
- *
- * interface I(X, Y) {}
- * class C : I(uint, double), I(char, double) {}
- * C x;
- * foo(T, U)( I!(T, U) x)
- *
- * deduces that U is double, but T remains ambiguous (could be char or uint).
- *
- * Given a baseclass b, and initial deduced types 'dedtypes', this function
- * tries to match tparam with b, and also tries all base interfaces of b.
- * If a match occurs, numBaseClassMatches is incremented, and the new deduced
- * types are ANDed with the current 'best' estimate for dedtypes.
- */
- static void deduceBaseClassParameters(BaseClass *b,
- Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes,
- Objects *best, int &numBaseClassMatches)
- {
- TemplateInstance *parti = b->sym ? b->sym->parent->isTemplateInstance() : NULL;
- if (parti)
- {
- // Make a temporary copy of dedtypes so we don't destroy it
- Objects *tmpdedtypes = new Objects();
- tmpdedtypes->setDim(dedtypes->length);
- memcpy(tmpdedtypes->tdata(), dedtypes->tdata(), dedtypes->length * sizeof(void *));
-
- TypeInstance *t = new TypeInstance(Loc(), parti);
- MATCH m = deduceType(t, sc, tparam, parameters, tmpdedtypes);
- if (m > MATCHnomatch)
- {
- // If this is the first ever match, it becomes our best estimate
- if (numBaseClassMatches==0)
- memcpy(best->tdata(), tmpdedtypes->tdata(), tmpdedtypes->length * sizeof(void *));
- else for (size_t k = 0; k < tmpdedtypes->length; ++k)
- {
- // If we've found more than one possible type for a parameter,
- // mark it as unknown.
- if ((*tmpdedtypes)[k] != (*best)[k])
- (*best)[k] = (*dedtypes)[k];
- }
- ++numBaseClassMatches;
- }
- }
- // Now recursively test the inherited interfaces
- for (size_t j = 0; j < b->baseInterfaces.length; ++j)
- {
- BaseClass *bi = &b->baseInterfaces.ptr[j];
- deduceBaseClassParameters(bi,
- sc, tparam, parameters, dedtypes,
- best, numBaseClassMatches);
- }
-
- }
-
- void visit(TypeClass *t)
- {
- //printf("TypeClass::deduceType(this = %s)\n", t->toChars());
-
- /* If this class is a template class, and we're matching
- * it against a template instance, convert the class type
- * to a template instance, too, and try again.
- */
- TemplateInstance *ti = t->sym->parent->isTemplateInstance();
-
- if (tparam && tparam->ty == Tinstance)
- {
- if (ti && ti->toAlias() == t->sym)
- {
- TypeInstance *tx = new TypeInstance(Loc(), ti);
- MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
- // Even if the match fails, there is still a chance it could match
- // a base class.
- if (m != MATCHnomatch)
- {
- result = m;
- return;
- }
- }
-
- /* Match things like:
- * S!(T).foo
- */
- TypeInstance *tpi = (TypeInstance *)tparam;
- if (tpi->idents.length)
- {
- RootObject *id = tpi->idents[tpi->idents.length - 1];
- if (id->dyncast() == DYNCAST_IDENTIFIER && t->sym->ident->equals((Identifier *)id))
- {
- Type *tparent = t->sym->parent->getType();
- if (tparent)
- {
- /* Slice off the .foo in S!(T).foo
- */
- tpi->idents.length--;
- result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
- tpi->idents.length++;
- return;
- }
- }
- }
-
- // If it matches exactly or via implicit conversion, we're done
- visit((Type *)t);
- if (result != MATCHnomatch)
- return;
-
- /* There is still a chance to match via implicit conversion to
- * a base class or interface. Because there could be more than one such
- * match, we need to check them all.
- */
-
- int numBaseClassMatches = 0; // Have we found an interface match?
-
- // Our best guess at dedtypes
- Objects *best = new Objects();
- best->setDim(dedtypes->length);
-
- ClassDeclaration *s = t->sym;
- while (s && s->baseclasses->length > 0)
- {
- // Test the base class
- deduceBaseClassParameters((*s->baseclasses)[0],
- sc, tparam, parameters, dedtypes,
- best, numBaseClassMatches);
-
- // Test the interfaces inherited by the base class
- for (size_t i = 0; i < s->interfaces.length; ++i)
- {
- BaseClass *b = s->interfaces.ptr[i];
- deduceBaseClassParameters(b, sc, tparam, parameters, dedtypes,
- best, numBaseClassMatches);
- }
- s = (*s->baseclasses)[0]->sym;
- }
-
- if (numBaseClassMatches == 0)
- {
- result = MATCHnomatch;
- return;
- }
-
- // If we got at least one match, copy the known types into dedtypes
- memcpy(dedtypes->tdata(), best->tdata(), best->length * sizeof(void *));
- result = MATCHconvert;
- return;
- }
-
- // Extra check
- if (tparam && tparam->ty == Tclass)
- {
- TypeClass *tp = (TypeClass *)tparam;
-
- //printf("\t%d\n", (MATCH) t->implicitConvTo(tp));
- if (wm && t->deduceWild(tparam, false))
- {
- result = MATCHconst;
- return;
- }
- result = t->implicitConvTo(tp);
- return;
- }
- visit((Type *)t);
- }
-
- void visit(Expression *e)
- {
- //printf("Expression::deduceType(e = %s)\n", e->toChars());
- size_t i = templateParameterLookup(tparam, parameters);
- if (i == IDX_NOTFOUND || ((TypeIdentifier *)tparam)->idents.length > 0)
- {
- if (e == emptyArrayElement && tparam->ty == Tarray)
- {
- Type *tn = ((TypeNext *)tparam)->next;
- result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
- return;
- }
- e->type->accept(this);
- return;
- }
-
- TemplateTypeParameter *tp = (*parameters)[i]->isTemplateTypeParameter();
- if (!tp)
- return; // nomatch
-
- if (e == emptyArrayElement)
- {
- if ((*dedtypes)[i])
- {
- result = MATCHexact;
- return;
- }
- if (tp->defaultType)
- {
- tp->defaultType->accept(this);
- return;
- }
- }
-
- Type *at = (Type *)(*dedtypes)[i];
- Type *tt;
- if (unsigned char wx = deduceWildHelper(e->type, &tt, tparam))
- {
- *wm |= wx;
- result = MATCHconst;
- }
- else if (MATCH m = deduceTypeHelper(e->type, &tt, tparam))
- {
- result = m;
- }
- else
- return; // nomatch
-
- // expression vs (none)
- if (!at)
- {
- (*dedtypes)[i] = new TypeDeduced(tt, e, tparam);
- return;
- }
-
- TypeDeduced *xt = NULL;
- if (at->ty == Tnone)
- {
- xt = (TypeDeduced *)at;
- at = xt->tded;
- }
-
- // From previous matched expressions to current deduced type
- MATCH match1 = xt ? xt->matchAll(tt) : MATCHnomatch;
-
- // From current expresssion to previous deduced type
- Type *pt = at->addMod(tparam->mod);
- if (*wm)
- pt = pt->substWildTo(*wm);
- MATCH match2 = e->implicitConvTo(pt);
-
- if (match1 > MATCHnomatch && match2 > MATCHnomatch)
- {
- if (at->implicitConvTo(tt) <= MATCHnomatch)
- match1 = MATCHnomatch; // Prefer at
- else if (tt->implicitConvTo(at) <= MATCHnomatch)
- match2 = MATCHnomatch; // Prefer tt
- else if (tt->isTypeBasic() && tt->ty == at->ty && tt->mod != at->mod)
- {
- if (!tt->isMutable() && !at->isMutable())
- tt = tt->mutableOf()->addMod(MODmerge(tt->mod, at->mod));
- else if (tt->isMutable())
- {
- if (at->mod == 0) // Prefer unshared
- match1 = MATCHnomatch;
- else
- match2 = MATCHnomatch;
- }
- else if (at->isMutable())
- {
- if (tt->mod == 0) // Prefer unshared
- match2 = MATCHnomatch;
- else
- match1 = MATCHnomatch;
- }
- //printf("tt = %s, at = %s\n", tt->toChars(), at->toChars());
- }
- else
- {
- match1 = MATCHnomatch;
- match2 = MATCHnomatch;
- }
- }
- if (match1 > MATCHnomatch)
- {
- // Prefer current match: tt
- if (xt)
- xt->update(tt, e, tparam);
- else
- (*dedtypes)[i] = tt;
- result = match1;
- return;
- }
- if (match2 > MATCHnomatch)
- {
- // Prefer previous match: (*dedtypes)[i]
- if (xt)
- xt->update(e, tparam);
- result = match2;
- return;
- }
-
- /* Deduce common type
- */
- if (Type *t = rawTypeMerge(at, tt))
- {
- if (xt)
- xt->update(t, e, tparam);
- else
- (*dedtypes)[i] = t;
-
- pt = tt->addMod(tparam->mod);
- if (*wm)
- pt = pt->substWildTo(*wm);
- result = e->implicitConvTo(pt);
- return;
- }
-
- result = MATCHnomatch;
- }
-
- MATCH deduceEmptyArrayElement()
- {
- if (!emptyArrayElement)
- {
- emptyArrayElement = new IdentifierExp(Loc(), Id::p); // dummy
- emptyArrayElement->type = Type::tvoid;
- }
- assert(tparam->ty == Tarray);
-
- Type *tn = ((TypeNext *)tparam)->next;
- return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
- }
-
- void visit(NullExp *e)
- {
- if (tparam->ty == Tarray && e->type->ty == Tnull)
- {
- // tparam:T[] <- e:null (void[])
- result = deduceEmptyArrayElement();
- return;
- }
- visit((Expression *)e);
- }
-
- void visit(StringExp *e)
- {
- Type *taai;
- if (e->type->ty == Tarray &&
- (tparam->ty == Tsarray ||
- (tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident &&
- ((TypeIdentifier *)taai)->idents.length == 0)))
- {
- // Consider compile-time known boundaries
- e->type->nextOf()->sarrayOf(e->len)->accept(this);
- return;
- }
- visit((Expression *)e);
- }
-
- void visit(ArrayLiteralExp *e)
- {
- // https://issues.dlang.org/show_bug.cgi?id=20092
- if (e->elements && e->elements->length &&
- e->type->toBasetype()->nextOf()->ty == Tvoid)
- {
- result = deduceEmptyArrayElement();
- return;
- }
- if ((!e->elements || !e->elements->length) &&
- e->type->toBasetype()->nextOf()->ty == Tvoid &&
- tparam->ty == Tarray)
- {
- // tparam:T[] <- e:[] (void[])
- result = deduceEmptyArrayElement();
- return;
- }
-
- if (tparam->ty == Tarray && e->elements && e->elements->length)
- {
- Type *tn = ((TypeDArray *)tparam)->next;
- result = MATCHexact;
- if (e->basis)
- {
- MATCH m = deduceType(e->basis, sc, tn, parameters, dedtypes, wm);
- if (m < result)
- result = m;
- }
- for (size_t i = 0; i < e->elements->length; i++)
- {
- if (result <= MATCHnomatch)
- break;
- Expression *el = (*e->elements)[i];
- if (!el)
- continue;
- MATCH m = deduceType(el, sc, tn, parameters, dedtypes, wm);
- if (m < result)
- result = m;
- }
- return;
- }
-
- Type *taai;
- if (e->type->ty == Tarray &&
- (tparam->ty == Tsarray ||
- (tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident &&
- ((TypeIdentifier *)taai)->idents.length == 0)))
- {
- // Consider compile-time known boundaries
- e->type->nextOf()->sarrayOf(e->elements->length)->accept(this);
- return;
- }
- visit((Expression *)e);
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- if (tparam->ty == Taarray && e->keys && e->keys->length)
- {
- TypeAArray *taa = (TypeAArray *)tparam;
- result = MATCHexact;
- for (size_t i = 0; i < e->keys->length; i++)
- {
- MATCH m1 = deduceType((*e->keys)[i], sc, taa->index, parameters, dedtypes, wm);
- if (m1 < result)
- result = m1;
- if (result <= MATCHnomatch)
- break;
- MATCH m2 = deduceType((*e->values)[i], sc, taa->next, parameters, dedtypes, wm);
- if (m2 < result)
- result = m2;
- if (result <= MATCHnomatch)
- break;
- }
- return;
- }
- visit((Expression *)e);
- }
-
- void visit(FuncExp *e)
- {
- //printf("e->type = %s, tparam = %s\n", e->type->toChars(), tparam->toChars());
- if (e->td)
- {
- Type *to = tparam;
- if (!to->nextOf() || to->nextOf()->ty != Tfunction)
- return;
- TypeFunction *tof = (TypeFunction *)to->nextOf();
-
- // Parameter types inference from 'tof'
- assert(e->td->_scope);
- TypeFunction *tf = (TypeFunction *)e->fd->type;
- //printf("\ttof = %s\n", tof->toChars());
- //printf("\ttf = %s\n", tf->toChars());
- size_t dim = tf->parameterList.length();
-
- if (tof->parameterList.length() != dim ||
- tof->parameterList.varargs != tf->parameterList.varargs)
- return;
-
- Objects *tiargs = new Objects();
- tiargs->reserve(e->td->parameters->length);
-
- for (size_t i = 0; i < e->td->parameters->length; i++)
- {
- TemplateParameter *tp = (*e->td->parameters)[i];
- size_t u = 0;
- for (; u < dim; u++)
- {
- Parameter *p = tf->parameterList[u];
- if (p->type->ty == Tident &&
- ((TypeIdentifier *)p->type)->ident == tp->ident)
- {
- break;
- }
- }
- assert(u < dim);
- Parameter *pto = tof->parameterList[u];
- if (!pto)
- break;
- Type *t = pto->type->syntaxCopy(); // Bugzilla 11774
- if (reliesOnTident(t, parameters, inferStart))
- return;
- t = typeSemantic(t, e->loc, sc);
- if (t->ty == Terror)
- return;
- tiargs->push(t);
- }
-
- // Set target of return type inference
- if (!tf->next && tof->next)
- e->fd->treq = tparam;
-
- TemplateInstance *ti = new TemplateInstance(e->loc, e->td, tiargs);
- Expression *ex = new ScopeExp(e->loc, ti);
- ex = expressionSemantic(ex, e->td->_scope);
-
- // Reset inference target for the later re-semantic
- e->fd->treq = NULL;
-
- if (ex->op == TOKerror)
- return;
- if (ex->op != TOKfunction)
- return;
- visit(ex->type);
- return;
- }
-
- Type *t = e->type;
-
- if (t->ty == Tdelegate && tparam->ty == Tpointer)
- return;
-
- // Allow conversion from implicit function pointer to delegate
- if (e->tok == TOKreserved &&
- t->ty == Tpointer && tparam->ty == Tdelegate)
- {
- TypeFunction *tf = (TypeFunction *)t->nextOf();
- t = (new TypeDelegate(tf))->merge();
- }
- //printf("tparam = %s <= e->type = %s, t = %s\n", tparam->toChars(), e->type->toChars(), t->toChars());
- visit(t);
- }
-
- void visit(SliceExp *e)
- {
- Type *taai;
- if (e->type->ty == Tarray &&
- (tparam->ty == Tsarray ||
- (tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident &&
- ((TypeIdentifier *)taai)->idents.length == 0)))
- {
- // Consider compile-time known boundaries
- if (Type *tsa = toStaticArrayType(e))
- {
- tsa->accept(this);
- return;
- }
- }
- visit((Expression *)e);
- }
-
- void visit(CommaExp *e)
- {
- ((CommaExp *)e)->e2->accept(this);
- }
- };
-
- DeduceType v(sc, tparam, parameters, dedtypes, wm, inferStart);
- if (Type *t = isType(o))
- t->accept(&v);
- else
- {
- assert(isExpression(o) && wm);
- ((Expression *)o)->accept(&v);
- }
- return v.result;
-}
-
-/*******************************
- * Input:
- * t Tested type, if NULL, returns NULL.
- * tparams Optional template parameters.
- * == NULL:
- * If one of the subtypes of this type is a TypeIdentifier,
- * i.e. it's an unresolved type, return that type.
- * != NULL:
- * Only when the TypeIdentifier is one of template parameters,
- * return that type.
- */
-
-bool reliesOnTident(Type *t, TemplateParameters *tparams, size_t iStart)
-{
- class ReliesOnTident : public Visitor
- {
- public:
- TemplateParameters *tparams;
- size_t iStart;
- bool result;
-
- ReliesOnTident(TemplateParameters *tparams, size_t iStart)
- : tparams(tparams), iStart(iStart)
- {
- result = false;
- }
-
- void visit(Type *)
- {
- }
-
- void visit(TypeNext *t)
- {
- t->next->accept(this);
- }
-
- void visit(TypeVector *t)
- {
- t->basetype->accept(this);
- }
-
- void visit(TypeAArray *t)
- {
- visit((TypeNext *)t);
- if (!result)
- t->index->accept(this);
- }
-
- void visit(TypeFunction *t)
- {
- size_t dim = t->parameterList.length();
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *fparam = t->parameterList[i];
- fparam->type->accept(this);
- if (result)
- return;
- }
- if (t->next)
- t->next->accept(this);
- }
-
- void visit(TypeIdentifier *t)
- {
- if (!tparams)
- {
- result = true;
- return;
- }
-
- for (size_t i = iStart; i < tparams->length; i++)
- {
- TemplateParameter *tp = (*tparams)[i];
- if (tp->ident->equals(t->ident))
- {
- result = true;
- return;
- }
- }
- }
-
- void visit(TypeInstance *t)
- {
- if (!tparams)
- return;
-
- for (size_t i = iStart; i < tparams->length; i++)
- {
- TemplateParameter *tp = (*tparams)[i];
- if (t->tempinst->name == tp->ident)
- {
- result = true;
- return;
- }
- }
- if (!t->tempinst->tiargs)
- return;
- for (size_t i = 0; i < t->tempinst->tiargs->length; i++)
- {
- Type *ta = isType((*t->tempinst->tiargs)[i]);
- if (ta)
- {
- ta->accept(this);
- if (result)
- return;
- }
- }
- }
-
- void visit(TypeTypeof *t)
- {
- //printf("TypeTypeof::reliesOnTident('%s')\n", t->toChars());
- t->exp->accept(this);
- }
-
- void visit(TypeTuple *t)
- {
- if (t->arguments)
- {
- for (size_t i = 0; i < t->arguments->length; i++)
- {
- Parameter *arg = (*t->arguments)[i];
- arg->type->accept(this);
- if (result)
- return;
- }
- }
- }
-
- void visit(Expression *)
- {
- //printf("Expression::reliesOnTident('%s')\n", e->toChars());
- }
-
- void visit(IdentifierExp *e)
- {
- //printf("IdentifierExp::reliesOnTident('%s')\n", e->toChars());
- for (size_t i = iStart; i < tparams->length; i++)
- {
- TemplateParameter *tp = (*tparams)[i];
- if (e->ident == tp->ident)
- {
- result = true;
- return;
- }
- }
- }
-
- void visit(TupleExp *e)
- {
- //printf("TupleExp::reliesOnTident('%s')\n", e->toChars());
- if (e->exps)
- {
- for (size_t i = 0; i < e->exps->length; i++)
- {
- Expression *ea = (*e->exps)[i];
- ea->accept(this);
- if (result)
- return;
- }
- }
- }
-
- void visit(ArrayLiteralExp *e)
- {
- //printf("ArrayLiteralExp::reliesOnTident('%s')\n", e->toChars());
- if (e->elements)
- {
- for (size_t i = 0; i < e->elements->length; i++)
- {
- Expression *el = (*e->elements)[i];
- el->accept(this);
- if (result)
- return;
- }
- }
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- //printf("AssocArrayLiteralExp::reliesOnTident('%s')\n", e->toChars());
- for (size_t i = 0; i < e->keys->length; i++)
- {
- Expression *ek = (*e->keys)[i];
- ek->accept(this);
- if (result)
- return;
- }
- for (size_t i = 0; i < e->values->length; i++)
- {
- Expression *ev = (*e->values)[i];
- ev->accept(this);
- if (result)
- return;
- }
- }
-
- void visit(StructLiteralExp *e)
- {
- //printf("StructLiteralExp::reliesOnTident('%s')\n", e->toChars());
- if (e->elements)
- {
- for (size_t i = 0; i < e->elements->length; i++)
- {
- Expression *ea = (*e->elements)[i];
- ea->accept(this);
- if (result)
- return;
- }
- }
- }
-
- void visit(TypeExp *e)
- {
- //printf("TypeExp::reliesOnTident('%s')\n", e->toChars());
- e->type->accept(this);
- }
-
- void visit(NewExp *e)
- {
- //printf("NewExp::reliesOnTident('%s')\n", e->toChars());
- if (e->thisexp)
- e->thisexp->accept(this);
- if (!result && e->newargs)
- {
- for (size_t i = 0; i < e->newargs->length; i++)
- {
- Expression *ea = (*e->newargs)[i];
- ea->accept(this);
- if (result)
- return;
- }
- }
- e->newtype->accept(this);
- if (!result && e->arguments)
- {
- for (size_t i = 0; i < e->arguments->length; i++)
- {
- Expression *ea = (*e->arguments)[i];
- ea->accept(this);
- if (result)
- return;
- }
- }
- }
-
- void visit(NewAnonClassExp *)
- {
- //printf("NewAnonClassExp::reliesOnTident('%s')\n", e->toChars());
- result = true;
- }
-
- void visit(FuncExp *)
- {
- //printf("FuncExp::reliesOnTident('%s')\n", e->toChars());
- result = true;
- }
-
- void visit(TypeidExp *e)
- {
- //printf("TypeidExp::reliesOnTident('%s')\n", e->toChars());
- if (Expression *ea = isExpression(e->obj))
- ea->accept(this);
- else if (Type *ta = isType(e->obj))
- ta->accept(this);
- }
-
- void visit(TraitsExp *e)
- {
- //printf("TraitsExp::reliesOnTident('%s')\n", e->toChars());
- if (e->args)
- {
- for (size_t i = 0; i < e->args->length; i++)
- {
- RootObject *oa = (*e->args)[i];
- if (Expression *ea = isExpression(oa))
- ea->accept(this);
- else if (Type *ta = isType(oa))
- ta->accept(this);
- if (result)
- return;
- }
- }
- }
-
- void visit(IsExp *e)
- {
- //printf("IsExp::reliesOnTident('%s')\n", e->toChars());
- e->targ->accept(this);
- }
-
- void visit(UnaExp *e)
- {
- //printf("UnaExp::reliesOnTident('%s')\n", e->toChars());
- e->e1->accept(this);
- }
-
- void visit(DotTemplateInstanceExp *e)
- {
- //printf("DotTemplateInstanceExp::reliesOnTident('%s')\n", e->toChars());
- visit((UnaExp *)e);
- if (!result && e->ti->tiargs)
- {
- for (size_t i = 0; i < e->ti->tiargs->length; i++)
- {
- RootObject *oa = (*e->ti->tiargs)[i];
- if (Expression *ea = isExpression(oa))
- ea->accept(this);
- else if (Type *ta = isType(oa))
- ta->accept(this);
- if (result)
- return;
- }
- }
- }
-
- void visit(CallExp *e)
- {
- //printf("CallExp::reliesOnTident('%s')\n", e->toChars());
- visit((UnaExp *)e);
- if (!result && e->arguments)
- {
- for (size_t i = 0; i < e->arguments->length; i++)
- {
- Expression *ea = (*e->arguments)[i];
- ea->accept(this);
- if (result)
- return;
- }
- }
- }
-
- void visit(CastExp *e)
- {
- //printf("CastExp::reliesOnTident('%s')\n", e->toChars());
- visit((UnaExp *)e);
- // e.to can be null for cast() with no type
- if (!result && e->to)
- e->to->accept(this);
- }
-
- void visit(SliceExp *e)
- {
- //printf("SliceExp::reliesOnTident('%s')\n", e->toChars());
- visit((UnaExp *)e);
- if (!result && e->lwr)
- e->lwr->accept(this);
- if (!result && e->upr)
- e->upr->accept(this);
- }
-
- void visit(IntervalExp *e)
- {
- //printf("IntervalExp::reliesOnTident('%s')\n", e->toChars());
- e->lwr->accept(this);
- if (!result)
- e->upr->accept(this);
- }
-
- void visit(ArrayExp *e)
- {
- //printf("ArrayExp::reliesOnTident('%s')\n", e->toChars());
- visit((UnaExp *)e);
- if (!result && e->arguments)
- {
- for (size_t i = 0; i < e->arguments->length; i++)
- {
- Expression *ea = (*e->arguments)[i];
- ea->accept(this);
- }
- }
- }
-
- void visit(BinExp *e)
- {
- //printf("BinExp::reliesOnTident('%s')\n", e->toChars());
- e->e1->accept(this);
- if (!result)
- e->e2->accept(this);
- }
-
- void visit(CondExp *e)
- {
- //printf("BinExp::reliesOnTident('%s')\n", e->toChars());
- e->econd->accept(this);
- if (!result)
- visit((BinExp *)e);
- }
- };
-
- if (!t)
- return false;
-
- ReliesOnTident v(tparams, iStart);
- t->accept(&v);
- return v.result;
-}
-
-/* ======================== TemplateParameter =============================== */
-
-TemplateParameter::TemplateParameter(Loc loc, Identifier *ident)
-{
- this->loc = loc;
- this->ident = ident;
- this->dependent = false;
-}
-
-TemplateTypeParameter *TemplateParameter::isTemplateTypeParameter()
-{
- return NULL;
-}
-
-TemplateValueParameter *TemplateParameter::isTemplateValueParameter()
-{
- return NULL;
-}
-
-TemplateAliasParameter *TemplateParameter::isTemplateAliasParameter()
-{
- return NULL;
-}
-
-TemplateTupleParameter *TemplateParameter::isTemplateTupleParameter()
-{
- return NULL;
-}
-
-TemplateThisParameter *TemplateParameter::isTemplateThisParameter()
-{
- return NULL;
-}
-
-/*******************************************
- * Match to a particular TemplateParameter.
- * Input:
- * instLoc location that the template is instantiated.
- * tiargs[] actual arguments to template instance
- * i i'th argument
- * parameters[] template parameters
- * dedtypes[] deduced arguments to template instance
- * *psparam set to symbol declared and initialized to dedtypes[i]
- */
-MATCH TemplateParameter::matchArg(Loc instLoc, Scope *sc, Objects *tiargs,
- size_t i, TemplateParameters *parameters, Objects *dedtypes,
- Declaration **psparam)
-{
- RootObject *oarg;
-
- if (i < tiargs->length)
- oarg = (*tiargs)[i];
- else
- {
- // Get default argument instead
- oarg = defaultArg(instLoc, sc);
- if (!oarg)
- {
- assert(i < dedtypes->length);
- // It might have already been deduced
- oarg = (*dedtypes)[i];
- if (!oarg)
- goto Lnomatch;
- }
- }
- return matchArg(sc, oarg, i, parameters, dedtypes, psparam);
-
-Lnomatch:
- if (psparam)
- *psparam = NULL;
- return MATCHnomatch;
-}
-
-/* ======================== TemplateTypeParameter =========================== */
-
-// type-parameter
-
-Type *TemplateTypeParameter::tdummy = NULL;
-
-TemplateTypeParameter::TemplateTypeParameter(Loc loc, Identifier *ident, Type *specType,
- Type *defaultType)
- : TemplateParameter(loc, ident)
-{
- this->ident = ident;
- this->specType = specType;
- this->defaultType = defaultType;
-}
-
-TemplateTypeParameter *TemplateTypeParameter::isTemplateTypeParameter()
-{
- return this;
-}
-
-TemplateParameter *TemplateTypeParameter::syntaxCopy()
-{
- return new TemplateTypeParameter(loc, ident,
- specType ? specType->syntaxCopy() : NULL,
- defaultType ? defaultType->syntaxCopy() : NULL);
-}
-
-bool TemplateTypeParameter::declareParameter(Scope *sc)
-{
- //printf("TemplateTypeParameter::declareParameter('%s')\n", ident->toChars());
- TypeIdentifier *ti = new TypeIdentifier(loc, ident);
- Declaration *ad = new AliasDeclaration(loc, ident, ti);
- return sc->insert(ad) != NULL;
-}
-
-MATCH TemplateTypeParameter::matchArg(Scope *sc, RootObject *oarg,
- size_t i, TemplateParameters *parameters, Objects *dedtypes,
- Declaration **psparam)
-{
- //printf("TemplateTypeParameter::matchArg('%s')\n", ident->toChars());
- MATCH m = MATCHexact;
- Type *ta = isType(oarg);
- if (!ta)
- {
- //printf("%s %p %p %p\n", oarg->toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg));
- goto Lnomatch;
- }
- //printf("ta is %s\n", ta->toChars());
-
- if (specType)
- {
- if (!ta || ta == tdummy)
- goto Lnomatch;
-
- //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta->toChars(), specType->toChars());
- MATCH m2 = deduceType(ta, sc, specType, parameters, dedtypes);
- if (m2 <= MATCHnomatch)
- {
- //printf("\tfailed deduceType\n");
- goto Lnomatch;
- }
-
- if (m2 < m)
- m = m2;
- if ((*dedtypes)[i])
- {
- Type *t = (Type *)(*dedtypes)[i];
-
- if (dependent && !t->equals(ta)) // Bugzilla 14357
- goto Lnomatch;
-
- /* This is a self-dependent parameter. For example:
- * template X(T : T*) {}
- * template X(T : S!T, alias S) {}
- */
- //printf("t = %s ta = %s\n", t->toChars(), ta->toChars());
- ta = t;
- }
- }
- else
- {
- if ((*dedtypes)[i])
- {
- // Must match already deduced type
- Type *t = (Type *)(*dedtypes)[i];
-
- if (!t->equals(ta))
- {
- //printf("t = %s ta = %s\n", t->toChars(), ta->toChars());
- goto Lnomatch;
- }
- }
- else
- {
- // So that matches with specializations are better
- m = MATCHconvert;
- }
- }
- (*dedtypes)[i] = ta;
-
- if (psparam)
- *psparam = new AliasDeclaration(loc, ident, ta);
- //printf("\tm = %d\n", m);
- return dependent ? MATCHexact : m;
-
-Lnomatch:
- if (psparam)
- *psparam = NULL;
- //printf("\tm = %d\n", MATCHnomatch);
- return MATCHnomatch;
-}
-
-
-void TemplateTypeParameter::print(RootObject *oarg, RootObject *oded)
-{
- printf(" %s\n", ident->toChars());
-
- Type *t = isType(oarg);
- Type *ta = isType(oded);
-
- assert(ta);
-
- if (specType)
- printf("\tSpecialization: %s\n", specType->toChars());
- if (defaultType)
- printf("\tDefault: %s\n", defaultType->toChars());
- printf("\tParameter: %s\n", t ? t->toChars() : "NULL");
- printf("\tDeduced Type: %s\n", ta->toChars());
-}
-
-void *TemplateTypeParameter::dummyArg()
-{
- Type *t = specType;
- if (!t)
- {
- // Use this for alias-parameter's too (?)
- if (!tdummy)
- tdummy = new TypeIdentifier(loc, ident);
- t = tdummy;
- }
- return (void *)t;
-}
-
-
-RootObject *TemplateTypeParameter::specialization()
-{
- return specType;
-}
-
-RootObject *TemplateTypeParameter::defaultArg(Loc, Scope *sc)
-{
- Type *t = defaultType;
- if (t)
- {
- t = t->syntaxCopy();
- t = typeSemantic(t, loc, sc); // use the parameter loc
- }
- return t;
-}
-
-bool TemplateTypeParameter::hasDefaultArg()
-{
- return defaultType != NULL;
-}
-
-/* ======================== TemplateThisParameter =========================== */
-
-// this-parameter
-
-TemplateThisParameter::TemplateThisParameter(Loc loc, Identifier *ident,
- Type *specType,
- Type *defaultType)
- : TemplateTypeParameter(loc, ident, specType, defaultType)
-{
-}
-
-TemplateThisParameter *TemplateThisParameter::isTemplateThisParameter()
-{
- return this;
-}
-
-TemplateParameter *TemplateThisParameter::syntaxCopy()
-{
- return new TemplateThisParameter(loc, ident,
- specType ? specType->syntaxCopy() : NULL,
- defaultType ? defaultType->syntaxCopy() : NULL);
-}
-
-/* ======================== TemplateAliasParameter ========================== */
-
-// alias-parameter
-
-Dsymbol *TemplateAliasParameter::sdummy = NULL;
-
-TemplateAliasParameter::TemplateAliasParameter(Loc loc, Identifier *ident,
- Type *specType, RootObject *specAlias, RootObject *defaultAlias)
- : TemplateParameter(loc, ident)
-{
- this->ident = ident;
- this->specType = specType;
- this->specAlias = specAlias;
- this->defaultAlias = defaultAlias;
-}
-
-TemplateAliasParameter *TemplateAliasParameter::isTemplateAliasParameter()
-{
- return this;
-}
-
-TemplateParameter *TemplateAliasParameter::syntaxCopy()
-{
- return new TemplateAliasParameter(loc, ident,
- specType ? specType->syntaxCopy() : NULL,
- objectSyntaxCopy(specAlias),
- objectSyntaxCopy(defaultAlias));
-}
-
-bool TemplateAliasParameter::declareParameter(Scope *sc)
-{
- TypeIdentifier *ti = new TypeIdentifier(loc, ident);
- Declaration *ad = new AliasDeclaration(loc, ident, ti);
- return sc->insert(ad) != NULL;
-}
-
-MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg,
- size_t i, TemplateParameters *parameters, Objects *dedtypes,
- Declaration **psparam)
-{
- //printf("TemplateAliasParameter::matchArg('%s')\n", ident->toChars());
- MATCH m = MATCHexact;
- Type *ta = isType(oarg);
- RootObject *sa = ta && !ta->deco ? NULL : getDsymbol(oarg);
- Expression *ea = isExpression(oarg);
- if (ea && (ea->op == TOKthis || ea->op == TOKsuper))
- sa = ((ThisExp *)ea)->var;
- else if (ea && ea->op == TOKscope)
- sa = ((ScopeExp *)ea)->sds;
- if (sa)
- {
- if (((Dsymbol *)sa)->isAggregateDeclaration())
- m = MATCHconvert;
-
- /* specType means the alias must be a declaration with a type
- * that matches specType.
- */
- if (specType)
- {
- Declaration *d = ((Dsymbol *)sa)->isDeclaration();
- if (!d)
- goto Lnomatch;
- if (!d->type->equals(specType))
- goto Lnomatch;
- }
- }
- else
- {
- sa = oarg;
- if (ea)
- {
- if (specType)
- {
- if (!ea->type->equals(specType))
- goto Lnomatch;
- }
- }
- else if (ta && ta->ty == Tinstance && !specAlias)
- {
- /* Bugzilla xxxxx: Specialized parameter should be prefeerd
- * match to the template type parameter.
- * template X(alias a) {} // a == this
- * template X(alias a : B!A, alias B, A...) {} // B!A => ta
- */
- }
- else if (sa && sa == TemplateTypeParameter::tdummy)
- {
- /* Bugzilla 2025: Aggregate Types should preferentially
- * match to the template type parameter.
- * template X(alias a) {} // a == this
- * template X(T) {} // T => sa
- */
- }
- else if (ta && ta->ty != Tident)
- {
- /* Match any type that's not a TypeIdentifier to alias parameters,
- * but prefer type parameter.
- * template X(alias a) { } // a == ta
- *
- * TypeIdentifiers are excluded because they might be not yet resolved aliases.
- */
- m = MATCHconvert;
- }
- else
- goto Lnomatch;
- }
-
- if (specAlias)
- {
- if (sa == sdummy)
- goto Lnomatch;
- Dsymbol *sx = isDsymbol(sa);
- if (sa != specAlias && sx)
- {
- Type *talias = isType(specAlias);
- if (!talias)
- goto Lnomatch;
-
- TemplateInstance *ti = sx->isTemplateInstance();
- if (!ti && sx->parent)
- {
- ti = sx->parent->isTemplateInstance();
- if (ti && ti->name != sx->ident)
- goto Lnomatch;
- }
- if (!ti)
- goto Lnomatch;
-
- Type *t = new TypeInstance(Loc(), ti);
- MATCH m2 = deduceType(t, sc, talias, parameters, dedtypes);
- if (m2 <= MATCHnomatch)
- goto Lnomatch;
- }
- }
- else if ((*dedtypes)[i])
- {
- // Must match already deduced symbol
- RootObject *si = (*dedtypes)[i];
- if (!sa || si != sa)
- goto Lnomatch;
- }
- (*dedtypes)[i] = sa;
-
- if (psparam)
- {
- if (Dsymbol *s = isDsymbol(sa))
- {
- *psparam = new AliasDeclaration(loc, ident, s);
- }
- else if (Type *t = isType(sa))
- {
- *psparam = new AliasDeclaration(loc, ident, t);
- }
- else
- {
- assert(ea);
-
- // Declare manifest constant
- Initializer *init = new ExpInitializer(loc, ea);
- VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init);
- v->storage_class = STCmanifest;
- dsymbolSemantic(v, sc);
- *psparam = v;
- }
- }
- return dependent ? MATCHexact : m;
-
-Lnomatch:
- if (psparam)
- *psparam = NULL;
- //printf("\tm = %d\n", MATCHnomatch);
- return MATCHnomatch;
-}
-
-
-void TemplateAliasParameter::print(RootObject *, RootObject *oded)
-{
- printf(" %s\n", ident->toChars());
-
- Dsymbol *sa = isDsymbol(oded);
- assert(sa);
-
- printf("\tParameter alias: %s\n", sa->toChars());
-}
-
-void *TemplateAliasParameter::dummyArg()
-{
- RootObject *s = specAlias;
- if (!s)
- {
- if (!sdummy)
- sdummy = new Dsymbol();
- s = sdummy;
- }
- return (void*)s;
-}
-
-
-RootObject *TemplateAliasParameter::specialization()
-{
- return specAlias;
-}
-
-RootObject *TemplateAliasParameter::defaultArg(Loc, Scope *sc)
-{
- RootObject *da = defaultAlias;
- Type *ta = isType(defaultAlias);
- if (ta)
- {
- if (ta->ty == Tinstance)
- {
- // If the default arg is a template, instantiate for each type
- da = ta->syntaxCopy();
- }
- }
-
- RootObject *o = aliasParameterSemantic(loc, sc, da, NULL); // use the parameter loc
- return o;
-}
-
-bool TemplateAliasParameter::hasDefaultArg()
-{
- return defaultAlias != NULL;
-}
-
-/* ======================== TemplateValueParameter ========================== */
-
-// value-parameter
-
-AA *TemplateValueParameter::edummies = NULL;
-
-TemplateValueParameter::TemplateValueParameter(Loc loc, Identifier *ident, Type *valType,
- Expression *specValue, Expression *defaultValue)
- : TemplateParameter(loc, ident)
-{
- this->ident = ident;
- this->valType = valType;
- this->specValue = specValue;
- this->defaultValue = defaultValue;
-}
-
-TemplateValueParameter *TemplateValueParameter::isTemplateValueParameter()
-{
- return this;
-}
-
-TemplateParameter *TemplateValueParameter::syntaxCopy()
-{
- return new TemplateValueParameter(loc, ident,
- valType->syntaxCopy(),
- specValue ? specValue->syntaxCopy() : NULL,
- defaultValue ? defaultValue->syntaxCopy() : NULL);
-}
-
-bool TemplateValueParameter::declareParameter(Scope *sc)
-{
- VarDeclaration *v = new VarDeclaration(loc, valType, ident, NULL);
- v->storage_class = STCtemplateparameter;
- return sc->insert(v) != NULL;
-}
-
-MATCH TemplateValueParameter::matchArg(Scope *sc, RootObject *oarg,
- size_t i, TemplateParameters *, Objects *dedtypes, Declaration **psparam)
-{
- //printf("TemplateValueParameter::matchArg('%s')\n", ident->toChars());
-
- MATCH m = MATCHexact;
-
- Expression *ei = isExpression(oarg);
- Type *vt;
-
- if (!ei && oarg)
- {
- Dsymbol *si = isDsymbol(oarg);
- FuncDeclaration *f = si ? si->isFuncDeclaration() : NULL;
- if (!f || !f->fbody || f->needThis())
- goto Lnomatch;
-
- ei = new VarExp(loc, f);
- ei = expressionSemantic(ei, sc);
-
- /* If a function is really property-like, and then
- * it's CTFEable, ei will be a literal expression.
- */
- unsigned int olderrors = global.startGagging();
- ei = resolveProperties(sc, ei);
- ei = ei->ctfeInterpret();
- if (global.endGagging(olderrors) || ei->op == TOKerror)
- goto Lnomatch;
-
- /* Bugzilla 14520: A property-like function can match to both
- * TemplateAlias and ValueParameter. But for template overloads,
- * it should always prefer alias parameter to be consistent
- * template match result.
- *
- * template X(alias f) { enum X = 1; }
- * template X(int val) { enum X = 2; }
- * int f1() { return 0; } // CTFEable
- * int f2(); // body-less function is not CTFEable
- * enum x1 = X!f1; // should be 1
- * enum x2 = X!f2; // should be 1
- *
- * e.g. The x1 value must be same even if the f1 definition will be moved
- * into di while stripping body code.
- */
- m = MATCHconvert;
- }
-
- if (ei && ei->op == TOKvar)
- {
- // Resolve const variables that we had skipped earlier
- ei = ei->ctfeInterpret();
- }
-
- //printf("\tvalType: %s, ty = %d\n", valType->toChars(), valType->ty);
- vt = typeSemantic(valType, loc, sc);
- //printf("ei: %s, ei->type: %s\n", ei->toChars(), ei->type->toChars());
- //printf("vt = %s\n", vt->toChars());
-
- if (ei->type)
- {
- MATCH m2 = ei->implicitConvTo(vt);
- //printf("m: %d\n", m);
- if (m2 < m)
- m = m2;
- if (m <= MATCHnomatch)
- goto Lnomatch;
- ei = ei->implicitCastTo(sc, vt);
- ei = ei->ctfeInterpret();
- }
-
- if (specValue)
- {
- if (!ei || (Expression *)dmd_aaGetRvalue(edummies, (void *)ei->type) == ei)
- goto Lnomatch;
-
- Expression *e = specValue;
-
- sc = sc->startCTFE();
- e = expressionSemantic(e, sc);
- e = resolveProperties(sc, e);
- sc = sc->endCTFE();
- e = e->implicitCastTo(sc, vt);
- e = e->ctfeInterpret();
-
- ei = ei->syntaxCopy();
- sc = sc->startCTFE();
- ei = expressionSemantic(ei, sc);
- sc = sc->endCTFE();
- ei = ei->implicitCastTo(sc, vt);
- ei = ei->ctfeInterpret();
- //printf("\tei: %s, %s\n", ei->toChars(), ei->type->toChars());
- //printf("\te : %s, %s\n", e->toChars(), e->type->toChars());
- if (!ei->equals(e))
- goto Lnomatch;
- }
- else
- {
- if ((*dedtypes)[i])
- {
- // Must match already deduced value
- Expression *e = (Expression *)(*dedtypes)[i];
-
- if (!ei || !ei->equals(e))
- goto Lnomatch;
- }
- }
- (*dedtypes)[i] = ei;
-
- if (psparam)
- {
- Initializer *init = new ExpInitializer(loc, ei);
- Declaration *sparam = new VarDeclaration(loc, vt, ident, init);
- sparam->storage_class = STCmanifest;
- *psparam = sparam;
- }
- return dependent ? MATCHexact : m;
-
-Lnomatch:
- //printf("\tno match\n");
- if (psparam)
- *psparam = NULL;
- return MATCHnomatch;
-}
-
-
-void TemplateValueParameter::print(RootObject *, RootObject *oded)
-{
- printf(" %s\n", ident->toChars());
-
- Expression *ea = isExpression(oded);
-
- if (specValue)
- printf("\tSpecialization: %s\n", specValue->toChars());
- printf("\tParameter Value: %s\n", ea ? ea->toChars() : "NULL");
-}
-
-void *TemplateValueParameter::dummyArg()
-{
- Expression *e = specValue;
- if (!e)
- {
- // Create a dummy value
- Expression **pe = (Expression **)dmd_aaGet(&edummies, (void *)valType);
- if (!*pe)
- *pe = valType->defaultInit();
- e = *pe;
- }
- return (void *)e;
-}
-
-
-RootObject *TemplateValueParameter::specialization()
-{
- return specValue;
-}
-
-RootObject *TemplateValueParameter::defaultArg(Loc instLoc, Scope *sc)
-{
- Expression *e = defaultValue;
- if (e)
- {
- e = e->syntaxCopy();
- unsigned olderrs = global.errors;
- if ((e = expressionSemantic(e, sc)) == NULL)
- return NULL;
- if ((e = resolveProperties(sc, e)) == NULL)
- return NULL;
- e = e->resolveLoc(instLoc, sc); // use the instantiated loc
- e = e->optimize(WANTvalue);
- if (global.errors != olderrs)
- e = new ErrorExp();
- }
- return e;
-}
-
-bool TemplateValueParameter::hasDefaultArg()
-{
- return defaultValue != NULL;
-}
-
-/* ======================== TemplateTupleParameter ========================== */
-
-// variadic-parameter
-
-TemplateTupleParameter::TemplateTupleParameter(Loc loc, Identifier *ident)
- : TemplateParameter(loc, ident)
-{
- this->ident = ident;
-}
-
-TemplateTupleParameter *TemplateTupleParameter::isTemplateTupleParameter()
-{
- return this;
-}
-
-TemplateParameter *TemplateTupleParameter::syntaxCopy()
-{
- return new TemplateTupleParameter(loc, ident);
-}
-
-bool TemplateTupleParameter::declareParameter(Scope *sc)
-{
- TypeIdentifier *ti = new TypeIdentifier(loc, ident);
- Declaration *ad = new AliasDeclaration(loc, ident, ti);
- return sc->insert(ad) != NULL;
-}
-
-MATCH TemplateTupleParameter::matchArg(Loc, Scope *sc, Objects *tiargs,
- size_t i, TemplateParameters *parameters, Objects *dedtypes,
- Declaration **psparam)
-{
- /* The rest of the actual arguments (tiargs[]) form the match
- * for the variadic parameter.
- */
- assert(i + 1 == dedtypes->length); // must be the last one
- Tuple *ovar;
-
- if (Tuple *u = isTuple((*dedtypes)[i]))
- {
- // It has already been deduced
- ovar = u;
- }
- else if (i + 1 == tiargs->length && isTuple((*tiargs)[i]))
- ovar = isTuple((*tiargs)[i]);
- else
- {
- ovar = new Tuple();
- //printf("ovar = %p\n", ovar);
- if (i < tiargs->length)
- {
- //printf("i = %d, tiargs->length = %d\n", i, tiargs->length);
- ovar->objects.setDim(tiargs->length - i);
- for (size_t j = 0; j < ovar->objects.length; j++)
- ovar->objects[j] = (*tiargs)[i + j];
- }
- }
- return matchArg(sc, ovar, i, parameters, dedtypes, psparam);
-}
-
-MATCH TemplateTupleParameter::matchArg(Scope *, RootObject *oarg,
- size_t i, TemplateParameters *, Objects *dedtypes, Declaration **psparam)
-{
- //printf("TemplateTupleParameter::matchArg('%s')\n", ident->toChars());
- Tuple *ovar = isTuple(oarg);
- if (!ovar)
- return MATCHnomatch;
- if ((*dedtypes)[i])
- {
- Tuple *tup = isTuple((*dedtypes)[i]);
- if (!tup)
- return MATCHnomatch;
- if (!match(tup, ovar))
- return MATCHnomatch;
- }
- (*dedtypes)[i] = ovar;
-
- if (psparam)
- *psparam = new TupleDeclaration(loc, ident, &ovar->objects);
- return dependent ? MATCHexact : MATCHconvert;
-}
-
-
-void TemplateTupleParameter::print(RootObject *, RootObject *oded)
-{
- printf(" %s... [", ident->toChars());
- Tuple *v = isTuple(oded);
- assert(v);
-
- //printf("|%d| ", v->objects.length);
- for (size_t i = 0; i < v->objects.length; i++)
- {
- if (i)
- printf(", ");
-
- RootObject *o = v->objects[i];
-
- Dsymbol *sa = isDsymbol(o);
- if (sa)
- printf("alias: %s", sa->toChars());
-
- Type *ta = isType(o);
- if (ta)
- printf("type: %s", ta->toChars());
-
- Expression *ea = isExpression(o);
- if (ea)
- printf("exp: %s", ea->toChars());
-
- assert(!isTuple(o)); // no nested Tuple arguments
- }
-
- printf("]\n");
-}
-
-void *TemplateTupleParameter::dummyArg()
-{
- return NULL;
-}
-
-
-RootObject *TemplateTupleParameter::specialization()
-{
- return NULL;
-}
-
-RootObject *TemplateTupleParameter::defaultArg(Loc, Scope *)
-{
- return NULL;
-}
-
-bool TemplateTupleParameter::hasDefaultArg()
-{
- return false;
-}
-
-/* ======================== TemplateInstance ================================ */
-
-TemplateInstance::TemplateInstance(Loc loc, Identifier *ident)
- : ScopeDsymbol(NULL)
-{
- this->loc = loc;
- this->name = ident;
- this->tiargs = NULL;
- this->tempdecl = NULL;
- this->inst = NULL;
- this->tinst = NULL;
- this->tnext = NULL;
- this->minst = NULL;
- this->deferred = NULL;
- this->memberOf = NULL;
- this->argsym = NULL;
- this->aliasdecl = NULL;
- this->semantictiargsdone = false;
- this->inuse = 0;
- this->nest = 0;
- this->havetempdecl = false;
- this->enclosing = NULL;
- this->gagged = false;
- this->hash = 0;
- this->fargs = NULL;
-}
-
-/*****************
- * This constructor is only called when we figured out which function
- * template to instantiate.
- */
-
-TemplateInstance::TemplateInstance(Loc loc, TemplateDeclaration *td, Objects *tiargs)
- : ScopeDsymbol(NULL)
-{
- this->loc = loc;
- this->name = td->ident;
- this->tiargs = tiargs;
- this->tempdecl = td;
- this->inst = NULL;
- this->tinst = NULL;
- this->tnext = NULL;
- this->minst = NULL;
- this->deferred = NULL;
- this->memberOf = NULL;
- this->argsym = NULL;
- this->aliasdecl = NULL;
- this->semantictiargsdone = true;
- this->inuse = 0;
- this->nest = 0;
- this->havetempdecl = true;
- this->enclosing = NULL;
- this->gagged = false;
- this->hash = 0;
- this->fargs = NULL;
-
- assert(tempdecl->_scope);
-}
-
-
-Objects *TemplateInstance::arraySyntaxCopy(Objects *objs)
-{
- Objects *a = NULL;
- if (objs)
- {
- a = new Objects();
- a->setDim(objs->length);
- for (size_t i = 0; i < objs->length; i++)
- (*a)[i] = objectSyntaxCopy((*objs)[i]);
- }
- return a;
-}
-
-Dsymbol *TemplateInstance::syntaxCopy(Dsymbol *s)
-{
- TemplateInstance *ti =
- s ? (TemplateInstance *)s
- : new TemplateInstance(loc, name);
- ti->tiargs = arraySyntaxCopy(tiargs);
- TemplateDeclaration *td;
- if (inst && tempdecl && (td = tempdecl->isTemplateDeclaration()) != NULL)
- td->ScopeDsymbol::syntaxCopy(ti);
- else
- ScopeDsymbol::syntaxCopy(ti);
- return ti;
-}
-
-void TemplateInstance::expandMembers(Scope *sc2)
-{
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- s->setScope(sc2);
- }
-
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- s->importAll(sc2);
- }
-
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- //printf("\t[%d] semantic on '%s' %p kind %s in '%s'\n", i, s->toChars(), s, s->kind(), this->toChars());
- //printf("test: enclosing = %d, sc2->parent = %s\n", enclosing, sc2->parent->toChars());
-// if (enclosing)
-// s->parent = sc->parent;
- //printf("test3: enclosing = %d, s->parent = %s\n", enclosing, s->parent->toChars());
- dsymbolSemantic(s, sc2);
- //printf("test4: enclosing = %d, s->parent = %s\n", enclosing, s->parent->toChars());
- Module::runDeferredSemantic();
- }
-}
-
-void TemplateInstance::tryExpandMembers(Scope *sc2)
-{
- static int nest;
- // extracted to a function to allow windows SEH to work without destructors in the same function
- //printf("%d\n", nest);
- if (++nest > global.recursionLimit)
- {
- global.gag = 0; // ensure error message gets printed
- error("recursive expansion exceeded allowed nesting limit");
- fatal();
- }
-
- expandMembers(sc2);
-
- nest--;
-}
-
-void TemplateInstance::trySemantic3(Scope *sc2)
-{
- // extracted to a function to allow windows SEH to work without destructors in the same function
- static int nest;
- //printf("%d\n", nest);
- if (++nest > global.recursionLimit)
- {
- global.gag = 0; // ensure error message gets printed
- error("recursive expansion exceeded allowed nesting limit");
- fatal();
- }
- semantic3(this, sc2);
-
- --nest;
-}
-
-/**********************************************
- * Find template declaration corresponding to template instance.
- *
- * Returns:
- * false if finding fails.
- * Note:
- * This function is reentrant against error occurrence. If returns false,
- * any members of this object won't be modified, and repetition call will
- * reproduce same error.
- */
-
-bool TemplateInstance::findTempDecl(Scope *sc, WithScopeSymbol **pwithsym)
-{
- if (pwithsym)
- *pwithsym = NULL;
-
- if (havetempdecl)
- return true;
-
- //printf("TemplateInstance::findTempDecl() %s\n", toChars());
- if (!tempdecl)
- {
- /* Given:
- * foo!( ... )
- * figure out which TemplateDeclaration foo refers to.
- */
- Identifier *id = name;
- Dsymbol *scopesym;
- Dsymbol *s = sc->search(loc, id, &scopesym);
- if (!s)
- {
- s = sc->search_correct(id);
- if (s)
- error("template `%s` is not defined, did you mean %s?", id->toChars(), s->toChars());
- else
- error("template `%s` is not defined", id->toChars());
- return false;
- }
-
- if (pwithsym)
- *pwithsym = scopesym->isWithScopeSymbol();
-
- /* We might have found an alias within a template when
- * we really want the template.
- */
- TemplateInstance *ti;
- if (s->parent &&
- (ti = s->parent->isTemplateInstance()) != NULL)
- {
- if (ti->tempdecl && ti->tempdecl->ident == id)
- {
- /* This is so that one can refer to the enclosing
- * template, even if it has the same name as a member
- * of the template, if it has a !(arguments)
- */
- TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration();
- assert(td);
- if (td->overroot) // if not start of overloaded list of TemplateDeclaration's
- td = td->overroot; // then get the start
- s = td;
- }
- }
-
- if (!updateTempDecl(sc, s))
- {
- return false;
- }
- }
- assert(tempdecl);
-
- struct ParamFwdTi
- {
- static int fp(void *param, Dsymbol *s)
- {
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (!td)
- return 0;
-
- TemplateInstance *ti = (TemplateInstance *)param;
- if (td->semanticRun == PASSinit)
- {
- if (td->_scope)
- {
- // Try to fix forward reference. Ungag errors while doing so.
- Ungag ungag = td->ungagSpeculative();
- dsymbolSemantic(td, td->_scope);
- }
- if (td->semanticRun == PASSinit)
- {
- ti->error("%s forward references template declaration %s", ti->toChars(), td->toChars());
- return 1;
- }
- }
- return 0;
- }
- };
- // Look for forward references
- OverloadSet *tovers = tempdecl->isOverloadSet();
- size_t overs_dim = tovers ? tovers->a.length : 1;
- for (size_t oi = 0; oi < overs_dim; oi++)
- {
- if (overloadApply(tovers ? tovers->a[oi] : tempdecl, (void *)this, &ParamFwdTi::fp))
- return false;
- }
- return true;
-}
-
-/**********************************************
- * Confirm s is a valid template, then store it.
- * Input:
- * sc
- * s candidate symbol of template. It may be:
- * TemplateDeclaration
- * FuncDeclaration with findTemplateDeclRoot() != NULL
- * OverloadSet which contains candidates
- * Returns:
- * true if updating succeeds.
- */
-
-bool TemplateInstance::updateTempDecl(Scope *sc, Dsymbol *s)
-{
- if (s)
- {
- Identifier *id = name;
- s = s->toAlias();
-
- /* If an OverloadSet, look for a unique member that is a template declaration
- */
- OverloadSet *os = s->isOverloadSet();
- if (os)
- {
- s = NULL;
- for (size_t i = 0; i < os->a.length; i++)
- {
- Dsymbol *s2 = os->a[i];
- if (FuncDeclaration *f = s2->isFuncDeclaration())
- s2 = f->findTemplateDeclRoot();
- else
- s2 = s2->isTemplateDeclaration();
- if (s2)
- {
- if (s)
- {
- tempdecl = os;
- return true;
- }
- s = s2;
- }
- }
- if (!s)
- {
- error("template `%s` is not defined", id->toChars());
- return false;
- }
- }
-
- OverDeclaration *od = s->isOverDeclaration();
- if (od)
- {
- tempdecl = od; // TODO: more strict check
- return true;
- }
-
- /* It should be a TemplateDeclaration, not some other symbol
- */
- if (FuncDeclaration *f = s->isFuncDeclaration())
- tempdecl = f->findTemplateDeclRoot();
- else
- tempdecl = s->isTemplateDeclaration();
- if (!tempdecl)
- {
- if (!s->parent && global.errors)
- return false;
- if (!s->parent && s->getType())
- {
- Dsymbol *s2 = s->getType()->toDsymbol(sc);
- if (!s2)
- {
- error("%s is not a template declaration, it is a %s", id->toChars(), s->kind());
- return false;
- }
- s = s2;
- }
- //assert(s->parent);
- TemplateInstance *ti = s->parent ? s->parent->isTemplateInstance() : NULL;
- if (ti &&
- (ti->name == s->ident ||
- ti->toAlias()->ident == s->ident)
- &&
- ti->tempdecl)
- {
- /* This is so that one can refer to the enclosing
- * template, even if it has the same name as a member
- * of the template, if it has a !(arguments)
- */
- TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration();
- assert(td);
- if (td->overroot) // if not start of overloaded list of TemplateDeclaration's
- td = td->overroot; // then get the start
- tempdecl = td;
- }
- else
- {
- error("%s is not a template declaration, it is a %s", id->toChars(), s->kind());
- return false;
- }
- }
- }
- return (tempdecl != NULL);
-}
-
-/**********************************
- * Run semantic on the elements of tiargs.
- * Input:
- * sc
- * Returns:
- * false if one or more arguments have errors.
- * Note:
- * This function is reentrant against error occurrence. If returns false,
- * all elements of tiargs won't be modified.
- */
-
-bool TemplateInstance::semanticTiargs(Scope *sc)
-{
- //printf("+TemplateInstance::semanticTiargs() %s\n", toChars());
- if (semantictiargsdone)
- return true;
- if (semanticTiargs(loc, sc, tiargs, 0))
- {
- // cache the result iff semantic analysis succeeded entirely
- semantictiargsdone = 1;
- return true;
- }
- return false;
-}
-
-/**********************************
- * Run semantic of tiargs as arguments of template.
- * Input:
- * loc
- * sc
- * tiargs array of template arguments
- * flags 1: replace const variables with their initializers
- * 2: don't devolve Parameter to Type
- * Returns:
- * false if one or more arguments have errors.
- */
-
-bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags)
-{
- // Run semantic on each argument, place results in tiargs[]
- //printf("+TemplateInstance::semanticTiargs()\n");
- if (!tiargs)
- return true;
- bool err = false;
- for (size_t j = 0; j < tiargs->length; j++)
- {
- RootObject *o = (*tiargs)[j];
- Type *ta = isType(o);
- Expression *ea = isExpression(o);
- Dsymbol *sa = isDsymbol(o);
-
- //printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta);
- if (ta)
- {
- //printf("type %s\n", ta->toChars());
-
- // It might really be an Expression or an Alias
- ta->resolve(loc, sc, &ea, &ta, &sa, (flags & 1) != 0);
- if (ea) goto Lexpr;
- if (sa) goto Ldsym;
- if (ta == NULL)
- {
- assert(global.errors);
- ta = Type::terror;
- }
-
- Ltype:
- if (ta->ty == Ttuple)
- {
- // Expand tuple
- TypeTuple *tt = (TypeTuple *)ta;
- size_t dim = tt->arguments->length;
- tiargs->remove(j);
- if (dim)
- {
- tiargs->reserve(dim);
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *arg = (*tt->arguments)[i];
- if (flags & 2 && (arg->ident || arg->userAttribDecl))
- tiargs->insert(j + i, arg);
- else
- tiargs->insert(j + i, arg->type);
- }
- }
- j--;
- continue;
- }
- if (ta->ty == Terror)
- {
- err = true;
- continue;
- }
- (*tiargs)[j] = ta->merge2();
- }
- else if (ea)
- {
- Lexpr:
- //printf("+[%d] ea = %s %s\n", j, Token::toChars(ea->op), ea->toChars());
- if (flags & 1) // only used by __traits
- {
- ea = expressionSemantic(ea, sc);
-
- // must not interpret the args, excepting template parameters
- if (ea->op != TOKvar ||
- (((VarExp *)ea)->var->storage_class & STCtemplateparameter))
- {
- ea = ea->optimize(WANTvalue);
- }
- }
- else
- {
- sc = sc->startCTFE();
- ea = expressionSemantic(ea, sc);
- sc = sc->endCTFE();
-
- if (ea->op == TOKvar)
- {
- /* This test is to skip substituting a const var with
- * its initializer. The problem is the initializer won't
- * match with an 'alias' parameter. Instead, do the
- * const substitution in TemplateValueParameter::matchArg().
- */
- }
- else if (definitelyValueParameter(ea))
- {
- if (ea->checkValue()) // check void expression
- ea = new ErrorExp();
- unsigned int olderrs = global.errors;
- ea = ea->ctfeInterpret();
- if (global.errors != olderrs)
- ea = new ErrorExp();
- }
- }
- //printf("-[%d] ea = %s %s\n", j, Token::toChars(ea->op), ea->toChars());
- if (ea->op == TOKtuple)
- {
- // Expand tuple
- TupleExp *te = (TupleExp *)ea;
- size_t dim = te->exps->length;
- tiargs->remove(j);
- if (dim)
- {
- tiargs->reserve(dim);
- for (size_t i = 0; i < dim; i++)
- tiargs->insert(j + i, (*te->exps)[i]);
- }
- j--;
- continue;
- }
- if (ea->op == TOKerror)
- {
- err = true;
- continue;
- }
- (*tiargs)[j] = ea;
-
- if (ea->op == TOKtype)
- {
- ta = ea->type;
- goto Ltype;
- }
- if (ea->op == TOKscope)
- {
- sa = ((ScopeExp *)ea)->sds;
- goto Ldsym;
- }
- if (ea->op == TOKfunction)
- {
- FuncExp *fe = (FuncExp *)ea;
- /* A function literal, that is passed to template and
- * already semanticed as function pointer, never requires
- * outer frame. So convert it to global function is valid.
- */
- if (fe->fd->tok == TOKreserved && fe->type->ty == Tpointer)
- {
- // change to non-nested
- fe->fd->tok = TOKfunction;
- fe->fd->vthis = NULL;
- }
- else if (fe->td)
- {
- /* If template argument is a template lambda,
- * get template declaration itself. */
- //sa = fe->td;
- //goto Ldsym;
- }
- }
- if (ea->op == TOKdotvar && !(flags & 1))
- {
- // translate expression to dsymbol.
- sa = ((DotVarExp *)ea)->var;
- goto Ldsym;
- }
- if (ea->op == TOKtemplate)
- {
- sa = ((TemplateExp *)ea)->td;
- goto Ldsym;
- }
- if (ea->op == TOKdottd && !(flags & 1))
- {
- // translate expression to dsymbol.
- sa = ((DotTemplateExp *)ea)->td;
- goto Ldsym;
- }
- if (ea->op == TOKdot)
- {
- if (ScopeExp *se = ((DotExp *)ea)->e2->isScopeExp())
- {
- sa = se->sds;
- goto Ldsym;
- }
- }
- }
- else if (sa)
- {
- Ldsym:
- //printf("dsym %s %s\n", sa->kind(), sa->toChars());
- if (sa->errors)
- {
- err = true;
- continue;
- }
-
- TupleDeclaration *d = sa->toAlias()->isTupleDeclaration();
- if (d)
- {
- // Expand tuple
- tiargs->remove(j);
- tiargs->insert(j, d->objects);
- j--;
- continue;
- }
- if (FuncAliasDeclaration *fa = sa->isFuncAliasDeclaration())
- {
- FuncDeclaration *f = fa->toAliasFunc();
- if (!fa->hasOverloads && f->isUnique())
- {
- // Strip FuncAlias only when the aliased function
- // does not have any overloads.
- sa = f;
- }
- }
- (*tiargs)[j] = sa;
-
- TemplateDeclaration *td = sa->isTemplateDeclaration();
- if (td && td->semanticRun == PASSinit && td->literal)
- {
- dsymbolSemantic(td, sc);
- }
- FuncDeclaration *fd = sa->isFuncDeclaration();
- if (fd)
- fd->functionSemantic();
- }
- else if (isParameter(o))
- {
- }
- else
- {
- assert(0);
- }
- //printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]);
- }
- return !err;
-}
-
-bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs)
-{
- if (havetempdecl)
- {
- TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration();
- assert(tempdecl);
- assert(tempdecl->_scope);
- // Deduce tdtypes
- tdtypes.setDim(tempdecl->parameters->length);
- if (!tempdecl->matchWithInstance(sc, this, &tdtypes, fargs, 2))
- {
- error("incompatible arguments for template instantiation");
- return false;
- }
- // TODO: Normalizing tiargs for bugzilla 7469 is necessary?
- return true;
- }
-
- unsigned errs = global.errors;
- TemplateDeclaration *td_last = NULL;
-
- struct ParamBest
- {
- // context
- Scope *sc;
- TemplateInstance *ti;
- Objects dedtypes;
- // result
- TemplateDeclaration *td_best;
- TemplateDeclaration *td_ambig;
- MATCH m_best;
-
- static int fp(void *param, Dsymbol *s)
- {
- return ((ParamBest *)param)->fp(s);
- }
- int fp(Dsymbol *s)
- {
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (!td)
- return 0;
- if (td->inuse)
- {
- td->error(ti->loc, "recursive template expansion");
- return 1;
- }
- if (td == td_best) // skip duplicates
- return 0;
-
- //printf("td = %s\n", td->toPrettyChars());
-
- // If more arguments than parameters,
- // then this is no match.
- if (td->parameters->length < ti->tiargs->length)
- {
- if (!td->isVariadic())
- return 0;
- }
-
- dedtypes.setDim(td->parameters->length);
- dedtypes.zero();
- assert(td->semanticRun != PASSinit);
- MATCH m = td->matchWithInstance(sc, ti, &dedtypes, ti->fargs, 0);
- //printf("matchWithInstance = %d\n", m);
- if (m <= MATCHnomatch) // no match at all
- return 0;
-
- if (m < m_best) goto Ltd_best;
- if (m > m_best) goto Ltd;
-
- {
- // Disambiguate by picking the most specialized TemplateDeclaration
- MATCH c1 = td->leastAsSpecialized(sc, td_best, ti->fargs);
- MATCH c2 = td_best->leastAsSpecialized(sc, td, ti->fargs);
- //printf("c1 = %d, c2 = %d\n", c1, c2);
- if (c1 > c2) goto Ltd;
- if (c1 < c2) goto Ltd_best;
- }
-
- td_ambig = td;
- return 0;
-
- Ltd_best: // td_best is the best match so far
- td_ambig = NULL;
- return 0;
-
- Ltd: // td is the new best match
- td_ambig = NULL;
- td_best = td;
- m_best = m;
- ti->tdtypes.setDim(dedtypes.length);
- memcpy(ti->tdtypes.tdata(), dedtypes.tdata(), ti->tdtypes.length * sizeof(void *));
- return 0;
- }
- };
- ParamBest p;
- // context
- p.ti = this;
- p.sc = sc;
-
- /* Since there can be multiple TemplateDeclaration's with the same
- * name, look for the best match.
- */
- OverloadSet *tovers = tempdecl->isOverloadSet();
- size_t overs_dim = tovers ? tovers->a.length : 1;
- for (size_t oi = 0; oi < overs_dim; oi++)
- {
- // result
- p.td_best = NULL;
- p.td_ambig = NULL;
- p.m_best = MATCHnomatch;
-
- Dsymbol *dstart = tovers ? tovers->a[oi] : tempdecl;
- overloadApply(dstart, &p, &ParamBest::fp);
-
- if (p.td_ambig)
- {
- ::error(loc, "%s %s.%s matches more than one template declaration:\n%s: %s\nand\n%s: %s",
- p.td_best->kind(), p.td_best->parent->toPrettyChars(), p.td_best->ident->toChars(),
- p.td_best->loc.toChars() , p.td_best->toChars(),
- p.td_ambig->loc.toChars(), p.td_ambig->toChars());
- return false;
- }
- if (p.td_best)
- {
- if (!td_last)
- td_last = p.td_best;
- else if (td_last != p.td_best)
- {
- ScopeDsymbol::multiplyDefined(loc, td_last, p.td_best);
- return false;
- }
- }
- }
-
- if (td_last)
- {
- /* Bugzilla 7469: Normalize tiargs by using corresponding deduced
- * template value parameters and tuples for the correct mangling.
- *
- * By doing this before hasNestedArgs, CTFEable local variable will be
- * accepted as a value parameter. For example:
- *
- * void foo() {
- * struct S(int n) {} // non-global template
- * const int num = 1; // CTFEable local variable
- * S!num s; // S!1 is instantiated, not S!num
- * }
- */
- size_t dim = td_last->parameters->length - (td_last->isVariadic() ? 1 : 0);
- for (size_t i = 0; i < dim; i++)
- {
- if (tiargs->length <= i)
- tiargs->push(tdtypes[i]);
- assert(i < tiargs->length);
-
- TemplateValueParameter *tvp = (*td_last->parameters)[i]->isTemplateValueParameter();
- if (!tvp)
- continue;
- assert(tdtypes[i]);
- // tdtypes[i] is already normalized to the required type in matchArg
-
- (*tiargs)[i] = tdtypes[i];
- }
- if (td_last->isVariadic() && tiargs->length == dim && tdtypes[dim])
- {
- Tuple *va = isTuple(tdtypes[dim]);
- assert(va);
- for (size_t i = 0; i < va->objects.length; i++)
- tiargs->push(va->objects[i]);
- }
- }
- else if (errors && inst)
- {
- // instantiation was failed with error reporting
- assert(global.errors);
- return false;
- }
- else
- {
- TemplateDeclaration *tdecl = tempdecl->isTemplateDeclaration();
-
- if (errs != global.errors)
- errorSupplemental(loc, "while looking for match for %s", toChars());
- else if (tdecl && !tdecl->overnext)
- {
- // Only one template, so we can give better error message
- error("does not match template declaration %s", tdecl->toChars());
- }
- else
- ::error(loc, "%s %s.%s does not match any template declaration",
- tempdecl->kind(), tempdecl->parent->toPrettyChars(), tempdecl->ident->toChars());
- return false;
- }
-
- /* The best match is td_last
- */
- tempdecl = td_last;
-
- return (errs == global.errors);
-}
-
-/*****************************************************
- * Determine if template instance is really a template function,
- * and that template function needs to infer types from the function
- * arguments.
- *
- * Like findBestMatch, iterate possible template candidates,
- * but just looks only the necessity of type inference.
- */
-
-bool TemplateInstance::needsTypeInference(Scope *sc, int flag)
-{
- //printf("TemplateInstance::needsTypeInference() %s\n", toChars());
- if (semanticRun != PASSinit)
- return false;
-
- struct ParamNeedsInf
- {
- // context
- Scope *sc;
- TemplateInstance *ti;
- int flag;
- // result
- Objects dedtypes;
- size_t count;
-
- static int fp(void *param, Dsymbol *s)
- {
- return ((ParamNeedsInf *)param)->fp(s);
- }
- int fp(Dsymbol *s)
- {
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (!td)
- return 0;
- if (td->inuse)
- {
- td->error(ti->loc, "recursive template expansion");
- return 1;
- }
-
- /* If any of the overloaded template declarations need inference,
- * then return true
- */
- FuncDeclaration *fd;
- if (!td->onemember)
- return 0;
- if (TemplateDeclaration *td2 = td->onemember->isTemplateDeclaration())
- {
- if (!td2->onemember || !td2->onemember->isFuncDeclaration())
- return 0;
- if (ti->tiargs->length >= td->parameters->length - (td->isVariadic() ? 1 : 0))
- return 0;
- return 1;
- }
- if ((fd = td->onemember->isFuncDeclaration()) == NULL ||
- fd->type->ty != Tfunction)
- {
- return 0;
- }
-
- for (size_t i = 0; i < td->parameters->length; i++)
- {
- if ((*td->parameters)[i]->isTemplateThisParameter())
- return 1;
- }
-
- /* Determine if the instance arguments, tiargs, are all that is necessary
- * to instantiate the template.
- */
- //printf("tp = %p, td->parameters->length = %d, tiargs->length = %d\n", tp, td->parameters->length, ti->tiargs->length);
- TypeFunction *tf = (TypeFunction *)fd->type;
- if (size_t dim = tf->parameterList.length())
- {
- TemplateParameter *tp = td->isVariadic();
- if (tp && td->parameters->length > 1)
- return 1;
-
- if (!tp && ti->tiargs->length < td->parameters->length)
- {
- // Can remain tiargs be filled by default arguments?
- for (size_t i = ti->tiargs->length; i < td->parameters->length; i++)
- {
- if (!(*td->parameters)[i]->hasDefaultArg())
- return 1;
- }
- }
-
- for (size_t i = 0; i < dim; i++)
- {
- // 'auto ref' needs inference.
- if (tf->parameterList[i]->storageClass & STCauto)
- return 1;
- }
- }
-
- if (!flag)
- {
- /* Calculate the need for overload resolution.
- * When only one template can match with tiargs, inference is not necessary.
- */
- dedtypes.setDim(td->parameters->length);
- dedtypes.zero();
- if (td->semanticRun == PASSinit)
- {
- if (td->_scope)
- {
- // Try to fix forward reference. Ungag errors while doing so.
- Ungag ungag = td->ungagSpeculative();
- dsymbolSemantic(td, td->_scope);
- }
- if (td->semanticRun == PASSinit)
- {
- ti->error("%s forward references template declaration %s", ti->toChars(), td->toChars());
- return 1;
- }
- }
- assert(td->semanticRun != PASSinit);
- MATCH m = td->matchWithInstance(sc, ti, &dedtypes, NULL, 0);
- if (m <= MATCHnomatch)
- return 0;
- }
-
- /* If there is more than one function template which matches, we may
- * need type inference (see Bugzilla 4430)
- */
- if (++count > 1)
- return 1;
-
- return 0;
- }
- };
- ParamNeedsInf p;
- // context
- p.ti = this;
- p.sc = sc;
- p.flag = flag;
- // result
- p.count = 0;
-
- OverloadSet *tovers = tempdecl->isOverloadSet();
- size_t overs_dim = tovers ? tovers->a.length : 1;
- unsigned olderrs = global.errors;
- for (size_t oi = 0; oi < overs_dim; oi++)
- {
- if (overloadApply(tovers ? tovers->a[oi] : tempdecl, &p, &ParamNeedsInf::fp))
- return true;
- }
- if (olderrs != global.errors)
- {
- if (!global.gag)
- {
- errorSupplemental(loc, "while looking for match for %s", toChars());
- semanticRun = PASSsemanticdone;
- inst = this;
- }
- errors = true;
- }
- //printf("false\n");
- return false;
-}
-
-
-/*****************************************
- * Determines if a TemplateInstance will need a nested
- * generation of the TemplateDeclaration.
- * Sets enclosing property if so, and returns != 0;
- */
-
-bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic)
-{
- int nested = 0;
- //printf("TemplateInstance::hasNestedArgs('%s')\n", tempdecl->ident->toChars());
-
- /* A nested instance happens when an argument references a local
- * symbol that is on the stack.
- */
- for (size_t i = 0; i < args->length; i++)
- {
- RootObject *o = (*args)[i];
- Expression *ea = isExpression(o);
- Dsymbol *sa = isDsymbol(o);
- Tuple *va = isTuple(o);
- if (ea)
- {
- if (ea->op == TOKvar)
- {
- sa = ((VarExp *)ea)->var;
- goto Lsa;
- }
- if (ea->op == TOKthis)
- {
- sa = ((ThisExp *)ea)->var;
- goto Lsa;
- }
- if (ea->op == TOKfunction)
- {
- if (((FuncExp *)ea)->td)
- sa = ((FuncExp *)ea)->td;
- else
- sa = ((FuncExp *)ea)->fd;
- goto Lsa;
- }
- // Emulate Expression::toMangleBuffer call that had exist in TemplateInstance::genIdent.
- if (ea->op != TOKint64 &&
- ea->op != TOKfloat64 &&
- ea->op != TOKcomplex80 &&
- ea->op != TOKnull &&
- ea->op != TOKstring &&
- ea->op != TOKarrayliteral &&
- ea->op != TOKassocarrayliteral &&
- ea->op != TOKstructliteral)
- {
- ea->error("expression %s is not a valid template value argument", ea->toChars());
- errors = true;
- }
- }
- else if (sa)
- {
- Lsa:
- sa = sa->toAlias();
- TemplateDeclaration *td = sa->isTemplateDeclaration();
- if (td)
- {
- TemplateInstance *ti = sa->toParent()->isTemplateInstance();
- if (ti && ti->enclosing)
- sa = ti;
- }
- TemplateInstance *ti = sa->isTemplateInstance();
- Declaration *d = sa->isDeclaration();
- if ((td && td->literal) ||
- (ti && ti->enclosing) ||
- (d && !d->isDataseg() &&
- !(d->storage_class & STCmanifest) &&
- (!d->isFuncDeclaration() || d->isFuncDeclaration()->isNested()) &&
- !isTemplateMixin()
- ))
- {
- // if module level template
- if (isstatic)
- {
- Dsymbol *dparent = sa->toParent2();
- if (!enclosing)
- enclosing = dparent;
- else if (enclosing != dparent)
- {
- /* Select the more deeply nested of the two.
- * Error if one is not nested inside the other.
- */
- for (Dsymbol *p = enclosing; p; p = p->parent)
- {
- if (p == dparent)
- goto L1; // enclosing is most nested
- }
- for (Dsymbol *p = dparent; p; p = p->parent)
- {
- if (p == enclosing)
- {
- enclosing = dparent;
- goto L1; // dparent is most nested
- }
- }
- error("%s is nested in both %s and %s",
- toChars(), enclosing->toChars(), dparent->toChars());
- errors = true;
- }
- L1:
- //printf("\tnested inside %s\n", enclosing->toChars());
- nested |= 1;
- }
- else
- {
- error("cannot use local `%s` as parameter to non-global template %s", sa->toChars(), tempdecl->toChars());
- errors = true;
- }
- }
- }
- else if (va)
- {
- nested |= (int)hasNestedArgs(&va->objects, isstatic);
- }
- }
- //printf("-TemplateInstance::hasNestedArgs('%s') = %d\n", tempdecl->ident->toChars(), nested);
- return nested != 0;
-}
-
-/*****************************************
- * Append 'this' to the specific module members[]
- */
-Dsymbols *TemplateInstance::appendToModuleMember()
-{
- Module *mi = minst; // instantiated -> inserted module
-
- if (global.params.useUnitTests)
- {
- // Turn all non-root instances to speculative
- if (mi && !mi->isRoot())
- mi = NULL;
- }
-
- //printf("%s->appendToModuleMember() enclosing = %s mi = %s\n",
- // toPrettyChars(),
- // enclosing ? enclosing->toPrettyChars() : NULL,
- // mi ? mi->toPrettyChars() : NULL);
- if (!mi || mi->isRoot())
- {
- /* If the instantiated module is speculative or root, insert to the
- * member of a root module. Then:
- * - semantic3 pass will get called on the instance members.
- * - codegen pass will get a selection chance to do/skip it.
- */
-
- struct N
- {
- static Dsymbol *getStrictEnclosing(TemplateInstance *ti)
- {
- do
- {
- if (ti->enclosing)
- return ti->enclosing;
- ti = ti->tempdecl->isInstantiated();
- }
- while (ti);
- return NULL;
- }
- };
- Dsymbol *enc = N::getStrictEnclosing(this);
-
- // insert target is made stable by using the module
- // where tempdecl is declared.
- mi = (enc ? enc : tempdecl)->getModule();
- if (!mi->isRoot())
- mi = mi->importedFrom;
- assert(mi->isRoot());
- }
- else
- {
- /* If the instantiated module is non-root, insert to the member of the
- * non-root module. Then:
- * - semantic3 pass won't be called on the instance.
- * - codegen pass won't reach to the instance.
- */
- }
- //printf("\t--> mi = %s\n", mi->toPrettyChars());
-
- if (memberOf == mi) // already a member
- {
- return NULL;
- }
-
- Dsymbols *a = mi->members;
- a->push(this);
- memberOf = mi;
- if (mi->semanticRun >= PASSsemantic2done && mi->isRoot())
- Module::addDeferredSemantic2(this);
- if (mi->semanticRun >= PASSsemantic3done && mi->isRoot())
- Module::addDeferredSemantic3(this);
- return a;
-}
-
-/****************************************
- * This instance needs an identifier for name mangling purposes.
- * Create one by taking the template declaration name and adding
- * the type signature for it.
- */
-
-Identifier *TemplateInstance::genIdent(Objects *args)
-{
- //printf("TemplateInstance::genIdent('%s')\n", tempdecl->ident->toChars());
- assert(args == tiargs);
- OutBuffer buf;
- mangleToBuffer(this, &buf);
- //printf("\tgenIdent = %s\n", id);
- return Identifier::idPool(buf.peekChars());
-}
-
-/*************************************
- * Lazily generate identifier for template instance.
- * This is because 75% of the ident's are never needed.
- */
-
-Identifier *TemplateInstance::getIdent()
-{
- if (!ident && inst && !errors)
- ident = genIdent(tiargs); // need an identifier for name mangling purposes.
- return ident;
-}
-
-/****************************************************
- * Declare parameters of template instance, initialize them with the
- * template instance arguments.
- */
-
-void TemplateInstance::declareParameters(Scope *sc)
-{
- TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration();
- assert(tempdecl);
-
- //printf("TemplateInstance::declareParameters()\n");
- for (size_t i = 0; i < tdtypes.length; i++)
- {
- TemplateParameter *tp = (*tempdecl->parameters)[i];
- //RootObject *o = (*tiargs)[i];
- RootObject *o = tdtypes[i]; // initializer for tp
-
- //printf("\ttdtypes[%d] = %p\n", i, o);
- tempdecl->declareParameter(sc, tp, o);
- }
-}
-
-/**************************************
- * Given an error instantiating the TemplateInstance,
- * give the nested TemplateInstance instantiations that got
- * us here. Those are a list threaded into the nested scopes.
- */
-void TemplateInstance::printInstantiationTrace()
-{
- if (global.gag)
- return;
-
- const unsigned max_shown = 6;
- const char format[] = "instantiated from here: %s";
-
- // determine instantiation depth and number of recursive instantiations
- unsigned n_instantiations = 1;
- unsigned n_totalrecursions = 0;
- for (TemplateInstance *cur = this; cur; cur = cur->tinst)
- {
- ++n_instantiations;
- // If two instantiations use the same declaration, they are recursive.
- // (this works even if they are instantiated from different places in the
- // same template).
- // In principle, we could also check for multiple-template recursion, but it's
- // probably not worthwhile.
- if (cur->tinst && cur->tempdecl && cur->tinst->tempdecl
- && cur->tempdecl->loc.equals(cur->tinst->tempdecl->loc))
- ++n_totalrecursions;
- }
-
- // show full trace only if it's short or verbose is on
- if (n_instantiations <= max_shown || global.params.verbose)
- {
- for (TemplateInstance *cur = this; cur; cur = cur->tinst)
- {
- cur->errors = true;
- errorSupplemental(cur->loc, format, cur->toChars());
- }
- }
- else if (n_instantiations - n_totalrecursions <= max_shown)
- {
- // By collapsing recursive instantiations into a single line,
- // we can stay under the limit.
- int recursionDepth=0;
- for (TemplateInstance *cur = this; cur; cur = cur->tinst)
- {
- cur->errors = true;
- if (cur->tinst && cur->tempdecl && cur->tinst->tempdecl
- && cur->tempdecl->loc.equals(cur->tinst->tempdecl->loc))
- {
- ++recursionDepth;
- }
- else
- {
- if (recursionDepth)
- errorSupplemental(cur->loc, "%d recursive instantiations from here: %s", recursionDepth+2, cur->toChars());
- else
- errorSupplemental(cur->loc, format, cur->toChars());
- recursionDepth = 0;
- }
- }
- }
- else
- {
- // Even after collapsing the recursions, the depth is too deep.
- // Just display the first few and last few instantiations.
- unsigned i = 0;
- for (TemplateInstance *cur = this; cur; cur = cur->tinst)
- {
- cur->errors = true;
-
- if (i == max_shown / 2)
- errorSupplemental(cur->loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown);
-
- if (i < max_shown / 2 ||
- i >= n_instantiations - max_shown + max_shown / 2)
- errorSupplemental(cur->loc, format, cur->toChars());
- ++i;
- }
- }
-}
-
-Dsymbol *TemplateInstance::toAlias()
-{
- if (!inst)
- {
- // Maybe we can resolve it
- if (_scope)
- {
- dsymbolSemantic(this, _scope);
- }
- if (!inst)
- {
- error("cannot resolve forward reference");
- errors = true;
- return this;
- }
- }
-
- if (inst != this)
- return inst->toAlias();
-
- if (aliasdecl)
- {
- return aliasdecl->toAlias();
- }
-
- return inst;
-}
-
-const char *TemplateInstance::kind() const
-{
- return "template instance";
-}
-
-bool TemplateInstance::oneMember(Dsymbol **ps, Identifier *)
-{
- *ps = NULL;
- return true;
-}
-
-const char *TemplateInstance::toChars()
-{
- OutBuffer buf;
- toCBufferInstance(this, &buf);
- return buf.extractChars();
-}
-
-const char *TemplateInstance::toPrettyCharsHelper()
-{
- OutBuffer buf;
- toCBufferInstance(this, &buf, true);
- return buf.extractChars();
-}
-
-/*************************************
- * Compare proposed template instantiation with existing template instantiation.
- * Note that this is not commutative because of the auto ref check.
- * Params:
- * this = proposed template instantiation
- * o = existing template instantiation
- * Returns:
- * 0 for match, 1 for no match
- */
-int TemplateInstance::compare(RootObject *o)
-{
- TemplateInstance *ti = (TemplateInstance *)o;
-
- //printf("this = %p, ti = %p\n", this, ti);
- assert(tdtypes.length == ti->tdtypes.length);
-
- // Nesting must match
- if (enclosing != ti->enclosing)
- {
- //printf("test2 enclosing %s ti->enclosing %s\n", enclosing ? enclosing->toChars() : "", ti->enclosing ? ti->enclosing->toChars() : "");
- goto Lnotequals;
- }
- //printf("parent = %s, ti->parent = %s\n", parent->toPrettyChars(), ti->parent->toPrettyChars());
-
- if (!arrayObjectMatch(&tdtypes, &ti->tdtypes))
- goto Lnotequals;
-
- /* Template functions may have different instantiations based on
- * "auto ref" parameters.
- */
- if (FuncDeclaration *fd = ti->toAlias()->isFuncDeclaration())
- {
- if (!fd->errors)
- {
- ParameterList fparameters = fd->getParameterList();
- size_t nfparams = fparameters.length(); // Num function parameters
- for (size_t j = 0; j < nfparams; j++)
- {
- Parameter *fparam = fparameters[j];
- if (fparam->storageClass & STCautoref) // if "auto ref"
- {
- if (!fargs)
- goto Lnotequals;
- if (fargs->length <= j)
- break;
- Expression *farg = (*fargs)[j];
- if (farg->isLvalue())
- {
- if (!(fparam->storageClass & STCref))
- goto Lnotequals; // auto ref's don't match
- }
- else
- {
- if (fparam->storageClass & STCref)
- goto Lnotequals; // auto ref's don't match
- }
- }
- }
- }
- }
- return 0;
-
- Lnotequals:
- return 1;
-}
-
-hash_t TemplateInstance::toHash()
-{
- if (!hash)
- {
- hash = (size_t)(void *)enclosing;
- hash += arrayObjectHash(&tdtypes);
- hash += hash == 0;
- }
- return hash;
-}
-
-/**************************************
- * IsExpression can evaluate the specified type speculatively, and even if
- * it instantiates any symbols, they are normally unnecessary for the
- * final executable.
- * However, if those symbols leak to the actual code, compiler should remark
- * them as non-speculative to generate their code and link to the final executable.
- */
-void unSpeculative(Scope *sc, RootObject *o)
-{
- if (!o)
- return;
-
- if (Tuple *tup = isTuple(o))
- {
- for (size_t i = 0; i < tup->objects.length; i++)
- {
- unSpeculative(sc, tup->objects[i]);
- }
- return;
- }
-
- Dsymbol *s = getDsymbol(o);
- if (!s)
- return;
-
- if (Declaration *d = s->isDeclaration())
- {
- if (VarDeclaration *vd = d->isVarDeclaration())
- o = vd->type;
- else if (AliasDeclaration *ad = d->isAliasDeclaration())
- {
- o = ad->getType();
- if (!o)
- o = ad->toAlias();
- }
- else
- o = d->toAlias();
-
- s = getDsymbol(o);
- if (!s)
- return;
- }
-
- if (TemplateInstance *ti = s->isTemplateInstance())
- {
- // If the instance is already non-speculative,
- // or it is leaked to the speculative scope.
- if (ti->minst != NULL || sc->minst == NULL)
- return;
-
- // Remark as non-speculative instance.
- ti->minst = sc->minst;
- if (!ti->tinst)
- ti->tinst = sc->tinst;
-
- unSpeculative(sc, ti->tempdecl);
- }
-
- if (TemplateInstance *ti = s->isInstantiated())
- unSpeculative(sc, ti);
-}
-
-/**
- Returns: true if the instances' innards are discardable.
-
- The idea of this function is to see if the template instantiation
- can be 100% replaced with its eponymous member. All other members
- can be discarded, even in the compiler to free memory (for example,
- the template could be expanded in a region allocator, deemed trivial,
- the end result copied back out independently and the entire region freed),
- and can be elided entirely from the binary.
-
- The current implementation affects code that generally looks like:
-
- ---
- template foo(args...) {
- some_basic_type_or_string helper() { .... }
- enum foo = helper();
- }
- ---
-
- since it was the easiest starting point of implementation but it can and
- should be expanded more later.
-*/
-static bool isDiscardable(TemplateInstance *ti)
-{
- if (ti->aliasdecl == NULL)
- return false;
-
- VarDeclaration *v = ti->aliasdecl->isVarDeclaration();
- if (v == NULL)
- return false;
-
- if (!(v->storage_class & STCmanifest))
- return false;
-
- // Currently only doing basic types here because it is the easiest proof-of-concept
- // implementation with minimal risk of side effects, but it could likely be
- // expanded to any type that already exists outside this particular instance.
- if (!(v->type->equals(Type::tstring) || (v->type->isTypeBasic() != NULL)))
- return false;
-
- // Static ctors and dtors, even in an eponymous enum template, are still run,
- // so if any of them are in here, we'd better not assume it is trivial lest
- // we break useful code
- for (size_t i = 0; i < ti->members->length; i++)
- {
- Dsymbol *member = (*ti->members)[i];
- if (member->hasStaticCtorOrDtor())
- return false;
- if (member->isStaticDtorDeclaration())
- return false;
- if (member->isStaticCtorDeclaration())
- return false;
- }
-
- // but if it passes through this gauntlet... it should be fine. D code will
- // see only the eponymous member, outside stuff can never access it, even through
- // reflection; the outside world ought to be none the wiser. Even dmd should be
- // able to simply free the memory of everything except the final result.
-
- return true;
-}
-
-/***********************************************
- * Returns true if this is not instantiated in non-root module, and
- * is a part of non-speculative instantiatiation.
- *
- * Note: minst does not stabilize until semantic analysis is completed,
- * so don't call this function during semantic analysis to return precise result.
- */
-bool TemplateInstance::needsCodegen()
-{
- if (!minst)
- {
- // If this is a speculative instantiation,
- // 1. do codegen if ancestors really needs codegen.
- // 2. become non-speculative if siblings are not speculative
-
- TemplateInstance *tnext = this->tnext;
- TemplateInstance *tinst = this->tinst;
- // At first, disconnect chain first to prevent infinite recursion.
- this->tnext = NULL;
- this->tinst = NULL;
-
- // Determine necessity of tinst before tnext.
- if (tinst && tinst->needsCodegen())
- {
- minst = tinst->minst; // cache result
- if (global.params.allInst && minst)
- {
- return true;
- }
- assert(minst);
- assert(minst->isRoot() || minst->rootImports());
- return true;
- }
- if (tnext && (tnext->needsCodegen() || tnext->minst))
- {
- minst = tnext->minst; // cache result
- if (global.params.allInst && minst)
- {
- return true;
- }
- assert(minst);
- return minst->isRoot() || minst->rootImports();
- }
-
- // Elide codegen because this is really speculative.
- return false;
- }
-
- if (global.params.allInst)
- {
- return true;
- }
-
- if (isDiscardable(this))
- {
- return false;
- }
-
- /* Even when this is reached to the codegen pass,
- * a non-root nested template should not generate code,
- * due to avoid ODR violation.
- */
- if (enclosing && enclosing->inNonRoot())
- {
- if (tinst)
- {
- bool r = tinst->needsCodegen();
- minst = tinst->minst; // cache result
- return r;
- }
- if (tnext)
- {
- bool r = tnext->needsCodegen();
- minst = tnext->minst; // cache result
- return r;
- }
- return false;
- }
-
- if (global.params.useUnitTests)
- {
- // Prefer instantiations from root modules, to maximize link-ability.
- if (minst->isRoot())
- return true;
-
- TemplateInstance *tnext = this->tnext;
- TemplateInstance *tinst = this->tinst;
- this->tnext = NULL;
- this->tinst = NULL;
-
- if (tinst && tinst->needsCodegen())
- {
- minst = tinst->minst; // cache result
- assert(minst);
- assert(minst->isRoot() || minst->rootImports());
- return true;
- }
- if (tnext && tnext->needsCodegen())
- {
- minst = tnext->minst; // cache result
- assert(minst);
- assert(minst->isRoot() || minst->rootImports());
- return true;
- }
-
- // Bugzilla 2500 case
- if (minst->rootImports())
- return true;
-
- // Elide codegen because this is not included in root instances.
- return false;
- }
- else
- {
- // Prefer instantiations from non-root module, to minimize object code size.
-
- /* If a TemplateInstance is ever instantiated by non-root modules,
- * we do not have to generate code for it,
- * because it will be generated when the non-root module is compiled.
- *
- * But, if the non-root 'minst' imports any root modules, it might still need codegen.
- *
- * The problem is if A imports B, and B imports A, and both A
- * and B instantiate the same template, does the compilation of A
- * or the compilation of B do the actual instantiation?
- *
- * See Bugzilla 2500.
- */
- if (!minst->isRoot() && !minst->rootImports())
- return false;
-
- TemplateInstance *tnext = this->tnext;
- this->tnext = NULL;
-
- if (tnext && !tnext->needsCodegen() && tnext->minst)
- {
- minst = tnext->minst; // cache result
- assert(!minst->isRoot());
- return false;
- }
-
- // Do codegen because this is not included in non-root instances.
- return true;
- }
-}
-
-/* ======================== TemplateMixin ================================ */
-
-TemplateMixin::TemplateMixin(Loc loc, Identifier *ident, TypeQualified *tqual, Objects *tiargs)
- : TemplateInstance(loc, tqual->idents.length ? (Identifier *)tqual->idents[tqual->idents.length - 1]
- : ((TypeIdentifier *)tqual)->ident)
-{
- //printf("TemplateMixin(ident = '%s')\n", ident ? ident->toChars() : "");
- this->ident = ident;
- this->tqual = tqual;
- this->tiargs = tiargs ? tiargs : new Objects();
-}
-
-Dsymbol *TemplateMixin::syntaxCopy(Dsymbol *)
-{
- TemplateMixin *tm = new TemplateMixin(loc, ident,
- (TypeQualified *)tqual->syntaxCopy(), tiargs);
- return TemplateInstance::syntaxCopy(tm);
-}
-
-bool TemplateMixin::findTempDecl(Scope *sc)
-{
- // Follow qualifications to find the TemplateDeclaration
- if (!tempdecl)
- {
- Expression *e;
- Type *t;
- Dsymbol *s;
- tqual->resolve(loc, sc, &e, &t, &s);
- if (!s)
- {
- error("is not defined");
- return false;
- }
- s = s->toAlias();
- tempdecl = s->isTemplateDeclaration();
- OverloadSet *os = s->isOverloadSet();
-
- /* If an OverloadSet, look for a unique member that is a template declaration
- */
- if (os)
- {
- Dsymbol *ds = NULL;
- for (size_t i = 0; i < os->a.length; i++)
- {
- Dsymbol *s2 = os->a[i]->isTemplateDeclaration();
- if (s2)
- {
- if (ds)
- {
- tempdecl = os;
- break;
- }
- ds = s2;
- }
- }
- }
- if (!tempdecl)
- {
- error("%s isn't a template", s->toChars());
- return false;
- }
- }
- assert(tempdecl);
-
- struct ParamFwdResTm
- {
- static int fp(void *param, Dsymbol *s)
- {
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (!td)
- return 0;
-
- TemplateMixin *tm = (TemplateMixin *)param;
- if (td->semanticRun == PASSinit)
- {
- if (td->_scope)
- dsymbolSemantic(td, td->_scope);
- else
- {
- tm->semanticRun = PASSinit;
- return 1;
- }
- }
- return 0;
- }
- };
- // Look for forward references
- OverloadSet *tovers = tempdecl->isOverloadSet();
- size_t overs_dim = tovers ? tovers->a.length : 1;
- for (size_t oi = 0; oi < overs_dim; oi++)
- {
- if (overloadApply(tovers ? tovers->a[oi] : tempdecl, (void *)this, &ParamFwdResTm::fp))
- return false;
- }
- return true;
-}
-
-const char *TemplateMixin::kind() const
-{
- return "mixin";
-}
-
-bool TemplateMixin::oneMember(Dsymbol **ps, Identifier *ident)
-{
- return Dsymbol::oneMember(ps, ident);
-}
-
-int TemplateMixin::apply(Dsymbol_apply_ft_t fp, void *param)
-{
- if (_scope) // if fwd reference
- dsymbolSemantic(this, NULL); // try to resolve it
- if (members)
- {
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- if (s)
- {
- if (s->apply(fp, param))
- return 1;
- }
- }
- }
- return 0;
-}
-
-bool TemplateMixin::hasPointers()
-{
- //printf("TemplateMixin::hasPointers() %s\n", toChars());
-
- if (members)
- {
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- //printf(" s = %s %s\n", s->kind(), s->toChars());
- if (s->hasPointers())
- {
- return true;
- }
- }
- }
- return false;
-}
-
-void TemplateMixin::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion)
-{
- //printf("TemplateMixin::setFieldOffset() %s\n", toChars());
- if (_scope) // if fwd reference
- dsymbolSemantic(this, NULL); // try to resolve it
- if (members)
- {
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- //printf("\t%s\n", s->toChars());
- s->setFieldOffset(ad, poffset, isunion);
- }
- }
-}
-
-const char *TemplateMixin::toChars()
-{
- OutBuffer buf;
- toCBufferInstance(this, &buf);
- return buf.extractChars();
-}
diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d
new file mode 100644
index 00000000000..c3503bbf7d6
--- /dev/null
+++ b/gcc/d/dmd/dtemplate.d
@@ -0,0 +1,8415 @@
+/**
+ * Defines `TemplateDeclaration`, `TemplateInstance` and a few utilities
+ *
+ * This modules holds the two main template types:
+ * `TemplateDeclaration`, which is the user-provided declaration of a template,
+ * and `TemplateInstance`, which is an instance of a `TemplateDeclaration`
+ * with specific arguments.
+ *
+ * Template_Parameter:
+ * Additionally, the classes for template parameters are defined in this module.
+ * The base class, `TemplateParameter`, is inherited by:
+ * - `TemplateTypeParameter`
+ * - `TemplateThisParameter`
+ * - `TemplateValueParameter`
+ * - `TemplateAliasParameter`
+ * - `TemplateTupleParameter`
+ *
+ * Templates_semantic:
+ * The start of the template instantiation process looks like this:
+ * - A `TypeInstance` or `TypeIdentifier` is encountered.
+ * `TypeInstance` have a bang (e.g. `Foo!(arg)`) while `TypeIdentifier` don't.
+ * - A `TemplateInstance` is instantiated
+ * - Semantic is run on the `TemplateInstance` (see `dmd.dsymbolsem`)
+ * - The `TemplateInstance` search for its `TemplateDeclaration`,
+ * runs semantic on the template arguments and deduce the best match
+ * among the possible overloads.
+ * - The `TemplateInstance` search for existing instances with the same
+ * arguments, and uses it if found.
+ * - Otherwise, the rest of semantic is run on the `TemplateInstance`.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dtemplate.d, _dtemplate.d)
+ * Documentation: https://dlang.org/phobos/dmd_dtemplate.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dtemplate.d
+ */
+
+module dmd.dtemplate;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.ast_node;
+import dmd.dcast;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dmangle;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.impcnvtab;
+import dmd.init;
+import dmd.initsem;
+import dmd.mtype;
+import dmd.opover;
+import dmd.root.array;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.semantic2;
+import dmd.semantic3;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.visitor;
+
+import dmd.templateparamsem;
+
+//debug = FindExistingInstance; // print debug stats of findExistingInstance
+private enum LOG = false;
+
+enum IDX_NOTFOUND = 0x12345678;
+
+pure nothrow @nogc
+{
+
+/********************************************
+ * These functions substitute for dynamic_cast. dynamic_cast does not work
+ * on earlier versions of gcc.
+ */
+extern (C++) inout(Expression) isExpression(inout RootObject o)
+{
+ //return dynamic_cast<Expression *>(o);
+ if (!o || o.dyncast() != DYNCAST.expression)
+ return null;
+ return cast(inout(Expression))o;
+}
+
+extern (C++) inout(Dsymbol) isDsymbol(inout RootObject o)
+{
+ //return dynamic_cast<Dsymbol *>(o);
+ if (!o || o.dyncast() != DYNCAST.dsymbol)
+ return null;
+ return cast(inout(Dsymbol))o;
+}
+
+extern (C++) inout(Type) isType(inout RootObject o)
+{
+ //return dynamic_cast<Type *>(o);
+ if (!o || o.dyncast() != DYNCAST.type)
+ return null;
+ return cast(inout(Type))o;
+}
+
+extern (C++) inout(Tuple) isTuple(inout RootObject o)
+{
+ //return dynamic_cast<Tuple *>(o);
+ if (!o || o.dyncast() != DYNCAST.tuple)
+ return null;
+ return cast(inout(Tuple))o;
+}
+
+extern (C++) inout(Parameter) isParameter(inout RootObject o)
+{
+ //return dynamic_cast<Parameter *>(o);
+ if (!o || o.dyncast() != DYNCAST.parameter)
+ return null;
+ return cast(inout(Parameter))o;
+}
+
+extern (C++) inout(TemplateParameter) isTemplateParameter(inout RootObject o)
+{
+ if (!o || o.dyncast() != DYNCAST.templateparameter)
+ return null;
+ return cast(inout(TemplateParameter))o;
+}
+
+/**************************************
+ * Is this Object an error?
+ */
+extern (C++) bool isError(const RootObject o)
+{
+ if (const t = isType(o))
+ return (t.ty == Terror);
+ if (const e = isExpression(o))
+ return (e.op == TOK.error || !e.type || e.type.ty == Terror);
+ if (const v = isTuple(o))
+ return arrayObjectIsError(&v.objects);
+ const s = isDsymbol(o);
+ assert(s);
+ if (s.errors)
+ return true;
+ return s.parent ? isError(s.parent) : false;
+}
+
+/**************************************
+ * Are any of the Objects an error?
+ */
+bool arrayObjectIsError(const Objects* args)
+{
+ foreach (const o; *args)
+ {
+ if (isError(o))
+ return true;
+ }
+ return false;
+}
+
+/***********************
+ * Try to get arg as a type.
+ */
+inout(Type) getType(inout RootObject o)
+{
+ inout t = isType(o);
+ if (!t)
+ {
+ if (inout e = isExpression(o))
+ return e.type;
+ }
+ return t;
+}
+
+}
+
+Dsymbol getDsymbol(RootObject oarg)
+{
+ //printf("getDsymbol()\n");
+ //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg));
+ if (auto ea = isExpression(oarg))
+ {
+ // Try to convert Expression to symbol
+ if (auto ve = ea.isVarExp())
+ return ve.var;
+ else if (auto fe = ea.isFuncExp())
+ return fe.td ? fe.td : fe.fd;
+ else if (auto te = ea.isTemplateExp())
+ return te.td;
+ else if (auto te = ea.isScopeExp())
+ return te.sds;
+ else
+ return null;
+ }
+ else
+ {
+ // Try to convert Type to symbol
+ if (auto ta = isType(oarg))
+ return ta.toDsymbol(null);
+ else
+ return isDsymbol(oarg); // if already a symbol
+ }
+}
+
+
+private Expression getValue(ref Dsymbol s)
+{
+ if (s)
+ {
+ if (VarDeclaration v = s.isVarDeclaration())
+ {
+ if (v.storage_class & STC.manifest)
+ return v.getConstInitializer();
+ }
+ }
+ return null;
+}
+
+/***********************
+ * Try to get value from manifest constant
+ */
+private Expression getValue(Expression e)
+{
+ if (!e)
+ return null;
+ if (auto ve = e.isVarExp())
+ {
+ if (auto v = ve.var.isVarDeclaration())
+ {
+ if (v.storage_class & STC.manifest)
+ {
+ e = v.getConstInitializer();
+ }
+ }
+ }
+ return e;
+}
+
+private Expression getExpression(RootObject o)
+{
+ auto s = isDsymbol(o);
+ return s ? .getValue(s) : .getValue(isExpression(o));
+}
+
+/******************************
+ * If o1 matches o2, return true.
+ * Else, return false.
+ */
+private bool match(RootObject o1, RootObject o2)
+{
+ enum log = false;
+
+ static if (log)
+ {
+ printf("match() o1 = %p %s (%d), o2 = %p %s (%d)\n",
+ o1, o1.toChars(), o1.dyncast(), o2, o2.toChars(), o2.dyncast());
+ }
+
+ /* A proper implementation of the various equals() overrides
+ * should make it possible to just do o1.equals(o2), but
+ * we'll do that another day.
+ */
+ /* Manifest constants should be compared by their values,
+ * at least in template arguments.
+ */
+
+ if (auto t1 = isType(o1))
+ {
+ auto t2 = isType(o2);
+ if (!t2)
+ goto Lnomatch;
+
+ static if (log)
+ {
+ printf("\tt1 = %s\n", t1.toChars());
+ printf("\tt2 = %s\n", t2.toChars());
+ }
+ if (!t1.equals(t2))
+ goto Lnomatch;
+
+ goto Lmatch;
+ }
+ if (auto e1 = getExpression(o1))
+ {
+ auto e2 = getExpression(o2);
+ if (!e2)
+ goto Lnomatch;
+
+ static if (log)
+ {
+ printf("\te1 = %s '%s' %s\n", e1.type ? e1.type.toChars() : "null", Token.toChars(e1.op), e1.toChars());
+ printf("\te2 = %s '%s' %s\n", e2.type ? e2.type.toChars() : "null", Token.toChars(e2.op), e2.toChars());
+ }
+
+ // two expressions can be equal although they do not have the same
+ // type; that happens when they have the same value. So check type
+ // as well as expression equality to ensure templates are properly
+ // matched.
+ if (!(e1.type && e2.type && e1.type.equals(e2.type)) || !e1.equals(e2))
+ goto Lnomatch;
+
+ goto Lmatch;
+ }
+ if (auto s1 = isDsymbol(o1))
+ {
+ auto s2 = isDsymbol(o2);
+ if (!s2)
+ goto Lnomatch;
+
+ static if (log)
+ {
+ printf("\ts1 = %s \n", s1.kind(), s1.toChars());
+ printf("\ts2 = %s \n", s2.kind(), s2.toChars());
+ }
+ if (!s1.equals(s2))
+ goto Lnomatch;
+ if (s1.parent != s2.parent && !s1.isFuncDeclaration() && !s2.isFuncDeclaration())
+ goto Lnomatch;
+
+ goto Lmatch;
+ }
+ if (auto u1 = isTuple(o1))
+ {
+ auto u2 = isTuple(o2);
+ if (!u2)
+ goto Lnomatch;
+
+ static if (log)
+ {
+ printf("\tu1 = %s\n", u1.toChars());
+ printf("\tu2 = %s\n", u2.toChars());
+ }
+ if (!arrayObjectMatch(&u1.objects, &u2.objects))
+ goto Lnomatch;
+
+ goto Lmatch;
+ }
+Lmatch:
+ static if (log)
+ printf("\t. match\n");
+ return true;
+
+Lnomatch:
+ static if (log)
+ printf("\t. nomatch\n");
+ return false;
+}
+
+/************************************
+ * Match an array of them.
+ */
+private bool arrayObjectMatch(Objects* oa1, Objects* oa2)
+{
+ if (oa1 == oa2)
+ return true;
+ if (oa1.dim != oa2.dim)
+ return false;
+ immutable oa1dim = oa1.dim;
+ auto oa1d = (*oa1)[].ptr;
+ auto oa2d = (*oa2)[].ptr;
+ foreach (j; 0 .. oa1dim)
+ {
+ RootObject o1 = oa1d[j];
+ RootObject o2 = oa2d[j];
+ if (!match(o1, o2))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+/************************************
+ * Return hash of Objects.
+ */
+private size_t arrayObjectHash(Objects* oa1)
+{
+ import dmd.root.hash : mixHash;
+
+ size_t hash = 0;
+ foreach (o1; *oa1)
+ {
+ /* Must follow the logic of match()
+ */
+ if (auto t1 = isType(o1))
+ hash = mixHash(hash, cast(size_t)t1.deco);
+ else if (auto e1 = getExpression(o1))
+ hash = mixHash(hash, expressionHash(e1));
+ else if (auto s1 = isDsymbol(o1))
+ {
+ auto fa1 = s1.isFuncAliasDeclaration();
+ if (fa1)
+ s1 = fa1.toAliasFunc();
+ hash = mixHash(hash, mixHash(cast(size_t)cast(void*)s1.getIdent(), cast(size_t)cast(void*)s1.parent));
+ }
+ else if (auto u1 = isTuple(o1))
+ hash = mixHash(hash, arrayObjectHash(&u1.objects));
+ }
+ return hash;
+}
+
+
+/************************************
+ * Computes hash of expression.
+ * Handles all Expression classes and MUST match their equals method,
+ * i.e. e1.equals(e2) implies expressionHash(e1) == expressionHash(e2).
+ */
+private size_t expressionHash(Expression e)
+{
+ import dmd.root.ctfloat : CTFloat;
+ import dmd.root.hash : calcHash, mixHash;
+
+ switch (e.op)
+ {
+ case TOK.int64:
+ return cast(size_t) e.isIntegerExp().getInteger();
+
+ case TOK.float64:
+ return CTFloat.hash(e.isRealExp().value);
+
+ case TOK.complex80:
+ auto ce = e.isComplexExp();
+ return mixHash(CTFloat.hash(ce.toReal), CTFloat.hash(ce.toImaginary));
+
+ case TOK.identifier:
+ return cast(size_t)cast(void*) e.isIdentifierExp().ident;
+
+ case TOK.null_:
+ return cast(size_t)cast(void*) e.isNullExp().type;
+
+ case TOK.string_:
+ return calcHash(e.isStringExp.peekData());
+
+ case TOK.tuple:
+ {
+ auto te = e.isTupleExp();
+ size_t hash = 0;
+ hash += te.e0 ? expressionHash(te.e0) : 0;
+ foreach (elem; *te.exps)
+ hash = mixHash(hash, expressionHash(elem));
+ return hash;
+ }
+
+ case TOK.arrayLiteral:
+ {
+ auto ae = e.isArrayLiteralExp();
+ size_t hash;
+ foreach (i; 0 .. ae.elements.dim)
+ hash = mixHash(hash, expressionHash(ae[i]));
+ return hash;
+ }
+
+ case TOK.assocArrayLiteral:
+ {
+ auto ae = e.isAssocArrayLiteralExp();
+ size_t hash;
+ foreach (i; 0 .. ae.keys.dim)
+ // reduction needs associative op as keys are unsorted (use XOR)
+ hash ^= mixHash(expressionHash((*ae.keys)[i]), expressionHash((*ae.values)[i]));
+ return hash;
+ }
+
+ case TOK.structLiteral:
+ {
+ auto se = e.isStructLiteralExp();
+ size_t hash;
+ foreach (elem; *se.elements)
+ hash = mixHash(hash, elem ? expressionHash(elem) : 0);
+ return hash;
+ }
+
+ case TOK.variable:
+ return cast(size_t)cast(void*) e.isVarExp().var;
+
+ case TOK.function_:
+ return cast(size_t)cast(void*) e.isFuncExp().fd;
+
+ default:
+ // no custom equals for this expression
+ assert((&e.equals).funcptr is &RootObject.equals);
+ // equals based on identity
+ return cast(size_t)cast(void*) e;
+ }
+}
+
+RootObject objectSyntaxCopy(RootObject o)
+{
+ if (!o)
+ return null;
+ if (Type t = isType(o))
+ return t.syntaxCopy();
+ if (Expression e = isExpression(o))
+ return e.syntaxCopy();
+ return o;
+}
+
+extern (C++) final class Tuple : RootObject
+{
+ Objects objects;
+
+ extern (D) this() {}
+
+ /**
+ Params:
+ numObjects = The initial number of objects.
+ */
+ extern (D) this(size_t numObjects)
+ {
+ objects.setDim(numObjects);
+ }
+
+ // kludge for template.isType()
+ override DYNCAST dyncast() const
+ {
+ return DYNCAST.tuple;
+ }
+
+ override const(char)* toChars() const
+ {
+ return objects.toChars();
+ }
+}
+
+struct TemplatePrevious
+{
+ TemplatePrevious* prev;
+ Scope* sc;
+ Objects* dedargs;
+}
+
+/***********************************************************
+ * [mixin] template Identifier (parameters) [Constraint]
+ * https://dlang.org/spec/template.html
+ * https://dlang.org/spec/template-mixin.html
+ */
+extern (C++) final class TemplateDeclaration : ScopeDsymbol
+{
+ import dmd.root.array : Array;
+
+ TemplateParameters* parameters; // array of TemplateParameter's
+ TemplateParameters* origParameters; // originals for Ddoc
+
+ Expression constraint;
+
+ // Hash table to look up TemplateInstance's of this TemplateDeclaration
+ TemplateInstance[TemplateInstanceBox] instances;
+
+ TemplateDeclaration overnext; // next overloaded TemplateDeclaration
+ TemplateDeclaration overroot; // first in overnext list
+ FuncDeclaration funcroot; // first function in unified overload list
+
+ Dsymbol onemember; // if !=null then one member of this template
+
+ bool literal; // this template declaration is a literal
+ bool ismixin; // this is a mixin template declaration
+ bool isstatic; // this is static template declaration
+ bool isTrivialAliasSeq; /// matches pattern `template AliasSeq(T...) { alias AliasSeq = T; }`
+ bool isTrivialAlias; /// matches pattern `template Alias(T) { alias Alias = qualifiers(T); }`
+ bool deprecated_; /// this template declaration is deprecated
+ Visibility visibility;
+ int inuse; /// for recursive expansion detection
+
+ // threaded list of previous instantiation attempts on stack
+ TemplatePrevious* previous;
+
+ private Expression lastConstraint; /// the constraint after the last failed evaluation
+ private Array!Expression lastConstraintNegs; /// its negative parts
+ private Objects* lastConstraintTiargs; /// template instance arguments for `lastConstraint`
+
+ extern (D) this(const ref Loc loc, Identifier ident, TemplateParameters* parameters, Expression constraint, Dsymbols* decldefs, bool ismixin = false, bool literal = false)
+ {
+ super(loc, ident);
+ static if (LOG)
+ {
+ printf("TemplateDeclaration(this = %p, id = '%s')\n", this, ident.toChars());
+ }
+ version (none)
+ {
+ if (parameters)
+ for (int i = 0; i < parameters.dim; i++)
+ {
+ TemplateParameter tp = (*parameters)[i];
+ //printf("\tparameter[%d] = %p\n", i, tp);
+ TemplateTypeParameter ttp = tp.isTemplateTypeParameter();
+ if (ttp)
+ {
+ printf("\tparameter[%d] = %s : %s\n", i, tp.ident.toChars(), ttp.specType ? ttp.specType.toChars() : "");
+ }
+ }
+ }
+ this.parameters = parameters;
+ this.origParameters = parameters;
+ this.constraint = constraint;
+ this.members = decldefs;
+ this.literal = literal;
+ this.ismixin = ismixin;
+ this.isstatic = true;
+ this.visibility = Visibility(Visibility.Kind.undefined);
+
+ // Compute in advance for Ddoc's use
+ // https://issues.dlang.org/show_bug.cgi?id=11153: ident could be NULL if parsing fails.
+ if (!members || !ident)
+ return;
+
+ Dsymbol s;
+ if (!Dsymbol.oneMembers(members, &s, ident) || !s)
+ return;
+
+ onemember = s;
+ s.parent = this;
+
+ /* Set isTrivialAliasSeq if this fits the pattern:
+ * template AliasSeq(T...) { alias AliasSeq = T; }
+ * or set isTrivialAlias if this fits the pattern:
+ * template Alias(T) { alias Alias = qualifiers(T); }
+ */
+ if (!(parameters && parameters.length == 1))
+ return;
+
+ auto ad = s.isAliasDeclaration();
+ if (!ad || !ad.type)
+ return;
+
+ auto ti = ad.type.isTypeIdentifier();
+
+ if (!ti || ti.idents.length != 0)
+ return;
+
+ if (auto ttp = (*parameters)[0].isTemplateTupleParameter())
+ {
+ if (ti.ident is ttp.ident &&
+ ti.mod == 0)
+ {
+ //printf("found isTrivialAliasSeq %s %s\n", s.toChars(), ad.type.toChars());
+ isTrivialAliasSeq = true;
+ }
+ }
+ else if (auto ttp = (*parameters)[0].isTemplateTypeParameter())
+ {
+ if (ti.ident is ttp.ident)
+ {
+ //printf("found isTrivialAlias %s %s\n", s.toChars(), ad.type.toChars());
+ isTrivialAlias = true;
+ }
+ }
+ }
+
+ override TemplateDeclaration syntaxCopy(Dsymbol)
+ {
+ //printf("TemplateDeclaration.syntaxCopy()\n");
+ TemplateParameters* p = null;
+ if (parameters)
+ {
+ p = new TemplateParameters(parameters.dim);
+ foreach (i, ref param; *p)
+ param = (*parameters)[i].syntaxCopy();
+ }
+ return new TemplateDeclaration(loc, ident, p, constraint ? constraint.syntaxCopy() : null, Dsymbol.arraySyntaxCopy(members), ismixin, literal);
+ }
+
+ /**********************************
+ * Overload existing TemplateDeclaration 'this' with the new one 's'.
+ * Return true if successful; i.e. no conflict.
+ */
+ override bool overloadInsert(Dsymbol s)
+ {
+ static if (LOG)
+ {
+ printf("TemplateDeclaration.overloadInsert('%s')\n", s.toChars());
+ }
+ FuncDeclaration fd = s.isFuncDeclaration();
+ if (fd)
+ {
+ if (funcroot)
+ return funcroot.overloadInsert(fd);
+ funcroot = fd;
+ return funcroot.overloadInsert(this);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=15795
+ // if candidate is an alias and its sema is not run then
+ // insertion can fail because the thing it alias is not known
+ if (AliasDeclaration ad = s.isAliasDeclaration())
+ {
+ if (s._scope)
+ aliasSemantic(ad, s._scope);
+ if (ad.aliassym && ad.aliassym is this)
+ return false;
+ }
+ TemplateDeclaration td = s.toAlias().isTemplateDeclaration();
+ if (!td)
+ return false;
+
+ TemplateDeclaration pthis = this;
+ TemplateDeclaration* ptd;
+ for (ptd = &pthis; *ptd; ptd = &(*ptd).overnext)
+ {
+ }
+
+ td.overroot = this;
+ *ptd = td;
+ static if (LOG)
+ {
+ printf("\ttrue: no conflict\n");
+ }
+ return true;
+ }
+
+ override bool hasStaticCtorOrDtor()
+ {
+ return false; // don't scan uninstantiated templates
+ }
+
+ override const(char)* kind() const
+ {
+ return (onemember && onemember.isAggregateDeclaration()) ? onemember.kind() : "template";
+ }
+
+ override const(char)* toChars() const
+ {
+ return toCharsMaybeConstraints(true);
+ }
+
+ /****************************
+ * Similar to `toChars`, but does not print the template constraints
+ */
+ const(char)* toCharsNoConstraints() const
+ {
+ return toCharsMaybeConstraints(false);
+ }
+
+ const(char)* toCharsMaybeConstraints(bool includeConstraints) const
+ {
+ if (literal)
+ return Dsymbol.toChars();
+
+ OutBuffer buf;
+ HdrGenState hgs;
+
+ buf.writestring(ident.toString());
+ buf.writeByte('(');
+ foreach (i, const tp; *parameters)
+ {
+ if (i)
+ buf.writestring(", ");
+ .toCBuffer(tp, &buf, &hgs);
+ }
+ buf.writeByte(')');
+
+ if (onemember)
+ {
+ const FuncDeclaration fd = onemember.isFuncDeclaration();
+ if (fd && fd.type)
+ {
+ TypeFunction tf = cast(TypeFunction)fd.type;
+ buf.writestring(parametersTypeToChars(tf.parameterList));
+ }
+ }
+
+ if (includeConstraints &&
+ constraint)
+ {
+ buf.writestring(" if (");
+ .toCBuffer(constraint, &buf, &hgs);
+ buf.writeByte(')');
+ }
+
+ return buf.extractChars();
+ }
+
+ override Visibility visible() pure nothrow @nogc @safe
+ {
+ return visibility;
+ }
+
+ /****************************
+ * Check to see if constraint is satisfied.
+ */
+ extern (D) bool evaluateConstraint(TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd)
+ {
+ /* Detect recursive attempts to instantiate this template declaration,
+ * https://issues.dlang.org/show_bug.cgi?id=4072
+ * void foo(T)(T x) if (is(typeof(foo(x)))) { }
+ * static assert(!is(typeof(foo(7))));
+ * Recursive attempts are regarded as a constraint failure.
+ */
+ /* There's a chicken-and-egg problem here. We don't know yet if this template
+ * instantiation will be a local one (enclosing is set), and we won't know until
+ * after selecting the correct template. Thus, function we're nesting inside
+ * is not on the sc scope chain, and this can cause errors in FuncDeclaration.getLevel().
+ * Workaround the problem by setting a flag to relax the checking on frame errors.
+ */
+
+ for (TemplatePrevious* p = previous; p; p = p.prev)
+ {
+ if (!arrayObjectMatch(p.dedargs, dedargs))
+ continue;
+ //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars());
+ /* It must be a subscope of p.sc, other scope chains are not recursive
+ * instantiations.
+ * the chain of enclosing scopes is broken by paramscope (its enclosing
+ * scope is _scope, but paramscope.callsc is the instantiating scope). So
+ * it's good enough to check the chain of callsc
+ */
+ for (Scope* scx = paramscope.callsc; scx; scx = scx.callsc)
+ {
+ // The first scx might be identical for nested eponymeous templates, e.g.
+ // template foo() { void foo()() {...} }
+ if (scx == p.sc && scx !is paramscope.callsc)
+ return false;
+ }
+ /* BUG: should also check for ref param differences
+ */
+ }
+
+ TemplatePrevious pr;
+ pr.prev = previous;
+ pr.sc = paramscope.callsc;
+ pr.dedargs = dedargs;
+ previous = &pr; // add this to threaded list
+
+ Scope* scx = paramscope.push(ti);
+ scx.parent = ti;
+ scx.tinst = null;
+ scx.minst = null;
+ // Set SCOPE.constraint before declaring function parameters for the static condition
+ // (previously, this was immediately before calling evalStaticCondition), so the
+ // semantic pass knows not to issue deprecation warnings for these throw-away decls.
+ // https://issues.dlang.org/show_bug.cgi?id=21831
+ scx.flags |= SCOPE.constraint;
+
+ assert(!ti.symtab);
+ if (fd)
+ {
+ /* Declare all the function parameters as variables and add them to the scope
+ * Making parameters is similar to FuncDeclaration.semantic3
+ */
+ auto tf = fd.type.isTypeFunction();
+
+ scx.parent = fd;
+
+ Parameters* fparameters = tf.parameterList.parameters;
+ const nfparams = tf.parameterList.length;
+ foreach (i, fparam; tf.parameterList)
+ {
+ fparam.storageClass &= (STC.IOR | STC.lazy_ | STC.final_ | STC.TYPECTOR | STC.nodtor);
+ fparam.storageClass |= STC.parameter;
+ if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nfparams)
+ {
+ fparam.storageClass |= STC.variadic;
+ /* Don't need to set STC.scope_ because this will only
+ * be evaluated at compile time
+ */
+ }
+ }
+ foreach (fparam; *fparameters)
+ {
+ if (!fparam.ident)
+ continue;
+ // don't add it, if it has no name
+ auto v = new VarDeclaration(loc, fparam.type, fparam.ident, null);
+ fparam.storageClass |= STC.parameter;
+ v.storage_class = fparam.storageClass;
+ v.dsymbolSemantic(scx);
+ if (!ti.symtab)
+ ti.symtab = new DsymbolTable();
+ if (!scx.insert(v))
+ error("parameter `%s.%s` is already defined", toChars(), v.toChars());
+ else
+ v.parent = fd;
+ }
+ if (isstatic)
+ fd.storage_class |= STC.static_;
+ fd.declareThis(scx);
+ }
+
+ lastConstraint = constraint.syntaxCopy();
+ lastConstraintTiargs = ti.tiargs;
+ lastConstraintNegs.setDim(0);
+
+ import dmd.staticcond;
+
+ assert(ti.inst is null);
+ ti.inst = ti; // temporary instantiation to enable genIdent()
+ bool errors;
+ const bool result = evalStaticCondition(scx, constraint, lastConstraint, errors, &lastConstraintNegs);
+ if (result || errors)
+ {
+ lastConstraint = null;
+ lastConstraintTiargs = null;
+ lastConstraintNegs.setDim(0);
+ }
+ ti.inst = null;
+ ti.symtab = null;
+ scx = scx.pop();
+ previous = pr.prev; // unlink from threaded list
+ if (errors)
+ return false;
+ return result;
+ }
+
+ /****************************
+ * Destructively get the error message from the last constraint evaluation
+ * Params:
+ * tip = tip to show after printing all overloads
+ */
+ const(char)* getConstraintEvalError(ref const(char)* tip)
+ {
+ import dmd.staticcond;
+
+ // there will be a full tree view in verbose mode, and more compact list in the usual
+ const full = global.params.verbose;
+ uint count;
+ const msg = visualizeStaticCondition(constraint, lastConstraint, lastConstraintNegs[], full, count);
+ scope (exit)
+ {
+ lastConstraint = null;
+ lastConstraintTiargs = null;
+ lastConstraintNegs.setDim(0);
+ }
+ if (!msg)
+ return null;
+
+ OutBuffer buf;
+
+ assert(parameters && lastConstraintTiargs);
+ if (parameters.length > 0)
+ {
+ formatParamsWithTiargs(*lastConstraintTiargs, buf);
+ buf.writenl();
+ }
+ if (!full)
+ {
+ // choosing singular/plural
+ const s = (count == 1) ?
+ " must satisfy the following constraint:" :
+ " must satisfy one of the following constraints:";
+ buf.writestring(s);
+ buf.writenl();
+ // the constraints
+ buf.writeByte('`');
+ buf.writestring(msg);
+ buf.writeByte('`');
+ }
+ else
+ {
+ buf.writestring(" whose parameters have the following constraints:");
+ buf.writenl();
+ const sep = " `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`";
+ buf.writestring(sep);
+ buf.writenl();
+ // the constraints
+ buf.writeByte('`');
+ buf.writestring(msg);
+ buf.writeByte('`');
+ buf.writestring(sep);
+ tip = "not satisfied constraints are marked with `>`";
+ }
+ return buf.extractChars();
+ }
+
+ private void formatParamsWithTiargs(ref Objects tiargs, ref OutBuffer buf)
+ {
+ buf.writestring(" with `");
+
+ // write usual arguments line-by-line
+ // skips trailing default ones - they are not present in `tiargs`
+ const bool variadic = isVariadic() !is null;
+ const end = cast(int)parameters.length - (variadic ? 1 : 0);
+ uint i;
+ for (; i < tiargs.length && i < end; i++)
+ {
+ if (i > 0)
+ {
+ buf.writeByte(',');
+ buf.writenl();
+ buf.writestring(" ");
+ }
+ buf.write((*parameters)[i]);
+ buf.writestring(" = ");
+ buf.write(tiargs[i]);
+ }
+ // write remaining variadic arguments on the last line
+ if (variadic)
+ {
+ if (i > 0)
+ {
+ buf.writeByte(',');
+ buf.writenl();
+ buf.writestring(" ");
+ }
+ buf.write((*parameters)[end]);
+ buf.writestring(" = ");
+ buf.writeByte('(');
+ if (cast(int)tiargs.length - end > 0)
+ {
+ buf.write(tiargs[end]);
+ foreach (j; parameters.length .. tiargs.length)
+ {
+ buf.writestring(", ");
+ buf.write(tiargs[j]);
+ }
+ }
+ buf.writeByte(')');
+ }
+ buf.writeByte('`');
+ }
+
+ /******************************
+ * Create a scope for the parameters of the TemplateInstance
+ * `ti` in the parent scope sc from the ScopeDsymbol paramsym.
+ *
+ * If paramsym is null a new ScopeDsymbol is used in place of
+ * paramsym.
+ * Params:
+ * ti = the TemplateInstance whose parameters to generate the scope for.
+ * sc = the parent scope of ti
+ * Returns:
+ * a scope for the parameters of ti
+ */
+ Scope* scopeForTemplateParameters(TemplateInstance ti, Scope* sc)
+ {
+ ScopeDsymbol paramsym = new ScopeDsymbol();
+ paramsym.parent = _scope.parent;
+ Scope* paramscope = _scope.push(paramsym);
+ paramscope.tinst = ti;
+ paramscope.minst = sc.minst;
+ paramscope.callsc = sc;
+ paramscope.stc = 0;
+ return paramscope;
+ }
+
+ /***************************************
+ * Given that ti is an instance of this TemplateDeclaration,
+ * deduce the types of the parameters to this, and store
+ * those deduced types in dedtypes[].
+ * Input:
+ * flag 1: don't do semantic() because of dummy types
+ * 2: don't change types in matchArg()
+ * Output:
+ * dedtypes deduced arguments
+ * Return match level.
+ */
+ extern (D) MATCH matchWithInstance(Scope* sc, TemplateInstance ti, Objects* dedtypes, Expressions* fargs, int flag)
+ {
+ enum LOGM = 0;
+ static if (LOGM)
+ {
+ printf("\n+TemplateDeclaration.matchWithInstance(this = %s, ti = %s, flag = %d)\n", toChars(), ti.toChars(), flag);
+ }
+ version (none)
+ {
+ printf("dedtypes.dim = %d, parameters.dim = %d\n", dedtypes.dim, parameters.dim);
+ if (ti.tiargs.dim)
+ printf("ti.tiargs.dim = %d, [0] = %p\n", ti.tiargs.dim, (*ti.tiargs)[0]);
+ }
+ MATCH nomatch()
+ {
+ static if (LOGM)
+ {
+ printf(" no match\n");
+ }
+ return MATCH.nomatch;
+ }
+ MATCH m;
+ size_t dedtypes_dim = dedtypes.dim;
+
+ dedtypes.zero();
+
+ if (errors)
+ return MATCH.nomatch;
+
+ size_t parameters_dim = parameters.dim;
+ int variadic = isVariadic() !is null;
+
+ // If more arguments than parameters, no match
+ if (ti.tiargs.dim > parameters_dim && !variadic)
+ {
+ static if (LOGM)
+ {
+ printf(" no match: more arguments than parameters\n");
+ }
+ return MATCH.nomatch;
+ }
+
+ assert(dedtypes_dim == parameters_dim);
+ assert(dedtypes_dim >= ti.tiargs.dim || variadic);
+
+ assert(_scope);
+
+ // Set up scope for template parameters
+ Scope* paramscope = scopeForTemplateParameters(ti,sc);
+
+ // Attempt type deduction
+ m = MATCH.exact;
+ for (size_t i = 0; i < dedtypes_dim; i++)
+ {
+ MATCH m2;
+ TemplateParameter tp = (*parameters)[i];
+ Declaration sparam;
+
+ //printf("\targument [%d]\n", i);
+ static if (LOGM)
+ {
+ //printf("\targument [%d] is %s\n", i, oarg ? oarg.toChars() : "null");
+ TemplateTypeParameter ttp = tp.isTemplateTypeParameter();
+ if (ttp)
+ printf("\tparameter[%d] is %s : %s\n", i, tp.ident.toChars(), ttp.specType ? ttp.specType.toChars() : "");
+ }
+
+ inuse++;
+ m2 = tp.matchArg(ti.loc, paramscope, ti.tiargs, i, parameters, dedtypes, &sparam);
+ inuse--;
+ //printf("\tm2 = %d\n", m2);
+ if (m2 == MATCH.nomatch)
+ {
+ version (none)
+ {
+ printf("\tmatchArg() for parameter %i failed\n", i);
+ }
+ return nomatch();
+ }
+
+ if (m2 < m)
+ m = m2;
+
+ if (!flag)
+ sparam.dsymbolSemantic(paramscope);
+ if (!paramscope.insert(sparam)) // TODO: This check can make more early
+ {
+ // in TemplateDeclaration.semantic, and
+ // then we don't need to make sparam if flags == 0
+ return nomatch();
+ }
+ }
+
+ if (!flag)
+ {
+ /* Any parameter left without a type gets the type of
+ * its corresponding arg
+ */
+ foreach (i, ref dedtype; *dedtypes)
+ {
+ if (!dedtype)
+ {
+ assert(i < ti.tiargs.dim);
+ dedtype = cast(Type)(*ti.tiargs)[i];
+ }
+ }
+ }
+
+ if (m > MATCH.nomatch && constraint && !flag)
+ {
+ if (ti.hasNestedArgs(ti.tiargs, this.isstatic)) // TODO: should gag error
+ ti.parent = ti.enclosing;
+ else
+ ti.parent = this.parent;
+
+ // Similar to doHeaderInstantiation
+ FuncDeclaration fd = onemember ? onemember.isFuncDeclaration() : null;
+ if (fd)
+ {
+ TypeFunction tf = fd.type.isTypeFunction().syntaxCopy();
+
+ fd = new FuncDeclaration(fd.loc, fd.endloc, fd.ident, fd.storage_class, tf);
+ fd.parent = ti;
+ fd.inferRetType = true;
+
+ // Shouldn't run semantic on default arguments and return type.
+ foreach (ref param; *tf.parameterList.parameters)
+ param.defaultArg = null;
+
+ tf.next = null;
+ tf.incomplete = true;
+
+ // Resolve parameter types and 'auto ref's.
+ tf.fargs = fargs;
+ uint olderrors = global.startGagging();
+ fd.type = tf.typeSemantic(loc, paramscope);
+ global.endGagging(olderrors);
+ if (fd.type.ty != Tfunction)
+ return nomatch();
+ fd.originalType = fd.type; // for mangling
+ }
+
+ // TODO: dedtypes => ti.tiargs ?
+ if (!evaluateConstraint(ti, sc, paramscope, dedtypes, fd))
+ return nomatch();
+ }
+
+ static if (LOGM)
+ {
+ // Print out the results
+ printf("--------------------------\n");
+ printf("template %s\n", toChars());
+ printf("instance %s\n", ti.toChars());
+ if (m > MATCH.nomatch)
+ {
+ for (size_t i = 0; i < dedtypes_dim; i++)
+ {
+ TemplateParameter tp = (*parameters)[i];
+ RootObject oarg;
+ printf(" [%d]", i);
+ if (i < ti.tiargs.dim)
+ oarg = (*ti.tiargs)[i];
+ else
+ oarg = null;
+ tp.print(oarg, (*dedtypes)[i]);
+ }
+ }
+ else
+ return nomatch();
+ }
+ static if (LOGM)
+ {
+ printf(" match = %d\n", m);
+ }
+
+ paramscope.pop();
+ static if (LOGM)
+ {
+ printf("-TemplateDeclaration.matchWithInstance(this = %p, ti = %p) = %d\n", this, ti, m);
+ }
+ return m;
+ }
+
+ /********************************************
+ * Determine partial specialization order of 'this' vs td2.
+ * Returns:
+ * match this is at least as specialized as td2
+ * 0 td2 is more specialized than this
+ */
+ MATCH leastAsSpecialized(Scope* sc, TemplateDeclaration td2, Expressions* fargs)
+ {
+ enum LOG_LEASTAS = 0;
+ static if (LOG_LEASTAS)
+ {
+ printf("%s.leastAsSpecialized(%s)\n", toChars(), td2.toChars());
+ }
+
+ /* This works by taking the template parameters to this template
+ * declaration and feeding them to td2 as if it were a template
+ * instance.
+ * If it works, then this template is at least as specialized
+ * as td2.
+ */
+
+ // Set type arguments to dummy template instance to be types
+ // generated from the parameters to this template declaration
+ auto tiargs = new Objects();
+ tiargs.reserve(parameters.dim);
+ foreach (tp; *parameters)
+ {
+ if (tp.dependent)
+ break;
+ RootObject p = tp.dummyArg();
+ if (!p) //TemplateTupleParameter
+ break;
+
+ tiargs.push(p);
+ }
+ scope TemplateInstance ti = new TemplateInstance(Loc.initial, ident, tiargs); // create dummy template instance
+
+ // Temporary Array to hold deduced types
+ Objects dedtypes = Objects(td2.parameters.dim);
+
+ // Attempt a type deduction
+ MATCH m = td2.matchWithInstance(sc, ti, &dedtypes, fargs, 1);
+ if (m > MATCH.nomatch)
+ {
+ /* A non-variadic template is more specialized than a
+ * variadic one.
+ */
+ TemplateTupleParameter tp = isVariadic();
+ if (tp && !tp.dependent && !td2.isVariadic())
+ goto L1;
+
+ static if (LOG_LEASTAS)
+ {
+ printf(" matches %d, so is least as specialized\n", m);
+ }
+ return m;
+ }
+ L1:
+ static if (LOG_LEASTAS)
+ {
+ printf(" doesn't match, so is not as specialized\n");
+ }
+ return MATCH.nomatch;
+ }
+
+ /*************************************************
+ * Match function arguments against a specific template function.
+ * Input:
+ * ti
+ * sc instantiation scope
+ * fd
+ * tthis 'this' argument if !NULL
+ * fargs arguments to function
+ * Output:
+ * fd Partially instantiated function declaration
+ * ti.tdtypes Expression/Type deduced template arguments
+ * Returns:
+ * match pair of initial and inferred template arguments
+ */
+ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateInstance ti, Scope* sc, ref FuncDeclaration fd, Type tthis, Expressions* fargs)
+ {
+ size_t nfparams;
+ size_t nfargs;
+ size_t ntargs; // array size of tiargs
+ size_t fptupindex = IDX_NOTFOUND;
+ MATCH match = MATCH.exact;
+ MATCH matchTiargs = MATCH.exact;
+ ParameterList fparameters; // function parameter list
+ VarArg fvarargs; // function varargs
+ uint wildmatch = 0;
+ size_t inferStart = 0;
+
+ Loc instLoc = ti.loc;
+ Objects* tiargs = ti.tiargs;
+ auto dedargs = new Objects();
+ Objects* dedtypes = &ti.tdtypes; // for T:T*, the dedargs is the T*, dedtypes is the T
+
+ version (none)
+ {
+ printf("\nTemplateDeclaration.deduceFunctionTemplateMatch() %s\n", toChars());
+ for (size_t i = 0; i < (fargs ? fargs.dim : 0); i++)
+ {
+ Expression e = (*fargs)[i];
+ printf("\tfarg[%d] is %s, type is %s\n", i, e.toChars(), e.type.toChars());
+ }
+ printf("fd = %s\n", fd.toChars());
+ printf("fd.type = %s\n", fd.type.toChars());
+ if (tthis)
+ printf("tthis = %s\n", tthis.toChars());
+ }
+
+ assert(_scope);
+
+ dedargs.setDim(parameters.dim);
+ dedargs.zero();
+
+ dedtypes.setDim(parameters.dim);
+ dedtypes.zero();
+
+ if (errors || fd.errors)
+ return MATCHpair(MATCH.nomatch, MATCH.nomatch);
+
+ // Set up scope for parameters
+ Scope* paramscope = scopeForTemplateParameters(ti,sc);
+
+ MATCHpair nomatch()
+ {
+ paramscope.pop();
+ //printf("\tnomatch\n");
+ return MATCHpair(MATCH.nomatch, MATCH.nomatch);
+ }
+
+ MATCHpair matcherror()
+ {
+ // todo: for the future improvement
+ paramscope.pop();
+ //printf("\terror\n");
+ return MATCHpair(MATCH.nomatch, MATCH.nomatch);
+ }
+ // Mark the parameter scope as deprecated if the templated
+ // function is deprecated (since paramscope.enclosing is the
+ // calling scope already)
+ paramscope.stc |= fd.storage_class & STC.deprecated_;
+
+ TemplateTupleParameter tp = isVariadic();
+ Tuple declaredTuple = null;
+
+ version (none)
+ {
+ for (size_t i = 0; i < dedargs.dim; i++)
+ {
+ printf("\tdedarg[%d] = ", i);
+ RootObject oarg = (*dedargs)[i];
+ if (oarg)
+ printf("%s", oarg.toChars());
+ printf("\n");
+ }
+ }
+
+ ntargs = 0;
+ if (tiargs)
+ {
+ // Set initial template arguments
+ ntargs = tiargs.dim;
+ size_t n = parameters.dim;
+ if (tp)
+ n--;
+ if (ntargs > n)
+ {
+ if (!tp)
+ return nomatch();
+
+ /* The extra initial template arguments
+ * now form the tuple argument.
+ */
+ auto t = new Tuple(ntargs - n);
+ assert(parameters.dim);
+ (*dedargs)[parameters.dim - 1] = t;
+
+ for (size_t i = 0; i < t.objects.dim; i++)
+ {
+ t.objects[i] = (*tiargs)[n + i];
+ }
+ declareParameter(paramscope, tp, t);
+ declaredTuple = t;
+ }
+ else
+ n = ntargs;
+
+ memcpy(dedargs.tdata(), tiargs.tdata(), n * (*dedargs.tdata()).sizeof);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ assert(i < parameters.dim);
+ Declaration sparam = null;
+ MATCH m = (*parameters)[i].matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, &sparam);
+ //printf("\tdeduceType m = %d\n", m);
+ if (m == MATCH.nomatch)
+ return nomatch();
+ if (m < matchTiargs)
+ matchTiargs = m;
+
+ sparam.dsymbolSemantic(paramscope);
+ if (!paramscope.insert(sparam))
+ return nomatch();
+ }
+ if (n < parameters.dim && !declaredTuple)
+ {
+ inferStart = n;
+ }
+ else
+ inferStart = parameters.dim;
+ //printf("tiargs matchTiargs = %d\n", matchTiargs);
+ }
+ version (none)
+ {
+ for (size_t i = 0; i < dedargs.dim; i++)
+ {
+ printf("\tdedarg[%d] = ", i);
+ RootObject oarg = (*dedargs)[i];
+ if (oarg)
+ printf("%s", oarg.toChars());
+ printf("\n");
+ }
+ }
+
+ fparameters = fd.getParameterList();
+ nfparams = fparameters.length; // number of function parameters
+ nfargs = fargs ? fargs.dim : 0; // number of function arguments
+
+ /* Check for match of function arguments with variadic template
+ * parameter, such as:
+ *
+ * void foo(T, A...)(T t, A a);
+ * void main() { foo(1,2,3); }
+ */
+ if (tp) // if variadic
+ {
+ // TemplateTupleParameter always makes most lesser matching.
+ matchTiargs = MATCH.convert;
+
+ if (nfparams == 0 && nfargs != 0) // if no function parameters
+ {
+ if (!declaredTuple)
+ {
+ auto t = new Tuple();
+ //printf("t = %p\n", t);
+ (*dedargs)[parameters.dim - 1] = t;
+ declareParameter(paramscope, tp, t);
+ declaredTuple = t;
+ }
+ }
+ else
+ {
+ /* Figure out which of the function parameters matches
+ * the tuple template parameter. Do this by matching
+ * type identifiers.
+ * Set the index of this function parameter to fptupindex.
+ */
+ for (fptupindex = 0; fptupindex < nfparams; fptupindex++)
+ {
+ auto fparam = (*fparameters.parameters)[fptupindex]; // fparameters[fptupindex] ?
+ if (fparam.type.ty != Tident)
+ continue;
+ TypeIdentifier tid = cast(TypeIdentifier)fparam.type;
+ if (!tp.ident.equals(tid.ident) || tid.idents.dim)
+ continue;
+
+ if (fparameters.varargs != VarArg.none) // variadic function doesn't
+ return nomatch(); // go with variadic template
+
+ goto L1;
+ }
+ fptupindex = IDX_NOTFOUND;
+ L1:
+ }
+ }
+
+ if (toParent().isModule() || (_scope.stc & STC.static_))
+ tthis = null;
+ if (tthis)
+ {
+ bool hasttp = false;
+
+ // Match 'tthis' to any TemplateThisParameter's
+ foreach (param; *parameters)
+ {
+ if (auto ttp = param.isTemplateThisParameter())
+ {
+ hasttp = true;
+
+ Type t = new TypeIdentifier(Loc.initial, ttp.ident);
+ MATCH m = deduceType(tthis, paramscope, t, parameters, dedtypes);
+ if (m == MATCH.nomatch)
+ return nomatch();
+ if (m < match)
+ match = m; // pick worst match
+ }
+ }
+
+ // Match attributes of tthis against attributes of fd
+ if (fd.type && !fd.isCtorDeclaration())
+ {
+ StorageClass stc = _scope.stc | fd.storage_class2;
+ // Propagate parent storage class, https://issues.dlang.org/show_bug.cgi?id=5504
+ Dsymbol p = parent;
+ while (p.isTemplateDeclaration() || p.isTemplateInstance())
+ p = p.parent;
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (ad)
+ stc |= ad.storage_class;
+
+ ubyte mod = fd.type.mod;
+ if (stc & STC.immutable_)
+ mod = MODFlags.immutable_;
+ else
+ {
+ if (stc & (STC.shared_ | STC.synchronized_))
+ mod |= MODFlags.shared_;
+ if (stc & STC.const_)
+ mod |= MODFlags.const_;
+ if (stc & STC.wild)
+ mod |= MODFlags.wild;
+ }
+
+ ubyte thismod = tthis.mod;
+ if (hasttp)
+ mod = MODmerge(thismod, mod);
+ MATCH m = MODmethodConv(thismod, mod);
+ if (m == MATCH.nomatch)
+ return nomatch();
+ if (m < match)
+ match = m;
+ }
+ }
+
+ // Loop through the function parameters
+ {
+ //printf("%s\n\tnfargs = %d, nfparams = %d, tuple_dim = %d\n", toChars(), nfargs, nfparams, declaredTuple ? declaredTuple.objects.dim : 0);
+ //printf("\ttp = %p, fptupindex = %d, found = %d, declaredTuple = %s\n", tp, fptupindex, fptupindex != IDX_NOTFOUND, declaredTuple ? declaredTuple.toChars() : NULL);
+ size_t argi = 0;
+ size_t nfargs2 = nfargs; // nfargs + supplied defaultArgs
+ for (size_t parami = 0; parami < nfparams; parami++)
+ {
+ Parameter fparam = fparameters[parami];
+
+ // Apply function parameter storage classes to parameter types
+ Type prmtype = fparam.type.addStorageClass(fparam.storageClass);
+
+ Expression farg;
+
+ /* See function parameters which wound up
+ * as part of a template tuple parameter.
+ */
+ if (fptupindex != IDX_NOTFOUND && parami == fptupindex)
+ {
+ assert(prmtype.ty == Tident);
+ TypeIdentifier tid = cast(TypeIdentifier)prmtype;
+ if (!declaredTuple)
+ {
+ /* The types of the function arguments
+ * now form the tuple argument.
+ */
+ declaredTuple = new Tuple();
+ (*dedargs)[parameters.dim - 1] = declaredTuple;
+
+ /* Count function parameters with no defaults following a tuple parameter.
+ * void foo(U, T...)(int y, T, U, double, int bar = 0) {} // rem == 2 (U, double)
+ */
+ size_t rem = 0;
+ for (size_t j = parami + 1; j < nfparams; j++)
+ {
+ Parameter p = fparameters[j];
+ if (p.defaultArg)
+ {
+ break;
+ }
+ if (!reliesOnTemplateParameters(p.type, (*parameters)[inferStart .. parameters.dim]))
+ {
+ Type pt = p.type.syntaxCopy().typeSemantic(fd.loc, paramscope);
+ rem += pt.ty == Ttuple ? (cast(TypeTuple)pt).arguments.dim : 1;
+ }
+ else
+ {
+ ++rem;
+ }
+ }
+
+ if (nfargs2 - argi < rem)
+ return nomatch();
+ declaredTuple.objects.setDim(nfargs2 - argi - rem);
+ for (size_t i = 0; i < declaredTuple.objects.dim; i++)
+ {
+ farg = (*fargs)[argi + i];
+
+ // Check invalid arguments to detect errors early.
+ if (farg.op == TOK.error || farg.type.ty == Terror)
+ return nomatch();
+
+ if (!(fparam.storageClass & STC.lazy_) && farg.type.ty == Tvoid)
+ return nomatch();
+
+ Type tt;
+ MATCH m;
+ if (ubyte wm = deduceWildHelper(farg.type, &tt, tid))
+ {
+ wildmatch |= wm;
+ m = MATCH.constant;
+ }
+ else
+ {
+ m = deduceTypeHelper(farg.type, &tt, tid);
+ }
+ if (m == MATCH.nomatch)
+ return nomatch();
+ if (m < match)
+ match = m;
+
+ /* Remove top const for dynamic array types and pointer types
+ */
+ if ((tt.ty == Tarray || tt.ty == Tpointer) && !tt.isMutable() && (!(fparam.storageClass & STC.ref_) || (fparam.storageClass & STC.auto_) && !farg.isLvalue()))
+ {
+ tt = tt.mutableOf();
+ }
+ declaredTuple.objects[i] = tt;
+ }
+ declareParameter(paramscope, tp, declaredTuple);
+ }
+ else
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=6810
+ // If declared tuple is not a type tuple,
+ // it cannot be function parameter types.
+ for (size_t i = 0; i < declaredTuple.objects.dim; i++)
+ {
+ if (!isType(declaredTuple.objects[i]))
+ return nomatch();
+ }
+ }
+ assert(declaredTuple);
+ argi += declaredTuple.objects.dim;
+ continue;
+ }
+
+ // If parameter type doesn't depend on inferred template parameters,
+ // semantic it to get actual type.
+ if (!reliesOnTemplateParameters(prmtype, (*parameters)[inferStart .. parameters.dim]))
+ {
+ // should copy prmtype to avoid affecting semantic result
+ prmtype = prmtype.syntaxCopy().typeSemantic(fd.loc, paramscope);
+
+ if (prmtype.ty == Ttuple)
+ {
+ TypeTuple tt = cast(TypeTuple)prmtype;
+ size_t tt_dim = tt.arguments.dim;
+ for (size_t j = 0; j < tt_dim; j++, ++argi)
+ {
+ Parameter p = (*tt.arguments)[j];
+ if (j == tt_dim - 1 && fparameters.varargs == VarArg.typesafe &&
+ parami + 1 == nfparams && argi < nfargs)
+ {
+ prmtype = p.type;
+ goto Lvarargs;
+ }
+ if (argi >= nfargs)
+ {
+ if (p.defaultArg)
+ continue;
+
+ // https://issues.dlang.org/show_bug.cgi?id=19888
+ if (fparam.defaultArg)
+ break;
+
+ return nomatch();
+ }
+ farg = (*fargs)[argi];
+ if (!farg.implicitConvTo(p.type))
+ return nomatch();
+ }
+ continue;
+ }
+ }
+
+ if (argi >= nfargs) // if not enough arguments
+ {
+ if (!fparam.defaultArg)
+ goto Lvarargs;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=2803
+ * Before the starting of type deduction from the function
+ * default arguments, set the already deduced parameters into paramscope.
+ * It's necessary to avoid breaking existing acceptable code. Cases:
+ *
+ * 1. Already deduced template parameters can appear in fparam.defaultArg:
+ * auto foo(A, B)(A a, B b = A.stringof);
+ * foo(1);
+ * // at fparam == 'B b = A.string', A is equivalent with the deduced type 'int'
+ *
+ * 2. If prmtype depends on default-specified template parameter, the
+ * default type should be preferred.
+ * auto foo(N = size_t, R)(R r, N start = 0)
+ * foo([1,2,3]);
+ * // at fparam `N start = 0`, N should be 'size_t' before
+ * // the deduction result from fparam.defaultArg.
+ */
+ if (argi == nfargs)
+ {
+ foreach (ref dedtype; *dedtypes)
+ {
+ Type at = isType(dedtype);
+ if (at && at.ty == Tnone)
+ {
+ TypeDeduced xt = cast(TypeDeduced)at;
+ dedtype = xt.tded; // 'unbox'
+ }
+ }
+ for (size_t i = ntargs; i < dedargs.dim; i++)
+ {
+ TemplateParameter tparam = (*parameters)[i];
+
+ RootObject oarg = (*dedargs)[i];
+ RootObject oded = (*dedtypes)[i];
+ if (oarg)
+ continue;
+
+ if (oded)
+ {
+ if (tparam.specialization() || !tparam.isTemplateTypeParameter())
+ {
+ /* The specialization can work as long as afterwards
+ * the oded == oarg
+ */
+ (*dedargs)[i] = oded;
+ MATCH m2 = tparam.matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, null);
+ //printf("m2 = %d\n", m2);
+ if (m2 == MATCH.nomatch)
+ return nomatch();
+ if (m2 < matchTiargs)
+ matchTiargs = m2; // pick worst match
+ if (!(*dedtypes)[i].equals(oded))
+ error("specialization not allowed for deduced parameter `%s`", tparam.ident.toChars());
+ }
+ else
+ {
+ if (MATCH.convert < matchTiargs)
+ matchTiargs = MATCH.convert;
+ }
+ (*dedargs)[i] = declareParameter(paramscope, tparam, oded);
+ }
+ else
+ {
+ inuse++;
+ oded = tparam.defaultArg(instLoc, paramscope);
+ inuse--;
+ if (oded)
+ (*dedargs)[i] = declareParameter(paramscope, tparam, oded);
+ }
+ }
+ }
+ nfargs2 = argi + 1;
+
+ /* If prmtype does not depend on any template parameters:
+ *
+ * auto foo(T)(T v, double x = 0);
+ * foo("str");
+ * // at fparam == 'double x = 0'
+ *
+ * or, if all template parameters in the prmtype are already deduced:
+ *
+ * auto foo(R)(R range, ElementType!R sum = 0);
+ * foo([1,2,3]);
+ * // at fparam == 'ElementType!R sum = 0'
+ *
+ * Deducing prmtype from fparam.defaultArg is not necessary.
+ */
+ if (prmtype.deco || prmtype.syntaxCopy().trySemantic(loc, paramscope))
+ {
+ ++argi;
+ continue;
+ }
+
+ // Deduce prmtype from the defaultArg.
+ farg = fparam.defaultArg.syntaxCopy();
+ farg = farg.expressionSemantic(paramscope);
+ farg = resolveProperties(paramscope, farg);
+ }
+ else
+ {
+ farg = (*fargs)[argi];
+ }
+ {
+ // Check invalid arguments to detect errors early.
+ if (farg.op == TOK.error || farg.type.ty == Terror)
+ return nomatch();
+
+ Type att = null;
+ Lretry:
+ version (none)
+ {
+ printf("\tfarg.type = %s\n", farg.type.toChars());
+ printf("\tfparam.type = %s\n", prmtype.toChars());
+ }
+ Type argtype = farg.type;
+
+ if (!(fparam.storageClass & STC.lazy_) && argtype.ty == Tvoid && farg.op != TOK.function_)
+ return nomatch();
+
+ // https://issues.dlang.org/show_bug.cgi?id=12876
+ // Optimize argument to allow CT-known length matching
+ farg = farg.optimize(WANTvalue, fparam.isReference());
+ //printf("farg = %s %s\n", farg.type.toChars(), farg.toChars());
+
+ RootObject oarg = farg;
+ if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue()))
+ {
+ /* Allow expressions that have CT-known boundaries and type [] to match with [dim]
+ */
+ Type taai;
+ if (argtype.ty == Tarray && (prmtype.ty == Tsarray || prmtype.ty == Taarray && (taai = (cast(TypeAArray)prmtype).index).ty == Tident && (cast(TypeIdentifier)taai).idents.dim == 0))
+ {
+ if (farg.op == TOK.string_)
+ {
+ StringExp se = cast(StringExp)farg;
+ argtype = se.type.nextOf().sarrayOf(se.len);
+ }
+ else if (farg.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp ae = cast(ArrayLiteralExp)farg;
+ argtype = ae.type.nextOf().sarrayOf(ae.elements.dim);
+ }
+ else if (farg.op == TOK.slice)
+ {
+ SliceExp se = cast(SliceExp)farg;
+ if (Type tsa = toStaticArrayType(se))
+ argtype = tsa;
+ }
+ }
+
+ oarg = argtype;
+ }
+ else if ((fparam.storageClass & STC.out_) == 0 && (argtype.ty == Tarray || argtype.ty == Tpointer) && templateParameterLookup(prmtype, parameters) != IDX_NOTFOUND && (cast(TypeIdentifier)prmtype).idents.dim == 0)
+ {
+ /* The farg passing to the prmtype always make a copy. Therefore,
+ * we can shrink the set of the deduced type arguments for prmtype
+ * by adjusting top-qualifier of the argtype.
+ *
+ * prmtype argtype ta
+ * T <- const(E)[] const(E)[]
+ * T <- const(E[]) const(E)[]
+ * qualifier(T) <- const(E)[] const(E[])
+ * qualifier(T) <- const(E[]) const(E[])
+ */
+ Type ta = argtype.castMod(prmtype.mod ? argtype.nextOf().mod : 0);
+ if (ta != argtype)
+ {
+ Expression ea = farg.copy();
+ ea.type = ta;
+ oarg = ea;
+ }
+ }
+
+ if (fparameters.varargs == VarArg.typesafe && parami + 1 == nfparams && argi + 1 < nfargs)
+ goto Lvarargs;
+
+ uint wm = 0;
+ MATCH m = deduceType(oarg, paramscope, prmtype, parameters, dedtypes, &wm, inferStart);
+ //printf("\tL%d deduceType m = %d, wm = x%x, wildmatch = x%x\n", __LINE__, m, wm, wildmatch);
+ wildmatch |= wm;
+
+ /* If no match, see if the argument can be matched by using
+ * implicit conversions.
+ */
+ if (m == MATCH.nomatch && prmtype.deco)
+ m = farg.implicitConvTo(prmtype);
+
+ if (m == MATCH.nomatch)
+ {
+ AggregateDeclaration ad = isAggregate(farg.type);
+ if (ad && ad.aliasthis && !isRecursiveAliasThis(att, argtype))
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=12537
+ // The isRecursiveAliasThis() call above
+
+ /* If a semantic error occurs while doing alias this,
+ * eg purity(https://issues.dlang.org/show_bug.cgi?id=7295),
+ * just regard it as not a match.
+ */
+ if (auto e = resolveAliasThis(sc, farg, true))
+ {
+ farg = e;
+ goto Lretry;
+ }
+ }
+ }
+
+ if (m > MATCH.nomatch && (fparam.storageClass & (STC.ref_ | STC.auto_)) == STC.ref_)
+ {
+ if (!farg.isLvalue())
+ {
+ if ((farg.op == TOK.string_ || farg.op == TOK.slice) && (prmtype.ty == Tsarray || prmtype.ty == Taarray))
+ {
+ // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
+ }
+ else if (global.params.rvalueRefParam)
+ {
+ // Allow implicit conversion to ref
+ }
+ else
+ return nomatch();
+ }
+ }
+ if (m > MATCH.nomatch && (fparam.storageClass & STC.out_))
+ {
+ if (!farg.isLvalue())
+ return nomatch();
+ if (!farg.type.isMutable()) // https://issues.dlang.org/show_bug.cgi?id=11916
+ return nomatch();
+ }
+ if (m == MATCH.nomatch && (fparam.storageClass & STC.lazy_) && prmtype.ty == Tvoid && farg.type.ty != Tvoid)
+ m = MATCH.convert;
+ if (m != MATCH.nomatch)
+ {
+ if (m < match)
+ match = m; // pick worst match
+ argi++;
+ continue;
+ }
+ }
+
+ Lvarargs:
+ /* The following code for variadic arguments closely
+ * matches TypeFunction.callMatch()
+ */
+ if (!(fparameters.varargs == VarArg.typesafe && parami + 1 == nfparams))
+ return nomatch();
+
+ /* Check for match with function parameter T...
+ */
+ Type tb = prmtype.toBasetype();
+ switch (tb.ty)
+ {
+ // 6764 fix - TypeAArray may be TypeSArray have not yet run semantic().
+ case Tsarray:
+ case Taarray:
+ {
+ // Perhaps we can do better with this, see TypeFunction.callMatch()
+ if (tb.ty == Tsarray)
+ {
+ TypeSArray tsa = cast(TypeSArray)tb;
+ dinteger_t sz = tsa.dim.toInteger();
+ if (sz != nfargs - argi)
+ return nomatch();
+ }
+ else if (tb.ty == Taarray)
+ {
+ TypeAArray taa = cast(TypeAArray)tb;
+ Expression dim = new IntegerExp(instLoc, nfargs - argi, Type.tsize_t);
+
+ size_t i = templateParameterLookup(taa.index, parameters);
+ if (i == IDX_NOTFOUND)
+ {
+ Expression e;
+ Type t;
+ Dsymbol s;
+ Scope *sco;
+
+ uint errors = global.startGagging();
+ /* ref: https://issues.dlang.org/show_bug.cgi?id=11118
+ * The parameter isn't part of the template
+ * ones, let's try to find it in the
+ * instantiation scope 'sc' and the one
+ * belonging to the template itself. */
+ sco = sc;
+ taa.index.resolve(instLoc, sco, e, t, s);
+ if (!e)
+ {
+ sco = paramscope;
+ taa.index.resolve(instLoc, sco, e, t, s);
+ }
+ global.endGagging(errors);
+
+ if (!e)
+ return nomatch();
+
+ e = e.ctfeInterpret();
+ e = e.implicitCastTo(sco, Type.tsize_t);
+ e = e.optimize(WANTvalue);
+ if (!dim.equals(e))
+ return nomatch();
+ }
+ else
+ {
+ // This code matches code in TypeInstance.deduceType()
+ TemplateParameter tprm = (*parameters)[i];
+ TemplateValueParameter tvp = tprm.isTemplateValueParameter();
+ if (!tvp)
+ return nomatch();
+ Expression e = cast(Expression)(*dedtypes)[i];
+ if (e)
+ {
+ if (!dim.equals(e))
+ return nomatch();
+ }
+ else
+ {
+ Type vt = tvp.valType.typeSemantic(Loc.initial, sc);
+ MATCH m = dim.implicitConvTo(vt);
+ if (m == MATCH.nomatch)
+ return nomatch();
+ (*dedtypes)[i] = dim;
+ }
+ }
+ }
+ goto case Tarray;
+ }
+ case Tarray:
+ {
+ TypeArray ta = cast(TypeArray)tb;
+ Type tret = fparam.isLazyArray();
+ for (; argi < nfargs; argi++)
+ {
+ Expression arg = (*fargs)[argi];
+ assert(arg);
+
+ MATCH m;
+ /* If lazy array of delegates,
+ * convert arg(s) to delegate(s)
+ */
+ if (tret)
+ {
+ if (ta.next.equals(arg.type))
+ {
+ m = MATCH.exact;
+ }
+ else
+ {
+ m = arg.implicitConvTo(tret);
+ if (m == MATCH.nomatch)
+ {
+ if (tret.toBasetype().ty == Tvoid)
+ m = MATCH.convert;
+ }
+ }
+ }
+ else
+ {
+ uint wm = 0;
+ m = deduceType(arg, paramscope, ta.next, parameters, dedtypes, &wm, inferStart);
+ wildmatch |= wm;
+ }
+ if (m == MATCH.nomatch)
+ return nomatch();
+ if (m < match)
+ match = m;
+ }
+ goto Lmatch;
+ }
+ case Tclass:
+ case Tident:
+ goto Lmatch;
+
+ default:
+ return nomatch();
+ }
+ assert(0);
+ }
+ //printf(". argi = %d, nfargs = %d, nfargs2 = %d\n", argi, nfargs, nfargs2);
+ if (argi != nfargs2 && fparameters.varargs == VarArg.none)
+ return nomatch();
+ }
+
+ Lmatch:
+ foreach (ref dedtype; *dedtypes)
+ {
+ Type at = isType(dedtype);
+ if (at)
+ {
+ if (at.ty == Tnone)
+ {
+ TypeDeduced xt = cast(TypeDeduced)at;
+ at = xt.tded; // 'unbox'
+ }
+ dedtype = at.merge2();
+ }
+ }
+ for (size_t i = ntargs; i < dedargs.dim; i++)
+ {
+ TemplateParameter tparam = (*parameters)[i];
+ //printf("tparam[%d] = %s\n", i, tparam.ident.toChars());
+
+ /* For T:T*, the dedargs is the T*, dedtypes is the T
+ * But for function templates, we really need them to match
+ */
+ RootObject oarg = (*dedargs)[i];
+ RootObject oded = (*dedtypes)[i];
+ //printf("1dedargs[%d] = %p, dedtypes[%d] = %p\n", i, oarg, i, oded);
+ //if (oarg) printf("oarg: %s\n", oarg.toChars());
+ //if (oded) printf("oded: %s\n", oded.toChars());
+ if (oarg)
+ continue;
+
+ if (oded)
+ {
+ if (tparam.specialization() || !tparam.isTemplateTypeParameter())
+ {
+ /* The specialization can work as long as afterwards
+ * the oded == oarg
+ */
+ (*dedargs)[i] = oded;
+ MATCH m2 = tparam.matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, null);
+ //printf("m2 = %d\n", m2);
+ if (m2 == MATCH.nomatch)
+ return nomatch();
+ if (m2 < matchTiargs)
+ matchTiargs = m2; // pick worst match
+ if (!(*dedtypes)[i].equals(oded))
+ error("specialization not allowed for deduced parameter `%s`", tparam.ident.toChars());
+ }
+ else
+ {
+ // Discussion: https://issues.dlang.org/show_bug.cgi?id=16484
+ if (MATCH.convert < matchTiargs)
+ matchTiargs = MATCH.convert;
+ }
+ }
+ else
+ {
+ inuse++;
+ oded = tparam.defaultArg(instLoc, paramscope);
+ inuse--;
+ if (!oded)
+ {
+ // if tuple parameter and
+ // tuple parameter was not in function parameter list and
+ // we're one or more arguments short (i.e. no tuple argument)
+ if (tparam == tp &&
+ fptupindex == IDX_NOTFOUND &&
+ ntargs <= dedargs.dim - 1)
+ {
+ // make tuple argument an empty tuple
+ oded = new Tuple();
+ }
+ else
+ return nomatch();
+ }
+ if (isError(oded))
+ return matcherror();
+ ntargs++;
+
+ /* At the template parameter T, the picked default template argument
+ * X!int should be matched to T in order to deduce dependent
+ * template parameter A.
+ * auto foo(T : X!A = X!int, A...)() { ... }
+ * foo(); // T <-- X!int, A <-- (int)
+ */
+ if (tparam.specialization())
+ {
+ (*dedargs)[i] = oded;
+ MATCH m2 = tparam.matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, null);
+ //printf("m2 = %d\n", m2);
+ if (m2 == MATCH.nomatch)
+ return nomatch();
+ if (m2 < matchTiargs)
+ matchTiargs = m2; // pick worst match
+ if (!(*dedtypes)[i].equals(oded))
+ error("specialization not allowed for deduced parameter `%s`", tparam.ident.toChars());
+ }
+ }
+ oded = declareParameter(paramscope, tparam, oded);
+ (*dedargs)[i] = oded;
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=7469
+ * As same as the code for 7469 in findBestMatch,
+ * expand a Tuple in dedargs to normalize template arguments.
+ */
+ if (auto d = dedargs.dim)
+ {
+ if (auto va = isTuple((*dedargs)[d - 1]))
+ {
+ dedargs.setDim(d - 1);
+ dedargs.insert(d - 1, &va.objects);
+ }
+ }
+ ti.tiargs = dedargs; // update to the normalized template arguments.
+
+ // Partially instantiate function for constraint and fd.leastAsSpecialized()
+ {
+ assert(paramscope.scopesym);
+ Scope* sc2 = _scope;
+ sc2 = sc2.push(paramscope.scopesym);
+ sc2 = sc2.push(ti);
+ sc2.parent = ti;
+ sc2.tinst = ti;
+ sc2.minst = sc.minst;
+ sc2.stc |= fd.storage_class & STC.deprecated_;
+
+ fd = doHeaderInstantiation(ti, sc2, fd, tthis, fargs);
+
+ sc2 = sc2.pop();
+ sc2 = sc2.pop();
+
+ if (!fd)
+ return nomatch();
+ }
+
+ if (constraint)
+ {
+ if (!evaluateConstraint(ti, sc, paramscope, dedargs, fd))
+ return nomatch();
+ }
+
+ version (none)
+ {
+ for (size_t i = 0; i < dedargs.dim; i++)
+ {
+ RootObject o = (*dedargs)[i];
+ printf("\tdedargs[%d] = %d, %s\n", i, o.dyncast(), o.toChars());
+ }
+ }
+
+ paramscope.pop();
+ //printf("\tmatch %d\n", match);
+ return MATCHpair(matchTiargs, match);
+ }
+
+ /**************************************************
+ * Declare template parameter tp with value o, and install it in the scope sc.
+ */
+ RootObject declareParameter(Scope* sc, TemplateParameter tp, RootObject o)
+ {
+ //printf("TemplateDeclaration.declareParameter('%s', o = %p)\n", tp.ident.toChars(), o);
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+ Tuple va = isTuple(o);
+
+ Declaration d;
+ VarDeclaration v = null;
+
+ if (ea && ea.op == TOK.type)
+ ta = ea.type;
+ else if (ea && ea.op == TOK.scope_)
+ sa = (cast(ScopeExp)ea).sds;
+ else if (ea && (ea.op == TOK.this_ || ea.op == TOK.super_))
+ sa = (cast(ThisExp)ea).var;
+ else if (ea && ea.op == TOK.function_)
+ {
+ if ((cast(FuncExp)ea).td)
+ sa = (cast(FuncExp)ea).td;
+ else
+ sa = (cast(FuncExp)ea).fd;
+ }
+
+ if (ta)
+ {
+ //printf("type %s\n", ta.toChars());
+ auto ad = new AliasDeclaration(Loc.initial, tp.ident, ta);
+ ad.storage_class |= STC.templateparameter;
+ d = ad;
+ }
+ else if (sa)
+ {
+ //printf("Alias %s %s;\n", sa.ident.toChars(), tp.ident.toChars());
+ auto ad = new AliasDeclaration(Loc.initial, tp.ident, sa);
+ ad.storage_class |= STC.templateparameter;
+ d = ad;
+ }
+ else if (ea)
+ {
+ // tdtypes.data[i] always matches ea here
+ Initializer _init = new ExpInitializer(loc, ea);
+ TemplateValueParameter tvp = tp.isTemplateValueParameter();
+ Type t = tvp ? tvp.valType : null;
+ v = new VarDeclaration(loc, t, tp.ident, _init);
+ v.storage_class = STC.manifest | STC.templateparameter;
+ d = v;
+ }
+ else if (va)
+ {
+ //printf("\ttuple\n");
+ d = new TupleDeclaration(loc, tp.ident, &va.objects);
+ }
+ else
+ {
+ assert(0);
+ }
+ d.storage_class |= STC.templateparameter;
+
+ if (ta)
+ {
+ Type t = ta;
+ // consistent with Type.checkDeprecated()
+ while (t.ty != Tenum)
+ {
+ if (!t.nextOf())
+ break;
+ t = (cast(TypeNext)t).next;
+ }
+ if (Dsymbol s = t.toDsymbol(sc))
+ {
+ if (s.isDeprecated())
+ d.storage_class |= STC.deprecated_;
+ }
+ }
+ else if (sa)
+ {
+ if (sa.isDeprecated())
+ d.storage_class |= STC.deprecated_;
+ }
+
+ if (!sc.insert(d))
+ error("declaration `%s` is already defined", tp.ident.toChars());
+ d.dsymbolSemantic(sc);
+ /* So the caller's o gets updated with the result of semantic() being run on o
+ */
+ if (v)
+ o = v._init.initializerToExpression();
+ return o;
+ }
+
+ /*************************************************
+ * Limited function template instantiation for using fd.leastAsSpecialized()
+ */
+ extern (D) FuncDeclaration doHeaderInstantiation(TemplateInstance ti, Scope* sc2, FuncDeclaration fd, Type tthis, Expressions* fargs)
+ {
+ assert(fd);
+ version (none)
+ {
+ printf("doHeaderInstantiation this = %s\n", toChars());
+ }
+
+ // function body and contracts are not need
+ if (fd.isCtorDeclaration())
+ fd = new CtorDeclaration(fd.loc, fd.endloc, fd.storage_class, fd.type.syntaxCopy());
+ else
+ fd = new FuncDeclaration(fd.loc, fd.endloc, fd.ident, fd.storage_class, fd.type.syntaxCopy());
+ fd.parent = ti;
+
+ assert(fd.type.ty == Tfunction);
+ auto tf = fd.type.isTypeFunction();
+ tf.fargs = fargs;
+
+ if (tthis)
+ {
+ // Match 'tthis' to any TemplateThisParameter's
+ bool hasttp = false;
+ foreach (tp; *parameters)
+ {
+ TemplateThisParameter ttp = tp.isTemplateThisParameter();
+ if (ttp)
+ hasttp = true;
+ }
+ if (hasttp)
+ {
+ tf = cast(TypeFunction)tf.addSTC(ModToStc(tthis.mod));
+ assert(!tf.deco);
+ }
+ }
+
+ Scope* scx = sc2.push();
+
+ // Shouldn't run semantic on default arguments and return type.
+ foreach (ref params; *tf.parameterList.parameters)
+ params.defaultArg = null;
+ tf.incomplete = true;
+
+ if (fd.isCtorDeclaration())
+ {
+ // For constructors, emitting return type is necessary for
+ // isReturnIsolated() in functionResolve.
+ tf.isctor = true;
+
+ Dsymbol parent = toParentDecl();
+ Type tret;
+ AggregateDeclaration ad = parent.isAggregateDeclaration();
+ if (!ad || parent.isUnionDeclaration())
+ {
+ tret = Type.tvoid;
+ }
+ else
+ {
+ tret = ad.handleType();
+ assert(tret);
+ tret = tret.addStorageClass(fd.storage_class | scx.stc);
+ tret = tret.addMod(tf.mod);
+ }
+ tf.next = tret;
+ if (ad && ad.isStructDeclaration())
+ tf.isref = 1;
+ //printf("tf = %s\n", tf.toChars());
+ }
+ else
+ tf.next = null;
+ fd.type = tf;
+ fd.type = fd.type.addSTC(scx.stc);
+ fd.type = fd.type.typeSemantic(fd.loc, scx);
+ scx = scx.pop();
+
+ if (fd.type.ty != Tfunction)
+ return null;
+
+ fd.originalType = fd.type; // for mangling
+ //printf("\t[%s] fd.type = %s, mod = %x, ", loc.toChars(), fd.type.toChars(), fd.type.mod);
+ //printf("fd.needThis() = %d\n", fd.needThis());
+
+ return fd;
+ }
+
+ debug (FindExistingInstance)
+ {
+ __gshared uint nFound, nNotFound, nAdded, nRemoved;
+
+ shared static ~this()
+ {
+ printf("debug (FindExistingInstance) nFound %u, nNotFound: %u, nAdded: %u, nRemoved: %u\n",
+ nFound, nNotFound, nAdded, nRemoved);
+ }
+ }
+
+ /****************************************************
+ * Given a new instance tithis of this TemplateDeclaration,
+ * see if there already exists an instance.
+ * If so, return that existing instance.
+ */
+ extern (D) TemplateInstance findExistingInstance(TemplateInstance tithis, Expressions* fargs)
+ {
+ //printf("findExistingInstance() %s\n", tithis.toChars());
+ tithis.fargs = fargs;
+ auto tibox = TemplateInstanceBox(tithis);
+ auto p = tibox in instances;
+ debug (FindExistingInstance) ++(p ? nFound : nNotFound);
+ //if (p) printf("\tfound %p\n", *p); else printf("\tnot found\n");
+ return p ? *p : null;
+ }
+
+ /********************************************
+ * Add instance ti to TemplateDeclaration's table of instances.
+ * Return a handle we can use to later remove it if it fails instantiation.
+ */
+ extern (D) TemplateInstance addInstance(TemplateInstance ti)
+ {
+ //printf("addInstance() %p %s\n", instances, ti.toChars());
+ auto tibox = TemplateInstanceBox(ti);
+ instances[tibox] = ti;
+ debug (FindExistingInstance) ++nAdded;
+ return ti;
+ }
+
+ /*******************************************
+ * Remove TemplateInstance from table of instances.
+ * Input:
+ * handle returned by addInstance()
+ */
+ extern (D) void removeInstance(TemplateInstance ti)
+ {
+ //printf("removeInstance() %s\n", ti.toChars());
+ auto tibox = TemplateInstanceBox(ti);
+ debug (FindExistingInstance) ++nRemoved;
+ instances.remove(tibox);
+ }
+
+ override inout(TemplateDeclaration) isTemplateDeclaration() inout
+ {
+ return this;
+ }
+
+ /**
+ * Check if the last template parameter is a tuple one,
+ * and returns it if so, else returns `null`.
+ *
+ * Returns:
+ * The last template parameter if it's a `TemplateTupleParameter`
+ */
+ TemplateTupleParameter isVariadic()
+ {
+ size_t dim = parameters.dim;
+ if (dim == 0)
+ return null;
+ return (*parameters)[dim - 1].isTemplateTupleParameter();
+ }
+
+ extern(C++) override bool isDeprecated() const
+ {
+ return this.deprecated_;
+ }
+
+ /***********************************
+ * We can overload templates.
+ */
+ override bool isOverloadable() const
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+extern (C++) final class TypeDeduced : Type
+{
+ Type tded;
+ Expressions argexps; // corresponding expressions
+ Types tparams; // tparams[i].mod
+
+ extern (D) this(Type tt, Expression e, Type tparam)
+ {
+ super(Tnone);
+ tded = tt;
+ argexps.push(e);
+ tparams.push(tparam);
+ }
+
+ void update(Expression e, Type tparam)
+ {
+ argexps.push(e);
+ tparams.push(tparam);
+ }
+
+ void update(Type tt, Expression e, Type tparam)
+ {
+ tded = tt;
+ argexps.push(e);
+ tparams.push(tparam);
+ }
+
+ MATCH matchAll(Type tt)
+ {
+ MATCH match = MATCH.exact;
+ foreach (j, e; argexps)
+ {
+ assert(e);
+ if (e == emptyArrayElement)
+ continue;
+
+ Type t = tt.addMod(tparams[j].mod).substWildTo(MODFlags.const_);
+
+ MATCH m = e.implicitConvTo(t);
+ if (match > m)
+ match = m;
+ if (match == MATCH.nomatch)
+ break;
+ }
+ return match;
+ }
+}
+
+
+/*************************************************
+ * Given function arguments, figure out which template function
+ * to expand, and return matching result.
+ * Params:
+ * m = matching result
+ * dstart = the root of overloaded function templates
+ * loc = instantiation location
+ * sc = instantiation scope
+ * tiargs = initial list of template arguments
+ * tthis = if !NULL, the 'this' pointer argument
+ * fargs = arguments to function
+ * pMessage = address to store error message, or null
+ */
+void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs,
+ Type tthis, Expressions* fargs, const(char)** pMessage = null)
+{
+ Expression[] fargs_ = fargs.peekSlice();
+ version (none)
+ {
+ printf("functionResolve() dstart = %s\n", dstart.toChars());
+ printf(" tiargs:\n");
+ if (tiargs)
+ {
+ for (size_t i = 0; i < tiargs.dim; i++)
+ {
+ RootObject arg = (*tiargs)[i];
+ printf("\t%s\n", arg.toChars());
+ }
+ }
+ printf(" fargs:\n");
+ for (size_t i = 0; i < (fargs ? fargs.dim : 0); i++)
+ {
+ Expression arg = (*fargs)[i];
+ printf("\t%s %s\n", arg.type.toChars(), arg.toChars());
+ //printf("\tty = %d\n", arg.type.ty);
+ }
+ //printf("stc = %llx\n", dstart.scope.stc);
+ //printf("match:t/f = %d/%d\n", ta_last, m.last);
+ }
+
+ // results
+ int property = 0; // 0: uninitialized
+ // 1: seen @property
+ // 2: not @property
+ size_t ov_index = 0;
+ TemplateDeclaration td_best;
+ TemplateInstance ti_best;
+ MATCH ta_last = m.last != MATCH.nomatch ? MATCH.exact : MATCH.nomatch;
+ Type tthis_best;
+
+ int applyFunction(FuncDeclaration fd)
+ {
+ // skip duplicates
+ if (fd == m.lastf)
+ return 0;
+ // explicitly specified tiargs never match to non template function
+ if (tiargs && tiargs.dim > 0)
+ return 0;
+
+ // constructors need a valid scope in order to detect semantic errors
+ if (!fd.isCtorDeclaration &&
+ fd.semanticRun < PASS.semanticdone)
+ {
+ Ungag ungag = fd.ungagSpeculative();
+ fd.dsymbolSemantic(null);
+ }
+ if (fd.semanticRun < PASS.semanticdone)
+ {
+ .error(loc, "forward reference to template `%s`", fd.toChars());
+ return 1;
+ }
+ //printf("fd = %s %s, fargs = %s\n", fd.toChars(), fd.type.toChars(), fargs.toChars());
+ auto tf = cast(TypeFunction)fd.type;
+
+ int prop = tf.isproperty ? 1 : 2;
+ if (property == 0)
+ property = prop;
+ else if (property != prop)
+ error(fd.loc, "cannot overload both property and non-property functions");
+
+ /* For constructors, qualifier check will be opposite direction.
+ * Qualified constructor always makes qualified object, then will be checked
+ * that it is implicitly convertible to tthis.
+ */
+ Type tthis_fd = fd.needThis() ? tthis : null;
+ bool isCtorCall = tthis_fd && fd.isCtorDeclaration();
+ if (isCtorCall)
+ {
+ //printf("%s tf.mod = x%x tthis_fd.mod = x%x %d\n", tf.toChars(),
+ // tf.mod, tthis_fd.mod, fd.isReturnIsolated());
+ if (MODimplicitConv(tf.mod, tthis_fd.mod) ||
+ tf.isWild() && tf.isShared() == tthis_fd.isShared() ||
+ fd.isReturnIsolated())
+ {
+ /* && tf.isShared() == tthis_fd.isShared()*/
+ // Uniquely constructed object can ignore shared qualifier.
+ // TODO: Is this appropriate?
+ tthis_fd = null;
+ }
+ else
+ return 0; // MATCH.nomatch
+ }
+ /* Fix Issue 17970:
+ If a struct is declared as shared the dtor is automatically
+ considered to be shared, but when the struct is instantiated
+ the instance is no longer considered to be shared when the
+ function call matching is done. The fix makes it so that if a
+ struct declaration is shared, when the destructor is called,
+ the instantiated struct is also considered shared.
+ */
+ if (auto dt = fd.isDtorDeclaration())
+ {
+ auto dtmod = dt.type.toTypeFunction();
+ auto shared_dtor = dtmod.mod & MODFlags.shared_;
+ auto shared_this = tthis_fd !is null ?
+ tthis_fd.mod & MODFlags.shared_ : 0;
+ if (shared_dtor && !shared_this)
+ tthis_fd = dtmod;
+ else if (shared_this && !shared_dtor && tthis_fd !is null)
+ tf.mod = tthis_fd.mod;
+ }
+ MATCH mfa = tf.callMatch(tthis_fd, fargs_, 0, pMessage, sc);
+ //printf("test1: mfa = %d\n", mfa);
+ if (mfa == MATCH.nomatch)
+ return 0;
+
+ if (mfa > m.last) goto LfIsBetter;
+ if (mfa < m.last) goto LlastIsBetter;
+
+ /* See if one of the matches overrides the other.
+ */
+ assert(m.lastf);
+ if (m.lastf.overrides(fd)) goto LlastIsBetter;
+ if (fd.overrides(m.lastf)) goto LfIsBetter;
+
+ /* Try to disambiguate using template-style partial ordering rules.
+ * In essence, if f() and g() are ambiguous, if f() can call g(),
+ * but g() cannot call f(), then pick f().
+ * This is because f() is "more specialized."
+ */
+ {
+ MATCH c1 = fd.leastAsSpecialized(m.lastf);
+ MATCH c2 = m.lastf.leastAsSpecialized(fd);
+ //printf("c1 = %d, c2 = %d\n", c1, c2);
+ if (c1 > c2) goto LfIsBetter;
+ if (c1 < c2) goto LlastIsBetter;
+ }
+
+ /* The 'overrides' check above does covariant checking only
+ * for virtual member functions. It should do it for all functions,
+ * but in order to not risk breaking code we put it after
+ * the 'leastAsSpecialized' check.
+ * In the future try moving it before.
+ * I.e. a not-the-same-but-covariant match is preferred,
+ * as it is more restrictive.
+ */
+ if (!m.lastf.type.equals(fd.type))
+ {
+ //printf("cov: %d %d\n", m.lastf.type.covariant(fd.type), fd.type.covariant(m.lastf.type));
+ const lastCovariant = m.lastf.type.covariant(fd.type);
+ const firstCovariant = fd.type.covariant(m.lastf.type);
+
+ if (lastCovariant == Covariant.yes || lastCovariant == Covariant.no)
+ {
+ if (firstCovariant != Covariant.yes && firstCovariant != Covariant.no)
+ {
+ goto LlastIsBetter;
+ }
+ }
+ else if (firstCovariant == Covariant.yes || firstCovariant == Covariant.no)
+ {
+ goto LfIsBetter;
+ }
+ }
+
+ /* If the two functions are the same function, like:
+ * int foo(int);
+ * int foo(int x) { ... }
+ * then pick the one with the body.
+ *
+ * If none has a body then don't care because the same
+ * real function would be linked to the decl (e.g from object file)
+ */
+ if (tf.equals(m.lastf.type) &&
+ fd.storage_class == m.lastf.storage_class &&
+ fd.parent == m.lastf.parent &&
+ fd.visibility == m.lastf.visibility &&
+ fd.linkage == m.lastf.linkage)
+ {
+ if (fd.fbody && !m.lastf.fbody)
+ goto LfIsBetter;
+ if (!fd.fbody)
+ goto LlastIsBetter;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=14450
+ // Prefer exact qualified constructor for the creating object type
+ if (isCtorCall && tf.mod != m.lastf.type.mod)
+ {
+ if (tthis.mod == tf.mod) goto LfIsBetter;
+ if (tthis.mod == m.lastf.type.mod) goto LlastIsBetter;
+ }
+
+ m.nextf = fd;
+ m.count++;
+ return 0;
+
+ LlastIsBetter:
+ return 0;
+
+ LfIsBetter:
+ td_best = null;
+ ti_best = null;
+ ta_last = MATCH.exact;
+ m.last = mfa;
+ m.lastf = fd;
+ tthis_best = tthis_fd;
+ ov_index = 0;
+ m.count = 1;
+ return 0;
+
+ }
+
+ int applyTemplate(TemplateDeclaration td)
+ {
+ //printf("applyTemplate()\n");
+ if (td.inuse)
+ {
+ td.error(loc, "recursive template expansion");
+ return 1;
+ }
+ if (td == td_best) // skip duplicates
+ return 0;
+
+ if (!sc)
+ sc = td._scope; // workaround for Type.aliasthisOf
+
+ if (td.semanticRun == PASS.init && td._scope)
+ {
+ // Try to fix forward reference. Ungag errors while doing so.
+ Ungag ungag = td.ungagSpeculative();
+ td.dsymbolSemantic(td._scope);
+ }
+ if (td.semanticRun == PASS.init)
+ {
+ .error(loc, "forward reference to template `%s`", td.toChars());
+ Lerror:
+ m.lastf = null;
+ m.count = 0;
+ m.last = MATCH.nomatch;
+ return 1;
+ }
+ //printf("td = %s\n", td.toChars());
+
+ auto f = td.onemember ? td.onemember.isFuncDeclaration() : null;
+ if (!f)
+ {
+ if (!tiargs)
+ tiargs = new Objects();
+ auto ti = new TemplateInstance(loc, td, tiargs);
+ Objects dedtypes = Objects(td.parameters.dim);
+ assert(td.semanticRun != PASS.init);
+ MATCH mta = td.matchWithInstance(sc, ti, &dedtypes, fargs, 0);
+ //printf("matchWithInstance = %d\n", mta);
+ if (mta == MATCH.nomatch || mta < ta_last) // no match or less match
+ return 0;
+
+ ti.templateInstanceSemantic(sc, fargs);
+ if (!ti.inst) // if template failed to expand
+ return 0;
+
+ Dsymbol s = ti.inst.toAlias();
+ FuncDeclaration fd;
+ if (auto tdx = s.isTemplateDeclaration())
+ {
+ Objects dedtypesX; // empty tiargs
+
+ // https://issues.dlang.org/show_bug.cgi?id=11553
+ // Check for recursive instantiation of tdx.
+ for (TemplatePrevious* p = tdx.previous; p; p = p.prev)
+ {
+ if (arrayObjectMatch(p.dedargs, &dedtypesX))
+ {
+ //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars());
+ /* It must be a subscope of p.sc, other scope chains are not recursive
+ * instantiations.
+ */
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (scx == p.sc)
+ {
+ error(loc, "recursive template expansion while looking for `%s.%s`", ti.toChars(), tdx.toChars());
+ goto Lerror;
+ }
+ }
+ }
+ /* BUG: should also check for ref param differences
+ */
+ }
+
+ TemplatePrevious pr;
+ pr.prev = tdx.previous;
+ pr.sc = sc;
+ pr.dedargs = &dedtypesX;
+ tdx.previous = &pr; // add this to threaded list
+
+ fd = resolveFuncCall(loc, sc, s, null, tthis, fargs, FuncResolveFlag.quiet);
+
+ tdx.previous = pr.prev; // unlink from threaded list
+ }
+ else if (s.isFuncDeclaration())
+ {
+ fd = resolveFuncCall(loc, sc, s, null, tthis, fargs, FuncResolveFlag.quiet);
+ }
+ else
+ goto Lerror;
+
+ if (!fd)
+ return 0;
+
+ if (fd.type.ty != Tfunction)
+ {
+ m.lastf = fd; // to propagate "error match"
+ m.count = 1;
+ m.last = MATCH.nomatch;
+ return 1;
+ }
+
+ Type tthis_fd = fd.needThis() && !fd.isCtorDeclaration() ? tthis : null;
+
+ auto tf = cast(TypeFunction)fd.type;
+ MATCH mfa = tf.callMatch(tthis_fd, fargs_, 0, null, sc);
+ if (mfa < m.last)
+ return 0;
+
+ if (mta < ta_last) goto Ltd_best2;
+ if (mta > ta_last) goto Ltd2;
+
+ if (mfa < m.last) goto Ltd_best2;
+ if (mfa > m.last) goto Ltd2;
+
+ // td_best and td are ambiguous
+ //printf("Lambig2\n");
+ m.nextf = fd;
+ m.count++;
+ return 0;
+
+ Ltd_best2:
+ return 0;
+
+ Ltd2:
+ // td is the new best match
+ assert(td._scope);
+ td_best = td;
+ ti_best = null;
+ property = 0; // (backward compatibility)
+ ta_last = mta;
+ m.last = mfa;
+ m.lastf = fd;
+ tthis_best = tthis_fd;
+ ov_index = 0;
+ m.nextf = null;
+ m.count = 1;
+ return 0;
+ }
+
+ //printf("td = %s\n", td.toChars());
+ for (size_t ovi = 0; f; f = f.overnext0, ovi++)
+ {
+ if (f.type.ty != Tfunction || f.errors)
+ goto Lerror;
+
+ /* This is a 'dummy' instance to evaluate constraint properly.
+ */
+ auto ti = new TemplateInstance(loc, td, tiargs);
+ ti.parent = td.parent; // Maybe calculating valid 'enclosing' is unnecessary.
+
+ auto fd = f;
+ MATCHpair x = td.deduceFunctionTemplateMatch(ti, sc, fd, tthis, fargs);
+ MATCH mta = x.mta;
+ MATCH mfa = x.mfa;
+ //printf("match:t/f = %d/%d\n", mta, mfa);
+ if (!fd || mfa == MATCH.nomatch)
+ continue;
+
+ Type tthis_fd = fd.needThis() ? tthis : null;
+
+ bool isCtorCall = tthis_fd && fd.isCtorDeclaration();
+ if (isCtorCall)
+ {
+ // Constructor call requires additional check.
+
+ auto tf = cast(TypeFunction)fd.type;
+ assert(tf.next);
+ if (MODimplicitConv(tf.mod, tthis_fd.mod) ||
+ tf.isWild() && tf.isShared() == tthis_fd.isShared() ||
+ fd.isReturnIsolated())
+ {
+ tthis_fd = null;
+ }
+ else
+ continue; // MATCH.nomatch
+ }
+
+ if (mta < ta_last) goto Ltd_best;
+ if (mta > ta_last) goto Ltd;
+
+ if (mfa < m.last) goto Ltd_best;
+ if (mfa > m.last) goto Ltd;
+
+ if (td_best)
+ {
+ // Disambiguate by picking the most specialized TemplateDeclaration
+ MATCH c1 = td.leastAsSpecialized(sc, td_best, fargs);
+ MATCH c2 = td_best.leastAsSpecialized(sc, td, fargs);
+ //printf("1: c1 = %d, c2 = %d\n", c1, c2);
+ if (c1 > c2) goto Ltd;
+ if (c1 < c2) goto Ltd_best;
+ }
+ assert(fd && m.lastf);
+ {
+ // Disambiguate by tf.callMatch
+ auto tf1 = fd.type.isTypeFunction();
+ auto tf2 = m.lastf.type.isTypeFunction();
+ MATCH c1 = tf1.callMatch(tthis_fd, fargs_, 0, null, sc);
+ MATCH c2 = tf2.callMatch(tthis_best, fargs_, 0, null, sc);
+ //printf("2: c1 = %d, c2 = %d\n", c1, c2);
+ if (c1 > c2) goto Ltd;
+ if (c1 < c2) goto Ltd_best;
+ }
+ {
+ // Disambiguate by picking the most specialized FunctionDeclaration
+ MATCH c1 = fd.leastAsSpecialized(m.lastf);
+ MATCH c2 = m.lastf.leastAsSpecialized(fd);
+ //printf("3: c1 = %d, c2 = %d\n", c1, c2);
+ if (c1 > c2) goto Ltd;
+ if (c1 < c2) goto Ltd_best;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=14450
+ // Prefer exact qualified constructor for the creating object type
+ if (isCtorCall && fd.type.mod != m.lastf.type.mod)
+ {
+ if (tthis.mod == fd.type.mod) goto Ltd;
+ if (tthis.mod == m.lastf.type.mod) goto Ltd_best;
+ }
+
+ m.nextf = fd;
+ m.count++;
+ continue;
+
+ Ltd_best: // td_best is the best match so far
+ //printf("Ltd_best\n");
+ continue;
+
+ Ltd: // td is the new best match
+ //printf("Ltd\n");
+ assert(td._scope);
+ td_best = td;
+ ti_best = ti;
+ property = 0; // (backward compatibility)
+ ta_last = mta;
+ m.last = mfa;
+ m.lastf = fd;
+ tthis_best = tthis_fd;
+ ov_index = ovi;
+ m.nextf = null;
+ m.count = 1;
+ continue;
+ }
+ return 0;
+ }
+
+ auto td = dstart.isTemplateDeclaration();
+ if (td && td.funcroot)
+ dstart = td.funcroot;
+ overloadApply(dstart, (Dsymbol s)
+ {
+ if (s.errors)
+ return 0;
+ if (auto fd = s.isFuncDeclaration())
+ return applyFunction(fd);
+ if (auto td = s.isTemplateDeclaration())
+ return applyTemplate(td);
+ return 0;
+ }, sc);
+
+ //printf("td_best = %p, m.lastf = %p\n", td_best, m.lastf);
+ if (td_best && ti_best && m.count == 1)
+ {
+ // Matches to template function
+ assert(td_best.onemember && td_best.onemember.isFuncDeclaration());
+ /* The best match is td_best with arguments tdargs.
+ * Now instantiate the template.
+ */
+ assert(td_best._scope);
+ if (!sc)
+ sc = td_best._scope; // workaround for Type.aliasthisOf
+
+ auto ti = new TemplateInstance(loc, td_best, ti_best.tiargs);
+ ti.templateInstanceSemantic(sc, fargs);
+
+ m.lastf = ti.toAlias().isFuncDeclaration();
+ if (!m.lastf)
+ goto Lnomatch;
+ if (ti.errors)
+ {
+ Lerror:
+ m.count = 1;
+ assert(m.lastf);
+ m.last = MATCH.nomatch;
+ return;
+ }
+
+ // look forward instantiated overload function
+ // Dsymbol.oneMembers is alredy called in TemplateInstance.semantic.
+ // it has filled overnext0d
+ while (ov_index--)
+ {
+ m.lastf = m.lastf.overnext0;
+ assert(m.lastf);
+ }
+
+ tthis_best = m.lastf.needThis() && !m.lastf.isCtorDeclaration() ? tthis : null;
+
+ if (m.lastf.type.ty == Terror)
+ goto Lerror;
+ auto tf = m.lastf.type.isTypeFunction();
+ if (!tf.callMatch(tthis_best, fargs_, 0, null, sc))
+ goto Lnomatch;
+
+ /* As https://issues.dlang.org/show_bug.cgi?id=3682 shows,
+ * a template instance can be matched while instantiating
+ * that same template. Thus, the function type can be incomplete. Complete it.
+ *
+ * https://issues.dlang.org/show_bug.cgi?id=9208
+ * For auto function, completion should be deferred to the end of
+ * its semantic3. Should not complete it in here.
+ */
+ if (tf.next && !m.lastf.inferRetType)
+ {
+ m.lastf.type = tf.typeSemantic(loc, sc);
+ }
+ }
+ else if (m.lastf)
+ {
+ // Matches to non template function,
+ // or found matches were ambiguous.
+ assert(m.count >= 1);
+ }
+ else
+ {
+ Lnomatch:
+ m.count = 0;
+ m.lastf = null;
+ m.last = MATCH.nomatch;
+ }
+}
+
+/* ======================== Type ============================================ */
+
+/****
+ * Given an identifier, figure out which TemplateParameter it is.
+ * Return IDX_NOTFOUND if not found.
+ */
+private size_t templateIdentifierLookup(Identifier id, TemplateParameters* parameters)
+{
+ for (size_t i = 0; i < parameters.dim; i++)
+ {
+ TemplateParameter tp = (*parameters)[i];
+ if (tp.ident.equals(id))
+ return i;
+ }
+ return IDX_NOTFOUND;
+}
+
+private size_t templateParameterLookup(Type tparam, TemplateParameters* parameters)
+{
+ if (tparam.ty == Tident)
+ {
+ TypeIdentifier tident = cast(TypeIdentifier)tparam;
+ //printf("\ttident = '%s'\n", tident.toChars());
+ return templateIdentifierLookup(tident.ident, parameters);
+ }
+ return IDX_NOTFOUND;
+}
+
+private ubyte deduceWildHelper(Type t, Type* at, Type tparam)
+{
+ if ((tparam.mod & MODFlags.wild) == 0)
+ return 0;
+
+ *at = null;
+
+ auto X(T, U)(T U, U T)
+ {
+ return (U << 4) | T;
+ }
+
+ switch (X(tparam.mod, t.mod))
+ {
+ case X(MODFlags.wild, 0):
+ case X(MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.wildconst, 0):
+ case X(MODFlags.wildconst, MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
+ {
+ ubyte wm = (t.mod & ~MODFlags.shared_);
+ if (wm == 0)
+ wm = MODFlags.mutable;
+ ubyte m = (t.mod & (MODFlags.const_ | MODFlags.immutable_)) | (tparam.mod & t.mod & MODFlags.shared_);
+ *at = t.unqualify(m);
+ return wm;
+ }
+ case X(MODFlags.wild, MODFlags.wild):
+ case X(MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.wildconst):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ {
+ *at = t.unqualify(tparam.mod & t.mod);
+ return MODFlags.wild;
+ }
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Returns the common type of the 2 types.
+ */
+private Type rawTypeMerge(Type t1, Type t2)
+{
+ if (t1.equals(t2))
+ return t1;
+ if (t1.equivalent(t2))
+ return t1.castMod(MODmerge(t1.mod, t2.mod));
+
+ auto t1b = t1.toBasetype();
+ auto t2b = t2.toBasetype();
+ if (t1b.equals(t2b))
+ return t1b;
+ if (t1b.equivalent(t2b))
+ return t1b.castMod(MODmerge(t1b.mod, t2b.mod));
+
+ auto ty = implicitConvCommonTy(t1b.ty, t2b.ty);
+ if (ty != Terror)
+ return Type.basic[ty];
+
+ return null;
+}
+
+private MATCH deduceTypeHelper(Type t, Type* at, Type tparam)
+{
+ // 9*9 == 81 cases
+
+ auto X(T, U)(T U, U T)
+ {
+ return (U << 4) | T;
+ }
+
+ switch (X(tparam.mod, t.mod))
+ {
+ case X(0, 0):
+ case X(0, MODFlags.const_):
+ case X(0, MODFlags.wild):
+ case X(0, MODFlags.wildconst):
+ case X(0, MODFlags.shared_):
+ case X(0, MODFlags.shared_ | MODFlags.const_):
+ case X(0, MODFlags.shared_ | MODFlags.wild):
+ case X(0, MODFlags.shared_ | MODFlags.wildconst):
+ case X(0, MODFlags.immutable_):
+ // foo(U) T => T
+ // foo(U) const(T) => const(T)
+ // foo(U) inout(T) => inout(T)
+ // foo(U) inout(const(T)) => inout(const(T))
+ // foo(U) shared(T) => shared(T)
+ // foo(U) shared(const(T)) => shared(const(T))
+ // foo(U) shared(inout(T)) => shared(inout(T))
+ // foo(U) shared(inout(const(T))) => shared(inout(const(T)))
+ // foo(U) immutable(T) => immutable(T)
+ {
+ *at = t;
+ return MATCH.exact;
+ }
+ case X(MODFlags.const_, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.wildconst):
+ case X(MODFlags.shared_, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.immutable_, MODFlags.immutable_):
+ // foo(const(U)) const(T) => T
+ // foo(inout(U)) inout(T) => T
+ // foo(inout(const(U))) inout(const(T)) => T
+ // foo(shared(U)) shared(T) => T
+ // foo(shared(const(U))) shared(const(T)) => T
+ // foo(shared(inout(U))) shared(inout(T)) => T
+ // foo(shared(inout(const(U)))) shared(inout(const(T))) => T
+ // foo(immutable(U)) immutable(T) => T
+ {
+ *at = t.mutableOf().unSharedOf();
+ return MATCH.exact;
+ }
+ case X(MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ // foo(const(U)) shared(const(T)) => shared(T)
+ // foo(inout(U)) shared(inout(T)) => shared(T)
+ // foo(inout(const(U))) shared(inout(const(T))) => shared(T)
+ {
+ *at = t.mutableOf();
+ return MATCH.exact;
+ }
+ case X(MODFlags.const_, 0):
+ case X(MODFlags.const_, MODFlags.wild):
+ case X(MODFlags.const_, MODFlags.wildconst):
+ case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.const_, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.immutable_):
+ // foo(const(U)) T => T
+ // foo(const(U)) inout(T) => T
+ // foo(const(U)) inout(const(T)) => T
+ // foo(const(U)) shared(inout(T)) => shared(T)
+ // foo(const(U)) shared(inout(const(T))) => shared(T)
+ // foo(const(U)) immutable(T) => T
+ // foo(shared(const(U))) immutable(T) => T
+ {
+ *at = t.mutableOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.const_, MODFlags.shared_):
+ // foo(const(U)) shared(T) => shared(T)
+ {
+ *at = t;
+ return MATCH.constant;
+ }
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wildconst):
+ // foo(shared(U)) shared(const(T)) => const(T)
+ // foo(shared(U)) shared(inout(T)) => inout(T)
+ // foo(shared(U)) shared(inout(const(T))) => inout(const(T))
+ {
+ *at = t.unSharedOf();
+ return MATCH.exact;
+ }
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_):
+ // foo(shared(const(U))) shared(T) => T
+ {
+ *at = t.unSharedOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.wildconst, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ // foo(inout(const(U))) immutable(T) => T
+ // foo(shared(const(U))) shared(inout(const(T))) => T
+ // foo(shared(inout(const(U)))) immutable(T) => T
+ // foo(shared(inout(const(U)))) shared(inout(T)) => T
+ {
+ *at = t.unSharedOf().mutableOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
+ // foo(shared(const(U))) shared(inout(T)) => T
+ {
+ *at = t.unSharedOf().mutableOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.wild, 0):
+ case X(MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.wildconst, 0):
+ case X(MODFlags.wildconst, MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_, 0):
+ case X(MODFlags.shared_, MODFlags.const_):
+ case X(MODFlags.shared_, MODFlags.wild):
+ case X(MODFlags.shared_, MODFlags.wildconst):
+ case X(MODFlags.shared_, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.const_, 0):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wild, 0):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, 0):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.immutable_, 0):
+ case X(MODFlags.immutable_, MODFlags.const_):
+ case X(MODFlags.immutable_, MODFlags.wild):
+ case X(MODFlags.immutable_, MODFlags.wildconst):
+ case X(MODFlags.immutable_, MODFlags.shared_):
+ case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wildconst):
+ // foo(inout(U)) T => nomatch
+ // foo(inout(U)) const(T) => nomatch
+ // foo(inout(U)) inout(const(T)) => nomatch
+ // foo(inout(U)) immutable(T) => nomatch
+ // foo(inout(U)) shared(T) => nomatch
+ // foo(inout(U)) shared(const(T)) => nomatch
+ // foo(inout(U)) shared(inout(const(T))) => nomatch
+ // foo(inout(const(U))) T => nomatch
+ // foo(inout(const(U))) const(T) => nomatch
+ // foo(inout(const(U))) inout(T) => nomatch
+ // foo(inout(const(U))) shared(T) => nomatch
+ // foo(inout(const(U))) shared(const(T)) => nomatch
+ // foo(inout(const(U))) shared(inout(T)) => nomatch
+ // foo(shared(U)) T => nomatch
+ // foo(shared(U)) const(T) => nomatch
+ // foo(shared(U)) inout(T) => nomatch
+ // foo(shared(U)) inout(const(T)) => nomatch
+ // foo(shared(U)) immutable(T) => nomatch
+ // foo(shared(const(U))) T => nomatch
+ // foo(shared(const(U))) const(T) => nomatch
+ // foo(shared(const(U))) inout(T) => nomatch
+ // foo(shared(const(U))) inout(const(T)) => nomatch
+ // foo(shared(inout(U))) T => nomatch
+ // foo(shared(inout(U))) const(T) => nomatch
+ // foo(shared(inout(U))) inout(T) => nomatch
+ // foo(shared(inout(U))) inout(const(T)) => nomatch
+ // foo(shared(inout(U))) immutable(T) => nomatch
+ // foo(shared(inout(U))) shared(T) => nomatch
+ // foo(shared(inout(U))) shared(const(T)) => nomatch
+ // foo(shared(inout(U))) shared(inout(const(T))) => nomatch
+ // foo(shared(inout(const(U)))) T => nomatch
+ // foo(shared(inout(const(U)))) const(T) => nomatch
+ // foo(shared(inout(const(U)))) inout(T) => nomatch
+ // foo(shared(inout(const(U)))) inout(const(T)) => nomatch
+ // foo(shared(inout(const(U)))) shared(T) => nomatch
+ // foo(shared(inout(const(U)))) shared(const(T)) => nomatch
+ // foo(immutable(U)) T => nomatch
+ // foo(immutable(U)) const(T) => nomatch
+ // foo(immutable(U)) inout(T) => nomatch
+ // foo(immutable(U)) inout(const(T)) => nomatch
+ // foo(immutable(U)) shared(T) => nomatch
+ // foo(immutable(U)) shared(const(T)) => nomatch
+ // foo(immutable(U)) shared(inout(T)) => nomatch
+ // foo(immutable(U)) shared(inout(const(T))) => nomatch
+ return MATCH.nomatch;
+
+ default:
+ assert(0);
+ }
+}
+
+__gshared Expression emptyArrayElement = null;
+
+/* These form the heart of template argument deduction.
+ * Given 'this' being the type argument to the template instance,
+ * it is matched against the template declaration parameter specialization
+ * 'tparam' to determine the type to be used for the parameter.
+ * Example:
+ * template Foo(T:T*) // template declaration
+ * Foo!(int*) // template instantiation
+ * Input:
+ * this = int*
+ * tparam = T*
+ * parameters = [ T:T* ] // Array of TemplateParameter's
+ * Output:
+ * dedtypes = [ int ] // Array of Expression/Type's
+ */
+MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* parameters, Objects* dedtypes, uint* wm = null, size_t inferStart = 0, bool ignoreAliasThis = false)
+{
+ extern (C++) final class DeduceType : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Scope* sc;
+ Type tparam;
+ TemplateParameters* parameters;
+ Objects* dedtypes;
+ uint* wm;
+ size_t inferStart;
+ bool ignoreAliasThis;
+ MATCH result;
+
+ extern (D) this(Scope* sc, Type tparam, TemplateParameters* parameters, Objects* dedtypes, uint* wm, size_t inferStart, bool ignoreAliasThis)
+ {
+ this.sc = sc;
+ this.tparam = tparam;
+ this.parameters = parameters;
+ this.dedtypes = dedtypes;
+ this.wm = wm;
+ this.inferStart = inferStart;
+ this.ignoreAliasThis = ignoreAliasThis;
+ result = MATCH.nomatch;
+ }
+
+ override void visit(Type t)
+ {
+ if (!tparam)
+ goto Lnomatch;
+
+ if (t == tparam)
+ goto Lexact;
+
+ if (tparam.ty == Tident)
+ {
+ // Determine which parameter tparam is
+ size_t i = templateParameterLookup(tparam, parameters);
+ if (i == IDX_NOTFOUND)
+ {
+ if (!sc)
+ goto Lnomatch;
+
+ /* Need a loc to go with the semantic routine.
+ */
+ Loc loc;
+ if (parameters.dim)
+ {
+ TemplateParameter tp = (*parameters)[0];
+ loc = tp.loc;
+ }
+
+ /* BUG: what if tparam is a template instance, that
+ * has as an argument another Tident?
+ */
+ tparam = tparam.typeSemantic(loc, sc);
+ assert(tparam.ty != Tident);
+ result = deduceType(t, sc, tparam, parameters, dedtypes, wm);
+ return;
+ }
+
+ TemplateParameter tp = (*parameters)[i];
+
+ TypeIdentifier tident = cast(TypeIdentifier)tparam;
+ if (tident.idents.dim > 0)
+ {
+ //printf("matching %s to %s\n", tparam.toChars(), t.toChars());
+ Dsymbol s = t.toDsymbol(sc);
+ for (size_t j = tident.idents.dim; j-- > 0;)
+ {
+ RootObject id = tident.idents[j];
+ if (id.dyncast() == DYNCAST.identifier)
+ {
+ if (!s || !s.parent)
+ goto Lnomatch;
+ Dsymbol s2 = s.parent.search(Loc.initial, cast(Identifier)id);
+ if (!s2)
+ goto Lnomatch;
+ s2 = s2.toAlias();
+ //printf("[%d] s = %s %s, s2 = %s %s\n", j, s.kind(), s.toChars(), s2.kind(), s2.toChars());
+ if (s != s2)
+ {
+ if (Type tx = s2.getType())
+ {
+ if (s != tx.toDsymbol(sc))
+ goto Lnomatch;
+ }
+ else
+ goto Lnomatch;
+ }
+ s = s.parent;
+ }
+ else
+ goto Lnomatch;
+ }
+ //printf("[e] s = %s\n", s?s.toChars():"(null)");
+ if (tp.isTemplateTypeParameter())
+ {
+ Type tt = s.getType();
+ if (!tt)
+ goto Lnomatch;
+ Type at = cast(Type)(*dedtypes)[i];
+ if (at && at.ty == Tnone)
+ at = (cast(TypeDeduced)at).tded;
+ if (!at || tt.equals(at))
+ {
+ (*dedtypes)[i] = tt;
+ goto Lexact;
+ }
+ }
+ if (tp.isTemplateAliasParameter())
+ {
+ Dsymbol s2 = cast(Dsymbol)(*dedtypes)[i];
+ if (!s2 || s == s2)
+ {
+ (*dedtypes)[i] = s;
+ goto Lexact;
+ }
+ }
+ goto Lnomatch;
+ }
+
+ // Found the corresponding parameter tp
+ if (!tp.isTemplateTypeParameter())
+ goto Lnomatch;
+ Type at = cast(Type)(*dedtypes)[i];
+ Type tt;
+ if (ubyte wx = wm ? deduceWildHelper(t, &tt, tparam) : 0)
+ {
+ // type vs (none)
+ if (!at)
+ {
+ (*dedtypes)[i] = tt;
+ *wm |= wx;
+ result = MATCH.constant;
+ return;
+ }
+
+ // type vs expressions
+ if (at.ty == Tnone)
+ {
+ TypeDeduced xt = cast(TypeDeduced)at;
+ result = xt.matchAll(tt);
+ if (result > MATCH.nomatch)
+ {
+ (*dedtypes)[i] = tt;
+ if (result > MATCH.constant)
+ result = MATCH.constant; // limit level for inout matches
+ }
+ return;
+ }
+
+ // type vs type
+ if (tt.equals(at))
+ {
+ (*dedtypes)[i] = tt; // Prefer current type match
+ goto Lconst;
+ }
+ if (tt.implicitConvTo(at.constOf()))
+ {
+ (*dedtypes)[i] = at.constOf().mutableOf();
+ *wm |= MODFlags.const_;
+ goto Lconst;
+ }
+ if (at.implicitConvTo(tt.constOf()))
+ {
+ (*dedtypes)[i] = tt.constOf().mutableOf();
+ *wm |= MODFlags.const_;
+ goto Lconst;
+ }
+ goto Lnomatch;
+ }
+ else if (MATCH m = deduceTypeHelper(t, &tt, tparam))
+ {
+ // type vs (none)
+ if (!at)
+ {
+ (*dedtypes)[i] = tt;
+ result = m;
+ return;
+ }
+
+ // type vs expressions
+ if (at.ty == Tnone)
+ {
+ TypeDeduced xt = cast(TypeDeduced)at;
+ result = xt.matchAll(tt);
+ if (result > MATCH.nomatch)
+ {
+ (*dedtypes)[i] = tt;
+ }
+ return;
+ }
+
+ // type vs type
+ if (tt.equals(at))
+ {
+ goto Lexact;
+ }
+ if (tt.ty == Tclass && at.ty == Tclass)
+ {
+ result = tt.implicitConvTo(at);
+ return;
+ }
+ if (tt.ty == Tsarray && at.ty == Tarray && tt.nextOf().implicitConvTo(at.nextOf()) >= MATCH.constant)
+ {
+ goto Lexact;
+ }
+ }
+ goto Lnomatch;
+ }
+
+ if (tparam.ty == Ttypeof)
+ {
+ /* Need a loc to go with the semantic routine.
+ */
+ Loc loc;
+ if (parameters.dim)
+ {
+ TemplateParameter tp = (*parameters)[0];
+ loc = tp.loc;
+ }
+
+ tparam = tparam.typeSemantic(loc, sc);
+ }
+ if (t.ty != tparam.ty)
+ {
+ if (Dsymbol sym = t.toDsymbol(sc))
+ {
+ if (sym.isforwardRef() && !tparam.deco)
+ goto Lnomatch;
+ }
+
+ MATCH m = t.implicitConvTo(tparam);
+ if (m == MATCH.nomatch && !ignoreAliasThis)
+ {
+ if (t.ty == Tclass)
+ {
+ TypeClass tc = cast(TypeClass)t;
+ if (tc.sym.aliasthis && !(tc.att & AliasThisRec.tracingDT))
+ {
+ if (auto ato = t.aliasthisOf())
+ {
+ tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracingDT);
+ m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
+ tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracingDT);
+ }
+ }
+ }
+ else if (t.ty == Tstruct)
+ {
+ TypeStruct ts = cast(TypeStruct)t;
+ if (ts.sym.aliasthis && !(ts.att & AliasThisRec.tracingDT))
+ {
+ if (auto ato = t.aliasthisOf())
+ {
+ ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracingDT);
+ m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
+ ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracingDT);
+ }
+ }
+ }
+ }
+ result = m;
+ return;
+ }
+
+ if (t.nextOf())
+ {
+ if (tparam.deco && !tparam.hasWild())
+ {
+ result = t.implicitConvTo(tparam);
+ return;
+ }
+
+ Type tpn = tparam.nextOf();
+ if (wm && t.ty == Taarray && tparam.isWild())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=12403
+ // In IFTI, stop inout matching on transitive part of AA types.
+ tpn = tpn.substWildTo(MODFlags.mutable);
+ }
+
+ result = deduceType(t.nextOf(), sc, tpn, parameters, dedtypes, wm);
+ return;
+ }
+
+ Lexact:
+ result = MATCH.exact;
+ return;
+
+ Lnomatch:
+ result = MATCH.nomatch;
+ return;
+
+ Lconst:
+ result = MATCH.constant;
+ }
+
+ override void visit(TypeVector t)
+ {
+ if (tparam.ty == Tvector)
+ {
+ TypeVector tp = cast(TypeVector)tparam;
+ result = deduceType(t.basetype, sc, tp.basetype, parameters, dedtypes, wm);
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeDArray t)
+ {
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeSArray t)
+ {
+ // Extra check that array dimensions must match
+ if (tparam)
+ {
+ if (tparam.ty == Tarray)
+ {
+ MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
+ result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch;
+ return;
+ }
+
+ TemplateParameter tp = null;
+ Expression edim = null;
+ size_t i;
+ if (tparam.ty == Tsarray)
+ {
+ TypeSArray tsa = cast(TypeSArray)tparam;
+ if (tsa.dim.op == TOK.variable && (cast(VarExp)tsa.dim).var.storage_class & STC.templateparameter)
+ {
+ Identifier id = (cast(VarExp)tsa.dim).var.ident;
+ i = templateIdentifierLookup(id, parameters);
+ assert(i != IDX_NOTFOUND);
+ tp = (*parameters)[i];
+ }
+ else
+ edim = tsa.dim;
+ }
+ else if (tparam.ty == Taarray)
+ {
+ TypeAArray taa = cast(TypeAArray)tparam;
+ i = templateParameterLookup(taa.index, parameters);
+ if (i != IDX_NOTFOUND)
+ tp = (*parameters)[i];
+ else
+ {
+ Expression e;
+ Type tx;
+ Dsymbol s;
+ taa.index.resolve(Loc.initial, sc, e, tx, s);
+ edim = s ? getValue(s) : getValue(e);
+ }
+ }
+ if (tp && tp.matchArg(sc, t.dim, i, parameters, dedtypes, null) || edim && edim.toInteger() == t.dim.toInteger())
+ {
+ result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
+ return;
+ }
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeAArray t)
+ {
+ // Extra check that index type must match
+ if (tparam && tparam.ty == Taarray)
+ {
+ TypeAArray tp = cast(TypeAArray)tparam;
+ if (!deduceType(t.index, sc, tp.index, parameters, dedtypes))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeFunction t)
+ {
+ // Extra check that function characteristics must match
+ if (!tparam)
+ return visit(cast(Type)t);
+
+ if (auto tp = tparam.isTypeFunction())
+ {
+ if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+
+ foreach (fparam; *tp.parameterList.parameters)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=2579
+ // Apply function parameter storage classes to parameter types
+ fparam.type = fparam.type.addStorageClass(fparam.storageClass);
+ fparam.storageClass &= ~(STC.TYPECTOR | STC.in_);
+
+ // https://issues.dlang.org/show_bug.cgi?id=15243
+ // Resolve parameter type if it's not related with template parameters
+ if (!reliesOnTemplateParameters(fparam.type, (*parameters)[inferStart .. parameters.dim]))
+ {
+ auto tx = fparam.type.typeSemantic(Loc.initial, sc);
+ if (tx.ty == Terror)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ fparam.type = tx;
+ }
+ }
+
+ size_t nfargs = t.parameterList.length;
+ size_t nfparams = tp.parameterList.length;
+
+ /* See if tuple match
+ */
+ if (nfparams > 0 && nfargs >= nfparams - 1)
+ {
+ /* See if 'A' of the template parameter matches 'A'
+ * of the type of the last function parameter.
+ */
+ Parameter fparam = tp.parameterList[nfparams - 1];
+ assert(fparam);
+ assert(fparam.type);
+ if (fparam.type.ty != Tident)
+ goto L1;
+ TypeIdentifier tid = cast(TypeIdentifier)fparam.type;
+ if (tid.idents.dim)
+ goto L1;
+
+ /* Look through parameters to find tuple matching tid.ident
+ */
+ size_t tupi = 0;
+ for (; 1; tupi++)
+ {
+ if (tupi == parameters.dim)
+ goto L1;
+ TemplateParameter tx = (*parameters)[tupi];
+ TemplateTupleParameter tup = tx.isTemplateTupleParameter();
+ if (tup && tup.ident.equals(tid.ident))
+ break;
+ }
+
+ /* The types of the function arguments [nfparams - 1 .. nfargs]
+ * now form the tuple argument.
+ */
+ size_t tuple_dim = nfargs - (nfparams - 1);
+
+ /* See if existing tuple, and whether it matches or not
+ */
+ RootObject o = (*dedtypes)[tupi];
+ if (o)
+ {
+ // Existing deduced argument must be a tuple, and must match
+ Tuple tup = isTuple(o);
+ if (!tup || tup.objects.dim != tuple_dim)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ for (size_t i = 0; i < tuple_dim; i++)
+ {
+ Parameter arg = t.parameterList[nfparams - 1 + i];
+ if (!arg.type.equals(tup.objects[i]))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+ }
+ else
+ {
+ // Create new tuple
+ auto tup = new Tuple(tuple_dim);
+ for (size_t i = 0; i < tuple_dim; i++)
+ {
+ Parameter arg = t.parameterList[nfparams - 1 + i];
+ tup.objects[i] = arg.type;
+ }
+ (*dedtypes)[tupi] = tup;
+ }
+ nfparams--; // don't consider the last parameter for type deduction
+ goto L2;
+ }
+
+ L1:
+ if (nfargs != nfparams)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ L2:
+ assert(nfparams <= tp.parameterList.length);
+ foreach (i, ap; tp.parameterList)
+ {
+ if (i == nfparams)
+ break;
+
+ Parameter a = t.parameterList[i];
+
+ if (!a.isCovariant(t.isref, ap) ||
+ !deduceType(a.type, sc, ap.type, parameters, dedtypes))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeIdentifier t)
+ {
+ // Extra check
+ if (tparam && tparam.ty == Tident)
+ {
+ TypeIdentifier tp = cast(TypeIdentifier)tparam;
+ for (size_t i = 0; i < t.idents.dim; i++)
+ {
+ RootObject id1 = t.idents[i];
+ RootObject id2 = tp.idents[i];
+ if (!id1.equals(id2))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeInstance t)
+ {
+ // Extra check
+ if (tparam && tparam.ty == Tinstance && t.tempinst.tempdecl)
+ {
+ TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ TypeInstance tp = cast(TypeInstance)tparam;
+
+ //printf("tempinst.tempdecl = %p\n", tempdecl);
+ //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl);
+ if (!tp.tempinst.tempdecl)
+ {
+ //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars());
+
+ /* Handle case of:
+ * template Foo(T : sa!(T), alias sa)
+ */
+ size_t i = templateIdentifierLookup(tp.tempinst.name, parameters);
+ if (i == IDX_NOTFOUND)
+ {
+ /* Didn't find it as a parameter identifier. Try looking
+ * it up and seeing if is an alias.
+ * https://issues.dlang.org/show_bug.cgi?id=1454
+ */
+ auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name);
+ Type tx;
+ Expression e;
+ Dsymbol s;
+ tid.resolve(tp.loc, sc, e, tx, s);
+ if (tx)
+ {
+ s = tx.toDsymbol(sc);
+ if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14290
+ // Try to match with ti.tempecl,
+ // only when ti is an enclosing instance.
+ Dsymbol p = sc.parent;
+ while (p && p != ti)
+ p = p.parent;
+ if (p)
+ s = ti.tempdecl;
+ }
+ }
+ if (s)
+ {
+ s = s.toAlias();
+ TemplateDeclaration td = s.isTemplateDeclaration();
+ if (td)
+ {
+ if (td.overroot)
+ td = td.overroot;
+ for (; td; td = td.overnext)
+ {
+ if (td == tempdecl)
+ goto L2;
+ }
+ }
+ }
+ goto Lnomatch;
+ }
+ TemplateParameter tpx = (*parameters)[i];
+ if (!tpx.matchArg(sc, tempdecl, i, parameters, dedtypes, null))
+ goto Lnomatch;
+ }
+ else if (tempdecl != tp.tempinst.tempdecl)
+ goto Lnomatch;
+
+ L2:
+ for (size_t i = 0; 1; i++)
+ {
+ //printf("\ttest: tempinst.tiargs[%d]\n", i);
+ RootObject o1 = null;
+ if (i < t.tempinst.tiargs.dim)
+ o1 = (*t.tempinst.tiargs)[i];
+ else if (i < t.tempinst.tdtypes.dim && i < tp.tempinst.tiargs.dim)
+ {
+ // Pick up default arg
+ o1 = t.tempinst.tdtypes[i];
+ }
+ else if (i >= tp.tempinst.tiargs.dim)
+ break;
+
+ if (i >= tp.tempinst.tiargs.dim)
+ {
+ size_t dim = tempdecl.parameters.dim - (tempdecl.isVariadic() ? 1 : 0);
+ while (i < dim && ((*tempdecl.parameters)[i].dependent || (*tempdecl.parameters)[i].hasDefaultArg()))
+ {
+ i++;
+ }
+ if (i >= dim)
+ break; // match if all remained parameters are dependent
+ goto Lnomatch;
+ }
+
+ RootObject o2 = (*tp.tempinst.tiargs)[i];
+ Type t2 = isType(o2);
+
+ size_t j = (t2 && t2.ty == Tident && i == tp.tempinst.tiargs.dim - 1)
+ ? templateParameterLookup(t2, parameters) : IDX_NOTFOUND;
+ if (j != IDX_NOTFOUND && j == parameters.dim - 1 &&
+ (*parameters)[j].isTemplateTupleParameter())
+ {
+ /* Given:
+ * struct A(B...) {}
+ * alias A!(int, float) X;
+ * static if (is(X Y == A!(Z), Z...)) {}
+ * deduce that Z is a tuple(int, float)
+ */
+
+ /* Create tuple from remaining args
+ */
+ size_t vtdim = (tempdecl.isVariadic() ? t.tempinst.tiargs.dim : t.tempinst.tdtypes.dim) - i;
+ auto vt = new Tuple(vtdim);
+ for (size_t k = 0; k < vtdim; k++)
+ {
+ RootObject o;
+ if (k < t.tempinst.tiargs.dim)
+ o = (*t.tempinst.tiargs)[i + k];
+ else // Pick up default arg
+ o = t.tempinst.tdtypes[i + k];
+ vt.objects[k] = o;
+ }
+
+ Tuple v = cast(Tuple)(*dedtypes)[j];
+ if (v)
+ {
+ if (!match(v, vt))
+ goto Lnomatch;
+ }
+ else
+ (*dedtypes)[j] = vt;
+ break;
+ }
+ else if (!o1)
+ break;
+
+ Type t1 = isType(o1);
+ Dsymbol s1 = isDsymbol(o1);
+ Dsymbol s2 = isDsymbol(o2);
+ Expression e1 = s1 ? getValue(s1) : getValue(isExpression(o1));
+ Expression e2 = isExpression(o2);
+ version (none)
+ {
+ Tuple v1 = isTuple(o1);
+ Tuple v2 = isTuple(o2);
+ if (t1)
+ printf("t1 = %s\n", t1.toChars());
+ if (t2)
+ printf("t2 = %s\n", t2.toChars());
+ if (e1)
+ printf("e1 = %s\n", e1.toChars());
+ if (e2)
+ printf("e2 = %s\n", e2.toChars());
+ if (s1)
+ printf("s1 = %s\n", s1.toChars());
+ if (s2)
+ printf("s2 = %s\n", s2.toChars());
+ if (v1)
+ printf("v1 = %s\n", v1.toChars());
+ if (v2)
+ printf("v2 = %s\n", v2.toChars());
+ }
+
+ if (t1 && t2)
+ {
+ if (!deduceType(t1, sc, t2, parameters, dedtypes))
+ goto Lnomatch;
+ }
+ else if (e1 && e2)
+ {
+ Le:
+ e1 = e1.ctfeInterpret();
+
+ /* If it is one of the template parameters for this template,
+ * we should not attempt to interpret it. It already has a value.
+ */
+ if (e2.op == TOK.variable && ((cast(VarExp)e2).var.storage_class & STC.templateparameter))
+ {
+ /*
+ * (T:Number!(e2), int e2)
+ */
+ j = templateIdentifierLookup((cast(VarExp)e2).var.ident, parameters);
+ if (j != IDX_NOTFOUND)
+ goto L1;
+ // The template parameter was not from this template
+ // (it may be from a parent template, for example)
+ }
+
+ e2 = e2.expressionSemantic(sc); // https://issues.dlang.org/show_bug.cgi?id=13417
+ e2 = e2.ctfeInterpret();
+
+ //printf("e1 = %s, type = %s %d\n", e1.toChars(), e1.type.toChars(), e1.type.ty);
+ //printf("e2 = %s, type = %s %d\n", e2.toChars(), e2.type.toChars(), e2.type.ty);
+ if (!e1.equals(e2))
+ {
+ if (!e2.implicitConvTo(e1.type))
+ goto Lnomatch;
+
+ e2 = e2.implicitCastTo(sc, e1.type);
+ e2 = e2.ctfeInterpret();
+ if (!e1.equals(e2))
+ goto Lnomatch;
+ }
+ }
+ else if (e1 && t2 && t2.ty == Tident)
+ {
+ j = templateParameterLookup(t2, parameters);
+ L1:
+ if (j == IDX_NOTFOUND)
+ {
+ t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2);
+ if (e2)
+ goto Le;
+ goto Lnomatch;
+ }
+ if (!(*parameters)[j].matchArg(sc, e1, j, parameters, dedtypes, null))
+ goto Lnomatch;
+ }
+ else if (s1 && s2)
+ {
+ Ls:
+ if (!s1.equals(s2))
+ goto Lnomatch;
+ }
+ else if (s1 && t2 && t2.ty == Tident)
+ {
+ j = templateParameterLookup(t2, parameters);
+ if (j == IDX_NOTFOUND)
+ {
+ t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2);
+ if (s2)
+ goto Ls;
+ goto Lnomatch;
+ }
+ if (!(*parameters)[j].matchArg(sc, s1, j, parameters, dedtypes, null))
+ goto Lnomatch;
+ }
+ else
+ goto Lnomatch;
+ }
+ }
+ visit(cast(Type)t);
+ return;
+
+ Lnomatch:
+ //printf("no match\n");
+ result = MATCH.nomatch;
+ }
+
+ override void visit(TypeStruct t)
+ {
+ /* If this struct is a template struct, and we're matching
+ * it against a template instance, convert the struct type
+ * to a template instance, too, and try again.
+ */
+ TemplateInstance ti = t.sym.parent.isTemplateInstance();
+
+ if (tparam && tparam.ty == Tinstance)
+ {
+ if (ti && ti.toAlias() == t.sym)
+ {
+ auto tx = new TypeInstance(Loc.initial, ti);
+ result = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
+ return;
+ }
+
+ /* Match things like:
+ * S!(T).foo
+ */
+ TypeInstance tpi = cast(TypeInstance)tparam;
+ if (tpi.idents.dim)
+ {
+ RootObject id = tpi.idents[tpi.idents.dim - 1];
+ if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id))
+ {
+ Type tparent = t.sym.parent.getType();
+ if (tparent)
+ {
+ /* Slice off the .foo in S!(T).foo
+ */
+ tpi.idents.dim--;
+ result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
+ tpi.idents.dim++;
+ return;
+ }
+ }
+ }
+ }
+
+ // Extra check
+ if (tparam && tparam.ty == Tstruct)
+ {
+ TypeStruct tp = cast(TypeStruct)tparam;
+
+ //printf("\t%d\n", (MATCH) t.implicitConvTo(tp));
+ if (wm && t.deduceWild(tparam, false))
+ {
+ result = MATCH.constant;
+ return;
+ }
+ result = t.implicitConvTo(tp);
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeEnum t)
+ {
+ // Extra check
+ if (tparam && tparam.ty == Tenum)
+ {
+ TypeEnum tp = cast(TypeEnum)tparam;
+ if (t.sym == tp.sym)
+ visit(cast(Type)t);
+ else
+ result = MATCH.nomatch;
+ return;
+ }
+ Type tb = t.toBasetype();
+ if (tb.ty == tparam.ty || tb.ty == Tsarray && tparam.ty == Taarray)
+ {
+ result = deduceType(tb, sc, tparam, parameters, dedtypes, wm);
+ if (result == MATCH.exact)
+ result = MATCH.convert;
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ /* Helper for TypeClass.deduceType().
+ * Classes can match with implicit conversion to a base class or interface.
+ * This is complicated, because there may be more than one base class which
+ * matches. In such cases, one or more parameters remain ambiguous.
+ * For example,
+ *
+ * interface I(X, Y) {}
+ * class C : I(uint, double), I(char, double) {}
+ * C x;
+ * foo(T, U)( I!(T, U) x)
+ *
+ * deduces that U is double, but T remains ambiguous (could be char or uint).
+ *
+ * Given a baseclass b, and initial deduced types 'dedtypes', this function
+ * tries to match tparam with b, and also tries all base interfaces of b.
+ * If a match occurs, numBaseClassMatches is incremented, and the new deduced
+ * types are ANDed with the current 'best' estimate for dedtypes.
+ */
+ static void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, TemplateParameters* parameters, Objects* dedtypes, Objects* best, ref int numBaseClassMatches)
+ {
+ TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null;
+ if (parti)
+ {
+ // Make a temporary copy of dedtypes so we don't destroy it
+ auto tmpdedtypes = new Objects(dedtypes.dim);
+ memcpy(tmpdedtypes.tdata(), dedtypes.tdata(), dedtypes.dim * (void*).sizeof);
+
+ auto t = new TypeInstance(Loc.initial, parti);
+ MATCH m = deduceType(t, sc, tparam, parameters, tmpdedtypes);
+ if (m > MATCH.nomatch)
+ {
+ // If this is the first ever match, it becomes our best estimate
+ if (numBaseClassMatches == 0)
+ memcpy(best.tdata(), tmpdedtypes.tdata(), tmpdedtypes.dim * (void*).sizeof);
+ else
+ for (size_t k = 0; k < tmpdedtypes.dim; ++k)
+ {
+ // If we've found more than one possible type for a parameter,
+ // mark it as unknown.
+ if ((*tmpdedtypes)[k] != (*best)[k])
+ (*best)[k] = (*dedtypes)[k];
+ }
+ ++numBaseClassMatches;
+ }
+ }
+
+ // Now recursively test the inherited interfaces
+ foreach (ref bi; b.baseInterfaces)
+ {
+ deduceBaseClassParameters(bi, sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+ }
+ }
+
+ override void visit(TypeClass t)
+ {
+ //printf("TypeClass.deduceType(this = %s)\n", t.toChars());
+
+ /* If this class is a template class, and we're matching
+ * it against a template instance, convert the class type
+ * to a template instance, too, and try again.
+ */
+ TemplateInstance ti = t.sym.parent.isTemplateInstance();
+
+ if (tparam && tparam.ty == Tinstance)
+ {
+ if (ti && ti.toAlias() == t.sym)
+ {
+ auto tx = new TypeInstance(Loc.initial, ti);
+ MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
+ // Even if the match fails, there is still a chance it could match
+ // a base class.
+ if (m != MATCH.nomatch)
+ {
+ result = m;
+ return;
+ }
+ }
+
+ /* Match things like:
+ * S!(T).foo
+ */
+ TypeInstance tpi = cast(TypeInstance)tparam;
+ if (tpi.idents.dim)
+ {
+ RootObject id = tpi.idents[tpi.idents.dim - 1];
+ if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id))
+ {
+ Type tparent = t.sym.parent.getType();
+ if (tparent)
+ {
+ /* Slice off the .foo in S!(T).foo
+ */
+ tpi.idents.dim--;
+ result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
+ tpi.idents.dim++;
+ return;
+ }
+ }
+ }
+
+ // If it matches exactly or via implicit conversion, we're done
+ visit(cast(Type)t);
+ if (result != MATCH.nomatch)
+ return;
+
+ /* There is still a chance to match via implicit conversion to
+ * a base class or interface. Because there could be more than one such
+ * match, we need to check them all.
+ */
+
+ int numBaseClassMatches = 0; // Have we found an interface match?
+
+ // Our best guess at dedtypes
+ auto best = new Objects(dedtypes.dim);
+
+ ClassDeclaration s = t.sym;
+ while (s && s.baseclasses.dim > 0)
+ {
+ // Test the base class
+ deduceBaseClassParameters(*(*s.baseclasses)[0], sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+
+ // Test the interfaces inherited by the base class
+ foreach (b; s.interfaces)
+ {
+ deduceBaseClassParameters(*b, sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+ }
+ s = (*s.baseclasses)[0].sym;
+ }
+
+ if (numBaseClassMatches == 0)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+
+ // If we got at least one match, copy the known types into dedtypes
+ memcpy(dedtypes.tdata(), best.tdata(), best.dim * (void*).sizeof);
+ result = MATCH.convert;
+ return;
+ }
+
+ // Extra check
+ if (tparam && tparam.ty == Tclass)
+ {
+ TypeClass tp = cast(TypeClass)tparam;
+
+ //printf("\t%d\n", (MATCH) t.implicitConvTo(tp));
+ if (wm && t.deduceWild(tparam, false))
+ {
+ result = MATCH.constant;
+ return;
+ }
+ result = t.implicitConvTo(tp);
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression.deduceType(e = %s)\n", e.toChars());
+ size_t i = templateParameterLookup(tparam, parameters);
+ if (i == IDX_NOTFOUND || (cast(TypeIdentifier)tparam).idents.dim > 0)
+ {
+ if (e == emptyArrayElement && tparam.ty == Tarray)
+ {
+ Type tn = (cast(TypeNext)tparam).next;
+ result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
+ return;
+ }
+ e.type.accept(this);
+ return;
+ }
+
+ TemplateTypeParameter tp = (*parameters)[i].isTemplateTypeParameter();
+ if (!tp)
+ return; // nomatch
+
+ if (e == emptyArrayElement)
+ {
+ if ((*dedtypes)[i])
+ {
+ result = MATCH.exact;
+ return;
+ }
+ if (tp.defaultType)
+ {
+ tp.defaultType.accept(this);
+ return;
+ }
+ }
+
+ /* Returns `true` if `t` is a reference type, or an array of reference types
+ */
+ bool isTopRef(Type t)
+ {
+ auto tb = t.baseElemOf();
+ return tb.ty == Tclass ||
+ tb.ty == Taarray ||
+ tb.ty == Tstruct && tb.hasPointers();
+ }
+
+ Type at = cast(Type)(*dedtypes)[i];
+ Type tt;
+ if (ubyte wx = deduceWildHelper(e.type, &tt, tparam))
+ {
+ *wm |= wx;
+ result = MATCH.constant;
+ }
+ else if (MATCH m = deduceTypeHelper(e.type, &tt, tparam))
+ {
+ result = m;
+ }
+ else if (!isTopRef(e.type))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=15653
+ * In IFTI, recognize top-qualifier conversions
+ * through the value copy, e.g.
+ * int --> immutable(int)
+ * immutable(string[]) --> immutable(string)[]
+ */
+ tt = e.type.mutableOf();
+ result = MATCH.convert;
+ }
+ else
+ return; // nomatch
+
+ // expression vs (none)
+ if (!at)
+ {
+ (*dedtypes)[i] = new TypeDeduced(tt, e, tparam);
+ return;
+ }
+
+ TypeDeduced xt = null;
+ if (at.ty == Tnone)
+ {
+ xt = cast(TypeDeduced)at;
+ at = xt.tded;
+ }
+
+ // From previous matched expressions to current deduced type
+ MATCH match1 = xt ? xt.matchAll(tt) : MATCH.nomatch;
+
+ // From current expressions to previous deduced type
+ Type pt = at.addMod(tparam.mod);
+ if (*wm)
+ pt = pt.substWildTo(*wm);
+ MATCH match2 = e.implicitConvTo(pt);
+
+ if (match1 > MATCH.nomatch && match2 > MATCH.nomatch)
+ {
+ if (at.implicitConvTo(tt) == MATCH.nomatch)
+ match1 = MATCH.nomatch; // Prefer at
+ else if (tt.implicitConvTo(at) == MATCH.nomatch)
+ match2 = MATCH.nomatch; // Prefer tt
+ else if (tt.isTypeBasic() && tt.ty == at.ty && tt.mod != at.mod)
+ {
+ if (!tt.isMutable() && !at.isMutable())
+ tt = tt.mutableOf().addMod(MODmerge(tt.mod, at.mod));
+ else if (tt.isMutable())
+ {
+ if (at.mod == 0) // Prefer unshared
+ match1 = MATCH.nomatch;
+ else
+ match2 = MATCH.nomatch;
+ }
+ else if (at.isMutable())
+ {
+ if (tt.mod == 0) // Prefer unshared
+ match2 = MATCH.nomatch;
+ else
+ match1 = MATCH.nomatch;
+ }
+ //printf("tt = %s, at = %s\n", tt.toChars(), at.toChars());
+ }
+ else
+ {
+ match1 = MATCH.nomatch;
+ match2 = MATCH.nomatch;
+ }
+ }
+ if (match1 > MATCH.nomatch)
+ {
+ // Prefer current match: tt
+ if (xt)
+ xt.update(tt, e, tparam);
+ else
+ (*dedtypes)[i] = tt;
+ result = match1;
+ return;
+ }
+ if (match2 > MATCH.nomatch)
+ {
+ // Prefer previous match: (*dedtypes)[i]
+ if (xt)
+ xt.update(e, tparam);
+ result = match2;
+ return;
+ }
+
+ /* Deduce common type
+ */
+ if (Type t = rawTypeMerge(at, tt))
+ {
+ if (xt)
+ xt.update(t, e, tparam);
+ else
+ (*dedtypes)[i] = t;
+
+ pt = tt.addMod(tparam.mod);
+ if (*wm)
+ pt = pt.substWildTo(*wm);
+ result = e.implicitConvTo(pt);
+ return;
+ }
+
+ result = MATCH.nomatch;
+ }
+
+ MATCH deduceEmptyArrayElement()
+ {
+ if (!emptyArrayElement)
+ {
+ emptyArrayElement = new IdentifierExp(Loc.initial, Id.p); // dummy
+ emptyArrayElement.type = Type.tvoid;
+ }
+ assert(tparam.ty == Tarray);
+
+ Type tn = (cast(TypeNext)tparam).next;
+ return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
+ }
+
+ override void visit(NullExp e)
+ {
+ if (tparam.ty == Tarray && e.type.ty == Tnull)
+ {
+ // tparam:T[] <- e:null (void[])
+ result = deduceEmptyArrayElement();
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(StringExp e)
+ {
+ Type taai;
+ if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.dim == 0))
+ {
+ // Consider compile-time known boundaries
+ e.type.nextOf().sarrayOf(e.len).accept(this);
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20092
+ if (e.elements && e.elements.dim && e.type.toBasetype().nextOf().ty == Tvoid)
+ {
+ result = deduceEmptyArrayElement();
+ return;
+ }
+ if ((!e.elements || !e.elements.dim) && e.type.toBasetype().nextOf().ty == Tvoid && tparam.ty == Tarray)
+ {
+ // tparam:T[] <- e:[] (void[])
+ result = deduceEmptyArrayElement();
+ return;
+ }
+
+ if (tparam.ty == Tarray && e.elements && e.elements.dim)
+ {
+ Type tn = (cast(TypeDArray)tparam).next;
+ result = MATCH.exact;
+ if (e.basis)
+ {
+ MATCH m = deduceType(e.basis, sc, tn, parameters, dedtypes, wm);
+ if (m < result)
+ result = m;
+ }
+ foreach (el; *e.elements)
+ {
+ if (result == MATCH.nomatch)
+ break;
+ if (!el)
+ continue;
+ MATCH m = deduceType(el, sc, tn, parameters, dedtypes, wm);
+ if (m < result)
+ result = m;
+ }
+ return;
+ }
+
+ Type taai;
+ if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.dim == 0))
+ {
+ // Consider compile-time known boundaries
+ e.type.nextOf().sarrayOf(e.elements.dim).accept(this);
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ if (tparam.ty == Taarray && e.keys && e.keys.dim)
+ {
+ TypeAArray taa = cast(TypeAArray)tparam;
+ result = MATCH.exact;
+ foreach (i, key; *e.keys)
+ {
+ MATCH m1 = deduceType(key, sc, taa.index, parameters, dedtypes, wm);
+ if (m1 < result)
+ result = m1;
+ if (result == MATCH.nomatch)
+ break;
+ MATCH m2 = deduceType((*e.values)[i], sc, taa.next, parameters, dedtypes, wm);
+ if (m2 < result)
+ result = m2;
+ if (result == MATCH.nomatch)
+ break;
+ }
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("e.type = %s, tparam = %s\n", e.type.toChars(), tparam.toChars());
+ if (e.td)
+ {
+ Type to = tparam;
+ if (!to.nextOf())
+ return;
+ auto tof = to.nextOf().isTypeFunction();
+ if (!tof)
+ return;
+
+ // Parameter types inference from 'tof'
+ assert(e.td._scope);
+ TypeFunction tf = cast(TypeFunction)e.fd.type;
+ //printf("\ttof = %s\n", tof.toChars());
+ //printf("\ttf = %s\n", tf.toChars());
+ const dim = tf.parameterList.length;
+
+ if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs)
+ return;
+
+ auto tiargs = new Objects();
+ tiargs.reserve(e.td.parameters.dim);
+
+ foreach (tp; *e.td.parameters)
+ {
+ size_t u = 0;
+ foreach (i, p; tf.parameterList)
+ {
+ if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
+ break;
+ ++u;
+ }
+ assert(u < dim);
+ Parameter pto = tof.parameterList[u];
+ if (!pto)
+ break;
+ Type t = pto.type.syntaxCopy(); // https://issues.dlang.org/show_bug.cgi?id=11774
+ if (reliesOnTemplateParameters(t, (*parameters)[inferStart .. parameters.dim]))
+ return;
+ t = t.typeSemantic(e.loc, sc);
+ if (t.ty == Terror)
+ return;
+ tiargs.push(t);
+ }
+
+ // Set target of return type inference
+ if (!tf.next && tof.next)
+ e.fd.treq = tparam;
+
+ auto ti = new TemplateInstance(e.loc, e.td, tiargs);
+ Expression ex = (new ScopeExp(e.loc, ti)).expressionSemantic(e.td._scope);
+
+ // Reset inference target for the later re-semantic
+ e.fd.treq = null;
+
+ if (ex.op == TOK.error)
+ return;
+ if (ex.op != TOK.function_)
+ return;
+ visit(ex.type);
+ return;
+ }
+
+ Type t = e.type;
+
+ if (t.ty == Tdelegate && tparam.ty == Tpointer)
+ return;
+
+ // Allow conversion from implicit function pointer to delegate
+ if (e.tok == TOK.reserved && t.ty == Tpointer && tparam.ty == Tdelegate)
+ {
+ TypeFunction tf = cast(TypeFunction)t.nextOf();
+ t = (new TypeDelegate(tf)).merge();
+ }
+ //printf("tparam = %s <= e.type = %s, t = %s\n", tparam.toChars(), e.type.toChars(), t.toChars());
+ visit(t);
+ }
+
+ override void visit(SliceExp e)
+ {
+ Type taai;
+ if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.dim == 0))
+ {
+ // Consider compile-time known boundaries
+ if (Type tsa = toStaticArrayType(e))
+ {
+ tsa.accept(this);
+ if (result > MATCH.convert)
+ result = MATCH.convert; // match with implicit conversion at most
+ return;
+ }
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(CommaExp e)
+ {
+ e.e2.accept(this);
+ }
+ }
+
+ scope DeduceType v = new DeduceType(sc, tparam, parameters, dedtypes, wm, inferStart, ignoreAliasThis);
+ if (Type t = isType(o))
+ t.accept(v);
+ else if (Expression e = isExpression(o))
+ {
+ assert(wm);
+ e.accept(v);
+ }
+ else
+ assert(0);
+ return v.result;
+}
+
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ * t = Tested type, if null, returns false.
+ * tparams = Template parameters.
+ * iStart = Start index of tparams to limit the tested parameters. If it's
+ * nonzero, tparams[0..iStart] will be excluded from the test target.
+ */
+bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0)
+{
+ return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.dim]);
+}
+
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ * t = Tested type, if null, returns false.
+ * tparams = Template parameters.
+ */
+private bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams)
+{
+ bool visitVector(TypeVector t)
+ {
+ return t.basetype.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitAArray(TypeAArray t)
+ {
+ return t.next.reliesOnTemplateParameters(tparams) ||
+ t.index.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitFunction(TypeFunction t)
+ {
+ foreach (i, fparam; t.parameterList)
+ {
+ if (fparam.type.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+ return t.next.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitIdentifier(TypeIdentifier t)
+ {
+ foreach (tp; tparams)
+ {
+ if (tp.ident.equals(t.ident))
+ return true;
+ }
+ return false;
+ }
+
+ bool visitInstance(TypeInstance t)
+ {
+ foreach (tp; tparams)
+ {
+ if (t.tempinst.name == tp.ident)
+ return true;
+ }
+
+ if (t.tempinst.tiargs)
+ foreach (arg; *t.tempinst.tiargs)
+ {
+ if (Type ta = isType(arg))
+ {
+ if (ta.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool visitTypeof(TypeTypeof t)
+ {
+ //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars());
+ return t.exp.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitTuple(TypeTuple t)
+ {
+ if (t.arguments)
+ foreach (arg; *t.arguments)
+ {
+ if (arg.type.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+
+ return false;
+ }
+
+ if (!t)
+ return false;
+
+ Type tb = t.toBasetype();
+ switch (tb.ty)
+ {
+ case Tvector: return visitVector(tb.isTypeVector());
+ case Taarray: return visitAArray(tb.isTypeAArray());
+ case Tfunction: return visitFunction(tb.isTypeFunction());
+ case Tident: return visitIdentifier(tb.isTypeIdentifier());
+ case Tinstance: return visitInstance(tb.isTypeInstance());
+ case Ttypeof: return visitTypeof(tb.isTypeTypeof());
+ case Ttuple: return visitTuple(tb.isTypeTuple());
+ case Tenum: return false;
+ default: return tb.nextOf().reliesOnTemplateParameters(tparams);
+ }
+}
+
+/***********************************************************
+ * Check whether the expression representation relies on one or more the template parameters.
+ * Params:
+ * e = expression to test
+ * tparams = Template parameters.
+ * Returns:
+ * true if it does
+ */
+private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams)
+{
+ extern (C++) final class ReliesOnTemplateParameters : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ TemplateParameter[] tparams;
+ bool result;
+
+ extern (D) this(TemplateParameter[] tparams)
+ {
+ this.tparams = tparams;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars());
+ }
+
+ override void visit(IdentifierExp e)
+ {
+ //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ foreach (tp; tparams)
+ {
+ if (e.ident == tp.ident)
+ {
+ result = true;
+ return;
+ }
+ }
+ }
+
+ override void visit(TupleExp e)
+ {
+ //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.exps)
+ {
+ foreach (ea; *e.exps)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.elements)
+ {
+ foreach (el; *e.elements)
+ {
+ el.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ foreach (ek; *e.keys)
+ {
+ ek.accept(this);
+ if (result)
+ return;
+ }
+ foreach (ev; *e.values)
+ {
+ ev.accept(this);
+ if (result)
+ return;
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.elements)
+ {
+ foreach (ea; *e.elements)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(TypeExp e)
+ {
+ //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = e.type.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(NewExp e)
+ {
+ //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.thisexp)
+ e.thisexp.accept(this);
+ if (!result && e.newargs)
+ {
+ foreach (ea; *e.newargs)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ result = e.newtype.reliesOnTemplateParameters(tparams);
+ if (!result && e.arguments)
+ {
+ foreach (ea; *e.arguments)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(NewAnonClassExp e)
+ {
+ //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = true;
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = true;
+ }
+
+ override void visit(TypeidExp e)
+ {
+ //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (auto ea = isExpression(e.obj))
+ ea.accept(this);
+ else if (auto ta = isType(e.obj))
+ result = ta.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(TraitsExp e)
+ {
+ //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.args)
+ {
+ foreach (oa; *e.args)
+ {
+ if (auto ea = isExpression(oa))
+ ea.accept(this);
+ else if (auto ta = isType(oa))
+ result = ta.reliesOnTemplateParameters(tparams);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(IsExp e)
+ {
+ //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = e.targ.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(UnaExp e)
+ {
+ //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.e1.accept(this);
+ }
+
+ override void visit(DotTemplateInstanceExp e)
+ {
+ //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ if (!result && e.ti.tiargs)
+ {
+ foreach (oa; *e.ti.tiargs)
+ {
+ if (auto ea = isExpression(oa))
+ ea.accept(this);
+ else if (auto ta = isType(oa))
+ result = ta.reliesOnTemplateParameters(tparams);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(CallExp e)
+ {
+ //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ if (!result && e.arguments)
+ {
+ foreach (ea; *e.arguments)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(CastExp e)
+ {
+ //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ // e.to can be null for cast() with no type
+ if (!result && e.to)
+ result = e.to.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(SliceExp e)
+ {
+ //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ if (!result && e.lwr)
+ e.lwr.accept(this);
+ if (!result && e.upr)
+ e.upr.accept(this);
+ }
+
+ override void visit(IntervalExp e)
+ {
+ //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.lwr.accept(this);
+ if (!result)
+ e.upr.accept(this);
+ }
+
+ override void visit(ArrayExp e)
+ {
+ //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ if (!result && e.arguments)
+ {
+ foreach (ea; *e.arguments)
+ ea.accept(this);
+ }
+ }
+
+ override void visit(BinExp e)
+ {
+ //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.e1.accept(this);
+ if (!result)
+ e.e2.accept(this);
+ }
+
+ override void visit(CondExp e)
+ {
+ //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.econd.accept(this);
+ if (!result)
+ visit(cast(BinExp)e);
+ }
+ }
+
+ scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
+ e.accept(v);
+ return v.result;
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateParameter
+ */
+extern (C++) class TemplateParameter : ASTNode
+{
+ Loc loc;
+ Identifier ident;
+
+ /* True if this is a part of precedent parameter specialization pattern.
+ *
+ * template A(T : X!TL, alias X, TL...) {}
+ * // X and TL are dependent template parameter
+ *
+ * A dependent template parameter should return MATCH.exact in matchArg()
+ * to respect the match level of the corresponding precedent parameter.
+ */
+ bool dependent;
+
+ /* ======================== TemplateParameter =============================== */
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ this.loc = loc;
+ this.ident = ident;
+ }
+
+ TemplateTypeParameter isTemplateTypeParameter()
+ {
+ return null;
+ }
+
+ TemplateValueParameter isTemplateValueParameter()
+ {
+ return null;
+ }
+
+ TemplateAliasParameter isTemplateAliasParameter()
+ {
+ return null;
+ }
+
+ TemplateThisParameter isTemplateThisParameter()
+ {
+ return null;
+ }
+
+ TemplateTupleParameter isTemplateTupleParameter()
+ {
+ return null;
+ }
+
+ abstract TemplateParameter syntaxCopy();
+
+ abstract bool declareParameter(Scope* sc);
+
+ abstract void print(RootObject oarg, RootObject oded);
+
+ abstract RootObject specialization();
+
+ abstract RootObject defaultArg(Loc instLoc, Scope* sc);
+
+ abstract bool hasDefaultArg();
+
+ override const(char)* toChars() const
+ {
+ return this.ident.toChars();
+ }
+
+ override DYNCAST dyncast() const pure @nogc nothrow @safe
+ {
+ return DYNCAST.templateparameter;
+ }
+
+ /* Create dummy argument based on parameter.
+ */
+ abstract RootObject dummyArg();
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateTypeParameter
+ * Syntax:
+ * ident : specType = defaultType
+ */
+extern (C++) class TemplateTypeParameter : TemplateParameter
+{
+ Type specType; // if !=null, this is the type specialization
+ Type defaultType;
+
+ extern (D) __gshared Type tdummy = null;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Type specType, Type defaultType)
+ {
+ super(loc, ident);
+ this.specType = specType;
+ this.defaultType = defaultType;
+ }
+
+ override final TemplateTypeParameter isTemplateTypeParameter()
+ {
+ return this;
+ }
+
+ override TemplateTypeParameter syntaxCopy()
+ {
+ return new TemplateTypeParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
+ }
+
+ override final bool declareParameter(Scope* sc)
+ {
+ //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars());
+ auto ti = new TypeIdentifier(loc, ident);
+ Declaration ad = new AliasDeclaration(loc, ident, ti);
+ return sc.insert(ad) !is null;
+ }
+
+ override final void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s\n", ident.toChars());
+
+ Type t = isType(oarg);
+ Type ta = isType(oded);
+ assert(ta);
+
+ if (specType)
+ printf("\tSpecialization: %s\n", specType.toChars());
+ if (defaultType)
+ printf("\tDefault: %s\n", defaultType.toChars());
+ printf("\tParameter: %s\n", t ? t.toChars() : "NULL");
+ printf("\tDeduced Type: %s\n", ta.toChars());
+ }
+
+ override final RootObject specialization()
+ {
+ return specType;
+ }
+
+ override final RootObject defaultArg(Loc instLoc, Scope* sc)
+ {
+ Type t = defaultType;
+ if (t)
+ {
+ t = t.syntaxCopy();
+ t = t.typeSemantic(loc, sc); // use the parameter loc
+ }
+ return t;
+ }
+
+ override final bool hasDefaultArg()
+ {
+ return defaultType !is null;
+ }
+
+ override final RootObject dummyArg()
+ {
+ Type t = specType;
+ if (!t)
+ {
+ // Use this for alias-parameter's too (?)
+ if (!tdummy)
+ tdummy = new TypeIdentifier(loc, ident);
+ t = tdummy;
+ }
+ return t;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateThisParameter
+ * Syntax:
+ * this ident : specType = defaultType
+ */
+extern (C++) final class TemplateThisParameter : TemplateTypeParameter
+{
+ extern (D) this(const ref Loc loc, Identifier ident, Type specType, Type defaultType)
+ {
+ super(loc, ident, specType, defaultType);
+ }
+
+ override TemplateThisParameter isTemplateThisParameter()
+ {
+ return this;
+ }
+
+ override TemplateThisParameter syntaxCopy()
+ {
+ return new TemplateThisParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateValueParameter
+ * Syntax:
+ * valType ident : specValue = defaultValue
+ */
+extern (C++) final class TemplateValueParameter : TemplateParameter
+{
+ Type valType;
+ Expression specValue;
+ Expression defaultValue;
+
+ extern (D) __gshared Expression[void*] edummies;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Type valType,
+ Expression specValue, Expression defaultValue)
+ {
+ super(loc, ident);
+ this.valType = valType;
+ this.specValue = specValue;
+ this.defaultValue = defaultValue;
+ }
+
+ override TemplateValueParameter isTemplateValueParameter()
+ {
+ return this;
+ }
+
+ override TemplateValueParameter syntaxCopy()
+ {
+ return new TemplateValueParameter(loc, ident,
+ valType.syntaxCopy(),
+ specValue ? specValue.syntaxCopy() : null,
+ defaultValue ? defaultValue.syntaxCopy() : null);
+ }
+
+ override bool declareParameter(Scope* sc)
+ {
+ auto v = new VarDeclaration(loc, valType, ident, null);
+ v.storage_class = STC.templateparameter;
+ return sc.insert(v) !is null;
+ }
+
+ override void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s\n", ident.toChars());
+ Expression ea = isExpression(oded);
+ if (specValue)
+ printf("\tSpecialization: %s\n", specValue.toChars());
+ printf("\tParameter Value: %s\n", ea ? ea.toChars() : "NULL");
+ }
+
+ override RootObject specialization()
+ {
+ return specValue;
+ }
+
+ override RootObject defaultArg(Loc instLoc, Scope* sc)
+ {
+ Expression e = defaultValue;
+ if (e)
+ {
+ e = e.syntaxCopy();
+ uint olderrs = global.errors;
+ if ((e = e.expressionSemantic(sc)) is null)
+ return null;
+ if ((e = resolveProperties(sc, e)) is null)
+ return null;
+ e = e.resolveLoc(instLoc, sc); // use the instantiated loc
+ e = e.optimize(WANTvalue);
+ if (global.errors != olderrs)
+ e = ErrorExp.get();
+ }
+ return e;
+ }
+
+ override bool hasDefaultArg()
+ {
+ return defaultValue !is null;
+ }
+
+ override RootObject dummyArg()
+ {
+ Expression e = specValue;
+ if (!e)
+ {
+ // Create a dummy value
+ auto pe = cast(void*)valType in edummies;
+ if (!pe)
+ {
+ e = valType.defaultInit(Loc.initial);
+ edummies[cast(void*)valType] = e;
+ }
+ else
+ e = *pe;
+ }
+ return e;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateAliasParameter
+ * Syntax:
+ * specType ident : specAlias = defaultAlias
+ */
+extern (C++) final class TemplateAliasParameter : TemplateParameter
+{
+ Type specType;
+ RootObject specAlias;
+ RootObject defaultAlias;
+
+ extern (D) __gshared Dsymbol sdummy = null;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias)
+ {
+ super(loc, ident);
+ this.specType = specType;
+ this.specAlias = specAlias;
+ this.defaultAlias = defaultAlias;
+ }
+
+ override TemplateAliasParameter isTemplateAliasParameter()
+ {
+ return this;
+ }
+
+ override TemplateAliasParameter syntaxCopy()
+ {
+ return new TemplateAliasParameter(loc, ident, specType ? specType.syntaxCopy() : null, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias));
+ }
+
+ override bool declareParameter(Scope* sc)
+ {
+ auto ti = new TypeIdentifier(loc, ident);
+ Declaration ad = new AliasDeclaration(loc, ident, ti);
+ return sc.insert(ad) !is null;
+ }
+
+ override void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s\n", ident.toChars());
+ Dsymbol sa = isDsymbol(oded);
+ assert(sa);
+ printf("\tParameter alias: %s\n", sa.toChars());
+ }
+
+ override RootObject specialization()
+ {
+ return specAlias;
+ }
+
+ override RootObject defaultArg(Loc instLoc, Scope* sc)
+ {
+ RootObject da = defaultAlias;
+ Type ta = isType(defaultAlias);
+ if (ta)
+ {
+ if (ta.ty == Tinstance)
+ {
+ // If the default arg is a template, instantiate for each type
+ da = ta.syntaxCopy();
+ }
+ }
+
+ RootObject o = aliasParameterSemantic(loc, sc, da, null); // use the parameter loc
+ return o;
+ }
+
+ override bool hasDefaultArg()
+ {
+ return defaultAlias !is null;
+ }
+
+ override RootObject dummyArg()
+ {
+ RootObject s = specAlias;
+ if (!s)
+ {
+ if (!sdummy)
+ sdummy = new Dsymbol();
+ s = sdummy;
+ }
+ return s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateSequenceParameter
+ * Syntax:
+ * ident ...
+ */
+extern (C++) final class TemplateTupleParameter : TemplateParameter
+{
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, ident);
+ }
+
+ override TemplateTupleParameter isTemplateTupleParameter()
+ {
+ return this;
+ }
+
+ override TemplateTupleParameter syntaxCopy()
+ {
+ return new TemplateTupleParameter(loc, ident);
+ }
+
+ override bool declareParameter(Scope* sc)
+ {
+ auto ti = new TypeIdentifier(loc, ident);
+ Declaration ad = new AliasDeclaration(loc, ident, ti);
+ return sc.insert(ad) !is null;
+ }
+
+ override void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s... [", ident.toChars());
+ Tuple v = isTuple(oded);
+ assert(v);
+
+ //printf("|%d| ", v.objects.dim);
+ foreach (i, o; v.objects)
+ {
+ if (i)
+ printf(", ");
+
+ Dsymbol sa = isDsymbol(o);
+ if (sa)
+ printf("alias: %s", sa.toChars());
+ Type ta = isType(o);
+ if (ta)
+ printf("type: %s", ta.toChars());
+ Expression ea = isExpression(o);
+ if (ea)
+ printf("exp: %s", ea.toChars());
+
+ assert(!isTuple(o)); // no nested Tuple arguments
+ }
+ printf("]\n");
+ }
+
+ override RootObject specialization()
+ {
+ return null;
+ }
+
+ override RootObject defaultArg(Loc instLoc, Scope* sc)
+ {
+ return null;
+ }
+
+ override bool hasDefaultArg()
+ {
+ return false;
+ }
+
+ override RootObject dummyArg()
+ {
+ return null;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#explicit_tmp_instantiation
+ * Given:
+ * foo!(args) =>
+ * name = foo
+ * tiargs = args
+ */
+extern (C++) class TemplateInstance : ScopeDsymbol
+{
+ Identifier name;
+
+ // Array of Types/Expressions of template
+ // instance arguments [int*, char, 10*10]
+ Objects* tiargs;
+
+ // Array of Types/Expressions corresponding
+ // to TemplateDeclaration.parameters
+ // [int, char, 100]
+ Objects tdtypes;
+
+ // Modules imported by this template instance
+ Modules importedModules;
+
+ Dsymbol tempdecl; // referenced by foo.bar.abc
+ Dsymbol enclosing; // if referencing local symbols, this is the context
+ Dsymbol aliasdecl; // !=null if instance is an alias for its sole member
+ TemplateInstance inst; // refer to existing instance
+ ScopeDsymbol argsym; // argument symbol table
+ size_t hash; // cached result of toHash()
+ Expressions* fargs; // for function template, these are the function arguments
+
+ TemplateInstances* deferred;
+
+ Module memberOf; // if !null, then this TemplateInstance appears in memberOf.members[]
+
+ // Used to determine the instance needs code generation.
+ // Note that these are inaccurate until semantic analysis phase completed.
+ TemplateInstance tinst; // enclosing template instance
+ TemplateInstance tnext; // non-first instantiated instances
+ Module minst; // the top module that instantiated this instance
+
+ private ushort _nest; // for recursive pretty printing detection, 3 MSBs reserved for flags (below)
+ ubyte inuse; // for recursive expansion detection
+
+ private enum Flag : uint
+ {
+ semantictiargsdone = 1u << (_nest.sizeof * 8 - 1), // MSB of _nest
+ havetempdecl = semantictiargsdone >> 1,
+ gagged = semantictiargsdone >> 2,
+ available = gagged - 1 // always last flag minus one, 1s for all available bits
+ }
+
+ extern(D) final @safe @property pure nothrow @nogc
+ {
+ ushort nest() const { return _nest & Flag.available; }
+ void nestUp() { assert(nest() < Flag.available); ++_nest; }
+ void nestDown() { assert(nest() > 0); --_nest; }
+ /// has semanticTiargs() been done?
+ bool semantictiargsdone() const { return (_nest & Flag.semantictiargsdone) != 0; }
+ void semantictiargsdone(bool x)
+ {
+ if (x) _nest |= Flag.semantictiargsdone;
+ else _nest &= ~Flag.semantictiargsdone;
+ }
+ /// if used second constructor
+ bool havetempdecl() const { return (_nest & Flag.havetempdecl) != 0; }
+ void havetempdecl(bool x)
+ {
+ if (x) _nest |= Flag.havetempdecl;
+ else _nest &= ~Flag.havetempdecl;
+ }
+ /// if the instantiation is done with error gagging
+ bool gagged() const { return (_nest & Flag.gagged) != 0; }
+ void gagged(bool x)
+ {
+ if (x) _nest |= Flag.gagged;
+ else _nest &= ~Flag.gagged;
+ }
+ }
+
+ extern (D) this(const ref Loc loc, Identifier ident, Objects* tiargs)
+ {
+ super(loc, null);
+ static if (LOG)
+ {
+ printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null");
+ }
+ this.name = ident;
+ this.tiargs = tiargs;
+ }
+
+ /*****************
+ * This constructor is only called when we figured out which function
+ * template to instantiate.
+ */
+ extern (D) this(const ref Loc loc, TemplateDeclaration td, Objects* tiargs)
+ {
+ super(loc, null);
+ static if (LOG)
+ {
+ printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars());
+ }
+ this.name = td.ident;
+ this.tiargs = tiargs;
+ this.tempdecl = td;
+ this.semantictiargsdone = true;
+ this.havetempdecl = true;
+ assert(tempdecl._scope);
+ }
+
+ extern (D) static Objects* arraySyntaxCopy(Objects* objs)
+ {
+ Objects* a = null;
+ if (objs)
+ {
+ a = new Objects(objs.dim);
+ foreach (i, o; *objs)
+ (*a)[i] = objectSyntaxCopy(o);
+ }
+ return a;
+ }
+
+ override TemplateInstance syntaxCopy(Dsymbol s)
+ {
+ TemplateInstance ti = s ? cast(TemplateInstance)s : new TemplateInstance(loc, name, null);
+ ti.tiargs = arraySyntaxCopy(tiargs);
+ TemplateDeclaration td;
+ if (inst && tempdecl && (td = tempdecl.isTemplateDeclaration()) !is null)
+ td.ScopeDsymbol.syntaxCopy(ti);
+ else
+ ScopeDsymbol.syntaxCopy(ti);
+ return ti;
+ }
+
+ // resolve real symbol
+ override final Dsymbol toAlias()
+ {
+ static if (LOG)
+ {
+ printf("TemplateInstance.toAlias()\n");
+ }
+ if (!inst)
+ {
+ // Maybe we can resolve it
+ if (_scope)
+ {
+ dsymbolSemantic(this, _scope);
+ }
+ if (!inst)
+ {
+ error("cannot resolve forward reference");
+ errors = true;
+ return this;
+ }
+ }
+
+ if (inst != this)
+ return inst.toAlias();
+
+ if (aliasdecl)
+ {
+ return aliasdecl.toAlias();
+ }
+
+ return inst;
+ }
+
+ override const(char)* kind() const
+ {
+ return "template instance";
+ }
+
+ override bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ *ps = null;
+ return true;
+ }
+
+ override const(char)* toChars() const
+ {
+ OutBuffer buf;
+ toCBufferInstance(this, &buf);
+ return buf.extractChars();
+ }
+
+ override final const(char)* toPrettyCharsHelper()
+ {
+ OutBuffer buf;
+ toCBufferInstance(this, &buf, true);
+ return buf.extractChars();
+ }
+
+ /**************************************
+ * Given an error instantiating the TemplateInstance,
+ * give the nested TemplateInstance instantiations that got
+ * us here. Those are a list threaded into the nested scopes.
+ */
+ extern(D) final void printInstantiationTrace(Classification cl = Classification.error)
+ {
+ if (global.gag)
+ return;
+
+ // Print full trace for verbose mode, otherwise only short traces
+ const(uint) max_shown = !global.params.verbose ? 6 : uint.max;
+ const(char)* format = "instantiated from here: `%s`";
+
+ // This returns a function pointer
+ scope printFn = () {
+ final switch (cl)
+ {
+ case Classification.error:
+ return &errorSupplemental;
+ case Classification.warning:
+ return &warningSupplemental;
+ case Classification.deprecation:
+ return &deprecationSupplemental;
+ case Classification.gagged, Classification.tip:
+ assert(0);
+ }
+ }();
+
+ // determine instantiation depth and number of recursive instantiations
+ int n_instantiations = 1;
+ int n_totalrecursions = 0;
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
+ {
+ ++n_instantiations;
+ // Set error here as we don't want it to depend on the number of
+ // entries that are being printed.
+ if (cl == Classification.error ||
+ (cl == Classification.warning && global.params.warnings == DiagnosticReporting.error) ||
+ (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error))
+ cur.errors = true;
+
+ // If two instantiations use the same declaration, they are recursive.
+ // (this works even if they are instantiated from different places in the
+ // same template).
+ // In principle, we could also check for multiple-template recursion, but it's
+ // probably not worthwhile.
+ if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
+ ++n_totalrecursions;
+ }
+
+ if (n_instantiations <= max_shown)
+ {
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
+ printFn(cur.loc, format, cur.toChars());
+ }
+ else if (n_instantiations - n_totalrecursions <= max_shown)
+ {
+ // By collapsing recursive instantiations into a single line,
+ // we can stay under the limit.
+ int recursionDepth = 0;
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
+ {
+ if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
+ {
+ ++recursionDepth;
+ }
+ else
+ {
+ if (recursionDepth)
+ printFn(cur.loc, "%d recursive instantiations from here: `%s`", recursionDepth + 2, cur.toChars());
+ else
+ printFn(cur.loc, format, cur.toChars());
+ recursionDepth = 0;
+ }
+ }
+ }
+ else
+ {
+ // Even after collapsing the recursions, the depth is too deep.
+ // Just display the first few and last few instantiations.
+ uint i = 0;
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
+ {
+ if (i == max_shown / 2)
+ printFn(cur.loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown);
+
+ if (i < max_shown / 2 || i >= n_instantiations - max_shown + max_shown / 2)
+ printFn(cur.loc, format, cur.toChars());
+ ++i;
+ }
+ }
+ }
+
+ /*************************************
+ * Lazily generate identifier for template instance.
+ * This is because 75% of the ident's are never needed.
+ */
+ override final Identifier getIdent()
+ {
+ if (!ident && inst && !errors)
+ ident = genIdent(tiargs); // need an identifier for name mangling purposes.
+ return ident;
+ }
+
+ /*************************************
+ * Compare proposed template instantiation with existing template instantiation.
+ * Note that this is not commutative because of the auto ref check.
+ * Params:
+ * ti = existing template instantiation
+ * Returns:
+ * true for match
+ */
+ final bool equalsx(TemplateInstance ti)
+ {
+ //printf("this = %p, ti = %p\n", this, ti);
+ assert(tdtypes.dim == ti.tdtypes.dim);
+
+ // Nesting must match
+ if (enclosing != ti.enclosing)
+ {
+ //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : "");
+ goto Lnotequals;
+ }
+ //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars());
+
+ if (!arrayObjectMatch(&tdtypes, &ti.tdtypes))
+ goto Lnotequals;
+
+ /* Template functions may have different instantiations based on
+ * "auto ref" parameters.
+ */
+ if (auto fd = ti.toAlias().isFuncDeclaration())
+ {
+ if (!fd.errors)
+ {
+ auto fparameters = fd.getParameterList();
+ size_t nfparams = fparameters.length; // Num function parameters
+ for (size_t j = 0; j < nfparams; j++)
+ {
+ Parameter fparam = fparameters[j];
+ if (fparam.storageClass & STC.autoref) // if "auto ref"
+ {
+ Expression farg = fargs && j < fargs.dim ? (*fargs)[j] : fparam.defaultArg;
+ if (!farg)
+ goto Lnotequals;
+ if (farg.isLvalue())
+ {
+ if (!(fparam.storageClass & STC.ref_))
+ goto Lnotequals; // auto ref's don't match
+ }
+ else
+ {
+ if (fparam.storageClass & STC.ref_)
+ goto Lnotequals; // auto ref's don't match
+ }
+ }
+ }
+ }
+ }
+ return true;
+
+ Lnotequals:
+ return false;
+ }
+
+ final size_t toHash()
+ {
+ if (!hash)
+ {
+ hash = cast(size_t)cast(void*)enclosing;
+ hash += arrayObjectHash(&tdtypes);
+ hash += hash == 0;
+ }
+ return hash;
+ }
+
+ /**
+ Returns: true if the instances' innards are discardable.
+
+ The idea of this function is to see if the template instantiation
+ can be 100% replaced with its eponymous member. All other members
+ can be discarded, even in the compiler to free memory (for example,
+ the template could be expanded in a region allocator, deemed trivial,
+ the end result copied back out independently and the entire region freed),
+ and can be elided entirely from the binary.
+
+ The current implementation affects code that generally looks like:
+
+ ---
+ template foo(args...) {
+ some_basic_type_or_string helper() { .... }
+ enum foo = helper();
+ }
+ ---
+
+ since it was the easiest starting point of implementation but it can and
+ should be expanded more later.
+ */
+ final bool isDiscardable()
+ {
+ if (aliasdecl is null)
+ return false;
+
+ auto v = aliasdecl.isVarDeclaration();
+ if (v is null)
+ return false;
+
+ if (!(v.storage_class & STC.manifest))
+ return false;
+
+ // Currently only doing basic types here because it is the easiest proof-of-concept
+ // implementation with minimal risk of side effects, but it could likely be
+ // expanded to any type that already exists outside this particular instance.
+ if (!(v.type.equals(Type.tstring) || (v.type.isTypeBasic() !is null)))
+ return false;
+
+ // Static ctors and dtors, even in an eponymous enum template, are still run,
+ // so if any of them are in here, we'd better not assume it is trivial lest
+ // we break useful code
+ foreach(member; *members)
+ {
+ if(member.hasStaticCtorOrDtor())
+ return false;
+ if(member.isStaticDtorDeclaration())
+ return false;
+ if(member.isStaticCtorDeclaration())
+ return false;
+ }
+
+ // but if it passes through this gauntlet... it should be fine. D code will
+ // see only the eponymous member, outside stuff can never access it, even through
+ // reflection; the outside world ought to be none the wiser. Even dmd should be
+ // able to simply free the memory of everything except the final result.
+
+ return true;
+ }
+
+
+ /***********************************************
+ * Returns true if this is not instantiated in non-root module, and
+ * is a part of non-speculative instantiatiation.
+ *
+ * Note: minst does not stabilize until semantic analysis is completed,
+ * so don't call this function during semantic analysis to return precise result.
+ */
+ final bool needsCodegen()
+ {
+ if (!minst)
+ {
+ // If this is a speculative instantiation,
+ // 1. do codegen if ancestors really needs codegen.
+ // 2. become non-speculative if siblings are not speculative
+
+ TemplateInstance tnext = this.tnext;
+ TemplateInstance tinst = this.tinst;
+ // At first, disconnect chain first to prevent infinite recursion.
+ this.tnext = null;
+ this.tinst = null;
+
+ // Determine necessity of tinst before tnext.
+ if (tinst && tinst.needsCodegen())
+ {
+ minst = tinst.minst; // cache result
+ if (global.params.allInst && minst)
+ {
+ return true;
+ }
+ assert(minst);
+ assert(minst.isRoot() || minst.rootImports());
+ return true;
+ }
+ if (tnext && (tnext.needsCodegen() || tnext.minst))
+ {
+ minst = tnext.minst; // cache result
+ if (global.params.allInst && minst)
+ {
+ return true;
+ }
+ assert(minst);
+ return minst.isRoot() || minst.rootImports();
+ }
+
+ // Elide codegen because this is really speculative.
+ return false;
+ }
+
+ if (global.params.allInst)
+ {
+ return true;
+ }
+
+ if (isDiscardable())
+ {
+ return false;
+ }
+
+ /* Even when this is reached to the codegen pass,
+ * a non-root nested template should not generate code,
+ * due to avoid ODR violation.
+ */
+ if (enclosing && enclosing.inNonRoot())
+ {
+ if (tinst)
+ {
+ auto r = tinst.needsCodegen();
+ minst = tinst.minst; // cache result
+ return r;
+ }
+ if (tnext)
+ {
+ auto r = tnext.needsCodegen();
+ minst = tnext.minst; // cache result
+ return r;
+ }
+ return false;
+ }
+
+ if (global.params.useUnitTests)
+ {
+ // Prefer instantiations from root modules, to maximize link-ability.
+ if (minst.isRoot())
+ return true;
+
+ TemplateInstance tnext = this.tnext;
+ TemplateInstance tinst = this.tinst;
+ this.tnext = null;
+ this.tinst = null;
+
+ if (tinst && tinst.needsCodegen())
+ {
+ minst = tinst.minst; // cache result
+ assert(minst);
+ assert(minst.isRoot() || minst.rootImports());
+ return true;
+ }
+ if (tnext && tnext.needsCodegen())
+ {
+ minst = tnext.minst; // cache result
+ assert(minst);
+ assert(minst.isRoot() || minst.rootImports());
+ return true;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=2500 case
+ if (minst.rootImports())
+ return true;
+
+ // Elide codegen because this is not included in root instances.
+ return false;
+ }
+ else
+ {
+ // Prefer instantiations from non-root module, to minimize object code size.
+
+ /* If a TemplateInstance is ever instantiated by non-root modules,
+ * we do not have to generate code for it,
+ * because it will be generated when the non-root module is compiled.
+ *
+ * But, if the non-root 'minst' imports any root modules, it might still need codegen.
+ *
+ * The problem is if A imports B, and B imports A, and both A
+ * and B instantiate the same template, does the compilation of A
+ * or the compilation of B do the actual instantiation?
+ *
+ * See https://issues.dlang.org/show_bug.cgi?id=2500.
+ */
+ if (!minst.isRoot() && !minst.rootImports())
+ return false;
+
+ TemplateInstance tnext = this.tnext;
+ this.tnext = null;
+
+ if (tnext && !tnext.needsCodegen() && tnext.minst)
+ {
+ minst = tnext.minst; // cache result
+ assert(!minst.isRoot());
+ return false;
+ }
+
+ // Do codegen because this is not included in non-root instances.
+ return true;
+ }
+ }
+
+ /**********************************************
+ * Find template declaration corresponding to template instance.
+ *
+ * Returns:
+ * false if finding fails.
+ * Note:
+ * This function is reentrant against error occurrence. If returns false,
+ * any members of this object won't be modified, and repetition call will
+ * reproduce same error.
+ */
+ extern (D) final bool findTempDecl(Scope* sc, WithScopeSymbol* pwithsym)
+ {
+ if (pwithsym)
+ *pwithsym = null;
+
+ if (havetempdecl)
+ return true;
+
+ //printf("TemplateInstance.findTempDecl() %s\n", toChars());
+ if (!tempdecl)
+ {
+ /* Given:
+ * foo!( ... )
+ * figure out which TemplateDeclaration foo refers to.
+ */
+ Identifier id = name;
+ Dsymbol scopesym;
+ Dsymbol s = sc.search(loc, id, &scopesym);
+ if (!s)
+ {
+ s = sc.search_correct(id);
+ if (s)
+ error("template `%s` is not defined, did you mean %s?", id.toChars(), s.toChars());
+ else
+ error("template `%s` is not defined", id.toChars());
+ return false;
+ }
+ static if (LOG)
+ {
+ printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind());
+ if (s.parent)
+ printf("s.parent = '%s'\n", s.parent.toChars());
+ }
+ if (pwithsym)
+ *pwithsym = scopesym.isWithScopeSymbol();
+
+ /* We might have found an alias within a template when
+ * we really want the template.
+ */
+ TemplateInstance ti;
+ if (s.parent && (ti = s.parent.isTemplateInstance()) !is null)
+ {
+ if (ti.tempdecl && ti.tempdecl.ident == id)
+ {
+ /* This is so that one can refer to the enclosing
+ * template, even if it has the same name as a member
+ * of the template, if it has a !(arguments)
+ */
+ TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
+ assert(td);
+ if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+ td = td.overroot; // then get the start
+ s = td;
+ }
+ }
+
+ // The template might originate from a selective import which implies that
+ // s is a lowered AliasDeclaration of the actual TemplateDeclaration.
+ // This is the last place where we see the deprecated alias because it is
+ // stripped below, so check if the selective import was deprecated.
+ // See https://issues.dlang.org/show_bug.cgi?id=20840.
+ if (s.isAliasDeclaration())
+ s.checkDeprecated(this.loc, sc);
+
+ if (!updateTempDecl(sc, s))
+ {
+ return false;
+ }
+ }
+ assert(tempdecl);
+
+ // Look for forward references
+ auto tovers = tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+
+ if (td.semanticRun == PASS.init)
+ {
+ if (td._scope)
+ {
+ // Try to fix forward reference. Ungag errors while doing so.
+ Ungag ungag = td.ungagSpeculative();
+ td.dsymbolSemantic(td._scope);
+ }
+ if (td.semanticRun == PASS.init)
+ {
+ error("`%s` forward references template declaration `%s`",
+ toChars(), td.toChars());
+ return 1;
+ }
+ }
+ return 0;
+ });
+ if (r)
+ return false;
+ }
+ return true;
+ }
+
+ /**********************************************
+ * Confirm s is a valid template, then store it.
+ * Input:
+ * sc
+ * s candidate symbol of template. It may be:
+ * TemplateDeclaration
+ * FuncDeclaration with findTemplateDeclRoot() != NULL
+ * OverloadSet which contains candidates
+ * Returns:
+ * true if updating succeeds.
+ */
+ extern (D) final bool updateTempDecl(Scope* sc, Dsymbol s)
+ {
+ if (!s)
+ return tempdecl !is null;
+
+ Identifier id = name;
+ s = s.toAlias();
+
+ /* If an OverloadSet, look for a unique member that is a template declaration
+ */
+ if (OverloadSet os = s.isOverloadSet())
+ {
+ s = null;
+ foreach (s2; os.a)
+ {
+ if (FuncDeclaration f = s2.isFuncDeclaration())
+ s2 = f.findTemplateDeclRoot();
+ else
+ s2 = s2.isTemplateDeclaration();
+ if (s2)
+ {
+ if (s)
+ {
+ tempdecl = os;
+ return true;
+ }
+ s = s2;
+ }
+ }
+ if (!s)
+ {
+ error("template `%s` is not defined", id.toChars());
+ return false;
+ }
+ }
+
+ if (OverDeclaration od = s.isOverDeclaration())
+ {
+ tempdecl = od; // TODO: more strict check
+ return true;
+ }
+
+ /* It should be a TemplateDeclaration, not some other symbol
+ */
+ if (FuncDeclaration f = s.isFuncDeclaration())
+ tempdecl = f.findTemplateDeclRoot();
+ else
+ tempdecl = s.isTemplateDeclaration();
+
+ // We're done
+ if (tempdecl)
+ return true;
+
+ // Error already issued, just return `false`
+ if (!s.parent && global.errors)
+ return false;
+
+ if (!s.parent && s.getType())
+ {
+ Dsymbol s2 = s.getType().toDsymbol(sc);
+ if (!s2)
+ {
+ .error(loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", toChars(), id.toChars(), id.toChars(), s.getType.kind());
+ return false;
+ }
+ // because s can be the alias created for a TemplateParameter
+ const AliasDeclaration ad = s.isAliasDeclaration();
+ version (none)
+ {
+ if (ad && ad.isAliasedTemplateParameter())
+ printf("`%s` is an alias created from a template parameter\n", s.toChars());
+ }
+ if (!ad || !ad.isAliasedTemplateParameter())
+ s = s2;
+ }
+
+ TemplateInstance ti = s.parent ? s.parent.isTemplateInstance() : null;
+ if (ti && (ti.name == s.ident || ti.toAlias().ident == s.ident) && ti.tempdecl)
+ {
+ /* This is so that one can refer to the enclosing
+ * template, even if it has the same name as a member
+ * of the template, if it has a !(arguments)
+ */
+ TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
+ assert(td);
+ if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+ td = td.overroot; // then get the start
+ tempdecl = td;
+ return true;
+ }
+ else
+ {
+ error("`%s` is not a template declaration, it is a %s", id.toChars(), s.kind());
+ return false;
+ }
+ }
+
+ /**********************************
+ * Run semantic of tiargs as arguments of template.
+ * Input:
+ * loc
+ * sc
+ * tiargs array of template arguments
+ * flags 1: replace const variables with their initializers
+ * 2: don't devolve Parameter to Type
+ * Returns:
+ * false if one or more arguments have errors.
+ */
+ extern (D) static bool semanticTiargs(const ref Loc loc, Scope* sc, Objects* tiargs, int flags)
+ {
+ // Run semantic on each argument, place results in tiargs[]
+ //printf("+TemplateInstance.semanticTiargs()\n");
+ if (!tiargs)
+ return true;
+ bool err = false;
+ for (size_t j = 0; j < tiargs.dim; j++)
+ {
+ RootObject o = (*tiargs)[j];
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+
+ //printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta);
+ if (ta)
+ {
+ //printf("type %s\n", ta.toChars());
+
+ // It might really be an Expression or an Alias
+ ta.resolve(loc, sc, ea, ta, sa, (flags & 1) != 0);
+ if (ea)
+ goto Lexpr;
+ if (sa)
+ goto Ldsym;
+ if (ta is null)
+ {
+ assert(global.errors);
+ ta = Type.terror;
+ }
+
+ Ltype:
+ if (ta.ty == Ttuple)
+ {
+ // Expand tuple
+ TypeTuple tt = cast(TypeTuple)ta;
+ size_t dim = tt.arguments.dim;
+ tiargs.remove(j);
+ if (dim)
+ {
+ tiargs.reserve(dim);
+ foreach (i, arg; *tt.arguments)
+ {
+ if (flags & 2 && (arg.storageClass & STC.parameter))
+ tiargs.insert(j + i, arg);
+ else
+ tiargs.insert(j + i, arg.type);
+ }
+ }
+ j--;
+ continue;
+ }
+ if (ta.ty == Terror)
+ {
+ err = true;
+ continue;
+ }
+ (*tiargs)[j] = ta.merge2();
+ }
+ else if (ea)
+ {
+ Lexpr:
+ //printf("+[%d] ea = %s %s\n", j, Token.toChars(ea.op), ea.toChars());
+ if (flags & 1) // only used by __traits
+ {
+ ea = ea.expressionSemantic(sc);
+
+ // must not interpret the args, excepting template parameters
+ if (ea.op != TOK.variable || ((cast(VarExp)ea).var.storage_class & STC.templateparameter))
+ {
+ ea = ea.optimize(WANTvalue);
+ }
+ }
+ else
+ {
+ sc = sc.startCTFE();
+ ea = ea.expressionSemantic(sc);
+ sc = sc.endCTFE();
+
+ if (ea.op == TOK.variable)
+ {
+ /* If the parameter is a function that is not called
+ * explicitly, i.e. `foo!func` as opposed to `foo!func()`,
+ * then it is a dsymbol, not the return value of `func()`
+ */
+ Declaration vd = (cast(VarExp)ea).var;
+ if (auto fd = vd.isFuncDeclaration())
+ {
+ sa = fd;
+ goto Ldsym;
+ }
+ /* Otherwise skip substituting a const var with
+ * its initializer. The problem is the initializer won't
+ * match with an 'alias' parameter. Instead, do the
+ * const substitution in TemplateValueParameter.matchArg().
+ */
+ }
+ else if (definitelyValueParameter(ea))
+ {
+ if (ea.checkValue()) // check void expression
+ ea = ErrorExp.get();
+ uint olderrs = global.errors;
+ ea = ea.ctfeInterpret();
+ if (global.errors != olderrs)
+ ea = ErrorExp.get();
+ }
+ }
+ //printf("-[%d] ea = %s %s\n", j, Token.toChars(ea.op), ea.toChars());
+ if (ea.op == TOK.tuple)
+ {
+ // Expand tuple
+ TupleExp te = cast(TupleExp)ea;
+ size_t dim = te.exps.dim;
+ tiargs.remove(j);
+ if (dim)
+ {
+ tiargs.reserve(dim);
+ foreach (i, exp; *te.exps)
+ tiargs.insert(j + i, exp);
+ }
+ j--;
+ continue;
+ }
+ if (ea.op == TOK.error)
+ {
+ err = true;
+ continue;
+ }
+ (*tiargs)[j] = ea;
+
+ if (ea.op == TOK.type)
+ {
+ ta = ea.type;
+ goto Ltype;
+ }
+ if (ea.op == TOK.scope_)
+ {
+ sa = (cast(ScopeExp)ea).sds;
+ goto Ldsym;
+ }
+ if (ea.op == TOK.function_)
+ {
+ FuncExp fe = cast(FuncExp)ea;
+ /* A function literal, that is passed to template and
+ * already semanticed as function pointer, never requires
+ * outer frame. So convert it to global function is valid.
+ */
+ if (fe.fd.tok == TOK.reserved && fe.type.ty == Tpointer)
+ {
+ // change to non-nested
+ fe.fd.tok = TOK.function_;
+ fe.fd.vthis = null;
+ }
+ else if (fe.td)
+ {
+ /* If template argument is a template lambda,
+ * get template declaration itself. */
+ //sa = fe.td;
+ //goto Ldsym;
+ }
+ }
+ if (ea.op == TOK.dotVariable && !(flags & 1))
+ {
+ // translate expression to dsymbol.
+ sa = (cast(DotVarExp)ea).var;
+ goto Ldsym;
+ }
+ if (ea.op == TOK.template_)
+ {
+ sa = (cast(TemplateExp)ea).td;
+ goto Ldsym;
+ }
+ if (ea.op == TOK.dotTemplateDeclaration && !(flags & 1))
+ {
+ // translate expression to dsymbol.
+ sa = (cast(DotTemplateExp)ea).td;
+ goto Ldsym;
+ }
+ if (ea.op == TOK.dot)
+ {
+ if (auto se = (cast(DotExp)ea).e2.isScopeExp())
+ {
+ sa = se.sds;
+ goto Ldsym;
+ }
+ }
+ }
+ else if (sa)
+ {
+ Ldsym:
+ //printf("dsym %s %s\n", sa.kind(), sa.toChars());
+ if (sa.errors)
+ {
+ err = true;
+ continue;
+ }
+
+ TupleDeclaration d = sa.toAlias().isTupleDeclaration();
+ if (d)
+ {
+ // Expand tuple
+ tiargs.remove(j);
+ tiargs.insert(j, d.objects);
+ j--;
+ continue;
+ }
+ if (FuncAliasDeclaration fa = sa.isFuncAliasDeclaration())
+ {
+ FuncDeclaration f = fa.toAliasFunc();
+ if (!fa.hasOverloads && f.isUnique())
+ {
+ // Strip FuncAlias only when the aliased function
+ // does not have any overloads.
+ sa = f;
+ }
+ }
+ (*tiargs)[j] = sa;
+
+ TemplateDeclaration td = sa.isTemplateDeclaration();
+ if (td && td.semanticRun == PASS.init && td.literal)
+ {
+ td.dsymbolSemantic(sc);
+ }
+ FuncDeclaration fd = sa.isFuncDeclaration();
+ if (fd)
+ fd.functionSemantic();
+ }
+ else if (isParameter(o))
+ {
+ }
+ else
+ {
+ assert(0);
+ }
+ //printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]);
+ }
+ version (none)
+ {
+ printf("-TemplateInstance.semanticTiargs()\n");
+ for (size_t j = 0; j < tiargs.dim; j++)
+ {
+ RootObject o = (*tiargs)[j];
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+ Tuple va = isTuple(o);
+ printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va);
+ }
+ }
+ return !err;
+ }
+
+ /**********************************
+ * Run semantic on the elements of tiargs.
+ * Input:
+ * sc
+ * Returns:
+ * false if one or more arguments have errors.
+ * Note:
+ * This function is reentrant against error occurrence. If returns false,
+ * all elements of tiargs won't be modified.
+ */
+ extern (D) final bool semanticTiargs(Scope* sc)
+ {
+ //printf("+TemplateInstance.semanticTiargs() %s\n", toChars());
+ if (semantictiargsdone)
+ return true;
+ if (semanticTiargs(loc, sc, tiargs, 0))
+ {
+ // cache the result iff semantic analysis succeeded entirely
+ semantictiargsdone = 1;
+ return true;
+ }
+ return false;
+ }
+
+ /**********************************
+ * Find the TemplateDeclaration that matches this TemplateInstance best.
+ *
+ * Params:
+ * sc = the scope this TemplateInstance resides in
+ * fargs = function arguments in case of a template function, null otherwise
+ *
+ * Returns:
+ * `true` if a match was found, `false` otherwise
+ */
+ extern (D) final bool findBestMatch(Scope* sc, Expressions* fargs)
+ {
+ if (havetempdecl)
+ {
+ TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+ assert(tempdecl._scope);
+ // Deduce tdtypes
+ tdtypes.setDim(tempdecl.parameters.dim);
+ if (!tempdecl.matchWithInstance(sc, this, &tdtypes, fargs, 2))
+ {
+ error("incompatible arguments for template instantiation");
+ return false;
+ }
+ // TODO: Normalizing tiargs for https://issues.dlang.org/show_bug.cgi?id=7469 is necessary?
+ return true;
+ }
+
+ static if (LOG)
+ {
+ printf("TemplateInstance.findBestMatch()\n");
+ }
+
+ uint errs = global.errors;
+ TemplateDeclaration td_last = null;
+ Objects dedtypes;
+
+ /* Since there can be multiple TemplateDeclaration's with the same
+ * name, look for the best match.
+ */
+ auto tovers = tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1)
+ {
+ TemplateDeclaration td_best;
+ TemplateDeclaration td_ambig;
+ MATCH m_best = MATCH.nomatch;
+
+ Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
+ overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+ if (td.inuse)
+ {
+ td.error(loc, "recursive template expansion");
+ return 1;
+ }
+ if (td == td_best) // skip duplicates
+ return 0;
+
+ //printf("td = %s\n", td.toPrettyChars());
+ // If more arguments than parameters,
+ // then this is no match.
+ if (td.parameters.dim < tiargs.dim)
+ {
+ if (!td.isVariadic())
+ return 0;
+ }
+
+ dedtypes.setDim(td.parameters.dim);
+ dedtypes.zero();
+ assert(td.semanticRun != PASS.init);
+
+ MATCH m = td.matchWithInstance(sc, this, &dedtypes, fargs, 0);
+ //printf("matchWithInstance = %d\n", m);
+ if (m == MATCH.nomatch) // no match at all
+ return 0;
+ if (m < m_best) goto Ltd_best;
+ if (m > m_best) goto Ltd;
+
+ // Disambiguate by picking the most specialized TemplateDeclaration
+ {
+ MATCH c1 = td.leastAsSpecialized(sc, td_best, fargs);
+ MATCH c2 = td_best.leastAsSpecialized(sc, td, fargs);
+ //printf("c1 = %d, c2 = %d\n", c1, c2);
+ if (c1 > c2) goto Ltd;
+ if (c1 < c2) goto Ltd_best;
+ }
+
+ td_ambig = td;
+ return 0;
+
+ Ltd_best:
+ // td_best is the best match so far
+ td_ambig = null;
+ return 0;
+
+ Ltd:
+ // td is the new best match
+ td_ambig = null;
+ td_best = td;
+ m_best = m;
+ tdtypes.setDim(dedtypes.dim);
+ memcpy(tdtypes.tdata(), dedtypes.tdata(), tdtypes.dim * (void*).sizeof);
+ return 0;
+ });
+
+ if (td_ambig)
+ {
+ .error(loc, "%s `%s.%s` matches more than one template declaration:\n%s: `%s`\nand\n%s: `%s`",
+ td_best.kind(), td_best.parent.toPrettyChars(), td_best.ident.toChars(),
+ td_best.loc.toChars(), td_best.toChars(),
+ td_ambig.loc.toChars(), td_ambig.toChars());
+ return false;
+ }
+ if (td_best)
+ {
+ if (!td_last)
+ td_last = td_best;
+ else if (td_last != td_best)
+ {
+ ScopeDsymbol.multiplyDefined(loc, td_last, td_best);
+ return false;
+ }
+ }
+ }
+
+ if (td_last)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=7469
+ * Normalize tiargs by using corresponding deduced
+ * template value parameters and tuples for the correct mangling.
+ *
+ * By doing this before hasNestedArgs, CTFEable local variable will be
+ * accepted as a value parameter. For example:
+ *
+ * void foo() {
+ * struct S(int n) {} // non-global template
+ * const int num = 1; // CTFEable local variable
+ * S!num s; // S!1 is instantiated, not S!num
+ * }
+ */
+ size_t dim = td_last.parameters.dim - (td_last.isVariadic() ? 1 : 0);
+ for (size_t i = 0; i < dim; i++)
+ {
+ if (tiargs.dim <= i)
+ tiargs.push(tdtypes[i]);
+ assert(i < tiargs.dim);
+
+ auto tvp = (*td_last.parameters)[i].isTemplateValueParameter();
+ if (!tvp)
+ continue;
+ assert(tdtypes[i]);
+ // tdtypes[i] is already normalized to the required type in matchArg
+
+ (*tiargs)[i] = tdtypes[i];
+ }
+ if (td_last.isVariadic() && tiargs.dim == dim && tdtypes[dim])
+ {
+ Tuple va = isTuple(tdtypes[dim]);
+ assert(va);
+ tiargs.pushSlice(va.objects[]);
+ }
+ }
+ else if (errors && inst)
+ {
+ // instantiation was failed with error reporting
+ assert(global.errors);
+ return false;
+ }
+ else
+ {
+ auto tdecl = tempdecl.isTemplateDeclaration();
+
+ if (errs != global.errors)
+ errorSupplemental(loc, "while looking for match for `%s`", toChars());
+ else if (tdecl && !tdecl.overnext)
+ {
+ // Only one template, so we can give better error message
+ const(char)* msg = "does not match template declaration";
+ const(char)* tip;
+ const tmsg = tdecl.toCharsNoConstraints();
+ const cmsg = tdecl.getConstraintEvalError(tip);
+ if (cmsg)
+ {
+ error("%s `%s`\n%s", msg, tmsg, cmsg);
+ if (tip)
+ .tip(tip);
+ }
+ else
+ {
+ error("%s `%s`", msg, tmsg);
+
+ if (tdecl.parameters.dim == tiargs.dim)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=7352
+ // print additional information, e.g. `foo` is not a type
+ foreach (i, param; *tdecl.parameters)
+ {
+ MATCH match = param.matchArg(loc, sc, tiargs, i, tdecl.parameters, &dedtypes, null);
+ auto arg = (*tiargs)[i];
+ auto sym = arg.isDsymbol;
+ auto exp = arg.isExpression;
+
+ if (exp)
+ exp = exp.optimize(WANTvalue);
+
+ if (match == MATCH.nomatch &&
+ ((sym && sym.isFuncDeclaration) ||
+ (exp && exp.isVarExp)))
+ {
+ if (param.isTemplateTypeParameter)
+ errorSupplemental(loc, "`%s` is not a type", arg.toChars);
+ else if (auto tvp = param.isTemplateValueParameter)
+ errorSupplemental(loc, "`%s` is not of a value of type `%s`",
+ arg.toChars, tvp.valType.toChars);
+
+ }
+ }
+ }
+ }
+ }
+ else
+ .error(loc, "%s `%s.%s` does not match any template declaration", tempdecl.kind(), tempdecl.parent.toPrettyChars(), tempdecl.ident.toChars());
+ return false;
+ }
+
+ /* The best match is td_last
+ */
+ tempdecl = td_last;
+
+ static if (LOG)
+ {
+ printf("\tIt's a match with template declaration '%s'\n", tempdecl.toChars());
+ }
+ return (errs == global.errors);
+ }
+
+ /*****************************************************
+ * Determine if template instance is really a template function,
+ * and that template function needs to infer types from the function
+ * arguments.
+ *
+ * Like findBestMatch, iterate possible template candidates,
+ * but just looks only the necessity of type inference.
+ */
+ extern (D) final bool needsTypeInference(Scope* sc, int flag = 0)
+ {
+ //printf("TemplateInstance.needsTypeInference() %s\n", toChars());
+ if (semanticRun != PASS.init)
+ return false;
+
+ uint olderrs = global.errors;
+ Objects dedtypes;
+ size_t count = 0;
+
+ auto tovers = tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+ if (td.inuse)
+ {
+ td.error(loc, "recursive template expansion");
+ return 1;
+ }
+
+ /* If any of the overloaded template declarations need inference,
+ * then return true
+ */
+ if (!td.onemember)
+ return 0;
+ if (auto td2 = td.onemember.isTemplateDeclaration())
+ {
+ if (!td2.onemember || !td2.onemember.isFuncDeclaration())
+ return 0;
+ if (tiargs.dim >= td.parameters.dim - (td.isVariadic() ? 1 : 0))
+ return 0;
+ return 1;
+ }
+ auto fd = td.onemember.isFuncDeclaration();
+ if (!fd || fd.type.ty != Tfunction)
+ return 0;
+
+ foreach (tp; *td.parameters)
+ {
+ if (tp.isTemplateThisParameter())
+ return 1;
+ }
+
+ /* Determine if the instance arguments, tiargs, are all that is necessary
+ * to instantiate the template.
+ */
+ //printf("tp = %p, td.parameters.dim = %d, tiargs.dim = %d\n", tp, td.parameters.dim, tiargs.dim);
+ auto tf = cast(TypeFunction)fd.type;
+ if (tf.parameterList.length)
+ {
+ auto tp = td.isVariadic();
+ if (tp && td.parameters.dim > 1)
+ return 1;
+
+ if (!tp && tiargs.dim < td.parameters.dim)
+ {
+ // Can remain tiargs be filled by default arguments?
+ foreach (size_t i; tiargs.dim .. td.parameters.dim)
+ {
+ if (!(*td.parameters)[i].hasDefaultArg())
+ return 1;
+ }
+ }
+
+ foreach (i, fparam; tf.parameterList)
+ {
+ // 'auto ref' needs inference.
+ if (fparam.storageClass & STC.auto_)
+ return 1;
+ }
+ }
+
+ if (!flag)
+ {
+ /* Calculate the need for overload resolution.
+ * When only one template can match with tiargs, inference is not necessary.
+ */
+ dedtypes.setDim(td.parameters.dim);
+ dedtypes.zero();
+ if (td.semanticRun == PASS.init)
+ {
+ if (td._scope)
+ {
+ // Try to fix forward reference. Ungag errors while doing so.
+ Ungag ungag = td.ungagSpeculative();
+ td.dsymbolSemantic(td._scope);
+ }
+ if (td.semanticRun == PASS.init)
+ {
+ error("`%s` forward references template declaration `%s`", toChars(), td.toChars());
+ return 1;
+ }
+ }
+ MATCH m = td.matchWithInstance(sc, this, &dedtypes, null, 0);
+ if (m == MATCH.nomatch)
+ return 0;
+ }
+
+ /* If there is more than one function template which matches, we may
+ * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430)
+ */
+ return ++count > 1 ? 1 : 0;
+ });
+ if (r)
+ return true;
+ }
+
+ if (olderrs != global.errors)
+ {
+ if (!global.gag)
+ {
+ errorSupplemental(loc, "while looking for match for `%s`", toChars());
+ semanticRun = PASS.semanticdone;
+ inst = this;
+ }
+ errors = true;
+ }
+ //printf("false\n");
+ return false;
+ }
+
+ /*****************************************
+ * Determines if a TemplateInstance will need a nested
+ * generation of the TemplateDeclaration.
+ * Sets enclosing property if so, and returns != 0;
+ */
+ extern (D) final bool hasNestedArgs(Objects* args, bool isstatic)
+ {
+ int nested = 0;
+ //printf("TemplateInstance.hasNestedArgs('%s')\n", tempdecl.ident.toChars());
+
+ // arguments from parent instances are also accessible
+ if (!enclosing)
+ {
+ if (TemplateInstance ti = tempdecl.toParent().isTemplateInstance())
+ enclosing = ti.enclosing;
+ }
+
+ /* A nested instance happens when an argument references a local
+ * symbol that is on the stack.
+ */
+ foreach (o; *args)
+ {
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+ Tuple va = isTuple(o);
+ if (ea)
+ {
+ if (ea.op == TOK.variable)
+ {
+ sa = (cast(VarExp)ea).var;
+ goto Lsa;
+ }
+ if (ea.op == TOK.this_)
+ {
+ sa = (cast(ThisExp)ea).var;
+ goto Lsa;
+ }
+ if (ea.op == TOK.function_)
+ {
+ if ((cast(FuncExp)ea).td)
+ sa = (cast(FuncExp)ea).td;
+ else
+ sa = (cast(FuncExp)ea).fd;
+ goto Lsa;
+ }
+ // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent.
+ if (ea.op != TOK.int64 && ea.op != TOK.float64 && ea.op != TOK.complex80 && ea.op != TOK.null_ && ea.op != TOK.string_ && ea.op != TOK.arrayLiteral && ea.op != TOK.assocArrayLiteral && ea.op != TOK.structLiteral)
+ {
+ ea.error("expression `%s` is not a valid template value argument", ea.toChars());
+ errors = true;
+ }
+ }
+ else if (sa)
+ {
+ Lsa:
+ sa = sa.toAlias();
+ TemplateDeclaration td = sa.isTemplateDeclaration();
+ if (td)
+ {
+ TemplateInstance ti = sa.toParent().isTemplateInstance();
+ if (ti && ti.enclosing)
+ sa = ti;
+ }
+ TemplateInstance ti = sa.isTemplateInstance();
+ Declaration d = sa.isDeclaration();
+ if ((td && td.literal) || (ti && ti.enclosing) || (d && !d.isDataseg() && !(d.storage_class & STC.manifest) && (!d.isFuncDeclaration() || d.isFuncDeclaration().isNested()) && !isTemplateMixin()))
+ {
+ Dsymbol dparent = sa.toParent2();
+ if (!dparent)
+ goto L1;
+ else if (!enclosing)
+ enclosing = dparent;
+ else if (enclosing != dparent)
+ {
+ /* Select the more deeply nested of the two.
+ * Error if one is not nested inside the other.
+ */
+ for (Dsymbol p = enclosing; p; p = p.parent)
+ {
+ if (p == dparent)
+ goto L1; // enclosing is most nested
+ }
+ for (Dsymbol p = dparent; p; p = p.parent)
+ {
+ if (p == enclosing)
+ {
+ enclosing = dparent;
+ goto L1; // dparent is most nested
+ }
+ }
+ error("`%s` is nested in both `%s` and `%s`", toChars(), enclosing.toChars(), dparent.toChars());
+ errors = true;
+ }
+ L1:
+ //printf("\tnested inside %s\n", enclosing.toChars());
+ nested |= 1;
+ }
+ }
+ else if (va)
+ {
+ nested |= cast(int)hasNestedArgs(&va.objects, isstatic);
+ }
+ }
+ //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested);
+ return nested != 0;
+ }
+
+ /*****************************************
+ * Append 'this' to the specific module members[]
+ */
+ extern (D) final Dsymbols* appendToModuleMember()
+ {
+ Module mi = minst; // instantiated . inserted module
+
+ if (global.params.useUnitTests)
+ {
+ // Turn all non-root instances to speculative
+ if (mi && !mi.isRoot())
+ mi = null;
+ }
+
+ //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n",
+ // toPrettyChars(),
+ // enclosing ? enclosing.toPrettyChars() : null,
+ // mi ? mi.toPrettyChars() : null);
+ if (!mi || mi.isRoot())
+ {
+ /* If the instantiated module is speculative or root, insert to the
+ * member of a root module. Then:
+ * - semantic3 pass will get called on the instance members.
+ * - codegen pass will get a selection chance to do/skip it.
+ */
+ static Dsymbol getStrictEnclosing(TemplateInstance ti)
+ {
+ do
+ {
+ if (ti.enclosing)
+ return ti.enclosing;
+ ti = ti.tempdecl.isInstantiated();
+ } while (ti);
+ return null;
+ }
+
+ Dsymbol enc = getStrictEnclosing(this);
+ // insert target is made stable by using the module
+ // where tempdecl is declared.
+ mi = (enc ? enc : tempdecl).getModule();
+ if (!mi.isRoot())
+ mi = mi.importedFrom;
+ assert(mi.isRoot());
+ }
+ else
+ {
+ /* If the instantiated module is non-root, insert to the member of the
+ * non-root module. Then:
+ * - semantic3 pass won't be called on the instance.
+ * - codegen pass won't reach to the instance.
+ */
+ }
+ //printf("\t-. mi = %s\n", mi.toPrettyChars());
+
+ if (memberOf is mi) // already a member
+ {
+ debug // make sure it really is a member
+ {
+ auto a = mi.members;
+ for (size_t i = 0; 1; ++i)
+ {
+ assert(i != a.dim);
+ if (this == (*a)[i])
+ break;
+ }
+ }
+ return null;
+ }
+
+ Dsymbols* a = mi.members;
+ a.push(this);
+ memberOf = mi;
+ if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
+ Module.addDeferredSemantic2(this);
+ if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
+ Module.addDeferredSemantic3(this);
+ return a;
+ }
+
+ /****************************************************
+ * Declare parameters of template instance, initialize them with the
+ * template instance arguments.
+ */
+ extern (D) final void declareParameters(Scope* sc)
+ {
+ TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ //printf("TemplateInstance.declareParameters()\n");
+ foreach (i, o; tdtypes) // initializer for tp
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ //printf("\ttdtypes[%d] = %p\n", i, o);
+ tempdecl.declareParameter(sc, tp, o);
+ }
+ }
+
+ /****************************************
+ * This instance needs an identifier for name mangling purposes.
+ * Create one by taking the template declaration name and adding
+ * the type signature for it.
+ */
+ extern (D) final Identifier genIdent(Objects* args)
+ {
+ //printf("TemplateInstance.genIdent('%s')\n", tempdecl.ident.toChars());
+ assert(args is tiargs);
+ OutBuffer buf;
+ mangleToBuffer(this, &buf);
+ //printf("\tgenIdent = %s\n", buf.peekChars());
+ return Identifier.idPool(buf[]);
+ }
+
+ extern (D) final void expandMembers(Scope* sc2)
+ {
+ members.foreachDsymbol( (s) { s.setScope (sc2); } );
+
+ members.foreachDsymbol( (s) { s.importAll(sc2); } );
+
+ void symbolDg(Dsymbol s)
+ {
+ //printf("\t semantic on '%s' %p kind %s in '%s'\n", s.toChars(), s, s.kind(), this.toChars());
+ //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars());
+ //if (enclosing)
+ // s.parent = sc.parent;
+ //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+ s.dsymbolSemantic(sc2);
+ //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+ Module.runDeferredSemantic();
+ }
+
+ members.foreachDsymbol(&symbolDg);
+ }
+
+ extern (D) final void tryExpandMembers(Scope* sc2)
+ {
+ __gshared int nest;
+ // extracted to a function to allow windows SEH to work without destructors in the same function
+ //printf("%d\n", nest);
+ if (++nest > global.recursionLimit)
+ {
+ global.gag = 0; // ensure error message gets printed
+ error("recursive expansion exceeded allowed nesting limit");
+ fatal();
+ }
+
+ expandMembers(sc2);
+
+ nest--;
+ }
+
+ extern (D) final void trySemantic3(Scope* sc2)
+ {
+ // extracted to a function to allow windows SEH to work without destructors in the same function
+ __gshared int nest;
+ //printf("%d\n", nest);
+ if (++nest > global.recursionLimit)
+ {
+ global.gag = 0; // ensure error message gets printed
+ error("recursive expansion exceeded allowed nesting limit");
+ fatal();
+ }
+
+ semantic3(this, sc2);
+
+ --nest;
+ }
+
+ override final inout(TemplateInstance) isTemplateInstance() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/**************************************
+ * IsExpression can evaluate the specified type speculatively, and even if
+ * it instantiates any symbols, they are normally unnecessary for the
+ * final executable.
+ * However, if those symbols leak to the actual code, compiler should remark
+ * them as non-speculative to generate their code and link to the final executable.
+ */
+void unSpeculative(Scope* sc, RootObject o)
+{
+ if (!o)
+ return;
+
+ if (Tuple tup = isTuple(o))
+ {
+ foreach (obj; tup.objects)
+ {
+ unSpeculative(sc, obj);
+ }
+ return;
+ }
+
+ Dsymbol s = getDsymbol(o);
+ if (!s)
+ return;
+
+ if (Declaration d = s.isDeclaration())
+ {
+ if (VarDeclaration vd = d.isVarDeclaration())
+ o = vd.type;
+ else if (AliasDeclaration ad = d.isAliasDeclaration())
+ {
+ o = ad.getType();
+ if (!o)
+ o = ad.toAlias();
+ }
+ else
+ o = d.toAlias();
+
+ s = getDsymbol(o);
+ if (!s)
+ return;
+ }
+
+ if (TemplateInstance ti = s.isTemplateInstance())
+ {
+ // If the instance is already non-speculative,
+ // or it is leaked to the speculative scope.
+ if (ti.minst !is null || sc.minst is null)
+ return;
+
+ // Remark as non-speculative instance.
+ ti.minst = sc.minst;
+ if (!ti.tinst)
+ ti.tinst = sc.tinst;
+
+ unSpeculative(sc, ti.tempdecl);
+ }
+
+ if (TemplateInstance ti = s.isInstantiated())
+ unSpeculative(sc, ti);
+}
+
+/**********************************
+ * Return true if e could be valid only as a template value parameter.
+ * Return false if it might be an alias or tuple.
+ * (Note that even in this case, it could still turn out to be a value).
+ */
+bool definitelyValueParameter(Expression e)
+{
+ // None of these can be value parameters
+ if (e.op == TOK.tuple || e.op == TOK.scope_ ||
+ e.op == TOK.type || e.op == TOK.dotType ||
+ e.op == TOK.template_ || e.op == TOK.dotTemplateDeclaration ||
+ e.op == TOK.function_ || e.op == TOK.error ||
+ e.op == TOK.this_ || e.op == TOK.super_ ||
+ e.op == TOK.dot)
+ return false;
+
+ if (e.op != TOK.dotVariable)
+ return true;
+
+ /* Template instantiations involving a DotVar expression are difficult.
+ * In most cases, they should be treated as a value parameter, and interpreted.
+ * But they might also just be a fully qualified name, which should be treated
+ * as an alias.
+ */
+
+ // x.y.f cannot be a value
+ FuncDeclaration f = (cast(DotVarExp)e).var.isFuncDeclaration();
+ if (f)
+ return false;
+
+ while (e.op == TOK.dotVariable)
+ {
+ e = (cast(DotVarExp)e).e1;
+ }
+ // this.x.y and super.x.y couldn't possibly be valid values.
+ if (e.op == TOK.this_ || e.op == TOK.super_)
+ return false;
+
+ // e.type.x could be an alias
+ if (e.op == TOK.dotType)
+ return false;
+
+ // var.x.y is the only other possible form of alias
+ if (e.op != TOK.variable)
+ return true;
+
+ VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
+ // func.x.y is not an alias
+ if (!v)
+ return true;
+
+ // https://issues.dlang.org/show_bug.cgi?id=16685
+ // var.x.y where var is a constant available at compile time
+ if (v.storage_class & STC.manifest)
+ return true;
+
+ // TODO: Should we force CTFE if it is a global constant?
+ return false;
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template-mixin.html
+ * Syntax:
+ * mixin MixinTemplateName [TemplateArguments] [Identifier];
+ */
+extern (C++) final class TemplateMixin : TemplateInstance
+{
+ TypeQualified tqual;
+
+ extern (D) this(const ref Loc loc, Identifier ident, TypeQualified tqual, Objects* tiargs)
+ {
+ super(loc,
+ tqual.idents.dim ? cast(Identifier)tqual.idents[tqual.idents.dim - 1] : (cast(TypeIdentifier)tqual).ident,
+ tiargs ? tiargs : new Objects());
+ //printf("TemplateMixin(ident = '%s')\n", ident ? ident.toChars() : "");
+ this.ident = ident;
+ this.tqual = tqual;
+ }
+
+ override TemplateInstance syntaxCopy(Dsymbol s)
+ {
+ auto tm = new TemplateMixin(loc, ident, tqual.syntaxCopy(), tiargs);
+ return TemplateInstance.syntaxCopy(tm);
+ }
+
+ override const(char)* kind() const
+ {
+ return "mixin";
+ }
+
+ override bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ return Dsymbol.oneMember(ps, ident);
+ }
+
+ override bool hasPointers()
+ {
+ //printf("TemplateMixin.hasPointers() %s\n", toChars());
+ return members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
+ }
+
+ override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
+ {
+ //printf("TemplateMixin.setFieldOffset() %s\n", toChars());
+ if (_scope) // if fwd reference
+ dsymbolSemantic(this, null); // try to resolve it
+
+ members.foreachDsymbol( (s) { s.setFieldOffset(ad, fieldState, isunion); } );
+ }
+
+ override const(char)* toChars() const
+ {
+ OutBuffer buf;
+ toCBufferInstance(this, &buf);
+ return buf.extractChars();
+ }
+
+ extern (D) bool findTempDecl(Scope* sc)
+ {
+ // Follow qualifications to find the TemplateDeclaration
+ if (!tempdecl)
+ {
+ Expression e;
+ Type t;
+ Dsymbol s;
+ tqual.resolve(loc, sc, e, t, s);
+ if (!s)
+ {
+ error("is not defined");
+ return false;
+ }
+ s = s.toAlias();
+ tempdecl = s.isTemplateDeclaration();
+ OverloadSet os = s.isOverloadSet();
+
+ /* If an OverloadSet, look for a unique member that is a template declaration
+ */
+ if (os)
+ {
+ Dsymbol ds = null;
+ foreach (i, sym; os.a)
+ {
+ Dsymbol s2 = sym.isTemplateDeclaration();
+ if (s2)
+ {
+ if (ds)
+ {
+ tempdecl = os;
+ break;
+ }
+ ds = s2;
+ }
+ }
+ }
+ if (!tempdecl)
+ {
+ error("`%s` isn't a template", s.toChars());
+ return false;
+ }
+ }
+ assert(tempdecl);
+
+ // Look for forward references
+ auto tovers = tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+
+ if (td.semanticRun == PASS.init)
+ {
+ if (td._scope)
+ td.dsymbolSemantic(td._scope);
+ else
+ {
+ semanticRun = PASS.init;
+ return 1;
+ }
+ }
+ return 0;
+ });
+ if (r)
+ return false;
+ }
+ return true;
+ }
+
+ override inout(TemplateMixin) isTemplateMixin() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/************************************
+ * This struct is needed for TemplateInstance to be the key in an associative array.
+ * Fixing https://issues.dlang.org/show_bug.cgi?id=15812 and
+ * https://issues.dlang.org/show_bug.cgi?id=15813 would make it unnecessary.
+ */
+struct TemplateInstanceBox
+{
+ TemplateInstance ti;
+
+ this(TemplateInstance ti)
+ {
+ this.ti = ti;
+ this.ti.toHash();
+ assert(this.ti.hash);
+ }
+
+ size_t toHash() const @trusted pure nothrow
+ {
+ assert(ti.hash);
+ return ti.hash;
+ }
+
+ bool opEquals(ref const TemplateInstanceBox s) @trusted const
+ {
+ bool res = void;
+ if (ti.inst && s.ti.inst)
+ /* This clause is only used when an instance with errors
+ * is replaced with a correct instance.
+ */
+ res = ti is s.ti;
+ else
+ /* Used when a proposed instance is used to see if there's
+ * an existing instance.
+ */
+ res = (cast()s.ti).equalsx(cast()ti);
+
+ debug (FindExistingInstance) ++(res ? nHits : nCollisions);
+ return res;
+ }
+
+ debug (FindExistingInstance)
+ {
+ __gshared uint nHits, nCollisions;
+
+ shared static ~this()
+ {
+ printf("debug (FindExistingInstance) TemplateInstanceBox.equals hits: %u collisions: %u\n",
+ nHits, nCollisions);
+ }
+ }
+}
+
+/*******************************************
+ * Match to a particular TemplateParameter.
+ * Input:
+ * instLoc location that the template is instantiated.
+ * tiargs[] actual arguments to template instance
+ * i i'th argument
+ * parameters[] template parameters
+ * dedtypes[] deduced arguments to template instance
+ * *psparam set to symbol declared and initialized to dedtypes[i]
+ */
+MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, size_t i, TemplateParameters* parameters, Objects* dedtypes, Declaration* psparam)
+{
+ MATCH matchArgNoMatch()
+ {
+ if (psparam)
+ *psparam = null;
+ return MATCH.nomatch;
+ }
+
+ MATCH matchArgParameter()
+ {
+ RootObject oarg;
+
+ if (i < tiargs.dim)
+ oarg = (*tiargs)[i];
+ else
+ {
+ // Get default argument instead
+ oarg = tp.defaultArg(instLoc, sc);
+ if (!oarg)
+ {
+ assert(i < dedtypes.dim);
+ // It might have already been deduced
+ oarg = (*dedtypes)[i];
+ if (!oarg)
+ return matchArgNoMatch();
+ }
+ }
+ return tp.matchArg(sc, oarg, i, parameters, dedtypes, psparam);
+ }
+
+ MATCH matchArgTuple(TemplateTupleParameter ttp)
+ {
+ /* The rest of the actual arguments (tiargs[]) form the match
+ * for the variadic parameter.
+ */
+ assert(i + 1 == dedtypes.dim); // must be the last one
+ Tuple ovar;
+
+ if (Tuple u = isTuple((*dedtypes)[i]))
+ {
+ // It has already been deduced
+ ovar = u;
+ }
+ else if (i + 1 == tiargs.dim && isTuple((*tiargs)[i]))
+ ovar = isTuple((*tiargs)[i]);
+ else
+ {
+ ovar = new Tuple();
+ //printf("ovar = %p\n", ovar);
+ if (i < tiargs.dim)
+ {
+ //printf("i = %d, tiargs.dim = %d\n", i, tiargs.dim);
+ ovar.objects.setDim(tiargs.dim - i);
+ foreach (j, ref obj; ovar.objects)
+ obj = (*tiargs)[i + j];
+ }
+ }
+ return ttp.matchArg(sc, ovar, i, parameters, dedtypes, psparam);
+ }
+
+ if (auto ttp = tp.isTemplateTupleParameter())
+ return matchArgTuple(ttp);
+ else
+ return matchArgParameter();
+}
+
+MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, Objects* dedtypes, Declaration* psparam)
+{
+ MATCH matchArgNoMatch()
+ {
+ //printf("\tm = %d\n", MATCH.nomatch);
+ if (psparam)
+ *psparam = null;
+ return MATCH.nomatch;
+ }
+
+ MATCH matchArgType(TemplateTypeParameter ttp)
+ {
+ //printf("TemplateTypeParameter.matchArg('%s')\n", ttp.ident.toChars());
+ MATCH m = MATCH.exact;
+ Type ta = isType(oarg);
+ if (!ta)
+ {
+ //printf("%s %p %p %p\n", oarg.toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg));
+ return matchArgNoMatch();
+ }
+ //printf("ta is %s\n", ta.toChars());
+
+ if (ttp.specType)
+ {
+ if (!ta || ta == TemplateTypeParameter.tdummy)
+ return matchArgNoMatch();
+
+ //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta.toChars(), ttp.specType.toChars());
+ MATCH m2 = deduceType(ta, sc, ttp.specType, parameters, dedtypes);
+ if (m2 == MATCH.nomatch)
+ {
+ //printf("\tfailed deduceType\n");
+ return matchArgNoMatch();
+ }
+
+ if (m2 < m)
+ m = m2;
+ if ((*dedtypes)[i])
+ {
+ Type t = cast(Type)(*dedtypes)[i];
+
+ if (ttp.dependent && !t.equals(ta)) // https://issues.dlang.org/show_bug.cgi?id=14357
+ return matchArgNoMatch();
+
+ /* This is a self-dependent parameter. For example:
+ * template X(T : T*) {}
+ * template X(T : S!T, alias S) {}
+ */
+ //printf("t = %s ta = %s\n", t.toChars(), ta.toChars());
+ ta = t;
+ }
+ }
+ else
+ {
+ if ((*dedtypes)[i])
+ {
+ // Must match already deduced type
+ Type t = cast(Type)(*dedtypes)[i];
+
+ if (!t.equals(ta))
+ {
+ //printf("t = %s ta = %s\n", t.toChars(), ta.toChars());
+ return matchArgNoMatch();
+ }
+ }
+ else
+ {
+ // So that matches with specializations are better
+ m = MATCH.convert;
+ }
+ }
+ (*dedtypes)[i] = ta;
+
+ if (psparam)
+ *psparam = new AliasDeclaration(ttp.loc, ttp.ident, ta);
+ //printf("\tm = %d\n", m);
+ return ttp.dependent ? MATCH.exact : m;
+ }
+
+ MATCH matchArgValue(TemplateValueParameter tvp)
+ {
+ //printf("TemplateValueParameter.matchArg('%s')\n", tvp.ident.toChars());
+ MATCH m = MATCH.exact;
+
+ Expression ei = isExpression(oarg);
+ Type vt;
+
+ if (!ei && oarg)
+ {
+ Dsymbol si = isDsymbol(oarg);
+ FuncDeclaration f = si ? si.isFuncDeclaration() : null;
+ if (!f || !f.fbody || f.needThis())
+ return matchArgNoMatch();
+
+ ei = new VarExp(tvp.loc, f);
+ ei = ei.expressionSemantic(sc);
+
+ /* If a function is really property-like, and then
+ * it's CTFEable, ei will be a literal expression.
+ */
+ uint olderrors = global.startGagging();
+ ei = resolveProperties(sc, ei);
+ ei = ei.ctfeInterpret();
+ if (global.endGagging(olderrors) || ei.op == TOK.error)
+ return matchArgNoMatch();
+
+ /* https://issues.dlang.org/show_bug.cgi?id=14520
+ * A property-like function can match to both
+ * TemplateAlias and ValueParameter. But for template overloads,
+ * it should always prefer alias parameter to be consistent
+ * template match result.
+ *
+ * template X(alias f) { enum X = 1; }
+ * template X(int val) { enum X = 2; }
+ * int f1() { return 0; } // CTFEable
+ * int f2(); // body-less function is not CTFEable
+ * enum x1 = X!f1; // should be 1
+ * enum x2 = X!f2; // should be 1
+ *
+ * e.g. The x1 value must be same even if the f1 definition will be moved
+ * into di while stripping body code.
+ */
+ m = MATCH.convert;
+ }
+
+ if (ei && ei.op == TOK.variable)
+ {
+ // Resolve const variables that we had skipped earlier
+ ei = ei.ctfeInterpret();
+ }
+
+ //printf("\tvalType: %s, ty = %d\n", tvp.valType.toChars(), tvp.valType.ty);
+ vt = tvp.valType.typeSemantic(tvp.loc, sc);
+ //printf("ei: %s, ei.type: %s\n", ei.toChars(), ei.type.toChars());
+ //printf("vt = %s\n", vt.toChars());
+
+ if (ei.type)
+ {
+ MATCH m2 = ei.implicitConvTo(vt);
+ //printf("m: %d\n", m);
+ if (m2 < m)
+ m = m2;
+ if (m == MATCH.nomatch)
+ return matchArgNoMatch();
+ ei = ei.implicitCastTo(sc, vt);
+ ei = ei.ctfeInterpret();
+ }
+
+ if (tvp.specValue)
+ {
+ if (ei is null || (cast(void*)ei.type in TemplateValueParameter.edummies &&
+ TemplateValueParameter.edummies[cast(void*)ei.type] == ei))
+ return matchArgNoMatch();
+
+ Expression e = tvp.specValue;
+
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = e.implicitCastTo(sc, vt);
+ e = e.ctfeInterpret();
+
+ ei = ei.syntaxCopy();
+ sc = sc.startCTFE();
+ ei = ei.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ ei = ei.implicitCastTo(sc, vt);
+ ei = ei.ctfeInterpret();
+ //printf("\tei: %s, %s\n", ei.toChars(), ei.type.toChars());
+ //printf("\te : %s, %s\n", e.toChars(), e.type.toChars());
+ if (!ei.equals(e))
+ return matchArgNoMatch();
+ }
+ else
+ {
+ if ((*dedtypes)[i])
+ {
+ // Must match already deduced value
+ Expression e = cast(Expression)(*dedtypes)[i];
+ if (!ei || !ei.equals(e))
+ return matchArgNoMatch();
+ }
+ }
+ (*dedtypes)[i] = ei;
+
+ if (psparam)
+ {
+ Initializer _init = new ExpInitializer(tvp.loc, ei);
+ Declaration sparam = new VarDeclaration(tvp.loc, vt, tvp.ident, _init);
+ sparam.storage_class = STC.manifest;
+ *psparam = sparam;
+ }
+ return tvp.dependent ? MATCH.exact : m;
+ }
+
+ MATCH matchArgAlias(TemplateAliasParameter tap)
+ {
+ //printf("TemplateAliasParameter.matchArg('%s')\n", tap.ident.toChars());
+ MATCH m = MATCH.exact;
+ Type ta = isType(oarg);
+ RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg);
+ Expression ea = isExpression(oarg);
+ if (ea && (ea.op == TOK.this_ || ea.op == TOK.super_))
+ sa = (cast(ThisExp)ea).var;
+ else if (ea && ea.op == TOK.scope_)
+ sa = (cast(ScopeExp)ea).sds;
+ if (sa)
+ {
+ if ((cast(Dsymbol)sa).isAggregateDeclaration())
+ m = MATCH.convert;
+
+ /* specType means the alias must be a declaration with a type
+ * that matches specType.
+ */
+ if (tap.specType)
+ {
+ Declaration d = (cast(Dsymbol)sa).isDeclaration();
+ if (!d)
+ return matchArgNoMatch();
+ if (!d.type.equals(tap.specType))
+ return matchArgNoMatch();
+ }
+ }
+ else
+ {
+ sa = oarg;
+ if (ea)
+ {
+ if (tap.specType)
+ {
+ if (!ea.type.equals(tap.specType))
+ return matchArgNoMatch();
+ }
+ }
+ else if (ta && ta.ty == Tinstance && !tap.specAlias)
+ {
+ /* Specialized parameter should be preferred
+ * match to the template type parameter.
+ * template X(alias a) {} // a == this
+ * template X(alias a : B!A, alias B, A...) {} // B!A => ta
+ */
+ }
+ else if (sa && sa == TemplateTypeParameter.tdummy)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=2025
+ * Aggregate Types should preferentially
+ * match to the template type parameter.
+ * template X(alias a) {} // a == this
+ * template X(T) {} // T => sa
+ */
+ }
+ else if (ta && ta.ty != Tident)
+ {
+ /* Match any type that's not a TypeIdentifier to alias parameters,
+ * but prefer type parameter.
+ * template X(alias a) { } // a == ta
+ *
+ * TypeIdentifiers are excluded because they might be not yet resolved aliases.
+ */
+ m = MATCH.convert;
+ }
+ else
+ return matchArgNoMatch();
+ }
+
+ if (tap.specAlias)
+ {
+ if (sa == TemplateAliasParameter.sdummy)
+ return matchArgNoMatch();
+ // check specialization if template arg is a symbol
+ Dsymbol sx = isDsymbol(sa);
+ if (sa != tap.specAlias && sx)
+ {
+ Type talias = isType(tap.specAlias);
+ if (!talias)
+ return matchArgNoMatch();
+
+ TemplateInstance ti = sx.isTemplateInstance();
+ if (!ti && sx.parent)
+ {
+ ti = sx.parent.isTemplateInstance();
+ if (ti && ti.name != sx.ident)
+ return matchArgNoMatch();
+ }
+ if (!ti)
+ return matchArgNoMatch();
+
+ Type t = new TypeInstance(Loc.initial, ti);
+ MATCH m2 = deduceType(t, sc, talias, parameters, dedtypes);
+ if (m2 == MATCH.nomatch)
+ return matchArgNoMatch();
+ }
+ // check specialization if template arg is a type
+ else if (ta)
+ {
+ if (Type tspec = isType(tap.specAlias))
+ {
+ MATCH m2 = ta.implicitConvTo(tspec);
+ if (m2 == MATCH.nomatch)
+ return matchArgNoMatch();
+ }
+ else
+ {
+ error(tap.loc, "template parameter specialization for a type must be a type and not `%s`",
+ tap.specAlias.toChars());
+ return matchArgNoMatch();
+ }
+ }
+ }
+ else if ((*dedtypes)[i])
+ {
+ // Must match already deduced symbol
+ RootObject si = (*dedtypes)[i];
+ if (!sa || si != sa)
+ return matchArgNoMatch();
+ }
+ (*dedtypes)[i] = sa;
+
+ if (psparam)
+ {
+ if (Dsymbol s = isDsymbol(sa))
+ {
+ *psparam = new AliasDeclaration(tap.loc, tap.ident, s);
+ }
+ else if (Type t = isType(sa))
+ {
+ *psparam = new AliasDeclaration(tap.loc, tap.ident, t);
+ }
+ else
+ {
+ assert(ea);
+
+ // Declare manifest constant
+ Initializer _init = new ExpInitializer(tap.loc, ea);
+ auto v = new VarDeclaration(tap.loc, null, tap.ident, _init);
+ v.storage_class = STC.manifest;
+ v.dsymbolSemantic(sc);
+ *psparam = v;
+ }
+ }
+ return tap.dependent ? MATCH.exact : m;
+ }
+
+ MATCH matchArgTuple(TemplateTupleParameter ttp)
+ {
+ //printf("TemplateTupleParameter.matchArg('%s')\n", ttp.ident.toChars());
+ Tuple ovar = isTuple(oarg);
+ if (!ovar)
+ return MATCH.nomatch;
+ if ((*dedtypes)[i])
+ {
+ Tuple tup = isTuple((*dedtypes)[i]);
+ if (!tup)
+ return MATCH.nomatch;
+ if (!match(tup, ovar))
+ return MATCH.nomatch;
+ }
+ (*dedtypes)[i] = ovar;
+
+ if (psparam)
+ *psparam = new TupleDeclaration(ttp.loc, ttp.ident, &ovar.objects);
+ return ttp.dependent ? MATCH.exact : MATCH.convert;
+ }
+
+ if (auto ttp = tp.isTemplateTypeParameter())
+ return matchArgType(ttp);
+ else if (auto tvp = tp.isTemplateValueParameter())
+ return matchArgValue(tvp);
+ else if (auto tap = tp.isTemplateAliasParameter())
+ return matchArgAlias(tap);
+ else if (auto ttp = tp.isTemplateTupleParameter())
+ return matchArgTuple(ttp);
+ else
+ assert(0);
+}
+
+
+/***********************************************
+ * Collect and print statistics on template instantiations.
+ */
+struct TemplateStats
+{
+ __gshared TemplateStats[const void*] stats;
+
+ uint numInstantiations; // number of instantiations of the template
+ uint uniqueInstantiations; // number of unique instantiations of the template
+
+ TemplateInstances* allInstances;
+
+ /*******************************
+ * Add this instance
+ */
+ static void incInstance(const TemplateDeclaration td,
+ const TemplateInstance ti)
+ {
+ void log(ref TemplateStats ts)
+ {
+ if (ts.allInstances is null)
+ ts.allInstances = new TemplateInstances();
+ if (global.params.vtemplatesListInstances)
+ ts.allInstances.push(cast() ti);
+ }
+
+ // message(ti.loc, "incInstance %p %p", td, ti);
+ if (!global.params.vtemplates)
+ return;
+ if (!td)
+ return;
+ assert(ti);
+ if (auto ts = cast(const void*) td in stats)
+ {
+ log(*ts);
+ ++ts.numInstantiations;
+ }
+ else
+ {
+ stats[cast(const void*) td] = TemplateStats(1, 0);
+ log(stats[cast(const void*) td]);
+ }
+ }
+
+ /*******************************
+ * Add this unique instance
+ */
+ static void incUnique(const TemplateDeclaration td,
+ const TemplateInstance ti)
+ {
+ // message(ti.loc, "incUnique %p %p", td, ti);
+ if (!global.params.vtemplates)
+ return;
+ if (!td)
+ return;
+ assert(ti);
+ if (auto ts = cast(const void*) td in stats)
+ ++ts.uniqueInstantiations;
+ else
+ stats[cast(const void*) td] = TemplateStats(0, 1);
+ }
+}
+
+void printTemplateStats()
+{
+ static struct TemplateDeclarationStats
+ {
+ TemplateDeclaration td;
+ TemplateStats ts;
+ static int compare(scope const TemplateDeclarationStats* a,
+ scope const TemplateDeclarationStats* b) @safe nothrow @nogc pure
+ {
+ auto diff = b.ts.uniqueInstantiations - a.ts.uniqueInstantiations;
+ if (diff)
+ return diff;
+ else
+ return b.ts.numInstantiations - a.ts.numInstantiations;
+ }
+ }
+
+ if (!global.params.vtemplates)
+ return;
+
+ Array!(TemplateDeclarationStats) sortedStats;
+ sortedStats.reserve(TemplateStats.stats.length);
+ foreach (td_, ref ts; TemplateStats.stats)
+ {
+ sortedStats.push(TemplateDeclarationStats(cast(TemplateDeclaration) td_, ts));
+ }
+
+ sortedStats.sort!(TemplateDeclarationStats.compare);
+
+ foreach (const ref ss; sortedStats[])
+ {
+ if (global.params.vtemplatesListInstances &&
+ ss.ts.allInstances)
+ {
+ message(ss.td.loc,
+ "vtemplate: %u (%u distinct) instantiation(s) of template `%s` found, they are:",
+ ss.ts.numInstantiations,
+ ss.ts.uniqueInstantiations,
+ ss.td.toCharsNoConstraints());
+ foreach (const ti; (*ss.ts.allInstances)[])
+ {
+ if (ti.tinst) // if has enclosing instance
+ message(ti.loc, "vtemplate: implicit instance `%s`", ti.toChars());
+ else
+ message(ti.loc, "vtemplate: explicit instance `%s`", ti.toChars());
+ }
+ }
+ else
+ {
+ message(ss.td.loc,
+ "vtemplate: %u (%u distinct) instantiation(s) of template `%s` found",
+ ss.ts.numInstantiations,
+ ss.ts.uniqueInstantiations,
+ ss.td.toCharsNoConstraints());
+ }
+ }
+}
+
+/// Pair of MATCHes
+private struct MATCHpair
+{
+ MATCH mta; /// match template parameters by initial template arguments
+ MATCH mfa; /// match template parameters by inferred template arguments
+
+ debug this(MATCH mta, MATCH mfa)
+ {
+ assert(MATCH.min <= mta && mta <= MATCH.max);
+ assert(MATCH.min <= mfa && mfa <= MATCH.max);
+ this.mta = mta;
+ this.mfa = mfa;
+ }
+}
diff --git a/gcc/d/dmd/dtoh.d b/gcc/d/dmd/dtoh.d
new file mode 100644
index 00000000000..28054d02904
--- /dev/null
+++ b/gcc/d/dmd/dtoh.d
@@ -0,0 +1,3225 @@
+/**
+ * This module contains the implementation of the C++ header generation available through
+ * the command line switch -Hc.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dtohd, _dtoh.d)
+ * Documentation: https://dlang.org/phobos/dmd_dtoh.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dtoh.d
+ */
+module dmd.dtoh;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import core.stdc.ctype;
+
+import dmd.astcodegen;
+import dmd.arraytypes;
+import dmd.dsymbol;
+import dmd.errors;
+import dmd.globals;
+import dmd.identifier;
+import dmd.root.filename;
+import dmd.visitor;
+import dmd.tokens;
+
+import dmd.root.outbuffer;
+import dmd.utils;
+
+//debug = Debug_DtoH;
+
+// Generate asserts to validate the header
+//debug = Debug_DtoH_Checks;
+
+/**
+ * Generates a C++ header containing bindings for all `extern(C[++])` declarations
+ * found in the supplied modules.
+ *
+ * Params:
+ * ms = the modules
+ *
+ * Notes:
+ * - the header is written to `<global.params.cxxhdrdir>/<global.params.cxxhdrfile>`
+ * or `stdout` if no explicit file was specified
+ * - bindings conform to the C++ standard defined in `global.params.cplusplus`
+ * - ignored declarations are mentioned in a comment if `global.params.doCxxHdrGeneration`
+ * is set to `CxxHeaderMode.verbose`
+ */
+extern(C++) void genCppHdrFiles(ref Modules ms)
+{
+ initialize();
+
+ OutBuffer fwd;
+ OutBuffer done;
+ OutBuffer decl;
+
+ // enable indent by spaces on buffers
+ fwd.doindent = true;
+ fwd.spaces = true;
+ decl.doindent = true;
+ decl.spaces = true;
+
+ scope v = new ToCppBuffer(&fwd, &done, &decl);
+
+ // Conditionally include another buffer for sanity checks
+ debug (Debug_DtoH_Checks)
+ {
+ OutBuffer check;
+ check.doindent = true;
+ check.spaces = true;
+ v.checkbuf = &check;
+ }
+
+ OutBuffer buf;
+ buf.doindent = true;
+ buf.spaces = true;
+
+ foreach (m; ms)
+ m.accept(v);
+
+ if (global.params.doCxxHdrGeneration == CxxHeaderMode.verbose)
+ buf.printf("// Automatically generated by %s Compiler v%d", global.vendor.ptr, global.versionNumber());
+ else
+ buf.printf("// Automatically generated by %s Compiler", global.vendor.ptr);
+
+ buf.writenl();
+ buf.writenl();
+ buf.writestringln("#pragma once");
+ buf.writenl();
+ hashInclude(buf, "<assert.h>");
+ hashInclude(buf, "<stddef.h>");
+ hashInclude(buf, "<stdint.h>");
+ hashInclude(buf, "<math.h>");
+// buf.writestring(buf, "#include <stdio.h>\n");
+// buf.writestring("#include <string.h>\n");
+
+ // Emit array compatibility because extern(C++) types may have slices
+ // as members (as opposed to function parameters)
+ buf.writestring(`
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+`);
+
+ if (v.hasReal)
+ {
+ hashIf(buf, "!defined(_d_real)");
+ {
+ hashDefine(buf, "_d_real long double");
+ }
+ hashEndIf(buf);
+ }
+ buf.writenl();
+ // buf.writestringln("// fwd:");
+ buf.write(&fwd);
+ if (fwd.length > 0)
+ buf.writenl();
+
+ // buf.writestringln("// done:");
+ buf.write(&done);
+
+ // buf.writestringln("// decl:");
+ buf.write(&decl);
+
+ debug (Debug_DtoH_Checks)
+ {
+ // buf.writestringln("// check:");
+ buf.writestring(`
+#if OFFSETS
+ template <class T>
+ size_t getSlotNumber(int dummy, ...)
+ {
+ T c;
+ va_list ap;
+ va_start(ap, dummy);
+
+ void *f = va_arg(ap, void*);
+ for (size_t i = 0; ; i++)
+ {
+ if ( (*(void***)&c)[i] == f)
+ return i;
+ }
+ va_end(ap);
+ }
+
+ void testOffsets()
+ {
+`);
+ buf.write(&check);
+ buf.writestring(`
+ }
+#endif
+`);
+ }
+
+ if (global.params.cxxhdrname is null)
+ {
+ // Write to stdout; assume it succeeds
+ size_t n = fwrite(buf[].ptr, 1, buf.length, stdout);
+ assert(n == buf.length); // keep gcc happy about return values
+ }
+ else
+ {
+ const(char)[] name = FileName.combine(global.params.cxxhdrdir, global.params.cxxhdrname);
+ writeFile(Loc.initial, name, buf[]);
+ }
+}
+
+private:
+
+/****************************************************
+ * Visitor that writes bindings for `extern(C[++]` declarations.
+ */
+extern(C++) final class ToCppBuffer : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ enum EnumKind
+ {
+ Int,
+ Numeric,
+ String,
+ Enum,
+ Other
+ }
+
+ /// Namespace providing the actual AST nodes
+ alias AST = ASTCodegen;
+
+ /// Visited nodes
+ bool[void*] visited;
+
+ /// Forward declared nodes (which might not be emitted yet)
+ bool[void*] forwarded;
+
+ /// Buffer for forward declarations
+ OutBuffer* fwdbuf;
+
+ /// Buffer for integrity checks
+ debug (Debug_DtoH_Checks) OutBuffer* checkbuf;
+
+ /// Buffer for declarations that must emitted before the currently
+ /// visited node but can't be forward declared (see `includeSymbol`)
+ OutBuffer* donebuf;
+
+ /// Default buffer for the currently visited declaration
+ OutBuffer* buf;
+
+ /// The generated header uses `real` emitted as `_d_real`?
+ bool hasReal;
+
+ /// The generated header should contain comments for skipped declarations?
+ const bool printIgnored;
+
+ /// State specific to the current context which depends
+ /// on the currently visited node and it's parents
+ static struct Context
+ {
+ /// Default linkage in the current scope (e.g. LINK.c inside `extern(C) { ... }`)
+ LINK linkage = LINK.d;
+
+ /// Enclosing class / struct / union
+ AST.AggregateDeclaration adparent;
+
+ /// Enclosing template declaration
+ AST.TemplateDeclaration tdparent;
+
+ /// Identifier of the currently visited `VarDeclaration`
+ /// (required to write variables of funtion pointers)
+ Identifier ident;
+
+ /// Original type of the currently visited declaration
+ AST.Type* origType;
+
+ /// Last written visibility level applying to the current scope
+ AST.Visibility.Kind currentVisibility;
+
+ /// Currently applicable storage classes
+ AST.STC storageClass;
+
+ /// How many symbols were ignored
+ int ignoredCounter;
+
+ /// Currently visited types are required by another declaration
+ /// and hence must be emitted
+ bool mustEmit;
+
+ /// Processing a type that can be forward referenced
+ bool forwarding;
+
+ /// Inside of an anonymous struct/union (AnonDeclaration)
+ bool inAnonymousDecl;
+ }
+
+ /// Informations about the current context in the AST
+ Context context;
+ alias context this;
+
+ this(OutBuffer* fwdbuf, OutBuffer* donebuf, OutBuffer* buf)
+ {
+ this.fwdbuf = fwdbuf;
+ this.donebuf = donebuf;
+ this.buf = buf;
+ this.printIgnored = global.params.doCxxHdrGeneration == CxxHeaderMode.verbose;
+ }
+
+ /**
+ * Emits `dsym` into `donebuf` s.t. it is declared before the currently
+ * visited symbol that written to `buf`.
+ *
+ * Temporarily clears `context` to behave as if it was visited normally.
+ */
+ private void includeSymbol(AST.Dsymbol dsym)
+ {
+ debug (Debug_DtoH)
+ {
+ printf("[includeSymbol(AST.Dsymbol) enter] %s\n", dsym.toChars());
+ scope(exit) printf("[includeSymbol(AST.Dsymbol) exit] %s\n", dsym.toChars());
+ }
+
+ auto ptr = cast(void*) dsym in visited;
+ if (ptr && *ptr)
+ return;
+
+ // Temporary replacement for `buf` which is appended to `donebuf`
+ OutBuffer decl;
+ decl.doindent = true;
+ decl.spaces = true;
+ scope (exit) donebuf.write(&decl);
+
+ auto ctxStash = this.context;
+ auto bufStash = this.buf;
+
+ this.context = Context.init;
+ this.buf = &decl;
+ this.mustEmit = true;
+
+ dsym.accept(this);
+
+ this.context = ctxStash;
+ this.buf = bufStash;
+ }
+
+ /// Determines what kind of enum `type` is (see `EnumKind`)
+ private EnumKind getEnumKind(AST.Type type)
+ {
+ if (type) switch (type.ty)
+ {
+ case AST.Tint32:
+ return EnumKind.Int;
+ case AST.Tbool,
+ AST.Tchar, AST.Twchar, AST.Tdchar,
+ AST.Tint8, AST.Tuns8,
+ AST.Tint16, AST.Tuns16,
+ AST.Tuns32,
+ AST.Tint64, AST.Tuns64:
+ return EnumKind.Numeric;
+ case AST.Tarray:
+ if (type.isString())
+ return EnumKind.String;
+ break;
+ case AST.Tenum:
+ return EnumKind.Enum;
+ default:
+ break;
+ }
+ return EnumKind.Other;
+ }
+
+ /// Determines the type used to represent `type` in C++.
+ /// Returns: `const [w,d]char*` for `[w,d]string` or `type`
+ private AST.Type determineEnumType(AST.Type type)
+ {
+ if (auto arr = type.isTypeDArray())
+ {
+ switch (arr.next.ty)
+ {
+ case AST.Tchar: return AST.Type.tchar.constOf.pointerTo;
+ case AST.Twchar: return AST.Type.twchar.constOf.pointerTo;
+ case AST.Tdchar: return AST.Type.tdchar.constOf.pointerTo;
+ default: break;
+ }
+ }
+ return type;
+ }
+
+ /// Writes a final `;` and insert an empty line outside of aggregates
+ private void writeDeclEnd()
+ {
+ buf.writestringln(";");
+
+ if (!adparent)
+ buf.writenl();
+ }
+
+ /// Writes the corresponding access specifier if necessary
+ private void writeProtection(const AST.Visibility.Kind kind)
+ {
+ // Don't write visibility for global declarations
+ if (!adparent || inAnonymousDecl)
+ return;
+
+ string token;
+
+ switch(kind) with(AST.Visibility.Kind)
+ {
+ case none, private_:
+ if (this.currentVisibility == AST.Visibility.Kind.private_)
+ return;
+ this.currentVisibility = AST.Visibility.Kind.private_;
+ token = "private:";
+ break;
+
+ case package_, protected_:
+ if (this.currentVisibility == AST.Visibility.Kind.protected_)
+ return;
+ this.currentVisibility = AST.Visibility.Kind.protected_;
+ token = "protected:";
+ break;
+
+ case undefined, public_, export_:
+ if (this.currentVisibility == AST.Visibility.Kind.public_)
+ return;
+ this.currentVisibility = AST.Visibility.Kind.public_;
+ token = "public:";
+ break;
+
+ default:
+ printf("Unexpected visibility: %d!\n", kind);
+ assert(0);
+ }
+
+ buf.level--;
+ buf.writestringln(token);
+ buf.level++;
+ }
+
+ /**
+ * Writes an identifier into `buf` and checks for reserved identifiers. The
+ * parameter `canFix` determines how this function handles C++ keywords:
+ *
+ * `false` => Raise a warning and print the identifier as-is
+ * `true` => Append an underscore to the identifier
+ *
+ * Params:
+ * s = the symbol denoting the identifier
+ * canFixup = whether the identifier may be changed without affecting
+ * binary compatibility
+ */
+ private void writeIdentifier(const AST.Dsymbol s, const bool canFix = false)
+ {
+ writeIdentifier(s.ident, s.loc, s.kind(), canFix);
+ }
+
+ /** Overload of `writeIdentifier` used for all AST nodes not descending from Dsymbol **/
+ private void writeIdentifier(const Identifier ident, const Loc loc, const char* kind, const bool canFix = false)
+ {
+ bool needsFix;
+
+ void warnCxxCompat(const(char)* reason)
+ {
+ if (canFix)
+ {
+ needsFix = true;
+ return;
+ }
+
+ __gshared bool warned = false;
+ warning(loc, "%s `%s` is a %s", kind, ident.toChars(), reason);
+
+ if (!warned)
+ {
+ warningSupplemental(loc, "The generated C++ header will contain " ~
+ "identifiers that are keywords in C++");
+ warned = true;
+ }
+ }
+
+ if (global.params.warnings != DiagnosticReporting.off || canFix)
+ {
+ // Warn about identifiers that are keywords in C++.
+ if (auto kc = keywordClass(ident))
+ warnCxxCompat(kc);
+ }
+ buf.writestring(ident.toString());
+ if (needsFix)
+ buf.writeByte('_');
+ }
+
+ /// Checks whether `t` is a type that can be exported to C++
+ private bool isSupportedType(AST.Type t)
+ {
+ if (!t)
+ {
+ assert(tdparent);
+ return true;
+ }
+
+ switch (t.ty)
+ {
+ // Nested types
+ case AST.Tarray:
+ case AST.Tsarray:
+ case AST.Tpointer:
+ case AST.Treference:
+ case AST.Tdelegate:
+ return isSupportedType((cast(AST.TypeNext) t).next);
+
+ // Function pointers
+ case AST.Tfunction:
+ {
+ auto tf = cast(AST.TypeFunction) t;
+ if (!isSupportedType(tf.next))
+ return false;
+ foreach (_, param; tf.parameterList)
+ {
+ if (!isSupportedType(param.type))
+ return false;
+ }
+ return true;
+ }
+
+ // Noreturn has a different mangling
+ case AST.Tnoreturn:
+
+ // _Imaginary is C only.
+ case AST.Timaginary32:
+ case AST.Timaginary64:
+ case AST.Timaginary80:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ override void visit(AST.Dsymbol s)
+ {
+ debug (Debug_DtoH)
+ {
+ mixin(traceVisit!s);
+ import dmd.asttypename;
+ printf("[AST.Dsymbol enter] %s\n", s.astTypeName().ptr);
+ }
+ }
+
+ override void visit(AST.Import i)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!i);
+
+ /// Writes `using <alias_> = <sym.ident>` into `buf`
+ const(char*) writeImport(AST.Dsymbol sym, const Identifier alias_)
+ {
+ /// `using` was introduced in C++ 11 and only works for types...
+ if (global.params.cplusplus < CppStdRevision.cpp11)
+ return "requires C++11";
+
+ if (auto ad = sym.isAliasDeclaration())
+ {
+ sym = ad.toAlias();
+ ad = sym.isAliasDeclaration();
+
+ // Might be an alias to a basic type
+ if (ad && !ad.aliassym && ad.type)
+ goto Emit;
+ }
+
+ // Restricted to types and other aliases
+ if (!sym.isScopeDsymbol() && !sym.isAggregateDeclaration())
+ return "only supports types";
+
+ // Write `using <alias_> = `<sym>`
+ Emit:
+ buf.writestring("using ");
+ writeIdentifier(alias_, i.loc, "renamed import");
+ buf.writestring(" = ");
+ // Start at module scope to avoid collisions with local symbols
+ if (this.context.adparent)
+ buf.writestring("::");
+ buf.writestring(sym.ident.toString());
+ writeDeclEnd();
+ return null;
+ }
+
+ // Only missing without semantic analysis
+ // FIXME: Templates need work due to missing parent & imported module
+ if (!i.parent)
+ {
+ assert(tdparent);
+ ignored("`%s` because it's inside of a template declaration", i.toChars());
+ return;
+ }
+
+ // Non-public imports don't create new symbols, include as needed
+ if (i.visibility.kind < AST.Visibility.Kind.public_)
+ return;
+
+ // Symbols from static imports should be emitted inline
+ if (i.isstatic)
+ return;
+
+ const isLocal = !i.parent.isModule();
+
+ // Need module for symbol lookup
+ assert(i.mod);
+
+ // Emit an alias for each public module member
+ if (isLocal && i.names.length == 0)
+ {
+ assert(i.mod.symtab);
+
+ // Sort alphabetically s.t. slight changes in semantic don't cause
+ // massive changes in the order of declarations
+ AST.Dsymbols entries;
+ entries.reserve(i.mod.symtab.length);
+
+ foreach (entry; i.mod.symtab.tab.asRange)
+ {
+ // Skip anonymous / invisible members
+ import dmd.access : symbolIsVisible;
+ if (!entry.key.isAnonymous() && symbolIsVisible(i, entry.value))
+ entries.push(entry.value);
+ }
+
+ // Seperate function because of a spurious dual-context deprecation
+ static int compare(const AST.Dsymbol* a, const AST.Dsymbol* b)
+ {
+ return strcmp(a.ident.toChars(), b.ident.toChars());
+ }
+ entries.sort!compare();
+
+ foreach (sym; entries)
+ {
+ includeSymbol(sym);
+ if (auto err = writeImport(sym, sym.ident))
+ ignored("public import for `%s` because `using` %s", sym.ident.toChars(), err);
+ }
+ return;
+ }
+
+ // Include all public imports and emit using declarations for each alias
+ foreach (const idx, name; i.names)
+ {
+ // Search the imported symbol
+ auto sym = i.mod.search(Loc.initial, name);
+ assert(sym); // Missing imports should error during semantic
+
+ includeSymbol(sym);
+
+ // Detect the assigned name for renamed import
+ auto alias_ = i.aliases[idx];
+ if (!alias_)
+ continue;
+
+ if (auto err = writeImport(sym, alias_))
+ ignored("renamed import `%s = %s` because `using` %s", alias_.toChars(), name.toChars(), err);
+ }
+ }
+
+ override void visit(AST.AttribDeclaration pd)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!pd);
+
+ Dsymbols* decl = pd.include(null);
+ if (!decl)
+ return;
+
+ foreach (s; *decl)
+ {
+ if (adparent || s.visible().kind >= AST.Visibility.Kind.public_)
+ s.accept(this);
+ }
+ }
+
+ override void visit(AST.StorageClassDeclaration scd)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!scd);
+
+ const stcStash = this.storageClass;
+ this.storageClass |= scd.stc;
+ visit(cast(AST.AttribDeclaration) scd);
+ this.storageClass = stcStash;
+ }
+
+ override void visit(AST.LinkDeclaration ld)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!ld);
+
+ auto save = linkage;
+ linkage = ld.linkage;
+ visit(cast(AST.AttribDeclaration)ld);
+ linkage = save;
+ }
+
+ override void visit(AST.CPPMangleDeclaration md)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!md);
+
+ const oldLinkage = this.linkage;
+ this.linkage = LINK.cpp;
+ visit(cast(AST.AttribDeclaration) md);
+ this.linkage = oldLinkage;
+ }
+
+ override void visit(AST.Module m)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!m);
+
+ foreach (s; *m.members)
+ {
+ if (s.visible().kind < AST.Visibility.Kind.public_)
+ continue;
+ s.accept(this);
+ }
+ }
+
+ override void visit(AST.FuncDeclaration fd)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!fd);
+
+ if (cast(void*)fd in visited)
+ return;
+ // printf("FuncDeclaration %s %s\n", fd.toPrettyChars(), fd.type.toChars());
+ visited[cast(void*)fd] = true;
+
+ // Note that tf might be null for templated (member) functions
+ auto tf = cast(AST.TypeFunction)fd.type;
+ if ((tf && tf.linkage != LINK.c && tf.linkage != LINK.cpp) || (!tf && fd.isPostBlitDeclaration()))
+ {
+ ignored("function %s because of linkage", fd.toPrettyChars());
+ return checkVirtualFunction(fd);
+ }
+ if (!adparent && !fd.fbody)
+ {
+ ignored("function %s because it is extern", fd.toPrettyChars());
+ return;
+ }
+ if (fd.visibility.kind == AST.Visibility.Kind.none || fd.visibility.kind == AST.Visibility.Kind.private_)
+ {
+ ignored("function %s because it is private", fd.toPrettyChars());
+ return;
+ }
+ if (tf && !isSupportedType(tf.next))
+ {
+ ignored("function %s because its return type cannot be mapped to C++", fd.toPrettyChars());
+ return checkVirtualFunction(fd);
+ }
+ if (tf) foreach (i, fparam; tf.parameterList)
+ {
+ if (!isSupportedType(fparam.type))
+ {
+ ignored("function %s because one of its parameters has type `%s` which cannot be mapped to C++",
+ fd.toPrettyChars(), fparam.type.toChars());
+ return checkVirtualFunction(fd);
+ }
+ }
+
+ writeProtection(fd.visibility.kind);
+
+ if (tf && tf.linkage == LINK.c)
+ buf.writestring("extern \"C\" ");
+ else if (!adparent)
+ buf.writestring("extern ");
+ if (adparent && fd.isStatic())
+ buf.writestring("static ");
+ else if (adparent && (
+ // Virtual functions in non-templated classes
+ (fd.vtblIndex != -1 && !fd.isOverride()) ||
+
+ // Virtual functions in templated classes (fd.vtblIndex still -1)
+ (tdparent && adparent.isClassDeclaration() && !(this.storageClass & AST.STC.final_ || fd.isFinal))))
+ buf.writestring("virtual ");
+
+ debug (Debug_DtoH_Checks)
+ if (adparent && !tdparent)
+ {
+ auto s = adparent.search(Loc.initial, fd.ident);
+ auto cd = adparent.isClassDeclaration();
+
+ if (!(adparent.storage_class & AST.STC.abstract_) &&
+ !(cd && cd.isAbstract()) &&
+ s is fd && !fd.overnext)
+ {
+ const cn = adparent.ident.toChars();
+ const fn = fd.ident.toChars();
+ const vi = fd.vtblIndex;
+
+ checkbuf.printf("assert(getSlotNumber <%s>(0, &%s::%s) == %d);",
+ cn, cn, fn, vi);
+ checkbuf.writenl();
+ }
+ }
+
+ if (adparent && fd.isDisabled && global.params.cplusplus < CppStdRevision.cpp11)
+ writeProtection(AST.Visibility.Kind.private_);
+ funcToBuffer(tf, fd);
+ // FIXME: How to determine if fd is const without tf?
+ if (adparent && tf && (tf.isConst() || tf.isImmutable()))
+ {
+ bool fdOverridesAreConst = true;
+ foreach (fdv; fd.foverrides)
+ {
+ auto tfv = cast(AST.TypeFunction)fdv.type;
+ if (!tfv.isConst() && !tfv.isImmutable())
+ {
+ fdOverridesAreConst = false;
+ break;
+ }
+ }
+
+ buf.writestring(fdOverridesAreConst ? " const" : " /* const */");
+ }
+ if (adparent && fd.isAbstract())
+ buf.writestring(" = 0");
+ if (adparent && fd.isDisabled && global.params.cplusplus >= CppStdRevision.cpp11)
+ buf.writestring(" = delete");
+ buf.writestringln(";");
+ if (adparent && fd.isDisabled && global.params.cplusplus < CppStdRevision.cpp11)
+ writeProtection(AST.Visibility.Kind.public_);
+
+ if (!adparent)
+ buf.writenl();
+
+ }
+
+ /// Checks whether `fd` is a virtual function and emits a dummy declaration
+ /// if required to ensure proper vtable layout
+ private void checkVirtualFunction(AST.FuncDeclaration fd)
+ {
+ // Omit redundant declarations - the slot was already
+ // reserved in the base class
+ if (fd.isVirtual() && fd.introducing)
+ {
+ // Hide placeholders because they are not ABI compatible
+ writeProtection(AST.Visibility.Kind.private_);
+
+ __gshared int counter; // Ensure unique names in all cases
+ buf.printf("virtual void __vtable_slot_%u();", counter++);
+ buf.writenl();
+ }
+ }
+
+ override void visit(AST.UnitTestDeclaration utd)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!utd);
+ }
+
+ override void visit(AST.VarDeclaration vd)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!vd);
+
+ if (!shouldEmitAndMarkVisited(vd))
+ return;
+
+ // Tuple field are expanded into multiple VarDeclarations
+ // (we'll visit them later)
+ if (vd.type && vd.type.isTypeTuple())
+ return;
+
+ if (vd.originalType && vd.type == AST.Type.tsize_t)
+ origType = &vd.originalType;
+ scope(exit) origType = null;
+
+ if (vd.alignment != STRUCTALIGN_DEFAULT)
+ {
+ buf.printf("// Ignoring var %s alignment %u", vd.toChars(), vd.alignment);
+ buf.writenl();
+ }
+
+ // Determine the variable type which might be missing inside of
+ // template declarations. Infer the type from the initializer then
+ AST.Type type = vd.type;
+ if (!type)
+ {
+ assert(tdparent);
+
+ // Just a precaution, implicit type without initializer should be rejected
+ if (!vd._init)
+ return;
+
+ if (auto ei = vd._init.isExpInitializer())
+ type = ei.exp.type;
+
+ // Can happen if the expression needs further semantic
+ if (!type)
+ {
+ ignored("%s because the type could not be determined", vd.toPrettyChars());
+ return;
+ }
+
+ // Apply const/immutable to the inferred type
+ if (vd.storage_class & (AST.STC.const_ | AST.STC.immutable_))
+ type = type.constOf();
+ }
+
+ if (vd.storage_class & AST.STC.manifest)
+ {
+ EnumKind kind = getEnumKind(type);
+
+ if (vd.visibility.kind == AST.Visibility.Kind.none || vd.visibility.kind == AST.Visibility.Kind.private_) {
+ ignored("enum `%s` because it is `%s`.", vd.toPrettyChars(), AST.visibilityToChars(vd.visibility.kind));
+ return;
+ }
+
+ writeProtection(vd.visibility.kind);
+
+ final switch (kind)
+ {
+ case EnumKind.Int, EnumKind.Numeric:
+ // 'enum : type' is only available from C++-11 onwards.
+ if (global.params.cplusplus < CppStdRevision.cpp11)
+ goto case;
+ buf.writestring("enum : ");
+ determineEnumType(type).accept(this);
+ buf.writestring(" { ");
+ writeIdentifier(vd, true);
+ buf.writestring(" = ");
+ auto ie = AST.initializerToExpression(vd._init).isIntegerExp();
+ visitInteger(ie.toInteger(), type);
+ buf.writestring(" };");
+ break;
+
+ case EnumKind.String, EnumKind.Enum:
+ buf.writestring("static ");
+ auto target = determineEnumType(type);
+ target.accept(this);
+ buf.writestring(" const ");
+ writeIdentifier(vd, true);
+ buf.writestring(" = ");
+ auto e = AST.initializerToExpression(vd._init);
+ printExpressionFor(target, e);
+ buf.writestring(";");
+ break;
+
+ case EnumKind.Other:
+ ignored("enum `%s` because type `%s` is currently not supported for enum constants.", vd.toPrettyChars(), type.toChars());
+ return;
+ }
+ buf.writenl();
+ buf.writenl();
+ return;
+ }
+
+ if (vd.storage_class & (AST.STC.static_ | AST.STC.extern_ | AST.STC.tls | AST.STC.gshared) ||
+ vd.parent && vd.parent.isModule())
+ {
+ if (vd.linkage != LINK.c && vd.linkage != LINK.cpp && !(tdparent && (this.linkage == LINK.c || this.linkage == LINK.cpp)))
+ {
+ ignored("variable %s because of linkage", vd.toPrettyChars());
+ return;
+ }
+ if (vd.storage_class & AST.STC.tls)
+ {
+ ignored("variable %s because of thread-local storage", vd.toPrettyChars());
+ return;
+ }
+ if (!isSupportedType(type))
+ {
+ ignored("variable %s because its type cannot be mapped to C++", vd.toPrettyChars());
+ return;
+ }
+ if (auto kc = keywordClass(vd.ident))
+ {
+ ignored("variable %s because its name is a %s", vd.toPrettyChars(), kc);
+ return;
+ }
+ writeProtection(vd.visibility.kind);
+ if (vd.linkage == LINK.c)
+ buf.writestring("extern \"C\" ");
+ else if (!adparent)
+ buf.writestring("extern ");
+ if (adparent)
+ buf.writestring("static ");
+ typeToBuffer(type, vd);
+ writeDeclEnd();
+ return;
+ }
+
+ if (adparent)
+ {
+ writeProtection(vd.visibility.kind);
+ typeToBuffer(type, vd, true);
+ buf.writestringln(";");
+
+ debug (Debug_DtoH_Checks)
+ {
+ checkbuf.level++;
+ const pn = adparent.ident.toChars();
+ const vn = vd.ident.toChars();
+ const vo = vd.offset;
+ checkbuf.printf("assert(offsetof(%s, %s) == %d);",
+ pn, vn, vo);
+ checkbuf.writenl();
+ checkbuf.level--;
+ }
+ return;
+ }
+
+ visit(cast(AST.Dsymbol)vd);
+ }
+
+ override void visit(AST.TypeInfoDeclaration tid)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!tid);
+ }
+
+ override void visit(AST.AliasDeclaration ad)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!ad);
+
+ if (!shouldEmitAndMarkVisited(ad))
+ return;
+
+ writeProtection(ad.visibility.kind);
+
+ if (auto t = ad.type)
+ {
+ if (t.ty == AST.Tdelegate || t.ty == AST.Tident)
+ {
+ visit(cast(AST.Dsymbol)ad);
+ return;
+ }
+
+ // for function pointers we need to original type
+ if (ad.originalType && ad.type.ty == AST.Tpointer &&
+ (cast(AST.TypePointer)t).nextOf.ty == AST.Tfunction)
+ {
+ origType = &ad.originalType;
+ }
+ scope(exit) origType = null;
+
+ buf.writestring("typedef ");
+ typeToBuffer(origType ? *origType : t, ad);
+ writeDeclEnd();
+ return;
+ }
+ if (!ad.aliassym)
+ {
+ assert(0);
+ }
+ if (auto ti = ad.aliassym.isTemplateInstance())
+ {
+ visitTi(ti);
+ return;
+ }
+ if (auto sd = ad.aliassym.isStructDeclaration())
+ {
+ buf.writestring("typedef ");
+ sd.type.accept(this);
+ buf.writestring(" ");
+ writeIdentifier(ad);
+ writeDeclEnd();
+ return;
+ }
+ else if (auto td = ad.aliassym.isTemplateDeclaration())
+ {
+ if (global.params.cplusplus < CppStdRevision.cpp11)
+ {
+ ignored("%s because `using` declarations require C++ 11", ad.toPrettyChars());
+ return;
+ }
+
+ printTemplateParams(td);
+ buf.writestring("using ");
+ writeIdentifier(ad);
+ buf.writestring(" = ");
+ writeFullName(td);
+ buf.writeByte('<');
+
+ foreach (const idx, const p; *td.parameters)
+ {
+ if (idx)
+ buf.writestring(", ");
+ writeIdentifier(p.ident, p.loc, "parameter", true);
+ }
+ buf.writestringln(">;");
+ return;
+ }
+
+ auto fd = ad.aliassym.isFuncDeclaration();
+
+ if (fd && (fd.generated || fd.isDtorDeclaration()))
+ {
+ // Ignore. It's taken care of while visiting FuncDeclaration
+ return;
+ }
+
+ // Recognize member function aliases, e.g. alias visit = Parent.visit;
+ if (adparent && fd)
+ {
+ auto pd = fd.isMember();
+ if (!pd)
+ {
+ ignored("%s because free functions cannot be aliased in C++", ad.toPrettyChars());
+ }
+ else if (global.params.cplusplus < CppStdRevision.cpp11)
+ {
+ ignored("%s because `using` declarations require C++ 11", ad.toPrettyChars());
+ }
+ else if (ad.ident != fd.ident)
+ {
+ ignored("%s because `using` cannot rename functions in aggregates", ad.toPrettyChars());
+ }
+ else if (fd.toAliasFunc().parent.isTemplateMixin())
+ {
+ // Member's of template mixins are directly emitted into the aggregate
+ }
+ else
+ {
+ buf.writestring("using ");
+
+ // Print prefix of the base class if this function originates from a superclass
+ // because alias might be resolved through multiple classes, e.g.
+ // e.g. for alias visit = typeof(super).visit in the visitors
+ if (!fd.introducing)
+ printPrefix(ad.toParent().isClassDeclaration().baseClass);
+ else
+ printPrefix(pd);
+
+ buf.writestring(fd.ident.toChars());
+ buf.writestringln(";");
+ }
+ return;
+ }
+
+ ignored("%s %s", ad.aliassym.kind(), ad.aliassym.toPrettyChars());
+ }
+
+ override void visit(AST.Nspace ns)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!ns);
+ handleNspace(ns, ns.members);
+ }
+
+ override void visit(AST.CPPNamespaceDeclaration ns)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!ns);
+ handleNspace(ns, ns.decl);
+ }
+
+ /// Writes the namespace declaration and visits all members
+ private void handleNspace(AST.Dsymbol namespace, Dsymbols* members)
+ {
+ buf.writestring("namespace ");
+ writeIdentifier(namespace);
+ buf.writenl();
+ buf.writestring("{");
+ buf.writenl();
+ buf.level++;
+ foreach(decl;(*members))
+ {
+ decl.accept(this);
+ }
+ buf.level--;
+ buf.writestring("}");
+ buf.writenl();
+ }
+
+ override void visit(AST.AnonDeclaration ad)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!ad);
+
+ const anonStash = inAnonymousDecl;
+ inAnonymousDecl = true;
+ scope (exit) inAnonymousDecl = anonStash;
+
+ buf.writestringln(ad.isunion ? "union" : "struct");
+ buf.writestringln("{");
+ buf.level++;
+ foreach (s; *ad.decl)
+ {
+ s.accept(this);
+ }
+ buf.level--;
+ buf.writestringln("};");
+ }
+
+ private bool memberField(AST.VarDeclaration vd)
+ {
+ if (!vd.type || !vd.type.deco || !vd.ident)
+ return false;
+ if (!vd.isField())
+ return false;
+ if (vd.type.ty == AST.Tfunction)
+ return false;
+ if (vd.type.ty == AST.Tsarray)
+ return false;
+ return true;
+ }
+
+ override void visit(AST.StructDeclaration sd)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!sd);
+
+ if (!shouldEmitAndMarkVisited(sd))
+ return;
+
+ const ignoredStash = this.ignoredCounter;
+ scope (exit) this.ignoredCounter = ignoredStash;
+
+ pushAlignToBuffer(sd.alignment);
+
+ writeProtection(sd.visibility.kind);
+
+ const structAsClass = sd.cppmangle == CPPMANGLE.asClass;
+ if (sd.isUnionDeclaration())
+ buf.writestring("union ");
+ else
+ buf.writestring(structAsClass ? "class " : "struct ");
+
+ writeIdentifier(sd);
+ if (!sd.members)
+ {
+ buf.writestringln(";");
+ buf.writenl();
+ return;
+ }
+
+ // D structs are always final
+ if (!sd.isUnionDeclaration())
+ buf.writestring(" final");
+
+ buf.writenl();
+ buf.writestring("{");
+
+ const protStash = this.currentVisibility;
+ this.currentVisibility = structAsClass ? AST.Visibility.Kind.private_ : AST.Visibility.Kind.public_;
+ scope (exit) this.currentVisibility = protStash;
+
+ buf.level++;
+ buf.writenl();
+ auto save = adparent;
+ adparent = sd;
+
+ foreach (m; *sd.members)
+ {
+ m.accept(this);
+ }
+ // Generate default ctor
+ if (!sd.noDefaultCtor && !sd.isUnionDeclaration())
+ {
+ writeProtection(AST.Visibility.Kind.public_);
+ buf.printf("%s()", sd.ident.toChars());
+ size_t varCount;
+ bool first = true;
+ buf.level++;
+ foreach (m; *sd.members)
+ {
+ if (auto vd = m.isVarDeclaration())
+ {
+ if (!memberField(vd))
+ continue;
+ varCount++;
+
+ if (!vd._init && !vd.type.isTypeBasic() && !vd.type.isTypePointer && !vd.type.isTypeStruct &&
+ !vd.type.isTypeClass && !vd.type.isTypeDArray && !vd.type.isTypeSArray)
+ {
+ continue;
+ }
+ if (vd._init && vd._init.isVoidInitializer())
+ continue;
+
+ if (first)
+ {
+ buf.writestringln(" :");
+ first = false;
+ }
+ else
+ {
+ buf.writestringln(",");
+ }
+ writeIdentifier(vd, true);
+ buf.writeByte('(');
+
+ if (vd._init)
+ {
+ auto e = AST.initializerToExpression(vd._init);
+ printExpressionFor(vd.type, e, true);
+ }
+ buf.printf(")");
+ }
+ }
+ buf.level--;
+ buf.writenl();
+ buf.writestringln("{");
+ buf.writestringln("}");
+ auto ctor = sd.ctor ? sd.ctor.isFuncDeclaration() : null;
+ if (varCount && (!ctor || ctor.storage_class & AST.STC.disable))
+ {
+ buf.printf("%s(", sd.ident.toChars());
+ first = true;
+ foreach (m; *sd.members)
+ {
+ if (auto vd = m.isVarDeclaration())
+ {
+ if (!memberField(vd))
+ continue;
+ if (!first)
+ buf.writestring(", ");
+ assert(vd.type);
+ assert(vd.ident);
+ typeToBuffer(vd.type, vd, true);
+ // Don't print default value for first parameter to not clash
+ // with the default ctor defined above
+ if (!first)
+ {
+ buf.writestring(" = ");
+ printExpressionFor(vd.type, findDefaultInitializer(vd));
+ }
+ first = false;
+ }
+ }
+ buf.writestring(") :");
+ buf.level++;
+ buf.writenl();
+
+ first = true;
+ foreach (m; *sd.members)
+ {
+ if (auto vd = m.isVarDeclaration())
+ {
+ if (!memberField(vd))
+ continue;
+
+ if (first)
+ first = false;
+ else
+ buf.writestringln(",");
+
+ writeIdentifier(vd, true);
+ buf.writeByte('(');
+ writeIdentifier(vd, true);
+ buf.writeByte(')');
+ }
+ }
+ buf.writenl();
+ buf.writestringln("{}");
+ buf.level--;
+ }
+ }
+
+ buf.level--;
+ adparent = save;
+ buf.writestringln("};");
+
+ popAlignToBuffer(sd.alignment);
+ buf.writenl();
+
+ // Workaround because size triggers a forward-reference error
+ // for struct templates (the size is undetermined even if the
+ // size doesn't depend on the parameters)
+ debug (Debug_DtoH_Checks)
+ if (!tdparent)
+ {
+ checkbuf.level++;
+ const sn = sd.ident.toChars();
+ const sz = sd.size(Loc.initial);
+ checkbuf.printf("assert(sizeof(%s) == %llu);", sn, sz);
+ checkbuf.writenl();
+ checkbuf.level--;
+ }
+ }
+
+ /// Starts a custom alignment section using `#pragma pack` if
+ /// `alignment` specifies a custom alignment
+ private void pushAlignToBuffer(uint alignment)
+ {
+ // DMD ensures alignment is a power of two
+ //assert(alignment > 0 && ((alignment & (alignment - 1)) == 0),
+ // "Invalid alignment size");
+
+ // When no alignment is specified, `uint.max` is the default
+ // FIXME: alignment is 0 for structs templated members
+ if (alignment == STRUCTALIGN_DEFAULT || (tdparent && alignment == 0))
+ {
+ return;
+ }
+
+ buf.printf("#pragma pack(push, %d)", alignment);
+ buf.writenl();
+ }
+
+ /// Ends a custom alignment section using `#pragma pack` if
+ /// `alignment` specifies a custom alignment
+ private void popAlignToBuffer(uint alignment)
+ {
+ if (alignment == STRUCTALIGN_DEFAULT || (tdparent && alignment == 0))
+ return;
+
+ buf.writestringln("#pragma pack(pop)");
+ }
+
+ override void visit(AST.ClassDeclaration cd)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!cd);
+
+ if (cd.baseClass && shouldEmit(cd))
+ includeSymbol(cd.baseClass);
+
+ if (!shouldEmitAndMarkVisited(cd))
+ return;
+
+ writeProtection(cd.visibility.kind);
+
+ const classAsStruct = cd.cppmangle == CPPMANGLE.asStruct;
+ buf.writestring(classAsStruct ? "struct " : "class ");
+ writeIdentifier(cd);
+
+ if (cd.storage_class & AST.STC.final_ || (tdparent && this.storageClass & AST.STC.final_))
+ buf.writestring(" final");
+
+ assert(cd.baseclasses);
+
+ foreach (i, base; *cd.baseclasses)
+ {
+ buf.writestring(i == 0 ? " : public " : ", public ");
+
+ // Base classes/interfaces might depend on template parameters,
+ // e.g. class A(T) : B!T { ... }
+ if (base.sym is null)
+ {
+ base.type.accept(this);
+ }
+ else
+ {
+ writeFullName(base.sym);
+ }
+ }
+
+ if (!cd.members)
+ {
+ buf.writestring(";");
+ buf.writenl();
+ buf.writenl();
+ return;
+ }
+
+ buf.writenl();
+ buf.writestringln("{");
+
+ const protStash = this.currentVisibility;
+ this.currentVisibility = classAsStruct ? AST.Visibility.Kind.public_ : AST.Visibility.Kind.private_;
+ scope (exit) this.currentVisibility = protStash;
+
+ auto save = adparent;
+ adparent = cd;
+ buf.level++;
+ foreach (m; *cd.members)
+ {
+ m.accept(this);
+ }
+ buf.level--;
+ adparent = save;
+
+ buf.writestringln("};");
+ buf.writenl();
+ }
+
+ override void visit(AST.EnumDeclaration ed)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!ed);
+
+ if (!shouldEmitAndMarkVisited(ed))
+ return;
+
+ if (ed.isSpecial())
+ {
+ //ignored("%s because it is a special C++ type", ed.toPrettyChars());
+ return;
+ }
+
+ // we need to know a bunch of stuff about the enum...
+ bool isAnonymous = ed.ident is null;
+ const isOpaque = !ed.members;
+ AST.Type type = ed.memtype;
+ if (!type && !isOpaque)
+ {
+ // check all keys have matching type
+ foreach (_m; *ed.members)
+ {
+ auto m = _m.isEnumMember();
+ if (!type)
+ type = m.type;
+ else if (m.type !is type)
+ {
+ type = null;
+ break;
+ }
+ }
+ }
+ EnumKind kind = getEnumKind(type);
+
+ if (isOpaque)
+ {
+ // Opaque enums were introduced in C++ 11 (workaround?)
+ if (global.params.cplusplus < CppStdRevision.cpp11)
+ {
+ ignored("%s because opaque enums require C++ 11", ed.toPrettyChars());
+ return;
+ }
+ // Opaque enum defaults to int but the type might not be set
+ else if (!type)
+ {
+ kind = EnumKind.Int;
+ }
+ // Cannot apply namespace workaround for non-integral types
+ else if (kind != EnumKind.Int && kind != EnumKind.Numeric)
+ {
+ ignored("enum %s because of its base type", ed.toPrettyChars());
+ return;
+ }
+ }
+
+ // determine if this is an enum, or just a group of manifest constants
+ bool manifestConstants = !isOpaque && (!type || (isAnonymous && kind == EnumKind.Other));
+ assert(!manifestConstants || isAnonymous);
+
+ writeProtection(ed.visibility.kind);
+
+ // write the enum header
+ if (!manifestConstants)
+ {
+ if (kind == EnumKind.Int || kind == EnumKind.Numeric)
+ {
+ buf.writestring("enum");
+ // D enums are strong enums, but there exists only a direct mapping
+ // with 'enum class' from C++-11 onwards.
+ if (global.params.cplusplus >= CppStdRevision.cpp11)
+ {
+ if (!isAnonymous)
+ {
+ buf.writestring(" class ");
+ writeIdentifier(ed);
+ }
+ if (kind == EnumKind.Numeric)
+ {
+ buf.writestring(" : ");
+ determineEnumType(type).accept(this);
+ }
+ }
+ else if (!isAnonymous)
+ {
+ buf.writeByte(' ');
+ writeIdentifier(ed);
+ }
+ }
+ else
+ {
+ buf.writestring("namespace");
+ if(!isAnonymous)
+ {
+ buf.writeByte(' ');
+ writeIdentifier(ed);
+ }
+ }
+ // Opaque enums have no members, hence skip the body
+ if (isOpaque)
+ {
+ buf.writestringln(";");
+ return;
+ }
+ else
+ {
+ buf.writenl();
+ buf.writestringln("{");
+ }
+ }
+
+ // emit constant for each member
+ if (!manifestConstants)
+ buf.level++;
+
+ foreach (_m; *ed.members)
+ {
+ auto m = _m.isEnumMember();
+ AST.Type memberType = type ? type : m.type;
+ const EnumKind memberKind = type ? kind : getEnumKind(memberType);
+
+ if (!manifestConstants && (kind == EnumKind.Int || kind == EnumKind.Numeric))
+ {
+ // C++-98 compatible enums must use the typename as a prefix to avoid
+ // collisions with other identifiers in scope. For consistency with D,
+ // the enum member `Type.member` is emitted as `Type_member` in C++-98.
+ if (!isAnonymous && global.params.cplusplus < CppStdRevision.cpp11)
+ {
+ writeIdentifier(ed);
+ buf.writeByte('_');
+ }
+ writeIdentifier(m, true);
+ buf.writestring(" = ");
+
+ auto ie = cast(AST.IntegerExp)m.value;
+ visitInteger(ie.toInteger(), memberType);
+ buf.writestring(",");
+ }
+ else if (global.params.cplusplus >= CppStdRevision.cpp11 &&
+ manifestConstants && (memberKind == EnumKind.Int || memberKind == EnumKind.Numeric))
+ {
+ buf.writestring("enum : ");
+ determineEnumType(memberType).accept(this);
+ buf.writestring(" { ");
+ writeIdentifier(m, true);
+ buf.writestring(" = ");
+
+ auto ie = cast(AST.IntegerExp)m.value;
+ visitInteger(ie.toInteger(), memberType);
+ buf.writestring(" };");
+ }
+ else
+ {
+ buf.writestring("static ");
+ auto target = determineEnumType(memberType);
+ target.accept(this);
+ buf.writestring(" const ");
+ writeIdentifier(m, true);
+ buf.writestring(" = ");
+ printExpressionFor(target, m.origValue);
+ buf.writestring(";");
+ }
+ buf.writenl();
+ }
+
+ if (!manifestConstants)
+ buf.level--;
+ // write the enum tail
+ if (!manifestConstants)
+ buf.writestring("};");
+ buf.writenl();
+ buf.writenl();
+ }
+
+ override void visit(AST.EnumMember em)
+ {
+ assert(em.ed);
+
+ // Members of anonymous members are reachable without referencing the
+ // EnumDeclaration, e.g. public import foo : someEnumMember;
+ if (em.ed.isAnonymous())
+ {
+ visit(em.ed);
+ return;
+ }
+
+ assert(false, "This node type should be handled in the EnumDeclaration");
+ }
+
+ /**
+ * Prints a member/parameter/variable declaration into `buf`.
+ *
+ * Params:
+ * t = the type (used if `this.origType` is null)
+ * s = the symbol denoting the identifier
+ * canFixup = whether the identifier may be changed without affecting
+ * binary compatibility (forwarded to `writeIdentifier`)
+ */
+ private void typeToBuffer(AST.Type t, AST.Dsymbol s, const bool canFixup = false)
+ {
+ debug (Debug_DtoH)
+ {
+ printf("[typeToBuffer(AST.Type, AST.Dsymbol) enter] %s sym %s\n", t.toChars(), s.toChars());
+ scope(exit) printf("[typeToBuffer(AST.Type, AST.Dsymbol) exit] %s sym %s\n", t.toChars(), s.toChars());
+ }
+
+ this.ident = s.ident;
+ auto type = origType ? *origType : t;
+ AST.Dsymbol customLength;
+
+ // Check for quirks that are usually resolved during semantic
+ if (tdparent)
+ {
+ // Declarations within template declarations might use TypeAArray
+ // instead of TypeSArray when the length is not an IntegerExp,
+ // e.g. int[SOME_CONSTANT]
+ if (auto taa = type.isTypeAArray())
+ {
+ // Try to resolve the symbol from the key if it's not an actual type
+ Identifier id;
+ if (auto ti = taa.index.isTypeIdentifier())
+ id = ti.ident;
+
+ if (id)
+ {
+ auto sym = findSymbol(id, adparent ? adparent : tdparent);
+ if (!sym)
+ {
+ // Couldn't resolve, assume actual AA
+ }
+ else if (AST.isType(sym))
+ {
+ // a real associative array, forward to visit
+ }
+ else if (auto vd = sym.isVarDeclaration())
+ {
+ // Actually a static array with length symbol
+ customLength = sym;
+ type = taa.next; // visit the element type, length is written below
+ }
+ else
+ {
+ printf("Resolved unexpected symbol while determining static array length: %s\n", sym.toChars());
+ fflush(stdout);
+ fatal();
+ }
+ }
+ }
+ }
+ type.accept(this);
+ if (this.ident)
+ {
+ buf.writeByte(' ');
+ writeIdentifier(s, canFixup);
+ }
+ this.ident = null;
+
+ // Size is either taken from the type or resolved above
+ auto tsa = t.isTypeSArray();
+ if (tsa || customLength)
+ {
+ buf.writeByte('[');
+ if (tsa)
+ tsa.dim.accept(this);
+ else
+ writeFullName(customLength);
+ buf.writeByte(']');
+ }
+ else if (t.isTypeNoreturn())
+ buf.writestring("[0]");
+ }
+
+ override void visit(AST.Type t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+ printf("Invalid type: %s\n", t.toPrettyChars());
+ assert(0);
+ }
+
+ override void visit(AST.TypeNoreturn t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ buf.writestring("/* noreturn */ char");
+ }
+
+ override void visit(AST.TypeIdentifier t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ // Try to resolve the referenced symbol
+ if (auto sym = findSymbol(t.ident))
+ ensureDeclared(outermostSymbol(sym));
+
+ if (t.idents.length)
+ buf.writestring("typename ");
+
+ writeIdentifier(t.ident, t.loc, "type", tdparent !is null);
+
+ foreach (arg; t.idents)
+ {
+ buf.writestring("::");
+
+ import dmd.root.rootobject;
+ // Is this even possible?
+ if (arg.dyncast != DYNCAST.identifier)
+ {
+ printf("arg.dyncast() = %d\n", arg.dyncast());
+ assert(false);
+ }
+ buf.writestring((cast(Identifier) arg).toChars());
+ }
+ }
+
+ override void visit(AST.TypeNull t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ if (global.params.cplusplus >= CppStdRevision.cpp11)
+ buf.writestring("nullptr_t");
+ else
+ buf.writestring("void*");
+
+ }
+
+ override void visit(AST.TypeTypeof t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ assert(t.exp);
+
+ if (t.exp.type)
+ {
+ t.exp.type.accept(this);
+ }
+ else if (t.exp.isThisExp())
+ {
+ // Short circuit typeof(this) => <Aggregate name>
+ assert(adparent);
+ buf.writestring(adparent.ident.toChars());
+ }
+ else
+ {
+ // Relying on C++'s typeof might produce wrong results
+ // but it's the best we've got here.
+ buf.writestring("typeof(");
+ t.exp.accept(this);
+ buf.writeByte(')');
+ }
+ }
+
+ override void visit(AST.TypeBasic t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ if (t.isConst() || t.isImmutable())
+ buf.writestring("const ");
+ string typeName;
+ switch (t.ty)
+ {
+ case AST.Tvoid: typeName = "void"; break;
+ case AST.Tbool: typeName = "bool"; break;
+ case AST.Tchar: typeName = "char"; break;
+ case AST.Twchar: typeName = "char16_t"; break;
+ case AST.Tdchar: typeName = "char32_t"; break;
+ case AST.Tint8: typeName = "int8_t"; break;
+ case AST.Tuns8: typeName = "uint8_t"; break;
+ case AST.Tint16: typeName = "int16_t"; break;
+ case AST.Tuns16: typeName = "uint16_t"; break;
+ case AST.Tint32: typeName = "int32_t"; break;
+ case AST.Tuns32: typeName = "uint32_t"; break;
+ case AST.Tint64: typeName = "int64_t"; break;
+ case AST.Tuns64: typeName = "uint64_t"; break;
+ case AST.Tfloat32: typeName = "float"; break;
+ case AST.Tfloat64: typeName = "double"; break;
+ case AST.Tfloat80:
+ typeName = "_d_real";
+ hasReal = true;
+ break;
+ case AST.Tcomplex32: typeName = "_Complex float"; break;
+ case AST.Tcomplex64: typeName = "_Complex double"; break;
+ case AST.Tcomplex80:
+ typeName = "_Complex _d_real";
+ hasReal = true;
+ break;
+ // ???: This is not strictly correct, but it should be ignored
+ // in all places where it matters most (variables, functions, ...).
+ case AST.Timaginary32: typeName = "float"; break;
+ case AST.Timaginary64: typeName = "double"; break;
+ case AST.Timaginary80:
+ typeName = "_d_real";
+ hasReal = true;
+ break;
+ default:
+ //t.print();
+ assert(0);
+ }
+ buf.writestring(typeName);
+ }
+
+ override void visit(AST.TypePointer t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ auto ts = t.next.isTypeStruct();
+ if (ts && !strcmp(ts.sym.ident.toChars(), "__va_list_tag"))
+ {
+ buf.writestring("va_list");
+ return;
+ }
+
+ // Pointer targets can be forward referenced
+ const fwdSave = forwarding;
+ forwarding = true;
+ scope (exit) forwarding = fwdSave;
+
+ t.next.accept(this);
+ if (t.next.ty != AST.Tfunction)
+ buf.writeByte('*');
+ if (t.isConst() || t.isImmutable())
+ buf.writestring(" const");
+ }
+
+ override void visit(AST.TypeSArray t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+ t.next.accept(this);
+ }
+
+ override void visit(AST.TypeAArray t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+ AST.Type.tvoidptr.accept(this);
+ }
+
+ override void visit(AST.TypeFunction tf)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!tf);
+
+ tf.next.accept(this);
+ buf.writeByte('(');
+ buf.writeByte('*');
+ if (ident)
+ buf.writestring(ident.toChars());
+ ident = null;
+ buf.writeByte(')');
+ buf.writeByte('(');
+ foreach (i, fparam; tf.parameterList)
+ {
+ if (i)
+ buf.writestring(", ");
+ fparam.accept(this);
+ }
+ if (tf.parameterList.varargs)
+ {
+ if (tf.parameterList.parameters.dim && tf.parameterList.varargs == 1)
+ buf.writestring(", ");
+ buf.writestring("...");
+ }
+ buf.writeByte(')');
+ }
+
+ /// Writes the type that represents `ed` into `buf`.
+ /// (Might not be `ed` for special enums or enums that were emitted as namespaces)
+ private void enumToBuffer(AST.EnumDeclaration ed)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!ed);
+
+ if (ed.isSpecial())
+ {
+ if (ed.ident == DMDType.c_long)
+ buf.writestring("long");
+ else if (ed.ident == DMDType.c_ulong)
+ buf.writestring("unsigned long");
+ else if (ed.ident == DMDType.c_longlong)
+ buf.writestring("long long");
+ else if (ed.ident == DMDType.c_ulonglong)
+ buf.writestring("unsigned long long");
+ else if (ed.ident == DMDType.c_long_double)
+ buf.writestring("long double");
+ else if (ed.ident == DMDType.c_wchar_t)
+ buf.writestring("wchar_t");
+ else if (ed.ident == DMDType.c_complex_float)
+ buf.writestring("_Complex float");
+ else if (ed.ident == DMDType.c_complex_double)
+ buf.writestring("_Complex double");
+ else if (ed.ident == DMDType.c_complex_real)
+ buf.writestring("_Complex long double");
+ else
+ {
+ //ed.print();
+ assert(0);
+ }
+ return;
+ }
+
+ const kind = getEnumKind(ed.memtype);
+
+ // Check if the enum was emitted as a real enum
+ if (kind == EnumKind.Int || kind == EnumKind.Numeric)
+ {
+ writeFullName(ed);
+ }
+ else
+ {
+ // Use the base type if the enum was emitted as a namespace
+ buf.printf("/* %s */ ", ed.ident.toChars());
+ ed.memtype.accept(this);
+ }
+ }
+
+ override void visit(AST.TypeEnum t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ if (t.isConst() || t.isImmutable())
+ buf.writestring("const ");
+ enumToBuffer(t.sym);
+ }
+
+ override void visit(AST.TypeStruct t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ if (t.isConst() || t.isImmutable())
+ buf.writestring("const ");
+ writeFullName(t.sym);
+ }
+
+ override void visit(AST.TypeDArray t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ if (t.isConst() || t.isImmutable())
+ buf.writestring("const ");
+ buf.writestring("_d_dynamicArray< ");
+ t.next.accept(this);
+ buf.writestring(" >");
+ }
+
+ override void visit(AST.TypeInstance t)
+ {
+ visitTi(t.tempinst);
+ }
+
+ private void visitTi(AST.TemplateInstance ti)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!ti);
+
+ // Ensure that the TD appears before the instance
+ if (auto td = findTemplateDeclaration(ti))
+ ensureDeclared(td);
+
+ foreach (o; *ti.tiargs)
+ {
+ if (!AST.isType(o))
+ return;
+ }
+ buf.writestring(ti.name.toChars());
+ buf.writeByte('<');
+ foreach (i, o; *ti.tiargs)
+ {
+ if (i)
+ buf.writestring(", ");
+ if (auto tt = AST.isType(o))
+ {
+ tt.accept(this);
+ }
+ else
+ {
+ //ti.print();
+ //o.print();
+ assert(0);
+ }
+ }
+ buf.writestring(" >");
+ }
+
+ override void visit(AST.TemplateDeclaration td)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!td);
+
+ if (!shouldEmitAndMarkVisited(td))
+ return;
+
+ if (!td.parameters || !td.onemember || (!td.onemember.isStructDeclaration && !td.onemember.isClassDeclaration && !td.onemember.isFuncDeclaration))
+ {
+ visit(cast(AST.Dsymbol)td);
+ return;
+ }
+
+ // Explicitly disallow templates with non-type parameters or specialization.
+ foreach (p; *td.parameters)
+ {
+ if (!p.isTemplateTypeParameter() || p.specialization())
+ {
+ visit(cast(AST.Dsymbol)td);
+ return;
+ }
+ }
+
+ auto save = tdparent;
+ tdparent = td;
+ const bookmark = buf.length;
+ printTemplateParams(td);
+
+ const oldIgnored = this.ignoredCounter;
+ td.onemember.accept(this);
+
+ // Remove "template<...>" if the symbol could not be emitted
+ if (oldIgnored != this.ignoredCounter)
+ buf.setsize(bookmark);
+
+ tdparent = save;
+ }
+
+ /// Writes the template<...> header for the supplied template declaration
+ private void printTemplateParams(const AST.TemplateDeclaration td)
+ {
+ buf.writestring("template <");
+ bool first = true;
+ foreach (p; *td.parameters)
+ {
+ if (first)
+ first = false;
+ else
+ buf.writestring(", ");
+ buf.writestring("typename ");
+ writeIdentifier(p.ident, p.loc, "template parameter", true);
+ }
+ buf.writestringln(">");
+ }
+
+ /// Emit declarations of the TemplateMixin in the current scope
+ override void visit(AST.TemplateMixin tm)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!tm);
+
+ auto members = tm.members;
+
+ // members are missing for instances inside of TemplateDeclarations, e.g.
+ // template Foo(T) { mixin Bar!T; }
+ if (!members)
+ {
+ if (auto td = findTemplateDeclaration(tm))
+ members = td.members; // Emit members of the template
+ else
+ return; // Cannot emit mixin
+ }
+
+ foreach (s; *members)
+ {
+ // kind is undefined without semantic
+ const kind = s.visible().kind;
+ if (kind == AST.Visibility.Kind.public_ || kind == AST.Visibility.Kind.undefined)
+ s.accept(this);
+ }
+ }
+
+ /**
+ * Finds a symbol with the identifier `name` by iterating the linked list of parent
+ * symbols, starting from `context`.
+ *
+ * Returns: the symbol or `null` if missing
+ */
+ private AST.Dsymbol findSymbol(Identifier name, AST.Dsymbol context)
+ {
+ // Follow the declaration context
+ for (auto par = context; par; par = par.toParentDecl())
+ {
+ // Check that `name` doesn't refer to a template parameter
+ if (auto td = par.isTemplateDeclaration())
+ {
+ foreach (const p; *td.parameters)
+ {
+ if (p.ident == name)
+ return null;
+ }
+ }
+
+ if (auto mem = findMember(par, name))
+ {
+ return mem;
+ }
+ }
+ return null;
+ }
+
+ /// ditto
+ private AST.Dsymbol findSymbol(Identifier name)
+ {
+ AST.Dsymbol sym;
+ if (adparent)
+ sym = findSymbol(name, adparent);
+
+ if (!sym && tdparent)
+ sym = findSymbol(name, tdparent);
+
+ return sym;
+ }
+
+ /// Finds the template declaration for instance `ti`
+ private AST.TemplateDeclaration findTemplateDeclaration(AST.TemplateInstance ti)
+ {
+ if (ti.tempdecl)
+ return ti.tempdecl.isTemplateDeclaration();
+
+ assert(tdparent); // Only missing inside of templates
+
+ // Search for the TemplateDeclaration, starting from the enclosing scope
+ // if known or the enclosing template.
+ auto sym = findSymbol(ti.name, ti.parent ? ti.parent : tdparent);
+ return sym ? sym.isTemplateDeclaration() : null;
+ }
+
+ override void visit(AST.TypeClass t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ // Classes are emitted as pointer and hence can be forwarded
+ const fwdSave = forwarding;
+ forwarding = true;
+ scope (exit) forwarding = fwdSave;
+
+ if (t.isConst() || t.isImmutable())
+ buf.writestring("const ");
+ writeFullName(t.sym);
+ buf.writeByte('*');
+ if (t.isConst() || t.isImmutable())
+ buf.writestring(" const");
+ }
+
+ /**
+ * Writes the function signature to `buf`.
+ *
+ * Params:
+ * fd = the function to print
+ * tf = fd's type
+ */
+ private void funcToBuffer(AST.TypeFunction tf, AST.FuncDeclaration fd)
+ {
+ debug (Debug_DtoH)
+ {
+ printf("[funcToBuffer(AST.TypeFunction) enter] %s\n", fd.toChars());
+ scope(exit) printf("[funcToBuffer(AST.TypeFunction) exit] %s\n", fd.toChars());
+ }
+
+ auto originalType = cast(AST.TypeFunction)fd.originalType;
+
+ if (fd.isCtorDeclaration() || fd.isDtorDeclaration())
+ {
+ if (fd.isDtorDeclaration())
+ {
+ buf.writeByte('~');
+ }
+ buf.writestring(adparent.toChars());
+ if (!tf)
+ {
+ assert(fd.isDtorDeclaration());
+ buf.writestring("()");
+ return;
+ }
+ }
+ else
+ {
+ import dmd.root.string : toDString;
+ assert(tf.next, fd.loc.toChars().toDString());
+
+ tf.next == AST.Type.tsize_t ? originalType.next.accept(this) : tf.next.accept(this);
+ if (tf.isref)
+ buf.writeByte('&');
+ buf.writeByte(' ');
+ writeIdentifier(fd);
+ }
+
+ buf.writeByte('(');
+ foreach (i, fparam; tf.parameterList)
+ {
+ if (i)
+ buf.writestring(", ");
+ if (fparam.type == AST.Type.tsize_t && originalType)
+ {
+ fparam = originalType.parameterList[i];
+ }
+ fparam.accept(this);
+ }
+ if (tf.parameterList.varargs)
+ {
+ if (tf.parameterList.parameters.dim && tf.parameterList.varargs == 1)
+ buf.writestring(", ");
+ buf.writestring("...");
+ }
+ buf.writeByte(')');
+ }
+
+ override void visit(AST.Parameter p)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!p);
+
+ ident = p.ident;
+
+ {
+ // Reference parameters can be forwarded
+ const fwdStash = this.forwarding;
+ this.forwarding = !!(p.storageClass & AST.STC.ref_);
+ p.type.accept(this);
+ this.forwarding = fwdStash;
+ }
+
+ if (p.storageClass & AST.STC.ref_)
+ buf.writeByte('&');
+ buf.writeByte(' ');
+ if (ident)
+ // FIXME: Parameter is missing a Loc
+ writeIdentifier(ident, Loc.initial, "parameter", true);
+ ident = null;
+
+ if (p.defaultArg)
+ {
+ //printf("%s %d\n", p.defaultArg.toChars, p.defaultArg.op);
+ buf.writestring(" = ");
+ printExpressionFor(p.type, p.defaultArg);
+ }
+ }
+
+ /**
+ * Prints `exp` as an expression of type `target` while inserting
+ * appropriate code when implicit conversion does not translate
+ * directly to C++, e.g. from an enum to its base type.
+ *
+ * Params:
+ * target = the type `exp` is converted to
+ * exp = the expression to print
+ * isCtor = if `exp` is a ctor argument
+ */
+ private void printExpressionFor(AST.Type target, AST.Expression exp, const bool isCtor = false)
+ {
+ /// Determines if a static_cast is required
+ static bool needsCast(AST.Type target, AST.Expression exp)
+ {
+ // import std.stdio;
+ // writefln("%s:%s: target = %s, type = %s (%s)", exp.loc.linnum, exp.loc.charnum, target, exp.type, exp.op);
+
+ auto source = exp.type;
+
+ // DotVarExp resolve conversions, e.g from an enum to its base type
+ if (auto dve = exp.isDotVarExp())
+ source = dve.var.type;
+
+ if (!source)
+ // Defensively assume that the cast is required
+ return true;
+
+ // Conversions from enum class to base type require static_cast
+ if (global.params.cplusplus >= CppStdRevision.cpp11 &&
+ source.isTypeEnum && !target.isTypeEnum)
+ return true;
+
+ return false;
+ }
+
+ // Slices are emitted as a special struct, hence we need to fix up
+ // any expression initialising a slice variable/member
+ if (auto ta = target.isTypeDArray())
+ {
+ if (exp.isNullExp())
+ {
+ if (isCtor)
+ {
+ // Don't emit, use default ctor
+ }
+ else if (global.params.cplusplus >= CppStdRevision.cpp11)
+ {
+ // Prefer initializer list
+ buf.writestring("{}");
+ }
+ else
+ {
+ // Write __d_dynamic_array<TYPE>()
+ visit(ta);
+ buf.writestring("()");
+ }
+ return;
+ }
+
+ if (auto se = exp.isStringExp())
+ {
+ // Rewrite as <length> + <literal> pair optionally
+ // wrapped in a initializer list/ctor call
+
+ const initList = global.params.cplusplus >= CppStdRevision.cpp11;
+ if (!isCtor)
+ {
+ if (initList)
+ buf.writestring("{ ");
+ else
+ {
+ visit(ta);
+ buf.writestring("( ");
+ }
+ }
+
+ buf.printf("%zu, ", se.len);
+ visit(se);
+
+ if (!isCtor)
+ buf.writestring(initList ? " }" : " )");
+
+ return;
+ }
+ }
+ else if (auto ce = exp.isCastExp())
+ {
+ buf.writeByte('(');
+ if (ce.to)
+ ce.to.accept(this);
+ else if (ce.e1.type)
+ // Try the expression type with modifiers in case of cast(const) in templates
+ ce.e1.type.castMod(ce.mod).accept(this);
+ else
+ // Fallback, not necessarily correct but the best we've got here
+ target.accept(this);
+ buf.writestring(") ");
+ ce.e1.accept(this);
+ }
+ else if (needsCast(target, exp))
+ {
+ buf.writestring("static_cast<");
+ target.accept(this);
+ buf.writestring(">(");
+ exp.accept(this);
+ buf.writeByte(')');
+ }
+ else
+ {
+ exp.accept(this);
+ }
+ }
+
+ override void visit(AST.Expression e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ // Valid in most cases, others should be overriden below
+ // to use the appropriate operators (:: and ->)
+ buf.writestring(e.toString());
+ }
+
+ override void visit(AST.UnaExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ buf.writestring(tokToString(e.op));
+ e.e1.accept(this);
+ }
+
+ override void visit(AST.BinExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ e.e1.accept(this);
+ buf.writeByte(' ');
+ buf.writestring(tokToString(e.op));
+ buf.writeByte(' ');
+ e.e2.accept(this);
+ }
+
+ /// Translates operator `op` into the C++ representation
+ private extern(D) static string tokToString(const TOK op)
+ {
+ switch (op) with (TOK)
+ {
+ case identity: return "==";
+ case notIdentity: return "!=";
+ default:
+ return Token.toString(op);
+ }
+ }
+
+ override void visit(AST.VarExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ // Local members don't need another prefix and might've been renamed
+ if (e.var.isThis())
+ {
+ includeSymbol(e.var);
+ writeIdentifier(e.var, true);
+ }
+ else
+ writeFullName(e.var);
+ }
+
+ /// Partially prints the FQN including parent aggregates
+ private void printPrefix(AST.Dsymbol var)
+ {
+ if (!var || var is adparent || var.isModule())
+ return;
+
+ writeFullName(var);
+ buf.writestring("::");
+ }
+
+ override void visit(AST.CallExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ // Dereferencing function pointers requires additional braces: (*f)(args)
+ const isFp = e.e1.isPtrExp();
+ if (isFp)
+ buf.writeByte('(');
+ else if (e.f)
+ includeSymbol(outermostSymbol(e.f));
+
+ e.e1.accept(this);
+
+ if (isFp) buf.writeByte(')');
+
+ assert(e.arguments);
+ buf.writeByte('(');
+ foreach (i, arg; *e.arguments)
+ {
+ if (i)
+ buf.writestring(", ");
+ arg.accept(this);
+ }
+ buf.writeByte(')');
+ }
+
+ override void visit(AST.DotVarExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ if (auto sym = symbolFromType(e.e1.type))
+ includeSymbol(outermostSymbol(sym));
+
+ // Accessing members through a pointer?
+ if (auto pe = e.e1.isPtrExp)
+ {
+ pe.e1.accept(this);
+ buf.writestring("->");
+ }
+ else
+ {
+ e.e1.accept(this);
+ buf.writeByte('.');
+ }
+
+ // Should only be used to access non-static members
+ assert(e.var.isThis());
+
+ writeIdentifier(e.var, true);
+ }
+
+ override void visit(AST.DotIdExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ e.e1.accept(this);
+ buf.writestring("::");
+ buf.writestring(e.ident.toChars());
+ }
+
+ override void visit(AST.ScopeExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ // Usually a template instance in a TemplateDeclaration
+ if (auto ti = e.sds.isTemplateInstance())
+ visitTi(ti);
+ else
+ writeFullName(e.sds);
+ }
+
+ override void visit(AST.NullExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ if (global.params.cplusplus >= CppStdRevision.cpp11)
+ buf.writestring("nullptr");
+ else
+ buf.writestring("NULL");
+ }
+
+ override void visit(AST.ArrayLiteralExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+ buf.writestring("arrayliteral");
+ }
+
+ override void visit(AST.StringExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ if (e.sz == 2)
+ buf.writeByte('u');
+ else if (e.sz == 4)
+ buf.writeByte('U');
+ buf.writeByte('"');
+
+ for (size_t i = 0; i < e.len; i++)
+ {
+ uint c = e.charAt(i);
+ switch (c)
+ {
+ case '"':
+ case '\\':
+ buf.writeByte('\\');
+ goto default;
+ default:
+ if (c <= 0xFF)
+ {
+ if (c >= 0x20 && c < 0x80)
+ buf.writeByte(c);
+ else
+ buf.printf("\\x%02x", c);
+ }
+ else if (c <= 0xFFFF)
+ buf.printf("\\u%04x", c);
+ else
+ buf.printf("\\U%08x", c);
+ break;
+ }
+ }
+ buf.writeByte('"');
+ }
+
+ override void visit(AST.RealExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+
+ import dmd.root.ctfloat : CTFloat;
+
+ // Special case NaN and Infinity because floatToBuffer
+ // uses D literals (`nan` and `infinity`)
+ if (CTFloat.isNaN(e.value))
+ {
+ buf.writestring("NAN");
+ }
+ else if (CTFloat.isInfinity(e.value))
+ {
+ if (e.value < CTFloat.zero)
+ buf.writeByte('-');
+ buf.writestring("INFINITY");
+ }
+ else
+ {
+ import dmd.hdrgen;
+ // Hex floating point literals were introduced in C++ 17
+ const allowHex = global.params.cplusplus >= CppStdRevision.cpp17;
+ floatToBuffer(e.type, e.value, buf, allowHex);
+ }
+ }
+
+ override void visit(AST.IntegerExp e)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!e);
+ visitInteger(e.toInteger, e.type);
+ }
+
+ /// Writes `v` as type `t` into `buf`
+ private void visitInteger(dinteger_t v, AST.Type t)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!t);
+
+ switch (t.ty)
+ {
+ case AST.Tenum:
+ auto te = cast(AST.TypeEnum)t;
+ buf.writestring("(");
+ enumToBuffer(te.sym);
+ buf.writestring(")");
+ visitInteger(v, te.sym.memtype);
+ break;
+ case AST.Tbool:
+ buf.writestring(v ? "true" : "false");
+ break;
+ case AST.Tint8:
+ buf.printf("%d", cast(byte)v);
+ break;
+ case AST.Tuns8:
+ buf.printf("%uu", cast(ubyte)v);
+ break;
+ case AST.Tint16:
+ buf.printf("%d", cast(short)v);
+ break;
+ case AST.Tuns16:
+ case AST.Twchar:
+ buf.printf("%uu", cast(ushort)v);
+ break;
+ case AST.Tint32:
+ case AST.Tdchar:
+ buf.printf("%d", cast(int)v);
+ break;
+ case AST.Tuns32:
+ buf.printf("%uu", cast(uint)v);
+ break;
+ case AST.Tint64:
+ buf.printf("%lldLL", v);
+ break;
+ case AST.Tuns64:
+ buf.printf("%lluLLU", v);
+ break;
+ case AST.Tchar:
+ if (v > 0x20 && v < 0x80)
+ buf.printf("'%c'", cast(int)v);
+ else
+ buf.printf("%uu", cast(ubyte)v);
+ break;
+ default:
+ //t.print();
+ assert(0);
+ }
+ }
+
+ override void visit(AST.StructLiteralExp sle)
+ {
+ debug (Debug_DtoH) mixin(traceVisit!sle);
+
+ const isUnion = sle.sd.isUnionDeclaration();
+ sle.sd.type.accept(this);
+ buf.writeByte('(');
+ foreach(i, e; *sle.elements)
+ {
+ if (i)
+ buf.writestring(", ");
+
+ auto vd = sle.sd.fields[i];
+
+ // Expression may be null for unspecified elements
+ if (!e)
+ e = findDefaultInitializer(vd);
+
+ printExpressionFor(vd.type, e);
+
+ // Only emit the initializer of the first union member
+ if (isUnion)
+ break;
+ }
+ buf.writeByte(')');
+ }
+
+ /// Finds the default initializer for the given VarDeclaration
+ private static AST.Expression findDefaultInitializer(AST.VarDeclaration vd)
+ {
+ if (vd._init && !vd._init.isVoidInitializer())
+ return AST.initializerToExpression(vd._init);
+ else
+ return vd.type.defaultInitLiteral(Loc.initial);
+ }
+
+ static if (__VERSION__ < 2092)
+ {
+ private void ignored(const char* format, ...) nothrow
+ {
+ this.ignoredCounter++;
+
+ import core.stdc.stdarg;
+ if (!printIgnored)
+ return;
+
+ va_list ap;
+ va_start(ap, format);
+ buf.writestring("// Ignored ");
+ buf.vprintf(format, ap);
+ buf.writenl();
+ va_end(ap);
+ }
+ }
+ else
+ {
+ /// Writes a formatted message into `buf` if `printIgnored` is true
+ /// and increments `ignoredCounter`
+ pragma(printf)
+ private void ignored(const char* format, ...) nothrow
+ {
+ this.ignoredCounter++;
+
+ import core.stdc.stdarg;
+ if (!printIgnored)
+ return;
+
+ va_list ap;
+ va_start(ap, format);
+ buf.writestring("// Ignored ");
+ buf.vprintf(format, ap);
+ buf.writenl();
+ va_end(ap);
+ }
+ }
+
+ /**
+ * Determines whether `s` should be emitted. This requires that `sym`
+ * - is `extern(C[++]`)
+ * - is not instantiated from a template (visits the `TemplateDeclaration` instead)
+ *
+ * Params:
+ * sym = the symbol
+ *
+ * Returns: whether `sym` should be emitted
+ */
+ private bool shouldEmit(AST.Dsymbol sym)
+ {
+ import dmd.aggregate : ClassKind;
+ debug (Debug_DtoH)
+ {
+ printf("[shouldEmitAndMarkVisited enter] %s\n", sym.toPrettyChars());
+ scope(exit) printf("[shouldEmitAndMarkVisited exit] %s\n", sym.toPrettyChars());
+ }
+
+ // Template *instances* should not be emitted
+ if (sym.isInstantiated())
+ return false;
+
+ // Matching linkage (except extern(C) classes which don't make sense)
+ if (linkage == LINK.cpp || (linkage == LINK.c && !sym.isClassDeclaration()))
+ return true;
+
+ // Check against the internal information which might be missing, e.g. inside of template declarations
+ if (auto dec = sym.isDeclaration())
+ return dec.linkage == LINK.cpp || dec.linkage == LINK.c;
+
+ if (auto ad = sym.isAggregateDeclaration())
+ return ad.classKind == ClassKind.cpp;
+
+ return false;
+ }
+
+ /**
+ * Determines whether `s` should be emitted. This requires that `sym`
+ * - was not visited before
+ * - is `extern(C[++]`)
+ * - is not instantiated from a template (visits the `TemplateDeclaration` instead)
+ * The result is cached in the visited nodes array.
+ *
+ * Params:
+ * sym = the symbol
+ *
+ * Returns: whether `sym` should be emitted
+ **/
+ private bool shouldEmitAndMarkVisited(AST.Dsymbol sym)
+ {
+ debug (Debug_DtoH)
+ {
+ printf("[shouldEmitAndMarkVisited enter] %s\n", sym.toPrettyChars());
+ scope(exit) printf("[shouldEmitAndMarkVisited exit] %s\n", sym.toPrettyChars());
+ }
+
+ auto statePtr = (cast(void*) sym) in visited;
+
+ // `sym` was already emitted or skipped and isn't required
+ if (statePtr && (*statePtr || !mustEmit))
+ return false;
+
+ // Template *instances* should not be emitted, forward to the declaration
+ if (auto ti = sym.isInstantiated())
+ {
+ auto td = findTemplateDeclaration(ti);
+ assert(td);
+ visit(td);
+ return false;
+ }
+
+ // Required or matching linkage (except extern(C) classes which don't make sense)
+ bool res = mustEmit || linkage == LINK.cpp || (linkage == LINK.c && !sym.isClassDeclaration());
+ if (!res)
+ {
+ // Check against the internal information which might be missing, e.g. inside of template declarations
+ auto dec = sym.isDeclaration();
+ res = dec && (dec.linkage == LINK.cpp || dec.linkage == LINK.c);
+ }
+
+ // Remember result for later calls
+ if (statePtr)
+ *statePtr = res;
+ else
+ visited[(cast(void*) sym)] = res;
+
+ // Print a warning when the symbol is ignored for the first time
+ // Might not be correct if it is required by symbol the is visited
+ // AFTER the current node
+ if (!statePtr && !res)
+ ignored("%s %s because of linkage", sym.kind(), sym.toPrettyChars());
+
+ return res;
+ }
+
+ /**
+ * Ensures that `sym` is declared before the current position in `buf` by
+ * either creating a forward reference in `fwdbuf` if possible or
+ * calling `includeSymbol` to emit the entire declaration into `donebuf`.
+ */
+ private void ensureDeclared(AST.Dsymbol sym)
+ {
+ auto par = sym.toParent2();
+ auto ed = sym.isEnumDeclaration();
+
+ // Eagerly include the symbol if we cannot create a valid forward declaration
+ // Forwarding of scoped enums requires C++11 or above
+ if (!forwarding || (par && !par.isModule()) || (ed && global.params.cplusplus < CppStdRevision.cpp11))
+ {
+ // Emit the entire enclosing declaration if any
+ includeSymbol(outermostSymbol(sym));
+ return;
+ }
+
+ auto ti = sym.isInstantiated();
+ auto td = ti ? findTemplateDeclaration(ti) : null;
+ auto check = cast(void*) (td ? td : sym);
+
+ // Omit redundant fwd-declaration if we already emitted the entire declaration
+ if (visited.get(check, false))
+ return;
+
+ // Already created a fwd-declaration?
+ if (check in forwarded)
+ return;
+ forwarded[check] = true;
+
+ // Print template<...>
+ if (ti)
+ {
+ auto bufSave = buf;
+ buf = fwdbuf;
+ printTemplateParams(td);
+ buf = bufSave;
+ }
+
+ // Determine the kind of symbol that is forwared: struct, ...
+ const(char)* kind;
+
+ if (auto ad = sym.isAggregateDeclaration())
+ {
+ // Look for extern(C++, class) <some aggregate>
+ if (ad.cppmangle == CPPMANGLE.def)
+ kind = ad.kind();
+ else if (ad.cppmangle == CPPMANGLE.asStruct)
+ kind = "struct";
+ else
+ kind = "class";
+ }
+ else if (ed)
+ {
+ // Only called from enumToBuffer, so should always be emitted as an actual enum
+ kind = "enum class";
+ }
+ else
+ kind = sym.kind(); // Should be unreachable but just to be sure
+
+ fwdbuf.writestring(kind);
+ fwdbuf.writeByte(' ');
+ fwdbuf.writestring(sym.toChars());
+ fwdbuf.writestringln(";");
+ }
+
+ /**
+ * Writes the qualified name of `sym` into `buf` including parent
+ * symbols and template parameters.
+ *
+ * Params:
+ * sym = the symbol
+ * mustInclude = whether sym may not be forward declared
+ */
+ private void writeFullName(AST.Dsymbol sym, const bool mustInclude = false)
+ in
+ {
+ assert(sym);
+ assert(sym.ident, sym.toString());
+ // Should never be called directly with a TI, only onemember
+ assert(!sym.isTemplateInstance(), sym.toString());
+ }
+ do
+ {
+ debug (Debug_DtoH)
+ {
+ printf("[writeFullName enter] %s\n", sym.toPrettyChars());
+ scope(exit) printf("[writeFullName exit] %s\n", sym.toPrettyChars());
+ }
+
+ /// Checks whether `sym` is nested in `par` and hence doesn't need the FQN
+ static bool isNestedIn(AST.Dsymbol sym, AST.Dsymbol par)
+ {
+ while (par)
+ {
+ if (sym is par)
+ return true;
+ par = par.toParent();
+ }
+ return false;
+ }
+ AST.TemplateInstance ti;
+ bool nested;
+
+ // Check if the `sym` is nested into another symbol and hence requires `Parent::sym`
+ if (auto par = sym.toParent())
+ {
+ // toParent() yields the template instance if `sym` is the onemember of a TI
+ ti = par.isTemplateInstance();
+
+ // Skip the TI because Foo!int.Foo is folded into Foo<int>
+ if (ti) par = ti.toParent();
+
+ // Prefix the name with any enclosing declaration
+ // Stop at either module or enclosing aggregate
+ nested = !par.isModule();
+ if (nested && !isNestedIn(par, adparent))
+ {
+ writeFullName(par, true);
+ buf.writestring("::");
+ }
+ }
+
+ if (!nested)
+ {
+ // Cannot forward the symbol when called recursively
+ // for a nested symbol
+ if (mustInclude)
+ includeSymbol(sym);
+ else
+ ensureDeclared(sym);
+ }
+
+ if (ti)
+ visitTi(ti);
+ else
+ buf.writestring(sym.ident.toString());
+ }
+}
+
+/// Namespace for identifiers used to represent special enums in C++
+struct DMDType
+{
+ __gshared Identifier c_long;
+ __gshared Identifier c_ulong;
+ __gshared Identifier c_longlong;
+ __gshared Identifier c_ulonglong;
+ __gshared Identifier c_long_double;
+ __gshared Identifier c_wchar_t;
+ __gshared Identifier c_complex_float;
+ __gshared Identifier c_complex_double;
+ __gshared Identifier c_complex_real;
+
+ static void _init()
+ {
+ c_long = Identifier.idPool("__c_long");
+ c_ulong = Identifier.idPool("__c_ulong");
+ c_longlong = Identifier.idPool("__c_longlong");
+ c_ulonglong = Identifier.idPool("__c_ulonglong");
+ c_long_double = Identifier.idPool("__c_long_double");
+ c_wchar_t = Identifier.idPool("__c_wchar_t");
+ c_complex_float = Identifier.idPool("__c_complex_float");
+ c_complex_double = Identifier.idPool("__c_complex_double");
+ c_complex_real = Identifier.idPool("__c_complex_real");
+ }
+}
+
+/// Initializes all data structures used by the header generator
+void initialize()
+{
+ __gshared bool initialized;
+
+ if (!initialized)
+ {
+ initialized = true;
+
+ DMDType._init();
+ }
+}
+
+/// Writes `#if <content>` into the supplied buffer
+void hashIf(ref OutBuffer buf, string content)
+{
+ buf.writestring("#if ");
+ buf.writestringln(content);
+}
+
+/// Writes `#elif <content>` into the supplied buffer
+void hashElIf(ref OutBuffer buf, string content)
+{
+ buf.writestring("#elif ");
+ buf.writestringln(content);
+}
+
+/// Writes `#endif` into the supplied buffer
+void hashEndIf(ref OutBuffer buf)
+{
+ buf.writestringln("#endif");
+}
+
+/// Writes `#define <content>` into the supplied buffer
+void hashDefine(ref OutBuffer buf, string content)
+{
+ buf.writestring("# define ");
+ buf.writestringln(content);
+}
+
+/// Writes `#include <content>` into the supplied buffer
+void hashInclude(ref OutBuffer buf, string content)
+{
+ buf.writestring("#include ");
+ buf.writestringln(content);
+}
+
+/// Determines whether `ident` is a reserved keyword in C++
+/// Returns: the kind of keyword or `null`
+const(char*) keywordClass(const Identifier ident)
+{
+ if (!ident)
+ return null;
+
+ const name = ident.toString();
+ switch (name)
+ {
+ // C++ operators
+ case "and":
+ case "and_eq":
+ case "bitand":
+ case "bitor":
+ case "compl":
+ case "not":
+ case "not_eq":
+ case "or":
+ case "or_eq":
+ case "xor":
+ case "xor_eq":
+ return "special operator in C++";
+
+ // C++ keywords
+ case "_Complex":
+ case "const_cast":
+ case "delete":
+ case "dynamic_cast":
+ case "explicit":
+ case "friend":
+ case "inline":
+ case "mutable":
+ case "namespace":
+ case "operator":
+ case "register":
+ case "reinterpret_cast":
+ case "signed":
+ case "static_cast":
+ case "typedef":
+ case "typename":
+ case "unsigned":
+ case "using":
+ case "virtual":
+ case "volatile":
+ return "keyword in C++";
+
+ // Common macros imported by this header
+ // stddef.h
+ case "offsetof":
+ case "NULL":
+ return "default macro in C++";
+
+ // C++11 keywords
+ case "alignas":
+ case "alignof":
+ case "char16_t":
+ case "char32_t":
+ case "constexpr":
+ case "decltype":
+ case "noexcept":
+ case "nullptr":
+ case "static_assert":
+ case "thread_local":
+ case "wchar_t":
+ if (global.params.cplusplus >= CppStdRevision.cpp11)
+ return "keyword in C++11";
+ return null;
+
+ // C++20 keywords
+ case "char8_t":
+ case "consteval":
+ case "constinit":
+ // Concepts-related keywords
+ case "concept":
+ case "requires":
+ // Coroutines-related keywords
+ case "co_await":
+ case "co_yield":
+ case "co_return":
+ if (global.params.cplusplus >= CppStdRevision.cpp20)
+ return "keyword in C++20";
+ return null;
+
+ default:
+ // Identifiers starting with __ are reserved
+ if (name.length >= 2 && name[0..2] == "__")
+ return "reserved identifier in C++";
+
+ return null;
+ }
+}
+
+/// Finds the outermost symbol if `sym` is nested.
+/// Returns `sym` if it appears at module scope
+ASTCodegen.Dsymbol outermostSymbol(ASTCodegen.Dsymbol sym)
+{
+ assert(sym);
+ while (true)
+ {
+ auto par = sym.toParent();
+ if (!par || par.isModule())
+ return sym;
+ sym = par;
+ }
+}
+
+/// Fetches the symbol for user-defined types from the type `t`
+/// if `t` is either `TypeClass`, `TypeStruct` or `TypeEnum`
+ASTCodegen.Dsymbol symbolFromType(ASTCodegen.Type t)
+{
+ if (auto tc = t.isTypeClass())
+ return tc.sym;
+ if (auto ts = t.isTypeStruct())
+ return ts.sym;
+ if (auto te = t.isTypeEnum())
+ return te.sym;
+ return null;
+}
+
+/**
+ * Searches `sym` for a member with the given name.
+ *
+ * This method usually delegates to `Dsymbol.search` but might also
+ * manually check the members if the symbol did not receive semantic
+ * analysis.
+ *
+ * Params:
+ * sym = symbol to search
+ * name = identifier of the requested symbol
+ *
+ * Returns: the symbol or `null` if not found
+ */
+ASTCodegen.Dsymbol findMember(ASTCodegen.Dsymbol sym, Identifier name)
+{
+ if (auto mem = sym.search(Loc.initial, name, ASTCodegen.IgnoreErrors))
+ return mem;
+
+ // search doesn't work for declarations inside of uninstantiated
+ // `TemplateDeclaration`s due to the missing symtab.
+ if (sym.semanticRun >= ASTCodegen.PASS.semanticdone)
+ return null;
+
+ // Manually check the members if present
+ auto sds = sym.isScopeDsymbol();
+ if (!sds || !sds.members)
+ return null;
+
+ /// Recursively searches for `name` without entering nested aggregates, ...
+ static ASTCodegen.Dsymbol search(ASTCodegen.Dsymbols* members, Identifier name)
+ {
+ foreach (mem; *members)
+ {
+ if (mem.ident == name)
+ return mem;
+
+ // Look inside of private:, ...
+ if (auto ad = mem.isAttribDeclaration())
+ {
+ if (auto s = search(ad.decl, name))
+ return s;
+ }
+ }
+ return null;
+ }
+
+ return search(sds.members, name);
+}
+
+debug (Debug_DtoH)
+{
+ /// Generates code to trace the entry and exit of the enclosing `visit` function
+ string traceVisit(alias node)()
+ {
+ const type = typeof(node).stringof;
+ const method = __traits(hasMember, node, "toPrettyChars") ? "toPrettyChars" : "toChars";
+ const arg = __traits(identifier, node) ~ '.' ~ method;
+
+ return `printf("[` ~ type ~ ` enter] %s\n", ` ~ arg ~ `());
+ scope(exit) printf("[` ~ type ~ ` exit] %s\n", ` ~ arg ~ `());`;
+ }
+}
diff --git a/gcc/d/dmd/dversion.c b/gcc/d/dmd/dversion.c
deleted file mode 100644
index 269d92466cd..00000000000
--- a/gcc/d/dmd/dversion.c
+++ /dev/null
@@ -1,187 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/version.c
- */
-
-#include "root/dsystem.h"
-#include "root/root.h"
-
-#include "identifier.h"
-#include "dsymbol.h"
-#include "cond.h"
-#include "version.h"
-#include "module.h"
-
-void checkReserved(Loc loc, const char *ident);
-
-/* ================================================== */
-
-/* DebugSymbol's happen for statements like:
- * debug = identifier;
- * debug = integer;
- */
-
-DebugSymbol::DebugSymbol(Loc loc, Identifier *ident)
- : Dsymbol(ident)
-{
- this->loc = loc;
-}
-
-DebugSymbol::DebugSymbol(Loc loc, unsigned level)
- : Dsymbol()
-{
- this->level = level;
- this->loc = loc;
-}
-
-const char *DebugSymbol::toChars()
-{
- if (ident)
- return ident->toChars();
- else
- {
- OutBuffer buf;
- buf.printf("%d", level);
- return buf.extractChars();
- }
-}
-
-Dsymbol *DebugSymbol::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- DebugSymbol *ds = new DebugSymbol(loc, ident);
- ds->level = level;
- return ds;
-}
-
-void DebugSymbol::addMember(Scope *, ScopeDsymbol *sds)
-{
- //printf("DebugSymbol::addMember('%s') %s\n", sds->toChars(), toChars());
- Module *m = sds->isModule();
-
- // Do not add the member to the symbol table,
- // just make sure subsequent debug declarations work.
- if (ident)
- {
- if (!m)
- {
- error("declaration must be at module level");
- errors = true;
- }
- else
- {
- if (findCondition(m->debugidsNot, ident))
- {
- error("defined after use");
- errors = true;
- }
- if (!m->debugids)
- m->debugids = new Identifiers();
- m->debugids->push(ident);
- }
- }
- else
- {
- if (!m)
- {
- error("level declaration must be at module level");
- errors = true;
- }
- else
- m->debuglevel = level;
- }
-}
-
-const char *DebugSymbol::kind() const
-{
- return "debug";
-}
-
-/* ================================================== */
-
-/* VersionSymbol's happen for statements like:
- * version = identifier;
- * version = integer;
- */
-
-VersionSymbol::VersionSymbol(Loc loc, Identifier *ident)
- : Dsymbol(ident)
-{
- this->loc = loc;
-}
-
-VersionSymbol::VersionSymbol(Loc loc, unsigned level)
- : Dsymbol()
-{
- this->level = level;
- this->loc = loc;
-}
-
-const char *VersionSymbol::toChars()
-{
- if (ident)
- return ident->toChars();
- else
- {
- OutBuffer buf;
- buf.printf("%d", level);
- return buf.extractChars();
- }
-}
-
-Dsymbol *VersionSymbol::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- VersionSymbol *ds = ident ? new VersionSymbol(loc, ident)
- : new VersionSymbol(loc, level);
- return ds;
-}
-
-void VersionSymbol::addMember(Scope *, ScopeDsymbol *sds)
-{
- //printf("VersionSymbol::addMember('%s') %s\n", sds->toChars(), toChars());
- Module *m = sds->isModule();
-
- // Do not add the member to the symbol table,
- // just make sure subsequent debug declarations work.
- if (ident)
- {
- checkReserved(loc, ident->toChars());
- if (!m)
- {
- error("declaration must be at module level");
- errors = true;
- }
- else
- {
- if (findCondition(m->versionidsNot, ident))
- {
- error("defined after use");
- errors = true;
- }
- if (!m->versionids)
- m->versionids = new Identifiers();
- m->versionids->push(ident);
- }
- }
- else
- {
- if (!m)
- {
- error("level declaration must be at module level");
- errors = true;
- }
- else
- m->versionlevel = level;
- }
-}
-
-const char *VersionSymbol::kind() const
-{
- return "version";
-}
diff --git a/gcc/d/dmd/dversion.d b/gcc/d/dmd/dversion.d
new file mode 100644
index 00000000000..0dff754bee5
--- /dev/null
+++ b/gcc/d/dmd/dversion.d
@@ -0,0 +1,215 @@
+/**
+ * Defines a `Dsymbol` for `version = identifier` and `debug = identifier` statements.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/version.html#version-specification, Version Specification),
+ * $(LINK2 https://dlang.org/spec/version.html#debug_specification, Debug Specification).
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dversion.d, _dversion.d)
+ * Documentation: https://dlang.org/phobos/dmd_dversion.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dversion.d
+ */
+
+module dmd.dversion;
+
+import dmd.arraytypes;
+import dmd.cond;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.globals;
+import dmd.identifier;
+import dmd.root.outbuffer;
+import dmd.visitor;
+
+/***********************************************************
+ * DebugSymbol's happen for statements like:
+ * debug = identifier;
+ * debug = integer;
+ */
+extern (C++) final class DebugSymbol : Dsymbol
+{
+ uint level;
+
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, ident);
+ }
+
+ extern (D) this(const ref Loc loc, uint level)
+ {
+ super(loc, null);
+ this.level = level;
+ }
+
+ override DebugSymbol syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto ds = new DebugSymbol(loc, ident);
+ ds.comment = comment;
+ ds.level = level;
+ return ds;
+ }
+
+ override const(char)* toChars() const nothrow
+ {
+ if (ident)
+ return ident.toChars();
+ else
+ {
+ OutBuffer buf;
+ buf.print(level);
+ return buf.extractChars();
+ }
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ //printf("DebugSymbol::addMember('%s') %s\n", sds.toChars(), toChars());
+ Module m = sds.isModule();
+ // Do not add the member to the symbol table,
+ // just make sure subsequent debug declarations work.
+ if (ident)
+ {
+ if (!m)
+ {
+ error("declaration must be at module level");
+ errors = true;
+ }
+ else
+ {
+ if (findCondition(m.debugidsNot, ident))
+ {
+ error("defined after use");
+ errors = true;
+ }
+ if (!m.debugids)
+ m.debugids = new Identifiers();
+ m.debugids.push(ident);
+ }
+ }
+ else
+ {
+ if (!m)
+ {
+ error("level declaration must be at module level");
+ errors = true;
+ }
+ else
+ m.debuglevel = level;
+ }
+ }
+
+ override const(char)* kind() const nothrow
+ {
+ return "debug";
+ }
+
+ override inout(DebugSymbol) isDebugSymbol() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * VersionSymbol's happen for statements like:
+ * version = identifier;
+ * version = integer;
+ */
+extern (C++) final class VersionSymbol : Dsymbol
+{
+ uint level;
+
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, ident);
+ }
+
+ extern (D) this(const ref Loc loc, uint level)
+ {
+ super(loc, null);
+ this.level = level;
+ }
+
+ override VersionSymbol syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto ds = ident ? new VersionSymbol(loc, ident)
+ : new VersionSymbol(loc, level);
+ ds.comment = comment;
+ return ds;
+ }
+
+ override const(char)* toChars() const nothrow
+ {
+ if (ident)
+ return ident.toChars();
+ else
+ {
+ OutBuffer buf;
+ buf.print(level);
+ return buf.extractChars();
+ }
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ //printf("VersionSymbol::addMember('%s') %s\n", sds.toChars(), toChars());
+ Module m = sds.isModule();
+ // Do not add the member to the symbol table,
+ // just make sure subsequent debug declarations work.
+ if (ident)
+ {
+ VersionCondition.checkReserved(loc, ident.toString());
+ if (!m)
+ {
+ error("declaration must be at module level");
+ errors = true;
+ }
+ else
+ {
+ if (findCondition(m.versionidsNot, ident))
+ {
+ error("defined after use");
+ errors = true;
+ }
+ if (!m.versionids)
+ m.versionids = new Identifiers();
+ m.versionids.push(ident);
+ }
+ }
+ else
+ {
+ if (!m)
+ {
+ error("level declaration must be at module level");
+ errors = true;
+ }
+ else
+ m.versionlevel = level;
+ }
+ }
+
+ override const(char)* kind() const nothrow
+ {
+ return "version";
+ }
+
+ override inout(VersionSymbol) isVersionSymbol() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
diff --git a/gcc/d/dmd/entity.c b/gcc/d/dmd/entity.c
deleted file mode 100644
index cac901d1a6e..00000000000
--- a/gcc/d/dmd/entity.c
+++ /dev/null
@@ -1,2390 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/entity.c
- */
-
-#include "root/dsystem.h"
-#include "root/port.h"
-
-/*********************************************
- * Convert from named entity to its encoding.
- * For reference:
- * http://www.htmlhelp.com/reference/html40/entities/
- * http://www.w3.org/2003/entities/2007/w3centities-f.ent
- */
-
-struct NameId
-{
- const char *name;
- unsigned value;
-};
-
-static NameId namesA[]={
- {"Aacgr", 0x00386}, // GREEK CAPITAL LETTER ALPHA WITH TONOS
- {"aacgr", 0x003AC}, // GREEK SMALL LETTER ALPHA WITH TONOS
- {"Aacute", 0x000C1}, // LATIN CAPITAL LETTER A WITH ACUTE
- {"aacute", 0x000E1}, // LATIN SMALL LETTER A WITH ACUTE
- {"Abreve", 0x00102}, // LATIN CAPITAL LETTER A WITH BREVE
- {"abreve", 0x00103}, // LATIN SMALL LETTER A WITH BREVE
- {"ac", 0x0223E}, // INVERTED LAZY S
- {"acd", 0x0223F}, // SINE WAVE
-// {"acE", 0x0223E;0x00333}, // INVERTED LAZY S with double underline
- {"Acirc", 0x000C2}, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
- {"acirc", 0x000E2}, // LATIN SMALL LETTER A WITH CIRCUMFLEX
- {"acute", 0x000B4}, // ACUTE ACCENT
- {"Acy", 0x00410}, // CYRILLIC CAPITAL LETTER A
- {"acy", 0x00430}, // CYRILLIC SMALL LETTER A
- {"AElig", 0x000C6}, // LATIN CAPITAL LETTER AE
- {"aelig", 0x000E6}, // LATIN SMALL LETTER AE
- {"af", 0x02061}, // FUNCTION APPLICATION
- {"Afr", 0x1D504}, // MATHEMATICAL FRAKTUR CAPITAL A
- {"afr", 0x1D51E}, // MATHEMATICAL FRAKTUR SMALL A
- {"Agr", 0x00391}, // GREEK CAPITAL LETTER ALPHA
- {"agr", 0x003B1}, // GREEK SMALL LETTER ALPHA
- {"Agrave", 0x000C0}, // LATIN CAPITAL LETTER A WITH GRAVE
- {"agrave", 0x000E0}, // LATIN SMALL LETTER A WITH GRAVE
- {"alefsym", 0x02135}, // ALEF SYMBOL
- {"aleph", 0x02135}, // ALEF SYMBOL
- {"Alpha", 0x00391}, // GREEK CAPITAL LETTER ALPHA
- {"alpha", 0x003B1}, // GREEK SMALL LETTER ALPHA
- {"Amacr", 0x00100}, // LATIN CAPITAL LETTER A WITH MACRON
- {"amacr", 0x00101}, // LATIN SMALL LETTER A WITH MACRON
- {"amalg", 0x02A3F}, // AMALGAMATION OR COPRODUCT
- {"amp", 0x00026}, // AMPERSAND
- {"AMP", 0x00026}, // AMPERSAND
- {"and", 0x02227}, // LOGICAL AND
- {"And", 0x02A53}, // DOUBLE LOGICAL AND
- {"andand", 0x02A55}, // TWO INTERSECTING LOGICAL AND
- {"andd", 0x02A5C}, // LOGICAL AND WITH HORIZONTAL DASH
- {"andslope", 0x02A58}, // SLOPING LARGE AND
- {"andv", 0x02A5A}, // LOGICAL AND WITH MIDDLE STEM
- {"ang", 0x02220}, // ANGLE
- {"ange", 0x029A4}, // ANGLE WITH UNDERBAR
- {"angle", 0x02220}, // ANGLE
- {"angmsd", 0x02221}, // MEASURED ANGLE
- {"angmsdaa", 0x029A8}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT
- {"angmsdab", 0x029A9}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT
- {"angmsdac", 0x029AA}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT
- {"angmsdad", 0x029AB}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT
- {"angmsdae", 0x029AC}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP
- {"angmsdaf", 0x029AD}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP
- {"angmsdag", 0x029AE}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN
- {"angmsdah", 0x029AF}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN
- {"angrt", 0x0221F}, // RIGHT ANGLE
- {"angrtvb", 0x022BE}, // RIGHT ANGLE WITH ARC
- {"angrtvbd", 0x0299D}, // MEASURED RIGHT ANGLE WITH DOT
- {"angsph", 0x02222}, // SPHERICAL ANGLE
- {"angst", 0x000C5}, // LATIN CAPITAL LETTER A WITH RING ABOVE
- {"angzarr", 0x0237C}, // RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW
- {"Aogon", 0x00104}, // LATIN CAPITAL LETTER A WITH OGONEK
- {"aogon", 0x00105}, // LATIN SMALL LETTER A WITH OGONEK
- {"Aopf", 0x1D538}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL A
- {"aopf", 0x1D552}, // MATHEMATICAL DOUBLE-STRUCK SMALL A
- {"ap", 0x02248}, // ALMOST EQUAL TO
- {"apacir", 0x02A6F}, // ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT
- {"ape", 0x0224A}, // ALMOST EQUAL OR EQUAL TO
- {"apE", 0x02A70}, // APPROXIMATELY EQUAL OR EQUAL TO
- {"apid", 0x0224B}, // TRIPLE TILDE
- {"apos", 0x00027}, // APOSTROPHE
- {"ApplyFunction", 0x02061}, // FUNCTION APPLICATION
- {"approx", 0x02248}, // ALMOST EQUAL TO
- {"approxeq", 0x0224A}, // ALMOST EQUAL OR EQUAL TO
- {"Aring", 0x000C5}, // LATIN CAPITAL LETTER A WITH RING ABOVE
- {"aring", 0x000E5}, // LATIN SMALL LETTER A WITH RING ABOVE
- {"Ascr", 0x1D49C}, // MATHEMATICAL SCRIPT CAPITAL A
- {"ascr", 0x1D4B6}, // MATHEMATICAL SCRIPT SMALL A
- {"Assign", 0x02254}, // COLON EQUALS
- {"ast", 0x0002A}, // ASTERISK
- {"asymp", 0x02248}, // ALMOST EQUAL TO
- {"asympeq", 0x0224D}, // EQUIVALENT TO
- {"Atilde", 0x000C3}, // LATIN CAPITAL LETTER A WITH TILDE
- {"atilde", 0x000E3}, // LATIN SMALL LETTER A WITH TILDE
- {"Auml", 0x000C4}, // LATIN CAPITAL LETTER A WITH DIAERESIS
- {"auml", 0x000E4}, // LATIN SMALL LETTER A WITH DIAERESIS
- {"awconint", 0x02233}, // ANTICLOCKWISE CONTOUR INTEGRAL
- {"awint", 0x02A11}, // ANTICLOCKWISE INTEGRATION
- {NULL, 0}
-};
-
-static NameId namesB[]={
- {"backcong", 0x0224C}, // ALL EQUAL TO
- {"backepsilon", 0x003F6}, // GREEK REVERSED LUNATE EPSILON SYMBOL
- {"backprime", 0x02035}, // REVERSED PRIME
- {"backsim", 0x0223D}, // REVERSED TILDE
- {"backsimeq", 0x022CD}, // REVERSED TILDE EQUALS
- {"Backslash", 0x02216}, // SET MINUS
-// "b.alpha", 0x1D6C2}, // MATHEMATICAL BOLD SMALL ALPHA
- {"Barv", 0x02AE7}, // SHORT DOWN TACK WITH OVERBAR
- {"barvee", 0x022BD}, // NOR
- {"barwed", 0x02305}, // PROJECTIVE
- {"Barwed", 0x02306}, // PERSPECTIVE
- {"barwedge", 0x02305}, // PROJECTIVE
-// "b.beta", 0x1D6C3}, // MATHEMATICAL BOLD SMALL BETA
- {"bbrk", 0x023B5}, // BOTTOM SQUARE BRACKET
- {"bbrktbrk", 0x023B6}, // BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET
-// "b.chi", 0x1D6D8}, // MATHEMATICAL BOLD SMALL CHI
- {"bcong", 0x0224C}, // ALL EQUAL TO
- {"Bcy", 0x00411}, // CYRILLIC CAPITAL LETTER BE
- {"bcy", 0x00431}, // CYRILLIC SMALL LETTER BE
-// "b.Delta", 0x1D6AB}, // MATHEMATICAL BOLD CAPITAL DELTA
-// "b.delta", 0x1D6C5}, // MATHEMATICAL BOLD SMALL DELTA
- {"bdquo", 0x0201E}, // DOUBLE LOW-9 QUOTATION MARK
- {"becaus", 0x02235}, // BECAUSE
- {"because", 0x02235}, // BECAUSE
- {"Because", 0x02235}, // BECAUSE
- {"bemptyv", 0x029B0}, // REVERSED EMPTY SET
- {"bepsi", 0x003F6}, // GREEK REVERSED LUNATE EPSILON SYMBOL
-// "b.epsi", 0x1D6C6}, // MATHEMATICAL BOLD SMALL EPSILON
-// "b.epsiv", 0x1D6DC}, // MATHEMATICAL BOLD EPSILON SYMBOL
- {"bernou", 0x0212C}, // SCRIPT CAPITAL B
- {"Bernoullis", 0x0212C}, // SCRIPT CAPITAL B
- {"Beta", 0x00392}, // GREEK CAPITAL LETTER BETA
- {"beta", 0x003B2}, // GREEK SMALL LETTER BETA
-// "b.eta", 0x1D6C8}, // MATHEMATICAL BOLD SMALL ETA
- {"beth", 0x02136}, // BET SYMBOL
- {"between", 0x0226C}, // BETWEEN
- {"Bfr", 0x1D505}, // MATHEMATICAL FRAKTUR CAPITAL B
- {"bfr", 0x1D51F}, // MATHEMATICAL FRAKTUR SMALL B
-// "b.Gamma", 0x1D6AA}, // MATHEMATICAL BOLD CAPITAL GAMMA
-// "b.gamma", 0x1D6C4}, // MATHEMATICAL BOLD SMALL GAMMA
-// "b.Gammad", 0x1D7CA}, // MATHEMATICAL BOLD CAPITAL DIGAMMA
-// "b.gammad", 0x1D7CB}, // MATHEMATICAL BOLD SMALL DIGAMMA
- {"Bgr", 0x00392}, // GREEK CAPITAL LETTER BETA
- {"bgr", 0x003B2}, // GREEK SMALL LETTER BETA
- {"bigcap", 0x022C2}, // N-ARY INTERSECTION
- {"bigcirc", 0x025EF}, // LARGE CIRCLE
- {"bigcup", 0x022C3}, // N-ARY UNION
- {"bigodot", 0x02A00}, // N-ARY CIRCLED DOT OPERATOR
- {"bigoplus", 0x02A01}, // N-ARY CIRCLED PLUS OPERATOR
- {"bigotimes", 0x02A02}, // N-ARY CIRCLED TIMES OPERATOR
- {"bigsqcup", 0x02A06}, // N-ARY SQUARE UNION OPERATOR
- {"bigstar", 0x02605}, // BLACK STAR
- {"bigtriangledown", 0x025BD}, // WHITE DOWN-POINTING TRIANGLE
- {"bigtriangleup", 0x025B3}, // WHITE UP-POINTING TRIANGLE
- {"biguplus", 0x02A04}, // N-ARY UNION OPERATOR WITH PLUS
- {"bigvee", 0x022C1}, // N-ARY LOGICAL OR
- {"bigwedge", 0x022C0}, // N-ARY LOGICAL AND
-// "b.iota", 0x1D6CA}, // MATHEMATICAL BOLD SMALL IOTA
-// "b.kappa", 0x1D6CB}, // MATHEMATICAL BOLD SMALL KAPPA
-// "b.kappav", 0x1D6DE}, // MATHEMATICAL BOLD KAPPA SYMBOL
- {"bkarow", 0x0290D}, // RIGHTWARDS DOUBLE DASH ARROW
- {"blacklozenge", 0x029EB}, // BLACK LOZENGE
- {"blacksquare", 0x025AA}, // BLACK SMALL SQUARE
- {"blacktriangle", 0x025B4}, // BLACK UP-POINTING SMALL TRIANGLE
- {"blacktriangledown", 0x025BE}, // BLACK DOWN-POINTING SMALL TRIANGLE
- {"blacktriangleleft", 0x025C2}, // BLACK LEFT-POINTING SMALL TRIANGLE
- {"blacktriangleright", 0x025B8}, // BLACK RIGHT-POINTING SMALL TRIANGLE
-// "b.Lambda", 0x1D6B2}, // MATHEMATICAL BOLD CAPITAL LAMDA
-// "b.lambda", 0x1D6CC}, // MATHEMATICAL BOLD SMALL LAMDA
- {"blank", 0x02423}, // OPEN BOX
- {"blk12", 0x02592}, // MEDIUM SHADE
- {"blk14", 0x02591}, // LIGHT SHADE
- {"blk34", 0x02593}, // DARK SHADE
- {"block", 0x02588}, // FULL BLOCK
-// "b.mu", 0x1D6CD}, // MATHEMATICAL BOLD SMALL MU
-// "bne", 0x0003D;0x020E5}, // EQUALS SIGN with reverse slash
-// "bnequiv", 0x02261;0x020E5}, // IDENTICAL TO with reverse slash
- {"bnot", 0x02310}, // REVERSED NOT SIGN
- {"bNot", 0x02AED}, // REVERSED DOUBLE STROKE NOT SIGN
-// "b.nu", 0x1D6CE}, // MATHEMATICAL BOLD SMALL NU
-// "b.Omega", 0x1D6C0}, // MATHEMATICAL BOLD CAPITAL OMEGA
-// "b.omega", 0x1D6DA}, // MATHEMATICAL BOLD SMALL OMEGA
- {"Bopf", 0x1D539}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL B
- {"bopf", 0x1D553}, // MATHEMATICAL DOUBLE-STRUCK SMALL B
- {"bot", 0x022A5}, // UP TACK
- {"bottom", 0x022A5}, // UP TACK
- {"bowtie", 0x022C8}, // BOWTIE
- {"boxbox", 0x029C9}, // TWO JOINED SQUARES
- {"boxdl", 0x02510}, // BOX DRAWINGS LIGHT DOWN AND LEFT
- {"boxdL", 0x02555}, // BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
- {"boxDl", 0x02556}, // BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
- {"boxDL", 0x02557}, // BOX DRAWINGS DOUBLE DOWN AND LEFT
- {"boxdr", 0x0250C}, // BOX DRAWINGS LIGHT DOWN AND RIGHT
- {"boxdR", 0x02552}, // BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
- {"boxDr", 0x02553}, // BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
- {"boxDR", 0x02554}, // BOX DRAWINGS DOUBLE DOWN AND RIGHT
- {"boxh", 0x02500}, // BOX DRAWINGS LIGHT HORIZONTAL
- {"boxH", 0x02550}, // BOX DRAWINGS DOUBLE HORIZONTAL
- {"boxhd", 0x0252C}, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
- {"boxHd", 0x02564}, // BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
- {"boxhD", 0x02565}, // BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
- {"boxHD", 0x02566}, // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
- {"boxhu", 0x02534}, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
- {"boxHu", 0x02567}, // BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
- {"boxhU", 0x02568}, // BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
- {"boxHU", 0x02569}, // BOX DRAWINGS DOUBLE UP AND HORIZONTAL
- {"boxminus", 0x0229F}, // SQUARED MINUS
- {"boxplus", 0x0229E}, // SQUARED PLUS
- {"boxtimes", 0x022A0}, // SQUARED TIMES
- {"boxul", 0x02518}, // BOX DRAWINGS LIGHT UP AND LEFT
- {"boxuL", 0x0255B}, // BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
- {"boxUl", 0x0255C}, // BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
- {"boxUL", 0x0255D}, // BOX DRAWINGS DOUBLE UP AND LEFT
- {"boxur", 0x02514}, // BOX DRAWINGS LIGHT UP AND RIGHT
- {"boxuR", 0x02558}, // BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
- {"boxUr", 0x02559}, // BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
- {"boxUR", 0x0255A}, // BOX DRAWINGS DOUBLE UP AND RIGHT
- {"boxv", 0x02502}, // BOX DRAWINGS LIGHT VERTICAL
- {"boxV", 0x02551}, // BOX DRAWINGS DOUBLE VERTICAL
- {"boxvh", 0x0253C}, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
- {"boxvH", 0x0256A}, // BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
- {"boxVh", 0x0256B}, // BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
- {"boxVH", 0x0256C}, // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
- {"boxvl", 0x02524}, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
- {"boxvL", 0x02561}, // BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
- {"boxVl", 0x02562}, // BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
- {"boxVL", 0x02563}, // BOX DRAWINGS DOUBLE VERTICAL AND LEFT
- {"boxvr", 0x0251C}, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
- {"boxvR", 0x0255E}, // BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
- {"boxVr", 0x0255F}, // BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
- {"boxVR", 0x02560}, // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
-// "b.Phi", 0x1D6BD}, // MATHEMATICAL BOLD CAPITAL PHI
-// "b.phi", 0x1D6D7}, // MATHEMATICAL BOLD SMALL PHI
-// "b.phiv", 0x1D6DF}, // MATHEMATICAL BOLD PHI SYMBOL
-// "b.Pi", 0x1D6B7}, // MATHEMATICAL BOLD CAPITAL PI
-// "b.pi", 0x1D6D1}, // MATHEMATICAL BOLD SMALL PI
-// "b.piv", 0x1D6E1}, // MATHEMATICAL BOLD PI SYMBOL
- {"bprime", 0x02035}, // REVERSED PRIME
-// "b.Psi", 0x1D6BF}, // MATHEMATICAL BOLD CAPITAL PSI
-// "b.psi", 0x1D6D9}, // MATHEMATICAL BOLD SMALL PSI
- {"breve", 0x002D8}, // BREVE
- {"Breve", 0x002D8}, // BREVE
-// "b.rho", 0x1D6D2}, // MATHEMATICAL BOLD SMALL RHO
-// "b.rhov", 0x1D6E0}, // MATHEMATICAL BOLD RHO SYMBOL
- {"brvbar", 0x000A6}, // BROKEN BAR
- {"Bscr", 0x0212C}, // SCRIPT CAPITAL B
- {"bscr", 0x1D4B7}, // MATHEMATICAL SCRIPT SMALL B
- {"bsemi", 0x0204F}, // REVERSED SEMICOLON
-// "b.Sigma", 0x1D6BA}, // MATHEMATICAL BOLD CAPITAL SIGMA
-// "b.sigma", 0x1D6D4}, // MATHEMATICAL BOLD SMALL SIGMA
-// "b.sigmav", 0x1D6D3}, // MATHEMATICAL BOLD SMALL FINAL SIGMA
- {"bsim", 0x0223D}, // REVERSED TILDE
- {"bsime", 0x022CD}, // REVERSED TILDE EQUALS
- {"bsol", 0x0005C}, // REVERSE SOLIDUS
- {"bsolb", 0x029C5}, // SQUARED FALLING DIAGONAL SLASH
- {"bsolhsub", 0x027C8}, // REVERSE SOLIDUS PRECEDING SUBSET
-// "b.tau", 0x1D6D5}, // MATHEMATICAL BOLD SMALL TAU
-// "b.Theta", 0x1D6AF}, // MATHEMATICAL BOLD CAPITAL THETA
-// "b.thetas", 0x1D6C9}, // MATHEMATICAL BOLD SMALL THETA
-// "b.thetav", 0x1D6DD}, // MATHEMATICAL BOLD THETA SYMBOL
- {"bull", 0x02022}, // BULLET
- {"bullet", 0x02022}, // BULLET
- {"bump", 0x0224E}, // GEOMETRICALLY EQUIVALENT TO
- {"bumpe", 0x0224F}, // DIFFERENCE BETWEEN
- {"bumpE", 0x02AAE}, // EQUALS SIGN WITH BUMPY ABOVE
- {"Bumpeq", 0x0224E}, // GEOMETRICALLY EQUIVALENT TO
- {"bumpeq", 0x0224F}, // DIFFERENCE BETWEEN
-// "b.Upsi", 0x1D6BC}, // MATHEMATICAL BOLD CAPITAL UPSILON
-// "b.upsi", 0x1D6D6}, // MATHEMATICAL BOLD SMALL UPSILON
-// "b.Xi", 0x1D6B5}, // MATHEMATICAL BOLD CAPITAL XI
-// "b.xi", 0x1D6CF}, // MATHEMATICAL BOLD SMALL XI
-// "b.zeta", 0x1D6C7}, // MATHEMATICAL BOLD SMALL ZETA
- {NULL, 0}
-};
-
-static NameId namesC[]={
- {"Cacute", 0x00106}, // LATIN CAPITAL LETTER C WITH ACUTE
- {"cacute", 0x00107}, // LATIN SMALL LETTER C WITH ACUTE
- {"cap", 0x02229}, // INTERSECTION
- {"Cap", 0x022D2}, // DOUBLE INTERSECTION
- {"capand", 0x02A44}, // INTERSECTION WITH LOGICAL AND
- {"capbrcup", 0x02A49}, // INTERSECTION ABOVE BAR ABOVE UNION
- {"capcap", 0x02A4B}, // INTERSECTION BESIDE AND JOINED WITH INTERSECTION
- {"capcup", 0x02A47}, // INTERSECTION ABOVE UNION
- {"capdot", 0x02A40}, // INTERSECTION WITH DOT
- {"CapitalDifferentialD", 0x02145}, // DOUBLE-STRUCK ITALIC CAPITAL D
-// "caps", 0x02229;0x0FE00}, // INTERSECTION with serifs
- {"caret", 0x02041}, // CARET INSERTION POINT
- {"caron", 0x002C7}, // CARON
- {"Cayleys", 0x0212D}, // BLACK-LETTER CAPITAL C
- {"ccaps", 0x02A4D}, // CLOSED INTERSECTION WITH SERIFS
- {"Ccaron", 0x0010C}, // LATIN CAPITAL LETTER C WITH CARON
- {"ccaron", 0x0010D}, // LATIN SMALL LETTER C WITH CARON
- {"Ccedil", 0x000C7}, // LATIN CAPITAL LETTER C WITH CEDILLA
- {"ccedil", 0x000E7}, // LATIN SMALL LETTER C WITH CEDILLA
- {"Ccirc", 0x00108}, // LATIN CAPITAL LETTER C WITH CIRCUMFLEX
- {"ccirc", 0x00109}, // LATIN SMALL LETTER C WITH CIRCUMFLEX
- {"Cconint", 0x02230}, // VOLUME INTEGRAL
- {"ccups", 0x02A4C}, // CLOSED UNION WITH SERIFS
- {"ccupssm", 0x02A50}, // CLOSED UNION WITH SERIFS AND SMASH PRODUCT
- {"Cdot", 0x0010A}, // LATIN CAPITAL LETTER C WITH DOT ABOVE
- {"cdot", 0x0010B}, // LATIN SMALL LETTER C WITH DOT ABOVE
- {"cedil", 0x000B8}, // CEDILLA
- {"Cedilla", 0x000B8}, // CEDILLA
- {"cemptyv", 0x029B2}, // EMPTY SET WITH SMALL CIRCLE ABOVE
- {"cent", 0x000A2}, // CENT SIGN
- {"centerdot", 0x000B7}, // MIDDLE DOT
- {"CenterDot", 0x000B7}, // MIDDLE DOT
- {"Cfr", 0x0212D}, // BLACK-LETTER CAPITAL C
- {"cfr", 0x1D520}, // MATHEMATICAL FRAKTUR SMALL C
- {"CHcy", 0x00427}, // CYRILLIC CAPITAL LETTER CHE
- {"chcy", 0x00447}, // CYRILLIC SMALL LETTER CHE
- {"check", 0x02713}, // CHECK MARK
- {"checkmark", 0x02713}, // CHECK MARK
- {"Chi", 0x003A7}, // GREEK CAPITAL LETTER CHI
- {"chi", 0x003C7}, // GREEK SMALL LETTER CHI
- {"cir", 0x025CB}, // WHITE CIRCLE
- {"circ", 0x002C6}, // MODIFIER LETTER CIRCUMFLEX ACCENT
- {"circeq", 0x02257}, // RING EQUAL TO
- {"circlearrowleft", 0x021BA}, // ANTICLOCKWISE OPEN CIRCLE ARROW
- {"circlearrowright", 0x021BB}, // CLOCKWISE OPEN CIRCLE ARROW
- {"circledast", 0x0229B}, // CIRCLED ASTERISK OPERATOR
- {"circledcirc", 0x0229A}, // CIRCLED RING OPERATOR
- {"circleddash", 0x0229D}, // CIRCLED DASH
- {"CircleDot", 0x02299}, // CIRCLED DOT OPERATOR
- {"circledR", 0x000AE}, // REGISTERED SIGN
- {"circledS", 0x024C8}, // CIRCLED LATIN CAPITAL LETTER S
- {"CircleMinus", 0x02296}, // CIRCLED MINUS
- {"CirclePlus", 0x02295}, // CIRCLED PLUS
- {"CircleTimes", 0x02297}, // CIRCLED TIMES
- {"cire", 0x02257}, // RING EQUAL TO
- {"cirE", 0x029C3}, // CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT
- {"cirfnint", 0x02A10}, // CIRCULATION FUNCTION
- {"cirmid", 0x02AEF}, // VERTICAL LINE WITH CIRCLE ABOVE
- {"cirscir", 0x029C2}, // CIRCLE WITH SMALL CIRCLE TO THE RIGHT
- {"ClockwiseContourIntegral", 0x02232}, // CLOCKWISE CONTOUR INTEGRAL
- {"CloseCurlyDoubleQuote", 0x0201D}, // RIGHT DOUBLE QUOTATION MARK
- {"CloseCurlyQuote", 0x02019}, // RIGHT SINGLE QUOTATION MARK
- {"clubs", 0x02663}, // BLACK CLUB SUIT
- {"clubsuit", 0x02663}, // BLACK CLUB SUIT
- {"colon", 0x0003A}, // COLON
- {"Colon", 0x02237}, // PROPORTION
- {"colone", 0x02254}, // COLON EQUALS
- {"Colone", 0x02A74}, // DOUBLE COLON EQUAL
- {"coloneq", 0x02254}, // COLON EQUALS
- {"comma", 0x0002C}, // COMMA
- {"commat", 0x00040}, // COMMERCIAL AT
- {"comp", 0x02201}, // COMPLEMENT
- {"compfn", 0x02218}, // RING OPERATOR
- {"complement", 0x02201}, // COMPLEMENT
- {"complexes", 0x02102}, // DOUBLE-STRUCK CAPITAL C
- {"cong", 0x02245}, // APPROXIMATELY EQUAL TO
- {"congdot", 0x02A6D}, // CONGRUENT WITH DOT ABOVE
- {"Congruent", 0x02261}, // IDENTICAL TO
- {"conint", 0x0222E}, // CONTOUR INTEGRAL
- {"Conint", 0x0222F}, // SURFACE INTEGRAL
- {"ContourIntegral", 0x0222E}, // CONTOUR INTEGRAL
- {"Copf", 0x02102}, // DOUBLE-STRUCK CAPITAL C
- {"copf", 0x1D554}, // MATHEMATICAL DOUBLE-STRUCK SMALL C
- {"coprod", 0x02210}, // N-ARY COPRODUCT
- {"Coproduct", 0x02210}, // N-ARY COPRODUCT
- {"copy", 0x000A9}, // COPYRIGHT SIGN
- {"COPY", 0x000A9}, // COPYRIGHT SIGN
- {"copysr", 0x02117}, // SOUND RECORDING COPYRIGHT
- {"CounterClockwiseContourIntegral", 0x02233}, // ANTICLOCKWISE CONTOUR INTEGRAL
- {"crarr", 0x021B5}, // DOWNWARDS ARROW WITH CORNER LEFTWARDS
- {"cross", 0x02717}, // BALLOT X
- {"Cross", 0x02A2F}, // VECTOR OR CROSS PRODUCT
- {"Cscr", 0x1D49E}, // MATHEMATICAL SCRIPT CAPITAL C
- {"cscr", 0x1D4B8}, // MATHEMATICAL SCRIPT SMALL C
- {"csub", 0x02ACF}, // CLOSED SUBSET
- {"csube", 0x02AD1}, // CLOSED SUBSET OR EQUAL TO
- {"csup", 0x02AD0}, // CLOSED SUPERSET
- {"csupe", 0x02AD2}, // CLOSED SUPERSET OR EQUAL TO
- {"ctdot", 0x022EF}, // MIDLINE HORIZONTAL ELLIPSIS
- {"cudarrl", 0x02938}, // RIGHT-SIDE ARC CLOCKWISE ARROW
- {"cudarrr", 0x02935}, // ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS
- {"cuepr", 0x022DE}, // EQUAL TO OR PRECEDES
- {"cuesc", 0x022DF}, // EQUAL TO OR SUCCEEDS
- {"cularr", 0x021B6}, // ANTICLOCKWISE TOP SEMICIRCLE ARROW
- {"cularrp", 0x0293D}, // TOP ARC ANTICLOCKWISE ARROW WITH PLUS
- {"cup", 0x0222A}, // UNION
- {"Cup", 0x022D3}, // DOUBLE UNION
- {"cupbrcap", 0x02A48}, // UNION ABOVE BAR ABOVE INTERSECTION
- {"CupCap", 0x0224D}, // EQUIVALENT TO
- {"cupcap", 0x02A46}, // UNION ABOVE INTERSECTION
- {"cupcup", 0x02A4A}, // UNION BESIDE AND JOINED WITH UNION
- {"cupdot", 0x0228D}, // MULTISET MULTIPLICATION
- {"cupor", 0x02A45}, // UNION WITH LOGICAL OR
-// "cups", 0x0222A;0x0FE00}, // UNION with serifs
- {"curarr", 0x021B7}, // CLOCKWISE TOP SEMICIRCLE ARROW
- {"curarrm", 0x0293C}, // TOP ARC CLOCKWISE ARROW WITH MINUS
- {"curlyeqprec", 0x022DE}, // EQUAL TO OR PRECEDES
- {"curlyeqsucc", 0x022DF}, // EQUAL TO OR SUCCEEDS
- {"curlyvee", 0x022CE}, // CURLY LOGICAL OR
- {"curlywedge", 0x022CF}, // CURLY LOGICAL AND
- {"curren", 0x000A4}, // CURRENCY SIGN
- {"curvearrowleft", 0x021B6}, // ANTICLOCKWISE TOP SEMICIRCLE ARROW
- {"curvearrowright", 0x021B7}, // CLOCKWISE TOP SEMICIRCLE ARROW
- {"cuvee", 0x022CE}, // CURLY LOGICAL OR
- {"cuwed", 0x022CF}, // CURLY LOGICAL AND
- {"cwconint", 0x02232}, // CLOCKWISE CONTOUR INTEGRAL
- {"cwint", 0x02231}, // CLOCKWISE INTEGRAL
- {"cylcty", 0x0232D}, // CYLINDRICITY
- {NULL, 0}
-};
-
-static NameId namesD[]={
- {"dagger", 0x02020}, // DAGGER
- {"Dagger", 0x02021}, // DOUBLE DAGGER
- {"daleth", 0x02138}, // DALET SYMBOL
- {"darr", 0x02193}, // DOWNWARDS ARROW
- {"Darr", 0x021A1}, // DOWNWARDS TWO HEADED ARROW
- {"dArr", 0x021D3}, // DOWNWARDS DOUBLE ARROW
- {"dash", 0x02010}, // HYPHEN
- {"dashv", 0x022A3}, // LEFT TACK
- {"Dashv", 0x02AE4}, // VERTICAL BAR DOUBLE LEFT TURNSTILE
- {"dbkarow", 0x0290F}, // RIGHTWARDS TRIPLE DASH ARROW
- {"dblac", 0x002DD}, // DOUBLE ACUTE ACCENT
- {"Dcaron", 0x0010E}, // LATIN CAPITAL LETTER D WITH CARON
- {"dcaron", 0x0010F}, // LATIN SMALL LETTER D WITH CARON
- {"Dcy", 0x00414}, // CYRILLIC CAPITAL LETTER DE
- {"dcy", 0x00434}, // CYRILLIC SMALL LETTER DE
- {"DD", 0x02145}, // DOUBLE-STRUCK ITALIC CAPITAL D
- {"dd", 0x02146}, // DOUBLE-STRUCK ITALIC SMALL D
- {"ddagger", 0x02021}, // DOUBLE DAGGER
- {"ddarr", 0x021CA}, // DOWNWARDS PAIRED ARROWS
- {"DDotrahd", 0x02911}, // RIGHTWARDS ARROW WITH DOTTED STEM
- {"ddotseq", 0x02A77}, // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW
- {"deg", 0x000B0}, // DEGREE SIGN
- {"Del", 0x02207}, // NABLA
- {"Delta", 0x00394}, // GREEK CAPITAL LETTER DELTA
- {"delta", 0x003B4}, // GREEK SMALL LETTER DELTA
- {"demptyv", 0x029B1}, // EMPTY SET WITH OVERBAR
- {"dfisht", 0x0297F}, // DOWN FISH TAIL
- {"Dfr", 0x1D507}, // MATHEMATICAL FRAKTUR CAPITAL D
- {"dfr", 0x1D521}, // MATHEMATICAL FRAKTUR SMALL D
- {"Dgr", 0x00394}, // GREEK CAPITAL LETTER DELTA
- {"dgr", 0x003B4}, // GREEK SMALL LETTER DELTA
- {"dHar", 0x02965}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
- {"dharl", 0x021C3}, // DOWNWARDS HARPOON WITH BARB LEFTWARDS
- {"dharr", 0x021C2}, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
- {"DiacriticalAcute", 0x000B4}, // ACUTE ACCENT
- {"DiacriticalDot", 0x002D9}, // DOT ABOVE
- {"DiacriticalDoubleAcute", 0x002DD}, // DOUBLE ACUTE ACCENT
- {"DiacriticalGrave", 0x00060}, // GRAVE ACCENT
- {"DiacriticalTilde", 0x002DC}, // SMALL TILDE
- {"diam", 0x022C4}, // DIAMOND OPERATOR
- {"diamond", 0x022C4}, // DIAMOND OPERATOR
- {"Diamond", 0x022C4}, // DIAMOND OPERATOR
- {"diamondsuit", 0x02666}, // BLACK DIAMOND SUIT
- {"diams", 0x02666}, // BLACK DIAMOND SUIT
- {"die", 0x000A8}, // DIAERESIS
- {"DifferentialD", 0x02146}, // DOUBLE-STRUCK ITALIC SMALL D
- {"digamma", 0x003DD}, // GREEK SMALL LETTER DIGAMMA
- {"disin", 0x022F2}, // ELEMENT OF WITH LONG HORIZONTAL STROKE
- {"div", 0x000F7}, // DIVISION SIGN
- {"divide", 0x000F7}, // DIVISION SIGN
- {"divideontimes", 0x022C7}, // DIVISION TIMES
- {"divonx", 0x022C7}, // DIVISION TIMES
- {"DJcy", 0x00402}, // CYRILLIC CAPITAL LETTER DJE
- {"djcy", 0x00452}, // CYRILLIC SMALL LETTER DJE
- {"dlcorn", 0x0231E}, // BOTTOM LEFT CORNER
- {"dlcrop", 0x0230D}, // BOTTOM LEFT CROP
- {"dollar", 0x00024}, // DOLLAR SIGN
- {"Dopf", 0x1D53B}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL D
- {"dopf", 0x1D555}, // MATHEMATICAL DOUBLE-STRUCK SMALL D
- {"Dot", 0x000A8}, // DIAERESIS
- {"dot", 0x002D9}, // DOT ABOVE
- {"DotDot", 0x020DC}, // COMBINING FOUR DOTS ABOVE
- {"doteq", 0x02250}, // APPROACHES THE LIMIT
- {"doteqdot", 0x02251}, // GEOMETRICALLY EQUAL TO
- {"DotEqual", 0x02250}, // APPROACHES THE LIMIT
- {"dotminus", 0x02238}, // DOT MINUS
- {"dotplus", 0x02214}, // DOT PLUS
- {"dotsquare", 0x022A1}, // SQUARED DOT OPERATOR
- {"doublebarwedge", 0x02306}, // PERSPECTIVE
- {"DoubleContourIntegral", 0x0222F}, // SURFACE INTEGRAL
- {"DoubleDot", 0x000A8}, // DIAERESIS
- {"DoubleDownArrow", 0x021D3}, // DOWNWARDS DOUBLE ARROW
- {"DoubleLeftArrow", 0x021D0}, // LEFTWARDS DOUBLE ARROW
- {"DoubleLeftRightArrow", 0x021D4}, // LEFT RIGHT DOUBLE ARROW
- {"DoubleLeftTee", 0x02AE4}, // VERTICAL BAR DOUBLE LEFT TURNSTILE
- {"DoubleLongLeftArrow", 0x027F8}, // LONG LEFTWARDS DOUBLE ARROW
- {"DoubleLongLeftRightArrow", 0x027FA}, // LONG LEFT RIGHT DOUBLE ARROW
- {"DoubleLongRightArrow", 0x027F9}, // LONG RIGHTWARDS DOUBLE ARROW
- {"DoubleRightArrow", 0x021D2}, // RIGHTWARDS DOUBLE ARROW
- {"DoubleRightTee", 0x022A8}, // TRUE
- {"DoubleUpArrow", 0x021D1}, // UPWARDS DOUBLE ARROW
- {"DoubleUpDownArrow", 0x021D5}, // UP DOWN DOUBLE ARROW
- {"DoubleVerticalBar", 0x02225}, // PARALLEL TO
- {"downarrow", 0x02193}, // DOWNWARDS ARROW
- {"DownArrow", 0x02193}, // DOWNWARDS ARROW
- {"Downarrow", 0x021D3}, // DOWNWARDS DOUBLE ARROW
- {"DownArrowBar", 0x02913}, // DOWNWARDS ARROW TO BAR
- {"DownArrowUpArrow", 0x021F5}, // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW
- {"DownBreve", 0x00311}, // COMBINING INVERTED BREVE
- {"downdownarrows", 0x021CA}, // DOWNWARDS PAIRED ARROWS
- {"downharpoonleft", 0x021C3}, // DOWNWARDS HARPOON WITH BARB LEFTWARDS
- {"downharpoonright", 0x021C2}, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
- {"DownLeftRightVector", 0x02950}, // LEFT BARB DOWN RIGHT BARB DOWN HARPOON
- {"DownLeftTeeVector", 0x0295E}, // LEFTWARDS HARPOON WITH BARB DOWN FROM BAR
- {"DownLeftVector", 0x021BD}, // LEFTWARDS HARPOON WITH BARB DOWNWARDS
- {"DownLeftVectorBar", 0x02956}, // LEFTWARDS HARPOON WITH BARB DOWN TO BAR
- {"DownRightTeeVector", 0x0295F}, // RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR
- {"DownRightVector", 0x021C1}, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
- {"DownRightVectorBar", 0x02957}, // RIGHTWARDS HARPOON WITH BARB DOWN TO BAR
- {"DownTee", 0x022A4}, // DOWN TACK
- {"DownTeeArrow", 0x021A7}, // DOWNWARDS ARROW FROM BAR
- {"drbkarow", 0x02910}, // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW
- {"drcorn", 0x0231F}, // BOTTOM RIGHT CORNER
- {"drcrop", 0x0230C}, // BOTTOM RIGHT CROP
- {"Dscr", 0x1D49F}, // MATHEMATICAL SCRIPT CAPITAL D
- {"dscr", 0x1D4B9}, // MATHEMATICAL SCRIPT SMALL D
- {"DScy", 0x00405}, // CYRILLIC CAPITAL LETTER DZE
- {"dscy", 0x00455}, // CYRILLIC SMALL LETTER DZE
- {"dsol", 0x029F6}, // SOLIDUS WITH OVERBAR
- {"Dstrok", 0x00110}, // LATIN CAPITAL LETTER D WITH STROKE
- {"dstrok", 0x00111}, // LATIN SMALL LETTER D WITH STROKE
- {"dtdot", 0x022F1}, // DOWN RIGHT DIAGONAL ELLIPSIS
- {"dtri", 0x025BF}, // WHITE DOWN-POINTING SMALL TRIANGLE
- {"dtrif", 0x025BE}, // BLACK DOWN-POINTING SMALL TRIANGLE
- {"duarr", 0x021F5}, // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW
- {"duhar", 0x0296F}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
- {"dwangle", 0x029A6}, // OBLIQUE ANGLE OPENING UP
- {"DZcy", 0x0040F}, // CYRILLIC CAPITAL LETTER DZHE
- {"dzcy", 0x0045F}, // CYRILLIC SMALL LETTER DZHE
- {"dzigrarr", 0x027FF}, // LONG RIGHTWARDS SQUIGGLE ARROW
- {NULL, 0}
-};
-
-static NameId namesE[]={
- {"Eacgr", 0x00388}, // GREEK CAPITAL LETTER EPSILON WITH TONOS
- {"eacgr", 0x003AD}, // GREEK SMALL LETTER EPSILON WITH TONOS
- {"Eacute", 0x000C9}, // LATIN CAPITAL LETTER E WITH ACUTE
- {"eacute", 0x000E9}, // LATIN SMALL LETTER E WITH ACUTE
- {"easter", 0x02A6E}, // EQUALS WITH ASTERISK
- {"Ecaron", 0x0011A}, // LATIN CAPITAL LETTER E WITH CARON
- {"ecaron", 0x0011B}, // LATIN SMALL LETTER E WITH CARON
- {"ecir", 0x02256}, // RING IN EQUAL TO
- {"Ecirc", 0x000CA}, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
- {"ecirc", 0x000EA}, // LATIN SMALL LETTER E WITH CIRCUMFLEX
- {"ecolon", 0x02255}, // EQUALS COLON
- {"Ecy", 0x0042D}, // CYRILLIC CAPITAL LETTER E
- {"ecy", 0x0044D}, // CYRILLIC SMALL LETTER E
- {"eDDot", 0x02A77}, // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW
- {"Edot", 0x00116}, // LATIN CAPITAL LETTER E WITH DOT ABOVE
- {"edot", 0x00117}, // LATIN SMALL LETTER E WITH DOT ABOVE
- {"eDot", 0x02251}, // GEOMETRICALLY EQUAL TO
- {"ee", 0x02147}, // DOUBLE-STRUCK ITALIC SMALL E
- {"EEacgr", 0x00389}, // GREEK CAPITAL LETTER ETA WITH TONOS
- {"eeacgr", 0x003AE}, // GREEK SMALL LETTER ETA WITH TONOS
- {"EEgr", 0x00397}, // GREEK CAPITAL LETTER ETA
- {"eegr", 0x003B7}, // GREEK SMALL LETTER ETA
- {"efDot", 0x02252}, // APPROXIMATELY EQUAL TO OR THE IMAGE OF
- {"Efr", 0x1D508}, // MATHEMATICAL FRAKTUR CAPITAL E
- {"efr", 0x1D522}, // MATHEMATICAL FRAKTUR SMALL E
- {"eg", 0x02A9A}, // DOUBLE-LINE EQUAL TO OR GREATER-THAN
- {"Egr", 0x00395}, // GREEK CAPITAL LETTER EPSILON
- {"egr", 0x003B5}, // GREEK SMALL LETTER EPSILON
- {"Egrave", 0x000C8}, // LATIN CAPITAL LETTER E WITH GRAVE
- {"egrave", 0x000E8}, // LATIN SMALL LETTER E WITH GRAVE
- {"egs", 0x02A96}, // SLANTED EQUAL TO OR GREATER-THAN
- {"egsdot", 0x02A98}, // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE
- {"el", 0x02A99}, // DOUBLE-LINE EQUAL TO OR LESS-THAN
- {"Element", 0x02208}, // ELEMENT OF
- {"elinters", 0x023E7}, // ELECTRICAL INTERSECTION
- {"ell", 0x02113}, // SCRIPT SMALL L
- {"els", 0x02A95}, // SLANTED EQUAL TO OR LESS-THAN
- {"elsdot", 0x02A97}, // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE
- {"Emacr", 0x00112}, // LATIN CAPITAL LETTER E WITH MACRON
- {"emacr", 0x00113}, // LATIN SMALL LETTER E WITH MACRON
- {"empty", 0x02205}, // EMPTY SET
- {"emptyset", 0x02205}, // EMPTY SET
- {"EmptySmallSquare", 0x025FB}, // WHITE MEDIUM SQUARE
- {"emptyv", 0x02205}, // EMPTY SET
- {"EmptyVerySmallSquare", 0x025AB}, // WHITE SMALL SQUARE
- {"emsp", 0x02003}, // EM SPACE
- {"emsp13", 0x02004}, // THREE-PER-EM SPACE
- {"emsp14", 0x02005}, // FOUR-PER-EM SPACE
- {"ENG", 0x0014A}, // LATIN CAPITAL LETTER ENG
- {"eng", 0x0014B}, // LATIN SMALL LETTER ENG
- {"ensp", 0x02002}, // EN SPACE
- {"Eogon", 0x00118}, // LATIN CAPITAL LETTER E WITH OGONEK
- {"eogon", 0x00119}, // LATIN SMALL LETTER E WITH OGONEK
- {"Eopf", 0x1D53C}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL E
- {"eopf", 0x1D556}, // MATHEMATICAL DOUBLE-STRUCK SMALL E
- {"epar", 0x022D5}, // EQUAL AND PARALLEL TO
- {"eparsl", 0x029E3}, // EQUALS SIGN AND SLANTED PARALLEL
- {"eplus", 0x02A71}, // EQUALS SIGN ABOVE PLUS SIGN
- {"epsi", 0x003B5}, // GREEK SMALL LETTER EPSILON
- {"Epsilon", 0x00395}, // GREEK CAPITAL LETTER EPSILON
- {"epsilon", 0x003B5}, // GREEK SMALL LETTER EPSILON
- {"epsiv", 0x003F5}, // GREEK LUNATE EPSILON SYMBOL
- {"eqcirc", 0x02256}, // RING IN EQUAL TO
- {"eqcolon", 0x02255}, // EQUALS COLON
- {"eqsim", 0x02242}, // MINUS TILDE
- {"eqslantgtr", 0x02A96}, // SLANTED EQUAL TO OR GREATER-THAN
- {"eqslantless", 0x02A95}, // SLANTED EQUAL TO OR LESS-THAN
- {"Equal", 0x02A75}, // TWO CONSECUTIVE EQUALS SIGNS
- {"equals", 0x0003D}, // EQUALS SIGN
- {"EqualTilde", 0x02242}, // MINUS TILDE
- {"equest", 0x0225F}, // QUESTIONED EQUAL TO
- {"Equilibrium", 0x021CC}, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
- {"equiv", 0x02261}, // IDENTICAL TO
- {"equivDD", 0x02A78}, // EQUIVALENT WITH FOUR DOTS ABOVE
- {"eqvparsl", 0x029E5}, // IDENTICAL TO AND SLANTED PARALLEL
- {"erarr", 0x02971}, // EQUALS SIGN ABOVE RIGHTWARDS ARROW
- {"erDot", 0x02253}, // IMAGE OF OR APPROXIMATELY EQUAL TO
- {"escr", 0x0212F}, // SCRIPT SMALL E
- {"Escr", 0x02130}, // SCRIPT CAPITAL E
- {"esdot", 0x02250}, // APPROACHES THE LIMIT
- {"esim", 0x02242}, // MINUS TILDE
- {"Esim", 0x02A73}, // EQUALS SIGN ABOVE TILDE OPERATOR
- {"Eta", 0x00397}, // GREEK CAPITAL LETTER ETA
- {"eta", 0x003B7}, // GREEK SMALL LETTER ETA
- {"ETH", 0x000D0}, // LATIN CAPITAL LETTER ETH
- {"eth", 0x000F0}, // LATIN SMALL LETTER ETH
- {"Euml", 0x000CB}, // LATIN CAPITAL LETTER E WITH DIAERESIS
- {"euml", 0x000EB}, // LATIN SMALL LETTER E WITH DIAERESIS
- {"euro", 0x020AC}, // EURO SIGN
- {"excl", 0x00021}, // EXCLAMATION MARK
- {"exist", 0x02203}, // THERE EXISTS
- {"Exists", 0x02203}, // THERE EXISTS
- {"expectation", 0x02130}, // SCRIPT CAPITAL E
- {"exponentiale", 0x02147}, // DOUBLE-STRUCK ITALIC SMALL E
- {"ExponentialE", 0x02147}, // DOUBLE-STRUCK ITALIC SMALL E
- {NULL, 0}
-};
-
-static NameId namesF[]={
- {"fallingdotseq", 0x02252}, // APPROXIMATELY EQUAL TO OR THE IMAGE OF
- {"Fcy", 0x00424}, // CYRILLIC CAPITAL LETTER EF
- {"fcy", 0x00444}, // CYRILLIC SMALL LETTER EF
- {"female", 0x02640}, // FEMALE SIGN
- {"ffilig", 0x0FB03}, // LATIN SMALL LIGATURE FFI
- {"fflig", 0x0FB00}, // LATIN SMALL LIGATURE FF
- {"ffllig", 0x0FB04}, // LATIN SMALL LIGATURE FFL
- {"Ffr", 0x1D509}, // MATHEMATICAL FRAKTUR CAPITAL F
- {"ffr", 0x1D523}, // MATHEMATICAL FRAKTUR SMALL F
- {"filig", 0x0FB01}, // LATIN SMALL LIGATURE FI
- {"FilledSmallSquare", 0x025FC}, // BLACK MEDIUM SQUARE
- {"FilledVerySmallSquare", 0x025AA}, // BLACK SMALL SQUARE
-// "fjlig", 0x00066;0x0006A}, // fj ligature
- {"flat", 0x0266D}, // MUSIC FLAT SIGN
- {"fllig", 0x0FB02}, // LATIN SMALL LIGATURE FL
- {"fltns", 0x025B1}, // WHITE PARALLELOGRAM
- {"fnof", 0x00192}, // LATIN SMALL LETTER F WITH HOOK
- {"Fopf", 0x1D53D}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL F
- {"fopf", 0x1D557}, // MATHEMATICAL DOUBLE-STRUCK SMALL F
- {"forall", 0x02200}, // FOR ALL
- {"ForAll", 0x02200}, // FOR ALL
- {"fork", 0x022D4}, // PITCHFORK
- {"forkv", 0x02AD9}, // ELEMENT OF OPENING DOWNWARDS
- {"Fouriertrf", 0x02131}, // SCRIPT CAPITAL F
- {"fpartint", 0x02A0D}, // FINITE PART INTEGRAL
- {"frac12", 0x000BD}, // VULGAR FRACTION ONE HALF
- {"frac13", 0x02153}, // VULGAR FRACTION ONE THIRD
- {"frac14", 0x000BC}, // VULGAR FRACTION ONE QUARTER
- {"frac15", 0x02155}, // VULGAR FRACTION ONE FIFTH
- {"frac16", 0x02159}, // VULGAR FRACTION ONE SIXTH
- {"frac18", 0x0215B}, // VULGAR FRACTION ONE EIGHTH
- {"frac23", 0x02154}, // VULGAR FRACTION TWO THIRDS
- {"frac25", 0x02156}, // VULGAR FRACTION TWO FIFTHS
- {"frac34", 0x000BE}, // VULGAR FRACTION THREE QUARTERS
- {"frac35", 0x02157}, // VULGAR FRACTION THREE FIFTHS
- {"frac38", 0x0215C}, // VULGAR FRACTION THREE EIGHTHS
- {"frac45", 0x02158}, // VULGAR FRACTION FOUR FIFTHS
- {"frac56", 0x0215A}, // VULGAR FRACTION FIVE SIXTHS
- {"frac58", 0x0215D}, // VULGAR FRACTION FIVE EIGHTHS
- {"frac78", 0x0215E}, // VULGAR FRACTION SEVEN EIGHTHS
- {"frasl", 0x02044}, // FRACTION SLASH
- {"frown", 0x02322}, // FROWN
- {"Fscr", 0x02131}, // SCRIPT CAPITAL F
- {"fscr", 0x1D4BB}, // MATHEMATICAL SCRIPT SMALL F
- {NULL, 0}
-};
-
-static NameId namesG[]={
- {"gacute", 0x001F5}, // LATIN SMALL LETTER G WITH ACUTE
- {"Gamma", 0x00393}, // GREEK CAPITAL LETTER GAMMA
- {"gamma", 0x003B3}, // GREEK SMALL LETTER GAMMA
- {"Gammad", 0x003DC}, // GREEK LETTER DIGAMMA
- {"gammad", 0x003DD}, // GREEK SMALL LETTER DIGAMMA
- {"gap", 0x02A86}, // GREATER-THAN OR APPROXIMATE
- {"Gbreve", 0x0011E}, // LATIN CAPITAL LETTER G WITH BREVE
- {"gbreve", 0x0011F}, // LATIN SMALL LETTER G WITH BREVE
- {"Gcedil", 0x00122}, // LATIN CAPITAL LETTER G WITH CEDILLA
- {"Gcirc", 0x0011C}, // LATIN CAPITAL LETTER G WITH CIRCUMFLEX
- {"gcirc", 0x0011D}, // LATIN SMALL LETTER G WITH CIRCUMFLEX
- {"Gcy", 0x00413}, // CYRILLIC CAPITAL LETTER GHE
- {"gcy", 0x00433}, // CYRILLIC SMALL LETTER GHE
- {"Gdot", 0x00120}, // LATIN CAPITAL LETTER G WITH DOT ABOVE
- {"gdot", 0x00121}, // LATIN SMALL LETTER G WITH DOT ABOVE
- {"ge", 0x02265}, // GREATER-THAN OR EQUAL TO
- {"gE", 0x02267}, // GREATER-THAN OVER EQUAL TO
- {"gel", 0x022DB}, // GREATER-THAN EQUAL TO OR LESS-THAN
- {"gEl", 0x02A8C}, // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
- {"geq", 0x02265}, // GREATER-THAN OR EQUAL TO
- {"geqq", 0x02267}, // GREATER-THAN OVER EQUAL TO
- {"geqslant", 0x02A7E}, // GREATER-THAN OR SLANTED EQUAL TO
- {"ges", 0x02A7E}, // GREATER-THAN OR SLANTED EQUAL TO
- {"gescc", 0x02AA9}, // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
- {"gesdot", 0x02A80}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
- {"gesdoto", 0x02A82}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
- {"gesdotol", 0x02A84}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT
-// "gesl", 0x022DB;0x0FE00}, // GREATER-THAN slanted EQUAL TO OR LESS-THAN
- {"gesles", 0x02A94}, // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL
- {"Gfr", 0x1D50A}, // MATHEMATICAL FRAKTUR CAPITAL G
- {"gfr", 0x1D524}, // MATHEMATICAL FRAKTUR SMALL G
- {"gg", 0x0226B}, // MUCH GREATER-THAN
- {"Gg", 0x022D9}, // VERY MUCH GREATER-THAN
- {"ggg", 0x022D9}, // VERY MUCH GREATER-THAN
- {"Ggr", 0x00393}, // GREEK CAPITAL LETTER GAMMA
- {"ggr", 0x003B3}, // GREEK SMALL LETTER GAMMA
- {"gimel", 0x02137}, // GIMEL SYMBOL
- {"GJcy", 0x00403}, // CYRILLIC CAPITAL LETTER GJE
- {"gjcy", 0x00453}, // CYRILLIC SMALL LETTER GJE
- {"gl", 0x02277}, // GREATER-THAN OR LESS-THAN
- {"gla", 0x02AA5}, // GREATER-THAN BESIDE LESS-THAN
- {"glE", 0x02A92}, // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL
- {"glj", 0x02AA4}, // GREATER-THAN OVERLAPPING LESS-THAN
- {"gnap", 0x02A8A}, // GREATER-THAN AND NOT APPROXIMATE
- {"gnapprox", 0x02A8A}, // GREATER-THAN AND NOT APPROXIMATE
- {"gnE", 0x02269}, // GREATER-THAN BUT NOT EQUAL TO
- {"gne", 0x02A88}, // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
- {"gneq", 0x02A88}, // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
- {"gneqq", 0x02269}, // GREATER-THAN BUT NOT EQUAL TO
- {"gnsim", 0x022E7}, // GREATER-THAN BUT NOT EQUIVALENT TO
- {"Gopf", 0x1D53E}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL G
- {"gopf", 0x1D558}, // MATHEMATICAL DOUBLE-STRUCK SMALL G
- {"grave", 0x00060}, // GRAVE ACCENT
- {"GreaterEqual", 0x02265}, // GREATER-THAN OR EQUAL TO
- {"GreaterEqualLess", 0x022DB}, // GREATER-THAN EQUAL TO OR LESS-THAN
- {"GreaterFullEqual", 0x02267}, // GREATER-THAN OVER EQUAL TO
- {"GreaterGreater", 0x02AA2}, // DOUBLE NESTED GREATER-THAN
- {"GreaterLess", 0x02277}, // GREATER-THAN OR LESS-THAN
- {"GreaterSlantEqual", 0x02A7E}, // GREATER-THAN OR SLANTED EQUAL TO
- {"GreaterTilde", 0x02273}, // GREATER-THAN OR EQUIVALENT TO
- {"gscr", 0x0210A}, // SCRIPT SMALL G
- {"Gscr", 0x1D4A2}, // MATHEMATICAL SCRIPT CAPITAL G
- {"gsim", 0x02273}, // GREATER-THAN OR EQUIVALENT TO
- {"gsime", 0x02A8E}, // GREATER-THAN ABOVE SIMILAR OR EQUAL
- {"gsiml", 0x02A90}, // GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN
- {"gt", 0x0003E}, // GREATER-THAN SIGN
- {"GT", 0x0003E}, // GREATER-THAN SIGN
- {"Gt", 0x0226B}, // MUCH GREATER-THAN
- {"gtcc", 0x02AA7}, // GREATER-THAN CLOSED BY CURVE
- {"gtcir", 0x02A7A}, // GREATER-THAN WITH CIRCLE INSIDE
- {"gtdot", 0x022D7}, // GREATER-THAN WITH DOT
- {"gtlPar", 0x02995}, // DOUBLE LEFT ARC GREATER-THAN BRACKET
- {"gtquest", 0x02A7C}, // GREATER-THAN WITH QUESTION MARK ABOVE
- {"gtrapprox", 0x02A86}, // GREATER-THAN OR APPROXIMATE
- {"gtrarr", 0x02978}, // GREATER-THAN ABOVE RIGHTWARDS ARROW
- {"gtrdot", 0x022D7}, // GREATER-THAN WITH DOT
- {"gtreqless", 0x022DB}, // GREATER-THAN EQUAL TO OR LESS-THAN
- {"gtreqqless", 0x02A8C}, // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
- {"gtrless", 0x02277}, // GREATER-THAN OR LESS-THAN
- {"gtrsim", 0x02273}, // GREATER-THAN OR EQUIVALENT TO
-// "gvertneqq", 0x02269;0x0FE00}, // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke
-// "gvnE", 0x02269;0x0FE00}, // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke
- {NULL, 0}
-};
-
-static NameId namesH[]={
- {"Hacek", 0x002C7}, // CARON
- {"hairsp", 0x0200A}, // HAIR SPACE
- {"half", 0x000BD}, // VULGAR FRACTION ONE HALF
- {"hamilt", 0x0210B}, // SCRIPT CAPITAL H
- {"HARDcy", 0x0042A}, // CYRILLIC CAPITAL LETTER HARD SIGN
- {"hardcy", 0x0044A}, // CYRILLIC SMALL LETTER HARD SIGN
- {"harr", 0x02194}, // LEFT RIGHT ARROW
- {"hArr", 0x021D4}, // LEFT RIGHT DOUBLE ARROW
- {"harrcir", 0x02948}, // LEFT RIGHT ARROW THROUGH SMALL CIRCLE
- {"harrw", 0x021AD}, // LEFT RIGHT WAVE ARROW
- {"Hat", 0x0005E}, // CIRCUMFLEX ACCENT
- {"hbar", 0x0210F}, // PLANCK CONSTANT OVER TWO PI
- {"Hcirc", 0x00124}, // LATIN CAPITAL LETTER H WITH CIRCUMFLEX
- {"hcirc", 0x00125}, // LATIN SMALL LETTER H WITH CIRCUMFLEX
- {"hearts", 0x02665}, // BLACK HEART SUIT
- {"heartsuit", 0x02665}, // BLACK HEART SUIT
- {"hellip", 0x02026}, // HORIZONTAL ELLIPSIS
- {"hercon", 0x022B9}, // HERMITIAN CONJUGATE MATRIX
- {"Hfr", 0x0210C}, // BLACK-LETTER CAPITAL H
- {"hfr", 0x1D525}, // MATHEMATICAL FRAKTUR SMALL H
- {"HilbertSpace", 0x0210B}, // SCRIPT CAPITAL H
- {"hksearow", 0x02925}, // SOUTH EAST ARROW WITH HOOK
- {"hkswarow", 0x02926}, // SOUTH WEST ARROW WITH HOOK
- {"hoarr", 0x021FF}, // LEFT RIGHT OPEN-HEADED ARROW
- {"homtht", 0x0223B}, // HOMOTHETIC
- {"hookleftarrow", 0x021A9}, // LEFTWARDS ARROW WITH HOOK
- {"hookrightarrow", 0x021AA}, // RIGHTWARDS ARROW WITH HOOK
- {"Hopf", 0x0210D}, // DOUBLE-STRUCK CAPITAL H
- {"hopf", 0x1D559}, // MATHEMATICAL DOUBLE-STRUCK SMALL H
- {"horbar", 0x02015}, // HORIZONTAL BAR
- {"HorizontalLine", 0x02500}, // BOX DRAWINGS LIGHT HORIZONTAL
- {"Hscr", 0x0210B}, // SCRIPT CAPITAL H
- {"hscr", 0x1D4BD}, // MATHEMATICAL SCRIPT SMALL H
- {"hslash", 0x0210F}, // PLANCK CONSTANT OVER TWO PI
- {"Hstrok", 0x00126}, // LATIN CAPITAL LETTER H WITH STROKE
- {"hstrok", 0x00127}, // LATIN SMALL LETTER H WITH STROKE
- {"HumpDownHump", 0x0224E}, // GEOMETRICALLY EQUIVALENT TO
- {"HumpEqual", 0x0224F}, // DIFFERENCE BETWEEN
- {"hybull", 0x02043}, // HYPHEN BULLET
- {"hyphen", 0x02010}, // HYPHEN
- {NULL, 0}
-};
-
-static NameId namesI[]={
- {"Iacgr", 0x0038A}, // GREEK CAPITAL LETTER IOTA WITH TONOS
- {"iacgr", 0x003AF}, // GREEK SMALL LETTER IOTA WITH TONOS
- {"Iacute", 0x000CD}, // LATIN CAPITAL LETTER I WITH ACUTE
- {"iacute", 0x000ED}, // LATIN SMALL LETTER I WITH ACUTE
- {"ic", 0x02063}, // INVISIBLE SEPARATOR
- {"Icirc", 0x000CE}, // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
- {"icirc", 0x000EE}, // LATIN SMALL LETTER I WITH CIRCUMFLEX
- {"Icy", 0x00418}, // CYRILLIC CAPITAL LETTER I
- {"icy", 0x00438}, // CYRILLIC SMALL LETTER I
- {"idiagr", 0x00390}, // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
- {"Idigr", 0x003AA}, // GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
- {"idigr", 0x003CA}, // GREEK SMALL LETTER IOTA WITH DIALYTIKA
- {"Idot", 0x00130}, // LATIN CAPITAL LETTER I WITH DOT ABOVE
- {"IEcy", 0x00415}, // CYRILLIC CAPITAL LETTER IE
- {"iecy", 0x00435}, // CYRILLIC SMALL LETTER IE
- {"iexcl", 0x000A1}, // INVERTED EXCLAMATION MARK
- {"iff", 0x021D4}, // LEFT RIGHT DOUBLE ARROW
- {"Ifr", 0x02111}, // BLACK-LETTER CAPITAL I
- {"ifr", 0x1D526}, // MATHEMATICAL FRAKTUR SMALL I
- {"Igr", 0x00399}, // GREEK CAPITAL LETTER IOTA
- {"igr", 0x003B9}, // GREEK SMALL LETTER IOTA
- {"Igrave", 0x000CC}, // LATIN CAPITAL LETTER I WITH GRAVE
- {"igrave", 0x000EC}, // LATIN SMALL LETTER I WITH GRAVE
- {"ii", 0x02148}, // DOUBLE-STRUCK ITALIC SMALL I
- {"iiiint", 0x02A0C}, // QUADRUPLE INTEGRAL OPERATOR
- {"iiint", 0x0222D}, // TRIPLE INTEGRAL
- {"iinfin", 0x029DC}, // INCOMPLETE INFINITY
- {"iiota", 0x02129}, // TURNED GREEK SMALL LETTER IOTA
- {"IJlig", 0x00132}, // LATIN CAPITAL LIGATURE IJ
- {"ijlig", 0x00133}, // LATIN SMALL LIGATURE IJ
- {"Im", 0x02111}, // BLACK-LETTER CAPITAL I
- {"Imacr", 0x0012A}, // LATIN CAPITAL LETTER I WITH MACRON
- {"imacr", 0x0012B}, // LATIN SMALL LETTER I WITH MACRON
- {"image", 0x02111}, // BLACK-LETTER CAPITAL I
- {"ImaginaryI", 0x02148}, // DOUBLE-STRUCK ITALIC SMALL I
- {"imagline", 0x02110}, // SCRIPT CAPITAL I
- {"imagpart", 0x02111}, // BLACK-LETTER CAPITAL I
- {"imath", 0x00131}, // LATIN SMALL LETTER DOTLESS I
- {"imof", 0x022B7}, // IMAGE OF
- {"imped", 0x001B5}, // LATIN CAPITAL LETTER Z WITH STROKE
- {"Implies", 0x021D2}, // RIGHTWARDS DOUBLE ARROW
- {"in", 0x02208}, // ELEMENT OF
- {"incare", 0x02105}, // CARE OF
- {"infin", 0x0221E}, // INFINITY
- {"infintie", 0x029DD}, // TIE OVER INFINITY
- {"inodot", 0x00131}, // LATIN SMALL LETTER DOTLESS I
- {"int", 0x0222B}, // INTEGRAL
- {"Int", 0x0222C}, // DOUBLE INTEGRAL
- {"intcal", 0x022BA}, // INTERCALATE
- {"integers", 0x02124}, // DOUBLE-STRUCK CAPITAL Z
- {"Integral", 0x0222B}, // INTEGRAL
- {"intercal", 0x022BA}, // INTERCALATE
- {"Intersection", 0x022C2}, // N-ARY INTERSECTION
- {"intlarhk", 0x02A17}, // INTEGRAL WITH LEFTWARDS ARROW WITH HOOK
- {"intprod", 0x02A3C}, // INTERIOR PRODUCT
- {"InvisibleComma", 0x02063}, // INVISIBLE SEPARATOR
- {"InvisibleTimes", 0x02062}, // INVISIBLE TIMES
- {"IOcy", 0x00401}, // CYRILLIC CAPITAL LETTER IO
- {"iocy", 0x00451}, // CYRILLIC SMALL LETTER IO
- {"Iogon", 0x0012E}, // LATIN CAPITAL LETTER I WITH OGONEK
- {"iogon", 0x0012F}, // LATIN SMALL LETTER I WITH OGONEK
- {"Iopf", 0x1D540}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL I
- {"iopf", 0x1D55A}, // MATHEMATICAL DOUBLE-STRUCK SMALL I
- {"Iota", 0x00399}, // GREEK CAPITAL LETTER IOTA
- {"iota", 0x003B9}, // GREEK SMALL LETTER IOTA
- {"iprod", 0x02A3C}, // INTERIOR PRODUCT
- {"iquest", 0x000BF}, // INVERTED QUESTION MARK
- {"Iscr", 0x02110}, // SCRIPT CAPITAL I
- {"iscr", 0x1D4BE}, // MATHEMATICAL SCRIPT SMALL I
- {"isin", 0x02208}, // ELEMENT OF
- {"isindot", 0x022F5}, // ELEMENT OF WITH DOT ABOVE
- {"isinE", 0x022F9}, // ELEMENT OF WITH TWO HORIZONTAL STROKES
- {"isins", 0x022F4}, // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
- {"isinsv", 0x022F3}, // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
- {"isinv", 0x02208}, // ELEMENT OF
- {"it", 0x02062}, // INVISIBLE TIMES
- {"Itilde", 0x00128}, // LATIN CAPITAL LETTER I WITH TILDE
- {"itilde", 0x00129}, // LATIN SMALL LETTER I WITH TILDE
- {"Iukcy", 0x00406}, // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
- {"iukcy", 0x00456}, // CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
- {"Iuml", 0x000CF}, // LATIN CAPITAL LETTER I WITH DIAERESIS
- {"iuml", 0x000EF}, // LATIN SMALL LETTER I WITH DIAERESIS
- {NULL, 0}
-};
-
-static NameId namesJ[]={
- {"Jcirc", 0x00134}, // LATIN CAPITAL LETTER J WITH CIRCUMFLEX
- {"jcirc", 0x00135}, // LATIN SMALL LETTER J WITH CIRCUMFLEX
- {"Jcy", 0x00419}, // CYRILLIC CAPITAL LETTER SHORT I
- {"jcy", 0x00439}, // CYRILLIC SMALL LETTER SHORT I
- {"Jfr", 0x1D50D}, // MATHEMATICAL FRAKTUR CAPITAL J
- {"jfr", 0x1D527}, // MATHEMATICAL FRAKTUR SMALL J
- {"jmath", 0x00237}, // LATIN SMALL LETTER DOTLESS J
- {"Jopf", 0x1D541}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL J
- {"jopf", 0x1D55B}, // MATHEMATICAL DOUBLE-STRUCK SMALL J
- {"Jscr", 0x1D4A5}, // MATHEMATICAL SCRIPT CAPITAL J
- {"jscr", 0x1D4BF}, // MATHEMATICAL SCRIPT SMALL J
- {"Jsercy", 0x00408}, // CYRILLIC CAPITAL LETTER JE
- {"jsercy", 0x00458}, // CYRILLIC SMALL LETTER JE
- {"Jukcy", 0x00404}, // CYRILLIC CAPITAL LETTER UKRAINIAN IE
- {"jukcy", 0x00454}, // CYRILLIC SMALL LETTER UKRAINIAN IE
- {NULL, 0}
-};
-
-static NameId namesK[]={
- {"Kappa", 0x0039A}, // GREEK CAPITAL LETTER KAPPA
- {"kappa", 0x003BA}, // GREEK SMALL LETTER KAPPA
- {"kappav", 0x003F0}, // GREEK KAPPA SYMBOL
- {"Kcedil", 0x00136}, // LATIN CAPITAL LETTER K WITH CEDILLA
- {"kcedil", 0x00137}, // LATIN SMALL LETTER K WITH CEDILLA
- {"Kcy", 0x0041A}, // CYRILLIC CAPITAL LETTER KA
- {"kcy", 0x0043A}, // CYRILLIC SMALL LETTER KA
- {"Kfr", 0x1D50E}, // MATHEMATICAL FRAKTUR CAPITAL K
- {"kfr", 0x1D528}, // MATHEMATICAL FRAKTUR SMALL K
- {"Kgr", 0x0039A}, // GREEK CAPITAL LETTER KAPPA
- {"kgr", 0x003BA}, // GREEK SMALL LETTER KAPPA
- {"kgreen", 0x00138}, // LATIN SMALL LETTER KRA
- {"KHcy", 0x00425}, // CYRILLIC CAPITAL LETTER HA
- {"khcy", 0x00445}, // CYRILLIC SMALL LETTER HA
- {"KHgr", 0x003A7}, // GREEK CAPITAL LETTER CHI
- {"khgr", 0x003C7}, // GREEK SMALL LETTER CHI
- {"KJcy", 0x0040C}, // CYRILLIC CAPITAL LETTER KJE
- {"kjcy", 0x0045C}, // CYRILLIC SMALL LETTER KJE
- {"Kopf", 0x1D542}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL K
- {"kopf", 0x1D55C}, // MATHEMATICAL DOUBLE-STRUCK SMALL K
- {"Kscr", 0x1D4A6}, // MATHEMATICAL SCRIPT CAPITAL K
- {"kscr", 0x1D4C0}, // MATHEMATICAL SCRIPT SMALL K
- {NULL, 0}
-};
-
-static NameId namesL[]={
- {"lAarr", 0x021DA}, // LEFTWARDS TRIPLE ARROW
- {"Lacute", 0x00139}, // LATIN CAPITAL LETTER L WITH ACUTE
- {"lacute", 0x0013A}, // LATIN SMALL LETTER L WITH ACUTE
- {"laemptyv", 0x029B4}, // EMPTY SET WITH LEFT ARROW ABOVE
- {"lagran", 0x02112}, // SCRIPT CAPITAL L
- {"Lambda", 0x0039B}, // GREEK CAPITAL LETTER LAMDA
- {"lambda", 0x003BB}, // GREEK SMALL LETTER LAMDA
- {"lang", 0x027E8}, // MATHEMATICAL LEFT ANGLE BRACKET
- {"Lang", 0x027EA}, // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
- {"langd", 0x02991}, // LEFT ANGLE BRACKET WITH DOT
- {"langle", 0x027E8}, // MATHEMATICAL LEFT ANGLE BRACKET
- {"lap", 0x02A85}, // LESS-THAN OR APPROXIMATE
- {"Laplacetrf", 0x02112}, // SCRIPT CAPITAL L
- {"laquo", 0x000AB}, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
- {"larr", 0x02190}, // LEFTWARDS ARROW
- {"Larr", 0x0219E}, // LEFTWARDS TWO HEADED ARROW
- {"lArr", 0x021D0}, // LEFTWARDS DOUBLE ARROW
- {"larrb", 0x021E4}, // LEFTWARDS ARROW TO BAR
- {"larrbfs", 0x0291F}, // LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND
- {"larrfs", 0x0291D}, // LEFTWARDS ARROW TO BLACK DIAMOND
- {"larrhk", 0x021A9}, // LEFTWARDS ARROW WITH HOOK
- {"larrlp", 0x021AB}, // LEFTWARDS ARROW WITH LOOP
- {"larrpl", 0x02939}, // LEFT-SIDE ARC ANTICLOCKWISE ARROW
- {"larrsim", 0x02973}, // LEFTWARDS ARROW ABOVE TILDE OPERATOR
- {"larrtl", 0x021A2}, // LEFTWARDS ARROW WITH TAIL
- {"lat", 0x02AAB}, // LARGER THAN
- {"latail", 0x02919}, // LEFTWARDS ARROW-TAIL
- {"lAtail", 0x0291B}, // LEFTWARDS DOUBLE ARROW-TAIL
- {"late", 0x02AAD}, // LARGER THAN OR EQUAL TO
-// "lates", 0x02AAD;0x0FE00}, // LARGER THAN OR slanted EQUAL
- {"lbarr", 0x0290C}, // LEFTWARDS DOUBLE DASH ARROW
- {"lBarr", 0x0290E}, // LEFTWARDS TRIPLE DASH ARROW
- {"lbbrk", 0x02772}, // LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
- {"lbrace", 0x0007B}, // LEFT CURLY BRACKET
- {"lbrack", 0x0005B}, // LEFT SQUARE BRACKET
- {"lbrke", 0x0298B}, // LEFT SQUARE BRACKET WITH UNDERBAR
- {"lbrksld", 0x0298F}, // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
- {"lbrkslu", 0x0298D}, // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
- {"Lcaron", 0x0013D}, // LATIN CAPITAL LETTER L WITH CARON
- {"lcaron", 0x0013E}, // LATIN SMALL LETTER L WITH CARON
- {"Lcedil", 0x0013B}, // LATIN CAPITAL LETTER L WITH CEDILLA
- {"lcedil", 0x0013C}, // LATIN SMALL LETTER L WITH CEDILLA
- {"lceil", 0x02308}, // LEFT CEILING
- {"lcub", 0x0007B}, // LEFT CURLY BRACKET
- {"Lcy", 0x0041B}, // CYRILLIC CAPITAL LETTER EL
- {"lcy", 0x0043B}, // CYRILLIC SMALL LETTER EL
- {"ldca", 0x02936}, // ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS
- {"ldquo", 0x0201C}, // LEFT DOUBLE QUOTATION MARK
- {"ldquor", 0x0201E}, // DOUBLE LOW-9 QUOTATION MARK
- {"ldrdhar", 0x02967}, // LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
- {"ldrushar", 0x0294B}, // LEFT BARB DOWN RIGHT BARB UP HARPOON
- {"ldsh", 0x021B2}, // DOWNWARDS ARROW WITH TIP LEFTWARDS
- {"le", 0x02264}, // LESS-THAN OR EQUAL TO
- {"lE", 0x02266}, // LESS-THAN OVER EQUAL TO
- {"LeftAngleBracket", 0x027E8}, // MATHEMATICAL LEFT ANGLE BRACKET
- {"leftarrow", 0x02190}, // LEFTWARDS ARROW
- {"LeftArrow", 0x02190}, // LEFTWARDS ARROW
- {"Leftarrow", 0x021D0}, // LEFTWARDS DOUBLE ARROW
- {"LeftArrowBar", 0x021E4}, // LEFTWARDS ARROW TO BAR
- {"LeftArrowRightArrow", 0x021C6}, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
- {"leftarrowtail", 0x021A2}, // LEFTWARDS ARROW WITH TAIL
- {"LeftCeiling", 0x02308}, // LEFT CEILING
- {"LeftDoubleBracket", 0x027E6}, // MATHEMATICAL LEFT WHITE SQUARE BRACKET
- {"LeftDownTeeVector", 0x02961}, // DOWNWARDS HARPOON WITH BARB LEFT FROM BAR
- {"LeftDownVector", 0x021C3}, // DOWNWARDS HARPOON WITH BARB LEFTWARDS
- {"LeftDownVectorBar", 0x02959}, // DOWNWARDS HARPOON WITH BARB LEFT TO BAR
- {"LeftFloor", 0x0230A}, // LEFT FLOOR
- {"leftharpoondown", 0x021BD}, // LEFTWARDS HARPOON WITH BARB DOWNWARDS
- {"leftharpoonup", 0x021BC}, // LEFTWARDS HARPOON WITH BARB UPWARDS
- {"leftleftarrows", 0x021C7}, // LEFTWARDS PAIRED ARROWS
- {"leftrightarrow", 0x02194}, // LEFT RIGHT ARROW
- {"LeftRightArrow", 0x02194}, // LEFT RIGHT ARROW
- {"Leftrightarrow", 0x021D4}, // LEFT RIGHT DOUBLE ARROW
- {"leftrightarrows", 0x021C6}, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
- {"leftrightharpoons", 0x021CB}, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
- {"leftrightsquigarrow", 0x021AD}, // LEFT RIGHT WAVE ARROW
- {"LeftRightVector", 0x0294E}, // LEFT BARB UP RIGHT BARB UP HARPOON
- {"LeftTee", 0x022A3}, // LEFT TACK
- {"LeftTeeArrow", 0x021A4}, // LEFTWARDS ARROW FROM BAR
- {"LeftTeeVector", 0x0295A}, // LEFTWARDS HARPOON WITH BARB UP FROM BAR
- {"leftthreetimes", 0x022CB}, // LEFT SEMIDIRECT PRODUCT
- {"LeftTriangle", 0x022B2}, // NORMAL SUBGROUP OF
- {"LeftTriangleBar", 0x029CF}, // LEFT TRIANGLE BESIDE VERTICAL BAR
- {"LeftTriangleEqual", 0x022B4}, // NORMAL SUBGROUP OF OR EQUAL TO
- {"LeftUpDownVector", 0x02951}, // UP BARB LEFT DOWN BARB LEFT HARPOON
- {"LeftUpTeeVector", 0x02960}, // UPWARDS HARPOON WITH BARB LEFT FROM BAR
- {"LeftUpVector", 0x021BF}, // UPWARDS HARPOON WITH BARB LEFTWARDS
- {"LeftUpVectorBar", 0x02958}, // UPWARDS HARPOON WITH BARB LEFT TO BAR
- {"LeftVector", 0x021BC}, // LEFTWARDS HARPOON WITH BARB UPWARDS
- {"LeftVectorBar", 0x02952}, // LEFTWARDS HARPOON WITH BARB UP TO BAR
- {"leg", 0x022DA}, // LESS-THAN EQUAL TO OR GREATER-THAN
- {"lEg", 0x02A8B}, // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
- {"leq", 0x02264}, // LESS-THAN OR EQUAL TO
- {"leqq", 0x02266}, // LESS-THAN OVER EQUAL TO
- {"leqslant", 0x02A7D}, // LESS-THAN OR SLANTED EQUAL TO
- {"les", 0x02A7D}, // LESS-THAN OR SLANTED EQUAL TO
- {"lescc", 0x02AA8}, // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
- {"lesdot", 0x02A7F}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
- {"lesdoto", 0x02A81}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
- {"lesdotor", 0x02A83}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT
-// "lesg", 0x022DA;0x0FE00}, // LESS-THAN slanted EQUAL TO OR GREATER-THAN
- {"lesges", 0x02A93}, // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL
- {"lessapprox", 0x02A85}, // LESS-THAN OR APPROXIMATE
- {"lessdot", 0x022D6}, // LESS-THAN WITH DOT
- {"lesseqgtr", 0x022DA}, // LESS-THAN EQUAL TO OR GREATER-THAN
- {"lesseqqgtr", 0x02A8B}, // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
- {"LessEqualGreater", 0x022DA}, // LESS-THAN EQUAL TO OR GREATER-THAN
- {"LessFullEqual", 0x02266}, // LESS-THAN OVER EQUAL TO
- {"LessGreater", 0x02276}, // LESS-THAN OR GREATER-THAN
- {"lessgtr", 0x02276}, // LESS-THAN OR GREATER-THAN
- {"LessLess", 0x02AA1}, // DOUBLE NESTED LESS-THAN
- {"lesssim", 0x02272}, // LESS-THAN OR EQUIVALENT TO
- {"LessSlantEqual", 0x02A7D}, // LESS-THAN OR SLANTED EQUAL TO
- {"LessTilde", 0x02272}, // LESS-THAN OR EQUIVALENT TO
- {"lfisht", 0x0297C}, // LEFT FISH TAIL
- {"lfloor", 0x0230A}, // LEFT FLOOR
- {"Lfr", 0x1D50F}, // MATHEMATICAL FRAKTUR CAPITAL L
- {"lfr", 0x1D529}, // MATHEMATICAL FRAKTUR SMALL L
- {"lg", 0x02276}, // LESS-THAN OR GREATER-THAN
- {"lgE", 0x02A91}, // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL
- {"Lgr", 0x0039B}, // GREEK CAPITAL LETTER LAMDA
- {"lgr", 0x003BB}, // GREEK SMALL LETTER LAMDA
- {"lHar", 0x02962}, // LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN
- {"lhard", 0x021BD}, // LEFTWARDS HARPOON WITH BARB DOWNWARDS
- {"lharu", 0x021BC}, // LEFTWARDS HARPOON WITH BARB UPWARDS
- {"lharul", 0x0296A}, // LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
- {"lhblk", 0x02584}, // LOWER HALF BLOCK
- {"LJcy", 0x00409}, // CYRILLIC CAPITAL LETTER LJE
- {"ljcy", 0x00459}, // CYRILLIC SMALL LETTER LJE
- {"ll", 0x0226A}, // MUCH LESS-THAN
- {"Ll", 0x022D8}, // VERY MUCH LESS-THAN
- {"llarr", 0x021C7}, // LEFTWARDS PAIRED ARROWS
- {"llcorner", 0x0231E}, // BOTTOM LEFT CORNER
- {"Lleftarrow", 0x021DA}, // LEFTWARDS TRIPLE ARROW
- {"llhard", 0x0296B}, // LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
- {"lltri", 0x025FA}, // LOWER LEFT TRIANGLE
- {"Lmidot", 0x0013F}, // LATIN CAPITAL LETTER L WITH MIDDLE DOT
- {"lmidot", 0x00140}, // LATIN SMALL LETTER L WITH MIDDLE DOT
- {"lmoust", 0x023B0}, // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION
- {"lmoustache", 0x023B0}, // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION
- {"lnap", 0x02A89}, // LESS-THAN AND NOT APPROXIMATE
- {"lnapprox", 0x02A89}, // LESS-THAN AND NOT APPROXIMATE
- {"lnE", 0x02268}, // LESS-THAN BUT NOT EQUAL TO
- {"lne", 0x02A87}, // LESS-THAN AND SINGLE-LINE NOT EQUAL TO
- {"lneq", 0x02A87}, // LESS-THAN AND SINGLE-LINE NOT EQUAL TO
- {"lneqq", 0x02268}, // LESS-THAN BUT NOT EQUAL TO
- {"lnsim", 0x022E6}, // LESS-THAN BUT NOT EQUIVALENT TO
- {"loang", 0x027EC}, // MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
- {"loarr", 0x021FD}, // LEFTWARDS OPEN-HEADED ARROW
- {"lobrk", 0x027E6}, // MATHEMATICAL LEFT WHITE SQUARE BRACKET
- {"longleftarrow", 0x027F5}, // LONG LEFTWARDS ARROW
- {"LongLeftArrow", 0x027F5}, // LONG LEFTWARDS ARROW
- {"Longleftarrow", 0x027F8}, // LONG LEFTWARDS DOUBLE ARROW
- {"longleftrightarrow", 0x027F7}, // LONG LEFT RIGHT ARROW
- {"LongLeftRightArrow", 0x027F7}, // LONG LEFT RIGHT ARROW
- {"Longleftrightarrow", 0x027FA}, // LONG LEFT RIGHT DOUBLE ARROW
- {"longmapsto", 0x027FC}, // LONG RIGHTWARDS ARROW FROM BAR
- {"longrightarrow", 0x027F6}, // LONG RIGHTWARDS ARROW
- {"LongRightArrow", 0x027F6}, // LONG RIGHTWARDS ARROW
- {"Longrightarrow", 0x027F9}, // LONG RIGHTWARDS DOUBLE ARROW
- {"looparrowleft", 0x021AB}, // LEFTWARDS ARROW WITH LOOP
- {"looparrowright", 0x021AC}, // RIGHTWARDS ARROW WITH LOOP
- {"lopar", 0x02985}, // LEFT WHITE PARENTHESIS
- {"Lopf", 0x1D543}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL L
- {"lopf", 0x1D55D}, // MATHEMATICAL DOUBLE-STRUCK SMALL L
- {"loplus", 0x02A2D}, // PLUS SIGN IN LEFT HALF CIRCLE
- {"lotimes", 0x02A34}, // MULTIPLICATION SIGN IN LEFT HALF CIRCLE
- {"lowast", 0x02217}, // ASTERISK OPERATOR
- {"lowbar", 0x0005F}, // LOW LINE
- {"LowerLeftArrow", 0x02199}, // SOUTH WEST ARROW
- {"LowerRightArrow", 0x02198}, // SOUTH EAST ARROW
- {"loz", 0x025CA}, // LOZENGE
- {"lozenge", 0x025CA}, // LOZENGE
- {"lozf", 0x029EB}, // BLACK LOZENGE
- {"lpar", 0x00028}, // LEFT PARENTHESIS
- {"lparlt", 0x02993}, // LEFT ARC LESS-THAN BRACKET
- {"lrarr", 0x021C6}, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
- {"lrcorner", 0x0231F}, // BOTTOM RIGHT CORNER
- {"lrhar", 0x021CB}, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
- {"lrhard", 0x0296D}, // RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
- {"lrm", 0x0200E}, // LEFT-TO-RIGHT MARK
- {"lrtri", 0x022BF}, // RIGHT TRIANGLE
- {"lsaquo", 0x02039}, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
- {"Lscr", 0x02112}, // SCRIPT CAPITAL L
- {"lscr", 0x1D4C1}, // MATHEMATICAL SCRIPT SMALL L
- {"lsh", 0x021B0}, // UPWARDS ARROW WITH TIP LEFTWARDS
- {"Lsh", 0x021B0}, // UPWARDS ARROW WITH TIP LEFTWARDS
- {"lsim", 0x02272}, // LESS-THAN OR EQUIVALENT TO
- {"lsime", 0x02A8D}, // LESS-THAN ABOVE SIMILAR OR EQUAL
- {"lsimg", 0x02A8F}, // LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN
- {"lsqb", 0x0005B}, // LEFT SQUARE BRACKET
- {"lsquo", 0x02018}, // LEFT SINGLE QUOTATION MARK
- {"lsquor", 0x0201A}, // SINGLE LOW-9 QUOTATION MARK
- {"Lstrok", 0x00141}, // LATIN CAPITAL LETTER L WITH STROKE
- {"lstrok", 0x00142}, // LATIN SMALL LETTER L WITH STROKE
- {"lt", 0x0003C}, // LESS-THAN SIGN
- {"LT", 0x0003C}, // LESS-THAN SIGN
- {"Lt", 0x0226A}, // MUCH LESS-THAN
- {"ltcc", 0x02AA6}, // LESS-THAN CLOSED BY CURVE
- {"ltcir", 0x02A79}, // LESS-THAN WITH CIRCLE INSIDE
- {"ltdot", 0x022D6}, // LESS-THAN WITH DOT
- {"lthree", 0x022CB}, // LEFT SEMIDIRECT PRODUCT
- {"ltimes", 0x022C9}, // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
- {"ltlarr", 0x02976}, // LESS-THAN ABOVE LEFTWARDS ARROW
- {"ltquest", 0x02A7B}, // LESS-THAN WITH QUESTION MARK ABOVE
- {"ltri", 0x025C3}, // WHITE LEFT-POINTING SMALL TRIANGLE
- {"ltrie", 0x022B4}, // NORMAL SUBGROUP OF OR EQUAL TO
- {"ltrif", 0x025C2}, // BLACK LEFT-POINTING SMALL TRIANGLE
- {"ltrPar", 0x02996}, // DOUBLE RIGHT ARC LESS-THAN BRACKET
- {"lurdshar", 0x0294A}, // LEFT BARB UP RIGHT BARB DOWN HARPOON
- {"luruhar", 0x02966}, // LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP
-// "lvertneqq", 0x02268;0x0FE00}, // LESS-THAN BUT NOT EQUAL TO - with vertical stroke
-// "lvnE", 0x02268;0x0FE00}, // LESS-THAN BUT NOT EQUAL TO - with vertical stroke
- {NULL, 0}
-};
-
-static NameId namesM[]={
- {"macr", 0x000AF}, // MACRON
- {"male", 0x02642}, // MALE SIGN
- {"malt", 0x02720}, // MALTESE CROSS
- {"maltese", 0x02720}, // MALTESE CROSS
- {"map", 0x021A6}, // RIGHTWARDS ARROW FROM BAR
- {"Map", 0x02905}, // RIGHTWARDS TWO-HEADED ARROW FROM BAR
- {"mapsto", 0x021A6}, // RIGHTWARDS ARROW FROM BAR
- {"mapstodown", 0x021A7}, // DOWNWARDS ARROW FROM BAR
- {"mapstoleft", 0x021A4}, // LEFTWARDS ARROW FROM BAR
- {"mapstoup", 0x021A5}, // UPWARDS ARROW FROM BAR
- {"marker", 0x025AE}, // BLACK VERTICAL RECTANGLE
- {"mcomma", 0x02A29}, // MINUS SIGN WITH COMMA ABOVE
- {"Mcy", 0x0041C}, // CYRILLIC CAPITAL LETTER EM
- {"mcy", 0x0043C}, // CYRILLIC SMALL LETTER EM
- {"mdash", 0x02014}, // EM DASH
- {"mDDot", 0x0223A}, // GEOMETRIC PROPORTION
- {"measuredangle", 0x02221}, // MEASURED ANGLE
- {"MediumSpace", 0x0205F}, // MEDIUM MATHEMATICAL SPACE
- {"Mellintrf", 0x02133}, // SCRIPT CAPITAL M
- {"Mfr", 0x1D510}, // MATHEMATICAL FRAKTUR CAPITAL M
- {"mfr", 0x1D52A}, // MATHEMATICAL FRAKTUR SMALL M
- {"Mgr", 0x0039C}, // GREEK CAPITAL LETTER MU
- {"mgr", 0x003BC}, // GREEK SMALL LETTER MU
- {"mho", 0x02127}, // INVERTED OHM SIGN
- {"micro", 0x000B5}, // MICRO SIGN
- {"mid", 0x02223}, // DIVIDES
- {"midast", 0x0002A}, // ASTERISK
- {"midcir", 0x02AF0}, // VERTICAL LINE WITH CIRCLE BELOW
- {"middot", 0x000B7}, // MIDDLE DOT
- {"minus", 0x02212}, // MINUS SIGN
- {"minusb", 0x0229F}, // SQUARED MINUS
- {"minusd", 0x02238}, // DOT MINUS
- {"minusdu", 0x02A2A}, // MINUS SIGN WITH DOT BELOW
- {"MinusPlus", 0x02213}, // MINUS-OR-PLUS SIGN
- {"mlcp", 0x02ADB}, // TRANSVERSAL INTERSECTION
- {"mldr", 0x02026}, // HORIZONTAL ELLIPSIS
- {"mnplus", 0x02213}, // MINUS-OR-PLUS SIGN
- {"models", 0x022A7}, // MODELS
- {"Mopf", 0x1D544}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL M
- {"mopf", 0x1D55E}, // MATHEMATICAL DOUBLE-STRUCK SMALL M
- {"mp", 0x02213}, // MINUS-OR-PLUS SIGN
- {"Mscr", 0x02133}, // SCRIPT CAPITAL M
- {"mscr", 0x1D4C2}, // MATHEMATICAL SCRIPT SMALL M
- {"mstpos", 0x0223E}, // INVERTED LAZY S
- {"Mu", 0x0039C}, // GREEK CAPITAL LETTER MU
- {"mu", 0x003BC}, // GREEK SMALL LETTER MU
- {"multimap", 0x022B8}, // MULTIMAP
- {"mumap", 0x022B8}, // MULTIMAP
- {NULL, 0}
-};
-
-static NameId namesN[]={
- {"nabla", 0x02207}, // NABLA
- {"Nacute", 0x00143}, // LATIN CAPITAL LETTER N WITH ACUTE
- {"nacute", 0x00144}, // LATIN SMALL LETTER N WITH ACUTE
-// "nang", 0x02220;0x020D2}, // ANGLE with vertical line
- {"nap", 0x02249}, // NOT ALMOST EQUAL TO
-// "napE", 0x02A70;0x00338}, // APPROXIMATELY EQUAL OR EQUAL TO with slash
-// "napid", 0x0224B;0x00338}, // TRIPLE TILDE with slash
- {"napos", 0x00149}, // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
- {"napprox", 0x02249}, // NOT ALMOST EQUAL TO
- {"natur", 0x0266E}, // MUSIC NATURAL SIGN
- {"natural", 0x0266E}, // MUSIC NATURAL SIGN
- {"naturals", 0x02115}, // DOUBLE-STRUCK CAPITAL N
- {"nbsp", 0x000A0}, // NO-BREAK SPACE
-// "nbump", 0x0224E;0x00338}, // GEOMETRICALLY EQUIVALENT TO with slash
-// "nbumpe", 0x0224F;0x00338}, // DIFFERENCE BETWEEN with slash
- {"ncap", 0x02A43}, // INTERSECTION WITH OVERBAR
- {"Ncaron", 0x00147}, // LATIN CAPITAL LETTER N WITH CARON
- {"ncaron", 0x00148}, // LATIN SMALL LETTER N WITH CARON
- {"Ncedil", 0x00145}, // LATIN CAPITAL LETTER N WITH CEDILLA
- {"ncedil", 0x00146}, // LATIN SMALL LETTER N WITH CEDILLA
- {"ncong", 0x02247}, // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
-// "ncongdot", 0x02A6D;0x00338}, // CONGRUENT WITH DOT ABOVE with slash
- {"ncup", 0x02A42}, // UNION WITH OVERBAR
- {"Ncy", 0x0041D}, // CYRILLIC CAPITAL LETTER EN
- {"ncy", 0x0043D}, // CYRILLIC SMALL LETTER EN
- {"ndash", 0x02013}, // EN DASH
- {"ne", 0x02260}, // NOT EQUAL TO
- {"nearhk", 0x02924}, // NORTH EAST ARROW WITH HOOK
- {"nearr", 0x02197}, // NORTH EAST ARROW
- {"neArr", 0x021D7}, // NORTH EAST DOUBLE ARROW
- {"nearrow", 0x02197}, // NORTH EAST ARROW
-// "nedot", 0x02250;0x00338}, // APPROACHES THE LIMIT with slash
- {"NegativeMediumSpace", 0x0200B}, // ZERO WIDTH SPACE
- {"NegativeThickSpace", 0x0200B}, // ZERO WIDTH SPACE
- {"NegativeThinSpace", 0x0200B}, // ZERO WIDTH SPACE
- {"NegativeVeryThinSpace", 0x0200B}, // ZERO WIDTH SPACE
- {"nequiv", 0x02262}, // NOT IDENTICAL TO
- {"nesear", 0x02928}, // NORTH EAST ARROW AND SOUTH EAST ARROW
-// "nesim", 0x02242;0x00338}, // MINUS TILDE with slash
- {"NestedGreaterGreater", 0x0226B}, // MUCH GREATER-THAN
- {"NestedLessLess", 0x0226A}, // MUCH LESS-THAN
- {"NewLine", 0x0000A}, // LINE FEED (LF)
- {"nexist", 0x02204}, // THERE DOES NOT EXIST
- {"nexists", 0x02204}, // THERE DOES NOT EXIST
- {"Nfr", 0x1D511}, // MATHEMATICAL FRAKTUR CAPITAL N
- {"nfr", 0x1D52B}, // MATHEMATICAL FRAKTUR SMALL N
-// "ngE", 0x02267;0x00338}, // GREATER-THAN OVER EQUAL TO with slash
- {"nge", 0x02271}, // NEITHER GREATER-THAN NOR EQUAL TO
- {"ngeq", 0x02271}, // NEITHER GREATER-THAN NOR EQUAL TO
-// "ngeqq", 0x02267;0x00338}, // GREATER-THAN OVER EQUAL TO with slash
-// "ngeqslant", 0x02A7E;0x00338}, // GREATER-THAN OR SLANTED EQUAL TO with slash
-// "nges", 0x02A7E;0x00338}, // GREATER-THAN OR SLANTED EQUAL TO with slash
-// "nGg", 0x022D9;0x00338}, // VERY MUCH GREATER-THAN with slash
- {"Ngr", 0x0039D}, // GREEK CAPITAL LETTER NU
- {"ngr", 0x003BD}, // GREEK SMALL LETTER NU
- {"ngsim", 0x02275}, // NEITHER GREATER-THAN NOR EQUIVALENT TO
-// "nGt", 0x0226B;0x020D2}, // MUCH GREATER THAN with vertical line
- {"ngt", 0x0226F}, // NOT GREATER-THAN
- {"ngtr", 0x0226F}, // NOT GREATER-THAN
-// "nGtv", 0x0226B;0x00338}, // MUCH GREATER THAN with slash
- {"nharr", 0x021AE}, // LEFT RIGHT ARROW WITH STROKE
- {"nhArr", 0x021CE}, // LEFT RIGHT DOUBLE ARROW WITH STROKE
- {"nhpar", 0x02AF2}, // PARALLEL WITH HORIZONTAL STROKE
- {"ni", 0x0220B}, // CONTAINS AS MEMBER
- {"nis", 0x022FC}, // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
- {"nisd", 0x022FA}, // CONTAINS WITH LONG HORIZONTAL STROKE
- {"niv", 0x0220B}, // CONTAINS AS MEMBER
- {"NJcy", 0x0040A}, // CYRILLIC CAPITAL LETTER NJE
- {"njcy", 0x0045A}, // CYRILLIC SMALL LETTER NJE
- {"nlarr", 0x0219A}, // LEFTWARDS ARROW WITH STROKE
- {"nlArr", 0x021CD}, // LEFTWARDS DOUBLE ARROW WITH STROKE
- {"nldr", 0x02025}, // TWO DOT LEADER
-// "nlE", 0x02266;0x00338}, // LESS-THAN OVER EQUAL TO with slash
- {"nle", 0x02270}, // NEITHER LESS-THAN NOR EQUAL TO
- {"nleftarrow", 0x0219A}, // LEFTWARDS ARROW WITH STROKE
- {"nLeftarrow", 0x021CD}, // LEFTWARDS DOUBLE ARROW WITH STROKE
- {"nleftrightarrow", 0x021AE}, // LEFT RIGHT ARROW WITH STROKE
- {"nLeftrightarrow", 0x021CE}, // LEFT RIGHT DOUBLE ARROW WITH STROKE
- {"nleq", 0x02270}, // NEITHER LESS-THAN NOR EQUAL TO
-// "nleqq", 0x02266;0x00338}, // LESS-THAN OVER EQUAL TO with slash
-// "nleqslant", 0x02A7D;0x00338}, // LESS-THAN OR SLANTED EQUAL TO with slash
-// "nles", 0x02A7D;0x00338}, // LESS-THAN OR SLANTED EQUAL TO with slash
- {"nless", 0x0226E}, // NOT LESS-THAN
-// "nLl", 0x022D8;0x00338}, // VERY MUCH LESS-THAN with slash
- {"nlsim", 0x02274}, // NEITHER LESS-THAN NOR EQUIVALENT TO
-// "nLt", 0x0226A;0x020D2}, // MUCH LESS THAN with vertical line
- {"nlt", 0x0226E}, // NOT LESS-THAN
- {"nltri", 0x022EA}, // NOT NORMAL SUBGROUP OF
- {"nltrie", 0x022EC}, // NOT NORMAL SUBGROUP OF OR EQUAL TO
-// "nLtv", 0x0226A;0x00338}, // MUCH LESS THAN with slash
- {"nmid", 0x02224}, // DOES NOT DIVIDE
- {"NoBreak", 0x02060}, // WORD JOINER
- {"NonBreakingSpace", 0x000A0}, // NO-BREAK SPACE
- {"Nopf", 0x02115}, // DOUBLE-STRUCK CAPITAL N
- {"nopf", 0x1D55F}, // MATHEMATICAL DOUBLE-STRUCK SMALL N
- {"not", 0x000AC}, // NOT SIGN
- {"Not", 0x02AEC}, // DOUBLE STROKE NOT SIGN
- {"NotCongruent", 0x02262}, // NOT IDENTICAL TO
- {"NotCupCap", 0x0226D}, // NOT EQUIVALENT TO
- {"NotDoubleVerticalBar", 0x02226}, // NOT PARALLEL TO
- {"NotElement", 0x02209}, // NOT AN ELEMENT OF
- {"NotEqual", 0x02260}, // NOT EQUAL TO
-// "NotEqualTilde", 0x02242;0x00338}, // MINUS TILDE with slash
- {"NotExists", 0x02204}, // THERE DOES NOT EXIST
- {"NotGreater", 0x0226F}, // NOT GREATER-THAN
- {"NotGreaterEqual", 0x02271}, // NEITHER GREATER-THAN NOR EQUAL TO
-// "NotGreaterFullEqual", 0x02267;0x00338}, // GREATER-THAN OVER EQUAL TO with slash
-// "NotGreaterGreater", 0x0226B;0x00338}, // MUCH GREATER THAN with slash
- {"NotGreaterLess", 0x02279}, // NEITHER GREATER-THAN NOR LESS-THAN
-// "NotGreaterSlantEqual", 0x02A7E;0x00338}, // GREATER-THAN OR SLANTED EQUAL TO with slash
- {"NotGreaterTilde", 0x02275}, // NEITHER GREATER-THAN NOR EQUIVALENT TO
-// "NotHumpDownHump", 0x0224E;0x00338}, // GEOMETRICALLY EQUIVALENT TO with slash
-// "NotHumpEqual", 0x0224F;0x00338}, // DIFFERENCE BETWEEN with slash
- {"notin", 0x02209}, // NOT AN ELEMENT OF
-// "notindot", 0x022F5;0x00338}, // ELEMENT OF WITH DOT ABOVE with slash
-// "notinE", 0x022F9;0x00338}, // ELEMENT OF WITH TWO HORIZONTAL STROKES with slash
- {"notinva", 0x02209}, // NOT AN ELEMENT OF
- {"notinvb", 0x022F7}, // SMALL ELEMENT OF WITH OVERBAR
- {"notinvc", 0x022F6}, // ELEMENT OF WITH OVERBAR
- {"NotLeftTriangle", 0x022EA}, // NOT NORMAL SUBGROUP OF
-// "NotLeftTriangleBar", 0x029CF;0x00338}, // LEFT TRIANGLE BESIDE VERTICAL BAR with slash
- {"NotLeftTriangleEqual", 0x022EC}, // NOT NORMAL SUBGROUP OF OR EQUAL TO
- {"NotLess", 0x0226E}, // NOT LESS-THAN
- {"NotLessEqual", 0x02270}, // NEITHER LESS-THAN NOR EQUAL TO
- {"NotLessGreater", 0x02278}, // NEITHER LESS-THAN NOR GREATER-THAN
-// "NotLessLess", 0x0226A;0x00338}, // MUCH LESS THAN with slash
-// "NotLessSlantEqual", 0x02A7D;0x00338}, // LESS-THAN OR SLANTED EQUAL TO with slash
- {"NotLessTilde", 0x02274}, // NEITHER LESS-THAN NOR EQUIVALENT TO
-// "NotNestedGreaterGreater", 0x02AA2;0x00338}, // DOUBLE NESTED GREATER-THAN with slash
-// "NotNestedLessLess", 0x02AA1;0x00338}, // DOUBLE NESTED LESS-THAN with slash
- {"notni", 0x0220C}, // DOES NOT CONTAIN AS MEMBER
- {"notniva", 0x0220C}, // DOES NOT CONTAIN AS MEMBER
- {"notnivb", 0x022FE}, // SMALL CONTAINS WITH OVERBAR
- {"notnivc", 0x022FD}, // CONTAINS WITH OVERBAR
- {"NotPrecedes", 0x02280}, // DOES NOT PRECEDE
-// "NotPrecedesEqual", 0x02AAF;0x00338}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash
- {"NotPrecedesSlantEqual", 0x022E0}, // DOES NOT PRECEDE OR EQUAL
- {"NotReverseElement", 0x0220C}, // DOES NOT CONTAIN AS MEMBER
- {"NotRightTriangle", 0x022EB}, // DOES NOT CONTAIN AS NORMAL SUBGROUP
-// "NotRightTriangleBar", 0x029D0;0x00338}, // VERTICAL BAR BESIDE RIGHT TRIANGLE with slash
- {"NotRightTriangleEqual", 0x022ED}, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
-// "NotSquareSubset", 0x0228F;0x00338}, // SQUARE IMAGE OF with slash
- {"NotSquareSubsetEqual", 0x022E2}, // NOT SQUARE IMAGE OF OR EQUAL TO
-// "NotSquareSuperset", 0x02290;0x00338}, // SQUARE ORIGINAL OF with slash
- {"NotSquareSupersetEqual", 0x022E3}, // NOT SQUARE ORIGINAL OF OR EQUAL TO
-// "NotSubset", 0x02282;0x020D2}, // SUBSET OF with vertical line
- {"NotSubsetEqual", 0x02288}, // NEITHER A SUBSET OF NOR EQUAL TO
- {"NotSucceeds", 0x02281}, // DOES NOT SUCCEED
-// "NotSucceedsEqual", 0x02AB0;0x00338}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash
- {"NotSucceedsSlantEqual", 0x022E1}, // DOES NOT SUCCEED OR EQUAL
-// "NotSucceedsTilde", 0x0227F;0x00338}, // SUCCEEDS OR EQUIVALENT TO with slash
-// "NotSuperset", 0x02283;0x020D2}, // SUPERSET OF with vertical line
- {"NotSupersetEqual", 0x02289}, // NEITHER A SUPERSET OF NOR EQUAL TO
- {"NotTilde", 0x02241}, // NOT TILDE
- {"NotTildeEqual", 0x02244}, // NOT ASYMPTOTICALLY EQUAL TO
- {"NotTildeFullEqual", 0x02247}, // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
- {"NotTildeTilde", 0x02249}, // NOT ALMOST EQUAL TO
- {"NotVerticalBar", 0x02224}, // DOES NOT DIVIDE
- {"npar", 0x02226}, // NOT PARALLEL TO
- {"nparallel", 0x02226}, // NOT PARALLEL TO
-// "nparsl", 0x02AFD;0x020E5}, // DOUBLE SOLIDUS OPERATOR with reverse slash
-// "npart", 0x02202;0x00338}, // PARTIAL DIFFERENTIAL with slash
- {"npolint", 0x02A14}, // LINE INTEGRATION NOT INCLUDING THE POLE
- {"npr", 0x02280}, // DOES NOT PRECEDE
- {"nprcue", 0x022E0}, // DOES NOT PRECEDE OR EQUAL
-// "npre", 0x02AAF;0x00338}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash
- {"nprec", 0x02280}, // DOES NOT PRECEDE
-// "npreceq", 0x02AAF;0x00338}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash
- {"nrarr", 0x0219B}, // RIGHTWARDS ARROW WITH STROKE
- {"nrArr", 0x021CF}, // RIGHTWARDS DOUBLE ARROW WITH STROKE
-// "nrarrc", 0x02933;0x00338}, // WAVE ARROW POINTING DIRECTLY RIGHT with slash
-// "nrarrw", 0x0219D;0x00338}, // RIGHTWARDS WAVE ARROW with slash
- {"nrightarrow", 0x0219B}, // RIGHTWARDS ARROW WITH STROKE
- {"nRightarrow", 0x021CF}, // RIGHTWARDS DOUBLE ARROW WITH STROKE
- {"nrtri", 0x022EB}, // DOES NOT CONTAIN AS NORMAL SUBGROUP
- {"nrtrie", 0x022ED}, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
- {"nsc", 0x02281}, // DOES NOT SUCCEED
- {"nsccue", 0x022E1}, // DOES NOT SUCCEED OR EQUAL
-// "nsce", 0x02AB0;0x00338}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash
- {"Nscr", 0x1D4A9}, // MATHEMATICAL SCRIPT CAPITAL N
- {"nscr", 0x1D4C3}, // MATHEMATICAL SCRIPT SMALL N
- {"nshortmid", 0x02224}, // DOES NOT DIVIDE
- {"nshortparallel", 0x02226}, // NOT PARALLEL TO
- {"nsim", 0x02241}, // NOT TILDE
- {"nsime", 0x02244}, // NOT ASYMPTOTICALLY EQUAL TO
- {"nsimeq", 0x02244}, // NOT ASYMPTOTICALLY EQUAL TO
- {"nsmid", 0x02224}, // DOES NOT DIVIDE
- {"nspar", 0x02226}, // NOT PARALLEL TO
- {"nsqsube", 0x022E2}, // NOT SQUARE IMAGE OF OR EQUAL TO
- {"nsqsupe", 0x022E3}, // NOT SQUARE ORIGINAL OF OR EQUAL TO
- {"nsub", 0x02284}, // NOT A SUBSET OF
- {"nsube", 0x02288}, // NEITHER A SUBSET OF NOR EQUAL TO
-// "nsubE", 0x02AC5;0x00338}, // SUBSET OF ABOVE EQUALS SIGN with slash
-// "nsubset", 0x02282;0x020D2}, // SUBSET OF with vertical line
- {"nsubseteq", 0x02288}, // NEITHER A SUBSET OF NOR EQUAL TO
-// "nsubseteqq", 0x02AC5;0x00338}, // SUBSET OF ABOVE EQUALS SIGN with slash
- {"nsucc", 0x02281}, // DOES NOT SUCCEED
-// "nsucceq", 0x02AB0;0x00338}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash
- {"nsup", 0x02285}, // NOT A SUPERSET OF
- {"nsupe", 0x02289}, // NEITHER A SUPERSET OF NOR EQUAL TO
-// "nsupE", 0x02AC6;0x00338}, // SUPERSET OF ABOVE EQUALS SIGN with slash
-// "nsupset", 0x02283;0x020D2}, // SUPERSET OF with vertical line
- {"nsupseteq", 0x02289}, // NEITHER A SUPERSET OF NOR EQUAL TO
-// "nsupseteqq", 0x02AC6;0x00338}, // SUPERSET OF ABOVE EQUALS SIGN with slash
- {"ntgl", 0x02279}, // NEITHER GREATER-THAN NOR LESS-THAN
- {"Ntilde", 0x000D1}, // LATIN CAPITAL LETTER N WITH TILDE
- {"ntilde", 0x000F1}, // LATIN SMALL LETTER N WITH TILDE
- {"ntlg", 0x02278}, // NEITHER LESS-THAN NOR GREATER-THAN
- {"ntriangleleft", 0x022EA}, // NOT NORMAL SUBGROUP OF
- {"ntrianglelefteq", 0x022EC}, // NOT NORMAL SUBGROUP OF OR EQUAL TO
- {"ntriangleright", 0x022EB}, // DOES NOT CONTAIN AS NORMAL SUBGROUP
- {"ntrianglerighteq", 0x022ED}, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
- {"Nu", 0x0039D}, // GREEK CAPITAL LETTER NU
- {"nu", 0x003BD}, // GREEK SMALL LETTER NU
- {"num", 0x00023}, // NUMBER SIGN
- {"numero", 0x02116}, // NUMERO SIGN
- {"numsp", 0x02007}, // FIGURE SPACE
-// "nvap", 0x0224D;0x020D2}, // EQUIVALENT TO with vertical line
- {"nvdash", 0x022AC}, // DOES NOT PROVE
- {"nvDash", 0x022AD}, // NOT TRUE
- {"nVdash", 0x022AE}, // DOES NOT FORCE
- {"nVDash", 0x022AF}, // NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
-// "nvge", 0x02265;0x020D2}, // GREATER-THAN OR EQUAL TO with vertical line
-// "nvgt", 0x0003E;0x020D2}, // GREATER-THAN SIGN with vertical line
- {"nvHarr", 0x02904}, // LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE
- {"nvinfin", 0x029DE}, // INFINITY NEGATED WITH VERTICAL BAR
- {"nvlArr", 0x02902}, // LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE
-// "nvle", 0x02264;0x020D2}, // LESS-THAN OR EQUAL TO with vertical line
-// "nvlt", 0x0003C;0x020D2}, // LESS-THAN SIGN with vertical line
-// "nvltrie", 0x022B4;0x020D2}, // NORMAL SUBGROUP OF OR EQUAL TO with vertical line
- {"nvrArr", 0x02903}, // RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE
-// "nvrtrie", 0x022B5;0x020D2}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO with vertical line
-// "nvsim", 0x0223C;0x020D2}, // TILDE OPERATOR with vertical line
- {"nwarhk", 0x02923}, // NORTH WEST ARROW WITH HOOK
- {"nwarr", 0x02196}, // NORTH WEST ARROW
- {"nwArr", 0x021D6}, // NORTH WEST DOUBLE ARROW
- {"nwarrow", 0x02196}, // NORTH WEST ARROW
- {"nwnear", 0x02927}, // NORTH WEST ARROW AND NORTH EAST ARROW
- {NULL, 0}
-};
-
-static NameId namesO[]={
- {"Oacgr", 0x0038C}, // GREEK CAPITAL LETTER OMICRON WITH TONOS
- {"oacgr", 0x003CC}, // GREEK SMALL LETTER OMICRON WITH TONOS
- {"Oacute", 0x000D3}, // LATIN CAPITAL LETTER O WITH ACUTE
- {"oacute", 0x000F3}, // LATIN SMALL LETTER O WITH ACUTE
- {"oast", 0x0229B}, // CIRCLED ASTERISK OPERATOR
- {"ocir", 0x0229A}, // CIRCLED RING OPERATOR
- {"Ocirc", 0x000D4}, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
- {"ocirc", 0x000F4}, // LATIN SMALL LETTER O WITH CIRCUMFLEX
- {"Ocy", 0x0041E}, // CYRILLIC CAPITAL LETTER O
- {"ocy", 0x0043E}, // CYRILLIC SMALL LETTER O
- {"odash", 0x0229D}, // CIRCLED DASH
- {"Odblac", 0x00150}, // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
- {"odblac", 0x00151}, // LATIN SMALL LETTER O WITH DOUBLE ACUTE
- {"odiv", 0x02A38}, // CIRCLED DIVISION SIGN
- {"odot", 0x02299}, // CIRCLED DOT OPERATOR
- {"odsold", 0x029BC}, // CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN
- {"OElig", 0x00152}, // LATIN CAPITAL LIGATURE OE
- {"oelig", 0x00153}, // LATIN SMALL LIGATURE OE
- {"ofcir", 0x029BF}, // CIRCLED BULLET
- {"Ofr", 0x1D512}, // MATHEMATICAL FRAKTUR CAPITAL O
- {"ofr", 0x1D52C}, // MATHEMATICAL FRAKTUR SMALL O
- {"ogon", 0x002DB}, // OGONEK
- {"Ogr", 0x0039F}, // GREEK CAPITAL LETTER OMICRON
- {"ogr", 0x003BF}, // GREEK SMALL LETTER OMICRON
- {"Ograve", 0x000D2}, // LATIN CAPITAL LETTER O WITH GRAVE
- {"ograve", 0x000F2}, // LATIN SMALL LETTER O WITH GRAVE
- {"ogt", 0x029C1}, // CIRCLED GREATER-THAN
- {"OHacgr", 0x0038F}, // GREEK CAPITAL LETTER OMEGA WITH TONOS
- {"ohacgr", 0x003CE}, // GREEK SMALL LETTER OMEGA WITH TONOS
- {"ohbar", 0x029B5}, // CIRCLE WITH HORIZONTAL BAR
- {"OHgr", 0x003A9}, // GREEK CAPITAL LETTER OMEGA
- {"ohgr", 0x003C9}, // GREEK SMALL LETTER OMEGA
- {"ohm", 0x003A9}, // GREEK CAPITAL LETTER OMEGA
- {"oint", 0x0222E}, // CONTOUR INTEGRAL
- {"olarr", 0x021BA}, // ANTICLOCKWISE OPEN CIRCLE ARROW
- {"olcir", 0x029BE}, // CIRCLED WHITE BULLET
- {"olcross", 0x029BB}, // CIRCLE WITH SUPERIMPOSED X
- {"oline", 0x0203E}, // OVERLINE
- {"olt", 0x029C0}, // CIRCLED LESS-THAN
- {"Omacr", 0x0014C}, // LATIN CAPITAL LETTER O WITH MACRON
- {"omacr", 0x0014D}, // LATIN SMALL LETTER O WITH MACRON
- {"Omega", 0x003A9}, // GREEK CAPITAL LETTER OMEGA
- {"omega", 0x003C9}, // GREEK SMALL LETTER OMEGA
- {"Omicron", 0x0039F}, // GREEK CAPITAL LETTER OMICRON
- {"omicron", 0x003BF}, // GREEK SMALL LETTER OMICRON
- {"omid", 0x029B6}, // CIRCLED VERTICAL BAR
- {"ominus", 0x02296}, // CIRCLED MINUS
- {"Oopf", 0x1D546}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL O
- {"oopf", 0x1D560}, // MATHEMATICAL DOUBLE-STRUCK SMALL O
- {"opar", 0x029B7}, // CIRCLED PARALLEL
- {"OpenCurlyDoubleQuote", 0x0201C}, // LEFT DOUBLE QUOTATION MARK
- {"OpenCurlyQuote", 0x02018}, // LEFT SINGLE QUOTATION MARK
- {"operp", 0x029B9}, // CIRCLED PERPENDICULAR
- {"oplus", 0x02295}, // CIRCLED PLUS
- {"or", 0x02228}, // LOGICAL OR
- {"Or", 0x02A54}, // DOUBLE LOGICAL OR
- {"orarr", 0x021BB}, // CLOCKWISE OPEN CIRCLE ARROW
- {"ord", 0x02A5D}, // LOGICAL OR WITH HORIZONTAL DASH
- {"order", 0x02134}, // SCRIPT SMALL O
- {"orderof", 0x02134}, // SCRIPT SMALL O
- {"ordf", 0x000AA}, // FEMININE ORDINAL INDICATOR
- {"ordm", 0x000BA}, // MASCULINE ORDINAL INDICATOR
- {"origof", 0x022B6}, // ORIGINAL OF
- {"oror", 0x02A56}, // TWO INTERSECTING LOGICAL OR
- {"orslope", 0x02A57}, // SLOPING LARGE OR
- {"orv", 0x02A5B}, // LOGICAL OR WITH MIDDLE STEM
- {"oS", 0x024C8}, // CIRCLED LATIN CAPITAL LETTER S
- {"oscr", 0x02134}, // SCRIPT SMALL O
- {"Oscr", 0x1D4AA}, // MATHEMATICAL SCRIPT CAPITAL O
- {"Oslash", 0x000D8}, // LATIN CAPITAL LETTER O WITH STROKE
- {"oslash", 0x000F8}, // LATIN SMALL LETTER O WITH STROKE
- {"osol", 0x02298}, // CIRCLED DIVISION SLASH
- {"Otilde", 0x000D5}, // LATIN CAPITAL LETTER O WITH TILDE
- {"otilde", 0x000F5}, // LATIN SMALL LETTER O WITH TILDE
- {"otimes", 0x02297}, // CIRCLED TIMES
- {"Otimes", 0x02A37}, // MULTIPLICATION SIGN IN DOUBLE CIRCLE
- {"otimesas", 0x02A36}, // CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT
- {"Ouml", 0x000D6}, // LATIN CAPITAL LETTER O WITH DIAERESIS
- {"ouml", 0x000F6}, // LATIN SMALL LETTER O WITH DIAERESIS
- {"ovbar", 0x0233D}, // APL FUNCTIONAL SYMBOL CIRCLE STILE
- {"OverBar", 0x0203E}, // OVERLINE
- {"OverBrace", 0x023DE}, // TOP CURLY BRACKET
- {"OverBracket", 0x023B4}, // TOP SQUARE BRACKET
- {"OverParenthesis", 0x023DC}, // TOP PARENTHESIS
- {NULL, 0}
-};
-
-static NameId namesP[]={
- {"par", 0x02225}, // PARALLEL TO
- {"para", 0x000B6}, // PILCROW SIGN
- {"parallel", 0x02225}, // PARALLEL TO
- {"parsim", 0x02AF3}, // PARALLEL WITH TILDE OPERATOR
- {"parsl", 0x02AFD}, // DOUBLE SOLIDUS OPERATOR
- {"part", 0x02202}, // PARTIAL DIFFERENTIAL
- {"PartialD", 0x02202}, // PARTIAL DIFFERENTIAL
- {"Pcy", 0x0041F}, // CYRILLIC CAPITAL LETTER PE
- {"pcy", 0x0043F}, // CYRILLIC SMALL LETTER PE
- {"percnt", 0x00025}, // PERCENT SIGN
- {"period", 0x0002E}, // FULL STOP
- {"permil", 0x02030}, // PER MILLE SIGN
- {"perp", 0x022A5}, // UP TACK
- {"pertenk", 0x02031}, // PER TEN THOUSAND SIGN
- {"Pfr", 0x1D513}, // MATHEMATICAL FRAKTUR CAPITAL P
- {"pfr", 0x1D52D}, // MATHEMATICAL FRAKTUR SMALL P
- {"Pgr", 0x003A0}, // GREEK CAPITAL LETTER PI
- {"pgr", 0x003C0}, // GREEK SMALL LETTER PI
- {"PHgr", 0x003A6}, // GREEK CAPITAL LETTER PHI
- {"phgr", 0x003C6}, // GREEK SMALL LETTER PHI
- {"Phi", 0x003A6}, // GREEK CAPITAL LETTER PHI
- {"phi", 0x003C6}, // GREEK SMALL LETTER PHI
- {"phiv", 0x003D5}, // GREEK PHI SYMBOL
- {"phmmat", 0x02133}, // SCRIPT CAPITAL M
- {"phone", 0x0260E}, // BLACK TELEPHONE
- {"Pi", 0x003A0}, // GREEK CAPITAL LETTER PI
- {"pi", 0x003C0}, // GREEK SMALL LETTER PI
- {"pitchfork", 0x022D4}, // PITCHFORK
- {"piv", 0x003D6}, // GREEK PI SYMBOL
- {"planck", 0x0210F}, // PLANCK CONSTANT OVER TWO PI
- {"planckh", 0x0210E}, // PLANCK CONSTANT
- {"plankv", 0x0210F}, // PLANCK CONSTANT OVER TWO PI
- {"plus", 0x0002B}, // PLUS SIGN
- {"plusacir", 0x02A23}, // PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE
- {"plusb", 0x0229E}, // SQUARED PLUS
- {"pluscir", 0x02A22}, // PLUS SIGN WITH SMALL CIRCLE ABOVE
- {"plusdo", 0x02214}, // DOT PLUS
- {"plusdu", 0x02A25}, // PLUS SIGN WITH DOT BELOW
- {"pluse", 0x02A72}, // PLUS SIGN ABOVE EQUALS SIGN
- {"PlusMinus", 0x000B1}, // PLUS-MINUS SIGN
- {"plusmn", 0x000B1}, // PLUS-MINUS SIGN
- {"plussim", 0x02A26}, // PLUS SIGN WITH TILDE BELOW
- {"plustwo", 0x02A27}, // PLUS SIGN WITH SUBSCRIPT TWO
- {"pm", 0x000B1}, // PLUS-MINUS SIGN
- {"Poincareplane", 0x0210C}, // BLACK-LETTER CAPITAL H
- {"pointint", 0x02A15}, // INTEGRAL AROUND A POINT OPERATOR
- {"Popf", 0x02119}, // DOUBLE-STRUCK CAPITAL P
- {"popf", 0x1D561}, // MATHEMATICAL DOUBLE-STRUCK SMALL P
- {"pound", 0x000A3}, // POUND SIGN
- {"pr", 0x0227A}, // PRECEDES
- {"Pr", 0x02ABB}, // DOUBLE PRECEDES
- {"prap", 0x02AB7}, // PRECEDES ABOVE ALMOST EQUAL TO
- {"prcue", 0x0227C}, // PRECEDES OR EQUAL TO
- {"pre", 0x02AAF}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
- {"prE", 0x02AB3}, // PRECEDES ABOVE EQUALS SIGN
- {"prec", 0x0227A}, // PRECEDES
- {"precapprox", 0x02AB7}, // PRECEDES ABOVE ALMOST EQUAL TO
- {"preccurlyeq", 0x0227C}, // PRECEDES OR EQUAL TO
- {"Precedes", 0x0227A}, // PRECEDES
- {"PrecedesEqual", 0x02AAF}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
- {"PrecedesSlantEqual", 0x0227C}, // PRECEDES OR EQUAL TO
- {"PrecedesTilde", 0x0227E}, // PRECEDES OR EQUIVALENT TO
- {"preceq", 0x02AAF}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
- {"precnapprox", 0x02AB9}, // PRECEDES ABOVE NOT ALMOST EQUAL TO
- {"precneqq", 0x02AB5}, // PRECEDES ABOVE NOT EQUAL TO
- {"precnsim", 0x022E8}, // PRECEDES BUT NOT EQUIVALENT TO
- {"precsim", 0x0227E}, // PRECEDES OR EQUIVALENT TO
- {"prime", 0x02032}, // PRIME
- {"Prime", 0x02033}, // DOUBLE PRIME
- {"primes", 0x02119}, // DOUBLE-STRUCK CAPITAL P
- {"prnap", 0x02AB9}, // PRECEDES ABOVE NOT ALMOST EQUAL TO
- {"prnE", 0x02AB5}, // PRECEDES ABOVE NOT EQUAL TO
- {"prnsim", 0x022E8}, // PRECEDES BUT NOT EQUIVALENT TO
- {"prod", 0x0220F}, // N-ARY PRODUCT
- {"Product", 0x0220F}, // N-ARY PRODUCT
- {"profalar", 0x0232E}, // ALL AROUND-PROFILE
- {"profline", 0x02312}, // ARC
- {"profsurf", 0x02313}, // SEGMENT
- {"prop", 0x0221D}, // PROPORTIONAL TO
- {"Proportion", 0x02237}, // PROPORTION
- {"Proportional", 0x0221D}, // PROPORTIONAL TO
- {"propto", 0x0221D}, // PROPORTIONAL TO
- {"prsim", 0x0227E}, // PRECEDES OR EQUIVALENT TO
- {"prurel", 0x022B0}, // PRECEDES UNDER RELATION
- {"Pscr", 0x1D4AB}, // MATHEMATICAL SCRIPT CAPITAL P
- {"pscr", 0x1D4C5}, // MATHEMATICAL SCRIPT SMALL P
- {"PSgr", 0x003A8}, // GREEK CAPITAL LETTER PSI
- {"psgr", 0x003C8}, // GREEK SMALL LETTER PSI
- {"Psi", 0x003A8}, // GREEK CAPITAL LETTER PSI
- {"psi", 0x003C8}, // GREEK SMALL LETTER PSI
- {"puncsp", 0x02008}, // PUNCTUATION SPACE
- {NULL, 0}
-};
-
-static NameId namesQ[]={
- {"Qfr", 0x1D514}, // MATHEMATICAL FRAKTUR CAPITAL Q
- {"qfr", 0x1D52E}, // MATHEMATICAL FRAKTUR SMALL Q
- {"qint", 0x02A0C}, // QUADRUPLE INTEGRAL OPERATOR
- {"Qopf", 0x0211A}, // DOUBLE-STRUCK CAPITAL Q
- {"qopf", 0x1D562}, // MATHEMATICAL DOUBLE-STRUCK SMALL Q
- {"qprime", 0x02057}, // QUADRUPLE PRIME
- {"Qscr", 0x1D4AC}, // MATHEMATICAL SCRIPT CAPITAL Q
- {"qscr", 0x1D4C6}, // MATHEMATICAL SCRIPT SMALL Q
- {"quaternions", 0x0210D}, // DOUBLE-STRUCK CAPITAL H
- {"quatint", 0x02A16}, // QUATERNION INTEGRAL OPERATOR
- {"quest", 0x0003F}, // QUESTION MARK
- {"questeq", 0x0225F}, // QUESTIONED EQUAL TO
- {"quot", 0x00022}, // QUOTATION MARK
- {"QUOT", 0x00022}, // QUOTATION MARK
- {NULL, 0}
-};
-
-static NameId namesR[]={
- {"rAarr", 0x021DB}, // RIGHTWARDS TRIPLE ARROW
-// "race", 0x0223D;0x00331}, // REVERSED TILDE with underline
- {"Racute", 0x00154}, // LATIN CAPITAL LETTER R WITH ACUTE
- {"racute", 0x00155}, // LATIN SMALL LETTER R WITH ACUTE
- {"radic", 0x0221A}, // SQUARE ROOT
- {"raemptyv", 0x029B3}, // EMPTY SET WITH RIGHT ARROW ABOVE
- {"rang", 0x027E9}, // MATHEMATICAL RIGHT ANGLE BRACKET
- {"Rang", 0x027EB}, // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
- {"rangd", 0x02992}, // RIGHT ANGLE BRACKET WITH DOT
- {"range", 0x029A5}, // REVERSED ANGLE WITH UNDERBAR
- {"rangle", 0x027E9}, // MATHEMATICAL RIGHT ANGLE BRACKET
- {"raquo", 0x000BB}, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- {"rarr", 0x02192}, // RIGHTWARDS ARROW
- {"Rarr", 0x021A0}, // RIGHTWARDS TWO HEADED ARROW
- {"rArr", 0x021D2}, // RIGHTWARDS DOUBLE ARROW
- {"rarrap", 0x02975}, // RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO
- {"rarrb", 0x021E5}, // RIGHTWARDS ARROW TO BAR
- {"rarrbfs", 0x02920}, // RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND
- {"rarrc", 0x02933}, // WAVE ARROW POINTING DIRECTLY RIGHT
- {"rarrfs", 0x0291E}, // RIGHTWARDS ARROW TO BLACK DIAMOND
- {"rarrhk", 0x021AA}, // RIGHTWARDS ARROW WITH HOOK
- {"rarrlp", 0x021AC}, // RIGHTWARDS ARROW WITH LOOP
- {"rarrpl", 0x02945}, // RIGHTWARDS ARROW WITH PLUS BELOW
- {"rarrsim", 0x02974}, // RIGHTWARDS ARROW ABOVE TILDE OPERATOR
- {"rarrtl", 0x021A3}, // RIGHTWARDS ARROW WITH TAIL
- {"Rarrtl", 0x02916}, // RIGHTWARDS TWO-HEADED ARROW WITH TAIL
- {"rarrw", 0x0219D}, // RIGHTWARDS WAVE ARROW
- {"ratail", 0x0291A}, // RIGHTWARDS ARROW-TAIL
- {"rAtail", 0x0291C}, // RIGHTWARDS DOUBLE ARROW-TAIL
- {"ratio", 0x02236}, // RATIO
- {"rationals", 0x0211A}, // DOUBLE-STRUCK CAPITAL Q
- {"rbarr", 0x0290D}, // RIGHTWARDS DOUBLE DASH ARROW
- {"rBarr", 0x0290F}, // RIGHTWARDS TRIPLE DASH ARROW
- {"RBarr", 0x02910}, // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW
- {"rbbrk", 0x02773}, // LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
- {"rbrace", 0x0007D}, // RIGHT CURLY BRACKET
- {"rbrack", 0x0005D}, // RIGHT SQUARE BRACKET
- {"rbrke", 0x0298C}, // RIGHT SQUARE BRACKET WITH UNDERBAR
- {"rbrksld", 0x0298E}, // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
- {"rbrkslu", 0x02990}, // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
- {"Rcaron", 0x00158}, // LATIN CAPITAL LETTER R WITH CARON
- {"rcaron", 0x00159}, // LATIN SMALL LETTER R WITH CARON
- {"Rcedil", 0x00156}, // LATIN CAPITAL LETTER R WITH CEDILLA
- {"rcedil", 0x00157}, // LATIN SMALL LETTER R WITH CEDILLA
- {"rceil", 0x02309}, // RIGHT CEILING
- {"rcub", 0x0007D}, // RIGHT CURLY BRACKET
- {"Rcy", 0x00420}, // CYRILLIC CAPITAL LETTER ER
- {"rcy", 0x00440}, // CYRILLIC SMALL LETTER ER
- {"rdca", 0x02937}, // ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS
- {"rdldhar", 0x02969}, // RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN
- {"rdquo", 0x0201D}, // RIGHT DOUBLE QUOTATION MARK
- {"rdquor", 0x0201D}, // RIGHT DOUBLE QUOTATION MARK
- {"rdsh", 0x021B3}, // DOWNWARDS ARROW WITH TIP RIGHTWARDS
- {"Re", 0x0211C}, // BLACK-LETTER CAPITAL R
- {"real", 0x0211C}, // BLACK-LETTER CAPITAL R
- {"realine", 0x0211B}, // SCRIPT CAPITAL R
- {"realpart", 0x0211C}, // BLACK-LETTER CAPITAL R
- {"reals", 0x0211D}, // DOUBLE-STRUCK CAPITAL R
- {"rect", 0x025AD}, // WHITE RECTANGLE
- {"reg", 0x000AE}, // REGISTERED SIGN
- {"REG", 0x000AE}, // REGISTERED SIGN
- {"ReverseElement", 0x0220B}, // CONTAINS AS MEMBER
- {"ReverseEquilibrium", 0x021CB}, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
- {"ReverseUpEquilibrium", 0x0296F}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
- {"rfisht", 0x0297D}, // RIGHT FISH TAIL
- {"rfloor", 0x0230B}, // RIGHT FLOOR
- {"Rfr", 0x0211C}, // BLACK-LETTER CAPITAL R
- {"rfr", 0x1D52F}, // MATHEMATICAL FRAKTUR SMALL R
- {"Rgr", 0x003A1}, // GREEK CAPITAL LETTER RHO
- {"rgr", 0x003C1}, // GREEK SMALL LETTER RHO
- {"rHar", 0x02964}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
- {"rhard", 0x021C1}, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
- {"rharu", 0x021C0}, // RIGHTWARDS HARPOON WITH BARB UPWARDS
- {"rharul", 0x0296C}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
- {"Rho", 0x003A1}, // GREEK CAPITAL LETTER RHO
- {"rho", 0x003C1}, // GREEK SMALL LETTER RHO
- {"rhov", 0x003F1}, // GREEK RHO SYMBOL
- {"RightAngleBracket", 0x027E9}, // MATHEMATICAL RIGHT ANGLE BRACKET
- {"rightarrow", 0x02192}, // RIGHTWARDS ARROW
- {"RightArrow", 0x02192}, // RIGHTWARDS ARROW
- {"Rightarrow", 0x021D2}, // RIGHTWARDS DOUBLE ARROW
- {"RightArrowBar", 0x021E5}, // RIGHTWARDS ARROW TO BAR
- {"RightArrowLeftArrow", 0x021C4}, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
- {"rightarrowtail", 0x021A3}, // RIGHTWARDS ARROW WITH TAIL
- {"RightCeiling", 0x02309}, // RIGHT CEILING
- {"RightDoubleBracket", 0x027E7}, // MATHEMATICAL RIGHT WHITE SQUARE BRACKET
- {"RightDownTeeVector", 0x0295D}, // DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR
- {"RightDownVector", 0x021C2}, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
- {"RightDownVectorBar", 0x02955}, // DOWNWARDS HARPOON WITH BARB RIGHT TO BAR
- {"RightFloor", 0x0230B}, // RIGHT FLOOR
- {"rightharpoondown", 0x021C1}, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
- {"rightharpoonup", 0x021C0}, // RIGHTWARDS HARPOON WITH BARB UPWARDS
- {"rightleftarrows", 0x021C4}, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
- {"rightleftharpoons", 0x021CC}, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
- {"rightrightarrows", 0x021C9}, // RIGHTWARDS PAIRED ARROWS
- {"rightsquigarrow", 0x0219D}, // RIGHTWARDS WAVE ARROW
- {"RightTee", 0x022A2}, // RIGHT TACK
- {"RightTeeArrow", 0x021A6}, // RIGHTWARDS ARROW FROM BAR
- {"RightTeeVector", 0x0295B}, // RIGHTWARDS HARPOON WITH BARB UP FROM BAR
- {"rightthreetimes", 0x022CC}, // RIGHT SEMIDIRECT PRODUCT
- {"RightTriangle", 0x022B3}, // CONTAINS AS NORMAL SUBGROUP
- {"RightTriangleBar", 0x029D0}, // VERTICAL BAR BESIDE RIGHT TRIANGLE
- {"RightTriangleEqual", 0x022B5}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
- {"RightUpDownVector", 0x0294F}, // UP BARB RIGHT DOWN BARB RIGHT HARPOON
- {"RightUpTeeVector", 0x0295C}, // UPWARDS HARPOON WITH BARB RIGHT FROM BAR
- {"RightUpVector", 0x021BE}, // UPWARDS HARPOON WITH BARB RIGHTWARDS
- {"RightUpVectorBar", 0x02954}, // UPWARDS HARPOON WITH BARB RIGHT TO BAR
- {"RightVector", 0x021C0}, // RIGHTWARDS HARPOON WITH BARB UPWARDS
- {"RightVectorBar", 0x02953}, // RIGHTWARDS HARPOON WITH BARB UP TO BAR
- {"ring", 0x002DA}, // RING ABOVE
- {"risingdotseq", 0x02253}, // IMAGE OF OR APPROXIMATELY EQUAL TO
- {"rlarr", 0x021C4}, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
- {"rlhar", 0x021CC}, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
- {"rlm", 0x0200F}, // RIGHT-TO-LEFT MARK
- {"rmoust", 0x023B1}, // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION
- {"rmoustache", 0x023B1}, // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION
- {"rnmid", 0x02AEE}, // DOES NOT DIVIDE WITH REVERSED NEGATION SLASH
- {"roang", 0x027ED}, // MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
- {"roarr", 0x021FE}, // RIGHTWARDS OPEN-HEADED ARROW
- {"robrk", 0x027E7}, // MATHEMATICAL RIGHT WHITE SQUARE BRACKET
- {"ropar", 0x02986}, // RIGHT WHITE PARENTHESIS
- {"Ropf", 0x0211D}, // DOUBLE-STRUCK CAPITAL R
- {"ropf", 0x1D563}, // MATHEMATICAL DOUBLE-STRUCK SMALL R
- {"roplus", 0x02A2E}, // PLUS SIGN IN RIGHT HALF CIRCLE
- {"rotimes", 0x02A35}, // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE
- {"RoundImplies", 0x02970}, // RIGHT DOUBLE ARROW WITH ROUNDED HEAD
- {"rpar", 0x00029}, // RIGHT PARENTHESIS
- {"rpargt", 0x02994}, // RIGHT ARC GREATER-THAN BRACKET
- {"rppolint", 0x02A12}, // LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE
- {"rrarr", 0x021C9}, // RIGHTWARDS PAIRED ARROWS
- {"Rrightarrow", 0x021DB}, // RIGHTWARDS TRIPLE ARROW
- {"rsaquo", 0x0203A}, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
- {"Rscr", 0x0211B}, // SCRIPT CAPITAL R
- {"rscr", 0x1D4C7}, // MATHEMATICAL SCRIPT SMALL R
- {"rsh", 0x021B1}, // UPWARDS ARROW WITH TIP RIGHTWARDS
- {"Rsh", 0x021B1}, // UPWARDS ARROW WITH TIP RIGHTWARDS
- {"rsqb", 0x0005D}, // RIGHT SQUARE BRACKET
- {"rsquo", 0x02019}, // RIGHT SINGLE QUOTATION MARK
- {"rsquor", 0x02019}, // RIGHT SINGLE QUOTATION MARK
- {"rthree", 0x022CC}, // RIGHT SEMIDIRECT PRODUCT
- {"rtimes", 0x022CA}, // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
- {"rtri", 0x025B9}, // WHITE RIGHT-POINTING SMALL TRIANGLE
- {"rtrie", 0x022B5}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
- {"rtrif", 0x025B8}, // BLACK RIGHT-POINTING SMALL TRIANGLE
- {"rtriltri", 0x029CE}, // RIGHT TRIANGLE ABOVE LEFT TRIANGLE
- {"RuleDelayed", 0x029F4}, // RULE-DELAYED
- {"ruluhar", 0x02968}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP
- {"rx", 0x0211E}, // PRESCRIPTION TAKE
- {NULL, 0}
-};
-
-static NameId namesS[]={
- {"Sacute", 0x0015A}, // LATIN CAPITAL LETTER S WITH ACUTE
- {"sacute", 0x0015B}, // LATIN SMALL LETTER S WITH ACUTE
- {"sbquo", 0x0201A}, // SINGLE LOW-9 QUOTATION MARK
- {"sc", 0x0227B}, // SUCCEEDS
- {"Sc", 0x02ABC}, // DOUBLE SUCCEEDS
- {"scap", 0x02AB8}, // SUCCEEDS ABOVE ALMOST EQUAL TO
- {"Scaron", 0x00160}, // LATIN CAPITAL LETTER S WITH CARON
- {"scaron", 0x00161}, // LATIN SMALL LETTER S WITH CARON
- {"sccue", 0x0227D}, // SUCCEEDS OR EQUAL TO
- {"sce", 0x02AB0}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
- {"scE", 0x02AB4}, // SUCCEEDS ABOVE EQUALS SIGN
- {"Scedil", 0x0015E}, // LATIN CAPITAL LETTER S WITH CEDILLA
- {"scedil", 0x0015F}, // LATIN SMALL LETTER S WITH CEDILLA
- {"Scirc", 0x0015C}, // LATIN CAPITAL LETTER S WITH CIRCUMFLEX
- {"scirc", 0x0015D}, // LATIN SMALL LETTER S WITH CIRCUMFLEX
- {"scnap", 0x02ABA}, // SUCCEEDS ABOVE NOT ALMOST EQUAL TO
- {"scnE", 0x02AB6}, // SUCCEEDS ABOVE NOT EQUAL TO
- {"scnsim", 0x022E9}, // SUCCEEDS BUT NOT EQUIVALENT TO
- {"scpolint", 0x02A13}, // LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE
- {"scsim", 0x0227F}, // SUCCEEDS OR EQUIVALENT TO
- {"Scy", 0x00421}, // CYRILLIC CAPITAL LETTER ES
- {"scy", 0x00441}, // CYRILLIC SMALL LETTER ES
- {"sdot", 0x022C5}, // DOT OPERATOR
- {"sdotb", 0x022A1}, // SQUARED DOT OPERATOR
- {"sdote", 0x02A66}, // EQUALS SIGN WITH DOT BELOW
- {"searhk", 0x02925}, // SOUTH EAST ARROW WITH HOOK
- {"searr", 0x02198}, // SOUTH EAST ARROW
- {"seArr", 0x021D8}, // SOUTH EAST DOUBLE ARROW
- {"searrow", 0x02198}, // SOUTH EAST ARROW
- {"sect", 0x000A7}, // SECTION SIGN
- {"semi", 0x0003B}, // SEMICOLON
- {"seswar", 0x02929}, // SOUTH EAST ARROW AND SOUTH WEST ARROW
- {"setminus", 0x02216}, // SET MINUS
- {"setmn", 0x02216}, // SET MINUS
- {"sext", 0x02736}, // SIX POINTED BLACK STAR
- {"sfgr", 0x003C2}, // GREEK SMALL LETTER FINAL SIGMA
- {"Sfr", 0x1D516}, // MATHEMATICAL FRAKTUR CAPITAL S
- {"sfr", 0x1D530}, // MATHEMATICAL FRAKTUR SMALL S
- {"sfrown", 0x02322}, // FROWN
- {"Sgr", 0x003A3}, // GREEK CAPITAL LETTER SIGMA
- {"sgr", 0x003C3}, // GREEK SMALL LETTER SIGMA
- {"sharp", 0x0266F}, // MUSIC SHARP SIGN
- {"SHCHcy", 0x00429}, // CYRILLIC CAPITAL LETTER SHCHA
- {"shchcy", 0x00449}, // CYRILLIC SMALL LETTER SHCHA
- {"SHcy", 0x00428}, // CYRILLIC CAPITAL LETTER SHA
- {"shcy", 0x00448}, // CYRILLIC SMALL LETTER SHA
- {"ShortDownArrow", 0x02193}, // DOWNWARDS ARROW
- {"ShortLeftArrow", 0x02190}, // LEFTWARDS ARROW
- {"shortmid", 0x02223}, // DIVIDES
- {"shortparallel", 0x02225}, // PARALLEL TO
- {"ShortRightArrow", 0x02192}, // RIGHTWARDS ARROW
- {"ShortUpArrow", 0x02191}, // UPWARDS ARROW
- {"shy", 0x000AD}, // SOFT HYPHEN
- {"Sigma", 0x003A3}, // GREEK CAPITAL LETTER SIGMA
- {"sigma", 0x003C3}, // GREEK SMALL LETTER SIGMA
- {"sigmaf", 0x003C2}, // GREEK SMALL LETTER FINAL SIGMA
- {"sigmav", 0x003C2}, // GREEK SMALL LETTER FINAL SIGMA
- {"sim", 0x0223C}, // TILDE OPERATOR
- {"simdot", 0x02A6A}, // TILDE OPERATOR WITH DOT ABOVE
- {"sime", 0x02243}, // ASYMPTOTICALLY EQUAL TO
- {"simeq", 0x02243}, // ASYMPTOTICALLY EQUAL TO
- {"simg", 0x02A9E}, // SIMILAR OR GREATER-THAN
- {"simgE", 0x02AA0}, // SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN
- {"siml", 0x02A9D}, // SIMILAR OR LESS-THAN
- {"simlE", 0x02A9F}, // SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN
- {"simne", 0x02246}, // APPROXIMATELY BUT NOT ACTUALLY EQUAL TO
- {"simplus", 0x02A24}, // PLUS SIGN WITH TILDE ABOVE
- {"simrarr", 0x02972}, // TILDE OPERATOR ABOVE RIGHTWARDS ARROW
- {"slarr", 0x02190}, // LEFTWARDS ARROW
- {"SmallCircle", 0x02218}, // RING OPERATOR
- {"smallsetminus", 0x02216}, // SET MINUS
- {"smashp", 0x02A33}, // SMASH PRODUCT
- {"smeparsl", 0x029E4}, // EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE
- {"smid", 0x02223}, // DIVIDES
- {"smile", 0x02323}, // SMILE
- {"smt", 0x02AAA}, // SMALLER THAN
- {"smte", 0x02AAC}, // SMALLER THAN OR EQUAL TO
-// "smtes", 0x02AAC;0x0FE00}, // SMALLER THAN OR slanted EQUAL
- {"SOFTcy", 0x0042C}, // CYRILLIC CAPITAL LETTER SOFT SIGN
- {"softcy", 0x0044C}, // CYRILLIC SMALL LETTER SOFT SIGN
- {"sol", 0x0002F}, // SOLIDUS
- {"solb", 0x029C4}, // SQUARED RISING DIAGONAL SLASH
- {"solbar", 0x0233F}, // APL FUNCTIONAL SYMBOL SLASH BAR
- {"Sopf", 0x1D54A}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL S
- {"sopf", 0x1D564}, // MATHEMATICAL DOUBLE-STRUCK SMALL S
- {"spades", 0x02660}, // BLACK SPADE SUIT
- {"spadesuit", 0x02660}, // BLACK SPADE SUIT
- {"spar", 0x02225}, // PARALLEL TO
- {"sqcap", 0x02293}, // SQUARE CAP
-// "sqcaps", 0x02293;0x0FE00}, // SQUARE CAP with serifs
- {"sqcup", 0x02294}, // SQUARE CUP
-// "sqcups", 0x02294;0x0FE00}, // SQUARE CUP with serifs
- {"Sqrt", 0x0221A}, // SQUARE ROOT
- {"sqsub", 0x0228F}, // SQUARE IMAGE OF
- {"sqsube", 0x02291}, // SQUARE IMAGE OF OR EQUAL TO
- {"sqsubset", 0x0228F}, // SQUARE IMAGE OF
- {"sqsubseteq", 0x02291}, // SQUARE IMAGE OF OR EQUAL TO
- {"sqsup", 0x02290}, // SQUARE ORIGINAL OF
- {"sqsupe", 0x02292}, // SQUARE ORIGINAL OF OR EQUAL TO
- {"sqsupset", 0x02290}, // SQUARE ORIGINAL OF
- {"sqsupseteq", 0x02292}, // SQUARE ORIGINAL OF OR EQUAL TO
- {"squ", 0x025A1}, // WHITE SQUARE
- {"square", 0x025A1}, // WHITE SQUARE
- {"Square", 0x025A1}, // WHITE SQUARE
- {"SquareIntersection", 0x02293}, // SQUARE CAP
- {"SquareSubset", 0x0228F}, // SQUARE IMAGE OF
- {"SquareSubsetEqual", 0x02291}, // SQUARE IMAGE OF OR EQUAL TO
- {"SquareSuperset", 0x02290}, // SQUARE ORIGINAL OF
- {"SquareSupersetEqual", 0x02292}, // SQUARE ORIGINAL OF OR EQUAL TO
- {"SquareUnion", 0x02294}, // SQUARE CUP
- {"squarf", 0x025AA}, // BLACK SMALL SQUARE
- {"squf", 0x025AA}, // BLACK SMALL SQUARE
- {"srarr", 0x02192}, // RIGHTWARDS ARROW
- {"Sscr", 0x1D4AE}, // MATHEMATICAL SCRIPT CAPITAL S
- {"sscr", 0x1D4C8}, // MATHEMATICAL SCRIPT SMALL S
- {"ssetmn", 0x02216}, // SET MINUS
- {"ssmile", 0x02323}, // SMILE
- {"sstarf", 0x022C6}, // STAR OPERATOR
- {"Star", 0x022C6}, // STAR OPERATOR
- {"star", 0x02606}, // WHITE STAR
- {"starf", 0x02605}, // BLACK STAR
- {"straightepsilon", 0x003F5}, // GREEK LUNATE EPSILON SYMBOL
- {"straightphi", 0x003D5}, // GREEK PHI SYMBOL
- {"strns", 0x000AF}, // MACRON
- {"sub", 0x02282}, // SUBSET OF
- {"Sub", 0x022D0}, // DOUBLE SUBSET
- {"subdot", 0x02ABD}, // SUBSET WITH DOT
- {"sube", 0x02286}, // SUBSET OF OR EQUAL TO
- {"subE", 0x02AC5}, // SUBSET OF ABOVE EQUALS SIGN
- {"subedot", 0x02AC3}, // SUBSET OF OR EQUAL TO WITH DOT ABOVE
- {"submult", 0x02AC1}, // SUBSET WITH MULTIPLICATION SIGN BELOW
- {"subne", 0x0228A}, // SUBSET OF WITH NOT EQUAL TO
- {"subnE", 0x02ACB}, // SUBSET OF ABOVE NOT EQUAL TO
- {"subplus", 0x02ABF}, // SUBSET WITH PLUS SIGN BELOW
- {"subrarr", 0x02979}, // SUBSET ABOVE RIGHTWARDS ARROW
- {"subset", 0x02282}, // SUBSET OF
- {"Subset", 0x022D0}, // DOUBLE SUBSET
- {"subseteq", 0x02286}, // SUBSET OF OR EQUAL TO
- {"subseteqq", 0x02AC5}, // SUBSET OF ABOVE EQUALS SIGN
- {"SubsetEqual", 0x02286}, // SUBSET OF OR EQUAL TO
- {"subsetneq", 0x0228A}, // SUBSET OF WITH NOT EQUAL TO
- {"subsetneqq", 0x02ACB}, // SUBSET OF ABOVE NOT EQUAL TO
- {"subsim", 0x02AC7}, // SUBSET OF ABOVE TILDE OPERATOR
- {"subsub", 0x02AD5}, // SUBSET ABOVE SUBSET
- {"subsup", 0x02AD3}, // SUBSET ABOVE SUPERSET
- {"succ", 0x0227B}, // SUCCEEDS
- {"succapprox", 0x02AB8}, // SUCCEEDS ABOVE ALMOST EQUAL TO
- {"succcurlyeq", 0x0227D}, // SUCCEEDS OR EQUAL TO
- {"Succeeds", 0x0227B}, // SUCCEEDS
- {"SucceedsEqual", 0x02AB0}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
- {"SucceedsSlantEqual", 0x0227D}, // SUCCEEDS OR EQUAL TO
- {"SucceedsTilde", 0x0227F}, // SUCCEEDS OR EQUIVALENT TO
- {"succeq", 0x02AB0}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
- {"succnapprox", 0x02ABA}, // SUCCEEDS ABOVE NOT ALMOST EQUAL TO
- {"succneqq", 0x02AB6}, // SUCCEEDS ABOVE NOT EQUAL TO
- {"succnsim", 0x022E9}, // SUCCEEDS BUT NOT EQUIVALENT TO
- {"succsim", 0x0227F}, // SUCCEEDS OR EQUIVALENT TO
- {"SuchThat", 0x0220B}, // CONTAINS AS MEMBER
- {"sum", 0x02211}, // N-ARY SUMMATION
- {"Sum", 0x02211}, // N-ARY SUMMATION
- {"sung", 0x0266A}, // EIGHTH NOTE
- {"sup", 0x02283}, // SUPERSET OF
- {"Sup", 0x022D1}, // DOUBLE SUPERSET
- {"sup1", 0x000B9}, // SUPERSCRIPT ONE
- {"sup2", 0x000B2}, // SUPERSCRIPT TWO
- {"sup3", 0x000B3}, // SUPERSCRIPT THREE
- {"supdot", 0x02ABE}, // SUPERSET WITH DOT
- {"supdsub", 0x02AD8}, // SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET
- {"supe", 0x02287}, // SUPERSET OF OR EQUAL TO
- {"supE", 0x02AC6}, // SUPERSET OF ABOVE EQUALS SIGN
- {"supedot", 0x02AC4}, // SUPERSET OF OR EQUAL TO WITH DOT ABOVE
- {"Superset", 0x02283}, // SUPERSET OF
- {"SupersetEqual", 0x02287}, // SUPERSET OF OR EQUAL TO
- {"suphsol", 0x027C9}, // SUPERSET PRECEDING SOLIDUS
- {"suphsub", 0x02AD7}, // SUPERSET BESIDE SUBSET
- {"suplarr", 0x0297B}, // SUPERSET ABOVE LEFTWARDS ARROW
- {"supmult", 0x02AC2}, // SUPERSET WITH MULTIPLICATION SIGN BELOW
- {"supne", 0x0228B}, // SUPERSET OF WITH NOT EQUAL TO
- {"supnE", 0x02ACC}, // SUPERSET OF ABOVE NOT EQUAL TO
- {"supplus", 0x02AC0}, // SUPERSET WITH PLUS SIGN BELOW
- {"supset", 0x02283}, // SUPERSET OF
- {"Supset", 0x022D1}, // DOUBLE SUPERSET
- {"supseteq", 0x02287}, // SUPERSET OF OR EQUAL TO
- {"supseteqq", 0x02AC6}, // SUPERSET OF ABOVE EQUALS SIGN
- {"supsetneq", 0x0228B}, // SUPERSET OF WITH NOT EQUAL TO
- {"supsetneqq", 0x02ACC}, // SUPERSET OF ABOVE NOT EQUAL TO
- {"supsim", 0x02AC8}, // SUPERSET OF ABOVE TILDE OPERATOR
- {"supsub", 0x02AD4}, // SUPERSET ABOVE SUBSET
- {"supsup", 0x02AD6}, // SUPERSET ABOVE SUPERSET
- {"swarhk", 0x02926}, // SOUTH WEST ARROW WITH HOOK
- {"swarr", 0x02199}, // SOUTH WEST ARROW
- {"swArr", 0x021D9}, // SOUTH WEST DOUBLE ARROW
- {"swarrow", 0x02199}, // SOUTH WEST ARROW
- {"swnwar", 0x0292A}, // SOUTH WEST ARROW AND NORTH WEST ARROW
- {"szlig", 0x000DF}, // LATIN SMALL LETTER SHARP S
- {NULL, 0}
-};
-
-static NameId namesT[]={
- {"Tab", 0x00009}, // CHARACTER TABULATION
- {"target", 0x02316}, // POSITION INDICATOR
- {"Tau", 0x003A4}, // GREEK CAPITAL LETTER TAU
- {"tau", 0x003C4}, // GREEK SMALL LETTER TAU
- {"tbrk", 0x023B4}, // TOP SQUARE BRACKET
- {"Tcaron", 0x00164}, // LATIN CAPITAL LETTER T WITH CARON
- {"tcaron", 0x00165}, // LATIN SMALL LETTER T WITH CARON
- {"Tcedil", 0x00162}, // LATIN CAPITAL LETTER T WITH CEDILLA
- {"tcedil", 0x00163}, // LATIN SMALL LETTER T WITH CEDILLA
- {"Tcy", 0x00422}, // CYRILLIC CAPITAL LETTER TE
- {"tcy", 0x00442}, // CYRILLIC SMALL LETTER TE
- {"tdot", 0x020DB}, // COMBINING THREE DOTS ABOVE
- {"telrec", 0x02315}, // TELEPHONE RECORDER
- {"Tfr", 0x1D517}, // MATHEMATICAL FRAKTUR CAPITAL T
- {"tfr", 0x1D531}, // MATHEMATICAL FRAKTUR SMALL T
- {"Tgr", 0x003A4}, // GREEK CAPITAL LETTER TAU
- {"tgr", 0x003C4}, // GREEK SMALL LETTER TAU
- {"there4", 0x02234}, // THEREFORE
- {"therefore", 0x02234}, // THEREFORE
- {"Therefore", 0x02234}, // THEREFORE
- {"Theta", 0x00398}, // GREEK CAPITAL LETTER THETA
- {"theta", 0x003B8}, // GREEK SMALL LETTER THETA
- {"thetasym", 0x003D1}, // GREEK THETA SYMBOL
- {"thetav", 0x003D1}, // GREEK THETA SYMBOL
- {"THgr", 0x00398}, // GREEK CAPITAL LETTER THETA
- {"thgr", 0x003B8}, // GREEK SMALL LETTER THETA
- {"thickapprox", 0x02248}, // ALMOST EQUAL TO
- {"thicksim", 0x0223C}, // TILDE OPERATOR
-// "ThickSpace", 0x0205F;0x0200A}, // space of width 5/18 em
- {"thinsp", 0x02009}, // THIN SPACE
- {"ThinSpace", 0x02009}, // THIN SPACE
- {"thkap", 0x02248}, // ALMOST EQUAL TO
- {"thksim", 0x0223C}, // TILDE OPERATOR
- {"THORN", 0x000DE}, // LATIN CAPITAL LETTER THORN
- {"thorn", 0x000FE}, // LATIN SMALL LETTER THORN
- {"tilde", 0x002DC}, // SMALL TILDE
- {"Tilde", 0x0223C}, // TILDE OPERATOR
- {"TildeEqual", 0x02243}, // ASYMPTOTICALLY EQUAL TO
- {"TildeFullEqual", 0x02245}, // APPROXIMATELY EQUAL TO
- {"TildeTilde", 0x02248}, // ALMOST EQUAL TO
- {"times", 0x000D7}, // MULTIPLICATION SIGN
- {"timesb", 0x022A0}, // SQUARED TIMES
- {"timesbar", 0x02A31}, // MULTIPLICATION SIGN WITH UNDERBAR
- {"timesd", 0x02A30}, // MULTIPLICATION SIGN WITH DOT ABOVE
- {"tint", 0x0222D}, // TRIPLE INTEGRAL
- {"toea", 0x02928}, // NORTH EAST ARROW AND SOUTH EAST ARROW
- {"top", 0x022A4}, // DOWN TACK
- {"topbot", 0x02336}, // APL FUNCTIONAL SYMBOL I-BEAM
- {"topcir", 0x02AF1}, // DOWN TACK WITH CIRCLE BELOW
- {"Topf", 0x1D54B}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL T
- {"topf", 0x1D565}, // MATHEMATICAL DOUBLE-STRUCK SMALL T
- {"topfork", 0x02ADA}, // PITCHFORK WITH TEE TOP
- {"tosa", 0x02929}, // SOUTH EAST ARROW AND SOUTH WEST ARROW
- {"tprime", 0x02034}, // TRIPLE PRIME
- {"trade", 0x02122}, // TRADE MARK SIGN
- {"TRADE", 0x02122}, // TRADE MARK SIGN
- {"triangle", 0x025B5}, // WHITE UP-POINTING SMALL TRIANGLE
- {"triangledown", 0x025BF}, // WHITE DOWN-POINTING SMALL TRIANGLE
- {"triangleleft", 0x025C3}, // WHITE LEFT-POINTING SMALL TRIANGLE
- {"trianglelefteq", 0x022B4}, // NORMAL SUBGROUP OF OR EQUAL TO
- {"triangleq", 0x0225C}, // DELTA EQUAL TO
- {"triangleright", 0x025B9}, // WHITE RIGHT-POINTING SMALL TRIANGLE
- {"trianglerighteq", 0x022B5}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
- {"tridot", 0x025EC}, // WHITE UP-POINTING TRIANGLE WITH DOT
- {"trie", 0x0225C}, // DELTA EQUAL TO
- {"triminus", 0x02A3A}, // MINUS SIGN IN TRIANGLE
- {"TripleDot", 0x020DB}, // COMBINING THREE DOTS ABOVE
- {"triplus", 0x02A39}, // PLUS SIGN IN TRIANGLE
- {"trisb", 0x029CD}, // TRIANGLE WITH SERIFS AT BOTTOM
- {"tritime", 0x02A3B}, // MULTIPLICATION SIGN IN TRIANGLE
- {"trpezium", 0x023E2}, // WHITE TRAPEZIUM
- {"Tscr", 0x1D4AF}, // MATHEMATICAL SCRIPT CAPITAL T
- {"tscr", 0x1D4C9}, // MATHEMATICAL SCRIPT SMALL T
- {"TScy", 0x00426}, // CYRILLIC CAPITAL LETTER TSE
- {"tscy", 0x00446}, // CYRILLIC SMALL LETTER TSE
- {"TSHcy", 0x0040B}, // CYRILLIC CAPITAL LETTER TSHE
- {"tshcy", 0x0045B}, // CYRILLIC SMALL LETTER TSHE
- {"Tstrok", 0x00166}, // LATIN CAPITAL LETTER T WITH STROKE
- {"tstrok", 0x00167}, // LATIN SMALL LETTER T WITH STROKE
- {"twixt", 0x0226C}, // BETWEEN
- {"twoheadleftarrow", 0x0219E}, // LEFTWARDS TWO HEADED ARROW
- {"twoheadrightarrow", 0x021A0}, // RIGHTWARDS TWO HEADED ARROW
- {NULL, 0}
-};
-
-static NameId namesU[]={
- {"Uacgr", 0x0038E}, // GREEK CAPITAL LETTER UPSILON WITH TONOS
- {"uacgr", 0x003CD}, // GREEK SMALL LETTER UPSILON WITH TONOS
- {"Uacute", 0x000DA}, // LATIN CAPITAL LETTER U WITH ACUTE
- {"uacute", 0x000FA}, // LATIN SMALL LETTER U WITH ACUTE
- {"uarr", 0x02191}, // UPWARDS ARROW
- {"Uarr", 0x0219F}, // UPWARDS TWO HEADED ARROW
- {"uArr", 0x021D1}, // UPWARDS DOUBLE ARROW
- {"Uarrocir", 0x02949}, // UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE
- {"Ubrcy", 0x0040E}, // CYRILLIC CAPITAL LETTER SHORT U
- {"ubrcy", 0x0045E}, // CYRILLIC SMALL LETTER SHORT U
- {"Ubreve", 0x0016C}, // LATIN CAPITAL LETTER U WITH BREVE
- {"ubreve", 0x0016D}, // LATIN SMALL LETTER U WITH BREVE
- {"Ucirc", 0x000DB}, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
- {"ucirc", 0x000FB}, // LATIN SMALL LETTER U WITH CIRCUMFLEX
- {"Ucy", 0x00423}, // CYRILLIC CAPITAL LETTER U
- {"ucy", 0x00443}, // CYRILLIC SMALL LETTER U
- {"udarr", 0x021C5}, // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
- {"Udblac", 0x00170}, // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
- {"udblac", 0x00171}, // LATIN SMALL LETTER U WITH DOUBLE ACUTE
- {"udhar", 0x0296E}, // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
- {"udiagr", 0x003B0}, // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
- {"Udigr", 0x003AB}, // GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
- {"udigr", 0x003CB}, // GREEK SMALL LETTER UPSILON WITH DIALYTIKA
- {"ufisht", 0x0297E}, // UP FISH TAIL
- {"Ufr", 0x1D518}, // MATHEMATICAL FRAKTUR CAPITAL U
- {"ufr", 0x1D532}, // MATHEMATICAL FRAKTUR SMALL U
- {"Ugr", 0x003A5}, // GREEK CAPITAL LETTER UPSILON
- {"ugr", 0x003C5}, // GREEK SMALL LETTER UPSILON
- {"Ugrave", 0x000D9}, // LATIN CAPITAL LETTER U WITH GRAVE
- {"ugrave", 0x000F9}, // LATIN SMALL LETTER U WITH GRAVE
- {"uHar", 0x02963}, // UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
- {"uharl", 0x021BF}, // UPWARDS HARPOON WITH BARB LEFTWARDS
- {"uharr", 0x021BE}, // UPWARDS HARPOON WITH BARB RIGHTWARDS
- {"uhblk", 0x02580}, // UPPER HALF BLOCK
- {"ulcorn", 0x0231C}, // TOP LEFT CORNER
- {"ulcorner", 0x0231C}, // TOP LEFT CORNER
- {"ulcrop", 0x0230F}, // TOP LEFT CROP
- {"ultri", 0x025F8}, // UPPER LEFT TRIANGLE
- {"Umacr", 0x0016A}, // LATIN CAPITAL LETTER U WITH MACRON
- {"umacr", 0x0016B}, // LATIN SMALL LETTER U WITH MACRON
- {"uml", 0x000A8}, // DIAERESIS
- {"UnderBar", 0x0005F}, // LOW LINE
- {"UnderBrace", 0x023DF}, // BOTTOM CURLY BRACKET
- {"UnderBracket", 0x023B5}, // BOTTOM SQUARE BRACKET
- {"UnderParenthesis", 0x023DD}, // BOTTOM PARENTHESIS
- {"Union", 0x022C3}, // N-ARY UNION
- {"UnionPlus", 0x0228E}, // MULTISET UNION
- {"Uogon", 0x00172}, // LATIN CAPITAL LETTER U WITH OGONEK
- {"uogon", 0x00173}, // LATIN SMALL LETTER U WITH OGONEK
- {"Uopf", 0x1D54C}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL U
- {"uopf", 0x1D566}, // MATHEMATICAL DOUBLE-STRUCK SMALL U
- {"uparrow", 0x02191}, // UPWARDS ARROW
- {"UpArrow", 0x02191}, // UPWARDS ARROW
- {"Uparrow", 0x021D1}, // UPWARDS DOUBLE ARROW
- {"UpArrowBar", 0x02912}, // UPWARDS ARROW TO BAR
- {"UpArrowDownArrow", 0x021C5}, // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
- {"updownarrow", 0x02195}, // UP DOWN ARROW
- {"UpDownArrow", 0x02195}, // UP DOWN ARROW
- {"Updownarrow", 0x021D5}, // UP DOWN DOUBLE ARROW
- {"UpEquilibrium", 0x0296E}, // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
- {"upharpoonleft", 0x021BF}, // UPWARDS HARPOON WITH BARB LEFTWARDS
- {"upharpoonright", 0x021BE}, // UPWARDS HARPOON WITH BARB RIGHTWARDS
- {"uplus", 0x0228E}, // MULTISET UNION
- {"UpperLeftArrow", 0x02196}, // NORTH WEST ARROW
- {"UpperRightArrow", 0x02197}, // NORTH EAST ARROW
- {"upsi", 0x003C5}, // GREEK SMALL LETTER UPSILON
- {"Upsi", 0x003D2}, // GREEK UPSILON WITH HOOK SYMBOL
- {"upsih", 0x003D2}, // GREEK UPSILON WITH HOOK SYMBOL
- {"Upsilon", 0x003A5}, // GREEK CAPITAL LETTER UPSILON
- {"upsilon", 0x003C5}, // GREEK SMALL LETTER UPSILON
- {"UpTee", 0x022A5}, // UP TACK
- {"UpTeeArrow", 0x021A5}, // UPWARDS ARROW FROM BAR
- {"upuparrows", 0x021C8}, // UPWARDS PAIRED ARROWS
- {"urcorn", 0x0231D}, // TOP RIGHT CORNER
- {"urcorner", 0x0231D}, // TOP RIGHT CORNER
- {"urcrop", 0x0230E}, // TOP RIGHT CROP
- {"Uring", 0x0016E}, // LATIN CAPITAL LETTER U WITH RING ABOVE
- {"uring", 0x0016F}, // LATIN SMALL LETTER U WITH RING ABOVE
- {"urtri", 0x025F9}, // UPPER RIGHT TRIANGLE
- {"Uscr", 0x1D4B0}, // MATHEMATICAL SCRIPT CAPITAL U
- {"uscr", 0x1D4CA}, // MATHEMATICAL SCRIPT SMALL U
- {"utdot", 0x022F0}, // UP RIGHT DIAGONAL ELLIPSIS
- {"Utilde", 0x00168}, // LATIN CAPITAL LETTER U WITH TILDE
- {"utilde", 0x00169}, // LATIN SMALL LETTER U WITH TILDE
- {"utri", 0x025B5}, // WHITE UP-POINTING SMALL TRIANGLE
- {"utrif", 0x025B4}, // BLACK UP-POINTING SMALL TRIANGLE
- {"uuarr", 0x021C8}, // UPWARDS PAIRED ARROWS
- {"Uuml", 0x000DC}, // LATIN CAPITAL LETTER U WITH DIAERESIS
- {"uuml", 0x000FC}, // LATIN SMALL LETTER U WITH DIAERESIS
- {"uwangle", 0x029A7}, // OBLIQUE ANGLE OPENING DOWN
- {NULL, 0}
-};
-
-static NameId namesV[]={
- {"vangrt", 0x0299C}, // RIGHT ANGLE VARIANT WITH SQUARE
- {"varepsilon", 0x003F5}, // GREEK LUNATE EPSILON SYMBOL
- {"varkappa", 0x003F0}, // GREEK KAPPA SYMBOL
- {"varnothing", 0x02205}, // EMPTY SET
- {"varphi", 0x003D5}, // GREEK PHI SYMBOL
- {"varpi", 0x003D6}, // GREEK PI SYMBOL
- {"varpropto", 0x0221D}, // PROPORTIONAL TO
- {"varr", 0x02195}, // UP DOWN ARROW
- {"vArr", 0x021D5}, // UP DOWN DOUBLE ARROW
- {"varrho", 0x003F1}, // GREEK RHO SYMBOL
- {"varsigma", 0x003C2}, // GREEK SMALL LETTER FINAL SIGMA
-// "varsubsetneq", 0x0228A;0x0FE00}, // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
-// "varsubsetneqq", 0x02ACB;0x0FE00}, // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
-// "varsupsetneq", 0x0228B;0x0FE00}, // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
-// "varsupsetneqq", 0x02ACC;0x0FE00}, // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
- {"vartheta", 0x003D1}, // GREEK THETA SYMBOL
- {"vartriangleleft", 0x022B2}, // NORMAL SUBGROUP OF
- {"vartriangleright", 0x022B3}, // CONTAINS AS NORMAL SUBGROUP
- {"vBar", 0x02AE8}, // SHORT UP TACK WITH UNDERBAR
- {"Vbar", 0x02AEB}, // DOUBLE UP TACK
- {"vBarv", 0x02AE9}, // SHORT UP TACK ABOVE SHORT DOWN TACK
- {"Vcy", 0x00412}, // CYRILLIC CAPITAL LETTER VE
- {"vcy", 0x00432}, // CYRILLIC SMALL LETTER VE
- {"vdash", 0x022A2}, // RIGHT TACK
- {"vDash", 0x022A8}, // TRUE
- {"Vdash", 0x022A9}, // FORCES
- {"VDash", 0x022AB}, // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
- {"Vdashl", 0x02AE6}, // LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL
- {"vee", 0x02228}, // LOGICAL OR
- {"Vee", 0x022C1}, // N-ARY LOGICAL OR
- {"veebar", 0x022BB}, // XOR
- {"veeeq", 0x0225A}, // EQUIANGULAR TO
- {"vellip", 0x022EE}, // VERTICAL ELLIPSIS
- {"verbar", 0x0007C}, // VERTICAL LINE
- {"Verbar", 0x02016}, // DOUBLE VERTICAL LINE
- {"vert", 0x0007C}, // VERTICAL LINE
- {"Vert", 0x02016}, // DOUBLE VERTICAL LINE
- {"VerticalBar", 0x02223}, // DIVIDES
- {"VerticalLine", 0x0007C}, // VERTICAL LINE
- {"VerticalSeparator", 0x02758}, // LIGHT VERTICAL BAR
- {"VerticalTilde", 0x02240}, // WREATH PRODUCT
- {"VeryThinSpace", 0x0200A}, // HAIR SPACE
- {"Vfr", 0x1D519}, // MATHEMATICAL FRAKTUR CAPITAL V
- {"vfr", 0x1D533}, // MATHEMATICAL FRAKTUR SMALL V
- {"vltri", 0x022B2}, // NORMAL SUBGROUP OF
-// "vnsub", 0x02282;0x020D2}, // SUBSET OF with vertical line
-// "vnsup", 0x02283;0x020D2}, // SUPERSET OF with vertical line
- {"Vopf", 0x1D54D}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL V
- {"vopf", 0x1D567}, // MATHEMATICAL DOUBLE-STRUCK SMALL V
- {"vprop", 0x0221D}, // PROPORTIONAL TO
- {"vrtri", 0x022B3}, // CONTAINS AS NORMAL SUBGROUP
- {"Vscr", 0x1D4B1}, // MATHEMATICAL SCRIPT CAPITAL V
- {"vscr", 0x1D4CB}, // MATHEMATICAL SCRIPT SMALL V
-// "vsubne", 0x0228A;0x0FE00}, // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
-// "vsubnE", 0x02ACB;0x0FE00}, // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
-// "vsupne", 0x0228B;0x0FE00}, // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
-// "vsupnE", 0x02ACC;0x0FE00}, // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
- {"Vvdash", 0x022AA}, // TRIPLE VERTICAL BAR RIGHT TURNSTILE
- {"vzigzag", 0x0299A}, // VERTICAL ZIGZAG LINE
- {NULL, 0}
-};
-
-static NameId namesW[]={
- {"Wcirc", 0x00174}, // LATIN CAPITAL LETTER W WITH CIRCUMFLEX
- {"wcirc", 0x00175}, // LATIN SMALL LETTER W WITH CIRCUMFLEX
- {"wedbar", 0x02A5F}, // LOGICAL AND WITH UNDERBAR
- {"wedge", 0x02227}, // LOGICAL AND
- {"Wedge", 0x022C0}, // N-ARY LOGICAL AND
- {"wedgeq", 0x02259}, // ESTIMATES
- {"weierp", 0x02118}, // SCRIPT CAPITAL P
- {"Wfr", 0x1D51A}, // MATHEMATICAL FRAKTUR CAPITAL W
- {"wfr", 0x1D534}, // MATHEMATICAL FRAKTUR SMALL W
- {"Wopf", 0x1D54E}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL W
- {"wopf", 0x1D568}, // MATHEMATICAL DOUBLE-STRUCK SMALL W
- {"wp", 0x02118}, // SCRIPT CAPITAL P
- {"wr", 0x02240}, // WREATH PRODUCT
- {"wreath", 0x02240}, // WREATH PRODUCT
- {"Wscr", 0x1D4B2}, // MATHEMATICAL SCRIPT CAPITAL W
- {"wscr", 0x1D4CC}, // MATHEMATICAL SCRIPT SMALL W
- {NULL, 0}
-};
-
-static NameId namesX[]={
- {"xcap", 0x022C2}, // N-ARY INTERSECTION
- {"xcirc", 0x025EF}, // LARGE CIRCLE
- {"xcup", 0x022C3}, // N-ARY UNION
- {"xdtri", 0x025BD}, // WHITE DOWN-POINTING TRIANGLE
- {"Xfr", 0x1D51B}, // MATHEMATICAL FRAKTUR CAPITAL X
- {"xfr", 0x1D535}, // MATHEMATICAL FRAKTUR SMALL X
- {"Xgr", 0x0039E}, // GREEK CAPITAL LETTER XI
- {"xgr", 0x003BE}, // GREEK SMALL LETTER XI
- {"xharr", 0x027F7}, // LONG LEFT RIGHT ARROW
- {"xhArr", 0x027FA}, // LONG LEFT RIGHT DOUBLE ARROW
- {"Xi", 0x0039E}, // GREEK CAPITAL LETTER XI
- {"xi", 0x003BE}, // GREEK SMALL LETTER XI
- {"xlarr", 0x027F5}, // LONG LEFTWARDS ARROW
- {"xlArr", 0x027F8}, // LONG LEFTWARDS DOUBLE ARROW
- {"xmap", 0x027FC}, // LONG RIGHTWARDS ARROW FROM BAR
- {"xnis", 0x022FB}, // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
- {"xodot", 0x02A00}, // N-ARY CIRCLED DOT OPERATOR
- {"Xopf", 0x1D54F}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL X
- {"xopf", 0x1D569}, // MATHEMATICAL DOUBLE-STRUCK SMALL X
- {"xoplus", 0x02A01}, // N-ARY CIRCLED PLUS OPERATOR
- {"xotime", 0x02A02}, // N-ARY CIRCLED TIMES OPERATOR
- {"xrarr", 0x027F6}, // LONG RIGHTWARDS ARROW
- {"xrArr", 0x027F9}, // LONG RIGHTWARDS DOUBLE ARROW
- {"Xscr", 0x1D4B3}, // MATHEMATICAL SCRIPT CAPITAL X
- {"xscr", 0x1D4CD}, // MATHEMATICAL SCRIPT SMALL X
- {"xsqcup", 0x02A06}, // N-ARY SQUARE UNION OPERATOR
- {"xuplus", 0x02A04}, // N-ARY UNION OPERATOR WITH PLUS
- {"xutri", 0x025B3}, // WHITE UP-POINTING TRIANGLE
- {"xvee", 0x022C1}, // N-ARY LOGICAL OR
- {"xwedge", 0x022C0}, // N-ARY LOGICAL AND
- {NULL, 0}
-};
-
-static NameId namesY[]={
- {"Yacute", 0x000DD}, // LATIN CAPITAL LETTER Y WITH ACUTE
- {"yacute", 0x000FD}, // LATIN SMALL LETTER Y WITH ACUTE
- {"YAcy", 0x0042F}, // CYRILLIC CAPITAL LETTER YA
- {"yacy", 0x0044F}, // CYRILLIC SMALL LETTER YA
- {"Ycirc", 0x00176}, // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
- {"ycirc", 0x00177}, // LATIN SMALL LETTER Y WITH CIRCUMFLEX
- {"Ycy", 0x0042B}, // CYRILLIC CAPITAL LETTER YERU
- {"ycy", 0x0044B}, // CYRILLIC SMALL LETTER YERU
- {"yen", 0x000A5}, // YEN SIGN
- {"Yfr", 0x1D51C}, // MATHEMATICAL FRAKTUR CAPITAL Y
- {"yfr", 0x1D536}, // MATHEMATICAL FRAKTUR SMALL Y
- {"YIcy", 0x00407}, // CYRILLIC CAPITAL LETTER YI
- {"yicy", 0x00457}, // CYRILLIC SMALL LETTER YI
- {"Yopf", 0x1D550}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
- {"yopf", 0x1D56A}, // MATHEMATICAL DOUBLE-STRUCK SMALL Y
- {"Yscr", 0x1D4B4}, // MATHEMATICAL SCRIPT CAPITAL Y
- {"yscr", 0x1D4CE}, // MATHEMATICAL SCRIPT SMALL Y
- {"YUcy", 0x0042E}, // CYRILLIC CAPITAL LETTER YU
- {"yucy", 0x0044E}, // CYRILLIC SMALL LETTER YU
- {"yuml", 0x000FF}, // LATIN SMALL LETTER Y WITH DIAERESIS
- {"Yuml", 0x00178}, // LATIN CAPITAL LETTER Y WITH DIAERESIS
- {NULL, 0}
-};
-
-static NameId namesZ[]={
- {"Zacute", 0x00179}, // LATIN CAPITAL LETTER Z WITH ACUTE
- {"zacute", 0x0017A}, // LATIN SMALL LETTER Z WITH ACUTE
- {"Zcaron", 0x0017D}, // LATIN CAPITAL LETTER Z WITH CARON
- {"zcaron", 0x0017E}, // LATIN SMALL LETTER Z WITH CARON
- {"Zcy", 0x00417}, // CYRILLIC CAPITAL LETTER ZE
- {"zcy", 0x00437}, // CYRILLIC SMALL LETTER ZE
- {"Zdot", 0x0017B}, // LATIN CAPITAL LETTER Z WITH DOT ABOVE
- {"zdot", 0x0017C}, // LATIN SMALL LETTER Z WITH DOT ABOVE
- {"zeetrf", 0x02128}, // BLACK-LETTER CAPITAL Z
- {"ZeroWidthSpace", 0x0200B}, // ZERO WIDTH SPACE
- {"Zeta", 0x00396}, // GREEK CAPITAL LETTER ZETA
- {"zeta", 0x003B6}, // GREEK SMALL LETTER ZETA
- {"Zfr", 0x02128}, // BLACK-LETTER CAPITAL Z
- {"zfr", 0x1D537}, // MATHEMATICAL FRAKTUR SMALL Z
- {"Zgr", 0x00396}, // GREEK CAPITAL LETTER ZETA
- {"zgr", 0x003B6}, // GREEK SMALL LETTER ZETA
- {"ZHcy", 0x00416}, // CYRILLIC CAPITAL LETTER ZHE
- {"zhcy", 0x00436}, // CYRILLIC SMALL LETTER ZHE
- {"zigrarr", 0x021DD}, // RIGHTWARDS SQUIGGLE ARROW
- {"Zopf", 0x02124}, // DOUBLE-STRUCK CAPITAL Z
- {"zopf", 0x1D56B}, // MATHEMATICAL DOUBLE-STRUCK SMALL Z
- {"Zscr", 0x1D4B5}, // MATHEMATICAL SCRIPT CAPITAL Z
- {"zscr", 0x1D4CF}, // MATHEMATICAL SCRIPT SMALL Z
- {"zwj", 0x0200D}, // ZERO WIDTH JOINER
- {"zwnj", 0x0200C}, // ZERO WIDTH NON-JOINER
- {NULL, 0}
-};
-
-// @todo@ order namesTable and names? by frequency
-static NameId* namesTable[] = {
- namesA, namesB, namesC, namesD, namesE, namesF, namesG, namesH, namesI,
- namesJ, namesK, namesL, namesM, namesN, namesO, namesP, namesQ, namesR,
- namesS, namesT, namesU, namesV, namesW, namesX, namesY, namesZ, NULL
-};
-
-int HtmlNamedEntity(const utf8_t *p, size_t length)
-{
- int tableIndex = tolower(*p) - 'a';
- if (tableIndex >= 0 && tableIndex < 26)
- {
- NameId* names = namesTable[tableIndex];
-
- for (size_t i = 0; names[i].name; i++)
- {
- if (strncmp(names[i].name, (const char *)p, length) == 0)
- return names[i].value;
- }
- }
- return -1;
-}
-
diff --git a/gcc/d/dmd/entity.d b/gcc/d/dmd/entity.d
new file mode 100644
index 00000000000..f22dfdb0249
--- /dev/null
+++ b/gcc/d/dmd/entity.d
@@ -0,0 +1,2395 @@
+/**
+ * Defines the named entities to support the "\\&amp;Entity;" escape sequence for strings / character literals.
+ *
+ * Specification $(LINK2 https://dlang.org/spec/entity.html, Named Character Entities)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/entity.d, _entity.d)
+ * Documentation: https://dlang.org/phobos/dmd_entity.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/entity.d
+ */
+
+module dmd.entity;
+
+import core.stdc.ctype;
+
+nothrow:
+
+public int HtmlNamedEntity(const(char)* p, size_t length)
+{
+ int tableIndex = tolower(*p) - 'a';
+ if (tableIndex >= 0 && tableIndex < 26)
+ {
+ foreach (entity; namesTable[tableIndex])
+ {
+ if (entity.name == p[0 .. length])
+ return entity.value;
+ }
+ }
+ return -1;
+}
+
+private:
+
+/*********************************************
+ * Convert from named entity to its encoding.
+ * For reference:
+ * http://www.htmlhelp.com/reference/html40/entities/
+ * http://www.w3.org/2003/entities/2007/w3centities-f.ent
+ */
+struct NameId
+{
+ string name;
+ uint value;
+}
+
+// @todo@ order namesTable and names? by frequency
+immutable NameId[][] namesTable =
+[
+ namesA, namesB, namesC, namesD, namesE, namesF, namesG, namesH, namesI,
+ namesJ, namesK, namesL, namesM, namesN, namesO, namesP, namesQ, namesR,
+ namesS, namesT, namesU, namesV, namesW, namesX, namesY, namesZ
+];
+
+immutable NameId[] namesA =
+[
+ {"Aacgr", 0x00386}, // GREEK CAPITAL LETTER ALPHA WITH TONOS
+ {"aacgr", 0x003AC}, // GREEK SMALL LETTER ALPHA WITH TONOS
+ {"Aacute", 0x000C1}, // LATIN CAPITAL LETTER A WITH ACUTE
+ {"aacute", 0x000E1}, // LATIN SMALL LETTER A WITH ACUTE
+ {"Abreve", 0x00102}, // LATIN CAPITAL LETTER A WITH BREVE
+ {"abreve", 0x00103}, // LATIN SMALL LETTER A WITH BREVE
+ {"ac", 0x0223E}, // INVERTED LAZY S
+ {"acd", 0x0223F}, // SINE WAVE
+// {"acE", 0x0223E;0x00333}, // INVERTED LAZY S with double underline
+ {"Acirc", 0x000C2}, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+ {"acirc", 0x000E2}, // LATIN SMALL LETTER A WITH CIRCUMFLEX
+ {"acute", 0x000B4}, // ACUTE ACCENT
+ {"Acy", 0x00410}, // CYRILLIC CAPITAL LETTER A
+ {"acy", 0x00430}, // CYRILLIC SMALL LETTER A
+ {"AElig", 0x000C6}, // LATIN CAPITAL LETTER AE
+ {"aelig", 0x000E6}, // LATIN SMALL LETTER AE
+ {"af", 0x02061}, // FUNCTION APPLICATION
+ {"Afr", 0x1D504}, // MATHEMATICAL FRAKTUR CAPITAL A
+ {"afr", 0x1D51E}, // MATHEMATICAL FRAKTUR SMALL A
+ {"Agr", 0x00391}, // GREEK CAPITAL LETTER ALPHA
+ {"agr", 0x003B1}, // GREEK SMALL LETTER ALPHA
+ {"Agrave", 0x000C0}, // LATIN CAPITAL LETTER A WITH GRAVE
+ {"agrave", 0x000E0}, // LATIN SMALL LETTER A WITH GRAVE
+ {"alefsym", 0x02135}, // ALEF SYMBOL
+ {"aleph", 0x02135}, // ALEF SYMBOL
+ {"Alpha", 0x00391}, // GREEK CAPITAL LETTER ALPHA
+ {"alpha", 0x003B1}, // GREEK SMALL LETTER ALPHA
+ {"Amacr", 0x00100}, // LATIN CAPITAL LETTER A WITH MACRON
+ {"amacr", 0x00101}, // LATIN SMALL LETTER A WITH MACRON
+ {"amalg", 0x02A3F}, // AMALGAMATION OR COPRODUCT
+ {"amp", 0x00026}, // AMPERSAND
+ {"AMP", 0x00026}, // AMPERSAND
+ {"and", 0x02227}, // LOGICAL AND
+ {"And", 0x02A53}, // DOUBLE LOGICAL AND
+ {"andand", 0x02A55}, // TWO INTERSECTING LOGICAL AND
+ {"andd", 0x02A5C}, // LOGICAL AND WITH HORIZONTAL DASH
+ {"andslope", 0x02A58}, // SLOPING LARGE AND
+ {"andv", 0x02A5A}, // LOGICAL AND WITH MIDDLE STEM
+ {"ang", 0x02220}, // ANGLE
+ {"ange", 0x029A4}, // ANGLE WITH UNDERBAR
+ {"angle", 0x02220}, // ANGLE
+ {"angmsd", 0x02221}, // MEASURED ANGLE
+ {"angmsdaa", 0x029A8}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT
+ {"angmsdab", 0x029A9}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT
+ {"angmsdac", 0x029AA}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT
+ {"angmsdad", 0x029AB}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT
+ {"angmsdae", 0x029AC}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP
+ {"angmsdaf", 0x029AD}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP
+ {"angmsdag", 0x029AE}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN
+ {"angmsdah", 0x029AF}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN
+ {"angrt", 0x0221F}, // RIGHT ANGLE
+ {"angrtvb", 0x022BE}, // RIGHT ANGLE WITH ARC
+ {"angrtvbd", 0x0299D}, // MEASURED RIGHT ANGLE WITH DOT
+ {"angsph", 0x02222}, // SPHERICAL ANGLE
+ {"angst", 0x000C5}, // LATIN CAPITAL LETTER A WITH RING ABOVE
+ {"angzarr", 0x0237C}, // RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW
+ {"Aogon", 0x00104}, // LATIN CAPITAL LETTER A WITH OGONEK
+ {"aogon", 0x00105}, // LATIN SMALL LETTER A WITH OGONEK
+ {"Aopf", 0x1D538}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL A
+ {"aopf", 0x1D552}, // MATHEMATICAL DOUBLE-STRUCK SMALL A
+ {"ap", 0x02248}, // ALMOST EQUAL TO
+ {"apacir", 0x02A6F}, // ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT
+ {"ape", 0x0224A}, // ALMOST EQUAL OR EQUAL TO
+ {"apE", 0x02A70}, // APPROXIMATELY EQUAL OR EQUAL TO
+ {"apid", 0x0224B}, // TRIPLE TILDE
+ {"apos", 0x00027}, // APOSTROPHE
+ {"ApplyFunction", 0x02061}, // FUNCTION APPLICATION
+ {"approx", 0x02248}, // ALMOST EQUAL TO
+ {"approxeq", 0x0224A}, // ALMOST EQUAL OR EQUAL TO
+ {"Aring", 0x000C5}, // LATIN CAPITAL LETTER A WITH RING ABOVE
+ {"aring", 0x000E5}, // LATIN SMALL LETTER A WITH RING ABOVE
+ {"Ascr", 0x1D49C}, // MATHEMATICAL SCRIPT CAPITAL A
+ {"ascr", 0x1D4B6}, // MATHEMATICAL SCRIPT SMALL A
+ {"Assign", 0x02254}, // COLON EQUALS
+ {"ast", 0x0002A}, // ASTERISK
+ {"asymp", 0x02248}, // ALMOST EQUAL TO
+ {"asympeq", 0x0224D}, // EQUIVALENT TO
+ {"Atilde", 0x000C3}, // LATIN CAPITAL LETTER A WITH TILDE
+ {"atilde", 0x000E3}, // LATIN SMALL LETTER A WITH TILDE
+ {"Auml", 0x000C4}, // LATIN CAPITAL LETTER A WITH DIAERESIS
+ {"auml", 0x000E4}, // LATIN SMALL LETTER A WITH DIAERESIS
+ {"awconint", 0x02233}, // ANTICLOCKWISE CONTOUR INTEGRAL
+ {"awint", 0x02A11}, // ANTICLOCKWISE INTEGRATION
+];
+
+immutable NameId[] namesB =
+[
+ {"backcong", 0x0224C}, // ALL EQUAL TO
+ {"backepsilon", 0x003F6}, // GREEK REVERSED LUNATE EPSILON SYMBOL
+ {"backprime", 0x02035}, // REVERSED PRIME
+ {"backsim", 0x0223D}, // REVERSED TILDE
+ {"backsimeq", 0x022CD}, // REVERSED TILDE EQUALS
+ {"Backslash", 0x02216}, // SET MINUS
+// "b.alpha", 0x1D6C2}, // MATHEMATICAL BOLD SMALL ALPHA
+ {"Barv", 0x02AE7}, // SHORT DOWN TACK WITH OVERBAR
+ {"barvee", 0x022BD}, // NOR
+ {"barwed", 0x02305}, // PROJECTIVE
+ {"Barwed", 0x02306}, // PERSPECTIVE
+ {"barwedge", 0x02305}, // PROJECTIVE
+// "b.beta", 0x1D6C3}, // MATHEMATICAL BOLD SMALL BETA
+ {"bbrk", 0x023B5}, // BOTTOM SQUARE BRACKET
+ {"bbrktbrk", 0x023B6}, // BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET
+// "b.chi", 0x1D6D8}, // MATHEMATICAL BOLD SMALL CHI
+ {"bcong", 0x0224C}, // ALL EQUAL TO
+ {"Bcy", 0x00411}, // CYRILLIC CAPITAL LETTER BE
+ {"bcy", 0x00431}, // CYRILLIC SMALL LETTER BE
+// "b.Delta", 0x1D6AB}, // MATHEMATICAL BOLD CAPITAL DELTA
+// "b.delta", 0x1D6C5}, // MATHEMATICAL BOLD SMALL DELTA
+ {"bdquo", 0x0201E}, // DOUBLE LOW-9 QUOTATION MARK
+ {"becaus", 0x02235}, // BECAUSE
+ {"because", 0x02235}, // BECAUSE
+ {"Because", 0x02235}, // BECAUSE
+ {"bemptyv", 0x029B0}, // REVERSED EMPTY SET
+ {"bepsi", 0x003F6}, // GREEK REVERSED LUNATE EPSILON SYMBOL
+// "b.epsi", 0x1D6C6}, // MATHEMATICAL BOLD SMALL EPSILON
+// "b.epsiv", 0x1D6DC}, // MATHEMATICAL BOLD EPSILON SYMBOL
+ {"bernou", 0x0212C}, // SCRIPT CAPITAL B
+ {"Bernoullis", 0x0212C}, // SCRIPT CAPITAL B
+ {"Beta", 0x00392}, // GREEK CAPITAL LETTER BETA
+ {"beta", 0x003B2}, // GREEK SMALL LETTER BETA
+// "b.eta", 0x1D6C8}, // MATHEMATICAL BOLD SMALL ETA
+ {"beth", 0x02136}, // BET SYMBOL
+ {"between", 0x0226C}, // BETWEEN
+ {"Bfr", 0x1D505}, // MATHEMATICAL FRAKTUR CAPITAL B
+ {"bfr", 0x1D51F}, // MATHEMATICAL FRAKTUR SMALL B
+// "b.Gamma", 0x1D6AA}, // MATHEMATICAL BOLD CAPITAL GAMMA
+// "b.gamma", 0x1D6C4}, // MATHEMATICAL BOLD SMALL GAMMA
+// "b.Gammad", 0x1D7CA}, // MATHEMATICAL BOLD CAPITAL DIGAMMA
+// "b.gammad", 0x1D7CB}, // MATHEMATICAL BOLD SMALL DIGAMMA
+ {"Bgr", 0x00392}, // GREEK CAPITAL LETTER BETA
+ {"bgr", 0x003B2}, // GREEK SMALL LETTER BETA
+ {"bigcap", 0x022C2}, // N-ARY INTERSECTION
+ {"bigcirc", 0x025EF}, // LARGE CIRCLE
+ {"bigcup", 0x022C3}, // N-ARY UNION
+ {"bigodot", 0x02A00}, // N-ARY CIRCLED DOT OPERATOR
+ {"bigoplus", 0x02A01}, // N-ARY CIRCLED PLUS OPERATOR
+ {"bigotimes", 0x02A02}, // N-ARY CIRCLED TIMES OPERATOR
+ {"bigsqcup", 0x02A06}, // N-ARY SQUARE UNION OPERATOR
+ {"bigstar", 0x02605}, // BLACK STAR
+ {"bigtriangledown", 0x025BD}, // WHITE DOWN-POINTING TRIANGLE
+ {"bigtriangleup", 0x025B3}, // WHITE UP-POINTING TRIANGLE
+ {"biguplus", 0x02A04}, // N-ARY UNION OPERATOR WITH PLUS
+ {"bigvee", 0x022C1}, // N-ARY LOGICAL OR
+ {"bigwedge", 0x022C0}, // N-ARY LOGICAL AND
+// "b.iota", 0x1D6CA}, // MATHEMATICAL BOLD SMALL IOTA
+// "b.kappa", 0x1D6CB}, // MATHEMATICAL BOLD SMALL KAPPA
+// "b.kappav", 0x1D6DE}, // MATHEMATICAL BOLD KAPPA SYMBOL
+ {"bkarow", 0x0290D}, // RIGHTWARDS DOUBLE DASH ARROW
+ {"blacklozenge", 0x029EB}, // BLACK LOZENGE
+ {"blacksquare", 0x025AA}, // BLACK SMALL SQUARE
+ {"blacktriangle", 0x025B4}, // BLACK UP-POINTING SMALL TRIANGLE
+ {"blacktriangledown", 0x025BE}, // BLACK DOWN-POINTING SMALL TRIANGLE
+ {"blacktriangleleft", 0x025C2}, // BLACK LEFT-POINTING SMALL TRIANGLE
+ {"blacktriangleright", 0x025B8}, // BLACK RIGHT-POINTING SMALL TRIANGLE
+// "b.Lambda", 0x1D6B2}, // MATHEMATICAL BOLD CAPITAL LAMDA
+// "b.lambda", 0x1D6CC}, // MATHEMATICAL BOLD SMALL LAMDA
+ {"blank", 0x02423}, // OPEN BOX
+ {"blk12", 0x02592}, // MEDIUM SHADE
+ {"blk14", 0x02591}, // LIGHT SHADE
+ {"blk34", 0x02593}, // DARK SHADE
+ {"block", 0x02588}, // FULL BLOCK
+// "b.mu", 0x1D6CD}, // MATHEMATICAL BOLD SMALL MU
+// "bne", 0x0003D;0x020E5}, // EQUALS SIGN with reverse slash
+// "bnequiv", 0x02261;0x020E5}, // IDENTICAL TO with reverse slash
+ {"bnot", 0x02310}, // REVERSED NOT SIGN
+ {"bNot", 0x02AED}, // REVERSED DOUBLE STROKE NOT SIGN
+// "b.nu", 0x1D6CE}, // MATHEMATICAL BOLD SMALL NU
+// "b.Omega", 0x1D6C0}, // MATHEMATICAL BOLD CAPITAL OMEGA
+// "b.omega", 0x1D6DA}, // MATHEMATICAL BOLD SMALL OMEGA
+ {"Bopf", 0x1D539}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+ {"bopf", 0x1D553}, // MATHEMATICAL DOUBLE-STRUCK SMALL B
+ {"bot", 0x022A5}, // UP TACK
+ {"bottom", 0x022A5}, // UP TACK
+ {"bowtie", 0x022C8}, // BOWTIE
+ {"boxbox", 0x029C9}, // TWO JOINED SQUARES
+ {"boxdl", 0x02510}, // BOX DRAWINGS LIGHT DOWN AND LEFT
+ {"boxdL", 0x02555}, // BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+ {"boxDl", 0x02556}, // BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+ {"boxDL", 0x02557}, // BOX DRAWINGS DOUBLE DOWN AND LEFT
+ {"boxdr", 0x0250C}, // BOX DRAWINGS LIGHT DOWN AND RIGHT
+ {"boxdR", 0x02552}, // BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+ {"boxDr", 0x02553}, // BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+ {"boxDR", 0x02554}, // BOX DRAWINGS DOUBLE DOWN AND RIGHT
+ {"boxh", 0x02500}, // BOX DRAWINGS LIGHT HORIZONTAL
+ {"boxH", 0x02550}, // BOX DRAWINGS DOUBLE HORIZONTAL
+ {"boxhd", 0x0252C}, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ {"boxHd", 0x02564}, // BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+ {"boxhD", 0x02565}, // BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+ {"boxHD", 0x02566}, // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+ {"boxhu", 0x02534}, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ {"boxHu", 0x02567}, // BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+ {"boxhU", 0x02568}, // BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+ {"boxHU", 0x02569}, // BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+ {"boxminus", 0x0229F}, // SQUARED MINUS
+ {"boxplus", 0x0229E}, // SQUARED PLUS
+ {"boxtimes", 0x022A0}, // SQUARED TIMES
+ {"boxul", 0x02518}, // BOX DRAWINGS LIGHT UP AND LEFT
+ {"boxuL", 0x0255B}, // BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+ {"boxUl", 0x0255C}, // BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+ {"boxUL", 0x0255D}, // BOX DRAWINGS DOUBLE UP AND LEFT
+ {"boxur", 0x02514}, // BOX DRAWINGS LIGHT UP AND RIGHT
+ {"boxuR", 0x02558}, // BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+ {"boxUr", 0x02559}, // BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+ {"boxUR", 0x0255A}, // BOX DRAWINGS DOUBLE UP AND RIGHT
+ {"boxv", 0x02502}, // BOX DRAWINGS LIGHT VERTICAL
+ {"boxV", 0x02551}, // BOX DRAWINGS DOUBLE VERTICAL
+ {"boxvh", 0x0253C}, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ {"boxvH", 0x0256A}, // BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+ {"boxVh", 0x0256B}, // BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+ {"boxVH", 0x0256C}, // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+ {"boxvl", 0x02524}, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ {"boxvL", 0x02561}, // BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+ {"boxVl", 0x02562}, // BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+ {"boxVL", 0x02563}, // BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+ {"boxvr", 0x0251C}, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ {"boxvR", 0x0255E}, // BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+ {"boxVr", 0x0255F}, // BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+ {"boxVR", 0x02560}, // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+// "b.Phi", 0x1D6BD}, // MATHEMATICAL BOLD CAPITAL PHI
+// "b.phi", 0x1D6D7}, // MATHEMATICAL BOLD SMALL PHI
+// "b.phiv", 0x1D6DF}, // MATHEMATICAL BOLD PHI SYMBOL
+// "b.Pi", 0x1D6B7}, // MATHEMATICAL BOLD CAPITAL PI
+// "b.pi", 0x1D6D1}, // MATHEMATICAL BOLD SMALL PI
+// "b.piv", 0x1D6E1}, // MATHEMATICAL BOLD PI SYMBOL
+ {"bprime", 0x02035}, // REVERSED PRIME
+// "b.Psi", 0x1D6BF}, // MATHEMATICAL BOLD CAPITAL PSI
+// "b.psi", 0x1D6D9}, // MATHEMATICAL BOLD SMALL PSI
+ {"breve", 0x002D8}, // BREVE
+ {"Breve", 0x002D8}, // BREVE
+// "b.rho", 0x1D6D2}, // MATHEMATICAL BOLD SMALL RHO
+// "b.rhov", 0x1D6E0}, // MATHEMATICAL BOLD RHO SYMBOL
+ {"brvbar", 0x000A6}, // BROKEN BAR
+ {"Bscr", 0x0212C}, // SCRIPT CAPITAL B
+ {"bscr", 0x1D4B7}, // MATHEMATICAL SCRIPT SMALL B
+ {"bsemi", 0x0204F}, // REVERSED SEMICOLON
+// "b.Sigma", 0x1D6BA}, // MATHEMATICAL BOLD CAPITAL SIGMA
+// "b.sigma", 0x1D6D4}, // MATHEMATICAL BOLD SMALL SIGMA
+// "b.sigmav", 0x1D6D3}, // MATHEMATICAL BOLD SMALL FINAL SIGMA
+ {"bsim", 0x0223D}, // REVERSED TILDE
+ {"bsime", 0x022CD}, // REVERSED TILDE EQUALS
+ {"bsol", 0x0005C}, // REVERSE SOLIDUS
+ {"bsolb", 0x029C5}, // SQUARED FALLING DIAGONAL SLASH
+ {"bsolhsub", 0x027C8}, // REVERSE SOLIDUS PRECEDING SUBSET
+// "b.tau", 0x1D6D5}, // MATHEMATICAL BOLD SMALL TAU
+// "b.Theta", 0x1D6AF}, // MATHEMATICAL BOLD CAPITAL THETA
+// "b.thetas", 0x1D6C9}, // MATHEMATICAL BOLD SMALL THETA
+// "b.thetav", 0x1D6DD}, // MATHEMATICAL BOLD THETA SYMBOL
+ {"bull", 0x02022}, // BULLET
+ {"bullet", 0x02022}, // BULLET
+ {"bump", 0x0224E}, // GEOMETRICALLY EQUIVALENT TO
+ {"bumpe", 0x0224F}, // DIFFERENCE BETWEEN
+ {"bumpE", 0x02AAE}, // EQUALS SIGN WITH BUMPY ABOVE
+ {"Bumpeq", 0x0224E}, // GEOMETRICALLY EQUIVALENT TO
+ {"bumpeq", 0x0224F}, // DIFFERENCE BETWEEN
+// "b.Upsi", 0x1D6BC}, // MATHEMATICAL BOLD CAPITAL UPSILON
+// "b.upsi", 0x1D6D6}, // MATHEMATICAL BOLD SMALL UPSILON
+// "b.Xi", 0x1D6B5}, // MATHEMATICAL BOLD CAPITAL XI
+// "b.xi", 0x1D6CF}, // MATHEMATICAL BOLD SMALL XI
+// "b.zeta", 0x1D6C7}, // MATHEMATICAL BOLD SMALL ZETA
+];
+
+immutable NameId[] namesC =
+[
+ {"Cacute", 0x00106}, // LATIN CAPITAL LETTER C WITH ACUTE
+ {"cacute", 0x00107}, // LATIN SMALL LETTER C WITH ACUTE
+ {"cap", 0x02229}, // INTERSECTION
+ {"Cap", 0x022D2}, // DOUBLE INTERSECTION
+ {"capand", 0x02A44}, // INTERSECTION WITH LOGICAL AND
+ {"capbrcup", 0x02A49}, // INTERSECTION ABOVE BAR ABOVE UNION
+ {"capcap", 0x02A4B}, // INTERSECTION BESIDE AND JOINED WITH INTERSECTION
+ {"capcup", 0x02A47}, // INTERSECTION ABOVE UNION
+ {"capdot", 0x02A40}, // INTERSECTION WITH DOT
+ {"CapitalDifferentialD", 0x02145}, // DOUBLE-STRUCK ITALIC CAPITAL D
+// "caps", 0x02229;0x0FE00}, // INTERSECTION with serifs
+ {"caret", 0x02041}, // CARET INSERTION POINT
+ {"caron", 0x002C7}, // CARON
+ {"Cayleys", 0x0212D}, // BLACK-LETTER CAPITAL C
+ {"ccaps", 0x02A4D}, // CLOSED INTERSECTION WITH SERIFS
+ {"Ccaron", 0x0010C}, // LATIN CAPITAL LETTER C WITH CARON
+ {"ccaron", 0x0010D}, // LATIN SMALL LETTER C WITH CARON
+ {"Ccedil", 0x000C7}, // LATIN CAPITAL LETTER C WITH CEDILLA
+ {"ccedil", 0x000E7}, // LATIN SMALL LETTER C WITH CEDILLA
+ {"Ccirc", 0x00108}, // LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+ {"ccirc", 0x00109}, // LATIN SMALL LETTER C WITH CIRCUMFLEX
+ {"Cconint", 0x02230}, // VOLUME INTEGRAL
+ {"ccups", 0x02A4C}, // CLOSED UNION WITH SERIFS
+ {"ccupssm", 0x02A50}, // CLOSED UNION WITH SERIFS AND SMASH PRODUCT
+ {"Cdot", 0x0010A}, // LATIN CAPITAL LETTER C WITH DOT ABOVE
+ {"cdot", 0x0010B}, // LATIN SMALL LETTER C WITH DOT ABOVE
+ {"cedil", 0x000B8}, // CEDILLA
+ {"Cedilla", 0x000B8}, // CEDILLA
+ {"cemptyv", 0x029B2}, // EMPTY SET WITH SMALL CIRCLE ABOVE
+ {"cent", 0x000A2}, // CENT SIGN
+ {"centerdot", 0x000B7}, // MIDDLE DOT
+ {"CenterDot", 0x000B7}, // MIDDLE DOT
+ {"Cfr", 0x0212D}, // BLACK-LETTER CAPITAL C
+ {"cfr", 0x1D520}, // MATHEMATICAL FRAKTUR SMALL C
+ {"CHcy", 0x00427}, // CYRILLIC CAPITAL LETTER CHE
+ {"chcy", 0x00447}, // CYRILLIC SMALL LETTER CHE
+ {"check", 0x02713}, // CHECK MARK
+ {"checkmark", 0x02713}, // CHECK MARK
+ {"Chi", 0x003A7}, // GREEK CAPITAL LETTER CHI
+ {"chi", 0x003C7}, // GREEK SMALL LETTER CHI
+ {"cir", 0x025CB}, // WHITE CIRCLE
+ {"circ", 0x002C6}, // MODIFIER LETTER CIRCUMFLEX ACCENT
+ {"circeq", 0x02257}, // RING EQUAL TO
+ {"circlearrowleft", 0x021BA}, // ANTICLOCKWISE OPEN CIRCLE ARROW
+ {"circlearrowright", 0x021BB}, // CLOCKWISE OPEN CIRCLE ARROW
+ {"circledast", 0x0229B}, // CIRCLED ASTERISK OPERATOR
+ {"circledcirc", 0x0229A}, // CIRCLED RING OPERATOR
+ {"circleddash", 0x0229D}, // CIRCLED DASH
+ {"CircleDot", 0x02299}, // CIRCLED DOT OPERATOR
+ {"circledR", 0x000AE}, // REGISTERED SIGN
+ {"circledS", 0x024C8}, // CIRCLED LATIN CAPITAL LETTER S
+ {"CircleMinus", 0x02296}, // CIRCLED MINUS
+ {"CirclePlus", 0x02295}, // CIRCLED PLUS
+ {"CircleTimes", 0x02297}, // CIRCLED TIMES
+ {"cire", 0x02257}, // RING EQUAL TO
+ {"cirE", 0x029C3}, // CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT
+ {"cirfnint", 0x02A10}, // CIRCULATION FUNCTION
+ {"cirmid", 0x02AEF}, // VERTICAL LINE WITH CIRCLE ABOVE
+ {"cirscir", 0x029C2}, // CIRCLE WITH SMALL CIRCLE TO THE RIGHT
+ {"ClockwiseContourIntegral", 0x02232}, // CLOCKWISE CONTOUR INTEGRAL
+ {"CloseCurlyDoubleQuote", 0x0201D}, // RIGHT DOUBLE QUOTATION MARK
+ {"CloseCurlyQuote", 0x02019}, // RIGHT SINGLE QUOTATION MARK
+ {"clubs", 0x02663}, // BLACK CLUB SUIT
+ {"clubsuit", 0x02663}, // BLACK CLUB SUIT
+ {"colon", 0x0003A}, // COLON
+ {"Colon", 0x02237}, // PROPORTION
+ {"colone", 0x02254}, // COLON EQUALS
+ {"Colone", 0x02A74}, // DOUBLE COLON EQUAL
+ {"coloneq", 0x02254}, // COLON EQUALS
+ {"comma", 0x0002C}, // COMMA
+ {"commat", 0x00040}, // COMMERCIAL AT
+ {"comp", 0x02201}, // COMPLEMENT
+ {"compfn", 0x02218}, // RING OPERATOR
+ {"complement", 0x02201}, // COMPLEMENT
+ {"complexes", 0x02102}, // DOUBLE-STRUCK CAPITAL C
+ {"cong", 0x02245}, // APPROXIMATELY EQUAL TO
+ {"congdot", 0x02A6D}, // CONGRUENT WITH DOT ABOVE
+ {"Congruent", 0x02261}, // IDENTICAL TO
+ {"conint", 0x0222E}, // CONTOUR INTEGRAL
+ {"Conint", 0x0222F}, // SURFACE INTEGRAL
+ {"ContourIntegral", 0x0222E}, // CONTOUR INTEGRAL
+ {"Copf", 0x02102}, // DOUBLE-STRUCK CAPITAL C
+ {"copf", 0x1D554}, // MATHEMATICAL DOUBLE-STRUCK SMALL C
+ {"coprod", 0x02210}, // N-ARY COPRODUCT
+ {"Coproduct", 0x02210}, // N-ARY COPRODUCT
+ {"copy", 0x000A9}, // COPYRIGHT SIGN
+ {"COPY", 0x000A9}, // COPYRIGHT SIGN
+ {"copysr", 0x02117}, // SOUND RECORDING COPYRIGHT
+ {"CounterClockwiseContourIntegral", 0x02233}, // ANTICLOCKWISE CONTOUR INTEGRAL
+ {"crarr", 0x021B5}, // DOWNWARDS ARROW WITH CORNER LEFTWARDS
+ {"cross", 0x02717}, // BALLOT X
+ {"Cross", 0x02A2F}, // VECTOR OR CROSS PRODUCT
+ {"Cscr", 0x1D49E}, // MATHEMATICAL SCRIPT CAPITAL C
+ {"cscr", 0x1D4B8}, // MATHEMATICAL SCRIPT SMALL C
+ {"csub", 0x02ACF}, // CLOSED SUBSET
+ {"csube", 0x02AD1}, // CLOSED SUBSET OR EQUAL TO
+ {"csup", 0x02AD0}, // CLOSED SUPERSET
+ {"csupe", 0x02AD2}, // CLOSED SUPERSET OR EQUAL TO
+ {"ctdot", 0x022EF}, // MIDLINE HORIZONTAL ELLIPSIS
+ {"cudarrl", 0x02938}, // RIGHT-SIDE ARC CLOCKWISE ARROW
+ {"cudarrr", 0x02935}, // ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS
+ {"cuepr", 0x022DE}, // EQUAL TO OR PRECEDES
+ {"cuesc", 0x022DF}, // EQUAL TO OR SUCCEEDS
+ {"cularr", 0x021B6}, // ANTICLOCKWISE TOP SEMICIRCLE ARROW
+ {"cularrp", 0x0293D}, // TOP ARC ANTICLOCKWISE ARROW WITH PLUS
+ {"cup", 0x0222A}, // UNION
+ {"Cup", 0x022D3}, // DOUBLE UNION
+ {"cupbrcap", 0x02A48}, // UNION ABOVE BAR ABOVE INTERSECTION
+ {"CupCap", 0x0224D}, // EQUIVALENT TO
+ {"cupcap", 0x02A46}, // UNION ABOVE INTERSECTION
+ {"cupcup", 0x02A4A}, // UNION BESIDE AND JOINED WITH UNION
+ {"cupdot", 0x0228D}, // MULTISET MULTIPLICATION
+ {"cupor", 0x02A45}, // UNION WITH LOGICAL OR
+// "cups", 0x0222A;0x0FE00}, // UNION with serifs
+ {"curarr", 0x021B7}, // CLOCKWISE TOP SEMICIRCLE ARROW
+ {"curarrm", 0x0293C}, // TOP ARC CLOCKWISE ARROW WITH MINUS
+ {"curlyeqprec", 0x022DE}, // EQUAL TO OR PRECEDES
+ {"curlyeqsucc", 0x022DF}, // EQUAL TO OR SUCCEEDS
+ {"curlyvee", 0x022CE}, // CURLY LOGICAL OR
+ {"curlywedge", 0x022CF}, // CURLY LOGICAL AND
+ {"curren", 0x000A4}, // CURRENCY SIGN
+ {"curvearrowleft", 0x021B6}, // ANTICLOCKWISE TOP SEMICIRCLE ARROW
+ {"curvearrowright", 0x021B7}, // CLOCKWISE TOP SEMICIRCLE ARROW
+ {"cuvee", 0x022CE}, // CURLY LOGICAL OR
+ {"cuwed", 0x022CF}, // CURLY LOGICAL AND
+ {"cwconint", 0x02232}, // CLOCKWISE CONTOUR INTEGRAL
+ {"cwint", 0x02231}, // CLOCKWISE INTEGRAL
+ {"cylcty", 0x0232D}, // CYLINDRICITY
+];
+
+immutable NameId[] namesD =
+[
+ {"dagger", 0x02020}, // DAGGER
+ {"Dagger", 0x02021}, // DOUBLE DAGGER
+ {"daleth", 0x02138}, // DALET SYMBOL
+ {"darr", 0x02193}, // DOWNWARDS ARROW
+ {"Darr", 0x021A1}, // DOWNWARDS TWO HEADED ARROW
+ {"dArr", 0x021D3}, // DOWNWARDS DOUBLE ARROW
+ {"dash", 0x02010}, // HYPHEN
+ {"dashv", 0x022A3}, // LEFT TACK
+ {"Dashv", 0x02AE4}, // VERTICAL BAR DOUBLE LEFT TURNSTILE
+ {"dbkarow", 0x0290F}, // RIGHTWARDS TRIPLE DASH ARROW
+ {"dblac", 0x002DD}, // DOUBLE ACUTE ACCENT
+ {"Dcaron", 0x0010E}, // LATIN CAPITAL LETTER D WITH CARON
+ {"dcaron", 0x0010F}, // LATIN SMALL LETTER D WITH CARON
+ {"Dcy", 0x00414}, // CYRILLIC CAPITAL LETTER DE
+ {"dcy", 0x00434}, // CYRILLIC SMALL LETTER DE
+ {"DD", 0x02145}, // DOUBLE-STRUCK ITALIC CAPITAL D
+ {"dd", 0x02146}, // DOUBLE-STRUCK ITALIC SMALL D
+ {"ddagger", 0x02021}, // DOUBLE DAGGER
+ {"ddarr", 0x021CA}, // DOWNWARDS PAIRED ARROWS
+ {"DDotrahd", 0x02911}, // RIGHTWARDS ARROW WITH DOTTED STEM
+ {"ddotseq", 0x02A77}, // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW
+ {"deg", 0x000B0}, // DEGREE SIGN
+ {"Del", 0x02207}, // NABLA
+ {"Delta", 0x00394}, // GREEK CAPITAL LETTER DELTA
+ {"delta", 0x003B4}, // GREEK SMALL LETTER DELTA
+ {"demptyv", 0x029B1}, // EMPTY SET WITH OVERBAR
+ {"dfisht", 0x0297F}, // DOWN FISH TAIL
+ {"Dfr", 0x1D507}, // MATHEMATICAL FRAKTUR CAPITAL D
+ {"dfr", 0x1D521}, // MATHEMATICAL FRAKTUR SMALL D
+ {"Dgr", 0x00394}, // GREEK CAPITAL LETTER DELTA
+ {"dgr", 0x003B4}, // GREEK SMALL LETTER DELTA
+ {"dHar", 0x02965}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
+ {"dharl", 0x021C3}, // DOWNWARDS HARPOON WITH BARB LEFTWARDS
+ {"dharr", 0x021C2}, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
+ {"DiacriticalAcute", 0x000B4}, // ACUTE ACCENT
+ {"DiacriticalDot", 0x002D9}, // DOT ABOVE
+ {"DiacriticalDoubleAcute", 0x002DD}, // DOUBLE ACUTE ACCENT
+ {"DiacriticalGrave", 0x00060}, // GRAVE ACCENT
+ {"DiacriticalTilde", 0x002DC}, // SMALL TILDE
+ {"diam", 0x022C4}, // DIAMOND OPERATOR
+ {"diamond", 0x022C4}, // DIAMOND OPERATOR
+ {"Diamond", 0x022C4}, // DIAMOND OPERATOR
+ {"diamondsuit", 0x02666}, // BLACK DIAMOND SUIT
+ {"diams", 0x02666}, // BLACK DIAMOND SUIT
+ {"die", 0x000A8}, // DIAERESIS
+ {"DifferentialD", 0x02146}, // DOUBLE-STRUCK ITALIC SMALL D
+ {"digamma", 0x003DD}, // GREEK SMALL LETTER DIGAMMA
+ {"disin", 0x022F2}, // ELEMENT OF WITH LONG HORIZONTAL STROKE
+ {"div", 0x000F7}, // DIVISION SIGN
+ {"divide", 0x000F7}, // DIVISION SIGN
+ {"divideontimes", 0x022C7}, // DIVISION TIMES
+ {"divonx", 0x022C7}, // DIVISION TIMES
+ {"DJcy", 0x00402}, // CYRILLIC CAPITAL LETTER DJE
+ {"djcy", 0x00452}, // CYRILLIC SMALL LETTER DJE
+ {"dlcorn", 0x0231E}, // BOTTOM LEFT CORNER
+ {"dlcrop", 0x0230D}, // BOTTOM LEFT CROP
+ {"dollar", 0x00024}, // DOLLAR SIGN
+ {"Dopf", 0x1D53B}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL D
+ {"dopf", 0x1D555}, // MATHEMATICAL DOUBLE-STRUCK SMALL D
+ {"Dot", 0x000A8}, // DIAERESIS
+ {"dot", 0x002D9}, // DOT ABOVE
+ {"DotDot", 0x020DC}, // COMBINING FOUR DOTS ABOVE
+ {"doteq", 0x02250}, // APPROACHES THE LIMIT
+ {"doteqdot", 0x02251}, // GEOMETRICALLY EQUAL TO
+ {"DotEqual", 0x02250}, // APPROACHES THE LIMIT
+ {"dotminus", 0x02238}, // DOT MINUS
+ {"dotplus", 0x02214}, // DOT PLUS
+ {"dotsquare", 0x022A1}, // SQUARED DOT OPERATOR
+ {"doublebarwedge", 0x02306}, // PERSPECTIVE
+ {"DoubleContourIntegral", 0x0222F}, // SURFACE INTEGRAL
+ {"DoubleDot", 0x000A8}, // DIAERESIS
+ {"DoubleDownArrow", 0x021D3}, // DOWNWARDS DOUBLE ARROW
+ {"DoubleLeftArrow", 0x021D0}, // LEFTWARDS DOUBLE ARROW
+ {"DoubleLeftRightArrow", 0x021D4}, // LEFT RIGHT DOUBLE ARROW
+ {"DoubleLeftTee", 0x02AE4}, // VERTICAL BAR DOUBLE LEFT TURNSTILE
+ {"DoubleLongLeftArrow", 0x027F8}, // LONG LEFTWARDS DOUBLE ARROW
+ {"DoubleLongLeftRightArrow", 0x027FA}, // LONG LEFT RIGHT DOUBLE ARROW
+ {"DoubleLongRightArrow", 0x027F9}, // LONG RIGHTWARDS DOUBLE ARROW
+ {"DoubleRightArrow", 0x021D2}, // RIGHTWARDS DOUBLE ARROW
+ {"DoubleRightTee", 0x022A8}, // TRUE
+ {"DoubleUpArrow", 0x021D1}, // UPWARDS DOUBLE ARROW
+ {"DoubleUpDownArrow", 0x021D5}, // UP DOWN DOUBLE ARROW
+ {"DoubleVerticalBar", 0x02225}, // PARALLEL TO
+ {"downarrow", 0x02193}, // DOWNWARDS ARROW
+ {"DownArrow", 0x02193}, // DOWNWARDS ARROW
+ {"Downarrow", 0x021D3}, // DOWNWARDS DOUBLE ARROW
+ {"DownArrowBar", 0x02913}, // DOWNWARDS ARROW TO BAR
+ {"DownArrowUpArrow", 0x021F5}, // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW
+ {"DownBreve", 0x00311}, // COMBINING INVERTED BREVE
+ {"downdownarrows", 0x021CA}, // DOWNWARDS PAIRED ARROWS
+ {"downharpoonleft", 0x021C3}, // DOWNWARDS HARPOON WITH BARB LEFTWARDS
+ {"downharpoonright", 0x021C2}, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
+ {"DownLeftRightVector", 0x02950}, // LEFT BARB DOWN RIGHT BARB DOWN HARPOON
+ {"DownLeftTeeVector", 0x0295E}, // LEFTWARDS HARPOON WITH BARB DOWN FROM BAR
+ {"DownLeftVector", 0x021BD}, // LEFTWARDS HARPOON WITH BARB DOWNWARDS
+ {"DownLeftVectorBar", 0x02956}, // LEFTWARDS HARPOON WITH BARB DOWN TO BAR
+ {"DownRightTeeVector", 0x0295F}, // RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR
+ {"DownRightVector", 0x021C1}, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
+ {"DownRightVectorBar", 0x02957}, // RIGHTWARDS HARPOON WITH BARB DOWN TO BAR
+ {"DownTee", 0x022A4}, // DOWN TACK
+ {"DownTeeArrow", 0x021A7}, // DOWNWARDS ARROW FROM BAR
+ {"drbkarow", 0x02910}, // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW
+ {"drcorn", 0x0231F}, // BOTTOM RIGHT CORNER
+ {"drcrop", 0x0230C}, // BOTTOM RIGHT CROP
+ {"Dscr", 0x1D49F}, // MATHEMATICAL SCRIPT CAPITAL D
+ {"dscr", 0x1D4B9}, // MATHEMATICAL SCRIPT SMALL D
+ {"DScy", 0x00405}, // CYRILLIC CAPITAL LETTER DZE
+ {"dscy", 0x00455}, // CYRILLIC SMALL LETTER DZE
+ {"dsol", 0x029F6}, // SOLIDUS WITH OVERBAR
+ {"Dstrok", 0x00110}, // LATIN CAPITAL LETTER D WITH STROKE
+ {"dstrok", 0x00111}, // LATIN SMALL LETTER D WITH STROKE
+ {"dtdot", 0x022F1}, // DOWN RIGHT DIAGONAL ELLIPSIS
+ {"dtri", 0x025BF}, // WHITE DOWN-POINTING SMALL TRIANGLE
+ {"dtrif", 0x025BE}, // BLACK DOWN-POINTING SMALL TRIANGLE
+ {"duarr", 0x021F5}, // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW
+ {"duhar", 0x0296F}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
+ {"dwangle", 0x029A6}, // OBLIQUE ANGLE OPENING UP
+ {"DZcy", 0x0040F}, // CYRILLIC CAPITAL LETTER DZHE
+ {"dzcy", 0x0045F}, // CYRILLIC SMALL LETTER DZHE
+ {"dzigrarr", 0x027FF}, // LONG RIGHTWARDS SQUIGGLE ARROW
+];
+
+immutable NameId[] namesE =
+[
+ {"Eacgr", 0x00388}, // GREEK CAPITAL LETTER EPSILON WITH TONOS
+ {"eacgr", 0x003AD}, // GREEK SMALL LETTER EPSILON WITH TONOS
+ {"Eacute", 0x000C9}, // LATIN CAPITAL LETTER E WITH ACUTE
+ {"eacute", 0x000E9}, // LATIN SMALL LETTER E WITH ACUTE
+ {"easter", 0x02A6E}, // EQUALS WITH ASTERISK
+ {"Ecaron", 0x0011A}, // LATIN CAPITAL LETTER E WITH CARON
+ {"ecaron", 0x0011B}, // LATIN SMALL LETTER E WITH CARON
+ {"ecir", 0x02256}, // RING IN EQUAL TO
+ {"Ecirc", 0x000CA}, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+ {"ecirc", 0x000EA}, // LATIN SMALL LETTER E WITH CIRCUMFLEX
+ {"ecolon", 0x02255}, // EQUALS COLON
+ {"Ecy", 0x0042D}, // CYRILLIC CAPITAL LETTER E
+ {"ecy", 0x0044D}, // CYRILLIC SMALL LETTER E
+ {"eDDot", 0x02A77}, // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW
+ {"Edot", 0x00116}, // LATIN CAPITAL LETTER E WITH DOT ABOVE
+ {"edot", 0x00117}, // LATIN SMALL LETTER E WITH DOT ABOVE
+ {"eDot", 0x02251}, // GEOMETRICALLY EQUAL TO
+ {"ee", 0x02147}, // DOUBLE-STRUCK ITALIC SMALL E
+ {"EEacgr", 0x00389}, // GREEK CAPITAL LETTER ETA WITH TONOS
+ {"eeacgr", 0x003AE}, // GREEK SMALL LETTER ETA WITH TONOS
+ {"EEgr", 0x00397}, // GREEK CAPITAL LETTER ETA
+ {"eegr", 0x003B7}, // GREEK SMALL LETTER ETA
+ {"efDot", 0x02252}, // APPROXIMATELY EQUAL TO OR THE IMAGE OF
+ {"Efr", 0x1D508}, // MATHEMATICAL FRAKTUR CAPITAL E
+ {"efr", 0x1D522}, // MATHEMATICAL FRAKTUR SMALL E
+ {"eg", 0x02A9A}, // DOUBLE-LINE EQUAL TO OR GREATER-THAN
+ {"Egr", 0x00395}, // GREEK CAPITAL LETTER EPSILON
+ {"egr", 0x003B5}, // GREEK SMALL LETTER EPSILON
+ {"Egrave", 0x000C8}, // LATIN CAPITAL LETTER E WITH GRAVE
+ {"egrave", 0x000E8}, // LATIN SMALL LETTER E WITH GRAVE
+ {"egs", 0x02A96}, // SLANTED EQUAL TO OR GREATER-THAN
+ {"egsdot", 0x02A98}, // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE
+ {"el", 0x02A99}, // DOUBLE-LINE EQUAL TO OR LESS-THAN
+ {"Element", 0x02208}, // ELEMENT OF
+ {"elinters", 0x023E7}, // ELECTRICAL INTERSECTION
+ {"ell", 0x02113}, // SCRIPT SMALL L
+ {"els", 0x02A95}, // SLANTED EQUAL TO OR LESS-THAN
+ {"elsdot", 0x02A97}, // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE
+ {"Emacr", 0x00112}, // LATIN CAPITAL LETTER E WITH MACRON
+ {"emacr", 0x00113}, // LATIN SMALL LETTER E WITH MACRON
+ {"empty", 0x02205}, // EMPTY SET
+ {"emptyset", 0x02205}, // EMPTY SET
+ {"EmptySmallSquare", 0x025FB}, // WHITE MEDIUM SQUARE
+ {"emptyv", 0x02205}, // EMPTY SET
+ {"EmptyVerySmallSquare", 0x025AB}, // WHITE SMALL SQUARE
+ {"emsp", 0x02003}, // EM SPACE
+ {"emsp13", 0x02004}, // THREE-PER-EM SPACE
+ {"emsp14", 0x02005}, // FOUR-PER-EM SPACE
+ {"ENG", 0x0014A}, // LATIN CAPITAL LETTER ENG
+ {"eng", 0x0014B}, // LATIN SMALL LETTER ENG
+ {"ensp", 0x02002}, // EN SPACE
+ {"Eogon", 0x00118}, // LATIN CAPITAL LETTER E WITH OGONEK
+ {"eogon", 0x00119}, // LATIN SMALL LETTER E WITH OGONEK
+ {"Eopf", 0x1D53C}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL E
+ {"eopf", 0x1D556}, // MATHEMATICAL DOUBLE-STRUCK SMALL E
+ {"epar", 0x022D5}, // EQUAL AND PARALLEL TO
+ {"eparsl", 0x029E3}, // EQUALS SIGN AND SLANTED PARALLEL
+ {"eplus", 0x02A71}, // EQUALS SIGN ABOVE PLUS SIGN
+ {"epsi", 0x003B5}, // GREEK SMALL LETTER EPSILON
+ {"Epsilon", 0x00395}, // GREEK CAPITAL LETTER EPSILON
+ {"epsilon", 0x003B5}, // GREEK SMALL LETTER EPSILON
+ {"epsiv", 0x003F5}, // GREEK LUNATE EPSILON SYMBOL
+ {"eqcirc", 0x02256}, // RING IN EQUAL TO
+ {"eqcolon", 0x02255}, // EQUALS COLON
+ {"eqsim", 0x02242}, // MINUS TILDE
+ {"eqslantgtr", 0x02A96}, // SLANTED EQUAL TO OR GREATER-THAN
+ {"eqslantless", 0x02A95}, // SLANTED EQUAL TO OR LESS-THAN
+ {"Equal", 0x02A75}, // TWO CONSECUTIVE EQUALS SIGNS
+ {"equals", 0x0003D}, // EQUALS SIGN
+ {"EqualTilde", 0x02242}, // MINUS TILDE
+ {"equest", 0x0225F}, // QUESTIONED EQUAL TO
+ {"Equilibrium", 0x021CC}, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
+ {"equiv", 0x02261}, // IDENTICAL TO
+ {"equivDD", 0x02A78}, // EQUIVALENT WITH FOUR DOTS ABOVE
+ {"eqvparsl", 0x029E5}, // IDENTICAL TO AND SLANTED PARALLEL
+ {"erarr", 0x02971}, // EQUALS SIGN ABOVE RIGHTWARDS ARROW
+ {"erDot", 0x02253}, // IMAGE OF OR APPROXIMATELY EQUAL TO
+ {"escr", 0x0212F}, // SCRIPT SMALL E
+ {"Escr", 0x02130}, // SCRIPT CAPITAL E
+ {"esdot", 0x02250}, // APPROACHES THE LIMIT
+ {"esim", 0x02242}, // MINUS TILDE
+ {"Esim", 0x02A73}, // EQUALS SIGN ABOVE TILDE OPERATOR
+ {"Eta", 0x00397}, // GREEK CAPITAL LETTER ETA
+ {"eta", 0x003B7}, // GREEK SMALL LETTER ETA
+ {"ETH", 0x000D0}, // LATIN CAPITAL LETTER ETH
+ {"eth", 0x000F0}, // LATIN SMALL LETTER ETH
+ {"Euml", 0x000CB}, // LATIN CAPITAL LETTER E WITH DIAERESIS
+ {"euml", 0x000EB}, // LATIN SMALL LETTER E WITH DIAERESIS
+ {"euro", 0x020AC}, // EURO SIGN
+ {"excl", 0x00021}, // EXCLAMATION MARK
+ {"exist", 0x02203}, // THERE EXISTS
+ {"Exists", 0x02203}, // THERE EXISTS
+ {"expectation", 0x02130}, // SCRIPT CAPITAL E
+ {"exponentiale", 0x02147}, // DOUBLE-STRUCK ITALIC SMALL E
+ {"ExponentialE", 0x02147}, // DOUBLE-STRUCK ITALIC SMALL E
+];
+
+immutable NameId[] namesF =
+[
+ {"fallingdotseq", 0x02252}, // APPROXIMATELY EQUAL TO OR THE IMAGE OF
+ {"Fcy", 0x00424}, // CYRILLIC CAPITAL LETTER EF
+ {"fcy", 0x00444}, // CYRILLIC SMALL LETTER EF
+ {"female", 0x02640}, // FEMALE SIGN
+ {"ffilig", 0x0FB03}, // LATIN SMALL LIGATURE FFI
+ {"fflig", 0x0FB00}, // LATIN SMALL LIGATURE FF
+ {"ffllig", 0x0FB04}, // LATIN SMALL LIGATURE FFL
+ {"Ffr", 0x1D509}, // MATHEMATICAL FRAKTUR CAPITAL F
+ {"ffr", 0x1D523}, // MATHEMATICAL FRAKTUR SMALL F
+ {"filig", 0x0FB01}, // LATIN SMALL LIGATURE FI
+ {"FilledSmallSquare", 0x025FC}, // BLACK MEDIUM SQUARE
+ {"FilledVerySmallSquare", 0x025AA}, // BLACK SMALL SQUARE
+// "fjlig", 0x00066;0x0006A}, // fj ligature
+ {"flat", 0x0266D}, // MUSIC FLAT SIGN
+ {"fllig", 0x0FB02}, // LATIN SMALL LIGATURE FL
+ {"fltns", 0x025B1}, // WHITE PARALLELOGRAM
+ {"fnof", 0x00192}, // LATIN SMALL LETTER F WITH HOOK
+ {"Fopf", 0x1D53D}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL F
+ {"fopf", 0x1D557}, // MATHEMATICAL DOUBLE-STRUCK SMALL F
+ {"forall", 0x02200}, // FOR ALL
+ {"ForAll", 0x02200}, // FOR ALL
+ {"fork", 0x022D4}, // PITCHFORK
+ {"forkv", 0x02AD9}, // ELEMENT OF OPENING DOWNWARDS
+ {"Fouriertrf", 0x02131}, // SCRIPT CAPITAL F
+ {"fpartint", 0x02A0D}, // FINITE PART INTEGRAL
+ {"frac12", 0x000BD}, // VULGAR FRACTION ONE HALF
+ {"frac13", 0x02153}, // VULGAR FRACTION ONE THIRD
+ {"frac14", 0x000BC}, // VULGAR FRACTION ONE QUARTER
+ {"frac15", 0x02155}, // VULGAR FRACTION ONE FIFTH
+ {"frac16", 0x02159}, // VULGAR FRACTION ONE SIXTH
+ {"frac18", 0x0215B}, // VULGAR FRACTION ONE EIGHTH
+ {"frac23", 0x02154}, // VULGAR FRACTION TWO THIRDS
+ {"frac25", 0x02156}, // VULGAR FRACTION TWO FIFTHS
+ {"frac34", 0x000BE}, // VULGAR FRACTION THREE QUARTERS
+ {"frac35", 0x02157}, // VULGAR FRACTION THREE FIFTHS
+ {"frac38", 0x0215C}, // VULGAR FRACTION THREE EIGHTHS
+ {"frac45", 0x02158}, // VULGAR FRACTION FOUR FIFTHS
+ {"frac56", 0x0215A}, // VULGAR FRACTION FIVE SIXTHS
+ {"frac58", 0x0215D}, // VULGAR FRACTION FIVE EIGHTHS
+ {"frac78", 0x0215E}, // VULGAR FRACTION SEVEN EIGHTHS
+ {"frasl", 0x02044}, // FRACTION SLASH
+ {"frown", 0x02322}, // FROWN
+ {"Fscr", 0x02131}, // SCRIPT CAPITAL F
+ {"fscr", 0x1D4BB}, // MATHEMATICAL SCRIPT SMALL F
+];
+
+immutable NameId[] namesG =
+[
+ {"gacute", 0x001F5}, // LATIN SMALL LETTER G WITH ACUTE
+ {"Gamma", 0x00393}, // GREEK CAPITAL LETTER GAMMA
+ {"gamma", 0x003B3}, // GREEK SMALL LETTER GAMMA
+ {"Gammad", 0x003DC}, // GREEK LETTER DIGAMMA
+ {"gammad", 0x003DD}, // GREEK SMALL LETTER DIGAMMA
+ {"gap", 0x02A86}, // GREATER-THAN OR APPROXIMATE
+ {"Gbreve", 0x0011E}, // LATIN CAPITAL LETTER G WITH BREVE
+ {"gbreve", 0x0011F}, // LATIN SMALL LETTER G WITH BREVE
+ {"Gcedil", 0x00122}, // LATIN CAPITAL LETTER G WITH CEDILLA
+ {"Gcirc", 0x0011C}, // LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+ {"gcirc", 0x0011D}, // LATIN SMALL LETTER G WITH CIRCUMFLEX
+ {"Gcy", 0x00413}, // CYRILLIC CAPITAL LETTER GHE
+ {"gcy", 0x00433}, // CYRILLIC SMALL LETTER GHE
+ {"Gdot", 0x00120}, // LATIN CAPITAL LETTER G WITH DOT ABOVE
+ {"gdot", 0x00121}, // LATIN SMALL LETTER G WITH DOT ABOVE
+ {"ge", 0x02265}, // GREATER-THAN OR EQUAL TO
+ {"gE", 0x02267}, // GREATER-THAN OVER EQUAL TO
+ {"gel", 0x022DB}, // GREATER-THAN EQUAL TO OR LESS-THAN
+ {"gEl", 0x02A8C}, // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
+ {"geq", 0x02265}, // GREATER-THAN OR EQUAL TO
+ {"geqq", 0x02267}, // GREATER-THAN OVER EQUAL TO
+ {"geqslant", 0x02A7E}, // GREATER-THAN OR SLANTED EQUAL TO
+ {"ges", 0x02A7E}, // GREATER-THAN OR SLANTED EQUAL TO
+ {"gescc", 0x02AA9}, // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
+ {"gesdot", 0x02A80}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
+ {"gesdoto", 0x02A82}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
+ {"gesdotol", 0x02A84}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT
+// "gesl", 0x022DB;0x0FE00}, // GREATER-THAN slanted EQUAL TO OR LESS-THAN
+ {"gesles", 0x02A94}, // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL
+ {"Gfr", 0x1D50A}, // MATHEMATICAL FRAKTUR CAPITAL G
+ {"gfr", 0x1D524}, // MATHEMATICAL FRAKTUR SMALL G
+ {"gg", 0x0226B}, // MUCH GREATER-THAN
+ {"Gg", 0x022D9}, // VERY MUCH GREATER-THAN
+ {"ggg", 0x022D9}, // VERY MUCH GREATER-THAN
+ {"Ggr", 0x00393}, // GREEK CAPITAL LETTER GAMMA
+ {"ggr", 0x003B3}, // GREEK SMALL LETTER GAMMA
+ {"gimel", 0x02137}, // GIMEL SYMBOL
+ {"GJcy", 0x00403}, // CYRILLIC CAPITAL LETTER GJE
+ {"gjcy", 0x00453}, // CYRILLIC SMALL LETTER GJE
+ {"gl", 0x02277}, // GREATER-THAN OR LESS-THAN
+ {"gla", 0x02AA5}, // GREATER-THAN BESIDE LESS-THAN
+ {"glE", 0x02A92}, // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL
+ {"glj", 0x02AA4}, // GREATER-THAN OVERLAPPING LESS-THAN
+ {"gnap", 0x02A8A}, // GREATER-THAN AND NOT APPROXIMATE
+ {"gnapprox", 0x02A8A}, // GREATER-THAN AND NOT APPROXIMATE
+ {"gnE", 0x02269}, // GREATER-THAN BUT NOT EQUAL TO
+ {"gne", 0x02A88}, // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
+ {"gneq", 0x02A88}, // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
+ {"gneqq", 0x02269}, // GREATER-THAN BUT NOT EQUAL TO
+ {"gnsim", 0x022E7}, // GREATER-THAN BUT NOT EQUIVALENT TO
+ {"Gopf", 0x1D53E}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+ {"gopf", 0x1D558}, // MATHEMATICAL DOUBLE-STRUCK SMALL G
+ {"grave", 0x00060}, // GRAVE ACCENT
+ {"GreaterEqual", 0x02265}, // GREATER-THAN OR EQUAL TO
+ {"GreaterEqualLess", 0x022DB}, // GREATER-THAN EQUAL TO OR LESS-THAN
+ {"GreaterFullEqual", 0x02267}, // GREATER-THAN OVER EQUAL TO
+ {"GreaterGreater", 0x02AA2}, // DOUBLE NESTED GREATER-THAN
+ {"GreaterLess", 0x02277}, // GREATER-THAN OR LESS-THAN
+ {"GreaterSlantEqual", 0x02A7E}, // GREATER-THAN OR SLANTED EQUAL TO
+ {"GreaterTilde", 0x02273}, // GREATER-THAN OR EQUIVALENT TO
+ {"gscr", 0x0210A}, // SCRIPT SMALL G
+ {"Gscr", 0x1D4A2}, // MATHEMATICAL SCRIPT CAPITAL G
+ {"gsim", 0x02273}, // GREATER-THAN OR EQUIVALENT TO
+ {"gsime", 0x02A8E}, // GREATER-THAN ABOVE SIMILAR OR EQUAL
+ {"gsiml", 0x02A90}, // GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN
+ {"gt", 0x0003E}, // GREATER-THAN SIGN
+ {"GT", 0x0003E}, // GREATER-THAN SIGN
+ {"Gt", 0x0226B}, // MUCH GREATER-THAN
+ {"gtcc", 0x02AA7}, // GREATER-THAN CLOSED BY CURVE
+ {"gtcir", 0x02A7A}, // GREATER-THAN WITH CIRCLE INSIDE
+ {"gtdot", 0x022D7}, // GREATER-THAN WITH DOT
+ {"gtlPar", 0x02995}, // DOUBLE LEFT ARC GREATER-THAN BRACKET
+ {"gtquest", 0x02A7C}, // GREATER-THAN WITH QUESTION MARK ABOVE
+ {"gtrapprox", 0x02A86}, // GREATER-THAN OR APPROXIMATE
+ {"gtrarr", 0x02978}, // GREATER-THAN ABOVE RIGHTWARDS ARROW
+ {"gtrdot", 0x022D7}, // GREATER-THAN WITH DOT
+ {"gtreqless", 0x022DB}, // GREATER-THAN EQUAL TO OR LESS-THAN
+ {"gtreqqless", 0x02A8C}, // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
+ {"gtrless", 0x02277}, // GREATER-THAN OR LESS-THAN
+ {"gtrsim", 0x02273}, // GREATER-THAN OR EQUIVALENT TO
+// "gvertneqq", 0x02269;0x0FE00}, // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke
+// "gvnE", 0x02269;0x0FE00}, // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke
+];
+
+immutable NameId[] namesH =
+[
+ {"Hacek", 0x002C7}, // CARON
+ {"hairsp", 0x0200A}, // HAIR SPACE
+ {"half", 0x000BD}, // VULGAR FRACTION ONE HALF
+ {"hamilt", 0x0210B}, // SCRIPT CAPITAL H
+ {"HARDcy", 0x0042A}, // CYRILLIC CAPITAL LETTER HARD SIGN
+ {"hardcy", 0x0044A}, // CYRILLIC SMALL LETTER HARD SIGN
+ {"harr", 0x02194}, // LEFT RIGHT ARROW
+ {"hArr", 0x021D4}, // LEFT RIGHT DOUBLE ARROW
+ {"harrcir", 0x02948}, // LEFT RIGHT ARROW THROUGH SMALL CIRCLE
+ {"harrw", 0x021AD}, // LEFT RIGHT WAVE ARROW
+ {"Hat", 0x0005E}, // CIRCUMFLEX ACCENT
+ {"hbar", 0x0210F}, // PLANCK CONSTANT OVER TWO PI
+ {"Hcirc", 0x00124}, // LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+ {"hcirc", 0x00125}, // LATIN SMALL LETTER H WITH CIRCUMFLEX
+ {"hearts", 0x02665}, // BLACK HEART SUIT
+ {"heartsuit", 0x02665}, // BLACK HEART SUIT
+ {"hellip", 0x02026}, // HORIZONTAL ELLIPSIS
+ {"hercon", 0x022B9}, // HERMITIAN CONJUGATE MATRIX
+ {"Hfr", 0x0210C}, // BLACK-LETTER CAPITAL H
+ {"hfr", 0x1D525}, // MATHEMATICAL FRAKTUR SMALL H
+ {"HilbertSpace", 0x0210B}, // SCRIPT CAPITAL H
+ {"hksearow", 0x02925}, // SOUTH EAST ARROW WITH HOOK
+ {"hkswarow", 0x02926}, // SOUTH WEST ARROW WITH HOOK
+ {"hoarr", 0x021FF}, // LEFT RIGHT OPEN-HEADED ARROW
+ {"homtht", 0x0223B}, // HOMOTHETIC
+ {"hookleftarrow", 0x021A9}, // LEFTWARDS ARROW WITH HOOK
+ {"hookrightarrow", 0x021AA}, // RIGHTWARDS ARROW WITH HOOK
+ {"Hopf", 0x0210D}, // DOUBLE-STRUCK CAPITAL H
+ {"hopf", 0x1D559}, // MATHEMATICAL DOUBLE-STRUCK SMALL H
+ {"horbar", 0x02015}, // HORIZONTAL BAR
+ {"HorizontalLine", 0x02500}, // BOX DRAWINGS LIGHT HORIZONTAL
+ {"Hscr", 0x0210B}, // SCRIPT CAPITAL H
+ {"hscr", 0x1D4BD}, // MATHEMATICAL SCRIPT SMALL H
+ {"hslash", 0x0210F}, // PLANCK CONSTANT OVER TWO PI
+ {"Hstrok", 0x00126}, // LATIN CAPITAL LETTER H WITH STROKE
+ {"hstrok", 0x00127}, // LATIN SMALL LETTER H WITH STROKE
+ {"HumpDownHump", 0x0224E}, // GEOMETRICALLY EQUIVALENT TO
+ {"HumpEqual", 0x0224F}, // DIFFERENCE BETWEEN
+ {"hybull", 0x02043}, // HYPHEN BULLET
+ {"hyphen", 0x02010}, // HYPHEN
+];
+
+immutable NameId[] namesI =
+[
+ {"Iacgr", 0x0038A}, // GREEK CAPITAL LETTER IOTA WITH TONOS
+ {"iacgr", 0x003AF}, // GREEK SMALL LETTER IOTA WITH TONOS
+ {"Iacute", 0x000CD}, // LATIN CAPITAL LETTER I WITH ACUTE
+ {"iacute", 0x000ED}, // LATIN SMALL LETTER I WITH ACUTE
+ {"ic", 0x02063}, // INVISIBLE SEPARATOR
+ {"Icirc", 0x000CE}, // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+ {"icirc", 0x000EE}, // LATIN SMALL LETTER I WITH CIRCUMFLEX
+ {"Icy", 0x00418}, // CYRILLIC CAPITAL LETTER I
+ {"icy", 0x00438}, // CYRILLIC SMALL LETTER I
+ {"idiagr", 0x00390}, // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+ {"Idigr", 0x003AA}, // GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+ {"idigr", 0x003CA}, // GREEK SMALL LETTER IOTA WITH DIALYTIKA
+ {"Idot", 0x00130}, // LATIN CAPITAL LETTER I WITH DOT ABOVE
+ {"IEcy", 0x00415}, // CYRILLIC CAPITAL LETTER IE
+ {"iecy", 0x00435}, // CYRILLIC SMALL LETTER IE
+ {"iexcl", 0x000A1}, // INVERTED EXCLAMATION MARK
+ {"iff", 0x021D4}, // LEFT RIGHT DOUBLE ARROW
+ {"Ifr", 0x02111}, // BLACK-LETTER CAPITAL I
+ {"ifr", 0x1D526}, // MATHEMATICAL FRAKTUR SMALL I
+ {"Igr", 0x00399}, // GREEK CAPITAL LETTER IOTA
+ {"igr", 0x003B9}, // GREEK SMALL LETTER IOTA
+ {"Igrave", 0x000CC}, // LATIN CAPITAL LETTER I WITH GRAVE
+ {"igrave", 0x000EC}, // LATIN SMALL LETTER I WITH GRAVE
+ {"ii", 0x02148}, // DOUBLE-STRUCK ITALIC SMALL I
+ {"iiiint", 0x02A0C}, // QUADRUPLE INTEGRAL OPERATOR
+ {"iiint", 0x0222D}, // TRIPLE INTEGRAL
+ {"iinfin", 0x029DC}, // INCOMPLETE INFINITY
+ {"iiota", 0x02129}, // TURNED GREEK SMALL LETTER IOTA
+ {"IJlig", 0x00132}, // LATIN CAPITAL LIGATURE IJ
+ {"ijlig", 0x00133}, // LATIN SMALL LIGATURE IJ
+ {"Im", 0x02111}, // BLACK-LETTER CAPITAL I
+ {"Imacr", 0x0012A}, // LATIN CAPITAL LETTER I WITH MACRON
+ {"imacr", 0x0012B}, // LATIN SMALL LETTER I WITH MACRON
+ {"image", 0x02111}, // BLACK-LETTER CAPITAL I
+ {"ImaginaryI", 0x02148}, // DOUBLE-STRUCK ITALIC SMALL I
+ {"imagline", 0x02110}, // SCRIPT CAPITAL I
+ {"imagpart", 0x02111}, // BLACK-LETTER CAPITAL I
+ {"imath", 0x00131}, // LATIN SMALL LETTER DOTLESS I
+ {"imof", 0x022B7}, // IMAGE OF
+ {"imped", 0x001B5}, // LATIN CAPITAL LETTER Z WITH STROKE
+ {"Implies", 0x021D2}, // RIGHTWARDS DOUBLE ARROW
+ {"in", 0x02208}, // ELEMENT OF
+ {"incare", 0x02105}, // CARE OF
+ {"infin", 0x0221E}, // INFINITY
+ {"infintie", 0x029DD}, // TIE OVER INFINITY
+ {"inodot", 0x00131}, // LATIN SMALL LETTER DOTLESS I
+ {"int", 0x0222B}, // INTEGRAL
+ {"Int", 0x0222C}, // DOUBLE INTEGRAL
+ {"intcal", 0x022BA}, // INTERCALATE
+ {"integers", 0x02124}, // DOUBLE-STRUCK CAPITAL Z
+ {"Integral", 0x0222B}, // INTEGRAL
+ {"intercal", 0x022BA}, // INTERCALATE
+ {"Intersection", 0x022C2}, // N-ARY INTERSECTION
+ {"intlarhk", 0x02A17}, // INTEGRAL WITH LEFTWARDS ARROW WITH HOOK
+ {"intprod", 0x02A3C}, // INTERIOR PRODUCT
+ {"InvisibleComma", 0x02063}, // INVISIBLE SEPARATOR
+ {"InvisibleTimes", 0x02062}, // INVISIBLE TIMES
+ {"IOcy", 0x00401}, // CYRILLIC CAPITAL LETTER IO
+ {"iocy", 0x00451}, // CYRILLIC SMALL LETTER IO
+ {"Iogon", 0x0012E}, // LATIN CAPITAL LETTER I WITH OGONEK
+ {"iogon", 0x0012F}, // LATIN SMALL LETTER I WITH OGONEK
+ {"Iopf", 0x1D540}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL I
+ {"iopf", 0x1D55A}, // MATHEMATICAL DOUBLE-STRUCK SMALL I
+ {"Iota", 0x00399}, // GREEK CAPITAL LETTER IOTA
+ {"iota", 0x003B9}, // GREEK SMALL LETTER IOTA
+ {"iprod", 0x02A3C}, // INTERIOR PRODUCT
+ {"iquest", 0x000BF}, // INVERTED QUESTION MARK
+ {"Iscr", 0x02110}, // SCRIPT CAPITAL I
+ {"iscr", 0x1D4BE}, // MATHEMATICAL SCRIPT SMALL I
+ {"isin", 0x02208}, // ELEMENT OF
+ {"isindot", 0x022F5}, // ELEMENT OF WITH DOT ABOVE
+ {"isinE", 0x022F9}, // ELEMENT OF WITH TWO HORIZONTAL STROKES
+ {"isins", 0x022F4}, // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ {"isinsv", 0x022F3}, // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ {"isinv", 0x02208}, // ELEMENT OF
+ {"it", 0x02062}, // INVISIBLE TIMES
+ {"Itilde", 0x00128}, // LATIN CAPITAL LETTER I WITH TILDE
+ {"itilde", 0x00129}, // LATIN SMALL LETTER I WITH TILDE
+ {"Iukcy", 0x00406}, // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+ {"iukcy", 0x00456}, // CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ {"Iuml", 0x000CF}, // LATIN CAPITAL LETTER I WITH DIAERESIS
+ {"iuml", 0x000EF}, // LATIN SMALL LETTER I WITH DIAERESIS
+];
+
+immutable NameId[] namesJ =
+[
+ {"Jcirc", 0x00134}, // LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+ {"jcirc", 0x00135}, // LATIN SMALL LETTER J WITH CIRCUMFLEX
+ {"Jcy", 0x00419}, // CYRILLIC CAPITAL LETTER SHORT I
+ {"jcy", 0x00439}, // CYRILLIC SMALL LETTER SHORT I
+ {"Jfr", 0x1D50D}, // MATHEMATICAL FRAKTUR CAPITAL J
+ {"jfr", 0x1D527}, // MATHEMATICAL FRAKTUR SMALL J
+ {"jmath", 0x00237}, // LATIN SMALL LETTER DOTLESS J
+ {"Jopf", 0x1D541}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL J
+ {"jopf", 0x1D55B}, // MATHEMATICAL DOUBLE-STRUCK SMALL J
+ {"Jscr", 0x1D4A5}, // MATHEMATICAL SCRIPT CAPITAL J
+ {"jscr", 0x1D4BF}, // MATHEMATICAL SCRIPT SMALL J
+ {"Jsercy", 0x00408}, // CYRILLIC CAPITAL LETTER JE
+ {"jsercy", 0x00458}, // CYRILLIC SMALL LETTER JE
+ {"Jukcy", 0x00404}, // CYRILLIC CAPITAL LETTER UKRAINIAN IE
+ {"jukcy", 0x00454}, // CYRILLIC SMALL LETTER UKRAINIAN IE
+];
+
+immutable NameId[] namesK =
+[
+ {"Kappa", 0x0039A}, // GREEK CAPITAL LETTER KAPPA
+ {"kappa", 0x003BA}, // GREEK SMALL LETTER KAPPA
+ {"kappav", 0x003F0}, // GREEK KAPPA SYMBOL
+ {"Kcedil", 0x00136}, // LATIN CAPITAL LETTER K WITH CEDILLA
+ {"kcedil", 0x00137}, // LATIN SMALL LETTER K WITH CEDILLA
+ {"Kcy", 0x0041A}, // CYRILLIC CAPITAL LETTER KA
+ {"kcy", 0x0043A}, // CYRILLIC SMALL LETTER KA
+ {"Kfr", 0x1D50E}, // MATHEMATICAL FRAKTUR CAPITAL K
+ {"kfr", 0x1D528}, // MATHEMATICAL FRAKTUR SMALL K
+ {"Kgr", 0x0039A}, // GREEK CAPITAL LETTER KAPPA
+ {"kgr", 0x003BA}, // GREEK SMALL LETTER KAPPA
+ {"kgreen", 0x00138}, // LATIN SMALL LETTER KRA
+ {"KHcy", 0x00425}, // CYRILLIC CAPITAL LETTER HA
+ {"khcy", 0x00445}, // CYRILLIC SMALL LETTER HA
+ {"KHgr", 0x003A7}, // GREEK CAPITAL LETTER CHI
+ {"khgr", 0x003C7}, // GREEK SMALL LETTER CHI
+ {"KJcy", 0x0040C}, // CYRILLIC CAPITAL LETTER KJE
+ {"kjcy", 0x0045C}, // CYRILLIC SMALL LETTER KJE
+ {"Kopf", 0x1D542}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL K
+ {"kopf", 0x1D55C}, // MATHEMATICAL DOUBLE-STRUCK SMALL K
+ {"Kscr", 0x1D4A6}, // MATHEMATICAL SCRIPT CAPITAL K
+ {"kscr", 0x1D4C0}, // MATHEMATICAL SCRIPT SMALL K
+];
+
+immutable NameId[] namesL =
+[
+ {"lAarr", 0x021DA}, // LEFTWARDS TRIPLE ARROW
+ {"Lacute", 0x00139}, // LATIN CAPITAL LETTER L WITH ACUTE
+ {"lacute", 0x0013A}, // LATIN SMALL LETTER L WITH ACUTE
+ {"laemptyv", 0x029B4}, // EMPTY SET WITH LEFT ARROW ABOVE
+ {"lagran", 0x02112}, // SCRIPT CAPITAL L
+ {"Lambda", 0x0039B}, // GREEK CAPITAL LETTER LAMDA
+ {"lambda", 0x003BB}, // GREEK SMALL LETTER LAMDA
+ {"lang", 0x027E8}, // MATHEMATICAL LEFT ANGLE BRACKET
+ {"Lang", 0x027EA}, // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
+ {"langd", 0x02991}, // LEFT ANGLE BRACKET WITH DOT
+ {"langle", 0x027E8}, // MATHEMATICAL LEFT ANGLE BRACKET
+ {"lap", 0x02A85}, // LESS-THAN OR APPROXIMATE
+ {"Laplacetrf", 0x02112}, // SCRIPT CAPITAL L
+ {"laquo", 0x000AB}, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ {"larr", 0x02190}, // LEFTWARDS ARROW
+ {"Larr", 0x0219E}, // LEFTWARDS TWO HEADED ARROW
+ {"lArr", 0x021D0}, // LEFTWARDS DOUBLE ARROW
+ {"larrb", 0x021E4}, // LEFTWARDS ARROW TO BAR
+ {"larrbfs", 0x0291F}, // LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND
+ {"larrfs", 0x0291D}, // LEFTWARDS ARROW TO BLACK DIAMOND
+ {"larrhk", 0x021A9}, // LEFTWARDS ARROW WITH HOOK
+ {"larrlp", 0x021AB}, // LEFTWARDS ARROW WITH LOOP
+ {"larrpl", 0x02939}, // LEFT-SIDE ARC ANTICLOCKWISE ARROW
+ {"larrsim", 0x02973}, // LEFTWARDS ARROW ABOVE TILDE OPERATOR
+ {"larrtl", 0x021A2}, // LEFTWARDS ARROW WITH TAIL
+ {"lat", 0x02AAB}, // LARGER THAN
+ {"latail", 0x02919}, // LEFTWARDS ARROW-TAIL
+ {"lAtail", 0x0291B}, // LEFTWARDS DOUBLE ARROW-TAIL
+ {"late", 0x02AAD}, // LARGER THAN OR EQUAL TO
+// "lates", 0x02AAD;0x0FE00}, // LARGER THAN OR slanted EQUAL
+ {"lbarr", 0x0290C}, // LEFTWARDS DOUBLE DASH ARROW
+ {"lBarr", 0x0290E}, // LEFTWARDS TRIPLE DASH ARROW
+ {"lbbrk", 0x02772}, // LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
+ {"lbrace", 0x0007B}, // LEFT CURLY BRACKET
+ {"lbrack", 0x0005B}, // LEFT SQUARE BRACKET
+ {"lbrke", 0x0298B}, // LEFT SQUARE BRACKET WITH UNDERBAR
+ {"lbrksld", 0x0298F}, // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+ {"lbrkslu", 0x0298D}, // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
+ {"Lcaron", 0x0013D}, // LATIN CAPITAL LETTER L WITH CARON
+ {"lcaron", 0x0013E}, // LATIN SMALL LETTER L WITH CARON
+ {"Lcedil", 0x0013B}, // LATIN CAPITAL LETTER L WITH CEDILLA
+ {"lcedil", 0x0013C}, // LATIN SMALL LETTER L WITH CEDILLA
+ {"lceil", 0x02308}, // LEFT CEILING
+ {"lcub", 0x0007B}, // LEFT CURLY BRACKET
+ {"Lcy", 0x0041B}, // CYRILLIC CAPITAL LETTER EL
+ {"lcy", 0x0043B}, // CYRILLIC SMALL LETTER EL
+ {"ldca", 0x02936}, // ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS
+ {"ldquo", 0x0201C}, // LEFT DOUBLE QUOTATION MARK
+ {"ldquor", 0x0201E}, // DOUBLE LOW-9 QUOTATION MARK
+ {"ldrdhar", 0x02967}, // LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
+ {"ldrushar", 0x0294B}, // LEFT BARB DOWN RIGHT BARB UP HARPOON
+ {"ldsh", 0x021B2}, // DOWNWARDS ARROW WITH TIP LEFTWARDS
+ {"le", 0x02264}, // LESS-THAN OR EQUAL TO
+ {"lE", 0x02266}, // LESS-THAN OVER EQUAL TO
+ {"LeftAngleBracket", 0x027E8}, // MATHEMATICAL LEFT ANGLE BRACKET
+ {"leftarrow", 0x02190}, // LEFTWARDS ARROW
+ {"LeftArrow", 0x02190}, // LEFTWARDS ARROW
+ {"Leftarrow", 0x021D0}, // LEFTWARDS DOUBLE ARROW
+ {"LeftArrowBar", 0x021E4}, // LEFTWARDS ARROW TO BAR
+ {"LeftArrowRightArrow", 0x021C6}, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
+ {"leftarrowtail", 0x021A2}, // LEFTWARDS ARROW WITH TAIL
+ {"LeftCeiling", 0x02308}, // LEFT CEILING
+ {"LeftDoubleBracket", 0x027E6}, // MATHEMATICAL LEFT WHITE SQUARE BRACKET
+ {"LeftDownTeeVector", 0x02961}, // DOWNWARDS HARPOON WITH BARB LEFT FROM BAR
+ {"LeftDownVector", 0x021C3}, // DOWNWARDS HARPOON WITH BARB LEFTWARDS
+ {"LeftDownVectorBar", 0x02959}, // DOWNWARDS HARPOON WITH BARB LEFT TO BAR
+ {"LeftFloor", 0x0230A}, // LEFT FLOOR
+ {"leftharpoondown", 0x021BD}, // LEFTWARDS HARPOON WITH BARB DOWNWARDS
+ {"leftharpoonup", 0x021BC}, // LEFTWARDS HARPOON WITH BARB UPWARDS
+ {"leftleftarrows", 0x021C7}, // LEFTWARDS PAIRED ARROWS
+ {"leftrightarrow", 0x02194}, // LEFT RIGHT ARROW
+ {"LeftRightArrow", 0x02194}, // LEFT RIGHT ARROW
+ {"Leftrightarrow", 0x021D4}, // LEFT RIGHT DOUBLE ARROW
+ {"leftrightarrows", 0x021C6}, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
+ {"leftrightharpoons", 0x021CB}, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
+ {"leftrightsquigarrow", 0x021AD}, // LEFT RIGHT WAVE ARROW
+ {"LeftRightVector", 0x0294E}, // LEFT BARB UP RIGHT BARB UP HARPOON
+ {"LeftTee", 0x022A3}, // LEFT TACK
+ {"LeftTeeArrow", 0x021A4}, // LEFTWARDS ARROW FROM BAR
+ {"LeftTeeVector", 0x0295A}, // LEFTWARDS HARPOON WITH BARB UP FROM BAR
+ {"leftthreetimes", 0x022CB}, // LEFT SEMIDIRECT PRODUCT
+ {"LeftTriangle", 0x022B2}, // NORMAL SUBGROUP OF
+ {"LeftTriangleBar", 0x029CF}, // LEFT TRIANGLE BESIDE VERTICAL BAR
+ {"LeftTriangleEqual", 0x022B4}, // NORMAL SUBGROUP OF OR EQUAL TO
+ {"LeftUpDownVector", 0x02951}, // UP BARB LEFT DOWN BARB LEFT HARPOON
+ {"LeftUpTeeVector", 0x02960}, // UPWARDS HARPOON WITH BARB LEFT FROM BAR
+ {"LeftUpVector", 0x021BF}, // UPWARDS HARPOON WITH BARB LEFTWARDS
+ {"LeftUpVectorBar", 0x02958}, // UPWARDS HARPOON WITH BARB LEFT TO BAR
+ {"LeftVector", 0x021BC}, // LEFTWARDS HARPOON WITH BARB UPWARDS
+ {"LeftVectorBar", 0x02952}, // LEFTWARDS HARPOON WITH BARB UP TO BAR
+ {"leg", 0x022DA}, // LESS-THAN EQUAL TO OR GREATER-THAN
+ {"lEg", 0x02A8B}, // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
+ {"leq", 0x02264}, // LESS-THAN OR EQUAL TO
+ {"leqq", 0x02266}, // LESS-THAN OVER EQUAL TO
+ {"leqslant", 0x02A7D}, // LESS-THAN OR SLANTED EQUAL TO
+ {"les", 0x02A7D}, // LESS-THAN OR SLANTED EQUAL TO
+ {"lescc", 0x02AA8}, // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
+ {"lesdot", 0x02A7F}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
+ {"lesdoto", 0x02A81}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
+ {"lesdotor", 0x02A83}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT
+// "lesg", 0x022DA;0x0FE00}, // LESS-THAN slanted EQUAL TO OR GREATER-THAN
+ {"lesges", 0x02A93}, // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL
+ {"lessapprox", 0x02A85}, // LESS-THAN OR APPROXIMATE
+ {"lessdot", 0x022D6}, // LESS-THAN WITH DOT
+ {"lesseqgtr", 0x022DA}, // LESS-THAN EQUAL TO OR GREATER-THAN
+ {"lesseqqgtr", 0x02A8B}, // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
+ {"LessEqualGreater", 0x022DA}, // LESS-THAN EQUAL TO OR GREATER-THAN
+ {"LessFullEqual", 0x02266}, // LESS-THAN OVER EQUAL TO
+ {"LessGreater", 0x02276}, // LESS-THAN OR GREATER-THAN
+ {"lessgtr", 0x02276}, // LESS-THAN OR GREATER-THAN
+ {"LessLess", 0x02AA1}, // DOUBLE NESTED LESS-THAN
+ {"lesssim", 0x02272}, // LESS-THAN OR EQUIVALENT TO
+ {"LessSlantEqual", 0x02A7D}, // LESS-THAN OR SLANTED EQUAL TO
+ {"LessTilde", 0x02272}, // LESS-THAN OR EQUIVALENT TO
+ {"lfisht", 0x0297C}, // LEFT FISH TAIL
+ {"lfloor", 0x0230A}, // LEFT FLOOR
+ {"Lfr", 0x1D50F}, // MATHEMATICAL FRAKTUR CAPITAL L
+ {"lfr", 0x1D529}, // MATHEMATICAL FRAKTUR SMALL L
+ {"lg", 0x02276}, // LESS-THAN OR GREATER-THAN
+ {"lgE", 0x02A91}, // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL
+ {"Lgr", 0x0039B}, // GREEK CAPITAL LETTER LAMDA
+ {"lgr", 0x003BB}, // GREEK SMALL LETTER LAMDA
+ {"lHar", 0x02962}, // LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN
+ {"lhard", 0x021BD}, // LEFTWARDS HARPOON WITH BARB DOWNWARDS
+ {"lharu", 0x021BC}, // LEFTWARDS HARPOON WITH BARB UPWARDS
+ {"lharul", 0x0296A}, // LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
+ {"lhblk", 0x02584}, // LOWER HALF BLOCK
+ {"LJcy", 0x00409}, // CYRILLIC CAPITAL LETTER LJE
+ {"ljcy", 0x00459}, // CYRILLIC SMALL LETTER LJE
+ {"ll", 0x0226A}, // MUCH LESS-THAN
+ {"Ll", 0x022D8}, // VERY MUCH LESS-THAN
+ {"llarr", 0x021C7}, // LEFTWARDS PAIRED ARROWS
+ {"llcorner", 0x0231E}, // BOTTOM LEFT CORNER
+ {"Lleftarrow", 0x021DA}, // LEFTWARDS TRIPLE ARROW
+ {"llhard", 0x0296B}, // LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
+ {"lltri", 0x025FA}, // LOWER LEFT TRIANGLE
+ {"Lmidot", 0x0013F}, // LATIN CAPITAL LETTER L WITH MIDDLE DOT
+ {"lmidot", 0x00140}, // LATIN SMALL LETTER L WITH MIDDLE DOT
+ {"lmoust", 0x023B0}, // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION
+ {"lmoustache", 0x023B0}, // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION
+ {"lnap", 0x02A89}, // LESS-THAN AND NOT APPROXIMATE
+ {"lnapprox", 0x02A89}, // LESS-THAN AND NOT APPROXIMATE
+ {"lnE", 0x02268}, // LESS-THAN BUT NOT EQUAL TO
+ {"lne", 0x02A87}, // LESS-THAN AND SINGLE-LINE NOT EQUAL TO
+ {"lneq", 0x02A87}, // LESS-THAN AND SINGLE-LINE NOT EQUAL TO
+ {"lneqq", 0x02268}, // LESS-THAN BUT NOT EQUAL TO
+ {"lnsim", 0x022E6}, // LESS-THAN BUT NOT EQUIVALENT TO
+ {"loang", 0x027EC}, // MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
+ {"loarr", 0x021FD}, // LEFTWARDS OPEN-HEADED ARROW
+ {"lobrk", 0x027E6}, // MATHEMATICAL LEFT WHITE SQUARE BRACKET
+ {"longleftarrow", 0x027F5}, // LONG LEFTWARDS ARROW
+ {"LongLeftArrow", 0x027F5}, // LONG LEFTWARDS ARROW
+ {"Longleftarrow", 0x027F8}, // LONG LEFTWARDS DOUBLE ARROW
+ {"longleftrightarrow", 0x027F7}, // LONG LEFT RIGHT ARROW
+ {"LongLeftRightArrow", 0x027F7}, // LONG LEFT RIGHT ARROW
+ {"Longleftrightarrow", 0x027FA}, // LONG LEFT RIGHT DOUBLE ARROW
+ {"longmapsto", 0x027FC}, // LONG RIGHTWARDS ARROW FROM BAR
+ {"longrightarrow", 0x027F6}, // LONG RIGHTWARDS ARROW
+ {"LongRightArrow", 0x027F6}, // LONG RIGHTWARDS ARROW
+ {"Longrightarrow", 0x027F9}, // LONG RIGHTWARDS DOUBLE ARROW
+ {"looparrowleft", 0x021AB}, // LEFTWARDS ARROW WITH LOOP
+ {"looparrowright", 0x021AC}, // RIGHTWARDS ARROW WITH LOOP
+ {"lopar", 0x02985}, // LEFT WHITE PARENTHESIS
+ {"Lopf", 0x1D543}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL L
+ {"lopf", 0x1D55D}, // MATHEMATICAL DOUBLE-STRUCK SMALL L
+ {"loplus", 0x02A2D}, // PLUS SIGN IN LEFT HALF CIRCLE
+ {"lotimes", 0x02A34}, // MULTIPLICATION SIGN IN LEFT HALF CIRCLE
+ {"lowast", 0x02217}, // ASTERISK OPERATOR
+ {"lowbar", 0x0005F}, // LOW LINE
+ {"LowerLeftArrow", 0x02199}, // SOUTH WEST ARROW
+ {"LowerRightArrow", 0x02198}, // SOUTH EAST ARROW
+ {"loz", 0x025CA}, // LOZENGE
+ {"lozenge", 0x025CA}, // LOZENGE
+ {"lozf", 0x029EB}, // BLACK LOZENGE
+ {"lpar", 0x00028}, // LEFT PARENTHESIS
+ {"lparlt", 0x02993}, // LEFT ARC LESS-THAN BRACKET
+ {"lrarr", 0x021C6}, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
+ {"lrcorner", 0x0231F}, // BOTTOM RIGHT CORNER
+ {"lrhar", 0x021CB}, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
+ {"lrhard", 0x0296D}, // RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
+ {"lrm", 0x0200E}, // LEFT-TO-RIGHT MARK
+ {"lrtri", 0x022BF}, // RIGHT TRIANGLE
+ {"lsaquo", 0x02039}, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ {"Lscr", 0x02112}, // SCRIPT CAPITAL L
+ {"lscr", 0x1D4C1}, // MATHEMATICAL SCRIPT SMALL L
+ {"lsh", 0x021B0}, // UPWARDS ARROW WITH TIP LEFTWARDS
+ {"Lsh", 0x021B0}, // UPWARDS ARROW WITH TIP LEFTWARDS
+ {"lsim", 0x02272}, // LESS-THAN OR EQUIVALENT TO
+ {"lsime", 0x02A8D}, // LESS-THAN ABOVE SIMILAR OR EQUAL
+ {"lsimg", 0x02A8F}, // LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN
+ {"lsqb", 0x0005B}, // LEFT SQUARE BRACKET
+ {"lsquo", 0x02018}, // LEFT SINGLE QUOTATION MARK
+ {"lsquor", 0x0201A}, // SINGLE LOW-9 QUOTATION MARK
+ {"Lstrok", 0x00141}, // LATIN CAPITAL LETTER L WITH STROKE
+ {"lstrok", 0x00142}, // LATIN SMALL LETTER L WITH STROKE
+ {"lt", 0x0003C}, // LESS-THAN SIGN
+ {"LT", 0x0003C}, // LESS-THAN SIGN
+ {"Lt", 0x0226A}, // MUCH LESS-THAN
+ {"ltcc", 0x02AA6}, // LESS-THAN CLOSED BY CURVE
+ {"ltcir", 0x02A79}, // LESS-THAN WITH CIRCLE INSIDE
+ {"ltdot", 0x022D6}, // LESS-THAN WITH DOT
+ {"lthree", 0x022CB}, // LEFT SEMIDIRECT PRODUCT
+ {"ltimes", 0x022C9}, // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
+ {"ltlarr", 0x02976}, // LESS-THAN ABOVE LEFTWARDS ARROW
+ {"ltquest", 0x02A7B}, // LESS-THAN WITH QUESTION MARK ABOVE
+ {"ltri", 0x025C3}, // WHITE LEFT-POINTING SMALL TRIANGLE
+ {"ltrie", 0x022B4}, // NORMAL SUBGROUP OF OR EQUAL TO
+ {"ltrif", 0x025C2}, // BLACK LEFT-POINTING SMALL TRIANGLE
+ {"ltrPar", 0x02996}, // DOUBLE RIGHT ARC LESS-THAN BRACKET
+ {"lurdshar", 0x0294A}, // LEFT BARB UP RIGHT BARB DOWN HARPOON
+ {"luruhar", 0x02966}, // LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP
+// "lvertneqq", 0x02268;0x0FE00}, // LESS-THAN BUT NOT EQUAL TO - with vertical stroke
+// "lvnE", 0x02268;0x0FE00}, // LESS-THAN BUT NOT EQUAL TO - with vertical stroke
+];
+
+immutable NameId[] namesM =
+[
+ {"macr", 0x000AF}, // MACRON
+ {"male", 0x02642}, // MALE SIGN
+ {"malt", 0x02720}, // MALTESE CROSS
+ {"maltese", 0x02720}, // MALTESE CROSS
+ {"map", 0x021A6}, // RIGHTWARDS ARROW FROM BAR
+ {"Map", 0x02905}, // RIGHTWARDS TWO-HEADED ARROW FROM BAR
+ {"mapsto", 0x021A6}, // RIGHTWARDS ARROW FROM BAR
+ {"mapstodown", 0x021A7}, // DOWNWARDS ARROW FROM BAR
+ {"mapstoleft", 0x021A4}, // LEFTWARDS ARROW FROM BAR
+ {"mapstoup", 0x021A5}, // UPWARDS ARROW FROM BAR
+ {"marker", 0x025AE}, // BLACK VERTICAL RECTANGLE
+ {"mcomma", 0x02A29}, // MINUS SIGN WITH COMMA ABOVE
+ {"Mcy", 0x0041C}, // CYRILLIC CAPITAL LETTER EM
+ {"mcy", 0x0043C}, // CYRILLIC SMALL LETTER EM
+ {"mdash", 0x02014}, // EM DASH
+ {"mDDot", 0x0223A}, // GEOMETRIC PROPORTION
+ {"measuredangle", 0x02221}, // MEASURED ANGLE
+ {"MediumSpace", 0x0205F}, // MEDIUM MATHEMATICAL SPACE
+ {"Mellintrf", 0x02133}, // SCRIPT CAPITAL M
+ {"Mfr", 0x1D510}, // MATHEMATICAL FRAKTUR CAPITAL M
+ {"mfr", 0x1D52A}, // MATHEMATICAL FRAKTUR SMALL M
+ {"Mgr", 0x0039C}, // GREEK CAPITAL LETTER MU
+ {"mgr", 0x003BC}, // GREEK SMALL LETTER MU
+ {"mho", 0x02127}, // INVERTED OHM SIGN
+ {"micro", 0x000B5}, // MICRO SIGN
+ {"mid", 0x02223}, // DIVIDES
+ {"midast", 0x0002A}, // ASTERISK
+ {"midcir", 0x02AF0}, // VERTICAL LINE WITH CIRCLE BELOW
+ {"middot", 0x000B7}, // MIDDLE DOT
+ {"minus", 0x02212}, // MINUS SIGN
+ {"minusb", 0x0229F}, // SQUARED MINUS
+ {"minusd", 0x02238}, // DOT MINUS
+ {"minusdu", 0x02A2A}, // MINUS SIGN WITH DOT BELOW
+ {"MinusPlus", 0x02213}, // MINUS-OR-PLUS SIGN
+ {"mlcp", 0x02ADB}, // TRANSVERSAL INTERSECTION
+ {"mldr", 0x02026}, // HORIZONTAL ELLIPSIS
+ {"mnplus", 0x02213}, // MINUS-OR-PLUS SIGN
+ {"models", 0x022A7}, // MODELS
+ {"Mopf", 0x1D544}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+ {"mopf", 0x1D55E}, // MATHEMATICAL DOUBLE-STRUCK SMALL M
+ {"mp", 0x02213}, // MINUS-OR-PLUS SIGN
+ {"Mscr", 0x02133}, // SCRIPT CAPITAL M
+ {"mscr", 0x1D4C2}, // MATHEMATICAL SCRIPT SMALL M
+ {"mstpos", 0x0223E}, // INVERTED LAZY S
+ {"Mu", 0x0039C}, // GREEK CAPITAL LETTER MU
+ {"mu", 0x003BC}, // GREEK SMALL LETTER MU
+ {"multimap", 0x022B8}, // MULTIMAP
+ {"mumap", 0x022B8}, // MULTIMAP
+];
+
+immutable NameId[] namesN =
+[
+ {"nabla", 0x02207}, // NABLA
+ {"Nacute", 0x00143}, // LATIN CAPITAL LETTER N WITH ACUTE
+ {"nacute", 0x00144}, // LATIN SMALL LETTER N WITH ACUTE
+// "nang", 0x02220;0x020D2}, // ANGLE with vertical line
+ {"nap", 0x02249}, // NOT ALMOST EQUAL TO
+// "napE", 0x02A70;0x00338}, // APPROXIMATELY EQUAL OR EQUAL TO with slash
+// "napid", 0x0224B;0x00338}, // TRIPLE TILDE with slash
+ {"napos", 0x00149}, // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+ {"napprox", 0x02249}, // NOT ALMOST EQUAL TO
+ {"natur", 0x0266E}, // MUSIC NATURAL SIGN
+ {"natural", 0x0266E}, // MUSIC NATURAL SIGN
+ {"naturals", 0x02115}, // DOUBLE-STRUCK CAPITAL N
+ {"nbsp", 0x000A0}, // NO-BREAK SPACE
+// "nbump", 0x0224E;0x00338}, // GEOMETRICALLY EQUIVALENT TO with slash
+// "nbumpe", 0x0224F;0x00338}, // DIFFERENCE BETWEEN with slash
+ {"ncap", 0x02A43}, // INTERSECTION WITH OVERBAR
+ {"Ncaron", 0x00147}, // LATIN CAPITAL LETTER N WITH CARON
+ {"ncaron", 0x00148}, // LATIN SMALL LETTER N WITH CARON
+ {"Ncedil", 0x00145}, // LATIN CAPITAL LETTER N WITH CEDILLA
+ {"ncedil", 0x00146}, // LATIN SMALL LETTER N WITH CEDILLA
+ {"ncong", 0x02247}, // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+// "ncongdot", 0x02A6D;0x00338}, // CONGRUENT WITH DOT ABOVE with slash
+ {"ncup", 0x02A42}, // UNION WITH OVERBAR
+ {"Ncy", 0x0041D}, // CYRILLIC CAPITAL LETTER EN
+ {"ncy", 0x0043D}, // CYRILLIC SMALL LETTER EN
+ {"ndash", 0x02013}, // EN DASH
+ {"ne", 0x02260}, // NOT EQUAL TO
+ {"nearhk", 0x02924}, // NORTH EAST ARROW WITH HOOK
+ {"nearr", 0x02197}, // NORTH EAST ARROW
+ {"neArr", 0x021D7}, // NORTH EAST DOUBLE ARROW
+ {"nearrow", 0x02197}, // NORTH EAST ARROW
+// "nedot", 0x02250;0x00338}, // APPROACHES THE LIMIT with slash
+ {"NegativeMediumSpace", 0x0200B}, // ZERO WIDTH SPACE
+ {"NegativeThickSpace", 0x0200B}, // ZERO WIDTH SPACE
+ {"NegativeThinSpace", 0x0200B}, // ZERO WIDTH SPACE
+ {"NegativeVeryThinSpace", 0x0200B}, // ZERO WIDTH SPACE
+ {"nequiv", 0x02262}, // NOT IDENTICAL TO
+ {"nesear", 0x02928}, // NORTH EAST ARROW AND SOUTH EAST ARROW
+// "nesim", 0x02242;0x00338}, // MINUS TILDE with slash
+ {"NestedGreaterGreater", 0x0226B}, // MUCH GREATER-THAN
+ {"NestedLessLess", 0x0226A}, // MUCH LESS-THAN
+ {"NewLine", 0x0000A}, // LINE FEED (LF)
+ {"nexist", 0x02204}, // THERE DOES NOT EXIST
+ {"nexists", 0x02204}, // THERE DOES NOT EXIST
+ {"Nfr", 0x1D511}, // MATHEMATICAL FRAKTUR CAPITAL N
+ {"nfr", 0x1D52B}, // MATHEMATICAL FRAKTUR SMALL N
+// "ngE", 0x02267;0x00338}, // GREATER-THAN OVER EQUAL TO with slash
+ {"nge", 0x02271}, // NEITHER GREATER-THAN NOR EQUAL TO
+ {"ngeq", 0x02271}, // NEITHER GREATER-THAN NOR EQUAL TO
+// "ngeqq", 0x02267;0x00338}, // GREATER-THAN OVER EQUAL TO with slash
+// "ngeqslant", 0x02A7E;0x00338}, // GREATER-THAN OR SLANTED EQUAL TO with slash
+// "nges", 0x02A7E;0x00338}, // GREATER-THAN OR SLANTED EQUAL TO with slash
+// "nGg", 0x022D9;0x00338}, // VERY MUCH GREATER-THAN with slash
+ {"Ngr", 0x0039D}, // GREEK CAPITAL LETTER NU
+ {"ngr", 0x003BD}, // GREEK SMALL LETTER NU
+ {"ngsim", 0x02275}, // NEITHER GREATER-THAN NOR EQUIVALENT TO
+// "nGt", 0x0226B;0x020D2}, // MUCH GREATER THAN with vertical line
+ {"ngt", 0x0226F}, // NOT GREATER-THAN
+ {"ngtr", 0x0226F}, // NOT GREATER-THAN
+// "nGtv", 0x0226B;0x00338}, // MUCH GREATER THAN with slash
+ {"nharr", 0x021AE}, // LEFT RIGHT ARROW WITH STROKE
+ {"nhArr", 0x021CE}, // LEFT RIGHT DOUBLE ARROW WITH STROKE
+ {"nhpar", 0x02AF2}, // PARALLEL WITH HORIZONTAL STROKE
+ {"ni", 0x0220B}, // CONTAINS AS MEMBER
+ {"nis", 0x022FC}, // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ {"nisd", 0x022FA}, // CONTAINS WITH LONG HORIZONTAL STROKE
+ {"niv", 0x0220B}, // CONTAINS AS MEMBER
+ {"NJcy", 0x0040A}, // CYRILLIC CAPITAL LETTER NJE
+ {"njcy", 0x0045A}, // CYRILLIC SMALL LETTER NJE
+ {"nlarr", 0x0219A}, // LEFTWARDS ARROW WITH STROKE
+ {"nlArr", 0x021CD}, // LEFTWARDS DOUBLE ARROW WITH STROKE
+ {"nldr", 0x02025}, // TWO DOT LEADER
+// "nlE", 0x02266;0x00338}, // LESS-THAN OVER EQUAL TO with slash
+ {"nle", 0x02270}, // NEITHER LESS-THAN NOR EQUAL TO
+ {"nleftarrow", 0x0219A}, // LEFTWARDS ARROW WITH STROKE
+ {"nLeftarrow", 0x021CD}, // LEFTWARDS DOUBLE ARROW WITH STROKE
+ {"nleftrightarrow", 0x021AE}, // LEFT RIGHT ARROW WITH STROKE
+ {"nLeftrightarrow", 0x021CE}, // LEFT RIGHT DOUBLE ARROW WITH STROKE
+ {"nleq", 0x02270}, // NEITHER LESS-THAN NOR EQUAL TO
+// "nleqq", 0x02266;0x00338}, // LESS-THAN OVER EQUAL TO with slash
+// "nleqslant", 0x02A7D;0x00338}, // LESS-THAN OR SLANTED EQUAL TO with slash
+// "nles", 0x02A7D;0x00338}, // LESS-THAN OR SLANTED EQUAL TO with slash
+ {"nless", 0x0226E}, // NOT LESS-THAN
+// "nLl", 0x022D8;0x00338}, // VERY MUCH LESS-THAN with slash
+ {"nlsim", 0x02274}, // NEITHER LESS-THAN NOR EQUIVALENT TO
+// "nLt", 0x0226A;0x020D2}, // MUCH LESS THAN with vertical line
+ {"nlt", 0x0226E}, // NOT LESS-THAN
+ {"nltri", 0x022EA}, // NOT NORMAL SUBGROUP OF
+ {"nltrie", 0x022EC}, // NOT NORMAL SUBGROUP OF OR EQUAL TO
+// "nLtv", 0x0226A;0x00338}, // MUCH LESS THAN with slash
+ {"nmid", 0x02224}, // DOES NOT DIVIDE
+ {"NoBreak", 0x02060}, // WORD JOINER
+ {"NonBreakingSpace", 0x000A0}, // NO-BREAK SPACE
+ {"Nopf", 0x02115}, // DOUBLE-STRUCK CAPITAL N
+ {"nopf", 0x1D55F}, // MATHEMATICAL DOUBLE-STRUCK SMALL N
+ {"not", 0x000AC}, // NOT SIGN
+ {"Not", 0x02AEC}, // DOUBLE STROKE NOT SIGN
+ {"NotCongruent", 0x02262}, // NOT IDENTICAL TO
+ {"NotCupCap", 0x0226D}, // NOT EQUIVALENT TO
+ {"NotDoubleVerticalBar", 0x02226}, // NOT PARALLEL TO
+ {"NotElement", 0x02209}, // NOT AN ELEMENT OF
+ {"NotEqual", 0x02260}, // NOT EQUAL TO
+// "NotEqualTilde", 0x02242;0x00338}, // MINUS TILDE with slash
+ {"NotExists", 0x02204}, // THERE DOES NOT EXIST
+ {"NotGreater", 0x0226F}, // NOT GREATER-THAN
+ {"NotGreaterEqual", 0x02271}, // NEITHER GREATER-THAN NOR EQUAL TO
+// "NotGreaterFullEqual", 0x02267;0x00338}, // GREATER-THAN OVER EQUAL TO with slash
+// "NotGreaterGreater", 0x0226B;0x00338}, // MUCH GREATER THAN with slash
+ {"NotGreaterLess", 0x02279}, // NEITHER GREATER-THAN NOR LESS-THAN
+// "NotGreaterSlantEqual", 0x02A7E;0x00338}, // GREATER-THAN OR SLANTED EQUAL TO with slash
+ {"NotGreaterTilde", 0x02275}, // NEITHER GREATER-THAN NOR EQUIVALENT TO
+// "NotHumpDownHump", 0x0224E;0x00338}, // GEOMETRICALLY EQUIVALENT TO with slash
+// "NotHumpEqual", 0x0224F;0x00338}, // DIFFERENCE BETWEEN with slash
+ {"notin", 0x02209}, // NOT AN ELEMENT OF
+// "notindot", 0x022F5;0x00338}, // ELEMENT OF WITH DOT ABOVE with slash
+// "notinE", 0x022F9;0x00338}, // ELEMENT OF WITH TWO HORIZONTAL STROKES with slash
+ {"notinva", 0x02209}, // NOT AN ELEMENT OF
+ {"notinvb", 0x022F7}, // SMALL ELEMENT OF WITH OVERBAR
+ {"notinvc", 0x022F6}, // ELEMENT OF WITH OVERBAR
+ {"NotLeftTriangle", 0x022EA}, // NOT NORMAL SUBGROUP OF
+// "NotLeftTriangleBar", 0x029CF;0x00338}, // LEFT TRIANGLE BESIDE VERTICAL BAR with slash
+ {"NotLeftTriangleEqual", 0x022EC}, // NOT NORMAL SUBGROUP OF OR EQUAL TO
+ {"NotLess", 0x0226E}, // NOT LESS-THAN
+ {"NotLessEqual", 0x02270}, // NEITHER LESS-THAN NOR EQUAL TO
+ {"NotLessGreater", 0x02278}, // NEITHER LESS-THAN NOR GREATER-THAN
+// "NotLessLess", 0x0226A;0x00338}, // MUCH LESS THAN with slash
+// "NotLessSlantEqual", 0x02A7D;0x00338}, // LESS-THAN OR SLANTED EQUAL TO with slash
+ {"NotLessTilde", 0x02274}, // NEITHER LESS-THAN NOR EQUIVALENT TO
+// "NotNestedGreaterGreater", 0x02AA2;0x00338}, // DOUBLE NESTED GREATER-THAN with slash
+// "NotNestedLessLess", 0x02AA1;0x00338}, // DOUBLE NESTED LESS-THAN with slash
+ {"notni", 0x0220C}, // DOES NOT CONTAIN AS MEMBER
+ {"notniva", 0x0220C}, // DOES NOT CONTAIN AS MEMBER
+ {"notnivb", 0x022FE}, // SMALL CONTAINS WITH OVERBAR
+ {"notnivc", 0x022FD}, // CONTAINS WITH OVERBAR
+ {"NotPrecedes", 0x02280}, // DOES NOT PRECEDE
+// "NotPrecedesEqual", 0x02AAF;0x00338}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash
+ {"NotPrecedesSlantEqual", 0x022E0}, // DOES NOT PRECEDE OR EQUAL
+ {"NotReverseElement", 0x0220C}, // DOES NOT CONTAIN AS MEMBER
+ {"NotRightTriangle", 0x022EB}, // DOES NOT CONTAIN AS NORMAL SUBGROUP
+// "NotRightTriangleBar", 0x029D0;0x00338}, // VERTICAL BAR BESIDE RIGHT TRIANGLE with slash
+ {"NotRightTriangleEqual", 0x022ED}, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+// "NotSquareSubset", 0x0228F;0x00338}, // SQUARE IMAGE OF with slash
+ {"NotSquareSubsetEqual", 0x022E2}, // NOT SQUARE IMAGE OF OR EQUAL TO
+// "NotSquareSuperset", 0x02290;0x00338}, // SQUARE ORIGINAL OF with slash
+ {"NotSquareSupersetEqual", 0x022E3}, // NOT SQUARE ORIGINAL OF OR EQUAL TO
+// "NotSubset", 0x02282;0x020D2}, // SUBSET OF with vertical line
+ {"NotSubsetEqual", 0x02288}, // NEITHER A SUBSET OF NOR EQUAL TO
+ {"NotSucceeds", 0x02281}, // DOES NOT SUCCEED
+// "NotSucceedsEqual", 0x02AB0;0x00338}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash
+ {"NotSucceedsSlantEqual", 0x022E1}, // DOES NOT SUCCEED OR EQUAL
+// "NotSucceedsTilde", 0x0227F;0x00338}, // SUCCEEDS OR EQUIVALENT TO with slash
+// "NotSuperset", 0x02283;0x020D2}, // SUPERSET OF with vertical line
+ {"NotSupersetEqual", 0x02289}, // NEITHER A SUPERSET OF NOR EQUAL TO
+ {"NotTilde", 0x02241}, // NOT TILDE
+ {"NotTildeEqual", 0x02244}, // NOT ASYMPTOTICALLY EQUAL TO
+ {"NotTildeFullEqual", 0x02247}, // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+ {"NotTildeTilde", 0x02249}, // NOT ALMOST EQUAL TO
+ {"NotVerticalBar", 0x02224}, // DOES NOT DIVIDE
+ {"npar", 0x02226}, // NOT PARALLEL TO
+ {"nparallel", 0x02226}, // NOT PARALLEL TO
+// "nparsl", 0x02AFD;0x020E5}, // DOUBLE SOLIDUS OPERATOR with reverse slash
+// "npart", 0x02202;0x00338}, // PARTIAL DIFFERENTIAL with slash
+ {"npolint", 0x02A14}, // LINE INTEGRATION NOT INCLUDING THE POLE
+ {"npr", 0x02280}, // DOES NOT PRECEDE
+ {"nprcue", 0x022E0}, // DOES NOT PRECEDE OR EQUAL
+// "npre", 0x02AAF;0x00338}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash
+ {"nprec", 0x02280}, // DOES NOT PRECEDE
+// "npreceq", 0x02AAF;0x00338}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash
+ {"nrarr", 0x0219B}, // RIGHTWARDS ARROW WITH STROKE
+ {"nrArr", 0x021CF}, // RIGHTWARDS DOUBLE ARROW WITH STROKE
+// "nrarrc", 0x02933;0x00338}, // WAVE ARROW POINTING DIRECTLY RIGHT with slash
+// "nrarrw", 0x0219D;0x00338}, // RIGHTWARDS WAVE ARROW with slash
+ {"nrightarrow", 0x0219B}, // RIGHTWARDS ARROW WITH STROKE
+ {"nRightarrow", 0x021CF}, // RIGHTWARDS DOUBLE ARROW WITH STROKE
+ {"nrtri", 0x022EB}, // DOES NOT CONTAIN AS NORMAL SUBGROUP
+ {"nrtrie", 0x022ED}, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+ {"nsc", 0x02281}, // DOES NOT SUCCEED
+ {"nsccue", 0x022E1}, // DOES NOT SUCCEED OR EQUAL
+// "nsce", 0x02AB0;0x00338}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash
+ {"Nscr", 0x1D4A9}, // MATHEMATICAL SCRIPT CAPITAL N
+ {"nscr", 0x1D4C3}, // MATHEMATICAL SCRIPT SMALL N
+ {"nshortmid", 0x02224}, // DOES NOT DIVIDE
+ {"nshortparallel", 0x02226}, // NOT PARALLEL TO
+ {"nsim", 0x02241}, // NOT TILDE
+ {"nsime", 0x02244}, // NOT ASYMPTOTICALLY EQUAL TO
+ {"nsimeq", 0x02244}, // NOT ASYMPTOTICALLY EQUAL TO
+ {"nsmid", 0x02224}, // DOES NOT DIVIDE
+ {"nspar", 0x02226}, // NOT PARALLEL TO
+ {"nsqsube", 0x022E2}, // NOT SQUARE IMAGE OF OR EQUAL TO
+ {"nsqsupe", 0x022E3}, // NOT SQUARE ORIGINAL OF OR EQUAL TO
+ {"nsub", 0x02284}, // NOT A SUBSET OF
+ {"nsube", 0x02288}, // NEITHER A SUBSET OF NOR EQUAL TO
+// "nsubE", 0x02AC5;0x00338}, // SUBSET OF ABOVE EQUALS SIGN with slash
+// "nsubset", 0x02282;0x020D2}, // SUBSET OF with vertical line
+ {"nsubseteq", 0x02288}, // NEITHER A SUBSET OF NOR EQUAL TO
+// "nsubseteqq", 0x02AC5;0x00338}, // SUBSET OF ABOVE EQUALS SIGN with slash
+ {"nsucc", 0x02281}, // DOES NOT SUCCEED
+// "nsucceq", 0x02AB0;0x00338}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash
+ {"nsup", 0x02285}, // NOT A SUPERSET OF
+ {"nsupe", 0x02289}, // NEITHER A SUPERSET OF NOR EQUAL TO
+// "nsupE", 0x02AC6;0x00338}, // SUPERSET OF ABOVE EQUALS SIGN with slash
+// "nsupset", 0x02283;0x020D2}, // SUPERSET OF with vertical line
+ {"nsupseteq", 0x02289}, // NEITHER A SUPERSET OF NOR EQUAL TO
+// "nsupseteqq", 0x02AC6;0x00338}, // SUPERSET OF ABOVE EQUALS SIGN with slash
+ {"ntgl", 0x02279}, // NEITHER GREATER-THAN NOR LESS-THAN
+ {"Ntilde", 0x000D1}, // LATIN CAPITAL LETTER N WITH TILDE
+ {"ntilde", 0x000F1}, // LATIN SMALL LETTER N WITH TILDE
+ {"ntlg", 0x02278}, // NEITHER LESS-THAN NOR GREATER-THAN
+ {"ntriangleleft", 0x022EA}, // NOT NORMAL SUBGROUP OF
+ {"ntrianglelefteq", 0x022EC}, // NOT NORMAL SUBGROUP OF OR EQUAL TO
+ {"ntriangleright", 0x022EB}, // DOES NOT CONTAIN AS NORMAL SUBGROUP
+ {"ntrianglerighteq", 0x022ED}, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+ {"Nu", 0x0039D}, // GREEK CAPITAL LETTER NU
+ {"nu", 0x003BD}, // GREEK SMALL LETTER NU
+ {"num", 0x00023}, // NUMBER SIGN
+ {"numero", 0x02116}, // NUMERO SIGN
+ {"numsp", 0x02007}, // FIGURE SPACE
+// "nvap", 0x0224D;0x020D2}, // EQUIVALENT TO with vertical line
+ {"nvdash", 0x022AC}, // DOES NOT PROVE
+ {"nvDash", 0x022AD}, // NOT TRUE
+ {"nVdash", 0x022AE}, // DOES NOT FORCE
+ {"nVDash", 0x022AF}, // NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+// "nvge", 0x02265;0x020D2}, // GREATER-THAN OR EQUAL TO with vertical line
+// "nvgt", 0x0003E;0x020D2}, // GREATER-THAN SIGN with vertical line
+ {"nvHarr", 0x02904}, // LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE
+ {"nvinfin", 0x029DE}, // INFINITY NEGATED WITH VERTICAL BAR
+ {"nvlArr", 0x02902}, // LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE
+// "nvle", 0x02264;0x020D2}, // LESS-THAN OR EQUAL TO with vertical line
+// "nvlt", 0x0003C;0x020D2}, // LESS-THAN SIGN with vertical line
+// "nvltrie", 0x022B4;0x020D2}, // NORMAL SUBGROUP OF OR EQUAL TO with vertical line
+ {"nvrArr", 0x02903}, // RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE
+// "nvrtrie", 0x022B5;0x020D2}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO with vertical line
+// "nvsim", 0x0223C;0x020D2}, // TILDE OPERATOR with vertical line
+ {"nwarhk", 0x02923}, // NORTH WEST ARROW WITH HOOK
+ {"nwarr", 0x02196}, // NORTH WEST ARROW
+ {"nwArr", 0x021D6}, // NORTH WEST DOUBLE ARROW
+ {"nwarrow", 0x02196}, // NORTH WEST ARROW
+ {"nwnear", 0x02927}, // NORTH WEST ARROW AND NORTH EAST ARROW
+];
+
+immutable NameId[] namesO =
+[
+ {"Oacgr", 0x0038C}, // GREEK CAPITAL LETTER OMICRON WITH TONOS
+ {"oacgr", 0x003CC}, // GREEK SMALL LETTER OMICRON WITH TONOS
+ {"Oacute", 0x000D3}, // LATIN CAPITAL LETTER O WITH ACUTE
+ {"oacute", 0x000F3}, // LATIN SMALL LETTER O WITH ACUTE
+ {"oast", 0x0229B}, // CIRCLED ASTERISK OPERATOR
+ {"ocir", 0x0229A}, // CIRCLED RING OPERATOR
+ {"Ocirc", 0x000D4}, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+ {"ocirc", 0x000F4}, // LATIN SMALL LETTER O WITH CIRCUMFLEX
+ {"Ocy", 0x0041E}, // CYRILLIC CAPITAL LETTER O
+ {"ocy", 0x0043E}, // CYRILLIC SMALL LETTER O
+ {"odash", 0x0229D}, // CIRCLED DASH
+ {"Odblac", 0x00150}, // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+ {"odblac", 0x00151}, // LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ {"odiv", 0x02A38}, // CIRCLED DIVISION SIGN
+ {"odot", 0x02299}, // CIRCLED DOT OPERATOR
+ {"odsold", 0x029BC}, // CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN
+ {"OElig", 0x00152}, // LATIN CAPITAL LIGATURE OE
+ {"oelig", 0x00153}, // LATIN SMALL LIGATURE OE
+ {"ofcir", 0x029BF}, // CIRCLED BULLET
+ {"Ofr", 0x1D512}, // MATHEMATICAL FRAKTUR CAPITAL O
+ {"ofr", 0x1D52C}, // MATHEMATICAL FRAKTUR SMALL O
+ {"ogon", 0x002DB}, // OGONEK
+ {"Ogr", 0x0039F}, // GREEK CAPITAL LETTER OMICRON
+ {"ogr", 0x003BF}, // GREEK SMALL LETTER OMICRON
+ {"Ograve", 0x000D2}, // LATIN CAPITAL LETTER O WITH GRAVE
+ {"ograve", 0x000F2}, // LATIN SMALL LETTER O WITH GRAVE
+ {"ogt", 0x029C1}, // CIRCLED GREATER-THAN
+ {"OHacgr", 0x0038F}, // GREEK CAPITAL LETTER OMEGA WITH TONOS
+ {"ohacgr", 0x003CE}, // GREEK SMALL LETTER OMEGA WITH TONOS
+ {"ohbar", 0x029B5}, // CIRCLE WITH HORIZONTAL BAR
+ {"OHgr", 0x003A9}, // GREEK CAPITAL LETTER OMEGA
+ {"ohgr", 0x003C9}, // GREEK SMALL LETTER OMEGA
+ {"ohm", 0x003A9}, // GREEK CAPITAL LETTER OMEGA
+ {"oint", 0x0222E}, // CONTOUR INTEGRAL
+ {"olarr", 0x021BA}, // ANTICLOCKWISE OPEN CIRCLE ARROW
+ {"olcir", 0x029BE}, // CIRCLED WHITE BULLET
+ {"olcross", 0x029BB}, // CIRCLE WITH SUPERIMPOSED X
+ {"oline", 0x0203E}, // OVERLINE
+ {"olt", 0x029C0}, // CIRCLED LESS-THAN
+ {"Omacr", 0x0014C}, // LATIN CAPITAL LETTER O WITH MACRON
+ {"omacr", 0x0014D}, // LATIN SMALL LETTER O WITH MACRON
+ {"Omega", 0x003A9}, // GREEK CAPITAL LETTER OMEGA
+ {"omega", 0x003C9}, // GREEK SMALL LETTER OMEGA
+ {"Omicron", 0x0039F}, // GREEK CAPITAL LETTER OMICRON
+ {"omicron", 0x003BF}, // GREEK SMALL LETTER OMICRON
+ {"omid", 0x029B6}, // CIRCLED VERTICAL BAR
+ {"ominus", 0x02296}, // CIRCLED MINUS
+ {"Oopf", 0x1D546}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+ {"oopf", 0x1D560}, // MATHEMATICAL DOUBLE-STRUCK SMALL O
+ {"opar", 0x029B7}, // CIRCLED PARALLEL
+ {"OpenCurlyDoubleQuote", 0x0201C}, // LEFT DOUBLE QUOTATION MARK
+ {"OpenCurlyQuote", 0x02018}, // LEFT SINGLE QUOTATION MARK
+ {"operp", 0x029B9}, // CIRCLED PERPENDICULAR
+ {"oplus", 0x02295}, // CIRCLED PLUS
+ {"or", 0x02228}, // LOGICAL OR
+ {"Or", 0x02A54}, // DOUBLE LOGICAL OR
+ {"orarr", 0x021BB}, // CLOCKWISE OPEN CIRCLE ARROW
+ {"ord", 0x02A5D}, // LOGICAL OR WITH HORIZONTAL DASH
+ {"order", 0x02134}, // SCRIPT SMALL O
+ {"orderof", 0x02134}, // SCRIPT SMALL O
+ {"ordf", 0x000AA}, // FEMININE ORDINAL INDICATOR
+ {"ordm", 0x000BA}, // MASCULINE ORDINAL INDICATOR
+ {"origof", 0x022B6}, // ORIGINAL OF
+ {"oror", 0x02A56}, // TWO INTERSECTING LOGICAL OR
+ {"orslope", 0x02A57}, // SLOPING LARGE OR
+ {"orv", 0x02A5B}, // LOGICAL OR WITH MIDDLE STEM
+ {"oS", 0x024C8}, // CIRCLED LATIN CAPITAL LETTER S
+ {"oscr", 0x02134}, // SCRIPT SMALL O
+ {"Oscr", 0x1D4AA}, // MATHEMATICAL SCRIPT CAPITAL O
+ {"Oslash", 0x000D8}, // LATIN CAPITAL LETTER O WITH STROKE
+ {"oslash", 0x000F8}, // LATIN SMALL LETTER O WITH STROKE
+ {"osol", 0x02298}, // CIRCLED DIVISION SLASH
+ {"Otilde", 0x000D5}, // LATIN CAPITAL LETTER O WITH TILDE
+ {"otilde", 0x000F5}, // LATIN SMALL LETTER O WITH TILDE
+ {"otimes", 0x02297}, // CIRCLED TIMES
+ {"Otimes", 0x02A37}, // MULTIPLICATION SIGN IN DOUBLE CIRCLE
+ {"otimesas", 0x02A36}, // CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT
+ {"Ouml", 0x000D6}, // LATIN CAPITAL LETTER O WITH DIAERESIS
+ {"ouml", 0x000F6}, // LATIN SMALL LETTER O WITH DIAERESIS
+ {"ovbar", 0x0233D}, // APL FUNCTIONAL SYMBOL CIRCLE STILE
+ {"OverBar", 0x0203E}, // OVERLINE
+ {"OverBrace", 0x023DE}, // TOP CURLY BRACKET
+ {"OverBracket", 0x023B4}, // TOP SQUARE BRACKET
+ {"OverParenthesis", 0x023DC}, // TOP PARENTHESIS
+];
+
+immutable NameId[] namesP =
+[
+ {"par", 0x02225}, // PARALLEL TO
+ {"para", 0x000B6}, // PILCROW SIGN
+ {"parallel", 0x02225}, // PARALLEL TO
+ {"parsim", 0x02AF3}, // PARALLEL WITH TILDE OPERATOR
+ {"parsl", 0x02AFD}, // DOUBLE SOLIDUS OPERATOR
+ {"part", 0x02202}, // PARTIAL DIFFERENTIAL
+ {"PartialD", 0x02202}, // PARTIAL DIFFERENTIAL
+ {"Pcy", 0x0041F}, // CYRILLIC CAPITAL LETTER PE
+ {"pcy", 0x0043F}, // CYRILLIC SMALL LETTER PE
+ {"percnt", 0x00025}, // PERCENT SIGN
+ {"period", 0x0002E}, // FULL STOP
+ {"permil", 0x02030}, // PER MILLE SIGN
+ {"perp", 0x022A5}, // UP TACK
+ {"pertenk", 0x02031}, // PER TEN THOUSAND SIGN
+ {"Pfr", 0x1D513}, // MATHEMATICAL FRAKTUR CAPITAL P
+ {"pfr", 0x1D52D}, // MATHEMATICAL FRAKTUR SMALL P
+ {"Pgr", 0x003A0}, // GREEK CAPITAL LETTER PI
+ {"pgr", 0x003C0}, // GREEK SMALL LETTER PI
+ {"PHgr", 0x003A6}, // GREEK CAPITAL LETTER PHI
+ {"phgr", 0x003C6}, // GREEK SMALL LETTER PHI
+ {"Phi", 0x003A6}, // GREEK CAPITAL LETTER PHI
+ {"phi", 0x003C6}, // GREEK SMALL LETTER PHI
+ {"phiv", 0x003D5}, // GREEK PHI SYMBOL
+ {"phmmat", 0x02133}, // SCRIPT CAPITAL M
+ {"phone", 0x0260E}, // BLACK TELEPHONE
+ {"Pi", 0x003A0}, // GREEK CAPITAL LETTER PI
+ {"pi", 0x003C0}, // GREEK SMALL LETTER PI
+ {"pitchfork", 0x022D4}, // PITCHFORK
+ {"piv", 0x003D6}, // GREEK PI SYMBOL
+ {"planck", 0x0210F}, // PLANCK CONSTANT OVER TWO PI
+ {"planckh", 0x0210E}, // PLANCK CONSTANT
+ {"plankv", 0x0210F}, // PLANCK CONSTANT OVER TWO PI
+ {"plus", 0x0002B}, // PLUS SIGN
+ {"plusacir", 0x02A23}, // PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE
+ {"plusb", 0x0229E}, // SQUARED PLUS
+ {"pluscir", 0x02A22}, // PLUS SIGN WITH SMALL CIRCLE ABOVE
+ {"plusdo", 0x02214}, // DOT PLUS
+ {"plusdu", 0x02A25}, // PLUS SIGN WITH DOT BELOW
+ {"pluse", 0x02A72}, // PLUS SIGN ABOVE EQUALS SIGN
+ {"PlusMinus", 0x000B1}, // PLUS-MINUS SIGN
+ {"plusmn", 0x000B1}, // PLUS-MINUS SIGN
+ {"plussim", 0x02A26}, // PLUS SIGN WITH TILDE BELOW
+ {"plustwo", 0x02A27}, // PLUS SIGN WITH SUBSCRIPT TWO
+ {"pm", 0x000B1}, // PLUS-MINUS SIGN
+ {"Poincareplane", 0x0210C}, // BLACK-LETTER CAPITAL H
+ {"pointint", 0x02A15}, // INTEGRAL AROUND A POINT OPERATOR
+ {"Popf", 0x02119}, // DOUBLE-STRUCK CAPITAL P
+ {"popf", 0x1D561}, // MATHEMATICAL DOUBLE-STRUCK SMALL P
+ {"pound", 0x000A3}, // POUND SIGN
+ {"pr", 0x0227A}, // PRECEDES
+ {"Pr", 0x02ABB}, // DOUBLE PRECEDES
+ {"prap", 0x02AB7}, // PRECEDES ABOVE ALMOST EQUAL TO
+ {"prcue", 0x0227C}, // PRECEDES OR EQUAL TO
+ {"pre", 0x02AAF}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
+ {"prE", 0x02AB3}, // PRECEDES ABOVE EQUALS SIGN
+ {"prec", 0x0227A}, // PRECEDES
+ {"precapprox", 0x02AB7}, // PRECEDES ABOVE ALMOST EQUAL TO
+ {"preccurlyeq", 0x0227C}, // PRECEDES OR EQUAL TO
+ {"Precedes", 0x0227A}, // PRECEDES
+ {"PrecedesEqual", 0x02AAF}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
+ {"PrecedesSlantEqual", 0x0227C}, // PRECEDES OR EQUAL TO
+ {"PrecedesTilde", 0x0227E}, // PRECEDES OR EQUIVALENT TO
+ {"preceq", 0x02AAF}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
+ {"precnapprox", 0x02AB9}, // PRECEDES ABOVE NOT ALMOST EQUAL TO
+ {"precneqq", 0x02AB5}, // PRECEDES ABOVE NOT EQUAL TO
+ {"precnsim", 0x022E8}, // PRECEDES BUT NOT EQUIVALENT TO
+ {"precsim", 0x0227E}, // PRECEDES OR EQUIVALENT TO
+ {"prime", 0x02032}, // PRIME
+ {"Prime", 0x02033}, // DOUBLE PRIME
+ {"primes", 0x02119}, // DOUBLE-STRUCK CAPITAL P
+ {"prnap", 0x02AB9}, // PRECEDES ABOVE NOT ALMOST EQUAL TO
+ {"prnE", 0x02AB5}, // PRECEDES ABOVE NOT EQUAL TO
+ {"prnsim", 0x022E8}, // PRECEDES BUT NOT EQUIVALENT TO
+ {"prod", 0x0220F}, // N-ARY PRODUCT
+ {"Product", 0x0220F}, // N-ARY PRODUCT
+ {"profalar", 0x0232E}, // ALL AROUND-PROFILE
+ {"profline", 0x02312}, // ARC
+ {"profsurf", 0x02313}, // SEGMENT
+ {"prop", 0x0221D}, // PROPORTIONAL TO
+ {"Proportion", 0x02237}, // PROPORTION
+ {"Proportional", 0x0221D}, // PROPORTIONAL TO
+ {"propto", 0x0221D}, // PROPORTIONAL TO
+ {"prsim", 0x0227E}, // PRECEDES OR EQUIVALENT TO
+ {"prurel", 0x022B0}, // PRECEDES UNDER RELATION
+ {"Pscr", 0x1D4AB}, // MATHEMATICAL SCRIPT CAPITAL P
+ {"pscr", 0x1D4C5}, // MATHEMATICAL SCRIPT SMALL P
+ {"PSgr", 0x003A8}, // GREEK CAPITAL LETTER PSI
+ {"psgr", 0x003C8}, // GREEK SMALL LETTER PSI
+ {"Psi", 0x003A8}, // GREEK CAPITAL LETTER PSI
+ {"psi", 0x003C8}, // GREEK SMALL LETTER PSI
+ {"puncsp", 0x02008}, // PUNCTUATION SPACE
+];
+
+immutable NameId[] namesQ =
+[
+ {"Qfr", 0x1D514}, // MATHEMATICAL FRAKTUR CAPITAL Q
+ {"qfr", 0x1D52E}, // MATHEMATICAL FRAKTUR SMALL Q
+ {"qint", 0x02A0C}, // QUADRUPLE INTEGRAL OPERATOR
+ {"Qopf", 0x0211A}, // DOUBLE-STRUCK CAPITAL Q
+ {"qopf", 0x1D562}, // MATHEMATICAL DOUBLE-STRUCK SMALL Q
+ {"qprime", 0x02057}, // QUADRUPLE PRIME
+ {"Qscr", 0x1D4AC}, // MATHEMATICAL SCRIPT CAPITAL Q
+ {"qscr", 0x1D4C6}, // MATHEMATICAL SCRIPT SMALL Q
+ {"quaternions", 0x0210D}, // DOUBLE-STRUCK CAPITAL H
+ {"quatint", 0x02A16}, // QUATERNION INTEGRAL OPERATOR
+ {"quest", 0x0003F}, // QUESTION MARK
+ {"questeq", 0x0225F}, // QUESTIONED EQUAL TO
+ {"quot", 0x00022}, // QUOTATION MARK
+ {"QUOT", 0x00022}, // QUOTATION MARK
+];
+
+immutable NameId[] namesR =
+[
+ {"rAarr", 0x021DB}, // RIGHTWARDS TRIPLE ARROW
+// "race", 0x0223D;0x00331}, // REVERSED TILDE with underline
+ {"Racute", 0x00154}, // LATIN CAPITAL LETTER R WITH ACUTE
+ {"racute", 0x00155}, // LATIN SMALL LETTER R WITH ACUTE
+ {"radic", 0x0221A}, // SQUARE ROOT
+ {"raemptyv", 0x029B3}, // EMPTY SET WITH RIGHT ARROW ABOVE
+ {"rang", 0x027E9}, // MATHEMATICAL RIGHT ANGLE BRACKET
+ {"Rang", 0x027EB}, // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
+ {"rangd", 0x02992}, // RIGHT ANGLE BRACKET WITH DOT
+ {"range", 0x029A5}, // REVERSED ANGLE WITH UNDERBAR
+ {"rangle", 0x027E9}, // MATHEMATICAL RIGHT ANGLE BRACKET
+ {"raquo", 0x000BB}, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ {"rarr", 0x02192}, // RIGHTWARDS ARROW
+ {"Rarr", 0x021A0}, // RIGHTWARDS TWO HEADED ARROW
+ {"rArr", 0x021D2}, // RIGHTWARDS DOUBLE ARROW
+ {"rarrap", 0x02975}, // RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO
+ {"rarrb", 0x021E5}, // RIGHTWARDS ARROW TO BAR
+ {"rarrbfs", 0x02920}, // RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND
+ {"rarrc", 0x02933}, // WAVE ARROW POINTING DIRECTLY RIGHT
+ {"rarrfs", 0x0291E}, // RIGHTWARDS ARROW TO BLACK DIAMOND
+ {"rarrhk", 0x021AA}, // RIGHTWARDS ARROW WITH HOOK
+ {"rarrlp", 0x021AC}, // RIGHTWARDS ARROW WITH LOOP
+ {"rarrpl", 0x02945}, // RIGHTWARDS ARROW WITH PLUS BELOW
+ {"rarrsim", 0x02974}, // RIGHTWARDS ARROW ABOVE TILDE OPERATOR
+ {"rarrtl", 0x021A3}, // RIGHTWARDS ARROW WITH TAIL
+ {"Rarrtl", 0x02916}, // RIGHTWARDS TWO-HEADED ARROW WITH TAIL
+ {"rarrw", 0x0219D}, // RIGHTWARDS WAVE ARROW
+ {"ratail", 0x0291A}, // RIGHTWARDS ARROW-TAIL
+ {"rAtail", 0x0291C}, // RIGHTWARDS DOUBLE ARROW-TAIL
+ {"ratio", 0x02236}, // RATIO
+ {"rationals", 0x0211A}, // DOUBLE-STRUCK CAPITAL Q
+ {"rbarr", 0x0290D}, // RIGHTWARDS DOUBLE DASH ARROW
+ {"rBarr", 0x0290F}, // RIGHTWARDS TRIPLE DASH ARROW
+ {"RBarr", 0x02910}, // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW
+ {"rbbrk", 0x02773}, // LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
+ {"rbrace", 0x0007D}, // RIGHT CURLY BRACKET
+ {"rbrack", 0x0005D}, // RIGHT SQUARE BRACKET
+ {"rbrke", 0x0298C}, // RIGHT SQUARE BRACKET WITH UNDERBAR
+ {"rbrksld", 0x0298E}, // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+ {"rbrkslu", 0x02990}, // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
+ {"Rcaron", 0x00158}, // LATIN CAPITAL LETTER R WITH CARON
+ {"rcaron", 0x00159}, // LATIN SMALL LETTER R WITH CARON
+ {"Rcedil", 0x00156}, // LATIN CAPITAL LETTER R WITH CEDILLA
+ {"rcedil", 0x00157}, // LATIN SMALL LETTER R WITH CEDILLA
+ {"rceil", 0x02309}, // RIGHT CEILING
+ {"rcub", 0x0007D}, // RIGHT CURLY BRACKET
+ {"Rcy", 0x00420}, // CYRILLIC CAPITAL LETTER ER
+ {"rcy", 0x00440}, // CYRILLIC SMALL LETTER ER
+ {"rdca", 0x02937}, // ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS
+ {"rdldhar", 0x02969}, // RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN
+ {"rdquo", 0x0201D}, // RIGHT DOUBLE QUOTATION MARK
+ {"rdquor", 0x0201D}, // RIGHT DOUBLE QUOTATION MARK
+ {"rdsh", 0x021B3}, // DOWNWARDS ARROW WITH TIP RIGHTWARDS
+ {"Re", 0x0211C}, // BLACK-LETTER CAPITAL R
+ {"real", 0x0211C}, // BLACK-LETTER CAPITAL R
+ {"realine", 0x0211B}, // SCRIPT CAPITAL R
+ {"realpart", 0x0211C}, // BLACK-LETTER CAPITAL R
+ {"reals", 0x0211D}, // DOUBLE-STRUCK CAPITAL R
+ {"rect", 0x025AD}, // WHITE RECTANGLE
+ {"reg", 0x000AE}, // REGISTERED SIGN
+ {"REG", 0x000AE}, // REGISTERED SIGN
+ {"ReverseElement", 0x0220B}, // CONTAINS AS MEMBER
+ {"ReverseEquilibrium", 0x021CB}, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
+ {"ReverseUpEquilibrium", 0x0296F}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
+ {"rfisht", 0x0297D}, // RIGHT FISH TAIL
+ {"rfloor", 0x0230B}, // RIGHT FLOOR
+ {"Rfr", 0x0211C}, // BLACK-LETTER CAPITAL R
+ {"rfr", 0x1D52F}, // MATHEMATICAL FRAKTUR SMALL R
+ {"Rgr", 0x003A1}, // GREEK CAPITAL LETTER RHO
+ {"rgr", 0x003C1}, // GREEK SMALL LETTER RHO
+ {"rHar", 0x02964}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
+ {"rhard", 0x021C1}, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
+ {"rharu", 0x021C0}, // RIGHTWARDS HARPOON WITH BARB UPWARDS
+ {"rharul", 0x0296C}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
+ {"Rho", 0x003A1}, // GREEK CAPITAL LETTER RHO
+ {"rho", 0x003C1}, // GREEK SMALL LETTER RHO
+ {"rhov", 0x003F1}, // GREEK RHO SYMBOL
+ {"RightAngleBracket", 0x027E9}, // MATHEMATICAL RIGHT ANGLE BRACKET
+ {"rightarrow", 0x02192}, // RIGHTWARDS ARROW
+ {"RightArrow", 0x02192}, // RIGHTWARDS ARROW
+ {"Rightarrow", 0x021D2}, // RIGHTWARDS DOUBLE ARROW
+ {"RightArrowBar", 0x021E5}, // RIGHTWARDS ARROW TO BAR
+ {"RightArrowLeftArrow", 0x021C4}, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
+ {"rightarrowtail", 0x021A3}, // RIGHTWARDS ARROW WITH TAIL
+ {"RightCeiling", 0x02309}, // RIGHT CEILING
+ {"RightDoubleBracket", 0x027E7}, // MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+ {"RightDownTeeVector", 0x0295D}, // DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR
+ {"RightDownVector", 0x021C2}, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
+ {"RightDownVectorBar", 0x02955}, // DOWNWARDS HARPOON WITH BARB RIGHT TO BAR
+ {"RightFloor", 0x0230B}, // RIGHT FLOOR
+ {"rightharpoondown", 0x021C1}, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
+ {"rightharpoonup", 0x021C0}, // RIGHTWARDS HARPOON WITH BARB UPWARDS
+ {"rightleftarrows", 0x021C4}, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
+ {"rightleftharpoons", 0x021CC}, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
+ {"rightrightarrows", 0x021C9}, // RIGHTWARDS PAIRED ARROWS
+ {"rightsquigarrow", 0x0219D}, // RIGHTWARDS WAVE ARROW
+ {"RightTee", 0x022A2}, // RIGHT TACK
+ {"RightTeeArrow", 0x021A6}, // RIGHTWARDS ARROW FROM BAR
+ {"RightTeeVector", 0x0295B}, // RIGHTWARDS HARPOON WITH BARB UP FROM BAR
+ {"rightthreetimes", 0x022CC}, // RIGHT SEMIDIRECT PRODUCT
+ {"RightTriangle", 0x022B3}, // CONTAINS AS NORMAL SUBGROUP
+ {"RightTriangleBar", 0x029D0}, // VERTICAL BAR BESIDE RIGHT TRIANGLE
+ {"RightTriangleEqual", 0x022B5}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+ {"RightUpDownVector", 0x0294F}, // UP BARB RIGHT DOWN BARB RIGHT HARPOON
+ {"RightUpTeeVector", 0x0295C}, // UPWARDS HARPOON WITH BARB RIGHT FROM BAR
+ {"RightUpVector", 0x021BE}, // UPWARDS HARPOON WITH BARB RIGHTWARDS
+ {"RightUpVectorBar", 0x02954}, // UPWARDS HARPOON WITH BARB RIGHT TO BAR
+ {"RightVector", 0x021C0}, // RIGHTWARDS HARPOON WITH BARB UPWARDS
+ {"RightVectorBar", 0x02953}, // RIGHTWARDS HARPOON WITH BARB UP TO BAR
+ {"ring", 0x002DA}, // RING ABOVE
+ {"risingdotseq", 0x02253}, // IMAGE OF OR APPROXIMATELY EQUAL TO
+ {"rlarr", 0x021C4}, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
+ {"rlhar", 0x021CC}, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
+ {"rlm", 0x0200F}, // RIGHT-TO-LEFT MARK
+ {"rmoust", 0x023B1}, // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION
+ {"rmoustache", 0x023B1}, // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION
+ {"rnmid", 0x02AEE}, // DOES NOT DIVIDE WITH REVERSED NEGATION SLASH
+ {"roang", 0x027ED}, // MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
+ {"roarr", 0x021FE}, // RIGHTWARDS OPEN-HEADED ARROW
+ {"robrk", 0x027E7}, // MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+ {"ropar", 0x02986}, // RIGHT WHITE PARENTHESIS
+ {"Ropf", 0x0211D}, // DOUBLE-STRUCK CAPITAL R
+ {"ropf", 0x1D563}, // MATHEMATICAL DOUBLE-STRUCK SMALL R
+ {"roplus", 0x02A2E}, // PLUS SIGN IN RIGHT HALF CIRCLE
+ {"rotimes", 0x02A35}, // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE
+ {"RoundImplies", 0x02970}, // RIGHT DOUBLE ARROW WITH ROUNDED HEAD
+ {"rpar", 0x00029}, // RIGHT PARENTHESIS
+ {"rpargt", 0x02994}, // RIGHT ARC GREATER-THAN BRACKET
+ {"rppolint", 0x02A12}, // LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE
+ {"rrarr", 0x021C9}, // RIGHTWARDS PAIRED ARROWS
+ {"Rrightarrow", 0x021DB}, // RIGHTWARDS TRIPLE ARROW
+ {"rsaquo", 0x0203A}, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ {"Rscr", 0x0211B}, // SCRIPT CAPITAL R
+ {"rscr", 0x1D4C7}, // MATHEMATICAL SCRIPT SMALL R
+ {"rsh", 0x021B1}, // UPWARDS ARROW WITH TIP RIGHTWARDS
+ {"Rsh", 0x021B1}, // UPWARDS ARROW WITH TIP RIGHTWARDS
+ {"rsqb", 0x0005D}, // RIGHT SQUARE BRACKET
+ {"rsquo", 0x02019}, // RIGHT SINGLE QUOTATION MARK
+ {"rsquor", 0x02019}, // RIGHT SINGLE QUOTATION MARK
+ {"rthree", 0x022CC}, // RIGHT SEMIDIRECT PRODUCT
+ {"rtimes", 0x022CA}, // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
+ {"rtri", 0x025B9}, // WHITE RIGHT-POINTING SMALL TRIANGLE
+ {"rtrie", 0x022B5}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+ {"rtrif", 0x025B8}, // BLACK RIGHT-POINTING SMALL TRIANGLE
+ {"rtriltri", 0x029CE}, // RIGHT TRIANGLE ABOVE LEFT TRIANGLE
+ {"RuleDelayed", 0x029F4}, // RULE-DELAYED
+ {"ruluhar", 0x02968}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP
+ {"rx", 0x0211E}, // PRESCRIPTION TAKE
+];
+
+immutable NameId[] namesS =
+[
+ {"Sacute", 0x0015A}, // LATIN CAPITAL LETTER S WITH ACUTE
+ {"sacute", 0x0015B}, // LATIN SMALL LETTER S WITH ACUTE
+ {"sbquo", 0x0201A}, // SINGLE LOW-9 QUOTATION MARK
+ {"sc", 0x0227B}, // SUCCEEDS
+ {"Sc", 0x02ABC}, // DOUBLE SUCCEEDS
+ {"scap", 0x02AB8}, // SUCCEEDS ABOVE ALMOST EQUAL TO
+ {"Scaron", 0x00160}, // LATIN CAPITAL LETTER S WITH CARON
+ {"scaron", 0x00161}, // LATIN SMALL LETTER S WITH CARON
+ {"sccue", 0x0227D}, // SUCCEEDS OR EQUAL TO
+ {"sce", 0x02AB0}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
+ {"scE", 0x02AB4}, // SUCCEEDS ABOVE EQUALS SIGN
+ {"Scedil", 0x0015E}, // LATIN CAPITAL LETTER S WITH CEDILLA
+ {"scedil", 0x0015F}, // LATIN SMALL LETTER S WITH CEDILLA
+ {"Scirc", 0x0015C}, // LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+ {"scirc", 0x0015D}, // LATIN SMALL LETTER S WITH CIRCUMFLEX
+ {"scnap", 0x02ABA}, // SUCCEEDS ABOVE NOT ALMOST EQUAL TO
+ {"scnE", 0x02AB6}, // SUCCEEDS ABOVE NOT EQUAL TO
+ {"scnsim", 0x022E9}, // SUCCEEDS BUT NOT EQUIVALENT TO
+ {"scpolint", 0x02A13}, // LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE
+ {"scsim", 0x0227F}, // SUCCEEDS OR EQUIVALENT TO
+ {"Scy", 0x00421}, // CYRILLIC CAPITAL LETTER ES
+ {"scy", 0x00441}, // CYRILLIC SMALL LETTER ES
+ {"sdot", 0x022C5}, // DOT OPERATOR
+ {"sdotb", 0x022A1}, // SQUARED DOT OPERATOR
+ {"sdote", 0x02A66}, // EQUALS SIGN WITH DOT BELOW
+ {"searhk", 0x02925}, // SOUTH EAST ARROW WITH HOOK
+ {"searr", 0x02198}, // SOUTH EAST ARROW
+ {"seArr", 0x021D8}, // SOUTH EAST DOUBLE ARROW
+ {"searrow", 0x02198}, // SOUTH EAST ARROW
+ {"sect", 0x000A7}, // SECTION SIGN
+ {"semi", 0x0003B}, // SEMICOLON
+ {"seswar", 0x02929}, // SOUTH EAST ARROW AND SOUTH WEST ARROW
+ {"setminus", 0x02216}, // SET MINUS
+ {"setmn", 0x02216}, // SET MINUS
+ {"sext", 0x02736}, // SIX POINTED BLACK STAR
+ {"sfgr", 0x003C2}, // GREEK SMALL LETTER FINAL SIGMA
+ {"Sfr", 0x1D516}, // MATHEMATICAL FRAKTUR CAPITAL S
+ {"sfr", 0x1D530}, // MATHEMATICAL FRAKTUR SMALL S
+ {"sfrown", 0x02322}, // FROWN
+ {"Sgr", 0x003A3}, // GREEK CAPITAL LETTER SIGMA
+ {"sgr", 0x003C3}, // GREEK SMALL LETTER SIGMA
+ {"sharp", 0x0266F}, // MUSIC SHARP SIGN
+ {"SHCHcy", 0x00429}, // CYRILLIC CAPITAL LETTER SHCHA
+ {"shchcy", 0x00449}, // CYRILLIC SMALL LETTER SHCHA
+ {"SHcy", 0x00428}, // CYRILLIC CAPITAL LETTER SHA
+ {"shcy", 0x00448}, // CYRILLIC SMALL LETTER SHA
+ {"ShortDownArrow", 0x02193}, // DOWNWARDS ARROW
+ {"ShortLeftArrow", 0x02190}, // LEFTWARDS ARROW
+ {"shortmid", 0x02223}, // DIVIDES
+ {"shortparallel", 0x02225}, // PARALLEL TO
+ {"ShortRightArrow", 0x02192}, // RIGHTWARDS ARROW
+ {"ShortUpArrow", 0x02191}, // UPWARDS ARROW
+ {"shy", 0x000AD}, // SOFT HYPHEN
+ {"Sigma", 0x003A3}, // GREEK CAPITAL LETTER SIGMA
+ {"sigma", 0x003C3}, // GREEK SMALL LETTER SIGMA
+ {"sigmaf", 0x003C2}, // GREEK SMALL LETTER FINAL SIGMA
+ {"sigmav", 0x003C2}, // GREEK SMALL LETTER FINAL SIGMA
+ {"sim", 0x0223C}, // TILDE OPERATOR
+ {"simdot", 0x02A6A}, // TILDE OPERATOR WITH DOT ABOVE
+ {"sime", 0x02243}, // ASYMPTOTICALLY EQUAL TO
+ {"simeq", 0x02243}, // ASYMPTOTICALLY EQUAL TO
+ {"simg", 0x02A9E}, // SIMILAR OR GREATER-THAN
+ {"simgE", 0x02AA0}, // SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN
+ {"siml", 0x02A9D}, // SIMILAR OR LESS-THAN
+ {"simlE", 0x02A9F}, // SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN
+ {"simne", 0x02246}, // APPROXIMATELY BUT NOT ACTUALLY EQUAL TO
+ {"simplus", 0x02A24}, // PLUS SIGN WITH TILDE ABOVE
+ {"simrarr", 0x02972}, // TILDE OPERATOR ABOVE RIGHTWARDS ARROW
+ {"slarr", 0x02190}, // LEFTWARDS ARROW
+ {"SmallCircle", 0x02218}, // RING OPERATOR
+ {"smallsetminus", 0x02216}, // SET MINUS
+ {"smashp", 0x02A33}, // SMASH PRODUCT
+ {"smeparsl", 0x029E4}, // EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE
+ {"smid", 0x02223}, // DIVIDES
+ {"smile", 0x02323}, // SMILE
+ {"smt", 0x02AAA}, // SMALLER THAN
+ {"smte", 0x02AAC}, // SMALLER THAN OR EQUAL TO
+// "smtes", 0x02AAC;0x0FE00}, // SMALLER THAN OR slanted EQUAL
+ {"SOFTcy", 0x0042C}, // CYRILLIC CAPITAL LETTER SOFT SIGN
+ {"softcy", 0x0044C}, // CYRILLIC SMALL LETTER SOFT SIGN
+ {"sol", 0x0002F}, // SOLIDUS
+ {"solb", 0x029C4}, // SQUARED RISING DIAGONAL SLASH
+ {"solbar", 0x0233F}, // APL FUNCTIONAL SYMBOL SLASH BAR
+ {"Sopf", 0x1D54A}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL S
+ {"sopf", 0x1D564}, // MATHEMATICAL DOUBLE-STRUCK SMALL S
+ {"spades", 0x02660}, // BLACK SPADE SUIT
+ {"spadesuit", 0x02660}, // BLACK SPADE SUIT
+ {"spar", 0x02225}, // PARALLEL TO
+ {"sqcap", 0x02293}, // SQUARE CAP
+// "sqcaps", 0x02293;0x0FE00}, // SQUARE CAP with serifs
+ {"sqcup", 0x02294}, // SQUARE CUP
+// "sqcups", 0x02294;0x0FE00}, // SQUARE CUP with serifs
+ {"Sqrt", 0x0221A}, // SQUARE ROOT
+ {"sqsub", 0x0228F}, // SQUARE IMAGE OF
+ {"sqsube", 0x02291}, // SQUARE IMAGE OF OR EQUAL TO
+ {"sqsubset", 0x0228F}, // SQUARE IMAGE OF
+ {"sqsubseteq", 0x02291}, // SQUARE IMAGE OF OR EQUAL TO
+ {"sqsup", 0x02290}, // SQUARE ORIGINAL OF
+ {"sqsupe", 0x02292}, // SQUARE ORIGINAL OF OR EQUAL TO
+ {"sqsupset", 0x02290}, // SQUARE ORIGINAL OF
+ {"sqsupseteq", 0x02292}, // SQUARE ORIGINAL OF OR EQUAL TO
+ {"squ", 0x025A1}, // WHITE SQUARE
+ {"square", 0x025A1}, // WHITE SQUARE
+ {"Square", 0x025A1}, // WHITE SQUARE
+ {"SquareIntersection", 0x02293}, // SQUARE CAP
+ {"SquareSubset", 0x0228F}, // SQUARE IMAGE OF
+ {"SquareSubsetEqual", 0x02291}, // SQUARE IMAGE OF OR EQUAL TO
+ {"SquareSuperset", 0x02290}, // SQUARE ORIGINAL OF
+ {"SquareSupersetEqual", 0x02292}, // SQUARE ORIGINAL OF OR EQUAL TO
+ {"SquareUnion", 0x02294}, // SQUARE CUP
+ {"squarf", 0x025AA}, // BLACK SMALL SQUARE
+ {"squf", 0x025AA}, // BLACK SMALL SQUARE
+ {"srarr", 0x02192}, // RIGHTWARDS ARROW
+ {"Sscr", 0x1D4AE}, // MATHEMATICAL SCRIPT CAPITAL S
+ {"sscr", 0x1D4C8}, // MATHEMATICAL SCRIPT SMALL S
+ {"ssetmn", 0x02216}, // SET MINUS
+ {"ssmile", 0x02323}, // SMILE
+ {"sstarf", 0x022C6}, // STAR OPERATOR
+ {"Star", 0x022C6}, // STAR OPERATOR
+ {"star", 0x02606}, // WHITE STAR
+ {"starf", 0x02605}, // BLACK STAR
+ {"straightepsilon", 0x003F5}, // GREEK LUNATE EPSILON SYMBOL
+ {"straightphi", 0x003D5}, // GREEK PHI SYMBOL
+ {"strns", 0x000AF}, // MACRON
+ {"sub", 0x02282}, // SUBSET OF
+ {"Sub", 0x022D0}, // DOUBLE SUBSET
+ {"subdot", 0x02ABD}, // SUBSET WITH DOT
+ {"sube", 0x02286}, // SUBSET OF OR EQUAL TO
+ {"subE", 0x02AC5}, // SUBSET OF ABOVE EQUALS SIGN
+ {"subedot", 0x02AC3}, // SUBSET OF OR EQUAL TO WITH DOT ABOVE
+ {"submult", 0x02AC1}, // SUBSET WITH MULTIPLICATION SIGN BELOW
+ {"subne", 0x0228A}, // SUBSET OF WITH NOT EQUAL TO
+ {"subnE", 0x02ACB}, // SUBSET OF ABOVE NOT EQUAL TO
+ {"subplus", 0x02ABF}, // SUBSET WITH PLUS SIGN BELOW
+ {"subrarr", 0x02979}, // SUBSET ABOVE RIGHTWARDS ARROW
+ {"subset", 0x02282}, // SUBSET OF
+ {"Subset", 0x022D0}, // DOUBLE SUBSET
+ {"subseteq", 0x02286}, // SUBSET OF OR EQUAL TO
+ {"subseteqq", 0x02AC5}, // SUBSET OF ABOVE EQUALS SIGN
+ {"SubsetEqual", 0x02286}, // SUBSET OF OR EQUAL TO
+ {"subsetneq", 0x0228A}, // SUBSET OF WITH NOT EQUAL TO
+ {"subsetneqq", 0x02ACB}, // SUBSET OF ABOVE NOT EQUAL TO
+ {"subsim", 0x02AC7}, // SUBSET OF ABOVE TILDE OPERATOR
+ {"subsub", 0x02AD5}, // SUBSET ABOVE SUBSET
+ {"subsup", 0x02AD3}, // SUBSET ABOVE SUPERSET
+ {"succ", 0x0227B}, // SUCCEEDS
+ {"succapprox", 0x02AB8}, // SUCCEEDS ABOVE ALMOST EQUAL TO
+ {"succcurlyeq", 0x0227D}, // SUCCEEDS OR EQUAL TO
+ {"Succeeds", 0x0227B}, // SUCCEEDS
+ {"SucceedsEqual", 0x02AB0}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
+ {"SucceedsSlantEqual", 0x0227D}, // SUCCEEDS OR EQUAL TO
+ {"SucceedsTilde", 0x0227F}, // SUCCEEDS OR EQUIVALENT TO
+ {"succeq", 0x02AB0}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
+ {"succnapprox", 0x02ABA}, // SUCCEEDS ABOVE NOT ALMOST EQUAL TO
+ {"succneqq", 0x02AB6}, // SUCCEEDS ABOVE NOT EQUAL TO
+ {"succnsim", 0x022E9}, // SUCCEEDS BUT NOT EQUIVALENT TO
+ {"succsim", 0x0227F}, // SUCCEEDS OR EQUIVALENT TO
+ {"SuchThat", 0x0220B}, // CONTAINS AS MEMBER
+ {"sum", 0x02211}, // N-ARY SUMMATION
+ {"Sum", 0x02211}, // N-ARY SUMMATION
+ {"sung", 0x0266A}, // EIGHTH NOTE
+ {"sup", 0x02283}, // SUPERSET OF
+ {"Sup", 0x022D1}, // DOUBLE SUPERSET
+ {"sup1", 0x000B9}, // SUPERSCRIPT ONE
+ {"sup2", 0x000B2}, // SUPERSCRIPT TWO
+ {"sup3", 0x000B3}, // SUPERSCRIPT THREE
+ {"supdot", 0x02ABE}, // SUPERSET WITH DOT
+ {"supdsub", 0x02AD8}, // SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET
+ {"supe", 0x02287}, // SUPERSET OF OR EQUAL TO
+ {"supE", 0x02AC6}, // SUPERSET OF ABOVE EQUALS SIGN
+ {"supedot", 0x02AC4}, // SUPERSET OF OR EQUAL TO WITH DOT ABOVE
+ {"Superset", 0x02283}, // SUPERSET OF
+ {"SupersetEqual", 0x02287}, // SUPERSET OF OR EQUAL TO
+ {"suphsol", 0x027C9}, // SUPERSET PRECEDING SOLIDUS
+ {"suphsub", 0x02AD7}, // SUPERSET BESIDE SUBSET
+ {"suplarr", 0x0297B}, // SUPERSET ABOVE LEFTWARDS ARROW
+ {"supmult", 0x02AC2}, // SUPERSET WITH MULTIPLICATION SIGN BELOW
+ {"supne", 0x0228B}, // SUPERSET OF WITH NOT EQUAL TO
+ {"supnE", 0x02ACC}, // SUPERSET OF ABOVE NOT EQUAL TO
+ {"supplus", 0x02AC0}, // SUPERSET WITH PLUS SIGN BELOW
+ {"supset", 0x02283}, // SUPERSET OF
+ {"Supset", 0x022D1}, // DOUBLE SUPERSET
+ {"supseteq", 0x02287}, // SUPERSET OF OR EQUAL TO
+ {"supseteqq", 0x02AC6}, // SUPERSET OF ABOVE EQUALS SIGN
+ {"supsetneq", 0x0228B}, // SUPERSET OF WITH NOT EQUAL TO
+ {"supsetneqq", 0x02ACC}, // SUPERSET OF ABOVE NOT EQUAL TO
+ {"supsim", 0x02AC8}, // SUPERSET OF ABOVE TILDE OPERATOR
+ {"supsub", 0x02AD4}, // SUPERSET ABOVE SUBSET
+ {"supsup", 0x02AD6}, // SUPERSET ABOVE SUPERSET
+ {"swarhk", 0x02926}, // SOUTH WEST ARROW WITH HOOK
+ {"swarr", 0x02199}, // SOUTH WEST ARROW
+ {"swArr", 0x021D9}, // SOUTH WEST DOUBLE ARROW
+ {"swarrow", 0x02199}, // SOUTH WEST ARROW
+ {"swnwar", 0x0292A}, // SOUTH WEST ARROW AND NORTH WEST ARROW
+ {"szlig", 0x000DF}, // LATIN SMALL LETTER SHARP S
+];
+
+immutable NameId[] namesT =
+[
+ {"Tab", 0x00009}, // CHARACTER TABULATION
+ {"target", 0x02316}, // POSITION INDICATOR
+ {"Tau", 0x003A4}, // GREEK CAPITAL LETTER TAU
+ {"tau", 0x003C4}, // GREEK SMALL LETTER TAU
+ {"tbrk", 0x023B4}, // TOP SQUARE BRACKET
+ {"Tcaron", 0x00164}, // LATIN CAPITAL LETTER T WITH CARON
+ {"tcaron", 0x00165}, // LATIN SMALL LETTER T WITH CARON
+ {"Tcedil", 0x00162}, // LATIN CAPITAL LETTER T WITH CEDILLA
+ {"tcedil", 0x00163}, // LATIN SMALL LETTER T WITH CEDILLA
+ {"Tcy", 0x00422}, // CYRILLIC CAPITAL LETTER TE
+ {"tcy", 0x00442}, // CYRILLIC SMALL LETTER TE
+ {"tdot", 0x020DB}, // COMBINING THREE DOTS ABOVE
+ {"telrec", 0x02315}, // TELEPHONE RECORDER
+ {"Tfr", 0x1D517}, // MATHEMATICAL FRAKTUR CAPITAL T
+ {"tfr", 0x1D531}, // MATHEMATICAL FRAKTUR SMALL T
+ {"Tgr", 0x003A4}, // GREEK CAPITAL LETTER TAU
+ {"tgr", 0x003C4}, // GREEK SMALL LETTER TAU
+ {"there4", 0x02234}, // THEREFORE
+ {"therefore", 0x02234}, // THEREFORE
+ {"Therefore", 0x02234}, // THEREFORE
+ {"Theta", 0x00398}, // GREEK CAPITAL LETTER THETA
+ {"theta", 0x003B8}, // GREEK SMALL LETTER THETA
+ {"thetasym", 0x003D1}, // GREEK THETA SYMBOL
+ {"thetav", 0x003D1}, // GREEK THETA SYMBOL
+ {"THgr", 0x00398}, // GREEK CAPITAL LETTER THETA
+ {"thgr", 0x003B8}, // GREEK SMALL LETTER THETA
+ {"thickapprox", 0x02248}, // ALMOST EQUAL TO
+ {"thicksim", 0x0223C}, // TILDE OPERATOR
+// "ThickSpace", 0x0205F;0x0200A}, // space of width 5/18 em
+ {"thinsp", 0x02009}, // THIN SPACE
+ {"ThinSpace", 0x02009}, // THIN SPACE
+ {"thkap", 0x02248}, // ALMOST EQUAL TO
+ {"thksim", 0x0223C}, // TILDE OPERATOR
+ {"THORN", 0x000DE}, // LATIN CAPITAL LETTER THORN
+ {"thorn", 0x000FE}, // LATIN SMALL LETTER THORN
+ {"tilde", 0x002DC}, // SMALL TILDE
+ {"Tilde", 0x0223C}, // TILDE OPERATOR
+ {"TildeEqual", 0x02243}, // ASYMPTOTICALLY EQUAL TO
+ {"TildeFullEqual", 0x02245}, // APPROXIMATELY EQUAL TO
+ {"TildeTilde", 0x02248}, // ALMOST EQUAL TO
+ {"times", 0x000D7}, // MULTIPLICATION SIGN
+ {"timesb", 0x022A0}, // SQUARED TIMES
+ {"timesbar", 0x02A31}, // MULTIPLICATION SIGN WITH UNDERBAR
+ {"timesd", 0x02A30}, // MULTIPLICATION SIGN WITH DOT ABOVE
+ {"tint", 0x0222D}, // TRIPLE INTEGRAL
+ {"toea", 0x02928}, // NORTH EAST ARROW AND SOUTH EAST ARROW
+ {"top", 0x022A4}, // DOWN TACK
+ {"topbot", 0x02336}, // APL FUNCTIONAL SYMBOL I-BEAM
+ {"topcir", 0x02AF1}, // DOWN TACK WITH CIRCLE BELOW
+ {"Topf", 0x1D54B}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL T
+ {"topf", 0x1D565}, // MATHEMATICAL DOUBLE-STRUCK SMALL T
+ {"topfork", 0x02ADA}, // PITCHFORK WITH TEE TOP
+ {"tosa", 0x02929}, // SOUTH EAST ARROW AND SOUTH WEST ARROW
+ {"tprime", 0x02034}, // TRIPLE PRIME
+ {"trade", 0x02122}, // TRADE MARK SIGN
+ {"TRADE", 0x02122}, // TRADE MARK SIGN
+ {"triangle", 0x025B5}, // WHITE UP-POINTING SMALL TRIANGLE
+ {"triangledown", 0x025BF}, // WHITE DOWN-POINTING SMALL TRIANGLE
+ {"triangleleft", 0x025C3}, // WHITE LEFT-POINTING SMALL TRIANGLE
+ {"trianglelefteq", 0x022B4}, // NORMAL SUBGROUP OF OR EQUAL TO
+ {"triangleq", 0x0225C}, // DELTA EQUAL TO
+ {"triangleright", 0x025B9}, // WHITE RIGHT-POINTING SMALL TRIANGLE
+ {"trianglerighteq", 0x022B5}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+ {"tridot", 0x025EC}, // WHITE UP-POINTING TRIANGLE WITH DOT
+ {"trie", 0x0225C}, // DELTA EQUAL TO
+ {"triminus", 0x02A3A}, // MINUS SIGN IN TRIANGLE
+ {"TripleDot", 0x020DB}, // COMBINING THREE DOTS ABOVE
+ {"triplus", 0x02A39}, // PLUS SIGN IN TRIANGLE
+ {"trisb", 0x029CD}, // TRIANGLE WITH SERIFS AT BOTTOM
+ {"tritime", 0x02A3B}, // MULTIPLICATION SIGN IN TRIANGLE
+ {"trpezium", 0x023E2}, // WHITE TRAPEZIUM
+ {"Tscr", 0x1D4AF}, // MATHEMATICAL SCRIPT CAPITAL T
+ {"tscr", 0x1D4C9}, // MATHEMATICAL SCRIPT SMALL T
+ {"TScy", 0x00426}, // CYRILLIC CAPITAL LETTER TSE
+ {"tscy", 0x00446}, // CYRILLIC SMALL LETTER TSE
+ {"TSHcy", 0x0040B}, // CYRILLIC CAPITAL LETTER TSHE
+ {"tshcy", 0x0045B}, // CYRILLIC SMALL LETTER TSHE
+ {"Tstrok", 0x00166}, // LATIN CAPITAL LETTER T WITH STROKE
+ {"tstrok", 0x00167}, // LATIN SMALL LETTER T WITH STROKE
+ {"twixt", 0x0226C}, // BETWEEN
+ {"twoheadleftarrow", 0x0219E}, // LEFTWARDS TWO HEADED ARROW
+ {"twoheadrightarrow", 0x021A0}, // RIGHTWARDS TWO HEADED ARROW
+];
+
+immutable NameId[] namesU =
+[
+ {"Uacgr", 0x0038E}, // GREEK CAPITAL LETTER UPSILON WITH TONOS
+ {"uacgr", 0x003CD}, // GREEK SMALL LETTER UPSILON WITH TONOS
+ {"Uacute", 0x000DA}, // LATIN CAPITAL LETTER U WITH ACUTE
+ {"uacute", 0x000FA}, // LATIN SMALL LETTER U WITH ACUTE
+ {"uarr", 0x02191}, // UPWARDS ARROW
+ {"Uarr", 0x0219F}, // UPWARDS TWO HEADED ARROW
+ {"uArr", 0x021D1}, // UPWARDS DOUBLE ARROW
+ {"Uarrocir", 0x02949}, // UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE
+ {"Ubrcy", 0x0040E}, // CYRILLIC CAPITAL LETTER SHORT U
+ {"ubrcy", 0x0045E}, // CYRILLIC SMALL LETTER SHORT U
+ {"Ubreve", 0x0016C}, // LATIN CAPITAL LETTER U WITH BREVE
+ {"ubreve", 0x0016D}, // LATIN SMALL LETTER U WITH BREVE
+ {"Ucirc", 0x000DB}, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+ {"ucirc", 0x000FB}, // LATIN SMALL LETTER U WITH CIRCUMFLEX
+ {"Ucy", 0x00423}, // CYRILLIC CAPITAL LETTER U
+ {"ucy", 0x00443}, // CYRILLIC SMALL LETTER U
+ {"udarr", 0x021C5}, // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
+ {"Udblac", 0x00170}, // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+ {"udblac", 0x00171}, // LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ {"udhar", 0x0296E}, // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
+ {"udiagr", 0x003B0}, // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+ {"Udigr", 0x003AB}, // GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+ {"udigr", 0x003CB}, // GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+ {"ufisht", 0x0297E}, // UP FISH TAIL
+ {"Ufr", 0x1D518}, // MATHEMATICAL FRAKTUR CAPITAL U
+ {"ufr", 0x1D532}, // MATHEMATICAL FRAKTUR SMALL U
+ {"Ugr", 0x003A5}, // GREEK CAPITAL LETTER UPSILON
+ {"ugr", 0x003C5}, // GREEK SMALL LETTER UPSILON
+ {"Ugrave", 0x000D9}, // LATIN CAPITAL LETTER U WITH GRAVE
+ {"ugrave", 0x000F9}, // LATIN SMALL LETTER U WITH GRAVE
+ {"uHar", 0x02963}, // UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
+ {"uharl", 0x021BF}, // UPWARDS HARPOON WITH BARB LEFTWARDS
+ {"uharr", 0x021BE}, // UPWARDS HARPOON WITH BARB RIGHTWARDS
+ {"uhblk", 0x02580}, // UPPER HALF BLOCK
+ {"ulcorn", 0x0231C}, // TOP LEFT CORNER
+ {"ulcorner", 0x0231C}, // TOP LEFT CORNER
+ {"ulcrop", 0x0230F}, // TOP LEFT CROP
+ {"ultri", 0x025F8}, // UPPER LEFT TRIANGLE
+ {"Umacr", 0x0016A}, // LATIN CAPITAL LETTER U WITH MACRON
+ {"umacr", 0x0016B}, // LATIN SMALL LETTER U WITH MACRON
+ {"uml", 0x000A8}, // DIAERESIS
+ {"UnderBar", 0x0005F}, // LOW LINE
+ {"UnderBrace", 0x023DF}, // BOTTOM CURLY BRACKET
+ {"UnderBracket", 0x023B5}, // BOTTOM SQUARE BRACKET
+ {"UnderParenthesis", 0x023DD}, // BOTTOM PARENTHESIS
+ {"Union", 0x022C3}, // N-ARY UNION
+ {"UnionPlus", 0x0228E}, // MULTISET UNION
+ {"Uogon", 0x00172}, // LATIN CAPITAL LETTER U WITH OGONEK
+ {"uogon", 0x00173}, // LATIN SMALL LETTER U WITH OGONEK
+ {"Uopf", 0x1D54C}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL U
+ {"uopf", 0x1D566}, // MATHEMATICAL DOUBLE-STRUCK SMALL U
+ {"uparrow", 0x02191}, // UPWARDS ARROW
+ {"UpArrow", 0x02191}, // UPWARDS ARROW
+ {"Uparrow", 0x021D1}, // UPWARDS DOUBLE ARROW
+ {"UpArrowBar", 0x02912}, // UPWARDS ARROW TO BAR
+ {"UpArrowDownArrow", 0x021C5}, // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
+ {"updownarrow", 0x02195}, // UP DOWN ARROW
+ {"UpDownArrow", 0x02195}, // UP DOWN ARROW
+ {"Updownarrow", 0x021D5}, // UP DOWN DOUBLE ARROW
+ {"UpEquilibrium", 0x0296E}, // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
+ {"upharpoonleft", 0x021BF}, // UPWARDS HARPOON WITH BARB LEFTWARDS
+ {"upharpoonright", 0x021BE}, // UPWARDS HARPOON WITH BARB RIGHTWARDS
+ {"uplus", 0x0228E}, // MULTISET UNION
+ {"UpperLeftArrow", 0x02196}, // NORTH WEST ARROW
+ {"UpperRightArrow", 0x02197}, // NORTH EAST ARROW
+ {"upsi", 0x003C5}, // GREEK SMALL LETTER UPSILON
+ {"Upsi", 0x003D2}, // GREEK UPSILON WITH HOOK SYMBOL
+ {"upsih", 0x003D2}, // GREEK UPSILON WITH HOOK SYMBOL
+ {"Upsilon", 0x003A5}, // GREEK CAPITAL LETTER UPSILON
+ {"upsilon", 0x003C5}, // GREEK SMALL LETTER UPSILON
+ {"UpTee", 0x022A5}, // UP TACK
+ {"UpTeeArrow", 0x021A5}, // UPWARDS ARROW FROM BAR
+ {"upuparrows", 0x021C8}, // UPWARDS PAIRED ARROWS
+ {"urcorn", 0x0231D}, // TOP RIGHT CORNER
+ {"urcorner", 0x0231D}, // TOP RIGHT CORNER
+ {"urcrop", 0x0230E}, // TOP RIGHT CROP
+ {"Uring", 0x0016E}, // LATIN CAPITAL LETTER U WITH RING ABOVE
+ {"uring", 0x0016F}, // LATIN SMALL LETTER U WITH RING ABOVE
+ {"urtri", 0x025F9}, // UPPER RIGHT TRIANGLE
+ {"Uscr", 0x1D4B0}, // MATHEMATICAL SCRIPT CAPITAL U
+ {"uscr", 0x1D4CA}, // MATHEMATICAL SCRIPT SMALL U
+ {"utdot", 0x022F0}, // UP RIGHT DIAGONAL ELLIPSIS
+ {"Utilde", 0x00168}, // LATIN CAPITAL LETTER U WITH TILDE
+ {"utilde", 0x00169}, // LATIN SMALL LETTER U WITH TILDE
+ {"utri", 0x025B5}, // WHITE UP-POINTING SMALL TRIANGLE
+ {"utrif", 0x025B4}, // BLACK UP-POINTING SMALL TRIANGLE
+ {"uuarr", 0x021C8}, // UPWARDS PAIRED ARROWS
+ {"Uuml", 0x000DC}, // LATIN CAPITAL LETTER U WITH DIAERESIS
+ {"uuml", 0x000FC}, // LATIN SMALL LETTER U WITH DIAERESIS
+ {"uwangle", 0x029A7}, // OBLIQUE ANGLE OPENING DOWN
+];
+
+immutable NameId[] namesV =
+[
+ {"vangrt", 0x0299C}, // RIGHT ANGLE VARIANT WITH SQUARE
+ {"varepsilon", 0x003F5}, // GREEK LUNATE EPSILON SYMBOL
+ {"varkappa", 0x003F0}, // GREEK KAPPA SYMBOL
+ {"varnothing", 0x02205}, // EMPTY SET
+ {"varphi", 0x003D5}, // GREEK PHI SYMBOL
+ {"varpi", 0x003D6}, // GREEK PI SYMBOL
+ {"varpropto", 0x0221D}, // PROPORTIONAL TO
+ {"varr", 0x02195}, // UP DOWN ARROW
+ {"vArr", 0x021D5}, // UP DOWN DOUBLE ARROW
+ {"varrho", 0x003F1}, // GREEK RHO SYMBOL
+ {"varsigma", 0x003C2}, // GREEK SMALL LETTER FINAL SIGMA
+// "varsubsetneq", 0x0228A;0x0FE00}, // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
+// "varsubsetneqq", 0x02ACB;0x0FE00}, // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
+// "varsupsetneq", 0x0228B;0x0FE00}, // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
+// "varsupsetneqq", 0x02ACC;0x0FE00}, // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
+ {"vartheta", 0x003D1}, // GREEK THETA SYMBOL
+ {"vartriangleleft", 0x022B2}, // NORMAL SUBGROUP OF
+ {"vartriangleright", 0x022B3}, // CONTAINS AS NORMAL SUBGROUP
+ {"vBar", 0x02AE8}, // SHORT UP TACK WITH UNDERBAR
+ {"Vbar", 0x02AEB}, // DOUBLE UP TACK
+ {"vBarv", 0x02AE9}, // SHORT UP TACK ABOVE SHORT DOWN TACK
+ {"Vcy", 0x00412}, // CYRILLIC CAPITAL LETTER VE
+ {"vcy", 0x00432}, // CYRILLIC SMALL LETTER VE
+ {"vdash", 0x022A2}, // RIGHT TACK
+ {"vDash", 0x022A8}, // TRUE
+ {"Vdash", 0x022A9}, // FORCES
+ {"VDash", 0x022AB}, // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+ {"Vdashl", 0x02AE6}, // LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL
+ {"vee", 0x02228}, // LOGICAL OR
+ {"Vee", 0x022C1}, // N-ARY LOGICAL OR
+ {"veebar", 0x022BB}, // XOR
+ {"veeeq", 0x0225A}, // EQUIANGULAR TO
+ {"vellip", 0x022EE}, // VERTICAL ELLIPSIS
+ {"verbar", 0x0007C}, // VERTICAL LINE
+ {"Verbar", 0x02017}, // DOUBLE VERTICAL LINE
+ {"vert", 0x0007C}, // VERTICAL LINE
+ {"Vert", 0x02017}, // DOUBLE VERTICAL LINE
+ {"VerticalBar", 0x02223}, // DIVIDES
+ {"VerticalLine", 0x0007C}, // VERTICAL LINE
+ {"VerticalSeparator", 0x02758}, // LIGHT VERTICAL BAR
+ {"VerticalTilde", 0x02240}, // WREATH PRODUCT
+ {"VeryThinSpace", 0x0200A}, // HAIR SPACE
+ {"Vfr", 0x1D519}, // MATHEMATICAL FRAKTUR CAPITAL V
+ {"vfr", 0x1D533}, // MATHEMATICAL FRAKTUR SMALL V
+ {"vltri", 0x022B2}, // NORMAL SUBGROUP OF
+// "vnsub", 0x02282;0x020D2}, // SUBSET OF with vertical line
+// "vnsup", 0x02283;0x020D2}, // SUPERSET OF with vertical line
+ {"Vopf", 0x1D54D}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL V
+ {"vopf", 0x1D567}, // MATHEMATICAL DOUBLE-STRUCK SMALL V
+ {"vprop", 0x0221D}, // PROPORTIONAL TO
+ {"vrtri", 0x022B3}, // CONTAINS AS NORMAL SUBGROUP
+ {"Vscr", 0x1D4B1}, // MATHEMATICAL SCRIPT CAPITAL V
+ {"vscr", 0x1D4CB}, // MATHEMATICAL SCRIPT SMALL V
+// "vsubne", 0x0228A;0x0FE00}, // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
+// "vsubnE", 0x02ACB;0x0FE00}, // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
+// "vsupne", 0x0228B;0x0FE00}, // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
+// "vsupnE", 0x02ACC;0x0FE00}, // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
+ {"Vvdash", 0x022AA}, // TRIPLE VERTICAL BAR RIGHT TURNSTILE
+ {"vzigzag", 0x0299A}, // VERTICAL ZIGZAG LINE
+];
+
+immutable NameId[] namesW =
+[
+ {"Wcirc", 0x00174}, // LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+ {"wcirc", 0x00175}, // LATIN SMALL LETTER W WITH CIRCUMFLEX
+ {"wedbar", 0x02A5F}, // LOGICAL AND WITH UNDERBAR
+ {"wedge", 0x02227}, // LOGICAL AND
+ {"Wedge", 0x022C0}, // N-ARY LOGICAL AND
+ {"wedgeq", 0x02259}, // ESTIMATES
+ {"weierp", 0x02118}, // SCRIPT CAPITAL P
+ {"Wfr", 0x1D51A}, // MATHEMATICAL FRAKTUR CAPITAL W
+ {"wfr", 0x1D534}, // MATHEMATICAL FRAKTUR SMALL W
+ {"Wopf", 0x1D54E}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL W
+ {"wopf", 0x1D568}, // MATHEMATICAL DOUBLE-STRUCK SMALL W
+ {"wp", 0x02118}, // SCRIPT CAPITAL P
+ {"wr", 0x02240}, // WREATH PRODUCT
+ {"wreath", 0x02240}, // WREATH PRODUCT
+ {"Wscr", 0x1D4B2}, // MATHEMATICAL SCRIPT CAPITAL W
+ {"wscr", 0x1D4CC}, // MATHEMATICAL SCRIPT SMALL W
+];
+
+immutable NameId[] namesX =
+[
+ {"xcap", 0x022C2}, // N-ARY INTERSECTION
+ {"xcirc", 0x025EF}, // LARGE CIRCLE
+ {"xcup", 0x022C3}, // N-ARY UNION
+ {"xdtri", 0x025BD}, // WHITE DOWN-POINTING TRIANGLE
+ {"Xfr", 0x1D51B}, // MATHEMATICAL FRAKTUR CAPITAL X
+ {"xfr", 0x1D535}, // MATHEMATICAL FRAKTUR SMALL X
+ {"Xgr", 0x0039E}, // GREEK CAPITAL LETTER XI
+ {"xgr", 0x003BE}, // GREEK SMALL LETTER XI
+ {"xharr", 0x027F7}, // LONG LEFT RIGHT ARROW
+ {"xhArr", 0x027FA}, // LONG LEFT RIGHT DOUBLE ARROW
+ {"Xi", 0x0039E}, // GREEK CAPITAL LETTER XI
+ {"xi", 0x003BE}, // GREEK SMALL LETTER XI
+ {"xlarr", 0x027F5}, // LONG LEFTWARDS ARROW
+ {"xlArr", 0x027F8}, // LONG LEFTWARDS DOUBLE ARROW
+ {"xmap", 0x027FC}, // LONG RIGHTWARDS ARROW FROM BAR
+ {"xnis", 0x022FB}, // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ {"xodot", 0x02A00}, // N-ARY CIRCLED DOT OPERATOR
+ {"Xopf", 0x1D54F}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL X
+ {"xopf", 0x1D569}, // MATHEMATICAL DOUBLE-STRUCK SMALL X
+ {"xoplus", 0x02A01}, // N-ARY CIRCLED PLUS OPERATOR
+ {"xotime", 0x02A02}, // N-ARY CIRCLED TIMES OPERATOR
+ {"xrarr", 0x027F6}, // LONG RIGHTWARDS ARROW
+ {"xrArr", 0x027F9}, // LONG RIGHTWARDS DOUBLE ARROW
+ {"Xscr", 0x1D4B3}, // MATHEMATICAL SCRIPT CAPITAL X
+ {"xscr", 0x1D4CD}, // MATHEMATICAL SCRIPT SMALL X
+ {"xsqcup", 0x02A06}, // N-ARY SQUARE UNION OPERATOR
+ {"xuplus", 0x02A04}, // N-ARY UNION OPERATOR WITH PLUS
+ {"xutri", 0x025B3}, // WHITE UP-POINTING TRIANGLE
+ {"xvee", 0x022C1}, // N-ARY LOGICAL OR
+ {"xwedge", 0x022C0}, // N-ARY LOGICAL AND
+];
+
+immutable NameId[] namesY =
+[
+ {"Yacute", 0x000DD}, // LATIN CAPITAL LETTER Y WITH ACUTE
+ {"yacute", 0x000FD}, // LATIN SMALL LETTER Y WITH ACUTE
+ {"YAcy", 0x0042F}, // CYRILLIC CAPITAL LETTER YA
+ {"yacy", 0x0044F}, // CYRILLIC SMALL LETTER YA
+ {"Ycirc", 0x00176}, // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+ {"ycirc", 0x00177}, // LATIN SMALL LETTER Y WITH CIRCUMFLEX
+ {"Ycy", 0x0042B}, // CYRILLIC CAPITAL LETTER YERU
+ {"ycy", 0x0044B}, // CYRILLIC SMALL LETTER YERU
+ {"yen", 0x000A5}, // YEN SIGN
+ {"Yfr", 0x1D51C}, // MATHEMATICAL FRAKTUR CAPITAL Y
+ {"yfr", 0x1D536}, // MATHEMATICAL FRAKTUR SMALL Y
+ {"YIcy", 0x00407}, // CYRILLIC CAPITAL LETTER YI
+ {"yicy", 0x00457}, // CYRILLIC SMALL LETTER YI
+ {"Yopf", 0x1D550}, // MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+ {"yopf", 0x1D56A}, // MATHEMATICAL DOUBLE-STRUCK SMALL Y
+ {"Yscr", 0x1D4B4}, // MATHEMATICAL SCRIPT CAPITAL Y
+ {"yscr", 0x1D4CE}, // MATHEMATICAL SCRIPT SMALL Y
+ {"YUcy", 0x0042E}, // CYRILLIC CAPITAL LETTER YU
+ {"yucy", 0x0044E}, // CYRILLIC SMALL LETTER YU
+ {"yuml", 0x000FF}, // LATIN SMALL LETTER Y WITH DIAERESIS
+ {"Yuml", 0x00178}, // LATIN CAPITAL LETTER Y WITH DIAERESIS
+];
+
+immutable NameId[] namesZ =
+[
+ {"Zacute", 0x00179}, // LATIN CAPITAL LETTER Z WITH ACUTE
+ {"zacute", 0x0017A}, // LATIN SMALL LETTER Z WITH ACUTE
+ {"Zcaron", 0x0017D}, // LATIN CAPITAL LETTER Z WITH CARON
+ {"zcaron", 0x0017E}, // LATIN SMALL LETTER Z WITH CARON
+ {"Zcy", 0x00417}, // CYRILLIC CAPITAL LETTER ZE
+ {"zcy", 0x00437}, // CYRILLIC SMALL LETTER ZE
+ {"Zdot", 0x0017B}, // LATIN CAPITAL LETTER Z WITH DOT ABOVE
+ {"zdot", 0x0017C}, // LATIN SMALL LETTER Z WITH DOT ABOVE
+ {"zeetrf", 0x02128}, // BLACK-LETTER CAPITAL Z
+ {"ZeroWidthSpace", 0x0200B}, // ZERO WIDTH SPACE
+ {"Zeta", 0x00396}, // GREEK CAPITAL LETTER ZETA
+ {"zeta", 0x003B6}, // GREEK SMALL LETTER ZETA
+ {"Zfr", 0x02128}, // BLACK-LETTER CAPITAL Z
+ {"zfr", 0x1D537}, // MATHEMATICAL FRAKTUR SMALL Z
+ {"Zgr", 0x00396}, // GREEK CAPITAL LETTER ZETA
+ {"zgr", 0x003B6}, // GREEK SMALL LETTER ZETA
+ {"ZHcy", 0x00416}, // CYRILLIC CAPITAL LETTER ZHE
+ {"zhcy", 0x00436}, // CYRILLIC SMALL LETTER ZHE
+ {"zigrarr", 0x021DD}, // RIGHTWARDS SQUIGGLE ARROW
+ {"Zopf", 0x02124}, // DOUBLE-STRUCK CAPITAL Z
+ {"zopf", 0x1D56B}, // MATHEMATICAL DOUBLE-STRUCK SMALL Z
+ {"Zscr", 0x1D4B5}, // MATHEMATICAL SCRIPT CAPITAL Z
+ {"zscr", 0x1D4CF}, // MATHEMATICAL SCRIPT SMALL Z
+ {"zwj", 0x0200D}, // ZERO WIDTH JOINER
+ {"zwnj", 0x0200C}, // ZERO WIDTH NON-JOINER
+];
diff --git a/gcc/d/dmd/enum.h b/gcc/d/dmd/enum.h
index ae5ea214a73..76c1235715b 100644
--- a/gcc/d/dmd/enum.h
+++ b/gcc/d/dmd/enum.h
@@ -10,15 +10,12 @@
#pragma once
-#include "root/root.h"
#include "dsymbol.h"
#include "declaration.h"
-#include "tokens.h"
class Identifier;
class Type;
class Expression;
-class VarDeclaration;
class EnumDeclaration : public ScopeDsymbol
{
@@ -33,7 +30,7 @@ public:
*/
Type *type; // the TypeEnum
Type *memtype; // type of the members
- Prot protection;
+ Visibility visibility;
Expression *maxval;
Expression *minval;
@@ -43,20 +40,18 @@ public:
bool added;
int inuse;
- EnumDeclaration(Loc loc, Identifier *id, Type *memtype);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ EnumDeclaration *syntaxCopy(Dsymbol *s);
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope *sc);
bool oneMember(Dsymbol **ps, Identifier *ident);
Type *getType();
const char *kind() const;
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
- bool isDeprecated(); // is Dsymbol deprecated?
- Prot prot();
- Expression *getMaxMinValue(Loc loc, Identifier *id);
+ bool isDeprecated() const; // is Dsymbol deprecated?
+ Visibility visible();
bool isSpecial() const;
- Expression *getDefaultValue(Loc loc);
- Type *getMemtype(Loc loc);
+ Expression *getDefaultValue(const Loc &loc);
+ Type *getMemtype(const Loc &loc);
EnumDeclaration *isEnumDeclaration() { return this; }
@@ -83,12 +78,8 @@ public:
EnumDeclaration *ed;
- EnumMember(Loc loc, Identifier *id, Expression *value, Type *origType);
- EnumMember(Loc loc, Identifier *id, Expression *value, Type *memType,
- StorageClass stc, UserAttributeDeclaration *uad, DeprecatedDeclaration *dd);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ EnumMember *syntaxCopy(Dsymbol *s);
const char *kind() const;
- Expression *getVarExp(Loc loc, Scope *sc);
EnumMember *isEnumMember() { return this; }
void accept(Visitor *v) { v->visit(this); }
diff --git a/gcc/d/dmd/errors.d b/gcc/d/dmd/errors.d
new file mode 100644
index 00000000000..91a5c77b53e
--- /dev/null
+++ b/gcc/d/dmd/errors.d
@@ -0,0 +1,446 @@
+/**
+ * Functions for raising errors.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/errors.d, _errors.d)
+ * Documentation: https://dlang.org/phobos/dmd_errors.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/errors.d
+ */
+
+module dmd.errors;
+
+import core.stdc.stdarg;
+import dmd.globals;
+
+nothrow:
+
+/**
+ * Color highlighting to classify messages
+ */
+enum Classification : Color
+{
+ error = Color.brightRed, /// for errors
+ gagged = Color.brightBlue, /// for gagged errors
+ warning = Color.brightYellow, /// for warnings
+ deprecation = Color.brightCyan, /// for deprecations
+ tip = Color.brightGreen, /// for tip messages
+}
+
+enum Color : int
+{
+ black = 0,
+ red = 1,
+ green = 2,
+ blue = 4,
+ yellow = red | green,
+ magenta = red | blue,
+ cyan = green | blue,
+ lightGray = red | green | blue,
+ bright = 8,
+ darkGray = bright | black,
+ brightRed = bright | red,
+ brightGreen = bright | green,
+ brightBlue = bright | blue,
+ brightYellow = bright | yellow,
+ brightMagenta = bright | magenta,
+ brightCyan = bright | cyan,
+ white = bright | lightGray,
+}
+
+
+static if (__VERSION__ < 2092)
+ private extern (C++) void noop(const ref Loc loc, const(char)* format, ...) {}
+else
+ pragma(printf) private extern (C++) void noop(const ref Loc loc, const(char)* format, ...) {}
+
+
+package auto previewErrorFunc(bool isDeprecated, FeatureState featureState) @safe @nogc pure nothrow
+{
+ if (featureState == FeatureState.enabled)
+ return &error;
+ else if (featureState == FeatureState.disabled || isDeprecated)
+ return &noop;
+ else
+ return &deprecation;
+}
+
+package auto previewSupplementalFunc(bool isDeprecated, FeatureState featureState) @safe @nogc pure nothrow
+{
+ if (featureState == FeatureState.enabled)
+ return &errorSupplemental;
+ else if (featureState == FeatureState.disabled || isDeprecated)
+ return &noop;
+ else
+ return &deprecationSupplemental;
+}
+
+
+/**
+ * Print an error message, increasing the global error count.
+ * Params:
+ * loc = location of error
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void error(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ verror(loc, format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void error(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ verror(loc, format, ap);
+ va_end(ap);
+ }
+
+/**
+ * Same as above, but takes a filename and line information arguments as separate parameters.
+ * Params:
+ * filename = source file of error
+ * linnum = line in the source file
+ * charnum = column number on the line
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...)
+ {
+ const loc = Loc(filename, linnum, charnum);
+ va_list ap;
+ va_start(ap, format);
+ verror(loc, format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...)
+ {
+ const loc = Loc(filename, linnum, charnum);
+ va_list ap;
+ va_start(ap, format);
+ verror(loc, format, ap);
+ va_end(ap);
+ }
+
+/**
+ * Print additional details about an error message.
+ * Doesn't increase the error count or print an additional error prefix.
+ * Params:
+ * loc = location of error
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void errorSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ verrorSupplemental(loc, format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void errorSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ verrorSupplemental(loc, format, ap);
+ va_end(ap);
+ }
+
+/**
+ * Print a warning message, increasing the global warning count.
+ * Params:
+ * loc = location of warning
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void warning(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vwarning(loc, format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void warning(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vwarning(loc, format, ap);
+ va_end(ap);
+ }
+
+/**
+ * Print additional details about a warning message.
+ * Doesn't increase the warning count or print an additional warning prefix.
+ * Params:
+ * loc = location of warning
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void warningSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vwarningSupplemental(loc, format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void warningSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vwarningSupplemental(loc, format, ap);
+ va_end(ap);
+ }
+
+/**
+ * Print a deprecation message, may increase the global warning or error count
+ * depending on whether deprecations are ignored.
+ * Params:
+ * loc = location of deprecation
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void deprecation(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vdeprecation(loc, format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void deprecation(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vdeprecation(loc, format, ap);
+ va_end(ap);
+ }
+
+/**
+ * Print additional details about a deprecation message.
+ * Doesn't increase the error count, or print an additional deprecation prefix.
+ * Params:
+ * loc = location of deprecation
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void deprecationSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vdeprecationSupplemental(loc, format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void deprecationSupplemental(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vdeprecationSupplemental(loc, format, ap);
+ va_end(ap);
+ }
+
+/**
+ * Print a verbose message.
+ * Doesn't prefix or highlight messages.
+ * Params:
+ * loc = location of message
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void message(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vmessage(loc, format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void message(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vmessage(loc, format, ap);
+ va_end(ap);
+ }
+
+/**
+ * Same as above, but doesn't take a location argument.
+ * Params:
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void message(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vmessage(Loc.initial, format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void message(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vmessage(Loc.initial, format, ap);
+ va_end(ap);
+ }
+
+/**
+ * The type of the diagnostic handler
+ * see verrorPrint for arguments
+ * Returns: true if error handling is done, false to continue printing to stderr
+ */
+alias DiagnosticHandler = bool delegate(const ref Loc location, Color headerColor, const(char)* header, const(char)* messageFormat, va_list args, const(char)* prefix1, const(char)* prefix2);
+
+/**
+ * The diagnostic handler.
+ * If non-null it will be called for every diagnostic message issued by the compiler.
+ * If it returns false, the message will be printed to stderr as usual.
+ */
+__gshared DiagnosticHandler diagnosticHandler;
+
+/**
+ * Print a tip message with the prefix and highlighting.
+ * Params:
+ * format = printf-style format specification
+ * ... = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void tip(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vtip(format, ap);
+ va_end(ap);
+ }
+else
+ pragma(printf) extern (C++) void tip(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ vtip(format, ap);
+ va_end(ap);
+ }
+
+
+/**
+ * Same as $(D error), but takes a va_list parameter, and optionally additional message prefixes.
+ * Params:
+ * loc = location of error
+ * format = printf-style format specification
+ * ap = printf-style variadic arguments
+ * p1 = additional message prefix
+ * p2 = additional message prefix
+ * header = title of error message
+ */
+extern (C++) void verror(const ref Loc loc, const(char)* format, va_list ap, const(char)* p1 = null, const(char)* p2 = null, const(char)* header = "Error: ");
+
+/**
+ * Same as $(D errorSupplemental), but takes a va_list parameter.
+ * Params:
+ * loc = location of error
+ * format = printf-style format specification
+ * ap = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap);
+else
+ pragma(printf) extern (C++) void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap);
+
+/**
+ * Same as $(D warning), but takes a va_list parameter.
+ * Params:
+ * loc = location of warning
+ * format = printf-style format specification
+ * ap = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void vwarning(const ref Loc loc, const(char)* format, va_list ap);
+else
+ pragma(printf) extern (C++) void vwarning(const ref Loc loc, const(char)* format, va_list ap);
+
+/**
+ * Same as $(D warningSupplemental), but takes a va_list parameter.
+ * Params:
+ * loc = location of warning
+ * format = printf-style format specification
+ * ap = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap);
+else
+ pragma(printf) extern (C++) void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap);
+
+/**
+ * Same as $(D deprecation), but takes a va_list parameter, and optionally additional message prefixes.
+ * Params:
+ * loc = location of deprecation
+ * format = printf-style format specification
+ * ap = printf-style variadic arguments
+ * p1 = additional message prefix
+ * p2 = additional message prefix
+ */
+extern (C++) void vdeprecation(const ref Loc loc, const(char)* format, va_list ap, const(char)* p1 = null, const(char)* p2 = null);
+
+/**
+ * Same as $(D message), but takes a va_list parameter.
+ * Params:
+ * loc = location of message
+ * format = printf-style format specification
+ * ap = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void vmessage(const ref Loc loc, const(char)* format, va_list ap);
+else
+ pragma(printf) extern (C++) void vmessage(const ref Loc loc, const(char)* format, va_list ap);
+
+/**
+ * Same as $(D tip), but takes a va_list parameter.
+ * Params:
+ * format = printf-style format specification
+ * ap = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void vtip(const(char)* format, va_list ap);
+else
+ pragma(printf) extern (C++) void vtip(const(char)* format, va_list ap);
+
+/**
+ * Same as $(D deprecationSupplemental), but takes a va_list parameter.
+ * Params:
+ * loc = location of deprecation
+ * format = printf-style format specification
+ * ap = printf-style variadic arguments
+ */
+static if (__VERSION__ < 2092)
+ extern (C++) void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap);
+else
+ pragma(printf) extern (C++) void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap);
+
+/**
+ * Call this after printing out fatal error messages to clean up and exit
+ * the compiler.
+ */
+extern (C++) void fatal();
+
+/**
+ * Try to stop forgetting to remove the breakpoints from
+ * release builds.
+ */
+extern (C++) void halt();
diff --git a/gcc/d/dmd/errors.h b/gcc/d/dmd/errors.h
index a92ae2abbc4..6d9587d1436 100644
--- a/gcc/d/dmd/errors.h
+++ b/gcc/d/dmd/errors.h
@@ -11,7 +11,8 @@
#pragma once
#include "root/dsystem.h"
-#include "globals.h"
+
+struct Loc;
bool isConsoleColorSupported();
@@ -27,6 +28,7 @@ D_ATTRIBUTE_FORMAT(2, 3) void warningSupplemental(const Loc& loc, const char *fo
D_ATTRIBUTE_FORMAT(2, 3) void deprecation(const Loc& loc, const char *format, ...);
D_ATTRIBUTE_FORMAT(2, 3) void deprecationSupplemental(const Loc& loc, const char *format, ...);
D_ATTRIBUTE_FORMAT(2, 3) void error(const Loc& loc, const char *format, ...);
+D_ATTRIBUTE_FORMAT(4, 5) void error(const char *filename, unsigned linnum, unsigned charnum, const char *format, ...);
D_ATTRIBUTE_FORMAT(2, 3) void errorSupplemental(const Loc& loc, const char *format, ...);
D_ATTRIBUTE_FORMAT(2, 0) void verror(const Loc& loc, const char *format, va_list ap, const char *p1 = NULL, const char *p2 = NULL, const char *header = "Error: ");
D_ATTRIBUTE_FORMAT(2, 0) void verrorSupplemental(const Loc& loc, const char *format, va_list ap);
@@ -36,7 +38,9 @@ D_ATTRIBUTE_FORMAT(2, 0) void vdeprecation(const Loc& loc, const char *format, v
D_ATTRIBUTE_FORMAT(2, 0) void vdeprecationSupplemental(const Loc& loc, const char *format, va_list ap);
D_ATTRIBUTE_FORMAT(1, 2) void message(const char *format, ...);
D_ATTRIBUTE_FORMAT(2, 3) void message(const Loc& loc, const char *format, ...);
-D_ATTRIBUTE_FORMAT(2, 0) void vmessage(const Loc& loc, const char *format, va_list);
+D_ATTRIBUTE_FORMAT(2, 0) void vmessage(const Loc& loc, const char *format, va_list ap);
+D_ATTRIBUTE_FORMAT(1, 2) void tip(const char *format, ...);
+D_ATTRIBUTE_FORMAT(1, 0) void vtip(const char *format, va_list ap);
#if defined(__GNUC__) || defined(__clang__)
#define D_ATTRIBUTE_NORETURN __attribute__((noreturn))
diff --git a/gcc/d/dmd/escape.c b/gcc/d/dmd/escape.c
deleted file mode 100644
index cd0382b0a22..00000000000
--- a/gcc/d/dmd/escape.c
+++ /dev/null
@@ -1,1234 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/escape.c
- */
-
-#include "mars.h"
-#include "init.h"
-#include "expression.h"
-#include "scope.h"
-#include "aggregate.h"
-#include "declaration.h"
-#include "module.h"
-
-/************************************
- * Aggregate the data collected by the escapeBy??() functions.
- */
-struct EscapeByResults
-{
- VarDeclarations byref; // array into which variables being returned by ref are inserted
- VarDeclarations byvalue; // array into which variables with values containing pointers are inserted
- FuncDeclarations byfunc; // nested functions that are turned into delegates
- Expressions byexp; // array into which temporaries being returned by ref are inserted
-};
-
-static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag);
-static void inferReturn(FuncDeclaration *fd, VarDeclaration *v);
-static void escapeByValue(Expression *e, EscapeByResults *er);
-static void escapeByRef(Expression *e, EscapeByResults *er);
-static void findAllOuterAccessedVariables(FuncDeclaration *fd, VarDeclarations *vars);
-
-/* 'v' is assigned unsafely to 'par'
-*/
-static void unsafeAssign(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag,
- bool &result, VarDeclaration *v, const char *desc)
-{
- if (global.params.vsafe && sc->func->setUnsafe())
- {
- if (!gag)
- error(arg->loc, "%s %s assigned to non-scope parameter %s calling %s",
- desc, v->toChars(),
- par ? par->toChars() : "unnamed",
- fdc ? fdc->toPrettyChars() : "indirectly");
- result = true;
- }
-}
-
-/****************************************
- * Function parameter par is being initialized to arg,
- * and par may escape.
- * Detect if scoped values can escape this way.
- * Print error messages when these are detected.
- * Params:
- * sc = used to determine current function and module
- * par = identifier of function parameter
- * arg = initializer for param
- * gag = do not print error messages
- * Returns:
- * true if pointers to the stack can escape via assignment
- */
-bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag)
-{
- //printf("checkParamArgumentEscape(arg: %s par: %s)\n", arg->toChars(), par->toChars());
- //printf("type = %s, %d\n", arg->type->toChars(), arg->type->hasPointers());
-
- if (!arg->type->hasPointers())
- return false;
-
- EscapeByResults er;
-
- escapeByValue(arg, &er);
-
- if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length)
- return false;
-
- bool result = false;
-
- for (size_t i = 0; i < er.byvalue.length; i++)
- {
- //printf("byvalue %s\n", v->toChars());
- VarDeclaration *v = er.byvalue[i];
- if (v->isDataseg())
- continue;
-
- Dsymbol *p = v->toParent2();
-
- v->storage_class &= ~STCmaybescope;
-
- if (v->isScope())
- {
- unsafeAssign(sc, fdc, par, arg, gag, result, v, "scope variable");
- }
- else if (v->storage_class & STCvariadic && p == sc->func)
- {
- Type *tb = v->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- unsafeAssign(sc, fdc, par, arg, gag, result, v, "variadic variable");
- }
- }
- else
- {
- /* v is not 'scope', and is assigned to a parameter that may escape.
- * Therefore, v can never be 'scope'.
- */
- v->doNotInferScope = true;
- }
- }
-
- for (size_t i = 0; i < er.byref.length; i++)
- {
- VarDeclaration *v = er.byref[i];
- if (v->isDataseg())
- continue;
-
- Dsymbol *p = v->toParent2();
-
- v->storage_class &= ~STCmaybescope;
-
- if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func)
- {
- unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local variable");
- continue;
- }
- }
-
- for (size_t i = 0; i < er.byfunc.length; i++)
- {
- FuncDeclaration *fd = er.byfunc[i];
- //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
- VarDeclarations vars;
- findAllOuterAccessedVariables(fd, &vars);
-
- for (size_t j = 0; j < vars.length; j++)
- {
- VarDeclaration *v = vars[j];
- //printf("v = %s\n", v->toChars());
- assert(!v->isDataseg()); // these are not put in the closureVars[]
-
- Dsymbol *p = v->toParent2();
-
- v->storage_class &= ~STCmaybescope;
-
- if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func)
- {
- unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local");
- continue;
- }
- }
- }
-
- for (size_t i = 0; i < er.byexp.length; i++)
- {
- Expression *ee = er.byexp[i];
- if (sc->func->setUnsafe())
- {
- if (!gag)
- error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope parameter %s",
- ee->toChars(),
- par ? par->toChars() : "unnamed");
- result = true;
- }
- }
-
- return result;
-}
-
-/****************************************
- * Given an AssignExp, determine if the lvalue will cause
- * the contents of the rvalue to escape.
- * Print error messages when these are detected.
- * Infer 'scope' for the lvalue where possible, in order
- * to eliminate the error.
- * Params:
- * sc = used to determine current function and module
- * ae = AssignExp to check for any pointers to the stack
- * gag = do not print error messages
- * Returns:
- * true if pointers to the stack can escape via assignment
- */
-bool checkAssignEscape(Scope *sc, Expression *e, bool gag)
-{
- //printf("checkAssignEscape(e: %s)\n", e->toChars());
- if (e->op != TOKassign && e->op != TOKblit && e->op != TOKconstruct)
- return false;
- AssignExp *ae = (AssignExp *)e;
- Expression *e1 = ae->e1;
- Expression *e2 = ae->e2;
- //printf("type = %s, %d\n", e1->type->toChars(), e1->type->hasPointers());
-
- if (!e1->type->hasPointers())
- return false;
-
- if (e1->op == TOKslice)
- return false;
-
- EscapeByResults er;
-
- escapeByValue(e2, &er);
-
- if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length)
- return false;
-
- VarDeclaration *va = NULL;
- while (e1->op == TOKdotvar)
- e1 = ((DotVarExp *)e1)->e1;
-
- if (e1->op == TOKvar)
- va = ((VarExp *)e1)->var->isVarDeclaration();
- else if (e1->op == TOKthis)
- va = ((ThisExp *)e1)->var->isVarDeclaration();
- else if (e1->op == TOKindex)
- {
- IndexExp *ie = (IndexExp *)e1;
- if (ie->e1->op == TOKvar && ie->e1->type->toBasetype()->ty == Tsarray)
- va = ((VarExp *)ie->e1)->var->isVarDeclaration();
- }
-
- // Try to infer 'scope' for va if in a function not marked @system
- bool inferScope = false;
- if (va && sc->func && sc->func->type && sc->func->type->ty == Tfunction)
- inferScope = ((TypeFunction *)sc->func->type)->trust != TRUSTsystem;
-
- bool result = false;
- for (size_t i = 0; i < er.byvalue.length; i++)
- {
- VarDeclaration *v = er.byvalue[i];
- //printf("byvalue: %s\n", v->toChars());
- if (v->isDataseg())
- continue;
-
- Dsymbol *p = v->toParent2();
-
- if (!(va && va->isScope()))
- v->storage_class &= ~STCmaybescope;
-
- if (v->isScope())
- {
- if (va && va->isScope() && va->storage_class & STCreturn && !(v->storage_class & STCreturn) &&
- sc->func->setUnsafe())
- {
- if (!gag)
- error(ae->loc, "scope variable %s assigned to return scope %s", v->toChars(), va->toChars());
- result = true;
- continue;
- }
-
- // If va's lifetime encloses v's, then error
- if (va &&
- ((va->enclosesLifetimeOf(v) && !(v->storage_class & STCparameter)) ||
- // va is class reference
- (ae->e1->op == TOKdotvar && va->type->toBasetype()->ty == Tclass && (va->enclosesLifetimeOf(v) || !va->isScope())) ||
- va->storage_class & STCref) &&
- sc->func->setUnsafe())
- {
- if (!gag)
- error(ae->loc, "scope variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars());
- result = true;
- continue;
- }
-
- if (va && !va->isDataseg() && !va->doNotInferScope)
- {
- if (!va->isScope() && inferScope)
- { //printf("inferring scope for %s\n", va->toChars());
- va->storage_class |= STCscope | STCscopeinferred;
- va->storage_class |= v->storage_class & STCreturn;
- }
- continue;
- }
- if (sc->func->setUnsafe())
- {
- if (!gag)
- error(ae->loc, "scope variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
- result = true;
- }
- }
- else if (v->storage_class & STCvariadic && p == sc->func)
- {
- Type *tb = v->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (va && !va->isDataseg() && !va->doNotInferScope)
- {
- if (!va->isScope() && inferScope)
- { //printf("inferring scope for %s\n", va->toChars());
- va->storage_class |= STCscope | STCscopeinferred;
- }
- continue;
- }
- if (sc->func->setUnsafe())
- {
- if (!gag)
- error(ae->loc, "variadic variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
- result = true;
- }
- }
- }
- else
- {
- /* v is not 'scope', and we didn't check the scope of where we assigned it to.
- * It may escape via that assignment, therefore, v can never be 'scope'.
- */
- v->doNotInferScope = true;
- }
- }
-
- for (size_t i = 0; i < er.byref.length; i++)
- {
- VarDeclaration *v = er.byref[i];
- //printf("byref: %s\n", v->toChars());
- if (v->isDataseg())
- continue;
-
- Dsymbol *p = v->toParent2();
-
- // If va's lifetime encloses v's, then error
- if (va &&
- ((va->enclosesLifetimeOf(v) && !(v->storage_class & STCparameter)) || va->storage_class & STCref) &&
- sc->func->setUnsafe())
- {
- if (!gag)
- error(ae->loc, "address of variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars());
- result = true;
- continue;
- }
-
- if (!(va && va->isScope()))
- v->storage_class &= ~STCmaybescope;
-
- if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func)
- {
- if (va && !va->isDataseg() && !va->doNotInferScope)
- {
- if (!va->isScope() && inferScope)
- { //printf("inferring scope for %s\n", va->toChars());
- va->storage_class |= STCscope | STCscopeinferred;
- }
- continue;
- }
- if (sc->func->setUnsafe())
- {
- if (!gag)
- error(ae->loc, "reference to local variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
- result = true;
- }
- continue;
- }
- }
-
- for (size_t i = 0; i < er.byfunc.length; i++)
- {
- FuncDeclaration *fd = er.byfunc[i];
- //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
- VarDeclarations vars;
- findAllOuterAccessedVariables(fd, &vars);
-
- for (size_t j = 0; j < vars.length; j++)
- {
- VarDeclaration *v = vars[j];
- //printf("v = %s\n", v->toChars());
- assert(!v->isDataseg()); // these are not put in the closureVars[]
-
- Dsymbol *p = v->toParent2();
-
- if (!(va && va->isScope()))
- v->storage_class &= ~STCmaybescope;
-
- if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func)
- {
- if (va && !va->isDataseg() && !va->doNotInferScope)
- {
- /* Don't infer STCscope for va, because then a closure
- * won't be generated for sc->func.
- */
- //if (!va->isScope() && inferScope)
- //va->storage_class |= STCscope | STCscopeinferred;
- continue;
- }
- if (sc->func->setUnsafe())
- {
- if (!gag)
- error(ae->loc, "reference to local %s assigned to non-scope %s in @safe code", v->toChars(), e1->toChars());
- result = true;
- }
- continue;
- }
- }
- }
-
- for (size_t i = 0; i < er.byexp.length; i++)
- {
- Expression *ee = er.byexp[i];
- if (va && !va->isDataseg() && !va->doNotInferScope)
- {
- if (!va->isScope() && inferScope)
- { //printf("inferring scope for %s\n", va->toChars());
- va->storage_class |= STCscope | STCscopeinferred;
- }
- continue;
- }
- if (sc->func->setUnsafe())
- {
- if (!gag)
- error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope %s",
- ee->toChars(), e1->toChars());
- result = true;
- }
- }
-
- return result;
-}
-
-/************************************
- * Detect cases where pointers to the stack can 'escape' the
- * lifetime of the stack frame when throwing `e`.
- * Print error messages when these are detected.
- * Params:
- * sc = used to determine current function and module
- * e = expression to check for any pointers to the stack
- * gag = do not print error messages
- * Returns:
- * true if pointers to the stack can escape
- */
-bool checkThrowEscape(Scope *sc, Expression *e, bool gag)
-{
- //printf("[%s] checkThrowEscape, e = %s\n", e->loc->toChars(), e->toChars());
- EscapeByResults er;
-
- escapeByValue(e, &er);
-
- if (!er.byref.length && !er.byvalue.length && !er.byexp.length)
- return false;
-
- bool result = false;
- for (size_t i = 0; i < er.byvalue.length; i++)
- {
- VarDeclaration *v = er.byvalue[i];
- //printf("byvalue %s\n", v->toChars());
- if (v->isDataseg())
- continue;
-
- if (v->isScope())
- {
- if (sc->_module && sc->_module->isRoot())
- {
- // Only look for errors if in module listed on command line
- if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
- {
- if (!gag)
- error(e->loc, "scope variable %s may not be thrown", v->toChars());
- result = true;
- }
- continue;
- }
- }
- else
- {
- //printf("no infer for %s\n", v->toChars());
- v->doNotInferScope = true;
- }
- }
- return result;
-}
-
-/************************************
- * Detect cases where pointers to the stack can 'escape' the
- * lifetime of the stack frame by returning 'e' by value.
- * Params:
- * sc = used to determine current function and module
- * e = expression to check for any pointers to the stack
- * gag = do not print error messages
- * Returns:
- * true if pointers to the stack can escape
- */
-
-bool checkReturnEscape(Scope *sc, Expression *e, bool gag)
-{
- //printf("[%s] checkReturnEscape, e = %s\n", e->loc->toChars(), e->toChars());
- return checkReturnEscapeImpl(sc, e, false, gag);
-}
-
-/************************************
- * Detect cases where returning 'e' by ref can result in a reference to the stack
- * being returned.
- * Print error messages when these are detected.
- * Params:
- * sc = used to determine current function and module
- * e = expression to check
- * gag = do not print error messages
- * Returns:
- * true if references to the stack can escape
- */
-bool checkReturnEscapeRef(Scope *sc, Expression *e, bool gag)
-{
- //printf("[%s] checkReturnEscapeRef, e = %s\n", e->loc.toChars(), e->toChars());
- //printf("current function %s\n", sc->func->toChars());
- //printf("parent2 function %s\n", sc->func->toParent2()->toChars());
-
- return checkReturnEscapeImpl(sc, e, true, gag);
-}
-
-static void escapingRef(VarDeclaration *v, Expression *e, bool &result, bool gag)
-{
- if (!gag)
- {
- const char *msg;
- if (v->storage_class & STCparameter)
- msg = "returning `%s` escapes a reference to parameter `%s`, perhaps annotate with `return`";
- else
- msg = "returning `%s` escapes a reference to local variable `%s`";
- error(e->loc, msg, e->toChars(), v->toChars());
- }
- result = true;
-}
-
-static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag)
-{
- //printf("[%s] checkReturnEscapeImpl, e = %s\n", e->loc->toChars(), e->toChars());
- EscapeByResults er;
-
- if (refs)
- escapeByRef(e, &er);
- else
- escapeByValue(e, &er);
-
- if (!er.byref.length && !er.byvalue.length && !er.byexp.length)
- return false;
-
- bool result = false;
- for (size_t i = 0; i < er.byvalue.length; i++)
- {
- VarDeclaration *v = er.byvalue[i];
- //printf("byvalue %s\n", v->toChars());
- if (v->isDataseg())
- continue;
-
- Dsymbol *p = v->toParent2();
-
- if ((v->isScope() || (v->storage_class & STCmaybescope)) &&
- !(v->storage_class & STCreturn) &&
- v->isParameter() &&
- sc->func->flags & FUNCFLAGreturnInprocess &&
- p == sc->func)
- {
- inferReturn(sc->func, v); // infer addition of 'return'
- continue;
- }
-
- if (v->isScope())
- {
- if (v->storage_class & STCreturn)
- continue;
-
- if (sc->_module && sc->_module->isRoot() &&
- /* This case comes up when the ReturnStatement of a __foreachbody is
- * checked for escapes by the caller of __foreachbody. Skip it.
- *
- * struct S { static int opApply(int delegate(S*) dg); }
- * S* foo() {
- * foreach (S* s; S) // create __foreachbody for body of foreach
- * return s; // s is inferred as 'scope' but incorrectly tested in foo()
- * return null; }
- */
- !(!refs && p->parent == sc->func))
- {
- // Only look for errors if in module listed on command line
- if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
- {
- if (!gag)
- error(e->loc, "scope variable %s may not be returned", v->toChars());
- result = true;
- }
- continue;
- }
- }
- else if (v->storage_class & STCvariadic && p == sc->func)
- {
- Type *tb = v->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!gag)
- error(e->loc, "returning `%s` escapes a reference to variadic parameter `%s`", e->toChars(), v->toChars());
- result = false;
- }
- }
- else
- {
- //printf("no infer for %s\n", v->toChars());
- v->doNotInferScope = true;
- }
- }
-
- for (size_t i = 0; i < er.byref.length; i++)
- {
- VarDeclaration *v = er.byref[i];
- //printf("byref %s\n", v->toChars());
- if (v->isDataseg())
- continue;
-
- Dsymbol *p = v->toParent2();
-
- if ((v->storage_class & (STCref | STCout)) == 0)
- {
- if (p == sc->func)
- {
- escapingRef(v, e, result, gag);
- continue;
- }
- FuncDeclaration *fd = p->isFuncDeclaration();
- if (fd && sc->func->flags & FUNCFLAGreturnInprocess)
- {
- /* Code like:
- * int x;
- * auto dg = () { return &x; }
- * Making it:
- * auto dg = () return { return &x; }
- * Because dg.ptr points to x, this is returning dt.ptr+offset
- */
- if (global.params.vsafe)
- sc->func->storage_class |= STCreturn;
- }
- }
-
- /* Check for returning a ref variable by 'ref', but should be 'return ref'
- * Infer the addition of 'return', or set result to be the offending expression.
- */
- if ( (v->storage_class & (STCref | STCout)) &&
- !(v->storage_class & (STCreturn | STCforeach)))
- {
- if ((sc->func->flags & FUNCFLAGreturnInprocess) && p == sc->func)
- {
- inferReturn(sc->func, v); // infer addition of 'return'
- }
- else if (global.params.useDIP25 &&
- sc->_module && sc->_module->isRoot())
- {
- // Only look for errors if in module listed on command line
-
- if (p == sc->func)
- {
- //printf("escaping reference to local ref variable %s\n", v->toChars());
- //printf("storage class = x%llx\n", v->storage_class);
- escapingRef(v, e, result, gag);
- continue;
- }
- // Don't need to be concerned if v's parent does not return a ref
- FuncDeclaration *fd = p->isFuncDeclaration();
- if (fd && fd->type && fd->type->ty == Tfunction)
- {
- TypeFunction *tf = (TypeFunction *)fd->type;
- if (tf->isref)
- {
- if (!gag)
- error(e->loc, "escaping reference to outer local variable %s", v->toChars());
- result = true;
- continue;
- }
- }
- }
- }
- }
-
- for (size_t i = 0; i < er.byexp.length; i++)
- {
- Expression *ee = er.byexp[i];
- //printf("byexp %s\n", ee->toChars());
- if (!gag)
- error(ee->loc, "escaping reference to stack allocated value returned by %s", ee->toChars());
- result = true;
- }
-
- return result;
-}
-
-
-/*************************************
- * Variable v needs to have 'return' inferred for it.
- * Params:
- * fd = function that v is a parameter to
- * v = parameter that needs to be STCreturn
- */
-
-static void inferReturn(FuncDeclaration *fd, VarDeclaration *v)
-{
- // v is a local in the current function
-
- //printf("for function '%s' inferring 'return' for variable '%s'\n", fd->toChars(), v->toChars());
- v->storage_class |= STCreturn;
-
- TypeFunction *tf = (TypeFunction *)fd->type;
- if (v == fd->vthis)
- {
- /* v is the 'this' reference, so mark the function
- */
- fd->storage_class |= STCreturn;
- if (tf->ty == Tfunction)
- {
- //printf("'this' too %p %s\n", tf, sc->func->toChars());
- tf->isreturn = true;
- }
- }
- else
- {
- // Perform 'return' inference on parameter
- if (tf->ty == Tfunction)
- {
- const size_t dim = tf->parameterList.length();
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *p = tf->parameterList[i];
- if (p->ident == v->ident)
- {
- p->storageClass |= STCreturn;
- break; // there can be only one
- }
- }
- }
- }
-}
-
-
-/****************************************
- * e is an expression to be returned by value, and that value contains pointers.
- * Walk e to determine which variables are possibly being
- * returned by value, such as:
- * int* function(int* p) { return p; }
- * If e is a form of &p, determine which variables have content
- * which is being returned as ref, such as:
- * int* function(int i) { return &i; }
- * Multiple variables can be inserted, because of expressions like this:
- * int function(bool b, int i, int* p) { return b ? &i : p; }
- *
- * No side effects.
- *
- * Params:
- * e = expression to be returned by value
- * er = where to place collected data
- */
-static void escapeByValue(Expression *e, EscapeByResults *er)
-{
- //printf("[%s] escapeByValue, e: %s\n", e->loc.toChars(), e->toChars());
-
- class EscapeVisitor : public Visitor
- {
- public:
- EscapeByResults *er;
-
- EscapeVisitor(EscapeByResults *er)
- : er(er)
- {
- }
-
- void visit(Expression *)
- {
- }
-
- void visit(AddrExp *e)
- {
- escapeByRef(e->e1, er);
- }
-
- void visit(SymOffExp *e)
- {
- VarDeclaration *v = e->var->isVarDeclaration();
- if (v)
- er->byref.push(v);
- }
-
- void visit(VarExp *e)
- {
- VarDeclaration *v = e->var->isVarDeclaration();
- if (v)
- er->byvalue.push(v);
- }
-
- void visit(ThisExp *e)
- {
- if (e->var)
- er->byvalue.push(e->var);
- }
-
- void visit(DotVarExp *e)
- {
- Type *t = e->e1->type->toBasetype();
- if (t->ty == Tstruct)
- e->e1->accept(this);
- }
-
- void visit(DelegateExp *e)
- {
- Type *t = e->e1->type->toBasetype();
- if (t->ty == Tclass || t->ty == Tpointer)
- escapeByValue(e->e1, er);
- else
- escapeByRef(e->e1, er);
- er->byfunc.push(e->func);
- }
-
- void visit(FuncExp *e)
- {
- if (e->fd->tok == TOKdelegate)
- er->byfunc.push(e->fd);
- }
-
- void visit(TupleExp *)
- {
- assert(0); // should have been lowered by now
- }
-
- void visit(ArrayLiteralExp *e)
- {
- Type *tb = e->type->toBasetype();
- if (tb->ty == Tsarray || tb->ty == Tarray)
- {
- if (e->basis)
- e->basis->accept(this);
- for (size_t i = 0; i < e->elements->length; i++)
- {
- Expression *el = (*e->elements)[i];
- if (el)
- el->accept(this);
- }
- }
- }
-
- void visit(StructLiteralExp *e)
- {
- if (e->elements)
- {
- for (size_t i = 0; i < e->elements->length; i++)
- {
- Expression *ex = (*e->elements)[i];
- if (ex)
- ex->accept(this);
- }
- }
- }
-
- void visit(NewExp *e)
- {
- Type *tb = e->newtype->toBasetype();
- if (tb->ty == Tstruct && !e->member && e->arguments)
- {
- for (size_t i = 0; i < e->arguments->length; i++)
- {
- Expression *ex = (*e->arguments)[i];
- if (ex)
- ex->accept(this);
- }
- }
- }
-
- void visit(CastExp *e)
- {
- Type *tb = e->type->toBasetype();
- if (tb->ty == Tarray &&
- e->e1->type->toBasetype()->ty == Tsarray)
- {
- escapeByRef(e->e1, er);
- }
- else
- e->e1->accept(this);
- }
-
- void visit(SliceExp *e)
- {
- if (e->e1->op == TOKvar)
- {
- VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
- Type *tb = e->type->toBasetype();
- if (v)
- {
- if (tb->ty == Tsarray)
- return;
- if (v->storage_class & STCvariadic)
- {
- er->byvalue.push(v);
- return;
- }
- }
- }
- Type *t1b = e->e1->type->toBasetype();
- if (t1b->ty == Tsarray)
- {
- Type *tb = e->type->toBasetype();
- if (tb->ty != Tsarray)
- escapeByRef(e->e1, er);
- }
- else
- e->e1->accept(this);
- }
-
- void visit(BinExp *e)
- {
- Type *tb = e->type->toBasetype();
- if (tb->ty == Tpointer)
- {
- e->e1->accept(this);
- e->e2->accept(this);
- }
- }
-
- void visit(BinAssignExp *e)
- {
- e->e1->accept(this);
- }
-
- void visit(AssignExp *e)
- {
- e->e1->accept(this);
- }
-
- void visit(CommaExp *e)
- {
- e->e2->accept(this);
- }
-
- void visit(CondExp *e)
- {
- e->e1->accept(this);
- e->e2->accept(this);
- }
-
- void visit(CallExp *e)
- {
- //printf("CallExp(): %s\n", e->toChars());
- /* Check each argument that is
- * passed as 'return scope'.
- */
- Type *t1 = e->e1->type->toBasetype();
- TypeFunction *tf = NULL;
- TypeDelegate *dg = NULL;
- if (t1->ty == Tdelegate)
- {
- dg = (TypeDelegate *)t1;
- tf = (TypeFunction *)dg->next;
- }
- else if (t1->ty == Tfunction)
- tf = (TypeFunction *)t1;
- else
- return;
-
- if (e->arguments && e->arguments->length)
- {
- /* j=1 if _arguments[] is first argument,
- * skip it because it is not passed by ref
- */
- size_t j = tf->isDstyleVariadic();
- for (size_t i = j; i < e->arguments->length; ++i)
- {
- Expression *arg = (*e->arguments)[i];
- size_t nparams = tf->parameterList.length();
- if (i - j < nparams && i >= j)
- {
- Parameter *p = tf->parameterList[i - j];
- const StorageClass stc = tf->parameterStorageClass(p);
- if ((stc & (STCscope)) && (stc & STCreturn))
- arg->accept(this);
- else if ((stc & (STCref)) && (stc & STCreturn))
- escapeByRef(arg, er);
- }
- }
- }
- // If 'this' is returned, check it too
- if (e->e1->op == TOKdotvar && t1->ty == Tfunction)
- {
- DotVarExp *dve = (DotVarExp *)e->e1;
- FuncDeclaration *fd = dve->var->isFuncDeclaration();
- AggregateDeclaration *ad = NULL;
- if (global.params.vsafe && tf->isreturn && fd && (ad = fd->isThis()) != NULL)
- {
- if (ad->isClassDeclaration() || tf->isscope) // this is 'return scope'
- dve->e1->accept(this);
- else if (ad->isStructDeclaration()) // this is 'return ref'
- escapeByRef(dve->e1, er);
- }
- else if (dve->var->storage_class & STCreturn || tf->isreturn)
- {
- if (dve->var->storage_class & STCscope)
- dve->e1->accept(this);
- else if (dve->var->storage_class & STCref)
- escapeByRef(dve->e1, er);
- }
- }
-
- /* If returning the result of a delegate call, the .ptr
- * field of the delegate must be checked.
- */
- if (dg)
- {
- if (tf->isreturn)
- e->e1->accept(this);
- }
- }
- };
-
- EscapeVisitor v(er);
- e->accept(&v);
-}
-
-/****************************************
- * e is an expression to be returned by 'ref'.
- * Walk e to determine which variables are possibly being
- * returned by ref, such as:
- * ref int function(int i) { return i; }
- * If e is a form of *p, determine which variables have content
- * which is being returned as ref, such as:
- * ref int function(int* p) { return *p; }
- * Multiple variables can be inserted, because of expressions like this:
- * ref int function(bool b, int i, int* p) { return b ? i : *p; }
- *
- * No side effects.
- *
- * Params:
- * e = expression to be returned by 'ref'
- * er = where to place collected data
- */
-static void escapeByRef(Expression *e, EscapeByResults *er)
-{
- //printf("[%s] escapeByRef, e: %s\n", e->loc->toChars(), e->toChars());
- class EscapeRefVisitor : public Visitor
- {
- public:
- EscapeByResults *er;
-
- EscapeRefVisitor(EscapeByResults *er)
- : er(er)
- {
- }
-
- void visit(Expression *)
- {
- }
-
- void visit(VarExp *e)
- {
- VarDeclaration *v = e->var->isVarDeclaration();
- if (v)
- {
- if (v->storage_class & STCref && v->storage_class & (STCforeach | STCtemp) && v->_init)
- {
- /* If compiler generated ref temporary
- * (ref v = ex; ex)
- * look at the initializer instead
- */
- if (ExpInitializer *ez = v->_init->isExpInitializer())
- {
- assert(ez->exp && ez->exp->op == TOKconstruct);
- Expression *ex = ((ConstructExp *)ez->exp)->e2;
- ex->accept(this);
- }
- }
- else
- er->byref.push(v);
- }
- }
-
- void visit(ThisExp *e)
- {
- if (e->var)
- er->byref.push(e->var);
- }
-
- void visit(PtrExp *e)
- {
- escapeByValue(e->e1, er);
- }
-
- void visit(IndexExp *e)
- {
- Type *tb = e->e1->type->toBasetype();
- if (e->e1->op == TOKvar)
- {
- VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (v->storage_class & STCvariadic)
- {
- er->byref.push(v);
- return;
- }
- }
- }
- if (tb->ty == Tsarray)
- {
- e->e1->accept(this);
- }
- else if (tb->ty == Tarray)
- {
- escapeByValue(e->e1, er);
- }
- }
-
- void visit(DotVarExp *e)
- {
- Type *t1b = e->e1->type->toBasetype();
- if (t1b->ty == Tclass)
- escapeByValue(e->e1, er);
- else
- e->e1->accept(this);
- }
-
- void visit(BinAssignExp *e)
- {
- e->e1->accept(this);
- }
-
- void visit(AssignExp *e)
- {
- e->e1->accept(this);
- }
-
- void visit(CommaExp *e)
- {
- e->e2->accept(this);
- }
-
- void visit(CondExp *e)
- {
- e->e1->accept(this);
- e->e2->accept(this);
- }
-
- void visit(CallExp *e)
- {
- /* If the function returns by ref, check each argument that is
- * passed as 'return ref'.
- */
- Type *t1 = e->e1->type->toBasetype();
- TypeFunction *tf;
- if (t1->ty == Tdelegate)
- tf = (TypeFunction *)((TypeDelegate *)t1)->next;
- else if (t1->ty == Tfunction)
- tf = (TypeFunction *)t1;
- else
- return;
- if (tf->isref)
- {
- if (e->arguments && e->arguments->length)
- {
- /* j=1 if _arguments[] is first argument,
- * skip it because it is not passed by ref
- */
- size_t j = tf->isDstyleVariadic();
-
- for (size_t i = j; i < e->arguments->length; ++i)
- {
- Expression *arg = (*e->arguments)[i];
- size_t nparams = tf->parameterList.length();
- if (i - j < nparams && i >= j)
- {
- Parameter *p = tf->parameterList[i - j];
- const StorageClass stc = tf->parameterStorageClass(p);
- if ((stc & (STCout | STCref)) && (stc & STCreturn))
- arg->accept(this);
- else if ((stc & STCscope) && (stc & STCreturn))
- {
- if (arg->op == TOKdelegate)
- {
- DelegateExp *de = (DelegateExp *)arg;
- if (de->func->isNested())
- er->byexp.push(de);
- }
- else
- escapeByValue(arg, er);
- }
- }
- }
- }
-
- // If 'this' is returned by ref, check it too
- if (e->e1->op == TOKdotvar && t1->ty == Tfunction)
- {
- DotVarExp *dve = (DotVarExp *)e->e1;
- if (dve->var->storage_class & STCreturn || tf->isreturn)
- {
- if ((dve->var->storage_class & STCscope) || tf->isscope)
- escapeByValue(dve->e1, er);
- else if ((dve->var->storage_class & STCref) || tf->isref)
- dve->e1->accept(this);
- }
-
- }
- // If it's a delegate, check it too
- if (e->e1->op == TOKvar && t1->ty == Tdelegate)
- {
- escapeByValue(e->e1, er);
- }
- }
- else
- er->byexp.push(e);
- }
- };
-
- EscapeRefVisitor v(er);
- e->accept(&v);
-}
-
-/*************************
- * Find all variables accessed by this delegate that are
- * in functions enclosing it.
- * Params:
- * fd = function
- * vars = array to append found variables to
- */
-void findAllOuterAccessedVariables(FuncDeclaration *fd, VarDeclarations *vars)
-{
- //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
- for (Dsymbol *p = fd->parent; p; p = p->parent)
- {
- FuncDeclaration *fdp = p->isFuncDeclaration();
- if (fdp)
- {
- for (size_t i = 0; i < fdp->closureVars.length; i++)
- {
- VarDeclaration *v = fdp->closureVars[i];
- for (size_t j = 0; j < v->nestedrefs.length; j++)
- {
- FuncDeclaration *fdv = v->nestedrefs[j];
- if (fdv == fd)
- {
- //printf("accessed: %s, type %s\n", v->toChars(), v->type->toChars());
- vars->push(v);
- }
- }
- }
- }
- }
-}
diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d
new file mode 100644
index 00000000000..d502f80975b
--- /dev/null
+++ b/gcc/d/dmd/escape.d
@@ -0,0 +1,2290 @@
+/**
+ * Most of the logic to implement scoped pointers and scoped references is here.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/escape.d, _escape.d)
+ * Documentation: https://dlang.org/phobos/dmd_escape.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/escape.d
+ */
+
+module dmd.escape;
+
+import core.stdc.stdio : printf;
+import core.stdc.stdlib;
+import core.stdc.string;
+
+import dmd.root.rmem;
+
+import dmd.aggregate;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.printast;
+import dmd.root.rootobject;
+import dmd.tokens;
+import dmd.visitor;
+import dmd.arraytypes;
+
+/******************************************************
+ * Checks memory objects passed to a function.
+ * Checks that if a memory object is passed by ref or by pointer,
+ * all of the refs or pointers are const, or there is only one mutable
+ * ref or pointer to it.
+ * References:
+ * DIP 1021
+ * Params:
+ * sc = used to determine current function and module
+ * fd = function being called
+ * tf = fd's type
+ * ethis = if not null, the `this` pointer
+ * arguments = actual arguments to function
+ * gag = do not print error messages
+ * Returns:
+ * `true` if error
+ */
+bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf,
+ Expression ethis, Expressions* arguments, bool gag)
+{
+ enum log = false;
+ if (log) printf("[%s] checkMutableArguments, fd: `%s`\n", fd.loc.toChars(), fd.toChars());
+ if (log && ethis) printf("ethis: `%s`\n", ethis.toChars());
+ bool errors = false;
+
+ /* Outer variable references are treated as if they are extra arguments
+ * passed by ref to the function (which they essentially are via the static link).
+ */
+ VarDeclaration[] outerVars = fd ? fd.outerVars[] : null;
+
+ const len = arguments.length + (ethis !is null) + outerVars.length;
+ if (len <= 1)
+ return errors;
+
+ struct EscapeBy
+ {
+ EscapeByResults er;
+ Parameter param; // null if no Parameter for this argument
+ bool isMutable; // true if reference to mutable
+ }
+
+ /* Store escapeBy as static data escapeByStorage so we can keep reusing the same
+ * arrays rather than reallocating them.
+ */
+ __gshared EscapeBy[] escapeByStorage;
+ auto escapeBy = escapeByStorage;
+ if (escapeBy.length < len)
+ {
+ auto newPtr = cast(EscapeBy*)mem.xrealloc(escapeBy.ptr, len * EscapeBy.sizeof);
+ // Clear the new section
+ memset(newPtr + escapeBy.length, 0, (len - escapeBy.length) * EscapeBy.sizeof);
+ escapeBy = newPtr[0 .. len];
+ escapeByStorage = escapeBy;
+ }
+ else
+ escapeBy = escapeBy[0 .. len];
+
+ const paramLength = tf.parameterList.length;
+
+ // Fill in escapeBy[] with arguments[], ethis, and outerVars[]
+ foreach (const i, ref eb; escapeBy)
+ {
+ bool refs;
+ Expression arg;
+ if (i < arguments.length)
+ {
+ arg = (*arguments)[i];
+ if (i < paramLength)
+ {
+ eb.param = tf.parameterList[i];
+ refs = eb.param.isReference();
+ eb.isMutable = eb.param.isReferenceToMutable(arg.type);
+ }
+ else
+ {
+ eb.param = null;
+ refs = false;
+ eb.isMutable = arg.type.isReferenceToMutable();
+ }
+ }
+ else if (ethis)
+ {
+ /* ethis is passed by value if a class reference,
+ * by ref if a struct value
+ */
+ eb.param = null;
+ arg = ethis;
+ auto ad = fd.isThis();
+ assert(ad);
+ assert(ethis);
+ if (ad.isClassDeclaration())
+ {
+ refs = false;
+ eb.isMutable = arg.type.isReferenceToMutable();
+ }
+ else
+ {
+ assert(ad.isStructDeclaration());
+ refs = true;
+ eb.isMutable = arg.type.isMutable();
+ }
+ }
+ else
+ {
+ // outer variables are passed by ref
+ eb.param = null;
+ refs = true;
+ auto var = outerVars[i - (len - outerVars.length)];
+ eb.isMutable = var.type.isMutable();
+ eb.er.byref.push(var);
+ continue;
+ }
+
+ if (refs)
+ escapeByRef(arg, &eb.er);
+ else
+ escapeByValue(arg, &eb.er);
+ }
+
+ void checkOnePair(size_t i, ref EscapeBy eb, ref EscapeBy eb2,
+ VarDeclaration v, VarDeclaration v2, bool of)
+ {
+ if (log) printf("v2: `%s`\n", v2.toChars());
+ if (v2 != v)
+ return;
+ //printf("v %d v2 %d\n", eb.isMutable, eb2.isMutable);
+ if (!(eb.isMutable || eb2.isMutable))
+ return;
+
+ if (!(global.params.useDIP1000 == FeatureState.enabled && sc.func.setUnsafe()))
+ return;
+
+ if (!gag)
+ {
+ // int i; funcThatEscapes(ref int i);
+ // funcThatEscapes(i); // error escaping reference _to_ `i`
+ // int* j; funcThatEscapes2(int* j);
+ // funcThatEscapes2(j); // error escaping reference _of_ `i`
+ const(char)* referenceVerb = of ? "of" : "to";
+ const(char)* msg = eb.isMutable && eb2.isMutable
+ ? "more than one mutable reference %s `%s` in arguments to `%s()`"
+ : "mutable and const references %s `%s` in arguments to `%s()`";
+ error((*arguments)[i].loc, msg,
+ referenceVerb,
+ v.toChars(),
+ fd ? fd.toPrettyChars() : "indirectly");
+ }
+ errors = true;
+ }
+
+ void escape(size_t i, ref EscapeBy eb, bool byval)
+ {
+ foreach (VarDeclaration v; byval ? eb.er.byvalue : eb.er.byref)
+ {
+ if (log)
+ {
+ const(char)* by = byval ? "byval" : "byref";
+ printf("%s %s\n", by, v.toChars());
+ }
+ if (byval && !v.type.hasPointers())
+ continue;
+ foreach (ref eb2; escapeBy[i + 1 .. $])
+ {
+ foreach (VarDeclaration v2; byval ? eb2.er.byvalue : eb2.er.byref)
+ {
+ checkOnePair(i, eb, eb2, v, v2, byval);
+ }
+ }
+ }
+ }
+ foreach (const i, ref eb; escapeBy[0 .. $ - 1])
+ {
+ escape(i, eb, true);
+ escape(i, eb, false);
+ }
+
+ /* Reset the arrays in escapeBy[] so we can reuse them next time through
+ */
+ foreach (ref eb; escapeBy)
+ {
+ eb.er.reset();
+ }
+
+ return errors;
+}
+
+/******************************************
+ * Array literal is going to be allocated on the GC heap.
+ * Check its elements to see if any would escape by going on the heap.
+ * Params:
+ * sc = used to determine current function and module
+ * ae = array literal expression
+ * gag = do not print error messages
+ * Returns:
+ * `true` if any elements escaped
+ */
+bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag)
+{
+ bool errors;
+ if (ae.basis)
+ errors = checkNewEscape(sc, ae.basis, gag);
+ foreach (ex; *ae.elements)
+ {
+ if (ex)
+ errors |= checkNewEscape(sc, ex, gag);
+ }
+ return errors;
+}
+
+/******************************************
+ * Associative array literal is going to be allocated on the GC heap.
+ * Check its elements to see if any would escape by going on the heap.
+ * Params:
+ * sc = used to determine current function and module
+ * ae = associative array literal expression
+ * gag = do not print error messages
+ * Returns:
+ * `true` if any elements escaped
+ */
+bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag)
+{
+ bool errors;
+ foreach (ex; *ae.keys)
+ {
+ if (ex)
+ errors |= checkNewEscape(sc, ex, gag);
+ }
+ foreach (ex; *ae.values)
+ {
+ if (ex)
+ errors |= checkNewEscape(sc, ex, gag);
+ }
+ return errors;
+}
+
+/****************************************
+ * Function parameter `par` is being initialized to `arg`,
+ * and `par` may escape.
+ * Detect if scoped values can escape this way.
+ * Print error messages when these are detected.
+ * Params:
+ * sc = used to determine current function and module
+ * fdc = function being called, `null` if called indirectly
+ * par = function parameter (`this` if null)
+ * arg = initializer for param
+ * assertmsg = true if the parameter is the msg argument to assert(bool, msg).
+ * gag = do not print error messages
+ * Returns:
+ * `true` if pointers to the stack can escape via assignment
+ */
+bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, Expression arg, bool assertmsg, bool gag)
+{
+ enum log = false;
+ if (log) printf("checkParamArgumentEscape(arg: %s par: %s)\n",
+ arg ? arg.toChars() : "null",
+ par ? par.toChars() : "this");
+ //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
+
+ if (!arg.type.hasPointers())
+ return false;
+
+ EscapeByResults er;
+
+ escapeByValue(arg, &er);
+
+ if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
+ return false;
+
+ bool result = false;
+
+ ScopeRef psr;
+ if (par && fdc && fdc.type.isTypeFunction())
+ psr = buildScopeRef(par.storageClass);
+ else
+ psr = ScopeRef.None;
+
+ /* 'v' is assigned unsafely to 'par'
+ */
+ void unsafeAssign(VarDeclaration v, const char* desc)
+ {
+ if (global.params.useDIP1000 == FeatureState.enabled && sc.func.setUnsafe())
+ {
+ if (!gag)
+ {
+ if (assertmsg)
+ {
+ error(arg.loc, "%s `%s` assigned to non-scope parameter calling `assert()`",
+ desc, v.toChars());
+ }
+ else
+ {
+ error(arg.loc, "%s `%s` assigned to non-scope parameter `%s` calling %s",
+ desc, v.toChars(),
+ par ? par.toChars() : "this",
+ fdc ? fdc.toPrettyChars() : "indirectly");
+ }
+ }
+ result = true;
+ }
+ }
+
+ foreach (VarDeclaration v; er.byvalue)
+ {
+ if (log) printf("byvalue %s\n", v.toChars());
+ if (v.isDataseg())
+ continue;
+
+ Dsymbol p = v.toParent2();
+
+ notMaybeScope(v);
+
+ if (v.isScope())
+ {
+ unsafeAssign(v, "scope variable");
+ }
+ else if (v.storage_class & STC.variadic && p == sc.func)
+ {
+ Type tb = v.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ unsafeAssign(v, "variadic variable");
+ }
+ }
+ else
+ {
+ /* v is not 'scope', and is assigned to a parameter that may escape.
+ * Therefore, v can never be 'scope'.
+ */
+ if (log) printf("no infer for %s in %s loc %s, fdc %s, %d\n",
+ v.toChars(), sc.func.ident.toChars(), sc.func.loc.toChars(), fdc.ident.toChars(), __LINE__);
+ v.doNotInferScope = true;
+ }
+ }
+
+ foreach (VarDeclaration v; er.byref)
+ {
+ if (log) printf("byref %s\n", v.toChars());
+ if (v.isDataseg())
+ continue;
+
+ Dsymbol p = v.toParent2();
+
+ notMaybeScope(v);
+
+ if (!v.isReference() && p == sc.func)
+ {
+ if (psr == ScopeRef.Scope ||
+ psr == ScopeRef.RefScope ||
+ psr == ScopeRef.ReturnRef_Scope)
+ {
+ continue;
+ }
+
+ unsafeAssign(v, "reference to local variable");
+ continue;
+ }
+ }
+
+ foreach (FuncDeclaration fd; er.byfunc)
+ {
+ //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf);
+ VarDeclarations vars;
+ findAllOuterAccessedVariables(fd, &vars);
+
+ foreach (v; vars)
+ {
+ //printf("v = %s\n", v.toChars());
+ assert(!v.isDataseg()); // these are not put in the closureVars[]
+
+ Dsymbol p = v.toParent2();
+
+ notMaybeScope(v);
+
+ if ((v.isReference() || v.isScope()) && p == sc.func)
+ {
+ unsafeAssign(v, "reference to local");
+ continue;
+ }
+ }
+ }
+
+ foreach (Expression ee; er.byexp)
+ {
+ if (sc.func && sc.func.setUnsafe())
+ {
+ if (!gag)
+ error(ee.loc, "reference to stack allocated value returned by `%s` assigned to non-scope parameter `%s`",
+ ee.toChars(),
+ par ? par.toChars() : "this");
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+/*****************************************************
+ * Function argument initializes a `return` parameter,
+ * and that parameter gets assigned to `firstArg`.
+ * Essentially, treat as `firstArg = arg;`
+ * Params:
+ * sc = used to determine current function and module
+ * firstArg = `ref` argument through which `arg` may be assigned
+ * arg = initializer for parameter
+ * gag = do not print error messages
+ * Returns:
+ * `true` if assignment to `firstArg` would cause an error
+ */
+bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, bool gag)
+{
+ enum log = false;
+ if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n",
+ firstArg.toChars(), arg.toChars());
+ //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
+
+ if (!arg.type.hasPointers())
+ return false;
+
+ scope e = new AssignExp(arg.loc, firstArg, arg);
+ return checkAssignEscape(sc, e, gag);
+}
+
+/*****************************************************
+ * Check struct constructor of the form `s.this(args)`, by
+ * checking each `return` parameter to see if it gets
+ * assigned to `s`.
+ * Params:
+ * sc = used to determine current function and module
+ * ce = constructor call of the form `s.this(args)`
+ * gag = do not print error messages
+ * Returns:
+ * `true` if construction would cause an escaping reference error
+ */
+bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag)
+{
+ enum log = false;
+ if (log) printf("checkConstructorEscape(%s, %s)\n", ce.toChars(), ce.type.toChars());
+ Type tthis = ce.type.toBasetype();
+ assert(tthis.ty == Tstruct);
+ if (!tthis.hasPointers())
+ return false;
+
+ if (!ce.arguments && ce.arguments.dim)
+ return false;
+
+ DotVarExp dve = ce.e1.isDotVarExp();
+ CtorDeclaration ctor = dve.var.isCtorDeclaration();
+ TypeFunction tf = ctor.type.isTypeFunction();
+
+ const nparams = tf.parameterList.length;
+ const n = ce.arguments.dim;
+
+ // j=1 if _arguments[] is first argument
+ const j = tf.isDstyleVariadic();
+
+ /* Attempt to assign each `return` arg to the `this` reference
+ */
+ foreach (const i; 0 .. n)
+ {
+ Expression arg = (*ce.arguments)[i];
+ if (!arg.type.hasPointers())
+ return false;
+
+ //printf("\targ[%d]: %s\n", i, arg.toChars());
+
+ if (i - j < nparams && i >= j)
+ {
+ Parameter p = tf.parameterList[i - j];
+
+ if (p.storageClass & STC.return_)
+ {
+ /* Fake `dve.e1 = arg;` and look for scope violations
+ */
+ scope e = new AssignExp(arg.loc, dve.e1, arg);
+ if (checkAssignEscape(sc, e, gag))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/****************************************
+ * Given an `AssignExp`, determine if the lvalue will cause
+ * the contents of the rvalue to escape.
+ * Print error messages when these are detected.
+ * Infer `scope` attribute for the lvalue where possible, in order
+ * to eliminate the error.
+ * Params:
+ * sc = used to determine current function and module
+ * e = `AssignExp` or `CatAssignExp` to check for any pointers to the stack
+ * gag = do not print error messages
+ * Returns:
+ * `true` if pointers to the stack can escape via assignment
+ */
+bool checkAssignEscape(Scope* sc, Expression e, bool gag)
+{
+ enum log = false;
+ if (log) printf("checkAssignEscape(e: %s)\n", e.toChars());
+ if (e.op != TOK.assign && e.op != TOK.blit && e.op != TOK.construct &&
+ e.op != TOK.concatenateAssign && e.op != TOK.concatenateElemAssign && e.op != TOK.concatenateDcharAssign)
+ return false;
+ auto ae = cast(BinExp)e;
+ Expression e1 = ae.e1;
+ Expression e2 = ae.e2;
+ //printf("type = %s, %d\n", e1.type.toChars(), e1.type.hasPointers());
+
+ if (!e1.type.hasPointers())
+ return false;
+
+ if (e1.isSliceExp())
+ return false;
+
+ /* The struct literal case can arise from the S(e2) constructor call:
+ * return S(e2);
+ * and appears in this function as:
+ * structLiteral = e2;
+ * Such an assignment does not necessarily remove scope-ness.
+ */
+ if (e1.isStructLiteralExp())
+ return false;
+
+ EscapeByResults er;
+
+ escapeByValue(e2, &er);
+
+ if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
+ return false;
+
+ VarDeclaration va = expToVariable(e1);
+
+ if (va && e.op == TOK.concatenateElemAssign)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=17842
+ * Draw an equivalence between:
+ * *q = p;
+ * and:
+ * va ~= e;
+ * since we are not assigning to va, but are assigning indirectly through va.
+ */
+ va = null;
+ }
+
+ if (va && e1.isDotVarExp() && va.type.toBasetype().isTypeClass())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=17949
+ * Draw an equivalence between:
+ * *q = p;
+ * and:
+ * va.field = e2;
+ * since we are not assigning to va, but are assigning indirectly through class reference va.
+ */
+ va = null;
+ }
+
+ if (log && va) printf("va: %s\n", va.toChars());
+
+ FuncDeclaration fd = sc.func;
+
+ // Try to infer 'scope' for va if in a function not marked @system
+ bool inferScope = false;
+ if (va && fd && fd.type && fd.type.isTypeFunction())
+ inferScope = fd.type.isTypeFunction().trust != TRUST.system;
+ //printf("inferScope = %d, %d\n", inferScope, (va.storage_class & STCmaybescope) != 0);
+
+ // Determine if va is a parameter that is an indirect reference
+ const bool vaIsRef = va && va.storage_class & STC.parameter &&
+ (va.isReference() || va.type.toBasetype().isTypeClass()); // ref, out, or class
+ if (log && vaIsRef) printf("va is ref `%s`\n", va.toChars());
+
+ /* Determine if va is the first parameter, through which other 'return' parameters
+ * can be assigned.
+ * This works the same as returning the value via a return statement.
+ * Although va is marked as `ref`, it is not regarded as returning by `ref`.
+ * https://dlang.org.spec/function.html#return-ref-parameters
+ */
+ bool isFirstRef()
+ {
+ if (!vaIsRef)
+ return false;
+ Dsymbol p = va.toParent2();
+ if (p == fd && fd.type && fd.type.isTypeFunction())
+ {
+ TypeFunction tf = fd.type.isTypeFunction();
+ if (!tf.nextOf() || (tf.nextOf().ty != Tvoid && !fd.isCtorDeclaration()))
+ return false;
+ if (va == fd.vthis) // `this` of a non-static member function is considered to be the first parameter
+ return true;
+ if (fd.parameters && fd.parameters.length && (*fd.parameters)[0] == va) // va is first parameter
+ return true;
+ }
+ return false;
+ }
+ const bool vaIsFirstRef = isFirstRef();
+ if (log && vaIsFirstRef) printf("va is first ref `%s`\n", va.toChars());
+
+ bool result = false;
+ foreach (VarDeclaration v; er.byvalue)
+ {
+ if (log) printf("byvalue: %s\n", v.toChars());
+ if (v.isDataseg())
+ continue;
+
+ if (v == va)
+ continue;
+
+ Dsymbol p = v.toParent2();
+
+ if (va && !vaIsRef && !va.isScope() && !v.isScope() &&
+ (va.storage_class & v.storage_class & (STC.maybescope | STC.variadic)) == STC.maybescope &&
+ p == fd)
+ {
+ /* Add v to va's list of dependencies
+ */
+ va.addMaybe(v);
+ continue;
+ }
+
+ if (vaIsFirstRef &&
+ (v.isScope() || (v.storage_class & STC.maybescope)) &&
+ !(v.storage_class & STC.return_) &&
+ v.isParameter() &&
+ fd.flags & FUNCFLAG.returnInprocess &&
+ p == fd)
+ {
+ if (log) printf("inferring 'return' for parameter %s in function %s\n", v.toChars(), fd.toChars());
+ inferReturn(fd, v); // infer addition of 'return' to make `return scope`
+ }
+
+ if (!(va && va.isScope()) || vaIsRef)
+ notMaybeScope(v);
+
+ if (v.isScope())
+ {
+ if (vaIsFirstRef && v.isParameter() && v.storage_class & STC.return_)
+ {
+ // va=v, where v is `return scope`
+ if (va.isScope())
+ continue;
+
+ if (inferScope && !va.doNotInferScope)
+ {
+ if (log) printf("inferring scope for lvalue %s\n", va.toChars());
+ va.storage_class |= STC.scope_ | STC.scopeinferred;
+ continue;
+ }
+ }
+
+ if (va && va.isScope() && va.storage_class & STC.return_ && !(v.storage_class & STC.return_) &&
+ fd.setUnsafe())
+ {
+ // va may return its value, but v does not allow that, so this is an error
+ if (!gag)
+ error(ae.loc, "scope variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars());
+ result = true;
+ continue;
+ }
+
+ // If va's lifetime encloses v's, then error
+ if (va &&
+ (va.enclosesLifetimeOf(v) && !(v.storage_class & (STC.parameter | STC.temp)) ||
+ // va is class reference
+ ae.e1.isDotVarExp() && va.type.toBasetype().isTypeClass() && (va.enclosesLifetimeOf(v) ||
+ !va.isScope()) ||
+ vaIsRef ||
+ va.isReference() && !(v.storage_class & (STC.parameter | STC.temp))) &&
+ fd.setUnsafe())
+ {
+ if (!gag)
+ error(ae.loc, "scope variable `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars());
+ result = true;
+ continue;
+ }
+
+ if (va && !va.isDataseg() && !va.doNotInferScope)
+ {
+ if (!va.isScope() && inferScope)
+ { /* v is scope, and va is not scope, so va needs to
+ * infer scope
+ */
+ if (log) printf("inferring scope for %s\n", va.toChars());
+ va.storage_class |= STC.scope_ | STC.scopeinferred;
+ /* v returns, and va does not return, so va needs
+ * to infer return
+ */
+ if (v.storage_class & STC.return_ &&
+ !(va.storage_class & STC.return_))
+ {
+ if (log) printf("infer return for %s\n", va.toChars());
+ va.storage_class |= STC.return_ | STC.returninferred;
+
+ // Added "return scope" so don't confuse it with "return ref"
+ if (isRefReturnScope(va.storage_class))
+ va.storage_class |= STC.returnScope;
+ }
+ }
+ continue;
+ }
+ if (fd.setUnsafe())
+ {
+ if (!gag)
+ error(ae.loc, "scope variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars());
+ result = true;
+ }
+ }
+ else if (v.storage_class & STC.variadic && p == fd)
+ {
+ Type tb = v.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (va && !va.isDataseg() && !va.doNotInferScope)
+ {
+ if (!va.isScope() && inferScope)
+ { //printf("inferring scope for %s\n", va.toChars());
+ va.storage_class |= STC.scope_ | STC.scopeinferred;
+ }
+ continue;
+ }
+ if (fd.setUnsafe())
+ {
+ if (!gag)
+ error(ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars());
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ /* v is not 'scope', and we didn't check the scope of where we assigned it to.
+ * It may escape via that assignment, therefore, v can never be 'scope'.
+ */
+ //printf("no infer for %s in %s, %d\n", v.toChars(), fd.ident.toChars(), __LINE__);
+ v.doNotInferScope = true;
+ }
+ }
+
+ByRef:
+ foreach (VarDeclaration v; er.byref)
+ {
+ if (log) printf("byref: %s\n", v.toChars());
+ if (v.isDataseg())
+ continue;
+
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (va && va.isScope() && !v.isReference())
+ {
+ if (!(va.storage_class & STC.return_))
+ {
+ va.doNotInferReturn = true;
+ }
+ else if (fd.setUnsafe())
+ {
+ if (!gag)
+ error(ae.loc, "address of local variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars());
+ result = true;
+ continue;
+ }
+ }
+ }
+
+ Dsymbol p = v.toParent2();
+
+ // If va's lifetime encloses v's, then error
+ if (va &&
+ (va.enclosesLifetimeOf(v) && !(v.isParameter() && v.isRef()) ||
+ va.isDataseg()) &&
+ fd.setUnsafe())
+ {
+ if (!gag)
+ error(ae.loc, "address of variable `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars());
+ result = true;
+ continue;
+ }
+
+ if (va && v.isReference())
+ {
+ Dsymbol pva = va.toParent2();
+ for (Dsymbol pv = p; pv; )
+ {
+ pv = pv.toParent2();
+ if (pva == pv) // if v is nested inside pva
+ {
+ if (fd.setUnsafe())
+ {
+ if (!gag)
+ error(ae.loc, "reference `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars());
+ result = true;
+ continue ByRef;
+ }
+ break;
+ }
+ }
+ }
+
+ if (!(va && va.isScope()))
+ notMaybeScope(v);
+
+ if ((global.params.useDIP1000 != FeatureState.enabled && v.isReference()) || p != sc.func)
+ continue;
+
+ if (va && !va.isDataseg() && !va.doNotInferScope)
+ {
+ if (!va.isScope() && inferScope)
+ { //printf("inferring scope for %s\n", va.toChars());
+ va.storage_class |= STC.scope_ | STC.scopeinferred;
+ }
+ if (v.storage_class & STC.return_ && !(va.storage_class & STC.return_))
+ va.storage_class |= STC.return_ | STC.returninferred;
+ continue;
+ }
+ if (e1.op == TOK.structLiteral)
+ continue;
+ if (fd.setUnsafe())
+ {
+ if (!gag)
+ error(ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars());
+ result = true;
+ }
+ }
+
+ foreach (FuncDeclaration func; er.byfunc)
+ {
+ if (log) printf("byfunc: %s, %d\n", func.toChars(), func.tookAddressOf);
+ VarDeclarations vars;
+ findAllOuterAccessedVariables(func, &vars);
+
+ /* https://issues.dlang.org/show_bug.cgi?id=16037
+ * If assigning the address of a delegate to a scope variable,
+ * then uncount that address of. This is so it won't cause a
+ * closure to be allocated.
+ */
+ if (va && va.isScope() && !(va.storage_class & STC.return_) && func.tookAddressOf)
+ --func.tookAddressOf;
+
+ foreach (v; vars)
+ {
+ //printf("v = %s\n", v.toChars());
+ assert(!v.isDataseg()); // these are not put in the closureVars[]
+
+ Dsymbol p = v.toParent2();
+
+ if (!(va && va.isScope()))
+ notMaybeScope(v);
+
+ if (!(v.isReference() || v.isScope()) || p != fd)
+ continue;
+
+ if (va && !va.isDataseg() && !va.doNotInferScope)
+ {
+ /* Don't infer STC.scope_ for va, because then a closure
+ * won't be generated for fd.
+ */
+ //if (!va.isScope() && inferScope)
+ //va.storage_class |= STC.scope_ | STC.scopeinferred;
+ continue;
+ }
+ if (fd.setUnsafe())
+ {
+ if (!gag)
+ error(ae.loc, "reference to local `%s` assigned to non-scope `%s` in @safe code", v.toChars(), e1.toChars());
+ result = true;
+ }
+ }
+ }
+
+ foreach (Expression ee; er.byexp)
+ {
+ if (log) printf("byexp: %s\n", ee.toChars());
+
+ /* Do not allow slicing of a static array returned by a function
+ */
+ if (ee.op == TOK.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray() &&
+ !(va && va.storage_class & STC.temp))
+ {
+ if (!gag)
+ deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`",
+ ee.toChars(), e1.toChars());
+ //result = true;
+ continue;
+ }
+
+ if (ee.op == TOK.call && ee.type.toBasetype().isTypeStruct() &&
+ (!va || !(va.storage_class & STC.temp)) &&
+ fd.setUnsafe())
+ {
+ if (!gag)
+ error(ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`",
+ ee.toChars(), e1.toChars());
+ result = true;
+ continue;
+ }
+
+ if (ee.op == TOK.structLiteral &&
+ (!va || !(va.storage_class & STC.temp)) &&
+ fd.setUnsafe())
+ {
+ if (!gag)
+ error(ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`",
+ ee.toChars(), e1.toChars());
+ result = true;
+ continue;
+ }
+
+ if (va && !va.isDataseg() && !va.doNotInferScope)
+ {
+ if (!va.isScope() && inferScope)
+ { //printf("inferring scope for %s\n", va.toChars());
+ va.storage_class |= STC.scope_ | STC.scopeinferred;
+ }
+ continue;
+ }
+
+ if (fd.setUnsafe())
+ {
+ if (!gag)
+ error(ee.loc, "reference to stack allocated value returned by `%s` assigned to non-scope `%s`",
+ ee.toChars(), e1.toChars());
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+/************************************
+ * Detect cases where pointers to the stack can escape the
+ * lifetime of the stack frame when throwing `e`.
+ * Print error messages when these are detected.
+ * Params:
+ * sc = used to determine current function and module
+ * e = expression to check for any pointers to the stack
+ * gag = do not print error messages
+ * Returns:
+ * `true` if pointers to the stack can escape
+ */
+bool checkThrowEscape(Scope* sc, Expression e, bool gag)
+{
+ //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars());
+ EscapeByResults er;
+
+ escapeByValue(e, &er);
+
+ if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
+ return false;
+
+ bool result = false;
+ foreach (VarDeclaration v; er.byvalue)
+ {
+ //printf("byvalue %s\n", v.toChars());
+ if (v.isDataseg())
+ continue;
+
+ if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown
+ // despite being `scope`
+ {
+ if (sc._module && sc._module.isRoot())
+ {
+ // Only look for errors if in module listed on command line
+ if (global.params.useDIP1000 == FeatureState.enabled) // https://issues.dlang.org/show_bug.cgi?id=17029
+ {
+ if (!gag)
+ error(e.loc, "scope variable `%s` may not be thrown", v.toChars());
+ result = true;
+ }
+ continue;
+ }
+ }
+ else
+ {
+ //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
+ v.doNotInferScope = true;
+ }
+ }
+ return result;
+}
+
+/************************************
+ * Detect cases where pointers to the stack can escape the
+ * lifetime of the stack frame by being placed into a GC allocated object.
+ * Print error messages when these are detected.
+ * Params:
+ * sc = used to determine current function and module
+ * e = expression to check for any pointers to the stack
+ * gag = do not print error messages
+ * Returns:
+ * `true` if pointers to the stack can escape
+ */
+bool checkNewEscape(Scope* sc, Expression e, bool gag)
+{
+ import dmd.globals: FeatureState;
+ import dmd.errors: previewErrorFunc;
+
+ //printf("[%s] checkNewEscape, e = %s\n", e.loc.toChars(), e.toChars());
+ enum log = false;
+ if (log) printf("[%s] checkNewEscape, e: `%s`\n", e.loc.toChars(), e.toChars());
+ EscapeByResults er;
+
+ escapeByValue(e, &er);
+
+ if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
+ return false;
+
+ bool result = false;
+ foreach (VarDeclaration v; er.byvalue)
+ {
+ if (log) printf("byvalue `%s`\n", v.toChars());
+ if (v.isDataseg())
+ continue;
+
+ Dsymbol p = v.toParent2();
+
+ if (v.isScope())
+ {
+ if (sc._module && sc._module.isRoot() &&
+ /* This case comes up when the ReturnStatement of a __foreachbody is
+ * checked for escapes by the caller of __foreachbody. Skip it.
+ *
+ * struct S { static int opApply(int delegate(S*) dg); }
+ * S* foo() {
+ * foreach (S* s; S) // create __foreachbody for body of foreach
+ * return s; // s is inferred as 'scope' but incorrectly tested in foo()
+ * return null; }
+ */
+ !(p.parent == sc.func))
+ {
+ // Only look for errors if in module listed on command line
+ if (global.params.useDIP1000 == FeatureState.enabled // https://issues.dlang.org/show_bug.cgi?id=17029
+ && sc.func.setUnsafe()) // https://issues.dlang.org/show_bug.cgi?id=20868
+ {
+ if (!gag)
+ error(e.loc, "scope variable `%s` may not be copied into allocated memory", v.toChars());
+ result = true;
+ }
+ continue;
+ }
+ }
+ else if (v.storage_class & STC.variadic && p == sc.func)
+ {
+ Type tb = v.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!gag)
+ error(e.loc, "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
+ result = false;
+ }
+ }
+ else
+ {
+ //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
+ v.doNotInferScope = true;
+ }
+ }
+
+ foreach (VarDeclaration v; er.byref)
+ {
+ if (log) printf("byref `%s`\n", v.toChars());
+
+ // 'featureState' tells us whether to emit an error or a deprecation,
+ // depending on the flag passed to the CLI for DIP25
+ void escapingRef(VarDeclaration v, FeatureState featureState = FeatureState.enabled)
+ {
+ if (!gag)
+ {
+ const(char)* kind = (v.storage_class & STC.parameter) ? "parameter" : "local";
+ const(char)* msg = "copying `%s` into allocated memory escapes a reference to %s variable `%s`";
+ previewErrorFunc(sc.isDeprecated(), featureState)(e.loc, msg, e.toChars(), kind, v.toChars());
+ }
+ result |= (featureState == FeatureState.enabled);
+ }
+
+ if (v.isDataseg())
+ continue;
+
+ Dsymbol p = v.toParent2();
+
+ if (!v.isReference())
+ {
+ if (p == sc.func)
+ {
+ escapingRef(v);
+ continue;
+ }
+ }
+
+ /* Check for returning a ref variable by 'ref', but should be 'return ref'
+ * Infer the addition of 'return', or set result to be the offending expression.
+ */
+ if (!v.isReference())
+ continue;
+
+ if (!sc._module || !sc._module.isRoot())
+ continue;
+
+ // https://dlang.org/spec/function.html#return-ref-parameters
+ // Only look for errors if in module listed on command line
+ if (p == sc.func)
+ {
+ //printf("escaping reference to local ref variable %s\n", v.toChars());
+ //printf("storage class = x%llx\n", v.storage_class);
+ escapingRef(v, global.params.useDIP25);
+ continue;
+ }
+ // Don't need to be concerned if v's parent does not return a ref
+ FuncDeclaration func = p.isFuncDeclaration();
+ if (!func || !func.type)
+ continue;
+ if (auto tf = func.type.isTypeFunction())
+ {
+ if (!tf.isref)
+ continue;
+
+ const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape";
+ if (!gag)
+ {
+ previewErrorFunc(sc.isDeprecated(), global.params.useDIP25)(e.loc, msg, v.toChars());
+ }
+
+ // If -preview=dip25 is used, the user wants an error
+ // Otherwise, issue a deprecation
+ result |= (global.params.useDIP25 == FeatureState.enabled);
+ }
+ }
+
+ foreach (Expression ee; er.byexp)
+ {
+ if (log) printf("byexp %s\n", ee.toChars());
+ if (!gag)
+ error(ee.loc, "storing reference to stack allocated value returned by `%s` into allocated memory causes it to escape",
+ ee.toChars());
+ result = true;
+ }
+
+ return result;
+}
+
+
+/************************************
+ * Detect cases where pointers to the stack can escape the
+ * lifetime of the stack frame by returning `e` by value.
+ * Print error messages when these are detected.
+ * Params:
+ * sc = used to determine current function and module
+ * e = expression to check for any pointers to the stack
+ * gag = do not print error messages
+ * Returns:
+ * `true` if pointers to the stack can escape
+ */
+bool checkReturnEscape(Scope* sc, Expression e, bool gag)
+{
+ //printf("[%s] checkReturnEscape, e: %s\n", e.loc.toChars(), e.toChars());
+ return checkReturnEscapeImpl(sc, e, false, gag);
+}
+
+/************************************
+ * Detect cases where returning `e` by `ref` can result in a reference to the stack
+ * being returned.
+ * Print error messages when these are detected.
+ * Params:
+ * sc = used to determine current function and module
+ * e = expression to check
+ * gag = do not print error messages
+ * Returns:
+ * `true` if references to the stack can escape
+ */
+bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag)
+{
+ version (none)
+ {
+ printf("[%s] checkReturnEscapeRef, e = %s\n", e.loc.toChars(), e.toChars());
+ printf("current function %s\n", sc.func.toChars());
+ printf("parent2 function %s\n", sc.func.toParent2().toChars());
+ }
+
+ return checkReturnEscapeImpl(sc, e, true, gag);
+}
+
+/***************************************
+ * Implementation of checking for escapes in return expressions.
+ * Params:
+ * sc = used to determine current function and module
+ * e = expression to check
+ * refs = `true`: escape by value, `false`: escape by `ref`
+ * gag = do not print error messages
+ * Returns:
+ * `true` if references to the stack can escape
+ */
+private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag)
+{
+ enum log = false;
+ if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars());
+ EscapeByResults er;
+
+ if (refs)
+ escapeByRef(e, &er);
+ else
+ escapeByValue(e, &er);
+
+ if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
+ return false;
+
+ bool result = false;
+ foreach (VarDeclaration v; er.byvalue)
+ {
+ if (log) printf("byvalue `%s`\n", v.toChars());
+ if (v.isDataseg())
+ continue;
+
+ Dsymbol p = v.toParent2();
+
+ if ((v.isScope() || (v.storage_class & STC.maybescope)) &&
+ !(v.storage_class & STC.return_) &&
+ v.isParameter() &&
+ !v.doNotInferReturn &&
+ sc.func.flags & FUNCFLAG.returnInprocess &&
+ p == sc.func)
+ {
+ inferReturn(sc.func, v); // infer addition of 'return'
+ continue;
+ }
+
+ if (v.isScope())
+ {
+ if (v.storage_class & STC.return_)
+ continue;
+
+ auto pfunc = p.isFuncDeclaration();
+ if (pfunc && sc._module && sc._module.isRoot() &&
+ /* This case comes up when the ReturnStatement of a __foreachbody is
+ * checked for escapes by the caller of __foreachbody. Skip it.
+ *
+ * struct S { static int opApply(int delegate(S*) dg); }
+ * S* foo() {
+ * foreach (S* s; S) // create __foreachbody for body of foreach
+ * return s; // s is inferred as 'scope' but incorrectly tested in foo()
+ * return null; }
+ */
+ !(!refs && p.parent == sc.func && pfunc.fes) &&
+ /*
+ * auto p(scope string s) {
+ * string scfunc() { return s; }
+ * }
+ */
+ !(!refs && sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0)
+ )
+ {
+ // Only look for errors if in module listed on command line
+ if (global.params.useDIP1000 == FeatureState.enabled) // https://issues.dlang.org/show_bug.cgi?id=17029
+ {
+ if (!gag)
+ error(e.loc, "scope variable `%s` may not be returned", v.toChars());
+ result = true;
+ }
+ continue;
+ }
+ }
+ else if (v.storage_class & STC.variadic && p == sc.func)
+ {
+ Type tb = v.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!gag)
+ error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
+ result = false;
+ }
+ }
+ else
+ {
+ //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
+ v.doNotInferScope = true;
+ }
+ }
+
+ foreach (VarDeclaration v; er.byref)
+ {
+ if (log)
+ {
+ printf("byref `%s`\n", v.toChars());
+ if (v.storage_class & STC.return_) printf(" return");
+ if (v.storage_class & STC.ref_) printf(" ref");
+ if (v.storage_class & STC.scope_) printf(" scope");
+ printf("\n");
+ }
+
+ // 'featureState' tells us whether to emit an error or a deprecation,
+ // depending on the flag passed to the CLI for DIP25
+ void escapingRef(VarDeclaration v, ScopeRef vsr, FeatureState featureState = FeatureState.enabled)
+ {
+ if (!gag)
+ {
+ const(char)* msg, supplemental;
+ if (v.storage_class & STC.parameter &&
+ (v.type.hasPointers() || v.storage_class & STC.ref_))
+ {
+ msg = "returning `%s` escapes a reference to parameter `%s`";
+ supplemental = vsr == ScopeRef.Ref_ReturnScope
+ ? "perhaps remove `scope` parameter annotation so `return` applies to `ref`"
+ : "perhaps annotate the parameter with `return`";
+ }
+ else
+ {
+ msg = "returning `%s` escapes a reference to local variable `%s`";
+ if (v.ident is Id.This)
+ supplemental = "perhaps annotate the function with `return`";
+ }
+
+ previewErrorFunc(sc.isDeprecated(), featureState)(e.loc, msg, e.toChars(), v.toChars());
+ if (supplemental)
+ previewSupplementalFunc(sc.isDeprecated(), featureState)(e.loc, supplemental);
+ }
+ result = true;
+ }
+
+ if (v.isDataseg())
+ continue;
+
+ const vsr = buildScopeRef(v.storage_class);
+
+ Dsymbol p = v.toParent2();
+
+ // https://issues.dlang.org/show_bug.cgi?id=19965
+ if (!refs && sc.func.vthis == v)
+ notMaybeScope(v);
+
+ if (!v.isReference())
+ {
+ if (p == sc.func)
+ {
+ escapingRef(v, vsr, FeatureState.enabled);
+ continue;
+ }
+ FuncDeclaration fd = p.isFuncDeclaration();
+ if (fd && sc.func.flags & FUNCFLAG.returnInprocess)
+ {
+ /* Code like:
+ * int x;
+ * auto dg = () { return &x; }
+ * Making it:
+ * auto dg = () return { return &x; }
+ * Because dg.ptr points to x, this is returning dt.ptr+offset
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ sc.func.storage_class |= STC.return_ | STC.returninferred;
+ }
+ }
+ }
+
+ /* Check for returning a ref variable by 'ref', but should be 'return ref'
+ * Infer the addition of 'return', or set result to be the offending expression.
+ */
+ if ((vsr == ScopeRef.Ref ||
+ vsr == ScopeRef.RefScope ||
+ vsr == ScopeRef.Ref_ReturnScope) &&
+ !(v.storage_class & STC.foreach_))
+ {
+ if (sc.func.flags & FUNCFLAG.returnInprocess && p == sc.func &&
+ (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope))
+ {
+ inferReturn(sc.func, v); // infer addition of 'return'
+ }
+ else if (sc._module && sc._module.isRoot())
+ {
+ // https://dlang.org/spec/function.html#return-ref-parameters
+ // Only look for errors if in module listed on command line
+ if (p == sc.func)
+ {
+ //printf("escaping reference to local ref variable %s\n", v.toChars());
+ //printf("storage class = x%llx\n", v.storage_class);
+ escapingRef(v, vsr, global.params.useDIP25);
+ continue;
+ }
+ // Don't need to be concerned if v's parent does not return a ref
+ FuncDeclaration fd = p.isFuncDeclaration();
+ if (fd && fd.type && fd.type.ty == Tfunction)
+ {
+ TypeFunction tf = fd.type.isTypeFunction();
+ if (tf.isref)
+ {
+ const(char)* msg = "escaping reference to outer local variable `%s`";
+ if (!gag)
+ previewErrorFunc(sc.isDeprecated(), global.params.useDIP25)(e.loc, msg, v.toChars());
+ result = true;
+ continue;
+ }
+ }
+
+ }
+ }
+ }
+
+ foreach (Expression ee; er.byexp)
+ {
+ if (log) printf("byexp %s\n", ee.toChars());
+ if (!gag)
+ error(ee.loc, "escaping reference to stack allocated value returned by `%s`", ee.toChars());
+ result = true;
+ }
+
+ return result;
+}
+
+
+/*************************************
+ * Variable v needs to have 'return' inferred for it.
+ * Params:
+ * fd = function that v is a parameter to
+ * v = parameter that needs to be STC.return_
+ */
+
+private void inferReturn(FuncDeclaration fd, VarDeclaration v)
+{
+ // v is a local in the current function
+
+ //printf("for function '%s' inferring 'return' for variable '%s'\n", fd.toChars(), v.toChars());
+ v.storage_class |= STC.return_ | STC.returninferred;
+
+ if (v == fd.vthis)
+ {
+ /* v is the 'this' reference, so mark the function
+ */
+ fd.storage_class |= STC.return_ | STC.returninferred;
+ if (auto tf = fd.type.isTypeFunction())
+ {
+ //printf("'this' too %p %s\n", tf, sc.func.toChars());
+ tf.isreturn = true;
+ tf.isreturninferred = true;
+ }
+ }
+ else
+ {
+ // Perform 'return' inference on parameter
+ if (auto tf = fd.type.isTypeFunction())
+ {
+ foreach (i, p; tf.parameterList)
+ {
+ if (p.ident == v.ident)
+ {
+ p.storageClass |= STC.return_ | STC.returninferred;
+ break; // there can be only one
+ }
+ }
+ }
+ }
+}
+
+
+/****************************************
+ * e is an expression to be returned by value, and that value contains pointers.
+ * Walk e to determine which variables are possibly being
+ * returned by value, such as:
+ * int* function(int* p) { return p; }
+ * If e is a form of &p, determine which variables have content
+ * which is being returned as ref, such as:
+ * int* function(int i) { return &i; }
+ * Multiple variables can be inserted, because of expressions like this:
+ * int function(bool b, int i, int* p) { return b ? &i : p; }
+ *
+ * No side effects.
+ *
+ * Params:
+ * e = expression to be returned by value
+ * er = where to place collected data
+ * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
+ */
+void escapeByValue(Expression e, EscapeByResults* er, bool live = false)
+{
+ //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars());
+ extern (C++) final class EscapeVisitor : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ EscapeByResults* er;
+ bool live;
+
+ extern (D) this(EscapeByResults* er, bool live)
+ {
+ this.er = er;
+ this.live = live;
+ }
+
+ override void visit(Expression e)
+ {
+ }
+
+ override void visit(AddrExp e)
+ {
+ /* Taking the address of struct literal is normally not
+ * allowed, but CTFE can generate one out of a new expression,
+ * but it'll be placed in static data so no need to check it.
+ */
+ if (e.e1.op != TOK.structLiteral)
+ escapeByRef(e.e1, er, live);
+ }
+
+ override void visit(SymOffExp e)
+ {
+ VarDeclaration v = e.var.isVarDeclaration();
+ if (v)
+ er.byref.push(v);
+ }
+
+ override void visit(VarExp e)
+ {
+ if (auto v = e.var.isVarDeclaration())
+ {
+ if (v.type.hasPointers() || // not tracking non-pointers
+ v.storage_class & STC.lazy_) // lazy variables are actually pointers
+ er.byvalue.push(v);
+ }
+ }
+
+ override void visit(ThisExp e)
+ {
+ if (e.var)
+ er.byvalue.push(e.var);
+ }
+
+ override void visit(PtrExp e)
+ {
+ if (live && e.type.hasPointers())
+ e.e1.accept(this);
+ }
+
+ override void visit(DotVarExp e)
+ {
+ auto t = e.e1.type.toBasetype();
+ if (e.type.hasPointers() && (live || t.ty == Tstruct))
+ {
+ e.e1.accept(this);
+ }
+ }
+
+ override void visit(DelegateExp e)
+ {
+ Type t = e.e1.type.toBasetype();
+ if (t.ty == Tclass || t.ty == Tpointer)
+ escapeByValue(e.e1, er, live);
+ else
+ escapeByRef(e.e1, er, live);
+ er.byfunc.push(e.func);
+ }
+
+ override void visit(FuncExp e)
+ {
+ if (e.fd.tok == TOK.delegate_)
+ er.byfunc.push(e.fd);
+ }
+
+ override void visit(TupleExp e)
+ {
+ assert(0); // should have been lowered by now
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ Type tb = e.type.toBasetype();
+ if (tb.ty == Tsarray || tb.ty == Tarray)
+ {
+ if (e.basis)
+ e.basis.accept(this);
+ foreach (el; *e.elements)
+ {
+ if (el)
+ el.accept(this);
+ }
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ if (e.elements)
+ {
+ foreach (ex; *e.elements)
+ {
+ if (ex)
+ ex.accept(this);
+ }
+ }
+ }
+
+ override void visit(NewExp e)
+ {
+ Type tb = e.newtype.toBasetype();
+ if (tb.ty == Tstruct && !e.member && e.arguments)
+ {
+ foreach (ex; *e.arguments)
+ {
+ if (ex)
+ ex.accept(this);
+ }
+ }
+ }
+
+ override void visit(CastExp e)
+ {
+ if (!e.type.hasPointers())
+ return;
+ Type tb = e.type.toBasetype();
+ if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray)
+ {
+ escapeByRef(e.e1, er, live);
+ }
+ else
+ e.e1.accept(this);
+ }
+
+ override void visit(SliceExp e)
+ {
+ if (auto ve = e.e1.isVarExp())
+ {
+ VarDeclaration v = ve.var.isVarDeclaration();
+ Type tb = e.type.toBasetype();
+ if (v)
+ {
+ if (tb.ty == Tsarray)
+ return;
+ if (v.storage_class & STC.variadic)
+ {
+ er.byvalue.push(v);
+ return;
+ }
+ }
+ }
+ Type t1b = e.e1.type.toBasetype();
+ if (t1b.ty == Tsarray)
+ {
+ Type tb = e.type.toBasetype();
+ if (tb.ty != Tsarray)
+ escapeByRef(e.e1, er, live);
+ }
+ else
+ e.e1.accept(this);
+ }
+
+ override void visit(IndexExp e)
+ {
+ if (e.e1.type.toBasetype().ty == Tsarray ||
+ live && e.type.hasPointers())
+ {
+ e.e1.accept(this);
+ }
+ }
+
+ override void visit(BinExp e)
+ {
+ Type tb = e.type.toBasetype();
+ if (tb.ty == Tpointer)
+ {
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+ }
+
+ override void visit(BinAssignExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(AssignExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(CommaExp e)
+ {
+ e.e2.accept(this);
+ }
+
+ override void visit(CondExp e)
+ {
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+
+ override void visit(CallExp e)
+ {
+ //printf("CallExp(): %s\n", e.toChars());
+ /* Check each argument that is
+ * passed as 'return scope'.
+ */
+ Type t1 = e.e1.type.toBasetype();
+ TypeFunction tf;
+ TypeDelegate dg;
+ if (t1.ty == Tdelegate)
+ {
+ dg = t1.isTypeDelegate();
+ tf = dg.next.isTypeFunction();
+ }
+ else if (t1.ty == Tfunction)
+ tf = t1.isTypeFunction();
+ else
+ return;
+
+ if (!e.type.hasPointers())
+ return;
+
+ if (e.arguments && e.arguments.dim)
+ {
+ /* j=1 if _arguments[] is first argument,
+ * skip it because it is not passed by ref
+ */
+ int j = tf.isDstyleVariadic();
+ for (size_t i = j; i < e.arguments.dim; ++i)
+ {
+ Expression arg = (*e.arguments)[i];
+ size_t nparams = tf.parameterList.length;
+ if (i - j < nparams && i >= j)
+ {
+ Parameter p = tf.parameterList[i - j];
+ const stc = tf.parameterStorageClass(null, p);
+ if ((stc & (STC.scope_)) && (stc & STC.return_))
+ arg.accept(this);
+ else if ((stc & (STC.ref_)) && (stc & STC.return_))
+ {
+ if (tf.isref)
+ {
+ /* Treat:
+ * ref P foo(return ref P p)
+ * as:
+ * p;
+ */
+ arg.accept(this);
+ }
+ else
+ escapeByRef(arg, er, live);
+ }
+ }
+ }
+ }
+ // If 'this' is returned, check it too
+ if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction)
+ {
+ DotVarExp dve = e.e1.isDotVarExp();
+ FuncDeclaration fd = dve.var.isFuncDeclaration();
+ AggregateDeclaration ad;
+ if (global.params.useDIP1000 == FeatureState.enabled && tf.isreturn && fd && (ad = fd.isThis()) !is null)
+ {
+ if (ad.isClassDeclaration() || tf.isScopeQual) // this is 'return scope'
+ dve.e1.accept(this);
+ else if (ad.isStructDeclaration()) // this is 'return ref'
+ {
+ if (tf.isref)
+ {
+ /* Treat calling:
+ * struct S { ref S foo() return; }
+ * as:
+ * this;
+ */
+ dve.e1.accept(this);
+ }
+ else
+ escapeByRef(dve.e1, er, live);
+ }
+ }
+ else if (dve.var.storage_class & STC.return_ || tf.isreturn)
+ {
+ if (dve.var.storage_class & STC.scope_)
+ dve.e1.accept(this);
+ else if (dve.var.storage_class & STC.ref_)
+ escapeByRef(dve.e1, er, live);
+ }
+ // If it's also a nested function that is 'return scope'
+ if (fd && fd.isNested())
+ {
+ if (tf.isreturn && tf.isScopeQual)
+ er.byexp.push(e);
+ }
+ }
+
+ /* If returning the result of a delegate call, the .ptr
+ * field of the delegate must be checked.
+ */
+ if (dg)
+ {
+ if (tf.isreturn)
+ e.e1.accept(this);
+ }
+
+ /* If it's a nested function that is 'return scope'
+ */
+ if (auto ve = e.e1.isVarExp())
+ {
+ FuncDeclaration fd = ve.var.isFuncDeclaration();
+ if (fd && fd.isNested())
+ {
+ if (tf.isreturn && tf.isScopeQual)
+ er.byexp.push(e);
+ }
+ }
+ }
+ }
+
+ scope EscapeVisitor v = new EscapeVisitor(er, live);
+ e.accept(v);
+}
+
+
+/****************************************
+ * e is an expression to be returned by 'ref'.
+ * Walk e to determine which variables are possibly being
+ * returned by ref, such as:
+ * ref int function(int i) { return i; }
+ * If e is a form of *p, determine which variables have content
+ * which is being returned as ref, such as:
+ * ref int function(int* p) { return *p; }
+ * Multiple variables can be inserted, because of expressions like this:
+ * ref int function(bool b, int i, int* p) { return b ? i : *p; }
+ *
+ * No side effects.
+ *
+ * Params:
+ * e = expression to be returned by 'ref'
+ * er = where to place collected data
+ * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
+ */
+void escapeByRef(Expression e, EscapeByResults* er, bool live = false)
+{
+ //printf("[%s] escapeByRef, e: %s\n", e.loc.toChars(), e.toChars());
+ extern (C++) final class EscapeRefVisitor : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ EscapeByResults* er;
+ bool live;
+
+ extern (D) this(EscapeByResults* er, bool live)
+ {
+ this.er = er;
+ this.live = live;
+ }
+
+ override void visit(Expression e)
+ {
+ }
+
+ override void visit(VarExp e)
+ {
+ auto v = e.var.isVarDeclaration();
+ if (v)
+ {
+ if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init)
+ {
+ /* If compiler generated ref temporary
+ * (ref v = ex; ex)
+ * look at the initializer instead
+ */
+ if (ExpInitializer ez = v._init.isExpInitializer())
+ {
+ if (auto ce = ez.exp.isConstructExp())
+ ce.e2.accept(this);
+ else
+ ez.exp.accept(this);
+ }
+ }
+ else
+ er.byref.push(v);
+ }
+ }
+
+ override void visit(ThisExp e)
+ {
+ if (e.var && e.var.toParent2().isFuncDeclaration().isThis2)
+ escapeByValue(e, er, live);
+ else if (e.var)
+ er.byref.push(e.var);
+ }
+
+ override void visit(PtrExp e)
+ {
+ escapeByValue(e.e1, er, live);
+ }
+
+ override void visit(IndexExp e)
+ {
+ Type tb = e.e1.type.toBasetype();
+ if (auto ve = e.e1.isVarExp())
+ {
+ VarDeclaration v = ve.var.isVarDeclaration();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (v && v.storage_class & STC.variadic)
+ {
+ er.byref.push(v);
+ return;
+ }
+ }
+ }
+ if (tb.ty == Tsarray)
+ {
+ e.e1.accept(this);
+ }
+ else if (tb.ty == Tarray)
+ {
+ escapeByValue(e.e1, er, live);
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ if (e.elements)
+ {
+ foreach (ex; *e.elements)
+ {
+ if (ex)
+ ex.accept(this);
+ }
+ }
+ er.byexp.push(e);
+ }
+
+ override void visit(DotVarExp e)
+ {
+ Type t1b = e.e1.type.toBasetype();
+ if (t1b.ty == Tclass)
+ escapeByValue(e.e1, er, live);
+ else
+ e.e1.accept(this);
+ }
+
+ override void visit(BinAssignExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(AssignExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(CommaExp e)
+ {
+ e.e2.accept(this);
+ }
+
+ override void visit(CondExp e)
+ {
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+
+ override void visit(CallExp e)
+ {
+ //printf("escapeByRef.CallExp(): %s\n", e.toChars());
+ /* If the function returns by ref, check each argument that is
+ * passed as 'return ref'.
+ */
+ Type t1 = e.e1.type.toBasetype();
+ TypeFunction tf;
+ if (t1.ty == Tdelegate)
+ tf = t1.isTypeDelegate().next.isTypeFunction();
+ else if (t1.ty == Tfunction)
+ tf = t1.isTypeFunction();
+ else
+ return;
+ if (tf.isref)
+ {
+ if (e.arguments && e.arguments.dim)
+ {
+ /* j=1 if _arguments[] is first argument,
+ * skip it because it is not passed by ref
+ */
+ int j = tf.isDstyleVariadic();
+ for (size_t i = j; i < e.arguments.dim; ++i)
+ {
+ Expression arg = (*e.arguments)[i];
+ size_t nparams = tf.parameterList.length;
+ if (i - j < nparams && i >= j)
+ {
+ Parameter p = tf.parameterList[i - j];
+ const stc = tf.parameterStorageClass(null, p);
+ if ((stc & (STC.out_ | STC.ref_)) && (stc & STC.return_))
+ arg.accept(this);
+ else if ((stc & STC.scope_) && (stc & STC.return_))
+ {
+ if (auto de = arg.isDelegateExp())
+ {
+ if (de.func.isNested())
+ er.byexp.push(de);
+ }
+ else
+ escapeByValue(arg, er, live);
+ }
+ }
+ }
+ }
+ // If 'this' is returned by ref, check it too
+ if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction)
+ {
+ DotVarExp dve = e.e1.isDotVarExp();
+
+ // https://issues.dlang.org/show_bug.cgi?id=20149#c10
+ if (dve.var.isCtorDeclaration())
+ {
+ er.byexp.push(e);
+ return;
+ }
+
+ if (dve.var.storage_class & STC.return_ || tf.isreturn)
+ {
+ if (dve.var.storage_class & STC.ref_ || tf.isref)
+ dve.e1.accept(this);
+ else if (dve.var.storage_class & STC.scope_ || tf.isScopeQual)
+ escapeByValue(dve.e1, er, live);
+ }
+ // If it's also a nested function that is 'return ref'
+ FuncDeclaration fd = dve.var.isFuncDeclaration();
+ if (fd && fd.isNested())
+ {
+ if (tf.isreturn)
+ er.byexp.push(e);
+ }
+ }
+ // If it's a delegate, check it too
+ if (e.e1.op == TOK.variable && t1.ty == Tdelegate)
+ {
+ escapeByValue(e.e1, er, live);
+ }
+
+ /* If it's a nested function that is 'return ref'
+ */
+ if (auto ve = e.e1.isVarExp())
+ {
+ FuncDeclaration fd = ve.var.isFuncDeclaration();
+ if (fd && fd.isNested())
+ {
+ if (tf.isreturn)
+ er.byexp.push(e);
+ }
+ }
+ }
+ else
+ er.byexp.push(e);
+ }
+ }
+
+ scope EscapeRefVisitor v = new EscapeRefVisitor(er, live);
+ e.accept(v);
+}
+
+
+/************************************
+ * Aggregate the data collected by the escapeBy??() functions.
+ */
+struct EscapeByResults
+{
+ VarDeclarations byref; // array into which variables being returned by ref are inserted
+ VarDeclarations byvalue; // array into which variables with values containing pointers are inserted
+ FuncDeclarations byfunc; // nested functions that are turned into delegates
+ Expressions byexp; // array into which temporaries being returned by ref are inserted
+
+ /** Reset arrays so the storage can be used again
+ */
+ void reset()
+ {
+ byref.setDim(0);
+ byvalue.setDim(0);
+ byfunc.setDim(0);
+ byexp.setDim(0);
+ }
+}
+
+/*************************
+ * Find all variables accessed by this delegate that are
+ * in functions enclosing it.
+ * Params:
+ * fd = function
+ * vars = array to append found variables to
+ */
+public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* vars)
+{
+ //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
+ for (auto p = fd.parent; p; p = p.parent)
+ {
+ auto fdp = p.isFuncDeclaration();
+ if (!fdp)
+ continue;
+
+ foreach (v; fdp.closureVars)
+ {
+ foreach (const fdv; v.nestedrefs)
+ {
+ if (fdv == fd)
+ {
+ //printf("accessed: %s, type %s\n", v.toChars(), v.type.toChars());
+ vars.push(v);
+ }
+ }
+ }
+ }
+}
+
+/***********************************
+ * Turn off `STC.maybescope` for variable `v`.
+ *
+ * This exists in order to find where `STC.maybescope` is getting turned off.
+ * Params:
+ * v = variable
+ */
+version (none)
+{
+ public void notMaybeScope(string file = __FILE__, int line = __LINE__)(VarDeclaration v)
+ {
+ printf("%.*s(%d): notMaybeScope('%s')\n", cast(int)file.length, file.ptr, line, v.toChars());
+ v.storage_class &= ~STC.maybescope;
+ }
+}
+else
+{
+ public void notMaybeScope(VarDeclaration v)
+ {
+ v.storage_class &= ~STC.maybescope;
+ }
+}
+
+
+/**********************************************
+ * Have some variables that are maybescopes that were
+ * assigned values from other maybescope variables.
+ * Now that semantic analysis of the function is
+ * complete, we can finalize this by turning off
+ * maybescope for array elements that cannot be scope.
+ *
+ * $(TABLE2 Scope Table,
+ * $(THEAD `va`, `v`, =>, `va` , `v` )
+ * $(TROW maybe, maybe, =>, scope, scope)
+ * $(TROW scope, scope, =>, scope, scope)
+ * $(TROW scope, maybe, =>, scope, scope)
+ * $(TROW maybe, scope, =>, scope, scope)
+ * $(TROW - , - , =>, - , - )
+ * $(TROW - , maybe, =>, - , - )
+ * $(TROW - , scope, =>, error, error)
+ * $(TROW maybe, - , =>, scope, - )
+ * $(TROW scope, - , =>, scope, - )
+ * )
+ * Params:
+ * array = array of variables that were assigned to from maybescope variables
+ */
+public void eliminateMaybeScopes(VarDeclaration[] array)
+{
+ enum log = false;
+ if (log) printf("eliminateMaybeScopes()\n");
+ bool changes;
+ do
+ {
+ changes = false;
+ foreach (va; array)
+ {
+ if (log) printf(" va = %s\n", va.toChars());
+ if (!(va.storage_class & (STC.maybescope | STC.scope_)))
+ {
+ if (va.maybes)
+ {
+ foreach (v; *va.maybes)
+ {
+ if (log) printf(" v = %s\n", v.toChars());
+ if (v.storage_class & STC.maybescope)
+ {
+ // v cannot be scope since it is assigned to a non-scope va
+ notMaybeScope(v);
+ if (!v.isReference())
+ v.storage_class &= ~(STC.return_ | STC.returninferred);
+ changes = true;
+ }
+ }
+ }
+ }
+ }
+ } while (changes);
+}
+
+/************************************************
+ * Is type a reference to a mutable value?
+ *
+ * This is used to determine if an argument that does not have a corresponding
+ * Parameter, i.e. a variadic argument, is a pointer to mutable data.
+ * Params:
+ * t = type of the argument
+ * Returns:
+ * true if it's a pointer (or reference) to mutable data
+ */
+bool isReferenceToMutable(Type t)
+{
+ t = t.baseElemOf();
+
+ if (!t.isMutable() ||
+ !t.hasPointers())
+ return false;
+
+ switch (t.ty)
+ {
+ case Tpointer:
+ if (t.nextOf().isTypeFunction())
+ break;
+ goto case;
+
+ case Tarray:
+ case Taarray:
+ case Tdelegate:
+ if (t.nextOf().isMutable())
+ return true;
+ break;
+
+ case Tclass:
+ return true; // even if the class fields are not mutable
+
+ case Tstruct:
+ // Have to look at each field
+ foreach (VarDeclaration v; t.isTypeStruct().sym.fields)
+ {
+ if (v.storage_class & STC.ref_)
+ {
+ if (v.type.isMutable())
+ return true;
+ }
+ else if (v.type.isReferenceToMutable())
+ return true;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ return false;
+}
+
+/****************************************
+ * Is parameter a reference to a mutable value?
+ *
+ * This is used if an argument has a corresponding Parameter.
+ * The argument type is necessary if the Parameter is inout.
+ * Params:
+ * p = Parameter to check
+ * t = type of corresponding argument
+ * Returns:
+ * true if it's a pointer (or reference) to mutable data
+ */
+bool isReferenceToMutable(Parameter p, Type t)
+{
+ if (p.isReference())
+ {
+ if (p.type.isConst() || p.type.isImmutable())
+ return false;
+ if (p.type.isWild())
+ {
+ return t.isMutable();
+ }
+ return p.type.isMutable();
+ }
+ return isReferenceToMutable(p.type);
+}
diff --git a/gcc/d/dmd/expression.c b/gcc/d/dmd/expression.c
deleted file mode 100644
index 18aa6aa9ab4..00000000000
--- a/gcc/d/dmd/expression.c
+++ /dev/null
@@ -1,5706 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/expression.c
- */
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-#include "root/root.h"
-
-#include "errors.h"
-#include "mtype.h"
-#include "init.h"
-#include "expression.h"
-#include "template.h"
-#include "utf.h"
-#include "enum.h"
-#include "scope.h"
-#include "statement.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "import.h"
-#include "id.h"
-#include "dsymbol.h"
-#include "module.h"
-#include "attrib.h"
-#include "hdrgen.h"
-#include "parse.h"
-#include "doc.h"
-#include "root/aav.h"
-#include "nspace.h"
-#include "ctfe.h"
-#include "target.h"
-
-bool walkPostorder(Expression *e, StoppableVisitor *v);
-bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag);
-bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember);
-VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e);
-Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expression *e, bool alwaysCopy = false);
-char *MODtoChars(MOD mod);
-bool MODimplicitConv(MOD modfrom, MOD modto);
-void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod);
-Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
-bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg);
-void toAutoQualChars(const char **result, Type *t1, Type *t2);
-
-/*****************************************
- * Determine if 'this' is available.
- * If it is, return the FuncDeclaration that has it.
- */
-
-FuncDeclaration *hasThis(Scope *sc)
-{
- //printf("hasThis()\n");
- Dsymbol *p = sc->parent;
- while (p && p->isTemplateMixin())
- p = p->parent;
- FuncDeclaration *fdthis = p ? p->isFuncDeclaration() : NULL;
- //printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis->toChars() : "");
-
- // Go upwards until we find the enclosing member function
- FuncDeclaration *fd = fdthis;
- while (1)
- {
- if (!fd)
- {
- goto Lno;
- }
- if (!fd->isNested())
- break;
-
- Dsymbol *parent = fd->parent;
- while (1)
- {
- if (!parent)
- goto Lno;
- TemplateInstance *ti = parent->isTemplateInstance();
- if (ti)
- parent = ti->parent;
- else
- break;
- }
- fd = parent->isFuncDeclaration();
- }
-
- if (!fd->isThis())
- { //printf("test '%s'\n", fd->toChars());
- goto Lno;
- }
-
- assert(fd->vthis);
- return fd;
-
-Lno:
- return NULL; // don't have 'this' available
-}
-
-bool isNeedThisScope(Scope *sc, Declaration *d)
-{
- if (sc->intypeof == 1)
- return false;
-
- AggregateDeclaration *ad = d->isThis();
- if (!ad)
- return false;
- //printf("d = %s, ad = %s\n", d->toChars(), ad->toChars());
-
- for (Dsymbol *s = sc->parent; s; s = s->toParent2())
- {
- //printf("\ts = %s %s, toParent2() = %p\n", s->kind(), s->toChars(), s->toParent2());
- if (AggregateDeclaration *ad2 = s->isAggregateDeclaration())
- {
- if (ad2 == ad)
- return false;
- else if (ad2->isNested())
- continue;
- else
- return true;
- }
- if (FuncDeclaration *f = s->isFuncDeclaration())
- {
- if (f->isMember2())
- break;
- }
- }
- return true;
-}
-
-/******************************
- * check e is exp.opDispatch!(tiargs) or not
- * It's used to switch to UFCS the semantic analysis path
- */
-
-bool isDotOpDispatch(Expression *e)
-{
- return e->op == TOKdotti &&
- ((DotTemplateInstanceExp *)e)->ti->name == Id::opDispatch;
-}
-
-/****************************************
- * Expand tuples.
- * Input:
- * exps aray of Expressions
- * Output:
- * exps rewritten in place
- */
-
-void expandTuples(Expressions *exps)
-{
- //printf("expandTuples()\n");
- if (exps)
- {
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *arg = (*exps)[i];
- if (!arg)
- continue;
-
- // Look for tuple with 0 members
- if (arg->op == TOKtype)
- {
- TypeExp *e = (TypeExp *)arg;
- if (e->type->toBasetype()->ty == Ttuple)
- {
- TypeTuple *tt = (TypeTuple *)e->type->toBasetype();
-
- if (!tt->arguments || tt->arguments->length == 0)
- {
- exps->remove(i);
- if (i == exps->length)
- return;
- i--;
- continue;
- }
- }
- }
-
- // Inline expand all the tuples
- while (arg->op == TOKtuple)
- {
- TupleExp *te = (TupleExp *)arg;
- exps->remove(i); // remove arg
- exps->insert(i, te->exps); // replace with tuple contents
- if (i == exps->length)
- return; // empty tuple, no more arguments
- (*exps)[i] = Expression::combine(te->e0, (*exps)[i]);
- arg = (*exps)[i];
- }
- }
- }
-}
-
-/****************************************
- * Expand alias this tuples.
- */
-
-TupleDeclaration *isAliasThisTuple(Expression *e)
-{
- if (!e->type)
- return NULL;
-
- Type *t = e->type->toBasetype();
-Lagain:
- if (Dsymbol *s = t->toDsymbol(NULL))
- {
- AggregateDeclaration *ad = s->isAggregateDeclaration();
- if (ad)
- {
- s = ad->aliasthis;
- if (s && s->isVarDeclaration())
- {
- TupleDeclaration *td = s->isVarDeclaration()->toAlias()->isTupleDeclaration();
- if (td && td->isexp)
- return td;
- }
- if (Type *att = t->aliasthisOf())
- {
- t = att;
- goto Lagain;
- }
- }
- }
- return NULL;
-}
-
-int expandAliasThisTuples(Expressions *exps, size_t starti)
-{
- if (!exps || exps->length == 0)
- return -1;
-
- for (size_t u = starti; u < exps->length; u++)
- {
- Expression *exp = (*exps)[u];
- TupleDeclaration *td = isAliasThisTuple(exp);
- if (td)
- {
- exps->remove(u);
- for (size_t i = 0; i<td->objects->length; ++i)
- {
- Expression *e = isExpression((*td->objects)[i]);
- assert(e);
- assert(e->op == TOKdsymbol);
- DsymbolExp *se = (DsymbolExp *)e;
- Declaration *d = se->s->isDeclaration();
- assert(d);
- e = new DotVarExp(exp->loc, exp, d);
- assert(d->type);
- e->type = d->type;
- exps->insert(u + i, e);
- }
- return (int)u;
- }
- }
-
- return -1;
-}
-
-/****************************************
- * Get TemplateDeclaration enclosing FuncDeclaration.
- */
-
-TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s)
-{
- FuncDeclaration *f = s->isFuncDeclaration();
- if (f && f->parent)
- {
- TemplateInstance *ti = f->parent->isTemplateInstance();
- if (ti && !ti->isTemplateMixin() &&
- ti->tempdecl && ((TemplateDeclaration *)ti->tempdecl)->onemember &&
- ti->tempdecl->ident == f->ident)
- {
- return (TemplateDeclaration *)ti->tempdecl;
- }
- }
- return NULL;
-}
-
-/************************************************
- * If we want the value of this expression, but do not want to call
- * the destructor on it.
- */
-
-Expression *valueNoDtor(Expression *e)
-{
- if (e->op == TOKcall)
- {
- /* The struct value returned from the function is transferred
- * so do not call the destructor on it.
- * Recognize:
- * ((S _ctmp = S.init), _ctmp).this(...)
- * and make sure the destructor is not called on _ctmp
- * BUG: if e is a CommaExp, we should go down the right side.
- */
- CallExp *ce = (CallExp *)e;
- if (ce->e1->op == TOKdotvar)
- {
- DotVarExp *dve = (DotVarExp *)ce->e1;
- if (dve->var->isCtorDeclaration())
- {
- // It's a constructor call
- if (dve->e1->op == TOKcomma)
- {
- CommaExp *comma = (CommaExp *)dve->e1;
- if (comma->e2->op == TOKvar)
- {
- VarExp *ve = (VarExp *)comma->e2;
- VarDeclaration *ctmp = ve->var->isVarDeclaration();
- if (ctmp)
- {
- ctmp->storage_class |= STCnodtor;
- assert(!ce->isLvalue());
- }
- }
- }
- }
- }
- }
- else if (e->op == TOKvar)
- {
- VarDeclaration *vtmp = ((VarExp *)e)->var->isVarDeclaration();
- if (vtmp && vtmp->storage_class & STCrvalue)
- {
- vtmp->storage_class |= STCnodtor;
- }
- }
- return e;
-}
-
-/*********************************************
- * If e is an instance of a struct, and that struct has a copy constructor,
- * rewrite e as:
- * (tmp = e),tmp
- * Input:
- * sc just used to specify the scope of created temporary variable
- */
-Expression *callCpCtor(Scope *sc, Expression *e)
-{
- Type *tv = e->type->baseElemOf();
- if (tv->ty == Tstruct)
- {
- StructDeclaration *sd = ((TypeStruct *)tv)->sym;
- if (sd->postblit)
- {
- /* Create a variable tmp, and replace the argument e with:
- * (tmp = e),tmp
- * and let AssignExp() handle the construction.
- * This is not the most efficent, ideally tmp would be constructed
- * directly onto the stack.
- */
- VarDeclaration *tmp = copyToTemp(STCrvalue, "__copytmp", e);
- tmp->storage_class |= STCnodtor;
- dsymbolSemantic(tmp, sc);
- Expression *de = new DeclarationExp(e->loc, tmp);
- Expression *ve = new VarExp(e->loc, tmp);
- de->type = Type::tvoid;
- ve->type = e->type;
- e = Expression::combine(de, ve);
- }
- }
- return e;
-}
-
-/************************************************
- * Handle the postblit call on lvalue, or the move of rvalue.
- */
-Expression *doCopyOrMove(Scope *sc, Expression *e)
-{
- if (e->op == TOKquestion)
- {
- CondExp *ce = (CondExp *)e;
- ce->e1 = doCopyOrMove(sc, ce->e1);
- ce->e2 = doCopyOrMove(sc, ce->e2);
- }
- else
- {
- e = e->isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e);
- }
- return e;
-}
-
-/******************************** Expression **************************/
-
-Expression::Expression(Loc loc, TOK op, int size)
-{
- //printf("Expression::Expression(op = %d) this = %p\n", op, this);
- this->loc = loc;
- this->op = op;
- this->size = (unsigned char)size;
- this->parens = 0;
- type = NULL;
-}
-
-void Expression::_init()
-{
- CTFEExp::cantexp = new CTFEExp(TOKcantexp);
- CTFEExp::voidexp = new CTFEExp(TOKvoidexp);
- CTFEExp::breakexp = new CTFEExp(TOKbreak);
- CTFEExp::continueexp = new CTFEExp(TOKcontinue);
- CTFEExp::gotoexp = new CTFEExp(TOKgoto);
-}
-
-Expression *Expression::syntaxCopy()
-{
- //printf("Expression::syntaxCopy()\n");
- //print();
- return copy();
-}
-
-/*********************************
- * Does *not* do a deep copy.
- */
-
-Expression *Expression::copy()
-{
- Expression *e;
- if (!size)
- {
- assert(0);
- }
- void *pe = mem.xmalloc(size);
- //printf("Expression::copy(op = %d) e = %p\n", op, pe);
- e = (Expression *)memcpy(pe, (void *)this, size);
- return e;
-}
-
-void Expression::print()
-{
- fprintf(stderr, "%s\n", toChars());
- fflush(stderr);
-}
-
-const char *Expression::toChars()
-{
- OutBuffer buf;
- HdrGenState hgs;
- toCBuffer(this, &buf, &hgs);
- return buf.extractChars();
-}
-
-void Expression::error(const char *format, ...) const
-{
- if (type != Type::terror)
- {
- va_list ap;
- va_start(ap, format);
- ::verror(loc, format, ap);
- va_end( ap );
- }
-}
-
-void Expression::warning(const char *format, ...) const
-{
- if (type != Type::terror)
- {
- va_list ap;
- va_start(ap, format);
- ::vwarning(loc, format, ap);
- va_end( ap );
- }
-}
-
-void Expression::deprecation(const char *format, ...) const
-{
- if (type != Type::terror)
- {
- va_list ap;
- va_start(ap, format);
- ::vdeprecation(loc, format, ap);
- va_end( ap );
- }
-}
-
-/**********************************
- * Combine e1 and e2 by CommaExp if both are not NULL.
- */
-Expression *Expression::combine(Expression *e1, Expression *e2)
-{
- if (e1)
- {
- if (e2)
- {
- e1 = new CommaExp(e1->loc, e1, e2);
- e1->type = e2->type;
- }
- }
- else
- e1 = e2;
- return e1;
-}
-
-/**********************************
- * If 'e' is a tree of commas, returns the leftmost expression
- * by stripping off it from the tree. The remained part of the tree
- * is returned via *pe0.
- * Otherwise 'e' is directly returned and *pe0 is set to NULL.
- */
-Expression *Expression::extractLast(Expression *e, Expression **pe0)
-{
- if (e->op != TOKcomma)
- {
- *pe0 = NULL;
- return e;
- }
-
- CommaExp *ce = (CommaExp *)e;
- if (ce->e2->op != TOKcomma)
- {
- *pe0 = ce->e1;
- return ce->e2;
- }
- else
- {
- *pe0 = e;
-
- Expression **pce = &ce->e2;
- while (((CommaExp *)(*pce))->e2->op == TOKcomma)
- {
- pce = &((CommaExp *)(*pce))->e2;
- }
- assert((*pce)->op == TOKcomma);
- ce = (CommaExp *)(*pce);
- *pce = ce->e1;
-
- return ce->e2;
- }
-}
-
-dinteger_t Expression::toInteger()
-{
- //printf("Expression %s\n", Token::toChars(op));
- error("integer constant expression expected instead of %s", toChars());
- return 0;
-}
-
-uinteger_t Expression::toUInteger()
-{
- //printf("Expression %s\n", Token::toChars(op));
- return (uinteger_t)toInteger();
-}
-
-real_t Expression::toReal()
-{
- error("floating point constant expression expected instead of %s", toChars());
- return CTFloat::zero;
-}
-
-real_t Expression::toImaginary()
-{
- error("floating point constant expression expected instead of %s", toChars());
- return CTFloat::zero;
-}
-
-complex_t Expression::toComplex()
-{
- error("floating point constant expression expected instead of %s", toChars());
- return complex_t(CTFloat::zero);
-}
-
-StringExp *Expression::toStringExp()
-{
- return NULL;
-}
-
-TupleExp *Expression::toTupleExp()
-{
- return NULL;
-}
-
-/***************************************
- * Return !=0 if expression is an lvalue.
- */
-
-bool Expression::isLvalue()
-{
- return false;
-}
-
-/*******************************
- * Give error if we're not an lvalue.
- * If we can, convert expression to be an lvalue.
- */
-
-Expression *Expression::toLvalue(Scope *, Expression *e)
-{
- if (!e)
- e = this;
- else if (!loc.filename)
- loc = e->loc;
-
- if (e->op == TOKtype)
- error("%s `%s` is a type, not an lvalue", e->type->kind(), e->type->toChars());
- else
- error("%s is not an lvalue", e->toChars());
-
- return new ErrorExp();
-}
-
-/***************************************
- * Parameters:
- * sc: scope
- * flag: 1: do not issue error message for invalid modification
- * Returns:
- * 0: is not modifiable
- * 1: is modifiable in default == being related to type->isMutable()
- * 2: is modifiable, because this is a part of initializing.
- */
-
-int Expression::checkModifiable(Scope *, int)
-{
- return type ? 1 : 0; // default modifiable
-}
-
-Expression *Expression::modifiableLvalue(Scope *sc, Expression *e)
-{
- //printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type->toChars());
-
- // See if this expression is a modifiable lvalue (i.e. not const)
- if (checkModifiable(sc) == 1)
- {
- assert(type);
- if (!type->isMutable())
- {
- error("cannot modify %s expression %s", MODtoChars(type->mod), toChars());
- return new ErrorExp();
- }
- else if (!type->isAssignable())
- {
- error("cannot modify struct %s %s with immutable members", toChars(), type->toChars());
- return new ErrorExp();
- }
- }
- return toLvalue(sc, e);
-}
-
-/****************************************
- * Check that the expression has a valid type.
- * If not, generates an error "... has no type".
- * Returns:
- * true if the expression is not valid.
- * Note:
- * When this function returns true, `checkValue()` should also return true.
- */
-bool Expression::checkType()
-{
- return false;
-}
-
-/****************************************
- * Check that the expression has a valid value.
- * If not, generates an error "... has no value".
- * Returns:
- * true if the expression is not valid or has void type.
- */
-bool Expression::checkValue()
-{
- if (type && type->toBasetype()->ty == Tvoid)
- {
- error("expression %s is void and has no value", toChars());
- //print(); halt();
- if (!global.gag)
- type = Type::terror;
- return true;
- }
- return false;
-}
-
-bool Expression::checkScalar()
-{
- if (op == TOKerror)
- return true;
- if (type->toBasetype()->ty == Terror)
- return true;
- if (!type->isscalar())
- {
- error("`%s` is not a scalar, it is a %s", toChars(), type->toChars());
- return true;
- }
- return checkValue();
-}
-
-bool Expression::checkNoBool()
-{
- if (op == TOKerror)
- return true;
- if (type->toBasetype()->ty == Terror)
- return true;
- if (type->toBasetype()->ty == Tbool)
- {
- error("operation not allowed on bool `%s`", toChars());
- return true;
- }
- return false;
-}
-
-bool Expression::checkIntegral()
-{
- if (op == TOKerror)
- return true;
- if (type->toBasetype()->ty == Terror)
- return true;
- if (!type->isintegral())
- {
- error("`%s` is not of integral type, it is a %s", toChars(), type->toChars());
- return true;
- }
- return checkValue();
-}
-
-bool Expression::checkArithmetic()
-{
- if (op == TOKerror)
- return true;
- if (type->toBasetype()->ty == Terror)
- return true;
- if (!type->isintegral() && !type->isfloating())
- {
- error("`%s` is not of arithmetic type, it is a %s", toChars(), type->toChars());
- return true;
- }
- return checkValue();
-}
-
-bool Expression::checkDeprecated(Scope *sc, Dsymbol *s)
-{
- return s->checkDeprecated(loc, sc);
-}
-
-bool Expression::checkDisabled(Scope *sc, Dsymbol *s)
-{
- if (Declaration *d = s->isDeclaration())
- {
- return d->checkDisabled(loc, sc);
- }
- return false;
-}
-
-/*********************************************
- * Calling function f.
- * Check the purity, i.e. if we're in a pure function
- * we can only call other pure functions.
- * Returns true if error occurs.
- */
-bool Expression::checkPurity(Scope *sc, FuncDeclaration *f)
-{
- if (!sc->func)
- return false;
- if (sc->func == f)
- return false;
- if (sc->intypeof == 1)
- return false;
- if (sc->flags & (SCOPEctfe | SCOPEdebug))
- return false;
-
- /* Given:
- * void f() {
- * pure void g() {
- * /+pure+/ void h() {
- * /+pure+/ void i() { }
- * }
- * }
- * }
- * g() can call h() but not f()
- * i() can call h() and g() but not f()
- */
-
- // Find the closest pure parent of the calling function
- FuncDeclaration *outerfunc = sc->func;
- FuncDeclaration *calledparent = f;
-
- if (outerfunc->isInstantiated())
- {
- // The attributes of outerfunc should be inferred from the call of f.
- }
- else if (f->isInstantiated())
- {
- // The attributes of f are inferred from its body.
- }
- else if (f->isFuncLiteralDeclaration())
- {
- // The attributes of f are always inferred in its declared place.
- }
- else
- {
- /* Today, static local functions are impure by default, but they cannot
- * violate purity of enclosing functions.
- *
- * auto foo() pure { // non instantiated funciton
- * static auto bar() { // static, without pure attribute
- * impureFunc(); // impure call
- * // Although impureFunc is called inside bar, f(= impureFunc)
- * // is not callable inside pure outerfunc(= foo <- bar).
- * }
- *
- * bar();
- * // Although bar is called inside foo, f(= bar) is callable
- * // bacause calledparent(= foo) is same with outerfunc(= foo).
- * }
- */
-
- while (outerfunc->toParent2() &&
- outerfunc->isPureBypassingInference() == PUREimpure &&
- outerfunc->toParent2()->isFuncDeclaration())
- {
- outerfunc = outerfunc->toParent2()->isFuncDeclaration();
- if (outerfunc->type->ty == Terror)
- return true;
- }
- while (calledparent->toParent2() &&
- calledparent->isPureBypassingInference() == PUREimpure &&
- calledparent->toParent2()->isFuncDeclaration())
- {
- calledparent = calledparent->toParent2()->isFuncDeclaration();
- if (calledparent->type->ty == Terror)
- return true;
- }
- }
-
- // If the caller has a pure parent, then either the called func must be pure,
- // OR, they must have the same pure parent.
- if (!f->isPure() && calledparent != outerfunc)
- {
- FuncDeclaration *ff = outerfunc;
- if (sc->flags & SCOPEcompile ? ff->isPureBypassingInference() >= PUREweak : ff->setImpure())
- {
- error("pure %s `%s` cannot call impure %s `%s`",
- ff->kind(), ff->toPrettyChars(), f->kind(), f->toPrettyChars());
- return true;
- }
- }
- return false;
-}
-
-/*******************************************
- * Accessing variable v.
- * Check for purity and safety violations.
- * Returns true if error occurs.
- */
-bool Expression::checkPurity(Scope *sc, VarDeclaration *v)
-{
- //printf("v = %s %s\n", v->type->toChars(), v->toChars());
-
- /* Look for purity and safety violations when accessing variable v
- * from current function.
- */
- if (!sc->func)
- return false;
- if (sc->intypeof == 1)
- return false; // allow violations inside typeof(expression)
- if (sc->flags & (SCOPEctfe | SCOPEdebug))
- return false; // allow violations inside compile-time evaluated expressions and debug conditionals
- if (v->ident == Id::ctfe)
- return false; // magic variable never violates pure and safe
- if (v->isImmutable())
- return false; // always safe and pure to access immutables...
- if (v->isConst() && !v->isRef() && (v->isDataseg() || v->isParameter()) &&
- v->type->implicitConvTo(v->type->immutableOf()))
- return false; // or const global/parameter values which have no mutable indirections
- if (v->storage_class & STCmanifest)
- return false; // ...or manifest constants
-
- bool err = false;
- if (v->isDataseg())
- {
- // Bugzilla 7533: Accessing implicit generated __gate is pure.
- if (v->ident == Id::gate)
- return false;
-
- /* Accessing global mutable state.
- * Therefore, this function and all its immediately enclosing
- * functions must be pure.
- */
- /* Today, static local functions are impure by default, but they cannot
- * violate purity of enclosing functions.
- *
- * auto foo() pure { // non instantiated funciton
- * static auto bar() { // static, without pure attribute
- * globalData++; // impure access
- * // Although globalData is accessed inside bar,
- * // it is not accessible inside pure foo.
- * }
- * }
- */
- for (Dsymbol *s = sc->func; s; s = s->toParent2())
- {
- FuncDeclaration *ff = s->isFuncDeclaration();
- if (!ff)
- break;
- if (sc->flags & SCOPEcompile ? ff->isPureBypassingInference() >= PUREweak : ff->setImpure())
- {
- error("pure %s `%s` cannot access mutable static data `%s`",
- ff->kind(), ff->toPrettyChars(), v->toChars());
- err = true;
- break;
- }
- /* If the enclosing is an instantiated function or a lambda, its
- * attribute inference result is preferred.
- */
- if (ff->isInstantiated())
- break;
- if (ff->isFuncLiteralDeclaration())
- break;
- }
- }
- else
- {
- /* Given:
- * void f() {
- * int fx;
- * pure void g() {
- * int gx;
- * /+pure+/ void h() {
- * int hx;
- * /+pure+/ void i() { }
- * }
- * }
- * }
- * i() can modify hx and gx but not fx
- */
-
- Dsymbol *vparent = v->toParent2();
- for (Dsymbol *s = sc->func; !err && s; s = s->toParent2())
- {
- if (s == vparent)
- break;
-
- if (AggregateDeclaration *ad = s->isAggregateDeclaration())
- {
- if (ad->isNested())
- continue;
- break;
- }
- FuncDeclaration *ff = s->isFuncDeclaration();
- if (!ff)
- break;
- if (ff->isNested() || ff->isThis())
- {
- if (ff->type->isImmutable() ||
- (ff->type->isShared() && !MODimplicitConv(ff->type->mod, v->type->mod)))
- {
- OutBuffer ffbuf;
- OutBuffer vbuf;
- MODMatchToBuffer(&ffbuf, ff->type->mod, v->type->mod);
- MODMatchToBuffer(&vbuf, v->type->mod, ff->type->mod);
- error("%s%s `%s` cannot access %sdata `%s`",
- ffbuf.peekChars(), ff->kind(), ff->toPrettyChars(), vbuf.peekChars(), v->toChars());
- err = true;
- break;
- }
- continue;
- }
- break;
- }
- }
-
- /* Do not allow safe functions to access __gshared data
- */
- if (v->storage_class & STCgshared)
- {
- if (sc->func->setUnsafe())
- {
- error("safe %s `%s` cannot access __gshared data `%s`",
- sc->func->kind(), sc->func->toChars(), v->toChars());
- err = true;
- }
- }
-
- return err;
-}
-
-/*********************************************
- * Calling function f.
- * Check the safety, i.e. if we're in a @safe function
- * we can only call @safe or @trusted functions.
- * Returns true if error occurs.
- */
-bool Expression::checkSafety(Scope *sc, FuncDeclaration *f)
-{
- if (!sc->func)
- return false;
- if (sc->func == f)
- return false;
- if (sc->intypeof == 1)
- return false;
- if (sc->flags & SCOPEctfe)
- return false;
-
- if (!f->isSafe() && !f->isTrusted())
- {
- if (sc->flags & SCOPEcompile ? sc->func->isSafeBypassingInference() : sc->func->setUnsafe())
- {
- if (loc.linnum == 0) // e.g. implicitly generated dtor
- loc = sc->func->loc;
-
- error("@safe %s `%s` cannot call @system %s `%s`",
- sc->func->kind(), sc->func->toPrettyChars(), f->kind(), f->toPrettyChars());
- return true;
- }
- }
- return false;
-}
-
-/*********************************************
- * Calling function f.
- * Check the @nogc-ness, i.e. if we're in a @nogc function
- * we can only call other @nogc functions.
- * Returns true if error occurs.
- */
-bool Expression::checkNogc(Scope *sc, FuncDeclaration *f)
-{
- if (!sc->func)
- return false;
- if (sc->func == f)
- return false;
- if (sc->intypeof == 1)
- return false;
- if (sc->flags & SCOPEctfe)
- return false;
-
- if (!f->isNogc())
- {
- if (sc->flags & SCOPEcompile ? sc->func->isNogcBypassingInference() : sc->func->setGC())
- {
- if (loc.linnum == 0) // e.g. implicitly generated dtor
- loc = sc->func->loc;
-
- error("@nogc %s `%s` cannot call non-@nogc %s `%s`",
- sc->func->kind(), sc->func->toPrettyChars(), f->kind(), f->toPrettyChars());
- return true;
- }
- }
- return false;
-}
-
-/********************************************
- * Check that the postblit is callable if t is an array of structs.
- * Returns true if error happens.
- */
-bool Expression::checkPostblit(Scope *sc, Type *t)
-{
- t = t->baseElemOf();
- if (t->ty == Tstruct)
- {
- if (global.params.useTypeInfo && Type::dtypeinfo)
- {
- // Bugzilla 11395: Require TypeInfo generation for array concatenation
- semanticTypeInfo(sc, t);
- }
-
- StructDeclaration *sd = ((TypeStruct *)t)->sym;
- if (sd->postblit)
- {
- if (sd->postblit->checkDisabled(loc, sc))
- return true;
- //checkDeprecated(sc, sd->postblit); // necessary?
- checkPurity(sc, sd->postblit);
- checkSafety(sc, sd->postblit);
- checkNogc(sc, sd->postblit);
- //checkAccess(sd, loc, sc, sd->postblit); // necessary?
- return false;
- }
- }
- return false;
-}
-
-bool Expression::checkRightThis(Scope *sc)
-{
- if (op == TOKerror)
- return true;
- if (op == TOKvar && type->ty != Terror)
- {
- VarExp *ve = (VarExp *)this;
- if (isNeedThisScope(sc, ve->var))
- {
- //printf("checkRightThis sc->intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
- // sc->intypeof, sc->getStructClassScope(), func, fdthis);
- error("need `this` for `%s` of type `%s`", ve->var->toChars(), ve->var->type->toChars());
- return true;
- }
- }
- return false;
-}
-
-/*******************************
- * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not.
- * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics)
- * Returns true if error occurs.
- */
-bool Expression::checkReadModifyWrite(TOK rmwOp, Expression *ex)
-{
- //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex->toChars() : "");
- if (!type || !type->isShared())
- return false;
-
- // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal.
- switch (rmwOp)
- {
- case TOKplusplus:
- case TOKpreplusplus:
- rmwOp = TOKaddass;
- break;
-
- case TOKminusminus:
- case TOKpreminusminus:
- rmwOp = TOKminass;
- break;
-
- default:
- break;
- }
-
- deprecation("read-modify-write operations are not allowed for shared variables. "
- "Use core.atomic.atomicOp!\"%s\"(%s, %s) instead.",
- Token::tochars[rmwOp], toChars(), ex ? ex->toChars() : "1");
- return false;
-
- // note: enable when deprecation becomes an error.
- // return true;
-}
-
-/*****************************
- * If expression can be tested for true or false,
- * returns the modified expression.
- * Otherwise returns ErrorExp.
- */
-Expression *Expression::toBoolean(Scope *sc)
-{
- // Default is 'yes' - do nothing
- Expression *e = this;
- Type *t = type;
- Type *tb = type->toBasetype();
- Type *att = NULL;
-Lagain:
- // Structs can be converted to bool using opCast(bool)()
- if (tb->ty == Tstruct)
- {
- AggregateDeclaration *ad = ((TypeStruct *)tb)->sym;
- /* Don't really need to check for opCast first, but by doing so we
- * get better error messages if it isn't there.
- */
- Dsymbol *fd = search_function(ad, Id::_cast);
- if (fd)
- {
- e = new CastExp(loc, e, Type::tbool);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- // Forward to aliasthis.
- if (ad->aliasthis && tb != att)
- {
- if (!att && tb->checkAliasThisRec())
- att = tb;
- e = resolveAliasThis(sc, e);
- t = e->type;
- tb = e->type->toBasetype();
- goto Lagain;
- }
- }
-
- if (!t->isBoolean())
- {
- if (tb != Type::terror)
- error("expression %s of type %s does not have a boolean value", toChars(), t->toChars());
- return new ErrorExp();
- }
- return e;
-}
-
-/******************************
- * Take address of expression.
- */
-
-Expression *Expression::addressOf()
-{
- //printf("Expression::addressOf()\n");
- Expression *e = new AddrExp(loc, this);
- e->type = type->pointerTo();
- return e;
-}
-
-/******************************
- * If this is a reference, dereference it.
- */
-
-Expression *Expression::deref()
-{
- //printf("Expression::deref()\n");
- // type could be null if forward referencing an 'auto' variable
- if (type && type->ty == Treference)
- {
- Expression *e = new PtrExp(loc, this);
- e->type = ((TypeReference *)type)->next;
- return e;
- }
- return this;
-}
-
-/********************************
- * Does this expression statically evaluate to a boolean 'result' (true or false)?
- */
-bool Expression::isBool(bool)
-{
- return false;
-}
-
-IntegerExp *Expression::isIntegerExp()
-{
- return op == TOKint64 ? (IntegerExp *)this : NULL;
-}
-
-ErrorExp *Expression::isErrorExp()
-{
- return op == TOKerror ? (ErrorExp *)this : NULL;
-}
-
-VoidInitExp *Expression::isVoidInitExp()
-{
- return op == TOKvoid ? (VoidInitExp *)this : NULL;
-}
-
-RealExp *Expression::isRealExp()
-{
- return op == TOKfloat64 ? (RealExp *)this : NULL;
-}
-
-ComplexExp *Expression::isComplexExp()
-{
- return op == TOKcomplex80 ? (ComplexExp *)this : NULL;
-}
-
-IdentifierExp *Expression::isIdentifierExp()
-{
- return op == TOKidentifier ? (IdentifierExp *)this : NULL;
-}
-
-DollarExp *Expression::isDollarExp()
-{
- return op == TOKdollar ? (DollarExp *)this : NULL;
-}
-
-DsymbolExp *Expression::isDsymbolExp()
-{
- return op == TOKdsymbol ? (DsymbolExp *)this : NULL;
-}
-
-ThisExp *Expression::isThisExp()
-{
- return op == TOKthis ? (ThisExp *)this : NULL;
-}
-
-SuperExp *Expression::isSuperExp()
-{
- return op == TOKsuper ? (SuperExp *)this : NULL;
-}
-
-NullExp *Expression::isNullExp()
-{
- return op == TOKnull ? (NullExp *)this : NULL;
-}
-
-StringExp *Expression::isStringExp()
-{
- return op == TOKstring ? (StringExp *)this : NULL;
-}
-
-TupleExp *Expression::isTupleExp()
-{
- return op == TOKtuple ? (TupleExp *)this : NULL;
-}
-
-ArrayLiteralExp *Expression::isArrayLiteralExp()
-{
- return op == TOKarrayliteral ? (ArrayLiteralExp *)this : NULL;
-}
-
-AssocArrayLiteralExp *Expression::isAssocArrayLiteralExp()
-{
- return op == TOKassocarrayliteral ? (AssocArrayLiteralExp *)this : NULL;
-}
-
-StructLiteralExp *Expression::isStructLiteralExp()
-{
- return op == TOKstructliteral ? (StructLiteralExp *)this : NULL;
-}
-
-TypeExp *Expression::isTypeExp()
-{
- return op == TOKtype ? (TypeExp *)this : NULL;
-}
-
-ScopeExp *Expression::isScopeExp()
-{
- return op == TOKscope ? (ScopeExp *)this : NULL;
-}
-
-TemplateExp *Expression::isTemplateExp()
-{
- return op == TOKtemplate ? (TemplateExp *)this : NULL;
-}
-
-NewExp *Expression::isNewExp()
-{
- return op == TOKnew ? (NewExp *)this : NULL;
-}
-
-NewAnonClassExp *Expression::isNewAnonClassExp()
-{
- return op == TOKnewanonclass ? (NewAnonClassExp *)this : NULL;
-}
-
-SymOffExp *Expression::isSymOffExp()
-{
- return op == TOKsymoff ? (SymOffExp *)this : NULL;
-}
-
-VarExp *Expression::isVarExp()
-{
- return op == TOKvar ? (VarExp *)this : NULL;
-}
-
-OverExp *Expression::isOverExp()
-{
- return op == TOKoverloadset ? (OverExp *)this : NULL;
-}
-
-FuncExp *Expression::isFuncExp()
-{
- return op == TOKfunction ? (FuncExp *)this : NULL;
-}
-
-DeclarationExp *Expression::isDeclarationExp()
-{
- return op == TOKdeclaration ? (DeclarationExp *)this : NULL;
-}
-
-TypeidExp *Expression::isTypeidExp()
-{
- return op == TOKtypeid ? (TypeidExp *)this : NULL;
-}
-
-TraitsExp *Expression::isTraitsExp()
-{
- return op == TOKtraits ? (TraitsExp *)this : NULL;
-}
-
-HaltExp *Expression::isHaltExp()
-{
- return op == TOKhalt ? (HaltExp *)this : NULL;
-}
-
-IsExp *Expression::isExp()
-{
- return op == TOKis ? (IsExp *)this : NULL;
-}
-
-CompileExp *Expression::isCompileExp()
-{
- return op == TOKmixin ? (CompileExp *)this : NULL;
-}
-
-ImportExp *Expression::isImportExp()
-{
- return op == TOKimport ? (ImportExp *)this : NULL;
-}
-
-AssertExp *Expression::isAssertExp()
-{
- return op == TOKassert ? (AssertExp *)this : NULL;
-}
-
-DotIdExp *Expression::isDotIdExp()
-{
- return op == TOKdotid ? (DotIdExp *)this : NULL;
-}
-
-DotTemplateExp *Expression::isDotTemplateExp()
-{
- return op == TOKdotti ? (DotTemplateExp *)this : NULL;
-}
-
-DotVarExp *Expression::isDotVarExp()
-{
- return op == TOKdotvar ? (DotVarExp *)this : NULL;
-}
-
-DotTemplateInstanceExp *Expression::isDotTemplateInstanceExp()
-{
- return op == TOKdotti ? (DotTemplateInstanceExp *)this : NULL;
-}
-
-DelegateExp *Expression::isDelegateExp()
-{
- return op == TOKdelegate ? (DelegateExp *)this : NULL;
-}
-
-DotTypeExp *Expression::isDotTypeExp()
-{
- return op == TOKdottype ? (DotTypeExp *)this : NULL;
-}
-
-CallExp *Expression::isCallExp()
-{
- return op == TOKcall ? (CallExp *)this : NULL;
-}
-
-AddrExp *Expression::isAddrExp()
-{
- return op == TOKaddress ? (AddrExp *)this : NULL;
-}
-
-PtrExp *Expression::isPtrExp()
-{
- return op == TOKstar ? (PtrExp *)this : NULL;
-}
-
-NegExp *Expression::isNegExp()
-{
- return op == TOKneg ? (NegExp *)this : NULL;
-}
-
-UAddExp *Expression::isUAddExp()
-{
- return op == TOKuadd ? (UAddExp *)this : NULL;
-}
-
-ComExp *Expression::isComExp()
-{
- return op == TOKtilde ? (ComExp *)this : NULL;
-}
-
-NotExp *Expression::isNotExp()
-{
- return op == TOKnot ? (NotExp *)this : NULL;
-}
-
-DeleteExp *Expression::isDeleteExp()
-{
- return op == TOKdelete ? (DeleteExp *)this : NULL;
-}
-
-CastExp *Expression::isCastExp()
-{
- return op == TOKcast ? (CastExp *)this : NULL;
-}
-
-VectorExp *Expression::isVectorExp()
-{
- return op == TOKvector ? (VectorExp *)this : NULL;
-}
-
-VectorArrayExp *Expression::isVectorArrayExp()
-{
- return op == TOKvectorarray ? (VectorArrayExp *)this : NULL;
-}
-
-SliceExp *Expression::isSliceExp()
-{
- return op == TOKslice ? (SliceExp *)this : NULL;
-}
-
-ArrayLengthExp *Expression::isArrayLengthExp()
-{
- return op == TOKarraylength ? (ArrayLengthExp *)this : NULL;
-}
-
-ArrayExp *Expression::isArrayExp()
-{
- return op == TOKarray ? (ArrayExp *)this : NULL;
-}
-
-DotExp *Expression::isDotExp()
-{
- return op == TOKdot ? (DotExp *)this : NULL;
-}
-
-CommaExp *Expression::isCommaExp()
-{
- return op == TOKcomma ? (CommaExp *)this : NULL;
-}
-
-IntervalExp *Expression::isIntervalExp()
-{
- return op == TOKinterval ? (IntervalExp *)this : NULL;
-}
-
-DelegatePtrExp *Expression::isDelegatePtrExp()
-{
- return op == TOKdelegateptr ? (DelegatePtrExp *)this : NULL;
-}
-
-DelegateFuncptrExp *Expression::isDelegateFuncptrExp()
-{
- return op == TOKdelegatefuncptr ? (DelegateFuncptrExp *)this : NULL;
-}
-
-IndexExp *Expression::isIndexExp()
-{
- return op == TOKindex ? (IndexExp *)this : NULL;
-}
-
-PostExp *Expression::isPostExp()
-{
- return (op == TOKplusplus || op == TOKminusminus) ? (PostExp *)this : NULL;
-}
-
-PreExp *Expression::isPreExp()
-{
- return (op == TOKpreplusplus || op == TOKpreminusminus) ? (PreExp *)this : NULL;
-}
-
-AssignExp *Expression::isAssignExp()
-{
- return op == TOKassign ? (AssignExp *)this : NULL;
-}
-
-ConstructExp *Expression::isConstructExp()
-{
- return op == TOKconstruct ? (ConstructExp *)this : NULL;
-}
-
-BlitExp *Expression::isBlitExp()
-{
- return op == TOKblit ? (BlitExp *)this : NULL;
-}
-
-AddAssignExp *Expression::isAddAssignExp()
-{
- return op == TOKaddass ? (AddAssignExp *)this : NULL;
-}
-
-MinAssignExp *Expression::isMinAssignExp()
-{
- return op == TOKminass ? (MinAssignExp *)this : NULL;
-}
-
-MulAssignExp *Expression::isMulAssignExp()
-{
- return op == TOKmulass ? (MulAssignExp *)this : NULL;
-}
-
-
-DivAssignExp *Expression::isDivAssignExp()
-{
- return op == TOKdivass ? (DivAssignExp *)this : NULL;
-}
-
-ModAssignExp *Expression::isModAssignExp()
-{
- return op == TOKmodass ? (ModAssignExp *)this : NULL;
-}
-
-AndAssignExp *Expression::isAndAssignExp()
-{
- return op == TOKandass ? (AndAssignExp *)this : NULL;
-}
-
-OrAssignExp *Expression::isOrAssignExp()
-{
- return op == TOKorass ? (OrAssignExp *)this : NULL;
-}
-
-XorAssignExp *Expression::isXorAssignExp()
-{
- return op == TOKxorass ? (XorAssignExp *)this : NULL;
-}
-
-PowAssignExp *Expression::isPowAssignExp()
-{
- return op == TOKpowass ? (PowAssignExp *)this : NULL;
-}
-
-
-ShlAssignExp *Expression::isShlAssignExp()
-{
- return op == TOKshlass ? (ShlAssignExp *)this : NULL;
-}
-
-ShrAssignExp *Expression::isShrAssignExp()
-{
- return op == TOKshrass ? (ShrAssignExp *)this : NULL;
-}
-
-UshrAssignExp *Expression::isUshrAssignExp()
-{
- return op == TOKushrass ? (UshrAssignExp *)this : NULL;
-}
-
-CatAssignExp *Expression::isCatAssignExp()
-{
- return op == TOKcatass ? (CatAssignExp *)this : NULL;
-}
-
-AddExp *Expression::isAddExp()
-{
- return op == TOKadd ? (AddExp *)this : NULL;
-}
-
-MinExp *Expression::isMinExp()
-{
- return op == TOKmin ? (MinExp *)this : NULL;
-}
-
-CatExp *Expression::isCatExp()
-{
- return op == TOKcat ? (CatExp *)this : NULL;
-}
-
-MulExp *Expression::isMulExp()
-{
- return op == TOKmul ? (MulExp *)this : NULL;
-}
-
-DivExp *Expression::isDivExp()
-{
- return op == TOKdiv ? (DivExp *)this : NULL;
-}
-
-ModExp *Expression::isModExp()
-{
- return op == TOKmod ? (ModExp *)this : NULL;
-}
-
-PowExp *Expression::isPowExp()
-{
- return op == TOKpow ? (PowExp *)this : NULL;
-}
-
-ShlExp *Expression::isShlExp()
-{
- return op == TOKshl ? (ShlExp *)this : NULL;
-}
-
-ShrExp *Expression::isShrExp()
-{
- return op == TOKshr ? (ShrExp *)this : NULL;
-}
-
-UshrExp *Expression::isUshrExp()
-{
- return op == TOKushr ? (UshrExp *)this : NULL;
-}
-
-AndExp *Expression::isAndExp()
-{
- return op == TOKand ? (AndExp *)this : NULL;
-}
-
-OrExp *Expression::isOrExp()
-{
- return op == TOKor ? (OrExp *)this : NULL;
-}
-
-XorExp *Expression::isXorExp()
-{
- return op == TOKxor ? (XorExp *)this : NULL;
-}
-
-LogicalExp *Expression::isLogicalExp()
-{
- return (op == TOKandand || op == TOKoror) ? (LogicalExp *)this : NULL;
-}
-
-InExp *Expression::isInExp()
-{
- return op == TOKin ? (InExp *)this : NULL;
-}
-
-RemoveExp *Expression::isRemoveExp()
-{
- return op == TOKremove ? (RemoveExp *)this : NULL;
-}
-
-EqualExp *Expression::isEqualExp()
-{
- return (op == TOKequal || op == TOKnotequal) ? (EqualExp *)this : NULL;
-}
-
-IdentityExp *Expression::isIdentityExp()
-{
- return (op == TOKidentity || op == TOKnotidentity) ? (IdentityExp *)this : NULL;
-}
-
-CondExp *Expression::isCondExp()
-{
- return op == TOKquestion ? (CondExp *)this : NULL;
-}
-
-DefaultInitExp *Expression::isDefaultInitExp()
-{
- return op == TOKdefault ? (DefaultInitExp *)this : NULL;
-}
-
-FileInitExp *Expression::isFileInitExp()
-{
- return (op == TOKfile || op == TOKfilefullpath) ? (FileInitExp *)this : NULL;
-}
-
-LineInitExp *Expression::isLineInitExp()
-{
- return op == TOKline ? (LineInitExp *)this : NULL;
-}
-
-ModuleInitExp *Expression::isModuleInitExp()
-{
- return op == TOKmodulestring ? (ModuleInitExp *)this : NULL;
-}
-
-FuncInitExp *Expression::isFuncInitExp()
-{
- return op == TOKfuncstring ? (FuncInitExp *)this : NULL;
-}
-
-PrettyFuncInitExp *Expression::isPrettyFuncInitExp()
-{
- return op == TOKprettyfunc ? (PrettyFuncInitExp *)this : NULL;
-}
-
-ClassReferenceExp *Expression::isClassReferenceExp()
-{
- return op == TOKclassreference ? (ClassReferenceExp *)this : NULL;
-}
-
-
-/****************************************
- * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE__FULL_PATH__ to loc.
- */
-
-Expression *Expression::resolveLoc(Loc, Scope *)
-{
- return this;
-}
-
-Expressions *Expression::arraySyntaxCopy(Expressions *exps)
-{
- Expressions *a = NULL;
- if (exps)
- {
- a = new Expressions();
- a->setDim(exps->length);
- for (size_t i = 0; i < a->length; i++)
- {
- Expression *e = (*exps)[i];
- (*a)[i] = e ? e->syntaxCopy() : NULL;
- }
- }
- return a;
-}
-
-/************************************************
- * Destructors are attached to VarDeclarations.
- * Hence, if expression returns a temp that needs a destructor,
- * make sure and create a VarDeclaration for that temp.
- */
-
-Expression *Expression::addDtorHook(Scope *)
-{
- return this;
-}
-
-/******************************** IntegerExp **************************/
-
-IntegerExp::IntegerExp(Loc loc, dinteger_t value, Type *type)
- : Expression(loc, TOKint64, sizeof(IntegerExp))
-{
- //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type->toChars() : "");
- assert(type);
- if (!type->isscalar())
- {
- //printf("%s, loc = %d\n", toChars(), loc.linnum);
- if (type->ty != Terror)
- error("integral constant must be scalar type, not %s", type->toChars());
- type = Type::terror;
- }
- this->type = type;
- setInteger(value);
-}
-
-IntegerExp::IntegerExp(dinteger_t value)
- : Expression(Loc(), TOKint64, sizeof(IntegerExp))
-{
- this->type = Type::tint32;
- this->value = (d_int32) value;
-}
-
-IntegerExp *IntegerExp::create(Loc loc, dinteger_t value, Type *type)
-{
- return new IntegerExp(loc, value, type);
-}
-
-bool IntegerExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (((Expression *)o)->op == TOKint64)
- {
- IntegerExp *ne = (IntegerExp *)o;
- if (type->toHeadMutable()->equals(ne->type->toHeadMutable()) &&
- value == ne->value)
- {
- return true;
- }
- }
- return false;
-}
-
-void IntegerExp::setInteger(dinteger_t value)
-{
- this->value = value;
- normalize();
-}
-
-void IntegerExp::normalize()
-{
- /* 'Normalize' the value of the integer to be in range of the type
- */
- switch (type->toBasetype()->ty)
- {
- case Tbool: value = (value != 0); break;
- case Tint8: value = (d_int8) value; break;
- case Tchar:
- case Tuns8: value = (d_uns8) value; break;
- case Tint16: value = (d_int16) value; break;
- case Twchar:
- case Tuns16: value = (d_uns16) value; break;
- case Tint32: value = (d_int32) value; break;
- case Tdchar:
- case Tuns32: value = (d_uns32) value; break;
- case Tint64: value = (d_int64) value; break;
- case Tuns64: value = (d_uns64) value; break;
- case Tpointer:
- if (target.ptrsize == 8)
- value = (d_uns64) value;
- else if (target.ptrsize == 4)
- value = (d_uns32) value;
- else if (target.ptrsize == 2)
- value = (d_uns16) value;
- else
- assert(0);
- break;
- default:
- break;
- }
-}
-
-dinteger_t IntegerExp::toInteger()
-{
- normalize(); // necessary until we fix all the paints of 'type'
- return value;
-}
-
-real_t IntegerExp::toReal()
-{
- normalize(); // necessary until we fix all the paints of 'type'
- Type *t = type->toBasetype();
- if (t->ty == Tuns64)
- return ldouble((d_uns64)value);
- else
- return ldouble((d_int64)value);
-}
-
-real_t IntegerExp::toImaginary()
-{
- return CTFloat::zero;
-}
-
-complex_t IntegerExp::toComplex()
-{
- return (complex_t)toReal();
-}
-
-bool IntegerExp::isBool(bool result)
-{
- bool r = toInteger() != 0;
- return result ? r : !r;
-}
-
-Expression *IntegerExp::toLvalue(Scope *, Expression *e)
-{
- if (!e)
- e = this;
- else if (!loc.filename)
- loc = e->loc;
- e->error("constant %s is not an lvalue", e->toChars());
- return new ErrorExp();
-}
-
-/******************************** ErrorExp **************************/
-
-/* Use this expression for error recovery.
- * It should behave as a 'sink' to prevent further cascaded error messages.
- */
-
-ErrorExp::ErrorExp()
- : Expression(Loc(), TOKerror, sizeof(ErrorExp))
-{
- type = Type::terror;
-}
-
-Expression *ErrorExp::toLvalue(Scope *, Expression *)
-{
- return this;
-}
-
-/******************************** RealExp **************************/
-
-RealExp::RealExp(Loc loc, real_t value, Type *type)
- : Expression(loc, TOKfloat64, sizeof(RealExp))
-{
- //printf("RealExp::RealExp(%Lg)\n", value);
- this->value = value;
- this->type = type;
-}
-
-RealExp *RealExp::create(Loc loc, real_t value, Type *type)
-{
- return new RealExp(loc, value,type);
-}
-
-dinteger_t RealExp::toInteger()
-{
- return (sinteger_t) toReal();
-}
-
-uinteger_t RealExp::toUInteger()
-{
- return (uinteger_t) toReal();
-}
-
-real_t RealExp::toReal()
-{
- return type->isreal() ? value : CTFloat::zero;
-}
-
-real_t RealExp::toImaginary()
-{
- return type->isreal() ? CTFloat::zero : value;
-}
-
-complex_t RealExp::toComplex()
-{
- return complex_t(toReal(), toImaginary());
-}
-
-/********************************
- * Test to see if two reals are the same.
- * Regard NaN's as equivalent.
- * Regard +0 and -0 as different.
- */
-
-int RealEquals(real_t x1, real_t x2)
-{
- return (CTFloat::isNaN(x1) && CTFloat::isNaN(x2)) ||
- CTFloat::isIdentical(x1, x2);
-}
-
-bool RealExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (((Expression *)o)->op == TOKfloat64)
- {
- RealExp *ne = (RealExp *)o;
- if (type->toHeadMutable()->equals(ne->type->toHeadMutable()) &&
- RealEquals(value, ne->value))
- {
- return true;
- }
- }
- return false;
-}
-
-bool RealExp::isBool(bool result)
-{
- return result ? (bool)value : !(bool)value;
-}
-
-/******************************** ComplexExp **************************/
-
-ComplexExp::ComplexExp(Loc loc, complex_t value, Type *type)
- : Expression(loc, TOKcomplex80, sizeof(ComplexExp)), value(value)
-{
- this->type = type;
- //printf("ComplexExp::ComplexExp(%s)\n", toChars());
-}
-
-ComplexExp *ComplexExp::create(Loc loc, complex_t value, Type *type)
-{
- return new ComplexExp(loc, value, type);
-}
-
-dinteger_t ComplexExp::toInteger()
-{
- return (sinteger_t) toReal();
-}
-
-uinteger_t ComplexExp::toUInteger()
-{
- return (uinteger_t) toReal();
-}
-
-real_t ComplexExp::toReal()
-{
- return creall(value);
-}
-
-real_t ComplexExp::toImaginary()
-{
- return cimagl(value);
-}
-
-complex_t ComplexExp::toComplex()
-{
- return value;
-}
-
-bool ComplexExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (((Expression *)o)->op == TOKcomplex80)
- {
- ComplexExp *ne = (ComplexExp *)o;
- if (type->toHeadMutable()->equals(ne->type->toHeadMutable()) &&
- RealEquals(creall(value), creall(ne->value)) &&
- RealEquals(cimagl(value), cimagl(ne->value)))
- {
- return true;
- }
- }
- return false;
-}
-
-bool ComplexExp::isBool(bool result)
-{
- if (result)
- return (bool)(value);
- else
- return !value;
-}
-
-/******************************** IdentifierExp **************************/
-
-IdentifierExp::IdentifierExp(Loc loc, Identifier *ident)
- : Expression(loc, TOKidentifier, sizeof(IdentifierExp))
-{
- this->ident = ident;
-}
-
-IdentifierExp *IdentifierExp::create(Loc loc, Identifier *ident)
-{
- return new IdentifierExp(loc, ident);
-}
-
-bool IdentifierExp::isLvalue()
-{
- return true;
-}
-
-Expression *IdentifierExp::toLvalue(Scope *, Expression *)
-{
- return this;
-}
-
-/******************************** DollarExp **************************/
-
-DollarExp::DollarExp(Loc loc)
- : IdentifierExp(loc, Id::dollar)
-{
-}
-
-/******************************** DsymbolExp **************************/
-
-DsymbolExp::DsymbolExp(Loc loc, Dsymbol *s, bool hasOverloads)
- : Expression(loc, TOKdsymbol, sizeof(DsymbolExp))
-{
- this->s = s;
- this->hasOverloads = hasOverloads;
-}
-
-/****************************************
- * Resolve a symbol `s` and wraps it in an expression object.
- * Params:
- * hasOverloads = works if the aliased symbol is a function.
- * true: it's overloaded and will be resolved later.
- * false: it's exact function symbol.
- */
-Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads)
-{
-Lagain:
- Expression *e;
-
- //printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars());
- //printf("s = '%s', s->kind = '%s'\n", s->toChars(), s->kind());
- Dsymbol *olds = s;
- Declaration *d = s->isDeclaration();
- if (d && (d->storage_class & STCtemplateparameter))
- {
- s = s->toAlias();
- }
- else
- {
- if (!s->isFuncDeclaration()) // functions are checked after overloading
- {
- s->checkDeprecated(loc, sc);
- if (d)
- d->checkDisabled(loc, sc);
- }
-
- // Bugzilla 12023: if 's' is a tuple variable, the tuple is returned.
- s = s->toAlias();
-
- //printf("s = '%s', s->kind = '%s', s->needThis() = %p\n", s->toChars(), s->kind(), s->needThis());
- if (s != olds && !s->isFuncDeclaration())
- {
- s->checkDeprecated(loc, sc);
- if (d)
- d->checkDisabled(loc, sc);
- }
- }
-
- if (EnumMember *em = s->isEnumMember())
- {
- return em->getVarExp(loc, sc);
- }
- if (VarDeclaration *v = s->isVarDeclaration())
- {
- //printf("Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars());
- if (!v->type || // during variable type inference
- (!v->type->deco && v->inuse)) // during variable type semantic
- {
- if (v->inuse) // variable type depends on the variable itself
- ::error(loc, "circular reference to %s `%s`", v->kind(), v->toPrettyChars());
- else // variable type cannot be determined
- ::error(loc, "forward reference to %s `%s`", v->kind(), v->toPrettyChars());
- return new ErrorExp();
- }
- if (v->type->ty == Terror)
- return new ErrorExp();
-
- if ((v->storage_class & STCmanifest) && v->_init)
- {
- if (v->inuse)
- {
- ::error(loc, "circular initialization of %s `%s`", v->kind(), v->toPrettyChars());
- return new ErrorExp();
- }
-
- e = v->expandInitializer(loc);
- v->inuse++;
- e = expressionSemantic(e, sc);
- v->inuse--;
- return e;
- }
-
- // Change the ancestor lambdas to delegate before hasThis(sc) call.
- if (v->checkNestedReference(sc, loc))
- return new ErrorExp();
-
- if (v->needThis() && hasThis(sc))
- e = new DotVarExp(loc, new ThisExp(loc), v);
- else
- e = new VarExp(loc, v);
- e = expressionSemantic(e, sc);
- return e;
- }
- if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
- {
- //printf("'%s' is a function literal\n", fld->toChars());
- e = new FuncExp(loc, fld);
- return expressionSemantic(e, sc);
- }
- if (FuncDeclaration *f = s->isFuncDeclaration())
- {
- f = f->toAliasFunc();
- if (!f->functionSemantic())
- return new ErrorExp();
-
- if (!hasOverloads && f->checkForwardRef(loc))
- return new ErrorExp();
-
- FuncDeclaration *fd = s->isFuncDeclaration();
- fd->type = f->type;
- return new VarExp(loc, fd, hasOverloads);
- }
- if (OverDeclaration *od = s->isOverDeclaration())
- {
- e = new VarExp(loc, od, true);
- e->type = Type::tvoid;
- return e;
- }
- if (OverloadSet *o = s->isOverloadSet())
- {
- //printf("'%s' is an overload set\n", o->toChars());
- return new OverExp(loc, o);
- }
-
- if (Import *imp = s->isImport())
- {
- if (!imp->pkg)
- {
- ::error(loc, "forward reference of import %s", imp->toChars());
- return new ErrorExp();
- }
- ScopeExp *ie = new ScopeExp(loc, imp->pkg);
- return expressionSemantic(ie, sc);
- }
- if (Package *pkg = s->isPackage())
- {
- ScopeExp *ie = new ScopeExp(loc, pkg);
- return expressionSemantic(ie, sc);
- }
- if (Module *mod = s->isModule())
- {
- ScopeExp *ie = new ScopeExp(loc, mod);
- return expressionSemantic(ie, sc);
- }
-
- if (Nspace *ns = s->isNspace())
- {
- ScopeExp *ie = new ScopeExp(loc, ns);
- return expressionSemantic(ie, sc);
- }
-
- if (Type *t = s->getType())
- {
- return expressionSemantic(new TypeExp(loc, t), sc);
- }
-
- if (TupleDeclaration *tup = s->isTupleDeclaration())
- {
- if (tup->needThis() && hasThis(sc))
- e = new DotVarExp(loc, new ThisExp(loc), tup);
- else
- e = new TupleExp(loc, tup);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- if (TemplateInstance *ti = s->isTemplateInstance())
- {
- dsymbolSemantic(ti, sc);
- if (!ti->inst || ti->errors)
- return new ErrorExp();
- s = ti->toAlias();
- if (!s->isTemplateInstance())
- goto Lagain;
- e = new ScopeExp(loc, ti);
- e = expressionSemantic(e, sc);
- return e;
- }
- if (TemplateDeclaration *td = s->isTemplateDeclaration())
- {
- Dsymbol *p = td->toParent2();
- FuncDeclaration *fdthis = hasThis(sc);
- AggregateDeclaration *ad = p ? p->isAggregateDeclaration() : NULL;
- if (fdthis && ad && isAggregate(fdthis->vthis->type) == ad &&
- (td->_scope->stc & STCstatic) == 0)
- {
- e = new DotTemplateExp(loc, new ThisExp(loc), td);
- }
- else
- e = new TemplateExp(loc, td);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- ::error(loc, "%s `%s` is not a variable", s->kind(), s->toChars());
- return new ErrorExp();
-}
-
-bool DsymbolExp::isLvalue()
-{
- return true;
-}
-
-Expression *DsymbolExp::toLvalue(Scope *, Expression *)
-{
- return this;
-}
-
-/******************************** ThisExp **************************/
-
-ThisExp::ThisExp(Loc loc)
- : Expression(loc, TOKthis, sizeof(ThisExp))
-{
- //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
- var = NULL;
-}
-
-bool ThisExp::isBool(bool result)
-{
- return result ? true : false;
-}
-
-bool ThisExp::isLvalue()
-{
- // Class `this` should be an rvalue; struct `this` should be an lvalue.
- return type->toBasetype()->ty != Tclass;
-}
-
-Expression *ThisExp::toLvalue(Scope *sc, Expression *e)
-{
- if (type->toBasetype()->ty == Tclass)
- {
- // Class `this` is an rvalue; struct `this` is an lvalue.
- return Expression::toLvalue(sc, e);
- }
- return this;
-}
-
-/******************************** SuperExp **************************/
-
-SuperExp::SuperExp(Loc loc)
- : ThisExp(loc)
-{
- op = TOKsuper;
-}
-
-/******************************** NullExp **************************/
-
-NullExp::NullExp(Loc loc, Type *type)
- : Expression(loc, TOKnull, sizeof(NullExp))
-{
- committed = 0;
- this->type = type;
-}
-
-bool NullExp::equals(RootObject *o)
-{
- if (o && o->dyncast() == DYNCAST_EXPRESSION)
- {
- Expression *e = (Expression *)o;
- if (e->op == TOKnull &&
- type->equals(e->type))
- {
- return true;
- }
- }
- return false;
-}
-
-bool NullExp::isBool(bool result)
-{
- return result ? false : true;
-}
-
-StringExp *NullExp::toStringExp()
-{
- if (implicitConvTo(Type::tstring))
- {
- StringExp *se = new StringExp(loc, (char*)mem.xcalloc(1, 1), 0);
- se->type = Type::tstring;
- return se;
- }
- return NULL;
-}
-
-/******************************** StringExp **************************/
-
-StringExp::StringExp(Loc loc, char *string)
- : Expression(loc, TOKstring, sizeof(StringExp))
-{
- this->string = string;
- this->len = strlen(string);
- this->sz = 1;
- this->committed = 0;
- this->postfix = 0;
- this->ownedByCtfe = OWNEDcode;
-}
-
-StringExp::StringExp(Loc loc, void *string, size_t len)
- : Expression(loc, TOKstring, sizeof(StringExp))
-{
- this->string = string;
- this->len = len;
- this->sz = 1;
- this->committed = 0;
- this->postfix = 0;
- this->ownedByCtfe = OWNEDcode;
-}
-
-StringExp::StringExp(Loc loc, void *string, size_t len, utf8_t postfix)
- : Expression(loc, TOKstring, sizeof(StringExp))
-{
- this->string = string;
- this->len = len;
- this->sz = 1;
- this->committed = 0;
- this->postfix = postfix;
- this->ownedByCtfe = OWNEDcode;
-}
-
-StringExp *StringExp::create(Loc loc, char *s)
-{
- return new StringExp(loc, s);
-}
-
-StringExp *StringExp::create(Loc loc, void *string, size_t len)
-{
- return new StringExp(loc, string, len);
-}
-
-bool StringExp::equals(RootObject *o)
-{
- //printf("StringExp::equals('%s') %s\n", o->toChars(), toChars());
- if (o && o->dyncast() == DYNCAST_EXPRESSION)
- {
- Expression *e = (Expression *)o;
- if (e->op == TOKstring)
- {
- return compare(o) == 0;
- }
- }
- return false;
-}
-
-/**********************************
- * Return the number of code units the string would be if it were re-encoded
- * as tynto.
- * Params:
- * tynto = code unit type of the target encoding
- * Returns:
- * number of code units
- */
-
-size_t StringExp::numberOfCodeUnits(int tynto) const
-{
- int encSize;
- switch (tynto)
- {
- case 0: return len;
- case Tchar: encSize = 1; break;
- case Twchar: encSize = 2; break;
- case Tdchar: encSize = 4; break;
- default:
- assert(0);
- }
- if (sz == encSize)
- return len;
-
- size_t result = 0;
- dchar_t c;
-
- switch (sz)
- {
- case 1:
- for (size_t u = 0; u < len;)
- {
- if (const char *p = utf_decodeChar((utf8_t *)string, len, &u, &c))
- {
- error("%s", p);
- return 0;
- }
- result += utf_codeLength(encSize, c);
- }
- break;
-
- case 2:
- for (size_t u = 0; u < len;)
- {
- if (const char *p = utf_decodeWchar((utf16_t *)string, len, &u, &c))
- {
- error("%s", p);
- return 0;
- }
- result += utf_codeLength(encSize, c);
- }
- break;
-
- case 4:
- for (size_t u = 0; u < len;)
- {
- c = *((utf32_t *)((char *)string + u));
- u += 4;
- result += utf_codeLength(encSize, c);
- }
- break;
-
- default:
- assert(0);
- }
- return result;
-}
-
-/**********************************************
- * Write the contents of the string to dest.
- * Use numberOfCodeUnits() to determine size of result.
- * Params:
- * dest = destination
- * tyto = encoding type of the result
- * zero = add terminating 0
- */
-void StringExp::writeTo(void *dest, bool zero, int tyto) const
-{
- int encSize;
- switch (tyto)
- {
- case 0: encSize = sz; break;
- case Tchar: encSize = 1; break;
- case Twchar: encSize = 2; break;
- case Tdchar: encSize = 4; break;
- default:
- assert(0);
- }
- if (sz == encSize)
- {
- memcpy(dest, string, len * sz);
- if (zero)
- memset((char *)dest + len * sz, 0, sz);
- }
- else
- assert(0);
-}
-
-/**************************************************
- * If the string data is UTF-8 and can be accessed directly,
- * return a pointer to it.
- * Do not assume a terminating 0.
- * Returns:
- * pointer to string data if possible, null if not
- */
-char *StringExp::toPtr()
-{
- return (sz == 1) ? (char*)string : NULL;
-}
-
-StringExp *StringExp::toStringExp()
-{
- return this;
-}
-
-/****************************************
- * Convert string to char[].
- */
-
-StringExp *StringExp::toUTF8(Scope *sc)
-{
- if (sz != 1)
- { // Convert to UTF-8 string
- committed = 0;
- Expression *e = castTo(sc, Type::tchar->arrayOf());
- e = e->optimize(WANTvalue);
- assert(e->op == TOKstring);
- StringExp *se = (StringExp *)e;
- assert(se->sz == 1);
- return se;
- }
- return this;
-}
-
-int StringExp::compare(RootObject *obj)
-{
- //printf("StringExp::compare()\n");
- // Used to sort case statement expressions so we can do an efficient lookup
- StringExp *se2 = (StringExp *)(obj);
-
- // This is a kludge so isExpression() in template.c will return 5
- // for StringExp's.
- if (!se2)
- return 5;
-
- assert(se2->op == TOKstring);
-
- size_t len1 = len;
- size_t len2 = se2->len;
-
- //printf("sz = %d, len1 = %d, len2 = %d\n", sz, (int)len1, (int)len2);
- if (len1 == len2)
- {
- switch (sz)
- {
- case 1:
- return memcmp((char *)string, (char *)se2->string, len1);
-
- case 2:
- {
- d_uns16 *s1 = (d_uns16 *)string;
- d_uns16 *s2 = (d_uns16 *)se2->string;
-
- for (size_t u = 0; u < len; u++)
- {
- if (s1[u] != s2[u])
- return s1[u] - s2[u];
- }
- }
- break;
-
- case 4:
- {
- d_uns32 *s1 = (d_uns32 *)string;
- d_uns32 *s2 = (d_uns32 *)se2->string;
-
- for (size_t u = 0; u < len; u++)
- {
- if (s1[u] != s2[u])
- return s1[u] - s2[u];
- }
- }
- break;
-
- default:
- assert(0);
- }
- }
- return (int)(len1 - len2);
-}
-
-bool StringExp::isBool(bool result)
-{
- return result ? true : false;
-}
-
-
-bool StringExp::isLvalue()
-{
- /* string literal is rvalue in default, but
- * conversion to reference of static array is only allowed.
- */
- return (type && type->toBasetype()->ty == Tsarray);
-}
-
-Expression *StringExp::toLvalue(Scope *sc, Expression *e)
-{
- //printf("StringExp::toLvalue(%s) type = %s\n", toChars(), type ? type->toChars() : NULL);
- return (type && type->toBasetype()->ty == Tsarray)
- ? this : Expression::toLvalue(sc, e);
-}
-
-Expression *StringExp::modifiableLvalue(Scope *, Expression *)
-{
- error("cannot modify string literal %s", toChars());
- return new ErrorExp();
-}
-
-unsigned StringExp::charAt(uinteger_t i) const
-{ unsigned value;
-
- switch (sz)
- {
- case 1:
- value = ((utf8_t *)string)[(size_t)i];
- break;
-
- case 2:
- value = ((unsigned short *)string)[(size_t)i];
- break;
-
- case 4:
- value = ((unsigned int *)string)[(size_t)i];
- break;
-
- default:
- assert(0);
- break;
- }
- return value;
-}
-
-/************************ ArrayLiteralExp ************************************/
-
-// [ e1, e2, e3, ... ]
-
-ArrayLiteralExp::ArrayLiteralExp(Loc loc, Type *type, Expressions *elements)
- : Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
-{
- this->basis = NULL;
- this->type = type;
- this->elements = elements;
- this->ownedByCtfe = OWNEDcode;
-}
-
-ArrayLiteralExp::ArrayLiteralExp(Loc loc, Type *type, Expression *e)
- : Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
-{
- this->basis = NULL;
- this->type = type;
- elements = new Expressions;
- elements->push(e);
- this->ownedByCtfe = OWNEDcode;
-}
-
-ArrayLiteralExp::ArrayLiteralExp(Loc loc, Type *type, Expression *basis, Expressions *elements)
- : Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
-{
- this->basis = basis;
- this->type = type;
- this->elements = elements;
- this->ownedByCtfe = OWNEDcode;
-}
-
-ArrayLiteralExp *ArrayLiteralExp::create(Loc loc, Expressions *elements)
-{
- return new ArrayLiteralExp(loc, NULL, elements);
-}
-
-bool ArrayLiteralExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (o && o->dyncast() == DYNCAST_EXPRESSION &&
- ((Expression *)o)->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ae = (ArrayLiteralExp *)o;
- if (elements->length != ae->elements->length)
- return false;
- if (elements->length == 0 &&
- !type->equals(ae->type))
- {
- return false;
- }
- for (size_t i = 0; i < elements->length; i++)
- {
- Expression *e1 = (*elements)[i];
- Expression *e2 = (*ae->elements)[i];
- if (!e1)
- e1 = basis;
- if (!e2)
- e2 = basis;
- if (e1 != e2 &&
- (!e1 || !e2 || !e1->equals(e2)))
- return false;
- }
- return true;
- }
- return false;
-}
-
-Expression *ArrayLiteralExp::syntaxCopy()
-{
- return new ArrayLiteralExp(loc,
- NULL,
- basis ? basis->syntaxCopy() : NULL,
- arraySyntaxCopy(elements));
-}
-
-Expression *ArrayLiteralExp::getElement(size_t i)
-{
- Expression *el = (*elements)[i];
- if (!el)
- el = basis;
- return el;
-}
-
-static void appendArrayLiteral(Expressions *elems, ArrayLiteralExp *ale)
-{
- if (!ale->elements)
- return;
- size_t d = elems->length;
- elems->append(ale->elements);
- for (size_t i = d; i < elems->length; i++)
- {
- Expression *el = (*elems)[i];
- if (!el)
- (*elems)[i] = ale->basis;
- }
-}
-
-/* Copy element `Expressions` in the parameters when they're `ArrayLiteralExp`s.
- * Params:
- * e1 = If it's ArrayLiteralExp, its `elements` will be copied.
- * Otherwise, `e1` itself will be pushed into the new `Expressions`.
- * e2 = If it's not `null`, it will be pushed/appended to the new
- * `Expressions` by the same way with `e1`.
- * Returns:
- * Newly allocated `Expressions`. Note that it points to the original
- * `Expression` values in e1 and e2.
- */
-Expressions* ArrayLiteralExp::copyElements(Expression *e1, Expression *e2)
-{
- Expressions *elems = new Expressions();
-
- if (e1->op == TOKarrayliteral)
- appendArrayLiteral(elems, (ArrayLiteralExp *)e1);
- else
- elems->push(e1);
-
- if (e2)
- {
- if (e2->op == TOKarrayliteral)
- appendArrayLiteral(elems, (ArrayLiteralExp *)e2);
- else
- elems->push(e2);
- }
-
- return elems;
-}
-
-bool ArrayLiteralExp::isBool(bool result)
-{
- size_t dim = elements ? elements->length : 0;
- return result ? (dim != 0) : (dim == 0);
-}
-
-StringExp *ArrayLiteralExp::toStringExp()
-{
- TY telem = type->nextOf()->toBasetype()->ty;
-
- if (telem == Tchar || telem == Twchar || telem == Tdchar ||
- (telem == Tvoid && (!elements || elements->length == 0)))
- {
- unsigned char sz = 1;
- if (telem == Twchar) sz = 2;
- else if (telem == Tdchar) sz = 4;
-
- OutBuffer buf;
- if (elements)
- {
- for (size_t i = 0; i < elements->length; ++i)
- {
- Expression *ch = getElement(i);
- if (ch->op != TOKint64)
- return NULL;
- if (sz == 1)
- buf.writeByte((unsigned)ch->toInteger());
- else if (sz == 2)
- buf.writeword((unsigned)ch->toInteger());
- else
- buf.write4((unsigned)ch->toInteger());
- }
- }
- char prefix;
- if (sz == 1) { prefix = 'c'; buf.writeByte(0); }
- else if (sz == 2) { prefix = 'w'; buf.writeword(0); }
- else { prefix = 'd'; buf.write4(0); }
-
- const size_t len = buf.length() / sz - 1;
- StringExp *se = new StringExp(loc, buf.extractData(), len, prefix);
- se->sz = sz;
- se->type = type;
- return se;
- }
- return NULL;
-}
-
-/************************ AssocArrayLiteralExp ************************************/
-
-// [ key0 : value0, key1 : value1, ... ]
-
-AssocArrayLiteralExp::AssocArrayLiteralExp(Loc loc,
- Expressions *keys, Expressions *values)
- : Expression(loc, TOKassocarrayliteral, sizeof(AssocArrayLiteralExp))
-{
- assert(keys->length == values->length);
- this->keys = keys;
- this->values = values;
- this->ownedByCtfe = OWNEDcode;
-}
-
-bool AssocArrayLiteralExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (o && o->dyncast() == DYNCAST_EXPRESSION &&
- ((Expression *)o)->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)o;
- if (keys->length != ae->keys->length)
- return false;
- size_t count = 0;
- for (size_t i = 0; i < keys->length; i++)
- {
- for (size_t j = 0; j < ae->keys->length; j++)
- {
- if ((*keys)[i]->equals((*ae->keys)[j]))
- {
- if (!(*values)[i]->equals((*ae->values)[j]))
- return false;
- ++count;
- }
- }
- }
- return count == keys->length;
- }
- return false;
-}
-
-Expression *AssocArrayLiteralExp::syntaxCopy()
-{
- return new AssocArrayLiteralExp(loc,
- arraySyntaxCopy(keys), arraySyntaxCopy(values));
-}
-
-bool AssocArrayLiteralExp::isBool(bool result)
-{
- size_t dim = keys->length;
- return result ? (dim != 0) : (dim == 0);
-}
-
-/************************ StructLiteralExp ************************************/
-
-// sd( e1, e2, e3, ... )
-
-StructLiteralExp::StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions *elements, Type *stype)
- : Expression(loc, TOKstructliteral, sizeof(StructLiteralExp))
-{
- this->sd = sd;
- if (!elements)
- elements = new Expressions();
- this->elements = elements;
- this->stype = stype;
- this->useStaticInit = false;
- this->sym = NULL;
- this->ownedByCtfe = OWNEDcode;
- this->origin = this;
- this->stageflags = 0;
- this->inlinecopy = NULL;
- //printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars());
-}
-
-StructLiteralExp *StructLiteralExp::create(Loc loc, StructDeclaration *sd, void *elements, Type *stype)
-{
- return new StructLiteralExp(loc, sd, (Expressions *)elements, stype);
-}
-
-bool StructLiteralExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (o && o->dyncast() == DYNCAST_EXPRESSION &&
- ((Expression *)o)->op == TOKstructliteral)
- {
- StructLiteralExp *se = (StructLiteralExp *)o;
- if (!type->equals(se->type))
- return false;
- if (elements->length != se->elements->length)
- return false;
- for (size_t i = 0; i < elements->length; i++)
- {
- Expression *e1 = (*elements)[i];
- Expression *e2 = (*se->elements)[i];
- if (e1 != e2 &&
- (!e1 || !e2 || !e1->equals(e2)))
- return false;
- }
- return true;
- }
- return false;
-}
-
-Expression *StructLiteralExp::syntaxCopy()
-{
- StructLiteralExp *exp = new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), type ? type : stype);
- exp->origin = this;
- return exp;
-}
-
-Expression *StructLiteralExp::addDtorHook(Scope *sc)
-{
- /* If struct requires a destructor, rewrite as:
- * (S tmp = S()),tmp
- * so that the destructor can be hung on tmp.
- */
- if (sd->dtor && sc->func)
- {
- /* Make an identifier for the temporary of the form:
- * __sl%s%d, where %s is the struct name
- */
- const size_t len = 10;
- char buf[len + 1];
- buf[len] = 0;
- strcpy(buf, "__sl");
- strncat(buf, sd->ident->toChars(), len - 4 - 1);
- assert(buf[len] == 0);
-
- VarDeclaration *tmp = copyToTemp(0, buf, this);
- Expression *ae = new DeclarationExp(loc, tmp);
- Expression *e = new CommaExp(loc, ae, new VarExp(loc, tmp));
- e = expressionSemantic(e, sc);
- return e;
- }
- return this;
-}
-
-/**************************************
- * Gets expression at offset of type.
- * Returns NULL if not found.
- */
-
-Expression *StructLiteralExp::getField(Type *type, unsigned offset)
-{
- //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
- // /*toChars()*/"", type->toChars(), offset);
- Expression *e = NULL;
- int i = getFieldIndex(type, offset);
-
- if (i != -1)
- {
- //printf("\ti = %d\n", i);
- if (i == (int)sd->fields.length - 1 && sd->isNested())
- return NULL;
-
- assert(i < (int)elements->length);
- e = (*elements)[i];
- if (e)
- {
- //printf("e = %s, e->type = %s\n", e->toChars(), e->type->toChars());
-
- /* If type is a static array, and e is an initializer for that array,
- * then the field initializer should be an array literal of e.
- */
- if (e->type->castMod(0) != type->castMod(0) && type->ty == Tsarray)
- { TypeSArray *tsa = (TypeSArray *)type;
- size_t length = (size_t)tsa->dim->toInteger();
- Expressions *z = new Expressions;
- z->setDim(length);
- for (size_t q = 0; q < length; ++q)
- (*z)[q] = e->copy();
- e = new ArrayLiteralExp(loc, type, z);
- }
- else
- {
- e = e->copy();
- e->type = type;
- }
- if (useStaticInit && e->op == TOKstructliteral &&
- e->type->needsNested())
- {
- StructLiteralExp *se = (StructLiteralExp *)e;
- se->useStaticInit = true;
- }
- }
- }
- return e;
-}
-
-/************************************
- * Get index of field.
- * Returns -1 if not found.
- */
-
-int StructLiteralExp::getFieldIndex(Type *type, unsigned offset)
-{
- /* Find which field offset is by looking at the field offsets
- */
- if (elements->length)
- {
- for (size_t i = 0; i < sd->fields.length; i++)
- {
- VarDeclaration *v = sd->fields[i];
-
- if (offset == v->offset &&
- type->size() == v->type->size())
- {
- /* context field might not be filled. */
- if (i == sd->fields.length - 1 && sd->isNested())
- return (int)i;
- Expression *e = (*elements)[i];
- if (e)
- {
- return (int)i;
- }
- break;
- }
- }
- }
- return -1;
-}
-
-/************************ TypeDotIdExp ************************************/
-
-/* Things like:
- * int.size
- * foo.size
- * (foo).size
- * cast(foo).size
- */
-
-DotIdExp *typeDotIdExp(Loc loc, Type *type, Identifier *ident)
-{
- return new DotIdExp(loc, new TypeExp(loc, type), ident);
-}
-
-
-/************************************************************/
-
-// Mainly just a placeholder
-
-TypeExp::TypeExp(Loc loc, Type *type)
- : Expression(loc, TOKtype, sizeof(TypeExp))
-{
- //printf("TypeExp::TypeExp(%s)\n", type->toChars());
- this->type = type;
-}
-
-Expression *TypeExp::syntaxCopy()
-{
- return new TypeExp(loc, type->syntaxCopy());
-}
-
-bool TypeExp::checkType()
-{
- error("type %s is not an expression", toChars());
- return true;
-}
-
-bool TypeExp::checkValue()
-{
- error("type %s has no value", toChars());
- return true;
-}
-
-/************************************************************/
-
-/***********************************************************
- * Mainly just a placeholder of
- * Package, Module, Nspace, and TemplateInstance (including TemplateMixin)
- *
- * A template instance that requires IFTI:
- * foo!tiargs(fargs) // foo!tiargs
- * is left until CallExp::semantic() or resolveProperties()
- */
-ScopeExp::ScopeExp(Loc loc, ScopeDsymbol *sds)
- : Expression(loc, TOKscope, sizeof(ScopeExp))
-{
- //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds->toChars());
- //static int count; if (++count == 38) *(char*)0=0;
- this->sds = sds;
- assert(!sds->isTemplateDeclaration()); // instead, you should use TemplateExp
-}
-
-Expression *ScopeExp::syntaxCopy()
-{
- return new ScopeExp(loc, (ScopeDsymbol *)sds->syntaxCopy(NULL));
-}
-
-bool ScopeExp::checkType()
-{
- if (sds->isPackage())
- {
- error("%s %s has no type", sds->kind(), sds->toChars());
- return true;
- }
- if (TemplateInstance *ti = sds->isTemplateInstance())
- {
- //assert(ti->needsTypeInference(sc));
- if (ti->tempdecl &&
- ti->semantictiargsdone &&
- ti->semanticRun == PASSinit)
- {
- error("partial %s %s has no type", sds->kind(), toChars());
- return true;
- }
- }
- return false;
-}
-
-bool ScopeExp::checkValue()
-{
- error("%s %s has no value", sds->kind(), sds->toChars());
- return true;
-}
-
-/********************** TemplateExp **************************************/
-
-// Mainly just a placeholder
-
-TemplateExp::TemplateExp(Loc loc, TemplateDeclaration *td, FuncDeclaration *fd)
- : Expression(loc, TOKtemplate, sizeof(TemplateExp))
-{
- //printf("TemplateExp(): %s\n", td->toChars());
- this->td = td;
- this->fd = fd;
-}
-
-bool TemplateExp::checkType()
-{
- error("%s %s has no type", td->kind(), toChars());
- return true;
-}
-
-bool TemplateExp::checkValue()
-{
- error("%s %s has no value", td->kind(), toChars());
- return true;
-}
-
-bool TemplateExp::isLvalue()
-{
- return fd != NULL;
-}
-
-Expression *TemplateExp::toLvalue(Scope *sc, Expression *e)
-{
- if (!fd)
- return Expression::toLvalue(sc, e);
-
- assert(sc);
- return resolve(loc, sc, fd, true);
-}
-
-/********************** NewExp **************************************/
-
-/* thisexp.new(newargs) newtype(arguments) */
-
-NewExp::NewExp(Loc loc, Expression *thisexp, Expressions *newargs,
- Type *newtype, Expressions *arguments)
- : Expression(loc, TOKnew, sizeof(NewExp))
-{
- this->thisexp = thisexp;
- this->newargs = newargs;
- this->newtype = newtype;
- this->arguments = arguments;
- argprefix = NULL;
- member = NULL;
- allocator = NULL;
- onstack = 0;
-}
-
-NewExp *NewExp::create(Loc loc, Expression *thisexp, Expressions *newargs,
- Type *newtype, Expressions *arguments)
-{
- return new NewExp(loc, thisexp, newargs, newtype, arguments);
-}
-
-Expression *NewExp::syntaxCopy()
-{
- return new NewExp(loc,
- thisexp ? thisexp->syntaxCopy() : NULL,
- arraySyntaxCopy(newargs),
- newtype->syntaxCopy(), arraySyntaxCopy(arguments));
-}
-
-/********************** NewAnonClassExp **************************************/
-
-NewAnonClassExp::NewAnonClassExp(Loc loc, Expression *thisexp,
- Expressions *newargs, ClassDeclaration *cd, Expressions *arguments)
- : Expression(loc, TOKnewanonclass, sizeof(NewAnonClassExp))
-{
- this->thisexp = thisexp;
- this->newargs = newargs;
- this->cd = cd;
- this->arguments = arguments;
-}
-
-Expression *NewAnonClassExp::syntaxCopy()
-{
- return new NewAnonClassExp(loc,
- thisexp ? thisexp->syntaxCopy() : NULL,
- arraySyntaxCopy(newargs),
- (ClassDeclaration *)cd->syntaxCopy(NULL),
- arraySyntaxCopy(arguments));
-}
-
-/********************** SymbolExp **************************************/
-
-SymbolExp::SymbolExp(Loc loc, TOK op, int size, Declaration *var, bool hasOverloads)
- : Expression(loc, op, size)
-{
- assert(var);
- this->var = var;
- this->hasOverloads = hasOverloads;
-}
-
-/********************** SymOffExp **************************************/
-
-SymOffExp::SymOffExp(Loc loc, Declaration *var, dinteger_t offset, bool hasOverloads)
- : SymbolExp(loc, TOKsymoff, sizeof(SymOffExp), var,
- var->isVarDeclaration() ? false : hasOverloads)
-{
- if (VarDeclaration *v = var->isVarDeclaration())
- {
- // FIXME: This error report will never be handled anyone.
- // It should be done before the SymOffExp construction.
- if (v->needThis())
- ::error(loc, "need `this` for address of %s", v->toChars());
- }
- this->offset = offset;
-}
-
-bool SymOffExp::isBool(bool result)
-{
- return result ? true : false;
-}
-
-/******************************** VarExp **************************/
-
-VarExp::VarExp(Loc loc, Declaration *var, bool hasOverloads)
- : SymbolExp(loc, TOKvar, sizeof(VarExp), var,
- var->isVarDeclaration() ? false : hasOverloads)
-{
- //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var->toChars(), loc.toChars());
- //if (strcmp(var->ident->toChars(), "func") == 0) halt();
- this->type = var->type;
-}
-
-VarExp *VarExp::create(Loc loc, Declaration *var, bool hasOverloads)
-{
- return new VarExp(loc, var, hasOverloads);
-}
-
-bool VarExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (((Expression *)o)->op == TOKvar)
- {
- VarExp *ne = (VarExp *)o;
- if (type->toHeadMutable()->equals(ne->type->toHeadMutable()) &&
- var == ne->var)
- {
- return true;
- }
- }
- return false;
-}
-
-bool VarExp::isLvalue()
-{
- if (var->storage_class & (STClazy | STCrvalue | STCmanifest))
- return false;
- return true;
-}
-
-Expression *VarExp::toLvalue(Scope *, Expression *)
-{
- if (var->storage_class & STCmanifest)
- {
- error("manifest constant `%s` is not lvalue", var->toChars());
- return new ErrorExp();
- }
- if (var->storage_class & STClazy)
- {
- error("lazy variables cannot be lvalues");
- return new ErrorExp();
- }
- if (var->ident == Id::ctfe)
- {
- error("compiler-generated variable __ctfe is not an lvalue");
- return new ErrorExp();
- }
- if (var->ident == Id::dollar) // Bugzilla 13574
- {
- error("`$` is not an lvalue");
- return new ErrorExp();
- }
- return this;
-}
-
-int VarExp::checkModifiable(Scope *sc, int flag)
-{
- //printf("VarExp::checkModifiable %s", toChars());
- assert(type);
- return var->checkModify(loc, sc, type, NULL, flag);
-}
-
-Expression *VarExp::modifiableLvalue(Scope *sc, Expression *e)
-{
- //printf("VarExp::modifiableLvalue('%s')\n", var->toChars());
- if (var->storage_class & STCmanifest)
- {
- error("cannot modify manifest constant `%s`", toChars());
- return new ErrorExp();
- }
- // See if this expression is a modifiable lvalue (i.e. not const)
- return Expression::modifiableLvalue(sc, e);
-}
-
-
-/******************************** OverExp **************************/
-
-OverExp::OverExp(Loc loc, OverloadSet *s)
- : Expression(loc, TOKoverloadset, sizeof(OverExp))
-{
- //printf("OverExp(this = %p, '%s')\n", this, var->toChars());
- vars = s;
- type = Type::tvoid;
-}
-
-bool OverExp::isLvalue()
-{
- return true;
-}
-
-Expression *OverExp::toLvalue(Scope *, Expression *)
-{
- return this;
-}
-
-/******************************** TupleExp **************************/
-
-TupleExp::TupleExp(Loc loc, Expression *e0, Expressions *exps)
- : Expression(loc, TOKtuple, sizeof(TupleExp))
-{
- //printf("TupleExp(this = %p)\n", this);
- this->e0 = e0;
- this->exps = exps;
-}
-
-TupleExp::TupleExp(Loc loc, Expressions *exps)
- : Expression(loc, TOKtuple, sizeof(TupleExp))
-{
- //printf("TupleExp(this = %p)\n", this);
- this->e0 = NULL;
- this->exps = exps;
-}
-
-TupleExp::TupleExp(Loc loc, TupleDeclaration *tup)
- : Expression(loc, TOKtuple, sizeof(TupleExp))
-{
- this->e0 = NULL;
- this->exps = new Expressions();
-
- this->exps->reserve(tup->objects->length);
- for (size_t i = 0; i < tup->objects->length; i++)
- { RootObject *o = (*tup->objects)[i];
- if (Dsymbol *s = getDsymbol(o))
- {
- /* If tuple element represents a symbol, translate to DsymbolExp
- * to supply implicit 'this' if needed later.
- */
- Expression *e = new DsymbolExp(loc, s);
- this->exps->push(e);
- }
- else if (o->dyncast() == DYNCAST_EXPRESSION)
- {
- Expression *e = ((Expression *)o)->copy();
- e->loc = loc; // Bugzilla 15669
- this->exps->push(e);
- }
- else if (o->dyncast() == DYNCAST_TYPE)
- {
- Type *t = (Type *)o;
- Expression *e = new TypeExp(loc, t);
- this->exps->push(e);
- }
- else
- {
- error("%s is not an expression", o->toChars());
- }
- }
-}
-
-bool TupleExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (((Expression *)o)->op == TOKtuple)
- {
- TupleExp *te = (TupleExp *)o;
- if (exps->length != te->exps->length)
- return false;
- if ((e0 && !e0->equals(te->e0)) || (!e0 && te->e0))
- return false;
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *e1 = (*exps)[i];
- Expression *e2 = (*te->exps)[i];
- if (!e1->equals(e2))
- return false;
- }
- return true;
- }
- return false;
-}
-
-Expression *TupleExp::syntaxCopy()
-{
- return new TupleExp(loc, e0 ? e0->syntaxCopy() : NULL, arraySyntaxCopy(exps));
-}
-
-TupleExp *TupleExp::toTupleExp()
-{
- return this;
-}
-
-/******************************** FuncExp *********************************/
-
-FuncExp::FuncExp(Loc loc, Dsymbol *s)
- : Expression(loc, TOKfunction, sizeof(FuncExp))
-{
- this->td = s->isTemplateDeclaration();
- this->fd = s->isFuncLiteralDeclaration();
- if (td)
- {
- assert(td->literal);
- assert(td->members && td->members->length == 1);
- fd = (*td->members)[0]->isFuncLiteralDeclaration();
- }
- tok = fd->tok; // save original kind of function/delegate/(infer)
- assert(fd->fbody);
-}
-
-bool FuncExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (o->dyncast() != DYNCAST_EXPRESSION)
- return false;
- if (((Expression *)o)->op == TOKfunction)
- {
- FuncExp *fe = (FuncExp *)o;
- return fd == fe->fd;
- }
- return false;
-}
-
-void FuncExp::genIdent(Scope *sc)
-{
- if (fd->ident == Id::empty)
- {
- const char *s;
- if (fd->fes) s = "__foreachbody";
- else if (fd->tok == TOKreserved) s = "__lambda";
- else if (fd->tok == TOKdelegate) s = "__dgliteral";
- else s = "__funcliteral";
-
- DsymbolTable *symtab;
- if (FuncDeclaration *func = sc->parent->isFuncDeclaration())
- {
- if (func->localsymtab == NULL)
- {
- // Inside template constraint, symtab is not set yet.
- // Initialize it lazily.
- func->localsymtab = new DsymbolTable();
- }
- symtab = func->localsymtab;
- }
- else
- {
- ScopeDsymbol *sds = sc->parent->isScopeDsymbol();
- if (!sds->symtab)
- {
- // Inside template constraint, symtab may not be set yet.
- // Initialize it lazily.
- assert(sds->isTemplateInstance());
- sds->symtab = new DsymbolTable();
- }
- symtab = sds->symtab;
- }
- assert(symtab);
- int num = (int)dmd_aaLen(symtab->tab) + 1;
- Identifier *id = Identifier::generateId(s, num);
- fd->ident = id;
- if (td) td->ident = id;
- symtab->insert(td ? (Dsymbol *)td : (Dsymbol *)fd);
- }
-}
-
-Expression *FuncExp::syntaxCopy()
-{
- if (td)
- return new FuncExp(loc, td->syntaxCopy(NULL));
- else if (fd->semanticRun == PASSinit)
- return new FuncExp(loc, fd->syntaxCopy(NULL));
- else // Bugzilla 13481: Prevent multiple semantic analysis of lambda body.
- return new FuncExp(loc, fd);
-}
-
-MATCH FuncExp::matchType(Type *to, Scope *sc, FuncExp **presult, int flag)
-{
- //printf("FuncExp::matchType('%s'), to=%s\n", type ? type->toChars() : "null", to->toChars());
- if (presult)
- *presult = NULL;
-
- TypeFunction *tof = NULL;
- if (to->ty == Tdelegate)
- {
- if (tok == TOKfunction)
- {
- if (!flag)
- error("cannot match function literal to delegate type `%s`", to->toChars());
- return MATCHnomatch;
- }
- tof = (TypeFunction *)to->nextOf();
- }
- else if (to->ty == Tpointer && to->nextOf()->ty == Tfunction)
- {
- if (tok == TOKdelegate)
- {
- if (!flag)
- error("cannot match delegate literal to function pointer type `%s`", to->toChars());
- return MATCHnomatch;
- }
- tof = (TypeFunction *)to->nextOf();
- }
-
- if (td)
- {
- if (!tof)
- {
- L1:
- if (!flag)
- error("cannot infer parameter types from %s", to->toChars());
- return MATCHnomatch;
- }
-
- // Parameter types inference from 'tof'
- assert(td->_scope);
- TypeFunction *tf = (TypeFunction *)fd->type;
- //printf("\ttof = %s\n", tof->toChars());
- //printf("\ttf = %s\n", tf->toChars());
- size_t dim = tf->parameterList.length();
-
- if (tof->parameterList.length() != dim ||
- tof->parameterList.varargs != tf->parameterList.varargs)
- goto L1;
-
- Objects *tiargs = new Objects();
- tiargs->reserve(td->parameters->length);
-
- for (size_t i = 0; i < td->parameters->length; i++)
- {
- TemplateParameter *tp = (*td->parameters)[i];
- size_t u = 0;
- for (; u < dim; u++)
- {
- Parameter *p = tf->parameterList[u];
- if (p->type->ty == Tident &&
- ((TypeIdentifier *)p->type)->ident == tp->ident)
- {
- break;
- }
- }
- assert(u < dim);
- Parameter *pto = tof->parameterList[u];
- Type *t = pto->type;
- if (t->ty == Terror)
- goto L1;
- tiargs->push(t);
- }
-
- // Set target of return type inference
- if (!tf->next && tof->next)
- fd->treq = to;
-
- TemplateInstance *ti = new TemplateInstance(loc, td, tiargs);
- Expression *ex = new ScopeExp(loc, ti);
- ex = expressionSemantic(ex, td->_scope);
-
- // Reset inference target for the later re-semantic
- fd->treq = NULL;
-
- if (ex->op == TOKerror)
- return MATCHnomatch;
- if (ex->op != TOKfunction)
- goto L1;
- return ((FuncExp *)ex)->matchType(to, sc, presult, flag);
- }
-
- if (!tof || !tof->next)
- return MATCHnomatch;
-
- assert(type && type != Type::tvoid);
- TypeFunction *tfx = (TypeFunction *)fd->type;
- bool convertMatch = (type->ty != to->ty);
-
- if (fd->inferRetType && tfx->next->implicitConvTo(tof->next) == MATCHconvert)
- {
- /* If return type is inferred and covariant return,
- * tweak return statements to required return type.
- *
- * interface I {}
- * class C : Object, I{}
- *
- * I delegate() dg = delegate() { return new class C(); }
- */
- convertMatch = true;
-
- TypeFunction *tfy = new TypeFunction(tfx->parameterList, tof->next, tfx->linkage, STCundefined);
- tfy->mod = tfx->mod;
- tfy->isnothrow = tfx->isnothrow;
- tfy->isnogc = tfx->isnogc;
- tfy->purity = tfx->purity;
- tfy->isproperty = tfx->isproperty;
- tfy->isref = tfx->isref;
- tfy->iswild = tfx->iswild;
- tfy->deco = tfy->merge()->deco;
-
- tfx = tfy;
- }
-
- Type *tx;
- if (tok == TOKdelegate ||
- (tok == TOKreserved && (type->ty == Tdelegate ||
- (type->ty == Tpointer && to->ty == Tdelegate))))
- {
- // Allow conversion from implicit function pointer to delegate
- tx = new TypeDelegate(tfx);
- tx->deco = tx->merge()->deco;
- }
- else
- {
- assert(tok == TOKfunction ||
- (tok == TOKreserved && type->ty == Tpointer));
- tx = tfx->pointerTo();
- }
- //printf("\ttx = %s, to = %s\n", tx->toChars(), to->toChars());
-
- MATCH m = tx->implicitConvTo(to);
- if (m > MATCHnomatch)
- {
- // MATCHexact: exact type match
- // MATCHconst: covairiant type match (eg. attributes difference)
- // MATCHconvert: context conversion
- m = convertMatch ? MATCHconvert : tx->equals(to) ? MATCHexact : MATCHconst;
-
- if (presult)
- {
- (*presult) = (FuncExp *)copy();
- (*presult)->type = to;
-
- // Bugzilla 12508: Tweak function body for covariant returns.
- (*presult)->fd->modifyReturns(sc, tof->next);
- }
- }
- else if (!flag)
- {
- const char *ts[2];
- toAutoQualChars(ts, tx, to);
- error("cannot implicitly convert expression (%s) of type %s to %s",
- toChars(), ts[0], ts[1]);
- }
- return m;
-}
-
-const char *FuncExp::toChars()
-{
- return fd->toChars();
-}
-
-bool FuncExp::checkType()
-{
- if (td)
- {
- error("template lambda has no type");
- return true;
- }
- return false;
-}
-
-bool FuncExp::checkValue()
-{
- if (td)
- {
- error("template lambda has no value");
- return true;
- }
- return false;
-}
-
-/******************************** DeclarationExp **************************/
-
-DeclarationExp::DeclarationExp(Loc loc, Dsymbol *declaration)
- : Expression(loc, TOKdeclaration, sizeof(DeclarationExp))
-{
- this->declaration = declaration;
-}
-
-Expression *DeclarationExp::syntaxCopy()
-{
- return new DeclarationExp(loc, declaration->syntaxCopy(NULL));
-}
-
-bool DeclarationExp::hasCode()
-{
- if (VarDeclaration *vd = declaration->isVarDeclaration())
- {
- return !(vd->storage_class & (STCmanifest | STCstatic));
- }
- return false;
-}
-
-/************************ TypeidExp ************************************/
-
-/*
- * typeid(int)
- */
-
-TypeidExp::TypeidExp(Loc loc, RootObject *o)
- : Expression(loc, TOKtypeid, sizeof(TypeidExp))
-{
- this->obj = o;
-}
-
-Expression *TypeidExp::syntaxCopy()
-{
- return new TypeidExp(loc, objectSyntaxCopy(obj));
-}
-
-/************************ TraitsExp ************************************/
-/*
- * __traits(identifier, args...)
- */
-
-TraitsExp::TraitsExp(Loc loc, Identifier *ident, Objects *args)
- : Expression(loc, TOKtraits, sizeof(TraitsExp))
-{
- this->ident = ident;
- this->args = args;
-}
-
-Expression *TraitsExp::syntaxCopy()
-{
- return new TraitsExp(loc, ident, TemplateInstance::arraySyntaxCopy(args));
-}
-
-/************************************************************/
-
-HaltExp::HaltExp(Loc loc)
- : Expression(loc, TOKhalt, sizeof(HaltExp))
-{
-}
-
-/************************************************************/
-
-IsExp::IsExp(Loc loc, Type *targ, Identifier *id, TOK tok,
- Type *tspec, TOK tok2, TemplateParameters *parameters)
- : Expression(loc, TOKis, sizeof(IsExp))
-{
- this->targ = targ;
- this->id = id;
- this->tok = tok;
- this->tspec = tspec;
- this->tok2 = tok2;
- this->parameters = parameters;
-}
-
-Expression *IsExp::syntaxCopy()
-{
- // This section is identical to that in TemplateDeclaration::syntaxCopy()
- TemplateParameters *p = NULL;
- if (parameters)
- {
- p = new TemplateParameters();
- p->setDim(parameters->length);
- for (size_t i = 0; i < p->length; i++)
- (*p)[i] = (*parameters)[i]->syntaxCopy();
- }
- return new IsExp(loc,
- targ->syntaxCopy(),
- id,
- tok,
- tspec ? tspec->syntaxCopy() : NULL,
- tok2,
- p);
-}
-
-void unSpeculative(Scope *sc, RootObject *o);
-
-/************************************************************/
-
-UnaExp::UnaExp(Loc loc, TOK op, int size, Expression *e1)
- : Expression(loc, op, size)
-{
- this->e1 = e1;
- this->att1 = NULL;
-}
-
-Expression *UnaExp::syntaxCopy()
-{
- UnaExp *e = (UnaExp *)copy();
- e->type = NULL;
- e->e1 = e->e1->syntaxCopy();
- return e;
-}
-
-/********************************
- * The type for a unary expression is incompatible.
- * Print error message.
- * Returns:
- * ErrorExp
- */
-Expression *UnaExp::incompatibleTypes()
-{
- if (e1->type->toBasetype() == Type::terror)
- return e1;
-
- if (e1->op == TOKtype)
- {
- error("incompatible type for (%s(%s)): cannot use `%s` with types",
- Token::toChars(op), e1->toChars(), Token::toChars(op));
- }
- else
- {
- error("incompatible type for (%s(%s)): `%s`",
- Token::toChars(op), e1->toChars(), e1->type->toChars());
- }
- return new ErrorExp();
-}
-
-Expression *UnaExp::resolveLoc(Loc loc, Scope *sc)
-{
- e1 = e1->resolveLoc(loc, sc);
- return this;
-}
-
-/************************************************************/
-
-BinExp::BinExp(Loc loc, TOK op, int size, Expression *e1, Expression *e2)
- : Expression(loc, op, size)
-{
- this->e1 = e1;
- this->e2 = e2;
-
- this->att1 = NULL;
- this->att2 = NULL;
-}
-
-Expression *BinExp::syntaxCopy()
-{
- BinExp *e = (BinExp *)copy();
- e->type = NULL;
- e->e1 = e->e1->syntaxCopy();
- e->e2 = e->e2->syntaxCopy();
- return e;
-}
-
-Expression *BinExp::checkOpAssignTypes(Scope *sc)
-{
- // At that point t1 and t2 are the merged types. type is the original type of the lhs.
- Type *t1 = e1->type;
- Type *t2 = e2->type;
-
- // T opAssign floating yields a floating. Prevent truncating conversions (float to int).
- // See issue 3841.
- // Should we also prevent double to float (type->isfloating() && type->size() < t2 ->size()) ?
- if (op == TOKaddass || op == TOKminass ||
- op == TOKmulass || op == TOKdivass || op == TOKmodass ||
- op == TOKpowass)
- {
- if ((type->isintegral() && t2->isfloating()))
- {
- warning("%s %s %s is performing truncating conversion",
- type->toChars(), Token::toChars(op), t2->toChars());
- }
- }
-
- // generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary
- if (op == TOKmulass || op == TOKdivass || op == TOKmodass)
- {
- // Any multiplication by an imaginary or complex number yields a complex result.
- // r *= c, i*=c, r*=i, i*=i are all forbidden operations.
- const char *opstr = Token::toChars(op);
- if (t1->isreal() && t2->iscomplex())
- {
- error("%s %s %s is undefined. Did you mean %s %s %s.re ?",
- t1->toChars(), opstr, t2->toChars(),
- t1->toChars(), opstr, t2->toChars());
- return new ErrorExp();
- }
- else if (t1->isimaginary() && t2->iscomplex())
- {
- error("%s %s %s is undefined. Did you mean %s %s %s.im ?",
- t1->toChars(), opstr, t2->toChars(),
- t1->toChars(), opstr, t2->toChars());
- return new ErrorExp();
- }
- else if ((t1->isreal() || t1->isimaginary()) &&
- t2->isimaginary())
- {
- error("%s %s %s is an undefined operation", t1->toChars(), opstr, t2->toChars());
- return new ErrorExp();
- }
- }
-
- // generate an error if this is a nonsensical += or -=, eg real += imaginary
- if (op == TOKaddass || op == TOKminass)
- {
- // Addition or subtraction of a real and an imaginary is a complex result.
- // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations.
- if ((t1->isreal() && (t2->isimaginary() || t2->iscomplex())) ||
- (t1->isimaginary() && (t2->isreal() || t2->iscomplex())))
- {
- error("%s %s %s is undefined (result is complex)",
- t1->toChars(), Token::toChars(op), t2->toChars());
- return new ErrorExp();
- }
- if (type->isreal() || type->isimaginary())
- {
- assert(global.errors || t2->isfloating());
- e2 = e2->castTo(sc, t1);
- }
- }
-
- if (op == TOKmulass)
- {
- if (t2->isfloating())
- {
- if (t1->isreal())
- {
- if (t2->isimaginary() || t2->iscomplex())
- {
- e2 = e2->castTo(sc, t1);
- }
- }
- else if (t1->isimaginary())
- {
- if (t2->isimaginary() || t2->iscomplex())
- {
- switch (t1->ty)
- {
- case Timaginary32: t2 = Type::tfloat32; break;
- case Timaginary64: t2 = Type::tfloat64; break;
- case Timaginary80: t2 = Type::tfloat80; break;
- default:
- assert(0);
- }
- e2 = e2->castTo(sc, t2);
- }
- }
- }
- }
- else if (op == TOKdivass)
- {
- if (t2->isimaginary())
- {
- if (t1->isreal())
- {
- // x/iv = i(-x/v)
- // Therefore, the result is 0
- e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat::zero, t1));
- e2->type = t1;
- Expression *e = new AssignExp(loc, e1, e2);
- e->type = t1;
- return e;
- }
- else if (t1->isimaginary())
- {
- Type *t3;
- switch (t1->ty)
- {
- case Timaginary32: t3 = Type::tfloat32; break;
- case Timaginary64: t3 = Type::tfloat64; break;
- case Timaginary80: t3 = Type::tfloat80; break;
- default:
- assert(0);
- }
- e2 = e2->castTo(sc, t3);
- Expression *e = new AssignExp(loc, e1, e2);
- e->type = t1;
- return e;
- }
- }
- }
- else if (op == TOKmodass)
- {
- if (t2->iscomplex())
- {
- error("cannot perform modulo complex arithmetic");
- return new ErrorExp();
- }
- }
- return this;
-}
-
-/********************************
- * The types for a binary expression are incompatible.
- * Print error message.
- * Returns:
- * ErrorExp
- */
-Expression *BinExp::incompatibleTypes()
-{
- if (e1->type->toBasetype() == Type::terror)
- return e1;
- if (e2->type->toBasetype() == Type::terror)
- return e2;
-
- // CondExp uses 'a ? b : c' but we're comparing 'b : c'
- TOK thisOp = (op == TOKquestion) ? TOKcolon : op;
- if (e1->op == TOKtype || e2->op == TOKtype)
- {
- error("incompatible types for ((%s) %s (%s)): cannot use `%s` with types",
- e1->toChars(), Token::toChars(thisOp), e2->toChars(), Token::toChars(op));
- }
- else if (e1->type->equals(e2->type))
- {
- error("incompatible types for ((%s) %s (%s)): both operands are of type `%s`",
- e1->toChars(), Token::toChars(thisOp), e2->toChars(), e1->type->toChars());
- }
- else
- {
- const char *ts[2];
- toAutoQualChars(ts, e1->type, e2->type);
- error("incompatible types for ((%s) %s (%s)): `%s` and `%s`",
- e1->toChars(), Token::toChars(thisOp), e2->toChars(), ts[0], ts[1]);
- }
- return new ErrorExp();
-}
-
-bool BinExp::checkIntegralBin()
-{
- bool r1 = e1->checkIntegral();
- bool r2 = e2->checkIntegral();
- return (r1 || r2);
-}
-
-bool BinExp::checkArithmeticBin()
-{
- bool r1 = e1->checkArithmetic();
- bool r2 = e2->checkArithmetic();
- return (r1 || r2);
-}
-
-/********************** BinAssignExp **************************************/
-
-BinAssignExp::BinAssignExp(Loc loc, TOK op, int size, Expression *e1, Expression *e2)
- : BinExp(loc, op, size, e1, e2)
-{
-}
-
-bool BinAssignExp::isLvalue()
-{
- return true;
-}
-
-Expression *BinAssignExp::toLvalue(Scope *, Expression *)
-{
- // Lvalue-ness will be handled in glue layer.
- return this;
-}
-
-Expression *BinAssignExp::modifiableLvalue(Scope *sc, Expression *)
-{
- // should check e1->checkModifiable() ?
- return toLvalue(sc, this);
-}
-
-/************************************************************/
-
-CompileExp::CompileExp(Loc loc, Expressions *exps)
- : Expression(loc, TOKmixin, sizeof(CompileExp))
-{
- this->exps = exps;
-}
-
-Expression *CompileExp::syntaxCopy()
-{
- return new CompileExp(loc, arraySyntaxCopy(exps));
-}
-
-bool CompileExp::equals(RootObject *o)
-{
- if (this == o)
- return true;
- if (o && o->dyncast() == DYNCAST_EXPRESSION && ((Expression *)o)->op == TOKmixin)
- {
- CompileExp *ce = (CompileExp *)o;
- if (exps->length != ce->exps->length)
- return false;
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *e1 = (*exps)[i];
- Expression *e2 = (*ce->exps)[i];
- if (e1 != e2 && (!e1 || !e2 || !e1->equals(e2)))
- return false;
- }
- return true;
- }
- return false;
-}
-
-/************************************************************/
-
-ImportExp::ImportExp(Loc loc, Expression *e)
- : UnaExp(loc, TOKimport, sizeof(ImportExp), e)
-{
-}
-
-/************************************************************/
-
-AssertExp::AssertExp(Loc loc, Expression *e, Expression *msg)
- : UnaExp(loc, TOKassert, sizeof(AssertExp), e)
-{
- this->msg = msg;
-}
-
-Expression *AssertExp::syntaxCopy()
-{
- return new AssertExp(loc, e1->syntaxCopy(), msg ? msg->syntaxCopy() : NULL);
-}
-
-/************************************************************/
-
-DotIdExp::DotIdExp(Loc loc, Expression *e, Identifier *ident)
- : UnaExp(loc, TOKdotid, sizeof(DotIdExp), e)
-{
- this->ident = ident;
- this->wantsym = false;
- this->noderef = false;
-}
-
-DotIdExp *DotIdExp::create(Loc loc, Expression *e, Identifier *ident)
-{
- return new DotIdExp(loc, e, ident);
-}
-
-/********************** DotTemplateExp ***********************************/
-
-// Mainly just a placeholder
-
-DotTemplateExp::DotTemplateExp(Loc loc, Expression *e, TemplateDeclaration *td)
- : UnaExp(loc, TOKdottd, sizeof(DotTemplateExp), e)
-
-{
- this->td = td;
-}
-
-bool DotTemplateExp::checkType()
-{
- error("%s %s has no type", td->kind(), toChars());
- return true;
-}
-
-bool DotTemplateExp::checkValue()
-{
- error("%s %s has no value", td->kind(), toChars());
- return true;
-}
-
-/************************************************************/
-
-DotVarExp::DotVarExp(Loc loc, Expression *e, Declaration *var, bool hasOverloads)
- : UnaExp(loc, TOKdotvar, sizeof(DotVarExp), e)
-{
- //printf("DotVarExp()\n");
- this->var = var;
- this->hasOverloads = var->isVarDeclaration() ? false : hasOverloads;
-}
-
-bool DotVarExp::isLvalue()
-{
- return true;
-}
-
-Expression *DotVarExp::toLvalue(Scope *, Expression *)
-{
- //printf("DotVarExp::toLvalue(%s)\n", toChars());
- return this;
-}
-
-/***********************************************
- * Mark variable v as modified if it is inside a constructor that var
- * is a field in.
- */
-int modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1)
-{
- //printf("modifyFieldVar(var = %s)\n", var->toChars());
- Dsymbol *s = sc->func;
- while (1)
- {
- FuncDeclaration *fd = NULL;
- if (s)
- fd = s->isFuncDeclaration();
- if (fd &&
- ((fd->isCtorDeclaration() && var->isField()) ||
- (fd->isStaticCtorDeclaration() && !var->isField())) &&
- fd->toParent2() == var->toParent2() &&
- (!e1 || e1->op == TOKthis)
- )
- {
- bool result = true;
-
- var->ctorinit = true;
- //printf("setting ctorinit\n");
-
- if (var->isField() && sc->fieldinit && !sc->intypeof)
- {
- assert(e1);
- bool mustInit = ((var->storage_class & STCnodefaultctor) != 0 ||
- var->type->needsNested());
-
- size_t dim = sc->fieldinit_dim;
- AggregateDeclaration *ad = fd->isMember2();
- assert(ad);
- size_t i;
- for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ?
- {
- if (ad->fields[i] == var)
- break;
- }
- assert(i < dim);
- unsigned fi = sc->fieldinit[i];
-
- if (fi & CSXthis_ctor)
- {
- if (var->type->isMutable() && e1->type->isMutable())
- result = false;
- else
- {
- const char *modStr = !var->type->isMutable() ? MODtoChars(var->type->mod) : MODtoChars(e1->type->mod);
- ::error(loc, "%s field `%s` initialized multiple times", modStr, var->toChars());
- }
- }
- else if (sc->noctor || (fi & CSXlabel))
- {
- if (!mustInit && var->type->isMutable() && e1->type->isMutable())
- result = false;
- else
- {
- const char *modStr = !var->type->isMutable() ? MODtoChars(var->type->mod) : MODtoChars(e1->type->mod);
- ::error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var->toChars());
- }
- }
- sc->fieldinit[i] |= CSXthis_ctor;
- if (var->overlapped) // Bugzilla 15258
- {
- for (size_t j = 0; j < ad->fields.length; j++)
- {
- VarDeclaration *v = ad->fields[j];
- if (v == var || !var->isOverlappedWith(v))
- continue;
- v->ctorinit = true;
- sc->fieldinit[j] = CSXthis_ctor;
- }
- }
- }
- else if (fd != sc->func)
- {
- if (var->type->isMutable())
- result = false;
- else if (sc->func->fes)
- {
- const char *p = var->isField() ? "field" : var->kind();
- ::error(loc, "%s %s `%s` initialization is not allowed in foreach loop",
- MODtoChars(var->type->mod), p, var->toChars());
- }
- else
- {
- const char *p = var->isField() ? "field" : var->kind();
- ::error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`",
- MODtoChars(var->type->mod), p, var->toChars(), sc->func->toChars());
- }
- }
- return result;
- }
- else
- {
- if (s)
- {
- s = s->toParent2();
- continue;
- }
- }
- break;
- }
- return false;
-}
-
-int DotVarExp::checkModifiable(Scope *sc, int flag)
-{
- //printf("DotVarExp::checkModifiable %s %s\n", toChars(), type->toChars());
- if (checkUnsafeAccess(sc, this, false, !flag))
- return 2;
-
- if (e1->op == TOKthis)
- return var->checkModify(loc, sc, type, e1, flag);
-
- //printf("\te1 = %s\n", e1->toChars());
- return e1->checkModifiable(sc, flag);
-}
-
-Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e)
-{
- return Expression::modifiableLvalue(sc, e);
-}
-
-/************************************************************/
-
-/* Things like:
- * foo.bar!(args)
- */
-
-DotTemplateInstanceExp::DotTemplateInstanceExp(Loc loc, Expression *e, Identifier *name, Objects *tiargs)
- : UnaExp(loc, TOKdotti, sizeof(DotTemplateInstanceExp), e)
-{
- //printf("DotTemplateInstanceExp()\n");
- this->ti = new TemplateInstance(loc, name);
- this->ti->tiargs = tiargs;
-}
-
-DotTemplateInstanceExp::DotTemplateInstanceExp(Loc loc, Expression *e, TemplateInstance *ti)
- : UnaExp(loc, TOKdotti, sizeof(DotTemplateInstanceExp), e)
-{
- this->ti = ti;
-}
-
-Expression *DotTemplateInstanceExp::syntaxCopy()
-{
- return new DotTemplateInstanceExp(loc,
- e1->syntaxCopy(),
- ti->name,
- TemplateInstance::arraySyntaxCopy(ti->tiargs));
-}
-
-bool DotTemplateInstanceExp::findTempDecl(Scope *sc)
-{
- if (ti->tempdecl)
- return true;
-
- Expression *e = new DotIdExp(loc, e1, ti->name);
- e = expressionSemantic(e, sc);
- if (e->op == TOKdot)
- e = ((DotExp *)e)->e2;
-
- Dsymbol *s = NULL;
- switch (e->op)
- {
- case TOKoverloadset: s = ((OverExp *)e)->vars; break;
- case TOKdottd: s = ((DotTemplateExp *)e)->td; break;
- case TOKscope: s = ((ScopeExp *)e)->sds; break;
- case TOKdotvar: s = ((DotVarExp *)e)->var; break;
- case TOKvar: s = ((VarExp *)e)->var; break;
- default: return false;
- }
- return ti->updateTempDecl(sc, s);
-}
-
-/************************************************************/
-
-DelegateExp::DelegateExp(Loc loc, Expression *e, FuncDeclaration *f, bool hasOverloads)
- : UnaExp(loc, TOKdelegate, sizeof(DelegateExp), e)
-{
- this->func = f;
- this->hasOverloads = hasOverloads;
-}
-
-/************************************************************/
-
-DotTypeExp::DotTypeExp(Loc loc, Expression *e, Dsymbol *s)
- : UnaExp(loc, TOKdottype, sizeof(DotTypeExp), e)
-{
- this->sym = s;
- this->type = NULL;
-}
-
-/************************************************************/
-
-CallExp::CallExp(Loc loc, Expression *e, Expressions *exps)
- : UnaExp(loc, TOKcall, sizeof(CallExp), e)
-{
- this->arguments = exps;
- this->f = NULL;
- this->directcall = false;
-}
-
-CallExp::CallExp(Loc loc, Expression *e)
- : UnaExp(loc, TOKcall, sizeof(CallExp), e)
-{
- this->arguments = NULL;
- this->f = NULL;
- this->directcall = false;
-}
-
-CallExp::CallExp(Loc loc, Expression *e, Expression *earg1)
- : UnaExp(loc, TOKcall, sizeof(CallExp), e)
-{
- Expressions *arguments = new Expressions();
- if (earg1)
- {
- arguments->setDim(1);
- (*arguments)[0] = earg1;
- }
- this->arguments = arguments;
- this->f = NULL;
- this->directcall = false;
-}
-
-CallExp::CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2)
- : UnaExp(loc, TOKcall, sizeof(CallExp), e)
-{
- Expressions *arguments = new Expressions();
- arguments->setDim(2);
- (*arguments)[0] = earg1;
- (*arguments)[1] = earg2;
-
- this->arguments = arguments;
- this->f = NULL;
- this->directcall = false;
-}
-
-CallExp *CallExp::create(Loc loc, Expression *e, Expressions *exps)
-{
- return new CallExp(loc, e, exps);
-}
-
-CallExp *CallExp::create(Loc loc, Expression *e)
-{
- return new CallExp(loc, e);
-}
-
-CallExp *CallExp::create(Loc loc, Expression *e, Expression *earg1)
-{
- return new CallExp(loc, e, earg1);
-}
-
-Expression *CallExp::syntaxCopy()
-{
- return new CallExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments));
-}
-
-bool CallExp::isLvalue()
-{
- Type *tb = e1->type->toBasetype();
- if (tb->ty == Tdelegate || tb->ty == Tpointer)
- tb = tb->nextOf();
- if (tb->ty == Tfunction && ((TypeFunction *)tb)->isref)
- {
- if (e1->op == TOKdotvar)
- if (((DotVarExp *)e1)->var->isCtorDeclaration())
- return false;
- return true; // function returns a reference
- }
- return false;
-}
-
-Expression *CallExp::toLvalue(Scope *sc, Expression *e)
-{
- if (isLvalue())
- return this;
- return Expression::toLvalue(sc, e);
-}
-
-Expression *CallExp::addDtorHook(Scope *sc)
-{
- /* Only need to add dtor hook if it's a type that needs destruction.
- * Use same logic as VarDeclaration::callScopeDtor()
- */
-
- if (e1->type && e1->type->ty == Tfunction)
- {
- TypeFunction *tf = (TypeFunction *)e1->type;
- if (tf->isref)
- return this;
- }
-
- Type *tv = type->baseElemOf();
- if (tv->ty == Tstruct)
- {
- TypeStruct *ts = (TypeStruct *)tv;
- StructDeclaration *sd = ts->sym;
- if (sd->dtor)
- {
- /* Type needs destruction, so declare a tmp
- * which the back end will recognize and call dtor on
- */
- VarDeclaration *tmp = copyToTemp(0, "__tmpfordtor", this);
- DeclarationExp *de = new DeclarationExp(loc, tmp);
- VarExp *ve = new VarExp(loc, tmp);
- Expression *e = new CommaExp(loc, de, ve);
- e = expressionSemantic(e, sc);
- return e;
- }
- }
- return this;
-}
-
-FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL)
-{
- if (e->op == TOKaddress)
- {
- Expression *ae1 = ((AddrExp *)e)->e1;
- if (ae1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)ae1;
- if (hasOverloads)
- *hasOverloads = ve->hasOverloads;
- return ve->var->isFuncDeclaration();
- }
- if (ae1->op == TOKdotvar)
- {
- DotVarExp *dve = (DotVarExp *)ae1;
- if (hasOverloads)
- *hasOverloads = dve->hasOverloads;
- return dve->var->isFuncDeclaration();
- }
- }
- else
- {
- if (e->op == TOKsymoff)
- {
- SymOffExp *soe = (SymOffExp *)e;
- if (hasOverloads)
- *hasOverloads = soe->hasOverloads;
- return soe->var->isFuncDeclaration();
- }
- if (e->op == TOKdelegate)
- {
- DelegateExp *dge = (DelegateExp *)e;
- if (hasOverloads)
- *hasOverloads = dge->hasOverloads;
- return dge->func->isFuncDeclaration();
- }
- }
- return NULL;
-}
-
-/************************************************************/
-
-AddrExp::AddrExp(Loc loc, Expression *e)
- : UnaExp(loc, TOKaddress, sizeof(AddrExp), e)
-{
-}
-
-AddrExp::AddrExp(Loc loc, Expression *e, Type *t)
- : UnaExp(loc, TOKaddress, sizeof(AddrExp), e)
-{
- type = t;
-}
-
-/************************************************************/
-
-PtrExp::PtrExp(Loc loc, Expression *e)
- : UnaExp(loc, TOKstar, sizeof(PtrExp), e)
-{
-// if (e->type)
-// type = ((TypePointer *)e->type)->next;
-}
-
-PtrExp::PtrExp(Loc loc, Expression *e, Type *t)
- : UnaExp(loc, TOKstar, sizeof(PtrExp), e)
-{
- type = t;
-}
-
-bool PtrExp::isLvalue()
-{
- return true;
-}
-
-Expression *PtrExp::toLvalue(Scope *, Expression *)
-{
- return this;
-}
-
-int PtrExp::checkModifiable(Scope *sc, int flag)
-{
- if (e1->op == TOKsymoff)
- { SymOffExp *se = (SymOffExp *)e1;
- return se->var->checkModify(loc, sc, type, NULL, flag);
- }
- else if (e1->op == TOKaddress)
- {
- AddrExp *ae = (AddrExp *)e1;
- return ae->e1->checkModifiable(sc, flag);
- }
- return 1;
-}
-
-Expression *PtrExp::modifiableLvalue(Scope *sc, Expression *e)
-{
- //printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type->toChars());
- return Expression::modifiableLvalue(sc, e);
-}
-
-/************************************************************/
-
-NegExp::NegExp(Loc loc, Expression *e)
- : UnaExp(loc, TOKneg, sizeof(NegExp), e)
-{
-}
-
-/************************************************************/
-
-UAddExp::UAddExp(Loc loc, Expression *e)
- : UnaExp(loc, TOKuadd, sizeof(UAddExp), e)
-{
-}
-
-/************************************************************/
-
-ComExp::ComExp(Loc loc, Expression *e)
- : UnaExp(loc, TOKtilde, sizeof(ComExp), e)
-{
-}
-
-/************************************************************/
-
-NotExp::NotExp(Loc loc, Expression *e)
- : UnaExp(loc, TOKnot, sizeof(NotExp), e)
-{
-}
-
-/************************************************************/
-
-DeleteExp::DeleteExp(Loc loc, Expression *e, bool isRAII)
- : UnaExp(loc, TOKdelete, sizeof(DeleteExp), e)
-{
- this->isRAII = isRAII;
-}
-
-Expression *DeleteExp::toBoolean(Scope *)
-{
- error("delete does not give a boolean result");
- return new ErrorExp();
-}
-
-/************************************************************/
-
-CastExp::CastExp(Loc loc, Expression *e, Type *t)
- : UnaExp(loc, TOKcast, sizeof(CastExp), e)
-{
- this->to = t;
- this->mod = (unsigned char)~0;
-}
-
-/* For cast(const) and cast(immutable)
- */
-CastExp::CastExp(Loc loc, Expression *e, unsigned char mod)
- : UnaExp(loc, TOKcast, sizeof(CastExp), e)
-{
- this->to = NULL;
- this->mod = mod;
-}
-
-Expression *CastExp::syntaxCopy()
-{
- return to ? new CastExp(loc, e1->syntaxCopy(), to->syntaxCopy())
- : new CastExp(loc, e1->syntaxCopy(), mod);
-}
-
-/************************************************************/
-
-VectorExp::VectorExp(Loc loc, Expression *e, Type *t)
- : UnaExp(loc, TOKvector, sizeof(VectorExp), e)
-{
- assert(t->ty == Tvector);
- to = (TypeVector *)t;
- dim = ~0;
- ownedByCtfe = OWNEDcode;
-}
-
-VectorExp *VectorExp::create(Loc loc, Expression *e, Type *t)
-{
- return new VectorExp(loc, e, t);
-}
-
-Expression *VectorExp::syntaxCopy()
-{
- return new VectorExp(loc, e1->syntaxCopy(), to->syntaxCopy());
-}
-
-/************************************************************/
-
-VectorArrayExp::VectorArrayExp(Loc loc, Expression *e1)
- : UnaExp(loc, TOKvectorarray, sizeof(VectorArrayExp), e1)
-{
-}
-
-bool VectorArrayExp::isLvalue()
-{
- return e1->isLvalue();
-}
-
-Expression *VectorArrayExp::toLvalue(Scope *sc, Expression *e)
-{
- e1 = e1->toLvalue(sc, e);
- return this;
-}
-
-/************************************************************/
-
-SliceExp::SliceExp(Loc loc, Expression *e1, IntervalExp *ie)
- : UnaExp(loc, TOKslice, sizeof(SliceExp), e1)
-{
- this->upr = ie ? ie->upr : NULL;
- this->lwr = ie ? ie->lwr : NULL;
- lengthVar = NULL;
- upperIsInBounds = false;
- lowerIsLessThanUpper = false;
- arrayop = false;
-}
-
-SliceExp::SliceExp(Loc loc, Expression *e1, Expression *lwr, Expression *upr)
- : UnaExp(loc, TOKslice, sizeof(SliceExp), e1)
-{
- this->upr = upr;
- this->lwr = lwr;
- lengthVar = NULL;
- upperIsInBounds = false;
- lowerIsLessThanUpper = false;
- arrayop = false;
-}
-
-Expression *SliceExp::syntaxCopy()
-{
- SliceExp *se = new SliceExp(loc, e1->syntaxCopy(),
- lwr ? lwr->syntaxCopy() : NULL,
- upr ? upr->syntaxCopy() : NULL);
- se->lengthVar = this->lengthVar; // bug7871
- return se;
-}
-
-int SliceExp::checkModifiable(Scope *sc, int flag)
-{
- //printf("SliceExp::checkModifiable %s\n", toChars());
- if (e1->type->ty == Tsarray ||
- (e1->op == TOKindex && e1->type->ty != Tarray) ||
- e1->op == TOKslice)
- {
- return e1->checkModifiable(sc, flag);
- }
- return 1;
-}
-
-bool SliceExp::isLvalue()
-{
- /* slice expression is rvalue in default, but
- * conversion to reference of static array is only allowed.
- */
- return (type && type->toBasetype()->ty == Tsarray);
-}
-
-Expression *SliceExp::toLvalue(Scope *sc, Expression *e)
-{
- //printf("SliceExp::toLvalue(%s) type = %s\n", toChars(), type ? type->toChars() : NULL);
- return (type && type->toBasetype()->ty == Tsarray)
- ? this : Expression::toLvalue(sc, e);
-}
-
-Expression *SliceExp::modifiableLvalue(Scope *, Expression *)
-{
- error("slice expression %s is not a modifiable lvalue", toChars());
- return this;
-}
-
-bool SliceExp::isBool(bool result)
-{
- return e1->isBool(result);
-}
-
-/********************** ArrayLength **************************************/
-
-ArrayLengthExp::ArrayLengthExp(Loc loc, Expression *e1)
- : UnaExp(loc, TOKarraylength, sizeof(ArrayLengthExp), e1)
-{
-}
-
-/*********************** IntervalExp ********************************/
-
-// Mainly just a placeholder
-
-IntervalExp::IntervalExp(Loc loc, Expression *lwr, Expression *upr)
- : Expression(loc, TOKinterval, sizeof(IntervalExp))
-{
- this->lwr = lwr;
- this->upr = upr;
-}
-
-Expression *IntervalExp::syntaxCopy()
-{
- return new IntervalExp(loc, lwr->syntaxCopy(), upr->syntaxCopy());
-}
-
-/********************** DelegatePtrExp **************************************/
-
-DelegatePtrExp::DelegatePtrExp(Loc loc, Expression *e1)
- : UnaExp(loc, TOKdelegateptr, sizeof(DelegatePtrExp), e1)
-{
-}
-
-bool DelegatePtrExp::isLvalue()
-{
- return e1->isLvalue();
-}
-
-Expression *DelegatePtrExp::toLvalue(Scope *sc, Expression *e)
-{
- e1 = e1->toLvalue(sc, e);
- return this;
-}
-
-Expression *DelegatePtrExp::modifiableLvalue(Scope *sc, Expression *e)
-{
- if (sc->func->setUnsafe())
- {
- error("cannot modify delegate pointer in @safe code %s", toChars());
- return new ErrorExp();
- }
- return Expression::modifiableLvalue(sc, e);
-}
-
-/********************** DelegateFuncptrExp **************************************/
-
-DelegateFuncptrExp::DelegateFuncptrExp(Loc loc, Expression *e1)
- : UnaExp(loc, TOKdelegatefuncptr, sizeof(DelegateFuncptrExp), e1)
-{
-}
-
-bool DelegateFuncptrExp::isLvalue()
-{
- return e1->isLvalue();
-}
-
-Expression *DelegateFuncptrExp::toLvalue(Scope *sc, Expression *e)
-{
- e1 = e1->toLvalue(sc, e);
- return this;
-}
-
-Expression *DelegateFuncptrExp::modifiableLvalue(Scope *sc, Expression *e)
-{
- if (sc->func->setUnsafe())
- {
- error("cannot modify delegate function pointer in @safe code %s", toChars());
- return new ErrorExp();
- }
- return Expression::modifiableLvalue(sc, e);
-}
-
-/*********************** ArrayExp *************************************/
-
-// e1 [ i1, i2, i3, ... ]
-
-ArrayExp::ArrayExp(Loc loc, Expression *e1, Expression *index)
- : UnaExp(loc, TOKarray, sizeof(ArrayExp), e1)
-{
- arguments = new Expressions();
- if (index)
- arguments->push(index);
- lengthVar = NULL;
- currentDimension = 0;
-}
-
-ArrayExp::ArrayExp(Loc loc, Expression *e1, Expressions *args)
- : UnaExp(loc, TOKarray, sizeof(ArrayExp), e1)
-{
- arguments = args;
- lengthVar = NULL;
- currentDimension = 0;
-}
-
-Expression *ArrayExp::syntaxCopy()
-{
- ArrayExp *ae = new ArrayExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments));
- ae->lengthVar = this->lengthVar; // bug7871
- return ae;
-}
-
-bool ArrayExp::isLvalue()
-{
- if (type && type->toBasetype()->ty == Tvoid)
- return false;
- return true;
-}
-
-Expression *ArrayExp::toLvalue(Scope *, Expression *)
-{
- if (type && type->toBasetype()->ty == Tvoid)
- error("voids have no value");
- return this;
-}
-
-/************************* DotExp ***********************************/
-
-DotExp::DotExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKdot, sizeof(DotExp), e1, e2)
-{
-}
-
-/************************* CommaExp ***********************************/
-
-CommaExp::CommaExp(Loc loc, Expression *e1, Expression *e2, bool generated)
- : BinExp(loc, TOKcomma, sizeof(CommaExp), e1, e2)
-{
- isGenerated = generated;
- allowCommaExp = generated;
-}
-
-bool CommaExp::isLvalue()
-{
- return e2->isLvalue();
-}
-
-Expression *CommaExp::toLvalue(Scope *sc, Expression *)
-{
- e2 = e2->toLvalue(sc, NULL);
- return this;
-}
-
-int CommaExp::checkModifiable(Scope *sc, int flag)
-{
- return e2->checkModifiable(sc, flag);
-}
-
-Expression *CommaExp::modifiableLvalue(Scope *sc, Expression *e)
-{
- e2 = e2->modifiableLvalue(sc, e);
- return this;
-}
-
-bool CommaExp::isBool(bool result)
-{
- return e2->isBool(result);
-}
-
-Expression *CommaExp::toBoolean(Scope *sc)
-{
- Expression *ex2 = e2->toBoolean(sc);
- if (ex2->op == TOKerror)
- return ex2;
- e2 = ex2;
- type = e2->type;
- return this;
-}
-
-Expression *CommaExp::addDtorHook(Scope *sc)
-{
- e2 = e2->addDtorHook(sc);
- return this;
-}
-
-/************************** IndexExp **********************************/
-
-// e1 [ e2 ]
-
-IndexExp::IndexExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKindex, sizeof(IndexExp), e1, e2)
-{
- //printf("IndexExp::IndexExp('%s')\n", toChars());
- lengthVar = NULL;
- modifiable = false; // assume it is an rvalue
- indexIsInBounds = false;
-}
-
-Expression *IndexExp::syntaxCopy()
-{
- IndexExp *ie = new IndexExp(loc, e1->syntaxCopy(), e2->syntaxCopy());
- ie->lengthVar = this->lengthVar; // bug7871
- return ie;
-}
-
-bool IndexExp::isLvalue()
-{
- return true;
-}
-
-Expression *IndexExp::toLvalue(Scope *, Expression *)
-{
- return this;
-}
-
-int IndexExp::checkModifiable(Scope *sc, int flag)
-{
- if (e1->type->ty == Tsarray ||
- e1->type->ty == Taarray ||
- (e1->op == TOKindex && e1->type->ty != Tarray) ||
- e1->op == TOKslice)
- {
- return e1->checkModifiable(sc, flag);
- }
- return 1;
-}
-
-Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e)
-{
- //printf("IndexExp::modifiableLvalue(%s)\n", toChars());
- Expression *ex = markSettingAAElem();
- if (ex->op == TOKerror)
- return ex;
-
- return Expression::modifiableLvalue(sc, e);
-}
-
-Expression *IndexExp::markSettingAAElem()
-{
- if (e1->type->toBasetype()->ty == Taarray)
- {
- Type *t2b = e2->type->toBasetype();
- if (t2b->ty == Tarray && t2b->nextOf()->isMutable())
- {
- error("associative arrays can only be assigned values with immutable keys, not %s", e2->type->toChars());
- return new ErrorExp();
- }
- modifiable = true;
-
- if (e1->op == TOKindex)
- {
- Expression *ex = ((IndexExp *)e1)->markSettingAAElem();
- if (ex->op == TOKerror)
- return ex;
- assert(ex == e1);
- }
- }
- return this;
-}
-
-/************************* PostExp ***********************************/
-
-PostExp::PostExp(TOK op, Loc loc, Expression *e)
- : BinExp(loc, op, sizeof(PostExp), e,
- new IntegerExp(loc, 1, Type::tint32))
-{
-}
-
-/************************* PreExp ***********************************/
-
-PreExp::PreExp(TOK op, Loc loc, Expression *e)
- : UnaExp(loc, op, sizeof(PreExp), e)
-{
-}
-
-/************************************************************/
-
-/* op can be TOKassign, TOKconstruct, or TOKblit */
-
-AssignExp::AssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKassign, sizeof(AssignExp), e1, e2)
-{
- memset = 0;
-}
-
-bool AssignExp::isLvalue()
-{
- // Array-op 'x[] = y[]' should make an rvalue.
- // Setting array length 'x.length = v' should make an rvalue.
- if (e1->op == TOKslice ||
- e1->op == TOKarraylength)
- {
- return false;
- }
- return true;
-}
-
-Expression *AssignExp::toLvalue(Scope *sc, Expression *ex)
-{
- if (e1->op == TOKslice ||
- e1->op == TOKarraylength)
- {
- return Expression::toLvalue(sc, ex);
- }
-
- /* In front-end level, AssignExp should make an lvalue of e1.
- * Taking the address of e1 will be handled in low level layer,
- * so this function does nothing.
- */
- return this;
-}
-
-Expression *AssignExp::toBoolean(Scope *)
-{
- // Things like:
- // if (a = b) ...
- // are usually mistakes.
-
- error("assignment cannot be used as a condition, perhaps == was meant?");
- return new ErrorExp();
-}
-
-/************************************************************/
-
-ConstructExp::ConstructExp(Loc loc, Expression *e1, Expression *e2)
- : AssignExp(loc, e1, e2)
-{
- op = TOKconstruct;
-}
-
-ConstructExp::ConstructExp(Loc loc, VarDeclaration *v, Expression *e2)
- : AssignExp(loc, new VarExp(loc, v), e2)
-{
- assert(v->type && e1->type);
- op = TOKconstruct;
-
- if (v->storage_class & (STCref | STCout))
- memset |= referenceInit;
-}
-
-/************************************************************/
-
-BlitExp::BlitExp(Loc loc, Expression *e1, Expression *e2)
- : AssignExp(loc, e1, e2)
-{
- op = TOKblit;
-}
-
-BlitExp::BlitExp(Loc loc, VarDeclaration *v, Expression *e2)
- : AssignExp(loc, new VarExp(loc, v), e2)
-{
- assert(v->type && e1->type);
- op = TOKblit;
-
- if (v->storage_class & (STCref | STCout))
- memset |= referenceInit;
-}
-
-/************************************************************/
-
-AddAssignExp::AddAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKaddass, sizeof(AddAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-MinAssignExp::MinAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKminass, sizeof(MinAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-CatAssignExp::CatAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKcatass, sizeof(CatAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-MulAssignExp::MulAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKmulass, sizeof(MulAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-DivAssignExp::DivAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKdivass, sizeof(DivAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-ModAssignExp::ModAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKmodass, sizeof(ModAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-ShlAssignExp::ShlAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKshlass, sizeof(ShlAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-ShrAssignExp::ShrAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKshrass, sizeof(ShrAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-UshrAssignExp::UshrAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKushrass, sizeof(UshrAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-AndAssignExp::AndAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKandass, sizeof(AndAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-OrAssignExp::OrAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKorass, sizeof(OrAssignExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-XorAssignExp::XorAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKxorass, sizeof(XorAssignExp), e1, e2)
-{
-}
-
-/***************** PowAssignExp *******************************************/
-
-PowAssignExp::PowAssignExp(Loc loc, Expression *e1, Expression *e2)
- : BinAssignExp(loc, TOKpowass, sizeof(PowAssignExp), e1, e2)
-{
-}
-
-/************************* AddExp *****************************/
-
-AddExp::AddExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKadd, sizeof(AddExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-MinExp::MinExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKmin, sizeof(MinExp), e1, e2)
-{
-}
-
-/************************* CatExp *****************************/
-
-CatExp::CatExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKcat, sizeof(CatExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-MulExp::MulExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKmul, sizeof(MulExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-DivExp::DivExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKdiv, sizeof(DivExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-ModExp::ModExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKmod, sizeof(ModExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-PowExp::PowExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKpow, sizeof(PowExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-ShlExp::ShlExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKshl, sizeof(ShlExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-ShrExp::ShrExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKshr, sizeof(ShrExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-UshrExp::UshrExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKushr, sizeof(UshrExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-AndExp::AndExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKand, sizeof(AndExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-OrExp::OrExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKor, sizeof(OrExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-XorExp::XorExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKxor, sizeof(XorExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-LogicalExp::LogicalExp(Loc loc, TOK op, Expression *e1, Expression *e2)
- : BinExp(loc, op, sizeof(LogicalExp), e1, e2)
-{
-}
-
-Expression *LogicalExp::toBoolean(Scope *sc)
-{
- Expression *ex2 = e2->toBoolean(sc);
- if (ex2->op == TOKerror)
- return ex2;
- e2 = ex2;
- return this;
-}
-
-/************************************************************/
-
-InExp::InExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKin, sizeof(InExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-/* This deletes the key e1 from the associative array e2
- */
-
-RemoveExp::RemoveExp(Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, TOKremove, sizeof(RemoveExp), e1, e2)
-{
- type = Type::tbool;
-}
-
-/************************************************************/
-
-CmpExp::CmpExp(TOK op, Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, op, sizeof(CmpExp), e1, e2)
-{
-}
-
-/************************************************************/
-
-EqualExp::EqualExp(TOK op, Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, op, sizeof(EqualExp), e1, e2)
-{
- assert(op == TOKequal || op == TOKnotequal);
-}
-
-/************************************************************/
-
-IdentityExp::IdentityExp(TOK op, Loc loc, Expression *e1, Expression *e2)
- : BinExp(loc, op, sizeof(IdentityExp), e1, e2)
-{
-}
-
-/****************************************************************/
-
-CondExp::CondExp(Loc loc, Expression *econd, Expression *e1, Expression *e2)
- : BinExp(loc, TOKquestion, sizeof(CondExp), e1, e2)
-{
- this->econd = econd;
-}
-
-Expression *CondExp::syntaxCopy()
-{
- return new CondExp(loc, econd->syntaxCopy(), e1->syntaxCopy(), e2->syntaxCopy());
-}
-
-void CondExp::hookDtors(Scope *sc)
-{
- class DtorVisitor : public StoppableVisitor
- {
- public:
- Scope *sc;
- CondExp *ce;
- VarDeclaration *vcond;
- bool isThen;
-
- DtorVisitor(Scope *sc, CondExp *ce)
- {
- this->sc = sc;
- this->ce = ce;
- this->vcond = NULL;
- }
-
- void visit(Expression *)
- {
- //printf("(e = %s)\n", e->toChars());
- }
-
- void visit(DeclarationExp *e)
- {
- VarDeclaration *v = e->declaration->isVarDeclaration();
- if (v && !v->isDataseg())
- {
- if (v->_init)
- {
- ExpInitializer *ei = v->_init->isExpInitializer();
- if (ei)
- ei->exp->accept(this);
- }
-
- if (v->needsScopeDtor())
- {
- if (!vcond)
- {
- vcond = copyToTemp(STCvolatile, "__cond", ce->econd);
- dsymbolSemantic(vcond, sc);
-
- Expression *de = new DeclarationExp(ce->econd->loc, vcond);
- de = expressionSemantic(de, sc);
-
- Expression *ve = new VarExp(ce->econd->loc, vcond);
- ce->econd = Expression::combine(de, ve);
- }
-
- //printf("\t++v = %s, v->edtor = %s\n", v->toChars(), v->edtor->toChars());
- Expression *ve = new VarExp(vcond->loc, vcond);
- if (isThen)
- v->edtor = new LogicalExp(v->edtor->loc, TOKandand, ve, v->edtor);
- else
- v->edtor = new LogicalExp(v->edtor->loc, TOKoror, ve, v->edtor);
- v->edtor = expressionSemantic(v->edtor, sc);
- //printf("\t--v = %s, v->edtor = %s\n", v->toChars(), v->edtor->toChars());
- }
- }
- }
- };
-
- DtorVisitor v(sc, this);
- //printf("+%s\n", toChars());
- v.isThen = true; walkPostorder(e1, &v);
- v.isThen = false; walkPostorder(e2, &v);
- //printf("-%s\n", toChars());
-}
-
-bool CondExp::isLvalue()
-{
- return e1->isLvalue() && e2->isLvalue();
-}
-
-
-Expression *CondExp::toLvalue(Scope *sc, Expression *)
-{
- // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
- CondExp *e = (CondExp *)copy();
- e->e1 = e1->toLvalue(sc, NULL)->addressOf();
- e->e2 = e2->toLvalue(sc, NULL)->addressOf();
- e->type = type->pointerTo();
- return new PtrExp(loc, e, type);
-}
-
-int CondExp::checkModifiable(Scope *sc, int flag)
-{
- return e1->checkModifiable(sc, flag) && e2->checkModifiable(sc, flag);
-}
-
-Expression *CondExp::modifiableLvalue(Scope *sc, Expression *)
-{
- //error("conditional expression %s is not a modifiable lvalue", toChars());
- e1 = e1->modifiableLvalue(sc, e1);
- e2 = e2->modifiableLvalue(sc, e2);
- return toLvalue(sc, this);
-}
-
-Expression *CondExp::toBoolean(Scope *sc)
-{
- Expression *ex1 = e1->toBoolean(sc);
- Expression *ex2 = e2->toBoolean(sc);
- if (ex1->op == TOKerror)
- return ex1;
- if (ex2->op == TOKerror)
- return ex2;
- e1 = ex1;
- e2 = ex2;
- return this;
-}
-
-/****************************************************************/
-
-DefaultInitExp::DefaultInitExp(Loc loc, TOK subop, int size)
- : Expression(loc, TOKdefault, size)
-{
- this->subop = subop;
-}
-
-/****************************************************************/
-
-FileInitExp::FileInitExp(Loc loc, TOK tok)
- : DefaultInitExp(loc, tok, sizeof(FileInitExp))
-{
-}
-
-Expression *FileInitExp::resolveLoc(Loc loc, Scope *sc)
-{
- //printf("FileInitExp::resolve() %s\n", toChars());
- const char *s;
- if (subop == TOKfilefullpath)
- s = FileName::toAbsolute(loc.filename != NULL ? loc.filename : sc->_module->srcfile->name->toChars());
- else
- s = loc.filename != NULL ? loc.filename : sc->_module->ident->toChars();
-
- Expression *e = new StringExp(loc, const_cast<char *>(s));
- e = expressionSemantic(e, sc);
- e = e->castTo(sc, type);
- return e;
-}
-
-/****************************************************************/
-
-LineInitExp::LineInitExp(Loc loc)
- : DefaultInitExp(loc, TOKline, sizeof(LineInitExp))
-{
-}
-
-Expression *LineInitExp::resolveLoc(Loc loc, Scope *sc)
-{
- Expression *e = new IntegerExp(loc, loc.linnum, Type::tint32);
- e = e->castTo(sc, type);
- return e;
-}
-
-/****************************************************************/
-
-ModuleInitExp::ModuleInitExp(Loc loc)
- : DefaultInitExp(loc, TOKmodulestring, sizeof(ModuleInitExp))
-{
-}
-
-Expression *ModuleInitExp::resolveLoc(Loc loc, Scope *sc)
-{
- const char *s;
- if (sc->callsc)
- s = sc->callsc->_module->toPrettyChars();
- else
- s = sc->_module->toPrettyChars();
- Expression *e = new StringExp(loc, const_cast<char *>(s));
- e = expressionSemantic(e, sc);
- e = e->castTo(sc, type);
- return e;
-}
-
-/****************************************************************/
-
-FuncInitExp::FuncInitExp(Loc loc)
- : DefaultInitExp(loc, TOKfuncstring, sizeof(FuncInitExp))
-{
-}
-
-Expression *FuncInitExp::resolveLoc(Loc loc, Scope *sc)
-{
- const char *s;
- if (sc->callsc && sc->callsc->func)
- s = sc->callsc->func->Dsymbol::toPrettyChars();
- else if (sc->func)
- s = sc->func->Dsymbol::toPrettyChars();
- else
- s = "";
- Expression *e = new StringExp(loc, const_cast<char *>(s));
- e = expressionSemantic(e, sc);
- e->type = Type::tstring;
- return e;
-}
-
-/****************************************************************/
-
-PrettyFuncInitExp::PrettyFuncInitExp(Loc loc)
- : DefaultInitExp(loc, TOKprettyfunc, sizeof(PrettyFuncInitExp))
-{
-}
-
-Expression *PrettyFuncInitExp::resolveLoc(Loc loc, Scope *sc)
-{
- FuncDeclaration *fd;
- if (sc->callsc && sc->callsc->func)
- fd = sc->callsc->func;
- else
- fd = sc->func;
-
- const char *s;
- if (fd)
- {
- const char *funcStr = fd->Dsymbol::toPrettyChars();
- OutBuffer buf;
- functionToBufferWithIdent((TypeFunction *)fd->type, &buf, funcStr);
- s = buf.extractChars();
- }
- else
- {
- s = "";
- }
-
- Expression *e = new StringExp(loc, const_cast<char *>(s));
- e = expressionSemantic(e, sc);
- e->type = Type::tstring;
- return e;
-}
-
-Expression *BinExp::reorderSettingAAElem(Scope *sc)
-{
- BinExp *be = this;
-
- if (be->e1->op != TOKindex)
- return be;
- IndexExp *ie = (IndexExp *)be->e1;
- if (ie->e1->type->toBasetype()->ty != Taarray)
- return be;
-
- /* Fix evaluation order of setting AA element. (Bugzilla 3825)
- * Rewrite:
- * aa[k1][k2][k3] op= val;
- * as:
- * auto ref __aatmp = aa;
- * auto ref __aakey3 = k1, __aakey2 = k2, __aakey1 = k3;
- * auto ref __aaval = val;
- * __aatmp[__aakey3][__aakey2][__aakey1] op= __aaval; // assignment
- */
-
- Expression *e0 = NULL;
- while (1)
- {
- Expression *de = NULL;
- ie->e2 = extractSideEffect(sc, "__aakey", &de, ie->e2);
- e0 = Expression::combine(de, e0);
-
- Expression *ie1 = ie->e1;
- if (ie1->op != TOKindex ||
- ((IndexExp *)ie1)->e1->type->toBasetype()->ty != Taarray)
- {
- break;
- }
- ie = (IndexExp *)ie1;
- }
- assert(ie->e1->type->toBasetype()->ty == Taarray);
-
- Expression *de = NULL;
- ie->e1 = extractSideEffect(sc, "__aatmp", &de, ie->e1);
- e0 = Expression::combine(de, e0);
-
- be->e2 = extractSideEffect(sc, "__aaval", &e0, be->e2, true);
-
- //printf("-e0 = %s, be = %s\n", e0->toChars(), be->toChars());
- return Expression::combine(e0, be);
-}
diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d
new file mode 100644
index 00000000000..0d6fa1e88f3
--- /dev/null
+++ b/gcc/d/dmd/expression.d
@@ -0,0 +1,6985 @@
+/**
+ * Defines the bulk of the classes which represent the AST at the expression level.
+ *
+ * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/expression.d, _expression.d)
+ * Documentation: https://dlang.org/phobos/dmd_expression.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expression.d
+ */
+
+module dmd.expression;
+
+import core.stdc.stdarg;
+import core.stdc.stdio;
+import core.stdc.string;
+
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.apply;
+import dmd.arrayop;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.ast_node;
+import dmd.gluelayer;
+import dmd.canthrow;
+import dmd.complex;
+import dmd.constfold;
+import dmd.ctfeexpr;
+import dmd.ctorflow;
+import dmd.dcast;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.delegatize;
+import dmd.dimport;
+import dmd.dinterpret;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.escape;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.inline;
+import dmd.mtype;
+import dmd.nspace;
+import dmd.objc;
+import dmd.opover;
+import dmd.optimize;
+import dmd.root.ctfloat;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.safe;
+import dmd.sideeffect;
+import dmd.target;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.utf;
+import dmd.visitor;
+
+enum LOGSEMANTIC = false;
+void emplaceExp(T : Expression, Args...)(void* p, Args args)
+{
+ scope tmp = new T(args);
+ memcpy(p, cast(void*)tmp, __traits(classInstanceSize, T));
+}
+
+void emplaceExp(T : UnionExp)(T* p, Expression e)
+{
+ memcpy(p, cast(void*)e, e.size);
+}
+
+// Return value for `checkModifiable`
+enum Modifiable
+{
+ /// Not modifiable
+ no,
+ /// Modifiable (the type is mutable)
+ yes,
+ /// Modifiable because it is initialization
+ initialization,
+}
+/**
+ * Specifies how the checkModify deals with certain situations
+ */
+enum ModifyFlags
+{
+ /// Issue error messages on invalid modifications of the variable
+ none,
+ /// No errors are emitted for invalid modifications
+ noError = 0x1,
+ /// The modification occurs for a subfield of the current variable
+ fieldAssign = 0x2,
+}
+
+/****************************************
+ * Find the first non-comma expression.
+ * Params:
+ * e = Expressions connected by commas
+ * Returns:
+ * left-most non-comma expression
+ */
+inout(Expression) firstComma(inout Expression e)
+{
+ Expression ex = cast()e;
+ while (ex.op == TOK.comma)
+ ex = (cast(CommaExp)ex).e1;
+ return cast(inout)ex;
+
+}
+
+/****************************************
+ * Find the last non-comma expression.
+ * Params:
+ * e = Expressions connected by commas
+ * Returns:
+ * right-most non-comma expression
+ */
+
+inout(Expression) lastComma(inout Expression e)
+{
+ Expression ex = cast()e;
+ while (ex.op == TOK.comma)
+ ex = (cast(CommaExp)ex).e2;
+ return cast(inout)ex;
+
+}
+
+/*****************************************
+ * Determine if `this` is available by walking up the enclosing
+ * scopes until a function is found.
+ *
+ * Params:
+ * sc = where to start looking for the enclosing function
+ * Returns:
+ * Found function if it satisfies `isThis()`, otherwise `null`
+ */
+FuncDeclaration hasThis(Scope* sc)
+{
+ //printf("hasThis()\n");
+ Dsymbol p = sc.parent;
+ while (p && p.isTemplateMixin())
+ p = p.parent;
+ FuncDeclaration fdthis = p ? p.isFuncDeclaration() : null;
+ //printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis.toChars() : "");
+
+ // Go upwards until we find the enclosing member function
+ FuncDeclaration fd = fdthis;
+ while (1)
+ {
+ if (!fd)
+ {
+ return null;
+ }
+ if (!fd.isNested() || fd.isThis() || (fd.isThis2 && fd.isMember2()))
+ break;
+
+ Dsymbol parent = fd.parent;
+ while (1)
+ {
+ if (!parent)
+ return null;
+ TemplateInstance ti = parent.isTemplateInstance();
+ if (ti)
+ parent = ti.parent;
+ else
+ break;
+ }
+ fd = parent.isFuncDeclaration();
+ }
+
+ if (!fd.isThis() && !(fd.isThis2 && fd.isMember2()))
+ {
+ return null;
+ }
+
+ assert(fd.vthis);
+ return fd;
+
+}
+
+/***********************************
+ * Determine if a `this` is needed to access `d`.
+ * Params:
+ * sc = context
+ * d = declaration to check
+ * Returns:
+ * true means a `this` is needed
+ */
+bool isNeedThisScope(Scope* sc, Declaration d)
+{
+ if (sc.intypeof == 1)
+ return false;
+
+ AggregateDeclaration ad = d.isThis();
+ if (!ad)
+ return false;
+ //printf("d = %s, ad = %s\n", d.toChars(), ad.toChars());
+
+ for (Dsymbol s = sc.parent; s; s = s.toParentLocal())
+ {
+ //printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2());
+ if (AggregateDeclaration ad2 = s.isAggregateDeclaration())
+ {
+ if (ad2 == ad)
+ return false;
+ else if (ad2.isNested())
+ continue;
+ else
+ return true;
+ }
+ if (FuncDeclaration f = s.isFuncDeclaration())
+ {
+ if (f.isMemberLocal())
+ break;
+ }
+ }
+ return true;
+}
+
+/******************************
+ * check e is exp.opDispatch!(tiargs) or not
+ * It's used to switch to UFCS the semantic analysis path
+ */
+bool isDotOpDispatch(Expression e)
+{
+ if (auto dtie = e.isDotTemplateInstanceExp())
+ return dtie.ti.name == Id.opDispatch;
+ return false;
+}
+
+/****************************************
+ * Expand tuples.
+ * Input:
+ * exps aray of Expressions
+ * Output:
+ * exps rewritten in place
+ */
+extern (C++) void expandTuples(Expressions* exps)
+{
+ //printf("expandTuples()\n");
+ if (exps is null)
+ return;
+
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression arg = (*exps)[i];
+ if (!arg)
+ continue;
+
+ // Look for tuple with 0 members
+ if (auto e = arg.isTypeExp())
+ {
+ if (auto tt = e.type.toBasetype().isTypeTuple())
+ {
+ if (!tt.arguments || tt.arguments.dim == 0)
+ {
+ exps.remove(i);
+ if (i == exps.dim)
+ return;
+ }
+ else // Expand a TypeTuple
+ {
+ exps.remove(i);
+ auto texps = new Expressions(tt.arguments.length);
+ foreach (j, a; *tt.arguments)
+ (*texps)[j] = new TypeExp(e.loc, a.type);
+ exps.insert(i, texps);
+ }
+ i--;
+ continue;
+ }
+ }
+
+ // Inline expand all the tuples
+ while (arg.op == TOK.tuple)
+ {
+ TupleExp te = cast(TupleExp)arg;
+ exps.remove(i); // remove arg
+ exps.insert(i, te.exps); // replace with tuple contents
+ if (i == exps.dim)
+ return; // empty tuple, no more arguments
+ (*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
+ arg = (*exps)[i];
+ }
+ }
+}
+
+/****************************************
+ * Expand alias this tuples.
+ */
+TupleDeclaration isAliasThisTuple(Expression e)
+{
+ if (!e.type)
+ return null;
+
+ Type t = e.type.toBasetype();
+ while (true)
+ {
+ if (Dsymbol s = t.toDsymbol(null))
+ {
+ if (auto ad = s.isAggregateDeclaration())
+ {
+ s = ad.aliasthis ? ad.aliasthis.sym : null;
+ if (s && s.isVarDeclaration())
+ {
+ TupleDeclaration td = s.isVarDeclaration().toAlias().isTupleDeclaration();
+ if (td && td.isexp)
+ return td;
+ }
+ if (Type att = t.aliasthisOf())
+ {
+ t = att;
+ continue;
+ }
+ }
+ }
+ return null;
+ }
+}
+
+int expandAliasThisTuples(Expressions* exps, size_t starti = 0)
+{
+ if (!exps || exps.dim == 0)
+ return -1;
+
+ for (size_t u = starti; u < exps.dim; u++)
+ {
+ Expression exp = (*exps)[u];
+ if (TupleDeclaration td = exp.isAliasThisTuple)
+ {
+ exps.remove(u);
+ foreach (i, o; *td.objects)
+ {
+ auto d = o.isExpression().isDsymbolExp().s.isDeclaration();
+ auto e = new DotVarExp(exp.loc, exp, d);
+ assert(d.type);
+ e.type = d.type;
+ exps.insert(u + i, e);
+ }
+ version (none)
+ {
+ printf("expansion ->\n");
+ foreach (e; exps)
+ {
+ printf("\texps[%d] e = %s %s\n", i, Token.tochars[e.op], e.toChars());
+ }
+ }
+ return cast(int)u;
+ }
+ }
+ return -1;
+}
+
+/****************************************
+ * If `s` is a function template, i.e. the only member of a template
+ * and that member is a function, return that template.
+ * Params:
+ * s = symbol that might be a function template
+ * Returns:
+ * template for that function, otherwise null
+ */
+TemplateDeclaration getFuncTemplateDecl(Dsymbol s)
+{
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f && f.parent)
+ {
+ if (auto ti = f.parent.isTemplateInstance())
+ {
+ if (!ti.isTemplateMixin() && ti.tempdecl)
+ {
+ auto td = ti.tempdecl.isTemplateDeclaration();
+ if (td.onemember && td.ident == f.ident)
+ {
+ return td;
+ }
+ }
+ }
+ }
+ return null;
+}
+
+/************************************************
+ * If we want the value of this expression, but do not want to call
+ * the destructor on it.
+ */
+Expression valueNoDtor(Expression e)
+{
+ auto ex = lastComma(e);
+
+ if (auto ce = ex.isCallExp())
+ {
+ /* The struct value returned from the function is transferred
+ * so do not call the destructor on it.
+ * Recognize:
+ * ((S _ctmp = S.init), _ctmp).this(...)
+ * and make sure the destructor is not called on _ctmp
+ * BUG: if ex is a CommaExp, we should go down the right side.
+ */
+ if (auto dve = ce.e1.isDotVarExp())
+ {
+ if (dve.var.isCtorDeclaration())
+ {
+ // It's a constructor call
+ if (auto comma = dve.e1.isCommaExp())
+ {
+ if (auto ve = comma.e2.isVarExp())
+ {
+ VarDeclaration ctmp = ve.var.isVarDeclaration();
+ if (ctmp)
+ {
+ ctmp.storage_class |= STC.nodtor;
+ assert(!ce.isLvalue());
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (auto ve = ex.isVarExp())
+ {
+ auto vtmp = ve.var.isVarDeclaration();
+ if (vtmp && (vtmp.storage_class & STC.rvalue))
+ {
+ vtmp.storage_class |= STC.nodtor;
+ }
+ }
+ return e;
+}
+
+/*********************************************
+ * If e is an instance of a struct, and that struct has a copy constructor,
+ * rewrite e as:
+ * (tmp = e),tmp
+ * Input:
+ * sc = just used to specify the scope of created temporary variable
+ * destinationType = the type of the object on which the copy constructor is called;
+ * may be null if the struct defines a postblit
+ */
+private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
+{
+ if (auto ts = e.type.baseElemOf().isTypeStruct())
+ {
+ StructDeclaration sd = ts.sym;
+ if (sd.postblit || sd.hasCopyCtor)
+ {
+ /* Create a variable tmp, and replace the argument e with:
+ * (tmp = e),tmp
+ * and let AssignExp() handle the construction.
+ * This is not the most efficient, ideally tmp would be constructed
+ * directly onto the stack.
+ */
+ auto tmp = copyToTemp(STC.rvalue, "__copytmp", e);
+ if (sd.hasCopyCtor && destinationType)
+ tmp.type = destinationType;
+ tmp.storage_class |= STC.nodtor;
+ tmp.dsymbolSemantic(sc);
+ Expression de = new DeclarationExp(e.loc, tmp);
+ Expression ve = new VarExp(e.loc, tmp);
+ de.type = Type.tvoid;
+ ve.type = e.type;
+ return Expression.combine(de, ve);
+ }
+ }
+ return e;
+}
+
+/************************************************
+ * Handle the postblit call on lvalue, or the move of rvalue.
+ *
+ * Params:
+ * sc = the scope where the expression is encountered
+ * e = the expression the needs to be moved or copied (source)
+ * t = if the struct defines a copy constructor, the type of the destination
+ *
+ * Returns:
+ * The expression that copy constructs or moves the value.
+ */
+extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
+{
+ if (auto ce = e.isCondExp())
+ {
+ ce.e1 = doCopyOrMove(sc, ce.e1);
+ ce.e2 = doCopyOrMove(sc, ce.e2);
+ }
+ else
+ {
+ e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e);
+ }
+ return e;
+}
+
+/****************************************************************/
+/* A type meant as a union of all the Expression types,
+ * to serve essentially as a Variant that will sit on the stack
+ * during CTFE to reduce memory consumption.
+ */
+extern (C++) struct UnionExp
+{
+ // yes, default constructor does nothing
+ extern (D) this(Expression e)
+ {
+ memcpy(&this, cast(void*)e, e.size);
+ }
+
+ /* Extract pointer to Expression
+ */
+ extern (C++) Expression exp() return
+ {
+ return cast(Expression)&u;
+ }
+
+ /* Convert to an allocated Expression
+ */
+ extern (C++) Expression copy()
+ {
+ Expression e = exp();
+ //if (e.size > sizeof(u)) printf("%s\n", Token::toChars(e.op));
+ assert(e.size <= u.sizeof);
+ switch (e.op)
+ {
+ case TOK.cantExpression: return CTFEExp.cantexp;
+ case TOK.voidExpression: return CTFEExp.voidexp;
+ case TOK.break_: return CTFEExp.breakexp;
+ case TOK.continue_: return CTFEExp.continueexp;
+ case TOK.goto_: return CTFEExp.gotoexp;
+ default: return e.copy();
+ }
+ }
+
+private:
+ // Ensure that the union is suitably aligned.
+ align(8) union __AnonStruct__u
+ {
+ char[__traits(classInstanceSize, Expression)] exp;
+ char[__traits(classInstanceSize, IntegerExp)] integerexp;
+ char[__traits(classInstanceSize, ErrorExp)] errorexp;
+ char[__traits(classInstanceSize, RealExp)] realexp;
+ char[__traits(classInstanceSize, ComplexExp)] complexexp;
+ char[__traits(classInstanceSize, SymOffExp)] symoffexp;
+ char[__traits(classInstanceSize, StringExp)] stringexp;
+ char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp;
+ char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp;
+ char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp;
+ char[__traits(classInstanceSize, CompoundLiteralExp)] compoundliteralexp;
+ char[__traits(classInstanceSize, NullExp)] nullexp;
+ char[__traits(classInstanceSize, DotVarExp)] dotvarexp;
+ char[__traits(classInstanceSize, AddrExp)] addrexp;
+ char[__traits(classInstanceSize, IndexExp)] indexexp;
+ char[__traits(classInstanceSize, SliceExp)] sliceexp;
+ char[__traits(classInstanceSize, VectorExp)] vectorexp;
+ }
+
+ __AnonStruct__u u;
+}
+
+/********************************
+ * Test to see if two reals are the same.
+ * Regard NaN's as equivalent.
+ * Regard +0 and -0 as different.
+ * Params:
+ * x1 = first operand
+ * x2 = second operand
+ * Returns:
+ * true if x1 is x2
+ * else false
+ */
+bool RealIdentical(real_t x1, real_t x2)
+{
+ return (CTFloat.isNaN(x1) && CTFloat.isNaN(x2)) || CTFloat.isIdentical(x1, x2);
+}
+
+/************************ TypeDotIdExp ************************************/
+/* Things like:
+ * int.size
+ * foo.size
+ * (foo).size
+ * cast(foo).size
+ */
+DotIdExp typeDotIdExp(const ref Loc loc, Type type, Identifier ident)
+{
+ return new DotIdExp(loc, new TypeExp(loc, type), ident);
+}
+
+/***************************************************
+ * Given an Expression, find the variable it really is.
+ *
+ * For example, `a[index]` is really `a`, and `s.f` is really `s`.
+ * Params:
+ * e = Expression to look at
+ * Returns:
+ * variable if there is one, null if not
+ */
+VarDeclaration expToVariable(Expression e)
+{
+ while (1)
+ {
+ switch (e.op)
+ {
+ case TOK.variable:
+ return (cast(VarExp)e).var.isVarDeclaration();
+
+ case TOK.dotVariable:
+ e = (cast(DotVarExp)e).e1;
+ continue;
+
+ case TOK.index:
+ {
+ IndexExp ei = cast(IndexExp)e;
+ e = ei.e1;
+ Type ti = e.type.toBasetype();
+ if (ti.ty == Tsarray)
+ continue;
+ return null;
+ }
+
+ case TOK.slice:
+ {
+ SliceExp ei = cast(SliceExp)e;
+ e = ei.e1;
+ Type ti = e.type.toBasetype();
+ if (ti.ty == Tsarray)
+ continue;
+ return null;
+ }
+
+ case TOK.this_:
+ case TOK.super_:
+ return (cast(ThisExp)e).var.isVarDeclaration();
+
+ default:
+ return null;
+ }
+ }
+}
+
+enum OwnedBy : ubyte
+{
+ code, // normal code expression in AST
+ ctfe, // value expression for CTFE
+ cache, // constant value cached for CTFE
+}
+
+enum WANTvalue = 0; // default
+enum WANTexpand = 1; // expand const/immutable variables if possible
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#expression
+ */
+extern (C++) abstract class Expression : ASTNode
+{
+ const TOK op; // to minimize use of dynamic_cast
+ ubyte size; // # of bytes in Expression so we can copy() it
+ ubyte parens; // if this is a parenthesized expression
+ Type type; // !=null means that semantic() has been run
+ Loc loc; // file location
+
+ extern (D) this(const ref Loc loc, TOK op, int size)
+ {
+ //printf("Expression::Expression(op = %d) this = %p\n", op, this);
+ this.loc = loc;
+ this.op = op;
+ this.size = cast(ubyte)size;
+ }
+
+ static void _init()
+ {
+ CTFEExp.cantexp = new CTFEExp(TOK.cantExpression);
+ CTFEExp.voidexp = new CTFEExp(TOK.voidExpression);
+ CTFEExp.breakexp = new CTFEExp(TOK.break_);
+ CTFEExp.continueexp = new CTFEExp(TOK.continue_);
+ CTFEExp.gotoexp = new CTFEExp(TOK.goto_);
+ CTFEExp.showcontext = new CTFEExp(TOK.showCtfeContext);
+ }
+
+ /**
+ * Deinitializes the global state of the compiler.
+ *
+ * This can be used to restore the state set by `_init` to its original
+ * state.
+ */
+ static void deinitialize()
+ {
+ CTFEExp.cantexp = CTFEExp.cantexp.init;
+ CTFEExp.voidexp = CTFEExp.voidexp.init;
+ CTFEExp.breakexp = CTFEExp.breakexp.init;
+ CTFEExp.continueexp = CTFEExp.continueexp.init;
+ CTFEExp.gotoexp = CTFEExp.gotoexp.init;
+ CTFEExp.showcontext = CTFEExp.showcontext.init;
+ }
+
+ /*********************************
+ * Does *not* do a deep copy.
+ */
+ final Expression copy()
+ {
+ Expression e;
+ if (!size)
+ {
+ debug
+ {
+ fprintf(stderr, "No expression copy for: %s\n", toChars());
+ printf("op = %d\n", op);
+ }
+ assert(0);
+ }
+
+ // memory never freed, so can use the faster bump-pointer-allocation
+ e = cast(Expression)allocmemory(size);
+ //printf("Expression::copy(op = %d) e = %p\n", op, e);
+ return cast(Expression)memcpy(cast(void*)e, cast(void*)this, size);
+ }
+
+ Expression syntaxCopy()
+ {
+ //printf("Expression::syntaxCopy()\n");
+ //print();
+ return copy();
+ }
+
+ // kludge for template.isExpression()
+ override final DYNCAST dyncast() const
+ {
+ return DYNCAST.expression;
+ }
+
+ override const(char)* toChars() const
+ {
+ OutBuffer buf;
+ HdrGenState hgs;
+ toCBuffer(this, &buf, &hgs);
+ return buf.extractChars();
+ }
+
+ static if (__VERSION__ < 2092)
+ {
+ final void error(const(char)* format, ...) const
+ {
+ if (type != Type.terror)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .verror(loc, format, ap);
+ va_end(ap);
+ }
+ }
+
+ final void errorSupplemental(const(char)* format, ...)
+ {
+ if (type == Type.terror)
+ return;
+
+ va_list ap;
+ va_start(ap, format);
+ .verrorSupplemental(loc, format, ap);
+ va_end(ap);
+ }
+
+ final void warning(const(char)* format, ...) const
+ {
+ if (type != Type.terror)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vwarning(loc, format, ap);
+ va_end(ap);
+ }
+ }
+
+ final void deprecation(const(char)* format, ...) const
+ {
+ if (type != Type.terror)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vdeprecation(loc, format, ap);
+ va_end(ap);
+ }
+ }
+ }
+ else
+ {
+ pragma(printf) final void error(const(char)* format, ...) const
+ {
+ if (type != Type.terror)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .verror(loc, format, ap);
+ va_end(ap);
+ }
+ }
+
+ pragma(printf) final void errorSupplemental(const(char)* format, ...)
+ {
+ if (type == Type.terror)
+ return;
+
+ va_list ap;
+ va_start(ap, format);
+ .verrorSupplemental(loc, format, ap);
+ va_end(ap);
+ }
+
+ pragma(printf) final void warning(const(char)* format, ...) const
+ {
+ if (type != Type.terror)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vwarning(loc, format, ap);
+ va_end(ap);
+ }
+ }
+
+ pragma(printf) final void deprecation(const(char)* format, ...) const
+ {
+ if (type != Type.terror)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vdeprecation(loc, format, ap);
+ va_end(ap);
+ }
+ }
+ }
+
+ /**********************************
+ * Combine e1 and e2 by CommaExp if both are not NULL.
+ */
+ extern (D) static Expression combine(Expression e1, Expression e2)
+ {
+ if (e1)
+ {
+ if (e2)
+ {
+ e1 = new CommaExp(e1.loc, e1, e2);
+ e1.type = e2.type;
+ }
+ }
+ else
+ e1 = e2;
+ return e1;
+ }
+
+ extern (D) static Expression combine(Expression e1, Expression e2, Expression e3)
+ {
+ return combine(combine(e1, e2), e3);
+ }
+
+ extern (D) static Expression combine(Expression e1, Expression e2, Expression e3, Expression e4)
+ {
+ return combine(combine(e1, e2), combine(e3, e4));
+ }
+
+ /**********************************
+ * If 'e' is a tree of commas, returns the rightmost expression
+ * by stripping off it from the tree. The remained part of the tree
+ * is returned via e0.
+ * Otherwise 'e' is directly returned and e0 is set to NULL.
+ */
+ extern (D) static Expression extractLast(Expression e, out Expression e0)
+ {
+ if (e.op != TOK.comma)
+ {
+ return e;
+ }
+
+ CommaExp ce = cast(CommaExp)e;
+ if (ce.e2.op != TOK.comma)
+ {
+ e0 = ce.e1;
+ return ce.e2;
+ }
+ else
+ {
+ e0 = e;
+
+ Expression* pce = &ce.e2;
+ while ((cast(CommaExp)(*pce)).e2.op == TOK.comma)
+ {
+ pce = &(cast(CommaExp)(*pce)).e2;
+ }
+ assert((*pce).op == TOK.comma);
+ ce = cast(CommaExp)(*pce);
+ *pce = ce.e1;
+
+ return ce.e2;
+ }
+ }
+
+ extern (D) static Expressions* arraySyntaxCopy(Expressions* exps)
+ {
+ Expressions* a = null;
+ if (exps)
+ {
+ a = new Expressions(exps.dim);
+ foreach (i, e; *exps)
+ {
+ (*a)[i] = e ? e.syntaxCopy() : null;
+ }
+ }
+ return a;
+ }
+
+ dinteger_t toInteger()
+ {
+ //printf("Expression %s\n", Token::toChars(op));
+ error("integer constant expression expected instead of `%s`", toChars());
+ return 0;
+ }
+
+ uinteger_t toUInteger()
+ {
+ //printf("Expression %s\n", Token::toChars(op));
+ return cast(uinteger_t)toInteger();
+ }
+
+ real_t toReal()
+ {
+ error("floating point constant expression expected instead of `%s`", toChars());
+ return CTFloat.zero;
+ }
+
+ real_t toImaginary()
+ {
+ error("floating point constant expression expected instead of `%s`", toChars());
+ return CTFloat.zero;
+ }
+
+ complex_t toComplex()
+ {
+ error("floating point constant expression expected instead of `%s`", toChars());
+ return complex_t(CTFloat.zero);
+ }
+
+ StringExp toStringExp()
+ {
+ return null;
+ }
+
+ TupleExp toTupleExp()
+ {
+ return null;
+ }
+
+ /***************************************
+ * Return !=0 if expression is an lvalue.
+ */
+ bool isLvalue()
+ {
+ return false;
+ }
+
+ /*******************************
+ * Give error if we're not an lvalue.
+ * If we can, convert expression to be an lvalue.
+ */
+ Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (!e)
+ e = this;
+ else if (!loc.isValid())
+ loc = e.loc;
+
+ if (e.op == TOK.type)
+ error("`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind());
+ else
+ error("`%s` is not an lvalue and cannot be modified", e.toChars());
+
+ return ErrorExp.get();
+ }
+
+ Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ //printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type.toChars());
+ // See if this expression is a modifiable lvalue (i.e. not const)
+ if (checkModifiable(this, sc) == Modifiable.yes)
+ {
+ assert(type);
+ if (!type.isMutable())
+ {
+ if (auto dve = this.isDotVarExp())
+ {
+ if (isNeedThisScope(sc, dve.var))
+ for (Dsymbol s = sc.func; s; s = s.toParentLocal())
+ {
+ FuncDeclaration ff = s.isFuncDeclaration();
+ if (!ff)
+ break;
+ if (!ff.type.isMutable)
+ {
+ error("cannot modify `%s` in `%s` function", toChars(), MODtoChars(type.mod));
+ return ErrorExp.get();
+ }
+ }
+ }
+ error("cannot modify `%s` expression `%s`", MODtoChars(type.mod), toChars());
+ return ErrorExp.get();
+ }
+ else if (!type.isAssignable())
+ {
+ error("cannot modify struct instance `%s` of type `%s` because it contains `const` or `immutable` members",
+ toChars(), type.toChars());
+ return ErrorExp.get();
+ }
+ }
+ return toLvalue(sc, e);
+ }
+
+ final Expression implicitCastTo(Scope* sc, Type t)
+ {
+ return .implicitCastTo(this, sc, t);
+ }
+
+ final MATCH implicitConvTo(Type t)
+ {
+ return .implicitConvTo(this, t);
+ }
+
+ final Expression castTo(Scope* sc, Type t)
+ {
+ return .castTo(this, sc, t);
+ }
+
+ /****************************************
+ * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc.
+ */
+ Expression resolveLoc(const ref Loc loc, Scope* sc)
+ {
+ this.loc = loc;
+ return this;
+ }
+
+ /****************************************
+ * Check that the expression has a valid type.
+ * If not, generates an error "... has no type".
+ * Returns:
+ * true if the expression is not valid.
+ * Note:
+ * When this function returns true, `checkValue()` should also return true.
+ */
+ bool checkType()
+ {
+ return false;
+ }
+
+ /****************************************
+ * Check that the expression has a valid value.
+ * If not, generates an error "... has no value".
+ * Returns:
+ * true if the expression is not valid or has void type.
+ */
+ bool checkValue()
+ {
+ if (type && type.toBasetype().ty == Tvoid)
+ {
+ error("expression `%s` is `void` and has no value", toChars());
+ //print(); assert(0);
+ if (!global.gag)
+ type = Type.terror;
+ return true;
+ }
+ return false;
+ }
+
+ extern (D) final bool checkScalar()
+ {
+ if (op == TOK.error)
+ return true;
+ if (type.toBasetype().ty == Terror)
+ return true;
+ if (!type.isscalar())
+ {
+ error("`%s` is not a scalar, it is a `%s`", toChars(), type.toChars());
+ return true;
+ }
+ return checkValue();
+ }
+
+ extern (D) final bool checkNoBool()
+ {
+ if (op == TOK.error)
+ return true;
+ if (type.toBasetype().ty == Terror)
+ return true;
+ if (type.toBasetype().ty == Tbool)
+ {
+ error("operation not allowed on `bool` `%s`", toChars());
+ return true;
+ }
+ return false;
+ }
+
+ extern (D) final bool checkIntegral()
+ {
+ if (op == TOK.error)
+ return true;
+ if (type.toBasetype().ty == Terror)
+ return true;
+ if (!type.isintegral())
+ {
+ error("`%s` is not of integral type, it is a `%s`", toChars(), type.toChars());
+ return true;
+ }
+ return checkValue();
+ }
+
+ extern (D) final bool checkArithmetic()
+ {
+ if (op == TOK.error)
+ return true;
+ if (type.toBasetype().ty == Terror)
+ return true;
+ if (!type.isintegral() && !type.isfloating())
+ {
+ error("`%s` is not of arithmetic type, it is a `%s`", toChars(), type.toChars());
+ return true;
+ }
+ return checkValue();
+ }
+
+ final bool checkDeprecated(Scope* sc, Dsymbol s)
+ {
+ return s.checkDeprecated(loc, sc);
+ }
+
+ extern (D) final bool checkDisabled(Scope* sc, Dsymbol s)
+ {
+ if (auto d = s.isDeclaration())
+ {
+ return d.checkDisabled(loc, sc);
+ }
+
+ return false;
+ }
+
+ /*********************************************
+ * Calling function f.
+ * Check the purity, i.e. if we're in a pure function
+ * we can only call other pure functions.
+ * Returns true if error occurs.
+ */
+ extern (D) final bool checkPurity(Scope* sc, FuncDeclaration f)
+ {
+ if (!sc.func)
+ return false;
+ if (sc.func == f)
+ return false;
+ if (sc.intypeof == 1)
+ return false;
+ if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ return false;
+
+ // If the call has a pure parent, then the called func must be pure.
+ if (!f.isPure() && checkImpure(sc))
+ {
+ error("`pure` %s `%s` cannot call impure %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
+ f.toPrettyChars());
+
+ checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure");
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether `f` is a generated `DtorDeclaration` that hides a user-defined one
+ * which passes `check` while `f` doesn't (e.g. when the user defined dtor is pure but
+ * the generated dtor is not).
+ * In that case the method will identify and print all members causing the attribute
+ * missmatch.
+ *
+ * Params:
+ * sc = scope
+ * f = potential `DtorDeclaration`
+ * check = current check (e.g. whether it's pure)
+ * checkName = the kind of check (e.g. `"pure"`)
+ */
+ extern (D) final void checkOverridenDtor(Scope* sc, FuncDeclaration f,
+ scope bool function(DtorDeclaration) check, const string checkName
+ ) {
+ auto dd = f.isDtorDeclaration();
+ if (!dd || !dd.generated)
+ return;
+
+ // DtorDeclaration without parents should fail at an earlier stage
+ auto ad = cast(AggregateDeclaration) f.toParent2();
+ assert(ad);
+ assert(ad.dtors.length);
+
+ // Search for the user-defined destructor (if any)
+ foreach(dtor; ad.dtors)
+ {
+ if (dtor.generated)
+ continue;
+
+ if (!check(dtor)) // doesn't match check (e.g. is impure as well)
+ return;
+
+ // Sanity check
+ assert(!check(cast(DtorDeclaration) ad.fieldDtor));
+ break;
+ }
+
+ dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
+ dd.generated ? "generated " : "".ptr,
+ ad.toChars,
+ cast(int) checkName.length, checkName.ptr);
+
+ // Search for the offending fields
+ foreach (field; ad.fields)
+ {
+ // Only structs may define automatically called destructors
+ auto ts = field.type.isTypeStruct();
+ if (!ts)
+ {
+ // But they might be part of a static array
+ auto ta = field.type.isTypeSArray();
+ if (!ta)
+ continue;
+
+ ts = ta.baseElemOf().isTypeStruct();
+ if (!ts)
+ continue;
+ }
+
+ auto fieldSym = ts.toDsymbol(sc);
+ assert(fieldSym); // Resolving ts must succeed because missing defs. should error before
+
+ auto fieldSd = fieldSym.isStructDeclaration();
+ assert(fieldSd); // ts is a TypeStruct, this would imply a malformed ASR
+
+ if (fieldSd.dtor && !check(fieldSd.dtor))
+ {
+ field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars());
+
+ if (fieldSd.dtor.generated)
+ checkOverridenDtor(sc, fieldSd.dtor, check, checkName);
+ else
+ fieldSd.dtor.loc.errorSupplemental(" %.*s `%s.~this` is declared here",
+ cast(int) checkName.length, checkName.ptr, fieldSd.toChars());
+ }
+ }
+ }
+
+ /*******************************************
+ * Accessing variable v.
+ * Check for purity and safety violations.
+ * Returns true if error occurs.
+ */
+ extern (D) final bool checkPurity(Scope* sc, VarDeclaration v)
+ {
+ //printf("v = %s %s\n", v.type.toChars(), v.toChars());
+ /* Look for purity and safety violations when accessing variable v
+ * from current function.
+ */
+ if (!sc.func)
+ return false;
+ if (sc.intypeof == 1)
+ return false; // allow violations inside typeof(expression)
+ if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ return false; // allow violations inside compile-time evaluated expressions and debug conditionals
+ if (v.ident == Id.ctfe)
+ return false; // magic variable never violates pure and safe
+ if (v.isImmutable())
+ return false; // always safe and pure to access immutables...
+ if (v.isConst() && !v.isRef() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf()))
+ return false; // or const global/parameter values which have no mutable indirections
+ if (v.storage_class & STC.manifest)
+ return false; // ...or manifest constants
+
+ // accessing empty structs is pure
+ if (v.type.ty == Tstruct)
+ {
+ StructDeclaration sd = (cast(TypeStruct)v.type).sym;
+ if (sd.members) // not opaque
+ {
+ sd.determineSize(v.loc);
+ if (sd.hasNoFields)
+ return false;
+ }
+ }
+
+ bool err = false;
+ if (v.isDataseg())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=7533
+ // Accessing implicit generated __gate is pure.
+ if (v.ident == Id.gate)
+ return false;
+
+ if (checkImpure(sc))
+ {
+ error("`pure` %s `%s` cannot access mutable static data `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), v.toChars());
+ err = true;
+ }
+ }
+ else
+ {
+ /* Given:
+ * void f() {
+ * int fx;
+ * pure void g() {
+ * int gx;
+ * /+pure+/ void h() {
+ * int hx;
+ * /+pure+/ void i() { }
+ * }
+ * }
+ * }
+ * i() can modify hx and gx but not fx
+ */
+
+ Dsymbol vparent = v.toParent2();
+ for (Dsymbol s = sc.func; !err && s; s = s.toParentP(vparent))
+ {
+ if (s == vparent)
+ break;
+
+ if (AggregateDeclaration ad = s.isAggregateDeclaration())
+ {
+ if (ad.isNested())
+ continue;
+ break;
+ }
+ FuncDeclaration ff = s.isFuncDeclaration();
+ if (!ff)
+ break;
+ if (ff.isNested() || ff.isThis())
+ {
+ if (ff.type.isImmutable() ||
+ ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod))
+ {
+ OutBuffer ffbuf;
+ OutBuffer vbuf;
+ MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod);
+ MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod);
+ error("%s%s `%s` cannot access %sdata `%s`",
+ ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars());
+ err = true;
+ break;
+ }
+ continue;
+ }
+ break;
+ }
+ }
+
+ /* Do not allow safe functions to access __gshared data
+ */
+ if (v.storage_class & STC.gshared)
+ {
+ if (sc.func.setUnsafe())
+ {
+ error("`@safe` %s `%s` cannot access `__gshared` data `%s`",
+ sc.func.kind(), sc.func.toChars(), v.toChars());
+ err = true;
+ }
+ }
+
+ return err;
+ }
+
+ /*
+ Check if sc.func is impure or can be made impure.
+ Returns true on error, i.e. if sc.func is pure and cannot be made impure.
+ */
+ private static bool checkImpure(Scope* sc)
+ {
+ return sc.func && (sc.flags & SCOPE.compile
+ ? sc.func.isPureBypassingInference() >= PURE.weak
+ : sc.func.setImpure());
+ }
+
+ /*********************************************
+ * Calling function f.
+ * Check the safety, i.e. if we're in a @safe function
+ * we can only call @safe or @trusted functions.
+ * Returns true if error occurs.
+ */
+ extern (D) final bool checkSafety(Scope* sc, FuncDeclaration f)
+ {
+ if (!sc.func)
+ return false;
+ if (sc.func == f)
+ return false;
+ if (sc.intypeof == 1)
+ return false;
+ if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ return false;
+
+ if (!f.isSafe() && !f.isTrusted())
+ {
+ if (sc.flags & SCOPE.compile ? sc.func.isSafeBypassingInference() : sc.func.setUnsafe())
+ {
+ if (!loc.isValid()) // e.g. implicitly generated dtor
+ loc = sc.func.loc;
+
+ const prettyChars = f.toPrettyChars();
+ error("`@safe` %s `%s` cannot call `@system` %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
+ prettyChars);
+ .errorSupplemental(f.loc, "`%s` is declared here", prettyChars);
+
+ checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system");
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*********************************************
+ * Calling function f.
+ * Check the @nogc-ness, i.e. if we're in a @nogc function
+ * we can only call other @nogc functions.
+ * Returns true if error occurs.
+ */
+ extern (D) final bool checkNogc(Scope* sc, FuncDeclaration f)
+ {
+ if (!sc.func)
+ return false;
+ if (sc.func == f)
+ return false;
+ if (sc.intypeof == 1)
+ return false;
+ if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+ return false;
+
+ if (!f.isNogc())
+ {
+ if (sc.flags & SCOPE.compile ? sc.func.isNogcBypassingInference() : sc.func.setGC())
+ {
+ if (loc.linnum == 0) // e.g. implicitly generated dtor
+ loc = sc.func.loc;
+
+ // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
+ // so don't print anything to avoid double error messages.
+ if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT))
+ error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
+
+ checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().isnogc, "non-@nogc");
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /********************************************
+ * Check that the postblit is callable if t is an array of structs.
+ * Returns true if error happens.
+ */
+ extern (D) final bool checkPostblit(Scope* sc, Type t)
+ {
+ if (auto ts = t.baseElemOf().isTypeStruct())
+ {
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=11395
+ // Require TypeInfo generation for array concatenation
+ semanticTypeInfo(sc, t);
+ }
+
+ StructDeclaration sd = ts.sym;
+ if (sd.postblit)
+ {
+ if (sd.postblit.checkDisabled(loc, sc))
+ return true;
+
+ //checkDeprecated(sc, sd.postblit); // necessary?
+ checkPurity(sc, sd.postblit);
+ checkSafety(sc, sd.postblit);
+ checkNogc(sc, sd.postblit);
+ //checkAccess(sd, loc, sc, sd.postblit); // necessary?
+ return false;
+ }
+ }
+ return false;
+ }
+
+ extern (D) final bool checkRightThis(Scope* sc)
+ {
+ if (op == TOK.error)
+ return true;
+ if (op == TOK.variable && type.ty != Terror)
+ {
+ VarExp ve = cast(VarExp)this;
+ if (isNeedThisScope(sc, ve.var))
+ {
+ //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
+ // sc.intypeof, sc.getStructClassScope(), func, fdthis);
+ error("need `this` for `%s` of type `%s`", ve.var.toChars(), ve.var.type.toChars());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*******************************
+ * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not.
+ * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics)
+ * Returns true if error occurs.
+ */
+ extern (D) final bool checkReadModifyWrite(TOK rmwOp, Expression ex = null)
+ {
+ //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : "");
+ if (!type || !type.isShared() || type.isTypeStruct() || type.isTypeClass())
+ return false;
+
+ // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal.
+ switch (rmwOp)
+ {
+ case TOK.plusPlus:
+ case TOK.prePlusPlus:
+ rmwOp = TOK.addAssign;
+ break;
+ case TOK.minusMinus:
+ case TOK.preMinusMinus:
+ rmwOp = TOK.minAssign;
+ break;
+ default:
+ break;
+ }
+
+ error("read-modify-write operations are not allowed for `shared` variables");
+ errorSupplemental("Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead",
+ Token.toChars(rmwOp), toChars(), ex ? ex.toChars() : "1");
+ return true;
+ }
+
+ /************************************************
+ * Destructors are attached to VarDeclarations.
+ * Hence, if expression returns a temp that needs a destructor,
+ * make sure and create a VarDeclaration for that temp.
+ */
+ Expression addDtorHook(Scope* sc)
+ {
+ return this;
+ }
+
+ /******************************
+ * Take address of expression.
+ */
+ final Expression addressOf()
+ {
+ //printf("Expression::addressOf()\n");
+ debug
+ {
+ assert(op == TOK.error || isLvalue());
+ }
+ Expression e = new AddrExp(loc, this, type.pointerTo());
+ return e;
+ }
+
+ /******************************
+ * If this is a reference, dereference it.
+ */
+ final Expression deref()
+ {
+ //printf("Expression::deref()\n");
+ // type could be null if forward referencing an 'auto' variable
+ if (type)
+ if (auto tr = type.isTypeReference())
+ {
+ Expression e = new PtrExp(loc, this, tr.next);
+ return e;
+ }
+ return this;
+ }
+
+ final Expression optimize(int result, bool keepLvalue = false)
+ {
+ return Expression_optimize(this, result, keepLvalue);
+ }
+
+ // Entry point for CTFE.
+ // A compile-time result is required. Give an error if not possible
+ final Expression ctfeInterpret()
+ {
+ return .ctfeInterpret(this);
+ }
+
+ final int isConst()
+ {
+ return .isConst(this);
+ }
+
+ /********************************
+ * Does this expression statically evaluate to a boolean 'result' (true or false)?
+ */
+ bool isBool(bool result)
+ {
+ return false;
+ }
+
+ bool hasCode()
+ {
+ return true;
+ }
+
+ final pure inout nothrow @nogc @safe
+ {
+ inout(IntegerExp) isIntegerExp() { return op == TOK.int64 ? cast(typeof(return))this : null; }
+ inout(ErrorExp) isErrorExp() { return op == TOK.error ? cast(typeof(return))this : null; }
+ inout(VoidInitExp) isVoidInitExp() { return op == TOK.void_ ? cast(typeof(return))this : null; }
+ inout(RealExp) isRealExp() { return op == TOK.float64 ? cast(typeof(return))this : null; }
+ inout(ComplexExp) isComplexExp() { return op == TOK.complex80 ? cast(typeof(return))this : null; }
+ inout(IdentifierExp) isIdentifierExp() { return op == TOK.identifier ? cast(typeof(return))this : null; }
+ inout(DollarExp) isDollarExp() { return op == TOK.dollar ? cast(typeof(return))this : null; }
+ inout(DsymbolExp) isDsymbolExp() { return op == TOK.dSymbol ? cast(typeof(return))this : null; }
+ inout(ThisExp) isThisExp() { return op == TOK.this_ ? cast(typeof(return))this : null; }
+ inout(SuperExp) isSuperExp() { return op == TOK.super_ ? cast(typeof(return))this : null; }
+ inout(NullExp) isNullExp() { return op == TOK.null_ ? cast(typeof(return))this : null; }
+ inout(StringExp) isStringExp() { return op == TOK.string_ ? cast(typeof(return))this : null; }
+ inout(TupleExp) isTupleExp() { return op == TOK.tuple ? cast(typeof(return))this : null; }
+ inout(ArrayLiteralExp) isArrayLiteralExp() { return op == TOK.arrayLiteral ? cast(typeof(return))this : null; }
+ inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == TOK.assocArrayLiteral ? cast(typeof(return))this : null; }
+ inout(StructLiteralExp) isStructLiteralExp() { return op == TOK.structLiteral ? cast(typeof(return))this : null; }
+ inout(CompoundLiteralExp) isCompoundLiteralExp() { return op == TOK.compoundLiteral ? cast(typeof(return))this : null; }
+ inout(TypeExp) isTypeExp() { return op == TOK.type ? cast(typeof(return))this : null; }
+ inout(ScopeExp) isScopeExp() { return op == TOK.scope_ ? cast(typeof(return))this : null; }
+ inout(TemplateExp) isTemplateExp() { return op == TOK.template_ ? cast(typeof(return))this : null; }
+ inout(NewExp) isNewExp() { return op == TOK.new_ ? cast(typeof(return))this : null; }
+ inout(NewAnonClassExp) isNewAnonClassExp() { return op == TOK.newAnonymousClass ? cast(typeof(return))this : null; }
+ inout(SymOffExp) isSymOffExp() { return op == TOK.symbolOffset ? cast(typeof(return))this : null; }
+ inout(VarExp) isVarExp() { return op == TOK.variable ? cast(typeof(return))this : null; }
+ inout(OverExp) isOverExp() { return op == TOK.overloadSet ? cast(typeof(return))this : null; }
+ inout(FuncExp) isFuncExp() { return op == TOK.function_ ? cast(typeof(return))this : null; }
+ inout(DeclarationExp) isDeclarationExp() { return op == TOK.declaration ? cast(typeof(return))this : null; }
+ inout(TypeidExp) isTypeidExp() { return op == TOK.typeid_ ? cast(typeof(return))this : null; }
+ inout(TraitsExp) isTraitsExp() { return op == TOK.traits ? cast(typeof(return))this : null; }
+ inout(HaltExp) isHaltExp() { return op == TOK.halt ? cast(typeof(return))this : null; }
+ inout(IsExp) isExp() { return op == TOK.is_ ? cast(typeof(return))this : null; }
+ inout(MixinExp) isMixinExp() { return op == TOK.mixin_ ? cast(typeof(return))this : null; }
+ inout(ImportExp) isImportExp() { return op == TOK.import_ ? cast(typeof(return))this : null; }
+ inout(AssertExp) isAssertExp() { return op == TOK.assert_ ? cast(typeof(return))this : null; }
+ inout(DotIdExp) isDotIdExp() { return op == TOK.dotIdentifier ? cast(typeof(return))this : null; }
+ inout(DotTemplateExp) isDotTemplateExp() { return op == TOK.dotTemplateDeclaration ? cast(typeof(return))this : null; }
+ inout(DotVarExp) isDotVarExp() { return op == TOK.dotVariable ? cast(typeof(return))this : null; }
+ inout(DotTemplateInstanceExp) isDotTemplateInstanceExp() { return op == TOK.dotTemplateInstance ? cast(typeof(return))this : null; }
+ inout(DelegateExp) isDelegateExp() { return op == TOK.delegate_ ? cast(typeof(return))this : null; }
+ inout(DotTypeExp) isDotTypeExp() { return op == TOK.dotType ? cast(typeof(return))this : null; }
+ inout(CallExp) isCallExp() { return op == TOK.call ? cast(typeof(return))this : null; }
+ inout(AddrExp) isAddrExp() { return op == TOK.address ? cast(typeof(return))this : null; }
+ inout(PtrExp) isPtrExp() { return op == TOK.star ? cast(typeof(return))this : null; }
+ inout(NegExp) isNegExp() { return op == TOK.negate ? cast(typeof(return))this : null; }
+ inout(UAddExp) isUAddExp() { return op == TOK.uadd ? cast(typeof(return))this : null; }
+ inout(ComExp) isComExp() { return op == TOK.tilde ? cast(typeof(return))this : null; }
+ inout(NotExp) isNotExp() { return op == TOK.not ? cast(typeof(return))this : null; }
+ inout(DeleteExp) isDeleteExp() { return op == TOK.delete_ ? cast(typeof(return))this : null; }
+ inout(CastExp) isCastExp() { return op == TOK.cast_ ? cast(typeof(return))this : null; }
+ inout(VectorExp) isVectorExp() { return op == TOK.vector ? cast(typeof(return))this : null; }
+ inout(VectorArrayExp) isVectorArrayExp() { return op == TOK.vectorArray ? cast(typeof(return))this : null; }
+ inout(SliceExp) isSliceExp() { return op == TOK.slice ? cast(typeof(return))this : null; }
+ inout(ArrayLengthExp) isArrayLengthExp() { return op == TOK.arrayLength ? cast(typeof(return))this : null; }
+ inout(ArrayExp) isArrayExp() { return op == TOK.array ? cast(typeof(return))this : null; }
+ inout(DotExp) isDotExp() { return op == TOK.dot ? cast(typeof(return))this : null; }
+ inout(CommaExp) isCommaExp() { return op == TOK.comma ? cast(typeof(return))this : null; }
+ inout(IntervalExp) isIntervalExp() { return op == TOK.interval ? cast(typeof(return))this : null; }
+ inout(DelegatePtrExp) isDelegatePtrExp() { return op == TOK.delegatePointer ? cast(typeof(return))this : null; }
+ inout(DelegateFuncptrExp) isDelegateFuncptrExp() { return op == TOK.delegateFunctionPointer ? cast(typeof(return))this : null; }
+ inout(IndexExp) isIndexExp() { return op == TOK.index ? cast(typeof(return))this : null; }
+ inout(PostExp) isPostExp() { return (op == TOK.plusPlus || op == TOK.minusMinus) ? cast(typeof(return))this : null; }
+ inout(PreExp) isPreExp() { return (op == TOK.prePlusPlus || op == TOK.preMinusMinus) ? cast(typeof(return))this : null; }
+ inout(AssignExp) isAssignExp() { return op == TOK.assign ? cast(typeof(return))this : null; }
+ inout(ConstructExp) isConstructExp() { return op == TOK.construct ? cast(typeof(return))this : null; }
+ inout(BlitExp) isBlitExp() { return op == TOK.blit ? cast(typeof(return))this : null; }
+ inout(AddAssignExp) isAddAssignExp() { return op == TOK.addAssign ? cast(typeof(return))this : null; }
+ inout(MinAssignExp) isMinAssignExp() { return op == TOK.minAssign ? cast(typeof(return))this : null; }
+ inout(MulAssignExp) isMulAssignExp() { return op == TOK.mulAssign ? cast(typeof(return))this : null; }
+
+ inout(DivAssignExp) isDivAssignExp() { return op == TOK.divAssign ? cast(typeof(return))this : null; }
+ inout(ModAssignExp) isModAssignExp() { return op == TOK.modAssign ? cast(typeof(return))this : null; }
+ inout(AndAssignExp) isAndAssignExp() { return op == TOK.andAssign ? cast(typeof(return))this : null; }
+ inout(OrAssignExp) isOrAssignExp() { return op == TOK.orAssign ? cast(typeof(return))this : null; }
+ inout(XorAssignExp) isXorAssignExp() { return op == TOK.xorAssign ? cast(typeof(return))this : null; }
+ inout(PowAssignExp) isPowAssignExp() { return op == TOK.powAssign ? cast(typeof(return))this : null; }
+
+ inout(ShlAssignExp) isShlAssignExp() { return op == TOK.leftShiftAssign ? cast(typeof(return))this : null; }
+ inout(ShrAssignExp) isShrAssignExp() { return op == TOK.rightShiftAssign ? cast(typeof(return))this : null; }
+ inout(UshrAssignExp) isUshrAssignExp() { return op == TOK.unsignedRightShiftAssign ? cast(typeof(return))this : null; }
+
+ inout(CatAssignExp) isCatAssignExp() { return op == TOK.concatenateAssign
+ ? cast(typeof(return))this
+ : null; }
+
+ inout(CatElemAssignExp) isCatElemAssignExp() { return op == TOK.concatenateElemAssign
+ ? cast(typeof(return))this
+ : null; }
+
+ inout(CatDcharAssignExp) isCatDcharAssignExp() { return op == TOK.concatenateDcharAssign
+ ? cast(typeof(return))this
+ : null; }
+
+ inout(AddExp) isAddExp() { return op == TOK.add ? cast(typeof(return))this : null; }
+ inout(MinExp) isMinExp() { return op == TOK.min ? cast(typeof(return))this : null; }
+ inout(CatExp) isCatExp() { return op == TOK.concatenate ? cast(typeof(return))this : null; }
+ inout(MulExp) isMulExp() { return op == TOK.mul ? cast(typeof(return))this : null; }
+ inout(DivExp) isDivExp() { return op == TOK.div ? cast(typeof(return))this : null; }
+ inout(ModExp) isModExp() { return op == TOK.mod ? cast(typeof(return))this : null; }
+ inout(PowExp) isPowExp() { return op == TOK.pow ? cast(typeof(return))this : null; }
+ inout(ShlExp) isShlExp() { return op == TOK.leftShift ? cast(typeof(return))this : null; }
+ inout(ShrExp) isShrExp() { return op == TOK.rightShift ? cast(typeof(return))this : null; }
+ inout(UshrExp) isUshrExp() { return op == TOK.unsignedRightShift ? cast(typeof(return))this : null; }
+ inout(AndExp) isAndExp() { return op == TOK.and ? cast(typeof(return))this : null; }
+ inout(OrExp) isOrExp() { return op == TOK.or ? cast(typeof(return))this : null; }
+ inout(XorExp) isXorExp() { return op == TOK.xor ? cast(typeof(return))this : null; }
+ inout(LogicalExp) isLogicalExp() { return (op == TOK.andAnd || op == TOK.orOr) ? cast(typeof(return))this : null; }
+ //inout(CmpExp) isCmpExp() { return op == TOK. ? cast(typeof(return))this : null; }
+ inout(InExp) isInExp() { return op == TOK.in_ ? cast(typeof(return))this : null; }
+ inout(RemoveExp) isRemoveExp() { return op == TOK.remove ? cast(typeof(return))this : null; }
+ inout(EqualExp) isEqualExp() { return (op == TOK.equal || op == TOK.notEqual) ? cast(typeof(return))this : null; }
+ inout(IdentityExp) isIdentityExp() { return (op == TOK.identity || op == TOK.notIdentity) ? cast(typeof(return))this : null; }
+ inout(CondExp) isCondExp() { return op == TOK.question ? cast(typeof(return))this : null; }
+ inout(GenericExp) isGenericExp() { return op == TOK._Generic ? cast(typeof(return))this : null; }
+ inout(DefaultInitExp) isDefaultInitExp() { return isDefaultInitOp(op) ? cast(typeof(return))this: null; }
+ inout(FileInitExp) isFileInitExp() { return (op == TOK.file || op == TOK.fileFullPath) ? cast(typeof(return))this : null; }
+ inout(LineInitExp) isLineInitExp() { return op == TOK.line ? cast(typeof(return))this : null; }
+ inout(ModuleInitExp) isModuleInitExp() { return op == TOK.moduleString ? cast(typeof(return))this : null; }
+ inout(FuncInitExp) isFuncInitExp() { return op == TOK.functionString ? cast(typeof(return))this : null; }
+ inout(PrettyFuncInitExp) isPrettyFuncInitExp() { return op == TOK.prettyFunction ? cast(typeof(return))this : null; }
+ inout(ClassReferenceExp) isClassReferenceExp() { return op == TOK.classReference ? cast(typeof(return))this : null; }
+ inout(ThrownExceptionExp) isThrownExceptionExp() { return op == TOK.thrownException ? cast(typeof(return))this : null; }
+ }
+
+ inout(BinAssignExp) isBinAssignExp() pure inout nothrow @nogc
+ {
+ return null;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class IntegerExp : Expression
+{
+ private dinteger_t value;
+
+ extern (D) this(const ref Loc loc, dinteger_t value, Type type)
+ {
+ super(loc, TOK.int64, __traits(classInstanceSize, IntegerExp));
+ //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : "");
+ assert(type);
+ if (!type.isscalar())
+ {
+ //printf("%s, loc = %d\n", toChars(), loc.linnum);
+ if (type.ty != Terror)
+ error("integral constant must be scalar type, not `%s`", type.toChars());
+ type = Type.terror;
+ }
+ this.type = type;
+ this.value = normalize(type.toBasetype().ty, value);
+ }
+
+ extern (D) this(dinteger_t value)
+ {
+ super(Loc.initial, TOK.int64, __traits(classInstanceSize, IntegerExp));
+ this.type = Type.tint32;
+ this.value = cast(d_int32)value;
+ }
+
+ static IntegerExp create(Loc loc, dinteger_t value, Type type)
+ {
+ return new IntegerExp(loc, value, type);
+ }
+
+ // Same as create, but doesn't allocate memory.
+ static void emplace(UnionExp* pue, Loc loc, dinteger_t value, Type type)
+ {
+ emplaceExp!(IntegerExp)(pue, loc, value, type);
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ if (auto ne = (cast(Expression)o).isIntegerExp())
+ {
+ if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && value == ne.value)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ override dinteger_t toInteger()
+ {
+ // normalize() is necessary until we fix all the paints of 'type'
+ return value = normalize(type.toBasetype().ty, value);
+ }
+
+ override real_t toReal()
+ {
+ // normalize() is necessary until we fix all the paints of 'type'
+ const ty = type.toBasetype().ty;
+ const val = normalize(ty, value);
+ value = val;
+ return (ty == Tuns64)
+ ? real_t(cast(d_uns64)val)
+ : real_t(cast(d_int64)val);
+ }
+
+ override real_t toImaginary()
+ {
+ return CTFloat.zero;
+ }
+
+ override complex_t toComplex()
+ {
+ return complex_t(toReal());
+ }
+
+ override bool isBool(bool result)
+ {
+ bool r = toInteger() != 0;
+ return result ? r : !r;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (!e)
+ e = this;
+ else if (!loc.isValid())
+ loc = e.loc;
+ e.error("cannot modify constant `%s`", e.toChars());
+ return ErrorExp.get();
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ dinteger_t getInteger()
+ {
+ return value;
+ }
+
+ void setInteger(dinteger_t value)
+ {
+ this.value = normalize(type.toBasetype().ty, value);
+ }
+
+ extern (D) static dinteger_t normalize(TY ty, dinteger_t value)
+ {
+ /* 'Normalize' the value of the integer to be in range of the type
+ */
+ dinteger_t result;
+ switch (ty)
+ {
+ case Tbool:
+ result = (value != 0);
+ break;
+
+ case Tint8:
+ result = cast(d_int8)value;
+ break;
+
+ case Tchar:
+ case Tuns8:
+ result = cast(d_uns8)value;
+ break;
+
+ case Tint16:
+ result = cast(d_int16)value;
+ break;
+
+ case Twchar:
+ case Tuns16:
+ result = cast(d_uns16)value;
+ break;
+
+ case Tint32:
+ result = cast(d_int32)value;
+ break;
+
+ case Tdchar:
+ case Tuns32:
+ result = cast(d_uns32)value;
+ break;
+
+ case Tint64:
+ result = cast(d_int64)value;
+ break;
+
+ case Tuns64:
+ result = cast(d_uns64)value;
+ break;
+
+ case Tpointer:
+ if (target.ptrsize == 8)
+ goto case Tuns64;
+ if (target.ptrsize == 4)
+ goto case Tuns32;
+ if (target.ptrsize == 2)
+ goto case Tuns16;
+ assert(0);
+
+ default:
+ break;
+ }
+ return result;
+ }
+
+ override IntegerExp syntaxCopy()
+ {
+ return this;
+ }
+
+ /**
+ * Use this instead of creating new instances for commonly used literals
+ * such as 0 or 1.
+ *
+ * Parameters:
+ * v = The value of the expression
+ * Returns:
+ * A static instance of the expression, typed as `Tint32`.
+ */
+ static IntegerExp literal(int v)()
+ {
+ __gshared IntegerExp theConstant;
+ if (!theConstant)
+ theConstant = new IntegerExp(v);
+ return theConstant;
+ }
+
+ /**
+ * Use this instead of creating new instances for commonly used bools.
+ *
+ * Parameters:
+ * b = The value of the expression
+ * Returns:
+ * A static instance of the expression, typed as `Type.tbool`.
+ */
+ static IntegerExp createBool(bool b)
+ {
+ __gshared IntegerExp trueExp, falseExp;
+ if (!trueExp)
+ {
+ trueExp = new IntegerExp(Loc.initial, 1, Type.tbool);
+ falseExp = new IntegerExp(Loc.initial, 0, Type.tbool);
+ }
+ return b ? trueExp : falseExp;
+ }
+}
+
+/***********************************************************
+ * Use this expression for error recovery.
+ * It should behave as a 'sink' to prevent further cascaded error messages.
+ */
+extern (C++) final class ErrorExp : Expression
+{
+ private extern (D) this()
+ {
+ super(Loc.initial, TOK.error, __traits(classInstanceSize, ErrorExp));
+ type = Type.terror;
+ }
+
+ static ErrorExp get ()
+ {
+ if (errorexp is null)
+ errorexp = new ErrorExp();
+
+ if (global.errors == 0 && global.gaggedErrors == 0)
+ {
+ /* Unfortunately, errors can still leak out of gagged errors,
+ * and we need to set the error count to prevent bogus code
+ * generation. At least give a message.
+ */
+ .error(Loc.initial, "unknown, please file report on issues.dlang.org");
+ }
+
+ return errorexp;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ extern (C++) __gshared ErrorExp errorexp; // handy shared value
+}
+
+
+/***********************************************************
+ * An uninitialized value,
+ * generated from void initializers.
+ */
+extern (C++) final class VoidInitExp : Expression
+{
+ VarDeclaration var; /// the variable from where the void value came from, null if not known
+ /// Useful for error messages
+
+ extern (D) this(VarDeclaration var)
+ {
+ super(var.loc, TOK.void_, __traits(classInstanceSize, VoidInitExp));
+ this.var = var;
+ this.type = var.type;
+ }
+
+ override const(char)* toChars() const
+ {
+ return "void";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+
+/***********************************************************
+ */
+extern (C++) final class RealExp : Expression
+{
+ real_t value;
+
+ extern (D) this(const ref Loc loc, real_t value, Type type)
+ {
+ super(loc, TOK.float64, __traits(classInstanceSize, RealExp));
+ //printf("RealExp::RealExp(%Lg)\n", value);
+ this.value = value;
+ this.type = type;
+ }
+
+ static RealExp create(Loc loc, real_t value, Type type)
+ {
+ return new RealExp(loc, value, type);
+ }
+
+ // Same as create, but doesn't allocate memory.
+ static void emplace(UnionExp* pue, Loc loc, real_t value, Type type)
+ {
+ emplaceExp!(RealExp)(pue, loc, value, type);
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ if (auto ne = (cast(Expression)o).isRealExp())
+ {
+ if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealIdentical(value, ne.value))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ override dinteger_t toInteger()
+ {
+ return cast(sinteger_t)toReal();
+ }
+
+ override uinteger_t toUInteger()
+ {
+ return cast(uinteger_t)toReal();
+ }
+
+ override real_t toReal()
+ {
+ return type.isreal() ? value : CTFloat.zero;
+ }
+
+ override real_t toImaginary()
+ {
+ return type.isreal() ? CTFloat.zero : value;
+ }
+
+ override complex_t toComplex()
+ {
+ return complex_t(toReal(), toImaginary());
+ }
+
+ override bool isBool(bool result)
+ {
+ return result ? cast(bool)value : !cast(bool)value;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ComplexExp : Expression
+{
+ complex_t value;
+
+ extern (D) this(const ref Loc loc, complex_t value, Type type)
+ {
+ super(loc, TOK.complex80, __traits(classInstanceSize, ComplexExp));
+ this.value = value;
+ this.type = type;
+ //printf("ComplexExp::ComplexExp(%s)\n", toChars());
+ }
+
+ static ComplexExp create(Loc loc, complex_t value, Type type)
+ {
+ return new ComplexExp(loc, value, type);
+ }
+
+ // Same as create, but doesn't allocate memory.
+ static void emplace(UnionExp* pue, Loc loc, complex_t value, Type type)
+ {
+ emplaceExp!(ComplexExp)(pue, loc, value, type);
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ if (auto ne = (cast(Expression)o).isComplexExp())
+ {
+ if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealIdentical(creall(value), creall(ne.value)) && RealIdentical(cimagl(value), cimagl(ne.value)))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ override dinteger_t toInteger()
+ {
+ return cast(sinteger_t)toReal();
+ }
+
+ override uinteger_t toUInteger()
+ {
+ return cast(uinteger_t)toReal();
+ }
+
+ override real_t toReal()
+ {
+ return creall(value);
+ }
+
+ override real_t toImaginary()
+ {
+ return cimagl(value);
+ }
+
+ override complex_t toComplex()
+ {
+ return value;
+ }
+
+ override bool isBool(bool result)
+ {
+ if (result)
+ return cast(bool)value;
+ else
+ return !value;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class IdentifierExp : Expression
+{
+ Identifier ident;
+
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, TOK.identifier, __traits(classInstanceSize, IdentifierExp));
+ this.ident = ident;
+ }
+
+ static IdentifierExp create(Loc loc, Identifier ident)
+ {
+ return new IdentifierExp(loc, ident);
+ }
+
+ override final bool isLvalue()
+ {
+ return true;
+ }
+
+ override final Expression toLvalue(Scope* sc, Expression e)
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DollarExp : IdentifierExp
+{
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, Id.dollar);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Won't be generated by parser.
+ */
+extern (C++) final class DsymbolExp : Expression
+{
+ Dsymbol s;
+ bool hasOverloads;
+
+ extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true)
+ {
+ super(loc, TOK.dSymbol, __traits(classInstanceSize, DsymbolExp));
+ this.s = s;
+ this.hasOverloads = hasOverloads;
+ }
+
+ override bool isLvalue()
+ {
+ return true;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#this
+ */
+extern (C++) class ThisExp : Expression
+{
+ VarDeclaration var;
+
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, TOK.this_, __traits(classInstanceSize, ThisExp));
+ //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
+ }
+
+ this(const ref Loc loc, const TOK tok)
+ {
+ super(loc, tok, __traits(classInstanceSize, ThisExp));
+ //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
+ }
+
+ override ThisExp syntaxCopy()
+ {
+ auto r = cast(ThisExp) super.syntaxCopy();
+ // require new semantic (possibly new `var` etc.)
+ r.type = null;
+ r.var = null;
+ return r;
+ }
+
+ override final bool isBool(bool result)
+ {
+ return result;
+ }
+
+ override final bool isLvalue()
+ {
+ // Class `this` should be an rvalue; struct `this` should be an lvalue.
+ return type.toBasetype().ty != Tclass;
+ }
+
+ override final Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (type.toBasetype().ty == Tclass)
+ {
+ // Class `this` is an rvalue; struct `this` is an lvalue.
+ return Expression.toLvalue(sc, e);
+ }
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#super
+ */
+extern (C++) final class SuperExp : ThisExp
+{
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, TOK.super_);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#null
+ */
+extern (C++) final class NullExp : Expression
+{
+ extern (D) this(const ref Loc loc, Type type = null)
+ {
+ super(loc, TOK.null_, __traits(classInstanceSize, NullExp));
+ this.type = type;
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (auto e = o.isExpression())
+ {
+ if (e.op == TOK.null_ && type.equals(e.type))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ override bool isBool(bool result)
+ {
+ return result ? false : true;
+ }
+
+ override StringExp toStringExp()
+ {
+ if (implicitConvTo(Type.tstring))
+ {
+ auto se = new StringExp(loc, (cast(char*)mem.xcalloc(1, 1))[0 .. 0]);
+ se.type = Type.tstring;
+ return se;
+ }
+ return null;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#string_literals
+ */
+extern (C++) final class StringExp : Expression
+{
+ private union
+ {
+ char* string; // if sz == 1
+ wchar* wstring; // if sz == 2
+ dchar* dstring; // if sz == 4
+ } // (const if ownedByCtfe == OwnedBy.code)
+ size_t len; // number of code units
+ ubyte sz = 1; // 1: char, 2: wchar, 4: dchar
+ ubyte committed; // !=0 if type is committed
+ enum char NoPostfix = 0;
+ char postfix = NoPostfix; // 'c', 'w', 'd'
+ OwnedBy ownedByCtfe = OwnedBy.code;
+
+ extern (D) this(const ref Loc loc, const(void)[] string)
+ {
+ super(loc, TOK.string_, __traits(classInstanceSize, StringExp));
+ this.string = cast(char*)string.ptr; // note that this.string should be const
+ this.len = string.length;
+ this.sz = 1; // work around LDC bug #1286
+ }
+
+ extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix)
+ {
+ super(loc, TOK.string_, __traits(classInstanceSize, StringExp));
+ this.string = cast(char*)string.ptr; // note that this.string should be const
+ this.len = len;
+ this.sz = sz;
+ this.postfix = postfix;
+ }
+
+ static StringExp create(Loc loc, char* s)
+ {
+ return new StringExp(loc, s.toDString());
+ }
+
+ static StringExp create(Loc loc, void* string, size_t len)
+ {
+ return new StringExp(loc, string[0 .. len]);
+ }
+
+ // Same as create, but doesn't allocate memory.
+ static void emplace(UnionExp* pue, Loc loc, char* s)
+ {
+ emplaceExp!(StringExp)(pue, loc, s.toDString());
+ }
+
+ extern (D) static void emplace(UnionExp* pue, Loc loc, const(void)[] string)
+ {
+ emplaceExp!(StringExp)(pue, loc, string);
+ }
+
+ extern (D) static void emplace(UnionExp* pue, Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix)
+ {
+ emplaceExp!(StringExp)(pue, loc, string, len, sz, postfix);
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ //printf("StringExp::equals('%s') %s\n", o.toChars(), toChars());
+ if (auto e = o.isExpression())
+ {
+ if (auto se = e.isStringExp())
+ {
+ return compare(se) == 0;
+ }
+ }
+ return false;
+ }
+
+ /**********************************
+ * Return the number of code units the string would be if it were re-encoded
+ * as tynto.
+ * Params:
+ * tynto = code unit type of the target encoding
+ * Returns:
+ * number of code units
+ */
+ size_t numberOfCodeUnits(int tynto = 0) const
+ {
+ int encSize;
+ switch (tynto)
+ {
+ case 0: return len;
+ case Tchar: encSize = 1; break;
+ case Twchar: encSize = 2; break;
+ case Tdchar: encSize = 4; break;
+ default:
+ assert(0);
+ }
+ if (sz == encSize)
+ return len;
+
+ size_t result = 0;
+ dchar c;
+
+ switch (sz)
+ {
+ case 1:
+ for (size_t u = 0; u < len;)
+ {
+ if (const s = utf_decodeChar(string[0 .. len], u, c))
+ {
+ error("%.*s", cast(int)s.length, s.ptr);
+ return 0;
+ }
+ result += utf_codeLength(encSize, c);
+ }
+ break;
+
+ case 2:
+ for (size_t u = 0; u < len;)
+ {
+ if (const s = utf_decodeWchar(wstring[0 .. len], u, c))
+ {
+ error("%.*s", cast(int)s.length, s.ptr);
+ return 0;
+ }
+ result += utf_codeLength(encSize, c);
+ }
+ break;
+
+ case 4:
+ foreach (u; 0 .. len)
+ {
+ result += utf_codeLength(encSize, dstring[u]);
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ return result;
+ }
+
+ /**********************************************
+ * Write the contents of the string to dest.
+ * Use numberOfCodeUnits() to determine size of result.
+ * Params:
+ * dest = destination
+ * tyto = encoding type of the result
+ * zero = add terminating 0
+ */
+ void writeTo(void* dest, bool zero, int tyto = 0) const
+ {
+ int encSize;
+ switch (tyto)
+ {
+ case 0: encSize = sz; break;
+ case Tchar: encSize = 1; break;
+ case Twchar: encSize = 2; break;
+ case Tdchar: encSize = 4; break;
+ default:
+ assert(0);
+ }
+ if (sz == encSize)
+ {
+ memcpy(dest, string, len * sz);
+ if (zero)
+ memset(dest + len * sz, 0, sz);
+ }
+ else
+ assert(0);
+ }
+
+ /*********************************************
+ * Get the code unit at index i
+ * Params:
+ * i = index
+ * Returns:
+ * code unit at index i
+ */
+ dchar getCodeUnit(size_t i) const pure
+ {
+ assert(i < len);
+ final switch (sz)
+ {
+ case 1:
+ return string[i];
+ case 2:
+ return wstring[i];
+ case 4:
+ return dstring[i];
+ }
+ }
+
+ /*********************************************
+ * Set the code unit at index i to c
+ * Params:
+ * i = index
+ * c = code unit to set it to
+ */
+ void setCodeUnit(size_t i, dchar c)
+ {
+ assert(i < len);
+ final switch (sz)
+ {
+ case 1:
+ string[i] = cast(char)c;
+ break;
+ case 2:
+ wstring[i] = cast(wchar)c;
+ break;
+ case 4:
+ dstring[i] = c;
+ break;
+ }
+ }
+
+ override StringExp toStringExp()
+ {
+ return this;
+ }
+
+ /****************************************
+ * Convert string to char[].
+ */
+ StringExp toUTF8(Scope* sc)
+ {
+ if (sz != 1)
+ {
+ // Convert to UTF-8 string
+ committed = 0;
+ Expression e = castTo(sc, Type.tchar.arrayOf());
+ e = e.optimize(WANTvalue);
+ auto se = e.isStringExp();
+ assert(se.sz == 1);
+ return se;
+ }
+ return this;
+ }
+
+ /**
+ * Compare two `StringExp` by length, then value
+ *
+ * The comparison is not the usual C-style comparison as seen with
+ * `strcmp` or `memcmp`, but instead first compare based on the length.
+ * This allows both faster lookup and sorting when comparing sparse data.
+ *
+ * This ordering scheme is relied on by the string-switching feature.
+ * Code in Druntime's `core.internal.switch_` relies on this ordering
+ * when doing a binary search among case statements.
+ *
+ * Both `StringExp` should be of the same encoding.
+ *
+ * Params:
+ * se2 = String expression to compare `this` to
+ *
+ * Returns:
+ * `0` when `this` is equal to se2, a value greater than `0` if
+ * `this` should be considered greater than `se2`,
+ * and a value less than `0` if `this` is lesser than `se2`.
+ */
+ int compare(const StringExp se2) const nothrow pure @nogc
+ {
+ //printf("StringExp::compare()\n");
+ const len1 = len;
+ const len2 = se2.len;
+
+ assert(this.sz == se2.sz, "Comparing string expressions of different sizes");
+ //printf("sz = %d, len1 = %d, len2 = %d\n", sz, (int)len1, (int)len2);
+ if (len1 == len2)
+ {
+ switch (sz)
+ {
+ case 1:
+ return memcmp(string, se2.string, len1);
+
+ case 2:
+ {
+ wchar* s1 = cast(wchar*)string;
+ wchar* s2 = cast(wchar*)se2.string;
+ foreach (u; 0 .. len)
+ {
+ if (s1[u] != s2[u])
+ return s1[u] - s2[u];
+ }
+ }
+ break;
+ case 4:
+ {
+ dchar* s1 = cast(dchar*)string;
+ dchar* s2 = cast(dchar*)se2.string;
+ foreach (u; 0 .. len)
+ {
+ if (s1[u] != s2[u])
+ return s1[u] - s2[u];
+ }
+ }
+ break;
+ default:
+ assert(0);
+ }
+ }
+ return cast(int)(len1 - len2);
+ }
+
+ override bool isBool(bool result)
+ {
+ return result;
+ }
+
+ override bool isLvalue()
+ {
+ /* string literal is rvalue in default, but
+ * conversion to reference of static array is only allowed.
+ */
+ return (type && type.toBasetype().ty == Tsarray);
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ //printf("StringExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL);
+ return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ error("cannot modify string literal `%s`", toChars());
+ return ErrorExp.get();
+ }
+
+ uint charAt(uinteger_t i) const
+ {
+ uint value;
+ switch (sz)
+ {
+ case 1:
+ value = (cast(char*)string)[cast(size_t)i];
+ break;
+
+ case 2:
+ value = (cast(ushort*)string)[cast(size_t)i];
+ break;
+
+ case 4:
+ value = (cast(uint*)string)[cast(size_t)i];
+ break;
+
+ default:
+ assert(0);
+ }
+ return value;
+ }
+
+ /********************************
+ * Convert string contents to a 0 terminated string,
+ * allocated by mem.xmalloc().
+ */
+ extern (D) const(char)[] toStringz() const
+ {
+ auto nbytes = len * sz;
+ char* s = cast(char*)mem.xmalloc(nbytes + sz);
+ writeTo(s, true);
+ return s[0 .. nbytes];
+ }
+
+ extern (D) const(char)[] peekString() const
+ {
+ assert(sz == 1);
+ return this.string[0 .. len];
+ }
+
+ extern (D) const(wchar)[] peekWstring() const
+ {
+ assert(sz == 2);
+ return this.wstring[0 .. len];
+ }
+
+ extern (D) const(dchar)[] peekDstring() const
+ {
+ assert(sz == 4);
+ return this.dstring[0 .. len];
+ }
+
+ /*******************
+ * Get a slice of the data.
+ */
+ extern (D) const(ubyte)[] peekData() const
+ {
+ return cast(const(ubyte)[])this.string[0 .. len * sz];
+ }
+
+ /*******************
+ * Borrow a slice of the data, so the caller can modify
+ * it in-place (!)
+ */
+ extern (D) ubyte[] borrowData()
+ {
+ return cast(ubyte[])this.string[0 .. len * sz];
+ }
+
+ /***********************
+ * Set new string data.
+ * `this` becomes the new owner of the data.
+ */
+ extern (D) void setData(void* s, size_t len, ubyte sz)
+ {
+ this.string = cast(char*)s;
+ this.len = len;
+ this.sz = sz;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TupleExp : Expression
+{
+ /* Tuple-field access may need to take out its side effect part.
+ * For example:
+ * foo().tupleof
+ * is rewritten as:
+ * (ref __tup = foo(); tuple(__tup.field0, __tup.field1, ...))
+ * The declaration of temporary variable __tup will be stored in TupleExp.e0.
+ */
+ Expression e0;
+
+ Expressions* exps;
+
+ extern (D) this(const ref Loc loc, Expression e0, Expressions* exps)
+ {
+ super(loc, TOK.tuple, __traits(classInstanceSize, TupleExp));
+ //printf("TupleExp(this = %p)\n", this);
+ this.e0 = e0;
+ this.exps = exps;
+ }
+
+ extern (D) this(const ref Loc loc, Expressions* exps)
+ {
+ super(loc, TOK.tuple, __traits(classInstanceSize, TupleExp));
+ //printf("TupleExp(this = %p)\n", this);
+ this.exps = exps;
+ }
+
+ extern (D) this(const ref Loc loc, TupleDeclaration tup)
+ {
+ super(loc, TOK.tuple, __traits(classInstanceSize, TupleExp));
+ this.exps = new Expressions();
+
+ this.exps.reserve(tup.objects.dim);
+ foreach (o; *tup.objects)
+ {
+ if (Dsymbol s = getDsymbol(o))
+ {
+ /* If tuple element represents a symbol, translate to DsymbolExp
+ * to supply implicit 'this' if needed later.
+ */
+ Expression e = new DsymbolExp(loc, s);
+ this.exps.push(e);
+ }
+ else if (auto eo = o.isExpression())
+ {
+ auto e = eo.copy();
+ e.loc = loc; // https://issues.dlang.org/show_bug.cgi?id=15669
+ this.exps.push(e);
+ }
+ else if (auto t = o.isType())
+ {
+ Expression e = new TypeExp(loc, t);
+ this.exps.push(e);
+ }
+ else
+ {
+ error("`%s` is not an expression", o.toChars());
+ }
+ }
+ }
+
+ static TupleExp create(Loc loc, Expressions* exps)
+ {
+ return new TupleExp(loc, exps);
+ }
+
+ override TupleExp toTupleExp()
+ {
+ return this;
+ }
+
+ override TupleExp syntaxCopy()
+ {
+ return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps));
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ if (auto e = o.isExpression())
+ if (auto te = e.isTupleExp())
+ {
+ if (exps.dim != te.exps.dim)
+ return false;
+ if (e0 && !e0.equals(te.e0) || !e0 && te.e0)
+ return false;
+ foreach (i, e1; *exps)
+ {
+ auto e2 = (*te.exps)[i];
+ if (!e1.equals(e2))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * [ e1, e2, e3, ... ]
+ *
+ * http://dlang.org/spec/expression.html#array_literals
+ */
+extern (C++) final class ArrayLiteralExp : Expression
+{
+ /** If !is null, elements[] can be sparse and basis is used for the
+ * "default" element value. In other words, non-null elements[i] overrides
+ * this 'basis' value.
+ */
+ Expression basis;
+
+ Expressions* elements;
+ OwnedBy ownedByCtfe = OwnedBy.code;
+
+
+ extern (D) this(const ref Loc loc, Type type, Expressions* elements)
+ {
+ super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
+ this.type = type;
+ this.elements = elements;
+ }
+
+ extern (D) this(const ref Loc loc, Type type, Expression e)
+ {
+ super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
+ this.type = type;
+ elements = new Expressions();
+ elements.push(e);
+ }
+
+ extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements)
+ {
+ super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
+ this.type = type;
+ this.basis = basis;
+ this.elements = elements;
+ }
+
+ static ArrayLiteralExp create(Loc loc, Expressions* elements)
+ {
+ return new ArrayLiteralExp(loc, null, elements);
+ }
+
+ // Same as create, but doesn't allocate memory.
+ static void emplace(UnionExp* pue, Loc loc, Expressions* elements)
+ {
+ emplaceExp!(ArrayLiteralExp)(pue, loc, null, elements);
+ }
+
+ override ArrayLiteralExp syntaxCopy()
+ {
+ return new ArrayLiteralExp(loc,
+ null,
+ basis ? basis.syntaxCopy() : null,
+ arraySyntaxCopy(elements));
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ auto e = o.isExpression();
+ if (!e)
+ return false;
+ if (auto ae = e.isArrayLiteralExp())
+ {
+ if (elements.dim != ae.elements.dim)
+ return false;
+ if (elements.dim == 0 && !type.equals(ae.type))
+ {
+ return false;
+ }
+
+ foreach (i, e1; *elements)
+ {
+ auto e2 = (*ae.elements)[i];
+ auto e1x = e1 ? e1 : basis;
+ auto e2x = e2 ? e2 : ae.basis;
+
+ if (e1x != e2x && (!e1x || !e2x || !e1x.equals(e2x)))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ Expression getElement(size_t i)
+ {
+ return this[i];
+ }
+
+ Expression opIndex(size_t i)
+ {
+ auto el = (*elements)[i];
+ return el ? el : basis;
+ }
+
+ override bool isBool(bool result)
+ {
+ size_t dim = elements ? elements.dim : 0;
+ return result ? (dim != 0) : (dim == 0);
+ }
+
+ override StringExp toStringExp()
+ {
+ TY telem = type.nextOf().toBasetype().ty;
+ if (telem.isSomeChar || (telem == Tvoid && (!elements || elements.dim == 0)))
+ {
+ ubyte sz = 1;
+ if (telem == Twchar)
+ sz = 2;
+ else if (telem == Tdchar)
+ sz = 4;
+
+ OutBuffer buf;
+ if (elements)
+ {
+ foreach (i; 0 .. elements.dim)
+ {
+ auto ch = this[i];
+ if (ch.op != TOK.int64)
+ return null;
+ if (sz == 1)
+ buf.writeByte(cast(uint)ch.toInteger());
+ else if (sz == 2)
+ buf.writeword(cast(uint)ch.toInteger());
+ else
+ buf.write4(cast(uint)ch.toInteger());
+ }
+ }
+ char prefix;
+ if (sz == 1)
+ {
+ prefix = 'c';
+ buf.writeByte(0);
+ }
+ else if (sz == 2)
+ {
+ prefix = 'w';
+ buf.writeword(0);
+ }
+ else
+ {
+ prefix = 'd';
+ buf.write4(0);
+ }
+
+ const size_t len = buf.length / sz - 1;
+ auto se = new StringExp(loc, buf.extractSlice()[0 .. len * sz], len, sz, prefix);
+ se.sz = sz;
+ se.type = type;
+ return se;
+ }
+ return null;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * [ key0 : value0, key1 : value1, ... ]
+ *
+ * http://dlang.org/spec/expression.html#associative_array_literals
+ */
+extern (C++) final class AssocArrayLiteralExp : Expression
+{
+ Expressions* keys;
+ Expressions* values;
+
+ OwnedBy ownedByCtfe = OwnedBy.code;
+
+ extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values)
+ {
+ super(loc, TOK.assocArrayLiteral, __traits(classInstanceSize, AssocArrayLiteralExp));
+ assert(keys.dim == values.dim);
+ this.keys = keys;
+ this.values = values;
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ auto e = o.isExpression();
+ if (!e)
+ return false;
+ if (auto ae = e.isAssocArrayLiteralExp())
+ {
+ if (keys.dim != ae.keys.dim)
+ return false;
+ size_t count = 0;
+ foreach (i, key; *keys)
+ {
+ foreach (j, akey; *ae.keys)
+ {
+ if (key.equals(akey))
+ {
+ if (!(*values)[i].equals((*ae.values)[j]))
+ return false;
+ ++count;
+ }
+ }
+ }
+ return count == keys.dim;
+ }
+ return false;
+ }
+
+ override AssocArrayLiteralExp syntaxCopy()
+ {
+ return new AssocArrayLiteralExp(loc, arraySyntaxCopy(keys), arraySyntaxCopy(values));
+ }
+
+ override bool isBool(bool result)
+ {
+ size_t dim = keys.dim;
+ return result ? (dim != 0) : (dim == 0);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+enum stageScrub = 0x1; /// scrubReturnValue is running
+enum stageSearchPointers = 0x2; /// hasNonConstPointers is running
+enum stageOptimize = 0x4; /// optimize is running
+enum stageApply = 0x8; /// apply is running
+enum stageInlineScan = 0x10; /// inlineScan is running
+enum stageToCBuffer = 0x20; /// toCBuffer is running
+
+/***********************************************************
+ * sd( e1, e2, e3, ... )
+ */
+extern (C++) final class StructLiteralExp : Expression
+{
+ StructDeclaration sd; /// which aggregate this is for
+ Expressions* elements; /// parallels sd.fields[] with null entries for fields to skip
+ Type stype; /// final type of result (can be different from sd's type)
+
+ Symbol* sym; /// back end symbol to initialize with literal
+
+ /** pointer to the origin instance of the expression.
+ * once a new expression is created, origin is set to 'this'.
+ * anytime when an expression copy is created, 'origin' pointer is set to
+ * 'origin' pointer value of the original expression.
+ */
+ StructLiteralExp origin;
+
+ /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer.
+ StructLiteralExp inlinecopy;
+
+ /** anytime when recursive function is calling, 'stageflags' marks with bit flag of
+ * current stage and unmarks before return from this function.
+ * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline'
+ * (with infinite recursion) of this expression.
+ */
+ int stageflags;
+
+ bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol
+ bool isOriginal = false; /// used when moving instances to indicate `this is this.origin`
+ OwnedBy ownedByCtfe = OwnedBy.code;
+
+ extern (D) this(const ref Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null)
+ {
+ super(loc, TOK.structLiteral, __traits(classInstanceSize, StructLiteralExp));
+ this.sd = sd;
+ if (!elements)
+ elements = new Expressions();
+ this.elements = elements;
+ this.stype = stype;
+ this.origin = this;
+ //printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars());
+ }
+
+ static StructLiteralExp create(Loc loc, StructDeclaration sd, void* elements, Type stype = null)
+ {
+ return new StructLiteralExp(loc, sd, cast(Expressions*)elements, stype);
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ auto e = o.isExpression();
+ if (!e)
+ return false;
+ if (auto se = e.isStructLiteralExp())
+ {
+ if (!type.equals(se.type))
+ return false;
+ if (elements.dim != se.elements.dim)
+ return false;
+ foreach (i, e1; *elements)
+ {
+ auto e2 = (*se.elements)[i];
+ if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ override StructLiteralExp syntaxCopy()
+ {
+ auto exp = new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), type ? type : stype);
+ exp.origin = this;
+ return exp;
+ }
+
+ /**************************************
+ * Gets expression at offset of type.
+ * Returns NULL if not found.
+ */
+ Expression getField(Type type, uint offset)
+ {
+ //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
+ // /*toChars()*/"", type.toChars(), offset);
+ Expression e = null;
+ int i = getFieldIndex(type, offset);
+
+ if (i != -1)
+ {
+ //printf("\ti = %d\n", i);
+ if (i >= sd.nonHiddenFields())
+ return null;
+
+ assert(i < elements.dim);
+ e = (*elements)[i];
+ if (e)
+ {
+ //printf("e = %s, e.type = %s\n", e.toChars(), e.type.toChars());
+
+ /* If type is a static array, and e is an initializer for that array,
+ * then the field initializer should be an array literal of e.
+ */
+ auto tsa = type.isTypeSArray();
+ if (tsa && e.type.castMod(0) != type.castMod(0))
+ {
+ const length = cast(size_t)tsa.dim.toInteger();
+ auto z = new Expressions(length);
+ foreach (ref q; *z)
+ q = e.copy();
+ e = new ArrayLiteralExp(loc, type, z);
+ }
+ else
+ {
+ e = e.copy();
+ e.type = type;
+ }
+ if (useStaticInit && e.type.needsNested())
+ if (auto se = e.isStructLiteralExp())
+ {
+ se.useStaticInit = true;
+ }
+ }
+ }
+ return e;
+ }
+
+ /************************************
+ * Get index of field.
+ * Returns -1 if not found.
+ */
+ int getFieldIndex(Type type, uint offset)
+ {
+ /* Find which field offset is by looking at the field offsets
+ */
+ if (elements.dim)
+ {
+ foreach (i, v; sd.fields)
+ {
+ if (offset == v.offset && type.size() == v.type.size())
+ {
+ /* context fields might not be filled. */
+ if (i >= sd.nonHiddenFields())
+ return cast(int)i;
+ if (auto e = (*elements)[i])
+ {
+ return cast(int)i;
+ }
+ break;
+ }
+ }
+ }
+ return -1;
+ }
+
+ override Expression addDtorHook(Scope* sc)
+ {
+ /* If struct requires a destructor, rewrite as:
+ * (S tmp = S()),tmp
+ * so that the destructor can be hung on tmp.
+ */
+ if (sd.dtor && sc.func)
+ {
+ /* Make an identifier for the temporary of the form:
+ * __sl%s%d, where %s is the struct name
+ */
+ char[10] buf = void;
+ const prefix = "__sl";
+ const ident = sd.ident.toString;
+ const fullLen = prefix.length + ident.length;
+ const len = fullLen < buf.length ? fullLen : buf.length;
+ buf[0 .. prefix.length] = prefix;
+ buf[prefix.length .. len] = ident[0 .. len - prefix.length];
+
+ auto tmp = copyToTemp(0, buf[0 .. len], this);
+ Expression ae = new DeclarationExp(loc, tmp);
+ Expression e = new CommaExp(loc, ae, new VarExp(loc, tmp));
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ return this;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (sc.flags & SCOPE.Cfile)
+ return this; // C struct literals are lvalues
+ else
+ return Expression.toLvalue(sc, e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * C11 6.5.2.5
+ * ( type-name ) { initializer-list }
+ */
+extern (C++) final class CompoundLiteralExp : Expression
+{
+ Initializer initializer; /// initializer-list
+
+ extern (D) this(const ref Loc loc, Type type_name, Initializer initializer)
+ {
+ super(loc, TOK.compoundLiteral, __traits(classInstanceSize, CompoundLiteralExp));
+ super.type = type_name;
+ this.initializer = initializer;
+ //printf("CompoundLiteralExp::CompoundLiteralExp(%s)\n", toChars());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Mainly just a placeholder
+ */
+extern (C++) final class TypeExp : Expression
+{
+ extern (D) this(const ref Loc loc, Type type)
+ {
+ super(loc, TOK.type, __traits(classInstanceSize, TypeExp));
+ //printf("TypeExp::TypeExp(%s)\n", type.toChars());
+ this.type = type;
+ }
+
+ override TypeExp syntaxCopy()
+ {
+ return new TypeExp(loc, type.syntaxCopy());
+ }
+
+ override bool checkType()
+ {
+ error("type `%s` is not an expression", toChars());
+ return true;
+ }
+
+ override bool checkValue()
+ {
+ error("type `%s` has no value", toChars());
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Mainly just a placeholder of
+ * Package, Module, Nspace, and TemplateInstance (including TemplateMixin)
+ *
+ * A template instance that requires IFTI:
+ * foo!tiargs(fargs) // foo!tiargs
+ * is left until CallExp::semantic() or resolveProperties()
+ */
+extern (C++) final class ScopeExp : Expression
+{
+ ScopeDsymbol sds;
+
+ extern (D) this(const ref Loc loc, ScopeDsymbol sds)
+ {
+ super(loc, TOK.scope_, __traits(classInstanceSize, ScopeExp));
+ //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars());
+ //static int count; if (++count == 38) *(char*)0=0;
+ this.sds = sds;
+ assert(!sds.isTemplateDeclaration()); // instead, you should use TemplateExp
+ }
+
+ override ScopeExp syntaxCopy()
+ {
+ return new ScopeExp(loc, sds.syntaxCopy(null));
+ }
+
+ override bool checkType()
+ {
+ if (sds.isPackage())
+ {
+ error("%s `%s` has no type", sds.kind(), sds.toChars());
+ return true;
+ }
+ if (auto ti = sds.isTemplateInstance())
+ {
+ //assert(ti.needsTypeInference(sc));
+ if (ti.tempdecl &&
+ ti.semantictiargsdone &&
+ ti.semanticRun == PASS.init)
+ {
+ error("partial %s `%s` has no type", sds.kind(), toChars());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ override bool checkValue()
+ {
+ error("%s `%s` has no value", sds.kind(), sds.toChars());
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Mainly just a placeholder
+ */
+extern (C++) final class TemplateExp : Expression
+{
+ TemplateDeclaration td;
+ FuncDeclaration fd;
+
+ extern (D) this(const ref Loc loc, TemplateDeclaration td, FuncDeclaration fd = null)
+ {
+ super(loc, TOK.template_, __traits(classInstanceSize, TemplateExp));
+ //printf("TemplateExp(): %s\n", td.toChars());
+ this.td = td;
+ this.fd = fd;
+ }
+
+ override bool isLvalue()
+ {
+ return fd !is null;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (!fd)
+ return Expression.toLvalue(sc, e);
+
+ assert(sc);
+ return symbolToExp(fd, loc, sc, true);
+ }
+
+ override bool checkType()
+ {
+ error("%s `%s` has no type", td.kind(), toChars());
+ return true;
+ }
+
+ override bool checkValue()
+ {
+ error("%s `%s` has no value", td.kind(), toChars());
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * thisexp.new(newargs) newtype(arguments)
+ */
+extern (C++) final class NewExp : Expression
+{
+ Expression thisexp; // if !=null, 'this' for class being allocated
+ Expressions* newargs; // Array of Expression's to call new operator
+ Type newtype;
+ Expressions* arguments; // Array of Expression's
+
+ Expression argprefix; // expression to be evaluated just before arguments[]
+ CtorDeclaration member; // constructor function
+ bool onstack; // allocate on stack
+ bool thrownew; // this NewExp is the expression of a ThrowStatement
+
+ extern (D) this(const ref Loc loc, Expression thisexp, Expressions* newargs, Type newtype, Expressions* arguments)
+ {
+ super(loc, TOK.new_, __traits(classInstanceSize, NewExp));
+ this.thisexp = thisexp;
+ this.newargs = newargs;
+ this.newtype = newtype;
+ this.arguments = arguments;
+ }
+
+ static NewExp create(Loc loc, Expression thisexp, Expressions* newargs, Type newtype, Expressions* arguments)
+ {
+ return new NewExp(loc, thisexp, newargs, newtype, arguments);
+ }
+
+ override NewExp syntaxCopy()
+ {
+ return new NewExp(loc,
+ thisexp ? thisexp.syntaxCopy() : null,
+ arraySyntaxCopy(newargs),
+ newtype.syntaxCopy(),
+ arraySyntaxCopy(arguments));
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * thisexp.new(newargs) class baseclasses { } (arguments)
+ */
+extern (C++) final class NewAnonClassExp : Expression
+{
+ Expression thisexp; // if !=null, 'this' for class being allocated
+ Expressions* newargs; // Array of Expression's to call new operator
+ ClassDeclaration cd; // class being instantiated
+ Expressions* arguments; // Array of Expression's to call class constructor
+
+ extern (D) this(const ref Loc loc, Expression thisexp, Expressions* newargs, ClassDeclaration cd, Expressions* arguments)
+ {
+ super(loc, TOK.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp));
+ this.thisexp = thisexp;
+ this.newargs = newargs;
+ this.cd = cd;
+ this.arguments = arguments;
+ }
+
+ override NewAnonClassExp syntaxCopy()
+ {
+ return new NewAnonClassExp(loc, thisexp ? thisexp.syntaxCopy() : null, arraySyntaxCopy(newargs), cd.syntaxCopy(null), arraySyntaxCopy(arguments));
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class SymbolExp : Expression
+{
+ Declaration var;
+ Dsymbol originalScope; // original scope before inlining
+ bool hasOverloads;
+
+ extern (D) this(const ref Loc loc, TOK op, int size, Declaration var, bool hasOverloads)
+ {
+ super(loc, op, size);
+ assert(var);
+ this.var = var;
+ this.hasOverloads = hasOverloads;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Offset from symbol
+ */
+extern (C++) final class SymOffExp : SymbolExp
+{
+ dinteger_t offset;
+
+ extern (D) this(const ref Loc loc, Declaration var, dinteger_t offset, bool hasOverloads = true)
+ {
+ if (auto v = var.isVarDeclaration())
+ {
+ // FIXME: This error report will never be handled anyone.
+ // It should be done before the SymOffExp construction.
+ if (v.needThis())
+ .error(loc, "need `this` for address of `%s`", v.toChars());
+ hasOverloads = false;
+ }
+ super(loc, TOK.symbolOffset, __traits(classInstanceSize, SymOffExp), var, hasOverloads);
+ this.offset = offset;
+ }
+
+ override bool isBool(bool result)
+ {
+ return result ? true : false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Variable
+ */
+extern (C++) final class VarExp : SymbolExp
+{
+ bool delegateWasExtracted;
+ extern (D) this(const ref Loc loc, Declaration var, bool hasOverloads = true)
+ {
+ if (var.isVarDeclaration())
+ hasOverloads = false;
+
+ super(loc, TOK.variable, __traits(classInstanceSize, VarExp), var, hasOverloads);
+ //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars());
+ //if (strcmp(var.ident.toChars(), "func") == 0) assert(0);
+ this.type = var.type;
+ }
+
+ static VarExp create(Loc loc, Declaration var, bool hasOverloads = true)
+ {
+ return new VarExp(loc, var, hasOverloads);
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ if (auto ne = o.isExpression().isVarExp())
+ {
+ if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && var == ne.var)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ override bool isLvalue()
+ {
+ if (var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest))
+ return false;
+ return true;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (var.storage_class & STC.manifest)
+ {
+ error("manifest constant `%s` cannot be modified", var.toChars());
+ return ErrorExp.get();
+ }
+ if (var.storage_class & STC.lazy_ && !delegateWasExtracted)
+ {
+ error("lazy variable `%s` cannot be modified", var.toChars());
+ return ErrorExp.get();
+ }
+ if (var.ident == Id.ctfe)
+ {
+ error("cannot modify compiler-generated variable `__ctfe`");
+ return ErrorExp.get();
+ }
+ if (var.ident == Id.dollar) // https://issues.dlang.org/show_bug.cgi?id=13574
+ {
+ error("cannot modify operator `$`");
+ return ErrorExp.get();
+ }
+ return this;
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ //printf("VarExp::modifiableLvalue('%s')\n", var.toChars());
+ if (var.storage_class & STC.manifest)
+ {
+ error("cannot modify manifest constant `%s`", toChars());
+ return ErrorExp.get();
+ }
+ // See if this expression is a modifiable lvalue (i.e. not const)
+ return Expression.modifiableLvalue(sc, e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Overload Set
+ */
+extern (C++) final class OverExp : Expression
+{
+ OverloadSet vars;
+
+ extern (D) this(const ref Loc loc, OverloadSet s)
+ {
+ super(loc, TOK.overloadSet, __traits(classInstanceSize, OverExp));
+ //printf("OverExp(this = %p, '%s')\n", this, var.toChars());
+ vars = s;
+ type = Type.tvoid;
+ }
+
+ override bool isLvalue()
+ {
+ return true;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Function/Delegate literal
+ */
+
+extern (C++) final class FuncExp : Expression
+{
+ FuncLiteralDeclaration fd;
+ TemplateDeclaration td;
+ TOK tok;
+
+ extern (D) this(const ref Loc loc, Dsymbol s)
+ {
+ super(loc, TOK.function_, __traits(classInstanceSize, FuncExp));
+ this.td = s.isTemplateDeclaration();
+ this.fd = s.isFuncLiteralDeclaration();
+ if (td)
+ {
+ assert(td.literal);
+ assert(td.members && td.members.dim == 1);
+ fd = (*td.members)[0].isFuncLiteralDeclaration();
+ }
+ tok = fd.tok; // save original kind of function/delegate/(infer)
+ assert(fd.fbody);
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ auto e = o.isExpression();
+ if (!e)
+ return false;
+ if (auto fe = e.isFuncExp())
+ {
+ return fd == fe.fd;
+ }
+ return false;
+ }
+
+ extern (D) void genIdent(Scope* sc)
+ {
+ if (fd.ident == Id.empty)
+ {
+ const(char)[] s;
+ if (fd.fes)
+ s = "__foreachbody";
+ else if (fd.tok == TOK.reserved)
+ s = "__lambda";
+ else if (fd.tok == TOK.delegate_)
+ s = "__dgliteral";
+ else
+ s = "__funcliteral";
+
+ DsymbolTable symtab;
+ if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+ {
+ if (func.localsymtab is null)
+ {
+ // Inside template constraint, symtab is not set yet.
+ // Initialize it lazily.
+ func.localsymtab = new DsymbolTable();
+ }
+ symtab = func.localsymtab;
+ }
+ else
+ {
+ ScopeDsymbol sds = sc.parent.isScopeDsymbol();
+ if (!sds.symtab)
+ {
+ // Inside template constraint, symtab may not be set yet.
+ // Initialize it lazily.
+ assert(sds.isTemplateInstance());
+ sds.symtab = new DsymbolTable();
+ }
+ symtab = sds.symtab;
+ }
+ assert(symtab);
+ Identifier id = Identifier.generateId(s, symtab.length() + 1);
+ fd.ident = id;
+ if (td)
+ td.ident = id;
+ symtab.insert(td ? cast(Dsymbol)td : cast(Dsymbol)fd);
+ }
+ }
+
+ override FuncExp syntaxCopy()
+ {
+ if (td)
+ return new FuncExp(loc, td.syntaxCopy(null));
+ else if (fd.semanticRun == PASS.init)
+ return new FuncExp(loc, fd.syntaxCopy(null));
+ else // https://issues.dlang.org/show_bug.cgi?id=13481
+ // Prevent multiple semantic analysis of lambda body.
+ return new FuncExp(loc, fd);
+ }
+
+ extern (D) MATCH matchType(Type to, Scope* sc, FuncExp* presult, int flag = 0)
+ {
+
+ static MATCH cannotInfer(Expression e, Type to, int flag)
+ {
+ if (!flag)
+ e.error("cannot infer parameter types from `%s`", to.toChars());
+ return MATCH.nomatch;
+ }
+
+ //printf("FuncExp::matchType('%s'), to=%s\n", type ? type.toChars() : "null", to.toChars());
+ if (presult)
+ *presult = null;
+
+ TypeFunction tof = null;
+ if (to.ty == Tdelegate)
+ {
+ if (tok == TOK.function_)
+ {
+ if (!flag)
+ error("cannot match function literal to delegate type `%s`", to.toChars());
+ return MATCH.nomatch;
+ }
+ tof = cast(TypeFunction)to.nextOf();
+ }
+ else if (to.ty == Tpointer && (tof = to.nextOf().isTypeFunction()) !is null)
+ {
+ if (tok == TOK.delegate_)
+ {
+ if (!flag)
+ error("cannot match delegate literal to function pointer type `%s`", to.toChars());
+ return MATCH.nomatch;
+ }
+ }
+
+ if (td)
+ {
+ if (!tof)
+ {
+ return cannotInfer(this, to, flag);
+ }
+
+ // Parameter types inference from 'tof'
+ assert(td._scope);
+ TypeFunction tf = fd.type.isTypeFunction();
+ //printf("\ttof = %s\n", tof.toChars());
+ //printf("\ttf = %s\n", tf.toChars());
+ const dim = tf.parameterList.length;
+
+ if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs)
+ return cannotInfer(this, to, flag);
+
+ auto tiargs = new Objects();
+ tiargs.reserve(td.parameters.dim);
+
+ foreach (tp; *td.parameters)
+ {
+ size_t u = 0;
+ foreach (i, p; tf.parameterList)
+ {
+ if (auto ti = p.type.isTypeIdentifier())
+ if (ti && ti.ident == tp.ident)
+ break;
+
+ ++u;
+ }
+ assert(u < dim);
+ Parameter pto = tof.parameterList[u];
+ Type t = pto.type;
+ if (t.ty == Terror)
+ return cannotInfer(this, to, flag);
+ tiargs.push(t);
+ }
+
+ // Set target of return type inference
+ if (!tf.next && tof.next)
+ fd.treq = to;
+
+ auto ti = new TemplateInstance(loc, td, tiargs);
+ Expression ex = (new ScopeExp(loc, ti)).expressionSemantic(td._scope);
+
+ // Reset inference target for the later re-semantic
+ fd.treq = null;
+
+ if (ex.op == TOK.error)
+ return MATCH.nomatch;
+ if (auto ef = ex.isFuncExp())
+ return ef.matchType(to, sc, presult, flag);
+ else
+ return cannotInfer(this, to, flag);
+ }
+
+ if (!tof || !tof.next)
+ return MATCH.nomatch;
+
+ assert(type && type != Type.tvoid);
+ if (fd.type.ty == Terror)
+ return MATCH.nomatch;
+ auto tfx = fd.type.isTypeFunction();
+ bool convertMatch = (type.ty != to.ty);
+
+ if (fd.inferRetType && tfx.next.implicitConvTo(tof.next) == MATCH.convert)
+ {
+ /* If return type is inferred and covariant return,
+ * tweak return statements to required return type.
+ *
+ * interface I {}
+ * class C : Object, I{}
+ *
+ * I delegate() dg = delegate() { return new class C(); }
+ */
+ convertMatch = true;
+
+ auto tfy = new TypeFunction(tfx.parameterList, tof.next,
+ tfx.linkage, STC.undefined_);
+ tfy.mod = tfx.mod;
+ tfy.isnothrow = tfx.isnothrow;
+ tfy.isnogc = tfx.isnogc;
+ tfy.purity = tfx.purity;
+ tfy.isproperty = tfx.isproperty;
+ tfy.isref = tfx.isref;
+ tfy.isInOutParam = tfx.isInOutParam;
+ tfy.isInOutQual = tfx.isInOutQual;
+ tfy.deco = tfy.merge().deco;
+
+ tfx = tfy;
+ }
+ Type tx;
+ if (tok == TOK.delegate_ ||
+ tok == TOK.reserved && (type.ty == Tdelegate || type.ty == Tpointer && to.ty == Tdelegate))
+ {
+ // Allow conversion from implicit function pointer to delegate
+ tx = new TypeDelegate(tfx);
+ tx.deco = tx.merge().deco;
+ }
+ else
+ {
+ assert(tok == TOK.function_ || tok == TOK.reserved && type.ty == Tpointer);
+ tx = tfx.pointerTo();
+ }
+ //printf("\ttx = %s, to = %s\n", tx.toChars(), to.toChars());
+
+ MATCH m = tx.implicitConvTo(to);
+ if (m > MATCH.nomatch)
+ {
+ // MATCH.exact: exact type match
+ // MATCH.constant: covairiant type match (eg. attributes difference)
+ // MATCH.convert: context conversion
+ m = convertMatch ? MATCH.convert : tx.equals(to) ? MATCH.exact : MATCH.constant;
+
+ if (presult)
+ {
+ (*presult) = cast(FuncExp)copy();
+ (*presult).type = to;
+
+ // https://issues.dlang.org/show_bug.cgi?id=12508
+ // Tweak function body for covariant returns.
+ (*presult).fd.modifyReturns(sc, tof.next);
+ }
+ }
+ else if (!flag)
+ {
+ auto ts = toAutoQualChars(tx, to);
+ error("cannot implicitly convert expression `%s` of type `%s` to `%s`",
+ toChars(), ts[0], ts[1]);
+ }
+ return m;
+ }
+
+ override const(char)* toChars() const
+ {
+ return fd.toChars();
+ }
+
+ override bool checkType()
+ {
+ if (td)
+ {
+ error("template lambda has no type");
+ return true;
+ }
+ return false;
+ }
+
+ override bool checkValue()
+ {
+ if (td)
+ {
+ error("template lambda has no value");
+ return true;
+ }
+ return false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Declaration of a symbol
+ *
+ * D grammar allows declarations only as statements. However in AST representation
+ * it can be part of any expression. This is used, for example, during internal
+ * syntax re-writes to inject hidden symbols.
+ */
+extern (C++) final class DeclarationExp : Expression
+{
+ Dsymbol declaration;
+
+ extern (D) this(const ref Loc loc, Dsymbol declaration)
+ {
+ super(loc, TOK.declaration, __traits(classInstanceSize, DeclarationExp));
+ this.declaration = declaration;
+ }
+
+ override DeclarationExp syntaxCopy()
+ {
+ return new DeclarationExp(loc, declaration.syntaxCopy(null));
+ }
+
+ override bool hasCode()
+ {
+ if (auto vd = declaration.isVarDeclaration())
+ {
+ return !(vd.storage_class & (STC.manifest | STC.static_));
+ }
+ return false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * typeid(int)
+ */
+extern (C++) final class TypeidExp : Expression
+{
+ RootObject obj;
+
+ extern (D) this(const ref Loc loc, RootObject o)
+ {
+ super(loc, TOK.typeid_, __traits(classInstanceSize, TypeidExp));
+ this.obj = o;
+ }
+
+ override TypeidExp syntaxCopy()
+ {
+ return new TypeidExp(loc, objectSyntaxCopy(obj));
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * __traits(identifier, args...)
+ */
+extern (C++) final class TraitsExp : Expression
+{
+ Identifier ident;
+ Objects* args;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Objects* args)
+ {
+ super(loc, TOK.traits, __traits(classInstanceSize, TraitsExp));
+ this.ident = ident;
+ this.args = args;
+ }
+
+ override TraitsExp syntaxCopy()
+ {
+ return new TraitsExp(loc, ident, TemplateInstance.arraySyntaxCopy(args));
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class HaltExp : Expression
+{
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, TOK.halt, __traits(classInstanceSize, HaltExp));
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * is(targ id tok tspec)
+ * is(targ id == tok2)
+ */
+extern (C++) final class IsExp : Expression
+{
+ Type targ;
+ Identifier id; // can be null
+ Type tspec; // can be null
+ TemplateParameters* parameters;
+ TOK tok; // ':' or '=='
+ TOK tok2; // 'struct', 'union', etc.
+
+ extern (D) this(const ref Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters)
+ {
+ super(loc, TOK.is_, __traits(classInstanceSize, IsExp));
+ this.targ = targ;
+ this.id = id;
+ this.tok = tok;
+ this.tspec = tspec;
+ this.tok2 = tok2;
+ this.parameters = parameters;
+ }
+
+ override IsExp syntaxCopy()
+ {
+ // This section is identical to that in TemplateDeclaration::syntaxCopy()
+ TemplateParameters* p = null;
+ if (parameters)
+ {
+ p = new TemplateParameters(parameters.dim);
+ foreach (i, el; *parameters)
+ (*p)[i] = el.syntaxCopy();
+ }
+ return new IsExp(loc, targ.syntaxCopy(), id, tok, tspec ? tspec.syntaxCopy() : null, tok2, p);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) abstract class UnaExp : Expression
+{
+ Expression e1;
+ Type att1; // Save alias this type to detect recursion
+
+ extern (D) this(const ref Loc loc, TOK op, int size, Expression e1)
+ {
+ super(loc, op, size);
+ this.e1 = e1;
+ }
+
+ override UnaExp syntaxCopy()
+ {
+ UnaExp e = cast(UnaExp)copy();
+ e.type = null;
+ e.e1 = e.e1.syntaxCopy();
+ return e;
+ }
+
+ /********************************
+ * The type for a unary expression is incompatible.
+ * Print error message.
+ * Returns:
+ * ErrorExp
+ */
+ final Expression incompatibleTypes()
+ {
+ if (e1.type.toBasetype() == Type.terror)
+ return e1;
+
+ if (e1.op == TOK.type)
+ {
+ error("incompatible type for `%s(%s)`: cannot use `%s` with types", Token.toChars(op), e1.toChars(), Token.toChars(op));
+ }
+ else
+ {
+ error("incompatible type for `%s(%s)`: `%s`", Token.toChars(op), e1.toChars(), e1.type.toChars());
+ }
+ return ErrorExp.get();
+ }
+
+ /*********************
+ * Mark the operand as will never be dereferenced,
+ * which is useful info for @safe checks.
+ * Do before semantic() on operands rewrites them.
+ */
+ final void setNoderefOperand()
+ {
+ if (auto edi = e1.isDotIdExp())
+ edi.noderef = true;
+
+ }
+
+ override final Expression resolveLoc(const ref Loc loc, Scope* sc)
+ {
+ e1 = e1.resolveLoc(loc, sc);
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+alias fp_t = UnionExp function(const ref Loc loc, Type, Expression, Expression);
+alias fp2_t = bool function(const ref Loc loc, TOK, Expression, Expression);
+
+/***********************************************************
+ */
+extern (C++) abstract class BinExp : Expression
+{
+ Expression e1;
+ Expression e2;
+ Type att1; // Save alias this type to detect recursion
+ Type att2; // Save alias this type to detect recursion
+
+ extern (D) this(const ref Loc loc, TOK op, int size, Expression e1, Expression e2)
+ {
+ super(loc, op, size);
+ this.e1 = e1;
+ this.e2 = e2;
+ }
+
+ override BinExp syntaxCopy()
+ {
+ BinExp e = cast(BinExp)copy();
+ e.type = null;
+ e.e1 = e.e1.syntaxCopy();
+ e.e2 = e.e2.syntaxCopy();
+ return e;
+ }
+
+ /********************************
+ * The types for a binary expression are incompatible.
+ * Print error message.
+ * Returns:
+ * ErrorExp
+ */
+ final Expression incompatibleTypes()
+ {
+ if (e1.type.toBasetype() == Type.terror)
+ return e1;
+ if (e2.type.toBasetype() == Type.terror)
+ return e2;
+
+ // CondExp uses 'a ? b : c' but we're comparing 'b : c'
+ TOK thisOp = (op == TOK.question) ? TOK.colon : op;
+ if (e1.op == TOK.type || e2.op == TOK.type)
+ {
+ error("incompatible types for `(%s) %s (%s)`: cannot use `%s` with types",
+ e1.toChars(), Token.toChars(thisOp), e2.toChars(), Token.toChars(op));
+ }
+ else if (e1.type.equals(e2.type))
+ {
+ error("incompatible types for `(%s) %s (%s)`: both operands are of type `%s`",
+ e1.toChars(), Token.toChars(thisOp), e2.toChars(), e1.type.toChars());
+ }
+ else
+ {
+ auto ts = toAutoQualChars(e1.type, e2.type);
+ error("incompatible types for `(%s) %s (%s)`: `%s` and `%s`",
+ e1.toChars(), Token.toChars(thisOp), e2.toChars(), ts[0], ts[1]);
+ }
+ return ErrorExp.get();
+ }
+
+ extern (D) final Expression checkOpAssignTypes(Scope* sc)
+ {
+ // At that point t1 and t2 are the merged types. type is the original type of the lhs.
+ Type t1 = e1.type;
+ Type t2 = e2.type;
+
+ // T opAssign floating yields a floating. Prevent truncating conversions (float to int).
+ // See issue 3841.
+ // Should we also prevent double to float (type.isfloating() && type.size() < t2.size()) ?
+ if (op == TOK.addAssign || op == TOK.minAssign ||
+ op == TOK.mulAssign || op == TOK.divAssign || op == TOK.modAssign ||
+ op == TOK.powAssign)
+ {
+ if ((type.isintegral() && t2.isfloating()))
+ {
+ warning("`%s %s %s` is performing truncating conversion", type.toChars(), Token.toChars(op), t2.toChars());
+ }
+ }
+
+ // generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary
+ if (op == TOK.mulAssign || op == TOK.divAssign || op == TOK.modAssign)
+ {
+ // Any multiplication by an imaginary or complex number yields a complex result.
+ // r *= c, i*=c, r*=i, i*=i are all forbidden operations.
+ const(char)* opstr = Token.toChars(op);
+ if (t1.isreal() && t2.iscomplex())
+ {
+ error("`%s %s %s` is undefined. Did you mean `%s %s %s.re`?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars());
+ return ErrorExp.get();
+ }
+ else if (t1.isimaginary() && t2.iscomplex())
+ {
+ error("`%s %s %s` is undefined. Did you mean `%s %s %s.im`?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars());
+ return ErrorExp.get();
+ }
+ else if ((t1.isreal() || t1.isimaginary()) && t2.isimaginary())
+ {
+ error("`%s %s %s` is an undefined operation", t1.toChars(), opstr, t2.toChars());
+ return ErrorExp.get();
+ }
+ }
+
+ // generate an error if this is a nonsensical += or -=, eg real += imaginary
+ if (op == TOK.addAssign || op == TOK.minAssign)
+ {
+ // Addition or subtraction of a real and an imaginary is a complex result.
+ // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations.
+ if ((t1.isreal() && (t2.isimaginary() || t2.iscomplex())) || (t1.isimaginary() && (t2.isreal() || t2.iscomplex())))
+ {
+ error("`%s %s %s` is undefined (result is complex)", t1.toChars(), Token.toChars(op), t2.toChars());
+ return ErrorExp.get();
+ }
+ if (type.isreal() || type.isimaginary())
+ {
+ assert(global.errors || t2.isfloating());
+ e2 = e2.castTo(sc, t1);
+ }
+ }
+ if (op == TOK.mulAssign)
+ {
+ if (t2.isfloating())
+ {
+ if (t1.isreal())
+ {
+ if (t2.isimaginary() || t2.iscomplex())
+ {
+ e2 = e2.castTo(sc, t1);
+ }
+ }
+ else if (t1.isimaginary())
+ {
+ if (t2.isimaginary() || t2.iscomplex())
+ {
+ switch (t1.ty)
+ {
+ case Timaginary32:
+ t2 = Type.tfloat32;
+ break;
+
+ case Timaginary64:
+ t2 = Type.tfloat64;
+ break;
+
+ case Timaginary80:
+ t2 = Type.tfloat80;
+ break;
+
+ default:
+ assert(0);
+ }
+ e2 = e2.castTo(sc, t2);
+ }
+ }
+ }
+ }
+ else if (op == TOK.divAssign)
+ {
+ if (t2.isimaginary())
+ {
+ if (t1.isreal())
+ {
+ // x/iv = i(-x/v)
+ // Therefore, the result is 0
+ e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1));
+ e2.type = t1;
+ Expression e = new AssignExp(loc, e1, e2);
+ e.type = t1;
+ return e;
+ }
+ else if (t1.isimaginary())
+ {
+ Type t3;
+ switch (t1.ty)
+ {
+ case Timaginary32:
+ t3 = Type.tfloat32;
+ break;
+
+ case Timaginary64:
+ t3 = Type.tfloat64;
+ break;
+
+ case Timaginary80:
+ t3 = Type.tfloat80;
+ break;
+
+ default:
+ assert(0);
+ }
+ e2 = e2.castTo(sc, t3);
+ Expression e = new AssignExp(loc, e1, e2);
+ e.type = t1;
+ return e;
+ }
+ }
+ }
+ else if (op == TOK.modAssign)
+ {
+ if (t2.iscomplex())
+ {
+ error("cannot perform modulo complex arithmetic");
+ return ErrorExp.get();
+ }
+ }
+ return this;
+ }
+
+ extern (D) final bool checkIntegralBin()
+ {
+ bool r1 = e1.checkIntegral();
+ bool r2 = e2.checkIntegral();
+ return (r1 || r2);
+ }
+
+ extern (D) final bool checkArithmeticBin()
+ {
+ bool r1 = e1.checkArithmetic();
+ bool r2 = e2.checkArithmetic();
+ return (r1 || r2);
+ }
+
+ extern (D) final bool checkSharedAccessBin(Scope* sc)
+ {
+ const r1 = e1.checkSharedAccess(sc);
+ const r2 = e2.checkSharedAccess(sc);
+ return (r1 || r2);
+ }
+
+ /*********************
+ * Mark the operands as will never be dereferenced,
+ * which is useful info for @safe checks.
+ * Do before semantic() on operands rewrites them.
+ */
+ final void setNoderefOperands()
+ {
+ if (auto edi = e1.isDotIdExp())
+ edi.noderef = true;
+ if (auto edi = e2.isDotIdExp())
+ edi.noderef = true;
+
+ }
+
+ final Expression reorderSettingAAElem(Scope* sc)
+ {
+ BinExp be = this;
+
+ auto ie = be.e1.isIndexExp();
+ if (!ie)
+ return be;
+ if (ie.e1.type.toBasetype().ty != Taarray)
+ return be;
+
+ /* Fix evaluation order of setting AA element
+ * https://issues.dlang.org/show_bug.cgi?id=3825
+ * Rewrite:
+ * aa[k1][k2][k3] op= val;
+ * as:
+ * auto ref __aatmp = aa;
+ * auto ref __aakey3 = k1, __aakey2 = k2, __aakey1 = k3;
+ * auto ref __aaval = val;
+ * __aatmp[__aakey3][__aakey2][__aakey1] op= __aaval; // assignment
+ */
+
+ Expression e0;
+ while (1)
+ {
+ Expression de;
+ ie.e2 = extractSideEffect(sc, "__aakey", de, ie.e2);
+ e0 = Expression.combine(de, e0);
+
+ auto ie1 = ie.e1.isIndexExp();
+ if (!ie1 ||
+ ie1.e1.type.toBasetype().ty != Taarray)
+ {
+ break;
+ }
+ ie = ie1;
+ }
+ assert(ie.e1.type.toBasetype().ty == Taarray);
+
+ Expression de;
+ ie.e1 = extractSideEffect(sc, "__aatmp", de, ie.e1);
+ e0 = Expression.combine(de, e0);
+
+ be.e2 = extractSideEffect(sc, "__aaval", e0, be.e2, true);
+
+ //printf("-e0 = %s, be = %s\n", e0.toChars(), be.toChars());
+ return Expression.combine(e0, be);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class BinAssignExp : BinExp
+{
+ extern (D) this(const ref Loc loc, TOK op, int size, Expression e1, Expression e2)
+ {
+ super(loc, op, size, e1, e2);
+ }
+
+ override final bool isLvalue()
+ {
+ return true;
+ }
+
+ override final Expression toLvalue(Scope* sc, Expression ex)
+ {
+ // Lvalue-ness will be handled in glue layer.
+ return this;
+ }
+
+ override final Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ // should check e1.checkModifiable() ?
+ return toLvalue(sc, this);
+ }
+
+ override inout(BinAssignExp) isBinAssignExp() pure inout nothrow @nogc @safe
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/expression.html#mixin_expressions
+ */
+extern (C++) final class MixinExp : Expression
+{
+ Expressions* exps;
+
+ extern (D) this(const ref Loc loc, Expressions* exps)
+ {
+ super(loc, TOK.mixin_, __traits(classInstanceSize, MixinExp));
+ this.exps = exps;
+ }
+
+ override MixinExp syntaxCopy()
+ {
+ return new MixinExp(loc, arraySyntaxCopy(exps));
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+ auto e = o.isExpression();
+ if (!e)
+ return false;
+ if (auto ce = e.isMixinExp())
+ {
+ if (exps.dim != ce.exps.dim)
+ return false;
+ foreach (i, e1; *exps)
+ {
+ auto e2 = (*ce.exps)[i];
+ if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ImportExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e)
+ {
+ super(loc, TOK.import_, __traits(classInstanceSize, ImportExp), e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/expression.html#assert_expressions
+ */
+extern (C++) final class AssertExp : UnaExp
+{
+ Expression msg;
+
+ extern (D) this(const ref Loc loc, Expression e, Expression msg = null)
+ {
+ super(loc, TOK.assert_, __traits(classInstanceSize, AssertExp), e);
+ this.msg = msg;
+ }
+
+ override AssertExp syntaxCopy()
+ {
+ return new AssertExp(loc, e1.syntaxCopy(), msg ? msg.syntaxCopy() : null);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DotIdExp : UnaExp
+{
+ Identifier ident;
+ bool noderef; // true if the result of the expression will never be dereferenced
+ bool wantsym; // do not replace Symbol with its initializer during semantic()
+
+ extern (D) this(const ref Loc loc, Expression e, Identifier ident)
+ {
+ super(loc, TOK.dotIdentifier, __traits(classInstanceSize, DotIdExp), e);
+ this.ident = ident;
+ }
+
+ static DotIdExp create(Loc loc, Expression e, Identifier ident)
+ {
+ return new DotIdExp(loc, e, ident);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Mainly just a placeholder
+ */
+extern (C++) final class DotTemplateExp : UnaExp
+{
+ TemplateDeclaration td;
+
+ extern (D) this(const ref Loc loc, Expression e, TemplateDeclaration td)
+ {
+ super(loc, TOK.dotTemplateDeclaration, __traits(classInstanceSize, DotTemplateExp), e);
+ this.td = td;
+ }
+
+ override bool checkType()
+ {
+ error("%s `%s` has no type", td.kind(), toChars());
+ return true;
+ }
+
+ override bool checkValue()
+ {
+ error("%s `%s` has no value", td.kind(), toChars());
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DotVarExp : UnaExp
+{
+ Declaration var;
+ bool hasOverloads;
+
+ extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true)
+ {
+ if (var.isVarDeclaration())
+ hasOverloads = false;
+
+ super(loc, TOK.dotVariable, __traits(classInstanceSize, DotVarExp), e);
+ //printf("DotVarExp()\n");
+ this.var = var;
+ this.hasOverloads = hasOverloads;
+ }
+
+ override bool isLvalue()
+ {
+ if (e1.op != TOK.structLiteral)
+ return true;
+ auto vd = var.isVarDeclaration();
+ return !(vd && vd.isField());
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ //printf("DotVarExp::toLvalue(%s)\n", toChars());
+ if (sc && sc.flags & SCOPE.Cfile)
+ {
+ /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator
+ * is an lvalue if the first expression is an lvalue.
+ */
+ if (!e1.isLvalue())
+ return Expression.toLvalue(sc, e);
+ }
+ if (!isLvalue())
+ return Expression.toLvalue(sc, e);
+ if (e1.op == TOK.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
+ {
+ if (VarDeclaration vd = var.isVarDeclaration())
+ {
+ auto ad = vd.isMember2();
+ if (ad && ad.fields.dim == sc.ctorflow.fieldinit.length)
+ {
+ foreach (i, f; ad.fields)
+ {
+ if (f == vd)
+ {
+ if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
+ {
+ /* If the address of vd is taken, assume it is thereby initialized
+ * https://issues.dlang.org/show_bug.cgi?id=15869
+ */
+ modifyFieldVar(loc, sc, vd, e1);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return this;
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ version (none)
+ {
+ printf("DotVarExp::modifiableLvalue(%s)\n", toChars());
+ printf("e1.type = %s\n", e1.type.toChars());
+ printf("var.type = %s\n", var.type.toChars());
+ }
+
+ return Expression.modifiableLvalue(sc, e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * foo.bar!(args)
+ */
+extern (C++) final class DotTemplateInstanceExp : UnaExp
+{
+ TemplateInstance ti;
+
+ extern (D) this(const ref Loc loc, Expression e, Identifier name, Objects* tiargs)
+ {
+ super(loc, TOK.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
+ //printf("DotTemplateInstanceExp()\n");
+ this.ti = new TemplateInstance(loc, name, tiargs);
+ }
+
+ extern (D) this(const ref Loc loc, Expression e, TemplateInstance ti)
+ {
+ super(loc, TOK.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
+ this.ti = ti;
+ }
+
+ override DotTemplateInstanceExp syntaxCopy()
+ {
+ return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
+ }
+
+ bool findTempDecl(Scope* sc)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotTemplateInstanceExp::findTempDecl('%s')\n", toChars());
+ }
+ if (ti.tempdecl)
+ return true;
+
+ Expression e = new DotIdExp(loc, e1, ti.name);
+ e = e.expressionSemantic(sc);
+ if (e.op == TOK.dot)
+ e = (cast(DotExp)e).e2;
+
+ Dsymbol s = null;
+ switch (e.op)
+ {
+ case TOK.overloadSet:
+ s = (cast(OverExp)e).vars;
+ break;
+
+ case TOK.dotTemplateDeclaration:
+ s = (cast(DotTemplateExp)e).td;
+ break;
+
+ case TOK.scope_:
+ s = (cast(ScopeExp)e).sds;
+ break;
+
+ case TOK.dotVariable:
+ s = (cast(DotVarExp)e).var;
+ break;
+
+ case TOK.variable:
+ s = (cast(VarExp)e).var;
+ break;
+
+ default:
+ return false;
+ }
+ return ti.updateTempDecl(sc, s);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DelegateExp : UnaExp
+{
+ FuncDeclaration func;
+ bool hasOverloads;
+ VarDeclaration vthis2; // container for multi-context
+
+ extern (D) this(const ref Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null)
+ {
+ super(loc, TOK.delegate_, __traits(classInstanceSize, DelegateExp), e);
+ this.func = f;
+ this.hasOverloads = hasOverloads;
+ this.vthis2 = vthis2;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DotTypeExp : UnaExp
+{
+ Dsymbol sym; // symbol that represents a type
+
+ extern (D) this(const ref Loc loc, Expression e, Dsymbol s)
+ {
+ super(loc, TOK.dotType, __traits(classInstanceSize, DotTypeExp), e);
+ this.sym = s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class CallExp : UnaExp
+{
+ Expressions* arguments; // function arguments
+ FuncDeclaration f; // symbol to call
+ bool directcall; // true if a virtual call is devirtualized
+ bool inDebugStatement; /// true if this was in a debug statement
+ bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code)
+ VarDeclaration vthis2; // container for multi-context
+
+ extern (D) this(const ref Loc loc, Expression e, Expressions* exps)
+ {
+ super(loc, TOK.call, __traits(classInstanceSize, CallExp), e);
+ this.arguments = exps;
+ }
+
+ extern (D) this(const ref Loc loc, Expression e)
+ {
+ super(loc, TOK.call, __traits(classInstanceSize, CallExp), e);
+ }
+
+ extern (D) this(const ref Loc loc, Expression e, Expression earg1)
+ {
+ super(loc, TOK.call, __traits(classInstanceSize, CallExp), e);
+ this.arguments = new Expressions();
+ if (earg1)
+ this.arguments.push(earg1);
+ }
+
+ extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2)
+ {
+ super(loc, TOK.call, __traits(classInstanceSize, CallExp), e);
+ auto arguments = new Expressions(2);
+ (*arguments)[0] = earg1;
+ (*arguments)[1] = earg2;
+ this.arguments = arguments;
+ }
+
+ /***********************************************************
+ * Instatiates a new function call expression
+ * Params:
+ * loc = location
+ * fd = the declaration of the function to call
+ * earg1 = the function argument
+ */
+ extern(D) this(const ref Loc loc, FuncDeclaration fd, Expression earg1)
+ {
+ this(loc, new VarExp(loc, fd, false), earg1);
+ this.f = fd;
+ }
+
+ static CallExp create(Loc loc, Expression e, Expressions* exps)
+ {
+ return new CallExp(loc, e, exps);
+ }
+
+ static CallExp create(Loc loc, Expression e)
+ {
+ return new CallExp(loc, e);
+ }
+
+ static CallExp create(Loc loc, Expression e, Expression earg1)
+ {
+ return new CallExp(loc, e, earg1);
+ }
+
+ /***********************************************************
+ * Creates a new function call expression
+ * Params:
+ * loc = location
+ * fd = the declaration of the function to call
+ * earg1 = the function argument
+ */
+ static CallExp create(Loc loc, FuncDeclaration fd, Expression earg1)
+ {
+ return new CallExp(loc, fd, earg1);
+ }
+
+ override CallExp syntaxCopy()
+ {
+ return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments));
+ }
+
+ override bool isLvalue()
+ {
+ Type tb = e1.type.toBasetype();
+ if (tb.ty == Tdelegate || tb.ty == Tpointer)
+ tb = tb.nextOf();
+ auto tf = tb.isTypeFunction();
+ if (tf && tf.isref)
+ {
+ if (auto dve = e1.isDotVarExp())
+ if (dve.var.isCtorDeclaration())
+ return false;
+ return true; // function returns a reference
+ }
+ return false;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (isLvalue())
+ return this;
+ return Expression.toLvalue(sc, e);
+ }
+
+ override Expression addDtorHook(Scope* sc)
+ {
+ /* Only need to add dtor hook if it's a type that needs destruction.
+ * Use same logic as VarDeclaration::callScopeDtor()
+ */
+
+ if (auto tf = e1.type.isTypeFunction())
+ {
+ if (tf.isref)
+ return this;
+ }
+
+ Type tv = type.baseElemOf();
+ if (auto ts = tv.isTypeStruct())
+ {
+ StructDeclaration sd = ts.sym;
+ if (sd.dtor)
+ {
+ /* Type needs destruction, so declare a tmp
+ * which the back end will recognize and call dtor on
+ */
+ auto tmp = copyToTemp(0, "__tmpfordtor", this);
+ auto de = new DeclarationExp(loc, tmp);
+ auto ve = new VarExp(loc, tmp);
+ Expression e = new CommaExp(loc, de, ve);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ }
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null)
+{
+ if (auto ae = e.isAddrExp())
+ {
+ auto ae1 = ae.e1;
+ if (auto ve = ae1.isVarExp())
+ {
+ if (hasOverloads)
+ *hasOverloads = ve.hasOverloads;
+ return ve.var.isFuncDeclaration();
+ }
+ if (auto dve = ae1.isDotVarExp())
+ {
+ if (hasOverloads)
+ *hasOverloads = dve.hasOverloads;
+ return dve.var.isFuncDeclaration();
+ }
+ }
+ else
+ {
+ if (auto soe = e.isSymOffExp())
+ {
+ if (hasOverloads)
+ *hasOverloads = soe.hasOverloads;
+ return soe.var.isFuncDeclaration();
+ }
+ if (auto dge = e.isDelegateExp())
+ {
+ if (hasOverloads)
+ *hasOverloads = dge.hasOverloads;
+ return dge.func.isFuncDeclaration();
+ }
+ }
+ return null;
+}
+
+/***********************************************************
+ */
+extern (C++) final class AddrExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e)
+ {
+ super(loc, TOK.address, __traits(classInstanceSize, AddrExp), e);
+ }
+
+ extern (D) this(const ref Loc loc, Expression e, Type t)
+ {
+ this(loc, e);
+ type = t;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class PtrExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e)
+ {
+ super(loc, TOK.star, __traits(classInstanceSize, PtrExp), e);
+ //if (e.type)
+ // type = ((TypePointer *)e.type).next;
+ }
+
+ extern (D) this(const ref Loc loc, Expression e, Type t)
+ {
+ super(loc, TOK.star, __traits(classInstanceSize, PtrExp), e);
+ type = t;
+ }
+
+ override bool isLvalue()
+ {
+ return true;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ return this;
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ //printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type.toChars());
+ return Expression.modifiableLvalue(sc, e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class NegExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e)
+ {
+ super(loc, TOK.negate, __traits(classInstanceSize, NegExp), e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class UAddExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e)
+ {
+ super(loc, TOK.uadd, __traits(classInstanceSize, UAddExp), e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ComExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e)
+ {
+ super(loc, TOK.tilde, __traits(classInstanceSize, ComExp), e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class NotExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e)
+ {
+ super(loc, TOK.not, __traits(classInstanceSize, NotExp), e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DeleteExp : UnaExp
+{
+ bool isRAII; // true if called automatically as a result of scoped destruction
+
+ extern (D) this(const ref Loc loc, Expression e, bool isRAII)
+ {
+ super(loc, TOK.delete_, __traits(classInstanceSize, DeleteExp), e);
+ this.isRAII = isRAII;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Possible to cast to one type while painting to another type
+ */
+extern (C++) final class CastExp : UnaExp
+{
+ Type to; // type to cast to
+ ubyte mod = cast(ubyte)~0; // MODxxxxx
+
+ extern (D) this(const ref Loc loc, Expression e, Type t)
+ {
+ super(loc, TOK.cast_, __traits(classInstanceSize, CastExp), e);
+ this.to = t;
+ }
+
+ /* For cast(const) and cast(immutable)
+ */
+ extern (D) this(const ref Loc loc, Expression e, ubyte mod)
+ {
+ super(loc, TOK.cast_, __traits(classInstanceSize, CastExp), e);
+ this.mod = mod;
+ }
+
+ override CastExp syntaxCopy()
+ {
+ return to ? new CastExp(loc, e1.syntaxCopy(), to.syntaxCopy()) : new CastExp(loc, e1.syntaxCopy(), mod);
+ }
+
+ override bool isLvalue()
+ {
+ //printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars());
+ if (!e1.isLvalue())
+ return false;
+ return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) ||
+ e1.type.mutableOf().unSharedOf().equals(to.mutableOf().unSharedOf());
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (sc && sc.flags & SCOPE.Cfile)
+ {
+ /* C11 6.5.4-5: A cast does not yield an lvalue.
+ */
+ return Expression.toLvalue(sc, e);
+ }
+ if (isLvalue())
+ return this;
+ return Expression.toLvalue(sc, e);
+ }
+
+ override Expression addDtorHook(Scope* sc)
+ {
+ if (to.toBasetype().ty == Tvoid) // look past the cast(void)
+ e1 = e1.addDtorHook(sc);
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class VectorExp : UnaExp
+{
+ TypeVector to; // the target vector type before semantic()
+ uint dim = ~0; // number of elements in the vector
+ OwnedBy ownedByCtfe = OwnedBy.code;
+
+ extern (D) this(const ref Loc loc, Expression e, Type t)
+ {
+ super(loc, TOK.vector, __traits(classInstanceSize, VectorExp), e);
+ assert(t.ty == Tvector);
+ to = cast(TypeVector)t;
+ }
+
+ static VectorExp create(Loc loc, Expression e, Type t)
+ {
+ return new VectorExp(loc, e, t);
+ }
+
+ // Same as create, but doesn't allocate memory.
+ static void emplace(UnionExp* pue, Loc loc, Expression e, Type type)
+ {
+ emplaceExp!(VectorExp)(pue, loc, e, type);
+ }
+
+ override VectorExp syntaxCopy()
+ {
+ return new VectorExp(loc, e1.syntaxCopy(), to.syntaxCopy());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * e1.array property for vectors.
+ *
+ * https://dlang.org/spec/simd.html#properties
+ */
+extern (C++) final class VectorArrayExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e1)
+ {
+ super(loc, TOK.vectorArray, __traits(classInstanceSize, VectorArrayExp), e1);
+ }
+
+ override bool isLvalue()
+ {
+ return e1.isLvalue();
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ e1 = e1.toLvalue(sc, e);
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * e1 [lwr .. upr]
+ *
+ * http://dlang.org/spec/expression.html#slice_expressions
+ */
+extern (C++) final class SliceExp : UnaExp
+{
+ Expression upr; // null if implicit 0
+ Expression lwr; // null if implicit [length - 1]
+
+ VarDeclaration lengthVar;
+ bool upperIsInBounds; // true if upr <= e1.length
+ bool lowerIsLessThanUpper; // true if lwr <= upr
+ bool arrayop; // an array operation, rather than a slice
+
+ /************************************************************/
+ extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie)
+ {
+ super(loc, TOK.slice, __traits(classInstanceSize, SliceExp), e1);
+ this.upr = ie ? ie.upr : null;
+ this.lwr = ie ? ie.lwr : null;
+ }
+
+ extern (D) this(const ref Loc loc, Expression e1, Expression lwr, Expression upr)
+ {
+ super(loc, TOK.slice, __traits(classInstanceSize, SliceExp), e1);
+ this.upr = upr;
+ this.lwr = lwr;
+ }
+
+ override SliceExp syntaxCopy()
+ {
+ auto se = new SliceExp(loc, e1.syntaxCopy(), lwr ? lwr.syntaxCopy() : null, upr ? upr.syntaxCopy() : null);
+ se.lengthVar = this.lengthVar; // bug7871
+ return se;
+ }
+
+ override bool isLvalue()
+ {
+ /* slice expression is rvalue in default, but
+ * conversion to reference of static array is only allowed.
+ */
+ return (type && type.toBasetype().ty == Tsarray);
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ //printf("SliceExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL);
+ return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ error("slice expression `%s` is not a modifiable lvalue", toChars());
+ return this;
+ }
+
+ override bool isBool(bool result)
+ {
+ return e1.isBool(result);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ArrayLengthExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e1)
+ {
+ super(loc, TOK.arrayLength, __traits(classInstanceSize, ArrayLengthExp), e1);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * e1 [ a0, a1, a2, a3 ,... ]
+ *
+ * http://dlang.org/spec/expression.html#index_expressions
+ */
+extern (C++) final class ArrayExp : UnaExp
+{
+ Expressions* arguments; // Array of Expression's a0..an
+
+ size_t currentDimension; // for opDollar
+ VarDeclaration lengthVar;
+
+ extern (D) this(const ref Loc loc, Expression e1, Expression index = null)
+ {
+ super(loc, TOK.array, __traits(classInstanceSize, ArrayExp), e1);
+ arguments = new Expressions();
+ if (index)
+ arguments.push(index);
+ }
+
+ extern (D) this(const ref Loc loc, Expression e1, Expressions* args)
+ {
+ super(loc, TOK.array, __traits(classInstanceSize, ArrayExp), e1);
+ arguments = args;
+ }
+
+ override ArrayExp syntaxCopy()
+ {
+ auto ae = new ArrayExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments));
+ ae.lengthVar = this.lengthVar; // bug7871
+ return ae;
+ }
+
+ override bool isLvalue()
+ {
+ if (type && type.toBasetype().ty == Tvoid)
+ return false;
+ return true;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (type && type.toBasetype().ty == Tvoid)
+ error("`void`s have no value");
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DotExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.dot, __traits(classInstanceSize, DotExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class CommaExp : BinExp
+{
+ /// This is needed because AssignExp rewrites CommaExp, hence it needs
+ /// to trigger the deprecation.
+ const bool isGenerated;
+
+ /// Temporary variable to enable / disable deprecation of comma expression
+ /// depending on the context.
+ /// Since most constructor calls are rewritting, the only place where
+ /// false will be passed will be from the parser.
+ bool allowCommaExp;
+
+
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool generated = true)
+ {
+ super(loc, TOK.comma, __traits(classInstanceSize, CommaExp), e1, e2);
+ allowCommaExp = isGenerated = generated;
+ }
+
+ override bool isLvalue()
+ {
+ return e2.isLvalue();
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ e2 = e2.toLvalue(sc, null);
+ return this;
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ e2 = e2.modifiableLvalue(sc, e);
+ return this;
+ }
+
+ override bool isBool(bool result)
+ {
+ return e2.isBool(result);
+ }
+
+ override Expression addDtorHook(Scope* sc)
+ {
+ e2 = e2.addDtorHook(sc);
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ /**
+ * If the argument is a CommaExp, set a flag to prevent deprecation messages
+ *
+ * It's impossible to know from CommaExp.semantic if the result will
+ * be used, hence when there is a result (type != void), a deprecation
+ * message is always emitted.
+ * However, some construct can produce a result but won't use it
+ * (ExpStatement and for loop increment). Those should call this function
+ * to prevent unwanted deprecations to be emitted.
+ *
+ * Params:
+ * exp = An expression that discards its result.
+ * If the argument is null or not a CommaExp, nothing happens.
+ */
+ static void allow(Expression exp)
+ {
+ if (exp)
+ if (auto ce = exp.isCommaExp())
+ ce.allowCommaExp = true;
+ }
+}
+
+/***********************************************************
+ * Mainly just a placeholder
+ */
+extern (C++) final class IntervalExp : Expression
+{
+ Expression lwr;
+ Expression upr;
+
+ extern (D) this(const ref Loc loc, Expression lwr, Expression upr)
+ {
+ super(loc, TOK.interval, __traits(classInstanceSize, IntervalExp));
+ this.lwr = lwr;
+ this.upr = upr;
+ }
+
+ override Expression syntaxCopy()
+ {
+ return new IntervalExp(loc, lwr.syntaxCopy(), upr.syntaxCopy());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+extern (C++) final class DelegatePtrExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e1)
+ {
+ super(loc, TOK.delegatePointer, __traits(classInstanceSize, DelegatePtrExp), e1);
+ }
+
+ override bool isLvalue()
+ {
+ return e1.isLvalue();
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ e1 = e1.toLvalue(sc, e);
+ return this;
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ if (sc.func.setUnsafe())
+ {
+ error("cannot modify delegate pointer in `@safe` code `%s`", toChars());
+ return ErrorExp.get();
+ }
+ return Expression.modifiableLvalue(sc, e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DelegateFuncptrExp : UnaExp
+{
+ extern (D) this(const ref Loc loc, Expression e1)
+ {
+ super(loc, TOK.delegateFunctionPointer, __traits(classInstanceSize, DelegateFuncptrExp), e1);
+ }
+
+ override bool isLvalue()
+ {
+ return e1.isLvalue();
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ e1 = e1.toLvalue(sc, e);
+ return this;
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ if (sc.func.setUnsafe())
+ {
+ error("cannot modify delegate function pointer in `@safe` code `%s`", toChars());
+ return ErrorExp.get();
+ }
+ return Expression.modifiableLvalue(sc, e);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * e1 [ e2 ]
+ */
+extern (C++) final class IndexExp : BinExp
+{
+ VarDeclaration lengthVar;
+ bool modifiable = false; // assume it is an rvalue
+ bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1
+
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.index, __traits(classInstanceSize, IndexExp), e1, e2);
+ //printf("IndexExp::IndexExp('%s')\n", toChars());
+ }
+
+ override IndexExp syntaxCopy()
+ {
+ auto ie = new IndexExp(loc, e1.syntaxCopy(), e2.syntaxCopy());
+ ie.lengthVar = this.lengthVar; // bug7871
+ return ie;
+ }
+
+ override bool isLvalue()
+ {
+ if (e1.op == TOK.assocArrayLiteral)
+ return false;
+ if (e1.type.ty == Tsarray ||
+ (e1.op == TOK.index && e1.type.ty != Tarray))
+ {
+ return e1.isLvalue();
+ }
+ return true;
+ }
+
+ override Expression toLvalue(Scope* sc, Expression e)
+ {
+ if (isLvalue())
+ return this;
+ return Expression.toLvalue(sc, e);
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ //printf("IndexExp::modifiableLvalue(%s)\n", toChars());
+ Expression ex = markSettingAAElem();
+ if (ex.op == TOK.error)
+ return ex;
+
+ return Expression.modifiableLvalue(sc, e);
+ }
+
+ extern (D) Expression markSettingAAElem()
+ {
+ if (e1.type.toBasetype().ty == Taarray)
+ {
+ Type t2b = e2.type.toBasetype();
+ if (t2b.ty == Tarray && t2b.nextOf().isMutable())
+ {
+ error("associative arrays can only be assigned values with immutable keys, not `%s`", e2.type.toChars());
+ return ErrorExp.get();
+ }
+ modifiable = true;
+
+ if (auto ie = e1.isIndexExp())
+ {
+ Expression ex = ie.markSettingAAElem();
+ if (ex.op == TOK.error)
+ return ex;
+ assert(ex == e1);
+ }
+ }
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * For both i++ and i--
+ */
+extern (C++) final class PostExp : BinExp
+{
+ extern (D) this(TOK op, const ref Loc loc, Expression e)
+ {
+ super(loc, op, __traits(classInstanceSize, PostExp), e, IntegerExp.literal!1);
+ assert(op == TOK.minusMinus || op == TOK.plusPlus);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * For both ++i and --i
+ */
+extern (C++) final class PreExp : UnaExp
+{
+ extern (D) this(TOK op, const ref Loc loc, Expression e)
+ {
+ super(loc, op, __traits(classInstanceSize, PreExp), e);
+ assert(op == TOK.preMinusMinus || op == TOK.prePlusPlus);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+enum MemorySet
+{
+ none = 0, // simple assignment
+ blockAssign = 1, // setting the contents of an array
+ referenceInit = 2, // setting the reference of STC.ref_ variable
+}
+
+/***********************************************************
+ */
+extern (C++) class AssignExp : BinExp
+{
+ MemorySet memset;
+
+ /************************************************************/
+ /* op can be TOK.assign, TOK.construct, or TOK.blit */
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.assign, __traits(classInstanceSize, AssignExp), e1, e2);
+ }
+
+ this(const ref Loc loc, TOK tok, Expression e1, Expression e2)
+ {
+ super(loc, tok, __traits(classInstanceSize, AssignExp), e1, e2);
+ }
+
+ override final bool isLvalue()
+ {
+ // Array-op 'x[] = y[]' should make an rvalue.
+ // Setting array length 'x.length = v' should make an rvalue.
+ if (e1.op == TOK.slice || e1.op == TOK.arrayLength)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ override final Expression toLvalue(Scope* sc, Expression ex)
+ {
+ if (e1.op == TOK.slice || e1.op == TOK.arrayLength)
+ {
+ return Expression.toLvalue(sc, ex);
+ }
+
+ /* In front-end level, AssignExp should make an lvalue of e1.
+ * Taking the address of e1 will be handled in low level layer,
+ * so this function does nothing.
+ */
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ConstructExp : AssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.construct, e1, e2);
+ }
+
+ // Internal use only. If `v` is a reference variable, the assignment
+ // will become a reference initialization automatically.
+ extern (D) this(const ref Loc loc, VarDeclaration v, Expression e2)
+ {
+ auto ve = new VarExp(loc, v);
+ assert(v.type && ve.type);
+
+ super(loc, TOK.construct, ve, e2);
+
+ if (v.isReference())
+ memset = MemorySet.referenceInit;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class BlitExp : AssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.blit, e1, e2);
+ }
+
+ // Internal use only. If `v` is a reference variable, the assinment
+ // will become a reference rebinding automatically.
+ extern (D) this(const ref Loc loc, VarDeclaration v, Expression e2)
+ {
+ auto ve = new VarExp(loc, v);
+ assert(v.type && ve.type);
+
+ super(loc, TOK.blit, ve, e2);
+
+ if (v.isReference())
+ memset = MemorySet.referenceInit;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class AddAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.addAssign, __traits(classInstanceSize, AddAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class MinAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.minAssign, __traits(classInstanceSize, MinAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class MulAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.mulAssign, __traits(classInstanceSize, MulAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DivAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.divAssign, __traits(classInstanceSize, DivAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ModAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.modAssign, __traits(classInstanceSize, ModAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class AndAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.andAssign, __traits(classInstanceSize, AndAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class OrAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.orAssign, __traits(classInstanceSize, OrAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class XorAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.xorAssign, __traits(classInstanceSize, XorAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class PowAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.powAssign, __traits(classInstanceSize, PowAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ShlAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.leftShiftAssign, __traits(classInstanceSize, ShlAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ShrAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.rightShiftAssign, __traits(classInstanceSize, ShrAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class UshrAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.unsignedRightShiftAssign, __traits(classInstanceSize, UshrAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * The ~= operator. It can have one of the following operators:
+ *
+ * TOK.concatenateAssign - appending T[] to T[]
+ * TOK.concatenateElemAssign - appending T to T[]
+ * TOK.concatenateDcharAssign - appending dchar to T[]
+ *
+ * The parser initially sets it to TOK.concatenateAssign, and semantic() later decides which
+ * of the three it will be set to.
+ */
+extern (C++) class CatAssignExp : BinAssignExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.concatenateAssign, __traits(classInstanceSize, CatAssignExp), e1, e2);
+ }
+
+ extern (D) this(const ref Loc loc, TOK tok, Expression e1, Expression e2)
+ {
+ super(loc, tok, __traits(classInstanceSize, CatAssignExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+///
+extern (C++) final class CatElemAssignExp : CatAssignExp
+{
+ extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2)
+ {
+ super(loc, TOK.concatenateElemAssign, e1, e2);
+ this.type = type;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+///
+extern (C++) final class CatDcharAssignExp : CatAssignExp
+{
+ extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2)
+ {
+ super(loc, TOK.concatenateDcharAssign, e1, e2);
+ this.type = type;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#add_expressions
+ */
+extern (C++) final class AddExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.add, __traits(classInstanceSize, AddExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class MinExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.min, __traits(classInstanceSize, MinExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#cat_expressions
+ */
+extern (C++) final class CatExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.concatenate, __traits(classInstanceSize, CatExp), e1, e2);
+ }
+
+ override Expression resolveLoc(const ref Loc loc, Scope* sc)
+ {
+ e1 = e1.resolveLoc(loc, sc);
+ e2 = e2.resolveLoc(loc, sc);
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#mul_expressions
+ */
+extern (C++) final class MulExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.mul, __traits(classInstanceSize, MulExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#mul_expressions
+ */
+extern (C++) final class DivExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.div, __traits(classInstanceSize, DivExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#mul_expressions
+ */
+extern (C++) final class ModExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.mod, __traits(classInstanceSize, ModExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#pow_expressions
+ */
+extern (C++) final class PowExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.pow, __traits(classInstanceSize, PowExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ShlExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.leftShift, __traits(classInstanceSize, ShlExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ShrExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.rightShift, __traits(classInstanceSize, ShrExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class UshrExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.unsignedRightShift, __traits(classInstanceSize, UshrExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class AndExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.and, __traits(classInstanceSize, AndExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class OrExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.or, __traits(classInstanceSize, OrExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class XorExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.xor, __traits(classInstanceSize, XorExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * http://dlang.org/spec/expression.html#andand_expressions
+ * http://dlang.org/spec/expression.html#oror_expressions
+ */
+extern (C++) final class LogicalExp : BinExp
+{
+ extern (D) this(const ref Loc loc, TOK op, Expression e1, Expression e2)
+ {
+ super(loc, op, __traits(classInstanceSize, LogicalExp), e1, e2);
+ assert(op == TOK.andAnd || op == TOK.orOr);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * `op` is one of:
+ * TOK.lessThan, TOK.lessOrEqual, TOK.greaterThan, TOK.greaterOrEqual
+ *
+ * http://dlang.org/spec/expression.html#relation_expressions
+ */
+extern (C++) final class CmpExp : BinExp
+{
+ extern (D) this(TOK op, const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, op, __traits(classInstanceSize, CmpExp), e1, e2);
+ assert(op == TOK.lessThan || op == TOK.lessOrEqual || op == TOK.greaterThan || op == TOK.greaterOrEqual);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class InExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.in_, __traits(classInstanceSize, InExp), e1, e2);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * This deletes the key e1 from the associative array e2
+ */
+extern (C++) final class RemoveExp : BinExp
+{
+ extern (D) this(const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, TOK.remove, __traits(classInstanceSize, RemoveExp), e1, e2);
+ type = Type.tbool;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * `==` and `!=`
+ *
+ * TOK.equal and TOK.notEqual
+ *
+ * http://dlang.org/spec/expression.html#equality_expressions
+ */
+extern (C++) final class EqualExp : BinExp
+{
+ extern (D) this(TOK op, const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, op, __traits(classInstanceSize, EqualExp), e1, e2);
+ assert(op == TOK.equal || op == TOK.notEqual);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * `is` and `!is`
+ *
+ * TOK.identity and TOK.notIdentity
+ *
+ * http://dlang.org/spec/expression.html#identity_expressions
+ */
+extern (C++) final class IdentityExp : BinExp
+{
+ extern (D) this(TOK op, const ref Loc loc, Expression e1, Expression e2)
+ {
+ super(loc, op, __traits(classInstanceSize, IdentityExp), e1, e2);
+ assert(op == TOK.identity || op == TOK.notIdentity);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * `econd ? e1 : e2`
+ *
+ * http://dlang.org/spec/expression.html#conditional_expressions
+ */
+extern (C++) final class CondExp : BinExp
+{
+ Expression econd;
+
+ extern (D) this(const ref Loc loc, Expression econd, Expression e1, Expression e2)
+ {
+ super(loc, TOK.question, __traits(classInstanceSize, CondExp), e1, e2);
+ this.econd = econd;
+ }
+
+ override CondExp syntaxCopy()
+ {
+ return new CondExp(loc, econd.syntaxCopy(), e1.syntaxCopy(), e2.syntaxCopy());
+ }
+
+ override bool isLvalue()
+ {
+ return e1.isLvalue() && e2.isLvalue();
+ }
+
+ override Expression toLvalue(Scope* sc, Expression ex)
+ {
+ // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
+ CondExp e = cast(CondExp)copy();
+ e.e1 = e1.toLvalue(sc, null).addressOf();
+ e.e2 = e2.toLvalue(sc, null).addressOf();
+ e.type = type.pointerTo();
+ return new PtrExp(loc, e, type);
+ }
+
+ override Expression modifiableLvalue(Scope* sc, Expression e)
+ {
+ if (!e1.isLvalue() && !e2.isLvalue())
+ {
+ error("conditional expression `%s` is not a modifiable lvalue", toChars());
+ return ErrorExp.get();
+ }
+ e1 = e1.modifiableLvalue(sc, e1);
+ e2 = e2.modifiableLvalue(sc, e2);
+ return toLvalue(sc, this);
+ }
+
+ void hookDtors(Scope* sc)
+ {
+ extern (C++) final class DtorVisitor : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ public:
+ Scope* sc;
+ CondExp ce;
+ VarDeclaration vcond;
+ bool isThen;
+
+ extern (D) this(Scope* sc, CondExp ce)
+ {
+ this.sc = sc;
+ this.ce = ce;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("(e = %s)\n", e.toChars());
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ auto v = e.declaration.isVarDeclaration();
+ if (v && !v.isDataseg())
+ {
+ if (v._init)
+ {
+ if (auto ei = v._init.isExpInitializer())
+ walkPostorder(ei.exp, this);
+ }
+
+ if (v.edtor)
+ walkPostorder(v.edtor, this);
+
+ if (v.needsScopeDtor())
+ {
+ if (!vcond)
+ {
+ vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd);
+ vcond.dsymbolSemantic(sc);
+
+ Expression de = new DeclarationExp(ce.econd.loc, vcond);
+ de = de.expressionSemantic(sc);
+
+ Expression ve = new VarExp(ce.econd.loc, vcond);
+ ce.econd = Expression.combine(de, ve);
+ }
+
+ //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
+ Expression ve = new VarExp(vcond.loc, vcond);
+ if (isThen)
+ v.edtor = new LogicalExp(v.edtor.loc, TOK.andAnd, ve, v.edtor);
+ else
+ v.edtor = new LogicalExp(v.edtor.loc, TOK.orOr, ve, v.edtor);
+ v.edtor = v.edtor.expressionSemantic(sc);
+ //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
+ }
+ }
+ }
+ }
+
+ scope DtorVisitor v = new DtorVisitor(sc, this);
+ //printf("+%s\n", toChars());
+ v.isThen = true;
+ walkPostorder(e1, v);
+ v.isThen = false;
+ walkPostorder(e2, v);
+ //printf("-%s\n", toChars());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/// Returns: if this token is the `op` for a derived `DefaultInitExp` class.
+bool isDefaultInitOp(TOK op) pure nothrow @safe @nogc
+{
+ return op == TOK.prettyFunction || op == TOK.functionString ||
+ op == TOK.line || op == TOK.moduleString ||
+ op == TOK.file || op == TOK.fileFullPath ;
+}
+
+/***********************************************************
+ */
+extern (C++) class DefaultInitExp : Expression
+{
+ extern (D) this(const ref Loc loc, TOK op, int size)
+ {
+ super(loc, op, size);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class FileInitExp : DefaultInitExp
+{
+ extern (D) this(const ref Loc loc, TOK tok)
+ {
+ super(loc, tok, __traits(classInstanceSize, FileInitExp));
+ }
+
+ override Expression resolveLoc(const ref Loc loc, Scope* sc)
+ {
+ //printf("FileInitExp::resolve() %s\n", toChars());
+ const(char)* s;
+ if (op == TOK.fileFullPath)
+ s = FileName.toAbsolute(loc.isValid() ? loc.filename : sc._module.srcfile.toChars());
+ else
+ s = loc.isValid() ? loc.filename : sc._module.ident.toChars();
+
+ Expression e = new StringExp(loc, s.toDString());
+ e = e.expressionSemantic(sc);
+ e = e.castTo(sc, type);
+ return e;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class LineInitExp : DefaultInitExp
+{
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, TOK.line, __traits(classInstanceSize, LineInitExp));
+ }
+
+ override Expression resolveLoc(const ref Loc loc, Scope* sc)
+ {
+ Expression e = new IntegerExp(loc, loc.linnum, Type.tint32);
+ e = e.castTo(sc, type);
+ return e;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ModuleInitExp : DefaultInitExp
+{
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, TOK.moduleString, __traits(classInstanceSize, ModuleInitExp));
+ }
+
+ override Expression resolveLoc(const ref Loc loc, Scope* sc)
+ {
+ const auto s = (sc.callsc ? sc.callsc : sc)._module.toPrettyChars().toDString();
+ Expression e = new StringExp(loc, s);
+ e = e.expressionSemantic(sc);
+ e = e.castTo(sc, type);
+ return e;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class FuncInitExp : DefaultInitExp
+{
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, TOK.functionString, __traits(classInstanceSize, FuncInitExp));
+ }
+
+ override Expression resolveLoc(const ref Loc loc, Scope* sc)
+ {
+ const(char)* s;
+ if (sc.callsc && sc.callsc.func)
+ s = sc.callsc.func.Dsymbol.toPrettyChars();
+ else if (sc.func)
+ s = sc.func.Dsymbol.toPrettyChars();
+ else
+ s = "";
+ Expression e = new StringExp(loc, s.toDString());
+ e = e.expressionSemantic(sc);
+ e.type = Type.tstring;
+ return e;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class PrettyFuncInitExp : DefaultInitExp
+{
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, TOK.prettyFunction, __traits(classInstanceSize, PrettyFuncInitExp));
+ }
+
+ override Expression resolveLoc(const ref Loc loc, Scope* sc)
+ {
+ FuncDeclaration fd = (sc.callsc && sc.callsc.func)
+ ? sc.callsc.func
+ : sc.func;
+
+ const(char)* s;
+ if (fd)
+ {
+ const funcStr = fd.Dsymbol.toPrettyChars();
+ OutBuffer buf;
+ functionToBufferWithIdent(fd.type.isTypeFunction(), &buf, funcStr, fd.isStatic);
+ s = buf.extractChars();
+ }
+ else
+ {
+ s = "";
+ }
+
+ Expression e = new StringExp(loc, s.toDString());
+ e = e.expressionSemantic(sc);
+ e.type = Type.tstring;
+ return e;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/**
+ * Objective-C class reference expression.
+ *
+ * Used to get the metaclass of an Objective-C class, `NSObject.Class`.
+ */
+extern (C++) final class ObjcClassReferenceExp : Expression
+{
+ ClassDeclaration classDeclaration;
+
+ extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration)
+ {
+ super(loc, TOK.objcClassReference,
+ __traits(classInstanceSize, ObjcClassReferenceExp));
+ this.classDeclaration = classDeclaration;
+ type = objc.getRuntimeMetaclass(classDeclaration).getType();
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/*******************
+ * C11 6.5.1.1 Generic Selection
+ * For ImportC
+ */
+extern (C++) final class GenericExp : Expression
+{
+ Expression cntlExp; /// controlling expression of a generic selection (not evaluated)
+ Types* types; /// type-names for generic associations (null entry for `default`)
+ Expressions* exps; /// 1:1 mapping of typeNames to exps
+
+ extern (D) this(const ref Loc loc, Expression cntlExp, Types* types, Expressions* exps)
+ {
+ super(loc, TOK._Generic, __traits(classInstanceSize, GenericExp));
+ this.cntlExp = cntlExp;
+ this.types = types;
+ this.exps = exps;
+ assert(types.length == exps.length); // must be the same and >=1
+ }
+
+ override GenericExp syntaxCopy()
+ {
+ return new GenericExp(loc, cntlExp.syntaxCopy(), Type.arraySyntaxCopy(types), Expression.arraySyntaxCopy(exps));
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***************************************
+ * Parameters:
+ * sc: scope
+ * flag: 1: do not issue error message for invalid modification
+ 2: the exp is a DotVarExp and a subfield of the leftmost
+ variable is modified
+ * Returns:
+ * Whether the type is modifiable
+ */
+extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyFlags.none)
+{
+ switch(exp.op)
+ {
+ case TOK.variable:
+ auto varExp = cast(VarExp)exp;
+
+ //printf("VarExp::checkModifiable %s", varExp.toChars());
+ assert(varExp.type);
+ return varExp.var.checkModify(varExp.loc, sc, null, flag);
+
+ case TOK.dotVariable:
+ auto dotVarExp = cast(DotVarExp)exp;
+
+ //printf("DotVarExp::checkModifiable %s %s\n", dotVarExp.toChars(), dotVarExp.type.toChars());
+ if (dotVarExp.e1.op == TOK.this_)
+ return dotVarExp.var.checkModify(dotVarExp.loc, sc, dotVarExp.e1, flag);
+
+ /* https://issues.dlang.org/show_bug.cgi?id=12764
+ * If inside a constructor and an expression of type `this.field.var`
+ * is encountered, where `field` is a struct declaration with
+ * default construction disabled, we must make sure that
+ * assigning to `var` does not imply that `field` was initialized
+ */
+ if (sc.func && sc.func.isCtorDeclaration())
+ {
+ // if inside a constructor scope and e1 of this DotVarExp
+ // is another DotVarExp, then check if the leftmost expression is a `this` identifier
+ if (auto dve = dotVarExp.e1.isDotVarExp())
+ {
+ // Iterate the chain of DotVarExp to find `this`
+ // Keep track whether access to fields was limited to union members
+ // s.t. one can initialize an entire struct inside nested unions
+ // (but not its members)
+ bool onlyUnion = true;
+ while (true)
+ {
+ auto v = dve.var.isVarDeclaration();
+ assert(v);
+
+ // Accessing union member?
+ auto t = v.type.isTypeStruct();
+ if (!t || !t.sym.isUnionDeclaration())
+ onlyUnion = false;
+
+ // Another DotVarExp left?
+ if (!dve.e1 || dve.e1.op != TOK.dotVariable)
+ break;
+
+ dve = cast(DotVarExp) dve.e1;
+ }
+
+ if (dve.e1.op == TOK.this_)
+ {
+ scope v = dve.var.isVarDeclaration();
+ /* if v is a struct member field with no initializer, no default construction
+ * and v wasn't intialized before
+ */
+ if (v && v.isField() && !v._init && !v.ctorinit)
+ {
+ if (auto ts = v.type.isTypeStruct())
+ {
+ if (ts.sym.noDefaultCtor)
+ {
+ /* checkModify will consider that this is an initialization
+ * of v while it is actually an assignment of a field of v
+ */
+ scope modifyLevel = v.checkModify(dotVarExp.loc, sc, dve.e1, !onlyUnion ? (flag | ModifyFlags.fieldAssign) : flag);
+ if (modifyLevel == Modifiable.initialization)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=22118
+ // v is a union type field that was assigned
+ // a variable, therefore it counts as initialization
+ if (v.ctorinit)
+ return Modifiable.initialization;
+
+ return Modifiable.yes;
+ }
+ return modifyLevel;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //printf("\te1 = %s\n", e1.toChars());
+ return dotVarExp.e1.checkModifiable(sc, flag);
+
+ case TOK.star:
+ auto ptrExp = cast(PtrExp)exp;
+ if (auto se = ptrExp.e1.isSymOffExp())
+ {
+ return se.var.checkModify(ptrExp.loc, sc, null, flag);
+ }
+ else if (auto ae = ptrExp.e1.isAddrExp())
+ {
+ return ae.e1.checkModifiable(sc, flag);
+ }
+ return Modifiable.yes;
+
+ case TOK.slice:
+ auto sliceExp = cast(SliceExp)exp;
+
+ //printf("SliceExp::checkModifiable %s\n", sliceExp.toChars());
+ auto e1 = sliceExp.e1;
+ if (e1.type.ty == Tsarray || (e1.op == TOK.index && e1.type.ty != Tarray) || e1.op == TOK.slice)
+ {
+ return e1.checkModifiable(sc, flag);
+ }
+ return Modifiable.yes;
+
+ case TOK.comma:
+ return (cast(CommaExp)exp).e2.checkModifiable(sc, flag);
+
+ case TOK.index:
+ auto indexExp = cast(IndexExp)exp;
+ auto e1 = indexExp.e1;
+ if (e1.type.ty == Tsarray ||
+ e1.type.ty == Taarray ||
+ (e1.op == TOK.index && e1.type.ty != Tarray) ||
+ e1.op == TOK.slice)
+ {
+ return e1.checkModifiable(sc, flag);
+ }
+ return Modifiable.yes;
+
+ case TOK.question:
+ auto condExp = cast(CondExp)exp;
+ if (condExp.e1.checkModifiable(sc, flag) != Modifiable.no
+ && condExp.e2.checkModifiable(sc, flag) != Modifiable.no)
+ return Modifiable.yes;
+ return Modifiable.no;
+
+ default:
+ return exp.type ? Modifiable.yes : Modifiable.no; // default modifiable
+ }
+}
diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h
index 9413ad9a931..dec3713b676 100644
--- a/gcc/d/dmd/expression.h
+++ b/gcc/d/dmd/expression.h
@@ -13,13 +13,11 @@
#include "ast_node.h"
#include "complex_t.h"
#include "globals.h"
-#include "identifier.h"
#include "arraytypes.h"
-#include "intrange.h"
#include "visitor.h"
#include "tokens.h"
-#include "root/rmem.h"
+#include "root/dcompat.h"
class Type;
class TypeVector;
@@ -28,29 +26,17 @@ class TupleDeclaration;
class VarDeclaration;
class FuncDeclaration;
class FuncLiteralDeclaration;
-class Declaration;
class CtorDeclaration;
-class NewDeclaration;
class Dsymbol;
-class Import;
-class Module;
class ScopeDsymbol;
class Expression;
class Declaration;
-class AggregateDeclaration;
class StructDeclaration;
class TemplateInstance;
class TemplateDeclaration;
class ClassDeclaration;
-class BinExp;
-class UnaExp;
-class DotIdExp;
-class DotTemplateInstanceExp;
class OverloadSet;
-class Initializer;
class StringExp;
-class ArrayExp;
-class SliceExp;
struct UnionExp;
#ifdef IN_GCC
typedef union tree_node Symbol;
@@ -58,113 +44,53 @@ typedef union tree_node Symbol;
struct Symbol; // back end symbol
#endif
-Expression *expressionSemantic(Expression *e, Scope *sc);
-Expression *semanticX(DotIdExp *exp, Scope *sc);
-Expression *semanticY(DotIdExp *exp, Scope *sc, int flag);
-Expression *semanticY(DotTemplateInstanceExp *exp, Scope *sc, int flag);
-Expression *trySemantic(Expression *e, Scope *sc);
-Expression *unaSemantic(UnaExp *e, Scope *sc);
-Expression *binSemantic(BinExp *e, Scope *sc);
-Expression *binSemanticProp(BinExp *e, Scope *sc);
-StringExp *semanticString(Scope *sc, Expression *exp, const char *s);
-
-Expression *resolveProperties(Scope *sc, Expression *e);
-Expression *resolvePropertiesOnly(Scope *sc, Expression *e1);
-bool checkAccess(Loc loc, Scope *sc, Expression *e, Declaration *d);
-bool checkAccess(Scope *sc, Package *p);
-Expression *build_overload(Loc loc, Scope *sc, Expression *ethis, Expression *earg, Dsymbol *d);
-Dsymbol *search_function(ScopeDsymbol *ad, Identifier *funcid);
void expandTuples(Expressions *exps);
-TupleDeclaration *isAliasThisTuple(Expression *e);
-int expandAliasThisTuples(Expressions *exps, size_t starti = 0);
-FuncDeclaration *hasThis(Scope *sc);
-Expression *fromConstInitializer(int result, Expression *e);
-bool arrayExpressionSemantic(Expressions *exps, Scope *sc, bool preserveErrors = false);
-TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s);
-Expression *valueNoDtor(Expression *e);
-int modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1);
-Expression *resolveAliasThis(Scope *sc, Expression *e, bool gag = false);
-Expression *doCopyOrMove(Scope *sc, Expression *e);
-Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, Expression **pe0);
-Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, IntervalExp *ie, Expression **pe0);
-Expression *integralPromotions(Expression *e, Scope *sc);
-bool discardValue(Expression *e);
bool isTrivialExp(Expression *e);
-
-int isConst(Expression *e);
-Expression *toDelegate(Expression *e, Type* t, Scope *sc);
-AggregateDeclaration *isAggregate(Type *t);
-IntRange getIntRange(Expression *e);
-bool checkNonAssignmentArrayOp(Expression *e, bool suggestion = false);
-bool isUnaArrayOp(TOK op);
-bool isBinArrayOp(TOK op);
-bool isBinAssignArrayOp(TOK op);
-bool isArrayOpOperand(Expression *e);
-Expression *arrayOp(BinExp *e, Scope *sc);
-Expression *arrayOp(BinAssignExp *e, Scope *sc);
-bool hasSideEffect(Expression *e);
+bool hasSideEffect(Expression *e, bool assumeImpureCalls = false);
bool canThrow(Expression *e, FuncDeclaration *func, bool mustNotThrow);
-Expression *Expression_optimize(Expression *e, int result, bool keepLvalue);
-MATCH implicitConvTo(Expression *e, Type *t);
-Expression *implicitCastTo(Expression *e, Scope *sc, Type *t);
-Expression *castTo(Expression *e, Scope *sc, Type *t);
-Expression *ctfeInterpret(Expression *);
-Expression *inlineCopy(Expression *e, Scope *sc);
-Expression *op_overload(Expression *e, Scope *sc);
-Type *toStaticArrayType(SliceExp *e);
-Expression *scaleFactor(BinExp *be, Scope *sc);
-Expression *typeCombine(BinExp *be, Scope *sc);
-Expression *inferType(Expression *e, Type *t, int flag = 0);
-Expression *semanticTraits(TraitsExp *e, Scope *sc);
-Type *getIndirection(Type *t);
-
-Expression *checkGC(Scope *sc, Expression *e);
-
-/* Run CTFE on the expression, but allow the expression to be a TypeExp
- * or a tuple containing a TypeExp. (This is required by pragma(msg)).
- */
-Expression *ctfeInterpretForPragmaMsg(Expression *e);
-enum OwnedBy
+typedef unsigned char OwnedBy;
+enum
{
OWNEDcode, // normal code expression in AST
OWNEDctfe, // value expression for CTFE
OWNEDcache // constant value cached for CTFE
};
-#define WANTvalue 0 // default
-#define WANTexpand 1 // expand const/immutable variables if possible
+/**
+ * Specifies how the checkModify deals with certain situations
+ */
+enum class ModifyFlags
+{
+ /// Issue error messages on invalid modifications of the variable
+ none,
+ /// No errors are emitted for invalid modifications
+ noError = 0x1,
+ /// The modification occurs for a subfield of the current variable
+ fieldAssign = 0x2,
+};
class Expression : public ASTNode
{
public:
- Loc loc; // file location
- Type *type; // !=NULL means that semantic() has been run
TOK op; // to minimize use of dynamic_cast
unsigned char size; // # of bytes in Expression so we can copy() it
unsigned char parens; // if this is a parenthesized expression
+ Type *type; // !=NULL means that semantic() has been run
+ Loc loc; // file location
- Expression(Loc loc, TOK op, int size);
static void _init();
Expression *copy();
virtual Expression *syntaxCopy();
// kludge for template.isExpression()
- int dyncast() const { return DYNCAST_EXPRESSION; }
+ DYNCAST dyncast() const { return DYNCAST_EXPRESSION; }
- void print();
- const char *toChars();
+ const char *toChars() const;
void error(const char *format, ...) const;
void warning(const char *format, ...) const;
void deprecation(const char *format, ...) const;
- // creates a single expression which is effectively (e1, e2)
- // this new expression does not necessarily need to have valid D source code representation,
- // for example, it may include declaration expressions
- static Expression *combine(Expression *e1, Expression *e2);
- static Expression *extractLast(Expression *e, Expression **pe0);
- static Expressions *arraySyntaxCopy(Expressions *exps);
-
virtual dinteger_t toInteger();
virtual uinteger_t toUInteger();
virtual real_t toReal();
@@ -175,58 +101,24 @@ public:
virtual bool isLvalue();
virtual Expression *toLvalue(Scope *sc, Expression *e);
virtual Expression *modifiableLvalue(Scope *sc, Expression *e);
- Expression *implicitCastTo(Scope *sc, Type *t)
- {
- return ::implicitCastTo(this, sc, t);
- }
- MATCH implicitConvTo(Type *t)
- {
- return ::implicitConvTo(this, t);
- }
- Expression *castTo(Scope *sc, Type *t)
- {
- return ::castTo(this, sc, t);
- }
- virtual Expression *resolveLoc(Loc loc, Scope *sc);
+ Expression *implicitCastTo(Scope *sc, Type *t);
+ MATCH implicitConvTo(Type *t);
+ Expression *castTo(Scope *sc, Type *t);
+ virtual Expression *resolveLoc(const Loc &loc, Scope *sc);
virtual bool checkType();
virtual bool checkValue();
- bool checkScalar();
- bool checkNoBool();
- bool checkIntegral();
- bool checkArithmetic();
bool checkDeprecated(Scope *sc, Dsymbol *s);
- bool checkDisabled(Scope *sc, Dsymbol *s);
- bool checkPurity(Scope *sc, FuncDeclaration *f);
- bool checkPurity(Scope *sc, VarDeclaration *v);
- bool checkSafety(Scope *sc, FuncDeclaration *f);
- bool checkNogc(Scope *sc, FuncDeclaration *f);
- bool checkPostblit(Scope *sc, Type *t);
- bool checkRightThis(Scope *sc);
- bool checkReadModifyWrite(TOK rmwOp, Expression *ex = NULL);
- virtual int checkModifiable(Scope *sc, int flag = 0);
- virtual Expression *toBoolean(Scope *sc);
virtual Expression *addDtorHook(Scope *sc);
Expression *addressOf();
Expression *deref();
- Expression *optimize(int result, bool keepLvalue = false)
- {
- return Expression_optimize(this, result, keepLvalue);
- }
+ Expression *optimize(int result, bool keepLvalue = false);
// Entry point for CTFE.
// A compile-time result is required. Give an error if not possible
- Expression *ctfeInterpret()
- {
- return ::ctfeInterpret(this);
- }
-
- int isConst() { return ::isConst(this); }
+ Expression *ctfeInterpret();
+ int isConst();
virtual bool isBool(bool result);
- Expression *op_overload(Scope *sc)
- {
- return ::op_overload(this, sc);
- }
virtual bool hasCode()
{
@@ -263,7 +155,7 @@ public:
TraitsExp* isTraitsExp();
HaltExp* isHaltExp();
IsExp* isExp();
- CompileExp* isCompileExp();
+ MixinExp* isMixinExp();
ImportExp* isImportExp();
AssertExp* isAssertExp();
DotIdExp* isDotIdExp();
@@ -329,6 +221,7 @@ public:
EqualExp* isEqualExp();
IdentityExp* isIdentityExp();
CondExp* isCondExp();
+ GenericExp* isGenericExp();
DefaultInitExp* isDefaultInitExp();
FileInitExp* isFileInitExp();
LineInitExp* isLineInitExp();
@@ -336,6 +229,7 @@ public:
FuncInitExp* isFuncInitExp();
PrettyFuncInitExp* isPrettyFuncInitExp();
ClassReferenceExp* isClassReferenceExp();
+ virtual BinAssignExp* isBinAssignExp();
void accept(Visitor *v) { v->visit(this); }
};
@@ -345,10 +239,9 @@ class IntegerExp : public Expression
public:
dinteger_t value;
- IntegerExp(Loc loc, dinteger_t value, Type *type);
- IntegerExp(dinteger_t value);
static IntegerExp *create(Loc loc, dinteger_t value, Type *type);
- bool equals(RootObject *o);
+ static void emplace(UnionExp *pue, Loc loc, dinteger_t value, Type *type);
+ bool equals(const RootObject *o) const;
dinteger_t toInteger();
real_t toReal();
real_t toImaginary();
@@ -358,13 +251,13 @@ public:
void accept(Visitor *v) { v->visit(this); }
dinteger_t getInteger() { return value; }
void setInteger(dinteger_t value);
- void normalize();
+ template<int v>
+ static IntegerExp literal();
};
class ErrorExp : public Expression
{
public:
- ErrorExp();
Expression *toLvalue(Scope *sc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
@@ -376,9 +269,9 @@ class RealExp : public Expression
public:
real_t value;
- RealExp(Loc loc, real_t value, Type *type);
static RealExp *create(Loc loc, real_t value, Type *type);
- bool equals(RootObject *o);
+ static void emplace(UnionExp *pue, Loc loc, real_t value, Type *type);
+ bool equals(const RootObject *o) const;
dinteger_t toInteger();
uinteger_t toUInteger();
real_t toReal();
@@ -393,9 +286,9 @@ class ComplexExp : public Expression
public:
complex_t value;
- ComplexExp(Loc loc, complex_t value, Type *type);
static ComplexExp *create(Loc loc, complex_t value, Type *type);
- bool equals(RootObject *o);
+ static void emplace(UnionExp *pue, Loc loc, complex_t value, Type *type);
+ bool equals(const RootObject *o) const;
dinteger_t toInteger();
uinteger_t toUInteger();
real_t toReal();
@@ -410,7 +303,6 @@ class IdentifierExp : public Expression
public:
Identifier *ident;
- IdentifierExp(Loc loc, Identifier *ident);
static IdentifierExp *create(Loc loc, Identifier *ident);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
@@ -420,7 +312,6 @@ public:
class DollarExp : public IdentifierExp
{
public:
- DollarExp(Loc loc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -430,7 +321,7 @@ public:
Dsymbol *s;
bool hasOverloads;
- DsymbolExp(Loc loc, Dsymbol *s, bool hasOverloads = true);
+ DsymbolExp *syntaxCopy();
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
@@ -441,7 +332,7 @@ class ThisExp : public Expression
public:
VarDeclaration *var;
- ThisExp(Loc loc);
+ ThisExp *syntaxCopy();
bool isBool(bool result);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
@@ -452,18 +343,13 @@ public:
class SuperExp : public ThisExp
{
public:
- SuperExp(Loc loc);
-
void accept(Visitor *v) { v->visit(this); }
};
class NullExp : public Expression
{
public:
- unsigned char committed; // !=0 if type is committed
-
- NullExp(Loc loc, Type *t = NULL);
- bool equals(RootObject *o);
+ bool equals(const RootObject *o) const;
bool isBool(bool result);
StringExp *toStringExp();
void accept(Visitor *v) { v->visit(this); }
@@ -479,15 +365,13 @@ public:
utf8_t postfix; // 'c', 'w', 'd'
OwnedBy ownedByCtfe;
- StringExp(Loc loc, char *s);
- StringExp(Loc loc, void *s, size_t len);
- StringExp(Loc loc, void *s, size_t len, utf8_t postfix);
static StringExp *create(Loc loc, char *s);
static StringExp *create(Loc loc, void *s, size_t len);
- bool equals(RootObject *o);
+ static void emplace(UnionExp *pue, Loc loc, char *s);
+ static void emplace(UnionExp *pue, Loc loc, void *s, size_t len);
+ bool equals(const RootObject *o) const;
StringExp *toStringExp();
StringExp *toUTF8(Scope *sc);
- int compare(RootObject *obj);
bool isBool(bool result);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
@@ -496,7 +380,6 @@ public:
void accept(Visitor *v) { v->visit(this); }
size_t numberOfCodeUnits(int tynto = 0) const;
void writeTo(void* dest, bool zero, int tyto = 0) const;
- char *toPtr();
};
// Tuple
@@ -514,12 +397,10 @@ public:
*/
Expressions *exps;
- TupleExp(Loc loc, Expression *e0, Expressions *exps);
- TupleExp(Loc loc, Expressions *exps);
- TupleExp(Loc loc, TupleDeclaration *tup);
+ static TupleExp *create(Loc loc, Expressions *exps);
TupleExp *toTupleExp();
- Expression *syntaxCopy();
- bool equals(RootObject *o);
+ TupleExp *syntaxCopy();
+ bool equals(const RootObject *o) const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -531,14 +412,12 @@ public:
Expressions *elements;
OwnedBy ownedByCtfe;
- ArrayLiteralExp(Loc loc, Type *type, Expressions *elements);
- ArrayLiteralExp(Loc loc, Type *type, Expression *e);
- ArrayLiteralExp(Loc loc, Type *type, Expression *basis, Expressions *elements);
static ArrayLiteralExp *create(Loc loc, Expressions *elements);
- Expression *syntaxCopy();
- bool equals(RootObject *o);
- Expression *getElement(size_t i);
- static Expressions* copyElements(Expression *e1, Expression *e2 = NULL);
+ static void emplace(UnionExp *pue, Loc loc, Expressions *elements);
+ ArrayLiteralExp *syntaxCopy();
+ bool equals(const RootObject *o) const;
+ Expression *getElement(d_size_t i); // use opIndex instead
+ Expression *opIndex(d_size_t i);
bool isBool(bool result);
StringExp *toStringExp();
@@ -552,27 +431,13 @@ public:
Expressions *values;
OwnedBy ownedByCtfe;
- AssocArrayLiteralExp(Loc loc, Expressions *keys, Expressions *values);
- bool equals(RootObject *o);
- Expression *syntaxCopy();
+ bool equals(const RootObject *o) const;
+ AssocArrayLiteralExp *syntaxCopy();
bool isBool(bool result);
void accept(Visitor *v) { v->visit(this); }
};
-// scrubReturnValue is running
-#define stageScrub 0x1
-// hasNonConstPointers is running
-#define stageSearchPointers 0x2
-// optimize is running
-#define stageOptimize 0x4
-// apply is running
-#define stageApply 0x8
-//inlineScan is running
-#define stageInlineScan 0x10
-// toCBuffer is running
-#define stageToCBuffer 0x20
-
class StructLiteralExp : public Expression
{
public:
@@ -580,45 +445,44 @@ public:
Expressions *elements; // parallels sd->fields[] with NULL entries for fields to skip
Type *stype; // final type of result (can be different from sd's type)
- bool useStaticInit; // if this is true, use the StructDeclaration's init symbol
Symbol *sym; // back end symbol to initialize with literal
- OwnedBy ownedByCtfe;
-
- // pointer to the origin instance of the expression.
- // once a new expression is created, origin is set to 'this'.
- // anytime when an expression copy is created, 'origin' pointer is set to
- // 'origin' pointer value of the original expression.
+ /** pointer to the origin instance of the expression.
+ * once a new expression is created, origin is set to 'this'.
+ * anytime when an expression copy is created, 'origin' pointer is set to
+ * 'origin' pointer value of the original expression.
+ */
StructLiteralExp *origin;
// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer.
StructLiteralExp *inlinecopy;
- // anytime when recursive function is calling, 'stageflags' marks with bit flag of
- // current stage and unmarks before return from this function.
- // 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline'
- // (with infinite recursion) of this expression.
+ /** anytime when recursive function is calling, 'stageflags' marks with bit flag of
+ * current stage and unmarks before return from this function.
+ * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline'
+ * (with infinite recursion) of this expression.
+ */
int stageflags;
- StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions *elements, Type *stype = NULL);
+ bool useStaticInit; // if this is true, use the StructDeclaration's init symbol
+ bool isOriginal; // used when moving instances to indicate `this is this.origin`
+ OwnedBy ownedByCtfe;
+
static StructLiteralExp *create(Loc loc, StructDeclaration *sd, void *elements, Type *stype = NULL);
- bool equals(RootObject *o);
- Expression *syntaxCopy();
+ bool equals(const RootObject *o) const;
+ StructLiteralExp *syntaxCopy();
Expression *getField(Type *type, unsigned offset);
int getFieldIndex(Type *type, unsigned offset);
Expression *addDtorHook(Scope *sc);
+ Expression *toLvalue(Scope *sc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
};
-class DotIdExp;
-DotIdExp *typeDotIdExp(Loc loc, Type *type, Identifier *ident);
-
class TypeExp : public Expression
{
public:
- TypeExp(Loc loc, Type *type);
- Expression *syntaxCopy();
+ TypeExp *syntaxCopy();
bool checkType();
bool checkValue();
void accept(Visitor *v) { v->visit(this); }
@@ -629,8 +493,7 @@ class ScopeExp : public Expression
public:
ScopeDsymbol *sds;
- ScopeExp(Loc loc, ScopeDsymbol *sds);
- Expression *syntaxCopy();
+ ScopeExp *syntaxCopy();
bool checkType();
bool checkValue();
void accept(Visitor *v) { v->visit(this); }
@@ -642,7 +505,6 @@ public:
TemplateDeclaration *td;
FuncDeclaration *fd;
- TemplateExp(Loc loc, TemplateDeclaration *td, FuncDeclaration *fd = NULL);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
bool checkType();
@@ -663,13 +525,11 @@ public:
Expression *argprefix; // expression to be evaluated just before arguments[]
CtorDeclaration *member; // constructor function
- NewDeclaration *allocator; // allocator function
- int onstack; // allocate on stack
+ bool onstack; // allocate on stack
+ bool thrownew; // this NewExp is the expression of a ThrowStatement
- NewExp(Loc loc, Expression *thisexp, Expressions *newargs,
- Type *newtype, Expressions *arguments);
static NewExp *create(Loc loc, Expression *thisexp, Expressions *newargs, Type *newtype, Expressions *arguments);
- Expression *syntaxCopy();
+ NewExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -684,9 +544,7 @@ public:
ClassDeclaration *cd; // class being instantiated
Expressions *arguments; // Array of Expression's to call class constructor
- NewAnonClassExp(Loc loc, Expression *thisexp, Expressions *newargs,
- ClassDeclaration *cd, Expressions *arguments);
- Expression *syntaxCopy();
+ NewAnonClassExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -694,8 +552,8 @@ class SymbolExp : public Expression
{
public:
Declaration *var;
+ Dsymbol *originalScope;
bool hasOverloads;
- SymbolExp(Loc loc, TOK op, int size, Declaration *var, bool hasOverloads);
void accept(Visitor *v) { v->visit(this); }
};
@@ -707,7 +565,6 @@ class SymOffExp : public SymbolExp
public:
dinteger_t offset;
- SymOffExp(Loc loc, Declaration *var, dinteger_t offset, bool hasOverloads = true);
bool isBool(bool result);
void accept(Visitor *v) { v->visit(this); }
@@ -718,11 +575,9 @@ public:
class VarExp : public SymbolExp
{
public:
- VarExp(Loc loc, Declaration *var, bool hasOverloads = true);
+ bool delegateWasExtracted;
static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true);
- bool equals(RootObject *o);
- int checkModifiable(Scope *sc, int flag);
- bool checkReadModifyWrite();
+ bool equals(const RootObject *o) const;
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *modifiableLvalue(Scope *sc, Expression *e);
@@ -737,7 +592,6 @@ class OverExp : public Expression
public:
OverloadSet *vars;
- OverExp(Loc loc, OverloadSet *s);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
@@ -752,12 +606,9 @@ public:
TemplateDeclaration *td;
TOK tok;
- FuncExp(Loc loc, Dsymbol *s);
- bool equals(RootObject *o);
- void genIdent(Scope *sc);
- Expression *syntaxCopy();
- MATCH matchType(Type *to, Scope *sc, FuncExp **pfe, int flag = 0);
- const char *toChars();
+ bool equals(const RootObject *o) const;
+ FuncExp *syntaxCopy();
+ const char *toChars() const;
bool checkType();
bool checkValue();
@@ -774,8 +625,7 @@ class DeclarationExp : public Expression
public:
Dsymbol *declaration;
- DeclarationExp(Loc loc, Dsymbol *declaration);
- Expression *syntaxCopy();
+ DeclarationExp *syntaxCopy();
bool hasCode();
@@ -787,8 +637,7 @@ class TypeidExp : public Expression
public:
RootObject *obj;
- TypeidExp(Loc loc, RootObject *obj);
- Expression *syntaxCopy();
+ TypeidExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -798,16 +647,13 @@ public:
Identifier *ident;
Objects *args;
- TraitsExp(Loc loc, Identifier *ident, Objects *args);
- Expression *syntaxCopy();
+ TraitsExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
class HaltExp : public Expression
{
public:
- HaltExp(Loc loc);
-
void accept(Visitor *v) { v->visit(this); }
};
@@ -819,14 +665,12 @@ public:
*/
Type *targ;
Identifier *id; // can be NULL
- TOK tok; // ':' or '=='
Type *tspec; // can be NULL
- TOK tok2; // 'struct', 'union', etc.
TemplateParameters *parameters;
+ TOK tok; // ':' or '=='
+ TOK tok2; // 'struct', 'union', etc.
- IsExp(Loc loc, Type *targ, Identifier *id, TOK tok, Type *tspec,
- TOK tok2, TemplateParameters *parameters);
- Expression *syntaxCopy();
+ IsExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -838,17 +682,13 @@ public:
Expression *e1;
Type *att1; // Save alias this type to detect recursion
- UnaExp(Loc loc, TOK op, int size, Expression *e1);
- Expression *syntaxCopy();
+ UnaExp *syntaxCopy();
Expression *incompatibleTypes();
- Expression *resolveLoc(Loc loc, Scope *sc);
+ Expression *resolveLoc(const Loc &loc, Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
-typedef UnionExp (*fp_t)(Loc loc, Type *, Expression *, Expression *);
-typedef int (*fp2_t)(Loc loc, TOK, Expression *, Expression *);
-
class BinExp : public Expression
{
public:
@@ -858,12 +698,8 @@ public:
Type *att1; // Save alias this type to detect recursion
Type *att2; // Save alias this type to detect recursion
- BinExp(Loc loc, TOK op, int size, Expression *e1, Expression *e2);
- Expression *syntaxCopy();
+ BinExp *syntaxCopy();
Expression *incompatibleTypes();
- Expression *checkOpAssignTypes(Scope *sc);
- bool checkIntegralBin();
- bool checkArithmeticBin();
Expression *reorderSettingAAElem(Scope *sc);
@@ -873,31 +709,24 @@ public:
class BinAssignExp : public BinExp
{
public:
- BinAssignExp(Loc loc, TOK op, int size, Expression *e1, Expression *e2);
-
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *ex);
Expression *modifiableLvalue(Scope *sc, Expression *e);
+ BinAssignExp* isBinAssignExp();
void accept(Visitor *v) { v->visit(this); }
};
/****************************************************************/
-class CompileExp : public Expression
+class MixinExp : public UnaExp
{
public:
- Expressions *exps;
-
- CompileExp(Loc loc, Expressions *exps);
- Expression *syntaxCopy();
- bool equals(RootObject *o);
void accept(Visitor *v) { v->visit(this); }
};
class ImportExp : public UnaExp
{
public:
- ImportExp(Loc loc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
};
@@ -906,8 +735,7 @@ class AssertExp : public UnaExp
public:
Expression *msg;
- AssertExp(Loc loc, Expression *e, Expression *msg = NULL);
- Expression *syntaxCopy();
+ AssertExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -919,7 +747,6 @@ public:
bool noderef; // true if the result of the expression will never be dereferenced
bool wantsym; // do not replace Symbol with its initializer during semantic()
- DotIdExp(Loc loc, Expression *e, Identifier *ident);
static DotIdExp *create(Loc loc, Expression *e, Identifier *ident);
void accept(Visitor *v) { v->visit(this); }
};
@@ -929,7 +756,6 @@ class DotTemplateExp : public UnaExp
public:
TemplateDeclaration *td;
- DotTemplateExp(Loc loc, Expression *e, TemplateDeclaration *td);
bool checkType();
bool checkValue();
void accept(Visitor *v) { v->visit(this); }
@@ -941,9 +767,6 @@ public:
Declaration *var;
bool hasOverloads;
- DotVarExp(Loc loc, Expression *e, Declaration *var, bool hasOverloads = true);
- int checkModifiable(Scope *sc, int flag);
- bool checkReadModifyWrite();
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *modifiableLvalue(Scope *sc, Expression *e);
@@ -955,9 +778,7 @@ class DotTemplateInstanceExp : public UnaExp
public:
TemplateInstance *ti;
- DotTemplateInstanceExp(Loc loc, Expression *e, Identifier *name, Objects *tiargs);
- DotTemplateInstanceExp(Loc loc, Expression *e, TemplateInstance *ti);
- Expression *syntaxCopy();
+ DotTemplateInstanceExp *syntaxCopy();
bool findTempDecl(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -967,8 +788,8 @@ class DelegateExp : public UnaExp
public:
FuncDeclaration *func;
bool hasOverloads;
+ VarDeclaration *vthis2; // container for multi-context
- DelegateExp(Loc loc, Expression *e, FuncDeclaration *func, bool hasOverloads = true);
void accept(Visitor *v) { v->visit(this); }
};
@@ -978,7 +799,6 @@ class DotTypeExp : public UnaExp
public:
Dsymbol *sym; // symbol that represents a type
- DotTypeExp(Loc loc, Expression *e, Dsymbol *sym);
void accept(Visitor *v) { v->visit(this); }
};
@@ -988,16 +808,16 @@ public:
Expressions *arguments; // function arguments
FuncDeclaration *f; // symbol to call
bool directcall; // true if a virtual call is devirtualized
- CallExp(Loc loc, Expression *e, Expressions *exps);
- CallExp(Loc loc, Expression *e);
- CallExp(Loc loc, Expression *e, Expression *earg1);
- CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2);
+ bool inDebugStatement; // true if this was in a debug statement
+ bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code)
+ VarDeclaration *vthis2; // container for multi-context
static CallExp *create(Loc loc, Expression *e, Expressions *exps);
static CallExp *create(Loc loc, Expression *e);
static CallExp *create(Loc loc, Expression *e, Expression *earg1);
+ static CallExp *create(Loc loc, FuncDeclaration *fd, Expression *earg1);
- Expression *syntaxCopy();
+ CallExp *syntaxCopy();
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *addDtorHook(Scope *sc);
@@ -1008,18 +828,12 @@ public:
class AddrExp : public UnaExp
{
public:
- AddrExp(Loc loc, Expression *e);
- AddrExp(Loc loc, Expression *e, Type *t);
-
void accept(Visitor *v) { v->visit(this); }
};
class PtrExp : public UnaExp
{
public:
- PtrExp(Loc loc, Expression *e);
- PtrExp(Loc loc, Expression *e, Type *t);
- int checkModifiable(Scope *sc, int flag);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *modifiableLvalue(Scope *sc, Expression *e);
@@ -1030,31 +844,24 @@ public:
class NegExp : public UnaExp
{
public:
- NegExp(Loc loc, Expression *e);
-
void accept(Visitor *v) { v->visit(this); }
};
class UAddExp : public UnaExp
{
public:
- UAddExp(Loc loc, Expression *e);
-
void accept(Visitor *v) { v->visit(this); }
};
class ComExp : public UnaExp
{
public:
- ComExp(Loc loc, Expression *e);
-
void accept(Visitor *v) { v->visit(this); }
};
class NotExp : public UnaExp
{
public:
- NotExp(Loc loc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1062,8 +869,6 @@ class DeleteExp : public UnaExp
{
public:
bool isRAII;
- DeleteExp(Loc loc, Expression *e, bool isRAII);
- Expression *toBoolean(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1074,9 +879,9 @@ public:
Type *to; // type to cast to
unsigned char mod; // MODxxxxx
- CastExp(Loc loc, Expression *e, Type *t);
- CastExp(Loc loc, Expression *e, unsigned char mod);
- Expression *syntaxCopy();
+ CastExp *syntaxCopy();
+ bool isLvalue();
+ Expression *toLvalue(Scope *sc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1088,16 +893,15 @@ public:
unsigned dim; // number of elements in the vector
OwnedBy ownedByCtfe;
- VectorExp(Loc loc, Expression *e, Type *t);
static VectorExp *create(Loc loc, Expression *e, Type *t);
- Expression *syntaxCopy();
+ static void emplace(UnionExp *pue, Loc loc, Expression *e, Type *t);
+ VectorExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
class VectorArrayExp : public UnaExp
{
public:
- VectorArrayExp(Loc loc, Expression *e1);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
@@ -1113,10 +917,7 @@ public:
bool lowerIsLessThanUpper; // true if lwr <= upr
bool arrayop; // an array operation, rather than a slice
- SliceExp(Loc loc, Expression *e1, IntervalExp *ie);
- SliceExp(Loc loc, Expression *e1, Expression *lwr, Expression *upr);
- Expression *syntaxCopy();
- int checkModifiable(Scope *sc, int flag);
+ SliceExp *syntaxCopy();
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *modifiableLvalue(Scope *sc, Expression *e);
@@ -1128,8 +929,6 @@ public:
class ArrayLengthExp : public UnaExp
{
public:
- ArrayLengthExp(Loc loc, Expression *e1);
-
void accept(Visitor *v) { v->visit(this); }
};
@@ -1139,15 +938,13 @@ public:
Expression *lwr;
Expression *upr;
- IntervalExp(Loc loc, Expression *lwr, Expression *upr);
- Expression *syntaxCopy();
+ IntervalExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
class DelegatePtrExp : public UnaExp
{
public:
- DelegatePtrExp(Loc loc, Expression *e1);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *modifiableLvalue(Scope *sc, Expression *e);
@@ -1157,7 +954,6 @@ public:
class DelegateFuncptrExp : public UnaExp
{
public:
- DelegateFuncptrExp(Loc loc, Expression *e1);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *modifiableLvalue(Scope *sc, Expression *e);
@@ -1173,9 +969,7 @@ public:
size_t currentDimension; // for opDollar
VarDeclaration *lengthVar;
- ArrayExp(Loc loc, Expression *e1, Expression *index = NULL);
- ArrayExp(Loc loc, Expression *e1, Expressions *args);
- Expression *syntaxCopy();
+ ArrayExp *syntaxCopy();
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
@@ -1187,7 +981,6 @@ public:
class DotExp : public BinExp
{
public:
- DotExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1196,13 +989,10 @@ class CommaExp : public BinExp
public:
bool isGenerated;
bool allowCommaExp;
- CommaExp(Loc loc, Expression *e1, Expression *e2, bool generated = true);
- int checkModifiable(Scope *sc, int flag);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *modifiableLvalue(Scope *sc, Expression *e);
bool isBool(bool result);
- Expression *toBoolean(Scope *sc);
Expression *addDtorHook(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1214,15 +1004,11 @@ public:
bool modifiable;
bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1
- IndexExp(Loc loc, Expression *e1, Expression *e2);
- Expression *syntaxCopy();
- int checkModifiable(Scope *sc, int flag);
+ IndexExp *syntaxCopy();
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *modifiableLvalue(Scope *sc, Expression *e);
- Expression *markSettingAAElem();
-
void accept(Visitor *v) { v->visit(this); }
};
@@ -1231,7 +1017,6 @@ public:
class PostExp : public BinExp
{
public:
- PostExp(TOK op, Loc loc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1240,12 +1025,12 @@ public:
class PreExp : public UnaExp
{
public:
- PreExp(TOK op, Loc loc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
};
-enum MemorySet
+enum class MemorySet
{
+ none = 0, // simple assignment
blockAssign = 1, // setting the contents of an array
referenceInit = 2 // setting the reference of STCref variable
};
@@ -1253,12 +1038,10 @@ enum MemorySet
class AssignExp : public BinExp
{
public:
- int memset; // combination of MemorySet flags
+ MemorySet memset;
- AssignExp(Loc loc, Expression *e1, Expression *e2);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *ex);
- Expression *toBoolean(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1266,242 +1049,192 @@ public:
class ConstructExp : public AssignExp
{
public:
- ConstructExp(Loc loc, Expression *e1, Expression *e2);
- ConstructExp(Loc loc, VarDeclaration *v, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class BlitExp : public AssignExp
{
public:
- BlitExp(Loc loc, Expression *e1, Expression *e2);
- BlitExp(Loc loc, VarDeclaration *v, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class AddAssignExp : public BinAssignExp
{
public:
- AddAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class MinAssignExp : public BinAssignExp
{
public:
- MinAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class MulAssignExp : public BinAssignExp
{
public:
- MulAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class DivAssignExp : public BinAssignExp
{
public:
- DivAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class ModAssignExp : public BinAssignExp
{
public:
- ModAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class AndAssignExp : public BinAssignExp
{
public:
- AndAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class OrAssignExp : public BinAssignExp
{
public:
- OrAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class XorAssignExp : public BinAssignExp
{
public:
- XorAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class PowAssignExp : public BinAssignExp
{
public:
- PowAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class ShlAssignExp : public BinAssignExp
{
public:
- ShlAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class ShrAssignExp : public BinAssignExp
{
public:
- ShrAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class UshrAssignExp : public BinAssignExp
{
public:
- UshrAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class CatAssignExp : public BinAssignExp
{
public:
- CatAssignExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
class AddExp : public BinExp
{
public:
- AddExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class MinExp : public BinExp
{
public:
- MinExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class CatExp : public BinExp
{
public:
- CatExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class MulExp : public BinExp
{
public:
- MulExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class DivExp : public BinExp
{
public:
- DivExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class ModExp : public BinExp
{
public:
- ModExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class PowExp : public BinExp
{
public:
- PowExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class ShlExp : public BinExp
{
public:
- ShlExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class ShrExp : public BinExp
{
public:
- ShrExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class UshrExp : public BinExp
{
public:
- UshrExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class AndExp : public BinExp
{
public:
- AndExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class OrExp : public BinExp
{
public:
- OrExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class XorExp : public BinExp
{
public:
- XorExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class LogicalExp : public BinExp
{
public:
- LogicalExp(Loc loc, TOK op, Expression *e1, Expression *e2);
- Expression *toBoolean(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
class CmpExp : public BinExp
{
public:
- CmpExp(TOK op, Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class InExp : public BinExp
{
public:
- InExp(Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
class RemoveExp : public BinExp
{
public:
- RemoveExp(Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1510,8 +1243,6 @@ public:
class EqualExp : public BinExp
{
public:
- EqualExp(TOK op, Loc loc, Expression *e1, Expression *e2);
-
void accept(Visitor *v) { v->visit(this); }
};
@@ -1520,7 +1251,6 @@ public:
class IdentityExp : public BinExp
{
public:
- IdentityExp(TOK op, Loc loc, Expression *e1, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1531,66 +1261,66 @@ class CondExp : public BinExp
public:
Expression *econd;
- CondExp(Loc loc, Expression *econd, Expression *e1, Expression *e2);
- Expression *syntaxCopy();
- int checkModifiable(Scope *sc, int flag);
+ CondExp *syntaxCopy();
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
Expression *modifiableLvalue(Scope *sc, Expression *e);
- Expression *toBoolean(Scope *sc);
void hookDtors(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
+class GenericExp : Expression
+{
+ Expression cntlExp;
+ Types *types;
+ Expressions *exps;
+
+ GenericExp *syntaxCopy();
+
+ void accept(Visitor *v) { v->visit(this); }
+};
+
/****************************************************************/
class DefaultInitExp : public Expression
{
public:
- TOK subop; // which of the derived classes this is
-
- DefaultInitExp(Loc loc, TOK subop, int size);
void accept(Visitor *v) { v->visit(this); }
};
class FileInitExp : public DefaultInitExp
{
public:
- FileInitExp(Loc loc, TOK tok);
- Expression *resolveLoc(Loc loc, Scope *sc);
+ Expression *resolveLoc(const Loc &loc, Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
class LineInitExp : public DefaultInitExp
{
public:
- LineInitExp(Loc loc);
- Expression *resolveLoc(Loc loc, Scope *sc);
+ Expression *resolveLoc(const Loc &loc, Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
class ModuleInitExp : public DefaultInitExp
{
public:
- ModuleInitExp(Loc loc);
- Expression *resolveLoc(Loc loc, Scope *sc);
+ Expression *resolveLoc(const Loc &loc, Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
class FuncInitExp : public DefaultInitExp
{
public:
- FuncInitExp(Loc loc);
- Expression *resolveLoc(Loc loc, Scope *sc);
+ Expression *resolveLoc(const Loc &loc, Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
class PrettyFuncInitExp : public DefaultInitExp
{
public:
- PrettyFuncInitExp(Loc loc);
- Expression *resolveLoc(Loc loc, Scope *sc);
+ Expression *resolveLoc(const Loc &loc, Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -1652,45 +1382,10 @@ private:
/****************************************************************/
-/* Special values used by the interpreter
- */
+class ObjcClassReferenceExp : public Expression
+{
+public:
+ ClassDeclaration* classDeclaration;
-Expression *expType(Type *type, Expression *e);
-
-UnionExp Neg(Type *type, Expression *e1);
-UnionExp Com(Type *type, Expression *e1);
-UnionExp Not(Type *type, Expression *e1);
-UnionExp Bool(Type *type, Expression *e1);
-UnionExp Cast(Loc loc, Type *type, Type *to, Expression *e1);
-UnionExp ArrayLength(Type *type, Expression *e1);
-UnionExp Ptr(Type *type, Expression *e1);
-
-UnionExp Add(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Min(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Mul(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Div(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Mod(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Pow(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Shl(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Shr(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Ushr(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp And(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Or(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Xor(Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Index(Type *type, Expression *e1, Expression *e2);
-UnionExp Cat(Type *type, Expression *e1, Expression *e2);
-
-UnionExp Equal(TOK op, Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Cmp(TOK op, Loc loc, Type *type, Expression *e1, Expression *e2);
-UnionExp Identity(TOK op, Loc loc, Type *type, Expression *e1, Expression *e2);
-
-UnionExp Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr);
-
-// Const-folding functions used by CTFE
-
-void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, size_t firstIndex);
-void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, size_t firstIndex);
-void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, size_t firstIndex);
-
-int sliceCmpStringWithString(StringExp *se1, StringExp *se2, size_t lo1, size_t lo2, size_t len);
-int sliceCmpStringWithArray(StringExp *se1, ArrayLiteralExp *ae2, size_t lo1, size_t lo2, size_t len);
+ void accept(Visitor *v) { v->visit(this); }
+};
diff --git a/gcc/d/dmd/expressionsem.c b/gcc/d/dmd/expressionsem.c
deleted file mode 100644
index 5ae5fe6a717..00000000000
--- a/gcc/d/dmd/expressionsem.c
+++ /dev/null
@@ -1,10740 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-#include "root/root.h"
-
-#include "mars.h"
-#include "mangle.h"
-#include "mtype.h"
-#include "init.h"
-#include "expression.h"
-#include "template.h"
-#include "utf.h"
-#include "enum.h"
-#include "scope.h"
-#include "statement.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "import.h"
-#include "id.h"
-#include "dsymbol.h"
-#include "module.h"
-#include "attrib.h"
-#include "hdrgen.h"
-#include "parse.h"
-#include "nspace.h"
-#include "ctfe.h"
-#include "target.h"
-
-bool typeMerge(Scope *sc, TOK op, Type **pt, Expression **pe1, Expression **pe2);
-bool isArrayOpValid(Expression *e);
-Expression *expandVar(int result, VarDeclaration *v);
-bool checkAssignEscape(Scope *sc, Expression *e, bool gag);
-bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag);
-bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember);
-bool checkNestedRef(Dsymbol *s, Dsymbol *p);
-bool checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad, size_t istart = 0);
-bool symbolIsVisible(Module *mod, Dsymbol *s);
-bool symbolIsVisible(Scope *sc, Dsymbol *s);
-VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e);
-Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expression *e, bool alwaysCopy = false);
-Type *getTypeInfoType(Loc loc, Type *t, Scope *sc);
-char *MODtoChars(MOD mod);
-bool MODimplicitConv(MOD modfrom, MOD modto);
-MOD MODmerge(MOD mod1, MOD mod2);
-MATCH MODmethodConv(MOD modfrom, MOD modto);
-void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod);
-
-void unSpeculative(Scope *sc, RootObject *o);
-bool isDotOpDispatch(Expression *e);
-bool isNeedThisScope(Scope *sc, Declaration *d);
-bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg);
-bool isSafeCast(Expression *e, Type *tfrom, Type *tto);
-FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL);
-Expression *callCpCtor(Scope *sc, Expression *e);
-
-Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
-
-bool checkPrintfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list);
-bool checkScanfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list);
-
-/********************************************************
- * Perform semantic analysis and CTFE on expressions to produce
- * a string.
- * Params:
- * buf = append generated string to buffer
- * sc = context
- * exps = array of Expressions
- * Returns:
- * true on error
- */
-bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps)
-{
- if (!exps)
- return false;
-
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *ex = (*exps)[i];
- if (!ex)
- continue;
- Scope *sc2 = sc->startCTFE();
- Expression *e2 = expressionSemantic(ex, sc2);
- Expression *e3 = resolveProperties(sc2, e2);
- sc2->endCTFE();
-
- // allowed to contain types as well as expressions
- Expression *e4 = ctfeInterpretForPragmaMsg(e3);
- if (!e4 || e4->op == TOKerror)
- return true;
-
- // expand tuple
- if (TupleExp *te = e4->isTupleExp())
- {
- if (expressionsToString(buf, sc, te->exps))
- return true;
- continue;
- }
- // char literals exp `.toStringExp` return `null` but we cant override it
- // because in most contexts we don't want the conversion to succeed.
- IntegerExp *ie = e4->isIntegerExp();
- const TY ty = (ie && ie->type) ? ie->type->ty : (TY)Terror;
- if (ty == Tchar || ty == Twchar || ty == Tdchar)
- {
- TypeSArray *tsa = new TypeSArray(ie->type, new IntegerExp(ex->loc, 1, Type::tint32));
- e4 = new ArrayLiteralExp(ex->loc, tsa, ie);
- }
-
- if (StringExp *se = e4->toStringExp())
- buf.writestring(se->toUTF8(sc)->toPtr());
- else
- buf.writestring(e4->toChars());
- }
- return false;
-}
-
-/***********************************************************
- * Resolve `exp` as a compile-time known string.
- * Params:
- * sc = scope
- * exp = Expression which expected as a string
- * s = What the string is expected for, will be used in error diagnostic.
- * Returns:
- * String literal, or `null` if error happens.
- */
-StringExp *semanticString(Scope *sc, Expression *exp, const char *s)
-{
- sc = sc->startCTFE();
- exp = expressionSemantic(exp, sc);
- exp = resolveProperties(sc, exp);
- sc = sc->endCTFE();
-
- if (exp->op == TOKerror)
- return NULL;
-
- Expression *e = exp;
- if (exp->type->isString())
- {
- e = e->ctfeInterpret();
- if (e->op == TOKerror)
- return NULL;
- }
-
- StringExp *se = e->toStringExp();
- if (!se)
- {
- exp->error("string expected for %s, not (%s) of type %s",
- s, exp->toChars(), exp->type->toChars());
- return NULL;
- }
- return se;
-}
-
-/****************************************************************/
-
-static Expression *extractOpDollarSideEffect(Scope *sc, UnaExp *ue)
-{
- Expression *e0;
- Expression *e1 = Expression::extractLast(ue->e1, &e0);
- // Bugzilla 12585: Extract the side effect part if ue->e1 is comma.
-
- if (!isTrivialExp(e1))
- {
- /* Even if opDollar is needed, 'e1' should be evaluate only once. So
- * Rewrite:
- * e1.opIndex( ... use of $ ... )
- * e1.opSlice( ... use of $ ... )
- * as:
- * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
- * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
- */
- e1 = extractSideEffect(sc, "__dop", &e0, e1, false);
- assert(e1->op == TOKvar);
- VarExp *ve = (VarExp *)e1;
- ve->var->storage_class |= STCexptemp; // lifetime limited to expression
- }
- ue->e1 = e1;
- return e0;
-}
-
-/**************************************
- * Runs semantic on ae->arguments. Declares temporary variables
- * if '$' was used.
- */
-Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, Expression **pe0)
-{
- assert(!ae->lengthVar);
-
- *pe0 = NULL;
-
- AggregateDeclaration *ad = isAggregate(ae->e1->type);
- Dsymbol *slice = search_function(ad, Id::slice);
- //printf("slice = %s %s\n", slice->kind(), slice->toChars());
-
- for (size_t i = 0; i < ae->arguments->length; i++)
- {
- if (i == 0)
- *pe0 = extractOpDollarSideEffect(sc, ae);
-
- Expression *e = (*ae->arguments)[i];
- if (e->op == TOKinterval && !(slice && slice->isTemplateDeclaration()))
- {
- Lfallback:
- if (ae->arguments->length == 1)
- return NULL;
- ae->error("multi-dimensional slicing requires template opSlice");
- return new ErrorExp();
- }
- //printf("[%d] e = %s\n", i, e->toChars());
-
- // Create scope for '$' variable for this dimension
- ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae);
- sym->loc = ae->loc;
- sym->parent = sc->scopesym;
- sc = sc->push(sym);
- ae->lengthVar = NULL; // Create it only if required
- ae->currentDimension = i; // Dimension for $, if required
-
- e = expressionSemantic(e, sc);
- e = resolveProperties(sc, e);
-
- if (ae->lengthVar && sc->func)
- {
- // If $ was used, declare it now
- Expression *de = new DeclarationExp(ae->loc, ae->lengthVar);
- de = expressionSemantic(de, sc);
- *pe0 = Expression::combine(*pe0, de);
- }
- sc = sc->pop();
-
- if (e->op == TOKinterval)
- {
- IntervalExp *ie = (IntervalExp *)e;
-
- Objects *tiargs = new Objects();
- Expression *edim = new IntegerExp(ae->loc, i, Type::tsize_t);
- edim = expressionSemantic(edim, sc);
- tiargs->push(edim);
-
- Expressions *fargs = new Expressions();
- fargs->push(ie->lwr);
- fargs->push(ie->upr);
-
- unsigned xerrors = global.startGagging();
- sc = sc->push();
- FuncDeclaration *fslice = resolveFuncCall(ae->loc, sc, slice, tiargs, ae->e1->type, fargs, 1);
- sc = sc->pop();
- global.endGagging(xerrors);
- if (!fslice)
- goto Lfallback;
-
- e = new DotTemplateInstanceExp(ae->loc, ae->e1, slice->ident, tiargs);
- e = new CallExp(ae->loc, e, fargs);
- e = expressionSemantic(e, sc);
- }
-
- if (!e->type)
- {
- ae->error("%s has no value", e->toChars());
- e = new ErrorExp();
- }
- if (e->op == TOKerror)
- return e;
-
- (*ae->arguments)[i] = e;
- }
-
- return ae;
-}
-
-/**************************************
- * Runs semantic on se->lwr and se->upr. Declares a temporary variable
- * if '$' was used.
- */
-Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, IntervalExp *ie, Expression **pe0)
-{
- //assert(!ae->lengthVar);
- if (!ie)
- return ae;
-
- VarDeclaration *lengthVar = ae->lengthVar;
-
- // create scope for '$'
- ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae);
- sym->loc = ae->loc;
- sym->parent = sc->scopesym;
- sc = sc->push(sym);
-
- for (size_t i = 0; i < 2; ++i)
- {
- Expression *e = i == 0 ? ie->lwr : ie->upr;
- e = expressionSemantic(e, sc);
- e = resolveProperties(sc, e);
- if (!e->type)
- {
- ae->error("%s has no value", e->toChars());
- return new ErrorExp();
- }
- (i == 0 ? ie->lwr : ie->upr) = e;
- }
-
- if (lengthVar != ae->lengthVar && sc->func)
- {
- // If $ was used, declare it now
- Expression *de = new DeclarationExp(ae->loc, ae->lengthVar);
- de = expressionSemantic(de, sc);
- *pe0 = Expression::combine(*pe0, de);
- }
- sc = sc->pop();
-
- return ae;
-}
-
-/******************************
- * Perform semantic() on an array of Expressions.
- */
-
-bool arrayExpressionSemantic(Expressions *exps, Scope *sc, bool preserveErrors)
-{
- bool err = false;
- if (exps)
- {
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *e = (*exps)[i];
- if (e)
- {
- e = expressionSemantic(e, sc);
- if (e->op == TOKerror)
- err = true;
- if (preserveErrors || e->op != TOKerror)
- (*exps)[i] = e;
- }
- }
- }
- return err;
-}
-
-/******************************
- * Check the tail CallExp is really property function call.
- */
-static bool checkPropertyCall(Expression *e)
-{
- while (e->op == TOKcomma)
- e = ((CommaExp *)e)->e2;
-
- if (e->op == TOKcall)
- {
- CallExp *ce = (CallExp *)e;
- TypeFunction *tf;
- if (ce->f)
- {
- tf = (TypeFunction *)ce->f->type;
- /* If a forward reference to ce->f, try to resolve it
- */
- if (!tf->deco && ce->f->semanticRun < PASSsemanticdone)
- {
- dsymbolSemantic(ce->f, NULL);
- tf = (TypeFunction *)ce->f->type;
- }
- }
- else if (ce->e1->type->ty == Tfunction)
- tf = (TypeFunction *)ce->e1->type;
- else if (ce->e1->type->ty == Tdelegate)
- tf = (TypeFunction *)ce->e1->type->nextOf();
- else if (ce->e1->type->ty == Tpointer && ce->e1->type->nextOf()->ty == Tfunction)
- tf = (TypeFunction *)ce->e1->type->nextOf();
- else
- assert(0);
- }
- return false;
-}
-
-// TODO: merge with Scope::search::searchScopes()
-static Dsymbol *searchScopes(Scope *sc, Loc loc, Identifier *ident, int flags)
-{
- Dsymbol *s = NULL;
- for (Scope *scx = sc; scx; scx = scx->enclosing)
- {
- if (!scx->scopesym)
- continue;
- if (scx->scopesym->isModule())
- flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
- s = scx->scopesym->search(loc, ident, flags);
- if (s)
- {
- // overload set contains only module scope symbols.
- if (s->isOverloadSet())
- break;
- // selective/renamed imports also be picked up
- if (AliasDeclaration *ad = s->isAliasDeclaration())
- {
- if (ad->_import)
- break;
- }
- // See only module scope symbols for UFCS target.
- Dsymbol *p = s->toParent2();
- if (p && p->isModule())
- break;
- }
- s = NULL;
-
- // Stop when we hit a module, but keep going if that is not just under the global scope
- if (scx->scopesym->isModule() && !(scx->enclosing && !scx->enclosing->enclosing))
- break;
- }
- return s;
-}
-
-/******************************
- * Find symbol in accordance with the UFCS name look up rule
- */
-
-static Expression *searchUFCS(Scope *sc, UnaExp *ue, Identifier *ident)
-{
- //printf("searchUFCS(ident = %s)\n", ident->toChars());
- Loc loc = ue->loc;
- int flags = 0;
- Dsymbol *s = NULL;
-
- if (sc->flags & SCOPEignoresymbolvisibility)
- flags |= IgnoreSymbolVisibility;
-
- // First look in local scopes
- s = searchScopes(sc, loc, ident, flags | SearchLocalsOnly);
- if (!s)
- {
- // Second look in imported modules
- s = searchScopes(sc, loc, ident, flags | SearchImportsOnly);
- }
-
- if (!s)
- return ue->e1->type->Type::getProperty(loc, ident, 0);
-
- FuncDeclaration *f = s->isFuncDeclaration();
- if (f)
- {
- TemplateDeclaration *td = getFuncTemplateDecl(f);
- if (td)
- {
- if (td->overroot)
- td = td->overroot;
- s = td;
- }
- }
-
- if (ue->op == TOKdotti)
- {
- DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ue;
- TemplateInstance *ti = new TemplateInstance(loc, s->ident);
- ti->tiargs = dti->ti->tiargs; // for better diagnostic message
- if (!ti->updateTempDecl(sc, s))
- return new ErrorExp();
- return new ScopeExp(loc, ti);
- }
- else
- {
- //printf("-searchUFCS() %s\n", s->toChars());
- return new DsymbolExp(loc, s);
- }
-}
-
-/******************************
- * Pull out callable entity with UFCS.
- */
-
-static Expression *resolveUFCS(Scope *sc, CallExp *ce)
-{
- Loc loc = ce->loc;
- Expression *eleft;
- Expression *e;
-
- if (ce->e1->op == TOKdotid)
- {
- DotIdExp *die = (DotIdExp *)ce->e1;
- Identifier *ident = die->ident;
-
- Expression *ex = semanticX(die, sc);
- if (ex != die)
- {
- ce->e1 = ex;
- return NULL;
- }
- eleft = die->e1;
-
- Type *t = eleft->type->toBasetype();
- if (t->ty == Tarray || t->ty == Tsarray ||
- t->ty == Tnull || (t->isTypeBasic() && t->ty != Tvoid))
- {
- /* Built-in types and arrays have no callable properties, so do shortcut.
- * It is necessary in: e.init()
- */
- }
- else if (t->ty == Taarray)
- {
- if (ident == Id::remove)
- {
- /* Transform:
- * aa.remove(arg) into delete aa[arg]
- */
- if (!ce->arguments || ce->arguments->length != 1)
- {
- ce->error("expected key as argument to aa.remove()");
- return new ErrorExp();
- }
- if (!eleft->type->isMutable())
- {
- ce->error("cannot remove key from %s associative array %s",
- MODtoChars(t->mod), eleft->toChars());
- return new ErrorExp();
- }
- Expression *key = (*ce->arguments)[0];
- key = expressionSemantic(key, sc);
- key = resolveProperties(sc, key);
-
- TypeAArray *taa = (TypeAArray *)t;
- key = key->implicitCastTo(sc, taa->index);
-
- if (key->checkValue())
- return new ErrorExp();
-
- semanticTypeInfo(sc, taa->index);
-
- return new RemoveExp(loc, eleft, key);
- }
- }
- else
- {
- if (Expression *ey = semanticY(die, sc, 1))
- {
- if (ey->op == TOKerror)
- return ey;
- ce->e1 = ey;
- if (isDotOpDispatch(ey))
- {
- unsigned errors = global.startGagging();
- e = expressionSemantic(ce->syntaxCopy(), sc);
- if (!global.endGagging(errors))
- return e;
- /* fall down to UFCS */
- }
- else
- return NULL;
- }
- }
- e = searchUFCS(sc, die, ident);
- }
- else if (ce->e1->op == TOKdotti)
- {
- DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ce->e1;
- if (Expression *ey = semanticY(dti, sc, 1))
- {
- ce->e1 = ey;
- return NULL;
- }
- eleft = dti->e1;
- e = searchUFCS(sc, dti, dti->ti->name);
- }
- else
- return NULL;
-
- // Rewrite
- ce->e1 = e;
- if (!ce->arguments)
- ce->arguments = new Expressions();
- ce->arguments->shift(eleft);
-
- return NULL;
-}
-
-/******************************
- * Pull out property with UFCS.
- */
-
-static Expression *resolveUFCSProperties(Scope *sc, Expression *e1, Expression *e2 = NULL)
-{
- Loc loc = e1->loc;
- Expression *eleft;
- Expression *e;
-
- if (e1->op == TOKdotid)
- {
- DotIdExp *die = (DotIdExp *)e1;
- eleft = die->e1;
- e = searchUFCS(sc, die, die->ident);
- }
- else if (e1->op == TOKdotti)
- {
- DotTemplateInstanceExp *dti;
- dti = (DotTemplateInstanceExp *)e1;
- eleft = dti->e1;
- e = searchUFCS(sc, dti, dti->ti->name);
- }
- else
- return NULL;
-
- if (e == NULL)
- return NULL;
-
- // Rewrite
- if (e2)
- {
- // run semantic without gagging
- e2 = expressionSemantic(e2, sc);
-
- /* f(e1) = e2
- */
- Expression *ex = e->copy();
- Expressions *a1 = new Expressions();
- a1->setDim(1);
- (*a1)[0] = eleft;
- ex = new CallExp(loc, ex, a1);
- ex = trySemantic(ex, sc);
-
- /* f(e1, e2)
- */
- Expressions *a2 = new Expressions();
- a2->setDim(2);
- (*a2)[0] = eleft;
- (*a2)[1] = e2;
- e = new CallExp(loc, e, a2);
- if (ex)
- { // if fallback setter exists, gag errors
- e = trySemantic(e, sc);
- if (!e)
- { checkPropertyCall(ex);
- ex = new AssignExp(loc, ex, e2);
- return expressionSemantic(ex, sc);
- }
- }
- else
- { // strict setter prints errors if fails
- e = expressionSemantic(e, sc);
- }
- checkPropertyCall(e);
- return e;
- }
- else
- {
- /* f(e1)
- */
- Expressions *arguments = new Expressions();
- arguments->setDim(1);
- (*arguments)[0] = eleft;
- e = new CallExp(loc, e, arguments);
- e = expressionSemantic(e, sc);
- checkPropertyCall(e);
- return expressionSemantic(e, sc);
- }
-}
-
-/******************************
- * If e1 is a property function (template), resolve it.
- */
-
-Expression *resolvePropertiesOnly(Scope *sc, Expression *e1)
-{
- //printf("e1 = %s %s\n", Token::toChars(e1->op), e1->toChars());
- OverloadSet *os;
- FuncDeclaration *fd;
- TemplateDeclaration *td;
-
- if (e1->op == TOKdot)
- {
- DotExp *de = (DotExp *)e1;
- if (de->e2->op == TOKoverloadset)
- {
- os = ((OverExp *)de->e2)->vars;
- goto Los;
- }
- }
- else if (e1->op == TOKoverloadset)
- {
- os = ((OverExp *)e1)->vars;
- Los:
- assert(os);
- for (size_t i = 0; i < os->a.length; i++)
- {
- Dsymbol *s = os->a[i];
- fd = s->isFuncDeclaration();
- td = s->isTemplateDeclaration();
- if (fd)
- {
- if (((TypeFunction *)fd->type)->isproperty)
- return resolveProperties(sc, e1);
- }
- else if (td && td->onemember &&
- (fd = td->onemember->isFuncDeclaration()) != NULL)
- {
- if (((TypeFunction *)fd->type)->isproperty ||
- (fd->storage_class2 & STCproperty) ||
- (td->_scope->stc & STCproperty))
- {
- return resolveProperties(sc, e1);
- }
- }
- }
- }
- else if (e1->op == TOKdotti)
- {
- DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1;
- if (dti->ti->tempdecl && (td = dti->ti->tempdecl->isTemplateDeclaration()) != NULL)
- goto Ltd;
- }
- else if (e1->op == TOKdottd)
- {
- td = ((DotTemplateExp *)e1)->td;
- goto Ltd;
- }
- else if (e1->op == TOKscope)
- {
- Dsymbol *s = ((ScopeExp *)e1)->sds;
- TemplateInstance *ti = s->isTemplateInstance();
- if (ti && !ti->semanticRun && ti->tempdecl)
- {
- if ((td = ti->tempdecl->isTemplateDeclaration()) != NULL)
- goto Ltd;
- }
- }
- else if (e1->op == TOKtemplate)
- {
- td = ((TemplateExp *)e1)->td;
- Ltd:
- assert(td);
- if (td->onemember &&
- (fd = td->onemember->isFuncDeclaration()) != NULL)
- {
- if (((TypeFunction *)fd->type)->isproperty ||
- (fd->storage_class2 & STCproperty) ||
- (td->_scope->stc & STCproperty))
- {
- return resolveProperties(sc, e1);
- }
- }
- }
- else if (e1->op == TOKdotvar && e1->type->ty == Tfunction)
- {
- DotVarExp *dve = (DotVarExp *)e1;
- fd = dve->var->isFuncDeclaration();
- goto Lfd;
- }
- else if (e1->op == TOKvar && e1->type->ty == Tfunction &&
- (sc->intypeof || !((VarExp *)e1)->var->needThis()))
- {
- fd = ((VarExp *)e1)->var->isFuncDeclaration();
- Lfd:
- assert(fd);
- if (((TypeFunction *)fd->type)->isproperty)
- return resolveProperties(sc, e1);
- }
- return e1;
-}
-
-/*************************************************************
- * Given var, we need to get the
- * right 'this' pointer if var is in an outer class, but our
- * existing 'this' pointer is in an inner class.
- * Input:
- * e1 existing 'this'
- * ad struct or class we need the correct 'this' for
- * var the specific member of ad we're accessing
- */
-
-static Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad,
- Expression *e1, Declaration *var, int flag = 0)
-{
- //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1->toChars(), ad->toChars(), var->toChars());
- L1:
- Type *t = e1->type->toBasetype();
- //printf("e1->type = %s, var->type = %s\n", e1->type->toChars(), var->type->toChars());
-
- /* If e1 is not the 'this' pointer for ad
- */
- if (ad &&
- !(t->ty == Tpointer && t->nextOf()->ty == Tstruct &&
- ((TypeStruct *)t->nextOf())->sym == ad)
- &&
- !(t->ty == Tstruct &&
- ((TypeStruct *)t)->sym == ad)
- )
- {
- ClassDeclaration *cd = ad->isClassDeclaration();
- ClassDeclaration *tcd = t->isClassHandle();
-
- /* e1 is the right this if ad is a base class of e1
- */
- if (!cd || !tcd ||
- !(tcd == cd || cd->isBaseOf(tcd, NULL))
- )
- {
- /* Only classes can be inner classes with an 'outer'
- * member pointing to the enclosing class instance
- */
- if (tcd && tcd->isNested())
- {
- /* e1 is the 'this' pointer for an inner class: tcd.
- * Rewrite it as the 'this' pointer for the outer class.
- */
-
- e1 = new DotVarExp(loc, e1, tcd->vthis);
- e1->type = tcd->vthis->type;
- e1->type = e1->type->addMod(t->mod);
- // Do not call checkNestedRef()
- //e1 = expressionSemantic(e1, sc);
-
- // Skip up over nested functions, and get the enclosing
- // class type.
- int n = 0;
- Dsymbol *s;
- for (s = tcd->toParent();
- s && s->isFuncDeclaration();
- s = s->toParent())
- {
- FuncDeclaration *f = s->isFuncDeclaration();
- if (f->vthis)
- {
- //printf("rewriting e1 to %s's this\n", f->toChars());
- n++;
- e1 = new VarExp(loc, f->vthis);
- }
- else
- {
- e1->error("need `this` of type %s to access member %s"
- " from static function %s",
- ad->toChars(), var->toChars(), f->toChars());
- e1 = new ErrorExp();
- return e1;
- }
- }
- if (s && s->isClassDeclaration())
- {
- e1->type = s->isClassDeclaration()->type;
- e1->type = e1->type->addMod(t->mod);
- if (n > 1)
- e1 = expressionSemantic(e1, sc);
- }
- else
- e1 = expressionSemantic(e1, sc);
- goto L1;
- }
-
- /* Can't find a path from e1 to ad
- */
- if (flag)
- return NULL;
- e1->error("this for %s needs to be type %s not type %s",
- var->toChars(), ad->toChars(), t->toChars());
- return new ErrorExp();
- }
- }
- return e1;
-}
-
-/***************************************
- * Pull out any properties.
- */
-
-static Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL)
-{
- //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token::toChars(e1->op), e1->toChars(), e2 ? e2->toChars() : NULL);
- Loc loc = e1->loc;
-
- OverloadSet *os;
- Dsymbol *s;
- Objects *tiargs;
- Type *tthis;
- if (e1->op == TOKdot)
- {
- DotExp *de = (DotExp *)e1;
- if (de->e2->op == TOKoverloadset)
- {
- tiargs = NULL;
- tthis = de->e1->type;
- os = ((OverExp *)de->e2)->vars;
- goto Los;
- }
- }
- else if (e1->op == TOKoverloadset)
- {
- tiargs = NULL;
- tthis = NULL;
- os = ((OverExp *)e1)->vars;
- Los:
- assert(os);
- FuncDeclaration *fd = NULL;
- if (e2)
- {
- e2 = expressionSemantic(e2, sc);
- if (e2->op == TOKerror)
- return new ErrorExp();
- e2 = resolveProperties(sc, e2);
-
- Expressions a;
- a.push(e2);
-
- for (size_t i = 0; i < os->a.length; i++)
- {
- FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, &a, 1);
- if (f)
- {
- if (f->errors)
- return new ErrorExp();
- fd = f;
- assert(fd->type->ty == Tfunction);
- }
- }
- if (fd)
- {
- Expression *e = new CallExp(loc, e1, e2);
- return expressionSemantic(e, sc);
- }
- }
- {
- for (size_t i = 0; i < os->a.length; i++)
- {
- FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, NULL, 1);
- if (f)
- {
- if (f->errors)
- return new ErrorExp();
- fd = f;
- assert(fd->type->ty == Tfunction);
- TypeFunction *tf = (TypeFunction *)fd->type;
- if (!tf->isref && e2)
- goto Leproplvalue;
- }
- }
- if (fd)
- {
- Expression *e = new CallExp(loc, e1);
- if (e2)
- e = new AssignExp(loc, e, e2);
- return expressionSemantic(e, sc);
- }
- }
- if (e2)
- goto Leprop;
- }
- else if (e1->op == TOKdotti)
- {
- DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1;
- if (!dti->findTempDecl(sc))
- goto Leprop;
- if (!dti->ti->semanticTiargs(sc))
- goto Leprop;
- tiargs = dti->ti->tiargs;
- tthis = dti->e1->type;
- if ((os = dti->ti->tempdecl->isOverloadSet()) != NULL)
- goto Los;
- if ((s = dti->ti->tempdecl) != NULL)
- goto Lfd;
- }
- else if (e1->op == TOKdottd)
- {
- DotTemplateExp *dte = (DotTemplateExp *)e1;
- s = dte->td;
- tiargs = NULL;
- tthis = dte->e1->type;
- goto Lfd;
- }
- else if (e1->op == TOKscope)
- {
- s = ((ScopeExp *)e1)->sds;
- TemplateInstance *ti = s->isTemplateInstance();
- if (ti && !ti->semanticRun && ti->tempdecl)
- {
- //assert(ti->needsTypeInference(sc));
- if (!ti->semanticTiargs(sc))
- goto Leprop;
- tiargs = ti->tiargs;
- tthis = NULL;
- if ((os = ti->tempdecl->isOverloadSet()) != NULL)
- goto Los;
- if ((s = ti->tempdecl) != NULL)
- goto Lfd;
- }
- }
- else if (e1->op == TOKtemplate)
- {
- s = ((TemplateExp *)e1)->td;
- tiargs = NULL;
- tthis = NULL;
- goto Lfd;
- }
- else if (e1->op == TOKdotvar && e1->type && e1->type->toBasetype()->ty == Tfunction)
- {
- DotVarExp *dve = (DotVarExp *)e1;
- s = dve->var->isFuncDeclaration();
- tiargs = NULL;
- tthis = dve->e1->type;
- goto Lfd;
- }
- else if (e1->op == TOKvar && e1->type && e1->type->toBasetype()->ty == Tfunction)
- {
- s = ((VarExp *)e1)->var->isFuncDeclaration();
- tiargs = NULL;
- tthis = NULL;
- Lfd:
- assert(s);
- if (e2)
- {
- e2 = expressionSemantic(e2, sc);
- if (e2->op == TOKerror)
- return new ErrorExp();
- e2 = resolveProperties(sc, e2);
-
- Expressions a;
- a.push(e2);
-
- FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, 1);
- if (fd && fd->type)
- {
- if (fd->errors)
- return new ErrorExp();
- assert(fd->type->ty == Tfunction);
- Expression *e = new CallExp(loc, e1, e2);
- return expressionSemantic(e, sc);
- }
- }
- {
- FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, NULL, 1);
- if (fd && fd->type)
- {
- if (fd->errors)
- return new ErrorExp();
- assert(fd->type->ty == Tfunction);
- TypeFunction *tf = (TypeFunction *)fd->type;
- if (!e2 || tf->isref)
- {
- Expression *e = new CallExp(loc, e1);
- if (e2)
- e = new AssignExp(loc, e, e2);
- return expressionSemantic(e, sc);
- }
- }
- }
- if (FuncDeclaration *fd = s->isFuncDeclaration())
- {
- // Keep better diagnostic message for invalid property usage of functions
- assert(fd->type->ty == Tfunction);
- Expression *e = new CallExp(loc, e1, e2);
- return expressionSemantic(e, sc);
- }
- if (e2)
- goto Leprop;
- }
- if (e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)e1;
- VarDeclaration *v = ve->var->isVarDeclaration();
- if (v && ve->checkPurity(sc, v))
- return new ErrorExp();
- }
- if (e2)
- return NULL;
-
- if (e1->type &&
- e1->op != TOKtype) // function type is not a property
- {
- /* Look for e1 being a lazy parameter; rewrite as delegate call
- */
- if (e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)e1;
-
- if (ve->var->storage_class & STClazy)
- {
- Expression *e = new CallExp(loc, e1);
- return expressionSemantic(e, sc);
- }
- }
- else if (e1->op == TOKdotvar)
- {
- // Check for reading overlapped pointer field in @safe code.
- if (checkUnsafeAccess(sc, e1, true, true))
- return new ErrorExp();
- }
- else if (e1->op == TOKcall)
- {
- CallExp *ce = (CallExp *)e1;
- // Check for reading overlapped pointer field in @safe code.
- if (checkUnsafeAccess(sc, ce->e1, true, true))
- return new ErrorExp();
- }
- }
-
- if (!e1->type)
- {
- error(loc, "cannot resolve type for %s", e1->toChars());
- e1 = new ErrorExp();
- }
- return e1;
-
-Leprop:
- error(loc, "not a property %s", e1->toChars());
- return new ErrorExp();
-
-Leproplvalue:
- error(loc, "%s is not an lvalue", e1->toChars());
- return new ErrorExp();
-}
-
-Expression *resolveProperties(Scope *sc, Expression *e)
-{
- //printf("resolveProperties(%s)\n", e->toChars());
-
- e = resolvePropertiesX(sc, e);
- if (e->checkRightThis(sc))
- return new ErrorExp();
- return e;
-}
-
-/****************************************
- * The common type is determined by applying ?: to each pair.
- * Output:
- * exps[] properties resolved, implicitly cast to common type, rewritten in place
- * *pt if pt is not NULL, set to the common type
- * Returns:
- * true a semantic error was detected
- */
-
-static bool arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt)
-{
- /* Still have a problem with:
- * ubyte[][] = [ cast(ubyte[])"hello", [1]];
- * which works if the array literal is initialized top down with the ubyte[][]
- * type, but fails with this function doing bottom up typing.
- */
- //printf("arrayExpressionToCommonType()\n");
- IntegerExp integerexp(0);
- CondExp condexp(Loc(), &integerexp, NULL, NULL);
-
- Type *t0 = NULL;
- Expression *e0 = NULL; // dead-store to prevent spurious warning
- size_t j0 = ~0; // dead-store to prevent spurious warning
- bool foundType = false;
-
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *e = (*exps)[i];
- if (!e)
- continue;
-
- e = resolveProperties(sc, e);
- if (!e->type)
- {
- e->error("%s has no value", e->toChars());
- t0 = Type::terror;
- continue;
- }
- if (e->op == TOKtype)
- {
- foundType = true; // do not break immediately, there might be more errors
- e->checkValue(); // report an error "type T has no value"
- t0 = Type::terror;
- continue;
- }
- if (e->type->ty == Tvoid)
- {
- // void expressions do not concur to the determination of the common
- // type.
- continue;
- }
- if (checkNonAssignmentArrayOp(e))
- {
- t0 = Type::terror;
- continue;
- }
-
- e = doCopyOrMove(sc, e);
-
- if (!foundType && t0 && !t0->equals(e->type))
- {
- /* This applies ?: to merge the types. It's backwards;
- * ?: should call this function to merge types.
- */
- condexp.type = NULL;
- condexp.e1 = e0;
- condexp.e2 = e;
- condexp.loc = e->loc;
- Expression *ex = expressionSemantic(&condexp, sc);
- if (ex->op == TOKerror)
- e = ex;
- else
- {
- (*exps)[j0] = condexp.e1;
- e = condexp.e2;
- }
- }
- j0 = i;
- e0 = e;
- t0 = e->type;
- if (e->op != TOKerror)
- (*exps)[i] = e;
- }
-
- if (!t0)
- t0 = Type::tvoid; // [] is typed as void[]
- else if (t0->ty != Terror)
- {
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *e = (*exps)[i];
- if (!e)
- continue;
-
- e = e->implicitCastTo(sc, t0);
- //assert(e->op != TOKerror);
- if (e->op == TOKerror)
- {
- /* Bugzilla 13024: a workaround for the bug in typeMerge -
- * it should paint e1 and e2 by deduced common type,
- * but doesn't in this particular case.
- */
- t0 = Type::terror;
- break;
- }
- (*exps)[i] = e;
- }
- }
- if (pt)
- *pt = t0;
-
- return (t0 == Type::terror);
-}
-
-static Expression *opAssignToOp(Loc loc, TOK op, Expression *e1, Expression *e2)
-{ Expression *e;
-
- switch (op)
- {
- case TOKaddass: e = new AddExp(loc, e1, e2); break;
- case TOKminass: e = new MinExp(loc, e1, e2); break;
- case TOKmulass: e = new MulExp(loc, e1, e2); break;
- case TOKdivass: e = new DivExp(loc, e1, e2); break;
- case TOKmodass: e = new ModExp(loc, e1, e2); break;
- case TOKandass: e = new AndExp(loc, e1, e2); break;
- case TOKorass: e = new OrExp (loc, e1, e2); break;
- case TOKxorass: e = new XorExp(loc, e1, e2); break;
- case TOKshlass: e = new ShlExp(loc, e1, e2); break;
- case TOKshrass: e = new ShrExp(loc, e1, e2); break;
- case TOKushrass: e = new UshrExp(loc, e1, e2); break;
- default: assert(0);
- }
- return e;
-}
-
-/*********************
- * Rewrite:
- * array.length op= e2
- * as:
- * array.length = array.length op e2
- * or:
- * auto tmp = &array;
- * (*tmp).length = (*tmp).length op e2
- */
-
-static Expression *rewriteOpAssign(BinExp *exp)
-{
- Expression *e;
-
- assert(exp->e1->op == TOKarraylength);
- ArrayLengthExp *ale = (ArrayLengthExp *)exp->e1;
- if (ale->e1->op == TOKvar)
- {
- e = opAssignToOp(exp->loc, exp->op, ale, exp->e2);
- e = new AssignExp(exp->loc, ale->syntaxCopy(), e);
- }
- else
- {
- /* auto tmp = &array;
- * (*tmp).length = (*tmp).length op e2
- */
- VarDeclaration *tmp = copyToTemp(0, "__arraylength", new AddrExp(ale->loc, ale->e1));
-
- Expression *e1 = new ArrayLengthExp(ale->loc, new PtrExp(ale->loc, new VarExp(ale->loc, tmp)));
- Expression *elvalue = e1->syntaxCopy();
- e = opAssignToOp(exp->loc, exp->op, e1, exp->e2);
- e = new AssignExp(exp->loc, elvalue, e);
- e = new CommaExp(exp->loc, new DeclarationExp(ale->loc, tmp), e);
- }
- return e;
-}
-
-/****************************************
- * Preprocess arguments to function.
- * Output:
- * exps[] tuples expanded, properties resolved, rewritten in place
- * Returns:
- * true a semantic error occurred
- */
-
-static bool preFunctionParameters(Scope *sc, Expressions *exps)
-{
- bool err = false;
- if (exps)
- {
- expandTuples(exps);
-
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *arg = (*exps)[i];
-
- arg = resolveProperties(sc, arg);
- if (arg->op == TOKtype)
- {
- arg->error("cannot pass type %s as a function argument", arg->toChars());
- arg = new ErrorExp();
- err = true;
- }
- else if (arg->type->toBasetype()->ty == Tfunction)
- {
- arg->error("cannot pass type %s as a function argument", arg->toChars());
- arg = new ErrorExp();
- err = true;
- }
- else if (checkNonAssignmentArrayOp(arg))
- {
- arg = new ErrorExp();
- err = true;
- }
- (*exps)[i] = arg;
- }
- }
- return err;
-}
-
-/********************************************
- * Issue an error if default construction is disabled for type t.
- * Default construction is required for arrays and 'out' parameters.
- * Returns:
- * true an error was issued
- */
-static bool checkDefCtor(Loc loc, Type *t)
-{
- t = t->baseElemOf();
- if (t->ty == Tstruct)
- {
- StructDeclaration *sd = ((TypeStruct *)t)->sym;
- if (sd->noDefaultCtor)
- {
- sd->error(loc, "default construction is disabled");
- return true;
- }
- }
- return false;
-}
-
-/****************************************
- * Now that we know the exact type of the function we're calling,
- * the arguments[] need to be adjusted:
- * 1. implicitly convert argument to the corresponding parameter type
- * 2. add default arguments for any missing arguments
- * 3. do default promotions on arguments corresponding to ...
- * 4. add hidden _arguments[] argument
- * 5. call copy constructor for struct value arguments
- * Input:
- * tf type of the function
- * fd the function being called, NULL if called indirectly
- * Output:
- * *prettype return type of function
- * *peprefix expression to execute before arguments[] are evaluated, NULL if none
- * Returns:
- * true errors happened
- */
-
-static bool functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
- Type *tthis, Expressions *arguments, FuncDeclaration *fd, Type **prettype, Expression **peprefix)
-{
- //printf("functionParameters()\n");
- assert(arguments);
- assert(fd || tf->next);
- size_t nargs = arguments ? arguments->length : 0;
- size_t nparams = tf->parameterList.length();
- unsigned olderrors = global.errors;
- bool err = false;
- *prettype = Type::terror;
- Expression *eprefix = NULL;
- *peprefix = NULL;
-
- if (nargs > nparams && tf->parameterList.varargs == VARARGnone)
- {
- error(loc, "expected %llu arguments, not %llu for non-variadic function type %s", (ulonglong)nparams, (ulonglong)nargs, tf->toChars());
- return true;
- }
-
- // If inferring return type, and semantic3() needs to be run if not already run
- if (!tf->next && fd->inferRetType)
- {
- fd->functionSemantic();
- }
- else if (fd && fd->parent)
- {
- TemplateInstance *ti = fd->parent->isTemplateInstance();
- if (ti && ti->tempdecl)
- {
- fd->functionSemantic3();
- }
- }
- bool isCtorCall = fd && fd->needThis() && fd->isCtorDeclaration();
-
- size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams)
-
- /* If the function return type has wildcards in it, we'll need to figure out the actual type
- * based on the actual argument types.
- */
- MOD wildmatch = 0;
- if (tthis && tf->isWild() && !isCtorCall)
- {
- Type *t = tthis;
- if (t->isImmutable())
- wildmatch = MODimmutable;
- else if (t->isWildConst())
- wildmatch = MODwildconst;
- else if (t->isWild())
- wildmatch = MODwild;
- else if (t->isConst())
- wildmatch = MODconst;
- else
- wildmatch = MODmutable;
- }
-
- int done = 0;
- for (size_t i = 0; i < n; i++)
- {
- Expression *arg;
-
- if (i < nargs)
- arg = (*arguments)[i];
- else
- arg = NULL;
-
- if (i < nparams)
- {
- Parameter *p = tf->parameterList[i];
-
- if (!arg)
- {
- if (!p->defaultArg)
- {
- if (tf->parameterList.varargs == VARARGtypesafe && i + 1 == nparams)
- goto L2;
- error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs);
- return true;
- }
- arg = p->defaultArg;
- arg = inlineCopy(arg, sc);
- // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__
- arg = arg->resolveLoc(loc, sc);
- arguments->push(arg);
- nargs++;
- }
-
- if (tf->parameterList.varargs == VARARGtypesafe && i + 1 == nparams)
- {
- //printf("\t\tvarargs == 2, p->type = '%s'\n", p->type->toChars());
- {
- MATCH m;
- if ((m = arg->implicitConvTo(p->type)) > MATCHnomatch)
- {
- if (p->type->nextOf() && arg->implicitConvTo(p->type->nextOf()) >= m)
- goto L2;
- else if (nargs != nparams)
- { error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs);
- return true;
- }
- goto L1;
- }
- }
- L2:
- Type *tb = p->type->toBasetype();
- Type *tret = p->isLazyArray();
- switch (tb->ty)
- {
- case Tsarray:
- case Tarray:
- {
- /* Create a static array variable v of type arg->type:
- * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ];
- *
- * The array literal in the initializer of the hidden variable
- * is now optimized. See Bugzilla 2356.
- */
- Type *tbn = ((TypeArray *)tb)->next;
-
- Expressions *elements = new Expressions();
- elements->setDim(nargs - i);
- for (size_t u = 0; u < elements->length; u++)
- {
- Expression *a = (*arguments)[i + u];
- if (tret && a->implicitConvTo(tret))
- {
- a = a->implicitCastTo(sc, tret);
- a = a->optimize(WANTvalue);
- a = toDelegate(a, a->type, sc);
- }
- else
- a = a->implicitCastTo(sc, tbn);
- (*elements)[u] = a;
- }
- // Bugzilla 14395: Convert to a static array literal, or its slice.
- arg = new ArrayLiteralExp(loc, tbn->sarrayOf(nargs - i), elements);
- if (tb->ty == Tarray)
- {
- arg = new SliceExp(loc, arg, NULL, NULL);
- arg->type = p->type;
- }
- break;
- }
- case Tclass:
- {
- /* Set arg to be:
- * new Tclass(arg0, arg1, ..., argn)
- */
- Expressions *args = new Expressions();
- args->setDim(nargs - i);
- for (size_t u = i; u < nargs; u++)
- (*args)[u - i] = (*arguments)[u];
- arg = new NewExp(loc, NULL, NULL, p->type, args);
- break;
- }
- default:
- if (!arg)
- {
- error(loc, "not enough arguments");
- return true;
- }
- break;
- }
- arg = expressionSemantic(arg, sc);
- //printf("\targ = '%s'\n", arg->toChars());
- arguments->setDim(i + 1);
- (*arguments)[i] = arg;
- nargs = i + 1;
- done = 1;
- }
-
- L1:
- if (!(p->storageClass & STClazy && p->type->ty == Tvoid))
- {
- bool isRef = (p->storageClass & (STCref | STCout)) != 0;
- if (unsigned char wm = arg->type->deduceWild(p->type, isRef))
- {
- if (wildmatch)
- wildmatch = MODmerge(wildmatch, wm);
- else
- wildmatch = wm;
- //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p->type->toChars(), arg->type->toChars(), wm, wildmatch);
- }
- }
- }
- if (done)
- break;
- }
- if ((wildmatch == MODmutable || wildmatch == MODimmutable) &&
- tf->next->hasWild() &&
- (tf->isref || !tf->next->implicitConvTo(tf->next->immutableOf())))
- {
- if (fd)
- {
- /* If the called function may return the reference to
- * outer inout data, it should be rejected.
- *
- * void foo(ref inout(int) x) {
- * ref inout(int) bar(inout(int)) { return x; }
- * struct S { ref inout(int) bar() inout { return x; } }
- * bar(int.init) = 1; // bad!
- * S().bar() = 1; // bad!
- * }
- */
- Dsymbol *s = NULL;
- if (fd->isThis() || fd->isNested())
- s = fd->toParent2();
- for (; s; s = s->toParent2())
- {
- if (AggregateDeclaration *ad = s->isAggregateDeclaration())
- {
- if (ad->isNested())
- continue;
- break;
- }
- if (FuncDeclaration *ff = s->isFuncDeclaration())
- {
- if (((TypeFunction *)ff->type)->iswild)
- goto Linouterr;
-
- if (ff->isNested() || ff->isThis())
- continue;
- }
- break;
- }
- }
- else if (tf->isWild())
- {
- Linouterr:
- const char *s = wildmatch == MODmutable ? "mutable" : MODtoChars(wildmatch);
- error(loc, "modify inout to %s is not allowed inside inout function", s);
- return true;
- }
- }
-
- assert(nargs >= nparams);
- for (size_t i = 0; i < nargs; i++)
- {
- Expression *arg = (*arguments)[i];
- assert(arg);
- if (i < nparams)
- {
- Parameter *p = tf->parameterList[i];
-
- if (!(p->storageClass & STClazy && p->type->ty == Tvoid))
- {
- Type *tprm = p->type;
- if (p->type->hasWild())
- tprm = p->type->substWildTo(wildmatch);
- if (!tprm->equals(arg->type))
- {
- //printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars());
- arg = arg->implicitCastTo(sc, tprm);
- arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0);
- }
- }
- if (p->storageClass & STCref)
- {
- arg = arg->toLvalue(sc, arg);
-
- // Look for mutable misaligned pointer, etc., in @safe mode
- err |= checkUnsafeAccess(sc, arg, false, true);
- }
- else if (p->storageClass & STCout)
- {
- Type *t = arg->type;
- if (!t->isMutable() || !t->isAssignable()) // check blit assignable
- {
- arg->error("cannot modify struct %s with immutable members", arg->toChars());
- err = true;
- }
- else
- {
- // Look for misaligned pointer, etc., in @safe mode
- err |= checkUnsafeAccess(sc, arg, false, true);
- err |= checkDefCtor(arg->loc, t); // t must be default constructible
- }
- arg = arg->toLvalue(sc, arg);
- }
- else if (p->storageClass & STClazy)
- {
- // Convert lazy argument to a delegate
- if (p->type->ty == Tvoid)
- arg = toDelegate(arg, p->type, sc);
- else
- arg = toDelegate(arg, arg->type, sc);
- }
-
- //printf("arg: %s\n", arg->toChars());
- //printf("type: %s\n", arg->type->toChars());
- if (tf->parameterEscapes(p))
- {
- /* Argument value can escape from the called function.
- * Check arg to see if it matters.
- */
- if (global.params.vsafe)
- err |= checkParamArgumentEscape(sc, fd, p->ident, arg, false);
- }
- else
- {
- /* Argument value cannot escape from the called function.
- */
- Expression *a = arg;
- if (a->op == TOKcast)
- a = ((CastExp *)a)->e1;
-
- if (a->op == TOKfunction)
- {
- /* Function literals can only appear once, so if this
- * appearance was scoped, there cannot be any others.
- */
- FuncExp *fe = (FuncExp *)a;
- fe->fd->tookAddressOf = 0;
- }
- else if (a->op == TOKdelegate)
- {
- /* For passing a delegate to a scoped parameter,
- * this doesn't count as taking the address of it.
- * We only worry about 'escaping' references to the function.
- */
- DelegateExp *de = (DelegateExp *)a;
- if (de->e1->op == TOKvar)
- { VarExp *ve = (VarExp *)de->e1;
- FuncDeclaration *f = ve->var->isFuncDeclaration();
- if (f)
- { f->tookAddressOf--;
- //printf("tookAddressOf = %d\n", f->tookAddressOf);
- }
- }
- }
- }
- arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0);
- }
- else
- {
- // These will be the trailing ... arguments
-
- // If not D linkage, do promotions
- if (tf->linkage != LINKd)
- {
- // Promote bytes, words, etc., to ints
- arg = integralPromotions(arg, sc);
-
- // Promote floats to doubles
- switch (arg->type->ty)
- {
- case Tfloat32:
- arg = arg->castTo(sc, Type::tfloat64);
- break;
-
- case Timaginary32:
- arg = arg->castTo(sc, Type::timaginary64);
- break;
- }
-
- if (tf->parameterList.varargs == VARARGvariadic)
- {
- const char *p = tf->linkage == LINKc ? "extern(C)" : "extern(C++)";
- if (arg->type->ty == Tarray)
- {
- arg->error("cannot pass dynamic arrays to %s vararg functions", p);
- err = true;
- }
- if (arg->type->ty == Tsarray)
- {
- arg->error("cannot pass static arrays to %s vararg functions", p);
- err = true;
- }
- }
- }
-
- // Do not allow types that need destructors
- if (arg->type->needsDestruction())
- {
- arg->error("cannot pass types that need destruction as variadic arguments");
- err = true;
- }
-
- // Convert static arrays to dynamic arrays
- // BUG: I don't think this is right for D2
- Type *tb = arg->type->toBasetype();
- if (tb->ty == Tsarray)
- {
- TypeSArray *ts = (TypeSArray *)tb;
- Type *ta = ts->next->arrayOf();
- if (ts->size(arg->loc) == 0)
- arg = new NullExp(arg->loc, ta);
- else
- arg = arg->castTo(sc, ta);
- }
- if (tb->ty == Tstruct)
- {
- //arg = callCpCtor(sc, arg);
- }
-
- // Give error for overloaded function addresses
- if (arg->op == TOKsymoff)
- { SymOffExp *se = (SymOffExp *)arg;
- if (se->hasOverloads &&
- !se->var->isFuncDeclaration()->isUnique())
- { arg->error("function %s is overloaded", arg->toChars());
- err = true;
- }
- }
- if (arg->checkValue())
- err = true;
- arg = arg->optimize(WANTvalue);
- }
- (*arguments)[i] = arg;
- }
-
- /* If calling C scanf(), printf(), or any variants, check the format string against the arguments
- */
- const bool isVa_list = tf->parameterList.varargs == VARARGnone;
- if (fd && (fd->flags & FUNCFLAGprintf))
- {
- if (StringExp *se = (*arguments)[nparams - 1 - isVa_list]->isStringExp())
- {
- Expressions argslice;
- argslice.reserve(nargs - nparams);
- for (size_t i = nparams; i < nargs; i++)
- argslice.push((*arguments)[i]);
- checkPrintfFormat(se->loc, se->toPtr(), argslice, isVa_list);
- }
- }
- else if (fd && (fd->flags & FUNCFLAGscanf))
- {
- if (StringExp *se = (*arguments)[nparams - 1 - isVa_list]->isStringExp())
- {
- Expressions argslice;
- argslice.reserve(nargs - nparams);
- for (size_t i = nparams; i < nargs; i++)
- argslice.push((*arguments)[i]);
- checkPrintfFormat(se->loc, se->toPtr(), argslice, isVa_list);
- }
- }
-
- /* Remaining problems:
- * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is
- * implemented by calling a function) we'll defer this for now.
- * 2. value structs (or static arrays of them) that need to be copy constructed
- * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the
- * function gets called (functions normally destroy their parameters)
- * 2 and 3 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned
- * up properly. Pushing arguments on the stack then cannot fail.
- */
- if (1)
- {
- /* TODO: tackle problem 1)
- */
- const bool leftToRight = true; // TODO: something like !fd.isArrayOp
- if (!leftToRight)
- assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity
-
- const ptrdiff_t start = (leftToRight ? 0 : (ptrdiff_t)nargs - 1);
- const ptrdiff_t end = (leftToRight ? (ptrdiff_t)nargs : -1);
- const ptrdiff_t step = (leftToRight ? 1 : -1);
-
- /* Compute indices of last throwing argument and first arg needing destruction.
- * Used to not set up destructors unless an arg needs destruction on a throw
- * in a later argument.
- */
- ptrdiff_t lastthrow = -1;
- ptrdiff_t firstdtor = -1;
- for (ptrdiff_t i = start; i != end; i += step)
- {
- Expression *arg = (*arguments)[i];
- if (canThrow(arg, sc->func, false))
- lastthrow = i;
- if (firstdtor == -1 && arg->type->needsDestruction())
- {
- Parameter *p = (i >= (ptrdiff_t)nparams ? NULL : tf->parameterList[i]);
- if (!(p && (p->storageClass & (STClazy | STCref | STCout))))
- firstdtor = i;
- }
- }
-
- /* Does problem 3) apply to this call?
- */
- const bool needsPrefix = (firstdtor >= 0 && lastthrow >= 0
- && (lastthrow - firstdtor) * step > 0);
-
- /* If so, initialize 'eprefix' by declaring the gate
- */
- VarDeclaration *gate = NULL;
- if (needsPrefix)
- {
- // eprefix => bool __gate [= false]
- Identifier *idtmp = Identifier::generateId("__gate");
- gate = new VarDeclaration(loc, Type::tbool, idtmp, NULL);
- gate->storage_class |= STCtemp | STCctfe | STCvolatile;
- dsymbolSemantic(gate, sc);
-
- Expression *ae = new DeclarationExp(loc, gate);
- eprefix = expressionSemantic(ae, sc);
- }
-
- for (ptrdiff_t i = start; i != end; i += step)
- {
- Expression *arg = (*arguments)[i];
-
- Parameter *parameter = (i >= (ptrdiff_t)nparams ? NULL : tf->parameterList[i]);
- const bool isRef = (parameter && (parameter->storageClass & (STCref | STCout)));
- const bool isLazy = (parameter && (parameter->storageClass & STClazy));
-
- /* Skip lazy parameters
- */
- if (isLazy)
- continue;
-
- /* Do we have a gate? Then we have a prefix and we're not yet past the last throwing arg.
- * Declare a temporary variable for this arg and append that declaration to 'eprefix',
- * which will implicitly take care of potential problem 2) for this arg.
- * 'eprefix' will therefore finally contain all args up to and including the last
- * potentially throwing arg, excluding all lazy parameters.
- */
- if (gate)
- {
- const bool needsDtor = (!isRef && arg->type->needsDestruction() && i != lastthrow);
-
- /* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor)
- */
- VarDeclaration *tmp = copyToTemp(0,
- needsDtor ? "__pfx" : "__pfy",
- !isRef ? arg : arg->addressOf());
- dsymbolSemantic(tmp, sc);
-
- /* Modify the destructor so it only runs if gate==false, i.e.,
- * only if there was a throw while constructing the args
- */
- if (!needsDtor)
- {
- if (tmp->edtor)
- {
- assert(i == lastthrow);
- tmp->edtor = NULL;
- }
- }
- else
- {
- // edtor => (__gate || edtor)
- assert(tmp->edtor);
- Expression *e = tmp->edtor;
- e = new LogicalExp(e->loc, TOKoror, new VarExp(e->loc, gate), e);
- tmp->edtor = expressionSemantic(e, sc);
- //printf("edtor: %s\n", tmp->edtor->toChars());
- }
-
- // eprefix => (eprefix, auto __pfx/y = arg)
- DeclarationExp *ae = new DeclarationExp(loc, tmp);
- eprefix = Expression::combine(eprefix, expressionSemantic(ae, sc));
-
- // arg => __pfx/y
- arg = new VarExp(loc, tmp);
- arg = expressionSemantic(arg, sc);
- if (isRef)
- {
- arg = new PtrExp(loc, arg);
- arg = expressionSemantic(arg, sc);
- }
-
- /* Last throwing arg? Then finalize eprefix => (eprefix, gate = true),
- * i.e., disable the dtors right after constructing the last throwing arg.
- * From now on, the callee will take care of destructing the args because
- * the args are implicitly moved into function parameters.
- *
- * Set gate to null to let the next iterations know they don't need to
- * append to eprefix anymore.
- */
- if (i == lastthrow)
- {
- Expression *e = new AssignExp(gate->loc, new VarExp(gate->loc, gate), new IntegerExp(gate->loc, 1, Type::tbool));
- eprefix = Expression::combine(eprefix, expressionSemantic(e, sc));
- gate = NULL;
- }
- }
- else
- {
- /* No gate, no prefix to append to.
- * Handle problem 2) by calling the copy constructor for value structs
- * (or static arrays of them) if appropriate.
- */
- Type *tv = arg->type->baseElemOf();
- if (!isRef && tv->ty == Tstruct)
- arg = doCopyOrMove(sc, arg);
- }
-
- (*arguments)[i] = arg;
- }
- }
- //if (eprefix) printf("eprefix: %s\n", eprefix->toChars());
-
- // If D linkage and variadic, add _arguments[] as first argument
- if (tf->isDstyleVariadic())
- {
- assert(arguments->length >= nparams);
-
- Parameters *args = new Parameters;
- args->setDim(arguments->length - nparams);
- for (size_t i = 0; i < arguments->length - nparams; i++)
- {
- Parameter *arg = new Parameter(STCin, (*arguments)[nparams + i]->type, NULL, NULL, NULL);
- (*args)[i] = arg;
- }
-
- TypeTuple *tup = new TypeTuple(args);
- Expression *e = new TypeidExp(loc, tup);
- e = expressionSemantic(e, sc);
- arguments->insert(0, e);
- }
-
- Type *tret = tf->next;
- if (isCtorCall)
- {
- //printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd->toChars(), fd->type->toChars(),
- // wildmatch, tf->isWild(), fd->isolateReturn());
- if (!tthis)
- {
- assert(sc->intypeof || global.errors);
- tthis = fd->isThis()->type->addMod(fd->type->mod);
- }
- if (tf->isWild() && !fd->isolateReturn())
- {
- if (wildmatch)
- tret = tret->substWildTo(wildmatch);
- int offset;
- if (!tret->implicitConvTo(tthis) &&
- !(MODimplicitConv(tret->mod, tthis->mod) && tret->isBaseOf(tthis, &offset) && offset == 0))
- {
- const char* s1 = tret ->isNaked() ? " mutable" : tret ->modToChars();
- const char* s2 = tthis->isNaked() ? " mutable" : tthis->modToChars();
- ::error(loc, "inout constructor %s creates%s object, not%s",
- fd->toPrettyChars(), s1, s2);
- err = true;
- }
- }
- tret = tthis;
- }
- else if (wildmatch && tret)
- {
- /* Adjust function return type based on wildmatch
- */
- //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret->toChars());
- tret = tret->substWildTo(wildmatch);
- }
- *prettype = tret;
- *peprefix = eprefix;
- return (err || olderrors != global.errors);
-}
-
-/**
- * Determines whether a symbol represents a module or package
- * (Used as a helper for is(type == module) and is(type == package))
- *
- * Params:
- * sym = the symbol to be checked
- *
- * Returns:
- * the symbol which `sym` represents (or `null` if it doesn't represent a `Package`)
- */
-Package *resolveIsPackage(Dsymbol *sym)
-{
- Package *pkg;
- if (Import *imp = sym->isImport())
- {
- if (imp->pkg == NULL)
- {
- error(sym->loc, "Internal Compiler Error: unable to process forward-referenced import `%s`",
- imp->toChars());
- assert(0);
- }
- pkg = imp->pkg;
- }
- else if (Module *mod = sym->isModule())
- pkg = mod->isPackageFile ? mod->pkg : sym->isPackage();
- else
- pkg = sym->isPackage();
- if (pkg)
- pkg->resolvePKGunknown();
- return pkg;
-}
-
-static Module *loadStdMath()
-{
- static Import *impStdMath = NULL;
- if (!impStdMath)
- {
- Identifiers *a = new Identifiers();
- a->push(Id::std);
- Import *s = new Import(Loc(), a, Id::math, NULL, false);
- s->load(NULL);
- if (s->mod)
- {
- s->mod->importAll(NULL);
- dsymbolSemantic(s->mod, NULL);
- }
- impStdMath = s;
- }
- return impStdMath->mod;
-}
-
-class ExpressionSemanticVisitor : public Visitor
-{
-public:
- Expression *result;
- Scope *sc;
-
- ExpressionSemanticVisitor(Scope *sc)
- {
- this->result = NULL;
- this->sc = sc;
- }
-
-private:
- void setError()
- {
- result = new ErrorExp();
- }
-
- /*********************
- * Mark the operand as will never be dereferenced,
- * which is useful info for @safe checks.
- * Do before semantic() on operands rewrites them.
- */
- static void setNoderefOperand(UnaExp *e)
- {
- if (e->e1->op == TOKdotid)
- ((DotIdExp *)e->e1)->noderef = true;
- }
-
- /*********************
- * Mark the operands as will never be dereferenced,
- * which is useful info for @safe checks.
- * Do before semantic() on operands rewrites them.
- */
- static void setNoderefOperands(BinExp *e)
- {
- if (e->e1->op == TOKdotid)
- ((DotIdExp *)e->e1)->noderef = true;
- if (e->e2->op == TOKdotid)
- ((DotIdExp *)e->e2)->noderef = true;
- }
-
- static FuncDeclaration *resolveOverloadSet(Loc loc, Scope *sc,
- OverloadSet *os, Objects* tiargs, Type *tthis, Expressions *arguments)
- {
- FuncDeclaration *f = NULL;
- for (size_t i = 0; i < os->a.length; i++)
- {
- Dsymbol *s = os->a[i];
- if (tiargs && s->isFuncDeclaration())
- continue;
- if (FuncDeclaration *f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, arguments, 1))
- {
- if (f2->errors)
- return NULL;
- if (f)
- {
- /* Error if match in more than one overload set,
- * even if one is a 'better' match than the other.
- */
- ScopeDsymbol::multiplyDefined(loc, f, f2);
- }
- else
- f = f2;
- }
- }
- if (!f)
- ::error(loc, "no overload matches for %s", os->toChars());
- else if (f->errors)
- f = NULL;
- return f;
- }
-
- /****************************************************
- * Determine if `exp`, which takes the address of `v`, can do so safely.
- * Params:
- * sc = context
- * exp = expression that takes the address of `v`
- * v = the variable getting its address taken
- * Returns:
- * `true` if ok, `false` for error
- */
- static bool checkAddressVar(Scope *sc, UnaExp *e, VarDeclaration *v)
- {
- if (v)
- {
- if (!v->canTakeAddressOf())
- {
- e->error("cannot take address of %s", e->e1->toChars());
- return false;
- }
- if (sc->func && !sc->intypeof && !v->isDataseg())
- {
- const char *p = v->isParameter() ? "parameter" : "local";
- if (global.params.vsafe)
- {
- // Taking the address of v means it cannot be set to 'scope' later
- v->storage_class &= ~STCmaybescope;
- v->doNotInferScope = true;
- if (v->storage_class & STCscope && sc->func->setUnsafe())
- {
- e->error("cannot take address of scope %s %s in @safe function %s", p, v->toChars(), sc->func->toChars());
- return false;
- }
- }
- else if (sc->func->setUnsafe())
- {
- e->error("cannot take address of %s %s in @safe function %s", p, v->toChars(), sc->func->toChars());
- return false;
- }
- }
- }
- return true;
- }
-
- static bool checkVectorElem(Expression *e, Expression *elem)
- {
- if (elem->isConst() == 1)
- return false;
-
- e->error("constant expression expected, not %s", elem->toChars());
- return true;
- }
-
-public:
- void visit(Expression *e)
- {
- if (e->type)
- e->type = typeSemantic(e->type, e->loc, sc);
- else
- e->type = Type::tvoid;
- result = e;
- }
-
- void visit(IntegerExp *e)
- {
- assert(e->type);
- if (e->type->ty == Terror)
- return setError();
- assert(e->type->deco);
- e->normalize();
- result = e;
- }
-
- void visit(RealExp *e)
- {
- if (!e->type)
- e->type = Type::tfloat64;
- else
- e->type = typeSemantic(e->type, e->loc, sc);
- result = e;
- }
-
- void visit(ComplexExp *e)
- {
- if (!e->type)
- e->type = Type::tcomplex80;
- else
- e->type = typeSemantic(e->type, e->loc, sc);
- result = e;
- }
-
- void visit(IdentifierExp *exp)
- {
- if (exp->type) // This is used as the dummy expression
- {
- result = exp;
- return;
- }
-
- Dsymbol *scopesym;
- Dsymbol *s = sc->search(exp->loc, exp->ident, &scopesym);
- if (s)
- {
- if (s->errors)
- return setError();
-
- Expression *e;
-
- /* See if the symbol was a member of an enclosing 'with'
- */
- WithScopeSymbol *withsym = scopesym->isWithScopeSymbol();
- if (withsym && withsym->withstate->wthis && symbolIsVisible(sc, s))
- {
- /* Disallow shadowing
- */
- // First find the scope of the with
- Scope *scwith = sc;
- while (scwith->scopesym != scopesym)
- {
- scwith = scwith->enclosing;
- assert(scwith);
- }
- // Look at enclosing scopes for symbols with the same name,
- // in the same function
- for (Scope *scx = scwith; scx && scx->func == scwith->func; scx = scx->enclosing)
- {
- Dsymbol *s2;
- if (scx->scopesym && scx->scopesym->symtab &&
- (s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL &&
- s != s2)
- {
- exp->error("with symbol %s is shadowing local symbol %s", s->toPrettyChars(), s2->toPrettyChars());
- return setError();
- }
- }
- s = s->toAlias();
-
- // Same as wthis.ident
- // TODO: DotIdExp.semantic will find 'ident' from 'wthis' again.
- // The redudancy should be removed.
- e = new VarExp(exp->loc, withsym->withstate->wthis);
- e = new DotIdExp(exp->loc, e, exp->ident);
- e = expressionSemantic(e, sc);
- }
- else
- {
- if (withsym)
- {
- if (withsym->withstate->exp->type->ty != Tvoid)
- {
- // with (exp)' is a type expression
- // or 's' is not visible there (for error message)
- e = new TypeExp(exp->loc, withsym->withstate->exp->type);
- }
- else
- {
- // 'with (exp)' is a Package/Module
- e = withsym->withstate->exp;
- }
- e = new DotIdExp(exp->loc, e, exp->ident);
- result = expressionSemantic(e, sc);
- return;
- }
-
- /* If f is really a function template,
- * then replace f with the function template declaration.
- */
- FuncDeclaration *f = s->isFuncDeclaration();
- if (f)
- {
- TemplateDeclaration *td = getFuncTemplateDecl(f);
- if (td)
- {
- if (td->overroot) // if not start of overloaded list of TemplateDeclaration's
- td = td->overroot; // then get the start
- e = new TemplateExp(exp->loc, td, f);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- }
- // Haven't done overload resolution yet, so pass 1
- e = resolve(exp->loc, sc, s, true);
- }
- result = e;
- return;
- }
-
- if (hasThis(sc))
- {
- AggregateDeclaration *ad = sc->getStructClassScope();
- if (ad && ad->aliasthis)
- {
- Expression *e;
- e = new IdentifierExp(exp->loc, Id::This);
- e = new DotIdExp(exp->loc, e, ad->aliasthis->ident);
- e = new DotIdExp(exp->loc, e, exp->ident);
- e = trySemantic(e, sc);
- if (e)
- {
- result = e;
- return;
- }
- }
- }
-
- if (exp->ident == Id::ctfe)
- {
- if (sc->flags & SCOPEctfe)
- {
- exp->error("variable __ctfe cannot be read at compile time");
- return setError();
- }
-
- // Create the magic __ctfe bool variable
- VarDeclaration *vd = new VarDeclaration(exp->loc, Type::tbool, Id::ctfe, NULL);
- vd->storage_class |= STCtemp;
- vd->semanticRun = PASSsemanticdone;
- Expression *e = new VarExp(exp->loc, vd);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
-
- // If we've reached this point and are inside a with() scope then we may
- // try one last attempt by checking whether the 'wthis' object supports
- // dynamic dispatching via opDispatch.
- // This is done by rewriting this expression as wthis.ident.
- for (Scope *sc2 = sc; sc2; sc2 = sc2->enclosing)
- {
- if (!sc2->scopesym)
- continue;
-
- if (WithScopeSymbol *ss = sc2->scopesym->isWithScopeSymbol())
- {
- if (ss->withstate->wthis)
- {
- Expression *e;
- e = new VarExp(exp->loc, ss->withstate->wthis);
- e = new DotIdExp(exp->loc, e, exp->ident);
- e = trySemantic(e, sc);
- if (e)
- {
- result = e;
- return;
- }
- }
- break;
- }
- }
-
- /* Look for what user might have meant
- */
- if (const char *n = importHint(exp->ident->toChars()))
- exp->error("`%s` is not defined, perhaps `import %s;` is needed?", exp->ident->toChars(), n);
- else if (Dsymbol *s2 = sc->search_correct(exp->ident))
- exp->error("undefined identifier `%s`, did you mean %s `%s`?", exp->ident->toChars(), s2->kind(), s2->toChars());
- else if (const char *p = Scope::search_correct_C(exp->ident))
- exp->error("undefined identifier `%s`, did you mean `%s`?", exp->ident->toChars(), p);
- else
- exp->error("undefined identifier `%s`", exp->ident->toChars());
- return setError();
- }
-
- void visit(DsymbolExp *e)
- {
- result = resolve(e->loc, sc, e->s, e->hasOverloads);
- }
-
- void visit(ThisExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- FuncDeclaration *fd = hasThis(sc); // fd is the uplevel function with the 'this' variable
-
- /* Special case for typeof(this) and typeof(super) since both
- * should work even if they are not inside a non-static member function
- */
- if (!fd && sc->intypeof == 1)
- {
- // Find enclosing struct or class
- for (Dsymbol *s = sc->getStructClassScope(); 1; s = s->parent)
- {
- if (!s)
- {
- e->error("%s is not in a class or struct scope", e->toChars());
- goto Lerr;
- }
- ClassDeclaration *cd = s->isClassDeclaration();
- if (cd)
- {
- e->type = cd->type;
- result = e;
- return;
- }
- StructDeclaration *sd = s->isStructDeclaration();
- if (sd)
- {
- e->type = sd->type;
- result = e;
- return;
- }
- }
- }
- if (!fd)
- goto Lerr;
-
- assert(fd->vthis);
- e->var = fd->vthis;
- assert(e->var->parent);
- e->type = e->var->type;
- if (e->var->checkNestedReference(sc, e->loc))
- return setError();
- if (!sc->intypeof)
- sc->callSuper |= CSXthis;
- result = e;
- return;
-
- Lerr:
- e->error("`this` is only defined in non-static member functions, not %s", sc->parent->toChars());
- return setError();
- }
-
- void visit(SuperExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- FuncDeclaration *fd = hasThis(sc);
- ClassDeclaration *cd;
- Dsymbol *s;
-
- /* Special case for typeof(this) and typeof(super) since both
- * should work even if they are not inside a non-static member function
- */
- if (!fd && sc->intypeof == 1)
- {
- // Find enclosing class
- for (s = sc->getStructClassScope(); 1; s = s->parent)
- {
- if (!s)
- {
- e->error("%s is not in a class scope", e->toChars());
- goto Lerr;
- }
- cd = s->isClassDeclaration();
- if (cd)
- {
- cd = cd->baseClass;
- if (!cd)
- {
- e->error("class %s has no `super`", s->toChars());
- goto Lerr;
- }
- e->type = cd->type;
- result = e;
- return;
- }
- }
- }
- if (!fd)
- goto Lerr;
-
- e->var = fd->vthis;
- assert(e->var && e->var->parent);
-
- s = fd->toParent();
- while (s && s->isTemplateInstance())
- s = s->toParent();
- if (s->isTemplateDeclaration()) // allow inside template constraint
- s = s->toParent();
- assert(s);
- cd = s->isClassDeclaration();
- //printf("parent is %s %s\n", fd->toParent()->kind(), fd->toParent()->toChars());
- if (!cd)
- goto Lerr;
- if (!cd->baseClass)
- {
- e->error("no base class for %s", cd->toChars());
- e->type = e->var->type;
- }
- else
- {
- e->type = cd->baseClass->type;
- e->type = e->type->castMod(e->var->type->mod);
- }
-
- if (e->var->checkNestedReference(sc, e->loc))
- return setError();
-
- if (!sc->intypeof)
- sc->callSuper |= CSXsuper;
- result = e;
- return;
-
- Lerr:
- e->error("`super` is only allowed in non-static class member functions");
- return setError();
- }
-
- void visit(NullExp *e)
- {
- // NULL is the same as (void *)0
- if (e->type)
- {
- result = e;
- return;
- }
- e->type = Type::tnull;
- result = e;
- }
-
- void visit(StringExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- OutBuffer buffer;
- size_t newlen = 0;
- const char *p;
- size_t u;
- unsigned c;
-
- switch (e->postfix)
- {
- case 'd':
- for (u = 0; u < e->len;)
- {
- p = utf_decodeChar((utf8_t *)e->string, e->len, &u, &c);
- if (p)
- {
- e->error("%s", p);
- return setError();
- }
- else
- {
- buffer.write4(c);
- newlen++;
- }
- }
- buffer.write4(0);
- e->string = buffer.extractData();
- e->len = newlen;
- e->sz = 4;
- e->type = new TypeDArray(Type::tdchar->immutableOf());
- e->committed = 1;
- break;
-
- case 'w':
- for (u = 0; u < e->len;)
- {
- p = utf_decodeChar((utf8_t *)e->string, e->len, &u, &c);
- if (p)
- {
- e->error("%s", p);
- return setError();
- }
- else
- {
- buffer.writeUTF16(c);
- newlen++;
- if (c >= 0x10000)
- newlen++;
- }
- }
- buffer.writeUTF16(0);
- e->string = buffer.extractData();
- e->len = newlen;
- e->sz = 2;
- e->type = new TypeDArray(Type::twchar->immutableOf());
- e->committed = 1;
- break;
-
- case 'c':
- e->committed = 1;
- /* fall through */
-
- default:
- e->type = new TypeDArray(Type::tchar->immutableOf());
- break;
- }
- e->type = typeSemantic(e->type, e->loc, sc);
- //e->type = e->type->immutableOf();
- //printf("type = %s\n", e->type->toChars());
-
- result = e;
- }
-
- void visit(ArrayLiteralExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- /* Perhaps an empty array literal [ ] should be rewritten as null?
- */
-
- if (e->basis)
- e->basis = expressionSemantic(e->basis, sc);
- if (arrayExpressionSemantic(e->elements, sc) || (e->basis && e->basis->op == TOKerror))
- return setError();
- expandTuples(e->elements);
-
- Type *t0;
- if (e->basis)
- e->elements->push(e->basis);
- bool err = arrayExpressionToCommonType(sc, e->elements, &t0);
- if (e->basis)
- e->elements->pop();
- if (err)
- return setError();
-
- e->type = t0->arrayOf();
- e->type = typeSemantic(e->type, e->loc, sc);
-
- /* Disallow array literals of type void being used.
- */
- if (e->elements->length > 0 && t0->ty == Tvoid)
- {
- e->error("%s of type %s has no value", e->toChars(), e->type->toChars());
- return setError();
- }
-
- if (global.params.useTypeInfo && Type::dtypeinfo)
- semanticTypeInfo(sc, e->type);
-
- result = e;
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- // Run semantic() on each element
- bool err_keys = arrayExpressionSemantic(e->keys, sc);
- bool err_vals = arrayExpressionSemantic(e->values, sc);
- if (err_keys || err_vals)
- return setError();
- expandTuples(e->keys);
- expandTuples(e->values);
- if (e->keys->length != e->values->length)
- {
- e->error("number of keys is %u, must match number of values %u", e->keys->length, e->values->length);
- return setError();
- }
-
- Type *tkey = NULL;
- Type *tvalue = NULL;
- err_keys = arrayExpressionToCommonType(sc, e->keys, &tkey);
- err_vals = arrayExpressionToCommonType(sc, e->values, &tvalue);
- if (err_keys || err_vals)
- return setError();
-
- if (tkey == Type::terror || tvalue == Type::terror)
- return setError();
-
- e->type = new TypeAArray(tvalue, tkey);
- e->type = typeSemantic(e->type, e->loc, sc);
-
- semanticTypeInfo(sc, e->type);
-
- result = e;
- }
-
- void visit(StructLiteralExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- e->sd->size(e->loc);
- if (e->sd->sizeok != SIZEOKdone)
- return setError();
-
- if (arrayExpressionSemantic(e->elements, sc)) // run semantic() on each element
- return setError();
- expandTuples(e->elements);
-
- /* Fit elements[] to the corresponding type of field[].
- */
- if (!e->sd->fit(e->loc, sc, e->elements, e->stype))
- return setError();
-
- /* Fill out remainder of elements[] with default initializers for fields[]
- */
- if (!e->sd->fill(e->loc, e->elements, false))
- {
- /* An error in the initializer needs to be recorded as an error
- * in the enclosing function or template, since the initializer
- * will be part of the stuct declaration.
- */
- global.increaseErrorCount();
- return setError();
- }
-
- if (checkFrameAccess(e->loc, sc, e->sd, e->elements->length))
- return setError();
-
- e->type = e->stype ? e->stype : e->sd->type;
- result = e;
- }
-
- void visit(TypeExp *exp)
- {
- if (exp->type->ty == Terror)
- return setError();
-
- //printf("TypeExp::semantic(%s)\n", exp->type->toChars());
- Expression *e;
- Type *t;
- Dsymbol *s;
-
- exp->type->resolve(exp->loc, sc, &e, &t, &s, true);
- if (e)
- {
- // `(Type)` is actually `(var)` so if `(var)` is a member requiring `this`
- // then rewrite as `(this.var)` in case it would be followed by a DotVar
- // to fix https://issues.dlang.org/show_bug.cgi?id=9490
- VarExp *ve = e->isVarExp();
- if (ve && ve->var && exp->parens && !ve->var->isStatic() && !(sc->stc & STCstatic) &&
- sc->func && sc->func->needThis() && ve->var->toParent2()->isAggregateDeclaration())
- {
- // printf("apply fix for issue 9490: add `this.` to `%s`...\n", e->toChars());
- e = new DotVarExp(exp->loc, new ThisExp(exp->loc), ve->var, false);
- }
- //printf("e = %s %s\n", Token::toChars(e->op), e->toChars());
- e = expressionSemantic(e, sc);
- }
- else if (t)
- {
- //printf("t = %d %s\n", t->ty, t->toChars());
- exp->type = typeSemantic(t, exp->loc, sc);
- e = exp;
- }
- else if (s)
- {
- //printf("s = %s %s\n", s->kind(), s->toChars());
- e = resolve(exp->loc, sc, s, true);
- }
- else
- assert(0);
-
- if (global.params.vcomplex)
- exp->type->checkComplexTransition(exp->loc);
-
- result = e;
- }
-
- void visit(ScopeExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- ScopeDsymbol *sds2 = exp->sds;
- TemplateInstance *ti = sds2->isTemplateInstance();
- while (ti)
- {
- WithScopeSymbol *withsym;
- if (!ti->findTempDecl(sc, &withsym) ||
- !ti->semanticTiargs(sc))
- return setError();
- if (withsym && withsym->withstate->wthis)
- {
- Expression *e = new VarExp(exp->loc, withsym->withstate->wthis);
- e = new DotTemplateInstanceExp(exp->loc, e, ti);
- result = expressionSemantic(e, sc);
- return;
- }
- if (ti->needsTypeInference(sc))
- {
- if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration())
- {
- Dsymbol *p = td->toParent2();
- FuncDeclaration *fdthis = hasThis(sc);
- AggregateDeclaration *ad = p ? p->isAggregateDeclaration() : NULL;
- if (fdthis && ad && isAggregate(fdthis->vthis->type) == ad &&
- (td->_scope->stc & STCstatic) == 0)
- {
- Expression *e = new DotTemplateInstanceExp(exp->loc, new ThisExp(exp->loc), ti->name, ti->tiargs);
- result = expressionSemantic(e, sc);
- return;
- }
- }
- else if (OverloadSet *os = ti->tempdecl->isOverloadSet())
- {
- FuncDeclaration *fdthis = hasThis(sc);
- AggregateDeclaration *ad = os->parent->isAggregateDeclaration();
- if (fdthis && ad && isAggregate(fdthis->vthis->type) == ad)
- {
- Expression *e = new DotTemplateInstanceExp(exp->loc, new ThisExp(exp->loc), ti->name, ti->tiargs);
- result = expressionSemantic(e, sc);
- return;
- }
- }
- // ti is an instance which requires IFTI.
- exp->sds = ti;
- exp->type = Type::tvoid;
- result = exp;
- return;
- }
- dsymbolSemantic(ti, sc);
- if (!ti->inst || ti->errors)
- return setError();
-
- Dsymbol *s = ti->toAlias();
- if (s == ti)
- {
- exp->sds = ti;
- exp->type = Type::tvoid;
- result = exp;
- return;
- }
- sds2 = s->isScopeDsymbol();
- if (sds2)
- {
- ti = sds2->isTemplateInstance();
- //printf("+ sds2 = %s, '%s'\n", sds2->kind(), sds2->toChars());
- continue;
- }
-
- if (VarDeclaration *v = s->isVarDeclaration())
- {
- if (!v->type)
- {
- exp->error("forward reference of %s %s", v->kind(), v->toChars());
- return setError();
- }
- if ((v->storage_class & STCmanifest) && v->_init)
- {
- /* When an instance that will be converted to a constant exists,
- * the instance representation "foo!tiargs" is treated like a
- * variable name, and its recursive appearance check (note that
- * it's equivalent with a recursive instantiation of foo) is done
- * separately from the circular initialization check for the
- * eponymous enum variable declaration.
- *
- * template foo(T) {
- * enum bool foo = foo; // recursive definition check (v.inuse)
- * }
- * template bar(T) {
- * enum bool bar = bar!T; // recursive instantiation check (ti.inuse)
- * }
- */
- if (ti->inuse)
- {
- exp->error("recursive expansion of %s `%s`", ti->kind(), ti->toPrettyChars());
- return setError();
- }
-
- Expression *e = v->expandInitializer(exp->loc);
- ti->inuse++;
- e = expressionSemantic(e, sc);
- ti->inuse--;
- result = e;
- return;
- }
- }
-
- //printf("s = %s, '%s'\n", s->kind(), s->toChars());
- Expression *e = resolve(exp->loc, sc, s, true);
- //printf("-1ScopeExp::semantic()\n");
- result = e;
- return;
- }
-
- //printf("sds2 = %s, '%s'\n", sds2->kind(), sds2->toChars());
- //printf("\tparent = '%s'\n", sds2->parent->toChars());
- dsymbolSemantic(sds2, sc);
-
- if (Type *t = sds2->getType()) // (Aggregate|Enum)Declaration
- {
- Expression *ex = new TypeExp(exp->loc, t);
- result = expressionSemantic(ex, sc);
- return;
- }
-
- if (TemplateDeclaration *td = sds2->isTemplateDeclaration())
- {
- result = expressionSemantic(new TemplateExp(exp->loc, td), sc);
- return;
- }
-
- exp->sds = sds2;
- exp->type = Type::tvoid;
- //printf("-2ScopeExp::semantic() %s\n", exp->toChars());
- result = exp;
- }
-
- void visit(NewExp *exp)
- {
- if (exp->type) // if semantic() already run
- {
- result = exp;
- return;
- }
-
- // Bugzilla 11581: With the syntax `new T[edim]` or `thisexp.new T[edim]`,
- // T should be analyzed first and edim should go into arguments iff it's
- // not a tuple.
- Expression *edim = NULL;
- if (!exp->arguments && exp->newtype->ty == Tsarray)
- {
- edim = ((TypeSArray *)exp->newtype)->dim;
- exp->newtype = ((TypeNext *)exp->newtype)->next;
- }
-
- ClassDeclaration *cdthis = NULL;
- if (exp->thisexp)
- {
- exp->thisexp = expressionSemantic(exp->thisexp, sc);
- if (exp->thisexp->op == TOKerror)
- return setError();
- cdthis = exp->thisexp->type->isClassHandle();
- if (!cdthis)
- {
- exp->error("`this` for nested class must be a class type, not %s", exp->thisexp->type->toChars());
- return setError();
- }
-
- sc = sc->push(cdthis);
- exp->type = typeSemantic(exp->newtype, exp->loc, sc);
- sc = sc->pop();
- }
- else
- {
- exp->type = typeSemantic(exp->newtype, exp->loc, sc);
- }
- if (exp->type->ty == Terror)
- return setError();
-
- if (edim)
- {
- if (exp->type->toBasetype()->ty == Ttuple)
- {
- // --> new T[edim]
- exp->type = new TypeSArray(exp->type, edim);
- exp->type = typeSemantic(exp->type, exp->loc, sc);
- if (exp->type->ty == Terror)
- return setError();
- }
- else
- {
- // --> new T[](edim)
- exp->arguments = new Expressions();
- exp->arguments->push(edim);
- exp->type = exp->type->arrayOf();
- }
- }
-
- exp->newtype = exp->type; // in case type gets cast to something else
- Type *tb = exp->type->toBasetype();
- //printf("tb: %s, deco = %s\n", tb->toChars(), tb->deco);
-
- if (arrayExpressionSemantic(exp->newargs, sc) ||
- preFunctionParameters(sc, exp->newargs))
- {
- return setError();
- }
- if (arrayExpressionSemantic(exp->arguments, sc) ||
- preFunctionParameters(sc, exp->arguments))
- {
- return setError();
- }
-
- if (exp->thisexp && tb->ty != Tclass)
- {
- exp->error("e.new is only for allocating nested classes, not %s", tb->toChars());
- return setError();
- }
-
- size_t nargs = exp->arguments ? exp->arguments->length : 0;
- Expression *newprefix = NULL;
-
- if (tb->ty == Tclass)
- {
- ClassDeclaration *cd = ((TypeClass *)tb)->sym;
- cd->size(exp->loc);
- if (cd->sizeok != SIZEOKdone)
- return setError();
- if (!cd->ctor)
- cd->ctor = cd->searchCtor();
- if (cd->noDefaultCtor && !nargs && !cd->defaultCtor)
- {
- exp->error("default construction is disabled for type %s", cd->type->toChars());
- return setError();
- }
-
- if (cd->isInterfaceDeclaration())
- {
- exp->error("cannot create instance of interface %s", cd->toChars());
- return setError();
- }
- if (cd->isAbstract())
- {
- exp->error("cannot create instance of abstract class %s", cd->toChars());
- for (size_t i = 0; i < cd->vtbl.length; i++)
- {
- FuncDeclaration *fd = cd->vtbl[i]->isFuncDeclaration();
- if (fd && fd->isAbstract())
- errorSupplemental(exp->loc, "function `%s` is not implemented", fd->toFullSignature());
- }
- return setError();
- }
- // checkDeprecated() is already done in newtype->semantic().
-
- if (cd->isNested())
- {
- /* We need a 'this' pointer for the nested class.
- * Ensure we have the right one.
- */
- Dsymbol *s = cd->toParent2();
- //printf("cd isNested, parent = %s '%s'\n", s->kind(), s->toPrettyChars());
- if (ClassDeclaration *cdn = s->isClassDeclaration())
- {
- if (!cdthis)
- {
- // Supply an implicit 'this' and try again
- exp->thisexp = new ThisExp(exp->loc);
- for (Dsymbol *sp = sc->parent; 1; sp = sp->parent)
- {
- if (!sp)
- {
- exp->error("outer class %s `this` needed to `new` nested class %s", cdn->toChars(), cd->toChars());
- return setError();
- }
- ClassDeclaration *cdp = sp->isClassDeclaration();
- if (!cdp)
- continue;
- if (cdp == cdn || cdn->isBaseOf(cdp, NULL))
- break;
- // Add a '.outer' and try again
- exp->thisexp = new DotIdExp(exp->loc, exp->thisexp, Id::outer);
- }
- exp->thisexp = expressionSemantic(exp->thisexp, sc);
- if (exp->thisexp->op == TOKerror)
- return setError();
- cdthis = exp->thisexp->type->isClassHandle();
- }
- if (cdthis != cdn && !cdn->isBaseOf(cdthis, NULL))
- {
- //printf("cdthis = %s\n", cdthis->toChars());
- exp->error("`this` for nested class must be of type %s, not %s",
- cdn->toChars(), exp->thisexp->type->toChars());
- return setError();
- }
- if (!MODimplicitConv(exp->thisexp->type->mod, exp->newtype->mod))
- {
- exp->error("nested type %s should have the same or weaker constancy as enclosing type %s",
- exp->newtype->toChars(), exp->thisexp->type->toChars());
- return setError();
- }
- }
- else if (exp->thisexp)
- {
- exp->error("e.new is only for allocating nested classes");
- return setError();
- }
- else if (FuncDeclaration *fdn = s->isFuncDeclaration())
- {
- // make sure the parent context fdn of cd is reachable from sc
- if (checkNestedRef(sc->parent, fdn))
- {
- exp->error("outer function context of %s is needed to `new` nested class %s",
- fdn->toPrettyChars(), cd->toPrettyChars());
- return setError();
- }
- }
- else
- assert(0);
- }
- else if (exp->thisexp)
- {
- exp->error("e.new is only for allocating nested classes");
- return setError();
- }
-
- if (cd->aggNew)
- {
- // Prepend the size argument to newargs[]
- Expression *e = new IntegerExp(exp->loc, cd->size(exp->loc), Type::tsize_t);
- if (!exp->newargs)
- exp->newargs = new Expressions();
- exp->newargs->shift(e);
-
- FuncDeclaration *f = resolveFuncCall(exp->loc, sc, cd->aggNew, NULL, tb, exp->newargs);
- if (!f || f->errors)
- return setError();
- exp->checkDeprecated(sc, f);
- exp->checkDisabled(sc, f);
- exp->checkPurity(sc, f);
- exp->checkSafety(sc, f);
- exp->checkNogc(sc, f);
- checkAccess(cd, exp->loc, sc, f);
-
- TypeFunction *tf = (TypeFunction *)f->type;
- Type *rettype;
- if (functionParameters(exp->loc, sc, tf, NULL, exp->newargs, f, &rettype, &newprefix))
- return setError();
-
- exp->allocator = f->isNewDeclaration();
- assert(exp->allocator);
- }
- else
- {
- if (exp->newargs && exp->newargs->length)
- {
- exp->error("no allocator for %s", cd->toChars());
- return setError();
- }
- }
-
- if (cd->ctor)
- {
- FuncDeclaration *f = resolveFuncCall(exp->loc, sc, cd->ctor, NULL, tb, exp->arguments, 0);
- if (!f || f->errors)
- return setError();
- exp->checkDeprecated(sc, f);
- exp->checkDisabled(sc, f);
- exp->checkPurity(sc, f);
- exp->checkSafety(sc, f);
- exp->checkNogc(sc, f);
- checkAccess(cd, exp->loc, sc, f);
-
- TypeFunction *tf = (TypeFunction *)f->type;
- if (!exp->arguments)
- exp->arguments = new Expressions();
- if (functionParameters(exp->loc, sc, tf, exp->type, exp->arguments, f, &exp->type, &exp->argprefix))
- return setError();
-
- exp->member = f->isCtorDeclaration();
- assert(exp->member);
- }
- else
- {
- if (nargs)
- {
- exp->error("no constructor for %s", cd->toChars());
- return setError();
- }
-
- // https://issues.dlang.org/show_bug.cgi?id=19941
- // Run semantic on all field initializers to resolve any forward
- // references. This is the same as done for structs in sd->fill().
- for (ClassDeclaration *c = cd; c; c = c->baseClass)
- {
- for (size_t i = 0; i < c->fields.length; i++)
- {
- VarDeclaration *v = c->fields[i];
- if (v->inuse || v->_scope == NULL || v->_init == NULL ||
- v->_init->isVoidInitializer())
- continue;
- v->inuse++;
- v->_init = initializerSemantic(v->_init, v->_scope, v->type, INITinterpret);
- v->inuse--;
- }
- }
- }
- }
- else if (tb->ty == Tstruct)
- {
- StructDeclaration *sd = ((TypeStruct *)tb)->sym;
- sd->size(exp->loc);
- if (sd->sizeok != SIZEOKdone)
- return setError();
- if (!sd->ctor)
- sd->ctor = sd->searchCtor();
- if (sd->noDefaultCtor && !nargs)
- {
- exp->error("default construction is disabled for type %s", sd->type->toChars());
- return setError();
- }
- // checkDeprecated() is already done in newtype->semantic().
-
- if (sd->aggNew)
- {
- // Prepend the uint size argument to newargs[]
- Expression *e = new IntegerExp(exp->loc, sd->size(exp->loc), Type::tsize_t);
- if (!exp->newargs)
- exp->newargs = new Expressions();
- exp->newargs->shift(e);
-
- FuncDeclaration *f = resolveFuncCall(exp->loc, sc, sd->aggNew, NULL, tb, exp->newargs);
- if (!f || f->errors)
- return setError();
- exp->checkDeprecated(sc, f);
- exp->checkDisabled(sc, f);
- exp->checkPurity(sc, f);
- exp->checkSafety(sc, f);
- exp->checkNogc(sc, f);
- checkAccess(sd, exp->loc, sc, f);
-
- TypeFunction *tf = (TypeFunction *)f->type;
- Type *rettype;
- if (functionParameters(exp->loc, sc, tf, NULL, exp->newargs, f, &rettype, &newprefix))
- return setError();
-
- exp->allocator = f->isNewDeclaration();
- assert(exp->allocator);
- }
- else
- {
- if (exp->newargs && exp->newargs->length)
- {
- exp->error("no allocator for %s", sd->toChars());
- return setError();
- }
- }
-
- if (sd->ctor && nargs)
- {
- FuncDeclaration *f = resolveFuncCall(exp->loc, sc, sd->ctor, NULL, tb, exp->arguments, 0);
- if (!f || f->errors)
- return setError();
- exp->checkDeprecated(sc, f);
- exp->checkDisabled(sc, f);
- exp->checkPurity(sc, f);
- exp->checkSafety(sc, f);
- exp->checkNogc(sc, f);
- checkAccess(sd, exp->loc, sc, f);
-
- TypeFunction *tf = (TypeFunction *)f->type;
- if (!exp->arguments)
- exp->arguments = new Expressions();
- if (functionParameters(exp->loc, sc, tf, exp->type, exp->arguments, f, &exp->type, &exp->argprefix))
- return setError();
-
- exp->member = f->isCtorDeclaration();
- assert(exp->member);
-
- if (checkFrameAccess(exp->loc, sc, sd, sd->fields.length))
- return setError();
- }
- else
- {
- if (!exp->arguments)
- exp->arguments = new Expressions();
-
- if (!sd->fit(exp->loc, sc, exp->arguments, tb))
- return setError();
- if (!sd->fill(exp->loc, exp->arguments, false))
- return setError();
- if (checkFrameAccess(exp->loc, sc, sd, exp->arguments ? exp->arguments->length : 0))
- return setError();
- }
-
- exp->type = exp->type->pointerTo();
- }
- else if (tb->ty == Tarray && nargs)
- {
- Type *tn = tb->nextOf()->baseElemOf();
- Dsymbol *s = tn->toDsymbol(sc);
- AggregateDeclaration *ad = s ? s->isAggregateDeclaration() : NULL;
- if (ad && ad->noDefaultCtor)
- {
- exp->error("default construction is disabled for type %s", tb->nextOf()->toChars());
- return setError();
- }
- for (size_t i = 0; i < nargs; i++)
- {
- if (tb->ty != Tarray)
- {
- exp->error("too many arguments for array");
- return setError();
- }
-
- Expression *arg = (*exp->arguments)[i];
- arg = resolveProperties(sc, arg);
- arg = arg->implicitCastTo(sc, Type::tsize_t);
- arg = arg->optimize(WANTvalue);
- if (arg->op == TOKint64 && (sinteger_t)arg->toInteger() < 0)
- {
- exp->error("negative array index %s", arg->toChars());
- return setError();
- }
- (*exp->arguments)[i] = arg;
- tb = ((TypeDArray *)tb)->next->toBasetype();
- }
- }
- else if (tb->isscalar())
- {
- if (!nargs)
- {
- }
- else if (nargs == 1)
- {
- Expression *e = (*exp->arguments)[0];
- e = e->implicitCastTo(sc, tb);
- (*exp->arguments)[0] = e;
- }
- else
- {
- exp->error("more than one argument for construction of %s", exp->type->toChars());
- return setError();
- }
-
- exp->type = exp->type->pointerTo();
- }
- else
- {
- exp->error("new can only create structs, dynamic arrays or class objects, not %s's", exp->type->toChars());
- return setError();
- }
-
- //printf("NewExp: '%s'\n", toChars());
- //printf("NewExp:type '%s'\n", exp->type->toChars());
- semanticTypeInfo(sc, exp->type);
-
- if (newprefix)
- {
- result = Expression::combine(newprefix, exp);
- return;
- }
- result = exp;
- }
-
- void visit(NewAnonClassExp *e)
- {
- Expression *d = new DeclarationExp(e->loc, e->cd);
- sc = sc->push(); // just create new scope
- sc->flags &= ~SCOPEctfe; // temporary stop CTFE
- d = expressionSemantic(d, sc);
- sc = sc->pop();
-
- if (!e->cd->errors && sc->intypeof && !sc->parent->inNonRoot())
- {
- ScopeDsymbol *sds = sc->tinst ? (ScopeDsymbol *)sc->tinst : sc->_module;
- sds->members->push(e->cd);
- }
-
- Expression *n = new NewExp(e->loc, e->thisexp, e->newargs, e->cd->type, e->arguments);
-
- Expression *c = new CommaExp(e->loc, d, n);
- result = expressionSemantic(c, sc);
- }
-
- void visit(SymOffExp *e)
- {
- //dsymbolSemantic(var, sc);
- if (!e->type)
- e->type = e->var->type->pointerTo();
- if (VarDeclaration *v = e->var->isVarDeclaration())
- {
- if (v->checkNestedReference(sc, e->loc))
- return setError();
- }
- else if (FuncDeclaration *f = e->var->isFuncDeclaration())
- {
- if (f->checkNestedReference(sc, e->loc))
- return setError();
- }
- result = e;
- }
-
- void visit(VarExp *e)
- {
- VarDeclaration *vd = e->var->isVarDeclaration();
- FuncDeclaration *fd = e->var->isFuncDeclaration();
-
- if (fd)
- {
- //printf("L%d fd = %s\n", __LINE__, f->toChars());
- if (!fd->functionSemantic())
- return setError();
- }
-
- if (!e->type)
- e->type = e->var->type;
-
- if (e->type && !e->type->deco)
- {
- Declaration *decl = e->var->isDeclaration();
- if (decl)
- decl->inuse++;
- e->type = typeSemantic(e->type, e->loc, sc);
- if (decl)
- decl->inuse--;
- }
-
- /* Fix for 1161 doesn't work because it causes protection
- * problems when instantiating imported templates passing private
- * variables as alias template parameters.
- */
- //checkAccess(e->loc, sc, NULL, e->var);
-
- if (vd)
- {
- if (vd->checkNestedReference(sc, e->loc))
- return setError();
- // Bugzilla 12025: If the variable is not actually used in runtime code,
- // the purity violation error is redundant.
- //checkPurity(sc, vd);
- }
- else if (fd)
- {
- // TODO: If fd isn't yet resolved its overload, the checkNestedReference
- // call would cause incorrect validation.
- // Maybe here should be moved in CallExp, or AddrExp for functions.
- if (fd->checkNestedReference(sc, e->loc))
- return setError();
- }
- else if (e->var->isOverDeclaration())
- {
- e->type = Type::tvoid; // ambiguous type?
- }
-
- result = e;
- }
-
- void visit(TupleExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (exp->e0)
- exp->e0 = expressionSemantic(exp->e0, sc);
-
- // Run semantic() on each argument
- bool err = false;
- for (size_t i = 0; i < exp->exps->length; i++)
- {
- Expression *e = (*exp->exps)[i];
- e = expressionSemantic(e, sc);
- if (!e->type)
- {
- exp->error("%s has no value", e->toChars());
- err = true;
- }
- else if (e->op == TOKerror)
- err = true;
- else
- (*exp->exps)[i] = e;
- }
- if (err)
- return setError();
-
- expandTuples(exp->exps);
- exp->type = new TypeTuple(exp->exps);
- exp->type = typeSemantic(exp->type, exp->loc, sc);
- //printf("-TupleExp::semantic(%s)\n", exp->toChars());
- result = exp;
- }
-
- void visit(FuncExp *exp)
- {
- Expression *e = exp;
-
- sc = sc->push(); // just create new scope
- sc->flags &= ~SCOPEctfe; // temporary stop CTFE
- sc->protection = Prot(Prot::public_); // Bugzilla 12506
-
- if (!exp->type || exp->type == Type::tvoid)
- {
- /* fd->treq might be incomplete type,
- * so should not semantic it.
- * void foo(T)(T delegate(int) dg){}
- * foo(a=>a); // in IFTI, treq == T delegate(int)
- */
- //if (exp->fd->treq)
- // exp->fd->treq = typeSemantic(exp->fd->treq, exp->loc, sc);
-
- exp->genIdent(sc);
-
- // Set target of return type inference
- if (exp->fd->treq && !exp->fd->type->nextOf())
- {
- TypeFunction *tfv = NULL;
- if (exp->fd->treq->ty == Tdelegate ||
- (exp->fd->treq->ty == Tpointer && exp->fd->treq->nextOf()->ty == Tfunction))
- tfv = (TypeFunction *)exp->fd->treq->nextOf();
- if (tfv)
- {
- TypeFunction *tfl = (TypeFunction *)exp->fd->type;
- tfl->next = tfv->nextOf();
- }
- }
-
- //printf("td = %p, treq = %p\n", exp->td, exp->fd->treq);
- if (exp->td)
- {
- assert(exp->td->parameters && exp->td->parameters->length);
- dsymbolSemantic(exp->td, sc);
- exp->type = Type::tvoid; // temporary type
-
- if (exp->fd->treq) // defer type determination
- {
- FuncExp *fe;
- if (exp->matchType(exp->fd->treq, sc, &fe) > MATCHnomatch)
- e = fe;
- else
- e = new ErrorExp();
- }
- goto Ldone;
- }
-
- unsigned olderrors = global.errors;
- dsymbolSemantic(exp->fd, sc);
- if (olderrors == global.errors)
- {
- semantic2(exp->fd, sc);
- if (olderrors == global.errors)
- semantic3(exp->fd, sc);
- }
- if (olderrors != global.errors)
- {
- if (exp->fd->type && exp->fd->type->ty == Tfunction && !exp->fd->type->nextOf())
- ((TypeFunction *)exp->fd->type)->next = Type::terror;
- e = new ErrorExp();
- goto Ldone;
- }
-
- // Type is a "delegate to" or "pointer to" the function literal
- if ((exp->fd->isNested() && exp->fd->tok == TOKdelegate) ||
- (exp->tok == TOKreserved && exp->fd->treq && exp->fd->treq->ty == Tdelegate))
- {
- exp->type = new TypeDelegate(exp->fd->type);
- exp->type = typeSemantic(exp->type, exp->loc, sc);
-
- exp->fd->tok = TOKdelegate;
- }
- else
- {
- exp->type = new TypePointer(exp->fd->type);
- exp->type = typeSemantic(exp->type, exp->loc, sc);
- //exp->type = exp->fd->type->pointerTo();
-
- /* A lambda expression deduced to function pointer might become
- * to a delegate literal implicitly.
- *
- * auto foo(void function() fp) { return 1; }
- * assert(foo({}) == 1);
- *
- * So, should keep fd->tok == TOKreserve if fd->treq == NULL.
- */
- if (exp->fd->treq && exp->fd->treq->ty == Tpointer)
- {
- // change to non-nested
- exp->fd->tok = TOKfunction;
- exp->fd->vthis = NULL;
- }
- }
- exp->fd->tookAddressOf++;
- }
- Ldone:
- sc = sc->pop();
- result = e;
- }
-
- // used from CallExp::semantic()
- Expression *callExpSemantic(FuncExp *exp, Scope *sc, Expressions *arguments)
- {
- if ((!exp->type || exp->type == Type::tvoid) && exp->td && arguments && arguments->length)
- {
- for (size_t k = 0; k < arguments->length; k++)
- { Expression *checkarg = (*arguments)[k];
- if (checkarg->op == TOKerror)
- return checkarg;
- }
-
- exp->genIdent(sc);
-
- assert(exp->td->parameters && exp->td->parameters->length);
- dsymbolSemantic(exp->td, sc);
-
- TypeFunction *tfl = (TypeFunction *)exp->fd->type;
- size_t dim = tfl->parameterList.length();
- if (arguments->length < dim)
- { // Default arguments are always typed, so they don't need inference.
- Parameter *p = tfl->parameterList[arguments->length];
- if (p->defaultArg)
- dim = arguments->length;
- }
-
- if ((tfl->parameterList.varargs == VARARGnone && arguments->length == dim) ||
- (tfl->parameterList.varargs != VARARGnone && arguments->length >= dim))
- {
- Objects *tiargs = new Objects();
- tiargs->reserve(exp->td->parameters->length);
-
- for (size_t i = 0; i < exp->td->parameters->length; i++)
- {
- TemplateParameter *tp = (*exp->td->parameters)[i];
- for (size_t u = 0; u < dim; u++)
- { Parameter *p = tfl->parameterList[u];
- if (p->type->ty == Tident &&
- ((TypeIdentifier *)p->type)->ident == tp->ident)
- { Expression *e = (*arguments)[u];
- tiargs->push(e->type);
- u = dim; // break inner loop
- }
- }
- }
-
- TemplateInstance *ti = new TemplateInstance(exp->loc, exp->td, tiargs);
- Expression *se = new ScopeExp(exp->loc, ti);
- return expressionSemantic(se, sc);
- }
- exp->error("cannot infer function literal type");
- return new ErrorExp();
- }
- return expressionSemantic(exp, sc);
- }
-
- void visit(DeclarationExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- unsigned olderrors = global.errors;
-
- /* This is here to support extern(linkage) declaration,
- * where the extern(linkage) winds up being an AttribDeclaration
- * wrapper.
- */
- Dsymbol *s = e->declaration;
-
- while (1)
- {
- AttribDeclaration *ad = s->isAttribDeclaration();
- if (ad)
- {
- if (ad->decl && ad->decl->length == 1)
- {
- s = (*ad->decl)[0];
- continue;
- }
- }
- break;
- }
-
- VarDeclaration *v = s->isVarDeclaration();
- if (v)
- {
- // Do semantic() on initializer first, so:
- // int a = a;
- // will be illegal.
- dsymbolSemantic(e->declaration, sc);
- s->parent = sc->parent;
- }
-
- //printf("inserting '%s' %p into sc = %p\n", s->toChars(), s, sc);
- // Insert into both local scope and function scope.
- // Must be unique in both.
- if (s->ident)
- {
- if (!sc->insert(s))
- {
- e->error("declaration %s is already defined", s->toPrettyChars());
- return setError();
- }
- else if (sc->func)
- {
- // Bugzilla 11720 - include Dataseg variables
- if ((s->isFuncDeclaration() ||
- s->isAggregateDeclaration() ||
- s->isEnumDeclaration() ||
- (v && v->isDataseg())) &&
- !sc->func->localsymtab->insert(s))
- {
- e->error("declaration %s is already defined in another scope in %s",
- s->toPrettyChars(), sc->func->toChars());
- return setError();
- }
- else
- {
- // Disallow shadowing
- for (Scope *scx = sc->enclosing; scx && (scx->func == sc->func || (scx->func && sc->func->fes)); scx = scx->enclosing)
- {
- Dsymbol *s2;
- if (scx->scopesym && scx->scopesym->symtab &&
- (s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL &&
- s != s2)
- {
- // allow STClocal symbols to be shadowed
- // TODO: not reallly an optimal design
- Declaration *decl = s2->isDeclaration();
- if (!decl || !(decl->storage_class & STClocal))
- {
- if (sc->func->fes)
- {
- e->deprecation("%s `%s` is shadowing %s `%s`. Rename the `foreach` variable.",
- s->kind(), s->ident->toChars(), s2->kind(), s2->toPrettyChars());
- }
- else
- {
- e->error("%s %s is shadowing %s %s",
- s->kind(), s->ident->toChars(), s2->kind(), s2->toPrettyChars());
- return setError();
- }
- }
- }
- }
- }
- }
- }
- if (!s->isVarDeclaration())
- {
- Scope *sc2 = sc;
- if (sc2->stc & (STCpure | STCnothrow | STCnogc))
- sc2 = sc->push();
- sc2->stc &= ~(STCpure | STCnothrow | STCnogc);
- dsymbolSemantic(e->declaration, sc2);
- if (sc2 != sc)
- sc2->pop();
- s->parent = sc->parent;
- }
- if (global.errors == olderrors)
- {
- semantic2(e->declaration, sc);
- if (global.errors == olderrors)
- {
- semantic3(e->declaration, sc);
- }
- }
- // todo: error in declaration should be propagated.
-
- e->type = Type::tvoid;
- result = e;
- }
-
- void visit(TypeidExp *exp)
- {
- Type *ta = isType(exp->obj);
- Expression *ea = isExpression(exp->obj);
- Dsymbol *sa = isDsymbol(exp->obj);
-
- //printf("ta %p ea %p sa %p\n", ta, ea, sa);
-
- if (ta)
- {
- ta->resolve(exp->loc, sc, &ea, &ta, &sa, true);
- }
-
- if (ea)
- {
- if (Dsymbol *sym = getDsymbol(ea))
- ea = resolve(exp->loc, sc, sym, false);
- else
- ea = expressionSemantic(ea, sc);
- ea = resolveProperties(sc, ea);
- ta = ea->type;
- if (ea->op == TOKtype)
- ea = NULL;
- }
-
- if (!ta)
- {
- //printf("ta %p ea %p sa %p\n", ta, ea, sa);
- exp->error("no type for typeid(%s)", ea ? ea->toChars() : (sa ? sa->toChars() : ""));
- return setError();
- }
-
- if (global.params.vcomplex)
- ta->checkComplexTransition(exp->loc);
-
- Expression *e;
- if (ea && ta->toBasetype()->ty == Tclass)
- {
- if (!Type::typeinfoclass)
- {
- error(exp->loc, "`object.TypeInfo_Class` could not be found, but is implicitly used");
- e = new ErrorExp();
- }
- else
- {
- /* Get the dynamic type, which is .classinfo
- */
- ea = expressionSemantic(ea, sc);
- e = new TypeidExp(ea->loc, ea);
- e->type = Type::typeinfoclass->type;
- }
- }
- else if (ta->ty == Terror)
- {
- e = new ErrorExp();
- }
- else
- {
- // Handle this in the glue layer
- e = new TypeidExp(exp->loc, ta);
- e->type = getTypeInfoType(exp->loc, ta, sc);
-
- semanticTypeInfo(sc, ta);
-
- if (ea)
- {
- e = new CommaExp(exp->loc, ea, e); // execute ea
- e = expressionSemantic(e, sc);
- }
- }
- result = e;
- }
-
- void visit(TraitsExp *e)
- {
- result = semanticTraits(e, sc);
- }
-
- void visit(HaltExp *e)
- {
- e->type = Type::tnoreturn;
- result = e;
- }
-
- void visit(IsExp *e)
- {
- /* is(targ id tok tspec)
- * is(targ id : tok2)
- * is(targ id == tok2)
- */
-
- //printf("IsExp::semantic(%s)\n", toChars());
- if (e->id && !(sc->flags & SCOPEcondition))
- {
- e->error("can only declare type aliases within static if conditionals or static asserts");
- return setError();
- }
-
- Type *tded = NULL;
- if (e->tok2 == TOKpackage || e->tok2 == TOKmodule) // These is() expressions are special because they can work on modules, not just types.
- {
- const unsigned oldErrors = global.startGagging();
- Dsymbol *sym = e->targ->toDsymbol(sc);
- global.endGagging(oldErrors);
- if (sym == NULL)
- goto Lno;
- Package *p = resolveIsPackage(sym);
- if (p == NULL)
- goto Lno;
- if (e->tok2 == TOKpackage && p->isModule()) // Note that isModule() will return null for package modules because they're not actually instances of Module.
- goto Lno;
- else if(e->tok2 == TOKmodule && !(p->isModule() || p->isPackageMod()))
- goto Lno;
- tded = e->targ;
- goto Lyes;
- }
-
- {
- Scope *sc2 = sc->copy(); // keep sc->flags
- sc2->tinst = NULL;
- sc2->minst = NULL;
- sc2->flags |= SCOPEfullinst;
- Type *t = e->targ->trySemantic(e->loc, sc2);
- sc2->pop();
- if (!t) // errors, so condition is false
- goto Lno;
- e->targ = t;
- }
-
- if (e->tok2 != TOKreserved)
- {
- switch (e->tok2)
- {
- case TOKstruct:
- if (e->targ->ty != Tstruct)
- goto Lno;
- if (((TypeStruct *)e->targ)->sym->isUnionDeclaration())
- goto Lno;
- tded = e->targ;
- break;
-
- case TOKunion:
- if (e->targ->ty != Tstruct)
- goto Lno;
- if (!((TypeStruct *)e->targ)->sym->isUnionDeclaration())
- goto Lno;
- tded = e->targ;
- break;
-
- case TOKclass:
- if (e->targ->ty != Tclass)
- goto Lno;
- if (((TypeClass *)e->targ)->sym->isInterfaceDeclaration())
- goto Lno;
- tded = e->targ;
- break;
-
- case TOKinterface:
- if (e->targ->ty != Tclass)
- goto Lno;
- if (!((TypeClass *)e->targ)->sym->isInterfaceDeclaration())
- goto Lno;
- tded = e->targ;
- break;
- case TOKconst:
- if (!e->targ->isConst())
- goto Lno;
- tded = e->targ;
- break;
-
- case TOKimmutable:
- if (!e->targ->isImmutable())
- goto Lno;
- tded = e->targ;
- break;
-
- case TOKshared:
- if (!e->targ->isShared())
- goto Lno;
- tded = e->targ;
- break;
-
- case TOKwild:
- if (!e->targ->isWild())
- goto Lno;
- tded = e->targ;
- break;
-
- case TOKsuper:
- // If class or interface, get the base class and interfaces
- if (e->targ->ty != Tclass)
- goto Lno;
- else
- {
- ClassDeclaration *cd = ((TypeClass *)e->targ)->sym;
- Parameters *args = new Parameters;
- args->reserve(cd->baseclasses->length);
- if (cd->semanticRun < PASSsemanticdone)
- dsymbolSemantic(cd, NULL);
- for (size_t i = 0; i < cd->baseclasses->length; i++)
- {
- BaseClass *b = (*cd->baseclasses)[i];
- args->push(new Parameter(STCin, b->type, NULL, NULL, NULL));
- }
- tded = new TypeTuple(args);
- }
- break;
-
- case TOKenum:
- if (e->targ->ty != Tenum)
- goto Lno;
- if (e->id)
- tded = ((TypeEnum *)e->targ)->sym->getMemtype(e->loc);
- else
- tded = e->targ;
- if (tded->ty == Terror)
- return setError();
- break;
-
- case TOKdelegate:
- if (e->targ->ty != Tdelegate)
- goto Lno;
- tded = ((TypeDelegate *)e->targ)->next; // the underlying function type
- break;
-
- case TOKfunction:
- case TOKparameters:
- {
- if (e->targ->ty != Tfunction)
- goto Lno;
- tded = e->targ;
-
- /* Generate tuple from function parameter types.
- */
- assert(tded->ty == Tfunction);
- TypeFunction *tdedf = (TypeFunction *)tded;
- size_t dim = tdedf->parameterList.length();
- Parameters *args = new Parameters;
- args->reserve(dim);
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *arg = tdedf->parameterList[i];
- assert(arg && arg->type);
- /* If one of the default arguments was an error,
- don't return an invalid tuple
- */
- if (e->tok2 == TOKparameters && arg->defaultArg &&
- arg->defaultArg->op == TOKerror)
- return setError();
- args->push(new Parameter(arg->storageClass, arg->type,
- (e->tok2 == TOKparameters) ? arg->ident : NULL,
- (e->tok2 == TOKparameters) ? arg->defaultArg : NULL,
- arg->userAttribDecl));
- }
- tded = new TypeTuple(args);
- break;
- }
- case TOKreturn:
- /* Get the 'return type' for the function,
- * delegate, or pointer to function.
- */
- if (e->targ->ty == Tfunction)
- tded = ((TypeFunction *)e->targ)->next;
- else if (e->targ->ty == Tdelegate)
- {
- tded = ((TypeDelegate *)e->targ)->next;
- tded = ((TypeFunction *)tded)->next;
- }
- else if (e->targ->ty == Tpointer &&
- ((TypePointer *)e->targ)->next->ty == Tfunction)
- {
- tded = ((TypePointer *)e->targ)->next;
- tded = ((TypeFunction *)tded)->next;
- }
- else
- goto Lno;
- break;
-
- case TOKargTypes:
- /* Generate a type tuple of the equivalent types used to determine if a
- * function argument of this type can be passed in registers.
- * The results of this are highly platform dependent, and intended
- * primarly for use in implementing va_arg().
- */
- tded = target.toArgTypes(e->targ);
- if (!tded)
- goto Lno; // not valid for a parameter
- break;
-
- case TOKvector:
- if (e->targ->ty != Tvector)
- goto Lno;
- tded = ((TypeVector *)e->targ)->basetype;
- break;
-
- default:
- assert(0);
- }
- goto Lyes;
- }
- else if (e->tspec && !e->id && !(e->parameters && e->parameters->length))
- {
- /* Evaluate to true if targ matches tspec
- * is(targ == tspec)
- * is(targ : tspec)
- */
- e->tspec = typeSemantic(e->tspec, e->loc, sc);
- //printf("targ = %s, %s\n", e->targ->toChars(), e->targ->deco);
- //printf("tspec = %s, %s\n", e->tspec->toChars(), e->tspec->deco);
- if (e->tok == TOKcolon)
- {
- if (e->targ->implicitConvTo(e->tspec))
- goto Lyes;
- else
- goto Lno;
- }
- else /* == */
- {
- if (e->targ->equals(e->tspec))
- goto Lyes;
- else
- goto Lno;
- }
- }
- else if (e->tspec)
- {
- /* Evaluate to true if targ matches tspec.
- * If true, declare id as an alias for the specialized type.
- * is(targ == tspec, tpl)
- * is(targ : tspec, tpl)
- * is(targ id == tspec)
- * is(targ id : tspec)
- * is(targ id == tspec, tpl)
- * is(targ id : tspec, tpl)
- */
-
- Identifier *tid = e->id ? e->id : Identifier::generateId("__isexp_id");
- e->parameters->insert(0, new TemplateTypeParameter(e->loc, tid, NULL, NULL));
-
- Objects dedtypes;
- dedtypes.setDim(e->parameters->length);
- dedtypes.zero();
-
- MATCH m = deduceType(e->targ, sc, e->tspec, e->parameters, &dedtypes);
- //printf("targ: %s\n", e->targ->toChars());
- //printf("tspec: %s\n", e->tspec->toChars());
- if (m <= MATCHnomatch ||
- (m != MATCHexact && e->tok == TOKequal))
- {
- goto Lno;
- }
- else
- {
- tded = (Type *)dedtypes[0];
- if (!tded)
- tded = e->targ;
- Objects tiargs;
- tiargs.setDim(1);
- tiargs[0] = e->targ;
-
- /* Declare trailing parameters
- */
- for (size_t i = 1; i < e->parameters->length; i++)
- {
- TemplateParameter *tp = (*e->parameters)[i];
- Declaration *s = NULL;
-
- m = tp->matchArg(e->loc, sc, &tiargs, i, e->parameters, &dedtypes, &s);
- if (m <= MATCHnomatch)
- goto Lno;
- dsymbolSemantic(s, sc);
- if (!sc->insert(s))
- e->error("declaration %s is already defined", s->toChars());
-
- unSpeculative(sc, s);
- }
- goto Lyes;
- }
- }
- else if (e->id)
- {
- /* Declare id as an alias for type targ. Evaluate to true
- * is(targ id)
- */
- tded = e->targ;
- goto Lyes;
- }
-
- Lyes:
- if (e->id)
- {
- Dsymbol *s;
- Tuple *tup = isTuple(tded);
- if (tup)
- s = new TupleDeclaration(e->loc, e->id, &(tup->objects));
- else
- s = new AliasDeclaration(e->loc, e->id, tded);
- dsymbolSemantic(s, sc);
- /* The reason for the !tup is unclear. It fails Phobos unittests if it is not there.
- * More investigation is needed.
- */
- if (!tup && !sc->insert(s))
- e->error("declaration %s is already defined", s->toChars());
-
- unSpeculative(sc, s);
- }
- //printf("Lyes\n");
- result = new IntegerExp(e->loc, 1, Type::tbool);
- return;
-
- Lno:
- //printf("Lno\n");
- result = new IntegerExp(e->loc, 0, Type::tbool);
- }
-
- void visit(BinAssignExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->e1->checkReadModifyWrite(exp->op, exp->e2))
- return setError();
-
- if (exp->e1->op == TOKarraylength)
- {
- // arr.length op= e2;
- e = rewriteOpAssign(exp);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- if (exp->e1->op == TOKslice || exp->e1->type->ty == Tarray || exp->e1->type->ty == Tsarray)
- {
- if (checkNonAssignmentArrayOp(exp->e1))
- return setError();
-
- if (exp->e1->op == TOKslice)
- ((SliceExp *)exp->e1)->arrayop = true;
-
- // T[] op= ...
- if (exp->e2->implicitConvTo(exp->e1->type->nextOf()))
- {
- // T[] op= T
- exp->e2 = exp->e2->castTo(sc, exp->e1->type->nextOf());
- }
- else if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
- exp->type = exp->e1->type;
- result = arrayOp(exp, sc);
- return;
- }
-
- exp->e1 = expressionSemantic(exp->e1, sc);
- exp->e1 = exp->e1->optimize(WANTvalue);
- exp->e1 = exp->e1->modifiableLvalue(sc, exp->e1);
- exp->type = exp->e1->type;
- if (exp->checkScalar())
- return setError();
-
- int arith = (exp->op == TOKaddass || exp->op == TOKminass || exp->op == TOKmulass ||
- exp->op == TOKdivass || exp->op == TOKmodass || exp->op == TOKpowass);
- int bitwise = (exp->op == TOKandass || exp->op == TOKorass || exp->op == TOKxorass);
- int shift = (exp->op == TOKshlass || exp->op == TOKshrass || exp->op == TOKushrass);
-
- if (bitwise && exp->type->toBasetype()->ty == Tbool)
- exp->e2 = exp->e2->implicitCastTo(sc, exp->type);
- else if (exp->checkNoBool())
- return setError();
-
- if ((exp->op == TOKaddass || exp->op == TOKminass) &&
- exp->e1->type->toBasetype()->ty == Tpointer &&
- exp->e2->type->toBasetype()->isintegral())
- {
- result = scaleFactor(exp, sc);
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- if (arith && exp->checkArithmeticBin())
- return setError();
- if ((bitwise || shift) && exp->checkIntegralBin())
- return setError();
- if (shift)
- {
- if (exp->e2->type->toBasetype()->ty != Tvector)
- exp->e2 = exp->e2->castTo(sc, Type::tshiftcnt);
- }
-
- if (!target.isVectorOpSupported(exp->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
-
- if (exp->e1->op == TOKerror || exp->e2->op == TOKerror)
- return setError();
-
- e = exp->checkOpAssignTypes(sc);
- if (e->op == TOKerror)
- {
- result = e;
- return;
- }
-
- assert(e->op == TOKassign || e == exp);
- result = ((BinExp *)e)->reorderSettingAAElem(sc);
- }
-
-private:
- Expression *compileIt(CompileExp *exp)
- {
- OutBuffer buf;
- if (expressionsToString(buf, sc, exp->exps))
- return NULL;
-
- unsigned errors = global.errors;
- const size_t len = buf.length();
- const char *str = buf.extractChars();
- Parser p(exp->loc, sc->_module, (const utf8_t *)str, len, false);
- p.nextToken();
- //printf("p.loc.linnum = %d\n", p.loc.linnum);
-
- Expression *e = p.parseExpression();
- if (global.errors != errors)
- return NULL;
-
- if (p.token.value != TOKeof)
- {
- exp->error("incomplete mixin expression (%s)", str);
- return NULL;
- }
- return e;
- }
-
-public:
- void visit(CompileExp *exp)
- {
- //printf("CompileExp::semantic('%s')\n", exp->toChars());
- Expression *e = compileIt(exp);
- if (!e)
- return setError();
- result = expressionSemantic(e, sc);
- }
-
- void visit(ImportExp *e)
- {
- StringExp *se = semanticString(sc, e->e1, "file name argument");
- if (!se)
- return setError();
- se = se->toUTF8(sc);
-
- const char *name = (char *)se->string;
- if (!global.params.fileImppath)
- {
- e->error("need -Jpath switch to import text file %s", name);
- return setError();
- }
-
- /* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
- * ('Path Traversal') attacks.
- * http://cwe.mitre.org/data/definitions/22.html
- */
-
- name = FileName::safeSearchPath(global.filePath, name);
- if (!name)
- {
- e->error("file %s cannot be found or not in a path specified with -J", se->toChars());
- return setError();
- }
-
- sc->_module->contentImportedFiles.push(name);
- if (global.params.verbose)
- message("file %.*s\t(%s)", (int)se->len, (char *)se->string, name);
- if (global.params.moduleDeps != NULL)
- {
- OutBuffer *ob = global.params.moduleDeps;
- Module* imod = sc->instantiatingModule();
-
- if (!global.params.moduleDepsFile.length)
- ob->writestring("depsFile ");
- ob->writestring(imod->toPrettyChars());
- ob->writestring(" (");
- escapePath(ob, imod->srcfile->toChars());
- ob->writestring(") : ");
- if (global.params.moduleDepsFile.length)
- ob->writestring("string : ");
- ob->writestring((char *) se->string);
- ob->writestring(" (");
- escapePath(ob, name);
- ob->writestring(")");
- ob->writenl();
- }
-
- {
- File f(name);
- if (f.read())
- {
- e->error("cannot read file %s", f.toChars());
- return setError();
- }
- else
- {
- f.ref = 1;
- se = new StringExp(e->loc, f.buffer, f.len);
- }
- }
- result = expressionSemantic(se, sc);
- }
-
- void visit(AssertExp *exp)
- {
- if (Expression *ex = unaSemantic(exp, sc))
- {
- result = ex;
- return;
- }
- exp->e1 = resolveProperties(sc, exp->e1);
- // BUG: see if we can do compile time elimination of the Assert
- exp->e1 = exp->e1->optimize(WANTvalue);
- exp->e1 = exp->e1->toBoolean(sc);
- if (exp->msg)
- {
- exp->msg = expressionSemantic(exp->msg, sc);
- exp->msg = resolveProperties(sc, exp->msg);
- exp->msg = exp->msg->implicitCastTo(sc, Type::tchar->constOf()->arrayOf());
- exp->msg = exp->msg->optimize(WANTvalue);
- }
-
- if (exp->e1->op == TOKerror)
- {
- result = exp->e1;
- return;
- }
- if (exp->msg && exp->msg->op == TOKerror)
- {
- result = exp->msg;
- return;
- }
-
- bool f1 = checkNonAssignmentArrayOp(exp->e1);
- bool f2 = exp->msg && checkNonAssignmentArrayOp(exp->msg);
- if (f1 || f2)
- return setError();
-
- if (exp->e1->isBool(false))
- {
- /* This is an `assert(0)` which means halt program execution
- */
- FuncDeclaration *fd = sc->parent->isFuncDeclaration();
- if (fd)
- fd->hasReturnExp |= 4;
- sc->callSuper |= CSXhalt;
- if (sc->fieldinit)
- {
- for (size_t i = 0; i < sc->fieldinit_dim; i++)
- sc->fieldinit[i] |= CSXhalt;
- }
-
- if (global.params.useAssert == CHECKENABLEoff)
- {
- Expression *e = new HaltExp(exp->loc);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- exp->type = Type::tnoreturn;
- }
- else
- exp->type = Type::tvoid;
- result = exp;
- }
-
- void visit(DotIdExp *exp)
- {
- Expression *e = semanticY(exp, sc, 1);
- if (e && isDotOpDispatch(e))
- {
- unsigned errors = global.startGagging();
- e = resolvePropertiesX(sc, e);
- if (global.endGagging(errors))
- e = NULL; /* fall down to UFCS */
- else
- {
- result = e;
- return;
- }
- }
- if (!e) // if failed to find the property
- {
- /* If ident is not a valid property, rewrite:
- * e1.ident
- * as:
- * .ident(e1)
- */
- e = resolveUFCSProperties(sc, exp);
- }
- result = e;
- }
-
- void visit(DotTemplateExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
- if (Expression *ex = unaSemantic(e, sc))
- {
- result = ex;
- return;
- }
- // 'void' like TemplateExp
- e->type = Type::tvoid;
- result = e;
- }
-
- void visit(DotVarExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- exp->var = exp->var->toAlias()->isDeclaration();
-
- exp->e1 = expressionSemantic(exp->e1, sc);
-
- if (TupleDeclaration *tup = exp->var->isTupleDeclaration())
- {
- /* Replace:
- * e1.tuple(a, b, c)
- * with:
- * tuple(e1.a, e1.b, e1.c)
- */
- Expression *e0 = NULL;
- Expression *ev = sc->func ? extractSideEffect(sc, "__tup", &e0, exp->e1) : exp->e1;
-
- Expressions *exps = new Expressions;
- exps->reserve(tup->objects->length);
- for (size_t i = 0; i < tup->objects->length; i++)
- {
- RootObject *o = (*tup->objects)[i];
- Expression *e;
- if (o->dyncast() == DYNCAST_EXPRESSION)
- {
- e = (Expression *)o;
- if (e->op == TOKdsymbol)
- {
- Dsymbol *s = ((DsymbolExp *)e)->s;
- e = new DotVarExp(exp->loc, ev, s->isDeclaration());
- }
- }
- else if (o->dyncast() == DYNCAST_DSYMBOL)
- {
- e = new DsymbolExp(exp->loc, (Dsymbol *)o);
- }
- else if (o->dyncast() == DYNCAST_TYPE)
- {
- e = new TypeExp(exp->loc, (Type *)o);
- }
- else
- {
- exp->error("%s is not an expression", o->toChars());
- return setError();
- }
- exps->push(e);
- }
-
- Expression *e = new TupleExp(exp->loc, e0, exps);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
-
- exp->e1 = exp->e1->addDtorHook(sc);
-
- Type *t1 = exp->e1->type;
-
- if (FuncDeclaration *fd = exp->var->isFuncDeclaration())
- {
- // for functions, do checks after overload resolution
- if (!fd->functionSemantic())
- return setError();
-
- /* Bugzilla 13843: If fd obviously has no overloads, we should
- * normalize AST, and it will give a chance to wrap fd with FuncExp.
- */
- if (fd->isNested() || fd->isFuncLiteralDeclaration())
- {
- // (e1, fd)
- Expression *e = resolve(exp->loc, sc, fd, false);
- result = Expression::combine(exp->e1, e);
- return;
- }
-
- exp->type = fd->type;
- assert(exp->type);
- }
- else if (exp->var->isOverDeclaration())
- {
- exp->type = Type::tvoid; // ambiguous type?
- }
- else
- {
- exp->type = exp->var->type;
- if (!exp->type && global.errors)
- {
- // var is goofed up, just return 0
- return setError();
- }
- assert(exp->type);
-
- if (t1->ty == Tpointer)
- t1 = t1->nextOf();
-
- exp->type = exp->type->addMod(t1->mod);
-
- Dsymbol *vparent = exp->var->toParent();
- AggregateDeclaration *ad = vparent ? vparent->isAggregateDeclaration() : NULL;
-
- if (Expression *e1x = getRightThis(exp->loc, sc, ad, exp->e1, exp->var, 1))
- exp->e1 = e1x;
- else
- {
- /* Later checkRightThis will report correct error for invalid field variable access.
- */
- Expression *e = new VarExp(exp->loc, exp->var);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- checkAccess(exp->loc, sc, exp->e1, exp->var);
-
- VarDeclaration *v = exp->var->isVarDeclaration();
- if (v && (v->isDataseg() || (v->storage_class & STCmanifest)))
- {
- Expression *e = expandVar(WANTvalue, v);
- if (e)
- {
- result = e;
- return;
- }
- }
-
- if (v && v->isDataseg()) // fix bugzilla 8238
- {
- // (e1, v)
- checkAccess(exp->loc, sc, exp->e1, v);
- Expression *e = new VarExp(exp->loc, v);
- e = new CommaExp(exp->loc, exp->e1, e);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- }
-
- //printf("-DotVarExp::semantic('%s')\n", exp->toChars());
- result = exp;
- }
-
- void visit(DotTemplateInstanceExp *exp)
- {
- // Indicate we need to resolve by UFCS.
- Expression *e = semanticY(exp, sc, 1);
- if (!e)
- e = resolveUFCSProperties(sc, exp);
- result = e;
- }
-
- void visit(DelegateExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- e->e1 = expressionSemantic(e->e1, sc);
- e->type = new TypeDelegate(e->func->type);
- e->type = typeSemantic(e->type, e->loc, sc);
- FuncDeclaration *f = e->func->toAliasFunc();
- AggregateDeclaration *ad = f->toParent()->isAggregateDeclaration();
- if (f->needThis())
- e->e1 = getRightThis(e->loc, sc, ad, e->e1, f);
- if (f->type->ty == Tfunction)
- {
- TypeFunction *tf = (TypeFunction *)f->type;
- if (!MODmethodConv(e->e1->type->mod, f->type->mod))
- {
- OutBuffer thisBuf, funcBuf;
- MODMatchToBuffer(&thisBuf, e->e1->type->mod, tf->mod);
- MODMatchToBuffer(&funcBuf, tf->mod, e->e1->type->mod);
- e->error("%smethod %s is not callable using a %s%s",
- funcBuf.peekChars(), f->toPrettyChars(), thisBuf.peekChars(), e->e1->toChars());
- return setError();
- }
- }
- if (ad && ad->isClassDeclaration() && ad->type != e->e1->type)
- {
- // A downcast is required for interfaces, see Bugzilla 3706
- e->e1 = new CastExp(e->loc, e->e1, ad->type);
- e->e1 = expressionSemantic(e->e1, sc);
- }
- result = e;
- }
-
- void visit(DotTypeExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *e = unaSemantic(exp, sc))
- {
- result = e;
- return;
- }
-
- exp->type = exp->sym->getType()->addMod(exp->e1->type->mod);
- result = exp;
- }
-
- void visit(CallExp *exp)
- {
- if (exp->type)
- {
- result = exp; // semantic() already run
- return;
- }
-
- Type *t1;
- Objects *tiargs = NULL; // initial list of template arguments
- Expression *ethis = NULL;
- Type *tthis = NULL;
- Expression *e1org = exp->e1;
-
- if (exp->e1->op == TOKcomma)
- {
- /* Rewrite (a,b)(args) as (a,(b(args)))
- */
- CommaExp *ce = (CommaExp *)exp->e1;
- exp->e1 = ce->e2;
- ce->e2 = exp;
- result = expressionSemantic(ce, sc);
- return;
- }
-
- if (exp->e1->op == TOKdelegate)
- {
- DelegateExp *de = (DelegateExp *)exp->e1;
- exp->e1 = new DotVarExp(de->loc, de->e1, de->func, de->hasOverloads);
- result = expressionSemantic(exp, sc);
- return;
- }
-
- if (exp->e1->op == TOKfunction)
- {
- if (arrayExpressionSemantic(exp->arguments, sc) ||
- preFunctionParameters(sc, exp->arguments))
- {
- return setError();
- }
-
- // Run e1 semantic even if arguments have any errors
- FuncExp *fe = (FuncExp *)exp->e1;
- exp->e1 = callExpSemantic(fe, sc, exp->arguments);
- if (exp->e1->op == TOKerror)
- {
- result = exp->e1;
- return;
- }
- }
-
- if (Expression *ex = resolveUFCS(sc, exp))
- {
- result = ex;
- return;
- }
-
- /* This recognizes:
- * foo!(tiargs)(funcargs)
- */
- if (exp->e1->op == TOKscope)
- {
- ScopeExp *se = (ScopeExp *)exp->e1;
- TemplateInstance *ti = se->sds->isTemplateInstance();
- if (ti)
- {
- /* Attempt to instantiate ti. If that works, go with it.
- * If not, go with partial explicit specialization.
- */
- WithScopeSymbol *withsym;
- if (!ti->findTempDecl(sc, &withsym) ||
- !ti->semanticTiargs(sc))
- {
- return setError();
- }
- if (withsym && withsym->withstate->wthis)
- {
- exp->e1 = new VarExp(exp->e1->loc, withsym->withstate->wthis);
- exp->e1 = new DotTemplateInstanceExp(exp->e1->loc, exp->e1, ti);
- goto Ldotti;
- }
- if (ti->needsTypeInference(sc, 1))
- {
- /* Go with partial explicit specialization
- */
- tiargs = ti->tiargs;
- assert(ti->tempdecl);
- if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration())
- exp->e1 = new TemplateExp(exp->loc, td);
- else if (OverDeclaration *od = ti->tempdecl->isOverDeclaration())
- exp->e1 = new VarExp(exp->loc, od);
- else
- exp->e1 = new OverExp(exp->loc, ti->tempdecl->isOverloadSet());
- }
- else
- {
- Expression *e1x = expressionSemantic(exp->e1, sc);
- if (e1x->op == TOKerror)
- {
- result = e1x;
- return;
- }
- exp->e1 = e1x;
- }
- }
- }
-
- /* This recognizes:
- * expr.foo!(tiargs)(funcargs)
- */
- Ldotti:
- if (exp->e1->op == TOKdotti && !exp->e1->type)
- {
- DotTemplateInstanceExp *se = (DotTemplateInstanceExp *)exp->e1;
- TemplateInstance *ti = se->ti;
- {
- /* Attempt to instantiate ti. If that works, go with it.
- * If not, go with partial explicit specialization.
- */
- if (!se->findTempDecl(sc) ||
- !ti->semanticTiargs(sc))
- {
- return setError();
- }
- if (ti->needsTypeInference(sc, 1))
- {
- /* Go with partial explicit specialization
- */
- tiargs = ti->tiargs;
- assert(ti->tempdecl);
- if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration())
- exp->e1 = new DotTemplateExp(exp->loc, se->e1, td);
- else if (OverDeclaration *od = ti->tempdecl->isOverDeclaration())
- {
- exp->e1 = new DotVarExp(exp->loc, se->e1, od, true);
- }
- else
- exp->e1 = new DotExp(exp->loc, se->e1, new OverExp(exp->loc, ti->tempdecl->isOverloadSet()));
- }
- else
- {
- Expression *e1x = expressionSemantic(exp->e1, sc);
- if (e1x->op == TOKerror)
- {
- result =e1x;
- return;
- }
- exp->e1 = e1x;
- }
- }
- }
-
- Lagain:
- //printf("Lagain: %s\n", exp->toChars());
- exp->f = NULL;
- if (exp->e1->op == TOKthis || exp->e1->op == TOKsuper)
- {
- // semantic() run later for these
- }
- else
- {
- if (exp->e1->op == TOKdotid)
- {
- DotIdExp *die = (DotIdExp *)exp->e1;
- exp->e1 = expressionSemantic(die, sc);
- /* Look for e1 having been rewritten to expr.opDispatch!(string)
- * We handle such earlier, so go back.
- * Note that in the rewrite, we carefully did not run semantic() on e1
- */
- if (exp->e1->op == TOKdotti && !exp->e1->type)
- {
- goto Ldotti;
- }
- }
- else
- {
- static int nest;
- if (++nest > global.recursionLimit)
- {
- exp->error("recursive evaluation of %s", exp->toChars());
- --nest;
- return setError();
- }
- Expression *ex = unaSemantic(exp, sc);
- --nest;
- if (ex)
- {
- result = ex;
- return;
- }
- }
-
- /* Look for e1 being a lazy parameter
- */
- if (exp->e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)exp->e1;
- if (ve->var->storage_class & STClazy)
- {
- // lazy paramaters can be called without violating purity and safety
- Type *tw = ve->var->type;
- Type *tc = ve->var->type->substWildTo(MODconst);
- TypeFunction *tf = new TypeFunction(ParameterList(), tc, LINKd, STCsafe | STCpure);
- (tf = (TypeFunction *)typeSemantic(tf, exp->loc, sc))->next = tw; // hack for bug7757
- TypeDelegate *t = new TypeDelegate(tf);
- ve->type = typeSemantic(t, exp->loc, sc);
- }
- VarDeclaration *v = ve->var->isVarDeclaration();
- if (v && ve->checkPurity(sc, v))
- return setError();
- }
-
- if (exp->e1->op == TOKsymoff && ((SymOffExp *)exp->e1)->hasOverloads)
- {
- SymOffExp *se = (SymOffExp *)exp->e1;
- exp->e1 = new VarExp(se->loc, se->var, true);
- exp->e1 = expressionSemantic(exp->e1, sc);
- }
- else if (exp->e1->op == TOKdot)
- {
- DotExp *de = (DotExp *) exp->e1;
-
- if (de->e2->op == TOKoverloadset)
- {
- ethis = de->e1;
- tthis = de->e1->type;
- exp->e1 = de->e2;
- }
- }
- else if (exp->e1->op == TOKstar && exp->e1->type->ty == Tfunction)
- {
- // Rewrite (*fp)(arguments) to fp(arguments)
- exp->e1 = ((PtrExp *)exp->e1)->e1;
- }
- }
-
- t1 = exp->e1->type ? exp->e1->type->toBasetype() : NULL;
-
- if (exp->e1->op == TOKerror)
- {
- result = exp->e1;
- return;
- }
- if (arrayExpressionSemantic(exp->arguments, sc) ||
- preFunctionParameters(sc, exp->arguments))
- {
- return setError();
- }
-
- // Check for call operator overload
- if (t1)
- {
- if (t1->ty == Tstruct)
- {
- StructDeclaration *sd = ((TypeStruct *)t1)->sym;
- sd->size(exp->loc); // Resolve forward references to construct object
- if (sd->sizeok != SIZEOKdone)
- return setError();
- if (!sd->ctor)
- sd->ctor = sd->searchCtor();
-
- // First look for constructor
- if (exp->e1->op == TOKtype && sd->ctor)
- {
- if (!sd->noDefaultCtor && !(exp->arguments && exp->arguments->length))
- goto Lx;
-
- StructLiteralExp *sle = new StructLiteralExp(exp->loc, sd, NULL, exp->e1->type);
- if (!sd->fill(exp->loc, sle->elements, true))
- return setError();
- if (checkFrameAccess(exp->loc, sc, sd, sle->elements->length))
- return setError();
- // Bugzilla 14556: Set concrete type to avoid further redundant semantic().
- sle->type = exp->e1->type;
-
- /* Constructor takes a mutable object, so don't use
- * the immutable initializer symbol.
- */
- sle->useStaticInit = false;
-
- Expression *e = sle;
- if (CtorDeclaration *cf = sd->ctor->isCtorDeclaration())
- {
- e = new DotVarExp(exp->loc, e, cf, true);
- }
- else if (TemplateDeclaration *td = sd->ctor->isTemplateDeclaration())
- {
- e = new DotTemplateExp(exp->loc, e, td);
- }
- else if (OverloadSet *os = sd->ctor->isOverloadSet())
- {
- e = new DotExp(exp->loc, e, new OverExp(exp->loc, os));
- }
- else
- assert(0);
- e = new CallExp(exp->loc, e, exp->arguments);
- result = expressionSemantic(e, sc);
- return;
- }
- // No constructor, look for overload of opCall
- if (search_function(sd, Id::call))
- goto L1; // overload of opCall, therefore it's a call
-
- if (exp->e1->op != TOKtype)
- {
- if (sd->aliasthis && exp->e1->type != exp->att1)
- {
- if (!exp->att1 && exp->e1->type->checkAliasThisRec())
- exp->att1 = exp->e1->type;
- exp->e1 = resolveAliasThis(sc, exp->e1);
- goto Lagain;
- }
- exp->error("%s %s does not overload ()", sd->kind(), sd->toChars());
- return setError();
- }
-
- /* It's a struct literal
- */
- Lx:
- Expression *e = new StructLiteralExp(exp->loc, sd, exp->arguments, exp->e1->type);
- result = expressionSemantic(e, sc);
- return;
- }
- else if (t1->ty == Tclass)
- {
- L1:
- // Rewrite as e1.call(arguments)
- Expression *e = new DotIdExp(exp->loc, exp->e1, Id::call);
- e = new CallExp(exp->loc, e, exp->arguments);
- result = expressionSemantic(e, sc);
- return;
- }
- else if (exp->e1->op == TOKtype && t1->isscalar())
- {
- Expression *e;
-
- // Make sure to use the the enum type itself rather than its
- // base type (see bugzilla 16346)
- if (exp->e1->type->ty == Tenum)
- {
- t1 = exp->e1->type;
- }
-
- if (!exp->arguments || exp->arguments->length == 0)
- {
- e = t1->defaultInitLiteral(exp->loc);
- }
- else if (exp->arguments->length == 1)
- {
- e = (*exp->arguments)[0];
- e = e->implicitCastTo(sc, t1);
- e = new CastExp(exp->loc, e, t1);
- }
- else
- {
- exp->error("more than one argument for construction of %s", t1->toChars());
- e = new ErrorExp();
- }
- result = expressionSemantic(e, sc);
- return;
- }
- }
-
- if ((exp->e1->op == TOKdotvar && t1->ty == Tfunction) ||
- exp->e1->op == TOKdottd)
- {
- UnaExp *ue = (UnaExp *)(exp->e1);
-
- Expression *ue1 = ue->e1;
- Expression *ue1old = ue1; // need for 'right this' check
- VarDeclaration *v;
- if (ue1->op == TOKvar &&
- (v = ((VarExp *)ue1)->var->isVarDeclaration()) != NULL &&
- v->needThis())
- {
- ue->e1 = new TypeExp(ue1->loc, ue1->type);
- ue1 = NULL;
- }
-
- DotVarExp *dve;
- DotTemplateExp *dte;
- Dsymbol *s;
- if (exp->e1->op == TOKdotvar)
- {
- dve = (DotVarExp *)(exp->e1);
- dte = NULL;
- s = dve->var;
- tiargs = NULL;
- }
- else
- {
- dve = NULL;
- dte = (DotTemplateExp *)(exp->e1);
- s = dte->td;
- }
-
- // Do overload resolution
- exp->f = resolveFuncCall(exp->loc, sc, s, tiargs, ue1 ? ue1->type : NULL, exp->arguments);
- if (!exp->f || exp->f->errors || exp->f->type->ty == Terror)
- return setError();
- if (exp->f->interfaceVirtual)
- {
- /* Cast 'this' to the type of the interface, and replace f with the interface's equivalent
- */
- BaseClass *b = exp->f->interfaceVirtual;
- ClassDeclaration *ad2 = b->sym;
- ue->e1 = ue->e1->castTo(sc, ad2->type->addMod(ue->e1->type->mod));
- ue->e1 = expressionSemantic(ue->e1, sc);
- ue1 = ue->e1;
- int vi = exp->f->findVtblIndex((Dsymbols*)&ad2->vtbl, (int)ad2->vtbl.length);
- assert(vi >= 0);
- exp->f = ad2->vtbl[vi]->isFuncDeclaration();
- assert(exp->f);
- }
- if (exp->f->needThis())
- {
- AggregateDeclaration *ad = exp->f->toParent2()->isAggregateDeclaration();
- ue->e1 = getRightThis(exp->loc, sc, ad, ue->e1, exp->f);
- if (ue->e1->op == TOKerror)
- {
- result = ue->e1;
- return;
- }
- ethis = ue->e1;
- tthis = ue->e1->type;
- if (!(exp->f->type->ty == Tfunction && ((TypeFunction *)exp->f->type)->isscope))
- {
- if (global.params.vsafe && checkParamArgumentEscape(sc, exp->f, Id::This, ethis, false))
- return setError();
- }
- }
-
- /* Cannot call public functions from inside invariant
- * (because then the invariant would have infinite recursion)
- */
- if (sc->func && sc->func->isInvariantDeclaration() &&
- ue->e1->op == TOKthis &&
- exp->f->addPostInvariant()
- )
- {
- exp->error("cannot call public/export function %s from invariant", exp->f->toChars());
- return setError();
- }
-
- exp->checkDeprecated(sc, exp->f);
- exp->checkDisabled(sc, exp->f);
- exp->checkPurity(sc, exp->f);
- exp->checkSafety(sc, exp->f);
- exp->checkNogc(sc, exp->f);
- checkAccess(exp->loc, sc, ue->e1, exp->f);
- if (!exp->f->needThis())
- {
- exp->e1 = Expression::combine(ue->e1, new VarExp(exp->loc, exp->f, false));
- }
- else
- {
- if (ue1old->checkRightThis(sc))
- return setError();
- if (exp->e1->op == TOKdotvar)
- {
- dve->var = exp->f;
- exp->e1->type = exp->f->type;
- }
- else
- {
- exp->e1 = new DotVarExp(exp->loc, dte->e1, exp->f, false);
- exp->e1 = expressionSemantic(exp->e1, sc);
- if (exp->e1->op == TOKerror)
- return setError();
- ue = (UnaExp *)exp->e1;
- }
-
- // See if we need to adjust the 'this' pointer
- AggregateDeclaration *ad = exp->f->isThis();
- ClassDeclaration *cd = ue->e1->type->isClassHandle();
- if (ad && cd && ad->isClassDeclaration())
- {
- if (ue->e1->op == TOKdottype)
- {
- ue->e1 = ((DotTypeExp *)ue->e1)->e1;
- exp->directcall = true;
- }
- else if (ue->e1->op == TOKsuper)
- exp->directcall = true;
- else if ((cd->storage_class & STCfinal) != 0) // Bugzilla 14211
- exp->directcall = true;
-
- if (ad != cd)
- {
- ue->e1 = ue->e1->castTo(sc, ad->type->addMod(ue->e1->type->mod));
- ue->e1 = expressionSemantic(ue->e1, sc);
- }
- }
- }
- // If we've got a pointer to a function then deference it
- // https://issues.dlang.org/show_bug.cgi?id=16483
- if (exp->e1->type->ty == Tpointer && exp->e1->type->nextOf()->ty == Tfunction)
- {
- Expression *e = new PtrExp(exp->loc, exp->e1);
- e->type = exp->e1->type->nextOf();
- exp->e1 = e;
- }
- t1 = exp->e1->type;
- }
- else if (exp->e1->op == TOKsuper)
- {
- // Base class constructor call
- AggregateDeclaration *ad = sc->func ? sc->func->isThis() : NULL;
- ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL;
- if (!cd || !cd->baseClass || !sc->func->isCtorDeclaration())
- {
- exp->error("super class constructor call must be in a constructor");
- return setError();
- }
- if (!cd->baseClass->ctor)
- {
- exp->error("no super class constructor for %s", cd->baseClass->toChars());
- return setError();
- }
-
- if (!sc->intypeof && !(sc->callSuper & CSXhalt))
- {
- if (sc->noctor || sc->callSuper & CSXlabel)
- exp->error("constructor calls not allowed in loops or after labels");
- if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor))
- exp->error("multiple constructor calls");
- if ((sc->callSuper & CSXreturn) && !(sc->callSuper & CSXany_ctor))
- exp->error("an earlier return statement skips constructor");
- sc->callSuper |= CSXany_ctor | CSXsuper_ctor;
- }
-
- tthis = cd->type->addMod(sc->func->type->mod);
- if (OverloadSet *os = cd->baseClass->ctor->isOverloadSet())
- exp->f = resolveOverloadSet(exp->loc, sc, os, NULL, tthis, exp->arguments);
- else
- exp->f = resolveFuncCall(exp->loc, sc, cd->baseClass->ctor, NULL, tthis, exp->arguments, 0);
- if (!exp->f || exp->f->errors)
- return setError();
- exp->checkDeprecated(sc, exp->f);
- exp->checkDisabled(sc, exp->f);
- exp->checkPurity(sc, exp->f);
- exp->checkSafety(sc, exp->f);
- exp->checkNogc(sc, exp->f);
- checkAccess(exp->loc, sc, NULL, exp->f);
-
- exp->e1 = new DotVarExp(exp->e1->loc, exp->e1, exp->f, false);
- exp->e1 = expressionSemantic(exp->e1, sc);
- t1 = exp->e1->type;
- }
- else if (exp->e1->op == TOKthis)
- {
- // same class constructor call
- AggregateDeclaration *ad = sc->func ? sc->func->isThis() : NULL;
- if (!ad || !sc->func->isCtorDeclaration())
- {
- exp->error("constructor call must be in a constructor");
- return setError();
- }
-
- // https://issues.dlang.org/show_bug.cgi?id=18719
- // If `exp` is a call expression to another constructor
- // then it means that all struct/class fields will be
- // initialized after this call.
- for (size_t i = 0; i < sc->fieldinit_dim; i++)
- sc->fieldinit[i] |= CSXthis_ctor;
-
- if (!sc->intypeof && !(sc->callSuper & CSXhalt))
- {
- if (sc->noctor || sc->callSuper & CSXlabel)
- exp->error("constructor calls not allowed in loops or after labels");
- if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor))
- exp->error("multiple constructor calls");
- if ((sc->callSuper & CSXreturn) && !(sc->callSuper & CSXany_ctor))
- exp->error("an earlier return statement skips constructor");
- sc->callSuper |= CSXany_ctor | CSXthis_ctor;
- }
-
- tthis = ad->type->addMod(sc->func->type->mod);
- if (OverloadSet *os = ad->ctor->isOverloadSet())
- exp->f = resolveOverloadSet(exp->loc, sc, os, NULL, tthis, exp->arguments);
- else
- exp->f = resolveFuncCall(exp->loc, sc, ad->ctor, NULL, tthis, exp->arguments, 0);
- if (!exp->f || exp->f->errors)
- return setError();
- exp->checkDeprecated(sc, exp->f);
- exp->checkDisabled(sc, exp->f);
- exp->checkPurity(sc, exp->f);
- exp->checkSafety(sc, exp->f);
- exp->checkNogc(sc, exp->f);
- //checkAccess(exp->loc, sc, NULL, exp->f); // necessary?
-
- exp->e1 = new DotVarExp(exp->e1->loc, exp->e1, exp->f, false);
- exp->e1 = expressionSemantic(exp->e1, sc);
- t1 = exp->e1->type;
-
- // BUG: this should really be done by checking the static
- // call graph
- if (exp->f == sc->func)
- {
- exp->error("cyclic constructor call");
- return setError();
- }
- }
- else if (exp->e1->op == TOKoverloadset)
- {
- OverloadSet *os = ((OverExp *)exp->e1)->vars;
- exp->f = resolveOverloadSet(exp->loc, sc, os, tiargs, tthis, exp->arguments);
- if (!exp->f)
- return setError();
- if (ethis)
- exp->e1 = new DotVarExp(exp->loc, ethis, exp->f, false);
- else
- exp->e1 = new VarExp(exp->loc, exp->f, false);
- goto Lagain;
- }
- else if (!t1)
- {
- exp->error("function expected before (), not `%s`", exp->e1->toChars());
- return setError();
- }
- else if (t1->ty == Terror)
- {
- return setError();
- }
- else if (t1->ty != Tfunction)
- {
- TypeFunction *tf;
- const char *p;
- Dsymbol *s;
- exp->f = NULL;
- if (exp->e1->op == TOKfunction)
- {
- // function literal that direct called is always inferred.
- assert(((FuncExp *)exp->e1)->fd);
- exp->f = ((FuncExp *)exp->e1)->fd;
- tf = (TypeFunction *)exp->f->type;
- p = "function literal";
- }
- else if (t1->ty == Tdelegate)
- {
- TypeDelegate *td = (TypeDelegate *)t1;
- assert(td->next->ty == Tfunction);
- tf = (TypeFunction *)(td->next);
- p = "delegate";
- }
- else if (t1->ty == Tpointer && ((TypePointer *)t1)->next->ty == Tfunction)
- {
- tf = (TypeFunction *)(((TypePointer *)t1)->next);
- p = "function pointer";
- }
- else if (exp->e1->op == TOKdotvar &&
- ((DotVarExp *)exp->e1)->var->isOverDeclaration())
- {
- DotVarExp *dve = (DotVarExp *)exp->e1;
- exp->f = resolveFuncCall(exp->loc, sc, dve->var, tiargs, dve->e1->type, exp->arguments, 2);
- if (!exp->f)
- return setError();
- if (exp->f->needThis())
- {
- dve->var = exp->f;
- dve->type = exp->f->type;
- dve->hasOverloads = false;
- goto Lagain;
- }
- exp->e1 = new VarExp(dve->loc, exp->f, false);
- Expression *e = new CommaExp(exp->loc, dve->e1, exp);
- result = expressionSemantic(e, sc);
- return;
- }
- else if (exp->e1->op == TOKvar &&
- ((VarExp *)exp->e1)->var->isOverDeclaration())
- {
- s = ((VarExp *)exp->e1)->var;
- goto L2;
- }
- else if (exp->e1->op == TOKtemplate)
- {
- s = ((TemplateExp *)exp->e1)->td;
- L2:
- exp->f = resolveFuncCall(exp->loc, sc, s, tiargs, NULL, exp->arguments);
- if (!exp->f || exp->f->errors)
- return setError();
- if (exp->f->needThis())
- {
- if (hasThis(sc))
- {
- // Supply an implicit 'this', as in
- // this.ident
- Expression *ex = new ThisExp(exp->loc);
- ex = expressionSemantic(ex, sc);
- exp->e1 = new DotVarExp(exp->loc, ex, exp->f, false);
- goto Lagain;
- }
- else if (isNeedThisScope(sc, exp->f))
- {
- exp->error("need `this` for `%s` of type `%s`", exp->f->toChars(), exp->f->type->toChars());
- return setError();
- }
- }
- exp->e1 = new VarExp(exp->e1->loc, exp->f, false);
- goto Lagain;
- }
- else
- {
- exp->error("function expected before (), not %s of type %s", exp->e1->toChars(), exp->e1->type->toChars());
- return setError();
- }
-
- const char *failMessage = NULL;
- if (!tf->callMatch(NULL, exp->arguments, 0, &failMessage))
- {
- OutBuffer buf;
-
- buf.writeByte('(');
- argExpTypesToCBuffer(&buf, exp->arguments);
- buf.writeByte(')');
- if (tthis)
- tthis->modToBuffer(&buf);
-
- //printf("tf = %s, args = %s\n", tf->deco, (*exp->arguments)[0]->type->deco);
- ::error(exp->loc, "%s `%s%s` is not callable using argument types `%s`",
- p, exp->e1->toChars(), parametersTypeToChars(tf->parameterList),
- buf.peekChars());
- if (failMessage)
- errorSupplemental(exp->loc, failMessage);
- return setError();
- }
-
- // Purity and safety check should run after testing arguments matching
- if (exp->f)
- {
- exp->checkPurity(sc, exp->f);
- exp->checkSafety(sc, exp->f);
- exp->checkNogc(sc, exp->f);
- if (exp->f->checkNestedReference(sc, exp->loc))
- return setError();
- }
- else if (sc->func && sc->intypeof != 1 && !(sc->flags & SCOPEctfe))
- {
- bool err = false;
- if (!tf->purity && !(sc->flags & SCOPEdebug) && sc->func->setImpure())
- {
- exp->error("pure %s `%s` cannot call impure %s `%s`",
- sc->func->kind(), sc->func->toPrettyChars(), p, exp->e1->toChars());
- err = true;
- }
- if (!tf->isnogc && sc->func->setGC())
- {
- exp->error("@nogc %s `%s` cannot call non-@nogc %s `%s`",
- sc->func->kind(), sc->func->toPrettyChars(), p, exp->e1->toChars());
- err = true;
- }
- if (tf->trust <= TRUSTsystem && sc->func->setUnsafe())
- {
- exp->error("@safe %s `%s` cannot call @system %s `%s`",
- sc->func->kind(), sc->func->toPrettyChars(), p, exp->e1->toChars());
- err = true;
- }
- if (err)
- return setError();
- }
-
- if (t1->ty == Tpointer)
- {
- Expression *e = new PtrExp(exp->loc, exp->e1);
- e->type = tf;
- exp->e1 = e;
- }
- t1 = tf;
- }
- else if (exp->e1->op == TOKvar)
- {
- // Do overload resolution
- VarExp *ve = (VarExp *)exp->e1;
-
- exp->f = ve->var->isFuncDeclaration();
- assert(exp->f);
- tiargs = NULL;
-
- if (exp->f->overnext)
- exp->f = resolveFuncCall(exp->loc, sc, exp->f, tiargs, NULL, exp->arguments, 2);
- else
- {
- exp->f = exp->f->toAliasFunc();
- TypeFunction *tf = (TypeFunction *)exp->f->type;
- const char *failMessage = NULL;
- if (!tf->callMatch(NULL, exp->arguments, 0, &failMessage))
- {
- OutBuffer buf;
-
- buf.writeByte('(');
- argExpTypesToCBuffer(&buf, exp->arguments);
- buf.writeByte(')');
-
- //printf("tf = %s, args = %s\n", tf->deco, (*exp->arguments)[0]->type->deco);
- ::error(exp->loc, "%s `%s%s` is not callable using argument types `%s`",
- exp->f->kind(), exp->e1->toChars(), parametersTypeToChars(tf->parameterList),
- buf.peekChars());
- if (failMessage)
- errorSupplemental(exp->loc, failMessage);
- exp->f = NULL;
- }
- }
- if (!exp->f || exp->f->errors)
- return setError();
-
- if (exp->f->needThis())
- {
- // Change the ancestor lambdas to delegate before hasThis(sc) call.
- if (exp->f->checkNestedReference(sc, exp->loc))
- return setError();
-
- if (hasThis(sc))
- {
- // Supply an implicit 'this', as in
- // this.ident
-
- Expression *ex = new ThisExp(exp->loc);
- ex = expressionSemantic(ex, sc);
- exp->e1 = new DotVarExp(exp->loc, ex, ve->var);
- // Note: we cannot use f directly, because further overload resolution
- // through the supplied 'this' may cause different result.
- goto Lagain;
- }
- else if (isNeedThisScope(sc, exp->f))
- {
- exp->error("need `this` for `%s` of type `%s`", exp->f->toChars(), exp->f->type->toChars());
- return setError();
- }
- }
-
- exp->checkDeprecated(sc, exp->f);
- exp->checkDisabled(sc, exp->f);
- exp->checkPurity(sc, exp->f);
- exp->checkSafety(sc, exp->f);
- exp->checkNogc(sc, exp->f);
- checkAccess(exp->loc, sc, NULL, exp->f);
- if (exp->f->checkNestedReference(sc, exp->loc))
- return setError();
-
- ethis = NULL;
- tthis = NULL;
-
- if (ve->hasOverloads)
- {
- exp->e1 = new VarExp(ve->loc, exp->f, false);
- exp->e1->type = exp->f->type;
- }
- t1 = exp->f->type;
- }
- assert(t1->ty == Tfunction);
-
- Expression *argprefix;
- if (!exp->arguments)
- exp->arguments = new Expressions();
- if (functionParameters(exp->loc, sc, (TypeFunction *)(t1), tthis, exp->arguments, exp->f, &exp->type, &argprefix))
- return setError();
-
- if (!exp->type)
- {
- exp->e1 = e1org; // Bugzilla 10922, avoid recursive expression printing
- exp->error("forward reference to inferred return type of function call `%s`", exp->toChars());
- return setError();
- }
-
- if (exp->f && exp->f->tintro)
- {
- Type *t = exp->type;
- int offset = 0;
- TypeFunction *tf = (TypeFunction *)exp->f->tintro;
-
- if (tf->next->isBaseOf(t, &offset) && offset)
- {
- exp->type = tf->next;
- result = Expression::combine(argprefix, exp->castTo(sc, t));
- return;
- }
- }
-
- // Handle the case of a direct lambda call
- if (exp->f && exp->f->isFuncLiteralDeclaration() &&
- sc->func && !sc->intypeof)
- {
- exp->f->tookAddressOf = 0;
- }
-
- result = Expression::combine(argprefix, exp);
- }
-
- void visit(AddrExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = unaSemantic(exp, sc))
- {
- result = ex;
- return;
- }
- int wasCond = exp->e1->op == TOKquestion;
- if (exp->e1->op == TOKdotti)
- {
- DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)exp->e1;
- TemplateInstance *ti = dti->ti;
- {
- //assert(ti->needsTypeInference(sc));
- dsymbolSemantic(ti, sc);
- if (!ti->inst || ti->errors) // if template failed to expand
- return setError();
- Dsymbol *s = ti->toAlias();
- FuncDeclaration *f = s->isFuncDeclaration();
- if (f)
- {
- exp->e1 = new DotVarExp(exp->e1->loc, dti->e1, f);
- exp->e1 = expressionSemantic(exp->e1, sc);
- }
- }
- }
- else if (exp->e1->op == TOKscope)
- {
- TemplateInstance *ti = ((ScopeExp *)exp->e1)->sds->isTemplateInstance();
- if (ti)
- {
- //assert(ti->needsTypeInference(sc));
- dsymbolSemantic(ti, sc);
- if (!ti->inst || ti->errors) // if template failed to expand
- return setError();
- Dsymbol *s = ti->toAlias();
- FuncDeclaration *f = s->isFuncDeclaration();
- if (f)
- {
- exp->e1 = new VarExp(exp->e1->loc, f);
- exp->e1 = expressionSemantic(exp->e1, sc);
- }
- }
- }
- exp->e1 = exp->e1->toLvalue(sc, NULL);
- if (exp->e1->op == TOKerror)
- {
- result = exp->e1;
- return;
- }
- if (checkNonAssignmentArrayOp(exp->e1))
- return setError();
-
- if (!exp->e1->type)
- {
- exp->error("cannot take address of %s", exp->e1->toChars());
- return setError();
- }
-
- bool hasOverloads = false;
- if (FuncDeclaration *f = isFuncAddress(exp, &hasOverloads))
- {
- if (!hasOverloads && f->checkForwardRef(exp->loc))
- return setError();
- }
- else if (!exp->e1->type->deco)
- {
- if (exp->e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)exp->e1;
- Declaration *d = ve->var;
- exp->error("forward reference to %s %s", d->kind(), d->toChars());
- }
- else
- exp->error("forward reference to %s", exp->e1->toChars());
- return setError();
- }
-
- exp->type = exp->e1->type->pointerTo();
-
- // See if this should really be a delegate
- if (exp->e1->op == TOKdotvar)
- {
- DotVarExp *dve = (DotVarExp *)exp->e1;
- FuncDeclaration *f = dve->var->isFuncDeclaration();
- if (f)
- {
- f = f->toAliasFunc(); // FIXME, should see overloads - Bugzilla 1983
- if (!dve->hasOverloads)
- f->tookAddressOf++;
-
- Expression *e;
- if (f->needThis())
- e = new DelegateExp(exp->loc, dve->e1, f, dve->hasOverloads);
- else // It is a function pointer. Convert &v.f() --> (v, &V.f())
- e = new CommaExp(exp->loc, dve->e1, new AddrExp(exp->loc, new VarExp(exp->loc, f, dve->hasOverloads)));
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
-
- // Look for misaligned pointer in @safe mode
- if (checkUnsafeAccess(sc, dve, !exp->type->isMutable(), true))
- return setError();
-
- if (dve->e1->op == TOKvar && global.params.vsafe)
- {
- VarExp *ve = (VarExp *)dve->e1;
- VarDeclaration *v = ve->var->isVarDeclaration();
- if (v)
- {
- if (!checkAddressVar(sc, exp, v))
- return setError();
- }
- }
- else if ((dve->e1->op == TOKthis || dve->e1->op == TOKsuper) && global.params.vsafe)
- {
- ThisExp *ve = (ThisExp *)dve->e1;
- VarDeclaration *v = ve->var->isVarDeclaration();
- if (v && v->storage_class & STCref)
- {
- if (!checkAddressVar(sc, exp, v))
- return setError();
- }
- }
- }
- else if (exp->e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)exp->e1;
-
- VarDeclaration *v = ve->var->isVarDeclaration();
- if (v)
- {
- if (!checkAddressVar(sc, exp, v))
- return setError();
-
- ve->checkPurity(sc, v);
- }
-
- FuncDeclaration *f = ve->var->isFuncDeclaration();
- if (f)
- {
- /* Because nested functions cannot be overloaded,
- * mark here that we took its address because castTo()
- * may not be called with an exact match.
- */
- if (!ve->hasOverloads || f->isNested())
- f->tookAddressOf++;
- if (f->isNested())
- {
- if (f->isFuncLiteralDeclaration())
- {
- if (!f->FuncDeclaration::isNested())
- {
- /* Supply a 'null' for a this pointer if no this is available
- */
- Expression *e = new DelegateExp(exp->loc, new NullExp(exp->loc, Type::tnull), f, ve->hasOverloads);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- }
- Expression *e = new DelegateExp(exp->loc, exp->e1, f, ve->hasOverloads);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- if (f->needThis())
- {
- if (hasThis(sc))
- {
- /* Should probably supply 'this' after overload resolution,
- * not before.
- */
- Expression *ethis = new ThisExp(exp->loc);
- Expression *e = new DelegateExp(exp->loc, ethis, f, ve->hasOverloads);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- if (sc->func && !sc->intypeof)
- {
- if (sc->func->setUnsafe())
- {
- exp->error("`this` reference necessary to take address of member %s in @safe function %s",
- f->toChars(), sc->func->toChars());
- }
- }
- }
- }
- }
- else if ((exp->e1->op == TOKthis || exp->e1->op == TOKsuper) && global.params.vsafe)
- {
- ThisExp *ve = (ThisExp *)exp->e1;
- VarDeclaration *v = ve->var->isVarDeclaration();
- if (v)
- {
- if (!checkAddressVar(sc, exp, v))
- return setError();
- }
- }
- else if (exp->e1->op == TOKcall)
- {
- CallExp *ce = (CallExp *)exp->e1;
- if (ce->e1->type->ty == Tfunction)
- {
- TypeFunction *tf = (TypeFunction *)ce->e1->type;
- if (tf->isref && sc->func && !sc->intypeof && sc->func->setUnsafe())
- {
- exp->error("cannot take address of ref return of %s() in @safe function %s",
- ce->e1->toChars(), sc->func->toChars());
- }
- }
- }
- else if (exp->e1->op == TOKindex)
- {
- /* For:
- * int[3] a;
- * &a[i]
- * check 'a' the same as for a regular variable
- */
- IndexExp *ei = (IndexExp *)exp->e1;
- Type *tyi = ei->e1->type->toBasetype();
- if (tyi->ty == Tsarray && ei->e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)ei->e1;
- VarDeclaration *v = ve->var->isVarDeclaration();
- if (v)
- {
- if (!checkAddressVar(sc, exp, v))
- return setError();
-
- ve->checkPurity(sc, v);
- }
- }
- }
- else if (wasCond)
- {
- /* a ? b : c was transformed to *(a ? &b : &c), but we still
- * need to do safety checks
- */
- assert(exp->e1->op == TOKstar);
- PtrExp *pe = (PtrExp *)exp->e1;
- assert(pe->e1->op == TOKquestion);
- CondExp *ce = (CondExp *)pe->e1;
- assert(ce->e1->op == TOKaddress);
- assert(ce->e2->op == TOKaddress);
-
- // Re-run semantic on the address expressions only
- ce->e1->type = NULL;
- ce->e1 = expressionSemantic(ce->e1, sc);
- ce->e2->type = NULL;
- ce->e2 = expressionSemantic(ce->e2, sc);
- }
-
- result = exp->optimize(WANTvalue);
- }
-
- void visit(PtrExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- Type *tb = exp->e1->type->toBasetype();
- switch (tb->ty)
- {
- case Tpointer:
- exp->type = ((TypePointer *)tb)->next;
- break;
-
- case Tsarray:
- case Tarray:
- exp->error("using * on an array is no longer supported; use *(%s).ptr instead", exp->e1->toChars());
- exp->type = ((TypeArray *)tb)->next;
- exp->e1 = exp->e1->castTo(sc, exp->type->pointerTo());
- break;
-
- case Tnull:
- exp->type = Type::tnoreturn; // typeof(*null) is bottom type
- break;
-
- default:
- exp->error("can only * a pointer, not a `%s`", exp->e1->type->toChars());
- /* fall through */
-
- case Terror:
- return setError();
- }
- if (exp->checkValue())
- return setError();
-
- result = exp;
- }
-
- void visit(NegExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- exp->type = exp->e1->type;
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp->e1))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- if (!target.isVectorOpSupported(tb, exp->op))
- {
- result = exp->incompatibleTypes();
- return;
- }
- if (exp->e1->checkNoBool())
- return setError();
- if (exp->e1->checkArithmetic())
- return setError();
-
- result = exp;
- }
-
- void visit(UAddExp *exp)
- {
- assert(!exp->type);
-
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op))
- {
- result = exp->incompatibleTypes();
- return;
- }
- if (exp->e1->checkNoBool())
- return setError();
- if (exp->e1->checkArithmetic())
- return setError();
-
- result = exp->e1;
- }
-
- void visit(ComExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- exp->type = exp->e1->type;
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp->e1))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- if (!target.isVectorOpSupported(tb, exp->op))
- {
- result = exp->incompatibleTypes();
- return;
- }
- if (exp->e1->checkNoBool())
- return setError();
- if (exp->e1->checkIntegral())
- return setError();
-
- result = exp;
- }
-
- void visit(NotExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- setNoderefOperand(e);
-
- // Note there is no operator overload
- if (Expression *ex = unaSemantic(e, sc))
- {
- result = ex;
- return;
- }
-
- // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
- if (e->e1->op == TOKtype)
- e->e1 = resolveAliasThis(sc, e->e1);
-
- e->e1 = resolveProperties(sc, e->e1);
- e->e1 = e->e1->toBoolean(sc);
- if (e->e1->type == Type::terror)
- {
- result = e->e1;
- return;
- }
-
- if (!target.isVectorOpSupported(e->e1->type->toBasetype(), e->op))
- {
- result = e->incompatibleTypes();
- return;
- }
- // Bugzilla 13910: Today NotExp can take an array as its operand.
- if (checkNonAssignmentArrayOp(e->e1))
- return setError();
-
- e->type = Type::tbool;
- result = e;
- }
-
- void visit(DeleteExp *exp)
- {
- if (Expression *ex = unaSemantic(exp, sc))
- {
- result = ex;
- return;
- }
- exp->e1 = resolveProperties(sc, exp->e1);
- exp->e1 = exp->e1->modifiableLvalue(sc, NULL);
- if (exp->e1->op == TOKerror)
- {
- result = exp->e1;
- return;
- }
- exp->type = Type::tvoid;
-
- AggregateDeclaration *ad = NULL;
- Type *tb = exp->e1->type->toBasetype();
- switch (tb->ty)
- { case Tclass:
- {
- ClassDeclaration *cd = ((TypeClass *)tb)->sym;
-
- if (cd->isCOMinterface())
- { /* Because COM classes are deleted by IUnknown.Release()
- */
- exp->error("cannot delete instance of COM interface %s", cd->toChars());
- return setError();
- }
-
- ad = cd;
- break;
- }
- case Tpointer:
- tb = ((TypePointer *)tb)->next->toBasetype();
- if (tb->ty == Tstruct)
- {
- ad = ((TypeStruct *)tb)->sym;
- FuncDeclaration *f = ad->aggDelete;
- FuncDeclaration *fd = ad->dtor;
-
- if (!f)
- {
- semanticTypeInfo(sc, tb);
- break;
- }
-
- /* Construct:
- * ea = copy e1 to a tmp to do side effects only once
- * eb = call destructor
- * ec = call deallocator
- */
- Expression *ea = NULL;
- Expression *eb = NULL;
- Expression *ec = NULL;
- VarDeclaration *v = NULL;
-
- if (fd && f)
- {
- v = copyToTemp(0, "__tmpea", exp->e1);
- dsymbolSemantic(v, sc);
- ea = new DeclarationExp(exp->loc, v);
- ea->type = v->type;
- }
-
- if (fd)
- {
- Expression *e = ea ? new VarExp(exp->loc, v) : exp->e1;
- e = new DotVarExp(Loc(), e, fd, false);
- eb = new CallExp(exp->loc, e);
- eb = expressionSemantic(eb, sc);
- }
-
- if (f)
- {
- Type *tpv = Type::tvoid->pointerTo();
- Expression *e = ea ? new VarExp(exp->loc, v) : exp->e1->castTo(sc, tpv);
- e = new CallExp(exp->loc, new VarExp(exp->loc, f, false), e);
- ec = expressionSemantic(e, sc);
- }
- ea = Expression::combine(ea, eb);
- ea = Expression::combine(ea, ec);
- assert(ea);
- result = ea;
- return;
- }
- break;
-
- case Tarray:
- {
- Type *tv = tb->nextOf()->baseElemOf();
- if (tv->ty == Tstruct)
- {
- ad = ((TypeStruct *)tv)->sym;
- if (ad->dtor)
- semanticTypeInfo(sc, ad->type);
- }
- break;
- }
- default:
- exp->error("cannot delete type %s", exp->e1->type->toChars());
- return setError();
- }
-
- bool err = false;
- if (ad)
- {
- if (ad->dtor)
- {
- err |= exp->checkPurity(sc, ad->dtor);
- err |= exp->checkSafety(sc, ad->dtor);
- err |= exp->checkNogc(sc, ad->dtor);
- }
- if (ad->aggDelete && tb->ty != Tarray)
- {
- err |= exp->checkPurity(sc, ad->aggDelete);
- err |= exp->checkSafety(sc, ad->aggDelete);
- err |= exp->checkNogc(sc, ad->aggDelete);
- }
- if (err)
- return setError();
- }
-
- if (!sc->intypeof && sc->func &&
- !exp->isRAII &&
- sc->func->setUnsafe())
- {
- exp->error("%s is not @safe but is used in @safe function %s", exp->toChars(), sc->func->toChars());
- err = true;
- }
- if (err)
- return setError();
-
- result = exp;
- }
-
- void visit(CastExp *exp)
- {
- //static int x; assert(++x < 10);
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (exp->to)
- {
- exp->to = typeSemantic(exp->to, exp->loc, sc);
- if (exp->to == Type::terror)
- return setError();
-
- if (!exp->to->hasPointers())
- setNoderefOperand(exp);
-
- // When e1 is a template lambda, this cast may instantiate it with
- // the type 'to'.
- exp->e1 = inferType(exp->e1, exp->to);
- }
-
- if (Expression *ex = unaSemantic(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e1x = resolveProperties(sc, exp->e1);
- if (e1x->op == TOKerror)
- {
- result = e1x;
- return;
- }
- if (e1x->checkType())
- return setError();
- exp->e1 = e1x;
-
- if (!exp->e1->type)
- {
- exp->error("cannot cast %s", exp->e1->toChars());
- return setError();
- }
-
- if (!exp->to) // Handle cast(const) and cast(immutable), etc.
- {
- exp->to = exp->e1->type->castMod(exp->mod);
- exp->to = typeSemantic(exp->to, exp->loc, sc);
- if (exp->to == Type::terror)
- return setError();
- }
-
- if (exp->to->ty == Ttuple)
- {
- exp->error("cannot cast %s to tuple type %s", exp->e1->toChars(), exp->to->toChars());
- return setError();
- }
- if (exp->e1->type->ty != Tvoid ||
- (exp->e1->op == TOKfunction && exp->to->ty == Tvoid) ||
- exp->e1->op == TOKtype ||
- exp->e1->op == TOKtemplate)
- {
- if (exp->e1->checkValue())
- return setError();
- }
-
- // cast(void) is used to mark e1 as unused, so it is safe
- if (exp->to->ty == Tvoid)
- {
- exp->type = exp->to;
- result = exp;
- return;
- }
-
- if (!exp->to->equals(exp->e1->type) && exp->mod == (unsigned char)~0)
- {
- if (Expression *e = exp->op_overload(sc))
- {
- result = e->implicitCastTo(sc, exp->to);
- return;
- }
- }
-
- Type *t1b = exp->e1->type->toBasetype();
- Type *tob = exp->to->toBasetype();
-
- if (tob->ty == Tstruct && !tob->equals(t1b))
- {
- /* Look to replace:
- * cast(S)t
- * with:
- * S(t)
- */
-
- // Rewrite as to.call(e1)
- Expression *e = new TypeExp(exp->loc, exp->to);
- e = new CallExp(exp->loc, e, exp->e1);
- e = trySemantic(e, sc);
- if (e)
- {
- result = e;
- return;
- }
- }
-
- if (!t1b->equals(tob) && (t1b->ty == Tarray || t1b->ty == Tsarray))
- {
- if (checkNonAssignmentArrayOp(exp->e1))
- return setError();
- }
-
- // Look for casting to a vector type
- if (tob->ty == Tvector && t1b->ty != Tvector)
- {
- result = new VectorExp(exp->loc, exp->e1, exp->to);
- return;
- }
-
- Expression *ex = exp->e1->castTo(sc, exp->to);
- if (ex->op == TOKerror)
- {
- result = ex;
- return;
- }
-
- // Check for unsafe casts
- if (sc->func && !sc->intypeof &&
- !isSafeCast(ex, t1b, tob) &&
- sc->func->setUnsafe())
- {
- exp->error("cast from %s to %s not allowed in safe code", exp->e1->type->toChars(), exp->to->toChars());
- return setError();
- }
-
- result = ex;
- }
-
- void visit(VectorExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- exp->e1 = expressionSemantic(exp->e1, sc);
- exp->type = typeSemantic(exp->to, exp->loc, sc);
- if (exp->e1->op == TOKerror || exp->type->ty == Terror)
- {
- result = exp->e1;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- assert(tb->ty == Tvector);
- TypeVector *tv = (TypeVector *)tb;
- Type *te = tv->elementType();
- exp->dim = (int)(tv->size(exp->loc) / te->size(exp->loc));
-
- exp->e1 = exp->e1->optimize(WANTvalue);
- bool res = false;
- if (exp->e1->op == TOKarrayliteral)
- {
- for (size_t i = 0; i < exp->dim; i++)
- {
- // Do not stop on first error - check all AST nodes even if error found
- res |= checkVectorElem(exp, ((ArrayLiteralExp *)exp->e1)->getElement(i));
- }
- }
- else if (exp->e1->type->ty == Tvoid)
- res = checkVectorElem(exp, exp->e1);
-
- Expression *e = exp;
- if (res)
- e = new ErrorExp();
- result = e;
- }
-
- void visit(VectorArrayExp *e)
- {
- if (!e->type)
- {
- unaSemantic(e, sc);
- e->e1 = resolveProperties(sc, e->e1);
-
- if (e->e1->op == TOKerror)
- {
- result = e->e1;
- return;
- }
- assert(e->e1->type->ty == Tvector);
- TypeVector *tv = (TypeVector *)e->e1->type;
- e->type = tv->basetype;
- }
- result = e;
- }
-
- void visit(SliceExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- // operator overloading should be handled in ArrayExp already.
-
- if (Expression *ex = unaSemantic(exp, sc))
- {
- result = ex;
- return;
- }
- exp->e1 = resolveProperties(sc, exp->e1);
- if (exp->e1->op == TOKtype && exp->e1->type->ty != Ttuple)
- {
- if (exp->lwr || exp->upr)
- {
- exp->error("cannot slice type `%s`", exp->e1->toChars());
- return setError();
- }
- Expression *e = new TypeExp(exp->loc, exp->e1->type->arrayOf());
- result = expressionSemantic(e, sc);
- return;
- }
- if (!exp->lwr && !exp->upr)
- {
- if (exp->e1->op == TOKarrayliteral)
- {
- // Convert [a,b,c][] to [a,b,c]
- Type *t1b = exp->e1->type->toBasetype();
- Expression *e = exp->e1;
- if (t1b->ty == Tsarray)
- {
- e = e->copy();
- e->type = t1b->nextOf()->arrayOf();
- }
- result = e;
- return;
- }
- if (exp->e1->op == TOKslice)
- {
- // Convert e[][] to e[]
- SliceExp *se = (SliceExp *)exp->e1;
- if (!se->lwr && !se->upr)
- {
- result = se;
- return;
- }
- }
- if (isArrayOpOperand(exp->e1))
- {
- // Convert (a[]+b[])[] to a[]+b[]
- result = exp->e1;
- return;
- }
- }
- if (exp->e1->op == TOKerror)
- {
- result = exp->e1;
- return;
- }
- if (exp->e1->type->ty == Terror)
- return setError();
-
- Type *t1b = exp->e1->type->toBasetype();
- if (t1b->ty == Tpointer)
- {
- if (((TypePointer *)t1b)->next->ty == Tfunction)
- {
- exp->error("cannot slice function pointer %s", exp->e1->toChars());
- return setError();
- }
- if (!exp->lwr || !exp->upr)
- {
- exp->error("need upper and lower bound to slice pointer");
- return setError();
- }
- if (sc->func && !sc->intypeof && sc->func->setUnsafe())
- {
- exp->error("pointer slicing not allowed in safe functions");
- return setError();
- }
- }
- else if (t1b->ty == Tarray)
- {
- }
- else if (t1b->ty == Tsarray)
- {
- if (!exp->arrayop && global.params.vsafe)
- {
- /* Slicing a static array is like taking the address of it.
- * Perform checks as if e[] was &e
- */
- VarDeclaration *v = NULL;
- if (exp->e1->op == TOKdotvar)
- {
- DotVarExp *dve = (DotVarExp *)exp->e1;
- if (dve->e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)dve->e1;
- v = ve->var->isVarDeclaration();
- }
- else if (dve->e1->op == TOKthis || dve->e1->op == TOKsuper)
- {
- ThisExp *ve = (ThisExp *)dve->e1;
- v = ve->var->isVarDeclaration();
- if (v && !(v->storage_class & STCref))
- v = NULL;
- }
- }
- else if (exp->e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)exp->e1;
- v = ve->var->isVarDeclaration();
- }
- else if (exp->e1->op == TOKthis || exp->e1->op == TOKsuper)
- {
- ThisExp *ve = (ThisExp *)exp->e1;
- v = ve->var->isVarDeclaration();
- }
-
- if (v)
- {
- if (!checkAddressVar(sc, exp, v))
- return setError();
- }
- }
- }
- else if (t1b->ty == Ttuple)
- {
- if (!exp->lwr && !exp->upr)
- {
- result = exp->e1;
- return;
- }
- if (!exp->lwr || !exp->upr)
- {
- exp->error("need upper and lower bound to slice tuple");
- return setError();
- }
- }
- else if (t1b->ty == Tvector)
- {
- // Convert e1 to corresponding static array
- TypeVector *tv1 = (TypeVector *)t1b;
- t1b = tv1->basetype;
- t1b = t1b->castMod(tv1->mod);
- exp->e1->type = t1b;
- }
- else
- {
- exp->error("%s cannot be sliced with []",
- t1b->ty == Tvoid ? exp->e1->toChars() : t1b->toChars());
- return setError();
- }
-
- /* Run semantic on lwr and upr.
- */
- Scope *scx = sc;
- if (t1b->ty == Tsarray || t1b->ty == Tarray || t1b->ty == Ttuple)
- {
- // Create scope for 'length' variable
- ScopeDsymbol *sym = new ArrayScopeSymbol(sc, exp);
- sym->loc = exp->loc;
- sym->parent = sc->scopesym;
- sc = sc->push(sym);
- }
- if (exp->lwr)
- {
- if (t1b->ty == Ttuple) sc = sc->startCTFE();
- exp->lwr = expressionSemantic(exp->lwr, sc);
- exp->lwr = resolveProperties(sc, exp->lwr);
- if (t1b->ty == Ttuple) sc = sc->endCTFE();
- exp->lwr = exp->lwr->implicitCastTo(sc, Type::tsize_t);
- }
- if (exp->upr)
- {
- if (t1b->ty == Ttuple) sc = sc->startCTFE();
- exp->upr = expressionSemantic(exp->upr, sc);
- exp->upr = resolveProperties(sc, exp->upr);
- if (t1b->ty == Ttuple) sc = sc->endCTFE();
- exp->upr = exp->upr->implicitCastTo(sc, Type::tsize_t);
- }
- if (sc != scx)
- sc = sc->pop();
- if ((exp->lwr && exp->lwr->type == Type::terror) ||
- (exp->upr && exp->upr->type == Type::terror))
- {
- return setError();
- }
-
- if (t1b->ty == Ttuple)
- {
- exp->lwr = exp->lwr->ctfeInterpret();
- exp->upr = exp->upr->ctfeInterpret();
- uinteger_t i1 = exp->lwr->toUInteger();
- uinteger_t i2 = exp->upr->toUInteger();
-
- TupleExp *te;
- TypeTuple *tup;
- size_t length;
- if (exp->e1->op == TOKtuple) // slicing an expression tuple
- {
- te = (TupleExp *)exp->e1;
- tup = NULL;
- length = te->exps->length;
- }
- else if (exp->e1->op == TOKtype) // slicing a type tuple
- {
- te = NULL;
- tup = (TypeTuple *)t1b;
- length = Parameter::dim(tup->arguments);
- }
- else
- assert(0);
-
- if (i2 < i1 || length < i2)
- {
- exp->error("string slice [%llu .. %llu] is out of bounds", i1, i2);
- return setError();
- }
-
- size_t j1 = (size_t) i1;
- size_t j2 = (size_t) i2;
- Expression *e;
- if (exp->e1->op == TOKtuple)
- {
- Expressions *exps = new Expressions;
- exps->setDim(j2 - j1);
- for (size_t i = 0; i < j2 - j1; i++)
- {
- (*exps)[i] = (*te->exps)[j1 + i];
- }
- e = new TupleExp(exp->loc, te->e0, exps);
- }
- else
- {
- Parameters *args = new Parameters;
- args->reserve(j2 - j1);
- for (size_t i = j1; i < j2; i++)
- {
- Parameter *arg = Parameter::getNth(tup->arguments, i);
- args->push(arg);
- }
- e = new TypeExp(exp->e1->loc, new TypeTuple(args));
- }
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
-
- exp->type = t1b->nextOf()->arrayOf();
- // Allow typedef[] -> typedef[]
- if (exp->type->equals(t1b))
- exp->type = exp->e1->type;
-
- if (exp->lwr && exp->upr)
- {
- exp->lwr = exp->lwr->optimize(WANTvalue);
- exp->upr = exp->upr->optimize(WANTvalue);
-
- IntRange lwrRange = getIntRange(exp->lwr);
- IntRange uprRange = getIntRange(exp->upr);
-
- if (t1b->ty == Tsarray || t1b->ty == Tarray)
- {
- Expression *el = new ArrayLengthExp(exp->loc, exp->e1);
- el = expressionSemantic(el, sc);
- el = el->optimize(WANTvalue);
- if (el->op == TOKint64)
- {
- dinteger_t length = el->toInteger();
- IntRange bounds(SignExtendedNumber(0), SignExtendedNumber(length));
- exp->upperIsInBounds = bounds.contains(uprRange);
- }
- }
- else if (t1b->ty == Tpointer)
- {
- exp->upperIsInBounds = true;
- }
- else
- assert(0);
-
- exp->lowerIsLessThanUpper = (lwrRange.imax <= uprRange.imin);
-
- //printf("upperIsInBounds = %d lowerIsLessThanUpper = %d\n", upperIsInBounds, lowerIsLessThanUpper);
- }
-
- result = exp;
- }
-
- void visit(ArrayLengthExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- if (Expression *ex = unaSemantic(e, sc))
- {
- result = ex;
- return;
- }
- e->e1 = resolveProperties(sc, e->e1);
-
- e->type = Type::tsize_t;
- result = e;
- }
-
- void visit(IntervalExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- Expression *le = e->lwr;
- le = expressionSemantic(le, sc);
- le = resolveProperties(sc, le);
-
- Expression *ue = e->upr;
- ue = expressionSemantic(ue, sc);
- ue = resolveProperties(sc, ue);
-
- if (le->op == TOKerror)
- {
- result = le;
- return;
- }
- if (ue->op == TOKerror)
- {
- result = ue;
- return;
- }
-
- e->lwr = le;
- e->upr = ue;
-
- e->type = Type::tvoid;
- result = e;
- }
-
- void visit(DelegatePtrExp *e)
- {
- if (!e->type)
- {
- unaSemantic(e, sc);
- e->e1 = resolveProperties(sc, e->e1);
-
- if (e->e1->op == TOKerror)
- {
- result = e->e1;
- return;
- }
- e->type = Type::tvoidptr;
- }
- result = e;
- }
-
- void visit(DelegateFuncptrExp *e)
- {
- if (!e->type)
- {
- unaSemantic(e, sc);
- e->e1 = resolveProperties(sc, e->e1);
-
- if (e->e1->op == TOKerror)
- {
- result = e->e1;
- return;
- }
- e->type = e->e1->type->nextOf()->pointerTo();
- }
- result = e;
- }
-
- void visit(ArrayExp *exp)
- {
- assert(!exp->type);
-
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (isAggregate(exp->e1->type))
- exp->error("no [] operator overload for type %s", exp->e1->type->toChars());
- else
- exp->error("only one index allowed to index %s", exp->e1->type->toChars());
- return setError();
- }
-
- void visit(DotExp *exp)
- {
- exp->e1 = expressionSemantic(exp->e1, sc);
- exp->e2 = expressionSemantic(exp->e2, sc);
-
- if (exp->e1->op == TOKtype)
- {
- result = exp->e2;
- return;
- }
- if (exp->e2->op == TOKtype)
- {
- result = exp->e2;
- return;
- }
- if (exp->e2->op == TOKtemplate)
- {
- TemplateDeclaration *td = ((TemplateExp *)exp->e2)->td;
- Expression *e = new DotTemplateExp(exp->loc, exp->e1, td);
- result = expressionSemantic(e, sc);
- return;
- }
- if (!exp->type)
- exp->type = exp->e2->type;
- result = exp;
- }
-
- void visit(CommaExp *e)
- {
- if (e->type)
- {
- result = e;
- return;
- }
-
- // Allow `((a,b),(x,y))`
- if (e->allowCommaExp)
- {
- if (e->e1 && e->e1->op == TOKcomma)
- ((CommaExp *)e->e1)->allowCommaExp = true;
- if (e->e2 && e->e2->op == TOKcomma)
- ((CommaExp *)e->e2)->allowCommaExp = true;
- }
-
- if (Expression *ex = binSemanticProp(e, sc))
- {
- result = ex;
- return;
- }
- e->e1 = e->e1->addDtorHook(sc);
-
- if (checkNonAssignmentArrayOp(e->e1))
- return setError();
-
- e->type = e->e2->type;
- if (e->type != Type::tvoid && !e->allowCommaExp && !e->isGenerated)
- e->deprecation("Using the result of a comma expression is deprecated");
- result = e;
- }
-
- void visit(IndexExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- // operator overloading should be handled in ArrayExp already.
-
- if (!exp->e1->type)
- exp->e1 = expressionSemantic(exp->e1, sc);
- assert(exp->e1->type); // semantic() should already be run on it
- if (exp->e1->op == TOKtype && exp->e1->type->ty != Ttuple)
- {
- exp->e2 = expressionSemantic(exp->e2, sc);
- exp->e2 = resolveProperties(sc, exp->e2);
- Type *nt;
- if (exp->e2->op == TOKtype)
- nt = new TypeAArray(exp->e1->type, exp->e2->type);
- else
- nt = new TypeSArray(exp->e1->type, exp->e2);
- Expression *e = new TypeExp(exp->loc, nt);
- result = expressionSemantic(e, sc);
- return;
- }
- if (exp->e1->op == TOKerror)
- {
- result = exp->e1;
- return;
- }
- if (exp->e1->type->ty == Terror)
- return setError();
-
- // Note that unlike C we do not implement the int[ptr]
-
- Type *t1b = exp->e1->type->toBasetype();
-
- if (t1b->ty == Tvector)
- {
- // Convert e1 to corresponding static array
- TypeVector *tv1 = (TypeVector *)t1b;
- t1b = tv1->basetype;
- t1b = t1b->castMod(tv1->mod);
- exp->e1->type = t1b;
- }
-
- /* Run semantic on e2
- */
- Scope *scx = sc;
- if (t1b->ty == Tsarray || t1b->ty == Tarray || t1b->ty == Ttuple)
- {
- // Create scope for 'length' variable
- ScopeDsymbol *sym = new ArrayScopeSymbol(sc, exp);
- sym->loc = exp->loc;
- sym->parent = sc->scopesym;
- sc = sc->push(sym);
- }
- if (t1b->ty == Ttuple) sc = sc->startCTFE();
- exp->e2 = expressionSemantic(exp->e2, sc);
- exp->e2 = resolveProperties(sc, exp->e2);
- if (t1b->ty == Ttuple) sc = sc->endCTFE();
- if (exp->e2->op == TOKtuple)
- {
- TupleExp *te = (TupleExp *)exp->e2;
- if (te->exps && te->exps->length == 1)
- exp->e2 = Expression::combine(te->e0, (*te->exps)[0]); // bug 4444 fix
- }
- if (sc != scx)
- sc = sc->pop();
- if (exp->e2->type == Type::terror)
- return setError();
-
- if (checkNonAssignmentArrayOp(exp->e1))
- return setError();
-
- switch (t1b->ty)
- {
- case Tpointer:
- if (((TypePointer *)t1b)->next->ty == Tfunction)
- {
- exp->error("cannot index function pointer %s", exp->e1->toChars());
- return setError();
- }
- exp->e2 = exp->e2->implicitCastTo(sc, Type::tsize_t);
- if (exp->e2->type == Type::terror)
- return setError();
- exp->e2 = exp->e2->optimize(WANTvalue);
- if (exp->e2->op == TOKint64 && exp->e2->toInteger() == 0)
- ;
- else if (sc->func && sc->func->setUnsafe())
- {
- exp->error("safe function `%s` cannot index pointer `%s`",
- sc->func->toPrettyChars(), exp->e1->toChars());
- return setError();
- }
- exp->type = ((TypeNext *)t1b)->next;
- break;
-
- case Tarray:
- exp->e2 = exp->e2->implicitCastTo(sc, Type::tsize_t);
- if (exp->e2->type == Type::terror)
- return setError();
- exp->type = ((TypeNext *)t1b)->next;
- break;
-
- case Tsarray:
- {
- exp->e2 = exp->e2->implicitCastTo(sc, Type::tsize_t);
- if (exp->e2->type == Type::terror)
- return setError();
- exp->type = t1b->nextOf();
- break;
- }
-
- case Taarray:
- {
- TypeAArray *taa = (TypeAArray *)t1b;
- /* We can skip the implicit conversion if they differ only by
- * constness (Bugzilla 2684, see also bug 2954b)
- */
- if (!arrayTypeCompatibleWithoutCasting(exp->e2->type, taa->index))
- {
- exp->e2 = exp->e2->implicitCastTo(sc, taa->index); // type checking
- if (exp->e2->type == Type::terror)
- return setError();
- }
-
- semanticTypeInfo(sc, taa);
-
- exp->type = taa->next;
- break;
- }
-
- case Ttuple:
- {
- exp->e2 = exp->e2->implicitCastTo(sc, Type::tsize_t);
- if (exp->e2->type == Type::terror)
- return setError();
- exp->e2 = exp->e2->ctfeInterpret();
- uinteger_t index = exp->e2->toUInteger();
-
- TupleExp *te;
- TypeTuple *tup;
- size_t length;
- if (exp->e1->op == TOKtuple)
- {
- te = (TupleExp *)exp->e1;
- tup = NULL;
- length = te->exps->length;
- }
- else if (exp->e1->op == TOKtype)
- {
- te = NULL;
- tup = (TypeTuple *)t1b;
- length = Parameter::dim(tup->arguments);
- }
- else
- assert(0);
-
- if (length <= index)
- {
- exp->error("array index [%llu] is outside array bounds [0 .. %llu]",
- index, (ulonglong)length);
- return setError();
- }
-
- Expression *e;
- if (exp->e1->op == TOKtuple)
- {
- e = (*te->exps)[(size_t)index];
- e = Expression::combine(te->e0, e);
- }
- else
- e = new TypeExp(exp->e1->loc, Parameter::getNth(tup->arguments, (size_t)index)->type);
- result = e;
- return;
- }
-
- default:
- exp->error("%s must be an array or pointer type, not %s",
- exp->e1->toChars(), exp->e1->type->toChars());
- return setError();
- }
-
- if (t1b->ty == Tsarray || t1b->ty == Tarray)
- {
- Expression *el = new ArrayLengthExp(exp->loc, exp->e1);
- el = expressionSemantic(el, sc);
- el = el->optimize(WANTvalue);
- if (el->op == TOKint64)
- {
- exp->e2 = exp->e2->optimize(WANTvalue);
- dinteger_t length = el->toInteger();
- if (length)
- {
- IntRange bounds(SignExtendedNumber(0), SignExtendedNumber(length - 1));
- exp->indexIsInBounds = bounds.contains(getIntRange(exp->e2));
- }
- }
- }
-
- result = exp;
- }
-
- void visit(PostExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemantic(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e1x = resolveProperties(sc, exp->e1);
- if (e1x->op == TOKerror)
- {
- result = e1x;
- return;
- }
- exp->e1 = e1x;
-
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->e1->checkReadModifyWrite(exp->op))
- return setError();
- if (exp->e1->op == TOKslice)
- {
- const char *s = exp->op == TOKplusplus ? "increment" : "decrement";
- exp->error("cannot post-%s array slice `%s`, use pre-%s instead", s, exp->e1->toChars(), s);
- return setError();
- }
-
- exp->e1 = exp->e1->optimize(WANTvalue);
-
- Type *t1 = exp->e1->type->toBasetype();
- if (t1->ty == Tclass || t1->ty == Tstruct || exp->e1->op == TOKarraylength)
- {
- /* Check for operator overloading,
- * but rewrite in terms of ++e instead of e++
- */
-
- /* If e1 is not trivial, take a reference to it
- */
- Expression *de = NULL;
- if (exp->e1->op != TOKvar && exp->e1->op != TOKarraylength)
- {
- // ref v = e1;
- VarDeclaration *v = copyToTemp(STCref, "__postref", exp->e1);
- de = new DeclarationExp(exp->loc, v);
- exp->e1 = new VarExp(exp->e1->loc, v);
- }
-
- /* Rewrite as:
- * auto tmp = e1; ++e1; tmp
- */
- VarDeclaration *tmp = copyToTemp(0, "__pitmp", exp->e1);
- Expression *ea = new DeclarationExp(exp->loc, tmp);
-
- Expression *eb = exp->e1->syntaxCopy();
- eb = new PreExp(exp->op == TOKplusplus ? TOKpreplusplus : TOKpreminusminus, exp->loc, eb);
-
- Expression *ec = new VarExp(exp->loc, tmp);
-
- // Combine de,ea,eb,ec
- if (de)
- ea = new CommaExp(exp->loc, de, ea);
- e = new CommaExp(exp->loc, ea, eb);
- e = new CommaExp(exp->loc, e, ec);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
-
- exp->e1 = exp->e1->modifiableLvalue(sc, exp->e1);
-
- e = exp;
- if (exp->e1->checkScalar())
- return setError();
- if (exp->e1->checkNoBool())
- return setError();
-
- if (exp->e1->type->ty == Tpointer)
- e = scaleFactor(exp, sc);
- else
- exp->e2 = exp->e2->castTo(sc, exp->e1->type);
- e->type = exp->e1->type;
- result = e;
- }
-
- void visit(PreExp *exp)
- {
- Expression *e = exp->op_overload(sc);
- // printf("PreExp::semantic('%s')\n", exp->toChars());
-
- if (e)
- {
- result = e;
- return;
- }
-
- // Rewrite as e1+=1 or e1-=1
- if (exp->op == TOKpreplusplus)
- e = new AddAssignExp(exp->loc, exp->e1, new IntegerExp(exp->loc, 1, Type::tint32));
- else
- e = new MinAssignExp(exp->loc, exp->e1, new IntegerExp(exp->loc, 1, Type::tint32));
- result = expressionSemantic(e, sc);
- }
-
- void visit(AssignExp *exp)
- {
- //printf("e1->op = %d, '%s'\n", exp->e1->op, Token::toChars(exp->e1->op));
- //printf("e2->op = %d, '%s'\n", exp->e2->op, Token::toChars(exp->e2->op));
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- Expression *e1old = exp->e1;
-
- if (exp->e2->op == TOKcomma)
- {
- /* Rewrite to get rid of the comma from rvalue
- */
- if (!((CommaExp *)exp->e2)->isGenerated)
- exp->deprecation("Using the result of a comma expression is deprecated");
- Expression *e0;
- exp->e2 = Expression::extractLast(exp->e2, &e0);
- Expression *e = Expression::combine(e0, exp);
- result = expressionSemantic(e, sc);
- return;
- }
-
- /* Look for operator overloading of a[arguments] = e2.
- * Do it before e1->semantic() otherwise the ArrayExp will have been
- * converted to unary operator overloading already.
- */
- if (exp->e1->op == TOKarray)
- {
- Expression *res;
-
- ArrayExp *ae = (ArrayExp *)exp->e1;
- ae->e1 = expressionSemantic(ae->e1, sc);
- ae->e1 = resolveProperties(sc, ae->e1);
- Expression *ae1old = ae->e1;
-
- const bool maybeSlice =
- (ae->arguments->length == 0 ||
- (ae->arguments->length == 1 && (*ae->arguments)[0]->op == TOKinterval));
- IntervalExp *ie = NULL;
- if (maybeSlice && ae->arguments->length)
- {
- assert((*ae->arguments)[0]->op == TOKinterval);
- ie = (IntervalExp *)(*ae->arguments)[0];
- }
-
- while (true)
- {
- if (ae->e1->op == TOKerror)
- {
- result = ae->e1;
- return;
- }
- Expression *e0 = NULL;
- Expression *ae1save = ae->e1;
- ae->lengthVar = NULL;
-
- Type *t1b = ae->e1->type->toBasetype();
- AggregateDeclaration *ad = isAggregate(t1b);
- if (!ad)
- break;
- if (search_function(ad, Id::indexass))
- {
- // Deal with $
- res = resolveOpDollar(sc, ae, &e0);
- if (!res) // a[i..j] = e2 might be: a.opSliceAssign(e2, i, j)
- goto Lfallback;
- if (res->op == TOKerror)
- {
- result = res;
- return;
- }
-
- res = expressionSemantic(exp->e2, sc);
- if (res->op == TOKerror)
- {
- result = res;
- return;
- }
- exp->e2 = res;
-
- /* Rewrite (a[arguments] = e2) as:
- * a.opIndexAssign(e2, arguments)
- */
- Expressions *a = (Expressions *)ae->arguments->copy();
- a->insert(0, exp->e2);
- res = new DotIdExp(exp->loc, ae->e1, Id::indexass);
- res = new CallExp(exp->loc, res, a);
- if (maybeSlice) // a[] = e2 might be: a.opSliceAssign(e2)
- res = trySemantic(res, sc);
- else
- res = expressionSemantic(res, sc);
- if (res)
- {
- res = Expression::combine(e0, res);
- result = res;
- return;
- }
- }
- Lfallback:
- if (maybeSlice && search_function(ad, Id::sliceass))
- {
- // Deal with $
- res = resolveOpDollar(sc, ae, ie, &e0);
- if (res->op == TOKerror)
- {
- result = res;
- return;
- }
-
- res = expressionSemantic(exp->e2, sc);
- if (res->op == TOKerror)
- {
- result = res;
- return;
- }
- exp->e2 = res;
-
- /* Rewrite (a[i..j] = e2) as:
- * a.opSliceAssign(e2, i, j)
- */
- Expressions *a = new Expressions();
- a->push(exp->e2);
- if (ie)
- {
- a->push(ie->lwr);
- a->push(ie->upr);
- }
- res = new DotIdExp(exp->loc, ae->e1, Id::sliceass);
- res = new CallExp(exp->loc, res, a);
- res = expressionSemantic(res, sc);
- res = Expression::combine(e0, res);
- result = res;
- return;
- }
-
- // No operator overloading member function found yet, but
- // there might be an alias this to try.
- if (ad->aliasthis && t1b != ae->att1)
- {
- if (!ae->att1 && t1b->checkAliasThisRec())
- ae->att1 = t1b;
-
- /* Rewrite (a[arguments] op e2) as:
- * a.aliasthis[arguments] op e2
- */
- ae->e1 = resolveAliasThis(sc, ae1save, true);
- if (ae->e1)
- continue;
- }
- break;
- }
- ae->e1 = ae1old; // recovery
- ae->lengthVar = NULL;
- }
-
- /* Run exp->e1 semantic.
- */
- {
- Expression *e1x = exp->e1;
-
- /* With UFCS, e.f = value
- * Could mean:
- * .f(e, value)
- * or:
- * .f(e) = value
- */
- if (e1x->op == TOKdotti)
- {
- DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)e1x;
- Expression *e = semanticY(dti, sc, 1);
- if (!e)
- {
- result = resolveUFCSProperties(sc, e1x, exp->e2);
- return;
- }
- e1x = e;
- }
- else if (e1x->op == TOKdotid)
- {
- DotIdExp *die = (DotIdExp *)e1x;
- Expression *e = semanticY(die, sc, 1);
- if (e && isDotOpDispatch(e))
- {
- unsigned errors = global.startGagging();
- e = resolvePropertiesX(sc, e, exp->e2);
- if (global.endGagging(errors))
- e = NULL; /* fall down to UFCS */
- else
- {
- result = e;
- return;
- }
- }
- if (!e)
- {
- result = resolveUFCSProperties(sc, e1x, exp->e2);
- return;
- }
- e1x = e;
- }
- else
- {
- if (e1x->op == TOKslice)
- ((SliceExp *)e1x)->arrayop = true;
-
- e1x = expressionSemantic(e1x, sc);
- }
-
- /* We have f = value.
- * Could mean:
- * f(value)
- * or:
- * f() = value
- */
- if (Expression *e = resolvePropertiesX(sc, e1x, exp->e2))
- {
- result = e;
- return;
- }
- if (e1x->checkRightThis(sc))
- return setError();
- exp->e1 = e1x;
- assert(exp->e1->type);
- }
- Type *t1 = exp->e1->type->toBasetype();
-
- /* Run exp->e2 semantic.
- * Different from other binary expressions, the analysis of e2
- * depends on the result of e1 in assignments.
- */
- {
- Expression *e2x = inferType(exp->e2, t1->baseElemOf());
-
- e2x = expressionSemantic(e2x, sc);
- e2x = resolveProperties(sc, e2x);
-
- if (e2x->op == TOKtype)
- e2x = resolveAliasThis(sc, e2x); //https://issues.dlang.org/show_bug.cgi?id=17684
- if (e2x->op == TOKerror)
- {
- result = e2x;
- return;
- }
- if (e2x->checkValue())
- return setError();
- exp->e2 = e2x;
- }
-
- /* Rewrite tuple assignment as a tuple of assignments.
- */
- {
- Expression *e2x = exp->e2;
-
- Ltupleassign:
- if (exp->e1->op == TOKtuple && e2x->op == TOKtuple)
- {
- TupleExp *tup1 = (TupleExp *)exp->e1;
- TupleExp *tup2 = (TupleExp *)e2x;
- size_t dim = tup1->exps->length;
- Expression *e = NULL;
- if (dim != tup2->exps->length)
- {
- exp->error("mismatched tuple lengths, %d and %d", (int)dim, (int)tup2->exps->length);
- return setError();
- }
- if (dim == 0)
- {
- e = new IntegerExp(exp->loc, 0, Type::tint32);
- e = new CastExp(exp->loc, e, Type::tvoid); // avoid "has no effect" error
- e = Expression::combine(Expression::combine(tup1->e0, tup2->e0), e);
- }
- else
- {
- Expressions *exps = new Expressions;
- exps->setDim(dim);
- for (size_t i = 0; i < dim; i++)
- {
- Expression *ex1 = (*tup1->exps)[i];
- Expression *ex2 = (*tup2->exps)[i];
- (*exps)[i] = new AssignExp(exp->loc, ex1, ex2);
- }
- e = new TupleExp(exp->loc, Expression::combine(tup1->e0, tup2->e0), exps);
- }
- result = expressionSemantic(e, sc);
- return;
- }
-
- /* Look for form: e1 = e2->aliasthis.
- */
- if (exp->e1->op == TOKtuple)
- {
- TupleDeclaration *td = isAliasThisTuple(e2x);
- if (!td)
- goto Lnomatch;
-
- assert(exp->e1->type->ty == Ttuple);
- TypeTuple *tt = (TypeTuple *)exp->e1->type;
-
- Expression *e0 = NULL;
- Expression *ev = extractSideEffect(sc, "__tup", &e0, e2x);
-
- Expressions *iexps = new Expressions();
- iexps->push(ev);
-
- for (size_t u = 0; u < iexps->length ; u++)
- {
- Lexpand:
- Expression *e = (*iexps)[u];
-
- Parameter *arg = Parameter::getNth(tt->arguments, u);
- //printf("[%d] iexps->length = %d, ", u, iexps->length);
- //printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars());
- //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars());
-
- if (!arg || !e->type->implicitConvTo(arg->type))
- {
- // expand initializer to tuple
- if (expandAliasThisTuples(iexps, u) != -1)
- {
- if (iexps->length <= u)
- break;
- goto Lexpand;
- }
- goto Lnomatch;
- }
- }
- e2x = new TupleExp(e2x->loc, e0, iexps);
- e2x = expressionSemantic(e2x, sc);
- if (e2x->op == TOKerror)
- {
- result = e2x;
- return;
- }
- // Do not need to overwrite exp->e2
- goto Ltupleassign;
- }
- Lnomatch:
- ;
- }
-
- /* Inside constructor, if this is the first assignment of object field,
- * rewrite this to initializing the field.
- */
- if (exp->op == TOKassign && exp->e1->checkModifiable(sc) == 2)
- {
- //printf("[%s] change to init - %s\n", exp->loc.toChars(), toChars());
- exp->op = TOKconstruct;
-
- // Bugzilla 13515: set Index::modifiable flag for complex AA element initialization
- if (exp->e1->op == TOKindex)
- {
- Expression *e1x = ((IndexExp *)exp->e1)->markSettingAAElem();
- if (e1x->op == TOKerror)
- {
- result = e1x;
- return;
- }
- }
- }
- else if (exp->op == TOKconstruct && exp->e1->op == TOKvar &&
- ((VarExp *)exp->e1)->var->storage_class & (STCout | STCref))
- {
- exp->memset |= referenceInit;
- }
-
- /* If it is an assignment from a 'foreign' type,
- * check for operator overloading.
- */
- if (exp->memset & referenceInit)
- {
- // If this is an initialization of a reference,
- // do nothing
- }
- else if (t1->ty == Tstruct)
- {
- Expression *e1x = exp->e1;
- Expression *e2x = exp->e2;
- StructDeclaration *sd = ((TypeStruct *)t1)->sym;
-
- if (exp->op == TOKconstruct)
- {
- Type *t2 = e2x->type->toBasetype();
- if (t2->ty == Tstruct && sd == ((TypeStruct *)t2)->sym)
- {
- sd->size(exp->loc);
- if (sd->sizeok != SIZEOKdone)
- return setError();
- if (!sd->ctor)
- sd->ctor = sd->searchCtor();
-
- // Bugzilla 15661: Look for the form from last of comma chain.
- Expression *e2y = e2x;
- while (e2y->op == TOKcomma)
- e2y = ((CommaExp *)e2y)->e2;
-
- CallExp *ce = (e2y->op == TOKcall) ? (CallExp *)e2y : NULL;
- DotVarExp *dve = (ce && ce->e1->op == TOKdotvar)
- ? (DotVarExp *)ce->e1 : NULL;
- if (sd->ctor && ce && dve && dve->var->isCtorDeclaration() &&
- e2y->type->implicitConvTo(t1))
- {
- /* Look for form of constructor call which is:
- * __ctmp.ctor(arguments...)
- */
-
- /* Before calling the constructor, initialize
- * variable with a bit copy of the default
- * initializer
- */
- AssignExp *ae = exp;
- if (sd->zeroInit == 1 && !sd->isNested())
- {
- // Bugzilla 14606: Always use BlitExp for the special expression: (struct = 0)
- ae = new BlitExp(ae->loc, ae->e1, new IntegerExp(exp->loc, 0, Type::tint32));
- }
- else
- {
- // Keep ae->op == TOKconstruct
- ae->e2 = sd->isNested() ? t1->defaultInitLiteral(exp->loc) : t1->defaultInit(exp->loc);
- }
- ae->type = e1x->type;
-
- /* Replace __ctmp being constructed with e1.
- * We need to copy constructor call expression,
- * because it may be used in other place.
- */
- DotVarExp *dvx = (DotVarExp *)dve->copy();
- dvx->e1 = e1x;
- CallExp *cx = (CallExp *)ce->copy();
- cx->e1 = dvx;
-
- Expression *e0;
- Expression::extractLast(e2x, &e0);
-
- Expression *e = Expression::combine(ae, cx);
- e = Expression::combine(e0, e);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- if (sd->postblit)
- {
- /* We have a copy constructor for this
- */
- if (e2x->op == TOKquestion)
- {
- /* Rewrite as:
- * a ? e1 = b : e1 = c;
- */
- CondExp *econd = (CondExp *)e2x;
- Expression *ea1 = new ConstructExp(econd->e1->loc, e1x, econd->e1);
- Expression *ea2 = new ConstructExp(econd->e1->loc, e1x, econd->e2);
- Expression *e = new CondExp(exp->loc, econd->econd, ea1, ea2);
- result = expressionSemantic(e, sc);
- return;
- }
-
- if (e2x->isLvalue())
- {
- if (!e2x->type->implicitConvTo(e1x->type))
- {
- exp->error("conversion error from %s to %s", e2x->type->toChars(), e1x->type->toChars());
- return setError();
- }
-
- /* Rewrite as:
- * (e1 = e2).postblit();
- *
- * Blit assignment e1 = e2 returns a reference to the original e1,
- * then call the postblit on it.
- */
- Expression *e = e1x->copy();
- e->type = e->type->mutableOf();
- e = new BlitExp(exp->loc, e, e2x);
- e = new DotVarExp(exp->loc, e, sd->postblit, false);
- e = new CallExp(exp->loc, e);
- result = expressionSemantic(e, sc);
- return;
- }
- else
- {
- /* The struct value returned from the function is transferred
- * so should not call the destructor on it.
- */
- e2x = valueNoDtor(e2x);
- }
- }
- }
- else if (!e2x->implicitConvTo(t1))
- {
- sd->size(exp->loc);
- if (sd->sizeok != SIZEOKdone)
- return setError();
- if (!sd->ctor)
- sd->ctor = sd->searchCtor();
-
- if (sd->ctor)
- {
- /* Look for implicit constructor call
- * Rewrite as:
- * e1 = init, e1.ctor(e2)
- */
- Expression *einit;
- einit = new BlitExp(exp->loc, e1x, e1x->type->defaultInit(exp->loc));
- einit->type = e1x->type;
-
- Expression *e;
- e = new DotIdExp(exp->loc, e1x, Id::ctor);
- e = new CallExp(exp->loc, e, e2x);
- e = new CommaExp(exp->loc, einit, e);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- if (search_function(sd, Id::call))
- {
- /* Look for static opCall
- * (See bugzilla 2702 for more discussion)
- * Rewrite as:
- * e1 = typeof(e1).opCall(arguments)
- */
- e2x = typeDotIdExp(e2x->loc, e1x->type, Id::call);
- e2x = new CallExp(exp->loc, e2x, exp->e2);
-
- e2x = expressionSemantic(e2x, sc);
- e2x = resolveProperties(sc, e2x);
- if (e2x->op == TOKerror)
- {
- result = e2x;
- return;
- }
- if (e2x->checkValue())
- return setError();
- }
- }
- else // Bugzilla 11355
- {
- AggregateDeclaration *ad2 = isAggregate(e2x->type);
- if (ad2 && ad2->aliasthis && !(exp->att2 && e2x->type == exp->att2))
- {
- if (!exp->att2 && exp->e2->type->checkAliasThisRec())
- exp->att2 = exp->e2->type;
-
- /* Rewrite (e1 op e2) as:
- * (e1 op e2.aliasthis)
- */
- exp->e2 = new DotIdExp(exp->e2->loc, exp->e2, ad2->aliasthis->ident);
- result = expressionSemantic(exp, sc);
- return;
- }
- }
- }
- else if (exp->op == TOKassign)
- {
- if (e1x->op == TOKindex &&
- ((IndexExp *)e1x)->e1->type->toBasetype()->ty == Taarray)
- {
- /*
- * Rewrite:
- * aa[key] = e2;
- * as:
- * ref __aatmp = aa;
- * ref __aakey = key;
- * ref __aaval = e2;
- * (__aakey in __aatmp
- * ? __aatmp[__aakey].opAssign(__aaval)
- * : ConstructExp(__aatmp[__aakey], __aaval));
- */
- IndexExp *ie = (IndexExp *)e1x;
- Type *t2 = e2x->type->toBasetype();
-
- Expression *e0 = NULL;
- Expression *ea = extractSideEffect(sc, "__aatmp", &e0, ie->e1);
- Expression *ek = extractSideEffect(sc, "__aakey", &e0, ie->e2);
- Expression *ev = extractSideEffect(sc, "__aaval", &e0, e2x);
-
- AssignExp *ae = (AssignExp *)exp->copy();
- ae->e1 = new IndexExp(exp->loc, ea, ek);
- ae->e1 = expressionSemantic(ae->e1, sc);
- ae->e1 = ae->e1->optimize(WANTvalue);
- ae->e2 = ev;
- Expression *e = ae->op_overload(sc);
- if (e)
- {
- Expression *ey = NULL;
- if (t2->ty == Tstruct && sd == t2->toDsymbol(sc))
- {
- ey = ev;
- }
- else if (!ev->implicitConvTo(ie->type) && sd->ctor)
- {
- // Look for implicit constructor call
- // Rewrite as S().ctor(e2)
- ey = new StructLiteralExp(exp->loc, sd, NULL);
- ey = new DotIdExp(exp->loc, ey, Id::ctor);
- ey = new CallExp(exp->loc, ey, ev);
- ey = trySemantic(ey, sc);
- }
- if (ey)
- {
- Expression *ex;
- ex = new IndexExp(exp->loc, ea, ek);
- ex = expressionSemantic(ex, sc);
- ex = ex->optimize(WANTvalue);
- ex = ex->modifiableLvalue(sc, ex); // allocate new slot
- ey = new ConstructExp(exp->loc, ex, ey);
- ey = expressionSemantic(ey, sc);
- if (ey->op == TOKerror)
- {
- result = ey;
- return;
- }
- ex = e;
-
- // Bugzilla 14144: The whole expression should have the common type
- // of opAssign() return and assigned AA entry.
- // Even if there's no common type, expression should be typed as void.
- Type *t = NULL;
- if (!typeMerge(sc, TOKquestion, &t, &ex, &ey))
- {
- ex = new CastExp(ex->loc, ex, Type::tvoid);
- ey = new CastExp(ey->loc, ey, Type::tvoid);
- }
- e = new CondExp(exp->loc, new InExp(exp->loc, ek, ea), ex, ey);
- }
- e = Expression::combine(e0, e);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- }
- else
- {
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
- }
- }
- else
- assert(exp->op == TOKblit);
-
- exp->e1 = e1x;
- exp->e2 = e2x;
- }
- else if (t1->ty == Tclass)
- {
- // Disallow assignment operator overloads for same type
- if (exp->op == TOKassign && !exp->e2->implicitConvTo(exp->e1->type))
- {
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
- }
- }
- else if (t1->ty == Tsarray)
- {
- // SliceExp cannot have static array type without context inference.
- assert(exp->e1->op != TOKslice);
-
- Expression *e1x = exp->e1;
- Expression *e2x = exp->e2;
-
- if (e2x->implicitConvTo(e1x->type))
- {
- if (exp->op != TOKblit &&
- ((e2x->op == TOKslice && ((UnaExp *)e2x)->e1->isLvalue()) ||
- (e2x->op == TOKcast && ((UnaExp *)e2x)->e1->isLvalue()) ||
- (e2x->op != TOKslice && e2x->isLvalue())))
- {
- if (e1x->checkPostblit(sc, t1))
- return setError();
- }
-
- // e2 matches to t1 because of the implicit length match, so
- if (isUnaArrayOp(e2x->op) || isBinArrayOp(e2x->op))
- {
- // convert e1 to e1[]
- // e.g. e1[] = a[] + b[];
- SliceExp *sle = new SliceExp(e1x->loc, e1x, NULL, NULL);
- sle->arrayop = true;
- e1x = expressionSemantic(sle, sc);
- }
- else
- {
- // convert e2 to t1 later
- // e.g. e1 = [1, 2, 3];
- }
- }
- else
- {
- if (e2x->implicitConvTo(t1->nextOf()->arrayOf()) > MATCHnomatch)
- {
- uinteger_t dim1 = ((TypeSArray *)t1)->dim->toInteger();
- uinteger_t dim2 = dim1;
- if (e2x->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)e2x;
- dim2 = ale->elements ? ale->elements->length : 0;
- }
- else if (e2x->op == TOKslice)
- {
- Type *tx = toStaticArrayType((SliceExp *)e2x);
- if (tx)
- dim2 = ((TypeSArray *)tx)->dim->toInteger();
- }
- if (dim1 != dim2)
- {
- exp->error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2);
- return setError();
- }
- }
-
- // May be block or element-wise assignment, so
- // convert e1 to e1[]
- if (exp->op != TOKassign)
- {
- // If multidimensional static array, treat as one large array
- dinteger_t dim = t1->numberOfElems(exp->loc);
- e1x->type = t1->baseElemOf()->sarrayOf(dim);
- }
- SliceExp *sle = new SliceExp(e1x->loc, e1x, NULL, NULL);
- sle->arrayop = true;
- e1x = expressionSemantic(sle, sc);
- }
- if (e1x->op == TOKerror)
- {
- result = e1x;
- return;
- }
- if (e2x->op == TOKerror)
- {
- result = e2x;
- return;
- }
-
- exp->e1 = e1x;
- exp->e2 = e2x;
- t1 = e1x->type->toBasetype();
- }
-
- /* Check the mutability of e1.
- */
- if (exp->e1->op == TOKarraylength)
- {
- // e1 is not an lvalue, but we let code generator handle it
- ArrayLengthExp *ale = (ArrayLengthExp *)exp->e1;
-
- Expression *ale1x = ale->e1;
- ale1x = ale1x->modifiableLvalue(sc, exp->e1);
- if (ale1x->op == TOKerror)
- {
- result = ale1x;
- return;
- }
- ale->e1 = ale1x;
-
- Type *tn = ale->e1->type->toBasetype()->nextOf();
- checkDefCtor(ale->loc, tn);
- semanticTypeInfo(sc, tn);
- }
- else if (exp->e1->op == TOKslice)
- {
- Type *tn = exp->e1->type->nextOf();
- if (exp->op == TOKassign && !tn->isMutable())
- {
- exp->error("slice %s is not mutable", exp->e1->toChars());
- return setError();
- }
-
- // For conditional operator, both branches need conversion.
- SliceExp *se = (SliceExp *)exp->e1;
- while (se->e1->op == TOKslice)
- se = (SliceExp *)se->e1;
- if (se->e1->op == TOKquestion &&
- se->e1->type->toBasetype()->ty == Tsarray)
- {
- se->e1 = se->e1->modifiableLvalue(sc, exp->e1);
- if (se->e1->op == TOKerror)
- {
- result = se->e1;
- return;
- }
- }
- }
- else
- {
- Expression *e1x = exp->e1;
-
- // Try to do a decent error message with the expression
- // before it got constant folded
- if (e1x->op != TOKvar)
- e1x = e1x->optimize(WANTvalue);
-
- if (exp->op == TOKassign)
- e1x = e1x->modifiableLvalue(sc, e1old);
-
- if (e1x->op == TOKerror)
- {
- result = e1x;
- return;
- }
- exp->e1 = e1x;
- }
-
- /* Tweak e2 based on the type of e1.
- */
- Expression *e2x = exp->e2;
- Type *t2 = e2x->type->toBasetype();
-
- // If it is a array, get the element type. Note that it may be
- // multi-dimensional.
- Type *telem = t1;
- while (telem->ty == Tarray)
- telem = telem->nextOf();
-
- if (exp->e1->op == TOKslice &&
- t1->nextOf() && (telem->ty != Tvoid || e2x->op == TOKnull) &&
- e2x->implicitConvTo(t1->nextOf())
- )
- {
- // Check for block assignment. If it is of type void[], void[][], etc,
- // '= null' is the only allowable block assignment (Bug 7493)
- // memset
- exp->memset |= blockAssign; // make it easy for back end to tell what this is
- e2x = e2x->implicitCastTo(sc, t1->nextOf());
- if (exp->op != TOKblit && e2x->isLvalue() &&
- exp->e1->checkPostblit(sc, t1->nextOf()))
- return setError();
- }
- else if (exp->e1->op == TOKslice &&
- (t2->ty == Tarray || t2->ty == Tsarray) &&
- t2->nextOf()->implicitConvTo(t1->nextOf()))
- {
- // Check element-wise assignment.
-
- /* If assigned elements number is known at compile time,
- * check the mismatch.
- */
- SliceExp *se1 = (SliceExp *)exp->e1;
- TypeSArray *tsa1 = (TypeSArray *)toStaticArrayType(se1);
- TypeSArray *tsa2 = NULL;
- if (e2x->op == TOKarrayliteral)
- tsa2 = (TypeSArray *)t2->nextOf()->sarrayOf(((ArrayLiteralExp *)e2x)->elements->length);
- else if (e2x->op == TOKslice)
- tsa2 = (TypeSArray *)toStaticArrayType((SliceExp *)e2x);
- else if (t2->ty == Tsarray)
- tsa2 = (TypeSArray *)t2;
- if (tsa1 && tsa2)
- {
- uinteger_t dim1 = tsa1->dim->toInteger();
- uinteger_t dim2 = tsa2->dim->toInteger();
- if (dim1 != dim2)
- {
- exp->error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2);
- return setError();
- }
- }
-
- if (exp->op != TOKblit &&
- ((e2x->op == TOKslice && ((UnaExp *)e2x)->e1->isLvalue()) ||
- (e2x->op == TOKcast && ((UnaExp *)e2x)->e1->isLvalue()) ||
- (e2x->op != TOKslice && e2x->isLvalue())))
- {
- if (exp->e1->checkPostblit(sc, t1->nextOf()))
- return setError();
- }
-
- if (0 && global.params.warnings != DIAGNOSTICoff && !global.gag && exp->op == TOKassign &&
- e2x->op != TOKslice && e2x->op != TOKassign &&
- e2x->op != TOKarrayliteral && e2x->op != TOKstring &&
- !(e2x->op == TOKadd || e2x->op == TOKmin ||
- e2x->op == TOKmul || e2x->op == TOKdiv ||
- e2x->op == TOKmod || e2x->op == TOKxor ||
- e2x->op == TOKand || e2x->op == TOKor ||
- e2x->op == TOKpow ||
- e2x->op == TOKtilde || e2x->op == TOKneg))
- {
- const char* e1str = exp->e1->toChars();
- const char* e2str = e2x->toChars();
- exp->warning("explicit element-wise assignment %s = (%s)[] is better than %s = %s",
- e1str, e2str, e1str, e2str);
- }
-
- Type *t2n = t2->nextOf();
- Type *t1n = t1->nextOf();
- int offset;
- if (t2n->equivalent(t1n) ||
- (t1n->isBaseOf(t2n, &offset) && offset == 0))
- {
- /* Allow copy of distinct qualifier elements.
- * eg.
- * char[] dst; const(char)[] src;
- * dst[] = src;
- *
- * class C {} class D : C {}
- * C[2] ca; D[] da;
- * ca[] = da;
- */
- if (isArrayOpValid(e2x))
- {
- // Don't add CastExp to keep AST for array operations
- e2x = e2x->copy();
- e2x->type = exp->e1->type->constOf();
- }
- else
- e2x = e2x->castTo(sc, exp->e1->type->constOf());
- }
- else
- {
- /* Bugzilla 15778: A string literal has an array type of immutable
- * elements by default, and normally it cannot be convertible to
- * array type of mutable elements. But for element-wise assignment,
- * elements need to be const at best. So we should give a chance
- * to change code unit size for polysemous string literal.
- */
- if (e2x->op == TOKstring)
- e2x = e2x->implicitCastTo(sc, exp->e1->type->constOf());
- else
- e2x = e2x->implicitCastTo(sc, exp->e1->type);
- }
- if (t1n->toBasetype()->ty == Tvoid && t2n->toBasetype()->ty == Tvoid)
- {
- if (!sc->intypeof && sc->func && sc->func->setUnsafe())
- {
- exp->error("cannot copy void[] to void[] in @safe code");
- return setError();
- }
- }
- }
- else
- {
- if (0 && global.params.warnings != DIAGNOSTICoff && !global.gag && exp->op == TOKassign &&
- t1->ty == Tarray && t2->ty == Tsarray &&
- e2x->op != TOKslice &&
- t2->implicitConvTo(t1))
- { // Disallow ar[] = sa (Converted to ar[] = sa[])
- // Disallow da = sa (Converted to da = sa[])
- const char* e1str = exp->e1->toChars();
- const char* e2str = e2x->toChars();
- const char* atypestr = exp->e1->op == TOKslice ? "element-wise" : "slice";
- exp->warning("explicit %s assignment %s = (%s)[] is better than %s = %s",
- atypestr, e1str, e2str, e1str, e2str);
- }
- if (exp->op == TOKblit)
- e2x = e2x->castTo(sc, exp->e1->type);
- else
- e2x = e2x->implicitCastTo(sc, exp->e1->type);
- }
- if (e2x->op == TOKerror)
- {
- result = e2x;
- return;
- }
- exp->e2 = e2x;
- t2 = exp->e2->type->toBasetype();
-
- /* Look for array operations
- */
- if ((t2->ty == Tarray || t2->ty == Tsarray) && isArrayOpValid(exp->e2))
- {
- // Look for valid array operations
- if (!(exp->memset & blockAssign) && exp->e1->op == TOKslice &&
- (isUnaArrayOp(exp->e2->op) || isBinArrayOp(exp->e2->op)))
- {
- exp->type = exp->e1->type;
- if (exp->op == TOKconstruct) // Bugzilla 10282: tweak mutability of e1 element
- exp->e1->type = exp->e1->type->nextOf()->mutableOf()->arrayOf();
- result = arrayOp(exp, sc);
- return;
- }
-
- // Drop invalid array operations in e2
- // d = a[] + b[], d = (a[] + b[])[0..2], etc
- if (checkNonAssignmentArrayOp(exp->e2, !(exp->memset & blockAssign) && exp->op == TOKassign))
- return setError();
-
- // Remains valid array assignments
- // d = d[], d = [1,2,3], etc
- }
-
- /* Don't allow assignment to classes that were allocated on the stack with:
- * scope Class c = new Class();
- */
-
- if (exp->e1->op == TOKvar && exp->op == TOKassign)
- {
- VarExp *ve = (VarExp *)exp->e1;
- VarDeclaration *vd = ve->var->isVarDeclaration();
- if (vd && (vd->onstack || vd->mynew))
- {
- assert(t1->ty == Tclass);
- exp->error("cannot rebind scope variables");
- }
- }
- if (exp->e1->op == TOKvar && ((VarExp *)exp->e1)->var->ident == Id::ctfe)
- {
- exp->error("cannot modify compiler-generated variable __ctfe");
- }
-
- exp->type = exp->e1->type;
- assert(exp->type);
- Expression *res = exp->op == TOKassign ? exp->reorderSettingAAElem(sc) : exp;
- checkAssignEscape(sc, res, false);
- result = res;
- }
-
- void visit(CatAssignExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- //printf("CatAssignExp::semantic() %s\n", toChars());
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->e1->op == TOKslice)
- {
- SliceExp *se = (SliceExp *)exp->e1;
- if (se->e1->type->toBasetype()->ty == Tsarray)
- {
- exp->error("cannot append to static array %s", se->e1->type->toChars());
- return setError();
- }
- }
-
- exp->e1 = exp->e1->modifiableLvalue(sc, exp->e1);
- if (exp->e1->op == TOKerror)
- {
- result = exp->e1;
- return;
- }
- if (exp->e2->op == TOKerror)
- {
- result = exp->e2;
- return;
- }
-
- if (checkNonAssignmentArrayOp(exp->e2))
- return setError();
-
- Type *tb1 = exp->e1->type->toBasetype();
- Type *tb1next = tb1->nextOf();
- Type *tb2 = exp->e2->type->toBasetype();
-
- if ((tb1->ty == Tarray) &&
- (tb2->ty == Tarray || tb2->ty == Tsarray) &&
- (exp->e2->implicitConvTo(exp->e1->type)
- || (tb2->nextOf()->implicitConvTo(tb1next) &&
- (tb2->nextOf()->size(Loc()) == tb1next->size(Loc())))
- )
- )
- {
- // Append array
- if (exp->e1->checkPostblit(sc, tb1next))
- return setError();
- exp->e2 = exp->e2->castTo(sc, exp->e1->type);
- }
- else if ((tb1->ty == Tarray) &&
- exp->e2->implicitConvTo(tb1next)
- )
- {
- // Append element
- if (exp->e2->checkPostblit(sc, tb2))
- return setError();
- exp->e2 = exp->e2->castTo(sc, tb1next);
- exp->e2 = doCopyOrMove(sc, exp->e2);
- }
- else if (tb1->ty == Tarray &&
- (tb1next->ty == Tchar || tb1next->ty == Twchar) &&
- exp->e2->type->ty != tb1next->ty &&
- exp->e2->implicitConvTo(Type::tdchar)
- )
- { // Append dchar to char[] or wchar[]
- exp->e2 = exp->e2->castTo(sc, Type::tdchar);
-
- /* Do not allow appending wchar to char[] because if wchar happens
- * to be a surrogate pair, nothing good can result.
- */
- }
- else
- {
- exp->error("cannot append type %s to type %s", tb2->toChars(), tb1->toChars());
- return setError();
- }
- if (exp->e2->checkValue())
- return setError();
-
- exp->type = exp->e1->type;
- result = exp->reorderSettingAAElem(sc);
- }
-
- void visit(PowAssignExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->e1->checkReadModifyWrite(exp->op, exp->e2))
- return setError();
-
- assert(exp->e1->type && exp->e2->type);
- if (exp->e1->op == TOKslice || exp->e1->type->ty == Tarray || exp->e1->type->ty == Tsarray)
- {
- if (checkNonAssignmentArrayOp(exp->e1))
- return setError();
-
- // T[] ^^= ...
- if (exp->e2->implicitConvTo(exp->e1->type->nextOf()))
- {
- // T[] ^^= T
- exp->e2 = exp->e2->castTo(sc, exp->e1->type->nextOf());
- }
- else if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- // Check element types are arithmetic
- Type *tb1 = exp->e1->type->nextOf()->toBasetype();
- Type *tb2 = exp->e2->type->toBasetype();
- if (tb2->ty == Tarray || tb2->ty == Tsarray)
- tb2 = tb2->nextOf()->toBasetype();
-
- if ( (tb1->isintegral() || tb1->isfloating()) &&
- (tb2->isintegral() || tb2->isfloating()))
- {
- exp->type = exp->e1->type;
- result = arrayOp(exp, sc);
- return;
- }
- }
- else
- {
- exp->e1 = exp->e1->modifiableLvalue(sc, exp->e1);
- }
-
- if ((exp->e1->type->isintegral() || exp->e1->type->isfloating()) &&
- (exp->e2->type->isintegral() || exp->e2->type->isfloating()))
- {
- Expression *e0 = NULL;
- e = exp->reorderSettingAAElem(sc);
- e = Expression::extractLast(e, &e0);
- assert(e == exp);
-
- if (exp->e1->op == TOKvar)
- {
- // Rewrite: e1 = e1 ^^ e2
- e = new PowExp(exp->loc, exp->e1->syntaxCopy(), exp->e2);
- e = new AssignExp(exp->loc, exp->e1, e);
- }
- else
- {
- // Rewrite: ref tmp = e1; tmp = tmp ^^ e2
- VarDeclaration *v = copyToTemp(STCref, "__powtmp", exp->e1);
- Expression *de = new DeclarationExp(exp->e1->loc, v);
- VarExp *ve = new VarExp(exp->e1->loc, v);
- e = new PowExp(exp->loc, ve, exp->e2);
- e = new AssignExp(exp->loc, new VarExp(exp->e1->loc, v), e);
- e = new CommaExp(exp->loc, de, e);
- }
- e = Expression::combine(e0, e);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- result = exp->incompatibleTypes();
- }
-
- void visit(AddExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- Type *tb1 = exp->e1->type->toBasetype();
- Type *tb2 = exp->e2->type->toBasetype();
-
- bool err = false;
- if (tb1->ty == Tdelegate ||
- (tb1->ty == Tpointer && tb1->nextOf()->ty == Tfunction))
- {
- err |= exp->e1->checkArithmetic();
- }
- if (tb2->ty == Tdelegate ||
- (tb2->ty == Tpointer && tb2->nextOf()->ty == Tfunction))
- {
- err |= exp->e2->checkArithmetic();
- }
- if (err)
- return setError();
-
- if ((tb1->ty == Tpointer && exp->e2->type->isintegral()) ||
- (tb2->ty == Tpointer && exp->e1->type->isintegral()))
- {
- result = scaleFactor(exp, sc);
- return;
- }
-
- if (tb1->ty == Tpointer && tb2->ty == Tpointer)
- {
- result = exp->incompatibleTypes();
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- tb1 = exp->e1->type->toBasetype();
- if (!target.isVectorOpSupported(tb1, exp->op, tb2))
- {
- result = exp->incompatibleTypes();
- return;
- }
- if ((tb1->isreal() && exp->e2->type->isimaginary()) ||
- (tb1->isimaginary() && exp->e2->type->isreal()))
- {
- switch (exp->type->toBasetype()->ty)
- {
- case Tfloat32:
- case Timaginary32:
- exp->type = Type::tcomplex32;
- break;
-
- case Tfloat64:
- case Timaginary64:
- exp->type = Type::tcomplex64;
- break;
-
- case Tfloat80:
- case Timaginary80:
- exp->type = Type::tcomplex80;
- break;
-
- default:
- assert(0);
- }
- }
- result = exp;
- }
-
- void visit(MinExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- Type *t1 = exp->e1->type->toBasetype();
- Type *t2 = exp->e2->type->toBasetype();
-
- bool err = false;
- if (t1->ty == Tdelegate ||
- (t1->ty == Tpointer && t1->nextOf()->ty == Tfunction))
- {
- err |= exp->e1->checkArithmetic();
- }
- if (t2->ty == Tdelegate ||
- (t2->ty == Tpointer && t2->nextOf()->ty == Tfunction))
- {
- err |= exp->e2->checkArithmetic();
- }
- if (err)
- return setError();
-
- if (t1->ty == Tpointer)
- {
- if (t2->ty == Tpointer)
- {
- // Need to divide the result by the stride
- // Replace (ptr - ptr) with (ptr - ptr) / stride
- d_int64 stride;
-
- // make sure pointer types are compatible
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- exp->type = Type::tptrdiff_t;
- stride = t2->nextOf()->size();
- if (stride == 0)
- {
- e = new IntegerExp(exp->loc, 0, Type::tptrdiff_t);
- }
- else
- {
- e = new DivExp(exp->loc, exp, new IntegerExp(Loc(), stride, Type::tptrdiff_t));
- e->type = Type::tptrdiff_t;
- }
- }
- else if (t2->isintegral())
- e = scaleFactor(exp, sc);
- else
- {
- exp->error("can't subtract %s from pointer", t2->toChars());
- e = new ErrorExp();
- }
- result = e;
- return;
- }
- if (t2->ty == Tpointer)
- {
- exp->type = exp->e2->type;
- exp->error("can't subtract pointer from %s", exp->e1->type->toChars());
- return setError();
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- t1 = exp->e1->type->toBasetype();
- t2 = exp->e2->type->toBasetype();
- if (!target.isVectorOpSupported(t1, exp->op, t2))
- {
- result = exp->incompatibleTypes();
- return;
- }
- if ((t1->isreal() && t2->isimaginary()) ||
- (t1->isimaginary() && t2->isreal()))
- {
- switch (exp->type->ty)
- {
- case Tfloat32:
- case Timaginary32:
- exp->type = Type::tcomplex32;
- break;
-
- case Tfloat64:
- case Timaginary64:
- exp->type = Type::tcomplex64;
- break;
-
- case Tfloat80:
- case Timaginary80:
- exp->type = Type::tcomplex80;
- break;
-
- default:
- assert(0);
- }
- }
- result = exp;
- }
-
- void visit(CatExp *exp)
- {
- //printf("CatExp::semantic() %s\n", exp->toChars());
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- Type *tb1 = exp->e1->type->toBasetype();
- Type *tb2 = exp->e2->type->toBasetype();
-
- bool f1 = checkNonAssignmentArrayOp(exp->e1);
- bool f2 = checkNonAssignmentArrayOp(exp->e2);
- if (f1 || f2)
- return setError();
-
- /* BUG: Should handle things like:
- * char c;
- * c ~ ' '
- * ' ' ~ c;
- */
- Type *tb1next = tb1->nextOf();
- Type *tb2next = tb2->nextOf();
-
- // Check for: array ~ array
- if (tb1next && tb2next &&
- (tb1next->implicitConvTo(tb2next) >= MATCHconst ||
- tb2next->implicitConvTo(tb1next) >= MATCHconst ||
- (exp->e1->op == TOKarrayliteral && exp->e1->implicitConvTo(tb2)) ||
- (exp->e2->op == TOKarrayliteral && exp->e2->implicitConvTo(tb1))
- )
- )
- {
- /* Bugzilla 9248: Here to avoid the case of:
- * void*[] a = [cast(void*)1];
- * void*[] b = [cast(void*)2];
- * a ~ b;
- * becoming:
- * a ~ [cast(void*)b];
- */
-
- /* Bugzilla 14682: Also to avoid the case of:
- * int[][] a;
- * a ~ [];
- * becoming:
- * a ~ cast(int[])[];
- */
- goto Lpeer;
- }
-
- // Check for: array ~ element
- if ((tb1->ty == Tsarray || tb1->ty == Tarray) && tb2->ty != Tvoid)
- {
- if (exp->e1->op == TOKarrayliteral)
- {
- exp->e2 = exp->e2->isLvalue() ? callCpCtor(sc, exp->e2) : valueNoDtor(exp->e2);
- // Bugzilla 14686: Postblit call appears in AST, and this is
- // finally translated to an ArrayLiteralExp in below otpimize().
- }
- else if (exp->e1->op == TOKstring)
- {
- // No postblit call exists on character (integer) value.
- }
- else
- {
- if (exp->e2->checkPostblit(sc, tb2))
- return setError();
- // Postblit call will be done in runtime helper function
- }
-
- if (exp->e1->op == TOKarrayliteral && exp->e1->implicitConvTo(tb2->arrayOf()))
- {
- exp->e1 = exp->e1->implicitCastTo(sc, tb2->arrayOf());
- exp->type = tb2->arrayOf();
- goto L2elem;
- }
- if (exp->e2->implicitConvTo(tb1next) >= MATCHconvert)
- {
- exp->e2 = exp->e2->implicitCastTo(sc, tb1next);
- exp->type = tb1next->arrayOf();
- L2elem:
- if (tb2->ty == Tarray || tb2->ty == Tsarray)
- {
- // Make e2 into [e2]
- exp->e2 = new ArrayLiteralExp(exp->e2->loc, exp->type, exp->e2);
- }
- result = exp->optimize(WANTvalue);
- return;
- }
- }
- // Check for: element ~ array
- if ((tb2->ty == Tsarray || tb2->ty == Tarray) && tb1->ty != Tvoid)
- {
- if (exp->e2->op == TOKarrayliteral)
- {
- exp->e1 = exp->e1->isLvalue() ? callCpCtor(sc, exp->e1) : valueNoDtor(exp->e1);
- }
- else if (exp->e2->op == TOKstring)
- {
- }
- else
- {
- if (exp->e1->checkPostblit(sc, tb1))
- return setError();
- }
-
- if (exp->e2->op == TOKarrayliteral && exp->e2->implicitConvTo(tb1->arrayOf()))
- {
- exp->e2 = exp->e2->implicitCastTo(sc, tb1->arrayOf());
- exp->type = tb1->arrayOf();
- goto L1elem;
- }
- if (exp->e1->implicitConvTo(tb2next) >= MATCHconvert)
- {
- exp->e1 = exp->e1->implicitCastTo(sc, tb2next);
- exp->type = tb2next->arrayOf();
- L1elem:
- if (tb1->ty == Tarray || tb1->ty == Tsarray)
- {
- // Make e1 into [e1]
- exp->e1 = new ArrayLiteralExp(exp->e1->loc, exp->type, exp->e1);
- }
- result = exp->optimize(WANTvalue);
- return;
- }
- }
-
- Lpeer:
- if ((tb1->ty == Tsarray || tb1->ty == Tarray) &&
- (tb2->ty == Tsarray || tb2->ty == Tarray) &&
- (tb1next->mod || tb2next->mod) &&
- (tb1next->mod != tb2next->mod)
- )
- {
- Type *t1 = tb1next->mutableOf()->constOf()->arrayOf();
- Type *t2 = tb2next->mutableOf()->constOf()->arrayOf();
- if (exp->e1->op == TOKstring && !((StringExp *)exp->e1)->committed)
- exp->e1->type = t1;
- else
- exp->e1 = exp->e1->castTo(sc, t1);
- if (exp->e2->op == TOKstring && !((StringExp *)exp->e2)->committed)
- exp->e2->type = t2;
- else
- exp->e2 = exp->e2->castTo(sc, t2);
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
- exp->type = exp->type->toHeadMutable();
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tsarray)
- exp->type = tb->nextOf()->arrayOf();
- if (exp->type->ty == Tarray && tb1next && tb2next &&
- tb1next->mod != tb2next->mod)
- {
- exp->type = exp->type->nextOf()->toHeadMutable()->arrayOf();
- }
- if (Type *tbn = tb->nextOf())
- {
- if (exp->checkPostblit(sc, tbn))
- return setError();
- }
- Type *t1 = exp->e1->type->toBasetype();
- Type *t2 = exp->e2->type->toBasetype();
- if ((t1->ty == Tarray || t1->ty == Tsarray) &&
- (t2->ty == Tarray || t2->ty == Tsarray))
- {
- // Normalize to ArrayLiteralExp or StringExp as far as possible
- e = exp->optimize(WANTvalue);
- }
- else
- {
- //printf("(%s) ~ (%s)\n", exp->e1->toChars(), exp->e2->toChars());
- result = exp->incompatibleTypes();
- return;
- }
- result = e;
- }
-
- void visit(MulExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- if (exp->checkArithmeticBin())
- return setError();
-
- if (exp->type->isfloating())
- {
- Type *t1 = exp->e1->type;
- Type *t2 = exp->e2->type;
-
- if (t1->isreal())
- {
- exp->type = t2;
- }
- else if (t2->isreal())
- {
- exp->type = t1;
- }
- else if (t1->isimaginary())
- {
- if (t2->isimaginary())
- {
-
- switch (t1->toBasetype()->ty)
- {
- case Timaginary32:
- exp->type = Type::tfloat32;
- break;
-
- case Timaginary64:
- exp->type = Type::tfloat64;
- break;
-
- case Timaginary80:
- exp->type = Type::tfloat80;
- break;
-
- default:
- assert(0);
- }
-
- // iy * iv = -yv
- exp->e1->type = exp->type;
- exp->e2->type = exp->type;
- e = new NegExp(exp->loc, exp);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- else
- exp->type = t2; // t2 is complex
- }
- else if (t2->isimaginary())
- {
- exp->type = t1; // t1 is complex
- }
- }
- else if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
- result = exp;
- }
-
- void visit(DivExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- if (exp->checkArithmeticBin())
- return setError();
-
- if (exp->type->isfloating())
- {
- Type *t1 = exp->e1->type;
- Type *t2 = exp->e2->type;
-
- if (t1->isreal())
- {
- exp->type = t2;
- if (t2->isimaginary())
- {
- // x/iv = i(-x/v)
- exp->e2->type = t1;
- e = new NegExp(exp->loc, exp);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
- }
- else if (t2->isreal())
- {
- exp->type = t1;
- }
- else if (t1->isimaginary())
- {
- if (t2->isimaginary())
- {
- switch (t1->toBasetype()->ty)
- {
- case Timaginary32:
- exp->type = Type::tfloat32;
- break;
-
- case Timaginary64:
- exp->type = Type::tfloat64;
- break;
-
- case Timaginary80:
- exp->type = Type::tfloat80;
- break;
-
- default:
- assert(0);
- }
- }
- else
- exp->type = t2; // t2 is complex
- }
- else if (t2->isimaginary())
- {
- exp->type = t1; // t1 is complex
- }
- }
- else if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
- result = exp;
- }
-
- void visit(ModExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
- if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
-
- if (exp->checkArithmeticBin())
- return setError();
-
- if (exp->type->isfloating())
- {
- exp->type = exp->e1->type;
- if (exp->e2->type->iscomplex())
- {
- exp->error("cannot perform modulo complex arithmetic");
- return setError();
- }
- }
- result = exp;
- }
-
- void visit(PowExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- //printf("PowExp::semantic() %s\n", exp->toChars());
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- if (exp->checkArithmeticBin())
- return setError();
-
- if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
-
- // For built-in numeric types, there are several cases.
- // TODO: backend support, especially for e1 ^^ 2.
-
- // First, attempt to fold the expression.
- e = exp->optimize(WANTvalue);
- if (e->op != TOKpow)
- {
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
-
- // Determine if we're raising to an integer power.
- sinteger_t intpow = 0;
- if (exp->e2->op == TOKint64 && ((sinteger_t)exp->e2->toInteger() == 2 || (sinteger_t)exp->e2->toInteger() == 3))
- intpow = exp->e2->toInteger();
- else if (exp->e2->op == TOKfloat64 && (exp->e2->toReal() == ldouble((sinteger_t)exp->e2->toReal())))
- intpow = (sinteger_t)(exp->e2->toReal());
-
- // Deal with x^^2, x^^3 immediately, since they are of practical importance.
- if (intpow == 2 || intpow == 3)
- {
- // Replace x^^2 with (tmp = x, tmp*tmp)
- // Replace x^^3 with (tmp = x, tmp*tmp*tmp)
- VarDeclaration *tmp = copyToTemp(0, "__powtmp", exp->e1);
- Expression *de = new DeclarationExp(exp->loc, tmp);
- Expression *ve = new VarExp(exp->loc, tmp);
-
- /* Note that we're reusing ve. This should be ok.
- */
- Expression *me = new MulExp(exp->loc, ve, ve);
- if (intpow == 3)
- me = new MulExp(exp->loc, me, ve);
- e = new CommaExp(exp->loc, de, me);
- e = expressionSemantic(e, sc);
- result = e;
- return;
- }
-
- Module *mmath = loadStdMath();
- if (!mmath)
- {
- //exp->error("requires std.math for ^^ operators");
- //fatal();
-
- // Leave handling of PowExp to the backend, or throw
- // an error gracefully if no backend support exists.
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
- result = exp;
- return;
- }
- e = new ScopeExp(exp->loc, mmath);
-
- if (exp->e2->op == TOKfloat64 && exp->e2->toReal() == CTFloat::half)
- {
- // Replace e1 ^^ 0.5 with .std.math.sqrt(x)
- e = new CallExp(exp->loc, new DotIdExp(exp->loc, e, Id::_sqrt), exp->e1);
- }
- else
- {
- // Replace e1 ^^ e2 with .std.math.pow(e1, e2)
- e = new CallExp(exp->loc, new DotIdExp(exp->loc, e, Id::_pow), exp->e1, exp->e2);
- }
- e = expressionSemantic(e, sc);
- result = e;
- }
-
- void visit(ShlExp *exp)
- {
- //printf("ShlExp::semantic(), type = %p\n", exp->type);
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->checkIntegralBin())
- return setError();
- if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
- exp->e1 = integralPromotions(exp->e1, sc);
- if (exp->e2->type->toBasetype()->ty != Tvector)
- exp->e2 = exp->e2->castTo(sc, Type::tshiftcnt);
-
- exp->type = exp->e1->type;
- result = exp;
- }
-
- void visit(ShrExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->checkIntegralBin())
- return setError();
- if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
- exp->e1 = integralPromotions(exp->e1, sc);
- if (exp->e2->type->toBasetype()->ty != Tvector)
- exp->e2 = exp->e2->castTo(sc, Type::tshiftcnt);
-
- exp->type = exp->e1->type;
- result = exp;
- }
-
- void visit(UshrExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->checkIntegralBin())
- return setError();
- if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
- exp->e1 = integralPromotions(exp->e1, sc);
- if (exp->e2->type->toBasetype()->ty != Tvector)
- exp->e2 = exp->e2->castTo(sc, Type::tshiftcnt);
-
- exp->type = exp->e1->type;
- result = exp;
- }
-
- void visit(AndExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->e1->type->toBasetype()->ty == Tbool &&
- exp->e2->type->toBasetype()->ty == Tbool)
- {
- exp->type = exp->e1->type;
- result = exp;
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
- if (exp->checkIntegralBin())
- return setError();
-
- result = exp;
- }
-
- void visit(OrExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->e1->type->toBasetype()->ty == Tbool &&
- exp->e2->type->toBasetype()->ty == Tbool)
- {
- exp->type = exp->e1->type;
- result = exp;
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
- if (exp->checkIntegralBin())
- return setError();
-
- result = exp;
- }
-
- void visit(XorExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- if (exp->e1->type->toBasetype()->ty == Tbool &&
- exp->e2->type->toBasetype()->ty == Tbool)
- {
- exp->type = exp->e1->type;
- result = exp;
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- Type *tb = exp->type->toBasetype();
- if (tb->ty == Tarray || tb->ty == Tsarray)
- {
- if (!isArrayOpValid(exp))
- {
- exp->error("invalid array operation %s (possible missing [])", exp->toChars());
- return setError();
- }
- result = exp;
- return;
- }
-
- if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
- {
- result = exp->incompatibleTypes();
- return;
- }
- if (exp->checkIntegralBin())
- return setError();
-
- result = exp;
- }
-
- void visit(LogicalExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- setNoderefOperands(exp);
-
- Expression *e1x = expressionSemantic(exp->e1, sc);
-
- // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
- if (e1x->op == TOKtype)
- e1x = resolveAliasThis(sc, e1x);
-
- e1x = resolveProperties(sc, e1x);
- e1x = e1x->toBoolean(sc);
- unsigned cs1 = sc->callSuper;
-
- if (sc->flags & SCOPEcondition)
- {
- /* If in static if, don't evaluate e2 if we don't have to.
- */
- e1x = e1x->optimize(WANTvalue);
- if (e1x->isBool(exp->op == TOKoror))
- {
- result = new IntegerExp(exp->loc, exp->op == TOKoror, Type::tbool);
- return;
- }
- }
-
- Expression *e2x = expressionSemantic(exp->e2, sc);
- sc->mergeCallSuper(exp->loc, cs1);
-
- // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
- if (e2x->op == TOKtype)
- e2x = resolveAliasThis(sc, e2x);
-
- e2x = resolveProperties(sc, e2x);
-
- bool f1 = checkNonAssignmentArrayOp(e1x);
- bool f2 = checkNonAssignmentArrayOp(e2x);
- if (f1 || f2)
- return setError();
-
- // Unless the right operand is 'void', the expression is converted to 'bool'.
- if (e2x->type->ty != Tvoid)
- e2x = e2x->toBoolean(sc);
-
- if (e2x->op == TOKtype || e2x->op == TOKscope)
- {
- exp->error("%s is not an expression", exp->e2->toChars());
- return setError();
- }
- if (e1x->op == TOKerror || e1x->type->ty == Tnoreturn)
- {
- result = e1x;
- return;
- }
- if (e2x->op == TOKerror)
- {
- result = e2x;
- return;
- }
-
- // The result type is 'bool', unless the right operand has type 'void'.
- if (e2x->type->ty == Tvoid)
- exp->type = Type::tvoid;
- else
- exp->type = Type::tbool;
-
- exp->e1 = e1x;
- exp->e2 = e2x;
- result = exp;
- }
-
- void visit(InExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- result = e;
- return;
- }
-
- Type *t2b = exp->e2->type->toBasetype();
- switch (t2b->ty)
- {
- case Taarray:
- {
- TypeAArray *ta = (TypeAArray *)t2b;
-
- // Special handling for array keys
- if (!arrayTypeCompatible(exp->e1->loc, exp->e1->type, ta->index))
- {
- // Convert key to type of key
- exp->e1 = exp->e1->implicitCastTo(sc, ta->index);
- }
-
- semanticTypeInfo(sc, ta->index);
-
- // Return type is pointer to value
- exp->type = ta->nextOf()->pointerTo();
- break;
- }
-
- default:
- result = exp->incompatibleTypes();
- return;
-
- case Terror:
- return setError();
- }
- result = exp;
- }
-
- void visit(RemoveExp *e)
- {
- if (Expression *ex = binSemantic(e, sc))
- {
- result = ex;
- return;
- }
- result = e;
- }
-
- void visit(CmpExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- setNoderefOperands(exp);
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
- Type *t1 = exp->e1->type->toBasetype();
- Type *t2 = exp->e2->type->toBasetype();
- if ((t1->ty == Tclass && exp->e2->op == TOKnull) ||
- (t2->ty == Tclass && exp->e1->op == TOKnull))
- {
- exp->error("do not use null when comparing class types");
- return setError();
- }
-
- Expression *e = exp->op_overload(sc);
- if (e)
- {
- if (!e->type->isscalar() && e->type->equals(exp->e1->type))
- {
- exp->error("recursive opCmp expansion");
- return setError();
- }
- if (e->op == TOKcall)
- {
- e = new CmpExp(exp->op, exp->loc, e, new IntegerExp(exp->loc, 0, Type::tint32));
- e = expressionSemantic(e, sc);
- }
- result = e;
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- bool f1 = checkNonAssignmentArrayOp(exp->e1);
- bool f2 = checkNonAssignmentArrayOp(exp->e2);
- if (f1 || f2)
- return setError();
-
- exp->type = Type::tbool;
-
- // Special handling for array comparisons
- t1 = exp->e1->type->toBasetype();
- t2 = exp->e2->type->toBasetype();
- if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) &&
- (t2->ty == Tarray || t2->ty == Tsarray || t2->ty == Tpointer))
- {
- Type *t1next = t1->nextOf();
- Type *t2next = t2->nextOf();
- if (t1next->implicitConvTo(t2next) < MATCHconst &&
- t2next->implicitConvTo(t1next) < MATCHconst &&
- (t1next->ty != Tvoid && t2next->ty != Tvoid))
- {
- exp->error("array comparison type mismatch, %s vs %s", t1next->toChars(), t2next->toChars());
- return setError();
- }
- if ((t1->ty == Tarray || t1->ty == Tsarray) &&
- (t2->ty == Tarray || t2->ty == Tsarray))
- {
- semanticTypeInfo(sc, t1->nextOf());
- }
- }
- else if (t1->ty == Tstruct || t2->ty == Tstruct ||
- (t1->ty == Tclass && t2->ty == Tclass))
- {
- if (t2->ty == Tstruct)
- exp->error("need member function opCmp() for %s %s to compare", t2->toDsymbol(sc)->kind(), t2->toChars());
- else
- exp->error("need member function opCmp() for %s %s to compare", t1->toDsymbol(sc)->kind(), t1->toChars());
- return setError();
- }
- else if (t1->iscomplex() || t2->iscomplex())
- {
- exp->error("compare not defined for complex operands");
- return setError();
- }
- else if (t1->ty == Taarray || t2->ty == Taarray)
- {
- exp->error("%s is not defined for associative arrays", Token::toChars(exp->op));
- return setError();
- }
- else if (!target.isVectorOpSupported(t1, exp->op, t2))
- {
- result = exp->incompatibleTypes();
- return;
- }
- else
- {
- bool r1 = exp->e1->checkValue();
- bool r2 = exp->e2->checkValue();
- if (r1 || r2)
- return setError();
- }
-
- TOK altop;
- switch (exp->op)
- {
- // Refer rel_integral[] table
- case TOKunord: altop = TOKerror; break;
- case TOKlg: altop = TOKnotequal; break;
- case TOKleg: altop = TOKerror; break;
- case TOKule: altop = TOKle; break;
- case TOKul: altop = TOKlt; break;
- case TOKuge: altop = TOKge; break;
- case TOKug: altop = TOKgt; break;
- case TOKue: altop = TOKequal; break;
- default: altop = TOKreserved; break;
- }
- if (altop == TOKerror &&
- (t1->ty == Tarray || t1->ty == Tsarray ||
- t2->ty == Tarray || t2->ty == Tsarray))
- {
- exp->error("`%s` is not defined for array comparisons", Token::toChars(exp->op));
- return setError();
- }
- if (altop != TOKreserved)
- {
- if (!t1->isfloating())
- {
- if (altop == TOKerror)
- {
- const char *s = exp->op == TOKunord ? "false" : "true";
- exp->error("floating point operator `%s` always returns %s for non-floating comparisons",
- Token::toChars(exp->op), s);
- }
- else
- {
- exp->error("use `%s` for non-floating comparisons rather than floating point operator `%s`",
- Token::toChars(altop), Token::toChars(exp->op));
- }
- }
- else
- {
- exp->error("use std.math.isNaN to deal with NaN operands rather than floating point operator `%s`",
- Token::toChars(exp->op));
- }
- return setError();
- }
-
- //printf("CmpExp: %s, type = %s\n", e->toChars(), e->type->toChars());
- result = exp;
- }
-
- void visit(EqualExp *exp)
- {
- //printf("EqualExp::semantic('%s')\n", toChars());
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- setNoderefOperands(exp);
-
- if (Expression *e = binSemanticProp(exp, sc))
- {
- result = e;
- return;
- }
- if (exp->e1->op == TOKtype || exp->e2->op == TOKtype)
- {
- result = exp->incompatibleTypes();
- return;
- }
-
- {
- Type *t1 = exp->e1->type;
- Type *t2 = exp->e2->type;
- if (t1->ty == Tenum && t2->ty == Tenum && !t1->equivalent(t2))
- exp->deprecation("Comparison between different enumeration types `%s` and `%s`; If this behavior is intended consider using `std.conv.asOriginalType`",
- t1->toChars(), t2->toChars());
- }
-
- /* Before checking for operator overloading, check to see if we're
- * comparing the addresses of two statics. If so, we can just see
- * if they are the same symbol.
- */
- if (exp->e1->op == TOKaddress && exp->e2->op == TOKaddress)
- {
- AddrExp *ae1 = (AddrExp *)exp->e1;
- AddrExp *ae2 = (AddrExp *)exp->e2;
- if (ae1->e1->op == TOKvar && ae2->e1->op == TOKvar)
- {
- VarExp *ve1 = (VarExp *)ae1->e1;
- VarExp *ve2 = (VarExp *)ae2->e1;
-
- if (ve1->var == ve2->var)
- {
- // They are the same, result is 'true' for ==, 'false' for !=
- result = new IntegerExp(exp->loc, (exp->op == TOKequal), Type::tbool);
- return;
- }
- }
- }
-
- if (Expression *e = exp->op_overload(sc))
- {
- result = e;
- return;
- }
-
- if (Expression *e = typeCombine(exp, sc))
- {
- result = e;
- return;
- }
-
- bool f1 = checkNonAssignmentArrayOp(exp->e1);
- bool f2 = checkNonAssignmentArrayOp(exp->e2);
- if (f1 || f2)
- return setError();
-
- exp->type = Type::tbool;
-
- // Special handling for array comparisons
- if (!arrayTypeCompatible(exp->loc, exp->e1->type, exp->e2->type))
- {
- if (exp->e1->type != exp->e2->type && exp->e1->type->isfloating() && exp->e2->type->isfloating())
- {
- // Cast both to complex
- exp->e1 = exp->e1->castTo(sc, Type::tcomplex80);
- exp->e2 = exp->e2->castTo(sc, Type::tcomplex80);
- }
- }
- if (exp->e1->type->toBasetype()->ty == Taarray)
- semanticTypeInfo(sc, exp->e1->type->toBasetype());
-
- Type *t1 = exp->e1->type->toBasetype();
- Type *t2 = exp->e2->type->toBasetype();
-
- if (!target.isVectorOpSupported(t1, exp->op, t2))
- {
- result = exp->incompatibleTypes();
- return;
- }
-
- result = exp;
- }
-
- void visit(IdentityExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- setNoderefOperands(exp);
-
- if (Expression *ex = binSemanticProp(exp, sc))
- {
- result = ex;
- return;
- }
-
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
-
- bool f1 = checkNonAssignmentArrayOp(exp->e1);
- bool f2 = checkNonAssignmentArrayOp(exp->e2);
- if (f1 || f2)
- return setError();
-
- if (exp->e1->op == TOKtype || exp->e2->op == TOKtype)
- {
- result = exp->incompatibleTypes();
- return;
- }
-
- exp->type = Type::tbool;
-
- if (exp->e1->type != exp->e2->type && exp->e1->type->isfloating() && exp->e2->type->isfloating())
- {
- // Cast both to complex
- exp->e1 = exp->e1->castTo(sc, Type::tcomplex80);
- exp->e2 = exp->e2->castTo(sc, Type::tcomplex80);
- }
-
- Type *tb1 = exp->e1->type->toBasetype();
- Type *tb2 = exp->e2->type->toBasetype();
- if (!target.isVectorOpSupported(tb1, exp->op, tb2))
- {
- result = exp->incompatibleTypes();
- return;
- }
-
- result = exp;
- }
-
- void visit(CondExp *exp)
- {
- if (exp->type)
- {
- result = exp;
- return;
- }
-
- if (exp->econd->op == TOKdotid)
- ((DotIdExp *)exp->econd)->noderef = true;
-
- Expression *ec = expressionSemantic(exp->econd, sc);
- ec = resolveProperties(sc, ec);
- ec = ec->toBoolean(sc);
-
- unsigned cs0 = sc->callSuper;
- unsigned *fi0 = sc->saveFieldInit();
- Expression *e1x = expressionSemantic(exp->e1, sc);
- e1x = resolveProperties(sc, e1x);
-
- unsigned cs1 = sc->callSuper;
- unsigned *fi1 = sc->fieldinit;
- sc->callSuper = cs0;
- sc->fieldinit = fi0;
- Expression *e2x = expressionSemantic(exp->e2, sc);
- e2x = resolveProperties(sc, e2x);
-
- sc->mergeCallSuper(exp->loc, cs1);
- sc->mergeFieldInit(exp->loc, fi1);
-
- if (ec->op == TOKerror)
- {
- result = ec;
- return;
- }
- if (ec->type == Type::terror)
- return setError();
- exp->econd = ec;
-
- if (e1x->op == TOKerror)
- {
- result = e1x;
- return;
- }
- if (e1x->type == Type::terror)
- return setError();
- exp->e1 = e1x;
-
- if (e2x->op == TOKerror)
- {
- result = e2x;
- return;
- }
- if (e2x->type == Type::terror)
- return setError();
- exp->e2 = e2x;
-
- bool f0 = checkNonAssignmentArrayOp(exp->econd);
- bool f1 = checkNonAssignmentArrayOp(exp->e1);
- bool f2 = checkNonAssignmentArrayOp(exp->e2);
- if (f0 || f1 || f2)
- return setError();
-
- Type *t1 = exp->e1->type;
- Type *t2 = exp->e2->type;
- if (t1->ty == Tnoreturn)
- {
- exp->type = t2;
- }
- else if (t2->ty == Tnoreturn)
- {
- exp->type = t1;
- }
- // If either operand is void the result is void, we have to cast both
- // the expression to void so that we explicitly discard the expression
- // value if any (bugzilla 16598)
- else if (t1->ty == Tvoid || t2->ty == Tvoid)
- {
- exp->type = Type::tvoid;
- exp->e1 = exp->e1->castTo(sc, exp->type);
- exp->e2 = exp->e2->castTo(sc, exp->type);
- }
- else if (t1 == t2)
- exp->type = t1;
- else
- {
- if (Expression *ex = typeCombine(exp, sc))
- {
- result = ex;
- return;
- }
- switch (exp->e1->type->toBasetype()->ty)
- {
- case Tcomplex32:
- case Tcomplex64:
- case Tcomplex80:
- exp->e2 = exp->e2->castTo(sc, exp->e1->type);
- break;
- }
- switch (exp->e2->type->toBasetype()->ty)
- {
- case Tcomplex32:
- case Tcomplex64:
- case Tcomplex80:
- exp->e1 = exp->e1->castTo(sc, exp->e2->type);
- break;
- }
- if (exp->type->toBasetype()->ty == Tarray)
- {
- exp->e1 = exp->e1->castTo(sc, exp->type);
- exp->e2 = exp->e2->castTo(sc, exp->type);
- }
- }
- exp->type = exp->type->merge2();
-
- /* Bugzilla 14696: If either e1 or e2 contain temporaries which need dtor,
- * make them conditional.
- * Rewrite:
- * cond ? (__tmp1 = ..., __tmp1) : (__tmp2 = ..., __tmp2)
- * to:
- * (auto __cond = cond) ? (... __tmp1) : (... __tmp2)
- * and replace edtors of __tmp1 and __tmp2 with:
- * __tmp1->edtor --> __cond && __tmp1.dtor()
- * __tmp2->edtor --> __cond || __tmp2.dtor()
- */
- exp->hookDtors(sc);
-
- result = exp;
- }
-
- void visit(FileInitExp *e)
- {
- //printf("FileInitExp::semantic()\n");
- e->type = Type::tstring;
- result = e;
- }
-
- void visit(LineInitExp *e)
- {
- e->type = Type::tint32;
- result = e;
- }
-
- void visit(ModuleInitExp *e)
- {
- //printf("ModuleInitExp::semantic()\n");
- e->type = Type::tstring;
- result = e;
- }
-
- void visit(FuncInitExp *e)
- {
- //printf("FuncInitExp::semantic()\n");
- e->type = Type::tstring;
- if (sc->func)
- {
- result = e->resolveLoc(Loc(), sc);
- return;
- }
- result = e;
- }
-
- void visit(PrettyFuncInitExp *e)
- {
- //printf("PrettyFuncInitExp::semantic()\n");
- e->type = Type::tstring;
- if (sc->func)
- {
- result = e->resolveLoc(Loc(), sc);
- return;
- }
- result = e;
- }
-};
-
-/**********************************
- * Try to run semantic routines.
- * If they fail, return NULL.
- */
-Expression *trySemantic(Expression *exp, Scope* sc)
-{
- //printf("+trySemantic(%s)\n", toChars());
- unsigned errors = global.startGagging();
- Expression *e = expressionSemantic(exp, sc);
- if (global.endGagging(errors))
- {
- e = NULL;
- }
- //printf("-trySemantic(%s)\n", toChars());
- return e;
-}
-
-/**************************
- * Helper function for easy error propagation.
- * If error occurs, returns ErrorExp. Otherwise returns NULL.
- */
-Expression *unaSemantic(UnaExp *e, Scope *sc)
-{
- Expression *e1x = expressionSemantic(e->e1, sc);
- if (e1x->op == TOKerror)
- return e1x;
- e->e1 = e1x;
- return NULL;
-}
-
-/**************************
- * Helper function for easy error propagation.
- * If error occurs, returns ErrorExp. Otherwise returns NULL.
- */
-Expression *binSemantic(BinExp *e, Scope *sc)
-{
- Expression *e1x = expressionSemantic(e->e1, sc);
- Expression *e2x = expressionSemantic(e->e2, sc);
-
- // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
- if (e1x->op == TOKtype)
- e1x = resolveAliasThis(sc, e1x);
- if (e2x->op == TOKtype)
- e2x = resolveAliasThis(sc, e2x);
-
- if (e1x->op == TOKerror)
- return e1x;
- if (e2x->op == TOKerror)
- return e2x;
- e->e1 = e1x;
- e->e2 = e2x;
- return NULL;
-}
-
-Expression *binSemanticProp(BinExp *e, Scope *sc)
-{
- if (Expression *ex = binSemantic(e, sc))
- return ex;
- Expression *e1x = resolveProperties(sc, e->e1);
- Expression *e2x = resolveProperties(sc, e->e2);
- if (e1x->op == TOKerror)
- return e1x;
- if (e2x->op == TOKerror)
- return e2x;
- e->e1 = e1x;
- e->e2 = e2x;
- return NULL;
-}
-
-// entrypoint for semantic ExpressionSemanticVisitor
-Expression *expressionSemantic(Expression *e, Scope *sc)
-{
- ExpressionSemanticVisitor v = ExpressionSemanticVisitor(sc);
- e->accept(&v);
- return v.result;
-}
-
-Expression *semanticX(DotIdExp *exp, Scope *sc)
-{
- //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars());
- if (Expression *ex = unaSemantic(exp, sc))
- return ex;
-
- if (exp->ident == Id::_mangleof)
- {
- // symbol.mangleof
- Dsymbol *ds;
- switch (exp->e1->op)
- {
- case TOKscope:
- ds = ((ScopeExp *)exp->e1)->sds;
- goto L1;
- case TOKvar:
- ds = ((VarExp *)exp->e1)->var;
- goto L1;
- case TOKdotvar:
- ds = ((DotVarExp *)exp->e1)->var;
- goto L1;
- case TOKoverloadset:
- ds = ((OverExp *)exp->e1)->vars;
- goto L1;
- case TOKtemplate:
- {
- TemplateExp *te = (TemplateExp *)exp->e1;
- ds = te->fd ? (Dsymbol *)te->fd : te->td;
- }
- L1:
- {
- assert(ds);
- if (FuncDeclaration *f = ds->isFuncDeclaration())
- {
- if (f->checkForwardRef(exp->loc))
- return new ErrorExp();
- }
- OutBuffer buf;
- mangleToBuffer(ds, &buf);
- const char *s = buf.extractChars();
- Expression *e = new StringExp(exp->loc, const_cast<char*>(s), strlen(s));
- e = expressionSemantic(e, sc);
- return e;
- }
- default:
- break;
- }
- }
-
- if (exp->e1->op == TOKvar && exp->e1->type->toBasetype()->ty == Tsarray && exp->ident == Id::length)
- {
- // bypass checkPurity
- return exp->e1->type->dotExp(sc, exp->e1, exp->ident, exp->noderef ? 2 : 0);
- }
-
- if (exp->e1->op == TOKdot)
- {
- }
- else
- {
- exp->e1 = resolvePropertiesX(sc, exp->e1);
- }
- if (exp->e1->op == TOKtuple && exp->ident == Id::offsetof)
- {
- /* 'distribute' the .offsetof to each of the tuple elements.
- */
- TupleExp *te = (TupleExp *)exp->e1;
- Expressions *exps = new Expressions();
- exps->setDim(te->exps->length);
- for (size_t i = 0; i < exps->length; i++)
- {
- Expression *e = (*te->exps)[i];
- e = expressionSemantic(e, sc);
- e = new DotIdExp(e->loc, e, Id::offsetof);
- (*exps)[i] = e;
- }
- // Don't evaluate te->e0 in runtime
- Expression *e = new TupleExp(exp->loc, NULL, exps);
- e = expressionSemantic(e, sc);
- return e;
- }
- if (exp->e1->op == TOKtuple && exp->ident == Id::length)
- {
- TupleExp *te = (TupleExp *)exp->e1;
- // Don't evaluate te->e0 in runtime
- Expression *e = new IntegerExp(exp->loc, te->exps->length, Type::tsize_t);
- return e;
- }
-
- // Bugzilla 14416: Template has no built-in properties except for 'stringof'.
- if ((exp->e1->op == TOKdottd || exp->e1->op == TOKtemplate) && exp->ident != Id::stringof)
- {
- exp->error("template %s does not have property `%s`", exp->e1->toChars(), exp->ident->toChars());
- return new ErrorExp();
- }
-
- if (!exp->e1->type)
- {
- exp->error("expression %s does not have property `%s`", exp->e1->toChars(), exp->ident->toChars());
- return new ErrorExp();
- }
-
- return exp;
-}
-
-// Resolve e1.ident without seeing UFCS.
-// If flag == 1, stop "not a property" error and return NULL.
-Expression *semanticY(DotIdExp *exp, Scope *sc, int flag)
-{
- //printf("DotIdExp::semanticY(this = %p, '%s')\n", this, toChars());
-
- //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }
-
- /* Special case: rewrite this.id and super.id
- * to be classtype.id and baseclasstype.id
- * if we have no this pointer.
- */
- if ((exp->e1->op == TOKthis || exp->e1->op == TOKsuper) && !hasThis(sc))
- {
- if (AggregateDeclaration *ad = sc->getStructClassScope())
- {
- if (exp->e1->op == TOKthis)
- {
- exp->e1 = new TypeExp(exp->e1->loc, ad->type);
- }
- else
- {
- ClassDeclaration *cd = ad->isClassDeclaration();
- if (cd && cd->baseClass)
- exp->e1 = new TypeExp(exp->e1->loc, cd->baseClass->type);
- }
- }
- }
-
- Expression *e = semanticX(exp, sc);
- if (e != exp)
- return e;
-
- Expression *eleft;
- Expression *eright;
- if (exp->e1->op == TOKdot)
- {
- DotExp *de = (DotExp *)exp->e1;
- eleft = de->e1;
- eright = de->e2;
- }
- else
- {
- eleft = NULL;
- eright = exp->e1;
- }
-
- Type *t1b = exp->e1->type->toBasetype();
-
- if (eright->op == TOKscope) // also used for template alias's
- {
- ScopeExp *ie = (ScopeExp *)eright;
- int flags = SearchLocalsOnly;
-
- /* Disable access to another module's private imports.
- * The check for 'is sds our current module' is because
- * the current module should have access to its own imports.
- */
- if (ie->sds->isModule() && ie->sds != sc->_module)
- flags |= IgnorePrivateImports;
- if (sc->flags & SCOPEignoresymbolvisibility)
- flags |= IgnoreSymbolVisibility;
- Dsymbol *s = ie->sds->search(exp->loc, exp->ident, flags);
- /* Check for visibility before resolving aliases because public
- * aliases to private symbols are public.
- */
- if (s && !(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc->_module, s))
- {
- s = NULL;
- }
- if (s)
- {
- Package *p = s->isPackage();
- if (p && checkAccess(sc, p))
- {
- s = NULL;
- }
- }
- if (s)
- {
- // if 's' is a tuple variable, the tuple is returned.
- s = s->toAlias();
-
- exp->checkDeprecated(sc, s);
- exp->checkDisabled(sc, s);
-
- EnumMember *em = s->isEnumMember();
- if (em)
- {
- return em->getVarExp(exp->loc, sc);
- }
-
- VarDeclaration *v = s->isVarDeclaration();
- if (v)
- {
- //printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars());
- if (!v->type ||
- (!v->type->deco && v->inuse))
- {
- if (v->inuse)
- exp->error("circular reference to %s `%s`", v->kind(), v->toPrettyChars());
- else
- exp->error("forward reference to %s `%s`", v->kind(), v->toPrettyChars());
- return new ErrorExp();
- }
- if (v->type->ty == Terror)
- return new ErrorExp();
-
- if ((v->storage_class & STCmanifest) && v->_init && !exp->wantsym)
- {
- /* Normally, the replacement of a symbol with its initializer is supposed to be in semantic2().
- * Introduced by https://github.com/dlang/dmd/pull/5588 which should probably
- * be reverted. `wantsym` is the hack to work around the problem.
- */
- if (v->inuse)
- {
- ::error(exp->loc, "circular initialization of %s `%s`", v->kind(), v->toPrettyChars());
- return new ErrorExp();
- }
- e = v->expandInitializer(exp->loc);
- v->inuse++;
- e = expressionSemantic(e, sc);
- v->inuse--;
- return e;
- }
-
- if (v->needThis())
- {
- if (!eleft)
- eleft = new ThisExp(exp->loc);
- e = new DotVarExp(exp->loc, eleft, v);
- e = expressionSemantic(e, sc);
- }
- else
- {
- e = new VarExp(exp->loc, v);
- if (eleft)
- { e = new CommaExp(exp->loc, eleft, e);
- e->type = v->type;
- }
- }
- e = e->deref();
- return expressionSemantic(e, sc);
- }
-
- FuncDeclaration *f = s->isFuncDeclaration();
- if (f)
- {
- //printf("it's a function\n");
- if (!f->functionSemantic())
- return new ErrorExp();
- if (f->needThis())
- {
- if (!eleft)
- eleft = new ThisExp(exp->loc);
- e = new DotVarExp(exp->loc, eleft, f, true);
- e = expressionSemantic(e, sc);
- }
- else
- {
- e = new VarExp(exp->loc, f, true);
- if (eleft)
- { e = new CommaExp(exp->loc, eleft, e);
- e->type = f->type;
- }
- }
- return e;
- }
- if (TemplateDeclaration *td = s->isTemplateDeclaration())
- {
- if (eleft)
- e = new DotTemplateExp(exp->loc, eleft, td);
- else
- e = new TemplateExp(exp->loc, td);
- e = expressionSemantic(e, sc);
- return e;
- }
- if (OverDeclaration *od = s->isOverDeclaration())
- {
- e = new VarExp(exp->loc, od, true);
- if (eleft)
- {
- e = new CommaExp(exp->loc, eleft, e);
- e->type = Type::tvoid; // ambiguous type?
- }
- return e;
- }
- OverloadSet *o = s->isOverloadSet();
- if (o)
- { //printf("'%s' is an overload set\n", o->toChars());
- return new OverExp(exp->loc, o);
- }
-
- if (Type *t = s->getType())
- {
- return expressionSemantic(new TypeExp(exp->loc, t), sc);
- }
-
- TupleDeclaration *tup = s->isTupleDeclaration();
- if (tup)
- {
- if (eleft)
- {
- e = new DotVarExp(exp->loc, eleft, tup);
- e = expressionSemantic(e, sc);
- return e;
- }
- e = new TupleExp(exp->loc, tup);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- ScopeDsymbol *sds = s->isScopeDsymbol();
- if (sds)
- {
- //printf("it's a ScopeDsymbol %s\n", exp->ident->toChars());
- e = new ScopeExp(exp->loc, sds);
- e = expressionSemantic(e, sc);
- if (eleft)
- e = new DotExp(exp->loc, eleft, e);
- return e;
- }
-
- Import *imp = s->isImport();
- if (imp)
- {
- ie = new ScopeExp(exp->loc, imp->pkg);
- return expressionSemantic(ie, sc);
- }
-
- // BUG: handle other cases like in IdentifierExp::semantic()
- assert(0);
- }
- else if (exp->ident == Id::stringof)
- {
- const char *p = ie->toChars();
- e = new StringExp(exp->loc, const_cast<char *>(p), strlen(p));
- e = expressionSemantic(e, sc);
- return e;
- }
- if (ie->sds->isPackage() ||
- ie->sds->isImport() ||
- ie->sds->isModule())
- {
- flag = 0;
- }
- if (flag)
- return NULL;
- s = ie->sds->search_correct(exp->ident);
- if (s)
- {
- if (s->isPackage())
- exp->error("undefined identifier `%s` in %s `%s`, perhaps add `static import %s;`",
- exp->ident->toChars(), ie->sds->kind(), ie->sds->toPrettyChars(), s->toPrettyChars());
- else
- exp->error("undefined identifier `%s` in %s `%s`, did you mean %s `%s`?",
- exp->ident->toChars(), ie->sds->kind(), ie->sds->toPrettyChars(), s->kind(), s->toChars());
- }
- else
- exp->error("undefined identifier `%s` in %s `%s`",
- exp->ident->toChars(), ie->sds->kind(), ie->sds->toPrettyChars());
- return new ErrorExp();
- }
- else if (t1b->ty == Tpointer && exp->e1->type->ty != Tenum &&
- exp->ident != Id::_init && exp->ident != Id::__sizeof &&
- exp->ident != Id::__xalignof && exp->ident != Id::offsetof &&
- exp->ident != Id::_mangleof && exp->ident != Id::stringof)
- {
- Type *t1bn = t1b->nextOf();
- if (flag)
- {
- AggregateDeclaration *ad = isAggregate(t1bn);
- if (ad && !ad->members) // Bugzilla 11312
- return NULL;
- }
-
- /* Rewrite:
- * p.ident
- * as:
- * (*p).ident
- */
- if (flag && t1bn->ty == Tvoid)
- return NULL;
- e = new PtrExp(exp->loc, exp->e1);
- e = expressionSemantic(e, sc);
- return e->type->dotExp(sc, e, exp->ident, flag | (exp->noderef ? 2 : 0));
- }
- else
- {
- if (exp->e1->op == TOKtype || exp->e1->op == TOKtemplate)
- flag = 0;
- e = exp->e1->type->dotExp(sc, exp->e1, exp->ident, flag | (exp->noderef ? 2 : 0));
- if (e)
- e = expressionSemantic(e, sc);
- return e;
- }
-}
-
-// Resolve e1.ident!tiargs without seeing UFCS.
-// If flag == 1, stop "not a property" error and return NULL.
-Expression *semanticY(DotTemplateInstanceExp *exp, Scope *sc, int flag)
-{
- DotIdExp *die = new DotIdExp(exp->loc, exp->e1, exp->ti->name);
-
- Expression *e = semanticX(die, sc);
- if (e == die)
- {
- exp->e1 = die->e1; // take back
-
- Type *t1b = exp->e1->type->toBasetype();
- if (t1b->ty == Tarray || t1b->ty == Tsarray || t1b->ty == Taarray ||
- t1b->ty == Tnull || (t1b->isTypeBasic() && t1b->ty != Tvoid))
- {
- /* No built-in type has templatized properties, so do shortcut.
- * It is necessary in: 1024.max!"a < b"
- */
- if (flag)
- return NULL;
- }
- e = semanticY(die, sc, flag);
- if (flag && e && isDotOpDispatch(e))
- {
- /* opDispatch!tiargs would be a function template that needs IFTI,
- * so it's not a template
- */
- e = NULL; /* fall down to UFCS */
- }
- if (flag && !e)
- return NULL;
- }
- assert(e);
-
- if (e->op == TOKerror)
- return e;
- if (e->op == TOKdotvar)
- {
- DotVarExp *dve = (DotVarExp *)e;
- if (FuncDeclaration *fd = dve->var->isFuncDeclaration())
- {
- TemplateDeclaration *td = fd->findTemplateDeclRoot();
- if (td)
- {
- e = new DotTemplateExp(dve->loc, dve->e1, td);
- e = expressionSemantic(e, sc);
- }
- }
- else if (dve->var->isOverDeclaration())
- {
- exp->e1 = dve->e1; // pull semantic() result
- if (!exp->findTempDecl(sc))
- goto Lerr;
- if (exp->ti->needsTypeInference(sc))
- return exp;
- dsymbolSemantic(exp->ti, sc);
- if (!exp->ti->inst || exp->ti->errors) // if template failed to expand
- return new ErrorExp();
- Dsymbol *s = exp->ti->toAlias();
- Declaration *v = s->isDeclaration();
- if (v)
- {
- if (v->type && !v->type->deco)
- v->type = typeSemantic(v->type, v->loc, sc);
- e = new DotVarExp(exp->loc, exp->e1, v);
- e = expressionSemantic(e, sc);
- return e;
- }
- e = new ScopeExp(exp->loc, exp->ti);
- e = new DotExp(exp->loc, exp->e1, e);
- e = expressionSemantic(e, sc);
- return e;
- }
- }
- else if (e->op == TOKvar)
- {
- VarExp *ve = (VarExp *)e;
- if (FuncDeclaration *fd = ve->var->isFuncDeclaration())
- {
- TemplateDeclaration *td = fd->findTemplateDeclRoot();
- if (td)
- {
- e = new TemplateExp(ve->loc, td);
- e = expressionSemantic(e, sc);
- }
- }
- else if (OverDeclaration *od = ve->var->isOverDeclaration())
- {
- exp->ti->tempdecl = od;
- e = new ScopeExp(exp->loc, exp->ti);
- e = expressionSemantic(e, sc);
- return e;
- }
- }
- if (e->op == TOKdottd)
- {
- DotTemplateExp *dte = (DotTemplateExp *)e;
- exp->e1 = dte->e1; // pull semantic() result
-
- exp->ti->tempdecl = dte->td;
- if (!exp->ti->semanticTiargs(sc))
- return new ErrorExp();
- if (exp->ti->needsTypeInference(sc))
- return exp;
- dsymbolSemantic(exp->ti, sc);
- if (!exp->ti->inst || exp->ti->errors) // if template failed to expand
- return new ErrorExp();
- Dsymbol *s = exp->ti->toAlias();
- Declaration *v = s->isDeclaration();
- if (v && (v->isFuncDeclaration() || v->isVarDeclaration()))
- {
- e = new DotVarExp(exp->loc, exp->e1, v);
- e = expressionSemantic(e, sc);
- return e;
- }
- e = new ScopeExp(exp->loc, exp->ti);
- e = new DotExp(exp->loc, exp->e1, e);
- e = expressionSemantic(e, sc);
- return e;
- }
- else if (e->op == TOKtemplate)
- {
- exp->ti->tempdecl = ((TemplateExp *)e)->td;
- e = new ScopeExp(exp->loc, exp->ti);
- e = expressionSemantic(e, sc);
- return e;
- }
- else if (e->op == TOKdot)
- {
- DotExp *de = (DotExp *)e;
-
- if (de->e2->op == TOKoverloadset)
- {
- if (!exp->findTempDecl(sc) ||
- !exp->ti->semanticTiargs(sc))
- {
- return new ErrorExp();
- }
- if (exp->ti->needsTypeInference(sc))
- return exp;
- dsymbolSemantic(exp->ti, sc);
- if (!exp->ti->inst || exp->ti->errors) // if template failed to expand
- return new ErrorExp();
- Dsymbol *s = exp->ti->toAlias();
- Declaration *v = s->isDeclaration();
- if (v)
- {
- if (v->type && !v->type->deco)
- v->type = typeSemantic(v->type, v->loc, sc);
- e = new DotVarExp(exp->loc, exp->e1, v);
- e = expressionSemantic(e, sc);
- return e;
- }
- e = new ScopeExp(exp->loc, exp->ti);
- e = new DotExp(exp->loc, exp->e1, e);
- e = expressionSemantic(e, sc);
- return e;
- }
- }
- else if (e->op == TOKoverloadset)
- {
- OverExp *oe = (OverExp *)e;
- exp->ti->tempdecl = oe->vars;
- e = new ScopeExp(exp->loc, exp->ti);
- e = expressionSemantic(e, sc);
- return e;
- }
-Lerr:
- e->error("%s isn't a template", e->toChars());
- return new ErrorExp();
-}
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
new file mode 100644
index 00000000000..3b604af53b9
--- /dev/null
+++ b/gcc/d/dmd/expressionsem.d
@@ -0,0 +1,13058 @@
+/**
+ * Semantic analysis of expressions.
+ *
+ * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/expressionsem.d, _expressionsem.d)
+ * Documentation: https://dlang.org/phobos/dmd_expressionsem.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expressionsem.d
+ */
+
+module dmd.expressionsem;
+
+import core.stdc.stdio;
+
+import dmd.access;
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arrayop;
+import dmd.arraytypes;
+import dmd.attrib;
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.canthrow;
+import dmd.chkformat;
+import dmd.ctorflow;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.declaration;
+import dmd.dclass;
+import dmd.dcast;
+import dmd.delegatize;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dinterpret;
+import dmd.dmangle;
+import dmd.dmodule;
+import dmd.dstruct;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.imphint;
+import dmd.init;
+import dmd.initsem;
+import dmd.inline;
+import dmd.intrange;
+import dmd.mtype;
+import dmd.nspace;
+import dmd.opover;
+import dmd.optimize;
+import dmd.parse;
+import dmd.printast;
+import dmd.root.ctfloat;
+import dmd.root.file;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.semantic2;
+import dmd.semantic3;
+import dmd.sideeffect;
+import dmd.safe;
+import dmd.target;
+import dmd.tokens;
+import dmd.traits;
+import dmd.typesem;
+import dmd.typinf;
+import dmd.utf;
+import dmd.utils;
+import dmd.visitor;
+
+enum LOGSEMANTIC = false;
+
+/********************************************************
+ * Perform semantic analysis and CTFE on expressions to produce
+ * a string.
+ * Params:
+ * buf = append generated string to buffer
+ * sc = context
+ * exps = array of Expressions
+ * Returns:
+ * true on error
+ */
+bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps)
+{
+ if (!exps)
+ return false;
+
+ foreach (ex; *exps)
+ {
+ if (!ex)
+ continue;
+ auto sc2 = sc.startCTFE();
+ auto e2 = ex.expressionSemantic(sc2);
+ auto e3 = resolveProperties(sc2, e2);
+ sc2.endCTFE();
+
+ // allowed to contain types as well as expressions
+ auto e4 = ctfeInterpretForPragmaMsg(e3);
+ if (!e4 || e4.op == TOK.error)
+ return true;
+
+ // expand tuple
+ if (auto te = e4.isTupleExp())
+ {
+ if (expressionsToString(buf, sc, te.exps))
+ return true;
+ continue;
+ }
+ // char literals exp `.toStringExp` return `null` but we cant override it
+ // because in most contexts we don't want the conversion to succeed.
+ IntegerExp ie = e4.isIntegerExp();
+ const ty = (ie && ie.type) ? ie.type.ty : Terror;
+ if (ty.isSomeChar)
+ {
+ auto tsa = new TypeSArray(ie.type, IntegerExp.literal!1);
+ e4 = new ArrayLiteralExp(ex.loc, tsa, ie);
+ }
+
+ if (StringExp se = e4.toStringExp())
+ buf.writestring(se.toUTF8(sc).peekString());
+ else
+ buf.writestring(e4.toString());
+ }
+ return false;
+}
+
+
+/***********************************************************
+ * Resolve `exp` as a compile-time known string.
+ * Params:
+ * sc = scope
+ * exp = Expression which expected as a string
+ * s = What the string is expected for, will be used in error diagnostic.
+ * Returns:
+ * String literal, or `null` if error happens.
+ */
+StringExp semanticString(Scope *sc, Expression exp, const char* s)
+{
+ sc = sc.startCTFE();
+ exp = exp.expressionSemantic(sc);
+ exp = resolveProperties(sc, exp);
+ sc = sc.endCTFE();
+
+ if (exp.op == TOK.error)
+ return null;
+
+ auto e = exp;
+ if (exp.type.isString())
+ {
+ e = e.ctfeInterpret();
+ if (e.op == TOK.error)
+ return null;
+ }
+
+ auto se = e.toStringExp();
+ if (!se)
+ {
+ exp.error("`string` expected for %s, not `(%s)` of type `%s`",
+ s, exp.toChars(), exp.type.toChars());
+ return null;
+ }
+ return se;
+}
+
+private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue)
+{
+ Expression e0;
+ Expression e1 = Expression.extractLast(ue.e1, e0);
+ // https://issues.dlang.org/show_bug.cgi?id=12585
+ // Extract the side effect part if ue.e1 is comma.
+
+ if ((sc.flags & SCOPE.ctfe) ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect()
+ {
+ /* Even if opDollar is needed, 'e1' should be evaluate only once. So
+ * Rewrite:
+ * e1.opIndex( ... use of $ ... )
+ * e1.opSlice( ... use of $ ... )
+ * as:
+ * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
+ * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
+ */
+ e1 = extractSideEffect(sc, "__dop", e0, e1, false);
+ assert(e1.isVarExp());
+ e1.isVarExp().var.storage_class |= STC.exptemp; // lifetime limited to expression
+ }
+ ue.e1 = e1;
+ return e0;
+}
+
+/**************************************
+ * Runs semantic on ae.arguments. Declares temporary variables
+ * if '$' was used.
+ */
+Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0)
+{
+ assert(!ae.lengthVar);
+ *pe0 = null;
+ AggregateDeclaration ad = isAggregate(ae.e1.type);
+ Dsymbol slice = search_function(ad, Id.slice);
+ //printf("slice = %s %s\n", slice.kind(), slice.toChars());
+ foreach (i, e; *ae.arguments)
+ {
+ if (i == 0)
+ *pe0 = extractOpDollarSideEffect(sc, ae);
+
+ if (e.op == TOK.interval && !(slice && slice.isTemplateDeclaration()))
+ {
+ Lfallback:
+ if (ae.arguments.dim == 1)
+ return null;
+ ae.error("multi-dimensional slicing requires template `opSlice`");
+ return ErrorExp.get();
+ }
+ //printf("[%d] e = %s\n", i, e.toChars());
+
+ // Create scope for '$' variable for this dimension
+ auto sym = new ArrayScopeSymbol(sc, ae);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+ ae.lengthVar = null; // Create it only if required
+ ae.currentDimension = i; // Dimension for $, if required
+
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+
+ if (ae.lengthVar && sc.func)
+ {
+ // If $ was used, declare it now
+ Expression de = new DeclarationExp(ae.loc, ae.lengthVar);
+ de = de.expressionSemantic(sc);
+ *pe0 = Expression.combine(*pe0, de);
+ }
+ sc = sc.pop();
+
+ if (auto ie = e.isIntervalExp())
+ {
+ auto tiargs = new Objects();
+ Expression edim = new IntegerExp(ae.loc, i, Type.tsize_t);
+ edim = edim.expressionSemantic(sc);
+ tiargs.push(edim);
+
+ auto fargs = new Expressions(2);
+ (*fargs)[0] = ie.lwr;
+ (*fargs)[1] = ie.upr;
+
+ uint xerrors = global.startGagging();
+ sc = sc.push();
+ FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, fargs, FuncResolveFlag.quiet);
+ sc = sc.pop();
+ global.endGagging(xerrors);
+ if (!fslice)
+ goto Lfallback;
+
+ e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs);
+ e = new CallExp(ae.loc, e, fargs);
+ e = e.expressionSemantic(sc);
+ }
+
+ if (!e.type)
+ {
+ ae.error("`%s` has no value", e.toChars());
+ e = ErrorExp.get();
+ }
+ if (e.op == TOK.error)
+ return e;
+
+ (*ae.arguments)[i] = e;
+ }
+ return ae;
+}
+
+/**************************************
+ * Runs semantic on se.lwr and se.upr. Declares a temporary variable
+ * if '$' was used.
+ * Returns:
+ * ae, or ErrorExp if errors occurred
+ */
+Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* pe0)
+{
+ //assert(!ae.lengthVar);
+ if (!ie)
+ return ae;
+
+ VarDeclaration lengthVar = ae.lengthVar;
+ bool errors = false;
+
+ // create scope for '$'
+ auto sym = new ArrayScopeSymbol(sc, ae);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+
+ Expression sem(Expression e)
+ {
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ if (!e.type)
+ {
+ ae.error("`%s` has no value", e.toChars());
+ errors = true;
+ }
+ return e;
+ }
+
+ ie.lwr = sem(ie.lwr);
+ ie.upr = sem(ie.upr);
+
+ if (lengthVar != ae.lengthVar && sc.func)
+ {
+ // If $ was used, declare it now
+ Expression de = new DeclarationExp(ae.loc, ae.lengthVar);
+ de = de.expressionSemantic(sc);
+ *pe0 = Expression.combine(*pe0, de);
+ }
+
+ sc = sc.pop();
+
+ return errors ? ErrorExp.get() : ae;
+}
+
+/******************************
+ * Perform semantic() on an array of Expressions.
+ */
+bool arrayExpressionSemantic(Expressions* exps, Scope* sc, bool preserveErrors = false)
+{
+ bool err = false;
+ if (exps)
+ {
+ foreach (ref e; *exps)
+ {
+ if (e)
+ {
+ auto e2 = e.expressionSemantic(sc);
+ if (e2.op == TOK.error)
+ err = true;
+ if (preserveErrors || e2.op != TOK.error)
+ e = e2;
+ }
+ }
+ }
+ return err;
+}
+
+/******************************
+ * Check the tail CallExp is really property function call.
+ * Bugs:
+ * This doesn't appear to do anything.
+ */
+private bool checkPropertyCall(Expression e)
+{
+ e = lastComma(e);
+
+ if (auto ce = e.isCallExp())
+ {
+ if (ce.f)
+ {
+ auto tf = ce.f.type.isTypeFunction();
+ /* If a forward reference to ce.f, try to resolve it
+ */
+ if (!tf.deco && ce.f.semanticRun < PASS.semanticdone)
+ {
+ ce.f.dsymbolSemantic(null);
+ tf = ce.f.type.isTypeFunction();
+ }
+ }
+ else if (!ce.e1.type.isFunction_Delegate_PtrToFunction())
+ assert(0);
+ }
+ return false;
+}
+
+/******************************
+ * Find symbol in accordance with the UFCS name look up rule
+ */
+private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident)
+{
+ //printf("searchUFCS(ident = %s)\n", ident.toChars());
+ Loc loc = ue.loc;
+
+ // TODO: merge with Scope.search.searchScopes()
+ Dsymbol searchScopes(int flags)
+ {
+ Dsymbol s = null;
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (!scx.scopesym)
+ continue;
+ if (scx.scopesym.isModule())
+ flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
+ s = scx.scopesym.search(loc, ident, flags);
+ if (s)
+ {
+ // overload set contains only module scope symbols.
+ if (s.isOverloadSet())
+ break;
+ // selective/renamed imports also be picked up
+ if (AliasDeclaration ad = s.isAliasDeclaration())
+ {
+ if (ad._import)
+ break;
+ }
+ // See only module scope symbols for UFCS target.
+ Dsymbol p = s.toParent2();
+ if (p && p.isModule())
+ break;
+ }
+ s = null;
+
+ // Stop when we hit a module, but keep going if that is not just under the global scope
+ if (scx.scopesym.isModule() && !(scx.enclosing && !scx.enclosing.enclosing))
+ break;
+ }
+ return s;
+ }
+
+ int flags = 0;
+ Dsymbol s;
+
+ if (sc.flags & SCOPE.ignoresymbolvisibility)
+ flags |= IgnoreSymbolVisibility;
+
+ // First look in local scopes
+ s = searchScopes(flags | SearchLocalsOnly);
+ if (!s)
+ {
+ // Second look in imported modules
+ s = searchScopes(flags | SearchImportsOnly);
+ }
+
+ if (!s)
+ return ue.e1.type.Type.getProperty(sc, loc, ident, 0);
+
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ TemplateDeclaration td = getFuncTemplateDecl(f);
+ if (td)
+ {
+ if (td.overroot)
+ td = td.overroot;
+ s = td;
+ }
+ }
+
+ if (auto dti = ue.isDotTemplateInstanceExp())
+ {
+ auto ti = new TemplateInstance(loc, s.ident, dti.ti.tiargs);
+ if (!ti.updateTempDecl(sc, s))
+ return ErrorExp.get();
+ return new ScopeExp(loc, ti);
+ }
+ else
+ {
+ //printf("-searchUFCS() %s\n", s.toChars());
+ return new DsymbolExp(loc, s);
+ }
+}
+
+/******************************
+ * Pull out callable entity with UFCS.
+ */
+private Expression resolveUFCS(Scope* sc, CallExp ce)
+{
+ Loc loc = ce.loc;
+ Expression eleft;
+ Expression e;
+
+ if (auto die = ce.e1.isDotIdExp())
+ {
+ Identifier ident = die.ident;
+
+ Expression ex = die.semanticX(sc);
+ if (ex != die)
+ {
+ ce.e1 = ex;
+ return null;
+ }
+ eleft = die.e1;
+
+ Type t = eleft.type.toBasetype();
+ if (t.ty == Tarray || t.ty == Tsarray || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid))
+ {
+ /* Built-in types and arrays have no callable properties, so do shortcut.
+ * It is necessary in: e.init()
+ */
+ }
+ else if (t.ty == Taarray)
+ {
+ if (ident == Id.remove)
+ {
+ /* Transform:
+ * aa.remove(arg) into delete aa[arg]
+ */
+ if (!ce.arguments || ce.arguments.dim != 1)
+ {
+ ce.error("expected key as argument to `aa.remove()`");
+ return ErrorExp.get();
+ }
+ if (!eleft.type.isMutable())
+ {
+ ce.error("cannot remove key from `%s` associative array `%s`", MODtoChars(t.mod), eleft.toChars());
+ return ErrorExp.get();
+ }
+ Expression key = (*ce.arguments)[0];
+ key = key.expressionSemantic(sc);
+ key = resolveProperties(sc, key);
+
+ TypeAArray taa = t.isTypeAArray();
+ key = key.implicitCastTo(sc, taa.index);
+
+ if (key.checkValue() || key.checkSharedAccess(sc))
+ return ErrorExp.get();
+
+ semanticTypeInfo(sc, taa.index);
+
+ return new RemoveExp(loc, eleft, key);
+ }
+ }
+ else
+ {
+ if (Expression ey = die.semanticY(sc, 1))
+ {
+ if (ey.op == TOK.error)
+ return ey;
+ ce.e1 = ey;
+ if (isDotOpDispatch(ey))
+ {
+ uint errors = global.startGagging();
+ e = ce.syntaxCopy().expressionSemantic(sc);
+ if (!global.endGagging(errors))
+ return e;
+
+ // even opDispatch and UFCS must have valid arguments,
+ // so now that we've seen indication of a problem,
+ // check them for issues.
+ Expressions* originalArguments = Expression.arraySyntaxCopy(ce.arguments);
+
+ if (arrayExpressionSemantic(originalArguments, sc))
+ return ErrorExp.get();
+
+ /* fall down to UFCS */
+ }
+ else
+ return null;
+ }
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=13953
+ *
+ * If a struct has an alias this to an associative array
+ * and remove is used on a struct instance, we have to
+ * check first if there is a remove function that can be called
+ * on the struct. If not we must check the alias this.
+ *
+ * struct A
+ * {
+ * string[string] a;
+ * alias a this;
+ * }
+ *
+ * void fun()
+ * {
+ * A s;
+ * s.remove("foo");
+ * }
+ */
+ const errors = global.startGagging();
+ e = searchUFCS(sc, die, ident);
+ // if there were any errors and the identifier was remove
+ if (global.endGagging(errors))
+ {
+ if (ident == Id.remove)
+ {
+ // check alias this
+ Expression alias_e = resolveAliasThis(sc, die.e1, 1);
+ if (alias_e && alias_e != die.e1)
+ {
+ die.e1 = alias_e;
+ CallExp ce2 = ce.syntaxCopy();
+ ce2.e1 = die;
+ e = ce2.isCallExp().trySemantic(sc);
+ if (e)
+ return e;
+ }
+ }
+ // if alias this did not work out, print the initial errors
+ searchUFCS(sc, die, ident);
+ }
+ }
+ else if (auto dti = ce.e1.isDotTemplateInstanceExp())
+ {
+ if (Expression ey = dti.semanticY(sc, 1))
+ {
+ ce.e1 = ey;
+ return null;
+ }
+ eleft = dti.e1;
+ e = searchUFCS(sc, dti, dti.ti.name);
+ }
+ else
+ return null;
+
+ // Rewrite
+ ce.e1 = e;
+ if (!ce.arguments)
+ ce.arguments = new Expressions();
+ ce.arguments.shift(eleft);
+
+ return null;
+}
+
+/******************************
+ * Pull out property with UFCS.
+ */
+private Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2 = null)
+{
+ Loc loc = e1.loc;
+ Expression eleft;
+ Expression e;
+
+ if (auto die = e1.isDotIdExp())
+ {
+ eleft = die.e1;
+ e = searchUFCS(sc, die, die.ident);
+ }
+ else if (auto dti = e1.isDotTemplateInstanceExp())
+ {
+ eleft = dti.e1;
+ e = searchUFCS(sc, dti, dti.ti.name);
+ }
+ else
+ return null;
+
+ if (e is null)
+ return null;
+
+ // Rewrite
+ if (e2)
+ {
+ // run semantic without gagging
+ e2 = e2.expressionSemantic(sc);
+
+ /* f(e1) = e2
+ */
+ Expression ex = e.copy();
+ auto a1 = new Expressions(1);
+ (*a1)[0] = eleft;
+ ex = new CallExp(loc, ex, a1);
+ auto e1PassSemantic = ex.trySemantic(sc);
+
+ /* f(e1, e2)
+ */
+ auto a2 = new Expressions(2);
+ (*a2)[0] = eleft;
+ (*a2)[1] = e2;
+ e = new CallExp(loc, e, a2);
+ e = e.trySemantic(sc);
+ if (!e1PassSemantic && !e)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=20448
+ *
+ * If both versions have failed to pass semantic,
+ * f(e1) = e2 gets priority in error printing
+ * because f might be a templated function that
+ * failed to instantiate and we have to print
+ * the instantiation errors.
+ */
+ return e1.expressionSemantic(sc);
+ }
+ else if (ex && !e)
+ {
+ checkPropertyCall(ex);
+ ex = new AssignExp(loc, ex, e2);
+ return ex.expressionSemantic(sc);
+ }
+ else
+ {
+ // strict setter prints errors if fails
+ e = e.expressionSemantic(sc);
+ }
+ checkPropertyCall(e);
+ return e;
+ }
+ else
+ {
+ /* f(e1)
+ */
+ auto arguments = new Expressions(1);
+ (*arguments)[0] = eleft;
+ e = new CallExp(loc, e, arguments);
+ e = e.expressionSemantic(sc);
+ checkPropertyCall(e);
+ return e.expressionSemantic(sc);
+ }
+}
+
+/******************************
+ * If e1 is a property function (template), resolve it.
+ */
+Expression resolvePropertiesOnly(Scope* sc, Expression e1)
+{
+ //printf("e1 = %s %s\n", Token::toChars(e1.op), e1.toChars());
+
+ Expression handleOverloadSet(OverloadSet os)
+ {
+ assert(os);
+ foreach (s; os.a)
+ {
+ auto fd = s.isFuncDeclaration();
+ auto td = s.isTemplateDeclaration();
+ if (fd)
+ {
+ if (fd.type.isTypeFunction().isproperty)
+ return resolveProperties(sc, e1);
+ }
+ else if (td && td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null)
+ {
+ if (fd.type.isTypeFunction().isproperty ||
+ (fd.storage_class2 & STC.property) ||
+ (td._scope.stc & STC.property))
+ return resolveProperties(sc, e1);
+ }
+ }
+ return e1;
+ }
+
+ Expression handleTemplateDecl(TemplateDeclaration td)
+ {
+ assert(td);
+ if (td.onemember)
+ {
+ if (auto fd = td.onemember.isFuncDeclaration())
+ {
+ if (fd.type.isTypeFunction().isproperty ||
+ (fd.storage_class2 & STC.property) ||
+ (td._scope.stc & STC.property))
+ return resolveProperties(sc, e1);
+ }
+ }
+ return e1;
+ }
+
+ Expression handleFuncDecl(FuncDeclaration fd)
+ {
+ assert(fd);
+ if (fd.type.isTypeFunction().isproperty)
+ return resolveProperties(sc, e1);
+ return e1;
+ }
+
+ if (auto de = e1.isDotExp())
+ {
+ if (auto os = de.e2.isOverExp())
+ return handleOverloadSet(os.vars);
+ }
+ else if (auto oe = e1.isOverExp())
+ return handleOverloadSet(oe.vars);
+ else if (auto dti = e1.isDotTemplateInstanceExp())
+ {
+ if (dti.ti.tempdecl)
+ if (auto td = dti.ti.tempdecl.isTemplateDeclaration())
+ return handleTemplateDecl(td);
+ }
+ else if (auto dte = e1.isDotTemplateExp())
+ return handleTemplateDecl(dte.td);
+ else if (auto se = e1.isScopeExp())
+ {
+ Dsymbol s = se.sds;
+ TemplateInstance ti = s.isTemplateInstance();
+ if (ti && !ti.semanticRun && ti.tempdecl)
+ if (auto td = ti.tempdecl.isTemplateDeclaration())
+ return handleTemplateDecl(td);
+ }
+ else if (auto et = e1.isTemplateExp())
+ return handleTemplateDecl(et.td);
+ else if (e1.isDotVarExp() && e1.type.isTypeFunction())
+ {
+ DotVarExp dve = e1.isDotVarExp();
+ return handleFuncDecl(dve.var.isFuncDeclaration());
+ }
+ else if (e1.isVarExp() && e1.type && e1.type.isTypeFunction() && (sc.intypeof || !e1.isVarExp().var.needThis()))
+ return handleFuncDecl(e1.isVarExp().var.isFuncDeclaration());
+ return e1;
+}
+
+/****************************************
+ * Turn symbol `s` into the expression it represents.
+ *
+ * Params:
+ * s = symbol to resolve
+ * loc = location of use of `s`
+ * sc = context
+ * hasOverloads = applies if `s` represents a function.
+ * true means it's overloaded and will be resolved later,
+ * false means it's the exact function symbol.
+ * Returns:
+ * `s` turned into an expression, `ErrorExp` if an error occurred
+ */
+Expression symbolToExp(Dsymbol s, const ref Loc loc, Scope *sc, bool hasOverloads)
+{
+ static if (LOGSEMANTIC)
+ {
+ printf("DsymbolExp::resolve(%s %s)\n", s.kind(), s.toChars());
+ }
+
+Lagain:
+ Expression e;
+
+ //printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars());
+ //printf("s = '%s', s.kind = '%s'\n", s.toChars(), s.kind());
+ Dsymbol olds = s;
+ Declaration d = s.isDeclaration();
+ if (d && (d.storage_class & STC.templateparameter))
+ {
+ s = s.toAlias();
+ }
+ else
+ {
+ // functions are checked after overloading
+ // templates are checked after matching constraints
+ if (!s.isFuncDeclaration() && !s.isTemplateDeclaration())
+ {
+ s.checkDeprecated(loc, sc);
+ if (d)
+ d.checkDisabled(loc, sc);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=12023
+ // if 's' is a tuple variable, the tuple is returned.
+ s = s.toAlias();
+
+ //printf("s = '%s', s.kind = '%s', s.needThis() = %p\n", s.toChars(), s.kind(), s.needThis());
+ if (s != olds && !s.isFuncDeclaration() && !s.isTemplateDeclaration())
+ {
+ s.checkDeprecated(loc, sc);
+ if (d)
+ d.checkDisabled(loc, sc);
+ }
+ }
+
+ if (auto em = s.isEnumMember())
+ {
+ return em.getVarExp(loc, sc);
+ }
+ if (auto v = s.isVarDeclaration())
+ {
+ //printf("Identifier '%s' is a variable, type '%s'\n", s.toChars(), v.type.toChars());
+ if (sc.intypeof == 1 && !v.inuse)
+ v.dsymbolSemantic(sc);
+ if (!v.type || // during variable type inference
+ !v.type.deco && v.inuse) // during variable type semantic
+ {
+ if (v.inuse) // variable type depends on the variable itself
+ error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars());
+ else // variable type cannot be determined
+ error(loc, "forward reference to %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ if (v.type.ty == Terror)
+ return ErrorExp.get();
+
+ if ((v.storage_class & STC.manifest) && v._init)
+ {
+ if (v.inuse)
+ {
+ error(loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ e = v.expandInitializer(loc);
+ v.inuse++;
+ e = e.expressionSemantic(sc);
+ v.inuse--;
+ return e;
+ }
+
+ // We need to run semantics to correctly set 'STC.field' if it is a member variable
+ // that could be forward referenced. This is needed for 'v.needThis()' to work
+ if (v.isThis())
+ v.dsymbolSemantic(sc);
+
+ // Change the ancestor lambdas to delegate before hasThis(sc) call.
+ if (v.checkNestedReference(sc, loc))
+ return ErrorExp.get();
+
+ if (v.needThis() && hasThis(sc))
+ e = new DotVarExp(loc, new ThisExp(loc), v);
+ else
+ e = new VarExp(loc, v);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (auto fld = s.isFuncLiteralDeclaration())
+ {
+ //printf("'%s' is a function literal\n", fld.toChars());
+ e = new FuncExp(loc, fld);
+ return e.expressionSemantic(sc);
+ }
+ if (auto f = s.isFuncDeclaration())
+ {
+ f = f.toAliasFunc();
+ if (!f.functionSemantic())
+ return ErrorExp.get();
+
+ if (!hasOverloads && f.checkForwardRef(loc))
+ return ErrorExp.get();
+
+ auto fd = s.isFuncDeclaration();
+ fd.type = f.type;
+ return new VarExp(loc, fd, hasOverloads);
+ }
+ if (OverDeclaration od = s.isOverDeclaration())
+ {
+ e = new VarExp(loc, od, true);
+ e.type = Type.tvoid;
+ return e;
+ }
+ if (OverloadSet o = s.isOverloadSet())
+ {
+ //printf("'%s' is an overload set\n", o.toChars());
+ return new OverExp(loc, o);
+ }
+
+ if (Import imp = s.isImport())
+ {
+ if (!imp.pkg)
+ {
+ .error(loc, "forward reference of import `%s`", imp.toChars());
+ return ErrorExp.get();
+ }
+ auto ie = new ScopeExp(loc, imp.pkg);
+ return ie.expressionSemantic(sc);
+ }
+ if (Package pkg = s.isPackage())
+ {
+ auto ie = new ScopeExp(loc, pkg);
+ return ie.expressionSemantic(sc);
+ }
+ if (Module mod = s.isModule())
+ {
+ auto ie = new ScopeExp(loc, mod);
+ return ie.expressionSemantic(sc);
+ }
+ if (Nspace ns = s.isNspace())
+ {
+ auto ie = new ScopeExp(loc, ns);
+ return ie.expressionSemantic(sc);
+ }
+
+ if (Type t = s.getType())
+ {
+ return (new TypeExp(loc, t)).expressionSemantic(sc);
+ }
+
+ if (TupleDeclaration tup = s.isTupleDeclaration())
+ {
+ if (tup.needThis() && hasThis(sc))
+ e = new DotVarExp(loc, new ThisExp(loc), tup);
+ else
+ e = new TupleExp(loc, tup);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ if (TemplateInstance ti = s.isTemplateInstance())
+ {
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors)
+ return ErrorExp.get();
+ s = ti.toAlias();
+ if (!s.isTemplateInstance())
+ goto Lagain;
+ e = new ScopeExp(loc, ti);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (TemplateDeclaration td = s.isTemplateDeclaration())
+ {
+ Dsymbol p = td.toParentLocal();
+ FuncDeclaration fdthis = hasThis(sc);
+ AggregateDeclaration ad = p ? p.isAggregateDeclaration() : null;
+ if (fdthis && ad && fdthis.isMemberLocal() == ad && (td._scope.stc & STC.static_) == 0)
+ {
+ e = new DotTemplateExp(loc, new ThisExp(loc), td);
+ }
+ else
+ e = new TemplateExp(loc, td);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ .error(loc, "%s `%s` is not a variable", s.kind(), s.toChars());
+ return ErrorExp.get();
+}
+
+/*************************************************************
+ * Given var, get the
+ * right `this` pointer if var is in an outer class, but our
+ * existing `this` pointer is in an inner class.
+ * Params:
+ * loc = location to use for error messages
+ * sc = context
+ * ad = struct or class we need the correct `this` for
+ * e1 = existing `this`
+ * var = the specific member of ad we're accessing
+ * flag = if true, return `null` instead of throwing an error
+ * Returns:
+ * Expression representing the `this` for the var
+ */
+private Expression getRightThis(const ref Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Dsymbol var, int flag = 0)
+{
+ //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1.toChars(), ad.toChars(), var.toChars());
+L1:
+ Type t = e1.type.toBasetype();
+ //printf("e1.type = %s, var.type = %s\n", e1.type.toChars(), var.type.toChars());
+
+ if (e1.op == TOK.objcClassReference)
+ {
+ // We already have an Objective-C class reference, just use that as 'this'.
+ return e1;
+ }
+ else if (ad && ad.isClassDeclaration && ad.isClassDeclaration.classKind == ClassKind.objc &&
+ var.isFuncDeclaration && var.isFuncDeclaration.isStatic &&
+ var.isFuncDeclaration.objc.selector)
+ {
+ return new ObjcClassReferenceExp(e1.loc, ad.isClassDeclaration());
+ }
+
+ /* Access of a member which is a template parameter in dual-scope scenario
+ * class A { inc(alias m)() { ++m; } } // `m` needs `this` of `B`
+ * class B {int m; inc() { new A().inc!m(); } }
+ */
+ if (e1.op == TOK.this_)
+ {
+ FuncDeclaration f = hasThis(sc);
+ if (f && f.isThis2)
+ {
+ if (f.followInstantiationContext(ad))
+ {
+ e1 = new VarExp(loc, f.vthis);
+ e1 = new PtrExp(loc, e1);
+ e1 = new IndexExp(loc, e1, IntegerExp.literal!1);
+ e1 = getThisSkipNestedFuncs(loc, sc, f.toParent2(), ad, e1, t, var);
+ if (e1.op == TOK.error)
+ return e1;
+ goto L1;
+ }
+ }
+ }
+
+ /* If e1 is not the 'this' pointer for ad
+ */
+ if (ad &&
+ !(t.isTypePointer() && t.nextOf().isTypeStruct() && t.nextOf().isTypeStruct().sym == ad) &&
+ !(t.isTypeStruct() && t.isTypeStruct().sym == ad))
+ {
+ ClassDeclaration cd = ad.isClassDeclaration();
+ ClassDeclaration tcd = t.isClassHandle();
+
+ /* e1 is the right this if ad is a base class of e1
+ */
+ if (!cd || !tcd || !(tcd == cd || cd.isBaseOf(tcd, null)))
+ {
+ /* Only classes can be inner classes with an 'outer'
+ * member pointing to the enclosing class instance
+ */
+ if (tcd && tcd.isNested())
+ {
+ /* e1 is the 'this' pointer for an inner class: tcd.
+ * Rewrite it as the 'this' pointer for the outer class.
+ */
+ auto vthis = tcd.followInstantiationContext(ad) ? tcd.vthis2 : tcd.vthis;
+ e1 = new DotVarExp(loc, e1, vthis);
+ e1.type = vthis.type;
+ e1.type = e1.type.addMod(t.mod);
+ // Do not call ensureStaticLinkTo()
+ //e1 = e1.semantic(sc);
+
+ // Skip up over nested functions, and get the enclosing
+ // class type.
+ e1 = getThisSkipNestedFuncs(loc, sc, tcd.toParentP(ad), ad, e1, t, var);
+ if (e1.op == TOK.error)
+ return e1;
+ goto L1;
+ }
+
+ /* Can't find a path from e1 to ad
+ */
+ if (flag)
+ return null;
+ e1.error("`this` for `%s` needs to be type `%s` not type `%s`", var.toChars(), ad.toChars(), t.toChars());
+ return ErrorExp.get();
+ }
+ }
+ return e1;
+}
+
+/***************************************
+ * Pull out any properties.
+ */
+private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null)
+{
+ //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token.toChars(e1.op), e1.toChars(), e2 ? e2.toChars() : null);
+ Loc loc = e1.loc;
+
+ OverloadSet os;
+ Dsymbol s;
+ Objects* tiargs;
+ Type tthis;
+ if (auto de = e1.isDotExp())
+ {
+ if (auto oe = de.e2.isOverExp())
+ {
+ tiargs = null;
+ tthis = de.e1.type;
+ os = oe.vars;
+ goto Los;
+ }
+ }
+ else if (e1.isOverExp())
+ {
+ tiargs = null;
+ tthis = null;
+ os = e1.isOverExp().vars;
+ Los:
+ assert(os);
+ FuncDeclaration fd = null;
+ if (e2)
+ {
+ e2 = e2.expressionSemantic(sc);
+ if (e2.op == TOK.error)
+ return ErrorExp.get();
+ e2 = resolveProperties(sc, e2);
+
+ Expressions a;
+ a.push(e2);
+
+ for (size_t i = 0; i < os.a.dim; i++)
+ {
+ if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, &a, FuncResolveFlag.quiet))
+ {
+ if (f.errors)
+ return ErrorExp.get();
+ fd = f;
+ assert(fd.type.ty == Tfunction);
+ }
+ }
+ if (fd)
+ {
+ Expression e = new CallExp(loc, e1, e2);
+ return e.expressionSemantic(sc);
+ }
+ }
+ {
+ for (size_t i = 0; i < os.a.dim; i++)
+ {
+ if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, null, FuncResolveFlag.quiet))
+ {
+ if (f.errors)
+ return ErrorExp.get();
+ fd = f;
+ assert(fd.type.ty == Tfunction);
+ auto tf = fd.type.isTypeFunction();
+ if (!tf.isref && e2)
+ {
+ error(loc, "%s is not an lvalue", e1.toChars());
+ return ErrorExp.get();
+ }
+ }
+ }
+ if (fd)
+ {
+ Expression e = new CallExp(loc, e1);
+ if (e2)
+ e = new AssignExp(loc, e, e2);
+ return e.expressionSemantic(sc);
+ }
+ }
+ if (e2)
+ goto Leprop;
+ }
+ else if (auto dti = e1.isDotTemplateInstanceExp())
+ {
+ if (!dti.findTempDecl(sc))
+ goto Leprop;
+ if (!dti.ti.semanticTiargs(sc))
+ goto Leprop;
+ tiargs = dti.ti.tiargs;
+ tthis = dti.e1.type;
+ if ((os = dti.ti.tempdecl.isOverloadSet()) !is null)
+ goto Los;
+ if ((s = dti.ti.tempdecl) !is null)
+ goto Lfd;
+ }
+ else if (auto dte = e1.isDotTemplateExp())
+ {
+ s = dte.td;
+ tiargs = null;
+ tthis = dte.e1.type;
+ goto Lfd;
+ }
+ else if (auto se = e1.isScopeExp())
+ {
+ s = se.sds;
+ TemplateInstance ti = s.isTemplateInstance();
+ if (ti && !ti.semanticRun && ti.tempdecl)
+ {
+ //assert(ti.needsTypeInference(sc));
+ if (!ti.semanticTiargs(sc))
+ goto Leprop;
+ tiargs = ti.tiargs;
+ tthis = null;
+ if ((os = ti.tempdecl.isOverloadSet()) !is null)
+ goto Los;
+ if ((s = ti.tempdecl) !is null)
+ goto Lfd;
+ }
+ }
+ else if (auto te = e1.isTemplateExp())
+ {
+ s = te.td;
+ tiargs = null;
+ tthis = null;
+ goto Lfd;
+ }
+ else if (e1.op == TOK.dotVariable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(DotVarExp)e1).var.isOverDeclaration()))
+ {
+ DotVarExp dve = cast(DotVarExp)e1;
+ s = dve.var;
+ tiargs = null;
+ tthis = dve.e1.type;
+ goto Lfd;
+ }
+ else if (e1.op == TOK.variable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(VarExp)e1).var.isOverDeclaration()))
+ {
+ s = (cast(VarExp)e1).var;
+ tiargs = null;
+ tthis = null;
+ Lfd:
+ assert(s);
+ if (e2)
+ {
+ e2 = e2.expressionSemantic(sc);
+ if (e2.op == TOK.error)
+ return ErrorExp.get();
+ e2 = resolveProperties(sc, e2);
+
+ Expressions a;
+ a.push(e2);
+
+ FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, FuncResolveFlag.quiet);
+ if (fd && fd.type)
+ {
+ if (fd.errors)
+ return ErrorExp.get();
+ if (!checkSymbolAccess(sc, fd))
+ {
+ // @@@DEPRECATED_2020-10@@@
+ // When turning into error, uncomment the return statement
+ TypeFunction tf = cast(TypeFunction)fd.type;
+ deprecation(loc, "Function `%s` of type `%s` is not accessible from module `%s`",
+ fd.toPrettyChars(), tf.toChars, sc._module.toChars);
+ //return ErrorExp.get();
+ }
+ assert(fd.type.ty == Tfunction);
+ Expression e = new CallExp(loc, e1, e2);
+ return e.expressionSemantic(sc);
+ }
+ }
+ {
+ FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, null, FuncResolveFlag.quiet);
+ if (fd && fd.type)
+ {
+ if (fd.errors)
+ return ErrorExp.get();
+ assert(fd.type.ty == Tfunction);
+ TypeFunction tf = cast(TypeFunction)fd.type;
+ if (!e2 || tf.isref)
+ {
+ if (!checkSymbolAccess(sc, fd))
+ {
+ // @@@DEPRECATED_2020-10@@@
+ // When turning into error, uncomment the return statement
+ deprecation(loc, "Function `%s` of type `%s` is not accessible from module `%s`",
+ fd.toPrettyChars(), tf.toChars, sc._module.toChars);
+ //return ErrorExp.get();
+ }
+ Expression e = new CallExp(loc, e1);
+ if (e2)
+ e = new AssignExp(loc, e, e2);
+ return e.expressionSemantic(sc);
+ }
+ }
+ }
+ if (FuncDeclaration fd = s.isFuncDeclaration())
+ {
+ // Keep better diagnostic message for invalid property usage of functions
+ assert(fd.type.ty == Tfunction);
+ Expression e = new CallExp(loc, e1, e2);
+ return e.expressionSemantic(sc);
+ }
+ if (e2)
+ goto Leprop;
+ }
+ if (e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)e1;
+ VarDeclaration v = ve.var.isVarDeclaration();
+ if (v && ve.checkPurity(sc, v))
+ return ErrorExp.get();
+ }
+ if (e2)
+ return null;
+
+ if (e1.type && e1.op != TOK.type) // function type is not a property
+ {
+ /* Look for e1 being a lazy parameter; rewrite as delegate call
+ * only if the symbol wasn't already treated as a delegate
+ */
+ auto ve = e1.isVarExp();
+ if (ve && ve.var.storage_class & STC.lazy_ && !ve.delegateWasExtracted)
+ {
+ Expression e = new CallExp(loc, e1);
+ return e.expressionSemantic(sc);
+ }
+ else if (e1.op == TOK.dotVariable)
+ {
+ // Check for reading overlapped pointer field in @safe code.
+ if (checkUnsafeAccess(sc, e1, true, true))
+ return ErrorExp.get();
+ }
+ else if (e1.op == TOK.call)
+ {
+ CallExp ce = cast(CallExp)e1;
+ // Check for reading overlapped pointer field in @safe code.
+ if (checkUnsafeAccess(sc, ce.e1, true, true))
+ return ErrorExp.get();
+ }
+ }
+
+ if (!e1.type)
+ {
+ error(loc, "cannot resolve type for %s", e1.toChars());
+ e1 = ErrorExp.get();
+ }
+ return e1;
+
+Leprop:
+ error(loc, "not a property %s", e1.toChars());
+ return ErrorExp.get();
+}
+
+extern (C++) Expression resolveProperties(Scope* sc, Expression e)
+{
+ //printf("resolveProperties(%s)\n", e.toChars());
+ e = resolvePropertiesX(sc, e);
+ if (e.checkRightThis(sc))
+ return ErrorExp.get();
+ return e;
+}
+
+/****************************************
+ * The common type is determined by applying ?: to each pair.
+ * Output:
+ * exps[] properties resolved, implicitly cast to common type, rewritten in place
+ * Returns:
+ * The common type, or `null` if an error has occured
+ */
+private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
+{
+ /* Still have a problem with:
+ * ubyte[][] = [ cast(ubyte[])"hello", [1]];
+ * which works if the array literal is initialized top down with the ubyte[][]
+ * type, but fails with this function doing bottom up typing.
+ */
+
+ //printf("arrayExpressionToCommonType()\n");
+ scope IntegerExp integerexp = IntegerExp.literal!0;
+ scope CondExp condexp = new CondExp(Loc.initial, integerexp, null, null);
+
+ Type t0 = null;
+ Expression e0 = null;
+ size_t j0 = ~0;
+ bool foundType;
+
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression e = exps[i];
+ if (!e)
+ continue;
+
+ e = resolveProperties(sc, e);
+ if (!e.type)
+ {
+ e.error("`%s` has no value", e.toChars());
+ t0 = Type.terror;
+ continue;
+ }
+ if (e.op == TOK.type)
+ {
+ foundType = true; // do not break immediately, there might be more errors
+ e.checkValue(); // report an error "type T has no value"
+ t0 = Type.terror;
+ continue;
+ }
+ if (e.type.ty == Tvoid)
+ {
+ // void expressions do not concur to the determination of the common
+ // type.
+ continue;
+ }
+ if (checkNonAssignmentArrayOp(e))
+ {
+ t0 = Type.terror;
+ continue;
+ }
+
+ e = doCopyOrMove(sc, e);
+
+ if (!foundType && t0 && !t0.equals(e.type))
+ {
+ /* This applies ?: to merge the types. It's backwards;
+ * ?: should call this function to merge types.
+ */
+ condexp.type = null;
+ condexp.e1 = e0;
+ condexp.e2 = e;
+ condexp.loc = e.loc;
+ Expression ex = condexp.expressionSemantic(sc);
+ if (ex.op == TOK.error)
+ e = ex;
+ else if (e.op == TOK.function_ || e.op == TOK.delegate_)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=21285
+ // Functions and delegates don't convert correctly with castTo below
+ exps[j0] = condexp.e1;
+ e = condexp.e2;
+ }
+ else
+ {
+ // Convert to common type
+ exps[j0] = condexp.e1.castTo(sc, condexp.type);
+ e = condexp.e2.castTo(sc, condexp.type);
+ }
+ }
+ j0 = i;
+ e0 = e;
+ t0 = e.type;
+ if (e.op != TOK.error)
+ exps[i] = e;
+ }
+
+ // [] is typed as void[]
+ if (!t0)
+ return Type.tvoid;
+
+ // It's an error, don't do the cast
+ if (t0.ty == Terror)
+ return null;
+
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression e = exps[i];
+ if (!e)
+ continue;
+
+ e = e.implicitCastTo(sc, t0);
+ if (e.op == TOK.error)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=13024
+ * a workaround for the bug in typeMerge -
+ * it should paint e1 and e2 by deduced common type,
+ * but doesn't in this particular case.
+ */
+ return null;
+ }
+ exps[i] = e;
+ }
+ return t0;
+}
+
+private Expression opAssignToOp(const ref Loc loc, TOK op, Expression e1, Expression e2)
+{
+ Expression e;
+ switch (op)
+ {
+ case TOK.addAssign:
+ e = new AddExp(loc, e1, e2);
+ break;
+
+ case TOK.minAssign:
+ e = new MinExp(loc, e1, e2);
+ break;
+
+ case TOK.mulAssign:
+ e = new MulExp(loc, e1, e2);
+ break;
+
+ case TOK.divAssign:
+ e = new DivExp(loc, e1, e2);
+ break;
+
+ case TOK.modAssign:
+ e = new ModExp(loc, e1, e2);
+ break;
+
+ case TOK.andAssign:
+ e = new AndExp(loc, e1, e2);
+ break;
+
+ case TOK.orAssign:
+ e = new OrExp(loc, e1, e2);
+ break;
+
+ case TOK.xorAssign:
+ e = new XorExp(loc, e1, e2);
+ break;
+
+ case TOK.leftShiftAssign:
+ e = new ShlExp(loc, e1, e2);
+ break;
+
+ case TOK.rightShiftAssign:
+ e = new ShrExp(loc, e1, e2);
+ break;
+
+ case TOK.unsignedRightShiftAssign:
+ e = new UshrExp(loc, e1, e2);
+ break;
+
+ default:
+ assert(0);
+ }
+ return e;
+}
+
+/*********************
+ * Rewrite:
+ * array.length op= e2
+ * as:
+ * array.length = array.length op e2
+ * or:
+ * auto tmp = &array;
+ * (*tmp).length = (*tmp).length op e2
+ */
+private Expression rewriteOpAssign(BinExp exp)
+{
+ Expression e;
+
+ assert(exp.e1.op == TOK.arrayLength);
+ ArrayLengthExp ale = cast(ArrayLengthExp)exp.e1;
+ if (ale.e1.op == TOK.variable)
+ {
+ e = opAssignToOp(exp.loc, exp.op, ale, exp.e2);
+ e = new AssignExp(exp.loc, ale.syntaxCopy(), e);
+ }
+ else
+ {
+ /* auto tmp = &array;
+ * (*tmp).length = (*tmp).length op e2
+ */
+ auto tmp = copyToTemp(0, "__arraylength", new AddrExp(ale.loc, ale.e1));
+
+ Expression e1 = new ArrayLengthExp(ale.loc, new PtrExp(ale.loc, new VarExp(ale.loc, tmp)));
+ Expression elvalue = e1.syntaxCopy();
+ e = opAssignToOp(exp.loc, exp.op, e1, exp.e2);
+ e = new AssignExp(exp.loc, elvalue, e);
+ e = new CommaExp(exp.loc, new DeclarationExp(ale.loc, tmp), e);
+ }
+ return e;
+}
+
+/****************************************
+ * Preprocess arguments to function.
+ * Input:
+ * reportErrors whether or not to report errors here. Some callers are not
+ * checking actual function params, so they'll do their own error reporting
+ * Output:
+ * exps[] tuples expanded, properties resolved, rewritten in place
+ * Returns:
+ * true a semantic error occurred
+ */
+private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool reportErrors = true)
+{
+ bool err = false;
+ if (exps)
+ {
+ expandTuples(exps);
+
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression arg = (*exps)[i];
+ arg = resolveProperties(sc, arg);
+ if (arg.op == TOK.type)
+ {
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ arg = resolveAliasThis(sc, arg);
+
+ if (arg.op == TOK.type)
+ {
+ if (reportErrors)
+ {
+ arg.error("cannot pass type `%s` as a function argument", arg.toChars());
+ arg = ErrorExp.get();
+ }
+ err = true;
+ }
+ }
+ else if (arg.type.toBasetype().ty == Tfunction)
+ {
+ if (reportErrors)
+ {
+ arg.error("cannot pass function `%s` as a function argument", arg.toChars());
+ arg = ErrorExp.get();
+ }
+ err = true;
+ }
+ else if (checkNonAssignmentArrayOp(arg))
+ {
+ arg = ErrorExp.get();
+ err = true;
+ }
+ (*exps)[i] = arg;
+ }
+ }
+ return err;
+}
+
+/********************************************
+ * Issue an error if default construction is disabled for type t.
+ * Default construction is required for arrays and 'out' parameters.
+ * Returns:
+ * true an error was issued
+ */
+private bool checkDefCtor(Loc loc, Type t)
+{
+ t = t.baseElemOf();
+ if (t.ty == Tstruct)
+ {
+ StructDeclaration sd = (cast(TypeStruct)t).sym;
+ if (sd.noDefaultCtor)
+ {
+ sd.error(loc, "default construction is disabled");
+ return true;
+ }
+ }
+ return false;
+}
+
+/****************************************
+ * Now that we know the exact type of the function we're calling,
+ * the arguments[] need to be adjusted:
+ * 1. implicitly convert argument to the corresponding parameter type
+ * 2. add default arguments for any missing arguments
+ * 3. do default promotions on arguments corresponding to ...
+ * 4. add hidden _arguments[] argument
+ * 5. call copy constructor for struct value arguments
+ * Params:
+ * loc = location of function call
+ * sc = context
+ * tf = type of the function
+ * ethis = `this` argument, `null` if none or not known
+ * tthis = type of `this` argument, `null` if no `this` argument
+ * arguments = array of actual arguments to function call
+ * fd = the function being called, `null` if called indirectly
+ * prettype = set to return type of function
+ * peprefix = set to expression to execute before `arguments[]` are evaluated, `null` if none
+ * Returns:
+ * true errors happened
+ */
+private bool functionParameters(const ref Loc loc, Scope* sc,
+ TypeFunction tf, Expression ethis, Type tthis, Expressions* arguments, FuncDeclaration fd,
+ Type* prettype, Expression* peprefix)
+{
+ //printf("functionParameters() %s\n", fd ? fd.toChars() : "");
+ assert(arguments);
+ assert(fd || tf.next);
+ size_t nargs = arguments ? arguments.dim : 0;
+ const size_t nparams = tf.parameterList.length;
+ const olderrors = global.errors;
+ bool err = false;
+ *prettype = Type.terror;
+ Expression eprefix = null;
+ *peprefix = null;
+
+ if (nargs > nparams && tf.parameterList.varargs == VarArg.none)
+ {
+ error(loc, "expected %llu arguments, not %llu for non-variadic function type `%s`", cast(ulong)nparams, cast(ulong)nargs, tf.toChars());
+ return true;
+ }
+
+ // If inferring return type, and semantic3() needs to be run if not already run
+ if (!tf.next && fd.inferRetType)
+ {
+ fd.functionSemantic();
+ }
+ else if (fd && fd.parent)
+ {
+ TemplateInstance ti = fd.parent.isTemplateInstance();
+ if (ti && ti.tempdecl)
+ {
+ fd.functionSemantic3();
+ }
+ }
+
+ /* If calling a pragma(inline, true) function,
+ * set flag to later scan for inlines.
+ */
+ if (fd && fd.inlining == PINLINE.always)
+ {
+ if (sc._module)
+ sc._module.hasAlwaysInlines = true;
+ if (sc.func)
+ sc.func.hasAlwaysInlines = true;
+ }
+
+ const isCtorCall = fd && fd.needThis() && fd.isCtorDeclaration();
+
+ const size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams)
+
+ /* If the function return type has wildcards in it, we'll need to figure out the actual type
+ * based on the actual argument types.
+ * Start with the `this` argument, later on merge into wildmatch the mod bits of the rest
+ * of the arguments.
+ */
+ MOD wildmatch = (tthis && !isCtorCall) ? tthis.Type.deduceWild(tf, false) : 0;
+
+ bool done = false;
+ foreach (const i; 0 .. n)
+ {
+ Expression arg = (i < nargs) ? (*arguments)[i] : null;
+
+ if (i < nparams)
+ {
+ bool errorArgs()
+ {
+ error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs);
+ return true;
+ }
+
+ Parameter p = tf.parameterList[i];
+
+ if (!arg)
+ {
+ if (!p.defaultArg)
+ {
+ if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams)
+ goto L2;
+ return errorArgs();
+ }
+ arg = p.defaultArg;
+ arg = inlineCopy(arg, sc);
+ // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__
+ arg = arg.resolveLoc(loc, sc);
+ arguments.push(arg);
+ nargs++;
+ }
+ else
+ {
+ if (isDefaultInitOp(arg.op))
+ {
+ arg = arg.resolveLoc(loc, sc);
+ (*arguments)[i] = arg;
+ }
+ }
+
+
+ if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic
+ {
+ //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars());
+ {
+ MATCH m;
+ if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch)
+ {
+ if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m)
+ goto L2;
+ else if (nargs != nparams)
+ return errorArgs();
+ goto L1;
+ }
+ }
+ L2:
+ Type tb = p.type.toBasetype();
+ switch (tb.ty)
+ {
+ case Tsarray:
+ case Tarray:
+ {
+ /* Create a static array variable v of type arg.type:
+ * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ];
+ *
+ * The array literal in the initializer of the hidden variable
+ * is now optimized.
+ * https://issues.dlang.org/show_bug.cgi?id=2356
+ */
+ Type tbn = (cast(TypeArray)tb).next; // array element type
+ Type tret = p.isLazyArray();
+
+ auto elements = new Expressions(nargs - i);
+ foreach (u; 0 .. elements.dim)
+ {
+ Expression a = (*arguments)[i + u];
+ if (tret && a.implicitConvTo(tret))
+ {
+ // p is a lazy array of delegates, tret is return type of the delegates
+ a = a.implicitCastTo(sc, tret)
+ .optimize(WANTvalue)
+ .toDelegate(tret, sc);
+ }
+ else
+ a = a.implicitCastTo(sc, tbn);
+ a = a.addDtorHook(sc);
+ (*elements)[u] = a;
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=14395
+ // Convert to a static array literal, or its slice.
+ arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements);
+ if (tb.ty == Tarray)
+ {
+ arg = new SliceExp(loc, arg, null, null);
+ arg.type = p.type;
+ }
+ break;
+ }
+ case Tclass:
+ {
+ /* Set arg to be:
+ * new Tclass(arg0, arg1, ..., argn)
+ */
+ auto args = new Expressions(nargs - i);
+ foreach (u; i .. nargs)
+ (*args)[u - i] = (*arguments)[u];
+ arg = new NewExp(loc, null, null, p.type, args);
+ break;
+ }
+ default:
+ if (!arg)
+ {
+ error(loc, "not enough arguments");
+ return true;
+ }
+ break;
+ }
+ arg = arg.expressionSemantic(sc);
+ //printf("\targ = '%s'\n", arg.toChars());
+ arguments.setDim(i + 1);
+ (*arguments)[i] = arg;
+ nargs = i + 1;
+ done = true;
+ }
+
+ L1:
+ if (!(p.storageClass & STC.lazy_ && p.type.ty == Tvoid))
+ {
+ if (ubyte wm = arg.type.deduceWild(p.type, p.isReference()))
+ {
+ wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm;
+ //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch);
+ }
+ }
+ }
+ if (done)
+ break;
+ }
+ if ((wildmatch == MODFlags.mutable || wildmatch == MODFlags.immutable_) &&
+ tf.next && tf.next.hasWild() &&
+ (tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf())))
+ {
+ bool errorInout(MOD wildmatch)
+ {
+ const(char)* s = wildmatch == MODFlags.mutable ? "mutable" : MODtoChars(wildmatch);
+ error(loc, "modify `inout` to `%s` is not allowed inside `inout` function", s);
+ return true;
+ }
+
+ if (fd)
+ {
+ /* If the called function may return the reference to
+ * outer inout data, it should be rejected.
+ *
+ * void foo(ref inout(int) x) {
+ * ref inout(int) bar(inout(int)) { return x; }
+ * struct S {
+ * ref inout(int) bar() inout { return x; }
+ * ref inout(int) baz(alias a)() inout { return x; }
+ * }
+ * bar(int.init) = 1; // bad!
+ * S().bar() = 1; // bad!
+ * }
+ * void test() {
+ * int a;
+ * auto s = foo(a);
+ * s.baz!a() = 1; // bad!
+ * }
+ *
+ */
+ bool checkEnclosingWild(Dsymbol s)
+ {
+ bool checkWild(Dsymbol s)
+ {
+ if (!s)
+ return false;
+ if (auto ad = s.isAggregateDeclaration())
+ {
+ if (ad.isNested())
+ return checkEnclosingWild(s);
+ }
+ else if (auto ff = s.isFuncDeclaration())
+ {
+ if ((cast(TypeFunction)ff.type).iswild)
+ return errorInout(wildmatch);
+
+ if (ff.isNested() || ff.isThis())
+ return checkEnclosingWild(s);
+ }
+ return false;
+ }
+
+ Dsymbol ctx0 = s.toParent2();
+ Dsymbol ctx1 = s.toParentLocal();
+ if (checkWild(ctx0))
+ return true;
+ if (ctx0 != ctx1)
+ return checkWild(ctx1);
+ return false;
+ }
+ if ((fd.isThis() || fd.isNested()) && checkEnclosingWild(fd))
+ return true;
+ }
+ else if (tf.isWild())
+ return errorInout(wildmatch);
+ }
+
+ Expression firstArg = ((tf.next && tf.next.ty == Tvoid || isCtorCall) &&
+ tthis &&
+ tthis.isMutable() && tthis.toBasetype().ty == Tstruct &&
+ tthis.hasPointers())
+ ? ethis : null;
+
+ assert(nargs >= nparams);
+ foreach (const i, arg; (*arguments)[0 .. nargs])
+ {
+ assert(arg);
+ if (i < nparams)
+ {
+ Parameter p = tf.parameterList[i];
+ Type targ = arg.type; // keep original type for isCopyable() because alias this
+ // resolution may hide an uncopyable type
+
+ if (!(p.storageClass & STC.lazy_ && p.type.ty == Tvoid))
+ {
+ Type tprm = p.type.hasWild()
+ ? p.type.substWildTo(wildmatch)
+ : p.type;
+
+ const hasCopyCtor = (arg.type.ty == Tstruct) && (cast(TypeStruct)arg.type).sym.hasCopyCtor;
+ const typesMatch = arg.type.mutableOf().unSharedOf().equals(tprm.mutableOf().unSharedOf());
+ if (!((hasCopyCtor && typesMatch) || tprm.equals(arg.type)))
+ {
+ //printf("arg.type = %s, p.type = %s\n", arg.type.toChars(), p.type.toChars());
+ arg = arg.implicitCastTo(sc, tprm);
+ arg = arg.optimize(WANTvalue, p.isReference());
+ }
+ }
+
+ // Support passing rvalue to `in` parameters
+ if ((p.storageClass & (STC.in_ | STC.ref_)) == (STC.in_ | STC.ref_))
+ {
+ if (!arg.isLvalue())
+ {
+ auto v = copyToTemp(STC.exptemp, "__rvalue", arg);
+ Expression ev = new DeclarationExp(arg.loc, v);
+ ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
+ arg = ev.expressionSemantic(sc);
+ }
+ arg = arg.toLvalue(sc, arg);
+
+ // Look for mutable misaligned pointer, etc., in @safe mode
+ err |= checkUnsafeAccess(sc, arg, false, true);
+ }
+ else if (p.storageClass & STC.ref_)
+ {
+ if (global.params.rvalueRefParam &&
+ !arg.isLvalue() &&
+ targ.isCopyable())
+ { /* allow rvalues to be passed to ref parameters by copying
+ * them to a temp, then pass the temp as the argument
+ */
+ auto v = copyToTemp(0, "__rvalue", arg);
+ Expression ev = new DeclarationExp(arg.loc, v);
+ ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
+ arg = ev.expressionSemantic(sc);
+ }
+ arg = arg.toLvalue(sc, arg);
+
+ // Look for mutable misaligned pointer, etc., in @safe mode
+ err |= checkUnsafeAccess(sc, arg, false, true);
+ }
+ else if (p.storageClass & STC.out_)
+ {
+ Type t = arg.type;
+ if (!t.isMutable() || !t.isAssignable()) // check blit assignable
+ {
+ arg.error("cannot modify struct `%s` with immutable members", arg.toChars());
+ err = true;
+ }
+ else
+ {
+ // Look for misaligned pointer, etc., in @safe mode
+ err |= checkUnsafeAccess(sc, arg, false, true);
+ err |= checkDefCtor(arg.loc, t); // t must be default constructible
+ }
+ arg = arg.toLvalue(sc, arg);
+ }
+ else if (p.storageClass & STC.lazy_)
+ {
+ // Convert lazy argument to a delegate
+ auto t = (p.type.ty == Tvoid) ? p.type : arg.type;
+ arg = toDelegate(arg, t, sc);
+ }
+ //printf("arg: %s\n", arg.toChars());
+ //printf("type: %s\n", arg.type.toChars());
+ //printf("param: %s\n", p.toChars());
+
+ if (firstArg && p.storageClass & STC.return_)
+ {
+ /* Argument value can be assigned to firstArg.
+ * Check arg to see if it matters.
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ err |= checkParamArgumentReturn(sc, firstArg, arg, false);
+ }
+ else if (tf.parameterEscapes(tthis, p))
+ {
+ /* Argument value can escape from the called function.
+ * Check arg to see if it matters.
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ err |= checkParamArgumentEscape(sc, fd, p, arg, false, false);
+ }
+ else if (!(p.storageClass & STC.return_))
+ {
+ /* Argument value cannot escape from the called function.
+ */
+ Expression a = arg;
+ if (a.op == TOK.cast_)
+ a = (cast(CastExp)a).e1;
+
+ ArrayLiteralExp ale;
+ if (p.type.toBasetype().ty == Tarray &&
+ (ale = a.isArrayLiteralExp()) !is null)
+ {
+ // allocate the array literal as temporary static array on the stack
+ ale.type = ale.type.nextOf().sarrayOf(ale.elements ? ale.elements.length : 0);
+ auto tmp = copyToTemp(0, "__arrayliteral_on_stack", ale);
+ auto declareTmp = new DeclarationExp(ale.loc, tmp);
+ auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp), p.type);
+ arg = CommaExp.combine(declareTmp, castToSlice);
+ arg = arg.expressionSemantic(sc);
+ }
+ else if (a.op == TOK.function_)
+ {
+ /* Function literals can only appear once, so if this
+ * appearance was scoped, there cannot be any others.
+ */
+ FuncExp fe = cast(FuncExp)a;
+ fe.fd.tookAddressOf = 0;
+ }
+ else if (a.op == TOK.delegate_)
+ {
+ /* For passing a delegate to a scoped parameter,
+ * this doesn't count as taking the address of it.
+ * We only worry about 'escaping' references to the function.
+ */
+ DelegateExp de = cast(DelegateExp)a;
+ if (de.e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)de.e1;
+ FuncDeclaration f = ve.var.isFuncDeclaration();
+ if (f)
+ {
+ if (f.tookAddressOf)
+ --f.tookAddressOf;
+ //printf("--tookAddressOf = %d\n", f.tookAddressOf);
+ }
+ }
+ }
+ }
+ if (!p.isReference())
+ err |= arg.checkSharedAccess(sc);
+
+ arg = arg.optimize(WANTvalue, p.isReference());
+
+ /* Determine if this parameter is the "first reference" parameter through which
+ * later "return" arguments can be stored.
+ */
+ if (i == 0 && !tthis && p.isReference() && p.type &&
+ (tf.next && tf.next.ty == Tvoid || isCtorCall))
+ {
+ Type tb = p.type.baseElemOf();
+ if (tb.isMutable() && tb.hasPointers())
+ {
+ firstArg = arg;
+ }
+ }
+ }
+ else
+ {
+ // These will be the trailing ... arguments
+ // If not D linkage, do promotions
+ if (tf.linkage != LINK.d)
+ {
+ // Promote bytes, words, etc., to ints
+ arg = integralPromotions(arg, sc);
+
+ // Promote floats to doubles
+ switch (arg.type.ty)
+ {
+ case Tfloat32:
+ arg = arg.castTo(sc, Type.tfloat64);
+ break;
+
+ case Timaginary32:
+ arg = arg.castTo(sc, Type.timaginary64);
+ break;
+
+ default:
+ break;
+ }
+ if (tf.parameterList.varargs == VarArg.variadic)
+ {
+ const(char)* p = tf.linkage == LINK.c ? "extern(C)" : "extern(C++)";
+ if (arg.type.ty == Tarray)
+ {
+ arg.error("cannot pass dynamic arrays to `%s` vararg functions", p);
+ err = true;
+ }
+ if (arg.type.ty == Tsarray)
+ {
+ arg.error("cannot pass static arrays to `%s` vararg functions", p);
+ err = true;
+ }
+ }
+ }
+
+ // Do not allow types that need destructors or copy constructors.
+ if (arg.type.needsDestruction())
+ {
+ arg.error("cannot pass types that need destruction as variadic arguments");
+ err = true;
+ }
+ if (arg.type.needsCopyOrPostblit())
+ {
+ arg.error("cannot pass types with postblits or copy constructors as variadic arguments");
+ err = true;
+ }
+
+ // Convert static arrays to dynamic arrays
+ // BUG: I don't think this is right for D2
+ Type tb = arg.type.toBasetype();
+ if (tb.ty == Tsarray)
+ {
+ TypeSArray ts = cast(TypeSArray)tb;
+ Type ta = ts.next.arrayOf();
+ if (ts.size(arg.loc) == 0)
+ arg = new NullExp(arg.loc, ta);
+ else
+ arg = arg.castTo(sc, ta);
+ }
+ if (tb.ty == Tstruct)
+ {
+ //arg = callCpCtor(sc, arg);
+ }
+ // Give error for overloaded function addresses
+ if (arg.op == TOK.symbolOffset)
+ {
+ SymOffExp se = cast(SymOffExp)arg;
+ if (se.hasOverloads && !se.var.isFuncDeclaration().isUnique())
+ {
+ arg.error("function `%s` is overloaded", arg.toChars());
+ err = true;
+ }
+ }
+ err |= arg.checkValue();
+ err |= arg.checkSharedAccess(sc);
+ arg = arg.optimize(WANTvalue);
+ }
+ (*arguments)[i] = arg;
+ }
+
+ /* If calling C scanf(), printf(), or any variants, check the format string against the arguments
+ */
+ const isVa_list = tf.parameterList.varargs == VarArg.none;
+ if (fd && fd.flags & FUNCFLAG.printf)
+ {
+ if (auto se = (*arguments)[nparams - 1 - isVa_list].isStringExp())
+ {
+ checkPrintfFormat(se.loc, se.peekString(), (*arguments)[nparams .. nargs], isVa_list);
+ }
+ }
+ else if (fd && fd.flags & FUNCFLAG.scanf)
+ {
+ if (auto se = (*arguments)[nparams - 1 - isVa_list].isStringExp())
+ {
+ checkScanfFormat(se.loc, se.peekString(), (*arguments)[nparams .. nargs], isVa_list);
+ }
+ }
+ else
+ {
+ // TODO: not checking the "v" functions yet (for those, check format string only, not args)
+ }
+
+ /* Remaining problems:
+ * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is
+ * implemented by calling a function) we'll defer this for now.
+ * 2. value structs (or static arrays of them) that need to be copy constructed
+ * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the
+ * function gets called.
+ * 4. value structs need to be destructed after the function call for platforms where the caller destroys the arguments.
+ * 2, 3 and 4 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned
+ * up properly. Pushing arguments on the stack then cannot fail.
+ */
+ {
+ /* TODO: tackle problem 1)
+ */
+ const bool leftToRight = true; // TODO: Any cases that need rightToLeft?
+ if (!leftToRight)
+ assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity
+
+ /* Does Problem (4) apply?
+ */
+ const bool callerDestroysArgs = !target.isCalleeDestroyingArgs(tf);
+
+ const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1);
+ const ptrdiff_t end = (leftToRight ? cast(ptrdiff_t)nargs : -1);
+ const ptrdiff_t step = (leftToRight ? 1 : -1);
+
+ /* Compute indices of last throwing argument and first arg needing destruction.
+ * Used to not set up destructors unless an arg needs destruction on a throw
+ * in a later argument.
+ */
+ ptrdiff_t lastthrow = -1; // last argument that may throw
+ ptrdiff_t firstdtor = -1; // first argument that needs destruction
+ ptrdiff_t lastdtor = -1; // last argument that needs destruction
+ for (ptrdiff_t i = start; i != end; i += step)
+ {
+ Expression arg = (*arguments)[i];
+ if (canThrow(arg, sc.func, false))
+ lastthrow = i;
+ if (arg.type.needsDestruction())
+ {
+ Parameter p = (i >= nparams ? null : tf.parameterList[i]);
+ if (!(p && (p.storageClass & (STC.lazy_ | STC.ref_ | STC.out_))))
+ {
+ if (firstdtor == -1)
+ firstdtor = i;
+ lastdtor = i;
+ }
+ }
+ }
+
+ /* Do we need 'eprefix' for problems 3 or 4?
+ */
+ const bool needsPrefix = callerDestroysArgs
+ ? firstdtor >= 0 // true if any argument needs destruction
+ : firstdtor >= 0 && lastthrow >= 0 &&
+ (lastthrow - firstdtor) * step > 0; // last throw after first destruction
+ const ptrdiff_t lastPrefix = callerDestroysArgs
+ ? lastdtor // up to last argument requiring destruction
+ : lastthrow; // up to last potentially throwing argument
+
+ /* Problem 3: initialize 'eprefix' by declaring the gate
+ */
+ VarDeclaration gate;
+ if (needsPrefix && !callerDestroysArgs)
+ {
+ // eprefix => bool __gate [= false]
+ Identifier idtmp = Identifier.generateId("__gate");
+ gate = new VarDeclaration(loc, Type.tbool, idtmp, null);
+ gate.storage_class |= STC.temp | STC.ctfe | STC.volatile_;
+ gate.dsymbolSemantic(sc);
+
+ auto ae = new DeclarationExp(loc, gate);
+ eprefix = ae.expressionSemantic(sc);
+ }
+
+ for (ptrdiff_t i = start; i != end; i += step)
+ {
+ Expression arg = (*arguments)[i];
+ //printf("arg[%d]: %s\n", cast(int)i, arg.toChars());
+
+ Parameter parameter = (i >= nparams ? null : tf.parameterList[i]);
+ const bool isRef = parameter && parameter.isReference();
+ const bool isLazy = (parameter && (parameter.storageClass & STC.lazy_));
+
+ /* Skip lazy parameters
+ */
+ if (isLazy)
+ continue;
+
+ /* Do we have 'eprefix' and aren't past 'lastPrefix' yet?
+ * Then declare a temporary variable for this arg and append that declaration
+ * to 'eprefix', which will implicitly take care of potential problem 2) for
+ * this arg.
+ * 'eprefix' will therefore finally contain all args up to and including 'lastPrefix',
+ * excluding all lazy parameters.
+ */
+ if (needsPrefix && (lastPrefix - i) * step >= 0)
+ {
+ const bool needsDtor = !isRef && arg.type.needsDestruction() &&
+ // Problem 3: last throwing arg doesn't require dtor patching
+ (callerDestroysArgs || i != lastPrefix);
+
+ /* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor)
+ */
+ auto tmp = copyToTemp(
+ (parameter ? parameter.storageClass : tf.parameterList.stc) & (STC.scope_),
+ needsDtor ? "__pfx" : "__pfy",
+ !isRef ? arg : arg.addressOf());
+ tmp.dsymbolSemantic(sc);
+
+ if (callerDestroysArgs)
+ {
+ /* Problem 4: Normal temporary, destructed after the call
+ */
+ if (needsDtor)
+ tmp.isArgDtorVar = true; // mark it so that the backend passes it by ref to the function being called
+ }
+ else
+ {
+ /* Problem 3: Modify the destructor so it only runs if gate==false,
+ * i.e., only if there was a throw while constructing the args
+ */
+ if (!needsDtor)
+ {
+ if (tmp.edtor)
+ {
+ assert(i == lastPrefix);
+ tmp.edtor = null;
+ }
+ }
+ else
+ {
+ // edtor => (__gate || edtor)
+ assert(tmp.edtor);
+ Expression e = tmp.edtor;
+ e = new LogicalExp(e.loc, TOK.orOr, new VarExp(e.loc, gate), e);
+ tmp.edtor = e.expressionSemantic(sc);
+ //printf("edtor: %s\n", tmp.edtor.toChars());
+ }
+ }
+
+ // eprefix => (eprefix, auto __pfx/y = arg)
+ auto ae = new DeclarationExp(loc, tmp);
+ eprefix = Expression.combine(eprefix, ae.expressionSemantic(sc));
+
+ // arg => __pfx/y
+ arg = new VarExp(loc, tmp);
+ arg = arg.expressionSemantic(sc);
+ if (isRef)
+ {
+ arg = new PtrExp(loc, arg);
+ arg = arg.expressionSemantic(sc);
+ }
+
+ /* Problem 3: Last throwing arg?
+ * Then finalize eprefix => (eprefix, gate = true), i.e., disable the
+ * dtors right after constructing the last throwing arg.
+ * From now on, the callee will take care of destructing the args because
+ * the args are implicitly moved into function parameters.
+ */
+ if (!callerDestroysArgs && i == lastPrefix)
+ {
+ auto e = new AssignExp(gate.loc, new VarExp(gate.loc, gate), IntegerExp.createBool(true));
+ eprefix = Expression.combine(eprefix, e.expressionSemantic(sc));
+ }
+ }
+ else // not part of 'eprefix'
+ {
+ /* Handle problem 2) by calling the copy constructor for value structs
+ * (or static arrays of them) if appropriate.
+ */
+ Type tv = arg.type.baseElemOf();
+ if (!isRef && tv.ty == Tstruct)
+ arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null);
+ }
+
+ (*arguments)[i] = arg;
+ }
+ }
+ //if (eprefix) printf("eprefix: %s\n", eprefix.toChars());
+
+ /* Test compliance with DIP1021
+ */
+ if (global.params.useDIP1021 &&
+ tf.trust != TRUST.system && tf.trust != TRUST.trusted)
+ err |= checkMutableArguments(sc, fd, tf, ethis, arguments, false);
+
+ // If D linkage and variadic, add _arguments[] as first argument
+ if (tf.isDstyleVariadic())
+ {
+ assert(arguments.dim >= nparams);
+
+ auto args = new Parameters(arguments.dim - nparams);
+ for (size_t i = 0; i < arguments.dim - nparams; i++)
+ {
+ auto arg = new Parameter(STC.in_, (*arguments)[nparams + i].type, null, null, null);
+ (*args)[i] = arg;
+ }
+ auto tup = new TypeTuple(args);
+ Expression e = (new TypeidExp(loc, tup)).expressionSemantic(sc);
+ arguments.insert(0, e);
+ }
+
+ /* Determine function return type: tret
+ */
+ Type tret = tf.next;
+ if (isCtorCall)
+ {
+ //printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd.toChars(), fd.type.toChars(),
+ // wildmatch, tf.isWild(), fd.isReturnIsolated());
+ if (!tthis)
+ {
+ assert(sc.intypeof || global.errors);
+ tthis = fd.isThis().type.addMod(fd.type.mod);
+ }
+ if (tf.isWild() && !fd.isReturnIsolated())
+ {
+ if (wildmatch)
+ tret = tret.substWildTo(wildmatch);
+ int offset;
+ if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0))
+ {
+ const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars();
+ const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars();
+ .error(loc, "`inout` constructor `%s` creates%s object, not%s", fd.toPrettyChars(), s1, s2);
+ err = true;
+ }
+ }
+ tret = tthis;
+ }
+ else if (wildmatch && tret)
+ {
+ /* Adjust function return type based on wildmatch
+ */
+ //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret.toChars());
+ tret = tret.substWildTo(wildmatch);
+ }
+
+ *prettype = tret;
+ *peprefix = eprefix;
+ return (err || olderrors != global.errors);
+}
+
+/**
+ * Determines whether a symbol represents a module or package
+ * (Used as a helper for is(type == module) and is(type == package))
+ *
+ * Params:
+ * sym = the symbol to be checked
+ *
+ * Returns:
+ * the symbol which `sym` represents (or `null` if it doesn't represent a `Package`)
+ */
+Package resolveIsPackage(Dsymbol sym)
+{
+ Package pkg;
+ if (Import imp = sym.isImport())
+ {
+ if (imp.pkg is null)
+ {
+ .error(sym.loc, "Internal Compiler Error: unable to process forward-referenced import `%s`",
+ imp.toChars());
+ assert(0);
+ }
+ pkg = imp.pkg;
+ }
+ else if (auto mod = sym.isModule())
+ pkg = mod.isPackageFile ? mod.pkg : sym.isPackage();
+ else
+ pkg = sym.isPackage();
+ if (pkg)
+ pkg.resolvePKGunknown();
+ return pkg;
+}
+
+private Module loadStdMath()
+{
+ __gshared Import impStdMath = null;
+ __gshared Identifier[1] stdID;
+ if (!impStdMath)
+ {
+ stdID[0] = Id.std;
+ auto s = new Import(Loc.initial, stdID[], Id.math, null, false);
+ // Module.load will call fatal() if there's no std.math available.
+ // Gag the error here, pushing the error handling to the caller.
+ uint errors = global.startGagging();
+ s.load(null);
+ if (s.mod)
+ {
+ s.mod.importAll(null);
+ s.mod.dsymbolSemantic(null);
+ }
+ global.endGagging(errors);
+ impStdMath = s;
+ }
+ return impStdMath.mod;
+}
+
+private extern (C++) final class ExpressionSemanticVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+
+ Scope* sc;
+ Expression result;
+
+ this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ private void setError()
+ {
+ result = ErrorExp.get();
+ }
+
+ /**************************
+ * Semantically analyze Expression.
+ * Determine types, fold constants, etc.
+ */
+ override void visit(Expression e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("Expression::semantic() %s\n", e.toChars());
+ }
+ if (e.type)
+ e.type = e.type.typeSemantic(e.loc, sc);
+ else
+ e.type = Type.tvoid;
+ result = e;
+ }
+
+ override void visit(IntegerExp e)
+ {
+ assert(e.type);
+ if (e.type.ty == Terror)
+ return setError();
+
+ assert(e.type.deco);
+ e.setInteger(e.getInteger());
+ result = e;
+ }
+
+ override void visit(RealExp e)
+ {
+ if (!e.type)
+ e.type = Type.tfloat64;
+ else
+ e.type = e.type.typeSemantic(e.loc, sc);
+ result = e;
+ }
+
+ override void visit(ComplexExp e)
+ {
+ if (!e.type)
+ e.type = Type.tcomplex80;
+ else
+ e.type = e.type.typeSemantic(e.loc, sc);
+ result = e;
+ }
+
+ override void visit(IdentifierExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars());
+ }
+ if (exp.type) // This is used as the dummy expression
+ {
+ result = exp;
+ return;
+ }
+
+ Dsymbol scopesym;
+ Dsymbol s = sc.search(exp.loc, exp.ident, &scopesym);
+ if (s)
+ {
+ if (s.errors)
+ return setError();
+
+ Expression e;
+
+ /* See if the symbol was a member of an enclosing 'with'
+ */
+ WithScopeSymbol withsym = scopesym.isWithScopeSymbol();
+ if (withsym && withsym.withstate.wthis && symbolIsVisible(sc, s))
+ {
+ /* Disallow shadowing
+ */
+ // First find the scope of the with
+ Scope* scwith = sc;
+ while (scwith.scopesym != scopesym)
+ {
+ scwith = scwith.enclosing;
+ assert(scwith);
+ }
+ // Look at enclosing scopes for symbols with the same name,
+ // in the same function
+ for (Scope* scx = scwith; scx && scx.func == scwith.func; scx = scx.enclosing)
+ {
+ Dsymbol s2;
+ if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2)
+ {
+ exp.error("with symbol `%s` is shadowing local symbol `%s`", s.toPrettyChars(), s2.toPrettyChars());
+ return setError();
+ }
+ }
+ s = s.toAlias();
+
+ // Same as wthis.ident
+ // TODO: DotIdExp.semantic will find 'ident' from 'wthis' again.
+ // The redudancy should be removed.
+ e = new VarExp(exp.loc, withsym.withstate.wthis);
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ e = e.expressionSemantic(sc);
+ }
+ else
+ {
+ if (withsym)
+ {
+ if (withsym.withstate.exp.type.ty != Tvoid)
+ {
+ // 'with (exp)' is a type expression
+ // or 's' is not visible there (for error message)
+ e = new TypeExp(exp.loc, withsym.withstate.exp.type);
+ }
+ else
+ {
+ // 'with (exp)' is a Package/Module
+ e = withsym.withstate.exp;
+ }
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+
+ /* If f is really a function template,
+ * then replace f with the function template declaration.
+ */
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ TemplateDeclaration td = getFuncTemplateDecl(f);
+ if (td)
+ {
+ if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+ td = td.overroot; // then get the start
+ e = new TemplateExp(exp.loc, td, f);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+
+ if (global.params.fixAliasThis)
+ {
+ ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol();
+ if (expDsym)
+ {
+ //printf("expDsym = %s\n", expDsym.exp.toChars());
+ result = expDsym.exp.expressionSemantic(sc);
+ return;
+ }
+ }
+ // Haven't done overload resolution yet, so pass 1
+ e = symbolToExp(s, exp.loc, sc, true);
+ }
+ result = e;
+ return;
+ }
+
+ if (!global.params.fixAliasThis && hasThis(sc))
+ {
+ for (AggregateDeclaration ad = sc.getStructClassScope(); ad;)
+ {
+ if (ad.aliasthis)
+ {
+ Expression e;
+ e = new ThisExp(exp.loc);
+ e = new DotIdExp(exp.loc, e, ad.aliasthis.ident);
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ e = e.trySemantic(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+
+ auto cd = ad.isClassDeclaration();
+ if (cd && cd.baseClass && cd.baseClass != ClassDeclaration.object)
+ {
+ ad = cd.baseClass;
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (exp.ident == Id.ctfe)
+ {
+ if (sc.flags & SCOPE.ctfe)
+ {
+ exp.error("variable `__ctfe` cannot be read at compile time");
+ return setError();
+ }
+
+ // Create the magic __ctfe bool variable
+ auto vd = new VarDeclaration(exp.loc, Type.tbool, Id.ctfe, null);
+ vd.storage_class |= STC.temp;
+ vd.semanticRun = PASS.semanticdone;
+ Expression e = new VarExp(exp.loc, vd);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ // If we've reached this point and are inside a with() scope then we may
+ // try one last attempt by checking whether the 'wthis' object supports
+ // dynamic dispatching via opDispatch.
+ // This is done by rewriting this expression as wthis.ident.
+ // The innermost with() scope of the hierarchy to satisfy the condition
+ // above wins.
+ // https://issues.dlang.org/show_bug.cgi?id=6400
+ for (Scope* sc2 = sc; sc2; sc2 = sc2.enclosing)
+ {
+ if (!sc2.scopesym)
+ continue;
+
+ if (auto ss = sc2.scopesym.isWithScopeSymbol())
+ {
+ if (ss.withstate.wthis)
+ {
+ Expression e;
+ e = new VarExp(exp.loc, ss.withstate.wthis);
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ e = e.trySemantic(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+ // Try Type.opDispatch (so the static version)
+ else if (ss.withstate.exp && ss.withstate.exp.op == TOK.type)
+ {
+ if (Type t = ss.withstate.exp.isTypeExp().type)
+ {
+ Expression e;
+ e = new TypeExp(exp.loc, t);
+ e = new DotIdExp(exp.loc, e, exp.ident);
+ e = e.trySemantic(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ /* Look for what user might have meant
+ */
+ if (const n = importHint(exp.ident.toString()))
+ exp.error("`%s` is not defined, perhaps `import %.*s;` is needed?", exp.ident.toChars(), cast(int)n.length, n.ptr);
+ else if (auto s2 = sc.search_correct(exp.ident))
+ exp.error("undefined identifier `%s`, did you mean %s `%s`?", exp.ident.toChars(), s2.kind(), s2.toChars());
+ else if (const p = Scope.search_correct_C(exp.ident))
+ exp.error("undefined identifier `%s`, did you mean `%s`?", exp.ident.toChars(), p);
+ else
+ exp.error("undefined identifier `%s`", exp.ident.toChars());
+
+ result = ErrorExp.get();
+ }
+
+ override void visit(DsymbolExp e)
+ {
+ result = symbolToExp(e.s, e.loc, sc, e.hasOverloads);
+ }
+
+ override void visit(ThisExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ThisExp::semantic()\n");
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ FuncDeclaration fd = hasThis(sc); // fd is the uplevel function with the 'this' variable
+ AggregateDeclaration ad;
+
+ /* Special case for typeof(this) and typeof(super) since both
+ * should work even if they are not inside a non-static member function
+ */
+ if (!fd && sc.intypeof == 1)
+ {
+ // Find enclosing struct or class
+ for (Dsymbol s = sc.getStructClassScope(); 1; s = s.parent)
+ {
+ if (!s)
+ {
+ e.error("`%s` is not in a class or struct scope", e.toChars());
+ goto Lerr;
+ }
+ ClassDeclaration cd = s.isClassDeclaration();
+ if (cd)
+ {
+ e.type = cd.type;
+ result = e;
+ return;
+ }
+ StructDeclaration sd = s.isStructDeclaration();
+ if (sd)
+ {
+ e.type = sd.type;
+ result = e;
+ return;
+ }
+ }
+ }
+ if (!fd)
+ goto Lerr;
+
+ assert(fd.vthis);
+ e.var = fd.vthis;
+ assert(e.var.parent);
+ ad = fd.isMemberLocal();
+ if (!ad)
+ ad = fd.isMember2();
+ assert(ad);
+ e.type = ad.type.addMod(e.var.type.mod);
+
+ if (e.var.checkNestedReference(sc, e.loc))
+ return setError();
+
+ result = e;
+ return;
+
+ Lerr:
+ e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars());
+ result = ErrorExp.get();
+ }
+
+ override void visit(SuperExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("SuperExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ FuncDeclaration fd = hasThis(sc);
+ ClassDeclaration cd;
+ Dsymbol s;
+
+ /* Special case for typeof(this) and typeof(super) since both
+ * should work even if they are not inside a non-static member function
+ */
+ if (!fd && sc.intypeof == 1)
+ {
+ // Find enclosing class
+ for (s = sc.getStructClassScope(); 1; s = s.parent)
+ {
+ if (!s)
+ {
+ e.error("`%s` is not in a class scope", e.toChars());
+ goto Lerr;
+ }
+ cd = s.isClassDeclaration();
+ if (cd)
+ {
+ cd = cd.baseClass;
+ if (!cd)
+ {
+ e.error("class `%s` has no `super`", s.toChars());
+ goto Lerr;
+ }
+ e.type = cd.type;
+ result = e;
+ return;
+ }
+ }
+ }
+ if (!fd)
+ goto Lerr;
+
+ e.var = fd.vthis;
+ assert(e.var && e.var.parent);
+
+ s = fd.toParentDecl();
+ if (s.isTemplateDeclaration()) // allow inside template constraint
+ s = s.toParent();
+ assert(s);
+ cd = s.isClassDeclaration();
+ //printf("parent is %s %s\n", fd.toParent().kind(), fd.toParent().toChars());
+ if (!cd)
+ goto Lerr;
+ if (!cd.baseClass)
+ {
+ e.error("no base class for `%s`", cd.toChars());
+ e.type = cd.type.addMod(e.var.type.mod);
+ }
+ else
+ {
+ e.type = cd.baseClass.type;
+ e.type = e.type.castMod(e.var.type.mod);
+ }
+
+ if (e.var.checkNestedReference(sc, e.loc))
+ return setError();
+
+ result = e;
+ return;
+
+ Lerr:
+ e.error("`super` is only allowed in non-static class member functions");
+ result = ErrorExp.get();
+ }
+
+ override void visit(NullExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("NullExp::semantic('%s')\n", e.toChars());
+ }
+ // NULL is the same as (void *)0
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+ e.type = Type.tnull;
+ result = e;
+ }
+
+ override void visit(StringExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("StringExp::semantic() %s\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ OutBuffer buffer;
+ size_t newlen = 0;
+ size_t u;
+ dchar c;
+
+ switch (e.postfix)
+ {
+ case 'd':
+ for (u = 0; u < e.len;)
+ {
+ if (const p = utf_decodeChar(e.peekString(), u, c))
+ {
+ e.error("%.*s", cast(int)p.length, p.ptr);
+ return setError();
+ }
+ else
+ {
+ buffer.write4(c);
+ newlen++;
+ }
+ }
+ buffer.write4(0);
+ e.setData(buffer.extractData(), newlen, 4);
+ if (sc && sc.flags & SCOPE.Cfile)
+ e.type = Type.tuns32.pointerTo();
+ else
+ e.type = Type.tdchar.immutableOf().arrayOf();
+ e.committed = 1;
+ break;
+
+ case 'w':
+ for (u = 0; u < e.len;)
+ {
+ if (const p = utf_decodeChar(e.peekString(), u, c))
+ {
+ e.error("%.*s", cast(int)p.length, p.ptr);
+ return setError();
+ }
+ else
+ {
+ buffer.writeUTF16(c);
+ newlen++;
+ if (c >= 0x10000)
+ newlen++;
+ }
+ }
+ buffer.writeUTF16(0);
+ e.setData(buffer.extractData(), newlen, 2);
+ if (sc && sc.flags & SCOPE.Cfile)
+ e.type = Type.tuns16.pointerTo();
+ else
+ e.type = Type.twchar.immutableOf().arrayOf();
+ e.committed = 1;
+ break;
+
+ case 'c':
+ e.committed = 1;
+ goto default;
+
+ default:
+ if (sc && sc.flags & SCOPE.Cfile)
+ e.type = Type.tchar.pointerTo();
+ else
+ e.type = Type.tchar.immutableOf().arrayOf();
+ break;
+ }
+ e.type = e.type.typeSemantic(e.loc, sc);
+ //type = type.immutableOf();
+ //printf("type = %s\n", type.toChars());
+
+ result = e;
+ }
+
+ override void visit(TupleExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("+TupleExp::semantic(%s)\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (exp.e0)
+ exp.e0 = exp.e0.expressionSemantic(sc);
+
+ // Run semantic() on each argument
+ bool err = false;
+ for (size_t i = 0; i < exp.exps.dim; i++)
+ {
+ Expression e = (*exp.exps)[i];
+ e = e.expressionSemantic(sc);
+ if (!e.type)
+ {
+ exp.error("`%s` has no value", e.toChars());
+ err = true;
+ }
+ else if (e.op == TOK.error)
+ err = true;
+ else
+ (*exp.exps)[i] = e;
+ }
+ if (err)
+ return setError();
+
+ expandTuples(exp.exps);
+
+ exp.type = new TypeTuple(exp.exps);
+ exp.type = exp.type.typeSemantic(exp.loc, sc);
+ //printf("-TupleExp::semantic(%s)\n", toChars());
+ result = exp;
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ArrayLiteralExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ /* Perhaps an empty array literal [ ] should be rewritten as null?
+ */
+
+ if (e.basis)
+ e.basis = e.basis.expressionSemantic(sc);
+ if (arrayExpressionSemantic(e.elements, sc) || (e.basis && e.basis.op == TOK.error))
+ return setError();
+
+ expandTuples(e.elements);
+
+ if (e.basis)
+ e.elements.push(e.basis);
+ Type t0 = arrayExpressionToCommonType(sc, *e.elements);
+ if (e.basis)
+ e.basis = e.elements.pop();
+ if (t0 is null)
+ return setError();
+
+ e.type = t0.arrayOf();
+ e.type = e.type.typeSemantic(e.loc, sc);
+
+ /* Disallow array literals of type void being used.
+ */
+ if (e.elements.dim > 0 && t0.ty == Tvoid)
+ {
+ e.error("`%s` of type `%s` has no value", e.toChars(), e.type.toChars());
+ return setError();
+ }
+
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ semanticTypeInfo(sc, e.type);
+
+ result = e;
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("AssocArrayLiteralExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ // Run semantic() on each element
+ bool err_keys = arrayExpressionSemantic(e.keys, sc);
+ bool err_vals = arrayExpressionSemantic(e.values, sc);
+ if (err_keys || err_vals)
+ return setError();
+
+ expandTuples(e.keys);
+ expandTuples(e.values);
+ if (e.keys.dim != e.values.dim)
+ {
+ e.error("number of keys is %llu, must match number of values %llu",
+ cast(ulong) e.keys.dim, cast(ulong) e.values.dim);
+ return setError();
+ }
+
+ Type tkey = arrayExpressionToCommonType(sc, *e.keys);
+ Type tvalue = arrayExpressionToCommonType(sc, *e.values);
+ if (tkey is null || tvalue is null)
+ return setError();
+
+ e.type = new TypeAArray(tvalue, tkey);
+ e.type = e.type.typeSemantic(e.loc, sc);
+
+ semanticTypeInfo(sc, e.type);
+
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (checkAssocArrayLiteralEscape(sc, e, false))
+ return setError();
+ }
+
+ result = e;
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("StructLiteralExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ e.sd.size(e.loc);
+ if (e.sd.sizeok != Sizeok.done)
+ return setError();
+
+ // run semantic() on each element
+ if (arrayExpressionSemantic(e.elements, sc))
+ return setError();
+
+ expandTuples(e.elements);
+
+ /* Fit elements[] to the corresponding type of field[].
+ */
+ if (!e.sd.fit(e.loc, sc, e.elements, e.stype))
+ return setError();
+
+ /* Fill out remainder of elements[] with default initializers for fields[]
+ */
+ if (!e.sd.fill(e.loc, e.elements, false))
+ {
+ /* An error in the initializer needs to be recorded as an error
+ * in the enclosing function or template, since the initializer
+ * will be part of the stuct declaration.
+ */
+ global.increaseErrorCount();
+ return setError();
+ }
+
+ if (checkFrameAccess(e.loc, sc, e.sd, e.elements.dim))
+ return setError();
+
+ e.type = e.stype ? e.stype : e.sd.type;
+ result = e;
+ }
+
+ override void visit(CompoundLiteralExp cle)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CompoundLiteralExp::semantic('%s')\n", cle.toChars());
+ }
+ Type t = cle.type.typeSemantic(cle.loc, sc);
+ auto init = initializerSemantic(cle.initializer, sc, t, INITnointerpret);
+ auto e = initializerToExpression(init, t);
+ if (!e)
+ {
+ error(cle.loc, "cannot convert initializer `%s` to expression", init.toChars());
+ return setError();
+ }
+ result = e;
+ return;
+ }
+
+ override void visit(TypeExp exp)
+ {
+ if (exp.type.ty == Terror)
+ return setError();
+
+ //printf("TypeExp::semantic(%s)\n", exp.type.toChars());
+ Expression e;
+ Type t;
+ Dsymbol s;
+
+ dmd.typesem.resolve(exp.type, exp.loc, sc, e, t, s, true);
+ if (e)
+ {
+ // `(Type)` is actually `(var)` so if `(var)` is a member requiring `this`
+ // then rewrite as `(this.var)` in case it would be followed by a DotVar
+ // to fix https://issues.dlang.org/show_bug.cgi?id=9490
+ VarExp ve = e.isVarExp();
+ if (ve && ve.var && exp.parens && !ve.var.isStatic() && !(sc.stc & STC.static_) &&
+ sc.func && sc.func.needThis && ve.var.toParent2().isAggregateDeclaration())
+ {
+ // printf("apply fix for issue 9490: add `this.` to `%s`...\n", e.toChars());
+ e = new DotVarExp(exp.loc, new ThisExp(exp.loc), ve.var, false);
+ }
+ //printf("e = %s %s\n", Token::toChars(e.op), e.toChars());
+ e = e.expressionSemantic(sc);
+ }
+ else if (t)
+ {
+ //printf("t = %d %s\n", t.ty, t.toChars());
+ exp.type = t.typeSemantic(exp.loc, sc);
+ e = exp;
+ }
+ else if (s)
+ {
+ //printf("s = %s %s\n", s.kind(), s.toChars());
+ e = symbolToExp(s, exp.loc, sc, true);
+ }
+ else
+ assert(0);
+
+ if (global.params.vcomplex)
+ exp.type.checkComplexTransition(exp.loc, sc);
+
+ result = e;
+ }
+
+ override void visit(ScopeExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("+ScopeExp::semantic(%p '%s')\n", exp, exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ ScopeDsymbol sds2 = exp.sds;
+ TemplateInstance ti = sds2.isTemplateInstance();
+ while (ti)
+ {
+ WithScopeSymbol withsym;
+ if (!ti.findTempDecl(sc, &withsym) || !ti.semanticTiargs(sc))
+ return setError();
+ if (withsym && withsym.withstate.wthis)
+ {
+ Expression e = new VarExp(exp.loc, withsym.withstate.wthis);
+ e = new DotTemplateInstanceExp(exp.loc, e, ti);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (ti.needsTypeInference(sc))
+ {
+ if (TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration())
+ {
+ Dsymbol p = td.toParentLocal();
+ FuncDeclaration fdthis = hasThis(sc);
+ AggregateDeclaration ad = p ? p.isAggregateDeclaration() : null;
+ if (fdthis && ad && fdthis.isMemberLocal() == ad && (td._scope.stc & STC.static_) == 0)
+ {
+ Expression e = new DotTemplateInstanceExp(exp.loc, new ThisExp(exp.loc), ti);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ }
+ else if (OverloadSet os = ti.tempdecl.isOverloadSet())
+ {
+ FuncDeclaration fdthis = hasThis(sc);
+ AggregateDeclaration ad = os.parent.isAggregateDeclaration();
+ if (fdthis && ad && fdthis.isMemberLocal() == ad)
+ {
+ Expression e = new DotTemplateInstanceExp(exp.loc, new ThisExp(exp.loc), ti);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ }
+ // ti is an instance which requires IFTI.
+ exp.sds = ti;
+ exp.type = Type.tvoid;
+ result = exp;
+ return;
+ }
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors)
+ return setError();
+
+ Dsymbol s = ti.toAlias();
+ if (s == ti)
+ {
+ exp.sds = ti;
+ exp.type = Type.tvoid;
+ result = exp;
+ return;
+ }
+ sds2 = s.isScopeDsymbol();
+ if (sds2)
+ {
+ ti = sds2.isTemplateInstance();
+ //printf("+ sds2 = %s, '%s'\n", sds2.kind(), sds2.toChars());
+ continue;
+ }
+
+ if (auto v = s.isVarDeclaration())
+ {
+ if (!v.type)
+ {
+ exp.error("forward reference of %s `%s`", v.kind(), v.toChars());
+ return setError();
+ }
+ if ((v.storage_class & STC.manifest) && v._init)
+ {
+ /* When an instance that will be converted to a constant exists,
+ * the instance representation "foo!tiargs" is treated like a
+ * variable name, and its recursive appearance check (note that
+ * it's equivalent with a recursive instantiation of foo) is done
+ * separately from the circular initialization check for the
+ * eponymous enum variable declaration.
+ *
+ * template foo(T) {
+ * enum bool foo = foo; // recursive definition check (v.inuse)
+ * }
+ * template bar(T) {
+ * enum bool bar = bar!T; // recursive instantiation check (ti.inuse)
+ * }
+ */
+ if (ti.inuse)
+ {
+ exp.error("recursive expansion of %s `%s`", ti.kind(), ti.toPrettyChars());
+ return setError();
+ }
+ v.checkDeprecated(exp.loc, sc);
+ auto e = v.expandInitializer(exp.loc);
+ ti.inuse++;
+ e = e.expressionSemantic(sc);
+ ti.inuse--;
+ result = e;
+ return;
+ }
+ }
+
+ //printf("s = %s, '%s'\n", s.kind(), s.toChars());
+ auto e = symbolToExp(s, exp.loc, sc, true);
+ //printf("-1ScopeExp::semantic()\n");
+ result = e;
+ return;
+ }
+
+ //printf("sds2 = %s, '%s'\n", sds2.kind(), sds2.toChars());
+ //printf("\tparent = '%s'\n", sds2.parent.toChars());
+ sds2.dsymbolSemantic(sc);
+
+ // (Aggregate|Enum)Declaration
+ if (auto t = sds2.getType())
+ {
+ result = (new TypeExp(exp.loc, t)).expressionSemantic(sc);
+ return;
+ }
+
+ if (auto td = sds2.isTemplateDeclaration())
+ {
+ result = (new TemplateExp(exp.loc, td)).expressionSemantic(sc);
+ return;
+ }
+
+ exp.sds = sds2;
+ exp.type = Type.tvoid;
+ //printf("-2ScopeExp::semantic() %s\n", toChars());
+ result = exp;
+ }
+
+ override void visit(NewExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("NewExp::semantic() %s\n", exp.toChars());
+ if (exp.thisexp)
+ printf("\tthisexp = %s\n", exp.thisexp.toChars());
+ printf("\tnewtype: %s\n", exp.newtype.toChars());
+ }
+ if (exp.type) // if semantic() already run
+ {
+ result = exp;
+ return;
+ }
+
+ //for error messages if the argument in [] is not convertible to size_t
+ const originalNewtype = exp.newtype;
+
+ // https://issues.dlang.org/show_bug.cgi?id=11581
+ // With the syntax `new T[edim]` or `thisexp.new T[edim]`,
+ // T should be analyzed first and edim should go into arguments iff it's
+ // not a tuple.
+ Expression edim = null;
+ if (!exp.arguments && exp.newtype.ty == Tsarray)
+ {
+ edim = (cast(TypeSArray)exp.newtype).dim;
+ exp.newtype = (cast(TypeNext)exp.newtype).next;
+ }
+
+ ClassDeclaration cdthis = null;
+ if (exp.thisexp)
+ {
+ exp.thisexp = exp.thisexp.expressionSemantic(sc);
+ if (exp.thisexp.op == TOK.error)
+ return setError();
+
+ cdthis = exp.thisexp.type.isClassHandle();
+ if (!cdthis)
+ {
+ exp.error("`this` for nested class must be a class type, not `%s`", exp.thisexp.type.toChars());
+ return setError();
+ }
+
+ sc = sc.push(cdthis);
+ exp.type = exp.newtype.typeSemantic(exp.loc, sc);
+ sc = sc.pop();
+ }
+ else
+ {
+ exp.type = exp.newtype.typeSemantic(exp.loc, sc);
+ }
+ if (exp.type.ty == Terror)
+ return setError();
+
+ if (edim)
+ {
+ if (exp.type.toBasetype().ty == Ttuple)
+ {
+ // --> new T[edim]
+ exp.type = new TypeSArray(exp.type, edim);
+ exp.type = exp.type.typeSemantic(exp.loc, sc);
+ if (exp.type.ty == Terror)
+ return setError();
+ }
+ else
+ {
+ // --> new T[](edim)
+ exp.arguments = new Expressions();
+ exp.arguments.push(edim);
+ exp.type = exp.type.arrayOf();
+ }
+ }
+
+ exp.newtype = exp.type; // in case type gets cast to something else
+ Type tb = exp.type.toBasetype();
+ //printf("tb: %s, deco = %s\n", tb.toChars(), tb.deco);
+ if (arrayExpressionSemantic(exp.newargs, sc) ||
+ preFunctionParameters(sc, exp.newargs))
+ {
+ return setError();
+ }
+ if (arrayExpressionSemantic(exp.arguments, sc))
+ {
+ return setError();
+ }
+ //https://issues.dlang.org/show_bug.cgi?id=20547
+ //exp.arguments are the "parameters" to [], not to a real function
+ //so the errors that come from preFunctionParameters are misleading
+ if (originalNewtype.ty == Tsarray)
+ {
+ if (preFunctionParameters(sc, exp.arguments, false))
+ {
+ exp.error("cannot create a `%s` with `new`", originalNewtype.toChars());
+ return setError();
+ }
+ }
+ else if (preFunctionParameters(sc, exp.arguments))
+ {
+ return setError();
+ }
+
+ if (exp.thisexp && tb.ty != Tclass)
+ {
+ exp.error("`.new` is only for allocating nested classes, not `%s`", tb.toChars());
+ return setError();
+ }
+
+ const size_t nargs = exp.arguments ? exp.arguments.dim : 0;
+ Expression newprefix = null;
+
+ if (tb.ty == Tclass)
+ {
+ auto cd = (cast(TypeClass)tb).sym;
+ cd.size(exp.loc);
+ if (cd.sizeok != Sizeok.done)
+ return setError();
+ if (!cd.ctor)
+ cd.ctor = cd.searchCtor();
+ if (cd.noDefaultCtor && !nargs && !cd.defaultCtor)
+ {
+ exp.error("default construction is disabled for type `%s`", cd.type.toChars());
+ return setError();
+ }
+
+ if (cd.isInterfaceDeclaration())
+ {
+ exp.error("cannot create instance of interface `%s`", cd.toChars());
+ return setError();
+ }
+
+ if (cd.isAbstract())
+ {
+ exp.error("cannot create instance of abstract class `%s`", cd.toChars());
+ for (size_t i = 0; i < cd.vtbl.dim; i++)
+ {
+ FuncDeclaration fd = cd.vtbl[i].isFuncDeclaration();
+ if (fd && fd.isAbstract())
+ {
+ errorSupplemental(exp.loc, "function `%s` is not implemented",
+ fd.toFullSignature());
+ }
+ }
+ return setError();
+ }
+ // checkDeprecated() is already done in newtype.typeSemantic().
+
+ if (cd.isNested())
+ {
+ /* We need a 'this' pointer for the nested class.
+ * Ensure we have the right one.
+ */
+ Dsymbol s = cd.toParentLocal();
+
+ //printf("cd isNested, parent = %s '%s'\n", s.kind(), s.toPrettyChars());
+ if (auto cdn = s.isClassDeclaration())
+ {
+ if (!cdthis)
+ {
+ // Supply an implicit 'this' and try again
+ exp.thisexp = new ThisExp(exp.loc);
+ for (Dsymbol sp = sc.parent; 1; sp = sp.toParentLocal())
+ {
+ if (!sp)
+ {
+ exp.error("outer class `%s` `this` needed to `new` nested class `%s`",
+ cdn.toChars(), cd.toChars());
+ return setError();
+ }
+ ClassDeclaration cdp = sp.isClassDeclaration();
+ if (!cdp)
+ continue;
+ if (cdp == cdn || cdn.isBaseOf(cdp, null))
+ break;
+ // Add a '.outer' and try again
+ exp.thisexp = new DotIdExp(exp.loc, exp.thisexp, Id.outer);
+ }
+
+ exp.thisexp = exp.thisexp.expressionSemantic(sc);
+ if (exp.thisexp.op == TOK.error)
+ return setError();
+ cdthis = exp.thisexp.type.isClassHandle();
+ }
+ if (cdthis != cdn && !cdn.isBaseOf(cdthis, null))
+ {
+ //printf("cdthis = %s\n", cdthis.toChars());
+ exp.error("`this` for nested class must be of type `%s`, not `%s`",
+ cdn.toChars(), exp.thisexp.type.toChars());
+ return setError();
+ }
+ if (!MODimplicitConv(exp.thisexp.type.mod, exp.newtype.mod))
+ {
+ exp.error("nested type `%s` should have the same or weaker constancy as enclosing type `%s`",
+ exp.newtype.toChars(), exp.thisexp.type.toChars());
+ return setError();
+ }
+ }
+ else if (exp.thisexp)
+ {
+ exp.error("`.new` is only for allocating nested classes");
+ return setError();
+ }
+ else if (auto fdn = s.isFuncDeclaration())
+ {
+ // make sure the parent context fdn of cd is reachable from sc
+ if (!ensureStaticLinkTo(sc.parent, fdn))
+ {
+ exp.error("outer function context of `%s` is needed to `new` nested class `%s`",
+ fdn.toPrettyChars(), cd.toPrettyChars());
+ return setError();
+ }
+ }
+ else
+ assert(0);
+ }
+ else if (exp.thisexp)
+ {
+ exp.error("`.new` is only for allocating nested classes");
+ return setError();
+ }
+
+ if (cd.vthis2)
+ {
+ if (AggregateDeclaration ad2 = cd.isMember2())
+ {
+ Expression te = new ThisExp(exp.loc).expressionSemantic(sc);
+ if (te.op != TOK.error)
+ te = getRightThis(exp.loc, sc, ad2, te, cd);
+ if (te.op == TOK.error)
+ {
+ exp.error("need `this` of type `%s` needed to `new` nested class `%s`", ad2.toChars(), cd.toChars());
+ return setError();
+ }
+ }
+ }
+
+ if (cd.disableNew)
+ {
+ exp.error("cannot allocate `class %s` with `new` because it is annotated with `@disable new()`",
+ originalNewtype.toChars());
+ return setError();
+ }
+ else
+ {
+ if (exp.newargs && exp.newargs.dim)
+ {
+ exp.error("no allocator for `%s`", cd.toChars());
+ return setError();
+ }
+ }
+
+ if (cd.ctor)
+ {
+ FuncDeclaration f = resolveFuncCall(exp.loc, sc, cd.ctor, null, tb, exp.arguments, FuncResolveFlag.standard);
+ if (!f || f.errors)
+ return setError();
+
+ checkFunctionAttributes(exp, sc, f);
+ checkAccess(cd, exp.loc, sc, f);
+
+ TypeFunction tf = cast(TypeFunction)f.type;
+ if (!exp.arguments)
+ exp.arguments = new Expressions();
+ if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.arguments, f, &exp.type, &exp.argprefix))
+ return setError();
+
+ exp.member = f.isCtorDeclaration();
+ assert(exp.member);
+ }
+ else
+ {
+ if (nargs)
+ {
+ exp.error("no constructor for `%s`", cd.toChars());
+ return setError();
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19941
+ // Run semantic on all field initializers to resolve any forward
+ // references. This is the same as done for structs in sd.fill().
+ for (ClassDeclaration c = cd; c; c = c.baseClass)
+ {
+ foreach (v; c.fields)
+ {
+ if (v.inuse || v._scope is null || v._init is null ||
+ v._init.isVoidInitializer())
+ continue;
+ v.inuse++;
+ v._init = v._init.initializerSemantic(v._scope, v.type, INITinterpret);
+ v.inuse--;
+ }
+ }
+ }
+ }
+ else if (tb.ty == Tstruct)
+ {
+ auto sd = (cast(TypeStruct)tb).sym;
+ sd.size(exp.loc);
+ if (sd.sizeok != Sizeok.done)
+ return setError();
+ if (!sd.ctor)
+ sd.ctor = sd.searchCtor();
+ if (sd.noDefaultCtor && !nargs)
+ {
+ exp.error("default construction is disabled for type `%s`", sd.type.toChars());
+ return setError();
+ }
+ // checkDeprecated() is already done in newtype.typeSemantic().
+
+ if (sd.disableNew)
+ {
+ exp.error("cannot allocate `struct %s` with `new` because it is annotated with `@disable new()`",
+ originalNewtype.toChars());
+ return setError();
+ }
+ else
+ {
+ if (exp.newargs && exp.newargs.dim)
+ {
+ exp.error("no allocator for `%s`", sd.toChars());
+ return setError();
+ }
+ }
+
+ if (sd.ctor && nargs)
+ {
+ FuncDeclaration f = resolveFuncCall(exp.loc, sc, sd.ctor, null, tb, exp.arguments, FuncResolveFlag.standard);
+ if (!f || f.errors)
+ return setError();
+
+ checkFunctionAttributes(exp, sc, f);
+ checkAccess(sd, exp.loc, sc, f);
+
+ TypeFunction tf = cast(TypeFunction)f.type;
+ if (!exp.arguments)
+ exp.arguments = new Expressions();
+ if (functionParameters(exp.loc, sc, tf, null, exp.type, exp.arguments, f, &exp.type, &exp.argprefix))
+ return setError();
+
+ exp.member = f.isCtorDeclaration();
+ assert(exp.member);
+
+ if (checkFrameAccess(exp.loc, sc, sd, sd.fields.dim))
+ return setError();
+ }
+ else
+ {
+ if (!exp.arguments)
+ exp.arguments = new Expressions();
+
+ if (!sd.fit(exp.loc, sc, exp.arguments, tb))
+ return setError();
+
+ if (!sd.fill(exp.loc, exp.arguments, false))
+ return setError();
+
+ if (checkFrameAccess(exp.loc, sc, sd, exp.arguments ? exp.arguments.dim : 0))
+ return setError();
+
+ /* Since a `new` allocation may escape, check each of the arguments for escaping
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ foreach (arg; *exp.arguments)
+ {
+ if (arg && checkNewEscape(sc, arg, false))
+ return setError();
+ }
+ }
+ }
+
+ exp.type = exp.type.pointerTo();
+ }
+ else if (tb.ty == Tarray)
+ {
+ if (!nargs)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20422
+ // Without this check the compiler would give a misleading error
+ exp.error("missing length argument for array");
+ return setError();
+ }
+
+ Type tn = tb.nextOf().baseElemOf();
+ Dsymbol s = tn.toDsymbol(sc);
+ AggregateDeclaration ad = s ? s.isAggregateDeclaration() : null;
+ if (ad && ad.noDefaultCtor)
+ {
+ exp.error("default construction is disabled for type `%s`", tb.nextOf().toChars());
+ return setError();
+ }
+ for (size_t i = 0; i < nargs; i++)
+ {
+ if (tb.ty != Tarray)
+ {
+ exp.error("too many arguments for array");
+ return setError();
+ }
+
+ Expression arg = (*exp.arguments)[i];
+ arg = resolveProperties(sc, arg);
+ arg = arg.implicitCastTo(sc, Type.tsize_t);
+ if (arg.op == TOK.error)
+ return setError();
+ arg = arg.optimize(WANTvalue);
+ if (arg.op == TOK.int64 && cast(sinteger_t)arg.toInteger() < 0)
+ {
+ exp.error("negative array index `%s`", arg.toChars());
+ return setError();
+ }
+ (*exp.arguments)[i] = arg;
+ tb = (cast(TypeDArray)tb).next.toBasetype();
+ }
+ }
+ else if (tb.isscalar())
+ {
+ if (!nargs)
+ {
+ }
+ else if (nargs == 1)
+ {
+ Expression e = (*exp.arguments)[0];
+ e = e.implicitCastTo(sc, tb);
+ (*exp.arguments)[0] = e;
+ }
+ else
+ {
+ exp.error("more than one argument for construction of `%s`", exp.type.toChars());
+ return setError();
+ }
+
+ exp.type = exp.type.pointerTo();
+ }
+ else
+ {
+ exp.error("cannot create a `%s` with `new`", exp.type.toChars());
+ return setError();
+ }
+
+ //printf("NewExp: '%s'\n", toChars());
+ //printf("NewExp:type '%s'\n", type.toChars());
+ semanticTypeInfo(sc, exp.type);
+
+ if (newprefix)
+ {
+ result = Expression.combine(newprefix, exp);
+ return;
+ }
+ result = exp;
+ }
+
+ override void visit(NewAnonClassExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("NewAnonClassExp::semantic() %s\n", e.toChars());
+ //printf("thisexp = %p\n", thisexp);
+ //printf("type: %s\n", type.toChars());
+ }
+
+ Expression d = new DeclarationExp(e.loc, e.cd);
+ sc = sc.push(); // just create new scope
+ sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE
+ d = d.expressionSemantic(sc);
+ sc = sc.pop();
+
+ if (!e.cd.errors && sc.intypeof && !sc.parent.inNonRoot())
+ {
+ ScopeDsymbol sds = sc.tinst ? cast(ScopeDsymbol)sc.tinst : sc._module;
+ if (!sds.members)
+ sds.members = new Dsymbols();
+ sds.members.push(e.cd);
+ }
+
+ Expression n = new NewExp(e.loc, e.thisexp, e.newargs, e.cd.type, e.arguments);
+
+ Expression c = new CommaExp(e.loc, d, n);
+ result = c.expressionSemantic(sc);
+ }
+
+ override void visit(SymOffExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("SymOffExp::semantic('%s')\n", e.toChars());
+ }
+ //var.dsymbolSemantic(sc);
+ if (!e.type)
+ e.type = e.var.type.pointerTo();
+
+ if (auto v = e.var.isVarDeclaration())
+ {
+ if (v.checkNestedReference(sc, e.loc))
+ return setError();
+ }
+ else if (auto f = e.var.isFuncDeclaration())
+ {
+ if (f.checkNestedReference(sc, e.loc))
+ return setError();
+ }
+
+ result = e;
+ }
+
+ override void visit(VarExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("VarExp::semantic(%s)\n", e.toChars());
+ }
+
+ auto vd = e.var.isVarDeclaration();
+ auto fd = e.var.isFuncDeclaration();
+
+ if (fd)
+ {
+ //printf("L%d fd = %s\n", __LINE__, f.toChars());
+ if (!fd.functionSemantic())
+ return setError();
+ }
+
+ if (!e.type)
+ e.type = e.var.type;
+ if (e.type && !e.type.deco)
+ {
+ auto decl = e.var.isDeclaration();
+ if (decl)
+ decl.inuse++;
+ e.type = e.type.typeSemantic(e.loc, sc);
+ if (decl)
+ decl.inuse--;
+ }
+
+ /* Fix for 1161 doesn't work because it causes visibility
+ * problems when instantiating imported templates passing private
+ * variables as alias template parameters.
+ */
+ //checkAccess(loc, sc, NULL, var);
+
+ if (vd)
+ {
+ if (vd.checkNestedReference(sc, e.loc))
+ return setError();
+
+ // https://issues.dlang.org/show_bug.cgi?id=12025
+ // If the variable is not actually used in runtime code,
+ // the purity violation error is redundant.
+ //checkPurity(sc, vd);
+ }
+ else if (fd)
+ {
+ // TODO: If fd isn't yet resolved its overload, the checkNestedReference
+ // call would cause incorrect validation.
+ // Maybe here should be moved in CallExp, or AddrExp for functions.
+ if (fd.checkNestedReference(sc, e.loc))
+ return setError();
+ }
+ else if (auto od = e.var.isOverDeclaration())
+ {
+ e.type = Type.tvoid; // ambiguous type?
+ }
+
+ result = e;
+ }
+
+ override void visit(FuncExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("FuncExp::semantic(%s)\n", exp.toChars());
+ if (exp.fd.treq)
+ printf(" treq = %s\n", exp.fd.treq.toChars());
+ }
+
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp;
+ uint olderrors;
+
+ sc = sc.push(); // just create new scope
+ sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE
+ sc.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=12506
+
+ /* fd.treq might be incomplete type,
+ * so should not semantic it.
+ * void foo(T)(T delegate(int) dg){}
+ * foo(a=>a); // in IFTI, treq == T delegate(int)
+ */
+ //if (fd.treq)
+ // fd.treq = fd.treq.dsymbolSemantic(loc, sc);
+
+ exp.genIdent(sc);
+
+ // Set target of return type inference
+ if (exp.fd.treq && !exp.fd.type.nextOf())
+ {
+ TypeFunction tfv = null;
+ if (exp.fd.treq.ty == Tdelegate || exp.fd.treq.isPtrToFunction())
+ tfv = cast(TypeFunction)exp.fd.treq.nextOf();
+ if (tfv)
+ {
+ TypeFunction tfl = cast(TypeFunction)exp.fd.type;
+ tfl.next = tfv.nextOf();
+ }
+ }
+
+ //printf("td = %p, treq = %p\n", td, fd.treq);
+ if (exp.td)
+ {
+ assert(exp.td.parameters && exp.td.parameters.dim);
+ exp.td.dsymbolSemantic(sc);
+ exp.type = Type.tvoid; // temporary type
+
+ if (exp.fd.treq) // defer type determination
+ {
+ FuncExp fe;
+ if (exp.matchType(exp.fd.treq, sc, &fe) > MATCH.nomatch)
+ e = fe;
+ else
+ e = ErrorExp.get();
+ }
+ goto Ldone;
+ }
+
+ olderrors = global.errors;
+ exp.fd.dsymbolSemantic(sc);
+ if (olderrors == global.errors)
+ {
+ exp.fd.semantic2(sc);
+ if (olderrors == global.errors)
+ exp.fd.semantic3(sc);
+ }
+ if (olderrors != global.errors)
+ {
+ if (exp.fd.type && exp.fd.type.ty == Tfunction && !exp.fd.type.nextOf())
+ (cast(TypeFunction)exp.fd.type).next = Type.terror;
+ e = ErrorExp.get();
+ goto Ldone;
+ }
+
+ // Type is a "delegate to" or "pointer to" the function literal
+ if ((exp.fd.isNested() && exp.fd.tok == TOK.delegate_) || (exp.tok == TOK.reserved && exp.fd.treq && exp.fd.treq.ty == Tdelegate))
+ {
+ exp.type = new TypeDelegate(exp.fd.type.isTypeFunction());
+ exp.type = exp.type.typeSemantic(exp.loc, sc);
+
+ exp.fd.tok = TOK.delegate_;
+ }
+ else
+ {
+ exp.type = new TypePointer(exp.fd.type);
+ exp.type = exp.type.typeSemantic(exp.loc, sc);
+ //type = fd.type.pointerTo();
+
+ /* A lambda expression deduced to function pointer might become
+ * to a delegate literal implicitly.
+ *
+ * auto foo(void function() fp) { return 1; }
+ * assert(foo({}) == 1);
+ *
+ * So, should keep fd.tok == TOKreserve if fd.treq == NULL.
+ */
+ if (exp.fd.treq && exp.fd.treq.ty == Tpointer)
+ {
+ // change to non-nested
+ exp.fd.tok = TOK.function_;
+ exp.fd.vthis = null;
+ }
+ }
+ exp.fd.tookAddressOf++;
+
+ Ldone:
+ sc = sc.pop();
+ result = e;
+ }
+
+ /**
+ * Perform semantic analysis on function literals
+ *
+ * Test the following construct:
+ * ---
+ * (x, y, z) { return x + y + z; }(42, 84, 1992);
+ * ---
+ */
+ Expression callExpSemantic(FuncExp exp, Scope* sc, Expressions* arguments)
+ {
+ if ((!exp.type || exp.type == Type.tvoid) && exp.td && arguments && arguments.dim)
+ {
+ for (size_t k = 0; k < arguments.dim; k++)
+ {
+ Expression checkarg = (*arguments)[k];
+ if (checkarg.op == TOK.error)
+ return checkarg;
+ }
+
+ exp.genIdent(sc);
+
+ assert(exp.td.parameters && exp.td.parameters.dim);
+ exp.td.dsymbolSemantic(sc);
+
+ TypeFunction tfl = cast(TypeFunction)exp.fd.type;
+ size_t dim = tfl.parameterList.length;
+ if (arguments.dim < dim)
+ {
+ // Default arguments are always typed, so they don't need inference.
+ Parameter p = tfl.parameterList[arguments.dim];
+ if (p.defaultArg)
+ dim = arguments.dim;
+ }
+
+ if ((tfl.parameterList.varargs == VarArg.none && arguments.dim > dim) ||
+ arguments.dim < dim)
+ {
+ OutBuffer buf;
+ foreach (idx, ref arg; *arguments)
+ buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars());
+ exp.error("function literal `%s%s` is not callable using argument types `(%s)`",
+ exp.fd.toChars(), parametersTypeToChars(tfl.parameterList),
+ buf.peekChars());
+ exp.errorSupplemental("too %s arguments, expected `%d`, got `%d`",
+ arguments.dim < dim ? "few".ptr : "many".ptr,
+ cast(int)dim, cast(int)arguments.dim);
+ return ErrorExp.get();
+ }
+
+ auto tiargs = new Objects();
+ tiargs.reserve(exp.td.parameters.dim);
+
+ for (size_t i = 0; i < exp.td.parameters.dim; i++)
+ {
+ TemplateParameter tp = (*exp.td.parameters)[i];
+ assert(dim <= tfl.parameterList.length);
+ foreach (u, p; tfl.parameterList)
+ {
+ if (u == dim)
+ break;
+
+ if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
+ {
+ Expression e = (*arguments)[u];
+ tiargs.push(e.type);
+ break;
+ }
+ }
+ }
+
+ auto ti = new TemplateInstance(exp.loc, exp.td, tiargs);
+ return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc);
+ }
+ return exp.expressionSemantic(sc);
+ }
+
+ override void visit(CallExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CallExp::semantic() %s\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return; // semantic() already run
+ }
+
+ Objects* tiargs = null; // initial list of template arguments
+ Expression ethis = null;
+ Type tthis = null;
+ Expression e1org = exp.e1;
+
+ if (exp.e1.op == TOK.comma)
+ {
+ /* Rewrite (a,b)(args) as (a,(b(args)))
+ */
+ auto ce = cast(CommaExp)exp.e1;
+ exp.e1 = ce.e2;
+ ce.e2 = exp;
+ result = ce.expressionSemantic(sc);
+ return;
+ }
+ if (exp.e1.op == TOK.delegate_)
+ {
+ DelegateExp de = cast(DelegateExp)exp.e1;
+ exp.e1 = new DotVarExp(de.loc, de.e1, de.func, de.hasOverloads);
+ visit(exp);
+ return;
+ }
+ if (exp.e1.op == TOK.function_)
+ {
+ if (arrayExpressionSemantic(exp.arguments, sc) || preFunctionParameters(sc, exp.arguments))
+ return setError();
+
+ // Run e1 semantic even if arguments have any errors
+ FuncExp fe = cast(FuncExp)exp.e1;
+ exp.e1 = callExpSemantic(fe, sc, exp.arguments);
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ }
+
+ if (Expression ex = resolveUFCS(sc, exp))
+ {
+ result = ex;
+ return;
+ }
+
+ /* This recognizes:
+ * foo!(tiargs)(funcargs)
+ */
+ if (exp.e1.op == TOK.scope_)
+ {
+ ScopeExp se = cast(ScopeExp)exp.e1;
+ TemplateInstance ti = se.sds.isTemplateInstance();
+ if (ti)
+ {
+ /* Attempt to instantiate ti. If that works, go with it.
+ * If not, go with partial explicit specialization.
+ */
+ WithScopeSymbol withsym;
+ if (!ti.findTempDecl(sc, &withsym) || !ti.semanticTiargs(sc))
+ return setError();
+ if (withsym && withsym.withstate.wthis)
+ {
+ exp.e1 = new VarExp(exp.e1.loc, withsym.withstate.wthis);
+ exp.e1 = new DotTemplateInstanceExp(exp.e1.loc, exp.e1, ti);
+ goto Ldotti;
+ }
+ if (ti.needsTypeInference(sc, 1))
+ {
+ /* Go with partial explicit specialization
+ */
+ tiargs = ti.tiargs;
+ assert(ti.tempdecl);
+ if (TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration())
+ exp.e1 = new TemplateExp(exp.loc, td);
+ else if (OverDeclaration od = ti.tempdecl.isOverDeclaration())
+ exp.e1 = new VarExp(exp.loc, od);
+ else
+ exp.e1 = new OverExp(exp.loc, ti.tempdecl.isOverloadSet());
+ }
+ else
+ {
+ Expression e1x = exp.e1.expressionSemantic(sc);
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ exp.e1 = e1x;
+ }
+ }
+ }
+
+ /* This recognizes:
+ * expr.foo!(tiargs)(funcargs)
+ */
+ Ldotti:
+ if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type)
+ {
+ DotTemplateInstanceExp se = cast(DotTemplateInstanceExp)exp.e1;
+ TemplateInstance ti = se.ti;
+ {
+ /* Attempt to instantiate ti. If that works, go with it.
+ * If not, go with partial explicit specialization.
+ */
+ if (!se.findTempDecl(sc) || !ti.semanticTiargs(sc))
+ return setError();
+ if (ti.needsTypeInference(sc, 1))
+ {
+ /* Go with partial explicit specialization
+ */
+ tiargs = ti.tiargs;
+ assert(ti.tempdecl);
+ if (TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration())
+ exp.e1 = new DotTemplateExp(exp.loc, se.e1, td);
+ else if (OverDeclaration od = ti.tempdecl.isOverDeclaration())
+ {
+ exp.e1 = new DotVarExp(exp.loc, se.e1, od, true);
+ }
+ else
+ exp.e1 = new DotExp(exp.loc, se.e1, new OverExp(exp.loc, ti.tempdecl.isOverloadSet()));
+ }
+ else
+ {
+ Expression e1x = exp.e1.expressionSemantic(sc);
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ exp.e1 = e1x;
+ }
+ }
+ }
+
+ Lagain:
+ //printf("Lagain: %s\n", toChars());
+ exp.f = null;
+ if (exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_)
+ {
+ // semantic() run later for these
+ }
+ else
+ {
+ if (exp.e1.op == TOK.dotIdentifier)
+ {
+ DotIdExp die = cast(DotIdExp)exp.e1;
+ exp.e1 = die.expressionSemantic(sc);
+ /* Look for e1 having been rewritten to expr.opDispatch!(string)
+ * We handle such earlier, so go back.
+ * Note that in the rewrite, we carefully did not run semantic() on e1
+ */
+ if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type)
+ {
+ goto Ldotti;
+ }
+ }
+ else
+ {
+ __gshared int nest;
+ if (++nest > global.recursionLimit)
+ {
+ exp.error("recursive evaluation of `%s`", exp.toChars());
+ --nest;
+ return setError();
+ }
+ Expression ex = unaSemantic(exp, sc);
+ --nest;
+ if (ex)
+ {
+ result = ex;
+ return;
+ }
+ }
+
+ /* Look for e1 being a lazy parameter
+ */
+ if (exp.e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)exp.e1;
+ if (ve.var.storage_class & STC.lazy_)
+ {
+ // lazy parameters can be called without violating purity and safety
+ Type tw = ve.var.type;
+ Type tc = ve.var.type.substWildTo(MODFlags.const_);
+ auto tf = new TypeFunction(ParameterList(), tc, LINK.d, STC.safe | STC.pure_);
+ (tf = cast(TypeFunction)tf.typeSemantic(exp.loc, sc)).next = tw; // hack for bug7757
+ auto t = new TypeDelegate(tf);
+ ve.type = t.typeSemantic(exp.loc, sc);
+ }
+ VarDeclaration v = ve.var.isVarDeclaration();
+ if (v && ve.checkPurity(sc, v))
+ return setError();
+ }
+
+ if (exp.e1.op == TOK.symbolOffset && (cast(SymOffExp)exp.e1).hasOverloads)
+ {
+ SymOffExp se = cast(SymOffExp)exp.e1;
+ exp.e1 = new VarExp(se.loc, se.var, true);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ }
+ else if (exp.e1.op == TOK.dot)
+ {
+ DotExp de = cast(DotExp)exp.e1;
+
+ if (de.e2.op == TOK.overloadSet)
+ {
+ ethis = de.e1;
+ tthis = de.e1.type;
+ exp.e1 = de.e2;
+ }
+ }
+ else if (exp.e1.op == TOK.star && exp.e1.type.ty == Tfunction)
+ {
+ // Rewrite (*fp)(arguments) to fp(arguments)
+ exp.e1 = (cast(PtrExp)exp.e1).e1;
+ }
+ else if (exp.e1.op == TOK.type && (sc && sc.flags & SCOPE.Cfile))
+ {
+ /* Ambiguous cases arise from CParser where there is not enough
+ * information to determine if we have a function call or declaration.
+ * type-name ( identifier ) ;
+ * identifier ( identifier ) ;
+ * If exp.e1 is a type-name, then this is a declaration. C11 does not
+ * have type construction syntax, so don't convert this to a cast().
+ */
+ if (exp.arguments && exp.arguments.dim == 1)
+ {
+ Expression arg = (*exp.arguments)[0];
+ if (auto ie = (*exp.arguments)[0].isIdentifierExp())
+ {
+ TypeExp te = cast(TypeExp)exp.e1;
+ auto initializer = new VoidInitializer(ie.loc);
+ Dsymbol s = new VarDeclaration(ie.loc, te.type, ie.ident, initializer);
+ auto decls = new Dsymbols(1);
+ (*decls)[0] = s;
+ s = new LinkDeclaration(s.loc, LINK.c, decls);
+ result = new DeclarationExp(exp.loc, s);
+ result = result.expressionSemantic(sc);
+ }
+ else
+ {
+ arg.error("identifier or `(` expected");
+ result = ErrorExp.get();
+ }
+ return;
+ }
+ exp.error("identifier or `(` expected before `)`");
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ Type t1 = exp.e1.type ? exp.e1.type.toBasetype() : null;
+
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (arrayExpressionSemantic(exp.arguments, sc) || preFunctionParameters(sc, exp.arguments))
+ return setError();
+
+ // Check for call operator overload
+ if (t1)
+ {
+ if (t1.ty == Tstruct)
+ {
+ auto sd = (cast(TypeStruct)t1).sym;
+ sd.size(exp.loc); // Resolve forward references to construct object
+ if (sd.sizeok != Sizeok.done)
+ return setError();
+ if (!sd.ctor)
+ sd.ctor = sd.searchCtor();
+ /* If `sd.ctor` is a generated copy constructor, this means that it
+ is the single constructor that this struct has. In order to not
+ disable default construction, the ctor is nullified. The side effect
+ of this is that the generated copy constructor cannot be called
+ explicitly, but that is ok, because when calling a constructor the
+ default constructor should have priority over the generated copy
+ constructor.
+ */
+ if (sd.ctor)
+ {
+ auto ctor = sd.ctor.isCtorDeclaration();
+ if (ctor && ctor.isCpCtor && ctor.generated)
+ sd.ctor = null;
+ }
+
+ // First look for constructor
+ if (exp.e1.op == TOK.type && sd.ctor)
+ {
+ if (!sd.noDefaultCtor && !(exp.arguments && exp.arguments.dim))
+ goto Lx;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=20695
+ If all constructors are copy constructors, then
+ try default construction.
+ */
+ if (!sd.hasRegularCtor)
+ goto Lx;
+
+ auto sle = new StructLiteralExp(exp.loc, sd, null, exp.e1.type);
+ if (!sd.fill(exp.loc, sle.elements, true))
+ return setError();
+ if (checkFrameAccess(exp.loc, sc, sd, sle.elements.dim))
+ return setError();
+
+ // https://issues.dlang.org/show_bug.cgi?id=14556
+ // Set concrete type to avoid further redundant semantic().
+ sle.type = exp.e1.type;
+
+ /* Constructor takes a mutable object, so don't use
+ * the immutable initializer symbol.
+ */
+ sle.useStaticInit = false;
+
+ Expression e = sle;
+ if (auto cf = sd.ctor.isCtorDeclaration())
+ {
+ e = new DotVarExp(exp.loc, e, cf, true);
+ }
+ else if (auto td = sd.ctor.isTemplateDeclaration())
+ {
+ e = new DotIdExp(exp.loc, e, td.ident);
+ }
+ else if (auto os = sd.ctor.isOverloadSet())
+ {
+ e = new DotExp(exp.loc, e, new OverExp(exp.loc, os));
+ }
+ else
+ assert(0);
+ e = new CallExp(exp.loc, e, exp.arguments);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ // No constructor, look for overload of opCall
+ if (search_function(sd, Id.call))
+ goto L1;
+ // overload of opCall, therefore it's a call
+ if (exp.e1.op != TOK.type)
+ {
+ if (sd.aliasthis && !isRecursiveAliasThis(exp.att1, exp.e1.type))
+ {
+ exp.e1 = resolveAliasThis(sc, exp.e1);
+ goto Lagain;
+ }
+ exp.error("%s `%s` does not overload ()", sd.kind(), sd.toChars());
+ return setError();
+ }
+
+ /* It's a struct literal
+ */
+ Lx:
+ Expression e = new StructLiteralExp(exp.loc, sd, exp.arguments, exp.e1.type);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ else if (t1.ty == Tclass)
+ {
+ L1:
+ // Rewrite as e1.call(arguments)
+ Expression e = new DotIdExp(exp.loc, exp.e1, Id.call);
+ e = new CallExp(exp.loc, e, exp.arguments);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ else if (exp.e1.op == TOK.type && t1.isscalar())
+ {
+ Expression e;
+
+ // Make sure to use the the enum type itself rather than its
+ // base type
+ // https://issues.dlang.org/show_bug.cgi?id=16346
+ if (exp.e1.type.ty == Tenum)
+ {
+ t1 = exp.e1.type;
+ }
+
+ if (!exp.arguments || exp.arguments.dim == 0)
+ {
+ e = t1.defaultInitLiteral(exp.loc);
+ }
+ else if (exp.arguments.dim == 1)
+ {
+ e = (*exp.arguments)[0];
+ e = e.implicitCastTo(sc, t1);
+ e = new CastExp(exp.loc, e, t1);
+ }
+ else
+ {
+ exp.error("more than one argument for construction of `%s`", t1.toChars());
+ return setError();
+ }
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+
+ static FuncDeclaration resolveOverloadSet(Loc loc, Scope* sc,
+ OverloadSet os, Objects* tiargs, Type tthis, Expressions* arguments)
+ {
+ FuncDeclaration f = null;
+ foreach (s; os.a)
+ {
+ if (tiargs && s.isFuncDeclaration())
+ continue;
+ if (auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, arguments, FuncResolveFlag.quiet))
+ {
+ if (f2.errors)
+ return null;
+ if (f)
+ {
+ /* Error if match in more than one overload set,
+ * even if one is a 'better' match than the other.
+ */
+ ScopeDsymbol.multiplyDefined(loc, f, f2);
+ }
+ else
+ f = f2;
+ }
+ }
+ if (!f)
+ .error(loc, "no overload matches for `%s`", os.toChars());
+ else if (f.errors)
+ f = null;
+ return f;
+ }
+
+ bool isSuper = false;
+ if (exp.e1.op == TOK.dotVariable && t1.ty == Tfunction || exp.e1.op == TOK.dotTemplateDeclaration)
+ {
+ UnaExp ue = cast(UnaExp)exp.e1;
+
+ Expression ue1 = ue.e1;
+ Expression ue1old = ue1; // need for 'right this' check
+ VarDeclaration v;
+ if (ue1.op == TOK.variable && (v = (cast(VarExp)ue1).var.isVarDeclaration()) !is null && v.needThis())
+ {
+ ue.e1 = new TypeExp(ue1.loc, ue1.type);
+ ue1 = null;
+ }
+
+ DotVarExp dve;
+ DotTemplateExp dte;
+ Dsymbol s;
+ if (exp.e1.op == TOK.dotVariable)
+ {
+ dve = cast(DotVarExp)exp.e1;
+ dte = null;
+ s = dve.var;
+ tiargs = null;
+ }
+ else
+ {
+ dve = null;
+ dte = cast(DotTemplateExp)exp.e1;
+ s = dte.td;
+ }
+
+ // Do overload resolution
+ exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, ue1 ? ue1.type : null, exp.arguments, FuncResolveFlag.standard);
+ if (!exp.f || exp.f.errors || exp.f.type.ty == Terror)
+ return setError();
+
+ if (exp.f.interfaceVirtual)
+ {
+ /* Cast 'this' to the type of the interface, and replace f with the interface's equivalent
+ */
+ auto b = exp.f.interfaceVirtual;
+ auto ad2 = b.sym;
+ ue.e1 = ue.e1.castTo(sc, ad2.type.addMod(ue.e1.type.mod));
+ ue.e1 = ue.e1.expressionSemantic(sc);
+ ue1 = ue.e1;
+ auto vi = exp.f.findVtblIndex(&ad2.vtbl, cast(int)ad2.vtbl.dim);
+ assert(vi >= 0);
+ exp.f = ad2.vtbl[vi].isFuncDeclaration();
+ assert(exp.f);
+ }
+ if (exp.f.needThis())
+ {
+ AggregateDeclaration ad = exp.f.toParentLocal().isAggregateDeclaration();
+ ue.e1 = getRightThis(exp.loc, sc, ad, ue.e1, exp.f);
+ if (ue.e1.op == TOK.error)
+ {
+ result = ue.e1;
+ return;
+ }
+ ethis = ue.e1;
+ tthis = ue.e1.type;
+ if (!(exp.f.type.ty == Tfunction && (cast(TypeFunction)exp.f.type).isScopeQual))
+ {
+ if (global.params.useDIP1000 == FeatureState.enabled && checkParamArgumentEscape(sc, exp.f, null, ethis, false, false))
+ return setError();
+ }
+ }
+
+ /* Cannot call public functions from inside invariant
+ * (because then the invariant would have infinite recursion)
+ */
+ if (sc.func && sc.func.isInvariantDeclaration() && ue.e1.op == TOK.this_ && exp.f.addPostInvariant())
+ {
+ exp.error("cannot call `public`/`export` function `%s` from invariant", exp.f.toChars());
+ return setError();
+ }
+
+ if (!exp.ignoreAttributes)
+ checkFunctionAttributes(exp, sc, exp.f);
+ checkAccess(exp.loc, sc, ue.e1, exp.f);
+ if (!exp.f.needThis())
+ {
+ exp.e1 = Expression.combine(ue.e1, new VarExp(exp.loc, exp.f, false));
+ }
+ else
+ {
+ if (ue1old.checkRightThis(sc))
+ return setError();
+ if (exp.e1.op == TOK.dotVariable)
+ {
+ dve.var = exp.f;
+ exp.e1.type = exp.f.type;
+ }
+ else
+ {
+ exp.e1 = new DotVarExp(exp.loc, dte.e1, exp.f, false);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ if (exp.e1.op == TOK.error)
+ return setError();
+ ue = cast(UnaExp)exp.e1;
+ }
+ version (none)
+ {
+ printf("ue.e1 = %s\n", ue.e1.toChars());
+ printf("f = %s\n", exp.f.toChars());
+ printf("t1 = %s\n", t1.toChars());
+ printf("e1 = %s\n", exp.e1.toChars());
+ printf("e1.type = %s\n", exp.e1.type.toChars());
+ }
+
+ // See if we need to adjust the 'this' pointer
+ AggregateDeclaration ad = exp.f.isThis();
+ ClassDeclaration cd = ue.e1.type.isClassHandle();
+ if (ad && cd && ad.isClassDeclaration())
+ {
+ if (ue.e1.op == TOK.dotType)
+ {
+ ue.e1 = (cast(DotTypeExp)ue.e1).e1;
+ exp.directcall = true;
+ }
+ else if (ue.e1.op == TOK.super_)
+ exp.directcall = true;
+ else if ((cd.storage_class & STC.final_) != 0) // https://issues.dlang.org/show_bug.cgi?id=14211
+ exp.directcall = true;
+
+ if (ad != cd)
+ {
+ ue.e1 = ue.e1.castTo(sc, ad.type.addMod(ue.e1.type.mod));
+ ue.e1 = ue.e1.expressionSemantic(sc);
+ }
+ }
+ }
+ // If we've got a pointer to a function then deference it
+ // https://issues.dlang.org/show_bug.cgi?id=16483
+ if (exp.e1.type.isPtrToFunction())
+ {
+ Expression e = new PtrExp(exp.loc, exp.e1);
+ e.type = exp.e1.type.nextOf();
+ exp.e1 = e;
+ }
+ t1 = exp.e1.type;
+ }
+ else if (exp.e1.op == TOK.super_ || exp.e1.op == TOK.this_)
+ {
+ auto ad = sc.func ? sc.func.isThis() : null;
+ auto cd = ad ? ad.isClassDeclaration() : null;
+
+ isSuper = exp.e1.op == TOK.super_;
+ if (isSuper)
+ {
+ // Base class constructor call
+ if (!cd || !cd.baseClass || !sc.func.isCtorDeclaration())
+ {
+ exp.error("super class constructor call must be in a constructor");
+ return setError();
+ }
+ if (!cd.baseClass.ctor)
+ {
+ exp.error("no super class constructor for `%s`", cd.baseClass.toChars());
+ return setError();
+ }
+ }
+ else
+ {
+ // `this` call expression must be inside a
+ // constructor
+ if (!ad || !sc.func.isCtorDeclaration())
+ {
+ exp.error("constructor call must be in a constructor");
+ return setError();
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=18719
+ // If `exp` is a call expression to another constructor
+ // then it means that all struct/class fields will be
+ // initialized after this call.
+ foreach (ref field; sc.ctorflow.fieldinit)
+ {
+ field.csx |= CSX.this_ctor;
+ }
+ }
+
+ if (!sc.intypeof && !(sc.ctorflow.callSuper & CSX.halt))
+ {
+ if (sc.inLoop || sc.ctorflow.callSuper & CSX.label)
+ exp.error("constructor calls not allowed in loops or after labels");
+ if (sc.ctorflow.callSuper & (CSX.super_ctor | CSX.this_ctor))
+ exp.error("multiple constructor calls");
+ if ((sc.ctorflow.callSuper & CSX.return_) && !(sc.ctorflow.callSuper & CSX.any_ctor))
+ exp.error("an earlier `return` statement skips constructor");
+ sc.ctorflow.callSuper |= CSX.any_ctor | (isSuper ? CSX.super_ctor : CSX.this_ctor);
+ }
+
+ tthis = ad.type.addMod(sc.func.type.mod);
+ auto ctor = isSuper ? cd.baseClass.ctor : ad.ctor;
+ if (auto os = ctor.isOverloadSet())
+ exp.f = resolveOverloadSet(exp.loc, sc, os, null, tthis, exp.arguments);
+ else
+ exp.f = resolveFuncCall(exp.loc, sc, ctor, null, tthis, exp.arguments, FuncResolveFlag.standard);
+
+ if (!exp.f || exp.f.errors)
+ return setError();
+
+ checkFunctionAttributes(exp, sc, exp.f);
+ checkAccess(exp.loc, sc, null, exp.f);
+
+ exp.e1 = new DotVarExp(exp.e1.loc, exp.e1, exp.f, false);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ // https://issues.dlang.org/show_bug.cgi?id=21095
+ if (exp.e1.op == TOK.error)
+ return setError();
+ t1 = exp.e1.type;
+
+ // BUG: this should really be done by checking the static
+ // call graph
+ if (exp.f == sc.func)
+ {
+ exp.error("cyclic constructor call");
+ return setError();
+ }
+ }
+ else if (exp.e1.op == TOK.overloadSet)
+ {
+ auto os = (cast(OverExp)exp.e1).vars;
+ exp.f = resolveOverloadSet(exp.loc, sc, os, tiargs, tthis, exp.arguments);
+ if (!exp.f)
+ return setError();
+ if (ethis)
+ exp.e1 = new DotVarExp(exp.loc, ethis, exp.f, false);
+ else
+ exp.e1 = new VarExp(exp.loc, exp.f, false);
+ goto Lagain;
+ }
+ else if (!t1)
+ {
+ exp.error("function expected before `()`, not `%s`", exp.e1.toChars());
+ return setError();
+ }
+ else if (t1.ty == Terror)
+ {
+ return setError();
+ }
+ else if (t1.ty != Tfunction)
+ {
+ TypeFunction tf;
+ const(char)* p;
+ Dsymbol s;
+ exp.f = null;
+ if (exp.e1.op == TOK.function_)
+ {
+ // function literal that direct called is always inferred.
+ assert((cast(FuncExp)exp.e1).fd);
+ exp.f = (cast(FuncExp)exp.e1).fd;
+ tf = cast(TypeFunction)exp.f.type;
+ p = "function literal";
+ }
+ else if (t1.ty == Tdelegate)
+ {
+ TypeDelegate td = cast(TypeDelegate)t1;
+ assert(td.next.ty == Tfunction);
+ tf = cast(TypeFunction)td.next;
+ p = "delegate";
+ }
+ else if (auto tfx = t1.isPtrToFunction())
+ {
+ tf = tfx;
+ p = "function pointer";
+ }
+ else if (exp.e1.op == TOK.dotVariable && (cast(DotVarExp)exp.e1).var.isOverDeclaration())
+ {
+ DotVarExp dve = cast(DotVarExp)exp.e1;
+ exp.f = resolveFuncCall(exp.loc, sc, dve.var, tiargs, dve.e1.type, exp.arguments, FuncResolveFlag.overloadOnly);
+ if (!exp.f)
+ return setError();
+ if (exp.f.needThis())
+ {
+ dve.var = exp.f;
+ dve.type = exp.f.type;
+ dve.hasOverloads = false;
+ goto Lagain;
+ }
+ exp.e1 = new VarExp(dve.loc, exp.f, false);
+ Expression e = new CommaExp(exp.loc, dve.e1, exp);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ else if (exp.e1.op == TOK.variable && (cast(VarExp)exp.e1).var.isOverDeclaration())
+ {
+ s = (cast(VarExp)exp.e1).var;
+ goto L2;
+ }
+ else if (exp.e1.op == TOK.template_)
+ {
+ s = (cast(TemplateExp)exp.e1).td;
+ L2:
+ exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, null, exp.arguments, FuncResolveFlag.standard);
+ if (!exp.f || exp.f.errors)
+ return setError();
+ if (exp.f.needThis())
+ {
+ if (hasThis(sc))
+ {
+ // Supply an implicit 'this', as in
+ // this.ident
+ exp.e1 = new DotVarExp(exp.loc, (new ThisExp(exp.loc)).expressionSemantic(sc), exp.f, false);
+ goto Lagain;
+ }
+ else if (isNeedThisScope(sc, exp.f))
+ {
+ exp.error("need `this` for `%s` of type `%s`", exp.f.toChars(), exp.f.type.toChars());
+ return setError();
+ }
+ }
+ exp.e1 = new VarExp(exp.e1.loc, exp.f, false);
+ goto Lagain;
+ }
+ else
+ {
+ exp.error("function expected before `()`, not `%s` of type `%s`", exp.e1.toChars(), exp.e1.type.toChars());
+ return setError();
+ }
+
+ const(char)* failMessage;
+ Expression[] fargs = exp.arguments ? (*exp.arguments)[] : null;
+ if (!tf.callMatch(null, fargs, 0, &failMessage, sc))
+ {
+ OutBuffer buf;
+ buf.writeByte('(');
+ argExpTypesToCBuffer(&buf, exp.arguments);
+ buf.writeByte(')');
+ if (tthis)
+ tthis.modToBuffer(&buf);
+
+ //printf("tf = %s, args = %s\n", tf.deco, (*arguments)[0].type.deco);
+ .error(exp.loc, "%s `%s%s` is not callable using argument types `%s`",
+ p, exp.e1.toChars(), parametersTypeToChars(tf.parameterList), buf.peekChars());
+ if (failMessage)
+ errorSupplemental(exp.loc, "%s", failMessage);
+ return setError();
+ }
+ // Purity and safety check should run after testing arguments matching
+ if (exp.f)
+ {
+ exp.checkPurity(sc, exp.f);
+ exp.checkSafety(sc, exp.f);
+ exp.checkNogc(sc, exp.f);
+ if (exp.f.checkNestedReference(sc, exp.loc))
+ return setError();
+ }
+ else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_)))
+ {
+ bool err = false;
+ if (!tf.purity && sc.func.setImpure())
+ {
+ exp.error("`pure` %s `%s` cannot call impure %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars());
+ err = true;
+ }
+ if (!tf.isnogc && sc.func.setGC())
+ {
+ exp.error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars());
+ err = true;
+ }
+ if (tf.trust <= TRUST.system && sc.func.setUnsafe())
+ {
+ exp.error("`@safe` %s `%s` cannot call `@system` %s `%s`",
+ sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars());
+ err = true;
+ }
+ if (err)
+ return setError();
+ }
+
+ if (t1.ty == Tpointer)
+ {
+ Expression e = new PtrExp(exp.loc, exp.e1);
+ e.type = tf;
+ exp.e1 = e;
+ }
+ t1 = tf;
+ }
+ else if (exp.e1.op == TOK.variable)
+ {
+ // Do overload resolution
+ VarExp ve = cast(VarExp)exp.e1;
+
+ exp.f = ve.var.isFuncDeclaration();
+ assert(exp.f);
+ tiargs = null;
+
+ if (exp.f.overnext)
+ exp.f = resolveFuncCall(exp.loc, sc, exp.f, tiargs, null, exp.arguments, FuncResolveFlag.overloadOnly);
+ else
+ {
+ exp.f = exp.f.toAliasFunc();
+ TypeFunction tf = cast(TypeFunction)exp.f.type;
+ const(char)* failMessage;
+ Expression[] fargs = exp.arguments ? (*exp.arguments)[] : null;
+ if (!tf.callMatch(null, fargs, 0, &failMessage, sc))
+ {
+ OutBuffer buf;
+ buf.writeByte('(');
+ argExpTypesToCBuffer(&buf, exp.arguments);
+ buf.writeByte(')');
+
+ //printf("tf = %s, args = %s\n", tf.deco, (*arguments)[0].type.deco);
+ .error(exp.loc, "%s `%s%s` is not callable using argument types `%s`",
+ exp.f.kind(), exp.f.toPrettyChars(), parametersTypeToChars(tf.parameterList), buf.peekChars());
+ if (failMessage)
+ errorSupplemental(exp.loc, "%s", failMessage);
+ exp.f = null;
+ }
+ }
+ if (!exp.f || exp.f.errors)
+ return setError();
+
+ if (exp.f.needThis())
+ {
+ // Change the ancestor lambdas to delegate before hasThis(sc) call.
+ if (exp.f.checkNestedReference(sc, exp.loc))
+ return setError();
+
+ if (hasThis(sc))
+ {
+ // Supply an implicit 'this', as in
+ // this.ident
+ exp.e1 = new DotVarExp(exp.loc, (new ThisExp(exp.loc)).expressionSemantic(sc), ve.var);
+ // Note: we cannot use f directly, because further overload resolution
+ // through the supplied 'this' may cause different result.
+ goto Lagain;
+ }
+ else if (isNeedThisScope(sc, exp.f))
+ {
+ // At this point it is possible that `exp.f` had an ambiguity error that was
+ // silenced because the previous call to `resolveFuncCall` was done using
+ // `FuncResolveFlag.overloadOnly`. To make sure that a proper error message
+ // is printed, redo the call with `FuncResolveFlag.standard`.
+ //
+ // https://issues.dlang.org/show_bug.cgi?id=22157
+ if (exp.f.overnext)
+ exp.f = resolveFuncCall(exp.loc, sc, exp.f, tiargs, null, exp.arguments, FuncResolveFlag.standard);
+
+ if (!exp.f || exp.f.errors)
+ return setError();
+
+ // If no error is printed, it means that `f` is the single matching overload
+ // and it needs `this`.
+ exp.error("need `this` for `%s` of type `%s`", exp.f.toChars(), exp.f.type.toChars());
+ return setError();
+ }
+ }
+
+ checkFunctionAttributes(exp, sc, exp.f);
+ checkAccess(exp.loc, sc, null, exp.f);
+ if (exp.f.checkNestedReference(sc, exp.loc))
+ return setError();
+
+ ethis = null;
+ tthis = null;
+
+ if (ve.hasOverloads)
+ {
+ exp.e1 = new VarExp(ve.loc, exp.f, false);
+ exp.e1.type = exp.f.type;
+ }
+ t1 = exp.f.type;
+ }
+ assert(t1.ty == Tfunction);
+
+ Expression argprefix;
+ if (!exp.arguments)
+ exp.arguments = new Expressions();
+ if (functionParameters(exp.loc, sc, cast(TypeFunction)t1, ethis, tthis, exp.arguments, exp.f, &exp.type, &argprefix))
+ return setError();
+
+ if (!exp.type)
+ {
+ exp.e1 = e1org; // https://issues.dlang.org/show_bug.cgi?id=10922
+ // avoid recursive expression printing
+ exp.error("forward reference to inferred return type of function call `%s`", exp.toChars());
+ return setError();
+ }
+
+ if (exp.f && exp.f.tintro)
+ {
+ Type t = exp.type;
+ int offset = 0;
+ TypeFunction tf = cast(TypeFunction)exp.f.tintro;
+ if (tf.next.isBaseOf(t, &offset) && offset)
+ {
+ exp.type = tf.next;
+ result = Expression.combine(argprefix, exp.castTo(sc, t));
+ return;
+ }
+ }
+
+ // Handle the case of a direct lambda call
+ if (exp.f && exp.f.isFuncLiteralDeclaration() && sc.func && !sc.intypeof)
+ {
+ exp.f.tookAddressOf = 0;
+ }
+
+ result = Expression.combine(argprefix, exp);
+
+ if (isSuper)
+ {
+ auto ad = sc.func ? sc.func.isThis() : null;
+ auto cd = ad ? ad.isClassDeclaration() : null;
+ if (cd && cd.classKind == ClassKind.cpp && exp.f && !exp.f.fbody)
+ {
+ // if super is defined in C++, it sets the vtable pointer to the base class
+ // so we have to restore it, but still return 'this' from super() call:
+ // (auto __vptrTmp = this.__vptr, auto __superTmp = super()), (this.__vptr = __vptrTmp, __superTmp)
+ Loc loc = exp.loc;
+
+ auto vptr = new DotIdExp(loc, new ThisExp(loc), Id.__vptr);
+ auto vptrTmpDecl = copyToTemp(0, "__vptrTmp", vptr);
+ auto declareVptrTmp = new DeclarationExp(loc, vptrTmpDecl);
+
+ auto superTmpDecl = copyToTemp(0, "__superTmp", result);
+ auto declareSuperTmp = new DeclarationExp(loc, superTmpDecl);
+
+ auto declareTmps = new CommaExp(loc, declareVptrTmp, declareSuperTmp);
+
+ auto restoreVptr = new AssignExp(loc, vptr.syntaxCopy(), new VarExp(loc, vptrTmpDecl));
+
+ Expression e = new CommaExp(loc, declareTmps, new CommaExp(loc, restoreVptr, new VarExp(loc, superTmpDecl)));
+ result = e.expressionSemantic(sc);
+ }
+ }
+
+ // declare dual-context container
+ if (exp.f && exp.f.isThis2 && !sc.intypeof && sc.func)
+ {
+ // check access to second `this`
+ if (AggregateDeclaration ad2 = exp.f.isMember2())
+ {
+ Expression te = new ThisExp(exp.loc).expressionSemantic(sc);
+ if (te.op != TOK.error)
+ te = getRightThis(exp.loc, sc, ad2, te, exp.f);
+ if (te.op == TOK.error)
+ {
+ exp.error("need `this` of type `%s` to call function `%s`", ad2.toChars(), exp.f.toChars());
+ return setError();
+ }
+ }
+ exp.vthis2 = makeThis2Argument(exp.loc, sc, exp.f);
+ Expression de = new DeclarationExp(exp.loc, exp.vthis2);
+ result = Expression.combine(de, result);
+ result = result.expressionSemantic(sc);
+ }
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+ static if (LOGSEMANTIC)
+ {
+ printf("DeclarationExp::semantic() %s\n", e.toChars());
+ }
+
+ uint olderrors = global.errors;
+
+ /* This is here to support extern(linkage) declaration,
+ * where the extern(linkage) winds up being an AttribDeclaration
+ * wrapper.
+ */
+ Dsymbol s = e.declaration;
+
+ while (1)
+ {
+ AttribDeclaration ad = s.isAttribDeclaration();
+ if (ad)
+ {
+ if (ad.decl && ad.decl.dim == 1)
+ {
+ s = (*ad.decl)[0];
+ continue;
+ }
+ }
+ break;
+ }
+
+ VarDeclaration v = s.isVarDeclaration();
+ if (v)
+ {
+ // Do semantic() on initializer first, so:
+ // int a = a;
+ // will be illegal.
+ e.declaration.dsymbolSemantic(sc);
+ s.parent = sc.parent;
+ }
+
+ //printf("inserting '%s' %p into sc = %p\n", s.toChars(), s, sc);
+ // Insert into both local scope and function scope.
+ // Must be unique in both.
+ if (s.ident)
+ {
+ if (!sc.insert(s))
+ {
+ auto conflict = sc.search(Loc.initial, s.ident, null);
+ e.error("declaration `%s` is already defined", s.toPrettyChars());
+ errorSupplemental(conflict.loc, "`%s` `%s` is defined here",
+ conflict.kind(), conflict.toChars());
+ return setError();
+ }
+ else if (sc.func)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=11720
+ if ((s.isFuncDeclaration() ||
+ s.isAggregateDeclaration() ||
+ s.isEnumDeclaration() ||
+ s.isTemplateDeclaration() ||
+ v
+ ) && !sc.func.localsymtab.insert(s))
+ {
+ // Get the previous symbol
+ Dsymbol originalSymbol = sc.func.localsymtab.lookup(s.ident);
+
+ // Perturb the name mangling so that the symbols can co-exist
+ // instead of colliding
+ s.localNum = cast(ushort)(originalSymbol.localNum + 1);
+ assert(s.localNum); // 65535 should be enough for anyone
+
+ // Replace originalSymbol with s, which updates the localCount
+ sc.func.localsymtab.update(s);
+
+ // The mangling change only works for D mangling
+ }
+// else
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=21272
+ * If we are in a foreach body we need to extract the
+ * function containing the foreach
+ */
+ FuncDeclaration fes_enclosing_func;
+ if (sc.func && sc.func.fes)
+ fes_enclosing_func = sc.enclosing.enclosing.func;
+
+ // Disallow shadowing
+ for (Scope* scx = sc.enclosing; scx && (scx.func == sc.func || (fes_enclosing_func && scx.func == fes_enclosing_func)); scx = scx.enclosing)
+ {
+ Dsymbol s2;
+ if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2)
+ {
+ // allow STC.local symbols to be shadowed
+ // TODO: not really an optimal design
+ auto decl = s2.isDeclaration();
+ if (!decl || !(decl.storage_class & STC.local))
+ {
+ if (sc.func.fes)
+ {
+ e.deprecation("%s `%s` is shadowing %s `%s`. Rename the `foreach` variable.", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars());
+ }
+ else
+ {
+ e.error("%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars());
+ return setError();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!s.isVarDeclaration())
+ {
+ Scope* sc2 = sc;
+ if (sc2.stc & (STC.pure_ | STC.nothrow_ | STC.nogc))
+ sc2 = sc.push();
+ sc2.stc &= ~(STC.pure_ | STC.nothrow_ | STC.nogc);
+ e.declaration.dsymbolSemantic(sc2);
+ if (sc2 != sc)
+ sc2.pop();
+ s.parent = sc.parent;
+ }
+ if (global.errors == olderrors)
+ {
+ e.declaration.semantic2(sc);
+ if (global.errors == olderrors)
+ {
+ e.declaration.semantic3(sc);
+ }
+ }
+ // todo: error in declaration should be propagated.
+
+ e.type = Type.tvoid;
+ result = e;
+ }
+
+ override void visit(TypeidExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("TypeidExp::semantic() %s\n", exp.toChars());
+ }
+ Type ta = isType(exp.obj);
+ Expression ea = isExpression(exp.obj);
+ Dsymbol sa = isDsymbol(exp.obj);
+ //printf("ta %p ea %p sa %p\n", ta, ea, sa);
+
+ if (ta)
+ {
+ dmd.typesem.resolve(ta, exp.loc, sc, ea, ta, sa, true);
+ }
+
+ if (ea)
+ {
+ if (auto sym = getDsymbol(ea))
+ ea = symbolToExp(sym, exp.loc, sc, false);
+ else
+ ea = ea.expressionSemantic(sc);
+ ea = resolveProperties(sc, ea);
+ ta = ea.type;
+ if (ea.op == TOK.type)
+ ea = null;
+ }
+
+ if (!ta)
+ {
+ //printf("ta %p ea %p sa %p\n", ta, ea, sa);
+ exp.error("no type for `typeid(%s)`", ea ? ea.toChars() : (sa ? sa.toChars() : ""));
+ return setError();
+ }
+
+ if (global.params.vcomplex)
+ ta.checkComplexTransition(exp.loc, sc);
+
+ Expression e;
+ auto tb = ta.toBasetype();
+ if (ea && tb.ty == Tclass)
+ {
+ if (tb.toDsymbol(sc).isClassDeclaration().classKind == ClassKind.cpp)
+ {
+ error(exp.loc, "Runtime type information is not supported for `extern(C++)` classes");
+ e = ErrorExp.get();
+ }
+ else if (!Type.typeinfoclass)
+ {
+ error(exp.loc, "`object.TypeInfo_Class` could not be found, but is implicitly used");
+ e = ErrorExp.get();
+ }
+ else
+ {
+ /* Get the dynamic type, which is .classinfo
+ */
+ ea = ea.expressionSemantic(sc);
+ e = new TypeidExp(ea.loc, ea);
+ e.type = Type.typeinfoclass.type;
+ }
+ }
+ else if (ta.ty == Terror)
+ {
+ e = ErrorExp.get();
+ }
+ else
+ {
+ // Handle this in the glue layer
+ e = new TypeidExp(exp.loc, ta);
+ e.type = getTypeInfoType(exp.loc, ta, sc);
+
+ semanticTypeInfo(sc, ta);
+
+ if (ea)
+ {
+ e = new CommaExp(exp.loc, ea, e); // execute ea
+ e = e.expressionSemantic(sc);
+ }
+ }
+ result = e;
+ }
+
+ override void visit(TraitsExp e)
+ {
+ result = semanticTraits(e, sc);
+ }
+
+ override void visit(HaltExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("HaltExp::semantic()\n");
+ }
+ e.type = Type.tnoreturn;
+ result = e;
+ }
+
+ override void visit(IsExp e)
+ {
+ /* is(targ id tok tspec)
+ * is(targ id : tok2)
+ * is(targ id == tok2)
+ */
+ Type tded = null;
+
+ void yes()
+ {
+ //printf("yes\n");
+ if (!e.id)
+ {
+ result = IntegerExp.createBool(true);
+ return;
+ }
+
+ Dsymbol s;
+ Tuple tup = isTuple(tded);
+ if (tup)
+ s = new TupleDeclaration(e.loc, e.id, &tup.objects);
+ else
+ s = new AliasDeclaration(e.loc, e.id, tded);
+ s.dsymbolSemantic(sc);
+
+ /* The reason for the !tup is unclear. It fails Phobos unittests if it is not there.
+ * More investigation is needed.
+ */
+ if (!tup && !sc.insert(s))
+ {
+ auto conflict = sc.search(Loc.initial, s.ident, null);
+ e.error("declaration `%s` is already defined", s.toPrettyChars());
+ errorSupplemental(conflict.loc, "`%s` `%s` is defined here",
+ conflict.kind(), conflict.toChars());
+ }
+
+ unSpeculative(sc, s);
+
+ result = IntegerExp.createBool(true);
+ }
+ void no()
+ {
+ result = IntegerExp.createBool(false);
+ //printf("no\n");
+ }
+
+ static if (LOGSEMANTIC)
+ {
+ printf("IsExp::semantic(%s)\n", e.toChars());
+ }
+ if (e.id && !(sc.flags & SCOPE.condition))
+ {
+ e.error("can only declare type aliases within `static if` conditionals or `static assert`s");
+ return setError();
+ }
+
+ if (e.tok2 == TOK.package_ || e.tok2 == TOK.module_) // These is() expressions are special because they can work on modules, not just types.
+ {
+ const oldErrors = global.startGagging();
+ Dsymbol sym = e.targ.toDsymbol(sc);
+ global.endGagging(oldErrors);
+
+ if (sym is null)
+ return no();
+ Package p = resolveIsPackage(sym);
+ if (p is null)
+ return no();
+ if (e.tok2 == TOK.package_ && p.isModule()) // Note that isModule() will return null for package modules because they're not actually instances of Module.
+ return no();
+ else if(e.tok2 == TOK.module_ && !(p.isModule() || p.isPackageMod()))
+ return no();
+ tded = e.targ;
+ return yes();
+ }
+
+ {
+ Scope* sc2 = sc.copy(); // keep sc.flags
+ sc2.tinst = null;
+ sc2.minst = null;
+ sc2.flags |= SCOPE.fullinst;
+ Type t = e.targ.trySemantic(e.loc, sc2);
+ sc2.pop();
+ if (!t) // errors, so condition is false
+ return no();
+ e.targ = t;
+ }
+
+ if (e.tok2 != TOK.reserved)
+ {
+ switch (e.tok2)
+ {
+ case TOK.struct_:
+ if (e.targ.ty != Tstruct)
+ return no();
+ if ((cast(TypeStruct)e.targ).sym.isUnionDeclaration())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.union_:
+ if (e.targ.ty != Tstruct)
+ return no();
+ if (!(cast(TypeStruct)e.targ).sym.isUnionDeclaration())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.class_:
+ if (e.targ.ty != Tclass)
+ return no();
+ if ((cast(TypeClass)e.targ).sym.isInterfaceDeclaration())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.interface_:
+ if (e.targ.ty != Tclass)
+ return no();
+ if (!(cast(TypeClass)e.targ).sym.isInterfaceDeclaration())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.const_:
+ if (!e.targ.isConst())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.immutable_:
+ if (!e.targ.isImmutable())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.shared_:
+ if (!e.targ.isShared())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.inout_:
+ if (!e.targ.isWild())
+ return no();
+ tded = e.targ;
+ break;
+
+ case TOK.super_:
+ // If class or interface, get the base class and interfaces
+ if (e.targ.ty != Tclass)
+ return no();
+ else
+ {
+ ClassDeclaration cd = (cast(TypeClass)e.targ).sym;
+ auto args = new Parameters();
+ args.reserve(cd.baseclasses.dim);
+ if (cd.semanticRun < PASS.semanticdone)
+ cd.dsymbolSemantic(null);
+ for (size_t i = 0; i < cd.baseclasses.dim; i++)
+ {
+ BaseClass* b = (*cd.baseclasses)[i];
+ args.push(new Parameter(STC.in_, b.type, null, null, null));
+ }
+ tded = new TypeTuple(args);
+ }
+ break;
+
+ case TOK.enum_:
+ if (e.targ.ty != Tenum)
+ return no();
+ if (e.id)
+ tded = (cast(TypeEnum)e.targ).sym.getMemtype(e.loc);
+ else
+ tded = e.targ;
+
+ if (tded.ty == Terror)
+ return setError();
+ break;
+
+ case TOK.delegate_:
+ if (e.targ.ty != Tdelegate)
+ return no();
+ tded = (cast(TypeDelegate)e.targ).next; // the underlying function type
+ break;
+
+ case TOK.function_:
+ case TOK.parameters:
+ {
+ if (e.targ.ty != Tfunction)
+ return no();
+ tded = e.targ;
+
+ /* Generate tuple from function parameter types.
+ */
+ assert(tded.ty == Tfunction);
+ auto tdedf = tded.isTypeFunction();
+ auto args = new Parameters();
+ foreach (i, arg; tdedf.parameterList)
+ {
+ assert(arg && arg.type);
+ /* If one of the default arguments was an error,
+ don't return an invalid tuple
+ */
+ if (e.tok2 == TOK.parameters && arg.defaultArg && arg.defaultArg.op == TOK.error)
+ return setError();
+ args.push(new Parameter(arg.storageClass, arg.type, (e.tok2 == TOK.parameters) ? arg.ident : null, (e.tok2 == TOK.parameters) ? arg.defaultArg : null, arg.userAttribDecl));
+ }
+ tded = new TypeTuple(args);
+ break;
+ }
+ case TOK.return_:
+ /* Get the 'return type' for the function,
+ * delegate, or pointer to function.
+ */
+ if (auto tf = e.targ.isFunction_Delegate_PtrToFunction())
+ tded = tf.next;
+ else
+ return no();
+ break;
+
+ case TOK.argumentTypes:
+ /* Generate a type tuple of the equivalent types used to determine if a
+ * function argument of this type can be passed in registers.
+ * The results of this are highly platform dependent, and intended
+ * primarly for use in implementing va_arg().
+ */
+ tded = target.toArgTypes(e.targ);
+ if (!tded)
+ return no();
+ // not valid for a parameter
+ break;
+
+ case TOK.vector:
+ if (e.targ.ty != Tvector)
+ return no();
+ tded = (cast(TypeVector)e.targ).basetype;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=18753
+ if (tded)
+ return yes();
+ return no();
+ }
+ else if (e.tspec && !e.id && !(e.parameters && e.parameters.dim))
+ {
+ /* Evaluate to true if targ matches tspec
+ * is(targ == tspec)
+ * is(targ : tspec)
+ */
+ e.tspec = e.tspec.typeSemantic(e.loc, sc);
+ //printf("targ = %s, %s\n", e.targ.toChars(), e.targ.deco);
+ //printf("tspec = %s, %s\n", e.tspec.toChars(), e.tspec.deco);
+
+ if (e.tok == TOK.colon)
+ {
+ // current scope is itself deprecated, or deprecations are not errors
+ const bool deprecationAllowed = sc.isDeprecated
+ || global.params.useDeprecated != DiagnosticReporting.error;
+ const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed;
+
+ // baseClass might not be set if either targ or tspec is forward referenced.
+ if (auto tc = e.targ.isTypeClass())
+ tc.sym.dsymbolSemantic(null);
+ if (auto tc = e.tspec.isTypeClass())
+ tc.sym.dsymbolSemantic(null);
+
+ if (preventAliasThis && e.targ.ty == Tstruct)
+ {
+ if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec))
+ return yes();
+ else
+ return no();
+ }
+ else if (preventAliasThis && e.targ.ty == Tclass)
+ {
+ if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec))
+ return yes();
+ else
+ return no();
+ }
+ else if (e.targ.implicitConvTo(e.tspec))
+ return yes();
+ else
+ return no();
+ }
+ else /* == */
+ {
+ if (e.targ.equals(e.tspec))
+ return yes();
+ else
+ return no();
+ }
+ }
+ else if (e.tspec)
+ {
+ /* Evaluate to true if targ matches tspec.
+ * If true, declare id as an alias for the specialized type.
+ * is(targ == tspec, tpl)
+ * is(targ : tspec, tpl)
+ * is(targ id == tspec)
+ * is(targ id : tspec)
+ * is(targ id == tspec, tpl)
+ * is(targ id : tspec, tpl)
+ */
+ Identifier tid = e.id ? e.id : Identifier.generateId("__isexp_id");
+ e.parameters.insert(0, new TemplateTypeParameter(e.loc, tid, null, null));
+
+ Objects dedtypes = Objects(e.parameters.dim);
+ dedtypes.zero();
+
+ MATCH m = deduceType(e.targ, sc, e.tspec, e.parameters, &dedtypes, null, 0, e.tok == TOK.equal);
+ //printf("targ: %s\n", targ.toChars());
+ //printf("tspec: %s\n", tspec.toChars());
+ if (m == MATCH.nomatch || (m != MATCH.exact && e.tok == TOK.equal))
+ {
+ return no();
+ }
+ else
+ {
+ tded = cast(Type)dedtypes[0];
+ if (!tded)
+ tded = e.targ;
+ Objects tiargs = Objects(1);
+ tiargs[0] = e.targ;
+
+ /* Declare trailing parameters
+ */
+ for (size_t i = 1; i < e.parameters.dim; i++)
+ {
+ TemplateParameter tp = (*e.parameters)[i];
+ Declaration s = null;
+
+ m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, &dedtypes, &s);
+ if (m == MATCH.nomatch)
+ return no();
+ s.dsymbolSemantic(sc);
+ if (!sc.insert(s))
+ {
+ auto conflict = sc.search(Loc.initial, s.ident, null);
+ e.error("declaration `%s` is already defined", s.toPrettyChars());
+ errorSupplemental(conflict.loc, "`%s` `%s` is defined here",
+ conflict.kind(), conflict.toChars());
+ }
+
+ unSpeculative(sc, s);
+ }
+ return yes();
+ }
+ }
+ else if (e.id)
+ {
+ /* Declare id as an alias for type targ. Evaluate to true
+ * is(targ id)
+ */
+ tded = e.targ;
+ }
+ return yes();
+ }
+
+ override void visit(BinAssignExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.op == TOK.arrayLength)
+ {
+ // arr.length op= e2;
+ e = rewriteOpAssign(exp);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ if (exp.e1.op == TOK.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray)
+ {
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+
+ if (exp.e1.op == TOK.slice)
+ (cast(SliceExp)exp.e1).arrayop = true;
+
+ // T[] op= ...
+ if (exp.e2.implicitConvTo(exp.e1.type.nextOf()))
+ {
+ // T[] op= T
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type.nextOf());
+ }
+ else if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ exp.type = exp.e1.type;
+ result = arrayOp(exp, sc);
+ return;
+ }
+
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true);
+ exp.type = exp.e1.type;
+
+ if (auto ad = isAggregate(exp.e1.type))
+ {
+ if (const s = search_function(ad, Id.opOpAssign))
+ {
+ error(exp.loc, "none of the `opOpAssign` overloads of `%s` are callable for `%s` of type `%s`", ad.toChars(), exp.e1.toChars(), exp.e1.type.toChars());
+ return setError();
+ }
+ }
+ if (exp.e1.checkScalar() ||
+ exp.e1.checkReadModifyWrite(exp.op, exp.e2) ||
+ exp.e1.checkSharedAccess(sc))
+ return setError();
+
+ int arith = (exp.op == TOK.addAssign || exp.op == TOK.minAssign || exp.op == TOK.mulAssign || exp.op == TOK.divAssign || exp.op == TOK.modAssign || exp.op == TOK.powAssign);
+ int bitwise = (exp.op == TOK.andAssign || exp.op == TOK.orAssign || exp.op == TOK.xorAssign);
+ int shift = (exp.op == TOK.leftShiftAssign || exp.op == TOK.rightShiftAssign || exp.op == TOK.unsignedRightShiftAssign);
+
+ if (bitwise && exp.type.toBasetype().ty == Tbool)
+ exp.e2 = exp.e2.implicitCastTo(sc, exp.type);
+ else if (exp.checkNoBool())
+ return setError();
+
+ if ((exp.op == TOK.addAssign || exp.op == TOK.minAssign) && exp.e1.type.toBasetype().ty == Tpointer && exp.e2.type.toBasetype().isintegral())
+ {
+ result = scaleFactor(exp, sc);
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ if (arith && (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)))
+ return setError();
+ if ((bitwise || shift) && (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)))
+ return setError();
+
+ if (shift)
+ {
+ if (exp.e2.type.toBasetype().ty != Tvector)
+ exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
+ }
+
+ if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ if (exp.e1.op == TOK.error || exp.e2.op == TOK.error)
+ return setError();
+
+ e = exp.checkOpAssignTypes(sc);
+ if (e.op == TOK.error)
+ {
+ result = e;
+ return;
+ }
+
+ assert(e.op == TOK.assign || e == exp);
+ result = (cast(BinExp)e).reorderSettingAAElem(sc);
+ }
+
+ private Expression compileIt(MixinExp exp)
+ {
+ OutBuffer buf;
+ if (expressionsToString(buf, sc, exp.exps))
+ return null;
+
+ uint errors = global.errors;
+ const len = buf.length;
+ const str = buf.extractChars()[0 .. len];
+ scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false);
+ p.nextToken();
+ //printf("p.loc.linnum = %d\n", p.loc.linnum);
+
+ Expression e = p.parseExpression();
+ if (global.errors != errors)
+ return null;
+
+ if (p.token.value != TOK.endOfFile)
+ {
+ exp.error("incomplete mixin expression `%s`", str.ptr);
+ return null;
+ }
+ return e;
+ }
+
+ override void visit(MixinExp exp)
+ {
+ /* https://dlang.org/spec/expression.html#mixin_expressions
+ */
+
+ static if (LOGSEMANTIC)
+ {
+ printf("MixinExp::semantic('%s')\n", exp.toChars());
+ }
+
+ auto e = compileIt(exp);
+ if (!e)
+ return setError();
+ result = e.expressionSemantic(sc);
+ }
+
+ override void visit(ImportExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ImportExp::semantic('%s')\n", e.toChars());
+ }
+
+ auto se = semanticString(sc, e.e1, "file name argument");
+ if (!se)
+ return setError();
+ se = se.toUTF8(sc);
+
+ auto namez = se.toStringz().ptr;
+ if (!global.filePath)
+ {
+ e.error("need `-J` switch to import text file `%s`", namez);
+ return setError();
+ }
+
+ /* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
+ * ('Path Traversal') attacks.
+ * http://cwe.mitre.org/data/definitions/22.html
+ */
+
+ if (FileName.absolute(namez))
+ {
+ e.error("absolute path is not allowed in import expression: `%s`", se.toChars());
+ return setError();
+ }
+
+ auto idxReserved = FileName.findReservedChar(namez);
+ if (idxReserved != size_t.max)
+ {
+ e.error("`%s` is not a valid filename on this platform", se.toChars());
+ e.errorSupplemental("Character `'%c'` is reserved and cannot be used", namez[idxReserved]);
+ return setError();
+ }
+
+ if (FileName.refersToParentDir(namez))
+ {
+ e.error("path refers to parent (`..`) directory: `%s`", se.toChars());
+ return setError();
+ }
+
+ auto name = FileName.searchPath(global.filePath, namez, false);
+ if (!name)
+ {
+ e.error("file `%s` cannot be found or not in a path specified with `-J`", se.toChars());
+ e.errorSupplemental("Path(s) searched (as provided by `-J`):");
+ foreach (idx, path; *global.filePath)
+ {
+ const attr = FileName.exists(path);
+ const(char)* err = attr == 2 ? "" :
+ (attr == 1 ? " (not a directory)" : " (path not found)");
+ e.errorSupplemental("[%zu]: `%s`%s", idx, path, err);
+ }
+ return setError();
+ }
+
+ sc._module.contentImportedFiles.push(name);
+ if (global.params.verbose)
+ {
+ const slice = se.peekString();
+ message("file %.*s\t(%s)", cast(int)slice.length, slice.ptr, name);
+ }
+ if (global.params.moduleDeps !is null)
+ {
+ OutBuffer* ob = global.params.moduleDeps;
+ Module imod = sc._module;
+
+ if (!global.params.moduleDepsFile)
+ ob.writestring("depsFile ");
+ ob.writestring(imod.toPrettyChars());
+ ob.writestring(" (");
+ escapePath(ob, imod.srcfile.toChars());
+ ob.writestring(") : ");
+ if (global.params.moduleDepsFile)
+ ob.writestring("string : ");
+ ob.write(se.peekString());
+ ob.writestring(" (");
+ escapePath(ob, name);
+ ob.writestring(")");
+ ob.writenl();
+ }
+ if (global.params.emitMakeDeps)
+ {
+ global.params.makeDeps.push(name);
+ }
+
+ {
+ auto readResult = File.read(name);
+ if (!readResult.success)
+ {
+ e.error("cannot read file `%s`", name);
+ return setError();
+ }
+ else
+ {
+ // take ownership of buffer (probably leaking)
+ auto data = readResult.extractSlice();
+ se = new StringExp(e.loc, data);
+ }
+ }
+ result = se.expressionSemantic(sc);
+ }
+
+ override void visit(AssertExp exp)
+ {
+ // https://dlang.org/spec/expression.html#assert_expressions
+ static if (LOGSEMANTIC)
+ {
+ printf("AssertExp::semantic('%s')\n", exp.toChars());
+ }
+
+ const generateMsg = !exp.msg && global.params.checkAction == CHECKACTION.context && global.params.useAssert == CHECKENABLE.on;
+ Expression temporariesPrefix;
+
+ if (generateMsg)
+ // no message - use assert expression as msg
+ {
+ if (!verifyHookExist(exp.loc, *sc, Id._d_assert_fail, "generating assert messages"))
+ return setError();
+
+ /*
+ {
+ auto a = e1, b = e2;
+ assert(a == b, _d_assert_fail!"=="(a, b));
+ }()
+ */
+
+ /*
+ Stores the result of an operand expression into a temporary
+ if necessary, e.g. if it is an impure fuction call containing side
+ effects as in https://issues.dlang.org/show_bug.cgi?id=20114
+
+ Params:
+ op = an expression which may require a temporary (added to
+ `temporariesPrefix`: `auto tmp = op`) and will be replaced
+ by `tmp` if necessary
+
+ Returns: (possibly replaced) `op`
+ */
+ Expression maybePromoteToTmp(ref Expression op)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20989
+ // Flag that _d_assert_fail will never dereference `array.ptr` to avoid safety
+ // errors for `assert(!array.ptr)` => `_d_assert_fail!"!"(array.ptr)`
+ {
+ auto die = op.isDotIdExp();
+ if (die && die.ident == Id.ptr)
+ die.noderef = true;
+ }
+
+ op = op.expressionSemantic(sc);
+ op = resolveProperties(sc, op);
+
+ // Detect assert's using static operator overloads (e.g. `"var" in environment`)
+ if (auto te = op.isTypeExp())
+ {
+ // Replace the TypeExp with it's textual representation
+ // Including "..." in the error message isn't quite right but
+ // proper solutions require more drastic changes, e.g. directly
+ // using miniFormat and combine instead of calling _d_assert_fail
+ auto name = new StringExp(te.loc, te.toString());
+ return name.expressionSemantic(sc);
+ }
+
+ // Create a temporary for expressions with side effects
+ // Defensively assume that function calls may have side effects even
+ // though it's not detected by hasSideEffect (e.g. `debug puts("Hello")` )
+ // Rewriting CallExp's also avoids some issues with the inliner/debug generation
+ if (op.hasSideEffect(true))
+ {
+ // Don't create an invalid temporary for void-expressions
+ // Further semantic will issue an appropriate error
+ if (op.type.ty == Tvoid)
+ return op;
+
+ // https://issues.dlang.org/show_bug.cgi?id=21590
+ // Don't create unnecessary temporaries and detect `assert(a = b)`
+ if (op.isAssignExp() || op.isBinAssignExp())
+ {
+ auto left = (cast(BinExp) op).e1;
+
+ // Find leftmost expression to handle other rewrites,
+ // e.g. --(++a) => a += 1 -= 1
+ while (left.isAssignExp() || left.isBinAssignExp())
+ left = (cast(BinExp) left).e1;
+
+ // Only use the assignee if it's a variable and skip
+ // other lvalues (e.g. ref's returned by functions)
+ if (left.isVarExp())
+ return left;
+
+ // Sanity check that `op` can be converted to boolean
+ // But don't raise errors for assignments enclosed in another expression
+ if (op is exp.e1)
+ op.toBoolean(sc);
+ }
+
+ // Tuples with side-effects already receive a temporary during semantic
+ if (op.type.isTypeTuple())
+ {
+ auto te = op.isTupleExp();
+ assert(te);
+
+ // Create a new tuple without the associated temporary
+ auto res = new TupleExp(op.loc, te.exps);
+ return res.expressionSemantic(sc);
+ }
+
+ const stc = op.isLvalue() ? STC.ref_ : 0;
+ auto tmp = copyToTemp(stc, "__assertOp", op);
+ tmp.dsymbolSemantic(sc);
+
+ auto decl = new DeclarationExp(op.loc, tmp);
+ temporariesPrefix = Expression.combine(temporariesPrefix, decl);
+
+ op = new VarExp(op.loc, tmp);
+ op = op.expressionSemantic(sc);
+ }
+ return op;
+ }
+
+ // if the assert condition is a mixin expression, try to compile it
+ if (auto ce = exp.e1.isMixinExp())
+ {
+ if (auto e1 = compileIt(ce))
+ exp.e1 = e1;
+ }
+
+ Expressions* es;
+ Objects* tiargs;
+ Loc loc = exp.e1.loc;
+
+ const tok = exp.e1.op;
+ bool isEqualsCallExpression;
+ if (tok == TOK.call)
+ {
+ const callExp = cast(CallExp) exp.e1;
+
+ // https://issues.dlang.org/show_bug.cgi?id=20331
+ // callExp.f may be null if the assert contains a call to
+ // a function pointer or literal
+ if (const callExpFunc = callExp.f)
+ {
+ const callExpIdent = callExpFunc.ident;
+ isEqualsCallExpression = callExpIdent == Id.__equals ||
+ callExpIdent == Id.eq;
+ }
+ }
+ if (tok == TOK.equal || tok == TOK.notEqual ||
+ tok == TOK.lessThan || tok == TOK.greaterThan ||
+ tok == TOK.lessOrEqual || tok == TOK.greaterOrEqual ||
+ tok == TOK.identity || tok == TOK.notIdentity ||
+ tok == TOK.in_ ||
+ isEqualsCallExpression)
+ {
+ es = new Expressions(3);
+ tiargs = new Objects(1);
+
+ if (isEqualsCallExpression)
+ {
+ auto callExp = cast(CallExp) exp.e1;
+ auto args = callExp.arguments;
+
+ // structs with opEquals get rewritten to a DotVarExp:
+ // a.opEquals(b)
+ // https://issues.dlang.org/show_bug.cgi?id=20100
+ if (args.length == 1)
+ {
+ auto dv = callExp.e1.isDotVarExp();
+ assert(dv);
+
+ // runtime args
+ (*es)[1] = maybePromoteToTmp(dv.e1);
+ (*es)[2] = maybePromoteToTmp((*args)[0]);
+ }
+ else
+ {
+ // runtime args
+ (*es)[1] = maybePromoteToTmp((*args)[0]);
+ (*es)[2] = maybePromoteToTmp((*args)[1]);
+ }
+ }
+ else
+ {
+ auto binExp = cast(EqualExp) exp.e1;
+
+ // runtime args
+ (*es)[1] = maybePromoteToTmp(binExp.e1);
+ (*es)[2] = maybePromoteToTmp(binExp.e2);
+ }
+
+ // template args
+ Expression comp = new StringExp(loc, isEqualsCallExpression ? "==" : Token.toString(exp.e1.op));
+ comp = comp.expressionSemantic(sc);
+ (*es)[0] = comp;
+ (*tiargs)[0] = (*es)[1].type;
+ }
+
+ // Format exp.e1 before any additional boolean conversion
+ // Ignore &&/|| because "assert(...) failed" is more informative than "false != true"
+ else if (tok != TOK.andAnd && tok != TOK.orOr)
+ {
+ es = new Expressions(2);
+ tiargs = new Objects(1);
+
+ if (auto ne = exp.e1.isNotExp())
+ {
+ // Fetch the (potential non-bool) expression and fold
+ // (n) negations into (n % 2) negations, e.g. !!a => a
+ for (bool neg = true; ; neg = !neg)
+ {
+ if (auto ne2 = ne.e1.isNotExp())
+ ne = ne2;
+ else
+ {
+ (*es)[0] = new StringExp(loc, neg ? "!" : "");
+ (*es)[1] = maybePromoteToTmp(ne.e1);
+ break;
+ }
+ }
+ }
+ else
+ { // Simply format exp.e1
+ (*es)[0] = new StringExp(loc, "");
+ (*es)[1] = maybePromoteToTmp(exp.e1);
+ }
+
+ (*tiargs)[0] = (*es)[1].type;
+
+ // Passing __ctfe to auto ref infers ref and aborts compilation:
+ // "cannot modify compiler-generated variable __ctfe"
+ auto ve = (*es)[1].isVarExp();
+ if (ve && ve.var.ident == Id.ctfe)
+ {
+ exp.msg = new StringExp(loc, "assert(__ctfe) failed!");
+ goto LSkip;
+ }
+ }
+ else
+ {
+ OutBuffer buf;
+ buf.printf("%s failed", exp.toChars());
+ exp.msg = new StringExp(Loc.initial, buf.extractSlice());
+ goto LSkip;
+ }
+
+ Expression __assertFail = new IdentifierExp(exp.loc, Id.empty);
+ auto assertFail = new DotIdExp(loc, __assertFail, Id.object);
+
+ auto dt = new DotTemplateInstanceExp(loc, assertFail, Id._d_assert_fail, tiargs);
+ auto ec = CallExp.create(loc, dt, es);
+ exp.msg = ec;
+ }
+
+ LSkip:
+ if (Expression ex = unaSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ exp.e1 = resolveProperties(sc, exp.e1);
+ // BUG: see if we can do compile time elimination of the Assert
+ exp.e1 = exp.e1.optimize(WANTvalue);
+ exp.e1 = exp.e1.toBoolean(sc);
+
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+
+ if (exp.msg)
+ {
+ exp.msg = expressionSemantic(exp.msg, sc);
+ exp.msg = resolveProperties(sc, exp.msg);
+ exp.msg = exp.msg.implicitCastTo(sc, Type.tchar.constOf().arrayOf());
+ exp.msg = exp.msg.optimize(WANTvalue);
+ checkParamArgumentEscape(sc, null, null, exp.msg, true, false);
+ }
+
+ if (exp.msg && exp.msg.op == TOK.error)
+ {
+ result = exp.msg;
+ return;
+ }
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = exp.msg && checkNonAssignmentArrayOp(exp.msg);
+ if (f1 || f2)
+ return setError();
+
+ if (exp.e1.isBool(false))
+ {
+ /* This is an `assert(0)` which means halt program execution
+ */
+ FuncDeclaration fd = sc.parent.isFuncDeclaration();
+ if (fd)
+ fd.hasReturnExp |= 4;
+ sc.ctorflow.orCSX(CSX.halt);
+
+ if (global.params.useAssert == CHECKENABLE.off)
+ {
+ Expression e = new HaltExp(exp.loc);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ exp.type = Type.tnoreturn;
+ }
+ else
+ exp.type = Type.tvoid;
+
+ result = !temporariesPrefix
+ ? exp
+ : Expression.combine(temporariesPrefix, exp).expressionSemantic(sc);
+ }
+
+ override void visit(DotIdExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars());
+ //printf("e1.op = %d, '%s'\n", e1.op, Token::toChars(e1.op));
+ }
+ Expression e = exp.semanticY(sc, 1);
+ if (e && isDotOpDispatch(e))
+ {
+ uint errors = global.startGagging();
+ e = resolvePropertiesX(sc, e);
+ if (global.endGagging(errors))
+ e = null; /* fall down to UFCS */
+ else
+ {
+ result = e;
+ return;
+ }
+ }
+ if (!e) // if failed to find the property
+ {
+ /* If ident is not a valid property, rewrite:
+ * e1.ident
+ * as:
+ * .ident(e1)
+ */
+ e = resolveUFCSProperties(sc, exp);
+ }
+ result = e;
+ }
+
+ override void visit(DotTemplateExp e)
+ {
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+ if (Expression ex = unaSemantic(e, sc))
+ {
+ result = ex;
+ return;
+ }
+ // 'void' like TemplateExp
+ e.type = Type.tvoid;
+ result = e;
+ }
+
+ override void visit(DotVarExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotVarExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.var = exp.var.toAlias().isDeclaration();
+
+ exp.e1 = exp.e1.expressionSemantic(sc);
+
+ if (auto tup = exp.var.isTupleDeclaration())
+ {
+ /* Replace:
+ * e1.tuple(a, b, c)
+ * with:
+ * tuple(e1.a, e1.b, e1.c)
+ */
+ Expression e0;
+ Expression ev = sc.func ? extractSideEffect(sc, "__tup", e0, exp.e1) : exp.e1;
+
+ auto exps = new Expressions();
+ exps.reserve(tup.objects.dim);
+ for (size_t i = 0; i < tup.objects.dim; i++)
+ {
+ RootObject o = (*tup.objects)[i];
+ Expression e;
+ Declaration var;
+ if (o.dyncast() == DYNCAST.expression)
+ {
+ e = cast(Expression)o;
+ if (auto se = e.isDsymbolExp())
+ var = se.s.isDeclaration();
+ else if (auto ve = e.isVarExp())
+ if (!ve.var.isFuncDeclaration())
+ // Exempt functions for backwards compatibility reasons.
+ // See: https://issues.dlang.org/show_bug.cgi?id=20470#c1
+ var = ve.var;
+ }
+ else if (o.dyncast() == DYNCAST.dsymbol)
+ {
+ Dsymbol s = cast(Dsymbol) o;
+ Declaration d = s.isDeclaration();
+ if (!d || d.isFuncDeclaration())
+ // Exempt functions for backwards compatibility reasons.
+ // See: https://issues.dlang.org/show_bug.cgi?id=20470#c1
+ e = new DsymbolExp(exp.loc, s);
+ else
+ var = d;
+ }
+ else if (o.dyncast() == DYNCAST.type)
+ {
+ e = new TypeExp(exp.loc, cast(Type)o);
+ }
+ else
+ {
+ exp.error("`%s` is not an expression", o.toChars());
+ return setError();
+ }
+ if (var)
+ e = new DotVarExp(exp.loc, ev, var);
+ exps.push(e);
+ }
+
+ Expression e = new TupleExp(exp.loc, e0, exps);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ exp.e1 = exp.e1.addDtorHook(sc);
+
+ Type t1 = exp.e1.type;
+
+ if (FuncDeclaration fd = exp.var.isFuncDeclaration())
+ {
+ // for functions, do checks after overload resolution
+ if (!fd.functionSemantic())
+ return setError();
+
+ /* https://issues.dlang.org/show_bug.cgi?id=13843
+ * If fd obviously has no overloads, we should
+ * normalize AST, and it will give a chance to wrap fd with FuncExp.
+ */
+ if ((fd.isNested() && !fd.isThis()) || fd.isFuncLiteralDeclaration())
+ {
+ // (e1, fd)
+ auto e = symbolToExp(fd, exp.loc, sc, false);
+ result = Expression.combine(exp.e1, e);
+ return;
+ }
+
+ exp.type = fd.type;
+ assert(exp.type);
+ }
+ else if (OverDeclaration od = exp.var.isOverDeclaration())
+ {
+ exp.type = Type.tvoid; // ambiguous type?
+ }
+ else
+ {
+ exp.type = exp.var.type;
+ if (!exp.type && global.errors) // var is goofed up, just return error.
+ return setError();
+ assert(exp.type);
+
+ if (t1.ty == Tpointer)
+ t1 = t1.nextOf();
+
+ exp.type = exp.type.addMod(t1.mod);
+
+ Dsymbol vparent = exp.var.toParent();
+ AggregateDeclaration ad = vparent ? vparent.isAggregateDeclaration() : null;
+ if (Expression e1x = getRightThis(exp.loc, sc, ad, exp.e1, exp.var, 1))
+ exp.e1 = e1x;
+ else
+ {
+ /* Later checkRightThis will report correct error for invalid field variable access.
+ */
+ Expression e = new VarExp(exp.loc, exp.var);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ checkAccess(exp.loc, sc, exp.e1, exp.var);
+
+ VarDeclaration v = exp.var.isVarDeclaration();
+ if (v && (v.isDataseg() || (v.storage_class & STC.manifest)))
+ {
+ Expression e = expandVar(WANTvalue, v);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+
+ if (v && (v.isDataseg() || // fix https://issues.dlang.org/show_bug.cgi?id=8238
+ (!v.needThis() && v.semanticRun > PASS.init))) // fix https://issues.dlang.org/show_bug.cgi?id=17258
+ {
+ // (e1, v)
+ checkAccess(exp.loc, sc, exp.e1, v);
+ Expression e = new VarExp(exp.loc, v);
+ e = new CommaExp(exp.loc, exp.e1, e);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+ //printf("-DotVarExp::semantic('%s')\n", toChars());
+ result = exp;
+ }
+
+ override void visit(DotTemplateInstanceExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars());
+ }
+ // Indicate we need to resolve by UFCS.
+ Expression e = exp.semanticY(sc, 1);
+ if (!e)
+ e = resolveUFCSProperties(sc, exp);
+ result = e;
+ }
+
+ override void visit(DelegateExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DelegateExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ e.e1 = e.e1.expressionSemantic(sc);
+
+ e.type = new TypeDelegate(e.func.type.isTypeFunction());
+ e.type = e.type.typeSemantic(e.loc, sc);
+
+ FuncDeclaration f = e.func.toAliasFunc();
+ AggregateDeclaration ad = f.toParentLocal().isAggregateDeclaration();
+ if (f.needThis())
+ e.e1 = getRightThis(e.loc, sc, ad, e.e1, f);
+ if (e.e1.op == TOK.error)
+ return setError();
+
+ /* A delegate takes the address of e.e1 in order to set the .ptr field
+ * https://issues.dlang.org/show_bug.cgi?id=18575
+ */
+ if (global.params.useDIP1000 == FeatureState.enabled && e.e1.type.toBasetype().ty == Tstruct)
+ {
+ if (auto v = expToVariable(e.e1))
+ {
+ if (!checkAddressVar(sc, e.e1, v))
+ return setError();
+ }
+ }
+
+ if (f.type.ty == Tfunction)
+ {
+ TypeFunction tf = cast(TypeFunction)f.type;
+ if (!MODmethodConv(e.e1.type.mod, f.type.mod))
+ {
+ OutBuffer thisBuf, funcBuf;
+ MODMatchToBuffer(&thisBuf, e.e1.type.mod, tf.mod);
+ MODMatchToBuffer(&funcBuf, tf.mod, e.e1.type.mod);
+ e.error("%smethod `%s` is not callable using a %s`%s`",
+ funcBuf.peekChars(), f.toPrettyChars(), thisBuf.peekChars(), e.e1.toChars());
+ return setError();
+ }
+ }
+ if (ad && ad.isClassDeclaration() && ad.type != e.e1.type)
+ {
+ // A downcast is required for interfaces
+ // https://issues.dlang.org/show_bug.cgi?id=3706
+ e.e1 = new CastExp(e.loc, e.e1, ad.type);
+ e.e1 = e.e1.expressionSemantic(sc);
+ }
+ result = e;
+ // declare dual-context container
+ if (f.isThis2 && !sc.intypeof && sc.func)
+ {
+ // check access to second `this`
+ if (AggregateDeclaration ad2 = f.isMember2())
+ {
+ Expression te = new ThisExp(e.loc).expressionSemantic(sc);
+ if (te.op != TOK.error)
+ te = getRightThis(e.loc, sc, ad2, te, f);
+ if (te.op == TOK.error)
+ {
+ e.error("need `this` of type `%s` to make delegate from function `%s`", ad2.toChars(), f.toChars());
+ return setError();
+ }
+ }
+ VarDeclaration vthis2 = makeThis2Argument(e.loc, sc, f);
+ e.vthis2 = vthis2;
+ Expression de = new DeclarationExp(e.loc, vthis2);
+ result = Expression.combine(de, result);
+ result = result.expressionSemantic(sc);
+ }
+ }
+
+ override void visit(DotTypeExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotTypeExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (auto e = unaSemantic(exp, sc))
+ {
+ result = e;
+ return;
+ }
+
+ exp.type = exp.sym.getType().addMod(exp.e1.type.mod);
+ result = exp;
+ }
+
+ override void visit(AddrExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("AddrExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = unaSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ if (sc.flags & SCOPE.Cfile)
+ {
+ /* Special handling for &"string"
+ * since C regards a string literal as an lvalue
+ */
+ if (auto se = exp.e1.isStringExp())
+ {
+ if (auto tp = se.type.toBasetype().isTypePointer())
+ {
+ /* Switch from pointer-to-char to pointer-to-static-array-of-char
+ */
+ auto ts = new TypeSArray(tp.nextOf(), new IntegerExp(Loc.initial, se.len + 1, Type.tsize_t));
+ se.type = typeSemantic(ts, Loc.initial, sc).pointerTo();
+ result = se;
+ return;
+ }
+ }
+ }
+
+ int wasCond = exp.e1.op == TOK.question;
+
+ if (exp.e1.op == TOK.dotTemplateInstance)
+ {
+ DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)exp.e1;
+ TemplateInstance ti = dti.ti;
+ {
+ //assert(ti.needsTypeInference(sc));
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors) // if template failed to expand
+ return setError();
+
+ Dsymbol s = ti.toAlias();
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ exp.e1 = new DotVarExp(exp.e1.loc, dti.e1, f);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ }
+ }
+ }
+ else if (exp.e1.op == TOK.scope_)
+ {
+ TemplateInstance ti = (cast(ScopeExp)exp.e1).sds.isTemplateInstance();
+ if (ti)
+ {
+ //assert(ti.needsTypeInference(sc));
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors) // if template failed to expand
+ return setError();
+
+ Dsymbol s = ti.toAlias();
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ exp.e1 = new VarExp(exp.e1.loc, f);
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ }
+ }
+ }
+ /* https://issues.dlang.org/show_bug.cgi?id=809
+ *
+ * If the address of a lazy variable is taken,
+ * the expression is rewritten so that the type
+ * of it is the delegate type. This means that
+ * the symbol is not going to represent a call
+ * to the delegate anymore, but rather, the
+ * actual symbol.
+ */
+ if (auto ve = exp.e1.isVarExp())
+ {
+ if (ve.var.storage_class & STC.lazy_)
+ {
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ exp.e1 = resolveProperties(sc, exp.e1);
+ if (auto callExp = exp.e1.isCallExp())
+ {
+ if (callExp.e1.type.toBasetype().ty == Tdelegate)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=20551
+ *
+ * Cannot take address of lazy parameter in @safe code
+ * because it might end up being a pointer to undefined
+ * memory.
+ */
+ if (sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("cannot take address of lazy parameter `%s` in `@safe` function `%s`",
+ ve.toChars(), sc.func.toChars());
+ setError();
+ }
+ else
+ {
+ VarExp ve2 = callExp.e1.isVarExp();
+ ve2.delegateWasExtracted = true;
+ ve2.var.storage_class |= STC.scope_;
+ result = ve2;
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ exp.e1 = exp.e1.toLvalue(sc, null);
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+
+ if (!exp.e1.type)
+ {
+ exp.error("cannot take address of `%s`", exp.e1.toChars());
+ return setError();
+ }
+
+ bool hasOverloads;
+ if (auto f = isFuncAddress(exp, &hasOverloads))
+ {
+ if (!hasOverloads && f.checkForwardRef(exp.loc))
+ return setError();
+ }
+ else if (!exp.e1.type.deco)
+ {
+ if (exp.e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)exp.e1;
+ Declaration d = ve.var;
+ exp.error("forward reference to %s `%s`", d.kind(), d.toChars());
+ }
+ else
+ exp.error("forward reference to `%s`", exp.e1.toChars());
+ return setError();
+ }
+
+ exp.type = exp.e1.type.pointerTo();
+
+ // See if this should really be a delegate
+ if (exp.e1.op == TOK.dotVariable)
+ {
+ DotVarExp dve = cast(DotVarExp)exp.e1;
+ FuncDeclaration f = dve.var.isFuncDeclaration();
+ if (f)
+ {
+ f = f.toAliasFunc(); // FIXME, should see overloads
+ // https://issues.dlang.org/show_bug.cgi?id=1983
+ if (!dve.hasOverloads)
+ f.tookAddressOf++;
+
+ Expression e;
+ if (f.needThis())
+ e = new DelegateExp(exp.loc, dve.e1, f, dve.hasOverloads);
+ else // It is a function pointer. Convert &v.f() --> (v, &V.f())
+ e = new CommaExp(exp.loc, dve.e1, new AddrExp(exp.loc, new VarExp(exp.loc, f, dve.hasOverloads)));
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ // Look for misaligned pointer in @safe mode
+ if (checkUnsafeAccess(sc, dve, !exp.type.isMutable(), true))
+ return setError();
+
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (VarDeclaration v = expToVariable(dve.e1))
+ {
+ if (!checkAddressVar(sc, exp.e1, v))
+ return setError();
+ }
+ }
+ }
+ else if (exp.e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)exp.e1;
+ VarDeclaration v = ve.var.isVarDeclaration();
+ if (v)
+ {
+ if (!checkAddressVar(sc, exp.e1, v))
+ return setError();
+
+ ve.checkPurity(sc, v);
+ }
+ FuncDeclaration f = ve.var.isFuncDeclaration();
+ if (f)
+ {
+ /* Because nested functions cannot be overloaded,
+ * mark here that we took its address because castTo()
+ * may not be called with an exact match.
+ */
+ if (!ve.hasOverloads || (f.isNested() && !f.needThis()))
+ f.tookAddressOf++;
+ if (f.isNested() && !f.needThis())
+ {
+ if (f.isFuncLiteralDeclaration())
+ {
+ if (!f.FuncDeclaration.isNested())
+ {
+ /* Supply a 'null' for a this pointer if no this is available
+ */
+ Expression e = new DelegateExp(exp.loc, new NullExp(exp.loc, Type.tnull), f, ve.hasOverloads);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+ Expression e = new DelegateExp(exp.loc, exp.e1, f, ve.hasOverloads);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ if (f.needThis())
+ {
+ if (hasThis(sc))
+ {
+ /* Should probably supply 'this' after overload resolution,
+ * not before.
+ */
+ Expression ethis = new ThisExp(exp.loc);
+ Expression e = new DelegateExp(exp.loc, ethis, f, ve.hasOverloads);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ if (sc.func && !sc.intypeof)
+ {
+ if (!(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("`this` reference necessary to take address of member `%s` in `@safe` function `%s`", f.toChars(), sc.func.toChars());
+ }
+ }
+ }
+ }
+ }
+ else if ((exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_) && global.params.useDIP1000 == FeatureState.enabled)
+ {
+ if (VarDeclaration v = expToVariable(exp.e1))
+ {
+ if (!checkAddressVar(sc, exp.e1, v))
+ return setError();
+ }
+ }
+ else if (exp.e1.op == TOK.call)
+ {
+ CallExp ce = cast(CallExp)exp.e1;
+ if (ce.e1.type.ty == Tfunction)
+ {
+ TypeFunction tf = cast(TypeFunction)ce.e1.type;
+ if (tf.isref && sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("cannot take address of `ref return` of `%s()` in `@safe` function `%s`",
+ ce.e1.toChars(), sc.func.toChars());
+ }
+ }
+ }
+ else if (exp.e1.op == TOK.index)
+ {
+ /* For:
+ * int[3] a;
+ * &a[i]
+ * check 'a' the same as for a regular variable
+ */
+ if (VarDeclaration v = expToVariable(exp.e1))
+ {
+ if (global.params.useDIP1000 == FeatureState.enabled && !checkAddressVar(sc, exp.e1, v))
+ return setError();
+
+ exp.e1.checkPurity(sc, v);
+ }
+ }
+ else if (wasCond)
+ {
+ /* a ? b : c was transformed to *(a ? &b : &c), but we still
+ * need to do safety checks
+ */
+ assert(exp.e1.op == TOK.star);
+ PtrExp pe = cast(PtrExp)exp.e1;
+ assert(pe.e1.op == TOK.question);
+ CondExp ce = cast(CondExp)pe.e1;
+ assert(ce.e1.op == TOK.address);
+ assert(ce.e2.op == TOK.address);
+
+ // Re-run semantic on the address expressions only
+ ce.e1.type = null;
+ ce.e1 = ce.e1.expressionSemantic(sc);
+ ce.e2.type = null;
+ ce.e2 = ce.e2.expressionSemantic(sc);
+ }
+ result = exp.optimize(WANTvalue);
+ }
+
+ override void visit(PtrExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("PtrExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb = exp.e1.type.toBasetype();
+ switch (tb.ty)
+ {
+ case Tpointer:
+ exp.type = (cast(TypePointer)tb).next;
+ break;
+
+ case Tsarray:
+ case Tarray:
+ if (isNonAssignmentArrayOp(exp.e1))
+ goto default;
+ exp.error("using `*` on an array is no longer supported; use `*(%s).ptr` instead", exp.e1.toChars());
+ exp.type = (cast(TypeArray)tb).next;
+ exp.e1 = exp.e1.castTo(sc, exp.type.pointerTo());
+ break;
+
+ case Terror:
+ return setError();
+
+ case Tnull:
+ exp.type = Type.tnoreturn; // typeof(*null) is bottom type
+ break;
+
+ default:
+ exp.error("can only `*` a pointer, not a `%s`", exp.e1.type.toChars());
+ goto case Terror;
+ }
+
+ if (exp.checkValue())
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(NegExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("NegExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ fix16997(sc, exp);
+ exp.type = exp.e1.type;
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp.e1))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.e1.checkNoBool())
+ return setError();
+ if (exp.e1.checkArithmetic() ||
+ exp.e1.checkSharedAccess(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(UAddExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("UAddExp::semantic('%s')\n", exp.toChars());
+ }
+ assert(!exp.type);
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ fix16997(sc, exp);
+ if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.e1.checkNoBool())
+ return setError();
+ if (exp.e1.checkArithmetic())
+ return setError();
+ if (exp.e1.checkSharedAccess(sc))
+ return setError();
+
+ result = exp.e1;
+ }
+
+ override void visit(ComExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ fix16997(sc, exp);
+ exp.type = exp.e1.type;
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp.e1))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.e1.checkNoBool())
+ return setError();
+ if (exp.e1.checkIntegral() ||
+ exp.e1.checkSharedAccess(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(NotExp e)
+ {
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ e.setNoderefOperand();
+
+ // Note there is no operator overload
+ if (Expression ex = unaSemantic(e, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e.e1.op == TOK.type)
+ e.e1 = resolveAliasThis(sc, e.e1);
+
+ e.e1 = resolveProperties(sc, e.e1);
+ e.e1 = e.e1.toBoolean(sc);
+ if (e.e1.type == Type.terror)
+ {
+ result = e.e1;
+ return;
+ }
+
+ if (!target.isVectorOpSupported(e.e1.type.toBasetype(), e.op))
+ {
+ result = e.incompatibleTypes();
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=13910
+ // Today NotExp can take an array as its operand.
+ if (checkNonAssignmentArrayOp(e.e1))
+ return setError();
+
+ e.type = Type.tbool;
+ result = e;
+ }
+
+ override void visit(DeleteExp exp)
+ {
+ if (!sc.isDeprecated)
+ {
+ // @@@DEPRECATED_2019-02@@@
+ // 1. Deprecation for 1 year
+ // 2. Error for 1 year
+ // 3. Removal of keyword, "delete" can be used for other identities
+ if (!exp.isRAII)
+ deprecation(exp.loc, "The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.");
+ }
+
+ if (Expression ex = unaSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ exp.e1 = resolveProperties(sc, exp.e1);
+ exp.e1 = exp.e1.modifiableLvalue(sc, null);
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ exp.type = Type.tvoid;
+
+ AggregateDeclaration ad = null;
+ Type tb = exp.e1.type.toBasetype();
+ switch (tb.ty)
+ {
+ case Tclass:
+ {
+ auto cd = (cast(TypeClass)tb).sym;
+ if (cd.isCOMinterface())
+ {
+ /* Because COM classes are deleted by IUnknown.Release()
+ */
+ exp.error("cannot `delete` instance of COM interface `%s`", cd.toChars());
+ return setError();
+ }
+ ad = cd;
+ break;
+ }
+ case Tpointer:
+ tb = (cast(TypePointer)tb).next.toBasetype();
+ if (tb.ty == Tstruct)
+ {
+ ad = (cast(TypeStruct)tb).sym;
+ semanticTypeInfo(sc, tb);
+ }
+ break;
+
+ case Tarray:
+ {
+ Type tv = tb.nextOf().baseElemOf();
+ if (tv.ty == Tstruct)
+ {
+ ad = (cast(TypeStruct)tv).sym;
+ if (ad.dtor)
+ semanticTypeInfo(sc, ad.type);
+ }
+ break;
+ }
+ default:
+ exp.error("cannot delete type `%s`", exp.e1.type.toChars());
+ return setError();
+ }
+
+ bool err = false;
+ if (ad)
+ {
+ if (ad.dtor)
+ {
+ err |= !ad.dtor.functionSemantic();
+ err |= exp.checkPurity(sc, ad.dtor);
+ err |= exp.checkSafety(sc, ad.dtor);
+ err |= exp.checkNogc(sc, ad.dtor);
+ }
+ if (err)
+ return setError();
+ }
+
+ if (!sc.intypeof && sc.func &&
+ !exp.isRAII &&
+ !(sc.flags & SCOPE.debug_) &&
+ sc.func.setUnsafe())
+ {
+ exp.error("`%s` is not `@safe` but is used in `@safe` function `%s`", exp.toChars(), sc.func.toChars());
+ err = true;
+ }
+ if (err)
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(CastExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CastExp::semantic('%s')\n", exp.toChars());
+ }
+ //static int x; assert(++x < 10);
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if ((sc && sc.flags & SCOPE.Cfile) &&
+ exp.to && exp.to.ty == Tident &&
+ (exp.e1.op == TOK.address || exp.e1.op == TOK.star ||
+ exp.e1.op == TOK.uadd || exp.e1.op == TOK.negate))
+ {
+ /* Ambiguous cases arise from CParser if type-name is just an identifier.
+ * ( identifier ) cast-expression
+ * If we determine that `identifier` is a variable, and cast-expression
+ * is one of the unary operators (& * + -), then rewrite this cast
+ * as a binary expression.
+ */
+ Loc loc = exp.loc;
+ Type t;
+ Expression e;
+ Dsymbol s;
+ exp.to.resolve(loc, sc, e, t, s);
+ if (e !is null)
+ {
+ if (auto ex = exp.e1.isAddrExp()) // (ident) &exp -> (ident & exp)
+ result = new AndExp(loc, e, ex.e1);
+ else if (auto ex = exp.e1.isPtrExp()) // (ident) *exp -> (ident * exp)
+ result = new MulExp(loc, e, ex.e1);
+ else if (auto ex = exp.e1.isUAddExp()) // (ident) +exp -> (ident + exp)
+ result = new AddExp(loc, e, ex.e1);
+ else if (auto ex = exp.e1.isNegExp()) // (ident) -exp -> (ident - exp)
+ result = new MinExp(loc, e, ex.e1);
+
+ assert(result);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+ }
+
+ if (exp.to)
+ {
+ exp.to = exp.to.typeSemantic(exp.loc, sc);
+ if (exp.to == Type.terror)
+ return setError();
+
+ if (!exp.to.hasPointers())
+ exp.setNoderefOperand();
+
+ // When e1 is a template lambda, this cast may instantiate it with
+ // the type 'to'.
+ exp.e1 = inferType(exp.e1, exp.to);
+ }
+
+ if (auto e = unaSemantic(exp, sc))
+ {
+ result = e;
+ return;
+ }
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (exp.e1.op == TOK.type)
+ exp.e1 = resolveAliasThis(sc, exp.e1);
+
+ auto e1x = resolveProperties(sc, exp.e1);
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ if (e1x.checkType())
+ return setError();
+ exp.e1 = e1x;
+
+ if (!exp.e1.type)
+ {
+ exp.error("cannot cast `%s`", exp.e1.toChars());
+ return setError();
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19954
+ if (exp.e1.type.ty == Ttuple)
+ {
+ TupleExp te = exp.e1.isTupleExp();
+ if (te.exps.dim == 1)
+ exp.e1 = (*te.exps)[0];
+ }
+
+ // only allow S(x) rewrite if cast specified S explicitly.
+ // See https://issues.dlang.org/show_bug.cgi?id=18545
+ const bool allowImplicitConstruction = exp.to !is null;
+
+ if (!exp.to) // Handle cast(const) and cast(immutable), etc.
+ {
+ exp.to = exp.e1.type.castMod(exp.mod);
+ exp.to = exp.to.typeSemantic(exp.loc, sc);
+
+ if (exp.to == Type.terror)
+ return setError();
+ }
+
+ if (exp.to.ty == Ttuple)
+ {
+ exp.error("cannot cast `%s` to tuple type `%s`", exp.e1.toChars(), exp.to.toChars());
+ return setError();
+ }
+
+ // cast(void) is used to mark e1 as unused, so it is safe
+ if (exp.to.ty == Tvoid)
+ {
+ exp.type = exp.to;
+ result = exp;
+ return;
+ }
+
+ if (!exp.to.equals(exp.e1.type) && exp.mod == cast(ubyte)~0)
+ {
+ if (Expression e = exp.op_overload(sc))
+ {
+ result = e.implicitCastTo(sc, exp.to);
+ return;
+ }
+ }
+
+ Type t1b = exp.e1.type.toBasetype();
+ Type tob = exp.to.toBasetype();
+
+ if (allowImplicitConstruction && tob.ty == Tstruct && !tob.equals(t1b))
+ {
+ /* Look to replace:
+ * cast(S)t
+ * with:
+ * S(t)
+ */
+
+ // Rewrite as to.call(e1)
+ Expression e = new TypeExp(exp.loc, exp.to);
+ e = new CallExp(exp.loc, e, exp.e1);
+ e = e.trySemantic(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+
+ if (!t1b.equals(tob) && (t1b.ty == Tarray || t1b.ty == Tsarray))
+ {
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+ }
+
+ // Look for casting to a vector type
+ if (tob.ty == Tvector && t1b.ty != Tvector)
+ {
+ result = new VectorExp(exp.loc, exp.e1, exp.to);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+
+ Expression ex = exp.e1.castTo(sc, exp.to);
+ if (ex.op == TOK.error)
+ {
+ result = ex;
+ return;
+ }
+
+ // Check for unsafe casts
+ if (!sc.intypeof &&
+ !(sc.flags & SCOPE.debug_) &&
+ !isSafeCast(ex, t1b, tob) &&
+ (!sc.func && sc.stc & STC.safe || sc.func && sc.func.setUnsafe()))
+ {
+ exp.error("cast from `%s` to `%s` not allowed in safe code", exp.e1.type.toChars(), exp.to.toChars());
+ return setError();
+ }
+
+ // `object.__ArrayCast` is a rewrite of an old runtime hook `_d_arraycast`. `_d_arraycast` was not built
+ // to handle certain casts. Those casts which `object.__ArrayCast` does not support are filtered out.
+ // See `e2ir.toElemCast` for other types of casts. If `object.__ArrayCast` is improved to support more
+ // casts these conditions and potentially some logic in `e2ir.toElemCast` can be removed.
+ if (tob.ty == Tarray)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=19840
+ if (auto ad = isAggregate(t1b))
+ {
+ if (ad.aliasthis)
+ {
+ Expression e = resolveAliasThis(sc, exp.e1);
+ e = new CastExp(exp.loc, e, exp.to);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ }
+
+ if(t1b.ty == Tarray && exp.e1.op != TOK.arrayLiteral && (sc.flags & SCOPE.ctfe) == 0)
+ {
+ auto tFrom = t1b.nextOf();
+ auto tTo = tob.nextOf();
+
+ // https://issues.dlang.org/show_bug.cgi?id=20130
+ if (exp.e1.op != TOK.string_ || !ex.isStringExp)
+ {
+ const uint fromSize = cast(uint)tFrom.size();
+ const uint toSize = cast(uint)tTo.size();
+
+ // If array element sizes do not match, we must adjust the dimensions
+ if (fromSize != toSize)
+ {
+ if (!verifyHookExist(exp.loc, *sc, Id.__ArrayCast, "casting array of structs"))
+ return setError();
+
+ // A runtime check is needed in case arrays don't line up. That check should
+ // be done in the implementation of `object.__ArrayCast`
+ if (toSize == 0 || (fromSize % toSize) != 0)
+ {
+ // lower to `object.__ArrayCast!(TFrom, TTo)(from)`
+
+ // fully qualify as `object.__ArrayCast`
+ Expression id = new IdentifierExp(exp.loc, Id.empty);
+ auto dotid = new DotIdExp(exp.loc, id, Id.object);
+
+ auto tiargs = new Objects();
+ tiargs.push(tFrom);
+ tiargs.push(tTo);
+ auto dt = new DotTemplateInstanceExp(exp.loc, dotid, Id.__ArrayCast, tiargs);
+
+ auto arguments = new Expressions();
+ arguments.push(exp.e1);
+ Expression ce = new CallExp(exp.loc, dt, arguments);
+
+ result = expressionSemantic(ce, sc);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ if (sc && sc.flags & SCOPE.Cfile)
+ {
+ /* C11 6.5.4-5: A cast does not yield an lvalue.
+ * So ensure that castTo does not strip away the cast so that this
+ * can be enforced in other semantic visitor methods.
+ */
+ if (!ex.isCastExp())
+ {
+ ex = new CastExp(exp.loc, ex, exp.to);
+ ex.type = exp.to;
+ }
+ }
+ result = ex;
+ }
+
+ override void visit(VectorExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("VectorExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ exp.type = exp.to.typeSemantic(exp.loc, sc);
+ if (exp.e1.op == TOK.error || exp.type.ty == Terror)
+ {
+ result = exp.e1;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ assert(tb.ty == Tvector);
+ TypeVector tv = cast(TypeVector)tb;
+ Type te = tv.elementType();
+ exp.dim = cast(int)(tv.size(exp.loc) / te.size(exp.loc));
+
+ bool checkElem(Expression elem)
+ {
+ if (elem.isConst() == 1)
+ return false;
+
+ exp.error("constant expression expected, not `%s`", elem.toChars());
+ return true;
+ }
+
+ exp.e1 = exp.e1.optimize(WANTvalue);
+ bool res;
+ if (exp.e1.op == TOK.arrayLiteral)
+ {
+ foreach (i; 0 .. exp.dim)
+ {
+ // Do not stop on first error - check all AST nodes even if error found
+ res |= checkElem(exp.e1.isArrayLiteralExp()[i]);
+ }
+ }
+ else if (exp.e1.type.ty == Tvoid)
+ checkElem(exp.e1);
+
+ result = res ? ErrorExp.get() : exp;
+ }
+
+ override void visit(VectorArrayExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("VectorArrayExp::semantic('%s')\n", e.toChars());
+ }
+ if (!e.type)
+ {
+ unaSemantic(e, sc);
+ e.e1 = resolveProperties(sc, e.e1);
+
+ if (e.e1.op == TOK.error)
+ {
+ result = e.e1;
+ return;
+ }
+ assert(e.e1.type.ty == Tvector);
+ e.type = e.e1.type.isTypeVector().basetype;
+ }
+ result = e;
+ }
+
+ override void visit(SliceExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("SliceExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ // operator overloading should be handled in ArrayExp already.
+ if (Expression ex = unaSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ exp.e1 = resolveProperties(sc, exp.e1);
+ if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
+ {
+ if (exp.lwr || exp.upr)
+ {
+ exp.error("cannot slice type `%s`", exp.e1.toChars());
+ return setError();
+ }
+ Expression e = new TypeExp(exp.loc, exp.e1.type.arrayOf());
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (!exp.lwr && !exp.upr)
+ {
+ if (exp.e1.op == TOK.arrayLiteral)
+ {
+ // Convert [a,b,c][] to [a,b,c]
+ Type t1b = exp.e1.type.toBasetype();
+ Expression e = exp.e1;
+ if (t1b.ty == Tsarray)
+ {
+ e = e.copy();
+ e.type = t1b.nextOf().arrayOf();
+ }
+ result = e;
+ return;
+ }
+ if (exp.e1.op == TOK.slice)
+ {
+ // Convert e[][] to e[]
+ SliceExp se = cast(SliceExp)exp.e1;
+ if (!se.lwr && !se.upr)
+ {
+ result = se;
+ return;
+ }
+ }
+ if (isArrayOpOperand(exp.e1))
+ {
+ // Convert (a[]+b[])[] to a[]+b[]
+ result = exp.e1;
+ return;
+ }
+ }
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (exp.e1.type.ty == Terror)
+ return setError();
+
+ Type t1b = exp.e1.type.toBasetype();
+ if (t1b.ty == Tpointer)
+ {
+ if (t1b.isPtrToFunction())
+ {
+ exp.error("cannot slice function pointer `%s`", exp.e1.toChars());
+ return setError();
+ }
+ if (!exp.lwr || !exp.upr)
+ {
+ exp.error("need upper and lower bound to slice pointer");
+ return setError();
+ }
+ if (sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("pointer slicing not allowed in safe functions");
+ return setError();
+ }
+ }
+ else if (t1b.ty == Tarray)
+ {
+ }
+ else if (t1b.ty == Tsarray)
+ {
+ if (!exp.arrayop && global.params.useDIP1000 == FeatureState.enabled)
+ {
+ /* Slicing a static array is like taking the address of it.
+ * Perform checks as if e[] was &e
+ */
+ if (VarDeclaration v = expToVariable(exp.e1))
+ {
+ if (exp.e1.op == TOK.dotVariable)
+ {
+ DotVarExp dve = cast(DotVarExp)exp.e1;
+ if ((dve.e1.op == TOK.this_ || dve.e1.op == TOK.super_) &&
+ !(v.storage_class & STC.ref_))
+ {
+ // because it's a class
+ v = null;
+ }
+ }
+
+ if (v && !checkAddressVar(sc, exp.e1, v))
+ return setError();
+ }
+ }
+ }
+ else if (t1b.ty == Ttuple)
+ {
+ if (!exp.lwr && !exp.upr)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (!exp.lwr || !exp.upr)
+ {
+ exp.error("need upper and lower bound to slice tuple");
+ return setError();
+ }
+ }
+ else if (t1b.ty == Tvector)
+ {
+ // Convert e1 to corresponding static array
+ TypeVector tv1 = cast(TypeVector)t1b;
+ t1b = tv1.basetype;
+ t1b = t1b.castMod(tv1.mod);
+ exp.e1.type = t1b;
+ }
+ else
+ {
+ exp.error("`%s` cannot be sliced with `[]`", t1b.ty == Tvoid ? exp.e1.toChars() : t1b.toChars());
+ return setError();
+ }
+
+ /* Run semantic on lwr and upr.
+ */
+ Scope* scx = sc;
+ if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple)
+ {
+ // Create scope for 'length' variable
+ ScopeDsymbol sym = new ArrayScopeSymbol(sc, exp);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+ }
+ if (exp.lwr)
+ {
+ if (t1b.ty == Ttuple)
+ sc = sc.startCTFE();
+ exp.lwr = exp.lwr.expressionSemantic(sc);
+ exp.lwr = resolveProperties(sc, exp.lwr);
+ if (t1b.ty == Ttuple)
+ sc = sc.endCTFE();
+ exp.lwr = exp.lwr.implicitCastTo(sc, Type.tsize_t);
+ }
+ if (exp.upr)
+ {
+ if (t1b.ty == Ttuple)
+ sc = sc.startCTFE();
+ exp.upr = exp.upr.expressionSemantic(sc);
+ exp.upr = resolveProperties(sc, exp.upr);
+ if (t1b.ty == Ttuple)
+ sc = sc.endCTFE();
+ exp.upr = exp.upr.implicitCastTo(sc, Type.tsize_t);
+ }
+ if (sc != scx)
+ sc = sc.pop();
+ if (exp.lwr && exp.lwr.type == Type.terror || exp.upr && exp.upr.type == Type.terror)
+ return setError();
+
+ if (t1b.ty == Ttuple)
+ {
+ exp.lwr = exp.lwr.ctfeInterpret();
+ exp.upr = exp.upr.ctfeInterpret();
+ uinteger_t i1 = exp.lwr.toUInteger();
+ uinteger_t i2 = exp.upr.toUInteger();
+
+ TupleExp te;
+ TypeTuple tup;
+ size_t length;
+ if (exp.e1.op == TOK.tuple) // slicing an expression tuple
+ {
+ te = cast(TupleExp)exp.e1;
+ tup = null;
+ length = te.exps.dim;
+ }
+ else if (exp.e1.op == TOK.type) // slicing a type tuple
+ {
+ te = null;
+ tup = cast(TypeTuple)t1b;
+ length = Parameter.dim(tup.arguments);
+ }
+ else
+ assert(0);
+
+ if (i2 < i1 || length < i2)
+ {
+ exp.error("string slice `[%llu .. %llu]` is out of bounds", i1, i2);
+ return setError();
+ }
+
+ size_t j1 = cast(size_t)i1;
+ size_t j2 = cast(size_t)i2;
+ Expression e;
+ if (exp.e1.op == TOK.tuple)
+ {
+ auto exps = new Expressions(j2 - j1);
+ for (size_t i = 0; i < j2 - j1; i++)
+ {
+ (*exps)[i] = (*te.exps)[j1 + i];
+ }
+ e = new TupleExp(exp.loc, te.e0, exps);
+ }
+ else
+ {
+ auto args = new Parameters();
+ args.reserve(j2 - j1);
+ for (size_t i = j1; i < j2; i++)
+ {
+ Parameter arg = Parameter.getNth(tup.arguments, i);
+ args.push(arg);
+ }
+ e = new TypeExp(exp.e1.loc, new TypeTuple(args));
+ }
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ exp.type = t1b.nextOf().arrayOf();
+ // Allow typedef[] -> typedef[]
+ if (exp.type.equals(t1b))
+ exp.type = exp.e1.type;
+
+ // We might know $ now
+ setLengthVarIfKnown(exp.lengthVar, t1b);
+
+ if (exp.lwr && exp.upr)
+ {
+ exp.lwr = exp.lwr.optimize(WANTvalue);
+ exp.upr = exp.upr.optimize(WANTvalue);
+
+ IntRange lwrRange = getIntRange(exp.lwr);
+ IntRange uprRange = getIntRange(exp.upr);
+
+ if (t1b.ty == Tsarray || t1b.ty == Tarray)
+ {
+ Expression el = new ArrayLengthExp(exp.loc, exp.e1);
+ el = el.expressionSemantic(sc);
+ el = el.optimize(WANTvalue);
+ if (el.op == TOK.int64)
+ {
+ // Array length is known at compile-time. Upper is in bounds if it fits length.
+ dinteger_t length = el.toInteger();
+ auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length));
+ exp.upperIsInBounds = bounds.contains(uprRange);
+ }
+ else if (exp.upr.op == TOK.int64 && exp.upr.toInteger() == 0)
+ {
+ // Upper slice expression is '0'. Value is always in bounds.
+ exp.upperIsInBounds = true;
+ }
+ else if (exp.upr.op == TOK.variable && (cast(VarExp)exp.upr).var.ident == Id.dollar)
+ {
+ // Upper slice expression is '$'. Value is always in bounds.
+ exp.upperIsInBounds = true;
+ }
+ }
+ else if (t1b.ty == Tpointer)
+ {
+ exp.upperIsInBounds = true;
+ }
+ else
+ assert(0);
+
+ exp.lowerIsLessThanUpper = (lwrRange.imax <= uprRange.imin);
+
+ //printf("upperIsInBounds = %d lowerIsLessThanUpper = %d\n", exp.upperIsInBounds, exp.lowerIsLessThanUpper);
+ }
+
+ result = exp;
+ }
+
+ override void visit(ArrayLengthExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ArrayLengthExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = unaSemantic(e, sc))
+ {
+ result = ex;
+ return;
+ }
+ e.e1 = resolveProperties(sc, e.e1);
+
+ e.type = Type.tsize_t;
+ result = e;
+ }
+
+ override void visit(ArrayExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("ArrayExp::semantic('%s')\n", exp.toChars());
+ }
+ assert(!exp.type);
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (isAggregate(exp.e1.type))
+ exp.error("no `[]` operator overload for type `%s`", exp.e1.type.toChars());
+ else if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
+ exp.error("static array of `%s` with multiple lengths not allowed", exp.e1.type.toChars());
+ else if (isIndexableNonAggregate(exp.e1.type))
+ exp.error("only one index allowed to index `%s`", exp.e1.type.toChars());
+ else
+ exp.error("cannot use `[]` operator on expression of type `%s`", exp.e1.type.toChars());
+
+ result = ErrorExp.get();
+ }
+
+ override void visit(DotExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DotExp::semantic('%s')\n", exp.toChars());
+ if (exp.type)
+ printf("\ttype = %s\n", exp.type.toChars());
+ }
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ exp.e2 = exp.e2.expressionSemantic(sc);
+
+ if (exp.e1.op == TOK.type)
+ {
+ result = exp.e2;
+ return;
+ }
+ if (exp.e2.op == TOK.type)
+ {
+ result = exp.e2;
+ return;
+ }
+ if (exp.e2.op == TOK.template_)
+ {
+ auto td = (cast(TemplateExp)exp.e2).td;
+ Expression e = new DotTemplateExp(exp.loc, exp.e1, td);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (!exp.type)
+ exp.type = exp.e2.type;
+ result = exp;
+ }
+
+ override void visit(CommaExp e)
+ {
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ // Allow `((a,b),(x,y))`
+ if (e.allowCommaExp)
+ {
+ CommaExp.allow(e.e1);
+ CommaExp.allow(e.e2);
+ }
+
+ if (Expression ex = binSemanticProp(e, sc))
+ {
+ result = ex;
+ return;
+ }
+ e.e1 = e.e1.addDtorHook(sc);
+
+ if (checkNonAssignmentArrayOp(e.e1))
+ return setError();
+
+ e.type = e.e2.type;
+ if (e.type is Type.tvoid)
+ discardValue(e.e1);
+ else if (!e.allowCommaExp && !e.isGenerated)
+ e.error("Using the result of a comma expression is not allowed");
+ result = e;
+ }
+
+ override void visit(IntervalExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("IntervalExp::semantic('%s')\n", e.toChars());
+ }
+ if (e.type)
+ {
+ result = e;
+ return;
+ }
+
+ Expression le = e.lwr;
+ le = le.expressionSemantic(sc);
+ le = resolveProperties(sc, le);
+
+ Expression ue = e.upr;
+ ue = ue.expressionSemantic(sc);
+ ue = resolveProperties(sc, ue);
+
+ if (le.op == TOK.error)
+ {
+ result = le;
+ return;
+ }
+ if (ue.op == TOK.error)
+ {
+ result = ue;
+ return;
+ }
+
+ e.lwr = le;
+ e.upr = ue;
+
+ e.type = Type.tvoid;
+ result = e;
+ }
+
+ override void visit(DelegatePtrExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DelegatePtrExp::semantic('%s')\n", e.toChars());
+ }
+ if (!e.type)
+ {
+ unaSemantic(e, sc);
+ e.e1 = resolveProperties(sc, e.e1);
+
+ if (e.e1.op == TOK.error)
+ {
+ result = e.e1;
+ return;
+ }
+ e.type = Type.tvoidptr;
+ }
+ result = e;
+ }
+
+ override void visit(DelegateFuncptrExp e)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("DelegateFuncptrExp::semantic('%s')\n", e.toChars());
+ }
+ if (!e.type)
+ {
+ unaSemantic(e, sc);
+ e.e1 = resolveProperties(sc, e.e1);
+ if (e.e1.op == TOK.error)
+ {
+ result = e.e1;
+ return;
+ }
+ e.type = e.e1.type.nextOf().pointerTo();
+ }
+ result = e;
+ }
+
+ override void visit(IndexExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("IndexExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ // operator overloading should be handled in ArrayExp already.
+ if (!exp.e1.type)
+ exp.e1 = exp.e1.expressionSemantic(sc);
+ assert(exp.e1.type); // semantic() should already be run on it
+ if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
+ {
+ exp.e2 = exp.e2.expressionSemantic(sc);
+ exp.e2 = resolveProperties(sc, exp.e2);
+ Type nt;
+ if (exp.e2.op == TOK.type)
+ nt = new TypeAArray(exp.e1.type, exp.e2.type);
+ else
+ nt = new TypeSArray(exp.e1.type, exp.e2);
+ Expression e = new TypeExp(exp.loc, nt);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (exp.e1.type.ty == Terror)
+ return setError();
+
+ // Note that unlike C we do not implement the int[ptr]
+
+ Type t1b = exp.e1.type.toBasetype();
+
+ if (t1b.ty == Tvector)
+ {
+ // Convert e1 to corresponding static array
+ TypeVector tv1 = cast(TypeVector)t1b;
+ t1b = tv1.basetype;
+ t1b = t1b.castMod(tv1.mod);
+ exp.e1.type = t1b;
+ }
+
+ /* Run semantic on e2
+ */
+ Scope* scx = sc;
+ if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple)
+ {
+ // Create scope for 'length' variable
+ ScopeDsymbol sym = new ArrayScopeSymbol(sc, exp);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+ }
+ if (t1b.ty == Ttuple)
+ sc = sc.startCTFE();
+ exp.e2 = exp.e2.expressionSemantic(sc);
+ exp.e2 = resolveProperties(sc, exp.e2);
+ if (t1b.ty == Ttuple)
+ sc = sc.endCTFE();
+ if (exp.e2.op == TOK.tuple)
+ {
+ TupleExp te = cast(TupleExp)exp.e2;
+ if (te.exps && te.exps.dim == 1)
+ exp.e2 = Expression.combine(te.e0, (*te.exps)[0]); // bug 4444 fix
+ }
+ if (sc != scx)
+ sc = sc.pop();
+ if (exp.e2.type == Type.terror)
+ return setError();
+
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+
+ switch (t1b.ty)
+ {
+ case Tpointer:
+ if (t1b.isPtrToFunction())
+ {
+ exp.error("cannot index function pointer `%s`", exp.e1.toChars());
+ return setError();
+ }
+ exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t);
+ if (exp.e2.type == Type.terror)
+ return setError();
+ exp.e2 = exp.e2.optimize(WANTvalue);
+ if (exp.e2.op == TOK.int64 && exp.e2.toInteger() == 0)
+ {
+ }
+ else if (sc.func && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("safe function `%s` cannot index pointer `%s`", sc.func.toPrettyChars(), exp.e1.toChars());
+ return setError();
+ }
+ exp.type = (cast(TypeNext)t1b).next;
+ break;
+
+ case Tarray:
+ exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t);
+ if (exp.e2.type == Type.terror)
+ return setError();
+ exp.type = (cast(TypeNext)t1b).next;
+ break;
+
+ case Tsarray:
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t);
+ if (exp.e2.type == Type.terror)
+ return setError();
+ exp.type = t1b.nextOf();
+ break;
+ }
+ case Taarray:
+ {
+ TypeAArray taa = cast(TypeAArray)t1b;
+ /* We can skip the implicit conversion if they differ only by
+ * constness
+ * https://issues.dlang.org/show_bug.cgi?id=2684
+ * see also bug https://issues.dlang.org/show_bug.cgi?id=2954 b
+ */
+ if (!arrayTypeCompatibleWithoutCasting(exp.e2.type, taa.index))
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, taa.index); // type checking
+ if (exp.e2.type == Type.terror)
+ return setError();
+ }
+
+ semanticTypeInfo(sc, taa);
+
+ exp.type = taa.next;
+ break;
+ }
+ case Ttuple:
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t);
+ if (exp.e2.type == Type.terror)
+ return setError();
+
+ exp.e2 = exp.e2.ctfeInterpret();
+ uinteger_t index = exp.e2.toUInteger();
+
+ TupleExp te;
+ TypeTuple tup;
+ size_t length;
+ if (exp.e1.op == TOK.tuple)
+ {
+ te = cast(TupleExp)exp.e1;
+ tup = null;
+ length = te.exps.dim;
+ }
+ else if (exp.e1.op == TOK.type)
+ {
+ te = null;
+ tup = cast(TypeTuple)t1b;
+ length = Parameter.dim(tup.arguments);
+ }
+ else
+ assert(0);
+
+ if (length <= index)
+ {
+ exp.error("array index `[%llu]` is outside array bounds `[0 .. %llu]`", index, cast(ulong)length);
+ return setError();
+ }
+ Expression e;
+ if (exp.e1.op == TOK.tuple)
+ {
+ e = (*te.exps)[cast(size_t)index];
+ e = Expression.combine(te.e0, e);
+ }
+ else
+ e = new TypeExp(exp.e1.loc, Parameter.getNth(tup.arguments, cast(size_t)index).type);
+ result = e;
+ return;
+ }
+ default:
+ exp.error("`%s` must be an array or pointer type, not `%s`", exp.e1.toChars(), exp.e1.type.toChars());
+ return setError();
+ }
+
+ // We might know $ now
+ setLengthVarIfKnown(exp.lengthVar, t1b);
+
+ if (t1b.ty == Tsarray || t1b.ty == Tarray)
+ {
+ Expression el = new ArrayLengthExp(exp.loc, exp.e1);
+ el = el.expressionSemantic(sc);
+ el = el.optimize(WANTvalue);
+ if (el.op == TOK.int64)
+ {
+ exp.e2 = exp.e2.optimize(WANTvalue);
+ dinteger_t length = el.toInteger();
+ if (length)
+ {
+ auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length - 1));
+ exp.indexIsInBounds = bounds.contains(getIntRange(exp.e2));
+ }
+ }
+ }
+
+ result = exp;
+ }
+
+ override void visit(PostExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("PostExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemantic(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e1x = resolveProperties(sc, exp.e1);
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ exp.e1 = e1x;
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.checkReadModifyWrite(exp.op))
+ return setError();
+
+ if (exp.e1.op == TOK.slice)
+ {
+ const(char)* s = exp.op == TOK.plusPlus ? "increment" : "decrement";
+ exp.error("cannot post-%s array slice `%s`, use pre-%s instead", s, exp.e1.toChars(), s);
+ return setError();
+ }
+
+ Type t1 = exp.e1.type.toBasetype();
+ if (t1.ty == Tclass || t1.ty == Tstruct || exp.e1.op == TOK.arrayLength)
+ {
+ /* Check for operator overloading,
+ * but rewrite in terms of ++e instead of e++
+ */
+
+ /* If e1 is not trivial, take a reference to it
+ */
+ Expression de = null;
+ if (exp.e1.op != TOK.variable && exp.e1.op != TOK.arrayLength)
+ {
+ // ref v = e1;
+ auto v = copyToTemp(STC.ref_, "__postref", exp.e1);
+ de = new DeclarationExp(exp.loc, v);
+ exp.e1 = new VarExp(exp.e1.loc, v);
+ }
+
+ /* Rewrite as:
+ * auto tmp = e1; ++e1; tmp
+ */
+ auto tmp = copyToTemp(0, "__pitmp", exp.e1);
+ Expression ea = new DeclarationExp(exp.loc, tmp);
+
+ Expression eb = exp.e1.syntaxCopy();
+ eb = new PreExp(exp.op == TOK.plusPlus ? TOK.prePlusPlus : TOK.preMinusMinus, exp.loc, eb);
+
+ Expression ec = new VarExp(exp.loc, tmp);
+
+ // Combine de,ea,eb,ec
+ if (de)
+ ea = new CommaExp(exp.loc, de, ea);
+ e = new CommaExp(exp.loc, ea, eb);
+ e = new CommaExp(exp.loc, e, ec);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true);
+
+ e = exp;
+ if (exp.e1.checkScalar() ||
+ exp.e1.checkSharedAccess(sc))
+ return setError();
+ if (exp.e1.checkNoBool())
+ return setError();
+
+ if (exp.e1.type.ty == Tpointer)
+ e = scaleFactor(exp, sc);
+ else
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type);
+ e.type = exp.e1.type;
+ result = e;
+ }
+
+ override void visit(PreExp exp)
+ {
+ Expression e = exp.op_overload(sc);
+ // printf("PreExp::semantic('%s')\n", toChars());
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ // Rewrite as e1+=1 or e1-=1
+ if (exp.op == TOK.prePlusPlus)
+ e = new AddAssignExp(exp.loc, exp.e1, IntegerExp.literal!1);
+ else
+ e = new MinAssignExp(exp.loc, exp.e1, IntegerExp.literal!1);
+ result = e.expressionSemantic(sc);
+ }
+
+ /*
+ * Get the expression initializer for a specific struct
+ *
+ * Params:
+ * sd = the struct for which the expression initializer is needed
+ * loc = the location of the initializer
+ * sc = the scope where the expression is located
+ * t = the type of the expression
+ *
+ * Returns:
+ * The expression initializer or error expression if any errors occured
+ */
+ private Expression getInitExp(StructDeclaration sd, Loc loc, Scope* sc, Type t)
+ {
+ if (sd.zeroInit && !sd.isNested())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14606
+ // Always use BlitExp for the special expression: (struct = 0)
+ return IntegerExp.literal!0;
+ }
+
+ if (sd.isNested())
+ {
+ auto sle = new StructLiteralExp(loc, sd, null, t);
+ if (!sd.fill(loc, sle.elements, true))
+ return ErrorExp.get();
+ if (checkFrameAccess(loc, sc, sd, sle.elements.dim))
+ return ErrorExp.get();
+
+ sle.type = t;
+ return sle;
+ }
+
+ return t.defaultInit(loc);
+ }
+
+ override void visit(AssignExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("AssignExp::semantic('%s')\n", exp.toChars());
+ }
+ //printf("exp.e1.op = %d, '%s'\n", exp.e1.op, Token.toChars(exp.e1.op));
+ //printf("exp.e2.op = %d, '%s'\n", exp.e2.op, Token.toChars(exp.e2.op));
+
+ void setResult(Expression e, int line = __LINE__)
+ {
+ //printf("line %d\n", line);
+ result = e;
+ }
+
+ if (exp.type)
+ {
+ return setResult(exp);
+ }
+
+ Expression e1old = exp.e1;
+
+ if (auto e2comma = exp.e2.isCommaExp())
+ {
+ if (!e2comma.isGenerated)
+ exp.error("Using the result of a comma expression is not allowed");
+
+ /* Rewrite to get rid of the comma from rvalue
+ * e1=(e0,e2) => e0,(e1=e2)
+ */
+ Expression e0;
+ exp.e2 = Expression.extractLast(e2comma, e0);
+ Expression e = Expression.combine(e0, exp);
+ return setResult(e.expressionSemantic(sc));
+ }
+
+ /* Look for operator overloading of a[arguments] = e2.
+ * Do it before e1.expressionSemantic() otherwise the ArrayExp will have been
+ * converted to unary operator overloading already.
+ */
+ if (auto ae = exp.e1.isArrayExp())
+ {
+ Expression res;
+
+ ae.e1 = ae.e1.expressionSemantic(sc);
+ ae.e1 = resolveProperties(sc, ae.e1);
+ Expression ae1old = ae.e1;
+
+ const(bool) maybeSlice =
+ (ae.arguments.dim == 0 ||
+ ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
+
+ IntervalExp ie = null;
+ if (maybeSlice && ae.arguments.dim)
+ {
+ assert((*ae.arguments)[0].op == TOK.interval);
+ ie = cast(IntervalExp)(*ae.arguments)[0];
+ }
+ while (true)
+ {
+ if (ae.e1.op == TOK.error)
+ return setResult(ae.e1);
+
+ Expression e0 = null;
+ Expression ae1save = ae.e1;
+ ae.lengthVar = null;
+
+ Type t1b = ae.e1.type.toBasetype();
+ AggregateDeclaration ad = isAggregate(t1b);
+ if (!ad)
+ break;
+ if (search_function(ad, Id.indexass))
+ {
+ // Deal with $
+ res = resolveOpDollar(sc, ae, &e0);
+ if (!res) // a[i..j] = e2 might be: a.opSliceAssign(e2, i, j)
+ goto Lfallback;
+ if (res.op == TOK.error)
+ return setResult(res);
+
+ res = exp.e2.expressionSemantic(sc);
+ if (res.op == TOK.error)
+ return setResult(res);
+ exp.e2 = res;
+
+ /* Rewrite (a[arguments] = e2) as:
+ * a.opIndexAssign(e2, arguments)
+ */
+ Expressions* a = ae.arguments.copy();
+ a.insert(0, exp.e2);
+ res = new DotIdExp(exp.loc, ae.e1, Id.indexass);
+ res = new CallExp(exp.loc, res, a);
+ if (maybeSlice) // a[] = e2 might be: a.opSliceAssign(e2)
+ res = res.trySemantic(sc);
+ else
+ res = res.expressionSemantic(sc);
+ if (res)
+ return setResult(Expression.combine(e0, res));
+ }
+
+ Lfallback:
+ if (maybeSlice && search_function(ad, Id.sliceass))
+ {
+ // Deal with $
+ res = resolveOpDollar(sc, ae, ie, &e0);
+ if (res.op == TOK.error)
+ return setResult(res);
+
+ res = exp.e2.expressionSemantic(sc);
+ if (res.op == TOK.error)
+ return setResult(res);
+
+ exp.e2 = res;
+
+ /* Rewrite (a[i..j] = e2) as:
+ * a.opSliceAssign(e2, i, j)
+ */
+ auto a = new Expressions();
+ a.push(exp.e2);
+ if (ie)
+ {
+ a.push(ie.lwr);
+ a.push(ie.upr);
+ }
+ res = new DotIdExp(exp.loc, ae.e1, Id.sliceass);
+ res = new CallExp(exp.loc, res, a);
+ res = res.expressionSemantic(sc);
+ return setResult(Expression.combine(e0, res));
+ }
+
+ // No operator overloading member function found yet, but
+ // there might be an alias this to try.
+ if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+ {
+ /* Rewrite (a[arguments] op e2) as:
+ * a.aliasthis[arguments] op e2
+ */
+ ae.e1 = resolveAliasThis(sc, ae1save, true);
+ if (ae.e1)
+ continue;
+ }
+ break;
+ }
+ ae.e1 = ae1old; // recovery
+ ae.lengthVar = null;
+ }
+
+ /* Run this.e1 semantic.
+ */
+ {
+ Expression e1x = exp.e1;
+
+ /* With UFCS, e.f = value
+ * Could mean:
+ * .f(e, value)
+ * or:
+ * .f(e) = value
+ */
+ if (auto dti = e1x.isDotTemplateInstanceExp())
+ {
+ Expression e = dti.semanticY(sc, 1);
+ if (!e)
+ {
+ return setResult(resolveUFCSProperties(sc, e1x, exp.e2));
+ }
+
+ e1x = e;
+ }
+ else if (auto die = e1x.isDotIdExp())
+ {
+ Expression e = die.semanticY(sc, 1);
+ if (e && isDotOpDispatch(e))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=19687
+ *
+ * On this branch, e2 is semantically analyzed in resolvePropertiesX,
+ * but that call is done with gagged errors. That is the only time when
+ * semantic gets ran on e2, that is why the error never gets to be printed.
+ * In order to make sure that UFCS is tried with correct parameters, e2
+ * needs to have semantic ran on it.
+ */
+ exp.e2 = exp.e2.expressionSemantic(sc);
+ uint errors = global.startGagging();
+ e = resolvePropertiesX(sc, e, exp.e2);
+ if (global.endGagging(errors))
+ e = null; /* fall down to UFCS */
+ else
+ return setResult(e);
+ }
+ if (!e)
+ return setResult(resolveUFCSProperties(sc, e1x, exp.e2));
+ e1x = e;
+ }
+ else
+ {
+ if (auto se = e1x.isSliceExp())
+ se.arrayop = true;
+
+ e1x = e1x.expressionSemantic(sc);
+ }
+
+ /* We have f = value.
+ * Could mean:
+ * f(value)
+ * or:
+ * f() = value
+ */
+ if (Expression e = resolvePropertiesX(sc, e1x, exp.e2))
+ return setResult(e);
+
+ if (e1x.checkRightThis(sc))
+ {
+ return setError();
+ }
+ exp.e1 = e1x;
+ assert(exp.e1.type);
+ }
+ Type t1 = exp.e1.type.toBasetype();
+
+ /* Run this.e2 semantic.
+ * Different from other binary expressions, the analysis of e2
+ * depends on the result of e1 in assignments.
+ */
+ {
+ Expression e2x = inferType(exp.e2, t1.baseElemOf());
+ e2x = e2x.expressionSemantic(sc);
+ e2x = resolveProperties(sc, e2x);
+ if (e2x.op == TOK.type)
+ e2x = resolveAliasThis(sc, e2x); //https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e2x.op == TOK.error)
+ return setResult(e2x);
+ // We skip checking the value for structs/classes as these might have
+ // an opAssign defined.
+ if ((t1.ty != Tstruct && t1.ty != Tclass && e2x.checkValue()) ||
+ e2x.checkSharedAccess(sc))
+ return setError();
+ exp.e2 = e2x;
+ }
+
+ /* Rewrite tuple assignment as a tuple of assignments.
+ */
+ {
+ Expression e2x = exp.e2;
+
+ Ltupleassign:
+ if (exp.e1.op == TOK.tuple && e2x.op == TOK.tuple)
+ {
+ TupleExp tup1 = cast(TupleExp)exp.e1;
+ TupleExp tup2 = cast(TupleExp)e2x;
+ size_t dim = tup1.exps.dim;
+ Expression e = null;
+ if (dim != tup2.exps.dim)
+ {
+ exp.error("mismatched tuple lengths, %d and %d", cast(int)dim, cast(int)tup2.exps.dim);
+ return setError();
+ }
+ if (dim == 0)
+ {
+ e = IntegerExp.literal!0;
+ e = new CastExp(exp.loc, e, Type.tvoid); // avoid "has no effect" error
+ e = Expression.combine(tup1.e0, tup2.e0, e);
+ }
+ else
+ {
+ auto exps = new Expressions(dim);
+ for (size_t i = 0; i < dim; i++)
+ {
+ Expression ex1 = (*tup1.exps)[i];
+ Expression ex2 = (*tup2.exps)[i];
+ (*exps)[i] = new AssignExp(exp.loc, ex1, ex2);
+ }
+ e = new TupleExp(exp.loc, Expression.combine(tup1.e0, tup2.e0), exps);
+ }
+ return setResult(e.expressionSemantic(sc));
+ }
+
+ /* Look for form: e1 = e2.aliasthis.
+ */
+ if (exp.e1.op == TOK.tuple)
+ {
+ TupleDeclaration td = isAliasThisTuple(e2x);
+ if (!td)
+ goto Lnomatch;
+
+ assert(exp.e1.type.ty == Ttuple);
+ TypeTuple tt = cast(TypeTuple)exp.e1.type;
+
+ Expression e0;
+ Expression ev = extractSideEffect(sc, "__tup", e0, e2x);
+
+ auto iexps = new Expressions();
+ iexps.push(ev);
+ for (size_t u = 0; u < iexps.dim; u++)
+ {
+ Lexpand:
+ Expression e = (*iexps)[u];
+
+ Parameter arg = Parameter.getNth(tt.arguments, u);
+ //printf("[%d] iexps.dim = %d, ", u, iexps.dim);
+ //printf("e = (%s %s, %s), ", Token::tochars[e.op], e.toChars(), e.type.toChars());
+ //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
+
+ if (!arg || !e.type.implicitConvTo(arg.type))
+ {
+ // expand initializer to tuple
+ if (expandAliasThisTuples(iexps, u) != -1)
+ {
+ if (iexps.dim <= u)
+ break;
+ goto Lexpand;
+ }
+ goto Lnomatch;
+ }
+ }
+ e2x = new TupleExp(e2x.loc, e0, iexps);
+ e2x = e2x.expressionSemantic(sc);
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+ // Do not need to overwrite this.e2
+ goto Ltupleassign;
+ }
+ Lnomatch:
+ }
+
+ /* Inside constructor, if this is the first assignment of object field,
+ * rewrite this to initializing the field.
+ */
+ if (exp.op == TOK.assign
+ && exp.e1.checkModifiable(sc) == Modifiable.initialization)
+ {
+ //printf("[%s] change to init - %s\n", exp.loc.toChars(), exp.toChars());
+ auto t = exp.type;
+ exp = new ConstructExp(exp.loc, exp.e1, exp.e2);
+ exp.type = t;
+
+ // https://issues.dlang.org/show_bug.cgi?id=13515
+ // set Index::modifiable flag for complex AA element initialization
+ if (auto ie1 = exp.e1.isIndexExp())
+ {
+ Expression e1x = ie1.markSettingAAElem();
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ }
+ }
+ else if (exp.op == TOK.construct && exp.e1.op == TOK.variable &&
+ (cast(VarExp)exp.e1).var.storage_class & (STC.out_ | STC.ref_))
+ {
+ exp.memset = MemorySet.referenceInit;
+ }
+
+ if (exp.op == TOK.assign) // skip TOK.blit and TOK.construct, which are initializations
+ {
+ exp.e1.checkSharedAccess(sc);
+ checkUnsafeAccess(sc, exp.e1, false, true);
+ }
+
+ checkUnsafeAccess(sc, exp.e2, true, true); // Initializer must always be checked
+
+ /* If it is an assignment from a 'foreign' type,
+ * check for operator overloading.
+ */
+ if (exp.memset == MemorySet.referenceInit)
+ {
+ // If this is an initialization of a reference,
+ // do nothing
+ }
+ else if (t1.ty == Tstruct)
+ {
+ auto e1x = exp.e1;
+ auto e2x = exp.e2;
+ auto sd = (cast(TypeStruct)t1).sym;
+
+ if (exp.op == TOK.construct)
+ {
+ Type t2 = e2x.type.toBasetype();
+ if (t2.ty == Tstruct && sd == (cast(TypeStruct)t2).sym)
+ {
+ sd.size(exp.loc);
+ if (sd.sizeok != Sizeok.done)
+ return setError();
+ if (!sd.ctor)
+ sd.ctor = sd.searchCtor();
+
+ // https://issues.dlang.org/show_bug.cgi?id=15661
+ // Look for the form from last of comma chain.
+ auto e2y = lastComma(e2x);
+
+ CallExp ce = (e2y.op == TOK.call) ? cast(CallExp)e2y : null;
+ DotVarExp dve = (ce && ce.e1.op == TOK.dotVariable)
+ ? cast(DotVarExp)ce.e1 : null;
+ if (sd.ctor && ce && dve && dve.var.isCtorDeclaration() &&
+ // https://issues.dlang.org/show_bug.cgi?id=19389
+ dve.e1.op != TOK.dotVariable &&
+ e2y.type.implicitConvTo(t1))
+ {
+ /* Look for form of constructor call which is:
+ * __ctmp.ctor(arguments...)
+ */
+
+ /* Before calling the constructor, initialize
+ * variable with a bit copy of the default
+ * initializer
+ */
+ Expression einit = getInitExp(sd, exp.loc, sc, t1);
+ if (einit.op == TOK.error)
+ {
+ result = einit;
+ return;
+ }
+
+ auto ae = new BlitExp(exp.loc, exp.e1, einit);
+ ae.type = e1x.type;
+
+ /* Replace __ctmp being constructed with e1.
+ * We need to copy constructor call expression,
+ * because it may be used in other place.
+ */
+ auto dvx = cast(DotVarExp)dve.copy();
+ dvx.e1 = e1x;
+ auto cx = cast(CallExp)ce.copy();
+ cx.e1 = dvx;
+ if (checkConstructorEscape(sc, cx, false))
+ return setError();
+
+ Expression e0;
+ Expression.extractLast(e2x, e0);
+
+ auto e = Expression.combine(e0, ae, cx);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=21586
+ // Rewrite CondExp or e1 will miss direct construction, e.g.
+ // e1 = a ? S(1) : ...; -> AST: e1 = a ? (S(0)).this(1) : ...;
+ // a temporary created and an extra destructor call.
+ // AST will be rewritten to:
+ // a ? e1 = 0, e1.this(1) : ...; -> blitting plus construction
+ if (e2x.op == TOK.question)
+ {
+ /* Rewrite as:
+ * a ? e1 = b : e1 = c;
+ */
+ CondExp econd = cast(CondExp)e2x;
+ Expression ea1 = new ConstructExp(econd.e1.loc, e1x, econd.e1);
+ Expression ea2 = new ConstructExp(econd.e2.loc, e1x, econd.e2);
+ Expression e = new CondExp(exp.loc, econd.econd, ea1, ea2);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ if (sd.postblit || sd.hasCopyCtor)
+ {
+ /* We have a copy constructor for this
+ */
+
+ if (e2x.isLvalue())
+ {
+ if (sd.hasCopyCtor)
+ {
+ /* Rewrite as:
+ * e1 = init, e1.copyCtor(e2);
+ */
+ Expression einit = new BlitExp(exp.loc, exp.e1, getInitExp(sd, exp.loc, sc, t1));
+ einit.type = e1x.type;
+
+ Expression e;
+ e = new DotIdExp(exp.loc, e1x, Id.ctor);
+ e = new CallExp(exp.loc, e, e2x);
+ e = new CommaExp(exp.loc, einit, e);
+
+ //printf("e: %s\n", e.toChars());
+
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ else
+ {
+ if (!e2x.type.implicitConvTo(e1x.type))
+ {
+ exp.error("conversion error from `%s` to `%s`",
+ e2x.type.toChars(), e1x.type.toChars());
+ return setError();
+ }
+
+ /* Rewrite as:
+ * (e1 = e2).postblit();
+ *
+ * Blit assignment e1 = e2 returns a reference to the original e1,
+ * then call the postblit on it.
+ */
+ Expression e = e1x.copy();
+ e.type = e.type.mutableOf();
+ if (e.type.isShared && !sd.type.isShared)
+ e.type = e.type.unSharedOf();
+ e = new BlitExp(exp.loc, e, e2x);
+ e = new DotVarExp(exp.loc, e, sd.postblit, false);
+ e = new CallExp(exp.loc, e);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+ }
+ else
+ {
+ /* The struct value returned from the function is transferred
+ * so should not call the destructor on it.
+ */
+ e2x = valueNoDtor(e2x);
+ }
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19251
+ // if e2 cannot be converted to e1.type, maybe there is an alias this
+ if (!e2x.implicitConvTo(t1))
+ {
+ AggregateDeclaration ad2 = isAggregate(e2x.type);
+ if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type))
+ {
+ /* Rewrite (e1 op e2) as:
+ * (e1 op e2.aliasthis)
+ */
+ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident);
+ result = exp.expressionSemantic(sc);
+ return;
+ }
+ }
+ }
+ else if (!e2x.implicitConvTo(t1))
+ {
+ sd.size(exp.loc);
+ if (sd.sizeok != Sizeok.done)
+ return setError();
+ if (!sd.ctor)
+ sd.ctor = sd.searchCtor();
+
+ if (sd.ctor)
+ {
+ /* Look for implicit constructor call
+ * Rewrite as:
+ * e1 = init, e1.ctor(e2)
+ */
+
+ /* Fix Issue 5153 : https://issues.dlang.org/show_bug.cgi?id=5153
+ * Using `new` to initialize a struct object is a common mistake, but
+ * the error message from the compiler is not very helpful in that
+ * case. If exp.e2 is a NewExp and the type of new is the same as
+ * the type as exp.e1 (struct in this case), then we know for sure
+ * that the user wants to instantiate a struct. This is done to avoid
+ * issuing an error when the user actually wants to call a constructor
+ * which receives a class object.
+ *
+ * Foo f = new Foo2(0); is a valid expression if Foo has a constructor
+ * which receives an instance of a Foo2 class
+ */
+ if (exp.e2.op == TOK.new_)
+ {
+ auto newExp = cast(NewExp)(exp.e2);
+ if (newExp.newtype && newExp.newtype == t1)
+ {
+ error(exp.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`",
+ newExp.toChars(), newExp.type.toChars(), t1.toChars());
+ errorSupplemental(exp.loc, "Perhaps remove the `new` keyword?");
+ return setError();
+ }
+ }
+
+ Expression einit = new BlitExp(exp.loc, e1x, getInitExp(sd, exp.loc, sc, t1));
+ einit.type = e1x.type;
+
+ Expression e;
+ e = new DotIdExp(exp.loc, e1x, Id.ctor);
+ e = new CallExp(exp.loc, e, e2x);
+ e = new CommaExp(exp.loc, einit, e);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ if (search_function(sd, Id.call))
+ {
+ /* Look for static opCall
+ * https://issues.dlang.org/show_bug.cgi?id=2702
+ * Rewrite as:
+ * e1 = typeof(e1).opCall(arguments)
+ */
+ e2x = typeDotIdExp(e2x.loc, e1x.type, Id.call);
+ e2x = new CallExp(exp.loc, e2x, exp.e2);
+
+ e2x = e2x.expressionSemantic(sc);
+ e2x = resolveProperties(sc, e2x);
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+ if (e2x.checkValue() || e2x.checkSharedAccess(sc))
+ return setError();
+ }
+ }
+ else // https://issues.dlang.org/show_bug.cgi?id=11355
+ {
+ AggregateDeclaration ad2 = isAggregate(e2x.type);
+ if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type))
+ {
+ /* Rewrite (e1 op e2) as:
+ * (e1 op e2.aliasthis)
+ */
+ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident);
+ result = exp.expressionSemantic(sc);
+ return;
+ }
+ }
+ }
+ else if (exp.op == TOK.assign)
+ {
+ if (e1x.op == TOK.index && (cast(IndexExp)e1x).e1.type.toBasetype().ty == Taarray)
+ {
+ /*
+ * Rewrite:
+ * aa[key] = e2;
+ * as:
+ * ref __aatmp = aa;
+ * ref __aakey = key;
+ * ref __aaval = e2;
+ * (__aakey in __aatmp
+ * ? __aatmp[__aakey].opAssign(__aaval)
+ * : ConstructExp(__aatmp[__aakey], __aaval));
+ */
+ // ensure we keep the expr modifiable
+ Expression esetting = (cast(IndexExp)e1x).markSettingAAElem();
+ if (esetting.op == TOK.error)
+ {
+ result = esetting;
+ return;
+ }
+ assert(esetting.op == TOK.index);
+ IndexExp ie = cast(IndexExp) esetting;
+ Type t2 = e2x.type.toBasetype();
+
+ Expression e0 = null;
+ Expression ea = extractSideEffect(sc, "__aatmp", e0, ie.e1);
+ Expression ek = extractSideEffect(sc, "__aakey", e0, ie.e2);
+ Expression ev = extractSideEffect(sc, "__aaval", e0, e2x);
+
+ AssignExp ae = cast(AssignExp)exp.copy();
+ ae.e1 = new IndexExp(exp.loc, ea, ek);
+ ae.e1 = ae.e1.expressionSemantic(sc);
+ ae.e1 = ae.e1.optimize(WANTvalue);
+ ae.e2 = ev;
+ Expression e = ae.op_overload(sc);
+ if (e)
+ {
+ Expression ey = null;
+ if (t2.ty == Tstruct && sd == t2.toDsymbol(sc))
+ {
+ ey = ev;
+ }
+ else if (!ev.implicitConvTo(ie.type) && sd.ctor)
+ {
+ // Look for implicit constructor call
+ // Rewrite as S().ctor(e2)
+ ey = new StructLiteralExp(exp.loc, sd, null);
+ ey = new DotIdExp(exp.loc, ey, Id.ctor);
+ ey = new CallExp(exp.loc, ey, ev);
+ ey = ey.trySemantic(sc);
+ }
+ if (ey)
+ {
+ Expression ex;
+ ex = new IndexExp(exp.loc, ea, ek);
+ ex = ex.expressionSemantic(sc);
+ ex = ex.modifiableLvalue(sc, ex); // allocate new slot
+ ex = ex.optimize(WANTvalue);
+
+ ey = new ConstructExp(exp.loc, ex, ey);
+ ey = ey.expressionSemantic(sc);
+ if (ey.op == TOK.error)
+ {
+ result = ey;
+ return;
+ }
+ ex = e;
+
+ // https://issues.dlang.org/show_bug.cgi?id=14144
+ // The whole expression should have the common type
+ // of opAssign() return and assigned AA entry.
+ // Even if there's no common type, expression should be typed as void.
+ if (!typeMerge(sc, TOK.question, ex, ey))
+ {
+ ex = new CastExp(ex.loc, ex, Type.tvoid);
+ ey = new CastExp(ey.loc, ey, Type.tvoid);
+ }
+ e = new CondExp(exp.loc, new InExp(exp.loc, ek, ea), ex, ey);
+ }
+ e = Expression.combine(e0, e);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+ else
+ {
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+ }
+ else
+ assert(exp.op == TOK.blit);
+
+ exp.e1 = e1x;
+ exp.e2 = e2x;
+ }
+ else if (t1.ty == Tclass)
+ {
+ // Disallow assignment operator overloads for same type
+ if (exp.op == TOK.assign && !exp.e2.implicitConvTo(exp.e1.type))
+ {
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+ }
+ }
+ else if (t1.ty == Tsarray)
+ {
+ // SliceExp cannot have static array type without context inference.
+ assert(exp.e1.op != TOK.slice);
+ Expression e1x = exp.e1;
+ Expression e2x = exp.e2;
+
+ if (e2x.implicitConvTo(e1x.type))
+ {
+ if (exp.op != TOK.blit && (e2x.op == TOK.slice && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op == TOK.cast_ && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op != TOK.slice && e2x.isLvalue()))
+ {
+ if (e1x.checkPostblit(sc, t1))
+ return setError();
+ }
+
+ // e2 matches to t1 because of the implicit length match, so
+ if (isUnaArrayOp(e2x.op) || isBinArrayOp(e2x.op))
+ {
+ // convert e1 to e1[]
+ // e.g. e1[] = a[] + b[];
+ auto sle = new SliceExp(e1x.loc, e1x, null, null);
+ sle.arrayop = true;
+ e1x = sle.expressionSemantic(sc);
+ }
+ else
+ {
+ // convert e2 to t1 later
+ // e.g. e1 = [1, 2, 3];
+ }
+ }
+ else
+ {
+ if (e2x.implicitConvTo(t1.nextOf().arrayOf()) > MATCH.nomatch)
+ {
+ uinteger_t dim1 = (cast(TypeSArray)t1).dim.toInteger();
+ uinteger_t dim2 = dim1;
+ if (auto ale = e2x.isArrayLiteralExp())
+ {
+ dim2 = ale.elements ? ale.elements.dim : 0;
+ }
+ else if (auto se = e2x.isSliceExp())
+ {
+ Type tx = toStaticArrayType(se);
+ if (tx)
+ dim2 = (cast(TypeSArray)tx).dim.toInteger();
+ }
+ if (dim1 != dim2)
+ {
+ exp.error("mismatched array lengths, %d and %d", cast(int)dim1, cast(int)dim2);
+ return setError();
+ }
+ }
+
+ // May be block or element-wise assignment, so
+ // convert e1 to e1[]
+ if (exp.op != TOK.assign)
+ {
+ // If multidimensional static array, treat as one large array
+ //
+ // Find the appropriate array type depending on the assignment, e.g.
+ // int[3] = int => int[3]
+ // int[3][2] = int => int[6]
+ // int[3][2] = int[] => int[3][2]
+ // int[3][2][4] + int => int[24]
+ // int[3][2][4] + int[] => int[3][8]
+ ulong dim = t1.isTypeSArray().dim.toUInteger();
+ auto type = t1.nextOf();
+
+ for (TypeSArray tsa; (tsa = type.isTypeSArray()) !is null; )
+ {
+ import core.checkedint : mulu;
+
+ // Accumulate skipped dimensions
+ bool overflow = false;
+ dim = mulu(dim, tsa.dim.toUInteger(), overflow);
+ if (overflow || dim >= uint.max)
+ {
+ // dym exceeds maximum array size
+ exp.error("static array `%s` size overflowed to %llu",
+ e1x.type.toChars(), cast(ulong) dim);
+ return setError();
+ }
+
+ // Move to the element type
+ type = tsa.nextOf().toBasetype();
+
+ // Rewrite ex1 as a static array if a matching type was found
+ if (e2x.implicitConvTo(type) > MATCH.nomatch)
+ {
+ e1x.type = type.sarrayOf(dim);
+ break;
+ }
+ }
+ }
+ auto sle = new SliceExp(e1x.loc, e1x, null, null);
+ sle.arrayop = true;
+ e1x = sle.expressionSemantic(sc);
+ }
+ if (e1x.op == TOK.error)
+ return setResult(e1x);
+ if (e2x.op == TOK.error)
+ return setResult(e2x);
+
+ exp.e1 = e1x;
+ exp.e2 = e2x;
+ t1 = e1x.type.toBasetype();
+ }
+ /* Check the mutability of e1.
+ */
+ if (auto ale = exp.e1.isArrayLengthExp())
+ {
+ // e1 is not an lvalue, but we let code generator handle it
+
+ auto ale1x = ale.e1.modifiableLvalue(sc, exp.e1);
+ if (ale1x.op == TOK.error)
+ return setResult(ale1x);
+ ale.e1 = ale1x;
+
+ Type tn = ale.e1.type.toBasetype().nextOf();
+ checkDefCtor(ale.loc, tn);
+
+ Identifier hook = global.params.tracegc ? Id._d_arraysetlengthTTrace : Id._d_arraysetlengthT;
+ if (!verifyHookExist(exp.loc, *sc, Id._d_arraysetlengthTImpl, "resizing arrays"))
+ return setError();
+
+ exp.e2 = exp.e2.expressionSemantic(sc);
+ auto lc = lastComma(exp.e2);
+ lc = lc.optimize(WANTvalue);
+ // use slice expression when arr.length = 0 to avoid runtime call
+ if(lc.op == TOK.int64 && lc.toInteger() == 0)
+ {
+ Expression se = new SliceExp(ale.loc, ale.e1, lc, lc);
+ Expression as = new AssignExp(ale.loc, ale.e1, se);
+ as = as.expressionSemantic(sc);
+ auto res = Expression.combine(as, exp.e2);
+ res.type = ale.type;
+ return setResult(res);
+ }
+
+ // Lower to object._d_arraysetlengthTImpl!(typeof(e1))._d_arraysetlengthT{,Trace}(e1, e2)
+ Expression id = new IdentifierExp(ale.loc, Id.empty);
+ id = new DotIdExp(ale.loc, id, Id.object);
+ auto tiargs = new Objects();
+ tiargs.push(ale.e1.type);
+ id = new DotTemplateInstanceExp(ale.loc, id, Id._d_arraysetlengthTImpl, tiargs);
+ id = new DotIdExp(ale.loc, id, hook);
+ id = id.expressionSemantic(sc);
+
+ auto arguments = new Expressions();
+ arguments.reserve(5);
+ if (global.params.tracegc)
+ {
+ auto funcname = (sc.callsc && sc.callsc.func) ? sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars();
+ arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString()));
+ arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32));
+ arguments.push(new StringExp(exp.loc, funcname.toDString()));
+ }
+ arguments.push(ale.e1);
+ arguments.push(exp.e2);
+
+ Expression ce = new CallExp(ale.loc, id, arguments);
+ auto res = ce.expressionSemantic(sc);
+ // if (global.params.verbose)
+ // message("lowered %s =>\n %s", exp.toChars(), res.toChars());
+ return setResult(res);
+ }
+ else if (auto se = exp.e1.isSliceExp())
+ {
+ Type tn = se.type.nextOf();
+ const fun = sc.func;
+ if (exp.op == TOK.assign && !tn.isMutable() &&
+ // allow modifiation in module ctor, see
+ // https://issues.dlang.org/show_bug.cgi?id=9884
+ (!fun || (fun && !fun.isStaticCtorDeclaration())))
+ {
+ exp.error("slice `%s` is not mutable", se.toChars());
+ return setError();
+ }
+
+ if (exp.op == TOK.assign && !tn.baseElemOf().isAssignable())
+ {
+ exp.error("slice `%s` is not mutable, struct `%s` has immutable members",
+ exp.e1.toChars(), tn.baseElemOf().toChars());
+ result = ErrorExp.get();
+ return;
+ }
+
+ // For conditional operator, both branches need conversion.
+ while (se.e1.op == TOK.slice)
+ se = cast(SliceExp)se.e1;
+ if (se.e1.op == TOK.question && se.e1.type.toBasetype().ty == Tsarray)
+ {
+ se.e1 = se.e1.modifiableLvalue(sc, exp.e1);
+ if (se.e1.op == TOK.error)
+ return setResult(se.e1);
+ }
+ }
+ else
+ {
+ if (t1.ty == Tsarray && exp.op == TOK.assign)
+ {
+ Type tn = exp.e1.type.nextOf();
+ if (tn && !tn.baseElemOf().isAssignable())
+ {
+ exp.error("array `%s` is not mutable, struct `%s` has immutable members",
+ exp.e1.toChars(), tn.baseElemOf().toChars());
+ result = ErrorExp.get();
+ return;
+ }
+ }
+
+ Expression e1x = exp.e1;
+
+ // Try to do a decent error message with the expression
+ // before it gets constant folded
+ if (exp.op == TOK.assign)
+ e1x = e1x.modifiableLvalue(sc, e1old);
+
+ e1x = e1x.optimize(WANTvalue, /*keepLvalue*/ true);
+
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ exp.e1 = e1x;
+ }
+
+ /* Tweak e2 based on the type of e1.
+ */
+ Expression e2x = exp.e2;
+ Type t2 = e2x.type.toBasetype();
+
+ // If it is a array, get the element type. Note that it may be
+ // multi-dimensional.
+ Type telem = t1;
+ while (telem.ty == Tarray)
+ telem = telem.nextOf();
+
+ if (exp.e1.op == TOK.slice && t1.nextOf() &&
+ (telem.ty != Tvoid || e2x.op == TOK.null_) &&
+ e2x.implicitConvTo(t1.nextOf()))
+ {
+ // Check for block assignment. If it is of type void[], void[][], etc,
+ // '= null' is the only allowable block assignment (Bug 7493)
+ exp.memset = MemorySet.blockAssign; // make it easy for back end to tell what this is
+ e2x = e2x.implicitCastTo(sc, t1.nextOf());
+ if (exp.op != TOK.blit && e2x.isLvalue() && exp.e1.checkPostblit(sc, t1.nextOf()))
+ return setError();
+ }
+ else if (exp.e1.op == TOK.slice &&
+ (t2.ty == Tarray || t2.ty == Tsarray) &&
+ t2.nextOf().implicitConvTo(t1.nextOf()))
+ {
+ // Check element-wise assignment.
+
+ /* If assigned elements number is known at compile time,
+ * check the mismatch.
+ */
+ SliceExp se1 = cast(SliceExp)exp.e1;
+ TypeSArray tsa1 = cast(TypeSArray)toStaticArrayType(se1);
+ TypeSArray tsa2 = null;
+ if (auto ale = e2x.isArrayLiteralExp())
+ tsa2 = cast(TypeSArray)t2.nextOf().sarrayOf(ale.elements.dim);
+ else if (auto se = e2x.isSliceExp())
+ tsa2 = cast(TypeSArray)toStaticArrayType(se);
+ else
+ tsa2 = t2.isTypeSArray();
+ if (tsa1 && tsa2)
+ {
+ uinteger_t dim1 = tsa1.dim.toInteger();
+ uinteger_t dim2 = tsa2.dim.toInteger();
+ if (dim1 != dim2)
+ {
+ exp.error("mismatched array lengths, %d and %d", cast(int)dim1, cast(int)dim2);
+ return setError();
+ }
+ }
+
+ if (exp.op != TOK.blit &&
+ (e2x.op == TOK.slice && (cast(UnaExp)e2x).e1.isLvalue() ||
+ e2x.op == TOK.cast_ && (cast(UnaExp)e2x).e1.isLvalue() ||
+ e2x.op != TOK.slice && e2x.isLvalue()))
+ {
+ if (exp.e1.checkPostblit(sc, t1.nextOf()))
+ return setError();
+ }
+
+ if (0 && global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == TOK.assign &&
+ e2x.op != TOK.slice && e2x.op != TOK.assign &&
+ e2x.op != TOK.arrayLiteral && e2x.op != TOK.string_ &&
+ !(e2x.op == TOK.add || e2x.op == TOK.min ||
+ e2x.op == TOK.mul || e2x.op == TOK.div ||
+ e2x.op == TOK.mod || e2x.op == TOK.xor ||
+ e2x.op == TOK.and || e2x.op == TOK.or ||
+ e2x.op == TOK.pow ||
+ e2x.op == TOK.tilde || e2x.op == TOK.negate))
+ {
+ const(char)* e1str = exp.e1.toChars();
+ const(char)* e2str = e2x.toChars();
+ exp.warning("explicit element-wise assignment `%s = (%s)[]` is better than `%s = %s`", e1str, e2str, e1str, e2str);
+ }
+
+ Type t2n = t2.nextOf();
+ Type t1n = t1.nextOf();
+ int offset;
+ if (t2n.equivalent(t1n) ||
+ t1n.isBaseOf(t2n, &offset) && offset == 0)
+ {
+ /* Allow copy of distinct qualifier elements.
+ * eg.
+ * char[] dst; const(char)[] src;
+ * dst[] = src;
+ *
+ * class C {} class D : C {}
+ * C[2] ca; D[] da;
+ * ca[] = da;
+ */
+ if (isArrayOpValid(e2x))
+ {
+ // Don't add CastExp to keep AST for array operations
+ e2x = e2x.copy();
+ e2x.type = exp.e1.type.constOf();
+ }
+ else
+ e2x = e2x.castTo(sc, exp.e1.type.constOf());
+ }
+ else
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=15778
+ * A string literal has an array type of immutable
+ * elements by default, and normally it cannot be convertible to
+ * array type of mutable elements. But for element-wise assignment,
+ * elements need to be const at best. So we should give a chance
+ * to change code unit size for polysemous string literal.
+ */
+ if (e2x.op == TOK.string_)
+ e2x = e2x.implicitCastTo(sc, exp.e1.type.constOf());
+ else
+ e2x = e2x.implicitCastTo(sc, exp.e1.type);
+ }
+ if (t1n.toBasetype.ty == Tvoid && t2n.toBasetype.ty == Tvoid)
+ {
+ if (!sc.intypeof && sc.func && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("cannot copy `void[]` to `void[]` in `@safe` code");
+ return setError();
+ }
+ }
+ }
+ else
+ {
+ if (0 && global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == TOK.assign &&
+ t1.ty == Tarray && t2.ty == Tsarray &&
+ e2x.op != TOK.slice &&
+ t2.implicitConvTo(t1))
+ {
+ // Disallow ar[] = sa (Converted to ar[] = sa[])
+ // Disallow da = sa (Converted to da = sa[])
+ const(char)* e1str = exp.e1.toChars();
+ const(char)* e2str = e2x.toChars();
+ const(char)* atypestr = exp.e1.op == TOK.slice ? "element-wise" : "slice";
+ exp.warning("explicit %s assignment `%s = (%s)[]` is better than `%s = %s`", atypestr, e1str, e2str, e1str, e2str);
+ }
+ if (exp.op == TOK.blit)
+ e2x = e2x.castTo(sc, exp.e1.type);
+ else
+ {
+ e2x = e2x.implicitCastTo(sc, exp.e1.type);
+
+ // Fix Issue 13435: https://issues.dlang.org/show_bug.cgi?id=13435
+
+ // If the implicit cast has failed and the assign expression is
+ // the initialization of a struct member field
+ if (e2x.op == TOK.error && exp.op == TOK.construct && t1.ty == Tstruct)
+ {
+ scope sd = (cast(TypeStruct)t1).sym;
+ Dsymbol opAssign = search_function(sd, Id.assign);
+
+ // and the struct defines an opAssign
+ if (opAssign)
+ {
+ // offer more information about the cause of the problem
+ errorSupplemental(exp.loc,
+ "`%s` is the first assignment of `%s` therefore it represents its initialization",
+ exp.toChars(), exp.e1.toChars());
+ errorSupplemental(exp.loc,
+ "`opAssign` methods are not used for initialization, but for subsequent assignments");
+ }
+ }
+ }
+ }
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+ exp.e2 = e2x;
+ t2 = exp.e2.type.toBasetype();
+
+ /* Look for array operations
+ */
+ if ((t2.ty == Tarray || t2.ty == Tsarray) && isArrayOpValid(exp.e2))
+ {
+ // Look for valid array operations
+ if (exp.memset != MemorySet.blockAssign &&
+ exp.e1.op == TOK.slice &&
+ (isUnaArrayOp(exp.e2.op) || isBinArrayOp(exp.e2.op)))
+ {
+ exp.type = exp.e1.type;
+ if (exp.op == TOK.construct) // https://issues.dlang.org/show_bug.cgi?id=10282
+ // tweak mutability of e1 element
+ exp.e1.type = exp.e1.type.nextOf().mutableOf().arrayOf();
+ result = arrayOp(exp, sc);
+ return;
+ }
+
+ // Drop invalid array operations in e2
+ // d = a[] + b[], d = (a[] + b[])[0..2], etc
+ if (checkNonAssignmentArrayOp(exp.e2, exp.memset != MemorySet.blockAssign && exp.op == TOK.assign))
+ return setError();
+
+ // Remains valid array assignments
+ // d = d[], d = [1,2,3], etc
+ }
+
+ /* Don't allow assignment to classes that were allocated on the stack with:
+ * scope Class c = new Class();
+ */
+ if (exp.e1.op == TOK.variable && exp.op == TOK.assign)
+ {
+ VarExp ve = cast(VarExp)exp.e1;
+ VarDeclaration vd = ve.var.isVarDeclaration();
+ if (vd && (vd.onstack || vd.mynew))
+ {
+ assert(t1.ty == Tclass);
+ exp.error("cannot rebind scope variables");
+ }
+ }
+
+ if (exp.e1.op == TOK.variable && (cast(VarExp)exp.e1).var.ident == Id.ctfe)
+ {
+ exp.error("cannot modify compiler-generated variable `__ctfe`");
+ }
+
+ exp.type = exp.e1.type;
+ assert(exp.type);
+ auto res = exp.op == TOK.assign ? exp.reorderSettingAAElem(sc) : exp;
+ checkAssignEscape(sc, res, false);
+ return setResult(res);
+ }
+
+ override void visit(PowAssignExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.checkReadModifyWrite(exp.op, exp.e2))
+ return setError();
+
+ assert(exp.e1.type && exp.e2.type);
+ if (exp.e1.op == TOK.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray)
+ {
+ if (checkNonAssignmentArrayOp(exp.e1))
+ return setError();
+
+ // T[] ^^= ...
+ if (exp.e2.implicitConvTo(exp.e1.type.nextOf()))
+ {
+ // T[] ^^= T
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type.nextOf());
+ }
+ else if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ // Check element types are arithmetic
+ Type tb1 = exp.e1.type.nextOf().toBasetype();
+ Type tb2 = exp.e2.type.toBasetype();
+ if (tb2.ty == Tarray || tb2.ty == Tsarray)
+ tb2 = tb2.nextOf().toBasetype();
+ if ((tb1.isintegral() || tb1.isfloating()) && (tb2.isintegral() || tb2.isfloating()))
+ {
+ exp.type = exp.e1.type;
+ result = arrayOp(exp, sc);
+ return;
+ }
+ }
+ else
+ {
+ exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ }
+
+ if ((exp.e1.type.isintegral() || exp.e1.type.isfloating()) && (exp.e2.type.isintegral() || exp.e2.type.isfloating()))
+ {
+ Expression e0 = null;
+ e = exp.reorderSettingAAElem(sc);
+ e = Expression.extractLast(e, e0);
+ assert(e == exp);
+
+ if (exp.e1.op == TOK.variable)
+ {
+ // Rewrite: e1 = e1 ^^ e2
+ e = new PowExp(exp.loc, exp.e1.syntaxCopy(), exp.e2);
+ e = new AssignExp(exp.loc, exp.e1, e);
+ }
+ else
+ {
+ // Rewrite: ref tmp = e1; tmp = tmp ^^ e2
+ auto v = copyToTemp(STC.ref_, "__powtmp", exp.e1);
+ auto de = new DeclarationExp(exp.e1.loc, v);
+ auto ve = new VarExp(exp.e1.loc, v);
+ e = new PowExp(exp.loc, ve, exp.e2);
+ e = new AssignExp(exp.loc, new VarExp(exp.e1.loc, v), e);
+ e = new CommaExp(exp.loc, de, e);
+ }
+ e = Expression.combine(e0, e);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ result = exp.incompatibleTypes();
+ }
+
+ override void visit(CatAssignExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ //printf("CatAssignExp::semantic() %s\n", exp.toChars());
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.op == TOK.slice)
+ {
+ SliceExp se = cast(SliceExp)exp.e1;
+ if (se.e1.type.toBasetype().ty == Tsarray)
+ {
+ exp.error("cannot append to static array `%s`", se.e1.type.toChars());
+ return setError();
+ }
+ }
+
+ exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+ if (exp.e1.op == TOK.error)
+ {
+ result = exp.e1;
+ return;
+ }
+ if (exp.e2.op == TOK.error)
+ {
+ result = exp.e2;
+ return;
+ }
+
+ if (checkNonAssignmentArrayOp(exp.e2))
+ return setError();
+
+ Type tb1 = exp.e1.type.toBasetype();
+ Type tb1next = tb1.nextOf();
+ Type tb2 = exp.e2.type.toBasetype();
+
+ /* Possibilities:
+ * TOK.concatenateAssign: appending T[] to T[]
+ * TOK.concatenateElemAssign: appending T to T[]
+ * TOK.concatenateDcharAssign: appending dchar to T[]
+ */
+ if ((tb1.ty == Tarray) &&
+ (tb2.ty == Tarray || tb2.ty == Tsarray) &&
+ (exp.e2.implicitConvTo(exp.e1.type) ||
+ (tb2.nextOf().implicitConvTo(tb1next) &&
+ (tb2.nextOf().size(Loc.initial) == tb1next.size(Loc.initial)))))
+ {
+ // TOK.concatenateAssign
+ assert(exp.op == TOK.concatenateAssign);
+ if (exp.e1.checkPostblit(sc, tb1next))
+ return setError();
+
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type);
+ }
+ else if ((tb1.ty == Tarray) && exp.e2.implicitConvTo(tb1next))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=19782
+ *
+ * If e2 is implicitly convertible to tb1next, the conversion
+ * might be done through alias this, in which case, e2 needs to
+ * be modified accordingly (e2 => e2.aliasthis).
+ */
+ if (tb2.ty == Tstruct && (cast(TypeStruct)tb2).implicitConvToThroughAliasThis(tb1next))
+ goto Laliasthis;
+ if (tb2.ty == Tclass && (cast(TypeClass)tb2).implicitConvToThroughAliasThis(tb1next))
+ goto Laliasthis;
+ // Append element
+ if (exp.e2.checkPostblit(sc, tb2))
+ return setError();
+
+ if (checkNewEscape(sc, exp.e2, false))
+ return setError();
+
+ exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, exp.e2.castTo(sc, tb1next));
+ exp.e2 = doCopyOrMove(sc, exp.e2);
+ }
+ else if (tb1.ty == Tarray &&
+ (tb1next.ty == Tchar || tb1next.ty == Twchar) &&
+ exp.e2.type.ty != tb1next.ty &&
+ exp.e2.implicitConvTo(Type.tdchar))
+ {
+ // Append dchar to char[] or wchar[]
+ exp = new CatDcharAssignExp(exp.loc, exp.type, exp.e1, exp.e2.castTo(sc, Type.tdchar));
+
+ /* Do not allow appending wchar to char[] because if wchar happens
+ * to be a surrogate pair, nothing good can result.
+ */
+ }
+ else
+ {
+ // Try alias this on first operand
+ static Expression tryAliasThisForLhs(BinAssignExp exp, Scope* sc)
+ {
+ AggregateDeclaration ad1 = isAggregate(exp.e1.type);
+ if (!ad1 || !ad1.aliasthis)
+ return null;
+
+ /* Rewrite (e1 op e2) as:
+ * (e1.aliasthis op e2)
+ */
+ if (isRecursiveAliasThis(exp.att1, exp.e1.type))
+ return null;
+ //printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars());
+ Expression e1 = new DotIdExp(exp.loc, exp.e1, ad1.aliasthis.ident);
+ BinExp be = cast(BinExp)exp.copy();
+ be.e1 = e1;
+ return be.trySemantic(sc);
+ }
+
+ // Try alias this on second operand
+ static Expression tryAliasThisForRhs(BinAssignExp exp, Scope* sc)
+ {
+ AggregateDeclaration ad2 = isAggregate(exp.e2.type);
+ if (!ad2 || !ad2.aliasthis)
+ return null;
+ /* Rewrite (e1 op e2) as:
+ * (e1 op e2.aliasthis)
+ */
+ if (isRecursiveAliasThis(exp.att2, exp.e2.type))
+ return null;
+ //printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars());
+ Expression e2 = new DotIdExp(exp.loc, exp.e2, ad2.aliasthis.ident);
+ BinExp be = cast(BinExp)exp.copy();
+ be.e2 = e2;
+ return be.trySemantic(sc);
+ }
+
+ Laliasthis:
+ result = tryAliasThisForLhs(exp, sc);
+ if (result)
+ return;
+
+ result = tryAliasThisForRhs(exp, sc);
+ if (result)
+ return;
+
+ exp.error("cannot append type `%s` to type `%s`", tb2.toChars(), tb1.toChars());
+ return setError();
+ }
+
+ if (exp.e2.checkValue() || exp.e2.checkSharedAccess(sc))
+ return setError();
+
+ exp.type = exp.e1.type;
+ auto res = exp.reorderSettingAAElem(sc);
+ if ((exp.op == TOK.concatenateElemAssign || exp.op == TOK.concatenateDcharAssign) &&
+ global.params.useDIP1000 == FeatureState.enabled)
+ checkAssignEscape(sc, res, false);
+ result = res;
+ }
+
+ override void visit(AddExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("AddExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb1 = exp.e1.type.toBasetype();
+ Type tb2 = exp.e2.type.toBasetype();
+
+ bool err = false;
+ if (tb1.ty == Tdelegate || tb1.isPtrToFunction())
+ {
+ err |= exp.e1.checkArithmetic() || exp.e1.checkSharedAccess(sc);
+ }
+ if (tb2.ty == Tdelegate || tb2.isPtrToFunction())
+ {
+ err |= exp.e2.checkArithmetic() || exp.e2.checkSharedAccess(sc);
+ }
+ if (err)
+ return setError();
+
+ if (tb1.ty == Tpointer && exp.e2.type.isintegral() || tb2.ty == Tpointer && exp.e1.type.isintegral())
+ {
+ result = scaleFactor(exp, sc);
+ return;
+ }
+
+ if (tb1.ty == Tpointer && tb2.ty == Tpointer)
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ tb1 = exp.e1.type.toBasetype();
+ if (!target.isVectorOpSupported(tb1, exp.op, tb2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if ((tb1.isreal() && exp.e2.type.isimaginary()) || (tb1.isimaginary() && exp.e2.type.isreal()))
+ {
+ switch (exp.type.toBasetype().ty)
+ {
+ case Tfloat32:
+ case Timaginary32:
+ exp.type = Type.tcomplex32;
+ break;
+
+ case Tfloat64:
+ case Timaginary64:
+ exp.type = Type.tcomplex64;
+ break;
+
+ case Tfloat80:
+ case Timaginary80:
+ exp.type = Type.tcomplex80;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ result = exp;
+ }
+
+ override void visit(MinExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("MinExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type t1 = exp.e1.type.toBasetype();
+ Type t2 = exp.e2.type.toBasetype();
+
+ bool err = false;
+ if (t1.ty == Tdelegate || t1.isPtrToFunction())
+ {
+ err |= exp.e1.checkArithmetic() || exp.e1.checkSharedAccess(sc);
+ }
+ if (t2.ty == Tdelegate || t2.isPtrToFunction())
+ {
+ err |= exp.e2.checkArithmetic() || exp.e2.checkSharedAccess(sc);
+ }
+ if (err)
+ return setError();
+
+ if (t1.ty == Tpointer)
+ {
+ if (t2.ty == Tpointer)
+ {
+ // https://dlang.org/spec/expression.html#add_expressions
+ // "If both operands are pointers, and the operator is -, the pointers are
+ // subtracted and the result is divided by the size of the type pointed to
+ // by the operands. It is an error if the pointers point to different types."
+ Type p1 = t1.nextOf();
+ Type p2 = t2.nextOf();
+
+ if (!p1.equivalent(p2))
+ {
+ // Deprecation to remain for at least a year, after which this should be
+ // changed to an error
+ // See https://github.com/dlang/dmd/pull/7332
+ deprecation(exp.loc,
+ "cannot subtract pointers to different types: `%s` and `%s`.",
+ t1.toChars(), t2.toChars());
+ }
+
+ // Need to divide the result by the stride
+ // Replace (ptr - ptr) with (ptr - ptr) / stride
+ d_int64 stride;
+
+ // make sure pointer types are compatible
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ exp.type = Type.tptrdiff_t;
+ stride = t2.nextOf().size();
+ if (stride == 0)
+ {
+ e = new IntegerExp(exp.loc, 0, Type.tptrdiff_t);
+ }
+ else
+ {
+ e = new DivExp(exp.loc, exp, new IntegerExp(Loc.initial, stride, Type.tptrdiff_t));
+ e.type = Type.tptrdiff_t;
+ }
+ }
+ else if (t2.isintegral())
+ e = scaleFactor(exp, sc);
+ else
+ {
+ exp.error("can't subtract `%s` from pointer", t2.toChars());
+ e = ErrorExp.get();
+ }
+ result = e;
+ return;
+ }
+ if (t2.ty == Tpointer)
+ {
+ exp.type = exp.e2.type;
+ exp.error("can't subtract pointer from `%s`", exp.e1.type.toChars());
+ return setError();
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ t1 = exp.e1.type.toBasetype();
+ t2 = exp.e2.type.toBasetype();
+ if (!target.isVectorOpSupported(t1, exp.op, t2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if ((t1.isreal() && t2.isimaginary()) || (t1.isimaginary() && t2.isreal()))
+ {
+ switch (exp.type.ty)
+ {
+ case Tfloat32:
+ case Timaginary32:
+ exp.type = Type.tcomplex32;
+ break;
+
+ case Tfloat64:
+ case Timaginary64:
+ exp.type = Type.tcomplex64;
+ break;
+
+ case Tfloat80:
+ case Timaginary80:
+ exp.type = Type.tcomplex80;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ result = exp;
+ return;
+ }
+
+ override void visit(CatExp exp)
+ {
+ // https://dlang.org/spec/expression.html#cat_expressions
+ //printf("CatExp.semantic() %s\n", toChars());
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type tb1 = exp.e1.type.toBasetype();
+ Type tb2 = exp.e2.type.toBasetype();
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f1 || f2)
+ return setError();
+
+ Type tb1next = tb1.nextOf();
+ Type tb2next = tb2.nextOf();
+
+ // Check for: array ~ array
+ if (tb1next && tb2next && (tb1next.implicitConvTo(tb2next) >= MATCH.constant || tb2next.implicitConvTo(tb1next) >= MATCH.constant || exp.e1.op == TOK.arrayLiteral && exp.e1.implicitConvTo(tb2) || exp.e2.op == TOK.arrayLiteral && exp.e2.implicitConvTo(tb1)))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=9248
+ * Here to avoid the case of:
+ * void*[] a = [cast(void*)1];
+ * void*[] b = [cast(void*)2];
+ * a ~ b;
+ * becoming:
+ * a ~ [cast(void*)b];
+ */
+
+ /* https://issues.dlang.org/show_bug.cgi?id=14682
+ * Also to avoid the case of:
+ * int[][] a;
+ * a ~ [];
+ * becoming:
+ * a ~ cast(int[])[];
+ */
+ goto Lpeer;
+ }
+
+ // Check for: array ~ element
+ if ((tb1.ty == Tsarray || tb1.ty == Tarray) && tb2.ty != Tvoid)
+ {
+ if (exp.e1.op == TOK.arrayLiteral)
+ {
+ exp.e2 = doCopyOrMove(sc, exp.e2);
+ // https://issues.dlang.org/show_bug.cgi?id=14686
+ // Postblit call appears in AST, and this is
+ // finally translated to an ArrayLiteralExp in below optimize().
+ }
+ else if (exp.e1.op == TOK.string_)
+ {
+ // No postblit call exists on character (integer) value.
+ }
+ else
+ {
+ if (exp.e2.checkPostblit(sc, tb2))
+ return setError();
+ // Postblit call will be done in runtime helper function
+ }
+
+ if (exp.e1.op == TOK.arrayLiteral && exp.e1.implicitConvTo(tb2.arrayOf()))
+ {
+ exp.e1 = exp.e1.implicitCastTo(sc, tb2.arrayOf());
+ exp.type = tb2.arrayOf();
+ goto L2elem;
+ }
+ if (exp.e2.implicitConvTo(tb1next) >= MATCH.convert)
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, tb1next);
+ exp.type = tb1next.arrayOf();
+ L2elem:
+ if (tb2.ty == Tarray || tb2.ty == Tsarray)
+ {
+ // Make e2 into [e2]
+ exp.e2 = new ArrayLiteralExp(exp.e2.loc, exp.type, exp.e2);
+ }
+ else if (checkNewEscape(sc, exp.e2, false))
+ return setError();
+ result = exp.optimize(WANTvalue);
+ return;
+ }
+ }
+ // Check for: element ~ array
+ if ((tb2.ty == Tsarray || tb2.ty == Tarray) && tb1.ty != Tvoid)
+ {
+ if (exp.e2.op == TOK.arrayLiteral)
+ {
+ exp.e1 = doCopyOrMove(sc, exp.e1);
+ }
+ else if (exp.e2.op == TOK.string_)
+ {
+ }
+ else
+ {
+ if (exp.e1.checkPostblit(sc, tb1))
+ return setError();
+ }
+
+ if (exp.e2.op == TOK.arrayLiteral && exp.e2.implicitConvTo(tb1.arrayOf()))
+ {
+ exp.e2 = exp.e2.implicitCastTo(sc, tb1.arrayOf());
+ exp.type = tb1.arrayOf();
+ goto L1elem;
+ }
+ if (exp.e1.implicitConvTo(tb2next) >= MATCH.convert)
+ {
+ exp.e1 = exp.e1.implicitCastTo(sc, tb2next);
+ exp.type = tb2next.arrayOf();
+ L1elem:
+ if (tb1.ty == Tarray || tb1.ty == Tsarray)
+ {
+ // Make e1 into [e1]
+ exp.e1 = new ArrayLiteralExp(exp.e1.loc, exp.type, exp.e1);
+ }
+ else if (checkNewEscape(sc, exp.e1, false))
+ return setError();
+ result = exp.optimize(WANTvalue);
+ return;
+ }
+ }
+
+ Lpeer:
+ if ((tb1.ty == Tsarray || tb1.ty == Tarray) && (tb2.ty == Tsarray || tb2.ty == Tarray) && (tb1next.mod || tb2next.mod) && (tb1next.mod != tb2next.mod))
+ {
+ Type t1 = tb1next.mutableOf().constOf().arrayOf();
+ Type t2 = tb2next.mutableOf().constOf().arrayOf();
+ if (exp.e1.op == TOK.string_ && !(cast(StringExp)exp.e1).committed)
+ exp.e1.type = t1;
+ else
+ exp.e1 = exp.e1.castTo(sc, t1);
+ if (exp.e2.op == TOK.string_ && !(cast(StringExp)exp.e2).committed)
+ exp.e2.type = t2;
+ else
+ exp.e2 = exp.e2.castTo(sc, t2);
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ exp.type = exp.type.toHeadMutable();
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tsarray)
+ exp.type = tb.nextOf().arrayOf();
+ if (exp.type.ty == Tarray && tb1next && tb2next && tb1next.mod != tb2next.mod)
+ {
+ exp.type = exp.type.nextOf().toHeadMutable().arrayOf();
+ }
+ if (Type tbn = tb.nextOf())
+ {
+ if (exp.checkPostblit(sc, tbn))
+ return setError();
+ }
+ Type t1 = exp.e1.type.toBasetype();
+ Type t2 = exp.e2.type.toBasetype();
+ if ((t1.ty == Tarray || t1.ty == Tsarray) &&
+ (t2.ty == Tarray || t2.ty == Tsarray))
+ {
+ // Normalize to ArrayLiteralExp or StringExp as far as possible
+ e = exp.optimize(WANTvalue);
+ }
+ else
+ {
+ //printf("(%s) ~ (%s)\n", e1.toChars(), e2.toChars());
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ result = e;
+ }
+
+ override void visit(MulExp exp)
+ {
+ version (none)
+ {
+ printf("MulExp::semantic() %s\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (exp.type.isfloating())
+ {
+ Type t1 = exp.e1.type;
+ Type t2 = exp.e2.type;
+
+ if (t1.isreal())
+ {
+ exp.type = t2;
+ }
+ else if (t2.isreal())
+ {
+ exp.type = t1;
+ }
+ else if (t1.isimaginary())
+ {
+ if (t2.isimaginary())
+ {
+ switch (t1.toBasetype().ty)
+ {
+ case Timaginary32:
+ exp.type = Type.tfloat32;
+ break;
+
+ case Timaginary64:
+ exp.type = Type.tfloat64;
+ break;
+
+ case Timaginary80:
+ exp.type = Type.tfloat80;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ // iy * iv = -yv
+ exp.e1.type = exp.type;
+ exp.e2.type = exp.type;
+ e = new NegExp(exp.loc, exp);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ else
+ exp.type = t2; // t2 is complex
+ }
+ else if (t2.isimaginary())
+ {
+ exp.type = t1; // t1 is complex
+ }
+ }
+ else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ result = exp;
+ }
+
+ override void visit(DivExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (exp.type.isfloating())
+ {
+ Type t1 = exp.e1.type;
+ Type t2 = exp.e2.type;
+
+ if (t1.isreal())
+ {
+ exp.type = t2;
+ if (t2.isimaginary())
+ {
+ // x/iv = i(-x/v)
+ exp.e2.type = t1;
+ e = new NegExp(exp.loc, exp);
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+ }
+ else if (t2.isreal())
+ {
+ exp.type = t1;
+ }
+ else if (t1.isimaginary())
+ {
+ if (t2.isimaginary())
+ {
+ switch (t1.toBasetype().ty)
+ {
+ case Timaginary32:
+ exp.type = Type.tfloat32;
+ break;
+
+ case Timaginary64:
+ exp.type = Type.tfloat64;
+ break;
+
+ case Timaginary80:
+ exp.type = Type.tfloat80;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ else
+ exp.type = t2; // t2 is complex
+ }
+ else if (t2.isimaginary())
+ {
+ exp.type = t1; // t1 is complex
+ }
+ }
+ else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ result = exp;
+ }
+
+ override void visit(ModExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (exp.type.isfloating())
+ {
+ exp.type = exp.e1.type;
+ if (exp.e2.type.iscomplex())
+ {
+ exp.error("cannot perform modulo complex arithmetic");
+ return setError();
+ }
+ }
+ result = exp;
+ }
+
+ override void visit(PowExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ //printf("PowExp::semantic() %s\n", toChars());
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ // First, attempt to fold the expression.
+ e = exp.optimize(WANTvalue);
+ if (e.op != TOK.pow)
+ {
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ Module mmath = loadStdMath();
+ if (!mmath)
+ {
+ e.error("`%s` requires `std.math` for `^^` operators", e.toChars());
+ return setError();
+ }
+ e = new ScopeExp(exp.loc, mmath);
+
+ if (exp.e2.op == TOK.float64 && exp.e2.toReal() == CTFloat.half)
+ {
+ // Replace e1 ^^ 0.5 with .std.math.sqrt(e1)
+ e = new CallExp(exp.loc, new DotIdExp(exp.loc, e, Id._sqrt), exp.e1);
+ }
+ else
+ {
+ // Replace e1 ^^ e2 with .std.math.pow(e1, e2)
+ e = new CallExp(exp.loc, new DotIdExp(exp.loc, e, Id._pow), exp.e1, exp.e2);
+ }
+ e = e.expressionSemantic(sc);
+ result = e;
+ return;
+ }
+
+ override void visit(ShlExp exp)
+ {
+ //printf("ShlExp::semantic(), type = %p\n", type);
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ exp.e1 = integralPromotions(exp.e1, sc);
+ if (exp.e2.type.toBasetype().ty != Tvector)
+ exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
+
+ exp.type = exp.e1.type;
+ result = exp;
+ }
+
+ override void visit(ShrExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ exp.e1 = integralPromotions(exp.e1, sc);
+ if (exp.e2.type.toBasetype().ty != Tvector)
+ exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
+
+ exp.type = exp.e1.type;
+ result = exp;
+ }
+
+ override void visit(UshrExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ exp.e1 = integralPromotions(exp.e1, sc);
+ if (exp.e2.type.toBasetype().ty != Tvector)
+ exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt);
+
+ exp.type = exp.e1.type;
+ result = exp;
+ }
+
+ override void visit(AndExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool)
+ {
+ exp.type = exp.e1.type;
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(OrExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool)
+ {
+ exp.type = exp.e1.type;
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(XorExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool)
+ {
+ exp.type = exp.e1.type;
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ Type tb = exp.type.toBasetype();
+ if (tb.ty == Tarray || tb.ty == Tsarray)
+ {
+ if (!isArrayOpValid(exp))
+ {
+ result = arrayOpInvalidError(exp);
+ return;
+ }
+ result = exp;
+ return;
+ }
+ if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype()))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc))
+ return setError();
+
+ result = exp;
+ }
+
+ override void visit(LogicalExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("LogicalExp::semantic() %s\n", exp.toChars());
+ }
+
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.setNoderefOperands();
+
+ Expression e1x = exp.e1.expressionSemantic(sc);
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e1x.op == TOK.type)
+ e1x = resolveAliasThis(sc, e1x);
+
+ e1x = resolveProperties(sc, e1x);
+ e1x = e1x.toBoolean(sc);
+
+ if (sc.flags & SCOPE.condition)
+ {
+ /* If in static if, don't evaluate e2 if we don't have to.
+ */
+ e1x = e1x.optimize(WANTvalue);
+ if (e1x.isBool(exp.op == TOK.orOr))
+ {
+ result = IntegerExp.createBool(exp.op == TOK.orOr);
+ return;
+ }
+ }
+
+ CtorFlow ctorflow = sc.ctorflow.clone();
+ Expression e2x = exp.e2.expressionSemantic(sc);
+ sc.merge(exp.loc, ctorflow);
+ ctorflow.freeFieldinit();
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e2x.op == TOK.type)
+ e2x = resolveAliasThis(sc, e2x);
+
+ e2x = resolveProperties(sc, e2x);
+
+ auto f1 = checkNonAssignmentArrayOp(e1x);
+ auto f2 = checkNonAssignmentArrayOp(e2x);
+ if (f1 || f2)
+ return setError();
+
+ // Unless the right operand is 'void', the expression is converted to 'bool'.
+ if (e2x.type.ty != Tvoid)
+ e2x = e2x.toBoolean(sc);
+
+ if (e2x.op == TOK.type || e2x.op == TOK.scope_)
+ {
+ exp.error("`%s` is not an expression", exp.e2.toChars());
+ return setError();
+ }
+ if (e1x.op == TOK.error || e1x.type.ty == Tnoreturn)
+ {
+ result = e1x;
+ return;
+ }
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+
+ // The result type is 'bool', unless the right operand has type 'void'.
+ if (e2x.type.ty == Tvoid)
+ exp.type = Type.tvoid;
+ else
+ exp.type = Type.tbool;
+
+ exp.e1 = e1x;
+ exp.e2 = e2x;
+ result = exp;
+ }
+
+
+ override void visit(CmpExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CmpExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.setNoderefOperands();
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Type t1 = exp.e1.type.toBasetype();
+ Type t2 = exp.e2.type.toBasetype();
+ if (t1.ty == Tclass && exp.e2.op == TOK.null_ || t2.ty == Tclass && exp.e1.op == TOK.null_)
+ {
+ exp.error("do not use `null` when comparing class types");
+ return setError();
+ }
+
+ TOK cmpop;
+ if (auto e = exp.op_overload(sc, &cmpop))
+ {
+ if (!e.type.isscalar() && e.type.equals(exp.e1.type))
+ {
+ exp.error("recursive `opCmp` expansion");
+ return setError();
+ }
+ if (e.op == TOK.call)
+ {
+ e = new CmpExp(cmpop, exp.loc, e, IntegerExp.literal!0);
+ e = e.expressionSemantic(sc);
+ }
+ result = e;
+ return;
+ }
+
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f1 || f2)
+ return setError();
+
+ exp.type = Type.tbool;
+
+ // Special handling for array comparisons
+ Expression arrayLowering = null;
+ t1 = exp.e1.type.toBasetype();
+ t2 = exp.e2.type.toBasetype();
+ if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && (t2.ty == Tarray || t2.ty == Tsarray || t2.ty == Tpointer))
+ {
+ Type t1next = t1.nextOf();
+ Type t2next = t2.nextOf();
+ if (t1next.implicitConvTo(t2next) < MATCH.constant && t2next.implicitConvTo(t1next) < MATCH.constant && (t1next.ty != Tvoid && t2next.ty != Tvoid))
+ {
+ exp.error("array comparison type mismatch, `%s` vs `%s`", t1next.toChars(), t2next.toChars());
+ return setError();
+ }
+ if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray))
+ {
+ if (!verifyHookExist(exp.loc, *sc, Id.__cmp, "comparing arrays"))
+ return setError();
+
+ // Lower to object.__cmp(e1, e2)
+ Expression al = new IdentifierExp(exp.loc, Id.empty);
+ al = new DotIdExp(exp.loc, al, Id.object);
+ al = new DotIdExp(exp.loc, al, Id.__cmp);
+ al = al.expressionSemantic(sc);
+
+ auto arguments = new Expressions(2);
+ (*arguments)[0] = exp.e1;
+ (*arguments)[1] = exp.e2;
+
+ al = new CallExp(exp.loc, al, arguments);
+ al = new CmpExp(exp.op, exp.loc, al, IntegerExp.literal!0);
+
+ arrayLowering = al;
+ }
+ }
+ else if (t1.ty == Tstruct || t2.ty == Tstruct || (t1.ty == Tclass && t2.ty == Tclass))
+ {
+ if (t2.ty == Tstruct)
+ exp.error("need member function `opCmp()` for %s `%s` to compare", t2.toDsymbol(sc).kind(), t2.toChars());
+ else
+ exp.error("need member function `opCmp()` for %s `%s` to compare", t1.toDsymbol(sc).kind(), t1.toChars());
+ return setError();
+ }
+ else if (t1.iscomplex() || t2.iscomplex())
+ {
+ exp.error("compare not defined for complex operands");
+ return setError();
+ }
+ else if (t1.ty == Taarray || t2.ty == Taarray)
+ {
+ exp.error("`%s` is not defined for associative arrays", Token.toChars(exp.op));
+ return setError();
+ }
+ else if (!target.isVectorOpSupported(t1, exp.op, t2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+ else
+ {
+ bool r1 = exp.e1.checkValue() || exp.e1.checkSharedAccess(sc);
+ bool r2 = exp.e2.checkValue() || exp.e2.checkSharedAccess(sc);
+ if (r1 || r2)
+ return setError();
+ }
+
+ //printf("CmpExp: %s, type = %s\n", e.toChars(), e.type.toChars());
+ if (arrayLowering)
+ {
+ arrayLowering = arrayLowering.expressionSemantic(sc);
+ result = arrayLowering;
+ return;
+ }
+ result = exp;
+ return;
+ }
+
+ override void visit(InExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (Expression ex = binSemanticProp(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+ Expression e = exp.op_overload(sc);
+ if (e)
+ {
+ result = e;
+ return;
+ }
+
+ Type t2b = exp.e2.type.toBasetype();
+ switch (t2b.ty)
+ {
+ case Taarray:
+ {
+ TypeAArray ta = cast(TypeAArray)t2b;
+
+ // Special handling for array keys
+ if (!arrayTypeCompatibleWithoutCasting(exp.e1.type, ta.index))
+ {
+ // Convert key to type of key
+ exp.e1 = exp.e1.implicitCastTo(sc, ta.index);
+ }
+
+ semanticTypeInfo(sc, ta.index);
+
+ // Return type is pointer to value
+ exp.type = ta.nextOf().pointerTo();
+ break;
+ }
+
+ case Terror:
+ return setError();
+
+ default:
+ result = exp.incompatibleTypes();
+ return;
+ }
+ result = exp;
+ }
+
+ override void visit(RemoveExp e)
+ {
+ if (Expression ex = binSemantic(e, sc))
+ {
+ result = ex;
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(EqualExp exp)
+ {
+ //printf("EqualExp::semantic('%s')\n", exp.toChars());
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.setNoderefOperands();
+
+ if (auto e = binSemanticProp(exp, sc))
+ {
+ result = e;
+ return;
+ }
+ if (exp.e1.op == TOK.type || exp.e2.op == TOK.type)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=12520
+ * empty tuples are represented as types so special cases are added
+ * so that they can be compared for equality with tuples of values.
+ */
+ static auto extractTypeTupAndExpTup(Expression e)
+ {
+ static struct Result { bool ttEmpty; bool te; }
+ auto tt = e.op == TOK.type ? e.isTypeExp().type.isTypeTuple() : null;
+ return Result(tt && (!tt.arguments || !tt.arguments.dim), e.isTupleExp() !is null);
+ }
+ auto tups1 = extractTypeTupAndExpTup(exp.e1);
+ auto tups2 = extractTypeTupAndExpTup(exp.e2);
+ // AliasSeq!() == AliasSeq!(<at least a value>)
+ if (tups1.ttEmpty && tups2.te)
+ {
+ result = IntegerExp.createBool(exp.op != TOK.equal);
+ return;
+ }
+ // AliasSeq!(<at least a value>) == AliasSeq!()
+ else if (tups1.te && tups2.ttEmpty)
+ {
+ result = IntegerExp.createBool(exp.op != TOK.equal);
+ return;
+ }
+ // AliasSeq!() == AliasSeq!()
+ else if (tups1.ttEmpty && tups2.ttEmpty)
+ {
+ result = IntegerExp.createBool(exp.op == TOK.equal);
+ return;
+ }
+ // otherwise, two types are really not comparable
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ {
+ auto t1 = exp.e1.type;
+ auto t2 = exp.e2.type;
+ if (t1.ty == Tenum && t2.ty == Tenum && !t1.equivalent(t2))
+ exp.error("Comparison between different enumeration types `%s` and `%s`; If this behavior is intended consider using `std.conv.asOriginalType`",
+ t1.toChars(), t2.toChars());
+ }
+
+ /* Before checking for operator overloading, check to see if we're
+ * comparing the addresses of two statics. If so, we can just see
+ * if they are the same symbol.
+ */
+ if (exp.e1.op == TOK.address && exp.e2.op == TOK.address)
+ {
+ AddrExp ae1 = cast(AddrExp)exp.e1;
+ AddrExp ae2 = cast(AddrExp)exp.e2;
+ if (ae1.e1.op == TOK.variable && ae2.e1.op == TOK.variable)
+ {
+ VarExp ve1 = cast(VarExp)ae1.e1;
+ VarExp ve2 = cast(VarExp)ae2.e1;
+ if (ve1.var == ve2.var)
+ {
+ // They are the same, result is 'true' for ==, 'false' for !=
+ result = IntegerExp.createBool(exp.op == TOK.equal);
+ return;
+ }
+ }
+ }
+
+ Type t1 = exp.e1.type.toBasetype();
+ Type t2 = exp.e2.type.toBasetype();
+
+ // Indicates whether the comparison of the 2 specified array types
+ // requires an object.__equals() lowering.
+ static bool needsDirectEq(Type t1, Type t2, Scope* sc)
+ {
+ Type t1n = t1.nextOf().toBasetype();
+ Type t2n = t2.nextOf().toBasetype();
+ if ((t1n.ty.isSomeChar && t2n.ty.isSomeChar) ||
+ (t1n.ty == Tvoid || t2n.ty == Tvoid))
+ {
+ return false;
+ }
+ if (t1n.constOf() != t2n.constOf())
+ return true;
+
+ Type t = t1n;
+ while (t.toBasetype().nextOf())
+ t = t.nextOf().toBasetype();
+ if (auto ts = t.isTypeStruct())
+ {
+ // semanticTypeInfo() makes sure hasIdentityEquals has been computed
+ if (global.params.useTypeInfo && Type.dtypeinfo)
+ semanticTypeInfo(sc, ts);
+
+ return ts.sym.hasIdentityEquals; // has custom opEquals
+ }
+
+ return false;
+ }
+
+ if (auto e = exp.op_overload(sc))
+ {
+ result = e;
+ return;
+ }
+
+
+ const isArrayComparison = (t1.ty == Tarray || t1.ty == Tsarray) &&
+ (t2.ty == Tarray || t2.ty == Tsarray);
+ const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc);
+
+ if (!needsArrayLowering)
+ {
+ if (auto e = typeCombine(exp, sc))
+ {
+ result = e;
+ return;
+ }
+ }
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f1 || f2)
+ return setError();
+
+ exp.type = Type.tbool;
+
+ if (!isArrayComparison)
+ {
+ if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating())
+ {
+ // Cast both to complex
+ exp.e1 = exp.e1.castTo(sc, Type.tcomplex80);
+ exp.e2 = exp.e2.castTo(sc, Type.tcomplex80);
+ }
+ }
+
+ // lower some array comparisons to object.__equals(e1, e2)
+ if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray))
+ {
+ //printf("Lowering to __equals %s %s\n", exp.e1.toChars(), exp.e2.toChars());
+
+ if (!verifyHookExist(exp.loc, *sc, Id.__equals, "equal checks on arrays"))
+ return setError();
+
+ Expression __equals = new IdentifierExp(exp.loc, Id.empty);
+ Identifier id = Identifier.idPool("__equals");
+ __equals = new DotIdExp(exp.loc, __equals, Id.object);
+ __equals = new DotIdExp(exp.loc, __equals, id);
+
+ auto arguments = new Expressions(2);
+ (*arguments)[0] = exp.e1;
+ (*arguments)[1] = exp.e2;
+
+ __equals = new CallExp(exp.loc, __equals, arguments);
+ if (exp.op == TOK.notEqual)
+ {
+ __equals = new NotExp(exp.loc, __equals);
+ }
+ __equals = __equals.trySemantic(sc); // for better error message
+ if (!__equals)
+ {
+ exp.error("incompatible types for array comparison: `%s` and `%s`",
+ exp.e1.type.toChars(), exp.e2.type.toChars());
+ __equals = ErrorExp.get();
+ }
+
+ result = __equals;
+ return;
+ }
+
+ if (exp.e1.type.toBasetype().ty == Taarray)
+ semanticTypeInfo(sc, exp.e1.type.toBasetype());
+
+
+ if (!target.isVectorOpSupported(t1, exp.op, t2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ result = exp;
+ }
+
+ override void visit(IdentityExp exp)
+ {
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ exp.setNoderefOperands();
+
+ if (auto e = binSemanticProp(exp, sc))
+ {
+ result = e;
+ return;
+ }
+
+ if (auto e = typeCombine(exp, sc))
+ {
+ result = e;
+ return;
+ }
+
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f1 || f2)
+ return setError();
+
+ if (exp.e1.op == TOK.type || exp.e2.op == TOK.type)
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ exp.type = Type.tbool;
+
+ if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating())
+ {
+ // Cast both to complex
+ exp.e1 = exp.e1.castTo(sc, Type.tcomplex80);
+ exp.e2 = exp.e2.castTo(sc, Type.tcomplex80);
+ }
+
+ auto tb1 = exp.e1.type.toBasetype();
+ auto tb2 = exp.e2.type.toBasetype();
+ if (!target.isVectorOpSupported(tb1, exp.op, tb2))
+ {
+ result = exp.incompatibleTypes();
+ return;
+ }
+
+ if (exp.e1.op == TOK.call)
+ exp.e1 = (cast(CallExp)exp.e1).addDtorHook(sc);
+ if (exp.e2.op == TOK.call)
+ exp.e2 = (cast(CallExp)exp.e2).addDtorHook(sc);
+
+ if (exp.e1.type.toBasetype().ty == Tsarray ||
+ exp.e2.type.toBasetype().ty == Tsarray)
+ exp.deprecation("identity comparison of static arrays "
+ ~ "implicitly coerces them to slices, "
+ ~ "which are compared by reference");
+
+ result = exp;
+ }
+
+ override void visit(CondExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("CondExp::semantic('%s')\n", exp.toChars());
+ }
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
+
+ if (exp.econd.op == TOK.dotIdentifier)
+ (cast(DotIdExp)exp.econd).noderef = true;
+
+ Expression ec = exp.econd.expressionSemantic(sc);
+ ec = resolveProperties(sc, ec);
+ ec = ec.toBoolean(sc);
+
+ CtorFlow ctorflow_root = sc.ctorflow.clone();
+ Expression e1x = exp.e1.expressionSemantic(sc);
+ e1x = resolveProperties(sc, e1x);
+
+ CtorFlow ctorflow1 = sc.ctorflow;
+ sc.ctorflow = ctorflow_root;
+ Expression e2x = exp.e2.expressionSemantic(sc);
+ e2x = resolveProperties(sc, e2x);
+
+ sc.merge(exp.loc, ctorflow1);
+ ctorflow1.freeFieldinit();
+
+ if (ec.op == TOK.error)
+ {
+ result = ec;
+ return;
+ }
+ if (ec.type == Type.terror)
+ return setError();
+ exp.econd = ec;
+
+ if (e1x.op == TOK.error)
+ {
+ result = e1x;
+ return;
+ }
+ if (e1x.type == Type.terror)
+ return setError();
+ exp.e1 = e1x;
+
+ if (e2x.op == TOK.error)
+ {
+ result = e2x;
+ return;
+ }
+ if (e2x.type == Type.terror)
+ return setError();
+ exp.e2 = e2x;
+
+ auto f0 = checkNonAssignmentArrayOp(exp.econd);
+ auto f1 = checkNonAssignmentArrayOp(exp.e1);
+ auto f2 = checkNonAssignmentArrayOp(exp.e2);
+ if (f0 || f1 || f2)
+ return setError();
+
+ Type t1 = exp.e1.type;
+ Type t2 = exp.e2.type;
+ if (t1.ty == Tnoreturn)
+ {
+ exp.type = t2;
+ }
+ else if (t2.ty == Tnoreturn)
+ {
+ exp.type = t1;
+ }
+ // If either operand is void the result is void, we have to cast both
+ // the expression to void so that we explicitly discard the expression
+ // value if any
+ // https://issues.dlang.org/show_bug.cgi?id=16598
+ else if (t1.ty == Tvoid || t2.ty == Tvoid)
+ {
+ exp.type = Type.tvoid;
+ exp.e1 = exp.e1.castTo(sc, exp.type);
+ exp.e2 = exp.e2.castTo(sc, exp.type);
+ }
+ else if (t1 == t2)
+ exp.type = t1;
+ else
+ {
+ if (Expression ex = typeCombine(exp, sc))
+ {
+ result = ex;
+ return;
+ }
+
+ switch (exp.e1.type.toBasetype().ty)
+ {
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ exp.e2 = exp.e2.castTo(sc, exp.e1.type);
+ break;
+ default:
+ break;
+ }
+ switch (exp.e2.type.toBasetype().ty)
+ {
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ exp.e1 = exp.e1.castTo(sc, exp.e2.type);
+ break;
+ default:
+ break;
+ }
+ if (exp.type.toBasetype().ty == Tarray)
+ {
+ exp.e1 = exp.e1.castTo(sc, exp.type);
+ exp.e2 = exp.e2.castTo(sc, exp.type);
+ }
+ }
+ exp.type = exp.type.merge2();
+ version (none)
+ {
+ printf("res: %s\n", exp.type.toChars());
+ printf("e1 : %s\n", exp.e1.type.toChars());
+ printf("e2 : %s\n", exp.e2.type.toChars());
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=14696
+ * If either e1 or e2 contain temporaries which need dtor,
+ * make them conditional.
+ * Rewrite:
+ * cond ? (__tmp1 = ..., __tmp1) : (__tmp2 = ..., __tmp2)
+ * to:
+ * (auto __cond = cond) ? (... __tmp1) : (... __tmp2)
+ * and replace edtors of __tmp1 and __tmp2 with:
+ * __tmp1.edtor --> __cond && __tmp1.dtor()
+ * __tmp2.edtor --> __cond || __tmp2.dtor()
+ */
+ exp.hookDtors(sc);
+
+ result = exp;
+ }
+
+ override void visit(GenericExp exp)
+ {
+ static if (LOGSEMANTIC)
+ {
+ printf("GenericExp::semantic('%s')\n", exp.toChars());
+ }
+ // C11 6.5.1.1 Generic Selection
+
+ auto ec = exp.cntlExp.expressionSemantic(sc);
+ bool errors = ec.isErrorExp() !is null;
+ auto tc = ec.type;
+
+ auto types = (*exp.types)[];
+ foreach (i, ref t; types)
+ {
+ if (!t)
+ continue; // `default:` case
+ t = t.typeSemantic(ec.loc, sc);
+ if (t.isTypeError())
+ {
+ errors = true;
+ continue;
+ }
+
+ /* C11 6.5.1-2 duplicate check
+ */
+ /* C11 distinguishes int, long, and long long. But D doesn't, so depending on the
+ * C target, a long may have the same type as `int` in the D type system.
+ * So, skip checks when this may be the case. Later pick the first match
+ */
+ if (
+ (t.ty == Tint32 || t.ty == Tuns32) && target.c.longsize == 4 ||
+ (t.ty == Tint64 || t.ty == Tuns64) && target.c.longsize == 8 ||
+ (t.ty == Tfloat64 || t.ty == Timaginary64 || t.ty == Tcomplex64) && target.c.long_doublesize == 8
+ )
+ continue;
+
+ foreach (t2; types[0 .. i])
+ {
+ if (t2 && t2.equals(t))
+ {
+ error(ec.loc, "generic association type `%s` can only appear once", t.toChars());
+ errors = true;
+ break;
+ }
+ }
+ }
+
+ auto exps = (*exp.exps)[];
+ foreach (ref e; exps)
+ {
+ e = e.expressionSemantic(sc);
+ if (e.isErrorExp())
+ errors = true;
+ }
+
+ if (errors)
+ return setError();
+
+ enum size_t None = ~0;
+ size_t imatch = None;
+ size_t idefault = None;
+ foreach (const i, t; types)
+ {
+ if (t)
+ {
+ /* if tc is compatible with t, it's a match
+ * C11 6.2.7 defines a compatible type as being the same type, including qualifiers
+ */
+ if (tc.equals(t))
+ {
+ assert(imatch == None);
+ imatch = i;
+ break; // pick first match
+ }
+ }
+ else
+ idefault = i; // multiple defaults are not allowed, and are caught by cparse
+ }
+
+ if (imatch == None)
+ imatch = idefault;
+ if (imatch == None)
+ {
+ error(exp.loc, "no compatible generic association type for controlling expression type `%s`", tc.toChars());
+ return setError();
+ }
+
+ result = exps[imatch];
+ }
+
+ override void visit(FileInitExp e)
+ {
+ //printf("FileInitExp::semantic()\n");
+ e.type = Type.tstring;
+ result = e;
+ }
+
+ override void visit(LineInitExp e)
+ {
+ e.type = Type.tint32;
+ result = e;
+ }
+
+ override void visit(ModuleInitExp e)
+ {
+ //printf("ModuleInitExp::semantic()\n");
+ e.type = Type.tstring;
+ result = e;
+ }
+
+ override void visit(FuncInitExp e)
+ {
+ //printf("FuncInitExp::semantic()\n");
+ e.type = Type.tstring;
+ if (sc.func)
+ {
+ result = e.resolveLoc(Loc.initial, sc);
+ return;
+ }
+ result = e;
+ }
+
+ override void visit(PrettyFuncInitExp e)
+ {
+ //printf("PrettyFuncInitExp::semantic()\n");
+ e.type = Type.tstring;
+ if (sc.func)
+ {
+ result = e.resolveLoc(Loc.initial, sc);
+ return;
+ }
+
+ result = e;
+ }
+}
+
+/**********************************
+ * Try to run semantic routines.
+ * If they fail, return NULL.
+ */
+Expression trySemantic(Expression exp, Scope* sc)
+{
+ //printf("+trySemantic(%s)\n", exp.toChars());
+ uint errors = global.startGagging();
+ Expression e = expressionSemantic(exp, sc);
+ if (global.endGagging(errors))
+ {
+ e = null;
+ }
+ //printf("-trySemantic(%s)\n", exp.toChars());
+ return e;
+}
+
+/**************************
+ * Helper function for easy error propagation.
+ * If error occurs, returns ErrorExp. Otherwise returns NULL.
+ */
+Expression unaSemantic(UnaExp e, Scope* sc)
+{
+ static if (LOGSEMANTIC)
+ {
+ printf("UnaExp::semantic('%s')\n", e.toChars());
+ }
+ Expression e1x = e.e1.expressionSemantic(sc);
+ if (e1x.op == TOK.error)
+ return e1x;
+ e.e1 = e1x;
+ return null;
+}
+
+/**************************
+ * Helper function for easy error propagation.
+ * If error occurs, returns ErrorExp. Otherwise returns NULL.
+ */
+Expression binSemantic(BinExp e, Scope* sc)
+{
+ static if (LOGSEMANTIC)
+ {
+ printf("BinExp::semantic('%s')\n", e.toChars());
+ }
+ Expression e1x = e.e1.expressionSemantic(sc);
+ Expression e2x = e.e2.expressionSemantic(sc);
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (e1x.op == TOK.type)
+ e1x = resolveAliasThis(sc, e1x);
+ if (e2x.op == TOK.type)
+ e2x = resolveAliasThis(sc, e2x);
+
+ if (e1x.op == TOK.error)
+ return e1x;
+ if (e2x.op == TOK.error)
+ return e2x;
+ e.e1 = e1x;
+ e.e2 = e2x;
+ return null;
+}
+
+Expression binSemanticProp(BinExp e, Scope* sc)
+{
+ if (Expression ex = binSemantic(e, sc))
+ return ex;
+ Expression e1x = resolveProperties(sc, e.e1);
+ Expression e2x = resolveProperties(sc, e.e2);
+ if (e1x.op == TOK.error)
+ return e1x;
+ if (e2x.op == TOK.error)
+ return e2x;
+ e.e1 = e1x;
+ e.e2 = e2x;
+ return null;
+}
+
+// entrypoint for semantic ExpressionSemanticVisitor
+extern (C++) Expression expressionSemantic(Expression e, Scope* sc)
+{
+ scope v = new ExpressionSemanticVisitor(sc);
+ e.accept(v);
+ return v.result;
+}
+
+Expression semanticX(DotIdExp exp, Scope* sc)
+{
+ //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars());
+ if (Expression ex = unaSemantic(exp, sc))
+ return ex;
+
+ if (exp.ident == Id._mangleof)
+ {
+ // symbol.mangleof
+ Dsymbol ds;
+ switch (exp.e1.op)
+ {
+ case TOK.scope_:
+ ds = (cast(ScopeExp)exp.e1).sds;
+ goto L1;
+ case TOK.variable:
+ ds = (cast(VarExp)exp.e1).var;
+ goto L1;
+ case TOK.dotVariable:
+ ds = (cast(DotVarExp)exp.e1).var;
+ goto L1;
+ case TOK.overloadSet:
+ ds = (cast(OverExp)exp.e1).vars;
+ goto L1;
+ case TOK.template_:
+ {
+ TemplateExp te = cast(TemplateExp)exp.e1;
+ ds = te.fd ? cast(Dsymbol)te.fd : te.td;
+ }
+ L1:
+ {
+ assert(ds);
+ if (auto f = ds.isFuncDeclaration())
+ {
+ if (f.checkForwardRef(exp.loc))
+ {
+ return ErrorExp.get();
+ }
+ if (f.flags & (FUNCFLAG.purityInprocess | FUNCFLAG.safetyInprocess |
+ FUNCFLAG.nothrowInprocess | FUNCFLAG.nogcInprocess))
+ {
+ f.error(exp.loc, "cannot retrieve its `.mangleof` while inferring attributes");
+ return ErrorExp.get();
+ }
+ }
+ OutBuffer buf;
+ mangleToBuffer(ds, &buf);
+ Expression e = new StringExp(exp.loc, buf.extractSlice());
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (exp.e1.op == TOK.variable && exp.e1.type.toBasetype().ty == Tsarray && exp.ident == Id.length)
+ {
+ // bypass checkPurity
+ return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? DotExpFlag.noDeref : 0);
+ }
+
+ if (exp.e1.op == TOK.dot)
+ {
+ }
+ else
+ {
+ exp.e1 = resolvePropertiesX(sc, exp.e1);
+ }
+ if (exp.e1.op == TOK.tuple && exp.ident == Id.offsetof)
+ {
+ /* 'distribute' the .offsetof to each of the tuple elements.
+ */
+ TupleExp te = cast(TupleExp)exp.e1;
+ auto exps = new Expressions(te.exps.dim);
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression e = (*te.exps)[i];
+ e = e.expressionSemantic(sc);
+ e = new DotIdExp(e.loc, e, Id.offsetof);
+ (*exps)[i] = e;
+ }
+ // Don't evaluate te.e0 in runtime
+ Expression e = new TupleExp(exp.loc, null, exps);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (exp.e1.op == TOK.tuple && exp.ident == Id.length)
+ {
+ TupleExp te = cast(TupleExp)exp.e1;
+ // Don't evaluate te.e0 in runtime
+ Expression e = new IntegerExp(exp.loc, te.exps.dim, Type.tsize_t);
+ return e;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=14416
+ // Template has no built-in properties except for 'stringof'.
+ if ((exp.e1.op == TOK.dotTemplateDeclaration || exp.e1.op == TOK.template_) && exp.ident != Id.stringof)
+ {
+ exp.error("template `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars());
+ return ErrorExp.get();
+ }
+ if (!exp.e1.type)
+ {
+ exp.error("expression `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars());
+ return ErrorExp.get();
+ }
+
+ return exp;
+}
+
+// Resolve e1.ident without seeing UFCS.
+// If flag == 1, stop "not a property" error and return NULL.
+Expression semanticY(DotIdExp exp, Scope* sc, int flag)
+{
+ //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars());
+
+ //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }
+
+ /* Special case: rewrite this.id and super.id
+ * to be classtype.id and baseclasstype.id
+ * if we have no this pointer.
+ */
+ if ((exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_) && !hasThis(sc))
+ {
+ if (AggregateDeclaration ad = sc.getStructClassScope())
+ {
+ if (exp.e1.op == TOK.this_)
+ {
+ exp.e1 = new TypeExp(exp.e1.loc, ad.type);
+ }
+ else
+ {
+ ClassDeclaration cd = ad.isClassDeclaration();
+ if (cd && cd.baseClass)
+ exp.e1 = new TypeExp(exp.e1.loc, cd.baseClass.type);
+ }
+ }
+ }
+
+ Expression e = semanticX(exp, sc);
+ if (e != exp)
+ return e;
+
+ Expression eleft;
+ Expression eright;
+ if (exp.e1.op == TOK.dot)
+ {
+ DotExp de = cast(DotExp)exp.e1;
+ eleft = de.e1;
+ eright = de.e2;
+ }
+ else
+ {
+ eleft = null;
+ eright = exp.e1;
+ }
+
+ Type t1b = exp.e1.type.toBasetype();
+
+ if (eright.op == TOK.scope_) // also used for template alias's
+ {
+ ScopeExp ie = cast(ScopeExp)eright;
+
+ int flags = SearchLocalsOnly;
+ /* Disable access to another module's private imports.
+ * The check for 'is sds our current module' is because
+ * the current module should have access to its own imports.
+ */
+ if (ie.sds.isModule() && ie.sds != sc._module)
+ flags |= IgnorePrivateImports;
+ if (sc.flags & SCOPE.ignoresymbolvisibility)
+ flags |= IgnoreSymbolVisibility;
+ Dsymbol s = ie.sds.search(exp.loc, exp.ident, flags);
+ /* Check for visibility before resolving aliases because public
+ * aliases to private symbols are public.
+ */
+ if (s && !(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc._module, s))
+ {
+ s = null;
+ }
+ if (s)
+ {
+ auto p = s.isPackage();
+ if (p && checkAccess(sc, p))
+ {
+ s = null;
+ }
+ }
+ if (s)
+ {
+ // if 's' is a tuple variable, the tuple is returned.
+ s = s.toAlias();
+
+ exp.checkDeprecated(sc, s);
+ exp.checkDisabled(sc, s);
+
+ EnumMember em = s.isEnumMember();
+ if (em)
+ {
+ return em.getVarExp(exp.loc, sc);
+ }
+ VarDeclaration v = s.isVarDeclaration();
+ if (v)
+ {
+ //printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v.type.toChars());
+ if (!v.type ||
+ !v.type.deco && v.inuse)
+ {
+ if (v.inuse)
+ exp.error("circular reference to %s `%s`", v.kind(), v.toPrettyChars());
+ else
+ exp.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ if (v.type.ty == Terror)
+ return ErrorExp.get();
+
+ if ((v.storage_class & STC.manifest) && v._init && !exp.wantsym)
+ {
+ /* Normally, the replacement of a symbol with its initializer is supposed to be in semantic2().
+ * Introduced by https://github.com/dlang/dmd/pull/5588 which should probably
+ * be reverted. `wantsym` is the hack to work around the problem.
+ */
+ if (v.inuse)
+ {
+ error(exp.loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ e = v.expandInitializer(exp.loc);
+ v.inuse++;
+ e = e.expressionSemantic(sc);
+ v.inuse--;
+ return e;
+ }
+
+ if (v.needThis())
+ {
+ if (!eleft)
+ eleft = new ThisExp(exp.loc);
+ e = new DotVarExp(exp.loc, eleft, v);
+ e = e.expressionSemantic(sc);
+ }
+ else
+ {
+ e = new VarExp(exp.loc, v);
+ if (eleft)
+ {
+ e = new CommaExp(exp.loc, eleft, e);
+ e.type = v.type;
+ }
+ }
+ e = e.deref();
+ return e.expressionSemantic(sc);
+ }
+
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f)
+ {
+ //printf("it's a function\n");
+ if (!f.functionSemantic())
+ return ErrorExp.get();
+ if (f.needThis())
+ {
+ if (!eleft)
+ eleft = new ThisExp(exp.loc);
+ e = new DotVarExp(exp.loc, eleft, f, true);
+ e = e.expressionSemantic(sc);
+ }
+ else
+ {
+ e = new VarExp(exp.loc, f, true);
+ if (eleft)
+ {
+ e = new CommaExp(exp.loc, eleft, e);
+ e.type = f.type;
+ }
+ }
+ return e;
+ }
+ if (auto td = s.isTemplateDeclaration())
+ {
+ if (eleft)
+ e = new DotTemplateExp(exp.loc, eleft, td);
+ else
+ e = new TemplateExp(exp.loc, td);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (OverDeclaration od = s.isOverDeclaration())
+ {
+ e = new VarExp(exp.loc, od, true);
+ if (eleft)
+ {
+ e = new CommaExp(exp.loc, eleft, e);
+ e.type = Type.tvoid; // ambiguous type?
+ }
+ return e;
+ }
+ OverloadSet o = s.isOverloadSet();
+ if (o)
+ {
+ //printf("'%s' is an overload set\n", o.toChars());
+ return new OverExp(exp.loc, o);
+ }
+
+ if (auto t = s.getType())
+ {
+ return (new TypeExp(exp.loc, t)).expressionSemantic(sc);
+ }
+
+ TupleDeclaration tup = s.isTupleDeclaration();
+ if (tup)
+ {
+ if (eleft)
+ {
+ e = new DotVarExp(exp.loc, eleft, tup);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ e = new TupleExp(exp.loc, tup);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ ScopeDsymbol sds = s.isScopeDsymbol();
+ if (sds)
+ {
+ //printf("it's a ScopeDsymbol %s\n", ident.toChars());
+ e = new ScopeExp(exp.loc, sds);
+ e = e.expressionSemantic(sc);
+ if (eleft)
+ e = new DotExp(exp.loc, eleft, e);
+ return e;
+ }
+
+ Import imp = s.isImport();
+ if (imp)
+ {
+ ie = new ScopeExp(exp.loc, imp.pkg);
+ return ie.expressionSemantic(sc);
+ }
+ // BUG: handle other cases like in IdentifierExp::semantic()
+ debug
+ {
+ printf("s = '%s', kind = '%s'\n", s.toChars(), s.kind());
+ }
+ assert(0);
+ }
+ else if (exp.ident == Id.stringof)
+ {
+ e = new StringExp(exp.loc, ie.toString());
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (ie.sds.isPackage() || ie.sds.isImport() || ie.sds.isModule())
+ {
+ flag = 0;
+ }
+ if (flag)
+ return null;
+ s = ie.sds.search_correct(exp.ident);
+ if (s && symbolIsVisible(sc, s))
+ {
+ if (s.isPackage())
+ exp.error("undefined identifier `%s` in %s `%s`, perhaps add `static import %s;`", exp.ident.toChars(), ie.sds.kind(), ie.sds.toPrettyChars(), s.toPrettyChars());
+ else
+ exp.error("undefined identifier `%s` in %s `%s`, did you mean %s `%s`?", exp.ident.toChars(), ie.sds.kind(), ie.sds.toPrettyChars(), s.kind(), s.toChars());
+ }
+ else
+ exp.error("undefined identifier `%s` in %s `%s`", exp.ident.toChars(), ie.sds.kind(), ie.sds.toPrettyChars());
+ return ErrorExp.get();
+ }
+ else if (t1b.ty == Tpointer && exp.e1.type.ty != Tenum && exp.ident != Id._init && exp.ident != Id.__sizeof && exp.ident != Id.__xalignof && exp.ident != Id.offsetof && exp.ident != Id._mangleof && exp.ident != Id.stringof)
+ {
+ Type t1bn = t1b.nextOf();
+ if (flag)
+ {
+ AggregateDeclaration ad = isAggregate(t1bn);
+ if (ad && !ad.members) // https://issues.dlang.org/show_bug.cgi?id=11312
+ return null;
+ }
+
+ /* Rewrite:
+ * p.ident
+ * as:
+ * (*p).ident
+ */
+ if (flag && t1bn.ty == Tvoid)
+ return null;
+ e = new PtrExp(exp.loc, exp.e1);
+ e = e.expressionSemantic(sc);
+ return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
+ }
+ else if (exp.ident == Id.__xalignof &&
+ exp.e1.isVarExp() &&
+ exp.e1.isVarExp().var.isVarDeclaration() &&
+ exp.e1.isVarExp().var.isVarDeclaration().alignment)
+ {
+ // For `x.alignof` get the alignment of the variable, not the alignment of its type
+ const explicitAlignment = exp.e1.isVarExp().var.isVarDeclaration().alignment;
+ const naturalAlignment = exp.e1.type.alignsize();
+ const actualAlignment = (explicitAlignment == STRUCTALIGN_DEFAULT ? naturalAlignment : explicitAlignment);
+ e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t);
+ return e;
+ }
+ else
+ {
+ if (exp.e1.op == TOK.type || exp.e1.op == TOK.template_)
+ flag = 0;
+ e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
+ if (e)
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+}
+
+// Resolve e1.ident!tiargs without seeing UFCS.
+// If flag == 1, stop "not a property" error and return NULL.
+Expression semanticY(DotTemplateInstanceExp exp, Scope* sc, int flag)
+{
+ static if (LOGSEMANTIC)
+ {
+ printf("DotTemplateInstanceExpY::semantic('%s')\n", exp.toChars());
+ }
+
+ static Expression errorExp()
+ {
+ return ErrorExp.get();
+ }
+
+ Expression e1 = exp.e1;
+
+ if (exp.ti.tempdecl && exp.ti.tempdecl.parent && exp.ti.tempdecl.parent.isTemplateMixin())
+ {
+ // if 'ti.tempdecl' happens to be found in a mixin template don't lose that info
+ // and do the symbol search in that context (Issue: 19476)
+ auto tm = cast(TemplateMixin)exp.ti.tempdecl.parent;
+ e1 = new DotExp(exp.e1.loc, exp.e1, new ScopeExp(tm.loc, tm));
+ }
+
+ auto die = new DotIdExp(exp.loc, e1, exp.ti.name);
+
+ Expression e = die.semanticX(sc);
+ if (e == die)
+ {
+ exp.e1 = die.e1; // take back
+ Type t1b = exp.e1.type.toBasetype();
+ if (t1b.ty == Tarray || t1b.ty == Tsarray || t1b.ty == Taarray || t1b.ty == Tnull || (t1b.isTypeBasic() && t1b.ty != Tvoid))
+ {
+ /* No built-in type has templatized properties, so do shortcut.
+ * It is necessary in: 1024.max!"a < b"
+ */
+ if (flag)
+ return null;
+ }
+ e = die.semanticY(sc, flag);
+ if (flag)
+ {
+ if (!e ||
+ isDotOpDispatch(e))
+ {
+ /* opDispatch!tiargs would be a function template that needs IFTI,
+ * so it's not a template
+ */
+ return null;
+ }
+ }
+ }
+ assert(e);
+
+ if (e.op == TOK.error)
+ return e;
+ if (e.op == TOK.dotVariable)
+ {
+ DotVarExp dve = cast(DotVarExp)e;
+ if (FuncDeclaration fd = dve.var.isFuncDeclaration())
+ {
+ if (TemplateDeclaration td = fd.findTemplateDeclRoot())
+ {
+ e = new DotTemplateExp(dve.loc, dve.e1, td);
+ e = e.expressionSemantic(sc);
+ }
+ }
+ else if (OverDeclaration od = dve.var.isOverDeclaration())
+ {
+ exp.e1 = dve.e1; // pull semantic() result
+
+ if (!exp.findTempDecl(sc))
+ goto Lerr;
+ if (exp.ti.needsTypeInference(sc))
+ return exp;
+ exp.ti.dsymbolSemantic(sc);
+ if (!exp.ti.inst || exp.ti.errors) // if template failed to expand
+ return errorExp();
+
+ if (Declaration v = exp.ti.toAlias().isDeclaration())
+ {
+ if (v.type && !v.type.deco)
+ v.type = v.type.typeSemantic(v.loc, sc);
+ return new DotVarExp(exp.loc, exp.e1, v)
+ .expressionSemantic(sc);
+ }
+ return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti))
+ .expressionSemantic(sc);
+ }
+ }
+ else if (e.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)e;
+ if (FuncDeclaration fd = ve.var.isFuncDeclaration())
+ {
+ if (TemplateDeclaration td = fd.findTemplateDeclRoot())
+ {
+ e = new TemplateExp(ve.loc, td)
+ .expressionSemantic(sc);
+ }
+ }
+ else if (OverDeclaration od = ve.var.isOverDeclaration())
+ {
+ exp.ti.tempdecl = od;
+ return new ScopeExp(exp.loc, exp.ti)
+ .expressionSemantic(sc);
+ }
+ }
+
+ if (e.op == TOK.dotTemplateDeclaration)
+ {
+ DotTemplateExp dte = cast(DotTemplateExp)e;
+ exp.e1 = dte.e1; // pull semantic() result
+
+ exp.ti.tempdecl = dte.td;
+ if (!exp.ti.semanticTiargs(sc))
+ return errorExp();
+ if (exp.ti.needsTypeInference(sc))
+ return exp;
+ exp.ti.dsymbolSemantic(sc);
+ if (!exp.ti.inst || exp.ti.errors) // if template failed to expand
+ return errorExp();
+
+ if (Declaration v = exp.ti.toAlias().isDeclaration())
+ {
+ if (v.isFuncDeclaration() || v.isVarDeclaration())
+ {
+ return new DotVarExp(exp.loc, exp.e1, v)
+ .expressionSemantic(sc);
+ }
+ }
+ return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti))
+ .expressionSemantic(sc);
+ }
+ else if (e.op == TOK.template_)
+ {
+ exp.ti.tempdecl = (cast(TemplateExp)e).td;
+ return new ScopeExp(exp.loc, exp.ti)
+ .expressionSemantic(sc);
+ }
+ else if (e.op == TOK.dot)
+ {
+ DotExp de = cast(DotExp)e;
+
+ if (de.e2.op == TOK.overloadSet)
+ {
+ if (!exp.findTempDecl(sc) || !exp.ti.semanticTiargs(sc))
+ {
+ return errorExp();
+ }
+ if (exp.ti.needsTypeInference(sc))
+ return exp;
+ exp.ti.dsymbolSemantic(sc);
+ if (!exp.ti.inst || exp.ti.errors) // if template failed to expand
+ return errorExp();
+
+ if (Declaration v = exp.ti.toAlias().isDeclaration())
+ {
+ if (v.type && !v.type.deco)
+ v.type = v.type.typeSemantic(v.loc, sc);
+ return new DotVarExp(exp.loc, exp.e1, v)
+ .expressionSemantic(sc);
+ }
+ return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti))
+ .expressionSemantic(sc);
+ }
+ }
+ else if (e.op == TOK.overloadSet)
+ {
+ OverExp oe = cast(OverExp)e;
+ exp.ti.tempdecl = oe.vars;
+ return new ScopeExp(exp.loc, exp.ti)
+ .expressionSemantic(sc);
+ }
+
+Lerr:
+ exp.error("`%s` isn't a template", e.toChars());
+ return errorExp();
+}
+
+/***************************************
+ * If expression is shared, check that we can access it.
+ * Give error message if not.
+ *
+ * Params:
+ * e = expression to check
+ * sc = context
+ * returnRef = Whether this expression is for a `return` statement
+ * off a `ref` function, in which case a single level
+ * of dereference is allowed (e.g. `shared(int)*`).
+ * Returns:
+ * true on error
+ */
+bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false)
+{
+ if (!global.params.noSharedAccess ||
+ sc.intypeof ||
+ sc.flags & SCOPE.ctfe)
+ {
+ return false;
+ }
+
+ //printf("checkSharedAccess() %s\n", e.toChars());
+
+ static extern(C++) final class SharedCheckVisitor : SemanticTimeTransitiveVisitor
+ {
+ /// In case we don't know which expression triggered it,
+ /// e.g. for `visit(Type)` overload
+ Expression original;
+ /// Where the result is stored (`true` == error)
+ bool result;
+ /// Whether we should allow one level of dereferencing
+ bool allowRef;
+
+ /// Ctor
+ this(Expression oe, bool allowRef_)
+ {
+ this.original = oe;
+ this.allowRef = allowRef_;
+ }
+
+ void sharedError(Expression e)
+ {
+ // https://dlang.org/phobos/core_atomic.html
+ e.error("direct access to shared `%s` is not allowed, see `core.atomic`", e.toChars());
+ this.result = true;
+ }
+
+ /// Introduce base class overrides
+ alias visit = SemanticTimeTransitiveVisitor.visit;
+
+ // Error by default
+ override void visit(Expression e)
+ {
+ if (e.type.isShared())
+ this.sharedError(e);
+ }
+
+ /// Ditto
+ override void visit(Type t)
+ {
+ // Note: This handles things like `new shared(Throwable).msg`,
+ // where accessing `msg` would violate `shared`.
+ if (t.isShared())
+ this.sharedError(this.original);
+ }
+
+ // Those have no indirections / can be ignored
+ override void visit(ErrorExp e) {}
+ override void visit(ComplexExp e) {}
+ override void visit(IntegerExp e) {}
+ override void visit(NullExp e) {}
+
+ override void visit(VarExp e)
+ {
+ if (!this.allowRef && e.var.type.isShared())
+ this.sharedError(e);
+ }
+
+ override void visit(AddrExp e)
+ {
+ this.allowRef = true;
+ e.e1.accept(this);
+ }
+
+ override void visit(PtrExp e)
+ {
+ if (!this.allowRef && e.type.isShared())
+ return this.sharedError(e);
+
+ if (e.e1.type.isShared())
+ return this.sharedError(e);
+
+ this.allowRef = false;
+ e.e1.accept(this);
+ }
+
+ override void visit(DotVarExp e)
+ {
+ auto fd = e.var.isFuncDeclaration();
+ const sharedFunc = fd && fd.type.isShared;
+
+ if (!this.allowRef && e.type.isShared() && !sharedFunc)
+ return this.sharedError(e);
+
+ // Allow to use `DotVarExp` within value types
+ if (e.e1.type.ty == Tsarray || e.e1.type.ty == Tstruct)
+ return e.e1.accept(this);
+
+ // If we end up with a single `VarExp`, it might be a `ref` param
+ // `shared ref T` param == `shared(T)*`.
+ if (auto ve = e.e1.isVarExp())
+ {
+ this.allowRef = this.allowRef && (ve.var.storage_class & STC.ref_);
+ return e.e1.accept(this);
+ }
+
+ this.allowRef = false;
+ return e.e1.accept(this);
+ }
+
+ override void visit(IndexExp e)
+ {
+ if (!this.allowRef && e.type.isShared())
+ return this.sharedError(e);
+
+ if (e.e1.type.isShared())
+ return this.sharedError(e);
+
+ this.allowRef = false;
+ e.e1.accept(this);
+ }
+
+ override void visit(CommaExp e)
+ {
+ // Cannot be `return ref` since we can't use the return,
+ // but it's better to show that error than an unrelated `shared` one
+ this.allowRef = true;
+ e.e2.accept(this);
+ }
+ }
+
+ scope visitor = new SharedCheckVisitor(e, returnRef);
+ e.accept(visitor);
+ return visitor.result;
+}
+
+
+
+/****************************************************
+ * Determine if `exp`, which gets its address taken, can do so safely.
+ * Params:
+ * sc = context
+ * exp = expression having its address taken
+ * v = the variable getting its address taken
+ * Returns:
+ * `true` if ok, `false` for error
+ */
+bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v)
+{
+ //printf("checkAddressVar(exp: %s, v: %s)\n", exp.toChars(), v.toChars());
+ if (v)
+ {
+ if (!v.canTakeAddressOf())
+ {
+ exp.error("cannot take address of `%s`", exp.toChars());
+ return false;
+ }
+ if (sc.func && !sc.intypeof && !v.isDataseg())
+ {
+ const(char)* p = v.isParameter() ? "parameter" : "local";
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ // Taking the address of v means it cannot be set to 'scope' later
+ v.storage_class &= ~STC.maybescope;
+ v.doNotInferScope = true;
+ if (exp.type.hasPointers() && v.storage_class & STC.scope_ &&
+ !(v.storage_class & STC.temp) &&
+ !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+ {
+ exp.error("cannot take address of `scope` %s `%s` in `@safe` function `%s`", p, v.toChars(), sc.func.toChars());
+ return false;
+ }
+ }
+ else if (!(sc.flags & SCOPE.debug_) &&
+ !(v.storage_class & STC.temp) &&
+ sc.func.setUnsafe())
+ {
+ exp.error("cannot take address of %s `%s` in `@safe` function `%s`", p, v.toChars(), sc.func.toChars());
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*******************************
+ * Checks the attributes of a function.
+ * Purity (`pure`), safety (`@safe`), no GC allocations(`@nogc`)
+ * and usage of `deprecated` and `@disabled`-ed symbols are checked.
+ *
+ * Params:
+ * exp = expression to check attributes for
+ * sc = scope of the function
+ * f = function to be checked
+ * Returns: `true` if error occur.
+ */
+private bool checkFunctionAttributes(Expression exp, Scope* sc, FuncDeclaration f)
+{
+ with(exp)
+ {
+ bool error = checkDisabled(sc, f);
+ error |= checkDeprecated(sc, f);
+ error |= checkPurity(sc, f);
+ error |= checkSafety(sc, f);
+ error |= checkNogc(sc, f);
+ return error;
+ }
+}
+
+/*******************************
+ * Helper function for `getRightThis()`.
+ * Gets `this` of the next outer aggregate.
+ * Params:
+ * loc = location to use for error messages
+ * sc = context
+ * s = the parent symbol of the existing `this`
+ * ad = struct or class we need the correct `this` for
+ * e1 = existing `this`
+ * t = type of the existing `this`
+ * var = the specific member of ad we're accessing
+ * flag = if true, return `null` instead of throwing an error
+ * Returns:
+ * Expression representing the `this` for the var
+ */
+Expression getThisSkipNestedFuncs(const ref Loc loc, Scope* sc, Dsymbol s, AggregateDeclaration ad, Expression e1, Type t, Dsymbol var, bool flag = false)
+{
+ int n = 0;
+ while (s && s.isFuncDeclaration())
+ {
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (f.vthis)
+ {
+ n++;
+ e1 = new VarExp(loc, f.vthis);
+ if (f.isThis2)
+ {
+ // (*__this)[i]
+ if (n > 1)
+ e1 = e1.expressionSemantic(sc);
+ e1 = new PtrExp(loc, e1);
+ uint i = f.followInstantiationContext(ad);
+ e1 = new IndexExp(loc, e1, new IntegerExp(i));
+ s = f.toParentP(ad);
+ continue;
+ }
+ }
+ else
+ {
+ if (flag)
+ return null;
+ e1.error("need `this` of type `%s` to access member `%s` from static function `%s`", ad.toChars(), var.toChars(), f.toChars());
+ e1 = ErrorExp.get();
+ return e1;
+ }
+ s = s.toParent2();
+ }
+ if (n > 1 || e1.op == TOK.index)
+ e1 = e1.expressionSemantic(sc);
+ if (s && e1.type.equivalent(Type.tvoidptr))
+ {
+ if (auto sad = s.isAggregateDeclaration())
+ {
+ Type ta = sad.handleType();
+ if (ta.ty == Tstruct)
+ ta = ta.pointerTo();
+ e1.type = ta;
+ }
+ }
+ e1.type = e1.type.addMod(t.mod);
+ return e1;
+}
+
+/*******************************
+ * Make a dual-context container for use as a `this` argument.
+ * Params:
+ * loc = location to use for error messages
+ * sc = current scope
+ * fd = target function that will take the `this` argument
+ * Returns:
+ * Temporary closure variable.
+ * Note:
+ * The function `fd` is added to the nested references of the
+ * newly created variable such that a closure is made for the variable when
+ * the address of `fd` is taken.
+ */
+VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration fd)
+{
+ Type tthis2 = Type.tvoidptr.sarrayOf(2);
+ VarDeclaration vthis2 = new VarDeclaration(loc, tthis2, Identifier.generateId("__this"), null);
+ vthis2.storage_class |= STC.temp;
+ vthis2.dsymbolSemantic(sc);
+ vthis2.parent = sc.parent;
+ // make it a closure var
+ assert(sc.func);
+ sc.func.closureVars.push(vthis2);
+ // add `fd` to the nested refs
+ vthis2.nestedrefs.push(fd);
+ return vthis2;
+}
+
+/*******************************
+ * Make sure that the runtime hook `id` exists.
+ * Params:
+ * loc = location to use for error messages
+ * sc = current scope
+ * id = the hook identifier
+ * description = what the hook does
+ * module_ = what module the hook is located in
+ * Returns:
+ * a `bool` indicating if the hook is present.
+ */
+bool verifyHookExist(const ref Loc loc, ref Scope sc, Identifier id, string description, Identifier module_ = Id.object)
+{
+ auto rootSymbol = sc.search(loc, Id.empty, null);
+ if (auto moduleSymbol = rootSymbol.search(loc, module_))
+ if (moduleSymbol.search(loc, id))
+ return true;
+ error(loc, "`%s.%s` not found. The current runtime does not support %.*s, or the runtime is corrupt.", module_.toChars(), id.toChars(), cast(int)description.length, description.ptr);
+ return false;
+}
+
+/***************************************
+ * Fit elements[] to the corresponding types of the `sd`'s fields.
+ *
+ * Params:
+ * sd = the struct declaration
+ * loc = location to use for error messages
+ * sc = context
+ * elements = explicit arguments used to construct object
+ * stype = the constructed object type.
+ * Returns:
+ * false if any errors occur,
+ * otherwise true and elements[] are rewritten for the output.
+ */
+private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions* elements, Type stype)
+{
+ if (!elements)
+ return true;
+
+ const nfields = sd.nonHiddenFields();
+ size_t offset = 0;
+ for (size_t i = 0; i < elements.dim; i++)
+ {
+ Expression e = (*elements)[i];
+ if (!e)
+ continue;
+
+ e = resolveProperties(sc, e);
+ if (i >= nfields)
+ {
+ if (i <= sd.fields.dim && e.op == TOK.null_)
+ {
+ // CTFE sometimes creates null as hidden pointer; we'll allow this.
+ continue;
+ }
+ .error(loc, "more initializers than fields (%zu) of `%s`", nfields, sd.toChars());
+ return false;
+ }
+ VarDeclaration v = sd.fields[i];
+ if (v.offset < offset)
+ {
+ .error(loc, "overlapping initialization for `%s`", v.toChars());
+ if (!sd.isUnionDeclaration())
+ {
+ enum errorMsg = "`struct` initializers that contain anonymous unions" ~
+ " must initialize only the first member of a `union`. All subsequent" ~
+ " non-overlapping fields are default initialized";
+ .errorSupplemental(loc, errorMsg);
+ }
+ return false;
+ }
+ offset = cast(uint)(v.offset + v.type.size());
+
+ Type t = v.type;
+ if (stype)
+ t = t.addMod(stype.mod);
+ Type origType = t;
+ Type tb = t.toBasetype();
+
+ const hasPointers = tb.hasPointers();
+ if (hasPointers)
+ {
+ if ((stype.alignment() < target.ptrsize ||
+ (v.offset & (target.ptrsize - 1))) &&
+ (sc.func && sc.func.setUnsafe()))
+ {
+ .error(loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code",
+ sd.toChars(), v.toChars());
+ return false;
+ }
+ }
+
+ /* Look for case of initializing a static array with a too-short
+ * string literal, such as:
+ * char[5] foo = "abc";
+ * Allow this by doing an explicit cast, which will lengthen the string
+ * literal.
+ */
+ if (e.op == TOK.string_ && tb.ty == Tsarray)
+ {
+ StringExp se = cast(StringExp)e;
+ Type typeb = se.type.toBasetype();
+ TY tynto = tb.nextOf().ty;
+ if (!se.committed &&
+ (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar &&
+ se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger())
+ {
+ e = se.castTo(sc, t);
+ goto L1;
+ }
+ }
+
+ while (!e.implicitConvTo(t) && tb.ty == Tsarray)
+ {
+ /* Static array initialization, as in:
+ * T[3][5] = e;
+ */
+ t = tb.nextOf();
+ tb = t.toBasetype();
+ }
+ if (!e.implicitConvTo(t))
+ t = origType; // restore type for better diagnostic
+
+ e = e.implicitCastTo(sc, t);
+ L1:
+ if (e.op == TOK.error)
+ return false;
+
+ (*elements)[i] = doCopyOrMove(sc, e);
+ }
+ return true;
+}
+
+
+/**
+ * Returns `em` as a VariableExp
+ * Params:
+ * em = the EnumMember to wrap
+ * loc = location of use of em
+ * sc = scope of use of em
+ * Returns:
+ * VarExp referenceing `em` or ErrorExp if `em` if disabled/deprecated
+ */
+Expression getVarExp(EnumMember em, const ref Loc loc, Scope* sc)
+{
+ dsymbolSemantic(em, sc);
+ if (em.errors)
+ return ErrorExp.get();
+ em.checkDisabled(loc, sc);
+
+ if (em.depdecl && !em.depdecl._scope)
+ em.depdecl._scope = sc;
+ em.checkDeprecated(loc, sc);
+
+ if (em.errors)
+ return ErrorExp.get();
+ Expression e = new VarExp(loc, em);
+ return e.expressionSemantic(sc);
+}
+
+
+/*****************************
+ * Try to treat `exp` as a boolean,
+ * Params:
+ * exp = the expression
+ * sc = scope to evalute `exp` in
+ * Returns:
+ * Modified expression on success, ErrorExp on error
+ */
+Expression toBoolean(Expression exp, Scope* sc)
+{
+ switch(exp.op)
+ {
+ case TOK.delete_:
+ exp.error("`delete` does not give a boolean result");
+ return ErrorExp.get();
+
+ case TOK.comma:
+ auto ce = exp.isCommaExp();
+ auto ex2 = ce.e2.toBoolean(sc);
+ if (ex2.op == TOK.error)
+ return ex2;
+ ce.e2 = ex2;
+ ce.type = ce.e2.type;
+ return ce;
+
+ case TOK.assign:
+ case TOK.construct:
+ case TOK.blit:
+ // Things like:
+ // if (a = b) ...
+ // are usually mistakes.
+ exp.error("assignment cannot be used as a condition, perhaps `==` was meant?");
+ return ErrorExp.get();
+
+ //LogicalExp
+ case TOK.andAnd:
+ case TOK.orOr:
+ auto le = exp.isLogicalExp();
+ auto ex2 = le.e2.toBoolean(sc);
+ if (ex2.op == TOK.error)
+ return ex2;
+ le.e2 = ex2;
+ return le;
+
+ case TOK.question:
+ auto ce = exp.isCondExp();
+ auto ex1 = ce.e1.toBoolean(sc);
+ auto ex2 = ce.e2.toBoolean(sc);
+ if (ex1.op == TOK.error)
+ return ex1;
+ if (ex2.op == TOK.error)
+ return ex2;
+ ce.e1 = ex1;
+ ce.e2 = ex2;
+ return ce;
+
+
+ default:
+ // Default is 'yes' - do nothing
+ Expression e = exp;
+ Type t = exp.type;
+ Type tb = t.toBasetype();
+ Type att = null;
+
+ while (1)
+ {
+ // Structs can be converted to bool using opCast(bool)()
+ if (auto ts = tb.isTypeStruct())
+ {
+ AggregateDeclaration ad = ts.sym;
+ /* Don't really need to check for opCast first, but by doing so we
+ * get better error messages if it isn't there.
+ */
+ if (Dsymbol fd = search_function(ad, Id._cast))
+ {
+ e = new CastExp(exp.loc, e, Type.tbool);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ // Forward to aliasthis.
+ if (ad.aliasthis && !isRecursiveAliasThis(att, tb))
+ {
+ e = resolveAliasThis(sc, e);
+ t = e.type;
+ tb = e.type.toBasetype();
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (!t.isBoolean())
+ {
+ if (tb != Type.terror)
+ exp.error("expression `%s` of type `%s` does not have a boolean value",
+ exp.toChars(), t.toChars());
+ return ErrorExp.get();
+ }
+ return e;
+ }
+}
diff --git a/gcc/d/dmd/foreachvar.d b/gcc/d/dmd/foreachvar.d
new file mode 100644
index 00000000000..9579ac7d6a5
--- /dev/null
+++ b/gcc/d/dmd/foreachvar.d
@@ -0,0 +1,323 @@
+/**
+ * Utility to visit every variable in an expression.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/foreachvar.d, _foreachvar.d)
+ * Documentation: https://dlang.org/phobos/dmd_foreachvar.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/foreachvar.d
+ */
+
+module dmd.foreachvar;
+
+import core.stdc.stdio;
+import core.stdc.stdlib;
+import core.stdc.string;
+
+import dmd.apply;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.initsem;
+import dmd.mtype;
+import dmd.printast;
+import dmd.root.array;
+import dmd.root.rootobject;
+import dmd.statement;
+import dmd.tokens;
+import dmd.visitor;
+
+/*********************************************
+ * Visit each Expression in e, and call dgVar() on each variable declared in it.
+ * Params:
+ * e = expression tree to visit
+ * dgVar = call when a variable is declared
+ */
+void foreachVar(Expression e, void delegate(VarDeclaration) dgVar)
+{
+ if (!e)
+ return;
+
+ extern (C++) final class VarWalker : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ extern (D) void delegate(VarDeclaration) dgVar;
+
+ extern (D) this(void delegate(VarDeclaration) dgVar)
+ {
+ this.dgVar = dgVar;
+ }
+
+ override void visit(Expression e)
+ {
+ }
+
+ override void visit(ErrorExp e)
+ {
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ VarDeclaration v = e.declaration.isVarDeclaration();
+ if (!v)
+ return;
+ if (TupleDeclaration td = v.toAlias().isTupleDeclaration())
+ {
+ if (!td.objects)
+ return;
+ foreach (o; *td.objects)
+ {
+ Expression ex = isExpression(o);
+ DsymbolExp s = ex ? ex.isDsymbolExp() : null;
+ assert(s);
+ VarDeclaration v2 = s.s.isVarDeclaration();
+ assert(v2);
+ dgVar(v2);
+ }
+ }
+ else
+ dgVar(v);
+ Dsymbol s = v.toAlias();
+ if (s == v && !v.isStatic() && v._init)
+ {
+ if (auto ie = v._init.isExpInitializer())
+ ie.exp.foreachVar(dgVar);
+ }
+ }
+
+ override void visit(IndexExp e)
+ {
+ if (e.lengthVar)
+ dgVar(e.lengthVar);
+ }
+
+ override void visit(SliceExp e)
+ {
+ if (e.lengthVar)
+ dgVar(e.lengthVar);
+ }
+ }
+
+ scope VarWalker v = new VarWalker(dgVar);
+ walkPostorder(e, v);
+}
+
+/***************
+ * Transitively walk Statement s, pass Expressions to dgExp(), VarDeclarations to dgVar().
+ * Params:
+ * s = Statement to traverse
+ * dgExp = delegate to pass found Expressions to
+ * dgVar = delegate to pass found VarDeclarations to
+ */
+void foreachExpAndVar(Statement s,
+ void delegate(Expression) dgExp,
+ void delegate(VarDeclaration) dgVar)
+{
+ void visit(Statement s)
+ {
+ void visitExp(ExpStatement s)
+ {
+ if (s.exp)
+ dgExp(s.exp);
+ }
+
+ void visitDtorExp(DtorExpStatement s)
+ {
+ if (s.exp)
+ dgExp(s.exp);
+ }
+
+ void visitIf(IfStatement s)
+ {
+ dgExp(s.condition);
+ visit(s.ifbody);
+ visit(s.elsebody);
+ }
+
+ void visitDo(DoStatement s)
+ {
+ dgExp(s.condition);
+ visit(s._body);
+ }
+
+ void visitFor(ForStatement s)
+ {
+ visit(s._init);
+ if (s.condition)
+ dgExp(s.condition);
+ if (s.increment)
+ dgExp(s.increment);
+ visit(s._body);
+ }
+
+ void visitSwitch(SwitchStatement s)
+ {
+ dgExp(s.condition);
+ // Note that the body contains the Case and Default
+ // statements, so we only need to compile the expressions
+ foreach (cs; *s.cases)
+ {
+ dgExp(cs.exp);
+ }
+ visit(s._body);
+ }
+
+ void visitCase(CaseStatement s)
+ {
+ visit(s.statement);
+ }
+
+ void visitReturn(ReturnStatement s)
+ {
+ if (s.exp)
+ dgExp(s.exp);
+ }
+
+ void visitCompound(CompoundStatement s)
+ {
+ if (s.statements)
+ {
+ foreach (s2; *s.statements)
+ {
+ visit(s2);
+ }
+ }
+ }
+
+ void visitCompoundDeclaration(CompoundDeclarationStatement s)
+ {
+ visitCompound(s);
+ }
+
+ void visitUnrolledLoop(UnrolledLoopStatement s)
+ {
+ foreach (s2; *s.statements)
+ {
+ visit(s2);
+ }
+ }
+
+ void visitScope(ScopeStatement s)
+ {
+ visit(s.statement);
+ }
+
+ void visitDefault(DefaultStatement s)
+ {
+ visit(s.statement);
+ }
+
+ void visitWith(WithStatement s)
+ {
+ // If it is with(Enum) {...}, just execute the body.
+ if (s.exp.op == TOK.scope_ || s.exp.op == TOK.type)
+ {
+ }
+ else
+ {
+ dgVar(s.wthis);
+ dgExp(s.exp);
+ }
+ visit(s._body);
+ }
+
+ void visitTryCatch(TryCatchStatement s)
+ {
+ visit(s._body);
+ foreach (ca; *s.catches)
+ {
+ if (ca.var)
+ dgVar(ca.var);
+ visit(ca.handler);
+ }
+ }
+
+ void visitTryFinally(TryFinallyStatement s)
+ {
+ visit(s._body);
+ visit(s.finalbody);
+ }
+
+ void visitThrow(ThrowStatement s)
+ {
+ dgExp(s.exp);
+ }
+
+ void visitLabel(LabelStatement s)
+ {
+ visit(s.statement);
+ }
+
+ if (!s)
+ return;
+
+ final switch (s.stmt)
+ {
+ case STMT.Exp: visitExp(s.isExpStatement()); break;
+ case STMT.DtorExp: visitDtorExp(s.isDtorExpStatement()); break;
+ case STMT.Compound: visitCompound(s.isCompoundStatement()); break;
+ case STMT.CompoundDeclaration: visitCompoundDeclaration(s.isCompoundDeclarationStatement()); break;
+ case STMT.UnrolledLoop: visitUnrolledLoop(s.isUnrolledLoopStatement()); break;
+ case STMT.Scope: visitScope(s.isScopeStatement()); break;
+ case STMT.Do: visitDo(s.isDoStatement()); break;
+ case STMT.For: visitFor(s.isForStatement()); break;
+ case STMT.If: visitIf(s.isIfStatement()); break;
+ case STMT.Switch: visitSwitch(s.isSwitchStatement()); break;
+ case STMT.Case: visitCase(s.isCaseStatement()); break;
+ case STMT.Default: visitDefault(s.isDefaultStatement()); break;
+ case STMT.Return: visitReturn(s.isReturnStatement()); break;
+ case STMT.With: visitWith(s.isWithStatement()); break;
+ case STMT.TryCatch: visitTryCatch(s.isTryCatchStatement()); break;
+ case STMT.TryFinally: visitTryFinally(s.isTryFinallyStatement()); break;
+ case STMT.Throw: visitThrow(s.isThrowStatement()); break;
+ case STMT.Label: visitLabel(s.isLabelStatement()); break;
+
+ case STMT.CompoundAsm:
+ case STMT.Asm:
+ case STMT.InlineAsm:
+ case STMT.GccAsm:
+
+ case STMT.Break:
+ case STMT.Continue:
+ case STMT.GotoDefault:
+ case STMT.GotoCase:
+ case STMT.SwitchError:
+ case STMT.Goto:
+ case STMT.Pragma:
+ case STMT.Import:
+ case STMT.Error:
+ break; // ignore these
+
+ case STMT.ScopeGuard:
+ case STMT.Foreach:
+ case STMT.ForeachRange:
+ case STMT.Debug:
+ case STMT.CaseRange:
+ case STMT.StaticForeach:
+ case STMT.StaticAssert:
+ case STMT.Conditional:
+ case STMT.While:
+ case STMT.Forwarding:
+ case STMT.Compile:
+ case STMT.Peel:
+ case STMT.Synchronized:
+ assert(0); // should have been rewritten
+ }
+ }
+
+ visit(s);
+}
+
diff --git a/gcc/d/dmd/func.c b/gcc/d/dmd/func.c
deleted file mode 100644
index b8e1e318376..00000000000
--- a/gcc/d/dmd/func.c
+++ /dev/null
@@ -1,3161 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/func.c
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "init.h"
-#include "declaration.h"
-#include "attrib.h"
-#include "expression.h"
-#include "scope.h"
-#include "mtype.h"
-#include "aggregate.h"
-#include "identifier.h"
-#include "id.h"
-#include "module.h"
-#include "statement.h"
-#include "statement_rewrite_walker.h"
-#include "template.h"
-#include "hdrgen.h"
-#include "target.h"
-#include "parse.h"
-#include "root/rmem.h"
-#include "visitor.h"
-
-bool checkNestedRef(Dsymbol *s, Dsymbol *p);
-int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow);
-TypeIdentifier *getThrowable();
-
-bool MODimplicitConv(MOD modfrom, MOD modto);
-MATCH MODmethodConv(MOD modfrom, MOD modto);
-
-/***********************************************************
- * Tuple of result identifier (possibly null) and statement.
- * This is used to store out contracts: out(id){ ensure }
- */
-Ensure::Ensure()
-{
- this->id = NULL;
- this->ensure = NULL;
-}
-
-Ensure::Ensure(Identifier *id, Statement *ensure)
-{
- this->id = id;
- this->ensure = ensure;
-}
-
-Ensure Ensure::syntaxCopy()
-{
- return Ensure(id, ensure->syntaxCopy());
-}
-
-/*****************************************
- * Do syntax copy of an array of Ensure's.
- */
-Ensures *Ensure::arraySyntaxCopy(Ensures *a)
-{
- Ensures *b = NULL;
- if (a)
- {
- b = a->copy();
- for (size_t i = 0; i < a->length; i++)
- (*b)[i] = (*a)[i].syntaxCopy();
- }
- return b;
-}
-
-/********************************* FuncDeclaration ****************************/
-
-FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type)
- : Declaration(id)
-{
- //printf("FuncDeclaration(id = '%s', type = %p)\n", id->toChars(), type);
- //printf("storage_class = x%x\n", storage_class);
- this->storage_class = storage_class;
- this->type = type;
- if (type)
- {
- // Normalize storage_class, because function-type related attributes
- // are already set in the 'type' in parsing phase.
- this->storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR);
- }
- this->loc = loc;
- this->endloc = endloc;
- fthrows = NULL;
- frequire = NULL;
- fdrequire = NULL;
- fdensure = NULL;
- mangleString = NULL;
- vresult = NULL;
- returnLabel = NULL;
- fensure = NULL;
- frequires = NULL;
- fensures = NULL;
- fbody = NULL;
- localsymtab = NULL;
- vthis = NULL;
- v_arguments = NULL;
- v_argptr = NULL;
- parameters = NULL;
- labtab = NULL;
- overnext = NULL;
- overnext0 = NULL;
- vtblIndex = -1;
- hasReturnExp = 0;
- naked = false;
- generated = false;
- inlineStatusExp = ILSuninitialized;
- inlineStatusStmt = ILSuninitialized;
- inlining = PINLINEdefault;
- inlineNest = 0;
- ctfeCode = NULL;
- isArrayOp = 0;
- semantic3Errors = false;
- fes = NULL;
- interfaceVirtual = NULL;
- introducing = 0;
- tintro = NULL;
- /* The type given for "infer the return type" is a TypeFunction with
- * NULL for the return type.
- */
- inferRetType = (type && type->nextOf() == NULL);
- storage_class2 = 0;
- hasReturnExp = 0;
- nrvo_can = 1;
- nrvo_var = NULL;
- shidden = NULL;
- builtin = BUILTINunknown;
- tookAddressOf = 0;
- requiresClosure = false;
- inlinedNestedCallees = NULL;
- flags = 0;
- returns = NULL;
- gotos = NULL;
- selector = NULL;
-}
-
-FuncDeclaration *FuncDeclaration::create(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type)
-{
- return new FuncDeclaration(loc, endloc, id, storage_class, type);
-}
-
-Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s)
-{
- //printf("FuncDeclaration::syntaxCopy('%s')\n", toChars());
- FuncDeclaration *f =
- s ? (FuncDeclaration *)s
- : new FuncDeclaration(loc, endloc, ident, storage_class, type->syntaxCopy());
- f->frequires = frequires ? Statement::arraySyntaxCopy(frequires) : NULL;
- f->fensures = fensures ? Ensure::arraySyntaxCopy(fensures) : NULL;
- f->fbody = fbody ? fbody->syntaxCopy() : NULL;
- assert(!fthrows); // deprecated
- return f;
-}
-
-// Returns true if a contract can appear without a function body.
-bool allowsContractWithoutBody(FuncDeclaration *funcdecl)
-{
- assert(!funcdecl->fbody);
-
- /* Contracts can only appear without a body when they are virtual
- * interface functions or abstract.
- */
- Dsymbol *parent = funcdecl->toParent();
- InterfaceDeclaration *id = parent->isInterfaceDeclaration();
-
- if (!funcdecl->isAbstract() &&
- (funcdecl->fensures || funcdecl->frequires) &&
- !(id && funcdecl->isVirtual()))
- {
- ClassDeclaration *cd = parent->isClassDeclaration();
- if (!(cd && cd->isAbstract()))
- return false;
- }
- return true;
-}
-
-/****************************************************
- * Determine whether an 'out' contract is declared inside
- * the given function or any of its overrides.
- * Params:
- * fd = the function to search
- * Returns:
- * true found an 'out' contract
- * false didn't find one
- */
-bool FuncDeclaration::needsFensure(FuncDeclaration *fd)
-{
- if (fd->fensures)
- return true;
-
- for (size_t i = 0; i < fd->foverrides.length; i++)
- {
- FuncDeclaration *fdv = fd->foverrides[i];
-
- if (fdv->fensure)
- return true;
-
- if (needsFensure(fdv))
- return true;
- }
- return false;
-}
-
-/****************************************************
- * Check whether result variable can be built.
- * Returns:
- * `true` if the function has a return type that
- * is different from `void`.
- */
-static bool canBuildResultVar(FuncDeclaration *fd)
-{
- TypeFunction *f = (TypeFunction *)fd->type;
- return f && f->nextOf() && f->nextOf()->toBasetype()->ty != Tvoid;
-}
-
-/****************************************************
- * Rewrite contracts as statements.
- */
-void FuncDeclaration::buildEnsureRequire()
-{
- if (frequires)
- {
- /* in { statements1... }
- * in { statements2... }
- * ...
- * becomes:
- * in { { statements1... } { statements2... } ... }
- */
- assert(frequires->length);
- Loc loc = (*frequires)[0]->loc;
- Statements *s = new Statements;
- for (size_t i = 0; i < frequires->length; i++)
- {
- Statement *r = (*frequires)[i];
- s->push(new ScopeStatement(r->loc, r, r->loc));
- }
- frequire = new CompoundStatement(loc, s);
- }
-
- if (fensures)
- {
- /* out(id1) { statements1... }
- * out(id2) { statements2... }
- * ...
- * becomes:
- * out(__result) { { ref id1 = __result; { statements1... } }
- * { ref id2 = __result; { statements2... } } ... }
- */
- assert(fensures->length);
- Loc loc = (*fensures)[0].ensure->loc;
- Statements *s = new Statements;
- for (size_t i = 0; i < fensures->length; i++)
- {
- Ensure r = (*fensures)[i];
- if (r.id && canBuildResultVar(this))
- {
- Loc rloc = r.ensure->loc;
- IdentifierExp *resultId = new IdentifierExp(rloc, Id::result);
- ExpInitializer *init = new ExpInitializer(rloc, resultId);
- StorageClass stc = STCref | STCtemp | STCresult;
- VarDeclaration *decl = new VarDeclaration(rloc, NULL, r.id, init);
- decl->storage_class = stc;
- ExpStatement *sdecl = new ExpStatement(rloc, decl);
- s->push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc));
- }
- else
- {
- s->push(r.ensure);
- }
- }
- fensure = new CompoundStatement(loc, s);
- }
-
- if (!isVirtual())
- return;
-
- /* Rewrite contracts as nested functions, then call them. Doing it as nested
- * functions means that overriding functions can call them.
- */
- TypeFunction *f = (TypeFunction *)type;
-
- if (frequire)
- {
- /* in { ... }
- * becomes:
- * void __require() { ... }
- * __require();
- */
- Loc loc = frequire->loc;
- TypeFunction *tf = new TypeFunction(ParameterList(), Type::tvoid, LINKd);
- tf->isnothrow = f->isnothrow;
- tf->isnogc = f->isnogc;
- tf->purity = f->purity;
- tf->trust = f->trust;
- FuncDeclaration *fd = new FuncDeclaration(loc, loc,
- Id::require, STCundefined, tf);
- fd->fbody = frequire;
- Statement *s1 = new ExpStatement(loc, fd);
- Expression *e = new CallExp(loc, new VarExp(loc, fd, false), (Expressions *)NULL);
- Statement *s2 = new ExpStatement(loc, e);
- frequire = new CompoundStatement(loc, s1, s2);
- fdrequire = fd;
- }
-
- if (fensure)
- {
- /* out (result) { ... }
- * becomes:
- * void __ensure(ref tret result) { ... }
- * __ensure(result);
- */
- Loc loc = fensure->loc;
- Parameters *fparams = new Parameters();
- Parameter *p = NULL;
- if (canBuildResultVar(this))
- {
- p = new Parameter(STCref | STCconst, f->nextOf(), Id::result, NULL, NULL);
- fparams->push(p);
- }
- TypeFunction *tf = new TypeFunction(ParameterList(fparams), Type::tvoid, LINKd);
- tf->isnothrow = f->isnothrow;
- tf->isnogc = f->isnogc;
- tf->purity = f->purity;
- tf->trust = f->trust;
- FuncDeclaration *fd = new FuncDeclaration(loc, loc,
- Id::ensure, STCundefined, tf);
- fd->fbody = fensure;
- Statement *s1 = new ExpStatement(loc, fd);
- Expression *eresult = NULL;
- if (canBuildResultVar(this))
- eresult = new IdentifierExp(loc, Id::result);
- Expression *e = new CallExp(loc, new VarExp(loc, fd, false), eresult);
- Statement *s2 = new ExpStatement(loc, e);
- fensure = new CompoundStatement(loc, s1, s2);
- fdensure = fd;
- }
-}
-
-/****************************************************
- * Resolve forward reference of function signature -
- * parameter types, return type, and attributes.
- * Returns false if any errors exist in the signature.
- */
-bool FuncDeclaration::functionSemantic()
-{
- if (!_scope)
- return !errors;
-
- if (!originalType) // semantic not yet run
- {
- TemplateInstance *spec = isSpeculative();
- unsigned olderrs = global.errors;
- unsigned oldgag = global.gag;
- if (global.gag && !spec)
- global.gag = 0;
- dsymbolSemantic(this, _scope);
- global.gag = oldgag;
- if (spec && global.errors != olderrs)
- spec->errors = (global.errors - olderrs != 0);
- if (olderrs != global.errors) // if errors compiling this function
- return false;
- }
-
- // if inferring return type, sematic3 needs to be run
- // - When the function body contains any errors, we cannot assume
- // the inferred return type is valid.
- // So, the body errors should become the function signature error.
- if (inferRetType && type && !type->nextOf())
- return functionSemantic3();
-
- TemplateInstance *ti;
- if (isInstantiated() && !isVirtualMethod() &&
- ((ti = parent->isTemplateInstance()) == NULL || ti->isTemplateMixin() || ti->tempdecl->ident == ident))
- {
- AggregateDeclaration *ad = isMember2();
- if (ad && ad->sizeok != SIZEOKdone)
- {
- /* Currently dmd cannot resolve forward references per methods,
- * then setting SIZOKfwd is too conservative and would break existing code.
- * So, just stop method attributes inference until ad->semantic() done.
- */
- //ad->sizeok = SIZEOKfwd;
- }
- else
- return functionSemantic3() || !errors;
- }
-
- if (storage_class & STCinference)
- return functionSemantic3() || !errors;
-
- return !errors;
-}
-
-/****************************************************
- * Resolve forward reference of function body.
- * Returns false if any errors exist in the body.
- */
-bool FuncDeclaration::functionSemantic3()
-{
- if (semanticRun < PASSsemantic3 && _scope)
- {
- /* Forward reference - we need to run semantic3 on this function.
- * If errors are gagged, and it's not part of a template instance,
- * we need to temporarily ungag errors.
- */
- TemplateInstance *spec = isSpeculative();
- unsigned olderrs = global.errors;
- unsigned oldgag = global.gag;
- if (global.gag && !spec)
- global.gag = 0;
- semantic3(this, _scope);
- global.gag = oldgag;
-
- // If it is a speculatively-instantiated template, and errors occur,
- // we need to mark the template as having errors.
- if (spec && global.errors != olderrs)
- spec->errors = (global.errors - olderrs != 0);
- if (olderrs != global.errors) // if errors compiling this function
- return false;
- }
-
- return !errors && !semantic3Errors;
-}
-
-/****************************************************
- * Check that this function type is properly resolved.
- * If not, report "forward reference error" and return true.
- */
-bool FuncDeclaration::checkForwardRef(Loc loc)
-{
- if (!functionSemantic())
- return true;
-
- /* No deco means the functionSemantic() call could not resolve
- * forward referenes in the type of this function.
- */
- if (!type->deco)
- {
- bool inSemantic3 = (inferRetType && semanticRun >= PASSsemantic3);
- ::error(loc, "forward reference to %s`%s`",
- (inSemantic3 ? "inferred return type of function " : ""),
- toChars());
- return true;
- }
- return false;
-}
-
-VarDeclaration *FuncDeclaration::declareThis(Scope *sc, AggregateDeclaration *ad)
-{
- if (ad)
- {
- VarDeclaration *v;
- {
- //printf("declareThis() %s\n", toChars());
- Type *thandle = ad->handleType();
- assert(thandle);
- thandle = thandle->addMod(type->mod);
- thandle = thandle->addStorageClass(storage_class);
- v = new ThisDeclaration(loc, thandle);
- v->storage_class |= STCparameter;
- if (thandle->ty == Tstruct)
- {
- v->storage_class |= STCref;
-
- // if member function is marked 'inout', then 'this' is 'return ref'
- if (type->ty == Tfunction && ((TypeFunction *)type)->iswild & 2)
- v->storage_class |= STCreturn;
- }
- if (type->ty == Tfunction)
- {
- TypeFunction *tf = (TypeFunction *)type;
- if (tf->isreturn)
- v->storage_class |= STCreturn;
- if (tf->isscope)
- v->storage_class |= STCscope;
- }
- if (flags & FUNCFLAGinferScope && !(v->storage_class & STCscope))
- v->storage_class |= STCmaybescope;
-
- dsymbolSemantic(v, sc);
- if (!sc->insert(v))
- assert(0);
- v->parent = this;
- return v;
- }
- }
- else if (isNested())
- {
- /* The 'this' for a nested function is the link to the
- * enclosing function's stack frame.
- * Note that nested functions and member functions are disjoint.
- */
- VarDeclaration *v = new ThisDeclaration(loc, Type::tvoid->pointerTo());
- v->storage_class |= STCparameter;
- if (type->ty == Tfunction)
- {
- TypeFunction *tf = (TypeFunction *)type;
- if (tf->isreturn)
- v->storage_class |= STCreturn;
- if (tf->isscope)
- v->storage_class |= STCscope;
- }
- if (flags & FUNCFLAGinferScope && !(v->storage_class & STCscope))
- v->storage_class |= STCmaybescope;
-
- dsymbolSemantic(v, sc);
- if (!sc->insert(v))
- assert(0);
- v->parent = this;
- return v;
- }
-
- return NULL;
-}
-
-bool FuncDeclaration::equals(RootObject *o)
-{
- if (this == o)
- return true;
-
- Dsymbol *s = isDsymbol(o);
- if (s)
- {
- FuncDeclaration *fd1 = this;
- FuncDeclaration *fd2 = s->isFuncDeclaration();
- if (!fd2)
- return false;
-
- FuncAliasDeclaration *fa1 = fd1->isFuncAliasDeclaration();
- FuncAliasDeclaration *fa2 = fd2->isFuncAliasDeclaration();
- if (fa1 && fa2)
- {
- return fa1->toAliasFunc()->equals(fa2->toAliasFunc()) &&
- fa1->hasOverloads == fa2->hasOverloads;
- }
-
- if (fa1 && (fd1 = fa1->toAliasFunc())->isUnique() && !fa1->hasOverloads)
- fa1 = NULL;
- if (fa2 && (fd2 = fa2->toAliasFunc())->isUnique() && !fa2->hasOverloads)
- fa2 = NULL;
- if ((fa1 != NULL) != (fa2 != NULL))
- return false;
-
- return fd1->toParent()->equals(fd2->toParent()) &&
- fd1->ident->equals(fd2->ident) && fd1->type->equals(fd2->type);
- }
- return false;
-}
-
-/****************************************************
- * Declare result variable lazily.
- */
-
-void FuncDeclaration::buildResultVar(Scope *sc, Type *tret)
-{
- if (!vresult)
- {
- Loc loc = fensure ? fensure->loc : this->loc;
-
- /* If inferRetType is true, tret may not be a correct return type yet.
- * So, in here it may be a temporary type for vresult, and after
- * fbody->semantic() running, vresult->type might be modified.
- */
- vresult = new VarDeclaration(loc, tret, Id::result, NULL);
- vresult->storage_class |= STCnodtor | STCtemp;
- if (!isVirtual())
- vresult->storage_class |= STCconst;
- vresult->storage_class |= STCresult;
-
- // set before the semantic() for checkNestedReference()
- vresult->parent = this;
- }
-
- if (sc && vresult->semanticRun == PASSinit)
- {
- TypeFunction *tf = type->toTypeFunction();
- if (tf->isref)
- vresult->storage_class |= STCref;
- vresult->type = tret;
-
- dsymbolSemantic(vresult, sc);
-
- if (!sc->insert(vresult))
- error("out result %s is already defined", vresult->toChars());
- assert(vresult->parent == this);
- }
-}
-
-/****************************************************
- * Merge into this function the 'in' contracts of all it overrides.
- * 'in's are OR'd together, i.e. only one of them needs to pass.
- */
-
-Statement *FuncDeclaration::mergeFrequire(Statement *sf)
-{
- /* If a base function and its override both have an IN contract, then
- * only one of them needs to succeed. This is done by generating:
- *
- * void derived.in() {
- * try {
- * base.in();
- * }
- * catch () {
- * ... body of derived.in() ...
- * }
- * }
- *
- * So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid.
- * If base.in() throws, then derived.in()'s body is executed.
- */
-
- /* Implementing this is done by having the overriding function call
- * nested functions (the fdrequire functions) nested inside the overridden
- * function. This requires that the stack layout of the calling function's
- * parameters and 'this' pointer be in the same place (as the nested
- * function refers to them).
- * This is easy for the parameters, as they are all on the stack in the same
- * place by definition, since it's an overriding function. The problem is
- * getting the 'this' pointer in the same place, since it is a local variable.
- * We did some hacks in the code generator to make this happen:
- * 1. always generate exception handler frame, or at least leave space for it
- * in the frame (Windows 32 SEH only)
- * 2. always generate an EBP style frame
- * 3. since 'this' is passed in a register that is subsequently copied into
- * a stack local, allocate that local immediately following the exception
- * handler block, so it is always at the same offset from EBP.
- */
- for (size_t i = 0; i < foverrides.length; i++)
- {
- FuncDeclaration *fdv = foverrides[i];
-
- /* The semantic pass on the contracts of the overridden functions must
- * be completed before code generation occurs.
- * https://issues.dlang.org/show_bug.cgi?id=3602
- */
- if (fdv->frequires && fdv->semanticRun != PASSsemantic3done)
- {
- assert(fdv->_scope);
- Scope *sc = fdv->_scope->push();
- sc->stc &= ~STCoverride;
- semantic3(fdv, sc);
- sc->pop();
- }
-
- sf = fdv->mergeFrequire(sf);
- if (sf && fdv->fdrequire)
- {
- //printf("fdv->frequire: %s\n", fdv->frequire->toChars());
- /* Make the call:
- * try { __require(); }
- * catch (Throwable) { frequire; }
- */
- Expression *eresult = NULL;
- Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdrequire, false), eresult);
- Statement *s2 = new ExpStatement(loc, e);
-
- Catch *c = new Catch(loc, getThrowable(), NULL, sf);
- c->internalCatch = true;
- Catches *catches = new Catches();
- catches->push(c);
- sf = new TryCatchStatement(loc, s2, catches);
- }
- else
- return NULL;
- }
- return sf;
-}
-
-/****************************************************
- * Merge into this function the 'out' contracts of all it overrides.
- * 'out's are AND'd together, i.e. all of them need to pass.
- */
-
-Statement *FuncDeclaration::mergeFensure(Statement *sf, Identifier *oid)
-{
- /* Same comments as for mergeFrequire(), except that we take care
- * of generating a consistent reference to the 'result' local by
- * explicitly passing 'result' to the nested function as a reference
- * argument.
- * This won't work for the 'this' parameter as it would require changing
- * the semantic code for the nested function so that it looks on the parameter
- * list for the 'this' pointer, something that would need an unknown amount
- * of tweaking of various parts of the compiler that I'd rather leave alone.
- */
- for (size_t i = 0; i < foverrides.length; i++)
- {
- FuncDeclaration *fdv = foverrides[i];
-
- /* The semantic pass on the contracts of the overridden functions must
- * be completed before code generation occurs.
- * https://issues.dlang.org/show_bug.cgi?id=3602 and
- * https://issues.dlang.org/show_bug.cgi?id=5230
- */
- if (needsFensure(fdv) && fdv->semanticRun != PASSsemantic3done)
- {
- assert(fdv->_scope);
- Scope *sc = fdv->_scope->push();
- sc->stc &= ~STCoverride;
- semantic3(fdv, sc);
- sc->pop();
- }
-
- sf = fdv->mergeFensure(sf, oid);
- if (fdv->fdensure)
- {
- //printf("fdv->fensure: %s\n", fdv->fensure->toChars());
- // Make the call: __ensure(result)
- Expression *eresult = NULL;
- if (canBuildResultVar(this))
- {
- eresult = new IdentifierExp(loc, oid);
-
- Type *t1 = fdv->type->nextOf()->toBasetype();
- Type *t2 = this->type->nextOf()->toBasetype();
- if (t1->isBaseOf(t2, NULL))
- {
- /* Making temporary reference variable is necessary
- * in covariant return.
- * See bugzilla 5204 and 10479.
- */
- ExpInitializer *ei = new ExpInitializer(Loc(), eresult);
- VarDeclaration *v = new VarDeclaration(Loc(), t1, Identifier::generateId("__covres"), ei);
- v->storage_class |= STCtemp;
- DeclarationExp *de = new DeclarationExp(Loc(), v);
- VarExp *ve = new VarExp(Loc(), v);
- eresult = new CommaExp(Loc(), de, ve);
- }
- }
- Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, false), eresult);
- Statement *s2 = new ExpStatement(loc, e);
-
- if (sf)
- {
- sf = new CompoundStatement(sf->loc, s2, sf);
- }
- else
- sf = s2;
- }
- }
- return sf;
-}
-
-/****************************************************
- * Determine if 'this' overrides fd.
- * Return !=0 if it does.
- */
-
-int FuncDeclaration::overrides(FuncDeclaration *fd)
-{ int result = 0;
-
- if (fd->ident == ident)
- {
- int cov = type->covariant(fd->type);
- if (cov)
- { ClassDeclaration *cd1 = toParent()->isClassDeclaration();
- ClassDeclaration *cd2 = fd->toParent()->isClassDeclaration();
-
- if (cd1 && cd2 && cd2->isBaseOf(cd1, NULL))
- result = 1;
- }
- }
- return result;
-}
-
-/*************************************************
- * Find index of function in vtbl[0..length] that
- * this function overrides.
- * Prefer an exact match to a covariant one.
- * Params:
- * fix17349 = enable fix https://issues.dlang.org/show_bug.cgi?id=17349
- * Returns:
- * -1 didn't find one
- * -2 can't determine because of forward references
- */
-
-int FuncDeclaration::findVtblIndex(Dsymbols *vtbl, int dim, bool fix17349)
-{
- //printf("findVtblIndex() %s\n", toChars());
- FuncDeclaration *mismatch = NULL;
- StorageClass mismatchstc = 0;
- int mismatchvi = -1;
- int exactvi = -1;
- int bestvi = -1;
- for (int vi = 0; vi < dim; vi++)
- {
- FuncDeclaration *fdv = (*vtbl)[vi]->isFuncDeclaration();
- if (fdv && fdv->ident == ident)
- {
- if (type->equals(fdv->type)) // if exact match
- {
- if (fdv->parent->isClassDeclaration())
- {
- if (fdv->isFuture())
- {
- bestvi = vi;
- continue; // keep looking
- }
- return vi; // no need to look further
- }
-
- if (exactvi >= 0)
- {
- error("cannot determine overridden function");
- return exactvi;
- }
- exactvi = vi;
-
- bestvi = vi;
- continue;
- }
-
- StorageClass stc = 0;
- int cov = type->covariant(fdv->type, &stc, fix17349);
- //printf("\tbaseclass cov = %d\n", cov);
- switch (cov)
- {
- case 0: // types are distinct
- break;
-
- case 1:
- bestvi = vi; // covariant, but not identical
- break; // keep looking for an exact match
-
- case 2:
- mismatchvi = vi;
- mismatchstc = stc;
- mismatch = fdv; // overrides, but is not covariant
- break; // keep looking for an exact match
-
- case 3:
- return -2; // forward references
-
- default:
- assert(0);
- }
- }
- }
- if (bestvi == -1 && mismatch)
- {
- //type->print();
- //mismatch->type->print();
- //printf("%s %s\n", type->deco, mismatch->type->deco);
- //printf("stc = %llx\n", mismatchstc);
- if (mismatchstc)
- { // Fix it by modifying the type to add the storage classes
- type = type->addStorageClass(mismatchstc);
- bestvi = mismatchvi;
- }
- }
- return bestvi;
-}
-
-/*********************************
- * If function a function in a base class,
- * return that base class.
- * Params:
- * cd = class that function is in
- * Returns:
- * base class if overriding, NULL if not
- */
-BaseClass *FuncDeclaration::overrideInterface()
-{
- ClassDeclaration *cd = parent->isClassDeclaration();
- for (size_t i = 0; i < cd->interfaces.length; i++)
- {
- BaseClass *b = cd->interfaces.ptr[i];
- int v = findVtblIndex((Dsymbols *)&b->sym->vtbl, (int)b->sym->vtbl.length);
- if (v >= 0)
- return b;
- }
- return NULL;
-}
-
-/****************************************************
- * Overload this FuncDeclaration with the new one f.
- * Return true if successful; i.e. no conflict.
- */
-
-bool FuncDeclaration::overloadInsert(Dsymbol *s)
-{
- //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s->toChars(), toChars());
- assert(s != this);
-
- AliasDeclaration *ad = s->isAliasDeclaration();
- if (ad)
- {
- if (overnext)
- return overnext->overloadInsert(ad);
- if (!ad->aliassym && ad->type->ty != Tident && ad->type->ty != Tinstance)
- {
- //printf("\tad = '%s'\n", ad->type->toChars());
- return false;
- }
- overnext = ad;
- //printf("\ttrue: no conflict\n");
- return true;
- }
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (td)
- {
- if (!td->funcroot)
- td->funcroot = this;
- if (overnext)
- return overnext->overloadInsert(td);
- overnext = td;
- return true;
- }
- FuncDeclaration *fd = s->isFuncDeclaration();
- if (!fd)
- return false;
-
- if (overnext)
- {
- td = overnext->isTemplateDeclaration();
- if (td)
- fd->overloadInsert(td);
- else
- return overnext->overloadInsert(fd);
- }
- overnext = fd;
- //printf("\ttrue: no conflict\n");
- return true;
-}
-
-/***************************************************
- * Visit each overloaded function/template in turn, and call
- * (*fp)(param, s) on it.
- * Exit when no more, or (*fp)(param, f) returns nonzero.
- * Returns:
- * ==0 continue
- * !=0 done
- */
-
-int overloadApply(Dsymbol *fstart, void *param, int (*fp)(void *, Dsymbol *))
-{
- Dsymbol *d;
- Dsymbol *next;
- for (d = fstart; d; d = next)
- {
- if (OverDeclaration *od = d->isOverDeclaration())
- {
- if (od->hasOverloads)
- {
- if (int r = overloadApply(od->aliassym, param, fp))
- return r;
- }
- else
- {
- if (int r = (*fp)(param, od->aliassym))
- return r;
- }
- next = od->overnext;
- }
- else if (FuncAliasDeclaration *fa = d->isFuncAliasDeclaration())
- {
- if (fa->hasOverloads)
- {
- if (int r = overloadApply(fa->funcalias, param, fp))
- return r;
- }
- else
- {
- FuncDeclaration *fd = fa->toAliasFunc();
- if (!fd)
- {
- d->error("is aliased to a function");
- break;
- }
- if (int r = (*fp)(param, fd))
- return r;
- }
- next = fa->overnext;
- }
- else if (AliasDeclaration *ad = d->isAliasDeclaration())
- {
- next = ad->toAlias();
- if (next == ad)
- break;
- if (next == fstart)
- break;
- }
- else if (TemplateDeclaration *td = d->isTemplateDeclaration())
- {
- if (int r = (*fp)(param, td))
- return r;
- next = td->overnext;
- }
- else
- {
- FuncDeclaration *fd = d->isFuncDeclaration();
- if (!fd)
- {
- d->error("is aliased to a function");
- break; // BUG: should print error message?
- }
- if (int r = (*fp)(param, fd))
- return r;
- next = fd->overnext;
- }
- }
- return 0;
-}
-
-/********************************************
- * If there are no overloads of function f, return that function,
- * otherwise return NULL.
- */
-
-FuncDeclaration *FuncDeclaration::isUnique()
-{
- struct ParamUnique
- {
- static int fp(void *param, Dsymbol *s)
- {
- FuncDeclaration *f = s->isFuncDeclaration();
- if (!f)
- return 0;
- FuncDeclaration **pf = (FuncDeclaration **)param;
-
- if (*pf)
- {
- *pf = NULL;
- return 1; // ambiguous, done
- }
- else
- {
- *pf = f;
- return 0;
- }
- }
- };
- FuncDeclaration *result = NULL;
- overloadApply(this, &result, &ParamUnique::fp);
- return result;
-}
-
-/********************************************
- * Find function in overload list that exactly matches t.
- */
-
-FuncDeclaration *FuncDeclaration::overloadExactMatch(Type *t)
-{
- struct ParamExact
- {
- Type *t; // type to match
- FuncDeclaration *f; // return value
-
- static int fp(void *param, Dsymbol *s)
- {
- FuncDeclaration *f = s->isFuncDeclaration();
- if (!f)
- return 0;
- ParamExact *p = (ParamExact *)param;
- Type *t = p->t;
-
- if (t->equals(f->type))
- {
- p->f = f;
- return 1;
- }
-
- /* Allow covariant matches, as long as the return type
- * is just a const conversion.
- * This allows things like pure functions to match with an impure function type.
- */
- if (t->ty == Tfunction)
- { TypeFunction *tf = (TypeFunction *)f->type;
- if (tf->covariant(t) == 1 &&
- tf->nextOf()->implicitConvTo(t->nextOf()) >= MATCHconst)
- {
- p->f = f;
- return 1;
- }
- }
- return 0;
- }
- };
- ParamExact p;
- p.t = t;
- p.f = NULL;
- overloadApply(this, &p, &ParamExact::fp);
- return p.f;
-}
-
-void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod)
-{
- bool bothMutable = ((lhsMod & rhsMod) == 0);
- bool sharedMismatch = ((lhsMod ^ rhsMod) & MODshared) != 0;
- bool sharedMismatchOnly = ((lhsMod ^ rhsMod) == MODshared);
-
- if (lhsMod & MODshared)
- buf->writestring("shared ");
- else if (sharedMismatch && !(lhsMod & MODimmutable))
- buf->writestring("non-shared ");
-
- if (bothMutable && sharedMismatchOnly)
- { }
- else if (lhsMod & MODimmutable)
- buf->writestring("immutable ");
- else if (lhsMod & MODconst)
- buf->writestring("const ");
- else if (lhsMod & MODwild)
- buf->writestring("inout ");
- else
- buf->writestring("mutable ");
-}
-
-/********************************************
- * Find function in overload list that matches to the 'this' modifier.
- * There's four result types.
- *
- * 1. If the 'tthis' matches only one candidate, it's an "exact match".
- * Returns the function and 'hasOverloads' is set to false.
- * eg. If 'tthis" is mutable and there's only one mutable method.
- * 2. If there's two or more match candidates, but a candidate function will be
- * a "better match".
- * Returns the better match function but 'hasOverloads' is set to true.
- * eg. If 'tthis' is mutable, and there's both mutable and const methods,
- * the mutable method will be a better match.
- * 3. If there's two or more match candidates, but there's no better match,
- * Returns NULL and 'hasOverloads' is set to true to represent "ambiguous match".
- * eg. If 'tthis' is mutable, and there's two or more mutable methods.
- * 4. If there's no candidates, it's "no match" and returns NULL with error report.
- * e.g. If 'tthis' is const but there's no const methods.
- */
-FuncDeclaration *FuncDeclaration::overloadModMatch(Loc loc, Type *tthis, bool &hasOverloads)
-{
- //printf("FuncDeclaration::overloadModMatch('%s')\n", toChars());
- Match m;
- memset(&m, 0, sizeof(m));
- m.last = MATCHnomatch;
-
- struct ParamMod
- {
- Match *m;
- Type *tthis;
-
- static int fp(void *param, Dsymbol *s)
- {
- if (FuncDeclaration *fd = s->isFuncDeclaration())
- return ((ParamMod *)param)->fp(fd);
- return 0;
- }
- int fp(FuncDeclaration *f)
- {
- if (f == m->lastf) // skip duplicates
- return 0;
-
- m->anyf = f;
- TypeFunction *tf = f->type->toTypeFunction();
- //printf("tf = %s\n", tf->toChars());
-
- MATCH match;
- if (tthis) // non-static functions are preferred than static ones
- {
- if (f->needThis())
- match = f->isCtorDeclaration() ? MATCHexact : MODmethodConv(tthis->mod, tf->mod);
- else
- match = MATCHconst; // keep static funciton in overload candidates
- }
- else // static functions are preferred than non-static ones
- {
- if (f->needThis())
- match = MATCHconvert;
- else
- match = MATCHexact;
- }
- if (match != MATCHnomatch)
- {
- if (match > m->last) goto LfIsBetter;
- if (match < m->last) goto LlastIsBetter;
-
- /* See if one of the matches overrides the other.
- */
- if (m->lastf->overrides(f)) goto LlastIsBetter;
- if (f->overrides(m->lastf)) goto LfIsBetter;
-
- //printf("\tambiguous\n");
- m->nextf = f;
- m->count++;
- return 0;
-
- LlastIsBetter:
- //printf("\tlastbetter\n");
- m->count++; // count up
- return 0;
-
- LfIsBetter:
- //printf("\tisbetter\n");
- if (m->last <= MATCHconvert)
- {
- // clear last secondary matching
- m->nextf = NULL;
- m->count = 0;
- }
- m->last = match;
- m->lastf = f;
- m->count++; // count up
- return 0;
- }
- return 0;
- }
- };
- ParamMod p;
- p.m = &m;
- p.tthis = tthis;
- overloadApply(this, &p, &ParamMod::fp);
-
- if (m.count == 1) // exact match
- {
- hasOverloads = false;
- }
- else if (m.count > 1) // better or ambiguous match
- {
- hasOverloads = true;
- }
- else // no match
- {
- hasOverloads = true;
- TypeFunction *tf = this->type->toTypeFunction();
- assert(tthis);
- assert(!MODimplicitConv(tthis->mod, tf->mod)); // modifier mismatch
- {
- OutBuffer thisBuf, funcBuf;
- MODMatchToBuffer(&thisBuf, tthis->mod, tf->mod);
- MODMatchToBuffer(&funcBuf, tf->mod, tthis->mod);
- ::error(loc, "%smethod %s is not callable using a %sobject",
- funcBuf.peekChars(), this->toPrettyChars(), thisBuf.peekChars());
- }
- }
-
- return m.lastf;
-}
-
-/********************************************
- * Returns true if function was declared
- * directly or indirectly in a unittest block
- */
-bool FuncDeclaration::inUnittest()
-{
- Dsymbol *f = this;
- do
- {
- if (f->isUnitTestDeclaration())
- return true;
- f = f->toParent();
- } while (f);
-
- return false;
-}
-
-/********************************************
- * find function template root in overload list
- */
-
-TemplateDeclaration *FuncDeclaration::findTemplateDeclRoot()
-{
- FuncDeclaration *f = this;
- while (f && f->overnext)
- {
- //printf("f->overnext = %p %s\n", f->overnext, f->overnext->toChars());
- TemplateDeclaration *td = f->overnext->isTemplateDeclaration();
- if (td)
- return td;
- f = f->overnext->isFuncDeclaration();
- }
- return NULL;
-}
-
-/*************************************
- * Determine partial specialization order of 'this' vs g.
- * This is very similar to TemplateDeclaration::leastAsSpecialized().
- * Returns:
- * match 'this' is at least as specialized as g
- * 0 g is more specialized than 'this'
- */
-
-MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g)
-{
- /* This works by calling g() with f()'s parameters, and
- * if that is possible, then f() is at least as specialized
- * as g() is.
- */
-
- TypeFunction *tf = type->toTypeFunction();
- TypeFunction *tg = g->type->toTypeFunction();
- size_t nfparams = tf->parameterList.length();
-
- /* If both functions have a 'this' pointer, and the mods are not
- * the same and g's is not const, then this is less specialized.
- */
- if (needThis() && g->needThis() && tf->mod != tg->mod)
- {
- if (isCtorDeclaration())
- {
- if (!MODimplicitConv(tg->mod, tf->mod))
- return MATCHnomatch;
- }
- else
- {
- if (!MODimplicitConv(tf->mod, tg->mod))
- return MATCHnomatch;
- }
- }
-
- /* Create a dummy array of arguments out of the parameters to f()
- */
- Expressions args;
- args.setDim(nfparams);
- for (size_t u = 0; u < nfparams; u++)
- {
- Parameter *p = tf->parameterList[u];
- Expression *e;
- if (p->storageClass & (STCref | STCout))
- {
- e = new IdentifierExp(Loc(), p->ident);
- e->type = p->type;
- }
- else
- e = p->type->defaultInitLiteral(Loc());
- args[u] = e;
- }
-
- MATCH m = (MATCH) tg->callMatch(NULL, &args, 1);
- if (m > MATCHnomatch)
- {
- /* A variadic parameter list is less specialized than a
- * non-variadic one.
- */
- if (tf->parameterList.varargs && !tg->parameterList.varargs)
- goto L1; // less specialized
-
- return m;
- }
- L1:
- return MATCHnomatch;
-}
-
-/// Walk through candidate template overloads and print them in the diagnostics.
-struct TemplateCandidateWalker
-{
- Loc loc;
- int numToDisplay; // max num of overloads to print (-v overrides this).
-
- /// Count template overloads.
- struct CountWalker
- {
- int numOverloads;
-
- static int fp(void *param, Dsymbol *)
- {
- CountWalker *p = (CountWalker *)param;
- ++(p->numOverloads);
- return 0;
- }
- };
-
- static int fp(void *param, Dsymbol *s)
- {
- TemplateDeclaration *t = s->isTemplateDeclaration();
- if (!t) return 0;
-
- TemplateCandidateWalker *p = (TemplateCandidateWalker *)param;
-
- ::errorSupplemental(t->loc, "%s", t->toPrettyChars());
-
- if (!global.params.verbose && --(p->numToDisplay) == 0 && t->overnext)
- {
- // Too many overloads to sensibly display.
- // Just show count of remaining overloads.
- CountWalker cw;
- cw.numOverloads = 0;
- overloadApply(t->overnext, &cw, &CountWalker::fp);
-
- if (cw.numOverloads > 0)
- ::errorSupplemental(p->loc, "... (%d more, -v to show) ...", cw.numOverloads);
-
- return 1; // stop iterating
- }
-
- return 0;
- }
-};
-
-/// Walk through candidate template overloads and print them in the diagnostics.
-struct FuncCandidateWalker
-{
- Loc loc;
- int numToDisplay; // max num of overloads to print (-v overrides this).
-
- /// Count function overloads.
- struct CountWalker
- {
- int numOverloads;
-
- static int fp(void *param, Dsymbol *)
- {
- CountWalker *p = (CountWalker *)param;
- ++(p->numOverloads);
- return 0;
- }
- };
-
- static int fp(void *param, Dsymbol *s)
- {
- FuncDeclaration *fd = s->isFuncDeclaration();
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (fd)
- {
- if (fd->errors || fd->type->ty == Terror)
- return 0;
-
- TypeFunction *tf = (TypeFunction *)fd->type;
-
- ::errorSupplemental(fd->loc, "%s%s", fd->toPrettyChars(),
- parametersTypeToChars(tf->parameterList));
- }
- else
- {
- ::errorSupplemental(td->loc, "%s", td->toPrettyChars());
- }
-
- FuncCandidateWalker *p = (FuncCandidateWalker *)param;
- if (global.params.verbose || --(p->numToDisplay) != 0 || !fd)
- return 0;
-
- // Too many overloads to sensibly display.
- CountWalker cw;
- cw.numOverloads = 0;
- overloadApply(fd->overnext, &cw, &CountWalker::fp);
-
- if (cw.numOverloads > 0)
- ::errorSupplemental(p->loc, "... (%d more, -v to show) ...", cw.numOverloads);
-
- return 1; // stop iterating
- }
-};
-
-/*******************************************
- * Given a symbol that could be either a FuncDeclaration or
- * a function template, resolve it to a function symbol.
- * loc instantiation location
- * sc instantiation scope
- * tiargs initial list of template arguments
- * tthis if !NULL, the 'this' pointer argument
- * fargs arguments to function
- * flags 1: do not issue error message on no match, just return NULL
- * 2: overloadResolve only
- */
-
-FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s,
- Objects *tiargs, Type *tthis, Expressions *fargs, int flags)
-{
- if (!s)
- return NULL; // no match
-
- if ((tiargs && arrayObjectIsError(tiargs)) ||
- (fargs && arrayObjectIsError((Objects *)fargs)))
- {
- return NULL;
- }
-
- Match m;
- memset(&m, 0, sizeof(m));
- m.last = MATCHnomatch;
-
- const char *failMessage = NULL;
- functionResolve(&m, s, loc, sc, tiargs, tthis, fargs, &failMessage);
-
- if (m.last > MATCHnomatch && m.lastf)
- {
- if (m.count == 1) // exactly one match
- {
- if (!(flags & 1))
- m.lastf->functionSemantic();
- return m.lastf;
- }
- if ((flags & 2) && !tthis && m.lastf->needThis())
- {
- return m.lastf;
- }
- }
-
- /* Failed to find a best match.
- * Do nothing or print error.
- */
- if (m.last <= MATCHnomatch)
- {
- // error was caused on matched function
- if (m.count == 1)
- return m.lastf;
-
- // if do not print error messages
- if (flags & 1)
- return NULL; // no match
- }
-
- FuncDeclaration *fd = s->isFuncDeclaration();
- OverDeclaration *od = s->isOverDeclaration();
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (td && td->funcroot)
- s = fd = td->funcroot;
-
- OutBuffer tiargsBuf;
- arrayObjectsToBuffer(&tiargsBuf, tiargs);
-
- OutBuffer fargsBuf;
- fargsBuf.writeByte('(');
- argExpTypesToCBuffer(&fargsBuf, fargs);
- fargsBuf.writeByte(')');
- if (tthis)
- tthis->modToBuffer(&fargsBuf);
-
- const int numOverloadsDisplay = 5; // sensible number to display
-
- if (!m.lastf && !(flags & 1)) // no match
- {
- if (td && !fd) // all of overloads are templates
- {
- ::error(loc, "%s %s.%s cannot deduce function from argument types !(%s)%s, candidates are:",
- td->kind(), td->parent->toPrettyChars(), td->ident->toChars(),
- tiargsBuf.peekChars(), fargsBuf.peekChars());
-
- // Display candidate templates (even if there are no multiple overloads)
- TemplateCandidateWalker tcw;
- tcw.loc = loc;
- tcw.numToDisplay = numOverloadsDisplay;
- overloadApply(td, &tcw, &TemplateCandidateWalker::fp);
- }
- else if (od)
- {
- ::error(loc, "none of the overloads of `%s` are callable using argument types !(%s)%s",
- od->ident->toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars());
- }
- else
- {
- assert(fd);
-
- bool hasOverloads = fd->overnext != NULL;
- TypeFunction *tf = fd->type->toTypeFunction();
- if (tthis && !MODimplicitConv(tthis->mod, tf->mod)) // modifier mismatch
- {
- OutBuffer thisBuf, funcBuf;
- MODMatchToBuffer(&thisBuf, tthis->mod, tf->mod);
- MODMatchToBuffer(&funcBuf, tf->mod, tthis->mod);
- if (hasOverloads)
- ::error(loc, "none of the overloads of `%s` are callable using a %sobject, candidates are:",
- fd->ident->toChars(), thisBuf.peekChars());
- else
- ::error(loc, "%smethod `%s` is not callable using a %sobject",
- funcBuf.peekChars(), fd->toPrettyChars(), thisBuf.peekChars());
- }
- else
- {
- //printf("tf = %s, args = %s\n", tf->deco, (*fargs)[0]->type->deco);
- if (hasOverloads)
- ::error(loc, "none of the overloads of `%s` are callable using argument types `%s`, candidates are:",
- fd->ident->toChars(), fargsBuf.peekChars());
- else
- {
- fd->error(loc, "%s `%s%s%s` is not callable using argument types `%s`",
- fd->kind(), fd->toPrettyChars(), parametersTypeToChars(tf->parameterList),
- tf->modToChars(), fargsBuf.peekChars());
- if (failMessage)
- errorSupplemental(loc, failMessage);
- }
- }
-
- // Display candidate functions
- if (hasOverloads)
- {
- FuncCandidateWalker fcw;
- fcw.loc = loc;
- fcw.numToDisplay = numOverloadsDisplay;
- overloadApply(fd, &fcw, &FuncCandidateWalker::fp);
- }
- }
- }
- else if (m.nextf)
- {
- TypeFunction *tf1 = m.lastf->type->toTypeFunction();
- TypeFunction *tf2 = m.nextf->type->toTypeFunction();
- const char *lastprms = parametersTypeToChars(tf1->parameterList);
- const char *nextprms = parametersTypeToChars(tf2->parameterList);
- ::error(loc, "%s.%s called with argument types %s matches both:\n"
- "%s: %s%s\nand:\n%s: %s%s",
- s->parent->toPrettyChars(), s->ident->toChars(),
- fargsBuf.peekChars(),
- m.lastf->loc.toChars(), m.lastf->toPrettyChars(), lastprms,
- m.nextf->loc.toChars(), m.nextf->toPrettyChars(), nextprms);
- }
- return NULL;
-}
-
-/********************************
- * Labels are in a separate scope, one per function.
- */
-
-LabelDsymbol *FuncDeclaration::searchLabel(Identifier *ident)
-{ Dsymbol *s;
-
- if (!labtab)
- labtab = new DsymbolTable(); // guess we need one
-
- s = labtab->lookup(ident);
- if (!s)
- {
- s = new LabelDsymbol(ident);
- labtab->insert(s);
- }
- return (LabelDsymbol *)s;
-}
-
-/*****************************************
- * Determine lexical level difference from 'this' to nested function 'fd'.
- * Error if this cannot call fd.
- * Returns:
- * 0 same level
- * >0 decrease nesting by number
- * -1 increase nesting by 1 (fd is nested within 'this')
- * -2 error
- */
-
-int FuncDeclaration::getLevel(Loc loc, Scope *sc, FuncDeclaration *fd)
-{
- int level;
- Dsymbol *s;
- Dsymbol *fdparent;
-
- //printf("FuncDeclaration::getLevel(fd = '%s')\n", fd->toChars());
- fdparent = fd->toParent2();
- if (fdparent == this)
- return -1;
- s = this;
- level = 0;
- while (fd != s && fdparent != s->toParent2())
- {
- //printf("\ts = %s, '%s'\n", s->kind(), s->toChars());
- FuncDeclaration *thisfd = s->isFuncDeclaration();
- if (thisfd)
- {
- if (!thisfd->isNested() && !thisfd->vthis && !sc->intypeof)
- goto Lerr;
- }
- else
- {
- AggregateDeclaration *thiscd = s->isAggregateDeclaration();
- if (thiscd)
- {
- /* AggregateDeclaration::isNested returns true only when
- * it has a hidden pointer.
- * But, calling the function belongs unrelated lexical scope
- * is still allowed inside typeof.
- *
- * struct Map(alias fun) {
- * typeof({ return fun(); }) RetType;
- * // No member function makes Map struct 'not nested'.
- * }
- */
- if (!thiscd->isNested() && !sc->intypeof)
- goto Lerr;
- }
- else
- goto Lerr;
- }
-
- s = s->toParent2();
- assert(s);
- level++;
- }
- return level;
-
-Lerr:
- // Don't give error if in template constraint
- if (!(sc->flags & SCOPEconstraint))
- {
- const char *xstatic = isStatic() ? "static " : "";
- // better diagnostics for static functions
- ::error(loc, "%s%s %s cannot access frame of function %s",
- xstatic, kind(), toPrettyChars(), fd->toPrettyChars());
- return -2;
- }
- return 1;
-}
-
-const char *FuncDeclaration::toPrettyChars(bool QualifyTypes)
-{
- if (isMain())
- return "D main";
- else
- return Dsymbol::toPrettyChars(QualifyTypes);
-}
-
-/** for diagnostics, e.g. 'int foo(int x, int y) pure' */
-const char *FuncDeclaration::toFullSignature()
-{
- OutBuffer buf;
- functionToBufferWithIdent(type->toTypeFunction(), &buf, toChars());
- return buf.extractChars();
-}
-
-bool FuncDeclaration::isMain()
-{
- return ident == Id::main &&
- linkage != LINKc && !isMember() && !isNested();
-}
-
-bool FuncDeclaration::isCMain()
-{
- return ident == Id::main &&
- linkage == LINKc && !isMember() && !isNested();
-}
-
-bool FuncDeclaration::isWinMain()
-{
- //printf("FuncDeclaration::isWinMain() %s\n", toChars());
- return ident == Id::WinMain &&
- linkage != LINKc && !isMember();
-}
-
-bool FuncDeclaration::isDllMain()
-{
- return ident == Id::DllMain &&
- linkage != LINKc && !isMember();
-}
-
-bool FuncDeclaration::isExport() const
-{
- return protection.kind == Prot::export_;
-}
-
-bool FuncDeclaration::isImportedSymbol() const
-{
- //printf("isImportedSymbol()\n");
- //printf("protection = %d\n", protection);
- return (protection.kind == Prot::export_) && !fbody;
-}
-
-// Determine if function goes into virtual function pointer table
-
-bool FuncDeclaration::isVirtual()
-{
- if (toAliasFunc() != this)
- return toAliasFunc()->isVirtual();
-
- Dsymbol *p = toParent();
- return isMember() &&
- !(isStatic() || protection.kind == Prot::private_ || protection.kind == Prot::package_) &&
- p->isClassDeclaration() &&
- !(p->isInterfaceDeclaration() && isFinalFunc());
-}
-
-// Determine if a function is pedantically virtual
-
-bool FuncDeclaration::isVirtualMethod()
-{
- if (toAliasFunc() != this)
- return toAliasFunc()->isVirtualMethod();
-
- //printf("FuncDeclaration::isVirtualMethod() %s\n", toChars());
- if (!isVirtual())
- return false;
- // If it's a final method, and does not override anything, then it is not virtual
- if (isFinalFunc() && foverrides.length == 0)
- {
- return false;
- }
- return true;
-}
-
-bool FuncDeclaration::isFinalFunc()
-{
- if (toAliasFunc() != this)
- return toAliasFunc()->isFinalFunc();
-
- ClassDeclaration *cd;
- return isMember() &&
- (Declaration::isFinal() ||
- ((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal));
-}
-
-bool FuncDeclaration::isCodeseg() const
-{
- return true; // functions are always in the code segment
-}
-
-bool FuncDeclaration::isOverloadable()
-{
- return true; // functions can be overloaded
-}
-
-PURE FuncDeclaration::isPure()
-{
- //printf("FuncDeclaration::isPure() '%s'\n", toChars());
- TypeFunction *tf = type->toTypeFunction();
- if (flags & FUNCFLAGpurityInprocess)
- setImpure();
- if (tf->purity == PUREfwdref)
- tf->purityLevel();
- PURE purity = tf->purity;
- if (purity > PUREweak && isNested())
- purity = PUREweak;
- if (purity > PUREweak && needThis())
- {
- // The attribute of the 'this' reference affects purity strength
- if (type->mod & MODimmutable)
- ;
- else if (type->mod & (MODconst | MODwild) && purity >= PUREconst)
- purity = PUREconst;
- else
- purity = PUREweak;
- }
- tf->purity = purity;
- // ^ This rely on the current situation that every FuncDeclaration has a
- // unique TypeFunction.
- return purity;
-}
-
-PURE FuncDeclaration::isPureBypassingInference()
-{
- if (flags & FUNCFLAGpurityInprocess)
- return PUREfwdref;
- else
- return isPure();
-}
-
-/**************************************
- * The function is doing something impure,
- * so mark it as impure.
- * If there's a purity error, return true.
- */
-bool FuncDeclaration::setImpure()
-{
- if (flags & FUNCFLAGpurityInprocess)
- {
- flags &= ~FUNCFLAGpurityInprocess;
- if (fes)
- fes->func->setImpure();
- }
- else if (isPure())
- return true;
- return false;
-}
-
-bool FuncDeclaration::isSafe()
-{
- if (flags & FUNCFLAGsafetyInprocess)
- setUnsafe();
- return type->toTypeFunction()->trust == TRUSTsafe;
-}
-
-bool FuncDeclaration::isSafeBypassingInference()
-{
- return !(flags & FUNCFLAGsafetyInprocess) && isSafe();
-}
-
-bool FuncDeclaration::isTrusted()
-{
- if (flags & FUNCFLAGsafetyInprocess)
- setUnsafe();
- return type->toTypeFunction()->trust == TRUSTtrusted;
-}
-
-/**************************************
- * The function is doing something unsave,
- * so mark it as unsafe.
- * If there's a safe error, return true.
- */
-bool FuncDeclaration::setUnsafe()
-{
- if (flags & FUNCFLAGsafetyInprocess)
- {
- flags &= ~FUNCFLAGsafetyInprocess;
- type->toTypeFunction()->trust = TRUSTsystem;
- if (fes)
- fes->func->setUnsafe();
- }
- else if (isSafe())
- return true;
- return false;
-}
-
-bool FuncDeclaration::isNogc()
-{
- if (flags & FUNCFLAGnogcInprocess)
- setGC();
- return type->toTypeFunction()->isnogc;
-}
-
-bool FuncDeclaration::isNogcBypassingInference()
-{
- return !(flags & FUNCFLAGnogcInprocess) && isNogc();
-}
-
-/**************************************
- * The function is doing something that may allocate with the GC,
- * so mark it as not nogc (not no-how).
- * Returns:
- * true if function is marked as @nogc, meaning a user error occurred
- */
-bool FuncDeclaration::setGC()
-{
- if (flags & FUNCFLAGnogcInprocess)
- {
- flags &= ~FUNCFLAGnogcInprocess;
- type->toTypeFunction()->isnogc = false;
- if (fes)
- fes->func->setGC();
- }
- else if (isNogc())
- return true;
- return false;
-}
-
-/**************************************
- * Returns an indirect type one step from t.
- */
-
-Type *getIndirection(Type *t)
-{
- t = t->baseElemOf();
- if (t->ty == Tarray || t->ty == Tpointer)
- return t->nextOf()->toBasetype();
- if (t->ty == Taarray || t->ty == Tclass)
- return t;
- if (t->ty == Tstruct)
- return t->hasPointers() ? t : NULL; // TODO
-
- // should consider TypeDelegate?
- return NULL;
-}
-
-/**************************************
- * Returns true if memory reachable through a reference B to a value of type tb,
- * which has been constructed with a reference A to a value of type ta
- * available, can alias memory reachable from A based on the types involved
- * (either directly or via any number of indirections).
- *
- * Note that this relation is not symmetric in the two arguments. For example,
- * a const(int) reference can point to a pre-existing int, but not the other
- * way round.
- */
-bool traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool reversePass = false)
-{
- Type *source = ta;
- Type *target = tb;
- if (reversePass)
- {
- source = tb;
- target = ta;
- }
-
- if (source->constConv(target))
- return true;
- else if (target->ty == Tvoid && MODimplicitConv(source->mod, target->mod))
- return true;
-
- // No direct match, so try breaking up one of the types (starting with tb).
- Type *tbb = tb->toBasetype()->baseElemOf();
- if (tbb != tb)
- return traverseIndirections(ta, tbb, p, reversePass);
-
- // context date to detect circular look up
- struct Ctxt
- {
- Ctxt *prev;
- Type *type;
- };
- Ctxt *ctxt = (Ctxt *)p;
-
- if (tb->ty == Tclass || tb->ty == Tstruct)
- {
- for (Ctxt *c = ctxt; c; c = c->prev)
- if (tb == c->type) return false;
- Ctxt c;
- c.prev = ctxt;
- c.type = tb;
-
- AggregateDeclaration *sym = tb->toDsymbol(NULL)->isAggregateDeclaration();
- for (size_t i = 0; i < sym->fields.length; i++)
- {
- VarDeclaration *v = sym->fields[i];
- Type *tprmi = v->type->addMod(tb->mod);
- //printf("\ttb = %s, tprmi = %s\n", tb->toChars(), tprmi->toChars());
- if (traverseIndirections(ta, tprmi, &c, reversePass))
- return true;
- }
- }
- else if (tb->ty == Tarray || tb->ty == Taarray || tb->ty == Tpointer)
- {
- Type *tind = tb->nextOf();
- if (traverseIndirections(ta, tind, ctxt, reversePass))
- return true;
- }
- else if (tb->hasPointers())
- {
- // FIXME: function pointer/delegate types should be considered.
- return true;
- }
-
- // Still no match, so try breaking up ta if we have note done so yet.
- if (!reversePass)
- return traverseIndirections(tb, ta, ctxt, true);
-
- return false;
-}
-
-/********************************************
- * Returns true if the function return value has no indirection
- * which comes from the parameters.
- */
-
-bool FuncDeclaration::isolateReturn()
-{
- TypeFunction *tf = type->toTypeFunction();
- assert(tf->next);
-
- Type *treti = tf->next;
- treti = tf->isref ? treti : getIndirection(treti);
- if (!treti)
- return true; // target has no mutable indirection
- return parametersIntersect(treti);
-}
-
-/********************************************
- * Returns true if an object typed t can have indirections
- * which come from the parameters.
- */
-
-bool FuncDeclaration::parametersIntersect(Type *t)
-{
- assert(t);
- if (!isPureBypassingInference() || isNested())
- return false;
-
- TypeFunction *tf = type->toTypeFunction();
-
- //printf("parametersIntersect(%s) t = %s\n", tf->toChars(), t->toChars());
-
- size_t dim = tf->parameterList.length();
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *fparam = tf->parameterList[i];
- if (!fparam->type)
- continue;
- Type *tprmi = (fparam->storageClass & (STClazy | STCout | STCref))
- ? fparam->type : getIndirection(fparam->type);
- if (!tprmi)
- continue; // there is no mutable indirection
-
- //printf("\t[%d] tprmi = %d %s\n", i, tprmi->ty, tprmi->toChars());
- if (traverseIndirections(tprmi, t))
- return false;
- }
- if (AggregateDeclaration *ad = isCtorDeclaration() ? NULL : isThis())
- {
- Type *tthis = ad->getType()->addMod(tf->mod);
- //printf("\ttthis = %s\n", tthis->toChars());
- if (traverseIndirections(tthis, t))
- return false;
- }
-
- return true;
-}
-
-/****************************************
- * Determine if function needs a static frame pointer.
- * Returns:
- * `true` if function is really nested within other function.
- * Contracts:
- * If isNested() returns true, isThis() should return false.
- */
-bool FuncDeclaration::isNested()
-{
- FuncDeclaration *f = toAliasFunc();
- //printf("\ttoParent2() = '%s'\n", f->toParent2()->toChars());
- return ((f->storage_class & STCstatic) == 0) &&
- (f->linkage == LINKd) &&
- (f->toParent2()->isFuncDeclaration() != NULL);
-}
-
-/****************************************
- * Determine if function is a non-static member function
- * that has an implicit 'this' expression.
- * Returns:
- * The aggregate it is a member of, or null.
- * Contracts:
- * If isThis() returns true, isNested() should return false.
- */
-AggregateDeclaration *FuncDeclaration::isThis()
-{
- //printf("+FuncDeclaration::isThis() '%s'\n", toChars());
- AggregateDeclaration *ad = (storage_class & STCstatic) ? NULL : isMember2();
- //printf("-FuncDeclaration::isThis() %p\n", ad);
- return ad;
-}
-
-bool FuncDeclaration::needThis()
-{
- //printf("FuncDeclaration::needThis() '%s'\n", toChars());
- return toAliasFunc()->isThis() != NULL;
-}
-
-bool FuncDeclaration::addPreInvariant()
-{
- AggregateDeclaration *ad = isThis();
- ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL;
- return (ad && !(cd && cd->isCPPclass()) &&
- global.params.useInvariants == CHECKENABLEon &&
- (protection.kind == Prot::protected_ || protection.kind == Prot::public_ || protection.kind == Prot::export_) &&
- !naked);
-}
-
-bool FuncDeclaration::addPostInvariant()
-{
- AggregateDeclaration *ad = isThis();
- ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL;
- return (ad && !(cd && cd->isCPPclass()) &&
- ad->inv &&
- global.params.useInvariants == CHECKENABLEon &&
- (protection.kind == Prot::protected_ || protection.kind == Prot::public_ || protection.kind == Prot::export_) &&
- !naked);
-}
-
-/**********************************
- * Generate a FuncDeclaration for a runtime library function.
- */
-
-FuncDeclaration *FuncDeclaration::genCfunc(Parameters *fparams, Type *treturn, const char *name, StorageClass stc)
-{
- return genCfunc(fparams, treturn, Identifier::idPool(name), stc);
-}
-
-FuncDeclaration *FuncDeclaration::genCfunc(Parameters *fparams, Type *treturn, Identifier *id, StorageClass stc)
-{
- FuncDeclaration *fd;
- TypeFunction *tf;
- Dsymbol *s;
- static DsymbolTable *st = NULL;
-
- //printf("genCfunc(name = '%s')\n", id->toChars());
- //printf("treturn\n\t"); treturn->print();
-
- // See if already in table
- if (!st)
- st = new DsymbolTable();
- s = st->lookup(id);
- if (s)
- {
- fd = s->isFuncDeclaration();
- assert(fd);
- assert(fd->type->nextOf()->equals(treturn));
- }
- else
- {
- tf = new TypeFunction(ParameterList(fparams), treturn, LINKc, stc);
- fd = new FuncDeclaration(Loc(), Loc(), id, STCstatic, tf);
- fd->protection = Prot(Prot::public_);
- fd->linkage = LINKc;
-
- st->insert(fd);
- }
- return fd;
-}
-
-/******************
- * Check parameters and return type of D main() function.
- * Issue error messages.
- */
-void FuncDeclaration::checkDmain()
-{
- TypeFunction *tf = type->toTypeFunction();
- const size_t nparams = tf->parameterList.length();
- bool argerr = false;
- if (nparams == 1)
- {
- Parameter *fparam0 = tf->parameterList[0];
- Type *t = fparam0->type->toBasetype();
- if (t->ty != Tarray ||
- t->nextOf()->ty != Tarray ||
- t->nextOf()->nextOf()->ty != Tchar ||
- fparam0->storageClass & (STCout | STCref | STClazy))
- {
- argerr = true;
- }
- }
-
- if (!tf->nextOf())
- error("must return int or void");
- else if (tf->nextOf()->ty != Tint32 && tf->nextOf()->ty != Tvoid)
- error("must return int or void, not %s", tf->nextOf()->toChars());
- else if (tf->parameterList.varargs || nparams >= 2 || argerr)
- error("parameters must be main() or main(string[] args)");
-}
-
-/***********************************************
- * Check all return statements for a function to verify that returning
- * using NRVO is possible.
- *
- * Returns:
- * `false` if the result cannot be returned by hidden reference.
- */
-bool FuncDeclaration::checkNRVO()
-{
- if (!nrvo_can || returns == NULL)
- return false;
-
- TypeFunction *tf = type->toTypeFunction();
- if (tf->isref)
- return false;
-
- for (size_t i = 0; i < returns->length; i++)
- {
- ReturnStatement *rs = (*returns)[i];
-
- if (VarExp *ve = rs->exp->isVarExp())
- {
- VarDeclaration *v = ve->var->isVarDeclaration();
- if (!v || v->isOut() || v->isRef())
- return false;
- else if (nrvo_var == NULL)
- {
- // Variables in the data segment (e.g. globals, TLS or not),
- // parameters and closure variables cannot be NRVOed.
- if (v->isDataseg() || v->isParameter() || v->toParent2() != this)
- return false;
- //printf("Setting nrvo to %s\n", v->toChars());
- nrvo_var = v;
- }
- else if (nrvo_var != v)
- return false;
- }
- else //if (!exp->isLvalue()) // keep NRVO-ability
- return false;
- }
- return true;
-}
-
-const char *FuncDeclaration::kind() const
-{
- return generated ? "generated function" : "function";
-}
-
-/*********************************************
- * In the current function, we are calling 'this' function.
- * 1. Check to see if the current function can call 'this' function, issue error if not.
- * 2. If the current function is not the parent of 'this' function, then add
- * the current function to the list of siblings of 'this' function.
- * 3. If the current function is a literal, and it's accessing an uplevel scope,
- * then mark it as a delegate.
- * Returns true if error occurs.
- */
-bool FuncDeclaration::checkNestedReference(Scope *sc, Loc loc)
-{
- //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
-
- if (FuncLiteralDeclaration *fld = this->isFuncLiteralDeclaration())
- {
- if (fld->tok == TOKreserved)
- {
- fld->tok = TOKfunction;
- fld->vthis = NULL;
- }
- }
-
- if (!parent || parent == sc->parent)
- return false;
- if (ident == Id::require || ident == Id::ensure)
- return false;
- if (!isThis() && !isNested())
- return false;
-
- // The current function
- FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();
- if (!fdthis)
- return false; // out of function scope
-
- Dsymbol *p = toParent2();
-
- // Function literals from fdthis to p must be delegates
- checkNestedRef(fdthis, p);
-
- if (isNested())
- {
- // The function that this function is in
- FuncDeclaration *fdv = p->isFuncDeclaration();
- if (!fdv)
- return false;
- if (fdv == fdthis)
- return false;
-
- //printf("this = %s in [%s]\n", this->toChars(), this->loc.toChars());
- //printf("fdv = %s in [%s]\n", fdv->toChars(), fdv->loc.toChars());
- //printf("fdthis = %s in [%s]\n", fdthis->toChars(), fdthis->loc.toChars());
-
- // Add this function to the list of those which called us
- if (fdthis != this)
- {
- bool found = false;
- for (size_t i = 0; i < siblingCallers.length; ++i)
- {
- if (siblingCallers[i] == fdthis)
- found = true;
- }
- if (!found)
- {
- //printf("\tadding sibling %s\n", fdthis->toPrettyChars());
- if (!sc->intypeof && !(sc->flags & SCOPEcompile))
- siblingCallers.push(fdthis);
- }
- }
-
- int lv = fdthis->getLevel(loc, sc, fdv);
- if (lv == -2)
- return true; // error
- if (lv == -1)
- return false; // downlevel call
- if (lv == 0)
- return false; // same level call
- // Uplevel call
- }
- return false;
-}
-
-/* For all functions between outerFunc and f, mark them as needing
- * a closure.
- */
-void markAsNeedingClosure(Dsymbol *f, FuncDeclaration *outerFunc)
-{
- for (Dsymbol *sx = f; sx && sx != outerFunc; sx = sx->parent)
- {
- FuncDeclaration *fy = sx->isFuncDeclaration();
- if (fy && fy->closureVars.length)
- {
- /* fy needs a closure if it has closureVars[],
- * because the frame pointer in the closure will be accessed.
- */
- fy->requiresClosure = true;
- }
- }
-}
-
-
-/* Given a nested function f inside a function outerFunc, check
- * if any sibling callers of f have escaped. If so, mark
- * all the enclosing functions as needing closures.
- * Return true if any closures were detected.
- * This is recursive: we need to check the callers of our siblings.
- * Note that nested functions can only call lexically earlier nested
- * functions, so loops are impossible.
- */
-bool checkEscapingSiblings(FuncDeclaration *f, FuncDeclaration *outerFunc, void *p = NULL)
-{
- struct PrevSibling
- {
- PrevSibling *p;
- FuncDeclaration *f;
- };
-
- PrevSibling ps;
- ps.p = (PrevSibling *)p;
- ps.f = f;
-
- //printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f->toChars(), outerFunc->toChars());
- bool bAnyClosures = false;
- for (size_t i = 0; i < f->siblingCallers.length; ++i)
- {
- FuncDeclaration *g = f->siblingCallers[i];
- if (g->isThis() || g->tookAddressOf)
- {
- markAsNeedingClosure(g, outerFunc);
- bAnyClosures = true;
- }
-
- PrevSibling *prev = (PrevSibling *)p;
- while (1)
- {
- if (!prev)
- {
- bAnyClosures |= checkEscapingSiblings(g, outerFunc, &ps);
- break;
- }
- if (prev->f == g)
- break;
- prev = prev->p;
- }
- }
- //printf("\t%d\n", bAnyClosures);
- return bAnyClosures;
-}
-
-
-/*******************************
- * Look at all the variables in this function that are referenced
- * by nested functions, and determine if a closure needs to be
- * created for them.
- */
-
-bool FuncDeclaration::needsClosure()
-{
- /* Need a closure for all the closureVars[] if any of the
- * closureVars[] are accessed by a
- * function that escapes the scope of this function.
- * We take the conservative approach and decide that a function needs
- * a closure if it:
- * 1) is a virtual function
- * 2) has its address taken
- * 3) has a parent that escapes
- * 4) calls another nested function that needs a closure
- *
- * Note that since a non-virtual function can be called by
- * a virtual one, if that non-virtual function accesses a closure
- * var, the closure still has to be taken. Hence, we check for isThis()
- * instead of isVirtual(). (thanks to David Friedman)
- *
- * When the function returns a local struct or class, `requiresClosure`
- * is already set to `true` upon entering this function when the
- * struct/class refers to a local variable and a closure is needed.
- */
-
- //printf("FuncDeclaration::needsClosure() %s\n", toChars());
-
- if (requiresClosure)
- goto Lyes;
-
- for (size_t i = 0; i < closureVars.length; i++)
- {
- VarDeclaration *v = closureVars[i];
- //printf("\tv = %s\n", v->toChars());
-
- for (size_t j = 0; j < v->nestedrefs.length; j++)
- {
- FuncDeclaration *f = v->nestedrefs[j];
- assert(f != this);
-
- //printf("\t\tf = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf);
-
- /* Look to see if f escapes. We consider all parents of f within
- * this, and also all siblings which call f; if any of them escape,
- * so does f.
- * Mark all affected functions as requiring closures.
- */
- for (Dsymbol *s = f; s && s != this; s = s->parent)
- {
- FuncDeclaration *fx = s->isFuncDeclaration();
- if (!fx)
- continue;
- if (fx->isThis() || fx->tookAddressOf)
- {
- //printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx->toChars(), fx->isVirtual(), fx->isThis(), fx->tookAddressOf);
-
- /* Mark as needing closure any functions between this and f
- */
- markAsNeedingClosure( (fx == f) ? fx->parent : fx, this);
-
- requiresClosure = true;
- }
-
- /* We also need to check if any sibling functions that
- * called us, have escaped. This is recursive: we need
- * to check the callers of our siblings.
- */
- if (checkEscapingSiblings(fx, this))
- requiresClosure = true;
-
- /* Bugzilla 12406: Iterate all closureVars to mark all descendant
- * nested functions that access to the closing context of this funciton.
- */
- }
- }
- }
- if (requiresClosure)
- goto Lyes;
-
- return false;
-
-Lyes:
- //printf("\tneeds closure\n");
- return true;
-}
-
-/***********************************************
- * Check that the function contains any closure.
- * If it's @nogc, report suitable errors.
- * This is mostly consistent with FuncDeclaration::needsClosure().
- *
- * Returns:
- * true if any errors occur.
- */
-bool FuncDeclaration::checkClosure()
-{
- if (!needsClosure())
- return false;
-
- if (setGC())
- {
- error("is @nogc yet allocates closures with the GC");
- if (global.gag) // need not report supplemental errors
- return true;
- }
- else
- {
- printGCUsage(loc, "using closure causes GC allocation");
- return false;
- }
-
- FuncDeclarations a;
- for (size_t i = 0; i < closureVars.length; i++)
- {
- VarDeclaration *v = closureVars[i];
-
- for (size_t j = 0; j < v->nestedrefs.length; j++)
- {
- FuncDeclaration *f = v->nestedrefs[j];
- assert(f != this);
-
- for (Dsymbol *s = f; s && s != this; s = s->parent)
- {
- FuncDeclaration *fx = s->isFuncDeclaration();
- if (!fx)
- continue;
- if (fx->isThis() || fx->tookAddressOf)
- goto Lfound;
- if (checkEscapingSiblings(fx, this))
- goto Lfound;
- }
- continue;
-
- Lfound:
- for (size_t k = 0; ; k++)
- {
- if (k == a.length)
- {
- a.push(f);
- ::errorSupplemental(f->loc, "%s closes over variable %s at %s",
- f->toPrettyChars(), v->toChars(), v->loc.toChars());
- break;
- }
- if (a[k] == f)
- break;
- }
- continue;
- }
- }
-
- return true;
-}
-
-/***********************************************
- * Determine if function's variables are referenced by a function
- * nested within it.
- */
-
-bool FuncDeclaration::hasNestedFrameRefs()
-{
- if (closureVars.length)
- return true;
-
- /* If a virtual function has contracts, assume its variables are referenced
- * by those contracts, even if they aren't. Because they might be referenced
- * by the overridden or overriding function's contracts.
- * This can happen because frequire and fensure are implemented as nested functions,
- * and they can be called directly by an overriding function and the overriding function's
- * context had better match, or Bugzilla 7335 will bite.
- */
- if (fdrequire || fdensure)
- return true;
-
- if (foverrides.length && isVirtualMethod())
- {
- for (size_t i = 0; i < foverrides.length; i++)
- {
- FuncDeclaration *fdv = foverrides[i];
- if (fdv->hasNestedFrameRefs())
- return true;
- }
- }
-
- return false;
-}
-
-/*********************************************
- * Return the function's parameter list, and whether
- * it is variadic or not.
- */
-
-ParameterList FuncDeclaration::getParameterList()
-{
- if (type)
- {
- TypeFunction *fdtype = type->toTypeFunction();
- return fdtype->parameterList;
- }
-
- return ParameterList();
-}
-
-
-/****************************** FuncAliasDeclaration ************************/
-
-// Used as a way to import a set of functions from another scope into this one.
-
-FuncAliasDeclaration::FuncAliasDeclaration(Identifier *ident, FuncDeclaration *funcalias, bool hasOverloads)
- : FuncDeclaration(funcalias->loc, funcalias->endloc, ident,
- funcalias->storage_class, funcalias->type)
-{
- assert(funcalias != this);
- this->funcalias = funcalias;
-
- this->hasOverloads = hasOverloads;
- if (hasOverloads)
- {
- if (FuncAliasDeclaration *fad = funcalias->isFuncAliasDeclaration())
- this->hasOverloads = fad->hasOverloads;
- }
- else
- { // for internal use
- assert(!funcalias->isFuncAliasDeclaration());
- this->hasOverloads = false;
- }
- userAttribDecl = funcalias->userAttribDecl;
-}
-
-const char *FuncAliasDeclaration::kind() const
-{
- return "function alias";
-}
-
-FuncDeclaration *FuncAliasDeclaration::toAliasFunc()
-{
- return funcalias->toAliasFunc();
-}
-
-
-/****************************** FuncLiteralDeclaration ************************/
-
-FuncLiteralDeclaration::FuncLiteralDeclaration(Loc loc, Loc endloc, Type *type,
- TOK tok, ForeachStatement *fes, Identifier *id)
- : FuncDeclaration(loc, endloc, NULL, STCundefined, type)
-{
- this->ident = id ? id : Id::empty;
- this->tok = tok;
- this->fes = fes;
- this->treq = NULL;
- this->deferToObj = false;
- //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this->ident->toChars(), type->toChars());
-}
-
-Dsymbol *FuncLiteralDeclaration::syntaxCopy(Dsymbol *s)
-{
- //printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars());
- assert(!s);
- FuncLiteralDeclaration *f = new FuncLiteralDeclaration(loc, endloc,
- type->syntaxCopy(), tok, fes, ident);
- f->treq = treq; // don't need to copy
- return FuncDeclaration::syntaxCopy(f);
-}
-
-bool FuncLiteralDeclaration::isNested()
-{
- //printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars());
- return (tok != TOKfunction) && !isThis();
-}
-
-AggregateDeclaration *FuncLiteralDeclaration::isThis()
-{
- //printf("FuncLiteralDeclaration::isThis() '%s'\n", toChars());
- return tok == TOKdelegate ? FuncDeclaration::isThis() : NULL;
-}
-
-bool FuncLiteralDeclaration::isVirtual()
-{
- return false;
-}
-
-bool FuncLiteralDeclaration::addPreInvariant()
-{
- return false;
-}
-
-bool FuncLiteralDeclaration::addPostInvariant()
-{
- return false;
-}
-
-/*******************************
- * Modify all expression type of return statements to tret.
- *
- * On function literals, return type may be modified based on the context type
- * after its semantic3 is done, in FuncExp::implicitCastTo.
- *
- * A function() dg = (){ return new B(); } // OK if is(B : A) == true
- *
- * If B to A conversion is convariant that requires offseet adjusting,
- * all return statements should be adjusted to return expressions typed A.
- */
-void FuncLiteralDeclaration::modifyReturns(Scope *sc, Type *tret)
-{
- class RetWalker : public StatementRewriteWalker
- {
- public:
- Scope *sc;
- Type *tret;
- FuncLiteralDeclaration *fld;
-
- void visit(ReturnStatement *s)
- {
- Expression *exp = s->exp;
- if (exp && !exp->type->equals(tret))
- {
- s->exp = exp->castTo(sc, tret);
- }
- }
- };
-
- if (semanticRun < PASSsemantic3done)
- return;
-
- if (fes)
- return;
-
- RetWalker w;
- w.sc = sc;
- w.tret = tret;
- w.fld = this;
- fbody->accept(&w);
-
- // Also update the inferred function type to match the new return type.
- // This is required so the code generator does not try to cast the
- // modified returns back to the original type.
- if (inferRetType && type->nextOf() != tret)
- type->toTypeFunction()->next = tret;
-}
-
-const char *FuncLiteralDeclaration::kind() const
-{
- return (tok != TOKfunction) ? "delegate" : "function";
-}
-
-const char *FuncLiteralDeclaration::toPrettyChars(bool QualifyTypes)
-{
- if (parent)
- {
- TemplateInstance *ti = parent->isTemplateInstance();
- if (ti)
- return ti->tempdecl->toPrettyChars(QualifyTypes);
- }
- return Dsymbol::toPrettyChars(QualifyTypes);
-}
-
-/********************************* CtorDeclaration ****************************/
-
-CtorDeclaration::CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type)
- : FuncDeclaration(loc, endloc, Id::ctor, stc, type)
-{
- //printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars());
-}
-
-Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- CtorDeclaration *f = new CtorDeclaration(loc, endloc, storage_class, type->syntaxCopy());
- return FuncDeclaration::syntaxCopy(f);
-}
-
-const char *CtorDeclaration::kind() const
-{
- return "constructor";
-}
-
-const char *CtorDeclaration::toChars()
-{
- return "this";
-}
-
-bool CtorDeclaration::isVirtual()
-{
- return false;
-}
-
-bool CtorDeclaration::addPreInvariant()
-{
- return false;
-}
-
-bool CtorDeclaration::addPostInvariant()
-{
- return (isThis() && vthis && global.params.useInvariants == CHECKENABLEon);
-}
-
-
-/********************************* PostBlitDeclaration ****************************/
-
-PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id)
- : FuncDeclaration(loc, endloc, id, stc, NULL)
-{
-}
-
-Dsymbol *PostBlitDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- PostBlitDeclaration *dd = new PostBlitDeclaration(loc, endloc, storage_class, ident);
- return FuncDeclaration::syntaxCopy(dd);
-}
-
-bool PostBlitDeclaration::overloadInsert(Dsymbol *)
-{
- return false; // cannot overload postblits
-}
-
-bool PostBlitDeclaration::addPreInvariant()
-{
- return false;
-}
-
-bool PostBlitDeclaration::addPostInvariant()
-{
- return (isThis() && vthis && global.params.useInvariants == CHECKENABLEon);
-}
-
-bool PostBlitDeclaration::isVirtual()
-{
- return false;
-}
-
-/********************************* DtorDeclaration ****************************/
-
-DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc)
- : FuncDeclaration(loc, endloc, Id::dtor, STCundefined, NULL)
-{
-}
-
-DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id)
- : FuncDeclaration(loc, endloc, id, stc, NULL)
-{
-}
-
-Dsymbol *DtorDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- DtorDeclaration *dd = new DtorDeclaration(loc, endloc, storage_class, ident);
- return FuncDeclaration::syntaxCopy(dd);
-}
-
-bool DtorDeclaration::overloadInsert(Dsymbol *)
-{
- return false; // cannot overload destructors
-}
-
-bool DtorDeclaration::addPreInvariant()
-{
- return (isThis() && vthis && global.params.useInvariants == CHECKENABLEon);
-}
-
-bool DtorDeclaration::addPostInvariant()
-{
- return false;
-}
-
-const char *DtorDeclaration::kind() const
-{
- return "destructor";
-}
-
-const char *DtorDeclaration::toChars()
-{
- return "~this";
-}
-
-bool DtorDeclaration::isVirtual()
-{
- // false so that dtor's don't get put into the vtbl[]
- return false;
-}
-
-/********************************* StaticCtorDeclaration ****************************/
-
-StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc)
- : FuncDeclaration(loc, endloc,
- Identifier::generateId("_staticCtor"), STCstatic | stc, NULL)
-{
-}
-
-StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc)
- : FuncDeclaration(loc, endloc,
- Identifier::generateId(name), STCstatic | stc, NULL)
-{
-}
-
-Dsymbol *StaticCtorDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- StaticCtorDeclaration *scd = new StaticCtorDeclaration(loc, endloc, storage_class);
- return FuncDeclaration::syntaxCopy(scd);
-}
-
-AggregateDeclaration *StaticCtorDeclaration::isThis()
-{
- return NULL;
-}
-
-bool StaticCtorDeclaration::isVirtual()
-{
- return false;
-}
-
-bool StaticCtorDeclaration::hasStaticCtorOrDtor()
-{
- return true;
-}
-
-bool StaticCtorDeclaration::addPreInvariant()
-{
- return false;
-}
-
-bool StaticCtorDeclaration::addPostInvariant()
-{
- return false;
-}
-
-/********************************* SharedStaticCtorDeclaration ****************************/
-
-SharedStaticCtorDeclaration::SharedStaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc)
- : StaticCtorDeclaration(loc, endloc, "_sharedStaticCtor", stc)
-{
-}
-
-Dsymbol *SharedStaticCtorDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- SharedStaticCtorDeclaration *scd = new SharedStaticCtorDeclaration(loc, endloc, storage_class);
- return FuncDeclaration::syntaxCopy(scd);
-}
-
-/********************************* StaticDtorDeclaration ****************************/
-
-StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc, StorageClass stc)
- : FuncDeclaration(loc, endloc,
- Identifier::generateId("_staticDtor"), STCstatic | stc, NULL)
-{
- vgate = NULL;
-}
-
-StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc)
- : FuncDeclaration(loc, endloc,
- Identifier::generateId(name), STCstatic | stc, NULL)
-{
- vgate = NULL;
-}
-
-Dsymbol *StaticDtorDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- StaticDtorDeclaration *sdd = new StaticDtorDeclaration(loc, endloc, storage_class);
- return FuncDeclaration::syntaxCopy(sdd);
-}
-
-AggregateDeclaration *StaticDtorDeclaration::isThis()
-{
- return NULL;
-}
-
-bool StaticDtorDeclaration::isVirtual()
-{
- return false;
-}
-
-bool StaticDtorDeclaration::hasStaticCtorOrDtor()
-{
- return true;
-}
-
-bool StaticDtorDeclaration::addPreInvariant()
-{
- return false;
-}
-
-bool StaticDtorDeclaration::addPostInvariant()
-{
- return false;
-}
-
-/********************************* SharedStaticDtorDeclaration ****************************/
-
-SharedStaticDtorDeclaration::SharedStaticDtorDeclaration(Loc loc, Loc endloc, StorageClass stc)
- : StaticDtorDeclaration(loc, endloc, "_sharedStaticDtor", stc)
-{
-}
-
-Dsymbol *SharedStaticDtorDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- SharedStaticDtorDeclaration *sdd = new SharedStaticDtorDeclaration(loc, endloc, storage_class);
- return FuncDeclaration::syntaxCopy(sdd);
-}
-
-/********************************* InvariantDeclaration ****************************/
-
-InvariantDeclaration::InvariantDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id)
- : FuncDeclaration(loc, endloc,
- id ? id : Identifier::generateId("__invariant"),
- stc, NULL)
-{
-}
-
-Dsymbol *InvariantDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- InvariantDeclaration *id = new InvariantDeclaration(loc, endloc, storage_class);
- return FuncDeclaration::syntaxCopy(id);
-}
-
-bool InvariantDeclaration::isVirtual()
-{
- return false;
-}
-
-bool InvariantDeclaration::addPreInvariant()
-{
- return false;
-}
-
-bool InvariantDeclaration::addPostInvariant()
-{
- return false;
-}
-
-/********************************* UnitTestDeclaration ****************************/
-
-/*******************************
- * Generate unique unittest function Id so we can have multiple
- * instances per module.
- */
-
-static Identifier *unitTestId(Loc loc)
-{
- OutBuffer buf;
- buf.printf("__unittestL%u_", loc.linnum);
- return Identifier::generateId(buf.peekChars());
-}
-
-UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc, StorageClass stc, char *codedoc)
- : FuncDeclaration(loc, endloc, unitTestId(loc), stc, NULL)
-{
- this->codedoc = codedoc;
-}
-
-Dsymbol *UnitTestDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- UnitTestDeclaration *utd = new UnitTestDeclaration(loc, endloc, storage_class, codedoc);
- return FuncDeclaration::syntaxCopy(utd);
-}
-
-AggregateDeclaration *UnitTestDeclaration::isThis()
-{
- return NULL;
-}
-
-bool UnitTestDeclaration::isVirtual()
-{
- return false;
-}
-
-bool UnitTestDeclaration::addPreInvariant()
-{
- return false;
-}
-
-bool UnitTestDeclaration::addPostInvariant()
-{
- return false;
-}
-
-/********************************* NewDeclaration ****************************/
-
-NewDeclaration::NewDeclaration(Loc loc, Loc endloc, StorageClass stc, Parameters *fparams, VarArg varargs)
- : FuncDeclaration(loc, endloc, Id::classNew, STCstatic | stc, NULL)
-{
- this->parameters = fparams;
- this->varargs = varargs;
-}
-
-Dsymbol *NewDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- NewDeclaration *f = new NewDeclaration(loc, endloc,
- storage_class, Parameter::arraySyntaxCopy(parameters), varargs);
- return FuncDeclaration::syntaxCopy(f);
-}
-
-const char *NewDeclaration::kind() const
-{
- return "allocator";
-}
-
-bool NewDeclaration::isVirtual()
-{
- return false;
-}
-
-bool NewDeclaration::addPreInvariant()
-{
- return false;
-}
-
-bool NewDeclaration::addPostInvariant()
-{
- return false;
-}
-
-/********************************* DeleteDeclaration ****************************/
-
-DeleteDeclaration::DeleteDeclaration(Loc loc, Loc endloc, StorageClass stc, Parameters *fparams)
- : FuncDeclaration(loc, endloc, Id::classDelete, STCstatic | stc, NULL)
-{
- this->parameters = fparams;
-}
-
-Dsymbol *DeleteDeclaration::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- DeleteDeclaration *f = new DeleteDeclaration(loc, endloc,
- storage_class, Parameter::arraySyntaxCopy(parameters));
- return FuncDeclaration::syntaxCopy(f);
-}
-
-const char *DeleteDeclaration::kind() const
-{
- return "deallocator";
-}
-
-bool DeleteDeclaration::isDelete()
-{
- return true;
-}
-
-bool DeleteDeclaration::isVirtual()
-{
- return false;
-}
-
-bool DeleteDeclaration::addPreInvariant()
-{
- return false;
-}
-
-bool DeleteDeclaration::addPostInvariant()
-{
- return false;
-}
diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d
new file mode 100644
index 00000000000..7f0b0bb9a28
--- /dev/null
+++ b/gcc/d/dmd/func.d
@@ -0,0 +1,4102 @@
+/**
+ * Defines a function declaration.
+ *
+ * Includes:
+ * - function/delegate literals
+ * - function aliases
+ * - (static/shared) constructors/destructors/post-blits
+ * - `invariant`
+ * - `unittest`
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/func.d, _func.d)
+ * Documentation: https://dlang.org/phobos/dmd_func.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/func.d
+ */
+
+module dmd.func;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.blockexit;
+import dmd.gluelayer;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.delegatize;
+import dmd.dinterpret;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.objc;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.root.stringtable;
+import dmd.semantic2;
+import dmd.semantic3;
+import dmd.statement_rewrite_walker;
+import dmd.statement;
+import dmd.statementsem;
+import dmd.tokens;
+import dmd.visitor;
+
+/// Inline Status
+enum ILS : ubyte
+{
+ uninitialized, /// not computed yet
+ no, /// cannot inline
+ yes, /// can inline
+}
+
+enum BUILTIN : ubyte
+{
+ unknown = 255, /// not known if this is a builtin
+ unimp = 0, /// this is not a builtin
+ gcc, /// this is a GCC builtin
+ llvm, /// this is an LLVM builtin
+ sin,
+ cos,
+ tan,
+ sqrt,
+ fabs,
+ ldexp,
+ log,
+ log2,
+ log10,
+ exp,
+ expm1,
+ exp2,
+ round,
+ floor,
+ ceil,
+ trunc,
+ copysign,
+ pow,
+ fmin,
+ fmax,
+ fma,
+ isnan,
+ isinfinity,
+ isfinite,
+ bsf,
+ bsr,
+ bswap,
+ popcnt,
+ yl2x,
+ yl2xp1,
+ toPrecFloat,
+ toPrecDouble,
+ toPrecReal
+}
+
+/* Tweak all return statements and dtor call for nrvo_var, for correct NRVO.
+ */
+extern (C++) final class NrvoWalker : StatementRewriteWalker
+{
+ alias visit = typeof(super).visit;
+public:
+ FuncDeclaration fd;
+ Scope* sc;
+
+ override void visit(ReturnStatement s)
+ {
+ // See if all returns are instead to be replaced with a goto returnLabel;
+ if (fd.returnLabel)
+ {
+ /* Rewrite:
+ * return exp;
+ * as:
+ * vresult = exp; goto Lresult;
+ */
+ auto gs = new GotoStatement(s.loc, Id.returnLabel);
+ gs.label = fd.returnLabel;
+
+ Statement s1 = gs;
+ if (s.exp)
+ s1 = new CompoundStatement(s.loc, new ExpStatement(s.loc, s.exp), gs);
+
+ replaceCurrent(s1);
+ }
+ }
+
+ override void visit(TryFinallyStatement s)
+ {
+ DtorExpStatement des;
+ if (fd.nrvo_can && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null &&
+ fd.nrvo_var == des.var)
+ {
+ if (!(global.params.useExceptions && ClassDeclaration.throwable))
+ {
+ /* Don't need to call destructor at all, since it is nrvo
+ */
+ replaceCurrent(s._body);
+ s._body.accept(this);
+ return;
+ }
+
+ /* Normally local variable dtors are called regardless exceptions.
+ * But for nrvo_var, its dtor should be called only when exception is thrown.
+ *
+ * Rewrite:
+ * try { s.body; } finally { nrvo_var.edtor; }
+ * // equivalent with:
+ * // s.body; scope(exit) nrvo_var.edtor;
+ * as:
+ * try { s.body; } catch(Throwable __o) { nrvo_var.edtor; throw __o; }
+ * // equivalent with:
+ * // s.body; scope(failure) nrvo_var.edtor;
+ */
+ Statement sexception = new DtorExpStatement(Loc.initial, fd.nrvo_var.edtor, fd.nrvo_var);
+ Identifier id = Identifier.generateId("__o");
+
+ Statement handler = new PeelStatement(sexception);
+ if (sexception.blockExit(fd, false) & BE.fallthru)
+ {
+ auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id));
+ ts.internalThrow = true;
+ handler = new CompoundStatement(Loc.initial, handler, ts);
+ }
+
+ auto catches = new Catches();
+ auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
+ ctch.internalCatch = true;
+ ctch.catchSemantic(sc); // Run semantic to resolve identifier '__o'
+ catches.push(ctch);
+
+ Statement s2 = new TryCatchStatement(Loc.initial, s._body, catches);
+ fd.eh_none = false;
+ replaceCurrent(s2);
+ s2.accept(this);
+ }
+ else
+ StatementRewriteWalker.visit(s);
+ }
+}
+
+enum FUNCFLAG : uint
+{
+ purityInprocess = 1, /// working on determining purity
+ safetyInprocess = 2, /// working on determining safety
+ nothrowInprocess = 4, /// working on determining nothrow
+ nogcInprocess = 8, /// working on determining @nogc
+ returnInprocess = 0x10, /// working on inferring 'return' for parameters
+ inlineScanned = 0x20, /// function has been scanned for inline possibilities
+ inferScope = 0x40, /// infer 'scope' for parameters
+ hasCatches = 0x80, /// function has try-catch statements
+ compileTimeOnly = 0x100, /// is a compile time only function; no code will be generated for it
+ printf = 0x200, /// is a printf-like function
+ scanf = 0x400, /// is a scanf-like function
+ noreturn = 0x800, /// the function does not return
+}
+
+/***********************************************************
+ * Tuple of result identifier (possibly null) and statement.
+ * This is used to store out contracts: out(id){ ensure }
+ */
+extern (C++) struct Ensure
+{
+ Identifier id;
+ Statement ensure;
+
+ Ensure syntaxCopy()
+ {
+ return Ensure(id, ensure.syntaxCopy());
+ }
+
+ /*****************************************
+ * Do syntax copy of an array of Ensure's.
+ */
+ static Ensures* arraySyntaxCopy(Ensures* a)
+ {
+ Ensures* b = null;
+ if (a)
+ {
+ b = a.copy();
+ foreach (i, e; *a)
+ {
+ (*b)[i] = e.syntaxCopy();
+ }
+ }
+ return b;
+ }
+
+}
+
+/***********************************************************
+ */
+extern (C++) class FuncDeclaration : Declaration
+{
+ Statements* frequires; /// in contracts
+ Ensures* fensures; /// out contracts
+ Statement frequire; /// lowered in contract
+ Statement fensure; /// lowered out contract
+ Statement fbody; /// function body
+
+ FuncDeclarations foverrides; /// functions this function overrides
+ FuncDeclaration fdrequire; /// function that does the in contract
+ FuncDeclaration fdensure; /// function that does the out contract
+
+ Expressions* fdrequireParams; /// argument list for __require
+ Expressions* fdensureParams; /// argument list for __ensure
+
+ const(char)* mangleString; /// mangled symbol created from mangleExact()
+
+ VarDeclaration vresult; /// result variable for out contracts
+ LabelDsymbol returnLabel; /// where the return goes
+
+ // used to prevent symbols in different
+ // scopes from having the same name
+ DsymbolTable localsymtab;
+ VarDeclaration vthis; /// 'this' parameter (member and nested)
+ bool isThis2; /// has a dual-context 'this' parameter
+ VarDeclaration v_arguments; /// '_arguments' parameter
+
+ VarDeclaration v_argptr; /// '_argptr' variable
+ VarDeclarations* parameters; /// Array of VarDeclaration's for parameters
+ DsymbolTable labtab; /// statement label symbol table
+ Dsymbol overnext; /// next in overload list
+ FuncDeclaration overnext0; /// next in overload list (only used during IFTI)
+ Loc endloc; /// location of closing curly bracket
+ int vtblIndex = -1; /// for member functions, index into vtbl[]
+ bool naked; /// true if naked
+ bool generated; /// true if function was generated by the compiler rather than
+ /// supplied by the user
+ bool hasAlwaysInlines; /// contains references to functions that must be inlined
+ ubyte isCrtCtorDtor; /// has attribute pragma(crt_constructor(1)/crt_destructor(2))
+ /// not set before the glue layer
+
+ ILS inlineStatusStmt = ILS.uninitialized;
+ ILS inlineStatusExp = ILS.uninitialized;
+ PINLINE inlining = PINLINE.default_;
+
+ int inlineNest; /// !=0 if nested inline
+ bool eh_none; /// true if no exception unwinding is needed
+
+ bool semantic3Errors; /// true if errors in semantic3 this function's frame ptr
+ ForeachStatement fes; /// if foreach body, this is the foreach
+ BaseClass* interfaceVirtual; /// if virtual, but only appears in base interface vtbl[]
+ bool introducing; /// true if 'introducing' function
+ /** if !=NULL, then this is the type
+ of the 'introducing' function
+ this one is overriding
+ */
+ Type tintro;
+
+ bool inferRetType; /// true if return type is to be inferred
+ StorageClass storage_class2; /// storage class for template onemember's
+
+ // Things that should really go into Scope
+
+ /// 1 if there's a return exp; statement
+ /// 2 if there's a throw statement
+ /// 4 if there's an assert(0)
+ /// 8 if there's inline asm
+ /// 16 if there are multiple return statements
+ int hasReturnExp;
+
+ // Support for NRVO (named return value optimization)
+ bool nrvo_can = true; /// true means we can do NRVO
+ VarDeclaration nrvo_var; /// variable to replace with shidden
+ Symbol* shidden; /// hidden pointer passed to function
+
+ ReturnStatements* returns;
+
+ GotoStatements* gotos; /// Gotos with forward references
+
+ /// set if this is a known, builtin function we can evaluate at compile time
+ BUILTIN builtin = BUILTIN.unknown;
+
+ /// set if someone took the address of this function
+ int tookAddressOf;
+
+ bool requiresClosure; // this function needs a closure
+
+ /** local variables in this function which are referenced by nested functions
+ * (They'll get put into the "closure" for this function.)
+ */
+ VarDeclarations closureVars;
+
+ /** Outer variables which are referenced by this nested function
+ * (the inverse of closureVars)
+ */
+ VarDeclarations outerVars;
+
+ /// Sibling nested functions which called this one
+ FuncDeclarations siblingCallers;
+
+ FuncDeclarations *inlinedNestedCallees;
+
+ uint flags; /// FUNCFLAG.xxxxx
+
+ /**
+ * Data for a function declaration that is needed for the Objective-C
+ * integration.
+ */
+ ObjcFuncDeclaration objc;
+
+ extern (D) this(const ref Loc loc, const ref Loc endloc, Identifier ident, StorageClass storage_class, Type type, bool noreturn = false)
+ {
+ super(loc, ident);
+ //printf("FuncDeclaration(id = '%s', type = %p)\n", id.toChars(), type);
+ //printf("storage_class = x%x\n", storage_class);
+ this.storage_class = storage_class;
+ this.type = type;
+ if (type)
+ {
+ // Normalize storage_class, because function-type related attributes
+ // are already set in the 'type' in parsing phase.
+ this.storage_class &= ~(STC.TYPECTOR | STC.FUNCATTR);
+ }
+ this.endloc = endloc;
+ if (noreturn)
+ this.flags |= FUNCFLAG.noreturn;
+
+ /* The type given for "infer the return type" is a TypeFunction with
+ * NULL for the return type.
+ */
+ inferRetType = (type && type.nextOf() is null);
+ }
+
+ static FuncDeclaration create(const ref Loc loc, const ref Loc endloc, Identifier id, StorageClass storage_class, Type type, bool noreturn = false)
+ {
+ return new FuncDeclaration(loc, endloc, id, storage_class, type, noreturn);
+ }
+
+ override FuncDeclaration syntaxCopy(Dsymbol s)
+ {
+ //printf("FuncDeclaration::syntaxCopy('%s')\n", toChars());
+ FuncDeclaration f = s ? cast(FuncDeclaration)s
+ : new FuncDeclaration(loc, endloc, ident, storage_class, type.syntaxCopy(), (flags & FUNCFLAG.noreturn) != 0);
+ f.frequires = frequires ? Statement.arraySyntaxCopy(frequires) : null;
+ f.fensures = fensures ? Ensure.arraySyntaxCopy(fensures) : null;
+ f.fbody = fbody ? fbody.syntaxCopy() : null;
+ return f;
+ }
+
+ /****************************************************
+ * Resolve forward reference of function signature -
+ * parameter types, return type, and attributes.
+ * Returns:
+ * false if any errors exist in the signature.
+ */
+ final bool functionSemantic()
+ {
+ //printf("functionSemantic() %p %s\n", this, toChars());
+ if (!_scope)
+ return !errors;
+
+ this.cppnamespace = _scope.namespace;
+
+ if (!originalType) // semantic not yet run
+ {
+ TemplateInstance spec = isSpeculative();
+ uint olderrs = global.errors;
+ uint oldgag = global.gag;
+ if (global.gag && !spec)
+ global.gag = 0;
+ dsymbolSemantic(this, _scope);
+ global.gag = oldgag;
+ if (spec && global.errors != olderrs)
+ spec.errors = (global.errors - olderrs != 0);
+ if (olderrs != global.errors) // if errors compiling this function
+ return false;
+ }
+
+ // if inferring return type, sematic3 needs to be run
+ // - When the function body contains any errors, we cannot assume
+ // the inferred return type is valid.
+ // So, the body errors should become the function signature error.
+ if (inferRetType && type && !type.nextOf())
+ return functionSemantic3();
+
+ TemplateInstance ti;
+ if (isInstantiated() && !isVirtualMethod() &&
+ ((ti = parent.isTemplateInstance()) is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident))
+ {
+ AggregateDeclaration ad = isMemberLocal();
+ if (ad && ad.sizeok != Sizeok.done)
+ {
+ /* Currently dmd cannot resolve forward references per methods,
+ * then setting SIZOKfwd is too conservative and would break existing code.
+ * So, just stop method attributes inference until ad.dsymbolSemantic() done.
+ */
+ //ad.sizeok = Sizeok.fwd;
+ }
+ else
+ return functionSemantic3() || !errors;
+ }
+
+ if (storage_class & STC.inference)
+ return functionSemantic3() || !errors;
+
+ return !errors;
+ }
+
+ /****************************************************
+ * Resolve forward reference of function body.
+ * Returns false if any errors exist in the body.
+ */
+ final bool functionSemantic3()
+ {
+ if (semanticRun < PASS.semantic3 && _scope)
+ {
+ /* Forward reference - we need to run semantic3 on this function.
+ * If errors are gagged, and it's not part of a template instance,
+ * we need to temporarily ungag errors.
+ */
+ TemplateInstance spec = isSpeculative();
+ uint olderrs = global.errors;
+ uint oldgag = global.gag;
+ if (global.gag && !spec)
+ global.gag = 0;
+ semantic3(this, _scope);
+ global.gag = oldgag;
+
+ // If it is a speculatively-instantiated template, and errors occur,
+ // we need to mark the template as having errors.
+ if (spec && global.errors != olderrs)
+ spec.errors = (global.errors - olderrs != 0);
+ if (olderrs != global.errors) // if errors compiling this function
+ return false;
+ }
+
+ return !errors && !semantic3Errors;
+ }
+
+ /****************************************************
+ * Check that this function type is properly resolved.
+ * If not, report "forward reference error" and return true.
+ */
+ extern (D) final bool checkForwardRef(const ref Loc loc)
+ {
+ if (!functionSemantic())
+ return true;
+
+ /* No deco means the functionSemantic() call could not resolve
+ * forward referenes in the type of this function.
+ */
+ if (!type.deco)
+ {
+ bool inSemantic3 = (inferRetType && semanticRun >= PASS.semantic3);
+ .error(loc, "forward reference to %s`%s`",
+ (inSemantic3 ? "inferred return type of function " : "").ptr,
+ toChars());
+ return true;
+ }
+ return false;
+ }
+
+ // called from semantic3
+ /**
+ * Creates and returns the hidden parameters for this function declaration.
+ *
+ * Hidden parameters include the `this` parameter of a class, struct or
+ * nested function and the selector parameter for Objective-C methods.
+ */
+ extern (D) final void declareThis(Scope* sc)
+ {
+ isThis2 = toParent2() != toParentLocal();
+ auto ad = isThis();
+ if (!isThis2 && !ad && !isNested())
+ {
+ vthis = null;
+ objc.selectorParameter = null;
+ return;
+ }
+
+ Type addModStc(Type t)
+ {
+ return t.addMod(type.mod).addStorageClass(storage_class);
+ }
+
+ if (isThis2 || isNested())
+ {
+ /* The 'this' for a nested function is the link to the
+ * enclosing function's stack frame.
+ * Note that nested functions and member functions are disjoint.
+ */
+ Type tthis = addModStc(isThis2 ?
+ Type.tvoidptr.sarrayOf(2).pointerTo() :
+ Type.tvoid.pointerTo());
+ vthis = new VarDeclaration(loc, tthis, isThis2 ? Id.this2 : Id.capture, null);
+ vthis.storage_class |= STC.parameter | STC.nodtor;
+ }
+ else if (ad)
+ {
+ Type thandle = addModStc(ad.handleType());
+ vthis = new ThisDeclaration(loc, thandle);
+ vthis.storage_class |= STC.parameter;
+ if (thandle.ty == Tstruct)
+ {
+ vthis.storage_class |= STC.ref_;
+ // if member function is marked 'inout', then 'this' is 'return ref'
+ if (type.ty == Tfunction && (cast(TypeFunction)type).isInOutQual())
+ vthis.storage_class |= STC.return_;
+ }
+ }
+
+ if (auto tf = type.isTypeFunction())
+ {
+ if (tf.isreturn)
+ vthis.storage_class |= STC.return_;
+ if (tf.isScopeQual)
+ vthis.storage_class |= STC.scope_;
+
+ /* Add STC.returnScope like typesem.d does for TypeFunction parameters,
+ * at least it should be the same. At the moment, we'll just
+ * do existing practice. But we should examine how TypeFunction does
+ * it, for consistency.
+ */
+ if (!tf.isref && isRefReturnScope(vthis.storage_class))
+ {
+ /* if `ref return scope`, evaluate to `ref` `return scope`
+ */
+ vthis.storage_class |= STC.returnScope;
+ }
+ }
+ if (flags & FUNCFLAG.inferScope && !(vthis.storage_class & STC.scope_))
+ vthis.storage_class |= STC.maybescope;
+
+ vthis.dsymbolSemantic(sc);
+ if (!sc.insert(vthis))
+ assert(0);
+ vthis.parent = this;
+ if (ad)
+ objc.selectorParameter = .objc.createSelectorParameter(this, sc);
+ }
+
+ override final bool equals(const RootObject o) const
+ {
+ if (this == o)
+ return true;
+
+ if (auto s = isDsymbol(o))
+ {
+ auto fd1 = this;
+ auto fd2 = s.isFuncDeclaration();
+ if (!fd2)
+ return false;
+
+ auto fa1 = fd1.isFuncAliasDeclaration();
+ auto faf1 = fa1 ? fa1.toAliasFunc() : fd1;
+
+ auto fa2 = fd2.isFuncAliasDeclaration();
+ auto faf2 = fa2 ? fa2.toAliasFunc() : fd2;
+
+ if (fa1 && fa2)
+ {
+ return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads;
+ }
+
+ bool b1 = fa1 !is null;
+ if (b1 && faf1.isUnique() && !fa1.hasOverloads)
+ b1 = false;
+
+ bool b2 = fa2 !is null;
+ if (b2 && faf2.isUnique() && !fa2.hasOverloads)
+ b2 = false;
+
+ if (b1 != b2)
+ return false;
+
+ return faf1.toParent().equals(faf2.toParent()) &&
+ faf1.ident.equals(faf2.ident) &&
+ faf1.type.equals(faf2.type);
+ }
+ return false;
+ }
+
+ /****************************************************
+ * Determine if 'this' overrides fd.
+ * Return !=0 if it does.
+ */
+ final int overrides(FuncDeclaration fd)
+ {
+ int result = 0;
+ if (fd.ident == ident)
+ {
+ const cov = type.covariant(fd.type);
+ if (cov != Covariant.distinct)
+ {
+ ClassDeclaration cd1 = toParent().isClassDeclaration();
+ ClassDeclaration cd2 = fd.toParent().isClassDeclaration();
+ if (cd1 && cd2 && cd2.isBaseOf(cd1, null))
+ result = 1;
+ }
+ }
+ return result;
+ }
+
+ /*************************************************
+ * Find index of function in vtbl[0..dim] that
+ * this function overrides.
+ * Prefer an exact match to a covariant one.
+ * Params:
+ * vtbl = vtable to use
+ * dim = maximal vtable dimension
+ * Returns:
+ * -1 didn't find one
+ * -2 can't determine because of forward references
+ */
+ final int findVtblIndex(Dsymbols* vtbl, int dim)
+ {
+ //printf("findVtblIndex() %s\n", toChars());
+ FuncDeclaration mismatch = null;
+ StorageClass mismatchstc = 0;
+ int mismatchvi = -1;
+ int exactvi = -1;
+ int bestvi = -1;
+ for (int vi = 0; vi < dim; vi++)
+ {
+ FuncDeclaration fdv = (*vtbl)[vi].isFuncDeclaration();
+ if (fdv && fdv.ident == ident)
+ {
+ if (type.equals(fdv.type)) // if exact match
+ {
+ if (fdv.parent.isClassDeclaration())
+ {
+ if (fdv.isFuture())
+ {
+ bestvi = vi;
+ continue; // keep looking
+ }
+ return vi; // no need to look further
+ }
+
+ if (exactvi >= 0)
+ {
+ error("cannot determine overridden function");
+ return exactvi;
+ }
+ exactvi = vi;
+ bestvi = vi;
+ continue;
+ }
+
+ StorageClass stc = 0;
+ const cov = type.covariant(fdv.type, &stc);
+ //printf("\tbaseclass cov = %d\n", cov);
+ final switch (cov)
+ {
+ case Covariant.distinct:
+ // types are distinct
+ break;
+
+ case Covariant.yes:
+ bestvi = vi; // covariant, but not identical
+ break;
+ // keep looking for an exact match
+
+ case Covariant.no:
+ mismatchvi = vi;
+ mismatchstc = stc;
+ mismatch = fdv; // overrides, but is not covariant
+ break;
+ // keep looking for an exact match
+
+ case Covariant.fwdref:
+ return -2; // forward references
+ }
+ }
+ }
+ if (bestvi == -1 && mismatch)
+ {
+ //type.print();
+ //mismatch.type.print();
+ //printf("%s %s\n", type.deco, mismatch.type.deco);
+ //printf("stc = %llx\n", mismatchstc);
+ if (mismatchstc)
+ {
+ // Fix it by modifying the type to add the storage classes
+ type = type.addStorageClass(mismatchstc);
+ bestvi = mismatchvi;
+ }
+ }
+ return bestvi;
+ }
+
+ /*********************************
+ * If function a function in a base class,
+ * return that base class.
+ * Returns:
+ * base class if overriding, null if not
+ */
+ final BaseClass* overrideInterface()
+ {
+ if (ClassDeclaration cd = toParent2().isClassDeclaration())
+ {
+ foreach (b; cd.interfaces)
+ {
+ auto v = findVtblIndex(&b.sym.vtbl, cast(int)b.sym.vtbl.dim);
+ if (v >= 0)
+ return b;
+ }
+ }
+ return null;
+ }
+
+ /****************************************************
+ * Overload this FuncDeclaration with the new one f.
+ * Return true if successful; i.e. no conflict.
+ */
+ override bool overloadInsert(Dsymbol s)
+ {
+ //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s.toChars(), toChars());
+ assert(s != this);
+ AliasDeclaration ad = s.isAliasDeclaration();
+ if (ad)
+ {
+ if (overnext)
+ return overnext.overloadInsert(ad);
+ if (!ad.aliassym && ad.type.ty != Tident && ad.type.ty != Tinstance && ad.type.ty != Ttypeof)
+ {
+ //printf("\tad = '%s'\n", ad.type.toChars());
+ return false;
+ }
+ overnext = ad;
+ //printf("\ttrue: no conflict\n");
+ return true;
+ }
+ TemplateDeclaration td = s.isTemplateDeclaration();
+ if (td)
+ {
+ if (!td.funcroot)
+ td.funcroot = this;
+ if (overnext)
+ return overnext.overloadInsert(td);
+ overnext = td;
+ return true;
+ }
+ FuncDeclaration fd = s.isFuncDeclaration();
+ if (!fd)
+ return false;
+
+ version (none)
+ {
+ /* Disable this check because:
+ * const void foo();
+ * semantic() isn't run yet on foo(), so the const hasn't been
+ * applied yet.
+ */
+ if (type)
+ {
+ printf("type = %s\n", type.toChars());
+ printf("fd.type = %s\n", fd.type.toChars());
+ }
+ // fd.type can be NULL for overloaded constructors
+ if (type && fd.type && fd.type.covariant(type) && fd.type.mod == type.mod && !isFuncAliasDeclaration())
+ {
+ //printf("\tfalse: conflict %s\n", kind());
+ return false;
+ }
+ }
+
+ if (overnext)
+ {
+ td = overnext.isTemplateDeclaration();
+ if (td)
+ fd.overloadInsert(td);
+ else
+ return overnext.overloadInsert(fd);
+ }
+ overnext = fd;
+ //printf("\ttrue: no conflict\n");
+ return true;
+ }
+
+ /********************************************
+ * Find function in overload list that exactly matches t.
+ */
+ extern (D) final FuncDeclaration overloadExactMatch(Type t)
+ {
+ FuncDeclaration fd;
+ overloadApply(this, (Dsymbol s)
+ {
+ auto f = s.isFuncDeclaration();
+ if (!f)
+ return 0;
+ if (t.equals(f.type))
+ {
+ fd = f;
+ return 1;
+ }
+
+ /* Allow covariant matches, as long as the return type
+ * is just a const conversion.
+ * This allows things like pure functions to match with an impure function type.
+ */
+ if (t.ty == Tfunction)
+ {
+ auto tf = cast(TypeFunction)f.type;
+ if (tf.covariant(t) == Covariant.yes &&
+ tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant)
+ {
+ fd = f;
+ return 1;
+ }
+ }
+ return 0;
+ });
+ return fd;
+ }
+
+ /********************************************
+ * Find function in overload list that matches to the 'this' modifier.
+ * There's four result types.
+ *
+ * 1. If the 'tthis' matches only one candidate, it's an "exact match".
+ * Returns the function and 'hasOverloads' is set to false.
+ * eg. If 'tthis" is mutable and there's only one mutable method.
+ * 2. If there's two or more match candidates, but a candidate function will be
+ * a "better match".
+ * Returns the better match function but 'hasOverloads' is set to true.
+ * eg. If 'tthis' is mutable, and there's both mutable and const methods,
+ * the mutable method will be a better match.
+ * 3. If there's two or more match candidates, but there's no better match,
+ * Returns null and 'hasOverloads' is set to true to represent "ambiguous match".
+ * eg. If 'tthis' is mutable, and there's two or more mutable methods.
+ * 4. If there's no candidates, it's "no match" and returns null with error report.
+ * e.g. If 'tthis' is const but there's no const methods.
+ */
+ extern (D) final FuncDeclaration overloadModMatch(const ref Loc loc, Type tthis, ref bool hasOverloads)
+ {
+ //printf("FuncDeclaration::overloadModMatch('%s')\n", toChars());
+ MatchAccumulator m;
+ overloadApply(this, (Dsymbol s)
+ {
+ auto f = s.isFuncDeclaration();
+ if (!f || f == m.lastf) // skip duplicates
+ return 0;
+
+ auto tf = f.type.toTypeFunction();
+ //printf("tf = %s\n", tf.toChars());
+
+ MATCH match;
+ if (tthis) // non-static functions are preferred than static ones
+ {
+ if (f.needThis())
+ match = f.isCtorDeclaration() ? MATCH.exact : MODmethodConv(tthis.mod, tf.mod);
+ else
+ match = MATCH.constant; // keep static function in overload candidates
+ }
+ else // static functions are preferred than non-static ones
+ {
+ if (f.needThis())
+ match = MATCH.convert;
+ else
+ match = MATCH.exact;
+ }
+ if (match == MATCH.nomatch)
+ return 0;
+
+ if (match > m.last) goto LcurrIsBetter;
+ if (match < m.last) goto LlastIsBetter;
+
+ // See if one of the matches overrides the other.
+ if (m.lastf.overrides(f)) goto LlastIsBetter;
+ if (f.overrides(m.lastf)) goto LcurrIsBetter;
+
+ //printf("\tambiguous\n");
+ m.nextf = f;
+ m.count++;
+ return 0;
+
+ LlastIsBetter:
+ //printf("\tlastbetter\n");
+ m.count++; // count up
+ return 0;
+
+ LcurrIsBetter:
+ //printf("\tisbetter\n");
+ if (m.last <= MATCH.convert)
+ {
+ // clear last secondary matching
+ m.nextf = null;
+ m.count = 0;
+ }
+ m.last = match;
+ m.lastf = f;
+ m.count++; // count up
+ return 0;
+ });
+
+ if (m.count == 1) // exact match
+ {
+ hasOverloads = false;
+ }
+ else if (m.count > 1) // better or ambiguous match
+ {
+ hasOverloads = true;
+ }
+ else // no match
+ {
+ hasOverloads = true;
+ auto tf = this.type.toTypeFunction();
+ assert(tthis);
+ assert(!MODimplicitConv(tthis.mod, tf.mod)); // modifier mismatch
+ {
+ OutBuffer thisBuf, funcBuf;
+ MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod);
+ MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod);
+ .error(loc, "%smethod %s is not callable using a %sobject",
+ funcBuf.peekChars(), this.toPrettyChars(), thisBuf.peekChars());
+ }
+ }
+ return m.lastf;
+ }
+
+ /********************************************
+ * find function template root in overload list
+ */
+ extern (D) final TemplateDeclaration findTemplateDeclRoot()
+ {
+ FuncDeclaration f = this;
+ while (f && f.overnext)
+ {
+ //printf("f.overnext = %p %s\n", f.overnext, f.overnext.toChars());
+ TemplateDeclaration td = f.overnext.isTemplateDeclaration();
+ if (td)
+ return td;
+ f = f.overnext.isFuncDeclaration();
+ }
+ return null;
+ }
+
+ /********************************************
+ * Returns true if function was declared
+ * directly or indirectly in a unittest block
+ */
+ final bool inUnittest()
+ {
+ Dsymbol f = this;
+ do
+ {
+ if (f.isUnitTestDeclaration())
+ return true;
+ f = f.toParent();
+ }
+ while (f);
+ return false;
+ }
+
+ /*************************************
+ * Determine partial specialization order of 'this' vs g.
+ * This is very similar to TemplateDeclaration::leastAsSpecialized().
+ * Returns:
+ * match 'this' is at least as specialized as g
+ * 0 g is more specialized than 'this'
+ */
+ final MATCH leastAsSpecialized(FuncDeclaration g)
+ {
+ enum LOG_LEASTAS = 0;
+ static if (LOG_LEASTAS)
+ {
+ printf("%s.leastAsSpecialized(%s)\n", toChars(), g.toChars());
+ printf("%s, %s\n", type.toChars(), g.type.toChars());
+ }
+
+ /* This works by calling g() with f()'s parameters, and
+ * if that is possible, then f() is at least as specialized
+ * as g() is.
+ */
+
+ TypeFunction tf = type.toTypeFunction();
+ TypeFunction tg = g.type.toTypeFunction();
+
+ /* If both functions have a 'this' pointer, and the mods are not
+ * the same and g's is not const, then this is less specialized.
+ */
+ if (needThis() && g.needThis() && tf.mod != tg.mod)
+ {
+ if (isCtorDeclaration())
+ {
+ if (!MODimplicitConv(tg.mod, tf.mod))
+ return MATCH.nomatch;
+ }
+ else
+ {
+ if (!MODimplicitConv(tf.mod, tg.mod))
+ return MATCH.nomatch;
+ }
+ }
+
+ /* Create a dummy array of arguments out of the parameters to f()
+ */
+ Expressions args;
+ foreach (u, p; tf.parameterList)
+ {
+ Expression e;
+ if (p.isReference())
+ {
+ e = new IdentifierExp(Loc.initial, p.ident);
+ e.type = p.type;
+ }
+ else
+ e = p.type.defaultInitLiteral(Loc.initial);
+ args.push(e);
+ }
+
+ MATCH m = tg.callMatch(null, args[], 1);
+ if (m > MATCH.nomatch)
+ {
+ /* A variadic parameter list is less specialized than a
+ * non-variadic one.
+ */
+ if (tf.parameterList.varargs && !tg.parameterList.varargs)
+ goto L1; // less specialized
+
+ static if (LOG_LEASTAS)
+ {
+ printf(" matches %d, so is least as specialized\n", m);
+ }
+ return m;
+ }
+ L1:
+ static if (LOG_LEASTAS)
+ {
+ printf(" doesn't match, so is not as specialized\n");
+ }
+ return MATCH.nomatch;
+ }
+
+ /********************************
+ * Searches for a label with the given identifier. This function will insert a new
+ * `LabelDsymbol` into `labtab` if it does not contain a mapping for `ident`.
+ *
+ * Params:
+ * ident = identifier of the requested label
+ * loc = location used when creating a new `LabelDsymbol`
+ *
+ * Returns: the `LabelDsymbol` for `ident`
+ */
+ final LabelDsymbol searchLabel(Identifier ident, const ref Loc loc = Loc.initial)
+ {
+ Dsymbol s;
+ if (!labtab)
+ labtab = new DsymbolTable(); // guess we need one
+
+ s = labtab.lookup(ident);
+ if (!s)
+ {
+ s = new LabelDsymbol(ident, loc);
+ labtab.insert(s);
+ }
+ return cast(LabelDsymbol)s;
+ }
+
+ /*****************************************
+ * Determine lexical level difference from `this` to nested function `fd`.
+ * Params:
+ * fd = target of call
+ * intypeof = !=0 if inside typeof
+ * Returns:
+ * 0 same level
+ * >0 decrease nesting by number
+ * -1 increase nesting by 1 (`fd` is nested within `this`)
+ * LevelError error, `this` cannot call `fd`
+ */
+ final int getLevel(FuncDeclaration fd, int intypeof)
+ {
+ //printf("FuncDeclaration::getLevel(fd = '%s')\n", fd.toChars());
+ Dsymbol fdparent = fd.toParent2();
+ if (fdparent == this)
+ return -1;
+
+ Dsymbol s = this;
+ int level = 0;
+ while (fd != s && fdparent != s.toParent2())
+ {
+ //printf("\ts = %s, '%s'\n", s.kind(), s.toChars());
+ if (auto thisfd = s.isFuncDeclaration())
+ {
+ if (!thisfd.isNested() && !thisfd.vthis && !intypeof)
+ return LevelError;
+ }
+ else
+ {
+ if (auto thiscd = s.isAggregateDeclaration())
+ {
+ /* AggregateDeclaration::isNested returns true only when
+ * it has a hidden pointer.
+ * But, calling the function belongs unrelated lexical scope
+ * is still allowed inside typeof.
+ *
+ * struct Map(alias fun) {
+ * typeof({ return fun(); }) RetType;
+ * // No member function makes Map struct 'not nested'.
+ * }
+ */
+ if (!thiscd.isNested() && !intypeof)
+ return LevelError;
+ }
+ else
+ return LevelError;
+ }
+
+ s = s.toParentP(fd);
+ assert(s);
+ level++;
+ }
+ return level;
+ }
+
+ /***********************************
+ * Determine lexical level difference from `this` to nested function `fd`.
+ * Issue error if `this` cannot call `fd`.
+ *
+ * Params:
+ * loc = location for error messages
+ * sc = context
+ * fd = target of call
+ * decl = The `Declaration` that triggered this check.
+ * Used to provide a better error message only.
+ * Returns:
+ * 0 same level
+ * >0 decrease nesting by number
+ * -1 increase nesting by 1 (`fd` is nested within 'this')
+ * LevelError error
+ */
+ final int getLevelAndCheck(const ref Loc loc, Scope* sc, FuncDeclaration fd,
+ Declaration decl)
+ {
+ int level = getLevel(fd, sc.intypeof);
+ if (level != LevelError)
+ return level;
+
+ // Don't give error if in template constraint
+ if (!(sc.flags & SCOPE.constraint))
+ {
+ const(char)* xstatic = isStatic() ? "`static` " : "";
+ // better diagnostics for static functions
+ .error(loc, "%s%s `%s` cannot access %s `%s` in frame of function `%s`",
+ xstatic, kind(), toPrettyChars(), decl.kind(), decl.toChars(),
+ fd.toPrettyChars());
+ .errorSupplemental(decl.loc, "`%s` declared here", decl.toChars());
+ return LevelError;
+ }
+ return 1;
+ }
+
+ enum LevelError = -2;
+
+ override const(char)* toPrettyChars(bool QualifyTypes = false)
+ {
+ if (isMain())
+ return "D main";
+ else
+ return Dsymbol.toPrettyChars(QualifyTypes);
+ }
+
+ /** for diagnostics, e.g. 'int foo(int x, int y) pure' */
+ final const(char)* toFullSignature()
+ {
+ OutBuffer buf;
+ functionToBufferWithIdent(type.toTypeFunction(), &buf, toChars(), isStatic);
+ return buf.extractChars();
+ }
+
+ final bool isMain() const
+ {
+ return ident == Id.main && linkage != LINK.c && !isMember() && !isNested();
+ }
+
+ final bool isCMain() const
+ {
+ return ident == Id.main && linkage == LINK.c && !isMember() && !isNested();
+ }
+
+ final bool isWinMain() const
+ {
+ //printf("FuncDeclaration::isWinMain() %s\n", toChars());
+ version (none)
+ {
+ bool x = ident == Id.WinMain && linkage != LINK.c && !isMember();
+ printf("%s\n", x ? "yes" : "no");
+ return x;
+ }
+ else
+ {
+ return ident == Id.WinMain && linkage != LINK.c && !isMember();
+ }
+ }
+
+ final bool isDllMain() const
+ {
+ return ident == Id.DllMain && linkage != LINK.c && !isMember();
+ }
+
+ final bool isRtInit() const
+ {
+ return ident == Id.rt_init && linkage == LINK.c && !isMember() && !isNested();
+ }
+
+ override final bool isExport() const
+ {
+ return visibility.kind == Visibility.Kind.export_;
+ }
+
+ override final bool isImportedSymbol() const
+ {
+ //printf("isImportedSymbol()\n");
+ //printf("protection = %d\n", visibility);
+ return (visibility.kind == Visibility.Kind.export_) && !fbody;
+ }
+
+ override final bool isCodeseg() const pure nothrow @nogc @safe
+ {
+ return true; // functions are always in the code segment
+ }
+
+ override final bool isOverloadable() const
+ {
+ return true; // functions can be overloaded
+ }
+
+ /***********************************
+ * Override so it can work even if semantic() hasn't yet
+ * been run.
+ */
+ override final bool isAbstract()
+ {
+ if (storage_class & STC.abstract_)
+ return true;
+ if (semanticRun >= PASS.semanticdone)
+ return false;
+
+ if (_scope)
+ {
+ if (_scope.stc & STC.abstract_)
+ return true;
+ parent = _scope.parent;
+ Dsymbol parent = toParent();
+ if (parent.isInterfaceDeclaration())
+ return true;
+ }
+ return false;
+ }
+
+ /**********************************
+ * Decide if attributes for this function can be inferred from examining
+ * the function body.
+ * Returns:
+ * true if can
+ */
+ final bool canInferAttributes(Scope* sc)
+ {
+ if (!fbody)
+ return false;
+
+ if (isVirtualMethod())
+ return false; // since they may be overridden
+
+ if (sc.func &&
+ /********** this is for backwards compatibility for the moment ********/
+ (!isMember() || sc.func.isSafeBypassingInference() && !isInstantiated()))
+ return true;
+
+ if (isFuncLiteralDeclaration() || // externs are not possible with literals
+ (storage_class & STC.inference) || // do attribute inference
+ (inferRetType && !isCtorDeclaration()))
+ return true;
+
+ if (isInstantiated())
+ {
+ auto ti = parent.isTemplateInstance();
+ if (ti is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident)
+ return true;
+ }
+
+ return false;
+ }
+
+ /*****************************************
+ * Initialize for inferring the attributes of this function.
+ */
+ final void initInferAttributes()
+ {
+ //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars());
+ TypeFunction tf = type.toTypeFunction();
+ if (tf.purity == PURE.impure) // purity not specified
+ flags |= FUNCFLAG.purityInprocess;
+
+ if (tf.trust == TRUST.default_)
+ flags |= FUNCFLAG.safetyInprocess;
+
+ if (!tf.isnothrow)
+ flags |= FUNCFLAG.nothrowInprocess;
+
+ if (!tf.isnogc)
+ flags |= FUNCFLAG.nogcInprocess;
+
+ if (!isVirtual() || introducing)
+ flags |= FUNCFLAG.returnInprocess;
+
+ // Initialize for inferring STC.scope_
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ flags |= FUNCFLAG.inferScope;
+ }
+
+ final PURE isPure()
+ {
+ //printf("FuncDeclaration::isPure() '%s'\n", toChars());
+ TypeFunction tf = type.toTypeFunction();
+ if (flags & FUNCFLAG.purityInprocess)
+ setImpure();
+ if (tf.purity == PURE.fwdref)
+ tf.purityLevel();
+ PURE purity = tf.purity;
+ if (purity > PURE.weak && isNested())
+ purity = PURE.weak;
+ if (purity > PURE.weak && needThis())
+ {
+ // The attribute of the 'this' reference affects purity strength
+ if (type.mod & MODFlags.immutable_)
+ {
+ }
+ else if (type.mod & (MODFlags.const_ | MODFlags.wild) && purity >= PURE.const_)
+ purity = PURE.const_;
+ else
+ purity = PURE.weak;
+ }
+ tf.purity = purity;
+ // ^ This rely on the current situation that every FuncDeclaration has a
+ // unique TypeFunction.
+ return purity;
+ }
+
+ final PURE isPureBypassingInference()
+ {
+ if (flags & FUNCFLAG.purityInprocess)
+ return PURE.fwdref;
+ else
+ return isPure();
+ }
+
+ /**************************************
+ * The function is doing something impure,
+ * so mark it as impure.
+ * If there's a purity error, return true.
+ */
+ extern (D) final bool setImpure()
+ {
+ if (flags & FUNCFLAG.purityInprocess)
+ {
+ flags &= ~FUNCFLAG.purityInprocess;
+ if (fes)
+ fes.func.setImpure();
+ }
+ else if (isPure())
+ return true;
+ return false;
+ }
+
+ final bool isSafe()
+ {
+ if (flags & FUNCFLAG.safetyInprocess)
+ setUnsafe();
+ return type.toTypeFunction().trust == TRUST.safe;
+ }
+
+ final bool isSafeBypassingInference()
+ {
+ return !(flags & FUNCFLAG.safetyInprocess) && isSafe();
+ }
+
+ final bool isTrusted()
+ {
+ if (flags & FUNCFLAG.safetyInprocess)
+ setUnsafe();
+ return type.toTypeFunction().trust == TRUST.trusted;
+ }
+
+ /**************************************
+ * The function is doing something unsafe,
+ * so mark it as unsafe.
+ * If there's a safe error, return true.
+ */
+ extern (D) final bool setUnsafe()
+ {
+ if (flags & FUNCFLAG.safetyInprocess)
+ {
+ flags &= ~FUNCFLAG.safetyInprocess;
+ type.toTypeFunction().trust = TRUST.system;
+ if (fes)
+ fes.func.setUnsafe();
+ }
+ else if (isSafe())
+ return true;
+ return false;
+ }
+
+ final bool isNogc()
+ {
+ //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess));
+ if (flags & FUNCFLAG.nogcInprocess)
+ setGC();
+ return type.toTypeFunction().isnogc;
+ }
+
+ final bool isNogcBypassingInference()
+ {
+ return !(flags & FUNCFLAG.nogcInprocess) && isNogc();
+ }
+
+ /**************************************
+ * The function is doing something that may allocate with the GC,
+ * so mark it as not nogc (not no-how).
+ * Returns:
+ * true if function is marked as @nogc, meaning a user error occurred
+ */
+ extern (D) final bool setGC()
+ {
+ //printf("setGC() %s\n", toChars());
+ if (flags & FUNCFLAG.nogcInprocess && semanticRun < PASS.semantic3 && _scope)
+ {
+ this.semantic2(_scope);
+ this.semantic3(_scope);
+ }
+
+ if (flags & FUNCFLAG.nogcInprocess)
+ {
+ flags &= ~FUNCFLAG.nogcInprocess;
+ type.toTypeFunction().isnogc = false;
+ if (fes)
+ fes.func.setGC();
+ }
+ else if (isNogc())
+ return true;
+ return false;
+ }
+
+ extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn)
+ {
+ if (!global.params.vgc)
+ return;
+
+ Module m = getModule();
+ if (m && m.isRoot() && !inUnittest())
+ {
+ message(loc, "vgc: %s", warn);
+ }
+ }
+
+ /********************************************
+ * See if pointers from function parameters, mutable globals, or uplevel functions
+ * could leak into return value.
+ * Returns:
+ * true if the function return value is isolated from
+ * any inputs to the function
+ */
+ extern (D) final bool isReturnIsolated()
+ {
+ //printf("isReturnIsolated(this: %s)\n", this.toChars);
+ TypeFunction tf = type.toTypeFunction();
+ assert(tf.next);
+
+ Type treti = tf.next;
+ if (tf.isref)
+ return isTypeIsolatedIndirect(treti); // check influence from parameters
+
+ return isTypeIsolated(treti);
+ }
+
+ /********************
+ * See if pointers from function parameters, mutable globals, or uplevel functions
+ * could leak into type `t`.
+ * Params:
+ * t = type to check if it is isolated
+ * Returns:
+ * true if `t` is isolated from
+ * any inputs to the function
+ */
+ extern (D) final bool isTypeIsolated(Type t)
+ {
+ StringTable!Type parentTypes;
+ parentTypes._init();
+ return isTypeIsolated(t, parentTypes);
+ }
+
+ ///ditto
+ extern (D) final bool isTypeIsolated(Type t, ref StringTable!Type parentTypes)
+ {
+ //printf("this: %s, isTypeIsolated(t: %s)\n", this.toChars(), t.toChars());
+
+ t = t.baseElemOf();
+ switch (t.ty)
+ {
+ case Tarray:
+ case Tpointer:
+ return isTypeIsolatedIndirect(t.nextOf()); // go down one level
+
+ case Taarray:
+ case Tclass:
+ return isTypeIsolatedIndirect(t);
+
+ case Tstruct:
+ /* Drill down and check the struct's fields
+ */
+ auto sym = t.toDsymbol(null).isStructDeclaration();
+ const tName = t.toChars.toDString;
+ const entry = parentTypes.insert(tName, t);
+ if (entry == null)
+ {
+ //we've already seen this type in a parent, not isolated
+ return false;
+ }
+ foreach (v; sym.fields)
+ {
+ Type tmi = v.type.addMod(t.mod);
+ //printf("\tt = %s, v: %s, vtype: %s, tmi = %s\n",
+ // t.toChars(), v.toChars(), v.type.toChars(), tmi.toChars());
+ if (!isTypeIsolated(tmi, parentTypes))
+ return false;
+ }
+ return true;
+
+ default:
+ return true;
+ }
+ }
+
+ /********************************************
+ * Params:
+ * t = type of object to test one level of indirection down
+ * Returns:
+ * true if an object typed `t` has no indirections
+ * which could have come from the function's parameters, mutable
+ * globals, or uplevel functions.
+ */
+ private bool isTypeIsolatedIndirect(Type t)
+ {
+ //printf("isTypeIsolatedIndirect(t: %s)\n", t.toChars());
+ assert(t);
+
+ /* Since `t` is one level down from an indirection, it could pick
+ * up a reference to a mutable global or an outer function, so
+ * return false.
+ */
+ if (!isPureBypassingInference() || isNested())
+ return false;
+
+ TypeFunction tf = type.toTypeFunction();
+
+ //printf("isTypeIsolatedIndirect(%s) t = %s\n", tf.toChars(), t.toChars());
+
+ foreach (i, fparam; tf.parameterList)
+ {
+ Type tp = fparam.type;
+ if (!tp)
+ continue;
+
+ if (fparam.storageClass & (STC.lazy_ | STC.out_ | STC.ref_))
+ {
+ if (!traverseIndirections(tp, t))
+ return false;
+ continue;
+ }
+
+ /* Goes down one level of indirection, then calls traverseIndirection() on
+ * the result.
+ * Returns:
+ * true if t is isolated from tp
+ */
+ static bool traverse(Type tp, Type t)
+ {
+ tp = tp.baseElemOf();
+ switch (tp.ty)
+ {
+ case Tarray:
+ case Tpointer:
+ return traverseIndirections(tp.nextOf(), t);
+
+ case Taarray:
+ case Tclass:
+ return traverseIndirections(tp, t);
+
+ case Tstruct:
+ /* Drill down and check the struct's fields
+ */
+ auto sym = tp.toDsymbol(null).isStructDeclaration();
+ foreach (v; sym.fields)
+ {
+ Type tprmi = v.type.addMod(tp.mod);
+ //printf("\ttp = %s, tprmi = %s\n", tp.toChars(), tprmi.toChars());
+ if (!traverse(tprmi, t))
+ return false;
+ }
+ return true;
+
+ default:
+ return true;
+ }
+ }
+
+ if (!traverse(tp, t))
+ return false;
+ }
+ // The 'this' reference is a parameter, too
+ if (AggregateDeclaration ad = isCtorDeclaration() ? null : isThis())
+ {
+ Type tthis = ad.getType().addMod(tf.mod);
+ //printf("\ttthis = %s\n", tthis.toChars());
+ if (!traverseIndirections(tthis, t))
+ return false;
+ }
+
+ return true;
+ }
+
+ /****************************************
+ * Determine if function needs a static frame pointer.
+ * Returns:
+ * `true` if function is really nested within other function.
+ * Contracts:
+ * If isNested() returns true, isThis() should return false,
+ * unless the function needs a dual-context pointer.
+ */
+ bool isNested() const
+ {
+ auto f = toAliasFunc();
+ //printf("\ttoParent2() = '%s'\n", f.toParent2().toChars());
+ return ((f.storage_class & STC.static_) == 0) &&
+ (f.linkage == LINK.d) &&
+ (f.toParent2().isFuncDeclaration() !is null ||
+ f.toParent2() !is f.toParentLocal());
+ }
+
+ /****************************************
+ * Determine if function is a non-static member function
+ * that has an implicit 'this' expression.
+ * Returns:
+ * The aggregate it is a member of, or null.
+ * Contracts:
+ * Both isThis() and isNested() should return true if function needs a dual-context pointer,
+ * otherwise if isThis() returns true, isNested() should return false.
+ */
+ override inout(AggregateDeclaration) isThis() inout
+ {
+ //printf("+FuncDeclaration::isThis() '%s'\n", toChars());
+ auto ad = (storage_class & STC.static_) ? .objc.isThis(this) : isMemberLocal();
+ //printf("-FuncDeclaration::isThis() %p\n", ad);
+ return ad;
+ }
+
+ override final bool needThis()
+ {
+ //printf("FuncDeclaration::needThis() '%s'\n", toChars());
+ return toAliasFunc().isThis() !is null;
+ }
+
+ // Determine if a function is pedantically virtual
+ final bool isVirtualMethod()
+ {
+ if (toAliasFunc() != this)
+ return toAliasFunc().isVirtualMethod();
+
+ //printf("FuncDeclaration::isVirtualMethod() %s\n", toChars());
+ if (!isVirtual())
+ return false;
+ // If it's a final method, and does not override anything, then it is not virtual
+ if (isFinalFunc() && foverrides.dim == 0)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ // Determine if function goes into virtual function pointer table
+ bool isVirtual() const
+ {
+ if (toAliasFunc() != this)
+ return toAliasFunc().isVirtual();
+
+ auto p = toParent();
+
+ if (!isMember || !p.isClassDeclaration)
+ return false;
+
+ if (p.isClassDeclaration.classKind == ClassKind.objc)
+ return .objc.isVirtual(this);
+
+ version (none)
+ {
+ printf("FuncDeclaration::isVirtual(%s)\n", toChars());
+ printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), visibility == Visibility.Kind.private_, isCtorDeclaration(), linkage != LINK.d);
+ printf("result is %d\n", isMember() && !(isStatic() || visibility == Visibility.Kind.private_ || visibility == Visibility.Kind.package_) && p.isClassDeclaration() && !(p.isInterfaceDeclaration() && isFinalFunc()));
+ }
+ return !(isStatic() || visibility.kind == Visibility.Kind.private_ || visibility.kind == Visibility.Kind.package_) && !(p.isInterfaceDeclaration() && isFinalFunc());
+ }
+
+ final bool isFinalFunc() const
+ {
+ if (toAliasFunc() != this)
+ return toAliasFunc().isFinalFunc();
+
+ version (none)
+ {{
+ auto cd = toParent().isClassDeclaration();
+ printf("FuncDeclaration::isFinalFunc(%s), %x\n", toChars(), Declaration.isFinal());
+ printf("%p %d %d %d\n", isMember(), isStatic(), Declaration.isFinal(), ((cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STC.final_));
+ printf("result is %d\n", isMember() && (Declaration.isFinal() || (cd !is null && cd.storage_class & STC.final_)));
+ if (cd)
+ printf("\tmember of %s\n", cd.toChars());
+ }}
+ if (!isMember())
+ return false;
+ if (Declaration.isFinal())
+ return true;
+ auto cd = toParent().isClassDeclaration();
+ return (cd !is null) && (cd.storage_class & STC.final_);
+ }
+
+ bool addPreInvariant()
+ {
+ auto ad = isThis();
+ ClassDeclaration cd = ad ? ad.isClassDeclaration() : null;
+ return (ad && !(cd && cd.isCPPclass()) && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !naked);
+ }
+
+ bool addPostInvariant()
+ {
+ auto ad = isThis();
+ ClassDeclaration cd = ad ? ad.isClassDeclaration() : null;
+ return (ad && !(cd && cd.isCPPclass()) && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !naked);
+ }
+
+ override const(char)* kind() const
+ {
+ return generated ? "generated function" : "function";
+ }
+
+ /********************************************
+ * Returns:
+ * true if there are no overloads of this function
+ */
+ final bool isUnique() const
+ {
+ bool result = false;
+ overloadApply(cast() this, (Dsymbol s)
+ {
+ auto f = s.isFuncDeclaration();
+ if (!f)
+ return 0;
+ if (result)
+ {
+ result = false;
+ return 1; // ambiguous, done
+ }
+ else
+ {
+ result = true;
+ return 0;
+ }
+ });
+ return result;
+ }
+
+ /*********************************************
+ * In the current function, we are calling 'this' function.
+ * 1. Check to see if the current function can call 'this' function, issue error if not.
+ * 2. If the current function is not the parent of 'this' function, then add
+ * the current function to the list of siblings of 'this' function.
+ * 3. If the current function is a literal, and it's accessing an uplevel scope,
+ * then mark it as a delegate.
+ * Returns true if error occurs.
+ */
+ extern (D) final bool checkNestedReference(Scope* sc, const ref Loc loc)
+ {
+ //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars());
+
+ if (auto fld = this.isFuncLiteralDeclaration())
+ {
+ if (fld.tok == TOK.reserved)
+ {
+ fld.tok = TOK.function_;
+ fld.vthis = null;
+ }
+ }
+
+ if (!parent || parent == sc.parent)
+ return false;
+ if (ident == Id.require || ident == Id.ensure)
+ return false;
+ if (!isThis() && !isNested())
+ return false;
+
+ // The current function
+ FuncDeclaration fdthis = sc.parent.isFuncDeclaration();
+ if (!fdthis)
+ return false; // out of function scope
+
+ Dsymbol p = toParentLocal();
+ Dsymbol p2 = toParent2();
+
+ // Function literals from fdthis to p must be delegates
+ ensureStaticLinkTo(fdthis, p);
+ if (p != p2)
+ ensureStaticLinkTo(fdthis, p2);
+
+ if (isNested())
+ {
+ // The function that this function is in
+ bool checkEnclosing(FuncDeclaration fdv)
+ {
+ if (!fdv)
+ return false;
+ if (fdv == fdthis)
+ return false;
+
+ //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars());
+ //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars());
+ //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars());
+
+ // Add this function to the list of those which called us
+ if (fdthis != this)
+ {
+ bool found = false;
+ for (size_t i = 0; i < siblingCallers.dim; ++i)
+ {
+ if (siblingCallers[i] == fdthis)
+ found = true;
+ }
+ if (!found)
+ {
+ //printf("\tadding sibling %s\n", fdthis.toPrettyChars());
+ if (!sc.intypeof && !(sc.flags & SCOPE.compile))
+ siblingCallers.push(fdthis);
+ }
+ }
+
+ const lv = fdthis.getLevelAndCheck(loc, sc, fdv, this);
+ if (lv == LevelError)
+ return true; // error
+ if (lv == -1)
+ return false; // downlevel call
+ if (lv == 0)
+ return false; // same level call
+
+ return false; // Uplevel call
+ }
+
+ if (checkEnclosing(p.isFuncDeclaration()))
+ return true;
+ if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration()))
+ return true;
+ }
+ return false;
+ }
+
+ /*******************************
+ * Look at all the variables in this function that are referenced
+ * by nested functions, and determine if a closure needs to be
+ * created for them.
+ */
+ final bool needsClosure()
+ {
+ /* Need a closure for all the closureVars[] if any of the
+ * closureVars[] are accessed by a
+ * function that escapes the scope of this function.
+ * We take the conservative approach and decide that a function needs
+ * a closure if it:
+ * 1) is a virtual function
+ * 2) has its address taken
+ * 3) has a parent that escapes
+ * 4) calls another nested function that needs a closure
+ *
+ * Note that since a non-virtual function can be called by
+ * a virtual one, if that non-virtual function accesses a closure
+ * var, the closure still has to be taken. Hence, we check for isThis()
+ * instead of isVirtual(). (thanks to David Friedman)
+ *
+ * When the function returns a local struct or class, `requiresClosure`
+ * is already set to `true` upon entering this function when the
+ * struct/class refers to a local variable and a closure is needed.
+ */
+
+ //printf("FuncDeclaration::needsClosure() %s\n", toChars());
+
+ if (requiresClosure)
+ goto Lyes;
+
+ for (size_t i = 0; i < closureVars.dim; i++)
+ {
+ VarDeclaration v = closureVars[i];
+ //printf("\tv = %s\n", v.toChars());
+
+ for (size_t j = 0; j < v.nestedrefs.dim; j++)
+ {
+ FuncDeclaration f = v.nestedrefs[j];
+ assert(f != this);
+
+ /* __require and __ensure will always get called directly,
+ * so they never make outer functions closure.
+ */
+ if (f.ident == Id.require || f.ident == Id.ensure)
+ continue;
+
+ //printf("\t\tf = %p, %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f, f.toChars(), f.isVirtual(), f.isThis(), f.tookAddressOf);
+
+ /* Look to see if f escapes. We consider all parents of f within
+ * this, and also all siblings which call f; if any of them escape,
+ * so does f.
+ * Mark all affected functions as requiring closures.
+ */
+ for (Dsymbol s = f; s && s != this; s = s.toParentP(this))
+ {
+ FuncDeclaration fx = s.isFuncDeclaration();
+ if (!fx)
+ continue;
+ if (fx.isThis() || fx.tookAddressOf)
+ {
+ //printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx.toChars(), fx.isVirtual(), fx.isThis(), fx.tookAddressOf);
+
+ /* Mark as needing closure any functions between this and f
+ */
+ markAsNeedingClosure((fx == f) ? fx.toParentP(this) : fx, this);
+
+ requiresClosure = true;
+ }
+
+ /* We also need to check if any sibling functions that
+ * called us, have escaped. This is recursive: we need
+ * to check the callers of our siblings.
+ */
+ if (checkEscapingSiblings(fx, this))
+ requiresClosure = true;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=12406
+ * Iterate all closureVars to mark all descendant
+ * nested functions that access to the closing context of this function.
+ */
+ }
+ }
+ }
+ if (requiresClosure)
+ goto Lyes;
+
+ return false;
+
+ Lyes:
+ //printf("\tneeds closure\n");
+ return true;
+ }
+
+ /***********************************************
+ * Check that the function contains any closure.
+ * If it's @nogc, report suitable errors.
+ * This is mostly consistent with FuncDeclaration::needsClosure().
+ *
+ * Returns:
+ * true if any errors occur.
+ */
+ extern (D) final bool checkClosure()
+ {
+ if (!needsClosure())
+ return false;
+
+ if (setGC())
+ {
+ error("is `@nogc` yet allocates closures with the GC");
+ if (global.gag) // need not report supplemental errors
+ return true;
+ }
+ else
+ {
+ printGCUsage(loc, "using closure causes GC allocation");
+ return false;
+ }
+
+ FuncDeclarations a;
+ foreach (v; closureVars)
+ {
+ foreach (f; v.nestedrefs)
+ {
+ assert(f !is this);
+
+ LcheckAncestorsOfANestedRef:
+ for (Dsymbol s = f; s && s !is this; s = s.toParentP(this))
+ {
+ auto fx = s.isFuncDeclaration();
+ if (!fx)
+ continue;
+ if (fx.isThis() ||
+ fx.tookAddressOf ||
+ checkEscapingSiblings(fx, this))
+ {
+ foreach (f2; a)
+ {
+ if (f2 == f)
+ break LcheckAncestorsOfANestedRef;
+ }
+ a.push(f);
+ .errorSupplemental(f.loc, "%s closes over variable %s at %s",
+ f.toPrettyChars(), v.toChars(), v.loc.toChars());
+ break LcheckAncestorsOfANestedRef;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /***********************************************
+ * Determine if function's variables are referenced by a function
+ * nested within it.
+ */
+ final bool hasNestedFrameRefs()
+ {
+ if (closureVars.dim)
+ return true;
+
+ /* If a virtual function has contracts, assume its variables are referenced
+ * by those contracts, even if they aren't. Because they might be referenced
+ * by the overridden or overriding function's contracts.
+ * This can happen because frequire and fensure are implemented as nested functions,
+ * and they can be called directly by an overriding function and the overriding function's
+ * context had better match, or
+ * https://issues.dlang.org/show_bug.cgi?id=7335 will bite.
+ */
+ if (fdrequire || fdensure)
+ return true;
+
+ if (foverrides.dim && isVirtualMethod())
+ {
+ for (size_t i = 0; i < foverrides.dim; i++)
+ {
+ FuncDeclaration fdv = foverrides[i];
+ if (fdv.hasNestedFrameRefs())
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /****************************************************
+ * Check whether result variable can be built.
+ * Returns:
+ * `true` if the function has a return type that
+ * is different from `void`.
+ */
+ extern (D) private bool canBuildResultVar()
+ {
+ auto f = cast(TypeFunction)type;
+ return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid;
+ }
+
+ /****************************************************
+ * Declare result variable lazily.
+ */
+ extern (D) final void buildResultVar(Scope* sc, Type tret)
+ {
+ if (!vresult)
+ {
+ Loc loc = fensure ? fensure.loc : this.loc;
+
+ /* If inferRetType is true, tret may not be a correct return type yet.
+ * So, in here it may be a temporary type for vresult, and after
+ * fbody.dsymbolSemantic() running, vresult.type might be modified.
+ */
+ vresult = new VarDeclaration(loc, tret, Id.result, null);
+ vresult.storage_class |= STC.nodtor | STC.temp;
+ if (!isVirtual())
+ vresult.storage_class |= STC.const_;
+ vresult.storage_class |= STC.result;
+
+ // set before the semantic() for checkNestedReference()
+ vresult.parent = this;
+ }
+
+ if (sc && vresult.semanticRun == PASS.init)
+ {
+ TypeFunction tf = type.toTypeFunction();
+ if (tf.isref)
+ vresult.storage_class |= STC.ref_;
+ vresult.type = tret;
+
+ vresult.dsymbolSemantic(sc);
+
+ if (!sc.insert(vresult))
+ error("out result %s is already defined", vresult.toChars());
+ assert(vresult.parent == this);
+ }
+ }
+
+ /****************************************************
+ * Merge into this function the 'in' contracts of all it overrides.
+ * 'in's are OR'd together, i.e. only one of them needs to pass.
+ */
+ extern (D) final Statement mergeFrequire(Statement sf, Expressions* params)
+ {
+ /* If a base function and its override both have an IN contract, then
+ * only one of them needs to succeed. This is done by generating:
+ *
+ * void derived.in() {
+ * try {
+ * base.in();
+ * }
+ * catch () {
+ * ... body of derived.in() ...
+ * }
+ * }
+ *
+ * So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid.
+ * If base.in() throws, then derived.in()'s body is executed.
+ */
+
+ foreach (fdv; foverrides)
+ {
+ /* The semantic pass on the contracts of the overridden functions must
+ * be completed before code generation occurs.
+ * https://issues.dlang.org/show_bug.cgi?id=3602
+ */
+ if (fdv.frequires && fdv.semanticRun != PASS.semantic3done)
+ {
+ assert(fdv._scope);
+ Scope* sc = fdv._scope.push();
+ sc.stc &= ~STC.override_;
+ fdv.semantic3(sc);
+ sc.pop();
+ }
+
+ sf = fdv.mergeFrequire(sf, params);
+ if (!sf || !fdv.fdrequire)
+ return null;
+ //printf("fdv.frequire: %s\n", fdv.frequire.toChars());
+ /* Make the call:
+ * try { __require(params); }
+ * catch (Throwable) { frequire; }
+ */
+ params = Expression.arraySyntaxCopy(params);
+ Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params);
+ Statement s2 = new ExpStatement(loc, e);
+
+ auto c = new Catch(loc, getThrowable(), null, sf);
+ c.internalCatch = true;
+ auto catches = new Catches();
+ catches.push(c);
+ sf = new TryCatchStatement(loc, s2, catches);
+ }
+ return sf;
+ }
+
+ /****************************************************
+ * Merge into this function the 'in' contracts of all it overrides.
+ */
+ extern (D) final Statement mergeFrequireInclusivePreview(Statement sf, Expressions* params)
+ {
+ /* If a base function and its override both have an IN contract, then
+ * the override in contract must widen the guarantee of the base contract.
+ * This is checked by generating:
+ *
+ * void derived.in() {
+ * try {
+ * ... body of derived.in() ...
+ * }
+ * catch () {
+ * // derived in rejected this argument. so parent must also reject it, or we've tightened the contract.
+ * base.in();
+ * assert(false, "Logic error: " ~ thr.msg);
+ * }
+ */
+
+ foreach (fdv; foverrides)
+ {
+ /* The semantic pass on the contracts of the overridden functions must
+ * be completed before code generation occurs.
+ * https://issues.dlang.org/show_bug.cgi?id=3602
+ */
+ if (fdv.frequires && fdv.semanticRun != PASS.semantic3done)
+ {
+ assert(fdv._scope);
+ Scope* sc = fdv._scope.push();
+ sc.stc &= ~STC.override_;
+ fdv.semantic3(sc);
+ sc.pop();
+ }
+
+ sf = fdv.mergeFrequireInclusivePreview(sf, params);
+ if (sf && fdv.fdrequire)
+ {
+ const loc = this.fdrequire.loc;
+
+ //printf("fdv.frequire: %s\n", fdv.frequire.toChars());
+ /* Make the call:
+ * try { frequire; }
+ * catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); }
+ */
+ Identifier id = Identifier.generateId("thr");
+ params = Expression.arraySyntaxCopy(params);
+ Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params);
+ Statement s2 = new ExpStatement(loc, e);
+ // assert(false, ...)
+ // TODO make this a runtime helper to allow:
+ // - chaining the original expression
+ // - nogc concatenation
+ Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract");
+ Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg));
+
+ Statement s3 = new CompoundStatement(loc, s2, fail);
+
+ auto c = new Catch(loc, getThrowable(), id, s3);
+ c.internalCatch = true;
+ auto catches = new Catches();
+ catches.push(c);
+ sf = new TryCatchStatement(loc, sf, catches);
+ }
+ else
+ return null;
+ }
+ return sf;
+ }
+
+ /****************************************************
+ * Determine whether an 'out' contract is declared inside
+ * the given function or any of its overrides.
+ * Params:
+ * fd = the function to search
+ * Returns:
+ * true found an 'out' contract
+ */
+ static bool needsFensure(FuncDeclaration fd)
+ {
+ if (fd.fensures)
+ return true;
+
+ foreach (fdv; fd.foverrides)
+ {
+ if (needsFensure(fdv))
+ return true;
+ }
+ return false;
+ }
+
+ /****************************************************
+ * Rewrite contracts as statements.
+ */
+ final void buildEnsureRequire()
+ {
+
+ if (frequires)
+ {
+ /* in { statements1... }
+ * in { statements2... }
+ * ...
+ * becomes:
+ * in { { statements1... } { statements2... } ... }
+ */
+ assert(frequires.dim);
+ auto loc = (*frequires)[0].loc;
+ auto s = new Statements;
+ foreach (r; *frequires)
+ {
+ s.push(new ScopeStatement(r.loc, r, r.loc));
+ }
+ frequire = new CompoundStatement(loc, s);
+ }
+
+ if (fensures)
+ {
+ /* out(id1) { statements1... }
+ * out(id2) { statements2... }
+ * ...
+ * becomes:
+ * out(__result) { { ref id1 = __result; { statements1... } }
+ * { ref id2 = __result; { statements2... } } ... }
+ */
+ assert(fensures.dim);
+ auto loc = (*fensures)[0].ensure.loc;
+ auto s = new Statements;
+ foreach (r; *fensures)
+ {
+ if (r.id && canBuildResultVar())
+ {
+ auto rloc = r.ensure.loc;
+ auto resultId = new IdentifierExp(rloc, Id.result);
+ auto init = new ExpInitializer(rloc, resultId);
+ auto stc = STC.ref_ | STC.temp | STC.result;
+ auto decl = new VarDeclaration(rloc, null, r.id, init, stc);
+ auto sdecl = new ExpStatement(rloc, decl);
+ s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc));
+ }
+ else
+ {
+ s.push(r.ensure);
+ }
+ }
+ fensure = new CompoundStatement(loc, s);
+ }
+
+ if (!isVirtual())
+ return;
+
+ /* Rewrite contracts as nested functions, then call them. Doing it as nested
+ * functions means that overriding functions can call them.
+ */
+ TypeFunction f = cast(TypeFunction) type;
+
+ /* Make a copy of the parameters and make them all ref */
+ static Parameters* toRefCopy(ParameterList parameterList)
+ {
+ auto result = new Parameters();
+
+ foreach (n, p; parameterList)
+ {
+ p = p.syntaxCopy();
+ if (!(p.storageClass & STC.lazy_))
+ p.storageClass = (p.storageClass | STC.ref_) & ~STC.out_;
+ p.defaultArg = null; // won't be the same with ref
+ result.push(p);
+ }
+
+ return result;
+ }
+
+ if (frequire)
+ {
+ /* in { ... }
+ * becomes:
+ * void __require(ref params) { ... }
+ * __require(params);
+ */
+ Loc loc = frequire.loc;
+ fdrequireParams = new Expressions();
+ if (parameters)
+ {
+ foreach (vd; *parameters)
+ fdrequireParams.push(new VarExp(loc, vd));
+ }
+ auto fo = cast(TypeFunction)(originalType ? originalType : f);
+ auto fparams = toRefCopy(fo.parameterList);
+ auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d);
+ tf.isnothrow = f.isnothrow;
+ tf.isnogc = f.isnogc;
+ tf.purity = f.purity;
+ tf.trust = f.trust;
+ auto fd = new FuncDeclaration(loc, loc, Id.require, STC.undefined_, tf);
+ fd.fbody = frequire;
+ Statement s1 = new ExpStatement(loc, fd);
+ Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdrequireParams);
+ Statement s2 = new ExpStatement(loc, e);
+ frequire = new CompoundStatement(loc, s1, s2);
+ fdrequire = fd;
+ }
+
+ /* We need to set fdensureParams here and not in the block below to
+ * have the parameters available when calling a base class ensure(),
+ * even if this function doesn't have an out contract.
+ */
+ fdensureParams = new Expressions();
+ if (canBuildResultVar())
+ fdensureParams.push(new IdentifierExp(loc, Id.result));
+ if (parameters)
+ {
+ foreach (vd; *parameters)
+ fdensureParams.push(new VarExp(loc, vd));
+ }
+
+ if (fensure)
+ {
+ /* out (result) { ... }
+ * becomes:
+ * void __ensure(ref tret result, ref params) { ... }
+ * __ensure(result, params);
+ */
+ Loc loc = fensure.loc;
+ auto fparams = new Parameters();
+ if (canBuildResultVar())
+ {
+ Parameter p = new Parameter(STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null);
+ fparams.push(p);
+ }
+ auto fo = cast(TypeFunction)(originalType ? originalType : f);
+ fparams.pushSlice((*toRefCopy(fo.parameterList))[]);
+ auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d);
+ tf.isnothrow = f.isnothrow;
+ tf.isnogc = f.isnogc;
+ tf.purity = f.purity;
+ tf.trust = f.trust;
+ auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.undefined_, tf);
+ fd.fbody = fensure;
+ Statement s1 = new ExpStatement(loc, fd);
+ Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdensureParams);
+ Statement s2 = new ExpStatement(loc, e);
+ fensure = new CompoundStatement(loc, s1, s2);
+ fdensure = fd;
+ }
+ }
+
+ /****************************************************
+ * Merge into this function the 'out' contracts of all it overrides.
+ * 'out's are AND'd together, i.e. all of them need to pass.
+ */
+ extern (D) final Statement mergeFensure(Statement sf, Identifier oid, Expressions* params)
+ {
+ /* Same comments as for mergeFrequire(), except that we take care
+ * of generating a consistent reference to the 'result' local by
+ * explicitly passing 'result' to the nested function as a reference
+ * argument.
+ * This won't work for the 'this' parameter as it would require changing
+ * the semantic code for the nested function so that it looks on the parameter
+ * list for the 'this' pointer, something that would need an unknown amount
+ * of tweaking of various parts of the compiler that I'd rather leave alone.
+ */
+ foreach (fdv; foverrides)
+ {
+ /* The semantic pass on the contracts of the overridden functions must
+ * be completed before code generation occurs.
+ * https://issues.dlang.org/show_bug.cgi?id=3602 and
+ * https://issues.dlang.org/show_bug.cgi?id=5230
+ */
+ if (needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done)
+ {
+ assert(fdv._scope);
+ Scope* sc = fdv._scope.push();
+ sc.stc &= ~STC.override_;
+ fdv.semantic3(sc);
+ sc.pop();
+ }
+
+ sf = fdv.mergeFensure(sf, oid, params);
+ if (fdv.fdensure)
+ {
+ //printf("fdv.fensure: %s\n", fdv.fensure.toChars());
+ // Make the call: __ensure(result, params)
+ params = Expression.arraySyntaxCopy(params);
+ if (canBuildResultVar())
+ {
+ Type t1 = fdv.type.nextOf().toBasetype();
+ Type t2 = this.type.nextOf().toBasetype();
+ if (t1.isBaseOf(t2, null))
+ {
+ /* Making temporary reference variable is necessary
+ * in covariant return.
+ * https://issues.dlang.org/show_bug.cgi?id=5204
+ * https://issues.dlang.org/show_bug.cgi?id=10479
+ */
+ Expression* eresult = &(*params)[0];
+ auto ei = new ExpInitializer(Loc.initial, *eresult);
+ auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei);
+ v.storage_class |= STC.temp;
+ auto de = new DeclarationExp(Loc.initial, v);
+ auto ve = new VarExp(Loc.initial, v);
+ *eresult = new CommaExp(Loc.initial, de, ve);
+ }
+ }
+ Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, false), params);
+ Statement s2 = new ExpStatement(loc, e);
+
+ if (sf)
+ {
+ sf = new CompoundStatement(sf.loc, s2, sf);
+ }
+ else
+ sf = s2;
+ }
+ }
+ return sf;
+ }
+
+ /*********************************************
+ * Returns: the function's parameter list, and whether
+ * it is variadic or not.
+ */
+ final ParameterList getParameterList()
+ {
+ if (type)
+ {
+ TypeFunction fdtype = type.isTypeFunction();
+ if (fdtype) // Could also be TypeError
+ return fdtype.parameterList;
+ }
+
+ return ParameterList(null, VarArg.none);
+ }
+
+ /**********************************
+ * Generate a FuncDeclaration for a runtime library function.
+ */
+ static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = 0)
+ {
+ return genCfunc(fparams, treturn, Identifier.idPool(name, cast(uint)strlen(name)), stc);
+ }
+
+ static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, StorageClass stc = 0)
+ {
+ FuncDeclaration fd;
+ TypeFunction tf;
+ Dsymbol s;
+ __gshared DsymbolTable st = null;
+
+ //printf("genCfunc(name = '%s')\n", id.toChars());
+ //printf("treturn\n\t"); treturn.print();
+
+ // See if already in table
+ if (!st)
+ st = new DsymbolTable();
+ s = st.lookup(id);
+ if (s)
+ {
+ fd = s.isFuncDeclaration();
+ assert(fd);
+ assert(fd.type.nextOf().equals(treturn));
+ }
+ else
+ {
+ tf = new TypeFunction(ParameterList(fparams), treturn, LINK.c, stc);
+ fd = new FuncDeclaration(Loc.initial, Loc.initial, id, STC.static_, tf);
+ fd.visibility = Visibility(Visibility.Kind.public_);
+ fd.linkage = LINK.c;
+
+ st.insert(fd);
+ }
+ return fd;
+ }
+
+ /******************
+ * Check parameters and return type of D main() function.
+ * Issue error messages.
+ */
+ extern (D) final void checkDmain()
+ {
+ TypeFunction tf = type.toTypeFunction();
+ const nparams = tf.parameterList.length;
+ bool argerr;
+ if (nparams == 1)
+ {
+ auto fparam0 = tf.parameterList[0];
+ auto t = fparam0.type.toBasetype();
+ if (t.ty != Tarray ||
+ t.nextOf().ty != Tarray ||
+ t.nextOf().nextOf().ty != Tchar ||
+ fparam0.storageClass & (STC.out_ | STC.ref_ | STC.lazy_))
+ {
+ argerr = true;
+ }
+ }
+
+ if (!tf.nextOf())
+ error("must return `int` or `void`");
+ else if (tf.nextOf().ty != Tint32 && tf.nextOf().ty != Tvoid)
+ error("must return `int` or `void`, not `%s`", tf.nextOf().toChars());
+ else if (tf.parameterList.varargs || nparams >= 2 || argerr)
+ error("parameters must be `main()` or `main(string[] args)`");
+ }
+
+ /***********************************************
+ * Check all return statements for a function to verify that returning
+ * using NRVO is possible.
+ *
+ * Returns:
+ * `false` if the result cannot be returned by hidden reference.
+ */
+ final bool checkNRVO()
+ {
+ if (!nrvo_can || returns is null)
+ return false;
+
+ auto tf = type.toTypeFunction();
+ if (tf.isref)
+ return false;
+
+ foreach (rs; *returns)
+ {
+ if (auto ve = rs.exp.isVarExp())
+ {
+ auto v = ve.var.isVarDeclaration();
+ if (!v || v.isOut() || v.isRef())
+ return false;
+ else if (nrvo_var is null)
+ {
+ // Variables in the data segment (e.g. globals, TLS or not),
+ // parameters and closure variables cannot be NRVOed.
+ if (v.isDataseg() || v.isParameter() || v.toParent2() != this)
+ return false;
+ // The variable type needs to be equivalent to the return type.
+ if (!v.type.equivalent(tf.next))
+ return false;
+ //printf("Setting nrvo to %s\n", v.toChars());
+ nrvo_var = v;
+ }
+ else if (nrvo_var != v)
+ return false;
+ }
+ else //if (!exp.isLvalue()) // keep NRVO-ability
+ return false;
+ }
+ return true;
+ }
+
+ override final inout(FuncDeclaration) isFuncDeclaration() inout
+ {
+ return this;
+ }
+
+ inout(FuncDeclaration) toAliasFunc() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/********************************************************
+ * Generate Expression to call the invariant.
+ * Input:
+ * ad aggregate with the invariant
+ * vthis variable with 'this'
+ * Returns:
+ * void expression that calls the invariant
+ */
+Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis)
+{
+ Expression e = null;
+ // Call invariant directly only if it exists
+ FuncDeclaration inv = ad.inv;
+ ClassDeclaration cd = ad.isClassDeclaration();
+
+ while (!inv && cd)
+ {
+ cd = cd.baseClass;
+ if (!cd)
+ break;
+ inv = cd.inv;
+ }
+ if (inv)
+ {
+ version (all)
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=13394
+ // For the correct mangling,
+ // run attribute inference on inv if needed.
+ inv.functionSemantic();
+ }
+
+ //e = new DsymbolExp(Loc.initial, inv);
+ //e = new CallExp(Loc.initial, e);
+ //e = e.semantic(sc2);
+
+ /* https://issues.dlang.org/show_bug.cgi?id=13113
+ * Currently virtual invariant calls completely
+ * bypass attribute enforcement.
+ * Change the behavior of pre-invariant call by following it.
+ */
+ e = new ThisExp(Loc.initial);
+ e.type = ad.type.addMod(vthis.type.mod);
+ e = new DotVarExp(Loc.initial, e, inv, false);
+ e.type = inv.type;
+ e = new CallExp(Loc.initial, e);
+ e.type = Type.tvoid;
+ }
+ return e;
+}
+
+/***************************************************
+ * Visit each overloaded function/template in turn, and call dg(s) on it.
+ * Exit when no more, or dg(s) returns nonzero.
+ *
+ * Params:
+ * fstart = symbol to start from
+ * dg = the delegate to be called on the overload
+ * sc = context used to check if symbol is accessible (and therefore visible),
+ * can be null
+ *
+ * Returns:
+ * ==0 continue
+ * !=0 done (and the return value from the last dg() call)
+ */
+extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null)
+{
+ Dsymbol next;
+ for (auto d = fstart; d; d = next)
+ {
+ import dmd.access : checkSymbolAccess;
+ if (auto od = d.isOverDeclaration())
+ {
+ /* The scope is needed here to check whether a function in
+ an overload set was added by means of a private alias (or a
+ selective import). If the scope where the alias is created
+ is imported somewhere, the overload set is visible, but the private
+ alias is not.
+ */
+ if (sc)
+ {
+ if (checkSymbolAccess(sc, od))
+ {
+ if (int r = overloadApply(od.aliassym, dg, sc))
+ return r;
+ }
+ }
+ else if (int r = overloadApply(od.aliassym, dg, sc))
+ return r;
+ next = od.overnext;
+ }
+ else if (auto fa = d.isFuncAliasDeclaration())
+ {
+ if (fa.hasOverloads)
+ {
+ if (int r = overloadApply(fa.funcalias, dg, sc))
+ return r;
+ }
+ else if (auto fd = fa.toAliasFunc())
+ {
+ if (int r = dg(fd))
+ return r;
+ }
+ else
+ {
+ d.error("is aliased to a function");
+ break;
+ }
+ next = fa.overnext;
+ }
+ else if (auto ad = d.isAliasDeclaration())
+ {
+ if (sc)
+ {
+ if (checkSymbolAccess(sc, ad))
+ next = ad.toAlias();
+ }
+ else
+ next = ad.toAlias();
+ if (next == ad)
+ break;
+ if (next == fstart)
+ break;
+ }
+ else if (auto td = d.isTemplateDeclaration())
+ {
+ if (int r = dg(td))
+ return r;
+ next = td.overnext;
+ }
+ else if (auto fd = d.isFuncDeclaration())
+ {
+ if (int r = dg(fd))
+ return r;
+ next = fd.overnext;
+ }
+ else if (auto os = d.isOverloadSet())
+ {
+ foreach (ds; os.a)
+ if (int r = dg(ds))
+ return r;
+ }
+ else
+ {
+ d.error("is aliased to a function");
+ break;
+ // BUG: should print error message?
+ }
+ }
+ return 0;
+}
+
+/**
+Checks for mismatching modifiers between `lhsMod` and `rhsMod` and prints the
+mismatching modifiers to `buf`.
+
+The modifiers of the `lhsMod` mismatching the ones with the `rhsMod` are printed, i.e.
+lhs(shared) vs. rhs() prints "`shared`", wheras lhs() vs rhs(shared) prints "non-shared".
+
+Params:
+ buf = output buffer to write to
+ lhsMod = modifier on the left-hand side
+ lhsMod = modifier on the right-hand side
+
+Returns:
+
+A tuple with `isMutable` and `isNotShared` set
+if the `lhsMod` is missing those modifiers (compared to rhs).
+*/
+auto MODMatchToBuffer(OutBuffer* buf, ubyte lhsMod, ubyte rhsMod)
+{
+ static struct Mismatches
+ {
+ bool isNotShared;
+ bool isMutable;
+ }
+
+ Mismatches mismatches;
+
+ bool bothMutable = ((lhsMod & rhsMod) == 0);
+ bool sharedMismatch = ((lhsMod ^ rhsMod) & MODFlags.shared_) != 0;
+ bool sharedMismatchOnly = ((lhsMod ^ rhsMod) == MODFlags.shared_);
+
+ if (lhsMod & MODFlags.shared_)
+ buf.writestring("`shared` ");
+ else if (sharedMismatch && !(lhsMod & MODFlags.immutable_))
+ {
+ buf.writestring("non-shared ");
+ mismatches.isNotShared = true;
+ }
+
+ if (bothMutable && sharedMismatchOnly)
+ {
+ }
+ else if (lhsMod & MODFlags.immutable_)
+ buf.writestring("`immutable` ");
+ else if (lhsMod & MODFlags.const_)
+ buf.writestring("`const` ");
+ else if (lhsMod & MODFlags.wild)
+ buf.writestring("`inout` ");
+ else
+ {
+ buf.writestring("mutable ");
+ mismatches.isMutable = true;
+ }
+
+ return mismatches;
+}
+
+///
+unittest
+{
+ OutBuffer buf;
+ auto mismatches = MODMatchToBuffer(&buf, MODFlags.shared_, 0);
+ assert(buf[] == "`shared` ");
+ assert(!mismatches.isNotShared);
+
+ buf.setsize(0);
+ mismatches = MODMatchToBuffer(&buf, 0, MODFlags.shared_);
+ assert(buf[] == "non-shared ");
+ assert(mismatches.isNotShared);
+
+ buf.setsize(0);
+ mismatches = MODMatchToBuffer(&buf, MODFlags.const_, 0);
+ assert(buf[] == "`const` ");
+ assert(!mismatches.isMutable);
+
+ buf.setsize(0);
+ mismatches = MODMatchToBuffer(&buf, 0, MODFlags.const_);
+ assert(buf[] == "mutable ");
+ assert(mismatches.isMutable);
+}
+
+private const(char)* prependSpace(const(char)* str)
+{
+ if (!str || !*str) return "";
+
+ return (" " ~ str.toDString() ~ "\0").ptr;
+}
+
+/// Flag used by $(LREF resolveFuncCall).
+enum FuncResolveFlag : ubyte
+{
+ standard = 0, /// issue error messages, solve the call.
+ quiet = 1, /// do not issue error message on no match, just return `null`.
+ overloadOnly = 2, /// only resolve overloads, i.e. do not issue error on ambiguous
+ /// matches and need explicit this.
+}
+
+/*******************************************
+ * Given a symbol that could be either a FuncDeclaration or
+ * a function template, resolve it to a function symbol.
+ * Params:
+ * loc = instantiation location
+ * sc = instantiation scope
+ * s = instantiation symbol
+ * tiargs = initial list of template arguments
+ * tthis = if !NULL, the `this` argument type
+ * fargs = arguments to function
+ * flags = see $(LREF FuncResolveFlag).
+ * Returns:
+ * if match is found, then function symbol, else null
+ */
+FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s,
+ Objects* tiargs, Type tthis, Expressions* fargs, FuncResolveFlag flags)
+{
+ if (!s)
+ return null; // no match
+
+ version (none)
+ {
+ printf("resolveFuncCall('%s')\n", s.toChars());
+ if (tthis)
+ printf("\tthis: %s\n", tthis.toChars());
+ if (fargs)
+ {
+ for (size_t i = 0; i < fargs.dim; i++)
+ {
+ Expression arg = (*fargs)[i];
+ assert(arg.type);
+ printf("\t%s: %s\n", arg.toChars(), arg.type.toChars());
+ }
+ }
+ }
+
+ if (tiargs && arrayObjectIsError(tiargs) ||
+ fargs && arrayObjectIsError(cast(Objects*)fargs))
+ {
+ return null;
+ }
+
+ MatchAccumulator m;
+ functionResolve(m, s, loc, sc, tiargs, tthis, fargs, null);
+ auto orig_s = s;
+
+ if (m.last > MATCH.nomatch && m.lastf)
+ {
+ if (m.count == 1) // exactly one match
+ {
+ if (!(flags & FuncResolveFlag.quiet))
+ m.lastf.functionSemantic();
+ return m.lastf;
+ }
+ if ((flags & FuncResolveFlag.overloadOnly) && !tthis && m.lastf.needThis())
+ {
+ return m.lastf;
+ }
+ }
+
+ /* Failed to find a best match.
+ * Do nothing or print error.
+ */
+ if (m.last == MATCH.nomatch)
+ {
+ // error was caused on matched function, not on the matching itself,
+ // so return the function to produce a better diagnostic
+ if (m.count == 1)
+ return m.lastf;
+ }
+
+ // We are done at this point, as the rest of this function generate
+ // a diagnostic on invalid match
+ if (flags & FuncResolveFlag.quiet)
+ return null;
+
+ auto fd = s.isFuncDeclaration();
+ auto od = s.isOverDeclaration();
+ auto td = s.isTemplateDeclaration();
+ if (td && td.funcroot)
+ s = fd = td.funcroot;
+
+ OutBuffer tiargsBuf;
+ arrayObjectsToBuffer(&tiargsBuf, tiargs);
+
+ OutBuffer fargsBuf;
+ fargsBuf.writeByte('(');
+ argExpTypesToCBuffer(&fargsBuf, fargs);
+ fargsBuf.writeByte(')');
+ if (tthis)
+ tthis.modToBuffer(&fargsBuf);
+
+ // The call is ambiguous
+ if (m.lastf && m.nextf)
+ {
+ TypeFunction tf1 = m.lastf.type.toTypeFunction();
+ TypeFunction tf2 = m.nextf.type.toTypeFunction();
+ const(char)* lastprms = parametersTypeToChars(tf1.parameterList);
+ const(char)* nextprms = parametersTypeToChars(tf2.parameterList);
+
+ const(char)* mod1 = prependSpace(MODtoChars(tf1.mod));
+ const(char)* mod2 = prependSpace(MODtoChars(tf2.mod));
+
+ .error(loc, "`%s.%s` called with argument types `%s` matches both:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`",
+ s.parent.toPrettyChars(), s.ident.toChars(),
+ fargsBuf.peekChars(),
+ m.lastf.loc.toChars(), m.lastf.toPrettyChars(), lastprms, mod1,
+ m.nextf.loc.toChars(), m.nextf.toPrettyChars(), nextprms, mod2);
+ return null;
+ }
+
+ // no match, generate an error messages
+ if (!fd)
+ {
+ // all of overloads are templates
+ if (td)
+ {
+ .error(loc, "%s `%s.%s` cannot deduce function from argument types `!(%s)%s`",
+ td.kind(), td.parent.toPrettyChars(), td.ident.toChars(),
+ tiargsBuf.peekChars(), fargsBuf.peekChars());
+
+ printCandidates(loc, td, sc.isDeprecated());
+ return null;
+ }
+ /* This case used to happen when several ctors are mixed in an agregate.
+ A (bad) error message is already generated in overloadApply().
+ see https://issues.dlang.org/show_bug.cgi?id=19729
+ and https://issues.dlang.org/show_bug.cgi?id=17259
+ */
+ if (!od)
+ return null;
+ }
+
+ if (od)
+ {
+ .error(loc, "none of the overloads of `%s` are callable using argument types `!(%s)%s`",
+ od.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars());
+ return null;
+ }
+
+ // remove when deprecation period of class allocators and deallocators is over
+ if (fd.isNewDeclaration() && fd.checkDisabled(loc, sc))
+ return null;
+
+ bool hasOverloads = fd.overnext !is null;
+ auto tf = fd.type.toTypeFunction();
+ if (tthis && !MODimplicitConv(tthis.mod, tf.mod)) // modifier mismatch
+ {
+ OutBuffer thisBuf, funcBuf;
+ MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod);
+ auto mismatches = MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod);
+ if (hasOverloads)
+ {
+ .error(loc, "none of the overloads of `%s` are callable using a %sobject",
+ fd.ident.toChars(), thisBuf.peekChars());
+ printCandidates(loc, fd, sc.isDeprecated());
+ return null;
+ }
+
+ const(char)* failMessage;
+ functionResolve(m, orig_s, loc, sc, tiargs, tthis, fargs, &failMessage);
+ if (failMessage)
+ {
+ .error(loc, "%s `%s%s%s` is not callable using argument types `%s`",
+ fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList),
+ tf.modToChars(), fargsBuf.peekChars());
+ errorSupplemental(loc, failMessage);
+ return null;
+ }
+
+ .error(loc, "%smethod `%s` is not callable using a %sobject",
+ funcBuf.peekChars(), fd.toPrettyChars(), thisBuf.peekChars());
+
+ if (mismatches.isNotShared)
+ .errorSupplemental(fd.loc, "Consider adding `shared` here");
+ else if (mismatches.isMutable)
+ .errorSupplemental(fd.loc, "Consider adding `const` or `inout` here");
+ return null;
+ }
+
+ //printf("tf = %s, args = %s\n", tf.deco, (*fargs)[0].type.deco);
+ if (hasOverloads)
+ {
+ .error(loc, "none of the overloads of `%s` are callable using argument types `%s`",
+ fd.toChars(), fargsBuf.peekChars());
+ printCandidates(loc, fd, sc.isDeprecated());
+ return null;
+ }
+
+ .error(loc, "%s `%s%s%s` is not callable using argument types `%s`",
+ fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList),
+ tf.modToChars(), fargsBuf.peekChars());
+ // re-resolve to check for supplemental message
+ const(char)* failMessage;
+ functionResolve(m, orig_s, loc, sc, tiargs, tthis, fargs, &failMessage);
+ if (failMessage)
+ errorSupplemental(loc, failMessage);
+ return null;
+}
+
+/*******************************************
+ * Prints template and function overload candidates as supplemental errors.
+ * Params:
+ * loc = instantiation location
+ * declaration = the declaration to print overload candidates for
+ * showDeprecated = If `false`, `deprecated` function won't be shown
+ */
+private void printCandidates(Decl)(const ref Loc loc, Decl declaration, bool showDeprecated)
+if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
+{
+ // max num of overloads to print (-v overrides this).
+ enum int DisplayLimit = 5;
+ int displayed;
+ const(char)* constraintsTip;
+
+ // determine if the first candidate was printed
+ bool printed = false;
+
+ overloadApply(declaration, (Dsymbol s)
+ {
+ Dsymbol nextOverload;
+
+ if (auto fd = s.isFuncDeclaration())
+ {
+ // Don't print overloads which have errors.
+ // Not that if the whole overload set has errors, we'll never reach
+ // this point so there's no risk of printing no candidate
+ if (fd.errors || fd.type.ty == Terror)
+ return 0;
+ // Don't print disabled functions, or `deprecated` outside of deprecated scope
+ if (fd.storage_class & STC.disable || (fd.isDeprecated() && !showDeprecated))
+ return 0;
+
+ const single_candidate = fd.overnext is null;
+ auto tf = cast(TypeFunction) fd.type;
+ .errorSupplemental(fd.loc,
+ printed ? " `%s%s`" :
+ single_candidate ? "Candidate is: `%s%s`" : "Candidates are: `%s%s`",
+ fd.toPrettyChars(),
+ parametersTypeToChars(tf.parameterList));
+ printed = true;
+ nextOverload = fd.overnext;
+ }
+ else if (auto td = s.isTemplateDeclaration())
+ {
+ import dmd.staticcond;
+
+ const tmsg = td.toCharsNoConstraints();
+ const cmsg = td.getConstraintEvalError(constraintsTip);
+
+ const single_candidate = td.overnext is null;
+
+ // add blank space if there are multiple candidates
+ // the length of the blank space is `strlen("Candidates are: ")`
+
+ if (cmsg)
+ {
+ .errorSupplemental(td.loc,
+ printed ? " `%s`\n%s" :
+ single_candidate ? "Candidate is: `%s`\n%s" : "Candidates are: `%s`\n%s",
+ tmsg, cmsg);
+ printed = true;
+ }
+ else
+ {
+ .errorSupplemental(td.loc,
+ printed ? " `%s`" :
+ single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`",
+ tmsg);
+ printed = true;
+ }
+ nextOverload = td.overnext;
+ }
+
+ if (global.params.verbose || ++displayed < DisplayLimit)
+ return 0;
+
+ // Too many overloads to sensibly display.
+ // Just show count of remaining overloads.
+ int num = 0;
+ overloadApply(nextOverload, (s) { ++num; return 0; });
+
+ if (num > 0)
+ .errorSupplemental(loc, "... (%d more, -v to show) ...", num);
+ return 1; // stop iterating
+ });
+
+ // Nothing was displayed, all overloads are either disabled or deprecated
+ if (!displayed)
+ .errorSupplemental(loc, "All possible candidates are marked as `deprecated` or `@disable`");
+ // should be only in verbose mode
+ if (constraintsTip)
+ .tip(constraintsTip);
+}
+
+/**************************************
+ * Returns an indirect type one step from t.
+ */
+Type getIndirection(Type t)
+{
+ t = t.baseElemOf();
+ if (t.ty == Tarray || t.ty == Tpointer)
+ return t.nextOf().toBasetype();
+ if (t.ty == Taarray || t.ty == Tclass)
+ return t;
+ if (t.ty == Tstruct)
+ return t.hasPointers() ? t : null; // TODO
+
+ // should consider TypeDelegate?
+ return null;
+}
+
+/**************************************
+ * Performs type-based alias analysis between a newly created value and a pre-
+ * existing memory reference:
+ *
+ * Assuming that a reference A to a value of type `ta` was available to the code
+ * that created a reference B to a value of type `tb`, it returns whether B
+ * might alias memory reachable from A based on the types involved (either
+ * directly or via any number of indirections in either A or B).
+ *
+ * This relation is not symmetric in the two arguments. For example, a
+ * a `const(int)` reference can point to a pre-existing `int`, but not the other
+ * way round.
+ *
+ * Examples:
+ *
+ * ta, tb, result
+ * `const(int)`, `int`, `false`
+ * `int`, `const(int)`, `true`
+ * `int`, `immutable(int)`, `false`
+ * const(immutable(int)*), immutable(int)*, false // BUG: returns true
+ *
+ * Params:
+ * ta = value type being referred to
+ * tb = referred to value type that could be constructed from ta
+ *
+ * Returns:
+ * true if reference to `tb` is isolated from reference to `ta`
+ */
+private bool traverseIndirections(Type ta, Type tb)
+{
+ //printf("traverseIndirections(%s, %s)\n", ta.toChars(), tb.toChars());
+
+ /* Threaded list of aggregate types already examined,
+ * used to break cycles.
+ * Cycles in type graphs can only occur with aggregates.
+ */
+ static struct Ctxt
+ {
+ Ctxt* prev;
+ Type type; // an aggregate type
+ }
+
+ static bool traverse(Type ta, Type tb, Ctxt* ctxt, bool reversePass)
+ {
+ //printf("traverse(%s, %s)\n", ta.toChars(), tb.toChars());
+ ta = ta.baseElemOf();
+ tb = tb.baseElemOf();
+
+ // First, check if the pointed-to types are convertible to each other such
+ // that they might alias directly.
+ static bool mayAliasDirect(Type source, Type target)
+ {
+ return
+ // if source is the same as target or can be const-converted to target
+ source.constConv(target) != MATCH.nomatch ||
+ // if target is void and source can be const-converted to target
+ (target.ty == Tvoid && MODimplicitConv(source.mod, target.mod));
+ }
+
+ if (mayAliasDirect(reversePass ? tb : ta, reversePass ? ta : tb))
+ {
+ //printf(" true mayalias %s %s %d\n", ta.toChars(), tb.toChars(), reversePass);
+ return false;
+ }
+ if (ta.nextOf() && ta.nextOf() == tb.nextOf())
+ {
+ //printf(" next==next %s %s %d\n", ta.toChars(), tb.toChars(), reversePass);
+ return true;
+ }
+
+ if (tb.ty == Tclass || tb.ty == Tstruct)
+ {
+ for (Ctxt* c = ctxt; c; c = c.prev)
+ if (tb == c.type)
+ return true;
+ Ctxt c;
+ c.prev = ctxt;
+ c.type = tb;
+
+ /* Traverse the type of each field of the aggregate
+ */
+ AggregateDeclaration sym = tb.toDsymbol(null).isAggregateDeclaration();
+ foreach (v; sym.fields)
+ {
+ Type tprmi = v.type.addMod(tb.mod);
+ //printf("\ttb = %s, tprmi = %s\n", tb.toChars(), tprmi.toChars());
+ if (!traverse(ta, tprmi, &c, reversePass))
+ return false;
+ }
+ }
+ else if (tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tpointer)
+ {
+ Type tind = tb.nextOf();
+ if (!traverse(ta, tind, ctxt, reversePass))
+ return false;
+ }
+ else if (tb.hasPointers())
+ {
+ // BUG: consider the context pointer of delegate types
+ return false;
+ }
+
+ // Still no match, so try breaking up ta if we have not done so yet.
+ if (!reversePass)
+ return traverse(tb, ta, ctxt, true);
+
+ return true;
+ }
+
+ // To handle arbitrary levels of indirections in both parameters, we
+ // recursively descend into aggregate members/levels of indirection in both
+ // `ta` and `tb` while avoiding cycles. Start with the original types.
+ const result = traverse(ta, tb, null, false);
+ //printf(" returns %d\n", result);
+ return result;
+}
+
+/* For all functions between outerFunc and f, mark them as needing
+ * a closure.
+ */
+private void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc)
+{
+ for (Dsymbol sx = f; sx && sx != outerFunc; sx = sx.toParentP(outerFunc))
+ {
+ FuncDeclaration fy = sx.isFuncDeclaration();
+ if (fy && fy.closureVars.dim)
+ {
+ /* fy needs a closure if it has closureVars[],
+ * because the frame pointer in the closure will be accessed.
+ */
+ fy.requiresClosure = true;
+ }
+ }
+}
+
+/********
+ * Given a nested function f inside a function outerFunc, check
+ * if any sibling callers of f have escaped. If so, mark
+ * all the enclosing functions as needing closures.
+ * This is recursive: we need to check the callers of our siblings.
+ * Note that nested functions can only call lexically earlier nested
+ * functions, so loops are impossible.
+ * Params:
+ * f = inner function (nested within outerFunc)
+ * outerFunc = outer function
+ * p = for internal recursion use
+ * Returns:
+ * true if any closures were needed
+ */
+private bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null)
+{
+ static struct PrevSibling
+ {
+ PrevSibling* p;
+ FuncDeclaration f;
+ }
+
+ PrevSibling ps;
+ ps.p = cast(PrevSibling*)p;
+ ps.f = f;
+
+ //printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f.toChars(), outerFunc.toChars());
+ bool bAnyClosures = false;
+ for (size_t i = 0; i < f.siblingCallers.dim; ++i)
+ {
+ FuncDeclaration g = f.siblingCallers[i];
+ if (g.isThis() || g.tookAddressOf)
+ {
+ markAsNeedingClosure(g, outerFunc);
+ bAnyClosures = true;
+ }
+
+ for (auto parent = g.toParentP(outerFunc); parent && parent !is outerFunc; parent = parent.toParentP(outerFunc))
+ {
+ // A parent of the sibling had its address taken.
+ // Assume escaping of parent affects its children, so needs propagating.
+ // see https://issues.dlang.org/show_bug.cgi?id=19679
+ FuncDeclaration parentFunc = parent.isFuncDeclaration;
+ if (parentFunc && parentFunc.tookAddressOf)
+ {
+ markAsNeedingClosure(parentFunc, outerFunc);
+ bAnyClosures = true;
+ }
+ }
+
+ PrevSibling* prev = cast(PrevSibling*)p;
+ while (1)
+ {
+ if (!prev)
+ {
+ bAnyClosures |= checkEscapingSiblings(g, outerFunc, &ps);
+ break;
+ }
+ if (prev.f == g)
+ break;
+ prev = prev.p;
+ }
+ }
+ //printf("\t%d\n", bAnyClosures);
+ return bAnyClosures;
+}
+
+/***********************************************************
+ * Used as a way to import a set of functions from another scope into this one.
+ */
+extern (C++) final class FuncAliasDeclaration : FuncDeclaration
+{
+ FuncDeclaration funcalias;
+ bool hasOverloads;
+
+ extern (D) this(Identifier ident, FuncDeclaration funcalias, bool hasOverloads = true)
+ {
+ super(funcalias.loc, funcalias.endloc, ident, funcalias.storage_class, funcalias.type);
+ assert(funcalias != this);
+ this.funcalias = funcalias;
+
+ this.hasOverloads = hasOverloads;
+ if (hasOverloads)
+ {
+ if (FuncAliasDeclaration fad = funcalias.isFuncAliasDeclaration())
+ this.hasOverloads = fad.hasOverloads;
+ }
+ else
+ {
+ // for internal use
+ assert(!funcalias.isFuncAliasDeclaration());
+ this.hasOverloads = false;
+ }
+ userAttribDecl = funcalias.userAttribDecl;
+ }
+
+ override inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout
+ {
+ return this;
+ }
+
+ override const(char)* kind() const
+ {
+ return "function alias";
+ }
+
+ override inout(FuncDeclaration) toAliasFunc() inout
+ {
+ return funcalias.toAliasFunc();
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
+{
+ TOK tok; // TOK.function_ or TOK.delegate_
+ Type treq; // target of return type inference
+
+ // backend
+ bool deferToObj;
+
+ extern (D) this(const ref Loc loc, const ref Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null)
+ {
+ super(loc, endloc, null, STC.undefined_, type);
+ this.ident = id ? id : Id.empty;
+ this.tok = tok;
+ this.fes = fes;
+ // Always infer scope for function literals
+ // See https://issues.dlang.org/show_bug.cgi?id=20362
+ this.flags |= FUNCFLAG.inferScope;
+ //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this.ident.toChars(), type.toChars());
+ }
+
+ override FuncLiteralDeclaration syntaxCopy(Dsymbol s)
+ {
+ //printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars());
+ assert(!s);
+ auto f = new FuncLiteralDeclaration(loc, endloc, type.syntaxCopy(), tok, fes, ident);
+ f.treq = treq; // don't need to copy
+ FuncDeclaration.syntaxCopy(f);
+ return f;
+ }
+
+ override bool isNested() const
+ {
+ //printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars());
+ return (tok != TOK.function_) && !isThis();
+ }
+
+ override inout(AggregateDeclaration) isThis() inout
+ {
+ return tok == TOK.delegate_ ? super.isThis() : null;
+ }
+
+ override bool isVirtual() const
+ {
+ return false;
+ }
+
+ override bool addPreInvariant()
+ {
+ return false;
+ }
+
+ override bool addPostInvariant()
+ {
+ return false;
+ }
+
+ /*******************************
+ * Modify all expression type of return statements to tret.
+ *
+ * On function literals, return type may be modified based on the context type
+ * after its semantic3 is done, in FuncExp::implicitCastTo.
+ *
+ * A function() dg = (){ return new B(); } // OK if is(B : A) == true
+ *
+ * If B to A conversion is convariant that requires offseet adjusting,
+ * all return statements should be adjusted to return expressions typed A.
+ */
+ void modifyReturns(Scope* sc, Type tret)
+ {
+ import dmd.statement_rewrite_walker;
+
+ extern (C++) final class RetWalker : StatementRewriteWalker
+ {
+ alias visit = typeof(super).visit;
+ public:
+ Scope* sc;
+ Type tret;
+ FuncLiteralDeclaration fld;
+
+ override void visit(ReturnStatement s)
+ {
+ Expression exp = s.exp;
+ if (exp && !exp.type.equals(tret))
+ {
+ s.exp = exp.castTo(sc, tret);
+ }
+ }
+ }
+
+ if (semanticRun < PASS.semantic3done)
+ return;
+
+ if (fes)
+ return;
+
+ scope RetWalker w = new RetWalker();
+ w.sc = sc;
+ w.tret = tret;
+ w.fld = this;
+ fbody.accept(w);
+
+ // Also update the inferred function type to match the new return type.
+ // This is required so the code generator does not try to cast the
+ // modified returns back to the original type.
+ if (inferRetType && type.nextOf() != tret)
+ type.toTypeFunction().next = tret;
+ }
+
+ override inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout
+ {
+ return this;
+ }
+
+ override const(char)* kind() const
+ {
+ // GCC requires the (char*) casts
+ return (tok != TOK.function_) ? "delegate" : "function";
+ }
+
+ override const(char)* toPrettyChars(bool QualifyTypes = false)
+ {
+ if (parent)
+ {
+ TemplateInstance ti = parent.isTemplateInstance();
+ if (ti)
+ return ti.tempdecl.toPrettyChars(QualifyTypes);
+ }
+ return Dsymbol.toPrettyChars(QualifyTypes);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class CtorDeclaration : FuncDeclaration
+{
+ bool isCpCtor;
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false)
+ {
+ super(loc, endloc, Id.ctor, stc, type);
+ this.isCpCtor = isCpCtor;
+ //printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars());
+ }
+
+ override CtorDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto f = new CtorDeclaration(loc, endloc, storage_class, type.syntaxCopy());
+ FuncDeclaration.syntaxCopy(f);
+ return f;
+ }
+
+ override const(char)* kind() const
+ {
+ return isCpCtor ? "copy constructor" : "constructor";
+ }
+
+ override const(char)* toChars() const
+ {
+ return "this";
+ }
+
+ override bool isVirtual() const
+ {
+ return false;
+ }
+
+ override bool addPreInvariant()
+ {
+ return false;
+ }
+
+ override bool addPostInvariant()
+ {
+ return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on);
+ }
+
+ override inout(CtorDeclaration) isCtorDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class PostBlitDeclaration : FuncDeclaration
+{
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id)
+ {
+ super(loc, endloc, id, stc, null);
+ }
+
+ override PostBlitDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto dd = new PostBlitDeclaration(loc, endloc, storage_class, ident);
+ FuncDeclaration.syntaxCopy(dd);
+ return dd;
+ }
+
+ override bool isVirtual() const
+ {
+ return false;
+ }
+
+ override bool addPreInvariant()
+ {
+ return false;
+ }
+
+ override bool addPostInvariant()
+ {
+ return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on);
+ }
+
+ override bool overloadInsert(Dsymbol s)
+ {
+ return false; // cannot overload postblits
+ }
+
+ override inout(PostBlitDeclaration) isPostBlitDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DtorDeclaration : FuncDeclaration
+{
+ extern (D) this(const ref Loc loc, const ref Loc endloc)
+ {
+ super(loc, endloc, Id.dtor, STC.undefined_, null);
+ }
+
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id)
+ {
+ super(loc, endloc, id, stc, null);
+ }
+
+ override DtorDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto dd = new DtorDeclaration(loc, endloc, storage_class, ident);
+ FuncDeclaration.syntaxCopy(dd);
+ return dd;
+ }
+
+ override const(char)* kind() const
+ {
+ return "destructor";
+ }
+
+ override const(char)* toChars() const
+ {
+ return "~this";
+ }
+
+ override bool isVirtual() const
+ {
+ // D dtor's don't get put into the vtbl[]
+ // this is a hack so that extern(C++) destructors report as virtual, which are manually added to the vtable
+ return vtblIndex != -1;
+ }
+
+ override bool addPreInvariant()
+ {
+ return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on);
+ }
+
+ override bool addPostInvariant()
+ {
+ return false;
+ }
+
+ override bool overloadInsert(Dsymbol s)
+ {
+ return false; // cannot overload destructors
+ }
+
+ override inout(DtorDeclaration) isDtorDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class StaticCtorDeclaration : FuncDeclaration
+{
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc)
+ {
+ super(loc, endloc, Identifier.generateIdWithLoc("_staticCtor", loc), STC.static_ | stc, null);
+ }
+
+ extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc)
+ {
+ super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null);
+ }
+
+ override StaticCtorDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto scd = new StaticCtorDeclaration(loc, endloc, storage_class);
+ FuncDeclaration.syntaxCopy(scd);
+ return scd;
+ }
+
+ override final inout(AggregateDeclaration) isThis() inout @nogc nothrow pure @safe
+ {
+ return null;
+ }
+
+ override final bool isVirtual() const @nogc nothrow pure @safe
+ {
+ return false;
+ }
+
+ override final bool addPreInvariant() @nogc nothrow pure @safe
+ {
+ return false;
+ }
+
+ override final bool addPostInvariant() @nogc nothrow pure @safe
+ {
+ return false;
+ }
+
+ override final bool hasStaticCtorOrDtor() @nogc nothrow pure @safe
+ {
+ return true;
+ }
+
+ override final inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout @nogc nothrow pure @safe
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class SharedStaticCtorDeclaration : StaticCtorDeclaration
+{
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc)
+ {
+ super(loc, endloc, "_sharedStaticCtor", stc);
+ }
+
+ override SharedStaticCtorDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto scd = new SharedStaticCtorDeclaration(loc, endloc, storage_class);
+ FuncDeclaration.syntaxCopy(scd);
+ return scd;
+ }
+
+ override inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class StaticDtorDeclaration : FuncDeclaration
+{
+ VarDeclaration vgate; // 'gate' variable
+
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc)
+ {
+ super(loc, endloc, Identifier.generateIdWithLoc("_staticDtor", loc), STC.static_ | stc, null);
+ }
+
+ extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc)
+ {
+ super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null);
+ }
+
+ override StaticDtorDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto sdd = new StaticDtorDeclaration(loc, endloc, storage_class);
+ FuncDeclaration.syntaxCopy(sdd);
+ return sdd;
+ }
+
+ override final inout(AggregateDeclaration) isThis() inout
+ {
+ return null;
+ }
+
+ override final bool isVirtual() const
+ {
+ return false;
+ }
+
+ override final bool hasStaticCtorOrDtor()
+ {
+ return true;
+ }
+
+ override final bool addPreInvariant()
+ {
+ return false;
+ }
+
+ override final bool addPostInvariant()
+ {
+ return false;
+ }
+
+ override final inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration
+{
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc)
+ {
+ super(loc, endloc, "_sharedStaticDtor", stc);
+ }
+
+ override SharedStaticDtorDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto sdd = new SharedStaticDtorDeclaration(loc, endloc, storage_class);
+ FuncDeclaration.syntaxCopy(sdd);
+ return sdd;
+ }
+
+ override inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class InvariantDeclaration : FuncDeclaration
+{
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id, Statement fbody)
+ {
+ super(loc, endloc, id ? id : Identifier.generateId("__invariant"), stc, null);
+ this.fbody = fbody;
+ }
+
+ override InvariantDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto id = new InvariantDeclaration(loc, endloc, storage_class, null, null);
+ FuncDeclaration.syntaxCopy(id);
+ return id;
+ }
+
+ override bool isVirtual() const
+ {
+ return false;
+ }
+
+ override bool addPreInvariant()
+ {
+ return false;
+ }
+
+ override bool addPostInvariant()
+ {
+ return false;
+ }
+
+ override inout(InvariantDeclaration) isInvariantDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+
+/***********************************************************
+ */
+extern (C++) final class UnitTestDeclaration : FuncDeclaration
+{
+ char* codedoc; // for documented unittest
+
+ // toObjFile() these nested functions after this one
+ FuncDeclarations deferredNested;
+
+ extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, char* codedoc)
+ {
+ super(loc, endloc, Identifier.generateIdWithLoc("__unittest", loc), stc, null);
+ this.codedoc = codedoc;
+ }
+
+ override UnitTestDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto utd = new UnitTestDeclaration(loc, endloc, storage_class, codedoc);
+ FuncDeclaration.syntaxCopy(utd);
+ return utd;
+ }
+
+ override inout(AggregateDeclaration) isThis() inout
+ {
+ return null;
+ }
+
+ override bool isVirtual() const
+ {
+ return false;
+ }
+
+ override bool addPreInvariant()
+ {
+ return false;
+ }
+
+ override bool addPostInvariant()
+ {
+ return false;
+ }
+
+ override inout(UnitTestDeclaration) isUnitTestDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class NewDeclaration : FuncDeclaration
+{
+ extern (D) this(const ref Loc loc, StorageClass stc)
+ {
+ super(loc, Loc.initial, Id.classNew, STC.static_ | stc, null);
+ }
+
+ override NewDeclaration syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ auto f = new NewDeclaration(loc, storage_class);
+ FuncDeclaration.syntaxCopy(f);
+ return f;
+ }
+
+ override const(char)* kind() const
+ {
+ return "allocator";
+ }
+
+ override bool isVirtual() const
+ {
+ return false;
+ }
+
+ override bool addPreInvariant()
+ {
+ return false;
+ }
+
+ override bool addPostInvariant()
+ {
+ return false;
+ }
+
+ override inout(NewDeclaration) isNewDeclaration() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d
new file mode 100644
index 00000000000..9b65d024b97
--- /dev/null
+++ b/gcc/d/dmd/globals.d
@@ -0,0 +1,640 @@
+/**
+ * Stores command line options and contains other miscellaneous declarations.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/globals.d, _globals.d)
+ * Documentation: https://dlang.org/phobos/dmd_globals.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/globals.d
+ */
+
+module dmd.globals;
+
+import core.stdc.stdint;
+import dmd.root.array;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.identifier;
+
+/// Defines a setting for how compiler warnings and deprecations are handled
+enum DiagnosticReporting : ubyte
+{
+ error, /// generate an error
+ inform, /// generate a warning
+ off, /// disable diagnostic
+}
+
+/// How code locations are formatted for diagnostic reporting
+enum MessageStyle : ubyte
+{
+ digitalmars, /// filename.d(line): message
+ gnu, /// filename.d:line: message, see https://www.gnu.org/prep/standards/html_node/Errors.html
+}
+
+/// In which context checks for assertions, contracts, bounds checks etc. are enabled
+enum CHECKENABLE : ubyte
+{
+ _default, /// initial value
+ off, /// never do checking
+ on, /// always do checking
+ safeonly, /// do checking only in @safe functions
+}
+
+/// What should happend when an assertion fails
+enum CHECKACTION : ubyte
+{
+ D, /// call D assert on failure
+ C, /// call C assert on failure
+ halt, /// cause program halt on failure
+ context, /// call D assert with the error context on failure
+}
+
+/// Position Indepent Code setting
+enum PIC : ubyte
+{
+ fixed, /// located at a specific address
+ pic, /// Position Independent Code
+ pie, /// Position Independent Executable
+}
+
+/**
+Each flag represents a field that can be included in the JSON output.
+
+NOTE: set type to uint so its size matches C++ unsigned type
+*/
+enum JsonFieldFlags : uint
+{
+ none = 0,
+ compilerInfo = (1 << 0),
+ buildInfo = (1 << 1),
+ modules = (1 << 2),
+ semantics = (1 << 3),
+}
+
+/// Version of C++ standard to support
+enum CppStdRevision : uint
+{
+ cpp98 = 1997_11,
+ cpp11 = 2011_03,
+ cpp14 = 2014_02,
+ cpp17 = 2017_03,
+ cpp20 = 2020_02,
+}
+
+/// Configuration for the C++ header generator
+enum CxxHeaderMode : uint
+{
+ none, /// Don't generate headers
+ silent, /// Generate headers
+ verbose /// Generate headers and add comments for hidden declarations
+}
+
+/// Trivalent boolean to represent the state of a `revert`able change
+enum FeatureState : byte
+{
+ default_ = -1, /// Not specified by the user
+ disabled = 0, /// Specified as `-revert=`
+ enabled = 1 /// Specified as `-preview=`
+}
+
+/// Put command line switches in here
+extern (C++) struct Param
+{
+ bool obj = true; // write object file
+ bool link = true; // perform link
+ bool dll; // generate shared dynamic library
+ bool lib; // write library file instead of object file(s)
+ bool multiobj; // break one object file into multiple ones
+ bool oneobj; // write one object file instead of multiple ones
+ bool trace; // insert profiling hooks
+ bool tracegc; // instrument calls to 'new'
+ bool verbose; // verbose compile
+ bool vcg_ast; // write-out codegen-ast
+ bool showColumns; // print character (column) numbers in diagnostics
+ bool vtls; // identify thread local variables
+ bool vtemplates; // collect and list statistics on template instantiations
+ bool vtemplatesListInstances; // collect and list statistics on template instantiations origins. TODO: make this an enum when we want to list other kinds of instances
+ bool vgc; // identify gc usage
+ bool vfield; // identify non-mutable field variables
+ bool vcomplex = true; // identify complex/imaginary type usage
+ ubyte symdebug; // insert debug symbolic information
+ bool symdebugref; // insert debug information for all referenced types, too
+ bool optimize; // run optimizer
+ DiagnosticReporting useDeprecated = DiagnosticReporting.inform; // how use of deprecated features are handled
+ bool stackstomp; // add stack stomping code
+ bool useUnitTests; // generate unittest code
+ bool useInline = false; // inline expand functions
+ FeatureState useDIP25; // implement http://wiki.dlang.org/DIP25
+ FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params
+ bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md
+ bool release; // build release version
+ bool preservePaths; // true means don't strip path from source file
+ DiagnosticReporting warnings = DiagnosticReporting.off; // how compiler warnings are handled
+ PIC pic = PIC.fixed; // generate fixed, pic or pie code
+ bool color; // use ANSI colors in console output
+ bool cov; // generate code coverage data
+ ubyte covPercent; // 0..100 code coverage percentage required
+ bool ctfe_cov = false; // generate coverage data for ctfe
+ bool nofloat; // code should not pull in floating point support
+ bool ignoreUnsupportedPragmas; // rather than error on them
+ bool useModuleInfo = true; // generate runtime module information
+ bool useTypeInfo = true; // generate runtime type information
+ bool useExceptions = true; // support exception handling
+ bool noSharedAccess; // read/write access to shared memory objects
+ bool previewIn; // `in` means `[ref] scope const`, accepts rvalues
+ bool shortenedMethods; // allow => in normal function declarations
+ bool betterC; // be a "better C" compiler; no dependency on D runtime
+ bool addMain; // add a default main() function
+ bool allInst; // generate code for all template instantiations
+ bool fix16997; // fix integral promotions for unary + - ~ operators
+ // https://issues.dlang.org/show_bug.cgi?id=16997
+ bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes
+ bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract
+ /** The --transition=safe switch should only be used to show code with
+ * silent semantics changes related to @safe improvements. It should not be
+ * used to hide a feature that will have to go through deprecate-then-error
+ * before becoming default.
+ */
+ bool ehnogc; // use @nogc exception handling
+ FeatureState dtorFields; // destruct fields of partially constructed objects
+ // https://issues.dlang.org/show_bug.cgi?id=14246
+ bool fieldwise; // do struct equality testing field-wise rather than by memcmp()
+ bool rvalueRefParam; // allow rvalues to be arguments to ref parameters
+ // http://dconf.org/2019/talks/alexandrescu.html
+ // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a
+ // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html
+ // Implementation: https://github.com/dlang/dmd/pull/9817
+
+ CppStdRevision cplusplus = CppStdRevision.cpp11; // version of C++ standard to support
+
+ bool markdown = true; // enable Markdown replacements in Ddoc
+ bool vmarkdown; // list instances of Markdown replacements in Ddoc
+
+ bool showGaggedErrors; // print gagged errors anyway
+ bool printErrorContext; // print errors with the error context (the error line in the source file)
+ bool manual; // open browser on compiler manual
+ bool usage; // print usage and exit
+ bool mcpuUsage; // print help on -mcpu switch
+ bool transitionUsage; // print help on -transition switch
+ bool checkUsage; // print help on -check switch
+ bool checkActionUsage; // print help on -checkaction switch
+ bool revertUsage; // print help on -revert switch
+ bool previewUsage; // print help on -preview switch
+ bool externStdUsage; // print help on -extern-std switch
+ bool hcUsage; // print help on -HC switch
+ bool logo; // print compiler logo
+
+ CHECKENABLE useInvariants = CHECKENABLE._default; // generate class invariant checks
+ CHECKENABLE useIn = CHECKENABLE._default; // generate precondition checks
+ CHECKENABLE useOut = CHECKENABLE._default; // generate postcondition checks
+ CHECKENABLE useArrayBounds = CHECKENABLE._default; // when to generate code for array bounds checks
+ CHECKENABLE useAssert = CHECKENABLE._default; // when to generate code for assert()'s
+ CHECKENABLE useSwitchError = CHECKENABLE._default; // check for switches without a default
+ CHECKENABLE boundscheck = CHECKENABLE._default; // state of -boundscheck switch
+
+ CHECKACTION checkAction = CHECKACTION.D; // action to take when bounds, asserts or switch defaults are violated
+
+ uint errorLimit = 20;
+
+ const(char)[] argv0; // program name
+ Array!(const(char)*) modFileAliasStrings; // array of char*'s of -I module filename alias strings
+ Array!(const(char)*)* imppath; // array of char*'s of where to look for import modules
+ Array!(const(char)*)* fileImppath; // array of char*'s of where to look for file import modules
+ const(char)[] objdir; // .obj/.lib file output directory
+ const(char)[] objname; // .obj file output name
+ const(char)[] libname; // .lib file output name
+
+ bool doDocComments; // process embedded documentation comments
+ const(char)[] docdir; // write documentation file to docdir directory
+ const(char)[] docname; // write documentation file to docname
+ Array!(const(char)*) ddocfiles; // macro include files for Ddoc
+
+ bool doHdrGeneration; // process embedded documentation comments
+ const(char)[] hdrdir; // write 'header' file to docdir directory
+ const(char)[] hdrname; // write 'header' file to docname
+ bool hdrStripPlainFunctions = true; // strip the bodies of plain (non-template) functions
+
+ CxxHeaderMode doCxxHdrGeneration; /// Generate 'Cxx header' file
+ const(char)[] cxxhdrdir; // write 'header' file to docdir directory
+ const(char)[] cxxhdrname; // write 'header' file to docname
+
+ bool doJsonGeneration; // write JSON file
+ const(char)[] jsonfilename; // write JSON file to jsonfilename
+ JsonFieldFlags jsonFieldFlags; // JSON field flags to include
+
+ OutBuffer* mixinOut; // write expanded mixins for debugging
+ const(char)* mixinFile; // .mixin file output name
+ int mixinLines; // Number of lines in writeMixins
+
+ uint debuglevel; // debug level
+ Array!(const(char)*)* debugids; // debug identifiers
+
+ uint versionlevel; // version level
+ Array!(const(char)*)* versionids; // version identifiers
+
+ const(char)[] defaultlibname; // default library for non-debug builds
+ const(char)[] debuglibname; // default library for debug builds
+ const(char)[] mscrtlib; // MS C runtime library
+
+ const(char)[] moduleDepsFile; // filename for deps output
+ OutBuffer* moduleDeps; // contents to be written to deps file
+
+ bool emitMakeDeps; // whether to emit makedeps
+ const(char)[] makeDepsFile; // filename for makedeps output
+ Array!(const(char)*) makeDeps; // dependencies for makedeps
+
+ MessageStyle messageStyle = MessageStyle.digitalmars; // style of file/line annotations on messages
+
+ bool run; // run resulting executable
+ Strings runargs; // arguments for executable
+
+ // Linker stuff
+ Array!(const(char)*) objfiles;
+ Array!(const(char)*) linkswitches;
+ Array!bool linkswitchIsForCC;
+ Array!(const(char)*) libfiles;
+ Array!(const(char)*) dllfiles;
+ const(char)[] deffile;
+ const(char)[] resfile;
+ const(char)[] exefile;
+ const(char)[] mapfile;
+}
+
+alias structalign_t = uint;
+
+// magic value means "match whatever the underlying C compiler does"
+// other values are all powers of 2
+enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0);
+
+enum mars_ext = "d"; // for D source files
+enum doc_ext = "html"; // for Ddoc generated files
+enum ddoc_ext = "ddoc"; // for Ddoc macro include files
+enum dd_ext = "dd"; // for Ddoc source files
+enum hdr_ext = "di"; // for D 'header' import files
+enum json_ext = "json"; // for JSON files
+enum map_ext = "map"; // for .map files
+enum c_ext = "c"; // for C source files
+enum i_ext = "i"; // for preprocessed C source file
+
+/**
+ * Collection of global compiler settings and global state used by the frontend
+ */
+extern (C++) struct Global
+{
+ const(char)[] inifilename; /// filename of configuration file as given by `-conf=`, or default value
+
+ string copyright = "Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved";
+ string written = "written by Walter Bright";
+
+ Array!(const(char)*)* path; /// Array of char*'s which form the import lookup path
+ Array!(const(char)*)* filePath; /// Array of char*'s which form the file import lookup path
+
+ private enum string _version = import("VERSION");
+ private enum uint _versionNumber = parseVersionNumber(_version);
+
+ const(char)[] vendor; /// Compiler backend name
+
+ Param params; /// command line parameters
+ uint errors; /// number of errors reported so far
+ uint warnings; /// number of warnings reported so far
+ uint gag; /// !=0 means gag reporting of errors & warnings
+ uint gaggedErrors; /// number of errors reported while gagged
+ uint gaggedWarnings; /// number of warnings reported while gagged
+
+ void* console; /// opaque pointer to console for controlling text attributes
+
+ Array!Identifier* versionids; /// command line versions and predefined versions
+ Array!Identifier* debugids; /// command line debug versions and predefined versions
+
+ enum recursionLimit = 500; /// number of recursive template expansions before abort
+
+ nothrow:
+
+ /**
+ * Start ignoring compile errors instead of reporting them.
+ *
+ * Used for speculative compilation like `__traits(compiles, XXX)`, but also internally
+ * to e.g. try out an `alias this` rewrite without comitting to it.
+ *
+ * Works like a stack, so N calls to `startGagging` should be paired with N
+ * calls to `endGagging`.
+ *
+ * Returns: the current number of gagged errors, which should later be passed to `endGagging`
+ */
+ extern (C++) uint startGagging()
+ {
+ ++gag;
+ gaggedWarnings = 0;
+ return gaggedErrors;
+ }
+
+ /**
+ * Stop gagging, restoring the old gagged state before the most recent call to `startGagging`.
+ *
+ * Params:
+ * oldGagged = the previous number of errors, as returned by `startGagging`
+ * Returns: true if errors occurred while gagged.
+ */
+ extern (C++) bool endGagging(uint oldGagged)
+ {
+ bool anyErrs = (gaggedErrors != oldGagged);
+ --gag;
+ // Restore the original state of gagged errors; set total errors
+ // to be original errors + new ungagged errors.
+ errors -= (gaggedErrors - oldGagged);
+ gaggedErrors = oldGagged;
+ return anyErrs;
+ }
+
+ /**
+ * Increment the error count to record that an error has occurred in the current context.
+ *
+ * An error message may or may not have been printed.
+ */
+ extern (C++) void increaseErrorCount()
+ {
+ if (gag)
+ ++gaggedErrors;
+ ++errors;
+ }
+
+ extern (C++) void _init()
+ {
+ version (MARS)
+ {
+ vendor = "Digital Mars D";
+
+ // -color=auto is the default value
+ import dmd.console : detectTerminal;
+ params.color = detectTerminal();
+ }
+ else version (IN_GCC)
+ {
+ vendor = "GNU D";
+ }
+ }
+
+ /**
+ * Deinitializes the global state of the compiler.
+ *
+ * This can be used to restore the state set by `_init` to its original
+ * state.
+ */
+ extern (D) void deinitialize()
+ {
+ this = this.init;
+ }
+
+ /**
+ * Computes the version number __VERSION__ from the compiler version string.
+ */
+ extern (D) private static uint parseVersionNumber(string version_)
+ {
+ //
+ // parse _version
+ //
+ uint major = 0;
+ uint minor = 0;
+ bool point = false;
+ // skip initial 'v'
+ foreach (const c; version_[1..$])
+ {
+ if ('0' <= c && c <= '9') // isdigit
+ {
+ minor = minor * 10 + c - '0';
+ }
+ else if (c == '.')
+ {
+ if (point)
+ break; // ignore everything after second '.'
+ point = true;
+ major = minor;
+ minor = 0;
+ }
+ else
+ break;
+ }
+ return major * 1000 + minor;
+ }
+
+ /**
+ Returns: the version as the number that would be returned for __VERSION__
+ */
+ extern(C++) uint versionNumber()
+ {
+ return _versionNumber;
+ }
+
+ /**
+ Returns: compiler version string.
+ */
+ extern(D) string versionString()
+ {
+ return _version;
+ }
+
+ /**
+ Returns: compiler version as char string.
+ */
+ extern(C++) const(char*) versionChars()
+ {
+ return _version.ptr;
+ }
+
+ /**
+ Returns: the final defaultlibname based on the command-line parameters
+ */
+ extern (D) const(char)[] finalDefaultlibname() const
+ {
+ return params.betterC ? null :
+ params.symdebug ? params.debuglibname : params.defaultlibname;
+ }
+}
+
+// Because int64_t and friends may be any integral type of the
+// correct size, we have to explicitly ask for the correct
+// integer type to get the correct mangling with dmd
+
+// Be careful not to care about sign when using dinteger_t
+// use this instead of integer_t to
+// avoid conflicts with system #include's
+alias dinteger_t = ulong;
+// Signed and unsigned variants
+alias sinteger_t = long;
+alias uinteger_t = ulong;
+
+alias d_int8 = int8_t;
+alias d_uns8 = uint8_t;
+alias d_int16 = int16_t;
+alias d_uns16 = uint16_t;
+alias d_int32 = int32_t;
+alias d_uns32 = uint32_t;
+alias d_int64 = int64_t;
+alias d_uns64 = uint64_t;
+
+version (DMDLIB)
+{
+ version = LocOffset;
+}
+
+/**
+A source code location
+
+Used for error messages, `__FILE__` and `__LINE__` tokens, `__traits(getLocation, XXX)`,
+debug info etc.
+*/
+struct Loc
+{
+ /// zero-terminated filename string, either absolute or relative to cwd
+ const(char)* filename;
+ uint linnum; /// line number, starting from 1
+ uint charnum; /// utf8 code unit index relative to start of line, starting from 1
+ version (LocOffset)
+ uint fileOffset; /// utf8 code unit index relative to start of file, starting from 0
+
+ static immutable Loc initial; /// use for default initialization of const ref Loc's
+
+nothrow:
+ extern (D) this(const(char)* filename, uint linnum, uint charnum) pure
+ {
+ this.linnum = linnum;
+ this.charnum = charnum;
+ this.filename = filename;
+ }
+
+ extern (C++) const(char)* toChars(
+ bool showColumns = global.params.showColumns,
+ ubyte messageStyle = global.params.messageStyle) const pure nothrow
+ {
+ OutBuffer buf;
+ if (filename)
+ {
+ buf.writestring(filename);
+ }
+ if (linnum)
+ {
+ final switch (messageStyle)
+ {
+ case MessageStyle.digitalmars:
+ buf.writeByte('(');
+ buf.print(linnum);
+ if (showColumns && charnum)
+ {
+ buf.writeByte(',');
+ buf.print(charnum);
+ }
+ buf.writeByte(')');
+ break;
+ case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html
+ buf.writeByte(':');
+ buf.print(linnum);
+ if (showColumns && charnum)
+ {
+ buf.writeByte(':');
+ buf.print(charnum);
+ }
+ break;
+ }
+ }
+ return buf.extractChars();
+ }
+
+ /**
+ * Checks for equivalence by comparing the filename contents (not the pointer) and character location.
+ *
+ * Note:
+ * - Uses case-insensitive comparison on Windows
+ * - Ignores `charnum` if `global.params.showColumns` is false.
+ */
+ extern (C++) bool equals(ref const(Loc) loc) const
+ {
+ return (!global.params.showColumns || charnum == loc.charnum) &&
+ linnum == loc.linnum &&
+ FileName.equals(filename, loc.filename);
+ }
+
+ /**
+ * `opEquals()` / `toHash()` for AA key usage
+ *
+ * Compare filename contents (case-sensitively on Windows too), not
+ * the pointer - a static foreach loop repeatedly mixing in a mixin
+ * may lead to multiple equivalent filenames (`foo.d-mixin-<line>`),
+ * e.g., for test/runnable/test18880.d.
+ */
+ extern (D) bool opEquals(ref const(Loc) loc) const @trusted pure nothrow @nogc
+ {
+ import core.stdc.string : strcmp;
+
+ return charnum == loc.charnum &&
+ linnum == loc.linnum &&
+ (filename == loc.filename ||
+ (filename && loc.filename && strcmp(filename, loc.filename) == 0));
+ }
+
+ /// ditto
+ extern (D) size_t toHash() const @trusted pure nothrow
+ {
+ import dmd.root.string : toDString;
+
+ auto hash = hashOf(linnum);
+ hash = hashOf(charnum, hash);
+ hash = hashOf(filename.toDString, hash);
+ return hash;
+ }
+
+ /******************
+ * Returns:
+ * true if Loc has been set to other than the default initialization
+ */
+ bool isValid() const pure
+ {
+ return filename !is null;
+ }
+}
+
+/// A linkage attribute as defined by `extern(XXX)`
+///
+/// https://dlang.org/spec/attribute.html#linkage
+enum LINK : ubyte
+{
+ default_,
+ d,
+ c,
+ cpp,
+ windows,
+ objc,
+ system,
+}
+
+/// Whether to mangle an external aggregate as a struct or class, as set by `extern(C++, struct)`
+enum CPPMANGLE : ubyte
+{
+ def, /// default
+ asStruct, /// `extern(C++, struct)`
+ asClass, /// `extern(C++, class)`
+}
+
+/// Function match levels
+///
+/// https://dlang.org/spec/function.html#function-overloading
+enum MATCH : int
+{
+ nomatch, /// no match
+ convert, /// match with conversions
+ constant, /// match with conversion to const
+ exact, /// exact match
+}
+
+/// Inline setting as defined by `pragma(inline, XXX)`
+enum PINLINE : ubyte
+{
+ default_, /// as specified on the command line
+ never, /// never inline
+ always, /// always inline
+}
+
+alias StorageClass = uinteger_t;
+
+/// Collection of global state
+extern (C++) __gshared Global global;
diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h
index d9d59d685ca..6e794748bee 100644
--- a/gcc/d/dmd/globals.h
+++ b/gcc/d/dmd/globals.h
@@ -27,6 +27,13 @@ enum
DIAGNOSTICoff // disable diagnostic
};
+typedef unsigned char MessageStyle;
+enum
+{
+ MESSAGESTYLEdigitalmars, // file(line,column): message
+ MESSAGESTYLEgnu // file:line:column: message
+};
+
// The state of array bounds checking
typedef unsigned char CHECKENABLE;
enum
@@ -42,26 +49,17 @@ enum
{
CHECKACTION_D, // call D assert on failure
CHECKACTION_C, // call C assert on failure
- CHECKACTION_halt // cause program halt on failure
+ CHECKACTION_halt, // cause program halt on failure
+ CHECKACTION_context // call D assert with the error context on failure
};
-enum CPU
+enum JsonFieldFlags
{
- x87,
- mmx,
- sse,
- sse2,
- sse3,
- ssse3,
- sse4_1,
- sse4_2,
- avx, // AVX1 instruction set
- avx2, // AVX2 instruction set
- avx512, // AVX-512 instruction set
-
- // Special values that don't survive past the command line processing
- baseline, // (default) the minimum capability CPU
- native // the machine the compiler is being run on
+ none = 0,
+ compilerInfo = (1 << 0),
+ buildInfo = (1 << 1),
+ modules = (1 << 2),
+ semantics = (1 << 3)
};
enum CppStdRevision
@@ -69,7 +67,24 @@ enum CppStdRevision
CppStdRevisionCpp98 = 199711,
CppStdRevisionCpp11 = 201103,
CppStdRevisionCpp14 = 201402,
- CppStdRevisionCpp17 = 201703
+ CppStdRevisionCpp17 = 201703,
+ CppStdRevisionCpp20 = 202002
+};
+
+/// Configuration for the C++ header generator
+enum class CxxHeaderMode
+{
+ none, /// Don't generate headers
+ silent, /// Generate headers
+ verbose /// Generate headers and add comments for hidden declarations
+};
+
+/// Trivalent boolean to represent the state of a `revert`able change
+enum class FeatureState : signed char
+{
+ default_ = -1, /// Not specified by the user
+ disabled = 0, /// Specified as `-revert=`
+ enabled = 1 /// Specified as `-preview=`
};
// Put command line switches in here
@@ -87,50 +102,65 @@ struct Param
bool vcg_ast; // write-out codegen-ast
bool showColumns; // print character (column) numbers in diagnostics
bool vtls; // identify thread local variables
- char vgc; // identify gc usage
+ bool vtemplates; // collect and list statistics on template instantiations
+ bool vtemplatesListInstances; // collect and list statistics on template instantiations origins
+ bool vgc; // identify gc usage
bool vfield; // identify non-mutable field variables
bool vcomplex; // identify complex/imaginary type usage
- char symdebug; // insert debug symbolic information
+ unsigned char symdebug; // insert debug symbolic information
bool symdebugref; // insert debug information for all referenced types, too
- bool alwaysframe; // always emit standard stack frame
bool optimize; // run optimizer
- bool map; // generate linker .map file
- bool is64bit; // generate 64 bit code
- bool isLP64; // generate code for LP64
- bool isLinux; // generate code for linux
- bool isOSX; // generate code for Mac OSX
- bool isWindows; // generate code for Windows
- bool isFreeBSD; // generate code for FreeBSD
- bool isOpenBSD; // generate code for OpenBSD
- bool isSolaris; // generate code for Solaris
- bool hasObjectiveC; // target supports Objective-C
- bool mscoff; // for Win32: write COFF object files instead of OMF
Diagnostic useDeprecated;
bool stackstomp; // add stack stomping code
bool useUnitTests; // generate unittest code
bool useInline; // inline expand functions
- bool useDIP25; // implement http://wiki.dlang.org/DIP25
+ FeatureState useDIP25; // implement http://wiki.dlang.org/DIP25
+ FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params
+ bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md
bool release; // build release version
bool preservePaths; // true means don't strip path from source file
Diagnostic warnings;
- bool pic; // generate position-independent-code for shared libs
+ unsigned char pic; // generate position-independent-code for shared libs
bool color; // use ANSI colors in console output
bool cov; // generate code coverage data
unsigned char covPercent; // 0..100 code coverage percentage required
+ bool ctfe_cov; // generate coverage data for ctfe
bool nofloat; // code should not pull in floating point support
bool ignoreUnsupportedPragmas; // rather than error on them
- bool enforcePropertySyntax;
bool useModuleInfo; // generate runtime module information
bool useTypeInfo; // generate runtime type information
bool useExceptions; // support exception handling
+ bool noSharedAccess; // read/write access to shared memory objects
+ bool previewIn; // `in` means `scope const`, perhaps `ref`, accepts rvalues
+ bool shortenedMethods; // allow => in normal function declarations
bool betterC; // be a "better C" compiler; no dependency on D runtime
bool addMain; // add a default main() function
bool allInst; // generate code for all template instantiations
- bool vsafe; // use enhanced @safe checking
- unsigned cplusplus; // version of C++ name mangling to support
+ bool fix16997; // fix integral promotions for unary + - ~ operators
+ // https://issues.dlang.org/show_bug.cgi?id=16997
+ bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes
+ bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract
+ bool ehnogc; // use @nogc exception handling
+ FeatureState dtorFields; // destruct fields of partially constructed objects
+ // https://issues.dlang.org/show_bug.cgi?id=14246
+ bool fieldwise; // do struct equality testing field-wise rather than by memcmp()
+ bool rvalueRefParam; // allow rvalues to be arguments to ref parameters
+ CppStdRevision cplusplus; // version of C++ name mangling to support
+ bool markdown; // enable Markdown replacements in Ddoc
+ bool vmarkdown; // list instances of Markdown replacements in Ddoc
bool showGaggedErrors; // print gagged errors anyway
-
- CPU cpu; // CPU instruction set to target
+ bool printErrorContext; // print errors with the error context (the error line in the source file)
+ bool manual; // open browser on compiler manual
+ bool usage; // print usage and exit
+ bool mcpuUsage; // print help on -mcpu switch
+ bool transitionUsage; // print help on -transition switch
+ bool checkUsage; // print help on -check switch
+ bool checkActionUsage; // print help on -checkaction switch
+ bool revertUsage; // print help on -revert switch
+ bool previewUsage; // print help on -preview switch
+ bool externStdUsage; // print help on -extern-std switch
+ bool hcUsage; // print help on -HC switch
+ bool logo; // print logo;
CHECKENABLE useInvariants; // generate class invariant checks
CHECKENABLE useIn; // generate precondition checks
@@ -162,8 +192,17 @@ struct Param
DString hdrname; // write 'header' file to docname
bool hdrStripPlainFunctions; // strip the bodies of plain (non-template) functions
+ CxxHeaderMode doCxxHdrGeneration; // write 'Cxx header' file
+ DString cxxhdrdir; // write 'header' file to docdir directory
+ DString cxxhdrname; // write 'header' file to docname
+
bool doJsonGeneration; // write JSON file
DString jsonfilename; // write JSON file to jsonfilename
+ unsigned jsonFieldFlags; // JSON field flags to include
+
+ OutBuffer *mixinOut; // write expanded mixins for debugging
+ const char *mixinFile; // .mixin file output name
+ int mixinLines; // Number of lines in writeMixins
unsigned debuglevel; // debug level
Array<const char *> *debugids; // debug identifiers
@@ -178,13 +217,11 @@ struct Param
DString moduleDepsFile; // filename for deps output
OutBuffer *moduleDeps; // contents to be written to deps file
- // Hidden debug switches
- bool debugb;
- bool debugc;
- bool debugf;
- bool debugr;
- bool debugx;
- bool debugy;
+ bool emitMakeDeps; // whether to emit makedeps
+ DString makeDepsFile; // filename for makedeps output
+ Array<const char *> makeDeps; // dependencies for makedeps
+
+ MessageStyle messageStyle; // style of file/line annotations on messages
bool run; // run resulting executable
Strings runargs; // arguments for executable
@@ -192,6 +229,7 @@ struct Param
// Linker stuff
Array<const char *> objfiles;
Array<const char *> linkswitches;
+ Array<bool> linkswitchIsForCC;
Array<const char *> libfiles;
Array<const char *> dllfiles;
DString deffile;
@@ -205,36 +243,30 @@ typedef unsigned structalign_t;
// other values are all powers of 2
#define STRUCTALIGN_DEFAULT ((structalign_t) ~0)
+const DString mars_ext = "d";
+const DString doc_ext = "html"; // for Ddoc generated files
+const DString ddoc_ext = "ddoc"; // for Ddoc macro include files
+const DString dd_ext = "dd"; // for Ddoc source files
+const DString hdr_ext = "di"; // for D 'header' import files
+const DString json_ext = "json"; // for JSON files
+const DString map_ext = "map"; // for .map files
+
struct Global
{
DString inifilename;
- DString mars_ext;
- DString obj_ext;
- DString lib_ext;
- DString dll_ext;
- DString doc_ext; // for Ddoc generated files
- DString ddoc_ext; // for Ddoc macro include files
- DString hdr_ext; // for D 'header' import files
- DString cxxhdr_ext; // for C/C++ 'header' files
- DString json_ext; // for JSON files
- DString map_ext; // for .map files
- bool run_noext; // allow -run sources without extensions.
-
- DString copyright;
- DString written;
- const char *main_d; // dummy filename for dummy main()
+
+ const DString copyright;
+ const DString written;
Array<const char *> *path; // Array of char*'s which form the import lookup path
Array<const char *> *filePath; // Array of char*'s which form the file import lookup path
- DString version; // Compiler version string
DString vendor; // Compiler backend name
Param params;
- unsigned errors; // number of errors reported so far
- unsigned warnings; // number of warnings reported so far
- FILE *stdmsg; // where to send verbose messages
- unsigned gag; // !=0 means gag reporting of errors & warnings
- unsigned gaggedErrors; // number of errors reported while gagged
+ unsigned errors; // number of errors reported so far
+ unsigned warnings; // number of warnings reported so far
+ unsigned gag; // !=0 means gag reporting of errors & warnings
+ unsigned gaggedErrors; // number of errors reported while gagged
unsigned gaggedWarnings; // number of warnings reported while gagged
void* console; // opaque pointer to console for controlling text attributes
@@ -242,8 +274,6 @@ struct Global
Array<class Identifier*>* versionids; // command line versions and predefined versions
Array<class Identifier*>* debugids; // command line debug versions and predefined versions
- enum { recursionLimit = 500 }; // number of recursive template expansions before abort
-
/* Start gagging. Return the current number of gagged errors
*/
unsigned startGagging();
@@ -260,17 +290,43 @@ struct Global
void increaseErrorCount();
void _init();
+
+ /**
+ Returns: the version as the number that would be returned for __VERSION__
+ */
+ unsigned versionNumber();
+
+ /**
+ Returns: the compiler version string.
+ */
+ const char * versionChars();
};
extern Global global;
+// Because int64_t and friends may be any integral type of the correct size,
+// we have to explicitly ask for the correct integer type to get the correct
+// mangling with dmd. The #if logic here should match the mangling of
+// Tint64 and Tuns64 in cppmangle.d.
+#if MARS && DMD_VERSION >= 2079 && DMD_VERSION <= 2081 && \
+ __APPLE__ && __SIZEOF_LONG__ == 8
+// DMD versions between 2.079 and 2.081 mapped D long to int64_t on OS X.
+typedef uint64_t dinteger_t;
+typedef int64_t sinteger_t;
+typedef uint64_t uinteger_t;
+#elif __SIZEOF_LONG__ == 8
// Be careful not to care about sign when using dinteger_t
// use this instead of integer_t to
// avoid conflicts with system #include's
-typedef uint64_t dinteger_t;
+typedef unsigned long dinteger_t;
// Signed and unsigned variants
-typedef int64_t sinteger_t;
-typedef uint64_t uinteger_t;
+typedef long sinteger_t;
+typedef unsigned long uinteger_t;
+#else
+typedef unsigned long long dinteger_t;
+typedef long long sinteger_t;
+typedef unsigned long long uinteger_t;
+#endif
typedef int8_t d_int8;
typedef uint8_t d_uns8;
@@ -295,43 +351,50 @@ struct Loc
filename = NULL;
}
- Loc(const char *filename, unsigned linnum, unsigned charnum);
+ Loc(const char *filename, unsigned linnum, unsigned charnum)
+ {
+ this->linnum = linnum;
+ this->charnum = charnum;
+ this->filename = filename;
+ }
- const char *toChars() const;
- bool equals(const Loc& loc);
+ const char *toChars(
+ bool showColumns = global.params.showColumns,
+ MessageStyle messageStyle = global.params.messageStyle) const;
+ bool equals(const Loc& loc) const;
};
-enum LINK
+enum class LINK : uint8_t
{
- LINKdefault,
- LINKd,
- LINKc,
- LINKcpp,
- LINKwindows,
- LINKobjc,
- LINKsystem
+ default_,
+ d,
+ c,
+ cpp,
+ windows,
+ objc,
+ system
};
-enum CPPMANGLE
+enum class CPPMANGLE : uint8_t
{
- CPPMANGLEdefault,
- CPPMANGLEstruct,
- CPPMANGLEclass
+ def,
+ asStruct,
+ asClass
};
-enum MATCH
+enum class MATCH : int
{
- MATCHnomatch, // no match
- MATCHconvert, // match with conversions
- MATCHconst, // match with conversion to const
- MATCHexact // exact match
+ nomatch, // no match
+ convert, // match with conversions
+ constant, // match with conversion to const
+ exact // exact match
};
-enum PINLINE
+enum class PINLINE : uint8_t
{
- PINLINEdefault, // as specified on the command line
- PINLINEnever, // never inline
- PINLINEalways // always inline
+ default_, // as specified on the command line
+ never, // never inline
+ always // always inline
};
typedef uinteger_t StorageClass;
diff --git a/gcc/d/dmd/gluelayer.d b/gcc/d/dmd/gluelayer.d
new file mode 100644
index 00000000000..debb9ca62d4
--- /dev/null
+++ b/gcc/d/dmd/gluelayer.d
@@ -0,0 +1,90 @@
+/**
+ * Declarations for back-end functions that the front-end invokes.
+ *
+ * This 'glues' either the DMC or GCC back-end to the front-end.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/gluelayer.d, _gluelayer.d)
+ * Documentation: https://dlang.org/phobos/dmd_gluelayer.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/gluelayer.d
+ */
+
+module dmd.gluelayer;
+
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.mtype;
+import dmd.statement;
+import dmd.root.file;
+
+version (NoBackend)
+{
+ struct Symbol;
+ struct code;
+ struct block;
+ struct Blockx;
+ struct elem;
+ struct TYPE;
+ alias type = TYPE;
+
+ extern (C++)
+ {
+ // iasm
+ Statement asmSemantic(AsmStatement s, Scope* sc)
+ {
+ sc.func.hasReturnExp = 8;
+ return null;
+ }
+
+ // toir
+ void toObjFile(Dsymbol ds, bool multiobj) {}
+
+ extern(C++) abstract class ObjcGlue
+ {
+ static void initialize() {}
+ }
+ }
+}
+else version (MARS)
+{
+ public import dmd.backend.cc : block, Blockx, Symbol;
+ public import dmd.backend.type : type;
+ public import dmd.backend.el : elem;
+ public import dmd.backend.code_x86 : code;
+
+ extern (C++)
+ {
+ Statement asmSemantic(AsmStatement s, Scope* sc);
+
+ void toObjFile(Dsymbol ds, bool multiobj);
+
+ extern(C++) abstract class ObjcGlue
+ {
+ static void initialize();
+ }
+ }
+}
+else version (IN_GCC)
+{
+ extern (C++) union tree_node;
+
+ alias Symbol = tree_node;
+ alias code = tree_node;
+ alias type = tree_node;
+
+ extern (C++)
+ {
+ Statement asmSemantic(AsmStatement s, Scope* sc);
+ }
+
+ // stubs
+ extern(C++) abstract class ObjcGlue
+ {
+ static void initialize() {}
+ }
+}
+else
+ static assert(false, "Unsupported compiler backend");
diff --git a/gcc/d/dmd/hdrgen.c b/gcc/d/dmd/hdrgen.c
deleted file mode 100644
index 9397b1e8abd..00000000000
--- a/gcc/d/dmd/hdrgen.c
+++ /dev/null
@@ -1,3591 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Dave Fladebo
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/hdrgen.c
- */
-
-// Routines to emit header files
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "id.h"
-#include "init.h"
-
-#include "attrib.h"
-#include "cond.h"
-#include "doc.h"
-#include "enum.h"
-#include "import.h"
-#include "module.h"
-#include "mtype.h"
-#include "parse.h"
-#include "scope.h"
-#include "staticassert.h"
-#include "target.h"
-#include "template.h"
-#include "utf.h"
-#include "version.h"
-
-#include "declaration.h"
-#include "aggregate.h"
-#include "expression.h"
-#include "ctfe.h"
-#include "statement.h"
-#include "aliasthis.h"
-#include "nspace.h"
-#include "hdrgen.h"
-
-void linkageToBuffer(OutBuffer *buf, LINK linkage);
-void MODtoBuffer(OutBuffer *buf, MOD mod);
-
-void genhdrfile(Module *m)
-{
- OutBuffer buf;
- buf.doindent = 1;
-
- buf.printf("// D import file generated from '%s'", m->srcfile->toChars());
- buf.writenl();
-
- HdrGenState hgs;
- hgs.hdrgen = true;
-
- toCBuffer(m, &buf, &hgs);
-
- // Transfer image to file
- m->hdrfile->setbuffer(buf.slice().ptr, buf.length());
- buf.extractData();
-
- ensurePathToNameExists(Loc(), m->hdrfile->toChars());
- writeFile(m->loc, m->hdrfile);
-}
-
-/**
- * Dumps the full contents of module `m` to `buf`.
- * Params:
- * buf = buffer to write to.
- * m = module to visit all members of.
- */
-void moduleToBuffer(OutBuffer *buf, Module *m)
-{
- HdrGenState hgs;
- hgs.fullDump = true;
- toCBuffer(m, buf, &hgs);
-}
-
-class PrettyPrintVisitor : public Visitor
-{
-public:
- OutBuffer *buf;
- HdrGenState *hgs;
- bool declstring; // set while declaring alias for string,wstring or dstring
- EnumDeclaration *inEnumDecl;
-
- PrettyPrintVisitor(OutBuffer *buf, HdrGenState *hgs)
- : buf(buf), hgs(hgs), declstring(false), inEnumDecl(NULL)
- {
- }
-
- void visit(Statement *)
- {
- buf->printf("Statement::toCBuffer()");
- buf->writenl();
- assert(0);
- }
-
- void visit(ErrorStatement *)
- {
- buf->printf("__error__");
- buf->writenl();
- }
-
- void visit(ExpStatement *s)
- {
- if (s->exp && s->exp->op == TOKdeclaration)
- {
- // bypass visit(DeclarationExp)
- ((DeclarationExp *)s->exp)->declaration->accept(this);
- return;
- }
- if (s->exp)
- s->exp->accept(this);
- buf->writeByte(';');
- if (!hgs->forStmtInit)
- buf->writenl();
- }
-
- void visit(CompileStatement *s)
- {
- buf->writestring("mixin(");
- argsToBuffer(s->exps);
- buf->writestring(");");
- if (!hgs->forStmtInit)
- buf->writenl();
- }
-
- void visit(CompoundStatement *s)
- {
- for (size_t i = 0; i < s->statements->length; i++)
- {
- Statement *sx = (*s->statements)[i];
- if (sx)
- sx->accept(this);
- }
- }
-
- void visit(CompoundDeclarationStatement *s)
- {
- bool anywritten = false;
- for (size_t i = 0; i < s->statements->length; i++)
- {
- Statement *sx = (*s->statements)[i];
- ExpStatement *ds = sx ? sx->isExpStatement() : NULL;
- if (ds && ds->exp->op == TOKdeclaration)
- {
- Dsymbol *d = ((DeclarationExp *)ds->exp)->declaration;
- assert(d->isDeclaration());
- if (VarDeclaration *v = d->isVarDeclaration())
- visitVarDecl(v, anywritten);
- else
- d->accept(this);
- anywritten = true;
- }
- }
- buf->writeByte(';');
- if (!hgs->forStmtInit)
- buf->writenl();
- }
-
- void visit(UnrolledLoopStatement *s)
- {
- buf->writestring("unrolled {");
- buf->writenl();
- buf->level++;
-
- for (size_t i = 0; i < s->statements->length; i++)
- {
- Statement *sx = (*s->statements)[i];
- if (sx)
- sx->accept(this);
- }
-
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
-
- void visit(ScopeStatement *s)
- {
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
-
- if (s->statement)
- s->statement->accept(this);
-
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
-
- void visit(WhileStatement *s)
- {
- buf->writestring("while (");
- s->condition->accept(this);
- buf->writeByte(')');
- buf->writenl();
- if (s->_body)
- s->_body->accept(this);
- }
-
- void visit(DoStatement *s)
- {
- buf->writestring("do");
- buf->writenl();
- if (s->_body)
- s->_body->accept(this);
- buf->writestring("while (");
- s->condition->accept(this);
- buf->writestring(");");
- buf->writenl();
- }
-
- void visit(ForStatement *s)
- {
- buf->writestring("for (");
- if (s->_init)
- {
- hgs->forStmtInit++;
- s->_init->accept(this);
- hgs->forStmtInit--;
- }
- else
- buf->writeByte(';');
- if (s->condition)
- {
- buf->writeByte(' ');
- s->condition->accept(this);
- }
- buf->writeByte(';');
- if (s->increment)
- {
- buf->writeByte(' ');
- s->increment->accept(this);
- }
- buf->writeByte(')');
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- if (s->_body)
- s->_body->accept(this);
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
-
- void foreachWithoutBody(ForeachStatement *s)
- {
- buf->writestring(Token::toChars(s->op));
- buf->writestring(" (");
- for (size_t i = 0; i < s->parameters->length; i++)
- {
- Parameter *p = (*s->parameters)[i];
- if (i)
- buf->writestring(", ");
- if (stcToBuffer(buf, p->storageClass))
- buf->writeByte(' ');
- if (p->type)
- typeToBuffer(p->type, p->ident);
- else
- buf->writestring(p->ident->toChars());
- }
- buf->writestring("; ");
- s->aggr->accept(this);
- buf->writeByte(')');
- buf->writenl();
- }
-
- void visit(ForeachStatement *s)
- {
- foreachWithoutBody(s);
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- if (s->_body)
- s->_body->accept(this);
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
-
- void foreachRangeWithoutBody(ForeachRangeStatement *s)
- {
- buf->writestring(Token::toChars(s->op));
- buf->writestring(" (");
-
- if (s->prm->type)
- typeToBuffer(s->prm->type, s->prm->ident);
- else
- buf->writestring(s->prm->ident->toChars());
-
- buf->writestring("; ");
- s->lwr->accept(this);
- buf->writestring(" .. ");
- s->upr->accept(this);
- buf->writeByte(')');
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- }
-
- void visit(ForeachRangeStatement *s)
- {
- foreachRangeWithoutBody(s);
- buf->level++;
- if (s->_body)
- s->_body->accept(this);
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
-
- void visit(StaticForeachStatement *s)
- {
- buf->writestring("static ");
- if (s->sfe->aggrfe)
- {
- visit(s->sfe->aggrfe);
- }
- else
- {
- assert(s->sfe->rangefe);
- visit(s->sfe->rangefe);
- }
- }
-
- void visit(IfStatement *s)
- {
- buf->writestring("if (");
- if (Parameter *p = s->prm)
- {
- StorageClass stc = p->storageClass;
- if (!p->type && !stc)
- stc = STCauto;
- if (stcToBuffer(buf, stc))
- buf->writeByte(' ');
- if (p->type)
- typeToBuffer(p->type, p->ident);
- else
- buf->writestring(p->ident->toChars());
- buf->writestring(" = ");
- }
- s->condition->accept(this);
- buf->writeByte(')');
- buf->writenl();
- if (s->ifbody->isScopeStatement())
- {
- s->ifbody->accept(this);
- }
- else
- {
- buf->level++;
- s->ifbody->accept(this);
- buf->level--;
- }
- if (s->elsebody)
- {
- buf->writestring("else");
- if (!s->elsebody->isIfStatement())
- {
- buf->writenl();
- }
- else
- {
- buf->writeByte(' ');
- }
- if (s->elsebody->isScopeStatement() || s->elsebody->isIfStatement())
- {
- s->elsebody->accept(this);
- }
- else
- {
- buf->level++;
- s->elsebody->accept(this);
- buf->level--;
- }
- }
- }
-
- void visit(ConditionalStatement *s)
- {
- s->condition->accept(this);
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- if (s->ifbody)
- s->ifbody->accept(this);
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- if (s->elsebody)
- {
- buf->writestring("else");
- buf->writenl();
- buf->writeByte('{');
- buf->level++;
- buf->writenl();
- s->elsebody->accept(this);
- buf->level--;
- buf->writeByte('}');
- }
- buf->writenl();
- }
-
- void visit(PragmaStatement *s)
- {
- buf->writestring("pragma (");
- buf->writestring(s->ident->toChars());
- if (s->args && s->args->length)
- {
- buf->writestring(", ");
- argsToBuffer(s->args);
- }
- buf->writeByte(')');
- if (s->_body)
- {
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
-
- s->_body->accept(this);
-
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
- else
- {
- buf->writeByte(';');
- buf->writenl();
- }
- }
-
- void visit(StaticAssertStatement *s)
- {
- s->sa->accept(this);
- }
-
- void visit(SwitchStatement *s)
- {
- buf->writestring(s->isFinal ? "final switch (" : "switch (");
- s->condition->accept(this);
- buf->writeByte(')');
- buf->writenl();
- if (s->_body)
- {
- if (!s->_body->isScopeStatement())
- {
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- s->_body->accept(this);
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
- else
- {
- s->_body->accept(this);
- }
- }
- }
-
- void visit(CaseStatement *s)
- {
- buf->writestring("case ");
- s->exp->accept(this);
- buf->writeByte(':');
- buf->writenl();
- s->statement->accept(this);
- }
-
- void visit(CaseRangeStatement *s)
- {
- buf->writestring("case ");
- s->first->accept(this);
- buf->writestring(": .. case ");
- s->last->accept(this);
- buf->writeByte(':');
- buf->writenl();
- s->statement->accept(this);
- }
-
- void visit(DefaultStatement *s)
- {
- buf->writestring("default:");
- buf->writenl();
- s->statement->accept(this);
- }
-
- void visit(GotoDefaultStatement *)
- {
- buf->writestring("goto default;");
- buf->writenl();
- }
-
- void visit(GotoCaseStatement *s)
- {
- buf->writestring("goto case");
- if (s->exp)
- {
- buf->writeByte(' ');
- s->exp->accept(this);
- }
- buf->writeByte(';');
- buf->writenl();
- }
-
- void visit(SwitchErrorStatement *)
- {
- buf->writestring("SwitchErrorStatement::toCBuffer()");
- buf->writenl();
- }
-
- void visit(ReturnStatement *s)
- {
- buf->printf("return ");
- if (s->exp)
- s->exp->accept(this);
- buf->writeByte(';');
- buf->writenl();
- }
-
- void visit(BreakStatement *s)
- {
- buf->writestring("break");
- if (s->ident)
- {
- buf->writeByte(' ');
- buf->writestring(s->ident->toChars());
- }
- buf->writeByte(';');
- buf->writenl();
- }
-
- void visit(ContinueStatement *s)
- {
- buf->writestring("continue");
- if (s->ident)
- {
- buf->writeByte(' ');
- buf->writestring(s->ident->toChars());
- }
- buf->writeByte(';');
- buf->writenl();
- }
-
- void visit(SynchronizedStatement *s)
- {
- buf->writestring("synchronized");
- if (s->exp)
- {
- buf->writeByte('(');
- s->exp->accept(this);
- buf->writeByte(')');
- }
- if (s->_body)
- {
- buf->writeByte(' ');
- s->_body->accept(this);
- }
- }
-
- void visit(WithStatement *s)
- {
- buf->writestring("with (");
- s->exp->accept(this);
- buf->writestring(")");
- buf->writenl();
- if (s->_body)
- s->_body->accept(this);
- }
-
- void visit(TryCatchStatement *s)
- {
- buf->writestring("try");
- buf->writenl();
- if (s->_body)
- s->_body->accept(this);
- for (size_t i = 0; i < s->catches->length; i++)
- {
- Catch *c = (*s->catches)[i];
- visit(c);
- }
- }
-
- void visit(TryFinallyStatement *s)
- {
- buf->writestring("try");
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- s->_body->accept(this);
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- buf->writestring("finally");
- buf->writenl();
- if (s->finalbody->isScopeStatement())
- {
- s->finalbody->accept(this);
- }
- else
- {
- buf->level++;
- s->finalbody->accept(this);
- buf->level--;
- }
- buf->writeByte('}');
- buf->writenl();
- }
-
- void visit(ScopeGuardStatement *s)
- {
- buf->writestring(Token::toChars(s->tok));
- buf->writeByte(' ');
- s->statement->accept(this);
- }
-
- void visit(ThrowStatement *s)
- {
- buf->printf("throw ");
- s->exp->accept(this);
- buf->writeByte(';');
- buf->writenl();
- }
-
- void visit(DebugStatement *s)
- {
- if (s->statement)
- {
- s->statement->accept(this);
- }
- }
-
- void visit(GotoStatement *s)
- {
- buf->writestring("goto ");
- buf->writestring(s->ident->toChars());
- buf->writeByte(';');
- buf->writenl();
- }
-
- void visit(LabelStatement *s)
- {
- buf->writestring(s->ident->toChars());
- buf->writeByte(':');
- buf->writenl();
- if (s->statement)
- s->statement->accept(this);
- }
-
- void visit(AsmStatement *s)
- {
- buf->writestring("asm { ");
- Token *t = s->tokens;
- buf->level++;
- while (t)
- {
- buf->writestring(t->toChars());
- if (t->next &&
- t->value != TOKmin &&
- t->value != TOKcomma && t->next->value != TOKcomma &&
- t->value != TOKlbracket && t->next->value != TOKlbracket &&
- t->next->value != TOKrbracket &&
- t->value != TOKlparen && t->next->value != TOKlparen &&
- t->next->value != TOKrparen &&
- t->value != TOKdot && t->next->value != TOKdot)
- {
- buf->writeByte(' ');
- }
- t = t->next;
- }
- buf->level--;
- buf->writestring("; }");
- buf->writenl();
- }
-
- void visit(ImportStatement *s)
- {
- for (size_t i = 0; i < s->imports->length; i++)
- {
- Dsymbol *imp = (*s->imports)[i];
- imp->accept(this);
- }
- }
-
- void visit(Catch *c)
- {
- buf->writestring("catch");
- if (c->type)
- {
- buf->writeByte('(');
- typeToBuffer(c->type, c->ident);
- buf->writeByte(')');
- }
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- if (c->handler)
- c->handler->accept(this);
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- /**************************************************
- * An entry point to pretty-print type.
- */
- void typeToBuffer(Type *t, Identifier *ident)
- {
- if (t->ty == Tfunction)
- {
- visitFuncIdentWithPrefix((TypeFunction *)t, ident, NULL);
- return;
- }
-
- visitWithMask(t, 0);
-
- if (ident)
- {
- buf->writeByte(' ');
- buf->writestring(ident->toChars());
- }
- }
-
- void visitWithMask(Type *t, unsigned char modMask)
- {
- // Tuples and functions don't use the type constructor syntax
- if (modMask == t->mod ||
- t->ty == Tfunction ||
- t->ty == Ttuple)
- {
- t->accept(this);
- }
- else
- {
- unsigned char m = t->mod & ~(t->mod & modMask);
- if (m & MODshared)
- {
- MODtoBuffer(buf, MODshared);
- buf->writeByte('(');
- }
- if (m & MODwild)
- {
- MODtoBuffer(buf, MODwild);
- buf->writeByte('(');
- }
- if (m & (MODconst | MODimmutable))
- {
- MODtoBuffer(buf, m & (MODconst | MODimmutable));
- buf->writeByte('(');
- }
-
- t->accept(this);
-
- if (m & (MODconst | MODimmutable))
- buf->writeByte(')');
- if (m & MODwild)
- buf->writeByte(')');
- if (m & MODshared)
- buf->writeByte(')');
- }
- }
-
- void visit(Type *t)
- {
- printf("t = %p, ty = %d\n", t, t->ty);
- assert(0);
- }
-
- void visit(TypeError *)
- {
- buf->writestring("_error_");
- }
-
- void visit(TypeBasic *t)
- {
- //printf("TypeBasic::toCBuffer2(t->mod = %d)\n", t->mod);
- buf->writestring(t->dstring);
- }
-
- void visit(TypeTraits *t)
- {
- //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
- t->exp->accept(this);
- }
-
- void visit(TypeVector *t)
- {
- //printf("TypeVector::toCBuffer2(t->mod = %d)\n", t->mod);
- buf->writestring("__vector(");
- visitWithMask(t->basetype, t->mod);
- buf->writestring(")");
- }
-
- void visit(TypeSArray *t)
- {
- visitWithMask(t->next, t->mod);
- buf->writeByte('[');
- sizeToBuffer(t->dim);
- buf->writeByte(']');
- }
-
- void visit(TypeDArray *t)
- {
- Type *ut = t->castMod(0);
- if (declstring)
- goto L1;
- if (ut->equals(Type::tstring))
- buf->writestring("string");
- else if (ut->equals(Type::twstring))
- buf->writestring("wstring");
- else if (ut->equals(Type::tdstring))
- buf->writestring("dstring");
- else
- {
- L1:
- visitWithMask(t->next, t->mod);
- buf->writestring("[]");
- }
- }
-
- void visit(TypeAArray *t)
- {
- visitWithMask(t->next, t->mod);
- buf->writeByte('[');
- visitWithMask(t->index, 0);
- buf->writeByte(']');
- }
-
- void visit(TypePointer *t)
- {
- //printf("TypePointer::toCBuffer2() next = %d\n", t->next->ty);
- if (t->next->ty == Tfunction)
- visitFuncIdentWithPostfix((TypeFunction *)t->next, "function");
- else
- {
- visitWithMask(t->next, t->mod);
- buf->writeByte('*');
- }
- }
-
- void visit(TypeReference *t)
- {
- visitWithMask(t->next, t->mod);
- buf->writeByte('&');
- }
-
- void visit(TypeFunction *t)
- {
- //printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t->isref);
- visitFuncIdentWithPostfix(t, NULL);
- }
-
- // callback for TypeFunction::attributesApply
- struct PrePostAppendStrings
- {
- OutBuffer *buf;
- bool isPostfixStyle;
- bool isCtor;
-
- static int fp(void *param, const char *str)
- {
- PrePostAppendStrings *p = (PrePostAppendStrings *)param;
-
- // don't write 'ref' for ctors
- if (p->isCtor && strcmp(str, "ref") == 0)
- return 0;
-
- if ( p->isPostfixStyle) p->buf->writeByte(' ');
- p->buf->writestring(str);
- if (!p->isPostfixStyle) p->buf->writeByte(' ');
- return 0;
- }
- };
-
- void visitFuncIdentWithPostfix(TypeFunction *t, const char *ident)
- {
- if (t->inuse)
- {
- t->inuse = 2; // flag error to caller
- return;
- }
- t->inuse++;
-
- PrePostAppendStrings pas;
- pas.buf = buf;
- pas.isCtor = false;
- pas.isPostfixStyle = true;
-
- if (t->linkage > LINKd && hgs->ddoc != 1 && !hgs->hdrgen)
- {
- linkageToBuffer(buf, t->linkage);
- buf->writeByte(' ');
- }
-
- if (t->next)
- {
- typeToBuffer(t->next, NULL);
- if (ident)
- buf->writeByte(' ');
- }
- else if (hgs->ddoc)
- buf->writestring("auto ");
-
- if (ident)
- buf->writestring(ident);
-
- parametersToBuffer(t->parameterList.parameters, t->parameterList.varargs);
-
- /* Use postfix style for attributes
- */
- if (t->mod)
- {
- buf->writeByte(' ');
- MODtoBuffer(buf, t->mod);
- }
- t->attributesApply(&pas, &PrePostAppendStrings::fp);
-
- t->inuse--;
- }
- void visitFuncIdentWithPrefix(TypeFunction *t, Identifier *ident, TemplateDeclaration *td)
- {
- if (t->inuse)
- {
- t->inuse = 2; // flag error to caller
- return;
- }
- t->inuse++;
-
- PrePostAppendStrings pas;
- pas.buf = buf;
- pas.isCtor = (ident == Id::ctor);
- pas.isPostfixStyle = false;
-
- /* Use 'storage class' (prefix) style for attributes
- */
- if (t->mod)
- {
- MODtoBuffer(buf, t->mod);
- buf->writeByte(' ');
- }
- t->attributesApply(&pas, &PrePostAppendStrings::fp);
-
- if (t->linkage > LINKd && hgs->ddoc != 1 && !hgs->hdrgen)
- {
- linkageToBuffer(buf, t->linkage);
- buf->writeByte(' ');
- }
-
- if (ident && ident->toHChars2() != ident->toChars())
- {
- // Don't print return type for ctor, dtor, unittest, etc
- }
- else if (t->next)
- {
- typeToBuffer(t->next, NULL);
- if (ident)
- buf->writeByte(' ');
- }
- else if (hgs->ddoc)
- buf->writestring("auto ");
-
- if (ident)
- buf->writestring(ident->toHChars2());
-
- if (td)
- {
- buf->writeByte('(');
- for (size_t i = 0; i < td->origParameters->length; i++)
- {
- TemplateParameter *p = (*td->origParameters)[i];
- if (i)
- buf->writestring(", ");
- p->accept(this);
- }
- buf->writeByte(')');
- }
- parametersToBuffer(t->parameterList.parameters, t->parameterList.varargs);
-
- t->inuse--;
- }
-
- void visit(TypeDelegate *t)
- {
- visitFuncIdentWithPostfix((TypeFunction *)t->next, "delegate");
- }
-
- void visitTypeQualifiedHelper(TypeQualified *t)
- {
- for (size_t i = 0; i < t->idents.length; i++)
- {
- RootObject *id = t->idents[i];
-
- if (id->dyncast() == DYNCAST_DSYMBOL)
- {
- buf->writeByte('.');
- TemplateInstance *ti = (TemplateInstance *)id;
- ti->accept(this);
- }
- else if (id->dyncast() == DYNCAST_EXPRESSION)
- {
- buf->writeByte('[');
- ((Expression *)id)->accept(this);
- buf->writeByte(']');
- }
- else if (id->dyncast() == DYNCAST_TYPE)
- {
- buf->writeByte('[');
- ((Type *)id)->accept(this);
- buf->writeByte(']');
- }
- else
- {
- buf->writeByte('.');
- buf->writestring(id->toChars());
- }
- }
- }
-
- void visit(TypeIdentifier *t)
- {
- buf->writestring(t->ident->toChars());
- visitTypeQualifiedHelper(t);
- }
-
- void visit(TypeInstance *t)
- {
- t->tempinst->accept(this);
- visitTypeQualifiedHelper(t);
- }
-
- void visit(TypeTypeof *t)
- {
- buf->writestring("typeof(");
- t->exp->accept(this);
- buf->writeByte(')');
- visitTypeQualifiedHelper(t);
- }
-
- void visit(TypeReturn *t)
- {
- buf->writestring("typeof(return)");
- visitTypeQualifiedHelper(t);
- }
-
- void visit(TypeEnum *t)
- {
- buf->writestring(t->sym->toChars());
- }
-
- void visit(TypeStruct *t)
- {
- // Bugzilla 13776: Don't use ti->toAlias() to avoid forward reference error
- // while printing messages.
- TemplateInstance *ti = t->sym->parent ? t->sym->parent->isTemplateInstance() : NULL;
- if (ti && ti->aliasdecl == t->sym)
- buf->writestring(hgs->fullQual ? ti->toPrettyChars() : ti->toChars());
- else
- buf->writestring(hgs->fullQual ? t->sym->toPrettyChars() : t->sym->toChars());
- }
-
- void visit(TypeClass *t)
- {
- // Bugzilla 13776: Don't use ti->toAlias() to avoid forward reference error
- // while printing messages.
- TemplateInstance *ti = t->sym->parent->isTemplateInstance();
- if (ti && ti->aliasdecl == t->sym)
- buf->writestring(hgs->fullQual ? ti->toPrettyChars() : ti->toChars());
- else
- buf->writestring(hgs->fullQual ? t->sym->toPrettyChars() : t->sym->toChars());
- }
-
- void visit(TypeTuple *t)
- {
- parametersToBuffer(t->arguments, 0);
- }
-
- void visit(TypeSlice *t)
- {
- visitWithMask(t->next, t->mod);
-
- buf->writeByte('[');
- sizeToBuffer(t->lwr);
- buf->writestring(" .. ");
- sizeToBuffer(t->upr);
- buf->writeByte(']');
- }
-
- void visit(TypeNull *)
- {
- buf->writestring("typeof(null)");
- }
-
- void visit(TypeMixin *t)
- {
- buf->writestring("mixin(");
- argsToBuffer(t->exps);
- buf->writeByte(')');
- }
-
- void visit(TypeNoreturn *)
- {
- buf->writestring("noreturn");
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- void visit(Dsymbol *s)
- {
- buf->writestring(s->toChars());
- }
-
- void visit(StaticAssert *s)
- {
- buf->writestring(s->kind());
- buf->writeByte('(');
- s->exp->accept(this);
- if (s->msg)
- {
- buf->writestring(", ");
- s->msg->accept(this);
- }
- buf->writestring(");");
- buf->writenl();
- }
-
- void visit(DebugSymbol *s)
- {
- buf->writestring("debug = ");
- if (s->ident)
- buf->writestring(s->ident->toChars());
- else
- buf->printf("%u", s->level);
- buf->writestring(";");
- buf->writenl();
- }
-
- void visit(VersionSymbol *s)
- {
- buf->writestring("version = ");
- if (s->ident)
- buf->writestring(s->ident->toChars());
- else
- buf->printf("%u", s->level);
- buf->writestring(";");
- buf->writenl();
- }
-
- void visit(EnumMember *em)
- {
- if (em->type)
- typeToBuffer(em->type, em->ident);
- else
- buf->writestring(em->ident->toChars());
- if (em->value())
- {
- buf->writestring(" = ");
- em->value()->accept(this);
- }
- }
-
- void visit(Import *imp)
- {
- if (hgs->hdrgen && imp->id == Id::object)
- return; // object is imported by default
-
- if (imp->isstatic)
- buf->writestring("static ");
- buf->writestring("import ");
- if (imp->aliasId)
- {
- buf->printf("%s = ", imp->aliasId->toChars());
- }
- if (imp->packages && imp->packages->length)
- {
- for (size_t i = 0; i < imp->packages->length; i++)
- {
- Identifier *pid = (*imp->packages)[i];
- buf->printf("%s.", pid->toChars());
- }
- }
- buf->printf("%s", imp->id->toChars());
- if (imp->names.length)
- {
- buf->writestring(" : ");
- for (size_t i = 0; i < imp->names.length; i++)
- {
- if (i)
- buf->writestring(", ");
-
- Identifier *name = imp->names[i];
- Identifier *alias = imp->aliases[i];
- if (alias)
- buf->printf("%s = %s", alias->toChars(), name->toChars());
- else
- buf->printf("%s", name->toChars());
- }
- }
- buf->printf(";");
- buf->writenl();
- }
-
- void visit(AliasThis *d)
- {
- buf->writestring("alias ");
- buf->writestring(d->ident->toChars());
- buf->writestring(" this;\n");
- }
-
- void visit(AttribDeclaration *d)
- {
- if (!d->decl)
- {
- buf->writeByte(';');
- buf->writenl();
- return;
- }
-
- if (d->decl->length == 0)
- buf->writestring("{}");
- else if (hgs->hdrgen && d->decl->length == 1 && (*d->decl)[0]->isUnitTestDeclaration())
- {
- // hack for bugzilla 8081
- buf->writestring("{}");
- }
- else if (d->decl->length == 1)
- {
- ((*d->decl)[0])->accept(this);
- return;
- }
- else
- {
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- for (size_t i = 0; i < d->decl->length; i++)
- {
- Dsymbol *de = (*d->decl)[i];
- de->accept(this);
- }
- buf->level--;
- buf->writeByte('}');
- }
- buf->writenl();
- }
-
- void visit(StorageClassDeclaration *d)
- {
- if (stcToBuffer(buf, d->stc))
- buf->writeByte(' ');
- visit((AttribDeclaration *)d);
- }
-
- void visit(DeprecatedDeclaration *d)
- {
- buf->writestring("deprecated(");
- d->msg->accept(this);
- buf->writestring(") ");
- visit((AttribDeclaration *)d);
- }
-
- void visit(LinkDeclaration *d)
- {
- const char *p;
-
- switch (d->linkage)
- {
- case LINKd: p = "D"; break;
- case LINKc: p = "C"; break;
- case LINKcpp: p = "C++"; break;
- case LINKwindows: p = "Windows"; break;
- case LINKobjc: p = "Objective-C"; break;
- default:
- assert(0);
- break;
- }
- buf->writestring("extern (");
- buf->writestring(p);
- buf->writestring(") ");
- visit((AttribDeclaration *)d);
- }
-
- void visit(CPPMangleDeclaration *d)
- {
- const char *p;
-
- switch (d->cppmangle)
- {
- case CPPMANGLEclass: p = "class"; break;
- case CPPMANGLEstruct: p = "struct"; break;
- default:
- assert(0);
- break;
- }
- buf->writestring("extern (C++, ");
- buf->writestring(p);
- buf->writestring(") ");
- visit((AttribDeclaration *)d);
- }
-
- void visit(ProtDeclaration *d)
- {
- protectionToBuffer(buf, d->protection);
- buf->writeByte(' ');
- visit((AttribDeclaration *)d);
- }
-
- void visit(AlignDeclaration *d)
- {
- if (!d->ealign)
- buf->printf("align ");
- else
- buf->printf("align (%s)", d->ealign->toChars());
- visit((AttribDeclaration *)d);
- }
-
- void visit(AnonDeclaration *d)
- {
- buf->printf(d->isunion ? "union" : "struct");
- buf->writenl();
- buf->writestring("{");
- buf->writenl();
- buf->level++;
- if (d->decl)
- {
- for (size_t i = 0; i < d->decl->length; i++)
- {
- Dsymbol *de = (*d->decl)[i];
- de->accept(this);
- }
- }
- buf->level--;
- buf->writestring("}");
- buf->writenl();
- }
-
- void visit(PragmaDeclaration *d)
- {
- buf->printf("pragma (%s", d->ident->toChars());
- if (d->args && d->args->length)
- {
- buf->writestring(", ");
- argsToBuffer(d->args);
- }
- buf->writeByte(')');
- visit((AttribDeclaration *)d);
- }
-
- void visit(ConditionalDeclaration *d)
- {
- d->condition->accept(this);
- if (d->decl || d->elsedecl)
- {
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- if (d->decl)
- {
- for (size_t i = 0; i < d->decl->length; i++)
- {
- Dsymbol *de = (*d->decl)[i];
- de->accept(this);
- }
- }
- buf->level--;
- buf->writeByte('}');
- if (d->elsedecl)
- {
- buf->writenl();
- buf->writestring("else");
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- for (size_t i = 0; i < d->elsedecl->length; i++)
- {
- Dsymbol *de = (*d->elsedecl)[i];
- de->accept(this);
- }
- buf->level--;
- buf->writeByte('}');
- }
- }
- else
- buf->writeByte(':');
- buf->writenl();
- }
-
- void visit(ForwardingStatement *s)
- {
- s->statement->accept(this);
- }
-
- void visit(StaticForeachDeclaration *s)
- {
- buf->writestring("static ");
- if (s->sfe->aggrfe)
- {
- foreachWithoutBody(s->sfe->aggrfe);
- }
- else
- {
- assert(s->sfe->rangefe);
- foreachRangeWithoutBody(s->sfe->rangefe);
- }
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- visit((AttribDeclaration *)s);
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
-
- void visit(CompileDeclaration *d)
- {
- buf->writestring("mixin(");
- argsToBuffer(d->exps);
- buf->writestring(");");
- buf->writenl();
- }
-
- void visit(UserAttributeDeclaration *d)
- {
- buf->writestring("@(");
- argsToBuffer(d->atts);
- buf->writeByte(')');
- visit((AttribDeclaration *)d);
- }
-
- void visit(TemplateDeclaration *d)
- {
- if ((hgs->hdrgen || hgs->fullDump) && visitEponymousMember(d))
- return;
-
- if (hgs->ddoc)
- buf->writestring(d->kind());
- else
- buf->writestring("template");
- buf->writeByte(' ');
- buf->writestring(d->ident->toChars());
- buf->writeByte('(');
- visitTemplateParameters(hgs->ddoc ? d->origParameters : d->parameters);
- buf->writeByte(')');
- visitTemplateConstraint(d->constraint);
-
- if (hgs->hdrgen || hgs->fullDump)
- {
- hgs->tpltMember++;
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- for (size_t i = 0; i < d->members->length; i++)
- {
- Dsymbol *s = (*d->members)[i];
- s->accept(this);
- }
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- hgs->tpltMember--;
- }
- }
-
- bool visitEponymousMember(TemplateDeclaration *d)
- {
- if (!d->members || d->members->length != 1)
- return false;
-
- Dsymbol *onemember = (*d->members)[0];
- if (onemember->ident != d->ident)
- return false;
-
- if (FuncDeclaration *fd = onemember->isFuncDeclaration())
- {
- assert(fd->type);
- if (stcToBuffer(buf, fd->storage_class))
- buf->writeByte(' ');
- functionToBufferFull((TypeFunction *)fd->type, buf, d->ident, hgs, d);
- visitTemplateConstraint(d->constraint);
-
- hgs->tpltMember++;
- bodyToBuffer(fd);
- hgs->tpltMember--;
- return true;
- }
- if (AggregateDeclaration *ad = onemember->isAggregateDeclaration())
- {
- buf->writestring(ad->kind());
- buf->writeByte(' ');
- buf->writestring(ad->ident->toChars());
- buf->writeByte('(');
- visitTemplateParameters(hgs->ddoc ? d->origParameters : d->parameters);
- buf->writeByte(')');
- visitTemplateConstraint(d->constraint);
- visitBaseClasses(ad->isClassDeclaration());
-
- hgs->tpltMember++;
- if (ad->members)
- {
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- for (size_t i = 0; i < ad->members->length; i++)
- {
- Dsymbol *s = (*ad->members)[i];
- s->accept(this);
- }
- buf->level--;
- buf->writeByte('}');
- }
- else
- buf->writeByte(';');
- buf->writenl();
- hgs->tpltMember--;
- return true;
- }
- if (VarDeclaration *vd = onemember->isVarDeclaration())
- {
- if (d->constraint)
- return false;
-
- if (stcToBuffer(buf, vd->storage_class))
- buf->writeByte(' ');
- if (vd->type)
- typeToBuffer(vd->type, vd->ident);
- else
- buf->writestring(vd->ident->toChars());
-
- buf->writeByte('(');
- visitTemplateParameters(hgs->ddoc ? d->origParameters : d->parameters);
- buf->writeByte(')');
-
- if (vd->_init)
- {
- buf->writestring(" = ");
- ExpInitializer *ie = vd->_init->isExpInitializer();
- if (ie && (ie->exp->op == TOKconstruct || ie->exp->op == TOKblit))
- ((AssignExp *)ie->exp)->e2->accept(this);
- else
- vd->_init->accept(this);
- }
- buf->writeByte(';');
- buf->writenl();
- return true;
- }
-
- return false;
- }
- void visitTemplateParameters(TemplateParameters *parameters)
- {
- if (!parameters || !parameters->length)
- return;
- for (size_t i = 0; i < parameters->length; i++)
- {
- TemplateParameter *p = (*parameters)[i];
- if (i)
- buf->writestring(", ");
- p->accept(this);
- }
- }
- void visitTemplateConstraint(Expression *constraint)
- {
- if (!constraint)
- return;
- buf->writestring(" if (");
- constraint->accept(this);
- buf->writeByte(')');
- }
-
- void visit(TemplateInstance *ti)
- {
- buf->writestring(ti->name->toChars());
- tiargsToBuffer(ti);
-
- if (hgs->fullDump)
- {
- buf->writenl();
- if (ti->aliasdecl)
- {
- // the ti.aliasDecl is the instantiated body
- // if we have it, print it.
- ti->aliasdecl->accept(this);
- }
- }
- }
-
- void visit(TemplateMixin *tm)
- {
- buf->writestring("mixin ");
-
- typeToBuffer(tm->tqual, NULL);
- tiargsToBuffer(tm);
-
- if (tm->ident && memcmp(tm->ident->toChars(), "__mixin", 7) != 0)
- {
- buf->writeByte(' ');
- buf->writestring(tm->ident->toChars());
- }
- buf->writeByte(';');
- buf->writenl();
- }
-
- void tiargsToBuffer(TemplateInstance *ti)
- {
- buf->writeByte('!');
- if (ti->nest)
- {
- buf->writestring("(...)");
- return;
- }
- if (!ti->tiargs)
- {
- buf->writestring("()");
- return;
- }
-
- if (ti->tiargs->length == 1)
- {
- RootObject *oarg = (*ti->tiargs)[0];
- if (Type *t = isType(oarg))
- {
- if (t->equals(Type::tstring) ||
- t->equals(Type::twstring) ||
- t->equals(Type::tdstring) ||
- (t->mod == 0 &&
- (t->isTypeBasic() ||
- (t->ty == Tident && ((TypeIdentifier *)t)->idents.length == 0))))
- {
- buf->writestring(t->toChars());
- return;
- }
- }
- else if (Expression *e = isExpression(oarg))
- {
- if (e->op == TOKint64 ||
- e->op == TOKfloat64 ||
- e->op == TOKnull ||
- e->op == TOKstring ||
- e->op == TOKthis)
- {
- buf->writestring(e->toChars());
- return;
- }
- }
- }
- buf->writeByte('(');
- ti->nest++;
- for (size_t i = 0; i < ti->tiargs->length; i++)
- {
- RootObject *arg = (*ti->tiargs)[i];
- if (i)
- buf->writestring(", ");
- objectToBuffer(arg);
- }
- ti->nest--;
- buf->writeByte(')');
- }
-
- /****************************************
- * This makes a 'pretty' version of the template arguments.
- * It's analogous to genIdent() which makes a mangled version.
- */
- void objectToBuffer(RootObject *oarg)
- {
- //printf("objectToBuffer()\n");
-
- /* The logic of this should match what genIdent() does. The _dynamic_cast()
- * function relies on all the pretty strings to be unique for different classes
- * (see Bugzilla 7375).
- * Perhaps it would be better to demangle what genIdent() does.
- */
- if (Type *t = isType(oarg))
- {
- //printf("\tt: %s ty = %d\n", t->toChars(), t->ty);
- typeToBuffer(t, NULL);
- }
- else if (Expression *e = isExpression(oarg))
- {
- if (e->op == TOKvar)
- e = e->optimize(WANTvalue); // added to fix Bugzilla 7375
- e->accept(this);
- }
- else if (Dsymbol *s = isDsymbol(oarg))
- {
- const char *p = s->ident ? s->ident->toChars() : s->toChars();
- buf->writestring(p);
- }
- else if (Tuple *v = isTuple(oarg))
- {
- Objects *args = &v->objects;
- for (size_t i = 0; i < args->length; i++)
- {
- RootObject *arg = (*args)[i];
- if (i)
- buf->writestring(", ");
- objectToBuffer(arg);
- }
- }
- else if (Parameter *p = isParameter(oarg))
- {
- p->accept(this);
- }
- else if (!oarg)
- {
- buf->writestring("NULL");
- }
- else
- {
- assert(0);
- }
- }
-
- void visit(EnumDeclaration *d)
- {
- EnumDeclaration *oldInEnumDecl = inEnumDecl;
- inEnumDecl = d;
- buf->writestring("enum ");
- if (d->ident)
- {
- buf->writestring(d->ident->toChars());
- buf->writeByte(' ');
- }
- if (d->memtype)
- {
- buf->writestring(": ");
- typeToBuffer(d->memtype, NULL);
- }
- if (!d->members)
- {
- buf->writeByte(';');
- buf->writenl();
- inEnumDecl = oldInEnumDecl;
- return;
- }
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- for (size_t i = 0; i < d->members->length; i++)
- {
- EnumMember *em = (*d->members)[i]->isEnumMember();
- if (!em)
- continue;
- em->accept(this);
- buf->writeByte(',');
- buf->writenl();
- }
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- inEnumDecl = oldInEnumDecl;
- }
-
- void visit(Nspace *d)
- {
- buf->writestring("extern (C++, ");
- buf->writestring(d->ident->toChars());
- buf->writeByte(')');
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- for (size_t i = 0; i < d->members->length; i++)
- {
- Dsymbol *s = (*d->members)[i];
- s->accept(this);
- }
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
-
- void visit(StructDeclaration *d)
- {
- buf->printf("%s ", d->kind());
- if (!d->isAnonymous())
- buf->writestring(d->toChars());
- if (!d->members)
- {
- buf->writeByte(';');
- buf->writenl();
- return;
- }
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- for (size_t i = 0; i < d->members->length; i++)
- {
- Dsymbol *s = (*d->members)[i];
- s->accept(this);
- }
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
- }
-
- void visit(ClassDeclaration *d)
- {
- if (!d->isAnonymous())
- {
- buf->writestring(d->kind());
- buf->writeByte(' ');
- buf->writestring(d->ident->toChars());
- }
- visitBaseClasses(d);
- if (d->members)
- {
- buf->writenl();
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- for (size_t i = 0; i < d->members->length; i++)
- {
- Dsymbol *s = (*d->members)[i];
- s->accept(this);
- }
- buf->level--;
- buf->writeByte('}');
- }
- else
- buf->writeByte(';');
- buf->writenl();
- }
-
- void visitBaseClasses(ClassDeclaration *d)
- {
- if (!d || !d->baseclasses->length)
- return;
-
- buf->writestring(" : ");
- for (size_t i = 0; i < d->baseclasses->length; i++)
- {
- if (i)
- buf->writestring(", ");
- BaseClass *b = (*d->baseclasses)[i];
- typeToBuffer(b->type, NULL);
- }
- }
-
- void visit(AliasDeclaration *d)
- {
- if (d->storage_class & STClocal)
- return;
- buf->writestring("alias ");
- if (d->aliassym)
- {
- buf->writestring(d->ident->toChars());
- buf->writestring(" = ");
- if (stcToBuffer(buf, d->storage_class))
- buf->writeByte(' ');
- d->aliassym->accept(this);
- }
- else if (d->type->ty == Tfunction)
- {
- if (stcToBuffer(buf, d->storage_class))
- buf->writeByte(' ');
- typeToBuffer(d->type, d->ident);
- }
- else
- {
- declstring = (d->ident == Id::string || d->ident == Id::wstring || d->ident == Id::dstring);
- buf->writestring(d->ident->toChars());
- buf->writestring(" = ");
- if (stcToBuffer(buf, d->storage_class))
- buf->writeByte(' ');
- typeToBuffer(d->type, NULL);
- declstring = false;
- }
- buf->writeByte(';');
- buf->writenl();
- }
-
- void visit(VarDeclaration *d)
- {
- if (d->storage_class & STClocal)
- return;
- visitVarDecl(d, false);
- buf->writeByte(';');
- buf->writenl();
- }
- void visitVarDecl(VarDeclaration *v, bool anywritten)
- {
- if (anywritten)
- {
- buf->writestring(", ");
- buf->writestring(v->ident->toChars());
- }
- else
- {
- if (stcToBuffer(buf, v->storage_class))
- buf->writeByte(' ');
- if (v->type)
- typeToBuffer(v->type, v->ident);
- else
- buf->writestring(v->ident->toChars());
- }
- if (v->_init)
- {
- buf->writestring(" = ");
- ExpInitializer *ie = v->_init->isExpInitializer();
- if (ie && (ie->exp->op == TOKconstruct || ie->exp->op == TOKblit))
- ((AssignExp *)ie->exp)->e2->accept(this);
- else
- v->_init->accept(this);
- }
- }
-
- void visit(FuncDeclaration *f)
- {
- //printf("FuncDeclaration::toCBuffer() '%s'\n", f->toChars());
-
- if (stcToBuffer(buf, f->storage_class))
- buf->writeByte(' ');
- TypeFunction *tf = (TypeFunction *)f->type;
- typeToBuffer(tf, f->ident);
- if (hgs->hdrgen)
- {
- // if the return type is missing (e.g. ref functions or auto)
- if (!tf->next || f->storage_class & STCauto)
- {
- hgs->autoMember++;
- bodyToBuffer(f);
- hgs->autoMember--;
- }
- else if (hgs->tpltMember == 0 && global.params.hdrStripPlainFunctions)
- {
- buf->writeByte(';');
- buf->writenl();
- }
- else
- bodyToBuffer(f);
- }
- else
- bodyToBuffer(f);
- }
-
- void bodyToBuffer(FuncDeclaration *f)
- {
- if (!f->fbody || (hgs->hdrgen && global.params.hdrStripPlainFunctions && !hgs->autoMember && !hgs->tpltMember))
- {
- buf->writeByte(';');
- buf->writenl();
- return;
- }
-
- int savetlpt = hgs->tpltMember;
- int saveauto = hgs->autoMember;
- hgs->tpltMember = 0;
- hgs->autoMember = 0;
- buf->writenl();
- bool requireDo = false;
- // in{}
- if (f->frequires)
- {
- for (size_t i = 0; i < f->frequires->length; i++)
- {
- Statement *frequire = (*f->frequires)[i];
- buf->writestring("in");
- if (ExpStatement *es = frequire->isExpStatement())
- {
- assert(es->exp && es->exp->op == TOKassert);
- buf->writestring(" (");
- ((AssertExp *)es->exp)->e1->accept(this);
- buf->writeByte(')');
- buf->writenl();
- requireDo = false;
- }
- else
- {
- buf->writenl();
- frequire->accept(this);
- requireDo = true;
- }
- }
- }
-
- // out{}
- if (f->fensures)
- {
- for (size_t i = 0; i < f->fensures->length; i++)
- {
- Ensure fensure = (*f->fensures)[i];
- buf->writestring("out");
- if (ExpStatement *es = fensure.ensure->isExpStatement())
- {
- assert(es->exp && es->exp->op == TOKassert);
- buf->writestring(" (");
- if (fensure.id)
- {
- buf->writestring(fensure.id->toChars());
- }
- buf->writestring("; ");
- ((AssertExp *)es->exp)->e1->accept(this);
- buf->writeByte(')');
- buf->writenl();
- requireDo = false;
- }
- else
- {
- if (fensure.id)
- {
- buf->writeByte('(');
- buf->writestring(fensure.id->toChars());
- buf->writeByte(')');
- }
- buf->writenl();
- fensure.ensure->accept(this);
- requireDo = true;
- }
- }
- }
-
- if (requireDo)
- {
- buf->writestring("body");
- buf->writenl();
- }
-
- buf->writeByte('{');
- buf->writenl();
- buf->level++;
- f->fbody->accept(this);
- buf->level--;
- buf->writeByte('}');
- buf->writenl();
-
- hgs->tpltMember = savetlpt;
- hgs->autoMember = saveauto;
- }
-
- void visit(FuncLiteralDeclaration *f)
- {
- if (f->type->ty == Terror)
- {
- buf->writestring("__error");
- return;
- }
-
- if (f->tok != TOKreserved)
- {
- buf->writestring(f->kind());
- buf->writeByte(' ');
- }
-
- TypeFunction *tf = (TypeFunction *)f->type;
- // Don't print tf->mod, tf->trust, and tf->linkage
- if (!f->inferRetType && tf->next)
- typeToBuffer(tf->next, NULL);
- parametersToBuffer(tf->parameterList.parameters, tf->parameterList.varargs);
-
- CompoundStatement *cs = f->fbody->isCompoundStatement();
- Statement *s1;
- if (f->semanticRun >= PASSsemantic3done && cs)
- {
- s1 = (*cs->statements)[cs->statements->length - 1];
- }
- else
- s1 = !cs ? f->fbody : NULL;
- ReturnStatement *rs = s1 ? s1->isReturnStatement() : NULL;
- if (rs && rs->exp)
- {
- buf->writestring(" => ");
- rs->exp->accept(this);
- }
- else
- {
- hgs->tpltMember++;
- bodyToBuffer(f);
- hgs->tpltMember--;
- }
- }
-
- void visit(PostBlitDeclaration *d)
- {
- if (stcToBuffer(buf, d->storage_class))
- buf->writeByte(' ');
- buf->writestring("this(this)");
- bodyToBuffer(d);
- }
-
- void visit(DtorDeclaration *d)
- {
- if (d->storage_class & STCtrusted)
- buf->writestring("@trusted ");
- if (d->storage_class & STCsafe)
- buf->writestring("@safe ");
- if (d->storage_class & STCnogc)
- buf->writestring("@nogc ");
- if (d->storage_class & STCdisable)
- buf->writestring("@disable ");
-
- buf->writestring("~this()");
- bodyToBuffer(d);
- }
-
- void visit(StaticCtorDeclaration *d)
- {
- if (stcToBuffer(buf, d->storage_class & ~STCstatic))
- buf->writeByte(' ');
- if (d->isSharedStaticCtorDeclaration())
- buf->writestring("shared ");
- buf->writestring("static this()");
- if (hgs->hdrgen && !hgs->tpltMember)
- {
- buf->writeByte(';');
- buf->writenl();
- }
- else
- bodyToBuffer(d);
- }
-
- void visit(StaticDtorDeclaration *d)
- {
- if (hgs->hdrgen)
- return;
- if (stcToBuffer(buf, d->storage_class & ~STCstatic))
- buf->writeByte(' ');
- if (d->isSharedStaticDtorDeclaration())
- buf->writestring("shared ");
- buf->writestring("static ~this()");
- bodyToBuffer(d);
- }
-
- void visit(InvariantDeclaration *d)
- {
- if (hgs->hdrgen)
- return;
- if (stcToBuffer(buf, d->storage_class))
- buf->writeByte(' ');
- buf->writestring("invariant");
- if (ExpStatement *es = d->fbody->isExpStatement())
- {
- assert(es->exp && es->exp->op == TOKassert);
- buf->writestring(" (");
- ((AssertExp *)es->exp)->e1->accept(this);
- buf->writestring(");");
- buf->writenl();
- }
- else
- {
- bodyToBuffer(d);
- }
- }
-
- void visit(UnitTestDeclaration *d)
- {
- if (hgs->hdrgen)
- return;
- if (stcToBuffer(buf, d->storage_class))
- buf->writeByte(' ');
- buf->writestring("unittest");
- bodyToBuffer(d);
- }
-
- void visit(NewDeclaration *d)
- {
- if (stcToBuffer(buf, d->storage_class & ~STCstatic))
- buf->writeByte(' ');
- buf->writestring("new");
- parametersToBuffer(d->parameters, d->varargs);
- bodyToBuffer(d);
- }
-
- void visit(DeleteDeclaration *d)
- {
- if (stcToBuffer(buf, d->storage_class & ~STCstatic))
- buf->writeByte(' ');
- buf->writestring("delete");
- parametersToBuffer(d->parameters, 0);
- bodyToBuffer(d);
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- void visit(ErrorInitializer *)
- {
- buf->writestring("__error__");
- }
-
- void visit(VoidInitializer *)
- {
- buf->writestring("void");
- }
-
- void visit(StructInitializer *si)
- {
- //printf("StructInitializer::toCBuffer()\n");
- buf->writeByte('{');
- for (size_t i = 0; i < si->field.length; i++)
- {
- if (i)
- buf->writestring(", ");
- if (Identifier *id = si->field[i])
- {
- buf->writestring(id->toChars());
- buf->writeByte(':');
- }
- if (Initializer *iz = si->value[i])
- iz->accept(this);
- }
- buf->writeByte('}');
- }
-
- void visit(ArrayInitializer *ai)
- {
- buf->writeByte('[');
- for (size_t i = 0; i < ai->index.length; i++)
- {
- if (i)
- buf->writestring(", ");
- if (Expression *ex = ai->index[i])
- {
- ex->accept(this);
- buf->writeByte(':');
- }
- if (Initializer *iz = ai->value[i])
- iz->accept(this);
- }
- buf->writeByte(']');
- }
-
- void visit(ExpInitializer *ei)
- {
- ei->exp->accept(this);
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- /**************************************************
- * Write out argument list to buf.
- */
- void argsToBuffer(Expressions *expressions, Expression *basis = NULL)
- {
- if (!expressions || !expressions->length)
- return;
-
- for (size_t i = 0; i < expressions->length; i++)
- {
- Expression *el = (*expressions)[i];
- if (i)
- buf->writestring(", ");
- if (!el)
- el = basis;
- if (el)
- expToBuffer(el, PREC_assign);
- }
- }
-
- void sizeToBuffer(Expression *e)
- {
- if (e->type == Type::tsize_t)
- {
- Expression *ex = (e->op == TOKcast ? ((CastExp *)e)->e1 : e);
- ex = ex->optimize(WANTvalue);
-
- dinteger_t uval = ex->op == TOKint64 ? ex->toInteger() : (dinteger_t)-1;
- if ((sinteger_t)uval >= 0)
- {
- dinteger_t sizemax;
- if (target.ptrsize == 8)
- sizemax = 0xFFFFFFFFFFFFFFFFULL;
- else if (target.ptrsize == 4)
- sizemax = 0xFFFFFFFFUL;
- else if (target.ptrsize == 2)
- sizemax = 0xFFFFUL;
- else
- assert(0);
- if (uval <= sizemax && uval <= 0x7FFFFFFFFFFFFFFFULL)
- {
- buf->printf("%llu", uval);
- return;
- }
- }
- }
- expToBuffer(e, PREC_assign);
- }
-
- /**************************************************
- * Write expression out to buf, but wrap it
- * in ( ) if its precedence is less than pr.
- */
- void expToBuffer(Expression *e, PREC pr)
- {
- assert(precedence[e->op] != PREC_zero);
- assert(pr != PREC_zero);
-
- //if (precedence[e->op] == 0) e->print();
- /* Despite precedence, we don't allow a<b<c expressions.
- * They must be parenthesized.
- */
- if (precedence[e->op] < pr ||
- (pr == PREC_rel && precedence[e->op] == pr))
- {
- buf->writeByte('(');
- e->accept(this);
- buf->writeByte(')');
- }
- else
- e->accept(this);
- }
-
- void visit(Expression *e)
- {
- buf->writestring(Token::toChars(e->op));
- }
-
- void visit(IntegerExp *e)
- {
- dinteger_t v = e->toInteger();
-
- if (e->type)
- {
- Type *t = e->type;
- L1:
- switch (t->ty)
- {
- case Tenum:
- {
- TypeEnum *te = (TypeEnum *)t;
- if (hgs->fullDump)
- {
- EnumDeclaration *sym = te->sym;
- if (inEnumDecl != sym)
- {
- for (size_t i = 0; i < sym->members->length; i++)
- {
- EnumMember *em = (EnumMember *)(*sym->members)[i];
- if (em->value()->toInteger() == v)
- {
- buf->printf("%s.%s", sym->toChars(), em->ident->toChars());
- return;
- }
- }
- }
- }
- buf->printf("cast(%s)", te->sym->toChars());
- t = te->sym->memtype;
- goto L1;
- }
-
- case Twchar: // BUG: need to cast(wchar)
- case Tdchar: // BUG: need to cast(dchar)
- if ((uinteger_t)v > 0xFF)
- {
- buf->printf("'\\U%08x'", v);
- break;
- }
- /* fall through */
- case Tchar:
- {
- size_t o = buf->length();
- if (v == '\'')
- buf->writestring("'\\''");
- else if (isprint((int)v) && v != '\\')
- buf->printf("'%c'", (int)v);
- else
- buf->printf("'\\x%02x'", (int)v);
- if (hgs->ddoc)
- escapeDdocString(buf, o);
- break;
- }
-
- case Tint8:
- buf->writestring("cast(byte)");
- goto L2;
-
- case Tint16:
- buf->writestring("cast(short)");
- goto L2;
-
- case Tint32:
- L2:
- buf->printf("%d", (int)v);
- break;
-
- case Tuns8:
- buf->writestring("cast(ubyte)");
- goto L3;
-
- case Tuns16:
- buf->writestring("cast(ushort)");
- goto L3;
-
- case Tuns32:
- L3:
- buf->printf("%uu", (unsigned)v);
- break;
-
- case Tint64:
- buf->printf("%lldL", v);
- break;
-
- case Tuns64:
- L4:
- buf->printf("%lluLU", v);
- break;
-
- case Tbool:
- buf->writestring(v ? "true" : "false");
- break;
-
- case Tpointer:
- buf->writestring("cast(");
- buf->writestring(t->toChars());
- buf->writeByte(')');
- if (target.ptrsize == 8)
- goto L4;
- else if (target.ptrsize == 4 ||
- target.ptrsize == 2)
- goto L3;
- else
- assert(0);
-
- case Tvoid:
- buf->writestring("cast(void)0");
- break;
-
- default:
- /* This can happen if errors, such as
- * the type is painted on like in fromConstInitializer().
- */
- if (!global.errors)
- {
- assert(0);
- }
- break;
- }
- }
- else if (v & 0x8000000000000000LL)
- buf->printf("0x%llx", v);
- else
- buf->printf("%lld", v);
- }
-
- void visit(ErrorExp *)
- {
- buf->writestring("__error");
- }
-
- void floatToBuffer(Type *type, real_t value)
- {
- /** sizeof(value)*3 is because each byte of mantissa is max
- of 256 (3 characters). The string will be "-M.MMMMe-4932".
- (ie, 8 chars more than mantissa). Plus one for trailing \0.
- Plus one for rounding. */
- const size_t BUFFER_LEN = sizeof(value) * 3 + 8 + 1 + 1;
- char buffer[BUFFER_LEN];
- memset(buffer, 0, BUFFER_LEN);
- CTFloat::sprint(buffer, 'g', value);
- assert(strlen(buffer) < BUFFER_LEN);
-
- if (hgs->hdrgen)
- {
- real_t r = CTFloat::parse(buffer);
- if (r != value) // if exact duplication
- CTFloat::sprint(buffer, 'a', value);
- }
- buf->writestring(buffer);
-
- if (type)
- {
- Type *t = type->toBasetype();
- switch (t->ty)
- {
- case Tfloat32:
- case Timaginary32:
- case Tcomplex32:
- buf->writeByte('F');
- break;
-
- case Tfloat80:
- case Timaginary80:
- case Tcomplex80:
- buf->writeByte('L');
- break;
-
- default:
- break;
- }
- if (t->isimaginary())
- buf->writeByte('i');
- }
- }
-
- void visit(RealExp *e)
- {
- floatToBuffer(e->type, e->value);
- }
-
- void visit(ComplexExp *e)
- {
- /* Print as:
- * (re+imi)
- */
- buf->writeByte('(');
- floatToBuffer(e->type, creall(e->value));
- buf->writeByte('+');
- floatToBuffer(e->type, cimagl(e->value));
- buf->writestring("i)");
- }
-
- void visit(IdentifierExp *e)
- {
- if (hgs->hdrgen || hgs->ddoc)
- buf->writestring(e->ident->toHChars2());
- else
- buf->writestring(e->ident->toChars());
- }
-
- void visit(DsymbolExp *e)
- {
- buf->writestring(e->s->toChars());
- }
-
- void visit(ThisExp *)
- {
- buf->writestring("this");
- }
-
- void visit(SuperExp *)
- {
- buf->writestring("super");
- }
-
- void visit(NullExp *)
- {
- buf->writestring("null");
- }
-
- void visit(StringExp *e)
- {
- buf->writeByte('"');
- size_t o = buf->length();
- for (size_t i = 0; i < e->len; i++)
- {
- unsigned c = e->charAt(i);
- switch (c)
- {
- case '"':
- case '\\':
- buf->writeByte('\\');
- /* fall through */
- default:
- if (c <= 0xFF)
- {
- if (c <= 0x7F && isprint(c))
- buf->writeByte(c);
- else
- buf->printf("\\x%02x", c);
- }
- else if (c <= 0xFFFF)
- buf->printf("\\x%02x\\x%02x", c & 0xFF, c >> 8);
- else
- buf->printf("\\x%02x\\x%02x\\x%02x\\x%02x",
- c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24);
- break;
- }
- }
- if (hgs->ddoc)
- escapeDdocString(buf, o);
- buf->writeByte('"');
- if (e->postfix)
- buf->writeByte(e->postfix);
- }
-
- void visit(ArrayLiteralExp *e)
- {
- buf->writeByte('[');
- argsToBuffer(e->elements, e->basis);
- buf->writeByte(']');
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- buf->writeByte('[');
- for (size_t i = 0; i < e->keys->length; i++)
- {
- Expression *key = (*e->keys)[i];
- Expression *value = (*e->values)[i];
-
- if (i)
- buf->writestring(", ");
- expToBuffer(key, PREC_assign);
- buf->writeByte(':');
- expToBuffer(value, PREC_assign);
- }
- buf->writeByte(']');
- }
-
- void visit(StructLiteralExp *e)
- {
- buf->writestring(e->sd->toChars());
- buf->writeByte('(');
-
- // CTFE can generate struct literals that contain an AddrExp pointing
- // to themselves, need to avoid infinite recursion:
- // struct S { this(int){ this.s = &this; } S* s; }
- // const foo = new S(0);
- if (e->stageflags & stageToCBuffer)
- buf->writestring("<recursion>");
- else
- {
- int old = e->stageflags;
- e->stageflags |= stageToCBuffer;
- argsToBuffer(e->elements);
- e->stageflags = old;
- }
-
- buf->writeByte(')');
- }
-
- void visit(TypeExp *e)
- {
- typeToBuffer(e->type, NULL);
- }
-
- void visit(ScopeExp *e)
- {
- if (e->sds->isTemplateInstance())
- {
- e->sds->accept(this);
- }
- else if (hgs != NULL && hgs->ddoc)
- {
- // fixes bug 6491
- Module *m = e->sds->isModule();
- if (m)
- buf->writestring(m->md->toChars());
- else
- buf->writestring(e->sds->toChars());
- }
- else
- {
- buf->writestring(e->sds->kind());
- buf->writeByte(' ');
- buf->writestring(e->sds->toChars());
- }
- }
-
- void visit(TemplateExp *e)
- {
- buf->writestring(e->td->toChars());
- }
-
- void visit(NewExp *e)
- {
- if (e->thisexp)
- {
- expToBuffer(e->thisexp, PREC_primary);
- buf->writeByte('.');
- }
- buf->writestring("new ");
- if (e->newargs && e->newargs->length)
- {
- buf->writeByte('(');
- argsToBuffer(e->newargs);
- buf->writeByte(')');
- }
- typeToBuffer(e->newtype, NULL);
- if (e->arguments && e->arguments->length)
- {
- buf->writeByte('(');
- argsToBuffer(e->arguments);
- buf->writeByte(')');
- }
- }
-
- void visit(NewAnonClassExp *e)
- {
- if (e->thisexp)
- {
- expToBuffer(e->thisexp, PREC_primary);
- buf->writeByte('.');
- }
- buf->writestring("new");
- if (e->newargs && e->newargs->length)
- {
- buf->writeByte('(');
- argsToBuffer(e->newargs);
- buf->writeByte(')');
- }
- buf->writestring(" class ");
- if (e->arguments && e->arguments->length)
- {
- buf->writeByte('(');
- argsToBuffer(e->arguments);
- buf->writeByte(')');
- }
- if (e->cd)
- e->cd->accept(this);
- }
-
- void visit(SymOffExp *e)
- {
- if (e->offset)
- buf->printf("(& %s+%u)", e->var->toChars(), e->offset);
- else if (e->var->isTypeInfoDeclaration())
- buf->printf("%s", e->var->toChars());
- else
- buf->printf("& %s", e->var->toChars());
- }
-
- void visit(VarExp *e)
- {
- buf->writestring(e->var->toChars());
- }
-
- void visit(OverExp *e)
- {
- buf->writestring(e->vars->ident->toChars());
- }
-
- void visit(TupleExp *e)
- {
- if (e->e0)
- {
- buf->writeByte('(');
- e->e0->accept(this);
- buf->writestring(", tuple(");
- argsToBuffer(e->exps);
- buf->writestring("))");
- }
- else
- {
- buf->writestring("tuple(");
- argsToBuffer(e->exps);
- buf->writeByte(')');
- }
- }
-
- void visit(FuncExp *e)
- {
- e->fd->accept(this);
- //buf->writestring(e->fd->toChars());
- }
-
- void visit(DeclarationExp *e)
- {
- /* Normal dmd execution won't reach here - regular variable declarations
- * are handled in visit(ExpStatement), so here would be used only when
- * we'll directly call Expression::toChars() for debugging.
- */
- if (VarDeclaration *v = e->declaration->isVarDeclaration())
- {
- // For debugging use:
- // - Avoid printing newline.
- // - Intentionally use the format (Type var;)
- // which isn't correct as regular D code.
- buf->writeByte('(');
- visitVarDecl(v, false);
- buf->writeByte(';');
- buf->writeByte(')');
- }
- else
- e->declaration->accept(this);
- }
-
- void visit(TypeidExp *e)
- {
- buf->writestring("typeid(");
- objectToBuffer(e->obj);
- buf->writeByte(')');
- }
-
- void visit(TraitsExp *e)
- {
- buf->writestring("__traits(");
- if (e->ident)
- buf->writestring(e->ident->toChars());
- if (e->args)
- {
- for (size_t i = 0; i < e->args->length; i++)
- {
- RootObject *arg = (*e->args)[i];
- buf->writestring(", ");
- objectToBuffer(arg);
- }
- }
- buf->writeByte(')');
- }
-
- void visit(HaltExp *)
- {
- buf->writestring("halt");
- }
-
- void visit(IsExp *e)
- {
- buf->writestring("is(");
- typeToBuffer(e->targ, e->id);
- if (e->tok2 != TOKreserved)
- {
- buf->printf(" %s %s", Token::toChars(e->tok), Token::toChars(e->tok2));
- }
- else if (e->tspec)
- {
- if (e->tok == TOKcolon)
- buf->writestring(" : ");
- else
- buf->writestring(" == ");
- typeToBuffer(e->tspec, NULL);
- }
- if (e->parameters && e->parameters->length)
- {
- buf->writestring(", ");
- visitTemplateParameters(e->parameters);
- }
- buf->writeByte(')');
- }
-
- void visit(UnaExp *e)
- {
- buf->writestring(Token::toChars(e->op));
- expToBuffer(e->e1, precedence[e->op]);
- }
-
- void visit(BinExp *e)
- {
- expToBuffer(e->e1, precedence[e->op]);
- buf->writeByte(' ');
- buf->writestring(Token::toChars(e->op));
- buf->writeByte(' ');
- expToBuffer(e->e2, (PREC)(precedence[e->op] + 1));
- }
-
- void visit(CompileExp *e)
- {
- buf->writestring("mixin(");
- argsToBuffer(e->exps);
- buf->writeByte(')');
- }
-
- void visit(ImportExp *e)
- {
- buf->writestring("import(");
- expToBuffer(e->e1, PREC_assign);
- buf->writeByte(')');
- }
-
- void visit(AssertExp *e)
- {
- buf->writestring("assert(");
- expToBuffer(e->e1, PREC_assign);
- if (e->msg)
- {
- buf->writestring(", ");
- expToBuffer(e->msg, PREC_assign);
- }
- buf->writeByte(')');
- }
-
- void visit(DotIdExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writeByte('.');
- buf->writestring(e->ident->toChars());
- }
-
- void visit(DotTemplateExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writeByte('.');
- buf->writestring(e->td->toChars());
- }
-
- void visit(DotVarExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writeByte('.');
- buf->writestring(e->var->toChars());
- }
-
- void visit(DotTemplateInstanceExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writeByte('.');
- e->ti->accept(this);
- }
-
- void visit(DelegateExp *e)
- {
- buf->writeByte('&');
- if (!e->func->isNested())
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writeByte('.');
- }
- buf->writestring(e->func->toChars());
- }
-
- void visit(DotTypeExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writeByte('.');
- buf->writestring(e->sym->toChars());
- }
-
- void visit(CallExp *e)
- {
- if (e->e1->op == TOKtype)
- {
- /* Avoid parens around type to prevent forbidden cast syntax:
- * (sometype)(arg1)
- * This is ok since types in constructor calls
- * can never depend on parens anyway
- */
- e->e1->accept(this);
- }
- else
- expToBuffer(e->e1, precedence[e->op]);
- buf->writeByte('(');
- argsToBuffer(e->arguments);
- buf->writeByte(')');
- }
-
- void visit(PtrExp *e)
- {
- buf->writeByte('*');
- expToBuffer(e->e1, precedence[e->op]);
- }
-
- void visit(DeleteExp *e)
- {
- buf->writestring("delete ");
- expToBuffer(e->e1, precedence[e->op]);
- }
-
- void visit(CastExp *e)
- {
- buf->writestring("cast(");
- if (e->to)
- typeToBuffer(e->to, NULL);
- else
- {
- MODtoBuffer(buf, e->mod);
- }
- buf->writeByte(')');
- expToBuffer(e->e1, precedence[e->op]);
- }
-
- void visit(VectorExp *e)
- {
- buf->writestring("cast(");
- typeToBuffer(e->to, NULL);
- buf->writeByte(')');
- expToBuffer(e->e1, precedence[e->op]);
- }
-
- void visit(VectorArrayExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writestring(".array");
- }
-
- void visit(SliceExp *e)
- {
- expToBuffer(e->e1, precedence[e->op]);
- buf->writeByte('[');
- if (e->upr || e->lwr)
- {
- if (e->lwr)
- sizeToBuffer(e->lwr);
- else
- buf->writeByte('0');
- buf->writestring("..");
- if (e->upr)
- sizeToBuffer(e->upr);
- else
- buf->writeByte('$');
- }
- buf->writeByte(']');
- }
-
- void visit(ArrayLengthExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writestring(".length");
- }
-
- void visit(IntervalExp *e)
- {
- expToBuffer(e->lwr, PREC_assign);
- buf->writestring("..");
- expToBuffer(e->upr, PREC_assign);
- }
-
- void visit(DelegatePtrExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writestring(".ptr");
- }
-
- void visit(DelegateFuncptrExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writestring(".funcptr");
- }
-
- void visit(ArrayExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writeByte('[');
- argsToBuffer(e->arguments);
- buf->writeByte(']');
- }
-
- void visit(DotExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writeByte('.');
- expToBuffer(e->e2, PREC_primary);
- }
-
- void visit(IndexExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writeByte('[');
- sizeToBuffer(e->e2);
- buf->writeByte(']');
- }
-
- void visit(PostExp *e)
- {
- expToBuffer(e->e1, precedence[e->op]);
- buf->writestring(Token::toChars(e->op));
- }
-
- void visit(PreExp *e)
- {
- buf->writestring(Token::toChars(e->op));
- expToBuffer(e->e1, precedence[e->op]);
- }
-
- void visit(RemoveExp *e)
- {
- expToBuffer(e->e1, PREC_primary);
- buf->writestring(".remove(");
- expToBuffer(e->e2, PREC_assign);
- buf->writeByte(')');
- }
-
- void visit(CondExp *e)
- {
- expToBuffer(e->econd, PREC_oror);
- buf->writestring(" ? ");
- expToBuffer(e->e1, PREC_expr);
- buf->writestring(" : ");
- expToBuffer(e->e2, PREC_cond);
- }
-
- void visit(DefaultInitExp *e)
- {
- buf->writestring(Token::toChars(e->subop));
- }
-
- void visit(ClassReferenceExp *e)
- {
- buf->writestring(e->value->toChars());
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- void visit(TemplateTypeParameter *tp)
- {
- buf->writestring(tp->ident->toChars());
- if (tp->specType)
- {
- buf->writestring(" : ");
- typeToBuffer(tp->specType, NULL);
- }
- if (tp->defaultType)
- {
- buf->writestring(" = ");
- typeToBuffer(tp->defaultType, NULL);
- }
- }
-
- void visit(TemplateThisParameter *tp)
- {
- buf->writestring("this ");
- visit((TemplateTypeParameter *)tp);
- }
-
- void visit(TemplateAliasParameter *tp)
- {
- buf->writestring("alias ");
- if (tp->specType)
- typeToBuffer(tp->specType, tp->ident);
- else
- buf->writestring(tp->ident->toChars());
- if (tp->specAlias)
- {
- buf->writestring(" : ");
- objectToBuffer(tp->specAlias);
- }
- if (tp->defaultAlias)
- {
- buf->writestring(" = ");
- objectToBuffer(tp->defaultAlias);
- }
- }
-
- void visit(TemplateValueParameter *tp)
- {
- typeToBuffer(tp->valType, tp->ident);
- if (tp->specValue)
- {
- buf->writestring(" : ");
- tp->specValue->accept(this);
- }
- if (tp->defaultValue)
- {
- buf->writestring(" = ");
- tp->defaultValue->accept(this);
- }
- }
-
- void visit(TemplateTupleParameter *tp)
- {
- buf->writestring(tp->ident->toChars());
- buf->writestring("...");
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- void visit(DebugCondition *c)
- {
- if (c->ident)
- buf->printf("debug (%s)", c->ident->toChars());
- else
- buf->printf("debug (%u)", c->level);
- }
-
- void visit(VersionCondition *c)
- {
- if (c->ident)
- buf->printf("version (%s)", c->ident->toChars());
- else
- buf->printf("version (%u)", c->level);
- }
-
- void visit(StaticIfCondition *c)
- {
- buf->writestring("static if (");
- c->exp->accept(this);
- buf->writeByte(')');
- }
-
- ////////////////////////////////////////////////////////////////////////////
-
- void visit(Parameter *p)
- {
- if (p->userAttribDecl)
- {
- buf->writestring("@");
- bool isAnonymous = p->userAttribDecl->atts->length > 0
- && (*p->userAttribDecl->atts)[0]->op != TOKcall;
- if (isAnonymous)
- buf->writestring("(");
- argsToBuffer(p->userAttribDecl->atts);
- if (isAnonymous)
- buf->writestring(")");
- buf->writestring(" ");
- }
- if (p->storageClass & STCauto)
- buf->writestring("auto ");
-
- if (p->storageClass & STCreturn)
- buf->writestring("return ");
-
- if (p->storageClass & STCout)
- buf->writestring("out ");
- else if (p->storageClass & STCref)
- buf->writestring("ref ");
- else if (p->storageClass & STCin)
- buf->writestring("in ");
- else if (p->storageClass & STClazy)
- buf->writestring("lazy ");
- else if (p->storageClass & STCalias)
- buf->writestring("alias ");
-
- StorageClass stc = p->storageClass;
- if (p->type && p->type->mod & MODshared)
- stc &= ~STCshared;
-
- if (stcToBuffer(buf, stc & (STCconst | STCimmutable | STCwild | STCshared | STCscope | STCscopeinferred)))
- buf->writeByte(' ');
-
- if (p->storageClass & STCalias)
- {
- if (p->ident)
- buf->writestring(p->ident->toChars());
- }
- else if (p->type->ty == Tident &&
- strlen(((TypeIdentifier *)p->type)->ident->toChars()) > 3 &&
- strncmp(((TypeIdentifier *)p->type)->ident->toChars(), "__T", 3) == 0)
- {
- // print parameter name, instead of undetermined type parameter
- buf->writestring(p->ident->toChars());
- }
- else
- typeToBuffer(p->type, p->ident);
- if (p->defaultArg)
- {
- buf->writestring(" = ");
- p->defaultArg->accept(this);
- }
- }
-
- void parametersToBuffer(Parameters *parameters, int varargs)
- {
- buf->writeByte('(');
- if (parameters)
- {
- size_t dim = Parameter::dim(parameters);
- for (size_t i = 0; i < dim; i++)
- {
- if (i)
- buf->writestring(", ");
- Parameter *fparam = Parameter::getNth(parameters, i);
- fparam->accept(this);
- }
- if (varargs)
- {
- if (parameters->length && varargs == 1)
- buf->writestring(", ");
- buf->writestring("...");
- }
- }
- buf->writeByte(')');
- }
-
- void visit(Module *m)
- {
- if (m->md)
- {
- if (m->userAttribDecl)
- {
- buf->writestring("@(");
- argsToBuffer(m->userAttribDecl->atts);
- buf->writeByte(')');
- buf->writenl();
- }
- if (m->md->isdeprecated)
- {
- if (m->md->msg)
- {
- buf->writestring("deprecated(");
- m->md->msg->accept(this);
- buf->writestring(") ");
- }
- else
- buf->writestring("deprecated ");
- }
-
- buf->writestring("module ");
- buf->writestring(m->md->toChars());
- buf->writeByte(';');
- buf->writenl();
- }
- for (size_t i = 0; i < m->members->length; i++)
- {
- Dsymbol *s = (*m->members)[i];
- s->accept(this);
- }
- }
-};
-
-void toCBuffer(Statement *s, OutBuffer *buf, HdrGenState *hgs)
-{
- PrettyPrintVisitor v(buf, hgs);
- s->accept(&v);
-}
-
-void toCBuffer(Type *t, OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
-{
- PrettyPrintVisitor v(buf, hgs);
- v.typeToBuffer(t, ident);
-}
-
-void toCBuffer(Dsymbol *s, OutBuffer *buf, HdrGenState *hgs)
-{
- PrettyPrintVisitor v(buf, hgs);
- s->accept(&v);
-}
-
-// used from TemplateInstance::toChars() and TemplateMixin::toChars()
-void toCBufferInstance(TemplateInstance *ti, OutBuffer *buf, bool qualifyTypes)
-{
- HdrGenState hgs;
- hgs.fullQual = qualifyTypes;
- PrettyPrintVisitor v(buf, &hgs);
- v.visit(ti);
-}
-
-void toCBuffer(Initializer *iz, OutBuffer *buf, HdrGenState *hgs)
-{
- PrettyPrintVisitor v(buf, hgs);
- iz->accept(&v);
-}
-
-bool stcToBuffer(OutBuffer *buf, StorageClass stc)
-{
- bool result = false;
- if ((stc & (STCreturn | STCscope)) == (STCreturn | STCscope))
- stc &= ~STCscope;
- if (stc & STCscopeinferred)
- stc &= ~(STCscope | STCscopeinferred);
- while (stc)
- {
- const char *p = stcToChars(stc);
- if (!p)
- break;
- if (!result)
- result = true;
- else
- buf->writeByte(' ');
- buf->writestring(p);
- }
- return result;
-}
-
-/*************************************************
- * Pick off one of the storage classes from stc,
- * and return a pointer to a string representation of it.
- * stc is reduced by the one picked.
- */
-const char *stcToChars(StorageClass& stc)
-{
- struct SCstring
- {
- StorageClass stc;
- TOK tok;
- const char *id;
- };
-
- static SCstring table[] =
- {
- { STCauto, TOKauto, NULL },
- { STCscope, TOKscope, NULL },
- { STCstatic, TOKstatic, NULL },
- { STCextern, TOKextern, NULL },
- { STCconst, TOKconst, NULL },
- { STCfinal, TOKfinal, NULL },
- { STCabstract, TOKabstract, NULL },
- { STCsynchronized, TOKsynchronized, NULL },
- { STCdeprecated, TOKdeprecated, NULL },
- { STCoverride, TOKoverride, NULL },
- { STClazy, TOKlazy, NULL },
- { STCalias, TOKalias, NULL },
- { STCout, TOKout, NULL },
- { STCin, TOKin, NULL },
- { STCmanifest, TOKenum, NULL },
- { STCimmutable, TOKimmutable, NULL },
- { STCshared, TOKshared, NULL },
- { STCnothrow, TOKnothrow, NULL },
- { STCwild, TOKwild, NULL },
- { STCpure, TOKpure, NULL },
- { STCref, TOKref, NULL },
- { STCtls, TOKreserved, NULL },
- { STCgshared, TOKgshared, NULL },
- { STCnogc, TOKat, "@nogc" },
- { STCproperty, TOKat, "@property" },
- { STCsafe, TOKat, "@safe" },
- { STCtrusted, TOKat, "@trusted" },
- { STCsystem, TOKat, "@system" },
- { STCdisable, TOKat, "@disable" },
- { STCfuture, TOKat, "@__future" },
- { STClocal, TOKat, "__local" },
- { 0, TOKreserved, NULL }
- };
-
- for (int i = 0; table[i].stc; i++)
- {
- StorageClass tbl = table[i].stc;
- assert(tbl & STCStorageClass);
- if (stc & tbl)
- {
- stc &= ~tbl;
- if (tbl == STCtls) // TOKtls was removed
- return "__thread";
-
- TOK tok = table[i].tok;
- if (tok == TOKat)
- return table[i].id;
- else
- return Token::toChars(tok);
- }
- }
- //printf("stc = %llx\n", stc);
- return NULL;
-}
-
-void trustToBuffer(OutBuffer *buf, TRUST trust)
-{
- const char *p = trustToChars(trust);
- if (p)
- buf->writestring(p);
-}
-
-const char *trustToChars(TRUST trust)
-{
- switch (trust)
- {
- case TRUSTdefault: return NULL;
- case TRUSTsystem: return "@system";
- case TRUSTtrusted: return "@trusted";
- case TRUSTsafe: return "@safe";
- default: assert(0);
- }
- return NULL; // never reached
-}
-
-void linkageToBuffer(OutBuffer *buf, LINK linkage)
-{
- const char *p = linkageToChars(linkage);
- if (p)
- {
- buf->writestring("extern (");
- buf->writestring(p);
- buf->writeByte(')');
- }
-}
-
-const char *linkageToChars(LINK linkage)
-{
- switch (linkage)
- {
- case LINKdefault: return NULL;
- case LINKd: return "D";
- case LINKc: return "C";
- case LINKcpp: return "C++";
- case LINKwindows: return "Windows";
- case LINKobjc: return "Objective-C";
- case LINKsystem: return "System";
- default: assert(0);
- }
- return NULL; // never reached
-}
-
-void protectionToBuffer(OutBuffer *buf, Prot prot)
-{
- const char *p = protectionToChars(prot.kind);
- if (p)
- buf->writestring(p);
-
- if (prot.kind == Prot::package_ && prot.pkg)
- {
- buf->writeByte('(');
- buf->writestring(prot.pkg->toPrettyChars(true));
- buf->writeByte(')');
- }
-}
-
-const char *protectionToChars(Prot::Kind kind)
-{
- switch (kind)
- {
- case Prot::undefined: return NULL;
- case Prot::none: return "none";
- case Prot::private_: return "private";
- case Prot::package_: return "package";
- case Prot::protected_: return "protected";
- case Prot::public_: return "public";
- case Prot::export_: return "export";
- default: assert(0);
- }
- return NULL; // never reached
-}
-
-// Print the full function signature with correct ident, attributes and template args
-void functionToBufferFull(TypeFunction *tf, OutBuffer *buf, Identifier *ident,
- HdrGenState* hgs, TemplateDeclaration *td)
-{
- //printf("TypeFunction::toCBuffer() this = %p\n", this);
- PrettyPrintVisitor v(buf, hgs);
- v.visitFuncIdentWithPrefix(tf, ident, td);
-}
-
-// ident is inserted before the argument list and will be "function" or "delegate" for a type
-void functionToBufferWithIdent(TypeFunction *tf, OutBuffer *buf, const char *ident)
-{
- HdrGenState hgs;
- PrettyPrintVisitor v(buf, &hgs);
- v.visitFuncIdentWithPostfix(tf, ident);
-}
-
-void toCBuffer(Expression *e, OutBuffer *buf, HdrGenState *hgs)
-{
- PrettyPrintVisitor v(buf, hgs);
- e->accept(&v);
-}
-
-/**************************************************
- * Write out argument types to buf.
- */
-void argExpTypesToCBuffer(OutBuffer *buf, Expressions *arguments)
-{
- if (!arguments || !arguments->length)
- return;
-
- HdrGenState hgs;
- PrettyPrintVisitor v(buf, &hgs);
- for (size_t i = 0; i < arguments->length; i++)
- {
- Expression *arg = (*arguments)[i];
- if (i)
- buf->writestring(", ");
- v.typeToBuffer(arg->type, NULL);
- }
-}
-
-void toCBuffer(TemplateParameter *tp, OutBuffer *buf, HdrGenState *hgs)
-{
- PrettyPrintVisitor v(buf, hgs);
- tp->accept(&v);
-}
-
-void arrayObjectsToBuffer(OutBuffer *buf, Objects *objects)
-{
- if (!objects || !objects->length)
- return;
-
- HdrGenState hgs;
- PrettyPrintVisitor v(buf, &hgs);
- for (size_t i = 0; i < objects->length; i++)
- {
- RootObject *o = (*objects)[i];
- if (i)
- buf->writestring(", ");
- v.objectToBuffer(o);
- }
-}
-
-/*************************************************************
- * Pretty print function parameters.
- * Params:
- * parameters = parameters to print, such as TypeFunction.parameters.
- * varargs = kind of varargs, see TypeFunction.varargs.
- * Returns: Null-terminated string representing parameters.
- */
-const char *parametersTypeToChars(ParameterList pl)
-{
- OutBuffer buf;
- HdrGenState hgs;
- PrettyPrintVisitor v(&buf, &hgs);
- v.parametersToBuffer(pl.parameters, pl.varargs);
- return buf.extractChars();
-}
-
-/*************************************************************
- * Pretty print function parameter.
- * Params:
- * parameter = parameter to print.
- * tf = TypeFunction which holds parameter.
- * fullQual = whether to fully qualify types.
- * Returns: Null-terminated string representing parameters.
- */
-const char *parameterToChars(Parameter *parameter, TypeFunction *tf, bool fullQual)
-{
- OutBuffer buf;
- HdrGenState hgs;
- hgs.fullQual = fullQual;
- PrettyPrintVisitor v(&buf, &hgs);
-
- parameter->accept(&v);
- if (tf->parameterList.varargs == 2 && parameter == tf->parameterList[tf->parameterList.parameters->length - 1])
- {
- buf.writestring("...");
- }
- return buf.extractChars();
-}
diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d
new file mode 100644
index 00000000000..8c31590a311
--- /dev/null
+++ b/gcc/d/dmd/hdrgen.d
@@ -0,0 +1,3956 @@
+/**
+ * Generate $(LINK2 https://dlang.org/dmd-windows.html#interface-files, D interface files).
+ *
+ * Also used to convert AST nodes to D code in general, e.g. for error messages or `printf` debugging.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/hdrgen.d, _hdrgen.d)
+ * Documentation: https://dlang.org/phobos/dmd_hdrgen.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/hdrgen.d
+ */
+
+module dmd.hdrgen;
+
+import core.stdc.ctype;
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.complex;
+import dmd.cond;
+import dmd.ctfeexpr;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dmodule;
+import dmd.doc;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.dversion;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.nspace;
+import dmd.parse;
+import dmd.root.ctfloat;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.statement;
+import dmd.staticassert;
+import dmd.target;
+import dmd.tokens;
+import dmd.utils;
+import dmd.visitor;
+
+struct HdrGenState
+{
+ bool hdrgen; /// true if generating header file
+ bool ddoc; /// true if generating Ddoc file
+ bool fullDump; /// true if generating a full AST dump file
+
+ bool fullQual; /// fully qualify types when printing
+ int tpltMember;
+ int autoMember;
+ int forStmtInit;
+
+ bool declstring; // set while declaring alias for string,wstring or dstring
+ EnumDeclaration inEnumDecl;
+}
+
+enum TEST_EMIT_ALL = 0;
+
+extern (C++) void genhdrfile(Module m)
+{
+ OutBuffer buf;
+ buf.doindent = 1;
+ buf.printf("// D import file generated from '%s'", m.srcfile.toChars());
+ buf.writenl();
+ HdrGenState hgs;
+ hgs.hdrgen = true;
+ toCBuffer(m, &buf, &hgs);
+ writeFile(m.loc, m.hdrfile.toString(), buf[]);
+}
+
+/**
+ * Dumps the full contents of module `m` to `buf`.
+ * Params:
+ * buf = buffer to write to.
+ * m = module to visit all members of.
+ */
+extern (C++) void moduleToBuffer(OutBuffer* buf, Module m)
+{
+ HdrGenState hgs;
+ hgs.fullDump = true;
+ toCBuffer(m, buf, &hgs);
+}
+
+void moduleToBuffer2(Module m, OutBuffer* buf, HdrGenState* hgs)
+{
+ if (m.md)
+ {
+ if (m.userAttribDecl)
+ {
+ buf.writestring("@(");
+ argsToBuffer(m.userAttribDecl.atts, buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ }
+ if (m.md.isdeprecated)
+ {
+ if (m.md.msg)
+ {
+ buf.writestring("deprecated(");
+ m.md.msg.expressionToBuffer(buf, hgs);
+ buf.writestring(") ");
+ }
+ else
+ buf.writestring("deprecated ");
+ }
+ buf.writestring("module ");
+ buf.writestring(m.md.toChars());
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ foreach (s; *m.members)
+ {
+ s.dsymbolToBuffer(buf, hgs);
+ }
+}
+
+private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs)
+{
+ scope v = new StatementPrettyPrintVisitor(buf, hgs);
+ s.accept(v);
+}
+
+private extern (C++) final class StatementPrettyPrintVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ OutBuffer* buf;
+ HdrGenState* hgs;
+
+ extern (D) this(OutBuffer* buf, HdrGenState* hgs)
+ {
+ this.buf = buf;
+ this.hgs = hgs;
+ }
+
+ override void visit(Statement s)
+ {
+ buf.writestring("Statement::toCBuffer()");
+ buf.writenl();
+ assert(0);
+ }
+
+ override void visit(ErrorStatement s)
+ {
+ buf.writestring("__error__");
+ buf.writenl();
+ }
+
+ override void visit(ExpStatement s)
+ {
+ if (s.exp && s.exp.op == TOK.declaration &&
+ (cast(DeclarationExp)s.exp).declaration)
+ {
+ // bypass visit(DeclarationExp)
+ (cast(DeclarationExp)s.exp).declaration.dsymbolToBuffer(buf, hgs);
+ return;
+ }
+ if (s.exp)
+ s.exp.expressionToBuffer(buf, hgs);
+ buf.writeByte(';');
+ if (!hgs.forStmtInit)
+ buf.writenl();
+ }
+
+ override void visit(CompileStatement s)
+ {
+ buf.writestring("mixin(");
+ argsToBuffer(s.exps, buf, hgs, null);
+ buf.writestring(");");
+ if (!hgs.forStmtInit)
+ buf.writenl();
+ }
+
+ override void visit(CompoundStatement s)
+ {
+ foreach (sx; *s.statements)
+ {
+ if (sx)
+ sx.accept(this);
+ }
+ }
+
+ override void visit(CompoundDeclarationStatement s)
+ {
+ bool anywritten = false;
+ foreach (sx; *s.statements)
+ {
+ auto ds = sx ? sx.isExpStatement() : null;
+ if (ds && ds.exp.op == TOK.declaration)
+ {
+ auto d = (cast(DeclarationExp)ds.exp).declaration;
+ assert(d.isDeclaration());
+ if (auto v = d.isVarDeclaration())
+ {
+ scope ppv = new DsymbolPrettyPrintVisitor(buf, hgs);
+ ppv.visitVarDecl(v, anywritten);
+ }
+ else
+ d.dsymbolToBuffer(buf, hgs);
+ anywritten = true;
+ }
+ }
+ buf.writeByte(';');
+ if (!hgs.forStmtInit)
+ buf.writenl();
+ }
+
+ override void visit(UnrolledLoopStatement s)
+ {
+ buf.writestring("/*unrolled*/ {");
+ buf.writenl();
+ buf.level++;
+ foreach (sx; *s.statements)
+ {
+ if (sx)
+ sx.accept(this);
+ }
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+
+ override void visit(ScopeStatement s)
+ {
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ if (s.statement)
+ s.statement.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+
+ override void visit(WhileStatement s)
+ {
+ buf.writestring("while (");
+ if (auto p = s.param)
+ {
+ // Print condition assignment
+ StorageClass stc = p.storageClass;
+ if (!p.type && !stc)
+ stc = STC.auto_;
+ if (stcToBuffer(buf, stc))
+ buf.writeByte(' ');
+ if (p.type)
+ typeToBuffer(p.type, p.ident, buf, hgs);
+ else
+ buf.writestring(p.ident.toString());
+ buf.writestring(" = ");
+ }
+ s.condition.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(DoStatement s)
+ {
+ buf.writestring("do");
+ buf.writenl();
+ if (s._body)
+ s._body.accept(this);
+ buf.writestring("while (");
+ s.condition.expressionToBuffer(buf, hgs);
+ buf.writestring(");");
+ buf.writenl();
+ }
+
+ override void visit(ForStatement s)
+ {
+ buf.writestring("for (");
+ if (s._init)
+ {
+ hgs.forStmtInit++;
+ s._init.accept(this);
+ hgs.forStmtInit--;
+ }
+ else
+ buf.writeByte(';');
+ if (s.condition)
+ {
+ buf.writeByte(' ');
+ s.condition.expressionToBuffer(buf, hgs);
+ }
+ buf.writeByte(';');
+ if (s.increment)
+ {
+ buf.writeByte(' ');
+ s.increment.expressionToBuffer(buf, hgs);
+ }
+ buf.writeByte(')');
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ if (s._body)
+ s._body.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+
+ private void foreachWithoutBody(ForeachStatement s)
+ {
+ buf.writestring(Token.toString(s.op));
+ buf.writestring(" (");
+ foreach (i, p; *s.parameters)
+ {
+ if (i)
+ buf.writestring(", ");
+ if (stcToBuffer(buf, p.storageClass))
+ buf.writeByte(' ');
+ if (p.type)
+ typeToBuffer(p.type, p.ident, buf, hgs);
+ else
+ buf.writestring(p.ident.toString());
+ }
+ buf.writestring("; ");
+ s.aggr.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ }
+
+ override void visit(ForeachStatement s)
+ {
+ foreachWithoutBody(s);
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ if (s._body)
+ s._body.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+
+ private void foreachRangeWithoutBody(ForeachRangeStatement s)
+ {
+ buf.writestring(Token.toString(s.op));
+ buf.writestring(" (");
+ if (s.prm.type)
+ typeToBuffer(s.prm.type, s.prm.ident, buf, hgs);
+ else
+ buf.writestring(s.prm.ident.toString());
+ buf.writestring("; ");
+ s.lwr.expressionToBuffer(buf, hgs);
+ buf.writestring(" .. ");
+ s.upr.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ }
+
+ override void visit(ForeachRangeStatement s)
+ {
+ foreachRangeWithoutBody(s);
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ if (s._body)
+ s._body.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+
+ override void visit(StaticForeachStatement s)
+ {
+ buf.writestring("static ");
+ if (s.sfe.aggrfe)
+ {
+ visit(s.sfe.aggrfe);
+ }
+ else
+ {
+ assert(s.sfe.rangefe);
+ visit(s.sfe.rangefe);
+ }
+ }
+
+ override void visit(ForwardingStatement s)
+ {
+ s.statement.accept(this);
+ }
+
+ override void visit(IfStatement s)
+ {
+ buf.writestring("if (");
+ if (Parameter p = s.prm)
+ {
+ StorageClass stc = p.storageClass;
+ if (!p.type && !stc)
+ stc = STC.auto_;
+ if (stcToBuffer(buf, stc))
+ buf.writeByte(' ');
+ if (p.type)
+ typeToBuffer(p.type, p.ident, buf, hgs);
+ else
+ buf.writestring(p.ident.toString());
+ buf.writestring(" = ");
+ }
+ s.condition.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ if (s.ifbody.isScopeStatement())
+ {
+ s.ifbody.accept(this);
+ }
+ else
+ {
+ buf.level++;
+ s.ifbody.accept(this);
+ buf.level--;
+ }
+ if (s.elsebody)
+ {
+ buf.writestring("else");
+ if (!s.elsebody.isIfStatement())
+ {
+ buf.writenl();
+ }
+ else
+ {
+ buf.writeByte(' ');
+ }
+ if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement())
+ {
+ s.elsebody.accept(this);
+ }
+ else
+ {
+ buf.level++;
+ s.elsebody.accept(this);
+ buf.level--;
+ }
+ }
+ }
+
+ override void visit(ConditionalStatement s)
+ {
+ s.condition.conditionToBuffer(buf, hgs);
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ if (s.ifbody)
+ s.ifbody.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ if (s.elsebody)
+ {
+ buf.writestring("else");
+ buf.writenl();
+ buf.writeByte('{');
+ buf.level++;
+ buf.writenl();
+ s.elsebody.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ }
+ buf.writenl();
+ }
+
+ override void visit(PragmaStatement s)
+ {
+ buf.writestring("pragma (");
+ buf.writestring(s.ident.toString());
+ if (s.args && s.args.dim)
+ {
+ buf.writestring(", ");
+ argsToBuffer(s.args, buf, hgs);
+ }
+ buf.writeByte(')');
+ if (s._body)
+ {
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ s._body.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+ else
+ {
+ buf.writeByte(';');
+ buf.writenl();
+ }
+ }
+
+ override void visit(StaticAssertStatement s)
+ {
+ s.sa.dsymbolToBuffer(buf, hgs);
+ }
+
+ override void visit(SwitchStatement s)
+ {
+ buf.writestring(s.isFinal ? "final switch (" : "switch (");
+ s.condition.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ if (s._body)
+ {
+ if (!s._body.isScopeStatement())
+ {
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ s._body.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+ else
+ {
+ s._body.accept(this);
+ }
+ }
+ }
+
+ override void visit(CaseStatement s)
+ {
+ buf.writestring("case ");
+ s.exp.expressionToBuffer(buf, hgs);
+ buf.writeByte(':');
+ buf.writenl();
+ s.statement.accept(this);
+ }
+
+ override void visit(CaseRangeStatement s)
+ {
+ buf.writestring("case ");
+ s.first.expressionToBuffer(buf, hgs);
+ buf.writestring(": .. case ");
+ s.last.expressionToBuffer(buf, hgs);
+ buf.writeByte(':');
+ buf.writenl();
+ s.statement.accept(this);
+ }
+
+ override void visit(DefaultStatement s)
+ {
+ buf.writestring("default:");
+ buf.writenl();
+ s.statement.accept(this);
+ }
+
+ override void visit(GotoDefaultStatement s)
+ {
+ buf.writestring("goto default;");
+ buf.writenl();
+ }
+
+ override void visit(GotoCaseStatement s)
+ {
+ buf.writestring("goto case");
+ if (s.exp)
+ {
+ buf.writeByte(' ');
+ s.exp.expressionToBuffer(buf, hgs);
+ }
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(SwitchErrorStatement s)
+ {
+ buf.writestring("SwitchErrorStatement::toCBuffer()");
+ buf.writenl();
+ }
+
+ override void visit(ReturnStatement s)
+ {
+ buf.writestring("return ");
+ if (s.exp)
+ s.exp.expressionToBuffer(buf, hgs);
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(BreakStatement s)
+ {
+ buf.writestring("break");
+ if (s.ident)
+ {
+ buf.writeByte(' ');
+ buf.writestring(s.ident.toString());
+ }
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(ContinueStatement s)
+ {
+ buf.writestring("continue");
+ if (s.ident)
+ {
+ buf.writeByte(' ');
+ buf.writestring(s.ident.toString());
+ }
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(SynchronizedStatement s)
+ {
+ buf.writestring("synchronized");
+ if (s.exp)
+ {
+ buf.writeByte('(');
+ s.exp.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ }
+ if (s._body)
+ {
+ buf.writeByte(' ');
+ s._body.accept(this);
+ }
+ }
+
+ override void visit(WithStatement s)
+ {
+ buf.writestring("with (");
+ s.exp.expressionToBuffer(buf, hgs);
+ buf.writestring(")");
+ buf.writenl();
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(TryCatchStatement s)
+ {
+ buf.writestring("try");
+ buf.writenl();
+ if (s._body)
+ {
+ if (s._body.isScopeStatement())
+ {
+ s._body.accept(this);
+ }
+ else
+ {
+ buf.level++;
+ s._body.accept(this);
+ buf.level--;
+ }
+ }
+ foreach (c; *s.catches)
+ {
+ visit(c);
+ }
+ }
+
+ override void visit(TryFinallyStatement s)
+ {
+ buf.writestring("try");
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ s._body.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ buf.writestring("finally");
+ buf.writenl();
+ if (s.finalbody.isScopeStatement())
+ {
+ s.finalbody.accept(this);
+ }
+ else
+ {
+ buf.level++;
+ s.finalbody.accept(this);
+ buf.level--;
+ }
+ }
+
+ override void visit(ScopeGuardStatement s)
+ {
+ buf.writestring(Token.toString(s.tok));
+ buf.writeByte(' ');
+ if (s.statement)
+ s.statement.accept(this);
+ }
+
+ override void visit(ThrowStatement s)
+ {
+ buf.writestring("throw ");
+ s.exp.expressionToBuffer(buf, hgs);
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(DebugStatement s)
+ {
+ if (s.statement)
+ {
+ s.statement.accept(this);
+ }
+ }
+
+ override void visit(GotoStatement s)
+ {
+ buf.writestring("goto ");
+ buf.writestring(s.ident.toString());
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(LabelStatement s)
+ {
+ buf.writestring(s.ident.toString());
+ buf.writeByte(':');
+ buf.writenl();
+ if (s.statement)
+ s.statement.accept(this);
+ }
+
+ override void visit(AsmStatement s)
+ {
+ buf.writestring("asm { ");
+ Token* t = s.tokens;
+ buf.level++;
+ while (t)
+ {
+ buf.writestring(t.toChars());
+ if (t.next &&
+ t.value != TOK.min &&
+ t.value != TOK.comma && t.next.value != TOK.comma &&
+ t.value != TOK.leftBracket && t.next.value != TOK.leftBracket &&
+ t.next.value != TOK.rightBracket &&
+ t.value != TOK.leftParenthesis && t.next.value != TOK.leftParenthesis &&
+ t.next.value != TOK.rightParenthesis &&
+ t.value != TOK.dot && t.next.value != TOK.dot)
+ {
+ buf.writeByte(' ');
+ }
+ t = t.next;
+ }
+ buf.level--;
+ buf.writestring("; }");
+ buf.writenl();
+ }
+
+ override void visit(ImportStatement s)
+ {
+ foreach (imp; *s.imports)
+ {
+ imp.dsymbolToBuffer(buf, hgs);
+ }
+ }
+
+ void visit(Catch c)
+ {
+ buf.writestring("catch");
+ if (c.type)
+ {
+ buf.writeByte('(');
+ typeToBuffer(c.type, c.ident, buf, hgs);
+ buf.writeByte(')');
+ }
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ if (c.handler)
+ c.handler.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+}
+
+private void dsymbolToBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
+{
+ scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
+ s.accept(v);
+}
+
+private extern (C++) final class DsymbolPrettyPrintVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ OutBuffer* buf;
+ HdrGenState* hgs;
+
+ extern (D) this(OutBuffer* buf, HdrGenState* hgs)
+ {
+ this.buf = buf;
+ this.hgs = hgs;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ override void visit(Dsymbol s)
+ {
+ buf.writestring(s.toChars());
+ }
+
+ override void visit(StaticAssert s)
+ {
+ buf.writestring(s.kind());
+ buf.writeByte('(');
+ s.exp.expressionToBuffer(buf, hgs);
+ if (s.msg)
+ {
+ buf.writestring(", ");
+ s.msg.expressionToBuffer(buf, hgs);
+ }
+ buf.writestring(");");
+ buf.writenl();
+ }
+
+ override void visit(DebugSymbol s)
+ {
+ buf.writestring("debug = ");
+ if (s.ident)
+ buf.writestring(s.ident.toString());
+ else
+ buf.print(s.level);
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(VersionSymbol s)
+ {
+ buf.writestring("version = ");
+ if (s.ident)
+ buf.writestring(s.ident.toString());
+ else
+ buf.print(s.level);
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(EnumMember em)
+ {
+ if (em.type)
+ typeToBuffer(em.type, em.ident, buf, hgs);
+ else
+ buf.writestring(em.ident.toString());
+ if (em.value)
+ {
+ buf.writestring(" = ");
+ em.value.expressionToBuffer(buf, hgs);
+ }
+ }
+
+ override void visit(Import imp)
+ {
+ if (hgs.hdrgen && imp.id == Id.object)
+ return; // object is imported by default
+ if (imp.isstatic)
+ buf.writestring("static ");
+ buf.writestring("import ");
+ if (imp.aliasId)
+ {
+ buf.printf("%s = ", imp.aliasId.toChars());
+ }
+ foreach (const pid; imp.packages)
+ {
+ buf.printf("%s.", pid.toChars());
+ }
+ buf.writestring(imp.id.toString());
+ if (imp.names.dim)
+ {
+ buf.writestring(" : ");
+ foreach (const i, const name; imp.names)
+ {
+ if (i)
+ buf.writestring(", ");
+ const _alias = imp.aliases[i];
+ if (_alias)
+ buf.printf("%s = %s", _alias.toChars(), name.toChars());
+ else
+ buf.writestring(name.toChars());
+ }
+ }
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(AliasThis d)
+ {
+ buf.writestring("alias ");
+ buf.writestring(d.ident.toString());
+ buf.writestring(" this;\n");
+ }
+
+ override void visit(AttribDeclaration d)
+ {
+ if (!d.decl)
+ {
+ buf.writeByte(';');
+ buf.writenl();
+ return;
+ }
+ if (d.decl.dim == 0 || (hgs.hdrgen && d.decl.dim == 1 && (*d.decl)[0].isUnitTestDeclaration()))
+ {
+ // hack for bugzilla 8081
+ buf.writestring("{}");
+ }
+ else if (d.decl.dim == 1)
+ {
+ (*d.decl)[0].accept(this);
+ return;
+ }
+ else
+ {
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ foreach (de; *d.decl)
+ de.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ }
+ buf.writenl();
+ }
+
+ override void visit(StorageClassDeclaration d)
+ {
+ if (stcToBuffer(buf, d.stc))
+ buf.writeByte(' ');
+ visit(cast(AttribDeclaration)d);
+ }
+
+ override void visit(DeprecatedDeclaration d)
+ {
+ buf.writestring("deprecated(");
+ d.msg.expressionToBuffer(buf, hgs);
+ buf.writestring(") ");
+ visit(cast(AttribDeclaration)d);
+ }
+
+ override void visit(LinkDeclaration d)
+ {
+ buf.writestring("extern (");
+ buf.writestring(linkageToString(d.linkage));
+ buf.writestring(") ");
+ visit(cast(AttribDeclaration)d);
+ }
+
+ override void visit(CPPMangleDeclaration d)
+ {
+ string s;
+ final switch (d.cppmangle)
+ {
+ case CPPMANGLE.asClass:
+ s = "class";
+ break;
+ case CPPMANGLE.asStruct:
+ s = "struct";
+ break;
+ case CPPMANGLE.def:
+ break;
+ }
+ buf.writestring("extern (C++, ");
+ buf.writestring(s);
+ buf.writestring(") ");
+ visit(cast(AttribDeclaration)d);
+ }
+
+ override void visit(VisibilityDeclaration d)
+ {
+ visibilityToBuffer(buf, d.visibility);
+ buf.writeByte(' ');
+ AttribDeclaration ad = cast(AttribDeclaration)d;
+ if (ad.decl.dim == 1 && (*ad.decl)[0].isVisibilityDeclaration)
+ visit(cast(AttribDeclaration)(*ad.decl)[0]);
+ else
+ visit(cast(AttribDeclaration)d);
+ }
+
+ override void visit(AlignDeclaration d)
+ {
+ if (d.exps)
+ {
+ foreach (i, exp; (*d.exps)[])
+ {
+ if (i)
+ buf.writeByte(' ');
+ buf.printf("align (%s)", exp.toChars());
+ }
+ if (d.decl && d.decl.dim < 2)
+ buf.writeByte(' ');
+ }
+ else
+ buf.writestring("align ");
+
+ visit(d.isAttribDeclaration());
+ }
+
+ override void visit(AnonDeclaration d)
+ {
+ buf.writestring(d.isunion ? "union" : "struct");
+ buf.writenl();
+ buf.writestring("{");
+ buf.writenl();
+ buf.level++;
+ if (d.decl)
+ {
+ foreach (de; *d.decl)
+ de.accept(this);
+ }
+ buf.level--;
+ buf.writestring("}");
+ buf.writenl();
+ }
+
+ override void visit(PragmaDeclaration d)
+ {
+ buf.writestring("pragma (");
+ buf.writestring(d.ident.toString());
+ if (d.args && d.args.dim)
+ {
+ buf.writestring(", ");
+ argsToBuffer(d.args, buf, hgs);
+ }
+ buf.writeByte(')');
+ visit(cast(AttribDeclaration)d);
+ }
+
+ override void visit(ConditionalDeclaration d)
+ {
+ d.condition.conditionToBuffer(buf, hgs);
+ if (d.decl || d.elsedecl)
+ {
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ if (d.decl)
+ {
+ foreach (de; *d.decl)
+ de.accept(this);
+ }
+ buf.level--;
+ buf.writeByte('}');
+ if (d.elsedecl)
+ {
+ buf.writenl();
+ buf.writestring("else");
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ foreach (de; *d.elsedecl)
+ de.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ }
+ }
+ else
+ buf.writeByte(':');
+ buf.writenl();
+ }
+
+ override void visit(StaticForeachDeclaration s)
+ {
+ void foreachWithoutBody(ForeachStatement s)
+ {
+ buf.writestring(Token.toString(s.op));
+ buf.writestring(" (");
+ foreach (i, p; *s.parameters)
+ {
+ if (i)
+ buf.writestring(", ");
+ if (stcToBuffer(buf, p.storageClass))
+ buf.writeByte(' ');
+ if (p.type)
+ typeToBuffer(p.type, p.ident, buf, hgs);
+ else
+ buf.writestring(p.ident.toString());
+ }
+ buf.writestring("; ");
+ s.aggr.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ }
+
+ void foreachRangeWithoutBody(ForeachRangeStatement s)
+ {
+ /* s.op ( prm ; lwr .. upr )
+ */
+ buf.writestring(Token.toString(s.op));
+ buf.writestring(" (");
+ if (s.prm.type)
+ typeToBuffer(s.prm.type, s.prm.ident, buf, hgs);
+ else
+ buf.writestring(s.prm.ident.toString());
+ buf.writestring("; ");
+ s.lwr.expressionToBuffer(buf, hgs);
+ buf.writestring(" .. ");
+ s.upr.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ }
+
+ buf.writestring("static ");
+ if (s.sfe.aggrfe)
+ {
+ foreachWithoutBody(s.sfe.aggrfe);
+ }
+ else
+ {
+ assert(s.sfe.rangefe);
+ foreachRangeWithoutBody(s.sfe.rangefe);
+ }
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ visit(cast(AttribDeclaration)s);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+
+ }
+
+ override void visit(CompileDeclaration d)
+ {
+ buf.writestring("mixin(");
+ argsToBuffer(d.exps, buf, hgs, null);
+ buf.writestring(");");
+ buf.writenl();
+ }
+
+ override void visit(UserAttributeDeclaration d)
+ {
+ buf.writestring("@(");
+ argsToBuffer(d.atts, buf, hgs);
+ buf.writeByte(')');
+ visit(cast(AttribDeclaration)d);
+ }
+
+ override void visit(TemplateDeclaration d)
+ {
+ version (none)
+ {
+ // Should handle template functions for doc generation
+ if (onemember && onemember.isFuncDeclaration())
+ buf.writestring("foo ");
+ }
+ if ((hgs.hdrgen || hgs.fullDump) && visitEponymousMember(d))
+ return;
+ if (hgs.ddoc)
+ buf.writestring(d.kind());
+ else
+ buf.writestring("template");
+ buf.writeByte(' ');
+ buf.writestring(d.ident.toString());
+ buf.writeByte('(');
+ visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
+ buf.writeByte(')');
+ visitTemplateConstraint(d.constraint);
+ if (hgs.hdrgen || hgs.fullDump)
+ {
+ hgs.tpltMember++;
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ foreach (s; *d.members)
+ s.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ hgs.tpltMember--;
+ }
+ }
+
+ bool visitEponymousMember(TemplateDeclaration d)
+ {
+ if (!d.members || d.members.dim != 1)
+ return false;
+ Dsymbol onemember = (*d.members)[0];
+ if (onemember.ident != d.ident)
+ return false;
+ if (FuncDeclaration fd = onemember.isFuncDeclaration())
+ {
+ assert(fd.type);
+ if (stcToBuffer(buf, fd.storage_class))
+ buf.writeByte(' ');
+ functionToBufferFull(cast(TypeFunction)fd.type, buf, d.ident, hgs, d);
+ visitTemplateConstraint(d.constraint);
+ hgs.tpltMember++;
+ bodyToBuffer(fd);
+ hgs.tpltMember--;
+ return true;
+ }
+ if (AggregateDeclaration ad = onemember.isAggregateDeclaration())
+ {
+ buf.writestring(ad.kind());
+ buf.writeByte(' ');
+ buf.writestring(ad.ident.toString());
+ buf.writeByte('(');
+ visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
+ buf.writeByte(')');
+ visitTemplateConstraint(d.constraint);
+ visitBaseClasses(ad.isClassDeclaration());
+ hgs.tpltMember++;
+ if (ad.members)
+ {
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ foreach (s; *ad.members)
+ s.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ }
+ else
+ buf.writeByte(';');
+ buf.writenl();
+ hgs.tpltMember--;
+ return true;
+ }
+ if (VarDeclaration vd = onemember.isVarDeclaration())
+ {
+ if (d.constraint)
+ return false;
+ if (stcToBuffer(buf, vd.storage_class))
+ buf.writeByte(' ');
+ if (vd.type)
+ typeToBuffer(vd.type, vd.ident, buf, hgs);
+ else
+ buf.writestring(vd.ident.toString());
+ buf.writeByte('(');
+ visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters);
+ buf.writeByte(')');
+ if (vd._init)
+ {
+ buf.writestring(" = ");
+ ExpInitializer ie = vd._init.isExpInitializer();
+ if (ie && (ie.exp.op == TOK.construct || ie.exp.op == TOK.blit))
+ (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
+ else
+ vd._init.initializerToBuffer(buf, hgs);
+ }
+ buf.writeByte(';');
+ buf.writenl();
+ return true;
+ }
+ return false;
+ }
+
+ void visitTemplateParameters(TemplateParameters* parameters)
+ {
+ if (!parameters || !parameters.dim)
+ return;
+ foreach (i, p; *parameters)
+ {
+ if (i)
+ buf.writestring(", ");
+ p.templateParameterToBuffer(buf, hgs);
+ }
+ }
+
+ void visitTemplateConstraint(Expression constraint)
+ {
+ if (!constraint)
+ return;
+ buf.writestring(" if (");
+ constraint.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ }
+
+ override void visit(TemplateInstance ti)
+ {
+ buf.writestring(ti.name.toChars());
+ tiargsToBuffer(ti, buf, hgs);
+
+ if (hgs.fullDump)
+ {
+ buf.writenl();
+ dumpTemplateInstance(ti, buf, hgs);
+ }
+ }
+
+ override void visit(TemplateMixin tm)
+ {
+ buf.writestring("mixin ");
+ typeToBuffer(tm.tqual, null, buf, hgs);
+ tiargsToBuffer(tm, buf, hgs);
+ if (tm.ident && memcmp(tm.ident.toChars(), cast(const(char)*)"__mixin", 7) != 0)
+ {
+ buf.writeByte(' ');
+ buf.writestring(tm.ident.toString());
+ }
+ buf.writeByte(';');
+ buf.writenl();
+ if (hgs.fullDump)
+ dumpTemplateInstance(tm, buf, hgs);
+ }
+
+ override void visit(EnumDeclaration d)
+ {
+ auto oldInEnumDecl = hgs.inEnumDecl;
+ scope(exit) hgs.inEnumDecl = oldInEnumDecl;
+ hgs.inEnumDecl = d;
+ buf.writestring("enum ");
+ if (d.ident)
+ {
+ buf.writestring(d.ident.toString());
+ buf.writeByte(' ');
+ }
+ if (d.memtype)
+ {
+ buf.writestring(": ");
+ typeToBuffer(d.memtype, null, buf, hgs);
+ }
+ if (!d.members)
+ {
+ buf.writeByte(';');
+ buf.writenl();
+ return;
+ }
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ foreach (em; *d.members)
+ {
+ if (!em)
+ continue;
+ em.accept(this);
+ buf.writeByte(',');
+ buf.writenl();
+ }
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+
+ override void visit(Nspace d)
+ {
+ buf.writestring("extern (C++, ");
+ buf.writestring(d.ident.toString());
+ buf.writeByte(')');
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ foreach (s; *d.members)
+ s.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+
+ override void visit(StructDeclaration d)
+ {
+ buf.writestring(d.kind());
+ buf.writeByte(' ');
+ if (!d.isAnonymous())
+ buf.writestring(d.toChars());
+ if (!d.members)
+ {
+ buf.writeByte(';');
+ buf.writenl();
+ return;
+ }
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ foreach (s; *d.members)
+ s.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ }
+
+ override void visit(ClassDeclaration d)
+ {
+ if (!d.isAnonymous())
+ {
+ buf.writestring(d.kind());
+ buf.writeByte(' ');
+ buf.writestring(d.ident.toString());
+ }
+ visitBaseClasses(d);
+ if (d.members)
+ {
+ buf.writenl();
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ foreach (s; *d.members)
+ s.accept(this);
+ buf.level--;
+ buf.writeByte('}');
+ }
+ else
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ void visitBaseClasses(ClassDeclaration d)
+ {
+ if (!d || !d.baseclasses.dim)
+ return;
+ if (!d.isAnonymous())
+ buf.writestring(" : ");
+ foreach (i, b; *d.baseclasses)
+ {
+ if (i)
+ buf.writestring(", ");
+ typeToBuffer(b.type, null, buf, hgs);
+ }
+ }
+
+ override void visit(AliasDeclaration d)
+ {
+ if (d.storage_class & STC.local)
+ return;
+ buf.writestring("alias ");
+ if (d.aliassym)
+ {
+ buf.writestring(d.ident.toString());
+ buf.writestring(" = ");
+ if (stcToBuffer(buf, d.storage_class))
+ buf.writeByte(' ');
+ d.aliassym.accept(this);
+ }
+ else if (d.type.ty == Tfunction)
+ {
+ if (stcToBuffer(buf, d.storage_class))
+ buf.writeByte(' ');
+ typeToBuffer(d.type, d.ident, buf, hgs);
+ }
+ else if (d.ident)
+ {
+ hgs.declstring = (d.ident == Id.string || d.ident == Id.wstring || d.ident == Id.dstring);
+ buf.writestring(d.ident.toString());
+ buf.writestring(" = ");
+ if (stcToBuffer(buf, d.storage_class))
+ buf.writeByte(' ');
+ typeToBuffer(d.type, null, buf, hgs);
+ hgs.declstring = false;
+ }
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(AliasAssign d)
+ {
+ buf.writestring(d.ident.toString());
+ buf.writestring(" = ");
+ if (d.aliassym)
+ d.aliassym.accept(this);
+ else // d.type
+ typeToBuffer(d.type, null, buf, hgs);
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(VarDeclaration d)
+ {
+ if (d.storage_class & STC.local)
+ return;
+ visitVarDecl(d, false);
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ void visitVarDecl(VarDeclaration v, bool anywritten)
+ {
+ if (anywritten)
+ {
+ buf.writestring(", ");
+ buf.writestring(v.ident.toString());
+ }
+ else
+ {
+ if (stcToBuffer(buf, v.storage_class))
+ buf.writeByte(' ');
+ if (v.type)
+ typeToBuffer(v.type, v.ident, buf, hgs);
+ else
+ buf.writestring(v.ident.toString());
+ }
+ if (v._init)
+ {
+ buf.writestring(" = ");
+ auto ie = v._init.isExpInitializer();
+ if (ie && (ie.exp.op == TOK.construct || ie.exp.op == TOK.blit))
+ (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
+ else
+ v._init.initializerToBuffer(buf, hgs);
+ }
+ }
+
+ override void visit(FuncDeclaration f)
+ {
+ //printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars());
+ if (stcToBuffer(buf, f.storage_class))
+ buf.writeByte(' ');
+ auto tf = cast(TypeFunction)f.type;
+ typeToBuffer(tf, f.ident, buf, hgs);
+
+ if (hgs.hdrgen)
+ {
+ // if the return type is missing (e.g. ref functions or auto)
+ if (!tf.next || f.storage_class & STC.auto_)
+ {
+ hgs.autoMember++;
+ bodyToBuffer(f);
+ hgs.autoMember--;
+ }
+ else if (hgs.tpltMember == 0 && global.params.hdrStripPlainFunctions)
+ {
+ buf.writeByte(';');
+ buf.writenl();
+ }
+ else
+ bodyToBuffer(f);
+ }
+ else
+ bodyToBuffer(f);
+ }
+
+ void bodyToBuffer(FuncDeclaration f)
+ {
+ if (!f.fbody || (hgs.hdrgen && global.params.hdrStripPlainFunctions && !hgs.autoMember && !hgs.tpltMember))
+ {
+ buf.writeByte(';');
+ buf.writenl();
+ return;
+ }
+ const savetlpt = hgs.tpltMember;
+ const saveauto = hgs.autoMember;
+ hgs.tpltMember = 0;
+ hgs.autoMember = 0;
+ buf.writenl();
+ bool requireDo = false;
+ // in{}
+ if (f.frequires)
+ {
+ foreach (frequire; *f.frequires)
+ {
+ buf.writestring("in");
+ if (auto es = frequire.isExpStatement())
+ {
+ assert(es.exp && es.exp.op == TOK.assert_);
+ buf.writestring(" (");
+ (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ requireDo = false;
+ }
+ else
+ {
+ buf.writenl();
+ frequire.statementToBuffer(buf, hgs);
+ requireDo = true;
+ }
+ }
+ }
+ // out{}
+ if (f.fensures)
+ {
+ foreach (fensure; *f.fensures)
+ {
+ buf.writestring("out");
+ if (auto es = fensure.ensure.isExpStatement())
+ {
+ assert(es.exp && es.exp.op == TOK.assert_);
+ buf.writestring(" (");
+ if (fensure.id)
+ {
+ buf.writestring(fensure.id.toString());
+ }
+ buf.writestring("; ");
+ (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ buf.writenl();
+ requireDo = false;
+ }
+ else
+ {
+ if (fensure.id)
+ {
+ buf.writeByte('(');
+ buf.writestring(fensure.id.toString());
+ buf.writeByte(')');
+ }
+ buf.writenl();
+ fensure.ensure.statementToBuffer(buf, hgs);
+ requireDo = true;
+ }
+ }
+ }
+ if (requireDo)
+ {
+ buf.writestring("do");
+ buf.writenl();
+ }
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+ f.fbody.statementToBuffer(buf, hgs);
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+ hgs.tpltMember = savetlpt;
+ hgs.autoMember = saveauto;
+ }
+
+ override void visit(FuncLiteralDeclaration f)
+ {
+ if (f.type.ty == Terror)
+ {
+ buf.writestring("__error");
+ return;
+ }
+ if (f.tok != TOK.reserved)
+ {
+ buf.writestring(f.kind());
+ buf.writeByte(' ');
+ }
+ TypeFunction tf = cast(TypeFunction)f.type;
+
+ if (!f.inferRetType && tf.next)
+ typeToBuffer(tf.next, null, buf, hgs);
+ parametersToBuffer(tf.parameterList, buf, hgs);
+
+ // https://issues.dlang.org/show_bug.cgi?id=20074
+ void printAttribute(string str)
+ {
+ buf.writeByte(' ');
+ buf.writestring(str);
+ }
+ tf.attributesApply(&printAttribute);
+
+
+ CompoundStatement cs = f.fbody.isCompoundStatement();
+ Statement s1;
+ if (f.semanticRun >= PASS.semantic3done && cs)
+ {
+ s1 = (*cs.statements)[cs.statements.dim - 1];
+ }
+ else
+ s1 = !cs ? f.fbody : null;
+ ReturnStatement rs = s1 ? s1.endsWithReturnStatement() : null;
+ if (rs && rs.exp)
+ {
+ buf.writestring(" => ");
+ rs.exp.expressionToBuffer(buf, hgs);
+ }
+ else
+ {
+ hgs.tpltMember++;
+ bodyToBuffer(f);
+ hgs.tpltMember--;
+ }
+ }
+
+ override void visit(PostBlitDeclaration d)
+ {
+ if (stcToBuffer(buf, d.storage_class))
+ buf.writeByte(' ');
+ buf.writestring("this(this)");
+ bodyToBuffer(d);
+ }
+
+ override void visit(DtorDeclaration d)
+ {
+ if (d.storage_class & STC.trusted)
+ buf.writestring("@trusted ");
+ if (d.storage_class & STC.safe)
+ buf.writestring("@safe ");
+ if (d.storage_class & STC.nogc)
+ buf.writestring("@nogc ");
+ if (d.storage_class & STC.live)
+ buf.writestring("@live ");
+ if (d.storage_class & STC.disable)
+ buf.writestring("@disable ");
+
+ buf.writestring("~this()");
+ bodyToBuffer(d);
+ }
+
+ override void visit(StaticCtorDeclaration d)
+ {
+ if (stcToBuffer(buf, d.storage_class & ~STC.static_))
+ buf.writeByte(' ');
+ if (d.isSharedStaticCtorDeclaration())
+ buf.writestring("shared ");
+ buf.writestring("static this()");
+ if (hgs.hdrgen && !hgs.tpltMember)
+ {
+ buf.writeByte(';');
+ buf.writenl();
+ }
+ else
+ bodyToBuffer(d);
+ }
+
+ override void visit(StaticDtorDeclaration d)
+ {
+ if (stcToBuffer(buf, d.storage_class & ~STC.static_))
+ buf.writeByte(' ');
+ if (d.isSharedStaticDtorDeclaration())
+ buf.writestring("shared ");
+ buf.writestring("static ~this()");
+ if (hgs.hdrgen && !hgs.tpltMember)
+ {
+ buf.writeByte(';');
+ buf.writenl();
+ }
+ else
+ bodyToBuffer(d);
+ }
+
+ override void visit(InvariantDeclaration d)
+ {
+ if (hgs.hdrgen)
+ return;
+ if (stcToBuffer(buf, d.storage_class))
+ buf.writeByte(' ');
+ buf.writestring("invariant");
+ if(auto es = d.fbody.isExpStatement())
+ {
+ assert(es.exp && es.exp.op == TOK.assert_);
+ buf.writestring(" (");
+ (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
+ buf.writestring(");");
+ buf.writenl();
+ }
+ else
+ {
+ bodyToBuffer(d);
+ }
+ }
+
+ override void visit(UnitTestDeclaration d)
+ {
+ if (hgs.hdrgen)
+ return;
+ if (stcToBuffer(buf, d.storage_class))
+ buf.writeByte(' ');
+ buf.writestring("unittest");
+ bodyToBuffer(d);
+ }
+
+ override void visit(BitFieldDeclaration d)
+ {
+ if (stcToBuffer(buf, d.storage_class))
+ buf.writeByte(' ');
+ Identifier id = d.isAnonymous() ? null : d.ident;
+ typeToBuffer(d.type, id, buf, hgs);
+ buf.writestring(" : ");
+ d.width.expressionToBuffer(buf, hgs);
+ buf.writeByte(';');
+ buf.writenl();
+ }
+
+ override void visit(NewDeclaration d)
+ {
+ if (stcToBuffer(buf, d.storage_class & ~STC.static_))
+ buf.writeByte(' ');
+ buf.writestring("new();");
+ }
+
+ override void visit(Module m)
+ {
+ moduleToBuffer2(m, buf, hgs);
+ }
+}
+
+private extern (C++) final class ExpressionPrettyPrintVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ OutBuffer* buf;
+ HdrGenState* hgs;
+
+ extern (D) this(OutBuffer* buf, HdrGenState* hgs)
+ {
+ this.buf = buf;
+ this.hgs = hgs;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ override void visit(Expression e)
+ {
+ buf.writestring(Token.toString(e.op));
+ }
+
+ override void visit(IntegerExp e)
+ {
+ const dinteger_t v = e.toInteger();
+ if (e.type)
+ {
+ Type t = e.type;
+ L1:
+ switch (t.ty)
+ {
+ case Tenum:
+ {
+ TypeEnum te = cast(TypeEnum)t;
+ auto sym = te.sym;
+ if (sym && sym.members && (!hgs.inEnumDecl || hgs.inEnumDecl != sym))
+ {
+ foreach (em; *sym.members)
+ {
+ if ((cast(EnumMember)em).value.toInteger == v)
+ {
+ buf.printf("%s.%s", sym.toChars(), em.ident.toChars());
+ return ;
+ }
+ }
+ }
+
+ buf.printf("cast(%s)", te.sym.toChars());
+ t = te.sym.memtype;
+ goto L1;
+ }
+ case Twchar:
+ // BUG: need to cast(wchar)
+ case Tdchar:
+ // BUG: need to cast(dchar)
+ if (cast(uinteger_t)v > 0xFF)
+ {
+ buf.printf("'\\U%08llx'", cast(long)v);
+ break;
+ }
+ goto case;
+ case Tchar:
+ {
+ size_t o = buf.length;
+ if (v == '\'')
+ buf.writestring("'\\''");
+ else if (isprint(cast(int)v) && v != '\\')
+ buf.printf("'%c'", cast(int)v);
+ else
+ buf.printf("'\\x%02x'", cast(int)v);
+ if (hgs.ddoc)
+ escapeDdocString(buf, o);
+ break;
+ }
+ case Tint8:
+ buf.writestring("cast(byte)");
+ goto L2;
+ case Tint16:
+ buf.writestring("cast(short)");
+ goto L2;
+ case Tint32:
+ L2:
+ buf.printf("%d", cast(int)v);
+ break;
+ case Tuns8:
+ buf.writestring("cast(ubyte)");
+ goto case Tuns32;
+ case Tuns16:
+ buf.writestring("cast(ushort)");
+ goto case Tuns32;
+ case Tuns32:
+ buf.printf("%uu", cast(uint)v);
+ break;
+ case Tint64:
+ buf.printf("%lldL", v);
+ break;
+ case Tuns64:
+ buf.printf("%lluLU", v);
+ break;
+ case Tbool:
+ buf.writestring(v ? "true" : "false");
+ break;
+ case Tpointer:
+ buf.writestring("cast(");
+ buf.writestring(t.toChars());
+ buf.writeByte(')');
+ if (target.ptrsize == 8)
+ goto case Tuns64;
+ else if (target.ptrsize == 4 ||
+ target.ptrsize == 2)
+ goto case Tuns32;
+ else
+ assert(0);
+
+ case Tvoid:
+ buf.writestring("cast(void)0");
+ break;
+
+ default:
+ /* This can happen if errors, such as
+ * the type is painted on like in fromConstInitializer().
+ */
+ if (!global.errors)
+ {
+ assert(0);
+ }
+ break;
+ }
+ }
+ else if (v & 0x8000000000000000L)
+ buf.printf("0x%llx", v);
+ else
+ buf.print(v);
+ }
+
+ override void visit(ErrorExp e)
+ {
+ buf.writestring("__error");
+ }
+
+ override void visit(VoidInitExp e)
+ {
+ buf.writestring("__void");
+ }
+
+ void floatToBuffer(Type type, real_t value)
+ {
+ .floatToBuffer(type, value, buf, hgs.hdrgen);
+ }
+
+ override void visit(RealExp e)
+ {
+ floatToBuffer(e.type, e.value);
+ }
+
+ override void visit(ComplexExp e)
+ {
+ /* Print as:
+ * (re+imi)
+ */
+ buf.writeByte('(');
+ floatToBuffer(e.type, creall(e.value));
+ buf.writeByte('+');
+ floatToBuffer(e.type, cimagl(e.value));
+ buf.writestring("i)");
+ }
+
+ override void visit(IdentifierExp e)
+ {
+ if (hgs.hdrgen || hgs.ddoc)
+ buf.writestring(e.ident.toHChars2());
+ else
+ buf.writestring(e.ident.toString());
+ }
+
+ override void visit(DsymbolExp e)
+ {
+ buf.writestring(e.s.toChars());
+ }
+
+ override void visit(ThisExp e)
+ {
+ buf.writestring("this");
+ }
+
+ override void visit(SuperExp e)
+ {
+ buf.writestring("super");
+ }
+
+ override void visit(NullExp e)
+ {
+ buf.writestring("null");
+ }
+
+ override void visit(StringExp e)
+ {
+ buf.writeByte('"');
+ const o = buf.length;
+ for (size_t i = 0; i < e.len; i++)
+ {
+ const c = e.charAt(i);
+ switch (c)
+ {
+ case '"':
+ case '\\':
+ buf.writeByte('\\');
+ goto default;
+ default:
+ if (c <= 0xFF)
+ {
+ if (c <= 0x7F && isprint(c))
+ buf.writeByte(c);
+ else
+ buf.printf("\\x%02x", c);
+ }
+ else if (c <= 0xFFFF)
+ buf.printf("\\x%02x\\x%02x", c & 0xFF, c >> 8);
+ else
+ buf.printf("\\x%02x\\x%02x\\x%02x\\x%02x", c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24);
+ break;
+ }
+ }
+ if (hgs.ddoc)
+ escapeDdocString(buf, o);
+ buf.writeByte('"');
+ if (e.postfix)
+ buf.writeByte(e.postfix);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ buf.writeByte('[');
+ argsToBuffer(e.elements, buf, hgs, e.basis);
+ buf.writeByte(']');
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ buf.writeByte('[');
+ foreach (i, key; *e.keys)
+ {
+ if (i)
+ buf.writestring(", ");
+ expToBuffer(key, PREC.assign, buf, hgs);
+ buf.writeByte(':');
+ auto value = (*e.values)[i];
+ expToBuffer(value, PREC.assign, buf, hgs);
+ }
+ buf.writeByte(']');
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ buf.writestring(e.sd.toChars());
+ buf.writeByte('(');
+ // CTFE can generate struct literals that contain an AddrExp pointing
+ // to themselves, need to avoid infinite recursion:
+ // struct S { this(int){ this.s = &this; } S* s; }
+ // const foo = new S(0);
+ if (e.stageflags & stageToCBuffer)
+ buf.writestring("<recursion>");
+ else
+ {
+ const old = e.stageflags;
+ e.stageflags |= stageToCBuffer;
+ argsToBuffer(e.elements, buf, hgs);
+ e.stageflags = old;
+ }
+ buf.writeByte(')');
+ }
+
+ override void visit(CompoundLiteralExp e)
+ {
+ buf.writeByte('(');
+ typeToBuffer(e.type, null, buf, hgs);
+ buf.writeByte(')');
+ e.initializer.initializerToBuffer(buf, hgs);
+ }
+
+ override void visit(TypeExp e)
+ {
+ typeToBuffer(e.type, null, buf, hgs);
+ }
+
+ override void visit(ScopeExp e)
+ {
+ if (e.sds.isTemplateInstance())
+ {
+ e.sds.dsymbolToBuffer(buf, hgs);
+ }
+ else if (hgs !is null && hgs.ddoc)
+ {
+ // fixes bug 6491
+ if (auto m = e.sds.isModule())
+ buf.writestring(m.md.toChars());
+ else
+ buf.writestring(e.sds.toChars());
+ }
+ else
+ {
+ buf.writestring(e.sds.kind());
+ buf.writeByte(' ');
+ buf.writestring(e.sds.toChars());
+ }
+ }
+
+ override void visit(TemplateExp e)
+ {
+ buf.writestring(e.td.toChars());
+ }
+
+ override void visit(NewExp e)
+ {
+ if (e.thisexp)
+ {
+ expToBuffer(e.thisexp, PREC.primary, buf, hgs);
+ buf.writeByte('.');
+ }
+ buf.writestring("new ");
+ if (e.newargs && e.newargs.dim)
+ {
+ buf.writeByte('(');
+ argsToBuffer(e.newargs, buf, hgs);
+ buf.writeByte(')');
+ }
+ typeToBuffer(e.newtype, null, buf, hgs);
+ if (e.arguments && e.arguments.dim)
+ {
+ buf.writeByte('(');
+ argsToBuffer(e.arguments, buf, hgs);
+ buf.writeByte(')');
+ }
+ }
+
+ override void visit(NewAnonClassExp e)
+ {
+ if (e.thisexp)
+ {
+ expToBuffer(e.thisexp, PREC.primary, buf, hgs);
+ buf.writeByte('.');
+ }
+ buf.writestring("new");
+ if (e.newargs && e.newargs.dim)
+ {
+ buf.writeByte('(');
+ argsToBuffer(e.newargs, buf, hgs);
+ buf.writeByte(')');
+ }
+ buf.writestring(" class ");
+ if (e.arguments && e.arguments.dim)
+ {
+ buf.writeByte('(');
+ argsToBuffer(e.arguments, buf, hgs);
+ buf.writeByte(')');
+ }
+ if (e.cd)
+ e.cd.dsymbolToBuffer(buf, hgs);
+ }
+
+ override void visit(SymOffExp e)
+ {
+ if (e.offset)
+ buf.printf("(& %s%+lld)", e.var.toChars(), e.offset);
+ else if (e.var.isTypeInfoDeclaration())
+ buf.writestring(e.var.toChars());
+ else
+ buf.printf("& %s", e.var.toChars());
+ }
+
+ override void visit(VarExp e)
+ {
+ buf.writestring(e.var.toChars());
+ }
+
+ override void visit(OverExp e)
+ {
+ buf.writestring(e.vars.ident.toString());
+ }
+
+ override void visit(TupleExp e)
+ {
+ if (e.e0)
+ {
+ buf.writeByte('(');
+ e.e0.accept(this);
+ buf.writestring(", tuple(");
+ argsToBuffer(e.exps, buf, hgs);
+ buf.writestring("))");
+ }
+ else
+ {
+ buf.writestring("tuple(");
+ argsToBuffer(e.exps, buf, hgs);
+ buf.writeByte(')');
+ }
+ }
+
+ override void visit(FuncExp e)
+ {
+ e.fd.dsymbolToBuffer(buf, hgs);
+ //buf.writestring(e.fd.toChars());
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ /* Normal dmd execution won't reach here - regular variable declarations
+ * are handled in visit(ExpStatement), so here would be used only when
+ * we'll directly call Expression.toChars() for debugging.
+ */
+ if (e.declaration)
+ {
+ if (auto var = e.declaration.isVarDeclaration())
+ {
+ // For debugging use:
+ // - Avoid printing newline.
+ // - Intentionally use the format (Type var;)
+ // which isn't correct as regular D code.
+ buf.writeByte('(');
+
+ scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
+ v.visitVarDecl(var, false);
+
+ buf.writeByte(';');
+ buf.writeByte(')');
+ }
+ else e.declaration.dsymbolToBuffer(buf, hgs);
+ }
+ }
+
+ override void visit(TypeidExp e)
+ {
+ buf.writestring("typeid(");
+ objectToBuffer(e.obj, buf, hgs);
+ buf.writeByte(')');
+ }
+
+ override void visit(TraitsExp e)
+ {
+ buf.writestring("__traits(");
+ if (e.ident)
+ buf.writestring(e.ident.toString());
+ if (e.args)
+ {
+ foreach (arg; *e.args)
+ {
+ buf.writestring(", ");
+ objectToBuffer(arg, buf, hgs);
+ }
+ }
+ buf.writeByte(')');
+ }
+
+ override void visit(HaltExp e)
+ {
+ buf.writestring("halt");
+ }
+
+ override void visit(IsExp e)
+ {
+ buf.writestring("is(");
+ typeToBuffer(e.targ, e.id, buf, hgs);
+ if (e.tok2 != TOK.reserved)
+ {
+ buf.printf(" %s %s", Token.toChars(e.tok), Token.toChars(e.tok2));
+ }
+ else if (e.tspec)
+ {
+ if (e.tok == TOK.colon)
+ buf.writestring(" : ");
+ else
+ buf.writestring(" == ");
+ typeToBuffer(e.tspec, null, buf, hgs);
+ }
+ if (e.parameters && e.parameters.dim)
+ {
+ buf.writestring(", ");
+ scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
+ v.visitTemplateParameters(e.parameters);
+ }
+ buf.writeByte(')');
+ }
+
+ override void visit(UnaExp e)
+ {
+ buf.writestring(Token.toString(e.op));
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ }
+
+ override void visit(BinExp e)
+ {
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ buf.writeByte(' ');
+ buf.writestring(Token.toString(e.op));
+ buf.writeByte(' ');
+ expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs);
+ }
+
+ override void visit(CommaExp e)
+ {
+ // CommaExp is generated by the compiler so it shouldn't
+ // appear in error messages or header files.
+ // For now, this treats the case where the compiler
+ // generates CommaExp for temporaries by calling
+ // the `sideeffect.copyToTemp` function.
+ auto ve = e.e2.isVarExp();
+
+ // not a CommaExp introduced for temporaries, go on
+ // the old path
+ if (!ve || !(ve.var.storage_class & STC.temp))
+ {
+ visit(cast(BinExp)e);
+ return;
+ }
+
+ // CommaExp that contain temporaries inserted via
+ // `copyToTemp` are usually of the form
+ // ((T __temp = exp), __tmp).
+ // Asserts are here to easily spot
+ // missing cases where CommaExp
+ // are used for other constructs
+ auto vd = ve.var.isVarDeclaration();
+ assert(vd && vd._init);
+
+ if (auto ei = vd._init.isExpInitializer())
+ {
+ Expression commaExtract;
+ auto exp = ei.exp;
+ if (auto ce = exp.isConstructExp())
+ commaExtract = ce.e2;
+ else if (auto se = exp.isStructLiteralExp())
+ commaExtract = se;
+
+ if (commaExtract)
+ {
+ expToBuffer(commaExtract, precedence[exp.op], buf, hgs);
+ return;
+ }
+ }
+
+ // not one of the known cases, go on the old path
+ visit(cast(BinExp)e);
+ return;
+ }
+
+ override void visit(MixinExp e)
+ {
+ buf.writestring("mixin(");
+ argsToBuffer(e.exps, buf, hgs, null);
+ buf.writeByte(')');
+ }
+
+ override void visit(ImportExp e)
+ {
+ buf.writestring("import(");
+ expToBuffer(e.e1, PREC.assign, buf, hgs);
+ buf.writeByte(')');
+ }
+
+ override void visit(AssertExp e)
+ {
+ buf.writestring("assert(");
+ expToBuffer(e.e1, PREC.assign, buf, hgs);
+ if (e.msg)
+ {
+ buf.writestring(", ");
+ expToBuffer(e.msg, PREC.assign, buf, hgs);
+ }
+ buf.writeByte(')');
+ }
+
+ override void visit(DotIdExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writeByte('.');
+ buf.writestring(e.ident.toString());
+ }
+
+ override void visit(DotTemplateExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writeByte('.');
+ buf.writestring(e.td.toChars());
+ }
+
+ override void visit(DotVarExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writeByte('.');
+ buf.writestring(e.var.toChars());
+ }
+
+ override void visit(DotTemplateInstanceExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writeByte('.');
+ e.ti.dsymbolToBuffer(buf, hgs);
+ }
+
+ override void visit(DelegateExp e)
+ {
+ buf.writeByte('&');
+ if (!e.func.isNested() || e.func.needThis())
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writeByte('.');
+ }
+ buf.writestring(e.func.toChars());
+ }
+
+ override void visit(DotTypeExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writeByte('.');
+ buf.writestring(e.sym.toChars());
+ }
+
+ override void visit(CallExp e)
+ {
+ if (e.e1.op == TOK.type)
+ {
+ /* Avoid parens around type to prevent forbidden cast syntax:
+ * (sometype)(arg1)
+ * This is ok since types in constructor calls
+ * can never depend on parens anyway
+ */
+ e.e1.accept(this);
+ }
+ else
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ buf.writeByte('(');
+ argsToBuffer(e.arguments, buf, hgs);
+ buf.writeByte(')');
+ }
+
+ override void visit(PtrExp e)
+ {
+ buf.writeByte('*');
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ }
+
+ override void visit(DeleteExp e)
+ {
+ buf.writestring("delete ");
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ }
+
+ override void visit(CastExp e)
+ {
+ buf.writestring("cast(");
+ if (e.to)
+ typeToBuffer(e.to, null, buf, hgs);
+ else
+ {
+ MODtoBuffer(buf, e.mod);
+ }
+ buf.writeByte(')');
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ }
+
+ override void visit(VectorExp e)
+ {
+ buf.writestring("cast(");
+ typeToBuffer(e.to, null, buf, hgs);
+ buf.writeByte(')');
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ }
+
+ override void visit(VectorArrayExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writestring(".array");
+ }
+
+ override void visit(SliceExp e)
+ {
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ buf.writeByte('[');
+ if (e.upr || e.lwr)
+ {
+ if (e.lwr)
+ sizeToBuffer(e.lwr, buf, hgs);
+ else
+ buf.writeByte('0');
+ buf.writestring("..");
+ if (e.upr)
+ sizeToBuffer(e.upr, buf, hgs);
+ else
+ buf.writeByte('$');
+ }
+ buf.writeByte(']');
+ }
+
+ override void visit(ArrayLengthExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writestring(".length");
+ }
+
+ override void visit(IntervalExp e)
+ {
+ expToBuffer(e.lwr, PREC.assign, buf, hgs);
+ buf.writestring("..");
+ expToBuffer(e.upr, PREC.assign, buf, hgs);
+ }
+
+ override void visit(DelegatePtrExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writestring(".ptr");
+ }
+
+ override void visit(DelegateFuncptrExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writestring(".funcptr");
+ }
+
+ override void visit(ArrayExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writeByte('[');
+ argsToBuffer(e.arguments, buf, hgs);
+ buf.writeByte(']');
+ }
+
+ override void visit(DotExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writeByte('.');
+ expToBuffer(e.e2, PREC.primary, buf, hgs);
+ }
+
+ override void visit(IndexExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writeByte('[');
+ sizeToBuffer(e.e2, buf, hgs);
+ buf.writeByte(']');
+ }
+
+ override void visit(PostExp e)
+ {
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ buf.writestring(Token.toString(e.op));
+ }
+
+ override void visit(PreExp e)
+ {
+ buf.writestring(Token.toString(e.op));
+ expToBuffer(e.e1, precedence[e.op], buf, hgs);
+ }
+
+ override void visit(RemoveExp e)
+ {
+ expToBuffer(e.e1, PREC.primary, buf, hgs);
+ buf.writestring(".remove(");
+ expToBuffer(e.e2, PREC.assign, buf, hgs);
+ buf.writeByte(')');
+ }
+
+ override void visit(CondExp e)
+ {
+ expToBuffer(e.econd, PREC.oror, buf, hgs);
+ buf.writestring(" ? ");
+ expToBuffer(e.e1, PREC.expr, buf, hgs);
+ buf.writestring(" : ");
+ expToBuffer(e.e2, PREC.cond, buf, hgs);
+ }
+
+ override void visit(DefaultInitExp e)
+ {
+ buf.writestring(Token.toString(e.op));
+ }
+
+ override void visit(ClassReferenceExp e)
+ {
+ buf.writestring(e.value.toChars());
+ }
+}
+
+/**
+ * Formats `value` as a literal of type `type` into `buf`.
+ *
+ * Params:
+ * type = literal type (e.g. Tfloat)
+ * value = value to print
+ * buf = target buffer
+ * allowHex = whether hex floating point literals may be used
+ * for greater accuracy
+ */
+void floatToBuffer(Type type, const real_t value, OutBuffer* buf, const bool allowHex)
+{
+ /** sizeof(value)*3 is because each byte of mantissa is max
+ of 256 (3 characters). The string will be "-M.MMMMe-4932".
+ (ie, 8 chars more than mantissa). Plus one for trailing \0.
+ Plus one for rounding. */
+ const(size_t) BUFFER_LEN = value.sizeof * 3 + 8 + 1 + 1;
+ char[BUFFER_LEN] buffer;
+ CTFloat.sprint(buffer.ptr, 'g', value);
+ assert(strlen(buffer.ptr) < BUFFER_LEN);
+ if (allowHex)
+ {
+ real_t r = CTFloat.parse(buffer.ptr);
+ if (r != value) // if exact duplication
+ CTFloat.sprint(buffer.ptr, 'a', value);
+ }
+ buf.writestring(buffer.ptr);
+ if (buffer.ptr[strlen(buffer.ptr) - 1] == '.')
+ buf.remove(buf.length() - 1, 1);
+
+ if (type)
+ {
+ Type t = type.toBasetype();
+ switch (t.ty)
+ {
+ case Tfloat32:
+ case Timaginary32:
+ case Tcomplex32:
+ buf.writeByte('F');
+ break;
+ case Tfloat80:
+ case Timaginary80:
+ case Tcomplex80:
+ buf.writeByte('L');
+ break;
+ default:
+ break;
+ }
+ if (t.isimaginary())
+ buf.writeByte('i');
+ }
+}
+
+private void templateParameterToBuffer(TemplateParameter tp, OutBuffer* buf, HdrGenState* hgs)
+{
+ scope v = new TemplateParameterPrettyPrintVisitor(buf, hgs);
+ tp.accept(v);
+}
+
+private extern (C++) final class TemplateParameterPrettyPrintVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ OutBuffer* buf;
+ HdrGenState* hgs;
+
+ extern (D) this(OutBuffer* buf, HdrGenState* hgs)
+ {
+ this.buf = buf;
+ this.hgs = hgs;
+ }
+
+ override void visit(TemplateTypeParameter tp)
+ {
+ buf.writestring(tp.ident.toString());
+ if (tp.specType)
+ {
+ buf.writestring(" : ");
+ typeToBuffer(tp.specType, null, buf, hgs);
+ }
+ if (tp.defaultType)
+ {
+ buf.writestring(" = ");
+ typeToBuffer(tp.defaultType, null, buf, hgs);
+ }
+ }
+
+ override void visit(TemplateThisParameter tp)
+ {
+ buf.writestring("this ");
+ visit(cast(TemplateTypeParameter)tp);
+ }
+
+ override void visit(TemplateAliasParameter tp)
+ {
+ buf.writestring("alias ");
+ if (tp.specType)
+ typeToBuffer(tp.specType, tp.ident, buf, hgs);
+ else
+ buf.writestring(tp.ident.toString());
+ if (tp.specAlias)
+ {
+ buf.writestring(" : ");
+ objectToBuffer(tp.specAlias, buf, hgs);
+ }
+ if (tp.defaultAlias)
+ {
+ buf.writestring(" = ");
+ objectToBuffer(tp.defaultAlias, buf, hgs);
+ }
+ }
+
+ override void visit(TemplateValueParameter tp)
+ {
+ typeToBuffer(tp.valType, tp.ident, buf, hgs);
+ if (tp.specValue)
+ {
+ buf.writestring(" : ");
+ tp.specValue.expressionToBuffer(buf, hgs);
+ }
+ if (tp.defaultValue)
+ {
+ buf.writestring(" = ");
+ tp.defaultValue.expressionToBuffer(buf, hgs);
+ }
+ }
+
+ override void visit(TemplateTupleParameter tp)
+ {
+ buf.writestring(tp.ident.toString());
+ buf.writestring("...");
+ }
+}
+
+private void conditionToBuffer(Condition c, OutBuffer* buf, HdrGenState* hgs)
+{
+ scope v = new ConditionPrettyPrintVisitor(buf, hgs);
+ c.accept(v);
+}
+
+private extern (C++) final class ConditionPrettyPrintVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ OutBuffer* buf;
+ HdrGenState* hgs;
+
+ extern (D) this(OutBuffer* buf, HdrGenState* hgs)
+ {
+ this.buf = buf;
+ this.hgs = hgs;
+ }
+
+ override void visit(DebugCondition c)
+ {
+ buf.writestring("debug (");
+ if (c.ident)
+ buf.writestring(c.ident.toString());
+ else
+ buf.print(c.level);
+ buf.writeByte(')');
+ }
+
+ override void visit(VersionCondition c)
+ {
+ buf.writestring("version (");
+ if (c.ident)
+ buf.writestring(c.ident.toString());
+ else
+ buf.print(c.level);
+ buf.writeByte(')');
+ }
+
+ override void visit(StaticIfCondition c)
+ {
+ buf.writestring("static if (");
+ c.exp.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ }
+}
+
+void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs)
+{
+ scope v = new StatementPrettyPrintVisitor(buf, hgs);
+ (cast() s).accept(v);
+}
+
+void toCBuffer(const Type t, OutBuffer* buf, const Identifier ident, HdrGenState* hgs)
+{
+ typeToBuffer(cast() t, ident, buf, hgs);
+}
+
+void toCBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs)
+{
+ scope v = new DsymbolPrettyPrintVisitor(buf, hgs);
+ s.accept(v);
+}
+
+// used from TemplateInstance::toChars() and TemplateMixin::toChars()
+void toCBufferInstance(const TemplateInstance ti, OutBuffer* buf, bool qualifyTypes = false)
+{
+ HdrGenState hgs;
+ hgs.fullQual = qualifyTypes;
+ scope v = new DsymbolPrettyPrintVisitor(buf, &hgs);
+ v.visit(cast() ti);
+}
+
+void toCBuffer(const Initializer iz, OutBuffer* buf, HdrGenState* hgs)
+{
+ initializerToBuffer(cast() iz, buf, hgs);
+}
+
+bool stcToBuffer(OutBuffer* buf, StorageClass stc)
+{
+ //printf("stc: %llx\n", stc);
+ bool result = false;
+
+ if (stc & STC.scopeinferred)
+ stc &= ~(STC.scope_ | STC.scopeinferred);
+ if (stc & STC.returninferred)
+ stc &= ~(STC.return_ | STC.returninferred);
+
+ /* Put scope ref return into a standard order
+ */
+ string rrs;
+ const isout = (stc & STC.out_) != 0;
+ //printf("bsr = %d %llx\n", buildScopeRef(stc), stc);
+ final switch (buildScopeRef(stc))
+ {
+ case ScopeRef.None:
+ case ScopeRef.Scope:
+ case ScopeRef.Ref:
+ case ScopeRef.Return:
+ break;
+
+ case ScopeRef.ReturnScope: rrs = "return scope"; goto L1;
+ case ScopeRef.ReturnRef: rrs = isout ? "return out" : "return ref"; goto L1;
+ case ScopeRef.RefScope: rrs = isout ? "out scope" : "ref scope"; goto L1;
+ case ScopeRef.ReturnRef_Scope: rrs = isout ? "return out scope" : "return ref scope"; goto L1;
+ case ScopeRef.Ref_ReturnScope: rrs = isout ? "out return scope" : "ref return scope"; goto L1;
+ L1:
+ buf.writestring(rrs);
+ result = true;
+ stc &= ~(STC.out_ | STC.scope_ | STC.ref_ | STC.return_);
+ break;
+ }
+
+ while (stc)
+ {
+ const s = stcToString(stc);
+ if (!s.length)
+ break;
+ if (result)
+ buf.writeByte(' ');
+ result = true;
+ buf.writestring(s);
+ }
+
+ return result;
+}
+
+/*************************************************
+ * Pick off one of the storage classes from stc,
+ * and return a string representation of it.
+ * stc is reduced by the one picked.
+ */
+string stcToString(ref StorageClass stc)
+{
+ static struct SCstring
+ {
+ StorageClass stc;
+ string id;
+ }
+
+ // Note: The identifier needs to be `\0` terminated
+ // as some code assumes it (e.g. when printing error messages)
+ static immutable SCstring[] table =
+ [
+ SCstring(STC.auto_, Token.toString(TOK.auto_)),
+ SCstring(STC.scope_, Token.toString(TOK.scope_)),
+ SCstring(STC.static_, Token.toString(TOK.static_)),
+ SCstring(STC.extern_, Token.toString(TOK.extern_)),
+ SCstring(STC.const_, Token.toString(TOK.const_)),
+ SCstring(STC.final_, Token.toString(TOK.final_)),
+ SCstring(STC.abstract_, Token.toString(TOK.abstract_)),
+ SCstring(STC.synchronized_, Token.toString(TOK.synchronized_)),
+ SCstring(STC.deprecated_, Token.toString(TOK.deprecated_)),
+ SCstring(STC.override_, Token.toString(TOK.override_)),
+ SCstring(STC.lazy_, Token.toString(TOK.lazy_)),
+ SCstring(STC.alias_, Token.toString(TOK.alias_)),
+ SCstring(STC.out_, Token.toString(TOK.out_)),
+ SCstring(STC.in_, Token.toString(TOK.in_)),
+ SCstring(STC.manifest, Token.toString(TOK.enum_)),
+ SCstring(STC.immutable_, Token.toString(TOK.immutable_)),
+ SCstring(STC.shared_, Token.toString(TOK.shared_)),
+ SCstring(STC.nothrow_, Token.toString(TOK.nothrow_)),
+ SCstring(STC.wild, Token.toString(TOK.inout_)),
+ SCstring(STC.pure_, Token.toString(TOK.pure_)),
+ SCstring(STC.ref_, Token.toString(TOK.ref_)),
+ SCstring(STC.return_, Token.toString(TOK.return_)),
+ SCstring(STC.tls, "__thread"),
+ SCstring(STC.gshared, Token.toString(TOK.gshared)),
+ SCstring(STC.nogc, "@nogc"),
+ SCstring(STC.live, "@live"),
+ SCstring(STC.property, "@property"),
+ SCstring(STC.safe, "@safe"),
+ SCstring(STC.trusted, "@trusted"),
+ SCstring(STC.system, "@system"),
+ SCstring(STC.disable, "@disable"),
+ SCstring(STC.future, "@__future"),
+ SCstring(STC.local, "__local"),
+ ];
+ foreach (ref entry; table)
+ {
+ const StorageClass tbl = entry.stc;
+ assert(tbl & STC.visibleStorageClasses);
+ if (stc & tbl)
+ {
+ stc &= ~tbl;
+ return entry.id;
+ }
+ }
+ //printf("stc = %llx\n", stc);
+ return null;
+}
+
+/// Ditto
+extern (D) string trustToString(TRUST trust) pure nothrow
+{
+ final switch (trust)
+ {
+ case TRUST.default_:
+ return null;
+ case TRUST.system:
+ return "@system";
+ case TRUST.trusted:
+ return "@trusted";
+ case TRUST.safe:
+ return "@safe";
+ }
+}
+
+private void linkageToBuffer(OutBuffer* buf, LINK linkage)
+{
+ const s = linkageToString(linkage);
+ if (s.length)
+ {
+ buf.writestring("extern (");
+ buf.writestring(s);
+ buf.writeByte(')');
+ }
+}
+
+const(char)* linkageToChars(LINK linkage)
+{
+ /// Works because we return a literal
+ return linkageToString(linkage).ptr;
+}
+
+string linkageToString(LINK linkage) pure nothrow
+{
+ final switch (linkage)
+ {
+ case LINK.default_:
+ return null;
+ case LINK.d:
+ return "D";
+ case LINK.c:
+ return "C";
+ case LINK.cpp:
+ return "C++";
+ case LINK.windows:
+ return "Windows";
+ case LINK.objc:
+ return "Objective-C";
+ case LINK.system:
+ return "System";
+ }
+}
+
+void visibilityToBuffer(OutBuffer* buf, Visibility vis)
+{
+ buf.writestring(visibilityToString(vis.kind));
+ if (vis.kind == Visibility.Kind.package_ && vis.pkg)
+ {
+ buf.writeByte('(');
+ buf.writestring(vis.pkg.toPrettyChars(true));
+ buf.writeByte(')');
+ }
+}
+
+/**
+ * Returns:
+ * a human readable representation of `kind`
+ */
+const(char)* visibilityToChars(Visibility.Kind kind)
+{
+ // Null terminated because we return a literal
+ return visibilityToString(kind).ptr;
+}
+
+/// Ditto
+extern (D) string visibilityToString(Visibility.Kind kind) nothrow pure
+{
+ final switch (kind)
+ {
+ case Visibility.Kind.undefined:
+ return null;
+ case Visibility.Kind.none:
+ return "none";
+ case Visibility.Kind.private_:
+ return "private";
+ case Visibility.Kind.package_:
+ return "package";
+ case Visibility.Kind.protected_:
+ return "protected";
+ case Visibility.Kind.public_:
+ return "public";
+ case Visibility.Kind.export_:
+ return "export";
+ }
+}
+
+// Print the full function signature with correct ident, attributes and template args
+void functionToBufferFull(TypeFunction tf, OutBuffer* buf, const Identifier ident, HdrGenState* hgs, TemplateDeclaration td)
+{
+ //printf("TypeFunction::toCBuffer() this = %p\n", this);
+ visitFuncIdentWithPrefix(tf, ident, td, buf, hgs);
+}
+
+// ident is inserted before the argument list and will be "function" or "delegate" for a type
+void functionToBufferWithIdent(TypeFunction tf, OutBuffer* buf, const(char)* ident, bool isStatic)
+{
+ HdrGenState hgs;
+ visitFuncIdentWithPostfix(tf, ident.toDString(), buf, &hgs, isStatic);
+}
+
+void toCBuffer(const Expression e, OutBuffer* buf, HdrGenState* hgs)
+{
+ scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
+ (cast() e).accept(v);
+}
+
+/**************************************************
+ * Write out argument types to buf.
+ */
+void argExpTypesToCBuffer(OutBuffer* buf, Expressions* arguments)
+{
+ if (!arguments || !arguments.dim)
+ return;
+ HdrGenState hgs;
+ foreach (i, arg; *arguments)
+ {
+ if (i)
+ buf.writestring(", ");
+ typeToBuffer(arg.type, null, buf, &hgs);
+ }
+}
+
+void toCBuffer(const TemplateParameter tp, OutBuffer* buf, HdrGenState* hgs)
+{
+ scope v = new TemplateParameterPrettyPrintVisitor(buf, hgs);
+ (cast() tp).accept(v);
+}
+
+void arrayObjectsToBuffer(OutBuffer* buf, Objects* objects)
+{
+ if (!objects || !objects.dim)
+ return;
+ HdrGenState hgs;
+ foreach (i, o; *objects)
+ {
+ if (i)
+ buf.writestring(", ");
+ objectToBuffer(o, buf, &hgs);
+ }
+}
+
+/*************************************************************
+ * Pretty print function parameters.
+ * Params:
+ * pl = parameter list to print
+ * Returns: Null-terminated string representing parameters.
+ */
+extern (C++) const(char)* parametersTypeToChars(ParameterList pl)
+{
+ OutBuffer buf;
+ HdrGenState hgs;
+ parametersToBuffer(pl, &buf, &hgs);
+ return buf.extractChars();
+}
+
+/*************************************************************
+ * Pretty print function parameter.
+ * Params:
+ * parameter = parameter to print.
+ * tf = TypeFunction which holds parameter.
+ * fullQual = whether to fully qualify types.
+ * Returns: Null-terminated string representing parameters.
+ */
+const(char)* parameterToChars(Parameter parameter, TypeFunction tf, bool fullQual)
+{
+ OutBuffer buf;
+ HdrGenState hgs;
+ hgs.fullQual = fullQual;
+
+ parameterToBuffer(parameter, &buf, &hgs);
+
+ if (tf.parameterList.varargs == VarArg.typesafe && parameter == tf.parameterList[tf.parameterList.parameters.dim - 1])
+ {
+ buf.writestring("...");
+ }
+ return buf.extractChars();
+}
+
+
+/*************************************************
+ * Write ParameterList to buffer.
+ * Params:
+ * pl = parameter list to serialize
+ * buf = buffer to write it to
+ * hgs = context
+ */
+
+private void parametersToBuffer(ParameterList pl, OutBuffer* buf, HdrGenState* hgs)
+{
+ buf.writeByte('(');
+ foreach (i; 0 .. pl.length)
+ {
+ if (i)
+ buf.writestring(", ");
+ pl[i].parameterToBuffer(buf, hgs);
+ }
+ final switch (pl.varargs)
+ {
+ case VarArg.none:
+ break;
+
+ case VarArg.variadic:
+ if (pl.length)
+ buf.writestring(", ");
+
+ if (stcToBuffer(buf, pl.stc))
+ buf.writeByte(' ');
+ goto case VarArg.typesafe;
+
+ case VarArg.typesafe:
+ buf.writestring("...");
+ break;
+ }
+ buf.writeByte(')');
+}
+
+
+/***********************************************************
+ * Write parameter `p` to buffer `buf`.
+ * Params:
+ * p = parameter to serialize
+ * buf = buffer to write it to
+ * hgs = context
+ */
+private void parameterToBuffer(Parameter p, OutBuffer* buf, HdrGenState* hgs)
+{
+ if (p.userAttribDecl)
+ {
+ buf.writeByte('@');
+
+ bool isAnonymous = p.userAttribDecl.atts.dim > 0 && (*p.userAttribDecl.atts)[0].op != TOK.call;
+ if (isAnonymous)
+ buf.writeByte('(');
+
+ argsToBuffer(p.userAttribDecl.atts, buf, hgs);
+
+ if (isAnonymous)
+ buf.writeByte(')');
+ buf.writeByte(' ');
+ }
+ if (p.storageClass & STC.auto_)
+ buf.writestring("auto ");
+
+ StorageClass stc = p.storageClass;
+ if (p.storageClass & STC.in_)
+ {
+ buf.writestring("in ");
+ if (global.params.previewIn && p.storageClass & STC.ref_)
+ stc &= ~STC.ref_;
+ }
+ else if (p.storageClass & STC.lazy_)
+ buf.writestring("lazy ");
+ else if (p.storageClass & STC.alias_)
+ buf.writestring("alias ");
+
+ if (p.type && p.type.mod & MODFlags.shared_)
+ stc &= ~STC.shared_;
+
+ if (stcToBuffer(buf, stc & (STC.const_ | STC.immutable_ | STC.wild | STC.shared_ |
+ STC.return_ | STC.returninferred | STC.scope_ | STC.scopeinferred | STC.out_ | STC.ref_ | STC.returnScope)))
+ buf.writeByte(' ');
+
+ if (p.storageClass & STC.alias_)
+ {
+ if (p.ident)
+ buf.writestring(p.ident.toString());
+ }
+ else if (p.type.ty == Tident &&
+ (cast(TypeIdentifier)p.type).ident.toString().length > 3 &&
+ strncmp((cast(TypeIdentifier)p.type).ident.toChars(), "__T", 3) == 0)
+ {
+ // print parameter name, instead of undetermined type parameter
+ buf.writestring(p.ident.toString());
+ }
+ else
+ {
+ typeToBuffer(p.type, p.ident, buf, hgs, (stc & STC.in_) ? MODFlags.const_ : 0);
+ }
+
+ if (p.defaultArg)
+ {
+ buf.writestring(" = ");
+ p.defaultArg.expToBuffer(PREC.assign, buf, hgs);
+ }
+}
+
+
+/**************************************************
+ * Write out argument list to buf.
+ */
+private void argsToBuffer(Expressions* expressions, OutBuffer* buf, HdrGenState* hgs, Expression basis = null)
+{
+ if (!expressions || !expressions.dim)
+ return;
+ version (all)
+ {
+ foreach (i, el; *expressions)
+ {
+ if (i)
+ buf.writestring(", ");
+ if (!el)
+ el = basis;
+ if (el)
+ expToBuffer(el, PREC.assign, buf, hgs);
+ }
+ }
+ else
+ {
+ // Sparse style formatting, for debug use only
+ // [0..dim: basis, 1: e1, 5: e5]
+ if (basis)
+ {
+ buf.writestring("0..");
+ buf.print(expressions.dim);
+ buf.writestring(": ");
+ expToBuffer(basis, PREC.assign, buf, hgs);
+ }
+ foreach (i, el; *expressions)
+ {
+ if (el)
+ {
+ if (basis)
+ {
+ buf.writestring(", ");
+ buf.print(i);
+ buf.writestring(": ");
+ }
+ else if (i)
+ buf.writestring(", ");
+ expToBuffer(el, PREC.assign, buf, hgs);
+ }
+ }
+ }
+}
+
+private void sizeToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
+{
+ if (e.type == Type.tsize_t)
+ {
+ Expression ex = (e.op == TOK.cast_ ? (cast(CastExp)e).e1 : e);
+ ex = ex.optimize(WANTvalue);
+ const dinteger_t uval = ex.op == TOK.int64 ? ex.toInteger() : cast(dinteger_t)-1;
+ if (cast(sinteger_t)uval >= 0)
+ {
+ dinteger_t sizemax = void;
+ if (target.ptrsize == 8)
+ sizemax = 0xFFFFFFFFFFFFFFFFUL;
+ else if (target.ptrsize == 4)
+ sizemax = 0xFFFFFFFFU;
+ else if (target.ptrsize == 2)
+ sizemax = 0xFFFFU;
+ else
+ assert(0);
+ if (uval <= sizemax && uval <= 0x7FFFFFFFFFFFFFFFUL)
+ {
+ buf.print(uval);
+ return;
+ }
+ }
+ }
+ expToBuffer(e, PREC.assign, buf, hgs);
+}
+
+private void expressionToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
+{
+ scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
+ e.accept(v);
+}
+
+/**************************************************
+ * Write expression out to buf, but wrap it
+ * in ( ) if its precedence is less than pr.
+ */
+private void expToBuffer(Expression e, PREC pr, OutBuffer* buf, HdrGenState* hgs)
+{
+ debug
+ {
+ if (precedence[e.op] == PREC.zero)
+ printf("precedence not defined for token '%s'\n", Token.toChars(e.op));
+ }
+ if (e.op == 0xFF)
+ {
+ buf.writestring("<FF>");
+ return;
+ }
+ assert(precedence[e.op] != PREC.zero);
+ assert(pr != PREC.zero);
+ /* Despite precedence, we don't allow a<b<c expressions.
+ * They must be parenthesized.
+ */
+ if (precedence[e.op] < pr || (pr == PREC.rel && precedence[e.op] == pr)
+ || (pr >= PREC.or && pr <= PREC.and && precedence[e.op] == PREC.rel))
+ {
+ buf.writeByte('(');
+ e.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ }
+ else
+ {
+ e.expressionToBuffer(buf, hgs);
+ }
+}
+
+
+/**************************************************
+ * An entry point to pretty-print type.
+ */
+private void typeToBuffer(Type t, const Identifier ident, OutBuffer* buf, HdrGenState* hgs,
+ ubyte modMask = 0)
+{
+ if (auto tf = t.isTypeFunction())
+ {
+ visitFuncIdentWithPrefix(tf, ident, null, buf, hgs);
+ return;
+ }
+ visitWithMask(t, modMask, buf, hgs);
+ if (ident)
+ {
+ buf.writeByte(' ');
+ buf.writestring(ident.toString());
+ }
+}
+
+private void visitWithMask(Type t, ubyte modMask, OutBuffer* buf, HdrGenState* hgs)
+{
+ // Tuples and functions don't use the type constructor syntax
+ if (modMask == t.mod || t.ty == Tfunction || t.ty == Ttuple)
+ {
+ typeToBufferx(t, buf, hgs);
+ }
+ else
+ {
+ ubyte m = t.mod & ~(t.mod & modMask);
+ if (m & MODFlags.shared_)
+ {
+ MODtoBuffer(buf, MODFlags.shared_);
+ buf.writeByte('(');
+ }
+ if (m & MODFlags.wild)
+ {
+ MODtoBuffer(buf, MODFlags.wild);
+ buf.writeByte('(');
+ }
+ if (m & (MODFlags.const_ | MODFlags.immutable_))
+ {
+ MODtoBuffer(buf, m & (MODFlags.const_ | MODFlags.immutable_));
+ buf.writeByte('(');
+ }
+ typeToBufferx(t, buf, hgs);
+ if (m & (MODFlags.const_ | MODFlags.immutable_))
+ buf.writeByte(')');
+ if (m & MODFlags.wild)
+ buf.writeByte(')');
+ if (m & MODFlags.shared_)
+ buf.writeByte(')');
+ }
+}
+
+
+private void dumpTemplateInstance(TemplateInstance ti, OutBuffer* buf, HdrGenState* hgs)
+{
+ buf.writeByte('{');
+ buf.writenl();
+ buf.level++;
+
+ if (ti.aliasdecl)
+ {
+ ti.aliasdecl.dsymbolToBuffer(buf, hgs);
+ buf.writenl();
+ }
+ else if (ti.members)
+ {
+ foreach(m;*ti.members)
+ m.dsymbolToBuffer(buf, hgs);
+ }
+
+ buf.level--;
+ buf.writeByte('}');
+ buf.writenl();
+
+}
+
+private void tiargsToBuffer(TemplateInstance ti, OutBuffer* buf, HdrGenState* hgs)
+{
+ buf.writeByte('!');
+ if (ti.nest)
+ {
+ buf.writestring("(...)");
+ return;
+ }
+ if (!ti.tiargs)
+ {
+ buf.writestring("()");
+ return;
+ }
+ if (ti.tiargs.dim == 1)
+ {
+ RootObject oarg = (*ti.tiargs)[0];
+ if (Type t = isType(oarg))
+ {
+ if (t.equals(Type.tstring) || t.equals(Type.twstring) || t.equals(Type.tdstring) || t.mod == 0 && (t.isTypeBasic() || t.ty == Tident && (cast(TypeIdentifier)t).idents.dim == 0))
+ {
+ buf.writestring(t.toChars());
+ return;
+ }
+ }
+ else if (Expression e = isExpression(oarg))
+ {
+ if (e.op == TOK.int64 || e.op == TOK.float64 || e.op == TOK.null_ || e.op == TOK.string_ || e.op == TOK.this_)
+ {
+ buf.writestring(e.toChars());
+ return;
+ }
+ }
+ }
+ buf.writeByte('(');
+ ti.nestUp();
+ foreach (i, arg; *ti.tiargs)
+ {
+ if (i)
+ buf.writestring(", ");
+ objectToBuffer(arg, buf, hgs);
+ }
+ ti.nestDown();
+ buf.writeByte(')');
+}
+
+/****************************************
+ * This makes a 'pretty' version of the template arguments.
+ * It's analogous to genIdent() which makes a mangled version.
+ */
+private void objectToBuffer(RootObject oarg, OutBuffer* buf, HdrGenState* hgs)
+{
+ //printf("objectToBuffer()\n");
+ /* The logic of this should match what genIdent() does. The _dynamic_cast()
+ * function relies on all the pretty strings to be unique for different classes
+ * See https://issues.dlang.org/show_bug.cgi?id=7375
+ * Perhaps it would be better to demangle what genIdent() does.
+ */
+ if (auto t = isType(oarg))
+ {
+ //printf("\tt: %s ty = %d\n", t.toChars(), t.ty);
+ typeToBuffer(t, null, buf, hgs);
+ }
+ else if (auto e = isExpression(oarg))
+ {
+ if (e.op == TOK.variable)
+ e = e.optimize(WANTvalue); // added to fix https://issues.dlang.org/show_bug.cgi?id=7375
+ expToBuffer(e, PREC.assign, buf, hgs);
+ }
+ else if (Dsymbol s = isDsymbol(oarg))
+ {
+ const p = s.ident ? s.ident.toChars() : s.toChars();
+ buf.writestring(p);
+ }
+ else if (auto v = isTuple(oarg))
+ {
+ auto args = &v.objects;
+ foreach (i, arg; *args)
+ {
+ if (i)
+ buf.writestring(", ");
+ objectToBuffer(arg, buf, hgs);
+ }
+ }
+ else if (auto p = isParameter(oarg))
+ {
+ parameterToBuffer(p, buf, hgs);
+ }
+ else if (!oarg)
+ {
+ buf.writestring("NULL");
+ }
+ else
+ {
+ debug
+ {
+ printf("bad Object = %p\n", oarg);
+ }
+ assert(0);
+ }
+}
+
+
+private void visitFuncIdentWithPostfix(TypeFunction t, const char[] ident, OutBuffer* buf, HdrGenState* hgs, bool isStatic)
+{
+ if (t.inuse)
+ {
+ t.inuse = 2; // flag error to caller
+ return;
+ }
+ t.inuse++;
+ if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
+ {
+ linkageToBuffer(buf, t.linkage);
+ buf.writeByte(' ');
+ }
+ if (t.linkage == LINK.objc && isStatic)
+ buf.write("static ");
+ if (t.next)
+ {
+ typeToBuffer(t.next, null, buf, hgs);
+ if (ident)
+ buf.writeByte(' ');
+ }
+ else if (hgs.ddoc)
+ buf.writestring("auto ");
+ if (ident)
+ buf.writestring(ident);
+ parametersToBuffer(t.parameterList, buf, hgs);
+ /* Use postfix style for attributes
+ */
+ if (t.mod)
+ {
+ buf.writeByte(' ');
+ MODtoBuffer(buf, t.mod);
+ }
+
+ void dg(string str)
+ {
+ buf.writeByte(' ');
+ buf.writestring(str);
+ }
+ t.attributesApply(&dg);
+
+ t.inuse--;
+}
+
+private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, TemplateDeclaration td,
+ OutBuffer* buf, HdrGenState* hgs)
+{
+ if (t.inuse)
+ {
+ t.inuse = 2; // flag error to caller
+ return;
+ }
+ t.inuse++;
+
+ /* Use 'storage class' (prefix) style for attributes
+ */
+ if (t.mod)
+ {
+ MODtoBuffer(buf, t.mod);
+ buf.writeByte(' ');
+ }
+
+ void ignoreReturn(string str)
+ {
+ if (str != "return")
+ {
+ // don't write 'ref' for ctors
+ if ((ident == Id.ctor) && str == "ref")
+ return;
+ buf.writestring(str);
+ buf.writeByte(' ');
+ }
+ }
+ t.attributesApply(&ignoreReturn);
+
+ if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
+ {
+ linkageToBuffer(buf, t.linkage);
+ buf.writeByte(' ');
+ }
+ if (ident && ident.toHChars2() != ident.toChars())
+ {
+ // Don't print return type for ctor, dtor, unittest, etc
+ }
+ else if (t.next)
+ {
+ typeToBuffer(t.next, null, buf, hgs);
+ if (ident)
+ buf.writeByte(' ');
+ }
+ else if (hgs.ddoc)
+ buf.writestring("auto ");
+ if (ident)
+ buf.writestring(ident.toHChars2());
+ if (td)
+ {
+ buf.writeByte('(');
+ foreach (i, p; *td.origParameters)
+ {
+ if (i)
+ buf.writestring(", ");
+ p.templateParameterToBuffer(buf, hgs);
+ }
+ buf.writeByte(')');
+ }
+ parametersToBuffer(t.parameterList, buf, hgs);
+ if (t.isreturn)
+ {
+ buf.writestring(" return");
+ }
+ t.inuse--;
+}
+
+
+private void initializerToBuffer(Initializer inx, OutBuffer* buf, HdrGenState* hgs)
+{
+ void visitError(ErrorInitializer iz)
+ {
+ buf.writestring("__error__");
+ }
+
+ void visitVoid(VoidInitializer iz)
+ {
+ buf.writestring("void");
+ }
+
+ void visitStruct(StructInitializer si)
+ {
+ //printf("StructInitializer::toCBuffer()\n");
+ buf.writeByte('{');
+ foreach (i, const id; si.field)
+ {
+ if (i)
+ buf.writestring(", ");
+ if (id)
+ {
+ buf.writestring(id.toString());
+ buf.writeByte(':');
+ }
+ if (auto iz = si.value[i])
+ initializerToBuffer(iz, buf, hgs);
+ }
+ buf.writeByte('}');
+ }
+
+ void visitArray(ArrayInitializer ai)
+ {
+ buf.writeByte('[');
+ foreach (i, ex; ai.index)
+ {
+ if (i)
+ buf.writestring(", ");
+ if (ex)
+ {
+ ex.expressionToBuffer(buf, hgs);
+ buf.writeByte(':');
+ }
+ if (auto iz = ai.value[i])
+ initializerToBuffer(iz, buf, hgs);
+ }
+ buf.writeByte(']');
+ }
+
+ void visitExp(ExpInitializer ei)
+ {
+ ei.exp.expressionToBuffer(buf, hgs);
+ }
+
+ void visitC(CInitializer ci)
+ {
+ buf.writeByte('{');
+ foreach (i, ref DesigInit di; ci.initializerList)
+ {
+ if (i)
+ buf.writestring(", ");
+ if (di.designatorList)
+ {
+ foreach (ref Designator d; (*di.designatorList)[])
+ {
+ if (d.exp)
+ {
+ buf.writeByte('[');
+ toCBuffer(d.exp, buf, hgs);
+ buf.writeByte(']');
+ }
+ else
+ {
+ buf.writeByte('.');
+ buf.writestring(d.ident.toString());
+ }
+ }
+ buf.writeByte('=');
+ }
+ initializerToBuffer(di.initializer, buf, hgs);
+ }
+ buf.writeByte('}');
+ }
+
+ final switch (inx.kind)
+ {
+ case InitKind.error: return visitError (inx.isErrorInitializer ());
+ case InitKind.void_: return visitVoid (inx.isVoidInitializer ());
+ case InitKind.struct_: return visitStruct(inx.isStructInitializer());
+ case InitKind.array: return visitArray (inx.isArrayInitializer ());
+ case InitKind.exp: return visitExp (inx.isExpInitializer ());
+ case InitKind.C_: return visitC (inx.isCInitializer ());
+ }
+}
+
+
+private void typeToBufferx(Type t, OutBuffer* buf, HdrGenState* hgs)
+{
+ void visitType(Type t)
+ {
+ printf("t = %p, ty = %d\n", t, t.ty);
+ assert(0);
+ }
+
+ void visitError(TypeError t)
+ {
+ buf.writestring("_error_");
+ }
+
+ void visitBasic(TypeBasic t)
+ {
+ //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
+ buf.writestring(t.dstring);
+ }
+
+ void visitTraits(TypeTraits t)
+ {
+ //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
+ t.exp.expressionToBuffer(buf, hgs);
+ }
+
+ void visitVector(TypeVector t)
+ {
+ //printf("TypeVector::toCBuffer2(t.mod = %d)\n", t.mod);
+ buf.writestring("__vector(");
+ visitWithMask(t.basetype, t.mod, buf, hgs);
+ buf.writestring(")");
+ }
+
+ void visitSArray(TypeSArray t)
+ {
+ visitWithMask(t.next, t.mod, buf, hgs);
+ buf.writeByte('[');
+ sizeToBuffer(t.dim, buf, hgs);
+ buf.writeByte(']');
+ }
+
+ void visitDArray(TypeDArray t)
+ {
+ Type ut = t.castMod(0);
+ if (hgs.declstring)
+ goto L1;
+ if (ut.equals(Type.tstring))
+ buf.writestring("string");
+ else if (ut.equals(Type.twstring))
+ buf.writestring("wstring");
+ else if (ut.equals(Type.tdstring))
+ buf.writestring("dstring");
+ else
+ {
+ L1:
+ visitWithMask(t.next, t.mod, buf, hgs);
+ buf.writestring("[]");
+ }
+ }
+
+ void visitAArray(TypeAArray t)
+ {
+ visitWithMask(t.next, t.mod, buf, hgs);
+ buf.writeByte('[');
+ visitWithMask(t.index, 0, buf, hgs);
+ buf.writeByte(']');
+ }
+
+ void visitPointer(TypePointer t)
+ {
+ //printf("TypePointer::toCBuffer2() next = %d\n", t.next.ty);
+ if (t.next.ty == Tfunction)
+ visitFuncIdentWithPostfix(cast(TypeFunction)t.next, "function", buf, hgs, false);
+ else
+ {
+ visitWithMask(t.next, t.mod, buf, hgs);
+ buf.writeByte('*');
+ }
+ }
+
+ void visitReference(TypeReference t)
+ {
+ visitWithMask(t.next, t.mod, buf, hgs);
+ buf.writeByte('&');
+ }
+
+ void visitFunction(TypeFunction t)
+ {
+ //printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t.isref);
+ visitFuncIdentWithPostfix(t, null, buf, hgs, false);
+ }
+
+ void visitDelegate(TypeDelegate t)
+ {
+ visitFuncIdentWithPostfix(cast(TypeFunction)t.next, "delegate", buf, hgs, false);
+ }
+
+ void visitTypeQualifiedHelper(TypeQualified t)
+ {
+ foreach (id; t.idents)
+ {
+ if (id.dyncast() == DYNCAST.dsymbol)
+ {
+ buf.writeByte('.');
+ TemplateInstance ti = cast(TemplateInstance)id;
+ ti.dsymbolToBuffer(buf, hgs);
+ }
+ else if (id.dyncast() == DYNCAST.expression)
+ {
+ buf.writeByte('[');
+ (cast(Expression)id).expressionToBuffer(buf, hgs);
+ buf.writeByte(']');
+ }
+ else if (id.dyncast() == DYNCAST.type)
+ {
+ buf.writeByte('[');
+ typeToBufferx(cast(Type)id, buf, hgs);
+ buf.writeByte(']');
+ }
+ else
+ {
+ buf.writeByte('.');
+ buf.writestring(id.toString());
+ }
+ }
+ }
+
+ void visitIdentifier(TypeIdentifier t)
+ {
+ buf.writestring(t.ident.toString());
+ visitTypeQualifiedHelper(t);
+ }
+
+ void visitInstance(TypeInstance t)
+ {
+ t.tempinst.dsymbolToBuffer(buf, hgs);
+ visitTypeQualifiedHelper(t);
+ }
+
+ void visitTypeof(TypeTypeof t)
+ {
+ buf.writestring("typeof(");
+ t.exp.expressionToBuffer(buf, hgs);
+ buf.writeByte(')');
+ visitTypeQualifiedHelper(t);
+ }
+
+ void visitReturn(TypeReturn t)
+ {
+ buf.writestring("typeof(return)");
+ visitTypeQualifiedHelper(t);
+ }
+
+ void visitEnum(TypeEnum t)
+ {
+ buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
+ }
+
+ void visitStruct(TypeStruct t)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13776
+ // Don't use ti.toAlias() to avoid forward reference error
+ // while printing messages.
+ TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
+ if (ti && ti.aliasdecl == t.sym)
+ buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
+ else
+ buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
+ }
+
+ void visitClass(TypeClass t)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13776
+ // Don't use ti.toAlias() to avoid forward reference error
+ // while printing messages.
+ TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
+ if (ti && ti.aliasdecl == t.sym)
+ buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
+ else
+ buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
+ }
+
+ void visitTag(TypeTag t)
+ {
+ buf.writestring(Token.toChars(t.tok));
+ buf.writeByte(' ');
+ if (t.id)
+ buf.writestring(t.id.toChars());
+ }
+
+ void visitTuple(TypeTuple t)
+ {
+ parametersToBuffer(ParameterList(t.arguments, VarArg.none), buf, hgs);
+ }
+
+ void visitSlice(TypeSlice t)
+ {
+ visitWithMask(t.next, t.mod, buf, hgs);
+ buf.writeByte('[');
+ sizeToBuffer(t.lwr, buf, hgs);
+ buf.writestring(" .. ");
+ sizeToBuffer(t.upr, buf, hgs);
+ buf.writeByte(']');
+ }
+
+ void visitNull(TypeNull t)
+ {
+ buf.writestring("typeof(null)");
+ }
+
+ void visitMixin(TypeMixin t)
+ {
+ buf.writestring("mixin(");
+ argsToBuffer(t.exps, buf, hgs, null);
+ buf.writeByte(')');
+ }
+
+ void visitNoreturn(TypeNoreturn t)
+ {
+ buf.writestring("noreturn");
+ }
+
+
+ switch (t.ty)
+ {
+ default: return t.isTypeBasic() ?
+ visitBasic(cast(TypeBasic)t) :
+ visitType(t);
+
+ case Terror: return visitError(cast(TypeError)t);
+ case Ttraits: return visitTraits(cast(TypeTraits)t);
+ case Tvector: return visitVector(cast(TypeVector)t);
+ case Tsarray: return visitSArray(cast(TypeSArray)t);
+ case Tarray: return visitDArray(cast(TypeDArray)t);
+ case Taarray: return visitAArray(cast(TypeAArray)t);
+ case Tpointer: return visitPointer(cast(TypePointer)t);
+ case Treference: return visitReference(cast(TypeReference)t);
+ case Tfunction: return visitFunction(cast(TypeFunction)t);
+ case Tdelegate: return visitDelegate(cast(TypeDelegate)t);
+ case Tident: return visitIdentifier(cast(TypeIdentifier)t);
+ case Tinstance: return visitInstance(cast(TypeInstance)t);
+ case Ttypeof: return visitTypeof(cast(TypeTypeof)t);
+ case Treturn: return visitReturn(cast(TypeReturn)t);
+ case Tenum: return visitEnum(cast(TypeEnum)t);
+ case Tstruct: return visitStruct(cast(TypeStruct)t);
+ case Tclass: return visitClass(cast(TypeClass)t);
+ case Ttuple: return visitTuple (cast(TypeTuple)t);
+ case Tslice: return visitSlice(cast(TypeSlice)t);
+ case Tnull: return visitNull(cast(TypeNull)t);
+ case Tmixin: return visitMixin(cast(TypeMixin)t);
+ case Tnoreturn: return visitNoreturn(cast(TypeNoreturn)t);
+ case Ttag: return visitTag(cast(TypeTag)t);
+ }
+}
diff --git a/gcc/d/dmd/hdrgen.h b/gcc/d/dmd/hdrgen.h
index 6822aaf44a9..531d5d8c0da 100644
--- a/gcc/d/dmd/hdrgen.h
+++ b/gcc/d/dmd/hdrgen.h
@@ -10,45 +10,12 @@
#pragma once
-#include "root/dsystem.h" // memset()
-#include "dsymbol.h"
+#include "globals.h"
+#include "mtype.h"
-void genhdrfile(Module *m);
-
-struct HdrGenState
-{
- bool hdrgen; // true if generating header file
- bool ddoc; // true if generating Ddoc file
- bool fullDump; // true if generating a full AST dump file
- bool fullQual; // fully qualify types when printing
- int tpltMember;
- int autoMember;
- int forStmtInit;
-
- HdrGenState() { memset(this, 0, sizeof(HdrGenState)); }
-};
-
-void toCBuffer(Statement *s, OutBuffer *buf, HdrGenState *hgs);
-void toCBuffer(Type *t, OutBuffer *buf, Identifier *ident, HdrGenState *hgs);
-void toCBuffer(Dsymbol *s, OutBuffer *buf, HdrGenState *hgs);
-void toCBuffer(Initializer *iz, OutBuffer *buf, HdrGenState *hgs);
-void toCBuffer(Expression *e, OutBuffer *buf, HdrGenState *hgs);
-void toCBuffer(TemplateParameter *tp, OutBuffer *buf, HdrGenState *hgs);
-
-void toCBufferInstance(TemplateInstance *ti, OutBuffer *buf, bool qualifyTypes = false);
-
-void functionToBufferFull(TypeFunction *tf, OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TemplateDeclaration *td);
-void functionToBufferWithIdent(TypeFunction *t, OutBuffer *buf, const char *ident);
-
-void argExpTypesToCBuffer(OutBuffer *buf, Expressions *arguments);
-
-void arrayObjectsToBuffer(OutBuffer *buf, Objects *objects);
+class Module;
+void genhdrfile(Module *m);
+void genCppHdrFiles(Modules &ms);
void moduleToBuffer(OutBuffer *buf, Module *m);
-
const char *parametersTypeToChars(ParameterList pl);
-const char *parameterToChars(Parameter *parameter, TypeFunction *tf, bool fullQual);
-
-bool stcToBuffer(OutBuffer *buf, StorageClass stc);
-const char *stcToChars(StorageClass& stc);
-const char *linkageToChars(LINK linkage);
diff --git a/gcc/d/dmd/iasm.c b/gcc/d/dmd/iasm.c
deleted file mode 100644
index fc58a3c4462..00000000000
--- a/gcc/d/dmd/iasm.c
+++ /dev/null
@@ -1,44 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 2018-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/iasm.c
- */
-
-/* Inline assembler for the D programming language compiler
- */
-
-#include "scope.h"
-#include "declaration.h"
-#include "statement.h"
-
-#ifdef IN_GCC
-Statement *gccAsmSemantic(GccAsmStatement *s, Scope *sc);
-#else
-Statement *inlineAsmSemantic(InlineAsmStatement *s, Scope *sc);
-#endif
-
-Statement *asmSemantic(AsmStatement *s, Scope *sc)
-{
- //printf("AsmStatement::semantic()\n");
-
- FuncDeclaration *fd = sc->parent->isFuncDeclaration();
- assert(fd);
-
- if (!s->tokens)
- return NULL;
-
- // Assume assembler code takes care of setting the return value
- sc->func->hasReturnExp |= 8;
-
-#ifdef IN_GCC
- GccAsmStatement *eas = new GccAsmStatement(s->loc, s->tokens);
- return gccAsmSemantic(eas, sc);
-#else
- InlineAsmStatement *ias = new InlineAsmStatement(s->loc, s->tokens);
- return inlineAsmSemantic(ias, sc);
-#endif
-}
diff --git a/gcc/d/dmd/iasm.d b/gcc/d/dmd/iasm.d
new file mode 100644
index 00000000000..df8d1c98770
--- /dev/null
+++ b/gcc/d/dmd/iasm.d
@@ -0,0 +1,59 @@
+/**
+ * Inline assembler for the D programming language compiler.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/iasm.html, Inline Assembler)
+ *
+ * Copyright (C) 2018-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasm.d, _iasm.d)
+ * Documentation: https://dlang.org/phobos/dmd_iasm.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasm.d
+ */
+
+module dmd.iasm;
+
+import dmd.dscope;
+import dmd.func;
+import dmd.statement;
+
+version (MARS)
+{
+ import dmd.iasmdmd;
+}
+else version (IN_GCC)
+{
+ import dmd.iasmgcc;
+}
+
+/************************ AsmStatement ***************************************/
+
+extern(C++) Statement asmSemantic(AsmStatement s, Scope *sc)
+{
+ //printf("AsmStatement.semantic()\n");
+
+ FuncDeclaration fd = sc.parent.isFuncDeclaration();
+ assert(fd);
+
+ if (!s.tokens)
+ return null;
+
+ // Assume assembler code takes care of setting the return value
+ sc.func.hasReturnExp |= 8;
+
+ version (MARS)
+ {
+ auto ias = new InlineAsmStatement(s.loc, s.tokens);
+ return inlineAsmSemantic(ias, sc);
+ }
+ else version (IN_GCC)
+ {
+ auto eas = new GccAsmStatement(s.loc, s.tokens);
+ return gccAsmSemantic(eas, sc);
+ }
+ else
+ {
+ s.error("D inline assembler statements are not supported");
+ return new ErrorStatement();
+ }
+}
diff --git a/gcc/d/dmd/iasmgcc.c b/gcc/d/dmd/iasmgcc.c
deleted file mode 100644
index e3940a8adbf..00000000000
--- a/gcc/d/dmd/iasmgcc.c
+++ /dev/null
@@ -1,379 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 2018-2021 by The D Language Foundation, All Rights Reserved
- * written by Iain Buclaw
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/iasmgcc.c
- */
-
-/* Inline assembler for the GCC D compiler.
- */
-
-#include "scope.h"
-#include "expression.h"
-#include "declaration.h"
-#include "errors.h"
-#include "parse.h"
-#include "statement.h"
-
-/***********************************
- * Parse list of extended asm input or output operands.
- * Grammar:
- * | Operands:
- * | SymbolicName(opt) StringLiteral ( AssignExpression )
- * | SymbolicName(opt) StringLiteral ( AssignExpression ), Operands
- * |
- * | SymbolicName:
- * | [ Identifier ]
- * Params:
- * p = parser state
- * s = asm statement to parse
- * Returns:
- * number of operands added to the gcc asm statement
- */
-static int parseExtAsmOperands(Parser *p, GccAsmStatement *s)
-{
- int numargs = 0;
-
- while (1)
- {
- Expression *arg = NULL;
- Identifier *name = NULL;
- Expression *constraint = NULL;
-
- switch (p->token.value)
- {
- case TOKsemicolon:
- case TOKcolon:
- case TOKeof:
- return numargs;
-
- case TOKlbracket:
- if (p->peekNext() == TOKidentifier)
- {
- // Skip over openings `[`
- p->nextToken();
- // Store the symbolic name
- name = p->token.ident;
- p->nextToken();
- }
- else
- {
- p->error(s->loc, "expected identifier after `[`");
- goto Lerror;
- }
- // Look for closing `]`
- p->check(TOKrbracket);
- // Look for the string literal and fall through
- if (p->token.value != TOKstring)
- goto Ldefault;
- // fall through
-
- case TOKstring:
- constraint = p->parsePrimaryExp();
- // @@@DEPRECATED@@@
- // Old parser allowed omitting parentheses around the expression.
- // Deprecated in 2.091. Can be made permanent error after 2.100
- if (p->token.value != TOKlparen)
- {
- arg = p->parseAssignExp();
- deprecation(arg->loc, "`%s` must be surrounded by parentheses", arg->toChars());
- }
- else
- {
- // Look for the opening `(`
- p->check(TOKlparen);
- // Parse the assign expression
- arg = p->parseAssignExp();
- // Look for the closing `)`
- p->check(TOKrparen);
- }
-
- if (!s->args)
- {
- s->names = new Identifiers();
- s->constraints = new Expressions();
- s->args = new Expressions();
- }
- s->names->push(name);
- s->args->push(arg);
- s->constraints->push(constraint);
- numargs++;
-
- if (p->token.value == TOKcomma)
- p->nextToken();
- break;
-
- default:
- Ldefault:
- p->error("expected constant string constraint for operand, not `%s`",
- p->token.toChars());
- goto Lerror;
- }
- }
-Lerror:
- while (p->token.value != TOKrcurly &&
- p->token.value != TOKsemicolon &&
- p->token.value != TOKeof)
- p->nextToken();
-
- return numargs;
-}
-
-/***********************************
- * Parse list of extended asm clobbers.
- * Grammar:
- * | Clobbers:
- * | StringLiteral
- * | StringLiteral , Clobbers
- * Params:
- * p = parser state
- * Returns:
- * array of parsed clobber expressions
- */
-static Expressions *parseExtAsmClobbers(Parser *p)
-{
- Expressions *clobbers = NULL;
-
- while (1)
- {
- Expression *clobber;
-
- switch (p->token.value)
- {
- case TOKsemicolon:
- case TOKcolon:
- case TOKeof:
- return clobbers;
-
- case TOKstring:
- clobber = p->parsePrimaryExp();
- if (!clobbers)
- clobbers = new Expressions();
- clobbers->push(clobber);
-
- if (p->token.value == TOKcomma)
- p->nextToken();
- break;
-
- default:
- p->error("expected constant string constraint for clobber name, not `%s`",
- p->token.toChars());
- goto Lerror;
- }
- }
-Lerror:
- while (p->token.value != TOKrcurly &&
- p->token.value != TOKsemicolon &&
- p->token.value != TOKeof)
- p->nextToken();
-
- return clobbers;
-}
-
-/***********************************
- * Parse list of extended asm goto labels.
- * Grammar:
- * | GotoLabels:
- * | Identifier
- * | Identifier , GotoLabels
- * Params:
- * p = parser state
- * Returns:
- * array of parsed goto labels
- */
-static Identifiers *parseExtAsmGotoLabels(Parser *p)
-{
- Identifiers *labels = NULL;
-
- while (1)
- {
- switch (p->token.value)
- {
- case TOKsemicolon:
- case TOKeof:
- return labels;
-
- case TOKidentifier:
- if (!labels)
- labels = new Identifiers();
- labels->push(p->token.ident);
-
- if (p->nextToken() == TOKcomma)
- p->nextToken();
- break;
-
- default:
- p->error("expected identifier for goto label name, not `%s`",
- p->token.toChars());
- goto Lerror;
- }
- }
-Lerror:
- while (p->token.value != TOKrcurly &&
- p->token.value != TOKsemicolon &&
- p->token.value != TOKeof)
- p->nextToken();
-
- return labels;
-}
-
-/***********************************
- * Parse a gcc asm statement.
- * There are three forms of inline asm statements, basic, extended, and goto.
- * Grammar:
- * | AsmInstruction:
- * | BasicAsmInstruction
- * | ExtAsmInstruction
- * | GotoAsmInstruction
- * |
- * | BasicAsmInstruction:
- * | Expression
- * |
- * | ExtAsmInstruction:
- * | Expression : Operands(opt) : Operands(opt) : Clobbers(opt)
- * |
- * | GotoAsmInstruction:
- * | Expression : : Operands(opt) : Clobbers(opt) : GotoLabels(opt)
- * Params:
- * p = parser state
- * s = asm statement to parse
- * Returns:
- * the parsed gcc asm statement
- */
-static GccAsmStatement *parseGccAsm(Parser *p, GccAsmStatement *s)
-{
- s->insn = p->parseExpression();
- if (p->token.value == TOKsemicolon || p->token.value == TOKeof)
- goto Ldone;
-
- // No semicolon followed after instruction template, treat as extended asm.
- for (int section = 0; section < 4; ++section)
- {
- p->check(TOKcolon);
-
- switch (section)
- {
- case 0:
- s->outputargs = parseExtAsmOperands(p, s);
- break;
-
- case 1:
- parseExtAsmOperands(p, s);
- break;
-
- case 2:
- s->clobbers = parseExtAsmClobbers(p);
- break;
-
- case 3:
- s->labels = parseExtAsmGotoLabels(p);
- break;
-
- default:
- assert(0);
- }
-
- if (p->token.value == TOKsemicolon || p->token.value == TOKeof)
- goto Ldone;
- }
-Ldone:
- p->check(TOKsemicolon);
-
- return s;
-}
-
-/***********************************
- * Parse and run semantic analysis on a GccAsmStatement.
- * Params:
- * s = gcc asm statement being parsed
- * sc = the scope where the asm statement is located
- * Returns:
- * the completed gcc asm statement, or null if errors occurred
- */
-Statement *gccAsmSemantic(GccAsmStatement *s, Scope *sc)
-{
- //printf("GccAsmStatement::semantic()\n");
- Parser p(sc->_module, (const utf8_t *)";", 1, false);
-
- // Make a safe copy of the token list before parsing.
- Token *toklist = NULL;
- Token **ptoklist = &toklist;
-
- for (Token *token = s->tokens; token; token = token->next)
- {
- *ptoklist = Token::alloc();
- memcpy(*ptoklist, token, sizeof(Token));
- ptoklist = &(*ptoklist)->next;
- *ptoklist = NULL;
- }
- p.token = *toklist;
- p.scanloc = s->loc;
-
- // Parse the gcc asm statement.
- s = parseGccAsm(&p, s);
- if (p.errors)
- return NULL;
- s->stc = sc->stc;
-
- // Fold the instruction template string.
- s->insn = expressionSemantic(s->insn, sc);
- s->insn = s->insn->ctfeInterpret();
-
- if (s->insn->op != TOKstring || ((StringExp *) s->insn)->sz != 1)
- s->insn->error("asm instruction template must be a constant char string");
-
- if (s->labels && s->outputargs)
- s->error("extended asm statements with labels cannot have output constraints");
-
- // Analyse all input and output operands.
- if (s->args)
- {
- for (size_t i = 0; i < s->args->length; i++)
- {
- Expression *e = (*s->args)[i];
- e = expressionSemantic(e, sc);
- // Check argument is a valid lvalue/rvalue.
- if (i < s->outputargs)
- e = e->modifiableLvalue(sc, NULL);
- else if (e->checkValue())
- e = new ErrorExp();
- (*s->args)[i] = e;
-
- e = (*s->constraints)[i];
- e = expressionSemantic(e, sc);
- assert(e->op == TOKstring && ((StringExp *) e)->sz == 1);
- (*s->constraints)[i] = e;
- }
- }
-
- // Analyse all clobbers.
- if (s->clobbers)
- {
- for (size_t i = 0; i < s->clobbers->length; i++)
- {
- Expression *e = (*s->clobbers)[i];
- e = expressionSemantic(e, sc);
- assert(e->op == TOKstring && ((StringExp *) e)->sz == 1);
- (*s->clobbers)[i] = e;
- }
- }
-
- // Analyse all goto labels.
- if (s->labels)
- {
- for (size_t i = 0; i < s->labels->length; i++)
- {
- Identifier *ident = (*s->labels)[i];
- GotoStatement *gs = new GotoStatement(s->loc, ident);
- if (!s->gotos)
- s->gotos = new GotoStatements();
- s->gotos->push(gs);
- statementSemantic(gs, sc);
- }
- }
-
- return s;
-}
diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d
new file mode 100644
index 00000000000..e61fb23eb5d
--- /dev/null
+++ b/gcc/d/dmd/iasmgcc.d
@@ -0,0 +1,537 @@
+/**
+ * Inline assembler for the GCC D compiler.
+ *
+ * Copyright (C) 2018-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Iain Buclaw
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d, _iasmgcc.d)
+ * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasmgcc.d
+ */
+
+module dmd.iasmgcc;
+
+import core.stdc.string;
+
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.dscope;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.identifier;
+import dmd.globals;
+import dmd.parse;
+import dmd.tokens;
+import dmd.statement;
+import dmd.statementsem;
+
+private:
+
+/***********************************
+ * Parse list of extended asm input or output operands.
+ * Grammar:
+ * | Operands:
+ * | SymbolicName(opt) StringLiteral ( AssignExpression )
+ * | SymbolicName(opt) StringLiteral ( AssignExpression ), Operands
+ * |
+ * | SymbolicName:
+ * | [ Identifier ]
+ * Params:
+ * p = parser state
+ * s = asm statement to parse
+ * Returns:
+ * number of operands added to the gcc asm statement
+ */
+int parseExtAsmOperands(Parser)(Parser p, GccAsmStatement s)
+{
+ int numargs = 0;
+
+ while (1)
+ {
+ Expression arg;
+ Identifier name;
+ Expression constraint;
+
+ switch (p.token.value)
+ {
+ case TOK.semicolon:
+ case TOK.colon:
+ case TOK.endOfFile:
+ return numargs;
+
+ case TOK.leftBracket:
+ if (p.peekNext() == TOK.identifier)
+ {
+ // Skip over opening `[`
+ p.nextToken();
+ // Store the symbolic name
+ name = p.token.ident;
+ p.nextToken();
+ }
+ else
+ {
+ p.error(s.loc, "expected identifier after `[`");
+ goto Lerror;
+ }
+ // Look for closing `]`
+ p.check(TOK.rightBracket);
+ // Look for the string literal and fall through
+ if (p.token.value == TOK.string_)
+ goto case;
+ else
+ goto default;
+
+ case TOK.string_:
+ constraint = p.parsePrimaryExp();
+ // @@@DEPRECATED@@@
+ // Old parser allowed omitting parentheses around the expression.
+ // Deprecated in 2.091. Can be made permanent error after 2.100
+ if (p.token.value != TOK.leftParenthesis)
+ {
+ arg = p.parseAssignExp();
+ deprecation(arg.loc, "`%s` must be surrounded by parentheses", arg.toChars());
+ }
+ else
+ {
+ // Look for the opening `(`
+ p.check(TOK.leftParenthesis);
+ // Parse the assign expression
+ arg = p.parseAssignExp();
+ // Look for the closing `)`
+ p.check(TOK.rightParenthesis);
+ }
+
+ if (!s.args)
+ {
+ s.names = new Identifiers();
+ s.constraints = new Expressions();
+ s.args = new Expressions();
+ }
+ s.names.push(name);
+ s.args.push(arg);
+ s.constraints.push(constraint);
+ numargs++;
+
+ if (p.token.value == TOK.comma)
+ p.nextToken();
+ break;
+
+ default:
+ p.error("expected constant string constraint for operand, not `%s`",
+ p.token.toChars());
+ goto Lerror;
+ }
+ }
+Lerror:
+ while (p.token.value != TOK.rightCurly &&
+ p.token.value != TOK.semicolon &&
+ p.token.value != TOK.endOfFile)
+ p.nextToken();
+
+ return numargs;
+}
+
+/***********************************
+ * Parse list of extended asm clobbers.
+ * Grammar:
+ * | Clobbers:
+ * | StringLiteral
+ * | StringLiteral , Clobbers
+ * Params:
+ * p = parser state
+ * Returns:
+ * array of parsed clobber expressions
+ */
+Expressions *parseExtAsmClobbers(Parser)(Parser p)
+{
+ Expressions *clobbers;
+
+ while (1)
+ {
+ Expression clobber;
+
+ switch (p.token.value)
+ {
+ case TOK.semicolon:
+ case TOK.colon:
+ case TOK.endOfFile:
+ return clobbers;
+
+ case TOK.string_:
+ clobber = p.parsePrimaryExp();
+ if (!clobbers)
+ clobbers = new Expressions();
+ clobbers.push(clobber);
+
+ if (p.token.value == TOK.comma)
+ p.nextToken();
+ break;
+
+ default:
+ p.error("expected constant string constraint for clobber name, not `%s`",
+ p.token.toChars());
+ goto Lerror;
+ }
+ }
+Lerror:
+ while (p.token.value != TOK.rightCurly &&
+ p.token.value != TOK.semicolon &&
+ p.token.value != TOK.endOfFile)
+ p.nextToken();
+
+ return clobbers;
+}
+
+/***********************************
+ * Parse list of extended asm goto labels.
+ * Grammar:
+ * | GotoLabels:
+ * | Identifier
+ * | Identifier , GotoLabels
+ * Params:
+ * p = parser state
+ * Returns:
+ * array of parsed goto labels
+ */
+Identifiers *parseExtAsmGotoLabels(Parser)(Parser p)
+{
+ Identifiers *labels;
+
+ while (1)
+ {
+ switch (p.token.value)
+ {
+ case TOK.semicolon:
+ case TOK.endOfFile:
+ return labels;
+
+ case TOK.identifier:
+ if (!labels)
+ labels = new Identifiers();
+ labels.push(p.token.ident);
+
+ if (p.nextToken() == TOK.comma)
+ p.nextToken();
+ break;
+
+ default:
+ p.error("expected identifier for goto label name, not `%s`",
+ p.token.toChars());
+ goto Lerror;
+ }
+ }
+Lerror:
+ while (p.token.value != TOK.rightCurly &&
+ p.token.value != TOK.semicolon &&
+ p.token.value != TOK.endOfFile)
+ p.nextToken();
+
+ return labels;
+}
+
+/***********************************
+ * Parse a gcc asm statement.
+ * There are three forms of inline asm statements, basic, extended, and goto.
+ * Grammar:
+ * | AsmInstruction:
+ * | BasicAsmInstruction
+ * | ExtAsmInstruction
+ * | GotoAsmInstruction
+ * |
+ * | BasicAsmInstruction:
+ * | AssignExpression
+ * |
+ * | ExtAsmInstruction:
+ * | AssignExpression : Operands(opt) : Operands(opt) : Clobbers(opt)
+ * |
+ * | GotoAsmInstruction:
+ * | AssignExpression : : Operands(opt) : Clobbers(opt) : GotoLabels(opt)
+ * Params:
+ * p = parser state
+ * s = asm statement to parse
+ * Returns:
+ * the parsed gcc asm statement
+ */
+GccAsmStatement parseGccAsm(Parser)(Parser p, GccAsmStatement s)
+{
+ s.insn = p.parseAssignExp();
+ if (p.token.value == TOK.semicolon || p.token.value == TOK.endOfFile)
+ goto Ldone;
+
+ // No semicolon followed after instruction template, treat as extended asm.
+ foreach (section; 0 .. 4)
+ {
+ p.check(TOK.colon);
+
+ final switch (section)
+ {
+ case 0:
+ s.outputargs = p.parseExtAsmOperands(s);
+ break;
+
+ case 1:
+ p.parseExtAsmOperands(s);
+ break;
+
+ case 2:
+ s.clobbers = p.parseExtAsmClobbers();
+ break;
+
+ case 3:
+ s.labels = p.parseExtAsmGotoLabels();
+ break;
+ }
+
+ if (p.token.value == TOK.semicolon || p.token.value == TOK.endOfFile)
+ goto Ldone;
+ }
+Ldone:
+ p.check(TOK.semicolon);
+
+ return s;
+}
+
+/***********************************
+ * Parse and run semantic analysis on a GccAsmStatement.
+ * Params:
+ * s = gcc asm statement being parsed
+ * sc = the scope where the asm statement is located
+ * Returns:
+ * the completed gcc asm statement, or null if errors occurred
+ */
+public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
+{
+ //printf("GccAsmStatement.semantic()\n");
+ scope p = new Parser!ASTCodegen(sc._module, ";", false);
+
+ // Make a safe copy of the token list before parsing.
+ Token *toklist = null;
+ Token **ptoklist = &toklist;
+
+ for (Token *token = s.tokens; token; token = token.next)
+ {
+ *ptoklist = p.allocateToken();
+ memcpy(*ptoklist, token, Token.sizeof);
+ ptoklist = &(*ptoklist).next;
+ *ptoklist = null;
+ }
+ p.token = *toklist;
+ p.scanloc = s.loc;
+
+ // Parse the gcc asm statement.
+ const errors = global.errors;
+ s = p.parseGccAsm(s);
+ if (errors != global.errors)
+ return null;
+ s.stc = sc.stc;
+
+ // Fold the instruction template string.
+ s.insn = semanticString(sc, s.insn, "asm instruction template");
+
+ if (s.labels && s.outputargs)
+ s.error("extended asm statements with labels cannot have output constraints");
+
+ // Analyse all input and output operands.
+ if (s.args)
+ {
+ foreach (i; 0 .. s.args.dim)
+ {
+ Expression e = (*s.args)[i];
+ e = e.expressionSemantic(sc);
+ // Check argument is a valid lvalue/rvalue.
+ if (i < s.outputargs)
+ e = e.modifiableLvalue(sc, null);
+ else if (e.checkValue())
+ e = ErrorExp.get();
+ (*s.args)[i] = e;
+
+ e = (*s.constraints)[i];
+ e = e.expressionSemantic(sc);
+ assert(e.op == TOK.string_ && (cast(StringExp) e).sz == 1);
+ (*s.constraints)[i] = e;
+ }
+ }
+
+ // Analyse all clobbers.
+ if (s.clobbers)
+ {
+ foreach (i; 0 .. s.clobbers.dim)
+ {
+ Expression e = (*s.clobbers)[i];
+ e = e.expressionSemantic(sc);
+ assert(e.op == TOK.string_ && (cast(StringExp) e).sz == 1);
+ (*s.clobbers)[i] = e;
+ }
+ }
+
+ // Analyse all goto labels.
+ if (s.labels)
+ {
+ foreach (i; 0 .. s.labels.dim)
+ {
+ Identifier ident = (*s.labels)[i];
+ GotoStatement gs = new GotoStatement(s.loc, ident);
+ if (!s.gotos)
+ s.gotos = new GotoStatements();
+ s.gotos.push(gs);
+ gs.statementSemantic(sc);
+ }
+ }
+
+ return s;
+}
+
+unittest
+{
+ import dmd.mtype : TypeBasic;
+
+ uint errors = global.startGagging();
+ scope(exit) global.endGagging(errors);
+
+ // If this check fails, then Type._init() was called before reaching here,
+ // and the entire chunk of code that follows can be removed.
+ assert(ASTCodegen.Type.tint32 is null);
+ // Minimally initialize the cached types in ASTCodegen.Type, as they are
+ // dependencies for some fail asm tests to succeed.
+ ASTCodegen.Type.stringtable._init();
+ scope(exit)
+ {
+ ASTCodegen.Type.deinitialize();
+ ASTCodegen.Type.tint32 = null;
+ }
+ scope tint32 = new TypeBasic(ASTCodegen.Tint32);
+ ASTCodegen.Type.tint32 = tint32;
+
+ // Imitates asmSemantic if version = IN_GCC.
+ static int semanticAsm(Token* tokens)
+ {
+ const errors = global.errors;
+ scope gas = new GccAsmStatement(Loc.initial, tokens);
+ scope p = new Parser!ASTCodegen(null, ";", false);
+ p.token = *tokens;
+ p.parseGccAsm(gas);
+ return global.errors - errors;
+ }
+
+ // Imitates parseStatement for asm statements.
+ static void parseAsm(string input, bool expectError)
+ {
+ // Generate tokens from input test.
+ scope p = new Parser!ASTCodegen(null, input, false);
+ p.nextToken();
+
+ Token* toklist = null;
+ Token** ptoklist = &toklist;
+ p.check(TOK.asm_);
+ p.check(TOK.leftCurly);
+ while (1)
+ {
+ if (p.token.value == TOK.rightCurly || p.token.value == TOK.endOfFile)
+ break;
+ if (p.token.value == TOK.colonColon)
+ {
+ *ptoklist = p.allocateToken();
+ memcpy(*ptoklist, &p.token, Token.sizeof);
+ (*ptoklist).value = TOK.colon;
+ ptoklist = &(*ptoklist).next;
+
+ *ptoklist = p.allocateToken();
+ memcpy(*ptoklist, &p.token, Token.sizeof);
+ (*ptoklist).value = TOK.colon;
+ ptoklist = &(*ptoklist).next;
+ }
+ else
+ {
+ *ptoklist = p.allocateToken();
+ memcpy(*ptoklist, &p.token, Token.sizeof);
+ ptoklist = &(*ptoklist).next;
+ }
+ *ptoklist = null;
+ p.nextToken();
+ }
+ p.check(TOK.rightCurly);
+
+ auto res = semanticAsm(toklist);
+ // Checks for both unexpected passes and failures.
+ assert((res == 0) != expectError);
+ }
+
+ /// Assembly Tests, all should pass.
+ /// Note: Frontend is not initialized, use only strings and identifiers.
+ immutable string[] passAsmTests = [
+ // Basic asm statement
+ q{ asm { "nop";
+ } },
+
+ // Extended asm statement
+ q{ asm { "cpuid"
+ : "=a" (a), "=b" (b), "=c" (c), "=d" (d)
+ : "a" (input);
+ } },
+
+ // Assembly with symbolic names
+ q{ asm { "bts %[base], %[offset]"
+ : [base] "+rm" (*ptr),
+ : [offset] "Ir" (bitnum);
+ } },
+
+ // Assembly with clobbers
+ q{ asm { "cpuid"
+ : "=a" (a)
+ : "a" (input)
+ : "ebx", "ecx", "edx";
+ } },
+
+ // Goto asm statement
+ q{ asm { "jmp %l0"
+ :
+ :
+ :
+ : Ljmplabel;
+ } },
+
+ // Any CTFE-able string allowed as instruction template.
+ q{ asm { generateAsm();
+ } },
+
+ // Likewise mixins, permissible so long as the result is a string.
+ q{ asm { mixin(`"repne"`, `~ "scasb"`);
+ } },
+
+ // :: token tests
+ q{ asm { "" : : : "memory"; } },
+ q{ asm { "" :: : "memory"; } },
+ q{ asm { "" : :: "memory"; } },
+ q{ asm { "" ::: "memory"; } },
+ ];
+
+ immutable string[] failAsmTests = [
+ // Found 'h' when expecting ';'
+ q{ asm { ""h;
+ } },
+
+ // https://issues.dlang.org/show_bug.cgi?id=20592
+ q{ asm { "nop" : [name] string (expr); } },
+
+ // Expression expected, not ';'
+ q{ asm { ""[;
+ } },
+
+ // Expression expected, not ':'
+ q{ asm { ""
+ :
+ : "g" (a ? b : : c);
+ } },
+
+ // Found ',' when expecting ':'
+ q{ asm { "", "";
+ } },
+ ];
+
+ foreach (test; passAsmTests)
+ parseAsm(test, false);
+
+ foreach (test; failAsmTests)
+ parseAsm(test, true);
+}
diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d
new file mode 100644
index 00000000000..1f04dcfbc11
--- /dev/null
+++ b/gcc/d/dmd/id.d
@@ -0,0 +1,568 @@
+/**
+ * Contains the `Id` struct with a list of predefined symbols the compiler knows about.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/id.d, _id.d)
+ * Documentation: https://dlang.org/phobos/dmd_id.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/id.d
+ */
+
+module dmd.id;
+
+import dmd.identifier;
+import dmd.tokens;
+
+/**
+ * Represents a list of predefined symbols the compiler knows about.
+ *
+ * All static fields in this struct represents a specific predefined symbol.
+ */
+extern (C++) struct Id
+{
+ static __gshared:
+
+ mixin(msgtable.generate(&identifier));
+
+ /**
+ * Populates the identifier pool with all predefined symbols.
+ *
+ * An identifier that corresponds to each static field in this struct will
+ * be placed in the identifier pool.
+ */
+ extern(C++) void initialize()
+ {
+ mixin(msgtable.generate(&initializer));
+ }
+
+ /**
+ * Deinitializes the global state of the compiler.
+ *
+ * This can be used to restore the state set by `initialize` to its original
+ * state.
+ */
+ extern (D) void deinitialize()
+ {
+ mixin(msgtable.generate(&deinitializer));
+ }
+}
+
+private:
+
+
+/**
+ * Each element in this array will generate one static field in the `Id` struct
+ * and a call to `Identifier.idPool` to populate the identifier pool in the
+ * `Id.initialize` method.
+ */
+immutable Msgtable[] msgtable =
+[
+ { "IUnknown" },
+ { "Object" },
+ { "object" },
+ { "string" },
+ { "wstring" },
+ { "dstring" },
+ { "max" },
+ { "min" },
+ { "This", "this" },
+ { "_super", "super" },
+ { "ctor", "__ctor" },
+ { "dtor", "__dtor" },
+ { "__xdtor", "__xdtor" },
+ { "__fieldDtor", "__fieldDtor" },
+ { "__aggrDtor", "__aggrDtor" },
+ { "cppdtor", "__cppdtor" },
+ { "ticppdtor", "__ticppdtor" },
+ { "postblit", "__postblit" },
+ { "__xpostblit", "__xpostblit" },
+ { "__fieldPostblit", "__fieldPostblit" },
+ { "__aggrPostblit", "__aggrPostblit" },
+ { "classInvariant", "__invariant" },
+ { "unitTest", "__unitTest" },
+ { "require", "__require" },
+ { "ensure", "__ensure" },
+ { "capture", "__capture" },
+ { "this2", "__this" },
+ { "_init", "init" },
+ { "__sizeof", "sizeof" },
+ { "__xalignof", "alignof" },
+ { "_mangleof", "mangleof" },
+ { "stringof" },
+ { "_tupleof", "tupleof" },
+ { "length" },
+ { "remove" },
+ { "ptr" },
+ { "array" },
+ { "funcptr" },
+ { "dollar", "__dollar" },
+ { "ctfe", "__ctfe" },
+ { "offset" },
+ { "offsetof" },
+ { "ModuleInfo" },
+ { "ClassInfo" },
+ { "classinfo" },
+ { "typeinfo" },
+ { "outer" },
+ { "Exception" },
+ { "RTInfo" },
+ { "Throwable" },
+ { "Error" },
+ { "withSym", "__withSym" },
+ { "result", "__result" },
+ { "returnLabel", "__returnLabel" },
+ { "line" },
+ { "empty", "" },
+ { "p" },
+ { "q" },
+ { "__vptr" },
+ { "__monitor" },
+ { "gate", "__gate" },
+ { "__c_long" },
+ { "__c_ulong" },
+ { "__c_longlong" },
+ { "__c_ulonglong" },
+ { "__c_long_double" },
+ { "__c_wchar_t" },
+ { "__c_complex_float" },
+ { "__c_complex_double" },
+ { "__c_complex_real" },
+ { "cpp_type_info_ptr", "__cpp_type_info_ptr" },
+ { "_assert", "assert" },
+ { "_unittest", "unittest" },
+ { "_body", "body" },
+ { "printf" },
+ { "scanf" },
+
+ { "TypeInfo" },
+ { "TypeInfo_Class" },
+ { "TypeInfo_Interface" },
+ { "TypeInfo_Struct" },
+ { "TypeInfo_Enum" },
+ { "TypeInfo_Pointer" },
+ { "TypeInfo_Vector" },
+ { "TypeInfo_Array" },
+ { "TypeInfo_StaticArray" },
+ { "TypeInfo_AssociativeArray" },
+ { "TypeInfo_Function" },
+ { "TypeInfo_Delegate" },
+ { "TypeInfo_Tuple" },
+ { "TypeInfo_Const" },
+ { "TypeInfo_Invariant" },
+ { "TypeInfo_Shared" },
+ { "TypeInfo_Wild", "TypeInfo_Inout" },
+ { "elements" },
+ { "_arguments_typeinfo" },
+ { "_arguments" },
+ { "_argptr" },
+ { "destroy" },
+ { "xopEquals", "__xopEquals" },
+ { "xopCmp", "__xopCmp" },
+ { "xtoHash", "__xtoHash" },
+
+ { "LINE", "__LINE__" },
+ { "FILE", "__FILE__" },
+ { "MODULE", "__MODULE__" },
+ { "FUNCTION", "__FUNCTION__" },
+ { "PRETTY_FUNCTION", "__PRETTY_FUNCTION__" },
+ { "DATE", "__DATE__" },
+ { "TIME", "__TIME__" },
+ { "TIMESTAMP", "__TIMESTAMP__" },
+ { "VENDOR", "__VENDOR__" },
+ { "VERSIONX", "__VERSION__" },
+ { "EOFX", "__EOF__" },
+
+ { "nan" },
+ { "infinity" },
+ { "dig" },
+ { "epsilon" },
+ { "mant_dig" },
+ { "max_10_exp" },
+ { "max_exp" },
+ { "min_10_exp" },
+ { "min_exp" },
+ { "min_normal" },
+ { "re" },
+ { "im" },
+
+ { "C" },
+ { "D" },
+ { "Windows" },
+ { "System" },
+ { "Objective" },
+
+ { "exit" },
+ { "success" },
+ { "failure" },
+
+ { "keys" },
+ { "values" },
+ { "rehash" },
+
+ { "future", "__future" },
+ { "property" },
+ { "nogc" },
+ { "live" },
+ { "safe" },
+ { "trusted" },
+ { "system" },
+ { "disable" },
+
+ // For inline assembler
+ { "___out", "out" },
+ { "___in", "in" },
+ { "__int", "int" },
+ { "_dollar", "$" },
+ { "__LOCAL_SIZE" },
+
+ // For operator overloads
+ { "uadd", "opPos" },
+ { "neg", "opNeg" },
+ { "com", "opCom" },
+ { "add", "opAdd" },
+ { "add_r", "opAdd_r" },
+ { "sub", "opSub" },
+ { "sub_r", "opSub_r" },
+ { "mul", "opMul" },
+ { "mul_r", "opMul_r" },
+ { "div", "opDiv" },
+ { "div_r", "opDiv_r" },
+ { "mod", "opMod" },
+ { "mod_r", "opMod_r" },
+ { "eq", "opEquals" },
+ { "cmp", "opCmp" },
+ { "iand", "opAnd" },
+ { "iand_r", "opAnd_r" },
+ { "ior", "opOr" },
+ { "ior_r", "opOr_r" },
+ { "ixor", "opXor" },
+ { "ixor_r", "opXor_r" },
+ { "shl", "opShl" },
+ { "shl_r", "opShl_r" },
+ { "shr", "opShr" },
+ { "shr_r", "opShr_r" },
+ { "ushr", "opUShr" },
+ { "ushr_r", "opUShr_r" },
+ { "cat", "opCat" },
+ { "cat_r", "opCat_r" },
+ { "assign", "opAssign" },
+ { "addass", "opAddAssign" },
+ { "subass", "opSubAssign" },
+ { "mulass", "opMulAssign" },
+ { "divass", "opDivAssign" },
+ { "modass", "opModAssign" },
+ { "andass", "opAndAssign" },
+ { "orass", "opOrAssign" },
+ { "xorass", "opXorAssign" },
+ { "shlass", "opShlAssign" },
+ { "shrass", "opShrAssign" },
+ { "ushrass", "opUShrAssign" },
+ { "catass", "opCatAssign" },
+ { "postinc", "opPostInc" },
+ { "postdec", "opPostDec" },
+ { "index", "opIndex" },
+ { "indexass", "opIndexAssign" },
+ { "slice", "opSlice" },
+ { "sliceass", "opSliceAssign" },
+ { "call", "opCall" },
+ { "_cast", "opCast" },
+ { "opIn" },
+ { "opIn_r" },
+ { "opStar" },
+ { "opDot" },
+ { "opDispatch" },
+ { "opDollar" },
+ { "opUnary" },
+ { "opIndexUnary" },
+ { "opSliceUnary" },
+ { "opBinary" },
+ { "opBinaryRight" },
+ { "opOpAssign" },
+ { "opIndexOpAssign" },
+ { "opSliceOpAssign" },
+ { "pow", "opPow" },
+ { "pow_r", "opPow_r" },
+ { "powass", "opPowAssign" },
+
+ { "classNew", "new" },
+ { "classDelete", "delete" },
+
+ // For foreach
+ { "apply", "opApply" },
+ { "applyReverse", "opApplyReverse" },
+
+ // Ranges
+ { "Fempty", "empty" },
+ { "Ffront", "front" },
+ { "Fback", "back" },
+ { "FpopFront", "popFront" },
+ { "FpopBack", "popBack" },
+
+ // For internal functions
+ { "aaLen", "_aaLen" },
+ { "aaKeys", "_aaKeys" },
+ { "aaValues", "_aaValues" },
+ { "aaRehash", "_aaRehash" },
+ { "monitorenter", "_d_monitorenter" },
+ { "monitorexit", "_d_monitorexit" },
+ { "criticalenter", "_d_criticalenter2" },
+ { "criticalexit", "_d_criticalexit" },
+ { "__ArrayPostblit" },
+ { "__ArrayDtor" },
+ { "_d_delThrowable" },
+ { "_d_assert_fail" },
+ { "dup" },
+ { "_aaApply" },
+ { "_aaApply2" },
+
+ // For pragma's
+ { "Pinline", "inline" },
+ { "lib" },
+ { "linkerDirective" },
+ { "mangle" },
+ { "msg" },
+ { "startaddress" },
+ { "crt_constructor" },
+ { "crt_destructor" },
+
+ // For special functions
+ { "tohash", "toHash" },
+ { "tostring", "toString" },
+ { "getmembers", "getMembers" },
+
+ // Special functions
+ { "__alloca", "alloca" },
+ { "main" },
+ { "WinMain" },
+ { "DllMain" },
+ { "CMain", "_d_cmain" },
+ { "rt_init" },
+ { "__cmp" },
+ { "__equals"},
+ { "__switch"},
+ { "__switch_error"},
+ { "__ArrayCast"},
+ { "_d_HookTraceImpl" },
+ { "_d_arraysetlengthTImpl"},
+ { "_d_arraysetlengthT"},
+ { "_d_arraysetlengthTTrace"},
+
+ // varargs implementation
+ { "stdc" },
+ { "stdarg" },
+ { "va_start" },
+
+ // Builtin functions
+ { "std" },
+ { "core" },
+ { "etc" },
+ { "attribute" },
+ { "math" },
+ { "sin" },
+ { "cos" },
+ { "tan" },
+ { "_sqrt", "sqrt" },
+ { "_pow", "pow" },
+ { "atan2" },
+ { "rint" },
+ { "ldexp" },
+ { "rndtol" },
+ { "exp" },
+ { "expm1" },
+ { "exp2" },
+ { "yl2x" },
+ { "yl2xp1" },
+ { "log" },
+ { "log2" },
+ { "log10" },
+ { "round" },
+ { "floor" },
+ { "trunc" },
+ { "fmax" },
+ { "fmin" },
+ { "fma" },
+ { "isnan" },
+ { "isInfinity" },
+ { "isfinite" },
+ { "ceil" },
+ { "copysign" },
+ { "fabs" },
+ { "toPrec" },
+ { "simd" },
+ { "__prefetch"},
+ { "__simd_sto"},
+ { "__simd"},
+ { "__simd_ib"},
+ { "bitop" },
+ { "bsf" },
+ { "bsr" },
+ { "btc" },
+ { "btr" },
+ { "bts" },
+ { "bswap" },
+ { "volatile"},
+ { "volatileLoad"},
+ { "volatileStore"},
+ { "_popcnt"},
+ { "inp"},
+ { "inpl"},
+ { "inpw"},
+ { "outp"},
+ { "outpl"},
+ { "outpw"},
+
+ // Traits
+ { "isAbstractClass" },
+ { "isArithmetic" },
+ { "isAssociativeArray" },
+ { "isFinalClass" },
+ { "isTemplate" },
+ { "isPOD" },
+ { "isDeprecated" },
+ { "isDisabled" },
+ { "isFuture" },
+ { "isNested" },
+ { "isFloating" },
+ { "isIntegral" },
+ { "isScalar" },
+ { "isStaticArray" },
+ { "isUnsigned" },
+ { "isVirtualFunction" },
+ { "isVirtualMethod" },
+ { "isAbstractFunction" },
+ { "isFinalFunction" },
+ { "isOverrideFunction" },
+ { "isStaticFunction" },
+ { "isModule" },
+ { "isPackage" },
+ { "isRef" },
+ { "isOut" },
+ { "isLazy" },
+ { "hasMember" },
+ { "identifier" },
+ { "getProtection" },
+ { "getVisibility" },
+ { "parent" },
+ { "child" },
+ { "getMember" },
+ { "getOverloads" },
+ { "getVirtualFunctions" },
+ { "getVirtualMethods" },
+ { "classInstanceSize" },
+ { "allMembers" },
+ { "derivedMembers" },
+ { "isSame" },
+ { "compiles" },
+ { "getAliasThis" },
+ { "getAttributes" },
+ { "getFunctionAttributes" },
+ { "getFunctionVariadicStyle" },
+ { "getParameterStorageClasses" },
+ { "getLinkage" },
+ { "getUnitTests" },
+ { "getVirtualIndex" },
+ { "getPointerBitmap" },
+ { "getCppNamespaces" },
+ { "isReturnOnStack" },
+ { "isZeroInit" },
+ { "getTargetInfo" },
+ { "getLocation" },
+ { "hasPostblit" },
+ { "hasCopyConstructor" },
+ { "isCopyable" },
+ { "toType" },
+
+ // For C++ mangling
+ { "allocator" },
+ { "basic_string" },
+ { "basic_istream" },
+ { "basic_ostream" },
+ { "basic_iostream" },
+ { "char_traits" },
+
+ // Compiler recognized UDA's
+ { "udaGNUAbiTag", "gnuAbiTag" },
+ { "udaSelector", "selector" },
+ { "udaOptional", "optional"},
+
+ // C names, for undefined identifier error messages
+ { "NULL" },
+ { "TRUE" },
+ { "FALSE" },
+ { "unsigned" },
+ { "wchar_t" },
+
+ // for C compiler
+ { "__tag" },
+ { "dllimport" },
+ { "dllexport" },
+ { "vector_size" },
+ { "__func__" },
+ { "noreturn" },
+];
+
+
+/*
+ * Tuple of DMD source code identifier and symbol in the D executable.
+ *
+ * The first element of the tuple is the identifier to use in the DMD source
+ * code and the second element, if present, is the name to use in the D
+ * executable. If second element, `name`, is not present the identifier,
+ * `ident`, will be used instead
+ */
+struct Msgtable
+{
+ // The identifier to use in the DMD source.
+ string ident;
+
+ // The name to use in the D executable
+ private string name_;
+
+ /*
+ * Returns: the name to use in the D executable, `name_` if non-empty,
+ * otherwise `ident`
+ */
+ string name()
+ {
+ return name_ ? name_ : ident;
+ }
+}
+
+/*
+ * Iterates the given Msgtable array, passes each element to the given lambda
+ * and accumulates a string from each return value of calling the lambda.
+ * Appends a newline character after each call to the lambda.
+ */
+string generate(immutable(Msgtable)[] msgtable, string function(Msgtable) dg)
+{
+ string code;
+
+ foreach (i, m ; msgtable)
+ {
+ if (i != 0)
+ code ~= '\n';
+
+ code ~= dg(m);
+ }
+
+ return code;
+}
+
+// Used to generate the code for each identifier.
+string identifier(Msgtable m)
+{
+ return "Identifier " ~ m.ident ~ ";";
+}
+
+// Used to generate the code for each initializer.
+string initializer(Msgtable m)
+{
+ return m.ident ~ ` = Identifier.idPool("` ~ m.name ~ `");`;
+}
+
+// Used to generate the code for each deinitializer.
+string deinitializer(Msgtable m)
+{
+ return m.ident ~ " = Identifier.init;";
+}
diff --git a/gcc/d/dmd/id.h b/gcc/d/dmd/id.h
new file mode 100644
index 00000000000..8066747c69b
--- /dev/null
+++ b/gcc/d/dmd/id.h
@@ -0,0 +1,16 @@
+
+/* Compiler implementation of the D programming language
+ * Copyright (C) 2017-2021 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
+ * http://www.digitalmars.com
+ * Distributed under the Boost Software License, Version 1.0.
+ * http://www.boost.org/LICENSE_1_0.txt
+ * https://github.com/dlang/dmd/blob/master/src/dmd/id.h
+ */
+
+#pragma once
+
+struct Id
+{
+ static void initialize();
+};
diff --git a/gcc/d/dmd/identifier.c b/gcc/d/dmd/identifier.c
deleted file mode 100644
index 197d288e532..00000000000
--- a/gcc/d/dmd/identifier.c
+++ /dev/null
@@ -1,188 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/identifier.c
- */
-
-#include "root/dsystem.h"
-#include "root/root.h"
-
-#include "identifier.h"
-#include "mars.h"
-#include "id.h"
-#include "tokens.h"
-#include "utf.h"
-
-Identifier::Identifier(const char *string, size_t length, int value)
-{
- //printf("Identifier('%s', %d)\n", string, value);
- this->string = string;
- this->value = value;
- this->len = length;
-}
-
-Identifier::Identifier(const char *string)
-{
- //printf("Identifier('%s')\n", string);
- this->string = string;
- this->value = TOKidentifier;
- this->len = strlen(string);
-}
-
-Identifier *Identifier::create(const char *string)
-{
- return new Identifier(string);
-}
-
-bool Identifier::equals(RootObject *o)
-{
- return this == o || strncmp(string,o->toChars(),len+1) == 0;
-}
-
-int Identifier::compare(RootObject *o)
-{
- return strncmp(string, o->toChars(), len + 1);
-}
-
-const char *Identifier::toChars()
-{
- return string;
-}
-
-int Identifier::getValue() const
-{
- return value;
-}
-
-const char *Identifier::toHChars2()
-{
- const char *p = NULL;
-
- if (this == Id::ctor) p = "this";
- else if (this == Id::dtor) p = "~this";
- else if (this == Id::unitTest) p = "unittest";
- else if (this == Id::dollar) p = "$";
- else if (this == Id::withSym) p = "with";
- else if (this == Id::result) p = "result";
- else if (this == Id::returnLabel) p = "return";
- else
- { p = toChars();
- if (*p == '_')
- {
- if (strncmp(p, "_staticCtor", 11) == 0)
- p = "static this";
- else if (strncmp(p, "_staticDtor", 11) == 0)
- p = "static ~this";
- else if (strncmp(p, "__invariant", 11) == 0)
- p = "invariant";
- }
- }
-
- return p;
-}
-
-void Identifier::print()
-{
- fprintf(stderr, "%s",string);
-}
-
-int Identifier::dyncast() const
-{
- return DYNCAST_IDENTIFIER;
-}
-
-StringTable Identifier::stringtable;
-
-Identifier *Identifier::generateId(const char *prefix)
-{
- static size_t i;
-
- return generateId(prefix, ++i);
-}
-
-Identifier *Identifier::generateId(const char *prefix, size_t i)
-{ OutBuffer buf;
-
- buf.writestring(prefix);
- buf.printf("%llu", (ulonglong)i);
-
- char *id = buf.peekChars();
- return idPool(id);
-}
-
-/********************************************
- * Create an identifier in the string table.
- */
-
-Identifier *Identifier::idPool(const char *s, size_t len)
-{
- StringValue *sv = stringtable.update(s, len);
- Identifier *id = (Identifier *) sv->ptrvalue;
- if (!id)
- {
- id = new Identifier(sv->toDchars(), len, TOKidentifier);
- sv->ptrvalue = (char *)id;
- }
- return id;
-}
-
-Identifier *Identifier::idPool(const char *s, size_t len, int value)
-{
- StringValue *sv = stringtable.insert(s, len, NULL);
- assert(sv);
- Identifier *id = new Identifier(sv->toDchars(), len, value);
- sv->ptrvalue = (char *)id;
- return id;
-}
-
-/**********************************
- * Determine if string is a valid Identifier.
- * Returns:
- * 0 invalid
- */
-
-bool Identifier::isValidIdentifier(const char *p)
-{
- size_t len;
- size_t idx;
-
- if (!p || !*p)
- goto Linvalid;
-
- if (*p >= '0' && *p <= '9') // beware of isdigit() on signed chars
- goto Linvalid;
-
- len = strlen(p);
- idx = 0;
- while (p[idx])
- {
- dchar_t dc;
- const char *q = utf_decodeChar((const utf8_t *)p, len, &idx, &dc);
- if (q)
- goto Linvalid;
-
- if (!((dc >= 0x80 && isUniAlpha(dc)) || isalnum(dc) || dc == '_'))
- goto Linvalid;
- }
- return true;
-
-Linvalid:
- return false;
-}
-
-Identifier *Identifier::lookup(const char *s, size_t len)
-{
- StringValue *sv = stringtable.lookup(s, len);
- if (!sv)
- return NULL;
- return (Identifier *)sv->ptrvalue;
-}
-
-void Identifier::initTable()
-{
- stringtable._init(28000);
-}
diff --git a/gcc/d/dmd/identifier.d b/gcc/d/dmd/identifier.d
new file mode 100644
index 00000000000..43a1435cd10
--- /dev/null
+++ b/gcc/d/dmd/identifier.d
@@ -0,0 +1,362 @@
+/**
+ * Defines an identifier, which is the name of a `Dsymbol`.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/identifier.d, _identifier.d)
+ * Documentation: https://dlang.org/phobos/dmd_identifier.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/identifier.d
+ */
+
+module dmd.identifier;
+
+import core.stdc.ctype;
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.globals;
+import dmd.id;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.root.stringtable;
+import dmd.tokens;
+import dmd.utf;
+
+
+/***********************************************************
+ */
+extern (C++) final class Identifier : RootObject
+{
+ private const int value;
+
+ // Indicates if this is an identifier used for an anonymous symbol.
+ private const bool isAnonymous_ = false;
+
+ private const char[] name;
+
+nothrow:
+
+ /// Construct an identifier from the given name.
+ extern (D) this(const(char)* name)
+ {
+ //printf("Identifier('%s', %d)\n", name, value);
+ this(name.toDString(), TOK.identifier);
+ }
+
+ /**
+ Construct an identifier from the given name.
+
+ Params:
+ name = the identifier name. There must be `'\0'` at `name[length]`.
+ length = the length of `name`, excluding the terminating `'\0'`
+ value = Identifier value (e.g. `Id.unitTest`) or `TOK.identifier`
+ */
+ extern (D) this(const(char)* name, size_t length, int value)
+ in
+ {
+ assert(name[length] == '\0');
+ }
+ do
+ {
+ //printf("Identifier('%s', %d)\n", name, value);
+ this(name[0 .. length], value);
+ }
+
+ /// ditto
+ extern (D) this(const(char)[] name, int value)
+ {
+ //printf("Identifier('%.*s', %d)\n", cast(int)name.length, name.ptr, value);
+ this(name, value, false);
+ }
+
+ extern (D) private this(const(char)[] name, int value, bool isAnonymous)
+ {
+ //printf("Identifier('%.*s', %d, %d)\n", cast(int)name.length, name.ptr, value, isAnonymous);
+ this.name = name;
+ this.value = value;
+ isAnonymous_ = isAnonymous;
+ }
+
+ static Identifier create(const(char)* name)
+ {
+ return new Identifier(name);
+ }
+
+ override const(char)* toChars() const pure
+ {
+ return name.ptr;
+ }
+
+ extern (D) override const(char)[] toString() const pure
+ {
+ return name;
+ }
+
+ int getValue() const pure
+ {
+ return value;
+ }
+
+ bool isAnonymous() const pure @nogc @safe
+ {
+ return isAnonymous_;
+ }
+
+ const(char)* toHChars2() const
+ {
+ const(char)* p = null;
+ if (this == Id.ctor)
+ p = "this";
+ else if (this == Id.dtor)
+ p = "~this";
+ else if (this == Id.unitTest)
+ p = "unittest";
+ else if (this == Id.dollar)
+ p = "$";
+ else if (this == Id.withSym)
+ p = "with";
+ else if (this == Id.result)
+ p = "result";
+ else if (this == Id.returnLabel)
+ p = "return";
+ else
+ {
+ p = toChars();
+ if (*p == '_')
+ {
+ if (strncmp(p, "_staticCtor", 11) == 0)
+ p = "static this";
+ else if (strncmp(p, "_staticDtor", 11) == 0)
+ p = "static ~this";
+ else if (strncmp(p, "__invariant", 11) == 0)
+ p = "invariant";
+ }
+ }
+ return p;
+ }
+
+ override DYNCAST dyncast() const
+ {
+ return DYNCAST.identifier;
+ }
+
+ private extern (D) __gshared StringTable!Identifier stringtable;
+
+ /**
+ * Generates a new identifier.
+ *
+ * Params:
+ * prefix = this will be the prefix of the name of the identifier. For debugging
+ * purpose.
+ */
+ extern(D) static Identifier generateId(const(char)[] prefix)
+ {
+ return generateId(prefix, newSuffix, false);
+ }
+
+ /**
+ * Generates a new anonymous identifier.
+ *
+ * Params:
+ * name = this will be part of the name of the identifier. For debugging
+ * purpose.
+ */
+ extern(D) static Identifier generateAnonymousId(const(char)[] name)
+ {
+ return generateId("__anon" ~ name, newSuffix, true);
+ }
+
+ /**
+ * Generates a new identifier.
+ *
+ * Params:
+ * prefix = this will be the prefix of the name of the identifier. For debugging
+ * purpose.
+ * suffix = this will be the suffix of the name of the identifier. This is
+ * what makes the identifier unique
+ */
+ extern(D) static Identifier generateId(const(char)[] prefix, size_t suffix)
+ {
+ return generateId(prefix, suffix, false);
+ }
+
+ /// ditto
+ static Identifier generateId(const(char)* prefix, size_t length, size_t suffix)
+ {
+ return generateId(prefix[0 .. length], suffix);
+ }
+
+ // Generates a new, unique, suffix for an identifier.
+ extern (D) private static size_t newSuffix()
+ {
+ __gshared size_t i;
+ return ++i;
+ }
+
+ extern(D) private static Identifier generateId(const(char)[] prefix, size_t suffix, bool isAnonymous)
+ {
+ OutBuffer buf;
+ buf.write(prefix);
+ buf.print(suffix);
+ return idPool(buf[], isAnonymous);
+ }
+
+ /***************************************
+ * Generate deterministic named identifier based on a source location,
+ * such that the name is consistent across multiple compilations.
+ * A new unique name is generated. If the prefix+location is already in
+ * the stringtable, an extra suffix is added (starting the count at "_1").
+ *
+ * Params:
+ * prefix = first part of the identifier name.
+ * loc = source location to use in the identifier name.
+ * Returns:
+ * Identifier (inside Identifier.idPool) with deterministic name based
+ * on the source location.
+ */
+ extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc)
+ {
+ // generate `<prefix>_L<line>_C<col>`
+ OutBuffer idBuf;
+ idBuf.writestring(prefix);
+ idBuf.writestring("_L");
+ idBuf.print(loc.linnum);
+ idBuf.writestring("_C");
+ idBuf.print(loc.charnum);
+
+ /**
+ * Make sure the identifiers are unique per filename, i.e., per module/mixin
+ * (`path/to/foo.d` and `path/to/foo.d-mixin-<line>`). See issues
+ * https://issues.dlang.org/show_bug.cgi?id=16995
+ * https://issues.dlang.org/show_bug.cgi?id=18097
+ * https://issues.dlang.org/show_bug.cgi?id=18111
+ * https://issues.dlang.org/show_bug.cgi?id=18880
+ * https://issues.dlang.org/show_bug.cgi?id=18868
+ * https://issues.dlang.org/show_bug.cgi?id=19058
+ */
+ static struct Key { Loc loc; string prefix; }
+ __gshared uint[Key] counters;
+
+ static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u)))
+ {
+ // 2.082+
+ counters.update(Key(loc, prefix),
+ () => 1u, // insertion
+ (ref uint counter) // update
+ {
+ idBuf.writestring("_");
+ idBuf.print(counter);
+ return counter + 1;
+ }
+ );
+ }
+ else
+ {
+ const key = Key(loc, prefix);
+ if (auto pCounter = key in counters)
+ {
+ idBuf.writestring("_");
+ idBuf.print((*pCounter)++);
+ }
+ else
+ counters[key] = 1;
+ }
+
+ return idPool(idBuf[]);
+ }
+
+ /********************************************
+ * Create an identifier in the string table.
+ */
+ static Identifier idPool(const(char)* s, uint len)
+ {
+ return idPool(s[0 .. len]);
+ }
+
+ extern (D) static Identifier idPool(const(char)[] s)
+ {
+ return idPool(s, false);
+ }
+
+ extern (D) private static Identifier idPool(const(char)[] s, bool isAnonymous)
+ {
+ auto sv = stringtable.update(s);
+ auto id = sv.value;
+ if (!id)
+ {
+ id = new Identifier(sv.toString(), TOK.identifier, isAnonymous);
+ sv.value = id;
+ }
+ return id;
+ }
+
+ extern (D) static Identifier idPool(const(char)* s, size_t len, int value)
+ {
+ return idPool(s[0 .. len], value);
+ }
+
+ extern (D) static Identifier idPool(const(char)[] s, int value)
+ {
+ auto sv = stringtable.insert(s, null);
+ assert(sv);
+ auto id = new Identifier(sv.toString(), value);
+ sv.value = id;
+ return id;
+ }
+
+ /**********************************
+ * Determine if string is a valid Identifier.
+ * Params:
+ * str = string to check
+ * Returns:
+ * false for invalid
+ */
+ static bool isValidIdentifier(const(char)* str)
+ {
+ return str && isValidIdentifier(str.toDString);
+ }
+
+ /**********************************
+ * ditto
+ */
+ extern (D) static bool isValidIdentifier(const(char)[] str)
+ {
+ if (str.length == 0 ||
+ (str[0] >= '0' && str[0] <= '9')) // beware of isdigit() on signed chars
+ {
+ return false;
+ }
+
+ size_t idx = 0;
+ while (idx < str.length)
+ {
+ dchar dc;
+ const s = utf_decodeChar(str, idx, dc);
+ if (s ||
+ !((dc >= 0x80 && isUniAlpha(dc)) || isalnum(dc) || dc == '_'))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ extern (D) static Identifier lookup(const(char)* s, size_t len)
+ {
+ return lookup(s[0 .. len]);
+ }
+
+ extern (D) static Identifier lookup(const(char)[] s)
+ {
+ auto sv = stringtable.lookup(s);
+ if (!sv)
+ return null;
+ return sv.value;
+ }
+
+ extern (D) static void initTable()
+ {
+ stringtable._init(28_000);
+ }
+}
diff --git a/gcc/d/dmd/identifier.h b/gcc/d/dmd/identifier.h
index 278ce9b8a76..790d5a036d4 100644
--- a/gcc/d/dmd/identifier.h
+++ b/gcc/d/dmd/identifier.h
@@ -10,40 +10,32 @@
#pragma once
-#include "root/root.h"
-#include "root/stringtable.h"
+#include "root/dcompat.h"
+#include "root/object.h"
class Identifier : public RootObject
{
private:
int value;
- const char *string;
- size_t len;
+ bool isAnonymous_;
+ DString string;
public:
- Identifier(const char *string, size_t length, int value);
- Identifier(const char *string);
static Identifier* create(const char *string);
- bool equals(RootObject *o);
- int compare(RootObject *o);
- void print();
- const char *toChars();
+ bool equals(const RootObject *o) const;
+ const char *toChars() const;
int getValue() const;
- const char *toHChars2();
- int dyncast() const;
+ bool isAnonymous() const;
+ const char *toHChars2() const;
+ DYNCAST dyncast() const;
- static StringTable stringtable;
- static Identifier *generateId(const char *prefix);
- static Identifier *generateId(const char *prefix, size_t i);
- static Identifier *idPool(const char *s, size_t len);
- static Identifier *idPool(const char *s, size_t len, int value);
+ static Identifier *generateId(const char *prefix, size_t length, size_t suffix);
+ static Identifier *idPool(const char *s, unsigned len);
static inline Identifier *idPool(const char *s)
{
- return idPool(s, strlen(s));
+ return idPool(s, static_cast<unsigned>(strlen(s)));
}
static bool isValidIdentifier(const char *p);
- static Identifier *lookup(const char *s, size_t len);
- static void initTable();
};
diff --git a/gcc/d/dmd/idgen.c b/gcc/d/dmd/idgen.c
deleted file mode 100644
index 0740653709a..00000000000
--- a/gcc/d/dmd/idgen.c
+++ /dev/null
@@ -1,560 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/idgen.c
- */
-
-// Program to generate string files in d data structures.
-// Saves much tedious typing, and eliminates typo problems.
-// Generates:
-// id.h
-// id.c
-
-#include "root/dsystem.h"
-
-struct Msgtable
-{
- const char* ident; // name to use in DMD source
- const char* name; // name in D executable
-};
-
-Msgtable msgtable[] =
-{
- { "IUnknown", NULL },
- { "Object", NULL },
- { "object", NULL },
- { "string", NULL },
- { "wstring", NULL },
- { "dstring", NULL },
- { "max", NULL },
- { "min", NULL },
- { "This", "this" },
- { "_super", "super" },
- { "ctor", "__ctor" },
- { "dtor", "__dtor" },
- { "__xdtor", "__xdtor" },
- { "__fieldDtor", "__fieldDtor" },
- { "__aggrDtor", "__aggrDtor" },
- { "postblit", "__postblit" },
- { "__xpostblit", "__xpostblit" },
- { "__fieldPostblit", "__fieldPostblit" },
- { "__aggrPostblit", "__aggrPostblit" },
- { "classInvariant", "__invariant" },
- { "unitTest", "__unitTest" },
- { "require", "__require" },
- { "ensure", "__ensure" },
- { "_init", "init" },
- { "__sizeof", "sizeof" },
- { "__xalignof", "alignof" },
- { "_mangleof", "mangleof" },
- { "stringof", NULL },
- { "_tupleof", "tupleof" },
- { "length", NULL },
- { "remove", NULL },
- { "ptr", NULL },
- { "array", NULL },
- { "funcptr", NULL },
- { "dollar", "__dollar" },
- { "ctfe", "__ctfe" },
- { "offset", NULL },
- { "offsetof", NULL },
- { "ModuleInfo", NULL },
- { "ClassInfo", NULL },
- { "classinfo", NULL },
- { "typeinfo", NULL },
- { "outer", NULL },
- { "Exception", NULL },
- { "RTInfo", NULL },
- { "Throwable", NULL },
- { "Error", NULL },
- { "withSym", "__withSym" },
- { "result", "__result" },
- { "returnLabel", "__returnLabel" },
- { "line", NULL },
- { "empty", "" },
- { "p", NULL },
- { "q", NULL },
- { "__vptr", NULL },
- { "__monitor", NULL },
- { "gate", "__gate" },
- { "__c_long", NULL },
- { "__c_ulong", NULL },
- { "__c_longlong", NULL },
- { "__c_ulonglong", NULL },
- { "__c_long_double", NULL },
- { "__c_wchar_t", NULL },
- { "__c_complex_float", NULL },
- { "__c_complex_double", NULL },
- { "__c_complex_real", NULL },
- { "cpp_type_info_ptr", "__cpp_type_info_ptr" },
- { "_assert", "assert" },
- { "_unittest", "unittest" },
- { "_body", "body" },
- { "printf", NULL },
- { "scanf", NULL },
-
- { "TypeInfo", NULL },
- { "TypeInfo_Class", NULL },
- { "TypeInfo_Interface", NULL },
- { "TypeInfo_Struct", NULL },
- { "TypeInfo_Enum", NULL },
- { "TypeInfo_Pointer", NULL },
- { "TypeInfo_Vector", NULL },
- { "TypeInfo_Array", NULL },
- { "TypeInfo_StaticArray", NULL },
- { "TypeInfo_AssociativeArray", NULL },
- { "TypeInfo_Function", NULL },
- { "TypeInfo_Delegate", NULL },
- { "TypeInfo_Tuple", NULL },
- { "TypeInfo_Const", NULL },
- { "TypeInfo_Invariant", NULL },
- { "TypeInfo_Shared", NULL },
- { "TypeInfo_Wild", "TypeInfo_Inout" },
- { "elements", NULL },
- { "_arguments_typeinfo", NULL },
- { "_arguments", NULL },
- { "_argptr", NULL },
- { "destroy", NULL },
- { "xopEquals", "__xopEquals" },
- { "xopCmp", "__xopCmp" },
- { "xtoHash", "__xtoHash" },
-
- { "LINE", "__LINE__" },
- { "FILE", "__FILE__" },
- { "MODULE", "__MODULE__" },
- { "FUNCTION", "__FUNCTION__" },
- { "PRETTY_FUNCTION", "__PRETTY_FUNCTION__" },
- { "DATE", "__DATE__" },
- { "TIME", "__TIME__" },
- { "TIMESTAMP", "__TIMESTAMP__" },
- { "VENDOR", "__VENDOR__" },
- { "VERSIONX", "__VERSION__" },
- { "EOFX", "__EOF__" },
-
- { "nan", NULL },
- { "infinity", NULL },
- { "dig", NULL },
- { "epsilon", NULL },
- { "mant_dig", NULL },
- { "max_10_exp", NULL },
- { "max_exp", NULL },
- { "min_10_exp", NULL },
- { "min_exp", NULL },
- { "min_normal", NULL },
- { "re", NULL },
- { "im", NULL },
-
- { "C", NULL },
- { "D", NULL },
- { "Windows", NULL },
- { "System", NULL },
- { "Objective", NULL },
-
- { "exit", NULL },
- { "success", NULL },
- { "failure", NULL },
-
- { "keys", NULL },
- { "values", NULL },
- { "rehash", NULL },
-
- { "future", "__future" },
- { "property", NULL },
- { "nogc", NULL },
- { "safe", NULL },
- { "trusted", NULL },
- { "system", NULL },
- { "disable", NULL },
-
- // For inline assembler
- { "___out", "out" },
- { "___in", "in" },
- { "__int", "int" },
- { "_dollar", "$" },
- { "__LOCAL_SIZE", NULL },
-
- // For operator overloads
- { "uadd", "opPos" },
- { "neg", "opNeg" },
- { "com", "opCom" },
- { "add", "opAdd" },
- { "add_r", "opAdd_r" },
- { "sub", "opSub" },
- { "sub_r", "opSub_r" },
- { "mul", "opMul" },
- { "mul_r", "opMul_r" },
- { "div", "opDiv" },
- { "div_r", "opDiv_r" },
- { "mod", "opMod" },
- { "mod_r", "opMod_r" },
- { "eq", "opEquals" },
- { "cmp", "opCmp" },
- { "iand", "opAnd" },
- { "iand_r", "opAnd_r" },
- { "ior", "opOr" },
- { "ior_r", "opOr_r" },
- { "ixor", "opXor" },
- { "ixor_r", "opXor_r" },
- { "shl", "opShl" },
- { "shl_r", "opShl_r" },
- { "shr", "opShr" },
- { "shr_r", "opShr_r" },
- { "ushr", "opUShr" },
- { "ushr_r", "opUShr_r" },
- { "cat", "opCat" },
- { "cat_r", "opCat_r" },
- { "assign", "opAssign" },
- { "addass", "opAddAssign" },
- { "subass", "opSubAssign" },
- { "mulass", "opMulAssign" },
- { "divass", "opDivAssign" },
- { "modass", "opModAssign" },
- { "andass", "opAndAssign" },
- { "orass", "opOrAssign" },
- { "xorass", "opXorAssign" },
- { "shlass", "opShlAssign" },
- { "shrass", "opShrAssign" },
- { "ushrass", "opUShrAssign" },
- { "catass", "opCatAssign" },
- { "postinc", "opPostInc" },
- { "postdec", "opPostDec" },
- { "index", "opIndex" },
- { "indexass", "opIndexAssign" },
- { "slice", "opSlice" },
- { "sliceass", "opSliceAssign" },
- { "call", "opCall" },
- { "_cast", "opCast" },
- { "opIn", NULL },
- { "opIn_r", NULL },
- { "opStar", NULL },
- { "opDot", NULL },
- { "opDispatch", NULL },
- { "opDollar", NULL },
- { "opUnary", NULL },
- { "opIndexUnary", NULL },
- { "opSliceUnary", NULL },
- { "opBinary", NULL },
- { "opBinaryRight", NULL },
- { "opOpAssign", NULL },
- { "opIndexOpAssign", NULL },
- { "opSliceOpAssign", NULL },
- { "pow", "opPow" },
- { "pow_r", "opPow_r" },
- { "powass", "opPowAssign" },
-
- { "classNew", "new" },
- { "classDelete", "delete" },
-
- // For foreach
- { "apply", "opApply" },
- { "applyReverse", "opApplyReverse" },
-
- // Ranges
- { "Fempty", "empty" },
- { "Ffront", "front" },
- { "Fback", "back" },
- { "FpopFront", "popFront" },
- { "FpopBack", "popBack" },
-
- // For internal functions
- { "aaLen", "_aaLen" },
- { "aaKeys", "_aaKeys" },
- { "aaValues", "_aaValues" },
- { "aaRehash", "_aaRehash" },
- { "monitorenter", "_d_monitorenter" },
- { "monitorexit", "_d_monitorexit" },
- { "criticalenter", "_d_criticalenter2" },
- { "criticalexit", "_d_criticalexit" },
- { "__ArrayEq", NULL },
- { "__ArrayPostblit", NULL },
- { "__ArrayDtor", NULL },
- { "dup", NULL },
- { "_aaApply", NULL },
- { "_aaApply2", NULL },
-
- // For pragma's
- { "Pinline", "inline" },
- { "lib", NULL },
- { "mangle", NULL },
- { "msg", NULL },
- { "startaddress", NULL },
-
- // For special functions
- { "tohash", "toHash" },
- { "tostring", "toString" },
- { "getmembers", "getMembers" },
-
- // Special functions
- { "__alloca", "alloca" },
- { "main", NULL },
- { "WinMain", NULL },
- { "DllMain", NULL },
- { "tls_get_addr", "___tls_get_addr" },
- { "entrypoint", "__entrypoint" },
-
- // varargs implementation
- { "stdc", NULL },
- { "stdarg", NULL },
- { "va_start", NULL },
-
- // Builtin functions
- { "std", NULL },
- { "core", NULL },
- { "attribute", NULL },
- { "math", NULL },
- { "sin", NULL },
- { "cos", NULL },
- { "tan", NULL },
- { "_sqrt", "sqrt" },
- { "_pow", "pow" },
- { "atan2", NULL },
- { "rint", NULL },
- { "ldexp", NULL },
- { "rndtol", NULL },
- { "exp", NULL },
- { "expm1", NULL },
- { "exp2", NULL },
- { "yl2x", NULL },
- { "yl2xp1", NULL },
- { "log", NULL },
- { "log2", NULL },
- { "log10", NULL },
- { "round", NULL },
- { "floor", NULL },
- { "trunc", NULL },
- { "fmax", NULL },
- { "fmin", NULL },
- { "fma", NULL },
- { "isnan", NULL },
- { "isInfinity", NULL },
- { "isfinite", NULL },
- { "ceil", NULL },
- { "copysign", NULL },
- { "fabs", NULL },
- { "toPrec", NULL },
- { "simd", NULL },
- { "__prefetch", NULL },
- { "__simd_sto", NULL },
- { "__simd", NULL },
- { "__simd_ib", NULL },
- { "bitop", NULL },
- { "bsf", NULL },
- { "bsr", NULL },
- { "btc", NULL },
- { "btr", NULL },
- { "bts", NULL },
- { "bswap", NULL },
- { "_volatile", "volatile" },
- { "volatileLoad", NULL },
- { "volatileStore", NULL },
- { "_popcnt", NULL },
- { "inp", NULL },
- { "inpl", NULL },
- { "inpw", NULL },
- { "outp", NULL },
- { "outpl", NULL },
- { "outpw", NULL },
-
- // Traits
- { "isAbstractClass", NULL },
- { "isArithmetic", NULL },
- { "isAssociativeArray", NULL },
- { "isFinalClass", NULL },
- { "isTemplate", NULL },
- { "isPOD", NULL },
- { "isDeprecated", NULL },
- { "isDisabled", NULL },
- { "isFuture" , NULL },
- { "isNested", NULL },
- { "isFloating", NULL },
- { "isIntegral", NULL },
- { "isScalar", NULL },
- { "isStaticArray", NULL },
- { "isUnsigned", NULL },
- { "isVirtualFunction", NULL },
- { "isVirtualMethod", NULL },
- { "isAbstractFunction", NULL },
- { "isFinalFunction", NULL },
- { "isOverrideFunction", NULL },
- { "isStaticFunction", NULL },
- { "isModule", NULL },
- { "isPackage", NULL },
- { "isRef", NULL },
- { "isOut", NULL },
- { "isLazy", NULL },
- { "hasMember", NULL },
- { "identifier", NULL },
- { "getProtection", NULL },
- { "getVisibility", NULL },
- { "parent", NULL },
- { "child", NULL },
- { "getMember", NULL },
- { "getOverloads", NULL },
- { "getVirtualFunctions", NULL },
- { "getVirtualMethods", NULL },
- { "classInstanceSize", NULL },
- { "allMembers", NULL },
- { "derivedMembers", NULL },
- { "isSame", NULL },
- { "compiles", NULL },
- { "getAliasThis", NULL },
- { "getAttributes", NULL },
- { "getFunctionAttributes", NULL },
- { "getFunctionVariadicStyle", NULL },
- { "getParameterStorageClasses", NULL },
- { "getLinkage", NULL },
- { "getUnitTests", NULL },
- { "getVirtualIndex", NULL },
- { "getPointerBitmap", NULL },
- { "isReturnOnStack", NULL },
- { "isZeroInit", NULL },
- { "getTargetInfo", NULL },
- { "getLocation", NULL },
- { "hasPostblit", NULL },
- { "isCopyable", NULL },
- { "toType", NULL },
-
- // For C++ mangling
- { "allocator", NULL },
- { "basic_string", NULL },
- { "basic_istream", NULL },
- { "basic_ostream", NULL },
- { "basic_iostream", NULL },
- { "char_traits", NULL },
-
- // Compiler recognized UDA's
- { "udaSelector", "selector" },
-
- // C names, for undefined identifier error messages
- { "C_NULL", "NULL" },
- { "C_TRUE", "TRUE" },
- { "C_FALSE", "FALSE" },
- { "C_unsigned", "unsigned" },
- { "C_wchar_t", "wchar_t" },
-};
-
-
-int main()
-{
- {
- FILE *fp = fopen("id.h","wb");
- if (!fp)
- {
- printf("can't open id.h\n");
- exit(EXIT_FAILURE);
- }
-
- fprintf(fp, "// File generated by idgen.c\n");
- fprintf(fp, "#ifndef DMD_ID_H\n");
- fprintf(fp, "#define DMD_ID_H 1\n");
- fprintf(fp, "class Identifier;\n");
- fprintf(fp, "struct Id\n");
- fprintf(fp, "{\n");
-
- for (unsigned i = 0; i < sizeof(msgtable) / sizeof(msgtable[0]); i++)
- {
- const char *id = msgtable[i].ident;
- fprintf(fp," static Identifier *%s;\n", id);
- }
-
- fprintf(fp, " static void initialize();\n");
- fprintf(fp, "};\n");
- fprintf(fp, "#endif\n");
-
- fclose(fp);
- }
-
- {
- FILE *fp = fopen("id.c","wb");
- if (!fp)
- {
- printf("can't open id.c\n");
- exit(EXIT_FAILURE);
- }
-
- fprintf(fp, "// File generated by idgen.c\n");
- fprintf(fp, "#include \"identifier.h\"\n");
- fprintf(fp, "#include \"id.h\"\n");
- fprintf(fp, "#include \"mars.h\"\n");
-
- for (unsigned i = 0; i < sizeof(msgtable) / sizeof(msgtable[0]); i++)
- {
- const char *id = msgtable[i].ident;
- const char *p = msgtable[i].name;
-
- if (!p)
- p = id;
- fprintf(fp,"Identifier *Id::%s;\n", id);
- }
-
- fprintf(fp, "void Id::initialize()\n");
- fprintf(fp, "{\n");
-
- for (unsigned i = 0; i < sizeof(msgtable) / sizeof(msgtable[0]); i++)
- {
- const char *id = msgtable[i].ident;
- const char *p = msgtable[i].name;
-
- if (!p)
- p = id;
- fprintf(fp," %s = Identifier::idPool(\"%s\");\n", id, p);
- }
-
- fprintf(fp, "}\n");
-
- fclose(fp);
- }
-
- {
- FILE *fp = fopen("id.d","wb");
- if (!fp)
- {
- printf("can't open id.d\n");
- exit(EXIT_FAILURE);
- }
-
- fprintf(fp, "// File generated by idgen.c\n");
- fprintf(fp, "\n");
- fprintf(fp, "module ddmd.id;\n");
- fprintf(fp, "\n");
- fprintf(fp, "import ddmd.identifier, ddmd.tokens;\n");
- fprintf(fp, "\n");
- fprintf(fp, "struct Id\n");
- fprintf(fp, "{\n");
-
- for (unsigned i = 0; i < sizeof(msgtable) / sizeof(msgtable[0]); i++)
- {
- const char *id = msgtable[i].ident;
- const char *p = msgtable[i].name;
-
- if (!p)
- p = id;
- fprintf(fp, " extern (C++) static __gshared Identifier %s;\n", id);
- }
-
- fprintf(fp, "\n");
- fprintf(fp, " extern (C++) static void initialize()\n");
- fprintf(fp, " {\n");
-
- for (unsigned i = 0; i < sizeof(msgtable) / sizeof(msgtable[0]); i++)
- {
- const char *id = msgtable[i].ident;
- const char *p = msgtable[i].name;
-
- if (!p)
- p = id;
- fprintf(fp," %s = Identifier.idPool(\"%s\");\n", id, p);
- }
-
- fprintf(fp, " }\n");
- fprintf(fp, "}\n");
-
- fclose(fp);
- }
-
- return EXIT_SUCCESS;
-}
diff --git a/gcc/d/dmd/impcnvgen.c b/gcc/d/dmd/impcnvgen.c
deleted file mode 100644
index d7c27eaa771..00000000000
--- a/gcc/d/dmd/impcnvgen.c
+++ /dev/null
@@ -1,598 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/impcnvgen.c
- */
-
-#include "root/dsystem.h"
-
-#include "mtype.h"
-
-TY impcnvResultTab[TMAX][TMAX];
-TY impcnvType1Tab[TMAX][TMAX];
-TY impcnvType2Tab[TMAX][TMAX];
-int impcnvWarnTab[TMAX][TMAX];
-
-int integral_promotion(int t)
-{
- switch (t)
- {
- case Tchar:
- case Twchar:
- case Tbool:
- case Tint8:
- case Tuns8:
- case Tint16:
- case Tuns16: return Tint32;
- case Tdchar: return Tuns32;
- default: return t;
- }
-}
-
-void init()
-{ int i, j;
-
- // Set conversion tables
- for (i = 0; i < TMAX; i++)
- for (j = 0; j < TMAX; j++)
- { impcnvResultTab[i][j] = Terror;
- impcnvType1Tab[i][j] = Terror;
- impcnvType2Tab[i][j] = Terror;
- impcnvWarnTab[i][j] = 0;
- }
-
-#define X(t1,t2, nt1,nt2, rt) \
- impcnvResultTab[t1][t2] = rt; \
- impcnvType1Tab[t1][t2] = nt1; \
- impcnvType2Tab[t1][t2] = nt2;
-
-
- /* ======================= */
-
- X(Tbool,Tbool, Tbool,Tbool, Tbool)
- X(Tbool,Tint8, Tint32,Tint32, Tint32)
- X(Tbool,Tuns8, Tint32,Tint32, Tint32)
- X(Tbool,Tint16, Tint32,Tint32, Tint32)
- X(Tbool,Tuns16, Tint32,Tint32, Tint32)
- X(Tbool,Tint32, Tint32,Tint32, Tint32)
- X(Tbool,Tuns32, Tuns32,Tuns32, Tuns32)
- X(Tbool,Tint64, Tint64,Tint64, Tint64)
- X(Tbool,Tuns64, Tuns64,Tuns64, Tuns64)
- X(Tbool,Tint128, Tint128,Tint128, Tint128)
- X(Tbool,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tbool,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tbool,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tbool,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tbool,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tbool,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tbool,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tbool,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tbool,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tbool,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tint8,Tint8, Tint32,Tint32, Tint32)
- X(Tint8,Tuns8, Tint32,Tint32, Tint32)
- X(Tint8,Tint16, Tint32,Tint32, Tint32)
- X(Tint8,Tuns16, Tint32,Tint32, Tint32)
- X(Tint8,Tint32, Tint32,Tint32, Tint32)
- X(Tint8,Tuns32, Tuns32,Tuns32, Tuns32)
- X(Tint8,Tint64, Tint64,Tint64, Tint64)
- X(Tint8,Tuns64, Tuns64,Tuns64, Tuns64)
- X(Tint8,Tint128, Tint128,Tint128, Tint128)
- X(Tint8,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tint8,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tint8,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tint8,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tint8,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tint8,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tint8,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tint8,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tint8,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tint8,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tuns8,Tuns8, Tint32,Tint32, Tint32)
- X(Tuns8,Tint16, Tint32,Tint32, Tint32)
- X(Tuns8,Tuns16, Tint32,Tint32, Tint32)
- X(Tuns8,Tint32, Tint32,Tint32, Tint32)
- X(Tuns8,Tuns32, Tuns32,Tuns32, Tuns32)
- X(Tuns8,Tint64, Tint64,Tint64, Tint64)
- X(Tuns8,Tuns64, Tuns64,Tuns64, Tuns64)
- X(Tuns8,Tint128, Tint128,Tint128, Tint128)
- X(Tuns8,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tuns8,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tuns8,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tuns8,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tuns8,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tuns8,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tuns8,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tuns8,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tuns8,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tuns8,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tint16,Tint16, Tint32,Tint32, Tint32)
- X(Tint16,Tuns16, Tint32,Tint32, Tint32)
- X(Tint16,Tint32, Tint32,Tint32, Tint32)
- X(Tint16,Tuns32, Tuns32,Tuns32, Tuns32)
- X(Tint16,Tint64, Tint64,Tint64, Tint64)
- X(Tint16,Tuns64, Tuns64,Tuns64, Tuns64)
- X(Tint16,Tint128, Tint128,Tint128, Tint128)
- X(Tint16,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tint16,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tint16,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tint16,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tint16,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tint16,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tint16,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tint16,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tint16,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tint16,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tuns16,Tuns16, Tint32,Tint32, Tint32)
- X(Tuns16,Tint32, Tint32,Tint32, Tint32)
- X(Tuns16,Tuns32, Tuns32,Tuns32, Tuns32)
- X(Tuns16,Tint64, Tint64,Tint64, Tint64)
- X(Tuns16,Tuns64, Tuns64,Tuns64, Tuns64)
- X(Tuns16,Tint128, Tint128,Tint128, Tint128)
- X(Tuns16,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tuns16,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tuns16,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tuns16,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tuns16,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tuns16,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tuns16,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tuns16,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tuns16,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tuns16,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tint32,Tint32, Tint32,Tint32, Tint32)
- X(Tint32,Tuns32, Tuns32,Tuns32, Tuns32)
- X(Tint32,Tint64, Tint64,Tint64, Tint64)
- X(Tint32,Tuns64, Tuns64,Tuns64, Tuns64)
- X(Tint32,Tint128, Tint128,Tint128, Tint128)
- X(Tint32,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tint32,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tint32,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tint32,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tint32,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tint32,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tint32,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tint32,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tint32,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tint32,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tuns32,Tuns32, Tuns32,Tuns32, Tuns32)
- X(Tuns32,Tint64, Tint64,Tint64, Tint64)
- X(Tuns32,Tuns64, Tuns64,Tuns64, Tuns64)
- X(Tuns32,Tint128, Tint128,Tint128, Tint128)
- X(Tuns32,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tuns32,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tuns32,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tuns32,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tuns32,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tuns32,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tuns32,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tuns32,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tuns32,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tuns32,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tint64,Tint64, Tint64,Tint64, Tint64)
- X(Tint64,Tuns64, Tuns64,Tuns64, Tuns64)
- X(Tint64,Tint128, Tint128,Tint128, Tint128)
- X(Tint64,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tint64,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tint64,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tint64,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tint64,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tint64,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tint64,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tint64,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tint64,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tint64,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tuns64,Tuns64, Tuns64,Tuns64, Tuns64)
- X(Tuns64,Tint128, Tint128,Tint128, Tint128)
- X(Tuns64,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tuns64,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tuns64,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tuns64,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tuns64,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tuns64,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tuns64,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tuns64,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tuns64,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tuns64,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tint128,Tint128, Tint128,Tint128, Tint128)
- X(Tint128,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tint128,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tint128,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tint128,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tint128,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tint128,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tint128,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tint128,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tint128,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tint128,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tuns128,Tuns128, Tuns128,Tuns128, Tuns128)
-
- X(Tuns128,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tuns128,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tuns128,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
- X(Tuns128,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tuns128,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tuns128,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
- X(Tuns128,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tuns128,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tuns128,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tfloat32,Tfloat32, Tfloat32,Tfloat32, Tfloat32)
- X(Tfloat32,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tfloat32,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
-
- X(Tfloat32,Timaginary32, Tfloat32,Timaginary32, Tfloat32)
- X(Tfloat32,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tfloat32,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
-
- X(Tfloat32,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32)
- X(Tfloat32,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tfloat32,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tfloat64,Tfloat64, Tfloat64,Tfloat64, Tfloat64)
- X(Tfloat64,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
-
- X(Tfloat64,Timaginary32, Tfloat64,Timaginary64, Tfloat64)
- X(Tfloat64,Timaginary64, Tfloat64,Timaginary64, Tfloat64)
- X(Tfloat64,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
-
- X(Tfloat64,Tcomplex32, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tfloat64,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64)
- X(Tfloat64,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tfloat80,Tfloat80, Tfloat80,Tfloat80, Tfloat80)
-
- X(Tfloat80,Timaginary32, Tfloat80,Timaginary80, Tfloat80)
- X(Tfloat80,Timaginary64, Tfloat80,Timaginary80, Tfloat80)
- X(Tfloat80,Timaginary80, Tfloat80,Timaginary80, Tfloat80)
-
- X(Tfloat80,Tcomplex32, Tfloat80,Tcomplex80, Tcomplex80)
- X(Tfloat80,Tcomplex64, Tfloat80,Tcomplex80, Tcomplex80)
- X(Tfloat80,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Timaginary32,Timaginary32, Timaginary32,Timaginary32, Timaginary32)
- X(Timaginary32,Timaginary64, Timaginary64,Timaginary64, Timaginary64)
- X(Timaginary32,Timaginary80, Timaginary80,Timaginary80, Timaginary80)
-
- X(Timaginary32,Tcomplex32, Timaginary32,Tcomplex32, Tcomplex32)
- X(Timaginary32,Tcomplex64, Timaginary64,Tcomplex64, Tcomplex64)
- X(Timaginary32,Tcomplex80, Timaginary80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Timaginary64,Timaginary64, Timaginary64,Timaginary64, Timaginary64)
- X(Timaginary64,Timaginary80, Timaginary80,Timaginary80, Timaginary80)
-
- X(Timaginary64,Tcomplex32, Timaginary64,Tcomplex64, Tcomplex64)
- X(Timaginary64,Tcomplex64, Timaginary64,Tcomplex64, Tcomplex64)
- X(Timaginary64,Tcomplex80, Timaginary80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Timaginary80,Timaginary80, Timaginary80,Timaginary80, Timaginary80)
-
- X(Timaginary80,Tcomplex32, Timaginary80,Tcomplex80, Tcomplex80)
- X(Timaginary80,Tcomplex64, Timaginary80,Tcomplex80, Tcomplex80)
- X(Timaginary80,Tcomplex80, Timaginary80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tcomplex32,Tcomplex32, Tcomplex32,Tcomplex32, Tcomplex32)
- X(Tcomplex32,Tcomplex64, Tcomplex64,Tcomplex64, Tcomplex64)
- X(Tcomplex32,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tcomplex64,Tcomplex64, Tcomplex64,Tcomplex64, Tcomplex64)
- X(Tcomplex64,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80)
-
- /* ======================= */
-
- X(Tcomplex80,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80)
-
-#undef X
-
-#define Y(t1,t2) impcnvWarnTab[t1][t2] = 1;
-
- Y(Tuns8, Tint8)
- Y(Tint16, Tint8)
- Y(Tuns16, Tint8)
- Y(Tint32, Tint8)
- Y(Tuns32, Tint8)
- Y(Tint64, Tint8)
- Y(Tuns64, Tint8)
- Y(Tint128, Tint8)
- Y(Tuns128, Tint8)
-
- Y(Tint8, Tuns8)
- Y(Tint16, Tuns8)
- Y(Tuns16, Tuns8)
- Y(Tint32, Tuns8)
- Y(Tuns32, Tuns8)
- Y(Tint64, Tuns8)
- Y(Tuns64, Tuns8)
- Y(Tint128, Tuns8)
- Y(Tuns128, Tuns8)
-
- Y(Tint8, Tchar)
- Y(Tint16, Tchar)
- Y(Tuns16, Tchar)
- Y(Tint32, Tchar)
- Y(Tuns32, Tchar)
- Y(Tint64, Tchar)
- Y(Tuns64, Tchar)
- Y(Tint128, Tchar)
- Y(Tuns128, Tchar)
-
- Y(Tuns16, Tint16)
- Y(Tint32, Tint16)
- Y(Tuns32, Tint16)
- Y(Tint64, Tint16)
- Y(Tuns64, Tint16)
- Y(Tint128, Tint16)
- Y(Tuns128, Tint16)
-
- Y(Tint16, Tuns16)
- Y(Tint32, Tuns16)
- Y(Tuns32, Tuns16)
- Y(Tint64, Tuns16)
- Y(Tuns64, Tuns16)
- Y(Tint128, Tuns16)
- Y(Tuns128, Tuns16)
-
- Y(Tint16, Twchar)
- Y(Tint32, Twchar)
- Y(Tuns32, Twchar)
- Y(Tint64, Twchar)
- Y(Tuns64, Twchar)
- Y(Tint128, Twchar)
- Y(Tuns128, Twchar)
-
-// Y(Tuns32, Tint32)
- Y(Tint64, Tint32)
- Y(Tuns64, Tint32)
- Y(Tint128, Tint32)
- Y(Tuns128, Tint32)
-
-// Y(Tint32, Tuns32)
- Y(Tint64, Tuns32)
- Y(Tuns64, Tuns32)
- Y(Tint128, Tuns32)
- Y(Tuns128, Tuns32)
-
- Y(Tint64, Tdchar)
- Y(Tuns64, Tdchar)
- Y(Tint128, Tdchar)
- Y(Tuns128, Tdchar)
-
-// Y(Tint64, Tuns64)
-// Y(Tuns64, Tint64)
- Y(Tint128, Tint64)
- Y(Tuns128, Tint64)
- Y(Tint128, Tuns64)
- Y(Tuns128, Tuns64)
-
-// Y(Tint128, Tuns128)
-// Y(Tuns128, Tint128)
-
- for (i = 0; i < TMAX; i++)
- for (j = 0; j < TMAX; j++)
- {
- if (impcnvResultTab[i][j] == Terror)
- {
- impcnvResultTab[i][j] = impcnvResultTab[j][i];
- impcnvType1Tab[i][j] = impcnvType2Tab[j][i];
- impcnvType2Tab[i][j] = impcnvType1Tab[j][i];
- }
- }
-}
-
-int main()
-{
- int i;
- int j;
-
- init();
-
- {
- FILE *fp = fopen("impcnvtab.c","wb");
-
- fprintf(fp,"// This file is generated by impcnvgen.c\n");
- fprintf(fp,"#include \"mtype.h\"\n");
-
- fprintf(fp,"unsigned char impcnvResult[TMAX][TMAX] =\n{\n");
- for (i = 0; i < TMAX; i++)
- {
- if (i)
- fprintf(fp, ",");
- fprintf(fp, "{");
- for (j = 0; j < TMAX; j++)
- {
- if (j)
- fprintf(fp, ",");
- fprintf(fp, "%d",impcnvResultTab[i][j]);
- }
- fprintf(fp, "}\n");
- }
- fprintf(fp,"};\n");
-
- fprintf(fp,"unsigned char impcnvType1[TMAX][TMAX] =\n{\n");
- for (i = 0; i < TMAX; i++)
- {
- if (i)
- fprintf(fp, ",");
- fprintf(fp, "{");
- for (j = 0; j < TMAX; j++)
- {
- if (j)
- fprintf(fp, ",");
- fprintf(fp, "%d",impcnvType1Tab[i][j]);
- }
- fprintf(fp, "}\n");
- }
- fprintf(fp,"};\n");
-
- fprintf(fp,"unsigned char impcnvType2[TMAX][TMAX] =\n{\n");
- for (i = 0; i < TMAX; i++)
- {
- if (i)
- fprintf(fp, ",");
- fprintf(fp, "{");
- for (j = 0; j < TMAX; j++)
- {
- if (j)
- fprintf(fp, ",");
- fprintf(fp, "%d",impcnvType2Tab[i][j]);
- }
- fprintf(fp, "}\n");
- }
- fprintf(fp,"};\n");
-
- fprintf(fp,"unsigned char impcnvWarn[TMAX][TMAX] =\n{\n");
- for (i = 0; i < TMAX; i++)
- {
- if (i)
- fprintf(fp, ",");
- fprintf(fp, "{");
- for (j = 0; j < TMAX; j++)
- {
- if (j)
- fprintf(fp, ",");
- fprintf(fp, "%d",impcnvWarnTab[i][j]);
- }
- fprintf(fp, "}\n");
- }
- fprintf(fp,"};\n");
-
- fclose(fp);
- }
-
- {
- FILE *fp = fopen("impcnvtab.d", "wb");
-
- fprintf(fp, "// This file is generated by impcnvgen.c\n");
- fprintf(fp, "module ddmd.impcnvtab;\n");
- fprintf(fp, "\n");
- fprintf(fp, "import ddmd.mtype;\n");
- fprintf(fp, "\n");
-
- fprintf(fp, "extern (C++) __gshared ubyte[TMAX][TMAX] impcnvResult =\n[\n");
- for (i = 0; i < TMAX; i++)
- {
- if (i)
- fprintf(fp, ",\n");
- fprintf(fp, " [");
- for (j = 0; j < TMAX; j++)
- {
- if (j)
- fprintf(fp, ",");
- fprintf(fp, "%d", impcnvResultTab[i][j]);
- }
- fprintf(fp, "]");
- }
- fprintf(fp, "\n];\n");
-
- fprintf(fp, "extern (C++) __gshared ubyte[TMAX][TMAX] impcnvType1 =\n[\n");
- for (i = 0; i < TMAX; i++)
- {
- if (i)
- fprintf(fp, ",\n");
- fprintf(fp, " [");
- for (j = 0; j < TMAX; j++)
- {
- if (j)
- fprintf(fp, ",");
- fprintf(fp, "%d", impcnvType1Tab[i][j]);
- }
- fprintf(fp, "]");
- }
- fprintf(fp, "\n];\n");
-
- fprintf(fp, "extern (C++) __gshared ubyte[TMAX][TMAX] impcnvType2 =\n[\n");
- for (i = 0; i < TMAX; i++)
- {
- if (i)
- fprintf(fp, ",\n");
- fprintf(fp, " [");
- for (j = 0; j < TMAX; j++)
- {
- if (j)
- fprintf(fp, ",");
- fprintf(fp, "%d",impcnvType2Tab[i][j]);
- }
- fprintf(fp, "]");
- }
- fprintf(fp,"\n];\n");
-
- fprintf(fp,"extern (C++) __gshared ubyte[TMAX][TMAX] impcnvWarn =\n[\n");
- for (i = 0; i < TMAX; i++)
- {
- if (i)
- fprintf(fp, ",\n");
- fprintf(fp, " [");
- for (j = 0; j < TMAX; j++)
- {
- if (j)
- fprintf(fp, ",");
- fprintf(fp, "%d", impcnvWarnTab[i][j]);
- }
- fprintf(fp, "]");
- }
- fprintf(fp, "\n];\n");
-
- fclose(fp);
- }
-
- return EXIT_SUCCESS;
-}
diff --git a/gcc/d/dmd/impcnvtab.d b/gcc/d/dmd/impcnvtab.d
new file mode 100644
index 00000000000..db09f0c568e
--- /dev/null
+++ b/gcc/d/dmd/impcnvtab.d
@@ -0,0 +1,379 @@
+/**
+ * Provides an implicit conversion table for basic types.
+ *
+ * Used to determine integer promotions and common types.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/type.html#integer-promotions, Integer Promotions),
+ * $(LINK2 https://dlang.org/spec/type.html#usual-arithmetic-conversions, Usual Arithmetic Conversions).
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/impcnvtab.d, _impcnvtab.d)
+ * Documentation: https://dlang.org/phobos/dmd_impcnvtab.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/impcnvtab.d
+ */
+
+module dmd.impcnvtab;
+
+import dmd.astenums;
+import dmd.mtype;
+
+pure @nogc nothrow @safe:
+
+/*************************************************
+ * If ty1 and ty2 are basic types, return the TY that both can
+ * be implicitly converted to.
+ * Params:
+ * ty1 = first operand type
+ * ty2 = second operand type
+ * Returns:
+ * ty = common type, else Terror
+ */
+TY implicitConvCommonTy(TY ty1, TY ty2)
+{
+ return impCnvTab.impcnvResultTab[ty1][ty2];
+}
+
+/*************************************************
+ * If ty1 and ty2 are basic types, return the TY that ty1 can
+ * be implicitly converted to to bring them to a common ty.
+ * It's symmetric, i.e. the operands can be swapped.
+ * Params:
+ * ty1 = first operand type
+ * ty2 = second operand type
+ * Returns:
+ * ty = what ty1 should be converted to, else Terror
+ */
+TY implicitConvTy1(TY ty1, TY ty2)
+{
+ return impCnvTab.impcnvType1Tab[ty1][ty2];
+}
+
+/******************************************************************************/
+
+private:
+
+struct ImpCnvTab
+{
+ TY[TMAX][TMAX] impcnvResultTab;
+ TY[TMAX][TMAX] impcnvType1Tab;
+}
+
+enum ImpCnvTab impCnvTab = generateImpCnvTab();
+
+ImpCnvTab generateImpCnvTab()
+{
+ ImpCnvTab impCnvTab;
+
+ // Set conversion tables
+ foreach (i; 0 .. cast(size_t)TMAX)
+ {
+ foreach (j; 0 .. cast(size_t)TMAX)
+ {
+ impCnvTab.impcnvResultTab[i][j] = Terror;
+ impCnvTab.impcnvType1Tab[i][j] = Terror;
+ }
+ }
+
+ void X(TY t1, TY t2, TY nt1, TY nt2, TY rt)
+ {
+ impCnvTab.impcnvResultTab[t1][t2] = rt;
+ impCnvTab.impcnvResultTab[t2][t1] = rt;
+
+ impCnvTab.impcnvType1Tab[t1][t2] = nt1;
+ impCnvTab.impcnvType1Tab[t2][t1] = nt2;
+ }
+
+ /* ======================= */
+
+ X(Tbool,Tbool, Tbool,Tbool, Tbool);
+ X(Tbool,Tint8, Tint32,Tint32, Tint32);
+ X(Tbool,Tuns8, Tint32,Tint32, Tint32);
+ X(Tbool,Tint16, Tint32,Tint32, Tint32);
+ X(Tbool,Tuns16, Tint32,Tint32, Tint32);
+ X(Tbool,Tint32, Tint32,Tint32, Tint32);
+ X(Tbool,Tuns32, Tuns32,Tuns32, Tuns32);
+ X(Tbool,Tint64, Tint64,Tint64, Tint64);
+ X(Tbool,Tuns64, Tuns64,Tuns64, Tuns64);
+ X(Tbool,Tint128, Tint128,Tint128, Tint128);
+ X(Tbool,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tbool,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tbool,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tbool,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tbool,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tbool,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tbool,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tbool,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tbool,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tbool,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tint8,Tint8, Tint32,Tint32, Tint32);
+ X(Tint8,Tuns8, Tint32,Tint32, Tint32);
+ X(Tint8,Tint16, Tint32,Tint32, Tint32);
+ X(Tint8,Tuns16, Tint32,Tint32, Tint32);
+ X(Tint8,Tint32, Tint32,Tint32, Tint32);
+ X(Tint8,Tuns32, Tuns32,Tuns32, Tuns32);
+ X(Tint8,Tint64, Tint64,Tint64, Tint64);
+ X(Tint8,Tuns64, Tuns64,Tuns64, Tuns64);
+ X(Tint8,Tint128, Tint128,Tint128, Tint128);
+ X(Tint8,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tint8,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tint8,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tint8,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tint8,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tint8,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tint8,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tint8,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tint8,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tint8,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tuns8,Tuns8, Tint32,Tint32, Tint32);
+ X(Tuns8,Tint16, Tint32,Tint32, Tint32);
+ X(Tuns8,Tuns16, Tint32,Tint32, Tint32);
+ X(Tuns8,Tint32, Tint32,Tint32, Tint32);
+ X(Tuns8,Tuns32, Tuns32,Tuns32, Tuns32);
+ X(Tuns8,Tint64, Tint64,Tint64, Tint64);
+ X(Tuns8,Tuns64, Tuns64,Tuns64, Tuns64);
+ X(Tuns8,Tint128, Tint128,Tint128, Tint128);
+ X(Tuns8,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tuns8,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tuns8,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tuns8,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tuns8,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tuns8,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tuns8,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tuns8,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tuns8,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tuns8,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tint16,Tint16, Tint32,Tint32, Tint32);
+ X(Tint16,Tuns16, Tint32,Tint32, Tint32);
+ X(Tint16,Tint32, Tint32,Tint32, Tint32);
+ X(Tint16,Tuns32, Tuns32,Tuns32, Tuns32);
+ X(Tint16,Tint64, Tint64,Tint64, Tint64);
+ X(Tint16,Tuns64, Tuns64,Tuns64, Tuns64);
+ X(Tint16,Tint128, Tint128,Tint128, Tint128);
+ X(Tint16,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tint16,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tint16,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tint16,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tint16,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tint16,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tint16,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tint16,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tint16,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tint16,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tuns16,Tuns16, Tint32,Tint32, Tint32);
+ X(Tuns16,Tint32, Tint32,Tint32, Tint32);
+ X(Tuns16,Tuns32, Tuns32,Tuns32, Tuns32);
+ X(Tuns16,Tint64, Tint64,Tint64, Tint64);
+ X(Tuns16,Tuns64, Tuns64,Tuns64, Tuns64);
+ X(Tuns16,Tint128, Tint128,Tint128, Tint128);
+ X(Tuns16,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tuns16,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tuns16,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tuns16,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tuns16,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tuns16,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tuns16,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tuns16,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tuns16,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tuns16,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tint32,Tint32, Tint32,Tint32, Tint32);
+ X(Tint32,Tuns32, Tuns32,Tuns32, Tuns32);
+ X(Tint32,Tint64, Tint64,Tint64, Tint64);
+ X(Tint32,Tuns64, Tuns64,Tuns64, Tuns64);
+ X(Tint32,Tint128, Tint128,Tint128, Tint128);
+ X(Tint32,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tint32,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tint32,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tint32,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tint32,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tint32,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tint32,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tint32,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tint32,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tint32,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tuns32,Tuns32, Tuns32,Tuns32, Tuns32);
+ X(Tuns32,Tint64, Tint64,Tint64, Tint64);
+ X(Tuns32,Tuns64, Tuns64,Tuns64, Tuns64);
+ X(Tuns32,Tint128, Tint128,Tint128, Tint128);
+ X(Tuns32,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tuns32,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tuns32,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tuns32,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tuns32,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tuns32,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tuns32,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tuns32,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tuns32,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tuns32,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tint64,Tint64, Tint64,Tint64, Tint64);
+ X(Tint64,Tuns64, Tuns64,Tuns64, Tuns64);
+ X(Tint64,Tint128, Tint128,Tint128, Tint128);
+ X(Tint64,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tint64,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tint64,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tint64,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tint64,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tint64,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tint64,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tint64,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tint64,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tint64,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tuns64,Tuns64, Tuns64,Tuns64, Tuns64);
+ X(Tuns64,Tint128, Tint128,Tint128, Tint128);
+ X(Tuns64,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tuns64,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tuns64,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tuns64,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tuns64,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tuns64,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tuns64,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tuns64,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tuns64,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tuns64,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tint128,Tint128, Tint128,Tint128, Tint128);
+ X(Tint128,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tint128,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tint128,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tint128,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tint128,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tint128,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tint128,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tint128,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tint128,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tint128,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tuns128,Tuns128, Tuns128,Tuns128, Tuns128);
+
+ X(Tuns128,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tuns128,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tuns128,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+ X(Tuns128,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tuns128,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tuns128,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+ X(Tuns128,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tuns128,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tuns128,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tfloat32,Tfloat32, Tfloat32,Tfloat32, Tfloat32);
+ X(Tfloat32,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tfloat32,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+
+ X(Tfloat32,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
+ X(Tfloat32,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tfloat32,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+
+ X(Tfloat32,Tcomplex32, Tfloat32,Tcomplex32, Tcomplex32);
+ X(Tfloat32,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tfloat32,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tfloat64,Tfloat64, Tfloat64,Tfloat64, Tfloat64);
+ X(Tfloat64,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+
+ X(Tfloat64,Timaginary32, Tfloat64,Timaginary64, Tfloat64);
+ X(Tfloat64,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
+ X(Tfloat64,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+
+ X(Tfloat64,Tcomplex32, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tfloat64,Tcomplex64, Tfloat64,Tcomplex64, Tcomplex64);
+ X(Tfloat64,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tfloat80,Tfloat80, Tfloat80,Tfloat80, Tfloat80);
+
+ X(Tfloat80,Timaginary32, Tfloat80,Timaginary80, Tfloat80);
+ X(Tfloat80,Timaginary64, Tfloat80,Timaginary80, Tfloat80);
+ X(Tfloat80,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
+
+ X(Tfloat80,Tcomplex32, Tfloat80,Tcomplex80, Tcomplex80);
+ X(Tfloat80,Tcomplex64, Tfloat80,Tcomplex80, Tcomplex80);
+ X(Tfloat80,Tcomplex80, Tfloat80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Timaginary32,Timaginary32, Timaginary32,Timaginary32, Timaginary32);
+ X(Timaginary32,Timaginary64, Timaginary64,Timaginary64, Timaginary64);
+ X(Timaginary32,Timaginary80, Timaginary80,Timaginary80, Timaginary80);
+
+ X(Timaginary32,Tcomplex32, Timaginary32,Tcomplex32, Tcomplex32);
+ X(Timaginary32,Tcomplex64, Timaginary64,Tcomplex64, Tcomplex64);
+ X(Timaginary32,Tcomplex80, Timaginary80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Timaginary64,Timaginary64, Timaginary64,Timaginary64, Timaginary64);
+ X(Timaginary64,Timaginary80, Timaginary80,Timaginary80, Timaginary80);
+
+ X(Timaginary64,Tcomplex32, Timaginary64,Tcomplex64, Tcomplex64);
+ X(Timaginary64,Tcomplex64, Timaginary64,Tcomplex64, Tcomplex64);
+ X(Timaginary64,Tcomplex80, Timaginary80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Timaginary80,Timaginary80, Timaginary80,Timaginary80, Timaginary80);
+
+ X(Timaginary80,Tcomplex32, Timaginary80,Tcomplex80, Tcomplex80);
+ X(Timaginary80,Tcomplex64, Timaginary80,Tcomplex80, Tcomplex80);
+ X(Timaginary80,Tcomplex80, Timaginary80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tcomplex32,Tcomplex32, Tcomplex32,Tcomplex32, Tcomplex32);
+ X(Tcomplex32,Tcomplex64, Tcomplex64,Tcomplex64, Tcomplex64);
+ X(Tcomplex32,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tcomplex64,Tcomplex64, Tcomplex64,Tcomplex64, Tcomplex64);
+ X(Tcomplex64,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80);
+
+ /* ======================= */
+
+ X(Tcomplex80,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80);
+
+ return impCnvTab;
+}
diff --git a/gcc/d/dmd/imphint.c b/gcc/d/dmd/imphint.c
deleted file mode 100644
index 239cb073332..00000000000
--- a/gcc/d/dmd/imphint.c
+++ /dev/null
@@ -1,52 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 2010-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/imphint.c
- */
-
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-
-/******************************************
- * Looks for undefined identifier s to see
- * if it might be undefined because an import
- * was not specified.
- * Not meant to be a comprehensive list of names in each module,
- * just the most common ones.
- */
-
-const char *importHint(const char *s)
-{
- static const char *modules[] =
- { "core.stdc.stdio",
- "std.stdio",
- "std.math",
- NULL
- };
- static const char *names[] =
- {
- "printf", NULL,
- "writeln", NULL,
- "sin", "cos", "sqrt", "fabs", NULL,
- };
- int m = 0;
- for (int n = 0; modules[m]; n++)
- {
- const char *p = names[n];
- if (p == NULL)
- {
- m++;
- continue;
- }
- assert(modules[m]);
- if (strcmp(s, p) == 0)
- return modules[m];
- }
- return NULL; // didn't find it
-}
diff --git a/gcc/d/dmd/imphint.d b/gcc/d/dmd/imphint.d
new file mode 100644
index 00000000000..e1919a67037
--- /dev/null
+++ b/gcc/d/dmd/imphint.d
@@ -0,0 +1,91 @@
+/**
+ * Give import hints for common symbol names that couldn't be resolved.
+ *
+ * For example, prompt to `import std.stdio` when using `writeln`.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/imphint.d, _imphint.d)
+ * Documentation: https://dlang.org/phobos/dmd_imphint.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/imphint.d
+ */
+
+module dmd.imphint;
+
+/******************************************
+ * Looks for undefined identifier s to see
+ * if it might be undefined because an import
+ * was not specified.
+ * Not meant to be a comprehensive list of names in each module,
+ * just the most common ones.
+ */
+const(char)[] importHint(const(char)[] s)
+{
+ if (auto entry = s in hints)
+ return *entry;
+ return null;
+}
+
+private immutable string[string] hints;
+
+shared static this()
+{
+ // in alphabetic order
+ hints = [
+ "AliasSeq": "std.meta",
+ "appender": "std.array",
+ "array": "std.array",
+ "calloc": "core.stdc.stdlib",
+ "chdir": "std.file",
+ "cos": "std.math",
+ "dirEntries": "std.file",
+ "drop": "std.range",
+ "each": "std.algorithm",
+ "empty": "std.range",
+ "endsWith": "std.algorithm",
+ "enforce": "std.exception",
+ "enumerate": "std.range",
+ "equal": "std.algorithm",
+ "exists": "std.file",
+ "fabs": "std.math",
+ "filter": "std.algorithm",
+ "format": "std.format",
+ "free": "core.stdc.stdlib",
+ "front": "std.range",
+ "iota": "std.range",
+ "isDir": "std.file",
+ "isFile": "std.file",
+ "join": "std.array",
+ "joiner": "std.algorithm",
+ "malloc": "core.stdc.stdlib",
+ "map": "std.algorithm",
+ "max": "std.algorithm",
+ "min": "std.algorithm",
+ "mkdir": "std.file",
+ "popFront": "std.range",
+ "printf": "core.stdc.stdio",
+ "realloc": "core.stdc.stdlib",
+ "replace": "std.array",
+ "rmdir": "std.file",
+ "sin": "std.math",
+ "sort": "std.algorithm",
+ "split": "std.array",
+ "sqrt": "std.math",
+ "startsWith": "std.algorithm",
+ "take": "std.range",
+ "text": "std.conv",
+ "to": "std.conv",
+ "writefln": "std.stdio",
+ "writeln": "std.stdio",
+ "__va_argsave_t": "core.stdc.stdarg",
+ "__va_list_tag": "core.stdc.stdarg",
+ ];
+}
+
+unittest
+{
+ assert(importHint("printf") !is null);
+ assert(importHint("fabs") !is null);
+ assert(importHint("xxxxx") is null);
+}
diff --git a/gcc/d/dmd/import.h b/gcc/d/dmd/import.h
index 07fb32aa070..34c5a05a8cd 100644
--- a/gcc/d/dmd/import.h
+++ b/gcc/d/dmd/import.h
@@ -16,7 +16,6 @@ class Identifier;
struct Scope;
class Module;
class Package;
-class AliasDeclaration;
class Import : public Dsymbol
{
@@ -24,11 +23,11 @@ public:
/* static import aliasId = pkg1.pkg2.id : alias1 = name1, alias2 = name2;
*/
- Identifiers *packages; // array of Identifier's representing packages
+ DArray<Identifier*> packages; // array of Identifier's representing packages
Identifier *id; // module Identifier
Identifier *aliasId;
int isstatic; // !=0 if static import
- Prot protection;
+ Visibility visibility;
// Pairs of alias=name to bind into current namespace
Identifiers names;
@@ -39,15 +38,11 @@ public:
AliasDeclarations aliasdecls; // corresponding AliasDeclarations for alias=name pairs
- Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId,
- int isstatic);
- void addAlias(Identifier *name, Identifier *alias);
const char *kind() const;
- Prot prot();
- Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees
+ Visibility visible();
+ Import *syntaxCopy(Dsymbol *s); // copy only syntax trees
void load(Scope *sc);
void importAll(Scope *sc);
- void addPackageAccess(ScopeDsymbol *scopesym);
Dsymbol *toAlias();
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope* sc);
diff --git a/gcc/d/dmd/init.c b/gcc/d/dmd/init.c
deleted file mode 100644
index d18e0549aac..00000000000
--- a/gcc/d/dmd/init.c
+++ /dev/null
@@ -1,282 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/init.c
- */
-
-#include "root/dsystem.h"
-#include "root/checkedint.h"
-
-#include "mars.h"
-#include "init.h"
-#include "expression.h"
-#include "statement.h"
-#include "identifier.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "scope.h"
-#include "mtype.h"
-#include "hdrgen.h"
-#include "template.h"
-#include "id.h"
-#include "tokens.h"
-
-/********************************** Initializer *******************************/
-
-Initializer::Initializer(Loc loc)
-{
- this->loc = loc;
-}
-
-Initializers *Initializer::arraySyntaxCopy(Initializers *ai)
-{
- Initializers *a = NULL;
- if (ai)
- {
- a = new Initializers();
- a->setDim(ai->length);
- for (size_t i = 0; i < a->length; i++)
- (*a)[i] = (*ai)[i]->syntaxCopy();
- }
- return a;
-}
-
-const char *Initializer::toChars()
-{
- OutBuffer buf;
- HdrGenState hgs;
- ::toCBuffer(this, &buf, &hgs);
- return buf.extractChars();
-}
-
-/********************************** ErrorInitializer ***************************/
-
-ErrorInitializer::ErrorInitializer()
- : Initializer(Loc())
-{
-}
-
-Initializer *ErrorInitializer::syntaxCopy()
-{
- return this;
-}
-
-/********************************** VoidInitializer ***************************/
-
-VoidInitializer::VoidInitializer(Loc loc)
- : Initializer(loc)
-{
- type = NULL;
-}
-
-Initializer *VoidInitializer::syntaxCopy()
-{
- return new VoidInitializer(loc);
-}
-
-/********************************** StructInitializer *************************/
-
-StructInitializer::StructInitializer(Loc loc)
- : Initializer(loc)
-{
-}
-
-Initializer *StructInitializer::syntaxCopy()
-{
- StructInitializer *ai = new StructInitializer(loc);
- assert(field.length == value.length);
- ai->field.setDim(field.length);
- ai->value.setDim(value.length);
- for (size_t i = 0; i < field.length; i++)
- {
- ai->field[i] = field[i];
- ai->value[i] = value[i]->syntaxCopy();
- }
- return ai;
-}
-
-void StructInitializer::addInit(Identifier *field, Initializer *value)
-{
- //printf("StructInitializer::addInit(field = %p, value = %p)\n", field, value);
- this->field.push(field);
- this->value.push(value);
-}
-
-/********************************** ArrayInitializer ************************************/
-
-ArrayInitializer::ArrayInitializer(Loc loc)
- : Initializer(loc)
-{
- dim = 0;
- type = NULL;
- sem = false;
-}
-
-Initializer *ArrayInitializer::syntaxCopy()
-{
- //printf("ArrayInitializer::syntaxCopy()\n");
- ArrayInitializer *ai = new ArrayInitializer(loc);
- assert(index.length == value.length);
- ai->index.setDim(index.length);
- ai->value.setDim(value.length);
- for (size_t i = 0; i < ai->value.length; i++)
- {
- ai->index[i] = index[i] ? index[i]->syntaxCopy() : NULL;
- ai->value[i] = value[i]->syntaxCopy();
- }
- return ai;
-}
-
-void ArrayInitializer::addInit(Expression *index, Initializer *value)
-{
- this->index.push(index);
- this->value.push(value);
- dim = 0;
- type = NULL;
-}
-
-bool ArrayInitializer::isAssociativeArray()
-{
- for (size_t i = 0; i < value.length; i++)
- {
- if (index[i])
- return true;
- }
- return false;
-}
-
-/********************************
- * If possible, convert array initializer to associative array initializer.
- */
-
-Expression *ArrayInitializer::toAssocArrayLiteral()
-{
- Expression *e;
-
- //printf("ArrayInitializer::toAssocArrayInitializer()\n");
- //static int i; if (++i == 2) halt();
- Expressions *keys = new Expressions();
- keys->setDim(value.length);
- Expressions *values = new Expressions();
- values->setDim(value.length);
-
- for (size_t i = 0; i < value.length; i++)
- {
- e = index[i];
- if (!e)
- goto Lno;
- (*keys)[i] = e;
-
- Initializer *iz = value[i];
- if (!iz)
- goto Lno;
- e = initializerToExpression(iz);
- if (!e)
- goto Lno;
- (*values)[i] = e;
- }
- e = new AssocArrayLiteralExp(loc, keys, values);
- return e;
-
-Lno:
- delete keys;
- delete values;
- error(loc, "not an associative array initializer");
- return new ErrorExp();
-}
-
-/********************************** ExpInitializer ************************************/
-
-ExpInitializer::ExpInitializer(Loc loc, Expression *exp)
- : Initializer(loc)
-{
- this->exp = exp;
- this->expandTuples = false;
-}
-
-Initializer *ExpInitializer::syntaxCopy()
-{
- return new ExpInitializer(loc, exp->syntaxCopy());
-}
-
-#if 1 // should be removed and rely on ctfeInterpreter()
-bool arrayHasNonConstPointers(Expressions *elems);
-
-bool hasNonConstPointers(Expression *e)
-{
- if (e->type->ty == Terror)
- return false;
-
- if (e->op == TOKnull)
- return false;
- if (e->op == TOKstructliteral)
- {
- StructLiteralExp *se = (StructLiteralExp *)e;
- return arrayHasNonConstPointers(se->elements);
- }
- if (e->op == TOKarrayliteral)
- {
- if (!e->type->nextOf()->hasPointers())
- return false;
- ArrayLiteralExp *ae = (ArrayLiteralExp *)e;
- return arrayHasNonConstPointers(ae->elements);
- }
- if (e->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e;
- if (ae->type->nextOf()->hasPointers() &&
- arrayHasNonConstPointers(ae->values))
- return true;
- if (((TypeAArray *)ae->type)->index->hasPointers())
- return arrayHasNonConstPointers(ae->keys);
- return false;
- }
- if (e->op == TOKaddress)
- {
- AddrExp *ae = (AddrExp *)e;
- if (ae->e1->op == TOKstructliteral)
- {
- StructLiteralExp *se = (StructLiteralExp *)ae->e1;
- if (!(se->stageflags & stageSearchPointers))
- {
- int old = se->stageflags;
- se->stageflags |= stageSearchPointers;
- bool ret = arrayHasNonConstPointers(se->elements);
- se->stageflags = old;
- return ret;
- }
- else
- {
- return false;
- }
- }
- return true;
- }
- if (e->type->ty== Tpointer && e->type->nextOf()->ty != Tfunction)
- {
- if (e->op == TOKsymoff) // address of a global is OK
- return false;
- if (e->op == TOKint64) // cast(void *)int is OK
- return false;
- if (e->op == TOKstring) // "abc".ptr is OK
- return false;
- return true;
- }
- return false;
-}
-
-bool arrayHasNonConstPointers(Expressions *elems)
-{
- for (size_t i = 0; i < elems->length; i++)
- {
- Expression *e = (*elems)[i];
- if (e && hasNonConstPointers(e))
- return true;
- }
- return false;
-}
-#endif
diff --git a/gcc/d/dmd/init.d b/gcc/d/dmd/init.d
new file mode 100644
index 00000000000..45e101b903a
--- /dev/null
+++ b/gcc/d/dmd/init.d
@@ -0,0 +1,332 @@
+/**
+ * Defines initializers of variables, e.g. the array literal in `int[3] x = [0, 1, 2]`.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/init.d, _init.d)
+ * Documentation: https://dlang.org/phobos/dmd_init.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/init.d
+ */
+
+module dmd.init;
+
+import core.stdc.stdio;
+import core.checkedint;
+
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.ast_node;
+import dmd.dsymbol;
+import dmd.expression;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.tokens;
+import dmd.visitor;
+
+enum NeedInterpret : int
+{
+ INITnointerpret,
+ INITinterpret,
+}
+
+alias INITnointerpret = NeedInterpret.INITnointerpret;
+alias INITinterpret = NeedInterpret.INITinterpret;
+
+/***********************************************************
+ */
+extern (C++) class Initializer : ASTNode
+{
+ Loc loc;
+ InitKind kind;
+
+ override DYNCAST dyncast() const nothrow pure
+ {
+ return DYNCAST.initializer;
+ }
+
+
+ extern (D) this(const ref Loc loc, InitKind kind)
+ {
+ this.loc = loc;
+ this.kind = kind;
+ }
+
+ override final const(char)* toChars() const
+ {
+ OutBuffer buf;
+ HdrGenState hgs;
+ .toCBuffer(this, &buf, &hgs);
+ return buf.extractChars();
+ }
+
+ final inout(ErrorInitializer) isErrorInitializer() inout @nogc nothrow pure
+ {
+ // Use void* cast to skip dynamic casting call
+ return kind == InitKind.error ? cast(inout ErrorInitializer)cast(void*)this : null;
+ }
+
+ final inout(VoidInitializer) isVoidInitializer() inout @nogc nothrow pure
+ {
+ return kind == InitKind.void_ ? cast(inout VoidInitializer)cast(void*)this : null;
+ }
+
+ final inout(StructInitializer) isStructInitializer() inout @nogc nothrow pure
+ {
+ return kind == InitKind.struct_ ? cast(inout StructInitializer)cast(void*)this : null;
+ }
+
+ final inout(ArrayInitializer) isArrayInitializer() inout @nogc nothrow pure
+ {
+ return kind == InitKind.array ? cast(inout ArrayInitializer)cast(void*)this : null;
+ }
+
+ final inout(ExpInitializer) isExpInitializer() inout @nogc nothrow pure
+ {
+ return kind == InitKind.exp ? cast(inout ExpInitializer)cast(void*)this : null;
+ }
+
+ final inout(CInitializer) isCInitializer() inout @nogc nothrow pure
+ {
+ return kind == InitKind.C_ ? cast(inout CInitializer)cast(void*)this : null;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class VoidInitializer : Initializer
+{
+ Type type; // type that this will initialize to
+
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, InitKind.void_);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ErrorInitializer : Initializer
+{
+ extern (D) this()
+ {
+ super(Loc.initial, InitKind.error);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class StructInitializer : Initializer
+{
+ Identifiers field; // of Identifier *'s
+ Initializers value; // parallel array of Initializer *'s
+
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, InitKind.struct_);
+ }
+
+ extern (D) void addInit(Identifier field, Initializer value)
+ {
+ //printf("StructInitializer::addInit(field = %p, value = %p)\n", field, value);
+ this.field.push(field);
+ this.value.push(value);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ArrayInitializer : Initializer
+{
+ Expressions index; // indices
+ Initializers value; // of Initializer *'s
+ uint dim; // length of array being initialized
+ Type type; // type that array will be used to initialize
+ bool sem; // true if semantic() is run
+
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, InitKind.array);
+ }
+
+ extern (D) void addInit(Expression index, Initializer value)
+ {
+ this.index.push(index);
+ this.value.push(value);
+ dim = 0;
+ type = null;
+ }
+
+ bool isAssociativeArray() const pure
+ {
+ foreach (idx; index)
+ {
+ if (idx)
+ return true;
+ }
+ return false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class ExpInitializer : Initializer
+{
+ bool expandTuples;
+ Expression exp;
+
+ extern (D) this(const ref Loc loc, Expression exp)
+ {
+ super(loc, InitKind.exp);
+ this.exp = exp;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/*********************************************
+ * Holds the `designator` for C initializers
+ */
+struct Designator
+{
+ Expression exp; /// [ constant-expression ]
+ Identifier ident; /// . identifier
+
+ this(Expression exp) { this.exp = exp; }
+ this(Identifier ident) { this.ident = ident; }
+}
+
+/*********************************************
+ * Holds the `designation (opt) initializer` for C initializers
+ */
+struct DesigInit
+{
+ Designators* designatorList; /// designation (opt)
+ Initializer initializer; /// initializer
+}
+
+/********************************
+ * C11 6.7.9 Initialization
+ * Represents the C initializer-list
+ */
+extern (C++) final class CInitializer : Initializer
+{
+ DesigInits initializerList; /// initializer-list
+ Type type; /// type that array will be used to initialize
+ bool sem; /// true if semantic() is run
+
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, InitKind.C_);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/****************************************
+ * Copy the AST for Initializer.
+ * Params:
+ * inx = Initializer AST to copy
+ * Returns:
+ * the copy
+ */
+Initializer syntaxCopy(Initializer inx)
+{
+ static Initializer copyStruct(StructInitializer vi)
+ {
+ auto si = new StructInitializer(vi.loc);
+ assert(vi.field.dim == vi.value.dim);
+ si.field.setDim(vi.field.dim);
+ si.value.setDim(vi.value.dim);
+ foreach (const i; 0 .. vi.field.dim)
+ {
+ si.field[i] = vi.field[i];
+ si.value[i] = vi.value[i].syntaxCopy();
+ }
+ return si;
+ }
+
+ static Initializer copyArray(ArrayInitializer vi)
+ {
+ auto ai = new ArrayInitializer(vi.loc);
+ assert(vi.index.dim == vi.value.dim);
+ ai.index.setDim(vi.index.dim);
+ ai.value.setDim(vi.value.dim);
+ foreach (const i; 0 .. vi.value.dim)
+ {
+ ai.index[i] = vi.index[i] ? vi.index[i].syntaxCopy() : null;
+ ai.value[i] = vi.value[i].syntaxCopy();
+ }
+ return ai;
+ }
+
+ static Initializer copyC(CInitializer vi)
+ {
+ auto ci = new CInitializer(vi.loc);
+ ci.initializerList.setDim(vi.initializerList.length);
+ foreach (const i; 0 .. vi.initializerList.length)
+ {
+ DesigInit* cdi = &ci.initializerList[i];
+ DesigInit* vdi = &ci.initializerList[i];
+ cdi.initializer = vdi.initializer.syntaxCopy();
+ if (vdi.designatorList)
+ {
+ cdi.designatorList = new Designators();
+ cdi.designatorList.setDim(vdi.designatorList.length);
+ foreach (const j; 0 .. vdi.designatorList.length)
+ {
+ Designator* cdid = &(*cdi.designatorList)[j];
+ Designator* vdid = &(*vdi.designatorList)[j];
+ cdid.exp = vdid.exp ? vdid.exp.syntaxCopy() : null;
+ cdid.ident = vdid.ident;
+ }
+ }
+ }
+ return ci;
+ }
+
+ final switch (inx.kind)
+ {
+ case InitKind.void_: return new VoidInitializer(inx.loc);
+ case InitKind.error: return inx;
+ case InitKind.struct_: return copyStruct(cast(StructInitializer)inx);
+ case InitKind.array: return copyArray(cast(ArrayInitializer)inx);
+ case InitKind.exp: return new ExpInitializer(inx.loc, (cast(ExpInitializer)inx).exp.syntaxCopy());
+ case InitKind.C_: return copyC(cast(CInitializer)inx);
+ }
+}
diff --git a/gcc/d/dmd/init.h b/gcc/d/dmd/init.h
index 4ba18d6a474..23204b81253 100644
--- a/gcc/d/dmd/init.h
+++ b/gcc/d/dmd/init.h
@@ -10,7 +10,6 @@
#pragma once
-#include "root/root.h"
#include "ast_node.h"
#include "globals.h"
#include "arraytypes.h"
@@ -18,36 +17,31 @@
class Identifier;
class Expression;
-struct Scope;
class Type;
-class AggregateDeclaration;
-class Initializer;
class ErrorInitializer;
class VoidInitializer;
class StructInitializer;
class ArrayInitializer;
class ExpInitializer;
+class CInitializer;
enum NeedInterpret { INITnointerpret, INITinterpret };
-Initializer *initializerSemantic(Initializer *init, Scope *sc, Type *t, NeedInterpret needInterpret);
-
class Initializer : public ASTNode
{
public:
Loc loc;
+ unsigned char kind;
- Initializer(Loc loc);
- virtual Initializer *syntaxCopy() = 0;
- static Initializers *arraySyntaxCopy(Initializers *ai);
+ const char *toChars() const;
- const char *toChars();
+ ErrorInitializer *isErrorInitializer();
+ VoidInitializer *isVoidInitializer();
+ StructInitializer *isStructInitializer();
+ ArrayInitializer *isArrayInitializer();
+ ExpInitializer *isExpInitializer();
+ CInitializer *isCInitializer();
- virtual ErrorInitializer *isErrorInitializer() { return NULL; }
- virtual VoidInitializer *isVoidInitializer() { return NULL; }
- virtual StructInitializer *isStructInitializer() { return NULL; }
- virtual ArrayInitializer *isArrayInitializer() { return NULL; }
- virtual ExpInitializer *isExpInitializer() { return NULL; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -56,20 +50,12 @@ class VoidInitializer : public Initializer
public:
Type *type; // type that this will initialize to
- VoidInitializer(Loc loc);
- Initializer *syntaxCopy();
-
- virtual VoidInitializer *isVoidInitializer() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
class ErrorInitializer : public Initializer
{
public:
- ErrorInitializer();
- Initializer *syntaxCopy();
-
- virtual ErrorInitializer *isErrorInitializer() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -79,11 +65,6 @@ public:
Identifiers field; // of Identifier *'s
Initializers value; // parallel array of Initializer *'s
- StructInitializer(Loc loc);
- Initializer *syntaxCopy();
- void addInit(Identifier *field, Initializer *value);
-
- StructInitializer *isStructInitializer() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -96,26 +77,40 @@ public:
Type *type; // type that array will be used to initialize
bool sem; // true if semantic() is run
- ArrayInitializer(Loc loc);
- Initializer *syntaxCopy();
- void addInit(Expression *index, Initializer *value);
- bool isAssociativeArray();
+ bool isAssociativeArray() const;
Expression *toAssocArrayLiteral();
- ArrayInitializer *isArrayInitializer() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
class ExpInitializer : public Initializer
{
public:
- Expression *exp;
bool expandTuples;
+ Expression *exp;
+
+ void accept(Visitor *v) { v->visit(this); }
+};
+
+struct Designator
+{
+ Expression *exp;
+ Identifier *ident;
+};
+
+struct DesigInit
+{
+ Designators *designatorList;
+ Initializer *initializer;
+};
- ExpInitializer(Loc loc, Expression *exp);
- Initializer *syntaxCopy();
+class CInitializer : public Initializer
+{
+public:
+ DesigInits initializerList;
+ Type *type; // type that array will be used to initialize
+ bool sem; // true if semantic() is run
- ExpInitializer *isExpInitializer() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
diff --git a/gcc/d/dmd/initsem.c b/gcc/d/dmd/initsem.c
deleted file mode 100644
index c7d1dfe0deb..00000000000
--- a/gcc/d/dmd/initsem.c
+++ /dev/null
@@ -1,914 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "root/checkedint.h"
-#include "mars.h"
-#include "init.h"
-#include "expression.h"
-#include "statement.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "scope.h"
-#include "mtype.h"
-#include "template.h"
-#include "id.h"
-
-FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL);
-Initializer *inferType(Initializer *init, Scope *sc);
-bool hasNonConstPointers(Expression *e);
-
-class InitializerSemanticVisitor : public Visitor
-{
-public:
- Initializer *result;
- Scope *sc;
- Type *t;
- NeedInterpret needInterpret;
-
- InitializerSemanticVisitor(Scope *sc, Type *t, NeedInterpret needInterpret)
- {
- this->result = NULL;
- this->sc = sc;
- this->t = t;
- this->needInterpret = needInterpret;
- }
-
- void visit(ErrorInitializer *i)
- {
- //printf("ErrorInitializer::semantic(t = %p)\n", t);
- result = i;
- }
-
- void visit(VoidInitializer *i)
- {
- //printf("VoidInitializer::semantic(t = %p)\n", t);
- i->type = t;
- result = i;
- }
-
- void visit(StructInitializer *i)
- {
- //printf("StructInitializer::semantic(t = %s) %s\n", t->toChars(), toChars());
- t = t->toBasetype();
- if (t->ty == Tsarray && t->nextOf()->toBasetype()->ty == Tstruct)
- t = t->nextOf()->toBasetype();
- if (t->ty == Tstruct)
- {
- StructDeclaration *sd = ((TypeStruct *)t)->sym;
- if (sd->ctor)
- {
- error(i->loc, "%s %s has constructors, cannot use { initializers }, use %s( initializers ) instead",
- sd->kind(), sd->toChars(), sd->toChars());
- result = new ErrorInitializer();
- return;
- }
- sd->size(i->loc);
- if (sd->sizeok != SIZEOKdone)
- {
- result = new ErrorInitializer();
- return;
- }
- size_t nfields = sd->fields.length - sd->isNested();
-
- //expandTuples for non-identity arguments?
-
- Expressions *elements = new Expressions();
- elements->setDim(nfields);
- for (size_t j = 0; j < elements->length; j++)
- (*elements)[j] = NULL;
-
- // Run semantic for explicitly given initializers
- // TODO: this part is slightly different from StructLiteralExp::semantic.
- bool errors = false;
- for (size_t fieldi = 0, j = 0; j < i->field.length; j++)
- {
- if (Identifier *id = i->field[j])
- {
- Dsymbol *s = sd->search(i->loc, id);
- if (!s)
- {
- s = sd->search_correct(id);
- if (s)
- error(i->loc, "`%s` is not a member of `%s`, did you mean %s `%s`?",
- id->toChars(), sd->toChars(), s->kind(), s->toChars());
- else
- error(i->loc, "`%s` is not a member of `%s`", id->toChars(), sd->toChars());
- result = new ErrorInitializer();
- return;
- }
- s = s->toAlias();
-
- // Find out which field index it is
- for (fieldi = 0; 1; fieldi++)
- {
- if (fieldi >= nfields)
- {
- error(i->loc, "%s.%s is not a per-instance initializable field",
- sd->toChars(), s->toChars());
- result = new ErrorInitializer();
- return;
- }
- if (s == sd->fields[fieldi])
- break;
- }
- }
- else if (fieldi >= nfields)
- {
- error(i->loc, "too many initializers for %s", sd->toChars());
- result = new ErrorInitializer();
- return;
- }
-
- VarDeclaration *vd = sd->fields[fieldi];
- if ((*elements)[fieldi])
- {
- error(i->loc, "duplicate initializer for field `%s`", vd->toChars());
- errors = true;
- continue;
- }
- for (size_t k = 0; k < nfields; k++)
- {
- VarDeclaration *v2 = sd->fields[k];
- if (vd->isOverlappedWith(v2) && (*elements)[k])
- {
- error(i->loc, "overlapping initialization for field %s and %s",
- v2->toChars(), vd->toChars());
- errors = true;
- continue;
- }
- }
-
- assert(sc);
- Initializer *iz = i->value[j];
- iz = initializerSemantic(iz, sc, vd->type->addMod(t->mod), needInterpret);
- Expression *ex = initializerToExpression(iz);
- if (ex->op == TOKerror)
- {
- errors = true;
- continue;
- }
- i->value[j] = iz;
- (*elements)[fieldi] = doCopyOrMove(sc, ex);
- ++fieldi;
- }
- if (errors)
- {
- result = new ErrorInitializer();
- return;
- }
-
- StructLiteralExp *sle = new StructLiteralExp(i->loc, sd, elements, t);
- if (!sd->fill(i->loc, elements, false))
- {
- result = new ErrorInitializer();
- return;
- }
- sle->type = t;
-
- ExpInitializer *ie = new ExpInitializer(i->loc, sle);
- result = initializerSemantic(ie, sc, t, needInterpret);
- return;
- }
- else if ((t->ty == Tdelegate || (t->ty == Tpointer && t->nextOf()->ty == Tfunction)) && i->value.length == 0)
- {
- TOK tok = (t->ty == Tdelegate) ? TOKdelegate : TOKfunction;
- /* Rewrite as empty delegate literal { }
- */
- Type *tf = new TypeFunction(ParameterList(), NULL, LINKd);
- FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(i->loc, Loc(), tf, tok, NULL);
- fd->fbody = new CompoundStatement(i->loc, new Statements());
- fd->endloc = i->loc;
- Expression *e = new FuncExp(i->loc, fd);
- ExpInitializer *ie = new ExpInitializer(i->loc, e);
- result = initializerSemantic(ie, sc, t, needInterpret);
- return;
- }
-
- error(i->loc, "a struct is not a valid initializer for a %s", t->toChars());
- result = new ErrorInitializer();
- }
-
- void visit(ArrayInitializer *i)
- {
- unsigned length;
- const unsigned amax = 0x80000000;
- bool errors = false;
-
- //printf("ArrayInitializer::semantic(%s)\n", t->toChars());
- if (i->sem) // if semantic() already run
- {
- result = i;
- return;
- }
- i->sem = true;
- t = t->toBasetype();
- switch (t->ty)
- {
- case Tsarray:
- case Tarray:
- break;
-
- case Tvector:
- t = ((TypeVector *)t)->basetype;
- break;
-
- case Taarray:
- case Tstruct: // consider implicit constructor call
- {
- Expression *e;
- // note: MyStruct foo = [1:2, 3:4] is correct code if MyStruct has a this(int[int])
- if (t->ty == Taarray || i->isAssociativeArray())
- e = i->toAssocArrayLiteral();
- else
- e = initializerToExpression(i);
- if (!e) // Bugzilla 13987
- {
- error(i->loc, "cannot use array to initialize %s", t->toChars());
- goto Lerr;
- }
- ExpInitializer *ei = new ExpInitializer(e->loc, e);
- result = initializerSemantic(ei, sc, t, needInterpret);
- return;
- }
- case Tpointer:
- if (t->nextOf()->ty != Tfunction)
- break;
- /* fall through */
-
- default:
- error(i->loc, "cannot use array to initialize %s", t->toChars());
- goto Lerr;
- }
-
- i->type = t;
-
- length = 0;
- for (size_t j = 0; j < i->index.length; j++)
- {
- Expression *idx = i->index[j];
- if (idx)
- {
- sc = sc->startCTFE();
- idx = expressionSemantic(idx, sc);
- sc = sc->endCTFE();
- idx = idx->ctfeInterpret();
- i->index[j] = idx;
- const uinteger_t idxvalue = idx->toInteger();
- if (idxvalue >= amax)
- {
- error(i->loc, "array index %llu overflow", (ulonglong)idxvalue);
- errors = true;
- }
- length = (unsigned)idx->toInteger();
- if (idx->op == TOKerror)
- errors = true;
- }
-
- Initializer *val = i->value[j];
- ExpInitializer *ei = val->isExpInitializer();
- if (ei && !idx)
- ei->expandTuples = true;
- val = initializerSemantic(val, sc, t->nextOf(), needInterpret);
- if (val->isErrorInitializer())
- errors = true;
-
- ei = val->isExpInitializer();
- // found a tuple, expand it
- if (ei && ei->exp->op == TOKtuple)
- {
- TupleExp *te = (TupleExp *)ei->exp;
- i->index.remove(j);
- i->value.remove(j);
-
- for (size_t k = 0; k < te->exps->length; ++k)
- {
- Expression *e = (*te->exps)[k];
- i->index.insert(j + k, (Expression *)NULL);
- i->value.insert(j + k, new ExpInitializer(e->loc, e));
- }
- j--;
- continue;
- }
- else
- {
- i->value[j] = val;
- }
-
- length++;
- if (length == 0)
- {
- error(i->loc, "array dimension overflow");
- goto Lerr;
- }
- if (length > i->dim)
- i->dim = length;
- }
- if (t->ty == Tsarray)
- {
- uinteger_t edim = ((TypeSArray *)t)->dim->toInteger();
- if (i->dim > edim)
- {
- error(i->loc, "array initializer has %u elements, but array length is %llu", i->dim, (ulonglong)edim);
- goto Lerr;
- }
- }
- if (errors)
- goto Lerr;
- else
- {
- d_uns64 sz = t->nextOf()->size();
- bool overflow = false;
- const d_uns64 max = mulu((d_uns64)i->dim, sz, overflow);
- if (overflow || max > amax)
- {
- error(i->loc, "array dimension %llu exceeds max of %llu", (ulonglong)i->dim, (ulonglong)(amax / sz));
- goto Lerr;
- }
- result = i;
- return;
- }
-
- Lerr:
- result = new ErrorInitializer();
- }
-
- void visit(ExpInitializer *i)
- {
- //printf("ExpInitializer::semantic(%s), type = %s\n", i->exp->toChars(), t->toChars());
- if (needInterpret) sc = sc->startCTFE();
- i->exp = expressionSemantic(i->exp, sc);
- i->exp = resolveProperties(sc, i->exp);
- if (needInterpret) sc = sc->endCTFE();
- if (i->exp->op == TOKerror)
- {
- result = new ErrorInitializer();
- return;
- }
-
- unsigned int olderrors = global.errors;
- if (needInterpret)
- {
- // If the result will be implicitly cast, move the cast into CTFE
- // to avoid premature truncation of polysemous types.
- // eg real [] x = [1.1, 2.2]; should use real precision.
- if (i->exp->implicitConvTo(t))
- {
- i->exp = i->exp->implicitCastTo(sc, t);
- }
- if (!global.gag && olderrors != global.errors)
- {
- result = i;
- return;
- }
- i->exp = i->exp->ctfeInterpret();
- }
- else
- {
- i->exp = i->exp->optimize(WANTvalue);
- }
- if (!global.gag && olderrors != global.errors)
- {
- result = i; // Failed, suppress duplicate error messages
- return;
- }
-
- if (i->exp->type->ty == Ttuple && ((TypeTuple *)i->exp->type)->arguments->length == 0)
- {
- Type *et = i->exp->type;
- i->exp = new TupleExp(i->exp->loc, new Expressions());
- i->exp->type = et;
- }
- if (i->exp->op == TOKtype)
- {
- i->exp->error("initializer must be an expression, not `%s`", i->exp->toChars());
- result = new ErrorInitializer();
- return;
- }
-
- // Make sure all pointers are constants
- if (needInterpret && hasNonConstPointers(i->exp))
- {
- i->exp->error("cannot use non-constant CTFE pointer in an initializer `%s`", i->exp->toChars());
- result = new ErrorInitializer();
- return;
- }
-
- Type *tb = t->toBasetype();
- Type *ti = i->exp->type->toBasetype();
-
- if (i->exp->op == TOKtuple && i->expandTuples && !i->exp->implicitConvTo(t))
- {
- result = new ExpInitializer(i->loc, i->exp);
- return;
- }
-
- /* Look for case of initializing a static array with a too-short
- * string literal, such as:
- * char[5] foo = "abc";
- * Allow this by doing an explicit cast, which will lengthen the string
- * literal.
- */
- if (i->exp->op == TOKstring && tb->ty == Tsarray)
- {
- StringExp *se = (StringExp *)i->exp;
- Type *typeb = se->type->toBasetype();
- TY tynto = tb->nextOf()->ty;
- if (!se->committed &&
- (typeb->ty == Tarray || typeb->ty == Tsarray) &&
- (tynto == Tchar || tynto == Twchar || tynto == Tdchar) &&
- se->numberOfCodeUnits(tynto) < ((TypeSArray *)tb)->dim->toInteger())
- {
- i->exp = se->castTo(sc, t);
- goto L1;
- }
- }
-
- // Look for implicit constructor call
- if (tb->ty == Tstruct &&
- !(ti->ty == Tstruct && tb->toDsymbol(sc) == ti->toDsymbol(sc)) &&
- !i->exp->implicitConvTo(t))
- {
- StructDeclaration *sd = ((TypeStruct *)tb)->sym;
- if (sd->ctor)
- {
- // Rewrite as S().ctor(exp)
- Expression *e;
- e = new StructLiteralExp(i->loc, sd, NULL);
- e = new DotIdExp(i->loc, e, Id::ctor);
- e = new CallExp(i->loc, e, i->exp);
- e = expressionSemantic(e, sc);
- if (needInterpret)
- i->exp = e->ctfeInterpret();
- else
- i->exp = e->optimize(WANTvalue);
- }
- }
-
- // Look for the case of statically initializing an array
- // with a single member.
- if (tb->ty == Tsarray &&
- !tb->nextOf()->equals(ti->toBasetype()->nextOf()) &&
- i->exp->implicitConvTo(tb->nextOf())
- )
- {
- /* If the variable is not actually used in compile time, array creation is
- * redundant. So delay it until invocation of toExpression() or toDt().
- */
- t = tb->nextOf();
- }
-
- if (i->exp->implicitConvTo(t))
- {
- i->exp = i->exp->implicitCastTo(sc, t);
- }
- else
- {
- // Look for mismatch of compile-time known length to emit
- // better diagnostic message, as same as AssignExp::semantic.
- if (tb->ty == Tsarray &&
- i->exp->implicitConvTo(tb->nextOf()->arrayOf()) > MATCHnomatch)
- {
- uinteger_t dim1 = ((TypeSArray *)tb)->dim->toInteger();
- uinteger_t dim2 = dim1;
- if (i->exp->op == TOKarrayliteral)
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)i->exp;
- dim2 = ale->elements ? ale->elements->length : 0;
- }
- else if (i->exp->op == TOKslice)
- {
- Type *tx = toStaticArrayType((SliceExp *)i->exp);
- if (tx)
- dim2 = ((TypeSArray *)tx)->dim->toInteger();
- }
- if (dim1 != dim2)
- {
- i->exp->error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2);
- i->exp = new ErrorExp();
- }
- }
- i->exp = i->exp->implicitCastTo(sc, t);
- }
- L1:
- if (i->exp->op == TOKerror)
- {
- result = i;
- return;
- }
- if (needInterpret)
- i->exp = i->exp->ctfeInterpret();
- else
- i->exp = i->exp->optimize(WANTvalue);
- //printf("-ExpInitializer::semantic(): "); i->exp->print();
- result = i;
- }
-};
-
-// Performs semantic analisys on Initializer AST nodes
-Initializer *initializerSemantic(Initializer *init, Scope *sc, Type *t, NeedInterpret needInterpret)
-{
- InitializerSemanticVisitor v = InitializerSemanticVisitor(sc, t, needInterpret);
- init->accept(&v);
- return v.result;
-}
-
-class InferTypeVisitor : public Visitor
-{
-public:
- Initializer *result;
- Scope *sc;
-
- InferTypeVisitor(Scope *sc)
- {
- this->result = NULL;
- this->sc = sc;
- }
-
- void visit(ErrorInitializer *i)
- {
- result = i;
- }
-
- void visit(VoidInitializer *i)
- {
- error(i->loc, "cannot infer type from void initializer");
- result = new ErrorInitializer();
- }
-
- void visit(StructInitializer *i)
- {
- error(i->loc, "cannot infer type from struct initializer");
- result = new ErrorInitializer();
- }
-
- void visit(ArrayInitializer *init)
- {
- //printf("ArrayInitializer::inferType() %s\n", init->toChars());
- Expressions *keys = NULL;
- Expressions *values;
- if (init->isAssociativeArray())
- {
- keys = new Expressions();
- keys->setDim(init->value.length);
- values = new Expressions();
- values->setDim(init->value.length);
-
- for (size_t i = 0; i < init->value.length; i++)
- {
- Expression *e = init->index[i];
- if (!e)
- goto Lno;
- (*keys)[i] = e;
-
- Initializer *iz = init->value[i];
- if (!iz)
- goto Lno;
- iz = inferType(iz, sc);
- if (iz->isErrorInitializer())
- {
- result = iz;
- return;
- }
- assert(iz->isExpInitializer());
- (*values)[i] = ((ExpInitializer *)iz)->exp;
- assert((*values)[i]->op != TOKerror);
- }
-
- Expression *e = new AssocArrayLiteralExp(init->loc, keys, values);
- ExpInitializer *ei = new ExpInitializer(init->loc, e);
- result = inferType(ei, sc);
- return;
- }
- else
- {
- Expressions *elements = new Expressions();
- elements->setDim(init->value.length);
- elements->zero();
-
- for (size_t i = 0; i < init->value.length; i++)
- {
- assert(!init->index[i]); // already asserted by isAssociativeArray()
-
- Initializer *iz = init->value[i];
- if (!iz)
- goto Lno;
- iz = inferType(iz, sc);
- if (iz->isErrorInitializer())
- {
- result = iz;
- return;
- }
- assert(iz->isExpInitializer());
- (*elements)[i] = ((ExpInitializer *)iz)->exp;
- assert((*elements)[i]->op != TOKerror);
- }
-
- Expression *e = new ArrayLiteralExp(init->loc, NULL, elements);
- ExpInitializer *ei = new ExpInitializer(init->loc, e);
- result = inferType(ei, sc);
- return;
- }
- Lno:
- if (keys)
- {
- delete keys;
- delete values;
- error(init->loc, "not an associative array initializer");
- }
- else
- {
- error(init->loc, "cannot infer type from array initializer");
- }
- result = new ErrorInitializer();
- }
-
- void visit(ExpInitializer *init)
- {
- //printf("ExpInitializer::inferType() %s\n", init->toChars());
- init->exp = expressionSemantic(init->exp, sc);
- init->exp = resolveProperties(sc, init->exp);
-
- if (init->exp->op == TOKscope)
- {
- ScopeExp *se = (ScopeExp *)init->exp;
- TemplateInstance *ti = se->sds->isTemplateInstance();
- if (ti && ti->semanticRun == PASSsemantic && !ti->aliasdecl)
- se->error("cannot infer type from %s %s, possible circular dependency", se->sds->kind(), se->toChars());
- else
- se->error("cannot infer type from %s %s", se->sds->kind(), se->toChars());
- result = new ErrorInitializer();
- return;
- }
-
- // Give error for overloaded function addresses
- bool hasOverloads = false;
- if (FuncDeclaration *f = isFuncAddress(init->exp, &hasOverloads))
- {
- if (f->checkForwardRef(init->loc))
- {
- result = new ErrorInitializer();
- return;
- }
-
- if (hasOverloads && !f->isUnique())
- {
- init->exp->error("cannot infer type from overloaded function symbol %s", init->exp->toChars());
- result = new ErrorInitializer();
- return;
- }
- }
- if (init->exp->op == TOKaddress)
- {
- AddrExp *ae = (AddrExp *)init->exp;
- if (ae->e1->op == TOKoverloadset)
- {
- init->exp->error("cannot infer type from overloaded function symbol %s", init->exp->toChars());
- result = new ErrorInitializer();
- return;
- }
- }
-
- if (init->exp->op == TOKerror)
- {
- result = new ErrorInitializer();
- return;
- }
- if (!init->exp->type)
- {
- result = new ErrorInitializer();
- return;
- }
- result = init;
- }
-};
-
-/* Translates to an expression to infer type.
- * Returns ExpInitializer or ErrorInitializer.
- */
-Initializer *inferType(Initializer *init, Scope *sc)
-{
- InferTypeVisitor v = InferTypeVisitor(sc);
- init->accept(&v);
- return v.result;
-}
-
-class InitToExpressionVisitor : public Visitor
-{
-public:
- Expression *result;
- Type *itype;
-
- InitToExpressionVisitor(Type *itype)
- {
- this->result = NULL;
- this->itype = itype;
- }
-
- void visit(ErrorInitializer *)
- {
- result = new ErrorExp();
- }
-
- void visit(VoidInitializer *)
- {
- result = NULL;
- }
-
- /***************************************
- * This works by transforming a struct initializer into
- * a struct literal. In the future, the two should be the
- * same thing.
- */
- void visit(StructInitializer *)
- {
- // cannot convert to an expression without target 'ad'
- result = NULL;
- }
-
- /********************************
- * If possible, convert array initializer to array literal.
- * Otherwise return NULL.
- */
-
- void visit(ArrayInitializer *init)
- {
- //printf("ArrayInitializer::toExpression(), dim = %d\n", init->length);
- //static int i; if (++i == 2) halt();
-
- Expressions *elements;
- unsigned edim;
- const unsigned amax = 0x80000000;
- Type *t = NULL;
- if (init->type)
- {
- if (init->type == Type::terror)
- {
- result = new ErrorExp();
- return;
- }
-
- t = init->type->toBasetype();
- switch (t->ty)
- {
- case Tvector:
- t = ((TypeVector *)t)->basetype;
- /* fall through */
-
- case Tsarray:
- {
- uinteger_t adim = ((TypeSArray *)t)->dim->toInteger();
- if (adim >= amax)
- goto Lno;
- edim = (unsigned)adim;
- break;
- }
-
- case Tpointer:
- case Tarray:
- edim = init->dim;
- break;
-
- default:
- assert(0);
- }
- }
- else
- {
- edim = (unsigned)init->value.length;
- for (size_t i = 0, j = 0; i < init->value.length; i++, j++)
- {
- if (init->index[i])
- {
- if (init->index[i]->op == TOKint64)
- {
- const uinteger_t idxval = init->index[i]->toInteger();
- if (idxval >= amax)
- goto Lno;
- j = (size_t)idxval;
- }
- else
- goto Lno;
- }
- if (j >= edim)
- edim = (unsigned)(j + 1);
- }
- }
-
- elements = new Expressions();
- elements->setDim(edim);
- elements->zero();
- for (size_t i = 0, j = 0; i < init->value.length; i++, j++)
- {
- if (init->index[i])
- j = (size_t)(init->index[i])->toInteger();
- assert(j < edim);
- Initializer *iz = init->value[i];
- if (!iz)
- goto Lno;
- Expression *ex = initializerToExpression(iz);
- if (!ex)
- {
- goto Lno;
- }
- (*elements)[j] = ex;
- }
-
- /* Fill in any missing elements with the default initializer
- */
- {
- Expression *_init = NULL;
- for (size_t i = 0; i < edim; i++)
- {
- if (!(*elements)[i])
- {
- if (!init->type)
- goto Lno;
- if (!_init)
- _init = ((TypeNext *)t)->next->defaultInit();
- (*elements)[i] = _init;
- }
- }
-
- /* Expand any static array initializers that are a single expression
- * into an array of them
- */
- if (t)
- {
- Type *tn = t->nextOf()->toBasetype();
- if (tn->ty == Tsarray)
- {
- size_t dim = ((TypeSArray *)tn)->dim->toInteger();
- Type *te = tn->nextOf()->toBasetype();
- for (size_t i = 0; i < elements->length; i++)
- {
- Expression *e = (*elements)[i];
- if (te->equals(e->type))
- {
- Expressions *elements2 = new Expressions();
- elements2->setDim(dim);
- for (size_t j = 0; j < dim; j++)
- (*elements2)[j] = e;
- e = new ArrayLiteralExp(e->loc, tn, elements2);
- (*elements)[i] = e;
- }
- }
- }
- }
-
- /* If any elements are errors, then the whole thing is an error
- */
- for (size_t i = 0; i < edim; i++)
- {
- Expression *e = (*elements)[i];
- if (e->op == TOKerror)
- {
- result = e;
- return;
- }
- }
-
- Expression *e = new ArrayLiteralExp(init->loc, init->type, elements);
- result = e;
- return;
- }
-
- Lno:
- result = NULL;
- }
-
- void visit(ExpInitializer *i)
- {
- if (itype)
- {
- //printf("ExpInitializer::toExpression(t = %s) exp = %s\n", itype->toChars(), i->exp->toChars());
- Type *tb = itype->toBasetype();
- Expression *e = (i->exp->op == TOKconstruct || i->exp->op == TOKblit) ? ((AssignExp *)i->exp)->e2 : i->exp;
- if (tb->ty == Tsarray && e->implicitConvTo(tb->nextOf()))
- {
- TypeSArray *tsa = (TypeSArray *)tb;
- size_t d = (size_t)tsa->dim->toInteger();
- Expressions *elements = new Expressions();
- elements->setDim(d);
- for (size_t j = 0; j < d; j++)
- (*elements)[j] = e;
- ArrayLiteralExp *ae = new ArrayLiteralExp(e->loc, itype, elements);
- result = ae;
- return;
- }
- }
- result = i->exp;
- }
-};
-
-Expression *initializerToExpression(Initializer *i, Type *t)
-{
- InitToExpressionVisitor v = InitToExpressionVisitor(t);
- i->accept(&v);
- return v.result;
-}
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
new file mode 100644
index 00000000000..ae8bde2ff57
--- /dev/null
+++ b/gcc/d/dmd/initsem.d
@@ -0,0 +1,1268 @@
+/**
+ * Semantic analysis of initializers.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/initsem.d, _initsem.d)
+ * Documentation: https://dlang.org/phobos/dmd_initsem.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/initsem.d
+ */
+
+module dmd.initsem;
+
+import core.stdc.stdio;
+import core.checkedint;
+
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.dcast;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.opover;
+import dmd.statement;
+import dmd.target;
+import dmd.tokens;
+import dmd.typesem;
+
+/********************************
+ * If possible, convert array initializer to associative array initializer.
+ *
+ * Params:
+ * ai = array initializer to be converted
+ *
+ * Returns:
+ * The converted associative array initializer or ErrorExp if `ai`
+ * is not an associative array initializer.
+ */
+Expression toAssocArrayLiteral(ArrayInitializer ai)
+{
+ Expression e;
+ //printf("ArrayInitializer::toAssocArrayInitializer()\n");
+ //static int i; if (++i == 2) assert(0);
+ const dim = ai.value.dim;
+ auto keys = new Expressions(dim);
+ auto values = new Expressions(dim);
+ for (size_t i = 0; i < dim; i++)
+ {
+ e = ai.index[i];
+ if (!e)
+ goto Lno;
+ (*keys)[i] = e;
+ Initializer iz = ai.value[i];
+ if (!iz)
+ goto Lno;
+ e = iz.initializerToExpression();
+ if (!e)
+ goto Lno;
+ (*values)[i] = e;
+ }
+ e = new AssocArrayLiteralExp(ai.loc, keys, values);
+ return e;
+Lno:
+ error(ai.loc, "not an associative array initializer");
+ return ErrorExp.get();
+}
+
+/******************************************
+ * Perform semantic analysis on init.
+ * Params:
+ * init = Initializer AST node
+ * sc = context
+ * tx = type that the initializer needs to become. If tx is an incomplete
+ * type and the initializer completes it, it is updated to be the
+ * complete type. ImportC has incomplete types
+ * needInterpret = if CTFE needs to be run on this,
+ * such as if it is the initializer for a const declaration
+ * Returns:
+ * `Initializer` with completed semantic analysis, `ErrorInitializer` if errors
+ * were encountered
+ */
+extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedInterpret needInterpret)
+{
+ Type t = tx;
+
+ static Initializer err()
+ {
+ return new ErrorInitializer();
+ }
+
+ Initializer visitVoid(VoidInitializer i)
+ {
+ i.type = t;
+ return i;
+ }
+
+ Initializer visitError(ErrorInitializer i)
+ {
+ return i;
+ }
+
+ Initializer visitStruct(StructInitializer i)
+ {
+ //printf("StructInitializer::semantic(t = %s) %s\n", t.toChars(), i.toChars());
+ /* This works by replacing the StructInitializer with an ExpInitializer.
+ */
+ t = t.toBasetype();
+ if (t.ty == Tsarray && t.nextOf().toBasetype().ty == Tstruct)
+ t = t.nextOf().toBasetype();
+ if (auto ts = t.isTypeStruct())
+ {
+ StructDeclaration sd = ts.sym;
+ // check if the sd has a regular ctor (user defined non-copy ctor)
+ // that is not disabled.
+ if (sd.hasRegularCtor(true))
+ {
+ error(i.loc, "%s `%s` has constructors, cannot use `{ initializers }`, use `%s( initializers )` instead", sd.kind(), sd.toChars(), sd.toChars());
+ return err();
+ }
+ sd.size(i.loc);
+ if (sd.sizeok != Sizeok.done)
+ return err();
+ const nfields = sd.nonHiddenFields();
+ //expandTuples for non-identity arguments?
+ auto elements = new Expressions(nfields);
+ auto elems = (*elements)[];
+ foreach (ref elem; elems)
+ elem = null;
+
+ // Run semantic for explicitly given initializers
+ // TODO: this part is slightly different from StructLiteralExp::semantic.
+ bool errors = false;
+ size_t fieldi = 0;
+ foreach (j, id; i.field[])
+ {
+ if (id)
+ {
+ /* Determine `fieldi` that `id` matches
+ */
+ Dsymbol s = sd.search(i.loc, id);
+ if (!s)
+ {
+ s = sd.search_correct(id);
+ const initLoc = i.value[j].loc;
+ if (s)
+ error(initLoc, "`%s` is not a member of `%s`, did you mean %s `%s`?", id.toChars(), sd.toChars(), s.kind(), s.toChars());
+ else
+ error(initLoc, "`%s` is not a member of `%s`", id.toChars(), sd.toChars());
+ return err();
+ }
+ s.checkDeprecated(i.loc, sc);
+ s = s.toAlias();
+
+ // Find out which field index `s` is
+ for (fieldi = 0; 1; fieldi++)
+ {
+ if (fieldi >= nfields)
+ {
+ error(i.loc, "`%s.%s` is not a per-instance initializable field", sd.toChars(), s.toChars());
+ return err();
+ }
+ if (s == sd.fields[fieldi])
+ break;
+ }
+ }
+ else if (fieldi >= nfields)
+ {
+ error(i.loc, "too many initializers for `%s`", sd.toChars());
+ return err();
+ }
+
+ VarDeclaration vd = sd.fields[fieldi];
+ if (elems[fieldi])
+ {
+ error(i.loc, "duplicate initializer for field `%s`", vd.toChars());
+ errors = true;
+ continue;
+ }
+
+ // Check for @safe violations
+ if (vd.type.hasPointers)
+ {
+ if ((t.alignment() < target.ptrsize ||
+ (vd.offset & (target.ptrsize - 1))) &&
+ sc.func && sc.func.setUnsafe())
+ {
+ error(i.loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code",
+ sd.toChars(), vd.toChars());
+ errors = true;
+ }
+ }
+
+ // Check for overlapping initializations (can happen with unions)
+ foreach (k, v2; sd.fields[0 .. nfields])
+ {
+ if (vd.isOverlappedWith(v2) && elems[k])
+ {
+ error(i.loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
+ errors = true;
+ continue;
+ }
+ }
+
+ // Convert initializer to Expression `ex`
+ assert(sc);
+ auto tm = vd.type.addMod(t.mod);
+ auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret);
+ auto ex = iz.initializerToExpression();
+ if (ex.op == TOK.error)
+ {
+ errors = true;
+ continue;
+ }
+
+ i.value[j] = iz;
+ elems[fieldi] = doCopyOrMove(sc, ex);
+ ++fieldi;
+ }
+ if (errors)
+ return err();
+
+ // Make a StructLiteralExp out of elements[]
+ auto sle = new StructLiteralExp(i.loc, sd, elements, t);
+ if (!sd.fill(i.loc, elements, false))
+ return err();
+ sle.type = t;
+ auto ie = new ExpInitializer(i.loc, sle);
+ return ie.initializerSemantic(sc, t, needInterpret);
+ }
+ else if ((t.ty == Tdelegate || t.isPtrToFunction()) && i.value.dim == 0)
+ {
+ const tok = (t.ty == Tdelegate) ? TOK.delegate_ : TOK.function_;
+ /* Rewrite as empty delegate literal { }
+ */
+ Type tf = new TypeFunction(ParameterList(), null, LINK.d);
+ auto fd = new FuncLiteralDeclaration(i.loc, Loc.initial, tf, tok, null);
+ fd.fbody = new CompoundStatement(i.loc, new Statements());
+ fd.endloc = i.loc;
+ Expression e = new FuncExp(i.loc, fd);
+ auto ie = new ExpInitializer(i.loc, e);
+ return ie.initializerSemantic(sc, t, needInterpret);
+ }
+ if (t.ty != Terror)
+ error(i.loc, "a struct is not a valid initializer for a `%s`", t.toChars());
+ return err();
+ }
+
+ Initializer visitArray(ArrayInitializer i)
+ {
+ uint length;
+ const(uint) amax = 0x80000000;
+ bool errors = false;
+ //printf("ArrayInitializer::semantic(%s)\n", t.toChars());
+ if (i.sem) // if semantic() already run
+ {
+ return i;
+ }
+ i.sem = true;
+ t = t.toBasetype();
+ switch (t.ty)
+ {
+ case Tsarray:
+ case Tarray:
+ break;
+ case Tvector:
+ t = (cast(TypeVector)t).basetype;
+ break;
+ case Taarray:
+ case Tstruct: // consider implicit constructor call
+ {
+ Expression e;
+ // note: MyStruct foo = [1:2, 3:4] is correct code if MyStruct has a this(int[int])
+ if (t.ty == Taarray || i.isAssociativeArray())
+ e = i.toAssocArrayLiteral();
+ else
+ e = i.initializerToExpression();
+ // Bugzilla 13987
+ if (!e)
+ {
+ error(i.loc, "cannot use array to initialize `%s`", t.toChars());
+ return err();
+ }
+ auto ei = new ExpInitializer(e.loc, e);
+ return ei.initializerSemantic(sc, t, needInterpret);
+ }
+ case Tpointer:
+ if (t.nextOf().ty != Tfunction)
+ break;
+ goto default;
+ default:
+ error(i.loc, "cannot use array to initialize `%s`", t.toChars());
+ return err();
+ }
+ i.type = t;
+ length = 0;
+ for (size_t j = 0; j < i.index.dim; j++)
+ {
+ Expression idx = i.index[j];
+ if (idx)
+ {
+ sc = sc.startCTFE();
+ idx = idx.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ idx = idx.ctfeInterpret();
+ i.index[j] = idx;
+ const uinteger_t idxvalue = idx.toInteger();
+ if (idxvalue >= amax)
+ {
+ error(i.loc, "array index %llu overflow", idxvalue);
+ errors = true;
+ }
+ length = cast(uint)idxvalue;
+ if (idx.op == TOK.error)
+ errors = true;
+ }
+ Initializer val = i.value[j];
+ ExpInitializer ei = val.isExpInitializer();
+ if (ei && !idx)
+ ei.expandTuples = true;
+ auto tn = t.nextOf();
+ val = val.initializerSemantic(sc, tn, needInterpret);
+ if (val.isErrorInitializer())
+ errors = true;
+ ei = val.isExpInitializer();
+ // found a tuple, expand it
+ if (ei && ei.exp.op == TOK.tuple)
+ {
+ TupleExp te = cast(TupleExp)ei.exp;
+ i.index.remove(j);
+ i.value.remove(j);
+ for (size_t k = 0; k < te.exps.dim; ++k)
+ {
+ Expression e = (*te.exps)[k];
+ i.index.insert(j + k, cast(Expression)null);
+ i.value.insert(j + k, new ExpInitializer(e.loc, e));
+ }
+ j--;
+ continue;
+ }
+ else
+ {
+ i.value[j] = val;
+ }
+ length++;
+ if (length == 0)
+ {
+ error(i.loc, "array dimension overflow");
+ return err();
+ }
+ if (length > i.dim)
+ i.dim = length;
+ }
+ if (t.ty == Tsarray)
+ {
+ uinteger_t edim = (cast(TypeSArray)t).dim.toInteger();
+ if (i.dim > edim)
+ {
+ error(i.loc, "array initializer has %u elements, but array length is %llu", i.dim, edim);
+ return err();
+ }
+ }
+ if (errors)
+ return err();
+
+ const sz = t.nextOf().size();
+ bool overflow;
+ const max = mulu(i.dim, sz, overflow);
+ if (overflow || max >= amax)
+ {
+ error(i.loc, "array dimension %llu exceeds max of %llu", ulong(i.dim), ulong(amax / sz));
+ return err();
+ }
+ return i;
+ }
+
+ Initializer visitExp(ExpInitializer i)
+ {
+ //printf("ExpInitializer::semantic(%s), type = %s\n", i.exp.toChars(), t.toChars());
+ if (needInterpret)
+ sc = sc.startCTFE();
+ i.exp = i.exp.expressionSemantic(sc);
+ i.exp = resolveProperties(sc, i.exp);
+ if (needInterpret)
+ sc = sc.endCTFE();
+ if (i.exp.op == TOK.error)
+ return err();
+ uint olderrors = global.errors;
+ /* Save the expression before ctfe
+ * Otherwise the error message would contain for example "&[0][0]" instead of "new int"
+ * Regression: https://issues.dlang.org/show_bug.cgi?id=21687
+ */
+ Expression currExp = i.exp;
+ if (needInterpret)
+ {
+ // If the result will be implicitly cast, move the cast into CTFE
+ // to avoid premature truncation of polysemous types.
+ // eg real [] x = [1.1, 2.2]; should use real precision.
+ if (i.exp.implicitConvTo(t))
+ {
+ i.exp = i.exp.implicitCastTo(sc, t);
+ }
+ if (!global.gag && olderrors != global.errors)
+ {
+ return i;
+ }
+ i.exp = i.exp.ctfeInterpret();
+ if (i.exp.op == TOK.voidExpression)
+ error(i.loc, "variables cannot be initialized with an expression of type `void`. Use `void` initialization instead.");
+ }
+ else
+ {
+ i.exp = i.exp.optimize(WANTvalue);
+ }
+ if (!global.gag && olderrors != global.errors)
+ {
+ return i; // Failed, suppress duplicate error messages
+ }
+ if (i.exp.type.ty == Ttuple && (cast(TypeTuple)i.exp.type).arguments.dim == 0)
+ {
+ Type et = i.exp.type;
+ i.exp = new TupleExp(i.exp.loc, new Expressions());
+ i.exp.type = et;
+ }
+ if (i.exp.op == TOK.type)
+ {
+ i.exp.error("initializer must be an expression, not `%s`", i.exp.toChars());
+ return err();
+ }
+ // Make sure all pointers are constants
+ if (needInterpret && hasNonConstPointers(i.exp))
+ {
+ i.exp.error("cannot use non-constant CTFE pointer in an initializer `%s`", currExp.toChars());
+ return err();
+ }
+ Type tb = t.toBasetype();
+ Type ti = i.exp.type.toBasetype();
+ if (i.exp.op == TOK.tuple && i.expandTuples && !i.exp.implicitConvTo(t))
+ {
+ return new ExpInitializer(i.loc, i.exp);
+ }
+ /* Look for case of initializing a static array with a too-short
+ * string literal, such as:
+ * char[5] foo = "abc";
+ * Allow this by doing an explicit cast, which will lengthen the string
+ * literal.
+ */
+ if (i.exp.op == TOK.string_ && tb.ty == Tsarray)
+ {
+ StringExp se = cast(StringExp)i.exp;
+ Type typeb = se.type.toBasetype();
+ TY tynto = tb.nextOf().ty;
+ if (!se.committed &&
+ (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar &&
+ se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger())
+ {
+ i.exp = se.castTo(sc, t);
+ goto L1;
+ }
+ }
+
+ /* C11 6.7.9-14..15
+ * Initialize an array of unknown size with a string.
+ * ImportC regards Tarray as an array of unknown size.
+ * Change to static array of known size
+ */
+ if (sc.flags & SCOPE.Cfile && i.exp.op == TOK.string_ && tb.ty == Tarray)
+ {
+ StringExp se = i.exp.isStringExp();
+ auto ts = new TypeSArray(tb.nextOf(), new IntegerExp(Loc.initial, se.len + 1, Type.tsize_t));
+ t = typeSemantic(ts, Loc.initial, sc);
+ i.exp.type = t;
+ tx = t;
+ }
+
+ // Look for implicit constructor call
+ if (tb.ty == Tstruct && !(ti.ty == Tstruct && tb.toDsymbol(sc) == ti.toDsymbol(sc)) && !i.exp.implicitConvTo(t))
+ {
+ StructDeclaration sd = (cast(TypeStruct)tb).sym;
+ if (sd.ctor)
+ {
+ // Rewrite as S().ctor(exp)
+ Expression e;
+ e = new StructLiteralExp(i.loc, sd, null);
+ e = new DotIdExp(i.loc, e, Id.ctor);
+ e = new CallExp(i.loc, e, i.exp);
+ e = e.expressionSemantic(sc);
+ if (needInterpret)
+ i.exp = e.ctfeInterpret();
+ else
+ i.exp = e.optimize(WANTvalue);
+ }
+ else if (search_function(sd, Id.call))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=1547
+ *
+ * Look for static opCall
+ *
+ * Rewrite as:
+ * i.exp = typeof(sd).opCall(arguments)
+ */
+
+ Expression e = typeDotIdExp(i.loc, sd.type, Id.call);
+ e = new CallExp(i.loc, e, i.exp);
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ if (needInterpret)
+ i.exp = e.ctfeInterpret();
+ else
+ i.exp = e.optimize(WANTvalue);
+ }
+ }
+ // Look for the case of statically initializing an array
+ // with a single member.
+ if (tb.ty == Tsarray && !tb.nextOf().equals(ti.toBasetype().nextOf()) && i.exp.implicitConvTo(tb.nextOf()))
+ {
+ /* If the variable is not actually used in compile time, array creation is
+ * redundant. So delay it until invocation of toExpression() or toDt().
+ */
+ t = tb.nextOf();
+ }
+ if (i.exp.implicitConvTo(t))
+ {
+ i.exp = i.exp.implicitCastTo(sc, t);
+ }
+ else
+ {
+ // Look for mismatch of compile-time known length to emit
+ // better diagnostic message, as same as AssignExp::semantic.
+ if (tb.ty == Tsarray && i.exp.implicitConvTo(tb.nextOf().arrayOf()) > MATCH.nomatch)
+ {
+ uinteger_t dim1 = (cast(TypeSArray)tb).dim.toInteger();
+ uinteger_t dim2 = dim1;
+ if (i.exp.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp ale = cast(ArrayLiteralExp)i.exp;
+ dim2 = ale.elements ? ale.elements.dim : 0;
+ }
+ else if (i.exp.op == TOK.slice)
+ {
+ Type tx = toStaticArrayType(cast(SliceExp)i.exp);
+ if (tx)
+ dim2 = (cast(TypeSArray)tx).dim.toInteger();
+ }
+ if (dim1 != dim2)
+ {
+ i.exp.error("mismatched array lengths, %d and %d", cast(int)dim1, cast(int)dim2);
+ i.exp = ErrorExp.get();
+ }
+ }
+ i.exp = i.exp.implicitCastTo(sc, t);
+ }
+ L1:
+ if (i.exp.op == TOK.error)
+ {
+ return i;
+ }
+ if (needInterpret)
+ i.exp = i.exp.ctfeInterpret();
+ else
+ i.exp = i.exp.optimize(WANTvalue);
+ //printf("-ExpInitializer::semantic(): "); i.exp.print();
+ return i;
+ }
+
+ Initializer visitC(CInitializer ci)
+ {
+ if (ci.sem) // if semantic() already run
+ return ci;
+ //printf("CInitializer::semantic() (%s) %s\n", t.toChars(), ci.toChars());
+ ci.sem = true;
+ t = t.toBasetype();
+ ci.type = t; // later passes will need this
+
+ auto dil = ci.initializerList[];
+ size_t i = 0; // index into dil[]
+ const uint amax = 0x8000_0000;
+ bool errors;
+
+ /* If `{ expression }` return the expression initializer
+ */
+ ExpInitializer isBraceExpression()
+ {
+ return (dil.length == 1 && !dil[0].designatorList)
+ ? dil[0].initializer.isExpInitializer()
+ : null;
+ }
+
+ /* Convert struct initializer into ExpInitializer
+ */
+ Initializer structs(TypeStruct ts)
+ {
+ //printf("structs %s\n", ts.toChars());
+ StructDeclaration sd = ts.sym;
+ sd.size(ci.loc);
+ if (sd.sizeok != Sizeok.done)
+ {
+ errors = true;
+ return err();
+ }
+ const nfields = sd.nonHiddenFields();
+ auto elements = new Expressions(nfields);
+ auto elems = (*elements)[];
+ foreach (ref elem; elems)
+ elem = null;
+
+ FieldLoop:
+ for (size_t fieldi = 0; fieldi < nfields; ++fieldi)
+ {
+ if (i == dil.length)
+ break;
+
+ auto di = dil[i];
+ if (di.designatorList)
+ {
+ error(ci.loc, "C designator-list not supported yet");
+ errors = true;
+ break;
+ }
+
+ VarDeclaration vd = sd.fields[fieldi];
+
+ // Check for overlapping initializations (can happen with unions)
+ foreach (k, v2; sd.fields[0 .. nfields])
+ {
+ if (vd.isOverlappedWith(v2) && elems[k])
+ {
+ continue FieldLoop; // skip it
+ }
+ }
+
+ ++i;
+
+ // Convert initializer to Expression `ex`
+ assert(sc);
+ auto tm = vd.type.addMod(ts.mod);
+ auto iz = di.initializer.initializerSemantic(sc, tm, needInterpret);
+ auto ex = iz.initializerToExpression();
+ if (ex.op == TOK.error)
+ {
+ errors = true;
+ continue;
+ }
+
+ elems[fieldi] = ex;
+ }
+ if (errors)
+ return err();
+
+ // Make a StructLiteralExp out of elements[]
+ Type tx = ts;
+ auto sle = new StructLiteralExp(ci.loc, sd, elements, tx);
+ if (!sd.fill(ci.loc, elements, false))
+ return err();
+ sle.type = tx;
+ auto ie = new ExpInitializer(ci.loc, sle);
+ return ie.initializerSemantic(sc, tx, needInterpret);
+ }
+
+ if (auto ts = t.isTypeStruct())
+ {
+ auto ei = structs(ts);
+ if (errors)
+ return err();
+ if (i < dil.length)
+ {
+ error(ci.loc, "%d extra initializer(s) for `struct %s`", cast(int)(dil.length - i), ts.toChars());
+ return err();
+ }
+ return ei;
+ }
+
+ auto tsa = t.isTypeSArray();
+ auto ta = t.isTypeDArray();
+ if (!(tsa || ta))
+ {
+ /* Not an array. See if it is `{ exp }` which can be
+ * converted to an ExpInitializer
+ */
+ if (ExpInitializer ei = isBraceExpression())
+ {
+ return ei.initializerSemantic(sc, t, needInterpret);
+ }
+
+ error(ci.loc, "C non-array initializer (%s) %s not supported yet", t.toChars(), ci.toChars());
+ return err();
+ }
+
+ /* If it's an array of integral being initialized by `{ string }`
+ * replace with `string`
+ */
+ auto tn = t.nextOf();
+ if (tn.isintegral())
+ {
+ if (ExpInitializer ei = isBraceExpression())
+ {
+ if (ei.exp.isStringExp())
+ return ei.initializerSemantic(sc, t, needInterpret);
+ }
+ }
+
+ /* Support recursion to handle un-braced array initializers
+ * Params:
+ * t = element type
+ * dim = max number of elements
+ * Returns:
+ * # of elements in array
+ */
+ size_t array(Type t, size_t dim)
+ {
+ //printf(" type %s i %d dim %d dil.length = %d\n", t.toChars(), cast(int)i, cast(int)dim, cast(int)dil.length);
+ auto tn = t.nextOf().toBasetype();
+ if (auto tna = tn.isTypeDArray())
+ {
+ // C11 6.2.5-20 "element type shall be complete whenever the array type is specified"
+ error(ci.loc, "incomplete element type `%s` not allowed", tna.toChars());
+ errors = true;
+ return 1;
+ }
+ if (i == dil.length)
+ return 0;
+ size_t n;
+ auto tnsa = tn.isTypeSArray();
+ const nelems = tnsa ? cast(size_t)tnsa.dim.toInteger() : 0;
+
+ foreach (j; 0 .. dim)
+ {
+ auto di = dil[i];
+ if (di.designatorList)
+ {
+ error(ci.loc, "C designator-list not supported yet");
+ errors = true;
+ break;
+ }
+ if (tnsa && di.initializer.isExpInitializer())
+ {
+ // no braces enclosing array initializer, so recurse
+ array(tnsa, nelems);
+ }
+ else if (auto tns = tn.isTypeStruct())
+ {
+ dil[n].initializer = structs(tns);
+ }
+ else
+ {
+ ++i;
+ auto tnx = tn; // in case initializerSemantic tries to change it
+ di.initializer = di.initializer.initializerSemantic(sc, tnx, needInterpret);
+ if (di.initializer.isErrorInitializer())
+ errors = true;
+ assert(tnx == tn); // sub-types should not be modified
+ }
+ ++n;
+ if (i == dil.length)
+ break;
+ }
+ //printf(" n: %d i: %d\n", cast(int)n, cast(int)i);
+ return n;
+ }
+
+ size_t dim = ta ? dil.length : cast(size_t)tsa.dim.toInteger();
+ auto n = array(t, dim);
+
+ if (errors)
+ return err();
+
+ if (ta) // array of unknown length
+ {
+ // Change to array of known length
+ tsa = new TypeSArray(tn, new IntegerExp(Loc.initial, n, Type.tsize_t));
+ tx = tsa; // rewrite caller's type
+ ci.type = tsa; // remember for later passes
+ }
+ const uinteger_t edim = tsa.dim.toInteger();
+ if (i < dil.length)
+ {
+ error(ci.loc, "%d extra initializer(s) for static array length of %d", cast(int)(dil.length - i), cast(int)edim);
+ return err();
+ }
+
+ const sz = tn.size(); // element size
+ bool overflow;
+ const max = mulu(edim, sz, overflow);
+ if (overflow || max >= amax)
+ {
+ error(ci.loc, "array dimension %llu exceeds max of %llu", ulong(edim), ulong(amax / sz));
+ return err();
+ }
+
+ return ci;
+ }
+
+ final switch (init.kind)
+ {
+ case InitKind.void_: return visitVoid (cast( VoidInitializer)init);
+ case InitKind.error: return visitError (cast( ErrorInitializer)init);
+ case InitKind.struct_: return visitStruct(cast(StructInitializer)init);
+ case InitKind.array: return visitArray (cast( ArrayInitializer)init);
+ case InitKind.exp: return visitExp (cast( ExpInitializer)init);
+ case InitKind.C_: return visitC (cast( CInitializer)init);
+ }
+}
+
+/***********************
+ * Translate init to an `Expression` in order to infer the type.
+ * Params:
+ * init = `Initializer` AST node
+ * sc = context
+ * Returns:
+ * an equivalent `ExpInitializer` if successful, or `ErrorInitializer` if it cannot be translated
+ */
+Initializer inferType(Initializer init, Scope* sc)
+{
+ Initializer visitVoid(VoidInitializer i)
+ {
+ error(i.loc, "cannot infer type from void initializer");
+ return new ErrorInitializer();
+ }
+
+ Initializer visitError(ErrorInitializer i)
+ {
+ return i;
+ }
+
+ Initializer visitStruct(StructInitializer i)
+ {
+ error(i.loc, "cannot infer type from struct initializer");
+ return new ErrorInitializer();
+ }
+
+ Initializer visitArray(ArrayInitializer init)
+ {
+ //printf("ArrayInitializer::inferType() %s\n", toChars());
+ Expressions* keys = null;
+ Expressions* values;
+ if (init.isAssociativeArray())
+ {
+ keys = new Expressions(init.value.dim);
+ values = new Expressions(init.value.dim);
+ for (size_t i = 0; i < init.value.dim; i++)
+ {
+ Expression e = init.index[i];
+ if (!e)
+ goto Lno;
+ (*keys)[i] = e;
+ Initializer iz = init.value[i];
+ if (!iz)
+ goto Lno;
+ iz = iz.inferType(sc);
+ if (iz.isErrorInitializer())
+ {
+ return iz;
+ }
+ assert(iz.isExpInitializer());
+ (*values)[i] = (cast(ExpInitializer)iz).exp;
+ assert((*values)[i].op != TOK.error);
+ }
+ Expression e = new AssocArrayLiteralExp(init.loc, keys, values);
+ auto ei = new ExpInitializer(init.loc, e);
+ return ei.inferType(sc);
+ }
+ else
+ {
+ auto elements = new Expressions(init.value.dim);
+ elements.zero();
+ for (size_t i = 0; i < init.value.dim; i++)
+ {
+ assert(!init.index[i]); // already asserted by isAssociativeArray()
+ Initializer iz = init.value[i];
+ if (!iz)
+ goto Lno;
+ iz = iz.inferType(sc);
+ if (iz.isErrorInitializer())
+ {
+ return iz;
+ }
+ assert(iz.isExpInitializer());
+ (*elements)[i] = (cast(ExpInitializer)iz).exp;
+ assert((*elements)[i].op != TOK.error);
+ }
+ Expression e = new ArrayLiteralExp(init.loc, null, elements);
+ auto ei = new ExpInitializer(init.loc, e);
+ return ei.inferType(sc);
+ }
+ Lno:
+ if (keys)
+ {
+ error(init.loc, "not an associative array initializer");
+ }
+ else
+ {
+ error(init.loc, "cannot infer type from array initializer");
+ }
+ return new ErrorInitializer();
+ }
+
+ Initializer visitExp(ExpInitializer init)
+ {
+ //printf("ExpInitializer::inferType() %s\n", init.toChars());
+ init.exp = init.exp.expressionSemantic(sc);
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (init.exp.op == TOK.type)
+ init.exp = resolveAliasThis(sc, init.exp);
+
+ init.exp = resolveProperties(sc, init.exp);
+ if (init.exp.op == TOK.scope_)
+ {
+ ScopeExp se = cast(ScopeExp)init.exp;
+ TemplateInstance ti = se.sds.isTemplateInstance();
+ if (ti && ti.semanticRun == PASS.semantic && !ti.aliasdecl)
+ se.error("cannot infer type from %s `%s`, possible circular dependency", se.sds.kind(), se.toChars());
+ else
+ se.error("cannot infer type from %s `%s`", se.sds.kind(), se.toChars());
+ return new ErrorInitializer();
+ }
+
+ // Give error for overloaded function addresses
+ bool hasOverloads;
+ if (auto f = isFuncAddress(init.exp, &hasOverloads))
+ {
+ if (f.checkForwardRef(init.loc))
+ {
+ return new ErrorInitializer();
+ }
+ if (hasOverloads && !f.isUnique())
+ {
+ init.exp.error("cannot infer type from overloaded function symbol `%s`", init.exp.toChars());
+ return new ErrorInitializer();
+ }
+ }
+ if (init.exp.op == TOK.address)
+ {
+ AddrExp ae = cast(AddrExp)init.exp;
+ if (ae.e1.op == TOK.overloadSet)
+ {
+ init.exp.error("cannot infer type from overloaded function symbol `%s`", init.exp.toChars());
+ return new ErrorInitializer();
+ }
+ }
+ if (init.exp.op == TOK.error)
+ {
+ return new ErrorInitializer();
+ }
+ if (!init.exp.type)
+ {
+ return new ErrorInitializer();
+ }
+ return init;
+ }
+
+ Initializer visitC(CInitializer i)
+ {
+ //printf(CInitializer::inferType()\n");
+ error(i.loc, "TODO C inferType initializers not supported yet");
+ return new ErrorInitializer();
+ }
+
+ final switch (init.kind)
+ {
+ case InitKind.void_: return visitVoid (cast( VoidInitializer)init);
+ case InitKind.error: return visitError (cast( ErrorInitializer)init);
+ case InitKind.struct_: return visitStruct(cast(StructInitializer)init);
+ case InitKind.array: return visitArray (cast( ArrayInitializer)init);
+ case InitKind.exp: return visitExp (cast( ExpInitializer)init);
+ case InitKind.C_: return visitC (cast( CInitializer)init);
+ }
+}
+
+/***********************
+ * Translate init to an `Expression`.
+ * Params:
+ * init = `Initializer` AST node
+ * itype = if not `null`, type to coerce expression to
+ * Returns:
+ * `Expression` created, `null` if cannot, `ErrorExp` for other errors
+ */
+extern (C++) Expression initializerToExpression(Initializer init, Type itype = null)
+{
+ Expression visitVoid(VoidInitializer)
+ {
+ return null;
+ }
+
+ Expression visitError(ErrorInitializer)
+ {
+ return ErrorExp.get();
+ }
+
+ /***************************************
+ * This works by transforming a struct initializer into
+ * a struct literal. In the future, the two should be the
+ * same thing.
+ */
+ Expression visitStruct(StructInitializer)
+ {
+ // cannot convert to an expression without target 'ad'
+ return null;
+ }
+
+ /********************************
+ * If possible, convert array initializer to array literal.
+ * Otherwise return NULL.
+ */
+ Expression visitArray(ArrayInitializer init)
+ {
+ //printf("ArrayInitializer::toExpression(), dim = %d\n", dim);
+ //static int i; if (++i == 2) assert(0);
+ uint edim; // the length of the resulting array literal
+ const(uint) amax = 0x80000000;
+ Type t = null; // type of the array literal being initialized
+ if (init.type)
+ {
+ if (init.type == Type.terror)
+ {
+ return ErrorExp.get();
+ }
+ t = init.type.toBasetype();
+ switch (t.ty)
+ {
+ case Tvector:
+ t = t.isTypeVector().basetype;
+ goto case Tsarray;
+
+ case Tsarray:
+ uinteger_t adim = t.isTypeSArray().dim.toInteger();
+ if (adim >= amax)
+ return null;
+ edim = cast(uint)adim;
+ break;
+
+ case Tpointer:
+ case Tarray:
+ edim = init.dim;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ else
+ {
+ /* Calculate the length of the array literal
+ */
+ edim = cast(uint)init.value.dim;
+ size_t j = 0;
+ foreach (i; 0 .. init.value.dim)
+ {
+ if (auto e = init.index[i])
+ {
+ if (e.op == TOK.int64)
+ {
+ const uinteger_t idxval = e.toInteger();
+ if (idxval >= amax)
+ return null;
+ j = cast(size_t)idxval;
+ }
+ else
+ return null;
+ }
+ ++j;
+ if (j > edim)
+ edim = cast(uint)j;
+ }
+ }
+
+ auto elements = new Expressions(edim);
+ elements.zero();
+ size_t j = 0;
+ foreach (i; 0 .. init.value.dim)
+ {
+ if (auto e = init.index[i])
+ j = cast(size_t)e.toInteger();
+ assert(j < edim);
+ if (Initializer iz = init.value[i])
+ {
+ if (Expression ex = iz.initializerToExpression())
+ {
+ (*elements)[j] = ex;
+ ++j;
+ }
+ else
+ return null;
+ }
+ else
+ return null;
+ }
+
+ /* Fill in any missing elements with the default initializer
+ */
+ Expression defaultInit = null; // lazily create it
+ foreach (ref element; (*elements)[0 .. edim])
+ {
+ if (!element)
+ {
+ if (!init.type) // don't know what type to use
+ return null;
+ if (!defaultInit)
+ defaultInit = (cast(TypeNext)t).next.defaultInit(Loc.initial);
+ element = defaultInit;
+ }
+ }
+
+ /* Expand any static array initializers that are a single expression
+ * into an array of them
+ * e => [e, e, ..., e, e]
+ */
+ if (t)
+ {
+ Type tn = t.nextOf().toBasetype();
+ if (tn.ty == Tsarray)
+ {
+ const dim = cast(size_t)(cast(TypeSArray)tn).dim.toInteger();
+ Type te = tn.nextOf().toBasetype();
+ foreach (ref e; *elements)
+ {
+ if (te.equals(e.type))
+ {
+ auto elements2 = new Expressions(dim);
+ foreach (ref e2; *elements2)
+ e2 = e;
+ e = new ArrayLiteralExp(e.loc, tn, elements2);
+ }
+ }
+ }
+ }
+
+ /* If any elements are errors, then the whole thing is an error
+ */
+ foreach (e; (*elements)[0 .. edim])
+ {
+ if (e.op == TOK.error)
+ {
+ return e;
+ }
+ }
+
+ Expression e = new ArrayLiteralExp(init.loc, init.type, elements);
+ return e;
+ }
+
+ Expression visitExp(ExpInitializer i)
+ {
+ if (itype)
+ {
+ //printf("ExpInitializer::toExpression(t = %s) exp = %s\n", itype.toChars(), i.exp.toChars());
+ Type tb = itype.toBasetype();
+ Expression e = (i.exp.op == TOK.construct || i.exp.op == TOK.blit) ? (cast(AssignExp)i.exp).e2 : i.exp;
+ if (tb.ty == Tsarray && e.implicitConvTo(tb.nextOf()))
+ {
+ TypeSArray tsa = cast(TypeSArray)tb;
+ size_t d = cast(size_t)tsa.dim.toInteger();
+ auto elements = new Expressions(d);
+ for (size_t j = 0; j < d; j++)
+ (*elements)[j] = e;
+ auto ae = new ArrayLiteralExp(e.loc, itype, elements);
+ return ae;
+ }
+ }
+ return i.exp;
+ }
+
+ Expression visitC(CInitializer i)
+ {
+ //printf("CInitializer.initializerToExpression()\n");
+ return null;
+ }
+
+ final switch (init.kind)
+ {
+ case InitKind.void_: return visitVoid (cast( VoidInitializer)init);
+ case InitKind.error: return visitError (cast( ErrorInitializer)init);
+ case InitKind.struct_: return visitStruct(cast(StructInitializer)init);
+ case InitKind.array: return visitArray (cast( ArrayInitializer)init);
+ case InitKind.exp: return visitExp (cast( ExpInitializer)init);
+ case InitKind.C_: return visitC (cast( CInitializer)init);
+ }
+}
+
+
+/**************************************
+ * Determine if expression has non-constant pointers, or more precisely,
+ * a pointer that CTFE cannot handle.
+ * Params:
+ * e = expression to check
+ * Returns:
+ * true if it has non-constant pointers
+ */
+private bool hasNonConstPointers(Expression e)
+{
+ static bool checkArray(Expressions* elems)
+ {
+ foreach (e; *elems)
+ {
+ if (e && hasNonConstPointers(e))
+ return true;
+ }
+ return false;
+ }
+
+ if (e.type.ty == Terror)
+ return false;
+ if (e.op == TOK.null_)
+ return false;
+ if (auto se = e.isStructLiteralExp())
+ {
+ return checkArray(se.elements);
+ }
+ if (auto ae = e.isArrayLiteralExp())
+ {
+ if (!ae.type.nextOf().hasPointers())
+ return false;
+ return checkArray(ae.elements);
+ }
+ if (auto ae = e.isAssocArrayLiteralExp())
+ {
+ if (ae.type.nextOf().hasPointers() && checkArray(ae.values))
+ return true;
+ if ((cast(TypeAArray)ae.type).index.hasPointers())
+ return checkArray(ae.keys);
+ return false;
+ }
+ if (auto ae = e.isAddrExp())
+ {
+ if (auto se = ae.e1.isStructLiteralExp())
+ {
+ if (!(se.stageflags & stageSearchPointers))
+ {
+ const old = se.stageflags;
+ se.stageflags |= stageSearchPointers;
+ bool ret = checkArray(se.elements);
+ se.stageflags = old;
+ return ret;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (e.type.ty == Tpointer && !e.type.isPtrToFunction())
+ {
+ if (e.op == TOK.symbolOffset) // address of a global is OK
+ return false;
+ if (e.op == TOK.int64) // cast(void *)int is OK
+ return false;
+ if (e.op == TOK.string_) // "abc".ptr is OK
+ return false;
+ return true;
+ }
+ return false;
+}
+
+
+
diff --git a/gcc/d/dmd/inline.d b/gcc/d/dmd/inline.d
new file mode 100644
index 00000000000..cfd619ac4a4
--- /dev/null
+++ b/gcc/d/dmd/inline.d
@@ -0,0 +1,30 @@
+/**
+ * Performs inlining, which is an optimization pass enabled with the `-inline` flag.
+ *
+ * The AST is traversed, and every function call is considered for inlining using `inlinecost.d`.
+ * The function call is then inlined if this cost is below a threshold.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/inline.d, _inline.d)
+ * Documentation: https://dlang.org/phobos/dmd_inline.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/inline.d
+ */
+
+module dmd.inline;
+
+import dmd.dscope;
+import dmd.expression;
+
+/***********************************************************
+ * Perform the "inline copying" of a default argument for a function parameter.
+ *
+ * Todo:
+ * The hack for bugzilla 4820 case is still questionable. Perhaps would have to
+ * handle a delegate expression with 'null' context properly in front-end.
+ */
+public Expression inlineCopy(Expression e, Scope* sc)
+{
+ return e.copy();
+}
diff --git a/gcc/d/dmd/intrange.c b/gcc/d/dmd/intrange.c
deleted file mode 100644
index 36af8dac9ee..00000000000
--- a/gcc/d/dmd/intrange.c
+++ /dev/null
@@ -1,839 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by KennyTM
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/intrange.c
- */
-
-#include "root/dsystem.h"
-
-#include "intrange.h"
-#include "mars.h"
-#include "mtype.h"
-#include "expression.h"
-
-// Copy the sign to the value *x*. Equivalent to `sign ? -x : x`.
-static uinteger_t copySign(uinteger_t x, bool sign)
-{
- // return sign ? -x : x;
- return (x - (uinteger_t)sign) ^ -(uinteger_t)sign;
-}
-
-#ifndef UINT64_MAX
-#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL
-#endif
-
-//==================== SignExtendedNumber ======================================
-
-SignExtendedNumber SignExtendedNumber::fromInteger(uinteger_t value_)
-{
- return SignExtendedNumber(value_, value_ >> 63);
-}
-
-bool SignExtendedNumber::operator==(const SignExtendedNumber& a) const
-{
- return value == a.value && negative == a.negative;
-}
-
-bool SignExtendedNumber::operator<(const SignExtendedNumber& a) const
-{
- return (negative && !a.negative)
- || (negative == a.negative && value < a.value);
-}
-
-SignExtendedNumber SignExtendedNumber::extreme(bool minimum)
-{
- return SignExtendedNumber(minimum-1, minimum);
-}
-
-SignExtendedNumber SignExtendedNumber::max()
-{
- return SignExtendedNumber(UINT64_MAX, false);
-}
-
-SignExtendedNumber& SignExtendedNumber::operator++()
-{
- if (value != UINT64_MAX)
- ++value;
- else if (negative)
- {
- value = 0;
- negative = false;
- }
- return *this;
-}
-
-SignExtendedNumber SignExtendedNumber::operator~() const
-{
- if (~value == 0)
- return SignExtendedNumber(~value);
- else
- return SignExtendedNumber(~value, !negative);
-}
-
-SignExtendedNumber SignExtendedNumber::operator-() const
-{
- if (value == 0)
- return SignExtendedNumber(-negative);
- else
- return SignExtendedNumber(-value, !negative);
-}
-
-SignExtendedNumber SignExtendedNumber::operator&(const SignExtendedNumber& rhs) const
-{
- return SignExtendedNumber(value & rhs.value);
-}
-
-SignExtendedNumber SignExtendedNumber::operator|(const SignExtendedNumber& rhs) const
-{
- return SignExtendedNumber(value | rhs.value);
-}
-
-SignExtendedNumber SignExtendedNumber::operator^(const SignExtendedNumber& rhs) const
-{
- return SignExtendedNumber(value ^ rhs.value);
-}
-
-SignExtendedNumber SignExtendedNumber::operator+(const SignExtendedNumber& rhs) const
-{
- uinteger_t sum = value + rhs.value;
- bool carry = sum < value && sum < rhs.value;
- if (negative != rhs.negative)
- return SignExtendedNumber(sum, !carry);
- else if (negative)
- return SignExtendedNumber(carry ? sum : 0, true);
- else
- return SignExtendedNumber(carry ? UINT64_MAX : sum, false);
-}
-
-SignExtendedNumber SignExtendedNumber::operator-(const SignExtendedNumber& rhs) const
-{
- if (rhs.isMinimum())
- return negative ? SignExtendedNumber(value, false) : max();
- else
- return *this + (-rhs);
-}
-
-SignExtendedNumber SignExtendedNumber::operator*(const SignExtendedNumber& rhs) const
-{
- // perform *saturated* multiplication, otherwise we may get bogus ranges
- // like 0x10 * 0x10 == 0x100 == 0.
-
- /* Special handling for zeros:
- INT65_MIN * 0 = 0
- INT65_MIN * + = INT65_MIN
- INT65_MIN * - = INT65_MAX
- 0 * anything = 0
- */
- if (value == 0)
- {
- if (!negative)
- return *this;
- else if (rhs.negative)
- return max();
- else
- return rhs.value == 0 ? rhs : *this;
- }
- else if (rhs.value == 0)
- return rhs * *this; // don't duplicate the symmetric case.
-
- SignExtendedNumber rv;
- // these are != 0 now surely.
- uinteger_t tAbs = copySign(value, negative);
- uinteger_t aAbs = copySign(rhs.value, rhs.negative);
- rv.negative = negative != rhs.negative;
- if (UINT64_MAX / tAbs < aAbs)
- rv.value = rv.negative-1;
- else
- rv.value = copySign(tAbs * aAbs, rv.negative);
- return rv;
-}
-
-SignExtendedNumber SignExtendedNumber::operator/(const SignExtendedNumber& rhs) const
-{
- /* special handling for zeros:
- INT65_MIN / INT65_MIN = 1
- anything / INT65_MIN = 0
- + / 0 = INT65_MAX (eh?)
- - / 0 = INT65_MIN (eh?)
- */
- if (rhs.value == 0)
- {
- if (rhs.negative)
- return SignExtendedNumber(value == 0 && negative);
- else
- return extreme(negative);
- }
-
- uinteger_t aAbs = copySign(rhs.value, rhs.negative);
- uinteger_t rvVal;
-
- if (!isMinimum())
- rvVal = copySign(value, negative) / aAbs;
- // Special handling for INT65_MIN
- // if the denominator is not a power of 2, it is same as UINT64_MAX / x.
- else if (aAbs & (aAbs-1))
- rvVal = UINT64_MAX / aAbs;
- // otherwise, it's the same as reversing the bits of x.
- else
- {
- if (aAbs == 1)
- return extreme(!rhs.negative);
- rvVal = 1ULL << 63;
- aAbs >>= 1;
- if (aAbs & 0xAAAAAAAAAAAAAAAAULL) rvVal >>= 1;
- if (aAbs & 0xCCCCCCCCCCCCCCCCULL) rvVal >>= 2;
- if (aAbs & 0xF0F0F0F0F0F0F0F0ULL) rvVal >>= 4;
- if (aAbs & 0xFF00FF00FF00FF00ULL) rvVal >>= 8;
- if (aAbs & 0xFFFF0000FFFF0000ULL) rvVal >>= 16;
- if (aAbs & 0xFFFFFFFF00000000ULL) rvVal >>= 32;
- }
- bool rvNeg = negative != rhs.negative;
- rvVal = copySign(rvVal, rvNeg);
-
- return SignExtendedNumber(rvVal, rvVal != 0 && rvNeg);
-}
-
-SignExtendedNumber SignExtendedNumber::operator%(const SignExtendedNumber& rhs) const
-{
- if (rhs.value == 0)
- return !rhs.negative ? rhs : isMinimum() ? SignExtendedNumber(0) : *this;
-
- uinteger_t aAbs = copySign(rhs.value, rhs.negative);
- uinteger_t rvVal;
-
- // a % b == sgn(a) * abs(a) % abs(b).
- if (!isMinimum())
- rvVal = copySign(value, negative) % aAbs;
- // Special handling for INT65_MIN
- // if the denominator is not a power of 2, it is same as UINT64_MAX%x + 1.
- else if (aAbs & (aAbs - 1))
- rvVal = UINT64_MAX % aAbs + 1;
- // otherwise, the modulus is trivially zero.
- else
- rvVal = 0;
-
- rvVal = copySign(rvVal, negative);
- return SignExtendedNumber(rvVal, rvVal != 0 && negative);
-}
-
-SignExtendedNumber SignExtendedNumber::operator<<(const SignExtendedNumber& rhs) const
-{
- // assume left-shift the shift-amount is always unsigned. Thus negative
- // shifts will give huge result.
- if (value == 0)
- return *this;
- else if (rhs.negative)
- return extreme(negative);
-
- uinteger_t v = copySign(value, negative);
-
- // compute base-2 log of 'v' to determine the maximum allowed bits to shift.
- // Ref: http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
-
- // Why is this a size_t? Looks like a bug.
- size_t r, s;
-
- r = (v > 0xFFFFFFFFULL) << 5; v >>= r;
- s = (v > 0xFFFFULL ) << 4; v >>= s; r |= s;
- s = (v > 0xFFULL ) << 3; v >>= s; r |= s;
- s = (v > 0xFULL ) << 2; v >>= s; r |= s;
- s = (v > 0x3ULL ) << 1; v >>= s; r |= s;
- r |= (v >> 1);
-
- uinteger_t allowableShift = 63 - r;
- if (rhs.value > allowableShift)
- return extreme(negative);
- else
- return SignExtendedNumber(value << rhs.value, negative);
-}
-
-SignExtendedNumber SignExtendedNumber::operator>>(const SignExtendedNumber& rhs) const
-{
- if (rhs.negative || rhs.value > 63)
- return negative ? SignExtendedNumber(-1, true) : SignExtendedNumber(0);
- else if (isMinimum())
- return rhs.value == 0 ? *this : SignExtendedNumber(-1ULL << (64 - rhs.value), true);
-
- uinteger_t x = value ^ -negative;
- x >>= rhs.value;
- return SignExtendedNumber(x ^ -negative, negative);
-}
-
-
-//==================== IntRange ================================================
-
-IntRange IntRange::widest()
-{
- return IntRange(SignExtendedNumber::min(), SignExtendedNumber::max());
-}
-
-IntRange IntRange::fromType(Type *type)
-{
- return fromType(type, type->isunsigned());
-}
-
-IntRange IntRange::fromType(Type *type, bool isUnsigned)
-{
- if (!type->isintegral() || type->toBasetype()->ty == Tvector)
- return widest();
-
- uinteger_t mask = type->sizemask();
- SignExtendedNumber lower(0), upper(mask);
- if (type->toBasetype()->ty == Tdchar)
- upper.value = 0x10FFFFULL;
- else if (!isUnsigned)
- {
- lower.value = ~(mask >> 1);
- lower.negative = true;
- upper.value = (mask >> 1);
- }
- return IntRange(lower, upper);
-}
-
-IntRange IntRange::fromNumbers2(const SignExtendedNumber numbers[2])
-{
- if (numbers[0] < numbers[1])
- return IntRange(numbers[0], numbers[1]);
- else
- return IntRange(numbers[1], numbers[0]);
-}
-IntRange IntRange::fromNumbers4(const SignExtendedNumber numbers[4])
-{
- IntRange ab = fromNumbers2(numbers);
- IntRange cd = fromNumbers2(numbers + 2);
- if (cd.imin < ab.imin)
- ab.imin = cd.imin;
- if (cd.imax > ab.imax)
- ab.imax = cd.imax;
- return ab;
-}
-
-bool IntRange::contains(const IntRange& a) const
-{
- return imin <= a.imin && imax >= a.imax;
-}
-
-bool IntRange::containsZero() const
-{
- return (imin.negative && !imax.negative)
- || (!imin.negative && imin.value == 0);
-}
-
-IntRange& IntRange::castUnsigned(uinteger_t mask)
-{
- // .... 0x1eff ] [0x1f00 .. 0x1fff] [0 .. 0xff] [0x100 .. 0x1ff] [0x200 ....
- //
- // regular unsigned type. We just need to see if ir steps across the
- // boundary of validRange. If yes, ir will represent the whole validRange,
- // otherwise, we just take the modulus.
- // e.g. [0x105, 0x107] & 0xff == [5, 7]
- // [0x105, 0x207] & 0xff == [0, 0xff]
- uinteger_t minChunk = imin.value & ~mask;
- uinteger_t maxChunk = imax.value & ~mask;
- if (minChunk == maxChunk && imin.negative == imax.negative)
- {
- imin.value &= mask;
- imax.value &= mask;
- }
- else
- {
- imin.value = 0;
- imax.value = mask;
- }
- imin.negative = imax.negative = false;
- return *this;
-}
-
-IntRange& IntRange::castSigned(uinteger_t mask)
-{
- // .... 0x1e7f ] [0x1e80 .. 0x1f7f] [0x1f80 .. 0x7f] [0x80 .. 0x17f] [0x180 ....
- //
- // regular signed type. We use a technique similar to the unsigned version,
- // but the chunk has to be offset by 1/2 of the range.
- uinteger_t halfChunkMask = mask >> 1;
- uinteger_t minHalfChunk = imin.value & ~halfChunkMask;
- uinteger_t maxHalfChunk = imax.value & ~halfChunkMask;
- int minHalfChunkNegativity = imin.negative; // 1 = neg, 0 = nonneg, -1 = chunk containing ::max
- int maxHalfChunkNegativity = imax.negative;
- if (minHalfChunk & mask)
- {
- minHalfChunk += halfChunkMask+1;
- if (minHalfChunk == 0)
- -- minHalfChunkNegativity;
- }
- if (maxHalfChunk & mask)
- {
- maxHalfChunk += halfChunkMask+1;
- if (maxHalfChunk == 0)
- -- maxHalfChunkNegativity;
- }
- if (minHalfChunk == maxHalfChunk && minHalfChunkNegativity == maxHalfChunkNegativity)
- {
- imin.value &= mask;
- imax.value &= mask;
- // sign extend if necessary.
- imin.negative = imin.value & ~halfChunkMask;
- imax.negative = imax.value & ~halfChunkMask;
- halfChunkMask += 1;
- imin.value = (imin.value ^ halfChunkMask) - halfChunkMask;
- imax.value = (imax.value ^ halfChunkMask) - halfChunkMask;
- }
- else
- {
- imin = SignExtendedNumber(~halfChunkMask, true);
- imax = SignExtendedNumber(halfChunkMask, false);
- }
- return *this;
-}
-
-IntRange& IntRange::castDchar()
-{
- // special case for dchar. Casting to dchar means "I'll ignore all
- // invalid characters."
- castUnsigned(0xFFFFFFFFULL);
- if (imin.value > 0x10FFFFULL) // ??
- imin.value = 0x10FFFFULL; // ??
- if (imax.value > 0x10FFFFULL)
- imax.value = 0x10FFFFULL;
- return *this;
-}
-
-IntRange& IntRange::cast(Type *type)
-{
- if (!type->isintegral() || type->toBasetype()->ty == Tvector)
- return *this;
- else if (!type->isunsigned())
- return castSigned(type->sizemask());
- else if (type->toBasetype()->ty == Tdchar)
- return castDchar();
- else
- return castUnsigned(type->sizemask());
-}
-
-IntRange& IntRange::castUnsigned(Type *type)
-{
- if (!type->isintegral() || type->toBasetype()->ty == Tvector)
- return castUnsigned(UINT64_MAX);
- else if (type->toBasetype()->ty == Tdchar)
- return castDchar();
- else
- return castUnsigned(type->sizemask());
-}
-
-IntRange IntRange::absNeg() const
-{
- if (imax.negative)
- return *this;
- else if (!imin.negative)
- return IntRange(-imax, -imin);
- else
- {
- SignExtendedNumber imaxAbsNeg = -imax;
- return IntRange(imaxAbsNeg < imin ? imaxAbsNeg : imin,
- SignExtendedNumber(0));
- }
-}
-
-IntRange IntRange::unionWith(const IntRange& other) const
-{
- return IntRange(imin < other.imin ? imin : other.imin,
- imax > other.imax ? imax : other.imax);
-}
-
-void IntRange::unionOrAssign(const IntRange& other, bool& union_)
-{
- if (!union_ || imin > other.imin)
- imin = other.imin;
- if (!union_ || imax < other.imax)
- imax = other.imax;
- union_ = true;
-}
-
-void IntRange::splitBySign(IntRange& negRange, bool& hasNegRange,
- IntRange& nonNegRange, bool& hasNonNegRange) const
-{
- hasNegRange = imin.negative;
- if (hasNegRange)
- {
- negRange.imin = imin;
- negRange.imax = imax.negative ? imax : SignExtendedNumber(-1, true);
- }
- hasNonNegRange = !imax.negative;
- if (hasNonNegRange)
- {
- nonNegRange.imin = imin.negative ? SignExtendedNumber(0) : imin;
- nonNegRange.imax = imax;
- }
-}
-
-IntRange IntRange::operator~() const
-{
- return IntRange(~imax, ~imin);
-}
-
-IntRange IntRange::operator-() const
-{
- return IntRange(-imax, -imin);
-}
-
-IntRange IntRange::operator&(const IntRange& rhs) const
-{
- // unsigned or identical sign bits
- if ((imin.negative ^ imax.negative) != 1 && (rhs.imin.negative ^ rhs.imax.negative) != 1)
- {
- return IntRange(minAnd(*this, rhs), maxAnd(*this, rhs));
- }
-
- IntRange l = IntRange(*this);
- IntRange r = IntRange(rhs);
-
- // both intervals span [-1,0]
- if ((l.imin.negative ^ l.imax.negative) == 1 && (r.imin.negative ^ r.imax.negative) == 1)
- {
- // cannot be larger than either l.max or r.max, set the other one to -1
- SignExtendedNumber max = l.imax.value > r.imax.value ? l.imax : r.imax;
-
- // only negative numbers for minimum
- l.imax.value = -1;
- l.imax.negative = true;
- r.imax.value = -1;
- r.imax.negative = true;
-
- return IntRange(minAnd(l, r), max);
- }
- else
- {
- // only one interval spans [-1,0]
- if ((l.imin.negative ^ l.imax.negative) == 1)
- {
- swap(l, r); // r spans [-1,0]
- }
-
- SignExtendedNumber minAndNeg = minAnd(l, IntRange(r.imin, SignExtendedNumber(-1)));
- SignExtendedNumber minAndPos = minAnd(l, IntRange(SignExtendedNumber(0), r.imax));
- SignExtendedNumber maxAndNeg = maxAnd(l, IntRange(r.imin, SignExtendedNumber(-1)));
- SignExtendedNumber maxAndPos = maxAnd(l, IntRange(SignExtendedNumber(0), r.imax));
-
- SignExtendedNumber min = minAndNeg < minAndPos ? minAndNeg : minAndPos;
- SignExtendedNumber max = maxAndNeg > maxAndPos ? maxAndNeg : maxAndPos;
-
- return IntRange(min, max);
- }
-}
-
-IntRange IntRange::operator|(const IntRange& rhs) const
-{
- // unsigned or identical sign bits:
- if ((imin.negative ^ imax.negative) == 0 && (rhs.imin.negative ^ rhs.imax.negative) == 0)
- {
- return IntRange(minOr(*this, rhs), maxOr(*this, rhs));
- }
-
- IntRange l = IntRange(*this);
- IntRange r = IntRange(rhs);
-
- // both intervals span [-1,0]
- if ((l.imin.negative ^ l.imax.negative) == 1 && (r.imin.negative ^ r.imax.negative) == 1)
- {
- // cannot be smaller than either l.min or r.min, set the other one to 0
- SignExtendedNumber min = l.imin.value < r.imin.value ? l.imin : r.imin;
-
- // only negative numbers for minimum
- l.imin.value = 0;
- l.imin.negative = false;
- r.imin.value = 0;
- r.imin.negative = false;
-
- return IntRange(min, maxOr(l, r));
- }
- else
- {
- // only one interval spans [-1,0]
- if ((imin.negative ^ imax.negative) == 1)
- {
- swap(l, r); // r spans [-1,0]
- }
-
- SignExtendedNumber minOrNeg = minOr(l, IntRange(r.imin, SignExtendedNumber(-1)));
- SignExtendedNumber minOrPos = minOr(l, IntRange(SignExtendedNumber(0), r.imax));
- SignExtendedNumber maxOrNeg = maxOr(l, IntRange(r.imin, SignExtendedNumber(-1)));
- SignExtendedNumber maxOrPos = maxOr(l, IntRange(SignExtendedNumber(0), r.imax));
-
- SignExtendedNumber min = minOrNeg < minOrPos ? minOrNeg : minOrPos;
- SignExtendedNumber max = maxOrNeg > maxOrPos ? maxOrNeg : maxOrPos;
-
- return IntRange(min, max);
- }
-}
-
-IntRange IntRange::operator^(const IntRange& rhs) const
-{
- return (*this & (~rhs)) | (~(*this) & rhs);
-}
-
-IntRange IntRange::operator+(const IntRange& rhs) const
-{
- return IntRange(imin + rhs.imin, imax + rhs.imax);
-}
-
-IntRange IntRange::operator-(const IntRange& rhs) const
-{
- return IntRange(imin - rhs.imax, imax - rhs.imin);
-}
-
-IntRange IntRange::operator*(const IntRange& rhs) const
-{
- // [a,b] * [c,d] = [min (ac, ad, bc, bd), max (ac, ad, bc, bd)]
- SignExtendedNumber bdy[4];
- bdy[0] = imin * rhs.imin;
- bdy[1] = imin * rhs.imax;
- bdy[2] = imax * rhs.imin;
- bdy[3] = imax * rhs.imax;
- return IntRange::fromNumbers4(bdy);
-}
-
-IntRange IntRange::operator/(const IntRange& rhs) const
-{
- // Handle divide by 0
- if (rhs.imax.value == 0 && rhs.imin.value == 0)
- return widest();
-
- IntRange r = IntRange(rhs);
-
- // Don't treat the whole range as divide by 0 if only one end of a range is 0.
- // Issue 15289
- if (r.imax.value == 0)
- {
- r.imax.value--;
- }
- else if (r.imin.value == 0)
- {
- r.imin.value++;
- }
-
- if (!imin.negative && !imax.negative && !r.imin.negative && !r.imax.negative)
- {
- return IntRange(imin / r.imax, imax / r.imin);
- }
- else
- {
- // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)]
- SignExtendedNumber bdy[4];
- bdy[0] = imin / r.imin;
- bdy[1] = imin / r.imax;
- bdy[2] = imax / r.imin;
- bdy[3] = imax / r.imax;
-
- return IntRange::fromNumbers4(bdy);
- }
-}
-
-IntRange IntRange::operator%(const IntRange& rhs) const
-{
- IntRange irNum = *this;
- IntRange irDen = rhs.absNeg();
-
- /*
- due to the rules of D (C)'s % operator, we need to consider the cases
- separately in different range of signs.
-
- case 1. [500, 1700] % [7, 23] (numerator is always positive)
- = [0, 22]
- case 2. [-500, 1700] % [7, 23] (numerator can be negative)
- = [-22, 22]
- case 3. [-1700, -500] % [7, 23] (numerator is always negative)
- = [-22, 0]
-
- the number 22 is the maximum absolute value in the denomator's range. We
- don't care about divide by zero.
- */
-
- irDen.imin = irDen.imin + SignExtendedNumber(1);
- irDen.imax = -irDen.imin;
-
- if (!irNum.imin.negative)
- {
- irNum.imin.value = 0;
- }
- else if (irNum.imin < irDen.imin)
- {
- irNum.imin = irDen.imin;
- }
-
- if (irNum.imax.negative)
- {
- irNum.imax.negative = false;
- irNum.imax.value = 0;
- }
- else if (irNum.imax > irDen.imax)
- {
- irNum.imax = irDen.imax;
- }
-
- return irNum;
-}
-
-IntRange IntRange::operator<<(const IntRange& rhs) const
-{
- IntRange r = IntRange(rhs);
- if (r.imin.negative)
- {
- r = IntRange(SignExtendedNumber(0), SignExtendedNumber(64));
- }
-
- SignExtendedNumber lower = imin << (imin.negative ? r.imax : r.imin);
- SignExtendedNumber upper = imax << (imax.negative ? r.imin : r.imax);
-
- return IntRange(lower, upper);
-}
-
-IntRange IntRange::operator>>(const IntRange& rhs) const
-{
- IntRange r = IntRange(rhs);
- if (r.imin.negative)
- {
- r = IntRange(SignExtendedNumber(0), SignExtendedNumber(64));
- }
-
- SignExtendedNumber lower = imin >> (imin.negative ? r.imin : r.imax);
- SignExtendedNumber upper = imax >> (imax.negative ? r.imax : r.imin);
-
- return IntRange(lower, upper);
-}
-
-SignExtendedNumber IntRange::maxOr(const IntRange& lhs, const IntRange& rhs)
-{
- uinteger_t x = 0;
- bool sign = false;
- uinteger_t xorvalue = lhs.imax.value ^ rhs.imax.value;
- uinteger_t andvalue = lhs.imax.value & rhs.imax.value;
- IntRange lhsc = IntRange(lhs);
- IntRange rhsc = IntRange(rhs);
-
- // Sign bit not part of the .value so we need an extra iteration
- if (lhsc.imax.negative ^ rhsc.imax.negative)
- {
- sign = true;
- if (lhsc.imax.negative)
- {
- if (!lhsc.imin.negative)
- {
- lhsc.imin.value = 0;
- }
- if (!rhsc.imin.negative)
- {
- rhsc.imin.value = 0;
- }
- }
- }
- else if (lhsc.imin.negative & rhsc.imin.negative)
- {
- sign = true;
- }
- else if (lhsc.imax.negative & rhsc.imax.negative)
- {
- return SignExtendedNumber(-1, false);
- }
-
- for (uinteger_t d = 1ULL << (8 * sizeof(uinteger_t) - 1); d; d >>= 1)
- {
- if (xorvalue & d)
- {
- x |= d;
- if (lhsc.imax.value & d)
- {
- if (~lhsc.imin.value & d)
- {
- lhsc.imin.value = 0;
- }
- }
- else
- {
- if (~rhsc.imin.value & d)
- {
- rhsc.imin.value = 0;
- }
- }
- }
- else if (lhsc.imin.value & rhsc.imin.value & d)
- {
- x |= d;
- }
- else if (andvalue & d)
- {
- x |= (d << 1) - 1;
- break;
- }
- }
-
- return SignExtendedNumber(x, sign);
-}
-
-SignExtendedNumber IntRange::minOr(const IntRange& lhs, const IntRange& rhs)
-{
- return ~maxAnd(~lhs, ~rhs);
-}
-
-SignExtendedNumber IntRange::maxAnd(const IntRange& lhs, const IntRange& rhs)
-{
- uinteger_t x = 0;
- bool sign = false;
- IntRange lhsc = IntRange(lhs);
- IntRange rhsc = IntRange(rhs);
-
- if (lhsc.imax.negative & rhsc.imax.negative)
- {
- sign = true;
- }
-
- for (uinteger_t d = 1ULL << (8 * sizeof(uinteger_t) - 1); d; d >>= 1)
- {
- if (lhsc.imax.value & rhsc.imax.value & d)
- {
- x |= d;
- if (~lhsc.imin.value & d)
- {
- lhsc.imin.value = 0;
- }
- if (~rhsc.imin.value & d)
- {
- rhsc.imin.value = 0;
- }
- }
- else if (~lhsc.imin.value & d && lhsc.imax.value & d)
- {
- lhsc.imax.value |= d - 1;
- }
- else if (~rhsc.imin.value & d && rhsc.imax.value & d)
- {
- rhsc.imax.value |= d - 1;
- }
- }
-
- return SignExtendedNumber(x, sign);
-}
-
-SignExtendedNumber IntRange::minAnd(const IntRange& lhs, const IntRange& rhs)
-{
- return ~maxOr(~lhs, ~rhs);
-}
-
-void IntRange::swap(IntRange& a, IntRange& b)
-{
- IntRange aux = a;
- a = b;
- b = aux;
-}
-
-const IntRange& IntRange::dump(const char* funcName, Expression *e) const
-{
- printf("[(%c)%#018llx, (%c)%#018llx] @ %s ::: %s\n",
- imin.negative?'-':'+', (unsigned long long)imin.value,
- imax.negative?'-':'+', (unsigned long long)imax.value,
- funcName, e->toChars());
- return *this;
-}
diff --git a/gcc/d/dmd/intrange.d b/gcc/d/dmd/intrange.d
new file mode 100644
index 00000000000..9b70f49a0e8
--- /dev/null
+++ b/gcc/d/dmd/intrange.d
@@ -0,0 +1,919 @@
+/**
+ * Implement $(LINK2 https://digitalmars.com/articles/b62.html, Value Range Propagation).
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/intrange.d, _intrange.d)
+ * Documentation: https://dlang.org/phobos/dmd_intrange.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/intrange.d
+ */
+
+module dmd.intrange;
+
+import core.stdc.stdio;
+
+import dmd.astenums;
+import dmd.mtype;
+import dmd.expression;
+import dmd.globals;
+
+private uinteger_t copySign(uinteger_t x, bool sign)
+{
+ // return sign ? -x : x;
+ return (x - cast(uinteger_t)sign) ^ -cast(uinteger_t)sign;
+}
+
+struct SignExtendedNumber
+{
+ uinteger_t value;
+ bool negative;
+
+ static SignExtendedNumber fromInteger(uinteger_t value_)
+ {
+ return SignExtendedNumber(value_, value_ >> 63);
+ }
+
+ static SignExtendedNumber extreme(bool minimum)
+ {
+ return SignExtendedNumber(minimum - 1, minimum);
+ }
+
+ static SignExtendedNumber max()
+ {
+ return SignExtendedNumber(ulong.max, false);
+ }
+
+ static SignExtendedNumber min()
+ {
+ return SignExtendedNumber(0, true);
+ }
+
+ bool isMinimum() const
+ {
+ return negative && value == 0;
+ }
+
+ bool opEquals(const ref SignExtendedNumber a) const
+ {
+ return value == a.value && negative == a.negative;
+ }
+
+ int opCmp(const ref SignExtendedNumber a) const
+ {
+ if (negative != a.negative)
+ {
+ if (negative)
+ return -1;
+ else
+ return 1;
+ }
+ if (value < a.value)
+ return -1;
+ else if (value > a.value)
+ return 1;
+ else
+ return 0;
+ }
+
+ SignExtendedNumber opUnary(string op : "++")()
+ {
+ if (value != ulong.max)
+ ++value;
+ else if (negative)
+ {
+ value = 0;
+ negative = false;
+ }
+ return this;
+ }
+
+ SignExtendedNumber opUnary(string op : "~")() const
+ {
+ if (~value == 0)
+ return SignExtendedNumber(~value);
+ else
+ return SignExtendedNumber(~value, !negative);
+ }
+
+ SignExtendedNumber opUnary(string op : "-")() const
+ {
+ if (value == 0)
+ return SignExtendedNumber(-cast(ulong)negative);
+ else
+ return SignExtendedNumber(-value, !negative);
+ }
+
+ SignExtendedNumber opBinary(string op : "&")(SignExtendedNumber rhs) const
+ {
+ return SignExtendedNumber(value & rhs.value);
+ }
+
+ SignExtendedNumber opBinary(string op : "|")(SignExtendedNumber rhs)
+ {
+ return SignExtendedNumber(value | rhs.value);
+ }
+
+ SignExtendedNumber opBinary(string op : "^")(SignExtendedNumber rhs)
+ {
+ return SignExtendedNumber(value ^ rhs.value);
+ }
+
+ SignExtendedNumber opBinary(string op : "+")(SignExtendedNumber rhs)
+ {
+ uinteger_t sum = value + rhs.value;
+ bool carry = sum < value && sum < rhs.value;
+ if (negative != rhs.negative)
+ return SignExtendedNumber(sum, !carry);
+ else if (negative)
+ return SignExtendedNumber(carry ? sum : 0, true);
+ else
+ return SignExtendedNumber(carry ? ulong.max : sum, false);
+ }
+
+
+ SignExtendedNumber opBinary(string op : "-")(SignExtendedNumber rhs)
+ {
+ if (rhs.isMinimum())
+ return negative ? SignExtendedNumber(value, false) : max();
+ else
+ return this + (-rhs);
+ }
+
+ SignExtendedNumber opBinary(string op : "*")(SignExtendedNumber rhs)
+ {
+ // perform *saturated* multiplication, otherwise we may get bogus ranges
+ // like 0x10 * 0x10 == 0x100 == 0.
+
+ /* Special handling for zeros:
+ INT65_MIN * 0 = 0
+ INT65_MIN * + = INT65_MIN
+ INT65_MIN * - = INT65_MAX
+ 0 * anything = 0
+ */
+ if (value == 0)
+ {
+ if (!negative)
+ return this;
+ else if (rhs.negative)
+ return max();
+ else
+ return rhs.value == 0 ? rhs : this;
+ }
+ else if (rhs.value == 0)
+ return rhs * this; // don't duplicate the symmetric case.
+
+ SignExtendedNumber rv;
+ // these are != 0 now surely.
+ uinteger_t tAbs = copySign(value, negative);
+ uinteger_t aAbs = copySign(rhs.value, rhs.negative);
+ rv.negative = negative != rhs.negative;
+ if (ulong.max / tAbs < aAbs)
+ rv.value = rv.negative - 1;
+ else
+ rv.value = copySign(tAbs * aAbs, rv.negative);
+ return rv;
+ }
+
+ SignExtendedNumber opBinary(string op : "/")(SignExtendedNumber rhs)
+ {
+ /* special handling for zeros:
+ INT65_MIN / INT65_MIN = 1
+ anything / INT65_MIN = 0
+ + / 0 = INT65_MAX (eh?)
+ - / 0 = INT65_MIN (eh?)
+ */
+ if (rhs.value == 0)
+ {
+ if (rhs.negative)
+ return SignExtendedNumber(value == 0 && negative);
+ else
+ return extreme(negative);
+ }
+
+ uinteger_t aAbs = copySign(rhs.value, rhs.negative);
+ uinteger_t rvVal;
+
+ if (!isMinimum())
+ rvVal = copySign(value, negative) / aAbs;
+ // Special handling for INT65_MIN
+ // if the denominator is not a power of 2, it is same as ulong.max / x.
+ else if (aAbs & (aAbs - 1))
+ rvVal = ulong.max / aAbs;
+ // otherwise, it's the same as reversing the bits of x.
+ else
+ {
+ if (aAbs == 1)
+ return extreme(!rhs.negative);
+ rvVal = 1UL << 63;
+ aAbs >>= 1;
+ if (aAbs & 0xAAAAAAAAAAAAAAAAUL) rvVal >>= 1;
+ if (aAbs & 0xCCCCCCCCCCCCCCCCUL) rvVal >>= 2;
+ if (aAbs & 0xF0F0F0F0F0F0F0F0UL) rvVal >>= 4;
+ if (aAbs & 0xFF00FF00FF00FF00UL) rvVal >>= 8;
+ if (aAbs & 0xFFFF0000FFFF0000UL) rvVal >>= 16;
+ if (aAbs & 0xFFFFFFFF00000000UL) rvVal >>= 32;
+ }
+ bool rvNeg = negative != rhs.negative;
+ rvVal = copySign(rvVal, rvNeg);
+
+ return SignExtendedNumber(rvVal, rvVal != 0 && rvNeg);
+ }
+
+ SignExtendedNumber opBinary(string op : "%")(SignExtendedNumber rhs)
+ {
+ if (rhs.value == 0)
+ return !rhs.negative ? rhs : isMinimum() ? SignExtendedNumber(0) : this;
+
+ uinteger_t aAbs = copySign(rhs.value, rhs.negative);
+ uinteger_t rvVal;
+
+ // a % b == sgn(a) * abs(a) % abs(b).
+ if (!isMinimum())
+ rvVal = copySign(value, negative) % aAbs;
+ // Special handling for INT65_MIN
+ // if the denominator is not a power of 2, it is same as ulong.max % x + 1.
+ else if (aAbs & (aAbs - 1))
+ rvVal = ulong.max % aAbs + 1;
+ // otherwise, the modulus is trivially zero.
+ else
+ rvVal = 0;
+
+ rvVal = copySign(rvVal, negative);
+ return SignExtendedNumber(rvVal, rvVal != 0 && negative);
+ }
+
+ SignExtendedNumber opBinary(string op : "<<")(SignExtendedNumber rhs)
+ {
+ // assume left-shift the shift-amount is always unsigned. Thus negative
+ // shifts will give huge result.
+ if (value == 0)
+ return this;
+ else if (rhs.negative)
+ return extreme(negative);
+
+ uinteger_t v = copySign(value, negative);
+
+ // compute base-2 log of 'v' to determine the maximum allowed bits to shift.
+ // Ref: http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
+
+ // Why is this a size_t? Looks like a bug.
+ size_t r, s;
+
+ r = (v > 0xFFFFFFFFUL) << 5; v >>= r;
+ s = (v > 0xFFFFUL ) << 4; v >>= s; r |= s;
+ s = (v > 0xFFUL ) << 3; v >>= s; r |= s;
+ s = (v > 0xFUL ) << 2; v >>= s; r |= s;
+ s = (v > 0x3UL ) << 1; v >>= s; r |= s;
+ r |= (v >> 1);
+
+ uinteger_t allowableShift = 63 - r;
+ if (rhs.value > allowableShift)
+ return extreme(negative);
+ else
+ return SignExtendedNumber(value << rhs.value, negative);
+ }
+
+ SignExtendedNumber opBinary(string op : ">>")(SignExtendedNumber rhs)
+ {
+ if (rhs.negative || rhs.value > 63)
+ return negative ? SignExtendedNumber(-1, true) : SignExtendedNumber(0);
+ else if (isMinimum())
+ return rhs.value == 0 ? this : SignExtendedNumber(-1UL << (64 - rhs.value), true);
+
+ uinteger_t x = value ^ -cast(int)negative;
+ x >>= rhs.value;
+ return SignExtendedNumber(x ^ -cast(int)negative, negative);
+ }
+
+ SignExtendedNumber opBinary(string op : "^^")(SignExtendedNumber rhs)
+ {
+ // Not yet implemented
+ assert(0);
+ }
+}
+
+struct IntRange
+{
+ SignExtendedNumber imin, imax;
+
+ this(IntRange another)
+ {
+ imin = another.imin;
+ imax = another.imax;
+ }
+
+ this(SignExtendedNumber a)
+ {
+ imin = a;
+ imax = a;
+ }
+
+ this(SignExtendedNumber lower, SignExtendedNumber upper)
+ {
+ imin = lower;
+ imax = upper;
+ }
+
+ static IntRange fromType(Type type)
+ {
+ return fromType(type, type.isunsigned());
+ }
+
+ static IntRange fromType(Type type, bool isUnsigned)
+ {
+ if (!type.isintegral() || type.toBasetype().ty == Tvector)
+ return widest();
+
+ uinteger_t mask = type.sizemask();
+ auto lower = SignExtendedNumber(0);
+ auto upper = SignExtendedNumber(mask);
+ if (type.toBasetype().ty == Tdchar)
+ upper.value = 0x10FFFFUL;
+ else if (!isUnsigned)
+ {
+ lower.value = ~(mask >> 1);
+ lower.negative = true;
+ upper.value = (mask >> 1);
+ }
+ return IntRange(lower, upper);
+ }
+
+ static IntRange fromNumbers2(SignExtendedNumber* numbers)
+ {
+ if (numbers[0] < numbers[1])
+ return IntRange(numbers[0], numbers[1]);
+ else
+ return IntRange(numbers[1], numbers[0]);
+ }
+
+ static IntRange fromNumbers4(SignExtendedNumber* numbers)
+ {
+ IntRange ab = fromNumbers2(numbers);
+ IntRange cd = fromNumbers2(numbers + 2);
+ if (cd.imin < ab.imin)
+ ab.imin = cd.imin;
+ if (cd.imax > ab.imax)
+ ab.imax = cd.imax;
+ return ab;
+ }
+
+ static IntRange widest()
+ {
+ return IntRange(SignExtendedNumber.min(), SignExtendedNumber.max());
+ }
+
+ IntRange castSigned(uinteger_t mask)
+ {
+ // .... 0x1e7f ] [0x1e80 .. 0x1f7f] [0x1f80 .. 0x7f] [0x80 .. 0x17f] [0x180 ....
+ //
+ // regular signed type. We use a technique similar to the unsigned version,
+ // but the chunk has to be offset by 1/2 of the range.
+ uinteger_t halfChunkMask = mask >> 1;
+ uinteger_t minHalfChunk = imin.value & ~halfChunkMask;
+ uinteger_t maxHalfChunk = imax.value & ~halfChunkMask;
+ int minHalfChunkNegativity = imin.negative; // 1 = neg, 0 = nonneg, -1 = chunk containing ::max
+ int maxHalfChunkNegativity = imax.negative;
+ if (minHalfChunk & mask)
+ {
+ minHalfChunk += halfChunkMask + 1;
+ if (minHalfChunk == 0)
+ --minHalfChunkNegativity;
+ }
+ if (maxHalfChunk & mask)
+ {
+ maxHalfChunk += halfChunkMask + 1;
+ if (maxHalfChunk == 0)
+ --maxHalfChunkNegativity;
+ }
+ if (minHalfChunk == maxHalfChunk && minHalfChunkNegativity == maxHalfChunkNegativity)
+ {
+ imin.value &= mask;
+ imax.value &= mask;
+ // sign extend if necessary.
+ imin.negative = (imin.value & ~halfChunkMask) != 0;
+ imax.negative = (imax.value & ~halfChunkMask) != 0;
+ halfChunkMask += 1;
+ imin.value = (imin.value ^ halfChunkMask) - halfChunkMask;
+ imax.value = (imax.value ^ halfChunkMask) - halfChunkMask;
+ }
+ else
+ {
+ imin = SignExtendedNumber(~halfChunkMask, true);
+ imax = SignExtendedNumber(halfChunkMask, false);
+ }
+ return this;
+ }
+
+ IntRange castUnsigned(uinteger_t mask)
+ {
+ // .... 0x1eff ] [0x1f00 .. 0x1fff] [0 .. 0xff] [0x100 .. 0x1ff] [0x200 ....
+ //
+ // regular unsigned type. We just need to see if ir steps across the
+ // boundary of validRange. If yes, ir will represent the whole validRange,
+ // otherwise, we just take the modulus.
+ // e.g. [0x105, 0x107] & 0xff == [5, 7]
+ // [0x105, 0x207] & 0xff == [0, 0xff]
+ uinteger_t minChunk = imin.value & ~mask;
+ uinteger_t maxChunk = imax.value & ~mask;
+ if (minChunk == maxChunk && imin.negative == imax.negative)
+ {
+ imin.value &= mask;
+ imax.value &= mask;
+ }
+ else
+ {
+ imin.value = 0;
+ imax.value = mask;
+ }
+ imin.negative = imax.negative = false;
+ return this;
+ }
+
+ IntRange castDchar()
+ {
+ // special case for dchar. Casting to dchar means "I'll ignore all
+ // invalid characters."
+ castUnsigned(0xFFFFFFFFUL);
+ if (imin.value > 0x10FFFFUL) // ??
+ imin.value = 0x10FFFFUL; // ??
+ if (imax.value > 0x10FFFFUL)
+ imax.value = 0x10FFFFUL;
+ return this;
+ }
+
+ IntRange _cast(Type type)
+ {
+ if (!type.isintegral() || type.toBasetype().ty == Tvector)
+ return this;
+ else if (!type.isunsigned())
+ return castSigned(type.sizemask());
+ else if (type.toBasetype().ty == Tdchar)
+ return castDchar();
+ else
+ return castUnsigned(type.sizemask());
+ }
+
+ IntRange castUnsigned(Type type)
+ {
+ if (!type.isintegral() || type.toBasetype().ty == Tvector)
+ return castUnsigned(ulong.max);
+ else if (type.toBasetype().ty == Tdchar)
+ return castDchar();
+ else
+ return castUnsigned(type.sizemask());
+ }
+
+ bool contains(IntRange a)
+ {
+ return imin <= a.imin && imax >= a.imax;
+ }
+
+ bool containsZero() const
+ {
+ return (imin.negative && !imax.negative)
+ || (!imin.negative && imin.value == 0);
+ }
+
+ IntRange absNeg() const
+ {
+ if (imax.negative)
+ return this;
+ else if (!imin.negative)
+ return IntRange(-imax, -imin);
+ else
+ {
+ SignExtendedNumber imaxAbsNeg = -imax;
+ return IntRange(imaxAbsNeg < imin ? imaxAbsNeg : imin,
+ SignExtendedNumber(0));
+ }
+ }
+
+ IntRange unionWith(const ref IntRange other) const
+ {
+ return IntRange(imin < other.imin ? imin : other.imin,
+ imax > other.imax ? imax : other.imax);
+ }
+
+ void unionOrAssign(IntRange other, ref bool union_)
+ {
+ if (!union_ || imin > other.imin)
+ imin = other.imin;
+ if (!union_ || imax < other.imax)
+ imax = other.imax;
+ union_ = true;
+ }
+
+ ref const(IntRange) dump(const(char)* funcName, Expression e) const return
+ {
+ printf("[(%c)%#018llx, (%c)%#018llx] @ %s ::: %s\n",
+ imin.negative?'-':'+', cast(ulong)imin.value,
+ imax.negative?'-':'+', cast(ulong)imax.value,
+ funcName, e.toChars());
+ return this;
+ }
+
+ void splitBySign(ref IntRange negRange, ref bool hasNegRange, ref IntRange nonNegRange, ref bool hasNonNegRange) const
+ {
+ hasNegRange = imin.negative;
+ if (hasNegRange)
+ {
+ negRange.imin = imin;
+ negRange.imax = imax.negative ? imax : SignExtendedNumber(-1, true);
+ }
+ hasNonNegRange = !imax.negative;
+ if (hasNonNegRange)
+ {
+ nonNegRange.imin = imin.negative ? SignExtendedNumber(0) : imin;
+ nonNegRange.imax = imax;
+ }
+ }
+
+ IntRange opUnary(string op:"~")() const
+ {
+ return IntRange(~imax, ~imin);
+ }
+
+ IntRange opUnary(string op : "-")()
+ {
+ return IntRange(-imax, -imin);
+ }
+
+ // Credits to Timon Gehr for the algorithms for &, |
+ // https://github.com/tgehr/d-compiler/blob/master/vrange.d
+ IntRange opBinary(string op : "&")(IntRange rhs) const
+ {
+ // unsigned or identical sign bits
+ if ((imin.negative ^ imax.negative) != 1 && (rhs.imin.negative ^ rhs.imax.negative) != 1)
+ {
+ return IntRange(minAnd(this, rhs), maxAnd(this, rhs));
+ }
+
+ IntRange l = IntRange(this);
+ IntRange r = IntRange(rhs);
+
+ // both intervals span [-1,0]
+ if ((imin.negative ^ imax.negative) == 1 && (rhs.imin.negative ^ rhs.imax.negative) == 1)
+ {
+ // cannot be larger than either l.max or r.max, set the other one to -1
+ SignExtendedNumber max = l.imax.value > r.imax.value ? l.imax : r.imax;
+
+ // only negative numbers for minimum
+ l.imax.value = -1;
+ l.imax.negative = true;
+ r.imax.value = -1;
+ r.imax.negative = true;
+
+ return IntRange(minAnd(l, r), max);
+ }
+ else
+ {
+ // only one interval spans [-1,0]
+ if ((l.imin.negative ^ l.imax.negative) == 1)
+ {
+ swap(l, r); // r spans [-1,0]
+ }
+
+ auto minAndNeg = minAnd(l, IntRange(r.imin, SignExtendedNumber(-1)));
+ auto minAndPos = minAnd(l, IntRange(SignExtendedNumber(0), r.imax));
+ auto maxAndNeg = maxAnd(l, IntRange(r.imin, SignExtendedNumber(-1)));
+ auto maxAndPos = maxAnd(l, IntRange(SignExtendedNumber(0), r.imax));
+
+ auto min = minAndNeg < minAndPos ? minAndNeg : minAndPos;
+ auto max = maxAndNeg > maxAndPos ? maxAndNeg : maxAndPos;
+
+ auto range = IntRange(min, max);
+ return range;
+ }
+ }
+
+ // Credits to Timon Gehr for the algorithms for &, |
+ // https://github.com/tgehr/d-compiler/blob/master/vrange.d
+ IntRange opBinary(string op : "|")(IntRange rhs) const
+ {
+ // unsigned or identical sign bits:
+ if ((imin.negative ^ imax.negative) == 0 && (rhs.imin.negative ^ rhs.imax.negative) == 0)
+ {
+ return IntRange(minOr(this, rhs), maxOr(this, rhs));
+ }
+
+ IntRange l = IntRange(this);
+ IntRange r = IntRange(rhs);
+
+ // both intervals span [-1,0]
+ if ((imin.negative ^ imax.negative) == 1 && (rhs.imin.negative ^ rhs.imax.negative) == 1)
+ {
+ // cannot be smaller than either l.min or r.min, set the other one to 0
+ SignExtendedNumber min = l.imin.value < r.imin.value ? l.imin : r.imin;
+
+ // only negative numbers for minimum
+ l.imin.value = 0;
+ l.imin.negative = false;
+ r.imin.value = 0;
+ r.imin.negative = false;
+
+ return IntRange(min, maxOr(l, r));
+ }
+ else
+ {
+ // only one interval spans [-1,0]
+ if ((imin.negative ^ imax.negative) == 1)
+ {
+ swap(l, r); // r spans [-1,0]
+ }
+
+ auto minOrNeg = minOr(l, IntRange(r.imin, SignExtendedNumber(-1)));
+ auto minOrPos = minOr(l, IntRange(SignExtendedNumber(0), r.imax));
+ auto maxOrNeg = maxOr(l, IntRange(r.imin, SignExtendedNumber(-1)));
+ auto maxOrPos = maxOr(l, IntRange(SignExtendedNumber(0), r.imax));
+
+ auto min = minOrNeg < minOrPos ? minOrNeg : minOrPos;
+ auto max = maxOrNeg > maxOrPos ? maxOrNeg : maxOrPos;
+
+ auto range = IntRange(min, max);
+ return range;
+ }
+ }
+
+ IntRange opBinary(string op : "^")(IntRange rhs) const
+ {
+ return this & ~rhs | ~this & rhs;
+ }
+
+ IntRange opBinary(string op : "+")(IntRange rhs)
+ {
+ return IntRange(imin + rhs.imin, imax + rhs.imax);
+ }
+
+ IntRange opBinary(string op : "-")(IntRange rhs)
+ {
+ return IntRange(imin - rhs.imax, imax - rhs.imin);
+ }
+
+ IntRange opBinary(string op : "*")(IntRange rhs)
+ {
+ // [a,b] * [c,d] = [min (ac, ad, bc, bd), max (ac, ad, bc, bd)]
+ SignExtendedNumber[4] bdy;
+ bdy[0] = imin * rhs.imin;
+ bdy[1] = imin * rhs.imax;
+ bdy[2] = imax * rhs.imin;
+ bdy[3] = imax * rhs.imax;
+ return IntRange.fromNumbers4(bdy.ptr);
+ }
+
+ IntRange opBinary(string op : "/")(IntRange rhs)
+ {
+ // Handle divide by 0
+ if (rhs.imax.value == 0 && rhs.imin.value == 0)
+ return widest();
+
+ // Don't treat the whole range as divide by 0 if only one end of a range is 0.
+ // Issue 15289
+ if (rhs.imax.value == 0)
+ {
+ rhs.imax.value--;
+ }
+ else if(rhs.imin.value == 0)
+ {
+ rhs.imin.value++;
+ }
+
+ if (!imin.negative && !imax.negative && !rhs.imin.negative && !rhs.imax.negative)
+ {
+ return IntRange(imin / rhs.imax, imax / rhs.imin);
+ }
+ else
+ {
+ // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)]
+ SignExtendedNumber[4] bdy;
+ bdy[0] = imin / rhs.imin;
+ bdy[1] = imin / rhs.imax;
+ bdy[2] = imax / rhs.imin;
+ bdy[3] = imax / rhs.imax;
+
+ return IntRange.fromNumbers4(bdy.ptr);
+ }
+ }
+
+ IntRange opBinary(string op : "%")(IntRange rhs)
+ {
+ IntRange irNum = this;
+ IntRange irDen = rhs.absNeg();
+
+ /*
+ due to the rules of D (C)'s % operator, we need to consider the cases
+ separately in different range of signs.
+
+ case 1. [500, 1700] % [7, 23] (numerator is always positive)
+ = [0, 22]
+ case 2. [-500, 1700] % [7, 23] (numerator can be negative)
+ = [-22, 22]
+ case 3. [-1700, -500] % [7, 23] (numerator is always negative)
+ = [-22, 0]
+
+ the number 22 is the maximum absolute value in the denomator's range. We
+ don't care about divide by zero.
+ */
+
+ irDen.imin = irDen.imin + SignExtendedNumber(1);
+ irDen.imax = -irDen.imin;
+
+ if (!irNum.imin.negative)
+ {
+ irNum.imin.value = 0;
+ }
+ else if (irNum.imin < irDen.imin)
+ {
+ irNum.imin = irDen.imin;
+ }
+
+ if (irNum.imax.negative)
+ {
+ irNum.imax.negative = false;
+ irNum.imax.value = 0;
+ }
+ else if (irNum.imax > irDen.imax)
+ {
+ irNum.imax = irDen.imax;
+ }
+
+ return irNum;
+ }
+
+ IntRange opBinary(string op : "<<")(IntRange rhs)
+ {
+ if (rhs.imin.negative)
+ {
+ rhs = IntRange(SignExtendedNumber(0), SignExtendedNumber(64));
+ }
+
+ SignExtendedNumber lower = imin << (imin.negative ? rhs.imax : rhs.imin);
+ SignExtendedNumber upper = imax << (imax.negative ? rhs.imin : rhs.imax);
+
+ return IntRange(lower, upper);
+ }
+
+ IntRange opBinary(string op : ">>")(IntRange rhs)
+ {
+ if (rhs.imin.negative)
+ {
+ rhs = IntRange(SignExtendedNumber(0), SignExtendedNumber(64));
+ }
+
+ SignExtendedNumber lower = imin >> (imin.negative ? rhs.imin : rhs.imax);
+ SignExtendedNumber upper = imax >> (imax.negative ? rhs.imax : rhs.imin);
+
+ return IntRange(lower, upper);
+ }
+
+ IntRange opBinary(string op : ">>>")(IntRange rhs)
+ {
+ if (rhs.imin.negative)
+ {
+ rhs = IntRange(SignExtendedNumber(0), SignExtendedNumber(64));
+ }
+
+ return IntRange(imin >> rhs.imax, imax >> rhs.imin);
+ }
+
+ IntRange opBinary(string op : "^^")(IntRange rhs)
+ {
+ // Not yet implemented
+ assert(0);
+ }
+
+private:
+ // Credits to Timon Gehr maxOr, minOr, maxAnd, minAnd
+ // https://github.com/tgehr/d-compiler/blob/master/vrange.d
+ static SignExtendedNumber maxOr(const IntRange lhs, const IntRange rhs)
+ {
+ uinteger_t x = 0;
+ auto sign = false;
+ auto xor = lhs.imax.value ^ rhs.imax.value;
+ auto and = lhs.imax.value & rhs.imax.value;
+ auto lhsc = IntRange(lhs);
+ auto rhsc = IntRange(rhs);
+
+ // Sign bit not part of the .value so we need an extra iteration
+ if (lhsc.imax.negative ^ rhsc.imax.negative)
+ {
+ sign = true;
+ if (lhsc.imax.negative)
+ {
+ if (!lhsc.imin.negative)
+ {
+ lhsc.imin.value = 0;
+ }
+ if (!rhsc.imin.negative)
+ {
+ rhsc.imin.value = 0;
+ }
+ }
+ }
+ else if (lhsc.imin.negative & rhsc.imin.negative)
+ {
+ sign = true;
+ }
+ else if (lhsc.imax.negative & rhsc.imax.negative)
+ {
+ return SignExtendedNumber(-1, false);
+ }
+
+ for (uinteger_t d = 1LU << (8 * uinteger_t.sizeof - 1); d; d >>= 1)
+ {
+ if (xor & d)
+ {
+ x |= d;
+ if (lhsc.imax.value & d)
+ {
+ if (~lhsc.imin.value & d)
+ {
+ lhsc.imin.value = 0;
+ }
+ }
+ else
+ {
+ if (~rhsc.imin.value & d)
+ {
+ rhsc.imin.value = 0;
+ }
+ }
+ }
+ else if (lhsc.imin.value & rhsc.imin.value & d)
+ {
+ x |= d;
+ }
+ else if (and & d)
+ {
+ x |= (d << 1) - 1;
+ break;
+ }
+ }
+
+ auto range = SignExtendedNumber(x, sign);
+ return range;
+ }
+
+ // Credits to Timon Gehr maxOr, minOr, maxAnd, minAnd
+ // https://github.com/tgehr/d-compiler/blob/master/vrange.d
+ static SignExtendedNumber minOr(const IntRange lhs, const IntRange rhs)
+ {
+ return ~maxAnd(~lhs, ~rhs);
+ }
+
+ // Credits to Timon Gehr maxOr, minOr, maxAnd, minAnd
+ // https://github.com/tgehr/d-compiler/blob/master/vrange.d
+ static SignExtendedNumber maxAnd(const IntRange lhs, const IntRange rhs)
+ {
+ uinteger_t x = 0;
+ bool sign = false;
+ auto lhsc = IntRange(lhs);
+ auto rhsc = IntRange(rhs);
+
+ if (lhsc.imax.negative & rhsc.imax.negative)
+ {
+ sign = true;
+ }
+
+ for (uinteger_t d = 1LU << (8 * uinteger_t.sizeof - 1); d; d >>= 1)
+ {
+ if (lhsc.imax.value & rhsc.imax.value & d)
+ {
+ x |= d;
+ if (~lhsc.imin.value & d)
+ {
+ lhsc.imin.value = 0;
+ }
+ if (~rhsc.imin.value & d)
+ {
+ rhsc.imin.value = 0;
+ }
+ }
+ else if (~lhsc.imin.value & d && lhsc.imax.value & d)
+ {
+ lhsc.imax.value |= d - 1;
+ }
+ else if (~rhsc.imin.value & d && rhsc.imax.value & d)
+ {
+ rhsc.imax.value |= d - 1;
+ }
+ }
+
+ auto range = SignExtendedNumber(x, sign);
+ return range;
+ }
+
+ // Credits to Timon Gehr maxOr, minOr, maxAnd, minAnd
+ // https://github.com/tgehr/d-compiler/blob/master/vrange.d
+ static SignExtendedNumber minAnd(const IntRange lhs, const IntRange rhs)
+ {
+ return ~maxOr(~lhs, ~rhs);
+ }
+
+ static swap(ref IntRange a, ref IntRange b)
+ {
+ auto aux = a;
+ a = b;
+ b = aux;
+ }
+}
diff --git a/gcc/d/dmd/json.c b/gcc/d/dmd/json.c
deleted file mode 100644
index 832e559a875..00000000000
--- a/gcc/d/dmd/json.c
+++ /dev/null
@@ -1,888 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/json.c
- */
-
-// This implements the JSON capability.
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "dsymbol.h"
-#include "template.h"
-#include "aggregate.h"
-#include "declaration.h"
-#include "enum.h"
-#include "module.h"
-#include "json.h"
-#include "mtype.h"
-#include "attrib.h"
-#include "cond.h"
-#include "init.h"
-#include "import.h"
-#include "id.h"
-#include "hdrgen.h"
-
-class ToJsonVisitor : public Visitor
-{
-public:
- OutBuffer *buf;
- int indentLevel;
- const char *filename;
-
- ToJsonVisitor(OutBuffer *buf)
- : buf(buf), indentLevel(0), filename(NULL)
- {
- }
-
- void indent()
- {
- if (buf->length() >= 1 &&
- buf->slice().ptr[buf->length() - 1] == '\n')
- for (int i = 0; i < indentLevel; i++)
- buf->writeByte(' ');
- }
-
- void removeComma()
- {
- if (buf->length() >= 2 &&
- buf->slice().ptr[buf->length() - 2] == ',' &&
- (buf->slice().ptr[buf->length() - 1] == '\n' || buf->slice().ptr[buf->length() - 1] == ' '))
- buf->setsize(buf->length() - 2);
- }
-
- void comma()
- {
- if (indentLevel > 0)
- buf->writestring(",\n");
- }
-
- void stringStart()
- {
- buf->writeByte('\"');
- }
-
- void stringEnd()
- {
- buf->writeByte('\"');
- }
-
- void stringPart(const char *s)
- {
- for (; *s; s++)
- {
- utf8_t c = (utf8_t) *s;
- switch (c)
- {
- case '\n':
- buf->writestring("\\n");
- break;
-
- case '\r':
- buf->writestring("\\r");
- break;
-
- case '\t':
- buf->writestring("\\t");
- break;
-
- case '\"':
- buf->writestring("\\\"");
- break;
-
- case '\\':
- buf->writestring("\\\\");
- break;
-
- case '\b':
- buf->writestring("\\b");
- break;
-
- case '\f':
- buf->writestring("\\f");
- break;
-
- default:
- if (c < 0x20)
- buf->printf("\\u%04x", c);
- else
- {
- // Note that UTF-8 chars pass through here just fine
- buf->writeByte(c);
- }
- break;
- }
- }
- }
-
- // Json value functions
-
- /*********************************
- * Encode string into buf, and wrap it in double quotes.
- */
- void value(const char *s)
- {
- stringStart();
- stringPart(s);
- stringEnd();
- }
-
- void value(int value)
- {
- buf->printf("%d", value);
- }
-
- void valueBool(bool value)
- {
- buf->writestring(value ? "true" : "false");
- }
-
- /*********************************
- * Item is an intented value and a comma, for use in arrays
- */
- void item(const char *s)
- {
- indent();
- value(s);
- comma();
- }
-
- void item(int i)
- {
- indent();
- value(i);
- comma();
- }
-
- void itemBool(bool b)
- {
- indent();
- valueBool(b);
- comma();
- }
-
-
- // Json array functions
-
- void arrayStart()
- {
- indent();
- buf->writestring("[\n");
- indentLevel++;
- }
-
- void arrayEnd()
- {
- indentLevel--;
- removeComma();
- if (buf->length() >= 2 &&
- buf->slice().ptr[buf->length() - 2] == '[' &&
- buf->slice().ptr[buf->length() - 1] == '\n')
- buf->setsize(buf->length() - 1);
- else if (!(buf->length() >= 1 &&
- buf->slice().ptr[buf->length() - 1] == '['))
- {
- buf->writestring("\n");
- indent();
- }
- buf->writestring("]");
- comma();
- }
-
-
- // Json object functions
-
- void objectStart()
- {
- indent();
- buf->writestring("{\n");
- indentLevel++;
- }
-
- void objectEnd()
- {
- indentLevel--;
- removeComma();
- if (buf->length() >= 2 &&
- buf->slice().ptr[buf->length() - 2] == '{' &&
- buf->slice().ptr[buf->length() - 1] == '\n')
- buf->setsize(buf->length() - 1);
- else
- {
- buf->writestring("\n");
- indent();
- }
- buf->writestring("}");
- comma();
- }
-
- // Json object property functions
-
- void propertyStart(const char *name)
- {
- indent();
- value(name);
- buf->writestring(" : ");
- }
-
- void property(const char *name, const char *s)
- {
- if (s == NULL) return;
-
- propertyStart(name);
- value(s);
- comma();
- }
-
- void property(const char *name, int i)
- {
- propertyStart(name);
- value(i);
- comma();
- }
-
- void propertyBool(const char *name, bool b)
- {
- propertyStart(name);
- valueBool(b);
- comma();
- }
-
-
- void property(const char *name, TRUST trust)
- {
- switch (trust)
- {
- case TRUSTdefault:
- // Should not be printed
- //property(name, "default");
- break;
- case TRUSTsystem:
- property(name, "system");
- break;
- case TRUSTtrusted:
- property(name, "trusted");
- break;
- case TRUSTsafe:
- property(name, "safe");
- break;
- default:
- assert(false);
- }
- }
-
- void property(const char *name, PURE purity)
- {
- switch (purity)
- {
- case PUREimpure:
- // Should not be printed
- //property(name, "impure");
- break;
- case PUREweak:
- property(name, "weak");
- break;
- case PUREconst:
- property(name, "const");
- break;
- case PUREstrong:
- property(name, "strong");
- break;
- case PUREfwdref:
- property(name, "fwdref");
- break;
- default:
- assert(false);
- }
- }
-
- void property(const char *name, LINK linkage)
- {
- switch (linkage)
- {
- case LINKdefault:
- // Should not be printed
- //property(name, "default");
- break;
- case LINKd:
- // Should not be printed
- //property(name, "d");
- break;
- case LINKc:
- property(name, "c");
- break;
- case LINKcpp:
- property(name, "cpp");
- break;
- case LINKwindows:
- property(name, "windows");
- break;
- default:
- assert(false);
- }
- }
-
- void propertyStorageClass(const char *name, StorageClass stc)
- {
- stc &= STCStorageClass;
- if (stc)
- {
- propertyStart(name);
- arrayStart();
-
- while (stc)
- {
- const char *p = stcToChars(stc);
- assert(p);
- item(p);
- }
-
- arrayEnd();
- }
- }
-
- void property(const char *linename, const char *charname, Loc *loc)
- {
- if (loc)
- {
- const char *filename = loc->filename;
- if (filename)
- {
- if (!this->filename || strcmp(filename, this->filename))
- {
- this->filename = filename;
- property("file", filename);
- }
- }
-
- if (loc->linnum)
- {
- property(linename, loc->linnum);
- if (loc->charnum)
- property(charname, loc->charnum);
- }
- }
- }
-
- void property(const char *name, Type *type)
- {
- if (type)
- {
- property(name, type->toChars());
- }
- }
-
- void property(const char *name, const char *deconame, Type *type)
- {
- if (type)
- {
- if (type->deco)
- property(deconame, type->deco);
- else
- property(name, type->toChars());
- }
- }
-
- void property(const char *name, Parameters *parameters)
- {
- if (parameters == NULL || parameters->length == 0)
- return;
-
- propertyStart(name);
- arrayStart();
-
- if (parameters)
- {
- for (size_t i = 0; i < parameters->length; i++)
- {
- Parameter *p = (*parameters)[i];
- objectStart();
-
- if (p->ident)
- property("name", p->ident->toChars());
-
- property("type", "deco", p->type);
-
- propertyStorageClass("storageClass", p->storageClass);
-
- if (p->defaultArg)
- property("default", p->defaultArg->toChars());
-
-
- objectEnd();
- }
- }
-
- arrayEnd();
- }
-
- /* ========================================================================== */
-
- void jsonProperties(Dsymbol *s)
- {
- if (s->isModule())
- return;
-
- if (!s->isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes
- {
- property("name", s->toChars());
- property("kind", s->kind());
- }
-
- if (s->prot().kind != Prot::public_) // TODO: How about package(names)?
- property("protection", protectionToChars(s->prot().kind));
-
- if (EnumMember *em = s->isEnumMember())
- {
- if (em->origValue)
- property("value", em->origValue->toChars());
- }
-
- property("comment", (const char *)s->comment);
-
- property("line", "char", &s->loc);
- }
-
- void jsonProperties(Declaration *d)
- {
- if (d->storage_class & STClocal)
- return;
- jsonProperties((Dsymbol *)d);
-
- propertyStorageClass("storageClass", d->storage_class);
-
- property("type", "deco", d->type);
-
- // Emit originalType if it differs from type
- if (d->type != d->originalType && d->originalType)
- {
- const char *ostr = d->originalType->toChars();
- if (d->type)
- {
- const char *tstr = d->type->toChars();
- if (strcmp(tstr, ostr))
- {
- //printf("tstr = %s, ostr = %s\n", tstr, ostr);
- property("originalType", ostr);
- }
- }
- else
- property("originalType", ostr);
- }
- }
-
- void jsonProperties(TemplateDeclaration *td)
- {
- jsonProperties((Dsymbol *)td);
-
- if (td->onemember && td->onemember->isCtorDeclaration())
- property("name", "this"); // __ctor -> this
- else
- property("name", td->ident->toChars()); // Foo(T) -> Foo
- }
-
- /* ========================================================================== */
-
- void visit(Dsymbol *)
- {
- }
-
- void visit(Module *s)
- {
- objectStart();
-
- if (s->md)
- property("name", s->md->toChars());
-
- property("kind", s->kind());
-
- filename = s->srcfile->toChars();
- property("file", filename);
-
- property("comment", (const char *)s->comment);
-
- propertyStart("members");
- arrayStart();
- for (size_t i = 0; i < s->members->length; i++)
- {
- (*s->members)[i]->accept(this);
- }
- arrayEnd();
-
- objectEnd();
- }
-
- void visit(Import *s)
- {
- if (s->id == Id::object)
- return;
-
- objectStart();
-
- propertyStart("name");
- stringStart();
- if (s->packages && s->packages->length)
- {
- for (size_t i = 0; i < s->packages->length; i++)
- {
- Identifier *pid = (*s->packages)[i];
- stringPart(pid->toChars());
- buf->writeByte('.');
- }
- }
- stringPart(s->id->toChars());
- stringEnd();
- comma();
-
- property("kind", s->kind());
- property("comment", (const char *)s->comment);
- property("line", "char", &s->loc);
- if (s->prot().kind != Prot::public_)
- property("protection", protectionToChars(s->prot().kind));
- if (s->aliasId)
- property("alias", s->aliasId->toChars());
-
- bool hasRenamed = false;
- bool hasSelective = false;
- for (size_t i = 0; i < s->aliases.length; i++)
- {
- // avoid empty "renamed" and "selective" sections
- if (hasRenamed && hasSelective)
- break;
- else if (s->aliases[i])
- hasRenamed = true;
- else
- hasSelective = true;
- }
-
- if (hasRenamed)
- {
- // import foo : alias1 = target1;
- propertyStart("renamed");
- objectStart();
- for (size_t i = 0; i < s->aliases.length; i++)
- {
- Identifier *name = s->names[i];
- Identifier *alias = s->aliases[i];
- if (alias) property(alias->toChars(), name->toChars());
- }
- objectEnd();
- }
-
- if (hasSelective)
- {
- // import foo : target1;
- propertyStart("selective");
- arrayStart();
- for (size_t i = 0; i < s->names.length; i++)
- {
- Identifier *name = s->names[i];
- if (!s->aliases[i]) item(name->toChars());
- }
- arrayEnd();
- }
-
- objectEnd();
- }
-
- void visit(AttribDeclaration *d)
- {
- Dsymbols *ds = d->include(NULL);
-
- if (ds)
- {
- for (size_t i = 0; i < ds->length; i++)
- {
- Dsymbol *s = (*ds)[i];
- s->accept(this);
- }
- }
- }
-
- void visit(ConditionalDeclaration *d)
- {
- if (d->condition->inc)
- {
- visit((AttribDeclaration *)d);
- }
- }
-
- void visit(TypeInfoDeclaration *) {}
- void visit(PostBlitDeclaration *) {}
-
- void visit(Declaration *d)
- {
- objectStart();
-
- //property("unknown", "declaration");
-
- jsonProperties(d);
-
- objectEnd();
- }
-
- void visit(AggregateDeclaration *d)
- {
- objectStart();
-
- jsonProperties(d);
-
- ClassDeclaration *cd = d->isClassDeclaration();
- if (cd)
- {
- if (cd->baseClass && cd->baseClass->ident != Id::Object)
- {
- property("base", cd->baseClass->toPrettyChars(true));
- }
- if (cd->interfaces.length)
- {
- propertyStart("interfaces");
- arrayStart();
- for (size_t i = 0; i < cd->interfaces.length; i++)
- {
- BaseClass *b = cd->interfaces.ptr[i];
- item(b->sym->toPrettyChars(true));
- }
- arrayEnd();
- }
- }
-
- if (d->members)
- {
- propertyStart("members");
- arrayStart();
- for (size_t i = 0; i < d->members->length; i++)
- {
- Dsymbol *s = (*d->members)[i];
- s->accept(this);
- }
- arrayEnd();
- }
-
- objectEnd();
- }
-
- void visit(FuncDeclaration *d)
- {
- objectStart();
-
- jsonProperties(d);
-
- TypeFunction *tf = (TypeFunction *)d->type;
- if (tf && tf->ty == Tfunction)
- property("parameters", tf->parameterList.parameters);
-
- property("endline", "endchar", &d->endloc);
-
- if (d->foverrides.length)
- {
- propertyStart("overrides");
- arrayStart();
- for (size_t i = 0; i < d->foverrides.length; i++)
- {
- FuncDeclaration *fd = d->foverrides[i];
- item(fd->toPrettyChars());
- }
- arrayEnd();
- }
-
- if (d->fdrequire)
- {
- propertyStart("in");
- d->fdrequire->accept(this);
- }
-
- if (d->fdensure)
- {
- propertyStart("out");
- d->fdensure->accept(this);
- }
-
- objectEnd();
- }
-
- void visit(TemplateDeclaration *d)
- {
- objectStart();
-
- // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one
- property("kind", "template");
-
- jsonProperties(d);
-
- propertyStart("parameters");
- arrayStart();
- for (size_t i = 0; i < d->parameters->length; i++)
- {
- TemplateParameter *s = (*d->parameters)[i];
- objectStart();
-
- property("name", s->ident->toChars());
-
- TemplateTypeParameter *type = s->isTemplateTypeParameter();
- if (type)
- {
- if (s->isTemplateThisParameter())
- property("kind", "this");
- else
- property("kind", "type");
- property("type", "deco", type->specType);
-
- property("default", "defaultDeco", type->defaultType);
- }
-
- TemplateValueParameter *value = s->isTemplateValueParameter();
- if (value)
- {
- property("kind", "value");
-
- property("type", "deco", value->valType);
-
- if (value->specValue)
- property("specValue", value->specValue->toChars());
-
- if (value->defaultValue)
- property("defaultValue", value->defaultValue->toChars());
- }
-
- TemplateAliasParameter *alias = s->isTemplateAliasParameter();
- if (alias)
- {
- property("kind", "alias");
-
- property("type", "deco", alias->specType);
-
- if (alias->specAlias)
- property("specAlias", alias->specAlias->toChars());
-
- if (alias->defaultAlias)
- property("defaultAlias", alias->defaultAlias->toChars());
- }
-
- TemplateTupleParameter *tuple = s->isTemplateTupleParameter();
- if (tuple)
- {
- property("kind", "tuple");
- }
-
- objectEnd();
- }
- arrayEnd();
-
- Expression *expression = d->constraint;
- if (expression)
- {
- property("constraint", expression->toChars());
- }
-
- propertyStart("members");
- arrayStart();
- for (size_t i = 0; i < d->members->length; i++)
- {
- Dsymbol *s = (*d->members)[i];
- s->accept(this);
- }
- arrayEnd();
-
- objectEnd();
- }
-
- void visit(EnumDeclaration *d)
- {
- if (d->isAnonymous())
- {
- if (d->members)
- {
- for (size_t i = 0; i < d->members->length; i++)
- {
- Dsymbol *s = (*d->members)[i];
- s->accept(this);
- }
- }
- return;
- }
-
- objectStart();
-
- jsonProperties(d);
-
- property("base", "baseDeco", d->memtype);
-
- if (d->members)
- {
- propertyStart("members");
- arrayStart();
- for (size_t i = 0; i < d->members->length; i++)
- {
- Dsymbol *s = (*d->members)[i];
- s->accept(this);
- }
- arrayEnd();
- }
-
- objectEnd();
- }
-
- void visit(EnumMember *s)
- {
- objectStart();
-
- jsonProperties((Dsymbol*)s);
-
- property("type", "deco", s->origType);
-
- objectEnd();
- }
-
- void visit(VarDeclaration *d)
- {
- if (d->storage_class & STClocal)
- return;
- objectStart();
-
- jsonProperties(d);
-
- if (d->_init)
- property("init", d->_init->toChars());
-
- if (d->isField())
- property("offset", d->offset);
-
- if (d->alignment && d->alignment != STRUCTALIGN_DEFAULT)
- property("align", d->alignment);
-
- objectEnd();
- }
-
- void visit(TemplateMixin *d)
- {
- objectStart();
-
- jsonProperties(d);
-
- objectEnd();
- }
-};
-
-
-void json_generate(OutBuffer *buf, Modules *modules)
-{
- ToJsonVisitor json(buf);
-
- json.arrayStart();
- for (size_t i = 0; i < modules->length; i++)
- {
- Module *m = (*modules)[i];
- if (global.params.verbose)
- message("json gen %s", m->toChars());
- m->accept(&json);
- }
- json.arrayEnd();
- json.removeComma();
-}
diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d
new file mode 100644
index 00000000000..bfd31bc1d13
--- /dev/null
+++ b/gcc/d/dmd/json.d
@@ -0,0 +1,1085 @@
+/**
+ * Code for generating .json descriptions of the module when passing the `-X` flag to dmd.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/json.d, _json.d)
+ * Documentation: https://dlang.org/phobos/dmd_json.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/json.d
+ */
+
+module dmd.json;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.cond;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dmodule;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.target;
+import dmd.visitor;
+
+version(Windows) {
+ extern (C) char* getcwd(char* buffer, size_t maxlen);
+} else {
+ import core.sys.posix.unistd : getcwd;
+}
+
+private extern (C++) final class ToJsonVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ OutBuffer* buf;
+ int indentLevel;
+ const(char)[] filename;
+
+ extern (D) this(OutBuffer* buf)
+ {
+ this.buf = buf;
+ }
+
+
+ void indent()
+ {
+ if (buf.length >= 1 && (*buf)[buf.length - 1] == '\n')
+ for (int i = 0; i < indentLevel; i++)
+ buf.writeByte(' ');
+ }
+
+ void removeComma()
+ {
+ if (buf.length >= 2 && (*buf)[buf.length - 2] == ',' && ((*buf)[buf.length - 1] == '\n' || (*buf)[buf.length - 1] == ' '))
+ buf.setsize(buf.length - 2);
+ }
+
+ void comma()
+ {
+ if (indentLevel > 0)
+ buf.writestring(",\n");
+ }
+
+ void stringStart()
+ {
+ buf.writeByte('\"');
+ }
+
+ void stringEnd()
+ {
+ buf.writeByte('\"');
+ }
+
+ extern(D) void stringPart(const char[] s)
+ {
+ foreach (char c; s)
+ {
+ switch (c)
+ {
+ case '\n':
+ buf.writestring("\\n");
+ break;
+ case '\r':
+ buf.writestring("\\r");
+ break;
+ case '\t':
+ buf.writestring("\\t");
+ break;
+ case '\"':
+ buf.writestring("\\\"");
+ break;
+ case '\\':
+ buf.writestring("\\\\");
+ break;
+ case '\b':
+ buf.writestring("\\b");
+ break;
+ case '\f':
+ buf.writestring("\\f");
+ break;
+ default:
+ if (c < 0x20)
+ buf.printf("\\u%04x", c);
+ else
+ {
+ // Note that UTF-8 chars pass through here just fine
+ buf.writeByte(c);
+ }
+ break;
+ }
+ }
+ }
+
+ // Json value functions
+ /*********************************
+ * Encode string into buf, and wrap it in double quotes.
+ */
+ extern(D) void value(const char[] s)
+ {
+ stringStart();
+ stringPart(s);
+ stringEnd();
+ }
+
+ void value(int value)
+ {
+ if (value < 0)
+ {
+ buf.writeByte('-');
+ value = -value;
+ }
+ buf.print(value);
+ }
+
+ void valueBool(bool value)
+ {
+ buf.writestring(value ? "true" : "false");
+ }
+
+ /*********************************
+ * Item is an intented value and a comma, for use in arrays
+ */
+ extern(D) void item(const char[] s)
+ {
+ indent();
+ value(s);
+ comma();
+ }
+
+ void item(int i)
+ {
+ indent();
+ value(i);
+ comma();
+ }
+
+ void itemBool(const bool b)
+ {
+ indent();
+ valueBool(b);
+ comma();
+ }
+
+ // Json array functions
+ void arrayStart()
+ {
+ indent();
+ buf.writestring("[\n");
+ indentLevel++;
+ }
+
+ void arrayEnd()
+ {
+ indentLevel--;
+ removeComma();
+ if (buf.length >= 2 && (*buf)[buf.length - 2] == '[' && (*buf)[buf.length - 1] == '\n')
+ buf.setsize(buf.length - 1);
+ else if (!(buf.length >= 1 && (*buf)[buf.length - 1] == '['))
+ {
+ buf.writestring("\n");
+ indent();
+ }
+ buf.writestring("]");
+ comma();
+ }
+
+ // Json object functions
+ void objectStart()
+ {
+ indent();
+ buf.writestring("{\n");
+ indentLevel++;
+ }
+
+ void objectEnd()
+ {
+ indentLevel--;
+ removeComma();
+ if (buf.length >= 2 && (*buf)[buf.length - 2] == '{' && (*buf)[buf.length - 1] == '\n')
+ buf.setsize(buf.length - 1);
+ else
+ {
+ buf.writestring("\n");
+ indent();
+ }
+ buf.writestring("}");
+ comma();
+ }
+
+ // Json object property functions
+ extern(D) void propertyStart(const char[] name)
+ {
+ indent();
+ value(name);
+ buf.writestring(" : ");
+ }
+
+ /**
+ Write the given string object property only if `s` is not null.
+
+ Params:
+ name = the name of the object property
+ s = the string value of the object property
+ */
+ extern(D) void property(const char[] name, const char[] s)
+ {
+ if (s is null)
+ return;
+ propertyStart(name);
+ value(s);
+ comma();
+ }
+
+ /**
+ Write the given string object property.
+
+ Params:
+ name = the name of the object property
+ s = the string value of the object property
+ */
+ extern(D) void requiredProperty(const char[] name, const char[] s)
+ {
+ propertyStart(name);
+ if (s is null)
+ buf.writestring("null");
+ else
+ value(s);
+ comma();
+ }
+
+ extern(D) void property(const char[] name, int i)
+ {
+ propertyStart(name);
+ value(i);
+ comma();
+ }
+
+ extern(D) void propertyBool(const char[] name, const bool b)
+ {
+ propertyStart(name);
+ valueBool(b);
+ comma();
+ }
+
+ extern(D) void property(const char[] name, TRUST trust)
+ {
+ final switch (trust)
+ {
+ case TRUST.default_:
+ // Should not be printed
+ //property(name, "default");
+ break;
+ case TRUST.system: return property(name, "system");
+ case TRUST.trusted: return property(name, "trusted");
+ case TRUST.safe: return property(name, "safe");
+ }
+ }
+
+ extern(D) void property(const char[] name, PURE purity)
+ {
+ final switch (purity)
+ {
+ case PURE.impure:
+ // Should not be printed
+ //property(name, "impure");
+ break;
+ case PURE.weak: return property(name, "weak");
+ case PURE.const_: return property(name, "const");
+ case PURE.strong: return property(name, "strong");
+ case PURE.fwdref: return property(name, "fwdref");
+ }
+ }
+
+ extern(D) void property(const char[] name, const LINK linkage)
+ {
+ final switch (linkage)
+ {
+ case LINK.default_:
+ // Should not be printed
+ //property(name, "default");
+ break;
+ case LINK.d:
+ // Should not be printed
+ //property(name, "d");
+ break;
+ case LINK.system:
+ // Should not be printed
+ //property(name, "system");
+ break;
+ case LINK.c: return property(name, "c");
+ case LINK.cpp: return property(name, "cpp");
+ case LINK.windows: return property(name, "windows");
+ case LINK.objc: return property(name, "objc");
+ }
+ }
+
+ extern(D) void propertyStorageClass(const char[] name, StorageClass stc)
+ {
+ stc &= STC.visibleStorageClasses;
+ if (stc)
+ {
+ propertyStart(name);
+ arrayStart();
+ while (stc)
+ {
+ auto p = stcToString(stc);
+ assert(p.length);
+ item(p);
+ }
+ arrayEnd();
+ }
+ }
+
+ extern(D) void property(const char[] linename, const char[] charname, const ref Loc loc)
+ {
+ if (loc.isValid())
+ {
+ if (auto filename = loc.filename.toDString)
+ {
+ if (filename != this.filename)
+ {
+ this.filename = filename;
+ property("file", filename);
+ }
+ }
+ if (loc.linnum)
+ {
+ property(linename, loc.linnum);
+ if (loc.charnum)
+ property(charname, loc.charnum);
+ }
+ }
+ }
+
+ extern(D) void property(const char[] name, Type type)
+ {
+ if (type)
+ {
+ property(name, type.toString());
+ }
+ }
+
+ extern(D) void property(const char[] name, const char[] deconame, Type type)
+ {
+ if (type)
+ {
+ if (type.deco)
+ property(deconame, type.deco.toDString);
+ else
+ property(name, type.toString());
+ }
+ }
+
+ extern(D) void property(const char[] name, Parameters* parameters)
+ {
+ if (parameters is null || parameters.dim == 0)
+ return;
+ propertyStart(name);
+ arrayStart();
+ if (parameters)
+ {
+ for (size_t i = 0; i < parameters.dim; i++)
+ {
+ Parameter p = (*parameters)[i];
+ objectStart();
+ if (p.ident)
+ property("name", p.ident.toString());
+ property("type", "deco", p.type);
+ propertyStorageClass("storageClass", p.storageClass);
+ if (p.defaultArg)
+ property("default", p.defaultArg.toString());
+ objectEnd();
+ }
+ }
+ arrayEnd();
+ }
+
+ /* ========================================================================== */
+ void jsonProperties(Dsymbol s)
+ {
+ if (s.isModule())
+ return;
+ if (!s.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes
+ {
+ property("name", s.toString());
+ if (s.isStaticCtorDeclaration())
+ {
+ property("kind", s.isSharedStaticCtorDeclaration()
+ ? "shared static constructor" : "static constructor");
+ }
+ else if (s.isStaticDtorDeclaration())
+ {
+ property("kind", s.isSharedStaticDtorDeclaration()
+ ? "shared static destructor" : "static destructor");
+ }
+ else
+ property("kind", s.kind.toDString);
+ }
+ // TODO: How about package(names)?
+ property("protection", visibilityToString(s.visible().kind));
+ if (EnumMember em = s.isEnumMember())
+ {
+ if (em.origValue)
+ property("value", em.origValue.toString());
+ }
+ property("comment", s.comment.toDString);
+ property("line", "char", s.loc);
+ }
+
+ void jsonProperties(Declaration d)
+ {
+ if (d.storage_class & STC.local)
+ return;
+ jsonProperties(cast(Dsymbol)d);
+ propertyStorageClass("storageClass", d.storage_class);
+ property("linkage", d.linkage);
+ property("type", "deco", d.type);
+ // Emit originalType if it differs from type
+ if (d.type != d.originalType && d.originalType)
+ {
+ auto ostr = d.originalType.toString();
+ if (d.type)
+ {
+ auto tstr = d.type.toString();
+ if (ostr != tstr)
+ {
+ //printf("tstr = %s, ostr = %s\n", tstr, ostr);
+ property("originalType", ostr);
+ }
+ }
+ else
+ property("originalType", ostr);
+ }
+ }
+
+ void jsonProperties(TemplateDeclaration td)
+ {
+ jsonProperties(cast(Dsymbol)td);
+ if (td.onemember && td.onemember.isCtorDeclaration())
+ property("name", "this"); // __ctor -> this
+ else
+ property("name", td.ident.toString()); // Foo(T) -> Foo
+ }
+
+ /* ========================================================================== */
+ override void visit(Dsymbol s)
+ {
+ }
+
+ override void visit(Module s)
+ {
+ objectStart();
+ if (s.md)
+ property("name", s.md.toString());
+ property("kind", s.kind.toDString);
+ filename = s.srcfile.toString();
+ property("file", filename);
+ property("comment", s.comment.toDString);
+ propertyStart("members");
+ arrayStart();
+ for (size_t i = 0; i < s.members.dim; i++)
+ {
+ (*s.members)[i].accept(this);
+ }
+ arrayEnd();
+ objectEnd();
+ }
+
+ override void visit(Import s)
+ {
+ if (s.id == Id.object)
+ return;
+ objectStart();
+ propertyStart("name");
+ stringStart();
+ foreach (const pid; s.packages){
+ stringPart(pid.toString());
+ buf.writeByte('.');
+ }
+ stringPart(s.id.toString());
+ stringEnd();
+ comma();
+ property("kind", s.kind.toDString);
+ property("comment", s.comment.toDString);
+ property("line", "char", s.loc);
+ if (s.visible().kind != Visibility.Kind.public_)
+ property("protection", visibilityToString(s.visible().kind));
+ if (s.aliasId)
+ property("alias", s.aliasId.toString());
+ bool hasRenamed = false;
+ bool hasSelective = false;
+ for (size_t i = 0; i < s.aliases.dim; i++)
+ {
+ // avoid empty "renamed" and "selective" sections
+ if (hasRenamed && hasSelective)
+ break;
+ else if (s.aliases[i])
+ hasRenamed = true;
+ else
+ hasSelective = true;
+ }
+ if (hasRenamed)
+ {
+ // import foo : alias1 = target1;
+ propertyStart("renamed");
+ objectStart();
+ for (size_t i = 0; i < s.aliases.dim; i++)
+ {
+ const name = s.names[i];
+ const _alias = s.aliases[i];
+ if (_alias)
+ property(_alias.toString(), name.toString());
+ }
+ objectEnd();
+ }
+ if (hasSelective)
+ {
+ // import foo : target1;
+ propertyStart("selective");
+ arrayStart();
+ foreach (i, name; s.names)
+ {
+ if (!s.aliases[i])
+ item(name.toString());
+ }
+ arrayEnd();
+ }
+ objectEnd();
+ }
+
+ override void visit(AttribDeclaration d)
+ {
+ Dsymbols* ds = d.include(null);
+ if (ds)
+ {
+ for (size_t i = 0; i < ds.dim; i++)
+ {
+ Dsymbol s = (*ds)[i];
+ s.accept(this);
+ }
+ }
+ }
+
+ override void visit(ConditionalDeclaration d)
+ {
+ if (d.condition.inc != Include.notComputed)
+ {
+ visit(cast(AttribDeclaration)d);
+ return; // Don't visit the if/else bodies again below
+ }
+ Dsymbols* ds = d.decl ? d.decl : d.elsedecl;
+ for (size_t i = 0; i < ds.dim; i++)
+ {
+ Dsymbol s = (*ds)[i];
+ s.accept(this);
+ }
+ }
+
+ override void visit(TypeInfoDeclaration d)
+ {
+ }
+
+ override void visit(PostBlitDeclaration d)
+ {
+ }
+
+ override void visit(Declaration d)
+ {
+ objectStart();
+ //property("unknown", "declaration");
+ jsonProperties(d);
+ objectEnd();
+ }
+
+ override void visit(AggregateDeclaration d)
+ {
+ objectStart();
+ jsonProperties(d);
+ ClassDeclaration cd = d.isClassDeclaration();
+ if (cd)
+ {
+ if (cd.baseClass && cd.baseClass.ident != Id.Object)
+ {
+ property("base", cd.baseClass.toPrettyChars(true).toDString);
+ }
+ if (cd.interfaces.length)
+ {
+ propertyStart("interfaces");
+ arrayStart();
+ foreach (b; cd.interfaces)
+ {
+ item(b.sym.toPrettyChars(true).toDString);
+ }
+ arrayEnd();
+ }
+ }
+ if (d.members)
+ {
+ propertyStart("members");
+ arrayStart();
+ for (size_t i = 0; i < d.members.dim; i++)
+ {
+ Dsymbol s = (*d.members)[i];
+ s.accept(this);
+ }
+ arrayEnd();
+ }
+ objectEnd();
+ }
+
+ override void visit(FuncDeclaration d)
+ {
+ objectStart();
+ jsonProperties(d);
+ TypeFunction tf = cast(TypeFunction)d.type;
+ if (tf && tf.ty == Tfunction)
+ property("parameters", tf.parameterList.parameters);
+ property("endline", "endchar", d.endloc);
+ if (d.foverrides.dim)
+ {
+ propertyStart("overrides");
+ arrayStart();
+ for (size_t i = 0; i < d.foverrides.dim; i++)
+ {
+ FuncDeclaration fd = d.foverrides[i];
+ item(fd.toPrettyChars().toDString);
+ }
+ arrayEnd();
+ }
+ if (d.fdrequire)
+ {
+ propertyStart("in");
+ d.fdrequire.accept(this);
+ }
+ if (d.fdensure)
+ {
+ propertyStart("out");
+ d.fdensure.accept(this);
+ }
+ objectEnd();
+ }
+
+ override void visit(TemplateDeclaration d)
+ {
+ objectStart();
+ // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one
+ property("kind", "template");
+ jsonProperties(d);
+ propertyStart("parameters");
+ arrayStart();
+ for (size_t i = 0; i < d.parameters.dim; i++)
+ {
+ TemplateParameter s = (*d.parameters)[i];
+ objectStart();
+ property("name", s.ident.toString());
+
+ if (auto type = s.isTemplateTypeParameter())
+ {
+ if (s.isTemplateThisParameter())
+ property("kind", "this");
+ else
+ property("kind", "type");
+ property("type", "deco", type.specType);
+ property("default", "defaultDeco", type.defaultType);
+ }
+
+ if (auto value = s.isTemplateValueParameter())
+ {
+ property("kind", "value");
+ property("type", "deco", value.valType);
+ if (value.specValue)
+ property("specValue", value.specValue.toString());
+ if (value.defaultValue)
+ property("defaultValue", value.defaultValue.toString());
+ }
+
+ if (auto _alias = s.isTemplateAliasParameter())
+ {
+ property("kind", "alias");
+ property("type", "deco", _alias.specType);
+ if (_alias.specAlias)
+ property("specAlias", _alias.specAlias.toString());
+ if (_alias.defaultAlias)
+ property("defaultAlias", _alias.defaultAlias.toString());
+ }
+
+ if (auto tuple = s.isTemplateTupleParameter())
+ {
+ property("kind", "tuple");
+ }
+
+ objectEnd();
+ }
+ arrayEnd();
+ Expression expression = d.constraint;
+ if (expression)
+ {
+ property("constraint", expression.toString());
+ }
+ propertyStart("members");
+ arrayStart();
+ for (size_t i = 0; i < d.members.dim; i++)
+ {
+ Dsymbol s = (*d.members)[i];
+ s.accept(this);
+ }
+ arrayEnd();
+ objectEnd();
+ }
+
+ override void visit(EnumDeclaration d)
+ {
+ if (d.isAnonymous())
+ {
+ if (d.members)
+ {
+ for (size_t i = 0; i < d.members.dim; i++)
+ {
+ Dsymbol s = (*d.members)[i];
+ s.accept(this);
+ }
+ }
+ return;
+ }
+ objectStart();
+ jsonProperties(d);
+ property("base", "baseDeco", d.memtype);
+ if (d.members)
+ {
+ propertyStart("members");
+ arrayStart();
+ for (size_t i = 0; i < d.members.dim; i++)
+ {
+ Dsymbol s = (*d.members)[i];
+ s.accept(this);
+ }
+ arrayEnd();
+ }
+ objectEnd();
+ }
+
+ override void visit(EnumMember s)
+ {
+ objectStart();
+ jsonProperties(cast(Dsymbol)s);
+ property("type", "deco", s.origType);
+ objectEnd();
+ }
+
+ override void visit(VarDeclaration d)
+ {
+ if (d.storage_class & STC.local)
+ return;
+ objectStart();
+ jsonProperties(d);
+ if (d._init)
+ property("init", d._init.toString());
+ if (d.isField())
+ property("offset", d.offset);
+ if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT)
+ property("align", d.alignment);
+ objectEnd();
+ }
+
+ override void visit(TemplateMixin d)
+ {
+ objectStart();
+ jsonProperties(d);
+ objectEnd();
+ }
+
+ /**
+ Generate an array of module objects that represent the syntax of each
+ "root module".
+
+ Params:
+ modules = array of the "root modules"
+ */
+ private void generateModules(Modules* modules)
+ {
+ arrayStart();
+ if (modules)
+ {
+ foreach (m; *modules)
+ {
+ if (global.params.verbose)
+ message("json gen %s", m.toChars());
+ m.accept(this);
+ }
+ }
+ arrayEnd();
+ }
+
+ /**
+ Generate the "compilerInfo" object which contains information about the compiler
+ such as the filename, version, supported features, etc.
+ */
+ private void generateCompilerInfo()
+ {
+ import dmd.target : target;
+ objectStart();
+ requiredProperty("vendor", global.vendor);
+ requiredProperty("version", global.versionString());
+ property("__VERSION__", global.versionNumber());
+ requiredProperty("interface", determineCompilerInterface());
+ property("size_t", size_t.sizeof);
+ propertyStart("platforms");
+ arrayStart();
+ if (target.os == Target.OS.Windows)
+ {
+ item("windows");
+ }
+ else
+ {
+ item("posix");
+ if (target.os == Target.OS.linux)
+ item("linux");
+ else if (target.os == Target.OS.OSX)
+ item("osx");
+ else if (target.os == Target.OS.FreeBSD)
+ {
+ item("freebsd");
+ item("bsd");
+ }
+ else if (target.os == Target.OS.OpenBSD)
+ {
+ item("openbsd");
+ item("bsd");
+ }
+ else if (target.os == Target.OS.Solaris)
+ {
+ item("solaris");
+ item("bsd");
+ }
+ }
+ arrayEnd();
+
+ propertyStart("architectures");
+ arrayStart();
+ item(target.architectureName);
+ arrayEnd();
+
+ propertyStart("predefinedVersions");
+ arrayStart();
+ if (global.versionids)
+ {
+ foreach (const versionid; *global.versionids)
+ {
+ item(versionid.toString());
+ }
+ }
+ arrayEnd();
+
+ propertyStart("supportedFeatures");
+ {
+ objectStart();
+ scope(exit) objectEnd();
+ propertyBool("includeImports", true);
+ }
+ objectEnd();
+ }
+
+ /**
+ Generate the "buildInfo" object which contains information specific to the
+ current build such as CWD, importPaths, configFile, etc.
+ */
+ private void generateBuildInfo()
+ {
+ objectStart();
+ requiredProperty("cwd", getcwd(null, 0).toDString);
+ requiredProperty("argv0", global.params.argv0);
+ requiredProperty("config", global.inifilename);
+ requiredProperty("libName", global.params.libname);
+
+ propertyStart("importPaths");
+ arrayStart();
+ if (global.params.imppath)
+ {
+ foreach (importPath; *global.params.imppath)
+ {
+ item(importPath.toDString);
+ }
+ }
+ arrayEnd();
+
+ propertyStart("objectFiles");
+ arrayStart();
+ foreach (objfile; global.params.objfiles)
+ {
+ item(objfile.toDString);
+ }
+ arrayEnd();
+
+ propertyStart("libraryFiles");
+ arrayStart();
+ foreach (lib; global.params.libfiles)
+ {
+ item(lib.toDString);
+ }
+ arrayEnd();
+
+ propertyStart("ddocFiles");
+ arrayStart();
+ foreach (ddocFile; global.params.ddocfiles)
+ {
+ item(ddocFile.toDString);
+ }
+ arrayEnd();
+
+ requiredProperty("mapFile", global.params.mapfile);
+ requiredProperty("resourceFile", global.params.resfile);
+ requiredProperty("defFile", global.params.deffile);
+
+ objectEnd();
+ }
+
+ /**
+ Generate the "semantics" object which contains a 'modules' field representing
+ semantic information about all the modules used in the compilation such as
+ module name, isRoot, contentImportedFiles, etc.
+ */
+ private void generateSemantics()
+ {
+ objectStart();
+ propertyStart("modules");
+ arrayStart();
+ foreach (m; Module.amodules)
+ {
+ objectStart();
+ requiredProperty("name", m.md ? m.md.toString() : null);
+ requiredProperty("file", m.srcfile.toString());
+ propertyBool("isRoot", m.isRoot());
+ if(m.contentImportedFiles.dim > 0)
+ {
+ propertyStart("contentImports");
+ arrayStart();
+ foreach (file; m.contentImportedFiles)
+ {
+ item(file.toDString);
+ }
+ arrayEnd();
+ }
+ objectEnd();
+ }
+ arrayEnd();
+ objectEnd();
+ }
+}
+
+extern (C++) void json_generate(OutBuffer* buf, Modules* modules)
+{
+ scope ToJsonVisitor json = new ToJsonVisitor(buf);
+ // write trailing newline
+ scope(exit) buf.writeByte('\n');
+
+ if (global.params.jsonFieldFlags == 0)
+ {
+ // Generate the original format, which is just an array
+ // of modules representing their syntax.
+ json.generateModules(modules);
+ json.removeComma();
+ }
+ else
+ {
+ // Generate the new format which is an object where each
+ // output option is its own field.
+
+ json.objectStart();
+ if (global.params.jsonFieldFlags & JsonFieldFlags.compilerInfo)
+ {
+ json.propertyStart("compilerInfo");
+ json.generateCompilerInfo();
+ }
+ if (global.params.jsonFieldFlags & JsonFieldFlags.buildInfo)
+ {
+ json.propertyStart("buildInfo");
+ json.generateBuildInfo();
+ }
+ if (global.params.jsonFieldFlags & JsonFieldFlags.modules)
+ {
+ json.propertyStart("modules");
+ json.generateModules(modules);
+ }
+ if (global.params.jsonFieldFlags & JsonFieldFlags.semantics)
+ {
+ json.propertyStart("semantics");
+ json.generateSemantics();
+ }
+ json.objectEnd();
+ }
+}
+
+/**
+A string listing the name of each JSON field. Useful for errors messages.
+*/
+enum jsonFieldNames = () {
+ string s;
+ string prefix = "";
+ foreach (idx, enumName; __traits(allMembers, JsonFieldFlags))
+ {
+ static if (idx > 0)
+ {
+ s ~= prefix ~ "`" ~ enumName ~ "`";
+ prefix = ", ";
+ }
+ }
+ return s;
+}();
+
+/**
+Parse the given `fieldName` and return its corresponding JsonFieldFlags value.
+
+Params:
+ fieldName = the field name to parse
+
+Returns: JsonFieldFlags.none on error, otherwise the JsonFieldFlags value
+ corresponding to the given fieldName.
+*/
+extern (C++) JsonFieldFlags tryParseJsonField(const(char)* fieldName)
+{
+ auto fieldNameString = fieldName.toDString();
+ foreach (idx, enumName; __traits(allMembers, JsonFieldFlags))
+ {
+ static if (idx > 0)
+ {
+ if (fieldNameString == enumName)
+ return __traits(getMember, JsonFieldFlags, enumName);
+ }
+ }
+ return JsonFieldFlags.none;
+}
+
+/**
+Determines and returns the compiler interface which is one of `dmd`, `ldc`,
+`gdc` or `sdc`. Returns `null` if no interface can be determined.
+*/
+private extern(D) string determineCompilerInterface()
+{
+ if (global.vendor == "Digital Mars D")
+ return "dmd";
+ if (global.vendor == "LDC")
+ return "ldc";
+ if (global.vendor == "GNU D")
+ return "gdc";
+ if (global.vendor == "SDC")
+ return "sdc";
+ return null;
+}
diff --git a/gcc/d/dmd/json.h b/gcc/d/dmd/json.h
index d680001000f..1311fb3ab2e 100644
--- a/gcc/d/dmd/json.h
+++ b/gcc/d/dmd/json.h
@@ -11,7 +11,9 @@
#pragma once
#include "arraytypes.h"
+#include "globals.h"
struct OutBuffer;
void json_generate(OutBuffer *, Modules *);
+JsonFieldFlags tryParseJsonField(const char *fieldName);
diff --git a/gcc/d/dmd/lambdacomp.d b/gcc/d/dmd/lambdacomp.d
new file mode 100644
index 00000000000..d29bdc13806
--- /dev/null
+++ b/gcc/d/dmd/lambdacomp.d
@@ -0,0 +1,495 @@
+/**
+ * Implements the serialization of a lambda function.
+ *
+ * The serializationis computed by visiting the abstract syntax subtree of the given lambda function.
+ * The serialization is a string which contains the type of the parameters and the string
+ * represantation of the lambda expression.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/lamdbacomp.d, _lambdacomp.d)
+ * Documentation: https://dlang.org/phobos/dmd_lambdacomp.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/lambdacomp.d
+ */
+
+module dmd.lambdacomp;
+
+import core.stdc.stdio;
+import core.stdc.string;
+
+import dmd.astenums;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.expression;
+import dmd.func;
+import dmd.dmangle;
+import dmd.mtype;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.root.stringtable;
+import dmd.dscope;
+import dmd.statement;
+import dmd.tokens;
+import dmd.visitor;
+
+enum LOG = false;
+
+/**
+ * The type of the visited expression.
+ */
+private enum ExpType
+{
+ None,
+ EnumDecl,
+ Arg
+}
+
+/**
+ * Compares 2 lambda functions described by their serialization.
+ *
+ * Params:
+ * l1 = first lambda to be compared
+ * l2 = second lambda to be compared
+ * sc = the scope where the lambdas are compared
+ *
+ * Returns:
+ * `true` if the 2 lambda functions are equal, `false` otherwise
+ */
+bool isSameFuncLiteral(FuncLiteralDeclaration l1, FuncLiteralDeclaration l2, Scope* sc)
+{
+ bool result;
+ if (auto ser1 = getSerialization(l1, sc))
+ {
+ //printf("l1 serialization: %.*s\n", cast(int)ser1.length, &ser1[0]);
+ if (auto ser2 = getSerialization(l2, sc))
+ {
+ //printf("l2 serialization: %.*s\n", cast(int)ser2.length, &ser2[0]);
+ if (ser1 == ser2)
+ result = true;
+ mem.xfree(cast(void*)ser2.ptr);
+ }
+ mem.xfree(cast(void*)ser1.ptr);
+ }
+ return result;
+}
+
+/**
+ * Computes the string representation of a
+ * lambda function described by the subtree starting from a
+ * $(REF dmd, func, FuncLiteralDeclaration).
+ *
+ * Limitations: only IntegerExps, Enums and function
+ * arguments are supported in the lambda function body. The
+ * arguments may be of any type (basic types, user defined types),
+ * except template instantiations. If a function call, a local
+ * variable or a template instance is encountered, the
+ * serialization is dropped and the function is considered
+ * uncomparable.
+ *
+ * Params:
+ * fld = the starting AST node for the lambda function
+ * sc = the scope in which the lambda function is located
+ *
+ * Returns:
+ * The serialization of `fld` allocated with mem.
+ */
+private string getSerialization(FuncLiteralDeclaration fld, Scope* sc)
+{
+ scope serVisitor = new SerializeVisitor(fld.parent._scope);
+ fld.accept(serVisitor);
+ const len = serVisitor.buf.length;
+ if (len == 0)
+ return null;
+
+ return cast(string)serVisitor.buf.extractSlice();
+}
+
+private extern (C++) class SerializeVisitor : SemanticTimeTransitiveVisitor
+{
+private:
+ StringTable!(const(char)[]) arg_hash;
+ Scope* sc;
+ ExpType et;
+ Dsymbol d;
+
+public:
+ OutBuffer buf;
+ alias visit = SemanticTimeTransitiveVisitor.visit;
+
+ this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ /**
+ * Entrypoint of the SerializeVisitor.
+ *
+ * Params:
+ * fld = the lambda function for which the serialization is computed
+ */
+ override void visit(FuncLiteralDeclaration fld)
+ {
+ assert(fld.type.ty != Terror);
+ static if (LOG)
+ printf("FuncLiteralDeclaration: %s\n", fld.toChars());
+
+ TypeFunction tf = cast(TypeFunction) fld.type;
+ const dim = cast(uint) tf.parameterList.length;
+ // Start the serialization by printing the number of
+ // arguments the lambda has.
+ buf.printf("%d:", dim);
+
+ arg_hash._init(dim + 1);
+ // For each argument
+ foreach (i, fparam; tf.parameterList)
+ {
+ if (fparam.ident !is null)
+ {
+ // the variable name is introduced into a hashtable
+ // where the key is the user defined name and the
+ // value is the cannonically name (arg0, arg1 ...)
+ auto key = fparam.ident.toString();
+ OutBuffer value;
+ value.writestring("arg");
+ value.print(i);
+ arg_hash.insert(key, value.extractSlice());
+ // and the type of the variable is serialized.
+ fparam.accept(this);
+ }
+ }
+
+ // Now the function body can be serialized.
+ ReturnStatement rs = fld.fbody.endsWithReturnStatement();
+ if (rs && rs.exp)
+ {
+ rs.exp.accept(this);
+ }
+ else
+ {
+ buf.setsize(0);
+ }
+ }
+
+ override void visit(DotIdExp exp)
+ {
+ static if (LOG)
+ printf("DotIdExp: %s\n", exp.toChars());
+ if (buf.length == 0)
+ return;
+
+ // First we need to see what kind of expression e1 is.
+ // It might an enum member (enum.value) or the field of
+ // an argument (argX.value) if the argument is an aggregate
+ // type. This is reported through the et variable.
+ exp.e1.accept(this);
+ if (buf.length == 0)
+ return;
+
+ if (et == ExpType.EnumDecl)
+ {
+ Dsymbol s = d.search(exp.loc, exp.ident);
+ if (s)
+ {
+ if (auto em = s.isEnumMember())
+ {
+ em.value.accept(this);
+ }
+ et = ExpType.None;
+ d = null;
+ }
+ }
+
+ else if (et == ExpType.Arg)
+ {
+ buf.setsize(buf.length -1);
+ buf.writeByte('.');
+ buf.writestring(exp.ident.toString());
+ buf.writeByte('_');
+ }
+ }
+
+ bool checkArgument(const(char)* id)
+ {
+ // The identifier may be an argument
+ auto stringtable_value = arg_hash.lookup(id, strlen(id));
+ if (stringtable_value)
+ {
+ // In which case we need to update the serialization accordingly
+ const(char)[] gen_id = stringtable_value.value;
+ buf.write(gen_id);
+ buf.writeByte('_');
+ et = ExpType.Arg;
+ return true;
+ }
+ return false;
+ }
+
+ override void visit(IdentifierExp exp)
+ {
+ static if (LOG)
+ printf("IdentifierExp: %s\n", exp.toChars());
+
+ if (buf.length == 0)
+ return;
+
+ auto id = exp.ident.toChars();
+
+ // If it's not an argument
+ if (!checkArgument(id))
+ {
+ // we must check what the identifier expression is.
+ Dsymbol scopesym;
+ Dsymbol s = sc.search(exp.loc, exp.ident, &scopesym);
+ if (s)
+ {
+ auto v = s.isVarDeclaration();
+ // If it's a VarDeclaration, it must be a manifest constant
+ if (v && (v.storage_class & STC.manifest))
+ {
+ v.getConstInitializer.accept(this);
+ }
+ else if (auto em = s.isEnumDeclaration())
+ {
+ d = em;
+ et = ExpType.EnumDecl;
+ }
+ else if (auto fd = s.isFuncDeclaration())
+ {
+ writeMangledName(fd);
+ }
+ // For anything else, the function is deemed uncomparable
+ else
+ {
+ buf.setsize(0);
+ }
+ }
+ // If it's an unknown symbol, consider the function incomparable
+ else
+ {
+ buf.setsize(0);
+ }
+ }
+ }
+
+ override void visit(DotVarExp exp)
+ {
+ static if (LOG)
+ printf("DotVarExp: %s, var: %s, e1: %s\n", exp.toChars(),
+ exp.var.toChars(), exp.e1.toChars());
+
+ exp.e1.accept(this);
+ if (buf.length == 0)
+ return;
+
+ buf.setsize(buf.length -1);
+ buf.writeByte('.');
+ buf.writestring(exp.var.toChars());
+ buf.writeByte('_');
+ }
+
+ override void visit(VarExp exp)
+ {
+ static if (LOG)
+ printf("VarExp: %s, var: %s\n", exp.toChars(), exp.var.toChars());
+
+ if (buf.length == 0)
+ return;
+
+ auto id = exp.var.ident.toChars();
+ if (!checkArgument(id))
+ {
+ buf.setsize(0);
+ }
+ }
+
+ // serialize function calls
+ override void visit(CallExp exp)
+ {
+ static if (LOG)
+ printf("CallExp: %s\n", exp.toChars());
+
+ if (buf.length == 0)
+ return;
+
+ if (!exp.f)
+ {
+ exp.e1.accept(this);
+ }
+ else
+ {
+ writeMangledName(exp.f);
+ }
+
+ buf.writeByte('(');
+ foreach (arg; *(exp.arguments))
+ {
+ arg.accept(this);
+ }
+ buf.writeByte(')');
+ }
+
+ override void visit(UnaExp exp)
+ {
+ if (buf.length == 0)
+ return;
+
+ buf.writeByte('(');
+ buf.writestring(Token.toString(exp.op));
+ exp.e1.accept(this);
+ if (buf.length != 0)
+ buf.writestring(")_");
+ }
+
+ override void visit(IntegerExp exp)
+ {
+ if (buf.length == 0)
+ return;
+
+ buf.print(exp.toInteger());
+ buf.writeByte('_');
+ }
+
+ override void visit(RealExp exp)
+ {
+ if (buf.length == 0)
+ return;
+
+ buf.writestring(exp.toChars());
+ buf.writeByte('_');
+ }
+
+ override void visit(BinExp exp)
+ {
+ static if (LOG)
+ printf("BinExp: %s\n", exp.toChars());
+
+ if (buf.length == 0)
+ return;
+
+ buf.writeByte('(');
+ buf.writestring(Token.toChars(exp.op));
+
+ exp.e1.accept(this);
+ if (buf.length == 0)
+ return;
+
+ exp.e2.accept(this);
+ if (buf.length == 0)
+ return;
+
+ buf.writeByte(')');
+ }
+
+ override void visit(TypeBasic t)
+ {
+ buf.writestring(t.dstring);
+ buf.writeByte('_');
+ }
+
+ void writeMangledName(Dsymbol s)
+ {
+ if (s)
+ {
+ OutBuffer mangledName;
+ mangleToBuffer(s, &mangledName);
+ buf.writestring(mangledName[]);
+ buf.writeByte('_');
+ }
+ else
+ buf.setsize(0);
+ }
+
+ private bool checkTemplateInstance(T)(T t)
+ if (is(T == TypeStruct) || is(T == TypeClass))
+ {
+ if (t.sym.parent && t.sym.parent.isTemplateInstance())
+ {
+ buf.setsize(0);
+ return true;
+ }
+ return false;
+ }
+
+ override void visit(TypeStruct t)
+ {
+ static if (LOG)
+ printf("TypeStruct: %s\n", t.toChars);
+
+ if (!checkTemplateInstance!TypeStruct(t))
+ writeMangledName(t.sym);
+ }
+
+ override void visit(TypeClass t)
+ {
+ static if (LOG)
+ printf("TypeClass: %s\n", t.toChars());
+
+ if (!checkTemplateInstance!TypeClass(t))
+ writeMangledName(t.sym);
+ }
+
+ override void visit(Parameter p)
+ {
+ if (p.type.ty == Tident
+ && (cast(TypeIdentifier)p.type).ident.toString().length > 3
+ && strncmp((cast(TypeIdentifier)p.type).ident.toChars(), "__T", 3) == 0)
+ {
+ buf.writestring("none_");
+ }
+ else
+ visitType(p.type);
+ }
+
+ override void visit(StructLiteralExp e) {
+ static if (LOG)
+ printf("StructLiteralExp: %s\n", e.toChars);
+
+ auto ty = cast(TypeStruct)e.stype;
+ if (ty)
+ {
+ writeMangledName(ty.sym);
+ auto dim = e.elements.dim;
+ foreach (i; 0..dim)
+ {
+ auto elem = (*e.elements)[i];
+ if (elem)
+ elem.accept(this);
+ else
+ buf.writestring("null_");
+ }
+ }
+ else
+ buf.setsize(0);
+ }
+
+ override void visit(ArrayLiteralExp) { buf.setsize(0); }
+ override void visit(AssocArrayLiteralExp) { buf.setsize(0); }
+ override void visit(MixinExp) { buf.setsize(0); }
+ override void visit(ComplexExp) { buf.setsize(0); }
+ override void visit(DeclarationExp) { buf.setsize(0); }
+ override void visit(DefaultInitExp) { buf.setsize(0); }
+ override void visit(DsymbolExp) { buf.setsize(0); }
+ override void visit(ErrorExp) { buf.setsize(0); }
+ override void visit(FuncExp) { buf.setsize(0); }
+ override void visit(HaltExp) { buf.setsize(0); }
+ override void visit(IntervalExp) { buf.setsize(0); }
+ override void visit(IsExp) { buf.setsize(0); }
+ override void visit(NewAnonClassExp) { buf.setsize(0); }
+ override void visit(NewExp) { buf.setsize(0); }
+ override void visit(NullExp) { buf.setsize(0); }
+ override void visit(ObjcClassReferenceExp) { buf.setsize(0); }
+ override void visit(OverExp) { buf.setsize(0); }
+ override void visit(ScopeExp) { buf.setsize(0); }
+ override void visit(StringExp) { buf.setsize(0); }
+ override void visit(SymbolExp) { buf.setsize(0); }
+ override void visit(TemplateExp) { buf.setsize(0); }
+ override void visit(ThisExp) { buf.setsize(0); }
+ override void visit(TraitsExp) { buf.setsize(0); }
+ override void visit(TupleExp) { buf.setsize(0); }
+ override void visit(TypeExp) { buf.setsize(0); }
+ override void visit(TypeidExp) { buf.setsize(0); }
+ override void visit(VoidInitExp) { buf.setsize(0); }
+}
diff --git a/gcc/d/dmd/lexer.c b/gcc/d/dmd/lexer.c
deleted file mode 100644
index 3ea932c036e..00000000000
--- a/gcc/d/dmd/lexer.c
+++ /dev/null
@@ -1,2405 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/lexer.c
- */
-
-/* Lexical Analyzer */
-
-#include "root/dsystem.h" // for time() and ctime()
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "lexer.h"
-#include "utf.h"
-#include "identifier.h"
-#include "id.h"
-
-extern int HtmlNamedEntity(const utf8_t *p, size_t length);
-
-#define LS 0x2028 // UTF line separator
-#define PS 0x2029 // UTF paragraph separator
-
-/********************************************
- * Do our own char maps
- */
-
-static unsigned char cmtable[256];
-
-const int CMoctal = 0x1;
-const int CMhex = 0x2;
-const int CMidchar = 0x4;
-
-inline bool isoctal (utf8_t c) { return (cmtable[c] & CMoctal) != 0; }
-inline bool ishex (utf8_t c) { return (cmtable[c] & CMhex) != 0; }
-inline bool isidchar(utf8_t c) { return (cmtable[c] & CMidchar) != 0; }
-
-struct CMTableInitializer
-{
- CMTableInitializer();
-};
-
-static CMTableInitializer cmtableinitializer;
-
-CMTableInitializer::CMTableInitializer()
-{
- for (unsigned c = 0; c < 256; c++)
- {
- if ('0' <= c && c <= '7')
- cmtable[c] |= CMoctal;
- if (isxdigit(c))
- cmtable[c] |= CMhex;
- if (isalnum(c) || c == '_')
- cmtable[c] |= CMidchar;
- }
-}
-
-/*************************** Lexer ********************************************/
-
-OutBuffer Lexer::stringbuffer;
-
-Lexer::Lexer(const char *filename,
- const utf8_t *base, size_t begoffset, size_t endoffset,
- bool doDocComment, bool commentToken)
-{
- scanloc = Loc(filename, 1, 1);
- //printf("Lexer::Lexer(%p,%d)\n",base,length);
- //printf("lexer.filename = %s\n", filename);
- this->token = Token();
- this->token.ptr = NULL;
- this->token.value = TOKreserved;
- this->token.blockComment = NULL;
- this->token.lineComment = NULL;
- this->base = base;
- this->end = base + endoffset;
- p = base + begoffset;
- line = p;
- this->doDocComment = doDocComment;
- this->anyToken = 0;
- this->commentToken = commentToken;
- this->errors = false;
- //initKeywords();
-
- /* If first line starts with '#!', ignore the line
- */
-
- if (p[0] == '#' && p[1] =='!')
- {
- p += 2;
- while (1)
- {
- utf8_t c = *p++;
- switch (c)
- {
- case 0:
- case 0x1A:
- p--;
- /* fall through */
-
- case '\n':
- break;
-
- default:
- continue;
- }
- break;
- }
- endOfLine();
- }
-}
-
-
-void Lexer::endOfLine()
-{
- scanloc.linnum++;
- line = p;
-}
-
-
-void Lexer::error(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::verror(token.loc, format, ap);
- va_end(ap);
- errors = true;
-}
-
-void Lexer::error(Loc loc, const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::verror(loc, format, ap);
- va_end(ap);
- errors = true;
-}
-
-void Lexer::deprecation(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::vdeprecation(token.loc, format, ap);
- va_end(ap);
- if (global.params.useDeprecated == DIAGNOSTICerror)
- errors = true;
-}
-
-TOK Lexer::nextToken()
-{
- if (token.next)
- {
- Token *t = token.next;
- memcpy(&token,t,sizeof(Token));
- t->free();
- }
- else
- {
- scan(&token);
- }
- //token.print();
- return token.value;
-}
-
-Token *Lexer::peek(Token *ct)
-{
- Token *t;
- if (ct->next)
- t = ct->next;
- else
- {
- t = Token::alloc();
- scan(t);
- ct->next = t;
- }
- return t;
-}
-
-/***********************
- * Look ahead at next token's value.
- */
-
-TOK Lexer::peekNext()
-{
- return peek(&token)->value;
-}
-
-/***********************
- * Look 2 tokens ahead at value.
- */
-
-TOK Lexer::peekNext2()
-{
- Token *t = peek(&token);
- return peek(t)->value;
-}
-
-/*********************************
- * tk is on the opening (.
- * Look ahead and return token that is past the closing ).
- */
-
-Token *Lexer::peekPastParen(Token *tk)
-{
- //printf("peekPastParen()\n");
- int parens = 1;
- int curlynest = 0;
- while (1)
- {
- tk = peek(tk);
- //tk->print();
- switch (tk->value)
- {
- case TOKlparen:
- parens++;
- continue;
-
- case TOKrparen:
- --parens;
- if (parens)
- continue;
- tk = peek(tk);
- break;
-
- case TOKlcurly:
- curlynest++;
- continue;
-
- case TOKrcurly:
- if (--curlynest >= 0)
- continue;
- break;
-
- case TOKsemicolon:
- if (curlynest)
- continue;
- break;
-
- case TOKeof:
- break;
-
- default:
- continue;
- }
- return tk;
- }
-}
-
-/****************************
- * Turn next token in buffer into a token.
- */
-
-void Lexer::scan(Token *t)
-{
- unsigned lastLine = scanloc.linnum;
- Loc startLoc;
-
- t->blockComment = NULL;
- t->lineComment = NULL;
- while (1)
- {
- t->ptr = p;
- //printf("p = %p, *p = '%c'\n",p,*p);
- t->loc = loc();
- switch (*p)
- {
- case 0:
- case 0x1A:
- t->value = TOKeof; // end of file
- return;
-
- case ' ':
- case '\t':
- case '\v':
- case '\f':
- p++;
- continue; // skip white space
-
- case '\r':
- p++;
- if (*p != '\n') // if CR stands by itself
- endOfLine();
- continue; // skip white space
-
- case '\n':
- p++;
- endOfLine();
- continue; // skip white space
-
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- t->value = number(t);
- return;
-
- case '\'':
- t->value = charConstant(t);
- return;
-
- case 'r':
- if (p[1] != '"')
- goto case_ident;
- p++;
- /* fall through */
- case '`':
- t->value = wysiwygStringConstant(t, *p);
- return;
-
- case 'x':
- if (p[1] != '"')
- goto case_ident;
- p++;
- t->value = hexStringConstant(t);
- return;
-
- case 'q':
- if (p[1] == '"')
- {
- p++;
- t->value = delimitedStringConstant(t);
- return;
- }
- else if (p[1] == '{')
- {
- p++;
- t->value = tokenStringConstant(t);
- return;
- }
- else
- goto case_ident;
-
- case '"':
- t->value = escapeStringConstant(t);
- return;
-
- case 'a': case 'b': case 'c': case 'd': case 'e':
- case 'f': case 'g': case 'h': case 'i': case 'j':
- case 'k': case 'l': case 'm': case 'n': case 'o':
- case 'p': /*case 'q': case 'r':*/ case 's': case 't':
- case 'u': case 'v': case 'w': /*case 'x':*/ case 'y':
- case 'z':
- case 'A': case 'B': case 'C': case 'D': case 'E':
- case 'F': case 'G': case 'H': case 'I': case 'J':
- case 'K': case 'L': case 'M': case 'N': case 'O':
- case 'P': case 'Q': case 'R': case 'S': case 'T':
- case 'U': case 'V': case 'W': case 'X': case 'Y':
- case 'Z':
- case '_':
- case_ident:
- { utf8_t c;
-
- while (1)
- {
- c = *++p;
- if (isidchar(c))
- continue;
- else if (c & 0x80)
- { const utf8_t *s = p;
- unsigned u = decodeUTF();
- if (isUniAlpha(u))
- continue;
- error("char 0x%04x not allowed in identifier", u);
- p = s;
- }
- break;
- }
-
- Identifier *id = Identifier::idPool((const char *)t->ptr, p - t->ptr);
- t->ident = id;
- t->value = (TOK) id->getValue();
- anyToken = 1;
- if (*t->ptr == '_') // if special identifier token
- {
- static bool initdone = false;
- static char date[11+1];
- static char time[8+1];
- static char timestamp[24+1];
-
- if (!initdone) // lazy evaluation
- {
- initdone = true;
- time_t ct;
- ::time(&ct);
- char *p = ctime(&ct);
- assert(p);
- sprintf(&date[0], "%.6s %.4s", p + 4, p + 20);
- sprintf(&time[0], "%.8s", p + 11);
- sprintf(&timestamp[0], "%.24s", p);
- }
-
- if (id == Id::DATE)
- {
- t->ustring = (utf8_t *)date;
- goto Lstr;
- }
- else if (id == Id::TIME)
- {
- t->ustring = (utf8_t *)time;
- goto Lstr;
- }
- else if (id == Id::VENDOR)
- {
- t->ustring = (utf8_t *)const_cast<char *>(global.vendor.ptr);
- goto Lstr;
- }
- else if (id == Id::TIMESTAMP)
- {
- t->ustring = (utf8_t *)timestamp;
- Lstr:
- t->value = TOKstring;
- t->postfix = 0;
- t->len = (unsigned)strlen((char *)t->ustring);
- }
- else if (id == Id::VERSIONX)
- { unsigned major = 0;
- unsigned minor = 0;
- bool point = false;
-
- for (const char *p = global.version.ptr + 1; 1; p++)
- {
- c = *p;
- if (isdigit((utf8_t)c))
- minor = minor * 10 + c - '0';
- else if (c == '.')
- {
- if (point)
- break; // ignore everything after second '.'
- point = true;
- major = minor;
- minor = 0;
- }
- else
- break;
- }
- t->value = TOKint64v;
- t->uns64value = major * 1000 + minor;
- }
- else if (id == Id::EOFX)
- {
- t->value = TOKeof;
- // Advance scanner to end of file
- while (!(*p == 0 || *p == 0x1A))
- p++;
- }
- }
- //printf("t->value = %d\n",t->value);
- return;
- }
-
- case '/':
- p++;
- switch (*p)
- {
- case '=':
- p++;
- t->value = TOKdivass;
- return;
-
- case '*':
- p++;
- startLoc = loc();
- while (1)
- {
- while (1)
- { utf8_t c = *p;
- switch (c)
- {
- case '/':
- break;
-
- case '\n':
- endOfLine();
- p++;
- continue;
-
- case '\r':
- p++;
- if (*p != '\n')
- endOfLine();
- continue;
-
- case 0:
- case 0x1A:
- error("unterminated /* */ comment");
- p = end;
- t->loc = loc();
- t->value = TOKeof;
- return;
-
- default:
- if (c & 0x80)
- { unsigned u = decodeUTF();
- if (u == PS || u == LS)
- endOfLine();
- }
- p++;
- continue;
- }
- break;
- }
- p++;
- if (p[-2] == '*' && p - 3 != t->ptr)
- break;
- }
- if (commentToken)
- {
- t->loc = startLoc;
- t->value = TOKcomment;
- return;
- }
- else if (doDocComment && t->ptr[2] == '*' && p - 4 != t->ptr)
- { // if /** but not /**/
- getDocComment(t, lastLine == startLoc.linnum);
- }
- continue;
-
- case '/': // do // style comments
- startLoc = loc();
- while (1)
- { utf8_t c = *++p;
- switch (c)
- {
- case '\n':
- break;
-
- case '\r':
- if (p[1] == '\n')
- p++;
- break;
-
- case 0:
- case 0x1A:
- if (commentToken)
- {
- p = end;
- t->loc = startLoc;
- t->value = TOKcomment;
- return;
- }
- if (doDocComment && t->ptr[2] == '/')
- getDocComment(t, lastLine == startLoc.linnum);
- p = end;
- t->loc = loc();
- t->value = TOKeof;
- return;
-
- default:
- if (c & 0x80)
- { unsigned u = decodeUTF();
- if (u == PS || u == LS)
- break;
- }
- continue;
- }
- break;
- }
-
- if (commentToken)
- {
- p++;
- endOfLine();
- t->loc = startLoc;
- t->value = TOKcomment;
- return;
- }
- if (doDocComment && t->ptr[2] == '/')
- getDocComment(t, lastLine == startLoc.linnum);
-
- p++;
- endOfLine();
- continue;
-
- case '+':
- { int nest;
-
- startLoc = loc();
- p++;
- nest = 1;
- while (1)
- { utf8_t c = *p;
- switch (c)
- {
- case '/':
- p++;
- if (*p == '+')
- {
- p++;
- nest++;
- }
- continue;
-
- case '+':
- p++;
- if (*p == '/')
- {
- p++;
- if (--nest == 0)
- break;
- }
- continue;
-
- case '\r':
- p++;
- if (*p != '\n')
- endOfLine();
- continue;
-
- case '\n':
- endOfLine();
- p++;
- continue;
-
- case 0:
- case 0x1A:
- error("unterminated /+ +/ comment");
- p = end;
- t->loc = loc();
- t->value = TOKeof;
- return;
-
- default:
- if (c & 0x80)
- { unsigned u = decodeUTF();
- if (u == PS || u == LS)
- endOfLine();
- }
- p++;
- continue;
- }
- break;
- }
- if (commentToken)
- {
- t->loc = startLoc;
- t->value = TOKcomment;
- return;
- }
- if (doDocComment && t->ptr[2] == '+' && p - 4 != t->ptr)
- { // if /++ but not /++/
- getDocComment(t, lastLine == startLoc.linnum);
- }
- continue;
- }
- default:
- break;
- }
- t->value = TOKdiv;
- return;
-
- case '.':
- p++;
- if (isdigit(*p))
- { /* Note that we don't allow ._1 and ._ as being
- * valid floating point numbers.
- */
- p--;
- t->value = inreal(t);
- }
- else if (p[0] == '.')
- {
- if (p[1] == '.')
- { p += 2;
- t->value = TOKdotdotdot;
- }
- else
- { p++;
- t->value = TOKslice;
- }
- }
- else
- t->value = TOKdot;
- return;
-
- case '&':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKandass;
- }
- else if (*p == '&')
- { p++;
- t->value = TOKandand;
- }
- else
- t->value = TOKand;
- return;
-
- case '|':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKorass;
- }
- else if (*p == '|')
- { p++;
- t->value = TOKoror;
- }
- else
- t->value = TOKor;
- return;
-
- case '-':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKminass;
- }
- else if (*p == '-')
- { p++;
- t->value = TOKminusminus;
- }
- else
- t->value = TOKmin;
- return;
-
- case '+':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKaddass;
- }
- else if (*p == '+')
- { p++;
- t->value = TOKplusplus;
- }
- else
- t->value = TOKadd;
- return;
-
- case '<':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKle; // <=
- }
- else if (*p == '<')
- { p++;
- if (*p == '=')
- { p++;
- t->value = TOKshlass; // <<=
- }
- else
- t->value = TOKshl; // <<
- }
- else if (*p == '>')
- { p++;
- if (*p == '=')
- { p++;
- t->value = TOKleg; // <>=
- }
- else
- t->value = TOKlg; // <>
- }
- else
- t->value = TOKlt; // <
- return;
-
- case '>':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKge; // >=
- }
- else if (*p == '>')
- { p++;
- if (*p == '=')
- { p++;
- t->value = TOKshrass; // >>=
- }
- else if (*p == '>')
- { p++;
- if (*p == '=')
- { p++;
- t->value = TOKushrass; // >>>=
- }
- else
- t->value = TOKushr; // >>>
- }
- else
- t->value = TOKshr; // >>
- }
- else
- t->value = TOKgt; // >
- return;
-
- case '!':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKnotequal; // !=
- }
- else if (*p == '<')
- { p++;
- if (*p == '>')
- { p++;
- if (*p == '=')
- { p++;
- t->value = TOKunord; // !<>=
- }
- else
- t->value = TOKue; // !<>
- }
- else if (*p == '=')
- { p++;
- t->value = TOKug; // !<=
- }
- else
- t->value = TOKuge; // !<
- }
- else if (*p == '>')
- { p++;
- if (*p == '=')
- { p++;
- t->value = TOKul; // !>=
- }
- else
- t->value = TOKule; // !>
- }
- else
- t->value = TOKnot; // !
- return;
-
- case '=':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKequal; // ==
- }
- else if (*p == '>')
- { p++;
- t->value = TOKgoesto; // =>
- }
- else
- t->value = TOKassign; // =
- return;
-
- case '~':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKcatass; // ~=
- }
- else
- t->value = TOKtilde; // ~
- return;
-
- case '^':
- p++;
- if (*p == '^')
- { p++;
- if (*p == '=')
- { p++;
- t->value = TOKpowass; // ^^=
- }
- else
- t->value = TOKpow; // ^^
- }
- else if (*p == '=')
- { p++;
- t->value = TOKxorass; // ^=
- }
- else
- t->value = TOKxor; // ^
- return;
-
- case '(': p++; t->value = TOKlparen; return;
- case ')': p++; t->value = TOKrparen; return;
- case '[': p++; t->value = TOKlbracket; return;
- case ']': p++; t->value = TOKrbracket; return;
- case '{': p++; t->value = TOKlcurly; return;
- case '}': p++; t->value = TOKrcurly; return;
- case '?': p++; t->value = TOKquestion; return;
- case ',': p++; t->value = TOKcomma; return;
- case ';': p++; t->value = TOKsemicolon; return;
- case ':': p++; t->value = TOKcolon; return;
- case '$': p++; t->value = TOKdollar; return;
- case '@': p++; t->value = TOKat; return;
-
- case '*':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKmulass;
- }
- else
- t->value = TOKmul;
- return;
- case '%':
- p++;
- if (*p == '=')
- { p++;
- t->value = TOKmodass;
- }
- else
- t->value = TOKmod;
- return;
-
- case '#':
- {
- p++;
- Token n;
- scan(&n);
- if (n.value == TOKidentifier)
- {
- if (n.ident == Id::line)
- {
- poundLine();
- continue;
- }
- else
- {
- const Loc locx = loc();
- warning(locx, "C preprocessor directive `#%s` is not supported", n.ident->toChars());
- }
- }
- else if (n.value == TOKif)
- {
- error("C preprocessor directive `#if` is not supported, use `version` or `static if`");
- }
- t->value = TOKpound;
- return;
- }
-
- default:
- { unsigned c = *p;
-
- if (c & 0x80)
- { c = decodeUTF();
-
- // Check for start of unicode identifier
- if (isUniAlpha(c))
- goto case_ident;
-
- if (c == PS || c == LS)
- {
- endOfLine();
- p++;
- continue;
- }
- }
- if (c < 0x80 && isprint(c))
- error("character '%c' is not a valid token", c);
- else
- error("character 0x%02x is not a valid token", c);
- p++;
- continue;
- }
- }
- }
-}
-
-/*******************************************
- * Parse escape sequence.
- */
-
-unsigned Lexer::escapeSequence()
-{ unsigned c = *p;
-
- int n;
- int ndigits;
-
- switch (c)
- {
- case '\'':
- case '"':
- case '?':
- case '\\':
- Lconsume:
- p++;
- break;
-
- case 'a': c = 7; goto Lconsume;
- case 'b': c = 8; goto Lconsume;
- case 'f': c = 12; goto Lconsume;
- case 'n': c = 10; goto Lconsume;
- case 'r': c = 13; goto Lconsume;
- case 't': c = 9; goto Lconsume;
- case 'v': c = 11; goto Lconsume;
-
- case 'u':
- ndigits = 4;
- goto Lhex;
- case 'U':
- ndigits = 8;
- goto Lhex;
- case 'x':
- ndigits = 2;
- Lhex:
- p++;
- c = *p;
- if (ishex((utf8_t)c))
- { unsigned v;
-
- n = 0;
- v = 0;
- while (1)
- {
- if (isdigit((utf8_t)c))
- c -= '0';
- else if (islower(c))
- c -= 'a' - 10;
- else
- c -= 'A' - 10;
- v = v * 16 + c;
- c = *++p;
- if (++n == ndigits)
- break;
- if (!ishex((utf8_t)c))
- { error("escape hex sequence has %d hex digits instead of %d", n, ndigits);
- break;
- }
- }
- if (ndigits != 2 && !utf_isValidDchar(v))
- { error("invalid UTF character \\U%08x", v);
- v = '?'; // recover with valid UTF character
- }
- c = v;
- }
- else
- error("undefined escape hex sequence \\%c",c);
- break;
-
- case '&': // named character entity
- for (const utf8_t *idstart = ++p; 1; p++)
- {
- switch (*p)
- {
- case ';':
- c = HtmlNamedEntity(idstart, p - idstart);
- if (c == ~0U)
- { error("unnamed character entity &%.*s;", (int)(p - idstart), idstart);
- c = ' ';
- }
- p++;
- break;
-
- default:
- if (isalpha(*p) ||
- (p != idstart && isdigit(*p)))
- continue;
- error("unterminated named entity &%.*s;", (int)(p - idstart + 1), idstart);
- break;
- }
- break;
- }
- break;
-
- case 0:
- case 0x1A: // end of file
- c = '\\';
- break;
-
- default:
- if (isoctal((utf8_t)c))
- { unsigned v;
-
- n = 0;
- v = 0;
- do
- {
- v = v * 8 + (c - '0');
- c = *++p;
- } while (++n < 3 && isoctal((utf8_t)c));
- c = v;
- if (c > 0xFF)
- error("escape octal sequence \\%03o is larger than \\377", c);
- }
- else
- error("undefined escape sequence \\%c",c);
- break;
- }
- return c;
-}
-
-/**************************************
- */
-
-TOK Lexer::wysiwygStringConstant(Token *t, int tc)
-{
- int c;
- Loc start = loc();
-
- p++;
- stringbuffer.reset();
- while (1)
- {
- c = *p++;
- switch (c)
- {
- case '\n':
- endOfLine();
- break;
-
- case '\r':
- if (*p == '\n')
- continue; // ignore
- c = '\n'; // treat EndOfLine as \n character
- endOfLine();
- break;
-
- case 0:
- case 0x1A:
- error("unterminated string constant starting at %s", start.toChars());
- t->ustring = (utf8_t *)const_cast<char *>("");
- t->len = 0;
- t->postfix = 0;
- return TOKstring;
-
- case '"':
- case '`':
- if (c == tc)
- {
- t->len = (unsigned)stringbuffer.length();
- stringbuffer.writeByte(0);
- t->ustring = (utf8_t *)mem.xmalloc(stringbuffer.length());
- memcpy(t->ustring, stringbuffer.slice().ptr, stringbuffer.length());
- stringPostfix(t);
- return TOKstring;
- }
- break;
-
- default:
- if (c & 0x80)
- { p--;
- unsigned u = decodeUTF();
- p++;
- if (u == PS || u == LS)
- endOfLine();
- stringbuffer.writeUTF8(u);
- continue;
- }
- break;
- }
- stringbuffer.writeByte(c);
- }
-}
-
-/**************************************
- * Lex hex strings:
- * x"0A ae 34FE BD"
- */
-
-TOK Lexer::hexStringConstant(Token *t)
-{
- unsigned c;
- Loc start = loc();
- unsigned n = 0;
- unsigned v = ~0; // dead assignment, needed to suppress warning
-
- p++;
- stringbuffer.reset();
- while (1)
- {
- c = *p++;
- switch (c)
- {
- case ' ':
- case '\t':
- case '\v':
- case '\f':
- continue; // skip white space
-
- case '\r':
- if (*p == '\n')
- continue; // ignore
- // Treat isolated '\r' as if it were a '\n'
- /* fall through */
- case '\n':
- endOfLine();
- continue;
-
- case 0:
- case 0x1A:
- error("unterminated string constant starting at %s", start.toChars());
- t->ustring = (utf8_t *)const_cast<char *>("");
- t->len = 0;
- t->postfix = 0;
- return TOKxstring;
-
- case '"':
- if (n & 1)
- { error("odd number (%d) of hex characters in hex string", n);
- stringbuffer.writeByte(v);
- }
- t->len = (unsigned)stringbuffer.length();
- stringbuffer.writeByte(0);
- t->ustring = (utf8_t *)mem.xmalloc(stringbuffer.length());
- memcpy(t->ustring, stringbuffer.slice().ptr, stringbuffer.length());
- stringPostfix(t);
- return TOKxstring;
-
- default:
- if (c >= '0' && c <= '9')
- c -= '0';
- else if (c >= 'a' && c <= 'f')
- c -= 'a' - 10;
- else if (c >= 'A' && c <= 'F')
- c -= 'A' - 10;
- else if (c & 0x80)
- { p--;
- unsigned u = decodeUTF();
- p++;
- if (u == PS || u == LS)
- endOfLine();
- else
- error("non-hex character \\u%04x in hex string", u);
- }
- else
- error("non-hex character '%c' in hex string", c);
- if (n & 1)
- { v = (v << 4) | c;
- stringbuffer.writeByte(v);
- }
- else
- v = c;
- n++;
- break;
- }
- }
-}
-
-
-/**************************************
- * Lex delimited strings:
- * q"(foo(xxx))" // "foo(xxx)"
- * q"[foo(]" // "foo("
- * q"/foo]/" // "foo]"
- * q"HERE
- * foo
- * HERE" // "foo\n"
- * Input:
- * p is on the "
- */
-
-TOK Lexer::delimitedStringConstant(Token *t)
-{
- unsigned c;
- Loc start = loc();
- unsigned delimleft = 0;
- unsigned delimright = 0;
- unsigned nest = 1;
- unsigned nestcount = ~0; // dead assignment, needed to suppress warning
- Identifier *hereid = NULL;
- unsigned blankrol = 0;
- unsigned startline = 0;
-
- p++;
- stringbuffer.reset();
- while (1)
- {
- c = *p++;
- //printf("c = '%c'\n", c);
- switch (c)
- {
- case '\n':
- Lnextline:
- endOfLine();
- startline = 1;
- if (blankrol)
- { blankrol = 0;
- continue;
- }
- if (hereid)
- {
- stringbuffer.writeUTF8(c);
- continue;
- }
- break;
-
- case '\r':
- if (*p == '\n')
- continue; // ignore
- c = '\n'; // treat EndOfLine as \n character
- goto Lnextline;
-
- case 0:
- case 0x1A:
- error("unterminated delimited string constant starting at %s", start.toChars());
- t->ustring = (utf8_t *)const_cast<char *>("");
- t->len = 0;
- t->postfix = 0;
- return TOKstring;
-
- default:
- if (c & 0x80)
- { p--;
- c = decodeUTF();
- p++;
- if (c == PS || c == LS)
- goto Lnextline;
- }
- break;
- }
- if (delimleft == 0)
- { delimleft = c;
- nest = 1;
- nestcount = 1;
- if (c == '(')
- delimright = ')';
- else if (c == '{')
- delimright = '}';
- else if (c == '[')
- delimright = ']';
- else if (c == '<')
- delimright = '>';
- else if (isalpha(c) || c == '_' || (c >= 0x80 && isUniAlpha(c)))
- { // Start of identifier; must be a heredoc
- Token tok;
- p--;
- scan(&tok); // read in heredoc identifier
- if (tok.value != TOKidentifier)
- { error("identifier expected for heredoc, not %s", tok.toChars());
- delimright = c;
- }
- else
- { hereid = tok.ident;
- //printf("hereid = '%s'\n", hereid->toChars());
- blankrol = 1;
- }
- nest = 0;
- }
- else
- { delimright = c;
- nest = 0;
- if (isspace(c))
- error("delimiter cannot be whitespace");
- }
- }
- else
- {
- if (blankrol)
- { error("heredoc rest of line should be blank");
- blankrol = 0;
- continue;
- }
- if (nest == 1)
- {
- if (c == delimleft)
- nestcount++;
- else if (c == delimright)
- { nestcount--;
- if (nestcount == 0)
- goto Ldone;
- }
- }
- else if (c == delimright)
- goto Ldone;
- if (startline && isalpha(c) && hereid)
- { Token tok;
- const utf8_t *psave = p;
- p--;
- scan(&tok); // read in possible heredoc identifier
- //printf("endid = '%s'\n", tok.ident->toChars());
- if (tok.value == TOKidentifier && tok.ident->equals(hereid))
- { /* should check that rest of line is blank
- */
- goto Ldone;
- }
- p = psave;
- }
- stringbuffer.writeUTF8(c);
- startline = 0;
- }
- }
-
-Ldone:
- if (*p == '"')
- p++;
- else if (hereid)
- error("delimited string must end in %s\"", hereid->toChars());
- else
- error("delimited string must end in %c\"", delimright);
- t->len = (unsigned)stringbuffer.length();
- stringbuffer.writeByte(0);
- t->ustring = (utf8_t *)mem.xmalloc(stringbuffer.length());
- memcpy(t->ustring, stringbuffer.slice().ptr, stringbuffer.length());
- stringPostfix(t);
- return TOKstring;
-}
-
-/**************************************
- * Lex delimited strings:
- * q{ foo(xxx) } // " foo(xxx) "
- * q{foo(} // "foo("
- * q{{foo}"}"} // "{foo}"}""
- * Input:
- * p is on the q
- */
-
-TOK Lexer::tokenStringConstant(Token *t)
-{
- unsigned nest = 1;
- Loc start = loc();
- const utf8_t *pstart = ++p;
-
- while (1)
- { Token tok;
-
- scan(&tok);
- switch (tok.value)
- {
- case TOKlcurly:
- nest++;
- continue;
-
- case TOKrcurly:
- if (--nest == 0)
- {
- t->len = (unsigned)(p - 1 - pstart);
- t->ustring = (utf8_t *)mem.xmalloc(t->len + 1);
- memcpy(t->ustring, pstart, t->len);
- t->ustring[t->len] = 0;
- stringPostfix(t);
- return TOKstring;
- }
- continue;
-
- case TOKeof:
- error("unterminated token string constant starting at %s", start.toChars());
- t->ustring = (utf8_t *)const_cast<char *>("");
- t->len = 0;
- t->postfix = 0;
- return TOKstring;
-
- default:
- continue;
- }
- }
-}
-
-
-
-/**************************************
- */
-
-TOK Lexer::escapeStringConstant(Token *t)
-{
- unsigned c;
- Loc start = loc();
-
- p++;
- stringbuffer.reset();
- while (1)
- {
- c = *p++;
- switch (c)
- {
- case '\\':
- switch (*p)
- {
- case 'u':
- case 'U':
- case '&':
- c = escapeSequence();
- stringbuffer.writeUTF8(c);
- continue;
-
- default:
- c = escapeSequence();
- break;
- }
- break;
- case '\n':
- endOfLine();
- break;
-
- case '\r':
- if (*p == '\n')
- continue; // ignore
- c = '\n'; // treat EndOfLine as \n character
- endOfLine();
- break;
-
- case '"':
- t->len = (unsigned)stringbuffer.length();
- stringbuffer.writeByte(0);
- t->ustring = (utf8_t *)mem.xmalloc(stringbuffer.length());
- memcpy(t->ustring, stringbuffer.slice().ptr, stringbuffer.length());
- stringPostfix(t);
- return TOKstring;
-
- case 0:
- case 0x1A:
- p--;
- error("unterminated string constant starting at %s", start.toChars());
- t->ustring = (utf8_t *)const_cast<char *>("");
- t->len = 0;
- t->postfix = 0;
- return TOKstring;
-
- default:
- if (c & 0x80)
- {
- p--;
- c = decodeUTF();
- if (c == LS || c == PS)
- { c = '\n';
- endOfLine();
- }
- p++;
- stringbuffer.writeUTF8(c);
- continue;
- }
- break;
- }
- stringbuffer.writeByte(c);
- }
-}
-
-/**************************************
- */
-
-TOK Lexer::charConstant(Token *t)
-{
- unsigned c;
- TOK tk = TOKcharv;
-
- //printf("Lexer::charConstant\n");
- p++;
- c = *p++;
- switch (c)
- {
- case '\\':
- switch (*p)
- {
- case 'u':
- t->uns64value = escapeSequence();
- tk = TOKwcharv;
- break;
-
- case 'U':
- case '&':
- t->uns64value = escapeSequence();
- tk = TOKdcharv;
- break;
-
- default:
- t->uns64value = escapeSequence();
- break;
- }
- break;
- case '\n':
- L1:
- endOfLine();
- /* fall through */
- case '\r':
- case 0:
- case 0x1A:
- case '\'':
- error("unterminated character constant");
- t->uns64value = '?';
- return tk;
-
- default:
- if (c & 0x80)
- {
- p--;
- c = decodeUTF();
- p++;
- if (c == LS || c == PS)
- goto L1;
- if (c < 0xD800 || (c >= 0xE000 && c < 0xFFFE))
- tk = TOKwcharv;
- else
- tk = TOKdcharv;
- }
- t->uns64value = c;
- break;
- }
-
- if (*p != '\'')
- {
- error("unterminated character constant");
- t->uns64value = '?';
- return tk;
- }
- p++;
- return tk;
-}
-
-/***************************************
- * Get postfix of string literal.
- */
-
-void Lexer::stringPostfix(Token *t)
-{
- switch (*p)
- {
- case 'c':
- case 'w':
- case 'd':
- t->postfix = *p;
- p++;
- break;
-
- default:
- t->postfix = 0;
- break;
- }
-}
-
-/**************************************
- * Read in a number.
- * If it's an integer, store it in tok.TKutok.Vlong.
- * integers can be decimal, octal or hex
- * Handle the suffixes U, UL, LU, L, etc.
- * If it's double, store it in tok.TKutok.Vdouble.
- * Returns:
- * TKnum
- * TKdouble,...
- */
-
-TOK Lexer::number(Token *t)
-{
- int base = 10;
- const utf8_t *start = p;
- unsigned c;
- uinteger_t n = 0; // unsigned >=64 bit integer type
- int d;
- bool err = false;
- bool overflow = false;
-
- c = *p;
- if (c == '0')
- {
- ++p;
- c = *p;
- switch (c)
- {
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- n = c - '0';
- ++p;
- base = 8;
- break;
-
- case 'x':
- case 'X':
- ++p;
- base = 16;
- break;
-
- case 'b':
- case 'B':
- ++p;
- base = 2;
- break;
-
- case '.':
- if (p[1] == '.')
- goto Ldone; // if ".."
- if (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80)
- goto Ldone; // if ".identifier" or ".unicode"
- goto Lreal; // '.' is part of current token
-
- case 'i':
- case 'f':
- case 'F':
- goto Lreal;
-
- case '_':
- ++p;
- base = 8;
- break;
-
- case 'L':
- if (p[1] == 'i')
- goto Lreal;
- break;
-
- default:
- break;
- }
- }
-
- while (1)
- {
- c = *p;
- switch (c)
- {
- case '0': case '1':
- ++p;
- d = c - '0';
- break;
-
- case '2': case '3':
- case '4': case '5': case '6': case '7':
- if (base == 2 && !err)
- {
- error("binary digit expected");
- err = true;
- }
- ++p;
- d = c - '0';
- break;
-
- case '8': case '9':
- ++p;
- if (base < 10 && !err)
- {
- error("radix %d digit expected, not `%c`", base, c);
- err = true;
- }
- d = c - '0';
- break;
-
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- ++p;
- if (base != 16)
- {
- if (c == 'e' || c == 'E' || c == 'f' || c == 'F')
- goto Lreal;
- if (!err)
- {
- error("radix %d digit expected, not `%c`", base, c);
- err = true;
- }
- }
- if (c >= 'a')
- d = c + 10 - 'a';
- else
- d = c + 10 - 'A';
- break;
-
- case 'L':
- if (p[1] == 'i')
- goto Lreal;
- goto Ldone;
-
- case '.':
- if (p[1] == '.')
- goto Ldone; // if ".."
- if (base == 10 && (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80))
- goto Ldone; // if ".identifier" or ".unicode"
- goto Lreal; // otherwise as part of a floating point literal
-
- case 'p':
- case 'P':
- case 'i':
- Lreal:
- p = start;
- return inreal(t);
-
- case '_':
- ++p;
- continue;
-
- default:
- goto Ldone;
- }
-
- uinteger_t n2 = n * base;
- if ((n2 / base != n || n2 + d < n))
- {
- overflow = true;
- }
- n = n2 + d;
-
- // if n needs more than 64 bits
- if (sizeof(n) > 8 &&
- n > 0xFFFFFFFFFFFFFFFFULL)
- {
- overflow = true;
- }
- }
-
-Ldone:
-
- if (overflow && !err)
- {
- error("integer overflow");
- err = true;
- }
-
- enum FLAGS
- {
- FLAGS_none = 0,
- FLAGS_decimal = 1, // decimal
- FLAGS_unsigned = 2, // u or U suffix
- FLAGS_long = 4, // L suffix
- };
-
- unsigned flags = (base == 10) ? FLAGS_decimal : FLAGS_none;
-
- // Parse trailing 'u', 'U', 'l' or 'L' in any combination
- const utf8_t *psuffix = p;
- while (1)
- {
- utf8_t f;
- switch (*p)
- {
- case 'U':
- case 'u':
- f = FLAGS_unsigned;
- goto L1;
-
- case 'l':
- f = FLAGS_long;
- error("lower case integer suffix 'l' is not allowed. Please use 'L' instead");
- goto L1;
-
- case 'L':
- f = FLAGS_long;
- L1:
- p++;
- if ((flags & f) && !err)
- {
- error("unrecognized token");
- err = true;
- }
- flags = (FLAGS) (flags | f);
- continue;
- default:
- break;
- }
- break;
- }
-
- if (base == 8 && n >= 8)
- error("octal literals 0%llo%.*s are no longer supported, use std.conv.octal!%llo%.*s instead",
- n, p - psuffix, psuffix, n, p - psuffix, psuffix);
-
- TOK result;
- switch (flags)
- {
- case FLAGS_none:
- /* Octal or Hexadecimal constant.
- * First that fits: int, uint, long, ulong
- */
- if (n & 0x8000000000000000LL)
- result = TOKuns64v;
- else if (n & 0xFFFFFFFF00000000LL)
- result = TOKint64v;
- else if (n & 0x80000000)
- result = TOKuns32v;
- else
- result = TOKint32v;
- break;
-
- case FLAGS_decimal:
- /* First that fits: int, long, long long
- */
- if (n & 0x8000000000000000LL)
- {
- if (!err)
- {
- error("signed integer overflow");
- err = true;
- }
- result = TOKuns64v;
- }
- else if (n & 0xFFFFFFFF80000000LL)
- result = TOKint64v;
- else
- result = TOKint32v;
- break;
-
- case FLAGS_unsigned:
- case FLAGS_decimal | FLAGS_unsigned:
- /* First that fits: uint, ulong
- */
- if (n & 0xFFFFFFFF00000000LL)
- result = TOKuns64v;
- else
- result = TOKuns32v;
- break;
-
- case FLAGS_decimal | FLAGS_long:
- if (n & 0x8000000000000000LL)
- {
- if (!err)
- {
- error("signed integer overflow");
- err = true;
- }
- result = TOKuns64v;
- }
- else
- result = TOKint64v;
- break;
-
- case FLAGS_long:
- if (n & 0x8000000000000000LL)
- result = TOKuns64v;
- else
- result = TOKint64v;
- break;
-
- case FLAGS_unsigned | FLAGS_long:
- case FLAGS_decimal | FLAGS_unsigned | FLAGS_long:
- result = TOKuns64v;
- break;
-
- default:
- assert(0);
- }
- t->uns64value = n;
- return result;
-}
-
-/**************************************
- * Read in characters, converting them to real.
- * Bugs:
- * Exponent overflow not detected.
- * Too much requested precision is not detected.
- */
-
-TOK Lexer::inreal(Token *t)
-{
- //printf("Lexer::inreal()\n");
- bool isWellformedString = true;
- stringbuffer.reset();
- const utf8_t *pstart = p;
- char hex = 0;
- unsigned c = *p++;
-
- // Leading '0x'
- if (c == '0')
- {
- c = *p++;
- if (c == 'x' || c == 'X')
- {
- hex = true;
- c = *p++;
- }
- }
-
- // Digits to left of '.'
- while (1)
- {
- if (c == '.')
- {
- c = *p++;
- break;
- }
- if (isdigit(c) || (hex && isxdigit(c)) || c == '_')
- {
- c = *p++;
- continue;
- }
- break;
- }
-
- // Digits to right of '.'
- while (1)
- {
- if (isdigit(c) || (hex && isxdigit(c)) || c == '_')
- {
- c = *p++;
- continue;
- }
- break;
- }
-
- if (c == 'e' || c == 'E' || (hex && (c == 'p' || c == 'P')))
- {
- c = *p++;
- if (c == '-' || c == '+')
- {
- c = *p++;
- }
- bool anyexp = false;
- while (1)
- {
- if (isdigit(c))
- {
- anyexp = true;
- c = *p++;
- continue;
- }
- if (c == '_')
- {
- c = *p++;
- continue;
- }
- if (!anyexp)
- {
- error("missing exponent");
- isWellformedString = false;
- }
- break;
- }
- }
- else if (hex)
- {
- error("exponent required for hex float");
- isWellformedString = false;
- }
- --p;
- while (pstart < p)
- {
- if (*pstart != '_')
- stringbuffer.writeByte(*pstart);
- ++pstart;
- }
-
- stringbuffer.writeByte(0);
- const char *sbufptr = (char *)stringbuffer.slice().ptr;
- TOK result;
- bool isOutOfRange = false;
- t->floatvalue = (isWellformedString ? CTFloat::parse(sbufptr, &isOutOfRange) : CTFloat::zero);
- errno = 0;
- switch (*p)
- {
- case 'F':
- case 'f':
- if (isWellformedString && !isOutOfRange)
- isOutOfRange = Port::isFloat32LiteralOutOfRange(sbufptr);
- result = TOKfloat32v;
- p++;
- break;
-
- default:
- if (isWellformedString && !isOutOfRange)
- isOutOfRange = Port::isFloat64LiteralOutOfRange(sbufptr);
- result = TOKfloat64v;
- break;
-
- case 'l':
- error("use 'L' suffix instead of 'l'");
- /* fall through */
- case 'L':
- result = TOKfloat80v;
- p++;
- break;
- }
- if (*p == 'i' || *p == 'I')
- {
- if (*p == 'I')
- error("use 'i' suffix instead of 'I'");
- p++;
- switch (result)
- {
- case TOKfloat32v:
- result = TOKimaginary32v;
- break;
- case TOKfloat64v:
- result = TOKimaginary64v;
- break;
- case TOKfloat80v:
- result = TOKimaginary80v;
- break;
- default: break;
- }
- }
- const bool isLong = (result == TOKfloat80v || result == TOKimaginary80v);
- if (isOutOfRange && !isLong)
- {
- const char *suffix = (result == TOKfloat32v || result == TOKimaginary32v) ? "f" : "";
- error(scanloc, "number `%s%s` is not representable", (char *)stringbuffer.slice().ptr, suffix);
- }
- return result;
-}
-
-/*********************************************
- * parse:
- * #line linnum [filespec]
- * also allow __LINE__ for linnum, and __FILE__ for filespec
- */
-
-void Lexer::poundLine()
-{
- Token tok;
- int linnum = this->scanloc.linnum;
- char *filespec = NULL;
- Loc loc = this->loc();
-
- scan(&tok);
- if (tok.value == TOKint32v || tok.value == TOKint64v)
- {
- int lin = (int)(tok.uns64value - 1);
- if ((unsigned)lin != tok.uns64value - 1)
- error("line number %lld out of range", (unsigned long long)tok.uns64value);
- else
- linnum = lin;
- }
- else if (tok.value == TOKline)
- {
- }
- else
- goto Lerr;
-
- while (1)
- {
- switch (*p)
- {
- case 0:
- case 0x1A:
- case '\n':
- Lnewline:
- this->scanloc.linnum = linnum;
- if (filespec)
- this->scanloc.filename = filespec;
- return;
-
- case '\r':
- p++;
- if (*p != '\n')
- { p--;
- goto Lnewline;
- }
- continue;
-
- case ' ':
- case '\t':
- case '\v':
- case '\f':
- p++;
- continue; // skip white space
-
- case '_':
- if (memcmp(p, "__FILE__", 8) == 0)
- {
- p += 8;
- filespec = mem.xstrdup(scanloc.filename);
- continue;
- }
- goto Lerr;
-
- case '"':
- if (filespec)
- goto Lerr;
- stringbuffer.reset();
- p++;
- while (1)
- { unsigned c;
-
- c = *p;
- switch (c)
- {
- case '\n':
- case '\r':
- case 0:
- case 0x1A:
- goto Lerr;
-
- case '"':
- stringbuffer.writeByte(0);
- filespec = mem.xstrdup((char *)stringbuffer.slice().ptr);
- p++;
- break;
-
- default:
- if (c & 0x80)
- { unsigned u = decodeUTF();
- if (u == PS || u == LS)
- goto Lerr;
- }
- stringbuffer.writeByte(c);
- p++;
- continue;
- }
- break;
- }
- continue;
-
- default:
- if (*p & 0x80)
- { unsigned u = decodeUTF();
- if (u == PS || u == LS)
- goto Lnewline;
- }
- goto Lerr;
- }
- }
-
-Lerr:
- error(loc, "#line integer [\"filespec\"]\\n expected");
-}
-
-
-/********************************************
- * Decode UTF character.
- * Issue error messages for invalid sequences.
- * Return decoded character, advance p to last character in UTF sequence.
- */
-
-unsigned Lexer::decodeUTF()
-{
- dchar_t u;
- utf8_t c;
- const utf8_t *s = p;
- size_t len;
- size_t idx;
- const char *msg;
-
- c = *s;
- assert(c & 0x80);
-
- // Check length of remaining string up to 6 UTF-8 characters
- for (len = 1; len < 6 && s[len]; len++)
- ;
-
- idx = 0;
- msg = utf_decodeChar(s, len, &idx, &u);
- p += idx - 1;
- if (msg)
- {
- error("%s", msg);
- }
- return u;
-}
-
-static void trimTrailingWhitespace(OutBuffer &buf)
-{
- const unsigned char *s = buf.slice().ptr;
- size_t len = buf.length();
- while (len && (s[len - 1] == ' ' || s[len - 1] == '\t'))
- --len;
- buf.setsize(len);
-}
-
-/***************************************************
- * Parse doc comment embedded between t->ptr and p.
- * Remove trailing blanks and tabs from lines.
- * Replace all newlines with \n.
- * Remove leading comment character from each line.
- * Decide if it's a lineComment or a blockComment.
- * Append to previous one for this token.
- */
-
-void Lexer::getDocComment(Token *t, unsigned lineComment)
-{
- /* ct tells us which kind of comment it is: '/', '*', or '+'
- */
- utf8_t ct = t->ptr[2];
-
- /* Start of comment text skips over / * *, / + +, or / / /
- */
- const utf8_t *q = t->ptr + 3; // start of comment text
-
- const utf8_t *qend = p;
- if (ct == '*' || ct == '+')
- qend -= 2;
-
- /* Scan over initial row of ****'s or ++++'s or ////'s
- */
- for (; q < qend; q++)
- {
- if (*q != ct)
- break;
- }
-
- /* Remove leading spaces until start of the comment
- */
- int linestart = 0;
- if (ct == '/')
- {
- while (q < qend && (*q == ' ' || *q == '\t'))
- ++q;
- }
- else if (q < qend)
- {
- if (*q == '\r')
- {
- ++q;
- if (q < qend && *q == '\n')
- ++q;
- linestart = 1;
- }
- else if (*q == '\n')
- {
- ++q;
- linestart = 1;
- }
- }
-
- /* Remove trailing row of ****'s or ++++'s
- */
- if (ct != '/')
- {
- for (; q < qend; qend--)
- {
- if (qend[-1] != ct)
- break;
- }
- }
-
- /* Comment is now [q .. qend].
- * Canonicalize it into buf[].
- */
- OutBuffer buf;
-
- for (; q < qend; q++)
- {
- utf8_t c = *q;
-
- switch (c)
- {
- case '*':
- case '+':
- if (linestart && c == ct)
- { linestart = 0;
- /* Trim preceding whitespace up to preceding \n
- */
- trimTrailingWhitespace(buf);
- continue;
- }
- break;
-
- case ' ':
- case '\t':
- break;
-
- case '\r':
- if (q[1] == '\n')
- continue; // skip the \r
- goto Lnewline;
-
- default:
- if (c == 226)
- {
- // If LS or PS
- if (q[1] == 128 &&
- (q[2] == 168 || q[2] == 169))
- {
- q += 2;
- goto Lnewline;
- }
- }
- linestart = 0;
- break;
-
- Lnewline:
- c = '\n'; // replace all newlines with \n
- /* fall through */
- case '\n':
- linestart = 1;
-
- /* Trim trailing whitespace
- */
- trimTrailingWhitespace(buf);
- break;
- }
- buf.writeByte(c);
- }
-
- /* Trim trailing whitespace (if the last line does not have newline)
- */
- if (buf.length() && (buf.slice().ptr[buf.length() - 1] == ' ' || buf.slice().ptr[buf.length() - 1] == '\t'))
- {
- trimTrailingWhitespace(buf);
- }
-
- // Always end with a newline
- if (!buf.length() || buf.slice().ptr[buf.length() - 1] != '\n')
- buf.writeByte('\n');
-
- buf.writeByte(0);
-
- // It's a line comment if the start of the doc comment comes
- // after other non-whitespace on the same line.
- const utf8_t** dc = (lineComment && anyToken)
- ? &t->lineComment
- : &t->blockComment;
-
- // Combine with previous doc comment, if any
- if (*dc)
- *dc = combineComments(*dc, (utf8_t *)buf.slice().ptr);
- else
- *dc = (utf8_t *)buf.extractData();
-}
-
-/********************************************
- * Combine two document comments into one,
- * separated by a newline.
- */
-
-const utf8_t *Lexer::combineComments(const utf8_t *c1, const utf8_t *c2)
-{
- //printf("Lexer::combineComments('%s', '%s')\n", c1, c2);
-
- const utf8_t *c = c2;
-
- if (c1)
- {
- c = c1;
- if (c2)
- {
- size_t len1 = strlen((const char *)c1);
- size_t len2 = strlen((const char *)c2);
-
- int insertNewLine = 0;
- if (len1 && c1[len1 - 1] != '\n')
- {
- ++len1;
- insertNewLine = 1;
- }
-
- utf8_t *p = (utf8_t *)mem.xmalloc(len1 + 1 + len2 + 1);
- memcpy(p, c1, len1 - insertNewLine);
- if (insertNewLine)
- p[len1 - 1] = '\n';
-
- p[len1] = '\n';
-
- memcpy(p + len1 + 1, c2, len2);
- p[len1 + 1 + len2] = 0;
- c = p;
- }
- }
- return c;
-}
diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d
new file mode 100644
index 00000000000..afffc2dcf30
--- /dev/null
+++ b/gcc/d/dmd/lexer.d
@@ -0,0 +1,3273 @@
+/**
+ * Implements the lexical analyzer, which converts source code into lexical tokens.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/lex.html, Lexical)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/lexer.d, _lexer.d)
+ * Documentation: https://dlang.org/phobos/dmd_lexer.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/lexer.d
+ */
+
+module dmd.lexer;
+
+import core.stdc.ctype;
+import core.stdc.errno;
+import core.stdc.stdarg;
+import core.stdc.stdio;
+import core.stdc.stdlib : getenv;
+import core.stdc.string;
+import core.stdc.time;
+
+import dmd.entity;
+import dmd.errors;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.root.ctfloat;
+import dmd.root.outbuffer;
+import dmd.root.port;
+import dmd.root.rmem;
+import dmd.root.string;
+import dmd.tokens;
+import dmd.utf;
+import dmd.utils;
+
+nothrow:
+
+private enum LS = 0x2028; // UTF line separator
+private enum PS = 0x2029; // UTF paragraph separator
+
+/********************************************
+ * Do our own char maps
+ */
+private static immutable cmtable = () {
+ ubyte[256] table;
+ foreach (const c; 0 .. table.length)
+ {
+ if ('0' <= c && c <= '7')
+ table[c] |= CMoctal;
+ if (c_isxdigit(c))
+ table[c] |= CMhex;
+ if (c_isalnum(c) || c == '_')
+ table[c] |= CMidchar;
+
+ switch (c)
+ {
+ case 'x': case 'X':
+ case 'b': case 'B':
+ table[c] |= CMzerosecond;
+ break;
+
+ case '0': .. case '9':
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'l': case 'L':
+ case 'p': case 'P':
+ case 'u': case 'U':
+ case 'i':
+ case '.':
+ case '_':
+ table[c] |= CMzerosecond | CMdigitsecond;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (c)
+ {
+ case '\\':
+ case '\n':
+ case '\r':
+ case 0:
+ case 0x1A:
+ case '\'':
+ break;
+ default:
+ if (!(c & 0x80))
+ table[c] |= CMsinglechar;
+ break;
+ }
+ }
+ return table;
+}();
+
+private
+{
+ enum CMoctal = 0x1;
+ enum CMhex = 0x2;
+ enum CMidchar = 0x4;
+ enum CMzerosecond = 0x8;
+ enum CMdigitsecond = 0x10;
+ enum CMsinglechar = 0x20;
+}
+
+private bool isoctal(const char c) pure @nogc @safe
+{
+ return (cmtable[c] & CMoctal) != 0;
+}
+
+private bool ishex(const char c) pure @nogc @safe
+{
+ return (cmtable[c] & CMhex) != 0;
+}
+
+private bool isidchar(const char c) pure @nogc @safe
+{
+ return (cmtable[c] & CMidchar) != 0;
+}
+
+private bool isZeroSecond(const char c) pure @nogc @safe
+{
+ return (cmtable[c] & CMzerosecond) != 0;
+}
+
+private bool isDigitSecond(const char c) pure @nogc @safe
+{
+ return (cmtable[c] & CMdigitsecond) != 0;
+}
+
+private bool issinglechar(const char c) pure @nogc @safe
+{
+ return (cmtable[c] & CMsinglechar) != 0;
+}
+
+private bool c_isxdigit(const int c) pure @nogc @safe
+{
+ return (( c >= '0' && c <= '9') ||
+ ( c >= 'a' && c <= 'f') ||
+ ( c >= 'A' && c <= 'F'));
+}
+
+private bool c_isalnum(const int c) pure @nogc @safe
+{
+ return (( c >= '0' && c <= '9') ||
+ ( c >= 'a' && c <= 'z') ||
+ ( c >= 'A' && c <= 'Z'));
+}
+
+unittest
+{
+ //printf("lexer.unittest\n");
+ /* Not much here, just trying things out.
+ */
+ string text = "int"; // We rely on the implicit null-terminator
+ scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, 0, 0);
+ TOK tok;
+ tok = lex1.nextToken();
+ //printf("tok == %s, %d, %d\n", Token::toChars(tok), tok, TOK.int32);
+ assert(tok == TOK.int32);
+ tok = lex1.nextToken();
+ assert(tok == TOK.endOfFile);
+ tok = lex1.nextToken();
+ assert(tok == TOK.endOfFile);
+ tok = lex1.nextToken();
+ assert(tok == TOK.endOfFile);
+}
+
+unittest
+{
+ // We don't want to see Lexer error output during these tests.
+ uint errors = global.startGagging();
+ scope(exit) global.endGagging(errors);
+
+ // Test malformed input: even malformed input should end in a TOK.endOfFile.
+ static immutable char[][] testcases =
+ [ // Testcase must end with 0 or 0x1A.
+ [0], // not malformed, but pathological
+ ['\'', 0],
+ ['\'', 0x1A],
+ ['{', '{', 'q', '{', 0],
+ [0xFF, 0],
+ [0xFF, 0x80, 0],
+ [0xFF, 0xFF, 0],
+ [0xFF, 0xFF, 0],
+ ['x', '"', 0x1A],
+ ];
+
+ foreach (testcase; testcases)
+ {
+ scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, 0, 0);
+ TOK tok = lex2.nextToken();
+ size_t iterations = 1;
+ while ((tok != TOK.endOfFile) && (iterations++ < testcase.length))
+ {
+ tok = lex2.nextToken();
+ }
+ assert(tok == TOK.endOfFile);
+ tok = lex2.nextToken();
+ assert(tok == TOK.endOfFile);
+ }
+}
+
+version (DMDLIB)
+{
+ version = LocOffset;
+}
+
+/***********************************************************
+ */
+class Lexer
+{
+ private __gshared OutBuffer stringbuffer;
+
+ Loc scanloc; // for error messages
+ Loc prevloc; // location of token before current
+
+ const(char)* p; // current character
+
+ Token token;
+
+ // For ImportC
+ bool Ccompile; /// true if compiling ImportC
+
+ // The following are valid only if (Ccompile == true)
+ ubyte longsize; /// size of C long, 4 or 8
+ ubyte long_doublesize; /// size of C long double, 8 or D real.sizeof
+ ubyte wchar_tsize; /// size of C wchar_t, 2 or 4
+
+ private
+ {
+ const(char)* base; // pointer to start of buffer
+ const(char)* end; // pointer to last element of buffer
+ const(char)* line; // start of current line
+
+ bool doDocComment; // collect doc comment information
+ bool anyToken; // seen at least one token
+ bool commentToken; // comments are TOK.comment's
+ int inTokenStringConstant; // can be larger than 1 when in nested q{} strings
+ int lastDocLine; // last line of previous doc comment
+
+ Token* tokenFreelist;
+ }
+
+ nothrow:
+
+ /*********************
+ * Creates a Lexer for the source code base[begoffset..endoffset+1].
+ * The last character, base[endoffset], must be null (0) or EOF (0x1A).
+ *
+ * Params:
+ * filename = used for error messages
+ * base = source code, must be terminated by a null (0) or EOF (0x1A) character
+ * begoffset = starting offset into base[]
+ * endoffset = the last offset to read into base[]
+ * doDocComment = handle documentation comments
+ * commentToken = comments become TOK.comment's
+ */
+ this(const(char)* filename, const(char)* base, size_t begoffset,
+ size_t endoffset, bool doDocComment, bool commentToken) pure
+ {
+ scanloc = Loc(filename, 1, 1);
+ //printf("Lexer::Lexer(%p,%d)\n",base,length);
+ //printf("lexer.filename = %s\n", filename);
+ token = Token.init;
+ this.base = base;
+ this.end = base + endoffset;
+ p = base + begoffset;
+ line = p;
+ this.doDocComment = doDocComment;
+ this.commentToken = commentToken;
+ this.inTokenStringConstant = 0;
+ this.lastDocLine = 0;
+ //initKeywords();
+ /* If first line starts with '#!', ignore the line
+ */
+ if (p && p[0] == '#' && p[1] == '!')
+ {
+ p += 2;
+ while (1)
+ {
+ char c = *p++;
+ switch (c)
+ {
+ case 0:
+ case 0x1A:
+ p--;
+ goto case;
+ case '\n':
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+ endOfLine();
+ }
+ }
+
+ /// Returns: a newly allocated `Token`.
+ Token* allocateToken() pure nothrow @safe
+ {
+ if (tokenFreelist)
+ {
+ Token* t = tokenFreelist;
+ tokenFreelist = t.next;
+ t.next = null;
+ return t;
+ }
+ return new Token();
+ }
+
+ /// Frees the given token by returning it to the freelist.
+ private void releaseToken(Token* token) pure nothrow @nogc @safe
+ {
+ if (mem.isGCEnabled)
+ *token = Token.init;
+ token.next = tokenFreelist;
+ tokenFreelist = token;
+ }
+
+ final TOK nextToken()
+ {
+ prevloc = token.loc;
+ if (token.next)
+ {
+ Token* t = token.next;
+ memcpy(&token, t, Token.sizeof);
+ releaseToken(t);
+ }
+ else
+ {
+ scan(&token);
+ }
+ //printf(token.toChars());
+ return token.value;
+ }
+
+ /***********************
+ * Look ahead at next token's value.
+ */
+ final TOK peekNext()
+ {
+ return peek(&token).value;
+ }
+
+ /***********************
+ * Look 2 tokens ahead at value.
+ */
+ final TOK peekNext2()
+ {
+ Token* t = peek(&token);
+ return peek(t).value;
+ }
+
+ /****************************
+ * Turn next token in buffer into a token.
+ */
+ final void scan(Token* t)
+ {
+ const lastLine = scanloc.linnum;
+ Loc startLoc;
+ t.blockComment = null;
+ t.lineComment = null;
+
+ while (1)
+ {
+ t.ptr = p;
+ //printf("p = %p, *p = '%c'\n",p,*p);
+ t.loc = loc();
+ switch (*p)
+ {
+ case 0:
+ case 0x1A:
+ t.value = TOK.endOfFile; // end of file
+ // Intentionally not advancing `p`, such that subsequent calls keep returning TOK.endOfFile.
+ return;
+ case ' ':
+ case '\t':
+ case '\v':
+ case '\f':
+ p++;
+ continue; // skip white space
+ case '\r':
+ p++;
+ if (*p != '\n') // if CR stands by itself
+ {
+ endOfLine();
+ goto skipFourSpaces;
+ }
+ continue; // skip white space
+ case '\n':
+ p++;
+ endOfLine();
+ skipFourSpaces:
+ while (*(cast(uint*)p) == 0x20202020) //' ' == 0x20
+ {
+ p+=4;
+ }
+ continue; // skip white space
+ case '0':
+ if (!isZeroSecond(p[1])) // if numeric literal does not continue
+ {
+ ++p;
+ t.unsvalue = 0;
+ t.value = TOK.int32Literal;
+ return;
+ }
+ goto Lnumber;
+
+ case '1': .. case '9':
+ if (!isDigitSecond(p[1])) // if numeric literal does not continue
+ {
+ t.unsvalue = *p - '0';
+ ++p;
+ t.value = TOK.int32Literal;
+ return;
+ }
+ Lnumber:
+ t.value = number(t);
+ return;
+
+ case '\'':
+ if (issinglechar(p[1]) && p[2] == '\'')
+ {
+ t.unsvalue = p[1]; // simple one character literal
+ t.value = Ccompile ? TOK.int32Literal : TOK.charLiteral;
+ p += 3;
+ }
+ else if (Ccompile)
+ {
+ clexerCharConstant(*t, 0);
+ }
+ else
+ {
+ t.value = charConstant(t);
+ }
+ return;
+
+ case 'u':
+ case 'U':
+ case 'L':
+ if (!Ccompile)
+ goto case_ident;
+ if (p[1] == '\'') // C wide character constant
+ {
+ char c = *p;
+ if (c == 'L') // convert L to u or U
+ c = (wchar_tsize == 4) ? 'u' : 'U';
+ ++p;
+ clexerCharConstant(*t, c);
+ return;
+ }
+ else if (p[1] == '\"') // C wide string literal
+ {
+ const c = *p;
+ ++p;
+ escapeStringConstant(t);
+ t.postfix = c == 'L' ? (wchar_tsize == 2 ? 'w' : 'd') :
+ c == 'u' ? 'w' :
+ 'd';
+ return;
+ }
+ goto case_ident;
+
+ case 'r':
+ if (p[1] != '"')
+ goto case_ident;
+ p++;
+ goto case '`';
+ case '`':
+ wysiwygStringConstant(t);
+ return;
+ case 'x':
+ if (p[1] != '"')
+ goto case_ident;
+ p++;
+ auto start = p;
+ OutBuffer hexString;
+ t.value = hexStringConstant(t);
+ hexString.write(start[0 .. p - start]);
+ error("Built-in hex string literals are obsolete, use `std.conv.hexString!%s` instead.", hexString.extractChars());
+ return;
+ case 'q':
+ if (p[1] == '"')
+ {
+ p++;
+ delimitedStringConstant(t);
+ return;
+ }
+ else if (p[1] == '{')
+ {
+ p++;
+ tokenStringConstant(t);
+ return;
+ }
+ else
+ goto case_ident;
+ case '"':
+ escapeStringConstant(t);
+ return;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ /*case 'q': case 'r':*/
+ case 's':
+ case 't':
+ //case 'u':
+ case 'v':
+ case 'w':
+ /*case 'x':*/
+ case 'y':
+ case 'z':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ //case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ //case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '_':
+ case_ident:
+ {
+ while (1)
+ {
+ const c = *++p;
+ if (isidchar(c))
+ continue;
+ else if (c & 0x80)
+ {
+ const s = p;
+ const u = decodeUTF();
+ if (isUniAlpha(u))
+ continue;
+ error("char 0x%04x not allowed in identifier", u);
+ p = s;
+ }
+ break;
+ }
+ Identifier id = Identifier.idPool(cast(char*)t.ptr, cast(uint)(p - t.ptr));
+ t.ident = id;
+ t.value = cast(TOK)id.getValue();
+
+ anyToken = 1;
+
+ /* Different keywords for C and D
+ */
+ if (Ccompile)
+ {
+ if (t.value != TOK.identifier)
+ {
+ t.value = Ckeywords[t.value]; // filter out D keywords
+ }
+ }
+ else if (t.value >= FirstCKeyword)
+ t.value = TOK.identifier; // filter out C keywords
+
+ else if (*t.ptr == '_') // if special identifier token
+ {
+ // Lazy initialization
+ TimeStampInfo.initialize(t.loc);
+
+ if (id == Id.DATE)
+ {
+ t.ustring = TimeStampInfo.date.ptr;
+ goto Lstr;
+ }
+ else if (id == Id.TIME)
+ {
+ t.ustring = TimeStampInfo.time.ptr;
+ goto Lstr;
+ }
+ else if (id == Id.VENDOR)
+ {
+ t.ustring = global.vendor.xarraydup.ptr;
+ goto Lstr;
+ }
+ else if (id == Id.TIMESTAMP)
+ {
+ t.ustring = TimeStampInfo.timestamp.ptr;
+ Lstr:
+ t.value = TOK.string_;
+ t.postfix = 0;
+ t.len = cast(uint)strlen(t.ustring);
+ }
+ else if (id == Id.VERSIONX)
+ {
+ t.value = TOK.int64Literal;
+ t.unsvalue = global.versionNumber();
+ }
+ else if (id == Id.EOFX)
+ {
+ t.value = TOK.endOfFile;
+ // Advance scanner to end of file
+ while (!(*p == 0 || *p == 0x1A))
+ p++;
+ }
+ }
+ //printf("t.value = %d\n",t.value);
+ return;
+ }
+ case '/':
+ p++;
+ switch (*p)
+ {
+ case '=':
+ p++;
+ t.value = TOK.divAssign;
+ return;
+ case '*':
+ p++;
+ startLoc = loc();
+ while (1)
+ {
+ while (1)
+ {
+ const c = *p;
+ switch (c)
+ {
+ case '/':
+ break;
+ case '\n':
+ endOfLine();
+ p++;
+ continue;
+ case '\r':
+ p++;
+ if (*p != '\n')
+ endOfLine();
+ continue;
+ case 0:
+ case 0x1A:
+ error("unterminated /* */ comment");
+ p = end;
+ t.loc = loc();
+ t.value = TOK.endOfFile;
+ return;
+ default:
+ if (c & 0x80)
+ {
+ const u = decodeUTF();
+ if (u == PS || u == LS)
+ endOfLine();
+ }
+ p++;
+ continue;
+ }
+ break;
+ }
+ p++;
+ if (p[-2] == '*' && p - 3 != t.ptr)
+ break;
+ }
+ if (commentToken)
+ {
+ t.loc = startLoc;
+ t.value = TOK.comment;
+ return;
+ }
+ else if (doDocComment && t.ptr[2] == '*' && p - 4 != t.ptr)
+ {
+ // if /** but not /**/
+ getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1);
+ lastDocLine = scanloc.linnum;
+ }
+ continue;
+ case '/': // do // style comments
+ startLoc = loc();
+ while (1)
+ {
+ const c = *++p;
+ switch (c)
+ {
+ case '\n':
+ break;
+ case '\r':
+ if (p[1] == '\n')
+ p++;
+ break;
+ case 0:
+ case 0x1A:
+ if (commentToken)
+ {
+ p = end;
+ t.loc = startLoc;
+ t.value = TOK.comment;
+ return;
+ }
+ if (doDocComment && t.ptr[2] == '/')
+ {
+ getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1);
+ lastDocLine = scanloc.linnum;
+ }
+ p = end;
+ t.loc = loc();
+ t.value = TOK.endOfFile;
+ return;
+ default:
+ if (c & 0x80)
+ {
+ const u = decodeUTF();
+ if (u == PS || u == LS)
+ break;
+ }
+ continue;
+ }
+ break;
+ }
+ if (commentToken)
+ {
+ p++;
+ endOfLine();
+ t.loc = startLoc;
+ t.value = TOK.comment;
+ return;
+ }
+ if (doDocComment && t.ptr[2] == '/')
+ {
+ getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1);
+ lastDocLine = scanloc.linnum;
+ }
+ p++;
+ endOfLine();
+ continue;
+ case '+':
+ {
+ int nest;
+ startLoc = loc();
+ p++;
+ nest = 1;
+ while (1)
+ {
+ char c = *p;
+ switch (c)
+ {
+ case '/':
+ p++;
+ if (*p == '+')
+ {
+ p++;
+ nest++;
+ }
+ continue;
+ case '+':
+ p++;
+ if (*p == '/')
+ {
+ p++;
+ if (--nest == 0)
+ break;
+ }
+ continue;
+ case '\r':
+ p++;
+ if (*p != '\n')
+ endOfLine();
+ continue;
+ case '\n':
+ endOfLine();
+ p++;
+ continue;
+ case 0:
+ case 0x1A:
+ error("unterminated /+ +/ comment");
+ p = end;
+ t.loc = loc();
+ t.value = TOK.endOfFile;
+ return;
+ default:
+ if (c & 0x80)
+ {
+ uint u = decodeUTF();
+ if (u == PS || u == LS)
+ endOfLine();
+ }
+ p++;
+ continue;
+ }
+ break;
+ }
+ if (commentToken)
+ {
+ t.loc = startLoc;
+ t.value = TOK.comment;
+ return;
+ }
+ if (doDocComment && t.ptr[2] == '+' && p - 4 != t.ptr)
+ {
+ // if /++ but not /++/
+ getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1);
+ lastDocLine = scanloc.linnum;
+ }
+ continue;
+ }
+ default:
+ break;
+ }
+ t.value = TOK.div;
+ return;
+ case '.':
+ p++;
+ if (isdigit(*p))
+ {
+ /* Note that we don't allow ._1 and ._ as being
+ * valid floating point numbers.
+ */
+ p--;
+ t.value = inreal(t);
+ }
+ else if (p[0] == '.')
+ {
+ if (p[1] == '.')
+ {
+ p += 2;
+ t.value = TOK.dotDotDot;
+ }
+ else
+ {
+ p++;
+ t.value = TOK.slice;
+ }
+ }
+ else
+ t.value = TOK.dot;
+ return;
+ case '&':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.andAssign;
+ }
+ else if (*p == '&')
+ {
+ p++;
+ t.value = TOK.andAnd;
+ }
+ else
+ t.value = TOK.and;
+ return;
+ case '|':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.orAssign;
+ }
+ else if (*p == '|')
+ {
+ p++;
+ t.value = TOK.orOr;
+ }
+ else
+ t.value = TOK.or;
+ return;
+ case '-':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.minAssign;
+ }
+ else if (*p == '-')
+ {
+ p++;
+ t.value = TOK.minusMinus;
+ }
+ else if (*p == '>')
+ {
+ ++p;
+ t.value = TOK.arrow;
+ }
+ else
+ t.value = TOK.min;
+ return;
+ case '+':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.addAssign;
+ }
+ else if (*p == '+')
+ {
+ p++;
+ t.value = TOK.plusPlus;
+ }
+ else
+ t.value = TOK.add;
+ return;
+ case '<':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.lessOrEqual; // <=
+ }
+ else if (*p == '<')
+ {
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.leftShiftAssign; // <<=
+ }
+ else
+ t.value = TOK.leftShift; // <<
+ }
+ else if (*p == ':' && Ccompile)
+ {
+ ++p;
+ t.value = TOK.leftBracket; // <:
+ }
+ else if (*p == '%' && Ccompile)
+ {
+ ++p;
+ t.value = TOK.leftCurly; // <%
+ }
+ else
+ t.value = TOK.lessThan; // <
+ return;
+ case '>':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.greaterOrEqual; // >=
+ }
+ else if (*p == '>')
+ {
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.rightShiftAssign; // >>=
+ }
+ else if (*p == '>')
+ {
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.unsignedRightShiftAssign; // >>>=
+ }
+ else
+ t.value = TOK.unsignedRightShift; // >>>
+ }
+ else
+ t.value = TOK.rightShift; // >>
+ }
+ else
+ t.value = TOK.greaterThan; // >
+ return;
+ case '!':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.notEqual; // !=
+ }
+ else
+ t.value = TOK.not; // !
+ return;
+ case '=':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.equal; // ==
+ }
+ else if (*p == '>')
+ {
+ p++;
+ t.value = TOK.goesTo; // =>
+ }
+ else
+ t.value = TOK.assign; // =
+ return;
+ case '~':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.concatenateAssign; // ~=
+ }
+ else
+ t.value = TOK.tilde; // ~
+ return;
+ case '^':
+ p++;
+ if (*p == '^')
+ {
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.powAssign; // ^^=
+ }
+ else
+ t.value = TOK.pow; // ^^
+ }
+ else if (*p == '=')
+ {
+ p++;
+ t.value = TOK.xorAssign; // ^=
+ }
+ else
+ t.value = TOK.xor; // ^
+ return;
+ case '(':
+ p++;
+ t.value = TOK.leftParenthesis;
+ return;
+ case ')':
+ p++;
+ t.value = TOK.rightParenthesis;
+ return;
+ case '[':
+ p++;
+ t.value = TOK.leftBracket;
+ return;
+ case ']':
+ p++;
+ t.value = TOK.rightBracket;
+ return;
+ case '{':
+ p++;
+ t.value = TOK.leftCurly;
+ return;
+ case '}':
+ p++;
+ t.value = TOK.rightCurly;
+ return;
+ case '?':
+ p++;
+ t.value = TOK.question;
+ return;
+ case ',':
+ p++;
+ t.value = TOK.comma;
+ return;
+ case ';':
+ p++;
+ t.value = TOK.semicolon;
+ return;
+ case ':':
+ p++;
+ if (*p == ':')
+ {
+ ++p;
+ t.value = TOK.colonColon;
+ }
+ else if (*p == '>' && Ccompile)
+ {
+ ++p;
+ t.value = TOK.rightBracket;
+ }
+ else
+ t.value = TOK.colon;
+ return;
+ case '$':
+ p++;
+ t.value = TOK.dollar;
+ return;
+ case '@':
+ p++;
+ t.value = TOK.at;
+ return;
+ case '*':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.mulAssign;
+ }
+ else
+ t.value = TOK.mul;
+ return;
+ case '%':
+ p++;
+ if (*p == '=')
+ {
+ p++;
+ t.value = TOK.modAssign;
+ }
+ else if (*p == '>' && Ccompile)
+ {
+ ++p;
+ t.value = TOK.rightCurly;
+ }
+ else if (*p == ':' && Ccompile)
+ {
+ goto case '#'; // %: means #
+ }
+ else
+ t.value = TOK.mod;
+ return;
+ case '#':
+ {
+ p++;
+ Token n;
+ scan(&n);
+ if (Ccompile && n.value == TOK.int32Literal)
+ {
+ poundLine(n, true);
+ continue;
+ }
+ if (n.value == TOK.identifier)
+ {
+ if (n.ident == Id.line)
+ {
+ poundLine(n, false);
+ continue;
+ }
+ else
+ {
+ const locx = loc();
+ warning(locx, "C preprocessor directive `#%s` is not supported", n.ident.toChars());
+ }
+ }
+ else if (n.value == TOK.if_)
+ {
+ error("C preprocessor directive `#if` is not supported, use `version` or `static if`");
+ }
+ t.value = TOK.pound;
+ return;
+ }
+ default:
+ {
+ dchar c = *p;
+ if (c & 0x80)
+ {
+ c = decodeUTF();
+ // Check for start of unicode identifier
+ if (isUniAlpha(c))
+ goto case_ident;
+ if (c == PS || c == LS)
+ {
+ endOfLine();
+ p++;
+ continue;
+ }
+ }
+ if (c < 0x80 && isprint(c))
+ error("character '%c' is not a valid token", c);
+ else
+ error("character 0x%02x is not a valid token", c);
+ p++;
+ continue;
+ }
+ }
+ }
+ }
+
+ final Token* peek(Token* ct)
+ {
+ Token* t;
+ if (ct.next)
+ t = ct.next;
+ else
+ {
+ t = allocateToken();
+ scan(t);
+ ct.next = t;
+ }
+ return t;
+ }
+
+ /*********************************
+ * tk is on the opening (.
+ * Look ahead and return token that is past the closing ).
+ */
+ final Token* peekPastParen(Token* tk)
+ {
+ //printf("peekPastParen()\n");
+ int parens = 1;
+ int curlynest = 0;
+ while (1)
+ {
+ tk = peek(tk);
+ //tk.print();
+ switch (tk.value)
+ {
+ case TOK.leftParenthesis:
+ parens++;
+ continue;
+ case TOK.rightParenthesis:
+ --parens;
+ if (parens)
+ continue;
+ tk = peek(tk);
+ break;
+ case TOK.leftCurly:
+ curlynest++;
+ continue;
+ case TOK.rightCurly:
+ if (--curlynest >= 0)
+ continue;
+ break;
+ case TOK.semicolon:
+ if (curlynest)
+ continue;
+ break;
+ case TOK.endOfFile:
+ break;
+ default:
+ continue;
+ }
+ return tk;
+ }
+ }
+
+ /*******************************************
+ * Parse escape sequence.
+ */
+ private uint escapeSequence()
+ {
+ return Lexer.escapeSequence(token.loc, p, Ccompile);
+ }
+
+ /********
+ * Parse the given string literal escape sequence into a single character.
+ * D https://dlang.org/spec/lex.html#escape_sequences
+ * C11 6.4.4.4
+ * Params:
+ * loc = location to use for error messages
+ * sequence = pointer to string with escape sequence to parse. Updated to
+ * point past the end of the escape sequence
+ * Ccompile = true for compile C11 escape sequences
+ * Returns:
+ * the escape sequence as a single character
+ */
+ private static dchar escapeSequence(const ref Loc loc, ref const(char)* sequence, bool Ccompile)
+ {
+ const(char)* p = sequence; // cache sequence reference on stack
+ scope(exit) sequence = p;
+
+ uint c = *p;
+ int ndigits;
+ switch (c)
+ {
+ case '\'':
+ case '"':
+ case '?':
+ case '\\':
+ Lconsume:
+ p++;
+ break;
+ case 'a':
+ c = 7;
+ goto Lconsume;
+ case 'b':
+ c = 8;
+ goto Lconsume;
+ case 'f':
+ c = 12;
+ goto Lconsume;
+ case 'n':
+ c = 10;
+ goto Lconsume;
+ case 'r':
+ c = 13;
+ goto Lconsume;
+ case 't':
+ c = 9;
+ goto Lconsume;
+ case 'v':
+ c = 11;
+ goto Lconsume;
+ case 'u':
+ ndigits = 4;
+ goto Lhex;
+ case 'U':
+ ndigits = 8;
+ goto Lhex;
+ case 'x':
+ ndigits = 2;
+ Lhex:
+ p++;
+ c = *p;
+ if (ishex(cast(char)c))
+ {
+ uint v = 0;
+ int n = 0;
+ while (1)
+ {
+ if (isdigit(cast(char)c))
+ c -= '0';
+ else if (islower(c))
+ c -= 'a' - 10;
+ else
+ c -= 'A' - 10;
+ v = v * 16 + c;
+ c = *++p;
+ if (++n == ndigits)
+ break;
+ if (!ishex(cast(char)c))
+ {
+ .error(loc, "escape hex sequence has %d hex digits instead of %d", n, ndigits);
+ break;
+ }
+ }
+ if (ndigits != 2 && !utf_isValidDchar(v))
+ {
+ .error(loc, "invalid UTF character \\U%08x", v);
+ v = '?'; // recover with valid UTF character
+ }
+ c = v;
+ }
+ else
+ {
+ .error(loc, "undefined escape hex sequence \\%c%c", sequence[0], c);
+ p++;
+ }
+ break;
+ case '&':
+ if (Ccompile)
+ goto default;
+
+ // named character entity
+ for (const idstart = ++p; 1; p++)
+ {
+ switch (*p)
+ {
+ case ';':
+ c = HtmlNamedEntity(idstart, p - idstart);
+ if (c == ~0)
+ {
+ .error(loc, "unnamed character entity &%.*s;", cast(int)(p - idstart), idstart);
+ c = '?';
+ }
+ p++;
+ break;
+ default:
+ if (isalpha(*p) || (p != idstart && isdigit(*p)))
+ continue;
+ .error(loc, "unterminated named entity &%.*s;", cast(int)(p - idstart + 1), idstart);
+ c = '?';
+ break;
+ }
+ break;
+ }
+ break;
+ case 0:
+ case 0x1A:
+ // end of file
+ c = '\\';
+ break;
+ default:
+ if (isoctal(cast(char)c))
+ {
+ uint v = 0;
+ int n = 0;
+ do
+ {
+ v = v * 8 + (c - '0');
+ c = *++p;
+ }
+ while (++n < 3 && isoctal(cast(char)c));
+ c = v;
+ if (c > 0xFF)
+ .error(loc, "escape octal sequence \\%03o is larger than \\377", c);
+ }
+ else
+ {
+ .error(loc, "undefined escape sequence \\%c", c);
+ p++;
+ }
+ break;
+ }
+ return c;
+ }
+
+ /**
+ Lex a wysiwyg string. `p` must be pointing to the first character before the
+ contents of the string literal. The character pointed to by `p` will be used as
+ the terminating character (i.e. backtick or double-quote).
+ Params:
+ result = pointer to the token that accepts the result
+ */
+ private void wysiwygStringConstant(Token* result)
+ {
+ result.value = TOK.string_;
+ Loc start = loc();
+ auto terminator = p[0];
+ p++;
+ stringbuffer.setsize(0);
+ while (1)
+ {
+ dchar c = p[0];
+ p++;
+ switch (c)
+ {
+ case '\n':
+ endOfLine();
+ break;
+ case '\r':
+ if (p[0] == '\n')
+ continue; // ignore
+ c = '\n'; // treat EndOfLine as \n character
+ endOfLine();
+ break;
+ case 0:
+ case 0x1A:
+ error("unterminated string constant starting at %s", start.toChars());
+ result.setString();
+ // rewind `p` so it points to the EOF character
+ p--;
+ return;
+ default:
+ if (c == terminator)
+ {
+ result.setString(stringbuffer);
+ stringPostfix(result);
+ return;
+ }
+ else if (c & 0x80)
+ {
+ p--;
+ const u = decodeUTF();
+ p++;
+ if (u == PS || u == LS)
+ endOfLine();
+ stringbuffer.writeUTF8(u);
+ continue;
+ }
+ break;
+ }
+ stringbuffer.writeByte(c);
+ }
+ }
+
+ /**************************************
+ * Lex hex strings:
+ * x"0A ae 34FE BD"
+ */
+ private TOK hexStringConstant(Token* t)
+ {
+ Loc start = loc();
+ uint n = 0;
+ uint v = ~0; // dead assignment, needed to suppress warning
+ p++;
+ stringbuffer.setsize(0);
+ while (1)
+ {
+ dchar c = *p++;
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ case '\v':
+ case '\f':
+ continue; // skip white space
+ case '\r':
+ if (*p == '\n')
+ continue; // ignore '\r' if followed by '\n'
+ // Treat isolated '\r' as if it were a '\n'
+ goto case '\n';
+ case '\n':
+ endOfLine();
+ continue;
+ case 0:
+ case 0x1A:
+ error("unterminated string constant starting at %s", start.toChars());
+ t.setString();
+ // decrement `p`, because it needs to point to the next token (the 0 or 0x1A character is the TOK.endOfFile token).
+ p--;
+ return TOK.hexadecimalString;
+ case '"':
+ if (n & 1)
+ {
+ error("odd number (%d) of hex characters in hex string", n);
+ stringbuffer.writeByte(v);
+ }
+ t.setString(stringbuffer);
+ stringPostfix(t);
+ return TOK.hexadecimalString;
+ default:
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'a' && c <= 'f')
+ c -= 'a' - 10;
+ else if (c >= 'A' && c <= 'F')
+ c -= 'A' - 10;
+ else if (c & 0x80)
+ {
+ p--;
+ const u = decodeUTF();
+ p++;
+ if (u == PS || u == LS)
+ endOfLine();
+ else
+ error("non-hex character \\u%04x in hex string", u);
+ }
+ else
+ error("non-hex character '%c' in hex string", c);
+ if (n & 1)
+ {
+ v = (v << 4) | c;
+ stringbuffer.writeByte(v);
+ }
+ else
+ v = c;
+ n++;
+ break;
+ }
+ }
+ assert(0); // see bug 15731
+ }
+
+ /**
+ Lex a delimited string. Some examples of delimited strings are:
+ ---
+ q"(foo(xxx))" // "foo(xxx)"
+ q"[foo$(LPAREN)]" // "foo$(LPAREN)"
+ q"/foo]/" // "foo]"
+ q"HERE
+ foo
+ HERE" // "foo\n"
+ ---
+ It is assumed that `p` points to the opening double-quote '"'.
+ Params:
+ result = pointer to the token that accepts the result
+ */
+ private void delimitedStringConstant(Token* result)
+ {
+ result.value = TOK.string_;
+ Loc start = loc();
+ dchar delimleft = 0;
+ dchar delimright = 0;
+ uint nest = 1;
+ uint nestcount = ~0; // dead assignment, needed to suppress warning
+ Identifier hereid = null;
+ uint blankrol = 0;
+ uint startline = 0;
+ p++;
+ stringbuffer.setsize(0);
+ while (1)
+ {
+ dchar c = *p++;
+ //printf("c = '%c'\n", c);
+ switch (c)
+ {
+ case '\n':
+ Lnextline:
+ endOfLine();
+ startline = 1;
+ if (blankrol)
+ {
+ blankrol = 0;
+ continue;
+ }
+ if (hereid)
+ {
+ stringbuffer.writeUTF8(c);
+ continue;
+ }
+ break;
+ case '\r':
+ if (*p == '\n')
+ continue; // ignore
+ c = '\n'; // treat EndOfLine as \n character
+ goto Lnextline;
+ case 0:
+ case 0x1A:
+ error("unterminated delimited string constant starting at %s", start.toChars());
+ result.setString();
+ // decrement `p`, because it needs to point to the next token (the 0 or 0x1A character is the TOK.endOfFile token).
+ p--;
+ return;
+ default:
+ if (c & 0x80)
+ {
+ p--;
+ c = decodeUTF();
+ p++;
+ if (c == PS || c == LS)
+ goto Lnextline;
+ }
+ break;
+ }
+ if (delimleft == 0)
+ {
+ delimleft = c;
+ nest = 1;
+ nestcount = 1;
+ if (c == '(')
+ delimright = ')';
+ else if (c == '{')
+ delimright = '}';
+ else if (c == '[')
+ delimright = ']';
+ else if (c == '<')
+ delimright = '>';
+ else if (isalpha(c) || c == '_' || (c >= 0x80 && isUniAlpha(c)))
+ {
+ // Start of identifier; must be a heredoc
+ Token tok;
+ p--;
+ scan(&tok); // read in heredoc identifier
+ if (tok.value != TOK.identifier)
+ {
+ error("identifier expected for heredoc, not %s", tok.toChars());
+ delimright = c;
+ }
+ else
+ {
+ hereid = tok.ident;
+ //printf("hereid = '%s'\n", hereid.toChars());
+ blankrol = 1;
+ }
+ nest = 0;
+ }
+ else
+ {
+ delimright = c;
+ nest = 0;
+ if (isspace(c))
+ error("delimiter cannot be whitespace");
+ }
+ }
+ else
+ {
+ if (blankrol)
+ {
+ error("heredoc rest of line should be blank");
+ blankrol = 0;
+ continue;
+ }
+ if (nest == 1)
+ {
+ if (c == delimleft)
+ nestcount++;
+ else if (c == delimright)
+ {
+ nestcount--;
+ if (nestcount == 0)
+ goto Ldone;
+ }
+ }
+ else if (c == delimright)
+ goto Ldone;
+ if (startline && (isalpha(c) || c == '_' || (c >= 0x80 && isUniAlpha(c))) && hereid)
+ {
+ Token tok;
+ auto psave = p;
+ p--;
+ scan(&tok); // read in possible heredoc identifier
+ //printf("endid = '%s'\n", tok.ident.toChars());
+ if (tok.value == TOK.identifier && tok.ident is hereid)
+ {
+ /* should check that rest of line is blank
+ */
+ goto Ldone;
+ }
+ p = psave;
+ }
+ stringbuffer.writeUTF8(c);
+ startline = 0;
+ }
+ }
+ Ldone:
+ if (*p == '"')
+ p++;
+ else if (hereid)
+ error("delimited string must end in %s\"", hereid.toChars());
+ else
+ error("delimited string must end in %c\"", delimright);
+ result.setString(stringbuffer);
+ stringPostfix(result);
+ }
+
+ /**
+ Lex a token string. Some examples of token strings are:
+ ---
+ q{ foo(xxx) } // " foo(xxx) "
+ q{foo$(LPAREN)} // "foo$(LPAREN)"
+ q{{foo}"}"} // "{foo}"}""
+ ---
+ It is assumed that `p` points to the opening curly-brace.
+ Params:
+ result = pointer to the token that accepts the result
+ */
+ private void tokenStringConstant(Token* result)
+ {
+ result.value = TOK.string_;
+
+ uint nest = 1;
+ const start = loc();
+ const pstart = ++p;
+ inTokenStringConstant++;
+ scope(exit) inTokenStringConstant--;
+ while (1)
+ {
+ Token tok;
+ scan(&tok);
+ switch (tok.value)
+ {
+ case TOK.leftCurly:
+ nest++;
+ continue;
+ case TOK.rightCurly:
+ if (--nest == 0)
+ {
+ result.setString(pstart, p - 1 - pstart);
+ stringPostfix(result);
+ return;
+ }
+ continue;
+ case TOK.endOfFile:
+ error("unterminated token string constant starting at %s", start.toChars());
+ result.setString();
+ return;
+ default:
+ continue;
+ }
+ }
+ }
+
+ /**
+ Scan a quoted string while building the processed string value by
+ handling escape sequences. The result is returned in the given `t` token.
+ This function assumes that `p` currently points to the opening quote
+ of the string.
+ Params:
+ t = the token to set the resulting string to
+ * References:
+ * D https://dlang.org/spec/lex.html#double_quoted_strings
+ * ImportC C11 6.4.5
+ */
+ private void escapeStringConstant(Token* t)
+ {
+ t.value = TOK.string_;
+
+ const start = loc();
+ const tc = *p++; // opening quote
+ stringbuffer.setsize(0);
+ while (1)
+ {
+ dchar c = *p++;
+ switch (c)
+ {
+ case '\\':
+ switch (*p)
+ {
+ case '&':
+ if (Ccompile)
+ goto default;
+ goto case;
+
+ case 'u':
+ case 'U':
+ c = escapeSequence();
+ stringbuffer.writeUTF8(c);
+ continue;
+ default:
+ c = escapeSequence();
+ break;
+ }
+ break;
+ case '\n':
+ endOfLine();
+ if (Ccompile)
+ goto Lunterminated;
+ break;
+ case '\r':
+ if (*p == '\n')
+ continue; // ignore
+ c = '\n'; // treat EndOfLine as \n character
+ endOfLine();
+ if (Ccompile)
+ goto Lunterminated;
+ break;
+ case '\'':
+ case '"':
+ if (c != tc)
+ goto default;
+ t.setString(stringbuffer);
+ if (!Ccompile)
+ stringPostfix(t);
+ return;
+ case 0:
+ case 0x1A:
+ // decrement `p`, because it needs to point to the next token (the 0 or 0x1A character is the TOK.endOfFile token).
+ p--;
+ Lunterminated:
+ error("unterminated string constant starting at %s", start.toChars());
+ t.setString();
+ return;
+ default:
+ if (c & 0x80)
+ {
+ p--;
+ c = decodeUTF();
+ if (c == LS || c == PS)
+ {
+ c = '\n';
+ endOfLine();
+ if (Ccompile)
+ goto Lunterminated;
+ }
+ p++;
+ stringbuffer.writeUTF8(c);
+ continue;
+ }
+ break;
+ }
+ stringbuffer.writeByte(c);
+ }
+ }
+
+ /**************************************
+ * Reference:
+ * https://dlang.org/spec/lex.html#characterliteral
+ */
+ private TOK charConstant(Token* t)
+ {
+ TOK tk = TOK.charLiteral;
+ //printf("Lexer::charConstant\n");
+ p++;
+ dchar c = *p++;
+ switch (c)
+ {
+ case '\\':
+ switch (*p)
+ {
+ case 'u':
+ t.unsvalue = escapeSequence();
+ tk = TOK.wcharLiteral;
+ break;
+ case 'U':
+ case '&':
+ t.unsvalue = escapeSequence();
+ tk = TOK.dcharLiteral;
+ break;
+ default:
+ t.unsvalue = escapeSequence();
+ break;
+ }
+ break;
+ case '\n':
+ L1:
+ endOfLine();
+ goto case;
+ case '\r':
+ goto case '\'';
+ case 0:
+ case 0x1A:
+ // decrement `p`, because it needs to point to the next token (the 0 or 0x1A character is the TOK.endOfFile token).
+ p--;
+ goto case;
+ case '\'':
+ error("unterminated character constant");
+ t.unsvalue = '?';
+ return tk;
+ default:
+ if (c & 0x80)
+ {
+ p--;
+ c = decodeUTF();
+ p++;
+ if (c == LS || c == PS)
+ goto L1;
+ if (c < 0xD800 || (c >= 0xE000 && c < 0xFFFE))
+ tk = TOK.wcharLiteral;
+ else
+ tk = TOK.dcharLiteral;
+ }
+ t.unsvalue = c;
+ break;
+ }
+ if (*p != '\'')
+ {
+ while (*p != '\'' && *p != 0x1A && *p != 0 && *p != '\n' &&
+ *p != '\r' && *p != ';' && *p != ')' && *p != ']' && *p != '}')
+ {
+ if (*p & 0x80)
+ {
+ const s = p;
+ c = decodeUTF();
+ if (c == LS || c == PS)
+ {
+ p = s;
+ break;
+ }
+ }
+ p++;
+ }
+
+ if (*p == '\'')
+ {
+ error("character constant has multiple characters");
+ p++;
+ }
+ else
+ error("unterminated character constant");
+ t.unsvalue = '?';
+ return tk;
+ }
+ p++;
+ return tk;
+ }
+
+ /***************************************
+ * Lex C character constant.
+ * Parser is on the opening quote.
+ * Params:
+ * t = token to fill in
+ * prefix = one of `u`, `U` or 0.
+ * Reference:
+ * C11 6.4.4.4
+ */
+ private void clexerCharConstant(ref Token t, char prefix)
+ {
+ escapeStringConstant(&t);
+ const(char)[] str = t.ustring[0 .. t.len];
+ const n = str.length;
+ const loc = t.loc;
+ if (n == 0)
+ {
+ error(loc, "empty character constant");
+ t.value = TOK.semicolon;
+ return;
+ }
+
+ uint u;
+ switch (prefix)
+ {
+ case 0:
+ if (n == 1) // fast case
+ {
+ u = str[0];
+ }
+ else if (n > 4)
+ error(loc, "max number of chars in character literal is 4, had %d",
+ cast(int)n);
+ else
+ {
+ foreach (i, c; str)
+ (cast(char*)&u)[n - 1 - i] = c;
+ }
+ break;
+
+ case 'u':
+ dchar d1;
+ size_t idx;
+ auto msg = utf_decodeChar(str, idx, d1);
+ dchar d2 = 0;
+ if (idx < n && !msg)
+ msg = utf_decodeChar(str, idx, d2);
+ if (msg)
+ error(loc, "%s", msg);
+ else if (idx < n)
+ error(loc, "max number of chars in 16 bit character literal is 2, had %d",
+ (n + 1) >> 1);
+ else if (d1 > 0x1_0000)
+ error(loc, "%d does not fit in 16 bits", d1);
+ else if (d2 > 0x1_0000)
+ error(loc, "%d does not fit in 16 bits", d2);
+ u = d1;
+ if (d2)
+ u = (d1 << 16) | d2;
+ break;
+
+ case 'U':
+ dchar d;
+ size_t idx;
+ auto msg = utf_decodeChar(str, idx, d);
+ if (msg)
+ error(loc, "%s", msg);
+ else if (idx < n)
+ error(loc, "max number of chars in 32 bit character literal is 1, had %d",
+ (n + 3) >> 2);
+ u = d;
+ break;
+
+ default:
+ assert(0);
+ }
+ t.value = TOK.int32Literal;
+ t.unsvalue = u;
+ }
+
+ /***************************************
+ * Get postfix of string literal.
+ */
+ private void stringPostfix(Token* t) pure @nogc
+ {
+ switch (*p)
+ {
+ case 'c':
+ case 'w':
+ case 'd':
+ t.postfix = *p;
+ p++;
+ break;
+ default:
+ t.postfix = 0;
+ break;
+ }
+ }
+
+ /**************************************
+ * Read in a number.
+ * If it's an integer, store it in tok.TKutok.Vlong.
+ * integers can be decimal, octal or hex
+ * Handle the suffixes U, UL, LU, L, etc.
+ * If it's double, store it in tok.TKutok.Vdouble.
+ * Returns:
+ * TKnum
+ * TKdouble,...
+ */
+ private TOK number(Token* t)
+ {
+ int base = 10;
+ const start = p;
+ uinteger_t n = 0; // unsigned >=64 bit integer type
+ int d;
+ bool err = false;
+ bool overflow = false;
+ bool anyBinaryDigitsNoSingleUS = false;
+ bool anyHexDigitsNoSingleUS = false;
+ dchar c = *p;
+ if (c == '0')
+ {
+ ++p;
+ c = *p;
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ base = 8;
+ break;
+
+ case '8':
+ case '9':
+ if (Ccompile)
+ error("octal digit expected, not `%c`", c);
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ ++p;
+ base = 16;
+ break;
+ case 'b':
+ case 'B':
+ if (Ccompile)
+ error("binary constants not allowed");
+ ++p;
+ base = 2;
+ break;
+ case '.':
+ if (p[1] == '.')
+ goto Ldone; // if ".."
+ if (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80)
+ goto Ldone; // if ".identifier" or ".unicode"
+ goto Lreal; // '.' is part of current token
+ case 'i':
+ case 'f':
+ case 'F':
+ goto Lreal;
+ case '_':
+ if (Ccompile)
+ error("embedded `_` not allowed");
+ ++p;
+ base = 8;
+ break;
+ case 'L':
+ if (p[1] == 'i')
+ goto Lreal;
+ break;
+ default:
+ break;
+ }
+ }
+ while (1)
+ {
+ c = *p;
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ++p;
+ d = c - '0';
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ ++p;
+ if (base != 16)
+ {
+ if (c == 'e' || c == 'E' || c == 'f' || c == 'F')
+ goto Lreal;
+ }
+ if (c >= 'a')
+ d = c + 10 - 'a';
+ else
+ d = c + 10 - 'A';
+ break;
+ case 'L':
+ if (p[1] == 'i')
+ goto Lreal;
+ goto Ldone;
+ case '.':
+ if (p[1] == '.')
+ goto Ldone; // if ".."
+ if (base == 10 && (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80))
+ goto Ldone; // if ".identifier" or ".unicode"
+ if (base == 16 && (!ishex(p[1]) || p[1] == '_' || p[1] & 0x80))
+ goto Ldone; // if ".identifier" or ".unicode"
+ if (base == 2)
+ goto Ldone; // if ".identifier" or ".unicode"
+ goto Lreal; // otherwise as part of a floating point literal
+ case 'p':
+ case 'P':
+ case 'i':
+ Lreal:
+ p = start;
+ return inreal(t);
+ case '_':
+ if (Ccompile)
+ goto default;
+ ++p;
+ continue;
+ default:
+ goto Ldone;
+ }
+ // got a digit here, set any necessary flags, check for errors
+ anyHexDigitsNoSingleUS = true;
+ anyBinaryDigitsNoSingleUS = true;
+ if (!err && d >= base)
+ {
+ error("%s digit expected, not `%c`", base == 2 ? "binary".ptr :
+ base == 8 ? "octal".ptr :
+ "decimal".ptr, c);
+ err = true;
+ }
+ // Avoid expensive overflow check if we aren't at risk of overflow
+ if (n <= 0x0FFF_FFFF_FFFF_FFFFUL)
+ n = n * base + d;
+ else
+ {
+ import core.checkedint : mulu, addu;
+
+ n = mulu(n, base, overflow);
+ n = addu(n, d, overflow);
+ }
+ }
+ Ldone:
+ if (overflow && !err)
+ {
+ error("integer overflow");
+ err = true;
+ }
+ if ((base == 2 && !anyBinaryDigitsNoSingleUS) ||
+ (base == 16 && !anyHexDigitsNoSingleUS))
+ error("`%.*s` isn't a valid integer literal, use `%.*s0` instead", cast(int)(p - start), start, 2, start);
+
+ t.unsvalue = n;
+
+ if (Ccompile)
+ return cnumber(base, n);
+
+ enum FLAGS : int
+ {
+ none = 0,
+ decimal = 1, // decimal
+ unsigned = 2, // u or U suffix
+ long_ = 4, // L suffix
+ }
+
+ FLAGS flags = (base == 10) ? FLAGS.decimal : FLAGS.none;
+ // Parse trailing 'u', 'U', 'l' or 'L' in any combination
+ const psuffix = p;
+ while (1)
+ {
+ FLAGS f;
+ switch (*p)
+ {
+ case 'U':
+ case 'u':
+ f = FLAGS.unsigned;
+ goto L1;
+ case 'l':
+ f = FLAGS.long_;
+ error("lower case integer suffix 'l' is not allowed. Please use 'L' instead");
+ goto L1;
+ case 'L':
+ f = FLAGS.long_;
+ L1:
+ p++;
+ if ((flags & f) && !err)
+ {
+ error("unrecognized token");
+ err = true;
+ }
+ flags = cast(FLAGS)(flags | f);
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ if (base == 8 && n >= 8)
+ {
+ if (err)
+ // can't translate invalid octal value, just show a generic message
+ error("octal literals larger than 7 are no longer supported");
+ else
+ error("octal literals `0%llo%.*s` are no longer supported, use `std.conv.octal!%llo%.*s` instead",
+ n, cast(int)(p - psuffix), psuffix, n, cast(int)(p - psuffix), psuffix);
+ }
+ TOK result;
+ switch (flags)
+ {
+ case FLAGS.none:
+ /* Octal or Hexadecimal constant.
+ * First that fits: int, uint, long, ulong
+ */
+ if (n & 0x8000000000000000L)
+ result = TOK.uns64Literal;
+ else if (n & 0xFFFFFFFF00000000L)
+ result = TOK.int64Literal;
+ else if (n & 0x80000000)
+ result = TOK.uns32Literal;
+ else
+ result = TOK.int32Literal;
+ break;
+ case FLAGS.decimal:
+ /* First that fits: int, long, long long
+ */
+ if (n & 0x8000000000000000L)
+ {
+ result = TOK.uns64Literal;
+ }
+ else if (n & 0xFFFFFFFF80000000L)
+ result = TOK.int64Literal;
+ else
+ result = TOK.int32Literal;
+ break;
+ case FLAGS.unsigned:
+ case FLAGS.decimal | FLAGS.unsigned:
+ /* First that fits: uint, ulong
+ */
+ if (n & 0xFFFFFFFF00000000L)
+ result = TOK.uns64Literal;
+ else
+ result = TOK.uns32Literal;
+ break;
+ case FLAGS.decimal | FLAGS.long_:
+ if (n & 0x8000000000000000L)
+ {
+ if (!err)
+ {
+ error("signed integer overflow");
+ err = true;
+ }
+ result = TOK.uns64Literal;
+ }
+ else
+ result = TOK.int64Literal;
+ break;
+ case FLAGS.long_:
+ if (n & 0x8000000000000000L)
+ result = TOK.uns64Literal;
+ else
+ result = TOK.int64Literal;
+ break;
+ case FLAGS.unsigned | FLAGS.long_:
+ case FLAGS.decimal | FLAGS.unsigned | FLAGS.long_:
+ result = TOK.uns64Literal;
+ break;
+ default:
+ debug
+ {
+ printf("%x\n", flags);
+ }
+ assert(0);
+ }
+ return result;
+ }
+
+ /**************************************
+ * Lex C integer-suffix
+ * Params:
+ * base = number base
+ * n = raw integer value
+ * Returns:
+ * token value
+ */
+ private TOK cnumber(int base, uinteger_t n)
+ {
+ /* C11 6.4.4.1
+ * Parse trailing suffixes:
+ * u or U
+ * l or L
+ * ll or LL
+ */
+ enum FLAGS : uint
+ {
+ octalhex = 1, // octal or hexadecimal
+ decimal = 2, // decimal
+ unsigned = 4, // u or U suffix
+ long_ = 8, // l or L suffix
+ llong = 0x10 // ll or LL
+ }
+ FLAGS flags = (base == 10) ? FLAGS.decimal : FLAGS.octalhex;
+ bool err;
+ Lsuffixes:
+ while (1)
+ {
+ FLAGS f;
+ const cs = *p;
+ switch (cs)
+ {
+ case 'U':
+ case 'u':
+ f = FLAGS.unsigned;
+ break;
+
+ case 'l':
+ case 'L':
+ f = FLAGS.long_;
+ if (cs == p[1])
+ {
+ f = FLAGS.long_ | FLAGS.llong;
+ ++p;
+ }
+ break;
+
+ default:
+ break Lsuffixes;
+ }
+ ++p;
+ if ((flags & f) && !err)
+ {
+ error("duplicate integer suffixes");
+ err = true;
+ }
+ flags = cast(FLAGS)(flags | f);
+ }
+
+ void overflow()
+ {
+ error("integer overflow");
+ }
+
+ TOK result = TOK.int32Literal; // default
+ switch (flags)
+ {
+ /* Since D doesn't have a variable sized `long` or `unsigned long` type,
+ * this code deviates from C by picking D int, uint, long, or ulong instead
+ */
+
+ case FLAGS.octalhex:
+ /* Octal or Hexadecimal constant.
+ * First that fits: int, unsigned, long, unsigned long,
+ * long long, unsigned long long
+ */
+ if (longsize == 4)
+ {
+ if (n & 0x8000000000000000L)
+ result = TOK.uns64Literal;
+ else if (n & 0xFFFFFFFF00000000L)
+ result = TOK.int64Literal;
+ else if (n & 0x80000000)
+ result = TOK.uns32Literal;
+ else
+ result = TOK.int32Literal;
+ }
+ else
+ {
+ if (n & 0x8000000000000000L)
+ result = TOK.uns64Literal; // unsigned long
+ else if (n & 0xFFFFFFFF00000000L)
+ result = TOK.int64Literal; // long
+ else if (n & 0x80000000)
+ result = TOK.uns32Literal;
+ else
+ result = TOK.int32Literal;
+ }
+ break;
+
+ case FLAGS.decimal:
+ /* First that fits: int, long, long long
+ */
+ if (longsize == 4)
+ {
+ if (n & 0x8000000000000000L)
+ result = TOK.uns64Literal;
+ else if (n & 0xFFFFFFFF80000000L)
+ result = TOK.int64Literal;
+ else
+ result = TOK.int32Literal;
+ }
+ else
+ {
+ if (n & 0x8000000000000000L)
+ result = TOK.uns64Literal; // unsigned long
+ else if (n & 0xFFFFFFFF80000000L)
+ result = TOK.int64Literal; // long
+ else
+ result = TOK.int32Literal;
+ }
+ break;
+
+ case FLAGS.octalhex | FLAGS.unsigned:
+ case FLAGS.decimal | FLAGS.unsigned:
+ /* First that fits: unsigned, unsigned long, unsigned long long
+ */
+ if (longsize == 4)
+ {
+ if (n & 0xFFFFFFFF00000000L)
+ result = TOK.uns64Literal;
+ else
+ result = TOK.uns32Literal;
+ }
+ else
+ {
+ if (n & 0xFFFFFFFF00000000L)
+ result = TOK.uns64Literal; // unsigned long
+ else
+ result = TOK.uns32Literal;
+ }
+ break;
+
+ case FLAGS.decimal | FLAGS.long_:
+ /* First that fits: long, long long
+ */
+ if (longsize == 4)
+ {
+ if (n & 0x8000000000000000L)
+ overflow();
+ else if (n & 0xFFFFFFFF_80000000L)
+ result = TOK.int64Literal;
+ else
+ result = TOK.int32Literal; // long
+ }
+ else
+ {
+ if (n & 0x8000000000000000L)
+ overflow();
+ else
+ result = TOK.int64Literal; // long
+ }
+ break;
+
+ case FLAGS.octalhex | FLAGS.long_:
+ /* First that fits: long, unsigned long, long long,
+ * unsigned long long
+ */
+ if (longsize == 4)
+ {
+ if (n & 0x8000000000000000L)
+ result = TOK.uns64Literal;
+ else if (n & 0xFFFFFFFF00000000L)
+ result = TOK.int64Literal;
+ else if (n & 0x80000000)
+ result = TOK.uns32Literal; // unsigned long
+ else
+ result = TOK.int32Literal; // long
+ }
+ else
+ {
+ if (n & 0x80000000_00000000L)
+ result = TOK.uns64Literal; // unsigned long
+ else
+ result = TOK.int64Literal; // long
+ }
+ break;
+
+ case FLAGS.octalhex | FLAGS.unsigned | FLAGS.long_:
+ case FLAGS.decimal | FLAGS.unsigned | FLAGS.long_:
+ /* First that fits: unsigned long, unsigned long long
+ */
+ if (longsize == 4)
+ {
+ if (n & 0xFFFFFFFF00000000L)
+ result = TOK.uns64Literal;
+ else
+ result = TOK.uns32Literal; // unsigned long
+ }
+ else
+ {
+ result = TOK.uns64Literal; // unsigned long
+ }
+ break;
+
+ case FLAGS.octalhex | FLAGS.long_ | FLAGS.llong:
+ /* First that fits: long long, unsigned long long
+ */
+ if (n & 0x8000000000000000L)
+ result = TOK.uns64Literal;
+ else
+ result = TOK.int64Literal;
+ break;
+
+ case FLAGS.decimal | FLAGS.long_ | FLAGS.llong:
+ /* long long
+ */
+ result = TOK.int64Literal;
+ break;
+
+ case FLAGS.octalhex | FLAGS.long_ | FLAGS.unsigned | FLAGS.llong:
+ case FLAGS.decimal | FLAGS.long_ | FLAGS.unsigned | FLAGS.llong:
+ result = TOK.uns64Literal;
+ break;
+
+ default:
+ debug printf("%x\n",flags);
+ assert(0);
+ }
+ return result;
+ }
+
+ /**************************************
+ * Read in characters, converting them to real.
+ * Bugs:
+ * Exponent overflow not detected.
+ * Too much requested precision is not detected.
+ */
+ private TOK inreal(Token* t)
+ {
+ //printf("Lexer::inreal()\n");
+ debug
+ {
+ assert(*p == '.' || isdigit(*p));
+ }
+ bool isWellformedString = true;
+ stringbuffer.setsize(0);
+ auto pstart = p;
+ bool hex = false;
+ dchar c = *p++;
+ // Leading '0x'
+ if (c == '0')
+ {
+ c = *p++;
+ if (c == 'x' || c == 'X')
+ {
+ hex = true;
+ c = *p++;
+ }
+ }
+ // Digits to left of '.'
+ while (1)
+ {
+ if (c == '.')
+ {
+ c = *p++;
+ break;
+ }
+ if (isdigit(c) || (hex && isxdigit(c)) || c == '_')
+ {
+ c = *p++;
+ continue;
+ }
+ break;
+ }
+ // Digits to right of '.'
+ while (1)
+ {
+ if (isdigit(c) || (hex && isxdigit(c)) || c == '_')
+ {
+ c = *p++;
+ continue;
+ }
+ break;
+ }
+ if (c == 'e' || c == 'E' || (hex && (c == 'p' || c == 'P')))
+ {
+ c = *p++;
+ if (c == '-' || c == '+')
+ {
+ c = *p++;
+ }
+ bool anyexp = false;
+ while (1)
+ {
+ if (isdigit(c))
+ {
+ anyexp = true;
+ c = *p++;
+ continue;
+ }
+ if (c == '_')
+ {
+ if (Ccompile)
+ error("embedded `_` in numeric literals not allowed");
+ c = *p++;
+ continue;
+ }
+ if (!anyexp)
+ {
+ error("missing exponent");
+ isWellformedString = false;
+ }
+ break;
+ }
+ }
+ else if (hex)
+ {
+ error("exponent required for hex float");
+ isWellformedString = false;
+ }
+ --p;
+ while (pstart < p)
+ {
+ if (*pstart != '_')
+ stringbuffer.writeByte(*pstart);
+ ++pstart;
+ }
+ stringbuffer.writeByte(0);
+ auto sbufptr = cast(const(char)*)stringbuffer[].ptr;
+ TOK result;
+ bool isOutOfRange = false;
+ t.floatvalue = (isWellformedString ? CTFloat.parse(sbufptr, &isOutOfRange) : CTFloat.zero);
+ switch (*p)
+ {
+ case 'F':
+ case 'f':
+ if (isWellformedString && !isOutOfRange)
+ isOutOfRange = Port.isFloat32LiteralOutOfRange(sbufptr);
+ result = TOK.float32Literal;
+ p++;
+ break;
+ default:
+ if (isWellformedString && !isOutOfRange)
+ isOutOfRange = Port.isFloat64LiteralOutOfRange(sbufptr);
+ result = TOK.float64Literal;
+ break;
+ case 'l':
+ if (!Ccompile)
+ error("use 'L' suffix instead of 'l'");
+ goto case 'L';
+ case 'L':
+ ++p;
+ if (Ccompile && long_doublesize == 8)
+ goto default;
+ result = TOK.float80Literal;
+ break;
+ }
+ if ((*p == 'i' || *p == 'I') && !Ccompile)
+ {
+ if (*p == 'I')
+ error("use 'i' suffix instead of 'I'");
+ p++;
+ switch (result)
+ {
+ case TOK.float32Literal:
+ result = TOK.imaginary32Literal;
+ break;
+ case TOK.float64Literal:
+ result = TOK.imaginary64Literal;
+ break;
+ case TOK.float80Literal:
+ result = TOK.imaginary80Literal;
+ break;
+ default:
+ break;
+ }
+ }
+ const isLong = (result == TOK.float80Literal || result == TOK.imaginary80Literal);
+ if (isOutOfRange && !isLong)
+ {
+ const char* suffix = (result == TOK.float32Literal || result == TOK.imaginary32Literal) ? "f" : "";
+ error(scanloc, "number `%s%s` is not representable", sbufptr, suffix);
+ }
+ debug
+ {
+ switch (result)
+ {
+ case TOK.float32Literal:
+ case TOK.float64Literal:
+ case TOK.float80Literal:
+ case TOK.imaginary32Literal:
+ case TOK.imaginary64Literal:
+ case TOK.imaginary80Literal:
+ break;
+ default:
+ assert(0);
+ }
+ }
+ return result;
+ }
+
+ final Loc loc() pure @nogc
+ {
+ scanloc.charnum = cast(uint)(1 + p - line);
+ version (LocOffset)
+ scanloc.fileOffset = cast(uint)(p - base);
+ return scanloc;
+ }
+
+ final void error(const(char)* format, ...)
+ {
+ va_list args;
+ va_start(args, format);
+ .verror(token.loc, format, args);
+ va_end(args);
+ }
+
+ final void error(const ref Loc loc, const(char)* format, ...)
+ {
+ va_list args;
+ va_start(args, format);
+ .verror(loc, format, args);
+ va_end(args);
+ }
+
+ final void deprecation(const(char)* format, ...)
+ {
+ va_list args;
+ va_start(args, format);
+ .vdeprecation(token.loc, format, args);
+ va_end(args);
+ }
+
+ /*********************************************
+ * Parse line/file preprocessor directive:
+ * #line linnum [filespec]
+ * Allow __LINE__ for linnum, and __FILE__ for filespec.
+ * Accept linemarker format:
+ * # linnum [filespec] {flags}
+ * There can be zero or more flags, which are one of the digits 1..4, and
+ * must be in ascending order. The flags are ignored.
+ * Params:
+ * tok = token we're on, which is linnum of linemarker
+ * linemarker = true if line marker format and lexer is on linnum
+ * References:
+ * linemarker https://gcc.gnu.org/onlinedocs/gcc-11.1.0/cpp/Preprocessor-Output.html
+ */
+ private void poundLine(ref Token tok, bool linemarker)
+ {
+ auto linnum = this.scanloc.linnum;
+ const(char)* filespec = null;
+ const loc = this.loc();
+ bool flags;
+
+ if (!linemarker)
+ scan(&tok);
+ if (tok.value == TOK.int32Literal || tok.value == TOK.int64Literal)
+ {
+ const lin = cast(int)(tok.unsvalue - 1);
+ if (lin != tok.unsvalue - 1)
+ error("line number `%lld` out of range", cast(ulong)tok.unsvalue);
+ else
+ linnum = lin;
+ }
+ else if (tok.value == TOK.line) // #line __LINE__
+ {
+ }
+ else
+ goto Lerr;
+ while (1)
+ {
+ switch (*p)
+ {
+ case 0:
+ case 0x1A:
+ case '\n':
+ Lnewline:
+ if (!inTokenStringConstant)
+ {
+ this.scanloc.linnum = linnum;
+ if (filespec)
+ this.scanloc.filename = filespec;
+ }
+ return;
+ case '\r':
+ p++;
+ if (*p != '\n')
+ {
+ p--;
+ goto Lnewline;
+ }
+ continue;
+ case ' ':
+ case '\t':
+ case '\v':
+ case '\f':
+ p++;
+ continue; // skip white space
+ case '_':
+ if (filespec || flags)
+ goto Lerr;
+ if (memcmp(p, "__FILE__".ptr, 8) == 0)
+ {
+ p += 8;
+ filespec = mem.xstrdup(scanloc.filename);
+ continue;
+ }
+ goto Lerr;
+ case '"':
+ if (filespec || flags)
+ goto Lerr;
+ stringbuffer.setsize(0);
+ p++;
+ while (1)
+ {
+ uint c;
+ c = *p;
+ switch (c)
+ {
+ case '\n':
+ case '\r':
+ case 0:
+ case 0x1A:
+ goto Lerr;
+ case '"':
+ stringbuffer.writeByte(0);
+ filespec = mem.xstrdup(cast(const(char)*)stringbuffer[].ptr);
+ p++;
+ break;
+ default:
+ if (c & 0x80)
+ {
+ uint u = decodeUTF();
+ if (u == PS || u == LS)
+ goto Lerr;
+ }
+ stringbuffer.writeByte(c);
+ p++;
+ continue;
+ }
+ break;
+ }
+ continue;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ flags = true; // linemarker flags seen
+ ++p;
+ if ('0' <= *p && *p <= '9')
+ goto Lerr; // only one digit allowed
+ continue;
+
+ default:
+ if (*p & 0x80)
+ {
+ uint u = decodeUTF();
+ if (u == PS || u == LS)
+ goto Lnewline;
+ }
+ goto Lerr;
+ }
+ }
+ Lerr:
+ if (linemarker)
+ error(loc, "# integer [\"filespec\"] { 1 | 2 | 3 | 4 }\\n expected");
+ else
+ error(loc, "#line integer [\"filespec\"]\\n expected");
+ }
+
+ /********************************************
+ * Decode UTF character.
+ * Issue error messages for invalid sequences.
+ * Return decoded character, advance p to last character in UTF sequence.
+ */
+ private uint decodeUTF()
+ {
+ const s = p;
+ assert(*s & 0x80);
+ // Check length of remaining string up to 4 UTF-8 characters
+ size_t len;
+ for (len = 1; len < 4 && s[len]; len++)
+ {
+ }
+ size_t idx = 0;
+ dchar u;
+ const msg = utf_decodeChar(s[0 .. len], idx, u);
+ p += idx - 1;
+ if (msg)
+ {
+ error("%.*s", cast(int)msg.length, msg.ptr);
+ }
+ return u;
+ }
+
+ /***************************************************
+ * Parse doc comment embedded between t.ptr and p.
+ * Remove trailing blanks and tabs from lines.
+ * Replace all newlines with \n.
+ * Remove leading comment character from each line.
+ * Decide if it's a lineComment or a blockComment.
+ * Append to previous one for this token.
+ *
+ * If newParagraph is true, an extra newline will be
+ * added between adjoining doc comments.
+ */
+ private void getDocComment(Token* t, uint lineComment, bool newParagraph) pure
+ {
+ /* ct tells us which kind of comment it is: '/', '*', or '+'
+ */
+ const ct = t.ptr[2];
+ /* Start of comment text skips over / * *, / + +, or / / /
+ */
+ const(char)* q = t.ptr + 3; // start of comment text
+ const(char)* qend = p;
+ if (ct == '*' || ct == '+')
+ qend -= 2;
+ /* Scan over initial row of ****'s or ++++'s or ////'s
+ */
+ for (; q < qend; q++)
+ {
+ if (*q != ct)
+ break;
+ }
+ /* Remove leading spaces until start of the comment
+ */
+ int linestart = 0;
+ if (ct == '/')
+ {
+ while (q < qend && (*q == ' ' || *q == '\t'))
+ ++q;
+ }
+ else if (q < qend)
+ {
+ if (*q == '\r')
+ {
+ ++q;
+ if (q < qend && *q == '\n')
+ ++q;
+ linestart = 1;
+ }
+ else if (*q == '\n')
+ {
+ ++q;
+ linestart = 1;
+ }
+ }
+ /* Remove trailing row of ****'s or ++++'s
+ */
+ if (ct != '/')
+ {
+ for (; q < qend; qend--)
+ {
+ if (qend[-1] != ct)
+ break;
+ }
+ }
+ /* Comment is now [q .. qend].
+ * Canonicalize it into buf[].
+ */
+ OutBuffer buf;
+
+ void trimTrailingWhitespace()
+ {
+ const s = buf[];
+ auto len = s.length;
+ while (len && (s[len - 1] == ' ' || s[len - 1] == '\t'))
+ --len;
+ buf.setsize(len);
+ }
+
+ for (; q < qend; q++)
+ {
+ char c = *q;
+ switch (c)
+ {
+ case '*':
+ case '+':
+ if (linestart && c == ct)
+ {
+ linestart = 0;
+ /* Trim preceding whitespace up to preceding \n
+ */
+ trimTrailingWhitespace();
+ continue;
+ }
+ break;
+ case ' ':
+ case '\t':
+ break;
+ case '\r':
+ if (q[1] == '\n')
+ continue; // skip the \r
+ goto Lnewline;
+ default:
+ if (c == 226)
+ {
+ // If LS or PS
+ if (q[1] == 128 && (q[2] == 168 || q[2] == 169))
+ {
+ q += 2;
+ goto Lnewline;
+ }
+ }
+ linestart = 0;
+ break;
+ Lnewline:
+ c = '\n'; // replace all newlines with \n
+ goto case;
+ case '\n':
+ linestart = 1;
+ /* Trim trailing whitespace
+ */
+ trimTrailingWhitespace();
+ break;
+ }
+ buf.writeByte(c);
+ }
+ /* Trim trailing whitespace (if the last line does not have newline)
+ */
+ trimTrailingWhitespace();
+
+ // Always end with a newline
+ const s = buf[];
+ if (s.length == 0 || s[$ - 1] != '\n')
+ buf.writeByte('\n');
+
+ // It's a line comment if the start of the doc comment comes
+ // after other non-whitespace on the same line.
+ auto dc = (lineComment && anyToken) ? &t.lineComment : &t.blockComment;
+ // Combine with previous doc comment, if any
+ if (*dc)
+ *dc = combineComments(*dc, buf[], newParagraph).toDString();
+ else
+ *dc = buf.extractSlice(true);
+ }
+
+ /********************************************
+ * Combine two document comments into one,
+ * separated by an extra newline if newParagraph is true.
+ */
+ static const(char)* combineComments(const(char)[] c1, const(char)[] c2, bool newParagraph) pure
+ {
+ //printf("Lexer::combineComments('%s', '%s', '%i')\n", c1, c2, newParagraph);
+ const(int) newParagraphSize = newParagraph ? 1 : 0; // Size of the combining '\n'
+ if (!c1)
+ return c2.ptr;
+ if (!c2)
+ return c1.ptr;
+
+ int insertNewLine = 0;
+ if (c1.length && c1[$ - 1] != '\n')
+ insertNewLine = 1;
+ const retSize = c1.length + insertNewLine + newParagraphSize + c2.length;
+ auto p = cast(char*)mem.xmalloc_noscan(retSize + 1);
+ p[0 .. c1.length] = c1[];
+ if (insertNewLine)
+ p[c1.length] = '\n';
+ if (newParagraph)
+ p[c1.length + insertNewLine] = '\n';
+ p[retSize - c2.length .. retSize] = c2[];
+ p[retSize] = 0;
+ return p;
+ }
+
+private:
+ void endOfLine() pure @nogc @safe
+ {
+ scanloc.linnum++;
+ line = p;
+ }
+}
+
+/// Support for `__DATE__`, `__TIME__`, and `__TIMESTAMP__`
+private struct TimeStampInfo
+{
+ private __gshared bool initdone = false;
+
+ // Note: Those properties need to be guarded by a call to `init`
+ // The API isn't safe, and quite brittle, but it was left this way
+ // over performance concerns.
+ // This is currently only called once, from the lexer.
+ __gshared char[11 + 1] date;
+ __gshared char[8 + 1] time;
+ __gshared char[24 + 1] timestamp;
+
+ public static void initialize(const ref Loc loc) nothrow
+ {
+ if (initdone)
+ return;
+
+ initdone = true;
+ time_t ct;
+ // https://issues.dlang.org/show_bug.cgi?id=20444
+ if (auto p = getenv("SOURCE_DATE_EPOCH"))
+ {
+ if (!ct.parseDigits(p.toDString()))
+ error(loc, "Value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p);
+ }
+ else
+ .time(&ct);
+ const p = ctime(&ct);
+ assert(p);
+ sprintf(&date[0], "%.6s %.4s", p + 4, p + 20);
+ sprintf(&time[0], "%.8s", p + 11);
+ sprintf(&timestamp[0], "%.24s", p);
+ }
+}
+
+unittest
+{
+ import dmd.console;
+ nothrow bool assertDiagnosticHandler(const ref Loc loc, Color headerColor, const(char)* header,
+ const(char)* format, va_list ap, const(char)* p1, const(char)* p2)
+ {
+ assert(0);
+ }
+ diagnosticHandler = &assertDiagnosticHandler;
+
+ static void test(T)(string sequence, T expected, bool Ccompile = false)
+ {
+ auto p = cast(const(char)*)sequence.ptr;
+ assert(expected == Lexer.escapeSequence(Loc.initial, p, Ccompile));
+ assert(p == sequence.ptr + sequence.length);
+ }
+
+ test(`'`, '\'');
+ test(`"`, '"');
+ test(`?`, '?');
+ test(`\`, '\\');
+ test(`0`, '\0');
+ test(`a`, '\a');
+ test(`b`, '\b');
+ test(`f`, '\f');
+ test(`n`, '\n');
+ test(`r`, '\r');
+ test(`t`, '\t');
+ test(`v`, '\v');
+
+ test(`x00`, 0x00);
+ test(`xff`, 0xff);
+ test(`xFF`, 0xff);
+ test(`xa7`, 0xa7);
+ test(`x3c`, 0x3c);
+ test(`xe2`, 0xe2);
+
+ test(`1`, '\1');
+ test(`42`, '\42');
+ test(`357`, '\357');
+
+ test(`u1234`, '\u1234');
+ test(`uf0e4`, '\uf0e4');
+
+ test(`U0001f603`, '\U0001f603');
+
+ test(`&quot;`, '"');
+ test(`&lt;`, '<');
+ test(`&gt;`, '>');
+
+ diagnosticHandler = null;
+}
+unittest
+{
+ import dmd.console;
+ string expected;
+ bool gotError;
+
+ nothrow bool expectDiagnosticHandler(const ref Loc loc, Color headerColor, const(char)* header,
+ const(char)* format, va_list ap, const(char)* p1, const(char)* p2)
+ {
+ assert(cast(Classification)headerColor == Classification.error);
+
+ gotError = true;
+ char[100] buffer = void;
+ auto actual = buffer[0 .. vsprintf(buffer.ptr, format, ap)];
+ assert(expected == actual);
+ return true;
+ }
+
+ diagnosticHandler = &expectDiagnosticHandler;
+
+ void test(string sequence, string expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false)
+ {
+ uint errors = global.errors;
+ gotError = false;
+ expected = expectedError;
+ auto p = cast(const(char)*)sequence.ptr;
+ auto actualReturnValue = Lexer.escapeSequence(Loc.initial, p, Ccompile);
+ assert(gotError);
+ assert(expectedReturnValue == actualReturnValue);
+
+ auto actualScanLength = p - sequence.ptr;
+ assert(expectedScanLength == actualScanLength);
+ global.errors = errors;
+ }
+
+ test("c", `undefined escape sequence \c`, 'c', 1);
+ test("!", `undefined escape sequence \!`, '!', 1);
+ test("&quot;", `undefined escape sequence \&`, '&', 1, true);
+
+ test("x1", `escape hex sequence has 1 hex digits instead of 2`, '\x01', 2);
+
+ test("u1" , `escape hex sequence has 1 hex digits instead of 4`, 0x1, 2);
+ test("u12" , `escape hex sequence has 2 hex digits instead of 4`, 0x12, 3);
+ test("u123", `escape hex sequence has 3 hex digits instead of 4`, 0x123, 4);
+
+ test("U0" , `escape hex sequence has 1 hex digits instead of 8`, 0x0, 2);
+ test("U00" , `escape hex sequence has 2 hex digits instead of 8`, 0x00, 3);
+ test("U000" , `escape hex sequence has 3 hex digits instead of 8`, 0x000, 4);
+ test("U0000" , `escape hex sequence has 4 hex digits instead of 8`, 0x0000, 5);
+ test("U0001f" , `escape hex sequence has 5 hex digits instead of 8`, 0x0001f, 6);
+ test("U0001f6" , `escape hex sequence has 6 hex digits instead of 8`, 0x0001f6, 7);
+ test("U0001f60", `escape hex sequence has 7 hex digits instead of 8`, 0x0001f60, 8);
+
+ test("ud800" , `invalid UTF character \U0000d800`, '?', 5);
+ test("udfff" , `invalid UTF character \U0000dfff`, '?', 5);
+ test("U00110000", `invalid UTF character \U00110000`, '?', 9);
+
+ test("xg0" , `undefined escape hex sequence \xg`, 'g', 2);
+ test("ug000" , `undefined escape hex sequence \ug`, 'g', 2);
+ test("Ug0000000", `undefined escape hex sequence \Ug`, 'g', 2);
+
+ test("&BAD;", `unnamed character entity &BAD;` , '?', 5);
+ test("&quot", `unterminated named entity &quot;`, '?', 5);
+ test("&quot", `unterminated named entity &quot;`, '?', 5);
+
+ test("400", `escape octal sequence \400 is larger than \377`, 0x100, 3);
+
+ diagnosticHandler = null;
+}
diff --git a/gcc/d/dmd/mangle.h b/gcc/d/dmd/mangle.h
index 544f77b8b0b..670cf4d6230 100644
--- a/gcc/d/dmd/mangle.h
+++ b/gcc/d/dmd/mangle.h
@@ -17,16 +17,16 @@ class TemplateInstance;
class Type;
struct OutBuffer;
-// In cppmangle.c
+// In cppmangle.d
const char *toCppMangleItanium(Dsymbol *s);
const char *cppTypeInfoMangleItanium(Dsymbol *s);
const char *cppThunkMangleItanium(FuncDeclaration *fd, int offset);
-// In cppmanglewin.c
+// In cppmanglewin.d
const char *toCppMangleMSVC(Dsymbol *s);
const char *cppTypeInfoMangleMSVC(Dsymbol *s);
-// In dmangle.c
+// In dmangle.d
const char *mangleExact(FuncDeclaration *fd);
void mangleToBuffer(Type *s, OutBuffer *buf);
void mangleToBuffer(Expression *s, OutBuffer *buf);
diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h
index 1664492bc2d..969290c476c 100644
--- a/gcc/d/dmd/module.h
+++ b/gcc/d/dmd/module.h
@@ -10,15 +10,16 @@
#pragma once
-#include "root/root.h"
#include "dsymbol.h"
-class ClassDeclaration;
struct ModuleDeclaration;
-struct Macro;
struct Escape;
-class VarDeclaration;
-class Library;
+struct FileBuffer;
+
+struct MacroTable
+{
+ void* internal; // PIMPL
+};
enum PKG
{
@@ -34,10 +35,9 @@ public:
unsigned tag; // auto incremented tag, used to mask package tree in scopes
Module *mod; // != NULL if isPkgMod == PKGmodule
- Package(Identifier *ident);
const char *kind() const;
- static DsymbolTable *resolve(Identifiers *packages, Dsymbol **pparent, Package **ppkg);
+ bool equals(const RootObject *o) const;
Package *isPackage() { return this; }
@@ -47,7 +47,6 @@ public:
void accept(Visitor *v) { v->visit(this); }
Module *isPackageMod();
- void resolvePKGunknown();
};
class Module : public Package
@@ -60,26 +59,31 @@ public:
static Dsymbols deferred2; // deferred Dsymbol's needing semantic2() run on them
static Dsymbols deferred3; // deferred Dsymbol's needing semantic3() run on them
static unsigned dprogress; // progress resolving the deferred list
+
static void _init();
static AggregateDeclaration *moduleinfo;
- const char *arg; // original argument name
+ DString arg; // original argument name
ModuleDeclaration *md; // if !NULL, the contents of the ModuleDeclaration declaration
- File *srcfile; // input source file
- File *objfile; // output .obj file
- File *hdrfile; // 'header' file
- File *docfile; // output documentation file
+ FileName srcfile; // input source file
+ FileName objfile; // output .obj file
+ FileName hdrfile; // 'header' file
+ FileName docfile; // output documentation file
+ FileBuffer *srcBuffer; // set during load(), free'd in parse()
unsigned errors; // if any errors in file
unsigned numlines; // number of lines in source file
- int isDocFile; // if it is a documentation input file, not D source
+ bool isHdrFile; // if it is a header (.di) file
+ bool isCFile; // if it is a C (.c) file
+ bool isDocFile; // if it is a documentation input file, not D source
+ bool hasAlwaysInlines; // contains references to functions that must be inlined
bool isPackageFile; // if it is a package.d
Package *pkg; // if isPackageFile is true, the Package that contains this package.d
Strings contentImportedFiles; // array of files whose content was imported
int needmoduleinfo;
-
int selfimports; // 0: don't know, 1: does not, 2: does
+ void* tagSymTab; // ImportC: tag symbols that conflict with other symbols used as the index
bool selfImports(); // returns true if module imports itself
int rootimports; // 0: don't know, 1: does not, 2: does
@@ -101,41 +105,34 @@ public:
unsigned debuglevel; // debug level
Identifiers *debugids; // debug identifiers
- Identifiers *debugidsNot; // forward referenced debug identifiers
+ Identifiers *debugidsNot; // forward referenced debug identifiers
unsigned versionlevel; // version level
Identifiers *versionids; // version identifiers
- Identifiers *versionidsNot; // forward referenced version identifiers
+ Identifiers *versionidsNot; // forward referenced version identifiers
- Macro *macrotable; // document comment macros
+ MacroTable macrotable; // document comment macros
Escape *escapetable; // document comment escapes
size_t nameoffset; // offset of module name from start of ModuleInfo
size_t namelen; // length of module name in characters
- Module(const char *arg, Identifier *ident, int doDocComment, int doHdrGen);
static Module* create(const char *arg, Identifier *ident, int doDocComment, int doHdrGen);
static Module *load(Loc loc, Identifiers *packages, Identifier *ident);
const char *kind() const;
- File *setOutfile(const char *name, const char *dir, const char *arg, const char *ext);
- void setDocfile();
- bool read(Loc loc); // read file, returns 'true' if succeed, 'false' otherwise.
+ bool read(const Loc &loc); // read file, returns 'true' if succeed, 'false' otherwise.
Module *parse(); // syntactic parse
void importAll(Scope *sc);
int needModuleInfo();
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
- bool isPackageAccessible(Package *p, Prot protection, int flags = 0);
+ bool isPackageAccessible(Package *p, Visibility visibility, int flags = 0);
Dsymbol *symtabInsert(Dsymbol *s);
void deleteObjFile();
- static void addDeferredSemantic(Dsymbol *s);
- static void addDeferredSemantic2(Dsymbol *s);
- static void addDeferredSemantic3(Dsymbol *s);
static void runDeferredSemantic();
static void runDeferredSemantic2();
static void runDeferredSemantic3();
- static void clearCache();
int imports(Module *m);
bool isRoot() { return this->importedFrom == this; }
@@ -158,6 +155,8 @@ public:
Symbol *sfilename; // symbol for filename
+ void *ctfe_cov; // stores coverage information from ctfe
+
Module *isModule() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -167,11 +166,9 @@ struct ModuleDeclaration
{
Loc loc;
Identifier *id;
- Identifiers *packages; // array of Identifier's representing packages
+ DArray<Identifier*> packages; // array of Identifier's representing packages
bool isdeprecated; // if it is a deprecated module
Expression *msg;
- ModuleDeclaration(Loc loc, Identifiers *packages, Identifier *id);
-
- const char *toChars();
+ const char *toChars() const;
};
diff --git a/gcc/d/dmd/mtype.c b/gcc/d/dmd/mtype.c
deleted file mode 100644
index 6cccf40df98..00000000000
--- a/gcc/d/dmd/mtype.c
+++ /dev/null
@@ -1,8722 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/mtype.c
- */
-
-#include "root/dsystem.h"
-#include "root/checkedint.h"
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "mangle.h"
-#include "dsymbol.h"
-#include "mtype.h"
-#include "scope.h"
-#include "init.h"
-#include "expression.h"
-#include "statement.h"
-#include "attrib.h"
-#include "declaration.h"
-#include "template.h"
-#include "id.h"
-#include "enum.h"
-#include "module.h"
-#include "import.h"
-#include "aggregate.h"
-#include "hdrgen.h"
-#include "target.h"
-
-bool symbolIsVisible(Scope *sc, Dsymbol *s);
-typedef int (*ForeachDg)(void *ctx, size_t paramidx, Parameter *param);
-int Parameter_foreach(Parameters *parameters, ForeachDg dg, void *ctx, size_t *pn = NULL);
-FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL);
-Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expression *e, bool alwaysCopy = false);
-Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
-Expression *typeToExpression(Type *t);
-Expression *typeToExpressionHelper(TypeQualified *t, Expression *e, size_t i = 0);
-RootObject *compileTypeMixin(TypeMixin *tm, Loc loc, Scope *sc);
-
-/***************************** Type *****************************/
-
-ClassDeclaration *Type::dtypeinfo;
-ClassDeclaration *Type::typeinfoclass;
-ClassDeclaration *Type::typeinfointerface;
-ClassDeclaration *Type::typeinfostruct;
-ClassDeclaration *Type::typeinfopointer;
-ClassDeclaration *Type::typeinfoarray;
-ClassDeclaration *Type::typeinfostaticarray;
-ClassDeclaration *Type::typeinfoassociativearray;
-ClassDeclaration *Type::typeinfovector;
-ClassDeclaration *Type::typeinfoenum;
-ClassDeclaration *Type::typeinfofunction;
-ClassDeclaration *Type::typeinfodelegate;
-ClassDeclaration *Type::typeinfotypelist;
-ClassDeclaration *Type::typeinfoconst;
-ClassDeclaration *Type::typeinfoinvariant;
-ClassDeclaration *Type::typeinfoshared;
-ClassDeclaration *Type::typeinfowild;
-
-TemplateDeclaration *Type::rtinfo;
-
-Type *Type::tvoid;
-Type *Type::tint8;
-Type *Type::tuns8;
-Type *Type::tint16;
-Type *Type::tuns16;
-Type *Type::tint32;
-Type *Type::tuns32;
-Type *Type::tint64;
-Type *Type::tuns64;
-Type *Type::tint128;
-Type *Type::tuns128;
-Type *Type::tfloat32;
-Type *Type::tfloat64;
-Type *Type::tfloat80;
-
-Type *Type::timaginary32;
-Type *Type::timaginary64;
-Type *Type::timaginary80;
-
-Type *Type::tcomplex32;
-Type *Type::tcomplex64;
-Type *Type::tcomplex80;
-
-Type *Type::tbool;
-Type *Type::tchar;
-Type *Type::twchar;
-Type *Type::tdchar;
-
-Type *Type::tshiftcnt;
-Type *Type::terror;
-Type *Type::tnull;
-Type *Type::tnoreturn;
-
-Type *Type::tsize_t;
-Type *Type::tptrdiff_t;
-Type *Type::thash_t;
-
-Type *Type::tvoidptr;
-Type *Type::tstring;
-Type *Type::twstring;
-Type *Type::tdstring;
-Type *Type::basic[TMAX];
-unsigned char Type::sizeTy[TMAX];
-StringTable Type::stringtable;
-
-void initTypeMangle();
-
-Type::Type(TY ty)
-{
- this->ty = ty;
- this->mod = 0;
- this->deco = NULL;
- this->cto = NULL;
- this->ito = NULL;
- this->sto = NULL;
- this->scto = NULL;
- this->wto = NULL;
- this->wcto = NULL;
- this->swto = NULL;
- this->swcto = NULL;
- this->pto = NULL;
- this->rto = NULL;
- this->arrayof = NULL;
- this->vtinfo = NULL;
- this->ctype = NULL;
-}
-
-const char *Type::kind()
-{
- assert(false); // should be overridden
- return NULL;
-}
-
-Type *Type::copy()
-{
- void *pt = mem.xmalloc(sizeTy[ty]);
- Type *t = (Type *)memcpy(pt, (void *)this, sizeTy[ty]);
- return t;
-}
-
-Type *Type::syntaxCopy()
-{
- print();
- fprintf(stderr, "ty = %d\n", ty);
- assert(0);
- return this;
-}
-
-bool Type::equals(RootObject *o)
-{
- Type *t = (Type *)o;
- //printf("Type::equals(%s, %s)\n", toChars(), t->toChars());
- // deco strings are unique
- // and semantic() has been run
- if (this == o || ((t && deco == t->deco) && deco != NULL))
- {
- //printf("deco = '%s', t->deco = '%s'\n", deco, t->deco);
- return true;
- }
- //if (deco && t && t->deco) printf("deco = '%s', t->deco = '%s'\n", deco, t->deco);
- return false;
-}
-
-bool Type::equivalent(Type *t)
-{
- return immutableOf()->equals(t->immutableOf());
-}
-
-void Type::_init()
-{
- stringtable._init(14000);
-
- for (size_t i = 0; i < TMAX; i++)
- sizeTy[i] = sizeof(TypeBasic);
- sizeTy[Tsarray] = sizeof(TypeSArray);
- sizeTy[Tarray] = sizeof(TypeDArray);
- sizeTy[Taarray] = sizeof(TypeAArray);
- sizeTy[Tpointer] = sizeof(TypePointer);
- sizeTy[Treference] = sizeof(TypeReference);
- sizeTy[Tfunction] = sizeof(TypeFunction);
- sizeTy[Tdelegate] = sizeof(TypeDelegate);
- sizeTy[Tident] = sizeof(TypeIdentifier);
- sizeTy[Tinstance] = sizeof(TypeInstance);
- sizeTy[Ttypeof] = sizeof(TypeTypeof);
- sizeTy[Tenum] = sizeof(TypeEnum);
- sizeTy[Tstruct] = sizeof(TypeStruct);
- sizeTy[Tclass] = sizeof(TypeClass);
- sizeTy[Ttuple] = sizeof(TypeTuple);
- sizeTy[Tslice] = sizeof(TypeSlice);
- sizeTy[Treturn] = sizeof(TypeReturn);
- sizeTy[Terror] = sizeof(TypeError);
- sizeTy[Tnull] = sizeof(TypeNull);
- sizeTy[Tvector] = sizeof(TypeVector);
- sizeTy[Ttraits] = sizeof(TypeTraits);
- sizeTy[Tmixin] = sizeof(TypeMixin);
- sizeTy[Tnoreturn] = sizeof(TypeNoreturn);
-
- initTypeMangle();
-
- // Set basic types
- static TY basetab[] =
- { Tvoid, Tint8, Tuns8, Tint16, Tuns16, Tint32, Tuns32, Tint64, Tuns64,
- Tint128, Tuns128,
- Tfloat32, Tfloat64, Tfloat80,
- Timaginary32, Timaginary64, Timaginary80,
- Tcomplex32, Tcomplex64, Tcomplex80,
- Tbool,
- Tchar, Twchar, Tdchar, Terror };
-
- for (size_t i = 0; basetab[i] != Terror; i++)
- {
- Type *t = new TypeBasic(basetab[i]);
- t = t->merge();
- basic[basetab[i]] = t;
- }
- basic[Terror] = new TypeError();
-
- tnoreturn = new TypeNoreturn();
- tnoreturn->deco = tnoreturn->merge()->deco;
- basic[Tnoreturn] = tnoreturn;
-
- tvoid = basic[Tvoid];
- tint8 = basic[Tint8];
- tuns8 = basic[Tuns8];
- tint16 = basic[Tint16];
- tuns16 = basic[Tuns16];
- tint32 = basic[Tint32];
- tuns32 = basic[Tuns32];
- tint64 = basic[Tint64];
- tuns64 = basic[Tuns64];
- tint128 = basic[Tint128];
- tuns128 = basic[Tuns128];
- tfloat32 = basic[Tfloat32];
- tfloat64 = basic[Tfloat64];
- tfloat80 = basic[Tfloat80];
-
- timaginary32 = basic[Timaginary32];
- timaginary64 = basic[Timaginary64];
- timaginary80 = basic[Timaginary80];
-
- tcomplex32 = basic[Tcomplex32];
- tcomplex64 = basic[Tcomplex64];
- tcomplex80 = basic[Tcomplex80];
-
- tbool = basic[Tbool];
- tchar = basic[Tchar];
- twchar = basic[Twchar];
- tdchar = basic[Tdchar];
-
- tshiftcnt = tint32;
- terror = basic[Terror];
- tnoreturn = basic[Tnoreturn];
- tnull = new TypeNull();
- tnull->deco = tnull->merge()->deco;
-
- tvoidptr = tvoid->pointerTo();
- tstring = tchar->immutableOf()->arrayOf();
- twstring = twchar->immutableOf()->arrayOf();
- tdstring = tdchar->immutableOf()->arrayOf();
-
- const bool isLP64 = global.params.isLP64;
-
- tsize_t = basic[isLP64 ? Tuns64 : Tuns32];
- tptrdiff_t = basic[isLP64 ? Tint64 : Tint32];
- thash_t = tsize_t;
-}
-
-d_uns64 Type::size()
-{
- return size(Loc());
-}
-
-d_uns64 Type::size(Loc loc)
-{
- error(loc, "no size for type %s", toChars());
- return SIZE_INVALID;
-}
-
-unsigned Type::alignsize()
-{
- return (unsigned)size(Loc());
-}
-
-Type *Type::trySemantic(Loc loc, Scope *sc)
-{
- //printf("+trySemantic(%s) %d\n", toChars(), global.errors);
- unsigned errors = global.startGagging();
- Type *t = typeSemantic(this, loc, sc);
- if (global.endGagging(errors) || t->ty == Terror) // if any errors happened
- {
- t = NULL;
- }
- //printf("-trySemantic(%s) %d\n", toChars(), global.errors);
- return t;
-}
-
-/********************************
- * Return a copy of this type with all attributes null-initialized.
- * Useful for creating a type with different modifiers.
- */
-
-Type *Type::nullAttributes()
-{
- unsigned sz = sizeTy[ty];
- void *pt = mem.xmalloc(sz);
- Type *t = (Type *)memcpy(pt, (void *)this, sz);
- t->deco = NULL;
- t->arrayof = NULL;
- t->pto = NULL;
- t->rto = NULL;
- t->cto = NULL;
- t->ito = NULL;
- t->sto = NULL;
- t->scto = NULL;
- t->wto = NULL;
- t->wcto = NULL;
- t->swto = NULL;
- t->swcto = NULL;
- t->vtinfo = NULL;
- t->ctype = NULL;
- if (t->ty == Tstruct) ((TypeStruct *)t)->att = RECfwdref;
- if (t->ty == Tclass) ((TypeClass *)t)->att = RECfwdref;
- return t;
-}
-
-/********************************
- * Convert to 'const'.
- */
-
-Type *Type::constOf()
-{
- //printf("Type::constOf() %p %s\n", this, toChars());
- if (mod == MODconst)
- return this;
- if (cto)
- {
- assert(cto->mod == MODconst);
- return cto;
- }
- Type *t = makeConst();
- t = t->merge();
- t->fixTo(this);
- //printf("-Type::constOf() %p %s\n", t, t->toChars());
- return t;
-}
-
-/********************************
- * Convert to 'immutable'.
- */
-
-Type *Type::immutableOf()
-{
- //printf("Type::immutableOf() %p %s\n", this, toChars());
- if (isImmutable())
- return this;
- if (ito)
- {
- assert(ito->isImmutable());
- return ito;
- }
- Type *t = makeImmutable();
- t = t->merge();
- t->fixTo(this);
- //printf("\t%p\n", t);
- return t;
-}
-
-/********************************
- * Make type mutable.
- */
-
-Type *Type::mutableOf()
-{
- //printf("Type::mutableOf() %p, %s\n", this, toChars());
- Type *t = this;
- if (isImmutable())
- {
- t = ito; // immutable => naked
- assert(!t || (t->isMutable() && !t->isShared()));
- }
- else if (isConst())
- {
- if (isShared())
- {
- if (isWild())
- t = swcto; // shared wild const -> shared
- else
- t = sto; // shared const => shared
- }
- else
- {
- if (isWild())
- t = wcto; // wild const -> naked
- else
- t = cto; // const => naked
- }
- assert(!t || t->isMutable());
- }
- else if (isWild())
- {
- if (isShared())
- t = sto; // shared wild => shared
- else
- t = wto; // wild => naked
- assert(!t || t->isMutable());
- }
- if (!t)
- {
- t = makeMutable();
- t = t->merge();
- t->fixTo(this);
- }
- else
- t = t->merge();
- assert(t->isMutable());
- return t;
-}
-
-Type *Type::sharedOf()
-{
- //printf("Type::sharedOf() %p, %s\n", this, toChars());
- if (mod == MODshared)
- return this;
- if (sto)
- {
- assert(sto->mod == MODshared);
- return sto;
- }
- Type *t = makeShared();
- t = t->merge();
- t->fixTo(this);
- //printf("\t%p\n", t);
- return t;
-}
-
-Type *Type::sharedConstOf()
-{
- //printf("Type::sharedConstOf() %p, %s\n", this, toChars());
- if (mod == (MODshared | MODconst))
- return this;
- if (scto)
- {
- assert(scto->mod == (MODshared | MODconst));
- return scto;
- }
- Type *t = makeSharedConst();
- t = t->merge();
- t->fixTo(this);
- //printf("\t%p\n", t);
- return t;
-}
-
-
-/********************************
- * Make type unshared.
- * 0 => 0
- * const => const
- * immutable => immutable
- * shared => 0
- * shared const => const
- * wild => wild
- * wild const => wild const
- * shared wild => wild
- * shared wild const => wild const
- */
-
-Type *Type::unSharedOf()
-{
- //printf("Type::unSharedOf() %p, %s\n", this, toChars());
- Type *t = this;
-
- if (isShared())
- {
- if (isWild())
- {
- if (isConst())
- t = wcto; // shared wild const => wild const
- else
- t = wto; // shared wild => wild
- }
- else
- {
- if (isConst())
- t = cto; // shared const => const
- else
- t = sto; // shared => naked
- }
- assert(!t || !t->isShared());
- }
-
- if (!t)
- {
- t = this->nullAttributes();
- t->mod = mod & ~MODshared;
- t->ctype = ctype;
- t = t->merge();
-
- t->fixTo(this);
- }
- else
- t = t->merge();
- assert(!t->isShared());
- return t;
-}
-
-/********************************
- * Convert to 'wild'.
- */
-
-Type *Type::wildOf()
-{
- //printf("Type::wildOf() %p %s\n", this, toChars());
- if (mod == MODwild)
- return this;
- if (wto)
- {
- assert(wto->mod == MODwild);
- return wto;
- }
- Type *t = makeWild();
- t = t->merge();
- t->fixTo(this);
- //printf("\t%p %s\n", t, t->toChars());
- return t;
-}
-
-Type *Type::wildConstOf()
-{
- //printf("Type::wildConstOf() %p %s\n", this, toChars());
- if (mod == MODwildconst)
- return this;
- if (wcto)
- {
- assert(wcto->mod == MODwildconst);
- return wcto;
- }
- Type *t = makeWildConst();
- t = t->merge();
- t->fixTo(this);
- //printf("\t%p %s\n", t, t->toChars());
- return t;
-}
-
-Type *Type::sharedWildOf()
-{
- //printf("Type::sharedWildOf() %p, %s\n", this, toChars());
- if (mod == (MODshared | MODwild))
- return this;
- if (swto)
- {
- assert(swto->mod == (MODshared | MODwild));
- return swto;
- }
- Type *t = makeSharedWild();
- t = t->merge();
- t->fixTo(this);
- //printf("\t%p %s\n", t, t->toChars());
- return t;
-}
-
-Type *Type::sharedWildConstOf()
-{
- //printf("Type::sharedWildConstOf() %p, %s\n", this, toChars());
- if (mod == (MODshared | MODwildconst))
- return this;
- if (swcto)
- {
- assert(swcto->mod == (MODshared | MODwildconst));
- return swcto;
- }
- Type *t = makeSharedWildConst();
- t = t->merge();
- t->fixTo(this);
- //printf("\t%p %s\n", t, t->toChars());
- return t;
-}
-
-/**********************************
- * For our new type 'this', which is type-constructed from t,
- * fill in the cto, ito, sto, scto, wto shortcuts.
- */
-
-void Type::fixTo(Type *t)
-{
- // If fixing this: immutable(T*) by t: immutable(T)*,
- // cache t to this->xto won't break transitivity.
- Type *mto = NULL;
- Type *tn = nextOf();
- if (!tn || (ty != Tsarray && tn->mod == t->nextOf()->mod))
- {
- switch (t->mod)
- {
- case 0: mto = t; break;
- case MODconst: cto = t; break;
- case MODwild: wto = t; break;
- case MODwildconst: wcto = t; break;
- case MODshared: sto = t; break;
- case MODshared | MODconst: scto = t; break;
- case MODshared | MODwild: swto = t; break;
- case MODshared | MODwildconst: swcto = t; break;
- case MODimmutable: ito = t; break;
- }
- }
-
- assert(mod != t->mod);
-#define X(m, n) (((m) << 4) | (n))
- switch (mod)
- {
- case 0:
- break;
-
- case MODconst:
- cto = mto;
- t->cto = this;
- break;
-
- case MODwild:
- wto = mto;
- t->wto = this;
- break;
-
- case MODwildconst:
- wcto = mto;
- t->wcto = this;
- break;
-
- case MODshared:
- sto = mto;
- t->sto = this;
- break;
-
- case MODshared | MODconst:
- scto = mto;
- t->scto = this;
- break;
-
- case MODshared | MODwild:
- swto = mto;
- t->swto = this;
- break;
-
- case MODshared | MODwildconst:
- swcto = mto;
- t->swcto = this;
- break;
-
- case MODimmutable:
- t->ito = this;
- if (t-> cto) t-> cto->ito = this;
- if (t-> sto) t-> sto->ito = this;
- if (t-> scto) t-> scto->ito = this;
- if (t-> wto) t-> wto->ito = this;
- if (t-> wcto) t-> wcto->ito = this;
- if (t-> swto) t-> swto->ito = this;
- if (t->swcto) t->swcto->ito = this;
- break;
-
- default:
- assert(0);
- }
-#undef X
-
- check();
- t->check();
- //printf("fixTo: %s, %s\n", toChars(), t->toChars());
-}
-
-/***************************
- * Look for bugs in constructing types.
- */
-
-void Type::check()
-{
- switch (mod)
- {
- case 0:
- if (cto) assert(cto->mod == MODconst);
- if (ito) assert(ito->mod == MODimmutable);
- if (sto) assert(sto->mod == MODshared);
- if (scto) assert(scto->mod == (MODshared | MODconst));
- if (wto) assert(wto->mod == MODwild);
- if (wcto) assert(wcto->mod == MODwildconst);
- if (swto) assert(swto->mod == (MODshared | MODwild));
- if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
- break;
-
- case MODconst:
- if (cto) assert(cto->mod == 0);
- if (ito) assert(ito->mod == MODimmutable);
- if (sto) assert(sto->mod == MODshared);
- if (scto) assert(scto->mod == (MODshared | MODconst));
- if (wto) assert(wto->mod == MODwild);
- if (wcto) assert(wcto->mod == MODwildconst);
- if (swto) assert(swto->mod == (MODshared | MODwild));
- if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
- break;
-
- case MODwild:
- if (cto) assert(cto->mod == MODconst);
- if (ito) assert(ito->mod == MODimmutable);
- if (sto) assert(sto->mod == MODshared);
- if (scto) assert(scto->mod == (MODshared | MODconst));
- if (wto) assert(wto->mod == 0);
- if (wcto) assert(wcto->mod == MODwildconst);
- if (swto) assert(swto->mod == (MODshared | MODwild));
- if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
- break;
-
- case MODwildconst:
- assert(! cto || cto->mod == MODconst);
- assert(! ito || ito->mod == MODimmutable);
- assert(! sto || sto->mod == MODshared);
- assert(! scto || scto->mod == (MODshared | MODconst));
- assert(! wto || wto->mod == MODwild);
- assert(! wcto || wcto->mod == 0);
- assert(! swto || swto->mod == (MODshared | MODwild));
- assert(!swcto || swcto->mod == (MODshared | MODwildconst));
- break;
-
- case MODshared:
- if (cto) assert(cto->mod == MODconst);
- if (ito) assert(ito->mod == MODimmutable);
- if (sto) assert(sto->mod == 0);
- if (scto) assert(scto->mod == (MODshared | MODconst));
- if (wto) assert(wto->mod == MODwild);
- if (wcto) assert(wcto->mod == MODwildconst);
- if (swto) assert(swto->mod == (MODshared | MODwild));
- if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
- break;
-
- case MODshared | MODconst:
- if (cto) assert(cto->mod == MODconst);
- if (ito) assert(ito->mod == MODimmutable);
- if (sto) assert(sto->mod == MODshared);
- if (scto) assert(scto->mod == 0);
- if (wto) assert(wto->mod == MODwild);
- if (wcto) assert(wcto->mod == MODwildconst);
- if (swto) assert(swto->mod == (MODshared | MODwild));
- if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
- break;
-
- case MODshared | MODwild:
- if (cto) assert(cto->mod == MODconst);
- if (ito) assert(ito->mod == MODimmutable);
- if (sto) assert(sto->mod == MODshared);
- if (scto) assert(scto->mod == (MODshared | MODconst));
- if (wto) assert(wto->mod == MODwild);
- if (wcto) assert(wcto->mod == MODwildconst);
- if (swto) assert(swto->mod == 0);
- if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
- break;
-
- case MODshared | MODwildconst:
- assert(! cto || cto->mod == MODconst);
- assert(! ito || ito->mod == MODimmutable);
- assert(! sto || sto->mod == MODshared);
- assert(! scto || scto->mod == (MODshared | MODconst));
- assert(! wto || wto->mod == MODwild);
- assert(! wcto || wcto->mod == MODwildconst);
- assert(! swto || swto->mod == (MODshared | MODwild));
- assert(!swcto || swcto->mod == 0);
- break;
-
- case MODimmutable:
- if (cto) assert(cto->mod == MODconst);
- if (ito) assert(ito->mod == 0);
- if (sto) assert(sto->mod == MODshared);
- if (scto) assert(scto->mod == (MODshared | MODconst));
- if (wto) assert(wto->mod == MODwild);
- if (wcto) assert(wcto->mod == MODwildconst);
- if (swto) assert(swto->mod == (MODshared | MODwild));
- if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
- break;
-
- default:
- assert(0);
- }
-
- Type *tn = nextOf();
- if (tn && ty != Tfunction && tn->ty != Tfunction && ty != Tenum)
- {
- // Verify transitivity
- switch (mod)
- {
- case 0:
- case MODconst:
- case MODwild:
- case MODwildconst:
- case MODshared:
- case MODshared | MODconst:
- case MODshared | MODwild:
- case MODshared | MODwildconst:
- case MODimmutable:
- assert(tn->mod == MODimmutable || (tn->mod & mod) == mod);
- break;
-
- default:
- assert(0);
- }
- tn->check();
- }
-}
-
-Type *Type::makeConst()
-{
- //printf("Type::makeConst() %p, %s\n", this, toChars());
- if (cto) return cto;
- Type *t = this->nullAttributes();
- t->mod = MODconst;
- //printf("-Type::makeConst() %p, %s\n", t, toChars());
- return t;
-}
-
-Type *Type::makeImmutable()
-{
- if (ito) return ito;
- Type *t = this->nullAttributes();
- t->mod = MODimmutable;
- return t;
-}
-
-Type *Type::makeShared()
-{
- if (sto) return sto;
- Type *t = this->nullAttributes();
- t->mod = MODshared;
- return t;
-}
-
-Type *Type::makeSharedConst()
-{
- if (scto) return scto;
- Type *t = this->nullAttributes();
- t->mod = MODshared | MODconst;
- return t;
-}
-
-Type *Type::makeWild()
-{
- if (wto) return wto;
- Type *t = this->nullAttributes();
- t->mod = MODwild;
- return t;
-}
-
-Type *Type::makeWildConst()
-{
- if (wcto) return wcto;
- Type *t = this->nullAttributes();
- t->mod = MODwildconst;
- return t;
-}
-
-Type *Type::makeSharedWild()
-{
- if (swto) return swto;
- Type *t = this->nullAttributes();
- t->mod = MODshared | MODwild;
- return t;
-}
-
-Type *Type::makeSharedWildConst()
-{
- if (swcto) return swcto;
- Type *t = this->nullAttributes();
- t->mod = MODshared | MODwildconst;
- return t;
-}
-
-Type *Type::makeMutable()
-{
- Type *t = this->nullAttributes();
- t->mod = mod & MODshared;
- return t;
-}
-
-/*************************************
- * Apply STCxxxx bits to existing type.
- * Use *before* semantic analysis is run.
- */
-
-Type *Type::addSTC(StorageClass stc)
-{
- Type *t = this;
- if (t->isImmutable())
- ;
- else if (stc & STCimmutable)
- {
- t = t->makeImmutable();
- }
- else
- {
- if ((stc & STCshared) && !t->isShared())
- {
- if (t->isWild())
- {
- if (t->isConst())
- t = t->makeSharedWildConst();
- else
- t = t->makeSharedWild();
- }
- else
- {
- if (t->isConst())
- t = t->makeSharedConst();
- else
- t = t->makeShared();
- }
- }
- if ((stc & STCconst) && !t->isConst())
- {
- if (t->isShared())
- {
- if (t->isWild())
- t = t->makeSharedWildConst();
- else
- t = t->makeSharedConst();
- }
- else
- {
- if (t->isWild())
- t = t->makeWildConst();
- else
- t = t->makeConst();
- }
- }
- if ((stc & STCwild) && !t->isWild())
- {
- if (t->isShared())
- {
- if (t->isConst())
- t = t->makeSharedWildConst();
- else
- t = t->makeSharedWild();
- }
- else
- {
- if (t->isConst())
- t = t->makeWildConst();
- else
- t = t->makeWild();
- }
- }
- }
- return t;
-}
-
-/************************************
- * Convert MODxxxx to STCxxx
- */
-
-StorageClass ModToStc(unsigned mod)
-{
- StorageClass stc = 0;
- if (mod & MODimmutable) stc |= STCimmutable;
- if (mod & MODconst) stc |= STCconst;
- if (mod & MODwild) stc |= STCwild;
- if (mod & MODshared) stc |= STCshared;
- return stc;
-}
-
-/************************************
- * Apply MODxxxx bits to existing type.
- */
-
-Type *Type::castMod(MOD mod)
-{ Type *t;
-
- switch (mod)
- {
- case 0:
- t = unSharedOf()->mutableOf();
- break;
-
- case MODconst:
- t = unSharedOf()->constOf();
- break;
-
- case MODwild:
- t = unSharedOf()->wildOf();
- break;
-
- case MODwildconst:
- t = unSharedOf()->wildConstOf();
- break;
-
- case MODshared:
- t = mutableOf()->sharedOf();
- break;
-
- case MODshared | MODconst:
- t = sharedConstOf();
- break;
-
- case MODshared | MODwild:
- t = sharedWildOf();
- break;
-
- case MODshared | MODwildconst:
- t = sharedWildConstOf();
- break;
-
- case MODimmutable:
- t = immutableOf();
- break;
-
- default:
- assert(0);
- }
- return t;
-}
-
-/************************************
- * Add MODxxxx bits to existing type.
- * We're adding, not replacing, so adding const to
- * a shared type => "shared const"
- */
-
-Type *Type::addMod(MOD mod)
-{
- /* Add anything to immutable, and it remains immutable
- */
- Type *t = this;
- if (!t->isImmutable())
- {
- //printf("addMod(%x) %s\n", mod, toChars());
- switch (mod)
- {
- case 0:
- break;
-
- case MODconst:
- if (isShared())
- {
- if (isWild())
- t = sharedWildConstOf();
- else
- t = sharedConstOf();
- }
- else
- {
- if (isWild())
- t = wildConstOf();
- else
- t = constOf();
- }
- break;
-
- case MODwild:
- if (isShared())
- {
- if (isConst())
- t = sharedWildConstOf();
- else
- t = sharedWildOf();
- }
- else
- {
- if (isConst())
- t = wildConstOf();
- else
- t = wildOf();
- }
- break;
-
- case MODwildconst:
- if (isShared())
- t = sharedWildConstOf();
- else
- t = wildConstOf();
- break;
-
- case MODshared:
- if (isWild())
- {
- if (isConst())
- t = sharedWildConstOf();
- else
- t = sharedWildOf();
- }
- else
- {
- if (isConst())
- t = sharedConstOf();
- else
- t = sharedOf();
- }
- break;
-
- case MODshared | MODconst:
- if (isWild())
- t = sharedWildConstOf();
- else
- t = sharedConstOf();
- break;
-
- case MODshared | MODwild:
- if (isConst())
- t = sharedWildConstOf();
- else
- t = sharedWildOf();
- break;
-
- case MODshared | MODwildconst:
- t = sharedWildConstOf();
- break;
-
- case MODimmutable:
- t = immutableOf();
- break;
-
- default:
- assert(0);
- }
- }
- return t;
-}
-
-/************************************
- * Add storage class modifiers to type.
- */
-
-Type *Type::addStorageClass(StorageClass stc)
-{
- /* Just translate to MOD bits and let addMod() do the work
- */
- MOD mod = 0;
-
- if (stc & STCimmutable)
- mod = MODimmutable;
- else
- {
- if (stc & (STCconst | STCin))
- mod |= MODconst;
- if (stc & STCwild)
- mod |= MODwild;
- if (stc & STCshared)
- mod |= MODshared;
- }
- return addMod(mod);
-}
-
-Type *Type::pointerTo()
-{
- if (ty == Terror)
- return this;
- if (!pto)
- {
- Type *t = new TypePointer(this);
- if (ty == Tfunction)
- {
- t->deco = t->merge()->deco;
- pto = t;
- }
- else
- pto = t->merge();
- }
- return pto;
-}
-
-Type *Type::referenceTo()
-{
- if (ty == Terror)
- return this;
- if (!rto)
- {
- Type *t = new TypeReference(this);
- rto = t->merge();
- }
- return rto;
-}
-
-Type *Type::arrayOf()
-{
- if (ty == Terror)
- return this;
- if (!arrayof)
- {
- Type *t = new TypeDArray(this);
- arrayof = t->merge();
- }
- return arrayof;
-}
-
-// Make corresponding static array type without semantic
-Type *Type::sarrayOf(dinteger_t dim)
-{
- assert(deco);
- Type *t = new TypeSArray(this, new IntegerExp(Loc(), dim, Type::tsize_t));
-
- // according to TypeSArray::semantic()
- t = t->addMod(mod);
- t = t->merge();
-
- return t;
-}
-
-Type *Type::aliasthisOf()
-{
- AggregateDeclaration *ad = isAggregate(this);
- if (ad && ad->aliasthis)
- {
- Dsymbol *s = ad->aliasthis;
- if (s->isAliasDeclaration())
- s = s->toAlias();
- Declaration *d = s->isDeclaration();
- if (d && !d->isTupleDeclaration())
- {
- assert(d->type);
- Type *t = d->type;
- if (d->isVarDeclaration() && d->needThis())
- {
- t = t->addMod(this->mod);
- }
- else if (d->isFuncDeclaration())
- {
- FuncDeclaration *fd = resolveFuncCall(Loc(), NULL, d, NULL, this, NULL, 1);
- if (fd && fd->errors)
- return Type::terror;
- if (fd && !fd->type->nextOf() && !fd->functionSemantic())
- fd = NULL;
- if (fd)
- {
- t = fd->type->nextOf();
- if (!t) // issue 14185
- return Type::terror;
- t = t->substWildTo(mod == 0 ? MODmutable : (MODFlags)mod);
- }
- else
- return Type::terror;
- }
- return t;
- }
- EnumDeclaration *ed = s->isEnumDeclaration();
- if (ed)
- {
- Type *t = ed->type;
- return t;
- }
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (td)
- {
- assert(td->_scope);
- FuncDeclaration *fd = resolveFuncCall(Loc(), NULL, td, NULL, this, NULL, 1);
- if (fd && fd->errors)
- return Type::terror;
- if (fd && fd->functionSemantic())
- {
- Type *t = fd->type->nextOf();
- t = t->substWildTo(mod == 0 ? MODmutable : (MODFlags)mod);
- return t;
- }
- else
- return Type::terror;
- }
- //printf("%s\n", s->kind());
- }
- return NULL;
-}
-
-bool Type::checkAliasThisRec()
-{
- Type *tb = toBasetype();
- AliasThisRec* pflag;
- if (tb->ty == Tstruct)
- pflag = &((TypeStruct *)tb)->att;
- else if (tb->ty == Tclass)
- pflag = &((TypeClass *)tb)->att;
- else
- return false;
-
- AliasThisRec flag = (AliasThisRec)(*pflag & RECtypeMask);
- if (flag == RECfwdref)
- {
- Type *att = aliasthisOf();
- flag = att && att->implicitConvTo(this) ? RECyes : RECno;
- }
- *pflag = (AliasThisRec)(flag | (*pflag & ~RECtypeMask));
- return flag == RECyes;
-}
-
-Dsymbol *Type::toDsymbol(Scope *)
-{
- return NULL;
-}
-
-/*******************************
- * If this is a shell around another type,
- * get that other type.
- */
-
-Type *Type::toBasetype()
-{
- return this;
-}
-
-/***************************
- * Return !=0 if modfrom can be implicitly converted to modto
- */
-bool MODimplicitConv(MOD modfrom, MOD modto)
-{
- if (modfrom == modto)
- return true;
-
- //printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto);
- #define X(m, n) (((m) << 4) | (n))
- switch (X(modfrom & ~MODshared, modto & ~MODshared))
- {
- case X(0, MODconst):
- case X(MODwild, MODconst):
- case X(MODwild, MODwildconst):
- case X(MODwildconst, MODconst):
- return (modfrom & MODshared) == (modto & MODshared);
-
- case X(MODimmutable, MODconst):
- case X(MODimmutable, MODwildconst):
- return true;
-
- default:
- return false;
- }
- #undef X
-}
-
-/***************************
- * Return MATCHexact or MATCHconst if a method of type '() modfrom' can call a method of type '() modto'.
- */
-MATCH MODmethodConv(MOD modfrom, MOD modto)
-{
- if (modfrom == modto)
- return MATCHexact;
- if (MODimplicitConv(modfrom, modto))
- return MATCHconst;
-
- #define X(m, n) (((m) << 4) | (n))
- switch (X(modfrom, modto))
- {
- case X(0, MODwild):
- case X(MODimmutable, MODwild):
- case X(MODconst, MODwild):
- case X(MODwildconst, MODwild):
- case X(MODshared, MODshared|MODwild):
- case X(MODshared|MODimmutable, MODshared|MODwild):
- case X(MODshared|MODconst, MODshared|MODwild):
- case X(MODshared|MODwildconst, MODshared|MODwild):
- return MATCHconst;
-
- default:
- return MATCHnomatch;
- }
- #undef X
-}
-
-/***************************
- * Merge mod bits to form common mod.
- */
-MOD MODmerge(MOD mod1, MOD mod2)
-{
- if (mod1 == mod2)
- return mod1;
-
- //printf("MODmerge(1 = %x, 2 = %x)\n", mod1, mod2);
- MOD result = 0;
- if ((mod1 | mod2) & MODshared)
- {
- // If either type is shared, the result will be shared
- result |= MODshared;
- mod1 &= ~MODshared;
- mod2 &= ~MODshared;
- }
- if (mod1 == 0 || mod1 == MODmutable || mod1 == MODconst ||
- mod2 == 0 || mod2 == MODmutable || mod2 == MODconst)
- {
- // If either type is mutable or const, the result will be const.
- result |= MODconst;
- }
- else
- {
- // MODimmutable vs MODwild
- // MODimmutable vs MODwildconst
- // MODwild vs MODwildconst
- assert(mod1 & MODwild || mod2 & MODwild);
- result |= MODwildconst;
- }
- return result;
-}
-
-/*********************************
- * Store modifier name into buf.
- */
-void MODtoBuffer(OutBuffer *buf, MOD mod)
-{
- switch (mod)
- {
- case 0:
- break;
-
- case MODimmutable:
- buf->writestring(Token::tochars[TOKimmutable]);
- break;
-
- case MODshared:
- buf->writestring(Token::tochars[TOKshared]);
- break;
-
- case MODshared | MODconst:
- buf->writestring(Token::tochars[TOKshared]);
- buf->writeByte(' ');
- /* fall through */
- case MODconst:
- buf->writestring(Token::tochars[TOKconst]);
- break;
-
- case MODshared | MODwild:
- buf->writestring(Token::tochars[TOKshared]);
- buf->writeByte(' ');
- /* fall through */
- case MODwild:
- buf->writestring(Token::tochars[TOKwild]);
- break;
-
- case MODshared | MODwildconst:
- buf->writestring(Token::tochars[TOKshared]);
- buf->writeByte(' ');
- /* fall through */
- case MODwildconst:
- buf->writestring(Token::tochars[TOKwild]);
- buf->writeByte(' ');
- buf->writestring(Token::tochars[TOKconst]);
- break;
-
- default:
- assert(0);
- }
-}
-
-
-/*********************************
- * Return modifier name.
- */
-char *MODtoChars(MOD mod)
-{
- OutBuffer buf;
- buf.reserve(16);
- MODtoBuffer(&buf, mod);
- return buf.extractChars();
-}
-
-/********************************
- * For pretty-printing a type.
- */
-
-const char *Type::toChars()
-{
- OutBuffer buf;
- buf.reserve(16);
- HdrGenState hgs;
- hgs.fullQual = (ty == Tclass && !mod);
-
- ::toCBuffer(this, &buf, NULL, &hgs);
- return buf.extractChars();
-}
-
-char *Type::toPrettyChars(bool QualifyTypes)
-{
- OutBuffer buf;
- buf.reserve(16);
- HdrGenState hgs;
- hgs.fullQual = QualifyTypes;
-
- ::toCBuffer(this, &buf, NULL, &hgs);
- return buf.extractChars();
-}
-
-/*********************************
- * Store this type's modifier name into buf.
- */
-void Type::modToBuffer(OutBuffer *buf)
-{
- if (mod)
- {
- buf->writeByte(' ');
- MODtoBuffer(buf, mod);
- }
-}
-
-/*********************************
- * Return this type's modifier name.
- */
-char *Type::modToChars()
-{
- OutBuffer buf;
- buf.reserve(16);
- modToBuffer(&buf);
- return buf.extractChars();
-}
-
-/** For each active modifier (MODconst, MODimmutable, etc) call fp with a
-void* for the work param and a string representation of the attribute. */
-int Type::modifiersApply(void *param, int (*fp)(void *, const char *))
-{
- static unsigned char modsArr[] = { MODconst, MODimmutable, MODwild, MODshared };
-
- for (size_t idx = 0; idx < 4; ++idx)
- {
- if (mod & modsArr[idx])
- {
- if (int res = fp(param, MODtoChars(modsArr[idx])))
- return res;
- }
- }
- return 0;
-}
-
-/************************************
- * Strip all parameter's idenfiers and their default arguments for merging types.
- * If some of parameter types or return type are function pointer, delegate, or
- * the types which contains either, then strip also from them.
- */
-
-Type *stripDefaultArgs(Type *t)
-{
- struct N
- {
- static Parameters *stripParams(Parameters *parameters)
- {
- Parameters *params = parameters;
- if (params && params->length > 0)
- {
- for (size_t i = 0; i < params->length; i++)
- {
- Parameter *p = (*params)[i];
- Type *ta = stripDefaultArgs(p->type);
- if (ta != p->type || p->defaultArg || p->ident || p->userAttribDecl)
- {
- if (params == parameters)
- {
- params = new Parameters();
- params->setDim(parameters->length);
- for (size_t j = 0; j < params->length; j++)
- (*params)[j] = (*parameters)[j];
- }
- (*params)[i] = new Parameter(p->storageClass, ta, NULL, NULL, NULL);
- }
- }
- }
- return params;
- }
- };
-
- if (t == NULL)
- return t;
-
- if (t->ty == Tfunction)
- {
- TypeFunction *tf = (TypeFunction *)t;
- Type *tret = stripDefaultArgs(tf->next);
- Parameters *params = N::stripParams(tf->parameterList.parameters);
- if (tret == tf->next && params == tf->parameterList.parameters)
- goto Lnot;
- tf = (TypeFunction *)tf->copy();
- tf->parameterList.parameters = params;
- tf->next = tret;
- //printf("strip %s\n <- %s\n", tf->toChars(), t->toChars());
- t = tf;
- }
- else if (t->ty == Ttuple)
- {
- TypeTuple *tt = (TypeTuple *)t;
- Parameters *args = N::stripParams(tt->arguments);
- if (args == tt->arguments)
- goto Lnot;
- t = t->copy();
- ((TypeTuple *)t)->arguments = args;
- }
- else if (t->ty == Tenum)
- {
- // TypeEnum::nextOf() may be != NULL, but it's not necessary here.
- goto Lnot;
- }
- else
- {
- Type *tn = t->nextOf();
- Type *n = stripDefaultArgs(tn);
- if (n == tn)
- goto Lnot;
- t = t->copy();
- ((TypeNext *)t)->next = n;
- }
- //printf("strip %s\n", t->toChars());
-Lnot:
- return t;
-}
-
-/************************************
- */
-
-Type *Type::merge()
-{
- if (ty == Terror) return this;
- if (ty == Ttypeof) return this;
- if (ty == Tident) return this;
- if (ty == Tinstance) return this;
- if (ty == Taarray && !((TypeAArray *)this)->index->merge()->deco)
- return this;
- if (ty != Tenum && nextOf() && !nextOf()->deco)
- return this;
-
- //printf("merge(%s)\n", toChars());
- Type *t = this;
- assert(t);
- if (!deco)
- {
- OutBuffer buf;
- buf.reserve(32);
-
- mangleToBuffer(this, &buf);
-
- StringValue *sv = stringtable.update((char *)buf.slice().ptr, buf.length());
- if (sv->ptrvalue)
- {
- t = (Type *) sv->ptrvalue;
- assert(t->deco);
- //printf("old value, deco = '%s' %p\n", t->deco, t->deco);
- }
- else
- {
- sv->ptrvalue = (char *)(t = stripDefaultArgs(t));
- deco = t->deco = const_cast<char *>(sv->toDchars());
- //printf("new value, deco = '%s' %p\n", t->deco, t->deco);
- }
- }
- return t;
-}
-
-/*************************************
- * This version does a merge even if the deco is already computed.
- * Necessary for types that have a deco, but are not merged.
- */
-Type *Type::merge2()
-{
- //printf("merge2(%s)\n", toChars());
- Type *t = this;
- assert(t);
- if (!t->deco)
- return t->merge();
-
- StringValue *sv = stringtable.lookup((char *)t->deco, strlen(t->deco));
- if (sv && sv->ptrvalue)
- { t = (Type *) sv->ptrvalue;
- assert(t->deco);
- }
- else
- assert(0);
- return t;
-}
-
-bool Type::isintegral()
-{
- return false;
-}
-
-bool Type::isfloating()
-{
- return false;
-}
-
-bool Type::isreal()
-{
- return false;
-}
-
-bool Type::isimaginary()
-{
- return false;
-}
-
-bool Type::iscomplex()
-{
- return false;
-}
-
-bool Type::isscalar()
-{
- return false;
-}
-
-bool Type::isunsigned()
-{
- return false;
-}
-
-ClassDeclaration *Type::isClassHandle()
-{
- return NULL;
-}
-
-bool Type::isscope()
-{
- return false;
-}
-
-bool Type::isString()
-{
- return false;
-}
-
-/**************************
- * When T is mutable,
- * Given:
- * T a, b;
- * Can we bitwise assign:
- * a = b;
- * ?
- */
-bool Type::isAssignable()
-{
- return true;
-}
-
-/**************************
- * Returns true if T can be converted to boolean value.
- */
-bool Type::isBoolean()
-{
- return isscalar();
-}
-
-/********************************
- * true if when type goes out of scope, it needs a destructor applied.
- * Only applies to value types, not ref types.
- */
-bool Type::needsDestruction()
-{
- return false;
-}
-
-/*********************************
- *
- */
-
-bool Type::needsNested()
-{
- return false;
-}
-
-/*********************************
- * Check type to see if it is based on a deprecated symbol.
- */
-
-void Type::checkDeprecated(Loc loc, Scope *sc)
-{
- if (Dsymbol *s = toDsymbol(sc))
- {
- s->checkDeprecated(loc, sc);
- }
-}
-
-
-Expression *Type::defaultInit(Loc)
-{
- return NULL;
-}
-
-/***************************************
- * Use when we prefer the default initializer to be a literal,
- * rather than a global immutable variable.
- */
-Expression *Type::defaultInitLiteral(Loc loc)
-{
- return defaultInit(loc);
-}
-
-bool Type::isZeroInit(Loc)
-{
- return false; // assume not
-}
-
-bool Type::isBaseOf(Type *, int *)
-{
- return 0; // assume not
-}
-
-/********************************
- * Determine if 'this' can be implicitly converted
- * to type 'to'.
- * Returns:
- * MATCHnomatch, MATCHconvert, MATCHconst, MATCHexact
- */
-
-MATCH Type::implicitConvTo(Type *to)
-{
- //printf("Type::implicitConvTo(this=%p, to=%p)\n", this, to);
- //printf("from: %s\n", toChars());
- //printf("to : %s\n", to->toChars());
- if (this->equals(to))
- return MATCHexact;
- return MATCHnomatch;
-}
-
-/*******************************
- * Determine if converting 'this' to 'to' is an identity operation,
- * a conversion to const operation, or the types aren't the same.
- * Returns:
- * MATCHexact 'this' == 'to'
- * MATCHconst 'to' is const
- * MATCHnomatch conversion to mutable or invariant
- */
-
-MATCH Type::constConv(Type *to)
-{
- //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to->toChars());
- if (equals(to))
- return MATCHexact;
- if (ty == to->ty && MODimplicitConv(mod, to->mod))
- return MATCHconst;
- return MATCHnomatch;
-}
-
-/***************************************
- * Return MOD bits matching this type to wild parameter type (tprm).
- */
-
-unsigned char Type::deduceWild(Type *t, bool)
-{
- //printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm->toChars());
-
- if (t->isWild())
- {
- if (isImmutable())
- return MODimmutable;
- else if (isWildConst())
- {
- if (t->isWildConst())
- return MODwild;
- else
- return MODwildconst;
- }
- else if (isWild())
- return MODwild;
- else if (isConst())
- return MODconst;
- else if (isMutable())
- return MODmutable;
- else
- assert(0);
- }
- return 0;
-}
-
-Type *Type::unqualify(unsigned m)
-{
- Type *t = mutableOf()->unSharedOf();
-
- Type *tn = ty == Tenum ? NULL : nextOf();
- if (tn && tn->ty != Tfunction)
- {
- Type *utn = tn->unqualify(m);
- if (utn != tn)
- {
- if (ty == Tpointer)
- t = utn->pointerTo();
- else if (ty == Tarray)
- t = utn->arrayOf();
- else if (ty == Tsarray)
- t = new TypeSArray(utn, ((TypeSArray *)this)->dim);
- else if (ty == Taarray)
- {
- t = new TypeAArray(utn, ((TypeAArray *)this)->index);
- ((TypeAArray *)t)->sc = ((TypeAArray *)this)->sc; // duplicate scope
- }
- else
- assert(0);
-
- t = t->merge();
- }
- }
- t = t->addMod(mod & ~m);
- return t;
-}
-
-Type *Type::substWildTo(unsigned mod)
-{
- //printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod);
- Type *t;
-
- if (Type *tn = nextOf())
- {
- // substitution has no effect on function pointer type.
- if (ty == Tpointer && tn->ty == Tfunction)
- {
- t = this;
- goto L1;
- }
-
- t = tn->substWildTo(mod);
- if (t == tn)
- t = this;
- else
- {
- if (ty == Tpointer)
- t = t->pointerTo();
- else if (ty == Tarray)
- t = t->arrayOf();
- else if (ty == Tsarray)
- t = new TypeSArray(t, ((TypeSArray *)this)->dim->syntaxCopy());
- else if (ty == Taarray)
- {
- t = new TypeAArray(t, ((TypeAArray *)this)->index->syntaxCopy());
- ((TypeAArray *)t)->sc = ((TypeAArray *)this)->sc; // duplicate scope
- }
- else if (ty == Tdelegate)
- {
- t = new TypeDelegate(t);
- }
- else
- assert(0);
-
- t = t->merge();
- }
- }
- else
- t = this;
-
-L1:
- if (isWild())
- {
- if (mod == MODimmutable)
- {
- t = t->immutableOf();
- }
- else if (mod == MODwildconst)
- {
- t = t->wildConstOf();
- }
- else if (mod == MODwild)
- {
- if (isWildConst())
- t = t->wildConstOf();
- else
- t = t->wildOf();
- }
- else if (mod == MODconst)
- {
- t = t->constOf();
- }
- else
- {
- if (isWildConst())
- t = t->constOf();
- else
- t = t->mutableOf();
- }
- }
- if (isConst())
- t = t->addMod(MODconst);
- if (isShared())
- t = t->addMod(MODshared);
-
- //printf("-Type::substWildTo t = %s\n", t->toChars());
- return t;
-}
-
-Type *TypeFunction::substWildTo(unsigned)
-{
- if (!iswild && !(mod & MODwild))
- return this;
-
- // Substitude inout qualifier of function type to mutable or immutable
- // would break type system. Instead substitude inout to the most weak
- // qualifer - const.
- unsigned m = MODconst;
-
- assert(next);
- Type *tret = next->substWildTo(m);
- Parameters *params = parameterList.parameters;
- if (mod & MODwild)
- params = parameterList.parameters->copy();
- for (size_t i = 0; i < params->length; i++)
- {
- Parameter *p = (*params)[i];
- Type *t = p->type->substWildTo(m);
- if (t == p->type)
- continue;
- if (params == parameterList.parameters)
- params = parameterList.parameters->copy();
- (*params)[i] = new Parameter(p->storageClass, t, NULL, NULL, NULL);
- }
- if (next == tret && params == parameterList.parameters)
- return this;
-
- // Similar to TypeFunction::syntaxCopy;
- TypeFunction *t = new TypeFunction(ParameterList(params, parameterList.varargs),
- tret, linkage);
- t->mod = ((mod & MODwild) ? (mod & ~MODwild) | MODconst : mod);
- t->isnothrow = isnothrow;
- t->isnogc = isnogc;
- t->purity = purity;
- t->isproperty = isproperty;
- t->isref = isref;
- t->isreturn = isreturn;
- t->isscope = isscope;
- t->isscopeinferred = isscopeinferred;
- t->iswild = 0;
- t->trust = trust;
- t->fargs = fargs;
- return t->merge();
-}
-
-/**************************
- * Return type with the top level of it being mutable.
- */
-Type *Type::toHeadMutable()
-{
- if (!mod)
- return this;
- return mutableOf();
-}
-
-/***************************************
- * Calculate built-in properties which just the type is necessary.
- *
- * If flag & 1, don't report "not a property" error and just return NULL.
- */
-Expression *Type::getProperty(Loc loc, Identifier *ident, int flag)
-{
- Expression *e;
-
- if (ident == Id::__sizeof)
- {
- d_uns64 sz = size(loc);
- if (sz == SIZE_INVALID)
- return new ErrorExp();
- e = new IntegerExp(loc, sz, Type::tsize_t);
- }
- else if (ident == Id::__xalignof)
- {
- unsigned explicitAlignment = alignment();
- unsigned naturalAlignment = alignsize();
- unsigned actualAlignment = (explicitAlignment == STRUCTALIGN_DEFAULT ? naturalAlignment : explicitAlignment);
- e = new IntegerExp(loc, actualAlignment, Type::tsize_t);
- }
- else if (ident == Id::_init)
- {
- Type *tb = toBasetype();
- e = defaultInitLiteral(loc);
- if (tb->ty == Tstruct && tb->needsNested())
- {
- StructLiteralExp *se = (StructLiteralExp *)e;
- se->useStaticInit = true;
- }
- }
- else if (ident == Id::_mangleof)
- {
- if (!deco)
- {
- error(loc, "forward reference of type %s.mangleof", toChars());
- e = new ErrorExp();
- }
- else
- {
- e = new StringExp(loc, (char *)deco, strlen(deco));
- Scope sc;
- e = expressionSemantic(e, &sc);
- }
- }
- else if (ident == Id::stringof)
- {
- const char *s = toChars();
- e = new StringExp(loc, const_cast<char *>(s), strlen(s));
- Scope sc;
- e = expressionSemantic(e, &sc);
- }
- else if (flag && this != Type::terror)
- {
- return NULL;
- }
- else
- {
- Dsymbol *s = NULL;
- if (ty == Tstruct || ty == Tclass || ty == Tenum)
- s = toDsymbol(NULL);
- if (s)
- s = s->search_correct(ident);
- if (this != Type::terror)
- {
- if (s)
- error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident->toChars(), toChars(), s->toPrettyChars());
- else
- error(loc, "no property `%s` for type `%s`", ident->toChars(), toChars());
- }
- e = new ErrorExp();
- }
- return e;
-}
-
-/***************************************
- * Access the members of the object e. This type is same as e->type.
- *
- * If flag & 1, don't report "not a property" error and just return NULL.
- */
-Expression *Type::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- VarDeclaration *v = NULL;
-
- Expression *ex = e;
- while (ex->op == TOKcomma)
- ex = ((CommaExp *)ex)->e2;
- if (ex->op == TOKdotvar)
- {
- DotVarExp *dv = (DotVarExp *)ex;
- v = dv->var->isVarDeclaration();
- }
- else if (ex->op == TOKvar)
- {
- VarExp *ve = (VarExp *)ex;
- v = ve->var->isVarDeclaration();
- }
- if (v)
- {
- if (ident == Id::offsetof)
- {
- if (v->isField())
- {
- AggregateDeclaration *ad = v->toParent()->isAggregateDeclaration();
- ad->size(e->loc);
- if (ad->sizeok != SIZEOKdone)
- return new ErrorExp();
- e = new IntegerExp(e->loc, v->offset, Type::tsize_t);
- return e;
- }
- }
- else if (ident == Id::_init)
- {
- Type *tb = toBasetype();
- e = defaultInitLiteral(e->loc);
- if (tb->ty == Tstruct && tb->needsNested())
- {
- StructLiteralExp *se = (StructLiteralExp *)e;
- se->useStaticInit = true;
- }
- goto Lreturn;
- }
- }
- if (ident == Id::stringof)
- {
- /* Bugzilla 3796: this should demangle e->type->deco rather than
- * pretty-printing the type.
- */
- const char *s = e->toChars();
- e = new StringExp(e->loc, const_cast<char *>(s), strlen(s));
- }
- else
- e = getProperty(e->loc, ident, flag & 1);
-
-Lreturn:
- if (e)
- e = expressionSemantic(e, sc);
- return e;
-}
-
-/************************************
- * Return alignment to use for this type.
- */
-
-structalign_t Type::alignment()
-{
- return STRUCTALIGN_DEFAULT;
-}
-
-/***************************************
- * Figures out what to do with an undefined member reference
- * for classes and structs.
- *
- * If flag & 1, don't report "not a property" error and just return NULL.
- */
-Expression *Type::noMember(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- //printf("Type::noMember(e: %s ident: %s flag: %d)\n", e->toChars(), ident->toChars(), flag);
-
- static int nest; // https://issues.dlang.org/show_bug.cgi?id=17380
-
- if (++nest > global.recursionLimit)
- {
- ::error(e->loc, "cannot resolve identifier `%s`", ident->toChars());
- --nest;
- return (flag & 1) ? NULL : new ErrorExp();
- }
-
- assert(ty == Tstruct || ty == Tclass);
- AggregateDeclaration *sym = toDsymbol(sc)->isAggregateDeclaration();
- assert(sym);
-
- if (ident != Id::__sizeof &&
- ident != Id::__xalignof &&
- ident != Id::_init &&
- ident != Id::_mangleof &&
- ident != Id::stringof &&
- ident != Id::offsetof &&
- // Bugzilla 15045: Don't forward special built-in member functions.
- ident != Id::ctor &&
- ident != Id::dtor &&
- ident != Id::__xdtor &&
- ident != Id::postblit &&
- ident != Id::__xpostblit)
- {
- /* Look for overloaded opDot() to see if we should forward request
- * to it.
- */
- if (Dsymbol *fd = search_function(sym, Id::opDot))
- {
- /* Rewrite e.ident as:
- * e.opDot().ident
- */
- e = build_overload(e->loc, sc, e, NULL, fd);
- e = new DotIdExp(e->loc, e, ident);
- e = expressionSemantic(e, sc);
- --nest;
- return e;
- }
-
- /* Look for overloaded opDispatch to see if we should forward request
- * to it.
- */
- if (Dsymbol *fd = search_function(sym, Id::opDispatch))
- {
- /* Rewrite e.ident as:
- * e.opDispatch!("ident")
- */
- TemplateDeclaration *td = fd->isTemplateDeclaration();
- if (!td)
- {
- fd->error("must be a template opDispatch(string s), not a %s", fd->kind());
- --nest;
- return new ErrorExp();
- }
- StringExp *se = new StringExp(e->loc, const_cast<char *>(ident->toChars()));
- Objects *tiargs = new Objects();
- tiargs->push(se);
- DotTemplateInstanceExp *dti = new DotTemplateInstanceExp(e->loc, e, Id::opDispatch, tiargs);
- dti->ti->tempdecl = td;
-
- /* opDispatch, which doesn't need IFTI, may occur instantiate error.
- * It should be gagged if flag & 1.
- * e.g.
- * template opDispatch(name) if (isValid!name) { ... }
- */
- unsigned errors = flag & 1 ? global.startGagging() : 0;
- e = semanticY(dti, sc, 0);
- if (flag & 1 && global.endGagging(errors))
- e = NULL;
- --nest;
- return e;
- }
-
- /* See if we should forward to the alias this.
- */
- if (sym->aliasthis)
- { /* Rewrite e.ident as:
- * e.aliasthis.ident
- */
- e = resolveAliasThis(sc, e);
- DotIdExp *die = new DotIdExp(e->loc, e, ident);
- e = semanticY(die, sc, flag & 1);
- --nest;
- return e;
- }
- }
-
- e = Type::dotExp(sc, e, ident, flag);
- --nest;
- return e;
-}
-
-void Type::error(Loc loc, const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::verror(loc, format, ap);
- va_end( ap );
-}
-
-void Type::warning(Loc loc, const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::vwarning(loc, format, ap);
- va_end( ap );
-}
-
-Identifier *Type::getTypeInfoIdent()
-{
- // _init_10TypeInfo_%s
- OutBuffer buf;
- buf.reserve(32);
- mangleToBuffer(this, &buf);
-
- size_t len = buf.length();
- buf.writeByte(0);
-
- // Allocate buffer on stack, fail over to using malloc()
- char namebuf[128];
- size_t namelen = 19 + sizeof(len) * 3 + len + 1;
- char *name = namelen <= sizeof(namebuf) ? namebuf : (char *)mem.xmalloc(namelen);
-
- int length = sprintf(name, "_D%lluTypeInfo_%s6__initZ", (unsigned long long) 9 + len, buf.slice().ptr);
- //printf("%p, deco = %s, name = %s\n", this, deco, name);
- assert(0 < length && (size_t)length < namelen); // don't overflow the buffer
-
- Identifier *id = Identifier::idPool(name, length);
-
- if (name != namebuf)
- free(name);
- return id;
-}
-
-TypeBasic *Type::isTypeBasic()
-{
- return NULL;
-}
-
-TypeError *Type::isTypeError()
-{
- return ty == Terror ? (TypeError *)this : NULL;
-}
-
-TypeVector *Type::isTypeVector()
-{
- return ty == Tvector ? (TypeVector *)this : NULL;
-}
-
-TypeSArray *Type::isTypeSArray()
-{
- return ty == Tsarray ? (TypeSArray *)this : NULL;
-}
-
-TypeDArray *Type::isTypeDArray()
-{
- return ty == Tarray ? (TypeDArray *)this : NULL;
-}
-
-TypeAArray *Type::isTypeAArray()
-{
- return ty == Taarray ? (TypeAArray *)this : NULL;
-}
-
-TypePointer *Type::isTypePointer()
-{
- return ty == Tpointer ? (TypePointer *)this : NULL;
-}
-
-TypeReference *Type::isTypeReference()
-{
- return ty == Treference ? (TypeReference *)this : NULL;
-}
-
-TypeFunction *Type::isTypeFunction()
-{
- return ty == Tfunction ? (TypeFunction *)this : NULL;
-}
-
-TypeDelegate *Type::isTypeDelegate()
-{
- return ty == Tdelegate ? (TypeDelegate *)this : NULL;
-}
-
-TypeIdentifier *Type::isTypeIdentifier()
-{
- return ty == Tident ? (TypeIdentifier *)this : NULL;
-}
-
-TypeInstance *Type::isTypeInstance()
-{
- return ty == Tinstance ? (TypeInstance *)this : NULL;
-}
-
-TypeTypeof *Type::isTypeTypeof()
-{
- return ty == Ttypeof ? (TypeTypeof *)this : NULL;
-}
-
-TypeReturn *Type::isTypeReturn()
-{
- return ty == Treturn ? (TypeReturn *)this : NULL;
-}
-
-TypeStruct *Type::isTypeStruct()
-{
- return ty == Tstruct ? (TypeStruct *)this : NULL;
-}
-
-TypeEnum *Type::isTypeEnum()
-{
- return ty == Tenum ? (TypeEnum *)this : NULL;
-}
-
-TypeClass *Type::isTypeClass()
-{
- return ty == Tclass ? (TypeClass *)this : NULL;
-}
-
-TypeTuple *Type::isTypeTuple()
-{
- return ty == Ttuple ? (TypeTuple *)this : NULL;
-}
-
-TypeSlice *Type::isTypeSlice()
-{
- return ty == Tslice ? (TypeSlice *)this : NULL;
-}
-
-TypeNull *Type::isTypeNull()
-{
- return ty == Tnull ? (TypeNull *)this : NULL;
-}
-
-TypeTraits *Type::isTypeTraits()
-{
- return ty == Ttraits ? (TypeTraits *)this : NULL;
-}
-
-TypeMixin *Type::isTypeMixin()
-{
- return ty == Tmixin ? (TypeMixin *)this : NULL;
-}
-
-TypeNoreturn *Type::isTypeNoreturn()
-{
- return ty == Tnoreturn ? (TypeNoreturn *)this : NULL;
-}
-
-TypeFunction *Type::toTypeFunction()
-{
- if (ty != Tfunction)
- assert(0);
- return (TypeFunction *)this;
-}
-
-/***************************************
- * Resolve 'this' type to either type, symbol, or expression.
- * If errors happened, resolved to Type.terror.
- */
-void Type::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool)
-{
- //printf("Type::resolve() %s, %d\n", toChars(), ty);
- Type *t = typeSemantic(this, loc, sc);
- *pt = t;
- *pe = NULL;
- *ps = NULL;
-}
-
-/***************************************
- * Normalize `e` as the result of Type::resolve() process.
- */
-void Type::resolveExp(Expression *e, Type **pt, Expression **pe, Dsymbol **ps)
-{
- *pt = NULL;
- *pe = NULL;
- *ps = NULL;
-
- Dsymbol *s;
- switch (e->op)
- {
- case TOKerror:
- *pt = Type::terror;
- return;
-
- case TOKtype:
- *pt = e->type;
- return;
-
- case TOKvar:
- s = ((VarExp *)e)->var;
- if (s->isVarDeclaration())
- goto Ldefault;
- //if (s->isOverDeclaration())
- // todo;
- break;
-
- case TOKtemplate:
- // TemplateDeclaration
- s = ((TemplateExp *)e)->td;
- break;
-
- case TOKimport:
- s = ((ScopeExp *)e)->sds;
- // TemplateDeclaration, TemplateInstance, Import, Package, Module
- break;
-
- case TOKfunction:
- s = getDsymbol(e);
- break;
-
- //case TOKthis:
- //case TOKsuper:
-
- //case TOKtuple:
-
- //case TOKoverloadset:
-
- //case TOKdotvar:
- //case TOKdottd:
- //case TOKdotti:
- //case TOKdottype:
- //case TOKdot:
-
- default:
- Ldefault:
- *pe = e;
- return;
- }
-
- *ps = s;
-}
-
-/***************************************
- * Return !=0 if the type or any of its subtypes is wild.
- */
-
-int Type::hasWild() const
-{
- return mod & MODwild;
-}
-
-/***************************************
- * Return !=0 if type has pointers that need to
- * be scanned by the GC during a collection cycle.
- */
-bool Type::hasPointers()
-{
- //printf("Type::hasPointers() %s, %d\n", toChars(), ty);
- return false;
-}
-
-/*************************************
- * Detect if type has pointer fields that are initialized to void.
- * Local stack variables with such void fields can remain uninitialized,
- * leading to pointer bugs.
- * Returns:
- * true if so
- */
-bool Type::hasVoidInitPointers()
-{
- return false;
-}
-
-/*************************************
- * If this is a type of something, return that something.
- */
-
-Type *Type::nextOf()
-{
- return NULL;
-}
-
-/*************************************
- * If this is a type of static array, return its base element type.
- */
-
-Type *Type::baseElemOf()
-{
- Type *t = toBasetype();
- while (t->ty == Tsarray)
- t = ((TypeSArray *)t)->next->toBasetype();
- return t;
-}
-
-/*************************************
- * Bugzilla 14488: Check if the inner most base type is complex or imaginary.
- * Should only give alerts when set to emit transitional messages.
- */
-
-void Type::checkComplexTransition(Loc loc)
-{
- Type *t = baseElemOf();
- while (t->ty == Tpointer || t->ty == Tarray)
- t = t->nextOf()->baseElemOf();
-
- if (t->isimaginary() || t->iscomplex())
- {
- Type *rt;
- switch (t->ty)
- {
- case Tcomplex32:
- case Timaginary32:
- rt = Type::tfloat32; break;
- case Tcomplex64:
- case Timaginary64:
- rt = Type::tfloat64; break;
- case Tcomplex80:
- case Timaginary80:
- rt = Type::tfloat80; break;
- default:
- assert(0);
- }
- if (t->iscomplex())
- {
- message(loc, "use of complex type `%s` is scheduled for deprecation, "
- "use `std.complex.Complex!(%s)` instead", toChars(), rt->toChars());
- }
- else
- {
- message(loc, "use of imaginary type `%s` is scheduled for deprecation, "
- "use `%s` instead\n", toChars(), rt->toChars());
- }
- }
-}
-
-/*******************************************
- * Compute number of elements for a (possibly multidimensional) static array,
- * or 1 for other types.
- * Params:
- * loc = for error message
- * Returns:
- * number of elements, uint.max on overflow
- */
-unsigned Type::numberOfElems(const Loc &loc)
-{
- //printf("Type::numberOfElems()\n");
- uinteger_t n = 1;
- Type *tb = this;
- while ((tb = tb->toBasetype())->ty == Tsarray)
- {
- bool overflow = false;
- n = mulu(n, ((TypeSArray *)tb)->dim->toUInteger(), overflow);
- if (overflow || n >= UINT32_MAX)
- {
- error(loc, "static array `%s` size overflowed to %llu", toChars(), (unsigned long long)n);
- return UINT32_MAX;
- }
- tb = ((TypeSArray *)tb)->next;
- }
- return (unsigned)n;
-}
-
-/****************************************
- * Return the mask that an integral type will
- * fit into.
- */
-uinteger_t Type::sizemask()
-{ uinteger_t m;
-
- switch (toBasetype()->ty)
- {
- case Tbool: m = 1; break;
- case Tchar:
- case Tint8:
- case Tuns8: m = 0xFF; break;
- case Twchar:
- case Tint16:
- case Tuns16: m = 0xFFFFUL; break;
- case Tdchar:
- case Tint32:
- case Tuns32: m = 0xFFFFFFFFUL; break;
- case Tint64:
- case Tuns64: m = 0xFFFFFFFFFFFFFFFFULL; break;
- default:
- assert(0);
- }
- return m;
-}
-
-/* ============================= TypeError =========================== */
-
-TypeError::TypeError()
- : Type(Terror)
-{
-}
-
-Type *TypeError::syntaxCopy()
-{
- // No semantic analysis done, no need to copy
- return this;
-}
-
-d_uns64 TypeError::size(Loc) { return SIZE_INVALID; }
-Expression *TypeError::getProperty(Loc, Identifier *, int) { return new ErrorExp(); }
-Expression *TypeError::dotExp(Scope *, Expression *, Identifier *, int) { return new ErrorExp(); }
-Expression *TypeError::defaultInit(Loc) { return new ErrorExp(); }
-Expression *TypeError::defaultInitLiteral(Loc) { return new ErrorExp(); }
-
-/* ============================= TypeNext =========================== */
-
-TypeNext::TypeNext(TY ty, Type *next)
- : Type(ty)
-{
- this->next = next;
-}
-
-void TypeNext::checkDeprecated(Loc loc, Scope *sc)
-{
- Type::checkDeprecated(loc, sc);
- if (next) // next can be NULL if TypeFunction and auto return type
- next->checkDeprecated(loc, sc);
-}
-
-int TypeNext::hasWild() const
-{
- if (ty == Tfunction)
- return 0;
- if (ty == Tdelegate)
- return Type::hasWild();
- return mod & MODwild || (next && next->hasWild());
-}
-
-
-/*******************************
- * For TypeFunction, nextOf() can return NULL if the function return
- * type is meant to be inferred, and semantic() hasn't yet ben run
- * on the function. After semantic(), it must no longer be NULL.
- */
-
-Type *TypeNext::nextOf()
-{
- return next;
-}
-
-Type *TypeNext::makeConst()
-{
- //printf("TypeNext::makeConst() %p, %s\n", this, toChars());
- if (cto)
- {
- assert(cto->mod == MODconst);
- return cto;
- }
- TypeNext *t = (TypeNext *)Type::makeConst();
- if (ty != Tfunction && next->ty != Tfunction &&
- !next->isImmutable())
- {
- if (next->isShared())
- {
- if (next->isWild())
- t->next = next->sharedWildConstOf();
- else
- t->next = next->sharedConstOf();
- }
- else
- {
- if (next->isWild())
- t->next = next->wildConstOf();
- else
- t->next = next->constOf();
- }
- }
- //printf("TypeNext::makeConst() returns %p, %s\n", t, t->toChars());
- return t;
-}
-
-Type *TypeNext::makeImmutable()
-{
- //printf("TypeNext::makeImmutable() %s\n", toChars());
- if (ito)
- {
- assert(ito->isImmutable());
- return ito;
- }
- TypeNext *t = (TypeNext *)Type::makeImmutable();
- if (ty != Tfunction && next->ty != Tfunction &&
- !next->isImmutable())
- {
- t->next = next->immutableOf();
- }
- return t;
-}
-
-Type *TypeNext::makeShared()
-{
- //printf("TypeNext::makeShared() %s\n", toChars());
- if (sto)
- {
- assert(sto->mod == MODshared);
- return sto;
- }
- TypeNext *t = (TypeNext *)Type::makeShared();
- if (ty != Tfunction && next->ty != Tfunction &&
- !next->isImmutable())
- {
- if (next->isWild())
- {
- if (next->isConst())
- t->next = next->sharedWildConstOf();
- else
- t->next = next->sharedWildOf();
- }
- else
- {
- if (next->isConst())
- t->next = next->sharedConstOf();
- else
- t->next = next->sharedOf();
- }
- }
- //printf("TypeNext::makeShared() returns %p, %s\n", t, t->toChars());
- return t;
-}
-
-Type *TypeNext::makeSharedConst()
-{
- //printf("TypeNext::makeSharedConst() %s\n", toChars());
- if (scto)
- {
- assert(scto->mod == (MODshared | MODconst));
- return scto;
- }
- TypeNext *t = (TypeNext *)Type::makeSharedConst();
- if (ty != Tfunction && next->ty != Tfunction &&
- !next->isImmutable())
- {
- if (next->isWild())
- t->next = next->sharedWildConstOf();
- else
- t->next = next->sharedConstOf();
- }
- //printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t->toChars());
- return t;
-}
-
-Type *TypeNext::makeWild()
-{
- //printf("TypeNext::makeWild() %s\n", toChars());
- if (wto)
- {
- assert(wto->mod == MODwild);
- return wto;
- }
- TypeNext *t = (TypeNext *)Type::makeWild();
- if (ty != Tfunction && next->ty != Tfunction &&
- !next->isImmutable())
- {
- if (next->isShared())
- {
- if (next->isConst())
- t->next = next->sharedWildConstOf();
- else
- t->next = next->sharedWildOf();
- }
- else
- {
- if (next->isConst())
- t->next = next->wildConstOf();
- else
- t->next = next->wildOf();
- }
- }
- //printf("TypeNext::makeWild() returns %p, %s\n", t, t->toChars());
- return t;
-}
-
-Type *TypeNext::makeWildConst()
-{
- //printf("TypeNext::makeWildConst() %s\n", toChars());
- if (wcto)
- {
- assert(wcto->mod == MODwildconst);
- return wcto;
- }
- TypeNext *t = (TypeNext *)Type::makeWildConst();
- if (ty != Tfunction && next->ty != Tfunction &&
- !next->isImmutable())
- {
- if (next->isShared())
- t->next = next->sharedWildConstOf();
- else
- t->next = next->wildConstOf();
- }
- //printf("TypeNext::makeWildConst() returns %p, %s\n", t, t->toChars());
- return t;
-}
-
-Type *TypeNext::makeSharedWild()
-{
- //printf("TypeNext::makeSharedWild() %s\n", toChars());
- if (swto)
- {
- assert(swto->isSharedWild());
- return swto;
- }
- TypeNext *t = (TypeNext *)Type::makeSharedWild();
- if (ty != Tfunction && next->ty != Tfunction &&
- !next->isImmutable())
- {
- if (next->isConst())
- t->next = next->sharedWildConstOf();
- else
- t->next = next->sharedWildOf();
- }
- //printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t->toChars());
- return t;
-}
-
-Type *TypeNext::makeSharedWildConst()
-{
- //printf("TypeNext::makeSharedWildConst() %s\n", toChars());
- if (swcto)
- {
- assert(swcto->mod == (MODshared | MODwildconst));
- return swcto;
- }
- TypeNext *t = (TypeNext *)Type::makeSharedWildConst();
- if (ty != Tfunction && next->ty != Tfunction &&
- !next->isImmutable())
- {
- t->next = next->sharedWildConstOf();
- }
- //printf("TypeNext::makeSharedWildConst() returns %p, %s\n", t, t->toChars());
- return t;
-}
-
-Type *TypeNext::makeMutable()
-{
- //printf("TypeNext::makeMutable() %p, %s\n", this, toChars());
- TypeNext *t = (TypeNext *)Type::makeMutable();
- if (ty == Tsarray)
- {
- t->next = next->mutableOf();
- }
- //printf("TypeNext::makeMutable() returns %p, %s\n", t, t->toChars());
- return t;
-}
-
-MATCH TypeNext::constConv(Type *to)
-{
- //printf("TypeNext::constConv from = %s, to = %s\n", toChars(), to->toChars());
- if (equals(to))
- return MATCHexact;
-
- if (!(ty == to->ty && MODimplicitConv(mod, to->mod)))
- return MATCHnomatch;
-
- Type *tn = to->nextOf();
- if (!(tn && next->ty == tn->ty))
- return MATCHnomatch;
-
- MATCH m;
- if (to->isConst()) // whole tail const conversion
- { // Recursive shared level check
- m = next->constConv(tn);
- if (m == MATCHexact)
- m = MATCHconst;
- }
- else
- { //printf("\tnext => %s, to->next => %s\n", next->toChars(), tn->toChars());
- m = next->equals(tn) ? MATCHconst : MATCHnomatch;
- }
- return m;
-}
-
-unsigned char TypeNext::deduceWild(Type *t, bool isRef)
-{
- if (ty == Tfunction)
- return 0;
-
- unsigned char wm;
-
- Type *tn = t->nextOf();
- if (!isRef && (ty == Tarray || ty == Tpointer) && tn)
- {
- wm = next->deduceWild(tn, true);
- if (!wm)
- wm = Type::deduceWild(t, true);
- }
- else
- {
- wm = Type::deduceWild(t, isRef);
- if (!wm && tn)
- wm = next->deduceWild(tn, true);
- }
-
- return wm;
-}
-
-
-void TypeNext::transitive()
-{
- /* Invoke transitivity of type attributes
- */
- next = next->addMod(mod);
-}
-
-/* ============================= TypeBasic =========================== */
-
-#define TFLAGSintegral 1
-#define TFLAGSfloating 2
-#define TFLAGSunsigned 4
-#define TFLAGSreal 8
-#define TFLAGSimaginary 0x10
-#define TFLAGScomplex 0x20
-
-TypeBasic::TypeBasic(TY ty)
- : Type(ty)
-{ const char *d;
- unsigned flags;
-
- flags = 0;
- switch (ty)
- {
- case Tvoid: d = Token::toChars(TOKvoid);
- break;
-
- case Tint8: d = Token::toChars(TOKint8);
- flags |= TFLAGSintegral;
- break;
-
- case Tuns8: d = Token::toChars(TOKuns8);
- flags |= TFLAGSintegral | TFLAGSunsigned;
- break;
-
- case Tint16: d = Token::toChars(TOKint16);
- flags |= TFLAGSintegral;
- break;
-
- case Tuns16: d = Token::toChars(TOKuns16);
- flags |= TFLAGSintegral | TFLAGSunsigned;
- break;
-
- case Tint32: d = Token::toChars(TOKint32);
- flags |= TFLAGSintegral;
- break;
-
- case Tuns32: d = Token::toChars(TOKuns32);
- flags |= TFLAGSintegral | TFLAGSunsigned;
- break;
-
- case Tfloat32: d = Token::toChars(TOKfloat32);
- flags |= TFLAGSfloating | TFLAGSreal;
- break;
-
- case Tint64: d = Token::toChars(TOKint64);
- flags |= TFLAGSintegral;
- break;
-
- case Tuns64: d = Token::toChars(TOKuns64);
- flags |= TFLAGSintegral | TFLAGSunsigned;
- break;
-
- case Tint128: d = Token::toChars(TOKint128);
- flags |= TFLAGSintegral;
- break;
-
- case Tuns128: d = Token::toChars(TOKuns128);
- flags |= TFLAGSintegral | TFLAGSunsigned;
- break;
-
- case Tfloat64: d = Token::toChars(TOKfloat64);
- flags |= TFLAGSfloating | TFLAGSreal;
- break;
-
- case Tfloat80: d = Token::toChars(TOKfloat80);
- flags |= TFLAGSfloating | TFLAGSreal;
- break;
-
- case Timaginary32: d = Token::toChars(TOKimaginary32);
- flags |= TFLAGSfloating | TFLAGSimaginary;
- break;
-
- case Timaginary64: d = Token::toChars(TOKimaginary64);
- flags |= TFLAGSfloating | TFLAGSimaginary;
- break;
-
- case Timaginary80: d = Token::toChars(TOKimaginary80);
- flags |= TFLAGSfloating | TFLAGSimaginary;
- break;
-
- case Tcomplex32: d = Token::toChars(TOKcomplex32);
- flags |= TFLAGSfloating | TFLAGScomplex;
- break;
-
- case Tcomplex64: d = Token::toChars(TOKcomplex64);
- flags |= TFLAGSfloating | TFLAGScomplex;
- break;
-
- case Tcomplex80: d = Token::toChars(TOKcomplex80);
- flags |= TFLAGSfloating | TFLAGScomplex;
- break;
-
- case Tbool: d = "bool";
- flags |= TFLAGSintegral | TFLAGSunsigned;
- break;
-
- case Tchar: d = Token::toChars(TOKchar);
- flags |= TFLAGSintegral | TFLAGSunsigned;
- break;
-
- case Twchar: d = Token::toChars(TOKwchar);
- flags |= TFLAGSintegral | TFLAGSunsigned;
- break;
-
- case Tdchar: d = Token::toChars(TOKdchar);
- flags |= TFLAGSintegral | TFLAGSunsigned;
- break;
-
- default: assert(0);
- }
- this->dstring = d;
- this->flags = flags;
- merge();
-}
-
-const char *TypeBasic::kind()
-{
- return dstring;
-}
-
-Type *TypeBasic::syntaxCopy()
-{
- // No semantic analysis done on basic types, no need to copy
- return this;
-}
-
-d_uns64 TypeBasic::size(Loc)
-{ unsigned size;
-
- //printf("TypeBasic::size()\n");
- switch (ty)
- {
- case Tint8:
- case Tuns8: size = 1; break;
- case Tint16:
- case Tuns16: size = 2; break;
- case Tint32:
- case Tuns32:
- case Tfloat32:
- case Timaginary32:
- size = 4; break;
- case Tint64:
- case Tuns64:
- case Tfloat64:
- case Timaginary64:
- size = 8; break;
- case Tfloat80:
- case Timaginary80:
- size = target.realsize; break;
- case Tcomplex32:
- size = 8; break;
- case Tcomplex64:
- case Tint128:
- case Tuns128:
- size = 16; break;
- case Tcomplex80:
- size = target.realsize * 2; break;
-
- case Tvoid:
- //size = Type::size(); // error message
- size = 1;
- break;
-
- case Tbool: size = 1; break;
- case Tchar: size = 1; break;
- case Twchar: size = 2; break;
- case Tdchar: size = 4; break;
-
- default:
- assert(0);
- break;
- }
- //printf("TypeBasic::size() = %d\n", size);
- return size;
-}
-
-unsigned TypeBasic::alignsize()
-{
- return target.alignsize(this);
-}
-
-
-Expression *TypeBasic::getProperty(Loc loc, Identifier *ident, int flag)
-{
- Expression *e;
- dinteger_t ivalue;
- real_t fvalue;
-
- //printf("TypeBasic::getProperty('%s')\n", ident->toChars());
- if (ident == Id::max)
- {
- switch (ty)
- {
- case Tint8:
- ivalue = 0x7F;
- goto Livalue;
- case Tuns8:
- ivalue = 0xFF;
- goto Livalue;
- case Tint16:
- ivalue = 0x7FFFUL;
- goto Livalue;
- case Tuns16:
- ivalue = 0xFFFFUL;
- goto Livalue;
- case Tint32:
- ivalue = 0x7FFFFFFFUL;
- goto Livalue;
- case Tuns32:
- ivalue = 0xFFFFFFFFUL;
- goto Livalue;
- case Tint64:
- ivalue = 0x7FFFFFFFFFFFFFFFLL;
- goto Livalue;
- case Tuns64:
- ivalue = 0xFFFFFFFFFFFFFFFFULL;
- goto Livalue;
- case Tbool:
- ivalue = 1;
- goto Livalue;
- case Tchar:
- ivalue = 0xFF;
- goto Livalue;
- case Twchar:
- ivalue = 0xFFFFUL;
- goto Livalue;
- case Tdchar:
- ivalue = 0x10FFFFUL;
- goto Livalue;
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- fvalue = target.FloatProperties.max;
- goto Lfvalue;
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- fvalue = target.DoubleProperties.max;
- goto Lfvalue;
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- fvalue = target.RealProperties.max;
- goto Lfvalue;
- }
- }
- else if (ident == Id::min)
- {
- switch (ty)
- {
- case Tint8:
- ivalue = -128;
- goto Livalue;
- case Tuns8:
- ivalue = 0;
- goto Livalue;
- case Tint16:
- ivalue = -32768;
- goto Livalue;
- case Tuns16:
- ivalue = 0;
- goto Livalue;
- case Tint32:
- ivalue = -2147483647L - 1;
- goto Livalue;
- case Tuns32:
- ivalue = 0;
- goto Livalue;
- case Tint64:
- ivalue = (-9223372036854775807LL-1LL);
- goto Livalue;
- case Tuns64:
- ivalue = 0;
- goto Livalue;
- case Tbool:
- ivalue = 0;
- goto Livalue;
- case Tchar:
- ivalue = 0;
- goto Livalue;
- case Twchar:
- ivalue = 0;
- goto Livalue;
- case Tdchar:
- ivalue = 0;
- goto Livalue;
-
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- error(loc, "use .min_normal property instead of .min");
- return new ErrorExp();
- }
- }
- else if (ident == Id::min_normal)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- fvalue = target.FloatProperties.min_normal;
- goto Lfvalue;
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- fvalue = target.DoubleProperties.min_normal;
- goto Lfvalue;
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- fvalue = target.RealProperties.min_normal;
- goto Lfvalue;
- }
- }
- else if (ident == Id::nan)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Tcomplex64:
- case Tcomplex80:
- case Timaginary32:
- case Timaginary64:
- case Timaginary80:
- case Tfloat32:
- case Tfloat64:
- case Tfloat80:
- fvalue = target.RealProperties.nan;
- goto Lfvalue;
- }
- }
- else if (ident == Id::infinity)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Tcomplex64:
- case Tcomplex80:
- case Timaginary32:
- case Timaginary64:
- case Timaginary80:
- case Tfloat32:
- case Tfloat64:
- case Tfloat80:
- fvalue = target.RealProperties.infinity;
- goto Lfvalue;
- }
- }
- else if (ident == Id::dig)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- ivalue = target.FloatProperties.dig;
- goto Lint;
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- ivalue = target.DoubleProperties.dig;
- goto Lint;
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- ivalue = target.RealProperties.dig;
- goto Lint;
- }
- }
- else if (ident == Id::epsilon)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- fvalue = target.FloatProperties.epsilon;
- goto Lfvalue;
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- fvalue = target.DoubleProperties.epsilon;
- goto Lfvalue;
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- fvalue = target.RealProperties.epsilon;
- goto Lfvalue;
- }
- }
- else if (ident == Id::mant_dig)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- ivalue = target.FloatProperties.mant_dig;
- goto Lint;
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- ivalue = target.DoubleProperties.mant_dig;
- goto Lint;
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- ivalue = target.RealProperties.mant_dig;
- goto Lint;
- }
- }
- else if (ident == Id::max_10_exp)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- ivalue = target.FloatProperties.max_10_exp;
- goto Lint;
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- ivalue = target.DoubleProperties.max_10_exp;
- goto Lint;
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- ivalue = target.RealProperties.max_10_exp;
- goto Lint;
- }
- }
- else if (ident == Id::max_exp)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- ivalue = target.FloatProperties.max_exp;
- goto Lint;
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- ivalue = target.DoubleProperties.max_exp;
- goto Lint;
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- ivalue = target.RealProperties.max_exp;
- goto Lint;
- }
- }
- else if (ident == Id::min_10_exp)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- ivalue = target.FloatProperties.min_10_exp;
- goto Lint;
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- ivalue = target.DoubleProperties.min_10_exp;
- goto Lint;
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- ivalue = target.RealProperties.min_10_exp;
- goto Lint;
- }
- }
- else if (ident == Id::min_exp)
- {
- switch (ty)
- {
- case Tcomplex32:
- case Timaginary32:
- case Tfloat32:
- ivalue = target.FloatProperties.min_exp;
- goto Lint;
- case Tcomplex64:
- case Timaginary64:
- case Tfloat64:
- ivalue = target.DoubleProperties.min_exp;
- goto Lint;
- case Tcomplex80:
- case Timaginary80:
- case Tfloat80:
- ivalue = target.RealProperties.min_exp;
- goto Lint;
- }
- }
-
- return Type::getProperty(loc, ident, flag);
-
-Livalue:
- e = new IntegerExp(loc, ivalue, this);
- return e;
-
-Lfvalue:
- if (isreal() || isimaginary())
- e = new RealExp(loc, fvalue, this);
- else
- {
- complex_t cvalue = complex_t(fvalue, fvalue);
- //for (int i = 0; i < 20; i++)
- // printf("%02x ", ((unsigned char *)&cvalue)[i]);
- //printf("\n");
- e = new ComplexExp(loc, cvalue, this);
- }
- return e;
-
-Lint:
- e = new IntegerExp(loc, ivalue, Type::tint32);
- return e;
-}
-
-Expression *TypeBasic::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- Type *t;
-
- if (ident == Id::re)
- {
- switch (ty)
- {
- case Tcomplex32: t = tfloat32; goto L1;
- case Tcomplex64: t = tfloat64; goto L1;
- case Tcomplex80: t = tfloat80; goto L1;
- L1:
- e = e->castTo(sc, t);
- break;
-
- case Tfloat32:
- case Tfloat64:
- case Tfloat80:
- break;
-
- case Timaginary32: t = tfloat32; goto L2;
- case Timaginary64: t = tfloat64; goto L2;
- case Timaginary80: t = tfloat80; goto L2;
- L2:
- e = new RealExp(e->loc, CTFloat::zero, t);
- break;
-
- default:
- e = Type::getProperty(e->loc, ident, flag);
- break;
- }
- }
- else if (ident == Id::im)
- { Type *t2;
-
- switch (ty)
- {
- case Tcomplex32: t = timaginary32; t2 = tfloat32; goto L3;
- case Tcomplex64: t = timaginary64; t2 = tfloat64; goto L3;
- case Tcomplex80: t = timaginary80; t2 = tfloat80; goto L3;
- L3:
- e = e->castTo(sc, t);
- e->type = t2;
- break;
-
- case Timaginary32: t = tfloat32; goto L4;
- case Timaginary64: t = tfloat64; goto L4;
- case Timaginary80: t = tfloat80; goto L4;
- L4:
- e = e->copy();
- e->type = t;
- break;
-
- case Tfloat32:
- case Tfloat64:
- case Tfloat80:
- e = new RealExp(e->loc, CTFloat::zero, this);
- break;
-
- default:
- e = Type::getProperty(e->loc, ident, flag);
- break;
- }
- }
- else
- {
- return Type::dotExp(sc, e, ident, flag);
- }
- if (!(flag & 1) || e)
- e = expressionSemantic(e, sc);
- return e;
-}
-
-Expression *TypeBasic::defaultInit(Loc loc)
-{
- dinteger_t value = 0;
-
- switch (ty)
- {
- case Tchar:
- value = 0xFF;
- break;
-
- case Twchar:
- case Tdchar:
- value = 0xFFFF;
- break;
-
- case Timaginary32:
- case Timaginary64:
- case Timaginary80:
- case Tfloat32:
- case Tfloat64:
- case Tfloat80:
- return new RealExp(loc, target.RealProperties.snan, this);
-
- case Tcomplex32:
- case Tcomplex64:
- case Tcomplex80:
- { // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN).
- complex_t cvalue = complex_t(target.RealProperties.snan, target.RealProperties.snan);
- return new ComplexExp(loc, cvalue, this);
- }
-
- case Tvoid:
- error(loc, "void does not have a default initializer");
- return new ErrorExp();
- }
- return new IntegerExp(loc, value, this);
-}
-
-bool TypeBasic::isZeroInit(Loc)
-{
- switch (ty)
- {
- case Tchar:
- case Twchar:
- case Tdchar:
- case Timaginary32:
- case Timaginary64:
- case Timaginary80:
- case Tfloat32:
- case Tfloat64:
- case Tfloat80:
- case Tcomplex32:
- case Tcomplex64:
- case Tcomplex80:
- return false; // no
- default:
- return true; // yes
- }
-}
-
-bool TypeBasic::isintegral()
-{
- //printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags);
- return (flags & TFLAGSintegral) != 0;
-}
-
-bool TypeBasic::isfloating()
-{
- return (flags & TFLAGSfloating) != 0;
-}
-
-bool TypeBasic::isreal()
-{
- return (flags & TFLAGSreal) != 0;
-}
-
-bool TypeBasic::isimaginary()
-{
- return (flags & TFLAGSimaginary) != 0;
-}
-
-bool TypeBasic::iscomplex()
-{
- return (flags & TFLAGScomplex) != 0;
-}
-
-bool TypeBasic::isunsigned()
-{
- return (flags & TFLAGSunsigned) != 0;
-}
-
-bool TypeBasic::isscalar()
-{
- return (flags & (TFLAGSintegral | TFLAGSfloating)) != 0;
-}
-
-MATCH TypeBasic::implicitConvTo(Type *to)
-{
- //printf("TypeBasic::implicitConvTo(%s) from %s\n", to->toChars(), toChars());
- if (this == to)
- return MATCHexact;
-
- if (ty == to->ty)
- {
- if (mod == to->mod)
- return MATCHexact;
- else if (MODimplicitConv(mod, to->mod))
- return MATCHconst;
- else if (!((mod ^ to->mod) & MODshared)) // for wild matching
- return MATCHconst;
- else
- return MATCHconvert;
- }
-
- if (ty == Tvoid || to->ty == Tvoid)
- return MATCHnomatch;
- if (to->ty == Tbool)
- return MATCHnomatch;
-
- TypeBasic *tob;
- if (to->ty == Tvector && to->deco)
- {
- TypeVector *tv = (TypeVector *)to;
- tob = tv->elementType();
- }
- else if (to->ty == Tenum)
- {
- EnumDeclaration *ed = ((TypeEnum *)to)->sym;
- if (ed->isSpecial())
- {
- /* Special enums that allow implicit conversions to them. */
- tob = to->toBasetype()->isTypeBasic();
- if (tob)
- return implicitConvTo(tob);
- }
- else
- return MATCHnomatch;
- }
- else
- tob = to->isTypeBasic();
- if (!tob)
- return MATCHnomatch;
-
- if (flags & TFLAGSintegral)
- {
- // Disallow implicit conversion of integers to imaginary or complex
- if (tob->flags & (TFLAGSimaginary | TFLAGScomplex))
- return MATCHnomatch;
-
- // If converting from integral to integral
- if (tob->flags & TFLAGSintegral)
- { d_uns64 sz = size(Loc());
- d_uns64 tosz = tob->size(Loc());
-
- /* Can't convert to smaller size
- */
- if (sz > tosz)
- return MATCHnomatch;
-
- /* Can't change sign if same size
- */
- /*if (sz == tosz && (flags ^ tob->flags) & TFLAGSunsigned)
- return MATCHnomatch;*/
- }
- }
- else if (flags & TFLAGSfloating)
- {
- // Disallow implicit conversion of floating point to integer
- if (tob->flags & TFLAGSintegral)
- return MATCHnomatch;
-
- assert(tob->flags & TFLAGSfloating || to->ty == Tvector);
-
- // Disallow implicit conversion from complex to non-complex
- if (flags & TFLAGScomplex && !(tob->flags & TFLAGScomplex))
- return MATCHnomatch;
-
- // Disallow implicit conversion of real or imaginary to complex
- if (flags & (TFLAGSreal | TFLAGSimaginary) &&
- tob->flags & TFLAGScomplex)
- return MATCHnomatch;
-
- // Disallow implicit conversion to-from real and imaginary
- if ((flags & (TFLAGSreal | TFLAGSimaginary)) !=
- (tob->flags & (TFLAGSreal | TFLAGSimaginary)))
- return MATCHnomatch;
- }
- return MATCHconvert;
-}
-
-TypeBasic *TypeBasic::isTypeBasic()
-{
- return (TypeBasic *)this;
-}
-
-/* ============================= TypeVector =========================== */
-
-/* The basetype must be one of:
- * byte[16],ubyte[16],short[8],ushort[8],int[4],uint[4],long[2],ulong[2],float[4],double[2]
- * For AVX:
- * byte[32],ubyte[32],short[16],ushort[16],int[8],uint[8],long[4],ulong[4],float[8],double[4]
- */
-TypeVector::TypeVector(Type *basetype)
- : Type(Tvector)
-{
- this->basetype = basetype;
-}
-
-TypeVector *TypeVector::create(Type *basetype)
-{
- return new TypeVector(basetype);
-}
-
-const char *TypeVector::kind()
-{
- return "vector";
-}
-
-Type *TypeVector::syntaxCopy()
-{
- return new TypeVector(basetype->syntaxCopy());
-}
-
-TypeBasic *TypeVector::elementType()
-{
- assert(basetype->ty == Tsarray);
- TypeSArray *t = (TypeSArray *)basetype;
- TypeBasic *tb = t->nextOf()->isTypeBasic();
- assert(tb);
- return tb;
-}
-
-bool TypeVector::isBoolean()
-{
- return false;
-}
-
-d_uns64 TypeVector::size(Loc)
-{
- return basetype->size();
-}
-
-unsigned TypeVector::alignsize()
-{
- return (unsigned)basetype->size();
-}
-
-Expression *TypeVector::getProperty(Loc loc, Identifier *ident, int flag)
-{
- return Type::getProperty(loc, ident, flag);
-}
-
-Expression *TypeVector::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- if (ident == Id::ptr && e->op == TOKcall)
- {
- /* The trouble with TOKcall is the return ABI for float[4] is different from
- * __vector(float[4]), and a type paint won't do.
- */
- e = new AddrExp(e->loc, e);
- e = expressionSemantic(e, sc);
- e = e->castTo(sc, basetype->nextOf()->pointerTo());
- return e;
- }
- if (ident == Id::array)
- {
- //e = e->castTo(sc, basetype);
- // Keep lvalue-ness
- e = new VectorArrayExp(e->loc, e);
- e = expressionSemantic(e, sc);
- return e;
- }
- if (ident == Id::_init || ident == Id::offsetof || ident == Id::stringof || ident == Id::__xalignof)
- {
- // init should return a new VectorExp (Bugzilla 12776)
- // offsetof does not work on a cast expression, so use e directly
- // stringof should not add a cast to the output
- return Type::dotExp(sc, e, ident, flag);
- }
- return basetype->dotExp(sc, e->castTo(sc, basetype), ident, flag);
-}
-
-Expression *TypeVector::defaultInit(Loc loc)
-{
- //printf("TypeVector::defaultInit()\n");
- assert(basetype->ty == Tsarray);
- Expression *e = basetype->defaultInit(loc);
- VectorExp *ve = new VectorExp(loc, e, this);
- ve->type = this;
- ve->dim = (int)(basetype->size(loc) / elementType()->size(loc));
- return ve;
-}
-
-Expression *TypeVector::defaultInitLiteral(Loc loc)
-{
- //printf("TypeVector::defaultInitLiteral()\n");
- assert(basetype->ty == Tsarray);
- Expression *e = basetype->defaultInitLiteral(loc);
- VectorExp *ve = new VectorExp(loc, e, this);
- ve->type = this;
- ve->dim = (int)(basetype->size(loc) / elementType()->size(loc));
- return ve;
-}
-
-bool TypeVector::isZeroInit(Loc loc)
-{
- return basetype->isZeroInit(loc);
-}
-
-bool TypeVector::isintegral()
-{
- //printf("TypeVector::isintegral('%s') x%x\n", toChars(), flags);
- return basetype->nextOf()->isintegral();
-}
-
-bool TypeVector::isfloating()
-{
- return basetype->nextOf()->isfloating();
-}
-
-bool TypeVector::isunsigned()
-{
- return basetype->nextOf()->isunsigned();
-}
-
-bool TypeVector::isscalar()
-{
- return basetype->nextOf()->isscalar();
-}
-
-MATCH TypeVector::implicitConvTo(Type *to)
-{
- //printf("TypeVector::implicitConvTo(%s) from %s\n", to->toChars(), toChars());
- if (this == to)
- return MATCHexact;
-#ifdef IN_GCC
- if (to->ty == Tvector)
- {
- TypeVector *tv = (TypeVector *)to;
- assert(basetype->ty == Tsarray && tv->basetype->ty == Tsarray);
-
- // Can't convert to a vector which has different size.
- if (basetype->size() != tv->basetype->size())
- return MATCHnomatch;
-
- // Allow conversion to void[]
- if (tv->basetype->nextOf()->ty == Tvoid)
- return MATCHconvert;
-
- // Otherwise implicitly convertible only if basetypes are.
- return basetype->implicitConvTo(tv->basetype);
- }
-#else
- if (ty == to->ty)
- return MATCHconvert;
-#endif
- return MATCHnomatch;
-}
-
-/***************************** TypeArray *****************************/
-
-TypeArray::TypeArray(TY ty, Type *next)
- : TypeNext(ty, next)
-{
-}
-
-Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- e = Type::dotExp(sc, e, ident, flag);
-
- if (!(flag & 1) || e)
- e = expressionSemantic(e, sc);
- return e;
-}
-
-
-/***************************** TypeSArray *****************************/
-
-TypeSArray::TypeSArray(Type *t, Expression *dim)
- : TypeArray(Tsarray, t)
-{
- //printf("TypeSArray(%s)\n", dim->toChars());
- this->dim = dim;
-}
-
-const char *TypeSArray::kind()
-{
- return "sarray";
-}
-
-Type *TypeSArray::syntaxCopy()
-{
- Type *t = next->syntaxCopy();
- Expression *e = dim->syntaxCopy();
- t = new TypeSArray(t, e);
- t->mod = mod;
- return t;
-}
-
-d_uns64 TypeSArray::size(Loc loc)
-{
- //printf("TypeSArray::size()\n");
- uinteger_t n = numberOfElems(loc);
- uinteger_t elemsize = baseElemOf()->size();
- bool overflow = false;
- uinteger_t sz = mulu(n, elemsize, overflow);
- if (overflow || sz >= UINT32_MAX)
- {
- if (elemsize != SIZE_INVALID && n != UINT32_MAX)
- error(loc, "static array `%s` size overflowed to %lld", toChars(), (long long)sz);
- return SIZE_INVALID;
- }
- return sz;
-}
-
-unsigned TypeSArray::alignsize()
-{
- return next->alignsize();
-}
-
-void TypeSArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- //printf("TypeSArray::resolve() %s\n", toChars());
- next->resolve(loc, sc, pe, pt, ps, intypeid);
- //printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt);
- if (*pe)
- {
- // It's really an index expression
- if (Dsymbol *s = getDsymbol(*pe))
- *pe = new DsymbolExp(loc, s);
- *pe = new ArrayExp(loc, *pe, dim);
- }
- else if (*ps)
- {
- Dsymbol *s = *ps;
- TupleDeclaration *td = s->isTupleDeclaration();
- if (td)
- {
- ScopeDsymbol *sym = new ArrayScopeSymbol(sc, td);
- sym->parent = sc->scopesym;
- sc = sc->push(sym);
- sc = sc->startCTFE();
- dim = expressionSemantic(dim, sc);
- sc = sc->endCTFE();
- sc = sc->pop();
-
- dim = dim->ctfeInterpret();
- uinteger_t d = dim->toUInteger();
-
- if (d >= td->objects->length)
- {
- error(loc, "tuple index %llu exceeds length %u", d, td->objects->length);
- *ps = NULL;
- *pt = Type::terror;
- return;
- }
- RootObject *o = (*td->objects)[(size_t)d];
- if (o->dyncast() == DYNCAST_DSYMBOL)
- {
- *ps = (Dsymbol *)o;
- return;
- }
- if (o->dyncast() == DYNCAST_EXPRESSION)
- {
- Expression *e = (Expression *)o;
- if (e->op == TOKdsymbol)
- {
- *ps = ((DsymbolExp *)e)->s;
- *pe = NULL;
- }
- else
- {
- *ps = NULL;
- *pe = e;
- }
- return;
- }
- if (o->dyncast() == DYNCAST_TYPE)
- {
- *ps = NULL;
- *pt = ((Type *)o)->addMod(this->mod);
- return;
- }
-
- /* Create a new TupleDeclaration which
- * is a slice [d..d+1] out of the old one.
- * Do it this way because TemplateInstance::semanticTiargs()
- * can handle unresolved Objects this way.
- */
- Objects *objects = new Objects;
- objects->setDim(1);
- (*objects)[0] = o;
-
- TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects);
- *ps = tds;
- }
- else
- goto Ldefault;
- }
- else
- {
- if ((*pt)->ty != Terror)
- next = *pt; // prevent re-running semantic() on 'next'
- Ldefault:
- Type::resolve(loc, sc, pe, pt, ps, intypeid);
- }
-}
-
-Expression *TypeSArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- if (ident == Id::length)
- {
- Loc oldLoc = e->loc;
- e = dim->copy();
- e->loc = oldLoc;
- }
- else if (ident == Id::ptr)
- {
- if (e->op == TOKtype)
- {
- e->error("%s is not an expression", e->toChars());
- return new ErrorExp();
- }
- else if (!(flag & 2) && sc->func && !sc->intypeof && sc->func->setUnsafe())
- {
- e->deprecation("%s.ptr cannot be used in @safe code, use &%s[0] instead", e->toChars(), e->toChars());
- // return new ErrorExp();
- }
- e = e->castTo(sc, e->type->nextOf()->pointerTo());
- }
- else
- {
- e = TypeArray::dotExp(sc, e, ident, flag);
- }
- if (!(flag & 1) || e)
- e = expressionSemantic(e, sc);
- return e;
-}
-
-structalign_t TypeSArray::alignment()
-{
- return next->alignment();
-}
-
-bool TypeSArray::isString()
-{
- TY nty = next->toBasetype()->ty;
- return nty == Tchar || nty == Twchar || nty == Tdchar;
-}
-
-MATCH TypeSArray::constConv(Type *to)
-{
- if (to->ty == Tsarray)
- {
- TypeSArray *tsa = (TypeSArray *)to;
- if (!dim->equals(tsa->dim))
- return MATCHnomatch;
- }
- return TypeNext::constConv(to);
-}
-
-MATCH TypeSArray::implicitConvTo(Type *to)
-{
- //printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
-
- if (to->ty == Tarray)
- {
- TypeDArray *ta = (TypeDArray *)to;
-
- if (!MODimplicitConv(next->mod, ta->next->mod))
- return MATCHnomatch;
-
- /* Allow conversion to void[]
- */
- if (ta->next->ty == Tvoid)
- {
- return MATCHconvert;
- }
-
- MATCH m = next->constConv(ta->next);
- if (m > MATCHnomatch)
- {
- return MATCHconvert;
- }
- return MATCHnomatch;
- }
-
- if (to->ty == Tsarray)
- {
- if (this == to)
- return MATCHexact;
-
- TypeSArray *tsa = (TypeSArray *)to;
-
- if (dim->equals(tsa->dim))
- {
- /* Since static arrays are value types, allow
- * conversions from const elements to non-const
- * ones, just like we allow conversion from const int
- * to int.
- */
- MATCH m = next->implicitConvTo(tsa->next);
- if (m >= MATCHconst)
- {
- if (mod != to->mod)
- m = MATCHconst;
- return m;
- }
- }
- }
- return MATCHnomatch;
-}
-
-Expression *TypeSArray::defaultInit(Loc loc)
-{
- if (next->ty == Tvoid)
- return tuns8->defaultInit(loc);
- else
- return next->defaultInit(loc);
-}
-
-bool TypeSArray::isZeroInit(Loc loc)
-{
- return next->isZeroInit(loc);
-}
-
-bool TypeSArray::needsDestruction()
-{
- return next->needsDestruction();
-}
-
-/*********************************
- *
- */
-
-bool TypeSArray::needsNested()
-{
- return next->needsNested();
-}
-
-Expression *TypeSArray::defaultInitLiteral(Loc loc)
-{
- size_t d = (size_t)dim->toInteger();
- Expression *elementinit;
- if (next->ty == Tvoid)
- elementinit = tuns8->defaultInitLiteral(loc);
- else
- elementinit = next->defaultInitLiteral(loc);
- Expressions *elements = new Expressions();
- elements->setDim(d);
- for (size_t i = 0; i < d; i++)
- (*elements)[i] = NULL;
- ArrayLiteralExp *ae = new ArrayLiteralExp(Loc(), this, elementinit, elements);
- return ae;
-}
-
-bool TypeSArray::hasPointers()
-{
- /* Don't want to do this, because:
- * struct S { T* array[0]; }
- * may be a variable length struct.
- */
- //if (dim->toInteger() == 0)
- // return false;
-
- if (next->ty == Tvoid)
- {
- // Arrays of void contain arbitrary data, which may include pointers
- return true;
- }
- else
- return next->hasPointers();
-}
-
-/***************************** TypeDArray *****************************/
-
-TypeDArray::TypeDArray(Type *t)
- : TypeArray(Tarray, t)
-{
- //printf("TypeDArray(t = %p)\n", t);
-}
-
-const char *TypeDArray::kind()
-{
- return "darray";
-}
-
-Type *TypeDArray::syntaxCopy()
-{
- Type *t = next->syntaxCopy();
- if (t == next)
- t = this;
- else
- {
- t = new TypeDArray(t);
- t->mod = mod;
- }
- return t;
-}
-
-d_uns64 TypeDArray::size(Loc)
-{
- //printf("TypeDArray::size()\n");
- return target.ptrsize * 2;
-}
-
-unsigned TypeDArray::alignsize()
-{
- // A DArray consists of two ptr-sized values, so align it on pointer size
- // boundary
- return target.ptrsize;
-}
-
-void TypeDArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- //printf("TypeDArray::resolve() %s\n", toChars());
- next->resolve(loc, sc, pe, pt, ps, intypeid);
- //printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt);
- if (*pe)
- {
- // It's really a slice expression
- if (Dsymbol *s = getDsymbol(*pe))
- *pe = new DsymbolExp(loc, s);
- *pe = new ArrayExp(loc, *pe);
- }
- else if (*ps)
- {
- TupleDeclaration *td = (*ps)->isTupleDeclaration();
- if (td)
- ; // keep *ps
- else
- goto Ldefault;
- }
- else
- {
- if ((*pt)->ty != Terror)
- next = *pt; // prevent re-running semantic() on 'next'
- Ldefault:
- Type::resolve(loc, sc, pe, pt, ps, intypeid);
- }
-}
-
-Expression *TypeDArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- if (e->op == TOKtype &&
- (ident == Id::length || ident == Id::ptr))
- {
- e->error("%s is not an expression", e->toChars());
- return new ErrorExp();
- }
- if (ident == Id::length)
- {
- if (e->op == TOKstring)
- {
- StringExp *se = (StringExp *)e;
- return new IntegerExp(se->loc, se->len, Type::tsize_t);
- }
- if (e->op == TOKnull)
- return new IntegerExp(e->loc, 0, Type::tsize_t);
- if (checkNonAssignmentArrayOp(e))
- return new ErrorExp();
- e = new ArrayLengthExp(e->loc, e);
- e->type = Type::tsize_t;
- return e;
- }
- else if (ident == Id::ptr)
- {
- if (!(flag & 2) && sc->func && !sc->intypeof && sc->func->setUnsafe())
- {
- e->deprecation("%s.ptr cannot be used in @safe code, use &%s[0] instead", e->toChars(), e->toChars());
- // return new ErrorExp();
- }
- e = e->castTo(sc, next->pointerTo());
- return e;
- }
- else
- {
- e = TypeArray::dotExp(sc, e, ident, flag);
- }
- return e;
-}
-
-bool TypeDArray::isString()
-{
- TY nty = next->toBasetype()->ty;
- return nty == Tchar || nty == Twchar || nty == Tdchar;
-}
-
-MATCH TypeDArray::implicitConvTo(Type *to)
-{
- //printf("TypeDArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
- if (equals(to))
- return MATCHexact;
-
- if (to->ty == Tarray)
- {
- TypeDArray *ta = (TypeDArray *)to;
-
- if (!MODimplicitConv(next->mod, ta->next->mod))
- return MATCHnomatch; // not const-compatible
-
- /* Allow conversion to void[]
- */
- if (next->ty != Tvoid && ta->next->ty == Tvoid)
- {
- return MATCHconvert;
- }
-
- MATCH m = next->constConv(ta->next);
- if (m > MATCHnomatch)
- {
- if (m == MATCHexact && mod != to->mod)
- m = MATCHconst;
- return m;
- }
- }
- return Type::implicitConvTo(to);
-}
-
-Expression *TypeDArray::defaultInit(Loc loc)
-{
- return new NullExp(loc, this);
-}
-
-bool TypeDArray::isZeroInit(Loc)
-{
- return true;
-}
-
-bool TypeDArray::isBoolean()
-{
- return true;
-}
-
-bool TypeDArray::hasPointers()
-{
- return true;
-}
-
-
-/***************************** TypeAArray *****************************/
-
-TypeAArray::TypeAArray(Type *t, Type *index)
- : TypeArray(Taarray, t)
-{
- this->index = index;
- this->loc = Loc();
- this->sc = NULL;
-}
-
-TypeAArray *TypeAArray::create(Type *t, Type *index)
-{
- return new TypeAArray(t, index);
-}
-
-const char *TypeAArray::kind()
-{
- return "aarray";
-}
-
-Type *TypeAArray::syntaxCopy()
-{
- Type *t = next->syntaxCopy();
- Type *ti = index->syntaxCopy();
- if (t == next && ti == index)
- t = this;
- else
- {
- t = new TypeAArray(t, ti);
- t->mod = mod;
- }
- return t;
-}
-
-d_uns64 TypeAArray::size(Loc)
-{
- return target.ptrsize;
-}
-
-void TypeAArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- //printf("TypeAArray::resolve() %s\n", toChars());
-
- // Deal with the case where we thought the index was a type, but
- // in reality it was an expression.
- if (index->ty == Tident || index->ty == Tinstance || index->ty == Tsarray)
- {
- Expression *e;
- Type *t;
- Dsymbol *s;
-
- index->resolve(loc, sc, &e, &t, &s, intypeid);
- if (e)
- {
- // It was an expression -
- // Rewrite as a static array
- TypeSArray *tsa = new TypeSArray(next, e);
- tsa->mod = this->mod; // just copy mod field so tsa's semantic is not yet done
- return tsa->resolve(loc, sc, pe, pt, ps, intypeid);
- }
- else if (t)
- index = t;
- else
- index->error(loc, "index is not a type or an expression");
- }
- Type::resolve(loc, sc, pe, pt, ps, intypeid);
-}
-
-
-Expression *TypeAArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- if (ident == Id::length)
- {
- static FuncDeclaration *fd_aaLen = NULL;
- if (fd_aaLen == NULL)
- {
- Parameters *fparams = new Parameters();
- fparams->push(new Parameter(STCin, this, NULL, NULL, NULL));
- fd_aaLen = FuncDeclaration::genCfunc(fparams, Type::tsize_t, Id::aaLen);
- TypeFunction *tf = fd_aaLen->type->toTypeFunction();
- tf->purity = PUREconst;
- tf->isnothrow = true;
- tf->isnogc = false;
- }
- Expression *ev = new VarExp(e->loc, fd_aaLen, false);
- e = new CallExp(e->loc, ev, e);
- e->type = fd_aaLen->type->toTypeFunction()->next;
- }
- else
- e = Type::dotExp(sc, e, ident, flag);
- return e;
-}
-
-Expression *TypeAArray::defaultInit(Loc loc)
-{
- return new NullExp(loc, this);
-}
-
-bool TypeAArray::isZeroInit(Loc)
-{
- return true;
-}
-
-bool TypeAArray::isBoolean()
-{
- return true;
-}
-
-bool TypeAArray::hasPointers()
-{
- return true;
-}
-
-MATCH TypeAArray::implicitConvTo(Type *to)
-{
- //printf("TypeAArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
- if (equals(to))
- return MATCHexact;
-
- if (to->ty == Taarray)
- { TypeAArray *ta = (TypeAArray *)to;
-
- if (!MODimplicitConv(next->mod, ta->next->mod))
- return MATCHnomatch; // not const-compatible
-
- if (!MODimplicitConv(index->mod, ta->index->mod))
- return MATCHnomatch; // not const-compatible
-
- MATCH m = next->constConv(ta->next);
- MATCH mi = index->constConv(ta->index);
- if (m > MATCHnomatch && mi > MATCHnomatch)
- {
- return MODimplicitConv(mod, to->mod) ? MATCHconst : MATCHnomatch;
- }
- }
- return Type::implicitConvTo(to);
-}
-
-MATCH TypeAArray::constConv(Type *to)
-{
- if (to->ty == Taarray)
- {
- TypeAArray *taa = (TypeAArray *)to;
- MATCH mindex = index->constConv(taa->index);
- MATCH mkey = next->constConv(taa->next);
- // Pick the worst match
- return mkey < mindex ? mkey : mindex;
- }
- return Type::constConv(to);
-}
-
-/***************************** TypePointer *****************************/
-
-TypePointer::TypePointer(Type *t)
- : TypeNext(Tpointer, t)
-{
-}
-
-TypePointer *TypePointer::create(Type *t)
-{
- return new TypePointer(t);
-}
-
-const char *TypePointer::kind()
-{
- return "pointer";
-}
-
-Type *TypePointer::syntaxCopy()
-{
- Type *t = next->syntaxCopy();
- if (t == next)
- t = this;
- else
- {
- t = new TypePointer(t);
- t->mod = mod;
- }
- return t;
-}
-
-d_uns64 TypePointer::size(Loc)
-{
- return target.ptrsize;
-}
-
-MATCH TypePointer::implicitConvTo(Type *to)
-{
- //printf("TypePointer::implicitConvTo(to = %s) %s\n", to->toChars(), toChars());
-
- if (equals(to))
- return MATCHexact;
- if (next->ty == Tfunction)
- {
- if (to->ty == Tpointer)
- {
- TypePointer *tp = (TypePointer *)to;
- if (tp->next->ty == Tfunction)
- {
- if (next->equals(tp->next))
- return MATCHconst;
-
- if (next->covariant(tp->next) == 1)
- {
- Type *tret = this->next->nextOf();
- Type *toret = tp->next->nextOf();
- if (tret->ty == Tclass && toret->ty == Tclass)
- {
- /* Bugzilla 10219: Check covariant interface return with offset tweaking.
- * interface I {}
- * class C : Object, I {}
- * I function() dg = function C() {} // should be error
- */
- int offset = 0;
- if (toret->isBaseOf(tret, &offset) && offset != 0)
- return MATCHnomatch;
- }
- return MATCHconvert;
- }
- }
- else if (tp->next->ty == Tvoid)
- {
- // Allow conversions to void*
- return MATCHconvert;
- }
- }
- return MATCHnomatch;
- }
- else if (to->ty == Tpointer)
- {
- TypePointer *tp = (TypePointer *)to;
- assert(tp->next);
-
- if (!MODimplicitConv(next->mod, tp->next->mod))
- return MATCHnomatch; // not const-compatible
-
- /* Alloc conversion to void*
- */
- if (next->ty != Tvoid && tp->next->ty == Tvoid)
- {
- return MATCHconvert;
- }
-
- MATCH m = next->constConv(tp->next);
- if (m > MATCHnomatch)
- {
- if (m == MATCHexact && mod != to->mod)
- m = MATCHconst;
- return m;
- }
- }
- return MATCHnomatch;
-}
-
-MATCH TypePointer::constConv(Type *to)
-{
- if (next->ty == Tfunction)
- {
- if (to->nextOf() && next->equals(((TypeNext *)to)->next))
- return Type::constConv(to);
- else
- return MATCHnomatch;
- }
- return TypeNext::constConv(to);
-}
-
-bool TypePointer::isscalar()
-{
- return true;
-}
-
-Expression *TypePointer::defaultInit(Loc loc)
-{
- return new NullExp(loc, this);
-}
-
-bool TypePointer::isZeroInit(Loc)
-{
- return true;
-}
-
-bool TypePointer::hasPointers()
-{
- return true;
-}
-
-
-/***************************** TypeReference *****************************/
-
-TypeReference::TypeReference(Type *t)
- : TypeNext(Treference, t)
-{
- // BUG: what about references to static arrays?
-}
-
-const char *TypeReference::kind()
-{
- return "reference";
-}
-
-Type *TypeReference::syntaxCopy()
-{
- Type *t = next->syntaxCopy();
- if (t == next)
- t = this;
- else
- {
- t = new TypeReference(t);
- t->mod = mod;
- }
- return t;
-}
-
-d_uns64 TypeReference::size(Loc)
-{
- return target.ptrsize;
-}
-
-Expression *TypeReference::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- // References just forward things along
- return next->dotExp(sc, e, ident, flag);
-}
-
-Expression *TypeReference::defaultInit(Loc loc)
-{
- return new NullExp(loc, this);
-}
-
-bool TypeReference::isZeroInit(Loc)
-{
- return true;
-}
-
-
-/***************************** TypeFunction *****************************/
-
-TypeFunction::TypeFunction(const ParameterList &pl, Type *treturn, LINK linkage, StorageClass stc)
- : TypeNext(Tfunction, treturn)
-{
-//if (!treturn) *(char*)0=0;
-// assert(treturn);
- assert(VARARGnone <= pl.varargs && pl.varargs <= VARARGtypesafe);
- this->parameterList = pl;
- this->linkage = linkage;
- this->inuse = 0;
- this->isnothrow = false;
- this->isnogc = false;
- this->purity = PUREimpure;
- this->isproperty = false;
- this->isref = false;
- this->isreturn = false;
- this->isscope = false;
- this->isscopeinferred = false;
- this->iswild = 0;
- this->fargs = NULL;
-
- if (stc & STCpure)
- this->purity = PUREfwdref;
- if (stc & STCnothrow)
- this->isnothrow = true;
- if (stc & STCnogc)
- this->isnogc = true;
- if (stc & STCproperty)
- this->isproperty = true;
-
- if (stc & STCref)
- this->isref = true;
- if (stc & STCreturn)
- this->isreturn = true;
- if (stc & STCscope)
- this->isscope = true;
- if (stc & STCscopeinferred)
- this->isscopeinferred = true;
-
- this->trust = TRUSTdefault;
- if (stc & STCsafe)
- this->trust = TRUSTsafe;
- if (stc & STCsystem)
- this->trust = TRUSTsystem;
- if (stc & STCtrusted)
- this->trust = TRUSTtrusted;
-}
-
-TypeFunction *TypeFunction::create(Parameters *parameters, Type *treturn, VarArg varargs, LINK linkage, StorageClass stc)
-{
- return new TypeFunction(ParameterList(parameters, varargs), treturn, linkage, stc);
-}
-
-const char *TypeFunction::kind()
-{
- return "function";
-}
-
-Type *TypeFunction::syntaxCopy()
-{
- Type *treturn = next ? next->syntaxCopy() : NULL;
- Parameters *parameters = Parameter::arraySyntaxCopy(parameterList.parameters);
- TypeFunction *t = new TypeFunction(ParameterList(parameters, parameterList.varargs),
- treturn, linkage);
- t->mod = mod;
- t->isnothrow = isnothrow;
- t->isnogc = isnogc;
- t->purity = purity;
- t->isproperty = isproperty;
- t->isref = isref;
- t->isreturn = isreturn;
- t->isscope = isscope;
- t->isscopeinferred = isscopeinferred;
- t->iswild = iswild;
- t->trust = trust;
- t->fargs = fargs;
- return t;
-}
-
-/*******************************
- * Covariant means that 'this' can substitute for 't',
- * i.e. a pure function is a match for an impure type.
- * Params:
- * t = type 'this' is covariant with
- * pstc = if not null, store STCxxxx which would make it covariant
- * fix17349 = enable fix https://issues.dlang.org/show_bug.cgi?id=17349
- * Returns:
- * 0 types are distinct
- * 1 this is covariant with t
- * 2 arguments match as far as overloading goes,
- * but types are not covariant
- * 3 cannot determine covariance because of forward references
- * *pstc STCxxxx which would make it covariant
- */
-
-int Type::covariant(Type *t, StorageClass *pstc, bool fix17349)
-{
- if (pstc)
- *pstc = 0;
- StorageClass stc = 0;
-
- bool notcovariant = false;
-
- TypeFunction *t1;
- TypeFunction *t2;
-
- if (equals(t))
- return 1; // covariant
-
- if (ty != Tfunction || t->ty != Tfunction)
- goto Ldistinct;
-
- t1 = (TypeFunction *)this;
- t2 = (TypeFunction *)t;
-
- if (t1->parameterList.varargs != t2->parameterList.varargs)
- goto Ldistinct;
-
- if (t1->parameterList.parameters && t2->parameterList.parameters)
- {
- size_t dim = t1->parameterList.length();
- if (dim != t2->parameterList.length())
- goto Ldistinct;
-
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *fparam1 = t1->parameterList[i];
- Parameter *fparam2 = t2->parameterList[i];
-
- if (!fparam1->type->equals(fparam2->type))
- {
- if (!fix17349)
- goto Ldistinct;
- Type *tp1 = fparam1->type;
- Type *tp2 = fparam2->type;
- if (tp1->ty == tp2->ty)
- {
- if (tp1->ty == Tclass)
- {
- if (((TypeClass *)tp1)->sym == ((TypeClass *)tp2)->sym && MODimplicitConv(tp2->mod, tp1->mod))
- goto Lcov;
- }
- else if (tp1->ty == Tstruct)
- {
- if (((TypeStruct *)tp1)->sym == ((TypeStruct *)tp2)->sym && MODimplicitConv(tp2->mod, tp1->mod))
- goto Lcov;
- }
- else if (tp1->ty == Tpointer)
- {
- if (tp2->implicitConvTo(tp1))
- goto Lcov;
- }
- else if (tp1->ty == Tarray)
- {
- if (tp2->implicitConvTo(tp1))
- goto Lcov;
- }
- else if (tp1->ty == Tdelegate)
- {
- if (tp1->implicitConvTo(tp2))
- goto Lcov;
- }
- }
- goto Ldistinct;
- }
- Lcov:
- notcovariant |= !fparam1->isCovariant(t1->isref, fparam2);
- }
- }
- else if (t1->parameterList.parameters != t2->parameterList.parameters)
- {
- size_t dim1 = t1->parameterList.length();
- size_t dim2 = t2->parameterList.length();
- if (dim1 || dim2)
- goto Ldistinct;
- }
-
- // The argument lists match
- if (notcovariant)
- goto Lnotcovariant;
- if (t1->linkage != t2->linkage)
- goto Lnotcovariant;
-
- {
- // Return types
- Type *t1n = t1->next;
- Type *t2n = t2->next;
-
- if (!t1n || !t2n) // happens with return type inference
- goto Lnotcovariant;
-
- if (t1n->equals(t2n))
- goto Lcovariant;
- if (t1n->ty == Tclass && t2n->ty == Tclass)
- {
- /* If same class type, but t2n is const, then it's
- * covariant. Do this test first because it can work on
- * forward references.
- */
- if (((TypeClass *)t1n)->sym == ((TypeClass *)t2n)->sym &&
- MODimplicitConv(t1n->mod, t2n->mod))
- goto Lcovariant;
-
- // If t1n is forward referenced:
- ClassDeclaration *cd = ((TypeClass *)t1n)->sym;
- if (cd->semanticRun < PASSsemanticdone && !cd->isBaseInfoComplete())
- dsymbolSemantic(cd, NULL);
- if (!cd->isBaseInfoComplete())
- {
- return 3; // forward references
- }
- }
- if (t1n->ty == Tstruct && t2n->ty == Tstruct)
- {
- if (((TypeStruct *)t1n)->sym == ((TypeStruct *)t2n)->sym &&
- MODimplicitConv(t1n->mod, t2n->mod))
- goto Lcovariant;
- }
- else if (t1n->ty == t2n->ty && t1n->implicitConvTo(t2n))
- goto Lcovariant;
- else if (t1n->ty == Tnull)
- {
- // NULL is covariant with any pointer type, but not with any
- // dynamic arrays, associative arrays or delegates.
- // https://issues.dlang.org/show_bug.cgi?id=8589
- // https://issues.dlang.org/show_bug.cgi?id=19618
- Type *t2bn = t2n->toBasetype();
- if (t2bn->ty == Tnull || t2bn->ty == Tpointer || t2bn->ty == Tclass)
- goto Lcovariant;
- }
- }
- goto Lnotcovariant;
-
-Lcovariant:
- if (t1->isref != t2->isref)
- goto Lnotcovariant;
-
- if (!t1->isref && (t1->isscope || t2->isscope))
- {
- StorageClass stc1 = t1->isscope ? STCscope : 0;
- StorageClass stc2 = t2->isscope ? STCscope : 0;
- if (t1->isreturn)
- {
- stc1 |= STCreturn;
- if (!t1->isscope)
- stc1 |= STCref;
- }
- if (t2->isreturn)
- {
- stc2 |= STCreturn;
- if (!t2->isscope)
- stc2 |= STCref;
- }
- if (!Parameter::isCovariantScope(t1->isref, stc1, stc2))
- goto Lnotcovariant;
- }
-
- // We can subtract 'return ref' from 'this', but cannot add it
- else if (t1->isreturn && !t2->isreturn)
- goto Lnotcovariant;
-
- /* Can convert mutable to const
- */
- if (!MODimplicitConv(t2->mod, t1->mod))
- {
- goto Ldistinct;
- }
-
- /* Can convert pure to impure, nothrow to throw, and nogc to gc
- */
- if (!t1->purity && t2->purity)
- stc |= STCpure;
-
- if (!t1->isnothrow && t2->isnothrow)
- stc |= STCnothrow;
-
- if (!t1->isnogc && t2->isnogc)
- stc |= STCnogc;
-
- /* Can convert safe/trusted to system
- */
- if (t1->trust <= TRUSTsystem && t2->trust >= TRUSTtrusted)
- {
- // Should we infer trusted or safe? Go with safe.
- stc |= STCsafe;
- }
-
- if (stc)
- { if (pstc)
- *pstc = stc;
- goto Lnotcovariant;
- }
-
- //printf("\tcovaraint: 1\n");
- return 1;
-
-Ldistinct:
- //printf("\tcovaraint: 0\n");
- return 0;
-
-Lnotcovariant:
- //printf("\tcovaraint: 2\n");
- return 2;
-}
-
-bool TypeFunction::checkRetType(Loc loc)
-{
- Type *tb = next->toBasetype();
- if (tb->ty == Tfunction)
- {
- error(loc, "functions cannot return a function");
- next = Type::terror;
- }
- if (tb->ty == Ttuple)
- {
- error(loc, "functions cannot return a tuple");
- next = Type::terror;
- }
- if (!isref && (tb->ty == Tstruct || tb->ty == Tsarray))
- {
- Type *tb2 = tb->baseElemOf();
- if (tb2->ty == Tstruct && !((TypeStruct *)tb2)->sym->members)
- {
- error(loc, "functions cannot return opaque type %s by value", tb->toChars());
- next = Type::terror;
- }
- }
- if (tb->ty == Terror)
- return true;
-
- return false;
-}
-
-/* Determine purity level based on mutability of t
- * and whether it is a 'ref' type or not.
- */
-static PURE purityOfType(bool isref, Type *t)
-{
- if (isref)
- {
- if (t->mod & MODimmutable)
- return PUREstrong;
- if (t->mod & (MODconst | MODwild))
- return PUREconst;
- return PUREweak;
- }
-
- t = t->baseElemOf();
-
- if (!t->hasPointers() || t->mod & MODimmutable)
- return PUREstrong;
-
- /* Accept immutable(T)[] and immutable(T)* as being strongly pure
- */
- if (t->ty == Tarray || t->ty == Tpointer)
- {
- Type *tn = t->nextOf()->toBasetype();
- if (tn->mod & MODimmutable)
- return PUREstrong;
- if (tn->mod & (MODconst | MODwild))
- return PUREconst;
- }
-
- /* The rest of this is too strict; fix later.
- * For example, the only pointer members of a struct may be immutable,
- * which would maintain strong purity.
- * (Just like for dynamic arrays and pointers above.)
- */
- if (t->mod & (MODconst | MODwild))
- return PUREconst;
-
- /* Should catch delegates and function pointers, and fold in their purity
- */
- return PUREweak;
-}
-
-/********************************************
- * Set 'purity' field of 'this'.
- * Do this lazily, as the parameter types might be forward referenced.
- */
-void TypeFunction::purityLevel()
-{
- TypeFunction *tf = this;
- if (tf->purity != PUREfwdref)
- return;
-
- purity = PUREstrong; // assume strong until something weakens it
-
- /* Evaluate what kind of purity based on the modifiers for the parameters
- */
- const size_t dim = tf->parameterList.length();
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *fparam = tf->parameterList[i];
- Type *t = fparam->type;
- if (!t)
- continue;
-
- if (fparam->storageClass & (STClazy | STCout))
- {
- purity = PUREweak;
- break;
- }
- switch (purityOfType((fparam->storageClass & STCref) != 0, t))
- {
- case PUREweak:
- purity = PUREweak;
- break;
-
- case PUREconst:
- purity = PUREconst;
- continue;
-
- case PUREstrong:
- continue;
-
- default:
- assert(0);
- }
- break; // since PUREweak, no need to check further
- }
-
- if (purity > PUREweak && tf->nextOf())
- {
- /* Adjust purity based on mutability of return type.
- * https://issues.dlang.org/show_bug.cgi?id=15862
- */
- const PURE purity2 = purityOfType(tf->isref, tf->nextOf());
- if (purity2 < purity)
- purity = purity2;
- }
- tf->purity = purity;
-}
-
-// arguments get specially formatted
-static const char *getParamError(TypeFunction *tf, Expression *arg, Parameter *par)
-{
- if (global.gag && !global.params.showGaggedErrors)
- return NULL;
- // show qualification when toChars() is the same but types are different
- const char *at = arg->type->toChars();
- bool qual = !arg->type->equals(par->type) && strcmp(at, par->type->toChars()) == 0;
- if (qual)
- at = arg->type->toPrettyChars(true);
- OutBuffer buf;
- // only mention rvalue if it's relevant
- const bool rv = !arg->isLvalue() && (par->storageClass & (STCref | STCout)) != 0;
- buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`",
- rv ? "rvalue " : "", arg->toChars(), at,
- parameterToChars(par, tf, qual));
- return buf.extractChars();
-}
-
-static const char *getMatchError(const char *format, ...)
-{
- if (global.gag && !global.params.showGaggedErrors)
- return NULL;
- OutBuffer buf;
- va_list ap;
- va_start(ap, format);
- buf.vprintf(format, ap);
- va_end(ap);
- return buf.extractChars();
-}
-
-/********************************
- * 'args' are being matched to function 'this'
- * Determine match level.
- * Input:
- * flag 1 performing a partial ordering match
- * pMessage address to store error message, or null
- * Returns:
- * MATCHxxxx
- */
-
-MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag, const char **pMessage)
-{
- //printf("TypeFunction::callMatch() %s\n", toChars());
- MATCH match = MATCHexact; // assume exact match
- unsigned char wildmatch = 0;
-
- if (tthis)
- {
- Type *t = tthis;
- if (t->toBasetype()->ty == Tpointer)
- t = t->toBasetype()->nextOf(); // change struct* to struct
- if (t->mod != mod)
- {
- if (MODimplicitConv(t->mod, mod))
- match = MATCHconst;
- else if ((mod & MODwild) && MODimplicitConv(t->mod, (mod & ~MODwild) | MODconst))
- {
- match = MATCHconst;
- }
- else
- return MATCHnomatch;
- }
- if (isWild())
- {
- if (t->isWild())
- wildmatch |= MODwild;
- else if (t->isConst())
- wildmatch |= MODconst;
- else if (t->isImmutable())
- wildmatch |= MODimmutable;
- else
- wildmatch |= MODmutable;
- }
- }
-
- size_t nparams = parameterList.length();
- size_t nargs = args ? args->length : 0;
- if (nargs > nparams)
- {
- if (parameterList.varargs == VARARGnone)
- {
- // suppress early exit if an error message is wanted,
- // so we can check any matching args are valid
- if (!pMessage)
- goto Nomatch; // too many args; no match
- }
- match = MATCHconvert; // match ... with a "conversion" match level
- }
-
- for (size_t u = 0; u < nargs; u++)
- {
- if (u >= nparams)
- break;
- Parameter *p = parameterList[u];
- Expression *arg = (*args)[u];
- assert(arg);
- Type *tprm = p->type;
- Type *targ = arg->type;
-
- if (!(p->storageClass & STClazy && tprm->ty == Tvoid && targ->ty != Tvoid))
- {
- bool isRef = (p->storageClass & (STCref | STCout)) != 0;
- wildmatch |= targ->deduceWild(tprm, isRef);
- }
- }
- if (wildmatch)
- {
- /* Calculate wild matching modifier
- */
- if (wildmatch & MODconst || wildmatch & (wildmatch - 1))
- wildmatch = MODconst;
- else if (wildmatch & MODimmutable)
- wildmatch = MODimmutable;
- else if (wildmatch & MODwild)
- wildmatch = MODwild;
- else
- {
- assert(wildmatch & MODmutable);
- wildmatch = MODmutable;
- }
- }
-
- for (size_t u = 0; u < nparams; u++)
- {
- MATCH m;
-
- Parameter *p = parameterList[u];
- assert(p);
- if (u >= nargs)
- {
- if (p->defaultArg)
- continue;
- goto L1; // try typesafe variadics
- }
- {
- Expression *arg = (*args)[u];
- assert(arg);
- //printf("arg: %s, type: %s\n", arg->toChars(), arg->type->toChars());
-
- Type *targ = arg->type;
- Type *tprm = wildmatch ? p->type->substWildTo(wildmatch) : p->type;
-
- if (p->storageClass & STClazy && tprm->ty == Tvoid && targ->ty != Tvoid)
- m = MATCHconvert;
- else
- {
- //printf("%s of type %s implicitConvTo %s\n", arg->toChars(), targ->toChars(), tprm->toChars());
- if (flag)
- {
- // for partial ordering, value is an irrelevant mockup, just look at the type
- m = targ->implicitConvTo(tprm);
- }
- else
- m = arg->implicitConvTo(tprm);
- //printf("match %d\n", m);
- }
-
- // Non-lvalues do not match ref or out parameters
- if (p->storageClass & (STCref | STCout))
- {
- // Bugzilla 13783: Don't use toBasetype() to handle enum types.
- Type *ta = targ;
- Type *tp = tprm;
- //printf("fparam[%d] ta = %s, tp = %s\n", u, ta->toChars(), tp->toChars());
-
- if (m && !arg->isLvalue())
- {
- if (p->storageClass & STCout)
- {
- if (pMessage) *pMessage = getParamError(this, arg, p);
- goto Nomatch;
- }
-
- if (arg->op == TOKstring && tp->ty == Tsarray)
- {
- if (ta->ty != Tsarray)
- {
- Type *tn = tp->nextOf()->castMod(ta->nextOf()->mod);
- dinteger_t dim = ((StringExp *)arg)->len;
- ta = tn->sarrayOf(dim);
- }
- }
- else if (arg->op == TOKslice && tp->ty == Tsarray)
- {
- // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
- if (ta->ty != Tsarray)
- {
- Type *tn = ta->nextOf();
- dinteger_t dim = ((TypeSArray *)tp)->dim->toUInteger();
- ta = tn->sarrayOf(dim);
- }
- }
- else
- {
- if (pMessage) *pMessage = getParamError(this, arg, p);
- goto Nomatch;
- }
- }
-
- /* Find most derived alias this type being matched.
- * Bugzilla 15674: Allow on both ref and out parameters.
- */
- while (1)
- {
- Type *tat = ta->toBasetype()->aliasthisOf();
- if (!tat || !tat->implicitConvTo(tprm))
- break;
- ta = tat;
- }
-
- /* A ref variable should work like a head-const reference.
- * e.g. disallows:
- * ref T <- an lvalue of const(T) argument
- * ref T[dim] <- an lvalue of const(T[dim]) argument
- */
- if (!ta->constConv(tp))
- {
- if (pMessage) *pMessage = getParamError(this, arg, p);
- goto Nomatch;
- }
- }
- }
-
- /* prefer matching the element type rather than the array
- * type when more arguments are present with T[]...
- */
- if (parameterList.varargs == VARARGtypesafe && u + 1 == nparams && nargs > nparams)
- goto L1;
-
- //printf("\tm = %d\n", m);
- if (m == MATCHnomatch) // if no match
- {
- L1:
- if (parameterList.varargs == VARARGtypesafe && u + 1 == nparams) // if last varargs param
- {
- Type *tb = p->type->toBasetype();
- TypeSArray *tsa;
- dinteger_t sz;
-
- switch (tb->ty)
- {
- case Tsarray:
- tsa = (TypeSArray *)tb;
- sz = tsa->dim->toInteger();
- if (sz != nargs - u)
- {
- if (pMessage)
- *pMessage = getMatchError("expected %llu variadic argument(s), not %zu", sz, nargs - u);
- goto Nomatch;
- }
- /* fall through */
- case Tarray:
- {
- TypeArray *ta = (TypeArray *)tb;
- for (; u < nargs; u++)
- {
- Expression *arg = (*args)[u];
- assert(arg);
-
- /* If lazy array of delegates,
- * convert arg(s) to delegate(s)
- */
- Type *tret = p->isLazyArray();
- if (tret)
- {
- if (ta->next->equals(arg->type))
- m = MATCHexact;
- else if (tret->toBasetype()->ty == Tvoid)
- m = MATCHconvert;
- else
- {
- m = arg->implicitConvTo(tret);
- if (m == MATCHnomatch)
- m = arg->implicitConvTo(ta->next);
- }
- }
- else
- m = arg->implicitConvTo(ta->next);
-
- if (m == MATCHnomatch)
- {
- if (pMessage) *pMessage = getParamError(this, arg, p);
- goto Nomatch;
- }
- if (m < match)
- match = m;
- }
- goto Ldone;
- }
- case Tclass:
- // Should see if there's a constructor match?
- // Or just leave it ambiguous?
- goto Ldone;
-
- default:
- break;
- }
- }
- if (pMessage && u < nargs)
- *pMessage = getParamError(this, (*args)[u], p);
- else if (pMessage)
- *pMessage = getMatchError("missing argument for parameter #%d: `%s`",
- u + 1, parameterToChars(p, this, false));
- goto Nomatch;
- }
- if (m < match)
- match = m; // pick worst match
- }
-
-Ldone:
- if (pMessage && !parameterList.varargs && nargs > nparams)
- {
- // all parameters had a match, but there are surplus args
- *pMessage = getMatchError("expected %d argument(s), not %d", nparams, nargs);
- goto Nomatch;
- }
- //printf("match = %d\n", match);
- return match;
-
-Nomatch:
- //printf("no match\n");
- return MATCHnomatch;
-}
-
-/********************************************
- * Return true if there are lazy parameters.
- */
-bool TypeFunction::hasLazyParameters()
-{
- size_t dim = parameterList.length();
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *fparam = parameterList[i];
- if (fparam->storageClass & STClazy)
- return true;
- }
- return false;
-}
-
-/*******************************
- * Check for `extern (D) U func(T t, ...)` variadic function type,
- * which has `_arguments[]` added as the first argument.
- * Returns:
- * true if D-style variadic
- */
-bool TypeFunction::isDstyleVariadic() const
-{
- return linkage == LINKd && parameterList.varargs == VARARGvariadic;
-}
-
-/***************************
- * Examine function signature for parameter p and see if
- * the value of p can 'escape' the scope of the function.
- * This is useful to minimize the needed annotations for the parameters.
- * Params:
- * p = parameter to this function
- * Returns:
- * true if escapes via assignment to global or through a parameter
- */
-
-bool TypeFunction::parameterEscapes(Parameter *p)
-{
- /* Scope parameters do not escape.
- * Allow 'lazy' to imply 'scope' -
- * lazy parameters can be passed along
- * as lazy parameters to the next function, but that isn't
- * escaping.
- */
- if (parameterStorageClass(p) & (STCscope | STClazy))
- return false;
- return true;
-}
-
-/************************************
- * Take the specified storage class for p,
- * and use the function signature to infer whether
- * STCscope and STCreturn should be OR'd in.
- * (This will not affect the name mangling.)
- * Params:
- * p = one of the parameters to 'this'
- * Returns:
- * storage class with STCscope or STCreturn OR'd in
- */
-StorageClass TypeFunction::parameterStorageClass(Parameter *p)
-{
- StorageClass stc = p->storageClass;
- if (!global.params.vsafe)
- return stc;
-
- if (stc & (STCscope | STCreturn | STClazy) || purity == PUREimpure)
- return stc;
-
- /* If haven't inferred the return type yet, can't infer storage classes
- */
- if (!nextOf())
- return stc;
-
- purityLevel();
-
- // See if p can escape via any of the other parameters
- if (purity == PUREweak)
- {
- const size_t dim = parameterList.length();
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *fparam = parameterList[i];
- Type *t = fparam->type;
- if (!t)
- continue;
- t = t->baseElemOf();
- if (t->isMutable() && t->hasPointers())
- {
- if (fparam->storageClass & (STCref | STCout))
- {
- }
- else if (t->ty == Tarray || t->ty == Tpointer)
- {
- Type *tn = t->nextOf()->toBasetype();
- if (!(tn->isMutable() && tn->hasPointers()))
- continue;
- }
- return stc;
- }
- }
- }
-
- stc |= STCscope;
-
- /* Inferring STCreturn here has false positives
- * for pure functions, producing spurious error messages
- * about escaping references.
- * Give up on it for now.
- */
- return stc;
-}
-
-Expression *TypeFunction::defaultInit(Loc loc)
-{
- error(loc, "function does not have a default initializer");
- return new ErrorExp();
-}
-
-Type *TypeFunction::addStorageClass(StorageClass stc)
-{
- //printf("addStorageClass(%llx) %d\n", stc, (stc & STCscope) != 0);
- TypeFunction *t = Type::addStorageClass(stc)->toTypeFunction();
- if ((stc & STCpure && !t->purity) ||
- (stc & STCnothrow && !t->isnothrow) ||
- (stc & STCnogc && !t->isnogc) ||
- (stc & STCscope && !t->isscope) ||
- (stc & STCsafe && t->trust < TRUSTtrusted))
- {
- // Klunky to change these
- TypeFunction *tf = new TypeFunction(t->parameterList, t->next, t->linkage, 0);
- tf->mod = t->mod;
- tf->fargs = fargs;
- tf->purity = t->purity;
- tf->isnothrow = t->isnothrow;
- tf->isnogc = t->isnogc;
- tf->isproperty = t->isproperty;
- tf->isref = t->isref;
- tf->isreturn = t->isreturn;
- tf->isscope = t->isscope;
- tf->isscopeinferred = t->isscopeinferred;
- tf->trust = t->trust;
- tf->iswild = t->iswild;
-
- if (stc & STCpure)
- tf->purity = PUREfwdref;
- if (stc & STCnothrow)
- tf->isnothrow = true;
- if (stc & STCnogc)
- tf->isnogc = true;
- if (stc & STCsafe)
- tf->trust = TRUSTsafe;
- if (stc & STCscope)
- {
- tf->isscope = true;
- if (stc & STCscopeinferred)
- tf->isscopeinferred = true;
- }
-
- tf->deco = tf->merge()->deco;
- t = tf;
- }
- return t;
-}
-
-/** For each active attribute (ref/const/nogc/etc) call fp with a void* for the
-work param and a string representation of the attribute. */
-int TypeFunction::attributesApply(void *param, int (*fp)(void *, const char *), TRUSTformat trustFormat)
-{
- int res = 0;
-
- if (purity) res = fp(param, "pure");
- if (res) return res;
-
- if (isnothrow) res = fp(param, "nothrow");
- if (res) return res;
-
- if (isnogc) res = fp(param, "@nogc");
- if (res) return res;
-
- if (isproperty) res = fp(param, "@property");
- if (res) return res;
-
- if (isref) res = fp(param, "ref");
- if (res) return res;
-
- if (isreturn) res = fp(param, "return");
- if (res) return res;
-
- if (isscope && !isscopeinferred) res = fp(param, "scope");
- if (res) return res;
-
- TRUST trustAttrib = trust;
-
- if (trustAttrib == TRUSTdefault)
- {
- // Print out "@system" when trust equals TRUSTdefault (if desired).
- if (trustFormat == TRUSTformatSystem)
- trustAttrib = TRUSTsystem;
- else
- return res; // avoid calling with an empty string
- }
-
- return fp(param, trustToChars(trustAttrib));
-}
-
-/***************************** TypeDelegate *****************************/
-
-TypeDelegate::TypeDelegate(Type *t)
- : TypeNext(Tfunction, t)
-{
- ty = Tdelegate;
-}
-
-TypeDelegate *TypeDelegate::create(Type *t)
-{
- return new TypeDelegate(t);
-}
-
-const char *TypeDelegate::kind()
-{
- return "delegate";
-}
-
-Type *TypeDelegate::syntaxCopy()
-{
- Type *t = next->syntaxCopy();
- if (t == next)
- t = this;
- else
- {
- t = new TypeDelegate(t);
- t->mod = mod;
- }
- return t;
-}
-
-Type *TypeDelegate::addStorageClass(StorageClass stc)
-{
- TypeDelegate *t = (TypeDelegate*)Type::addStorageClass(stc);
- if (!global.params.vsafe)
- return t;
-
- /* The rest is meant to add 'scope' to a delegate declaration if it is of the form:
- * alias dg_t = void* delegate();
- * scope dg_t dg = ...;
- */
- if (stc & STCscope)
- {
- Type *n = t->next->addStorageClass(STCscope | STCscopeinferred);
- if (n != t->next)
- {
- t->next = n;
- t->deco = t->merge()->deco; // mangling supposed to not be changed due to STCscopeinferrred
- }
- }
- return t;
-}
-
-d_uns64 TypeDelegate::size(Loc)
-{
- return target.ptrsize * 2;
-}
-
-unsigned TypeDelegate::alignsize()
-{
- return target.ptrsize;
-}
-
-MATCH TypeDelegate::implicitConvTo(Type *to)
-{
- //printf("TypeDelegate::implicitConvTo(this=%p, to=%p)\n", this, to);
- //printf("from: %s\n", toChars());
- //printf("to : %s\n", to->toChars());
- if (this == to)
- return MATCHexact;
-#if 1 // not allowing covariant conversions because it interferes with overriding
- if (to->ty == Tdelegate && this->nextOf()->covariant(to->nextOf()) == 1)
- {
- Type *tret = this->next->nextOf();
- Type *toret = ((TypeDelegate *)to)->next->nextOf();
- if (tret->ty == Tclass && toret->ty == Tclass)
- {
- /* Bugzilla 10219: Check covariant interface return with offset tweaking.
- * interface I {}
- * class C : Object, I {}
- * I delegate() dg = delegate C() {} // should be error
- */
- int offset = 0;
- if (toret->isBaseOf(tret, &offset) && offset != 0)
- return MATCHnomatch;
- }
- return MATCHconvert;
- }
-#endif
- return MATCHnomatch;
-}
-
-Expression *TypeDelegate::defaultInit(Loc loc)
-{
- return new NullExp(loc, this);
-}
-
-bool TypeDelegate::isZeroInit(Loc)
-{
- return true;
-}
-
-bool TypeDelegate::isBoolean()
-{
- return true;
-}
-
-Expression *TypeDelegate::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- if (ident == Id::ptr)
- {
- e = new DelegatePtrExp(e->loc, e);
- e = expressionSemantic(e, sc);
- }
- else if (ident == Id::funcptr)
- {
- if (!(flag & 2) && sc->func && !sc->intypeof && sc->func->setUnsafe())
- {
- e->error("%s.funcptr cannot be used in @safe code", e->toChars());
- return new ErrorExp();
- }
- e = new DelegateFuncptrExp(e->loc, e);
- e = expressionSemantic(e, sc);
- }
- else
- {
- e = Type::dotExp(sc, e, ident, flag);
- }
- return e;
-}
-
-bool TypeDelegate::hasPointers()
-{
- return true;
-}
-
-/***************************** TypeTraits ********************************/
-
-TypeTraits::TypeTraits(const Loc &loc, TraitsExp *exp)
- : Type(Ttraits)
-{
- this->loc = loc;
- this->exp = exp;
- this->sym = NULL;
-}
-
-Type *TypeTraits::syntaxCopy()
-{
- TraitsExp *te = (TraitsExp *) exp->syntaxCopy();
- TypeTraits *tt = new TypeTraits(loc, te);
- tt->mod = mod;
- return tt;
-}
-
-Dsymbol *TypeTraits::toDsymbol(Scope *sc)
-{
- Type *t = NULL;
- Expression *e = NULL;
- Dsymbol *s = NULL;
- resolve(loc, sc, &e, &t, &s);
- if (t && t->ty != Terror)
- s = t->toDsymbol(sc);
- else if (e)
- s = getDsymbol(e);
-
- return s;
-}
-
-void TypeTraits::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool)
-{
- *pt = NULL;
- *pe = NULL;
- *ps = NULL;
-
- if (Type *t = typeSemantic(this, loc, sc))
- *pt = t;
- else if (sym)
- *ps = sym;
- else
- *pt = Type::terror;
-}
-
-d_uns64 TypeTraits::size(Loc)
-{
- return SIZE_INVALID;
-}
-
-/***************************** TypeMixin *****************************/
-
-/******
- * Implements mixin types.
- *
- * Semantic analysis will convert it to a real type.
- */
-TypeMixin::TypeMixin(const Loc &loc, Expressions *exps)
- : Type(Tmixin)
-{
- this->loc = loc;
- this->exps = exps;
- this->obj = NULL; // cached result of semantic analysis.
-}
-
-const char *TypeMixin::kind()
-{
- return "mixin";
-}
-
-Type *TypeMixin::syntaxCopy()
-{
- return new TypeMixin(loc, Expression::arraySyntaxCopy(exps));
-}
-
-Dsymbol *TypeMixin::toDsymbol(Scope *sc)
-{
- Type *t = NULL;
- Expression *e = NULL;
- Dsymbol *s = NULL;
- resolve(loc, sc, &e, &t, &s);
- if (t)
- s = t->toDsymbol(sc);
- else if (e)
- s = getDsymbol(e);
-
- return s;
-}
-
-void TypeMixin::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- // if already resolved just set pe/pt/ps and return.
- if (obj)
- {
- *pe = isExpression(obj);
- *pt = isType(obj);
- *ps = isDsymbol(obj);
- return;
- }
-
- RootObject *o = compileTypeMixin(this, loc, sc);
- if (Type *t = isType(o))
- {
- t->resolve(loc, sc, pe, pt, ps, intypeid);
- if (*pt)
- (*pt) = (*pt)->addMod(mod);
- }
- else if (Expression *e = isExpression(o))
- {
- e = expressionSemantic(e, sc);
- if (TypeExp *et = e->isTypeExp())
- {
- *pe = NULL;
- *pt = et->type->addMod(mod);
- *ps = NULL;
- }
- else
- {
- *pe = e;
- *pt = NULL;
- *ps = NULL;
- }
- }
- else
- {
- *pe = NULL;
- *pt = Type::terror;
- *ps = NULL;
- }
-
- // save the result
- obj = *pe ? (RootObject *)*pe : (*pt ? (RootObject *)*pt : (RootObject *)*ps);
-}
-
-/***************************** TypeQualified *****************************/
-
-TypeQualified::TypeQualified(TY ty, Loc loc)
- : Type(ty)
-{
- this->loc = loc;
-}
-
-void TypeQualified::syntaxCopyHelper(TypeQualified *t)
-{
- //printf("TypeQualified::syntaxCopyHelper(%s) %s\n", t->toChars(), toChars());
- idents.setDim(t->idents.length);
- for (size_t i = 0; i < idents.length; i++)
- {
- RootObject *id = t->idents[i];
- if (id->dyncast() == DYNCAST_DSYMBOL)
- {
- TemplateInstance *ti = (TemplateInstance *)id;
-
- ti = (TemplateInstance *)ti->syntaxCopy(NULL);
- id = ti;
- }
- else if (id->dyncast() == DYNCAST_EXPRESSION)
- {
- Expression *e = (Expression *)id;
- e = e->syntaxCopy();
- id = e;
- }
- else if (id->dyncast() == DYNCAST_TYPE)
- {
- Type *tx = (Type *)id;
- tx = tx->syntaxCopy();
- id = tx;
- }
- idents[i] = id;
- }
-}
-
-void TypeQualified::addIdent(Identifier *ident)
-{
- idents.push(ident);
-}
-
-void TypeQualified::addInst(TemplateInstance *inst)
-{
- idents.push(inst);
-}
-
-void TypeQualified::addIndex(RootObject *e)
-{
- idents.push(e);
-}
-
-d_uns64 TypeQualified::size(Loc)
-{
- error(this->loc, "size of type %s is not known", toChars());
- return SIZE_INVALID;
-}
-
-/*************************************
- * Resolve a tuple index.
- */
-void TypeQualified::resolveTupleIndex(Loc loc, Scope *sc, Dsymbol *s,
- Expression **pe, Type **pt, Dsymbol **ps, RootObject *oindex)
-{
- *pt = NULL;
- *ps = NULL;
- *pe = NULL;
-
- TupleDeclaration *td = s->isTupleDeclaration();
-
- Expression *eindex = isExpression(oindex);
- Type *tindex = isType(oindex);
- Dsymbol *sindex = isDsymbol(oindex);
-
- if (!td)
- {
- // It's really an index expression
- if (tindex)
- eindex = new TypeExp(loc, tindex);
- else if (sindex)
- eindex = ::resolve(loc, sc, sindex, false);
- Expression *e = new IndexExp(loc, ::resolve(loc, sc, s, false), eindex);
- e = expressionSemantic(e, sc);
- resolveExp(e, pt, pe, ps);
- return;
- }
-
- // Convert oindex to Expression, then try to resolve to constant.
- if (tindex)
- tindex->resolve(loc, sc, &eindex, &tindex, &sindex);
- if (sindex)
- eindex = ::resolve(loc, sc, sindex, false);
- if (!eindex)
- {
- ::error(loc, "index is %s not an expression", oindex->toChars());
- *pt = Type::terror;
- return;
- }
- sc = sc->startCTFE();
- eindex = expressionSemantic(eindex, sc);
- sc = sc->endCTFE();
-
- eindex = eindex->ctfeInterpret();
- if (eindex->op == TOKerror)
- {
- *pt = Type::terror;
- return;
- }
-
- const uinteger_t d = eindex->toUInteger();
- if (d >= td->objects->length)
- {
- ::error(loc, "tuple index %llu exceeds length %u", (ulonglong)d, (unsigned)td->objects->length);
- *pt = Type::terror;
- return;
- }
-
- RootObject *o = (*td->objects)[(size_t)d];
- *pt = isType(o);
- *ps = isDsymbol(o);
- *pe = isExpression(o);
-
- if (*pt)
- *pt = typeSemantic(*pt, loc, sc);
- if (*pe)
- resolveExp(*pe, pt, pe, ps);
-}
-
-/*************************************
- * Takes an array of Identifiers and figures out if
- * it represents a Type or an Expression.
- * Output:
- * if expression, *pe is set
- * if type, *pt is set
- */
-void TypeQualified::resolveHelper(Loc loc, Scope *sc,
- Dsymbol *s, Dsymbol *,
- Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- *pe = NULL;
- *pt = NULL;
- *ps = NULL;
- if (s)
- {
- //printf("\t1: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
- Declaration *d = s->isDeclaration();
- if (d && (d->storage_class & STCtemplateparameter))
- s = s->toAlias();
- else
- {
- // check for deprecated aliases
- s->checkDeprecated(loc, sc);
- if (d)
- d->checkDisabled(loc, sc, true);
- }
-
- s = s->toAlias();
- //printf("\t2: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
- for (size_t i = 0; i < idents.length; i++)
- {
- RootObject *id = idents[i];
-
- if (id->dyncast() == DYNCAST_EXPRESSION ||
- id->dyncast() == DYNCAST_TYPE)
- {
- Type *tx;
- Expression *ex;
- Dsymbol *sx;
- resolveTupleIndex(loc, sc, s, &ex, &tx, &sx, id);
- if (sx)
- {
- s = sx->toAlias();
- continue;
- }
- if (tx)
- ex = new TypeExp(loc, tx);
- assert(ex);
-
- ex = typeToExpressionHelper(this, ex, i + 1);
- ex = expressionSemantic(ex, sc);
- resolveExp(ex, pt, pe, ps);
- return;
- }
-
- Type *t = s->getType(); // type symbol, type alias, or type tuple?
- unsigned errorsave = global.errors;
- int flags = t == NULL ? SearchLocalsOnly : IgnorePrivateImports;
- Dsymbol *sm = s->searchX(loc, sc, id, flags);
- if (sm)
- {
- if (!(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, sm))
- {
- ::error(loc, "`%s` is not visible from module `%s`", sm->toPrettyChars(), sc->_module->toChars());
- sm = NULL;
- }
- // Same check as in Expression::semanticY(DotIdExp)
- else if (sm->isPackage() && checkAccess(sc, (Package *)sm))
- {
- // @@@DEPRECATED_2.096@@@
- // Should be an error in 2.106. Just remove the deprecation call
- // and uncomment the null assignment
- ::deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'",
- sm->kind(), sm->toPrettyChars(), sm->toPrettyChars());
- //sm = null;
- }
- }
- if (global.errors != errorsave)
- {
- *pt = Type::terror;
- return;
- }
- //printf("\t3: s = %p %s %s, sm = %p\n", s, s->kind(), s->toChars(), sm);
- if (intypeid && !t && sm && sm->needThis())
- goto L3;
- if (VarDeclaration *v = s->isVarDeclaration())
- {
- // https://issues.dlang.org/show_bug.cgi?id=19913
- // v->type would be null if it is a forward referenced member.
- if (v->type == NULL)
- dsymbolSemantic(v, sc);
- if (v->storage_class & (STCconst | STCimmutable | STCmanifest) ||
- v->type->isConst() || v->type->isImmutable())
- {
- // Bugzilla 13087: this.field is not constant always
- if (!v->isThisDeclaration())
- goto L3;
- }
- }
- if (!sm)
- {
- if (!t)
- {
- if (s->isDeclaration()) // var, func, or tuple declaration?
- {
- t = s->isDeclaration()->type;
- if (!t && s->isTupleDeclaration()) // expression tuple?
- goto L3;
- }
- else if (s->isTemplateInstance() ||
- s->isImport() || s->isPackage() || s->isModule())
- {
- goto L3;
- }
- }
- if (t)
- {
- sm = t->toDsymbol(sc);
- if (sm && id->dyncast() == DYNCAST_IDENTIFIER)
- {
- sm = sm->search(loc, (Identifier *)id, IgnorePrivateImports);
- if (sm)
- goto L2;
- }
- L3:
- Expression *e;
- VarDeclaration *v = s->isVarDeclaration();
- FuncDeclaration *f = s->isFuncDeclaration();
- if (intypeid || (!v && !f))
- e = ::resolve(loc, sc, s, true);
- else
- e = new VarExp(loc, s->isDeclaration(), true);
-
- e = typeToExpressionHelper(this, e, i);
- e = expressionSemantic(e, sc);
- resolveExp(e, pt, pe, ps);
- return;
- }
- else
- {
- if (id->dyncast() == DYNCAST_DSYMBOL)
- {
- // searchX already handles errors for template instances
- assert(global.errors);
- }
- else
- {
- assert(id->dyncast() == DYNCAST_IDENTIFIER);
- sm = s->search_correct((Identifier *)id);
- if (sm)
- error(loc, "identifier `%s` of `%s` is not defined, did you mean %s `%s`?",
- id->toChars(), toChars(), sm->kind(), sm->toChars());
- else
- error(loc, "identifier `%s` of `%s` is not defined", id->toChars(), toChars());
- }
- *pe = new ErrorExp();
- }
- return;
- }
- L2:
- s = sm->toAlias();
- }
-
- if (EnumMember *em = s->isEnumMember())
- {
- // It's not a type, it's an expression
- *pe = em->getVarExp(loc, sc);
- return;
- }
- if (VarDeclaration *v = s->isVarDeclaration())
- {
- /* This is mostly same with DsymbolExp::semantic(), but we cannot use it
- * because some variables used in type context need to prevent lowering
- * to a literal or contextful expression. For example:
- *
- * enum a = 1; alias b = a;
- * template X(alias e){ alias v = e; } alias x = X!(1);
- * struct S { int v; alias w = v; }
- * // TypeIdentifier 'a', 'e', and 'v' should be TOKvar,
- * // because getDsymbol() need to work in AliasDeclaration::semantic().
- */
- if (!v->type ||
- (!v->type->deco && v->inuse))
- {
- if (v->inuse) // Bugzilla 9494
- error(loc, "circular reference to %s `%s`", v->kind(), v->toPrettyChars());
- else
- error(loc, "forward reference to %s `%s`", v->kind(), v->toPrettyChars());
- *pt = Type::terror;
- return;
- }
- if (v->type->ty == Terror)
- *pt = Type::terror;
- else
- *pe = new VarExp(loc, v);
- return;
- }
- if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
- {
- //printf("'%s' is a function literal\n", fld->toChars());
- *pe = new FuncExp(loc, fld);
- *pe = expressionSemantic(*pe, sc);
- return;
- }
-L1:
- Type *t = s->getType();
- if (!t)
- {
- // If the symbol is an import, try looking inside the import
- if (Import *si = s->isImport())
- {
- s = si->search(loc, s->ident);
- if (s && s != si)
- goto L1;
- s = si;
- }
- *ps = s;
- return;
- }
- if (t->ty == Tinstance && t != this && !t->deco)
- {
- if (!((TypeInstance *)t)->tempinst->errors)
- error(loc, "forward reference to `%s`", t->toChars());
- *pt = Type::terror;
- return;
- }
-
- if (t->ty == Ttuple)
- *pt = t;
- else
- *pt = t->merge();
- }
- if (!s)
- {
- /* Look for what user might have intended
- */
- const char *p = mutableOf()->unSharedOf()->toChars();
- Identifier *id = Identifier::idPool(p, strlen(p));
- if (const char *n = importHint(p))
- error(loc, "`%s` is not defined, perhaps `import %s;` ?", p, n);
- else if (Dsymbol *s2 = sc->search_correct(id))
- error(loc, "undefined identifier `%s`, did you mean %s `%s`?", p, s2->kind(), s2->toChars());
- else if (const char *q = Scope::search_correct_C(id))
- error(loc, "undefined identifier `%s`, did you mean `%s`?", p, q);
- else
- error(loc, "undefined identifier `%s`", p);
-
- *pt = Type::terror;
- }
-}
-
-/***************************** TypeIdentifier *****************************/
-
-TypeIdentifier::TypeIdentifier(Loc loc, Identifier *ident)
- : TypeQualified(Tident, loc)
-{
- this->ident = ident;
-}
-
-const char *TypeIdentifier::kind()
-{
- return "identifier";
-}
-
-Type *TypeIdentifier::syntaxCopy()
-{
- TypeIdentifier *t = new TypeIdentifier(loc, ident);
- t->syntaxCopyHelper(this);
- t->mod = mod;
- return t;
-}
-
-/*************************************
- * Takes an array of Identifiers and figures out if
- * it represents a Type or an Expression.
- * Output:
- * if expression, *pe is set
- * if type, *pt is set
- */
-
-void TypeIdentifier::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- //printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, toChars());
-
- if ((ident->equals(Id::_super) || ident->equals(Id::This)) && !hasThis(sc))
- {
- AggregateDeclaration *ad = sc->getStructClassScope();
- if (ad)
- {
- ClassDeclaration *cd = ad->isClassDeclaration();
- if (cd)
- {
- if (ident->equals(Id::This))
- ident = cd->ident;
- else if (cd->baseClass && ident->equals(Id::_super))
- ident = cd->baseClass->ident;
- }
- else
- {
- StructDeclaration *sd = ad->isStructDeclaration();
- if (sd && ident->equals(Id::This))
- ident = sd->ident;
- }
- }
- }
- if (ident == Id::ctfe)
- {
- error(loc, "variable __ctfe cannot be read at compile time");
- *pe = NULL;
- *ps = NULL;
- *pt = Type::terror;
- return;
- }
-
- Dsymbol *scopesym;
- Dsymbol *s = sc->search(loc, ident, &scopesym);
- resolveHelper(loc, sc, s, scopesym, pe, pt, ps, intypeid);
- if (*pt)
- (*pt) = (*pt)->addMod(mod);
-}
-
-/*****************************************
- * See if type resolves to a symbol, if so,
- * return that symbol.
- */
-
-Dsymbol *TypeIdentifier::toDsymbol(Scope *sc)
-{
- //printf("TypeIdentifier::toDsymbol('%s')\n", toChars());
- if (!sc)
- return NULL;
-
- Type *t;
- Expression *e;
- Dsymbol *s;
-
- resolve(loc, sc, &e, &t, &s);
- if (t && t->ty != Tident)
- s = t->toDsymbol(sc);
- if (e)
- s = getDsymbol(e);
-
- return s;
-}
-
-/***************************** TypeInstance *****************************/
-
-TypeInstance::TypeInstance(Loc loc, TemplateInstance *tempinst)
- : TypeQualified(Tinstance, loc)
-{
- this->tempinst = tempinst;
-}
-
-const char *TypeInstance::kind()
-{
- return "instance";
-}
-
-Type *TypeInstance::syntaxCopy()
-{
- //printf("TypeInstance::syntaxCopy() %s, %d\n", toChars(), idents.length);
- TypeInstance *t = new TypeInstance(loc, (TemplateInstance *)tempinst->syntaxCopy(NULL));
- t->syntaxCopyHelper(this);
- t->mod = mod;
- return t;
-}
-
-void TypeInstance::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- // Note close similarity to TypeIdentifier::resolve()
- *pe = NULL;
- *pt = NULL;
- *ps = NULL;
- //printf("TypeInstance::resolve(sc = %p, tempinst = '%s')\n", sc, tempinst->toChars());
- dsymbolSemantic(tempinst, sc);
- if (!global.gag && tempinst->errors)
- {
- *pt = terror;
- return;
- }
-
- resolveHelper(loc, sc, tempinst, NULL, pe, pt, ps, intypeid);
- if (*pt)
- *pt = (*pt)->addMod(mod);
- //if (*pt) printf("pt = '%s'\n", (*pt)->toChars());
-}
-
-Dsymbol *TypeInstance::toDsymbol(Scope *sc)
-{
- Type *t;
- Expression *e;
- Dsymbol *s;
-
- //printf("TypeInstance::semantic(%s)\n", toChars());
- resolve(loc, sc, &e, &t, &s);
- if (t && t->ty != Tinstance)
- s = t->toDsymbol(sc);
-
- return s;
-}
-
-
-/***************************** TypeTypeof *****************************/
-
-TypeTypeof::TypeTypeof(Loc loc, Expression *exp)
- : TypeQualified(Ttypeof, loc)
-{
- this->exp = exp;
- inuse = 0;
-}
-
-const char *TypeTypeof::kind()
-{
- return "typeof";
-}
-
-Type *TypeTypeof::syntaxCopy()
-{
- //printf("TypeTypeof::syntaxCopy() %s\n", toChars());
- TypeTypeof *t = new TypeTypeof(loc, exp->syntaxCopy());
- t->syntaxCopyHelper(this);
- t->mod = mod;
- return t;
-}
-
-Dsymbol *TypeTypeof::toDsymbol(Scope *sc)
-{
- //printf("TypeTypeof::toDsymbol('%s')\n", toChars());
- Expression *e;
- Type *t;
- Dsymbol *s;
- resolve(loc, sc, &e, &t, &s);
-
- return s;
-}
-
-void TypeTypeof::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- *pe = NULL;
- *pt = NULL;
- *ps = NULL;
-
- //printf("TypeTypeof::resolve(sc = %p, idents = '%s')\n", sc, toChars());
- //static int nest; if (++nest == 50) *(char*)0=0;
- if (sc == NULL)
- {
- *pt = Type::terror;
- error(loc, "Invalid scope.");
- return;
- }
- if (inuse)
- {
- inuse = 2;
- error(loc, "circular typeof definition");
- Lerr:
- *pt = Type::terror;
- inuse--;
- return;
- }
- inuse++;
-
- Type *t;
- {
- /* Currently we cannot evalute 'exp' in speculative context, because
- * the type implementation may leak to the final execution. Consider:
- *
- * struct S(T) {
- * string toString() const { return "x"; }
- * }
- * void main() {
- * alias X = typeof(S!int());
- * assert(typeid(X).xtoString(null) == "x");
- * }
- */
- Scope *sc2 = sc->push();
- sc2->intypeof = 1;
- Expression *exp2 = expressionSemantic(exp, sc2);
- exp2 = resolvePropertiesOnly(sc2, exp2);
- sc2->pop();
-
- if (exp2->op == TOKerror)
- {
- if (!global.gag)
- exp = exp2;
- goto Lerr;
- }
- exp = exp2;
-
- if (exp->op == TOKtype ||
- exp->op == TOKscope)
- {
- if (exp->checkType())
- goto Lerr;
-
- /* Today, 'typeof(func)' returns void if func is a
- * function template (TemplateExp), or
- * template lambda (FuncExp).
- * It's actually used in Phobos as an idiom, to branch code for
- * template functions.
- */
- }
- if (FuncDeclaration *f = exp->op == TOKvar ? (( VarExp *)exp)->var->isFuncDeclaration()
- : exp->op == TOKdotvar ? ((DotVarExp *)exp)->var->isFuncDeclaration() : NULL)
- {
- if (f->checkForwardRef(loc))
- goto Lerr;
- }
- if (FuncDeclaration *f = isFuncAddress(exp))
- {
- if (f->checkForwardRef(loc))
- goto Lerr;
- }
-
- t = exp->type;
- if (!t)
- {
- error(loc, "expression (%s) has no type", exp->toChars());
- goto Lerr;
- }
- if (t->ty == Ttypeof)
- {
- error(loc, "forward reference to %s", toChars());
- goto Lerr;
- }
- }
- if (idents.length == 0)
- *pt = t;
- else
- {
- if (Dsymbol *s = t->toDsymbol(sc))
- resolveHelper(loc, sc, s, NULL, pe, pt, ps, intypeid);
- else
- {
- Expression *e = typeToExpressionHelper(this, new TypeExp(loc, t));
- e = expressionSemantic(e, sc);
- resolveExp(e, pt, pe, ps);
- }
- }
- if (*pt)
- (*pt) = (*pt)->addMod(mod);
- inuse--;
- return;
-}
-
-d_uns64 TypeTypeof::size(Loc loc)
-{
- if (exp->type)
- return exp->type->size(loc);
- else
- return TypeQualified::size(loc);
-}
-
-
-
-/***************************** TypeReturn *****************************/
-
-TypeReturn::TypeReturn(Loc loc)
- : TypeQualified(Treturn, loc)
-{
-}
-
-const char *TypeReturn::kind()
-{
- return "return";
-}
-
-Type *TypeReturn::syntaxCopy()
-{
- TypeReturn *t = new TypeReturn(loc);
- t->syntaxCopyHelper(this);
- t->mod = mod;
- return t;
-}
-
-Dsymbol *TypeReturn::toDsymbol(Scope *sc)
-{
- Expression *e;
- Type *t;
- Dsymbol *s;
- resolve(loc, sc, &e, &t, &s);
-
- return s;
-}
-
-void TypeReturn::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- *pe = NULL;
- *pt = NULL;
- *ps = NULL;
-
- //printf("TypeReturn::resolve(sc = %p, idents = '%s')\n", sc, toChars());
- Type *t;
- {
- FuncDeclaration *func = sc->func;
- if (!func)
- {
- error(loc, "typeof(return) must be inside function");
- goto Lerr;
- }
- if (func->fes)
- func = func->fes->func;
-
- t = func->type->nextOf();
- if (!t)
- {
- error(loc, "cannot use typeof(return) inside function %s with inferred return type", sc->func->toChars());
- goto Lerr;
- }
- }
- if (idents.length == 0)
- *pt = t;
- else
- {
- if (Dsymbol *s = t->toDsymbol(sc))
- resolveHelper(loc, sc, s, NULL, pe, pt, ps, intypeid);
- else
- {
- Expression *e = typeToExpressionHelper(this, new TypeExp(loc, t));
- e = expressionSemantic(e, sc);
- resolveExp(e, pt, pe, ps);
- }
- }
- if (*pt)
- (*pt) = (*pt)->addMod(mod);
- return;
-
-Lerr:
- *pt = Type::terror;
- return;
-}
-
-/***************************** TypeEnum *****************************/
-
-TypeEnum::TypeEnum(EnumDeclaration *sym)
- : Type(Tenum)
-{
- this->sym = sym;
-}
-
-const char *TypeEnum::kind()
-{
- return "enum";
-}
-
-Type *TypeEnum::syntaxCopy()
-{
- return this;
-}
-
-d_uns64 TypeEnum::size(Loc loc)
-{
- return sym->getMemtype(loc)->size(loc);
-}
-
-unsigned TypeEnum::alignsize()
-{
- Type *t = sym->getMemtype(Loc());
- if (t->ty == Terror)
- return 4;
- return t->alignsize();
-}
-
-Dsymbol *TypeEnum::toDsymbol(Scope *)
-{
- return sym;
-}
-
-Type *TypeEnum::toBasetype()
-{
- if (!sym->members && !sym->memtype)
- return this;
- Type *tb = sym->getMemtype(Loc())->toBasetype();
- return tb->castMod(mod); // retain modifier bits from 'this'
-}
-
-Expression *TypeEnum::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- // Bugzilla 14010
- if (ident == Id::_mangleof)
- return getProperty(e->loc, ident, flag & 1);
-
- if (sym->semanticRun < PASSsemanticdone)
- dsymbolSemantic(sym, NULL);
-
- Dsymbol *s = sym->search(e->loc, ident);
- if (!s)
- {
- if (ident == Id::max ||
- ident == Id::min ||
- ident == Id::_init)
- {
- return getProperty(e->loc, ident, flag & 1);
- }
- Expression *res = sym->getMemtype(Loc())->dotExp(sc, e, ident, 1);
- if (!(flag & 1) && !res)
- {
- if (Dsymbol *ns = sym->search_correct(ident))
- e->error("no property `%s` for type `%s`. Did you mean `%s.%s` ?",
- ident->toChars(), toChars(), toChars(), ns->toChars());
- else
- e->error("no property `%s` for type `%s`",
- ident->toChars(), toChars());
-
- return new ErrorExp();
- }
- return res;
- }
- EnumMember *m = s->isEnumMember();
- return m->getVarExp(e->loc, sc);
-}
-
-Expression *TypeEnum::getProperty(Loc loc, Identifier *ident, int flag)
-{
- Expression *e;
- if (ident == Id::max || ident == Id::min)
- {
- return sym->getMaxMinValue(loc, ident);
- }
- else if (ident == Id::_init)
- {
- e = defaultInitLiteral(loc);
- }
- else if (ident == Id::stringof)
- {
- const char *s = toChars();
- e = new StringExp(loc, const_cast<char *>(s), strlen(s));
- Scope sc;
- e = expressionSemantic(e, &sc);
- }
- else if (ident == Id::_mangleof)
- {
- e = Type::getProperty(loc, ident, flag);
- }
- else
- {
- e = toBasetype()->getProperty(loc, ident, flag);
- }
- return e;
-}
-
-bool TypeEnum::isintegral()
-{
- return sym->getMemtype(Loc())->isintegral();
-}
-
-bool TypeEnum::isfloating()
-{
- return sym->getMemtype(Loc())->isfloating();
-}
-
-bool TypeEnum::isreal()
-{
- return sym->getMemtype(Loc())->isreal();
-}
-
-bool TypeEnum::isimaginary()
-{
- return sym->getMemtype(Loc())->isimaginary();
-}
-
-bool TypeEnum::iscomplex()
-{
- return sym->getMemtype(Loc())->iscomplex();
-}
-
-bool TypeEnum::isunsigned()
-{
- return sym->getMemtype(Loc())->isunsigned();
-}
-
-bool TypeEnum::isscalar()
-{
- return sym->getMemtype(Loc())->isscalar();
-}
-
-bool TypeEnum::isString()
-{
- return sym->getMemtype(Loc())->isString();
-}
-
-bool TypeEnum::isAssignable()
-{
- return sym->getMemtype(Loc())->isAssignable();
-}
-
-bool TypeEnum::isBoolean()
-{
- return sym->getMemtype(Loc())->isBoolean();
-}
-
-bool TypeEnum::needsDestruction()
-{
- return sym->getMemtype(Loc())->needsDestruction();
-}
-
-bool TypeEnum::needsNested()
-{
- return sym->getMemtype(Loc())->needsNested();
-}
-
-MATCH TypeEnum::implicitConvTo(Type *to)
-{
- MATCH m;
-
- //printf("TypeEnum::implicitConvTo()\n");
- if (ty == to->ty && sym == ((TypeEnum *)to)->sym)
- m = (mod == to->mod) ? MATCHexact : MATCHconst;
- else if (sym->getMemtype(Loc())->implicitConvTo(to))
- m = MATCHconvert; // match with conversions
- else
- m = MATCHnomatch; // no match
- return m;
-}
-
-MATCH TypeEnum::constConv(Type *to)
-{
- if (equals(to))
- return MATCHexact;
- if (ty == to->ty && sym == ((TypeEnum *)to)->sym &&
- MODimplicitConv(mod, to->mod))
- return MATCHconst;
- return MATCHnomatch;
-}
-
-
-Expression *TypeEnum::defaultInit(Loc loc)
-{
- // Initialize to first member of enum
- Expression *e = sym->getDefaultValue(loc);
- e = e->copy();
- e->loc = loc;
- e->type = this; // to deal with const, immutable, etc., variants
- return e;
-}
-
-bool TypeEnum::isZeroInit(Loc loc)
-{
- return sym->getDefaultValue(loc)->isBool(false);
-}
-
-bool TypeEnum::hasPointers()
-{
- return sym->getMemtype(Loc())->hasPointers();
-}
-
-bool TypeEnum::hasVoidInitPointers()
-{
- return sym->getMemtype(Loc())->hasVoidInitPointers();
-}
-
-Type *TypeEnum::nextOf()
-{
- return sym->getMemtype(Loc())->nextOf();
-}
-
-/***************************** TypeStruct *****************************/
-
-TypeStruct::TypeStruct(StructDeclaration *sym)
- : Type(Tstruct)
-{
- this->sym = sym;
- this->att = RECfwdref;
- this->cppmangle = CPPMANGLEdefault;
-}
-
-TypeStruct *TypeStruct::create(StructDeclaration *sym)
-{
- return new TypeStruct(sym);
-}
-
-const char *TypeStruct::kind()
-{
- return "struct";
-}
-
-Type *TypeStruct::syntaxCopy()
-{
- return this;
-}
-
-d_uns64 TypeStruct::size(Loc loc)
-{
- return sym->size(loc);
-}
-
-unsigned TypeStruct::alignsize()
-{
- sym->size(Loc()); // give error for forward references
- return sym->alignsize;
-}
-
-Dsymbol *TypeStruct::toDsymbol(Scope *)
-{
- return sym;
-}
-
-Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- Dsymbol *s;
-
- assert(e->op != TOKdot);
-
- // Bugzilla 14010
- if (ident == Id::_mangleof)
- return getProperty(e->loc, ident, flag & 1);
-
- /* If e.tupleof
- */
- if (ident == Id::_tupleof)
- {
- /* Create a TupleExp out of the fields of the struct e:
- * (e.field0, e.field1, e.field2, ...)
- */
- e = expressionSemantic(e, sc); // do this before turning on noaccesscheck
-
- if (!sym->determineFields())
- {
- error(e->loc, "unable to determine fields of `%s` because of forward references", toChars());
- }
-
- Expression *e0 = NULL;
- Expression *ev = e->op == TOKtype ? NULL : e;
- if (ev)
- ev = extractSideEffect(sc, "__tup", &e0, ev);
-
- Expressions *exps = new Expressions;
- exps->reserve(sym->fields.length);
- for (size_t i = 0; i < sym->fields.length; i++)
- {
- VarDeclaration *v = sym->fields[i];
- Expression *ex;
- if (ev)
- ex = new DotVarExp(e->loc, ev, v);
- else
- {
- ex = new VarExp(e->loc, v);
- ex->type = ex->type->addMod(e->type->mod);
- }
- exps->push(ex);
- }
-
- e = new TupleExp(e->loc, e0, exps);
- Scope *sc2 = sc->push();
- sc2->flags = sc->flags | SCOPEnoaccesscheck;
- e = expressionSemantic(e, sc2);
- sc2->pop();
- return e;
- }
-
- const int flags = sc->flags & SCOPEignoresymbolvisibility ? IgnoreSymbolVisibility : 0;
- s = sym->search(e->loc, ident, flags | IgnorePrivateImports);
-L1:
- if (!s)
- {
- return noMember(sc, e, ident, flag);
- }
- if (!(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, s))
- {
- return noMember(sc, e, ident, flag);
- }
- if (!s->isFuncDeclaration()) // because of overloading
- {
- s->checkDeprecated(e->loc, sc);
- if (Declaration *d = s->isDeclaration())
- d->checkDisabled(e->loc, sc);
- }
- s = s->toAlias();
-
- EnumMember *em = s->isEnumMember();
- if (em)
- {
- return em->getVarExp(e->loc, sc);
- }
-
- if (VarDeclaration *v = s->isVarDeclaration())
- {
- if (!v->type ||
- (!v->type->deco && v->inuse))
- {
- if (v->inuse) // Bugzilla 9494
- e->error("circular reference to %s `%s`", v->kind(), v->toPrettyChars());
- else
- e->error("forward reference to %s `%s`", v->kind(), v->toPrettyChars());
- return new ErrorExp();
- }
- if (v->type->ty == Terror)
- return new ErrorExp();
-
- if ((v->storage_class & STCmanifest) && v->_init)
- {
- if (v->inuse)
- {
- e->error("circular initialization of %s `%s`", v->kind(), v->toPrettyChars());
- return new ErrorExp();
- }
- checkAccess(e->loc, sc, NULL, v);
- Expression *ve = new VarExp(e->loc, v);
- ve = expressionSemantic(ve, sc);
- return ve;
- }
- }
-
- if (Type *t = s->getType())
- {
- return expressionSemantic(new TypeExp(e->loc, t), sc);
- }
-
- TemplateMixin *tm = s->isTemplateMixin();
- if (tm)
- {
- Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, tm));
- de->type = e->type;
- return de;
- }
-
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (td)
- {
- if (e->op == TOKtype)
- e = new TemplateExp(e->loc, td);
- else
- e = new DotTemplateExp(e->loc, e, td);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- TemplateInstance *ti = s->isTemplateInstance();
- if (ti)
- {
- if (!ti->semanticRun)
- {
- dsymbolSemantic(ti, sc);
- if (!ti->inst || ti->errors) // if template failed to expand
- return new ErrorExp();
- }
- s = ti->inst->toAlias();
- if (!s->isTemplateInstance())
- goto L1;
- if (e->op == TOKtype)
- e = new ScopeExp(e->loc, ti);
- else
- e = new DotExp(e->loc, e, new ScopeExp(e->loc, ti));
- return expressionSemantic(e, sc);
- }
-
- if (s->isImport() || s->isModule() || s->isPackage())
- {
- e = ::resolve(e->loc, sc, s, false);
- return e;
- }
-
- OverloadSet *o = s->isOverloadSet();
- if (o)
- {
- OverExp *oe = new OverExp(e->loc, o);
- if (e->op == TOKtype)
- return oe;
- return new DotExp(e->loc, e, oe);
- }
-
- Declaration *d = s->isDeclaration();
- if (!d)
- {
- e->error("%s.%s is not a declaration", e->toChars(), ident->toChars());
- return new ErrorExp();
- }
-
- if (e->op == TOKtype)
- {
- /* It's:
- * Struct.d
- */
- if (TupleDeclaration *tup = d->isTupleDeclaration())
- {
- e = new TupleExp(e->loc, tup);
- e = expressionSemantic(e, sc);
- return e;
- }
- if (d->needThis() && sc->intypeof != 1)
- {
- /* Rewrite as:
- * this.d
- */
- if (hasThis(sc))
- {
- e = new DotVarExp(e->loc, new ThisExp(e->loc), d);
- e = expressionSemantic(e, sc);
- return e;
- }
- }
- if (d->semanticRun == PASSinit)
- dsymbolSemantic(d, NULL);
- checkAccess(e->loc, sc, e, d);
- VarExp *ve = new VarExp(e->loc, d);
- if (d->isVarDeclaration() && d->needThis())
- ve->type = d->type->addMod(e->type->mod);
- return ve;
- }
-
- bool unreal = e->op == TOKvar && ((VarExp *)e)->var->isField();
- if (d->isDataseg() || (unreal && d->isField()))
- {
- // (e, d)
- checkAccess(e->loc, sc, e, d);
- Expression *ve = new VarExp(e->loc, d);
- e = unreal ? ve : new CommaExp(e->loc, e, ve);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- e = new DotVarExp(e->loc, e, d);
- e = expressionSemantic(e, sc);
- return e;
-}
-
-structalign_t TypeStruct::alignment()
-{
- if (sym->alignment == 0)
- sym->size(sym->loc);
- return sym->alignment;
-}
-
-Expression *TypeStruct::defaultInit(Loc)
-{
- Declaration *d = new SymbolDeclaration(sym->loc, sym);
- assert(d);
- d->type = this;
- d->storage_class |= STCrvalue; // Bugzilla 14398
- return new VarExp(sym->loc, d);
-}
-
-/***************************************
- * Use when we prefer the default initializer to be a literal,
- * rather than a global immutable variable.
- */
-Expression *TypeStruct::defaultInitLiteral(Loc loc)
-{
- sym->size(loc);
- if (sym->sizeok != SIZEOKdone)
- return new ErrorExp();
- Expressions *structelems = new Expressions();
- structelems->setDim(sym->fields.length - sym->isNested());
- unsigned offset = 0;
- for (size_t j = 0; j < structelems->length; j++)
- {
- VarDeclaration *vd = sym->fields[j];
- Expression *e;
- if (vd->inuse)
- {
- error(loc, "circular reference to `%s`", vd->toPrettyChars());
- return new ErrorExp();
- }
- if (vd->offset < offset || vd->type->size() == 0)
- e = NULL;
- else if (vd->_init)
- {
- if (vd->_init->isVoidInitializer())
- e = NULL;
- else
- e = vd->getConstInitializer(false);
- }
- else
- e = vd->type->defaultInitLiteral(loc);
- if (e && e->op == TOKerror)
- return e;
- if (e)
- offset = vd->offset + (unsigned)vd->type->size();
- (*structelems)[j] = e;
- }
- StructLiteralExp *structinit = new StructLiteralExp(loc, (StructDeclaration *)sym, structelems);
-
- /* Copy from the initializer symbol for larger symbols,
- * otherwise the literals expressed as code get excessively large.
- */
- if (size(loc) > target.ptrsize * 4U && !needsNested())
- structinit->useStaticInit = true;
-
- structinit->type = this;
- return structinit;
-}
-
-
-bool TypeStruct::isZeroInit(Loc)
-{
- return sym->zeroInit != 0;
-}
-
-bool TypeStruct::isBoolean()
-{
- return false;
-}
-
-bool TypeStruct::needsDestruction()
-{
- return sym->dtor != NULL;
-}
-
-bool TypeStruct::needsNested()
-{
- if (sym->isNested())
- return true;
-
- for (size_t i = 0; i < sym->fields.length; i++)
- {
- VarDeclaration *v = sym->fields[i];
- if (!v->isDataseg() && v->type->needsNested())
- return true;
- }
- return false;
-}
-
-bool TypeStruct::isAssignable()
-{
- bool assignable = true;
- unsigned offset = ~0; // dead-store initialize to prevent spurious warning
-
- /* If any of the fields are const or immutable,
- * then one cannot assign this struct.
- */
- for (size_t i = 0; i < sym->fields.length; i++)
- {
- VarDeclaration *v = sym->fields[i];
- //printf("%s [%d] v = (%s) %s, v->offset = %d, v->parent = %s", sym->toChars(), i, v->kind(), v->toChars(), v->offset, v->parent->kind());
- if (i == 0)
- ;
- else if (v->offset == offset)
- {
- /* If any fields of anonymous union are assignable,
- * then regard union as assignable.
- * This is to support unsafe things like Rebindable templates.
- */
- if (assignable)
- continue;
- }
- else
- {
- if (!assignable)
- return false;
- }
- assignable = v->type->isMutable() && v->type->isAssignable();
- offset = v->offset;
- //printf(" -> assignable = %d\n", assignable);
- }
-
- return assignable;
-}
-
-bool TypeStruct::hasPointers()
-{
- // Probably should cache this information in sym rather than recompute
- StructDeclaration *s = sym;
-
- if (sym->members && !sym->determineFields() && sym->type != Type::terror)
- error(sym->loc, "no size because of forward references");
-
- for (size_t i = 0; i < s->fields.length; i++)
- {
- Declaration *d = s->fields[i];
- if (d->storage_class & STCref || d->hasPointers())
- return true;
- }
- return false;
-}
-
-bool TypeStruct::hasVoidInitPointers()
-{
- // Probably should cache this information in sym rather than recompute
- StructDeclaration *s = sym;
-
- sym->size(Loc()); // give error for forward references
- for (size_t i = 0; i < s->fields.length; i++)
- {
- VarDeclaration *v = s->fields[i];
- if (v->_init && v->_init->isVoidInitializer() && v->type->hasPointers())
- return true;
- if (!v->_init && v->type->hasVoidInitPointers())
- return true;
- }
- return false;
-}
-
-MATCH TypeStruct::implicitConvTo(Type *to)
-{ MATCH m;
-
- //printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to->toChars());
-
- if (ty == to->ty && sym == ((TypeStruct *)to)->sym)
- {
- m = MATCHexact; // exact match
- if (mod != to->mod)
- {
- m = MATCHconst;
- if (MODimplicitConv(mod, to->mod))
- ;
- else
- {
- /* Check all the fields. If they can all be converted,
- * allow the conversion.
- */
- unsigned offset = ~0; // dead-store to prevent spurious warning
- for (size_t i = 0; i < sym->fields.length; i++)
- {
- VarDeclaration *v = sym->fields[i];
- if (i == 0)
- ;
- else if (v->offset == offset)
- {
- if (m > MATCHnomatch)
- continue;
- }
- else
- {
- if (m <= MATCHnomatch)
- return m;
- }
-
- // 'from' type
- Type *tvf = v->type->addMod(mod);
-
- // 'to' type
- Type *tv = v->type->addMod(to->mod);
-
- // field match
- MATCH mf = tvf->implicitConvTo(tv);
- //printf("\t%s => %s, match = %d\n", v->type->toChars(), tv->toChars(), mf);
-
- if (mf <= MATCHnomatch)
- return mf;
- if (mf < m) // if field match is worse
- m = mf;
- offset = v->offset;
- }
- }
- }
- }
- else if (sym->aliasthis && !(att & RECtracing))
- {
- att = (AliasThisRec)(att | RECtracing);
- m = aliasthisOf()->implicitConvTo(to);
- att = (AliasThisRec)(att & ~RECtracing);
- }
- else
- m = MATCHnomatch; // no match
- return m;
-}
-
-MATCH TypeStruct::constConv(Type *to)
-{
- if (equals(to))
- return MATCHexact;
- if (ty == to->ty && sym == ((TypeStruct *)to)->sym &&
- MODimplicitConv(mod, to->mod))
- return MATCHconst;
- return MATCHnomatch;
-}
-
-unsigned char TypeStruct::deduceWild(Type *t, bool isRef)
-{
- if (ty == t->ty && sym == ((TypeStruct *)t)->sym)
- return Type::deduceWild(t, isRef);
-
- unsigned char wm = 0;
-
- if (t->hasWild() && sym->aliasthis && !(att & RECtracing))
- {
- att = (AliasThisRec)(att | RECtracing);
- wm = aliasthisOf()->deduceWild(t, isRef);
- att = (AliasThisRec)(att & ~RECtracing);
- }
-
- return wm;
-}
-
-Type *TypeStruct::toHeadMutable()
-{
- return this;
-}
-
-
-/***************************** TypeClass *****************************/
-
-TypeClass::TypeClass(ClassDeclaration *sym)
- : Type(Tclass)
-{
- this->sym = sym;
- this->att = RECfwdref;
- this->cppmangle = CPPMANGLEdefault;
-}
-
-const char *TypeClass::kind()
-{
- return "class";
-}
-
-Type *TypeClass::syntaxCopy()
-{
- return this;
-}
-
-d_uns64 TypeClass::size(Loc)
-{
- return target.ptrsize;
-}
-
-Dsymbol *TypeClass::toDsymbol(Scope *)
-{
- return sym;
-}
-
-Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
-{
- Dsymbol *s;
- assert(e->op != TOKdot);
-
- // Bugzilla 12543
- if (ident == Id::__sizeof || ident == Id::__xalignof || ident == Id::_mangleof)
- {
- return Type::getProperty(e->loc, ident, 0);
- }
-
- /* If e.tupleof
- */
- if (ident == Id::_tupleof)
- {
- /* Create a TupleExp
- */
- e = expressionSemantic(e, sc); // do this before turning on noaccesscheck
-
- sym->size(e->loc); // do semantic of type
-
- Expression *e0 = NULL;
- Expression *ev = e->op == TOKtype ? NULL : e;
- if (ev)
- ev = extractSideEffect(sc, "__tup", &e0, ev);
-
- Expressions *exps = new Expressions;
- exps->reserve(sym->fields.length);
- for (size_t i = 0; i < sym->fields.length; i++)
- {
- VarDeclaration *v = sym->fields[i];
- // Don't include hidden 'this' pointer
- if (v->isThisDeclaration())
- continue;
- Expression *ex;
- if (ev)
- ex = new DotVarExp(e->loc, ev, v);
- else
- {
- ex = new VarExp(e->loc, v);
- ex->type = ex->type->addMod(e->type->mod);
- }
- exps->push(ex);
- }
-
- e = new TupleExp(e->loc, e0, exps);
- Scope *sc2 = sc->push();
- sc2->flags = sc->flags | SCOPEnoaccesscheck;
- e = expressionSemantic(e, sc2);
- sc2->pop();
- return e;
- }
-
- int flags = sc->flags & SCOPEignoresymbolvisibility ? IgnoreSymbolVisibility : 0;
- s = sym->search(e->loc, ident, flags | IgnorePrivateImports);
-
-L1:
- if (!s)
- {
- // See if it's 'this' class or a base class
- if (sym->ident == ident)
- {
- if (e->op == TOKtype)
- return Type::getProperty(e->loc, ident, 0);
- e = new DotTypeExp(e->loc, e, sym);
- e = expressionSemantic(e, sc);
- return e;
- }
- if (ClassDeclaration *cbase = sym->searchBase(ident))
- {
- if (e->op == TOKtype)
- return Type::getProperty(e->loc, ident, 0);
- if (InterfaceDeclaration *ifbase = cbase->isInterfaceDeclaration())
- e = new CastExp(e->loc, e, ifbase->type);
- else
- e = new DotTypeExp(e->loc, e, cbase);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- if (ident == Id::classinfo)
- {
- if (!Type::typeinfoclass)
- {
- error(e->loc, "`object.TypeInfo_Class` could not be found, but is implicitly used");
- return new ErrorExp();
- }
-
- Type *t = Type::typeinfoclass->type;
- if (e->op == TOKtype || e->op == TOKdottype)
- {
- /* For type.classinfo, we know the classinfo
- * at compile time.
- */
- if (!sym->vclassinfo)
- sym->vclassinfo = new TypeInfoClassDeclaration(sym->type);
- e = new VarExp(e->loc, sym->vclassinfo);
- e = e->addressOf();
- e->type = t; // do this so we don't get redundant dereference
- }
- else
- {
- /* For class objects, the classinfo reference is the first
- * entry in the vtbl[]
- */
- e = new PtrExp(e->loc, e);
- e->type = t->pointerTo();
- if (sym->isInterfaceDeclaration())
- {
- if (sym->isCPPinterface())
- {
- /* C++ interface vtbl[]s are different in that the
- * first entry is always pointer to the first virtual
- * function, not classinfo.
- * We can't get a .classinfo for it.
- */
- error(e->loc, "no .classinfo for C++ interface objects");
- }
- /* For an interface, the first entry in the vtbl[]
- * is actually a pointer to an instance of struct Interface.
- * The first member of Interface is the .classinfo,
- * so add an extra pointer indirection.
- */
- e->type = e->type->pointerTo();
- e = new PtrExp(e->loc, e);
- e->type = t->pointerTo();
- }
- e = new PtrExp(e->loc, e, t);
- }
- return e;
- }
-
- if (ident == Id::__vptr)
- {
- /* The pointer to the vtbl[]
- * *cast(immutable(void*)**)e
- */
- e = e->castTo(sc, tvoidptr->immutableOf()->pointerTo()->pointerTo());
- e = new PtrExp(e->loc, e);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- if (ident == Id::__monitor && sym->hasMonitor())
- {
- /* The handle to the monitor (call it a void*)
- * *(cast(void**)e + 1)
- */
- e = e->castTo(sc, tvoidptr->pointerTo());
- e = new AddExp(e->loc, e, new IntegerExp(1));
- e = new PtrExp(e->loc, e);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- if (ident == Id::outer && sym->vthis)
- {
- if (sym->vthis->semanticRun == PASSinit)
- dsymbolSemantic(sym->vthis, NULL);
-
- if (ClassDeclaration *cdp = sym->toParent2()->isClassDeclaration())
- {
- DotVarExp *dve = new DotVarExp(e->loc, e, sym->vthis);
- dve->type = cdp->type->addMod(e->type->mod);
- return dve;
- }
-
- /* Bugzilla 15839: Find closest parent class through nested functions.
- */
- for (Dsymbol *p = sym->toParent2(); p; p = p->toParent2())
- {
- FuncDeclaration *fd = p->isFuncDeclaration();
- if (!fd)
- break;
- if (fd->isNested())
- continue;
- AggregateDeclaration *ad = fd->isThis();
- if (!ad)
- break;
- if (ad->isClassDeclaration())
- {
- ThisExp *ve = new ThisExp(e->loc);
-
- ve->var = fd->vthis;
- const bool nestedError = fd->vthis->checkNestedReference(sc, e->loc);
- assert(!nestedError);
-
- ve->type = fd->vthis->type->addMod(e->type->mod);
- return ve;
- }
- break;
- }
-
- // Continue to show enclosing function's frame (stack or closure).
- DotVarExp *dve = new DotVarExp(e->loc, e, sym->vthis);
- dve->type = sym->vthis->type->addMod(e->type->mod);
- return dve;
- }
-
- return noMember(sc, e, ident, flag & 1);
- }
- if (!(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, s))
- {
- return noMember(sc, e, ident, flag);
- }
- if (!s->isFuncDeclaration()) // because of overloading
- {
- s->checkDeprecated(e->loc, sc);
- if (Declaration *d = s->isDeclaration())
- d->checkDisabled(e->loc, sc);
- }
- s = s->toAlias();
-
- EnumMember *em = s->isEnumMember();
- if (em)
- {
- return em->getVarExp(e->loc, sc);
- }
-
- if (VarDeclaration *v = s->isVarDeclaration())
- {
- if (!v->type ||
- (!v->type->deco && v->inuse))
- {
- if (v->inuse) // Bugzilla 9494
- e->error("circular reference to %s `%s`", v->kind(), v->toPrettyChars());
- else
- e->error("forward reference to %s `%s`", v->kind(), v->toPrettyChars());
- return new ErrorExp();
- }
- if (v->type->ty == Terror)
- return new ErrorExp();
-
- if ((v->storage_class & STCmanifest) && v->_init)
- {
- if (v->inuse)
- {
- e->error("circular initialization of %s `%s`", v->kind(), v->toPrettyChars());
- return new ErrorExp();
- }
- checkAccess(e->loc, sc, NULL, v);
- Expression *ve = new VarExp(e->loc, v);
- ve = expressionSemantic(ve, sc);
- return ve;
- }
- }
-
- if (Type *t = s->getType())
- {
- return expressionSemantic(new TypeExp(e->loc, t), sc);
- }
-
- TemplateMixin *tm = s->isTemplateMixin();
- if (tm)
- {
- Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, tm));
- de->type = e->type;
- return de;
- }
-
- TemplateDeclaration *td = s->isTemplateDeclaration();
- if (td)
- {
- if (e->op == TOKtype)
- e = new TemplateExp(e->loc, td);
- else
- e = new DotTemplateExp(e->loc, e, td);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- TemplateInstance *ti = s->isTemplateInstance();
- if (ti)
- {
- if (!ti->semanticRun)
- {
- dsymbolSemantic(ti, sc);
- if (!ti->inst || ti->errors) // if template failed to expand
- return new ErrorExp();
- }
- s = ti->inst->toAlias();
- if (!s->isTemplateInstance())
- goto L1;
- if (e->op == TOKtype)
- e = new ScopeExp(e->loc, ti);
- else
- e = new DotExp(e->loc, e, new ScopeExp(e->loc, ti));
- return expressionSemantic(e, sc);
- }
-
- if (s->isImport() || s->isModule() || s->isPackage())
- {
- e = ::resolve(e->loc, sc, s, false);
- return e;
- }
-
- OverloadSet *o = s->isOverloadSet();
- if (o)
- {
- OverExp *oe = new OverExp(e->loc, o);
- if (e->op == TOKtype)
- return oe;
- return new DotExp(e->loc, e, oe);
- }
-
- Declaration *d = s->isDeclaration();
- if (!d)
- {
- e->error("%s.%s is not a declaration", e->toChars(), ident->toChars());
- return new ErrorExp();
- }
-
- if (e->op == TOKtype)
- {
- /* It's:
- * Class.d
- */
- if (TupleDeclaration *tup = d->isTupleDeclaration())
- {
- e = new TupleExp(e->loc, tup);
- e = expressionSemantic(e, sc);
- return e;
- }
- if (d->needThis() && sc->intypeof != 1)
- {
- /* Rewrite as:
- * this.d
- */
- if (hasThis(sc))
- {
- // This is almost same as getRightThis() in expression.c
- Expression *e1 = new ThisExp(e->loc);
- e1 = expressionSemantic(e1, sc);
- L2:
- Type *t = e1->type->toBasetype();
- ClassDeclaration *cd = e->type->isClassHandle();
- ClassDeclaration *tcd = t->isClassHandle();
- if (cd && tcd && (tcd == cd || cd->isBaseOf(tcd, NULL)))
- {
- e = new DotTypeExp(e1->loc, e1, cd);
- e = new DotVarExp(e->loc, e, d);
- e = expressionSemantic(e, sc);
- return e;
- }
- if (tcd && tcd->isNested())
- { /* e1 is the 'this' pointer for an inner class: tcd.
- * Rewrite it as the 'this' pointer for the outer class.
- */
-
- e1 = new DotVarExp(e->loc, e1, tcd->vthis);
- e1->type = tcd->vthis->type;
- e1->type = e1->type->addMod(t->mod);
- // Do not call checkNestedRef()
- //e1 = expressionSemantic(e1, sc);
-
- // Skip up over nested functions, and get the enclosing
- // class type.
- int n = 0;
- for (s = tcd->toParent();
- s && s->isFuncDeclaration();
- s = s->toParent())
- { FuncDeclaration *f = s->isFuncDeclaration();
- if (f->vthis)
- {
- //printf("rewriting e1 to %s's this\n", f->toChars());
- n++;
- e1 = new VarExp(e->loc, f->vthis);
- }
- else
- {
- e = new VarExp(e->loc, d);
- return e;
- }
- }
- if (s && s->isClassDeclaration())
- { e1->type = s->isClassDeclaration()->type;
- e1->type = e1->type->addMod(t->mod);
- if (n > 1)
- e1 = expressionSemantic(e1, sc);
- }
- else
- e1 = expressionSemantic(e1, sc);
- goto L2;
- }
- }
- }
- //printf("e = %s, d = %s\n", e->toChars(), d->toChars());
- if (d->semanticRun == PASSinit)
- dsymbolSemantic(d, NULL);
- checkAccess(e->loc, sc, e, d);
- VarExp *ve = new VarExp(e->loc, d);
- if (d->isVarDeclaration() && d->needThis())
- ve->type = d->type->addMod(e->type->mod);
- return ve;
- }
-
- bool unreal = e->op == TOKvar && ((VarExp *)e)->var->isField();
- if (d->isDataseg() || (unreal && d->isField()))
- {
- // (e, d)
- checkAccess(e->loc, sc, e, d);
- Expression *ve = new VarExp(e->loc, d);
- e = unreal ? ve : new CommaExp(e->loc, e, ve);
- e = expressionSemantic(e, sc);
- return e;
- }
-
- e = new DotVarExp(e->loc, e, d);
- e = expressionSemantic(e, sc);
- return e;
-}
-
-ClassDeclaration *TypeClass::isClassHandle()
-{
- return sym;
-}
-
-bool TypeClass::isscope()
-{
- return sym->isscope;
-}
-
-bool TypeClass::isBaseOf(Type *t, int *poffset)
-{
- if (t && t->ty == Tclass)
- {
- ClassDeclaration *cd = ((TypeClass *)t)->sym;
- if (sym->isBaseOf(cd, poffset))
- return true;
- }
- return false;
-}
-
-MATCH TypeClass::implicitConvTo(Type *to)
-{
- //printf("TypeClass::implicitConvTo(to = '%s') %s\n", to->toChars(), toChars());
- MATCH m = constConv(to);
- if (m > MATCHnomatch)
- return m;
-
- ClassDeclaration *cdto = to->isClassHandle();
- if (cdto)
- {
- //printf("TypeClass::implicitConvTo(to = '%s') %s, isbase = %d %d\n", to->toChars(), toChars(), cdto->isBaseInfoComplete(), sym->isBaseInfoComplete());
- if (cdto->semanticRun < PASSsemanticdone && !cdto->isBaseInfoComplete())
- dsymbolSemantic(cdto, NULL);
- if (sym->semanticRun < PASSsemanticdone && !sym->isBaseInfoComplete())
- dsymbolSemantic(sym, NULL);
- if (cdto->isBaseOf(sym, NULL) && MODimplicitConv(mod, to->mod))
- {
- //printf("'to' is base\n");
- return MATCHconvert;
- }
- }
-
- m = MATCHnomatch;
- if (sym->aliasthis && !(att & RECtracing))
- {
- att = (AliasThisRec)(att | RECtracing);
- m = aliasthisOf()->implicitConvTo(to);
- att = (AliasThisRec)(att & ~RECtracing);
- }
-
- return m;
-}
-
-MATCH TypeClass::constConv(Type *to)
-{
- if (equals(to))
- return MATCHexact;
- if (ty == to->ty && sym == ((TypeClass *)to)->sym &&
- MODimplicitConv(mod, to->mod))
- return MATCHconst;
-
- /* Conversion derived to const(base)
- */
- int offset = 0;
- if (to->isBaseOf(this, &offset) && offset == 0 &&
- MODimplicitConv(mod, to->mod))
- {
- // Disallow:
- // derived to base
- // inout(derived) to inout(base)
- if (!to->isMutable() && !to->isWild())
- return MATCHconvert;
- }
-
- return MATCHnomatch;
-}
-
-unsigned char TypeClass::deduceWild(Type *t, bool isRef)
-{
- ClassDeclaration *cd = t->isClassHandle();
- if (cd && (sym == cd || cd->isBaseOf(sym, NULL)))
- return Type::deduceWild(t, isRef);
-
- unsigned char wm = 0;
-
- if (t->hasWild() && sym->aliasthis && !(att & RECtracing))
- {
- att = (AliasThisRec)(att | RECtracing);
- wm = aliasthisOf()->deduceWild(t, isRef);
- att = (AliasThisRec)(att & ~RECtracing);
- }
-
- return wm;
-}
-
-Type *TypeClass::toHeadMutable()
-{
- return this;
-}
-
-Expression *TypeClass::defaultInit(Loc loc)
-{
- return new NullExp(loc, this);
-}
-
-bool TypeClass::isZeroInit(Loc)
-{
- return true;
-}
-
-bool TypeClass::isBoolean()
-{
- return true;
-}
-
-bool TypeClass::hasPointers()
-{
- return true;
-}
-
-/***************************** TypeTuple *****************************/
-
-TypeTuple::TypeTuple(Parameters *arguments)
- : Type(Ttuple)
-{
- //printf("TypeTuple(this = %p)\n", this);
- this->arguments = arguments;
- //printf("TypeTuple() %p, %s\n", this, toChars());
-}
-
-/****************
- * Form TypeTuple from the types of the expressions.
- * Assume exps[] is already tuple expanded.
- */
-
-TypeTuple::TypeTuple(Expressions *exps)
- : Type(Ttuple)
-{
- Parameters *arguments = new Parameters;
- if (exps)
- {
- arguments->setDim(exps->length);
- for (size_t i = 0; i < exps->length; i++)
- { Expression *e = (*exps)[i];
- if (e->type->ty == Ttuple)
- e->error("cannot form tuple of tuples");
- Parameter *arg = new Parameter(STCundefined, e->type, NULL, NULL, NULL);
- (*arguments)[i] = arg;
- }
- }
- this->arguments = arguments;
- //printf("TypeTuple() %p, %s\n", this, toChars());
-}
-
-TypeTuple *TypeTuple::create(Parameters *arguments)
-{
- return new TypeTuple(arguments);
-}
-
-/*******************************************
- * Type tuple with 0, 1 or 2 types in it.
- */
-TypeTuple::TypeTuple()
- : Type(Ttuple)
-{
- arguments = new Parameters();
-}
-
-TypeTuple::TypeTuple(Type *t1)
- : Type(Ttuple)
-{
- arguments = new Parameters();
- arguments->push(new Parameter(0, t1, NULL, NULL, NULL));
-}
-
-TypeTuple::TypeTuple(Type *t1, Type *t2)
- : Type(Ttuple)
-{
- arguments = new Parameters();
- arguments->push(new Parameter(0, t1, NULL, NULL, NULL));
- arguments->push(new Parameter(0, t2, NULL, NULL, NULL));
-}
-
-const char *TypeTuple::kind()
-{
- return "tuple";
-}
-
-Type *TypeTuple::syntaxCopy()
-{
- Parameters *args = Parameter::arraySyntaxCopy(arguments);
- Type *t = new TypeTuple(args);
- t->mod = mod;
- return t;
-}
-
-bool TypeTuple::equals(RootObject *o)
-{
- Type *t = (Type *)o;
- //printf("TypeTuple::equals(%s, %s)\n", toChars(), t->toChars());
- if (this == t)
- return true;
- if (t->ty == Ttuple)
- {
- TypeTuple *tt = (TypeTuple *)t;
- if (arguments->length == tt->arguments->length)
- {
- for (size_t i = 0; i < tt->arguments->length; i++)
- {
- Parameter *arg1 = (*arguments)[i];
- Parameter *arg2 = (*tt->arguments)[i];
- if (!arg1->type->equals(arg2->type))
- return false;
- }
- return true;
- }
- }
- return false;
-}
-
-Expression *TypeTuple::getProperty(Loc loc, Identifier *ident, int flag)
-{
- Expression *e;
-
- if (ident == Id::length)
- {
- e = new IntegerExp(loc, arguments->length, Type::tsize_t);
- }
- else if (ident == Id::_init)
- {
- e = defaultInitLiteral(loc);
- }
- else if (flag)
- {
- e = NULL;
- }
- else
- {
- error(loc, "no property `%s` for tuple `%s`", ident->toChars(), toChars());
- e = new ErrorExp();
- }
- return e;
-}
-
-Expression *TypeTuple::defaultInit(Loc loc)
-{
- Expressions *exps = new Expressions();
- exps->setDim(arguments->length);
- for (size_t i = 0; i < arguments->length; i++)
- {
- Parameter *p = (*arguments)[i];
- assert(p->type);
- Expression *e = p->type->defaultInitLiteral(loc);
- if (e->op == TOKerror)
- return e;
- (*exps)[i] = e;
- }
- return new TupleExp(loc, exps);
-}
-
-/***************************** TypeSlice *****************************/
-
-/* This is so we can slice a TypeTuple */
-
-TypeSlice::TypeSlice(Type *next, Expression *lwr, Expression *upr)
- : TypeNext(Tslice, next)
-{
- //printf("TypeSlice[%s .. %s]\n", lwr->toChars(), upr->toChars());
- this->lwr = lwr;
- this->upr = upr;
-}
-
-const char *TypeSlice::kind()
-{
- return "slice";
-}
-
-Type *TypeSlice::syntaxCopy()
-{
- Type *t = new TypeSlice(next->syntaxCopy(), lwr->syntaxCopy(), upr->syntaxCopy());
- t->mod = mod;
- return t;
-}
-
-void TypeSlice::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
-{
- next->resolve(loc, sc, pe, pt, ps, intypeid);
- if (*pe)
- {
- // It's really a slice expression
- if (Dsymbol *s = getDsymbol(*pe))
- *pe = new DsymbolExp(loc, s);
- *pe = new ArrayExp(loc, *pe, new IntervalExp(loc, lwr, upr));
- }
- else if (*ps)
- {
- Dsymbol *s = *ps;
- TupleDeclaration *td = s->isTupleDeclaration();
- if (td)
- {
- /* It's a slice of a TupleDeclaration
- */
- ScopeDsymbol *sym = new ArrayScopeSymbol(sc, td);
- sym->parent = sc->scopesym;
- sc = sc->push(sym);
- sc = sc->startCTFE();
- lwr = expressionSemantic(lwr, sc);
- upr = expressionSemantic(upr, sc);
- sc = sc->endCTFE();
- sc = sc->pop();
-
- lwr = lwr->ctfeInterpret();
- upr = upr->ctfeInterpret();
- uinteger_t i1 = lwr->toUInteger();
- uinteger_t i2 = upr->toUInteger();
-
- if (!(i1 <= i2 && i2 <= td->objects->length))
- {
- error(loc, "slice [%llu..%llu] is out of range of [0..%u]", i1, i2, td->objects->length);
- *ps = NULL;
- *pt = Type::terror;
- return;
- }
-
- if (i1 == 0 && i2 == td->objects->length)
- {
- *ps = td;
- return;
- }
-
- /* Create a new TupleDeclaration which
- * is a slice [i1..i2] out of the old one.
- */
- Objects *objects = new Objects;
- objects->setDim((size_t)(i2 - i1));
- for (size_t i = 0; i < objects->length; i++)
- {
- (*objects)[i] = (*td->objects)[(size_t)i1 + i];
- }
-
- TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects);
- *ps = tds;
- }
- else
- goto Ldefault;
- }
- else
- {
- if ((*pt)->ty != Terror)
- next = *pt; // prevent re-running semantic() on 'next'
- Ldefault:
- Type::resolve(loc, sc, pe, pt, ps, intypeid);
- }
-}
-
-/***************************** TypeNull *****************************/
-
-TypeNull::TypeNull()
- : Type(Tnull)
-{
-}
-
-const char *TypeNull::kind()
-{
- return "null";
-}
-
-Type *TypeNull::syntaxCopy()
-{
- // No semantic analysis done, no need to copy
- return this;
-}
-
-MATCH TypeNull::implicitConvTo(Type *to)
-{
- //printf("TypeNull::implicitConvTo(this=%p, to=%p)\n", this, to);
- //printf("from: %s\n", toChars());
- //printf("to : %s\n", to->toChars());
- MATCH m = Type::implicitConvTo(to);
- if (m != MATCHnomatch)
- return m;
-
- // NULL implicitly converts to any pointer type or dynamic array
- //if (type->ty == Tpointer && type->nextOf()->ty == Tvoid)
- {
- Type *tb = to->toBasetype();
- if (tb->ty == Tnull ||
- tb->ty == Tpointer || tb->ty == Tarray ||
- tb->ty == Taarray || tb->ty == Tclass ||
- tb->ty == Tdelegate)
- return MATCHconst;
- }
-
- return MATCHnomatch;
-}
-
-bool TypeNull::isBoolean()
-{
- return true;
-}
-
-d_uns64 TypeNull::size(Loc loc)
-{
- return tvoidptr->size(loc);
-}
-
-Expression *TypeNull::defaultInit(Loc)
-{
- return new NullExp(Loc(), Type::tnull);
-}
-
-/***************************** TypeNoreturn *****************************/
-
-TypeNoreturn::TypeNoreturn()
- : Type(Tnoreturn)
-{
- //printf("TypeNoreturn %p\n", this);
-}
-
-const char *TypeNoreturn::kind()
-{
- return "noreturn";
-}
-
-Type *TypeNoreturn::syntaxCopy()
-{
- // No semantic analysis done, no need to copy
- return this;
-}
-
-MATCH TypeNoreturn::implicitConvTo(Type *to)
-{
- //printf("TypeNoreturn::implicitConvTo(this=%p, to=%p)\n", this, to);
- //printf("from: %s\n", toChars());
- //printf("to : %s\n", to.toChars());
- MATCH m = Type::implicitConvTo(to);
- return (m == MATCHexact) ? MATCHexact : MATCHconvert;
-}
-
-bool TypeNoreturn::isBoolean()
-{
- return true; // bottom type can be implicitly converted to any other type
-}
-
-d_uns64 TypeNoreturn::size(Loc)
-{
- return 0;
-}
-
-unsigned TypeNoreturn::alignsize()
-{
- return 0;
-}
-
-/***********************************************************
- * Encapsulate Parameters* so .length and [i] can be used on it.
- * https://dlang.org/spec/function.html#ParameterList
- */
-
-ParameterList::ParameterList(Parameters *parameters, VarArg varargs)
-{
- this->parameters = parameters;
- this->varargs = varargs;
-}
-
-size_t ParameterList::length()
-{
- return Parameter::dim(parameters);
-}
-
-/***************************** Parameter *****************************/
-
-Parameter::Parameter(StorageClass storageClass, Type *type, Identifier *ident,
- Expression *defaultArg, UserAttributeDeclaration *userAttribDecl)
-{
- this->type = type;
- this->ident = ident;
- this->storageClass = storageClass;
- this->defaultArg = defaultArg;
- this->userAttribDecl = userAttribDecl;
-}
-
-Parameter *Parameter::create(StorageClass storageClass, Type *type, Identifier *ident,
- Expression *defaultArg, UserAttributeDeclaration *userAttribDecl)
-{
- return new Parameter(storageClass, type, ident, defaultArg, userAttribDecl);
-}
-
-Parameter *Parameter::syntaxCopy()
-{
- return new Parameter(storageClass,
- type ? type->syntaxCopy() : NULL,
- ident,
- defaultArg ? defaultArg->syntaxCopy() : NULL,
- userAttribDecl ? (UserAttributeDeclaration *) userAttribDecl->syntaxCopy(NULL) : NULL);
-}
-
-Parameters *Parameter::arraySyntaxCopy(Parameters *parameters)
-{
- Parameters *params = NULL;
- if (parameters)
- {
- params = new Parameters();
- params->setDim(parameters->length);
- for (size_t i = 0; i < params->length; i++)
- (*params)[i] = (*parameters)[i]->syntaxCopy();
- }
- return params;
-}
-
-/****************************************************
- * Determine if parameter is a lazy array of delegates.
- * If so, return the return type of those delegates.
- * If not, return NULL.
- *
- * Returns T if the type is one of the following forms:
- * T delegate()[]
- * T delegate()[dim]
- */
-
-Type *Parameter::isLazyArray()
-{
- Type *tb = type->toBasetype();
- if (tb->ty == Tsarray || tb->ty == Tarray)
- {
- Type *tel = ((TypeArray *)tb)->next->toBasetype();
- if (tel->ty == Tdelegate)
- {
- TypeDelegate *td = (TypeDelegate *)tel;
- TypeFunction *tf = td->next->toTypeFunction();
-
- if (tf->parameterList.varargs == VARARGnone && tf->parameterList.length() == 0)
- {
- return tf->next; // return type of delegate
- }
- }
- }
- return NULL;
-}
-
-/***************************************
- * Determine number of arguments, folding in tuples.
- */
-
-static int dimDg(void *ctx, size_t, Parameter *)
-{
- ++*(size_t *)ctx;
- return 0;
-}
-
-size_t Parameter::dim(Parameters *parameters)
-{
- size_t n = 0;
- Parameter_foreach(parameters, &dimDg, &n);
- return n;
-}
-
-/***************************************
- * Get nth Parameter, folding in tuples.
- * Returns:
- * Parameter* nth Parameter
- * NULL not found, *pn gets incremented by the number
- * of Parameters
- */
-
-struct GetNthParamCtx
-{
- size_t nth;
- Parameter *param;
-};
-
-static int getNthParamDg(void *ctx, size_t n, Parameter *p)
-{
- GetNthParamCtx *c = (GetNthParamCtx *)ctx;
- if (n == c->nth)
- {
- c->param = p;
- return 1;
- }
- return 0;
-}
-
-Parameter *Parameter::getNth(Parameters *parameters, size_t nth, size_t *)
-{
- GetNthParamCtx ctx = { nth, NULL };
- int res = Parameter_foreach(parameters, &getNthParamDg, &ctx);
- return res ? ctx.param : NULL;
-}
-
-/***************************************
- * Expands tuples in args in depth first order. Calls
- * dg(void *ctx, size_t argidx, Parameter *arg) for each Parameter.
- * If dg returns !=0, stops and returns that value else returns 0.
- * Use this function to avoid the O(N + N^2/2) complexity of
- * calculating dim and calling N times getNth.
- */
-
-int Parameter_foreach(Parameters *parameters, ForeachDg dg, void *ctx, size_t *pn)
-{
- assert(dg);
- if (!parameters)
- return 0;
-
- size_t n = pn ? *pn : 0; // take over index
- int result = 0;
- for (size_t i = 0; i < parameters->length; i++)
- {
- Parameter *p = (*parameters)[i];
- Type *t = p->type->toBasetype();
-
- if (t->ty == Ttuple)
- {
- TypeTuple *tu = (TypeTuple *)t;
- result = Parameter_foreach(tu->arguments, dg, ctx, &n);
- }
- else
- result = dg(ctx, n++, p);
-
- if (result)
- break;
- }
-
- if (pn)
- *pn = n; // update index
- return result;
-}
-
-
-const char *Parameter::toChars()
-{
- return ident ? ident->toChars() : "__anonymous_param";
-}
-
-/*********************************
- * Compute covariance of parameters `this` and `p`
- * as determined by the storage classes of both.
- * Params:
- * p = Parameter to compare with
- * Returns:
- * true = `this` can be used in place of `p`
- * false = nope
- */
-bool Parameter::isCovariant(bool returnByRef, const Parameter *p) const
-{
- const StorageClass stc = STCref | STCin | STCout | STClazy;
- if ((this->storageClass & stc) != (p->storageClass & stc))
- return false;
-
- return isCovariantScope(returnByRef, this->storageClass, p->storageClass);
-}
-
-bool Parameter::isCovariantScope(bool returnByRef, StorageClass from, StorageClass to)
-{
- if (from == to)
- return true;
-
- struct SR
- {
- /* Classification of 'scope-return-ref' possibilities
- */
- enum
- {
- SRNone,
- SRScope,
- SRReturnScope,
- SRRef,
- SRReturnRef,
- SRRefScope,
- SRReturnRef_Scope,
- SRRef_ReturnScope,
- SRMAX,
- };
-
- /* Shrinking the representation is necessary because StorageClass is so wide
- * Params:
- * returnByRef = true if the function returns by ref
- * stc = storage class of parameter
- */
- static unsigned buildSR(bool returnByRef, StorageClass stc)
- {
- unsigned result;
- StorageClass stc2 = stc & (STCref | STCscope | STCreturn);
- if (stc2 == 0)
- result = SRNone;
- else if (stc2 == STCref)
- result = SRRef;
- else if (stc2 == STCscope)
- result = SRScope;
- else if (stc2 == (STCscope | STCreturn))
- result = SRReturnScope;
- else if (stc2 == (STCref | STCreturn))
- result = SRReturnRef;
- else if (stc2 == (STCscope | STCref))
- result = SRRefScope;
- else if (stc2 == (STCscope | STCref | STCreturn))
- result = returnByRef ? SRReturnRef_Scope : SRRef_ReturnScope;
- else
- assert(0);
- return result;
- }
-
- static void covariantInit(bool covariant[SRMAX][SRMAX])
- {
- /* Initialize covariant[][] with this:
-
- From\To n rs s
- None X
- ReturnScope X X
- Scope X X X
-
- From\To r rr rs rr-s r-rs
- Ref X X
- ReturnRef X
- RefScope X X X X X
- ReturnRef-Scope X X
- Ref-ReturnScope X X X
- */
- for (int i = 0; i < SRMAX; i++)
- {
- covariant[i][i] = true;
- covariant[SRRefScope][i] = true;
- }
- covariant[SRReturnScope][SRNone] = true;
- covariant[SRScope ][SRNone] = true;
- covariant[SRScope ][SRReturnScope] = true;
-
- covariant[SRRef ][SRReturnRef] = true;
- covariant[SRReturnRef_Scope][SRReturnRef] = true;
- covariant[SRRef_ReturnScope][SRRef ] = true;
- covariant[SRRef_ReturnScope][SRReturnRef] = true;
- }
- };
-
- /* result is true if the 'from' can be used as a 'to'
- */
-
- if ((from ^ to) & STCref) // differing in 'ref' means no covariance
- return false;
-
- static bool covariant[SR::SRMAX][SR::SRMAX];
- static bool init = false;
- if (!init)
- {
- SR::covariantInit(covariant);
- init = true;
- }
-
- return covariant[SR::buildSR(returnByRef, from)][SR::buildSR(returnByRef, to)];
-}
-
-/**
- * For printing two types with qualification when necessary.
- * Params:
- * t1 = The first type to receive the type name for
- * t2 = The second type to receive the type name for
- * Returns:
- * The fully-qualified names of both types if the two type names are not the same,
- * or the unqualified names of both types if the two type names are the same.
- */
-void toAutoQualChars(const char **result, Type *t1, Type *t2)
-{
- const char *s1 = t1->toChars();
- const char *s2 = t2->toChars();
- if (strcmp(s1, s2) == 0)
- {
- s1 = t1->toPrettyChars(true);
- s2 = t2->toPrettyChars(true);
- }
- result[0] = s1;
- result[1] = s2;
-}
diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d
new file mode 100644
index 00000000000..80e47918f2d
--- /dev/null
+++ b/gcc/d/dmd/mtype.d
@@ -0,0 +1,7355 @@
+/**
+ * Defines a D type.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mtype.d, _mtype.d)
+ * Documentation: https://dlang.org/phobos/dmd_mtype.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mtype.d
+ */
+
+module dmd.mtype;
+
+import core.checkedint;
+import core.stdc.stdarg;
+import core.stdc.stdio;
+import core.stdc.stdlib;
+import core.stdc.string;
+
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.attrib;
+import dmd.astenums;
+import dmd.ast_node;
+import dmd.gluelayer;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dmangle;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.opover;
+import dmd.root.ctfloat;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.root.stringtable;
+import dmd.target;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.visitor;
+
+enum LOGDOTEXP = 0; // log ::dotExp()
+enum LOGDEFAULTINIT = 0; // log ::defaultInit()
+
+enum SIZE_INVALID = (~cast(d_uns64)0); // error return from size() functions
+
+
+/***************************
+ * Return !=0 if modfrom can be implicitly converted to modto
+ */
+bool MODimplicitConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe
+{
+ if (modfrom == modto)
+ return true;
+
+ //printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto);
+ auto X(T, U)(T m, U n)
+ {
+ return ((m << 4) | n);
+ }
+
+ switch (X(modfrom & ~MODFlags.shared_, modto & ~MODFlags.shared_))
+ {
+ case X(0, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.wildconst, MODFlags.const_):
+ return (modfrom & MODFlags.shared_) == (modto & MODFlags.shared_);
+
+ case X(MODFlags.immutable_, MODFlags.const_):
+ case X(MODFlags.immutable_, MODFlags.wildconst):
+ return true;
+ default:
+ return false;
+ }
+}
+
+/***************************
+ * Return MATCH.exact or MATCH.constant if a method of type '() modfrom' can call a method of type '() modto'.
+ */
+MATCH MODmethodConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe
+{
+ if (modfrom == modto)
+ return MATCH.exact;
+ if (MODimplicitConv(modfrom, modto))
+ return MATCH.constant;
+
+ auto X(T, U)(T m, U n)
+ {
+ return ((m << 4) | n);
+ }
+
+ switch (X(modfrom, modto))
+ {
+ case X(0, MODFlags.wild):
+ case X(MODFlags.immutable_, MODFlags.wild):
+ case X(MODFlags.const_, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.immutable_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ return MATCH.constant;
+
+ default:
+ return MATCH.nomatch;
+ }
+}
+
+/***************************
+ * Merge mod bits to form common mod.
+ */
+MOD MODmerge(MOD mod1, MOD mod2) pure nothrow @nogc @safe
+{
+ if (mod1 == mod2)
+ return mod1;
+
+ //printf("MODmerge(1 = %x, 2 = %x)\n", mod1, mod2);
+ MOD result = 0;
+ if ((mod1 | mod2) & MODFlags.shared_)
+ {
+ // If either type is shared, the result will be shared
+ result |= MODFlags.shared_;
+ mod1 &= ~MODFlags.shared_;
+ mod2 &= ~MODFlags.shared_;
+ }
+ if (mod1 == 0 || mod1 == MODFlags.mutable || mod1 == MODFlags.const_ || mod2 == 0 || mod2 == MODFlags.mutable || mod2 == MODFlags.const_)
+ {
+ // If either type is mutable or const, the result will be const.
+ result |= MODFlags.const_;
+ }
+ else
+ {
+ // MODFlags.immutable_ vs MODFlags.wild
+ // MODFlags.immutable_ vs MODFlags.wildconst
+ // MODFlags.wild vs MODFlags.wildconst
+ assert(mod1 & MODFlags.wild || mod2 & MODFlags.wild);
+ result |= MODFlags.wildconst;
+ }
+ return result;
+}
+
+/*********************************
+ * Store modifier name into buf.
+ */
+void MODtoBuffer(OutBuffer* buf, MOD mod) nothrow
+{
+ buf.writestring(MODtoString(mod));
+}
+
+/*********************************
+ * Returns:
+ * a human readable representation of `mod`,
+ * which is the token `mod` corresponds to
+ */
+const(char)* MODtoChars(MOD mod) nothrow pure
+{
+ /// Works because we return a literal
+ return MODtoString(mod).ptr;
+}
+
+/// Ditto
+string MODtoString(MOD mod) nothrow pure
+{
+ final switch (mod)
+ {
+ case 0:
+ return "";
+
+ case MODFlags.immutable_:
+ return "immutable";
+
+ case MODFlags.shared_:
+ return "shared";
+
+ case MODFlags.shared_ | MODFlags.const_:
+ return "shared const";
+
+ case MODFlags.const_:
+ return "const";
+
+ case MODFlags.shared_ | MODFlags.wild:
+ return "shared inout";
+
+ case MODFlags.wild:
+ return "inout";
+
+ case MODFlags.shared_ | MODFlags.wildconst:
+ return "shared inout const";
+
+ case MODFlags.wildconst:
+ return "inout const";
+ }
+}
+
+
+/************************************
+ * Convert MODxxxx to STCxxx
+ */
+StorageClass ModToStc(uint mod) pure nothrow @nogc @safe
+{
+ StorageClass stc = 0;
+ if (mod & MODFlags.immutable_)
+ stc |= STC.immutable_;
+ if (mod & MODFlags.const_)
+ stc |= STC.const_;
+ if (mod & MODFlags.wild)
+ stc |= STC.wild;
+ if (mod & MODFlags.shared_)
+ stc |= STC.shared_;
+ return stc;
+}
+
+///Returns true if ty is char, wchar, or dchar
+bool isSomeChar(TY ty) pure nothrow @nogc @safe
+{
+ return ty == Tchar || ty == Twchar || ty == Tdchar;
+}
+
+/****************
+ * dotExp() bit flags
+ */
+enum DotExpFlag
+{
+ gag = 1, // don't report "not a property" error and just return null
+ noDeref = 2, // the use of the expression will not attempt a dereference
+}
+
+/// Result of a check whether two types are covariant
+enum Covariant
+{
+ distinct = 0, /// types are distinct
+ yes = 1, /// types are covariant
+ no = 2, /// arguments match as far as overloading goes, but types are not covariant
+ fwdref = 3, /// cannot determine covariance because of forward references
+}
+
+/***********************************************************
+ */
+extern (C++) abstract class Type : ASTNode
+{
+ TY ty;
+ MOD mod; // modifiers MODxxxx
+ char* deco;
+
+ static struct Mcache
+ {
+ /* These are cached values that are lazily evaluated by constOf(), immutableOf(), etc.
+ * They should not be referenced by anybody but mtype.d.
+ * They can be null if not lazily evaluated yet.
+ * Note that there is no "shared immutable", because that is just immutable
+ * The point of this is to reduce the size of each Type instance as
+ * we bank on the idea that usually only one of variants exist.
+ * It will also speed up code because these are rarely referenced and
+ * so need not be in the cache.
+ */
+ Type cto; // MODFlags.const_
+ Type ito; // MODFlags.immutable_
+ Type sto; // MODFlags.shared_
+ Type scto; // MODFlags.shared_ | MODFlags.const_
+ Type wto; // MODFlags.wild
+ Type wcto; // MODFlags.wildconst
+ Type swto; // MODFlags.shared_ | MODFlags.wild
+ Type swcto; // MODFlags.shared_ | MODFlags.wildconst
+ }
+ private Mcache* mcache;
+
+ Type pto; // merged pointer to this type
+ Type rto; // reference to this type
+ Type arrayof; // array of this type
+
+ TypeInfoDeclaration vtinfo; // TypeInfo object for this Type
+
+ type* ctype; // for back end
+
+ extern (C++) __gshared Type tvoid;
+ extern (C++) __gshared Type tint8;
+ extern (C++) __gshared Type tuns8;
+ extern (C++) __gshared Type tint16;
+ extern (C++) __gshared Type tuns16;
+ extern (C++) __gshared Type tint32;
+ extern (C++) __gshared Type tuns32;
+ extern (C++) __gshared Type tint64;
+ extern (C++) __gshared Type tuns64;
+ extern (C++) __gshared Type tint128;
+ extern (C++) __gshared Type tuns128;
+ extern (C++) __gshared Type tfloat32;
+ extern (C++) __gshared Type tfloat64;
+ extern (C++) __gshared Type tfloat80;
+ extern (C++) __gshared Type timaginary32;
+ extern (C++) __gshared Type timaginary64;
+ extern (C++) __gshared Type timaginary80;
+ extern (C++) __gshared Type tcomplex32;
+ extern (C++) __gshared Type tcomplex64;
+ extern (C++) __gshared Type tcomplex80;
+ extern (C++) __gshared Type tbool;
+ extern (C++) __gshared Type tchar;
+ extern (C++) __gshared Type twchar;
+ extern (C++) __gshared Type tdchar;
+
+ // Some special types
+ extern (C++) __gshared Type tshiftcnt;
+ extern (C++) __gshared Type tvoidptr; // void*
+ extern (C++) __gshared Type tstring; // immutable(char)[]
+ extern (C++) __gshared Type twstring; // immutable(wchar)[]
+ extern (C++) __gshared Type tdstring; // immutable(dchar)[]
+ extern (C++) __gshared Type terror; // for error recovery
+ extern (C++) __gshared Type tnull; // for null type
+ extern (C++) __gshared Type tnoreturn; // for bottom type typeof(*null)
+
+ extern (C++) __gshared Type tsize_t; // matches size_t alias
+ extern (C++) __gshared Type tptrdiff_t; // matches ptrdiff_t alias
+ extern (C++) __gshared Type thash_t; // matches hash_t alias
+
+ extern (C++) __gshared ClassDeclaration dtypeinfo;
+ extern (C++) __gshared ClassDeclaration typeinfoclass;
+ extern (C++) __gshared ClassDeclaration typeinfointerface;
+ extern (C++) __gshared ClassDeclaration typeinfostruct;
+ extern (C++) __gshared ClassDeclaration typeinfopointer;
+ extern (C++) __gshared ClassDeclaration typeinfoarray;
+ extern (C++) __gshared ClassDeclaration typeinfostaticarray;
+ extern (C++) __gshared ClassDeclaration typeinfoassociativearray;
+ extern (C++) __gshared ClassDeclaration typeinfovector;
+ extern (C++) __gshared ClassDeclaration typeinfoenum;
+ extern (C++) __gshared ClassDeclaration typeinfofunction;
+ extern (C++) __gshared ClassDeclaration typeinfodelegate;
+ extern (C++) __gshared ClassDeclaration typeinfotypelist;
+ extern (C++) __gshared ClassDeclaration typeinfoconst;
+ extern (C++) __gshared ClassDeclaration typeinfoinvariant;
+ extern (C++) __gshared ClassDeclaration typeinfoshared;
+ extern (C++) __gshared ClassDeclaration typeinfowild;
+
+ extern (C++) __gshared TemplateDeclaration rtinfo;
+
+ extern (C++) __gshared Type[TMAX] basic;
+
+ extern (D) __gshared StringTable!Type stringtable;
+ extern (D) private __gshared ubyte[TMAX] sizeTy = ()
+ {
+ ubyte[TMAX] sizeTy = __traits(classInstanceSize, TypeBasic);
+ sizeTy[Tsarray] = __traits(classInstanceSize, TypeSArray);
+ sizeTy[Tarray] = __traits(classInstanceSize, TypeDArray);
+ sizeTy[Taarray] = __traits(classInstanceSize, TypeAArray);
+ sizeTy[Tpointer] = __traits(classInstanceSize, TypePointer);
+ sizeTy[Treference] = __traits(classInstanceSize, TypeReference);
+ sizeTy[Tfunction] = __traits(classInstanceSize, TypeFunction);
+ sizeTy[Tdelegate] = __traits(classInstanceSize, TypeDelegate);
+ sizeTy[Tident] = __traits(classInstanceSize, TypeIdentifier);
+ sizeTy[Tinstance] = __traits(classInstanceSize, TypeInstance);
+ sizeTy[Ttypeof] = __traits(classInstanceSize, TypeTypeof);
+ sizeTy[Tenum] = __traits(classInstanceSize, TypeEnum);
+ sizeTy[Tstruct] = __traits(classInstanceSize, TypeStruct);
+ sizeTy[Tclass] = __traits(classInstanceSize, TypeClass);
+ sizeTy[Ttuple] = __traits(classInstanceSize, TypeTuple);
+ sizeTy[Tslice] = __traits(classInstanceSize, TypeSlice);
+ sizeTy[Treturn] = __traits(classInstanceSize, TypeReturn);
+ sizeTy[Terror] = __traits(classInstanceSize, TypeError);
+ sizeTy[Tnull] = __traits(classInstanceSize, TypeNull);
+ sizeTy[Tvector] = __traits(classInstanceSize, TypeVector);
+ sizeTy[Ttraits] = __traits(classInstanceSize, TypeTraits);
+ sizeTy[Tmixin] = __traits(classInstanceSize, TypeMixin);
+ sizeTy[Tnoreturn] = __traits(classInstanceSize, TypeNoreturn);
+ sizeTy[Ttag] = __traits(classInstanceSize, TypeTag);
+ return sizeTy;
+ }();
+
+ final extern (D) this(TY ty)
+ {
+ this.ty = ty;
+ }
+
+ const(char)* kind() const nothrow pure @nogc @safe
+ {
+ assert(false); // should be overridden
+ }
+
+ final Type copy() nothrow const
+ {
+ Type t = cast(Type)mem.xmalloc(sizeTy[ty]);
+ memcpy(cast(void*)t, cast(void*)this, sizeTy[ty]);
+ return t;
+ }
+
+ Type syntaxCopy()
+ {
+ fprintf(stderr, "this = %s, ty = %d\n", toChars(), ty);
+ assert(0);
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ Type t = cast(Type)o;
+ //printf("Type::equals(%s, %s)\n", toChars(), t.toChars());
+ // deco strings are unique
+ // and semantic() has been run
+ if (this == o || ((t && deco == t.deco) && deco !is null))
+ {
+ //printf("deco = '%s', t.deco = '%s'\n", deco, t.deco);
+ return true;
+ }
+ //if (deco && t && t.deco) printf("deco = '%s', t.deco = '%s'\n", deco, t.deco);
+ return false;
+ }
+
+ final bool equivalent(Type t)
+ {
+ return immutableOf().equals(t.immutableOf());
+ }
+
+ // kludge for template.isType()
+ override final DYNCAST dyncast() const
+ {
+ return DYNCAST.type;
+ }
+
+ extern (D)
+ final Mcache* getMcache()
+ {
+ if (!mcache)
+ mcache = cast(Mcache*) mem.xcalloc(Mcache.sizeof, 1);
+ return mcache;
+ }
+
+ /*******************************
+ * Covariant means that 'this' can substitute for 't',
+ * i.e. a pure function is a match for an impure type.
+ * Params:
+ * t = type 'this' is covariant with
+ * pstc = if not null, store STCxxxx which would make it covariant
+ * Returns:
+ * An enum value of either `Covariant.yes` or a reason it's not covariant.
+ */
+ final Covariant covariant(Type t, StorageClass* pstc = null)
+ {
+ version (none)
+ {
+ printf("Type::covariant(t = %s) %s\n", t.toChars(), toChars());
+ printf("deco = %p, %p\n", deco, t.deco);
+ // printf("ty = %d\n", next.ty);
+ printf("mod = %x, %x\n", mod, t.mod);
+ }
+ if (pstc)
+ *pstc = 0;
+ StorageClass stc = 0;
+
+ bool notcovariant = false;
+
+ if (equals(t))
+ return Covariant.yes;
+
+ TypeFunction t1 = this.isTypeFunction();
+ TypeFunction t2 = t.isTypeFunction();
+
+ if (!t1 || !t2)
+ goto Ldistinct;
+
+ if (t1.parameterList.varargs != t2.parameterList.varargs)
+ goto Ldistinct;
+
+ if (t1.parameterList.parameters && t2.parameterList.parameters)
+ {
+ if (t1.parameterList.length != t2.parameterList.length)
+ goto Ldistinct;
+
+ foreach (i, fparam1; t1.parameterList)
+ {
+ Parameter fparam2 = t2.parameterList[i];
+
+ if (!fparam1.type.equals(fparam2.type))
+ {
+ Type tp1 = fparam1.type;
+ Type tp2 = fparam2.type;
+ if (tp1.ty == tp2.ty)
+ {
+ if (auto tc1 = tp1.isTypeClass())
+ {
+ if (tc1.sym == (cast(TypeClass)tp2).sym && MODimplicitConv(tp2.mod, tp1.mod))
+ goto Lcov;
+ }
+ else if (auto ts1 = tp1.isTypeStruct())
+ {
+ if (ts1.sym == (cast(TypeStruct)tp2).sym && MODimplicitConv(tp2.mod, tp1.mod))
+ goto Lcov;
+ }
+ else if (tp1.ty == Tpointer)
+ {
+ if (tp2.implicitConvTo(tp1))
+ goto Lcov;
+ }
+ else if (tp1.ty == Tarray)
+ {
+ if (tp2.implicitConvTo(tp1))
+ goto Lcov;
+ }
+ else if (tp1.ty == Tdelegate)
+ {
+ if (tp1.implicitConvTo(tp2))
+ goto Lcov;
+ }
+ }
+ goto Ldistinct;
+ }
+ Lcov:
+ notcovariant |= !fparam1.isCovariant(t1.isref, fparam2);
+ }
+ }
+ else if (t1.parameterList.parameters != t2.parameterList.parameters)
+ {
+ if (t1.parameterList.length || t2.parameterList.length)
+ goto Ldistinct;
+ }
+
+ // The argument lists match
+ if (notcovariant)
+ goto Lnotcovariant;
+ if (t1.linkage != t2.linkage)
+ goto Lnotcovariant;
+
+ {
+ // Return types
+ Type t1n = t1.next;
+ Type t2n = t2.next;
+
+ if (!t1n || !t2n) // happens with return type inference
+ goto Lnotcovariant;
+
+ if (t1n.equals(t2n))
+ goto Lcovariant;
+ if (t1n.ty == Tclass && t2n.ty == Tclass)
+ {
+ /* If same class type, but t2n is const, then it's
+ * covariant. Do this test first because it can work on
+ * forward references.
+ */
+ if ((cast(TypeClass)t1n).sym == (cast(TypeClass)t2n).sym && MODimplicitConv(t1n.mod, t2n.mod))
+ goto Lcovariant;
+
+ // If t1n is forward referenced:
+ ClassDeclaration cd = (cast(TypeClass)t1n).sym;
+ if (cd.semanticRun < PASS.semanticdone && !cd.isBaseInfoComplete())
+ cd.dsymbolSemantic(null);
+ if (!cd.isBaseInfoComplete())
+ {
+ return Covariant.fwdref;
+ }
+ }
+ if (t1n.ty == Tstruct && t2n.ty == Tstruct)
+ {
+ if ((cast(TypeStruct)t1n).sym == (cast(TypeStruct)t2n).sym && MODimplicitConv(t1n.mod, t2n.mod))
+ goto Lcovariant;
+ }
+ else if (t1n.ty == t2n.ty && t1n.implicitConvTo(t2n))
+ goto Lcovariant;
+ else if (t1n.ty == Tnull)
+ {
+ // NULL is covariant with any pointer type, but not with any
+ // dynamic arrays, associative arrays or delegates.
+ // https://issues.dlang.org/show_bug.cgi?id=8589
+ // https://issues.dlang.org/show_bug.cgi?id=19618
+ Type t2bn = t2n.toBasetype();
+ if (t2bn.ty == Tnull || t2bn.ty == Tpointer || t2bn.ty == Tclass)
+ goto Lcovariant;
+ }
+ // bottom type is covariant to any type
+ else if (t1n.ty == Tnoreturn)
+ goto Lcovariant;
+ }
+ goto Lnotcovariant;
+
+ Lcovariant:
+ if (t1.isref != t2.isref)
+ goto Lnotcovariant;
+
+ if (!t1.isref && (t1.isScopeQual || t2.isScopeQual))
+ {
+ StorageClass stc1 = t1.isScopeQual ? STC.scope_ : 0;
+ StorageClass stc2 = t2.isScopeQual ? STC.scope_ : 0;
+ if (t1.isreturn)
+ {
+ stc1 |= STC.return_;
+ if (!t1.isScopeQual)
+ stc1 |= STC.ref_;
+ }
+ if (t2.isreturn)
+ {
+ stc2 |= STC.return_;
+ if (!t2.isScopeQual)
+ stc2 |= STC.ref_;
+ }
+ if (!Parameter.isCovariantScope(t1.isref, stc1, stc2))
+ goto Lnotcovariant;
+ }
+
+ // We can subtract 'return ref' from 'this', but cannot add it
+ else if (t1.isreturn && !t2.isreturn)
+ goto Lnotcovariant;
+
+ /* Can convert mutable to const
+ */
+ if (!MODimplicitConv(t2.mod, t1.mod))
+ {
+ version (none)
+ {
+ //stop attribute inference with const
+ // If adding 'const' will make it covariant
+ if (MODimplicitConv(t2.mod, MODmerge(t1.mod, MODFlags.const_)))
+ stc |= STC.const_;
+ else
+ goto Lnotcovariant;
+ }
+ else
+ {
+ goto Ldistinct;
+ }
+ }
+
+ /* Can convert pure to impure, nothrow to throw, and nogc to gc
+ */
+ if (!t1.purity && t2.purity)
+ stc |= STC.pure_;
+
+ if (!t1.isnothrow && t2.isnothrow)
+ stc |= STC.nothrow_;
+
+ if (!t1.isnogc && t2.isnogc)
+ stc |= STC.nogc;
+
+ /* Can convert safe/trusted to system
+ */
+ if (t1.trust <= TRUST.system && t2.trust >= TRUST.trusted)
+ {
+ // Should we infer trusted or safe? Go with safe.
+ stc |= STC.safe;
+ }
+
+ if (stc)
+ {
+ if (pstc)
+ *pstc = stc;
+ goto Lnotcovariant;
+ }
+
+ //printf("\tcovaraint: 1\n");
+ return Covariant.yes;
+
+ Ldistinct:
+ //printf("\tcovaraint: 0\n");
+ return Covariant.distinct;
+
+ Lnotcovariant:
+ //printf("\tcovaraint: 2\n");
+ return Covariant.no;
+ }
+
+ /********************************
+ * For pretty-printing a type.
+ */
+ final override const(char)* toChars() const
+ {
+ OutBuffer buf;
+ buf.reserve(16);
+ HdrGenState hgs;
+ hgs.fullQual = (ty == Tclass && !mod);
+
+ .toCBuffer(this, &buf, null, &hgs);
+ return buf.extractChars();
+ }
+
+ /// ditto
+ final char* toPrettyChars(bool QualifyTypes = false)
+ {
+ OutBuffer buf;
+ buf.reserve(16);
+ HdrGenState hgs;
+ hgs.fullQual = QualifyTypes;
+
+ .toCBuffer(this, &buf, null, &hgs);
+ return buf.extractChars();
+ }
+
+ static void _init()
+ {
+ stringtable._init(14_000);
+
+ // Set basic types
+ __gshared TY* basetab =
+ [
+ Tvoid,
+ Tint8,
+ Tuns8,
+ Tint16,
+ Tuns16,
+ Tint32,
+ Tuns32,
+ Tint64,
+ Tuns64,
+ Tint128,
+ Tuns128,
+ Tfloat32,
+ Tfloat64,
+ Tfloat80,
+ Timaginary32,
+ Timaginary64,
+ Timaginary80,
+ Tcomplex32,
+ Tcomplex64,
+ Tcomplex80,
+ Tbool,
+ Tchar,
+ Twchar,
+ Tdchar,
+ Terror
+ ];
+
+ for (size_t i = 0; basetab[i] != Terror; i++)
+ {
+ Type t = new TypeBasic(basetab[i]);
+ t = t.merge();
+ basic[basetab[i]] = t;
+ }
+ basic[Terror] = new TypeError();
+
+ tnoreturn = new TypeNoreturn();
+ tnoreturn.deco = tnoreturn.merge().deco;
+ basic[Tnoreturn] = tnoreturn;
+
+ tvoid = basic[Tvoid];
+ tint8 = basic[Tint8];
+ tuns8 = basic[Tuns8];
+ tint16 = basic[Tint16];
+ tuns16 = basic[Tuns16];
+ tint32 = basic[Tint32];
+ tuns32 = basic[Tuns32];
+ tint64 = basic[Tint64];
+ tuns64 = basic[Tuns64];
+ tint128 = basic[Tint128];
+ tuns128 = basic[Tuns128];
+ tfloat32 = basic[Tfloat32];
+ tfloat64 = basic[Tfloat64];
+ tfloat80 = basic[Tfloat80];
+
+ timaginary32 = basic[Timaginary32];
+ timaginary64 = basic[Timaginary64];
+ timaginary80 = basic[Timaginary80];
+
+ tcomplex32 = basic[Tcomplex32];
+ tcomplex64 = basic[Tcomplex64];
+ tcomplex80 = basic[Tcomplex80];
+
+ tbool = basic[Tbool];
+ tchar = basic[Tchar];
+ twchar = basic[Twchar];
+ tdchar = basic[Tdchar];
+
+ tshiftcnt = tint32;
+ terror = basic[Terror];
+ tnoreturn = basic[Tnoreturn];
+ tnull = new TypeNull();
+ tnull.deco = tnull.merge().deco;
+
+ tvoidptr = tvoid.pointerTo();
+ tstring = tchar.immutableOf().arrayOf();
+ twstring = twchar.immutableOf().arrayOf();
+ tdstring = tdchar.immutableOf().arrayOf();
+
+ const isLP64 = target.isLP64;
+
+ tsize_t = basic[isLP64 ? Tuns64 : Tuns32];
+ tptrdiff_t = basic[isLP64 ? Tint64 : Tint32];
+ thash_t = tsize_t;
+ }
+
+ /**
+ * Deinitializes the global state of the compiler.
+ *
+ * This can be used to restore the state set by `_init` to its original
+ * state.
+ */
+ static void deinitialize()
+ {
+ stringtable = stringtable.init;
+ }
+
+ final d_uns64 size()
+ {
+ return size(Loc.initial);
+ }
+
+ d_uns64 size(const ref Loc loc)
+ {
+ error(loc, "no size for type `%s`", toChars());
+ return SIZE_INVALID;
+ }
+
+ uint alignsize()
+ {
+ return cast(uint)size(Loc.initial);
+ }
+
+ final Type trySemantic(const ref Loc loc, Scope* sc)
+ {
+ //printf("+trySemantic(%s) %d\n", toChars(), global.errors);
+
+ // Needed to display any deprecations that were gagged
+ auto tcopy = this.syntaxCopy();
+
+ const errors = global.startGagging();
+ Type t = typeSemantic(this, loc, sc);
+ if (global.endGagging(errors) || t.ty == Terror) // if any errors happened
+ {
+ t = null;
+ }
+ else
+ {
+ // If `typeSemantic` succeeded, there may have been deprecations that
+ // were gagged due the the `startGagging` above. Run again to display
+ // those deprecations. https://issues.dlang.org/show_bug.cgi?id=19107
+ if (global.gaggedWarnings > 0)
+ typeSemantic(tcopy, loc, sc);
+ }
+ //printf("-trySemantic(%s) %d\n", toChars(), global.errors);
+ return t;
+ }
+
+ /*************************************
+ * This version does a merge even if the deco is already computed.
+ * Necessary for types that have a deco, but are not merged.
+ */
+ final Type merge2()
+ {
+ //printf("merge2(%s)\n", toChars());
+ Type t = this;
+ assert(t);
+ if (!t.deco)
+ return t.merge();
+
+ auto sv = stringtable.lookup(t.deco, strlen(t.deco));
+ if (sv && sv.value)
+ {
+ t = sv.value;
+ assert(t.deco);
+ }
+ else
+ assert(0);
+ return t;
+ }
+
+ /*********************************
+ * Store this type's modifier name into buf.
+ */
+ final void modToBuffer(OutBuffer* buf) nothrow const
+ {
+ if (mod)
+ {
+ buf.writeByte(' ');
+ MODtoBuffer(buf, mod);
+ }
+ }
+
+ /*********************************
+ * Return this type's modifier name.
+ */
+ final char* modToChars() nothrow const
+ {
+ OutBuffer buf;
+ buf.reserve(16);
+ modToBuffer(&buf);
+ return buf.extractChars();
+ }
+
+ bool isintegral()
+ {
+ return false;
+ }
+
+ // real, imaginary, or complex
+ bool isfloating()
+ {
+ return false;
+ }
+
+ bool isreal()
+ {
+ return false;
+ }
+
+ bool isimaginary()
+ {
+ return false;
+ }
+
+ bool iscomplex()
+ {
+ return false;
+ }
+
+ bool isscalar()
+ {
+ return false;
+ }
+
+ bool isunsigned()
+ {
+ return false;
+ }
+
+ bool isscope()
+ {
+ return false;
+ }
+
+ bool isString()
+ {
+ return false;
+ }
+
+ /**************************
+ * When T is mutable,
+ * Given:
+ * T a, b;
+ * Can we bitwise assign:
+ * a = b;
+ * ?
+ */
+ bool isAssignable()
+ {
+ return true;
+ }
+
+ /**************************
+ * Returns true if T can be converted to boolean value.
+ */
+ bool isBoolean()
+ {
+ return isscalar();
+ }
+
+ /*********************************
+ * Check type to see if it is based on a deprecated symbol.
+ */
+ void checkDeprecated(const ref Loc loc, Scope* sc)
+ {
+ if (Dsymbol s = toDsymbol(sc))
+ {
+ s.checkDeprecated(loc, sc);
+ }
+ }
+
+ final bool isConst() const nothrow pure @nogc @safe
+ {
+ return (mod & MODFlags.const_) != 0;
+ }
+
+ final bool isImmutable() const nothrow pure @nogc @safe
+ {
+ return (mod & MODFlags.immutable_) != 0;
+ }
+
+ final bool isMutable() const nothrow pure @nogc @safe
+ {
+ return (mod & (MODFlags.const_ | MODFlags.immutable_ | MODFlags.wild)) == 0;
+ }
+
+ final bool isShared() const nothrow pure @nogc @safe
+ {
+ return (mod & MODFlags.shared_) != 0;
+ }
+
+ final bool isSharedConst() const nothrow pure @nogc @safe
+ {
+ return (mod & (MODFlags.shared_ | MODFlags.const_)) == (MODFlags.shared_ | MODFlags.const_);
+ }
+
+ final bool isWild() const nothrow pure @nogc @safe
+ {
+ return (mod & MODFlags.wild) != 0;
+ }
+
+ final bool isWildConst() const nothrow pure @nogc @safe
+ {
+ return (mod & MODFlags.wildconst) == MODFlags.wildconst;
+ }
+
+ final bool isSharedWild() const nothrow pure @nogc @safe
+ {
+ return (mod & (MODFlags.shared_ | MODFlags.wild)) == (MODFlags.shared_ | MODFlags.wild);
+ }
+
+ final bool isNaked() const nothrow pure @nogc @safe
+ {
+ return mod == 0;
+ }
+
+ /********************************
+ * Return a copy of this type with all attributes null-initialized.
+ * Useful for creating a type with different modifiers.
+ */
+ final Type nullAttributes() nothrow const
+ {
+ uint sz = sizeTy[ty];
+ Type t = cast(Type)mem.xmalloc(sz);
+ memcpy(cast(void*)t, cast(void*)this, sz);
+ // t.mod = NULL; // leave mod unchanged
+ t.deco = null;
+ t.arrayof = null;
+ t.pto = null;
+ t.rto = null;
+ t.vtinfo = null;
+ t.ctype = null;
+ t.mcache = null;
+ if (t.ty == Tstruct)
+ (cast(TypeStruct)t).att = AliasThisRec.fwdref;
+ if (t.ty == Tclass)
+ (cast(TypeClass)t).att = AliasThisRec.fwdref;
+ return t;
+ }
+
+ /********************************
+ * Convert to 'const'.
+ */
+ final Type constOf()
+ {
+ //printf("Type::constOf() %p %s\n", this, toChars());
+ if (mod == MODFlags.const_)
+ return this;
+ if (mcache && mcache.cto)
+ {
+ assert(mcache.cto.mod == MODFlags.const_);
+ return mcache.cto;
+ }
+ Type t = makeConst();
+ t = t.merge();
+ t.fixTo(this);
+ //printf("-Type::constOf() %p %s\n", t, t.toChars());
+ return t;
+ }
+
+ /********************************
+ * Convert to 'immutable'.
+ */
+ final Type immutableOf()
+ {
+ //printf("Type::immutableOf() %p %s\n", this, toChars());
+ if (isImmutable())
+ return this;
+ if (mcache && mcache.ito)
+ {
+ assert(mcache.ito.isImmutable());
+ return mcache.ito;
+ }
+ Type t = makeImmutable();
+ t = t.merge();
+ t.fixTo(this);
+ //printf("\t%p\n", t);
+ return t;
+ }
+
+ /********************************
+ * Make type mutable.
+ */
+ final Type mutableOf()
+ {
+ //printf("Type::mutableOf() %p, %s\n", this, toChars());
+ Type t = this;
+ if (isImmutable())
+ {
+ getMcache();
+ t = mcache.ito; // immutable => naked
+ assert(!t || (t.isMutable() && !t.isShared()));
+ }
+ else if (isConst())
+ {
+ getMcache();
+ if (isShared())
+ {
+ if (isWild())
+ t = mcache.swcto; // shared wild const -> shared
+ else
+ t = mcache.sto; // shared const => shared
+ }
+ else
+ {
+ if (isWild())
+ t = mcache.wcto; // wild const -> naked
+ else
+ t = mcache.cto; // const => naked
+ }
+ assert(!t || t.isMutable());
+ }
+ else if (isWild())
+ {
+ getMcache();
+ if (isShared())
+ t = mcache.sto; // shared wild => shared
+ else
+ t = mcache.wto; // wild => naked
+ assert(!t || t.isMutable());
+ }
+ if (!t)
+ {
+ t = makeMutable();
+ t = t.merge();
+ t.fixTo(this);
+ }
+ else
+ t = t.merge();
+ assert(t.isMutable());
+ return t;
+ }
+
+ final Type sharedOf()
+ {
+ //printf("Type::sharedOf() %p, %s\n", this, toChars());
+ if (mod == MODFlags.shared_)
+ return this;
+ if (mcache && mcache.sto)
+ {
+ assert(mcache.sto.mod == MODFlags.shared_);
+ return mcache.sto;
+ }
+ Type t = makeShared();
+ t = t.merge();
+ t.fixTo(this);
+ //printf("\t%p\n", t);
+ return t;
+ }
+
+ final Type sharedConstOf()
+ {
+ //printf("Type::sharedConstOf() %p, %s\n", this, toChars());
+ if (mod == (MODFlags.shared_ | MODFlags.const_))
+ return this;
+ if (mcache && mcache.scto)
+ {
+ assert(mcache.scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ return mcache.scto;
+ }
+ Type t = makeSharedConst();
+ t = t.merge();
+ t.fixTo(this);
+ //printf("\t%p\n", t);
+ return t;
+ }
+
+ /********************************
+ * Make type unshared.
+ * 0 => 0
+ * const => const
+ * immutable => immutable
+ * shared => 0
+ * shared const => const
+ * wild => wild
+ * wild const => wild const
+ * shared wild => wild
+ * shared wild const => wild const
+ */
+ final Type unSharedOf()
+ {
+ //printf("Type::unSharedOf() %p, %s\n", this, toChars());
+ Type t = this;
+
+ if (isShared())
+ {
+ getMcache();
+ if (isWild())
+ {
+ if (isConst())
+ t = mcache.wcto; // shared wild const => wild const
+ else
+ t = mcache.wto; // shared wild => wild
+ }
+ else
+ {
+ if (isConst())
+ t = mcache.cto; // shared const => const
+ else
+ t = mcache.sto; // shared => naked
+ }
+ assert(!t || !t.isShared());
+ }
+
+ if (!t)
+ {
+ t = this.nullAttributes();
+ t.mod = mod & ~MODFlags.shared_;
+ t.ctype = ctype;
+ t = t.merge();
+ t.fixTo(this);
+ }
+ else
+ t = t.merge();
+ assert(!t.isShared());
+ return t;
+ }
+
+ /********************************
+ * Convert to 'wild'.
+ */
+ final Type wildOf()
+ {
+ //printf("Type::wildOf() %p %s\n", this, toChars());
+ if (mod == MODFlags.wild)
+ return this;
+ if (mcache && mcache.wto)
+ {
+ assert(mcache.wto.mod == MODFlags.wild);
+ return mcache.wto;
+ }
+ Type t = makeWild();
+ t = t.merge();
+ t.fixTo(this);
+ //printf("\t%p %s\n", t, t.toChars());
+ return t;
+ }
+
+ final Type wildConstOf()
+ {
+ //printf("Type::wildConstOf() %p %s\n", this, toChars());
+ if (mod == MODFlags.wildconst)
+ return this;
+ if (mcache && mcache.wcto)
+ {
+ assert(mcache.wcto.mod == MODFlags.wildconst);
+ return mcache.wcto;
+ }
+ Type t = makeWildConst();
+ t = t.merge();
+ t.fixTo(this);
+ //printf("\t%p %s\n", t, t.toChars());
+ return t;
+ }
+
+ final Type sharedWildOf()
+ {
+ //printf("Type::sharedWildOf() %p, %s\n", this, toChars());
+ if (mod == (MODFlags.shared_ | MODFlags.wild))
+ return this;
+ if (mcache && mcache.swto)
+ {
+ assert(mcache.swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ return mcache.swto;
+ }
+ Type t = makeSharedWild();
+ t = t.merge();
+ t.fixTo(this);
+ //printf("\t%p %s\n", t, t.toChars());
+ return t;
+ }
+
+ final Type sharedWildConstOf()
+ {
+ //printf("Type::sharedWildConstOf() %p, %s\n", this, toChars());
+ if (mod == (MODFlags.shared_ | MODFlags.wildconst))
+ return this;
+ if (mcache && mcache.swcto)
+ {
+ assert(mcache.swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ return mcache.swcto;
+ }
+ Type t = makeSharedWildConst();
+ t = t.merge();
+ t.fixTo(this);
+ //printf("\t%p %s\n", t, t.toChars());
+ return t;
+ }
+
+ /**********************************
+ * For our new type 'this', which is type-constructed from t,
+ * fill in the cto, ito, sto, scto, wto shortcuts.
+ */
+ final void fixTo(Type t)
+ {
+ // If fixing this: immutable(T*) by t: immutable(T)*,
+ // cache t to this.xto won't break transitivity.
+ Type mto = null;
+ Type tn = nextOf();
+ if (!tn || ty != Tsarray && tn.mod == t.nextOf().mod)
+ {
+ switch (t.mod)
+ {
+ case 0:
+ mto = t;
+ break;
+
+ case MODFlags.const_:
+ getMcache();
+ mcache.cto = t;
+ break;
+
+ case MODFlags.wild:
+ getMcache();
+ mcache.wto = t;
+ break;
+
+ case MODFlags.wildconst:
+ getMcache();
+ mcache.wcto = t;
+ break;
+
+ case MODFlags.shared_:
+ getMcache();
+ mcache.sto = t;
+ break;
+
+ case MODFlags.shared_ | MODFlags.const_:
+ getMcache();
+ mcache.scto = t;
+ break;
+
+ case MODFlags.shared_ | MODFlags.wild:
+ getMcache();
+ mcache.swto = t;
+ break;
+
+ case MODFlags.shared_ | MODFlags.wildconst:
+ getMcache();
+ mcache.swcto = t;
+ break;
+
+ case MODFlags.immutable_:
+ getMcache();
+ mcache.ito = t;
+ break;
+
+ default:
+ break;
+ }
+ }
+ assert(mod != t.mod);
+
+ if (mod)
+ {
+ getMcache();
+ t.getMcache();
+ }
+ switch (mod)
+ {
+ case 0:
+ break;
+
+ case MODFlags.const_:
+ mcache.cto = mto;
+ t.mcache.cto = this;
+ break;
+
+ case MODFlags.wild:
+ mcache.wto = mto;
+ t.mcache.wto = this;
+ break;
+
+ case MODFlags.wildconst:
+ mcache.wcto = mto;
+ t.mcache.wcto = this;
+ break;
+
+ case MODFlags.shared_:
+ mcache.sto = mto;
+ t.mcache.sto = this;
+ break;
+
+ case MODFlags.shared_ | MODFlags.const_:
+ mcache.scto = mto;
+ t.mcache.scto = this;
+ break;
+
+ case MODFlags.shared_ | MODFlags.wild:
+ mcache.swto = mto;
+ t.mcache.swto = this;
+ break;
+
+ case MODFlags.shared_ | MODFlags.wildconst:
+ mcache.swcto = mto;
+ t.mcache.swcto = this;
+ break;
+
+ case MODFlags.immutable_:
+ t.mcache.ito = this;
+ if (t.mcache.cto)
+ t.mcache.cto.getMcache().ito = this;
+ if (t.mcache.sto)
+ t.mcache.sto.getMcache().ito = this;
+ if (t.mcache.scto)
+ t.mcache.scto.getMcache().ito = this;
+ if (t.mcache.wto)
+ t.mcache.wto.getMcache().ito = this;
+ if (t.mcache.wcto)
+ t.mcache.wcto.getMcache().ito = this;
+ if (t.mcache.swto)
+ t.mcache.swto.getMcache().ito = this;
+ if (t.mcache.swcto)
+ t.mcache.swcto.getMcache().ito = this;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ check();
+ t.check();
+ //printf("fixTo: %s, %s\n", toChars(), t.toChars());
+ }
+
+ /***************************
+ * Look for bugs in constructing types.
+ */
+ final void check()
+ {
+ if (mcache)
+ with (mcache)
+ switch (mod)
+ {
+ case 0:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.const_:
+ if (cto)
+ assert(cto.mod == 0);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.wild:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == 0);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.wildconst:
+ assert(!cto || cto.mod == MODFlags.const_);
+ assert(!ito || ito.mod == MODFlags.immutable_);
+ assert(!sto || sto.mod == MODFlags.shared_);
+ assert(!scto || scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ assert(!wto || wto.mod == MODFlags.wild);
+ assert(!wcto || wcto.mod == 0);
+ assert(!swto || swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ assert(!swcto || swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.shared_:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == 0);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.shared_ | MODFlags.const_:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == 0);
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.shared_ | MODFlags.wild:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == MODFlags.immutable_);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == 0);
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ case MODFlags.shared_ | MODFlags.wildconst:
+ assert(!cto || cto.mod == MODFlags.const_);
+ assert(!ito || ito.mod == MODFlags.immutable_);
+ assert(!sto || sto.mod == MODFlags.shared_);
+ assert(!scto || scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ assert(!wto || wto.mod == MODFlags.wild);
+ assert(!wcto || wcto.mod == MODFlags.wildconst);
+ assert(!swto || swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ assert(!swcto || swcto.mod == 0);
+ break;
+
+ case MODFlags.immutable_:
+ if (cto)
+ assert(cto.mod == MODFlags.const_);
+ if (ito)
+ assert(ito.mod == 0);
+ if (sto)
+ assert(sto.mod == MODFlags.shared_);
+ if (scto)
+ assert(scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ if (wto)
+ assert(wto.mod == MODFlags.wild);
+ if (wcto)
+ assert(wcto.mod == MODFlags.wildconst);
+ if (swto)
+ assert(swto.mod == (MODFlags.shared_ | MODFlags.wild));
+ if (swcto)
+ assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ break;
+
+ default:
+ assert(0);
+ }
+
+ Type tn = nextOf();
+ if (tn && ty != Tfunction && tn.ty != Tfunction && ty != Tenum)
+ {
+ // Verify transitivity
+ switch (mod)
+ {
+ case 0:
+ case MODFlags.const_:
+ case MODFlags.wild:
+ case MODFlags.wildconst:
+ case MODFlags.shared_:
+ case MODFlags.shared_ | MODFlags.const_:
+ case MODFlags.shared_ | MODFlags.wild:
+ case MODFlags.shared_ | MODFlags.wildconst:
+ case MODFlags.immutable_:
+ assert(tn.mod == MODFlags.immutable_ || (tn.mod & mod) == mod);
+ break;
+
+ default:
+ assert(0);
+ }
+ tn.check();
+ }
+ }
+
+ /*************************************
+ * Apply STCxxxx bits to existing type.
+ * Use *before* semantic analysis is run.
+ */
+ final Type addSTC(StorageClass stc)
+ {
+ Type t = this;
+ if (t.isImmutable())
+ {
+ }
+ else if (stc & STC.immutable_)
+ {
+ t = t.makeImmutable();
+ }
+ else
+ {
+ if ((stc & STC.shared_) && !t.isShared())
+ {
+ if (t.isWild())
+ {
+ if (t.isConst())
+ t = t.makeSharedWildConst();
+ else
+ t = t.makeSharedWild();
+ }
+ else
+ {
+ if (t.isConst())
+ t = t.makeSharedConst();
+ else
+ t = t.makeShared();
+ }
+ }
+ if ((stc & STC.const_) && !t.isConst())
+ {
+ if (t.isShared())
+ {
+ if (t.isWild())
+ t = t.makeSharedWildConst();
+ else
+ t = t.makeSharedConst();
+ }
+ else
+ {
+ if (t.isWild())
+ t = t.makeWildConst();
+ else
+ t = t.makeConst();
+ }
+ }
+ if ((stc & STC.wild) && !t.isWild())
+ {
+ if (t.isShared())
+ {
+ if (t.isConst())
+ t = t.makeSharedWildConst();
+ else
+ t = t.makeSharedWild();
+ }
+ else
+ {
+ if (t.isConst())
+ t = t.makeWildConst();
+ else
+ t = t.makeWild();
+ }
+ }
+ }
+ return t;
+ }
+
+ /************************************
+ * Apply MODxxxx bits to existing type.
+ */
+ final Type castMod(MOD mod)
+ {
+ Type t;
+ switch (mod)
+ {
+ case 0:
+ t = unSharedOf().mutableOf();
+ break;
+
+ case MODFlags.const_:
+ t = unSharedOf().constOf();
+ break;
+
+ case MODFlags.wild:
+ t = unSharedOf().wildOf();
+ break;
+
+ case MODFlags.wildconst:
+ t = unSharedOf().wildConstOf();
+ break;
+
+ case MODFlags.shared_:
+ t = mutableOf().sharedOf();
+ break;
+
+ case MODFlags.shared_ | MODFlags.const_:
+ t = sharedConstOf();
+ break;
+
+ case MODFlags.shared_ | MODFlags.wild:
+ t = sharedWildOf();
+ break;
+
+ case MODFlags.shared_ | MODFlags.wildconst:
+ t = sharedWildConstOf();
+ break;
+
+ case MODFlags.immutable_:
+ t = immutableOf();
+ break;
+
+ default:
+ assert(0);
+ }
+ return t;
+ }
+
+ /************************************
+ * Add MODxxxx bits to existing type.
+ * We're adding, not replacing, so adding const to
+ * a shared type => "shared const"
+ */
+ final Type addMod(MOD mod)
+ {
+ /* Add anything to immutable, and it remains immutable
+ */
+ Type t = this;
+ if (!t.isImmutable())
+ {
+ //printf("addMod(%x) %s\n", mod, toChars());
+ switch (mod)
+ {
+ case 0:
+ break;
+
+ case MODFlags.const_:
+ if (isShared())
+ {
+ if (isWild())
+ t = sharedWildConstOf();
+ else
+ t = sharedConstOf();
+ }
+ else
+ {
+ if (isWild())
+ t = wildConstOf();
+ else
+ t = constOf();
+ }
+ break;
+
+ case MODFlags.wild:
+ if (isShared())
+ {
+ if (isConst())
+ t = sharedWildConstOf();
+ else
+ t = sharedWildOf();
+ }
+ else
+ {
+ if (isConst())
+ t = wildConstOf();
+ else
+ t = wildOf();
+ }
+ break;
+
+ case MODFlags.wildconst:
+ if (isShared())
+ t = sharedWildConstOf();
+ else
+ t = wildConstOf();
+ break;
+
+ case MODFlags.shared_:
+ if (isWild())
+ {
+ if (isConst())
+ t = sharedWildConstOf();
+ else
+ t = sharedWildOf();
+ }
+ else
+ {
+ if (isConst())
+ t = sharedConstOf();
+ else
+ t = sharedOf();
+ }
+ break;
+
+ case MODFlags.shared_ | MODFlags.const_:
+ if (isWild())
+ t = sharedWildConstOf();
+ else
+ t = sharedConstOf();
+ break;
+
+ case MODFlags.shared_ | MODFlags.wild:
+ if (isConst())
+ t = sharedWildConstOf();
+ else
+ t = sharedWildOf();
+ break;
+
+ case MODFlags.shared_ | MODFlags.wildconst:
+ t = sharedWildConstOf();
+ break;
+
+ case MODFlags.immutable_:
+ t = immutableOf();
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ return t;
+ }
+
+ /************************************
+ * Add storage class modifiers to type.
+ */
+ Type addStorageClass(StorageClass stc)
+ {
+ /* Just translate to MOD bits and let addMod() do the work
+ */
+ MOD mod = 0;
+ if (stc & STC.immutable_)
+ mod = MODFlags.immutable_;
+ else
+ {
+ if (stc & (STC.const_ | STC.in_))
+ mod |= MODFlags.const_;
+ if (stc & STC.wild)
+ mod |= MODFlags.wild;
+ if (stc & STC.shared_)
+ mod |= MODFlags.shared_;
+ }
+ return addMod(mod);
+ }
+
+ final Type pointerTo()
+ {
+ if (ty == Terror)
+ return this;
+ if (!pto)
+ {
+ Type t = new TypePointer(this);
+ if (ty == Tfunction)
+ {
+ t.deco = t.merge().deco;
+ pto = t;
+ }
+ else
+ pto = t.merge();
+ }
+ return pto;
+ }
+
+ final Type referenceTo()
+ {
+ if (ty == Terror)
+ return this;
+ if (!rto)
+ {
+ Type t = new TypeReference(this);
+ rto = t.merge();
+ }
+ return rto;
+ }
+
+ final Type arrayOf()
+ {
+ if (ty == Terror)
+ return this;
+ if (!arrayof)
+ {
+ Type t = new TypeDArray(this);
+ arrayof = t.merge();
+ }
+ return arrayof;
+ }
+
+ // Make corresponding static array type without semantic
+ final Type sarrayOf(dinteger_t dim)
+ {
+ assert(deco);
+ Type t = new TypeSArray(this, new IntegerExp(Loc.initial, dim, Type.tsize_t));
+ // according to TypeSArray::semantic()
+ t = t.addMod(mod);
+ t = t.merge();
+ return t;
+ }
+
+ final bool hasDeprecatedAliasThis()
+ {
+ auto ad = isAggregate(this);
+ return ad && ad.aliasthis && (ad.aliasthis.isDeprecated || ad.aliasthis.sym.isDeprecated);
+ }
+
+ final Type aliasthisOf()
+ {
+ auto ad = isAggregate(this);
+ if (!ad || !ad.aliasthis)
+ return null;
+
+ auto s = ad.aliasthis.sym;
+ if (s.isAliasDeclaration())
+ s = s.toAlias();
+
+ if (s.isTupleDeclaration())
+ return null;
+
+ if (auto vd = s.isVarDeclaration())
+ {
+ auto t = vd.type;
+ if (vd.needThis())
+ t = t.addMod(this.mod);
+ return t;
+ }
+ if (auto fd = s.isFuncDeclaration())
+ {
+ fd = resolveFuncCall(Loc.initial, null, fd, null, this, null, FuncResolveFlag.quiet);
+ if (!fd || fd.errors || !fd.functionSemantic())
+ return Type.terror;
+
+ auto t = fd.type.nextOf();
+ if (!t) // issue 14185
+ return Type.terror;
+ t = t.substWildTo(mod == 0 ? MODFlags.mutable : mod);
+ return t;
+ }
+ if (auto d = s.isDeclaration())
+ {
+ assert(d.type);
+ return d.type;
+ }
+ if (auto ed = s.isEnumDeclaration())
+ {
+ return ed.type;
+ }
+ if (auto td = s.isTemplateDeclaration())
+ {
+ assert(td._scope);
+ auto fd = resolveFuncCall(Loc.initial, null, td, null, this, null, FuncResolveFlag.quiet);
+ if (!fd || fd.errors || !fd.functionSemantic())
+ return Type.terror;
+
+ auto t = fd.type.nextOf();
+ if (!t)
+ return Type.terror;
+ t = t.substWildTo(mod == 0 ? MODFlags.mutable : mod);
+ return t;
+ }
+
+ //printf("%s\n", s.kind());
+ return null;
+ }
+
+ extern (D) final bool checkAliasThisRec()
+ {
+ Type tb = toBasetype();
+ AliasThisRec* pflag;
+ if (tb.ty == Tstruct)
+ pflag = &(cast(TypeStruct)tb).att;
+ else if (tb.ty == Tclass)
+ pflag = &(cast(TypeClass)tb).att;
+ else
+ return false;
+
+ AliasThisRec flag = cast(AliasThisRec)(*pflag & AliasThisRec.typeMask);
+ if (flag == AliasThisRec.fwdref)
+ {
+ Type att = aliasthisOf();
+ flag = att && att.implicitConvTo(this) ? AliasThisRec.yes : AliasThisRec.no;
+ }
+ *pflag = cast(AliasThisRec)(flag | (*pflag & ~AliasThisRec.typeMask));
+ return flag == AliasThisRec.yes;
+ }
+
+ Type makeConst()
+ {
+ //printf("Type::makeConst() %p, %s\n", this, toChars());
+ if (mcache && mcache.cto)
+ return mcache.cto;
+ Type t = this.nullAttributes();
+ t.mod = MODFlags.const_;
+ //printf("-Type::makeConst() %p, %s\n", t, toChars());
+ return t;
+ }
+
+ Type makeImmutable()
+ {
+ if (mcache && mcache.ito)
+ return mcache.ito;
+ Type t = this.nullAttributes();
+ t.mod = MODFlags.immutable_;
+ return t;
+ }
+
+ Type makeShared()
+ {
+ if (mcache && mcache.sto)
+ return mcache.sto;
+ Type t = this.nullAttributes();
+ t.mod = MODFlags.shared_;
+ return t;
+ }
+
+ Type makeSharedConst()
+ {
+ if (mcache && mcache.scto)
+ return mcache.scto;
+ Type t = this.nullAttributes();
+ t.mod = MODFlags.shared_ | MODFlags.const_;
+ return t;
+ }
+
+ Type makeWild()
+ {
+ if (mcache && mcache.wto)
+ return mcache.wto;
+ Type t = this.nullAttributes();
+ t.mod = MODFlags.wild;
+ return t;
+ }
+
+ Type makeWildConst()
+ {
+ if (mcache && mcache.wcto)
+ return mcache.wcto;
+ Type t = this.nullAttributes();
+ t.mod = MODFlags.wildconst;
+ return t;
+ }
+
+ Type makeSharedWild()
+ {
+ if (mcache && mcache.swto)
+ return mcache.swto;
+ Type t = this.nullAttributes();
+ t.mod = MODFlags.shared_ | MODFlags.wild;
+ return t;
+ }
+
+ Type makeSharedWildConst()
+ {
+ if (mcache && mcache.swcto)
+ return mcache.swcto;
+ Type t = this.nullAttributes();
+ t.mod = MODFlags.shared_ | MODFlags.wildconst;
+ return t;
+ }
+
+ Type makeMutable()
+ {
+ Type t = this.nullAttributes();
+ t.mod = mod & MODFlags.shared_;
+ return t;
+ }
+
+ Dsymbol toDsymbol(Scope* sc)
+ {
+ return null;
+ }
+
+ /*******************************
+ * If this is a shell around another type,
+ * get that other type.
+ */
+ final Type toBasetype()
+ {
+ /* This function is used heavily.
+ * De-virtualize it so it can be easily inlined.
+ */
+ TypeEnum te;
+ return ((te = isTypeEnum()) !is null) ? te.toBasetype2() : this;
+ }
+
+ bool isBaseOf(Type t, int* poffset)
+ {
+ return 0; // assume not
+ }
+
+ /********************************
+ * Determine if 'this' can be implicitly converted
+ * to type 'to'.
+ * Returns:
+ * MATCH.nomatch, MATCH.convert, MATCH.constant, MATCH.exact
+ */
+ MATCH implicitConvTo(Type to)
+ {
+ //printf("Type::implicitConvTo(this=%p, to=%p)\n", this, to);
+ //printf("from: %s\n", toChars());
+ //printf("to : %s\n", to.toChars());
+ if (this.equals(to))
+ return MATCH.exact;
+ return MATCH.nomatch;
+ }
+
+ /*******************************
+ * Determine if converting 'this' to 'to' is an identity operation,
+ * a conversion to const operation, or the types aren't the same.
+ * Returns:
+ * MATCH.exact 'this' == 'to'
+ * MATCH.constant 'to' is const
+ * MATCH.nomatch conversion to mutable or invariant
+ */
+ MATCH constConv(Type to)
+ {
+ //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to.toChars());
+ if (equals(to))
+ return MATCH.exact;
+ if (ty == to.ty && MODimplicitConv(mod, to.mod))
+ return MATCH.constant;
+ return MATCH.nomatch;
+ }
+
+ /***************************************
+ * Compute MOD bits matching `this` argument type to wild parameter type.
+ * Params:
+ * t = corresponding parameter type
+ * isRef = parameter is `ref` or `out`
+ * Returns:
+ * MOD bits
+ */
+ MOD deduceWild(Type t, bool isRef)
+ {
+ //printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm.toChars());
+ if (t.isWild())
+ {
+ if (isImmutable())
+ return MODFlags.immutable_;
+ else if (isWildConst())
+ {
+ if (t.isWildConst())
+ return MODFlags.wild;
+ else
+ return MODFlags.wildconst;
+ }
+ else if (isWild())
+ return MODFlags.wild;
+ else if (isConst())
+ return MODFlags.const_;
+ else if (isMutable())
+ return MODFlags.mutable;
+ else
+ assert(0);
+ }
+ return 0;
+ }
+
+ Type substWildTo(uint mod)
+ {
+ //printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod);
+ Type t;
+
+ if (Type tn = nextOf())
+ {
+ // substitution has no effect on function pointer type.
+ if (ty == Tpointer && tn.ty == Tfunction)
+ {
+ t = this;
+ goto L1;
+ }
+
+ t = tn.substWildTo(mod);
+ if (t == tn)
+ t = this;
+ else
+ {
+ if (ty == Tpointer)
+ t = t.pointerTo();
+ else if (ty == Tarray)
+ t = t.arrayOf();
+ else if (ty == Tsarray)
+ t = new TypeSArray(t, (cast(TypeSArray)this).dim.syntaxCopy());
+ else if (ty == Taarray)
+ {
+ t = new TypeAArray(t, (cast(TypeAArray)this).index.syntaxCopy());
+ }
+ else if (ty == Tdelegate)
+ {
+ t = new TypeDelegate(t.isTypeFunction());
+ }
+ else
+ assert(0);
+
+ t = t.merge();
+ }
+ }
+ else
+ t = this;
+
+ L1:
+ if (isWild())
+ {
+ if (mod == MODFlags.immutable_)
+ {
+ t = t.immutableOf();
+ }
+ else if (mod == MODFlags.wildconst)
+ {
+ t = t.wildConstOf();
+ }
+ else if (mod == MODFlags.wild)
+ {
+ if (isWildConst())
+ t = t.wildConstOf();
+ else
+ t = t.wildOf();
+ }
+ else if (mod == MODFlags.const_)
+ {
+ t = t.constOf();
+ }
+ else
+ {
+ if (isWildConst())
+ t = t.constOf();
+ else
+ t = t.mutableOf();
+ }
+ }
+ if (isConst())
+ t = t.addMod(MODFlags.const_);
+ if (isShared())
+ t = t.addMod(MODFlags.shared_);
+
+ //printf("-Type::substWildTo t = %s\n", t.toChars());
+ return t;
+ }
+
+ final Type unqualify(uint m)
+ {
+ Type t = mutableOf().unSharedOf();
+
+ Type tn = ty == Tenum ? null : nextOf();
+ if (tn && tn.ty != Tfunction)
+ {
+ Type utn = tn.unqualify(m);
+ if (utn != tn)
+ {
+ if (ty == Tpointer)
+ t = utn.pointerTo();
+ else if (ty == Tarray)
+ t = utn.arrayOf();
+ else if (ty == Tsarray)
+ t = new TypeSArray(utn, (cast(TypeSArray)this).dim);
+ else if (ty == Taarray)
+ {
+ t = new TypeAArray(utn, (cast(TypeAArray)this).index);
+ }
+ else
+ assert(0);
+
+ t = t.merge();
+ }
+ }
+ t = t.addMod(mod & ~m);
+ return t;
+ }
+
+ /**************************
+ * Return type with the top level of it being mutable.
+ */
+ inout(Type) toHeadMutable() inout
+ {
+ if (!mod)
+ return this;
+ Type unqualThis = cast(Type) this;
+ // `mutableOf` needs a mutable `this` only for caching
+ return cast(inout(Type)) unqualThis.mutableOf();
+ }
+
+ inout(ClassDeclaration) isClassHandle() inout
+ {
+ return null;
+ }
+
+ /************************************
+ * Return alignment to use for this type.
+ */
+ structalign_t alignment()
+ {
+ return STRUCTALIGN_DEFAULT;
+ }
+
+ /***************************************
+ * Use when we prefer the default initializer to be a literal,
+ * rather than a global immutable variable.
+ */
+ Expression defaultInitLiteral(const ref Loc loc)
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("Type::defaultInitLiteral() '%s'\n", toChars());
+ }
+ return defaultInit(this, loc);
+ }
+
+ // if initializer is 0
+ bool isZeroInit(const ref Loc loc)
+ {
+ return false; // assume not
+ }
+
+ final Identifier getTypeInfoIdent()
+ {
+ // _init_10TypeInfo_%s
+ OutBuffer buf;
+ buf.reserve(32);
+ mangleToBuffer(this, &buf);
+
+ const slice = buf[];
+
+ // Allocate buffer on stack, fail over to using malloc()
+ char[128] namebuf;
+ const namelen = 19 + size_t.sizeof * 3 + slice.length + 1;
+ auto name = namelen <= namebuf.length ? namebuf.ptr : cast(char*)Mem.check(malloc(namelen));
+
+ const length = sprintf(name, "_D%lluTypeInfo_%.*s6__initZ",
+ cast(ulong)(9 + slice.length), cast(int)slice.length, slice.ptr);
+ //printf("%p %s, deco = %s, name = %s\n", this, toChars(), deco, name);
+ assert(0 < length && length < namelen); // don't overflow the buffer
+
+ auto id = Identifier.idPool(name, length);
+
+ if (name != namebuf.ptr)
+ free(name);
+ return id;
+ }
+
+ /***************************************
+ * Return !=0 if the type or any of its subtypes is wild.
+ */
+ int hasWild() const
+ {
+ return mod & MODFlags.wild;
+ }
+
+ /***************************************
+ * Return !=0 if type has pointers that need to
+ * be scanned by the GC during a collection cycle.
+ */
+ bool hasPointers()
+ {
+ //printf("Type::hasPointers() %s, %d\n", toChars(), ty);
+ return false;
+ }
+
+ /*************************************
+ * Detect if type has pointer fields that are initialized to void.
+ * Local stack variables with such void fields can remain uninitialized,
+ * leading to pointer bugs.
+ * Returns:
+ * true if so
+ */
+ bool hasVoidInitPointers()
+ {
+ return false;
+ }
+
+ /***************************************
+ * Returns: true if type has any invariants
+ */
+ bool hasInvariant()
+ {
+ //printf("Type::hasInvariant() %s, %d\n", toChars(), ty);
+ return false;
+ }
+
+ /*************************************
+ * If this is a type of something, return that something.
+ */
+ Type nextOf()
+ {
+ return null;
+ }
+
+ /*************************************
+ * If this is a type of static array, return its base element type.
+ */
+ final Type baseElemOf()
+ {
+ Type t = toBasetype();
+ TypeSArray tsa;
+ while ((tsa = t.isTypeSArray()) !is null)
+ t = tsa.next.toBasetype();
+ return t;
+ }
+
+ /*******************************************
+ * Compute number of elements for a (possibly multidimensional) static array,
+ * or 1 for other types.
+ * Params:
+ * loc = for error message
+ * Returns:
+ * number of elements, uint.max on overflow
+ */
+ final uint numberOfElems(const ref Loc loc)
+ {
+ //printf("Type::numberOfElems()\n");
+ uinteger_t n = 1;
+ Type tb = this;
+ while ((tb = tb.toBasetype()).ty == Tsarray)
+ {
+ bool overflow = false;
+ n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow);
+ if (overflow || n >= uint.max)
+ {
+ error(loc, "static array `%s` size overflowed to %llu", toChars(), cast(ulong)n);
+ return uint.max;
+ }
+ tb = (cast(TypeSArray)tb).next;
+ }
+ return cast(uint)n;
+ }
+
+ /****************************************
+ * Return the mask that an integral type will
+ * fit into.
+ */
+ final uinteger_t sizemask()
+ {
+ uinteger_t m;
+ switch (toBasetype().ty)
+ {
+ case Tbool:
+ m = 1;
+ break;
+ case Tchar:
+ case Tint8:
+ case Tuns8:
+ m = 0xFF;
+ break;
+ case Twchar:
+ case Tint16:
+ case Tuns16:
+ m = 0xFFFFU;
+ break;
+ case Tdchar:
+ case Tint32:
+ case Tuns32:
+ m = 0xFFFFFFFFU;
+ break;
+ case Tint64:
+ case Tuns64:
+ m = 0xFFFFFFFFFFFFFFFFUL;
+ break;
+ default:
+ assert(0);
+ }
+ return m;
+ }
+
+ /********************************
+ * true if when type goes out of scope, it needs a destructor applied.
+ * Only applies to value types, not ref types.
+ */
+ bool needsDestruction()
+ {
+ return false;
+ }
+
+ /********************************
+ * true if when type is copied, it needs a copy constructor or postblit
+ * applied. Only applies to value types, not ref types.
+ */
+ bool needsCopyOrPostblit()
+ {
+ return false;
+ }
+
+ /*********************************
+ *
+ */
+ bool needsNested()
+ {
+ return false;
+ }
+
+ /*************************************
+ * https://issues.dlang.org/show_bug.cgi?id=14488
+ * Check if the inner most base type is complex or imaginary.
+ * Should only give alerts when set to emit transitional messages.
+ * Params:
+ * loc = The source location.
+ * sc = scope of the type
+ */
+ extern (D) final bool checkComplexTransition(const ref Loc loc, Scope* sc)
+ {
+ if (sc.isDeprecated())
+ return false;
+ // Don't complain if we're inside a template constraint
+ // https://issues.dlang.org/show_bug.cgi?id=21831
+ if (sc.flags & SCOPE.constraint)
+ return false;
+
+ Type t = baseElemOf();
+ while (t.ty == Tpointer || t.ty == Tarray)
+ t = t.nextOf().baseElemOf();
+
+ // Basetype is an opaque enum, nothing to check.
+ if (t.ty == Tenum && !(cast(TypeEnum)t).sym.memtype)
+ return false;
+
+ if (t.isimaginary() || t.iscomplex())
+ {
+ Type rt;
+ switch (t.ty)
+ {
+ case Tcomplex32:
+ case Timaginary32:
+ rt = Type.tfloat32;
+ break;
+
+ case Tcomplex64:
+ case Timaginary64:
+ rt = Type.tfloat64;
+ break;
+
+ case Tcomplex80:
+ case Timaginary80:
+ rt = Type.tfloat80;
+ break;
+
+ default:
+ assert(0);
+ }
+ if (t.iscomplex())
+ {
+ deprecation(loc, "use of complex type `%s` is deprecated, use `std.complex.Complex!(%s)` instead",
+ toChars(), rt.toChars());
+ return true;
+ }
+ else
+ {
+ deprecation(loc, "use of imaginary type `%s` is deprecated, use `%s` instead",
+ toChars(), rt.toChars());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // For eliminating dynamic_cast
+ TypeBasic isTypeBasic()
+ {
+ return null;
+ }
+
+ final pure inout nothrow @nogc
+ {
+ /****************
+ * Is this type a pointer to a function?
+ * Returns:
+ * the function type if it is
+ */
+ inout(TypeFunction) isPtrToFunction()
+ {
+ return (ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction)
+ ? cast(typeof(return))(cast(TypePointer)this).next
+ : null;
+ }
+
+ /*****************
+ * Is this type a function, delegate, or pointer to a function?
+ * Returns:
+ * the function type if it is
+ */
+ inout(TypeFunction) isFunction_Delegate_PtrToFunction()
+ {
+ return ty == Tfunction ? cast(typeof(return))this :
+
+ ty == Tdelegate ? cast(typeof(return))(cast(TypePointer)this).next :
+
+ ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction ?
+ cast(typeof(return))(cast(TypePointer)this).next :
+
+ null;
+ }
+ }
+
+ final pure inout nothrow @nogc @safe
+ {
+ inout(TypeError) isTypeError() { return ty == Terror ? cast(typeof(return))this : null; }
+ inout(TypeVector) isTypeVector() { return ty == Tvector ? cast(typeof(return))this : null; }
+ inout(TypeSArray) isTypeSArray() { return ty == Tsarray ? cast(typeof(return))this : null; }
+ inout(TypeDArray) isTypeDArray() { return ty == Tarray ? cast(typeof(return))this : null; }
+ inout(TypeAArray) isTypeAArray() { return ty == Taarray ? cast(typeof(return))this : null; }
+ inout(TypePointer) isTypePointer() { return ty == Tpointer ? cast(typeof(return))this : null; }
+ inout(TypeReference) isTypeReference() { return ty == Treference ? cast(typeof(return))this : null; }
+ inout(TypeFunction) isTypeFunction() { return ty == Tfunction ? cast(typeof(return))this : null; }
+ inout(TypeDelegate) isTypeDelegate() { return ty == Tdelegate ? cast(typeof(return))this : null; }
+ inout(TypeIdentifier) isTypeIdentifier() { return ty == Tident ? cast(typeof(return))this : null; }
+ inout(TypeInstance) isTypeInstance() { return ty == Tinstance ? cast(typeof(return))this : null; }
+ inout(TypeTypeof) isTypeTypeof() { return ty == Ttypeof ? cast(typeof(return))this : null; }
+ inout(TypeReturn) isTypeReturn() { return ty == Treturn ? cast(typeof(return))this : null; }
+ inout(TypeStruct) isTypeStruct() { return ty == Tstruct ? cast(typeof(return))this : null; }
+ inout(TypeEnum) isTypeEnum() { return ty == Tenum ? cast(typeof(return))this : null; }
+ inout(TypeClass) isTypeClass() { return ty == Tclass ? cast(typeof(return))this : null; }
+ inout(TypeTuple) isTypeTuple() { return ty == Ttuple ? cast(typeof(return))this : null; }
+ inout(TypeSlice) isTypeSlice() { return ty == Tslice ? cast(typeof(return))this : null; }
+ inout(TypeNull) isTypeNull() { return ty == Tnull ? cast(typeof(return))this : null; }
+ inout(TypeMixin) isTypeMixin() { return ty == Tmixin ? cast(typeof(return))this : null; }
+ inout(TypeTraits) isTypeTraits() { return ty == Ttraits ? cast(typeof(return))this : null; }
+ inout(TypeNoreturn) isTypeNoreturn() { return ty == Tnoreturn ? cast(typeof(return))this : null; }
+ inout(TypeTag) isTypeTag() { return ty == Ttag ? cast(typeof(return))this : null; }
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ final TypeFunction toTypeFunction()
+ {
+ if (ty != Tfunction)
+ assert(0);
+ return cast(TypeFunction)this;
+ }
+
+ extern (D) static Types* arraySyntaxCopy(Types* types)
+ {
+ Types* a = null;
+ if (types)
+ {
+ a = new Types(types.length);
+ foreach (i, t; *types)
+ {
+ (*a)[i] = t ? t.syntaxCopy() : null;
+ }
+ }
+ return a;
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeError : Type
+{
+ extern (D) this()
+ {
+ super(Terror);
+ }
+
+ override const(char)* kind() const
+ {
+ return "error";
+ }
+
+ override TypeError syntaxCopy()
+ {
+ // No semantic analysis done, no need to copy
+ return this;
+ }
+
+ override d_uns64 size(const ref Loc loc)
+ {
+ return SIZE_INVALID;
+ }
+
+ override Expression defaultInitLiteral(const ref Loc loc)
+ {
+ return ErrorExp.get();
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) abstract class TypeNext : Type
+{
+ Type next;
+
+ final extern (D) this(TY ty, Type next)
+ {
+ super(ty);
+ this.next = next;
+ }
+
+ override final void checkDeprecated(const ref Loc loc, Scope* sc)
+ {
+ Type.checkDeprecated(loc, sc);
+ if (next) // next can be NULL if TypeFunction and auto return type
+ next.checkDeprecated(loc, sc);
+ }
+
+ override final int hasWild() const
+ {
+ if (ty == Tfunction)
+ return 0;
+ if (ty == Tdelegate)
+ return Type.hasWild();
+ return mod & MODFlags.wild || (next && next.hasWild());
+ }
+
+ /*******************************
+ * For TypeFunction, nextOf() can return NULL if the function return
+ * type is meant to be inferred, and semantic() hasn't yet ben run
+ * on the function. After semantic(), it must no longer be NULL.
+ */
+ override final Type nextOf()
+ {
+ return next;
+ }
+
+ override final Type makeConst()
+ {
+ //printf("TypeNext::makeConst() %p, %s\n", this, toChars());
+ if (mcache && mcache.cto)
+ {
+ assert(mcache.cto.mod == MODFlags.const_);
+ return mcache.cto;
+ }
+ TypeNext t = cast(TypeNext)Type.makeConst();
+ if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
+ {
+ if (next.isShared())
+ {
+ if (next.isWild())
+ t.next = next.sharedWildConstOf();
+ else
+ t.next = next.sharedConstOf();
+ }
+ else
+ {
+ if (next.isWild())
+ t.next = next.wildConstOf();
+ else
+ t.next = next.constOf();
+ }
+ }
+ //printf("TypeNext::makeConst() returns %p, %s\n", t, t.toChars());
+ return t;
+ }
+
+ override final Type makeImmutable()
+ {
+ //printf("TypeNext::makeImmutable() %s\n", toChars());
+ if (mcache && mcache.ito)
+ {
+ assert(mcache.ito.isImmutable());
+ return mcache.ito;
+ }
+ TypeNext t = cast(TypeNext)Type.makeImmutable();
+ if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
+ {
+ t.next = next.immutableOf();
+ }
+ return t;
+ }
+
+ override final Type makeShared()
+ {
+ //printf("TypeNext::makeShared() %s\n", toChars());
+ if (mcache && mcache.sto)
+ {
+ assert(mcache.sto.mod == MODFlags.shared_);
+ return mcache.sto;
+ }
+ TypeNext t = cast(TypeNext)Type.makeShared();
+ if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
+ {
+ if (next.isWild())
+ {
+ if (next.isConst())
+ t.next = next.sharedWildConstOf();
+ else
+ t.next = next.sharedWildOf();
+ }
+ else
+ {
+ if (next.isConst())
+ t.next = next.sharedConstOf();
+ else
+ t.next = next.sharedOf();
+ }
+ }
+ //printf("TypeNext::makeShared() returns %p, %s\n", t, t.toChars());
+ return t;
+ }
+
+ override final Type makeSharedConst()
+ {
+ //printf("TypeNext::makeSharedConst() %s\n", toChars());
+ if (mcache && mcache.scto)
+ {
+ assert(mcache.scto.mod == (MODFlags.shared_ | MODFlags.const_));
+ return mcache.scto;
+ }
+ TypeNext t = cast(TypeNext)Type.makeSharedConst();
+ if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
+ {
+ if (next.isWild())
+ t.next = next.sharedWildConstOf();
+ else
+ t.next = next.sharedConstOf();
+ }
+ //printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t.toChars());
+ return t;
+ }
+
+ override final Type makeWild()
+ {
+ //printf("TypeNext::makeWild() %s\n", toChars());
+ if (mcache && mcache.wto)
+ {
+ assert(mcache.wto.mod == MODFlags.wild);
+ return mcache.wto;
+ }
+ TypeNext t = cast(TypeNext)Type.makeWild();
+ if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
+ {
+ if (next.isShared())
+ {
+ if (next.isConst())
+ t.next = next.sharedWildConstOf();
+ else
+ t.next = next.sharedWildOf();
+ }
+ else
+ {
+ if (next.isConst())
+ t.next = next.wildConstOf();
+ else
+ t.next = next.wildOf();
+ }
+ }
+ //printf("TypeNext::makeWild() returns %p, %s\n", t, t.toChars());
+ return t;
+ }
+
+ override final Type makeWildConst()
+ {
+ //printf("TypeNext::makeWildConst() %s\n", toChars());
+ if (mcache && mcache.wcto)
+ {
+ assert(mcache.wcto.mod == MODFlags.wildconst);
+ return mcache.wcto;
+ }
+ TypeNext t = cast(TypeNext)Type.makeWildConst();
+ if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
+ {
+ if (next.isShared())
+ t.next = next.sharedWildConstOf();
+ else
+ t.next = next.wildConstOf();
+ }
+ //printf("TypeNext::makeWildConst() returns %p, %s\n", t, t.toChars());
+ return t;
+ }
+
+ override final Type makeSharedWild()
+ {
+ //printf("TypeNext::makeSharedWild() %s\n", toChars());
+ if (mcache && mcache.swto)
+ {
+ assert(mcache.swto.isSharedWild());
+ return mcache.swto;
+ }
+ TypeNext t = cast(TypeNext)Type.makeSharedWild();
+ if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
+ {
+ if (next.isConst())
+ t.next = next.sharedWildConstOf();
+ else
+ t.next = next.sharedWildOf();
+ }
+ //printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t.toChars());
+ return t;
+ }
+
+ override final Type makeSharedWildConst()
+ {
+ //printf("TypeNext::makeSharedWildConst() %s\n", toChars());
+ if (mcache && mcache.swcto)
+ {
+ assert(mcache.swcto.mod == (MODFlags.shared_ | MODFlags.wildconst));
+ return mcache.swcto;
+ }
+ TypeNext t = cast(TypeNext)Type.makeSharedWildConst();
+ if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
+ {
+ t.next = next.sharedWildConstOf();
+ }
+ //printf("TypeNext::makeSharedWildConst() returns %p, %s\n", t, t.toChars());
+ return t;
+ }
+
+ override final Type makeMutable()
+ {
+ //printf("TypeNext::makeMutable() %p, %s\n", this, toChars());
+ TypeNext t = cast(TypeNext)Type.makeMutable();
+ if (ty == Tsarray)
+ {
+ t.next = next.mutableOf();
+ }
+ //printf("TypeNext::makeMutable() returns %p, %s\n", t, t.toChars());
+ return t;
+ }
+
+ override MATCH constConv(Type to)
+ {
+ //printf("TypeNext::constConv from = %s, to = %s\n", toChars(), to.toChars());
+ if (equals(to))
+ return MATCH.exact;
+
+ if (!(ty == to.ty && MODimplicitConv(mod, to.mod)))
+ return MATCH.nomatch;
+
+ Type tn = to.nextOf();
+ if (!(tn && next.ty == tn.ty))
+ return MATCH.nomatch;
+
+ MATCH m;
+ if (to.isConst()) // whole tail const conversion
+ {
+ // Recursive shared level check
+ m = next.constConv(tn);
+ if (m == MATCH.exact)
+ m = MATCH.constant;
+ }
+ else
+ {
+ //printf("\tnext => %s, to.next => %s\n", next.toChars(), tn.toChars());
+ m = next.equals(tn) ? MATCH.constant : MATCH.nomatch;
+ }
+ return m;
+ }
+
+ override final MOD deduceWild(Type t, bool isRef)
+ {
+ if (ty == Tfunction)
+ return 0;
+
+ ubyte wm;
+
+ Type tn = t.nextOf();
+ if (!isRef && (ty == Tarray || ty == Tpointer) && tn)
+ {
+ wm = next.deduceWild(tn, true);
+ if (!wm)
+ wm = Type.deduceWild(t, true);
+ }
+ else
+ {
+ wm = Type.deduceWild(t, isRef);
+ if (!wm && tn)
+ wm = next.deduceWild(tn, true);
+ }
+
+ return wm;
+ }
+
+ final void transitive()
+ {
+ /* Invoke transitivity of type attributes
+ */
+ next = next.addMod(mod);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeBasic : Type
+{
+ const(char)* dstring;
+ uint flags;
+
+ extern (D) this(TY ty)
+ {
+ super(ty);
+ const(char)* d;
+ uint flags = 0;
+ switch (ty)
+ {
+ case Tvoid:
+ d = Token.toChars(TOK.void_);
+ break;
+
+ case Tint8:
+ d = Token.toChars(TOK.int8);
+ flags |= TFlags.integral;
+ break;
+
+ case Tuns8:
+ d = Token.toChars(TOK.uns8);
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
+
+ case Tint16:
+ d = Token.toChars(TOK.int16);
+ flags |= TFlags.integral;
+ break;
+
+ case Tuns16:
+ d = Token.toChars(TOK.uns16);
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
+
+ case Tint32:
+ d = Token.toChars(TOK.int32);
+ flags |= TFlags.integral;
+ break;
+
+ case Tuns32:
+ d = Token.toChars(TOK.uns32);
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
+
+ case Tfloat32:
+ d = Token.toChars(TOK.float32);
+ flags |= TFlags.floating | TFlags.real_;
+ break;
+
+ case Tint64:
+ d = Token.toChars(TOK.int64);
+ flags |= TFlags.integral;
+ break;
+
+ case Tuns64:
+ d = Token.toChars(TOK.uns64);
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
+
+ case Tint128:
+ d = Token.toChars(TOK.int128);
+ flags |= TFlags.integral;
+ break;
+
+ case Tuns128:
+ d = Token.toChars(TOK.uns128);
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
+
+ case Tfloat64:
+ d = Token.toChars(TOK.float64);
+ flags |= TFlags.floating | TFlags.real_;
+ break;
+
+ case Tfloat80:
+ d = Token.toChars(TOK.float80);
+ flags |= TFlags.floating | TFlags.real_;
+ break;
+
+ case Timaginary32:
+ d = Token.toChars(TOK.imaginary32);
+ flags |= TFlags.floating | TFlags.imaginary;
+ break;
+
+ case Timaginary64:
+ d = Token.toChars(TOK.imaginary64);
+ flags |= TFlags.floating | TFlags.imaginary;
+ break;
+
+ case Timaginary80:
+ d = Token.toChars(TOK.imaginary80);
+ flags |= TFlags.floating | TFlags.imaginary;
+ break;
+
+ case Tcomplex32:
+ d = Token.toChars(TOK.complex32);
+ flags |= TFlags.floating | TFlags.complex;
+ break;
+
+ case Tcomplex64:
+ d = Token.toChars(TOK.complex64);
+ flags |= TFlags.floating | TFlags.complex;
+ break;
+
+ case Tcomplex80:
+ d = Token.toChars(TOK.complex80);
+ flags |= TFlags.floating | TFlags.complex;
+ break;
+
+ case Tbool:
+ d = "bool";
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
+
+ case Tchar:
+ d = Token.toChars(TOK.char_);
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
+
+ case Twchar:
+ d = Token.toChars(TOK.wchar_);
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
+
+ case Tdchar:
+ d = Token.toChars(TOK.dchar_);
+ flags |= TFlags.integral | TFlags.unsigned;
+ break;
+
+ default:
+ assert(0);
+ }
+ this.dstring = d;
+ this.flags = flags;
+ merge(this);
+ }
+
+ override const(char)* kind() const
+ {
+ return dstring;
+ }
+
+ override TypeBasic syntaxCopy()
+ {
+ // No semantic analysis done on basic types, no need to copy
+ return this;
+ }
+
+ override d_uns64 size(const ref Loc loc) const
+ {
+ uint size;
+ //printf("TypeBasic::size()\n");
+ switch (ty)
+ {
+ case Tint8:
+ case Tuns8:
+ size = 1;
+ break;
+
+ case Tint16:
+ case Tuns16:
+ size = 2;
+ break;
+
+ case Tint32:
+ case Tuns32:
+ case Tfloat32:
+ case Timaginary32:
+ size = 4;
+ break;
+
+ case Tint64:
+ case Tuns64:
+ case Tfloat64:
+ case Timaginary64:
+ size = 8;
+ break;
+
+ case Tfloat80:
+ case Timaginary80:
+ size = target.realsize;
+ break;
+
+ case Tcomplex32:
+ size = 8;
+ break;
+
+ case Tcomplex64:
+ case Tint128:
+ case Tuns128:
+ size = 16;
+ break;
+
+ case Tcomplex80:
+ size = target.realsize * 2;
+ break;
+
+ case Tvoid:
+ //size = Type::size(); // error message
+ size = 1;
+ break;
+
+ case Tbool:
+ size = 1;
+ break;
+
+ case Tchar:
+ size = 1;
+ break;
+
+ case Twchar:
+ size = 2;
+ break;
+
+ case Tdchar:
+ size = 4;
+ break;
+
+ default:
+ assert(0);
+ }
+ //printf("TypeBasic::size() = %d\n", size);
+ return size;
+ }
+
+ override uint alignsize()
+ {
+ return target.alignsize(this);
+ }
+
+ override bool isintegral()
+ {
+ //printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags);
+ return (flags & TFlags.integral) != 0;
+ }
+
+ override bool isfloating() const
+ {
+ return (flags & TFlags.floating) != 0;
+ }
+
+ override bool isreal() const
+ {
+ return (flags & TFlags.real_) != 0;
+ }
+
+ override bool isimaginary() const
+ {
+ return (flags & TFlags.imaginary) != 0;
+ }
+
+ override bool iscomplex() const
+ {
+ return (flags & TFlags.complex) != 0;
+ }
+
+ override bool isscalar() const
+ {
+ return (flags & (TFlags.integral | TFlags.floating)) != 0;
+ }
+
+ override bool isunsigned() const
+ {
+ return (flags & TFlags.unsigned) != 0;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeBasic::implicitConvTo(%s) from %s\n", to.toChars(), toChars());
+ if (this == to)
+ return MATCH.exact;
+
+ if (ty == to.ty)
+ {
+ if (mod == to.mod)
+ return MATCH.exact;
+ else if (MODimplicitConv(mod, to.mod))
+ return MATCH.constant;
+ else if (!((mod ^ to.mod) & MODFlags.shared_)) // for wild matching
+ return MATCH.constant;
+ else
+ return MATCH.convert;
+ }
+
+ if (ty == Tvoid || to.ty == Tvoid)
+ return MATCH.nomatch;
+ if (to.ty == Tbool)
+ return MATCH.nomatch;
+
+ TypeBasic tob;
+ if (to.ty == Tvector && to.deco)
+ {
+ TypeVector tv = cast(TypeVector)to;
+ tob = tv.elementType();
+ }
+ else if (auto te = to.isTypeEnum())
+ {
+ EnumDeclaration ed = te.sym;
+ if (ed.isSpecial())
+ {
+ /* Special enums that allow implicit conversions to them
+ * with a MATCH.convert
+ */
+ tob = to.toBasetype().isTypeBasic();
+ }
+ else
+ return MATCH.nomatch;
+ }
+ else
+ tob = to.isTypeBasic();
+ if (!tob)
+ return MATCH.nomatch;
+
+ if (flags & TFlags.integral)
+ {
+ // Disallow implicit conversion of integers to imaginary or complex
+ if (tob.flags & (TFlags.imaginary | TFlags.complex))
+ return MATCH.nomatch;
+
+ // If converting from integral to integral
+ if (tob.flags & TFlags.integral)
+ {
+ d_uns64 sz = size(Loc.initial);
+ d_uns64 tosz = tob.size(Loc.initial);
+
+ /* Can't convert to smaller size
+ */
+ if (sz > tosz)
+ return MATCH.nomatch;
+ /* Can't change sign if same size
+ */
+ //if (sz == tosz && (flags ^ tob.flags) & TFlags.unsigned)
+ // return MATCH.nomatch;
+ }
+ }
+ else if (flags & TFlags.floating)
+ {
+ // Disallow implicit conversion of floating point to integer
+ if (tob.flags & TFlags.integral)
+ return MATCH.nomatch;
+
+ assert(tob.flags & TFlags.floating || to.ty == Tvector);
+
+ // Disallow implicit conversion from complex to non-complex
+ if (flags & TFlags.complex && !(tob.flags & TFlags.complex))
+ return MATCH.nomatch;
+
+ // Disallow implicit conversion of real or imaginary to complex
+ if (flags & (TFlags.real_ | TFlags.imaginary) && tob.flags & TFlags.complex)
+ return MATCH.nomatch;
+
+ // Disallow implicit conversion to-from real and imaginary
+ if ((flags & (TFlags.real_ | TFlags.imaginary)) != (tob.flags & (TFlags.real_ | TFlags.imaginary)))
+ return MATCH.nomatch;
+ }
+ return MATCH.convert;
+ }
+
+ override bool isZeroInit(const ref Loc loc) const
+ {
+ switch (ty)
+ {
+ case Tchar:
+ case Twchar:
+ case Tdchar:
+ case Timaginary32:
+ case Timaginary64:
+ case Timaginary80:
+ case Tfloat32:
+ case Tfloat64:
+ case Tfloat80:
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ return false; // no
+ default:
+ return true; // yes
+ }
+ }
+
+ // For eliminating dynamic_cast
+ override TypeBasic isTypeBasic()
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * The basetype must be one of:
+ * byte[16],ubyte[16],short[8],ushort[8],int[4],uint[4],long[2],ulong[2],float[4],double[2]
+ * For AVX:
+ * byte[32],ubyte[32],short[16],ushort[16],int[8],uint[8],long[4],ulong[4],float[8],double[4]
+ */
+extern (C++) final class TypeVector : Type
+{
+ Type basetype;
+
+ extern (D) this(Type basetype)
+ {
+ super(Tvector);
+ this.basetype = basetype;
+ }
+
+ static TypeVector create(Type basetype)
+ {
+ return new TypeVector(basetype);
+ }
+
+ override const(char)* kind() const
+ {
+ return "vector";
+ }
+
+ override TypeVector syntaxCopy()
+ {
+ return new TypeVector(basetype.syntaxCopy());
+ }
+
+ override d_uns64 size(const ref Loc loc)
+ {
+ return basetype.size();
+ }
+
+ override uint alignsize()
+ {
+ return cast(uint)basetype.size();
+ }
+
+ override bool isintegral()
+ {
+ //printf("TypeVector::isintegral('%s') x%x\n", toChars(), flags);
+ return basetype.nextOf().isintegral();
+ }
+
+ override bool isfloating()
+ {
+ return basetype.nextOf().isfloating();
+ }
+
+ override bool isscalar()
+ {
+ return basetype.nextOf().isscalar();
+ }
+
+ override bool isunsigned()
+ {
+ return basetype.nextOf().isunsigned();
+ }
+
+ override bool isBoolean() const
+ {
+ return false;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeVector::implicitConvTo(%s) from %s\n", to.toChars(), toChars());
+ if (this == to)
+ return MATCH.exact;
+ if (to.ty != Tvector)
+ return MATCH.nomatch;
+
+ TypeVector tv = cast(TypeVector)to;
+ assert(basetype.ty == Tsarray && tv.basetype.ty == Tsarray);
+
+ // Can't convert to a vector which has different size.
+ if (basetype.size() != tv.basetype.size())
+ return MATCH.nomatch;
+
+ // Allow conversion to void[]
+ if (tv.basetype.nextOf().ty == Tvoid)
+ return MATCH.convert;
+
+ // Otherwise implicitly convertible only if basetypes are.
+ return basetype.implicitConvTo(tv.basetype);
+ }
+
+ override Expression defaultInitLiteral(const ref Loc loc)
+ {
+ //printf("TypeVector::defaultInitLiteral()\n");
+ assert(basetype.ty == Tsarray);
+ Expression e = basetype.defaultInitLiteral(loc);
+ auto ve = new VectorExp(loc, e, this);
+ ve.type = this;
+ ve.dim = cast(int)(basetype.size(loc) / elementType().size(loc));
+ return ve;
+ }
+
+ TypeBasic elementType()
+ {
+ assert(basetype.ty == Tsarray);
+ TypeSArray t = cast(TypeSArray)basetype;
+ TypeBasic tb = t.nextOf().isTypeBasic();
+ assert(tb);
+ return tb;
+ }
+
+ override bool isZeroInit(const ref Loc loc)
+ {
+ return basetype.isZeroInit(loc);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) abstract class TypeArray : TypeNext
+{
+ final extern (D) this(TY ty, Type next)
+ {
+ super(ty, next);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Static array, one with a fixed dimension
+ */
+extern (C++) final class TypeSArray : TypeArray
+{
+ Expression dim;
+
+ extern (D) this(Type t, Expression dim)
+ {
+ super(Tsarray, t);
+ //printf("TypeSArray(%s)\n", dim.toChars());
+ this.dim = dim;
+ }
+
+ override const(char)* kind() const
+ {
+ return "sarray";
+ }
+
+ override TypeSArray syntaxCopy()
+ {
+ Type t = next.syntaxCopy();
+ Expression e = dim.syntaxCopy();
+ auto result = new TypeSArray(t, e);
+ result.mod = mod;
+ return result;
+ }
+
+ override d_uns64 size(const ref Loc loc)
+ {
+ //printf("TypeSArray::size()\n");
+ const n = numberOfElems(loc);
+ const elemsize = baseElemOf().size(loc);
+ bool overflow = false;
+ const sz = mulu(n, elemsize, overflow);
+ if (overflow || sz >= uint.max)
+ {
+ if (elemsize != SIZE_INVALID && n != uint.max)
+ error(loc, "static array `%s` size overflowed to %lld", toChars(), cast(long)sz);
+ return SIZE_INVALID;
+ }
+ return sz;
+ }
+
+ override uint alignsize()
+ {
+ return next.alignsize();
+ }
+
+ override bool isString()
+ {
+ TY nty = next.toBasetype().ty;
+ return nty.isSomeChar;
+ }
+
+ override bool isZeroInit(const ref Loc loc)
+ {
+ return next.isZeroInit(loc);
+ }
+
+ override structalign_t alignment()
+ {
+ return next.alignment();
+ }
+
+ override MATCH constConv(Type to)
+ {
+ if (auto tsa = to.isTypeSArray())
+ {
+ if (!dim.equals(tsa.dim))
+ return MATCH.nomatch;
+ }
+ return TypeNext.constConv(to);
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars());
+ if (auto ta = to.isTypeDArray())
+ {
+ if (!MODimplicitConv(next.mod, ta.next.mod))
+ return MATCH.nomatch;
+
+ /* Allow conversion to void[]
+ */
+ if (ta.next.ty == Tvoid)
+ {
+ return MATCH.convert;
+ }
+
+ MATCH m = next.constConv(ta.next);
+ if (m > MATCH.nomatch)
+ {
+ return MATCH.convert;
+ }
+ return MATCH.nomatch;
+ }
+ if (auto tsa = to.isTypeSArray())
+ {
+ if (this == to)
+ return MATCH.exact;
+
+ if (dim.equals(tsa.dim))
+ {
+ MATCH m = next.implicitConvTo(tsa.next);
+
+ /* Allow conversion to non-interface base class.
+ */
+ if (m == MATCH.convert &&
+ next.ty == Tclass)
+ {
+ if (auto toc = tsa.next.isTypeClass)
+ {
+ if (!toc.sym.isInterfaceDeclaration)
+ return MATCH.convert;
+ }
+ }
+
+ /* Since static arrays are value types, allow
+ * conversions from const elements to non-const
+ * ones, just like we allow conversion from const int
+ * to int.
+ */
+ if (m >= MATCH.constant)
+ {
+ if (mod != to.mod)
+ m = MATCH.constant;
+ return m;
+ }
+ }
+ }
+ return MATCH.nomatch;
+ }
+
+ override Expression defaultInitLiteral(const ref Loc loc)
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars());
+ }
+ size_t d = cast(size_t)dim.toInteger();
+ Expression elementinit;
+ if (next.ty == Tvoid)
+ elementinit = tuns8.defaultInitLiteral(loc);
+ else
+ elementinit = next.defaultInitLiteral(loc);
+ auto elements = new Expressions(d);
+ foreach (ref e; *elements)
+ e = null;
+ auto ae = new ArrayLiteralExp(Loc.initial, this, elementinit, elements);
+ return ae;
+ }
+
+ override bool hasPointers()
+ {
+ /* Don't want to do this, because:
+ * struct S { T* array[0]; }
+ * may be a variable length struct.
+ */
+ //if (dim.toInteger() == 0)
+ // return false;
+
+ if (next.ty == Tvoid)
+ {
+ // Arrays of void contain arbitrary data, which may include pointers
+ return true;
+ }
+ else
+ return next.hasPointers();
+ }
+
+ override bool hasInvariant()
+ {
+ return next.hasInvariant();
+ }
+
+ override bool needsDestruction()
+ {
+ return next.needsDestruction();
+ }
+
+ override bool needsCopyOrPostblit()
+ {
+ return next.needsCopyOrPostblit();
+ }
+
+ /*********************************
+ *
+ */
+ override bool needsNested()
+ {
+ return next.needsNested();
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Dynamic array, no dimension
+ */
+extern (C++) final class TypeDArray : TypeArray
+{
+ extern (D) this(Type t)
+ {
+ super(Tarray, t);
+ //printf("TypeDArray(t = %p)\n", t);
+ }
+
+ override const(char)* kind() const
+ {
+ return "darray";
+ }
+
+ override TypeDArray syntaxCopy()
+ {
+ Type t = next.syntaxCopy();
+ if (t == next)
+ return this;
+
+ auto result = new TypeDArray(t);
+ result.mod = mod;
+ return result;
+ }
+
+ override d_uns64 size(const ref Loc loc) const
+ {
+ //printf("TypeDArray::size()\n");
+ return target.ptrsize * 2;
+ }
+
+ override uint alignsize() const
+ {
+ // A DArray consists of two ptr-sized values, so align it on pointer size
+ // boundary
+ return target.ptrsize;
+ }
+
+ override bool isString()
+ {
+ TY nty = next.toBasetype().ty;
+ return nty.isSomeChar;
+ }
+
+ override bool isZeroInit(const ref Loc loc) const
+ {
+ return true;
+ }
+
+ override bool isBoolean() const
+ {
+ return true;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeDArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars());
+ if (equals(to))
+ return MATCH.exact;
+
+ if (auto ta = to.isTypeDArray())
+ {
+ if (!MODimplicitConv(next.mod, ta.next.mod))
+ return MATCH.nomatch; // not const-compatible
+
+ /* Allow conversion to void[]
+ */
+ if (next.ty != Tvoid && ta.next.ty == Tvoid)
+ {
+ return MATCH.convert;
+ }
+
+ MATCH m = next.constConv(ta.next);
+ if (m > MATCH.nomatch)
+ {
+ if (m == MATCH.exact && mod != to.mod)
+ m = MATCH.constant;
+ return m;
+ }
+ }
+ return Type.implicitConvTo(to);
+ }
+
+ override bool hasPointers() const
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeAArray : TypeArray
+{
+ Type index; // key type
+ Loc loc;
+
+ extern (D) this(Type t, Type index)
+ {
+ super(Taarray, t);
+ this.index = index;
+ }
+
+ static TypeAArray create(Type t, Type index)
+ {
+ return new TypeAArray(t, index);
+ }
+
+ override const(char)* kind() const
+ {
+ return "aarray";
+ }
+
+ override TypeAArray syntaxCopy()
+ {
+ Type t = next.syntaxCopy();
+ Type ti = index.syntaxCopy();
+ if (t == next && ti == index)
+ return this;
+
+ auto result = new TypeAArray(t, ti);
+ result.mod = mod;
+ return result;
+ }
+
+ override d_uns64 size(const ref Loc loc) const
+ {
+ return target.ptrsize;
+ }
+
+ override bool isZeroInit(const ref Loc loc) const
+ {
+ return true;
+ }
+
+ override bool isBoolean() const
+ {
+ return true;
+ }
+
+ override bool hasPointers() const
+ {
+ return true;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeAArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars());
+ if (equals(to))
+ return MATCH.exact;
+
+ if (auto ta = to.isTypeAArray())
+ {
+ if (!MODimplicitConv(next.mod, ta.next.mod))
+ return MATCH.nomatch; // not const-compatible
+
+ if (!MODimplicitConv(index.mod, ta.index.mod))
+ return MATCH.nomatch; // not const-compatible
+
+ MATCH m = next.constConv(ta.next);
+ MATCH mi = index.constConv(ta.index);
+ if (m > MATCH.nomatch && mi > MATCH.nomatch)
+ {
+ return MODimplicitConv(mod, to.mod) ? MATCH.constant : MATCH.nomatch;
+ }
+ }
+ return Type.implicitConvTo(to);
+ }
+
+ override MATCH constConv(Type to)
+ {
+ if (auto taa = to.isTypeAArray())
+ {
+ MATCH mindex = index.constConv(taa.index);
+ MATCH mkey = next.constConv(taa.next);
+ // Pick the worst match
+ return mkey < mindex ? mkey : mindex;
+ }
+ return Type.constConv(to);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypePointer : TypeNext
+{
+ extern (D) this(Type t)
+ {
+ super(Tpointer, t);
+ }
+
+ static TypePointer create(Type t)
+ {
+ return new TypePointer(t);
+ }
+
+ override const(char)* kind() const
+ {
+ return "pointer";
+ }
+
+ override TypePointer syntaxCopy()
+ {
+ Type t = next.syntaxCopy();
+ if (t == next)
+ return this;
+
+ auto result = new TypePointer(t);
+ result.mod = mod;
+ return result;
+ }
+
+ override d_uns64 size(const ref Loc loc) const
+ {
+ return target.ptrsize;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypePointer::implicitConvTo(to = %s) %s\n", to.toChars(), toChars());
+ if (equals(to))
+ return MATCH.exact;
+
+ if (next.ty == Tfunction)
+ {
+ if (auto tp = to.isTypePointer())
+ {
+ if (tp.next.ty == Tfunction)
+ {
+ if (next.equals(tp.next))
+ return MATCH.constant;
+
+ if (next.covariant(tp.next) == Covariant.yes)
+ {
+ Type tret = this.next.nextOf();
+ Type toret = tp.next.nextOf();
+ if (tret.ty == Tclass && toret.ty == Tclass)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=10219
+ * Check covariant interface return with offset tweaking.
+ * interface I {}
+ * class C : Object, I {}
+ * I function() dg = function C() {} // should be error
+ */
+ int offset = 0;
+ if (toret.isBaseOf(tret, &offset) && offset != 0)
+ return MATCH.nomatch;
+ }
+ return MATCH.convert;
+ }
+ }
+ else if (tp.next.ty == Tvoid)
+ {
+ // Allow conversions to void*
+ return MATCH.convert;
+ }
+ }
+ return MATCH.nomatch;
+ }
+ else if (auto tp = to.isTypePointer())
+ {
+ assert(tp.next);
+
+ if (!MODimplicitConv(next.mod, tp.next.mod))
+ return MATCH.nomatch; // not const-compatible
+
+ /* Alloc conversion to void*
+ */
+ if (next.ty != Tvoid && tp.next.ty == Tvoid)
+ {
+ return MATCH.convert;
+ }
+
+ MATCH m = next.constConv(tp.next);
+ if (m > MATCH.nomatch)
+ {
+ if (m == MATCH.exact && mod != to.mod)
+ m = MATCH.constant;
+ return m;
+ }
+ }
+ return MATCH.nomatch;
+ }
+
+ override MATCH constConv(Type to)
+ {
+ if (next.ty == Tfunction)
+ {
+ if (to.nextOf() && next.equals((cast(TypeNext)to).next))
+ return Type.constConv(to);
+ else
+ return MATCH.nomatch;
+ }
+ return TypeNext.constConv(to);
+ }
+
+ override bool isscalar() const
+ {
+ return true;
+ }
+
+ override bool isZeroInit(const ref Loc loc) const
+ {
+ return true;
+ }
+
+ override bool hasPointers() const
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeReference : TypeNext
+{
+ extern (D) this(Type t)
+ {
+ super(Treference, t);
+ // BUG: what about references to static arrays?
+ }
+
+ override const(char)* kind() const
+ {
+ return "reference";
+ }
+
+ override TypeReference syntaxCopy()
+ {
+ Type t = next.syntaxCopy();
+ if (t == next)
+ return this;
+
+ auto result = new TypeReference(t);
+ result.mod = mod;
+ return result;
+ }
+
+ override d_uns64 size(const ref Loc loc) const
+ {
+ return target.ptrsize;
+ }
+
+ override bool isZeroInit(const ref Loc loc) const
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+enum RET : int
+{
+ regs = 1, // returned in registers
+ stack = 2, // returned on stack
+}
+
+enum TRUSTformat : int
+{
+ TRUSTformatDefault, // do not emit @system when trust == TRUST.default_
+ TRUSTformatSystem, // emit @system when trust == TRUST.default_
+}
+
+alias TRUSTformatDefault = TRUSTformat.TRUSTformatDefault;
+alias TRUSTformatSystem = TRUSTformat.TRUSTformatSystem;
+
+/***********************************************************
+ */
+extern (C++) final class TypeFunction : TypeNext
+{
+ // .next is the return type
+
+ ParameterList parameterList; // function parameters
+
+ private enum FunctionFlag : uint
+ {
+ none = 0,
+ isnothrow = 0x0001, // nothrow
+ isnogc = 0x0002, // is @nogc
+ isproperty = 0x0004, // can be called without parentheses
+ isref = 0x0008, // returns a reference
+ isreturn = 0x0010, // 'this' is returned by ref
+ isscope = 0x0020, // 'this' is scope
+ isreturninferred= 0x0040, // 'this' is return from inference
+ isscopeinferred = 0x0080, // 'this' is scope from inference
+ islive = 0x0100, // is @live
+ incomplete = 0x0200, // return type or default arguments removed
+ inoutParam = 0x0400, // inout on the parameters
+ inoutQual = 0x0800, // inout on the qualifier
+ isctor = 0x1000, // the function is a constructor
+ }
+
+ LINK linkage; // calling convention
+ FunctionFlag funcFlags;
+ TRUST trust; // level of trust
+ PURE purity = PURE.impure;
+ byte inuse;
+ Expressions* fargs; // function arguments
+
+ extern (D) this(ParameterList pl, Type treturn, LINK linkage, StorageClass stc = 0)
+ {
+ super(Tfunction, treturn);
+ //if (!treturn) *(char*)0=0;
+ // assert(treturn);
+ assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.typesafe);
+ this.parameterList = pl;
+ this.linkage = linkage;
+
+ if (stc & STC.pure_)
+ this.purity = PURE.fwdref;
+ if (stc & STC.nothrow_)
+ this.isnothrow = true;
+ if (stc & STC.nogc)
+ this.isnogc = true;
+ if (stc & STC.property)
+ this.isproperty = true;
+ if (stc & STC.live)
+ this.islive = true;
+
+ if (stc & STC.ref_)
+ this.isref = true;
+ if (stc & STC.return_)
+ this.isreturn = true;
+ if (stc & STC.returninferred)
+ this.isreturninferred = true;
+ if (stc & STC.scope_)
+ this.isScopeQual = true;
+ if (stc & STC.scopeinferred)
+ this.isscopeinferred = true;
+
+ this.trust = TRUST.default_;
+ if (stc & STC.safe)
+ this.trust = TRUST.safe;
+ if (stc & STC.system)
+ this.trust = TRUST.system;
+ if (stc & STC.trusted)
+ this.trust = TRUST.trusted;
+ }
+
+ static TypeFunction create(Parameters* parameters, Type treturn, ubyte varargs, LINK linkage, StorageClass stc = 0)
+ {
+ return new TypeFunction(ParameterList(parameters, cast(VarArg)varargs), treturn, linkage, stc);
+ }
+
+ override const(char)* kind() const
+ {
+ return "function";
+ }
+
+ override TypeFunction syntaxCopy()
+ {
+ Type treturn = next ? next.syntaxCopy() : null;
+ auto t = new TypeFunction(parameterList.syntaxCopy(), treturn, linkage);
+ t.mod = mod;
+ t.isnothrow = isnothrow;
+ t.isnogc = isnogc;
+ t.islive = islive;
+ t.purity = purity;
+ t.isproperty = isproperty;
+ t.isref = isref;
+ t.isreturn = isreturn;
+ t.isScopeQual = isScopeQual;
+ t.isreturninferred = isreturninferred;
+ t.isscopeinferred = isscopeinferred;
+ t.isInOutParam = isInOutParam;
+ t.isInOutQual = isInOutQual;
+ t.trust = trust;
+ t.fargs = fargs;
+ t.isctor = isctor;
+ return t;
+ }
+
+ /********************************************
+ * Set 'purity' field of 'this'.
+ * Do this lazily, as the parameter types might be forward referenced.
+ */
+ void purityLevel()
+ {
+ TypeFunction tf = this;
+ if (tf.purity != PURE.fwdref)
+ return;
+
+ /* Determine purity level based on mutability of t
+ * and whether it is a 'ref' type or not.
+ */
+ static PURE purityOfType(bool isref, Type t)
+ {
+ if (isref)
+ {
+ if (t.mod & MODFlags.immutable_)
+ return PURE.strong;
+ if (t.mod & (MODFlags.const_ | MODFlags.wild))
+ return PURE.const_;
+ return PURE.weak;
+ }
+
+ t = t.baseElemOf();
+
+ if (!t.hasPointers() || t.mod & MODFlags.immutable_)
+ return PURE.strong;
+
+ /* Accept immutable(T)[] and immutable(T)* as being strongly pure
+ */
+ if (t.ty == Tarray || t.ty == Tpointer)
+ {
+ Type tn = t.nextOf().toBasetype();
+ if (tn.mod & MODFlags.immutable_)
+ return PURE.strong;
+ if (tn.mod & (MODFlags.const_ | MODFlags.wild))
+ return PURE.const_;
+ }
+
+ /* The rest of this is too strict; fix later.
+ * For example, the only pointer members of a struct may be immutable,
+ * which would maintain strong purity.
+ * (Just like for dynamic arrays and pointers above.)
+ */
+ if (t.mod & (MODFlags.const_ | MODFlags.wild))
+ return PURE.const_;
+
+ /* Should catch delegates and function pointers, and fold in their purity
+ */
+ return PURE.weak;
+ }
+
+ purity = PURE.strong; // assume strong until something weakens it
+
+ /* Evaluate what kind of purity based on the modifiers for the parameters
+ */
+ Lloop: foreach (i, fparam; tf.parameterList)
+ {
+ Type t = fparam.type;
+ if (!t)
+ continue;
+
+ if (fparam.storageClass & (STC.lazy_ | STC.out_))
+ {
+ purity = PURE.weak;
+ break;
+ }
+ switch (purityOfType((fparam.storageClass & STC.ref_) != 0, t))
+ {
+ case PURE.weak:
+ purity = PURE.weak;
+ break Lloop; // since PURE.weak, no need to check further
+
+ case PURE.const_:
+ purity = PURE.const_;
+ continue;
+
+ case PURE.strong:
+ continue;
+
+ default:
+ assert(0);
+ }
+ }
+
+ if (purity > PURE.weak && tf.nextOf())
+ {
+ /* Adjust purity based on mutability of return type.
+ * https://issues.dlang.org/show_bug.cgi?id=15862
+ */
+ const purity2 = purityOfType(tf.isref, tf.nextOf());
+ if (purity2 < purity)
+ purity = purity2;
+ }
+ tf.purity = purity;
+ }
+
+ /********************************************
+ * Return true if there are lazy parameters.
+ */
+ bool hasLazyParameters()
+ {
+ foreach (i, fparam; parameterList)
+ {
+ if (fparam.storageClass & STC.lazy_)
+ return true;
+ }
+ return false;
+ }
+
+ /*******************************
+ * Check for `extern (D) U func(T t, ...)` variadic function type,
+ * which has `_arguments[]` added as the first argument.
+ * Returns:
+ * true if D-style variadic
+ */
+ bool isDstyleVariadic() const pure nothrow
+ {
+ return linkage == LINK.d && parameterList.varargs == VarArg.variadic;
+ }
+
+ /***************************
+ * Examine function signature for parameter p and see if
+ * the value of p can 'escape' the scope of the function.
+ * This is useful to minimize the needed annotations for the parameters.
+ * Params:
+ * tthis = type of `this` parameter, null if none
+ * p = parameter to this function
+ * Returns:
+ * true if escapes via assignment to global or through a parameter
+ */
+ bool parameterEscapes(Type tthis, Parameter p)
+ {
+ /* Scope parameters do not escape.
+ * Allow 'lazy' to imply 'scope' -
+ * lazy parameters can be passed along
+ * as lazy parameters to the next function, but that isn't
+ * escaping.
+ */
+ if (parameterStorageClass(tthis, p) & (STC.scope_ | STC.lazy_))
+ return false;
+ return true;
+ }
+
+ /************************************
+ * Take the specified storage class for p,
+ * and use the function signature to infer whether
+ * STC.scope_ and STC.return_ should be OR'd in.
+ * (This will not affect the name mangling.)
+ * Params:
+ * tthis = type of `this` parameter, null if none
+ * p = parameter to this function
+ * Returns:
+ * storage class with STC.scope_ or STC.return_ OR'd in
+ */
+ StorageClass parameterStorageClass(Type tthis, Parameter p)
+ {
+ //printf("parameterStorageClass(p: %s)\n", p.toChars());
+ auto stc = p.storageClass;
+ if (global.params.useDIP1000 != FeatureState.enabled)
+ return stc;
+
+ // When the preview switch is enable, `in` parameters are `scope`
+ if (stc & STC.in_ && global.params.previewIn)
+ return stc | STC.scope_;
+
+ if (stc & (STC.scope_ | STC.return_ | STC.lazy_) || purity == PURE.impure)
+ return stc;
+
+ /* If haven't inferred the return type yet, can't infer storage classes
+ */
+ if (!nextOf())
+ return stc;
+
+ purityLevel();
+
+ // See if p can escape via any of the other parameters
+ if (purity == PURE.weak)
+ {
+ // Check escaping through parameters
+ foreach (i, fparam; parameterList)
+ {
+ if (fparam == p)
+ continue;
+ Type t = fparam.type;
+ if (!t)
+ continue;
+ t = t.baseElemOf();
+ if (t.isMutable() && t.hasPointers())
+ {
+ if (fparam.isReference())
+ {
+ }
+ else if (t.ty == Tarray || t.ty == Tpointer)
+ {
+ Type tn = t.nextOf().toBasetype();
+ if (!(tn.isMutable() && tn.hasPointers()))
+ continue;
+ }
+ return stc;
+ }
+ }
+
+ // Check escaping through `this`
+ if (tthis && tthis.isMutable())
+ {
+ auto tb = tthis.toBasetype();
+ AggregateDeclaration ad;
+ if (auto tc = tb.isTypeClass())
+ ad = tc.sym;
+ else if (auto ts = tb.isTypeStruct())
+ ad = ts.sym;
+ else
+ assert(0);
+ foreach (VarDeclaration v; ad.fields)
+ {
+ if (v.hasPointers())
+ return stc;
+ }
+ }
+ }
+
+ /* Inferring STC.return_ here has false positives
+ * for pure functions, producing spurious error messages
+ * about escaping references.
+ * Give up on it for now.
+ */
+ version (none)
+ {
+ stc |= STC.scope_;
+
+ Type tret = nextOf().toBasetype();
+ if (isref || tret.hasPointers())
+ {
+ /* The result has references, so p could be escaping
+ * that way.
+ */
+ stc |= STC.return_;
+ }
+ }
+ else
+ {
+ // Check escaping through return value
+ Type tret = nextOf().toBasetype();
+ if (isref || tret.hasPointers())
+ {
+ return stc;
+ }
+
+ stc |= STC.scope_;
+ }
+
+ return stc;
+ }
+
+ override Type addStorageClass(StorageClass stc)
+ {
+ //printf("addStorageClass(%llx) %d\n", stc, (stc & STC.scope_) != 0);
+ TypeFunction t = Type.addStorageClass(stc).toTypeFunction();
+ if ((stc & STC.pure_ && !t.purity) ||
+ (stc & STC.nothrow_ && !t.isnothrow) ||
+ (stc & STC.nogc && !t.isnogc) ||
+ (stc & STC.scope_ && !t.isScopeQual) ||
+ (stc & STC.safe && t.trust < TRUST.trusted))
+ {
+ // Klunky to change these
+ auto tf = new TypeFunction(t.parameterList, t.next, t.linkage, 0);
+ tf.mod = t.mod;
+ tf.fargs = fargs;
+ tf.purity = t.purity;
+ tf.isnothrow = t.isnothrow;
+ tf.isnogc = t.isnogc;
+ tf.isproperty = t.isproperty;
+ tf.isref = t.isref;
+ tf.isreturn = t.isreturn;
+ tf.isScopeQual = t.isScopeQual;
+ tf.isreturninferred = t.isreturninferred;
+ tf.isscopeinferred = t.isscopeinferred;
+ tf.trust = t.trust;
+ tf.isInOutParam = t.isInOutParam;
+ tf.isInOutQual = t.isInOutQual;
+ tf.isctor = t.isctor;
+
+ if (stc & STC.pure_)
+ tf.purity = PURE.fwdref;
+ if (stc & STC.nothrow_)
+ tf.isnothrow = true;
+ if (stc & STC.nogc)
+ tf.isnogc = true;
+ if (stc & STC.safe)
+ tf.trust = TRUST.safe;
+ if (stc & STC.scope_)
+ {
+ tf.isScopeQual = true;
+ if (stc & STC.scopeinferred)
+ tf.isscopeinferred = true;
+ }
+
+ tf.deco = tf.merge().deco;
+ t = tf;
+ }
+ return t;
+ }
+
+ override Type substWildTo(uint)
+ {
+ if (!iswild && !(mod & MODFlags.wild))
+ return this;
+
+ // Substitude inout qualifier of function type to mutable or immutable
+ // would break type system. Instead substitude inout to the most weak
+ // qualifer - const.
+ uint m = MODFlags.const_;
+
+ assert(next);
+ Type tret = next.substWildTo(m);
+ Parameters* params = parameterList.parameters;
+ if (mod & MODFlags.wild)
+ params = parameterList.parameters.copy();
+ for (size_t i = 0; i < params.dim; i++)
+ {
+ Parameter p = (*params)[i];
+ Type t = p.type.substWildTo(m);
+ if (t == p.type)
+ continue;
+ if (params == parameterList.parameters)
+ params = parameterList.parameters.copy();
+ (*params)[i] = new Parameter(p.storageClass, t, null, null, null);
+ }
+ if (next == tret && params == parameterList.parameters)
+ return this;
+
+ // Similar to TypeFunction::syntaxCopy;
+ auto t = new TypeFunction(ParameterList(params, parameterList.varargs), tret, linkage);
+ t.mod = ((mod & MODFlags.wild) ? (mod & ~MODFlags.wild) | MODFlags.const_ : mod);
+ t.isnothrow = isnothrow;
+ t.isnogc = isnogc;
+ t.purity = purity;
+ t.isproperty = isproperty;
+ t.isref = isref;
+ t.isreturn = isreturn;
+ t.isScopeQual = isScopeQual;
+ t.isreturninferred = isreturninferred;
+ t.isscopeinferred = isscopeinferred;
+ t.isInOutParam = false;
+ t.isInOutQual = false;
+ t.trust = trust;
+ t.fargs = fargs;
+ t.isctor = isctor;
+ return t.merge();
+ }
+
+ // arguments get specially formatted
+ private const(char)* getParamError(Expression arg, Parameter par)
+ {
+ if (global.gag && !global.params.showGaggedErrors)
+ return null;
+ // show qualification when toChars() is the same but types are different
+ auto at = arg.type.toChars();
+ bool qual = !arg.type.equals(par.type) && strcmp(at, par.type.toChars()) == 0;
+ if (qual)
+ at = arg.type.toPrettyChars(true);
+ OutBuffer buf;
+ // only mention rvalue if it's relevant
+ const rv = !arg.isLvalue() && par.isReference();
+ buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`",
+ rv ? "rvalue ".ptr : "".ptr, arg.toChars(), at,
+ parameterToChars(par, this, qual));
+ return buf.extractChars();
+ }
+
+ private extern(D) const(char)* getMatchError(A...)(const(char)* format, A args)
+ {
+ if (global.gag && !global.params.showGaggedErrors)
+ return null;
+ OutBuffer buf;
+ buf.printf(format, args);
+ return buf.extractChars();
+ }
+
+ /********************************
+ * 'args' are being matched to function 'this'
+ * Determine match level.
+ * Params:
+ * tthis = type of `this` pointer, null if not member function
+ * args = array of function arguments
+ * flag = 1: performing a partial ordering match
+ * pMessage = address to store error message, or null
+ * sc = context
+ * Returns:
+ * MATCHxxxx
+ */
+ extern (D) MATCH callMatch(Type tthis, Expression[] args, int flag = 0, const(char)** pMessage = null, Scope* sc = null)
+ {
+ //printf("TypeFunction::callMatch() %s\n", toChars());
+ MATCH match = MATCH.exact; // assume exact match
+ ubyte wildmatch = 0;
+
+ if (tthis)
+ {
+ Type t = tthis;
+ if (t.toBasetype().ty == Tpointer)
+ t = t.toBasetype().nextOf(); // change struct* to struct
+ if (t.mod != mod)
+ {
+ if (MODimplicitConv(t.mod, mod))
+ match = MATCH.constant;
+ else if ((mod & MODFlags.wild) && MODimplicitConv(t.mod, (mod & ~MODFlags.wild) | MODFlags.const_))
+ {
+ match = MATCH.constant;
+ }
+ else
+ return MATCH.nomatch;
+ }
+ if (isWild())
+ {
+ if (t.isWild())
+ wildmatch |= MODFlags.wild;
+ else if (t.isConst())
+ wildmatch |= MODFlags.const_;
+ else if (t.isImmutable())
+ wildmatch |= MODFlags.immutable_;
+ else
+ wildmatch |= MODFlags.mutable;
+ }
+ }
+
+ const nparams = parameterList.length;
+ const nargs = args.length;
+ if (nargs > nparams)
+ {
+ if (parameterList.varargs == VarArg.none)
+ {
+ // suppress early exit if an error message is wanted,
+ // so we can check any matching args are valid
+ if (!pMessage)
+ goto Nomatch;
+ }
+ // too many args; no match
+ match = MATCH.convert; // match ... with a "conversion" match level
+ }
+
+ foreach (u, p; parameterList)
+ {
+ if (u == nargs)
+ break;
+
+ Expression arg = args[u];
+ assert(arg);
+ Type tprm = p.type;
+ Type targ = arg.type;
+
+ if (!(p.storageClass & STC.lazy_ && tprm.ty == Tvoid && targ.ty != Tvoid))
+ {
+ const isRef = p.isReference();
+ wildmatch |= targ.deduceWild(tprm, isRef);
+ }
+ }
+ if (wildmatch)
+ {
+ /* Calculate wild matching modifier
+ */
+ if (wildmatch & MODFlags.const_ || wildmatch & (wildmatch - 1))
+ wildmatch = MODFlags.const_;
+ else if (wildmatch & MODFlags.immutable_)
+ wildmatch = MODFlags.immutable_;
+ else if (wildmatch & MODFlags.wild)
+ wildmatch = MODFlags.wild;
+ else
+ {
+ assert(wildmatch & MODFlags.mutable);
+ wildmatch = MODFlags.mutable;
+ }
+ }
+
+ foreach (u, p; parameterList)
+ {
+ MATCH m;
+
+ assert(p);
+ if (u >= nargs)
+ {
+ if (p.defaultArg)
+ continue;
+ // try typesafe variadics
+ goto L1;
+ }
+ {
+ Expression arg = args[u];
+ assert(arg);
+ //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars());
+
+ Type targ = arg.type;
+ Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type;
+
+ if (p.storageClass & STC.lazy_ && tprm.ty == Tvoid && targ.ty != Tvoid)
+ m = MATCH.convert;
+ else
+ {
+ //printf("%s of type %s implicitConvTo %s\n", arg.toChars(), targ.toChars(), tprm.toChars());
+ if (flag)
+ {
+ // for partial ordering, value is an irrelevant mockup, just look at the type
+ m = targ.implicitConvTo(tprm);
+ }
+ else
+ {
+ const isRef = p.isReference();
+
+ StructDeclaration argStruct, prmStruct;
+
+ // first look for a copy constructor
+ if (arg.isLvalue() && !isRef && targ.ty == Tstruct && tprm.ty == Tstruct)
+ {
+ // if the argument and the parameter are of the same unqualified struct type
+ argStruct = (cast(TypeStruct)targ).sym;
+ prmStruct = (cast(TypeStruct)tprm).sym;
+ }
+
+ // check if the copy constructor may be called to copy the argument
+ if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
+ {
+ /* this is done by seeing if a call to the copy constructor can be made:
+ *
+ * typeof(tprm) __copytmp;
+ * copytmp.__copyCtor(arg);
+ */
+ auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytmp"), null);
+ tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe;
+ tmp.dsymbolSemantic(sc);
+ Expression ve = new VarExp(arg.loc, tmp);
+ Expression e = new DotIdExp(arg.loc, ve, Id.ctor);
+ e = new CallExp(arg.loc, e, arg);
+ //printf("e = %s\n", e.toChars());
+ if(.trySemantic(e, sc))
+ m = MATCH.exact;
+ else
+ {
+ m = MATCH.nomatch;
+ if (pMessage)
+ {
+ OutBuffer buf;
+ buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies",
+ argStruct.toChars(), targ.toChars(), tprm.toChars());
+ *pMessage = buf.extractChars();
+ }
+ goto Nomatch;
+ }
+ }
+ else
+ m = arg.implicitConvTo(tprm);
+ }
+ //printf("match %d\n", m);
+ }
+
+ // Non-lvalues do not match ref or out parameters
+ if (p.isReference())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13783
+ // Don't use toBasetype() to handle enum types.
+ Type ta = targ;
+ Type tp = tprm;
+ //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars());
+
+ if (m && !arg.isLvalue())
+ {
+ if (p.storageClass & STC.out_)
+ {
+ if (pMessage) *pMessage = getParamError(arg, p);
+ goto Nomatch;
+ }
+
+ if (arg.op == TOK.string_ && tp.ty == Tsarray)
+ {
+ if (ta.ty != Tsarray)
+ {
+ Type tn = tp.nextOf().castMod(ta.nextOf().mod);
+ dinteger_t dim = (cast(StringExp)arg).len;
+ ta = tn.sarrayOf(dim);
+ }
+ }
+ else if (arg.op == TOK.slice && tp.ty == Tsarray)
+ {
+ // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
+ if (ta.ty != Tsarray)
+ {
+ Type tn = ta.nextOf();
+ dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
+ ta = tn.sarrayOf(dim);
+ }
+ }
+ else if ((p.storageClass & STC.in_) && global.params.previewIn)
+ {
+ // Allow converting a literal to an `in` which is `ref`
+ if (arg.op == TOK.arrayLiteral && tp.ty == Tsarray)
+ {
+ Type tn = tp.nextOf();
+ dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
+ ta = tn.sarrayOf(dim);
+ }
+
+ // Need to make this a rvalue through a temporary
+ m = MATCH.convert;
+ }
+ else if (!global.params.rvalueRefParam ||
+ p.storageClass & STC.out_ ||
+ !arg.type.isCopyable()) // can't copy to temp for ref parameter
+ {
+ if (pMessage) *pMessage = getParamError(arg, p);
+ goto Nomatch;
+ }
+ else
+ {
+ /* in functionParameters() we'll convert this
+ * rvalue into a temporary
+ */
+ m = MATCH.convert;
+ }
+ }
+
+ /* If the match is not already perfect or if the arg
+ is not a lvalue then try the `alias this` chain
+ see https://issues.dlang.org/show_bug.cgi?id=15674
+ and https://issues.dlang.org/show_bug.cgi?id=21905
+ */
+ if (ta != tp || !arg.isLvalue())
+ {
+ Type firsttab = ta.toBasetype();
+ while (1)
+ {
+ Type tab = ta.toBasetype();
+ Type tat = tab.aliasthisOf();
+ if (!tat || !tat.implicitConvTo(tprm))
+ break;
+ if (tat == tab || tat == firsttab)
+ break;
+ ta = tat;
+ }
+ }
+
+ /* A ref variable should work like a head-const reference.
+ * e.g. disallows:
+ * ref T <- an lvalue of const(T) argument
+ * ref T[dim] <- an lvalue of const(T[dim]) argument
+ */
+ if (!ta.constConv(tp))
+ {
+ if (pMessage) *pMessage = getParamError(arg, p);
+ goto Nomatch;
+ }
+ }
+ }
+
+ /* prefer matching the element type rather than the array
+ * type when more arguments are present with T[]...
+ */
+ if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams && nargs > nparams)
+ goto L1;
+
+ //printf("\tm = %d\n", m);
+ if (m == MATCH.nomatch) // if no match
+ {
+ L1:
+ if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams) // if last varargs param
+ {
+ Type tb = p.type.toBasetype();
+ TypeSArray tsa;
+ dinteger_t sz;
+
+ switch (tb.ty)
+ {
+ case Tsarray:
+ tsa = cast(TypeSArray)tb;
+ sz = tsa.dim.toInteger();
+ if (sz != nargs - u)
+ {
+ if (pMessage)
+ // Windows (Vista) OutBuffer.vprintf issue? 2nd argument always zero
+ //*pMessage = getMatchError("expected %d variadic argument(s), not %d", sz, nargs - u);
+ if (!global.gag || global.params.showGaggedErrors)
+ {
+ OutBuffer buf;
+ buf.printf("expected %llu variadic argument(s)", sz);
+ buf.printf(", not %zu", nargs - u);
+ *pMessage = buf.extractChars();
+ }
+ goto Nomatch;
+ }
+ goto case Tarray;
+ case Tarray:
+ {
+ TypeArray ta = cast(TypeArray)tb;
+ foreach (arg; args[u .. nargs])
+ {
+ assert(arg);
+
+ /* If lazy array of delegates,
+ * convert arg(s) to delegate(s)
+ */
+ Type tret = p.isLazyArray();
+ if (tret)
+ {
+ if (ta.next.equals(arg.type))
+ m = MATCH.exact;
+ else if (tret.toBasetype().ty == Tvoid)
+ m = MATCH.convert;
+ else
+ {
+ m = arg.implicitConvTo(tret);
+ if (m == MATCH.nomatch)
+ m = arg.implicitConvTo(ta.next);
+ }
+ }
+ else
+ m = arg.implicitConvTo(ta.next);
+
+ if (m == MATCH.nomatch)
+ {
+ if (pMessage) *pMessage = getParamError(arg, p);
+ goto Nomatch;
+ }
+ if (m < match)
+ match = m;
+ }
+ goto Ldone;
+ }
+ case Tclass:
+ // Should see if there's a constructor match?
+ // Or just leave it ambiguous?
+ goto Ldone;
+
+ default:
+ break;
+ }
+ }
+ if (pMessage && u < nargs)
+ *pMessage = getParamError(args[u], p);
+ else if (pMessage)
+ *pMessage = getMatchError("missing argument for parameter #%d: `%s`",
+ u + 1, parameterToChars(p, this, false));
+ goto Nomatch;
+ }
+ if (m < match)
+ match = m; // pick worst match
+ }
+
+ Ldone:
+ if (pMessage && !parameterList.varargs && nargs > nparams)
+ {
+ // all parameters had a match, but there are surplus args
+ *pMessage = getMatchError("expected %d argument(s), not %d", nparams, nargs);
+ goto Nomatch;
+ }
+ //printf("match = %d\n", match);
+ return match;
+
+ Nomatch:
+ //printf("no match\n");
+ return MATCH.nomatch;
+ }
+
+ /** Extends TypeNext.constConv by also checking for matching attributes **/
+ override MATCH constConv(Type to)
+ {
+ // Attributes need to match exactly, otherwise it's an implicit conversion
+ if (this.ty != to.ty || !this.attributesEqual(cast(TypeFunction) to))
+ return MATCH.nomatch;
+
+ return super.constConv(to);
+ }
+
+ extern (D) bool checkRetType(const ref Loc loc)
+ {
+ Type tb = next.toBasetype();
+ if (tb.ty == Tfunction)
+ {
+ error(loc, "functions cannot return a function");
+ next = Type.terror;
+ }
+ if (tb.ty == Ttuple)
+ {
+ error(loc, "functions cannot return a tuple");
+ next = Type.terror;
+ }
+ if (!isref && (tb.ty == Tstruct || tb.ty == Tsarray))
+ {
+ if (auto ts = tb.baseElemOf().isTypeStruct())
+ {
+ if (!ts.sym.members)
+ {
+ error(loc, "functions cannot return opaque type `%s` by value", tb.toChars());
+ next = Type.terror;
+ }
+ }
+ }
+ if (tb.ty == Terror)
+ return true;
+ return false;
+ }
+
+ /// set or get if the function has the `nothrow` attribute
+ bool isnothrow() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.isnothrow) != 0;
+ }
+ /// ditto
+ void isnothrow(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.isnothrow;
+ else funcFlags &= ~FunctionFlag.isnothrow;
+ }
+
+ /// set or get if the function has the `@nogc` attribute
+ bool isnogc() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.isnogc) != 0;
+ }
+ /// ditto
+ void isnogc(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.isnogc;
+ else funcFlags &= ~FunctionFlag.isnogc;
+ }
+
+ /// set or get if the function has the `@property` attribute
+ bool isproperty() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.isproperty) != 0;
+ }
+ /// ditto
+ void isproperty(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.isproperty;
+ else funcFlags &= ~FunctionFlag.isproperty;
+ }
+
+ /// set or get if the function has the `ref` attribute
+ bool isref() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.isref) != 0;
+ }
+ /// ditto
+ void isref(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.isref;
+ else funcFlags &= ~FunctionFlag.isref;
+ }
+
+ /// set or get if the function has the `return` attribute
+ bool isreturn() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.isreturn) != 0;
+ }
+ /// ditto
+ void isreturn(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.isreturn;
+ else funcFlags &= ~FunctionFlag.isreturn;
+ }
+
+ /// set or get if the function has the `scope` attribute
+ bool isScopeQual() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.isscope) != 0;
+ }
+ /// ditto
+ void isScopeQual(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.isscope;
+ else funcFlags &= ~FunctionFlag.isscope;
+ }
+
+ /// set or get if the function has the `return` attribute inferred
+ bool isreturninferred() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.isreturninferred) != 0;
+ }
+ /// ditto
+ void isreturninferred(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.isreturninferred;
+ else funcFlags &= ~FunctionFlag.isreturninferred;
+ }
+
+ /// set or get if the function has the `scope` attribute inferred
+ bool isscopeinferred() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.isscopeinferred) != 0;
+ }
+ /// ditoo
+ void isscopeinferred(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.isscopeinferred;
+ else funcFlags &= ~FunctionFlag.isscopeinferred;
+ }
+
+ /// set or get if the function has the `@live` attribute
+ bool islive() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.islive) != 0;
+ }
+ /// ditto
+ void islive(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.islive;
+ else funcFlags &= ~FunctionFlag.islive;
+ }
+
+ /// set or get if the return type or the default arguments are removed
+ bool incomplete() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.incomplete) != 0;
+ }
+ /// ditto
+ void incomplete(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.incomplete;
+ else funcFlags &= ~FunctionFlag.incomplete;
+ }
+
+ /// set or get if the function has the `inout` on the parameters
+ bool isInOutParam() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.inoutParam) != 0;
+ }
+ /// ditto
+ void isInOutParam(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.inoutParam;
+ else funcFlags &= ~FunctionFlag.inoutParam;
+ }
+
+ /// set or get if the function has the `inout` on the parameters
+ bool isInOutQual() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.inoutQual) != 0;
+ }
+ /// ditto
+ void isInOutQual(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.inoutQual;
+ else funcFlags &= ~FunctionFlag.inoutQual;
+ }
+ /// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise.
+ bool iswild() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & (FunctionFlag.inoutParam | FunctionFlag.inoutQual)) != 0;
+ }
+
+ /// set or get if the function is a constructor
+ bool isctor() const pure nothrow @safe @nogc
+ {
+ return (funcFlags & FunctionFlag.isctor) != 0;
+ }
+ /// ditto
+ void isctor(bool v) pure nothrow @safe @nogc
+ {
+ if (v) funcFlags |= FunctionFlag.isctor;
+ else funcFlags &= ~FunctionFlag.isctor;
+ }
+
+ /// Returns: whether `this` function type has the same attributes (`@safe`,...) as `other`
+ bool attributesEqual(const scope TypeFunction other) const pure nothrow @safe @nogc
+ {
+ enum attributes = FunctionFlag.isnothrow
+ | FunctionFlag.isnogc
+ | FunctionFlag.islive;
+
+ return this.trust == other.trust &&
+ this.purity == other.purity &&
+ (this.funcFlags & attributes) == (other.funcFlags & attributes);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeDelegate : TypeNext
+{
+ // .next is a TypeFunction
+
+ extern (D) this(TypeFunction t)
+ {
+ super(Tfunction, t);
+ ty = Tdelegate;
+ }
+
+ static TypeDelegate create(TypeFunction t)
+ {
+ return new TypeDelegate(t);
+ }
+
+ override const(char)* kind() const
+ {
+ return "delegate";
+ }
+
+ override TypeDelegate syntaxCopy()
+ {
+ auto tf = next.syntaxCopy().isTypeFunction();
+ if (tf == next)
+ return this;
+
+ auto result = new TypeDelegate(tf);
+ result.mod = mod;
+ return result;
+ }
+
+ override Type addStorageClass(StorageClass stc)
+ {
+ TypeDelegate t = cast(TypeDelegate)Type.addStorageClass(stc);
+ if (global.params.useDIP1000 != FeatureState.enabled)
+ return t;
+
+ /* The rest is meant to add 'scope' to a delegate declaration if it is of the form:
+ * alias dg_t = void* delegate();
+ * scope dg_t dg = ...;
+ */
+ if(stc & STC.scope_)
+ {
+ auto n = t.next.addStorageClass(STC.scope_ | STC.scopeinferred);
+ if (n != t.next)
+ {
+ t.next = n;
+ t.deco = t.merge().deco; // mangling supposed to not be changed due to STC.scope_inferrred
+ }
+ }
+ return t;
+ }
+
+ override d_uns64 size(const ref Loc loc) const
+ {
+ return target.ptrsize * 2;
+ }
+
+ override uint alignsize() const
+ {
+ return target.ptrsize;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeDelegate.implicitConvTo(this=%p, to=%p)\n", this, to);
+ //printf("from: %s\n", toChars());
+ //printf("to : %s\n", to.toChars());
+ if (this == to)
+ return MATCH.exact;
+
+ version (all)
+ {
+ // not allowing covariant conversions because it interferes with overriding
+ if (to.ty == Tdelegate && this.nextOf().covariant(to.nextOf()) == Covariant.yes)
+ {
+ Type tret = this.next.nextOf();
+ Type toret = (cast(TypeDelegate)to).next.nextOf();
+ if (tret.ty == Tclass && toret.ty == Tclass)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=10219
+ * Check covariant interface return with offset tweaking.
+ * interface I {}
+ * class C : Object, I {}
+ * I delegate() dg = delegate C() {} // should be error
+ */
+ int offset = 0;
+ if (toret.isBaseOf(tret, &offset) && offset != 0)
+ return MATCH.nomatch;
+ }
+ return MATCH.convert;
+ }
+ }
+
+ return MATCH.nomatch;
+ }
+
+ override bool isZeroInit(const ref Loc loc) const
+ {
+ return true;
+ }
+
+ override bool isBoolean() const
+ {
+ return true;
+ }
+
+ override bool hasPointers() const
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/**
+ * This is a shell containing a TraitsExp that can be
+ * either resolved to a type or to a symbol.
+ *
+ * The point is to allow AliasDeclarationY to use `__traits()`, see issue 7804.
+ */
+extern (C++) final class TypeTraits : Type
+{
+ Loc loc;
+ /// The expression to resolve as type or symbol.
+ TraitsExp exp;
+ /// After `typeSemantic` the symbol when `exp` doesn't represent a type.
+ Dsymbol sym;
+
+ final extern (D) this(const ref Loc loc, TraitsExp exp)
+ {
+ super(Ttraits);
+ this.loc = loc;
+ this.exp = exp;
+ }
+
+ override const(char)* kind() const
+ {
+ return "traits";
+ }
+
+ override TypeTraits syntaxCopy()
+ {
+ TraitsExp te = exp.syntaxCopy();
+ TypeTraits tt = new TypeTraits(loc, te);
+ tt.mod = mod;
+ return tt;
+ }
+
+ override Dsymbol toDsymbol(Scope* sc)
+ {
+ Type t;
+ Expression e;
+ Dsymbol s;
+ resolve(this, loc, sc, e, t, s);
+ if (t && t.ty != Terror)
+ s = t.toDsymbol(sc);
+ else if (e)
+ s = getDsymbol(e);
+
+ return s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ override d_uns64 size(const ref Loc loc)
+ {
+ return SIZE_INVALID;
+ }
+}
+
+/******
+ * Implements mixin types.
+ *
+ * Semantic analysis will convert it to a real type.
+ */
+extern (C++) final class TypeMixin : Type
+{
+ Loc loc;
+ Expressions* exps;
+ RootObject obj; // cached result of semantic analysis.
+
+ extern (D) this(const ref Loc loc, Expressions* exps)
+ {
+ super(Tmixin);
+ this.loc = loc;
+ this.exps = exps;
+ }
+
+ override const(char)* kind() const
+ {
+ return "mixin";
+ }
+
+ override TypeMixin syntaxCopy()
+ {
+ return new TypeMixin(loc, Expression.arraySyntaxCopy(exps));
+ }
+
+ override Dsymbol toDsymbol(Scope* sc)
+ {
+ Type t;
+ Expression e;
+ Dsymbol s;
+ resolve(this, loc, sc, e, t, s);
+ if (t)
+ s = t.toDsymbol(sc);
+ else if (e)
+ s = getDsymbol(e);
+
+ return s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) abstract class TypeQualified : Type
+{
+ Loc loc;
+
+ // array of Identifier and TypeInstance,
+ // representing ident.ident!tiargs.ident. ... etc.
+ Objects idents;
+
+ final extern (D) this(TY ty, Loc loc)
+ {
+ super(ty);
+ this.loc = loc;
+ }
+
+ // abstract override so that using `TypeQualified.syntaxCopy` gets
+ // us a `TypeQualified`
+ abstract override TypeQualified syntaxCopy();
+
+ final void syntaxCopyHelper(TypeQualified t)
+ {
+ //printf("TypeQualified::syntaxCopyHelper(%s) %s\n", t.toChars(), toChars());
+ idents.setDim(t.idents.dim);
+ for (size_t i = 0; i < idents.dim; i++)
+ {
+ RootObject id = t.idents[i];
+ with (DYNCAST) final switch (id.dyncast())
+ {
+ case object:
+ break;
+ case expression:
+ Expression e = cast(Expression)id;
+ e = e.syntaxCopy();
+ id = e;
+ break;
+ case dsymbol:
+ TemplateInstance ti = cast(TemplateInstance)id;
+ ti = ti.syntaxCopy(null);
+ id = ti;
+ break;
+ case type:
+ Type tx = cast(Type)id;
+ tx = tx.syntaxCopy();
+ id = tx;
+ break;
+ case identifier:
+ case tuple:
+ case parameter:
+ case statement:
+ case condition:
+ case templateparameter:
+ case initializer:
+ }
+ idents[i] = id;
+ }
+ }
+
+ final void addIdent(Identifier ident)
+ {
+ idents.push(ident);
+ }
+
+ final void addInst(TemplateInstance inst)
+ {
+ idents.push(inst);
+ }
+
+ final void addIndex(RootObject e)
+ {
+ idents.push(e);
+ }
+
+ override d_uns64 size(const ref Loc loc)
+ {
+ error(this.loc, "size of type `%s` is not known", toChars());
+ return SIZE_INVALID;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeIdentifier : TypeQualified
+{
+ Identifier ident;
+
+ // The symbol representing this identifier, before alias resolution
+ Dsymbol originalSymbol;
+
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(Tident, loc);
+ this.ident = ident;
+ }
+
+ override const(char)* kind() const
+ {
+ return "identifier";
+ }
+
+ override TypeIdentifier syntaxCopy()
+ {
+ auto t = new TypeIdentifier(loc, ident);
+ t.syntaxCopyHelper(this);
+ t.mod = mod;
+ return t;
+ }
+
+ /*****************************************
+ * See if type resolves to a symbol, if so,
+ * return that symbol.
+ */
+ override Dsymbol toDsymbol(Scope* sc)
+ {
+ //printf("TypeIdentifier::toDsymbol('%s')\n", toChars());
+ if (!sc)
+ return null;
+
+ Type t;
+ Expression e;
+ Dsymbol s;
+ resolve(this, loc, sc, e, t, s);
+ if (t && t.ty != Tident)
+ s = t.toDsymbol(sc);
+ if (e)
+ s = getDsymbol(e);
+
+ return s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Similar to TypeIdentifier, but with a TemplateInstance as the root
+ */
+extern (C++) final class TypeInstance : TypeQualified
+{
+ TemplateInstance tempinst;
+
+ extern (D) this(const ref Loc loc, TemplateInstance tempinst)
+ {
+ super(Tinstance, loc);
+ this.tempinst = tempinst;
+ }
+
+ override const(char)* kind() const
+ {
+ return "instance";
+ }
+
+ override TypeInstance syntaxCopy()
+ {
+ //printf("TypeInstance::syntaxCopy() %s, %d\n", toChars(), idents.dim);
+ auto t = new TypeInstance(loc, tempinst.syntaxCopy(null));
+ t.syntaxCopyHelper(this);
+ t.mod = mod;
+ return t;
+ }
+
+ override Dsymbol toDsymbol(Scope* sc)
+ {
+ Type t;
+ Expression e;
+ Dsymbol s;
+ //printf("TypeInstance::semantic(%s)\n", toChars());
+ resolve(this, loc, sc, e, t, s);
+ if (t && t.ty != Tinstance)
+ s = t.toDsymbol(sc);
+ return s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeTypeof : TypeQualified
+{
+ Expression exp;
+ int inuse;
+
+ extern (D) this(const ref Loc loc, Expression exp)
+ {
+ super(Ttypeof, loc);
+ this.exp = exp;
+ }
+
+ override const(char)* kind() const
+ {
+ return "typeof";
+ }
+
+ override TypeTypeof syntaxCopy()
+ {
+ //printf("TypeTypeof::syntaxCopy() %s\n", toChars());
+ auto t = new TypeTypeof(loc, exp.syntaxCopy());
+ t.syntaxCopyHelper(this);
+ t.mod = mod;
+ return t;
+ }
+
+ override Dsymbol toDsymbol(Scope* sc)
+ {
+ //printf("TypeTypeof::toDsymbol('%s')\n", toChars());
+ Expression e;
+ Type t;
+ Dsymbol s;
+ resolve(this, loc, sc, e, t, s);
+ return s;
+ }
+
+ override d_uns64 size(const ref Loc loc)
+ {
+ if (exp.type)
+ return exp.type.size(loc);
+ else
+ return TypeQualified.size(loc);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeReturn : TypeQualified
+{
+ extern (D) this(const ref Loc loc)
+ {
+ super(Treturn, loc);
+ }
+
+ override const(char)* kind() const
+ {
+ return "return";
+ }
+
+ override TypeReturn syntaxCopy()
+ {
+ auto t = new TypeReturn(loc);
+ t.syntaxCopyHelper(this);
+ t.mod = mod;
+ return t;
+ }
+
+ override Dsymbol toDsymbol(Scope* sc)
+ {
+ Expression e;
+ Type t;
+ Dsymbol s;
+ resolve(this, loc, sc, e, t, s);
+ return s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeStruct : Type
+{
+ StructDeclaration sym;
+ AliasThisRec att = AliasThisRec.fwdref;
+ bool inuse = false; // struct currently subject of recursive method call
+
+ extern (D) this(StructDeclaration sym)
+ {
+ super(Tstruct);
+ this.sym = sym;
+ }
+
+ static TypeStruct create(StructDeclaration sym)
+ {
+ return new TypeStruct(sym);
+ }
+
+ override const(char)* kind() const
+ {
+ return "struct";
+ }
+
+ override d_uns64 size(const ref Loc loc)
+ {
+ return sym.size(loc);
+ }
+
+ override uint alignsize()
+ {
+ sym.size(Loc.initial); // give error for forward references
+ return sym.alignsize;
+ }
+
+ override TypeStruct syntaxCopy()
+ {
+ return this;
+ }
+
+ override Dsymbol toDsymbol(Scope* sc)
+ {
+ return sym;
+ }
+
+ override structalign_t alignment()
+ {
+ if (sym.alignment == 0)
+ sym.size(sym.loc);
+ return sym.alignment;
+ }
+
+ /***************************************
+ * Use when we prefer the default initializer to be a literal,
+ * rather than a global immutable variable.
+ */
+ override Expression defaultInitLiteral(const ref Loc loc)
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars());
+ }
+ sym.size(loc);
+ if (sym.sizeok != Sizeok.done)
+ return ErrorExp.get();
+
+ auto structelems = new Expressions(sym.nonHiddenFields());
+ uint offset = 0;
+ foreach (j; 0 .. structelems.dim)
+ {
+ VarDeclaration vd = sym.fields[j];
+ Expression e;
+ if (vd.inuse)
+ {
+ error(loc, "circular reference to `%s`", vd.toPrettyChars());
+ return ErrorExp.get();
+ }
+ if (vd.offset < offset || vd.type.size() == 0)
+ e = null;
+ else if (vd._init)
+ {
+ if (vd._init.isVoidInitializer())
+ e = null;
+ else
+ e = vd.getConstInitializer(false);
+ }
+ else
+ e = vd.type.defaultInitLiteral(loc);
+ if (e && e.op == TOK.error)
+ return e;
+ if (e)
+ offset = vd.offset + cast(uint)vd.type.size();
+ (*structelems)[j] = e;
+ }
+ auto structinit = new StructLiteralExp(loc, sym, structelems);
+
+ /* Copy from the initializer symbol for larger symbols,
+ * otherwise the literals expressed as code get excessively large.
+ */
+ if (size(loc) > target.ptrsize * 4 && !needsNested())
+ structinit.useStaticInit = true;
+
+ structinit.type = this;
+ return structinit;
+ }
+
+ override bool isZeroInit(const ref Loc loc)
+ {
+ // Determine zeroInit here, as this can be called before semantic2
+ sym.determineSize(sym.loc);
+ return sym.zeroInit;
+ }
+
+ override bool isAssignable()
+ {
+ bool assignable = true;
+ uint offset = ~0; // dead-store initialize to prevent spurious warning
+
+ sym.determineSize(sym.loc);
+
+ /* If any of the fields are const or immutable,
+ * then one cannot assign this struct.
+ */
+ for (size_t i = 0; i < sym.fields.dim; i++)
+ {
+ VarDeclaration v = sym.fields[i];
+ //printf("%s [%d] v = (%s) %s, v.offset = %d, v.parent = %s\n", sym.toChars(), i, v.kind(), v.toChars(), v.offset, v.parent.kind());
+ if (i == 0)
+ {
+ }
+ else if (v.offset == offset)
+ {
+ /* If any fields of anonymous union are assignable,
+ * then regard union as assignable.
+ * This is to support unsafe things like Rebindable templates.
+ */
+ if (assignable)
+ continue;
+ }
+ else
+ {
+ if (!assignable)
+ return false;
+ }
+ assignable = v.type.isMutable() && v.type.isAssignable();
+ offset = v.offset;
+ //printf(" -> assignable = %d\n", assignable);
+ }
+
+ return assignable;
+ }
+
+ override bool isBoolean() const
+ {
+ return false;
+ }
+
+ override bool needsDestruction() const
+ {
+ return sym.dtor !is null;
+ }
+
+ override bool needsCopyOrPostblit()
+ {
+ return sym.hasCopyCtor || sym.postblit;
+ }
+
+ override bool needsNested()
+ {
+ if (inuse) return false; // circular type, error instead of crashing
+
+ inuse = true;
+ scope(exit) inuse = false;
+
+ if (sym.isNested())
+ return true;
+
+ for (size_t i = 0; i < sym.fields.dim; i++)
+ {
+ VarDeclaration v = sym.fields[i];
+ if (!v.isDataseg() && v.type.needsNested())
+ return true;
+ }
+ return false;
+ }
+
+ override bool hasPointers()
+ {
+ // Probably should cache this information in sym rather than recompute
+ StructDeclaration s = sym;
+
+ if (sym.members && !sym.determineFields() && sym.type != Type.terror)
+ error(sym.loc, "no size because of forward references");
+
+ foreach (VarDeclaration v; s.fields)
+ {
+ if (v.storage_class & STC.ref_ || v.hasPointers())
+ return true;
+ }
+ return false;
+ }
+
+ override bool hasVoidInitPointers()
+ {
+ // Probably should cache this information in sym rather than recompute
+ StructDeclaration s = sym;
+
+ sym.size(Loc.initial); // give error for forward references
+ foreach (VarDeclaration v; s.fields)
+ {
+ if (v._init && v._init.isVoidInitializer() && v.type.hasPointers())
+ return true;
+ if (!v._init && v.type.hasVoidInitPointers())
+ return true;
+ }
+ return false;
+ }
+
+ override bool hasInvariant()
+ {
+ // Probably should cache this information in sym rather than recompute
+ StructDeclaration s = sym;
+
+ sym.size(Loc.initial); // give error for forward references
+
+ if (s.hasInvariant())
+ return true;
+
+ foreach (VarDeclaration v; s.fields)
+ {
+ if (v.type.hasInvariant())
+ return true;
+ }
+ return false;
+ }
+
+ extern (D) MATCH implicitConvToWithoutAliasThis(Type to)
+ {
+ MATCH m;
+
+ if (ty == to.ty && sym == (cast(TypeStruct)to).sym)
+ {
+ m = MATCH.exact; // exact match
+ if (mod != to.mod)
+ {
+ m = MATCH.constant;
+ if (MODimplicitConv(mod, to.mod))
+ {
+ }
+ else
+ {
+ /* Check all the fields. If they can all be converted,
+ * allow the conversion.
+ */
+ uint offset = ~0; // dead-store to prevent spurious warning
+ for (size_t i = 0; i < sym.fields.dim; i++)
+ {
+ VarDeclaration v = sym.fields[i];
+ if (i == 0)
+ {
+ }
+ else if (v.offset == offset)
+ {
+ if (m > MATCH.nomatch)
+ continue;
+ }
+ else
+ {
+ if (m == MATCH.nomatch)
+ return m;
+ }
+
+ // 'from' type
+ Type tvf = v.type.addMod(mod);
+
+ // 'to' type
+ Type tv = v.type.addMod(to.mod);
+
+ // field match
+ MATCH mf = tvf.implicitConvTo(tv);
+ //printf("\t%s => %s, match = %d\n", v.type.toChars(), tv.toChars(), mf);
+
+ if (mf == MATCH.nomatch)
+ return mf;
+ if (mf < m) // if field match is worse
+ m = mf;
+ offset = v.offset;
+ }
+ }
+ }
+ }
+ return m;
+ }
+
+ extern (D) MATCH implicitConvToThroughAliasThis(Type to)
+ {
+ MATCH m;
+ if (!(ty == to.ty && sym == (cast(TypeStruct)to).sym) && sym.aliasthis && !(att & AliasThisRec.tracing))
+ {
+ if (auto ato = aliasthisOf())
+ {
+ att = cast(AliasThisRec)(att | AliasThisRec.tracing);
+ m = ato.implicitConvTo(to);
+ att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
+ }
+ else
+ m = MATCH.nomatch; // no match
+ }
+ return m;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to.toChars());
+ MATCH m = implicitConvToWithoutAliasThis(to);
+ return m ? m : implicitConvToThroughAliasThis(to);
+ }
+
+ override MATCH constConv(Type to)
+ {
+ if (equals(to))
+ return MATCH.exact;
+ if (ty == to.ty && sym == (cast(TypeStruct)to).sym && MODimplicitConv(mod, to.mod))
+ return MATCH.constant;
+ return MATCH.nomatch;
+ }
+
+ override MOD deduceWild(Type t, bool isRef)
+ {
+ if (ty == t.ty && sym == (cast(TypeStruct)t).sym)
+ return Type.deduceWild(t, isRef);
+
+ ubyte wm = 0;
+
+ if (t.hasWild() && sym.aliasthis && !(att & AliasThisRec.tracing))
+ {
+ if (auto ato = aliasthisOf())
+ {
+ att = cast(AliasThisRec)(att | AliasThisRec.tracing);
+ wm = ato.deduceWild(t, isRef);
+ att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
+ }
+ }
+
+ return wm;
+ }
+
+ override inout(Type) toHeadMutable() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeEnum : Type
+{
+ EnumDeclaration sym;
+
+ extern (D) this(EnumDeclaration sym)
+ {
+ super(Tenum);
+ this.sym = sym;
+ }
+
+ override const(char)* kind() const
+ {
+ return "enum";
+ }
+
+ override TypeEnum syntaxCopy()
+ {
+ return this;
+ }
+
+ override d_uns64 size(const ref Loc loc)
+ {
+ return sym.getMemtype(loc).size(loc);
+ }
+
+ Type memType(const ref Loc loc = Loc.initial)
+ {
+ return sym.getMemtype(loc);
+ }
+ override uint alignsize()
+ {
+ Type t = memType();
+ if (t.ty == Terror)
+ return 4;
+ return t.alignsize();
+ }
+
+ override Dsymbol toDsymbol(Scope* sc)
+ {
+ return sym;
+ }
+
+ override bool isintegral()
+ {
+ return memType().isintegral();
+ }
+
+ override bool isfloating()
+ {
+ return memType().isfloating();
+ }
+
+ override bool isreal()
+ {
+ return memType().isreal();
+ }
+
+ override bool isimaginary()
+ {
+ return memType().isimaginary();
+ }
+
+ override bool iscomplex()
+ {
+ return memType().iscomplex();
+ }
+
+ override bool isscalar()
+ {
+ return memType().isscalar();
+ }
+
+ override bool isunsigned()
+ {
+ return memType().isunsigned();
+ }
+
+ override bool isBoolean()
+ {
+ return memType().isBoolean();
+ }
+
+ override bool isString()
+ {
+ return memType().isString();
+ }
+
+ override bool isAssignable()
+ {
+ return memType().isAssignable();
+ }
+
+ override bool needsDestruction()
+ {
+ return memType().needsDestruction();
+ }
+
+ override bool needsCopyOrPostblit()
+ {
+ return memType().needsCopyOrPostblit();
+ }
+
+ override bool needsNested()
+ {
+ return memType().needsNested();
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ MATCH m;
+ //printf("TypeEnum::implicitConvTo() %s to %s\n", toChars(), to.toChars());
+ if (ty == to.ty && sym == (cast(TypeEnum)to).sym)
+ m = (mod == to.mod) ? MATCH.exact : MATCH.constant;
+ else if (sym.getMemtype(Loc.initial).implicitConvTo(to))
+ m = MATCH.convert; // match with conversions
+ else
+ m = MATCH.nomatch; // no match
+ return m;
+ }
+
+ override MATCH constConv(Type to)
+ {
+ if (equals(to))
+ return MATCH.exact;
+ if (ty == to.ty && sym == (cast(TypeEnum)to).sym && MODimplicitConv(mod, to.mod))
+ return MATCH.constant;
+ return MATCH.nomatch;
+ }
+
+ extern (D) Type toBasetype2()
+ {
+ if (!sym.members && !sym.memtype)
+ return this;
+ auto tb = sym.getMemtype(Loc.initial).toBasetype();
+ return tb.castMod(mod); // retain modifier bits from 'this'
+ }
+
+ override bool isZeroInit(const ref Loc loc)
+ {
+ return sym.getDefaultValue(loc).isBool(false);
+ }
+
+ override bool hasPointers()
+ {
+ return memType().hasPointers();
+ }
+
+ override bool hasVoidInitPointers()
+ {
+ return memType().hasVoidInitPointers();
+ }
+
+ override bool hasInvariant()
+ {
+ return memType().hasInvariant();
+ }
+
+ override Type nextOf()
+ {
+ return memType().nextOf();
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeClass : Type
+{
+ ClassDeclaration sym;
+ AliasThisRec att = AliasThisRec.fwdref;
+ CPPMANGLE cppmangle = CPPMANGLE.def;
+
+ extern (D) this(ClassDeclaration sym)
+ {
+ super(Tclass);
+ this.sym = sym;
+ }
+
+ override const(char)* kind() const
+ {
+ return "class";
+ }
+
+ override d_uns64 size(const ref Loc loc) const
+ {
+ return target.ptrsize;
+ }
+
+ override TypeClass syntaxCopy()
+ {
+ return this;
+ }
+
+ override Dsymbol toDsymbol(Scope* sc)
+ {
+ return sym;
+ }
+
+ override inout(ClassDeclaration) isClassHandle() inout
+ {
+ return sym;
+ }
+
+ override bool isBaseOf(Type t, int* poffset)
+ {
+ if (t && t.ty == Tclass)
+ {
+ ClassDeclaration cd = (cast(TypeClass)t).sym;
+ if (sym.isBaseOf(cd, poffset))
+ return true;
+ }
+ return false;
+ }
+
+ extern (D) MATCH implicitConvToWithoutAliasThis(Type to)
+ {
+ MATCH m = constConv(to);
+ if (m > MATCH.nomatch)
+ return m;
+
+ ClassDeclaration cdto = to.isClassHandle();
+ if (cdto)
+ {
+ //printf("TypeClass::implicitConvTo(to = '%s') %s, isbase = %d %d\n", to.toChars(), toChars(), cdto.isBaseInfoComplete(), sym.isBaseInfoComplete());
+ if (cdto.semanticRun < PASS.semanticdone && !cdto.isBaseInfoComplete())
+ cdto.dsymbolSemantic(null);
+ if (sym.semanticRun < PASS.semanticdone && !sym.isBaseInfoComplete())
+ sym.dsymbolSemantic(null);
+ if (cdto.isBaseOf(sym, null) && MODimplicitConv(mod, to.mod))
+ {
+ //printf("'to' is base\n");
+ return MATCH.convert;
+ }
+ }
+ return MATCH.nomatch;
+ }
+
+ extern (D) MATCH implicitConvToThroughAliasThis(Type to)
+ {
+ MATCH m;
+ if (sym.aliasthis && !(att & AliasThisRec.tracing))
+ {
+ if (auto ato = aliasthisOf())
+ {
+ att = cast(AliasThisRec)(att | AliasThisRec.tracing);
+ m = ato.implicitConvTo(to);
+ att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
+ }
+ }
+ return m;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeClass::implicitConvTo(to = '%s') %s\n", to.toChars(), toChars());
+ MATCH m = implicitConvToWithoutAliasThis(to);
+ return m ? m : implicitConvToThroughAliasThis(to);
+ }
+
+ override MATCH constConv(Type to)
+ {
+ if (equals(to))
+ return MATCH.exact;
+ if (ty == to.ty && sym == (cast(TypeClass)to).sym && MODimplicitConv(mod, to.mod))
+ return MATCH.constant;
+
+ /* Conversion derived to const(base)
+ */
+ int offset = 0;
+ if (to.isBaseOf(this, &offset) && offset == 0 && MODimplicitConv(mod, to.mod))
+ {
+ // Disallow:
+ // derived to base
+ // inout(derived) to inout(base)
+ if (!to.isMutable() && !to.isWild())
+ return MATCH.convert;
+ }
+
+ return MATCH.nomatch;
+ }
+
+ override MOD deduceWild(Type t, bool isRef)
+ {
+ ClassDeclaration cd = t.isClassHandle();
+ if (cd && (sym == cd || cd.isBaseOf(sym, null)))
+ return Type.deduceWild(t, isRef);
+
+ ubyte wm = 0;
+
+ if (t.hasWild() && sym.aliasthis && !(att & AliasThisRec.tracing))
+ {
+ if (auto ato = aliasthisOf())
+ {
+ att = cast(AliasThisRec)(att | AliasThisRec.tracing);
+ wm = ato.deduceWild(t, isRef);
+ att = cast(AliasThisRec)(att & ~AliasThisRec.tracing);
+ }
+ }
+
+ return wm;
+ }
+
+ override inout(Type) toHeadMutable() inout
+ {
+ return this;
+ }
+
+ override bool isZeroInit(const ref Loc loc) const
+ {
+ return true;
+ }
+
+ override bool isscope() const
+ {
+ return sym.stack;
+ }
+
+ override bool isBoolean() const
+ {
+ return true;
+ }
+
+ override bool hasPointers() const
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeTuple : Type
+{
+ // 'logically immutable' cached global - don't modify!
+ __gshared TypeTuple empty = new TypeTuple();
+
+ Parameters* arguments; // types making up the tuple
+
+ extern (D) this(Parameters* arguments)
+ {
+ super(Ttuple);
+ //printf("TypeTuple(this = %p)\n", this);
+ this.arguments = arguments;
+ //printf("TypeTuple() %p, %s\n", this, toChars());
+ debug
+ {
+ if (arguments)
+ {
+ for (size_t i = 0; i < arguments.dim; i++)
+ {
+ Parameter arg = (*arguments)[i];
+ assert(arg && arg.type);
+ }
+ }
+ }
+ }
+
+ /****************
+ * Form TypeTuple from the types of the expressions.
+ * Assume exps[] is already tuple expanded.
+ */
+ extern (D) this(Expressions* exps)
+ {
+ super(Ttuple);
+ auto arguments = new Parameters();
+ if (exps)
+ {
+ arguments.setDim(exps.dim);
+ for (size_t i = 0; i < exps.dim; i++)
+ {
+ Expression e = (*exps)[i];
+ if (e.type.ty == Ttuple)
+ e.error("cannot form tuple of tuples");
+ auto arg = new Parameter(STC.undefined_, e.type, null, null, null);
+ (*arguments)[i] = arg;
+ }
+ }
+ this.arguments = arguments;
+ //printf("TypeTuple() %p, %s\n", this, toChars());
+ }
+
+ static TypeTuple create(Parameters* arguments)
+ {
+ return new TypeTuple(arguments);
+ }
+
+ /*******************************************
+ * Type tuple with 0, 1 or 2 types in it.
+ */
+ extern (D) this()
+ {
+ super(Ttuple);
+ arguments = new Parameters();
+ }
+
+ extern (D) this(Type t1)
+ {
+ super(Ttuple);
+ arguments = new Parameters();
+ arguments.push(new Parameter(0, t1, null, null, null));
+ }
+
+ extern (D) this(Type t1, Type t2)
+ {
+ super(Ttuple);
+ arguments = new Parameters();
+ arguments.push(new Parameter(0, t1, null, null, null));
+ arguments.push(new Parameter(0, t2, null, null, null));
+ }
+
+ static TypeTuple create()
+ {
+ return new TypeTuple();
+ }
+
+ static TypeTuple create(Type t1)
+ {
+ return new TypeTuple(t1);
+ }
+
+ static TypeTuple create(Type t1, Type t2)
+ {
+ return new TypeTuple(t1, t2);
+ }
+
+ override const(char)* kind() const
+ {
+ return "tuple";
+ }
+
+ override TypeTuple syntaxCopy()
+ {
+ Parameters* args = Parameter.arraySyntaxCopy(arguments);
+ auto t = new TypeTuple(args);
+ t.mod = mod;
+ return t;
+ }
+
+ override bool equals(const RootObject o) const
+ {
+ Type t = cast(Type)o;
+ //printf("TypeTuple::equals(%s, %s)\n", toChars(), t.toChars());
+ if (this == t)
+ return true;
+ if (auto tt = t.isTypeTuple())
+ {
+ if (arguments.dim == tt.arguments.dim)
+ {
+ for (size_t i = 0; i < tt.arguments.dim; i++)
+ {
+ const Parameter arg1 = (*arguments)[i];
+ Parameter arg2 = (*tt.arguments)[i];
+ if (!arg1.type.equals(arg2.type))
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * This is so we can slice a TypeTuple
+ */
+extern (C++) final class TypeSlice : TypeNext
+{
+ Expression lwr;
+ Expression upr;
+
+ extern (D) this(Type next, Expression lwr, Expression upr)
+ {
+ super(Tslice, next);
+ //printf("TypeSlice[%s .. %s]\n", lwr.toChars(), upr.toChars());
+ this.lwr = lwr;
+ this.upr = upr;
+ }
+
+ override const(char)* kind() const
+ {
+ return "slice";
+ }
+
+ override TypeSlice syntaxCopy()
+ {
+ auto t = new TypeSlice(next.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy());
+ t.mod = mod;
+ return t;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeNull : Type
+{
+ extern (D) this()
+ {
+ //printf("TypeNull %p\n", this);
+ super(Tnull);
+ }
+
+ override const(char)* kind() const
+ {
+ return "null";
+ }
+
+ override TypeNull syntaxCopy()
+ {
+ // No semantic analysis done, no need to copy
+ return this;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeNull::implicitConvTo(this=%p, to=%p)\n", this, to);
+ //printf("from: %s\n", toChars());
+ //printf("to : %s\n", to.toChars());
+ MATCH m = Type.implicitConvTo(to);
+ if (m != MATCH.nomatch)
+ return m;
+
+ // NULL implicitly converts to any pointer type or dynamic array
+ //if (type.ty == Tpointer && type.nextOf().ty == Tvoid)
+ {
+ Type tb = to.toBasetype();
+ if (tb.ty == Tnull || tb.ty == Tpointer || tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tclass || tb.ty == Tdelegate)
+ return MATCH.constant;
+ }
+
+ return MATCH.nomatch;
+ }
+
+ override bool hasPointers()
+ {
+ /* Although null isn't dereferencable, treat it as a pointer type for
+ * attribute inference, generic code, etc.
+ */
+ return true;
+ }
+
+ override bool isBoolean() const
+ {
+ return true;
+ }
+
+ override d_uns64 size(const ref Loc loc) const
+ {
+ return tvoidptr.size(loc);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class TypeNoreturn : Type
+{
+ extern (D) this()
+ {
+ //printf("TypeNoreturn %p\n", this);
+ super(Tnoreturn);
+ }
+
+ override const(char)* kind() const
+ {
+ return "noreturn";
+ }
+
+ override TypeNoreturn syntaxCopy()
+ {
+ // No semantic analysis done, no need to copy
+ return this;
+ }
+
+ override MATCH implicitConvTo(Type to)
+ {
+ //printf("TypeNoreturn::implicitConvTo(this=%p, to=%p)\n", this, to);
+ //printf("from: %s\n", toChars());
+ //printf("to : %s\n", to.toChars());
+ if (this.equals(to))
+ return MATCH.exact;
+
+ // Different qualifiers?
+ if (to.ty == Tnoreturn)
+ return MATCH.constant;
+
+ // Implicitly convertible to any type
+ return MATCH.convert;
+ }
+
+ override MATCH constConv(Type to)
+ {
+ // Either another noreturn or conversion to any type
+ return this.implicitConvTo(to);
+ }
+
+ override bool isBoolean() const
+ {
+ return true; // bottom type can be implicitly converted to any other type
+ }
+
+ override d_uns64 size(const ref Loc loc) const
+ {
+ return 0;
+ }
+
+ override uint alignsize()
+ {
+ return 0;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Unlike D, C can declare/define struct/union/enum tag names
+ * inside Declarators, instead of separately as in D.
+ * The order these appear in the symbol table must be in lexical
+ * order. There isn't enough info at the parsing stage to determine if
+ * it's a declaration or a reference to an existing name, so this Type
+ * collects the necessary info and defers it to semantic().
+ */
+extern (C++) final class TypeTag : Type
+{
+ Loc loc; /// location of declaration
+ TOK tok; /// TOK.struct_, TOK.union_, TOK.enum_
+ Identifier id; /// tag name identifier
+ Dsymbols* members; /// members of struct, null if none
+
+ Type resolved; /// type after semantic() in case there are more others
+ /// pointing to this instance, which can happen with
+ /// struct S { int a; } s1, *s2;
+
+ extern (D) this(const ref Loc loc, TOK tok, Identifier id, Dsymbols* members)
+ {
+ //printf("TypeTag %p\n", this);
+ super(Ttag);
+ this.loc = loc;
+ this.tok = tok;
+ this.id = id;
+ this.members = members;
+ }
+
+ override const(char)* kind() const
+ {
+ return "tag";
+ }
+
+ override TypeTag syntaxCopy()
+ {
+ // No semantic analysis done, no need to copy
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Represents a function's formal parameters + variadics info.
+ * Length, indexing and iteration are based on a depth-first tuple expansion.
+ * https://dlang.org/spec/function.html#ParameterList
+ */
+extern (C++) struct ParameterList
+{
+ /// The raw (unexpanded) formal parameters, possibly containing tuples.
+ Parameters* parameters;
+ StorageClass stc; // storage class of ...
+ VarArg varargs = VarArg.none;
+ bool hasIdentifierList; // true if C identifier-list style
+
+ this(Parameters* parameters, VarArg varargs = VarArg.none, StorageClass stc = 0)
+ {
+ this.parameters = parameters;
+ this.varargs = varargs;
+ this.stc = stc;
+ }
+
+ /// Returns the number of expanded parameters. Complexity: O(N).
+ size_t length()
+ {
+ return Parameter.dim(parameters);
+ }
+
+ /// Returns the expanded parameter at the given index, or null if out of
+ /// bounds. Complexity: O(i).
+ Parameter opIndex(size_t i)
+ {
+ return Parameter.getNth(parameters, i);
+ }
+
+ /// Iterates over the expanded parameters. Complexity: O(N).
+ /// Prefer this to avoid the O(N + N^2/2) complexity of calculating length
+ /// and calling N times opIndex.
+ extern (D) int opApply(scope Parameter.ForeachDg dg)
+ {
+ return Parameter._foreach(parameters, dg);
+ }
+
+ /// Iterates over the expanded parameters, matching them with the unexpanded
+ /// ones, for semantic processing
+ extern (D) int opApply(scope Parameter.SemanticForeachDg dg)
+ {
+ return Parameter._foreach(this.parameters, dg);
+ }
+
+ extern (D) ParameterList syntaxCopy()
+ {
+ return ParameterList(Parameter.arraySyntaxCopy(parameters), varargs);
+ }
+
+ /// Compares this to another ParameterList (and expands tuples if necessary)
+ extern (D) bool opEquals(scope ref ParameterList other) const
+ {
+ if (stc != other.stc || varargs != other.varargs || (!parameters != !other.parameters))
+ return false;
+
+ if (this.parameters is other.parameters)
+ return true;
+
+ size_t idx;
+ bool diff;
+
+ // Pairwise compare each parameter
+ // Can this avoid the O(n) indexing for the second list?
+ foreach (_, p1; cast() this)
+ {
+ auto p2 = other[idx++];
+ if (!p2 || p1 != p2) {
+ diff = true;
+ break;
+ }
+ }
+
+ // Ensure no remaining parameters in `other`
+ return !diff && other[idx] is null;
+ }
+}
+
+
+/***********************************************************
+ */
+extern (C++) final class Parameter : ASTNode
+{
+ import dmd.attrib : UserAttributeDeclaration;
+
+ StorageClass storageClass;
+ Type type;
+ Identifier ident;
+ Expression defaultArg;
+ UserAttributeDeclaration userAttribDecl; // user defined attributes
+
+ extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl)
+ {
+ this.type = type;
+ this.ident = ident;
+ this.storageClass = storageClass;
+ this.defaultArg = defaultArg;
+ this.userAttribDecl = userAttribDecl;
+ }
+
+ static Parameter create(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl)
+ {
+ return new Parameter(storageClass, type, ident, defaultArg, userAttribDecl);
+ }
+
+ Parameter syntaxCopy()
+ {
+ return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null, userAttribDecl ? userAttribDecl.syntaxCopy(null) : null);
+ }
+
+ /****************************************************
+ * Determine if parameter is a lazy array of delegates.
+ * If so, return the return type of those delegates.
+ * If not, return NULL.
+ *
+ * Returns T if the type is one of the following forms:
+ * T delegate()[]
+ * T delegate()[dim]
+ */
+ Type isLazyArray()
+ {
+ Type tb = type.toBasetype();
+ if (tb.ty == Tsarray || tb.ty == Tarray)
+ {
+ Type tel = (cast(TypeArray)tb).next.toBasetype();
+ if (auto td = tel.isTypeDelegate())
+ {
+ TypeFunction tf = td.next.toTypeFunction();
+ if (tf.parameterList.varargs == VarArg.none && tf.parameterList.length == 0)
+ {
+ return tf.next; // return type of delegate
+ }
+ }
+ }
+ return null;
+ }
+
+ /// Returns: Whether the function parameter is a reference (out / ref)
+ bool isReference() const @safe pure nothrow @nogc
+ {
+ return (this.storageClass & (STC.ref_ | STC.out_)) != 0;
+ }
+
+ // kludge for template.isType()
+ override DYNCAST dyncast() const
+ {
+ return DYNCAST.parameter;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ extern (D) static Parameters* arraySyntaxCopy(Parameters* parameters)
+ {
+ Parameters* params = null;
+ if (parameters)
+ {
+ params = new Parameters(parameters.dim);
+ for (size_t i = 0; i < params.dim; i++)
+ (*params)[i] = (*parameters)[i].syntaxCopy();
+ }
+ return params;
+ }
+
+ /***************************************
+ * Determine number of arguments, folding in tuples.
+ */
+ static size_t dim(Parameters* parameters)
+ {
+ size_t nargs = 0;
+
+ int dimDg(size_t n, Parameter p)
+ {
+ ++nargs;
+ return 0;
+ }
+
+ _foreach(parameters, &dimDg);
+ return nargs;
+ }
+
+ /**
+ * Get nth `Parameter`, folding in tuples.
+ *
+ * Since `parameters` can include tuples, which would increase its
+ * length, this function allows to get the `nth` parameter as if
+ * all tuples transitively contained in `parameters` were flattened.
+ *
+ * Params:
+ * parameters = Array of `Parameter` to iterate over
+ * nth = Index of the desired parameter.
+ *
+ * Returns:
+ * The parameter at index `nth` (taking tuples into account),
+ * or `null` if out of bound.
+ */
+ static Parameter getNth(Parameters* parameters, size_t nth)
+ {
+ Parameter param;
+
+ int getNthParamDg(size_t n, Parameter p)
+ {
+ if (n == nth)
+ {
+ param = p;
+ return 1;
+ }
+ return 0;
+ }
+
+ int res = _foreach(parameters, &getNthParamDg);
+ return res ? param : null;
+ }
+
+ /// Type of delegate when iterating solely on the parameters
+ alias ForeachDg = extern (D) int delegate(size_t paramidx, Parameter param);
+ /// Type of delegate when iterating on both the original set of parameters,
+ /// and the type tuple. Useful for semantic analysis.
+ /// 'o' stands for 'original' and 'e' stands for 'expanded'.
+ alias SemanticForeachDg = extern (D) int delegate(
+ size_t oidx, Parameter oparam, size_t eidx, Parameter eparam);
+
+ /***************************************
+ * Expands tuples in args in depth first order. Calls
+ * dg(void *ctx, size_t argidx, Parameter *arg) for each Parameter.
+ * If dg returns !=0, stops and returns that value else returns 0.
+ * Use this function to avoid the O(N + N^2/2) complexity of
+ * calculating dim and calling N times getNth.
+ */
+ extern (D) static int _foreach(Parameters* parameters, scope ForeachDg dg)
+ {
+ assert(dg !is null);
+ return _foreach(parameters, (_oidx, _oparam, idx, param) => dg(idx, param));
+ }
+
+ /// Ditto
+ extern (D) static int _foreach(
+ Parameters* parameters, scope SemanticForeachDg dg)
+ {
+ assert(dg !is null);
+ if (parameters is null)
+ return 0;
+
+ size_t eidx;
+ foreach (oidx; 0 .. parameters.length)
+ {
+ Parameter oparam = (*parameters)[oidx];
+ if (auto r = _foreachImpl(dg, oidx, oparam, eidx, /* eparam */ oparam))
+ return r;
+ }
+ return 0;
+ }
+
+ /// Implementation of the iteration process, which recurses in itself
+ /// and just forwards `oidx` and `oparam`.
+ extern (D) private static int _foreachImpl(scope SemanticForeachDg dg,
+ size_t oidx, Parameter oparam, ref size_t eidx, Parameter eparam)
+ {
+ if (eparam is null)
+ return 0;
+
+ Type t = eparam.type.toBasetype();
+ if (auto tu = t.isTypeTuple())
+ {
+ // Check for empty tuples
+ if (tu.arguments is null)
+ return 0;
+
+ foreach (nidx; 0 .. tu.arguments.length)
+ {
+ Parameter nextep = (*tu.arguments)[nidx];
+ if (auto r = _foreachImpl(dg, oidx, oparam, eidx, nextep))
+ return r;
+ }
+ }
+ else
+ {
+ if (auto r = dg(oidx, oparam, eidx, eparam))
+ return r;
+ // The only place where we should increment eidx is here,
+ // as a TypeTuple doesn't count as a parameter (for arity)
+ // it it is empty.
+ eidx++;
+ }
+ return 0;
+ }
+
+ override const(char)* toChars() const
+ {
+ return ident ? ident.toChars() : "__anonymous_param";
+ }
+
+ /*********************************
+ * Compute covariance of parameters `this` and `p`
+ * as determined by the storage classes of both.
+ *
+ * Params:
+ * returnByRef = true if the function returns by ref
+ * p = Parameter to compare with
+ * previewIn = Whether `-preview=in` is being used, and thus if
+ * `in` means `scope [ref]`.
+ *
+ * Returns:
+ * true = `this` can be used in place of `p`
+ * false = nope
+ */
+ bool isCovariant(bool returnByRef, const Parameter p, bool previewIn = global.params.previewIn)
+ const pure nothrow @nogc @safe
+ {
+ ulong thisSTC = this.storageClass;
+ ulong otherSTC = p.storageClass;
+
+ if (previewIn)
+ {
+ if (thisSTC & STC.in_)
+ thisSTC |= STC.scope_;
+ if (otherSTC & STC.in_)
+ otherSTC |= STC.scope_;
+ }
+
+ const mask = STC.ref_ | STC.out_ | STC.lazy_ | (previewIn ? STC.in_ : 0);
+ if ((thisSTC & mask) != (otherSTC & mask))
+ return false;
+ return isCovariantScope(returnByRef, thisSTC, otherSTC);
+ }
+
+ extern (D) private static bool isCovariantScope(bool returnByRef, StorageClass from, StorageClass to) pure nothrow @nogc @safe
+ {
+ if (from == to)
+ return true;
+
+ /* result is true if the 'from' can be used as a 'to'
+ */
+
+ if ((from ^ to) & STC.ref_) // differing in 'ref' means no covariance
+ return false;
+
+ /* workaround until we get STC.returnScope reliably set correctly
+ */
+ if (returnByRef)
+ {
+ from &= ~STC.returnScope;
+ to &= ~STC.returnScope;
+ }
+ else
+ {
+ from |= STC.returnScope;
+ to |= STC.returnScope;
+ }
+ return covariant[buildScopeRef(from)][buildScopeRef(to)];
+ }
+
+ extern (D) private static bool[ScopeRef.max + 1][ScopeRef.max + 1] covariantInit() pure nothrow @nogc @safe
+ {
+ /* Initialize covariant[][] with this:
+
+ From\To n rs s
+ None X
+ ReturnScope X X
+ Scope X X X
+
+ From\To r rr rs rr-s r-rs
+ Ref X X
+ ReturnRef X
+ RefScope X X X X X
+ ReturnRef-Scope X X
+ Ref-ReturnScope X X X
+ */
+ bool[ScopeRef.max + 1][ScopeRef.max + 1] covariant;
+
+ foreach (i; 0 .. ScopeRef.max + 1)
+ {
+ covariant[i][i] = true;
+ covariant[ScopeRef.RefScope][i] = true;
+ }
+ covariant[ScopeRef.ReturnScope][ScopeRef.None] = true;
+ covariant[ScopeRef.Scope ][ScopeRef.None] = true;
+ covariant[ScopeRef.Scope ][ScopeRef.ReturnScope] = true;
+
+ covariant[ScopeRef.Ref ][ScopeRef.ReturnRef] = true;
+ covariant[ScopeRef.ReturnRef_Scope][ScopeRef.ReturnRef] = true;
+ covariant[ScopeRef.Ref_ReturnScope][ScopeRef.Ref ] = true;
+ covariant[ScopeRef.Ref_ReturnScope][ScopeRef.ReturnRef] = true;
+
+ return covariant;
+ }
+
+ extern (D) private static immutable bool[ScopeRef.max + 1][ScopeRef.max + 1] covariant = covariantInit();
+
+ extern (D) bool opEquals(const Parameter other) const
+ {
+ return this.storageClass == other.storageClass
+ && this.type == other.type;
+ }
+}
+
+/*************************************************************
+ * For printing two types with qualification when necessary.
+ * Params:
+ * t1 = The first type to receive the type name for
+ * t2 = The second type to receive the type name for
+ * Returns:
+ * The fully-qualified names of both types if the two type names are not the same,
+ * or the unqualified names of both types if the two type names are the same.
+ */
+const(char*)[2] toAutoQualChars(Type t1, Type t2)
+{
+ auto s1 = t1.toChars();
+ auto s2 = t2.toChars();
+ // show qualification only if it's different
+ if (!t1.equals(t2) && strcmp(s1, s2) == 0)
+ {
+ s1 = t1.toPrettyChars(true);
+ s2 = t2.toPrettyChars(true);
+ }
+ return [s1, s2];
+}
+
+
+/**
+ * For each active modifier (MODFlags.const_, MODFlags.immutable_, etc) call `fp` with a
+ * void* for the work param and a string representation of the attribute.
+ */
+void modifiersApply(const TypeFunction tf, void delegate(string) dg)
+{
+ immutable ubyte[4] modsArr = [MODFlags.const_, MODFlags.immutable_, MODFlags.wild, MODFlags.shared_];
+
+ foreach (modsarr; modsArr)
+ {
+ if (tf.mod & modsarr)
+ {
+ dg(MODtoString(modsarr));
+ }
+ }
+}
+
+/**
+ * For each active attribute (ref/const/nogc/etc) call `fp` with a void* for the
+ * work param and a string representation of the attribute.
+ */
+void attributesApply(const TypeFunction tf, void delegate(string) dg, TRUSTformat trustFormat = TRUSTformatDefault)
+{
+ if (tf.purity)
+ dg("pure");
+ if (tf.isnothrow)
+ dg("nothrow");
+ if (tf.isnogc)
+ dg("@nogc");
+ if (tf.isproperty)
+ dg("@property");
+ if (tf.isref)
+ dg("ref");
+ if (tf.isreturn && !tf.isreturninferred)
+ dg("return");
+ if (tf.isScopeQual && !tf.isscopeinferred)
+ dg("scope");
+ if (tf.islive)
+ dg("@live");
+
+ TRUST trustAttrib = tf.trust;
+
+ if (trustAttrib == TRUST.default_)
+ {
+ if (trustFormat == TRUSTformatSystem)
+ trustAttrib = TRUST.system;
+ else
+ return; // avoid calling with an empty string
+ }
+
+ dg(trustToString(trustAttrib));
+}
+
+/**
+ * If the type is a class or struct, returns the symbol for it,
+ * else null.
+ */
+extern (C++) AggregateDeclaration isAggregate(Type t)
+{
+ t = t.toBasetype();
+ if (t.ty == Tclass)
+ return (cast(TypeClass)t).sym;
+ if (t.ty == Tstruct)
+ return (cast(TypeStruct)t).sym;
+ return null;
+}
+
+/***************************************************
+ * Determine if type t can be indexed or sliced given that it is not an
+ * aggregate with operator overloads.
+ * Params:
+ * t = type to check
+ * Returns:
+ * true if an expression of type t can be e1 in an array expression
+ */
+bool isIndexableNonAggregate(Type t)
+{
+ t = t.toBasetype();
+ return (t.ty == Tpointer || t.ty == Tsarray || t.ty == Tarray || t.ty == Taarray ||
+ t.ty == Ttuple || t.ty == Tvector);
+}
+
+/***************************************************
+ * Determine if type t is copyable.
+ * Params:
+ * t = type to check
+ * Returns:
+ * true if we can copy it
+ */
+bool isCopyable(Type t)
+{
+ //printf("isCopyable() %s\n", t.toChars());
+ if (auto ts = t.isTypeStruct())
+ {
+ if (ts.sym.postblit &&
+ ts.sym.postblit.storage_class & STC.disable)
+ return false;
+ if (ts.sym.hasCopyCtor)
+ {
+ // check if there is a matching overload of the copy constructor and whether it is disabled or not
+ // `assert(ctor)` fails on Win32 and Win_32_64. See: https://auto-tester.puremagic.com/pull-history.ghtml?projectid=1&repoid=1&pullid=10575
+ Dsymbol ctor = search_function(ts.sym, Id.ctor);
+ assert(ctor);
+ scope el = new IdentifierExp(Loc.initial, Id.p); // dummy lvalue
+ el.type = cast() ts;
+ Expressions args;
+ args.push(el);
+ FuncDeclaration f = resolveFuncCall(Loc.initial, null, ctor, null, cast()ts, &args, FuncResolveFlag.quiet);
+ if (!f || f.storage_class & STC.disable)
+ return false;
+ }
+ }
+ return true;
+}
+
+/***************************************
+ * Computes how a parameter may be returned.
+ * Shrinking the representation is necessary because StorageClass is so wide
+ * Params:
+ * stc = storage class of parameter
+ * Returns:
+ * value from enum ScopeRef
+ */
+ScopeRef buildScopeRef(StorageClass stc) pure nothrow @nogc @safe
+{
+ if (stc & STC.out_)
+ stc |= STC.ref_; // treat `out` and `ref` the same
+
+ ScopeRef result;
+ final switch (stc & (STC.ref_ | STC.scope_ | STC.return_))
+ {
+ case 0: result = ScopeRef.None; break;
+
+ /* can occur in case test/compilable/testsctreturn.d
+ * related to https://issues.dlang.org/show_bug.cgi?id=20149
+ * where inout adds `return` without `scope` or `ref`
+ */
+ case STC.return_: result = ScopeRef.Return; break;
+
+ case STC.ref_: result = ScopeRef.Ref; break;
+ case STC.scope_: result = ScopeRef.Scope; break;
+ case STC.return_ | STC.ref_: result = ScopeRef.ReturnRef; break;
+ case STC.return_ | STC.scope_: result = ScopeRef.ReturnScope; break;
+ case STC.ref_ | STC.scope_: result = ScopeRef.RefScope; break;
+
+ case STC.return_ | STC.ref_ | STC.scope_:
+ result = stc & STC.returnScope ? ScopeRef.Ref_ReturnScope
+ : ScopeRef.ReturnRef_Scope;
+ break;
+ }
+ return result;
+}
+
+/**
+ * Classification of 'scope-return-ref' possibilities
+ */
+enum ScopeRef
+{
+ None,
+ Scope,
+ ReturnScope,
+ Ref,
+ ReturnRef,
+ RefScope,
+ ReturnRef_Scope,
+ Ref_ReturnScope,
+ Return,
+}
+
+/*********************************
+ * Give us a nice string for debugging purposes.
+ * Params:
+ * sr = value
+ * Returns:
+ * corresponding string
+ */
+const(char)* toChars(ScopeRef sr) pure nothrow @nogc @safe
+{
+ with (ScopeRef)
+ {
+ static immutable char*[ScopeRef.max + 1] names =
+ [
+ None: "None",
+ Scope: "Scope",
+ ReturnScope: "ReturnScope",
+ Ref: "Ref",
+ ReturnRef: "ReturnRef",
+ RefScope: "RefScope",
+ ReturnRef_Scope: "ReturnRef_Scope",
+ Ref_ReturnScope: "Ref_ReturnScope",
+ Return: "Return",
+ ];
+ return names[sr];
+ }
+}
diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h
index 3687053488d..cdf221f55f7 100644
--- a/gcc/d/dmd/mtype.h
+++ b/gcc/d/dmd/mtype.h
@@ -10,15 +10,15 @@
#pragma once
-#include "root/root.h"
-#include "root/stringtable.h"
+#include "root/dcompat.h" // for d_size_t
#include "arraytypes.h"
#include "ast_node.h"
-#include "expression.h"
+#include "globals.h"
#include "visitor.h"
struct Scope;
+class AggregateDeclaration;
class Identifier;
class Expression;
class StructDeclaration;
@@ -28,7 +28,6 @@ class TypeInfoDeclaration;
class Dsymbol;
class TemplateInstance;
class TemplateDeclaration;
-enum LINK;
class TypeBasic;
class Parameter;
@@ -40,12 +39,12 @@ typedef union tree_node type;
typedef struct TYPE type;
#endif
-Type *typeSemantic(Type *type, const Loc &loc, Scope *sc);
void semanticTypeInfo(Scope *sc, Type *t);
-MATCH deduceType(RootObject *o, Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL, size_t inferStart = 0);
-StorageClass ModToStc(unsigned mod);
-enum ENUMTY
+Type *typeSemantic(Type *t, const Loc &loc, Scope *sc);
+Type *merge(Type *type);
+
+enum class TY : uint8_t
{
Tarray, // slice array, aka T[]
Tsarray, // static array, aka T[dimension]
@@ -100,7 +99,6 @@ enum ENUMTY
Tnoreturn,
TMAX
};
-typedef unsigned char TY; // ENUMTY
#define SIZE_INVALID (~(d_uns64)0) // error return from size() functions
@@ -120,22 +118,22 @@ enum MODFlags
};
typedef unsigned char MOD;
-// These tables are for implicit conversion of binary ops;
-// the indices are the type of operand one, followed by operand two.
-extern unsigned char impcnvResult[TMAX][TMAX];
-extern unsigned char impcnvType1[TMAX][TMAX];
-extern unsigned char impcnvType2[TMAX][TMAX];
-
-// If !=0, give warning on implicit conversion
-extern unsigned char impcnvWarn[TMAX][TMAX];
+enum class Covariant
+{
+ distinct = 0,
+ yes = 1,
+ no = 2,
+ fwdref = 3,
+};
-enum VarArg
+enum VarArgValues
{
VARARGnone = 0, /// fixed number of arguments
VARARGvariadic = 1, /// T t, ...) can be C-style (core.stdc.stdarg) or D-style (core.vararg)
VARARGtypesafe = 2 /// T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions
/// or https://dlang.org/spec/function.html#typesafe_variadic_functions
};
+typedef unsigned char VarArg;
class Type : public ASTNode
{
@@ -144,22 +142,10 @@ public:
MOD mod; // modifiers MODxxxx
char *deco;
- /* These are cached values that are lazily evaluated by constOf(), immutableOf(), etc.
- * They should not be referenced by anybody but mtype.c.
- * They can be NULL if not lazily evaluated yet.
- * Note that there is no "shared immutable", because that is just immutable
- * Naked == no MOD bits
- */
-
- Type *cto; // MODconst ? naked version of this type : const version
- Type *ito; // MODimmutable ? naked version of this type : immutable version
- Type *sto; // MODshared ? naked version of this type : shared mutable version
- Type *scto; // MODshared | MODconst ? naked version of this type : shared const version
- Type *wto; // MODwild ? naked version of this type : wild version
- Type *wcto; // MODwildconst ? naked version of this type : wild const version
- Type *swto; // MODshared | MODwild ? naked version of this type : shared wild version
- Type *swcto; // MODshared | MODwildconst ? naked version of this type : shared wild const version
+private:
+ void* mcache;
+public:
Type *pto; // merged pointer to this type
Type *rto; // reference to this type
Type *arrayof; // array of this type
@@ -229,35 +215,27 @@ public:
static TemplateDeclaration *rtinfo;
- static Type *basic[TMAX];
- static unsigned char sizeTy[TMAX];
- static StringTable stringtable;
+ static Type *basic[(int)TY::TMAX];
- Type(TY ty);
virtual const char *kind();
- Type *copy();
+ Type *copy() const;
virtual Type *syntaxCopy();
- bool equals(RootObject *o);
+ bool equals(const RootObject *o) const;
bool equivalent(Type *t);
// kludge for template.isType()
- int dyncast() const { return DYNCAST_TYPE; }
- int covariant(Type *t, StorageClass *pstc = NULL, bool fix17349 = true);
- const char *toChars();
+ DYNCAST dyncast() const { return DYNCAST_TYPE; }
+ Covariant covariant(Type *t, StorageClass *pstc = NULL);
+ const char *toChars() const;
char *toPrettyChars(bool QualifyTypes = false);
static void _init();
d_uns64 size();
- virtual d_uns64 size(Loc loc);
+ virtual d_uns64 size(const Loc &loc);
virtual unsigned alignsize();
- Type *trySemantic(Loc loc, Scope *sc);
- Type *merge();
+ Type *trySemantic(const Loc &loc, Scope *sc);
Type *merge2();
- void modToBuffer(OutBuffer *buf);
- char *modToChars();
-
- /** For each active modifier (MODconst, MODimmutable, etc) call fp with a
- void* for the work param and a string representation of the attribute. */
- int modifiersApply(void *param, int (*fp)(void *, const char *));
+ void modToBuffer(OutBuffer *buf) const;
+ char *modToChars() const;
virtual bool isintegral();
virtual bool isfloating(); // real, imaginary, or complex
@@ -270,7 +248,7 @@ public:
virtual bool isString();
virtual bool isAssignable();
virtual bool isBoolean();
- virtual void checkDeprecated(Loc loc, Scope *sc);
+ virtual void checkDeprecated(const Loc &loc, Scope *sc);
bool isConst() const { return (mod & MODconst) != 0; }
bool isImmutable() const { return (mod & MODimmutable) != 0; }
bool isMutable() const { return (mod & (MODconst | MODimmutable | MODwild)) == 0; }
@@ -280,7 +258,7 @@ public:
bool isWildConst() const { return (mod & MODwildconst) == MODwildconst; }
bool isSharedWild() const { return (mod & (MODshared | MODwild)) == (MODshared | MODwild); }
bool isNaked() const { return mod == 0; }
- Type *nullAttributes();
+ Type *nullAttributes() const;
Type *constOf();
Type *immutableOf();
Type *mutableOf();
@@ -301,8 +279,8 @@ public:
Type *referenceTo();
Type *arrayOf();
Type *sarrayOf(dinteger_t dim);
+ bool hasDeprecatedAliasThis();
Type *aliasthisOf();
- bool checkAliasThisRec();
virtual Type *makeConst();
virtual Type *makeImmutable();
virtual Type *makeShared();
@@ -313,7 +291,7 @@ public:
virtual Type *makeSharedWildConst();
virtual Type *makeMutable();
virtual Dsymbol *toDsymbol(Scope *sc);
- virtual Type *toBasetype();
+ Type *toBasetype();
virtual bool isBaseOf(Type *t, int *poffset);
virtual MATCH implicitConvTo(Type *to);
virtual MATCH constConv(Type *to);
@@ -324,33 +302,27 @@ public:
virtual Type *toHeadMutable();
virtual ClassDeclaration *isClassHandle();
- virtual Expression *getProperty(Loc loc, Identifier *ident, int flag);
- virtual Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
virtual structalign_t alignment();
- Expression *noMember(Scope *sc, Expression *e, Identifier *ident, int flag);
- virtual Expression *defaultInit(Loc loc = Loc());
- virtual Expression *defaultInitLiteral(Loc loc);
- virtual bool isZeroInit(Loc loc = Loc()); // if initializer is 0
+ virtual Expression *defaultInitLiteral(const Loc &loc);
+ virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0
Identifier *getTypeInfoIdent();
- virtual void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
- void resolveExp(Expression *e, Type **pt, Expression **pe, Dsymbol **ps);
virtual int hasWild() const;
virtual bool hasPointers();
virtual bool hasVoidInitPointers();
+ virtual bool hasInvariant();
virtual Type *nextOf();
Type *baseElemOf();
uinteger_t sizemask();
- unsigned numberOfElems(const Loc &loc);
virtual bool needsDestruction();
+ virtual bool needsCopyOrPostblit();
virtual bool needsNested();
- void checkComplexTransition(Loc loc);
- TypeFunction *toTypeFunction();
- static void error(Loc loc, const char *format, ...);
- static void warning(Loc loc, const char *format, ...);
+ TypeFunction *toTypeFunction();
// For eliminating dynamic_cast
virtual TypeBasic *isTypeBasic();
+ TypeFunction *isPtrToFunction();
+ TypeFunction *isFunction_Delegate_PtrToFunction();
TypeError *isTypeError();
TypeVector *isTypeVector();
TypeSArray *isTypeSArray();
@@ -373,6 +345,7 @@ public:
TypeMixin *isTypeMixin();
TypeTraits *isTypeTraits();
TypeNoreturn *isTypeNoreturn();
+ TypeTag *isTypeTag();
void accept(Visitor *v) { v->visit(this); }
};
@@ -380,14 +353,11 @@ public:
class TypeError : public Type
{
public:
- TypeError();
- Type *syntaxCopy();
-
- d_uns64 size(Loc loc);
- Expression *getProperty(Loc loc, Identifier *ident, int flag);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
- Expression *defaultInit(Loc loc);
- Expression *defaultInitLiteral(Loc loc);
+ const char *kind();
+ TypeError *syntaxCopy();
+
+ d_uns64 size(const Loc &loc);
+ Expression *defaultInitLiteral(const Loc &loc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -396,8 +366,7 @@ class TypeNext : public Type
public:
Type *next;
- TypeNext(TY ty, Type *next);
- void checkDeprecated(Loc loc, Scope *sc);
+ void checkDeprecated(const Loc &loc, Scope *sc);
int hasWild() const;
Type *nextOf();
Type *makeConst();
@@ -421,13 +390,10 @@ public:
const char *dstring;
unsigned flags;
- TypeBasic(TY ty);
const char *kind();
- Type *syntaxCopy();
- d_uns64 size(Loc loc) /*const*/;
+ TypeBasic *syntaxCopy();
+ d_uns64 size(const Loc &loc) /*const*/;
unsigned alignsize();
- Expression *getProperty(Loc loc, Identifier *ident, int flag);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
bool isintegral();
bool isfloating() /*const*/;
bool isreal() /*const*/;
@@ -436,8 +402,7 @@ public:
bool isscalar() /*const*/;
bool isunsigned() /*const*/;
MATCH implicitConvTo(Type *to);
- Expression *defaultInit(Loc loc);
- bool isZeroInit(Loc loc) /*const*/;
+ bool isZeroInit(const Loc &loc) /*const*/;
// For eliminating dynamic_cast
TypeBasic *isTypeBasic();
@@ -449,24 +414,20 @@ class TypeVector : public Type
public:
Type *basetype;
- TypeVector(Type *basetype);
static TypeVector *create(Type *basetype);
const char *kind();
- Type *syntaxCopy();
- d_uns64 size(Loc loc);
+ TypeVector *syntaxCopy();
+ d_uns64 size(const Loc &loc);
unsigned alignsize();
- Expression *getProperty(Loc loc, Identifier *ident, int flag);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
bool isintegral();
bool isfloating();
bool isscalar();
bool isunsigned();
bool isBoolean() /*const*/;
MATCH implicitConvTo(Type *to);
- Expression *defaultInit(Loc loc);
- Expression *defaultInitLiteral(Loc loc);
+ Expression *defaultInitLiteral(const Loc &loc);
TypeBasic *elementType();
- bool isZeroInit(Loc loc);
+ bool isZeroInit(const Loc &loc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -474,8 +435,6 @@ public:
class TypeArray : public TypeNext
{
public:
- TypeArray(TY ty, Type *next);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
void accept(Visitor *v) { v->visit(this); }
};
@@ -485,22 +444,20 @@ class TypeSArray : public TypeArray
public:
Expression *dim;
- TypeSArray(Type *t, Expression *dim);
const char *kind();
- Type *syntaxCopy();
- d_uns64 size(Loc loc);
+ TypeSArray *syntaxCopy();
+ d_uns64 size(const Loc &loc);
unsigned alignsize();
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
bool isString();
- bool isZeroInit(Loc loc);
+ bool isZeroInit(const Loc &loc);
structalign_t alignment();
MATCH constConv(Type *to);
MATCH implicitConvTo(Type *to);
- Expression *defaultInit(Loc loc);
- Expression *defaultInitLiteral(Loc loc);
+ Expression *defaultInitLiteral(const Loc &loc);
bool hasPointers();
+ bool hasInvariant();
bool needsDestruction();
+ bool needsCopyOrPostblit();
bool needsNested();
void accept(Visitor *v) { v->visit(this); }
@@ -510,18 +467,14 @@ public:
class TypeDArray : public TypeArray
{
public:
- TypeDArray(Type *t);
const char *kind();
- Type *syntaxCopy();
- d_uns64 size(Loc loc) /*const*/;
+ TypeDArray *syntaxCopy();
+ d_uns64 size(const Loc &loc) /*const*/;
unsigned alignsize() /*const*/;
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
bool isString();
- bool isZeroInit(Loc loc) /*const*/;
+ bool isZeroInit(const Loc &loc) /*const*/;
bool isBoolean() /*const*/;
MATCH implicitConvTo(Type *to);
- Expression *defaultInit(Loc loc);
bool hasPointers() /*const*/;
void accept(Visitor *v) { v->visit(this); }
@@ -532,17 +485,12 @@ class TypeAArray : public TypeArray
public:
Type *index; // key type
Loc loc;
- Scope *sc;
- TypeAArray(Type *t, Type *index);
static TypeAArray *create(Type *t, Type *index);
const char *kind();
- Type *syntaxCopy();
- d_uns64 size(Loc loc);
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
- Expression *defaultInit(Loc loc);
- bool isZeroInit(Loc loc) /*const*/;
+ TypeAArray *syntaxCopy();
+ d_uns64 size(const Loc &loc);
+ bool isZeroInit(const Loc &loc) /*const*/;
bool isBoolean() /*const*/;
bool hasPointers() /*const*/;
MATCH implicitConvTo(Type *to);
@@ -554,16 +502,14 @@ public:
class TypePointer : public TypeNext
{
public:
- TypePointer(Type *t);
static TypePointer *create(Type *t);
const char *kind();
- Type *syntaxCopy();
- d_uns64 size(Loc loc) /*const*/;
+ TypePointer *syntaxCopy();
+ d_uns64 size(const Loc &loc) /*const*/;
MATCH implicitConvTo(Type *to);
MATCH constConv(Type *to);
bool isscalar() /*const*/;
- Expression *defaultInit(Loc loc);
- bool isZeroInit(Loc loc) /*const*/;
+ bool isZeroInit(const Loc &loc) /*const*/;
bool hasPointers() /*const*/;
void accept(Visitor *v) { v->visit(this); }
@@ -572,13 +518,10 @@ public:
class TypeReference : public TypeNext
{
public:
- TypeReference(Type *t);
const char *kind();
- Type *syntaxCopy();
- d_uns64 size(Loc loc) /*const*/;
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
- Expression *defaultInit(Loc loc);
- bool isZeroInit(Loc loc) /*const*/;
+ TypeReference *syntaxCopy();
+ d_uns64 size(const Loc &loc) /*const*/;
+ bool isZeroInit(const Loc &loc) /*const*/;
void accept(Visitor *v) { v->visit(this); }
};
@@ -588,31 +531,27 @@ enum RET
RETstack = 2 // returned on stack
};
-enum TRUST
+enum class TRUST : unsigned char
{
- TRUSTdefault = 0,
- TRUSTsystem = 1, // @system (same as TRUSTdefault)
- TRUSTtrusted = 2, // @trusted
- TRUSTsafe = 3 // @safe
+ default_ = 0,
+ system = 1, // @system (same as TRUSTdefault)
+ trusted = 2, // @trusted
+ safe = 3 // @safe
};
-// in hdrgen.c
-void trustToBuffer(OutBuffer *buf, TRUST trust);
-const char *trustToChars(TRUST trust);
-
enum TRUSTformat
{
TRUSTformatDefault, // do not emit @system when trust == TRUSTdefault
TRUSTformatSystem // emit @system when trust == TRUSTdefault
};
-enum PURE
+enum class PURE : unsigned char
{
- PUREimpure = 0, // not pure at all
- PUREfwdref = 1, // it's pure, but not known which level yet
- PUREweak = 2, // no mutable globals are read or written
- PUREconst = 3, // parameters are values or const
- PUREstrong = 4 // parameters are values or immutable
+ impure = 0, // not pure at all
+ fwdref = 1, // it's pure, but not known which level yet
+ weak = 2, // no mutable globals are read or written
+ const_ = 3, // parameters are values or const
+ strong = 4 // parameters are values or immutable
};
class Parameter : public ASTNode
@@ -624,31 +563,26 @@ public:
Expression *defaultArg;
UserAttributeDeclaration *userAttribDecl; // user defined attributes
- Parameter(StorageClass storageClass, Type *type, Identifier *ident,
- Expression *defaultArg, UserAttributeDeclaration *userAttribDecl);
static Parameter *create(StorageClass storageClass, Type *type, Identifier *ident,
Expression *defaultArg, UserAttributeDeclaration *userAttribDecl);
Parameter *syntaxCopy();
Type *isLazyArray();
// kludge for template.isType()
- int dyncast() const { return DYNCAST_PARAMETER; }
+ DYNCAST dyncast() const { return DYNCAST_PARAMETER; }
void accept(Visitor *v) { v->visit(this); }
- static Parameters *arraySyntaxCopy(Parameters *parameters);
static size_t dim(Parameters *parameters);
- static Parameter *getNth(Parameters *parameters, size_t nth, size_t *pn = NULL);
- const char *toChars();
- bool isCovariant(bool returnByRef, const Parameter *p) const;
- static bool isCovariantScope(bool returnByRef, StorageClass from, StorageClass to);
+ static Parameter *getNth(Parameters *parameters, d_size_t nth);
+ const char *toChars() const;
+ bool isCovariant(bool returnByRef, const Parameter *p, bool previewIn) const;
};
struct ParameterList
{
- Parameters *parameters;
+ Parameters* parameters;
+ StorageClass stc;
VarArg varargs;
- ParameterList(Parameters *parameters = NULL, VarArg varargs = VARARGnone);
-
size_t length();
Parameter *operator[](size_t i) { return Parameter::getNth(parameters, i); }
};
@@ -658,27 +592,17 @@ class TypeFunction : public TypeNext
public:
// .next is the return type
- ParameterList parameterList; // function parameters
-
- bool isnothrow; // true: nothrow
- bool isnogc; // true: is @nogc
- bool isproperty; // can be called without parentheses
- bool isref; // true: returns a reference
- bool isreturn; // true: 'this' is returned by ref
- bool isscope; // true: 'this' is scope
- bool isscopeinferred; // true: 'this' is scope from inference
- LINK linkage; // calling convention
- TRUST trust; // level of trust
- PURE purity; // PURExxxx
- unsigned char iswild; // bit0: inout on params, bit1: inout on qualifier
- Expressions *fargs; // function arguments
+ ParameterList parameterList; // function parameters
+ LINK linkage; // calling convention
+ unsigned funcFlags;
+ TRUST trust; // level of trust
+ PURE purity; // PURExxxx
+ char inuse;
+ Expressions *fargs; // function arguments
- int inuse;
-
- TypeFunction(const ParameterList &pl, Type *treturn, LINK linkage, StorageClass stc = 0);
static TypeFunction *create(Parameters *parameters, Type *treturn, VarArg varargs, LINK linkage, StorageClass stc = 0);
const char *kind();
- Type *syntaxCopy();
+ TypeFunction *syntaxCopy();
void purityLevel();
bool hasLazyParameters();
bool isDstyleVariadic() const;
@@ -686,15 +610,35 @@ public:
StorageClass parameterStorageClass(Parameter *p);
Type *addStorageClass(StorageClass stc);
- /** For each active attribute (ref/const/nogc/etc) call fp with a void* for the
- work param and a string representation of the attribute. */
- int attributesApply(void *param, int (*fp)(void *, const char *), TRUSTformat trustFormat = TRUSTformatDefault);
-
Type *substWildTo(unsigned mod);
- MATCH callMatch(Type *tthis, Expressions *toargs, int flag = 0, const char **pMessage = NULL);
- bool checkRetType(Loc loc);
+ MATCH constConv(Type *to);
+
+ bool isnothrow() const;
+ void isnothrow(bool v);
+ bool isnogc() const;
+ void isnogc(bool v);
+ bool isproperty() const;
+ void isproperty(bool v);
+ bool isref() const;
+ void isref(bool v);
+ bool isreturn() const;
+ void isreturn(bool v);
+ bool isScopeQual() const;
+ void isScopeQual(bool v);
+ bool isreturninferred() const;
+ void isreturninferred(bool v);
+ bool isscopeinferred() const;
+ void isscopeinferred(bool v);
+ bool islive() const;
+ void islive(bool v);
+ bool incomplete() const;
+ void incomplete(bool v);
+ bool isInOutParam() const;
+ void isInOutParam(bool v);
+ bool isInOutQual() const;
+ void isInOutQual(bool v);
+ bool iswild() const;
- Expression *defaultInit(Loc loc) /*const*/;
void accept(Visitor *v) { v->visit(this); }
};
@@ -703,18 +647,15 @@ class TypeDelegate : public TypeNext
public:
// .next is a TypeFunction
- TypeDelegate(Type *t);
- static TypeDelegate *create(Type *t);
+ static TypeDelegate *create(TypeFunction *t);
const char *kind();
- Type *syntaxCopy();
+ TypeDelegate *syntaxCopy();
Type *addStorageClass(StorageClass stc);
- d_uns64 size(Loc loc) /*const*/;
+ d_uns64 size(const Loc &loc) /*const*/;
unsigned alignsize() /*const*/;
MATCH implicitConvTo(Type *to);
- Expression *defaultInit(Loc loc);
- bool isZeroInit(Loc loc) /*const*/;
+ bool isZeroInit(const Loc &loc) /*const*/;
bool isBoolean() /*const*/;
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
bool hasPointers() /*const*/;
void accept(Visitor *v) { v->visit(this); }
@@ -722,33 +663,28 @@ public:
class TypeTraits : public Type
{
-public:
Loc loc;
/// The expression to resolve as type or symbol.
TraitsExp *exp;
/// The symbol when exp doesn't represent a type.
Dsymbol *sym;
- TypeTraits(const Loc &loc, TraitsExp *exp);
- Type *syntaxCopy();
+ const char *kind();
+ TypeTraits *syntaxCopy();
+ d_uns64 size(const Loc &loc);
Dsymbol *toDsymbol(Scope *sc);
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
- d_uns64 size(Loc loc);
void accept(Visitor *v) { v->visit(this); }
};
class TypeMixin : public Type
{
-public:
Loc loc;
Expressions *exps;
RootObject *obj;
- TypeMixin(const Loc &loc, Expressions *exps);
const char *kind();
- Type *syntaxCopy();
+ TypeMixin *syntaxCopy();
Dsymbol *toDsymbol(Scope *sc);
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
void accept(Visitor *v) { v->visit(this); }
};
@@ -760,17 +696,11 @@ public:
// representing ident.ident!tiargs.ident. ... etc.
Objects idents;
- TypeQualified(TY ty, Loc loc);
void syntaxCopyHelper(TypeQualified *t);
void addIdent(Identifier *ident);
void addInst(TemplateInstance *inst);
void addIndex(RootObject *expr);
- d_uns64 size(Loc loc);
-
- void resolveTupleIndex(Loc loc, Scope *sc, Dsymbol *s,
- Expression **pe, Type **pt, Dsymbol **ps, RootObject *oindex);
- void resolveHelper(Loc loc, Scope *sc, Dsymbol *s, Dsymbol *scopesym,
- Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
+ d_uns64 size(const Loc &loc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -781,10 +711,8 @@ public:
Identifier *ident;
Dsymbol *originalSymbol; // The symbol representing this identifier, before alias resolution
- TypeIdentifier(Loc loc, Identifier *ident);
const char *kind();
- Type *syntaxCopy();
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
+ TypeIdentifier *syntaxCopy();
Dsymbol *toDsymbol(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -796,10 +724,8 @@ class TypeInstance : public TypeQualified
public:
TemplateInstance *tempinst;
- TypeInstance(Loc loc, TemplateInstance *tempinst);
const char *kind();
- Type *syntaxCopy();
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
+ TypeInstance *syntaxCopy();
Dsymbol *toDsymbol(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -810,23 +736,19 @@ public:
Expression *exp;
int inuse;
- TypeTypeof(Loc loc, Expression *exp);
const char *kind();
- Type *syntaxCopy();
+ TypeTypeof *syntaxCopy();
Dsymbol *toDsymbol(Scope *sc);
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
- d_uns64 size(Loc loc);
+ d_uns64 size(const Loc &loc);
void accept(Visitor *v) { v->visit(this); }
};
class TypeReturn : public TypeQualified
{
public:
- TypeReturn(Loc loc);
const char *kind();
- Type *syntaxCopy();
+ TypeReturn *syntaxCopy();
Dsymbol *toDsymbol(Scope *sc);
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
void accept(Visitor *v) { v->visit(this); }
};
@@ -847,26 +769,25 @@ class TypeStruct : public Type
public:
StructDeclaration *sym;
AliasThisRec att;
- CPPMANGLE cppmangle;
+ bool inuse;
- TypeStruct(StructDeclaration *sym);
static TypeStruct *create(StructDeclaration *sym);
const char *kind();
- d_uns64 size(Loc loc);
+ d_uns64 size(const Loc &loc);
unsigned alignsize();
- Type *syntaxCopy();
+ TypeStruct *syntaxCopy();
Dsymbol *toDsymbol(Scope *sc);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
structalign_t alignment();
- Expression *defaultInit(Loc loc);
- Expression *defaultInitLiteral(Loc loc);
- bool isZeroInit(Loc loc) /*const*/;
+ Expression *defaultInitLiteral(const Loc &loc);
+ bool isZeroInit(const Loc &loc);
bool isAssignable();
bool isBoolean() /*const*/;
bool needsDestruction() /*const*/;
+ bool needsCopyOrPostblit();
bool needsNested();
bool hasPointers();
bool hasVoidInitPointers();
+ bool hasInvariant();
MATCH implicitConvTo(Type *to);
MATCH constConv(Type *to);
unsigned char deduceWild(Type *t, bool isRef);
@@ -880,14 +801,12 @@ class TypeEnum : public Type
public:
EnumDeclaration *sym;
- TypeEnum(EnumDeclaration *sym);
const char *kind();
- Type *syntaxCopy();
- d_uns64 size(Loc loc);
+ TypeEnum *syntaxCopy();
+ d_uns64 size(const Loc &loc);
unsigned alignsize();
+ Type *memType(const Loc &loc = Loc());
Dsymbol *toDsymbol(Scope *sc);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
- Expression *getProperty(Loc loc, Identifier *ident, int flag);
bool isintegral();
bool isfloating();
bool isreal();
@@ -899,14 +818,14 @@ public:
bool isString();
bool isAssignable();
bool needsDestruction();
+ bool needsCopyOrPostblit();
bool needsNested();
MATCH implicitConvTo(Type *to);
MATCH constConv(Type *to);
- Type *toBasetype();
- Expression *defaultInit(Loc loc);
- bool isZeroInit(Loc loc);
+ bool isZeroInit(const Loc &loc);
bool hasPointers();
bool hasVoidInitPointers();
+ bool hasInvariant();
Type *nextOf();
void accept(Visitor *v) { v->visit(this); }
@@ -919,20 +838,17 @@ public:
AliasThisRec att;
CPPMANGLE cppmangle;
- TypeClass(ClassDeclaration *sym);
const char *kind();
- d_uns64 size(Loc loc) /*const*/;
- Type *syntaxCopy();
+ d_uns64 size(const Loc &loc) /*const*/;
+ TypeClass *syntaxCopy();
Dsymbol *toDsymbol(Scope *sc);
- Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag);
ClassDeclaration *isClassHandle();
bool isBaseOf(Type *t, int *poffset);
MATCH implicitConvTo(Type *to);
MATCH constConv(Type *to);
unsigned char deduceWild(Type *t, bool isRef);
Type *toHeadMutable();
- Expression *defaultInit(Loc loc);
- bool isZeroInit(Loc loc) /*const*/;
+ bool isZeroInit(const Loc &loc) /*const*/;
bool isscope() /*const*/;
bool isBoolean() /*const*/;
bool hasPointers() /*const*/;
@@ -943,19 +859,18 @@ public:
class TypeTuple : public Type
{
public:
+ // 'logically immutable' cached global - don't modify (neither pointer nor pointee)!
+ static TypeTuple *empty;
+
Parameters *arguments; // types making up the tuple
- TypeTuple(Parameters *arguments);
- TypeTuple(Expressions *exps);
static TypeTuple *create(Parameters *arguments);
- TypeTuple();
- TypeTuple(Type *t1);
- TypeTuple(Type *t1, Type *t2);
+ static TypeTuple *create();
+ static TypeTuple *create(Type *t1);
+ static TypeTuple *create(Type *t1, Type *t2);
const char *kind();
- Type *syntaxCopy();
- bool equals(RootObject *o);
- Expression *getProperty(Loc loc, Identifier *ident, int flag);
- Expression *defaultInit(Loc loc);
+ TypeTuple *syntaxCopy();
+ bool equals(const RootObject *o) const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -965,44 +880,49 @@ public:
Expression *lwr;
Expression *upr;
- TypeSlice(Type *next, Expression *lwr, Expression *upr);
const char *kind();
- Type *syntaxCopy();
- void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false);
+ TypeSlice *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
class TypeNull : public Type
{
public:
- TypeNull();
const char *kind();
- Type *syntaxCopy();
+ TypeNull *syntaxCopy();
MATCH implicitConvTo(Type *to);
bool isBoolean() /*const*/;
- d_uns64 size(Loc loc) /*const*/;
- Expression *defaultInit(Loc loc) /*const*/;
+ d_uns64 size(const Loc &loc) /*const*/;
void accept(Visitor *v) { v->visit(this); }
};
-class TypeNoreturn : public Type
+class TypeNoreturn final : public Type
{
public:
- TypeNoreturn();
const char *kind();
+ TypeNoreturn *syntaxCopy();
+ MATCH implicitConvTo(Type* to);
+ MATCH constConv(Type* to);
+ bool isBoolean() /* const */;
+ d_uns64 size(const Loc& loc) /* const */;
+ unsigned alignsize();
- Type *syntaxCopy();
- MATCH implicitConvTo(Type *to);
- bool isBoolean() /*const*/;
+ void accept(Visitor *v) { v->visit(this); }
+};
+
+class TypeTag final : public Type
+{
+public:
+ TypeTag *syntaxCopy();
- d_uns64 size(Loc loc) /*const*/;
- unsigned alignsize();
void accept(Visitor *v) { v->visit(this); }
};
/**************************************************************/
-bool arrayTypeCompatible(Loc loc, Type *t1, Type *t2);
bool arrayTypeCompatibleWithoutCasting(Type *t1, Type *t2);
+
+// If the type is a class or struct, returns the symbol for it, else null.
+AggregateDeclaration *isAggregate(Type *t);
diff --git a/gcc/d/dmd/nogc.c b/gcc/d/dmd/nogc.c
deleted file mode 100644
index 12c8b490cfb..00000000000
--- a/gcc/d/dmd/nogc.c
+++ /dev/null
@@ -1,241 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/nogc.c
- */
-
-#include "mars.h"
-#include "init.h"
-#include "visitor.h"
-#include "expression.h"
-#include "statement.h"
-#include "declaration.h"
-#include "id.h"
-#include "module.h"
-#include "scope.h"
-#include "tokens.h"
-#include "aggregate.h"
-
-bool walkPostorder(Expression *e, StoppableVisitor *v);
-
-void FuncDeclaration::printGCUsage(Loc loc, const char* warn)
-{
- if (!global.params.vgc)
- return;
-
- Module *m = getModule();
- if (m && m->isRoot() && !inUnittest())
- {
- message(loc, "vgc: %s", warn);
- }
-}
-
-/**************************************
- * Look for GC-allocations
- */
-class NOGCVisitor : public StoppableVisitor
-{
-public:
- FuncDeclaration *f;
- bool err;
-
- NOGCVisitor(FuncDeclaration *f)
- {
- this->f = f;
- this->err = false;
- }
-
- void doCond(Expression *exp)
- {
- if (exp)
- walkPostorder(exp, this);
- }
-
- void visit(Expression *)
- {
- }
-
- void visit(DeclarationExp *e)
- {
- // Note that, walkPostorder does not support DeclarationExp today.
- VarDeclaration *v = e->declaration->isVarDeclaration();
- if (v && !(v->storage_class & STCmanifest) && !v->isDataseg() && v->_init)
- {
- if (ExpInitializer *ei = v->_init->isExpInitializer())
- {
- doCond(ei->exp);
- }
- }
- }
-
- void visit(CallExp *)
- {
- }
-
- void visit(ArrayLiteralExp *e)
- {
- if (e->type->ty != Tarray || !e->elements || !e->elements->length)
- return;
-
- if (f->setGC())
- {
- e->error("array literal in @nogc %s `%s` may cause GC allocation",
- f->kind(), f->toPrettyChars());
- err = true;
- return;
- }
- f->printGCUsage(e->loc, "array literal may cause GC allocation");
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- if (!e->keys->length)
- return;
-
- if (f->setGC())
- {
- e->error("associative array literal in @nogc %s `%s` may cause GC allocation",
- f->kind(), f->toPrettyChars());
- err = true;
- return;
- }
- f->printGCUsage(e->loc, "associative array literal may cause GC allocation");
- }
-
- void visit(NewExp *e)
- {
- if (e->member && !e->member->isNogc() && f->setGC())
- {
- // @nogc-ness is already checked in NewExp::semantic
- return;
- }
- if (e->onstack)
- return;
- if (e->allocator)
- return;
-
- if (f->setGC())
- {
- e->error("cannot use `new` in @nogc %s `%s`",
- f->kind(), f->toPrettyChars());
- err = true;
- return;
- }
- f->printGCUsage(e->loc, "`new` causes GC allocation");
- }
-
- void visit(DeleteExp *e)
- {
- if (e->e1->op == TOKvar)
- {
- VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
- if (v && v->onstack)
- return; // delete for scope allocated class object
- }
-
- Type *tb = e->e1->type->toBasetype();
- AggregateDeclaration *ad = NULL;
- switch (tb->ty)
- {
- case Tclass:
- ad = ((TypeClass *)tb)->sym;
- break;
-
- case Tpointer:
- tb = ((TypePointer *)tb)->next->toBasetype();
- if (tb->ty == Tstruct)
- ad = ((TypeStruct *)tb)->sym;
- break;
-
- default:
- break;
- }
- if (ad && ad->aggDelete)
- return;
-
- if (f->setGC())
- {
- e->error("cannot use `delete` in @nogc %s `%s`",
- f->kind(), f->toPrettyChars());
- err = true;
- return;
- }
- f->printGCUsage(e->loc, "`delete` requires GC");
- }
-
- void visit(IndexExp* e)
- {
- Type *t1b = e->e1->type->toBasetype();
- if (t1b->ty == Taarray)
- {
- if (f->setGC())
- {
- e->error("indexing an associative array in @nogc %s `%s` may cause GC allocation",
- f->kind(), f->toPrettyChars());
- err = true;
- return;
- }
- f->printGCUsage(e->loc, "indexing an associative array may cause GC allocation");
- }
- }
-
- void visit(AssignExp *e)
- {
- if (e->e1->op == TOKarraylength)
- {
- if (f->setGC())
- {
- e->error("setting `length` in @nogc %s `%s` may cause GC allocation",
- f->kind(), f->toPrettyChars());
- err = true;
- return;
- }
- f->printGCUsage(e->loc, "setting `length` may cause GC allocation");
- }
- }
-
- void visit(CatAssignExp *e)
- {
- if (f->setGC())
- {
- e->error("cannot use operator ~= in @nogc %s `%s`",
- f->kind(), f->toPrettyChars());
- err = true;
- return;
- }
- f->printGCUsage(e->loc, "operator ~= may cause GC allocation");
- }
-
- void visit(CatExp *e)
- {
- if (f->setGC())
- {
- e->error("cannot use operator ~ in @nogc %s `%s`",
- f->kind(), f->toPrettyChars());
- err = true;
- return;
- }
- f->printGCUsage(e->loc, "operator ~ may cause GC allocation");
- }
-};
-
-Expression *checkGC(Scope *sc, Expression *e)
-{
- FuncDeclaration *f = sc->func;
- if (e && e->op != TOKerror &&
- f && sc->intypeof != 1 && !(sc->flags & SCOPEctfe) &&
- ((f->type->ty == Tfunction && ((TypeFunction *)f->type)->isnogc) ||
- (f->flags & FUNCFLAGnogcInprocess) ||
- global.params.vgc))
- {
- NOGCVisitor gcv(f);
- walkPostorder(e, &gcv);
- if (gcv.err)
- return new ErrorExp();
- }
- return e;
-}
diff --git a/gcc/d/dmd/nogc.d b/gcc/d/dmd/nogc.d
new file mode 100644
index 00000000000..4bb2907c9ba
--- /dev/null
+++ b/gcc/d/dmd/nogc.d
@@ -0,0 +1,266 @@
+/**
+ * Checks that a function marked `@nogc` does not invoke the Garbage Collector.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/function.html#nogc-functions, No-GC Functions)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/nogc.d, _nogc.d)
+ * Documentation: https://dlang.org/phobos/dmd_nogc.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/nogc.d
+ */
+
+module dmd.nogc;
+
+import dmd.aggregate;
+import dmd.apply;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.init;
+import dmd.mtype;
+import dmd.tokens;
+import dmd.visitor;
+
+/**************************************
+ * Look for GC-allocations
+ */
+extern (C++) final class NOGCVisitor : StoppableVisitor
+{
+ alias visit = typeof(super).visit;
+public:
+ FuncDeclaration f;
+ bool err;
+
+ extern (D) this(FuncDeclaration f)
+ {
+ this.f = f;
+ }
+
+ void doCond(Expression exp)
+ {
+ if (exp)
+ walkPostorder(exp, this);
+ }
+
+ override void visit(Expression e)
+ {
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ // Note that, walkPostorder does not support DeclarationExp today.
+ VarDeclaration v = e.declaration.isVarDeclaration();
+ if (v && !(v.storage_class & STC.manifest) && !v.isDataseg() && v._init)
+ {
+ if (ExpInitializer ei = v._init.isExpInitializer())
+ {
+ doCond(ei.exp);
+ }
+ }
+ }
+
+ override void visit(CallExp e)
+ {
+ import dmd.id : Id;
+ import core.stdc.stdio : printf;
+ if (!e.f)
+ return;
+
+ auto fd = stripHookTraceImpl(e.f);
+ if (fd.ident == Id._d_arraysetlengthT)
+ {
+ if (f.setGC())
+ {
+ e.error("setting `length` in `@nogc` %s `%s` may cause a GC allocation",
+ f.kind(), f.toPrettyChars());
+ err = true;
+ return;
+ }
+ f.printGCUsage(e.loc, "setting `length` may cause a GC allocation");
+ }
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ if (e.type.ty != Tarray || !e.elements || !e.elements.dim)
+ return;
+ if (f.setGC())
+ {
+ e.error("array literal in `@nogc` %s `%s` may cause a GC allocation",
+ f.kind(), f.toPrettyChars());
+ err = true;
+ return;
+ }
+ f.printGCUsage(e.loc, "array literal may cause a GC allocation");
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ if (!e.keys.dim)
+ return;
+ if (f.setGC())
+ {
+ e.error("associative array literal in `@nogc` %s `%s` may cause a GC allocation",
+ f.kind(), f.toPrettyChars());
+ err = true;
+ return;
+ }
+ f.printGCUsage(e.loc, "associative array literal may cause a GC allocation");
+ }
+
+ override void visit(NewExp e)
+ {
+ if (e.member && !e.member.isNogc() && f.setGC())
+ {
+ // @nogc-ness is already checked in NewExp::semantic
+ return;
+ }
+ if (e.onstack)
+ return;
+ if (global.params.ehnogc && e.thrownew)
+ return; // separate allocator is called for this, not the GC
+ if (f.setGC())
+ {
+ e.error("cannot use `new` in `@nogc` %s `%s`",
+ f.kind(), f.toPrettyChars());
+ err = true;
+ return;
+ }
+ f.printGCUsage(e.loc, "`new` causes a GC allocation");
+ }
+
+ override void visit(DeleteExp e)
+ {
+ if (e.e1.op == TOK.variable)
+ {
+ VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration();
+ if (v && v.onstack)
+ return; // delete for scope allocated class object
+ }
+
+ Type tb = e.e1.type.toBasetype();
+ AggregateDeclaration ad = null;
+ switch (tb.ty)
+ {
+ case Tclass:
+ ad = (cast(TypeClass)tb).sym;
+ break;
+
+ case Tpointer:
+ tb = (cast(TypePointer)tb).next.toBasetype();
+ if (tb.ty == Tstruct)
+ ad = (cast(TypeStruct)tb).sym;
+ break;
+
+ default:
+ break;
+ }
+
+ if (f.setGC())
+ {
+ e.error("cannot use `delete` in `@nogc` %s `%s`",
+ f.kind(), f.toPrettyChars());
+ err = true;
+ return;
+ }
+ f.printGCUsage(e.loc, "`delete` requires the GC");
+ }
+
+ override void visit(IndexExp e)
+ {
+ Type t1b = e.e1.type.toBasetype();
+ if (t1b.ty == Taarray)
+ {
+ if (f.setGC())
+ {
+ e.error("indexing an associative array in `@nogc` %s `%s` may cause a GC allocation",
+ f.kind(), f.toPrettyChars());
+ err = true;
+ return;
+ }
+ f.printGCUsage(e.loc, "indexing an associative array may cause a GC allocation");
+ }
+ }
+
+ override void visit(AssignExp e)
+ {
+ if (e.e1.op == TOK.arrayLength)
+ {
+ if (f.setGC())
+ {
+ e.error("setting `length` in `@nogc` %s `%s` may cause a GC allocation",
+ f.kind(), f.toPrettyChars());
+ err = true;
+ return;
+ }
+ f.printGCUsage(e.loc, "setting `length` may cause a GC allocation");
+ }
+ }
+
+ override void visit(CatAssignExp e)
+ {
+ if (f.setGC())
+ {
+ e.error("cannot use operator `~=` in `@nogc` %s `%s`",
+ f.kind(), f.toPrettyChars());
+ err = true;
+ return;
+ }
+ f.printGCUsage(e.loc, "operator `~=` may cause a GC allocation");
+ }
+
+ override void visit(CatExp e)
+ {
+ if (f.setGC())
+ {
+ e.error("cannot use operator `~` in `@nogc` %s `%s`",
+ f.kind(), f.toPrettyChars());
+ err = true;
+ return;
+ }
+ f.printGCUsage(e.loc, "operator `~` may cause a GC allocation");
+ }
+}
+
+Expression checkGC(Scope* sc, Expression e)
+{
+ FuncDeclaration f = sc.func;
+ if (e && e.op != TOK.error && f && sc.intypeof != 1 && !(sc.flags & SCOPE.ctfe) &&
+ (f.type.ty == Tfunction &&
+ (cast(TypeFunction)f.type).isnogc || (f.flags & FUNCFLAG.nogcInprocess) || global.params.vgc) &&
+ !(sc.flags & SCOPE.debug_))
+ {
+ scope NOGCVisitor gcv = new NOGCVisitor(f);
+ walkPostorder(e, gcv);
+ if (gcv.err)
+ return ErrorExp.get();
+ }
+ return e;
+}
+
+/**
+ * Removes `_d_HookTraceImpl` if found from `fd`.
+ * This is needed to be able to find hooks that are called though the hook's `*Trace` wrapper.
+ * Parameters:
+ * fd = The function declaration to remove `_d_HookTraceImpl` from
+ */
+private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd)
+{
+ import dmd.id : Id;
+ import dmd.dsymbol : Dsymbol;
+ import dmd.root.rootobject : RootObject, DYNCAST;
+
+ if (fd.ident != Id._d_HookTraceImpl)
+ return fd;
+
+ // Get the Hook from the second template parameter
+ auto templateInstance = fd.parent.isTemplateInstance;
+ RootObject hook = (*templateInstance.tiargs)[1];
+ assert(hook.dyncast() == DYNCAST.dsymbol, "Expected _d_HookTraceImpl's second template parameter to be an alias to the hook!");
+ return (cast(Dsymbol)hook).isFuncDeclaration;
+}
diff --git a/gcc/d/dmd/nspace.c b/gcc/d/dmd/nspace.c
deleted file mode 100644
index 95cfb6f0d34..00000000000
--- a/gcc/d/dmd/nspace.c
+++ /dev/null
@@ -1,164 +0,0 @@
-
-// Compiler implementation of the D programming language
-// Copyright: Copyright (C) 2014-2021 by The D Language Foundation, All Rights Reserved
-// Authors: Walter Bright, http://www.digitalmars.com
-// License: http://boost.org/LICENSE_1_0.txt
-// Source: https://github.com/D-Programming-Language/dmd/blob/master/src/nspace.c
-
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "dsymbol.h"
-#include "nspace.h"
-#include "identifier.h"
-#include "scope.h"
-
-/* This implements namespaces.
- */
-
-Nspace::Nspace(Loc loc, Identifier *ident, Dsymbols *members, bool mangleOnly)
- : ScopeDsymbol(ident)
-{
- //printf("Nspace::Nspace(ident = %s)\n", ident->toChars());
- this->loc = loc;
- this->members = members;
- // Determines whether the symbol for this namespace should be included in
- // the symbol table.
- this->mangleOnly = mangleOnly;
-}
-
-Dsymbol *Nspace::syntaxCopy(Dsymbol *)
-{
- Nspace *ns = new Nspace(loc, ident, NULL, mangleOnly);
- return ScopeDsymbol::syntaxCopy(ns);
-}
-
-void Nspace::addMember(Scope *sc, ScopeDsymbol *sds)
-{
- if (mangleOnly)
- parent = sds;
- else
- ScopeDsymbol::addMember(sc, sds);
- if (members)
- {
- if (!symtab)
- symtab = new DsymbolTable();
- // The namespace becomes 'imported' into the enclosing scope
- for (Scope *sce = sc; 1; sce = sce->enclosing)
- {
- ScopeDsymbol *sds2 = sce->scopesym;
- if (sds2)
- {
- sds2->importScope(this, Prot(Prot::public_));
- break;
- }
- }
- assert(sc);
- sc = sc->push(this);
- sc->linkage = LINKcpp; // namespaces default to C++ linkage
- sc->parent = this;
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- //printf("add %s to scope %s\n", s->toChars(), toChars());
- s->addMember(sc, this);
- }
- sc->pop();
- }
-}
-
-void Nspace::setScope(Scope *sc)
-{
- ScopeDsymbol::setScope(sc);
- if (members)
- {
- assert(sc);
- sc = sc->push(this);
- sc->linkage = LINKcpp; // namespaces default to C++ linkage
- sc->parent = this;
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- s->setScope(sc);
- }
- sc->pop();
- }
-}
-
-const char *Nspace::kind() const
-{
- return "namespace";
-}
-
-bool Nspace::oneMember(Dsymbol **ps, Identifier *ident)
-{
- return Dsymbol::oneMember(ps, ident);
-}
-
-Dsymbol *Nspace::search(const Loc &loc, Identifier *ident, int flags)
-{
- //printf("%s::Nspace::search('%s')\n", toChars(), ident->toChars());
- if (_scope && !symtab)
- dsymbolSemantic(this, _scope);
-
- if (!members || !symtab) // opaque or semantic() is not yet called
- {
- error("is forward referenced when looking for `%s`", ident->toChars());
- return NULL;
- }
-
- return ScopeDsymbol::search(loc, ident, flags);
-}
-
-int Nspace::apply(Dsymbol_apply_ft_t fp, void *param)
-{
- if (members)
- {
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- if (s)
- {
- if (s->apply(fp, param))
- return 1;
- }
- }
- }
- return 0;
-}
-
-bool Nspace::hasPointers()
-{
- //printf("Nspace::hasPointers() %s\n", toChars());
-
- if (members)
- {
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- //printf(" s = %s %s\n", s->kind(), s->toChars());
- if (s->hasPointers())
- {
- return true;
- }
- }
- }
- return false;
-}
-
-void Nspace::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion)
-{
- //printf("Nspace::setFieldOffset() %s\n", toChars());
- if (_scope) // if fwd reference
- dsymbolSemantic(this, NULL); // try to resolve it
- if (members)
- {
- for (size_t i = 0; i < members->length; i++)
- {
- Dsymbol *s = (*members)[i];
- //printf("\t%s\n", s->toChars());
- s->setFieldOffset(ad, poffset, isunion);
- }
- }
-}
diff --git a/gcc/d/dmd/nspace.d b/gcc/d/dmd/nspace.d
new file mode 100644
index 00000000000..215f2595888
--- /dev/null
+++ b/gcc/d/dmd/nspace.d
@@ -0,0 +1,170 @@
+/**
+ * A scoped C++ namespace symbol
+ *
+ * D supports the following syntax to declare symbol(s) as being part of a
+ * C++ namespace:
+ * ---
+ * extern (C++, "myNamespace") { /+ Symbols +/ } // String variant
+ * extern (C++, SomeNamespace) { /+ Other symbols +/ } // Identifier variant
+ * ---
+ * The first form is an attribute and only affects mangling, and is implemented
+ * in `dmd.attrib`.
+ * The second form introduces a named scope and allows symbols to be refered
+ * to with or without the namespace name, much like a named template mixin,
+ * and is implemented in this module.
+ * ---
+ * extern (C++, Basket)
+ * {
+ * struct StrawBerry;
+ * void swapFood (Strawberry* f1, Strawberry* f2);
+ * }
+ * void main ()
+ * {
+ * Basket.StrawBerry fruit1;
+ * StrawBerry fruit2;
+ * Basket.swapFood(fruit1, fruit2);
+ * swapFood(fruit1, fruit2);
+ * }
+ * ---
+ * Hence the `Nspace` symbol implements the usual `ScopeDsymbol` semantics.
+ *
+ * Note that it implies `extern(C++)` so it cannot be used as a generic
+ * named scope. Additionally, `Nspace` with the same `Identifier` can be
+ * defined in different module (as C++ allows a namespace to be spread accross
+ * translation units), but symbols in it should be considered
+ * part of the same scope. Lastly, not all possible C++ namespace names
+ * are valid D identifier.
+ *
+ * See_Also: https://github.com/dlang/dmd/pull/10031
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/nspace.d, _nspace.d)
+ * Documentation: https://dlang.org/phobos/dmd_nspace.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/nspace.d
+ */
+
+module dmd.nspace;
+
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.expression;
+import dmd.globals;
+import dmd.identifier;
+import dmd.visitor;
+import core.stdc.stdio;
+
+private enum LOG = false;
+
+/// Ditto
+extern (C++) final class Nspace : ScopeDsymbol
+{
+ /**
+ * Namespace identifier resolved during semantic.
+ */
+ Expression identExp;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Expression identExp, Dsymbols* members)
+ {
+ super(loc, ident);
+ //printf("Nspace::Nspace(ident = %s)\n", ident.toChars());
+ this.members = members;
+ this.identExp = identExp;
+ }
+
+ override Nspace syntaxCopy(Dsymbol s)
+ {
+ auto ns = new Nspace(loc, ident, identExp, null);
+ ScopeDsymbol.syntaxCopy(ns);
+ return ns;
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ ScopeDsymbol.addMember(sc, sds);
+
+ if (members)
+ {
+ if (!symtab)
+ symtab = new DsymbolTable();
+ // The namespace becomes 'imported' into the enclosing scope
+ for (Scope* sce = sc; 1; sce = sce.enclosing)
+ {
+ ScopeDsymbol sds2 = sce.scopesym;
+ if (sds2)
+ {
+ sds2.importScope(this, Visibility(Visibility.Kind.public_));
+ break;
+ }
+ }
+ assert(sc);
+ sc = sc.push(this);
+ sc.linkage = LINK.cpp; // namespaces default to C++ linkage
+ sc.parent = this;
+ members.foreachDsymbol(s => s.addMember(sc, this));
+ sc.pop();
+ }
+ }
+
+ override void setScope(Scope* sc)
+ {
+ ScopeDsymbol.setScope(sc);
+ if (members)
+ {
+ assert(sc);
+ sc = sc.push(this);
+ sc.linkage = LINK.cpp; // namespaces default to C++ linkage
+ sc.parent = this;
+ members.foreachDsymbol(s => s.setScope(sc));
+ sc.pop();
+ }
+ }
+
+ override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
+ {
+ //printf("%s.Nspace.search('%s')\n", toChars(), ident.toChars());
+ if (_scope && !symtab)
+ dsymbolSemantic(this, _scope);
+
+ if (!members || !symtab) // opaque or semantic() is not yet called
+ {
+ if (!(flags & IgnoreErrors))
+ error("is forward referenced when looking for `%s`", ident.toChars());
+ return null;
+ }
+
+ return ScopeDsymbol.search(loc, ident, flags);
+ }
+
+ override bool hasPointers()
+ {
+ //printf("Nspace::hasPointers() %s\n", toChars());
+ return members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
+ }
+
+ override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
+ {
+ //printf("Nspace::setFieldOffset() %s\n", toChars());
+ if (_scope) // if fwd reference
+ dsymbolSemantic(this, null); // try to resolve it
+ members.foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) );
+ }
+
+ override const(char)* kind() const
+ {
+ return "namespace";
+ }
+
+ override inout(Nspace) isNspace() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
diff --git a/gcc/d/dmd/nspace.h b/gcc/d/dmd/nspace.h
index 71dafb2553d..43d36e9e4e0 100644
--- a/gcc/d/dmd/nspace.h
+++ b/gcc/d/dmd/nspace.h
@@ -19,17 +19,13 @@
class Nspace : public ScopeDsymbol
{
public:
- bool mangleOnly;
- Nspace(Loc loc, Identifier *ident, Dsymbols *members, bool mangleOnly);
-
- Dsymbol *syntaxCopy(Dsymbol *s);
+ Expression *identExp;
+ Nspace *syntaxCopy(Dsymbol *s);
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope *sc);
- bool oneMember(Dsymbol **ps, Identifier *ident);
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
- int apply(Dsymbol_apply_ft_t fp, void *param);
bool hasPointers();
- void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
+ void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion);
const char *kind() const;
Nspace *isNspace() { return this; }
void accept(Visitor *v) { v->visit(this); }
diff --git a/gcc/d/dmd/ob.d b/gcc/d/dmd/ob.d
new file mode 100644
index 00000000000..7719ccfe297
--- /dev/null
+++ b/gcc/d/dmd/ob.d
@@ -0,0 +1,2680 @@
+/**
+ * Flow analysis for Ownership/Borrowing
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ob.d, _ob.d)
+ * Documentation: https://dlang.org/phobos/dmd_escape.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ob.d
+ */
+
+module dmd.ob;
+
+import core.stdc.stdio : printf;
+import core.stdc.stdlib;
+import core.stdc.string;
+
+import dmd.root.array;
+import dmd.root.rootobject;
+import dmd.root.rmem;
+
+import dmd.aggregate;
+import dmd.apply;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.foreachvar;
+import dmd.func;
+import dmd.globals;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.printast;
+import dmd.statement;
+import dmd.stmtstate;
+import dmd.tokens;
+import dmd.visitor;
+
+import dmd.root.bitarray;
+import dmd.root.outbuffer;
+
+/**********************************
+ * Perform ownership/borrowing checks for funcdecl.
+ * Does not modify the AST, just checks for errors.
+ */
+
+void oblive(FuncDeclaration funcdecl)
+{
+ //printf("oblive() %s\n", funcdecl.toChars());
+ //printf("fbody: %s\n", funcdecl.fbody.toChars());
+ ObState obstate;
+
+ /* Build the flow graph
+ */
+ setLabelStatementExtraFields(funcdecl.labtab);
+ toObNodes(obstate.nodes, funcdecl.fbody);
+ insertFinallyBlockCalls(obstate.nodes);
+ insertFinallyBlockGotos(obstate.nodes);
+ removeUnreachable(obstate.nodes);
+ computePreds(obstate.nodes);
+
+ numberNodes(obstate.nodes);
+ //foreach (ob; obstate.nodes) ob.print();
+
+ collectVars(funcdecl, obstate.vars);
+ allocStates(obstate);
+ doDataFlowAnalysis(obstate);
+
+ checkObErrors(obstate);
+}
+
+alias ObNodes = Array!(ObNode*);
+
+alias StmtState = dmd.stmtstate.StmtState!ObNode;
+
+/*******************************************
+ * Collect the state information.
+ */
+struct ObState
+{
+ ObNodes nodes;
+ VarDeclarations vars;
+
+ Array!size_t varStack; /// temporary storage
+ Array!bool mutableStack; /// parallel to varStack[], is type mutable?
+
+ PtrVarState[] varPool; /// memory pool
+
+ ~this()
+ {
+ mem.xfree(varPool.ptr);
+ }
+}
+
+/***********************************************
+ * A node in the function's expression graph, and its edges to predecessors and successors.
+ */
+struct ObNode
+{
+ Expression exp; /// expression for the node
+ ObNodes preds; /// predecessors
+ ObNodes succs; /// successors
+ ObNode* tryBlock; /// try-finally block we're inside
+ ObType obtype;
+ uint index; /// index of this in obnodes
+
+ PtrVarState[] gen; /// new states generated for this node
+ PtrVarState[] input; /// variable states on entry to exp
+ PtrVarState[] output; /// variable states on exit to exp
+
+ this(ObNode* tryBlock)
+ {
+ this.tryBlock = tryBlock;
+ }
+
+ void print()
+ {
+ printf("%d: %s %s\n", index, obtype.toString.ptr, exp ? exp.toChars() : "-");
+ printf(" preds: ");
+ foreach (ob; preds)
+ printf(" %d", ob.index);
+ printf("\n succs: ");
+ foreach (ob; succs)
+ printf(" %d", ob.index);
+ printf("\n\n");
+ }
+}
+
+
+enum ObType : ubyte
+{
+ goto_, /// goto one of the succs[]
+ return_, /// returns from function
+ retexp, /// returns expression from function
+ throw_, /// exits with throw
+ exit, /// exits program
+ try_,
+ finally_,
+ fend,
+}
+
+string toString(ObType obtype)
+{
+ return obtype == ObType.goto_ ? "goto " :
+ obtype == ObType.return_ ? "ret " :
+ obtype == ObType.retexp ? "retexp" :
+ obtype == ObType.throw_ ? "throw" :
+ obtype == ObType.exit ? "exit" :
+ obtype == ObType.try_ ? "try" :
+ obtype == ObType.finally_ ? "finally" :
+ obtype == ObType.fend ? "fend" :
+ "---";
+}
+
+/***********
+ Pointer variable states:
+
+ Initial state is not known; ignore for now
+
+ Undefined not in a usable state
+
+ T* p = void;
+
+ Owner mutable pointer
+
+ T* p = initializer;
+
+ Borrowed scope mutable pointer, borrowed from [p]
+
+ T* p = initializer;
+ scope T* b = p;
+
+ Readonly scope const pointer, copied from [p]
+
+ T* p = initializer;
+ scope const(T)* cp = p;
+
+ Examples:
+
+ T* p = initializer; // p is owner
+ T** pp = &p; // pp borrows from p
+
+ T* p = initialize; // p is owner
+ T* q = p; // transfer: q is owner, p is undefined
+ */
+
+enum PtrState : ubyte
+{
+ Initial, Undefined, Owner, Borrowed, Readonly
+}
+
+/************
+ */
+const(char)* toChars(PtrState state)
+{
+ return toString(state).ptr;
+}
+
+string toString(PtrState state)
+{
+ return ["Initial", "Undefined", "Owner", "Borrowed", "Readonly"][state];
+}
+
+/******
+ * Carries the state of a pointer variable.
+ */
+struct PtrVarState
+{
+ BitArray deps; /// dependencies
+ PtrState state; /// state the pointer variable is in
+
+ void opAssign(const ref PtrVarState pvs)
+ {
+ state = pvs.state;
+ deps = pvs.deps;
+ }
+
+ /* Combine `this` and `pvs` into `this`,
+ * on the idea that the `this` and the `pvs` paths
+ * are being merged
+ * Params:
+ * pvs = path to be merged with `this`
+ */
+ void combine(ref PtrVarState pvs, size_t vi, PtrVarState[] gen)
+ {
+ static uint X(PtrState x1, PtrState x2) { return x1 * (PtrState.max + 1) + x2; }
+
+ with (PtrState)
+ {
+ switch (X(state, pvs.state))
+ {
+ case X(Initial, Initial):
+ break;
+
+ case X(Initial, Owner ):
+ case X(Initial, Borrowed ):
+ case X(Initial, Readonly ):
+ // Transfer state to `this`
+ state = pvs.state;
+ deps = pvs.deps;
+ break;
+
+ case X(Owner, Initial):
+ case X(Borrowed, Initial):
+ case X(Readonly, Initial):
+ break;
+
+ case X(Undefined, Initial):
+ case X(Undefined, Undefined):
+ case X(Undefined, Owner ):
+ case X(Undefined, Borrowed ):
+ case X(Undefined, Readonly ):
+ break;
+
+ case X(Owner , Owner ):
+ break;
+
+ case X(Borrowed , Borrowed):
+ case X(Readonly , Readonly):
+ deps.or(pvs.deps);
+ break;
+
+ default:
+ makeUndefined(vi, gen);
+ break;
+ }
+ }
+ }
+
+ bool opEquals(const ref PtrVarState pvs) const
+ {
+ return state == pvs.state &&
+ deps == pvs.deps;
+ }
+
+ /***********************
+ */
+ void print(VarDeclaration[] vars)
+ {
+ string s = toString(state);
+ printf("%.*s [", cast(int)s.length, s.ptr);
+ assert(vars.length == deps.length);
+ OutBuffer buf;
+ depsToBuf(buf, vars);
+ auto t = buf[];
+ printf("%.*s]\n", cast(int)t.length, t.ptr);
+ }
+
+ /*****************************
+ * Produce a user-readable comma separated string of the
+ * dependencies.
+ * Params:
+ * buf = write resulting string here
+ * vars = array from which to get the variable names
+ */
+ void depsToBuf(ref OutBuffer buf, const VarDeclaration[] vars)
+ {
+ bool any = false;
+ foreach (i; 0 .. deps.length)
+ {
+ if (deps[i])
+ {
+ if (any)
+ buf.writestring(", ");
+ buf.writestring(vars[i].toString());
+ any = true;
+ }
+ }
+ }
+}
+
+
+/*****************************************
+ * Set the `.extra` field for LabelStatements in labtab[].
+ */
+void setLabelStatementExtraFields(DsymbolTable labtab)
+{
+ if (labtab)
+ foreach (keyValue; labtab.tab.asRange)
+ {
+ //printf(" KV: %s = %s\n", keyValue.key.toChars(), keyValue.value.toChars());
+ auto label = cast(LabelDsymbol)keyValue.value;
+ if (label.statement)
+ label.statement.extra = cast(void*) new ObNode(null);
+ }
+}
+
+/*****************************************
+ * Convert statement into ObNodes.
+ */
+
+void toObNodes(ref ObNodes obnodes, Statement s)
+{
+ ObNode* curblock = new ObNode(null);
+ obnodes.push(curblock);
+
+ void visit(Statement s, StmtState* stmtstate)
+ {
+ if (!s)
+ return;
+
+ ObNode* newNode()
+ {
+ return new ObNode(stmtstate.tryBlock);
+ }
+
+ ObNode* nextNodeIs(ObNode* ob)
+ {
+ obnodes.push(ob);
+ curblock = ob;
+ return ob;
+ }
+
+ ObNode* nextNode()
+ {
+ return nextNodeIs(newNode());
+ }
+
+ ObNode* gotoNextNodeIs(ObNode* ob)
+ {
+ obnodes.push(ob);
+ curblock.succs.push(ob);
+ curblock = ob;
+ return ob;
+ }
+
+ // block_goto(blx, BCgoto, null)
+ ObNode* gotoNextNode()
+ {
+ return gotoNextNodeIs(newNode());
+ }
+
+ /***
+ * Doing a goto to dest
+ */
+ ObNode* gotoDest(ObNode* dest)
+ {
+ curblock.succs.push(dest);
+ return nextNode();
+ }
+
+ void visitExp(ExpStatement s)
+ {
+ curblock.obtype = ObType.goto_;
+ curblock.exp = s.exp;
+ gotoNextNode();
+ }
+
+ void visitDtorExp(DtorExpStatement s)
+ {
+ visitExp(s);
+ }
+
+ void visitCompound(CompoundStatement s)
+ {
+ if (s.statements)
+ {
+ foreach (s2; *s.statements)
+ {
+ visit(s2, stmtstate);
+ }
+ }
+ }
+
+ void visitCompoundDeclaration(CompoundDeclarationStatement s)
+ {
+ visitCompound(s);
+ }
+
+ void visitUnrolledLoop(UnrolledLoopStatement s)
+ {
+ StmtState mystate = StmtState(stmtstate, s);
+ mystate.breakBlock = newNode();
+
+ gotoNextNode();
+
+ foreach (s2; *s.statements)
+ {
+ if (s2)
+ {
+ mystate.contBlock = newNode();
+
+ visit(s2, &mystate);
+
+ gotoNextNodeIs(mystate.contBlock);
+ }
+ }
+
+ gotoNextNodeIs(mystate.breakBlock);
+ }
+
+ void visitScope(ScopeStatement s)
+ {
+ if (s.statement)
+ {
+ StmtState mystate = StmtState(stmtstate, s);
+
+ if (mystate.prev.ident)
+ mystate.ident = mystate.prev.ident;
+
+ visit(s.statement, &mystate);
+
+ if (mystate.breakBlock)
+ gotoNextNodeIs(mystate.breakBlock);
+ }
+ }
+
+ void visitDo(DoStatement s)
+ {
+ StmtState mystate = StmtState(stmtstate, s);
+ mystate.breakBlock = newNode();
+ mystate.contBlock = newNode();
+
+ auto bpre = curblock;
+
+ auto ob = newNode();
+ obnodes.push(ob);
+ curblock.succs.push(ob);
+ curblock = ob;
+ bpre.succs.push(curblock);
+
+ mystate.contBlock.succs.push(curblock);
+ mystate.contBlock.succs.push(mystate.breakBlock);
+
+ visit(s._body, &mystate);
+
+ gotoNextNodeIs(mystate.contBlock);
+ mystate.contBlock.exp = s.condition;
+ nextNodeIs(mystate.breakBlock);
+ }
+
+ void visitFor(ForStatement s)
+ {
+ //printf("visit(ForStatement)) %u..%u\n", s.loc.linnum, s.endloc.linnum);
+ StmtState mystate = StmtState(stmtstate, s);
+ mystate.breakBlock = newNode();
+ mystate.contBlock = newNode();
+
+ visit(s._init, &mystate);
+
+ auto bcond = gotoNextNode();
+ mystate.contBlock.succs.push(bcond);
+
+ if (s.condition)
+ {
+ bcond.exp = s.condition;
+ auto ob = newNode();
+ obnodes.push(ob);
+ bcond.succs.push(ob);
+ bcond.succs.push(mystate.breakBlock);
+ curblock = ob;
+ }
+ else
+ { /* No conditional, it's a straight goto
+ */
+ bcond.exp = s.condition;
+ bcond.succs.push(nextNode());
+ }
+
+ visit(s._body, &mystate);
+ /* End of the body goes to the continue block
+ */
+ curblock.succs.push(mystate.contBlock);
+ nextNodeIs(mystate.contBlock);
+
+ if (s.increment)
+ curblock.exp = s.increment;
+
+ /* The 'break' block follows the for statement.
+ */
+ nextNodeIs(mystate.breakBlock);
+ }
+
+ void visitIf(IfStatement s)
+ {
+ StmtState mystate = StmtState(stmtstate, s);
+
+ // bexit is the block that gets control after this IfStatement is done
+ auto bexit = mystate.breakBlock ? mystate.breakBlock : newNode();
+
+ curblock.exp = s.condition;
+
+ auto bcond = curblock;
+ gotoNextNode();
+
+ visit(s.ifbody, &mystate);
+ curblock.succs.push(bexit);
+
+ if (s.elsebody)
+ {
+ bcond.succs.push(nextNode());
+
+ visit(s.elsebody, &mystate);
+
+ gotoNextNodeIs(bexit);
+ }
+ else
+ {
+ bcond.succs.push(bexit);
+ nextNodeIs(bexit);
+ }
+ }
+
+ void visitSwitch(SwitchStatement s)
+ {
+ StmtState mystate = StmtState(stmtstate, s);
+
+ mystate.switchBlock = curblock;
+
+ /* Block for where "break" goes to
+ */
+ mystate.breakBlock = newNode();
+
+ /* Block for where "default" goes to.
+ * If there is a default statement, then that is where default goes.
+ * If not, then do:
+ * default: break;
+ * by making the default block the same as the break block.
+ */
+ mystate.defaultBlock = s.sdefault ? newNode() : mystate.breakBlock;
+
+ const numcases = s.cases ? s.cases.dim : 0;
+
+ /* allocate a block for each case
+ */
+ if (numcases)
+ foreach (cs; *s.cases)
+ {
+ cs.extra = cast(void*)newNode();
+ }
+
+ curblock.exp = s.condition;
+
+ if (s.hasVars)
+ { /* Generate a sequence of if-then-else blocks for the cases.
+ */
+ if (numcases)
+ foreach (cs; *s.cases)
+ {
+ auto ecase = newNode();
+ obnodes.push(ecase);
+ ecase.exp = cs.exp;
+ curblock.succs.push(ecase);
+
+ auto cn = cast(ObNode*)cs.extra;
+ ecase.succs.push(cn);
+ ecase.succs.push(nextNode());
+ }
+
+ /* The final 'else' clause goes to the default
+ */
+ curblock.succs.push(mystate.defaultBlock);
+ nextNode();
+
+ visit(s._body, &mystate);
+
+ /* Have the end of the switch body fall through to the block
+ * following the switch statement.
+ */
+ gotoNextNodeIs(mystate.breakBlock);
+ return;
+ }
+
+ auto ob = newNode();
+ obnodes.push(ob);
+ curblock = ob;
+
+ mystate.switchBlock.succs.push(mystate.defaultBlock);
+
+ visit(s._body, &mystate);
+
+ /* Have the end of the switch body fall through to the block
+ * following the switch statement.
+ */
+ gotoNextNodeIs(mystate.breakBlock);
+ }
+
+ void visitCase(CaseStatement s)
+ {
+ auto cb = cast(ObNode*)s.extra;
+ cb.tryBlock = stmtstate.tryBlock;
+ auto bsw = stmtstate.getSwitchBlock();
+ bsw.succs.push(cb);
+ gotoNextNodeIs(cb);
+
+ visit(s.statement, stmtstate);
+ }
+
+ void visitDefault(DefaultStatement s)
+ {
+ auto bdefault = stmtstate.getDefaultBlock;
+ bdefault.tryBlock = stmtstate.tryBlock;
+ gotoNextNodeIs(bdefault);
+ visit(s.statement, stmtstate);
+ }
+
+ void visitGotoDefault(GotoDefaultStatement s)
+ {
+ gotoDest(stmtstate.getDefaultBlock);
+ }
+
+ void visitGotoCase(GotoCaseStatement s)
+ {
+ gotoDest(cast(ObNode*)s.cs.extra);
+ }
+
+ void visitSwitchError(SwitchErrorStatement s)
+ {
+ curblock.obtype = ObType.throw_;
+ curblock.exp = s.exp;
+
+ nextNode();
+ }
+
+ void visitReturn(ReturnStatement s)
+ {
+ //printf("visitReturn() %s\n", s.toChars());
+ curblock.obtype = s.exp && s.exp.type.toBasetype().ty != Tvoid
+ ? ObType.retexp
+ : ObType.return_;
+ curblock.exp = s.exp;
+
+ nextNode();
+ }
+
+ void visitBreak(BreakStatement s)
+ {
+ gotoDest(stmtstate.getBreakBlock(s.ident));
+ }
+
+ void visitContinue(ContinueStatement s)
+ {
+ gotoDest(stmtstate.getContBlock(s.ident));
+ }
+
+ void visitWith(WithStatement s)
+ {
+ visit(s._body, stmtstate);
+ }
+
+ void visitTryCatch(TryCatchStatement s)
+ {
+ /* tryblock
+ * body
+ * breakBlock
+ * catches
+ * breakBlock2
+ */
+
+ StmtState mystate = StmtState(stmtstate, s);
+ mystate.breakBlock = newNode();
+
+ auto tryblock = gotoNextNode();
+
+ visit(s._body, &mystate);
+
+ gotoNextNodeIs(mystate.breakBlock);
+
+ // create new break block that follows all the catches
+ auto breakBlock2 = newNode();
+
+ gotoDest(breakBlock2);
+
+ foreach (cs; *s.catches)
+ {
+ /* Each catch block is a successor to tryblock
+ * and the last block of try body
+ */
+ StmtState catchState = StmtState(stmtstate, s);
+
+ auto bcatch = curblock;
+ tryblock.succs.push(bcatch);
+ mystate.breakBlock.succs.push(bcatch);
+
+ nextNode();
+
+ visit(cs.handler, &catchState);
+
+ gotoDest(breakBlock2);
+ }
+
+ curblock.succs.push(breakBlock2);
+ obnodes.push(breakBlock2);
+ curblock = breakBlock2;
+ }
+
+ void visitTryFinally(TryFinallyStatement s)
+ {
+ /* Build this:
+ * 1 goto [2]
+ * 2 try_ [3] [5] [7]
+ * 3 body
+ * 4 goto [8]
+ * 5 finally_ [6]
+ * 6 finalbody
+ * 7 fend [8]
+ * 8 lastblock
+ */
+
+ StmtState bodyState = StmtState(stmtstate, s);
+
+ auto b2 = gotoNextNode();
+ b2.obtype = ObType.try_;
+ bodyState.tryBlock = b2;
+
+ gotoNextNode();
+
+ visit(s._body, &bodyState);
+
+ auto b4 = gotoNextNode();
+
+ auto b5 = newNode();
+ b5.obtype = ObType.finally_;
+ nextNodeIs(b5);
+ gotoNextNode();
+
+ StmtState finallyState = StmtState(stmtstate, s);
+ visit(s.finalbody, &finallyState);
+
+ auto b7 = gotoNextNode();
+ b7.obtype = ObType.fend;
+
+ auto b8 = gotoNextNode();
+
+ b2.succs.push(b5);
+ b2.succs.push(b7);
+
+ b4.succs.push(b8);
+ }
+
+ void visitThrow(ThrowStatement s)
+ {
+ curblock.obtype = ObType.throw_;
+ curblock.exp = s.exp;
+ nextNode();
+ }
+
+ void visitGoto(GotoStatement s)
+ {
+ gotoDest(cast(ObNode*)s.label.statement.extra);
+ }
+
+ void visitLabel(LabelStatement s)
+ {
+ StmtState mystate = StmtState(stmtstate, s);
+ mystate.ident = s.ident;
+
+ auto ob = cast(ObNode*)s.extra;
+ ob.tryBlock = mystate.tryBlock;
+ visit(s.statement, &mystate);
+ }
+
+ final switch (s.stmt)
+ {
+ case STMT.Exp: visitExp(s.isExpStatement()); break;
+ case STMT.DtorExp: visitDtorExp(s.isDtorExpStatement()); break;
+ case STMT.Compound: visitCompound(s.isCompoundStatement()); break;
+ case STMT.CompoundDeclaration: visitCompoundDeclaration(s.isCompoundDeclarationStatement()); break;
+ case STMT.UnrolledLoop: visitUnrolledLoop(s.isUnrolledLoopStatement()); break;
+ case STMT.Scope: visitScope(s.isScopeStatement()); break;
+ case STMT.Do: visitDo(s.isDoStatement()); break;
+ case STMT.For: visitFor(s.isForStatement()); break;
+ case STMT.If: visitIf(s.isIfStatement()); break;
+ case STMT.Switch: visitSwitch(s.isSwitchStatement()); break;
+ case STMT.Case: visitCase(s.isCaseStatement()); break;
+ case STMT.Default: visitDefault(s.isDefaultStatement()); break;
+ case STMT.GotoDefault: visitGotoDefault(s.isGotoDefaultStatement()); break;
+ case STMT.GotoCase: visitGotoCase(s.isGotoCaseStatement()); break;
+ case STMT.SwitchError: visitSwitchError(s.isSwitchErrorStatement()); break;
+ case STMT.Return: visitReturn(s.isReturnStatement()); break;
+ case STMT.Break: visitBreak(s.isBreakStatement()); break;
+ case STMT.Continue: visitContinue(s.isContinueStatement()); break;
+ case STMT.With: visitWith(s.isWithStatement()); break;
+ case STMT.TryCatch: visitTryCatch(s.isTryCatchStatement()); break;
+ case STMT.TryFinally: visitTryFinally(s.isTryFinallyStatement()); break;
+ case STMT.Throw: visitThrow(s.isThrowStatement()); break;
+ case STMT.Goto: visitGoto(s.isGotoStatement()); break;
+ case STMT.Label: visitLabel(s.isLabelStatement()); break;
+
+ case STMT.CompoundAsm:
+ case STMT.Asm:
+ case STMT.InlineAsm:
+ case STMT.GccAsm:
+
+ case STMT.Pragma:
+ case STMT.Import:
+ case STMT.ScopeGuard:
+ case STMT.Error:
+ break; // ignore these
+
+ case STMT.Foreach:
+ case STMT.ForeachRange:
+ case STMT.Debug:
+ case STMT.CaseRange:
+ case STMT.StaticForeach:
+ case STMT.StaticAssert:
+ case STMT.Conditional:
+ case STMT.While:
+ case STMT.Forwarding:
+ case STMT.Compile:
+ case STMT.Peel:
+ case STMT.Synchronized:
+ debug printf("s: %s\n", s.toChars());
+ assert(0); // should have been rewritten
+ }
+ }
+
+ StmtState stmtstate;
+ visit(s, &stmtstate);
+ curblock.obtype = ObType.return_;
+
+ static if (0)
+ {
+ printf("toObNodes()\n");
+ printf("------- before ----------\n");
+ numberNodes(obnodes);
+ foreach (ob; obnodes) ob.print();
+ printf("-------------------------\n");
+ }
+
+ assert(stmtstate.breakBlock is null);
+ assert(stmtstate.contBlock is null);
+ assert(stmtstate.switchBlock is null);
+ assert(stmtstate.defaultBlock is null);
+ assert(stmtstate.tryBlock is null);
+}
+
+/***************************************************
+ * Insert finally block calls when doing a goto from
+ * inside a try block to outside.
+ * Done after blocks are generated because then we know all
+ * the edges of the graph, but before the pred's are computed.
+ * Params:
+ * obnodes = graph of the function
+ */
+
+void insertFinallyBlockCalls(ref ObNodes obnodes)
+{
+ ObNode* bcret = null;
+ ObNode* bcretexp = null;
+
+ enum log = false;
+
+ static if (log)
+ {
+ printf("insertFinallyBlockCalls()\n");
+ printf("------- before ----------\n");
+ numberNodes(obnodes);
+ foreach (ob; obnodes) ob.print();
+ printf("-------------------------\n");
+ }
+
+ foreach (ob; obnodes)
+ {
+ if (!ob.tryBlock)
+ continue;
+
+ switch (ob.obtype)
+ {
+ case ObType.return_:
+ // Rewrite into a ObType.goto_ => ObType.return_
+ if (!bcret)
+ {
+ bcret = new ObNode();
+ bcret.obtype = ob.obtype;
+ }
+ ob.obtype = ObType.goto_;
+ ob.succs.push(bcret);
+ goto case_goto;
+
+ case ObType.retexp:
+ // Rewrite into a ObType.goto_ => ObType.retexp
+ if (!bcretexp)
+ {
+ bcretexp = new ObNode();
+ bcretexp.obtype = ob.obtype;
+ }
+ ob.obtype = ObType.goto_;
+ ob.succs.push(bcretexp);
+ goto case_goto;
+
+ case ObType.goto_:
+ if (ob.succs.length != 1)
+ break;
+
+ case_goto:
+ {
+ auto target = ob.succs[0]; // destination of goto
+ ob.succs.setDim(0);
+ auto lasttry = target.tryBlock;
+ auto blast = ob;
+ for (auto bt = ob.tryBlock; bt != lasttry; bt = bt.tryBlock)
+ {
+ assert(bt.obtype == ObType.try_);
+ auto bf = bt.succs[1];
+ assert(bf.obtype == ObType.finally_);
+ auto bfend = bt.succs[2];
+ assert(bfend.obtype == ObType.fend);
+
+ if (!blast.succs.contains(bf.succs[0]))
+ blast.succs.push(bf.succs[0]);
+
+ blast = bfend;
+ }
+ if (!blast.succs.contains(target))
+ blast.succs.push(target);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ if (bcret)
+ obnodes.push(bcret);
+ if (bcretexp)
+ obnodes.push(bcretexp);
+
+ static if (log)
+ {
+ printf("------- after ----------\n");
+ numberNodes(obnodes);
+ foreach (ob; obnodes) ob.print();
+ printf("-------------------------\n");
+ }
+}
+
+/***************************************************
+ * Remove try-finally scaffolding.
+ * Params:
+ * obnodes = nodes for the function
+ */
+
+void insertFinallyBlockGotos(ref ObNodes obnodes)
+{
+ /* Remove all the try_, finally_, lpad and ret nodes.
+ * Actually, just make them into no-ops.
+ */
+ foreach (ob; obnodes)
+ {
+ ob.tryBlock = null;
+ switch (ob.obtype)
+ {
+ case ObType.try_:
+ ob.obtype = ObType.goto_;
+ ob.succs.remove(2); // remove fend
+ ob.succs.remove(1); // remove finally_
+ break;
+
+ case ObType.finally_:
+ ob.obtype = ObType.goto_;
+ break;
+
+ case ObType.fend:
+ ob.obtype = ObType.goto_;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/*********************************
+ * Set the `index` field of each ObNode
+ * to its index in the `obnodes[]` array.
+ */
+void numberNodes(ref ObNodes obnodes)
+{
+ //printf("numberNodes()\n");
+ foreach (i, ob; obnodes)
+ {
+ //printf("ob = %d, %p\n", i, ob);
+ ob.index = cast(uint)i;
+ }
+
+ // Verify that nodes do not appear more than once in obnodes[]
+ debug
+ foreach (i, ob; obnodes)
+ {
+ assert(ob.index == cast(uint)i);
+ }
+}
+
+
+/*********************************
+ * Remove unreachable nodes and compress
+ * them out of obnodes[].
+ * Params:
+ * obnodes = array of nodes
+ */
+void removeUnreachable(ref ObNodes obnodes)
+{
+ if (!obnodes.length)
+ return;
+
+ /* Mark all nodes as unreachable,
+ * temporarilly reusing ObNode.index
+ */
+ foreach (ob; obnodes)
+ ob.index = 0;
+
+ /* Recursively mark ob and all its successors as reachable
+ */
+ static void mark(ObNode* ob)
+ {
+ ob.index = 1;
+ foreach (succ; ob.succs)
+ {
+ if (!succ.index)
+ mark(succ);
+ }
+ }
+
+ mark(obnodes[0]); // first node is entry point
+
+ /* Remove unreachable nodes by shifting the remainder left
+ */
+ size_t j = 1;
+ foreach (i; 1 .. obnodes.length)
+ {
+ if (obnodes[i].index)
+ {
+ if (i != j)
+ obnodes[j] = obnodes[i];
+ ++j;
+ }
+ else
+ {
+ obnodes[i].destroy();
+ }
+ }
+ obnodes.setDim(j);
+}
+
+
+
+/*************************************
+ * Compute predecessors.
+ */
+void computePreds(ref ObNodes obnodes)
+{
+ foreach (ob; obnodes)
+ {
+ foreach (succ; ob.succs)
+ {
+ succ.preds.push(ob);
+ }
+ }
+}
+
+/*******************************
+ * Are we interested in tracking variable `v`?
+ */
+bool isTrackableVar(VarDeclaration v)
+{
+ //printf("isTrackableVar() %s\n", v.toChars());
+ auto tb = v.type.toBasetype();
+
+ /* Assume class references are managed by the GC,
+ * don't need to track them
+ */
+ if (tb.ty == Tclass)
+ return false;
+
+ /* Assume types with a destructor are doing their own tracking,
+ * such as being a ref counted type
+ */
+ if (v.needsScopeDtor())
+ return false;
+
+ /* Not tracking function parameters that are not mutable
+ */
+ if (v.storage_class & STC.parameter && !tb.hasPointersToMutableFields())
+ return false;
+
+ /* Not tracking global variables
+ */
+ return !v.isDataseg();
+}
+
+/*******************************
+ * Are we interested in tracking this expression?
+ * Returns:
+ * variable if so, null if not
+ */
+VarDeclaration isTrackableVarExp(Expression e)
+{
+ if (auto ve = e.isVarExp())
+ {
+ if (auto v = ve.var.isVarDeclaration())
+ if (isTrackableVar(v))
+ return v;
+ }
+ return null;
+}
+
+
+/**************
+ * Find the pointer variable declarations in this function,
+ * and fill `vars` with them.
+ * Params:
+ * funcdecl = function we are in
+ * vars = array to fill in
+ */
+void collectVars(FuncDeclaration funcdecl, out VarDeclarations vars)
+{
+ enum log = false;
+ if (log)
+ printf("----------------collectVars()---------------\n");
+
+ if (funcdecl.parameters)
+ foreach (v; (*funcdecl.parameters)[])
+ {
+ if (isTrackableVar(v))
+ vars.push(v);
+ }
+
+ void dgVar(VarDeclaration v)
+ {
+ if (isTrackableVar(v))
+ vars.push(v);
+ }
+
+ void dgExp(Expression e)
+ {
+ foreachVar(e, &dgVar);
+ }
+
+ foreachExpAndVar(funcdecl.fbody, &dgExp, &dgVar);
+
+ static if (log)
+ {
+ foreach (i, v; vars[])
+ {
+ printf("vars[%d] = %s\n", cast(int)i, v.toChars());
+ }
+ }
+}
+
+/***********************************
+ * Allocate BitArrays in PtrVarState.
+ * Can be allocated much more efficiently by subdividing a single
+ * large array of bits
+ */
+void allocDeps(PtrVarState[] pvss)
+{
+ //printf("allocDeps()\n");
+ foreach (ref pvs; pvss)
+ {
+ pvs.deps.length = pvss.length;
+ }
+}
+
+
+/**************************************
+ * Allocate state variables foreach node.
+ */
+void allocStates(ref ObState obstate)
+{
+ //printf("---------------allocStates()------------------\n");
+ const vlen = obstate.vars.length;
+ PtrVarState* p = cast(PtrVarState*) mem.xcalloc(obstate.nodes.length * 3 * vlen, PtrVarState.sizeof);
+ obstate.varPool = p[0 .. obstate.nodes.length * 3 * vlen];
+ foreach (i, ob; obstate.nodes)
+ {
+ //printf(" [%d]\n", cast(int)i);
+// ob.kill.length = obstate.vars.length;
+// ob.comb.length = obstate.vars.length;
+ ob.gen = p[0 .. vlen]; p += vlen;
+ ob.input = p[0 .. vlen]; p += vlen;
+ ob.output = p[0 .. vlen]; p += vlen;
+
+ allocDeps(ob.gen);
+ allocDeps(ob.input);
+ allocDeps(ob.output);
+ }
+}
+
+/******************************
+ * Does v meet the definiton of a `Borrowed` pointer?
+ * Returns:
+ * true if it does
+ */
+bool isBorrowedPtr(VarDeclaration v)
+{
+ return v.isScope() && !v.isowner && v.type.nextOf().isMutable();
+}
+
+/******************************
+ * Does v meet the definiton of a `Readonly` pointer?
+ * Returns:
+ * true if it does
+ */
+bool isReadonlyPtr(VarDeclaration v)
+{
+ return v.isScope() && !v.type.nextOf().isMutable();
+}
+
+/***************************************
+ * Compute the gen vector for ob.
+ */
+void genKill(ref ObState obstate, ObNode* ob)
+{
+ enum log = false;
+ if (log)
+ printf("-----------computeGenKill()-----------\n");
+
+ /***************
+ * Assigning result of expression `e` to variable `v`.
+ */
+ void dgWriteVar(ObNode* ob, VarDeclaration v, Expression e, bool initializer)
+ {
+ if (log)
+ printf("dgWriteVar() %s := %s %d\n", v.toChars(), e.toChars(), initializer);
+ const vi = obstate.vars.find(v);
+ assert(vi != size_t.max);
+ PtrVarState* pvs = &ob.gen[vi];
+ readVar(ob, vi, true, ob.gen);
+ if (e)
+ {
+ if (isBorrowedPtr(v))
+ pvs.state = PtrState.Borrowed;
+ else if (isReadonlyPtr(v))
+ pvs.state = PtrState.Readonly;
+ else
+ pvs.state = PtrState.Owner;
+ pvs.deps.zero();
+
+ EscapeByResults er;
+ escapeByValue(e, &er, true);
+ bool any = false; // if any variables are assigned to v
+
+ void by(VarDeclaration r)
+ {
+ const ri = obstate.vars.find(r);
+ if (ri != size_t.max && ri != vi)
+ {
+ pvs.deps[ri] = true; // v took from r
+ auto pvsr = &ob.gen[ri];
+ any = true;
+
+ if (isBorrowedPtr(v))
+ {
+ // v is borrowing from r
+ pvs.state = PtrState.Borrowed;
+ }
+ else if (isReadonlyPtr(v))
+ {
+ pvs.state = PtrState.Readonly;
+ }
+ else
+ {
+ // move r to v, which "consumes" r
+ pvsr.state = PtrState.Undefined;
+ pvsr.deps.zero();
+ }
+ }
+ }
+
+ foreach (VarDeclaration v2; er.byvalue)
+ by(v2);
+ foreach (VarDeclaration v2; er.byref)
+ by(v2);
+
+ /* Make v an Owner for initializations like:
+ * scope v = malloc();
+ */
+ if (initializer && !any && isBorrowedPtr(v))
+ {
+ v.isowner = true;
+ pvs.state = PtrState.Owner;
+ }
+ }
+ else
+ {
+ if (isBorrowedPtr(v))
+ pvs.state = PtrState.Borrowed;
+ else if (isReadonlyPtr(v))
+ pvs.state = PtrState.Readonly;
+ else
+ pvs.state = PtrState.Owner;
+ pvs.deps.zero();
+ }
+ }
+
+ void dgReadVar(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable)
+ {
+ if (log)
+ printf("dgReadVar() %s %d\n", v.toChars(), mutable);
+ const vi = obstate.vars.find(v);
+ assert(vi != size_t.max);
+ readVar(ob, vi, mutable, ob.gen);
+ }
+
+ void foreachExp(ObNode* ob, Expression e)
+ {
+ extern (C++) final class ExpWalker : Visitor
+ {
+ alias visit = typeof(super).visit;
+ extern (D) void delegate(ObNode*, VarDeclaration, Expression, bool) dgWriteVar;
+ extern (D) void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar;
+ ObNode* ob;
+ ObState* obstate;
+
+ extern (D) this(void delegate(ObNode*, VarDeclaration, Expression, bool) dgWriteVar,
+ void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar,
+ ObNode* ob, ref ObState obstate)
+ {
+ this.dgWriteVar = dgWriteVar;
+ this.dgReadVar = dgReadVar;
+ this.ob = ob;
+ this.obstate = &obstate;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("[%s] %s: %s\n", e.loc.toChars(), Token.toChars(e.op), e.toChars());
+ //assert(0);
+ }
+
+ void visitAssign(AssignExp ae, bool initializer)
+ {
+ ae.e2.accept(this);
+ if (auto ve = ae.e1.isVarExp())
+ {
+ if (auto v = ve.var.isVarDeclaration())
+ if (isTrackableVar(v))
+ dgWriteVar(ob, v, ae.e2, initializer);
+ }
+ else
+ ae.e1.accept(this);
+ }
+
+ override void visit(AssignExp ae)
+ {
+ visitAssign(ae, false);
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ void Dsymbol_visit(Dsymbol s)
+ {
+ if (auto vd = s.isVarDeclaration())
+ {
+ s = s.toAlias();
+ if (s != vd)
+ return Dsymbol_visit(s);
+ if (!isTrackableVar(vd))
+ return;
+
+ if (!(vd._init && vd._init.isVoidInitializer()))
+ {
+ auto ei = vd._init ? vd._init.isExpInitializer() : null;
+ if (ei)
+ visitAssign(cast(AssignExp)ei.exp, true);
+ else
+ dgWriteVar(ob, vd, null, false);
+ }
+ }
+ else if (auto td = s.isTupleDeclaration())
+ {
+ foreach (o; *td.objects)
+ {
+ if (auto eo = o.isExpression())
+ {
+ if (auto se = eo.isDsymbolExp())
+ {
+ Dsymbol_visit(se.s);
+ }
+ }
+ }
+ }
+ }
+
+ Dsymbol_visit(e.declaration);
+ }
+
+ override void visit(VarExp ve)
+ {
+ //printf("VarExp: %s\n", ve.toChars());
+ if (auto v = ve.var.isVarDeclaration())
+ if (isTrackableVar(v))
+ {
+ dgReadVar(ve.loc, ob, v, isMutableRef(ve.type));
+ }
+ }
+
+ override void visit(CallExp ce)
+ {
+ //printf("CallExp() %s\n", ce.toChars());
+ ce.e1.accept(this);
+ auto t = ce.e1.type.toBasetype();
+ auto tf = t.isTypeFunction();
+ if (!tf)
+ {
+ assert(t.ty == Tdelegate);
+ tf = t.nextOf().isTypeFunction();
+ assert(tf);
+ }
+
+ // j=1 if _arguments[] is first argument
+ const int j = tf.isDstyleVariadic();
+ bool hasOut;
+ const varStackSave = obstate.varStack.length;
+
+ foreach (const i, arg; (*ce.arguments)[])
+ {
+ if (i - j < tf.parameterList.length &&
+ i >= j)
+ {
+ Parameter p = tf.parameterList[i - j];
+ auto pt = p.type.toBasetype();
+
+ EscapeByResults er;
+ escapeByValue(arg, &er, true);
+
+ if (!(p.storageClass & STC.out_ && arg.isVarExp()))
+ arg.accept(this);
+
+ void by(VarDeclaration v)
+ {
+ if (!isTrackableVar(v))
+ return;
+
+ const vi = obstate.vars.find(v);
+ if (vi == size_t.max)
+ return;
+
+ if (p.storageClass & STC.out_)
+ {
+ /// initialize
+ hasOut = true;
+ makeUndefined(vi, ob.gen);
+ }
+ else if (p.storageClass & STC.scope_)
+ {
+ // borrow
+ obstate.varStack.push(vi);
+ obstate.mutableStack.push(isMutableRef(pt));
+ }
+ else
+ {
+ // move (i.e. consume arg)
+ makeUndefined(vi, ob.gen);
+ }
+ }
+
+ foreach (VarDeclaration v2; er.byvalue)
+ by(v2);
+ foreach (VarDeclaration v2; er.byref)
+ by(v2);
+ }
+ else // variadic args
+ {
+ arg.accept(this);
+
+ EscapeByResults er;
+ escapeByValue(arg, &er, true);
+
+ void byv(VarDeclaration v)
+ {
+ if (!isTrackableVar(v))
+ return;
+
+ const vi = obstate.vars.find(v);
+ if (vi == size_t.max)
+ return;
+
+ if (tf.parameterList.stc & STC.scope_)
+ {
+ // borrow
+ obstate.varStack.push(vi);
+ obstate.mutableStack.push(isMutableRef(arg.type) &&
+ !(tf.parameterList.stc & (STC.const_ | STC.immutable_)));
+ }
+ else
+ // move (i.e. consume arg)
+ makeUndefined(vi, ob.gen);
+ }
+
+ foreach (VarDeclaration v2; er.byvalue)
+ byv(v2);
+ foreach (VarDeclaration v2; er.byref)
+ byv(v2);
+ }
+ }
+
+ /* Do a dummy 'read' of each variable passed to the function,
+ * to detect O/B errors
+ */
+ assert(obstate.varStack.length == obstate.mutableStack.length);
+ foreach (i; varStackSave .. obstate.varStack.length)
+ {
+ const vi = obstate.varStack[i];
+ // auto pvs = &ob.gen[vi];
+ auto v = obstate.vars[vi];
+ //if (pvs.state == PtrState.Undefined)
+ //v.error(ce.loc, "is Undefined, cannot pass to function");
+
+ dgReadVar(ce.loc, ob, v, obstate.mutableStack[i]);
+ }
+
+ /* Pop off stack all variables for this function call
+ */
+ obstate.varStack.setDim(varStackSave);
+ obstate.mutableStack.setDim(varStackSave);
+
+ if (hasOut)
+ // Initialization of out's only happens after the function call
+ foreach (const i, arg; (*ce.arguments)[])
+ {
+ if (i - j < tf.parameterList.length &&
+ i >= j)
+ {
+ Parameter p = tf.parameterList[i - j];
+ if (p.storageClass & STC.out_)
+ {
+ if (auto v = isTrackableVarExp(arg))
+ dgWriteVar(ob, v, null, true);
+ }
+ }
+ }
+ }
+
+ override void visit(SymOffExp e)
+ {
+ if (auto v = e.var.isVarDeclaration())
+ if (isTrackableVar(v))
+ {
+ dgReadVar(e.loc, ob, v, isMutableRef(e.type));
+ }
+ }
+
+ override void visit(LogicalExp e)
+ {
+ e.e1.accept(this);
+
+ const vlen = obstate.vars.length;
+ auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
+ PtrVarState[] gen1 = p[0 .. vlen];
+ foreach (i, ref pvs; gen1)
+ {
+ pvs = ob.gen[i];
+ }
+
+ e.e2.accept(this);
+
+ // Merge gen1 into ob.gen
+ foreach (i; 0 .. vlen)
+ {
+ ob.gen[i].combine(gen1[i], i, ob.gen);
+ }
+
+ mem.xfree(p); // should free .deps too
+ }
+
+ override void visit(CondExp e)
+ {
+ e.econd.accept(this);
+
+ const vlen = obstate.vars.length;
+ auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
+ PtrVarState[] gen1 = p[0 .. vlen];
+ foreach (i, ref pvs; gen1)
+ {
+ pvs = ob.gen[i];
+ }
+
+ e.e1.accept(this);
+
+ // Swap gen1 with ob.gen
+ foreach (i; 0 .. vlen)
+ {
+ gen1[i].deps.swap(ob.gen[i].deps);
+ const state = gen1[i].state;
+ gen1[i].state = ob.gen[i].state;
+ ob.gen[i].state = state;
+ }
+
+ e.e2.accept(this);
+
+ /* xxx1 is the state from expression e1, ob.xxx is the state from e2.
+ * Merge xxx1 into ob.xxx to get the state from `e`.
+ */
+ foreach (i; 0 .. vlen)
+ {
+ ob.gen[i].combine(gen1[i], i, ob.gen);
+ }
+
+ mem.xfree(p); // should free .deps too
+ }
+
+ override void visit(AddrExp e)
+ {
+ /* Taking the address of struct literal is normally not
+ * allowed, but CTFE can generate one out of a new expression,
+ * but it'll be placed in static data so no need to check it.
+ */
+ if (e.e1.op != TOK.structLiteral)
+ e.e1.accept(this);
+ }
+
+ override void visit(UnaExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(BinExp e)
+ {
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ Type tb = e.type.toBasetype();
+ if (tb.ty == Tsarray || tb.ty == Tarray)
+ {
+ if (e.basis)
+ e.basis.accept(this);
+ foreach (el; *e.elements)
+ {
+ if (el)
+ el.accept(this);
+ }
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ if (e.elements)
+ {
+ foreach (ex; *e.elements)
+ {
+ if (ex)
+ ex.accept(this);
+ }
+ }
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ if (e.keys)
+ {
+ foreach (i, key; *e.keys)
+ {
+ if (key)
+ key.accept(this);
+ if (auto value = (*e.values)[i])
+ value.accept(this);
+ }
+ }
+ }
+
+ override void visit(NewExp e)
+ {
+ if (e.arguments)
+ {
+ foreach (ex; *e.arguments)
+ {
+ if (ex)
+ ex.accept(this);
+ }
+ }
+ }
+
+ override void visit(SliceExp e)
+ {
+ e.e1.accept(this);
+ if (e.lwr)
+ e.lwr.accept(this);
+ if (e.upr)
+ e.upr.accept(this);
+ }
+ }
+
+ if (e)
+ {
+ scope ExpWalker ew = new ExpWalker(&dgWriteVar, &dgReadVar, ob, obstate);
+ e.accept(ew);
+ }
+ }
+
+ foreachExp(ob, ob.exp);
+}
+
+/***************************************
+ * Determine the state of a variable based on
+ * its type and storage class.
+ */
+PtrState toPtrState(VarDeclaration v)
+{
+ /* pointer to mutable: Owner
+ * pointer to mutable, scope: Borrowed
+ * pointer to const: Owner
+ * pointer to const, scope: Readonly
+ * ref: Borrowed
+ * const ref: Readonly
+ */
+
+ auto t = v.type;
+ if (v.isRef())
+ {
+ return t.hasMutableFields() ? PtrState.Borrowed : PtrState.Readonly;
+ }
+ if (v.isScope())
+ {
+ return t.hasPointersToMutableFields() ? PtrState.Borrowed : PtrState.Readonly;
+ }
+ else
+ return PtrState.Owner;
+}
+
+/**********************************
+ * Does type `t` contain any pointers to mutable?
+ */
+bool hasPointersToMutableFields(Type t)
+{
+ auto tb = t.toBasetype();
+ if (!tb.isMutable())
+ return false;
+ if (auto tsa = tb.isTypeSArray())
+ {
+ return tsa.nextOf().hasPointersToMutableFields();
+ }
+ if (auto ts = tb.isTypeStruct())
+ {
+ foreach (v; ts.sym.fields)
+ {
+ if (v.isRef())
+ {
+ if (v.type.hasMutableFields())
+ return true;
+ }
+ else if (v.type.hasPointersToMutableFields())
+ return true;
+ }
+ return false;
+ }
+ auto tbn = tb.nextOf();
+ return tbn && tbn.hasMutableFields();
+}
+
+/********************************
+ * Does type `t` have any mutable fields?
+ */
+bool hasMutableFields(Type t)
+{
+ auto tb = t.toBasetype();
+ if (!tb.isMutable())
+ return false;
+ if (auto tsa = tb.isTypeSArray())
+ {
+ return tsa.nextOf().hasMutableFields();
+ }
+ if (auto ts = tb.isTypeStruct())
+ {
+ foreach (v; ts.sym.fields)
+ {
+ if (v.type.hasMutableFields())
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+
+
+/***************************************
+ * Do the data flow analysis (i.e. compute the input[]
+ * and output[] vectors for each ObNode).
+ */
+void doDataFlowAnalysis(ref ObState obstate)
+{
+ enum log = false;
+ if (log)
+ {
+ printf("-----------------doDataFlowAnalysis()-------------------------\n");
+ foreach (ob; obstate.nodes) ob.print();
+ printf("------------------------------------------\n");
+ }
+
+ if (!obstate.nodes.length)
+ return;
+
+ auto startnode = obstate.nodes[0];
+ assert(startnode.preds.length == 0);
+
+ /* Set opening state `input[]` for first node
+ */
+ foreach (i, ref ps; startnode.input)
+ {
+ auto v = obstate.vars[i];
+ auto state = toPtrState(v);
+ if (v.isParameter())
+ {
+ if (v.isOut())
+ state = PtrState.Undefined;
+ else if (v.isBorrowedPtr())
+ state = PtrState.Borrowed;
+ else
+ state = PtrState.Owner;
+ }
+ else
+ state = PtrState.Undefined;
+ ps.state = state;
+ ps.deps.zero();
+ startnode.gen[i] = ps;
+ }
+
+ /* Set all output[]s to Initial
+ */
+ foreach (ob; obstate.nodes[])
+ {
+ foreach (ref ps; ob.output)
+ {
+ ps.state = PtrState.Initial;
+ ps.deps.zero();
+ }
+ }
+
+ const vlen = obstate.vars.length;
+ PtrVarState pvs;
+ pvs.deps.length = vlen;
+ int counter = 0;
+ bool changes;
+ do
+ {
+ changes = false;
+ assert(++counter <= 1000); // should converge, but don't hang if it doesn't
+ foreach (ob; obstate.nodes[])
+ {
+ /* Construct ob.gen[] by combining the .output[]s of each ob.preds[]
+ * and set ob.input[] to the same state
+ */
+ if (ob != startnode)
+ {
+ assert(ob.preds.length);
+
+ foreach (i; 0 .. vlen)
+ {
+ ob.gen[i] = ob.preds[0].output[i];
+ }
+
+ foreach (j; 1 .. ob.preds.length)
+ {
+ foreach (i; 0 .. vlen)
+ {
+ ob.gen[i].combine(ob.preds[j].output[i], i, ob.gen);
+ }
+ }
+
+ /* Set ob.input[] to ob.gen[],
+ * if any changes were made we'll have to do another iteration
+ */
+ foreach (i; 0 .. vlen)
+ {
+ if (ob.gen[i] != ob.input[i])
+ {
+ ob.input[i] = ob.gen[i];
+ changes = true;
+ }
+ }
+ }
+
+ /* Compute gen[] for node ob
+ */
+ genKill(obstate, ob);
+
+ foreach (i; 0 .. vlen)
+ {
+ if (ob.gen[i] != ob.output[i])
+ {
+ ob.output[i] = ob.gen[i];
+ changes = true;
+ }
+ }
+ }
+ } while (changes);
+
+ static if (log)
+ {
+ foreach (obi, ob; obstate.nodes)
+ {
+ printf("%d: %s\n", obi, ob.exp ? ob.exp.toChars() : "".ptr);
+ printf(" input:\n");
+ foreach (i, ref pvs2; ob.input[])
+ {
+ printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]);
+ }
+
+ printf(" gen:\n");
+ foreach (i, ref pvs2; ob.gen[])
+ {
+ printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]);
+ }
+ printf(" output:\n");
+ foreach (i, ref pvs2; ob.output[])
+ {
+ printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]);
+ }
+ }
+ printf("\n");
+ }
+}
+
+
+/***************************************
+ * Check for Ownership/Borrowing errors.
+ */
+void checkObErrors(ref ObState obstate)
+{
+ enum log = false;
+ if (log)
+ printf("------------checkObErrors()----------\n");
+
+ void dgWriteVar(ObNode* ob, PtrVarState[] cpvs, VarDeclaration v, Expression e)
+ {
+ if (log) printf("dgWriteVar(v:%s, e:%s)\n", v.toChars(), e ? e.toChars() : "null");
+ const vi = obstate.vars.find(v);
+ assert(vi != size_t.max);
+ PtrVarState* pvs = &cpvs[vi];
+ readVar(ob, vi, true, cpvs);
+ if (e)
+ {
+ if (isBorrowedPtr(v))
+ pvs.state = PtrState.Borrowed;
+ else if (isReadonlyPtr(v))
+ pvs.state = PtrState.Readonly;
+ else
+ pvs.state = PtrState.Owner;
+ pvs.deps.zero();
+
+ EscapeByResults er;
+ escapeByValue(e, &er, true);
+
+ void by(VarDeclaration r) // `v` = `r`
+ {
+ //printf(" by(%s)\n", r.toChars());
+ const ri = obstate.vars.find(r);
+ if (ri == size_t.max)
+ return;
+
+ with (PtrState)
+ {
+ pvs.deps[ri] = true; // v took from r
+ auto pvsr = &cpvs[ri];
+
+ if (pvsr.state == Undefined)
+ {
+ v.error(e.loc, "is reading from `%s` which is Undefined", r.toChars());
+ }
+ else if (isBorrowedPtr(v)) // v is going to borrow from r
+ {
+ if (pvsr.state == Readonly)
+ v.error(e.loc, "is borrowing from `%s` which is Readonly", r.toChars());
+
+ pvs.state = Borrowed;
+ }
+ else if (isReadonlyPtr(v))
+ {
+ pvs.state = Readonly;
+ }
+ else
+ {
+ // move from r to v
+ pvsr.state = Undefined;
+ pvsr.deps.zero();
+ }
+ }
+ }
+
+ foreach (VarDeclaration v2; er.byvalue)
+ by(v2);
+ foreach (VarDeclaration v2; er.byref)
+ by(v2);
+ }
+ else
+ {
+ if (isBorrowedPtr(v))
+ pvs.state = PtrState.Borrowed;
+ else if (isReadonlyPtr(v))
+ pvs.state = PtrState.Readonly;
+ else
+ pvs.state = PtrState.Owner;
+ pvs.deps.zero();
+ }
+ }
+
+ void dgReadVar(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[] gen)
+ {
+ if (log) printf("dgReadVar() %s\n", v.toChars());
+ const vi = obstate.vars.find(v);
+ assert(vi != size_t.max);
+ auto pvs = &gen[vi];
+ if (pvs.state == PtrState.Undefined)
+ v.error(loc, "has undefined state and cannot be read");
+
+ readVar(ob, vi, mutable, gen);
+ }
+
+ void foreachExp(ObNode* ob, Expression e, PtrVarState[] cpvs)
+ {
+ extern (C++) final class ExpWalker : Visitor
+ {
+ alias visit = typeof(super).visit;
+ extern (D) void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar;
+ extern (D) void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar;
+ PtrVarState[] cpvs;
+ ObNode* ob;
+ ObState* obstate;
+
+ extern (D) this(void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar,
+ void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar,
+ PtrVarState[] cpvs, ObNode* ob, ref ObState obstate)
+ {
+ this.dgReadVar = dgReadVar;
+ this.dgWriteVar = dgWriteVar;
+ this.cpvs = cpvs;
+ this.ob = ob;
+ this.obstate = &obstate;
+ }
+
+ override void visit(Expression)
+ {
+ }
+
+ override void visit(DeclarationExp e)
+ {
+ void Dsymbol_visit(Dsymbol s)
+ {
+ if (auto vd = s.isVarDeclaration())
+ {
+ s = s.toAlias();
+ if (s != vd)
+ return Dsymbol_visit(s);
+ if (!isTrackableVar(vd))
+ return;
+
+ if (vd._init && vd._init.isVoidInitializer())
+ return;
+
+ auto ei = vd._init ? vd._init.isExpInitializer() : null;
+ if (ei)
+ {
+ auto e = ei.exp;
+ if (auto ae = e.isConstructExp())
+ e = ae.e2;
+ dgWriteVar(ob, cpvs, vd, e);
+ }
+ else
+ dgWriteVar(ob, cpvs, vd, null);
+ }
+ else if (auto td = s.isTupleDeclaration())
+ {
+ foreach (o; *td.objects)
+ {
+ if (auto eo = o.isExpression())
+ {
+ if (auto se = eo.isDsymbolExp())
+ {
+ Dsymbol_visit(se.s);
+ }
+ }
+ }
+ }
+ }
+
+ Dsymbol_visit(e.declaration);
+ }
+
+ override void visit(AssignExp ae)
+ {
+ ae.e2.accept(this);
+ if (auto ve = ae.e1.isVarExp())
+ {
+ if (auto v = ve.var.isVarDeclaration())
+ if (isTrackableVar(v))
+ dgWriteVar(ob, cpvs, v, ae.e2);
+ }
+ else
+ ae.e1.accept(this);
+ }
+
+ override void visit(VarExp ve)
+ {
+ //printf("VarExp: %s\n", ve.toChars());
+ if (auto v = ve.var.isVarDeclaration())
+ if (isTrackableVar(v))
+ {
+ dgReadVar(ve.loc, ob, v, isMutableRef(ve.type), cpvs);
+ }
+ }
+
+ override void visit(CallExp ce)
+ {
+ //printf("CallExp(%s)\n", ce.toChars());
+ ce.e1.accept(this);
+ auto t = ce.e1.type.toBasetype();
+ auto tf = t.isTypeFunction();
+ if (!tf)
+ {
+ assert(t.ty == Tdelegate);
+ tf = t.nextOf().isTypeFunction();
+ assert(tf);
+ }
+
+ // j=1 if _arguments[] is first argument
+ const int j = tf.isDstyleVariadic();
+ bool hasOut;
+ const varStackSave = obstate.varStack.length;
+
+ foreach (const i, arg; (*ce.arguments)[])
+ {
+ if (i - j < tf.parameterList.length &&
+ i >= j)
+ {
+ Parameter p = tf.parameterList[i - j];
+ auto pt = p.type.toBasetype();
+
+ if (!(p.storageClass & STC.out_ && arg.isVarExp()))
+ arg.accept(this);
+
+ EscapeByResults er;
+ escapeByValue(arg, &er, true);
+
+ void by(VarDeclaration v)
+ {
+ if (!isTrackableVar(v))
+ return;
+
+ const vi = obstate.vars.find(v);
+ if (vi == size_t.max)
+ return;
+
+ auto pvs = &cpvs[vi];
+
+ if (p.storageClass & STC.out_)
+ {
+ /// initialize
+ hasOut = true;
+ makeUndefined(vi, cpvs);
+ }
+ else if (p.storageClass & STC.scope_)
+ {
+ // borrow
+ obstate.varStack.push(vi);
+ obstate.mutableStack.push(isMutableRef(pt));
+ }
+ else
+ {
+ // move (i.e. consume arg)
+ if (pvs.state != PtrState.Owner)
+ v.error(arg.loc, "is not Owner, cannot consume its value");
+ makeUndefined(vi, cpvs);
+ }
+ }
+
+ foreach (VarDeclaration v2; er.byvalue)
+ by(v2);
+ foreach (VarDeclaration v2; er.byref)
+ by(v2);
+ }
+ else // variadic args
+ {
+ arg.accept(this);
+
+ EscapeByResults er;
+ escapeByValue(arg, &er, true);
+
+ void byv(VarDeclaration v)
+ {
+ if (!isTrackableVar(v))
+ return;
+
+ const vi = obstate.vars.find(v);
+ if (vi == size_t.max)
+ return;
+
+ auto pvs = &cpvs[vi];
+
+ if (tf.parameterList.stc & STC.scope_)
+ {
+ // borrow
+ obstate.varStack.push(vi);
+ obstate.mutableStack.push(isMutableRef(arg.type) &&
+ !(tf.parameterList.stc & (STC.const_ | STC.immutable_)));
+ }
+ else
+ {
+ // move (i.e. consume arg)
+ if (pvs.state != PtrState.Owner)
+ v.error(arg.loc, "is not Owner, cannot consume its value");
+ makeUndefined(vi, cpvs);
+ }
+ }
+
+ foreach (VarDeclaration v2; er.byvalue)
+ byv(v2);
+ foreach (VarDeclaration v2; er.byref)
+ byv(v2);
+ }
+ }
+
+ /* Do a dummy 'read' of each variable passed to the function,
+ * to detect O/B errors
+ */
+ assert(obstate.varStack.length == obstate.mutableStack.length);
+ foreach (i; varStackSave .. obstate.varStack.length)
+ {
+ const vi = obstate.varStack[i];
+ auto pvs = &cpvs[vi];
+ auto v = obstate.vars[vi];
+ //if (pvs.state == PtrState.Undefined)
+ //v.error(ce.loc, "is Undefined, cannot pass to function");
+
+ dgReadVar(ce.loc, ob, v, obstate.mutableStack[i], cpvs);
+
+ if (pvs.state == PtrState.Owner)
+ {
+ for (size_t k = i + 1; k < obstate.varStack.length;++k)
+ {
+ const vk = obstate.varStack[k];
+ if (vk == vi)
+ {
+ if (obstate.mutableStack[vi] || obstate.mutableStack[vk])
+ {
+ v.error(ce.loc, "is passed as Owner more than once");
+ break; // no need to continue
+ }
+ }
+ }
+ }
+ }
+
+ /* Pop off stack all variables for this function call
+ */
+ obstate.varStack.setDim(varStackSave);
+ obstate.mutableStack.setDim(varStackSave);
+
+ if (hasOut)
+ // Initialization of out's only happens after the function call
+ foreach (const i, arg; (*ce.arguments)[])
+ {
+ if (i - j < tf.parameterList.length &&
+ i >= j)
+ {
+ Parameter p = tf.parameterList[i - j];
+ if (p.storageClass & STC.out_)
+ {
+ if (auto v = isTrackableVarExp(arg))
+ {
+ dgWriteVar(ob, cpvs, v, null);
+ }
+ }
+ }
+ }
+ }
+
+ override void visit(SymOffExp e)
+ {
+ if (auto v = e.var.isVarDeclaration())
+ if (isTrackableVar(v))
+ {
+ dgReadVar(e.loc, ob, v, isMutableRef(e.type), cpvs);
+ }
+ }
+
+ override void visit(LogicalExp e)
+ {
+ e.e1.accept(this);
+
+ const vlen = obstate.vars.length;
+ auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
+ PtrVarState[] out1 = p[0 .. vlen];
+ foreach (i, ref pvs; out1)
+ {
+ pvs = cpvs[i];
+ }
+
+ e.e2.accept(this);
+
+ // Merge out1 into cpvs
+ foreach (i; 0 .. vlen)
+ {
+ cpvs[i].combine(out1[i], i, cpvs);
+ }
+
+ mem.xfree(p); // should free .deps too
+ }
+
+ override void visit(CondExp e)
+ {
+ e.econd.accept(this);
+
+ const vlen = obstate.vars.length;
+ auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
+ PtrVarState[] out1 = p[0 .. vlen];
+ foreach (i, ref pvs; out1)
+ {
+ pvs = cpvs[i];
+ }
+
+ e.e1.accept(this);
+
+ // Swap out1 with cpvs
+ foreach (i; 0 .. vlen)
+ {
+ out1[i].deps.swap(cpvs[i].deps);
+ const state = out1[i].state;
+ out1[i].state = cpvs[i].state;
+ cpvs[i].state = state;
+ }
+
+ e.e2.accept(this);
+
+ // Merge out1 into cpvs
+ foreach (i; 0 .. vlen)
+ {
+ cpvs[i].combine(out1[i], i, cpvs);
+ }
+
+ mem.xfree(p); // should free .deps too
+ }
+
+ override void visit(AddrExp e)
+ {
+ /* Taking the address of struct literal is normally not
+ * allowed, but CTFE can generate one out of a new expression,
+ * but it'll be placed in static data so no need to check it.
+ */
+ if (e.e1.op != TOK.structLiteral)
+ e.e1.accept(this);
+ }
+
+ override void visit(UnaExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(BinExp e)
+ {
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ Type tb = e.type.toBasetype();
+ if (tb.ty == Tsarray || tb.ty == Tarray)
+ {
+ if (e.basis)
+ e.basis.accept(this);
+ foreach (el; *e.elements)
+ {
+ if (el)
+ el.accept(this);
+ }
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ if (e.elements)
+ {
+ foreach (ex; *e.elements)
+ {
+ if (ex)
+ ex.accept(this);
+ }
+ }
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ if (e.keys)
+ {
+ foreach (i, key; *e.keys)
+ {
+ if (key)
+ key.accept(this);
+ if (auto value = (*e.values)[i])
+ value.accept(this);
+ }
+ }
+ }
+
+ override void visit(NewExp e)
+ {
+ if (e.arguments)
+ {
+ foreach (ex; *e.arguments)
+ {
+ if (ex)
+ ex.accept(this);
+ }
+ }
+ }
+
+ override void visit(SliceExp e)
+ {
+ e.e1.accept(this);
+ if (e.lwr)
+ e.lwr.accept(this);
+ if (e.upr)
+ e.upr.accept(this);
+ }
+ }
+
+ if (e)
+ {
+ scope ExpWalker ew = new ExpWalker(&dgReadVar, &dgWriteVar, cpvs, ob, obstate);
+ e.accept(ew);
+ }
+ }
+
+ const vlen = obstate.vars.length;
+ auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
+ PtrVarState[] cpvs = p[0 .. vlen];
+ foreach (ref pvs; cpvs)
+ pvs.deps.length = vlen;
+
+ foreach (obi, ob; obstate.nodes)
+ {
+ static if (log)
+ {
+ printf("%d: %s\n", obi, ob.exp ? ob.exp.toChars() : "".ptr);
+ printf(" input:\n");
+ foreach (i, ref pvs; ob.input[])
+ {
+ printf(" %s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]);
+ }
+ }
+
+ /* Combine the .output[]s of each ob.preds[] looking for errors
+ */
+ if (obi) // skip startnode
+ {
+ assert(ob.preds.length);
+
+ foreach (i; 0 .. vlen)
+ {
+ ob.gen[i] = ob.preds[0].output[i];
+ }
+
+ foreach (j; 1 .. ob.preds.length)
+ {
+ foreach (i; 0 .. vlen)
+ {
+ auto pvs1 = &ob.gen[i];
+ auto pvs2 = &ob.preds[j].output[i];
+ const s1 = pvs1.state;
+ const s2 = pvs2.state;
+ if (s1 != s2 && (s1 == PtrState.Owner || s2 == PtrState.Owner))
+ {
+ auto v = obstate.vars[i];
+ v.error(ob.exp ? ob.exp.loc : v.loc, "is both %s and %s", s1.toChars(), s2.toChars());
+ }
+ pvs1.combine(*pvs2, i, ob.gen);
+ }
+ }
+ }
+
+ /* Prolly should use gen[] instead of cpvs[], or vice versa
+ */
+ foreach (i, ref pvs; ob.input)
+ {
+ cpvs[i] = pvs;
+ }
+
+ foreachExp(ob, ob.exp, cpvs);
+
+ static if (log)
+ {
+ printf(" cpvs:\n");
+ foreach (i, ref pvs; cpvs[])
+ {
+ printf(" %s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]);
+ }
+ printf(" output:\n");
+ foreach (i, ref pvs; ob.output[])
+ {
+ printf(" %s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]);
+ }
+ }
+
+ if (ob.obtype == ObType.retexp)
+ {
+ EscapeByResults er;
+ escapeByValue(ob.exp, &er, true);
+
+ void by(VarDeclaration r) // `r` is the rvalue
+ {
+ const ri = obstate.vars.find(r);
+ if (ri == size_t.max)
+ return;
+ with (PtrState)
+ {
+ auto pvsr = &ob.output[ri];
+ switch (pvsr.state)
+ {
+ case Undefined:
+ r.error(ob.exp.loc, "is returned but is Undefined");
+ break;
+
+ case Owner:
+ pvsr.state = Undefined; // returning a pointer "consumes" it
+ break;
+
+ case Borrowed:
+ case Readonly:
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ }
+
+ foreach (VarDeclaration v2; er.byvalue)
+ by(v2);
+ foreach (VarDeclaration v2; er.byref)
+ by(v2);
+ }
+
+ if (ob.obtype == ObType.return_ || ob.obtype == ObType.retexp)
+ {
+ foreach (i, ref pvs; ob.output[])
+ {
+ //printf("%s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]);
+ if (pvs.state == PtrState.Owner)
+ {
+ auto v = obstate.vars[i];
+ if (v.type.hasPointers())
+ v.error(v.loc, "is left dangling at return");
+ }
+ }
+ }
+ }
+}
+
+
+/***************************************************
+ * Read from variable vi.
+ * The beginning of the 'scope' of a variable is when it is first read.
+ * Hence, when a read is done, instead of when assignment to the variable is done, the O/B rules are enforced.
+ * (Also called "non-lexical scoping".)
+ */
+void readVar(ObNode* ob, const size_t vi, bool mutable, PtrVarState[] gen)
+{
+ //printf("readVar(v%d)\n", cast(int)vi);
+ auto pvso = &gen[vi];
+ switch (pvso.state)
+ {
+ case PtrState.Owner:
+ //printf("t: %s\n", t.toChars());
+ if (mutable) // if mutable read
+ {
+ makeChildrenUndefined(vi, gen);
+ }
+ else // const read
+ {
+ // If there's a Borrow child, set that to Undefined
+ foreach (di; 0 .. gen.length)
+ {
+ auto pvsd = &gen[di];
+ if (pvsd.deps[vi] && pvsd.state == PtrState.Borrowed) // if di borrowed vi
+ {
+ makeUndefined(di, gen);
+ }
+ }
+ }
+ break;
+
+ case PtrState.Borrowed:
+ /* All children become Undefined
+ */
+ makeChildrenUndefined(vi, gen);
+ break;
+
+ case PtrState.Readonly:
+ break;
+
+ case PtrState.Undefined:
+ break;
+
+ default:
+ break;
+ }
+}
+
+/********************
+ * Recursively make Undefined all who list vi as a dependency
+ */
+void makeChildrenUndefined(size_t vi, PtrVarState[] gen)
+{
+ //printf("makeChildrenUndefined(%d)\n", vi);
+ foreach (di; 0 .. gen.length)
+ {
+ if (gen[di].deps[vi]) // if di depends on vi
+ {
+ if (gen[di].state != PtrState.Undefined)
+ {
+ gen[di].state = PtrState.Undefined; // set this first to avoid infinite recursion
+ makeChildrenUndefined(di, gen);
+ gen[di].deps.zero();
+ }
+ }
+ }
+}
+
+
+/********************
+ * Recursively make Undefined vi undefined and all who list vi as a dependency
+ */
+void makeUndefined(size_t vi, PtrVarState[] gen)
+{
+ auto pvs = &gen[vi];
+ pvs.state = PtrState.Undefined; // set this first to avoid infinite recursion
+ makeChildrenUndefined(vi, gen);
+ pvs.deps.zero();
+}
+
+/*************************
+ * Is type `t` a reference to a const or a reference to a mutable?
+ */
+bool isMutableRef(Type t)
+{
+ auto tb = t.toBasetype();
+ return (tb.nextOf() ? tb.nextOf() : tb).isMutable();
+}
diff --git a/gcc/d/dmd/objc.c b/gcc/d/dmd/objc.c
deleted file mode 100644
index 3199a0164ee..00000000000
--- a/gcc/d/dmd/objc.c
+++ /dev/null
@@ -1,84 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 2015-2021 by The D Language Foundation, All Rights Reserved
- * written by Michel Fortin
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/objc_stubs.c
- */
-
-#include "objc.h"
-#include "aggregate.h"
-#include "scope.h"
-
-class FuncDeclaration;
-
-// MARK: ObjcSelector
-
-ObjcSelector::ObjcSelector(const char *, size_t, size_t)
-{
- printf("Should never be called when D_OBJC is false\n");
- assert(0);
-}
-
-ObjcSelector *ObjcSelector::lookup(const char *)
-{
- printf("Should never be called when D_OBJC is false\n");
- assert(0);
- return NULL;
-}
-
-ObjcSelector *ObjcSelector::lookup(const char *, size_t, size_t)
-{
- printf("Should never be called when D_OBJC is false\n");
- assert(0);
- return NULL;
-}
-
-ObjcSelector *ObjcSelector::create(FuncDeclaration *)
-{
- printf("Should never be called when D_OBJC is false\n");
- assert(0);
- return NULL;
-}
-
-class UnsupportedObjc : public Objc
-{
- void setObjc(ClassDeclaration *cd)
- {
- cd->error("Objective-C classes not supported");
- }
-
- void setObjc(InterfaceDeclaration *id)
- {
- id->error("Objective-C interfaces not supported");
- }
-
- void setSelector(FuncDeclaration *, Scope *)
- {
- // noop
- }
-
- void validateSelector(FuncDeclaration *)
- {
- // noop
- }
-
- void checkLinkage(FuncDeclaration *)
- {
- // noop
- }
-};
-
-static Objc *_objc;
-
-Objc *objc()
-{
- return _objc;
-}
-
-void Objc::_init()
-{
- _objc = new UnsupportedObjc();
-}
diff --git a/gcc/d/dmd/objc.d b/gcc/d/dmd/objc.d
new file mode 100644
index 00000000000..85e371e6e63
--- /dev/null
+++ b/gcc/d/dmd/objc.d
@@ -0,0 +1,953 @@
+/**
+ * Interfacing with Objective-C.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/objc_interface.html, Interfacing to Objective-C)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/objc.d, _objc.d)
+ * Documentation: https://dlang.org/phobos/dmd_objc.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc.d
+ */
+
+module dmd.objc;
+
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.cond;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dmangle;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.gluelayer;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.root.array;
+import dmd.root.outbuffer;
+import dmd.root.stringtable;
+import dmd.target;
+import dmd.tokens;
+
+struct ObjcSelector
+{
+ // MARK: Selector
+ private __gshared StringTable!(ObjcSelector*) stringtable;
+ private __gshared int incnum = 0;
+ const(char)* stringvalue;
+ size_t stringlen;
+ size_t paramCount;
+
+ extern (C++) static void _init()
+ {
+ stringtable._init();
+ }
+
+ extern (D) this(const(char)* sv, size_t len, size_t pcount)
+ {
+ stringvalue = sv;
+ stringlen = len;
+ paramCount = pcount;
+ }
+
+ extern (D) static ObjcSelector* lookup(const(char)* s)
+ {
+ size_t len = 0;
+ size_t pcount = 0;
+ const(char)* i = s;
+ while (*i != 0)
+ {
+ ++len;
+ if (*i == ':')
+ ++pcount;
+ ++i;
+ }
+ return lookup(s, len, pcount);
+ }
+
+ extern (D) static ObjcSelector* lookup(const(char)* s, size_t len, size_t pcount)
+ {
+ auto sv = stringtable.update(s, len);
+ ObjcSelector* sel = sv.value;
+ if (!sel)
+ {
+ sel = new ObjcSelector(sv.toDchars(), len, pcount);
+ sv.value = sel;
+ }
+ return sel;
+ }
+
+ extern (C++) static ObjcSelector* create(FuncDeclaration fdecl)
+ {
+ OutBuffer buf;
+ TypeFunction ftype = cast(TypeFunction)fdecl.type;
+ const id = fdecl.ident.toString();
+ const nparams = ftype.parameterList.length;
+ // Special case: property setter
+ if (ftype.isproperty && nparams == 1)
+ {
+ // rewrite "identifier" as "setIdentifier"
+ char firstChar = id[0];
+ if (firstChar >= 'a' && firstChar <= 'z')
+ firstChar = cast(char)(firstChar - 'a' + 'A');
+ buf.writestring("set");
+ buf.writeByte(firstChar);
+ buf.write(id[1 .. id.length - 1]);
+ buf.writeByte(':');
+ goto Lcomplete;
+ }
+ // write identifier in selector
+ buf.write(id[]);
+ // add mangled type and colon for each parameter
+ if (nparams)
+ {
+ buf.writeByte('_');
+ foreach (i, fparam; ftype.parameterList)
+ {
+ mangleToBuffer(fparam.type, &buf);
+ buf.writeByte(':');
+ }
+ }
+ Lcomplete:
+ buf.writeByte('\0');
+ // the slice is not expected to include a terminating 0
+ return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams);
+ }
+
+ extern (D) const(char)[] toString() const pure
+ {
+ return stringvalue[0 .. stringlen];
+ }
+}
+
+private __gshared Objc _objc;
+
+Objc objc()
+{
+ return _objc;
+}
+
+
+/**
+ * Contains all data for a class declaration that is needed for the Objective-C
+ * integration.
+ */
+extern (C++) struct ObjcClassDeclaration
+{
+ /// `true` if this class is a metaclass.
+ bool isMeta = false;
+
+ /// `true` if this class is externally defined.
+ bool isExtern = false;
+
+ /// Name of this class.
+ Identifier identifier;
+
+ /// The class declaration this belongs to.
+ ClassDeclaration classDeclaration;
+
+ /// The metaclass of this class.
+ ClassDeclaration metaclass;
+
+ /// List of non-inherited methods.
+ FuncDeclaration[] methodList;
+
+ extern (D) this(ClassDeclaration classDeclaration)
+ {
+ this.classDeclaration = classDeclaration;
+ }
+
+ bool isRootClass() const
+ {
+ return classDeclaration.classKind == ClassKind.objc &&
+ !metaclass &&
+ !classDeclaration.baseClass;
+ }
+}
+
+/**
+ * Contains all data for a function declaration that is needed for the
+ * Objective-C integration.
+ */
+extern (C++) struct ObjcFuncDeclaration
+{
+ /// The method selector (member functions only).
+ ObjcSelector* selector;
+
+ /// The implicit selector parameter.
+ VarDeclaration selectorParameter;
+
+ /// `true` if this function declaration is declared optional.
+ bool isOptional;
+}
+
+// Should be an interface
+extern(C++) abstract class Objc
+{
+ static void _init()
+ {
+ if (target.objc.supported)
+ _objc = new Supported;
+ else
+ _objc = new Unsupported;
+ }
+
+ /**
+ * Deinitializes the global state of the compiler.
+ *
+ * This can be used to restore the state set by `_init` to its original
+ * state.
+ */
+ static void deinitialize()
+ {
+ _objc = _objc.init;
+ }
+
+ abstract void setObjc(ClassDeclaration cd);
+ abstract void setObjc(InterfaceDeclaration);
+
+ /**
+ * Returns a pretty textual representation of the given class declaration.
+ *
+ * Params:
+ * classDeclaration = the class declaration to return the textual representation for
+ * qualifyTypes = `true` if types should be qualified in the result
+ *
+ * Returns: the textual representation
+ */
+ abstract const(char)* toPrettyChars(ClassDeclaration classDeclaration, bool qualifyTypes) const;
+
+ abstract void setSelector(FuncDeclaration, Scope* sc);
+ abstract void validateSelector(FuncDeclaration fd);
+ abstract void checkLinkage(FuncDeclaration fd);
+
+ /**
+ * Returns `true` if the given function declaration is virtual.
+ *
+ * Function declarations with Objective-C linkage and which are static or
+ * final are considered virtual.
+ *
+ * Params:
+ * fd = the function declaration to check if it's virtual
+ *
+ * Returns: `true` if the given function declaration is virtual
+ */
+ abstract bool isVirtual(const FuncDeclaration fd) const;
+
+ /**
+ * Marks the given function declaration as optional.
+ *
+ * A function declaration is considered optional if it's annotated with the
+ * UDA: `@(core.attribute.optional)`. Only function declarations inside
+ * interface declarations and with Objective-C linkage can be declared as
+ * optional.
+ *
+ * Params:
+ * functionDeclaration = the function declaration to be set as optional
+ * sc = the scope from the semantic phase
+ */
+ abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const;
+
+ /**
+ * Validates function declarations declared optional.
+ *
+ * Params:
+ * functionDeclaration = the function declaration to validate
+ */
+ abstract void validateOptional(FuncDeclaration functionDeclaration) const;
+
+ /**
+ * Gets the parent of the given function declaration.
+ *
+ * Handles Objective-C static member functions, which are virtual functions
+ * of the metaclass, by returning the parent class declaration to the
+ * metaclass.
+ *
+ * Params:
+ * fd = the function declaration to get the parent of
+ * cd = the current parent, i.e. the class declaration the given function
+ * declaration belongs to
+ *
+ * Returns: the parent
+ */
+ abstract ClassDeclaration getParent(FuncDeclaration fd,
+ ClassDeclaration cd) const;
+
+ /**
+ * Adds the given function to the list of Objective-C methods.
+ *
+ * This list will later be used output the necessary Objective-C module info.
+ *
+ * Params:
+ * fd = the function declaration to be added to the list
+ * cd = the class declaration the function belongs to
+ */
+ abstract void addToClassMethodList(FuncDeclaration fd,
+ ClassDeclaration cd) const;
+
+ /**
+ * Returns the `this` pointer of the given function declaration.
+ *
+ * This is only used for class/static methods. For instance methods, no
+ * Objective-C specialization is necessary.
+ *
+ * Params:
+ * funcDeclaration = the function declaration to get the `this` pointer for
+ *
+ * Returns: the `this` pointer of the given function declaration, or `null`
+ * if the given function declaration is not an Objective-C method.
+ */
+ abstract inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const;
+
+ /**
+ * Creates the selector parameter for the given function declaration.
+ *
+ * Objective-C methods has an extra hidden parameter that comes after the
+ * `this` parameter. The selector parameter is of the Objective-C type `SEL`
+ * and contains the selector which this method was called with.
+ *
+ * Params:
+ * fd = the function declaration to create the parameter for
+ * sc = the scope from the semantic phase
+ *
+ * Returns: the newly created selector parameter or `null` for
+ * non-Objective-C functions
+ */
+ abstract VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const;
+
+ /**
+ * Creates and sets the metaclass on the given class/interface declaration.
+ *
+ * Will only be performed on regular Objective-C classes, not on metaclasses.
+ *
+ * Params:
+ * classDeclaration = the class/interface declaration to set the metaclass on
+ */
+ abstract void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const;
+
+ /// ditto
+ abstract void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const;
+
+ /**
+ * Returns Objective-C runtime metaclass of the given class declaration.
+ *
+ * `ClassDeclaration.ObjcClassDeclaration.metaclass` contains the metaclass
+ * from the semantic point of view. This function returns the metaclass from
+ * the Objective-C runtime's point of view. Here, the metaclass of a
+ * metaclass is the root metaclass, not `null`. The root metaclass's
+ * metaclass is itself.
+ *
+ * Params:
+ * classDeclaration = The class declaration to return the metaclass of
+ *
+ * Returns: the Objective-C runtime metaclass of the given class declaration
+ */
+ abstract ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const;
+
+ ///
+ abstract void addSymbols(AttribDeclaration attribDeclaration,
+ ClassDeclarations* classes, ClassDeclarations* categories) const;
+
+ ///
+ abstract void addSymbols(ClassDeclaration classDeclaration,
+ ClassDeclarations* classes, ClassDeclarations* categories) const;
+
+ /**
+ * Issues a compile time error if the `.offsetof`/`.tupleof` property is
+ * used on a field of an Objective-C class.
+ *
+ * To solve the fragile base class problem in Objective-C, fields have a
+ * dynamic offset instead of a static offset. The compiler outputs a
+ * statically known offset which later the dynamic loader can update, if
+ * necessary, when the application is loaded. Due to this behavior it
+ * doesn't make sense to be able to get the offset of a field at compile
+ * time, because this offset might not actually be the same at runtime.
+ *
+ * To get the offset of a field that is correct at runtime, functionality
+ * from the Objective-C runtime can be used instead.
+ *
+ * Params:
+ * expression = the `.offsetof`/`.tupleof` expression
+ * aggregateDeclaration = the aggregate declaration the field of the
+ * `.offsetof`/`.tupleof` expression belongs to
+ * type = the type of the receiver of the `.tupleof` expression
+ *
+ * See_Also:
+ * $(LINK2 https://en.wikipedia.org/wiki/Fragile_binary_interface_problem,
+ * Fragile Binary Interface Problem)
+ *
+ * See_Also:
+ * $(LINK2 https://developer.apple.com/documentation/objectivec/objective_c_runtime,
+ * Objective-C Runtime)
+ */
+ abstract void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const;
+
+ /// ditto
+ abstract void checkTupleof(Expression expression, TypeClass type) const;
+}
+
+extern(C++) private final class Unsupported : Objc
+{
+ extern(D) final this()
+ {
+ ObjcGlue.initialize();
+ }
+
+ override void setObjc(ClassDeclaration cd)
+ {
+ cd.error("Objective-C classes not supported");
+ }
+
+ override void setObjc(InterfaceDeclaration id)
+ {
+ id.error("Objective-C interfaces not supported");
+ }
+
+ override const(char)* toPrettyChars(ClassDeclaration, bool qualifyTypes) const
+ {
+ assert(0, "Should never be called when Objective-C is not supported");
+ }
+
+ override void setSelector(FuncDeclaration, Scope*)
+ {
+ // noop
+ }
+
+ override void validateSelector(FuncDeclaration)
+ {
+ // noop
+ }
+
+ override void checkLinkage(FuncDeclaration)
+ {
+ // noop
+ }
+
+ override bool isVirtual(const FuncDeclaration) const
+ {
+ assert(0, "Should never be called when Objective-C is not supported");
+ }
+
+ override void setAsOptional(FuncDeclaration, Scope*) const
+ {
+ // noop
+ }
+
+ override void validateOptional(FuncDeclaration) const
+ {
+ // noop
+ }
+
+ override ClassDeclaration getParent(FuncDeclaration, ClassDeclaration cd) const
+ {
+ return cd;
+ }
+
+ override void addToClassMethodList(FuncDeclaration, ClassDeclaration) const
+ {
+ // noop
+ }
+
+ override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const
+ {
+ return null;
+ }
+
+ override VarDeclaration createSelectorParameter(FuncDeclaration, Scope*) const
+ {
+ return null;
+ }
+
+ override void setMetaclass(InterfaceDeclaration, Scope*) const
+ {
+ // noop
+ }
+
+ override void setMetaclass(ClassDeclaration, Scope*) const
+ {
+ // noop
+ }
+
+ override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const
+ {
+ assert(0, "Should never be called when Objective-C is not supported");
+ }
+
+ override void addSymbols(AttribDeclaration attribDeclaration,
+ ClassDeclarations* classes, ClassDeclarations* categories) const
+ {
+ // noop
+ }
+
+ override void addSymbols(ClassDeclaration classDeclaration,
+ ClassDeclarations* classes, ClassDeclarations* categories) const
+ {
+ // noop
+ }
+
+ override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const
+ {
+ // noop
+ }
+
+ override void checkTupleof(Expression expression, TypeClass type) const
+ {
+ // noop
+ }
+}
+
+extern(C++) private final class Supported : Objc
+{
+ extern(D) final this()
+ {
+ VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC");
+
+ ObjcGlue.initialize();
+ ObjcSelector._init();
+ }
+
+ override void setObjc(ClassDeclaration cd)
+ {
+ cd.classKind = ClassKind.objc;
+ cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0;
+ }
+
+ override void setObjc(InterfaceDeclaration id)
+ {
+ id.classKind = ClassKind.objc;
+ id.objc.isExtern = true;
+ }
+
+ override const(char)* toPrettyChars(ClassDeclaration cd, bool qualifyTypes) const
+ {
+ return cd.parent.toPrettyChars(qualifyTypes);
+ }
+
+ override void setSelector(FuncDeclaration fd, Scope* sc)
+ {
+ foreachUda(fd, sc, (e) {
+ if (e.op != TOK.structLiteral)
+ return 0;
+
+ auto literal = cast(StructLiteralExp) e;
+ assert(literal.sd);
+
+ if (!isCoreUda(literal.sd, Id.udaSelector))
+ return 0;
+
+ if (fd.objc.selector)
+ {
+ fd.error("can only have one Objective-C selector per method");
+ return 1;
+ }
+
+ assert(literal.elements.dim == 1);
+ auto se = (*literal.elements)[0].toStringExp();
+ assert(se);
+
+ fd.objc.selector = ObjcSelector.lookup(se.toUTF8(sc).peekString().ptr);
+
+ return 0;
+ });
+ }
+
+ override void validateSelector(FuncDeclaration fd)
+ {
+ if (!fd.objc.selector)
+ return;
+ TypeFunction tf = cast(TypeFunction)fd.type;
+ if (fd.objc.selector.paramCount != tf.parameterList.parameters.dim)
+ fd.error("number of colons in Objective-C selector must match number of parameters");
+ if (fd.parent && fd.parent.isTemplateInstance())
+ fd.error("template cannot have an Objective-C selector attached");
+ }
+
+ override void checkLinkage(FuncDeclaration fd)
+ {
+ if (fd.linkage != LINK.objc && fd.objc.selector)
+ fd.error("must have Objective-C linkage to attach a selector");
+ }
+
+ override bool isVirtual(const FuncDeclaration fd) const
+ in
+ {
+ assert(fd.selector);
+ assert(fd.isMember);
+ }
+ do
+ {
+ if (fd.toParent.isInterfaceDeclaration && fd.isFinal)
+ return false;
+
+ // * final member functions are kept virtual with Objective-C linkage
+ // because the Objective-C runtime always use dynamic dispatch.
+ // * static member functions are kept virtual too, as they represent
+ // methods of the metaclass.
+ with (fd.visibility)
+ return !(kind == Visibility.Kind.private_ || kind == Visibility.Kind.package_);
+ }
+
+ override void setAsOptional(FuncDeclaration fd, Scope* sc) const
+ {
+ const count = declaredAsOptionalCount(fd, sc);
+ fd.objc.isOptional = count > 0;
+
+ if (count > 1)
+ fd.error("can only declare a function as optional once");
+ }
+
+ /// Returns: the number of times `fd` has been declared as optional.
+ private int declaredAsOptionalCount(FuncDeclaration fd , Scope* sc) const
+ {
+ int count;
+
+ foreachUda(fd, sc, (e) {
+ if (e.op != TOK.type)
+ return 0;
+
+ auto typeExp = cast(TypeExp) e;
+
+ if (typeExp.type.ty != Tenum)
+ return 0;
+
+ auto typeEnum = cast(TypeEnum) typeExp.type;
+
+ if (isCoreUda(typeEnum.sym, Id.udaOptional))
+ count++;
+
+ return 0;
+ });
+
+ return count;
+ }
+
+ override void validateOptional(FuncDeclaration fd) const
+ {
+ if (!fd.objc.isOptional)
+ return;
+
+ if (fd.linkage != LINK.objc)
+ {
+ fd.error("only functions with Objective-C linkage can be declared as optional");
+
+ const linkage = linkageToString(fd.linkage);
+
+ errorSupplemental(fd.loc, "function is declared with %.*s linkage",
+ cast(uint) linkage.length, linkage.ptr);
+ }
+
+ auto parent = fd.parent;
+
+ if (parent && parent.isTemplateInstance())
+ {
+ fd.error("template cannot be optional");
+ parent = parent.parent;
+ assert(parent);
+ }
+
+ if (parent && !parent.isInterfaceDeclaration())
+ {
+ fd.error("only functions declared inside interfaces can be optional");
+ errorSupplemental(fd.loc, "function is declared inside %s", fd.parent.kind);
+ }
+ }
+
+ override ClassDeclaration getParent(FuncDeclaration fd, ClassDeclaration cd) const
+ out(metaclass)
+ {
+ assert(metaclass);
+ }
+ do
+ {
+ if (cd.classKind == ClassKind.objc && fd.isStatic && !cd.objc.isMeta)
+ return cd.objc.metaclass;
+ else
+ return cd;
+ }
+
+ override void addToClassMethodList(FuncDeclaration fd, ClassDeclaration cd) const
+ in
+ {
+ assert(fd.parent.isClassDeclaration);
+ }
+ do
+ {
+ if (cd.classKind != ClassKind.objc)
+ return;
+
+ if (!fd.objc.selector)
+ return;
+
+ assert(fd.isStatic ? cd.objc.isMeta : !cd.objc.isMeta);
+
+ cd.objc.methodList ~= fd;
+ }
+
+ override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const
+ {
+ with(funcDeclaration)
+ {
+ if (!objc.selector)
+ return null;
+
+ // Use Objective-C class object as 'this'
+ auto cd = isMember2().isClassDeclaration();
+
+ if (cd.classKind == ClassKind.objc)
+ {
+ if (!cd.objc.isMeta)
+ return cd.objc.metaclass;
+ }
+
+ return null;
+ }
+ }
+
+ override VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const
+ in
+ {
+ assert(fd.selectorParameter is null);
+ }
+ do
+ {
+ if (!fd.objc.selector)
+ return null;
+
+ auto ident = Identifier.generateAnonymousId("_cmd");
+ auto var = new VarDeclaration(fd.loc, Type.tvoidptr, ident, null);
+ var.storage_class |= STC.parameter;
+ var.dsymbolSemantic(sc);
+ if (!sc.insert(var))
+ assert(false);
+ var.parent = fd;
+
+ return var;
+ }
+
+ override void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const
+ {
+ auto newMetaclass(Loc loc, BaseClasses* metaBases)
+ {
+ auto ident = createMetaclassIdentifier(interfaceDeclaration);
+ return new InterfaceDeclaration(loc, ident, metaBases);
+ }
+
+ .setMetaclass!newMetaclass(interfaceDeclaration, sc);
+ }
+
+ override void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const
+ {
+ auto newMetaclass(Loc loc, BaseClasses* metaBases)
+ {
+ auto ident = createMetaclassIdentifier(classDeclaration);
+ return new ClassDeclaration(loc, ident, metaBases, new Dsymbols(), 0);
+ }
+
+ .setMetaclass!newMetaclass(classDeclaration, sc);
+ }
+
+ override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const
+ {
+ if (!classDeclaration.objc.metaclass && classDeclaration.objc.isMeta)
+ {
+ if (classDeclaration.baseClass)
+ return getRuntimeMetaclass(classDeclaration.baseClass);
+ else
+ return classDeclaration;
+ }
+ else
+ return classDeclaration.objc.metaclass;
+ }
+
+ override void addSymbols(AttribDeclaration attribDeclaration,
+ ClassDeclarations* classes, ClassDeclarations* categories) const
+ {
+ auto symbols = attribDeclaration.include(null);
+
+ if (!symbols)
+ return;
+
+ foreach (symbol; *symbols)
+ symbol.addObjcSymbols(classes, categories);
+ }
+
+ override void addSymbols(ClassDeclaration classDeclaration,
+ ClassDeclarations* classes, ClassDeclarations* categories) const
+ {
+ with (classDeclaration)
+ if (classKind == ClassKind.objc && !objc.isExtern && !objc.isMeta)
+ classes.push(classDeclaration);
+ }
+
+ override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const
+ {
+ if (aggregateDeclaration.classKind != ClassKind.objc)
+ return;
+
+ enum errorMessage = "no property `offsetof` for member `%s` of type " ~
+ "`%s`";
+
+ enum supplementalMessage = "`offsetof` is not available for members " ~
+ "of Objective-C classes. Please use the Objective-C runtime instead";
+
+ expression.error(errorMessage, expression.toChars(),
+ expression.type.toChars());
+ expression.errorSupplemental(supplementalMessage);
+ }
+
+ override void checkTupleof(Expression expression, TypeClass type) const
+ {
+ if (type.sym.classKind != ClassKind.objc)
+ return;
+
+ expression.error("no property `tupleof` for type `%s`", type.toChars());
+ expression.errorSupplemental("`tupleof` is not available for members " ~
+ "of Objective-C classes. Please use the Objective-C runtime instead");
+ }
+
+extern(D) private:
+
+ /**
+ * Returns `true` if the given symbol is a symbol declared in
+ * `core.attribute` and has the given identifier.
+ *
+ * This is used to determine if a symbol is a UDA declared in
+ * `core.attribute`.
+ *
+ * Params:
+ * sd = the symbol to check
+ * ident = the name of the expected UDA
+ */
+ bool isCoreUda(ScopeDsymbol sd, Identifier ident) const
+ {
+ if (sd.ident != ident || !sd.parent)
+ return false;
+
+ auto _module = sd.parent.isModule();
+ return _module && _module.isCoreModule(Id.attribute);
+ }
+
+ /**
+ * Iterates the UDAs attached to the given function declaration.
+ *
+ * If `dg` returns `!= 0`, it will stop the iteration and return that
+ * value, otherwise it will return 0.
+ *
+ * Params:
+ * fd = the function declaration to get the UDAs from
+ * dg = called once for each UDA. If `dg` returns `!= 0`, it will stop the
+ * iteration and return that value, otherwise it will return `0`.
+ */
+ int foreachUda(FuncDeclaration fd, Scope* sc, int delegate(Expression) dg) const
+ {
+ if (!fd.userAttribDecl)
+ return 0;
+
+ auto udas = fd.userAttribDecl.getAttributes();
+ arrayExpressionSemantic(udas, sc, true);
+
+ return udas.each!((uda) {
+ if (uda.op != TOK.tuple)
+ return 0;
+
+ auto exps = (cast(TupleExp) uda).exps;
+
+ return exps.each!((e) {
+ assert(e);
+
+ if (auto result = dg(e))
+ return result;
+
+ return 0;
+ });
+ });
+ }
+}
+
+/*
+ * Creates and sets the metaclass on the given class/interface declaration.
+ *
+ * Will only be performed on regular Objective-C classes, not on metaclasses.
+ *
+ * Params:
+ * newMetaclass = a function that returns the metaclass to set. This should
+ * return the same type as `T`.
+ * classDeclaration = the class/interface declaration to set the metaclass on
+ */
+private void setMetaclass(alias newMetaclass, T)(T classDeclaration, Scope* sc)
+if (is(T == ClassDeclaration) || is(T == InterfaceDeclaration))
+{
+ static if (is(T == ClassDeclaration))
+ enum errorType = "class";
+ else
+ enum errorType = "interface";
+
+ with (classDeclaration)
+ {
+ if (classKind != ClassKind.objc || objc.isMeta || objc.metaclass)
+ return;
+
+ if (!objc.identifier)
+ objc.identifier = classDeclaration.ident;
+
+ auto metaBases = new BaseClasses();
+
+ foreach (base ; baseclasses.opSlice)
+ {
+ auto baseCd = base.sym;
+ assert(baseCd);
+
+ if (baseCd.classKind == ClassKind.objc)
+ {
+ assert(baseCd.objc.metaclass);
+ assert(baseCd.objc.metaclass.objc.isMeta);
+ assert(baseCd.objc.metaclass.type.ty == Tclass);
+
+ auto metaBase = new BaseClass(baseCd.objc.metaclass.type);
+ metaBase.sym = baseCd.objc.metaclass;
+ metaBases.push(metaBase);
+ }
+ else
+ {
+ error("base " ~ errorType ~ " for an Objective-C " ~
+ errorType ~ " must be `extern (Objective-C)`");
+ }
+ }
+
+ objc.metaclass = newMetaclass(loc, metaBases);
+ objc.metaclass.storage_class |= STC.static_;
+ objc.metaclass.classKind = ClassKind.objc;
+ objc.metaclass.objc.isMeta = true;
+ objc.metaclass.objc.isExtern = objc.isExtern;
+ objc.metaclass.objc.identifier = objc.identifier;
+
+ if (baseClass)
+ objc.metaclass.baseClass = baseClass.objc.metaclass;
+
+ members.push(objc.metaclass);
+ objc.metaclass.addMember(sc, classDeclaration);
+
+ objc.metaclass.members = new Dsymbols();
+ objc.metaclass.dsymbolSemantic(sc);
+ }
+}
+
+private Identifier createMetaclassIdentifier(ClassDeclaration classDeclaration)
+{
+ const name = "class_" ~ classDeclaration.ident.toString ~ "_Meta";
+ return Identifier.generateAnonymousId(name);
+}
diff --git a/gcc/d/dmd/objc.h b/gcc/d/dmd/objc.h
index f3da5a3755f..483e50149e7 100644
--- a/gcc/d/dmd/objc.h
+++ b/gcc/d/dmd/objc.h
@@ -10,22 +10,20 @@
#pragma once
-#include "root/root.h"
-#include "root/stringtable.h"
+#include "root/dsystem.h"
+#include "arraytypes.h"
-class Identifier;
-class FuncDeclaration;
+class AggregateDeclaration;
+class AttribDeclaration;
class ClassDeclaration;
+class FuncDeclaration;
+class Identifier;
class InterfaceDeclaration;
+
struct Scope;
-class StructDeclaration;
struct ObjcSelector
{
- static StringTable stringtable;
- static StringTable vTableDispatchSelectors;
- static int incnum;
-
const char *stringvalue;
size_t stringlen;
size_t paramCount;
@@ -34,12 +32,29 @@ struct ObjcSelector
ObjcSelector(const char *sv, size_t len, size_t pcount);
- static ObjcSelector *lookup(const char *s);
- static ObjcSelector *lookup(const char *s, size_t len, size_t pcount);
-
static ObjcSelector *create(FuncDeclaration *fdecl);
};
+struct ObjcClassDeclaration
+{
+ bool isMeta;
+ bool isExtern;
+
+ Identifier* identifier;
+ ClassDeclaration* classDeclaration;
+ ClassDeclaration* metaclass;
+ DArray<FuncDeclaration*> methodList;
+
+ bool isRootClass() const;
+};
+
+struct ObjcFuncDeclaration
+{
+ ObjcSelector* selector;
+ VarDeclaration* selectorParameter;
+ bool isOptional;
+};
+
class Objc
{
public:
@@ -47,7 +62,23 @@ public:
virtual void setObjc(ClassDeclaration* cd) = 0;
virtual void setObjc(InterfaceDeclaration*) = 0;
+ virtual const char *toPrettyChars(ClassDeclaration *cd, bool qualifyTypes) const = 0;
+
virtual void setSelector(FuncDeclaration*, Scope* sc) = 0;
virtual void validateSelector(FuncDeclaration* fd) = 0;
virtual void checkLinkage(FuncDeclaration* fd) = 0;
+ virtual bool isVirtual(const FuncDeclaration*) const = 0;
+ virtual void setAsOptional(FuncDeclaration *fd, Scope *sc) const = 0;
+ virtual void validateOptional(FuncDeclaration *fd) const = 0;
+ virtual ClassDeclaration* getParent(FuncDeclaration*, ClassDeclaration*) const = 0;
+ virtual void addToClassMethodList(FuncDeclaration*, ClassDeclaration*) const = 0;
+ virtual AggregateDeclaration* isThis(FuncDeclaration* fd) = 0;
+ virtual VarDeclaration* createSelectorParameter(FuncDeclaration*, Scope*) const = 0;
+
+ virtual void setMetaclass(InterfaceDeclaration* id, Scope*) const = 0;
+ virtual void setMetaclass(ClassDeclaration* id, Scope*) const = 0;
+ virtual ClassDeclaration* getRuntimeMetaclass(ClassDeclaration* cd) = 0;
+
+ virtual void addSymbols(AttribDeclaration*, ClassDeclarations*, ClassDeclarations*) const = 0;
+ virtual void addSymbols(ClassDeclaration*, ClassDeclarations*, ClassDeclarations*) const = 0;
};
diff --git a/gcc/d/dmd/opover.c b/gcc/d/dmd/opover.c
deleted file mode 100644
index 0aff8b43551..00000000000
--- a/gcc/d/dmd/opover.c
+++ /dev/null
@@ -1,1960 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/opover.c
- */
-
-#include "root/dsystem.h" // memset()
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "mtype.h"
-#include "init.h"
-#include "expression.h"
-#include "statement.h"
-#include "scope.h"
-#include "id.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "template.h"
-#include "tokens.h"
-
-static Dsymbol *inferApplyArgTypesX(Expression *ethis, FuncDeclaration *fstart, Parameters *parameters);
-static int inferApplyArgTypesY(TypeFunction *tf, Parameters *parameters, int flags = 0);
-Expression *compare_overload(BinExp *e, Scope *sc, Identifier *id);
-bool MODimplicitConv(MOD modfrom, MOD modto);
-
-/******************************** Expression **************************/
-
-
-/***********************************
- * Determine if operands of binary op can be reversed
- * to fit operator overload.
- */
-
-bool isCommutative(TOK op)
-{
- switch (op)
- {
- case TOKadd:
- case TOKmul:
- case TOKand:
- case TOKor:
- case TOKxor:
-
- // EqualExp
- case TOKequal:
- case TOKnotequal:
-
- // CmpExp
- case TOKlt:
- case TOKle:
- case TOKgt:
- case TOKge:
- return true;
-
- default:
- break;
- }
- return false;
-}
-
-/***********************************
- * Get Identifier for operator overload.
- */
-
-static Identifier *opId(Expression *e)
-{
- class OpIdVisitor : public Visitor
- {
- public:
- Identifier *id;
- void visit(Expression *) { assert(0); }
- void visit(UAddExp *) { id = Id::uadd; }
- void visit(NegExp *) { id = Id::neg; }
- void visit(ComExp *) { id = Id::com; }
- void visit(CastExp *) { id = Id::_cast; }
- void visit(InExp *) { id = Id::opIn; }
- void visit(PostExp *e) { id = (e->op == TOKplusplus) ? Id::postinc : Id::postdec; }
- void visit(AddExp *) { id = Id::add; }
- void visit(MinExp *) { id = Id::sub; }
- void visit(MulExp *) { id = Id::mul; }
- void visit(DivExp *) { id = Id::div; }
- void visit(ModExp *) { id = Id::mod; }
- void visit(PowExp *) { id = Id::pow; }
- void visit(ShlExp *) { id = Id::shl; }
- void visit(ShrExp *) { id = Id::shr; }
- void visit(UshrExp *) { id = Id::ushr; }
- void visit(AndExp *) { id = Id::iand; }
- void visit(OrExp *) { id = Id::ior; }
- void visit(XorExp *) { id = Id::ixor; }
- void visit(CatExp *) { id = Id::cat; }
- void visit(AssignExp *) { id = Id::assign; }
- void visit(AddAssignExp *) { id = Id::addass; }
- void visit(MinAssignExp *) { id = Id::subass; }
- void visit(MulAssignExp *) { id = Id::mulass; }
- void visit(DivAssignExp *) { id = Id::divass; }
- void visit(ModAssignExp *) { id = Id::modass; }
- void visit(AndAssignExp *) { id = Id::andass; }
- void visit(OrAssignExp *) { id = Id::orass; }
- void visit(XorAssignExp *) { id = Id::xorass; }
- void visit(ShlAssignExp *) { id = Id::shlass; }
- void visit(ShrAssignExp *) { id = Id::shrass; }
- void visit(UshrAssignExp *) { id = Id::ushrass; }
- void visit(CatAssignExp *) { id = Id::catass; }
- void visit(PowAssignExp *) { id = Id::powass; }
- void visit(EqualExp *) { id = Id::eq; }
- void visit(CmpExp *) { id = Id::cmp; }
- void visit(ArrayExp *) { id = Id::index; }
- void visit(PtrExp *) { id = Id::opStar; }
- };
- OpIdVisitor v;
- e->accept(&v);
- return v.id;
-}
-
-/***********************************
- * Get Identifier for reverse operator overload,
- * NULL if not supported for this operator.
- */
-
-static Identifier *opId_r(Expression *e)
-{
- class OpIdRVisitor : public Visitor
- {
- public:
- Identifier *id;
- void visit(Expression *) { id = NULL; }
- void visit(InExp *) { id = Id::opIn_r; }
- void visit(AddExp *) { id = Id::add_r; }
- void visit(MinExp *) { id = Id::sub_r; }
- void visit(MulExp *) { id = Id::mul_r; }
- void visit(DivExp *) { id = Id::div_r; }
- void visit(ModExp *) { id = Id::mod_r; }
- void visit(PowExp *) { id = Id::pow_r; }
- void visit(ShlExp *) { id = Id::shl_r; }
- void visit(ShrExp *) { id = Id::shr_r; }
- void visit(UshrExp *) { id = Id::ushr_r; }
- void visit(AndExp *) { id = Id::iand_r; }
- void visit(OrExp *) { id = Id::ior_r; }
- void visit(XorExp *) { id = Id::ixor_r; }
- void visit(CatExp *) { id = Id::cat_r; }
- };
- OpIdRVisitor v;
- e->accept(&v);
- return v.id;
-}
-
-/************************************
- * If type is a class or struct, return the symbol for it,
- * else NULL
- */
-AggregateDeclaration *isAggregate(Type *t)
-{
- t = t->toBasetype();
- if (t->ty == Tclass)
- {
- return ((TypeClass *)t)->sym;
- }
- else if (t->ty == Tstruct)
- {
- return ((TypeStruct *)t)->sym;
- }
- return NULL;
-}
-
-/*******************************************
- * Helper function to turn operator into template argument list
- */
-Objects *opToArg(Scope *sc, TOK op)
-{
- /* Remove the = from op=
- */
- switch (op)
- {
- case TOKaddass: op = TOKadd; break;
- case TOKminass: op = TOKmin; break;
- case TOKmulass: op = TOKmul; break;
- case TOKdivass: op = TOKdiv; break;
- case TOKmodass: op = TOKmod; break;
- case TOKandass: op = TOKand; break;
- case TOKorass: op = TOKor; break;
- case TOKxorass: op = TOKxor; break;
- case TOKshlass: op = TOKshl; break;
- case TOKshrass: op = TOKshr; break;
- case TOKushrass: op = TOKushr; break;
- case TOKcatass: op = TOKcat; break;
- case TOKpowass: op = TOKpow; break;
- default: break;
- }
- Expression *e = new StringExp(Loc(), const_cast<char *>(Token::toChars(op)));
- e = expressionSemantic(e, sc);
- Objects *tiargs = new Objects();
- tiargs->push(e);
- return tiargs;
-}
-
-/************************************
- * Operator overload.
- * Check for operator overload, if so, replace
- * with function call.
- * Return NULL if not an operator overload.
- */
-
-Expression *op_overload(Expression *e, Scope *sc)
-{
- class OpOverload : public Visitor
- {
- public:
- Scope *sc;
- Expression *result;
-
- OpOverload(Scope *sc)
- : sc(sc)
- {
- result = NULL;
- }
-
- void visit(Expression *)
- {
- assert(0);
- }
-
- void visit(UnaExp *e)
- {
- //printf("UnaExp::op_overload() (%s)\n", e->toChars());
-
- if (e->e1->op == TOKarray)
- {
- ArrayExp *ae = (ArrayExp *)e->e1;
- ae->e1 = expressionSemantic(ae->e1, sc);
- ae->e1 = resolveProperties(sc, ae->e1);
- Expression *ae1old = ae->e1;
-
- const bool maybeSlice =
- (ae->arguments->length == 0 ||
- (ae->arguments->length == 1 && (*ae->arguments)[0]->op == TOKinterval));
- IntervalExp *ie = NULL;
- if (maybeSlice && ae->arguments->length)
- {
- assert((*ae->arguments)[0]->op == TOKinterval);
- ie = (IntervalExp *)(*ae->arguments)[0];
- }
-
- while (true)
- {
- if (ae->e1->op == TOKerror)
- {
- result = ae->e1;
- return;
- }
- Expression *e0 = NULL;
- Expression *ae1save = ae->e1;
- ae->lengthVar = NULL;
-
- Type *t1b = ae->e1->type->toBasetype();
- AggregateDeclaration *ad = isAggregate(t1b);
- if (!ad)
- break;
- if (search_function(ad, Id::opIndexUnary))
- {
- // Deal with $
- result = resolveOpDollar(sc, ae, &e0);
- if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
- goto Lfallback;
- if (result->op == TOKerror)
- return;
-
- /* Rewrite op(a[arguments]) as:
- * a.opIndexUnary!(op)(arguments)
- */
- Expressions *a = (Expressions *)ae->arguments->copy();
- Objects *tiargs = opToArg(sc, e->op);
- result = new DotTemplateInstanceExp(e->loc, ae->e1, Id::opIndexUnary, tiargs);
- result = new CallExp(e->loc, result, a);
- if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)()
- result = trySemantic(result, sc);
- else
- result = expressionSemantic(result, sc);
- if (result)
- {
- result = Expression::combine(e0, result);
- return;
- }
- }
- Lfallback:
- if (maybeSlice && search_function(ad, Id::opSliceUnary))
- {
- // Deal with $
- result = resolveOpDollar(sc, ae, ie, &e0);
- if (result->op == TOKerror)
- return;
-
- /* Rewrite op(a[i..j]) as:
- * a.opSliceUnary!(op)(i, j)
- */
- Expressions *a = new Expressions();
- if (ie)
- {
- a->push(ie->lwr);
- a->push(ie->upr);
- }
- Objects *tiargs = opToArg(sc, e->op);
- result = new DotTemplateInstanceExp(e->loc, ae->e1, Id::opSliceUnary, tiargs);
- result = new CallExp(e->loc, result, a);
- result = expressionSemantic(result, sc);
- result = Expression::combine(e0, result);
- return;
- }
-
- // Didn't find it. Forward to aliasthis
- if (ad->aliasthis && t1b != ae->att1)
- {
- if (!ae->att1 && t1b->checkAliasThisRec())
- ae->att1 = t1b;
-
- /* Rewrite op(a[arguments]) as:
- * op(a.aliasthis[arguments])
- */
- ae->e1 = resolveAliasThis(sc, ae1save, true);
- if (ae->e1)
- continue;
- }
- break;
- }
- ae->e1 = ae1old; // recovery
- ae->lengthVar = NULL;
- }
-
- e->e1 = expressionSemantic(e->e1, sc);
- e->e1 = resolveProperties(sc, e->e1);
- if (e->e1->op == TOKerror)
- {
- result = e->e1;
- return;
- }
-
- AggregateDeclaration *ad = isAggregate(e->e1->type);
- if (ad)
- {
- Dsymbol *fd = NULL;
- #if 1 // Old way, kept for compatibility with D1
- if (e->op != TOKpreplusplus && e->op != TOKpreminusminus)
- {
- fd = search_function(ad, opId(e));
- if (fd)
- {
- // Rewrite +e1 as e1.add()
- result = build_overload(e->loc, sc, e->e1, NULL, fd);
- return;
- }
- }
- #endif
-
- /* Rewrite as:
- * e1.opUnary!(op)()
- */
- fd = search_function(ad, Id::opUnary);
- if (fd)
- {
- Objects *tiargs = opToArg(sc, e->op);
- result = new DotTemplateInstanceExp(e->loc, e->e1, fd->ident, tiargs);
- result = new CallExp(e->loc, result);
- result = expressionSemantic(result, sc);
- return;
- }
-
- // Didn't find it. Forward to aliasthis
- if (ad->aliasthis && e->e1->type != e->att1)
- {
- /* Rewrite op(e1) as:
- * op(e1.aliasthis)
- */
- //printf("att una %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars());
- Expression *e1 = new DotIdExp(e->loc, e->e1, ad->aliasthis->ident);
- UnaExp *ue = (UnaExp *)e->copy();
- if (!ue->att1 && e->e1->type->checkAliasThisRec())
- ue->att1 = e->e1->type;
- ue->e1 = e1;
- result = trySemantic(ue, sc);
- return;
- }
- }
- }
-
- void visit(ArrayExp *ae)
- {
- //printf("ArrayExp::op_overload() (%s)\n", ae->toChars());
- ae->e1 = expressionSemantic(ae->e1, sc);
- ae->e1 = resolveProperties(sc, ae->e1);
- Expression *ae1old = ae->e1;
-
- const bool maybeSlice =
- (ae->arguments->length == 0 ||
- (ae->arguments->length == 1 && (*ae->arguments)[0]->op == TOKinterval));
- IntervalExp *ie = NULL;
- if (maybeSlice && ae->arguments->length)
- {
- assert((*ae->arguments)[0]->op == TOKinterval);
- ie = (IntervalExp *)(*ae->arguments)[0];
- }
-
- while (true)
- {
- if (ae->e1->op == TOKerror)
- {
- result = ae->e1;
- return;
- }
- Expression *e0 = NULL;
- Expression *ae1save = ae->e1;
- ae->lengthVar = NULL;
-
- Type *t1b = ae->e1->type->toBasetype();
- AggregateDeclaration *ad = isAggregate(t1b);
- if (!ad)
- {
- // If the non-aggregate expression ae->e1 is indexable or sliceable,
- // convert it to the corresponding concrete expression.
- if (t1b->ty == Tpointer ||
- t1b->ty == Tsarray ||
- t1b->ty == Tarray ||
- t1b->ty == Taarray ||
- t1b->ty == Ttuple ||
- t1b->ty == Tvector ||
- ae->e1->op == TOKtype)
- {
- // Convert to SliceExp
- if (maybeSlice)
- {
- result = new SliceExp(ae->loc, ae->e1, ie);
- result = expressionSemantic(result, sc);
- return;
- }
- // Convert to IndexExp
- if (ae->arguments->length == 1)
- {
- result = new IndexExp(ae->loc, ae->e1, (*ae->arguments)[0]);
- result = expressionSemantic(result, sc);
- return;
- }
- }
- break;
- }
- if (search_function(ad, Id::index))
- {
- // Deal with $
- result = resolveOpDollar(sc, ae, &e0);
- if (!result) // a[i..j] might be: a.opSlice(i, j)
- goto Lfallback;
- if (result->op == TOKerror)
- return;
-
- /* Rewrite e1[arguments] as:
- * e1.opIndex(arguments)
- */
- Expressions *a = (Expressions *)ae->arguments->copy();
- result = new DotIdExp(ae->loc, ae->e1, Id::index);
- result = new CallExp(ae->loc, result, a);
- if (maybeSlice) // a[] might be: a.opSlice()
- result = trySemantic(result, sc);
- else
- result = expressionSemantic(result, sc);
- if (result)
- {
- result = Expression::combine(e0, result);
- return;
- }
- }
- Lfallback:
- if (maybeSlice && ae->e1->op == TOKtype)
- {
- result = new SliceExp(ae->loc, ae->e1, ie);
- result = expressionSemantic(result, sc);
- result = Expression::combine(e0, result);
- return;
- }
- if (maybeSlice && search_function(ad, Id::slice))
- {
- // Deal with $
- result = resolveOpDollar(sc, ae, ie, &e0);
- if (result->op == TOKerror)
- return;
-
- /* Rewrite a[i..j] as:
- * a.opSlice(i, j)
- */
- Expressions *a = new Expressions();
- if (ie)
- {
- a->push(ie->lwr);
- a->push(ie->upr);
- }
- result = new DotIdExp(ae->loc, ae->e1, Id::slice);
- result = new CallExp(ae->loc, result, a);
- result = expressionSemantic(result, sc);
- result = Expression::combine(e0, result);
- return;
- }
-
- // Didn't find it. Forward to aliasthis
- if (ad->aliasthis && t1b != ae->att1)
- {
- if (!ae->att1 && t1b->checkAliasThisRec())
- ae->att1 = t1b;
- //printf("att arr e1 = %s\n", this->e1->type->toChars());
-
- /* Rewrite op(a[arguments]) as:
- * op(a.aliasthis[arguments])
- */
- ae->e1 = resolveAliasThis(sc, ae1save, true);
- if (ae->e1)
- continue;
- }
- break;
- }
- ae->e1 = ae1old; // recovery
- ae->lengthVar = NULL;
- }
-
- /***********************************************
- * This is mostly the same as UnaryExp::op_overload(), but has
- * a different rewrite.
- */
- void visit(CastExp *e)
- {
- //printf("CastExp::op_overload() (%s)\n", e->toChars());
- AggregateDeclaration *ad = isAggregate(e->e1->type);
- if (ad)
- {
- Dsymbol *fd = NULL;
- /* Rewrite as:
- * e1.opCast!(T)()
- */
- fd = search_function(ad, Id::_cast);
- if (fd)
- {
- #if 1 // Backwards compatibility with D1 if opCast is a function, not a template
- if (fd->isFuncDeclaration())
- {
- // Rewrite as: e1.opCast()
- result = build_overload(e->loc, sc, e->e1, NULL, fd);
- return;
- }
- #endif
- Objects *tiargs = new Objects();
- tiargs->push(e->to);
- result = new DotTemplateInstanceExp(e->loc, e->e1, fd->ident, tiargs);
- result = new CallExp(e->loc, result);
- result = expressionSemantic(result, sc);
- return;
- }
-
- // Didn't find it. Forward to aliasthis
- if (ad->aliasthis)
- {
- /* Rewrite op(e1) as:
- * op(e1.aliasthis)
- */
- Expression *e1 = new DotIdExp(e->loc, e->e1, ad->aliasthis->ident);
- result = e->copy();
- ((UnaExp *)result)->e1 = e1;
- result = trySemantic(result, sc);
- return;
- }
- }
- }
-
- void visit(BinExp *e)
- {
- //printf("BinExp::op_overload() (%s)\n", e->toChars());
-
- Identifier *id = opId(e);
- Identifier *id_r = opId_r(e);
-
- Expressions args1;
- Expressions args2;
- int argsset = 0;
-
- AggregateDeclaration *ad1 = isAggregate(e->e1->type);
- AggregateDeclaration *ad2 = isAggregate(e->e2->type);
-
- if (e->op == TOKassign && ad1 == ad2)
- {
- StructDeclaration *sd = ad1->isStructDeclaration();
- if (sd && !sd->hasIdentityAssign)
- {
- /* This is bitwise struct assignment. */
- return;
- }
- }
-
- Dsymbol *s = NULL;
- Dsymbol *s_r = NULL;
-
- #if 1 // the old D1 scheme
- if (ad1 && id)
- {
- s = search_function(ad1, id);
- }
- if (ad2 && id_r)
- {
- s_r = search_function(ad2, id_r);
-
- // Bugzilla 12778: If both x.opBinary(y) and y.opBinaryRight(x) found,
- // and they are exactly same symbol, x.opBinary(y) should be preferred.
- if (s_r && s_r == s)
- s_r = NULL;
- }
- #endif
-
- Objects *tiargs = NULL;
- if (e->op == TOKplusplus || e->op == TOKminusminus)
- {
- // Bug4099 fix
- if (ad1 && search_function(ad1, Id::opUnary))
- return;
- }
- if (!s && !s_r && e->op != TOKequal && e->op != TOKnotequal && e->op != TOKassign &&
- e->op != TOKplusplus && e->op != TOKminusminus)
- {
- /* Try the new D2 scheme, opBinary and opBinaryRight
- */
- if (ad1)
- {
- s = search_function(ad1, Id::opBinary);
- if (s && !s->isTemplateDeclaration())
- {
- e->e1->error("%s.opBinary isn't a template", e->e1->toChars());
- result = new ErrorExp();
- return;
- }
- }
- if (ad2)
- {
- s_r = search_function(ad2, Id::opBinaryRight);
- if (s_r && !s_r->isTemplateDeclaration())
- {
- e->e2->error("%s.opBinaryRight isn't a template", e->e2->toChars());
- result = new ErrorExp();
- return;
- }
- if (s_r && s_r == s) // Bugzilla 12778
- s_r = NULL;
- }
-
- // Set tiargs, the template argument list, which will be the operator string
- if (s || s_r)
- {
- id = Id::opBinary;
- id_r = Id::opBinaryRight;
- tiargs = opToArg(sc, e->op);
- }
- }
-
- if (s || s_r)
- {
- /* Try:
- * a.opfunc(b)
- * b.opfunc_r(a)
- * and see which is better.
- */
-
- args1.setDim(1);
- args1[0] = e->e1;
- expandTuples(&args1);
- args2.setDim(1);
- args2[0] = e->e2;
- expandTuples(&args2);
- argsset = 1;
-
- Match m;
- memset(&m, 0, sizeof(m));
- m.last = MATCHnomatch;
-
- if (s)
- {
- functionResolve(&m, s, e->loc, sc, tiargs, e->e1->type, &args2);
- if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors))
- {
- result = new ErrorExp();
- return;
- }
- }
-
- FuncDeclaration *lastf = m.lastf;
-
- if (s_r)
- {
- functionResolve(&m, s_r, e->loc, sc, tiargs, e->e2->type, &args1);
- if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors))
- {
- result = new ErrorExp();
- return;
- }
- }
-
- if (m.count > 1)
- {
- // Error, ambiguous
- e->error("overloads %s and %s both match argument list for %s",
- m.lastf->type->toChars(),
- m.nextf->type->toChars(),
- m.lastf->toChars());
- }
- else if (m.last <= MATCHnomatch)
- {
- m.lastf = m.anyf;
- if (tiargs)
- goto L1;
- }
-
- if (e->op == TOKplusplus || e->op == TOKminusminus)
- {
- // Kludge because operator overloading regards e++ and e--
- // as unary, but it's implemented as a binary.
- // Rewrite (e1 ++ e2) as e1.postinc()
- // Rewrite (e1 -- e2) as e1.postdec()
- result = build_overload(e->loc, sc, e->e1, NULL, m.lastf ? m.lastf : s);
- }
- else if ((lastf && m.lastf == lastf) || (!s_r && m.last <= MATCHnomatch))
- {
- // Rewrite (e1 op e2) as e1.opfunc(e2)
- result = build_overload(e->loc, sc, e->e1, e->e2, m.lastf ? m.lastf : s);
- }
- else
- {
- // Rewrite (e1 op e2) as e2.opfunc_r(e1)
- result = build_overload(e->loc, sc, e->e2, e->e1, m.lastf ? m.lastf : s_r);
- }
- return;
- }
-
- L1:
- #if 1 // Retained for D1 compatibility
- if (isCommutative(e->op) && !tiargs)
- {
- s = NULL;
- s_r = NULL;
- if (ad1 && id_r)
- {
- s_r = search_function(ad1, id_r);
- }
- if (ad2 && id)
- {
- s = search_function(ad2, id);
- if (s && s == s_r) // Bugzilla 12778
- s = NULL;
- }
-
- if (s || s_r)
- {
- /* Try:
- * a.opfunc_r(b)
- * b.opfunc(a)
- * and see which is better.
- */
-
- if (!argsset)
- {
- args1.setDim(1);
- args1[0] = e->e1;
- expandTuples(&args1);
- args2.setDim(1);
- args2[0] = e->e2;
- expandTuples(&args2);
- }
-
- Match m;
- memset(&m, 0, sizeof(m));
- m.last = MATCHnomatch;
-
- if (s_r)
- {
- functionResolve(&m, s_r, e->loc, sc, tiargs, e->e1->type, &args2);
- if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors))
- {
- result = new ErrorExp();
- return;
- }
- }
-
- FuncDeclaration *lastf = m.lastf;
-
- if (s)
- {
- functionResolve(&m, s, e->loc, sc, tiargs, e->e2->type, &args1);
- if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors))
- {
- result = new ErrorExp();
- return;
- }
- }
-
- if (m.count > 1)
- {
- // Error, ambiguous
- e->error("overloads %s and %s both match argument list for %s",
- m.lastf->type->toChars(),
- m.nextf->type->toChars(),
- m.lastf->toChars());
- }
- else if (m.last <= MATCHnomatch)
- {
- m.lastf = m.anyf;
- }
-
- if ((lastf && m.lastf == lastf) || (!s && m.last <= MATCHnomatch))
- {
- // Rewrite (e1 op e2) as e1.opfunc_r(e2)
- result = build_overload(e->loc, sc, e->e1, e->e2, m.lastf ? m.lastf : s_r);
- }
- else
- {
- // Rewrite (e1 op e2) as e2.opfunc(e1)
- result = build_overload(e->loc, sc, e->e2, e->e1, m.lastf ? m.lastf : s);
- }
-
- // When reversing operands of comparison operators,
- // need to reverse the sense of the op
- switch (e->op)
- {
- case TOKlt: e->op = TOKgt; break;
- case TOKgt: e->op = TOKlt; break;
- case TOKle: e->op = TOKge; break;
- case TOKge: e->op = TOKle; break;
- default: break;
- }
-
- return;
- }
- }
- #endif
-
- // Try alias this on first operand
- if (ad1 && ad1->aliasthis &&
- !(e->op == TOKassign && ad2 && ad1 == ad2)) // See Bugzilla 2943
- {
- /* Rewrite (e1 op e2) as:
- * (e1.aliasthis op e2)
- */
- if (e->att1 && e->e1->type == e->att1)
- return;
- //printf("att bin e1 = %s\n", this->e1->type->toChars());
- Expression *e1 = new DotIdExp(e->loc, e->e1, ad1->aliasthis->ident);
- BinExp *be = (BinExp *)e->copy();
- if (!be->att1 && e->e1->type->checkAliasThisRec())
- be->att1 = e->e1->type;
- be->e1 = e1;
- result = trySemantic(be, sc);
- return;
- }
-
- // Try alias this on second operand
- /* Bugzilla 2943: make sure that when we're copying the struct, we don't
- * just copy the alias this member
- */
- if (ad2 && ad2->aliasthis &&
- !(e->op == TOKassign && ad1 && ad1 == ad2))
- {
- /* Rewrite (e1 op e2) as:
- * (e1 op e2.aliasthis)
- */
- if (e->att2 && e->e2->type == e->att2)
- return;
- //printf("att bin e2 = %s\n", e->e2->type->toChars());
- Expression *e2 = new DotIdExp(e->loc, e->e2, ad2->aliasthis->ident);
- BinExp *be = (BinExp *)e->copy();
- if (!be->att2 && e->e2->type->checkAliasThisRec())
- be->att2 = e->e2->type;
- be->e2 = e2;
- result = trySemantic(be, sc);
- return;
- }
- return;
- }
-
- static bool needsDirectEq(Type *t1, Type *t2, Scope *sc)
- {
- Type *t1n = t1->nextOf()->toBasetype();
- Type *t2n = t2->nextOf()->toBasetype();
- if (((t1n->ty == Tchar || t1n->ty == Twchar || t1n->ty == Tdchar) &&
- (t2n->ty == Tchar || t2n->ty == Twchar || t2n->ty == Tdchar)) ||
- (t1n->ty == Tvoid || t2n->ty == Tvoid))
- {
- return false;
- }
- if (t1n->constOf() != t2n->constOf())
- return true;
-
- Type *t = t1n;
- while (t->toBasetype()->nextOf())
- t = t->nextOf()->toBasetype();
- if (t->ty != Tstruct)
- return false;
-
- if (global.params.useTypeInfo && Type::dtypeinfo)
- semanticTypeInfo(sc, t);
-
- return ((TypeStruct *)t)->sym->hasIdentityEquals;
- }
-
- void visit(EqualExp *e)
- {
- //printf("EqualExp::op_overload() (%s)\n", e->toChars());
-
- Type *t1 = e->e1->type->toBasetype();
- Type *t2 = e->e2->type->toBasetype();
-
- /* Check for array equality.
- */
- if ((t1->ty == Tarray || t1->ty == Tsarray) &&
- (t2->ty == Tarray || t2->ty == Tsarray))
- {
- if (needsDirectEq(t1, t2, sc))
- {
- /* Rewrite as:
- * __ArrayEq(e1, e2)
- */
- Expression *eeq = new IdentifierExp(e->loc, Id::__ArrayEq);
- result = new CallExp(e->loc, eeq, e->e1, e->e2);
- if (e->op == TOKnotequal)
- result = new NotExp(e->loc, result);
- result = trySemantic(result, sc); // for better error message
- if (!result)
- {
- e->error("cannot compare %s and %s", t1->toChars(), t2->toChars());
- result = new ErrorExp();
- }
- return;
- }
- }
-
- /* Check for class equality with null literal or typeof(null).
- */
- if ((t1->ty == Tclass && e->e2->op == TOKnull) ||
- (t2->ty == Tclass && e->e1->op == TOKnull))
- {
- e->error("use `%s` instead of `%s` when comparing with null",
- Token::toChars(e->op == TOKequal ? TOKidentity : TOKnotidentity),
- Token::toChars(e->op));
- result = new ErrorExp();
- return;
- }
- if ((t1->ty == Tclass && t2->ty == Tnull) ||
- (t1->ty == Tnull && t2->ty == Tclass))
- {
- // Comparing a class with typeof(null) should not call opEquals
- return;
- }
-
- /* Check for class equality.
- */
- if (t1->ty == Tclass && t2->ty == Tclass)
- {
- ClassDeclaration *cd1 = t1->isClassHandle();
- ClassDeclaration *cd2 = t2->isClassHandle();
-
- if (!(cd1->isCPPclass() || cd2->isCPPclass()))
- {
- /* Rewrite as:
- * .object.opEquals(e1, e2)
- */
- Expression *e1x = e->e1;
- Expression *e2x = e->e2;
-
- /* The explicit cast is necessary for interfaces,
- * see Bugzilla 4088.
- */
- Type *to = ClassDeclaration::object->getType();
- if (cd1->isInterfaceDeclaration())
- e1x = new CastExp(e->loc, e->e1, t1->isMutable() ? to : to->constOf());
- if (cd2->isInterfaceDeclaration())
- e2x = new CastExp(e->loc, e->e2, t2->isMutable() ? to : to->constOf());
-
- result = new IdentifierExp(e->loc, Id::empty);
- result = new DotIdExp(e->loc, result, Id::object);
- result = new DotIdExp(e->loc, result, Id::eq);
- result = new CallExp(e->loc, result, e1x, e2x);
- if (e->op == TOKnotequal)
- result = new NotExp(e->loc, result);
- result = expressionSemantic(result, sc);
- return;
- }
- }
-
- result = compare_overload(e, sc, Id::eq);
- if (result)
- {
- if (result->op == TOKcall && e->op == TOKnotequal)
- {
- result = new NotExp(result->loc, result);
- result = expressionSemantic(result, sc);
- }
- return;
- }
-
- /* Check for pointer equality.
- */
- if (t1->ty == Tpointer || t2->ty == Tpointer)
- {
- /* Rewrite:
- * ptr1 == ptr2
- * as:
- * ptr1 is ptr2
- *
- * This is just a rewriting for deterministic AST representation
- * as the backend input.
- */
- TOK op2 = e->op == TOKequal ? TOKidentity : TOKnotidentity;
- result = new IdentityExp(op2, e->loc, e->e1, e->e2);
- result = expressionSemantic(result, sc);
- return;
- }
-
- /* Check for struct equality without opEquals.
- */
- if (t1->ty == Tstruct && t2->ty == Tstruct)
- {
- StructDeclaration *sd = ((TypeStruct *)t1)->sym;
- if (sd != ((TypeStruct *)t2)->sym)
- return;
-
- if (!needOpEquals(sd))
- {
- // Use bitwise equality.
- TOK op2 = e->op == TOKequal ? TOKidentity : TOKnotidentity;
- result = new IdentityExp(op2, e->loc, e->e1, e->e2);
- result = expressionSemantic(result, sc);
- return;
- }
-
- /* Do memberwise equality.
- * Rewrite:
- * e1 == e2
- * as:
- * e1.tupleof == e2.tupleof
- *
- * If sd is a nested struct, and if it's nested in a class, it will
- * also compare the parent class's equality. Otherwise, compares
- * the identity of parent context through void*.
- */
- if (e->att1 && t1 == e->att1)
- return;
- if (e->att2 && t2 == e->att2)
- return;
-
- e = (EqualExp *)e->copy();
- if (!e->att1)
- e->att1 = t1;
- if (!e->att2)
- e->att2 = t2;
- e->e1 = new DotIdExp(e->loc, e->e1, Id::_tupleof);
- e->e2 = new DotIdExp(e->loc, e->e2, Id::_tupleof);
- result = expressionSemantic(e, sc);
-
- /* Bugzilla 15292, if the rewrite result is same with the original,
- * the equality is unresolvable because it has recursive definition.
- */
- if (result->op == e->op &&
- ((EqualExp *)result)->e1->type->toBasetype() == t1)
- {
- e->error("cannot compare %s because its auto generated member-wise equality has recursive definition",
- t1->toChars());
- result = new ErrorExp();
- }
- return;
- }
-
- /* Check for tuple equality.
- */
- if (e->e1->op == TOKtuple && e->e2->op == TOKtuple)
- {
- TupleExp *tup1 = (TupleExp *)e->e1;
- TupleExp *tup2 = (TupleExp *)e->e2;
- size_t dim = tup1->exps->length;
- if (dim != tup2->exps->length)
- {
- e->error("mismatched tuple lengths, %d and %d",
- (int)dim, (int)tup2->exps->length);
- result = new ErrorExp();
- return;
- }
-
- if (dim == 0)
- {
- // zero-length tuple comparison should always return true or false.
- result = new IntegerExp(e->loc, (e->op == TOKequal), Type::tbool);
- }
- else
- {
- for (size_t i = 0; i < dim; i++)
- {
- Expression *ex1 = (*tup1->exps)[i];
- Expression *ex2 = (*tup2->exps)[i];
- EqualExp *eeq = new EqualExp(e->op, e->loc, ex1, ex2);
- eeq->att1 = e->att1;
- eeq->att2 = e->att2;
-
- if (!result)
- result = eeq;
- else if (e->op == TOKequal)
- result = new LogicalExp(e->loc, TOKandand, result, eeq);
- else
- result = new LogicalExp(e->loc, TOKoror, result, eeq);
- }
- assert(result);
- }
- result = Expression::combine(Expression::combine(tup1->e0, tup2->e0), result);
- result = expressionSemantic(result, sc);
- return;
- }
- }
-
- void visit(CmpExp *e)
- {
- //printf("CmpExp::op_overload() (%s)\n", e->toChars());
-
- result = compare_overload(e, sc, Id::cmp);
- }
-
- /*********************************
- * Operator overloading for op=
- */
- void visit(BinAssignExp *e)
- {
- //printf("BinAssignExp::op_overload() (%s)\n", e->toChars());
-
- if (e->e1->op == TOKarray)
- {
- ArrayExp *ae = (ArrayExp *)e->e1;
- ae->e1 = expressionSemantic(ae->e1, sc);
- ae->e1 = resolveProperties(sc, ae->e1);
- Expression *ae1old = ae->e1;
-
- const bool maybeSlice =
- (ae->arguments->length == 0 ||
- (ae->arguments->length == 1 && (*ae->arguments)[0]->op == TOKinterval));
- IntervalExp *ie = NULL;
- if (maybeSlice && ae->arguments->length)
- {
- assert((*ae->arguments)[0]->op == TOKinterval);
- ie = (IntervalExp *)(*ae->arguments)[0];
- }
-
- while (true)
- {
- if (ae->e1->op == TOKerror)
- {
- result = ae->e1;
- return;
- }
- Expression *e0 = NULL;
- Expression *ae1save = ae->e1;
- ae->lengthVar = NULL;
-
- Type *t1b = ae->e1->type->toBasetype();
- AggregateDeclaration *ad = isAggregate(t1b);
- if (!ad)
- break;
- if (search_function(ad, Id::opIndexOpAssign))
- {
- // Deal with $
- result = resolveOpDollar(sc, ae, &e0);
- if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j)
- goto Lfallback;
- if (result->op == TOKerror)
- return;
-
- result = expressionSemantic(e->e2, sc);
- if (result->op == TOKerror)
- return;
- e->e2 = result;
-
- /* Rewrite a[arguments] op= e2 as:
- * a.opIndexOpAssign!(op)(e2, arguments)
- */
- Expressions *a = (Expressions *)ae->arguments->copy();
- a->insert(0, e->e2);
- Objects *tiargs = opToArg(sc, e->op);
- result = new DotTemplateInstanceExp(e->loc, ae->e1, Id::opIndexOpAssign, tiargs);
- result = new CallExp(e->loc, result, a);
- if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2)
- result = trySemantic(result, sc);
- else
- result = expressionSemantic(result, sc);
- if (result)
- {
- result = Expression::combine(e0, result);
- return;
- }
- }
- Lfallback:
- if (maybeSlice && search_function(ad, Id::opSliceOpAssign))
- {
- // Deal with $
- result = resolveOpDollar(sc, ae, ie, &e0);
- if (result->op == TOKerror)
- return;
-
- result = expressionSemantic(e->e2, sc);
- if (result->op == TOKerror)
- return;
- e->e2 = result;
-
- /* Rewrite (a[i..j] op= e2) as:
- * a.opSliceOpAssign!(op)(e2, i, j)
- */
- Expressions *a = new Expressions();
- a->push(e->e2);
- if (ie)
- {
- a->push(ie->lwr);
- a->push(ie->upr);
- }
- Objects *tiargs = opToArg(sc, e->op);
- result = new DotTemplateInstanceExp(e->loc, ae->e1, Id::opSliceOpAssign, tiargs);
- result = new CallExp(e->loc, result, a);
- result = expressionSemantic(result, sc);
- result = Expression::combine(e0, result);
- return;
- }
-
- // Didn't find it. Forward to aliasthis
- if (ad->aliasthis && t1b != ae->att1)
- {
- if (!ae->att1 && t1b->checkAliasThisRec())
- ae->att1 = t1b;
-
- /* Rewrite (a[arguments] op= e2) as:
- * a.aliasthis[arguments] op= e2
- */
- ae->e1 = resolveAliasThis(sc, ae1save, true);
- if (ae->e1)
- continue;
- }
- break;
- }
- ae->e1 = ae1old; // recovery
- ae->lengthVar = NULL;
- }
-
- result = binSemanticProp(e, sc);
- if (result)
- return;
-
- // Don't attempt 'alias this' if an error occured
- if (e->e1->type->ty == Terror || e->e2->type->ty == Terror)
- {
- result = new ErrorExp();
- return;
- }
-
- Identifier *id = opId(e);
-
- Expressions args2;
-
- AggregateDeclaration *ad1 = isAggregate(e->e1->type);
-
- Dsymbol *s = NULL;
-
- #if 1 // the old D1 scheme
- if (ad1 && id)
- {
- s = search_function(ad1, id);
- }
- #endif
-
- Objects *tiargs = NULL;
- if (!s)
- {
- /* Try the new D2 scheme, opOpAssign
- */
- if (ad1)
- {
- s = search_function(ad1, Id::opOpAssign);
- if (s && !s->isTemplateDeclaration())
- {
- e->error("%s.opOpAssign isn't a template", e->e1->toChars());
- result = new ErrorExp();
- return;
- }
- }
-
- // Set tiargs, the template argument list, which will be the operator string
- if (s)
- {
- id = Id::opOpAssign;
- tiargs = opToArg(sc, e->op);
- }
- }
-
- if (s)
- {
- /* Try:
- * a.opOpAssign(b)
- */
-
- args2.setDim(1);
- args2[0] = e->e2;
- expandTuples(&args2);
-
- Match m;
- memset(&m, 0, sizeof(m));
- m.last = MATCHnomatch;
-
- if (s)
- {
- functionResolve(&m, s, e->loc, sc, tiargs, e->e1->type, &args2);
- if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors))
- {
- result = new ErrorExp();
- return;
- }
- }
-
- if (m.count > 1)
- {
- // Error, ambiguous
- e->error("overloads %s and %s both match argument list for %s",
- m.lastf->type->toChars(),
- m.nextf->type->toChars(),
- m.lastf->toChars());
- }
- else if (m.last <= MATCHnomatch)
- {
- m.lastf = m.anyf;
- if (tiargs)
- goto L1;
- }
-
- // Rewrite (e1 op e2) as e1.opOpAssign(e2)
- result = build_overload(e->loc, sc, e->e1, e->e2, m.lastf ? m.lastf : s);
- return;
- }
-
- L1:
-
- // Try alias this on first operand
- if (ad1 && ad1->aliasthis)
- {
- /* Rewrite (e1 op e2) as:
- * (e1.aliasthis op e2)
- */
- if (e->att1 && e->e1->type == e->att1)
- return;
- //printf("att %s e1 = %s\n", Token::toChars(e->op), e->e1->type->toChars());
- Expression *e1 = new DotIdExp(e->loc, e->e1, ad1->aliasthis->ident);
- BinExp *be = (BinExp *)e->copy();
- if (!be->att1 && e->e1->type->checkAliasThisRec())
- be->att1 = e->e1->type;
- be->e1 = e1;
- result = trySemantic(be, sc);
- return;
- }
-
- // Try alias this on second operand
- AggregateDeclaration *ad2 = isAggregate(e->e2->type);
- if (ad2 && ad2->aliasthis)
- {
- /* Rewrite (e1 op e2) as:
- * (e1 op e2.aliasthis)
- */
- if (e->att2 && e->e2->type == e->att2)
- return;
- //printf("att %s e2 = %s\n", Token::toChars(e->op), e->e2->type->toChars());
- Expression *e2 = new DotIdExp(e->loc, e->e2, ad2->aliasthis->ident);
- BinExp *be = (BinExp *)e->copy();
- if (!be->att2 && e->e2->type->checkAliasThisRec())
- be->att2 = e->e2->type;
- be->e2 = e2;
- result = trySemantic(be, sc);
- return;
- }
- }
- };
-
- OpOverload v(sc);
- e->accept(&v);
- return v.result;
-}
-
-/******************************************
- * Common code for overloading of EqualExp and CmpExp
- */
-Expression *compare_overload(BinExp *e, Scope *sc, Identifier *id)
-{
- //printf("BinExp::compare_overload(id = %s) %s\n", id->toChars(), e->toChars());
-
- AggregateDeclaration *ad1 = isAggregate(e->e1->type);
- AggregateDeclaration *ad2 = isAggregate(e->e2->type);
-
- Dsymbol *s = NULL;
- Dsymbol *s_r = NULL;
-
- if (ad1)
- {
- s = search_function(ad1, id);
- }
- if (ad2)
- {
- s_r = search_function(ad2, id);
- if (s == s_r)
- s_r = NULL;
- }
-
- Objects *tiargs = NULL;
-
- if (s || s_r)
- {
- /* Try:
- * a.opEquals(b)
- * b.opEquals(a)
- * and see which is better.
- */
-
- Expressions args1;
- Expressions args2;
-
- args1.setDim(1);
- args1[0] = e->e1;
- expandTuples(&args1);
- args2.setDim(1);
- args2[0] = e->e2;
- expandTuples(&args2);
-
- Match m;
- memset(&m, 0, sizeof(m));
- m.last = MATCHnomatch;
-
- if (0 && s && s_r)
- {
- printf("s : %s\n", s->toPrettyChars());
- printf("s_r: %s\n", s_r->toPrettyChars());
- }
-
- if (s)
- {
- functionResolve(&m, s, e->loc, sc, tiargs, e->e1->type, &args2);
- if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors))
- return new ErrorExp();
- }
-
- FuncDeclaration *lastf = m.lastf;
- int count = m.count;
-
- if (s_r)
- {
- functionResolve(&m, s_r, e->loc, sc, tiargs, e->e2->type, &args1);
- if (m.lastf && (m.lastf->errors || m.lastf->semantic3Errors))
- return new ErrorExp();
- }
-
- if (m.count > 1)
- {
- /* The following if says "not ambiguous" if there's one match
- * from s and one from s_r, in which case we pick s.
- * This doesn't follow the spec, but is a workaround for the case
- * where opEquals was generated from templates and we cannot figure
- * out if both s and s_r came from the same declaration or not.
- * The test case is:
- * import std.typecons;
- * void main() {
- * assert(tuple("has a", 2u) == tuple("has a", 1));
- * }
- */
- if (!(m.lastf == lastf && m.count == 2 && count == 1))
- {
- // Error, ambiguous
- e->error("overloads %s and %s both match argument list for %s",
- m.lastf->type->toChars(),
- m.nextf->type->toChars(),
- m.lastf->toChars());
- }
- }
- else if (m.last <= MATCHnomatch)
- {
- m.lastf = m.anyf;
- }
-
- Expression *result;
- if ((lastf && m.lastf == lastf) || (!s_r && m.last <= MATCHnomatch))
- {
- // Rewrite (e1 op e2) as e1.opfunc(e2)
- result = build_overload(e->loc, sc, e->e1, e->e2, m.lastf ? m.lastf : s);
- }
- else
- {
- // Rewrite (e1 op e2) as e2.opfunc_r(e1)
- result = build_overload(e->loc, sc, e->e2, e->e1, m.lastf ? m.lastf : s_r);
-
- // When reversing operands of comparison operators,
- // need to reverse the sense of the op
- switch (e->op)
- {
- case TOKlt: e->op = TOKgt; break;
- case TOKgt: e->op = TOKlt; break;
- case TOKle: e->op = TOKge; break;
- case TOKge: e->op = TOKle; break;
-
- // The rest are symmetric
- default:
- break;
- }
- }
-
- return result;
- }
-
- // Try alias this on first operand
- if (ad1 && ad1->aliasthis)
- {
- /* Rewrite (e1 op e2) as:
- * (e1.aliasthis op e2)
- */
- if (e->att1 && e->e1->type == e->att1)
- return NULL;
- //printf("att cmp_bin e1 = %s\n", e->e1->type->toChars());
- Expression *e1 = new DotIdExp(e->loc, e->e1, ad1->aliasthis->ident);
- BinExp *be = (BinExp *)e->copy();
- if (!be->att1 && e->e1->type->checkAliasThisRec())
- be->att1 = e->e1->type;
- be->e1 = e1;
- return trySemantic(be, sc);
- }
-
- // Try alias this on second operand
- if (ad2 && ad2->aliasthis)
- {
- /* Rewrite (e1 op e2) as:
- * (e1 op e2.aliasthis)
- */
- if (e->att2 && e->e2->type == e->att2)
- return NULL;
- //printf("att cmp_bin e2 = %s\n", e->e2->type->toChars());
- Expression *e2 = new DotIdExp(e->loc, e->e2, ad2->aliasthis->ident);
- BinExp *be = (BinExp *)e->copy();
- if (!be->att2 && e->e2->type->checkAliasThisRec())
- be->att2 = e->e2->type;
- be->e2 = e2;
- return trySemantic(be, sc);
- }
-
- return NULL;
-}
-
-/***********************************
- * Utility to build a function call out of this reference and argument.
- */
-
-Expression *build_overload(Loc loc, Scope *sc, Expression *ethis, Expression *earg,
- Dsymbol *d)
-{
- assert(d);
- Expression *e;
-
- //printf("build_overload(id = '%s')\n", id->toChars());
- //earg->print();
- //earg->type->print();
- Declaration *decl = d->isDeclaration();
- if (decl)
- e = new DotVarExp(loc, ethis, decl, false);
- else
- e = new DotIdExp(loc, ethis, d->ident);
- e = new CallExp(loc, e, earg);
-
- e = expressionSemantic(e, sc);
- return e;
-}
-
-/***************************************
- * Search for function funcid in aggregate ad.
- */
-
-Dsymbol *search_function(ScopeDsymbol *ad, Identifier *funcid)
-{
- Dsymbol *s = ad->search(Loc(), funcid);
- if (s)
- {
- //printf("search_function: s = '%s'\n", s->kind());
- Dsymbol *s2 = s->toAlias();
- //printf("search_function: s2 = '%s'\n", s2->kind());
- FuncDeclaration *fd = s2->isFuncDeclaration();
- if (fd && fd->type->ty == Tfunction)
- return fd;
-
- TemplateDeclaration *td = s2->isTemplateDeclaration();
- if (td)
- return td;
- }
- return NULL;
-}
-
-
-bool inferAggregate(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply)
-{
- //printf("inferAggregate(%s)\n", fes->aggr->toChars());
- Identifier *idapply = (fes->op == TOKforeach) ? Id::apply : Id::applyReverse;
- Identifier *idfront = (fes->op == TOKforeach) ? Id::Ffront : Id::Fback;
- int sliced = 0;
- Type *tab;
- Type *att = NULL;
- Expression *aggr = fes->aggr;
- AggregateDeclaration *ad;
-
- while (1)
- {
- aggr = expressionSemantic(aggr, sc);
- aggr = resolveProperties(sc, aggr);
- aggr = aggr->optimize(WANTvalue);
- if (!aggr->type || aggr->op == TOKerror)
- goto Lerr;
-
- tab = aggr->type->toBasetype();
- switch (tab->ty)
- {
- case Tarray:
- case Tsarray:
- case Ttuple:
- case Taarray:
- break;
-
- case Tclass:
- ad = ((TypeClass *)tab)->sym;
- goto Laggr;
-
- case Tstruct:
- ad = ((TypeStruct *)tab)->sym;
- goto Laggr;
-
- Laggr:
- if (!sliced)
- {
- sapply = search_function(ad, idapply);
- if (sapply)
- {
- // opApply aggregate
- break;
- }
-
- if (fes->aggr->op != TOKtype)
- {
- Expression *rinit = new ArrayExp(fes->aggr->loc, fes->aggr);
- rinit = trySemantic(rinit, sc);
- if (rinit) // if application of [] succeeded
- {
- aggr = rinit;
- sliced = 1;
- continue;
- }
- }
- }
-
- if (ad->search(Loc(), idfront))
- {
- // range aggregate
- break;
- }
-
- if (ad->aliasthis)
- {
- if (att == tab)
- goto Lerr;
- if (!att && tab->checkAliasThisRec())
- att = tab;
- aggr = resolveAliasThis(sc, aggr);
- continue;
- }
- goto Lerr;
-
- case Tdelegate:
- if (aggr->op == TOKdelegate)
- {
- sapply = ((DelegateExp *)aggr)->func;
- }
- break;
-
- case Terror:
- break;
-
- default:
- goto Lerr;
- }
- break;
- }
- fes->aggr = aggr;
- return true;
-
-Lerr:
- return false;
-}
-
-/*****************************************
- * Given array of parameters and an aggregate type,
- * if any of the parameter types are missing, attempt to infer
- * them from the aggregate type.
- */
-
-bool inferApplyArgTypes(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply)
-{
- if (!fes->parameters || !fes->parameters->length)
- return false;
-
- if (sapply) // prefer opApply
- {
- for (size_t u = 0; u < fes->parameters->length; u++)
- {
- Parameter *p = (*fes->parameters)[u];
- if (p->type)
- {
- p->type = typeSemantic(p->type, fes->loc, sc);
- p->type = p->type->addStorageClass(p->storageClass);
- }
- }
-
- Expression *ethis;
- Type *tab = fes->aggr->type->toBasetype();
- if (tab->ty == Tclass || tab->ty == Tstruct)
- ethis = fes->aggr;
- else
- { assert(tab->ty == Tdelegate && fes->aggr->op == TOKdelegate);
- ethis = ((DelegateExp *)fes->aggr)->e1;
- }
-
- /* Look for like an
- * int opApply(int delegate(ref Type [, ...]) dg);
- * overload
- */
- FuncDeclaration *fd = sapply->isFuncDeclaration();
- if (fd)
- {
- sapply = inferApplyArgTypesX(ethis, fd, fes->parameters);
- }
- return sapply != NULL;
- }
-
- /* Return if no parameters need types.
- */
- for (size_t u = 0; u < fes->parameters->length; u++)
- {
- Parameter *p = (*fes->parameters)[u];
- if (!p->type)
- break;
- }
-
- AggregateDeclaration *ad;
-
- Parameter *p = (*fes->parameters)[0];
- Type *taggr = fes->aggr->type;
- assert(taggr);
- Type *tab = taggr->toBasetype();
- switch (tab->ty)
- {
- case Tarray:
- case Tsarray:
- case Ttuple:
- if (fes->parameters->length == 2)
- {
- if (!p->type)
- {
- p->type = Type::tsize_t; // key type
- p->type = p->type->addStorageClass(p->storageClass);
- }
- p = (*fes->parameters)[1];
- }
- if (!p->type && tab->ty != Ttuple)
- {
- p->type = tab->nextOf(); // value type
- p->type = p->type->addStorageClass(p->storageClass);
- }
- break;
-
- case Taarray:
- {
- TypeAArray *taa = (TypeAArray *)tab;
-
- if (fes->parameters->length == 2)
- {
- if (!p->type)
- {
- p->type = taa->index; // key type
- p->type = p->type->addStorageClass(p->storageClass);
- if (p->storageClass & STCref) // key must not be mutated via ref
- p->type = p->type->addMod(MODconst);
- }
- p = (*fes->parameters)[1];
- }
- if (!p->type)
- {
- p->type = taa->next; // value type
- p->type = p->type->addStorageClass(p->storageClass);
- }
- break;
- }
-
- case Tclass:
- ad = ((TypeClass *)tab)->sym;
- goto Laggr;
-
- case Tstruct:
- ad = ((TypeStruct *)tab)->sym;
- goto Laggr;
-
- Laggr:
- if (fes->parameters->length == 1)
- {
- if (!p->type)
- {
- /* Look for a front() or back() overload
- */
- Identifier *id = (fes->op == TOKforeach) ? Id::Ffront : Id::Fback;
- Dsymbol *s = ad->search(Loc(), id);
- FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL;
- if (fd)
- {
- // Resolve inout qualifier of front type
- p->type = fd->type->nextOf();
- if (p->type)
- {
- p->type = p->type->substWildTo(tab->mod);
- p->type = p->type->addStorageClass(p->storageClass);
- }
- }
- else if (s && s->isTemplateDeclaration())
- ;
- else if (s && s->isDeclaration())
- p->type = ((Declaration *)s)->type;
- else
- break;
- }
- break;
- }
- break;
-
- case Tdelegate:
- {
- if (!inferApplyArgTypesY((TypeFunction *)tab->nextOf(), fes->parameters))
- return false;
- break;
- }
-
- default:
- break; // ignore error, caught later
- }
- return true;
-}
-
-static Dsymbol *inferApplyArgTypesX(Expression *ethis, FuncDeclaration *fstart, Parameters *parameters)
-{
- struct ParamOpOver
- {
- Parameters *parameters;
- MOD mod;
- MATCH match;
- FuncDeclaration *fd_best;
- FuncDeclaration *fd_ambig;
-
- static int fp(void *param, Dsymbol *s)
- {
- FuncDeclaration *f = s->isFuncDeclaration();
- if (!f)
- return 0;
- ParamOpOver *p = (ParamOpOver *)param;
- TypeFunction *tf = (TypeFunction *)f->type;
- MATCH m = MATCHexact;
-
- if (f->isThis())
- {
- if (!MODimplicitConv(p->mod, tf->mod))
- m = MATCHnomatch;
- else if (p->mod != tf->mod)
- m = MATCHconst;
- }
- if (!inferApplyArgTypesY(tf, p->parameters, 1))
- m = MATCHnomatch;
-
- if (m > p->match)
- {
- p->fd_best = f;
- p->fd_ambig = NULL;
- p->match = m;
- }
- else if (m == p->match)
- p->fd_ambig = f;
- return 0;
- }
- };
- ParamOpOver p;
- p.parameters = parameters;
- p.mod = ethis->type->mod;
- p.match = MATCHnomatch;
- p.fd_best = NULL;
- p.fd_ambig = NULL;
- overloadApply(fstart, &p, &ParamOpOver::fp);
- if (p.fd_best)
- {
- inferApplyArgTypesY((TypeFunction *)p.fd_best->type, parameters);
- if (p.fd_ambig)
- { ::error(ethis->loc, "%s.%s matches more than one declaration:\n%s: %s\nand:\n%s: %s",
- ethis->toChars(), fstart->ident->toChars(),
- p.fd_best ->loc.toChars(), p.fd_best ->type->toChars(),
- p.fd_ambig->loc.toChars(), p.fd_ambig->type->toChars());
- p.fd_best = NULL;
- }
- }
- return p.fd_best;
-}
-
-/******************************
- * Infer parameters from type of function.
- * Returns:
- * 1 match for this function
- * 0 no match for this function
- */
-
-static int inferApplyArgTypesY(TypeFunction *tf, Parameters *parameters, int flags)
-{ size_t nparams;
- Parameter *p;
-
- if (tf->parameterList.length() != 1)
- goto Lnomatch;
- p = tf->parameterList[0];
- if (p->type->ty != Tdelegate)
- goto Lnomatch;
- tf = (TypeFunction *)p->type->nextOf();
- assert(tf->ty == Tfunction);
-
- /* We now have tf, the type of the delegate. Match it against
- * the parameters, filling in missing parameter types.
- */
- nparams = tf->parameterList.length();
- if (nparams == 0 || tf->parameterList.varargs != VARARGnone)
- goto Lnomatch; // not enough parameters
- if (parameters->length != nparams)
- goto Lnomatch; // not enough parameters
-
- for (size_t u = 0; u < nparams; u++)
- {
- p = (*parameters)[u];
- Parameter *param = tf->parameterList[u];
- if (p->type)
- {
- if (!p->type->equals(param->type))
- goto Lnomatch;
- }
- else if (!flags)
- {
- p->type = param->type;
- p->type = p->type->addStorageClass(p->storageClass);
- }
- }
- return 1;
-
-Lnomatch:
- return 0;
-}
-
diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d
new file mode 100644
index 00000000000..4ef55f3acc9
--- /dev/null
+++ b/gcc/d/dmd/opover.d
@@ -0,0 +1,1843 @@
+/**
+ * Handles operator overloading.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/operatoroverloading.html, Operator Overloading)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/opover.d, _opover.d)
+ * Documentation: https://dlang.org/phobos/dmd_opover.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/opover.d
+ */
+
+module dmd.opover;
+
+import core.stdc.stdio;
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.statement;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.visitor;
+
+/***********************************
+ * Determine if operands of binary op can be reversed
+ * to fit operator overload.
+ */
+bool isCommutative(TOK op)
+{
+ switch (op)
+ {
+ case TOK.add:
+ case TOK.mul:
+ case TOK.and:
+ case TOK.or:
+ case TOK.xor:
+ // EqualExp
+ case TOK.equal:
+ case TOK.notEqual:
+ // CmpExp
+ case TOK.lessThan:
+ case TOK.lessOrEqual:
+ case TOK.greaterThan:
+ case TOK.greaterOrEqual:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/***********************************
+ * Get Identifier for operator overload.
+ */
+private Identifier opId(Expression e)
+{
+ switch (e.op)
+ {
+ case TOK.uadd: return Id.uadd;
+ case TOK.negate: return Id.neg;
+ case TOK.tilde: return Id.com;
+ case TOK.cast_: return Id._cast;
+ case TOK.in_: return Id.opIn;
+ case TOK.plusPlus: return Id.postinc;
+ case TOK.minusMinus: return Id.postdec;
+ case TOK.add: return Id.add;
+ case TOK.min: return Id.sub;
+ case TOK.mul: return Id.mul;
+ case TOK.div: return Id.div;
+ case TOK.mod: return Id.mod;
+ case TOK.pow: return Id.pow;
+ case TOK.leftShift: return Id.shl;
+ case TOK.rightShift: return Id.shr;
+ case TOK.unsignedRightShift: return Id.ushr;
+ case TOK.and: return Id.iand;
+ case TOK.or: return Id.ior;
+ case TOK.xor: return Id.ixor;
+ case TOK.concatenate: return Id.cat;
+ case TOK.assign: return Id.assign;
+ case TOK.addAssign: return Id.addass;
+ case TOK.minAssign: return Id.subass;
+ case TOK.mulAssign: return Id.mulass;
+ case TOK.divAssign: return Id.divass;
+ case TOK.modAssign: return Id.modass;
+ case TOK.powAssign: return Id.powass;
+ case TOK.leftShiftAssign: return Id.shlass;
+ case TOK.rightShiftAssign: return Id.shrass;
+ case TOK.unsignedRightShiftAssign: return Id.ushrass;
+ case TOK.andAssign: return Id.andass;
+ case TOK.orAssign: return Id.orass;
+ case TOK.xorAssign: return Id.xorass;
+ case TOK.concatenateAssign: return Id.catass;
+ case TOK.equal: return Id.eq;
+ case TOK.lessThan:
+ case TOK.lessOrEqual:
+ case TOK.greaterThan:
+ case TOK.greaterOrEqual: return Id.cmp;
+ case TOK.array: return Id.index;
+ case TOK.star: return Id.opStar;
+ default: assert(0);
+ }
+}
+
+/***********************************
+ * Get Identifier for reverse operator overload,
+ * `null` if not supported for this operator.
+ */
+private Identifier opId_r(Expression e)
+{
+ switch (e.op)
+ {
+ case TOK.in_: return Id.opIn_r;
+ case TOK.add: return Id.add_r;
+ case TOK.min: return Id.sub_r;
+ case TOK.mul: return Id.mul_r;
+ case TOK.div: return Id.div_r;
+ case TOK.mod: return Id.mod_r;
+ case TOK.pow: return Id.pow_r;
+ case TOK.leftShift: return Id.shl_r;
+ case TOK.rightShift: return Id.shr_r;
+ case TOK.unsignedRightShift:return Id.ushr_r;
+ case TOK.and: return Id.iand_r;
+ case TOK.or: return Id.ior_r;
+ case TOK.xor: return Id.ixor_r;
+ case TOK.concatenate: return Id.cat_r;
+ default: return null;
+ }
+}
+
+/*******************************************
+ * Helper function to turn operator into template argument list
+ */
+Objects* opToArg(Scope* sc, TOK op)
+{
+ /* Remove the = from op=
+ */
+ switch (op)
+ {
+ case TOK.addAssign:
+ op = TOK.add;
+ break;
+ case TOK.minAssign:
+ op = TOK.min;
+ break;
+ case TOK.mulAssign:
+ op = TOK.mul;
+ break;
+ case TOK.divAssign:
+ op = TOK.div;
+ break;
+ case TOK.modAssign:
+ op = TOK.mod;
+ break;
+ case TOK.andAssign:
+ op = TOK.and;
+ break;
+ case TOK.orAssign:
+ op = TOK.or;
+ break;
+ case TOK.xorAssign:
+ op = TOK.xor;
+ break;
+ case TOK.leftShiftAssign:
+ op = TOK.leftShift;
+ break;
+ case TOK.rightShiftAssign:
+ op = TOK.rightShift;
+ break;
+ case TOK.unsignedRightShiftAssign:
+ op = TOK.unsignedRightShift;
+ break;
+ case TOK.concatenateAssign:
+ op = TOK.concatenate;
+ break;
+ case TOK.powAssign:
+ op = TOK.pow;
+ break;
+ default:
+ break;
+ }
+ Expression e = new StringExp(Loc.initial, Token.toString(op));
+ e = e.expressionSemantic(sc);
+ auto tiargs = new Objects();
+ tiargs.push(e);
+ return tiargs;
+}
+
+// Try alias this on first operand
+private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e)
+{
+ if (!ad || !ad.aliasthis)
+ return null;
+
+ /* Rewrite (e1 op e2) as:
+ * (e1.aliasthis op e2)
+ */
+ if (isRecursiveAliasThis(e.att1, e.e1.type))
+ return null;
+ //printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars());
+ Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
+ BinExp be = cast(BinExp)e.copy();
+ be.e1 = e1;
+
+ Expression result;
+ if (be.op == TOK.concatenateAssign)
+ result = be.op_overload(sc);
+ else
+ result = be.trySemantic(sc);
+
+ return result;
+}
+
+// Try alias this on second operand
+private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e)
+{
+ if (!ad || !ad.aliasthis)
+ return null;
+ /* Rewrite (e1 op e2) as:
+ * (e1 op e2.aliasthis)
+ */
+ if (isRecursiveAliasThis(e.att2, e.e2.type))
+ return null;
+ //printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars());
+ Expression e2 = new DotIdExp(e.loc, e.e2, ad.aliasthis.ident);
+ BinExp be = cast(BinExp)e.copy();
+ be.e2 = e2;
+
+ Expression result;
+ if (be.op == TOK.concatenateAssign)
+ result = be.op_overload(sc);
+ else
+ result = be.trySemantic(sc);
+
+ return result;
+}
+
+/************************************
+ * Operator overload.
+ * Check for operator overload, if so, replace
+ * with function call.
+ * Params:
+ * e = expression with operator
+ * sc = context
+ * pop = if not null, is set to the operator that was actually overloaded,
+ * which may not be `e.op`. Happens when operands are reversed to
+ * match an overload
+ * Returns:
+ * `null` if not an operator overload,
+ * otherwise the lowered expression
+ */
+Expression op_overload(Expression e, Scope* sc, TOK* pop = null)
+{
+ extern (C++) final class OpOverload : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Scope* sc;
+ TOK* pop;
+ Expression result;
+
+ extern (D) this(Scope* sc, TOK* pop)
+ {
+ this.sc = sc;
+ this.pop = pop;
+ }
+
+ override void visit(Expression e)
+ {
+ assert(0);
+ }
+
+ override void visit(UnaExp e)
+ {
+ //printf("UnaExp::op_overload() (%s)\n", e.toChars());
+ if (e.e1.op == TOK.array)
+ {
+ ArrayExp ae = cast(ArrayExp)e.e1;
+ ae.e1 = ae.e1.expressionSemantic(sc);
+ ae.e1 = resolveProperties(sc, ae.e1);
+ Expression ae1old = ae.e1;
+ const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
+ IntervalExp ie = null;
+ if (maybeSlice && ae.arguments.dim)
+ {
+ assert((*ae.arguments)[0].op == TOK.interval);
+ ie = cast(IntervalExp)(*ae.arguments)[0];
+ }
+ while (true)
+ {
+ if (ae.e1.op == TOK.error)
+ {
+ result = ae.e1;
+ return;
+ }
+ Expression e0 = null;
+ Expression ae1save = ae.e1;
+ ae.lengthVar = null;
+ Type t1b = ae.e1.type.toBasetype();
+ AggregateDeclaration ad = isAggregate(t1b);
+ if (!ad)
+ break;
+ if (search_function(ad, Id.opIndexUnary))
+ {
+ // Deal with $
+ result = resolveOpDollar(sc, ae, &e0);
+ if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
+ goto Lfallback;
+ if (result.op == TOK.error)
+ return;
+ /* Rewrite op(a[arguments]) as:
+ * a.opIndexUnary!(op)(arguments)
+ */
+ Expressions* a = ae.arguments.copy();
+ Objects* tiargs = opToArg(sc, e.op);
+ result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs);
+ result = new CallExp(e.loc, result, a);
+ if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)()
+ result = result.trySemantic(sc);
+ else
+ result = result.expressionSemantic(sc);
+ if (result)
+ {
+ result = Expression.combine(e0, result);
+ return;
+ }
+ }
+ Lfallback:
+ if (maybeSlice && search_function(ad, Id.opSliceUnary))
+ {
+ // Deal with $
+ result = resolveOpDollar(sc, ae, ie, &e0);
+ if (result.op == TOK.error)
+ return;
+ /* Rewrite op(a[i..j]) as:
+ * a.opSliceUnary!(op)(i, j)
+ */
+ auto a = new Expressions();
+ if (ie)
+ {
+ a.push(ie.lwr);
+ a.push(ie.upr);
+ }
+ Objects* tiargs = opToArg(sc, e.op);
+ result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs);
+ result = new CallExp(e.loc, result, a);
+ result = result.expressionSemantic(sc);
+ result = Expression.combine(e0, result);
+ return;
+ }
+ // Didn't find it. Forward to aliasthis
+ if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+ {
+ /* Rewrite op(a[arguments]) as:
+ * op(a.aliasthis[arguments])
+ */
+ ae.e1 = resolveAliasThis(sc, ae1save, true);
+ if (ae.e1)
+ continue;
+ }
+ break;
+ }
+ ae.e1 = ae1old; // recovery
+ ae.lengthVar = null;
+ }
+ e.e1 = e.e1.expressionSemantic(sc);
+ e.e1 = resolveProperties(sc, e.e1);
+ if (e.e1.op == TOK.error)
+ {
+ result = e.e1;
+ return;
+ }
+ AggregateDeclaration ad = isAggregate(e.e1.type);
+ if (ad)
+ {
+ Dsymbol fd = null;
+ /* Rewrite as:
+ * e1.opUnary!(op)()
+ */
+ fd = search_function(ad, Id.opUnary);
+ if (fd)
+ {
+ Objects* tiargs = opToArg(sc, e.op);
+ result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
+ result = new CallExp(e.loc, result);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+ // D1-style operator overloads, deprecated
+ if (e.op != TOK.prePlusPlus && e.op != TOK.preMinusMinus)
+ {
+ auto id = opId(e);
+ fd = search_function(ad, id);
+ if (fd)
+ {
+ // @@@DEPRECATED_2.098@@@.
+ // Deprecated in 2.088
+ // Make an error in 2.098
+ e.deprecation("`%s` is deprecated. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), Token.toChars(e.op));
+ // Rewrite +e1 as e1.add()
+ result = build_overload(e.loc, sc, e.e1, null, fd);
+ return;
+ }
+ }
+ // Didn't find it. Forward to aliasthis
+ if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type))
+ {
+ /* Rewrite op(e1) as:
+ * op(e1.aliasthis)
+ */
+ //printf("att una %s e1 = %s\n", Token::toChars(op), this.e1.type.toChars());
+ Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
+ UnaExp ue = cast(UnaExp)e.copy();
+ ue.e1 = e1;
+ result = ue.trySemantic(sc);
+ return;
+ }
+ }
+ }
+
+ override void visit(ArrayExp ae)
+ {
+ //printf("ArrayExp::op_overload() (%s)\n", ae.toChars());
+ ae.e1 = ae.e1.expressionSemantic(sc);
+ ae.e1 = resolveProperties(sc, ae.e1);
+ Expression ae1old = ae.e1;
+ const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
+ IntervalExp ie = null;
+ if (maybeSlice && ae.arguments.dim)
+ {
+ assert((*ae.arguments)[0].op == TOK.interval);
+ ie = cast(IntervalExp)(*ae.arguments)[0];
+ }
+ while (true)
+ {
+ if (ae.e1.op == TOK.error)
+ {
+ result = ae.e1;
+ return;
+ }
+ Expression e0 = null;
+ Expression ae1save = ae.e1;
+ ae.lengthVar = null;
+ Type t1b = ae.e1.type.toBasetype();
+ AggregateDeclaration ad = isAggregate(t1b);
+ if (!ad)
+ {
+ // If the non-aggregate expression ae.e1 is indexable or sliceable,
+ // convert it to the corresponding concrete expression.
+ if (isIndexableNonAggregate(t1b) || ae.e1.op == TOK.type)
+ {
+ // Convert to SliceExp
+ if (maybeSlice)
+ {
+ result = new SliceExp(ae.loc, ae.e1, ie);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+ // Convert to IndexExp
+ if (ae.arguments.dim == 1)
+ {
+ result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+ }
+ break;
+ }
+ if (search_function(ad, Id.index))
+ {
+ // Deal with $
+ result = resolveOpDollar(sc, ae, &e0);
+ if (!result) // a[i..j] might be: a.opSlice(i, j)
+ goto Lfallback;
+ if (result.op == TOK.error)
+ return;
+ /* Rewrite e1[arguments] as:
+ * e1.opIndex(arguments)
+ */
+ Expressions* a = ae.arguments.copy();
+ result = new DotIdExp(ae.loc, ae.e1, Id.index);
+ result = new CallExp(ae.loc, result, a);
+ if (maybeSlice) // a[] might be: a.opSlice()
+ result = result.trySemantic(sc);
+ else
+ result = result.expressionSemantic(sc);
+ if (result)
+ {
+ result = Expression.combine(e0, result);
+ return;
+ }
+ }
+ Lfallback:
+ if (maybeSlice && ae.e1.op == TOK.type)
+ {
+ result = new SliceExp(ae.loc, ae.e1, ie);
+ result = result.expressionSemantic(sc);
+ result = Expression.combine(e0, result);
+ return;
+ }
+ if (maybeSlice && search_function(ad, Id.slice))
+ {
+ // Deal with $
+ result = resolveOpDollar(sc, ae, ie, &e0);
+ if (result.op == TOK.error)
+ return;
+ /* Rewrite a[i..j] as:
+ * a.opSlice(i, j)
+ */
+ auto a = new Expressions();
+ if (ie)
+ {
+ a.push(ie.lwr);
+ a.push(ie.upr);
+ }
+ result = new DotIdExp(ae.loc, ae.e1, Id.slice);
+ result = new CallExp(ae.loc, result, a);
+ result = result.expressionSemantic(sc);
+ result = Expression.combine(e0, result);
+ return;
+ }
+ // Didn't find it. Forward to aliasthis
+ if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+ {
+ //printf("att arr e1 = %s\n", this.e1.type.toChars());
+ /* Rewrite op(a[arguments]) as:
+ * op(a.aliasthis[arguments])
+ */
+ ae.e1 = resolveAliasThis(sc, ae1save, true);
+ if (ae.e1)
+ continue;
+ }
+ break;
+ }
+ ae.e1 = ae1old; // recovery
+ ae.lengthVar = null;
+ }
+
+ /***********************************************
+ * This is mostly the same as UnaryExp::op_overload(), but has
+ * a different rewrite.
+ */
+ override void visit(CastExp e)
+ {
+ //printf("CastExp::op_overload() (%s)\n", e.toChars());
+ AggregateDeclaration ad = isAggregate(e.e1.type);
+ if (ad)
+ {
+ Dsymbol fd = null;
+ /* Rewrite as:
+ * e1.opCast!(T)()
+ */
+ fd = search_function(ad, Id._cast);
+ if (fd)
+ {
+ version (all)
+ {
+ // Backwards compatibility with D1 if opCast is a function, not a template
+ if (fd.isFuncDeclaration())
+ {
+ // Rewrite as: e1.opCast()
+ result = build_overload(e.loc, sc, e.e1, null, fd);
+ return;
+ }
+ }
+ auto tiargs = new Objects();
+ tiargs.push(e.to);
+ result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
+ result = new CallExp(e.loc, result);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+ // Didn't find it. Forward to aliasthis
+ if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type))
+ {
+ /* Rewrite op(e1) as:
+ * op(e1.aliasthis)
+ */
+ Expression e1 = resolveAliasThis(sc, e.e1);
+ result = e.copy();
+ (cast(UnaExp)result).e1 = e1;
+ result = result.op_overload(sc);
+ return;
+ }
+ }
+ }
+
+ override void visit(BinExp e)
+ {
+ //printf("BinExp::op_overload() (%s)\n", e.toChars());
+ Identifier id = opId(e);
+ Identifier id_r = opId_r(e);
+ Expressions args1;
+ Expressions args2;
+ int argsset = 0;
+ AggregateDeclaration ad1 = isAggregate(e.e1.type);
+ AggregateDeclaration ad2 = isAggregate(e.e2.type);
+ if (e.op == TOK.assign && ad1 == ad2)
+ {
+ StructDeclaration sd = ad1.isStructDeclaration();
+ if (sd &&
+ (!sd.hasIdentityAssign ||
+ /* Do a blit if we can and the rvalue is something like .init,
+ * where a postblit is not necessary.
+ */
+ (sd.hasBlitAssign && !e.e2.isLvalue())))
+ {
+ /* This is bitwise struct assignment. */
+ return;
+ }
+ }
+ Dsymbol s = null;
+ Dsymbol s_r = null;
+ Objects* tiargs = null;
+ if (e.op == TOK.plusPlus || e.op == TOK.minusMinus)
+ {
+ // Bug4099 fix
+ if (ad1 && search_function(ad1, Id.opUnary))
+ return;
+ }
+ if (e.op != TOK.equal && e.op != TOK.notEqual && e.op != TOK.assign && e.op != TOK.plusPlus && e.op != TOK.minusMinus)
+ {
+ /* Try opBinary and opBinaryRight
+ */
+ if (ad1)
+ {
+ s = search_function(ad1, Id.opBinary);
+ if (s && !s.isTemplateDeclaration())
+ {
+ e.e1.error("`%s.opBinary` isn't a template", e.e1.toChars());
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ if (ad2)
+ {
+ s_r = search_function(ad2, Id.opBinaryRight);
+ if (s_r && !s_r.isTemplateDeclaration())
+ {
+ e.e2.error("`%s.opBinaryRight` isn't a template", e.e2.toChars());
+ result = ErrorExp.get();
+ return;
+ }
+ if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778
+ s_r = null;
+ }
+ // Set tiargs, the template argument list, which will be the operator string
+ if (s || s_r)
+ {
+ id = Id.opBinary;
+ id_r = Id.opBinaryRight;
+ tiargs = opToArg(sc, e.op);
+ }
+ }
+ if (!s && !s_r)
+ {
+ // Try the D1-style operators, deprecated
+ if (ad1 && id)
+ {
+ s = search_function(ad1, id);
+ if (s && id != Id.assign)
+ {
+ // @@@DEPRECATED_2.098@@@.
+ // Deprecated in 2.088
+ // Make an error in 2.098
+ if (id == Id.postinc || id == Id.postdec)
+ e.deprecation("`%s` is deprecated. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), Token.toChars(e.op));
+ else
+ e.deprecation("`%s` is deprecated. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), Token.toChars(e.op));
+ }
+ }
+ if (ad2 && id_r)
+ {
+ s_r = search_function(ad2, id_r);
+ // https://issues.dlang.org/show_bug.cgi?id=12778
+ // If both x.opBinary(y) and y.opBinaryRight(x) found,
+ // and they are exactly same symbol, x.opBinary(y) should be preferred.
+ if (s_r && s_r == s)
+ s_r = null;
+ if (s_r)
+ {
+ // @@@DEPRECATED_2.098@@@.
+ // Deprecated in 2.088
+ // Make an error in 2.098
+ e.deprecation("`%s` is deprecated. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), Token.toChars(e.op));
+ }
+ }
+ }
+ if (s || s_r)
+ {
+ /* Try:
+ * a.opfunc(b)
+ * b.opfunc_r(a)
+ * and see which is better.
+ */
+ args1.setDim(1);
+ args1[0] = e.e1;
+ expandTuples(&args1);
+ args2.setDim(1);
+ args2[0] = e.e2;
+ expandTuples(&args2);
+ argsset = 1;
+ MatchAccumulator m;
+ if (s)
+ {
+ functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
+ if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ FuncDeclaration lastf = m.lastf;
+ if (s_r)
+ {
+ functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1);
+ if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ if (m.count > 1)
+ {
+ // Error, ambiguous
+ e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
+ }
+ else if (m.last == MATCH.nomatch)
+ {
+ if (tiargs)
+ goto L1;
+ m.lastf = null;
+ }
+ if (e.op == TOK.plusPlus || e.op == TOK.minusMinus)
+ {
+ // Kludge because operator overloading regards e++ and e--
+ // as unary, but it's implemented as a binary.
+ // Rewrite (e1 ++ e2) as e1.postinc()
+ // Rewrite (e1 -- e2) as e1.postdec()
+ result = build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s);
+ }
+ else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
+ {
+ // Rewrite (e1 op e2) as e1.opfunc(e2)
+ result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
+ }
+ else
+ {
+ // Rewrite (e1 op e2) as e2.opfunc_r(e1)
+ result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
+ }
+ return;
+ }
+ L1:
+ version (all)
+ {
+ // Retained for D1 compatibility
+ if (isCommutative(e.op) && !tiargs)
+ {
+ s = null;
+ s_r = null;
+ if (ad1 && id_r)
+ {
+ s_r = search_function(ad1, id_r);
+ }
+ if (ad2 && id)
+ {
+ s = search_function(ad2, id);
+ if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778
+ s = null;
+ }
+ if (s || s_r)
+ {
+ /* Try:
+ * a.opfunc_r(b)
+ * b.opfunc(a)
+ * and see which is better.
+ */
+ if (!argsset)
+ {
+ args1.setDim(1);
+ args1[0] = e.e1;
+ expandTuples(&args1);
+ args2.setDim(1);
+ args2[0] = e.e2;
+ expandTuples(&args2);
+ }
+ MatchAccumulator m;
+ if (s_r)
+ {
+ functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, &args2);
+ if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ FuncDeclaration lastf = m.lastf;
+ if (s)
+ {
+ functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, &args1);
+ if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ if (m.count > 1)
+ {
+ // Error, ambiguous
+ e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
+ }
+ else if (m.last == MATCH.nomatch)
+ {
+ m.lastf = null;
+ }
+
+ if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch)
+ {
+ // Rewrite (e1 op e2) as e1.opfunc_r(e2)
+ result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r);
+ }
+ else
+ {
+ // Rewrite (e1 op e2) as e2.opfunc(e1)
+ result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s);
+ }
+ // When reversing operands of comparison operators,
+ // need to reverse the sense of the op
+ if (pop)
+ *pop = reverseRelation(e.op);
+ return;
+ }
+ }
+ }
+
+ Expression tempResult;
+ if (!(e.op == TOK.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
+ {
+ result = checkAliasThisForLhs(ad1, sc, e);
+ if (result)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=19441
+ *
+ * alias this may not be used for partial assignment.
+ * If a struct has a single member which is aliased this
+ * directly or aliased to a ref getter function that returns
+ * the mentioned member, then alias this may be
+ * used since the object will be fully initialised.
+ * If the struct is nested, the context pointer is considered
+ * one of the members, hence the `ad1.fields.dim == 2 && ad1.vthis`
+ * condition.
+ */
+ if (e.op != TOK.assign || e.e1.op == TOK.type)
+ return;
+
+ if (ad1.fields.dim == 1 || (ad1.fields.dim == 2 && ad1.vthis))
+ {
+ auto var = ad1.aliasthis.sym.isVarDeclaration();
+ if (var && var.type == ad1.fields[0].type)
+ return;
+
+ auto func = ad1.aliasthis.sym.isFuncDeclaration();
+ auto tf = cast(TypeFunction)(func.type);
+ if (tf.isref && ad1.fields[0].type == tf.next)
+ return;
+ }
+ tempResult = result;
+ }
+ }
+ if (!(e.op == TOK.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
+ {
+ result = checkAliasThisForRhs(ad2, sc, e);
+ if (result)
+ return;
+ }
+
+ // @@@DEPRECATED_2019-02@@@
+ // 1. Deprecation for 1 year
+ // 2. Turn to error after
+ if (tempResult)
+ {
+ // move this line where tempResult is assigned to result and turn to error when derecation period is over
+ e.deprecation("Cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", e.e1.toChars(), ad1.toChars(), (cast(BinExp)tempResult).e1.toChars());
+ // delete this line when deprecation period is over
+ result = tempResult;
+ }
+ }
+
+ override void visit(EqualExp e)
+ {
+ //printf("EqualExp::op_overload() (%s)\n", e.toChars());
+ Type t1 = e.e1.type.toBasetype();
+ Type t2 = e.e2.type.toBasetype();
+
+ /* Array equality is handled by expressionSemantic() potentially
+ * lowering to object.__equals(), which takes care of overloaded
+ * operators for the element types.
+ */
+ if ((t1.ty == Tarray || t1.ty == Tsarray) &&
+ (t2.ty == Tarray || t2.ty == Tsarray))
+ {
+ return;
+ }
+
+ /* Check for class equality with null literal or typeof(null).
+ */
+ if (t1.ty == Tclass && e.e2.op == TOK.null_ ||
+ t2.ty == Tclass && e.e1.op == TOK.null_)
+ {
+ e.error("use `%s` instead of `%s` when comparing with `null`",
+ Token.toChars(e.op == TOK.equal ? TOK.identity : TOK.notIdentity),
+ Token.toChars(e.op));
+ result = ErrorExp.get();
+ return;
+ }
+ if (t1.ty == Tclass && t2.ty == Tnull ||
+ t1.ty == Tnull && t2.ty == Tclass)
+ {
+ // Comparing a class with typeof(null) should not call opEquals
+ return;
+ }
+
+ /* Check for class equality.
+ */
+ if (t1.ty == Tclass && t2.ty == Tclass)
+ {
+ ClassDeclaration cd1 = t1.isClassHandle();
+ ClassDeclaration cd2 = t2.isClassHandle();
+ if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp))
+ {
+ /* Rewrite as:
+ * .object.opEquals(e1, e2)
+ */
+ Expression e1x = e.e1;
+ Expression e2x = e.e2;
+
+ /* The explicit cast is necessary for interfaces
+ * https://issues.dlang.org/show_bug.cgi?id=4088
+ */
+ Type to = ClassDeclaration.object.getType();
+ if (cd1.isInterfaceDeclaration())
+ e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
+ if (cd2.isInterfaceDeclaration())
+ e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf());
+
+ result = new IdentifierExp(e.loc, Id.empty);
+ result = new DotIdExp(e.loc, result, Id.object);
+ result = new DotIdExp(e.loc, result, Id.eq);
+ result = new CallExp(e.loc, result, e1x, e2x);
+ if (e.op == TOK.notEqual)
+ result = new NotExp(e.loc, result);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+ }
+
+ result = compare_overload(e, sc, Id.eq, null);
+ if (result)
+ {
+ if (lastComma(result).op == TOK.call && e.op == TOK.notEqual)
+ {
+ result = new NotExp(result.loc, result);
+ result = result.expressionSemantic(sc);
+ }
+ return;
+ }
+
+ /* Check for pointer equality.
+ */
+ if (t1.ty == Tpointer || t2.ty == Tpointer)
+ {
+ /* Rewrite:
+ * ptr1 == ptr2
+ * as:
+ * ptr1 is ptr2
+ *
+ * This is just a rewriting for deterministic AST representation
+ * as the backend input.
+ */
+ auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity;
+ result = new IdentityExp(op2, e.loc, e.e1, e.e2);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+
+ /* Check for struct equality without opEquals.
+ */
+ if (t1.ty == Tstruct && t2.ty == Tstruct)
+ {
+ auto sd = (cast(TypeStruct)t1).sym;
+ if (sd != (cast(TypeStruct)t2).sym)
+ return;
+
+ import dmd.clone : needOpEquals;
+ if (!global.params.fieldwise && !needOpEquals(sd))
+ {
+ // Use bitwise equality.
+ auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity;
+ result = new IdentityExp(op2, e.loc, e.e1, e.e2);
+ result = result.expressionSemantic(sc);
+ return;
+ }
+
+ /* Do memberwise equality.
+ * https://dlang.org/spec/expression.html#equality_expressions
+ * Rewrite:
+ * e1 == e2
+ * as:
+ * e1.tupleof == e2.tupleof
+ *
+ * If sd is a nested struct, and if it's nested in a class, it will
+ * also compare the parent class's equality. Otherwise, compares
+ * the identity of parent context through void*.
+ */
+ if (e.att1 && t1.equivalent(e.att1)) return;
+ if (e.att2 && t2.equivalent(e.att2)) return;
+
+ e = cast(EqualExp)e.copy();
+ if (!e.att1) e.att1 = t1;
+ if (!e.att2) e.att2 = t2;
+ e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof);
+ e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
+
+ auto sc2 = sc.push();
+ sc2.flags = (sc2.flags & ~SCOPE.onlysafeaccess) | SCOPE.noaccesscheck;
+ result = e.expressionSemantic(sc2);
+ sc2.pop();
+
+ /* https://issues.dlang.org/show_bug.cgi?id=15292
+ * if the rewrite result is same with the original,
+ * the equality is unresolvable because it has recursive definition.
+ */
+ if (result.op == e.op &&
+ (cast(EqualExp)result).e1.type.toBasetype() == t1)
+ {
+ e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition",
+ t1.toChars());
+ result = ErrorExp.get();
+ }
+ return;
+ }
+
+ /* Check for tuple equality.
+ */
+ if (e.e1.op == TOK.tuple && e.e2.op == TOK.tuple)
+ {
+ auto tup1 = cast(TupleExp)e.e1;
+ auto tup2 = cast(TupleExp)e.e2;
+ size_t dim = tup1.exps.dim;
+ if (dim != tup2.exps.dim)
+ {
+ e.error("mismatched tuple lengths, `%d` and `%d`",
+ cast(int)dim, cast(int)tup2.exps.dim);
+ result = ErrorExp.get();
+ return;
+ }
+
+ if (dim == 0)
+ {
+ // zero-length tuple comparison should always return true or false.
+ result = IntegerExp.createBool(e.op == TOK.equal);
+ }
+ else
+ {
+ for (size_t i = 0; i < dim; i++)
+ {
+ auto ex1 = (*tup1.exps)[i];
+ auto ex2 = (*tup2.exps)[i];
+ auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
+ eeq.att1 = e.att1;
+ eeq.att2 = e.att2;
+
+ if (!result)
+ result = eeq;
+ else if (e.op == TOK.equal)
+ result = new LogicalExp(e.loc, TOK.andAnd, result, eeq);
+ else
+ result = new LogicalExp(e.loc, TOK.orOr, result, eeq);
+ }
+ assert(result);
+ }
+ result = Expression.combine(tup1.e0, tup2.e0, result);
+ result = result.expressionSemantic(sc);
+
+ return;
+ }
+ }
+
+ override void visit(CmpExp e)
+ {
+ //printf("CmpExp:: () (%s)\n", e.toChars());
+ result = compare_overload(e, sc, Id.cmp, pop);
+ }
+
+ /*********************************
+ * Operator overloading for op=
+ */
+ override void visit(BinAssignExp e)
+ {
+ //printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
+ if (e.e1.op == TOK.array)
+ {
+ ArrayExp ae = cast(ArrayExp)e.e1;
+ ae.e1 = ae.e1.expressionSemantic(sc);
+ ae.e1 = resolveProperties(sc, ae.e1);
+ Expression ae1old = ae.e1;
+ const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
+ IntervalExp ie = null;
+ if (maybeSlice && ae.arguments.dim)
+ {
+ assert((*ae.arguments)[0].op == TOK.interval);
+ ie = cast(IntervalExp)(*ae.arguments)[0];
+ }
+ while (true)
+ {
+ if (ae.e1.op == TOK.error)
+ {
+ result = ae.e1;
+ return;
+ }
+ Expression e0 = null;
+ Expression ae1save = ae.e1;
+ ae.lengthVar = null;
+ Type t1b = ae.e1.type.toBasetype();
+ AggregateDeclaration ad = isAggregate(t1b);
+ if (!ad)
+ break;
+ if (search_function(ad, Id.opIndexOpAssign))
+ {
+ // Deal with $
+ result = resolveOpDollar(sc, ae, &e0);
+ if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j)
+ goto Lfallback;
+ if (result.op == TOK.error)
+ return;
+ result = e.e2.expressionSemantic(sc);
+ if (result.op == TOK.error)
+ return;
+ e.e2 = result;
+ /* Rewrite a[arguments] op= e2 as:
+ * a.opIndexOpAssign!(op)(e2, arguments)
+ */
+ Expressions* a = ae.arguments.copy();
+ a.insert(0, e.e2);
+ Objects* tiargs = opToArg(sc, e.op);
+ result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs);
+ result = new CallExp(e.loc, result, a);
+ if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2)
+ result = result.trySemantic(sc);
+ else
+ result = result.expressionSemantic(sc);
+ if (result)
+ {
+ result = Expression.combine(e0, result);
+ return;
+ }
+ }
+ Lfallback:
+ if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
+ {
+ // Deal with $
+ result = resolveOpDollar(sc, ae, ie, &e0);
+ if (result.op == TOK.error)
+ return;
+ result = e.e2.expressionSemantic(sc);
+ if (result.op == TOK.error)
+ return;
+ e.e2 = result;
+ /* Rewrite (a[i..j] op= e2) as:
+ * a.opSliceOpAssign!(op)(e2, i, j)
+ */
+ auto a = new Expressions();
+ a.push(e.e2);
+ if (ie)
+ {
+ a.push(ie.lwr);
+ a.push(ie.upr);
+ }
+ Objects* tiargs = opToArg(sc, e.op);
+ result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs);
+ result = new CallExp(e.loc, result, a);
+ result = result.expressionSemantic(sc);
+ result = Expression.combine(e0, result);
+ return;
+ }
+ // Didn't find it. Forward to aliasthis
+ if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type))
+ {
+ /* Rewrite (a[arguments] op= e2) as:
+ * a.aliasthis[arguments] op= e2
+ */
+ ae.e1 = resolveAliasThis(sc, ae1save, true);
+ if (ae.e1)
+ continue;
+ }
+ break;
+ }
+ ae.e1 = ae1old; // recovery
+ ae.lengthVar = null;
+ }
+ result = e.binSemanticProp(sc);
+ if (result)
+ return;
+ // Don't attempt 'alias this' if an error occurred
+ if (e.e1.type.ty == Terror || e.e2.type.ty == Terror)
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ Identifier id = opId(e);
+ Expressions args2;
+ AggregateDeclaration ad1 = isAggregate(e.e1.type);
+ Dsymbol s = null;
+ Objects* tiargs = null;
+ /* Try opOpAssign
+ */
+ if (ad1)
+ {
+ s = search_function(ad1, Id.opOpAssign);
+ if (s && !s.isTemplateDeclaration())
+ {
+ e.error("`%s.opOpAssign` isn't a template", e.e1.toChars());
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ // Set tiargs, the template argument list, which will be the operator string
+ if (s)
+ {
+ id = Id.opOpAssign;
+ tiargs = opToArg(sc, e.op);
+ }
+
+ // Try D1-style operator overload, deprecated
+ if (!s && ad1 && id)
+ {
+ s = search_function(ad1, id);
+ if (s)
+ {
+ // @@@DEPRECATED_2.098@@@.
+ // Deprecated in 2.088
+ // Make an error in 2.098
+ scope char[] op = Token.toString(e.op).dup;
+ op[$-1] = '\0'; // remove trailing `=`
+ e.deprecation("`%s` is deprecated. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr);
+ }
+ }
+
+ if (s)
+ {
+ /* Try:
+ * a.opOpAssign(b)
+ */
+ args2.setDim(1);
+ args2[0] = e.e2;
+ expandTuples(&args2);
+ MatchAccumulator m;
+ if (s)
+ {
+ functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
+ if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ {
+ result = ErrorExp.get();
+ return;
+ }
+ }
+ if (m.count > 1)
+ {
+ // Error, ambiguous
+ e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
+ }
+ else if (m.last == MATCH.nomatch)
+ {
+ if (tiargs)
+ goto L1;
+ m.lastf = null;
+ }
+ // Rewrite (e1 op e2) as e1.opOpAssign(e2)
+ result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
+ return;
+ }
+ L1:
+ result = checkAliasThisForLhs(ad1, sc, e);
+ if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs
+ return;
+
+ result = checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
+ }
+ }
+
+ if (pop)
+ *pop = e.op;
+ scope OpOverload v = new OpOverload(sc, pop);
+ e.accept(v);
+ return v.result;
+}
+
+/******************************************
+ * Common code for overloading of EqualExp and CmpExp
+ */
+private Expression compare_overload(BinExp e, Scope* sc, Identifier id, TOK* pop)
+{
+ //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars());
+ AggregateDeclaration ad1 = isAggregate(e.e1.type);
+ AggregateDeclaration ad2 = isAggregate(e.e2.type);
+ Dsymbol s = null;
+ Dsymbol s_r = null;
+ if (ad1)
+ {
+ s = search_function(ad1, id);
+ }
+ if (ad2)
+ {
+ s_r = search_function(ad2, id);
+ if (s == s_r)
+ s_r = null;
+ }
+ Objects* tiargs = null;
+ if (s || s_r)
+ {
+ /* Try:
+ * a.opEquals(b)
+ * b.opEquals(a)
+ * and see which is better.
+ */
+ Expressions args1 = Expressions(1);
+ args1[0] = e.e1;
+ expandTuples(&args1);
+ Expressions args2 = Expressions(1);
+ args2[0] = e.e2;
+ expandTuples(&args2);
+ MatchAccumulator m;
+ if (0 && s && s_r)
+ {
+ printf("s : %s\n", s.toPrettyChars());
+ printf("s_r: %s\n", s_r.toPrettyChars());
+ }
+ if (s)
+ {
+ functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2);
+ if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ return ErrorExp.get();
+ }
+ FuncDeclaration lastf = m.lastf;
+ int count = m.count;
+ if (s_r)
+ {
+ functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1);
+ if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors))
+ return ErrorExp.get();
+ }
+ if (m.count > 1)
+ {
+ /* The following if says "not ambiguous" if there's one match
+ * from s and one from s_r, in which case we pick s.
+ * This doesn't follow the spec, but is a workaround for the case
+ * where opEquals was generated from templates and we cannot figure
+ * out if both s and s_r came from the same declaration or not.
+ * The test case is:
+ * import std.typecons;
+ * void main() {
+ * assert(tuple("has a", 2u) == tuple("has a", 1));
+ * }
+ */
+ if (!(m.lastf == lastf && m.count == 2 && count == 1))
+ {
+ // Error, ambiguous
+ e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
+ }
+ }
+ else if (m.last == MATCH.nomatch)
+ {
+ m.lastf = null;
+ }
+ Expression result;
+ if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
+ {
+ // Rewrite (e1 op e2) as e1.opfunc(e2)
+ result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
+ }
+ else
+ {
+ // Rewrite (e1 op e2) as e2.opfunc_r(e1)
+ result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
+ // When reversing operands of comparison operators,
+ // need to reverse the sense of the op
+ if (pop)
+ *pop = reverseRelation(e.op);
+ }
+ return result;
+ }
+ /*
+ * https://issues.dlang.org/show_bug.cgi?id=16657
+ * at this point, no matching opEquals was found for structs,
+ * so we should not follow the alias this comparison code.
+ */
+ if ((e.op == TOK.equal || e.op == TOK.notEqual) && ad1 == ad2)
+ return null;
+ Expression result = checkAliasThisForLhs(ad1, sc, e);
+ return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
+}
+
+/***********************************
+ * Utility to build a function call out of this reference and argument.
+ */
+Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d)
+{
+ assert(d);
+ Expression e;
+ Declaration decl = d.isDeclaration();
+ if (decl)
+ e = new DotVarExp(loc, ethis, decl, false);
+ else
+ e = new DotIdExp(loc, ethis, d.ident);
+ e = new CallExp(loc, e, earg);
+ e = e.expressionSemantic(sc);
+ return e;
+}
+
+/***************************************
+ * Search for function funcid in aggregate ad.
+ */
+Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
+{
+ Dsymbol s = ad.search(Loc.initial, funcid);
+ if (s)
+ {
+ //printf("search_function: s = '%s'\n", s.kind());
+ Dsymbol s2 = s.toAlias();
+ //printf("search_function: s2 = '%s'\n", s2.kind());
+ FuncDeclaration fd = s2.isFuncDeclaration();
+ if (fd && fd.type.ty == Tfunction)
+ return fd;
+ TemplateDeclaration td = s2.isTemplateDeclaration();
+ if (td)
+ return td;
+ }
+ return null;
+}
+
+/**************************************
+ * Figure out what is being foreach'd over by looking at the ForeachAggregate.
+ * Params:
+ * sc = context
+ * isForeach = true for foreach, false for foreach_reverse
+ * feaggr = ForeachAggregate
+ * sapply = set to function opApply/opApplyReverse, or delegate, or null.
+ * Overload resolution is not done.
+ * Returns:
+ * true if successfully figured it out; feaggr updated with semantic analysis.
+ * false for failed, which is an error.
+ */
+bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out Dsymbol sapply)
+{
+ //printf("inferForeachAggregate(%s)\n", feaggr.toChars());
+ bool sliced;
+ Type att = null;
+ auto aggr = feaggr;
+ while (1)
+ {
+ aggr = aggr.expressionSemantic(sc);
+ aggr = resolveProperties(sc, aggr);
+ aggr = aggr.optimize(WANTvalue);
+ if (!aggr.type || aggr.op == TOK.error)
+ return false;
+ Type tab = aggr.type.toBasetype();
+ switch (tab.ty)
+ {
+ case Tarray: // https://dlang.org/spec/statement.html#foreach_over_arrays
+ case Tsarray: // https://dlang.org/spec/statement.html#foreach_over_arrays
+ case Ttuple: // https://dlang.org/spec/statement.html#foreach_over_tuples
+ case Taarray: // https://dlang.org/spec/statement.html#foreach_over_associative_arrays
+ break;
+
+ case Tclass:
+ case Tstruct:
+ {
+ AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym
+ : (cast(TypeStruct)tab).sym;
+ if (!sliced)
+ {
+ sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse);
+ if (sapply)
+ {
+ // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes
+ // opApply aggregate
+ break;
+ }
+ if (feaggr.op != TOK.type)
+ {
+ /* See if rewriting `aggr` to `aggr[]` will work
+ */
+ Expression rinit = new ArrayExp(aggr.loc, feaggr);
+ rinit = rinit.trySemantic(sc);
+ if (rinit) // if it worked
+ {
+ aggr = rinit;
+ sliced = true; // only try it once
+ continue;
+ }
+ }
+ }
+ if (ad.search(Loc.initial, isForeach ? Id.Ffront : Id.Fback))
+ {
+ // https://dlang.org/spec/statement.html#foreach-with-ranges
+ // range aggregate
+ break;
+ }
+ if (ad.aliasthis)
+ {
+ if (isRecursiveAliasThis(att, tab)) // error, circular alias this
+ return false;
+ aggr = resolveAliasThis(sc, aggr);
+ continue;
+ }
+ return false;
+ }
+
+ case Tdelegate: // https://dlang.org/spec/statement.html#foreach_over_delegates
+ if (aggr.op == TOK.delegate_)
+ {
+ sapply = (cast(DelegateExp)aggr).func;
+ }
+ break;
+
+ case Terror:
+ break;
+
+ default:
+ return false;
+ }
+ feaggr = aggr;
+ return true;
+ }
+ assert(0);
+}
+
+/*****************************************
+ * Given array of foreach parameters and an aggregate type,
+ * find best opApply overload,
+ * if any of the parameter types are missing, attempt to infer
+ * them from the aggregate type.
+ * Params:
+ * fes = the foreach statement
+ * sc = context
+ * sapply = null or opApply or delegate
+ * Returns:
+ * false for errors
+ */
+bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply)
+{
+ if (!fes.parameters || !fes.parameters.dim)
+ return false;
+ if (sapply) // prefer opApply
+ {
+ foreach (Parameter p; *fes.parameters)
+ {
+ if (p.type)
+ {
+ p.type = p.type.typeSemantic(fes.loc, sc);
+ p.type = p.type.addStorageClass(p.storageClass);
+ }
+ }
+
+ // Determine ethis for sapply
+ Expression ethis;
+ Type tab = fes.aggr.type.toBasetype();
+ if (tab.ty == Tclass || tab.ty == Tstruct)
+ ethis = fes.aggr;
+ else
+ {
+ assert(tab.ty == Tdelegate && fes.aggr.op == TOK.delegate_);
+ ethis = (cast(DelegateExp)fes.aggr).e1;
+ }
+
+ /* Look for like an
+ * int opApply(int delegate(ref Type [, ...]) dg);
+ * overload
+ */
+ if (FuncDeclaration fd = sapply.isFuncDeclaration())
+ {
+ auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters);
+ if (fdapply)
+ {
+ // Fill in any missing types on foreach parameters[]
+ matchParamsToOpApply(cast(TypeFunction)fdapply.type, fes.parameters, true);
+ sapply = fdapply;
+ return true;
+ }
+ return false;
+ }
+ return sapply !is null;
+ }
+
+ Parameter p = (*fes.parameters)[0];
+ Type taggr = fes.aggr.type;
+ assert(taggr);
+ Type tab = taggr.toBasetype();
+ switch (tab.ty)
+ {
+ case Tarray:
+ case Tsarray:
+ case Ttuple:
+ if (fes.parameters.dim == 2)
+ {
+ if (!p.type)
+ {
+ p.type = Type.tsize_t; // key type
+ p.type = p.type.addStorageClass(p.storageClass);
+ }
+ p = (*fes.parameters)[1];
+ }
+ if (!p.type && tab.ty != Ttuple)
+ {
+ p.type = tab.nextOf(); // value type
+ p.type = p.type.addStorageClass(p.storageClass);
+ }
+ break;
+
+ case Taarray:
+ {
+ TypeAArray taa = cast(TypeAArray)tab;
+ if (fes.parameters.dim == 2)
+ {
+ if (!p.type)
+ {
+ p.type = taa.index; // key type
+ p.type = p.type.addStorageClass(p.storageClass);
+ if (p.storageClass & STC.ref_) // key must not be mutated via ref
+ p.type = p.type.addMod(MODFlags.const_);
+ }
+ p = (*fes.parameters)[1];
+ }
+ if (!p.type)
+ {
+ p.type = taa.next; // value type
+ p.type = p.type.addStorageClass(p.storageClass);
+ }
+ break;
+ }
+
+ case Tclass:
+ case Tstruct:
+ {
+ AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym
+ : (cast(TypeStruct)tab).sym;
+ if (fes.parameters.dim == 1)
+ {
+ if (!p.type)
+ {
+ /* Look for a front() or back() overload
+ */
+ Identifier id = (fes.op == TOK.foreach_) ? Id.Ffront : Id.Fback;
+ Dsymbol s = ad.search(Loc.initial, id);
+ FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
+ if (fd)
+ {
+ // Resolve inout qualifier of front type
+ p.type = fd.type.nextOf();
+ if (p.type)
+ {
+ p.type = p.type.substWildTo(tab.mod);
+ p.type = p.type.addStorageClass(p.storageClass);
+ }
+ }
+ else if (s && s.isTemplateDeclaration())
+ {
+ }
+ else if (s && s.isDeclaration())
+ p.type = (cast(Declaration)s).type;
+ else
+ break;
+ }
+ break;
+ }
+ break;
+ }
+
+ case Tdelegate:
+ if (!matchParamsToOpApply(cast(TypeFunction)tab.nextOf(), fes.parameters, true))
+ return false;
+ break;
+
+ default:
+ break; // ignore error, caught later
+ }
+ return true;
+}
+
+/*********************************************
+ * Find best overload match on fstart given ethis and parameters[].
+ * Params:
+ * ethis = expression to use for `this`
+ * fstart = opApply or foreach delegate
+ * parameters = ForeachTypeList (i.e. foreach parameters)
+ * Returns:
+ * best match if there is one, null if error
+ */
+private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration fstart, Parameters* parameters)
+{
+ MOD mod = ethis.type.mod;
+ MATCH match = MATCH.nomatch;
+ FuncDeclaration fd_best;
+ FuncDeclaration fd_ambig;
+
+ overloadApply(fstart, (Dsymbol s)
+ {
+ auto f = s.isFuncDeclaration();
+ if (!f)
+ return 0; // continue
+ auto tf = cast(TypeFunction)f.type;
+ MATCH m = MATCH.exact;
+ if (f.isThis())
+ {
+ if (!MODimplicitConv(mod, tf.mod))
+ m = MATCH.nomatch;
+ else if (mod != tf.mod)
+ m = MATCH.constant;
+ }
+ if (!matchParamsToOpApply(tf, parameters, false))
+ m = MATCH.nomatch;
+ if (m > match)
+ {
+ fd_best = f;
+ fd_ambig = null;
+ match = m;
+ }
+ else if (m == match && m > MATCH.nomatch)
+ {
+ assert(fd_best);
+ /* Ignore covariant matches, as later on it can be redone
+ * after the opApply delegate has its attributes inferred.
+ */
+ if (tf.covariant(fd_best.type) != Covariant.yes &&
+ fd_best.type.covariant(tf) != Covariant.yes)
+ fd_ambig = f; // not covariant, so ambiguous
+ }
+ return 0; // continue
+ });
+
+ if (fd_ambig)
+ {
+ .error(ethis.loc, "`%s.%s` matches more than one declaration:\n`%s`: `%s`\nand:\n`%s`: `%s`",
+ ethis.toChars(), fstart.ident.toChars(),
+ fd_best.loc.toChars(), fd_best.type.toChars(),
+ fd_ambig.loc.toChars(), fd_ambig.type.toChars());
+ return null;
+ }
+
+ return fd_best;
+}
+
+/******************************
+ * Determine if foreach parameters match opApply parameters.
+ * Infer missing foreach parameter types from type of opApply delegate.
+ * Params:
+ * tf = type of opApply or delegate
+ * parameters = foreach parameters
+ * infer = infer missing parameter types
+ * Returns:
+ * true for match for this function
+ * false for no match for this function
+ */
+private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool infer)
+{
+ enum nomatch = false;
+
+ /* opApply/delegate has exactly one parameter, and that parameter
+ * is a delegate that looks like:
+ * int opApply(int delegate(ref Type [, ...]) dg);
+ */
+ if (tf.parameterList.length != 1)
+ return nomatch;
+
+ /* Get the type of opApply's dg parameter
+ */
+ Parameter p0 = tf.parameterList[0];
+ if (p0.type.ty != Tdelegate)
+ return nomatch;
+ TypeFunction tdg = cast(TypeFunction)p0.type.nextOf();
+ assert(tdg.ty == Tfunction);
+
+ /* We now have tdg, the type of the delegate.
+ * tdg's parameters must match that of the foreach arglist (i.e. parameters).
+ * Fill in missing types in parameters.
+ */
+ const nparams = tdg.parameterList.length;
+ if (nparams == 0 || nparams != parameters.dim || tdg.parameterList.varargs != VarArg.none)
+ return nomatch; // parameter mismatch
+
+ foreach (u, p; *parameters)
+ {
+ Parameter param = tdg.parameterList[u];
+ if (p.type)
+ {
+ if (!p.type.equals(param.type))
+ return nomatch;
+ }
+ else if (infer)
+ {
+ p.type = param.type;
+ p.type = p.type.addStorageClass(p.storageClass);
+ }
+ }
+ return true;
+}
+
+/**
+ * Reverse relational operator, eg >= becomes <=
+ * Note this is not negation.
+ * Params:
+ * op = comparison operator to reverse
+ * Returns:
+ * reverse of op
+ */
+private TOK reverseRelation(TOK op) pure
+{
+ switch (op)
+ {
+ case TOK.greaterOrEqual: op = TOK.lessOrEqual; break;
+ case TOK.greaterThan: op = TOK.lessThan; break;
+ case TOK.lessOrEqual: op = TOK.greaterOrEqual; break;
+ case TOK.lessThan: op = TOK.greaterThan; break;
+ default: break;
+ }
+ return op;
+}
diff --git a/gcc/d/dmd/optimize.c b/gcc/d/dmd/optimize.c
deleted file mode 100644
index 44dedd84038..00000000000
--- a/gcc/d/dmd/optimize.c
+++ /dev/null
@@ -1,1230 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/optimize.c
- */
-
-#include "root/dsystem.h"
-
-#include "root/checkedint.h"
-#include "lexer.h"
-#include "mtype.h"
-#include "expression.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "init.h"
-#include "enum.h"
-#include "ctfe.h"
-#include "errors.h"
-
-/*************************************
- * If variable has a const initializer,
- * return that initializer.
- */
-
-Expression *expandVar(int result, VarDeclaration *v)
-{
- //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v->toChars() : "null");
-
- Expression *e = NULL;
- if (!v)
- return e;
- if (!v->originalType && v->semanticRun < PASSsemanticdone) // semantic() not yet run
- dsymbolSemantic(v, NULL);
-
- if (v->isConst() || v->isImmutable() || v->storage_class & STCmanifest)
- {
- if (!v->type)
- {
- return e;
- }
- Type *tb = v->type->toBasetype();
- if (v->storage_class & STCmanifest ||
- v->type->toBasetype()->isscalar() ||
- ((result & WANTexpand) && (tb->ty != Tsarray && tb->ty != Tstruct))
- )
- {
- if (v->_init)
- {
- if (v->inuse)
- {
- if (v->storage_class & STCmanifest)
- {
- v->error("recursive initialization of constant");
- goto Lerror;
- }
- goto L1;
- }
- Expression *ei = v->getConstInitializer();
- if (!ei)
- {
- if (v->storage_class & STCmanifest)
- {
- v->error("enum cannot be initialized with %s", v->_init->toChars());
- goto Lerror;
- }
- goto L1;
- }
- if (ei->op == TOKconstruct || ei->op == TOKblit)
- {
- AssignExp *ae = (AssignExp *)ei;
- ei = ae->e2;
- if (ei->isConst() == 1)
- {
- }
- else if (ei->op == TOKstring)
- {
- // Bugzilla 14459: We should not constfold the string literal
- // if it's typed as a C string, because the value expansion
- // will drop the pointer identity.
- if (!(result & WANTexpand) && ei->type->toBasetype()->ty == Tpointer)
- goto L1;
- }
- else
- goto L1;
-
- if (ei->type == v->type)
- {
- // const variable initialized with const expression
- }
- else if (ei->implicitConvTo(v->type) >= MATCHconst)
- {
- // const var initialized with non-const expression
- ei = ei->implicitCastTo(NULL, v->type);
- ei = expressionSemantic(ei, NULL);
- }
- else
- goto L1;
- }
- else if (!(v->storage_class & STCmanifest) &&
- ei->isConst() != 1 && ei->op != TOKstring &&
- ei->op != TOKaddress)
- {
- goto L1;
- }
- if (!ei->type)
- {
- goto L1;
- }
- else
- {
- // Should remove the copy() operation by
- // making all mods to expressions copy-on-write
- e = ei->copy();
- }
- }
- else
- {
- goto L1;
- }
- if (e->type != v->type)
- {
- e = e->castTo(NULL, v->type);
- }
- v->inuse++;
- e = e->optimize(result);
- v->inuse--;
- }
- }
-L1:
- //if (e) printf("\te = %p, %s, e->type = %d, %s\n", e, e->toChars(), e->type->ty, e->type->toChars());
- return e;
-
-Lerror:
- return new ErrorExp();
-}
-
-
-Expression *fromConstInitializer(int result, Expression *e1)
-{
- //printf("fromConstInitializer(result = %x, %s)\n", result, e1->toChars());
- //static int xx; if (xx++ == 10) assert(0);
- Expression *e = e1;
- if (e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)e1;
- VarDeclaration *v = ve->var->isVarDeclaration();
- e = expandVar(result, v);
- if (e)
- {
- // If it is a comma expression involving a declaration, we mustn't
- // perform a copy -- we'd get two declarations of the same variable.
- // See bugzilla 4465.
- if (e->op == TOKcomma && ((CommaExp *)e)->e1->op == TOKdeclaration)
- e = e1;
- else
-
- if (e->type != e1->type && e1->type && e1->type->ty != Tident)
- {
- // Type 'paint' operation
- e = e->copy();
- e->type = e1->type;
- }
- e->loc = e1->loc;
- }
- else
- {
- e = e1;
- }
- }
- return e;
-}
-
-Expression *Expression_optimize(Expression *e, int result, bool keepLvalue)
-{
- class OptimizeVisitor : public Visitor
- {
- public:
- int result;
- bool keepLvalue;
- Expression *ret;
-
- OptimizeVisitor(int result, bool keepLvalue)
- : result(result), keepLvalue(keepLvalue)
- {
- }
-
- void error()
- {
- ret = new ErrorExp();
- }
-
- bool expOptimize(Expression *&e, int flags, bool keepLvalue = false)
- {
- if (!e)
- return false;
- Expression *ex = e->optimize(flags, keepLvalue);
- if (ex->op == TOKerror)
- {
- ret = ex; // store error result
- return true;
- }
- else
- {
- e = ex; // modify original
- return false;
- }
- }
-
- bool unaOptimize(UnaExp *e, int flags)
- {
- return expOptimize(e->e1, flags);
- }
-
- bool binOptimize(BinExp *e, int flags)
- {
- expOptimize(e->e1, flags);
- expOptimize(e->e2, flags);
- return ret->op == TOKerror;
- }
-
- void visit(Expression *)
- {
- //printf("Expression::optimize(result = x%x) %s\n", result, e->toChars());
- }
-
- void visit(VarExp *e)
- {
- if (keepLvalue)
- {
- VarDeclaration *v = e->var->isVarDeclaration();
- if (v && !(v->storage_class & STCmanifest))
- return;
- }
- ret = fromConstInitializer(result, e);
- }
-
- void visit(TupleExp *e)
- {
- expOptimize(e->e0, WANTvalue);
- for (size_t i = 0; i < e->exps->length; i++)
- {
- expOptimize((*e->exps)[i], WANTvalue);
- }
- }
-
- void visit(ArrayLiteralExp *e)
- {
- if (e->elements)
- {
- expOptimize(e->basis, result & WANTexpand);
- for (size_t i = 0; i < e->elements->length; i++)
- {
- expOptimize((*e->elements)[i], result & WANTexpand);
- }
- }
- }
-
- void visit(AssocArrayLiteralExp *e)
- {
- assert(e->keys->length == e->values->length);
- for (size_t i = 0; i < e->keys->length; i++)
- {
- expOptimize((*e->keys)[i], result & WANTexpand);
- expOptimize((*e->values)[i], result & WANTexpand);
- }
- }
-
- void visit(StructLiteralExp *e)
- {
- if (e->stageflags & stageOptimize) return;
- int old = e->stageflags;
- e->stageflags |= stageOptimize;
- if (e->elements)
- {
- for (size_t i = 0; i < e->elements->length; i++)
- {
- expOptimize((*e->elements)[i], result & WANTexpand);
- }
- }
- e->stageflags = old;
- }
-
- void visit(UnaExp *e)
- {
- //printf("UnaExp::optimize() %s\n", e->toChars());
- if (unaOptimize(e, result))
- return;
- }
-
- void visit(NegExp *e)
- {
- if (unaOptimize(e, result))
- return;
-
- if (e->e1->isConst() == 1)
- {
- ret = Neg(e->type, e->e1).copy();
- }
- }
-
- void visit(ComExp *e)
- {
- if (unaOptimize(e, result))
- return;
-
- if (e->e1->isConst() == 1)
- {
- ret = Com(e->type, e->e1).copy();
- }
- }
-
- void visit(NotExp *e)
- {
- if (unaOptimize(e, result))
- return;
-
- if (e->e1->isConst() == 1)
- {
- ret = Not(e->type, e->e1).copy();
- }
- }
-
- void visit(SymOffExp *e)
- {
- assert(e->var);
- }
-
- void visit(AddrExp *e)
- {
- //printf("AddrExp::optimize(result = %d) %s\n", result, e->toChars());
-
- /* Rewrite &(a,b) as (a,&b)
- */
- if (e->e1->op == TOKcomma)
- {
- CommaExp *ce = (CommaExp *)e->e1;
- AddrExp *ae = new AddrExp(e->loc, ce->e2, e->type);
- ret = new CommaExp(ce->loc, ce->e1, ae);
- ret->type = e->type;
- return;
- }
-
- // Keep lvalue-ness
- if (expOptimize(e->e1, result, true))
- return;
-
- // Convert &*ex to ex
- if (e->e1->op == TOKstar)
- {
- Expression *ex = ((PtrExp *)e->e1)->e1;
- if (e->type->equals(ex->type))
- ret = ex;
- else if (e->type->toBasetype()->equivalent(ex->type->toBasetype()))
- {
- ret = ex->copy();
- ret->type = e->type;
- }
- return;
- }
- if (e->e1->op == TOKvar)
- {
- VarExp *ve = (VarExp *)e->e1;
- if (!ve->var->isOut() && !ve->var->isRef() &&
- !ve->var->isImportedSymbol())
- {
- ret = new SymOffExp(e->loc, ve->var, 0, ve->hasOverloads);
- ret->type = e->type;
- return;
- }
- }
- if (e->e1->op == TOKindex)
- {
- // Convert &array[n] to &array+n
- IndexExp *ae = (IndexExp *)e->e1;
-
- if (ae->e2->op == TOKint64 && ae->e1->op == TOKvar)
- {
- sinteger_t index = ae->e2->toInteger();
- VarExp *ve = (VarExp *)ae->e1;
- if (ve->type->ty == Tsarray
- && !ve->var->isImportedSymbol())
- {
- TypeSArray *ts = (TypeSArray *)ve->type;
- sinteger_t dim = ts->dim->toInteger();
- if (index < 0 || index >= dim)
- {
- e->error("array index %lld is out of bounds [0..%lld]", index, dim);
- return error();
- }
-
- bool overflow = false;
- const d_uns64 offset = mulu(index, ts->nextOf()->size(e->loc), overflow);
- if (overflow)
- {
- e->error("array offset overflow");
- return error();
- }
-
- ret = new SymOffExp(e->loc, ve->var, offset);
- ret->type = e->type;
- return;
- }
- }
- }
- }
-
- void visit(PtrExp *e)
- {
- //printf("PtrExp::optimize(result = x%x) %s\n", result, e->toChars());
- if (expOptimize(e->e1, result))
- return;
- // Convert *&ex to ex
- // But only if there is no type punning involved
- if (e->e1->op == TOKaddress)
- {
- Expression *ex = ((AddrExp *)e->e1)->e1;
- if (e->type->equals(ex->type))
- ret = ex;
- else if (e->type->toBasetype()->equivalent(ex->type->toBasetype()))
- {
- ret = ex->copy();
- ret->type = e->type;
- }
- }
- if (keepLvalue)
- return;
-
- // Constant fold *(&structliteral + offset)
- if (e->e1->op == TOKadd)
- {
- Expression *ex = Ptr(e->type, e->e1).copy();
- if (!CTFEExp::isCantExp(ex))
- {
- ret = ex;
- return;
- }
- }
-
- if (e->e1->op == TOKsymoff)
- {
- SymOffExp *se = (SymOffExp *)e->e1;
- VarDeclaration *v = se->var->isVarDeclaration();
- Expression *ex = expandVar(result, v);
- if (ex && ex->op == TOKstructliteral)
- {
- StructLiteralExp *sle = (StructLiteralExp *)ex;
- ex = sle->getField(e->type, (unsigned)se->offset);
- if (ex && !CTFEExp::isCantExp(ex))
- {
- ret = ex;
- return;
- }
- }
- }
- }
-
- void visit(DotVarExp *e)
- {
- //printf("DotVarExp::optimize(result = x%x) %s\n", result, e->toChars());
- if (expOptimize(e->e1, result))
- return;
- if (keepLvalue)
- return;
-
- Expression *ex = e->e1;
-
- if (ex->op == TOKvar)
- {
- VarExp *ve = (VarExp *)ex;
- VarDeclaration *v = ve->var->isVarDeclaration();
- ex = expandVar(result, v);
- }
-
- if (ex && ex->op == TOKstructliteral)
- {
- StructLiteralExp *sle = (StructLiteralExp *)ex;
- VarDeclaration *vf = e->var->isVarDeclaration();
- if (vf && !vf->overlapped)
- {
- /* Bugzilla 13021: Prevent optimization if vf has overlapped fields.
- */
- ex = sle->getField(e->type, vf->offset);
- if (ex && !CTFEExp::isCantExp(ex))
- {
- ret = ex;
- return;
- }
- }
- }
- }
-
- void visit(NewExp *e)
- {
- expOptimize(e->thisexp, WANTvalue);
-
- // Optimize parameters
- if (e->newargs)
- {
- for (size_t i = 0; i < e->newargs->length; i++)
- {
- expOptimize((*e->newargs)[i], WANTvalue);
- }
- }
-
- if (e->arguments)
- {
- for (size_t i = 0; i < e->arguments->length; i++)
- {
- expOptimize((*e->arguments)[i], WANTvalue);
- }
- }
- }
-
- void visit(CallExp *e)
- {
- //printf("CallExp::optimize(result = %d) %s\n", result, e->toChars());
-
- // Optimize parameters with keeping lvalue-ness
- if (expOptimize(e->e1, result))
- return;
- if (e->arguments)
- {
- Type *t1 = e->e1->type->toBasetype();
- if (t1->ty == Tdelegate) t1 = t1->nextOf();
- assert(t1->ty == Tfunction);
- TypeFunction *tf = (TypeFunction *)t1;
- for (size_t i = 0; i < e->arguments->length; i++)
- {
- Parameter *p = tf->parameterList[i];
- bool keep = p && (p->storageClass & (STCref | STCout)) != 0;
- expOptimize((*e->arguments)[i], WANTvalue, keep);
- }
- }
- }
-
- void visit(CastExp *e)
- {
- //printf("CastExp::optimize(result = %d) %s\n", result, e->toChars());
- //printf("from %s to %s\n", e->type->toChars(), e->to->toChars());
- //printf("from %s\n", e->type->toChars());
- //printf("e1->type %s\n", e->e1->type->toChars());
- //printf("type = %p\n", e->type);
- assert(e->type);
- TOK op1 = e->e1->op;
-
- Expression *e1old = e->e1;
- if (expOptimize(e->e1, result))
- return;
- e->e1 = fromConstInitializer(result, e->e1);
-
- if (e->e1 == e1old &&
- e->e1->op == TOKarrayliteral &&
- e->type->toBasetype()->ty == Tpointer &&
- e->e1->type->toBasetype()->ty != Tsarray)
- {
- // Casting this will result in the same expression, and
- // infinite loop because of Expression::implicitCastTo()
- return; // no change
- }
-
- if ((e->e1->op == TOKstring || e->e1->op == TOKarrayliteral) &&
- (e->type->ty == Tpointer || e->type->ty == Tarray))
- {
- const d_uns64 esz = e->type->nextOf()->size(e->loc);
- const d_uns64 e1sz = e->e1->type->toBasetype()->nextOf()->size(e->e1->loc);
- if (esz == SIZE_INVALID || e1sz == SIZE_INVALID)
- return error();
-
- if (e1sz == esz)
- {
- // Bugzilla 12937: If target type is void array, trying to paint
- // e->e1 with that type will cause infinite recursive optimization.
- if (e->type->nextOf()->ty == Tvoid)
- return;
-
- ret = e->e1->castTo(NULL, e->type);
- //printf(" returning1 %s\n", ret->toChars());
- return;
- }
- }
-
- if (e->e1->op == TOKstructliteral &&
- e->e1->type->implicitConvTo(e->type) >= MATCHconst)
- {
- //printf(" returning2 %s\n", e->e1->toChars());
- L1: // Returning e1 with changing its type
- ret = (e1old == e->e1 ? e->e1->copy() : e->e1);
- ret->type = e->type;
- return;
- }
-
- /* The first test here is to prevent infinite loops
- */
- if (op1 != TOKarrayliteral && e->e1->op == TOKarrayliteral)
- {
- ret = e->e1->castTo(NULL, e->to);
- return;
- }
- if (e->e1->op == TOKnull &&
- (e->type->ty == Tpointer || e->type->ty == Tclass || e->type->ty == Tarray))
- {
- //printf(" returning3 %s\n", e->e1->toChars());
- goto L1;
- }
-
- if (e->type->ty == Tclass && e->e1->type->ty == Tclass)
- {
- // See if we can remove an unnecessary cast
- ClassDeclaration *cdfrom = e->e1->type->isClassHandle();
- ClassDeclaration *cdto = e->type->isClassHandle();
- if (cdto == ClassDeclaration::object && !cdfrom->isInterfaceDeclaration())
- goto L1; // can always convert a class to Object
- // Need to determine correct offset before optimizing away the cast.
- // https://issues.dlang.org/show_bug.cgi?id=16980
- cdfrom->size(e->loc);
- assert(cdfrom->sizeok == SIZEOKdone);
- assert(cdto->sizeok == SIZEOKdone || !cdto->isBaseOf(cdfrom, NULL));
- int offset;
- if (cdto->isBaseOf(cdfrom, &offset) && offset == 0)
- {
- //printf(" returning4 %s\n", e->e1->toChars());
- goto L1;
- }
- }
-
- // We can convert 'head const' to mutable
- if (e->to->mutableOf()->constOf()->equals(e->e1->type->mutableOf()->constOf()))
- {
- //printf(" returning5 %s\n", e->e1->toChars());
- goto L1;
- }
-
- if (e->e1->isConst())
- {
- if (e->e1->op == TOKsymoff)
- {
- if (e->type->toBasetype()->ty != Tsarray)
- {
- const d_uns64 esz = e->type->size(e->loc);
- const d_uns64 e1sz = e->e1->type->size(e->e1->loc);
- if (esz == SIZE_INVALID ||
- e1sz == SIZE_INVALID)
- return error();
-
- if (esz == e1sz)
- goto L1;
- }
- return;
- }
- if (e->to->toBasetype()->ty != Tvoid)
- {
- if (e->e1->type->equals(e->type) && e->type->equals(e->to))
- ret = e->e1;
- else
- ret = Cast(e->loc, e->type, e->to, e->e1).copy();
- }
- }
- //printf(" returning6 %s\n", ret->toChars());
- }
-
- void visit(BinExp *e)
- {
- //printf("BinExp::optimize(result = %d) %s\n", result, e->toChars());
- // don't replace const variable with its initializer in e1
- bool e2only = (e->op == TOKconstruct || e->op == TOKblit);
- if (e2only ? expOptimize(e->e2, result) : binOptimize(e, result))
- return;
-
- if (e->op == TOKshlass || e->op == TOKshrass || e->op == TOKushrass)
- {
- if (e->e2->isConst() == 1)
- {
- sinteger_t i2 = e->e2->toInteger();
- d_uns64 sz = e->e1->type->size(e->e1->loc);
- assert(sz != SIZE_INVALID);
- sz *= 8;
- if (i2 < 0 || (d_uns64)i2 >= sz)
- {
- e->error("shift assign by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1);
- return error();
- }
- }
- }
- }
-
- void visit(AddExp *e)
- {
- //printf("AddExp::optimize(%s)\n", e->toChars());
-
- if (binOptimize(e, result))
- return;
-
- if (e->e1->isConst() && e->e2->isConst())
- {
- if (e->e1->op == TOKsymoff && e->e2->op == TOKsymoff)
- return;
- ret = Add(e->loc, e->type, e->e1, e->e2).copy();
- }
- }
-
- void visit(MinExp *e)
- {
- if (binOptimize(e, result))
- return;
-
- if (e->e1->isConst() && e->e2->isConst())
- {
- if (e->e2->op == TOKsymoff)
- return;
- ret = Min(e->loc, e->type, e->e1, e->e2).copy();
- }
- }
-
- void visit(MulExp *e)
- {
- //printf("MulExp::optimize(result = %d) %s\n", result, e->toChars());
-
- if (binOptimize(e, result))
- return;
-
- if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
- {
- ret = Mul(e->loc, e->type, e->e1, e->e2).copy();
- }
- }
-
- void visit(DivExp *e)
- {
- //printf("DivExp::optimize(%s)\n", e->toChars());
-
- if (binOptimize(e, result))
- return;
-
- if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
- {
- ret = Div(e->loc, e->type, e->e1, e->e2).copy();
- }
- }
-
- void visit(ModExp *e)
- {
- if (binOptimize(e, result))
- return;
-
- if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
- {
- ret = Mod(e->loc, e->type, e->e1, e->e2).copy();
- }
- }
-
- void shift_optimize(BinExp *e, UnionExp (*shift)(Loc, Type *, Expression *, Expression *))
- {
- if (binOptimize(e, result))
- return;
-
- if (e->e2->isConst() == 1)
- {
- sinteger_t i2 = e->e2->toInteger();
- d_uns64 sz = e->e1->type->size();
- assert(sz != SIZE_INVALID);
- sz *= 8;
- if (i2 < 0 || (d_uns64)i2 >= sz)
- {
- e->error("shift by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1);
- return error();
- }
- if (e->e1->isConst() == 1)
- ret = (*shift)(e->loc, e->type, e->e1, e->e2).copy();
- }
- }
-
- void visit(ShlExp *e)
- {
- //printf("ShlExp::optimize(result = %d) %s\n", result, e->toChars());
- shift_optimize(e, &Shl);
- }
-
- void visit(ShrExp *e)
- {
- //printf("ShrExp::optimize(result = %d) %s\n", result, e->toChars());
- shift_optimize(e, &Shr);
- }
-
- void visit(UshrExp *e)
- {
- //printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
- shift_optimize(e, &Ushr);
- }
-
- void visit(AndExp *e)
- {
- if (binOptimize(e, result))
- return;
-
- if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
- ret = And(e->loc, e->type, e->e1, e->e2).copy();
- }
-
- void visit(OrExp *e)
- {
- if (binOptimize(e, result))
- return;
-
- if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
- ret = Or(e->loc, e->type, e->e1, e->e2).copy();
- }
-
- void visit(XorExp *e)
- {
- if (binOptimize(e, result))
- return;
-
- if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
- ret = Xor(e->loc, e->type, e->e1, e->e2).copy();
- }
-
- void visit(PowExp *e)
- {
- if (binOptimize(e, result))
- return;
-
- // Replace 1 ^^ x or 1.0^^x by (x, 1)
- if ((e->e1->op == TOKint64 && e->e1->toInteger() == 1) ||
- (e->e1->op == TOKfloat64 && e->e1->toReal() == CTFloat::one))
- {
- ret = new CommaExp(e->loc, e->e2, e->e1);
- ret->type = e->type;
- return;
- }
-
- // Replace -1 ^^ x by (x&1) ? -1 : 1, where x is integral
- if (e->e2->type->isintegral() && e->e1->op == TOKint64 && (sinteger_t)e->e1->toInteger() == -1L)
- {
- ret = new AndExp(e->loc, e->e2, new IntegerExp(e->loc, 1, e->e2->type));
- ret->type = e->e2->type;
- ret = new CondExp(e->loc, ret, new IntegerExp(e->loc, -1L, e->type), new IntegerExp(e->loc, 1L, e->type));
- ret->type = e->type;
- return;
- }
-
- // Replace x ^^ 0 or x^^0.0 by (x, 1)
- if ((e->e2->op == TOKint64 && e->e2->toInteger() == 0) ||
- (e->e2->op == TOKfloat64 && e->e2->toReal() == CTFloat::zero))
- {
- if (e->e1->type->isintegral())
- ret = new IntegerExp(e->loc, 1, e->e1->type);
- else
- ret = new RealExp(e->loc, CTFloat::one, e->e1->type);
-
- ret = new CommaExp(e->loc, e->e1, ret);
- ret->type = e->type;
- return;
- }
-
- // Replace x ^^ 1 or x^^1.0 by (x)
- if ((e->e2->op == TOKint64 && e->e2->toInteger() == 1) ||
- (e->e2->op == TOKfloat64 && e->e2->toReal() == CTFloat::one))
- {
- ret = e->e1;
- return;
- }
-
- // Replace x ^^ -1.0 by (1.0 / x)
- if ((e->e2->op == TOKfloat64 && e->e2->toReal() == CTFloat::minusone))
- {
- ret = new DivExp(e->loc, new RealExp(e->loc, CTFloat::one, e->e2->type), e->e1);
- ret->type = e->type;
- return;
- }
-
- // All other negative integral powers are illegal
- if ((e->e1->type->isintegral()) && (e->e2->op == TOKint64) && (sinteger_t)e->e2->toInteger() < 0)
- {
- e->error("cannot raise %s to a negative integer power. Did you mean (cast(real)%s)^^%s ?",
- e->e1->type->toBasetype()->toChars(), e->e1->toChars(), e->e2->toChars());
- return error();
- }
-
- // If e2 *could* have been an integer, make it one.
- if (e->e2->op == TOKfloat64 && (e->e2->toReal() == ldouble((sinteger_t)e->e2->toReal())))
- e->e2 = new IntegerExp(e->loc, e->e2->toInteger(), Type::tint64);
-
- if (e->e1->isConst() == 1 && e->e2->isConst() == 1)
- {
- Expression *ex = Pow(e->loc, e->type, e->e1, e->e2).copy();
- if (!CTFEExp::isCantExp(ex))
- {
- ret = ex;
- return;
- }
- }
-
- // (2 ^^ n) ^^ p -> 1 << n * p
- if (e->e1->op == TOKint64 && e->e1->toInteger() > 0 &&
- !((e->e1->toInteger() - 1) & e->e1->toInteger()) &&
- e->e2->type->isintegral() && e->e2->type->isunsigned())
- {
- dinteger_t i = e->e1->toInteger();
- dinteger_t mul = 1;
- while ((i >>= 1) > 1)
- mul++;
- Expression *shift = new MulExp(e->loc, e->e2, new IntegerExp(e->loc, mul, e->e2->type));
- shift->type = e->e2->type;
- shift = shift->castTo(NULL, Type::tshiftcnt);
- ret = new ShlExp(e->loc, new IntegerExp(e->loc, 1, e->e1->type), shift);
- ret->type = e->type;
- return;
- }
- }
-
- void visit(CommaExp *e)
- {
- //printf("CommaExp::optimize(result = %d) %s\n", result, e->toChars());
- // Comma needs special treatment, because it may
- // contain compiler-generated declarations. We can interpret them, but
- // otherwise we must NOT attempt to constant-fold them.
- // In particular, if the comma returns a temporary variable, it needs
- // to be an lvalue (this is particularly important for struct constructors)
-
- expOptimize(e->e1, WANTvalue);
- expOptimize(e->e2, result, keepLvalue);
- if (ret->op == TOKerror)
- return;
-
- if (!e->e1 || e->e1->op == TOKint64 || e->e1->op == TOKfloat64 || !hasSideEffect(e->e1))
- {
- ret = e->e2;
- if (ret)
- ret->type = e->type;
- }
-
- //printf("-CommaExp::optimize(result = %d) %s\n", result, e->e->toChars());
- }
-
- void visit(ArrayLengthExp *e)
- {
- //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e->toChars());
-
- if (unaOptimize(e, WANTexpand))
- return;
-
- // CTFE interpret static immutable arrays (to get better diagnostics)
- if (e->e1->op == TOKvar)
- {
- VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
- if (v && (v->storage_class & STCstatic) && (v->storage_class & STCimmutable) && v->_init)
- {
- if (Expression *ci = v->getConstInitializer())
- e->e1 = ci;
- }
- }
-
- if (e->e1->op == TOKstring || e->e1->op == TOKarrayliteral || e->e1->op == TOKassocarrayliteral ||
- e->e1->type->toBasetype()->ty == Tsarray)
- {
- ret = ArrayLength(e->type, e->e1).copy();
- }
- }
-
- void visit(EqualExp *e)
- {
- //printf("EqualExp::optimize(result = %x) %s\n", result, e->toChars());
- if (binOptimize(e, WANTvalue))
- return;
-
- Expression *e1 = fromConstInitializer(result, e->e1);
- Expression *e2 = fromConstInitializer(result, e->e2);
- if (e1->op == TOKerror)
- {
- ret = e1;
- return;
- }
- if (e2->op == TOKerror)
- {
- ret = e2;
- return;
- }
-
- ret = Equal(e->op, e->loc, e->type, e1, e2).copy();
- if (CTFEExp::isCantExp(ret))
- ret = e;
- }
-
- void visit(IdentityExp *e)
- {
- //printf("IdentityExp::optimize(result = %d) %s\n", result, e->toChars());
-
- if (binOptimize(e, WANTvalue))
- return;
-
- if ((e->e1->isConst() && e->e2->isConst()) ||
- (e->e1->op == TOKnull && e->e2->op == TOKnull)
- )
- {
- ret = Identity(e->op, e->loc, e->type, e->e1, e->e2).copy();
- if (CTFEExp::isCantExp(ret))
- ret = e;
- }
- }
-
- /* It is possible for constant folding to change an array expression of
- * unknown length, into one where the length is known.
- * If the expression 'arr' is a literal, set lengthVar to be its length.
- */
- static void setLengthVarIfKnown(VarDeclaration *lengthVar, Expression *arr)
- {
- if (!lengthVar)
- return;
- if (lengthVar->_init && !lengthVar->_init->isVoidInitializer())
- return; // we have previously calculated the length
- size_t len;
- if (arr->op == TOKstring)
- len = ((StringExp *)arr)->len;
- else if (arr->op == TOKarrayliteral)
- len = ((ArrayLiteralExp *)arr)->elements->length;
- else
- {
- Type *t = arr->type->toBasetype();
- if (t->ty == Tsarray)
- len = (size_t)((TypeSArray *)t)->dim->toInteger();
- else
- return; // we don't know the length yet
- }
-
- Expression *dollar = new IntegerExp(Loc(), len, Type::tsize_t);
- lengthVar->_init = new ExpInitializer(Loc(), dollar);
- lengthVar->storage_class |= STCstatic | STCconst;
- }
-
- void visit(IndexExp *e)
- {
- //printf("IndexExp::optimize(result = %d) %s\n", result, e->toChars());
- if (expOptimize(e->e1, result & WANTexpand))
- return;
-
- Expression *ex = fromConstInitializer(result, e->e1);
-
- // We might know $ now
- setLengthVarIfKnown(e->lengthVar, ex);
-
- if (expOptimize(e->e2, WANTvalue))
- return;
- if (keepLvalue)
- return;
- ret = Index(e->type, ex, e->e2).copy();
- if (CTFEExp::isCantExp(ret))
- ret = e;
- }
-
- void visit(SliceExp *e)
- {
- //printf("SliceExp::optimize(result = %d) %s\n", result, e->toChars());
- if (expOptimize(e->e1, result & WANTexpand))
- return;
- if (!e->lwr)
- {
- if (e->e1->op == TOKstring)
- {
- // Convert slice of string literal into dynamic array
- Type *t = e->e1->type->toBasetype();
- if (Type *tn = t->nextOf())
- ret = e->e1->castTo(NULL, tn->arrayOf());
- }
- }
- else
- {
- e->e1 = fromConstInitializer(result, e->e1);
- // We might know $ now
- setLengthVarIfKnown(e->lengthVar, e->e1);
- expOptimize(e->lwr, WANTvalue);
- expOptimize(e->upr, WANTvalue);
- if (ret->op == TOKerror)
- return;
- ret = Slice(e->type, e->e1, e->lwr, e->upr).copy();
- if (CTFEExp::isCantExp(ret))
- ret = e;
- }
-
- // Bugzilla 14649: We need to leave the slice form so it might be
- // a part of array operation.
- // Assume that the backend codegen will handle the form `e[]`
- // as an equal to `e` itself.
- if (ret->op == TOKstring)
- {
- e->e1 = ret;
- e->lwr = NULL;
- e->upr = NULL;
- ret = e;
- }
- //printf("-SliceExp::optimize() %s\n", ret->toChars());
- }
-
- void visit(LogicalExp *e)
- {
- //printf("LogicalExp::optimize(%d) %s\n", result, e->toChars());
- if (expOptimize(e->e1, WANTvalue))
- return;
- const bool oror = e->op == TOKoror;
- if (e->e1->isBool(oror))
- {
- // Replace with (e1, oror)
- ret = new IntegerExp(e->loc, oror, Type::tbool);
- ret = Expression::combine(e->e1, ret);
- if (e->type->toBasetype()->ty == Tvoid)
- {
- ret = new CastExp(e->loc, ret, Type::tvoid);
- ret->type = e->type;
- }
- ret = ret->optimize(result);
- return;
- }
-
- if (expOptimize(e->e2, WANTvalue))
- return;
-
- if (e->e1->isConst())
- {
- if (e->e2->isConst())
- {
- bool n1 = e->e1->isBool(true);
- bool n2 = e->e2->isBool(true);
- ret = new IntegerExp(e->loc, oror ? (n1 || n2) : (n1 && n2), e->type);
- }
- else if (e->e1->isBool(!oror))
- {
- if (e->type->toBasetype()->ty == Tvoid)
- ret = e->e2;
- else
- {
- ret = new CastExp(e->loc, e->e2, e->type);
- ret->type = e->type;
- }
- }
- }
- }
-
- void visit(CmpExp *e)
- {
- //printf("CmpExp::optimize() %s\n", e->toChars());
- if (binOptimize(e, WANTvalue))
- return;
-
- Expression *e1 = fromConstInitializer(result, e->e1);
- Expression *e2 = fromConstInitializer(result, e->e2);
-
- ret = Cmp(e->op, e->loc, e->type, e1, e2).copy();
- if (CTFEExp::isCantExp(ret))
- ret = e;
- }
-
- void visit(CatExp *e)
- {
- //printf("CatExp::optimize(%d) %s\n", result, e->toChars());
-
- if (binOptimize(e, result))
- return;
-
- if (e->e1->op == TOKcat)
- {
- // Bugzilla 12798: optimize ((expr ~ str1) ~ str2)
- CatExp *ce1 = (CatExp *)e->e1;
- CatExp cex(e->loc, ce1->e2, e->e2);
- cex.type = e->type;
- Expression *ex = cex.optimize(result);
- if (ex != &cex)
- {
- e->e1 = ce1->e1;
- e->e2 = ex;
- }
- }
-
- // optimize "str"[] -> "str"
- if (e->e1->op == TOKslice)
- {
- SliceExp *se1 = (SliceExp *)e->e1;
- if (se1->e1->op == TOKstring && !se1->lwr)
- e->e1 = se1->e1;
- }
- if (e->e2->op == TOKslice)
- {
- SliceExp *se2 = (SliceExp *)e->e2;
- if (se2->e1->op == TOKstring && !se2->lwr)
- e->e2 = se2->e1;
- }
-
- ret = Cat(e->type, e->e1, e->e2).copy();
- if (CTFEExp::isCantExp(ret))
- ret = e;
- }
-
- void visit(CondExp *e)
- {
- if (expOptimize(e->econd, WANTvalue))
- return;
- if (e->econd->isBool(true))
- ret = e->e1->optimize(result, keepLvalue);
- else if (e->econd->isBool(false))
- ret = e->e2->optimize(result, keepLvalue);
- else
- {
- expOptimize(e->e1, result, keepLvalue);
- expOptimize(e->e2, result, keepLvalue);
- }
- }
- };
-
- OptimizeVisitor v(result, keepLvalue);
- Expression *ex = NULL;
- v.ret = e;
-
- // Optimize the expression until it can no longer be simplified.
- size_t b = 0;
- while (1)
- {
- if (b++ == global.recursionLimit)
- {
- e->error("infinite loop while optimizing expression");
- fatal();
- }
- ex = v.ret;
- ex->accept(&v);
- if (ex == v.ret)
- break;
- }
- return ex;
-}
diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d
new file mode 100644
index 00000000000..3ae30619a20
--- /dev/null
+++ b/gcc/d/dmd/optimize.d
@@ -0,0 +1,1186 @@
+/**
+ * Perform constant folding.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d, _optimize.d)
+ * Documentation: https://dlang.org/phobos/dmd_optimize.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/optimize.d
+ */
+
+module dmd.optimize;
+
+import core.stdc.stdio;
+
+import dmd.astenums;
+import dmd.constfold;
+import dmd.ctfeexpr;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.globals;
+import dmd.init;
+import dmd.mtype;
+import dmd.root.ctfloat;
+import dmd.sideeffect;
+import dmd.tokens;
+import dmd.visitor;
+
+/*************************************
+ * If variable has a const initializer,
+ * return that initializer.
+ * Returns:
+ * initializer if there is one,
+ * null if not,
+ * ErrorExp if error
+ */
+Expression expandVar(int result, VarDeclaration v)
+{
+ //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null");
+
+ /********
+ * Params:
+ * e = initializer expression
+ */
+ Expression initializerReturn(Expression e)
+ {
+ if (e.type != v.type)
+ {
+ e = e.castTo(null, v.type);
+ }
+ v.inuse++;
+ e = e.optimize(result);
+ v.inuse--;
+ //if (e) printf("\te = %p, %s, e.type = %d, %s\n", e, e.toChars(), e.type.ty, e.type.toChars());
+ return e;
+ }
+
+ static Expression nullReturn()
+ {
+ return null;
+ }
+
+ static Expression errorReturn()
+ {
+ return ErrorExp.get();
+ }
+
+ if (!v)
+ return nullReturn();
+ if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run
+ v.dsymbolSemantic(null);
+ if (v.type &&
+ (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest))
+ {
+ Type tb = v.type.toBasetype();
+ if (v.storage_class & STC.manifest ||
+ tb.isscalar() ||
+ ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct)))
+ {
+ if (v._init)
+ {
+ if (v.inuse)
+ {
+ if (v.storage_class & STC.manifest)
+ {
+ v.error("recursive initialization of constant");
+ return errorReturn();
+ }
+ return nullReturn();
+ }
+ Expression ei = v.getConstInitializer();
+ if (!ei)
+ {
+ if (v.storage_class & STC.manifest)
+ {
+ v.error("enum cannot be initialized with `%s`", v._init.toChars());
+ return errorReturn();
+ }
+ return nullReturn();
+ }
+ if (ei.op == TOK.construct || ei.op == TOK.blit)
+ {
+ AssignExp ae = cast(AssignExp)ei;
+ ei = ae.e2;
+ if (ei.isConst() == 1)
+ {
+ }
+ else if (ei.op == TOK.string_)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14459
+ // Do not constfold the string literal
+ // if it's typed as a C string, because the value expansion
+ // will drop the pointer identity.
+ if (!(result & WANTexpand) && ei.type.toBasetype().ty == Tpointer)
+ return nullReturn();
+ }
+ else
+ return nullReturn();
+ if (ei.type == v.type)
+ {
+ // const variable initialized with const expression
+ }
+ else if (ei.implicitConvTo(v.type) >= MATCH.constant)
+ {
+ // const var initialized with non-const expression
+ ei = ei.implicitCastTo(null, v.type);
+ ei = ei.expressionSemantic(null);
+ }
+ else
+ return nullReturn();
+ }
+ else if (!(v.storage_class & STC.manifest) &&
+ ei.isConst() != 1 &&
+ ei.op != TOK.string_ &&
+ ei.op != TOK.address)
+ {
+ return nullReturn();
+ }
+
+ if (!ei.type)
+ {
+ return nullReturn();
+ }
+ else
+ {
+ // Should remove the copy() operation by
+ // making all mods to expressions copy-on-write
+ return initializerReturn(ei.copy());
+ }
+ }
+ else
+ {
+ // v does not have an initializer
+ version (all)
+ {
+ return nullReturn();
+ }
+ else
+ {
+ // BUG: what if const is initialized in constructor?
+ auto e = v.type.defaultInit();
+ e.loc = e1.loc;
+ return initializerReturn(e);
+ }
+ }
+ assert(0);
+ }
+ }
+ return nullReturn();
+}
+
+private Expression fromConstInitializer(int result, Expression e1)
+{
+ //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars());
+ //static int xx; if (xx++ == 10) assert(0);
+ Expression e = e1;
+ if (e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)e1;
+ VarDeclaration v = ve.var.isVarDeclaration();
+ e = expandVar(result, v);
+ if (e)
+ {
+ // If it is a comma expression involving a declaration, we mustn't
+ // perform a copy -- we'd get two declarations of the same variable.
+ // See bugzilla 4465.
+ if (e.op == TOK.comma && (cast(CommaExp)e).e1.op == TOK.declaration)
+ e = e1;
+ else if (e.type != e1.type && e1.type && e1.type.ty != Tident)
+ {
+ // Type 'paint' operation
+ e = e.copy();
+ e.type = e1.type;
+ }
+ e.loc = e1.loc;
+ }
+ else
+ {
+ e = e1;
+ }
+ }
+ return e;
+}
+
+/* It is possible for constant folding to change an array expression of
+ * unknown length, into one where the length is known.
+ * If the expression 'arr' is a literal, set lengthVar to be its length.
+ */
+package void setLengthVarIfKnown(VarDeclaration lengthVar, Expression arr)
+{
+ if (!lengthVar)
+ return;
+ if (lengthVar._init && !lengthVar._init.isVoidInitializer())
+ return; // we have previously calculated the length
+ size_t len;
+ if (arr.op == TOK.string_)
+ len = (cast(StringExp)arr).len;
+ else if (arr.op == TOK.arrayLiteral)
+ len = (cast(ArrayLiteralExp)arr).elements.dim;
+ else
+ {
+ Type t = arr.type.toBasetype();
+ if (t.ty == Tsarray)
+ len = cast(size_t)(cast(TypeSArray)t).dim.toInteger();
+ else
+ return; // we don't know the length yet
+ }
+ Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
+ lengthVar._init = new ExpInitializer(Loc.initial, dollar);
+ lengthVar.storage_class |= STC.static_ | STC.const_;
+}
+
+/* Same as above, but determines the length from 'type'. */
+package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
+{
+ if (!lengthVar)
+ return;
+ if (lengthVar._init && !lengthVar._init.isVoidInitializer())
+ return; // we have previously calculated the length
+ size_t len;
+ Type t = type.toBasetype();
+ if (t.ty == Tsarray)
+ len = cast(size_t)(cast(TypeSArray)t).dim.toInteger();
+ else
+ return; // we don't know the length yet
+ Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
+ lengthVar._init = new ExpInitializer(Loc.initial, dollar);
+ lengthVar.storage_class |= STC.static_ | STC.const_;
+}
+
+/*********************************
+ * Constant fold an Expression.
+ * Params:
+ * e = expression to const fold; this may get modified in-place
+ * result = WANTvalue, WANTexpand, or both
+ * keepLvalue = `e` is an lvalue, and keep it as an lvalue since it is
+ * an argument to a `ref` or `out` parameter, or the operand of `&` operator
+ * Returns:
+ * Constant folded version of `e`
+ */
+Expression Expression_optimize(Expression e, int result, bool keepLvalue)
+{
+ extern (C++) final class OptimizeVisitor : Visitor
+ {
+ alias visit = Visitor.visit;
+
+ Expression ret;
+ private const int result;
+ private const bool keepLvalue;
+
+ extern (D) this(Expression e, int result, bool keepLvalue)
+ {
+ this.ret = e; // default result is original expression
+ this.result = result;
+ this.keepLvalue = keepLvalue;
+ }
+
+ void error()
+ {
+ ret = ErrorExp.get();
+ }
+
+ bool expOptimize(ref Expression e, int flags, bool keepLvalue = false)
+ {
+ if (!e)
+ return false;
+ Expression ex = Expression_optimize(e, flags, keepLvalue);
+ if (ex.op == TOK.error)
+ {
+ ret = ex; // store error result
+ return true;
+ }
+ else
+ {
+ e = ex; // modify original
+ return false;
+ }
+ }
+
+ bool unaOptimize(UnaExp e, int flags)
+ {
+ return expOptimize(e.e1, flags);
+ }
+
+ bool binOptimize(BinExp e, int flags, bool keepLhsLvalue = false)
+ {
+ expOptimize(e.e1, flags, keepLhsLvalue);
+ expOptimize(e.e2, flags);
+ return ret.op == TOK.error;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression::optimize(result = x%x) %s\n", result, e.toChars());
+ }
+
+ override void visit(VarExp e)
+ {
+ VarDeclaration v = e.var.isVarDeclaration();
+
+ if (!(keepLvalue && v && !(v.storage_class & STC.manifest)))
+ ret = fromConstInitializer(result, e);
+
+ // if unoptimized, try to optimize the dtor expression
+ // (e.g., might be a LogicalExp with constant lhs)
+ if (ret == e && v && v.edtor)
+ {
+ // prevent infinite recursion (`<var>.~this()`)
+ if (!v.inuse)
+ {
+ v.inuse++;
+ expOptimize(v.edtor, WANTvalue);
+ v.inuse--;
+ }
+ }
+ }
+
+ override void visit(TupleExp e)
+ {
+ expOptimize(e.e0, WANTvalue);
+ for (size_t i = 0; i < e.exps.dim; i++)
+ {
+ expOptimize((*e.exps)[i], WANTvalue);
+ }
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ if (e.elements)
+ {
+ expOptimize(e.basis, result & WANTexpand);
+ for (size_t i = 0; i < e.elements.dim; i++)
+ {
+ expOptimize((*e.elements)[i], result & WANTexpand);
+ }
+ }
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ assert(e.keys.dim == e.values.dim);
+ for (size_t i = 0; i < e.keys.dim; i++)
+ {
+ expOptimize((*e.keys)[i], result & WANTexpand);
+ expOptimize((*e.values)[i], result & WANTexpand);
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ if (e.stageflags & stageOptimize)
+ return;
+ int old = e.stageflags;
+ e.stageflags |= stageOptimize;
+ if (e.elements)
+ {
+ for (size_t i = 0; i < e.elements.dim; i++)
+ {
+ expOptimize((*e.elements)[i], result & WANTexpand);
+ }
+ }
+ e.stageflags = old;
+ }
+
+ override void visit(UnaExp e)
+ {
+ //printf("UnaExp::optimize() %s\n", e.toChars());
+ if (unaOptimize(e, result))
+ return;
+ }
+
+ override void visit(NegExp e)
+ {
+ if (unaOptimize(e, result))
+ return;
+ if (e.e1.isConst() == 1)
+ {
+ ret = Neg(e.type, e.e1).copy();
+ }
+ }
+
+ override void visit(ComExp e)
+ {
+ if (unaOptimize(e, result))
+ return;
+ if (e.e1.isConst() == 1)
+ {
+ ret = Com(e.type, e.e1).copy();
+ }
+ }
+
+ override void visit(NotExp e)
+ {
+ if (unaOptimize(e, result))
+ return;
+ if (e.e1.isConst() == 1)
+ {
+ ret = Not(e.type, e.e1).copy();
+ }
+ }
+
+ override void visit(SymOffExp e)
+ {
+ assert(e.var);
+ }
+
+ override void visit(AddrExp e)
+ {
+ //printf("AddrExp::optimize(result = %d) %s\n", result, e.toChars());
+ /* Rewrite &(a,b) as (a,&b)
+ */
+ if (e.e1.op == TOK.comma)
+ {
+ CommaExp ce = cast(CommaExp)e.e1;
+ auto ae = new AddrExp(e.loc, ce.e2, e.type);
+ ret = new CommaExp(ce.loc, ce.e1, ae);
+ ret.type = e.type;
+ return;
+ }
+ // Keep lvalue-ness
+ if (expOptimize(e.e1, result, true))
+ return;
+ // Convert &*ex to ex
+ if (e.e1.op == TOK.star)
+ {
+ Expression ex = (cast(PtrExp)e.e1).e1;
+ if (e.type.equals(ex.type))
+ ret = ex;
+ else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
+ {
+ ret = ex.copy();
+ ret.type = e.type;
+ }
+ return;
+ }
+ if (e.e1.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)e.e1;
+ if (!ve.var.isReference() && !ve.var.isImportedSymbol())
+ {
+ ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads);
+ ret.type = e.type;
+ return;
+ }
+ }
+ if (e.e1.op == TOK.index)
+ {
+ // Convert &array[n] to &array+n
+ IndexExp ae = cast(IndexExp)e.e1;
+ if (ae.e2.op == TOK.int64 && ae.e1.op == TOK.variable)
+ {
+ sinteger_t index = ae.e2.toInteger();
+ VarExp ve = cast(VarExp)ae.e1;
+ if (ve.type.ty == Tsarray && !ve.var.isImportedSymbol())
+ {
+ TypeSArray ts = cast(TypeSArray)ve.type;
+ sinteger_t dim = ts.dim.toInteger();
+ if (index < 0 || index >= dim)
+ {
+ e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
+ return error();
+ }
+
+ import core.checkedint : mulu;
+ bool overflow;
+ const offset = mulu(index, ts.nextOf().size(e.loc), overflow);
+ if (overflow)
+ {
+ e.error("array offset overflow");
+ return error();
+ }
+
+ ret = new SymOffExp(e.loc, ve.var, offset);
+ ret.type = e.type;
+ return;
+ }
+ }
+ }
+ }
+
+ override void visit(PtrExp e)
+ {
+ //printf("PtrExp::optimize(result = x%x) %s\n", result, e.toChars());
+ if (expOptimize(e.e1, result))
+ return;
+ // Convert *&ex to ex
+ // But only if there is no type punning involved
+ if (e.e1.op == TOK.address)
+ {
+ Expression ex = (cast(AddrExp)e.e1).e1;
+ if (e.type.equals(ex.type))
+ ret = ex;
+ else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
+ {
+ ret = ex.copy();
+ ret.type = e.type;
+ }
+ }
+ if (keepLvalue)
+ return;
+ // Constant fold *(&structliteral + offset)
+ if (e.e1.op == TOK.add)
+ {
+ Expression ex = Ptr(e.type, e.e1).copy();
+ if (!CTFEExp.isCantExp(ex))
+ {
+ ret = ex;
+ return;
+ }
+ }
+ if (e.e1.op == TOK.symbolOffset)
+ {
+ SymOffExp se = cast(SymOffExp)e.e1;
+ VarDeclaration v = se.var.isVarDeclaration();
+ Expression ex = expandVar(result, v);
+ if (ex && ex.op == TOK.structLiteral)
+ {
+ StructLiteralExp sle = cast(StructLiteralExp)ex;
+ ex = sle.getField(e.type, cast(uint)se.offset);
+ if (ex && !CTFEExp.isCantExp(ex))
+ {
+ ret = ex;
+ return;
+ }
+ }
+ }
+ }
+
+ override void visit(DotVarExp e)
+ {
+ //printf("DotVarExp::optimize(result = x%x) %s\n", result, e.toChars());
+ if (expOptimize(e.e1, result))
+ return;
+ if (keepLvalue)
+ return;
+ Expression ex = e.e1;
+ if (ex.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)ex;
+ VarDeclaration v = ve.var.isVarDeclaration();
+ ex = expandVar(result, v);
+ }
+ if (ex && ex.op == TOK.structLiteral)
+ {
+ StructLiteralExp sle = cast(StructLiteralExp)ex;
+ VarDeclaration vf = e.var.isVarDeclaration();
+ if (vf && !vf.overlapped)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=13021
+ * Prevent optimization if vf has overlapped fields.
+ */
+ ex = sle.getField(e.type, vf.offset);
+ if (ex && !CTFEExp.isCantExp(ex))
+ {
+ ret = ex;
+ return;
+ }
+ }
+ }
+ }
+
+ override void visit(NewExp e)
+ {
+ expOptimize(e.thisexp, WANTvalue);
+ // Optimize parameters
+ if (e.newargs)
+ {
+ for (size_t i = 0; i < e.newargs.dim; i++)
+ {
+ expOptimize((*e.newargs)[i], WANTvalue);
+ }
+ }
+ if (e.arguments)
+ {
+ for (size_t i = 0; i < e.arguments.dim; i++)
+ {
+ expOptimize((*e.arguments)[i], WANTvalue);
+ }
+ }
+ }
+
+ override void visit(CallExp e)
+ {
+ //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars());
+ // Optimize parameters with keeping lvalue-ness
+ if (expOptimize(e.e1, result))
+ return;
+ if (e.arguments)
+ {
+ Type t1 = e.e1.type.toBasetype();
+ if (t1.ty == Tdelegate)
+ t1 = t1.nextOf();
+ // t1 can apparently be void for __ArrayDtor(T) calls
+ if (auto tf = t1.isTypeFunction())
+ {
+ for (size_t i = 0; i < e.arguments.dim; i++)
+ {
+ Parameter p = tf.parameterList[i];
+ bool keep = p && p.isReference();
+ expOptimize((*e.arguments)[i], WANTvalue, keep);
+ }
+ }
+ }
+ }
+
+ override void visit(CastExp e)
+ {
+ //printf("CastExp::optimize(result = %d) %s\n", result, e.toChars());
+ //printf("from %s to %s\n", e.type.toChars(), e.to.toChars());
+ //printf("from %s\n", e.type.toChars());
+ //printf("e1.type %s\n", e.e1.type.toChars());
+ //printf("type = %p\n", e.type);
+ assert(e.type);
+ TOK op1 = e.e1.op;
+ Expression e1old = e.e1;
+ if (expOptimize(e.e1, result, keepLvalue))
+ return;
+ if (!keepLvalue)
+ e.e1 = fromConstInitializer(result, e.e1);
+ if (e.e1 == e1old && e.e1.op == TOK.arrayLiteral && e.type.toBasetype().ty == Tpointer && e.e1.type.toBasetype().ty != Tsarray)
+ {
+ // Casting this will result in the same expression, and
+ // infinite loop because of Expression::implicitCastTo()
+ return; // no change
+ }
+ if ((e.e1.op == TOK.string_ || e.e1.op == TOK.arrayLiteral) &&
+ (e.type.ty == Tpointer || e.type.ty == Tarray))
+ {
+ const esz = e.type.nextOf().size(e.loc);
+ const e1sz = e.e1.type.toBasetype().nextOf().size(e.e1.loc);
+ if (esz == SIZE_INVALID || e1sz == SIZE_INVALID)
+ return error();
+
+ if (e1sz == esz)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=12937
+ // If target type is void array, trying to paint
+ // e.e1 with that type will cause infinite recursive optimization.
+ if (e.type.nextOf().ty == Tvoid)
+ return;
+ ret = e.e1.castTo(null, e.type);
+ //printf(" returning1 %s\n", ret.toChars());
+ return;
+ }
+ }
+
+ if (e.e1.op == TOK.structLiteral && e.e1.type.implicitConvTo(e.type) >= MATCH.constant)
+ {
+ //printf(" returning2 %s\n", e.e1.toChars());
+ L1:
+ // Returning e1 with changing its type
+ ret = (e1old == e.e1 ? e.e1.copy() : e.e1);
+ ret.type = e.type;
+ return;
+ }
+ /* The first test here is to prevent infinite loops
+ */
+ if (op1 != TOK.arrayLiteral && e.e1.op == TOK.arrayLiteral)
+ {
+ ret = e.e1.castTo(null, e.to);
+ return;
+ }
+ if (e.e1.op == TOK.null_ && (e.type.ty == Tpointer || e.type.ty == Tclass || e.type.ty == Tarray))
+ {
+ //printf(" returning3 %s\n", e.e1.toChars());
+ goto L1;
+ }
+ if (e.type.ty == Tclass && e.e1.type.ty == Tclass)
+ {
+ import dmd.astenums : Sizeok;
+
+ // See if we can remove an unnecessary cast
+ ClassDeclaration cdfrom = e.e1.type.isClassHandle();
+ ClassDeclaration cdto = e.type.isClassHandle();
+ if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration())
+ goto L1; // can always convert a class to Object
+ // Need to determine correct offset before optimizing away the cast.
+ // https://issues.dlang.org/show_bug.cgi?id=16980
+ cdfrom.size(e.loc);
+ assert(cdfrom.sizeok == Sizeok.done);
+ assert(cdto.sizeok == Sizeok.done || !cdto.isBaseOf(cdfrom, null));
+ int offset;
+ if (cdto.isBaseOf(cdfrom, &offset) && offset == 0)
+ {
+ //printf(" returning4 %s\n", e.e1.toChars());
+ goto L1;
+ }
+ }
+ if (e.e1.type.mutableOf().unSharedOf().equals(e.to.mutableOf().unSharedOf()))
+ {
+ //printf(" returning5 %s\n", e.e1.toChars());
+ goto L1;
+ }
+ if (e.e1.isConst())
+ {
+ if (e.e1.op == TOK.symbolOffset)
+ {
+ if (e.type.toBasetype().ty != Tsarray)
+ {
+ const esz = e.type.size(e.loc);
+ const e1sz = e.e1.type.size(e.e1.loc);
+ if (esz == SIZE_INVALID ||
+ e1sz == SIZE_INVALID)
+ return error();
+
+ if (esz == e1sz)
+ goto L1;
+ }
+ return;
+ }
+ if (e.to.toBasetype().ty != Tvoid)
+ {
+ if (e.e1.type.equals(e.type) && e.type.equals(e.to))
+ ret = e.e1;
+ else
+ ret = Cast(e.loc, e.type, e.to, e.e1).copy();
+ }
+ }
+ //printf(" returning6 %s\n", ret.toChars());
+ }
+
+ override void visit(BinAssignExp e)
+ {
+ //printf("BinAssignExp::optimize(result = %d) %s\n", result, e.toChars());
+ if (binOptimize(e, result, /*keepLhsLvalue*/ true))
+ return;
+ if (e.op == TOK.leftShiftAssign || e.op == TOK.rightShiftAssign || e.op == TOK.unsignedRightShiftAssign)
+ {
+ if (e.e2.isConst() == 1)
+ {
+ sinteger_t i2 = e.e2.toInteger();
+ d_uns64 sz = e.e1.type.size(e.e1.loc);
+ assert(sz != SIZE_INVALID);
+ sz *= 8;
+ if (i2 < 0 || i2 >= sz)
+ {
+ e.error("shift assign by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
+ return error();
+ }
+ }
+ }
+ }
+
+ override void visit(BinExp e)
+ {
+ //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars());
+ const keepLhsLvalue = e.op == TOK.construct || e.op == TOK.blit || e.op == TOK.assign
+ || e.op == TOK.plusPlus || e.op == TOK.minusMinus
+ || e.op == TOK.prePlusPlus || e.op == TOK.preMinusMinus;
+ binOptimize(e, result, keepLhsLvalue);
+ }
+
+ override void visit(AddExp e)
+ {
+ //printf("AddExp::optimize(%s)\n", e.toChars());
+ if (binOptimize(e, result))
+ return;
+ if (e.e1.isConst() && e.e2.isConst())
+ {
+ if (e.e1.op == TOK.symbolOffset && e.e2.op == TOK.symbolOffset)
+ return;
+ ret = Add(e.loc, e.type, e.e1, e.e2).copy();
+ }
+ }
+
+ override void visit(MinExp e)
+ {
+ if (binOptimize(e, result))
+ return;
+ if (e.e1.isConst() && e.e2.isConst())
+ {
+ if (e.e2.op == TOK.symbolOffset)
+ return;
+ ret = Min(e.loc, e.type, e.e1, e.e2).copy();
+ }
+ }
+
+ override void visit(MulExp e)
+ {
+ //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars());
+ if (binOptimize(e, result))
+ return;
+ if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
+ {
+ ret = Mul(e.loc, e.type, e.e1, e.e2).copy();
+ }
+ }
+
+ override void visit(DivExp e)
+ {
+ //printf("DivExp::optimize(%s)\n", e.toChars());
+ if (binOptimize(e, result))
+ return;
+ if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
+ {
+ ret = Div(e.loc, e.type, e.e1, e.e2).copy();
+ }
+ }
+
+ override void visit(ModExp e)
+ {
+ if (binOptimize(e, result))
+ return;
+ if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
+ {
+ ret = Mod(e.loc, e.type, e.e1, e.e2).copy();
+ }
+ }
+
+ extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift)
+ {
+ if (binOptimize(e, result))
+ return;
+ if (e.e2.isConst() == 1)
+ {
+ sinteger_t i2 = e.e2.toInteger();
+ d_uns64 sz = e.e1.type.size(e.e1.loc);
+ assert(sz != SIZE_INVALID);
+ sz *= 8;
+ if (i2 < 0 || i2 >= sz)
+ {
+ e.error("shift by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
+ return error();
+ }
+ if (e.e1.isConst() == 1)
+ ret = (*shift)(e.loc, e.type, e.e1, e.e2).copy();
+ }
+ }
+
+ override void visit(ShlExp e)
+ {
+ //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars());
+ shift_optimize(e, &Shl);
+ }
+
+ override void visit(ShrExp e)
+ {
+ //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars());
+ shift_optimize(e, &Shr);
+ }
+
+ override void visit(UshrExp e)
+ {
+ //printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
+ shift_optimize(e, &Ushr);
+ }
+
+ override void visit(AndExp e)
+ {
+ if (binOptimize(e, result))
+ return;
+ if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
+ ret = And(e.loc, e.type, e.e1, e.e2).copy();
+ }
+
+ override void visit(OrExp e)
+ {
+ if (binOptimize(e, result))
+ return;
+ if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
+ ret = Or(e.loc, e.type, e.e1, e.e2).copy();
+ }
+
+ override void visit(XorExp e)
+ {
+ if (binOptimize(e, result))
+ return;
+ if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
+ ret = Xor(e.loc, e.type, e.e1, e.e2).copy();
+ }
+
+ override void visit(PowExp e)
+ {
+ if (binOptimize(e, result))
+ return;
+ // All negative integral powers are illegal.
+ if (e.e1.type.isintegral() && (e.e2.op == TOK.int64) && cast(sinteger_t)e.e2.toInteger() < 0)
+ {
+ e.error("cannot raise `%s` to a negative integer power. Did you mean `(cast(real)%s)^^%s` ?", e.e1.type.toBasetype().toChars(), e.e1.toChars(), e.e2.toChars());
+ return error();
+ }
+ // If e2 *could* have been an integer, make it one.
+ if (e.e2.op == TOK.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal()))
+ {
+ // This only applies to floating point, or positive integral powers.
+ if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0)
+ e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64);
+ }
+ if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
+ {
+ Expression ex = Pow(e.loc, e.type, e.e1, e.e2).copy();
+ if (!CTFEExp.isCantExp(ex))
+ {
+ ret = ex;
+ return;
+ }
+ }
+ }
+
+ override void visit(CommaExp e)
+ {
+ //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars());
+ // Comma needs special treatment, because it may
+ // contain compiler-generated declarations. We can interpret them, but
+ // otherwise we must NOT attempt to constant-fold them.
+ // In particular, if the comma returns a temporary variable, it needs
+ // to be an lvalue (this is particularly important for struct constructors)
+ expOptimize(e.e1, WANTvalue);
+ expOptimize(e.e2, result, keepLvalue);
+ if (ret.op == TOK.error)
+ return;
+ if (!e.e1 || e.e1.op == TOK.int64 || e.e1.op == TOK.float64 || !hasSideEffect(e.e1))
+ {
+ ret = e.e2;
+ if (ret)
+ ret.type = e.type;
+ }
+ //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars());
+ }
+
+ override void visit(ArrayLengthExp e)
+ {
+ //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars());
+ if (unaOptimize(e, WANTexpand))
+ return;
+ // CTFE interpret static immutable arrays (to get better diagnostics)
+ if (e.e1.op == TOK.variable)
+ {
+ VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration();
+ if (v && (v.storage_class & STC.static_) && (v.storage_class & STC.immutable_) && v._init)
+ {
+ if (Expression ci = v.getConstInitializer())
+ e.e1 = ci;
+ }
+ }
+ if (e.e1.op == TOK.string_ || e.e1.op == TOK.arrayLiteral || e.e1.op == TOK.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray)
+ {
+ ret = ArrayLength(e.type, e.e1).copy();
+ }
+ }
+
+ override void visit(EqualExp e)
+ {
+ //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars());
+ if (binOptimize(e, WANTvalue))
+ return;
+ Expression e1 = fromConstInitializer(result, e.e1);
+ Expression e2 = fromConstInitializer(result, e.e2);
+ if (e1.op == TOK.error)
+ {
+ ret = e1;
+ return;
+ }
+ if (e2.op == TOK.error)
+ {
+ ret = e2;
+ return;
+ }
+ ret = Equal(e.op, e.loc, e.type, e1, e2).copy();
+ if (CTFEExp.isCantExp(ret))
+ ret = e;
+ }
+
+ override void visit(IdentityExp e)
+ {
+ //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars());
+ if (binOptimize(e, WANTvalue))
+ return;
+ if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == TOK.null_ && e.e2.op == TOK.null_))
+ {
+ ret = Identity(e.op, e.loc, e.type, e.e1, e.e2).copy();
+ if (CTFEExp.isCantExp(ret))
+ ret = e;
+ }
+ }
+
+ override void visit(IndexExp e)
+ {
+ //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars());
+ if (expOptimize(e.e1, result & WANTexpand))
+ return;
+ Expression ex = fromConstInitializer(result, e.e1);
+ // We might know $ now
+ setLengthVarIfKnown(e.lengthVar, ex);
+ if (expOptimize(e.e2, WANTvalue))
+ return;
+ // Don't optimize to an array literal element directly in case an lvalue is requested
+ if (keepLvalue && ex.op == TOK.arrayLiteral)
+ return;
+ ret = Index(e.type, ex, e.e2).copy();
+ if (CTFEExp.isCantExp(ret) || (!ret.isErrorExp() && keepLvalue && !ret.isLvalue()))
+ ret = e;
+ }
+
+ override void visit(SliceExp e)
+ {
+ //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars());
+ if (expOptimize(e.e1, result & WANTexpand))
+ return;
+ if (!e.lwr)
+ {
+ if (e.e1.op == TOK.string_)
+ {
+ // Convert slice of string literal into dynamic array
+ Type t = e.e1.type.toBasetype();
+ if (Type tn = t.nextOf())
+ ret = e.e1.castTo(null, tn.arrayOf());
+ }
+ }
+ else
+ {
+ e.e1 = fromConstInitializer(result, e.e1);
+ // We might know $ now
+ setLengthVarIfKnown(e.lengthVar, e.e1);
+ expOptimize(e.lwr, WANTvalue);
+ expOptimize(e.upr, WANTvalue);
+ if (ret.op == TOK.error)
+ return;
+ ret = Slice(e.type, e.e1, e.lwr, e.upr).copy();
+ if (CTFEExp.isCantExp(ret))
+ ret = e;
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=14649
+ // Leave the slice form so it might be
+ // a part of array operation.
+ // Assume that the backend codegen will handle the form `e[]`
+ // as an equal to `e` itself.
+ if (ret.op == TOK.string_)
+ {
+ e.e1 = ret;
+ e.lwr = null;
+ e.upr = null;
+ ret = e;
+ }
+ //printf("-SliceExp::optimize() %s\n", ret.toChars());
+ }
+
+ override void visit(LogicalExp e)
+ {
+ //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars());
+ if (expOptimize(e.e1, WANTvalue))
+ return;
+ const oror = e.op == TOK.orOr;
+ if (e.e1.isBool(oror))
+ {
+ // Replace with (e1, oror)
+ ret = IntegerExp.createBool(oror);
+ ret = Expression.combine(e.e1, ret);
+ if (e.type.toBasetype().ty == Tvoid)
+ {
+ ret = new CastExp(e.loc, ret, Type.tvoid);
+ ret.type = e.type;
+ }
+ ret = Expression_optimize(ret, result, false);
+ return;
+ }
+ expOptimize(e.e2, WANTvalue);
+ if (e.e1.isConst())
+ {
+ if (e.e2.isConst())
+ {
+ bool n1 = e.e1.isBool(true);
+ bool n2 = e.e2.isBool(true);
+ ret = new IntegerExp(e.loc, oror ? (n1 || n2) : (n1 && n2), e.type);
+ }
+ else if (e.e1.isBool(!oror))
+ {
+ if (e.type.toBasetype().ty == Tvoid)
+ ret = e.e2;
+ else
+ {
+ ret = new CastExp(e.loc, e.e2, e.type);
+ ret.type = e.type;
+ }
+ }
+ }
+ }
+
+ override void visit(CmpExp e)
+ {
+ //printf("CmpExp::optimize() %s\n", e.toChars());
+ if (binOptimize(e, WANTvalue))
+ return;
+ Expression e1 = fromConstInitializer(result, e.e1);
+ Expression e2 = fromConstInitializer(result, e.e2);
+ ret = Cmp(e.op, e.loc, e.type, e1, e2).copy();
+ if (CTFEExp.isCantExp(ret))
+ ret = e;
+ }
+
+ override void visit(CatExp e)
+ {
+ //printf("CatExp::optimize(%d) %s\n", result, e.toChars());
+ if (binOptimize(e, result))
+ return;
+ if (e.e1.op == TOK.concatenate)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=12798
+ // optimize ((expr ~ str1) ~ str2)
+ CatExp ce1 = cast(CatExp)e.e1;
+ scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2);
+ cex.type = e.type;
+ Expression ex = Expression_optimize(cex, result, false);
+ if (ex != cex)
+ {
+ e.e1 = ce1.e1;
+ e.e2 = ex;
+ }
+ }
+ // optimize "str"[] -> "str"
+ if (e.e1.op == TOK.slice)
+ {
+ SliceExp se1 = cast(SliceExp)e.e1;
+ if (se1.e1.op == TOK.string_ && !se1.lwr)
+ e.e1 = se1.e1;
+ }
+ if (e.e2.op == TOK.slice)
+ {
+ SliceExp se2 = cast(SliceExp)e.e2;
+ if (se2.e1.op == TOK.string_ && !se2.lwr)
+ e.e2 = se2.e1;
+ }
+ ret = Cat(e.loc, e.type, e.e1, e.e2).copy();
+ if (CTFEExp.isCantExp(ret))
+ ret = e;
+ }
+
+ override void visit(CondExp e)
+ {
+ if (expOptimize(e.econd, WANTvalue))
+ return;
+ if (e.econd.isBool(true))
+ ret = Expression_optimize(e.e1, result, keepLvalue);
+ else if (e.econd.isBool(false))
+ ret = Expression_optimize(e.e2, result, keepLvalue);
+ else
+ {
+ expOptimize(e.e1, result, keepLvalue);
+ expOptimize(e.e2, result, keepLvalue);
+ }
+ }
+ }
+
+ scope OptimizeVisitor v = new OptimizeVisitor(e, result, keepLvalue);
+
+ // Optimize the expression until it can no longer be simplified.
+ size_t b;
+ while (1)
+ {
+ if (b++ == global.recursionLimit)
+ {
+ e.error("infinite loop while optimizing expression");
+ fatal();
+ }
+ auto ex = v.ret;
+ ex.accept(v);
+ if (ex == v.ret)
+ break;
+ }
+ return v.ret;
+}
diff --git a/gcc/d/dmd/parse.c b/gcc/d/dmd/parse.c
deleted file mode 100644
index e1f13214d58..00000000000
--- a/gcc/d/dmd/parse.c
+++ /dev/null
@@ -1,8492 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/parse.c
- */
-
-// This is the D parser
-
-#include "root/dsystem.h" // strlen(),memcpy()
-#include "root/rmem.h"
-
-#include "mars.h"
-#include "lexer.h"
-#include "parse.h"
-#include "init.h"
-#include "attrib.h"
-#include "cond.h"
-#include "mtype.h"
-#include "template.h"
-#include "staticassert.h"
-#include "expression.h"
-#include "statement.h"
-#include "module.h"
-#include "dsymbol.h"
-#include "import.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "enum.h"
-#include "id.h"
-#include "version.h"
-#include "aliasthis.h"
-#include "nspace.h"
-#include "hdrgen.h"
-
-Expression *typeToExpression(Type *t);
-
-// Support C cast syntax:
-// (type)(expression)
-#define CCASTSYNTAX 1
-
-// Support postfix C array declarations, such as
-// int a[3][4];
-#define CARRAYDECL 1
-
-Parser::Parser(Module *module, const utf8_t *base, size_t length, bool doDocComment)
- : Lexer(module ? module->srcfile->toChars() : NULL, base, 0, length, doDocComment, false)
-{
- //printf("Parser::Parser()\n");
- mod = module;
- md = NULL;
- linkage = LINKd;
- endloc = Loc();
- inBrackets = 0;
- lookingForElse = Loc();
- //nextToken(); // start up the scanner
-}
-
-/*********************
- * Use this constructor for string mixins.
- * Input:
- * loc location in source file of mixin
- */
-Parser::Parser(Loc loc, Module *module, const utf8_t *base, size_t length, bool doDocComment)
- : Lexer(module ? module->srcfile->toChars() : NULL, base, 0, length, doDocComment, false)
-{
- //printf("Parser::Parser()\n");
- scanloc = loc;
-
- if (loc.filename)
- {
- /* Create a pseudo-filename for the mixin string, as it may not even exist
- * in the source file.
- */
- char *filename = (char *)mem.xmalloc(strlen(loc.filename) + 7 + sizeof(loc.linnum) * 3 + 1);
- sprintf(filename, "%s-mixin-%d", loc.filename, (int)loc.linnum);
- scanloc.filename = filename;
- }
-
- mod = module;
- md = NULL;
- linkage = LINKd;
- endloc = Loc();
- inBrackets = 0;
- lookingForElse = Loc();
- //nextToken(); // start up the scanner
-}
-
-Dsymbols *Parser::parseModule()
-{
- const utf8_t *comment = token.blockComment;
- bool isdeprecated = false;
- Expression *msg = NULL;
- Expressions *udas = NULL;
- Dsymbols *decldefs;
-
- Token *tk;
- if (skipAttributes(&token, &tk) && tk->value == TOKmodule)
- {
- while (token.value != TOKmodule)
- {
- switch (token.value)
- {
- case TOKdeprecated:
- {
- // deprecated (...) module ...
- if (isdeprecated)
- {
- error("there is only one deprecation attribute allowed for module declaration");
- }
- else
- {
- isdeprecated = true;
- }
- nextToken();
- if (token.value == TOKlparen)
- {
- check(TOKlparen);
- msg = parseAssignExp();
- check(TOKrparen);
- }
- break;
- }
- case TOKat:
- {
- Expressions *exps = NULL;
- StorageClass stc = parseAttribute(&exps);
-
- if (stc == STCproperty || stc == STCnogc || stc == STCdisable ||
- stc == STCsafe || stc == STCtrusted || stc == STCsystem)
- {
- error("@%s attribute for module declaration is not supported", token.toChars());
- }
- else
- {
- udas = UserAttributeDeclaration::concat(udas, exps);
- }
- if (stc)
- nextToken();
- break;
- }
- default:
- {
- error("`module` expected instead of %s", token.toChars());
- nextToken();
- break;
- }
- }
- }
- }
-
- if (udas)
- {
- Dsymbols *a = new Dsymbols();
- UserAttributeDeclaration *udad = new UserAttributeDeclaration(udas, a);
- mod->userAttribDecl = udad;
- }
-
- // ModuleDeclation leads off
- if (token.value == TOKmodule)
- {
- Loc loc = token.loc;
-
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following module");
- goto Lerr;
- }
- else
- {
- Identifiers *a = NULL;
- Identifier *id;
-
- id = token.ident;
- while (nextToken() == TOKdot)
- {
- if (!a)
- a = new Identifiers();
- a->push(id);
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following package");
- goto Lerr;
- }
- id = token.ident;
- }
-
- md = new ModuleDeclaration(loc, a, id);
- md->isdeprecated = isdeprecated;
- md->msg = msg;
-
- if (token.value != TOKsemicolon)
- error("`;` expected following module declaration instead of %s", token.toChars());
- nextToken();
- addComment(mod, comment);
- }
- }
-
- decldefs = parseDeclDefs(0);
- if (token.value != TOKeof)
- {
- error(token.loc, "unrecognized declaration");
- goto Lerr;
- }
- return decldefs;
-
-Lerr:
- while (token.value != TOKsemicolon && token.value != TOKeof)
- nextToken();
- nextToken();
- return new Dsymbols();
-}
-
-static StorageClass parseDeprecatedAttribute(Parser *p, Expression **msg)
-{
- if (p->peekNext() != TOKlparen)
- return STCdeprecated;
-
- p->nextToken();
- p->check(TOKlparen);
- Expression *e = p->parseAssignExp();
- p->check(TOKrparen);
- if (*msg)
- {
- p->error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`",
- (*msg)->toChars(), e->toChars());
- }
- *msg = e;
- return STCundefined;
-}
-
-struct PrefixAttributes
-{
- StorageClass storageClass;
- Expression *depmsg;
- LINK link;
- Prot protection;
- bool setAlignment;
- Expression *ealign;
- Expressions *udas;
- const utf8_t *comment;
-
- PrefixAttributes()
- : storageClass(STCundefined),
- depmsg(NULL),
- link(LINKdefault),
- protection(Prot::undefined),
- setAlignment(false),
- ealign(NULL),
- udas(NULL),
- comment(NULL)
- {
- }
-};
-
-Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl, PrefixAttributes *pAttrs)
-{
- Dsymbol *lastDecl = NULL; // used to link unittest to its previous declaration
- if (!pLastDecl)
- pLastDecl = &lastDecl;
-
- LINK linksave = linkage; // save global state
-
- //printf("Parser::parseDeclDefs()\n");
- Dsymbols *decldefs = new Dsymbols();
- do
- {
- // parse result
- Dsymbol *s = NULL;
- Dsymbols *a = NULL;
-
- PrefixAttributes attrs;
- if (!once || !pAttrs)
- {
- pAttrs = &attrs;
- pAttrs->comment = token.blockComment;
- }
- Prot::Kind prot;
- StorageClass stc;
- Condition *condition;
-
- linkage = linksave;
-
- switch (token.value)
- {
- case TOKenum:
- {
- /* Determine if this is a manifest constant declaration,
- * or a conventional enum.
- */
- Token *t = peek(&token);
- if (t->value == TOKlcurly || t->value == TOKcolon)
- s = parseEnum();
- else if (t->value != TOKidentifier)
- goto Ldeclaration;
- else
- {
- t = peek(t);
- if (t->value == TOKlcurly || t->value == TOKcolon ||
- t->value == TOKsemicolon)
- s = parseEnum();
- else
- goto Ldeclaration;
- }
- break;
- }
-
- case TOKimport:
- a = parseImport();
- // keep pLastDecl
- break;
-
- case TOKtemplate:
- s = (Dsymbol *)parseTemplateDeclaration();
- break;
-
- case TOKmixin:
- {
- Loc loc = token.loc;
- switch (peekNext())
- {
- case TOKlparen:
- {
- // mixin(string)
- nextToken();
- Expressions *exps = parseArguments();
- check(TOKsemicolon);
- s = new CompileDeclaration(loc, exps);
- break;
- }
- case TOKtemplate:
- // mixin template
- nextToken();
- s = (Dsymbol *)parseTemplateDeclaration(true);
- break;
-
- default:
- s = parseMixin();
- break;
- }
- break;
- }
-
- case TOKwchar: case TOKdchar:
- case TOKbool: case TOKchar:
- case TOKint8: case TOKuns8:
- case TOKint16: case TOKuns16:
- case TOKint32: case TOKuns32:
- case TOKint64: case TOKuns64:
- case TOKint128: case TOKuns128:
- case TOKfloat32: case TOKfloat64: case TOKfloat80:
- case TOKimaginary32: case TOKimaginary64: case TOKimaginary80:
- case TOKcomplex32: case TOKcomplex64: case TOKcomplex80:
- case TOKvoid:
- case TOKalias:
- case TOKidentifier:
- case TOKsuper:
- case TOKtypeof:
- case TOKdot:
- case TOKvector:
- case TOKstruct:
- case TOKunion:
- case TOKclass:
- case TOKinterface:
- case TOKtraits:
- Ldeclaration:
- a = parseDeclarations(false, pAttrs, pAttrs->comment);
- if (a && a->length)
- *pLastDecl = (*a)[a->length-1];
- break;
-
- case TOKthis:
- if (peekNext() == TOKdot)
- goto Ldeclaration;
- else
- s = parseCtor(pAttrs);
- break;
-
- case TOKtilde:
- s = parseDtor(pAttrs);
- break;
-
- case TOKinvariant:
- {
- Token *t = peek(&token);
- if (t->value == TOKlparen || t->value == TOKlcurly)
- {
- // invariant { statements... }
- // invariant() { statements... }
- // invariant (expression);
- s = parseInvariant(pAttrs);
- }
- else
- {
- error("invariant body expected, not `%s`", token.toChars());
- goto Lerror;
- }
- break;
- }
-
- case TOKunittest:
- if (global.params.useUnitTests || global.params.doDocComments || global.params.doHdrGeneration)
- {
- s = parseUnitTest(pAttrs);
- if (*pLastDecl)
- (*pLastDecl)->ddocUnittest = (UnitTestDeclaration *)s;
- }
- else
- {
- // Skip over unittest block by counting { }
- Loc loc = token.loc;
- int braces = 0;
- while (1)
- {
- nextToken();
- switch (token.value)
- {
- case TOKlcurly:
- ++braces;
- continue;
-
- case TOKrcurly:
- if (--braces)
- continue;
- nextToken();
- break;
-
- case TOKeof:
- /* { */
- error(loc, "closing } of unittest not found before end of file");
- goto Lerror;
-
- default:
- continue;
- }
- break;
- }
- // Workaround 14894. Add an empty unittest declaration to keep
- // the number of symbols in this scope independent of -unittest.
- s = new UnitTestDeclaration(loc, token.loc, STCundefined, NULL);
- }
- break;
-
- case TOKnew:
- s = parseNew(pAttrs);
- break;
-
- case TOKdelete:
- s = parseDelete(pAttrs);
- break;
-
- case TOKcolon:
- case TOKlcurly:
- error("declaration expected, not `%s`",token.toChars());
- goto Lerror;
-
- case TOKrcurly:
- case TOKeof:
- if (once)
- error("declaration expected, not `%s`", token.toChars());
- return decldefs;
-
- case TOKstatic:
- {
- TOK next = peekNext();
- if (next == TOKthis)
- s = parseStaticCtor(pAttrs);
- else if (next == TOKtilde)
- s = parseStaticDtor(pAttrs);
- else if (next == TOKassert)
- s = parseStaticAssert();
- else if (next == TOKif)
- {
- condition = parseStaticIfCondition();
- Dsymbols *athen;
- if (token.value == TOKcolon)
- athen = parseBlock(pLastDecl);
- else
- {
- Loc lookingForElseSave = lookingForElse;
- lookingForElse = token.loc;
- athen = parseBlock(pLastDecl);
- lookingForElse = lookingForElseSave;
- }
- Dsymbols *aelse = NULL;
- if (token.value == TOKelse)
- {
- Loc elseloc = token.loc;
- nextToken();
- aelse = parseBlock(pLastDecl);
- checkDanglingElse(elseloc);
- }
- s = new StaticIfDeclaration(condition, athen, aelse);
- }
- else if (next == TOKimport)
- {
- a = parseImport();
- // keep pLastDecl
- }
- else if (next == TOKforeach || next == TOKforeach_reverse)
- {
- s = parseForeachStaticDecl(token.loc, pLastDecl);
- }
- else
- {
- stc = STCstatic;
- goto Lstc;
- }
- break;
- }
-
- case TOKconst:
- if (peekNext() == TOKlparen)
- goto Ldeclaration;
- stc = STCconst;
- goto Lstc;
-
- case TOKimmutable:
- if (peekNext() == TOKlparen)
- goto Ldeclaration;
- stc = STCimmutable;
- goto Lstc;
-
- case TOKshared:
- {
- TOK next = peekNext();
- if (next == TOKlparen)
- goto Ldeclaration;
- if (next == TOKstatic)
- {
- TOK next2 = peekNext2();
- if (next2 == TOKthis)
- {
- s = parseSharedStaticCtor(pAttrs);
- break;
- }
- if (next2 == TOKtilde)
- {
- s = parseSharedStaticDtor(pAttrs);
- break;
- }
- }
- stc = STCshared;
- goto Lstc;
- }
-
- case TOKwild:
- if (peekNext() == TOKlparen)
- goto Ldeclaration;
- stc = STCwild;
- goto Lstc;
-
- case TOKfinal: stc = STCfinal; goto Lstc;
- case TOKauto: stc = STCauto; goto Lstc;
- case TOKscope: stc = STCscope; goto Lstc;
- case TOKoverride: stc = STCoverride; goto Lstc;
- case TOKabstract: stc = STCabstract; goto Lstc;
- case TOKsynchronized: stc = STCsynchronized; goto Lstc;
- case TOKnothrow: stc = STCnothrow; goto Lstc;
- case TOKpure: stc = STCpure; goto Lstc;
- case TOKref: stc = STCref; goto Lstc;
- case TOKgshared: stc = STCgshared; goto Lstc;
- //case TOKmanifest: stc = STCmanifest; goto Lstc;
- case TOKat:
- {
- Expressions *exps = NULL;
- stc = parseAttribute(&exps);
- if (stc)
- goto Lstc; // it's a predefined attribute
- // no redundant/conflicting check for UDAs
- pAttrs->udas = UserAttributeDeclaration::concat(pAttrs->udas, exps);
- goto Lautodecl;
- }
- Lstc:
- pAttrs->storageClass = appendStorageClass(pAttrs->storageClass, stc);
- nextToken();
-
- Lautodecl:
- Token *tk;
-
- /* Look for auto initializers:
- * storage_class identifier = initializer;
- * storage_class identifier(...) = initializer;
- */
- if (token.value == TOKidentifier &&
- skipParensIf(peek(&token), &tk) &&
- tk->value == TOKassign)
- {
- a = parseAutoDeclarations(pAttrs->storageClass, pAttrs->comment);
- pAttrs->storageClass = STCundefined;
- if (a && a->length)
- *pLastDecl = (*a)[a->length-1];
- if (pAttrs->udas)
- {
- s = new UserAttributeDeclaration(pAttrs->udas, a);
- pAttrs->udas = NULL;
- }
- break;
- }
-
- /* Look for return type inference for template functions.
- */
- if (token.value == TOKidentifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) &&
- (tk->value == TOKlparen || tk->value == TOKlcurly || tk->value == TOKin ||
- tk->value == TOKout || tk->value == TOKdo ||
- (tk->value == TOKidentifier && tk->ident == Id::_body))
- )
- {
- a = parseDeclarations(true, pAttrs, pAttrs->comment);
- if (a && a->length)
- *pLastDecl = (*a)[a->length-1];
- if (pAttrs->udas)
- {
- s = new UserAttributeDeclaration(pAttrs->udas, a);
- pAttrs->udas = NULL;
- }
- break;
- }
-
- a = parseBlock(pLastDecl, pAttrs);
- if (pAttrs->storageClass != STCundefined)
- {
- s = new StorageClassDeclaration(pAttrs->storageClass, a);
- pAttrs->storageClass = STCundefined;
- }
- if (pAttrs->udas)
- {
- if (s)
- {
- a = new Dsymbols();
- a->push(s);
- }
- s = new UserAttributeDeclaration(pAttrs->udas, a);
- pAttrs->udas = NULL;
- }
- break;
-
- case TOKdeprecated:
- {
- if (StorageClass _stc = parseDeprecatedAttribute(this, &pAttrs->depmsg))
- {
- stc = _stc;
- goto Lstc;
- }
- a = parseBlock(pLastDecl, pAttrs);
- if (pAttrs->depmsg)
- {
- s = new DeprecatedDeclaration(pAttrs->depmsg, a);
- pAttrs->depmsg = NULL;
- }
- break;
- }
-
- case TOKlbracket:
- {
- if (peekNext() == TOKrbracket)
- error("empty attribute list is not allowed");
- error("use @(attributes) instead of [attributes]");
- Expressions *exps = parseArguments();
- // no redundant/conflicting check for UDAs
-
- pAttrs->udas = UserAttributeDeclaration::concat(pAttrs->udas, exps);
- a = parseBlock(pLastDecl, pAttrs);
- if (pAttrs->udas)
- {
- s = new UserAttributeDeclaration(pAttrs->udas, a);
- pAttrs->udas = NULL;
- }
- break;
- }
-
- case TOKextern:
- {
- if (peek(&token)->value != TOKlparen)
- {
- stc = STCextern;
- goto Lstc;
- }
-
- Loc linkLoc = token.loc;
- Identifiers *idents = NULL;
- CPPMANGLE cppmangle = CPPMANGLEdefault;
- bool cppMangleOnly = false;
- LINK link = parseLinkage(&idents, &cppmangle, &cppMangleOnly);
- if (pAttrs->link != LINKdefault)
- {
- if (pAttrs->link != link)
- {
- error("conflicting linkage extern (%s) and extern (%s)",
- linkageToChars(pAttrs->link), linkageToChars(link));
- }
- else if (idents)
- {
- // Allow:
- // extern(C++, foo) extern(C++, bar) void foo();
- // to be equivalent with:
- // extern(C++, foo.bar) void foo();
- }
- else
- error("redundant linkage extern (%s)", linkageToChars(pAttrs->link));
- }
- pAttrs->link = link;
- this->linkage = link;
- a = parseBlock(pLastDecl, pAttrs);
- if (idents)
- {
- assert(link == LINKcpp);
- assert(idents->length);
- for (size_t i = idents->length; i;)
- {
- Identifier *id = (*idents)[--i];
- if (s)
- {
- a = new Dsymbols();
- a->push(s);
- }
- s = new Nspace(linkLoc, id, a, cppMangleOnly);
- }
- delete idents;
- pAttrs->link = LINKdefault;
- }
- else if (pAttrs->link != LINKdefault)
- {
- s = new LinkDeclaration(pAttrs->link, a);
- pAttrs->link = LINKdefault;
- }
- else if (cppmangle != CPPMANGLEdefault)
- {
- assert(link == LINKcpp);
- s = new CPPMangleDeclaration(cppmangle, a);
- }
- break;
- }
-
- case TOKprivate: prot = Prot::private_; goto Lprot;
- case TOKpackage: prot = Prot::package_; goto Lprot;
- case TOKprotected: prot = Prot::protected_; goto Lprot;
- case TOKpublic: prot = Prot::public_; goto Lprot;
- case TOKexport: prot = Prot::export_; goto Lprot;
- Lprot:
- {
- if (pAttrs->protection.kind != Prot::undefined)
- {
- if (pAttrs->protection.kind != prot)
- error("conflicting protection attribute `%s` and `%s`",
- protectionToChars(pAttrs->protection.kind), protectionToChars(prot));
- else
- error("redundant protection attribute `%s`", protectionToChars(prot));
- }
- pAttrs->protection.kind = prot;
-
- nextToken();
-
- // optional qualified package identifier to bind
- // protection to
- Identifiers *pkg_prot_idents = NULL;
- if (pAttrs->protection.kind == Prot::package_ && token.value == TOKlparen)
- {
- pkg_prot_idents = parseQualifiedIdentifier("protection package");
-
- if (pkg_prot_idents)
- check(TOKrparen);
- else
- {
- while (token.value != TOKsemicolon && token.value != TOKeof)
- nextToken();
- nextToken();
- break;
- }
- }
-
- Loc attrloc = token.loc;
- a = parseBlock(pLastDecl, pAttrs);
- if (pAttrs->protection.kind != Prot::undefined)
- {
- if (pAttrs->protection.kind == Prot::package_ && pkg_prot_idents)
- s = new ProtDeclaration(attrloc, pkg_prot_idents, a);
- else
- s = new ProtDeclaration(attrloc, pAttrs->protection, a);
-
- pAttrs->protection = Prot(Prot::undefined);
- }
- break;
- }
-
- case TOKalign:
- {
- const Loc attrLoc = token.loc;
-
- nextToken();
-
- Expression *e = NULL; // default
- if (token.value == TOKlparen)
- {
- nextToken();
- e = parseAssignExp();
- check(TOKrparen);
- }
-
- if (pAttrs->setAlignment)
- {
- const char *s1 = "";
- OutBuffer buf1;
- if (e)
- {
- buf1.printf("(%s)", e->toChars());
- s1 = buf1.peekChars();
- }
- error("redundant alignment attribute align%s", s1);
- }
-
- pAttrs->setAlignment = true;
- pAttrs->ealign = e;
- a = parseBlock(pLastDecl, pAttrs);
- if (pAttrs->setAlignment)
- {
- s = new AlignDeclaration(attrLoc, pAttrs->ealign, a);
- pAttrs->setAlignment = false;
- pAttrs->ealign = NULL;
- }
- break;
- }
-
- case TOKpragma:
- {
- Expressions *args = NULL;
- Loc loc = token.loc;
-
- nextToken();
- check(TOKlparen);
- if (token.value != TOKidentifier)
- {
- error("pragma(identifier) expected");
- goto Lerror;
- }
- Identifier *ident = token.ident;
- nextToken();
- if (token.value == TOKcomma && peekNext() != TOKrparen)
- args = parseArguments(); // pragma(identifier, args...)
- else
- check(TOKrparen); // pragma(identifier)
-
- Dsymbols *a2 = NULL;
- if (token.value == TOKsemicolon)
- {
- /* Bugzilla 2354: Accept single semicolon as an empty
- * DeclarationBlock following attribute.
- *
- * Attribute DeclarationBlock
- * Pragma DeclDef
- * ;
- */
- nextToken();
- }
- else
- a2 = parseBlock(pLastDecl);
- s = new PragmaDeclaration(loc, ident, args, a2);
- break;
- }
-
- case TOKdebug:
- nextToken();
- if (token.value == TOKassign)
- {
- nextToken();
- if (token.value == TOKidentifier)
- s = new DebugSymbol(token.loc, token.ident);
- else if (token.value == TOKint32v || token.value == TOKint64v)
- s = new DebugSymbol(token.loc, (unsigned)token.uns64value);
- else
- {
- error("identifier or integer expected, not %s", token.toChars());
- s = NULL;
- }
- nextToken();
- if (token.value != TOKsemicolon)
- error("semicolon expected");
- nextToken();
- break;
- }
-
- condition = parseDebugCondition();
- goto Lcondition;
-
- case TOKversion:
- nextToken();
- if (token.value == TOKassign)
- {
- nextToken();
- if (token.value == TOKidentifier)
- s = new VersionSymbol(token.loc, token.ident);
- else if (token.value == TOKint32v || token.value == TOKint64v)
- s = new VersionSymbol(token.loc, (unsigned)token.uns64value);
- else
- {
- error("identifier or integer expected, not %s", token.toChars());
- s = NULL;
- }
- nextToken();
- if (token.value != TOKsemicolon)
- error("semicolon expected");
- nextToken();
- break;
- }
- condition = parseVersionCondition();
- goto Lcondition;
-
- Lcondition:
- {
- Dsymbols *athen;
- if (token.value == TOKcolon)
- athen = parseBlock(pLastDecl);
- else
- {
- Loc lookingForElseSave = lookingForElse;
- lookingForElse = token.loc;
- athen = parseBlock(pLastDecl);
- lookingForElse = lookingForElseSave;
- }
- Dsymbols *aelse = NULL;
- if (token.value == TOKelse)
- {
- Loc elseloc = token.loc;
- nextToken();
- aelse = parseBlock(pLastDecl);
- checkDanglingElse(elseloc);
- }
- s = new ConditionalDeclaration(condition, athen, aelse);
- break;
- }
-
- case TOKsemicolon: // empty declaration
- //error("empty declaration");
- nextToken();
- continue;
-
- default:
- error("declaration expected, not `%s`",token.toChars());
- Lerror:
- while (token.value != TOKsemicolon && token.value != TOKeof)
- nextToken();
- nextToken();
- s = NULL;
- continue;
- }
-
- if (s)
- {
- if (!s->isAttribDeclaration())
- *pLastDecl = s;
- decldefs->push(s);
- addComment(s, pAttrs->comment);
- }
- else if (a && a->length)
- {
- decldefs->append(a);
- }
- } while (!once);
-
- linkage = linksave;
-
- return decldefs;
-}
-
-/*********************************************
- * Give error on redundant/conflicting storage class.
- *
- * TODO: remove deprecation in 2.068 and keep only error
- */
-
-StorageClass Parser::appendStorageClass(StorageClass storageClass, StorageClass stc,
- bool deprec)
-{
- if ((storageClass & stc) ||
- (storageClass & STCin && stc & (STCconst | STCscope)) ||
- (stc & STCin && storageClass & (STCconst | STCscope)))
- {
- OutBuffer buf;
- stcToBuffer(&buf, stc);
- if (deprec)
- deprecation("redundant attribute `%s`", buf.peekChars());
- else
- error("redundant attribute `%s`", buf.peekChars());
- return storageClass | stc;
- }
-
- storageClass |= stc;
-
- if (stc & (STCconst | STCimmutable | STCmanifest))
- {
- StorageClass u = storageClass & (STCconst | STCimmutable | STCmanifest);
- if (u & (u - 1))
- error("conflicting attribute `%s`", Token::toChars(token.value));
- }
- if (stc & (STCgshared | STCshared | STCtls))
- {
- StorageClass u = storageClass & (STCgshared | STCshared | STCtls);
- if (u & (u - 1))
- error("conflicting attribute `%s`", Token::toChars(token.value));
- }
- if (stc & (STCsafe | STCsystem | STCtrusted))
- {
- StorageClass u = storageClass & (STCsafe | STCsystem | STCtrusted);
- if (u & (u - 1))
- error("conflicting attribute `@%s`", token.toChars());
- }
-
- return storageClass;
-}
-
-/***********************************************
- * Parse attribute, lexer is on '@'.
- * Input:
- * pudas array of UDAs to append to
- * Returns:
- * storage class if a predefined attribute; also scanner remains on identifier.
- * 0 if not a predefined attribute
- * *pudas set if user defined attribute, scanner is past UDA
- * *pudas NULL if not a user defined attribute
- */
-
-StorageClass Parser::parseAttribute(Expressions **pudas)
-{
- nextToken();
- Expressions *udas = NULL;
- StorageClass stc = 0;
- if (token.value == TOKidentifier)
- {
- if (token.ident == Id::property)
- stc = STCproperty;
- else if (token.ident == Id::nogc)
- stc = STCnogc;
- else if (token.ident == Id::safe)
- stc = STCsafe;
- else if (token.ident == Id::trusted)
- stc = STCtrusted;
- else if (token.ident == Id::system)
- stc = STCsystem;
- else if (token.ident == Id::disable)
- stc = STCdisable;
- else if (token.ident == Id::future)
- stc = STCfuture;
- else
- {
- // Allow identifier, template instantiation, or function call
- Expression *exp = parsePrimaryExp();
- if (token.value == TOKlparen)
- {
- Loc loc = token.loc;
- exp = new CallExp(loc, exp, parseArguments());
- }
-
- udas = new Expressions();
- udas->push(exp);
- }
- }
- else if (token.value == TOKlparen)
- {
- // @( ArgumentList )
- // Concatenate with existing
- if (peekNext() == TOKrparen)
- error("empty attribute list is not allowed");
- udas = parseArguments();
- }
- else
- {
- error("@identifier or @(ArgumentList) expected, not @%s", token.toChars());
- }
-
- if (stc)
- {
- }
- else if (udas)
- {
- *pudas = UserAttributeDeclaration::concat(*pudas, udas);
- }
- else
- error("valid attributes are @property, @safe, @trusted, @system, @disable");
- return stc;
-}
-
-/***********************************************
- * Parse const/immutable/shared/inout/nothrow/pure postfix
- */
-
-StorageClass Parser::parsePostfix(StorageClass storageClass, Expressions **pudas)
-{
- while (1)
- {
- StorageClass stc;
- switch (token.value)
- {
- case TOKconst: stc = STCconst; break;
- case TOKimmutable: stc = STCimmutable; break;
- case TOKshared: stc = STCshared; break;
- case TOKwild: stc = STCwild; break;
- case TOKnothrow: stc = STCnothrow; break;
- case TOKpure: stc = STCpure; break;
- case TOKreturn: stc = STCreturn; break;
- case TOKscope: stc = STCscope; break;
- case TOKat:
- {
- Expressions *udas = NULL;
- stc = parseAttribute(&udas);
- if (udas)
- {
- if (pudas)
- *pudas = UserAttributeDeclaration::concat(*pudas, udas);
- else
- {
- // Disallow:
- // void function() @uda fp;
- // () @uda { return 1; }
- error("user-defined attributes cannot appear as postfixes");
- }
- continue;
- }
- break;
- }
-
- default:
- return storageClass;
- }
- storageClass = appendStorageClass(storageClass, stc, true);
- nextToken();
- }
-}
-
-StorageClass Parser::parseTypeCtor()
-{
- StorageClass storageClass = STCundefined;
-
- while (1)
- {
- if (peek(&token)->value == TOKlparen)
- return storageClass;
-
- StorageClass stc;
- switch (token.value)
- {
- case TOKconst: stc = STCconst; break;
- case TOKimmutable: stc = STCimmutable; break;
- case TOKshared: stc = STCshared; break;
- case TOKwild: stc = STCwild; break;
-
- default:
- return storageClass;
- }
- storageClass = appendStorageClass(storageClass, stc);
- nextToken();
- }
-}
-
-/********************************************
- * Parse declarations after an align, protection, or extern decl.
- */
-
-Dsymbols *Parser::parseBlock(Dsymbol **pLastDecl, PrefixAttributes *pAttrs)
-{
- Dsymbols *a = NULL;
-
- //printf("parseBlock()\n");
- switch (token.value)
- {
- case TOKsemicolon:
- error("declaration expected following attribute, not `;`");
- nextToken();
- break;
-
- case TOKeof:
- error("declaration expected following attribute, not EOF");
- break;
-
- case TOKlcurly:
- {
- Loc lookingForElseSave = lookingForElse;
- lookingForElse = Loc();
-
- nextToken();
- a = parseDeclDefs(0, pLastDecl);
- if (token.value != TOKrcurly)
- {
- /* { */
- error("matching `}` expected, not %s", token.toChars());
- }
- else
- nextToken();
- lookingForElse = lookingForElseSave;
- break;
- }
-
- case TOKcolon:
- nextToken();
- a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket
- break;
-
- default:
- a = parseDeclDefs(1, pLastDecl, pAttrs);
- break;
- }
- return a;
-}
-
-/**********************************
- * Parse a static assertion.
- * Current token is 'static'.
- */
-
-StaticAssert *Parser::parseStaticAssert()
-{
- Loc loc = token.loc;
- Expression *exp;
- Expression *msg = NULL;
-
- //printf("parseStaticAssert()\n");
- nextToken();
- nextToken();
- check(TOKlparen);
- exp = parseAssignExp();
- if (token.value == TOKcomma)
- {
- nextToken();
- if (token.value != TOKrparen)
- {
- msg = parseAssignExp();
- if (token.value == TOKcomma)
- nextToken();
- }
- }
- check(TOKrparen);
- check(TOKsemicolon);
- return new StaticAssert(loc, exp, msg);
-}
-
-/***********************************
- * Parse typeof(expression).
- * Current token is on the 'typeof'.
- */
-
-TypeQualified *Parser::parseTypeof()
-{
- TypeQualified *t;
- Loc loc = token.loc;
-
- nextToken();
- check(TOKlparen);
- if (token.value == TOKreturn) // typeof(return)
- {
- nextToken();
- t = new TypeReturn(loc);
- }
- else
- {
- Expression *exp = parseExpression(); // typeof(expression)
- t = new TypeTypeof(loc, exp);
- }
- check(TOKrparen);
- return t;
-}
-
-/***********************************
- * Parse __vector(type).
- * Current token is on the '__vector'.
- */
-
-Type *Parser::parseVector()
-{
- nextToken();
- check(TOKlparen);
- Type *tb = parseType();
- check(TOKrparen);
- return new TypeVector(tb);
-}
-
-/***********************************
- * Parse:
- * extern (linkage)
- * extern (C++, namespaces)
- * extern (C++, "namespace", "namespaces", ...)
- * The parser is on the 'extern' token.
- */
-
-LINK Parser::parseLinkage(Identifiers **pidents, CPPMANGLE *pcppmangle, bool *pcppMangleOnly)
-{
- Identifiers *idents = NULL;
- CPPMANGLE cppmangle = CPPMANGLEdefault;
- bool cppMangleOnly = false;
- LINK link = LINKdefault;
- nextToken();
- assert(token.value == TOKlparen);
- nextToken();
- if (token.value == TOKidentifier)
- { Identifier *id = token.ident;
-
- nextToken();
- if (id == Id::Windows)
- link = LINKwindows;
- else if (id == Id::D)
- link = LINKd;
- else if (id == Id::C)
- {
- link = LINKc;
- if (token.value == TOKplusplus)
- {
- link = LINKcpp;
- nextToken();
- if (token.value == TOKcomma) // , namespaces or class or struct
- {
- nextToken();
- if (token.value == TOKclass || token.value == TOKstruct)
- {
- cppmangle = token.value == TOKclass ? CPPMANGLEclass : CPPMANGLEstruct;
- nextToken();
- }
- else if (token.value == TOKstring) // extern(C++, "namespace", "namespaces")
- {
- cppMangleOnly = true;
- idents = new Identifiers();
-
- while (1)
- {
- StringExp *stringExp = (StringExp *)parsePrimaryExp();
- const char *name = stringExp->toPtr();
- if (stringExp->len == 0)
- {
- error("invalid zero length C++ namespace");
- idents = NULL;
- break;
- }
- else if (!Identifier::isValidIdentifier(name))
- {
- error("expected valid identifier for C++ namespace but got `%s`", name);
- idents = NULL;
- break;
- }
- idents->push(Identifier::idPool(name));
- if (token.value == TOKcomma)
- {
- nextToken();
- if (token.value != TOKstring)
- {
- error("string expected following `,` for C++ namespace, not `%s`", token.toChars());
- idents = NULL;
- break;
- }
- }
- else
- break;
- }
- }
- else
- {
- idents = new Identifiers();
- while (1)
- {
- if (token.value == TOKidentifier)
- {
- Identifier *idn = token.ident;
- idents->push(idn);
- nextToken();
- if (token.value == TOKdot)
- {
- nextToken();
- continue;
- }
- }
- else
- {
- error("identifier expected for C++ namespace");
- idents = NULL; // error occurred, invalidate list of elements.
- }
- break;
- }
- }
- }
- }
- }
- else if (id == Id::Objective) // Looking for tokens "Objective-C"
- {
- if (token.value == TOKmin)
- {
- nextToken();
- if (token.ident == Id::C)
- {
- link = LINKobjc;
- nextToken();
- }
- else
- goto LinvalidLinkage;
- }
- else
- goto LinvalidLinkage;
- }
- else if (id == Id::System)
- {
- link = LINKsystem;
- }
- else
- {
- LinvalidLinkage:
- error("valid linkage identifiers are D, C, C++, Objective-C, Windows, System");
- link = LINKd;
- }
- }
- else
- {
- link = LINKd; // default
- }
- check(TOKrparen);
- *pidents = idents;
- *pcppmangle = cppmangle;
- *pcppMangleOnly = cppMangleOnly;
- return link;
-}
-
-/***********************************
- * Parse ident1.ident2.ident3
- *
- * Params:
- * entity = what qualified identifier is expected to resolve into.
- * Used only for better error message
- *
- * Returns:
- * array of identifiers with actual qualified one stored last
- */
-Identifiers *Parser::parseQualifiedIdentifier(const char *entity)
-{
- Identifiers *qualified = NULL;
-
- do
- {
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("%s expected as dot-separated identifiers, got `%s`",
- entity, token.toChars());
- return NULL;
- }
-
- Identifier *id = token.ident;
- if (!qualified)
- qualified = new Identifiers();
- qualified->push(id);
-
- nextToken();
- } while (token.value == TOKdot);
-
- return qualified;
-}
-
-/**************************************
- * Parse a debug conditional
- */
-
-Condition *Parser::parseDebugCondition()
-{
- Condition *c;
-
- if (token.value == TOKlparen)
- {
- nextToken();
- unsigned level = 1;
- Identifier *id = NULL;
-
- if (token.value == TOKidentifier)
- id = token.ident;
- else if (token.value == TOKint32v || token.value == TOKint64v)
- level = (unsigned)token.uns64value;
- else
- error("identifier or integer expected, not %s", token.toChars());
- nextToken();
- check(TOKrparen);
- c = new DebugCondition(mod, level, id);
- }
- else
- c = new DebugCondition(mod, 1, NULL);
- return c;
-
-}
-
-/**************************************
- * Parse a version conditional
- */
-
-Condition *Parser::parseVersionCondition()
-{
- Condition *c;
- unsigned level = 1;
- Identifier *id = NULL;
-
- if (token.value == TOKlparen)
- {
- nextToken();
- /* Allow:
- * version (unittest)
- * version (assert)
- * even though they are keywords
- */
- if (token.value == TOKidentifier)
- id = token.ident;
- else if (token.value == TOKint32v || token.value == TOKint64v)
- level = (unsigned)token.uns64value;
- else if (token.value == TOKunittest)
- id = Identifier::idPool(Token::toChars(TOKunittest));
- else if (token.value == TOKassert)
- id = Identifier::idPool(Token::toChars(TOKassert));
- else
- error("identifier or integer expected, not %s", token.toChars());
- nextToken();
- check(TOKrparen);
-
- }
- else
- error("(condition) expected following version");
- c = new VersionCondition(mod, level, id);
- return c;
-
-}
-
-/***********************************************
- * static if (expression)
- * body
- * else
- * body
- * Current token is 'static'.
- */
-
-Condition *Parser::parseStaticIfCondition()
-{
- Expression *exp;
- Condition *condition;
- Loc loc = token.loc;
-
- nextToken();
- nextToken();
- if (token.value == TOKlparen)
- {
- nextToken();
- exp = parseAssignExp();
- check(TOKrparen);
- }
- else
- {
- error("(expression) expected following static if");
- exp = NULL;
- }
- condition = new StaticIfCondition(loc, exp);
- return condition;
-}
-
-
-/*****************************************
- * Parse a constructor definition:
- * this(parameters) { body }
- * or postblit:
- * this(this) { body }
- * or constructor template:
- * this(templateparameters)(parameters) { body }
- * Current token is 'this'.
- */
-
-Dsymbol *Parser::parseCtor(PrefixAttributes *pAttrs)
-{
- Expressions *udas = NULL;
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
- if (token.value == TOKlparen && peekNext() == TOKthis && peekNext2() == TOKrparen)
- {
- // this(this) { ... }
- nextToken();
- nextToken();
- check(TOKrparen);
-
- stc = parsePostfix(stc, &udas);
- if (stc & STCstatic)
- error(loc, "postblit cannot be static");
-
- PostBlitDeclaration *f = new PostBlitDeclaration(loc, Loc(), stc, Id::postblit);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- Dsymbol *s = parseContracts(f);
- if (udas)
- {
- Dsymbols *a = new Dsymbols();
- a->push(f);
- s = new UserAttributeDeclaration(udas, a);
- }
- return s;
- }
-
- /* Look ahead to see if:
- * this(...)(...)
- * which is a constructor template
- */
- TemplateParameters *tpl = NULL;
- if (token.value == TOKlparen && peekPastParen(&token)->value == TOKlparen)
- {
- tpl = parseTemplateParameterList();
- }
-
- /* Just a regular constructor
- */
- VarArg varargs;
- Parameters *parameters = parseParameters(&varargs);
- stc = parsePostfix(stc, &udas);
- if (varargs != VARARGnone || Parameter::dim(parameters) != 0)
- {
- if (stc & STCstatic)
- error(loc, "constructor cannot be static");
- }
- else if (StorageClass ss = stc & (STCshared | STCstatic)) // this()
- {
- if (ss == STCstatic)
- error(loc, "use `static this()` to declare a static constructor");
- else if (ss == (STCshared | STCstatic))
- error(loc, "use `shared static this()` to declare a shared static constructor");
- }
-
- Expression *constraint = tpl ? parseConstraint() : NULL;
-
- Type *tf = new TypeFunction(ParameterList(parameters, varargs),
- NULL, linkage, stc); // ReturnType -> auto
- tf = tf->addSTC(stc);
-
- CtorDeclaration *f = new CtorDeclaration(loc, Loc(), stc, tf);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- Dsymbol *s = parseContracts(f);
- if (udas)
- {
- Dsymbols *a = new Dsymbols();
- a->push(f);
- s = new UserAttributeDeclaration(udas, a);
- }
-
- if (tpl)
- {
- // Wrap a template around it
- Dsymbols *decldefs = new Dsymbols();
- decldefs->push(s);
- s = new TemplateDeclaration(loc, f->ident, tpl, constraint, decldefs);
- }
-
- return s;
-}
-
-/*****************************************
- * Parse a destructor definition:
- * ~this() { body }
- * Current token is '~'.
- */
-
-Dsymbol *Parser::parseDtor(PrefixAttributes *pAttrs)
-{
- Expressions *udas = NULL;
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
- check(TOKthis);
- check(TOKlparen);
- check(TOKrparen);
-
- stc = parsePostfix(stc, &udas);
- if (StorageClass ss = stc & (STCshared | STCstatic))
- {
- if (ss == STCstatic)
- error(loc, "use `static ~this()` to declare a static destructor");
- else if (ss == (STCshared | STCstatic))
- error(loc, "use `shared static ~this()` to declare a shared static destructor");
- }
-
- DtorDeclaration *f = new DtorDeclaration(loc, Loc(), stc, Id::dtor);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- Dsymbol *s = parseContracts(f);
- if (udas)
- {
- Dsymbols *a = new Dsymbols();
- a->push(f);
- s = new UserAttributeDeclaration(udas, a);
- }
- return s;
-}
-
-/*****************************************
- * Parse a static constructor definition:
- * static this() { body }
- * Current token is 'static'.
- */
-
-Dsymbol *Parser::parseStaticCtor(PrefixAttributes *pAttrs)
-{
- //Expressions *udas = NULL;
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
- nextToken();
- check(TOKlparen);
- check(TOKrparen);
-
- stc = parsePostfix(stc & ~STC_TYPECTOR, NULL) | stc;
- if (stc & STCshared)
- error(loc, "use `shared static this()` to declare a shared static constructor");
- else if (stc & STCstatic)
- appendStorageClass(stc, STCstatic); // complaint for the redundancy
- else if (StorageClass modStc = stc & STC_TYPECTOR)
- {
- OutBuffer buf;
- stcToBuffer(&buf, modStc);
- error(loc, "static constructor cannot be %s", buf.peekChars());
- }
- stc &= ~(STCstatic | STC_TYPECTOR);
-
- StaticCtorDeclaration *f = new StaticCtorDeclaration(loc, Loc(), stc);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- Dsymbol *s = parseContracts(f);
- return s;
-}
-
-/*****************************************
- * Parse a static destructor definition:
- * static ~this() { body }
- * Current token is 'static'.
- */
-
-Dsymbol *Parser::parseStaticDtor(PrefixAttributes *pAttrs)
-{
- Expressions *udas = NULL;
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
- nextToken();
- check(TOKthis);
- check(TOKlparen);
- check(TOKrparen);
-
- stc = parsePostfix(stc & ~STC_TYPECTOR, &udas) | stc;
- if (stc & STCshared)
- error(loc, "use `shared static ~this()` to declare a shared static destructor");
- else if (stc & STCstatic)
- appendStorageClass(stc, STCstatic); // complaint for the redundancy
- else if (StorageClass modStc = stc & STC_TYPECTOR)
- {
- OutBuffer buf;
- stcToBuffer(&buf, modStc);
- error(loc, "static destructor cannot be %s", buf.peekChars());
- }
- stc &= ~(STCstatic | STC_TYPECTOR);
-
- StaticDtorDeclaration *f = new StaticDtorDeclaration(loc, Loc(), stc);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- Dsymbol *s = parseContracts(f);
- if (udas)
- {
- Dsymbols *a = new Dsymbols();
- a->push(f);
- s = new UserAttributeDeclaration(udas, a);
- }
- return s;
-}
-
-/*****************************************
- * Parse a shared static constructor definition:
- * shared static this() { body }
- * Current token is 'shared'.
- */
-
-Dsymbol *Parser::parseSharedStaticCtor(PrefixAttributes *pAttrs)
-{
- //Expressions *udas = NULL;
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
- nextToken();
- nextToken();
- check(TOKlparen);
- check(TOKrparen);
-
- stc = parsePostfix(stc & ~STC_TYPECTOR, NULL) | stc;
- if (StorageClass ss = stc & (STCshared | STCstatic))
- appendStorageClass(stc, ss); // complaint for the redundancy
- else if (StorageClass modStc = stc & STC_TYPECTOR)
- {
- OutBuffer buf;
- stcToBuffer(&buf, modStc);
- error(loc, "shared static constructor cannot be %s", buf.peekChars());
- }
- stc &= ~(STCstatic | STC_TYPECTOR);
-
- SharedStaticCtorDeclaration *f = new SharedStaticCtorDeclaration(loc, Loc(), stc);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- Dsymbol *s = parseContracts(f);
- return s;
-}
-
-/*****************************************
- * Parse a shared static destructor definition:
- * shared static ~this() { body }
- * Current token is 'shared'.
- */
-
-Dsymbol *Parser::parseSharedStaticDtor(PrefixAttributes *pAttrs)
-{
- Expressions *udas = NULL;
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
- nextToken();
- nextToken();
- check(TOKthis);
- check(TOKlparen);
- check(TOKrparen);
-
- stc = parsePostfix(stc & ~STC_TYPECTOR, &udas) | stc;
- if (StorageClass ss = stc & (STCshared | STCstatic))
- appendStorageClass(stc, ss); // complaint for the redundancy
- else if (StorageClass modStc = stc & STC_TYPECTOR)
- {
- OutBuffer buf;
- stcToBuffer(&buf, modStc);
- error(loc, "shared static destructor cannot be %s", buf.peekChars());
- }
- stc &= ~(STCstatic | STC_TYPECTOR);
-
- SharedStaticDtorDeclaration *f = new SharedStaticDtorDeclaration(loc, Loc(), stc);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- Dsymbol *s = parseContracts(f);
- if (udas)
- {
- Dsymbols *a = new Dsymbols();
- a->push(f);
- s = new UserAttributeDeclaration(udas, a);
- }
- return s;
-}
-
-/*****************************************
- * Parse an invariant definition:
- * invariant { statements... }
- * invariant() { statements... }
- * invariant (expression);
- * Current token is 'invariant'.
- */
-
-Dsymbol *Parser::parseInvariant(PrefixAttributes *pAttrs)
-{
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
- if (token.value == TOKlparen) // optional () or invariant (expression);
- {
- nextToken();
- if (token.value != TOKrparen) // invariant (expression);
- {
- Expression *e = parseAssignExp();
- Expression *msg = NULL;
- if (token.value == TOKcomma)
- {
- nextToken();
- if (token.value != TOKrparen)
- {
- msg = parseAssignExp();
- if (token.value == TOKcomma)
- nextToken();
- }
- }
- check(TOKrparen);
- check(TOKsemicolon);
- e = new AssertExp(loc, e, msg);
- ExpStatement *fbody = new ExpStatement(loc, e);
- InvariantDeclaration *f = new InvariantDeclaration(loc, token.loc, stc);
- f->fbody = fbody;
- return f;
- }
- else
- {
- nextToken();
- }
- }
-
- InvariantDeclaration *f = new InvariantDeclaration(loc, Loc(), stc);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- f->fbody = parseStatement(PScurly);
- return f;
-}
-
-/*****************************************
- * Parse a unittest definition:
- * unittest { body }
- * Current token is 'unittest'.
- */
-
-Dsymbol *Parser::parseUnitTest(PrefixAttributes *pAttrs)
-{
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
-
- const utf8_t *begPtr = token.ptr + 1; // skip '{'
- const utf8_t *endPtr = NULL;
- Statement *sbody = parseStatement(PScurly, &endPtr);
-
- /** Extract unittest body as a string. Must be done eagerly since memory
- will be released by the lexer before doc gen. */
- char *docline = NULL;
- if (global.params.doDocComments && endPtr > begPtr)
- {
- /* Remove trailing whitespaces */
- for (const utf8_t *p = endPtr - 1;
- begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
- {
- endPtr = p;
- }
-
- size_t len = endPtr - begPtr;
- if (len > 0)
- {
- docline = (char *)mem.xmalloc(len + 2);
- memcpy(docline, begPtr, len);
- docline[len ] = '\n'; // Terminate all lines by LF
- docline[len+1] = '\0';
- }
- }
-
- UnitTestDeclaration *f = new UnitTestDeclaration(loc, token.loc, stc, docline);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- f->fbody = sbody;
- return f;
-}
-
-/*****************************************
- * Parse a new definition:
- * new(parameters) { body }
- * Current token is 'new'.
- */
-
-Dsymbol *Parser::parseNew(PrefixAttributes *pAttrs)
-{
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
-
- VarArg varargs;
- Parameters *parameters = parseParameters(&varargs);
- NewDeclaration *f = new NewDeclaration(loc, Loc(), stc, parameters, varargs);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- Dsymbol *s = parseContracts(f);
- return s;
-}
-
-/*****************************************
- * Parse a delete definition:
- * delete(parameters) { body }
- * Current token is 'delete'.
- */
-
-Dsymbol *Parser::parseDelete(PrefixAttributes *pAttrs)
-{
- Loc loc = token.loc;
- StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
-
- nextToken();
-
- VarArg varargs;
- Parameters *parameters = parseParameters(&varargs);
- if (varargs != VARARGnone)
- error("... not allowed in delete function parameter list");
- DeleteDeclaration *f = new DeleteDeclaration(loc, Loc(), stc, parameters);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- Dsymbol *s = parseContracts(f);
- return s;
-}
-
-/**********************************************
- * Parse parameter list.
- */
-
-Parameters *Parser::parseParameters(VarArg *pvarargs, TemplateParameters **tpl)
-{
- Parameters *parameters = new Parameters();
- VarArg varargs = VARARGnone;
- int hasdefault = 0;
-
- check(TOKlparen);
- while (1)
- {
- Identifier *ai = NULL;
- Type *at;
- StorageClass storageClass = 0;
- StorageClass stc;
- Expression *ae;
- Expressions *udas = NULL;
-
- for (;1; nextToken())
- {
- L3:
- switch (token.value)
- {
- case TOKrparen:
- break;
-
- case TOKdotdotdot:
- varargs = VARARGvariadic;
- nextToken();
- break;
-
- case TOKconst:
- if (peek(&token)->value == TOKlparen)
- goto Ldefault;
- stc = STCconst;
- goto L2;
-
- case TOKimmutable:
- if (peek(&token)->value == TOKlparen)
- goto Ldefault;
- stc = STCimmutable;
- goto L2;
-
- case TOKshared:
- if (peek(&token)->value == TOKlparen)
- goto Ldefault;
- stc = STCshared;
- goto L2;
-
- case TOKwild:
- if (peek(&token)->value == TOKlparen)
- goto Ldefault;
- stc = STCwild;
- goto L2;
-
- case TOKat:
- {
- Expressions *exps = NULL;
- StorageClass stc2 = parseAttribute(&exps);
- if (stc2 == STCproperty || stc2 == STCnogc ||
- stc2 == STCdisable || stc2 == STCsafe ||
- stc2 == STCtrusted || stc2 == STCsystem)
- {
- error("`@%s` attribute for function parameter is not supported", token.toChars());
- }
- else
- {
- udas = UserAttributeDeclaration::concat(udas, exps);
- }
- if (token.value == TOKdotdotdot)
- error("variadic parameter cannot have user-defined attributes");
- if (stc2)
- nextToken();
- goto L3;
- // Don't call nextToken again.
- }
-
- case TOKin: stc = STCin; goto L2;
- case TOKout: stc = STCout; goto L2;
- case TOKref: stc = STCref; goto L2;
- case TOKlazy: stc = STClazy; goto L2;
- case TOKscope: stc = STCscope; goto L2;
- case TOKfinal: stc = STCfinal; goto L2;
- case TOKauto: stc = STCauto; goto L2;
- case TOKreturn: stc = STCreturn; goto L2;
- L2:
- storageClass = appendStorageClass(storageClass, stc);
- continue;
-
- default:
- Ldefault:
- { stc = storageClass & (STCin | STCout | STCref | STClazy);
- // if stc is not a power of 2
- if (stc & (stc - 1) &&
- !(stc == (STCin | STCref)))
- error("incompatible parameter storage classes");
- //if ((storageClass & STCscope) && (storageClass & (STCref | STCout)))
- //error("scope cannot be ref or out");
-
- Token *t;
- if (tpl && token.value == TOKidentifier &&
- (t = peek(&token), (t->value == TOKcomma ||
- t->value == TOKrparen ||
- t->value == TOKdotdotdot)))
- {
- Identifier *id = Identifier::generateId("__T");
- Loc loc = token.loc;
- at = new TypeIdentifier(loc, id);
- if (!*tpl)
- *tpl = new TemplateParameters();
- TemplateParameter *tp = new TemplateTypeParameter(loc, id, NULL, NULL);
- (*tpl)->push(tp);
-
- ai = token.ident;
- nextToken();
- }
- else
- at = parseType(&ai);
- ae = NULL;
- if (token.value == TOKassign) // = defaultArg
- { nextToken();
- ae = parseDefaultInitExp();
- hasdefault = 1;
- }
- else
- { if (hasdefault)
- error("default argument expected for %s",
- ai ? ai->toChars() : at->toChars());
- }
- Parameter *param = new Parameter(storageClass, at, ai, ae, NULL);
- if (udas)
- {
- Dsymbols *a = new Dsymbols();
- UserAttributeDeclaration *udad = new UserAttributeDeclaration(udas, a);
- param->userAttribDecl = udad;
- }
- if (token.value == TOKat)
- {
- Expressions *exps = NULL;
- StorageClass stc2 = parseAttribute(&exps);
- if (stc2 == STCproperty || stc2 == STCnogc ||
- stc2 == STCdisable || stc2 == STCsafe ||
- stc2 == STCtrusted || stc2 == STCsystem)
- {
- error("`@%s` attribute for function parameter is not supported", token.toChars());
- }
- else
- {
- error("user-defined attributes cannot appear as postfixes", token.toChars());
- }
- if (stc2)
- nextToken();
- }
- if (token.value == TOKdotdotdot)
- { /* This is:
- * at ai ...
- */
-
- if (storageClass & (STCout | STCref))
- error("variadic argument cannot be out or ref");
- varargs = VARARGtypesafe;
- parameters->push(param);
- nextToken();
- break;
- }
- parameters->push(param);
- if (token.value == TOKcomma)
- { nextToken();
- goto L1;
- }
- break;
- }
- }
- break;
- }
- break;
-
- L1: ;
- }
- check(TOKrparen);
- *pvarargs = varargs;
- return parameters;
-}
-
-
-/*************************************
- */
-
-EnumDeclaration *Parser::parseEnum()
-{
- EnumDeclaration *e;
- Identifier *id;
- Type *memtype;
- Loc loc = token.loc;
-
- // printf("Parser::parseEnum()\n");
- nextToken();
- if (token.value == TOKidentifier)
- {
- id = token.ident;
- nextToken();
- }
- else
- id = NULL;
-
- if (token.value == TOKcolon)
- {
- nextToken();
-
- int alt = 0;
- Loc typeLoc = token.loc;
- memtype = parseBasicType();
- memtype = parseDeclarator(memtype, &alt, NULL);
- checkCstyleTypeSyntax(typeLoc, memtype, alt, NULL);
- }
- else
- memtype = NULL;
-
- e = new EnumDeclaration(loc, id, memtype);
- if (token.value == TOKsemicolon && id)
- nextToken();
- else if (token.value == TOKlcurly)
- {
- bool isAnonymousEnum = !id;
-
- //printf("enum definition\n");
- e->members = new Dsymbols();
- nextToken();
- const utf8_t *comment = token.blockComment;
- while (token.value != TOKrcurly)
- {
- /* Can take the following forms...
- * 1. ident
- * 2. ident = value
- * 3. type ident = value
- * ... prefixed by valid attributes
- */
- loc = token.loc;
-
- Type *type = NULL;
- Identifier *ident = NULL;
-
- Expressions *udas = NULL;
- StorageClass stc = STCundefined;
- Expression *deprecationMessage = NULL;
-
- while (token.value != TOKrcurly &&
- token.value != TOKcomma &&
- token.value != TOKassign)
- {
- switch (token.value)
- {
- case TOKat:
- if (StorageClass _stc = parseAttribute(&udas))
- {
- if (_stc == STCdisable)
- stc |= _stc;
- else
- {
- OutBuffer buf;
- stcToBuffer(&buf, _stc);
- error("`%s` is not a valid attribute for enum members", buf.peekChars());
- }
- nextToken();
- }
- break;
- case TOKdeprecated:
- if (StorageClass _stc = parseDeprecatedAttribute(this, &deprecationMessage))
- {
- stc |= _stc;
- nextToken();
- }
- break;
- case TOKidentifier:
- {
- Token *tp = peek(&token);
- if (tp->value == TOKassign || tp->value == TOKcomma || tp->value == TOKrcurly)
- {
- ident = token.ident;
- type = NULL;
- nextToken();
- }
- else
- {
- goto Ldefault;
- }
- break;
- }
- default:
- Ldefault:
- if (isAnonymousEnum)
- {
- type = parseType(&ident, NULL);
- if (type == Type::terror)
- {
- type = NULL;
- nextToken();
- }
- }
- else
- {
- error("`%s` is not a valid attribute for enum members", token.toChars());
- nextToken();
- }
- break;
- }
- }
-
- if (type && type != Type::terror)
- {
- if (!ident)
- error("no identifier for declarator %s", type->toChars());
- if (!isAnonymousEnum)
- error("type only allowed if anonymous enum and no enum type");
- }
-
- Expression *value;
- if (token.value == TOKassign)
- {
- nextToken();
- value = parseAssignExp();
- }
- else
- {
- value = NULL;
- if (type && type != Type::terror && isAnonymousEnum)
- error("if type, there must be an initializer");
- }
-
- UserAttributeDeclaration *uad = NULL;
- if (udas)
- uad = new UserAttributeDeclaration(udas, NULL);
-
- DeprecatedDeclaration *dd = NULL;
- if (deprecationMessage)
- {
- dd = new DeprecatedDeclaration(deprecationMessage, NULL);
- stc |= STCdeprecated;
- }
-
- EnumMember *em = new EnumMember(loc, ident, value, type, stc, uad, dd);
- e->members->push(em);
-
- if (token.value == TOKrcurly)
- ;
- else
- {
- addComment(em, comment);
- comment = NULL;
- check(TOKcomma);
- }
- addComment(em, comment);
- comment = token.blockComment;
-
- if (token.value == TOKeof)
- {
- error("premature end of file");
- break;
- }
- }
- nextToken();
- }
- else
- error("enum declaration is invalid");
-
- //printf("-parseEnum() %s\n", e->toChars());
- return e;
-}
-
-/********************************
- * Parse struct, union, interface, class.
- */
-
-Dsymbol *Parser::parseAggregate()
-{
- AggregateDeclaration *a = NULL;
- int anon = 0;
- Identifier *id;
- TemplateParameters *tpl = NULL;
- Expression *constraint = NULL;
- Loc loc = token.loc;
- TOK tok = token.value;
-
- //printf("Parser::parseAggregate()\n");
- nextToken();
- if (token.value != TOKidentifier)
- {
- id = NULL;
- }
- else
- {
- id = token.ident;
- nextToken();
-
- if (token.value == TOKlparen)
- {
- // Class template declaration.
- // Gather template parameter list
- tpl = parseTemplateParameterList();
- constraint = parseConstraint();
- }
- }
-
- switch (tok)
- {
- case TOKclass:
- case TOKinterface:
- {
- if (!id)
- error(loc, "anonymous classes not allowed");
-
- // Collect base class(es)
- BaseClasses *baseclasses = NULL;
- if (token.value == TOKcolon)
- {
- nextToken();
- baseclasses = parseBaseClasses();
-
- if (tpl)
- {
- Expression *tempCons = parseConstraint();
- if (tempCons)
- {
- if (constraint)
- error("members expected");
- else
- constraint = tempCons;
- }
- }
-
- if (token.value != TOKlcurly)
- error("members expected");
- }
-
- if (tok == TOKclass)
- {
- bool inObject = md && !md->packages && md->id == Id::object;
- a = new ClassDeclaration(loc, id, baseclasses, NULL, inObject);
- }
- else
- a = new InterfaceDeclaration(loc, id, baseclasses);
- break;
- }
-
- case TOKstruct:
- if (id)
- {
- bool inObject = md && !md->packages && md->id == Id::object;
- a = new StructDeclaration(loc, id, inObject);
- }
- else
- anon = 1;
- break;
-
- case TOKunion:
- if (id)
- a = new UnionDeclaration(loc, id);
- else
- anon = 2;
- break;
-
- default:
- assert(0);
- break;
- }
- if (a && token.value == TOKsemicolon)
- {
- nextToken();
- }
- else if (token.value == TOKlcurly)
- {
- const Loc lookingForElseSave = lookingForElse;
- lookingForElse = Loc();
- //printf("aggregate definition\n");
- nextToken();
- Dsymbols *decl = parseDeclDefs(0);
- lookingForElse = lookingForElseSave;
- if (token.value != TOKrcurly)
- error("} expected following members in %s declaration at %s",
- Token::toChars(tok), loc.toChars());
- nextToken();
- if (anon)
- {
- /* Anonymous structs/unions are more like attributes.
- */
- return new AnonDeclaration(loc, anon == 2, decl);
- }
- else
- a->members = decl;
- }
- else
- {
- error("{ } expected following %s declaration", Token::toChars(tok));
- a = new StructDeclaration(loc, NULL, false);
- }
-
- if (tpl)
- {
- // Wrap a template around the aggregate declaration
- Dsymbols *decldefs = new Dsymbols();
- decldefs->push(a);
- TemplateDeclaration *tempdecl =
- new TemplateDeclaration(loc, id, tpl, constraint, decldefs);
- return tempdecl;
- }
-
- return a;
-}
-
-/*******************************************
- */
-
-BaseClasses *Parser::parseBaseClasses()
-{
- BaseClasses *baseclasses = new BaseClasses();
-
- for (; 1; nextToken())
- {
- bool prot = false;
- Prot protection = Prot(Prot::public_);
- switch (token.value)
- {
- case TOKprivate:
- prot = true;
- protection = Prot(Prot::private_);
- nextToken();
- break;
- case TOKpackage:
- prot = true;
- protection = Prot(Prot::package_);
- nextToken();
- break;
- case TOKprotected:
- prot = true;
- protection = Prot(Prot::protected_);
- nextToken();
- break;
- case TOKpublic:
- prot = true;
- protection = Prot(Prot::public_);
- nextToken();
- break;
- default: break;
- }
- if (prot)
- error("use of base class protection is no longer supported");
- BaseClass *b = new BaseClass(parseBasicType());
- baseclasses->push(b);
- if (token.value != TOKcomma)
- break;
- }
- return baseclasses;
-}
-
-/**************************************
- * Parse constraint.
- * Constraint is of the form:
- * if ( ConstraintExpression )
- */
-
-Expression *Parser::parseConstraint()
-{ Expression *e = NULL;
-
- if (token.value == TOKif)
- {
- nextToken(); // skip over 'if'
- check(TOKlparen);
- e = parseExpression();
- check(TOKrparen);
- }
- return e;
-}
-
-/**************************************
- * Parse a TemplateDeclaration.
- */
-
-TemplateDeclaration *Parser::parseTemplateDeclaration(bool ismixin)
-{
- TemplateDeclaration *tempdecl;
- Identifier *id;
- TemplateParameters *tpl;
- Dsymbols *decldefs;
- Expression *constraint = NULL;
- Loc loc = token.loc;
-
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following template");
- goto Lerr;
- }
- id = token.ident;
- nextToken();
- tpl = parseTemplateParameterList();
- if (!tpl)
- goto Lerr;
-
- constraint = parseConstraint();
-
- if (token.value != TOKlcurly)
- {
- error("members of template declaration expected");
- goto Lerr;
- }
- else
- decldefs = parseBlock(NULL);
-
- tempdecl = new TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
- return tempdecl;
-
-Lerr:
- return NULL;
-}
-
-/******************************************
- * Parse template parameter list.
- * Input:
- * flag 0: parsing "( list )"
- * 1: parsing non-empty "list )"
- */
-
-TemplateParameters *Parser::parseTemplateParameterList(int flag)
-{
- TemplateParameters *tpl = new TemplateParameters();
-
- if (!flag && token.value != TOKlparen)
- { error("parenthesized TemplateParameterList expected following TemplateIdentifier");
- goto Lerr;
- }
- nextToken();
-
- // Get array of TemplateParameters
- if (flag || token.value != TOKrparen)
- {
- int isvariadic = 0;
- while (token.value != TOKrparen)
- {
- TemplateParameter *tp;
- Loc loc;
- Identifier *tp_ident = NULL;
- Type *tp_spectype = NULL;
- Type *tp_valtype = NULL;
- Type *tp_defaulttype = NULL;
- Expression *tp_specvalue = NULL;
- Expression *tp_defaultvalue = NULL;
- Token *t;
-
- // Get TemplateParameter
-
- // First, look ahead to see if it is a TypeParameter or a ValueParameter
- t = peek(&token);
- if (token.value == TOKalias)
- { // AliasParameter
- nextToken();
- loc = token.loc; // todo
- Type *spectype = NULL;
- if (isDeclaration(&token, 2, TOKreserved, NULL))
- {
- spectype = parseType(&tp_ident);
- }
- else
- {
- if (token.value != TOKidentifier)
- {
- error("identifier expected for template alias parameter");
- goto Lerr;
- }
- tp_ident = token.ident;
- nextToken();
- }
- RootObject *spec = NULL;
- if (token.value == TOKcolon) // : Type
- {
- nextToken();
- if (isDeclaration(&token, 0, TOKreserved, NULL))
- spec = parseType();
- else
- spec = parseCondExp();
- }
- RootObject *def = NULL;
- if (token.value == TOKassign) // = Type
- {
- nextToken();
- if (isDeclaration(&token, 0, TOKreserved, NULL))
- def = parseType();
- else
- def = parseCondExp();
- }
- tp = new TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
- }
- else if (t->value == TOKcolon || t->value == TOKassign ||
- t->value == TOKcomma || t->value == TOKrparen)
- {
- // TypeParameter
- if (token.value != TOKidentifier)
- {
- error("identifier expected for template type parameter");
- goto Lerr;
- }
- loc = token.loc;
- tp_ident = token.ident;
- nextToken();
- if (token.value == TOKcolon) // : Type
- {
- nextToken();
- tp_spectype = parseType();
- }
- if (token.value == TOKassign) // = Type
- {
- nextToken();
- tp_defaulttype = parseType();
- }
- tp = new TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
- }
- else if (token.value == TOKidentifier && t->value == TOKdotdotdot)
- {
- // ident...
- if (isvariadic)
- error("variadic template parameter must be last");
- isvariadic = 1;
- loc = token.loc;
- tp_ident = token.ident;
- nextToken();
- nextToken();
- tp = new TemplateTupleParameter(loc, tp_ident);
- }
- else if (token.value == TOKthis)
- {
- // ThisParameter
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected for template this parameter");
- goto Lerr;
- }
- loc = token.loc;
- tp_ident = token.ident;
- nextToken();
- if (token.value == TOKcolon) // : Type
- {
- nextToken();
- tp_spectype = parseType();
- }
- if (token.value == TOKassign) // = Type
- {
- nextToken();
- tp_defaulttype = parseType();
- }
- tp = new TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
- }
- else
- {
- // ValueParameter
- loc = token.loc; // todo
- tp_valtype = parseType(&tp_ident);
- if (!tp_ident)
- {
- error("identifier expected for template value parameter");
- tp_ident = Identifier::idPool("error");
- }
- if (token.value == TOKcolon) // : CondExpression
- {
- nextToken();
- tp_specvalue = parseCondExp();
- }
- if (token.value == TOKassign) // = CondExpression
- {
- nextToken();
- tp_defaultvalue = parseDefaultInitExp();
- }
- tp = new TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
- }
- tpl->push(tp);
- if (token.value != TOKcomma)
- break;
- nextToken();
- }
- }
- check(TOKrparen);
-Lerr:
- return tpl;
-}
-
-/******************************************
- * Parse template mixin.
- * mixin Foo;
- * mixin Foo!(args);
- * mixin a.b.c!(args).Foo!(args);
- * mixin Foo!(args) identifier;
- * mixin typeof(expr).identifier!(args);
- */
-
-Dsymbol *Parser::parseMixin()
-{
- TemplateMixin *tm;
- Identifier *id;
- Objects *tiargs;
-
- //printf("parseMixin()\n");
- Loc locMixin = token.loc;
- nextToken(); // skip 'mixin'
-
- Loc loc = token.loc;
- TypeQualified *tqual = NULL;
- if (token.value == TOKdot)
- {
- id = Id::empty;
- }
- else
- {
- if (token.value == TOKtypeof)
- {
- tqual = parseTypeof();
- check(TOKdot);
- }
- if (token.value != TOKidentifier)
- {
- error("identifier expected, not %s", token.toChars());
- id = Id::empty;
- }
- else
- id = token.ident;
- nextToken();
- }
-
- while (1)
- {
- tiargs = NULL;
- if (token.value == TOKnot)
- {
- tiargs = parseTemplateArguments();
- }
-
- if (tiargs && token.value == TOKdot)
- {
- TemplateInstance *tempinst = new TemplateInstance(loc, id);
- tempinst->tiargs = tiargs;
- if (!tqual)
- tqual = new TypeInstance(loc, tempinst);
- else
- tqual->addInst(tempinst);
- tiargs = NULL;
- }
- else
- {
- if (!tqual)
- tqual = new TypeIdentifier(loc, id);
- else
- tqual->addIdent(id);
- }
-
- if (token.value != TOKdot)
- break;
-
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following `.` instead of `%s`", token.toChars());
- break;
- }
- loc = token.loc;
- id = token.ident;
- nextToken();
- }
-
- if (token.value == TOKidentifier)
- {
- id = token.ident;
- nextToken();
- }
- else
- id = NULL;
-
- tm = new TemplateMixin(locMixin, id, tqual, tiargs);
- if (token.value != TOKsemicolon)
- error("`;` expected after mixin");
- nextToken();
-
- return tm;
-}
-
-/******************************************
- * Parse template arguments.
- * Input:
- * current token is opening '!'
- * Output:
- * current token is one after closing ')'
- */
-
-Objects *Parser::parseTemplateArguments()
-{
- Objects *tiargs;
-
- nextToken();
- if (token.value == TOKlparen)
- {
- // ident!(template_arguments)
- tiargs = parseTemplateArgumentList();
- }
- else
- {
- // ident!template_argument
- tiargs = parseTemplateSingleArgument();
- }
- if (token.value == TOKnot)
- {
- TOK tok = peekNext();
- if (tok != TOKis && tok != TOKin)
- {
- error("multiple ! arguments are not allowed");
- Lagain:
- nextToken();
- if (token.value == TOKlparen)
- parseTemplateArgumentList();
- else
- parseTemplateSingleArgument();
- if (token.value == TOKnot && (tok = peekNext()) != TOKis && tok != TOKin)
- goto Lagain;
- }
- }
- return tiargs;
-}
-
-/***************************************
- * Parse a Type or an Expression
- * Returns:
- * RootObject representing the AST
- */
-RootObject *Parser::parseTypeOrAssignExp(TOK endtoken)
-{
- return isDeclaration(&token, 0, endtoken, NULL)
- ? (RootObject *)parseType() // argument is a type
- : (RootObject *)parseAssignExp(); // argument is an expression
-}
-
-/******************************************
- * Parse template argument list.
- * Input:
- * current token is opening '(',
- * or ',' for __traits
- * Output:
- * current token is one after closing ')'
- */
-
-Objects *Parser::parseTemplateArgumentList()
-{
- //printf("Parser::parseTemplateArgumentList()\n");
- Objects *tiargs = new Objects();
- TOK endtok = TOKrparen;
- assert(token.value == TOKlparen || token.value == TOKcomma);
- nextToken();
-
- // Get TemplateArgumentList
- while (token.value != endtok)
- {
- tiargs->push(parseTypeOrAssignExp());
- if (token.value != TOKcomma)
- break;
- nextToken();
- }
- check(endtok, "template argument list");
- return tiargs;
-}
-
-/*****************************
- * Parse single template argument, to support the syntax:
- * foo!arg
- * Input:
- * current token is the arg
- */
-
-Objects *Parser::parseTemplateSingleArgument()
-{
- //printf("parseTemplateSingleArgument()\n");
- Objects *tiargs = new Objects();
- Type *ta;
- switch (token.value)
- {
- case TOKidentifier:
- ta = new TypeIdentifier(token.loc, token.ident);
- goto LabelX;
-
- case TOKvector:
- ta = parseVector();
- goto LabelX;
-
- case TOKvoid: ta = Type::tvoid; goto LabelX;
- case TOKint8: ta = Type::tint8; goto LabelX;
- case TOKuns8: ta = Type::tuns8; goto LabelX;
- case TOKint16: ta = Type::tint16; goto LabelX;
- case TOKuns16: ta = Type::tuns16; goto LabelX;
- case TOKint32: ta = Type::tint32; goto LabelX;
- case TOKuns32: ta = Type::tuns32; goto LabelX;
- case TOKint64: ta = Type::tint64; goto LabelX;
- case TOKuns64: ta = Type::tuns64; goto LabelX;
- case TOKint128: ta = Type::tint128; goto LabelX;
- case TOKuns128: ta = Type::tuns128; goto LabelX;
- case TOKfloat32: ta = Type::tfloat32; goto LabelX;
- case TOKfloat64: ta = Type::tfloat64; goto LabelX;
- case TOKfloat80: ta = Type::tfloat80; goto LabelX;
- case TOKimaginary32: ta = Type::timaginary32; goto LabelX;
- case TOKimaginary64: ta = Type::timaginary64; goto LabelX;
- case TOKimaginary80: ta = Type::timaginary80; goto LabelX;
- case TOKcomplex32: ta = Type::tcomplex32; goto LabelX;
- case TOKcomplex64: ta = Type::tcomplex64; goto LabelX;
- case TOKcomplex80: ta = Type::tcomplex80; goto LabelX;
- case TOKbool: ta = Type::tbool; goto LabelX;
- case TOKchar: ta = Type::tchar; goto LabelX;
- case TOKwchar: ta = Type::twchar; goto LabelX;
- case TOKdchar: ta = Type::tdchar; goto LabelX;
- LabelX:
- tiargs->push(ta);
- nextToken();
- break;
-
- case TOKint32v:
- case TOKuns32v:
- case TOKint64v:
- case TOKuns64v:
- case TOKint128v:
- case TOKuns128v:
- case TOKfloat32v:
- case TOKfloat64v:
- case TOKfloat80v:
- case TOKimaginary32v:
- case TOKimaginary64v:
- case TOKimaginary80v:
- case TOKnull:
- case TOKtrue:
- case TOKfalse:
- case TOKcharv:
- case TOKwcharv:
- case TOKdcharv:
- case TOKstring:
- case TOKxstring:
- case TOKfile:
- case TOKfilefullpath:
- case TOKline:
- case TOKmodulestring:
- case TOKfuncstring:
- case TOKprettyfunc:
- case TOKthis:
- { // Template argument is an expression
- Expression *ea = parsePrimaryExp();
- tiargs->push(ea);
- break;
- }
-
- default:
- error("template argument expected following !");
- break;
- }
- return tiargs;
-}
-
-Dsymbols *Parser::parseImport()
-{
- Dsymbols *decldefs = new Dsymbols();
- Identifier *aliasid = NULL;
-
- int isstatic = token.value == TOKstatic;
- if (isstatic)
- nextToken();
-
- //printf("Parser::parseImport()\n");
- do
- {
- L1:
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following import");
- break;
- }
-
- Loc loc = token.loc;
- Identifier *id = token.ident;
- Identifiers *a = NULL;
- nextToken();
- if (!aliasid && token.value == TOKassign)
- {
- aliasid = id;
- goto L1;
- }
- while (token.value == TOKdot)
- {
- if (!a)
- a = new Identifiers();
- a->push(id);
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following package");
- break;
- }
- id = token.ident;
- nextToken();
- }
-
- Import *s = new Import(loc, a, id, aliasid, isstatic);
- decldefs->push(s);
-
- /* Look for
- * : alias=name, alias=name;
- * syntax.
- */
- if (token.value == TOKcolon)
- {
- do
- {
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following :");
- break;
- }
- Identifier *alias = token.ident;
- Identifier *name;
- nextToken();
- if (token.value == TOKassign)
- {
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following %s=", alias->toChars());
- break;
- }
- name = token.ident;
- nextToken();
- }
- else
- {
- name = alias;
- alias = NULL;
- }
- s->addAlias(name, alias);
- } while (token.value == TOKcomma);
- break; // no comma-separated imports of this form
- }
-
- aliasid = NULL;
- } while (token.value == TOKcomma);
-
- if (token.value == TOKsemicolon)
- nextToken();
- else
- {
- error("`;` expected");
- nextToken();
- }
-
- return decldefs;
-}
-
-Type *Parser::parseType(Identifier **pident, TemplateParameters **ptpl)
-{
- /* Take care of the storage class prefixes that
- * serve as type attributes:
- * const type
- * immutable type
- * shared type
- * inout type
- * inout const type
- * shared const type
- * shared inout type
- * shared inout const type
- */
- StorageClass stc = 0;
- while (1)
- {
- switch (token.value)
- {
- case TOKconst:
- if (peekNext() == TOKlparen)
- break; // const as type constructor
- stc |= STCconst; // const as storage class
- nextToken();
- continue;
-
- case TOKimmutable:
- if (peekNext() == TOKlparen)
- break;
- stc |= STCimmutable;
- nextToken();
- continue;
-
- case TOKshared:
- if (peekNext() == TOKlparen)
- break;
- stc |= STCshared;
- nextToken();
- continue;
-
- case TOKwild:
- if (peekNext() == TOKlparen)
- break;
- stc |= STCwild;
- nextToken();
- continue;
-
- default:
- break;
- }
- break;
- }
-
- Loc typeLoc = token.loc;
-
- Type *t;
- t = parseBasicType();
-
- int alt = 0;
- t = parseDeclarator(t, &alt, pident, ptpl);
- checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : NULL);
-
- t = t->addSTC(stc);
- return t;
-}
-
-Type *Parser::parseBasicType(bool dontLookDotIdents)
-{
- Type *t;
- Loc loc;
- Identifier *id;
-
- //printf("parseBasicType()\n");
- switch (token.value)
- {
- case TOKvoid: t = Type::tvoid; goto LabelX;
- case TOKint8: t = Type::tint8; goto LabelX;
- case TOKuns8: t = Type::tuns8; goto LabelX;
- case TOKint16: t = Type::tint16; goto LabelX;
- case TOKuns16: t = Type::tuns16; goto LabelX;
- case TOKint32: t = Type::tint32; goto LabelX;
- case TOKuns32: t = Type::tuns32; goto LabelX;
- case TOKint64:
- t = Type::tint64;
- nextToken();
- if (token.value == TOKint64) // if `long long`
- {
- error("use `long` for a 64 bit integer instead of `long long`");
- nextToken();
- }
- else if (token.value == TOKfloat64) // if `long double`
- {
- error("use `real` instead of `long double`");
- t = Type::tfloat80;
- nextToken();
-
- }
- break;
-
- case TOKuns64: t = Type::tuns64; goto LabelX;
- case TOKint128: t = Type::tint128; goto LabelX;
- case TOKuns128: t = Type::tuns128; goto LabelX;
- case TOKfloat32: t = Type::tfloat32; goto LabelX;
- case TOKfloat64: t = Type::tfloat64; goto LabelX;
- case TOKfloat80: t = Type::tfloat80; goto LabelX;
- case TOKimaginary32: t = Type::timaginary32; goto LabelX;
- case TOKimaginary64: t = Type::timaginary64; goto LabelX;
- case TOKimaginary80: t = Type::timaginary80; goto LabelX;
- case TOKcomplex32: t = Type::tcomplex32; goto LabelX;
- case TOKcomplex64: t = Type::tcomplex64; goto LabelX;
- case TOKcomplex80: t = Type::tcomplex80; goto LabelX;
- case TOKbool: t = Type::tbool; goto LabelX;
- case TOKchar: t = Type::tchar; goto LabelX;
- case TOKwchar: t = Type::twchar; goto LabelX;
- case TOKdchar: t = Type::tdchar; goto LabelX;
- LabelX:
- nextToken();
- break;
-
- case TOKthis:
- case TOKsuper:
- case TOKidentifier:
- loc = token.loc;
- id = token.ident;
- nextToken();
- if (token.value == TOKnot)
- {
- // ident!(template_arguments)
- TemplateInstance *tempinst = new TemplateInstance(loc, id);
- tempinst->tiargs = parseTemplateArguments();
- t = parseBasicTypeStartingAt(new TypeInstance(loc, tempinst), dontLookDotIdents);
- }
- else
- {
- t = parseBasicTypeStartingAt(new TypeIdentifier(loc, id), dontLookDotIdents);
- }
- break;
-
- case TOKmixin:
- // https://dlang.org/spec/expression.html#mixin_types
- loc = token.loc;
- nextToken();
- if (token.value != TOKlparen)
- error("found `%s` when expecting `%s` following %s", token.toChars(), Token::toChars(TOKlparen), "`mixin`");
- t = new TypeMixin(loc, parseArguments());
- break;
-
- case TOKdot:
- // Leading . as in .foo
- t = parseBasicTypeStartingAt(new TypeIdentifier(token.loc, Id::empty), dontLookDotIdents);
- break;
-
- case TOKtypeof:
- // typeof(expression)
- t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
- break;
-
- case TOKvector:
- t = parseVector();
- break;
-
- case TOKtraits:
- if (TraitsExp *te = (TraitsExp *) parsePrimaryExp())
- {
- if (te->ident && te->args)
- {
- t = new TypeTraits(token.loc, te);
- break;
- }
- }
- t = new TypeError();
- break;
-
- case TOKconst:
- // const(type)
- nextToken();
- check(TOKlparen);
- t = parseType()->addSTC(STCconst);
- check(TOKrparen);
- break;
-
- case TOKimmutable:
- // immutable(type)
- nextToken();
- check(TOKlparen);
- t = parseType()->addSTC(STCimmutable);
- check(TOKrparen);
- break;
-
- case TOKshared:
- // shared(type)
- nextToken();
- check(TOKlparen);
- t = parseType()->addSTC(STCshared);
- check(TOKrparen);
- break;
-
- case TOKwild:
- // wild(type)
- nextToken();
- check(TOKlparen);
- t = parseType()->addSTC(STCwild);
- check(TOKrparen);
- break;
-
- default:
- error("basic type expected, not %s", token.toChars());
- t = Type::terror;
- break;
- }
- return t;
-}
-
-Type *Parser::parseBasicTypeStartingAt(TypeQualified *tid, bool dontLookDotIdents)
-{
- Type *maybeArray = NULL;
- // See https://issues.dlang.org/show_bug.cgi?id=1215
- // A basic type can look like MyType (typical case), but also:
- // MyType.T -> A type
- // MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
- // MyType[expr].T -> A type.
- // MyType[expr].T[expr] -> Either a static array of MyType[expr].T or a type
- // (iif MyType[expr].T is a Ttuple)
- while (1)
- {
- switch (token.value)
- {
- case TOKdot:
- {
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following `.` instead of `%s`", token.toChars());
- break;
- }
- if (maybeArray)
- {
- // This is actually a TypeTuple index, not an {a/s}array.
- // We need to have a while loop to unwind all index taking:
- // T[e1][e2].U -> T, addIndex(e1), addIndex(e2)
- Objects dimStack;
- Type *t = maybeArray;
- while (true)
- {
- if (t->ty == Tsarray)
- {
- // The index expression is an Expression.
- TypeSArray *a = (TypeSArray *)t;
- dimStack.push(a->dim->syntaxCopy());
- t = a->next->syntaxCopy();
- }
- else if (t->ty == Taarray)
- {
- // The index expression is a Type. It will be interpreted as an expression at semantic time.
- TypeAArray *a = (TypeAArray *)t;
- dimStack.push(a->index->syntaxCopy());
- t = a->next->syntaxCopy();
- }
- else
- {
- break;
- }
- }
- assert(dimStack.length > 0);
- // We're good. Replay indices in the reverse order.
- tid = (TypeQualified *)t;
- while (dimStack.length)
- {
- tid->addIndex(dimStack.pop());
- }
- maybeArray = NULL;
- }
- Loc loc = token.loc;
- Identifier *id = token.ident;
- nextToken();
- if (token.value == TOKnot)
- {
- TemplateInstance *tempinst = new TemplateInstance(loc, id);
- tempinst->tiargs = parseTemplateArguments();
- tid->addInst(tempinst);
- }
- else
- tid->addIdent(id);
- continue;
- }
- case TOKlbracket:
- {
- if (dontLookDotIdents) // workaround for Bugzilla 14911
- goto Lend;
-
- nextToken();
- Type *t = maybeArray ? maybeArray : (Type *)tid;
- if (token.value == TOKrbracket)
- {
- // It's a dynamic array, and we're done:
- // T[].U does not make sense.
- t = new TypeDArray(t);
- nextToken();
- return t;
- }
- else if (isDeclaration(&token, 0, TOKrbracket, NULL))
- {
- // This can be one of two things:
- // 1 - an associative array declaration, T[type]
- // 2 - an associative array declaration, T[expr]
- // These can only be disambiguated later.
- Type *index = parseType(); // [ type ]
- maybeArray = new TypeAArray(t, index);
- check(TOKrbracket);
- }
- else
- {
- // This can be one of three things:
- // 1 - an static array declaration, T[expr]
- // 2 - a slice, T[expr .. expr]
- // 3 - a template parameter pack index expression, T[expr].U
- // 1 and 3 can only be disambiguated later.
- //printf("it's type[expression]\n");
- inBrackets++;
- Expression *e = parseAssignExp(); // [ expression ]
- if (token.value == TOKslice)
- {
- // It's a slice, and we're done.
- nextToken();
- Expression *e2 = parseAssignExp(); // [ exp .. exp ]
- t = new TypeSlice(t, e, e2);
- inBrackets--;
- check(TOKrbracket);
- return t;
- }
- else
- {
- maybeArray = new TypeSArray(t, e);
- inBrackets--;
- check(TOKrbracket);
- continue;
- }
- }
- break;
- }
- default:
- goto Lend;
- }
- }
-Lend:
- return maybeArray ? maybeArray : (Type *)tid;
-}
-
-/******************************************
- * Parse things that follow the initial type t.
- * t *
- * t []
- * t [type]
- * t [expression]
- * t [expression .. expression]
- * t function
- * t delegate
- */
-
-Type *Parser::parseBasicType2(Type *t)
-{
- //printf("parseBasicType2()\n");
- while (1)
- {
- switch (token.value)
- {
- case TOKmul:
- t = new TypePointer(t);
- nextToken();
- continue;
-
- case TOKlbracket:
- // Handle []. Make sure things like
- // int[3][1] a;
- // is (array[1] of array[3] of int)
- nextToken();
- if (token.value == TOKrbracket)
- {
- t = new TypeDArray(t); // []
- nextToken();
- }
- else if (isDeclaration(&token, 0, TOKrbracket, NULL))
- {
- // It's an associative array declaration
- //printf("it's an associative array\n");
- Type *index = parseType(); // [ type ]
- t = new TypeAArray(t, index);
- check(TOKrbracket);
- }
- else
- {
- //printf("it's type[expression]\n");
- inBrackets++;
- Expression *e = parseAssignExp(); // [ expression ]
- if (token.value == TOKslice)
- {
- nextToken();
- Expression *e2 = parseAssignExp(); // [ exp .. exp ]
- t = new TypeSlice(t, e, e2);
- }
- else
- {
- t = new TypeSArray(t,e);
- }
- inBrackets--;
- check(TOKrbracket);
- }
- continue;
-
- case TOKdelegate:
- case TOKfunction:
- {
- // Handle delegate declaration:
- // t delegate(parameter list) nothrow pure
- // t function(parameter list) nothrow pure
- TOK save = token.value;
- nextToken();
-
- VarArg varargs;
- Parameters *parameters = parseParameters(&varargs);
-
- StorageClass stc = parsePostfix(STCundefined, NULL);
- TypeFunction *tf = new TypeFunction(ParameterList(parameters, varargs),
- t, linkage, stc);
- if (stc & (STCconst | STCimmutable | STCshared | STCwild | STCreturn))
- {
- if (save == TOKfunction)
- error("const/immutable/shared/inout/return attributes are only valid for non-static member functions");
- else
- tf = (TypeFunction *)tf->addSTC(stc);
- }
-
- if (save == TOKdelegate)
- t = new TypeDelegate(tf);
- else
- t = new TypePointer(tf); // pointer to function
- continue;
- }
-
- default:
- return t;
- }
- assert(0);
- }
- assert(0);
- return NULL;
-}
-
-Type *Parser::parseDeclarator(Type *t, int *palt, Identifier **pident,
- TemplateParameters **tpl, StorageClass storageClass, int *pdisable, Expressions **pudas)
-{
- //printf("parseDeclarator(tpl = %p)\n", tpl);
- t = parseBasicType2(t);
-
- Type *ts;
- switch (token.value)
- {
- case TOKidentifier:
- if (pident)
- *pident = token.ident;
- else
- error("unexpected identifier `%s` in declarator", token.ident->toChars());
- ts = t;
- nextToken();
- break;
-
- case TOKlparen:
- {
- // like: T (*fp)();
- // like: T ((*fp))();
- if (peekNext() == TOKmul ||
- peekNext() == TOKlparen)
- {
- /* Parse things with parentheses around the identifier, like:
- * int (*ident[3])[]
- * although the D style would be:
- * int[]*[3] ident
- */
- *palt |= 1;
- nextToken();
- ts = parseDeclarator(t, palt, pident);
- check(TOKrparen);
- break;
- }
- ts = t;
-
- Token *peekt = &token;
- /* Completely disallow C-style things like:
- * T (a);
- * Improve error messages for the common bug of a missing return type
- * by looking to see if (a) looks like a parameter list.
- */
- if (isParameters(&peekt))
- {
- error("function declaration without return type. (Note that constructors are always named `this`)");
- }
- else
- error("unexpected ( in declarator");
- break;
- }
-
- default:
- ts = t;
- break;
- }
-
- // parse DeclaratorSuffixes
- while (1)
- {
- switch (token.value)
- {
-#if CARRAYDECL
- /* Support C style array syntax:
- * int ident[]
- * as opposed to D-style:
- * int[] ident
- */
- case TOKlbracket:
- {
- // This is the old C-style post [] syntax.
- TypeNext *ta;
- nextToken();
- if (token.value == TOKrbracket)
- {
- // It's a dynamic array
- ta = new TypeDArray(t); // []
- nextToken();
- *palt |= 2;
- }
- else if (isDeclaration(&token, 0, TOKrbracket, NULL))
- {
- // It's an associative array
- //printf("it's an associative array\n");
- Type *index = parseType(); // [ type ]
- check(TOKrbracket);
- ta = new TypeAArray(t, index);
- *palt |= 2;
- }
- else
- {
- //printf("It's a static array\n");
- Expression *e = parseAssignExp(); // [ expression ]
- ta = new TypeSArray(t, e);
- check(TOKrbracket);
- *palt |= 2;
- }
-
- /* Insert ta into
- * ts -> ... -> t
- * so that
- * ts -> ... -> ta -> t
- */
- Type **pt;
- for (pt = &ts; *pt != t; pt = &((TypeNext *)*pt)->next)
- ;
- *pt = ta;
- continue;
- }
-#endif
- case TOKlparen:
- {
- if (tpl)
- {
- Token *tk = peekPastParen(&token);
- if (tk->value == TOKlparen)
- {
- /* Look ahead to see if this is (...)(...),
- * i.e. a function template declaration
- */
- //printf("function template declaration\n");
-
- // Gather template parameter list
- *tpl = parseTemplateParameterList();
- }
- else if (tk->value == TOKassign)
- {
- /* or (...) =,
- * i.e. a variable template declaration
- */
- //printf("variable template declaration\n");
- *tpl = parseTemplateParameterList();
- break;
- }
- }
-
- VarArg varargs;
- Parameters *parameters = parseParameters(&varargs);
-
- /* Parse const/immutable/shared/inout/nothrow/pure/return postfix
- */
- StorageClass stc = parsePostfix(storageClass, pudas);
- // merge prefix storage classes
- Type *tf = new TypeFunction(ParameterList(parameters, varargs),
- t, linkage, stc);
- tf = tf->addSTC(stc);
- if (pdisable)
- *pdisable = stc & STCdisable ? 1 : 0;
-
- /* Insert tf into
- * ts -> ... -> t
- * so that
- * ts -> ... -> tf -> t
- */
- Type **pt;
- for (pt = &ts; *pt != t; pt = &((TypeNext *)*pt)->next)
- ;
- *pt = tf;
- break;
- }
- default: break;
- }
- break;
- }
-
- return ts;
-}
-
-void Parser::parseStorageClasses(StorageClass &storage_class, LINK &link,
- bool &setAlignment, Expression *&ealign, Expressions *&udas)
-{
- StorageClass stc;
- bool sawLinkage = false; // seen a linkage declaration
-
- while (1)
- {
- switch (token.value)
- {
- case TOKconst:
- if (peek(&token)->value == TOKlparen)
- break; // const as type constructor
- stc = STCconst; // const as storage class
- goto L1;
-
- case TOKimmutable:
- if (peek(&token)->value == TOKlparen)
- break;
- stc = STCimmutable;
- goto L1;
-
- case TOKshared:
- if (peek(&token)->value == TOKlparen)
- break;
- stc = STCshared;
- goto L1;
-
- case TOKwild:
- if (peek(&token)->value == TOKlparen)
- break;
- stc = STCwild;
- goto L1;
-
- case TOKstatic: stc = STCstatic; goto L1;
- case TOKfinal: stc = STCfinal; goto L1;
- case TOKauto: stc = STCauto; goto L1;
- case TOKscope: stc = STCscope; goto L1;
- case TOKoverride: stc = STCoverride; goto L1;
- case TOKabstract: stc = STCabstract; goto L1;
- case TOKsynchronized: stc = STCsynchronized; goto L1;
- case TOKdeprecated: stc = STCdeprecated; goto L1;
- case TOKnothrow: stc = STCnothrow; goto L1;
- case TOKpure: stc = STCpure; goto L1;
- case TOKref: stc = STCref; goto L1;
- case TOKgshared: stc = STCgshared; goto L1;
- case TOKenum: stc = STCmanifest; goto L1;
- case TOKat:
- {
- stc = parseAttribute(&udas);
- if (stc)
- goto L1;
- continue;
- }
- L1:
- storage_class = appendStorageClass(storage_class, stc);
- nextToken();
- continue;
-
- case TOKextern:
- {
- if (peek(&token)->value != TOKlparen)
- {
- stc = STCextern;
- goto L1;
- }
-
- if (sawLinkage)
- error("redundant linkage declaration");
- sawLinkage = true;
- Identifiers *idents = NULL;
- CPPMANGLE cppmangle = CPPMANGLEdefault;
- bool cppMangleOnly = false;
- link = parseLinkage(&idents, &cppmangle, &cppMangleOnly);
- if (idents)
- {
- error("C++ name spaces not allowed here");
- delete idents;
- }
- if (cppmangle != CPPMANGLEdefault)
- {
- error("C++ mangle declaration not allowed here");
- }
- continue;
- }
-
- case TOKalign:
- {
- nextToken();
- setAlignment = true;
- if (token.value == TOKlparen)
- {
- nextToken();
- ealign = parseExpression();
- check(TOKrparen);
- }
- continue;
- }
- default:
- break;
- }
- break;
- }
-}
-
-static void parseAttributes(Parser *p, bool &hasParsedAttributes,
- StorageClass &storage_class, LINK &link, bool &setAlignment,
- Expression *&ealign, Expressions *&udas)
-{
- if (hasParsedAttributes) // only parse once
- return;
- hasParsedAttributes = true;
- udas = NULL;
- storage_class = STCundefined;
- link = p->linkage;
- setAlignment = false;
- ealign = NULL;
- p->parseStorageClasses(storage_class, link, setAlignment, ealign, udas);
-}
-
-/**********************************
- * Parse Declarations.
- * These can be:
- * 1. declarations at global/class level
- * 2. declarations at statement level
- * Return array of Declaration *'s.
- */
-
-Dsymbols *Parser::parseDeclarations(bool autodecl, PrefixAttributes *pAttrs, const utf8_t *comment)
-{
- StorageClass storage_class = STCundefined;
- Type *ts;
- Type *t;
- Type *tfirst;
- Identifier *ident;
- TOK tok = TOKreserved;
- LINK link = linkage;
- bool setAlignment = false;
- Expression *ealign = NULL;
- Loc loc = token.loc;
- Expressions *udas = NULL;
- Token *tk;
-
- //printf("parseDeclarations() %s\n", token.toChars());
- if (!comment)
- comment = token.blockComment;
-
- if (autodecl)
- {
- ts = NULL; // infer type
- goto L2;
- }
-
- if (token.value == TOKalias)
- {
- tok = token.value;
- nextToken();
-
- /* Look for:
- * alias identifier this;
- */
- if (token.value == TOKidentifier && peekNext() == TOKthis)
- {
- AliasThis *s = new AliasThis(loc, token.ident);
- nextToken();
- check(TOKthis);
- check(TOKsemicolon);
- Dsymbols *a = new Dsymbols();
- a->push(s);
- addComment(s, comment);
- return a;
- }
- /* Look for:
- * alias identifier = type;
- * alias identifier(...) = type;
- */
- if (token.value == TOKidentifier &&
- skipParensIf(peek(&token), &tk) &&
- tk->value == TOKassign)
- {
- Dsymbols *a = new Dsymbols();
- while (1)
- {
- ident = token.ident;
- nextToken();
- TemplateParameters *tpl = NULL;
- if (token.value == TOKlparen)
- tpl = parseTemplateParameterList();
- check(TOKassign);
-
- bool hasParsedAttributes = false;
- if (token.value == TOKat)
- {
- parseAttributes(this, hasParsedAttributes,
- storage_class, link, setAlignment, ealign, udas);
- }
-
- Declaration *v;
- Dsymbol *s;
-
- // try to parse function type:
- // TypeCtors? BasicType ( Parameters ) MemberFunctionAttributes
- bool attributesAppended = false;
- const StorageClass funcStc = parseTypeCtor();
- Token *tlu = &token;
- if (token.value != TOKfunction &&
- token.value != TOKdelegate &&
- isBasicType(&tlu) && tlu &&
- tlu->value == TOKlparen)
- {
- VarArg vargs;
- Type *tret = parseBasicType();
- Parameters *prms = parseParameters(&vargs);
- ParameterList pl = ParameterList(prms, vargs);
-
- parseAttributes(this, hasParsedAttributes,
- storage_class, link, setAlignment, ealign, udas);
- if (udas)
- error("user-defined attributes not allowed for `alias` declarations");
-
- attributesAppended = true;
- storage_class = appendStorageClass(storage_class, funcStc);
- Type *tf = new TypeFunction(pl, tret, link, storage_class);
- v = new AliasDeclaration(loc, ident, tf);
- }
- else if (token.value == TOKfunction ||
- token.value == TOKdelegate ||
- (token.value == TOKlparen &&
- skipAttributes(peekPastParen(&token), &tk) &&
- (tk->value == TOKgoesto || tk->value == TOKlcurly)) ||
- token.value == TOKlcurly ||
- (token.value == TOKidentifier && peekNext() == TOKgoesto) ||
- (token.value == TOKref && peekNext() == TOKlparen &&
- skipAttributes(peekPastParen(peek(&token)), &tk) &&
- (tk->value == TOKgoesto || tk->value == TOKlcurly)))
- {
- // function (parameters) { statements... }
- // delegate (parameters) { statements... }
- // (parameters) { statements... }
- // (parameters) => expression
- // { statements... }
- // identifier => expression
- // ref (parameters) { statements... }
- // ref (parameters) => expression
-
- s = parseFunctionLiteral();
-
- if (udas != NULL)
- {
- if (storage_class != 0)
- error("Cannot put a storage-class in an alias declaration.");
- // shouldn't have set these variables
- assert(link == linkage && !setAlignment && ealign == NULL);
- TemplateDeclaration *tpl_ = (TemplateDeclaration *) s;
- assert(tpl_ != NULL && tpl_->members->length == 1);
- FuncLiteralDeclaration *fd = (FuncLiteralDeclaration *) (*tpl_->members)[0];
- TypeFunction *tf = (TypeFunction *) fd->type;
- assert(tf->parameterList.length() > 0);
- Dsymbols *as = new Dsymbols();
- (*tf->parameterList.parameters)[0]->userAttribDecl = new UserAttributeDeclaration(udas, as);
- }
- v = new AliasDeclaration(loc, ident, s);
- }
- else
- {
- // StorageClasses type
- parseAttributes(this, hasParsedAttributes,
- storage_class, link, setAlignment, ealign, udas);
- if (udas)
- error("user-defined attributes not allowed for %s declarations", Token::toChars(tok));
-
- t = parseType();
- v = new AliasDeclaration(loc, ident, t);
- }
- if (!attributesAppended)
- storage_class = appendStorageClass(storage_class, funcStc);
- v->storage_class = storage_class;
-
- s = v;
- if (tpl)
- {
- Dsymbols *a2 = new Dsymbols();
- a2->push(s);
- TemplateDeclaration *tempdecl =
- new TemplateDeclaration(loc, ident, tpl, NULL, a2);
- s = tempdecl;
- }
- if (setAlignment)
- {
- Dsymbols *ax = new Dsymbols();
- ax->push(s);
- s = new AlignDeclaration(v->loc, ealign, ax);
- }
- if (link != linkage)
- {
- Dsymbols *a2 = new Dsymbols();
- a2->push(s);
- s = new LinkDeclaration(link, a2);
- }
- a->push(s);
-
- switch (token.value)
- {
- case TOKsemicolon:
- nextToken();
- addComment(s, comment);
- break;
- case TOKcomma:
- nextToken();
- addComment(s, comment);
- if (token.value != TOKidentifier)
- {
- error("identifier expected following comma, not %s", token.toChars());
- break;
- }
- if (peekNext() != TOKassign && peekNext() != TOKlparen)
- {
- error("= expected following identifier");
- nextToken();
- break;
- }
- continue;
- default:
- error("semicolon expected to close %s declaration", Token::toChars(tok));
- break;
- }
- break;
- }
- return a;
- }
-
- // alias StorageClasses type ident;
- }
-
- parseStorageClasses(storage_class, link, setAlignment, ealign, udas);
-
- if (token.value == TOKstruct ||
- token.value == TOKunion ||
- token.value == TOKclass ||
- token.value == TOKinterface)
- {
- Dsymbol *s = parseAggregate();
- Dsymbols *a = new Dsymbols();
- a->push(s);
-
- if (storage_class)
- {
- s = new StorageClassDeclaration(storage_class, a);
- a = new Dsymbols();
- a->push(s);
- }
- if (setAlignment)
- {
- s = new AlignDeclaration(s->loc, ealign, a);
- a = new Dsymbols();
- a->push(s);
- }
- if (link != linkage)
- {
- s = new LinkDeclaration(link, a);
- a = new Dsymbols();
- a->push(s);
- }
- if (udas)
- {
- s = new UserAttributeDeclaration(udas, a);
- a = new Dsymbols();
- a->push(s);
- }
-
- addComment(s, comment);
- return a;
- }
-
- /* Look for auto initializers:
- * storage_class identifier = initializer;
- * storage_class identifier(...) = initializer;
- */
- if ((storage_class || udas) &&
- token.value == TOKidentifier &&
- skipParensIf(peek(&token), &tk) &&
- tk->value == TOKassign)
- {
- Dsymbols *a = parseAutoDeclarations(storage_class, comment);
- if (udas)
- {
- Dsymbol *s = new UserAttributeDeclaration(udas, a);
- a = new Dsymbols();
- a->push(s);
- }
- return a;
- }
-
- /* Look for return type inference for template functions.
- */
- if ((storage_class || udas) && token.value == TOKidentifier && skipParens(peek(&token), &tk) &&
- skipAttributes(tk, &tk) &&
- (tk->value == TOKlparen || tk->value == TOKlcurly || tk->value == TOKin || tk->value == TOKout ||
- tk->value == TOKdo || (tk->value == TOKidentifier && tk->ident == Id::_body)))
- {
- ts = NULL;
- }
- else
- {
- ts = parseBasicType();
- ts = parseBasicType2(ts);
- }
-
-L2:
- tfirst = NULL;
- Dsymbols *a = new Dsymbols();
-
- if (pAttrs)
- {
- storage_class |= pAttrs->storageClass;
- //pAttrs->storageClass = STCundefined;
- }
-
- while (1)
- {
- TemplateParameters *tpl = NULL;
- int disable;
- int alt = 0;
-
- loc = token.loc;
- ident = NULL;
- t = parseDeclarator(ts, &alt, &ident, &tpl, storage_class, &disable, &udas);
- assert(t);
- if (!tfirst)
- tfirst = t;
- else if (t != tfirst)
- error("multiple declarations must have the same type, not %s and %s",
- tfirst->toChars(), t->toChars());
- bool isThis = (t->ty == Tident && ((TypeIdentifier *)t)->ident == Id::This && token.value == TOKassign);
- if (ident)
- checkCstyleTypeSyntax(loc, t, alt, ident);
- else if (!isThis)
- error("no identifier for declarator %s", t->toChars());
-
- if (tok == TOKalias)
- {
- Declaration *v;
- Initializer *init = NULL;
-
- /* Aliases can no longer have multiple declarators, storage classes,
- * linkages, or auto declarations.
- * These never made any sense, anyway.
- * The code below needs to be fixed to reject them.
- * The grammar has already been fixed to preclude them.
- */
-
- if (udas)
- error("user-defined attributes not allowed for %s declarations", Token::toChars(tok));
-
- if (token.value == TOKassign)
- {
- nextToken();
- init = parseInitializer();
- }
- if (init)
- {
- if (isThis)
- error("cannot use syntax `alias this = %s`, use `alias %s this` instead",
- init->toChars(), init->toChars());
- else
- error("alias cannot have initializer");
- }
- v = new AliasDeclaration(loc, ident, t);
-
- v->storage_class = storage_class;
- if (pAttrs)
- {
- /* AliasDeclaration distinguish @safe, @system, @trusted atttributes
- * on prefix and postfix.
- * @safe alias void function() FP1;
- * alias @safe void function() FP2; // FP2 is not @safe
- * alias void function() @safe FP3;
- */
- pAttrs->storageClass &= (STCsafe | STCsystem | STCtrusted);
- }
- Dsymbol *s = v;
-
- if (link != linkage)
- {
- Dsymbols *ax = new Dsymbols();
- ax->push(v);
- s = new LinkDeclaration(link, ax);
- }
- a->push(s);
- switch (token.value)
- {
- case TOKsemicolon:
- nextToken();
- addComment(s, comment);
- break;
-
- case TOKcomma:
- nextToken();
- addComment(s, comment);
- continue;
-
- default:
- error("semicolon expected to close %s declaration", Token::toChars(tok));
- break;
- }
- }
- else if (t->ty == Tfunction)
- {
- Expression *constraint = NULL;
-
- //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t->toChars(), storage_class);
- FuncDeclaration *f =
- new FuncDeclaration(loc, Loc(), ident, storage_class | (disable ? STCdisable : 0), t);
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
- if (tpl)
- constraint = parseConstraint();
- Dsymbol *s = parseContracts(f);
- Identifier *tplIdent = s->ident;
- if (link != linkage)
- {
- Dsymbols *ax = new Dsymbols();
- ax->push(s);
- s = new LinkDeclaration(link, ax);
- }
- if (udas)
- {
- Dsymbols *ax = new Dsymbols();
- ax->push(s);
- s = new UserAttributeDeclaration(udas, ax);
- }
-
- /* A template parameter list means it's a function template
- */
- if (tpl)
- {
- // Wrap a template around the function declaration
- Dsymbols *decldefs = new Dsymbols();
- decldefs->push(s);
- TemplateDeclaration *tempdecl =
- new TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs);
- s = tempdecl;
-
- if (storage_class & STCstatic)
- {
- assert(f->storage_class & STCstatic);
- f->storage_class &= ~STCstatic;
-
- Dsymbols *ax = new Dsymbols();
- ax->push(s);
- s = new StorageClassDeclaration(STCstatic, ax);
- }
- }
- a->push(s);
- addComment(s, comment);
- }
- else if (ident)
- {
- Initializer *init = NULL;
- if (token.value == TOKassign)
- {
- nextToken();
- init = parseInitializer();
- }
-
- VarDeclaration *v = new VarDeclaration(loc, t, ident, init);
- v->storage_class = storage_class;
- if (pAttrs)
- pAttrs->storageClass = STCundefined;
-
- Dsymbol *s = v;
-
- if (tpl && init)
- {
- Dsymbols *a2 = new Dsymbols();
- a2->push(s);
- TemplateDeclaration *tempdecl =
- new TemplateDeclaration(loc, ident, tpl, NULL, a2, 0);
- s = tempdecl;
- }
- if (link != linkage)
- {
- Dsymbols *ax = new Dsymbols();
- ax->push(s);
- s = new LinkDeclaration(link, ax);
- }
- if (udas)
- {
- Dsymbols *ax = new Dsymbols();
- ax->push(s);
- s = new UserAttributeDeclaration(udas, ax);
- }
- a->push(s);
- switch (token.value)
- {
- case TOKsemicolon:
- nextToken();
- addComment(s, comment);
- break;
-
- case TOKcomma:
- nextToken();
- addComment(s, comment);
- continue;
-
- default:
- error("semicolon expected, not `%s`", token.toChars());
- break;
- }
- }
- break;
- }
- return a;
-}
-
-Dsymbol *Parser::parseFunctionLiteral()
-{
- Loc loc = token.loc;
-
- TemplateParameters *tpl = NULL;
- Parameters *parameters = NULL;
- VarArg varargs = VARARGnone;
- Type *tret = NULL;
- StorageClass stc = 0;
- TOK save = TOKreserved;
-
- switch (token.value)
- {
- case TOKfunction:
- case TOKdelegate:
- save = token.value;
- nextToken();
- if (token.value == TOKref)
- {
- // function ref (parameters) { statements... }
- // delegate ref (parameters) { statements... }
- stc = STCref;
- nextToken();
- }
- if (token.value != TOKlparen && token.value != TOKlcurly)
- {
- // function type (parameters) { statements... }
- // delegate type (parameters) { statements... }
- tret = parseBasicType();
- tret = parseBasicType2(tret); // function return type
- }
-
- if (token.value == TOKlparen)
- {
- // function (parameters) { statements... }
- // delegate (parameters) { statements... }
- }
- else
- {
- // function { statements... }
- // delegate { statements... }
- break;
- }
- goto LTOKlparen;
-
- case TOKref:
- // ref (parameters) => expression
- // ref (parameters) { statements... }
- stc = STCref;
- nextToken();
- goto LTOKlparen;
-
- case TOKlparen:
- LTOKlparen:
- {
- // (parameters) => expression
- // (parameters) { statements... }
- parameters = parseParameters(&varargs, &tpl);
- stc = parsePostfix(stc, NULL);
- if (StorageClass modStc = stc & STC_TYPECTOR)
- {
- if (save == TOKfunction)
- {
- OutBuffer buf;
- stcToBuffer(&buf, modStc);
- error("function literal cannot be %s", buf.peekChars());
- }
- else
- save = TOKdelegate;
- }
- break;
- }
- case TOKlcurly:
- // { statements... }
- break;
-
- case TOKidentifier:
- {
- // identifier => expression
- parameters = new Parameters();
- Identifier *id = Identifier::generateId("__T");
- Type *t = new TypeIdentifier(loc, id);
- parameters->push(new Parameter(0, t, token.ident, NULL, NULL));
-
- tpl = new TemplateParameters();
- TemplateParameter *tp = new TemplateTypeParameter(loc, id, NULL, NULL);
- tpl->push(tp);
-
- nextToken();
- break;
- }
- default:
- assert(0);
- }
-
- if (!parameters)
- parameters = new Parameters();
- TypeFunction *tf = new TypeFunction(ParameterList(parameters, varargs),
- tret, linkage, stc);
- tf = (TypeFunction *)tf->addSTC(stc);
- FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, Loc(), tf, save, NULL);
-
- if (token.value == TOKgoesto)
- {
- check(TOKgoesto);
- Loc returnloc = token.loc;
- Expression *ae = parseAssignExp();
- fd->fbody = new ReturnStatement(returnloc, ae);
- fd->endloc = token.loc;
- }
- else
- {
- parseContracts(fd);
- }
-
- if (tpl)
- {
- // Wrap a template around function fd
- Dsymbols *decldefs = new Dsymbols();
- decldefs->push(fd);
- return new TemplateDeclaration(fd->loc, fd->ident, tpl, NULL, decldefs, false, true);
- }
- else
- return fd;
-}
-
-/*****************************************
- * Parse auto declarations of the form:
- * storageClass ident = init, ident = init, ... ;
- * and return the array of them.
- * Starts with token on the first ident.
- * Ends with scanner past closing ';'
- */
-
-Dsymbols *Parser::parseAutoDeclarations(StorageClass storageClass, const utf8_t *comment)
-{
- //printf("parseAutoDeclarations\n");
- Token *tk;
- Dsymbols *a = new Dsymbols;
-
- while (1)
- {
- Loc loc = token.loc;
- Identifier *ident = token.ident;
- nextToken(); // skip over ident
-
- TemplateParameters *tpl = NULL;
- if (token.value == TOKlparen)
- tpl = parseTemplateParameterList();
-
- check(TOKassign); // skip over '='
- Initializer *init = parseInitializer();
- VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init);
- v->storage_class = storageClass;
-
- Dsymbol *s = v;
- if (tpl)
- {
- Dsymbols *a2 = new Dsymbols();
- a2->push(v);
- TemplateDeclaration *tempdecl =
- new TemplateDeclaration(loc, ident, tpl, NULL, a2, 0);
- s = tempdecl;
- }
- a->push(s);
- switch (token.value)
- {
- case TOKsemicolon:
- nextToken();
- addComment(s, comment);
- break;
-
- case TOKcomma:
- nextToken();
- if (!(token.value == TOKidentifier &&
- skipParensIf(peek(&token), &tk) &&
- tk->value == TOKassign))
- {
- error("identifier expected following comma");
- break;
- }
- addComment(s, comment);
- continue;
-
- default:
- error("semicolon expected following auto declaration, not `%s`", token.toChars());
- break;
- }
- break;
- }
- return a;
-}
-
-/*****************************************
- * Parse contracts following function declaration.
- */
-
-FuncDeclaration *Parser::parseContracts(FuncDeclaration *f)
-{
- LINK linksave = linkage;
-
- bool literal = f->isFuncLiteralDeclaration() != NULL;
-
- // The following is irrelevant, as it is overridden by sc->linkage in
- // TypeFunction::semantic
- linkage = LINKd; // nested functions have D linkage
- bool requireDo = false;
-L1:
- switch (token.value)
- {
- case TOKlcurly:
- if (requireDo)
- error("missing body { ... } after in or out");
- f->fbody = parseStatement(PSsemi);
- f->endloc = endloc;
- break;
-
- case TOKidentifier:
- if (token.ident != Id::_body)
- goto Ldefault;
- /* fall through */
-
- case TOKdo:
- nextToken();
- f->fbody = parseStatement(PScurly);
- f->endloc = endloc;
- break;
-
- case TOKin:
- {
- // in { statements... }
- // in (expression)
- Loc loc = token.loc;
- nextToken();
- if (!f->frequires)
- {
- f->frequires = new Statements();
- }
- if (token.value == TOKlparen)
- {
- nextToken();
- Expression *e = parseAssignExp();
- Expression *msg = NULL;
- if (token.value == TOKcomma)
- {
- nextToken();
- if (token.value != TOKrparen)
- {
- msg = parseAssignExp();
- if (token.value == TOKcomma)
- nextToken();
- }
- }
- check(TOKrparen);
- e = new AssertExp(loc, e, msg);
- f->frequires->push(new ExpStatement(loc, e));
- requireDo = false;
- }
- else
- {
- f->frequires->push(parseStatement(PScurly | PSscope));
- requireDo = true;
- }
- goto L1;
- }
-
- case TOKout:
- {
- // out { statements... }
- // out (; expression)
- // out (identifier) { statements... }
- // out (identifier; expression)
- Loc loc = token.loc;
- nextToken();
- if (!f->fensures)
- {
- f->fensures = new Ensures();
- }
- Identifier *id = NULL;
- if (token.value != TOKlcurly)
- {
- check(TOKlparen);
- if (token.value != TOKidentifier && token.value != TOKsemicolon)
- error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
- if (token.value != TOKsemicolon)
- {
- id = token.ident;
- nextToken();
- }
- if (token.value == TOKsemicolon)
- {
- nextToken();
- Expression *e = parseAssignExp();
- Expression *msg = NULL;
- if (token.value == TOKcomma)
- {
- nextToken();
- if (token.value != TOKrparen)
- {
- msg = parseAssignExp();
- if (token.value == TOKcomma)
- nextToken();
- }
- }
- check(TOKrparen);
- e = new AssertExp(loc, e, msg);
- f->fensures->push(Ensure(id, new ExpStatement(loc, e)));
- requireDo = false;
- goto L1;
- }
- check(TOKrparen);
- }
- f->fensures->push(Ensure(id, parseStatement(PScurly | PSscope)));
- requireDo = true;
- goto L1;
- }
-
- case TOKsemicolon:
- if (!literal)
- {
- // Bugzilla 15799: Semicolon becomes a part of function declaration
- // only when 'do' is not required
- if (!requireDo)
- nextToken();
- break;
- }
- /* fall through */
-
- default:
- Ldefault:
- if (literal)
- {
- const char *sbody = requireDo ? "do " : "";
- error("missing %s{ ... } for function literal", sbody);
- }
- else if (!requireDo) // allow these even with no body
- {
- error("semicolon expected following function declaration");
- }
- break;
- }
- if (literal && !f->fbody)
- {
- // Set empty function body for error recovery
- f->fbody = new CompoundStatement(Loc(), (Statement *)NULL);
- }
-
- linkage = linksave;
-
- return f;
-}
-
-/*****************************************
- * Parse initializer for variable declaration.
- */
-
-Initializer *Parser::parseInitializer()
-{
- StructInitializer *is;
- ArrayInitializer *ia;
- ExpInitializer *ie;
- Expression *e;
- Identifier *id;
- Initializer *value;
- int comma;
- Loc loc = token.loc;
- Token *t;
- int braces;
- int brackets;
-
- switch (token.value)
- {
- case TOKlcurly:
- /* Scan ahead to see if it is a struct initializer or
- * a function literal.
- * If it contains a ';', it is a function literal.
- * Treat { } as a struct initializer.
- */
- braces = 1;
- for (t = peek(&token); 1; t = peek(t))
- {
- switch (t->value)
- {
- case TOKsemicolon:
- case TOKreturn:
- goto Lexpression;
-
- case TOKlcurly:
- braces++;
- continue;
-
- case TOKrcurly:
- if (--braces == 0)
- break;
- continue;
-
- case TOKeof:
- break;
-
- default:
- continue;
- }
- break;
- }
-
- is = new StructInitializer(loc);
- nextToken();
- comma = 2;
- while (1)
- {
- switch (token.value)
- {
- case TOKidentifier:
- if (comma == 1)
- error("comma expected separating field initializers");
- t = peek(&token);
- if (t->value == TOKcolon)
- {
- id = token.ident;
- nextToken();
- nextToken(); // skip over ':'
- }
- else
- { id = NULL;
- }
- value = parseInitializer();
- is->addInit(id, value);
- comma = 1;
- continue;
-
- case TOKcomma:
- if (comma == 2)
- error("expression expected, not `,`");
- nextToken();
- comma = 2;
- continue;
-
- case TOKrcurly: // allow trailing comma's
- nextToken();
- break;
-
- case TOKeof:
- error("found EOF instead of initializer");
- break;
-
- default:
- if (comma == 1)
- error("comma expected separating field initializers");
- value = parseInitializer();
- is->addInit(NULL, value);
- comma = 1;
- continue;
- //error("found `%s` instead of field initializer", token.toChars());
- //break;
- }
- break;
- }
- return is;
-
- case TOKlbracket:
- /* Scan ahead to see if it is an array initializer or
- * an expression.
- * If it ends with a ';' ',' or '}', it is an array initializer.
- */
- brackets = 1;
- for (t = peek(&token); 1; t = peek(t))
- {
- switch (t->value)
- {
- case TOKlbracket:
- brackets++;
- continue;
-
- case TOKrbracket:
- if (--brackets == 0)
- { t = peek(t);
- if (t->value != TOKsemicolon &&
- t->value != TOKcomma &&
- t->value != TOKrbracket &&
- t->value != TOKrcurly)
- goto Lexpression;
- break;
- }
- continue;
-
- case TOKeof:
- break;
-
- default:
- continue;
- }
- break;
- }
-
- ia = new ArrayInitializer(loc);
- nextToken();
- comma = 2;
- while (1)
- {
- switch (token.value)
- {
- default:
- if (comma == 1)
- { error("comma expected separating array initializers, not %s", token.toChars());
- nextToken();
- break;
- }
- e = parseAssignExp();
- if (!e)
- break;
- if (token.value == TOKcolon)
- {
- nextToken();
- value = parseInitializer();
- }
- else
- { value = new ExpInitializer(e->loc, e);
- e = NULL;
- }
- ia->addInit(e, value);
- comma = 1;
- continue;
-
- case TOKlcurly:
- case TOKlbracket:
- if (comma == 1)
- error("comma expected separating array initializers, not %s", token.toChars());
- value = parseInitializer();
- if (token.value == TOKcolon)
- {
- nextToken();
- e = initializerToExpression(value);
- value = parseInitializer();
- }
- else
- e = NULL;
- ia->addInit(e, value);
- comma = 1;
- continue;
-
- case TOKcomma:
- if (comma == 2)
- error("expression expected, not `,`");
- nextToken();
- comma = 2;
- continue;
-
- case TOKrbracket: // allow trailing comma's
- nextToken();
- break;
-
- case TOKeof:
- error("found `%s` instead of array initializer", token.toChars());
- break;
- }
- break;
- }
- return ia;
-
- case TOKvoid:
- t = peek(&token);
- if (t->value == TOKsemicolon || t->value == TOKcomma)
- {
- nextToken();
- return new VoidInitializer(loc);
- }
- goto Lexpression;
-
- default:
- Lexpression:
- e = parseAssignExp();
- ie = new ExpInitializer(loc, e);
- return ie;
- }
-}
-
-/*****************************************
- * Parses default argument initializer expression that is an assign expression,
- * with special handling for __FILE__, __FILE_FULL_PATH__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__.
- */
-
-Expression *Parser::parseDefaultInitExp()
-{
- if (token.value == TOKfile ||
- token.value == TOKfilefullpath ||
- token.value == TOKline ||
- token.value == TOKmodulestring ||
- token.value == TOKfuncstring ||
- token.value == TOKprettyfunc)
- {
- Token *t = peek(&token);
- if (t->value == TOKcomma || t->value == TOKrparen)
- {
- Expression *e = NULL;
- if (token.value == TOKfile)
- e = new FileInitExp(token.loc, TOKfile);
- else if (token.value == TOKfilefullpath)
- e = new FileInitExp(token.loc, TOKfilefullpath);
- else if (token.value == TOKline)
- e = new LineInitExp(token.loc);
- else if (token.value == TOKmodulestring)
- e = new ModuleInitExp(token.loc);
- else if (token.value == TOKfuncstring)
- e = new FuncInitExp(token.loc);
- else if (token.value == TOKprettyfunc)
- e = new PrettyFuncInitExp(token.loc);
- else
- assert(0);
- nextToken();
- return e;
- }
- }
-
- Expression *e = parseAssignExp();
- return e;
-}
-
-/*****************************************
- */
-
-void Parser::checkDanglingElse(Loc elseloc)
-{
- if (token.value != TOKelse &&
- token.value != TOKcatch &&
- token.value != TOKfinally &&
- lookingForElse.linnum != 0)
- {
- warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
- }
-}
-
-void Parser::checkCstyleTypeSyntax(Loc loc, Type *t, int alt, Identifier *ident)
-{
- if (!alt)
- return;
-
- const char *sp = !ident ? "" : " ";
- const char *s = !ident ? "" : ident->toChars();
- if (alt & 1) // contains C-style function pointer syntax
- error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t->toChars(), sp, s);
- else
- ::warning(loc, "instead of C-style syntax, use D-style syntax `%s%s%s`", t->toChars(), sp, s);
-
-}
-
-/*****************************************
- * Parses `foreach` statements, `static foreach` statements and
- * `static foreach` declarations. The template parameter
- * `isStatic` is true, iff a `static foreach` should be parsed.
- * If `isStatic` is true, `isDecl` can be true to indicate that a
- * `static foreach` declaration should be parsed.
- */
-Statement *Parser::parseForeach(Loc loc, bool *isRange, bool isDecl)
-{
- TOK op = token.value;
-
- nextToken();
- check(TOKlparen);
-
- Parameters *parameters = new Parameters();
-
- while (1)
- {
- Identifier *ai = NULL;
- Type *at;
-
- StorageClass storageClass = 0;
- StorageClass stc = 0;
- Lagain:
- if (stc)
- {
- storageClass = appendStorageClass(storageClass, stc);
- nextToken();
- }
- switch (token.value)
- {
- case TOKref:
- stc = STCref;
- goto Lagain;
-
- case TOKenum:
- stc = STCmanifest;
- goto Lagain;
-
- case TOKalias:
- storageClass = appendStorageClass(storageClass, STCalias);
- nextToken();
- break;
-
- case TOKconst:
- if (peekNext() != TOKlparen)
- {
- stc = STCconst;
- goto Lagain;
- }
- break;
-
- case TOKimmutable:
- if (peekNext() != TOKlparen)
- {
- stc = STCimmutable;
- goto Lagain;
- }
- break;
-
- case TOKshared:
- if (peekNext() != TOKlparen)
- {
- stc = STCshared;
- goto Lagain;
- }
- break;
-
- case TOKwild:
- if (peekNext() != TOKlparen)
- {
- stc = STCwild;
- goto Lagain;
- }
- break;
-
- default:
- break;
- }
- if (token.value == TOKidentifier)
- {
- Token *t = peek(&token);
- if (t->value == TOKcomma || t->value == TOKsemicolon)
- { ai = token.ident;
- at = NULL; // infer argument type
- nextToken();
- goto Larg;
- }
- }
- at = parseType(&ai);
- if (!ai)
- error("no identifier for declarator %s", at->toChars());
- Larg:
- Parameter *p = new Parameter(storageClass, at, ai, NULL, NULL);
- parameters->push(p);
- if (token.value == TOKcomma)
- { nextToken();
- continue;
- }
- break;
- }
- check(TOKsemicolon);
-
- Expression *aggr = parseExpression();
- if (token.value == TOKslice && parameters->length == 1)
- {
- Parameter *p = (*parameters)[0];
- delete parameters;
- nextToken();
- Expression *upr = parseExpression();
- check(TOKrparen);
- Loc endloc;
- Statement *body = (!isDecl) ? parseStatement(0, NULL, &endloc) : NULL;
- if (isRange)
- *isRange = true;
- return new ForeachRangeStatement(loc, op, p, aggr, upr, body, endloc);
- }
- else
- {
- check(TOKrparen);
- Loc endloc;
- Statement *body = (!isDecl) ? parseStatement(0, NULL, &endloc) : NULL;
- if (isRange)
- *isRange = false;
- return new ForeachStatement(loc, op, parameters, aggr, body, endloc);
- }
-}
-
-Dsymbol *Parser::parseForeachStaticDecl(Loc loc, Dsymbol **pLastDecl)
-{
- nextToken();
-
- bool isRange = false;
- Statement *s = parseForeach(loc, &isRange, true);
-
- return new StaticForeachDeclaration(
- new StaticForeach(loc, isRange ? NULL : (ForeachStatement *)s,
- isRange ? (ForeachRangeStatement *)s : NULL),
- parseBlock(pLastDecl)
- );
-}
-
-Statement *Parser::parseForeachStatic(Loc loc)
-{
- nextToken();
-
- bool isRange = false;
- Statement *s = parseForeach(loc, &isRange, false);
-
- return new StaticForeachStatement(loc,
- new StaticForeach(loc, isRange ? NULL : (ForeachStatement *)s,
- isRange ? (ForeachRangeStatement *)s : NULL)
- );
-}
-
-/*****************************************
- * Input:
- * flags PSxxxx
- * Output:
- * pEndloc if { ... statements ... }, store location of closing brace, otherwise loc of first token of next statement
- */
-
-Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc)
-{
- Statement *s = NULL;
- Condition *cond;
- Statement *ifbody;
- Statement *elsebody;
- bool isfinal;
- Loc loc = token.loc;
-
- //printf("parseStatement()\n");
-
- if (flags & PScurly && token.value != TOKlcurly)
- error("statement expected to be { }, not %s", token.toChars());
-
- switch (token.value)
- {
- case TOKidentifier:
- { /* A leading identifier can be a declaration, label, or expression.
- * The easiest case to check first is label:
- */
- Token *t = peek(&token);
- if (t->value == TOKcolon)
- {
- Token *nt = peek(t);
- if (nt->value == TOKcolon)
- {
- // skip ident::
- nextToken();
- nextToken();
- nextToken();
- error("use `.` for member lookup, not `::`");
- break;
- }
- // It's a label
- Identifier *ident = token.ident;
- nextToken();
- nextToken();
- if (token.value == TOKrcurly)
- s = NULL;
- else if (token.value == TOKlcurly)
- s = parseStatement(PScurly | PSscope);
- else
- s = parseStatement(PSsemi_ok);
- s = new LabelStatement(loc, ident, s);
- break;
- }
- }
- /* fall through */
- case TOKdot:
- case TOKtypeof:
- case TOKvector:
- case TOKtraits:
- /* Bugzilla 15163: If tokens can be handled as
- * old C-style declaration or D expression, prefer the latter.
- */
- if (isDeclaration(&token, 3, TOKreserved, NULL))
- goto Ldeclaration;
- else
- goto Lexp;
- break;
-
- case TOKassert:
- case TOKthis:
- case TOKsuper:
- case TOKint32v:
- case TOKuns32v:
- case TOKint64v:
- case TOKuns64v:
- case TOKint128v:
- case TOKuns128v:
- case TOKfloat32v:
- case TOKfloat64v:
- case TOKfloat80v:
- case TOKimaginary32v:
- case TOKimaginary64v:
- case TOKimaginary80v:
- case TOKcharv:
- case TOKwcharv:
- case TOKdcharv:
- case TOKnull:
- case TOKtrue:
- case TOKfalse:
- case TOKstring:
- case TOKxstring:
- case TOKlparen:
- case TOKcast:
- case TOKmul:
- case TOKmin:
- case TOKadd:
- case TOKtilde:
- case TOKnot:
- case TOKplusplus:
- case TOKminusminus:
- case TOKnew:
- case TOKdelete:
- case TOKdelegate:
- case TOKfunction:
- case TOKtypeid:
- case TOKis:
- case TOKlbracket:
- case TOKfile:
- case TOKfilefullpath:
- case TOKline:
- case TOKmodulestring:
- case TOKfuncstring:
- case TOKprettyfunc:
- Lexp:
- {
- Expression *exp = parseExpression();
- check(TOKsemicolon, "statement");
- s = new ExpStatement(loc, exp);
- break;
- }
-
- case TOKstatic:
- { // Look ahead to see if it's static assert() or static if()
-
- Token *t = peek(&token);
- if (t->value == TOKassert)
- {
- s = new StaticAssertStatement(parseStaticAssert());
- break;
- }
- if (t->value == TOKif)
- {
- cond = parseStaticIfCondition();
- goto Lcondition;
- }
- else if (t->value == TOKforeach || t->value == TOKforeach_reverse)
- {
- s = parseForeachStatic(loc);
- if (flags & PSscope)
- s = new ScopeStatement(loc, s, token.loc);
- break;
- }
- if (t->value == TOKimport)
- {
- Dsymbols *imports = parseImport();
- s = new ImportStatement(loc, imports);
- if (flags & PSscope)
- s = new ScopeStatement(loc, s, token.loc);
- break;
- }
- goto Ldeclaration;
- }
-
- case TOKfinal:
- if (peekNext() == TOKswitch)
- {
- nextToken();
- isfinal = true;
- goto Lswitch;
- }
- goto Ldeclaration;
-
- case TOKwchar: case TOKdchar:
- case TOKbool: case TOKchar:
- case TOKint8: case TOKuns8:
- case TOKint16: case TOKuns16:
- case TOKint32: case TOKuns32:
- case TOKint64: case TOKuns64:
- case TOKint128: case TOKuns128:
- case TOKfloat32: case TOKfloat64: case TOKfloat80:
- case TOKimaginary32: case TOKimaginary64: case TOKimaginary80:
- case TOKcomplex32: case TOKcomplex64: case TOKcomplex80:
- case TOKvoid:
- // bug 7773: int.max is always a part of expression
- if (peekNext() == TOKdot)
- goto Lexp;
- if (peekNext() == TOKlparen)
- goto Lexp;
- /* fall through */
-
- case TOKalias:
- case TOKconst:
- case TOKauto:
- case TOKabstract:
- case TOKextern:
- case TOKalign:
- case TOKimmutable:
- case TOKshared:
- case TOKwild:
- case TOKdeprecated:
- case TOKnothrow:
- case TOKpure:
- case TOKref:
- case TOKgshared:
- case TOKat:
- case TOKstruct:
- case TOKunion:
- case TOKclass:
- case TOKinterface:
- Ldeclaration:
- {
- Dsymbols *a = parseDeclarations(false, NULL, NULL);
- if (a->length > 1)
- {
- Statements *as = new Statements();
- as->reserve(a->length);
- for (size_t i = 0; i < a->length; i++)
- {
- Dsymbol *d = (*a)[i];
- s = new ExpStatement(loc, d);
- as->push(s);
- }
- s = new CompoundDeclarationStatement(loc, as);
- }
- else if (a->length == 1)
- {
- Dsymbol *d = (*a)[0];
- s = new ExpStatement(loc, d);
- }
- else
- s = new ExpStatement(loc, (Expression *)NULL);
- if (flags & PSscope)
- s = new ScopeStatement(loc, s, token.loc);
- break;
- }
-
- case TOKenum:
- { /* Determine if this is a manifest constant declaration,
- * or a conventional enum.
- */
- Dsymbol *d;
- Token *t = peek(&token);
- if (t->value == TOKlcurly || t->value == TOKcolon)
- d = parseEnum();
- else if (t->value != TOKidentifier)
- goto Ldeclaration;
- else
- {
- t = peek(t);
- if (t->value == TOKlcurly || t->value == TOKcolon ||
- t->value == TOKsemicolon)
- d = parseEnum();
- else
- goto Ldeclaration;
- }
- s = new ExpStatement(loc, d);
- if (flags & PSscope)
- s = new ScopeStatement(loc, s, token.loc);
- break;
- }
-
- case TOKmixin:
- {
- if (isDeclaration(&token, 3, TOKreserved, NULL))
- goto Ldeclaration;
- Token *t = peek(&token);
- if (t->value == TOKlparen)
- {
- // mixin(string)
- Expression *e = parseAssignExp();
- check(TOKsemicolon);
- if (e->op == TOKmixin)
- {
- CompileExp *cpe = (CompileExp *)e;
- s = new CompileStatement(loc, cpe->exps);
- }
- else
- {
- s = new ExpStatement(loc, e);
- }
- break;
- }
- Dsymbol *d = parseMixin();
- s = new ExpStatement(loc, d);
- if (flags & PSscope)
- s = new ScopeStatement(loc, s, token.loc);
- break;
- }
-
- case TOKlcurly:
- {
- Loc lookingForElseSave = lookingForElse;
- lookingForElse = Loc();
-
- nextToken();
- //if (token.value == TOKsemicolon)
- //error("use `{ }` for an empty statement, not a `;`");
- Statements *statements = new Statements();
- while (token.value != TOKrcurly && token.value != TOKeof)
- {
- statements->push(parseStatement(PSsemi | PScurlyscope));
- }
- if (endPtr) *endPtr = token.ptr;
- endloc = token.loc;
- if (pEndloc)
- {
- *pEndloc = token.loc;
- pEndloc = NULL; // don't set it again
- }
- s = new CompoundStatement(loc, statements);
- if (flags & (PSscope | PScurlyscope))
- s = new ScopeStatement(loc, s, token.loc);
- check(TOKrcurly, "compound statement");
- lookingForElse = lookingForElseSave;
- break;
- }
-
- case TOKwhile:
- {
- nextToken();
- check(TOKlparen);
- Expression *condition = parseExpression();
- check(TOKrparen);
- Loc endloc;
- Statement *body = parseStatement(PSscope, NULL, &endloc);
- s = new WhileStatement(loc, condition, body, endloc);
- break;
- }
-
- case TOKsemicolon:
- if (!(flags & PSsemi_ok))
- {
- if (flags & PSsemi)
- deprecation("use `{ }` for an empty statement, not a `;`");
- else
- error("use `{ }` for an empty statement, not a `;`");
- }
- nextToken();
- s = new ExpStatement(loc, (Expression *)NULL);
- break;
-
- case TOKdo:
- { Statement *body;
- Expression *condition;
-
- nextToken();
- Loc lookingForElseSave = lookingForElse;
- lookingForElse = Loc();
- body = parseStatement(PSscope);
- lookingForElse = lookingForElseSave;
- check(TOKwhile);
- check(TOKlparen);
- condition = parseExpression();
- check(TOKrparen);
- if (token.value == TOKsemicolon)
- nextToken();
- else
- error("terminating `;` required after do-while statement");
- s = new DoStatement(loc, body, condition, token.loc);
- break;
- }
-
- case TOKfor:
- {
- Statement *init;
- Expression *condition;
- Expression *increment;
-
- nextToken();
- check(TOKlparen);
- if (token.value == TOKsemicolon)
- { init = NULL;
- nextToken();
- }
- else
- {
- Loc lookingForElseSave = lookingForElse;
- lookingForElse = Loc();
- init = parseStatement(0);
- lookingForElse = lookingForElseSave;
- }
- if (token.value == TOKsemicolon)
- {
- condition = NULL;
- nextToken();
- }
- else
- {
- condition = parseExpression();
- check(TOKsemicolon, "for condition");
- }
- if (token.value == TOKrparen)
- { increment = NULL;
- nextToken();
- }
- else
- { increment = parseExpression();
- check(TOKrparen);
- }
- Loc endloc;
- Statement *body = parseStatement(PSscope, NULL, &endloc);
- s = new ForStatement(loc, init, condition, increment, body, endloc);
- break;
- }
-
- case TOKforeach:
- case TOKforeach_reverse:
- {
- s = parseForeach(loc, NULL, false);
- break;
- }
-
- case TOKif:
- {
- Parameter *param = NULL;
- Expression *condition;
-
- nextToken();
- check(TOKlparen);
-
- StorageClass storageClass = 0;
- StorageClass stc = 0;
- LagainStc:
- if (stc)
- {
- storageClass = appendStorageClass(storageClass, stc);
- nextToken();
- }
- switch (token.value)
- {
- case TOKref:
- stc = STCref;
- goto LagainStc;
- case TOKauto:
- stc = STCauto;
- goto LagainStc;
- case TOKconst:
- if (peekNext() != TOKlparen)
- {
- stc = STCconst;
- goto LagainStc;
- }
- break;
- case TOKimmutable:
- if (peekNext() != TOKlparen)
- {
- stc = STCimmutable;
- goto LagainStc;
- }
- break;
- case TOKshared:
- if (peekNext() != TOKlparen)
- {
- stc = STCshared;
- goto LagainStc;
- }
- break;
- case TOKwild:
- if (peekNext() != TOKlparen)
- {
- stc = STCwild;
- goto LagainStc;
- }
- break;
- default:
- break;
- }
-
- if (storageClass != 0 &&
- token.value == TOKidentifier &&
- peek(&token)->value == TOKassign)
- {
- Identifier *ai = token.ident;
- Type *at = NULL; // infer parameter type
- nextToken();
- check(TOKassign);
- param = new Parameter(storageClass, at, ai, NULL, NULL);
- }
- else if (isDeclaration(&token, 2, TOKassign, NULL))
- {
- Identifier *ai;
- Type *at = parseType(&ai);
- check(TOKassign);
- param = new Parameter(storageClass, at, ai, NULL, NULL);
- }
-
- condition = parseExpression();
- check(TOKrparen);
- {
- Loc lookingForElseSave = lookingForElse;
- lookingForElse = loc;
- ifbody = parseStatement(PSscope);
- lookingForElse = lookingForElseSave;
- }
- if (token.value == TOKelse)
- {
- Loc elseloc = token.loc;
- nextToken();
- elsebody = parseStatement(PSscope);
- checkDanglingElse(elseloc);
- }
- else
- elsebody = NULL;
- if (condition && ifbody)
- s = new IfStatement(loc, param, condition, ifbody, elsebody, token.loc);
- else
- s = NULL; // don't propagate parsing errors
- break;
- }
-
- case TOKscope:
- if (peek(&token)->value != TOKlparen)
- goto Ldeclaration; // scope used as storage class
- nextToken();
- check(TOKlparen);
- if (token.value != TOKidentifier)
- { error("scope identifier expected");
- goto Lerror;
- }
- else
- { TOK t = TOKon_scope_exit;
- Identifier *id = token.ident;
-
- if (id == Id::exit)
- t = TOKon_scope_exit;
- else if (id == Id::failure)
- t = TOKon_scope_failure;
- else if (id == Id::success)
- t = TOKon_scope_success;
- else
- error("valid scope identifiers are exit, failure, or success, not %s", id->toChars());
- nextToken();
- check(TOKrparen);
- Statement *st = parseStatement(PSscope);
- s = new ScopeGuardStatement(loc, t, st);
- break;
- }
-
- case TOKdebug:
- nextToken();
- if (token.value == TOKassign)
- {
- error("debug conditions can only be declared at module scope");
- nextToken();
- nextToken();
- goto Lerror;
- }
- cond = parseDebugCondition();
- goto Lcondition;
-
- case TOKversion:
- nextToken();
- if (token.value == TOKassign)
- {
- error("version conditions can only be declared at module scope");
- nextToken();
- nextToken();
- goto Lerror;
- }
- cond = parseVersionCondition();
- goto Lcondition;
-
- Lcondition:
- {
- Loc lookingForElseSave = lookingForElse;
- lookingForElse = loc;
- ifbody = parseStatement(0);
- lookingForElse = lookingForElseSave;
- }
- elsebody = NULL;
- if (token.value == TOKelse)
- {
- Loc elseloc = token.loc;
- nextToken();
- elsebody = parseStatement(0);
- checkDanglingElse(elseloc);
- }
- s = new ConditionalStatement(loc, cond, ifbody, elsebody);
- if (flags & PSscope)
- s = new ScopeStatement(loc, s, token.loc);
- break;
-
- case TOKpragma:
- { Identifier *ident;
- Expressions *args = NULL;
- Statement *body;
-
- nextToken();
- check(TOKlparen);
- if (token.value != TOKidentifier)
- { error("pragma(identifier expected");
- goto Lerror;
- }
- ident = token.ident;
- nextToken();
- if (token.value == TOKcomma && peekNext() != TOKrparen)
- args = parseArguments(); // pragma(identifier, args...);
- else
- check(TOKrparen); // pragma(identifier);
- if (token.value == TOKsemicolon)
- { nextToken();
- body = NULL;
- }
- else
- body = parseStatement(PSsemi);
- s = new PragmaStatement(loc, ident, args, body);
- break;
- }
-
- case TOKswitch:
- isfinal = false;
- goto Lswitch;
-
- Lswitch:
- {
- nextToken();
- check(TOKlparen);
- Expression *condition = parseExpression();
- check(TOKrparen);
- Statement *body = parseStatement(PSscope);
- s = new SwitchStatement(loc, condition, body, isfinal);
- break;
- }
-
- case TOKcase:
- { Expression *exp;
- Expressions cases; // array of Expression's
- Expression *last = NULL;
-
- while (1)
- {
- nextToken();
- exp = parseAssignExp();
- cases.push(exp);
- if (token.value != TOKcomma)
- break;
- }
- check(TOKcolon);
-
- /* case exp: .. case last:
- */
- if (token.value == TOKslice)
- {
- if (cases.length > 1)
- error("only one case allowed for start of case range");
- nextToken();
- check(TOKcase);
- last = parseAssignExp();
- check(TOKcolon);
- }
-
- if (flags & PScurlyscope)
- {
- Statements *statements = new Statements();
- while (token.value != TOKcase &&
- token.value != TOKdefault &&
- token.value != TOKeof &&
- token.value != TOKrcurly)
- {
- statements->push(parseStatement(PSsemi | PScurlyscope));
- }
- s = new CompoundStatement(loc, statements);
- }
- else
- s = parseStatement(PSsemi | PScurlyscope);
- s = new ScopeStatement(loc, s, token.loc);
-
- if (last)
- {
- s = new CaseRangeStatement(loc, exp, last, s);
- }
- else
- {
- // Keep cases in order by building the case statements backwards
- for (size_t i = cases.length; i; i--)
- {
- exp = cases[i - 1];
- s = new CaseStatement(loc, exp, s);
- }
- }
- break;
- }
-
- case TOKdefault:
- {
- nextToken();
- check(TOKcolon);
-
- if (flags & PScurlyscope)
- {
- Statements *statements = new Statements();
- while (token.value != TOKcase &&
- token.value != TOKdefault &&
- token.value != TOKeof &&
- token.value != TOKrcurly)
- {
- statements->push(parseStatement(PSsemi | PScurlyscope));
- }
- s = new CompoundStatement(loc, statements);
- }
- else
- s = parseStatement(PSsemi | PScurlyscope);
- s = new ScopeStatement(loc, s, token.loc);
- s = new DefaultStatement(loc, s);
- break;
- }
-
- case TOKreturn:
- { Expression *exp;
-
- nextToken();
- if (token.value == TOKsemicolon)
- exp = NULL;
- else
- exp = parseExpression();
- check(TOKsemicolon, "return statement");
- s = new ReturnStatement(loc, exp);
- break;
- }
-
- case TOKbreak:
- { Identifier *ident;
-
- nextToken();
- if (token.value == TOKidentifier)
- { ident = token.ident;
- nextToken();
- }
- else
- ident = NULL;
- check(TOKsemicolon, "break statement");
- s = new BreakStatement(loc, ident);
- break;
- }
-
- case TOKcontinue:
- { Identifier *ident;
-
- nextToken();
- if (token.value == TOKidentifier)
- { ident = token.ident;
- nextToken();
- }
- else
- ident = NULL;
- check(TOKsemicolon, "continue statement");
- s = new ContinueStatement(loc, ident);
- break;
- }
-
- case TOKgoto:
- { Identifier *ident;
-
- nextToken();
- if (token.value == TOKdefault)
- {
- nextToken();
- s = new GotoDefaultStatement(loc);
- }
- else if (token.value == TOKcase)
- {
- Expression *exp = NULL;
-
- nextToken();
- if (token.value != TOKsemicolon)
- exp = parseExpression();
- s = new GotoCaseStatement(loc, exp);
- }
- else
- {
- if (token.value != TOKidentifier)
- {
- error("identifier expected following goto");
- ident = NULL;
- }
- else
- {
- ident = token.ident;
- nextToken();
- }
- s = new GotoStatement(loc, ident);
- }
- check(TOKsemicolon, "goto statement");
- break;
- }
-
- case TOKsynchronized:
- { Expression *exp;
- Statement *body;
-
- Token *t = peek(&token);
- if (skipAttributes(t, &t) && t->value == TOKclass)
- goto Ldeclaration;
-
- nextToken();
- if (token.value == TOKlparen)
- {
- nextToken();
- exp = parseExpression();
- check(TOKrparen);
- }
- else
- exp = NULL;
- body = parseStatement(PSscope);
- s = new SynchronizedStatement(loc, exp, body);
- break;
- }
-
- case TOKwith:
- { Expression *exp;
- Statement *body;
-
- nextToken();
- check(TOKlparen);
- exp = parseExpression();
- check(TOKrparen);
- body = parseStatement(PSscope);
- s = new WithStatement(loc, exp, body, token.loc);
- break;
- }
-
- case TOKtry:
- { Statement *body;
- Catches *catches = NULL;
- Statement *finalbody = NULL;
-
- nextToken();
- Loc lookingForElseSave = lookingForElse;
- lookingForElse = Loc();
- body = parseStatement(PSscope);
- lookingForElse = lookingForElseSave;
- while (token.value == TOKcatch)
- {
- Statement *handler;
- Catch *c;
- Type *t;
- Identifier *id;
- Loc catchloc = token.loc;
-
- nextToken();
- if (token.value == TOKlcurly || token.value != TOKlparen)
- {
- t = NULL;
- id = NULL;
- }
- else
- {
- check(TOKlparen);
- id = NULL;
- t = parseType(&id);
- check(TOKrparen);
- }
- handler = parseStatement(0);
- c = new Catch(catchloc, t, id, handler);
- if (!catches)
- catches = new Catches();
- catches->push(c);
- }
-
- if (token.value == TOKfinally)
- {
- nextToken();
- finalbody = parseStatement(PSscope);
- }
-
- s = body;
- if (!catches && !finalbody)
- error("catch or finally expected following try");
- else
- { if (catches)
- s = new TryCatchStatement(loc, body, catches);
- if (finalbody)
- s = new TryFinallyStatement(loc, s, finalbody);
- }
- break;
- }
-
- case TOKthrow:
- { Expression *exp;
-
- nextToken();
- exp = parseExpression();
- check(TOKsemicolon, "throw statement");
- s = new ThrowStatement(loc, exp);
- break;
- }
-
- case TOKasm:
- {
- // Parse the asm block into a sequence of AsmStatements,
- // each AsmStatement is one instruction.
- // Separate out labels.
- // Defer parsing of AsmStatements until semantic processing.
-
- Loc labelloc;
-
- nextToken();
- StorageClass stc = parsePostfix(STCundefined, NULL);
- if (stc & (STCconst | STCimmutable | STCshared | STCwild))
- error("const/immutable/shared/inout attributes are not allowed on asm blocks");
-
- check(TOKlcurly);
- Token *toklist = NULL;
- Token **ptoklist = &toklist;
- Identifier *label = NULL;
- Statements *statements = new Statements();
- size_t nestlevel = 0;
- while (1)
- {
- switch (token.value)
- {
- case TOKidentifier:
- if (!toklist)
- {
- // Look ahead to see if it is a label
- Token *t = peek(&token);
- if (t->value == TOKcolon)
- { // It's a label
- label = token.ident;
- labelloc = token.loc;
- nextToken();
- nextToken();
- continue;
- }
- }
- goto Ldefault;
-
- case TOKlcurly:
- ++nestlevel;
- goto Ldefault;
-
- case TOKrcurly:
- if (nestlevel > 0)
- {
- --nestlevel;
- goto Ldefault;
- }
-
- if (toklist || label)
- {
- error("asm statements must end in `;`");
- }
- break;
-
- case TOKsemicolon:
- if (nestlevel != 0)
- error("mismatched number of curly brackets");
-
- s = NULL;
- if (toklist || label)
- {
- // Create AsmStatement from list of tokens we've saved
- s = new AsmStatement(token.loc, toklist);
- toklist = NULL;
- ptoklist = &toklist;
- if (label)
- { s = new LabelStatement(labelloc, label, s);
- label = NULL;
- }
- statements->push(s);
- }
- nextToken();
- continue;
-
- case TOKeof:
- /* { */
- error("matching `}` expected, not end of file");
- goto Lerror;
- /* fall through */
-
- default:
- Ldefault:
- *ptoklist = Token::alloc();
- memcpy(*ptoklist, &token, sizeof(Token));
- ptoklist = &(*ptoklist)->next;
- *ptoklist = NULL;
-
- nextToken();
- continue;
- }
- break;
- }
- s = new CompoundAsmStatement(loc, statements, stc);
- nextToken();
- break;
- }
-
- case TOKimport:
- {
- Dsymbols *imports = parseImport();
- s = new ImportStatement(loc, imports);
- if (flags & PSscope)
- s = new ScopeStatement(loc, s, token.loc);
- break;
- }
-
- case TOKtemplate:
- {
- Dsymbol *d = parseTemplateDeclaration();
- s = new ExpStatement(loc, d);
- break;
- }
-
- default:
- error("found `%s` instead of statement", token.toChars());
- goto Lerror;
-
- Lerror:
- while (token.value != TOKrcurly &&
- token.value != TOKsemicolon &&
- token.value != TOKeof)
- nextToken();
- if (token.value == TOKsemicolon)
- nextToken();
- s = NULL;
- break;
- }
- if (pEndloc)
- *pEndloc = token.loc;
- return s;
-}
-
-void Parser::check(TOK value)
-{
- check(token.loc, value);
-}
-
-void Parser::check(Loc loc, TOK value)
-{
- if (token.value != value)
- error(loc, "found `%s` when expecting `%s`", token.toChars(), Token::toChars(value));
- nextToken();
-}
-
-void Parser::check(TOK value, const char *string)
-{
- if (token.value != value)
- error("found `%s` when expecting `%s` following %s",
- token.toChars(), Token::toChars(value), string);
- nextToken();
-}
-
-void Parser::checkParens(TOK value, Expression *e)
-{
- if (precedence[e->op] == PREC_rel && !e->parens)
- error(e->loc, "%s must be parenthesized when next to operator %s", e->toChars(), Token::toChars(value));
-}
-
-/************************************
- * Determine if the scanner is sitting on the start of a declaration.
- * Input:
- * needId 0 no identifier
- * 1 identifier optional
- * 2 must have identifier
- * 3 must have identifier, but don't recognize old C-style syntax.
- * Output:
- * if *pt is not NULL, it is set to the ending token, which would be endtok
- */
-
-bool Parser::isDeclaration(Token *t, int needId, TOK endtok, Token **pt)
-{
- //printf("isDeclaration(needId = %d)\n", needId);
- int haveId = 0;
- int haveTpl = 0;
-
- while (1)
- {
- if ((t->value == TOKconst ||
- t->value == TOKimmutable ||
- t->value == TOKwild ||
- t->value == TOKshared) &&
- peek(t)->value != TOKlparen)
- {
- /* const type
- * immutable type
- * shared type
- * wild type
- */
- t = peek(t);
- continue;
- }
- break;
- }
-
- if (!isBasicType(&t))
- {
- goto Lisnot;
- }
- if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != 3))
- goto Lisnot;
- if ((needId == 0 && !haveId) ||
- (needId == 1) ||
- (needId == 2 && haveId) ||
- (needId == 3 && haveId))
- {
- if (pt)
- *pt = t;
- goto Lis;
- }
- else
- goto Lisnot;
-
-Lis:
- //printf("\tis declaration, t = %s\n", t->toChars());
- return true;
-
-Lisnot:
- //printf("\tis not declaration\n");
- return false;
-}
-
-bool Parser::isBasicType(Token **pt)
-{
- // This code parallels parseBasicType()
- Token *t = *pt;
-
- switch (t->value)
- {
- case TOKwchar: case TOKdchar:
- case TOKbool: case TOKchar:
- case TOKint8: case TOKuns8:
- case TOKint16: case TOKuns16:
- case TOKint32: case TOKuns32:
- case TOKint64: case TOKuns64:
- case TOKint128: case TOKuns128:
- case TOKfloat32: case TOKfloat64: case TOKfloat80:
- case TOKimaginary32: case TOKimaginary64: case TOKimaginary80:
- case TOKcomplex32: case TOKcomplex64: case TOKcomplex80:
- case TOKvoid:
- t = peek(t);
- break;
-
- case TOKidentifier:
- L5:
- t = peek(t);
- if (t->value == TOKnot)
- {
- goto L4;
- }
- goto L3;
- while (1)
- {
- L2:
- t = peek(t);
- L3:
- if (t->value == TOKdot)
- {
- Ldot:
- t = peek(t);
- if (t->value != TOKidentifier)
- goto Lfalse;
- t = peek(t);
- if (t->value != TOKnot)
- goto L3;
- L4:
- /* Seen a !
- * Look for:
- * !( args ), !identifier, etc.
- */
- t = peek(t);
- switch (t->value)
- {
- case TOKidentifier:
- goto L5;
- case TOKlparen:
- if (!skipParens(t, &t))
- goto Lfalse;
- goto L3;
- case TOKwchar: case TOKdchar:
- case TOKbool: case TOKchar:
- case TOKint8: case TOKuns8:
- case TOKint16: case TOKuns16:
- case TOKint32: case TOKuns32:
- case TOKint64: case TOKuns64:
- case TOKint128: case TOKuns128:
- case TOKfloat32: case TOKfloat64: case TOKfloat80:
- case TOKimaginary32: case TOKimaginary64: case TOKimaginary80:
- case TOKcomplex32: case TOKcomplex64: case TOKcomplex80:
- case TOKvoid:
- case TOKint32v:
- case TOKuns32v:
- case TOKint64v:
- case TOKuns64v:
- case TOKint128v:
- case TOKuns128v:
- case TOKfloat32v:
- case TOKfloat64v:
- case TOKfloat80v:
- case TOKimaginary32v:
- case TOKimaginary64v:
- case TOKimaginary80v:
- case TOKnull:
- case TOKtrue:
- case TOKfalse:
- case TOKcharv:
- case TOKwcharv:
- case TOKdcharv:
- case TOKstring:
- case TOKxstring:
- case TOKfile:
- case TOKfilefullpath:
- case TOKline:
- case TOKmodulestring:
- case TOKfuncstring:
- case TOKprettyfunc:
- goto L2;
- default:
- goto Lfalse;
- }
- }
- else
- break;
- }
- break;
-
- case TOKdot:
- goto Ldot;
-
- case TOKtypeof:
- case TOKvector:
- case TOKmixin:
- /* typeof(exp).identifier...
- */
- t = peek(t);
- if (!skipParens(t, &t))
- goto Lfalse;
- goto L3;
-
- case TOKtraits:
- {
- // __traits(getMember
- t = peek(t);
- if (t->value != TOKlparen)
- goto Lfalse;
- Token *lp = t;
- t = peek(t);
- if (t->value != TOKidentifier || t->ident != Id::getMember)
- goto Lfalse;
- if (!skipParens(lp, &lp))
- goto Lfalse;
- // we are in a lookup for decl VS statement
- // so we expect a declarator following __trait if it's a type.
- // other usages wont be ambiguous (alias, template instance, type qual, etc.)
- if (lp->value != TOKidentifier)
- goto Lfalse;
-
- break;
- }
-
- case TOKconst:
- case TOKimmutable:
- case TOKshared:
- case TOKwild:
- // const(type) or immutable(type) or shared(type) or wild(type)
- t = peek(t);
- if (t->value != TOKlparen)
- goto Lfalse;
- t = peek(t);
- if (!isDeclaration(t, 0, TOKrparen, &t))
- {
- goto Lfalse;
- }
- t = peek(t);
- break;
-
- default:
- goto Lfalse;
- }
- *pt = t;
- //printf("is\n");
- return true;
-
-Lfalse:
- //printf("is not\n");
- return false;
-}
-
-bool Parser::isDeclarator(Token **pt, int *haveId, int *haveTpl, TOK endtok, bool allowAltSyntax)
-{ // This code parallels parseDeclarator()
- Token *t = *pt;
- int parens;
-
- //printf("Parser::isDeclarator() %s\n", t->toChars());
- if (t->value == TOKassign)
- return false;
-
- while (1)
- {
- parens = false;
- switch (t->value)
- {
- case TOKmul:
- //case TOKand:
- t = peek(t);
- continue;
-
- case TOKlbracket:
- t = peek(t);
- if (t->value == TOKrbracket)
- {
- t = peek(t);
- }
- else if (isDeclaration(t, 0, TOKrbracket, &t))
- {
- // It's an associative array declaration
- t = peek(t);
-
- // ...[type].ident
- if (t->value == TOKdot && peek(t)->value == TOKidentifier)
- {
- t = peek(t);
- t = peek(t);
- }
- }
- else
- {
- // [ expression ]
- // [ expression .. expression ]
- if (!isExpression(&t))
- return false;
- if (t->value == TOKslice)
- {
- t = peek(t);
- if (!isExpression(&t))
- return false;
- if (t->value != TOKrbracket)
- return false;
- t = peek(t);
- }
- else
- {
- if (t->value != TOKrbracket)
- return false;
- t = peek(t);
-
- // ...[index].ident
- if (t->value == TOKdot && peek(t)->value == TOKidentifier)
- {
- t = peek(t);
- t = peek(t);
- }
- }
- }
- continue;
-
- case TOKidentifier:
- if (*haveId)
- return false;
- *haveId = true;
- t = peek(t);
- break;
-
- case TOKlparen:
- if (!allowAltSyntax)
- return false; // Do not recognize C-style declarations.
-
- t = peek(t);
-
- if (t->value == TOKrparen)
- return false; // () is not a declarator
-
- /* Regard ( identifier ) as not a declarator
- * BUG: what about ( *identifier ) in
- * f(*p)(x);
- * where f is a class instance with overloaded () ?
- * Should we just disallow C-style function pointer declarations?
- */
- if (t->value == TOKidentifier)
- { Token *t2 = peek(t);
- if (t2->value == TOKrparen)
- return false;
- }
-
-
- if (!isDeclarator(&t, haveId, NULL, TOKrparen))
- return false;
- t = peek(t);
- parens = true;
- break;
-
- case TOKdelegate:
- case TOKfunction:
- t = peek(t);
- if (!isParameters(&t))
- return false;
- skipAttributes(t, &t);
- continue;
- default: break;
- }
- break;
- }
-
- while (1)
- {
- switch (t->value)
- {
-#if CARRAYDECL
- case TOKlbracket:
- parens = false;
- t = peek(t);
- if (t->value == TOKrbracket)
- {
- t = peek(t);
- }
- else if (isDeclaration(t, 0, TOKrbracket, &t))
- { // It's an associative array declaration
- t = peek(t);
- }
- else
- {
- // [ expression ]
- if (!isExpression(&t))
- return false;
- if (t->value != TOKrbracket)
- return false;
- t = peek(t);
- }
- continue;
-#endif
-
- case TOKlparen:
- parens = false;
- if (Token *tk = peekPastParen(t))
- {
- if (tk->value == TOKlparen)
- {
- if (!haveTpl) return false;
- *haveTpl = 1;
- t = tk;
- }
- else if (tk->value == TOKassign)
- {
- if (!haveTpl) return false;
- *haveTpl = 1;
- *pt = tk;
- return true;
- }
- }
- if (!isParameters(&t))
- return false;
- while (1)
- {
- switch (t->value)
- {
- case TOKconst:
- case TOKimmutable:
- case TOKshared:
- case TOKwild:
- case TOKpure:
- case TOKnothrow:
- case TOKreturn:
- case TOKscope:
- t = peek(t);
- continue;
- case TOKat:
- t = peek(t); // skip '@'
- t = peek(t); // skip identifier
- continue;
- default:
- break;
- }
- break;
- }
- continue;
-
- case TOKidentifier:
- if (t->ident != Id::_body)
- goto Ldefault;
- /* fall through */
-
- // Valid tokens that follow a declaration
- case TOKrparen:
- case TOKrbracket:
- case TOKassign:
- case TOKcomma:
- case TOKdotdotdot:
- case TOKsemicolon:
- case TOKlcurly:
- case TOKin:
- case TOKout:
- case TOKdo:
- LTOKdo:
- // The !parens is to disallow unnecessary parentheses
- if (!parens && (endtok == TOKreserved || endtok == t->value))
- {
- *pt = t;
- return true;
- }
- return false;
-
- case TOKif:
- return haveTpl ? true : false;
-
- // Used for mixin type parsing
- case TOKeof:
- if (endtok == TOKeof)
- goto LTOKdo;
- return false;
-
- default:
- Ldefault:
- return false;
- }
- }
- assert(0);
-}
-
-
-bool Parser::isParameters(Token **pt)
-{ // This code parallels parseParameters()
- Token *t = *pt;
-
- //printf("isParameters()\n");
- if (t->value != TOKlparen)
- return false;
-
- t = peek(t);
- for (;1; t = peek(t))
- {
- L1:
- switch (t->value)
- {
- case TOKrparen:
- break;
-
- case TOKdotdotdot:
- t = peek(t);
- break;
-
- case TOKin:
- case TOKout:
- case TOKref:
- case TOKlazy:
- case TOKscope:
- case TOKfinal:
- case TOKauto:
- case TOKreturn:
- continue;
-
- case TOKconst:
- case TOKimmutable:
- case TOKshared:
- case TOKwild:
- t = peek(t);
- if (t->value == TOKlparen)
- {
- t = peek(t);
- if (!isDeclaration(t, 0, TOKrparen, &t))
- return false;
- t = peek(t); // skip past closing ')'
- goto L2;
- }
- goto L1;
-
- default:
- { if (!isBasicType(&t))
- return false;
- L2:
- int tmp = false;
- if (t->value != TOKdotdotdot &&
- !isDeclarator(&t, &tmp, NULL, TOKreserved))
- return false;
- if (t->value == TOKassign)
- { t = peek(t);
- if (!isExpression(&t))
- return false;
- }
- if (t->value == TOKdotdotdot)
- {
- t = peek(t);
- break;
- }
- }
- if (t->value == TOKcomma)
- {
- continue;
- }
- break;
- }
- break;
- }
- if (t->value != TOKrparen)
- return false;
- t = peek(t);
- *pt = t;
- return true;
-}
-
-bool Parser::isExpression(Token **pt)
-{
- // This is supposed to determine if something is an expression.
- // What it actually does is scan until a closing right bracket
- // is found.
-
- Token *t = *pt;
- int brnest = 0;
- int panest = 0;
- int curlynest = 0;
-
- for (;; t = peek(t))
- {
- switch (t->value)
- {
- case TOKlbracket:
- brnest++;
- continue;
-
- case TOKrbracket:
- if (--brnest >= 0)
- continue;
- break;
-
- case TOKlparen:
- panest++;
- continue;
-
- case TOKcomma:
- if (brnest || panest)
- continue;
- break;
-
- case TOKrparen:
- if (--panest >= 0)
- continue;
- break;
-
- case TOKlcurly:
- curlynest++;
- continue;
-
- case TOKrcurly:
- if (--curlynest >= 0)
- continue;
- return false;
-
- case TOKslice:
- if (brnest)
- continue;
- break;
-
- case TOKsemicolon:
- if (curlynest)
- continue;
- return false;
-
- case TOKeof:
- return false;
-
- default:
- continue;
- }
- break;
- }
-
- *pt = t;
- return true;
-}
-
-/*******************************************
- * Skip parens, brackets.
- * Input:
- * t is on opening (
- * Output:
- * *pt is set to closing token, which is ')' on success
- * Returns:
- * true successful
- * false some parsing error
- */
-
-bool Parser::skipParens(Token *t, Token **pt)
-{
- if (t->value != TOKlparen)
- return false;
-
- int parens = 0;
-
- while (1)
- {
- switch (t->value)
- {
- case TOKlparen:
- parens++;
- break;
-
- case TOKrparen:
- parens--;
- if (parens < 0)
- goto Lfalse;
- if (parens == 0)
- goto Ldone;
- break;
-
- case TOKeof:
- goto Lfalse;
-
- default:
- break;
- }
- t = peek(t);
- }
-
- Ldone:
- if (pt)
- *pt = peek(t); // skip found rparen
- return true;
-
- Lfalse:
- return false;
-}
-
-bool Parser::skipParensIf(Token *t, Token **pt)
-{
- if (t->value != TOKlparen)
- {
- if (pt)
- *pt = t;
- return true;
- }
- return skipParens(t, pt);
-}
-
-/*******************************************
- * Skip attributes.
- * Input:
- * t is on a candidate attribute
- * Output:
- * *pt is set to first non-attribute token on success
- * Returns:
- * true successful
- * false some parsing error
- */
-
-bool Parser::skipAttributes(Token *t, Token **pt)
-{
- while (1)
- {
- switch (t->value)
- {
- case TOKconst:
- case TOKimmutable:
- case TOKshared:
- case TOKwild:
- case TOKfinal:
- case TOKauto:
- case TOKscope:
- case TOKoverride:
- case TOKabstract:
- case TOKsynchronized:
- break;
- case TOKdeprecated:
- if (peek(t)->value == TOKlparen)
- {
- t = peek(t);
- if (!skipParens(t, &t))
- goto Lerror;
- // t is on the next of closing parenthesis
- continue;
- }
- break;
- case TOKnothrow:
- case TOKpure:
- case TOKref:
- case TOKgshared:
- case TOKreturn:
- //case TOKmanifest:
- break;
- case TOKat:
- t = peek(t);
- if (t->value == TOKidentifier)
- {
- /* @identifier
- * @identifier!arg
- * @identifier!(arglist)
- * any of the above followed by (arglist)
- * @predefined_attribute
- */
- if (t->ident == Id::property ||
- t->ident == Id::nogc ||
- t->ident == Id::safe ||
- t->ident == Id::trusted ||
- t->ident == Id::system ||
- t->ident == Id::disable)
- break;
- t = peek(t);
- if (t->value == TOKnot)
- {
- t = peek(t);
- if (t->value == TOKlparen)
- {
- // @identifier!(arglist)
- if (!skipParens(t, &t))
- goto Lerror;
- // t is on the next of closing parenthesis
- }
- else
- {
- // @identifier!arg
- // Do low rent skipTemplateArgument
- if (t->value == TOKvector)
- {
- // identifier!__vector(type)
- t = peek(t);
- if (!skipParens(t, &t))
- goto Lerror;
- }
- else
- t = peek(t);
- }
- }
- if (t->value == TOKlparen)
- {
- if (!skipParens(t, &t))
- goto Lerror;
- // t is on the next of closing parenthesis
- continue;
- }
- continue;
- }
- if (t->value == TOKlparen)
- {
- // @( ArgumentList )
- if (!skipParens(t, &t))
- goto Lerror;
- // t is on the next of closing parenthesis
- continue;
- }
- goto Lerror;
- default:
- goto Ldone;
- }
- t = peek(t);
- }
-
- Ldone:
- if (pt)
- *pt = t;
- return true;
-
- Lerror:
- return false;
-}
-
-/********************************* Expression Parser ***************************/
-
-Expression *Parser::parsePrimaryExp()
-{
- Expression *e;
- Type *t;
- Identifier *id;
- Loc loc = token.loc;
-
- //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
- switch (token.value)
- {
- case TOKidentifier:
- {
- Token *t1 = peek(&token);
- Token *t2 = peek(t1);
- if (t1->value == TOKmin && t2->value == TOKgt)
- {
- // skip ident.
- nextToken();
- nextToken();
- nextToken();
- error("use `.` for member lookup, not `->`");
- goto Lerr;
- }
-
- if (peekNext() == TOKgoesto)
- goto case_delegate;
-
- id = token.ident;
- nextToken();
- TOK save;
- if (token.value == TOKnot && (save = peekNext()) != TOKis && save != TOKin)
- {
- // identifier!(template-argument-list)
- TemplateInstance *tempinst;
- tempinst = new TemplateInstance(loc, id);
- tempinst->tiargs = parseTemplateArguments();
- e = new ScopeExp(loc, tempinst);
- }
- else
- e = new IdentifierExp(loc, id);
- break;
- }
-
- case TOKdollar:
- if (!inBrackets)
- error("`$` is valid only inside [] of index or slice");
- e = new DollarExp(loc);
- nextToken();
- break;
-
- case TOKdot:
- // Signal global scope '.' operator with "" identifier
- e = new IdentifierExp(loc, Id::empty);
- break;
-
- case TOKthis:
- e = new ThisExp(loc);
- nextToken();
- break;
-
- case TOKsuper:
- e = new SuperExp(loc);
- nextToken();
- break;
-
- case TOKint32v:
- e = new IntegerExp(loc, (d_int32)token.int64value, Type::tint32);
- nextToken();
- break;
-
- case TOKuns32v:
- e = new IntegerExp(loc, (d_uns32)token.uns64value, Type::tuns32);
- nextToken();
- break;
-
- case TOKint64v:
- e = new IntegerExp(loc, token.int64value, Type::tint64);
- nextToken();
- break;
-
- case TOKuns64v:
- e = new IntegerExp(loc, token.uns64value, Type::tuns64);
- nextToken();
- break;
-
- case TOKfloat32v:
- e = new RealExp(loc, token.floatvalue, Type::tfloat32);
- nextToken();
- break;
-
- case TOKfloat64v:
- e = new RealExp(loc, token.floatvalue, Type::tfloat64);
- nextToken();
- break;
-
- case TOKfloat80v:
- e = new RealExp(loc, token.floatvalue, Type::tfloat80);
- nextToken();
- break;
-
- case TOKimaginary32v:
- e = new RealExp(loc, token.floatvalue, Type::timaginary32);
- nextToken();
- break;
-
- case TOKimaginary64v:
- e = new RealExp(loc, token.floatvalue, Type::timaginary64);
- nextToken();
- break;
-
- case TOKimaginary80v:
- e = new RealExp(loc, token.floatvalue, Type::timaginary80);
- nextToken();
- break;
-
- case TOKnull:
- e = new NullExp(loc);
- nextToken();
- break;
-
- case TOKfile:
- {
- const char *s = loc.filename ? loc.filename : mod->ident->toChars();
- e = new StringExp(loc, const_cast<char *>(s), strlen(s), 0);
- nextToken();
- break;
- }
-
- case TOKfilefullpath:
- {
- assert(loc.filename); // __FILE_FULL_PATH__ does not work with an invalid location
- const char *s = FileName::toAbsolute(loc.filename);
- e = new StringExp(loc, const_cast<char *>(s), strlen(s), 0);
- nextToken();
- break;
- }
-
- case TOKline:
- e = new IntegerExp(loc, loc.linnum, Type::tint32);
- nextToken();
- break;
-
- case TOKmodulestring:
- {
- const char *s = md ? md->toChars() : mod->toChars();
- e = new StringExp(loc, const_cast<char *>(s), strlen(s), 0);
- nextToken();
- break;
- }
-
- case TOKfuncstring:
- e = new FuncInitExp(loc);
- nextToken();
- break;
-
- case TOKprettyfunc:
- e = new PrettyFuncInitExp(loc);
- nextToken();
- break;
-
- case TOKtrue:
- e = new IntegerExp(loc, 1, Type::tbool);
- nextToken();
- break;
-
- case TOKfalse:
- e = new IntegerExp(loc, 0, Type::tbool);
- nextToken();
- break;
-
- case TOKcharv:
- e = new IntegerExp(loc, (d_uns8)token.uns64value, Type::tchar);
- nextToken();
- break;
-
- case TOKwcharv:
- e = new IntegerExp(loc, (d_uns16)token.uns64value, Type::twchar);
- nextToken();
- break;
-
- case TOKdcharv:
- e = new IntegerExp(loc, (d_uns32)token.uns64value, Type::tdchar);
- nextToken();
- break;
-
- case TOKstring:
- case TOKxstring:
- {
- // cat adjacent strings
- utf8_t *s = token.ustring;
- size_t len = token.len;
- unsigned char postfix = token.postfix;
- while (1)
- {
- const Token prev = token;
- nextToken();
- if (token.value == TOKstring ||
- token.value == TOKxstring)
- {
- if (token.postfix)
- { if (token.postfix != postfix)
- error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
- postfix = token.postfix;
- }
-
- deprecation("Implicit string concatenation is deprecated, use %s ~ %s instead",
- prev.toChars(), token.toChars());
-
- size_t len1 = len;
- size_t len2 = token.len;
- len = len1 + len2;
- utf8_t *s2 = (utf8_t *)mem.xmalloc((len + 1) * sizeof(utf8_t));
- memcpy(s2, s, len1 * sizeof(utf8_t));
- memcpy(s2 + len1, token.ustring, (len2 + 1) * sizeof(utf8_t));
- s = s2;
- }
- else
- break;
- }
- e = new StringExp(loc, s, len, postfix);
- break;
- }
-
- case TOKvoid: t = Type::tvoid; goto LabelX;
- case TOKint8: t = Type::tint8; goto LabelX;
- case TOKuns8: t = Type::tuns8; goto LabelX;
- case TOKint16: t = Type::tint16; goto LabelX;
- case TOKuns16: t = Type::tuns16; goto LabelX;
- case TOKint32: t = Type::tint32; goto LabelX;
- case TOKuns32: t = Type::tuns32; goto LabelX;
- case TOKint64: t = Type::tint64; goto LabelX;
- case TOKuns64: t = Type::tuns64; goto LabelX;
- case TOKint128: t = Type::tint128; goto LabelX;
- case TOKuns128: t = Type::tuns128; goto LabelX;
- case TOKfloat32: t = Type::tfloat32; goto LabelX;
- case TOKfloat64: t = Type::tfloat64; goto LabelX;
- case TOKfloat80: t = Type::tfloat80; goto LabelX;
- case TOKimaginary32: t = Type::timaginary32; goto LabelX;
- case TOKimaginary64: t = Type::timaginary64; goto LabelX;
- case TOKimaginary80: t = Type::timaginary80; goto LabelX;
- case TOKcomplex32: t = Type::tcomplex32; goto LabelX;
- case TOKcomplex64: t = Type::tcomplex64; goto LabelX;
- case TOKcomplex80: t = Type::tcomplex80; goto LabelX;
- case TOKbool: t = Type::tbool; goto LabelX;
- case TOKchar: t = Type::tchar; goto LabelX;
- case TOKwchar: t = Type::twchar; goto LabelX;
- case TOKdchar: t = Type::tdchar; goto LabelX;
- LabelX:
- nextToken();
- if (token.value == TOKlparen)
- {
- e = new TypeExp(loc, t);
- e = new CallExp(loc, e, parseArguments());
- break;
- }
- check(TOKdot, t->toChars());
- if (token.value != TOKidentifier)
- { error("found `%s` when expecting identifier following `%s.`", token.toChars(), t->toChars());
- goto Lerr;
- }
- e = typeDotIdExp(loc, t, token.ident);
- nextToken();
- break;
-
- case TOKtypeof:
- {
- t = parseTypeof();
- e = new TypeExp(loc, t);
- break;
- }
-
- case TOKvector:
- {
- t = parseVector();
- e = new TypeExp(loc, t);
- break;
- }
-
- case TOKtypeid:
- {
- nextToken();
- check(TOKlparen, "typeid");
- RootObject *o = parseTypeOrAssignExp();
- check(TOKrparen);
- e = new TypeidExp(loc, o);
- break;
- }
-
- case TOKtraits:
- { /* __traits(identifier, args...)
- */
- Identifier *ident;
- Objects *args = NULL;
-
- nextToken();
- check(TOKlparen);
- if (token.value != TOKidentifier)
- { error("__traits(identifier, args...) expected");
- goto Lerr;
- }
- ident = token.ident;
- nextToken();
- if (token.value == TOKcomma)
- args = parseTemplateArgumentList(); // __traits(identifier, args...)
- else
- check(TOKrparen); // __traits(identifier)
-
- e = new TraitsExp(loc, ident, args);
- break;
- }
-
- case TOKis:
- {
- Type *targ;
- Identifier *ident = NULL;
- Type *tspec = NULL;
- TOK tok = TOKreserved;
- TOK tok2 = TOKreserved;
- TemplateParameters *tpl = NULL;
-
- nextToken();
- if (token.value == TOKlparen)
- {
- nextToken();
- if (token.value == TOKidentifier && peekNext() == TOKlparen)
- {
- error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars());
- nextToken();
- Token *tempTok = peekPastParen(&token);
- memcpy(&token, tempTok, sizeof(Token));
- goto Lerr;
- }
- targ = parseType(&ident);
- if (token.value == TOKcolon || token.value == TOKequal)
- {
- tok = token.value;
- nextToken();
- if (tok == TOKequal &&
- (token.value == TOKstruct ||
- token.value == TOKunion ||
- token.value == TOKclass ||
- token.value == TOKsuper ||
- token.value == TOKenum ||
- token.value == TOKinterface ||
- token.value == TOKmodule ||
- token.value == TOKpackage ||
- token.value == TOKargTypes ||
- token.value == TOKparameters ||
- (token.value == TOKconst && peek(&token)->value == TOKrparen) ||
- (token.value == TOKimmutable && peek(&token)->value == TOKrparen) ||
- (token.value == TOKshared && peek(&token)->value == TOKrparen) ||
- (token.value == TOKwild && peek(&token)->value == TOKrparen) ||
- token.value == TOKfunction ||
- token.value == TOKdelegate ||
- token.value == TOKreturn ||
- (token.value == TOKvector && peek(&token)->value == TOKrparen)))
- {
- tok2 = token.value;
- nextToken();
- }
- else
- {
- tspec = parseType();
- }
- }
- if (tspec)
- {
- if (token.value == TOKcomma)
- tpl = parseTemplateParameterList(1);
- else
- {
- tpl = new TemplateParameters();
- check(TOKrparen);
- }
- }
- else
- check(TOKrparen);
- }
- else
- {
- error("(type identifier : specialization) expected following is");
- goto Lerr;
- }
- e = new IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
- break;
- }
-
- case TOKassert:
- { Expression *msg = NULL;
-
- nextToken();
- check(TOKlparen, "assert");
- e = parseAssignExp();
- if (token.value == TOKcomma)
- {
- nextToken();
- if (token.value != TOKrparen)
- {
- msg = parseAssignExp();
- if (token.value == TOKcomma)
- nextToken();
- }
- }
- check(TOKrparen);
- e = new AssertExp(loc, e, msg);
- break;
- }
-
- case TOKmixin:
- {
- // https://dlang.org/spec/expression.html#mixin_expressions
- nextToken();
- if (token.value != TOKlparen)
- error("found `%s` when expecting `%s` following %s", token.toChars(), Token::toChars(TOKlparen), "`mixin`");
- e = new CompileExp(loc, parseArguments());
- break;
- }
-
- case TOKimport:
- {
- nextToken();
- check(TOKlparen, "import");
- e = parseAssignExp();
- check(TOKrparen);
- e = new ImportExp(loc, e);
- break;
- }
-
- case TOKnew:
- e = parseNewExp(NULL);
- break;
-
- case TOKref:
- {
- if (peekNext() == TOKlparen)
- {
- Token *tk = peekPastParen(peek(&token));
- if (skipAttributes(tk, &tk) &&
- (tk->value == TOKgoesto || tk->value == TOKlcurly))
- {
- // ref (arguments) => expression
- // ref (arguments) { statements... }
- goto case_delegate;
- }
- }
- nextToken();
- error("found `%s` when expecting function literal following `ref`", token.toChars());
- goto Lerr;
- }
-
- case TOKlparen:
- {
- Token *tk = peekPastParen(&token);
- if (skipAttributes(tk, &tk) &&
- (tk->value == TOKgoesto || tk->value == TOKlcurly))
- {
- // (arguments) => expression
- // (arguments) { statements... }
- goto case_delegate;
- }
-
- // ( expression )
- nextToken();
- e = parseExpression();
- e->parens = 1;
- check(loc, TOKrparen);
- break;
- }
-
- case TOKlbracket:
- { /* Parse array literals and associative array literals:
- * [ value, value, value ... ]
- * [ key:value, key:value, key:value ... ]
- */
- Expressions *values = new Expressions();
- Expressions *keys = NULL;
-
- nextToken();
- while (token.value != TOKrbracket && token.value != TOKeof)
- {
- e = parseAssignExp();
- if (token.value == TOKcolon && (keys || values->length == 0))
- { nextToken();
- if (!keys)
- keys = new Expressions();
- keys->push(e);
- e = parseAssignExp();
- }
- else if (keys)
- { error("`key:value` expected for associative array literal");
- delete keys;
- keys = NULL;
- }
- values->push(e);
- if (token.value == TOKrbracket)
- break;
- check(TOKcomma);
- }
- check(loc, TOKrbracket);
-
- if (keys)
- e = new AssocArrayLiteralExp(loc, keys, values);
- else
- e = new ArrayLiteralExp(loc, NULL, values);
- break;
- }
-
- case TOKlcurly:
- case TOKfunction:
- case TOKdelegate:
- case_delegate:
- {
- Dsymbol *s = parseFunctionLiteral();
- e = new FuncExp(loc, s);
- break;
- }
-
- default:
- error("expression expected, not `%s`", token.toChars());
- Lerr:
- // Anything for e, as long as it's not NULL
- e = new IntegerExp(loc, 0, Type::tint32);
- nextToken();
- break;
- }
- return e;
-}
-
-Expression *Parser::parsePostExp(Expression *e)
-{
- Loc loc;
-
- while (1)
- {
- loc = token.loc;
- switch (token.value)
- {
- case TOKdot:
- nextToken();
- if (token.value == TOKidentifier)
- { Identifier *id = token.ident;
-
- nextToken();
- if (token.value == TOKnot && peekNext() != TOKis && peekNext() != TOKin)
- {
- Objects *tiargs = parseTemplateArguments();
- e = new DotTemplateInstanceExp(loc, e, id, tiargs);
- }
- else
- e = new DotIdExp(loc, e, id);
- continue;
- }
- else if (token.value == TOKnew)
- {
- e = parseNewExp(e);
- continue;
- }
- else
- error("identifier expected following `.`, not `%s`", token.toChars());
- break;
-
- case TOKplusplus:
- e = new PostExp(TOKplusplus, loc, e);
- break;
-
- case TOKminusminus:
- e = new PostExp(TOKminusminus, loc, e);
- break;
-
- case TOKlparen:
- e = new CallExp(loc, e, parseArguments());
- continue;
-
- case TOKlbracket:
- { // array dereferences:
- // array[index]
- // array[]
- // array[lwr .. upr]
- Expression *index;
- Expression *upr;
- Expressions *arguments = new Expressions();
-
- inBrackets++;
- nextToken();
- while (token.value != TOKrbracket && token.value != TOKeof)
- {
- index = parseAssignExp();
- if (token.value == TOKslice)
- {
- // array[..., lwr..upr, ...]
- nextToken();
- upr = parseAssignExp();
- arguments->push(new IntervalExp(loc, index, upr));
- }
- else
- arguments->push(index);
- if (token.value == TOKrbracket)
- break;
- check(TOKcomma);
- }
- check(TOKrbracket);
- inBrackets--;
- e = new ArrayExp(loc, e, arguments);
- continue;
- }
-
- default:
- return e;
- }
- nextToken();
- }
-}
-
-Expression *Parser::parseUnaryExp()
-{
- Expression *e;
- Loc loc = token.loc;
-
- switch (token.value)
- {
- case TOKand:
- nextToken();
- e = parseUnaryExp();
- e = new AddrExp(loc, e);
- break;
-
- case TOKplusplus:
- nextToken();
- e = parseUnaryExp();
- //e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
- e = new PreExp(TOKpreplusplus, loc, e);
- break;
-
- case TOKminusminus:
- nextToken();
- e = parseUnaryExp();
- //e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
- e = new PreExp(TOKpreminusminus, loc, e);
- break;
-
- case TOKmul:
- nextToken();
- e = parseUnaryExp();
- e = new PtrExp(loc, e);
- break;
-
- case TOKmin:
- nextToken();
- e = parseUnaryExp();
- e = new NegExp(loc, e);
- break;
-
- case TOKadd:
- nextToken();
- e = parseUnaryExp();
- e = new UAddExp(loc, e);
- break;
-
- case TOKnot:
- nextToken();
- e = parseUnaryExp();
- e = new NotExp(loc, e);
- break;
-
- case TOKtilde:
- nextToken();
- e = parseUnaryExp();
- e = new ComExp(loc, e);
- break;
-
- case TOKdelete:
- nextToken();
- e = parseUnaryExp();
- e = new DeleteExp(loc, e, false);
- break;
-
- case TOKcast: // cast(type) expression
- {
- nextToken();
- check(TOKlparen);
- /* Look for cast(), cast(const), cast(immutable),
- * cast(shared), cast(shared const), cast(wild), cast(shared wild)
- */
- unsigned char m = 0;
- while (1)
- {
- switch (token.value)
- {
- case TOKconst:
- if (peekNext() == TOKlparen)
- break; // const as type constructor
- m |= MODconst; // const as storage class
- nextToken();
- continue;
-
- case TOKimmutable:
- if (peekNext() == TOKlparen)
- break;
- m |= MODimmutable;
- nextToken();
- continue;
-
- case TOKshared:
- if (peekNext() == TOKlparen)
- break;
- m |= MODshared;
- nextToken();
- continue;
-
- case TOKwild:
- if (peekNext() == TOKlparen)
- break;
- m |= MODwild;
- nextToken();
- continue;
-
- default:
- break;
- }
- break;
- }
- if (token.value == TOKrparen)
- {
- nextToken();
- e = parseUnaryExp();
- e = new CastExp(loc, e, m);
- }
- else
- {
- Type *t = parseType(); // cast( type )
- t = t->addMod(m); // cast( const type )
- check(TOKrparen);
- e = parseUnaryExp();
- e = new CastExp(loc, e, t);
- }
- break;
- }
-
- case TOKwild:
- case TOKshared:
- case TOKconst:
- case TOKimmutable: // immutable(type)(arguments) / immutable(type).init
- {
- StorageClass stc = parseTypeCtor();
- Type *t = parseBasicType();
- t = t->addSTC(stc);
- e = new TypeExp(loc, t);
- if (stc == 0 && token.value == TOKdot)
- {
- nextToken();
- if (token.value != TOKidentifier)
- {
- error("identifier expected following (type).");
- return NULL;
- }
- e = typeDotIdExp(loc, t, token.ident);
- nextToken();
- e = parsePostExp(e);
- break;
- }
- else if (token.value != TOKlparen)
- {
- error("(arguments) expected following %s", t->toChars());
- return e;
- }
- e = new CallExp(loc, e, parseArguments());
- break;
- }
-
-
- case TOKlparen:
- { Token *tk;
-
- tk = peek(&token);
-#if CCASTSYNTAX
- // If cast
- if (isDeclaration(tk, 0, TOKrparen, &tk))
- {
- tk = peek(tk); // skip over right parenthesis
- switch (tk->value)
- {
- case TOKnot:
- tk = peek(tk);
- if (tk->value == TOKis || tk->value == TOKin) // !is or !in
- break;
- /* fall through */
-
- case TOKdot:
- case TOKplusplus:
- case TOKminusminus:
- case TOKdelete:
- case TOKnew:
- case TOKlparen:
- case TOKidentifier:
- case TOKthis:
- case TOKsuper:
- case TOKint32v:
- case TOKuns32v:
- case TOKint64v:
- case TOKuns64v:
- case TOKint128v:
- case TOKuns128v:
- case TOKfloat32v:
- case TOKfloat64v:
- case TOKfloat80v:
- case TOKimaginary32v:
- case TOKimaginary64v:
- case TOKimaginary80v:
- case TOKnull:
- case TOKtrue:
- case TOKfalse:
- case TOKcharv:
- case TOKwcharv:
- case TOKdcharv:
- case TOKstring:
- case TOKfunction:
- case TOKdelegate:
- case TOKtypeof:
- case TOKtraits:
- case TOKvector:
- case TOKfile:
- case TOKfilefullpath:
- case TOKline:
- case TOKmodulestring:
- case TOKfuncstring:
- case TOKprettyfunc:
- case TOKwchar: case TOKdchar:
- case TOKbool: case TOKchar:
- case TOKint8: case TOKuns8:
- case TOKint16: case TOKuns16:
- case TOKint32: case TOKuns32:
- case TOKint64: case TOKuns64:
- case TOKint128: case TOKuns128:
- case TOKfloat32: case TOKfloat64: case TOKfloat80:
- case TOKimaginary32: case TOKimaginary64: case TOKimaginary80:
- case TOKcomplex32: case TOKcomplex64: case TOKcomplex80:
- case TOKvoid:
- { // (type) una_exp
- Type *t;
-
- nextToken();
- t = parseType();
- check(TOKrparen);
-
- // if .identifier
- // or .identifier!( ... )
- if (token.value == TOKdot)
- {
- if (peekNext() != TOKidentifier && peekNext() != TOKnew)
- {
- error("identifier or new keyword expected following (...).");
- return NULL;
- }
- e = new TypeExp(loc, t);
- e->parens = 1;
- e = parsePostExp(e);
- }
- else
- {
- e = parseUnaryExp();
- e = new CastExp(loc, e, t);
- error("C style cast illegal, use %s", e->toChars());
- }
- return e;
- }
- default:
- break;
- }
- }
-#endif
- e = parsePrimaryExp();
- e = parsePostExp(e);
- break;
- }
- default:
- e = parsePrimaryExp();
- e = parsePostExp(e);
- break;
- }
- assert(e);
-
- // ^^ is right associative and has higher precedence than the unary operators
- while (token.value == TOKpow)
- {
- nextToken();
- Expression *e2 = parseUnaryExp();
- e = new PowExp(loc, e, e2);
- }
-
- return e;
-}
-
-Expression *Parser::parseMulExp()
-{
- Expression *e;
- Expression *e2;
- Loc loc = token.loc;
-
- e = parseUnaryExp();
- while (1)
- {
- switch (token.value)
- {
- case TOKmul: nextToken(); e2 = parseUnaryExp(); e = new MulExp(loc,e,e2); continue;
- case TOKdiv: nextToken(); e2 = parseUnaryExp(); e = new DivExp(loc,e,e2); continue;
- case TOKmod: nextToken(); e2 = parseUnaryExp(); e = new ModExp(loc,e,e2); continue;
-
- default:
- break;
- }
- break;
- }
- return e;
-}
-
-Expression *Parser::parseAddExp()
-{
- Expression *e;
- Expression *e2;
- Loc loc = token.loc;
-
- e = parseMulExp();
- while (1)
- {
- switch (token.value)
- {
- case TOKadd: nextToken(); e2 = parseMulExp(); e = new AddExp(loc,e,e2); continue;
- case TOKmin: nextToken(); e2 = parseMulExp(); e = new MinExp(loc,e,e2); continue;
- case TOKtilde: nextToken(); e2 = parseMulExp(); e = new CatExp(loc,e,e2); continue;
-
- default:
- break;
- }
- break;
- }
- return e;
-}
-
-Expression *Parser::parseShiftExp()
-{
- Expression *e;
- Expression *e2;
- Loc loc = token.loc;
-
- e = parseAddExp();
- while (1)
- {
- switch (token.value)
- {
- case TOKshl: nextToken(); e2 = parseAddExp(); e = new ShlExp(loc,e,e2); continue;
- case TOKshr: nextToken(); e2 = parseAddExp(); e = new ShrExp(loc,e,e2); continue;
- case TOKushr: nextToken(); e2 = parseAddExp(); e = new UshrExp(loc,e,e2); continue;
-
- default:
- break;
- }
- break;
- }
- return e;
-}
-
-Expression *Parser::parseCmpExp()
-{
- Expression *e;
- Expression *e2;
- Token *t;
- Loc loc = token.loc;
-
- e = parseShiftExp();
- TOK op = token.value;
-
- switch (op)
- {
- case TOKequal:
- case TOKnotequal:
- nextToken();
- e2 = parseShiftExp();
- e = new EqualExp(op, loc, e, e2);
- break;
-
- case TOKis:
- op = TOKidentity;
- goto L1;
-
- case TOKnot:
- // Attempt to identify '!is'
- t = peek(&token);
- if (t->value == TOKin)
- {
- nextToken();
- nextToken();
- e2 = parseShiftExp();
- e = new InExp(loc, e, e2);
- e = new NotExp(loc, e);
- break;
- }
- if (t->value != TOKis)
- break;
- nextToken();
- op = TOKnotidentity;
- goto L1;
-
- L1:
- nextToken();
- e2 = parseShiftExp();
- e = new IdentityExp(op, loc, e, e2);
- break;
-
- case TOKlt:
- case TOKle:
- case TOKgt:
- case TOKge:
- case TOKunord:
- case TOKlg:
- case TOKleg:
- case TOKule:
- case TOKul:
- case TOKuge:
- case TOKug:
- case TOKue:
- nextToken();
- e2 = parseShiftExp();
- e = new CmpExp(op, loc, e, e2);
- break;
-
- case TOKin:
- nextToken();
- e2 = parseShiftExp();
- e = new InExp(loc, e, e2);
- break;
-
- default:
- break;
- }
- return e;
-}
-
-Expression *Parser::parseAndExp()
-{
- Loc loc = token.loc;
-
- Expression *e = parseCmpExp();
- while (token.value == TOKand)
- {
- checkParens(TOKand, e);
- nextToken();
- Expression *e2 = parseCmpExp();
- checkParens(TOKand, e2);
- e = new AndExp(loc,e,e2);
- loc = token.loc;
- }
- return e;
-}
-
-Expression *Parser::parseXorExp()
-{
- Loc loc = token.loc;
-
- Expression *e = parseAndExp();
- while (token.value == TOKxor)
- {
- checkParens(TOKxor, e);
- nextToken();
- Expression *e2 = parseAndExp();
- checkParens(TOKxor, e2);
- e = new XorExp(loc, e, e2);
- }
- return e;
-}
-
-Expression *Parser::parseOrExp()
-{
- Loc loc = token.loc;
-
- Expression *e = parseXorExp();
- while (token.value == TOKor)
- {
- checkParens(TOKor, e);
- nextToken();
- Expression *e2 = parseXorExp();
- checkParens(TOKor, e2);
- e = new OrExp(loc, e, e2);
- }
- return e;
-}
-
-Expression *Parser::parseAndAndExp()
-{
- Expression *e;
- Expression *e2;
- Loc loc = token.loc;
-
- e = parseOrExp();
- while (token.value == TOKandand)
- {
- nextToken();
- e2 = parseOrExp();
- e = new LogicalExp(loc, TOKandand, e, e2);
- }
- return e;
-}
-
-Expression *Parser::parseOrOrExp()
-{
- Expression *e;
- Expression *e2;
- Loc loc = token.loc;
-
- e = parseAndAndExp();
- while (token.value == TOKoror)
- {
- nextToken();
- e2 = parseAndAndExp();
- e = new LogicalExp(loc, TOKoror, e, e2);
- }
- return e;
-}
-
-Expression *Parser::parseCondExp()
-{
- Expression *e;
- Expression *e1;
- Expression *e2;
- Loc loc = token.loc;
-
- e = parseOrOrExp();
- if (token.value == TOKquestion)
- {
- nextToken();
- e1 = parseExpression();
- check(TOKcolon);
- e2 = parseCondExp();
- e = new CondExp(loc, e, e1, e2);
- }
- return e;
-}
-
-Expression *Parser::parseAssignExp()
-{
- Expression *e;
- Expression *e2;
- Loc loc;
-
- e = parseCondExp();
- while (1)
- {
- loc = token.loc;
- switch (token.value)
- {
- case TOKassign: nextToken(); e2 = parseAssignExp(); e = new AssignExp(loc,e,e2); continue;
- case TOKaddass: nextToken(); e2 = parseAssignExp(); e = new AddAssignExp(loc,e,e2); continue;
- case TOKminass: nextToken(); e2 = parseAssignExp(); e = new MinAssignExp(loc,e,e2); continue;
- case TOKmulass: nextToken(); e2 = parseAssignExp(); e = new MulAssignExp(loc,e,e2); continue;
- case TOKdivass: nextToken(); e2 = parseAssignExp(); e = new DivAssignExp(loc,e,e2); continue;
- case TOKmodass: nextToken(); e2 = parseAssignExp(); e = new ModAssignExp(loc,e,e2); continue;
- case TOKpowass: nextToken(); e2 = parseAssignExp(); e = new PowAssignExp(loc,e,e2); continue;
- case TOKandass: nextToken(); e2 = parseAssignExp(); e = new AndAssignExp(loc,e,e2); continue;
- case TOKorass: nextToken(); e2 = parseAssignExp(); e = new OrAssignExp(loc,e,e2); continue;
- case TOKxorass: nextToken(); e2 = parseAssignExp(); e = new XorAssignExp(loc,e,e2); continue;
- case TOKshlass: nextToken(); e2 = parseAssignExp(); e = new ShlAssignExp(loc,e,e2); continue;
- case TOKshrass: nextToken(); e2 = parseAssignExp(); e = new ShrAssignExp(loc,e,e2); continue;
- case TOKushrass: nextToken(); e2 = parseAssignExp(); e = new UshrAssignExp(loc,e,e2); continue;
- case TOKcatass: nextToken(); e2 = parseAssignExp(); e = new CatAssignExp(loc,e,e2); continue;
- default:
- break;
- }
- break;
- }
- return e;
-}
-
-Expression *Parser::parseExpression()
-{
- Expression *e;
- Expression *e2;
- Loc loc = token.loc;
-
- //printf("Parser::parseExpression() loc = %d\n", loc.linnum);
- e = parseAssignExp();
- while (token.value == TOKcomma)
- {
- nextToken();
- e2 = parseAssignExp();
- e = new CommaExp(loc, e, e2, false);
- loc = token.loc;
- }
- return e;
-}
-
-
-/*************************
- * Collect argument list.
- * Assume current token is ',', '(' or '['.
- */
-
-Expressions *Parser::parseArguments()
-{ // function call
- Expressions *arguments;
- Expression *arg;
- TOK endtok;
-
- arguments = new Expressions();
- if (token.value == TOKlbracket)
- endtok = TOKrbracket;
- else
- endtok = TOKrparen;
-
- {
- nextToken();
- while (token.value != endtok && token.value != TOKeof)
- {
- arg = parseAssignExp();
- arguments->push(arg);
- if (token.value == endtok)
- break;
- check(TOKcomma);
- }
- check(endtok);
- }
- return arguments;
-}
-
-/*******************************************
- */
-
-Expression *Parser::parseNewExp(Expression *thisexp)
-{
- Type *t;
- Expressions *newargs;
- Expressions *arguments = NULL;
- Loc loc = token.loc;
-
- nextToken();
- newargs = NULL;
- if (token.value == TOKlparen)
- {
- newargs = parseArguments();
- }
-
- // An anonymous nested class starts with "class"
- if (token.value == TOKclass)
- {
- nextToken();
- if (token.value == TOKlparen)
- arguments = parseArguments();
-
- BaseClasses *baseclasses = NULL;
- if (token.value != TOKlcurly)
- baseclasses = parseBaseClasses();
-
- Identifier *id = NULL;
- Dsymbols *members = NULL;
-
- if (token.value != TOKlcurly)
- {
- error("{ members } expected for anonymous class");
- }
- else
- {
- nextToken();
- members = parseDeclDefs(0);
- if (token.value != TOKrcurly)
- error("class member expected");
- nextToken();
- }
-
- ClassDeclaration *cd = new ClassDeclaration(loc, id, baseclasses, members, false);
- Expression *e = new NewAnonClassExp(loc, thisexp, newargs, cd, arguments);
-
- return e;
- }
-
- StorageClass stc = parseTypeCtor();
- t = parseBasicType(true);
- t = parseBasicType2(t);
- t = t->addSTC(stc);
- if (t->ty == Taarray)
- {
- TypeAArray *taa = (TypeAArray *)t;
- Type *index = taa->index;
-
- Expression *edim = typeToExpression(index);
- if (!edim)
- {
- error("need size of rightmost array, not type %s", index->toChars());
- return new NullExp(loc);
- }
- t = new TypeSArray(taa->next, edim);
- }
- else if (t->ty == Tsarray)
- {
- }
- else if (token.value == TOKlparen)
- {
- arguments = parseArguments();
- }
- Expression *e = new NewExp(loc, thisexp, newargs, t, arguments);
- return e;
-}
-
-/**********************************************
- */
-
-void Parser::addComment(Dsymbol *s, const utf8_t *blockComment)
-{
- s->addComment(combineComments(blockComment, token.lineComment));
- token.lineComment = NULL;
-}
-
-
-/**********************************
- * Set operator precedence for each operator.
- */
-
-PREC precedence[TOKMAX];
-
-struct PrecedenceInitializer
-{
- PrecedenceInitializer();
-};
-
-static PrecedenceInitializer precedenceinitializer;
-
-PrecedenceInitializer::PrecedenceInitializer()
-{
- for (size_t i = 0; i < TOKMAX; i++)
- precedence[i] = PREC_zero;
-
- precedence[TOKtype] = PREC_expr;
- precedence[TOKerror] = PREC_expr;
-
- precedence[TOKtypeof] = PREC_primary;
- precedence[TOKmixin] = PREC_primary;
- precedence[TOKimport] = PREC_primary;
-
- precedence[TOKdotvar] = PREC_primary;
- precedence[TOKscope] = PREC_primary;
- precedence[TOKidentifier] = PREC_primary;
- precedence[TOKthis] = PREC_primary;
- precedence[TOKsuper] = PREC_primary;
- precedence[TOKint64] = PREC_primary;
- precedence[TOKfloat64] = PREC_primary;
- precedence[TOKcomplex80] = PREC_primary;
- precedence[TOKnull] = PREC_primary;
- precedence[TOKstring] = PREC_primary;
- precedence[TOKarrayliteral] = PREC_primary;
- precedence[TOKassocarrayliteral] = PREC_primary;
- precedence[TOKclassreference] = PREC_primary;
- precedence[TOKfile] = PREC_primary;
- precedence[TOKfilefullpath] = PREC_primary;
- precedence[TOKline] = PREC_primary;
- precedence[TOKmodulestring] = PREC_primary;
- precedence[TOKfuncstring] = PREC_primary;
- precedence[TOKprettyfunc] = PREC_primary;
- precedence[TOKtypeid] = PREC_primary;
- precedence[TOKis] = PREC_primary;
- precedence[TOKassert] = PREC_primary;
- precedence[TOKhalt] = PREC_primary;
- precedence[TOKtemplate] = PREC_primary;
- precedence[TOKdsymbol] = PREC_primary;
- precedence[TOKfunction] = PREC_primary;
- precedence[TOKvar] = PREC_primary;
- precedence[TOKsymoff] = PREC_primary;
- precedence[TOKstructliteral] = PREC_primary;
- precedence[TOKarraylength] = PREC_primary;
- precedence[TOKdelegateptr] = PREC_primary;
- precedence[TOKdelegatefuncptr] = PREC_primary;
- precedence[TOKremove] = PREC_primary;
- precedence[TOKtuple] = PREC_primary;
- precedence[TOKtraits] = PREC_primary;
- precedence[TOKdefault] = PREC_primary;
- precedence[TOKoverloadset] = PREC_primary;
- precedence[TOKvoid] = PREC_primary;
- precedence[TOKvectorarray] = PREC_primary;
-
- // post
- precedence[TOKdotti] = PREC_primary;
- precedence[TOKdotid] = PREC_primary;
- precedence[TOKdottd] = PREC_primary;
- precedence[TOKdot] = PREC_primary;
- precedence[TOKdottype] = PREC_primary;
-// precedence[TOKarrow] = PREC_primary;
- precedence[TOKplusplus] = PREC_primary;
- precedence[TOKminusminus] = PREC_primary;
- precedence[TOKpreplusplus] = PREC_primary;
- precedence[TOKpreminusminus] = PREC_primary;
- precedence[TOKcall] = PREC_primary;
- precedence[TOKslice] = PREC_primary;
- precedence[TOKarray] = PREC_primary;
- precedence[TOKindex] = PREC_primary;
-
- precedence[TOKdelegate] = PREC_unary;
- precedence[TOKaddress] = PREC_unary;
- precedence[TOKstar] = PREC_unary;
- precedence[TOKneg] = PREC_unary;
- precedence[TOKuadd] = PREC_unary;
- precedence[TOKnot] = PREC_unary;
- precedence[TOKtilde] = PREC_unary;
- precedence[TOKdelete] = PREC_unary;
- precedence[TOKnew] = PREC_unary;
- precedence[TOKnewanonclass] = PREC_unary;
- precedence[TOKcast] = PREC_unary;
-
- precedence[TOKvector] = PREC_unary;
- precedence[TOKpow] = PREC_pow;
-
- precedence[TOKmul] = PREC_mul;
- precedence[TOKdiv] = PREC_mul;
- precedence[TOKmod] = PREC_mul;
-
- precedence[TOKadd] = PREC_add;
- precedence[TOKmin] = PREC_add;
- precedence[TOKcat] = PREC_add;
-
- precedence[TOKshl] = PREC_shift;
- precedence[TOKshr] = PREC_shift;
- precedence[TOKushr] = PREC_shift;
-
- precedence[TOKlt] = PREC_rel;
- precedence[TOKle] = PREC_rel;
- precedence[TOKgt] = PREC_rel;
- precedence[TOKge] = PREC_rel;
- precedence[TOKunord] = PREC_rel;
- precedence[TOKlg] = PREC_rel;
- precedence[TOKleg] = PREC_rel;
- precedence[TOKule] = PREC_rel;
- precedence[TOKul] = PREC_rel;
- precedence[TOKuge] = PREC_rel;
- precedence[TOKug] = PREC_rel;
- precedence[TOKue] = PREC_rel;
- precedence[TOKin] = PREC_rel;
-
- /* Note that we changed precedence, so that < and != have the same
- * precedence. This change is in the parser, too.
- */
- precedence[TOKequal] = PREC_rel;
- precedence[TOKnotequal] = PREC_rel;
- precedence[TOKidentity] = PREC_rel;
- precedence[TOKnotidentity] = PREC_rel;
-
- precedence[TOKand] = PREC_and;
-
- precedence[TOKxor] = PREC_xor;
-
- precedence[TOKor] = PREC_or;
-
- precedence[TOKandand] = PREC_andand;
-
- precedence[TOKoror] = PREC_oror;
-
- precedence[TOKquestion] = PREC_cond;
-
- precedence[TOKassign] = PREC_assign;
- precedence[TOKconstruct] = PREC_assign;
- precedence[TOKblit] = PREC_assign;
- precedence[TOKaddass] = PREC_assign;
- precedence[TOKminass] = PREC_assign;
- precedence[TOKcatass] = PREC_assign;
- precedence[TOKmulass] = PREC_assign;
- precedence[TOKdivass] = PREC_assign;
- precedence[TOKmodass] = PREC_assign;
- precedence[TOKpowass] = PREC_assign;
- precedence[TOKshlass] = PREC_assign;
- precedence[TOKshrass] = PREC_assign;
- precedence[TOKushrass] = PREC_assign;
- precedence[TOKandass] = PREC_assign;
- precedence[TOKorass] = PREC_assign;
- precedence[TOKxorass] = PREC_assign;
-
- precedence[TOKcomma] = PREC_expr;
- precedence[TOKdeclaration] = PREC_expr;
-
- precedence[TOKinterval] = PREC_assign;
-}
diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d
new file mode 100644
index 00000000000..21042dd80ea
--- /dev/null
+++ b/gcc/d/dmd/parse.d
@@ -0,0 +1,9365 @@
+/**
+ * Takes a token stream from the lexer, and parses it into an abstract syntax tree.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/grammar.html, D Grammar)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/parse.d, _parse.d)
+ * Documentation: https://dlang.org/phobos/dmd_parse.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parse.d
+ */
+
+module dmd.parse;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.astenums;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.lexer;
+import dmd.errors;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.tokens;
+
+// How multiple declarations are parsed.
+// If 1, treat as C.
+// If 0, treat:
+// int *p, i;
+// as:
+// int* p;
+// int* i;
+private enum CDECLSYNTAX = 0;
+
+// Support C cast syntax:
+// (type)(expression)
+private enum CCASTSYNTAX = 1;
+
+// Support postfix C array declarations, such as
+// int a[3][4];
+private enum CARRAYDECL = 1;
+
+/**********************************
+ * Set operator precedence for each operator.
+ *
+ * Used by hdrgen
+ */
+immutable PREC[TOK.max + 1] precedence =
+[
+ TOK.type : PREC.expr,
+ TOK.error : PREC.expr,
+ TOK.objcClassReference : PREC.expr, // Objective-C class reference, same as TOK.type
+
+ TOK.typeof_ : PREC.primary,
+ TOK.mixin_ : PREC.primary,
+
+ TOK.import_ : PREC.primary,
+ TOK.dotVariable : PREC.primary,
+ TOK.scope_ : PREC.primary,
+ TOK.identifier : PREC.primary,
+ TOK.this_ : PREC.primary,
+ TOK.super_ : PREC.primary,
+ TOK.int64 : PREC.primary,
+ TOK.float64 : PREC.primary,
+ TOK.complex80 : PREC.primary,
+ TOK.null_ : PREC.primary,
+ TOK.string_ : PREC.primary,
+ TOK.arrayLiteral : PREC.primary,
+ TOK.assocArrayLiteral : PREC.primary,
+ TOK.classReference : PREC.primary,
+ TOK.file : PREC.primary,
+ TOK.fileFullPath : PREC.primary,
+ TOK.line : PREC.primary,
+ TOK.moduleString : PREC.primary,
+ TOK.functionString : PREC.primary,
+ TOK.prettyFunction : PREC.primary,
+ TOK.typeid_ : PREC.primary,
+ TOK.is_ : PREC.primary,
+ TOK.assert_ : PREC.primary,
+ TOK.halt : PREC.primary,
+ TOK.template_ : PREC.primary,
+ TOK.dSymbol : PREC.primary,
+ TOK.function_ : PREC.primary,
+ TOK.variable : PREC.primary,
+ TOK.symbolOffset : PREC.primary,
+ TOK.structLiteral : PREC.primary,
+ TOK.compoundLiteral : PREC.primary,
+ TOK.arrayLength : PREC.primary,
+ TOK.delegatePointer : PREC.primary,
+ TOK.delegateFunctionPointer : PREC.primary,
+ TOK.remove : PREC.primary,
+ TOK.tuple : PREC.primary,
+ TOK.traits : PREC.primary,
+ TOK.default_ : PREC.primary,
+ TOK.overloadSet : PREC.primary,
+ TOK.void_ : PREC.primary,
+ TOK.vectorArray : PREC.primary,
+ TOK._Generic : PREC.primary,
+
+ // post
+ TOK.dotTemplateInstance : PREC.primary,
+ TOK.dotIdentifier : PREC.primary,
+ TOK.dotTemplateDeclaration : PREC.primary,
+ TOK.dot : PREC.primary,
+ TOK.dotType : PREC.primary,
+ TOK.plusPlus : PREC.primary,
+ TOK.minusMinus : PREC.primary,
+ TOK.prePlusPlus : PREC.primary,
+ TOK.preMinusMinus : PREC.primary,
+ TOK.call : PREC.primary,
+ TOK.slice : PREC.primary,
+ TOK.array : PREC.primary,
+ TOK.index : PREC.primary,
+
+ TOK.delegate_ : PREC.unary,
+ TOK.address : PREC.unary,
+ TOK.star : PREC.unary,
+ TOK.negate : PREC.unary,
+ TOK.uadd : PREC.unary,
+ TOK.not : PREC.unary,
+ TOK.tilde : PREC.unary,
+ TOK.delete_ : PREC.unary,
+ TOK.new_ : PREC.unary,
+ TOK.newAnonymousClass : PREC.unary,
+ TOK.cast_ : PREC.unary,
+
+ TOK.vector : PREC.unary,
+ TOK.pow : PREC.pow,
+
+ TOK.mul : PREC.mul,
+ TOK.div : PREC.mul,
+ TOK.mod : PREC.mul,
+
+ TOK.add : PREC.add,
+ TOK.min : PREC.add,
+ TOK.concatenate : PREC.add,
+
+ TOK.leftShift : PREC.shift,
+ TOK.rightShift : PREC.shift,
+ TOK.unsignedRightShift : PREC.shift,
+
+ TOK.lessThan : PREC.rel,
+ TOK.lessOrEqual : PREC.rel,
+ TOK.greaterThan : PREC.rel,
+ TOK.greaterOrEqual : PREC.rel,
+ TOK.in_ : PREC.rel,
+
+ /* Note that we changed precedence, so that < and != have the same
+ * precedence. This change is in the parser, too.
+ */
+ TOK.equal : PREC.rel,
+ TOK.notEqual : PREC.rel,
+ TOK.identity : PREC.rel,
+ TOK.notIdentity : PREC.rel,
+
+ TOK.and : PREC.and,
+ TOK.xor : PREC.xor,
+ TOK.or : PREC.or,
+
+ TOK.andAnd : PREC.andand,
+ TOK.orOr : PREC.oror,
+
+ TOK.question : PREC.cond,
+
+ TOK.assign : PREC.assign,
+ TOK.construct : PREC.assign,
+ TOK.blit : PREC.assign,
+ TOK.addAssign : PREC.assign,
+ TOK.minAssign : PREC.assign,
+ TOK.concatenateAssign : PREC.assign,
+ TOK.concatenateElemAssign : PREC.assign,
+ TOK.concatenateDcharAssign : PREC.assign,
+ TOK.mulAssign : PREC.assign,
+ TOK.divAssign : PREC.assign,
+ TOK.modAssign : PREC.assign,
+ TOK.powAssign : PREC.assign,
+ TOK.leftShiftAssign : PREC.assign,
+ TOK.rightShiftAssign : PREC.assign,
+ TOK.unsignedRightShiftAssign : PREC.assign,
+ TOK.andAssign : PREC.assign,
+ TOK.orAssign : PREC.assign,
+ TOK.xorAssign : PREC.assign,
+
+ TOK.comma : PREC.expr,
+ TOK.declaration : PREC.expr,
+
+ TOK.interval : PREC.assign,
+];
+
+enum ParseStatementFlags : int
+{
+ semi = 1, // empty ';' statements are allowed, but deprecated
+ scope_ = 2, // start a new scope
+ curly = 4, // { } statement is required
+ curlyScope = 8, // { } starts a new scope
+ semiOk = 0x10, // empty ';' are really ok
+}
+
+struct PrefixAttributes(AST)
+{
+ StorageClass storageClass;
+ AST.Expression depmsg;
+ LINK link;
+ AST.Visibility visibility;
+ bool setAlignment;
+ AST.Expression ealign;
+ AST.Expressions* udas;
+ const(char)* comment;
+}
+
+/// The result of the `ParseLinkage` function
+struct ParsedLinkage(AST)
+{
+ /// What linkage was specified
+ LINK link;
+ /// If `extern(C++, class|struct)`, contains the `class|struct`
+ CPPMANGLE cppmangle;
+ /// If `extern(C++, some.identifier)`, will be the identifiers
+ AST.Identifiers* idents;
+ /// If `extern(C++, (some_tuple_expression)|"string"), will be the expressions
+ AST.Expressions* identExps;
+}
+
+/*****************************
+ * Destructively extract storage class from pAttrs.
+ */
+private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs)
+{
+ StorageClass stc = STC.undefined_;
+ if (pAttrs)
+ {
+ stc = pAttrs.storageClass;
+ pAttrs.storageClass = STC.undefined_;
+ }
+ return stc;
+}
+
+/**************************************
+ * dump mixin expansion to file for better debugging
+ */
+private bool writeMixin(const(char)[] s, ref Loc loc)
+{
+ if (!global.params.mixinOut)
+ return false;
+
+ OutBuffer* ob = global.params.mixinOut;
+
+ ob.writestring("// expansion at ");
+ ob.writestring(loc.toChars());
+ ob.writenl();
+
+ global.params.mixinLines++;
+
+ loc = Loc(global.params.mixinFile, global.params.mixinLines + 1, loc.charnum);
+
+ // write by line to create consistent line endings
+ size_t lastpos = 0;
+ for (size_t i = 0; i < s.length; ++i)
+ {
+ // detect LF and CRLF
+ const c = s[i];
+ if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n'))
+ {
+ ob.writestring(s[lastpos .. i]);
+ ob.writenl();
+ global.params.mixinLines++;
+ if (c == '\r')
+ ++i;
+ lastpos = i + 1;
+ }
+ }
+
+ if(lastpos < s.length)
+ ob.writestring(s[lastpos .. $]);
+
+ if (s.length == 0 || s[$-1] != '\n')
+ {
+ ob.writenl(); // ensure empty line after expansion
+ global.params.mixinLines++;
+ }
+ ob.writenl();
+ global.params.mixinLines++;
+
+ return true;
+}
+
+/***********************************************************
+ */
+class Parser(AST) : Lexer
+{
+ AST.ModuleDeclaration* md;
+
+ protected
+ {
+ AST.Module mod;
+ LINK linkage;
+ Loc linkLoc;
+ CPPMANGLE cppmangle;
+ Loc endloc; // set to location of last right curly
+ int inBrackets; // inside [] of array index or slice
+ Loc lookingForElse; // location of lonely if looking for an else
+ }
+
+ /*********************
+ * Use this constructor for string mixins.
+ * Input:
+ * loc location in source file of mixin
+ */
+ extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment)
+ {
+ super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
+
+ //printf("Parser::Parser()\n");
+ scanloc = loc;
+
+ if (!writeMixin(input, scanloc) && loc.filename)
+ {
+ /* Create a pseudo-filename for the mixin string, as it may not even exist
+ * in the source file.
+ */
+ char* filename = cast(char*)mem.xmalloc(strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1);
+ sprintf(filename, "%s-mixin-%d", loc.filename, cast(int)loc.linnum);
+ scanloc.filename = filename;
+ }
+
+ mod = _module;
+ linkage = LINK.d;
+ //nextToken(); // start up the scanner
+ }
+
+ extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment)
+ {
+ super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false);
+
+ //printf("Parser::Parser()\n");
+ mod = _module;
+ linkage = LINK.d;
+ //nextToken(); // start up the scanner
+ }
+
+ AST.Dsymbols* parseModule()
+ {
+ const comment = token.blockComment;
+ bool isdeprecated = false;
+ AST.Expression msg = null;
+ AST.Expressions* udas = null;
+ AST.Dsymbols* decldefs;
+ AST.Dsymbol lastDecl = mod; // for attaching ddoc unittests to module decl
+
+ Token* tk;
+ if (skipAttributes(&token, &tk) && tk.value == TOK.module_)
+ {
+ while (token.value != TOK.module_)
+ {
+ switch (token.value)
+ {
+ case TOK.deprecated_:
+ {
+ // deprecated (...) module ...
+ if (isdeprecated)
+ error("there is only one deprecation attribute allowed for module declaration");
+ isdeprecated = true;
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ {
+ check(TOK.leftParenthesis);
+ msg = parseAssignExp();
+ check(TOK.rightParenthesis);
+ }
+ break;
+ }
+ case TOK.at:
+ {
+ AST.Expressions* exps = null;
+ const stc = parseAttribute(exps);
+ if (stc & atAttrGroup)
+ {
+ error("`@%s` attribute for module declaration is not supported", token.toChars());
+ }
+ else
+ {
+ udas = AST.UserAttributeDeclaration.concat(udas, exps);
+ }
+ if (stc)
+ nextToken();
+ break;
+ }
+ default:
+ {
+ error("`module` expected instead of `%s`", token.toChars());
+ nextToken();
+ break;
+ }
+ }
+ }
+ }
+
+ if (udas)
+ {
+ auto a = new AST.Dsymbols();
+ auto udad = new AST.UserAttributeDeclaration(udas, a);
+ mod.userAttribDecl = udad;
+ }
+
+ // ModuleDeclation leads off
+ if (token.value == TOK.module_)
+ {
+ const loc = token.loc;
+
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `module`");
+ goto Lerr;
+ }
+
+ Identifier[] a;
+ Identifier id = token.ident;
+
+ while (nextToken() == TOK.dot)
+ {
+ a ~= id;
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `package`");
+ goto Lerr;
+ }
+ id = token.ident;
+ }
+
+ md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
+
+ if (token.value != TOK.semicolon)
+ error("`;` expected following module declaration instead of `%s`", token.toChars());
+ nextToken();
+ addComment(mod, comment);
+ }
+
+ decldefs = parseDeclDefs(0, &lastDecl);
+ if (token.value != TOK.endOfFile)
+ {
+ error(token.loc, "unrecognized declaration");
+ goto Lerr;
+ }
+ return decldefs;
+
+ Lerr:
+ while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
+ nextToken();
+ nextToken();
+ return new AST.Dsymbols();
+ }
+
+ final:
+
+ /**
+ * Parses a `deprecated` declaration
+ *
+ * Params:
+ * msg = Deprecated message, if any.
+ * Used to support overriding a deprecated storage class with
+ * a deprecated declaration with a message, but to error
+ * if both declaration have a message.
+ *
+ * Returns:
+ * Whether the deprecated declaration has a message
+ */
+ private bool parseDeprecatedAttribute(ref AST.Expression msg)
+ {
+ if (peekNext() != TOK.leftParenthesis)
+ return false;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ AST.Expression e = parseAssignExp();
+ check(TOK.rightParenthesis);
+ if (msg)
+ {
+ error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars());
+ }
+ msg = e;
+ return true;
+ }
+
+ AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
+ {
+ AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
+ if (!pLastDecl)
+ pLastDecl = &lastDecl;
+
+ const linksave = linkage; // save global state
+
+ //printf("Parser::parseDeclDefs()\n");
+ auto decldefs = new AST.Dsymbols();
+ do
+ {
+ // parse result
+ AST.Dsymbol s = null;
+ AST.Dsymbols* a = null;
+
+ PrefixAttributes!AST attrs;
+ if (!once || !pAttrs)
+ {
+ pAttrs = &attrs;
+ pAttrs.comment = token.blockComment.ptr;
+ }
+ AST.Visibility.Kind prot;
+ StorageClass stc;
+ AST.Condition condition;
+
+ linkage = linksave;
+
+ Loc startloc;
+
+ switch (token.value)
+ {
+ case TOK.enum_:
+ {
+ /* Determine if this is a manifest constant declaration,
+ * or a conventional enum.
+ */
+ const tv = peekNext();
+ if (tv == TOK.leftCurly || tv == TOK.colon)
+ s = parseEnum();
+ else if (tv != TOK.identifier)
+ goto Ldeclaration;
+ else
+ {
+ const nextv = peekNext2();
+ if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
+ s = parseEnum();
+ else
+ goto Ldeclaration;
+ }
+ break;
+ }
+ case TOK.import_:
+ a = parseImport();
+ // keep pLastDecl
+ break;
+
+ case TOK.template_:
+ s = cast(AST.Dsymbol)parseTemplateDeclaration();
+ break;
+
+ case TOK.mixin_:
+ {
+ const loc = token.loc;
+ switch (peekNext())
+ {
+ case TOK.leftParenthesis:
+ {
+ // mixin(string)
+ nextToken();
+ auto exps = parseArguments();
+ check(TOK.semicolon);
+ s = new AST.CompileDeclaration(loc, exps);
+ break;
+ }
+ case TOK.template_:
+ // mixin template
+ nextToken();
+ s = cast(AST.Dsymbol)parseTemplateDeclaration(true);
+ break;
+
+ default:
+ s = parseMixin();
+ break;
+ }
+ break;
+ }
+ case TOK.wchar_:
+ case TOK.dchar_:
+ case TOK.bool_:
+ case TOK.char_:
+ case TOK.int8:
+ case TOK.uns8:
+ case TOK.int16:
+ case TOK.uns16:
+ case TOK.int32:
+ case TOK.uns32:
+ case TOK.int64:
+ case TOK.uns64:
+ case TOK.int128:
+ case TOK.uns128:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.float80:
+ case TOK.imaginary32:
+ case TOK.imaginary64:
+ case TOK.imaginary80:
+ case TOK.complex32:
+ case TOK.complex64:
+ case TOK.complex80:
+ case TOK.void_:
+ case TOK.alias_:
+ case TOK.identifier:
+ case TOK.super_:
+ case TOK.typeof_:
+ case TOK.dot:
+ case TOK.vector:
+ case TOK.struct_:
+ case TOK.union_:
+ case TOK.class_:
+ case TOK.interface_:
+ case TOK.traits:
+ Ldeclaration:
+ a = parseDeclarations(false, pAttrs, pAttrs.comment);
+ if (a && a.dim)
+ *pLastDecl = (*a)[a.dim - 1];
+ break;
+
+ case TOK.this_:
+ if (peekNext() == TOK.dot)
+ goto Ldeclaration;
+ s = parseCtor(pAttrs);
+ break;
+
+ case TOK.tilde:
+ s = parseDtor(pAttrs);
+ break;
+
+ case TOK.invariant_:
+ const tv = peekNext();
+ if (tv == TOK.leftParenthesis || tv == TOK.leftCurly)
+ {
+ // invariant { statements... }
+ // invariant() { statements... }
+ // invariant (expression);
+ s = parseInvariant(pAttrs);
+ break;
+ }
+ error("invariant body expected, not `%s`", token.toChars());
+ goto Lerror;
+
+ case TOK.unittest_:
+ if (global.params.useUnitTests || global.params.doDocComments || global.params.doHdrGeneration)
+ {
+ s = parseUnitTest(pAttrs);
+ if (*pLastDecl)
+ (*pLastDecl).ddocUnittest = cast(AST.UnitTestDeclaration)s;
+ }
+ else
+ {
+ // Skip over unittest block by counting { }
+ Loc loc = token.loc;
+ int braces = 0;
+ while (1)
+ {
+ nextToken();
+ switch (token.value)
+ {
+ case TOK.leftCurly:
+ ++braces;
+ continue;
+
+ case TOK.rightCurly:
+ if (--braces)
+ continue;
+ nextToken();
+ break;
+
+ case TOK.endOfFile:
+ /* { */
+ error(loc, "closing `}` of unittest not found before end of file");
+ goto Lerror;
+
+ default:
+ continue;
+ }
+ break;
+ }
+ // Workaround 14894. Add an empty unittest declaration to keep
+ // the number of symbols in this scope independent of -unittest.
+ s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null);
+ }
+ break;
+
+ case TOK.new_:
+ s = parseNew(pAttrs);
+ break;
+
+ case TOK.colon:
+ case TOK.leftCurly:
+ error("declaration expected, not `%s`", token.toChars());
+ goto Lerror;
+
+ case TOK.rightCurly:
+ case TOK.endOfFile:
+ if (once)
+ error("declaration expected, not `%s`", token.toChars());
+ return decldefs;
+
+ case TOK.static_:
+ {
+ const next = peekNext();
+ if (next == TOK.this_)
+ s = parseStaticCtor(pAttrs);
+ else if (next == TOK.tilde)
+ s = parseStaticDtor(pAttrs);
+ else if (next == TOK.assert_)
+ s = parseStaticAssert();
+ else if (next == TOK.if_)
+ {
+ const Loc loc = token.loc;
+ condition = parseStaticIfCondition();
+ AST.Dsymbols* athen;
+ if (token.value == TOK.colon)
+ athen = parseBlock(pLastDecl);
+ else
+ {
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = token.loc;
+ athen = parseBlock(pLastDecl);
+ lookingForElse = lookingForElseSave;
+ }
+ AST.Dsymbols* aelse = null;
+ if (token.value == TOK.else_)
+ {
+ const elseloc = token.loc;
+ nextToken();
+ aelse = parseBlock(pLastDecl);
+ checkDanglingElse(elseloc);
+ }
+ s = new AST.StaticIfDeclaration(loc, condition, athen, aelse);
+ }
+ else if (next == TOK.import_)
+ {
+ a = parseImport();
+ // keep pLastDecl
+ }
+ else if (next == TOK.foreach_ || next == TOK.foreach_reverse_)
+ {
+ s = parseForeach!(AST.StaticForeachDeclaration)(token.loc, pLastDecl);
+ }
+ else
+ {
+ stc = STC.static_;
+ goto Lstc;
+ }
+ break;
+ }
+ case TOK.const_:
+ if (peekNext() == TOK.leftParenthesis)
+ goto Ldeclaration;
+ stc = STC.const_;
+ goto Lstc;
+
+ case TOK.immutable_:
+ if (peekNext() == TOK.leftParenthesis)
+ goto Ldeclaration;
+ stc = STC.immutable_;
+ goto Lstc;
+
+ case TOK.shared_:
+ {
+ const next = peekNext();
+ if (next == TOK.leftParenthesis)
+ goto Ldeclaration;
+ if (next == TOK.static_)
+ {
+ TOK next2 = peekNext2();
+ if (next2 == TOK.this_)
+ {
+ s = parseSharedStaticCtor(pAttrs);
+ break;
+ }
+ if (next2 == TOK.tilde)
+ {
+ s = parseSharedStaticDtor(pAttrs);
+ break;
+ }
+ }
+ stc = STC.shared_;
+ goto Lstc;
+ }
+ case TOK.inout_:
+ if (peekNext() == TOK.leftParenthesis)
+ goto Ldeclaration;
+ stc = STC.wild;
+ goto Lstc;
+
+ case TOK.final_:
+ stc = STC.final_;
+ goto Lstc;
+
+ case TOK.auto_:
+ stc = STC.auto_;
+ goto Lstc;
+
+ case TOK.scope_:
+ stc = STC.scope_;
+ goto Lstc;
+
+ case TOK.override_:
+ stc = STC.override_;
+ goto Lstc;
+
+ case TOK.abstract_:
+ stc = STC.abstract_;
+ goto Lstc;
+
+ case TOK.synchronized_:
+ stc = STC.synchronized_;
+ goto Lstc;
+
+ case TOK.nothrow_:
+ stc = STC.nothrow_;
+ goto Lstc;
+
+ case TOK.pure_:
+ stc = STC.pure_;
+ goto Lstc;
+
+ case TOK.ref_:
+ stc = STC.ref_;
+ goto Lstc;
+
+ case TOK.gshared:
+ stc = STC.gshared;
+ goto Lstc;
+
+ case TOK.at:
+ {
+ AST.Expressions* exps = null;
+ stc = parseAttribute(exps);
+ if (stc)
+ goto Lstc; // it's a predefined attribute
+ // no redundant/conflicting check for UDAs
+ pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
+ goto Lautodecl;
+ }
+ Lstc:
+ pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc);
+ nextToken();
+
+ Lautodecl:
+
+ /* Look for auto initializers:
+ * storage_class identifier = initializer;
+ * storage_class identifier(...) = initializer;
+ */
+ if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
+ {
+ a = parseAutoDeclarations(getStorageClass!AST(pAttrs), pAttrs.comment);
+ if (a && a.dim)
+ *pLastDecl = (*a)[a.dim - 1];
+ if (pAttrs.udas)
+ {
+ s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
+ pAttrs.udas = null;
+ }
+ break;
+ }
+
+ /* Look for return type inference for template functions.
+ */
+ Token* tk;
+ if (token.value == TOK.identifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) &&
+ (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ ||
+ tk.value == TOK.out_ || tk.value == TOK.do_ || tk.value == TOK.goesTo ||
+ tk.value == TOK.identifier && tk.ident == Id._body))
+ {
+ // @@@DEPRECATED@@@
+ // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
+ // Deprecated in 2.097 - Can be removed from 2.117
+ // The deprecation period is longer than usual as `body`
+ // was quite widely used.
+ if (tk.value == TOK.identifier && tk.ident == Id._body)
+ deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
+
+ a = parseDeclarations(true, pAttrs, pAttrs.comment);
+ if (a && a.dim)
+ *pLastDecl = (*a)[a.dim - 1];
+ if (pAttrs.udas)
+ {
+ s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
+ pAttrs.udas = null;
+ }
+ break;
+ }
+
+ a = parseBlock(pLastDecl, pAttrs);
+ auto stc2 = getStorageClass!AST(pAttrs);
+ if (stc2 != STC.undefined_)
+ {
+ s = new AST.StorageClassDeclaration(stc2, a);
+ }
+ if (pAttrs.udas)
+ {
+ if (s)
+ {
+ a = new AST.Dsymbols();
+ a.push(s);
+ }
+ s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
+ pAttrs.udas = null;
+ }
+ break;
+
+ case TOK.deprecated_:
+ {
+ stc |= STC.deprecated_;
+ if (!parseDeprecatedAttribute(pAttrs.depmsg))
+ goto Lstc;
+
+ a = parseBlock(pLastDecl, pAttrs);
+ s = new AST.DeprecatedDeclaration(pAttrs.depmsg, a);
+ pAttrs.depmsg = null;
+ break;
+ }
+ case TOK.leftBracket:
+ {
+ if (peekNext() == TOK.rightBracket)
+ error("empty attribute list is not allowed");
+ error("use `@(attributes)` instead of `[attributes]`");
+ AST.Expressions* exps = parseArguments();
+ // no redundant/conflicting check for UDAs
+
+ pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps);
+ a = parseBlock(pLastDecl, pAttrs);
+ if (pAttrs.udas)
+ {
+ s = new AST.UserAttributeDeclaration(pAttrs.udas, a);
+ pAttrs.udas = null;
+ }
+ break;
+ }
+ case TOK.extern_:
+ {
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.extern_;
+ goto Lstc;
+ }
+
+ const linkLoc = token.loc;
+ auto res = parseLinkage();
+ if (pAttrs.link != LINK.default_)
+ {
+ if (pAttrs.link != res.link)
+ {
+ error("conflicting linkage `extern (%s)` and `extern (%s)`", AST.linkageToChars(pAttrs.link), AST.linkageToChars(res.link));
+ }
+ else if (res.idents || res.identExps || res.cppmangle != CPPMANGLE.def)
+ {
+ // Allow:
+ // extern(C++, foo) extern(C++, bar) void foo();
+ // to be equivalent with:
+ // extern(C++, foo.bar) void foo();
+ // Allow also:
+ // extern(C++, "ns") extern(C++, class) struct test {}
+ // extern(C++, class) extern(C++, "ns") struct test {}
+ }
+ else
+ error("redundant linkage `extern (%s)`", AST.linkageToChars(pAttrs.link));
+ }
+ pAttrs.link = res.link;
+ this.linkage = res.link;
+ this.linkLoc = linkLoc;
+ a = parseBlock(pLastDecl, pAttrs);
+ if (res.idents)
+ {
+ assert(res.link == LINK.cpp);
+ assert(res.idents.dim);
+ for (size_t i = res.idents.dim; i;)
+ {
+ Identifier id = (*res.idents)[--i];
+ if (s)
+ {
+ a = new AST.Dsymbols();
+ a.push(s);
+ }
+ s = new AST.Nspace(linkLoc, id, null, a);
+ }
+ pAttrs.link = LINK.default_;
+ }
+ else if (res.identExps)
+ {
+ assert(res.link == LINK.cpp);
+ assert(res.identExps.dim);
+ for (size_t i = res.identExps.dim; i;)
+ {
+ AST.Expression exp = (*res.identExps)[--i];
+ if (s)
+ {
+ a = new AST.Dsymbols();
+ a.push(s);
+ }
+ s = new AST.CPPNamespaceDeclaration(linkLoc, exp, a);
+ }
+ pAttrs.link = LINK.default_;
+ }
+ else if (res.cppmangle != CPPMANGLE.def)
+ {
+ assert(res.link == LINK.cpp);
+ s = new AST.CPPMangleDeclaration(linkLoc, res.cppmangle, a);
+ }
+ else if (pAttrs.link != LINK.default_)
+ {
+ s = new AST.LinkDeclaration(linkLoc, pAttrs.link, a);
+ pAttrs.link = LINK.default_;
+ }
+ break;
+ }
+
+ case TOK.private_:
+ prot = AST.Visibility.Kind.private_;
+ goto Lprot;
+
+ case TOK.package_:
+ prot = AST.Visibility.Kind.package_;
+ goto Lprot;
+
+ case TOK.protected_:
+ prot = AST.Visibility.Kind.protected_;
+ goto Lprot;
+
+ case TOK.public_:
+ prot = AST.Visibility.Kind.public_;
+ goto Lprot;
+
+ case TOK.export_:
+ prot = AST.Visibility.Kind.export_;
+ goto Lprot;
+ Lprot:
+ {
+ if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
+ {
+ if (pAttrs.visibility.kind != prot)
+ error("conflicting visibility attribute `%s` and `%s`", AST.visibilityToChars(pAttrs.visibility.kind), AST.visibilityToChars(prot));
+ else
+ error("redundant visibility attribute `%s`", AST.visibilityToChars(prot));
+ }
+ pAttrs.visibility.kind = prot;
+
+ nextToken();
+
+ // optional qualified package identifier to bind
+ // visibility to
+ Identifier[] pkg_prot_idents;
+ if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && token.value == TOK.leftParenthesis)
+ {
+ pkg_prot_idents = parseQualifiedIdentifier("protection package");
+ if (pkg_prot_idents)
+ check(TOK.rightParenthesis);
+ else
+ {
+ while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
+ nextToken();
+ nextToken();
+ break;
+ }
+ }
+
+ const attrloc = token.loc;
+ a = parseBlock(pLastDecl, pAttrs);
+ if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined)
+ {
+ if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && pkg_prot_idents)
+ s = new AST.VisibilityDeclaration(attrloc, pkg_prot_idents, a);
+ else
+ s = new AST.VisibilityDeclaration(attrloc, pAttrs.visibility, a);
+
+ pAttrs.visibility = AST.Visibility(AST.Visibility.Kind.undefined);
+ }
+ break;
+ }
+ case TOK.align_:
+ {
+ const attrLoc = token.loc;
+
+ nextToken();
+
+ AST.Expression e = null; // default
+ if (token.value == TOK.leftParenthesis)
+ {
+ nextToken();
+ e = parseAssignExp();
+ check(TOK.rightParenthesis);
+ }
+
+ if (pAttrs.setAlignment)
+ {
+ if (e)
+ error("redundant alignment attribute `align(%s)`", e.toChars());
+ else
+ error("redundant alignment attribute `align`");
+ }
+
+ pAttrs.setAlignment = true;
+ pAttrs.ealign = e;
+ a = parseBlock(pLastDecl, pAttrs);
+ if (pAttrs.setAlignment)
+ {
+ s = new AST.AlignDeclaration(attrLoc, pAttrs.ealign, a);
+ pAttrs.setAlignment = false;
+ pAttrs.ealign = null;
+ }
+ break;
+ }
+ case TOK.pragma_:
+ {
+ AST.Expressions* args = null;
+ const loc = token.loc;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ if (token.value != TOK.identifier)
+ {
+ error("`pragma(identifier)` expected");
+ goto Lerror;
+ }
+ Identifier ident = token.ident;
+ nextToken();
+ if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
+ args = parseArguments(); // pragma(identifier, args...)
+ else
+ check(TOK.rightParenthesis); // pragma(identifier)
+
+ AST.Dsymbols* a2 = null;
+ if (token.value == TOK.semicolon)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=2354
+ * Accept single semicolon as an empty
+ * DeclarationBlock following attribute.
+ *
+ * Attribute DeclarationBlock
+ * Pragma DeclDef
+ * ;
+ */
+ nextToken();
+ }
+ else
+ a2 = parseBlock(pLastDecl);
+ s = new AST.PragmaDeclaration(loc, ident, args, a2);
+ break;
+ }
+ case TOK.debug_:
+ startloc = token.loc;
+ nextToken();
+ if (token.value == TOK.assign)
+ {
+ nextToken();
+ if (token.value == TOK.identifier)
+ s = new AST.DebugSymbol(token.loc, token.ident);
+ else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
+ s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue);
+ else
+ {
+ error("identifier or integer expected, not `%s`", token.toChars());
+ s = null;
+ }
+ nextToken();
+ if (token.value != TOK.semicolon)
+ error("semicolon expected");
+ nextToken();
+ break;
+ }
+
+ condition = parseDebugCondition();
+ goto Lcondition;
+
+ case TOK.version_:
+ startloc = token.loc;
+ nextToken();
+ if (token.value == TOK.assign)
+ {
+ nextToken();
+ if (token.value == TOK.identifier)
+ s = new AST.VersionSymbol(token.loc, token.ident);
+ else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
+ s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue);
+ else
+ {
+ error("identifier or integer expected, not `%s`", token.toChars());
+ s = null;
+ }
+ nextToken();
+ if (token.value != TOK.semicolon)
+ error("semicolon expected");
+ nextToken();
+ break;
+ }
+ condition = parseVersionCondition();
+ goto Lcondition;
+
+ Lcondition:
+ {
+ AST.Dsymbols* athen;
+ if (token.value == TOK.colon)
+ athen = parseBlock(pLastDecl);
+ else
+ {
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = token.loc;
+ athen = parseBlock(pLastDecl);
+ lookingForElse = lookingForElseSave;
+ }
+ AST.Dsymbols* aelse = null;
+ if (token.value == TOK.else_)
+ {
+ const elseloc = token.loc;
+ nextToken();
+ aelse = parseBlock(pLastDecl);
+ checkDanglingElse(elseloc);
+ }
+ s = new AST.ConditionalDeclaration(startloc, condition, athen, aelse);
+ break;
+ }
+ case TOK.semicolon:
+ // empty declaration
+ //error("empty declaration");
+ nextToken();
+ continue;
+
+ default:
+ error("declaration expected, not `%s`", token.toChars());
+ Lerror:
+ while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
+ nextToken();
+ nextToken();
+ s = null;
+ continue;
+ }
+
+ if (s)
+ {
+ if (!s.isAttribDeclaration())
+ *pLastDecl = s;
+ decldefs.push(s);
+ addComment(s, pAttrs.comment);
+ }
+ else if (a && a.dim)
+ {
+ decldefs.append(a);
+ }
+ }
+ while (!once);
+
+ linkage = linksave;
+
+ return decldefs;
+ }
+
+ /*****************************************
+ * Parse auto declarations of the form:
+ * storageClass ident = init, ident = init, ... ;
+ * and return the array of them.
+ * Starts with token on the first ident.
+ * Ends with scanner past closing ';'
+ */
+ private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment)
+ {
+ //printf("parseAutoDeclarations\n");
+ auto a = new AST.Dsymbols();
+
+ while (1)
+ {
+ const loc = token.loc;
+ Identifier ident = token.ident;
+ nextToken(); // skip over ident
+
+ AST.TemplateParameters* tpl = null;
+ if (token.value == TOK.leftParenthesis)
+ tpl = parseTemplateParameterList();
+
+ check(TOK.assign); // skip over '='
+ AST.Initializer _init = parseInitializer();
+ auto v = new AST.VarDeclaration(loc, null, ident, _init, storageClass);
+
+ AST.Dsymbol s = v;
+ if (tpl)
+ {
+ auto a2 = new AST.Dsymbols();
+ a2.push(v);
+ auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
+ s = tempdecl;
+ }
+ a.push(s);
+ switch (token.value)
+ {
+ case TOK.semicolon:
+ nextToken();
+ addComment(s, comment);
+ break;
+
+ case TOK.comma:
+ nextToken();
+ if (!(token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign)))
+ {
+ error("identifier expected following comma");
+ break;
+ }
+ addComment(s, comment);
+ continue;
+
+ default:
+ error("semicolon expected following auto declaration, not `%s`", token.toChars());
+ break;
+ }
+ break;
+ }
+ return a;
+ }
+
+ /********************************************
+ * Parse declarations after an align, visibility, or extern decl.
+ */
+ private AST.Dsymbols* parseBlock(AST.Dsymbol* pLastDecl, PrefixAttributes!AST* pAttrs = null)
+ {
+ AST.Dsymbols* a = null;
+
+ //printf("parseBlock()\n");
+ switch (token.value)
+ {
+ case TOK.semicolon:
+ error("declaration expected following attribute, not `;`");
+ nextToken();
+ break;
+
+ case TOK.endOfFile:
+ error("declaration expected following attribute, not end of file");
+ break;
+
+ case TOK.leftCurly:
+ {
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = Loc();
+
+ nextToken();
+ a = parseDeclDefs(0, pLastDecl);
+ if (token.value != TOK.rightCurly)
+ {
+ /* { */
+ error("matching `}` expected, not `%s`", token.toChars());
+ }
+ else
+ nextToken();
+ lookingForElse = lookingForElseSave;
+ break;
+ }
+ case TOK.colon:
+ nextToken();
+ a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket
+ break;
+
+ default:
+ a = parseDeclDefs(1, pLastDecl, pAttrs);
+ break;
+ }
+ return a;
+ }
+
+ /**
+ * Provide an error message if `added` contains storage classes which are
+ * redundant with those in `orig`; otherwise, return the combination.
+ *
+ * Params:
+ * orig = The already applied storage class.
+ * added = The new storage class to add to `orig`.
+ *
+ * Returns:
+ * The combination of both storage classes (`orig | added`).
+ */
+ private StorageClass appendStorageClass(StorageClass orig, StorageClass added)
+ {
+ if (orig & added)
+ {
+ OutBuffer buf;
+ AST.stcToBuffer(&buf, added);
+ error("redundant attribute `%s`", buf.peekChars());
+ return orig | added;
+ }
+
+ const Redundant = (STC.const_ | STC.scope_ |
+ (global.params.previewIn ? STC.ref_ : 0));
+ orig |= added;
+
+ if ((orig & STC.in_) && (added & Redundant))
+ {
+ if (added & STC.const_)
+ error("attribute `const` is redundant with previously-applied `in`");
+ else if (global.params.previewIn)
+ {
+ error("attribute `%s` is redundant with previously-applied `in`",
+ (orig & STC.scope_) ? "scope".ptr : "ref".ptr);
+ }
+ else
+ error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead");
+ return orig;
+ }
+
+ if ((added & STC.in_) && (orig & Redundant))
+ {
+ if (orig & STC.const_)
+ error("attribute `in` cannot be added after `const`: remove `const`");
+ else if (global.params.previewIn)
+ {
+ // Windows `printf` does not support `%1$s`
+ const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr;
+ error("attribute `in` cannot be added after `%s`: remove `%s`",
+ stc_str, stc_str);
+ }
+ else
+ error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`");
+ return orig;
+ }
+
+ if (added & (STC.const_ | STC.immutable_ | STC.manifest))
+ {
+ StorageClass u = orig & (STC.const_ | STC.immutable_ | STC.manifest);
+ if (u & (u - 1))
+ error("conflicting attribute `%s`", Token.toChars(token.value));
+ }
+ if (added & (STC.gshared | STC.shared_ | STC.tls))
+ {
+ StorageClass u = orig & (STC.gshared | STC.shared_ | STC.tls);
+ if (u & (u - 1))
+ error("conflicting attribute `%s`", Token.toChars(token.value));
+ }
+ if (added & STC.safeGroup)
+ {
+ StorageClass u = orig & STC.safeGroup;
+ if (u & (u - 1))
+ error("conflicting attribute `@%s`", token.toChars());
+ }
+
+ return orig;
+ }
+
+ /***********************************************
+ * Parse attribute(s), lexer is on '@'.
+ *
+ * Attributes can be builtin (e.g. `@safe`, `@nogc`, etc...),
+ * or be user-defined (UDAs). In the former case, we return the storage
+ * class via the return value, while in thelater case we return `0`
+ * and set `pudas`.
+ *
+ * Params:
+ * pudas = An array of UDAs to append to
+ *
+ * Returns:
+ * If the attribute is builtin, the return value will be non-zero.
+ * Otherwise, 0 is returned, and `pudas` will be appended to.
+ */
+ private StorageClass parseAttribute(ref AST.Expressions* udas)
+ {
+ nextToken();
+ if (token.value == TOK.identifier)
+ {
+ // If we find a builtin attribute, we're done, return immediately.
+ if (StorageClass stc = isBuiltinAtAttribute(token.ident))
+ return stc;
+
+ // Allow identifier, template instantiation, or function call
+ // for `@Argument` (single UDA) form.
+ AST.Expression exp = parsePrimaryExp();
+ if (token.value == TOK.leftParenthesis)
+ {
+ const loc = token.loc;
+ exp = new AST.CallExp(loc, exp, parseArguments());
+ }
+
+ if (udas is null)
+ udas = new AST.Expressions();
+ udas.push(exp);
+ return 0;
+ }
+
+ if (token.value == TOK.leftParenthesis)
+ {
+ // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing
+ if (peekNext() == TOK.rightParenthesis)
+ error("empty attribute list is not allowed");
+ udas = AST.UserAttributeDeclaration.concat(udas, parseArguments());
+ return 0;
+ }
+
+ if (token.isKeyword())
+ error("`%s` is a keyword, not an `@` attribute", token.toChars());
+ else
+ error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars());
+
+ return 0;
+ }
+
+ /***********************************************
+ * Parse const/immutable/shared/inout/nothrow/pure postfix
+ */
+ private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas)
+ {
+ while (1)
+ {
+ StorageClass stc;
+ switch (token.value)
+ {
+ case TOK.const_:
+ stc = STC.const_;
+ break;
+
+ case TOK.immutable_:
+ stc = STC.immutable_;
+ break;
+
+ case TOK.shared_:
+ stc = STC.shared_;
+ break;
+
+ case TOK.inout_:
+ stc = STC.wild;
+ break;
+
+ case TOK.nothrow_:
+ stc = STC.nothrow_;
+ break;
+
+ case TOK.pure_:
+ stc = STC.pure_;
+ break;
+
+ case TOK.return_:
+ stc = STC.return_;
+ break;
+
+ case TOK.scope_:
+ stc = STC.scope_;
+ break;
+
+ case TOK.at:
+ {
+ AST.Expressions* udas = null;
+ stc = parseAttribute(udas);
+ if (udas)
+ {
+ if (pudas)
+ *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas);
+ else
+ {
+ // Disallow:
+ // void function() @uda fp;
+ // () @uda { return 1; }
+ error("user-defined attributes cannot appear as postfixes");
+ }
+ continue;
+ }
+ break;
+ }
+ default:
+ return storageClass;
+ }
+ storageClass = appendStorageClass(storageClass, stc);
+ nextToken();
+ }
+ }
+
+ private StorageClass parseTypeCtor()
+ {
+ StorageClass storageClass = STC.undefined_;
+
+ while (1)
+ {
+ if (peekNext() == TOK.leftParenthesis)
+ return storageClass;
+
+ StorageClass stc;
+ switch (token.value)
+ {
+ case TOK.const_:
+ stc = STC.const_;
+ break;
+
+ case TOK.immutable_:
+ stc = STC.immutable_;
+ break;
+
+ case TOK.shared_:
+ stc = STC.shared_;
+ break;
+
+ case TOK.inout_:
+ stc = STC.wild;
+ break;
+
+ default:
+ return storageClass;
+ }
+ storageClass = appendStorageClass(storageClass, stc);
+ nextToken();
+ }
+ }
+
+ /**************************************
+ * Parse constraint.
+ * Constraint is of the form:
+ * if ( ConstraintExpression )
+ */
+ private AST.Expression parseConstraint()
+ {
+ AST.Expression e = null;
+ if (token.value == TOK.if_)
+ {
+ nextToken(); // skip over 'if'
+ check(TOK.leftParenthesis);
+ e = parseExpression();
+ check(TOK.rightParenthesis);
+ }
+ return e;
+ }
+
+ /**************************************
+ * Parse a TemplateDeclaration.
+ */
+ private AST.TemplateDeclaration parseTemplateDeclaration(bool ismixin = false)
+ {
+ AST.TemplateDeclaration tempdecl;
+ Identifier id;
+ AST.TemplateParameters* tpl;
+ AST.Dsymbols* decldefs;
+ AST.Expression constraint = null;
+ const loc = token.loc;
+
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `template`");
+ goto Lerr;
+ }
+ id = token.ident;
+ nextToken();
+ tpl = parseTemplateParameterList();
+ if (!tpl)
+ goto Lerr;
+
+ constraint = parseConstraint();
+
+ if (token.value != TOK.leftCurly)
+ {
+ error("members of template declaration expected");
+ goto Lerr;
+ }
+ decldefs = parseBlock(null);
+
+ tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin);
+ return tempdecl;
+
+ Lerr:
+ return null;
+ }
+
+ /******************************************
+ * Parse template parameter list.
+ * Input:
+ * flag 0: parsing "( list )"
+ * 1: parsing non-empty "list $(RPAREN)"
+ */
+ private AST.TemplateParameters* parseTemplateParameterList(int flag = 0)
+ {
+ auto tpl = new AST.TemplateParameters();
+
+ if (!flag && token.value != TOK.leftParenthesis)
+ {
+ error("parenthesized template parameter list expected following template identifier");
+ goto Lerr;
+ }
+ nextToken();
+
+ // Get array of TemplateParameters
+ if (flag || token.value != TOK.rightParenthesis)
+ {
+ while (token.value != TOK.rightParenthesis)
+ {
+ AST.TemplateParameter tp;
+ Loc loc;
+ Identifier tp_ident = null;
+ AST.Type tp_spectype = null;
+ AST.Type tp_valtype = null;
+ AST.Type tp_defaulttype = null;
+ AST.Expression tp_specvalue = null;
+ AST.Expression tp_defaultvalue = null;
+
+ // Get TemplateParameter
+
+ // First, look ahead to see if it is a TypeParameter or a ValueParameter
+ const tv = peekNext();
+ if (token.value == TOK.alias_)
+ {
+ // AliasParameter
+ nextToken();
+ loc = token.loc; // todo
+ AST.Type spectype = null;
+ if (isDeclaration(&token, NeedDeclaratorId.must, TOK.reserved, null))
+ {
+ spectype = parseType(&tp_ident);
+ }
+ else
+ {
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected for template `alias` parameter");
+ goto Lerr;
+ }
+ tp_ident = token.ident;
+ nextToken();
+ }
+ RootObject spec = null;
+ if (token.value == TOK.colon) // : Type
+ {
+ nextToken();
+ if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
+ spec = parseType();
+ else
+ spec = parseCondExp();
+ }
+ RootObject def = null;
+ if (token.value == TOK.assign) // = Type
+ {
+ nextToken();
+ if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null))
+ def = parseType();
+ else
+ def = parseCondExp();
+ }
+ tp = new AST.TemplateAliasParameter(loc, tp_ident, spectype, spec, def);
+ }
+ else if (tv == TOK.colon || tv == TOK.assign || tv == TOK.comma || tv == TOK.rightParenthesis)
+ {
+ // TypeParameter
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected for template type parameter");
+ goto Lerr;
+ }
+ loc = token.loc;
+ tp_ident = token.ident;
+ nextToken();
+ if (token.value == TOK.colon) // : Type
+ {
+ nextToken();
+ tp_spectype = parseType();
+ }
+ if (token.value == TOK.assign) // = Type
+ {
+ nextToken();
+ tp_defaulttype = parseType();
+ }
+ tp = new AST.TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
+ }
+ else if (token.value == TOK.identifier && tv == TOK.dotDotDot)
+ {
+ // ident...
+ loc = token.loc;
+ tp_ident = token.ident;
+ nextToken();
+ nextToken();
+ tp = new AST.TemplateTupleParameter(loc, tp_ident);
+ }
+ else if (token.value == TOK.this_)
+ {
+ // ThisParameter
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected for template `this` parameter");
+ goto Lerr;
+ }
+ loc = token.loc;
+ tp_ident = token.ident;
+ nextToken();
+ if (token.value == TOK.colon) // : Type
+ {
+ nextToken();
+ tp_spectype = parseType();
+ }
+ if (token.value == TOK.assign) // = Type
+ {
+ nextToken();
+ tp_defaulttype = parseType();
+ }
+ tp = new AST.TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype);
+ }
+ else
+ {
+ // ValueParameter
+ loc = token.loc; // todo
+ tp_valtype = parseType(&tp_ident);
+ if (!tp_ident)
+ {
+ error("identifier expected for template value parameter");
+ tp_ident = Identifier.idPool("error");
+ }
+ if (token.value == TOK.colon) // : CondExpression
+ {
+ nextToken();
+ tp_specvalue = parseCondExp();
+ }
+ if (token.value == TOK.assign) // = CondExpression
+ {
+ nextToken();
+ tp_defaultvalue = parseDefaultInitExp();
+ }
+ tp = new AST.TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue);
+ }
+ tpl.push(tp);
+ if (token.value != TOK.comma)
+ break;
+ nextToken();
+ }
+ }
+ check(TOK.rightParenthesis);
+
+ Lerr:
+ return tpl;
+ }
+
+ /******************************************
+ * Parse template mixin.
+ * mixin Foo;
+ * mixin Foo!(args);
+ * mixin a.b.c!(args).Foo!(args);
+ * mixin Foo!(args) identifier;
+ * mixin typeof(expr).identifier!(args);
+ */
+ private AST.Dsymbol parseMixin()
+ {
+ AST.TemplateMixin tm;
+ Identifier id;
+ AST.Objects* tiargs;
+
+ //printf("parseMixin()\n");
+ const locMixin = token.loc;
+ nextToken(); // skip 'mixin'
+
+ auto loc = token.loc;
+ AST.TypeQualified tqual = null;
+ if (token.value == TOK.dot)
+ {
+ id = Id.empty;
+ }
+ else
+ {
+ if (token.value == TOK.typeof_)
+ {
+ tqual = parseTypeof();
+ check(TOK.dot);
+ }
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected, not `%s`", token.toChars());
+ id = Id.empty;
+ }
+ else
+ id = token.ident;
+ nextToken();
+ }
+
+ while (1)
+ {
+ tiargs = null;
+ if (token.value == TOK.not)
+ {
+ tiargs = parseTemplateArguments();
+ }
+
+ if (tiargs && token.value == TOK.dot)
+ {
+ auto tempinst = new AST.TemplateInstance(loc, id, tiargs);
+ if (!tqual)
+ tqual = new AST.TypeInstance(loc, tempinst);
+ else
+ tqual.addInst(tempinst);
+ tiargs = null;
+ }
+ else
+ {
+ if (!tqual)
+ tqual = new AST.TypeIdentifier(loc, id);
+ else
+ tqual.addIdent(id);
+ }
+
+ if (token.value != TOK.dot)
+ break;
+
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `.` instead of `%s`", token.toChars());
+ break;
+ }
+ loc = token.loc;
+ id = token.ident;
+ nextToken();
+ }
+
+ id = null;
+ if (token.value == TOK.identifier)
+ {
+ id = token.ident;
+ nextToken();
+ }
+
+ tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs);
+ if (token.value != TOK.semicolon)
+ error("`;` expected after `mixin`");
+ nextToken();
+
+ return tm;
+ }
+
+ /******************************************
+ * Parse template arguments.
+ * Input:
+ * current token is opening '!'
+ * Output:
+ * current token is one after closing '$(RPAREN)'
+ */
+ private AST.Objects* parseTemplateArguments()
+ {
+ AST.Objects* tiargs;
+
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ {
+ // ident!(template_arguments)
+ tiargs = parseTemplateArgumentList();
+ }
+ else
+ {
+ // ident!template_argument
+ tiargs = parseTemplateSingleArgument();
+ }
+ if (token.value == TOK.not)
+ {
+ TOK tok = peekNext();
+ if (tok != TOK.is_ && tok != TOK.in_)
+ {
+ error("multiple ! arguments are not allowed");
+ Lagain:
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ parseTemplateArgumentList();
+ else
+ parseTemplateSingleArgument();
+ if (token.value == TOK.not && (tok = peekNext()) != TOK.is_ && tok != TOK.in_)
+ goto Lagain;
+ }
+ }
+ return tiargs;
+ }
+
+ /******************************************
+ * Parse template argument list.
+ * Input:
+ * current token is opening '$(LPAREN)',
+ * or ',' for __traits
+ * Output:
+ * current token is one after closing '$(RPAREN)'
+ */
+ private AST.Objects* parseTemplateArgumentList()
+ {
+ //printf("Parser::parseTemplateArgumentList()\n");
+ auto tiargs = new AST.Objects();
+ TOK endtok = TOK.rightParenthesis;
+ assert(token.value == TOK.leftParenthesis || token.value == TOK.comma);
+ nextToken();
+
+ // Get TemplateArgumentList
+ while (token.value != endtok)
+ {
+ tiargs.push(parseTypeOrAssignExp());
+ if (token.value != TOK.comma)
+ break;
+ nextToken();
+ }
+ check(endtok, "template argument list");
+ return tiargs;
+ }
+
+ /***************************************
+ * Parse a Type or an Expression
+ * Returns:
+ * RootObject representing the AST
+ */
+ RootObject parseTypeOrAssignExp(TOK endtoken = TOK.reserved)
+ {
+ return isDeclaration(&token, NeedDeclaratorId.no, endtoken, null)
+ ? parseType() // argument is a type
+ : parseAssignExp(); // argument is an expression
+ }
+
+ /*****************************
+ * Parse single template argument, to support the syntax:
+ * foo!arg
+ * Input:
+ * current token is the arg
+ */
+ private AST.Objects* parseTemplateSingleArgument()
+ {
+ //printf("parseTemplateSingleArgument()\n");
+ auto tiargs = new AST.Objects();
+ AST.Type ta;
+ switch (token.value)
+ {
+ case TOK.identifier:
+ ta = new AST.TypeIdentifier(token.loc, token.ident);
+ goto LabelX;
+
+ case TOK.vector:
+ ta = parseVector();
+ goto LabelX;
+
+ case TOK.void_:
+ ta = AST.Type.tvoid;
+ goto LabelX;
+
+ case TOK.int8:
+ ta = AST.Type.tint8;
+ goto LabelX;
+
+ case TOK.uns8:
+ ta = AST.Type.tuns8;
+ goto LabelX;
+
+ case TOK.int16:
+ ta = AST.Type.tint16;
+ goto LabelX;
+
+ case TOK.uns16:
+ ta = AST.Type.tuns16;
+ goto LabelX;
+
+ case TOK.int32:
+ ta = AST.Type.tint32;
+ goto LabelX;
+
+ case TOK.uns32:
+ ta = AST.Type.tuns32;
+ goto LabelX;
+
+ case TOK.int64:
+ ta = AST.Type.tint64;
+ goto LabelX;
+
+ case TOK.uns64:
+ ta = AST.Type.tuns64;
+ goto LabelX;
+
+ case TOK.int128:
+ ta = AST.Type.tint128;
+ goto LabelX;
+
+ case TOK.uns128:
+ ta = AST.Type.tuns128;
+ goto LabelX;
+
+ case TOK.float32:
+ ta = AST.Type.tfloat32;
+ goto LabelX;
+
+ case TOK.float64:
+ ta = AST.Type.tfloat64;
+ goto LabelX;
+
+ case TOK.float80:
+ ta = AST.Type.tfloat80;
+ goto LabelX;
+
+ case TOK.imaginary32:
+ ta = AST.Type.timaginary32;
+ goto LabelX;
+
+ case TOK.imaginary64:
+ ta = AST.Type.timaginary64;
+ goto LabelX;
+
+ case TOK.imaginary80:
+ ta = AST.Type.timaginary80;
+ goto LabelX;
+
+ case TOK.complex32:
+ ta = AST.Type.tcomplex32;
+ goto LabelX;
+
+ case TOK.complex64:
+ ta = AST.Type.tcomplex64;
+ goto LabelX;
+
+ case TOK.complex80:
+ ta = AST.Type.tcomplex80;
+ goto LabelX;
+
+ case TOK.bool_:
+ ta = AST.Type.tbool;
+ goto LabelX;
+
+ case TOK.char_:
+ ta = AST.Type.tchar;
+ goto LabelX;
+
+ case TOK.wchar_:
+ ta = AST.Type.twchar;
+ goto LabelX;
+
+ case TOK.dchar_:
+ ta = AST.Type.tdchar;
+ goto LabelX;
+ LabelX:
+ tiargs.push(ta);
+ nextToken();
+ break;
+
+ case TOK.int32Literal:
+ case TOK.uns32Literal:
+ case TOK.int64Literal:
+ case TOK.uns64Literal:
+ case TOK.int128Literal:
+ case TOK.uns128Literal:
+ case TOK.float32Literal:
+ case TOK.float64Literal:
+ case TOK.float80Literal:
+ case TOK.imaginary32Literal:
+ case TOK.imaginary64Literal:
+ case TOK.imaginary80Literal:
+ case TOK.null_:
+ case TOK.true_:
+ case TOK.false_:
+ case TOK.charLiteral:
+ case TOK.wcharLiteral:
+ case TOK.dcharLiteral:
+ case TOK.string_:
+ case TOK.hexadecimalString:
+ case TOK.file:
+ case TOK.fileFullPath:
+ case TOK.line:
+ case TOK.moduleString:
+ case TOK.functionString:
+ case TOK.prettyFunction:
+ case TOK.this_:
+ {
+ // Template argument is an expression
+ AST.Expression ea = parsePrimaryExp();
+ tiargs.push(ea);
+ break;
+ }
+ default:
+ error("template argument expected following `!`");
+ break;
+ }
+ return tiargs;
+ }
+
+ /**********************************
+ * Parse a static assertion.
+ * Current token is 'static'.
+ */
+ private AST.StaticAssert parseStaticAssert()
+ {
+ const loc = token.loc;
+ AST.Expression exp;
+ AST.Expression msg = null;
+
+ //printf("parseStaticAssert()\n");
+ nextToken();
+ nextToken();
+ check(TOK.leftParenthesis);
+ exp = parseAssignExp();
+ if (token.value == TOK.comma)
+ {
+ nextToken();
+ if (token.value != TOK.rightParenthesis)
+ {
+ msg = parseAssignExp();
+ if (token.value == TOK.comma)
+ nextToken();
+ }
+ }
+ check(TOK.rightParenthesis);
+ check(TOK.semicolon);
+ return new AST.StaticAssert(loc, exp, msg);
+ }
+
+ /***********************************
+ * Parse typeof(expression).
+ * Current token is on the 'typeof'.
+ */
+ private AST.TypeQualified parseTypeof()
+ {
+ AST.TypeQualified t;
+ const loc = token.loc;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ if (token.value == TOK.return_) // typeof(return)
+ {
+ nextToken();
+ t = new AST.TypeReturn(loc);
+ }
+ else
+ {
+ AST.Expression exp = parseExpression(); // typeof(expression)
+ t = new AST.TypeTypeof(loc, exp);
+ }
+ check(TOK.rightParenthesis);
+ return t;
+ }
+
+ /***********************************
+ * Parse __vector(type).
+ * Current token is on the '__vector'.
+ */
+ private AST.Type parseVector()
+ {
+ nextToken();
+ check(TOK.leftParenthesis);
+ AST.Type tb = parseType();
+ check(TOK.rightParenthesis);
+ return new AST.TypeVector(tb);
+ }
+
+ /***********************************
+ * Parse:
+ * extern (linkage)
+ * extern (C++, namespaces)
+ * extern (C++, "namespace", "namespaces", ...)
+ * extern (C++, (StringExp))
+ * The parser is on the 'extern' token.
+ */
+ private ParsedLinkage!(AST) parseLinkage()
+ {
+ ParsedLinkage!(AST) result;
+ nextToken();
+ assert(token.value == TOK.leftParenthesis);
+ nextToken();
+ ParsedLinkage!(AST) returnLinkage(LINK link)
+ {
+ check(TOK.rightParenthesis);
+ result.link = link;
+ return result;
+ }
+ ParsedLinkage!(AST) invalidLinkage()
+ {
+ error("valid linkage identifiers are `D`, `C`, `C++`, `Objective-C`, `Windows`, `System`");
+ return returnLinkage(LINK.d);
+ }
+
+ if (token.value != TOK.identifier)
+ return returnLinkage(LINK.d);
+
+ Identifier id = token.ident;
+ nextToken();
+ if (id == Id.Windows)
+ return returnLinkage(LINK.windows);
+ else if (id == Id.D)
+ return returnLinkage(LINK.d);
+ else if (id == Id.System)
+ return returnLinkage(LINK.system);
+ else if (id == Id.Objective) // Looking for tokens "Objective-C"
+ {
+ if (token.value != TOK.min)
+ return invalidLinkage();
+
+ nextToken();
+ if (token.ident != Id.C)
+ return invalidLinkage();
+
+ nextToken();
+ return returnLinkage(LINK.objc);
+ }
+ else if (id != Id.C)
+ return invalidLinkage();
+
+ if (token.value != TOK.plusPlus)
+ return returnLinkage(LINK.c);
+
+ nextToken();
+ if (token.value != TOK.comma) // , namespaces or class or struct
+ return returnLinkage(LINK.cpp);
+
+ nextToken();
+
+ if (token.value == TOK.rightParenthesis)
+ return returnLinkage(LINK.cpp); // extern(C++,)
+
+ if (token.value == TOK.class_ || token.value == TOK.struct_)
+ {
+ result.cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct;
+ nextToken();
+ }
+ else if (token.value == TOK.identifier) // named scope namespace
+ {
+ result.idents = new AST.Identifiers();
+ while (1)
+ {
+ Identifier idn = token.ident;
+ result.idents.push(idn);
+ nextToken();
+ if (token.value == TOK.dot)
+ {
+ nextToken();
+ if (token.value == TOK.identifier)
+ continue;
+ error("identifier expected for C++ namespace");
+ result.idents = null; // error occurred, invalidate list of elements.
+ }
+ break;
+ }
+ }
+ else // non-scoped StringExp namespace
+ {
+ result.identExps = new AST.Expressions();
+ while (1)
+ {
+ result.identExps.push(parseCondExp());
+ if (token.value != TOK.comma)
+ break;
+ nextToken();
+ // Allow trailing commas as done for argument lists, arrays, ...
+ if (token.value == TOK.rightParenthesis)
+ break;
+ }
+ }
+ return returnLinkage(LINK.cpp);
+ }
+
+ /***********************************
+ * Parse ident1.ident2.ident3
+ *
+ * Params:
+ * entity = what qualified identifier is expected to resolve into.
+ * Used only for better error message
+ *
+ * Returns:
+ * array of identifiers with actual qualified one stored last
+ */
+ private Identifier[] parseQualifiedIdentifier(const(char)* entity)
+ {
+ Identifier[] qualified;
+
+ do
+ {
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("`%s` expected as dot-separated identifiers, got `%s`", entity, token.toChars());
+ return qualified;
+ }
+
+ Identifier id = token.ident;
+ qualified ~= id;
+
+ nextToken();
+ }
+ while (token.value == TOK.dot);
+
+ return qualified;
+ }
+
+ /**************************************
+ * Parse a debug conditional
+ */
+ private AST.Condition parseDebugCondition()
+ {
+ uint level = 1;
+ Identifier id = null;
+ Loc loc = token.loc;
+
+ if (token.value == TOK.leftParenthesis)
+ {
+ nextToken();
+
+ if (token.value == TOK.identifier)
+ id = token.ident;
+ else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
+ level = cast(uint)token.unsvalue;
+ else
+ error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars());
+ loc = token.loc;
+ nextToken();
+ check(TOK.rightParenthesis);
+ }
+ return new AST.DebugCondition(loc, mod, level, id);
+ }
+
+ /**************************************
+ * Parse a version conditional
+ */
+ private AST.Condition parseVersionCondition()
+ {
+ uint level = 1;
+ Identifier id = null;
+ Loc loc;
+
+ if (token.value == TOK.leftParenthesis)
+ {
+ nextToken();
+ /* Allow:
+ * version (unittest)
+ * version (assert)
+ * even though they are keywords
+ */
+ loc = token.loc;
+ if (token.value == TOK.identifier)
+ id = token.ident;
+ else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal)
+ level = cast(uint)token.unsvalue;
+ else if (token.value == TOK.unittest_)
+ id = Identifier.idPool(Token.toString(TOK.unittest_));
+ else if (token.value == TOK.assert_)
+ id = Identifier.idPool(Token.toString(TOK.assert_));
+ else
+ error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars());
+ nextToken();
+ check(TOK.rightParenthesis);
+ }
+ else
+ error("(condition) expected following `version`");
+ return new AST.VersionCondition(loc, mod, level, id);
+ }
+
+ /***********************************************
+ * static if (expression)
+ * body
+ * else
+ * body
+ * Current token is 'static'.
+ */
+ private AST.Condition parseStaticIfCondition()
+ {
+ AST.Expression exp;
+ AST.Condition condition;
+ const loc = token.loc;
+
+ nextToken();
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ {
+ nextToken();
+ exp = parseAssignExp();
+ check(TOK.rightParenthesis);
+ }
+ else
+ {
+ error("(expression) expected following `static if`");
+ exp = null;
+ }
+ condition = new AST.StaticIfCondition(loc, exp);
+ return condition;
+ }
+
+ /*****************************************
+ * Parse a constructor definition:
+ * this(parameters) { body }
+ * or postblit:
+ * this(this) { body }
+ * or constructor template:
+ * this(templateparameters)(parameters) { body }
+ * Current token is 'this'.
+ */
+ private AST.Dsymbol parseCtor(PrefixAttributes!AST* pAttrs)
+ {
+ AST.Expressions* udas = null;
+ const loc = token.loc;
+ StorageClass stc = getStorageClass!AST(pAttrs);
+
+ nextToken();
+ if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis)
+ {
+ // this(this) { ... }
+ nextToken();
+ nextToken();
+ check(TOK.rightParenthesis);
+
+ stc = parsePostfix(stc, &udas);
+ if (stc & STC.immutable_)
+ deprecation("`immutable` postblit is deprecated. Please use an unqualified postblit.");
+ if (stc & STC.shared_)
+ deprecation("`shared` postblit is deprecated. Please use an unqualified postblit.");
+ if (stc & STC.const_)
+ deprecation("`const` postblit is deprecated. Please use an unqualified postblit.");
+ if (stc & STC.static_)
+ error(loc, "postblit cannot be `static`");
+
+ auto f = new AST.PostBlitDeclaration(loc, Loc.initial, stc, Id.postblit);
+ AST.Dsymbol s = parseContracts(f);
+ if (udas)
+ {
+ auto a = new AST.Dsymbols();
+ a.push(f);
+ s = new AST.UserAttributeDeclaration(udas, a);
+ }
+ return s;
+ }
+
+ /* Look ahead to see if:
+ * this(...)(...)
+ * which is a constructor template
+ */
+ AST.TemplateParameters* tpl = null;
+ if (token.value == TOK.leftParenthesis && peekPastParen(&token).value == TOK.leftParenthesis)
+ {
+ tpl = parseTemplateParameterList();
+ }
+
+ /* Just a regular constructor
+ */
+ auto parameterList = parseParameterList(null);
+ stc = parsePostfix(stc, &udas);
+
+ if (parameterList.varargs != VarArg.none || AST.Parameter.dim(parameterList.parameters) != 0)
+ {
+ if (stc & STC.static_)
+ error(loc, "constructor cannot be static");
+ }
+ else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this()
+ {
+ if (ss == STC.static_)
+ error(loc, "use `static this()` to declare a static constructor");
+ else if (ss == (STC.shared_ | STC.static_))
+ error(loc, "use `shared static this()` to declare a shared static constructor");
+ }
+
+ AST.Expression constraint = tpl ? parseConstraint() : null;
+
+ AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto
+ tf = tf.addSTC(stc);
+
+ auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf);
+ AST.Dsymbol s = parseContracts(f);
+ if (udas)
+ {
+ auto a = new AST.Dsymbols();
+ a.push(f);
+ s = new AST.UserAttributeDeclaration(udas, a);
+ }
+
+ if (tpl)
+ {
+ // Wrap a template around it
+ auto decldefs = new AST.Dsymbols();
+ decldefs.push(s);
+ s = new AST.TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs);
+ }
+
+ return s;
+ }
+
+ /*****************************************
+ * Parse a destructor definition:
+ * ~this() { body }
+ * Current token is '~'.
+ */
+ private AST.Dsymbol parseDtor(PrefixAttributes!AST* pAttrs)
+ {
+ AST.Expressions* udas = null;
+ const loc = token.loc;
+ StorageClass stc = getStorageClass!AST(pAttrs);
+
+ nextToken();
+ check(TOK.this_);
+ check(TOK.leftParenthesis);
+ check(TOK.rightParenthesis);
+
+ stc = parsePostfix(stc, &udas);
+ if (StorageClass ss = stc & (STC.shared_ | STC.static_))
+ {
+ if (ss == STC.static_)
+ error(loc, "use `static ~this()` to declare a static destructor");
+ else if (ss == (STC.shared_ | STC.static_))
+ error(loc, "use `shared static ~this()` to declare a shared static destructor");
+ }
+
+ auto f = new AST.DtorDeclaration(loc, Loc.initial, stc, Id.dtor);
+ AST.Dsymbol s = parseContracts(f);
+ if (udas)
+ {
+ auto a = new AST.Dsymbols();
+ a.push(f);
+ s = new AST.UserAttributeDeclaration(udas, a);
+ }
+ return s;
+ }
+
+ /*****************************************
+ * Parse a static constructor definition:
+ * static this() { body }
+ * Current token is 'static'.
+ */
+ private AST.Dsymbol parseStaticCtor(PrefixAttributes!AST* pAttrs)
+ {
+ //Expressions *udas = NULL;
+ const loc = token.loc;
+ StorageClass stc = getStorageClass!AST(pAttrs);
+
+ nextToken();
+ nextToken();
+ check(TOK.leftParenthesis);
+ check(TOK.rightParenthesis);
+
+ stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
+ if (stc & STC.shared_)
+ error(loc, "use `shared static this()` to declare a shared static constructor");
+ else if (stc & STC.static_)
+ appendStorageClass(stc, STC.static_); // complaint for the redundancy
+ else if (StorageClass modStc = stc & STC.TYPECTOR)
+ {
+ OutBuffer buf;
+ AST.stcToBuffer(&buf, modStc);
+ error(loc, "static constructor cannot be `%s`", buf.peekChars());
+ }
+ stc &= ~(STC.static_ | STC.TYPECTOR);
+
+ auto f = new AST.StaticCtorDeclaration(loc, Loc.initial, stc);
+ AST.Dsymbol s = parseContracts(f);
+ return s;
+ }
+
+ /*****************************************
+ * Parse a static destructor definition:
+ * static ~this() { body }
+ * Current token is 'static'.
+ */
+ private AST.Dsymbol parseStaticDtor(PrefixAttributes!AST* pAttrs)
+ {
+ AST.Expressions* udas = null;
+ const loc = token.loc;
+ StorageClass stc = getStorageClass!AST(pAttrs);
+
+ nextToken();
+ nextToken();
+ check(TOK.this_);
+ check(TOK.leftParenthesis);
+ check(TOK.rightParenthesis);
+
+ stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
+ if (stc & STC.shared_)
+ error(loc, "use `shared static ~this()` to declare a shared static destructor");
+ else if (stc & STC.static_)
+ appendStorageClass(stc, STC.static_); // complaint for the redundancy
+ else if (StorageClass modStc = stc & STC.TYPECTOR)
+ {
+ OutBuffer buf;
+ AST.stcToBuffer(&buf, modStc);
+ error(loc, "static destructor cannot be `%s`", buf.peekChars());
+ }
+ stc &= ~(STC.static_ | STC.TYPECTOR);
+
+ auto f = new AST.StaticDtorDeclaration(loc, Loc.initial, stc);
+ AST.Dsymbol s = parseContracts(f);
+ if (udas)
+ {
+ auto a = new AST.Dsymbols();
+ a.push(f);
+ s = new AST.UserAttributeDeclaration(udas, a);
+ }
+ return s;
+ }
+
+ /*****************************************
+ * Parse a shared static constructor definition:
+ * shared static this() { body }
+ * Current token is 'shared'.
+ */
+ private AST.Dsymbol parseSharedStaticCtor(PrefixAttributes!AST* pAttrs)
+ {
+ //Expressions *udas = NULL;
+ const loc = token.loc;
+ StorageClass stc = getStorageClass!AST(pAttrs);
+
+ nextToken();
+ nextToken();
+ nextToken();
+ check(TOK.leftParenthesis);
+ check(TOK.rightParenthesis);
+
+ stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc;
+ if (StorageClass ss = stc & (STC.shared_ | STC.static_))
+ appendStorageClass(stc, ss); // complaint for the redundancy
+ else if (StorageClass modStc = stc & STC.TYPECTOR)
+ {
+ OutBuffer buf;
+ AST.stcToBuffer(&buf, modStc);
+ error(loc, "shared static constructor cannot be `%s`", buf.peekChars());
+ }
+ stc &= ~(STC.static_ | STC.TYPECTOR);
+
+ auto f = new AST.SharedStaticCtorDeclaration(loc, Loc.initial, stc);
+ AST.Dsymbol s = parseContracts(f);
+ return s;
+ }
+
+ /*****************************************
+ * Parse a shared static destructor definition:
+ * shared static ~this() { body }
+ * Current token is 'shared'.
+ */
+ private AST.Dsymbol parseSharedStaticDtor(PrefixAttributes!AST* pAttrs)
+ {
+ AST.Expressions* udas = null;
+ const loc = token.loc;
+ StorageClass stc = getStorageClass!AST(pAttrs);
+
+ nextToken();
+ nextToken();
+ nextToken();
+ check(TOK.this_);
+ check(TOK.leftParenthesis);
+ check(TOK.rightParenthesis);
+
+ stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc;
+ if (StorageClass ss = stc & (STC.shared_ | STC.static_))
+ appendStorageClass(stc, ss); // complaint for the redundancy
+ else if (StorageClass modStc = stc & STC.TYPECTOR)
+ {
+ OutBuffer buf;
+ AST.stcToBuffer(&buf, modStc);
+ error(loc, "shared static destructor cannot be `%s`", buf.peekChars());
+ }
+ stc &= ~(STC.static_ | STC.TYPECTOR);
+
+ auto f = new AST.SharedStaticDtorDeclaration(loc, Loc.initial, stc);
+ AST.Dsymbol s = parseContracts(f);
+ if (udas)
+ {
+ auto a = new AST.Dsymbols();
+ a.push(f);
+ s = new AST.UserAttributeDeclaration(udas, a);
+ }
+ return s;
+ }
+
+ /*****************************************
+ * Parse an invariant definition:
+ * invariant { statements... }
+ * invariant() { statements... }
+ * invariant (expression);
+ * Current token is 'invariant'.
+ */
+ private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs)
+ {
+ const loc = token.loc;
+ StorageClass stc = getStorageClass!AST(pAttrs);
+
+ nextToken();
+ if (token.value == TOK.leftParenthesis) // optional () or invariant (expression);
+ {
+ nextToken();
+ if (token.value != TOK.rightParenthesis) // invariant (expression);
+ {
+ AST.Expression e = parseAssignExp(), msg = null;
+ if (token.value == TOK.comma)
+ {
+ nextToken();
+ if (token.value != TOK.rightParenthesis)
+ {
+ msg = parseAssignExp();
+ if (token.value == TOK.comma)
+ nextToken();
+ }
+ }
+ check(TOK.rightParenthesis);
+ check(TOK.semicolon);
+ e = new AST.AssertExp(loc, e, msg);
+ auto fbody = new AST.ExpStatement(loc, e);
+ auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
+ return f;
+ }
+ nextToken();
+ }
+
+ auto fbody = parseStatement(ParseStatementFlags.curly);
+ auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody);
+ return f;
+ }
+
+ /*****************************************
+ * Parse a unittest definition:
+ * unittest { body }
+ * Current token is 'unittest'.
+ */
+ private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs)
+ {
+ const loc = token.loc;
+ StorageClass stc = getStorageClass!AST(pAttrs);
+
+ nextToken();
+
+ const(char)* begPtr = token.ptr + 1; // skip '{'
+ const(char)* endPtr = null;
+ AST.Statement sbody = parseStatement(ParseStatementFlags.curly, &endPtr);
+
+ /** Extract unittest body as a string. Must be done eagerly since memory
+ will be released by the lexer before doc gen. */
+ char* docline = null;
+ if (global.params.doDocComments && endPtr > begPtr)
+ {
+ /* Remove trailing whitespaces */
+ for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p)
+ {
+ endPtr = p;
+ }
+
+ size_t len = endPtr - begPtr;
+ if (len > 0)
+ {
+ docline = cast(char*)mem.xmalloc_noscan(len + 2);
+ memcpy(docline, begPtr, len);
+ docline[len] = '\n'; // Terminate all lines by LF
+ docline[len + 1] = '\0';
+ }
+ }
+
+ auto f = new AST.UnitTestDeclaration(loc, token.loc, stc, docline);
+ f.fbody = sbody;
+ return f;
+ }
+
+ /*****************************************
+ * Parse a new definition:
+ * @disable new();
+ * Current token is 'new'.
+ */
+ private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs)
+ {
+ const loc = token.loc;
+ StorageClass stc = getStorageClass!AST(pAttrs);
+ if (!(stc & STC.disable))
+ {
+ error("`new` allocator must be annotated with `@disabled`");
+ }
+ nextToken();
+
+ /* @@@DEPRECATED_2.098@@@
+ * After deprecation period (2.108), remove all code in the version(all) block.
+ */
+ version (all)
+ {
+ auto parameterList = parseParameterList(null); // parameterList ignored
+ if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none)
+ deprecation("`new` allocator with non-empty parameter list is deprecated");
+ auto f = new AST.NewDeclaration(loc, stc);
+ if (token.value != TOK.semicolon)
+ {
+ deprecation("`new` allocator with function definition is deprecated");
+ parseContracts(f); // body ignored
+ f.fbody = null;
+ f.fensures = null;
+ f.frequires = null;
+ }
+ else
+ nextToken();
+ return f;
+ }
+ else
+ {
+ check(TOK.leftParenthesis);
+ check(TOK.rightParenthesis);
+ check(TOK.semicolon);
+ return new AST.NewDeclaration(loc, stc);
+ }
+ }
+
+ /**********************************************
+ * Parse parameter list.
+ */
+ private AST.ParameterList parseParameterList(AST.TemplateParameters** tpl)
+ {
+ auto parameters = new AST.Parameters();
+ VarArg varargs = VarArg.none;
+ int hasdefault = 0;
+ StorageClass varargsStc;
+
+ // Attributes allowed for ...
+ enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_;
+
+ check(TOK.leftParenthesis);
+ while (1)
+ {
+ Identifier ai = null;
+ AST.Type at;
+ StorageClass storageClass = 0;
+ StorageClass stc;
+ AST.Expression ae;
+ AST.Expressions* udas = null;
+ for (; 1; nextToken())
+ {
+ L3:
+ switch (token.value)
+ {
+ case TOK.rightParenthesis:
+ if (storageClass != 0 || udas !is null)
+ error("basic type expected, not `)`");
+ break;
+
+ case TOK.dotDotDot:
+ varargs = VarArg.variadic;
+ varargsStc = storageClass;
+ if (varargsStc & ~VarArgsStc)
+ {
+ OutBuffer buf;
+ AST.stcToBuffer(&buf, varargsStc & ~VarArgsStc);
+ error("variadic parameter cannot have attributes `%s`", buf.peekChars());
+ varargsStc &= VarArgsStc;
+ }
+ nextToken();
+ break;
+
+ case TOK.const_:
+ if (peekNext() == TOK.leftParenthesis)
+ goto default;
+ stc = STC.const_;
+ goto L2;
+
+ case TOK.immutable_:
+ if (peekNext() == TOK.leftParenthesis)
+ goto default;
+ stc = STC.immutable_;
+ goto L2;
+
+ case TOK.shared_:
+ if (peekNext() == TOK.leftParenthesis)
+ goto default;
+ stc = STC.shared_;
+ goto L2;
+
+ case TOK.inout_:
+ if (peekNext() == TOK.leftParenthesis)
+ goto default;
+ stc = STC.wild;
+ goto L2;
+ case TOK.at:
+ {
+ AST.Expressions* exps = null;
+ StorageClass stc2 = parseAttribute(exps);
+ if (stc2 & atAttrGroup)
+ {
+ error("`@%s` attribute for function parameter is not supported", token.toChars());
+ }
+ else
+ {
+ udas = AST.UserAttributeDeclaration.concat(udas, exps);
+ }
+ if (token.value == TOK.dotDotDot)
+ error("variadic parameter cannot have user-defined attributes");
+ if (stc2)
+ nextToken();
+ goto L3;
+ // Don't call nextToken again.
+ }
+ case TOK.in_:
+ stc = STC.in_;
+ goto L2;
+
+ case TOK.out_:
+ stc = STC.out_;
+ goto L2;
+
+ case TOK.ref_:
+ stc = STC.ref_;
+ goto L2;
+
+ case TOK.lazy_:
+ stc = STC.lazy_;
+ goto L2;
+
+ case TOK.scope_:
+ stc = STC.scope_;
+ goto L2;
+
+ case TOK.final_:
+ stc = STC.final_;
+ goto L2;
+
+ case TOK.auto_:
+ stc = STC.auto_;
+ goto L2;
+
+ case TOK.return_:
+ stc = STC.return_;
+ goto L2;
+ L2:
+ storageClass = appendStorageClass(storageClass, stc);
+ continue;
+
+ version (none)
+ {
+ case TOK.static_:
+ stc = STC.static_;
+ goto L2;
+
+ case TOK.auto_:
+ storageClass = STC.auto_;
+ goto L4;
+
+ case TOK.alias_:
+ storageClass = STC.alias_;
+ goto L4;
+ L4:
+ nextToken();
+ ai = null;
+ if (token.value == TOK.identifier)
+ {
+ ai = token.ident;
+ nextToken();
+ }
+
+ at = null; // no type
+ ae = null; // no default argument
+ if (token.value == TOK.assign) // = defaultArg
+ {
+ nextToken();
+ ae = parseDefaultInitExp();
+ hasdefault = 1;
+ }
+ else
+ {
+ if (hasdefault)
+ error("default argument expected for `alias %s`", ai ? ai.toChars() : "");
+ }
+ goto L3;
+ }
+ default:
+ {
+ stc = storageClass & (STC.IOR | STC.lazy_);
+ // if stc is not a power of 2
+ if (stc & (stc - 1) && !(stc == (STC.in_ | STC.ref_)))
+ error("incompatible parameter storage classes");
+ //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_)))
+ //error("scope cannot be ref or out");
+
+ if (tpl && token.value == TOK.identifier)
+ {
+ const tv = peekNext();
+ if (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot)
+ {
+ Identifier id = Identifier.generateId("__T");
+ const loc = token.loc;
+ at = new AST.TypeIdentifier(loc, id);
+ if (!*tpl)
+ *tpl = new AST.TemplateParameters();
+ AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
+ (*tpl).push(tp);
+
+ ai = token.ident;
+ nextToken();
+ }
+ else goto _else;
+ }
+ else
+ {
+ _else:
+ at = parseType(&ai);
+ }
+ ae = null;
+ if (token.value == TOK.assign) // = defaultArg
+ {
+ nextToken();
+ ae = parseDefaultInitExp();
+ hasdefault = 1;
+ }
+ else
+ {
+ if (hasdefault)
+ error("default argument expected for `%s`", ai ? ai.toChars() : at.toChars());
+ }
+ auto param = new AST.Parameter(storageClass | STC.parameter, at, ai, ae, null);
+ if (udas)
+ {
+ auto a = new AST.Dsymbols();
+ auto udad = new AST.UserAttributeDeclaration(udas, a);
+ param.userAttribDecl = udad;
+ }
+ if (token.value == TOK.at)
+ {
+ AST.Expressions* exps = null;
+ StorageClass stc2 = parseAttribute(exps);
+ if (stc2 & atAttrGroup)
+ {
+ error("`@%s` attribute for function parameter is not supported", token.toChars());
+ }
+ else
+ {
+ error("user-defined attributes cannot appear as postfixes", token.toChars());
+ }
+ if (stc2)
+ nextToken();
+ }
+ if (token.value == TOK.dotDotDot)
+ {
+ /* This is:
+ * at ai ...
+ */
+ if (storageClass & (STC.out_ | STC.ref_))
+ error("variadic argument cannot be `out` or `ref`");
+ varargs = VarArg.typesafe;
+ parameters.push(param);
+ nextToken();
+ break;
+ }
+ parameters.push(param);
+ if (token.value == TOK.comma)
+ {
+ nextToken();
+ goto L1;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ break;
+
+ L1:
+ }
+ check(TOK.rightParenthesis);
+ return AST.ParameterList(parameters, varargs, varargsStc);
+ }
+
+ /*************************************
+ */
+ private AST.EnumDeclaration parseEnum()
+ {
+ AST.EnumDeclaration e;
+ Identifier id;
+ AST.Type memtype;
+ auto loc = token.loc;
+
+ // printf("Parser::parseEnum()\n");
+ nextToken();
+ id = null;
+ if (token.value == TOK.identifier)
+ {
+ id = token.ident;
+ nextToken();
+ }
+
+ memtype = null;
+ if (token.value == TOK.colon)
+ {
+ nextToken();
+ int alt = 0;
+ const typeLoc = token.loc;
+ memtype = parseBasicType();
+ memtype = parseDeclarator(memtype, alt, null);
+ checkCstyleTypeSyntax(typeLoc, memtype, alt, null);
+ }
+
+ e = new AST.EnumDeclaration(loc, id, memtype);
+ if (token.value == TOK.semicolon && id)
+ nextToken();
+ else if (token.value == TOK.leftCurly)
+ {
+ bool isAnonymousEnum = !id;
+ TOK prevTOK;
+
+ //printf("enum definition\n");
+ e.members = new AST.Dsymbols();
+ nextToken();
+ const(char)[] comment = token.blockComment;
+ while (token.value != TOK.rightCurly)
+ {
+ /* Can take the following forms...
+ * 1. ident
+ * 2. ident = value
+ * 3. type ident = value
+ * ... prefixed by valid attributes
+ */
+ loc = token.loc;
+
+ AST.Type type = null;
+ Identifier ident = null;
+
+ AST.Expressions* udas;
+ StorageClass stc;
+ AST.Expression deprecationMessage;
+ enum attributeErrorMessage = "`%s` is not a valid attribute for enum members";
+ while(token.value != TOK.rightCurly
+ && token.value != TOK.comma
+ && token.value != TOK.assign)
+ {
+ switch(token.value)
+ {
+ case TOK.at:
+ if (StorageClass _stc = parseAttribute(udas))
+ {
+ if (_stc == STC.disable)
+ stc |= _stc;
+ else
+ {
+ OutBuffer buf;
+ AST.stcToBuffer(&buf, _stc);
+ error(attributeErrorMessage, buf.peekChars());
+ }
+ prevTOK = token.value;
+ nextToken();
+ }
+ break;
+ case TOK.deprecated_:
+ stc |= STC.deprecated_;
+ if (!parseDeprecatedAttribute(deprecationMessage))
+ {
+ prevTOK = token.value;
+ nextToken();
+ }
+ break;
+ case TOK.identifier:
+ const tv = peekNext();
+ if (tv == TOK.assign || tv == TOK.comma || tv == TOK.rightCurly)
+ {
+ ident = token.ident;
+ type = null;
+ prevTOK = token.value;
+ nextToken();
+ }
+ else
+ {
+ goto default;
+ }
+ break;
+ default:
+ if (isAnonymousEnum)
+ {
+ type = parseType(&ident, null);
+ if (type == AST.Type.terror)
+ {
+ type = null;
+ prevTOK = token.value;
+ nextToken();
+ }
+ else
+ {
+ prevTOK = TOK.identifier;
+ }
+ }
+ else
+ {
+ error(attributeErrorMessage, token.toChars());
+ prevTOK = token.value;
+ nextToken();
+ }
+ break;
+ }
+ if (token.value == TOK.comma)
+ {
+ prevTOK = token.value;
+ }
+ }
+
+ if (type && type != AST.Type.terror)
+ {
+ if (!ident)
+ error("no identifier for declarator `%s`", type.toChars());
+ if (!isAnonymousEnum)
+ error("type only allowed if anonymous enum and no enum type");
+ }
+ AST.Expression value;
+ if (token.value == TOK.assign)
+ {
+ if (prevTOK == TOK.identifier)
+ {
+ nextToken();
+ value = parseAssignExp();
+ }
+ else
+ {
+ error("assignment must be preceded by an identifier");
+ nextToken();
+ }
+ }
+ else
+ {
+ value = null;
+ if (type && type != AST.Type.terror && isAnonymousEnum)
+ error("if type, there must be an initializer");
+ }
+
+ AST.DeprecatedDeclaration dd;
+ if (deprecationMessage)
+ {
+ dd = new AST.DeprecatedDeclaration(deprecationMessage, null);
+ stc |= STC.deprecated_;
+ }
+
+ auto em = new AST.EnumMember(loc, ident, value, type, stc, null, dd);
+ e.members.push(em);
+
+ if (udas)
+ {
+ auto s = new AST.Dsymbols();
+ s.push(em);
+ auto uad = new AST.UserAttributeDeclaration(udas, s);
+ em.userAttribDecl = uad;
+ }
+
+ if (token.value == TOK.rightCurly)
+ {
+ }
+ else
+ {
+ addComment(em, comment);
+ comment = null;
+ check(TOK.comma);
+ }
+ addComment(em, comment);
+ comment = token.blockComment;
+
+ if (token.value == TOK.endOfFile)
+ {
+ error("premature end of file");
+ break;
+ }
+ }
+ nextToken();
+ }
+ else
+ error("enum declaration is invalid");
+
+ //printf("-parseEnum() %s\n", e.toChars());
+ return e;
+ }
+
+ /********************************
+ * Parse struct, union, interface, class.
+ */
+ private AST.Dsymbol parseAggregate()
+ {
+ AST.TemplateParameters* tpl = null;
+ AST.Expression constraint;
+ const loc = token.loc;
+ TOK tok = token.value;
+
+ //printf("Parser::parseAggregate()\n");
+ nextToken();
+ Identifier id;
+ if (token.value != TOK.identifier)
+ {
+ id = null;
+ }
+ else
+ {
+ id = token.ident;
+ nextToken();
+
+ if (token.value == TOK.leftParenthesis)
+ {
+ // struct/class template declaration.
+ tpl = parseTemplateParameterList();
+ constraint = parseConstraint();
+ }
+ }
+
+ // Collect base class(es)
+ AST.BaseClasses* baseclasses = null;
+ if (token.value == TOK.colon)
+ {
+ if (tok != TOK.interface_ && tok != TOK.class_)
+ error("base classes are not allowed for `%s`, did you mean `;`?", Token.toChars(tok));
+ nextToken();
+ baseclasses = parseBaseClasses();
+ }
+
+ if (token.value == TOK.if_)
+ {
+ if (constraint)
+ error("template constraints appear both before and after BaseClassList, put them before");
+ constraint = parseConstraint();
+ }
+ if (constraint)
+ {
+ if (!id)
+ error("template constraints not allowed for anonymous `%s`", Token.toChars(tok));
+ if (!tpl)
+ error("template constraints only allowed for templates");
+ }
+
+ AST.Dsymbols* members = null;
+ if (token.value == TOK.leftCurly)
+ {
+ //printf("aggregate definition\n");
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = Loc();
+ nextToken();
+ members = parseDeclDefs(0);
+ lookingForElse = lookingForElseSave;
+ if (token.value != TOK.rightCurly)
+ {
+ /* { */
+ error("`}` expected following members in `%s` declaration at %s",
+ Token.toChars(tok), loc.toChars());
+ }
+ nextToken();
+ }
+ else if (token.value == TOK.semicolon && id)
+ {
+ if (baseclasses || constraint)
+ error("members expected");
+ nextToken();
+ }
+ else
+ {
+ error("{ } expected following `%s` declaration", Token.toChars(tok));
+ }
+
+ AST.AggregateDeclaration a;
+ switch (tok)
+ {
+ case TOK.interface_:
+ if (!id)
+ error(loc, "anonymous interfaces not allowed");
+ a = new AST.InterfaceDeclaration(loc, id, baseclasses);
+ a.members = members;
+ break;
+
+ case TOK.class_:
+ if (!id)
+ error(loc, "anonymous classes not allowed");
+ bool inObject = md && !md.packages && md.id == Id.object;
+ a = new AST.ClassDeclaration(loc, id, baseclasses, members, inObject);
+ break;
+
+ case TOK.struct_:
+ if (id)
+ {
+ bool inObject = md && !md.packages && md.id == Id.object;
+ a = new AST.StructDeclaration(loc, id, inObject);
+ a.members = members;
+ }
+ else
+ {
+ /* Anonymous structs/unions are more like attributes.
+ */
+ assert(!tpl);
+ return new AST.AnonDeclaration(loc, false, members);
+ }
+ break;
+
+ case TOK.union_:
+ if (id)
+ {
+ a = new AST.UnionDeclaration(loc, id);
+ a.members = members;
+ }
+ else
+ {
+ /* Anonymous structs/unions are more like attributes.
+ */
+ assert(!tpl);
+ return new AST.AnonDeclaration(loc, true, members);
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+
+ if (tpl)
+ {
+ // Wrap a template around the aggregate declaration
+ auto decldefs = new AST.Dsymbols();
+ decldefs.push(a);
+ auto tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs);
+ return tempdecl;
+ }
+ return a;
+ }
+
+ /*******************************************
+ */
+ private AST.BaseClasses* parseBaseClasses()
+ {
+ auto baseclasses = new AST.BaseClasses();
+
+ for (; 1; nextToken())
+ {
+ auto b = new AST.BaseClass(parseBasicType());
+ baseclasses.push(b);
+ if (token.value != TOK.comma)
+ break;
+ }
+ return baseclasses;
+ }
+
+ private AST.Dsymbols* parseImport()
+ {
+ auto decldefs = new AST.Dsymbols();
+ Identifier aliasid = null;
+
+ int isstatic = token.value == TOK.static_;
+ if (isstatic)
+ nextToken();
+
+ //printf("Parser::parseImport()\n");
+ do
+ {
+ L1:
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `import`");
+ break;
+ }
+
+ const loc = token.loc;
+ Identifier id = token.ident;
+ Identifier[] a;
+ nextToken();
+ if (!aliasid && token.value == TOK.assign)
+ {
+ aliasid = id;
+ goto L1;
+ }
+ while (token.value == TOK.dot)
+ {
+ a ~= id;
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `package`");
+ break;
+ }
+ id = token.ident;
+ nextToken();
+ }
+
+ auto s = new AST.Import(loc, a, id, aliasid, isstatic);
+ decldefs.push(s);
+
+ /* Look for
+ * : alias=name, alias=name;
+ * syntax.
+ */
+ if (token.value == TOK.colon)
+ {
+ do
+ {
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `:`");
+ break;
+ }
+ Identifier _alias = token.ident;
+ Identifier name;
+ nextToken();
+ if (token.value == TOK.assign)
+ {
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `%s=`", _alias.toChars());
+ break;
+ }
+ name = token.ident;
+ nextToken();
+ }
+ else
+ {
+ name = _alias;
+ _alias = null;
+ }
+ s.addAlias(name, _alias);
+ }
+ while (token.value == TOK.comma);
+ break; // no comma-separated imports of this form
+ }
+ aliasid = null;
+ }
+ while (token.value == TOK.comma);
+
+ if (token.value == TOK.semicolon)
+ nextToken();
+ else
+ {
+ error("`;` expected");
+ nextToken();
+ }
+
+ return decldefs;
+ }
+
+ AST.Type parseType(Identifier* pident = null, AST.TemplateParameters** ptpl = null)
+ {
+ /* Take care of the storage class prefixes that
+ * serve as type attributes:
+ * const type
+ * immutable type
+ * shared type
+ * inout type
+ * inout const type
+ * shared const type
+ * shared inout type
+ * shared inout const type
+ */
+ StorageClass stc = 0;
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.const_:
+ if (peekNext() == TOK.leftParenthesis)
+ break; // const as type constructor
+ stc |= STC.const_; // const as storage class
+ nextToken();
+ continue;
+
+ case TOK.immutable_:
+ if (peekNext() == TOK.leftParenthesis)
+ break;
+ stc |= STC.immutable_;
+ nextToken();
+ continue;
+
+ case TOK.shared_:
+ if (peekNext() == TOK.leftParenthesis)
+ break;
+ stc |= STC.shared_;
+ nextToken();
+ continue;
+
+ case TOK.inout_:
+ if (peekNext() == TOK.leftParenthesis)
+ break;
+ stc |= STC.wild;
+ nextToken();
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ const typeLoc = token.loc;
+
+ AST.Type t;
+ t = parseBasicType();
+
+ int alt = 0;
+ t = parseDeclarator(t, alt, pident, ptpl);
+ checkCstyleTypeSyntax(typeLoc, t, alt, pident ? *pident : null);
+
+ t = t.addSTC(stc);
+ return t;
+ }
+
+ private AST.Type parseBasicType(bool dontLookDotIdents = false)
+ {
+ AST.Type t;
+ Loc loc;
+ Identifier id;
+ //printf("parseBasicType()\n");
+ switch (token.value)
+ {
+ case TOK.void_:
+ t = AST.Type.tvoid;
+ goto LabelX;
+
+ case TOK.int8:
+ t = AST.Type.tint8;
+ goto LabelX;
+
+ case TOK.uns8:
+ t = AST.Type.tuns8;
+ goto LabelX;
+
+ case TOK.int16:
+ t = AST.Type.tint16;
+ goto LabelX;
+
+ case TOK.uns16:
+ t = AST.Type.tuns16;
+ goto LabelX;
+
+ case TOK.int32:
+ t = AST.Type.tint32;
+ goto LabelX;
+
+ case TOK.uns32:
+ t = AST.Type.tuns32;
+ goto LabelX;
+
+ case TOK.int64:
+ t = AST.Type.tint64;
+ nextToken();
+ if (token.value == TOK.int64) // if `long long`
+ {
+ error("use `long` for a 64 bit integer instead of `long long`");
+ nextToken();
+ }
+ else if (token.value == TOK.float64) // if `long double`
+ {
+ error("use `real` instead of `long double`");
+ t = AST.Type.tfloat80;
+ nextToken();
+ }
+ break;
+
+ case TOK.uns64:
+ t = AST.Type.tuns64;
+ goto LabelX;
+
+ case TOK.int128:
+ t = AST.Type.tint128;
+ goto LabelX;
+
+ case TOK.uns128:
+ t = AST.Type.tuns128;
+ goto LabelX;
+
+ case TOK.float32:
+ t = AST.Type.tfloat32;
+ goto LabelX;
+
+ case TOK.float64:
+ t = AST.Type.tfloat64;
+ goto LabelX;
+
+ case TOK.float80:
+ t = AST.Type.tfloat80;
+ goto LabelX;
+
+ case TOK.imaginary32:
+ t = AST.Type.timaginary32;
+ goto LabelX;
+
+ case TOK.imaginary64:
+ t = AST.Type.timaginary64;
+ goto LabelX;
+
+ case TOK.imaginary80:
+ t = AST.Type.timaginary80;
+ goto LabelX;
+
+ case TOK.complex32:
+ t = AST.Type.tcomplex32;
+ goto LabelX;
+
+ case TOK.complex64:
+ t = AST.Type.tcomplex64;
+ goto LabelX;
+
+ case TOK.complex80:
+ t = AST.Type.tcomplex80;
+ goto LabelX;
+
+ case TOK.bool_:
+ t = AST.Type.tbool;
+ goto LabelX;
+
+ case TOK.char_:
+ t = AST.Type.tchar;
+ goto LabelX;
+
+ case TOK.wchar_:
+ t = AST.Type.twchar;
+ goto LabelX;
+
+ case TOK.dchar_:
+ t = AST.Type.tdchar;
+ goto LabelX;
+ LabelX:
+ nextToken();
+ break;
+
+ case TOK.this_:
+ case TOK.super_:
+ case TOK.identifier:
+ loc = token.loc;
+ id = token.ident;
+ nextToken();
+ if (token.value == TOK.not)
+ {
+ // ident!(template_arguments)
+ auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
+ t = parseBasicTypeStartingAt(new AST.TypeInstance(loc, tempinst), dontLookDotIdents);
+ }
+ else
+ {
+ t = parseBasicTypeStartingAt(new AST.TypeIdentifier(loc, id), dontLookDotIdents);
+ }
+ break;
+
+ case TOK.mixin_:
+ // https://dlang.org/spec/expression.html#mixin_types
+ loc = token.loc;
+ nextToken();
+ if (token.value != TOK.leftParenthesis)
+ error("found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
+ auto exps = parseArguments();
+ t = new AST.TypeMixin(loc, exps);
+ break;
+
+ case TOK.dot:
+ // Leading . as in .foo
+ t = parseBasicTypeStartingAt(new AST.TypeIdentifier(token.loc, Id.empty), dontLookDotIdents);
+ break;
+
+ case TOK.typeof_:
+ // typeof(expression)
+ t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
+ break;
+
+ case TOK.vector:
+ t = parseVector();
+ break;
+
+ case TOK.traits:
+ if (AST.TraitsExp te = cast(AST.TraitsExp) parsePrimaryExp())
+ if (te.ident && te.args)
+ {
+ t = new AST.TypeTraits(token.loc, te);
+ break;
+ }
+ t = new AST.TypeError;
+ break;
+
+ case TOK.const_:
+ // const(type)
+ nextToken();
+ check(TOK.leftParenthesis);
+ t = parseType().addSTC(STC.const_);
+ check(TOK.rightParenthesis);
+ break;
+
+ case TOK.immutable_:
+ // immutable(type)
+ nextToken();
+ check(TOK.leftParenthesis);
+ t = parseType().addSTC(STC.immutable_);
+ check(TOK.rightParenthesis);
+ break;
+
+ case TOK.shared_:
+ // shared(type)
+ nextToken();
+ check(TOK.leftParenthesis);
+ t = parseType().addSTC(STC.shared_);
+ check(TOK.rightParenthesis);
+ break;
+
+ case TOK.inout_:
+ // wild(type)
+ nextToken();
+ check(TOK.leftParenthesis);
+ t = parseType().addSTC(STC.wild);
+ check(TOK.rightParenthesis);
+ break;
+
+ default:
+ error("basic type expected, not `%s`", token.toChars());
+ if (token.value == TOK.else_)
+ errorSupplemental(token.loc, "There's no `static else`, use `else` instead.");
+ t = AST.Type.terror;
+ break;
+ }
+ return t;
+ }
+
+ private AST.Type parseBasicTypeStartingAt(AST.TypeQualified tid, bool dontLookDotIdents)
+ {
+ AST.Type maybeArray = null;
+ // See https://issues.dlang.org/show_bug.cgi?id=1215
+ // A basic type can look like MyType (typical case), but also:
+ // MyType.T -> A type
+ // MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
+ // MyType[expr].T -> A type.
+ // MyType[expr].T[expr] -> Either a static array of MyType[expr].T or a type
+ // (iif MyType[expr].T is a Ttuple)
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.dot:
+ {
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `.` instead of `%s`", token.toChars());
+ break;
+ }
+ if (maybeArray)
+ {
+ // This is actually a TypeTuple index, not an {a/s}array.
+ // We need to have a while loop to unwind all index taking:
+ // T[e1][e2].U -> T, addIndex(e1), addIndex(e2)
+ AST.Objects dimStack;
+ AST.Type t = maybeArray;
+ while (true)
+ {
+ if (t.ty == Tsarray)
+ {
+ // The index expression is an Expression.
+ AST.TypeSArray a = cast(AST.TypeSArray)t;
+ dimStack.push(a.dim.syntaxCopy());
+ t = a.next.syntaxCopy();
+ }
+ else if (t.ty == Taarray)
+ {
+ // The index expression is a Type. It will be interpreted as an expression at semantic time.
+ AST.TypeAArray a = cast(AST.TypeAArray)t;
+ dimStack.push(a.index.syntaxCopy());
+ t = a.next.syntaxCopy();
+ }
+ else
+ {
+ break;
+ }
+ }
+ assert(dimStack.dim > 0);
+ // We're good. Replay indices in the reverse order.
+ tid = cast(AST.TypeQualified)t;
+ while (dimStack.dim)
+ {
+ tid.addIndex(dimStack.pop());
+ }
+ maybeArray = null;
+ }
+ const loc = token.loc;
+ Identifier id = token.ident;
+ nextToken();
+ if (token.value == TOK.not)
+ {
+ auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
+ tid.addInst(tempinst);
+ }
+ else
+ tid.addIdent(id);
+ continue;
+ }
+ case TOK.leftBracket:
+ {
+ if (dontLookDotIdents) // workaround for https://issues.dlang.org/show_bug.cgi?id=14911
+ goto Lend;
+
+ nextToken();
+ AST.Type t = maybeArray ? maybeArray : cast(AST.Type)tid;
+ if (token.value == TOK.rightBracket)
+ {
+ // It's a dynamic array, and we're done:
+ // T[].U does not make sense.
+ t = new AST.TypeDArray(t);
+ nextToken();
+ return t;
+ }
+ else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
+ {
+ // This can be one of two things:
+ // 1 - an associative array declaration, T[type]
+ // 2 - an associative array declaration, T[expr]
+ // These can only be disambiguated later.
+ AST.Type index = parseType(); // [ type ]
+ maybeArray = new AST.TypeAArray(t, index);
+ check(TOK.rightBracket);
+ }
+ else
+ {
+ // This can be one of three things:
+ // 1 - an static array declaration, T[expr]
+ // 2 - a slice, T[expr .. expr]
+ // 3 - a template parameter pack index expression, T[expr].U
+ // 1 and 3 can only be disambiguated later.
+ //printf("it's type[expression]\n");
+ inBrackets++;
+ AST.Expression e = parseAssignExp(); // [ expression ]
+ if (token.value == TOK.slice)
+ {
+ // It's a slice, and we're done.
+ nextToken();
+ AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
+ t = new AST.TypeSlice(t, e, e2);
+ inBrackets--;
+ check(TOK.rightBracket);
+ return t;
+ }
+ else
+ {
+ maybeArray = new AST.TypeSArray(t, e);
+ inBrackets--;
+ check(TOK.rightBracket);
+ continue;
+ }
+ }
+ break;
+ }
+ default:
+ goto Lend;
+ }
+ }
+ Lend:
+ return maybeArray ? maybeArray : cast(AST.Type)tid;
+ }
+
+ /******************************************
+ * Parse suffixes to type t.
+ * *
+ * []
+ * [AssignExpression]
+ * [AssignExpression .. AssignExpression]
+ * [Type]
+ * delegate Parameters MemberFunctionAttributes(opt)
+ * function Parameters FunctionAttributes(opt)
+ * Params:
+ * t = the already parsed type
+ * Returns:
+ * t with the suffixes added
+ * See_Also:
+ * https://dlang.org/spec/declaration.html#TypeSuffixes
+ */
+ private AST.Type parseTypeSuffixes(AST.Type t)
+ {
+ //printf("parseTypeSuffixes()\n");
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.mul:
+ t = new AST.TypePointer(t);
+ nextToken();
+ continue;
+
+ case TOK.leftBracket:
+ // Handle []. Make sure things like
+ // int[3][1] a;
+ // is (array[1] of array[3] of int)
+ nextToken();
+ if (token.value == TOK.rightBracket)
+ {
+ t = new AST.TypeDArray(t); // []
+ nextToken();
+ }
+ else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
+ {
+ // It's an associative array declaration
+ //printf("it's an associative array\n");
+ AST.Type index = parseType(); // [ type ]
+ t = new AST.TypeAArray(t, index);
+ check(TOK.rightBracket);
+ }
+ else
+ {
+ //printf("it's type[expression]\n");
+ inBrackets++;
+ AST.Expression e = parseAssignExp(); // [ expression ]
+ if (!e)
+ {
+ inBrackets--;
+ check(TOK.rightBracket);
+ continue;
+ }
+ if (token.value == TOK.slice)
+ {
+ nextToken();
+ AST.Expression e2 = parseAssignExp(); // [ exp .. exp ]
+ t = new AST.TypeSlice(t, e, e2);
+ }
+ else
+ {
+ t = new AST.TypeSArray(t, e);
+ }
+ inBrackets--;
+ check(TOK.rightBracket);
+ }
+ continue;
+
+ case TOK.delegate_:
+ case TOK.function_:
+ {
+ // Handle delegate declaration:
+ // t delegate(parameter list) nothrow pure
+ // t function(parameter list) nothrow pure
+ TOK save = token.value;
+ nextToken();
+
+ auto parameterList = parseParameterList(null);
+
+ StorageClass stc = parsePostfix(STC.undefined_, null);
+ auto tf = new AST.TypeFunction(parameterList, t, linkage, stc);
+ if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild | STC.return_))
+ {
+ if (save == TOK.function_)
+ error("`const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions");
+ else
+ tf = cast(AST.TypeFunction)tf.addSTC(stc);
+ }
+ t = save == TOK.delegate_ ? new AST.TypeDelegate(tf) : new AST.TypePointer(tf); // pointer to function
+ continue;
+ }
+ default:
+ return t;
+ }
+ assert(0);
+ }
+ assert(0);
+ }
+
+ /**********************
+ * Parse Declarator
+ * Params:
+ * t = base type to start with
+ * palt = OR in 1 for C-style function pointer declaration syntax,
+ * 2 for C-style array declaration syntax, otherwise don't modify
+ * pident = set to Identifier if there is one, null if not
+ * tpl = if !null, then set to TemplateParameterList
+ * storageClass = any storage classes seen so far
+ * pdisable = set to true if @disable seen
+ * pudas = any user defined attributes seen so far. Merged with any more found
+ * Returns:
+ * type declared
+ * Reference: https://dlang.org/spec/declaration.html#Declarator
+ */
+ private AST.Type parseDeclarator(AST.Type t, ref int palt, Identifier* pident,
+ AST.TemplateParameters** tpl = null, StorageClass storageClass = 0,
+ bool* pdisable = null, AST.Expressions** pudas = null)
+ {
+ //printf("parseDeclarator(tpl = %p)\n", tpl);
+ t = parseTypeSuffixes(t);
+ AST.Type ts;
+ switch (token.value)
+ {
+ case TOK.identifier:
+ if (pident)
+ *pident = token.ident;
+ else
+ error("unexpected identifier `%s` in declarator", token.ident.toChars());
+ ts = t;
+ nextToken();
+ break;
+
+ case TOK.leftParenthesis:
+ {
+ // like: T (*fp)();
+ // like: T ((*fp))();
+ if (peekNext() == TOK.mul || peekNext() == TOK.leftParenthesis)
+ {
+ /* Parse things with parentheses around the identifier, like:
+ * int (*ident[3])[]
+ * although the D style would be:
+ * int[]*[3] ident
+ */
+ palt |= 1;
+ nextToken();
+ ts = parseDeclarator(t, palt, pident);
+ check(TOK.rightParenthesis);
+ break;
+ }
+ ts = t;
+
+ Token* peekt = &token;
+ /* Completely disallow C-style things like:
+ * T (a);
+ * Improve error messages for the common bug of a missing return type
+ * by looking to see if (a) looks like a parameter list.
+ */
+ if (isParameters(&peekt))
+ {
+ error("function declaration without return type. (Note that constructors are always named `this`)");
+ }
+ else
+ error("unexpected `(` in declarator");
+ break;
+ }
+ default:
+ ts = t;
+ break;
+ }
+
+ // parse DeclaratorSuffixes
+ while (1)
+ {
+ switch (token.value)
+ {
+ static if (CARRAYDECL)
+ {
+ /* Support C style array syntax:
+ * int ident[]
+ * as opposed to D-style:
+ * int[] ident
+ */
+ case TOK.leftBracket:
+ {
+ // This is the old C-style post [] syntax.
+ AST.TypeNext ta;
+ nextToken();
+ if (token.value == TOK.rightBracket)
+ {
+ // It's a dynamic array
+ ta = new AST.TypeDArray(t); // []
+ nextToken();
+ palt |= 2;
+ }
+ else if (isDeclaration(&token, NeedDeclaratorId.no, TOK.rightBracket, null))
+ {
+ // It's an associative array
+ //printf("it's an associative array\n");
+ AST.Type index = parseType(); // [ type ]
+ check(TOK.rightBracket);
+ ta = new AST.TypeAArray(t, index);
+ palt |= 2;
+ }
+ else
+ {
+ //printf("It's a static array\n");
+ AST.Expression e = parseAssignExp(); // [ expression ]
+ ta = new AST.TypeSArray(t, e);
+ check(TOK.rightBracket);
+ palt |= 2;
+ }
+
+ /* Insert ta into
+ * ts -> ... -> t
+ * so that
+ * ts -> ... -> ta -> t
+ */
+ AST.Type* pt;
+ for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
+ {
+ }
+ *pt = ta;
+ continue;
+ }
+ }
+ case TOK.leftParenthesis:
+ {
+ if (tpl)
+ {
+ Token* tk = peekPastParen(&token);
+ if (tk.value == TOK.leftParenthesis)
+ {
+ /* Look ahead to see if this is (...)(...),
+ * i.e. a function template declaration
+ */
+ //printf("function template declaration\n");
+
+ // Gather template parameter list
+ *tpl = parseTemplateParameterList();
+ }
+ else if (tk.value == TOK.assign)
+ {
+ /* or (...) =,
+ * i.e. a variable template declaration
+ */
+ //printf("variable template declaration\n");
+ *tpl = parseTemplateParameterList();
+ break;
+ }
+ }
+
+ auto parameterList = parseParameterList(null);
+
+ /* Parse const/immutable/shared/inout/nothrow/pure/return postfix
+ */
+ // merge prefix storage classes
+ StorageClass stc = parsePostfix(storageClass, pudas);
+
+ AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc);
+ tf = tf.addSTC(stc);
+ if (pdisable)
+ *pdisable = stc & STC.disable ? true : false;
+
+ /* Insert tf into
+ * ts -> ... -> t
+ * so that
+ * ts -> ... -> tf -> t
+ */
+ AST.Type* pt;
+ for (pt = &ts; *pt != t; pt = &(cast(AST.TypeNext)*pt).next)
+ {
+ }
+ *pt = tf;
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ return ts;
+ }
+
+ private void parseStorageClasses(ref StorageClass storage_class, ref LINK link,
+ ref bool setAlignment, ref AST.Expression ealign, ref AST.Expressions* udas,
+ out Loc linkloc)
+ {
+ StorageClass stc;
+ bool sawLinkage = false; // seen a linkage declaration
+
+ linkloc = Loc.initial;
+
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.const_:
+ if (peekNext() == TOK.leftParenthesis)
+ break; // const as type constructor
+ stc = STC.const_; // const as storage class
+ goto L1;
+
+ case TOK.immutable_:
+ if (peekNext() == TOK.leftParenthesis)
+ break;
+ stc = STC.immutable_;
+ goto L1;
+
+ case TOK.shared_:
+ if (peekNext() == TOK.leftParenthesis)
+ break;
+ stc = STC.shared_;
+ goto L1;
+
+ case TOK.inout_:
+ if (peekNext() == TOK.leftParenthesis)
+ break;
+ stc = STC.wild;
+ goto L1;
+
+ case TOK.static_:
+ stc = STC.static_;
+ goto L1;
+
+ case TOK.final_:
+ stc = STC.final_;
+ goto L1;
+
+ case TOK.auto_:
+ stc = STC.auto_;
+ goto L1;
+
+ case TOK.scope_:
+ stc = STC.scope_;
+ goto L1;
+
+ case TOK.override_:
+ stc = STC.override_;
+ goto L1;
+
+ case TOK.abstract_:
+ stc = STC.abstract_;
+ goto L1;
+
+ case TOK.synchronized_:
+ stc = STC.synchronized_;
+ goto L1;
+
+ case TOK.deprecated_:
+ stc = STC.deprecated_;
+ goto L1;
+
+ case TOK.nothrow_:
+ stc = STC.nothrow_;
+ goto L1;
+
+ case TOK.pure_:
+ stc = STC.pure_;
+ goto L1;
+
+ case TOK.ref_:
+ stc = STC.ref_;
+ goto L1;
+
+ case TOK.gshared:
+ stc = STC.gshared;
+ goto L1;
+
+ case TOK.enum_:
+ {
+ const tv = peekNext();
+ if (tv == TOK.leftCurly || tv == TOK.colon)
+ break;
+ if (tv == TOK.identifier)
+ {
+ const nextv = peekNext2();
+ if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
+ break;
+ }
+ stc = STC.manifest;
+ goto L1;
+ }
+
+ case TOK.at:
+ {
+ stc = parseAttribute(udas);
+ if (stc)
+ goto L1;
+ continue;
+ }
+ L1:
+ storage_class = appendStorageClass(storage_class, stc);
+ nextToken();
+ continue;
+
+ case TOK.extern_:
+ {
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.extern_;
+ goto L1;
+ }
+
+ if (sawLinkage)
+ error("redundant linkage declaration");
+ sawLinkage = true;
+ linkloc = token.loc;
+ auto res = parseLinkage();
+ link = res.link;
+ if (res.idents || res.identExps)
+ {
+ error("C++ name spaces not allowed here");
+ }
+ if (res.cppmangle != CPPMANGLE.def)
+ {
+ error("C++ mangle declaration not allowed here");
+ }
+ continue;
+ }
+ case TOK.align_:
+ {
+ nextToken();
+ setAlignment = true;
+ if (token.value == TOK.leftParenthesis)
+ {
+ nextToken();
+ ealign = parseExpression();
+ check(TOK.rightParenthesis);
+ }
+ continue;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ /**********************************
+ * Parse Declarations.
+ * These can be:
+ * 1. declarations at global/class level
+ * 2. declarations at statement level
+ * Return array of Declaration *'s.
+ */
+ private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment)
+ {
+ StorageClass storage_class = STC.undefined_;
+ TOK tok = TOK.reserved;
+ LINK link = linkage;
+ Loc linkloc = this.linkLoc;
+ bool setAlignment = false;
+ AST.Expression ealign;
+ AST.Expressions* udas = null;
+
+ //printf("parseDeclarations() %s\n", token.toChars());
+ if (!comment)
+ comment = token.blockComment.ptr;
+
+ /* Look for AliasAssignment:
+ * identifier = type;
+ */
+ if (token.value == TOK.identifier && peekNext() == TOK.assign)
+ {
+ const loc = token.loc;
+ auto ident = token.ident;
+ nextToken();
+ nextToken(); // advance past =
+ auto t = parseType();
+ AST.Dsymbol s = new AST.AliasAssign(loc, ident, t, null);
+ check(TOK.semicolon);
+ addComment(s, comment);
+ auto a = new AST.Dsymbols();
+ a.push(s);
+ return a;
+ }
+
+ if (token.value == TOK.alias_)
+ {
+ const loc = token.loc;
+ tok = token.value;
+ nextToken();
+
+ /* Look for:
+ * alias identifier this;
+ */
+ if (token.value == TOK.identifier && peekNext() == TOK.this_)
+ {
+ auto s = new AST.AliasThis(loc, token.ident);
+ nextToken();
+ check(TOK.this_);
+ check(TOK.semicolon);
+ auto a = new AST.Dsymbols();
+ a.push(s);
+ addComment(s, comment);
+ return a;
+ }
+ version (none)
+ {
+ /* Look for:
+ * alias this = identifier;
+ */
+ if (token.value == TOK.this_ && peekNext() == TOK.assign && peekNext2() == TOK.identifier)
+ {
+ check(TOK.this_);
+ check(TOK.assign);
+ auto s = new AliasThis(loc, token.ident);
+ nextToken();
+ check(TOK.semicolon);
+ auto a = new Dsymbols();
+ a.push(s);
+ addComment(s, comment);
+ return a;
+ }
+ }
+ /* Look for:
+ * alias identifier = type;
+ * alias identifier(...) = type;
+ */
+ if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
+ {
+ auto a = new AST.Dsymbols();
+ while (1)
+ {
+ auto ident = token.ident;
+ nextToken();
+ AST.TemplateParameters* tpl = null;
+ if (token.value == TOK.leftParenthesis)
+ tpl = parseTemplateParameterList();
+ check(TOK.assign);
+
+ bool hasParsedAttributes;
+ void parseAttributes()
+ {
+ if (hasParsedAttributes) // only parse once
+ return;
+ hasParsedAttributes = true;
+ udas = null;
+ storage_class = STC.undefined_;
+ link = linkage;
+ linkloc = this.linkLoc;
+ setAlignment = false;
+ ealign = null;
+ parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
+ }
+
+ if (token.value == TOK.at)
+ parseAttributes;
+
+ AST.Declaration v;
+ AST.Dsymbol s;
+
+ // try to parse function type:
+ // TypeCtors? BasicType ( Parameters ) MemberFunctionAttributes
+ bool attributesAppended;
+ const StorageClass funcStc = parseTypeCtor();
+ Token* tlu = &token;
+ Token* tk;
+ if (token.value != TOK.function_ &&
+ token.value != TOK.delegate_ &&
+ isBasicType(&tlu) && tlu &&
+ tlu.value == TOK.leftParenthesis)
+ {
+ AST.Type tret = parseBasicType();
+ auto parameterList = parseParameterList(null);
+
+ parseAttributes();
+ if (udas)
+ error("user-defined attributes not allowed for `alias` declarations");
+
+ attributesAppended = true;
+ storage_class = appendStorageClass(storage_class, funcStc);
+ AST.Type tf = new AST.TypeFunction(parameterList, tret, link, storage_class);
+ v = new AST.AliasDeclaration(loc, ident, tf);
+ }
+ else if (token.value == TOK.function_ ||
+ token.value == TOK.delegate_ ||
+ token.value == TOK.leftParenthesis &&
+ skipAttributes(peekPastParen(&token), &tk) &&
+ (tk.value == TOK.goesTo || tk.value == TOK.leftCurly) ||
+ token.value == TOK.leftCurly ||
+ token.value == TOK.identifier && peekNext() == TOK.goesTo ||
+ token.value == TOK.ref_ && peekNext() == TOK.leftParenthesis &&
+ skipAttributes(peekPastParen(peek(&token)), &tk) &&
+ (tk.value == TOK.goesTo || tk.value == TOK.leftCurly)
+ )
+ {
+ // function (parameters) { statements... }
+ // delegate (parameters) { statements... }
+ // (parameters) { statements... }
+ // (parameters) => expression
+ // { statements... }
+ // identifier => expression
+ // ref (parameters) { statements... }
+ // ref (parameters) => expression
+
+ s = parseFunctionLiteral();
+
+ if (udas !is null)
+ {
+ if (storage_class != 0)
+ error("Cannot put a storage-class in an alias declaration.");
+ // parseAttributes shouldn't have set these variables
+ assert(link == linkage && !setAlignment && ealign is null);
+ auto tpl_ = cast(AST.TemplateDeclaration) s;
+ assert(tpl_ !is null && tpl_.members.dim == 1);
+ auto fd = cast(AST.FuncLiteralDeclaration) (*tpl_.members)[0];
+ auto tf = cast(AST.TypeFunction) fd.type;
+ assert(tf.parameterList.parameters.dim > 0);
+ auto as = new AST.Dsymbols();
+ (*tf.parameterList.parameters)[0].userAttribDecl = new AST.UserAttributeDeclaration(udas, as);
+ }
+
+ v = new AST.AliasDeclaration(loc, ident, s);
+ }
+ else
+ {
+ parseAttributes();
+ // type
+ if (udas)
+ error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok));
+
+ auto t = parseType();
+
+ // Disallow meaningless storage classes on type aliases
+ if (storage_class)
+ {
+ // Don't raise errors for STC that are part of a function/delegate type, e.g.
+ // `alias F = ref pure nothrow @nogc @safe int function();`
+ auto tp = t.isTypePointer;
+ const isFuncType = (tp && tp.next.isTypeFunction) || t.isTypeDelegate;
+ const remStc = isFuncType ? (storage_class & ~STC.FUNCATTR) : storage_class;
+
+ if (remStc)
+ {
+ OutBuffer buf;
+ AST.stcToBuffer(&buf, remStc);
+ // @@@DEPRECATED_2.093@@@
+ // Deprecated in 2020-07, can be made an error in 2.103
+ deprecation("storage class `%s` has no effect in type aliases", buf.peekChars());
+ }
+ }
+
+ v = new AST.AliasDeclaration(loc, ident, t);
+ }
+ if (!attributesAppended)
+ storage_class = appendStorageClass(storage_class, funcStc);
+ v.storage_class = storage_class;
+
+ s = v;
+ if (tpl)
+ {
+ auto a2 = new AST.Dsymbols();
+ a2.push(s);
+ auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2);
+ s = tempdecl;
+ }
+ if (link != linkage)
+ {
+ auto a2 = new AST.Dsymbols();
+ a2.push(s);
+ s = new AST.LinkDeclaration(linkloc, link, a2);
+ }
+ a.push(s);
+
+ switch (token.value)
+ {
+ case TOK.semicolon:
+ nextToken();
+ addComment(s, comment);
+ break;
+
+ case TOK.comma:
+ nextToken();
+ addComment(s, comment);
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following comma, not `%s`", token.toChars());
+ break;
+ }
+ if (peekNext() != TOK.assign && peekNext() != TOK.leftParenthesis)
+ {
+ error("`=` expected following identifier");
+ nextToken();
+ break;
+ }
+ continue;
+
+ default:
+ error("semicolon expected to close `%s` declaration", Token.toChars(tok));
+ break;
+ }
+ break;
+ }
+ return a;
+ }
+
+ // alias StorageClasses type ident;
+ }
+
+ AST.Type ts;
+
+ if (!autodecl)
+ {
+ parseStorageClasses(storage_class, link, setAlignment, ealign, udas, linkloc);
+
+ if (token.value == TOK.enum_)
+ {
+ AST.Dsymbol d = parseEnum();
+ auto a = new AST.Dsymbols();
+ a.push(d);
+
+ if (udas)
+ {
+ d = new AST.UserAttributeDeclaration(udas, a);
+ a = new AST.Dsymbols();
+ a.push(d);
+ }
+
+ addComment(d, comment);
+ return a;
+ }
+ if (token.value == TOK.struct_ ||
+ token.value == TOK.union_ ||
+ token.value == TOK.class_ ||
+ token.value == TOK.interface_)
+ {
+ AST.Dsymbol s = parseAggregate();
+ auto a = new AST.Dsymbols();
+ a.push(s);
+
+ if (storage_class)
+ {
+ s = new AST.StorageClassDeclaration(storage_class, a);
+ a = new AST.Dsymbols();
+ a.push(s);
+ }
+ if (setAlignment)
+ {
+ s = new AST.AlignDeclaration(s.loc, ealign, a);
+ a = new AST.Dsymbols();
+ a.push(s);
+ }
+ if (link != linkage)
+ {
+ s = new AST.LinkDeclaration(linkloc, link, a);
+ a = new AST.Dsymbols();
+ a.push(s);
+ }
+ if (udas)
+ {
+ s = new AST.UserAttributeDeclaration(udas, a);
+ a = new AST.Dsymbols();
+ a.push(s);
+ }
+
+ addComment(s, comment);
+ return a;
+ }
+
+ /* Look for auto initializers:
+ * storage_class identifier = initializer;
+ * storage_class identifier(...) = initializer;
+ */
+ if ((storage_class || udas) && token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))
+ {
+ AST.Dsymbols* a = parseAutoDeclarations(storage_class, comment);
+ if (udas)
+ {
+ AST.Dsymbol s = new AST.UserAttributeDeclaration(udas, a);
+ a = new AST.Dsymbols();
+ a.push(s);
+ }
+ return a;
+ }
+
+ /* Look for return type inference for template functions.
+ */
+ {
+ Token* tk;
+ if ((storage_class || udas) && token.value == TOK.identifier && skipParens(peek(&token), &tk) &&
+ skipAttributes(tk, &tk) &&
+ (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ || tk.value == TOK.out_ || tk.value == TOK.goesTo ||
+ tk.value == TOK.do_ || tk.value == TOK.identifier && tk.ident == Id._body))
+ {
+ // @@@DEPRECATED@@@
+ // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
+ // Deprecated in 2.097 - Can be removed from 2.117
+ // The deprecation period is longer than usual as `body`
+ // was quite widely used.
+ if (tk.value == TOK.identifier && tk.ident == Id._body)
+ deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
+
+ ts = null;
+ }
+ else
+ {
+ ts = parseBasicType();
+ ts = parseTypeSuffixes(ts);
+ }
+ }
+ }
+
+ if (pAttrs)
+ {
+ storage_class |= pAttrs.storageClass;
+ //pAttrs.storageClass = STC.undefined_;
+ }
+
+ AST.Type tfirst = null;
+ auto a = new AST.Dsymbols();
+
+ while (1)
+ {
+ AST.TemplateParameters* tpl = null;
+ bool disable;
+ int alt = 0;
+
+ const loc = token.loc;
+ Identifier ident;
+
+ auto t = parseDeclarator(ts, alt, &ident, &tpl, storage_class, &disable, &udas);
+ assert(t);
+ if (!tfirst)
+ tfirst = t;
+ else if (t != tfirst)
+ error("multiple declarations must have the same type, not `%s` and `%s`", tfirst.toChars(), t.toChars());
+
+ bool isThis = (t.ty == Tident && (cast(AST.TypeIdentifier)t).ident == Id.This && token.value == TOK.assign);
+ if (ident)
+ checkCstyleTypeSyntax(loc, t, alt, ident);
+ else if (!isThis && (t != AST.Type.terror))
+ error("no identifier for declarator `%s`", t.toChars());
+
+ if (tok == TOK.alias_)
+ {
+ AST.Declaration v;
+ AST.Initializer _init = null;
+
+ /* Aliases can no longer have multiple declarators, storage classes,
+ * linkages, or auto declarations.
+ * These never made any sense, anyway.
+ * The code below needs to be fixed to reject them.
+ * The grammar has already been fixed to preclude them.
+ */
+
+ if (udas)
+ error("user-defined attributes not allowed for `%s` declarations", Token.toChars(tok));
+
+ if (token.value == TOK.assign)
+ {
+ nextToken();
+ _init = parseInitializer();
+ }
+ if (_init)
+ {
+ if (isThis)
+ error("cannot use syntax `alias this = %s`, use `alias %s this` instead", _init.toChars(), _init.toChars());
+ else
+ error("alias cannot have initializer");
+ }
+ v = new AST.AliasDeclaration(loc, ident, t);
+
+ v.storage_class = storage_class;
+ if (pAttrs)
+ {
+ /* AliasDeclaration distinguish @safe, @system, @trusted attributes
+ * on prefix and postfix.
+ * @safe alias void function() FP1;
+ * alias @safe void function() FP2; // FP2 is not @safe
+ * alias void function() @safe FP3;
+ */
+ pAttrs.storageClass &= STC.safeGroup;
+ }
+ AST.Dsymbol s = v;
+
+ if (link != linkage)
+ {
+ auto ax = new AST.Dsymbols();
+ ax.push(v);
+ s = new AST.LinkDeclaration(linkloc, link, ax);
+ }
+ a.push(s);
+ switch (token.value)
+ {
+ case TOK.semicolon:
+ nextToken();
+ addComment(s, comment);
+ break;
+
+ case TOK.comma:
+ nextToken();
+ addComment(s, comment);
+ continue;
+
+ default:
+ error("semicolon expected to close `%s` declaration", Token.toChars(tok));
+ break;
+ }
+ }
+ else if (t.ty == Tfunction)
+ {
+ AST.Expression constraint = null;
+ //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t.toChars(), storage_class);
+ auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : 0), t);
+ if (pAttrs)
+ pAttrs.storageClass = STC.undefined_;
+ if (tpl)
+ constraint = parseConstraint();
+ AST.Dsymbol s = parseContracts(f);
+ auto tplIdent = s.ident;
+
+ if (link != linkage)
+ {
+ auto ax = new AST.Dsymbols();
+ ax.push(s);
+ s = new AST.LinkDeclaration(linkloc, link, ax);
+ }
+ if (udas)
+ {
+ auto ax = new AST.Dsymbols();
+ ax.push(s);
+ s = new AST.UserAttributeDeclaration(udas, ax);
+ }
+
+ /* A template parameter list means it's a function template
+ */
+ if (tpl)
+ {
+ // Wrap a template around the function declaration
+ auto decldefs = new AST.Dsymbols();
+ decldefs.push(s);
+ auto tempdecl = new AST.TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs);
+ s = tempdecl;
+
+ StorageClass stc2 = STC.undefined_;
+ if (storage_class & STC.static_)
+ {
+ assert(f.storage_class & STC.static_);
+ f.storage_class &= ~STC.static_;
+ stc2 |= STC.static_;
+ }
+ if (storage_class & STC.deprecated_)
+ {
+ assert(f.storage_class & STC.deprecated_);
+ f.storage_class &= ~STC.deprecated_;
+ stc2 |= STC.deprecated_;
+ }
+ if (stc2 != STC.undefined_)
+ {
+ auto ax = new AST.Dsymbols();
+ ax.push(s);
+ s = new AST.StorageClassDeclaration(stc2, ax);
+ }
+ }
+ a.push(s);
+ addComment(s, comment);
+ }
+ else if (ident)
+ {
+ AST.Initializer _init = null;
+ if (token.value == TOK.assign)
+ {
+ nextToken();
+ _init = parseInitializer();
+ }
+
+ auto v = new AST.VarDeclaration(loc, t, ident, _init);
+ v.storage_class = storage_class;
+ if (pAttrs)
+ pAttrs.storageClass = STC.undefined_;
+
+ AST.Dsymbol s = v;
+
+ if (tpl && _init)
+ {
+ auto a2 = new AST.Dsymbols();
+ a2.push(s);
+ auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0);
+ s = tempdecl;
+ }
+ if (setAlignment)
+ {
+ auto ax = new AST.Dsymbols();
+ ax.push(s);
+ s = new AST.AlignDeclaration(v.loc, ealign, ax);
+ }
+ if (link != linkage)
+ {
+ auto ax = new AST.Dsymbols();
+ ax.push(s);
+ s = new AST.LinkDeclaration(linkloc, link, ax);
+ }
+ if (udas)
+ {
+ auto ax = new AST.Dsymbols();
+ ax.push(s);
+ s = new AST.UserAttributeDeclaration(udas, ax);
+ }
+ a.push(s);
+ switch (token.value)
+ {
+ case TOK.semicolon:
+ nextToken();
+ addComment(s, comment);
+ break;
+
+ case TOK.comma:
+ nextToken();
+ addComment(s, comment);
+ continue;
+
+ default:
+ error("semicolon expected, not `%s`", token.toChars());
+ break;
+ }
+ }
+ break;
+ }
+ return a;
+ }
+
+ private AST.Dsymbol parseFunctionLiteral()
+ {
+ const loc = token.loc;
+ AST.TemplateParameters* tpl = null;
+ AST.ParameterList parameterList;
+ AST.Type tret = null;
+ StorageClass stc = 0;
+ TOK save = TOK.reserved;
+
+ switch (token.value)
+ {
+ case TOK.function_:
+ case TOK.delegate_:
+ save = token.value;
+ nextToken();
+ if (token.value == TOK.ref_)
+ {
+ // function ref (parameters) { statements... }
+ // delegate ref (parameters) { statements... }
+ stc = STC.ref_;
+ nextToken();
+ }
+ if (token.value != TOK.leftParenthesis && token.value != TOK.leftCurly)
+ {
+ // function type (parameters) { statements... }
+ // delegate type (parameters) { statements... }
+ tret = parseBasicType();
+ tret = parseTypeSuffixes(tret); // function return type
+ }
+
+ if (token.value == TOK.leftParenthesis)
+ {
+ // function (parameters) { statements... }
+ // delegate (parameters) { statements... }
+ }
+ else
+ {
+ // function { statements... }
+ // delegate { statements... }
+ break;
+ }
+ goto case TOK.leftParenthesis;
+
+ case TOK.ref_:
+ {
+ // ref (parameters) => expression
+ // ref (parameters) { statements... }
+ stc = STC.ref_;
+ nextToken();
+ goto case TOK.leftParenthesis;
+ }
+ case TOK.leftParenthesis:
+ {
+ // (parameters) => expression
+ // (parameters) { statements... }
+ parameterList = parseParameterList(&tpl);
+ stc = parsePostfix(stc, null);
+ if (StorageClass modStc = stc & STC.TYPECTOR)
+ {
+ if (save == TOK.function_)
+ {
+ OutBuffer buf;
+ AST.stcToBuffer(&buf, modStc);
+ error("function literal cannot be `%s`", buf.peekChars());
+ }
+ else
+ save = TOK.delegate_;
+ }
+ break;
+ }
+ case TOK.leftCurly:
+ // { statements... }
+ break;
+
+ case TOK.identifier:
+ {
+ // identifier => expression
+ parameterList.parameters = new AST.Parameters();
+ Identifier id = Identifier.generateId("__T");
+ AST.Type t = new AST.TypeIdentifier(loc, id);
+ parameterList.parameters.push(new AST.Parameter(STC.parameter, t, token.ident, null, null));
+
+ tpl = new AST.TemplateParameters();
+ AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null);
+ tpl.push(tp);
+
+ nextToken();
+ break;
+ }
+ default:
+ assert(0);
+ }
+
+ auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
+ tf = cast(AST.TypeFunction)tf.addSTC(stc);
+ auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save, null);
+
+ if (token.value == TOK.goesTo)
+ {
+ check(TOK.goesTo);
+ if (token.value == TOK.leftCurly)
+ {
+ deprecation("Using `(args) => { ... }` to create a delegate that returns a delegate is error-prone.");
+ deprecationSupplemental(token.loc, "Use `(args) { ... }` for a multi-statement function literal or use `(args) => () { }` if you intended for the lambda to return a delegate.");
+ }
+ const returnloc = token.loc;
+ AST.Expression ae = parseAssignExp();
+ fd.fbody = new AST.ReturnStatement(returnloc, ae);
+ fd.endloc = token.loc;
+ }
+ else
+ {
+ parseContracts(fd);
+ }
+
+ if (tpl)
+ {
+ // Wrap a template around function fd
+ auto decldefs = new AST.Dsymbols();
+ decldefs.push(fd);
+ return new AST.TemplateDeclaration(fd.loc, fd.ident, tpl, null, decldefs, false, true);
+ }
+ return fd;
+ }
+
+ /*****************************************
+ * Parse contracts following function declaration.
+ */
+ private AST.FuncDeclaration parseContracts(AST.FuncDeclaration f)
+ {
+ LINK linksave = linkage;
+
+ bool literal = f.isFuncLiteralDeclaration() !is null;
+
+ // The following is irrelevant, as it is overridden by sc.linkage in
+ // TypeFunction::semantic
+ linkage = LINK.d; // nested functions have D linkage
+ bool requireDo = false;
+ L1:
+ switch (token.value)
+ {
+ case TOK.goesTo:
+ if (requireDo)
+ error("missing `do { ... }` after `in` or `out`");
+ if (!global.params.shortenedMethods)
+ error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`");
+ const returnloc = token.loc;
+ nextToken();
+ f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
+ f.endloc = token.loc;
+ check(TOK.semicolon);
+ break;
+
+ case TOK.leftCurly:
+ if (requireDo)
+ error("missing `do { ... }` after `in` or `out`");
+ f.fbody = parseStatement(ParseStatementFlags.semi);
+ f.endloc = endloc;
+ break;
+
+ case TOK.identifier:
+ if (token.ident == Id._body)
+ {
+ // @@@DEPRECATED@@@
+ // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
+ // Deprecated in 2.097 - Can be removed from 2.117
+ // The deprecation period is longer than usual as `body`
+ // was quite widely used.
+ deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
+ goto case TOK.do_;
+ }
+ goto default;
+
+ case TOK.do_:
+ nextToken();
+ f.fbody = parseStatement(ParseStatementFlags.curly);
+ f.endloc = endloc;
+ break;
+
+ version (none)
+ {
+ // Do we want this for function declarations, so we can do:
+ // int x, y, foo(), z;
+ case TOK.comma:
+ nextToken();
+ continue;
+ }
+
+ case TOK.in_:
+ // in { statements... }
+ // in (expression)
+ auto loc = token.loc;
+ nextToken();
+ if (!f.frequires)
+ {
+ f.frequires = new AST.Statements;
+ }
+ if (token.value == TOK.leftParenthesis)
+ {
+ nextToken();
+ AST.Expression e = parseAssignExp(), msg = null;
+ if (token.value == TOK.comma)
+ {
+ nextToken();
+ if (token.value != TOK.rightParenthesis)
+ {
+ msg = parseAssignExp();
+ if (token.value == TOK.comma)
+ nextToken();
+ }
+ }
+ check(TOK.rightParenthesis);
+ e = new AST.AssertExp(loc, e, msg);
+ f.frequires.push(new AST.ExpStatement(loc, e));
+ requireDo = false;
+ }
+ else
+ {
+ f.frequires.push(parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_));
+ requireDo = true;
+ }
+ goto L1;
+
+ case TOK.out_:
+ // out { statements... }
+ // out (; expression)
+ // out (identifier) { statements... }
+ // out (identifier; expression)
+ auto loc = token.loc;
+ nextToken();
+ if (!f.fensures)
+ {
+ f.fensures = new AST.Ensures;
+ }
+ Identifier id = null;
+ if (token.value != TOK.leftCurly)
+ {
+ check(TOK.leftParenthesis);
+ if (token.value != TOK.identifier && token.value != TOK.semicolon)
+ error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
+ if (token.value != TOK.semicolon)
+ {
+ id = token.ident;
+ nextToken();
+ }
+ if (token.value == TOK.semicolon)
+ {
+ nextToken();
+ AST.Expression e = parseAssignExp(), msg = null;
+ if (token.value == TOK.comma)
+ {
+ nextToken();
+ if (token.value != TOK.rightParenthesis)
+ {
+ msg = parseAssignExp();
+ if (token.value == TOK.comma)
+ nextToken();
+ }
+ }
+ check(TOK.rightParenthesis);
+ e = new AST.AssertExp(loc, e, msg);
+ f.fensures.push(AST.Ensure(id, new AST.ExpStatement(loc, e)));
+ requireDo = false;
+ goto L1;
+ }
+ check(TOK.rightParenthesis);
+ }
+ f.fensures.push(AST.Ensure(id, parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_)));
+ requireDo = true;
+ goto L1;
+
+ case TOK.semicolon:
+ if (!literal)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=15799
+ // Semicolon becomes a part of function declaration
+ // only when 'do' is not required
+ if (!requireDo)
+ nextToken();
+ break;
+ }
+ goto default;
+
+ default:
+ if (literal)
+ {
+ const(char)* sbody = requireDo ? "do " : "";
+ error("missing `%s{ ... }` for function literal", sbody);
+ }
+ else if (!requireDo) // allow contracts even with no body
+ {
+ TOK t = token.value;
+ if (t == TOK.const_ || t == TOK.immutable_ || t == TOK.inout_ || t == TOK.return_ ||
+ t == TOK.shared_ || t == TOK.nothrow_ || t == TOK.pure_)
+ error("'%s' cannot be placed after a template constraint", token.toChars);
+ else if (t == TOK.at)
+ error("attributes cannot be placed after a template constraint");
+ else if (t == TOK.if_)
+ error("cannot use function constraints for non-template functions. Use `static if` instead");
+ else
+ error("semicolon expected following function declaration");
+ }
+ break;
+ }
+ if (literal && !f.fbody)
+ {
+ // Set empty function body for error recovery
+ f.fbody = new AST.CompoundStatement(Loc.initial, cast(AST.Statement)null);
+ }
+
+ linkage = linksave;
+
+ return f;
+ }
+
+ /*****************************************
+ */
+ private void checkDanglingElse(Loc elseloc)
+ {
+ if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.linnum != 0)
+ {
+ warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars());
+ }
+ }
+
+ /* *************************
+ * Issue errors if C-style syntax
+ * Params:
+ * alt = !=0 for C-style syntax
+ */
+ private void checkCstyleTypeSyntax(Loc loc, AST.Type t, int alt, Identifier ident)
+ {
+ if (!alt)
+ return;
+
+ const(char)* sp = !ident ? "" : " ";
+ const(char)* s = !ident ? "" : ident.toChars();
+ error(loc, "instead of C-style syntax, use D-style `%s%s%s`", t.toChars(), sp, s);
+ }
+
+ /*****************************************
+ * Parses `foreach` statements, `static foreach` statements and
+ * `static foreach` declarations.
+ * Params:
+ * Foreach = one of Statement, StaticForeachStatement, StaticForeachDeclaration
+ * loc = location of foreach
+ * pLastDecl = non-null for StaticForeachDeclaration
+ * Returns:
+ * the Foreach generated
+ */
+ private Foreach parseForeach(alias Foreach)(Loc loc, AST.Dsymbol* pLastDecl)
+ {
+ static if (is(Foreach == AST.StaticForeachStatement) || is(Foreach == AST.StaticForeachDeclaration))
+ {
+ nextToken();
+ }
+
+ TOK op = token.value;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+
+ auto parameters = new AST.Parameters();
+ while (1)
+ {
+ Identifier ai = null;
+ AST.Type at;
+
+ StorageClass storageClass = 0;
+ StorageClass stc = 0;
+ Lagain:
+ if (stc)
+ {
+ storageClass = appendStorageClass(storageClass, stc);
+ nextToken();
+ }
+ switch (token.value)
+ {
+ case TOK.ref_:
+ stc = STC.ref_;
+ goto Lagain;
+
+ case TOK.scope_:
+ stc = STC.scope_;
+ goto Lagain;
+
+ case TOK.enum_:
+ stc = STC.manifest;
+ goto Lagain;
+
+ case TOK.alias_:
+ storageClass = appendStorageClass(storageClass, STC.alias_);
+ nextToken();
+ break;
+
+ case TOK.const_:
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.const_;
+ goto Lagain;
+ }
+ break;
+
+ case TOK.immutable_:
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.immutable_;
+ goto Lagain;
+ }
+ break;
+
+ case TOK.shared_:
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.shared_;
+ goto Lagain;
+ }
+ break;
+
+ case TOK.inout_:
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.wild;
+ goto Lagain;
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (token.value == TOK.identifier)
+ {
+ const tv = peekNext();
+ if (tv == TOK.comma || tv == TOK.semicolon)
+ {
+ ai = token.ident;
+ at = null; // infer argument type
+ nextToken();
+ goto Larg;
+ }
+ }
+ at = parseType(&ai);
+ if (!ai)
+ error("no identifier for declarator `%s`", at.toChars());
+ Larg:
+ auto p = new AST.Parameter(storageClass, at, ai, null, null);
+ parameters.push(p);
+ if (token.value == TOK.comma)
+ {
+ nextToken();
+ continue;
+ }
+ break;
+ }
+ check(TOK.semicolon);
+
+ AST.Expression aggr = parseExpression();
+ if (token.value == TOK.slice && parameters.dim == 1)
+ {
+ AST.Parameter p = (*parameters)[0];
+ nextToken();
+ AST.Expression upr = parseExpression();
+ check(TOK.rightParenthesis);
+ Loc endloc;
+ static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
+ {
+ AST.Statement _body = parseStatement(0, null, &endloc);
+ }
+ else
+ {
+ AST.Statement _body = null;
+ }
+ auto rangefe = new AST.ForeachRangeStatement(loc, op, p, aggr, upr, _body, endloc);
+ static if (is(Foreach == AST.Statement))
+ {
+ return rangefe;
+ }
+ else static if(is(Foreach == AST.StaticForeachDeclaration))
+ {
+ return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, null, rangefe), parseBlock(pLastDecl));
+ }
+ else static if (is(Foreach == AST.StaticForeachStatement))
+ {
+ return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, null, rangefe));
+ }
+ }
+ else
+ {
+ check(TOK.rightParenthesis);
+ Loc endloc;
+ static if (is(Foreach == AST.Statement) || is(Foreach == AST.StaticForeachStatement))
+ {
+ AST.Statement _body = parseStatement(0, null, &endloc);
+ }
+ else
+ {
+ AST.Statement _body = null;
+ }
+ auto aggrfe = new AST.ForeachStatement(loc, op, parameters, aggr, _body, endloc);
+ static if (is(Foreach == AST.Statement))
+ {
+ return aggrfe;
+ }
+ else static if(is(Foreach == AST.StaticForeachDeclaration))
+ {
+ return new AST.StaticForeachDeclaration(new AST.StaticForeach(loc, aggrfe, null), parseBlock(pLastDecl));
+ }
+ else static if (is(Foreach == AST.StaticForeachStatement))
+ {
+ return new AST.StaticForeachStatement(loc, new AST.StaticForeach(loc, aggrfe, null));
+ }
+ }
+
+ }
+
+ /***
+ * Parse an assignment condition for if or while statements.
+ *
+ * Returns:
+ * The variable that is declared inside the condition
+ */
+ AST.Parameter parseAssignCondition()
+ {
+ AST.Parameter param = null;
+ StorageClass storageClass = 0;
+ StorageClass stc = 0;
+LagainStc:
+ if (stc)
+ {
+ storageClass = appendStorageClass(storageClass, stc);
+ nextToken();
+ }
+ switch (token.value)
+ {
+ case TOK.ref_:
+ stc = STC.ref_;
+ goto LagainStc;
+
+ case TOK.scope_:
+ stc = STC.scope_;
+ goto LagainStc;
+
+ case TOK.auto_:
+ stc = STC.auto_;
+ goto LagainStc;
+
+ case TOK.const_:
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.const_;
+ goto LagainStc;
+ }
+ break;
+
+ case TOK.immutable_:
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.immutable_;
+ goto LagainStc;
+ }
+ break;
+
+ case TOK.shared_:
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.shared_;
+ goto LagainStc;
+ }
+ break;
+
+ case TOK.inout_:
+ if (peekNext() != TOK.leftParenthesis)
+ {
+ stc = STC.wild;
+ goto LagainStc;
+ }
+ break;
+
+ default:
+ break;
+ }
+ auto n = peek(&token);
+ if (storageClass != 0 && token.value == TOK.identifier && n.value == TOK.assign)
+ {
+ Identifier ai = token.ident;
+ AST.Type at = null; // infer parameter type
+ nextToken();
+ check(TOK.assign);
+ param = new AST.Parameter(storageClass, at, ai, null, null);
+ }
+ else if (isDeclaration(&token, NeedDeclaratorId.must, TOK.assign, null))
+ {
+ Identifier ai;
+ AST.Type at = parseType(&ai);
+ check(TOK.assign);
+ param = new AST.Parameter(storageClass, at, ai, null, null);
+ }
+ else if (storageClass != 0)
+ error("found `%s` while expecting `=` or identifier", n.toChars());
+
+ return param;
+ }
+
+ /*****************************************
+ * Input:
+ * flags PSxxxx
+ * Output:
+ * pEndloc if { ... statements ... }, store location of closing brace, otherwise loc of last token of statement
+ */
+ AST.Statement parseStatement(int flags, const(char)** endPtr = null, Loc* pEndloc = null)
+ {
+ AST.Statement s;
+ AST.Condition cond;
+ AST.Statement ifbody;
+ AST.Statement elsebody;
+ bool isfinal;
+ const loc = token.loc;
+
+ //printf("parseStatement()\n");
+ if (flags & ParseStatementFlags.curly && token.value != TOK.leftCurly)
+ error("statement expected to be `{ }`, not `%s`", token.toChars());
+
+ switch (token.value)
+ {
+ case TOK.identifier:
+ {
+ /* A leading identifier can be a declaration, label, or expression.
+ * The easiest case to check first is label:
+ */
+ if (peekNext() == TOK.colonColon)
+ {
+ // skip ident::
+ nextToken();
+ nextToken();
+ error("use `.` for member lookup, not `::`");
+ break;
+ }
+
+ if (peekNext() == TOK.colon)
+ {
+ // It's a label
+ Identifier ident = token.ident;
+ nextToken();
+ nextToken();
+ if (token.value == TOK.rightCurly)
+ s = null;
+ else if (token.value == TOK.leftCurly)
+ s = parseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_);
+ else
+ s = parseStatement(ParseStatementFlags.semiOk);
+ s = new AST.LabelStatement(loc, ident, s);
+ break;
+ }
+ goto case TOK.dot;
+ }
+ case TOK.dot:
+ case TOK.typeof_:
+ case TOK.vector:
+ case TOK.traits:
+ /* https://issues.dlang.org/show_bug.cgi?id=15163
+ * If tokens can be handled as
+ * old C-style declaration or D expression, prefer the latter.
+ */
+ if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
+ goto Ldeclaration;
+ goto Lexp;
+
+ case TOK.assert_:
+ case TOK.this_:
+ case TOK.super_:
+ case TOK.int32Literal:
+ case TOK.uns32Literal:
+ case TOK.int64Literal:
+ case TOK.uns64Literal:
+ case TOK.int128Literal:
+ case TOK.uns128Literal:
+ case TOK.float32Literal:
+ case TOK.float64Literal:
+ case TOK.float80Literal:
+ case TOK.imaginary32Literal:
+ case TOK.imaginary64Literal:
+ case TOK.imaginary80Literal:
+ case TOK.charLiteral:
+ case TOK.wcharLiteral:
+ case TOK.dcharLiteral:
+ case TOK.null_:
+ case TOK.true_:
+ case TOK.false_:
+ case TOK.string_:
+ case TOK.hexadecimalString:
+ case TOK.leftParenthesis:
+ case TOK.cast_:
+ case TOK.mul:
+ case TOK.min:
+ case TOK.add:
+ case TOK.tilde:
+ case TOK.not:
+ case TOK.plusPlus:
+ case TOK.minusMinus:
+ case TOK.new_:
+ case TOK.delete_:
+ case TOK.delegate_:
+ case TOK.function_:
+ case TOK.typeid_:
+ case TOK.is_:
+ case TOK.leftBracket:
+ case TOK.file:
+ case TOK.fileFullPath:
+ case TOK.line:
+ case TOK.moduleString:
+ case TOK.functionString:
+ case TOK.prettyFunction:
+ Lexp:
+ {
+ AST.Expression exp = parseExpression();
+ /* https://issues.dlang.org/show_bug.cgi?id=15103
+ * Improve declaration / initialization syntax error message
+ * Error: found 'foo' when expecting ';' following statement
+ * becomes Error: found `(` when expecting `;` or `=`, did you mean `Foo foo = 42`?
+ */
+ if (token.value == TOK.identifier && exp.op == TOK.identifier)
+ {
+ error("found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
+ nextToken();
+ }
+ else
+ check(TOK.semicolon, "statement");
+ s = new AST.ExpStatement(loc, exp);
+ break;
+ }
+ case TOK.static_:
+ {
+ // Look ahead to see if it's static assert() or static if()
+ const tv = peekNext();
+ if (tv == TOK.assert_)
+ {
+ s = new AST.StaticAssertStatement(parseStaticAssert());
+ break;
+ }
+ if (tv == TOK.if_)
+ {
+ cond = parseStaticIfCondition();
+ goto Lcondition;
+ }
+ if (tv == TOK.foreach_ || tv == TOK.foreach_reverse_)
+ {
+ s = parseForeach!(AST.StaticForeachStatement)(loc, null);
+ if (flags & ParseStatementFlags.scope_)
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ break;
+ }
+ if (tv == TOK.import_)
+ {
+ AST.Dsymbols* imports = parseImport();
+ s = new AST.ImportStatement(loc, imports);
+ if (flags & ParseStatementFlags.scope_)
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ break;
+ }
+ goto Ldeclaration;
+ }
+ case TOK.final_:
+ if (peekNext() == TOK.switch_)
+ {
+ nextToken();
+ isfinal = true;
+ goto Lswitch;
+ }
+ goto Ldeclaration;
+
+ case TOK.wchar_:
+ case TOK.dchar_:
+ case TOK.bool_:
+ case TOK.char_:
+ case TOK.int8:
+ case TOK.uns8:
+ case TOK.int16:
+ case TOK.uns16:
+ case TOK.int32:
+ case TOK.uns32:
+ case TOK.int64:
+ case TOK.uns64:
+ case TOK.int128:
+ case TOK.uns128:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.float80:
+ case TOK.imaginary32:
+ case TOK.imaginary64:
+ case TOK.imaginary80:
+ case TOK.complex32:
+ case TOK.complex64:
+ case TOK.complex80:
+ case TOK.void_:
+ // bug 7773: int.max is always a part of expression
+ if (peekNext() == TOK.dot)
+ goto Lexp;
+ if (peekNext() == TOK.leftParenthesis)
+ goto Lexp;
+ goto case;
+
+ case TOK.alias_:
+ case TOK.const_:
+ case TOK.auto_:
+ case TOK.abstract_:
+ case TOK.extern_:
+ case TOK.align_:
+ case TOK.immutable_:
+ case TOK.shared_:
+ case TOK.inout_:
+ case TOK.deprecated_:
+ case TOK.nothrow_:
+ case TOK.pure_:
+ case TOK.ref_:
+ case TOK.gshared:
+ case TOK.at:
+ case TOK.struct_:
+ case TOK.union_:
+ case TOK.class_:
+ case TOK.interface_:
+ Ldeclaration:
+ {
+ AST.Dsymbols* a = parseDeclarations(false, null, null);
+ if (a.dim > 1)
+ {
+ auto as = new AST.Statements();
+ as.reserve(a.dim);
+ foreach (i; 0 .. a.dim)
+ {
+ AST.Dsymbol d = (*a)[i];
+ s = new AST.ExpStatement(loc, d);
+ as.push(s);
+ }
+ s = new AST.CompoundDeclarationStatement(loc, as);
+ }
+ else if (a.dim == 1)
+ {
+ AST.Dsymbol d = (*a)[0];
+ s = new AST.ExpStatement(loc, d);
+ }
+ else
+ s = new AST.ExpStatement(loc, cast(AST.Expression)null);
+ if (flags & ParseStatementFlags.scope_)
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ break;
+ }
+ case TOK.enum_:
+ {
+ /* Determine if this is a manifest constant declaration,
+ * or a conventional enum.
+ */
+ AST.Dsymbol d;
+ const tv = peekNext();
+ if (tv == TOK.leftCurly || tv == TOK.colon)
+ d = parseEnum();
+ else if (tv != TOK.identifier)
+ goto Ldeclaration;
+ else
+ {
+ const nextv = peekNext2();
+ if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon)
+ d = parseEnum();
+ else
+ goto Ldeclaration;
+ }
+ s = new AST.ExpStatement(loc, d);
+ if (flags & ParseStatementFlags.scope_)
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ break;
+ }
+ case TOK.mixin_:
+ {
+ if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
+ goto Ldeclaration;
+ if (peekNext() == TOK.leftParenthesis)
+ {
+ // mixin(string)
+ AST.Expression e = parseAssignExp();
+ check(TOK.semicolon);
+ if (e.op == TOK.mixin_)
+ {
+ AST.MixinExp cpe = cast(AST.MixinExp)e;
+ s = new AST.CompileStatement(loc, cpe.exps);
+ }
+ else
+ {
+ s = new AST.ExpStatement(loc, e);
+ }
+ break;
+ }
+ AST.Dsymbol d = parseMixin();
+ s = new AST.ExpStatement(loc, d);
+ if (flags & ParseStatementFlags.scope_)
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ break;
+ }
+ case TOK.leftCurly:
+ {
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = Loc.initial;
+
+ nextToken();
+ //if (token.value == TOK.semicolon)
+ // error("use `{ }` for an empty statement, not `;`");
+ auto statements = new AST.Statements();
+ while (token.value != TOK.rightCurly && token.value != TOK.endOfFile)
+ {
+ statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
+ }
+ if (endPtr)
+ *endPtr = token.ptr;
+ endloc = token.loc;
+ if (pEndloc)
+ {
+ *pEndloc = token.loc;
+ pEndloc = null; // don't set it again
+ }
+ s = new AST.CompoundStatement(loc, statements);
+ if (flags & (ParseStatementFlags.scope_ | ParseStatementFlags.curlyScope))
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ check(TOK.rightCurly, "compound statement");
+ lookingForElse = lookingForElseSave;
+ break;
+ }
+ case TOK.while_:
+ {
+ AST.Parameter param = null;
+ nextToken();
+ check(TOK.leftParenthesis);
+ param = parseAssignCondition();
+ AST.Expression condition = parseExpression();
+ check(TOK.rightParenthesis);
+ Loc endloc;
+ AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
+ s = new AST.WhileStatement(loc, condition, _body, endloc, param);
+ break;
+ }
+ case TOK.semicolon:
+ if (!(flags & ParseStatementFlags.semiOk))
+ {
+ if (flags & ParseStatementFlags.semi)
+ deprecation("use `{ }` for an empty statement, not `;`");
+ else
+ error("use `{ }` for an empty statement, not `;`");
+ }
+ nextToken();
+ s = new AST.ExpStatement(loc, cast(AST.Expression)null);
+ break;
+
+ case TOK.do_:
+ {
+ AST.Statement _body;
+ AST.Expression condition;
+
+ nextToken();
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = Loc.initial;
+ _body = parseStatement(ParseStatementFlags.scope_);
+ lookingForElse = lookingForElseSave;
+ check(TOK.while_);
+ check(TOK.leftParenthesis);
+ condition = parseExpression();
+ check(TOK.rightParenthesis);
+ if (token.value == TOK.semicolon)
+ nextToken();
+ else
+ error("terminating `;` required after do-while statement");
+ s = new AST.DoStatement(loc, _body, condition, token.loc);
+ break;
+ }
+ case TOK.for_:
+ {
+ AST.Statement _init;
+ AST.Expression condition;
+ AST.Expression increment;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ if (token.value == TOK.semicolon)
+ {
+ _init = null;
+ nextToken();
+ }
+ else
+ {
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = Loc.initial;
+ _init = parseStatement(0);
+ lookingForElse = lookingForElseSave;
+ }
+ if (token.value == TOK.semicolon)
+ {
+ condition = null;
+ nextToken();
+ }
+ else
+ {
+ condition = parseExpression();
+ check(TOK.semicolon, "`for` condition");
+ }
+ if (token.value == TOK.rightParenthesis)
+ {
+ increment = null;
+ nextToken();
+ }
+ else
+ {
+ increment = parseExpression();
+ check(TOK.rightParenthesis);
+ }
+ Loc endloc;
+ AST.Statement _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
+ s = new AST.ForStatement(loc, _init, condition, increment, _body, endloc);
+ break;
+ }
+ case TOK.foreach_:
+ case TOK.foreach_reverse_:
+ {
+ s = parseForeach!(AST.Statement)(loc, null);
+ break;
+ }
+ case TOK.if_:
+ {
+ AST.Parameter param = null;
+ AST.Expression condition;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ param = parseAssignCondition();
+ condition = parseExpression();
+ check(TOK.rightParenthesis);
+ {
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = loc;
+ ifbody = parseStatement(ParseStatementFlags.scope_);
+ lookingForElse = lookingForElseSave;
+ }
+ if (token.value == TOK.else_)
+ {
+ const elseloc = token.loc;
+ nextToken();
+ elsebody = parseStatement(ParseStatementFlags.scope_);
+ checkDanglingElse(elseloc);
+ }
+ else
+ elsebody = null;
+ if (condition && ifbody)
+ s = new AST.IfStatement(loc, param, condition, ifbody, elsebody, token.loc);
+ else
+ s = null; // don't propagate parsing errors
+ break;
+ }
+
+ case TOK.else_:
+ error("found `else` without a corresponding `if`, `version` or `debug` statement");
+ goto Lerror;
+
+ case TOK.scope_:
+ if (peekNext() != TOK.leftParenthesis)
+ goto Ldeclaration; // scope used as storage class
+ nextToken();
+ check(TOK.leftParenthesis);
+ if (token.value != TOK.identifier)
+ {
+ error("scope identifier expected");
+ goto Lerror;
+ }
+ else
+ {
+ TOK t = TOK.onScopeExit;
+ Identifier id = token.ident;
+ if (id == Id.exit)
+ t = TOK.onScopeExit;
+ else if (id == Id.failure)
+ t = TOK.onScopeFailure;
+ else if (id == Id.success)
+ t = TOK.onScopeSuccess;
+ else
+ error("valid scope identifiers are `exit`, `failure`, or `success`, not `%s`", id.toChars());
+ nextToken();
+ check(TOK.rightParenthesis);
+ AST.Statement st = parseStatement(ParseStatementFlags.scope_);
+ s = new AST.ScopeGuardStatement(loc, t, st);
+ break;
+ }
+
+ case TOK.debug_:
+ nextToken();
+ if (token.value == TOK.assign)
+ {
+ error("debug conditions can only be declared at module scope");
+ nextToken();
+ nextToken();
+ goto Lerror;
+ }
+ cond = parseDebugCondition();
+ goto Lcondition;
+
+ case TOK.version_:
+ nextToken();
+ if (token.value == TOK.assign)
+ {
+ error("version conditions can only be declared at module scope");
+ nextToken();
+ nextToken();
+ goto Lerror;
+ }
+ cond = parseVersionCondition();
+ goto Lcondition;
+
+ Lcondition:
+ {
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = loc;
+ ifbody = parseStatement(0);
+ lookingForElse = lookingForElseSave;
+ }
+ elsebody = null;
+ if (token.value == TOK.else_)
+ {
+ const elseloc = token.loc;
+ nextToken();
+ elsebody = parseStatement(0);
+ checkDanglingElse(elseloc);
+ }
+ s = new AST.ConditionalStatement(loc, cond, ifbody, elsebody);
+ if (flags & ParseStatementFlags.scope_)
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ break;
+
+ case TOK.pragma_:
+ {
+ Identifier ident;
+ AST.Expressions* args = null;
+ AST.Statement _body;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ if (token.value != TOK.identifier)
+ {
+ error("`pragma(identifier)` expected");
+ goto Lerror;
+ }
+ ident = token.ident;
+ nextToken();
+ if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis)
+ args = parseArguments(); // pragma(identifier, args...);
+ else
+ check(TOK.rightParenthesis); // pragma(identifier);
+ if (token.value == TOK.semicolon)
+ {
+ nextToken();
+ _body = null;
+ }
+ else
+ _body = parseStatement(ParseStatementFlags.semi);
+ s = new AST.PragmaStatement(loc, ident, args, _body);
+ break;
+ }
+ case TOK.switch_:
+ isfinal = false;
+ goto Lswitch;
+
+ Lswitch:
+ {
+ nextToken();
+ check(TOK.leftParenthesis);
+ AST.Expression condition = parseExpression();
+ check(TOK.rightParenthesis);
+ AST.Statement _body = parseStatement(ParseStatementFlags.scope_);
+ s = new AST.SwitchStatement(loc, condition, _body, isfinal);
+ break;
+ }
+ case TOK.case_:
+ {
+ AST.Expression exp;
+ AST.Expressions cases; // array of Expression's
+ AST.Expression last = null;
+
+ nextToken();
+ do
+ {
+ exp = parseAssignExp();
+ cases.push(exp);
+ if (token.value != TOK.comma)
+ break;
+ nextToken(); //comma
+ }
+ while (token.value != TOK.colon && token.value != TOK.endOfFile);
+ check(TOK.colon);
+
+ /* case exp: .. case last:
+ */
+ if (token.value == TOK.slice)
+ {
+ if (cases.dim > 1)
+ error("only one `case` allowed for start of case range");
+ nextToken();
+ check(TOK.case_);
+ last = parseAssignExp();
+ check(TOK.colon);
+ }
+
+ if (flags & ParseStatementFlags.curlyScope)
+ {
+ auto statements = new AST.Statements();
+ while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
+ {
+ auto cur = parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
+ statements.push(cur);
+
+ // https://issues.dlang.org/show_bug.cgi?id=21739
+ // Stop at the last break s.t. the following non-case statements are
+ // not merged into the current case. This can happen for
+ // case 1: ... break;
+ // debug { case 2: ... }
+ if (cur && cur.isBreakStatement())
+ break;
+ }
+ s = new AST.CompoundStatement(loc, statements);
+ }
+ else
+ {
+ s = parseStatement(ParseStatementFlags.semi);
+ }
+ s = new AST.ScopeStatement(loc, s, token.loc);
+
+ if (last)
+ {
+ s = new AST.CaseRangeStatement(loc, exp, last, s);
+ }
+ else
+ {
+ // Keep cases in order by building the case statements backwards
+ for (size_t i = cases.dim; i; i--)
+ {
+ exp = cases[i - 1];
+ s = new AST.CaseStatement(loc, exp, s);
+ }
+ }
+ break;
+ }
+ case TOK.default_:
+ {
+ nextToken();
+ check(TOK.colon);
+
+ if (flags & ParseStatementFlags.curlyScope)
+ {
+ auto statements = new AST.Statements();
+ while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly)
+ {
+ statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope));
+ }
+ s = new AST.CompoundStatement(loc, statements);
+ }
+ else
+ s = parseStatement(ParseStatementFlags.semi);
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ s = new AST.DefaultStatement(loc, s);
+ break;
+ }
+ case TOK.return_:
+ {
+ AST.Expression exp;
+ nextToken();
+ exp = token.value == TOK.semicolon ? null : parseExpression();
+ check(TOK.semicolon, "`return` statement");
+ s = new AST.ReturnStatement(loc, exp);
+ break;
+ }
+ case TOK.break_:
+ {
+ Identifier ident;
+ nextToken();
+ ident = null;
+ if (token.value == TOK.identifier)
+ {
+ ident = token.ident;
+ nextToken();
+ }
+ check(TOK.semicolon, "`break` statement");
+ s = new AST.BreakStatement(loc, ident);
+ break;
+ }
+ case TOK.continue_:
+ {
+ Identifier ident;
+ nextToken();
+ ident = null;
+ if (token.value == TOK.identifier)
+ {
+ ident = token.ident;
+ nextToken();
+ }
+ check(TOK.semicolon, "`continue` statement");
+ s = new AST.ContinueStatement(loc, ident);
+ break;
+ }
+ case TOK.goto_:
+ {
+ Identifier ident;
+ nextToken();
+ if (token.value == TOK.default_)
+ {
+ nextToken();
+ s = new AST.GotoDefaultStatement(loc);
+ }
+ else if (token.value == TOK.case_)
+ {
+ AST.Expression exp = null;
+ nextToken();
+ if (token.value != TOK.semicolon)
+ exp = parseExpression();
+ s = new AST.GotoCaseStatement(loc, exp);
+ }
+ else
+ {
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `goto`");
+ ident = null;
+ }
+ else
+ {
+ ident = token.ident;
+ nextToken();
+ }
+ s = new AST.GotoStatement(loc, ident);
+ }
+ check(TOK.semicolon, "`goto` statement");
+ break;
+ }
+ case TOK.synchronized_:
+ {
+ AST.Expression exp;
+ AST.Statement _body;
+
+ Token* t = peek(&token);
+ if (skipAttributes(t, &t) && t.value == TOK.class_)
+ goto Ldeclaration;
+
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ {
+ nextToken();
+ exp = parseExpression();
+ check(TOK.rightParenthesis);
+ }
+ else
+ exp = null;
+ _body = parseStatement(ParseStatementFlags.scope_);
+ s = new AST.SynchronizedStatement(loc, exp, _body);
+ break;
+ }
+ case TOK.with_:
+ {
+ AST.Expression exp;
+ AST.Statement _body;
+ Loc endloc = loc;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ exp = parseExpression();
+ check(TOK.rightParenthesis);
+ _body = parseStatement(ParseStatementFlags.scope_, null, &endloc);
+ s = new AST.WithStatement(loc, exp, _body, endloc);
+ break;
+ }
+ case TOK.try_:
+ {
+ AST.Statement _body;
+ AST.Catches* catches = null;
+ AST.Statement finalbody = null;
+
+ nextToken();
+ const lookingForElseSave = lookingForElse;
+ lookingForElse = Loc.initial;
+ _body = parseStatement(ParseStatementFlags.scope_);
+ lookingForElse = lookingForElseSave;
+ while (token.value == TOK.catch_)
+ {
+ AST.Statement handler;
+ AST.Catch c;
+ AST.Type t;
+ Identifier id;
+ const catchloc = token.loc;
+
+ nextToken();
+ if (token.value != TOK.leftParenthesis)
+ {
+ deprecation("`catch` statement without an exception specification is deprecated");
+ deprecationSupplemental(token.loc, "use `catch(Throwable)` for old behavior");
+ t = null;
+ id = null;
+ }
+ else
+ {
+ check(TOK.leftParenthesis);
+ id = null;
+ t = parseType(&id);
+ check(TOK.rightParenthesis);
+ }
+ handler = parseStatement(0);
+ c = new AST.Catch(catchloc, t, id, handler);
+ if (!catches)
+ catches = new AST.Catches();
+ catches.push(c);
+ }
+
+ if (token.value == TOK.finally_)
+ {
+ nextToken();
+ finalbody = parseStatement(ParseStatementFlags.scope_);
+ }
+
+ s = _body;
+ if (!catches && !finalbody)
+ error("`catch` or `finally` expected following `try`");
+ else
+ {
+ if (catches)
+ s = new AST.TryCatchStatement(loc, _body, catches);
+ if (finalbody)
+ s = new AST.TryFinallyStatement(loc, s, finalbody);
+ }
+ break;
+ }
+ case TOK.throw_:
+ {
+ AST.Expression exp;
+ nextToken();
+ exp = parseExpression();
+ check(TOK.semicolon, "`throw` statement");
+ s = new AST.ThrowStatement(loc, exp);
+ break;
+ }
+
+ case TOK.asm_:
+ s = parseAsm();
+ break;
+
+ case TOK.import_:
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=16088
+ *
+ * At this point it can either be an
+ * https://dlang.org/spec/grammar.html#ImportExpression
+ * or an
+ * https://dlang.org/spec/grammar.html#ImportDeclaration.
+ * See if the next token after `import` is a `(`; if so,
+ * then it is an import expression.
+ */
+ if (peekNext() == TOK.leftParenthesis)
+ {
+ AST.Expression e = parseExpression();
+ check(TOK.semicolon);
+ s = new AST.ExpStatement(loc, e);
+ }
+ else
+ {
+ AST.Dsymbols* imports = parseImport();
+ s = new AST.ImportStatement(loc, imports);
+ if (flags & ParseStatementFlags.scope_)
+ s = new AST.ScopeStatement(loc, s, token.loc);
+ }
+ break;
+ }
+ case TOK.template_:
+ {
+ AST.Dsymbol d = parseTemplateDeclaration();
+ s = new AST.ExpStatement(loc, d);
+ break;
+ }
+ default:
+ error("found `%s` instead of statement", token.toChars());
+ goto Lerror;
+
+ Lerror:
+ while (token.value != TOK.rightCurly && token.value != TOK.semicolon && token.value != TOK.endOfFile)
+ nextToken();
+ if (token.value == TOK.semicolon)
+ nextToken();
+ s = null;
+ break;
+ }
+ if (pEndloc)
+ *pEndloc = prevloc;
+ return s;
+ }
+
+
+ private AST.ExpInitializer parseExpInitializer(Loc loc)
+ {
+ auto ae = parseAssignExp();
+ return new AST.ExpInitializer(loc, ae);
+ }
+
+ private AST.Initializer parseStructInitializer(Loc loc)
+ {
+ /* Scan ahead to discern between a struct initializer and
+ * parameterless function literal.
+ *
+ * We'll scan the topmost curly bracket level for statement-related
+ * tokens, thereby ruling out a struct initializer. (A struct
+ * initializer which itself contains function literals may have
+ * statements at nested curly bracket levels.)
+ *
+ * It's important that this function literal check not be
+ * pendantic, otherwise a function having the slightest syntax
+ * error would emit confusing errors when we proceed to parse it
+ * as a struct initializer.
+ *
+ * The following two ambiguous cases will be treated as a struct
+ * initializer (best we can do without type info):
+ * {}
+ * {{statements...}} - i.e. it could be struct initializer
+ * with one function literal, or function literal having an
+ * extra level of curly brackets
+ * If a function literal is intended in these cases (unlikely),
+ * source can use a more explicit function literal syntax
+ * (e.g. prefix with "()" for empty parameter list).
+ */
+ int braces = 1;
+ int parens = 0;
+ for (auto t = peek(&token); 1; t = peek(t))
+ {
+ switch (t.value)
+ {
+ case TOK.leftParenthesis:
+ parens++;
+ continue;
+ case TOK.rightParenthesis:
+ parens--;
+ continue;
+ // https://issues.dlang.org/show_bug.cgi?id=21163
+ // lambda params can have the `scope` storage class, e.g
+ // `S s = { (scope Type Id){} }`
+ case TOK.scope_:
+ if (!parens) goto case;
+ continue;
+ /* Look for a semicolon or keyword of statements which don't
+ * require a semicolon (typically containing BlockStatement).
+ * Tokens like "else", "catch", etc. are omitted where the
+ * leading token of the statement is sufficient.
+ */
+ case TOK.asm_:
+ case TOK.class_:
+ case TOK.debug_:
+ case TOK.enum_:
+ case TOK.if_:
+ case TOK.interface_:
+ case TOK.pragma_:
+ case TOK.semicolon:
+ case TOK.struct_:
+ case TOK.switch_:
+ case TOK.synchronized_:
+ case TOK.try_:
+ case TOK.union_:
+ case TOK.version_:
+ case TOK.while_:
+ case TOK.with_:
+ if (braces == 1)
+ return parseExpInitializer(loc);
+ continue;
+
+ case TOK.leftCurly:
+ braces++;
+ continue;
+
+ case TOK.rightCurly:
+ if (--braces == 0)
+ break;
+ continue;
+
+ case TOK.endOfFile:
+ break;
+
+ default:
+ continue;
+ }
+ break;
+ }
+
+ auto _is = new AST.StructInitializer(loc);
+ bool commaExpected = false;
+ nextToken();
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.identifier:
+ {
+
+ if (commaExpected)
+ error("comma expected separating field initializers");
+ const t = peek(&token);
+ Identifier id;
+ if (t.value == TOK.colon)
+ {
+ id = token.ident;
+ nextToken();
+ nextToken(); // skip over ':'
+ }
+ auto value = parseInitializer();
+ _is.addInit(id, value);
+ commaExpected = true;
+ continue;
+ }
+ case TOK.comma:
+ if (!commaExpected)
+ error("expression expected, not `,`");
+ nextToken();
+ commaExpected = false;
+ continue;
+
+ case TOK.rightCurly: // allow trailing comma's
+ nextToken();
+ break;
+
+ case TOK.endOfFile:
+ error("found end of file instead of initializer");
+ break;
+
+ default:
+ if (commaExpected)
+ error("comma expected separating field initializers");
+ auto value = parseInitializer();
+ _is.addInit(null, value);
+ commaExpected = true;
+ continue;
+ }
+ break;
+ }
+ return _is;
+
+ }
+
+ /*****************************************
+ * Parse initializer for variable declaration.
+ */
+ private AST.Initializer parseInitializer()
+ {
+ const loc = token.loc;
+
+ switch (token.value)
+ {
+ case TOK.leftCurly:
+ return parseStructInitializer(loc);
+
+ case TOK.leftBracket:
+ /* Scan ahead to see if it is an array initializer or
+ * an expression.
+ * If it ends with a ';' ',' or '}', it is an array initializer.
+ */
+ int brackets = 1;
+ for (auto t = peek(&token); 1; t = peek(t))
+ {
+ switch (t.value)
+ {
+ case TOK.leftBracket:
+ brackets++;
+ continue;
+
+ case TOK.rightBracket:
+ if (--brackets == 0)
+ {
+ t = peek(t);
+ if (t.value != TOK.semicolon && t.value != TOK.comma && t.value != TOK.rightBracket && t.value != TOK.rightCurly)
+ return parseExpInitializer(loc);
+ break;
+ }
+ continue;
+
+ case TOK.endOfFile:
+ break;
+
+ default:
+ continue;
+ }
+ break;
+ }
+
+ auto ia = new AST.ArrayInitializer(loc);
+ bool commaExpected = false;
+
+ nextToken();
+ while (1)
+ {
+ switch (token.value)
+ {
+ default:
+ if (commaExpected)
+ {
+ error("comma expected separating array initializers, not `%s`", token.toChars());
+ nextToken();
+ break;
+ }
+ auto e = parseAssignExp();
+ if (!e)
+ break;
+
+ AST.Initializer value;
+ if (token.value == TOK.colon)
+ {
+ nextToken();
+ value = parseInitializer();
+ }
+ else
+ {
+ value = new AST.ExpInitializer(e.loc, e);
+ e = null;
+ }
+ ia.addInit(e, value);
+ commaExpected = true;
+ continue;
+
+ case TOK.leftCurly:
+ case TOK.leftBracket:
+ if (commaExpected)
+ error("comma expected separating array initializers, not `%s`", token.toChars());
+ auto value = parseInitializer();
+ AST.Expression e;
+
+ if (token.value == TOK.colon)
+ {
+ nextToken();
+ if (auto ei = value.isExpInitializer())
+ {
+ e = ei.exp;
+ value = parseInitializer();
+ }
+ else
+ error("initializer expression expected following colon, not `%s`", token.toChars());
+ }
+ ia.addInit(e, value);
+ commaExpected = true;
+ continue;
+
+ case TOK.comma:
+ if (!commaExpected)
+ error("expression expected, not `,`");
+ nextToken();
+ commaExpected = false;
+ continue;
+
+ case TOK.rightBracket: // allow trailing comma's
+ nextToken();
+ break;
+
+ case TOK.endOfFile:
+ error("found `%s` instead of array initializer", token.toChars());
+ break;
+ }
+ break;
+ }
+ return ia;
+
+ case TOK.void_:
+ const tv = peekNext();
+ if (tv == TOK.semicolon || tv == TOK.comma)
+ {
+ nextToken();
+ return new AST.VoidInitializer(loc);
+ }
+ return parseExpInitializer(loc);
+
+ default:
+ return parseExpInitializer(loc);
+ }
+ }
+
+ /*****************************************
+ * Parses default argument initializer expression that is an assign expression,
+ * with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__.
+ */
+ private AST.Expression parseDefaultInitExp()
+ {
+ AST.Expression e = null;
+ const tv = peekNext();
+ if (tv == TOK.comma || tv == TOK.rightParenthesis)
+ {
+ switch (token.value)
+ {
+ case TOK.file: e = new AST.FileInitExp(token.loc, TOK.file); break;
+ case TOK.fileFullPath: e = new AST.FileInitExp(token.loc, TOK.fileFullPath); break;
+ case TOK.line: e = new AST.LineInitExp(token.loc); break;
+ case TOK.moduleString: e = new AST.ModuleInitExp(token.loc); break;
+ case TOK.functionString: e = new AST.FuncInitExp(token.loc); break;
+ case TOK.prettyFunction: e = new AST.PrettyFuncInitExp(token.loc); break;
+ default: goto LExp;
+ }
+ nextToken();
+ return e;
+ }
+ LExp:
+ return parseAssignExp();
+ }
+
+ /********************
+ * Parse inline assembler block.
+ * Returns:
+ * inline assembler block as a Statement
+ */
+ AST.Statement parseAsm()
+ {
+ // Parse the asm block into a sequence of AsmStatements,
+ // each AsmStatement is one instruction.
+ // Separate out labels.
+ // Defer parsing of AsmStatements until semantic processing.
+
+ const loc = token.loc;
+ Loc labelloc;
+
+ nextToken();
+ StorageClass stc = parsePostfix(STC.undefined_, null);
+ if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild))
+ error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks");
+
+ check(TOK.leftCurly);
+ Token* toklist = null;
+ Token** ptoklist = &toklist;
+ Identifier label = null;
+ auto statements = new AST.Statements();
+ size_t nestlevel = 0;
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.identifier:
+ if (!toklist)
+ {
+ // Look ahead to see if it is a label
+ if (peekNext() == TOK.colon)
+ {
+ // It's a label
+ label = token.ident;
+ labelloc = token.loc;
+ nextToken();
+ nextToken();
+ continue;
+ }
+ }
+ goto default;
+
+ case TOK.leftCurly:
+ ++nestlevel;
+ goto default;
+
+ case TOK.rightCurly:
+ if (nestlevel > 0)
+ {
+ --nestlevel;
+ goto default;
+ }
+ if (toklist || label)
+ {
+ error("`asm` statements must end in `;`");
+ }
+ break;
+
+ case TOK.semicolon:
+ if (nestlevel != 0)
+ error("mismatched number of curly brackets");
+
+ if (toklist || label)
+ {
+ // Create AsmStatement from list of tokens we've saved
+ AST.Statement s = new AST.AsmStatement(token.loc, toklist);
+ toklist = null;
+ ptoklist = &toklist;
+ if (label)
+ {
+ s = new AST.LabelStatement(labelloc, label, s);
+ label = null;
+ }
+ statements.push(s);
+ }
+ nextToken();
+ continue;
+
+ case TOK.endOfFile:
+ /* { */
+ error("matching `}` expected, not end of file");
+ break;
+
+ case TOK.colonColon: // treat as two separate : tokens for iasmgcc
+ *ptoklist = allocateToken();
+ memcpy(*ptoklist, &token, Token.sizeof);
+ (*ptoklist).value = TOK.colon;
+ ptoklist = &(*ptoklist).next;
+
+ *ptoklist = allocateToken();
+ memcpy(*ptoklist, &token, Token.sizeof);
+ (*ptoklist).value = TOK.colon;
+ ptoklist = &(*ptoklist).next;
+
+ *ptoklist = null;
+ nextToken();
+ continue;
+
+ default:
+ *ptoklist = allocateToken();
+ memcpy(*ptoklist, &token, Token.sizeof);
+ ptoklist = &(*ptoklist).next;
+ *ptoklist = null;
+ nextToken();
+ continue;
+ }
+ break;
+ }
+ nextToken();
+ auto s = new AST.CompoundAsmStatement(loc, statements, stc);
+ return s;
+ }
+
+ /**********************************
+ * Issue error if the current token is not `value`,
+ * advance to next token.
+ * Params:
+ * loc = location for error message
+ * value = token value to compare with
+ */
+ void check(Loc loc, TOK value)
+ {
+ if (token.value != value)
+ error(loc, "found `%s` when expecting `%s`", token.toChars(), Token.toChars(value));
+ nextToken();
+ }
+
+ /**********************************
+ * Issue error if the current token is not `value`,
+ * advance to next token.
+ * Params:
+ * value = token value to compare with
+ */
+ void check(TOK value)
+ {
+ check(token.loc, value);
+ }
+
+ /**********************************
+ * Issue error if the current token is not `value`,
+ * advance to next token.
+ * Params:
+ * value = token value to compare with
+ * string = for error message
+ */
+ void check(TOK value, const(char)* string)
+ {
+ if (token.value != value)
+ error("found `%s` when expecting `%s` following %s", token.toChars(), Token.toChars(value), string);
+ nextToken();
+ }
+
+ private void checkParens(TOK value, AST.Expression e)
+ {
+ if (precedence[e.op] == PREC.rel && !e.parens)
+ error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(value));
+ }
+
+ ///
+ enum NeedDeclaratorId
+ {
+ no, // Declarator part must have no identifier
+ opt, // Declarator part identifier is optional
+ must, // Declarator part must have identifier
+ mustIfDstyle, // Declarator part must have identifier, but don't recognize old C-style syntax
+ }
+
+ /************************************
+ * Determine if the scanner is sitting on the start of a declaration.
+ * Params:
+ * t = current token of the scanner
+ * needId = flag with additional requirements for a declaration
+ * endtok = ending token
+ * pt = will be set ending token (if not null)
+ * Output:
+ * true if the token `t` is a declaration, false otherwise
+ */
+ private bool isDeclaration(Token* t, NeedDeclaratorId needId, TOK endtok, Token** pt)
+ {
+ //printf("isDeclaration(needId = %d)\n", needId);
+ int haveId = 0;
+ int haveTpl = 0;
+
+ while (1)
+ {
+ if ((t.value == TOK.const_ || t.value == TOK.immutable_ || t.value == TOK.inout_ || t.value == TOK.shared_) && peek(t).value != TOK.leftParenthesis)
+ {
+ /* const type
+ * immutable type
+ * shared type
+ * wild type
+ */
+ t = peek(t);
+ continue;
+ }
+ break;
+ }
+
+ if (!isBasicType(&t))
+ {
+ goto Lisnot;
+ }
+ if (!isDeclarator(&t, &haveId, &haveTpl, endtok, needId != NeedDeclaratorId.mustIfDstyle))
+ goto Lisnot;
+ if ((needId == NeedDeclaratorId.no && !haveId) ||
+ (needId == NeedDeclaratorId.opt) ||
+ (needId == NeedDeclaratorId.must && haveId) ||
+ (needId == NeedDeclaratorId.mustIfDstyle && haveId))
+ {
+ if (pt)
+ *pt = t;
+ goto Lis;
+ }
+ goto Lisnot;
+
+ Lis:
+ //printf("\tis declaration, t = %s\n", t.toChars());
+ return true;
+
+ Lisnot:
+ //printf("\tis not declaration\n");
+ return false;
+ }
+
+ private bool isBasicType(Token** pt)
+ {
+ // This code parallels parseBasicType()
+ Token* t = *pt;
+ switch (t.value)
+ {
+ case TOK.wchar_:
+ case TOK.dchar_:
+ case TOK.bool_:
+ case TOK.char_:
+ case TOK.int8:
+ case TOK.uns8:
+ case TOK.int16:
+ case TOK.uns16:
+ case TOK.int32:
+ case TOK.uns32:
+ case TOK.int64:
+ case TOK.uns64:
+ case TOK.int128:
+ case TOK.uns128:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.float80:
+ case TOK.imaginary32:
+ case TOK.imaginary64:
+ case TOK.imaginary80:
+ case TOK.complex32:
+ case TOK.complex64:
+ case TOK.complex80:
+ case TOK.void_:
+ t = peek(t);
+ break;
+
+ case TOK.identifier:
+ L5:
+ t = peek(t);
+ if (t.value == TOK.not)
+ {
+ goto L4;
+ }
+ goto L3;
+ while (1)
+ {
+ L2:
+ t = peek(t);
+ L3:
+ if (t.value == TOK.dot)
+ {
+ Ldot:
+ t = peek(t);
+ if (t.value != TOK.identifier)
+ goto Lfalse;
+ t = peek(t);
+ if (t.value != TOK.not)
+ goto L3;
+ L4:
+ /* Seen a !
+ * Look for:
+ * !( args ), !identifier, etc.
+ */
+ t = peek(t);
+ switch (t.value)
+ {
+ case TOK.identifier:
+ goto L5;
+
+ case TOK.leftParenthesis:
+ if (!skipParens(t, &t))
+ goto Lfalse;
+ goto L3;
+
+ case TOK.wchar_:
+ case TOK.dchar_:
+ case TOK.bool_:
+ case TOK.char_:
+ case TOK.int8:
+ case TOK.uns8:
+ case TOK.int16:
+ case TOK.uns16:
+ case TOK.int32:
+ case TOK.uns32:
+ case TOK.int64:
+ case TOK.uns64:
+ case TOK.int128:
+ case TOK.uns128:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.float80:
+ case TOK.imaginary32:
+ case TOK.imaginary64:
+ case TOK.imaginary80:
+ case TOK.complex32:
+ case TOK.complex64:
+ case TOK.complex80:
+ case TOK.void_:
+ case TOK.int32Literal:
+ case TOK.uns32Literal:
+ case TOK.int64Literal:
+ case TOK.uns64Literal:
+ case TOK.int128Literal:
+ case TOK.uns128Literal:
+ case TOK.float32Literal:
+ case TOK.float64Literal:
+ case TOK.float80Literal:
+ case TOK.imaginary32Literal:
+ case TOK.imaginary64Literal:
+ case TOK.imaginary80Literal:
+ case TOK.null_:
+ case TOK.true_:
+ case TOK.false_:
+ case TOK.charLiteral:
+ case TOK.wcharLiteral:
+ case TOK.dcharLiteral:
+ case TOK.string_:
+ case TOK.hexadecimalString:
+ case TOK.file:
+ case TOK.fileFullPath:
+ case TOK.line:
+ case TOK.moduleString:
+ case TOK.functionString:
+ case TOK.prettyFunction:
+ goto L2;
+
+ default:
+ goto Lfalse;
+ }
+ }
+ break;
+ }
+ break;
+
+ case TOK.dot:
+ goto Ldot;
+
+ case TOK.typeof_:
+ case TOK.vector:
+ case TOK.mixin_:
+ /* typeof(exp).identifier...
+ */
+ t = peek(t);
+ if (!skipParens(t, &t))
+ goto Lfalse;
+ goto L3;
+
+ case TOK.traits:
+ // __traits(getMember
+ t = peek(t);
+ if (t.value != TOK.leftParenthesis)
+ goto Lfalse;
+ auto lp = t;
+ t = peek(t);
+ if (t.value != TOK.identifier || t.ident != Id.getMember)
+ goto Lfalse;
+ if (!skipParens(lp, &lp))
+ goto Lfalse;
+ // we are in a lookup for decl VS statement
+ // so we expect a declarator following __trait if it's a type.
+ // other usages wont be ambiguous (alias, template instance, type qual, etc.)
+ if (lp.value != TOK.identifier)
+ goto Lfalse;
+
+ break;
+
+ case TOK.const_:
+ case TOK.immutable_:
+ case TOK.shared_:
+ case TOK.inout_:
+ // const(type) or immutable(type) or shared(type) or wild(type)
+ t = peek(t);
+ if (t.value != TOK.leftParenthesis)
+ goto Lfalse;
+ t = peek(t);
+ if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
+ {
+ goto Lfalse;
+ }
+ t = peek(t);
+ break;
+
+ default:
+ goto Lfalse;
+ }
+ *pt = t;
+ //printf("is\n");
+ return true;
+
+ Lfalse:
+ //printf("is not\n");
+ return false;
+ }
+
+ private bool isDeclarator(Token** pt, int* haveId, int* haveTpl, TOK endtok, bool allowAltSyntax = true)
+ {
+ // This code parallels parseDeclarator()
+ Token* t = *pt;
+ int parens;
+
+ //printf("Parser::isDeclarator() %s\n", t.toChars());
+ if (t.value == TOK.assign)
+ return false;
+
+ while (1)
+ {
+ parens = false;
+ switch (t.value)
+ {
+ case TOK.mul:
+ //case TOK.and:
+ t = peek(t);
+ continue;
+
+ case TOK.leftBracket:
+ t = peek(t);
+ if (t.value == TOK.rightBracket)
+ {
+ t = peek(t);
+ }
+ else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
+ {
+ // It's an associative array declaration
+ t = peek(t);
+
+ // ...[type].ident
+ if (t.value == TOK.dot && peek(t).value == TOK.identifier)
+ {
+ t = peek(t);
+ t = peek(t);
+ }
+ }
+ else
+ {
+ // [ expression ]
+ // [ expression .. expression ]
+ if (!isExpression(&t))
+ return false;
+ if (t.value == TOK.slice)
+ {
+ t = peek(t);
+ if (!isExpression(&t))
+ return false;
+ if (t.value != TOK.rightBracket)
+ return false;
+ t = peek(t);
+ }
+ else
+ {
+ if (t.value != TOK.rightBracket)
+ return false;
+ t = peek(t);
+ // ...[index].ident
+ if (t.value == TOK.dot && peek(t).value == TOK.identifier)
+ {
+ t = peek(t);
+ t = peek(t);
+ }
+ }
+ }
+ continue;
+
+ case TOK.identifier:
+ if (*haveId)
+ return false;
+ *haveId = true;
+ t = peek(t);
+ break;
+
+ case TOK.leftParenthesis:
+ if (!allowAltSyntax)
+ return false; // Do not recognize C-style declarations.
+
+ t = peek(t);
+ if (t.value == TOK.rightParenthesis)
+ return false; // () is not a declarator
+
+ /* Regard ( identifier ) as not a declarator
+ * BUG: what about ( *identifier ) in
+ * f(*p)(x);
+ * where f is a class instance with overloaded () ?
+ * Should we just disallow C-style function pointer declarations?
+ */
+ if (t.value == TOK.identifier)
+ {
+ Token* t2 = peek(t);
+ if (t2.value == TOK.rightParenthesis)
+ return false;
+ }
+
+ if (!isDeclarator(&t, haveId, null, TOK.rightParenthesis))
+ return false;
+ t = peek(t);
+ parens = true;
+ break;
+
+ case TOK.delegate_:
+ case TOK.function_:
+ t = peek(t);
+ if (!isParameters(&t))
+ return false;
+ skipAttributes(t, &t);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ while (1)
+ {
+ switch (t.value)
+ {
+ static if (CARRAYDECL)
+ {
+ case TOK.leftBracket:
+ parens = false;
+ t = peek(t);
+ if (t.value == TOK.rightBracket)
+ {
+ t = peek(t);
+ }
+ else if (isDeclaration(t, NeedDeclaratorId.no, TOK.rightBracket, &t))
+ {
+ // It's an associative array declaration
+ t = peek(t);
+ }
+ else
+ {
+ // [ expression ]
+ if (!isExpression(&t))
+ return false;
+ if (t.value != TOK.rightBracket)
+ return false;
+ t = peek(t);
+ }
+ continue;
+ }
+
+ case TOK.leftParenthesis:
+ parens = false;
+ if (Token* tk = peekPastParen(t))
+ {
+ if (tk.value == TOK.leftParenthesis)
+ {
+ if (!haveTpl)
+ return false;
+ *haveTpl = 1;
+ t = tk;
+ }
+ else if (tk.value == TOK.assign)
+ {
+ if (!haveTpl)
+ return false;
+ *haveTpl = 1;
+ *pt = tk;
+ return true;
+ }
+ }
+ if (!isParameters(&t))
+ return false;
+ while (1)
+ {
+ switch (t.value)
+ {
+ case TOK.const_:
+ case TOK.immutable_:
+ case TOK.shared_:
+ case TOK.inout_:
+ case TOK.pure_:
+ case TOK.nothrow_:
+ case TOK.return_:
+ case TOK.scope_:
+ t = peek(t);
+ continue;
+
+ case TOK.at:
+ t = peek(t); // skip '@'
+ t = peek(t); // skip identifier
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ continue;
+
+ // Valid tokens that follow a declaration
+ case TOK.rightParenthesis:
+ case TOK.rightBracket:
+ case TOK.assign:
+ case TOK.comma:
+ case TOK.dotDotDot:
+ case TOK.semicolon:
+ case TOK.leftCurly:
+ case TOK.in_:
+ case TOK.out_:
+ case TOK.do_:
+ // The !parens is to disallow unnecessary parentheses
+ if (!parens && (endtok == TOK.reserved || endtok == t.value))
+ {
+ *pt = t;
+ return true;
+ }
+ return false;
+
+ case TOK.identifier:
+ if (t.ident == Id._body)
+ {
+ // @@@DEPRECATED@@@
+ // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md
+ // Deprecated in 2.097 - Can be removed from 2.117
+ // The deprecation period is longer than usual as `body`
+ // was quite widely used.
+ deprecation("Usage of the `body` keyword is deprecated. Use `do` instead.");
+ goto case TOK.do_;
+ }
+ goto default;
+
+ case TOK.if_:
+ return haveTpl ? true : false;
+
+ // Used for mixin type parsing
+ case TOK.endOfFile:
+ if (endtok == TOK.endOfFile)
+ goto case TOK.do_;
+ return false;
+
+ default:
+ return false;
+ }
+ }
+ assert(0);
+ }
+
+ private bool isParameters(Token** pt)
+ {
+ // This code parallels parseParameterList()
+ Token* t = *pt;
+
+ //printf("isParameters()\n");
+ if (t.value != TOK.leftParenthesis)
+ return false;
+
+ t = peek(t);
+ for (; 1; t = peek(t))
+ {
+ L1:
+ switch (t.value)
+ {
+ case TOK.rightParenthesis:
+ break;
+
+ case TOK.at:
+ Token* pastAttr;
+ if (skipAttributes(t, &pastAttr))
+ {
+ t = pastAttr;
+ goto default;
+ }
+ break;
+
+ case TOK.dotDotDot:
+ t = peek(t);
+ break;
+
+ case TOK.in_:
+ case TOK.out_:
+ case TOK.ref_:
+ case TOK.lazy_:
+ case TOK.scope_:
+ case TOK.final_:
+ case TOK.auto_:
+ case TOK.return_:
+ continue;
+
+ case TOK.const_:
+ case TOK.immutable_:
+ case TOK.shared_:
+ case TOK.inout_:
+ t = peek(t);
+ if (t.value == TOK.leftParenthesis)
+ {
+ t = peek(t);
+ if (!isDeclaration(t, NeedDeclaratorId.no, TOK.rightParenthesis, &t))
+ return false;
+ t = peek(t); // skip past closing ')'
+ goto L2;
+ }
+ goto L1;
+
+ version (none)
+ {
+ case TOK.static_:
+ continue;
+ case TOK.auto_:
+ case TOK.alias_:
+ t = peek(t);
+ if (t.value == TOK.identifier)
+ t = peek(t);
+ if (t.value == TOK.assign)
+ {
+ t = peek(t);
+ if (!isExpression(&t))
+ return false;
+ }
+ goto L3;
+ }
+
+ default:
+ {
+ if (!isBasicType(&t))
+ return false;
+ L2:
+ int tmp = false;
+ if (t.value != TOK.dotDotDot && !isDeclarator(&t, &tmp, null, TOK.reserved))
+ return false;
+ if (t.value == TOK.assign)
+ {
+ t = peek(t);
+ if (!isExpression(&t))
+ return false;
+ }
+ if (t.value == TOK.dotDotDot)
+ {
+ t = peek(t);
+ break;
+ }
+ }
+ if (t.value == TOK.comma)
+ {
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+ if (t.value != TOK.rightParenthesis)
+ return false;
+ t = peek(t);
+ *pt = t;
+ return true;
+ }
+
+ private bool isExpression(Token** pt)
+ {
+ // This is supposed to determine if something is an expression.
+ // What it actually does is scan until a closing right bracket
+ // is found.
+
+ Token* t = *pt;
+ int brnest = 0;
+ int panest = 0;
+ int curlynest = 0;
+
+ for (;; t = peek(t))
+ {
+ switch (t.value)
+ {
+ case TOK.leftBracket:
+ brnest++;
+ continue;
+
+ case TOK.rightBracket:
+ if (--brnest >= 0)
+ continue;
+ break;
+
+ case TOK.leftParenthesis:
+ panest++;
+ continue;
+
+ case TOK.comma:
+ if (brnest || panest)
+ continue;
+ break;
+
+ case TOK.rightParenthesis:
+ if (--panest >= 0)
+ continue;
+ break;
+
+ case TOK.leftCurly:
+ curlynest++;
+ continue;
+
+ case TOK.rightCurly:
+ if (--curlynest >= 0)
+ continue;
+ return false;
+
+ case TOK.slice:
+ if (brnest)
+ continue;
+ break;
+
+ case TOK.semicolon:
+ if (curlynest)
+ continue;
+ return false;
+
+ case TOK.endOfFile:
+ return false;
+
+ default:
+ continue;
+ }
+ break;
+ }
+
+ *pt = t;
+ return true;
+ }
+
+ /*******************************************
+ * Skip parentheses.
+ * Params:
+ * t = on opening $(LPAREN)
+ * pt = *pt is set to token past '$(RPAREN)' on true
+ * Returns:
+ * true successful
+ * false some parsing error
+ */
+ bool skipParens(Token* t, Token** pt)
+ {
+ if (t.value != TOK.leftParenthesis)
+ return false;
+
+ int parens = 0;
+
+ while (1)
+ {
+ switch (t.value)
+ {
+ case TOK.leftParenthesis:
+ parens++;
+ break;
+
+ case TOK.rightParenthesis:
+ parens--;
+ if (parens < 0)
+ goto Lfalse;
+ if (parens == 0)
+ goto Ldone;
+ break;
+
+ case TOK.endOfFile:
+ goto Lfalse;
+
+ default:
+ break;
+ }
+ t = peek(t);
+ }
+ Ldone:
+ if (pt)
+ *pt = peek(t); // skip found rparen
+ return true;
+
+ Lfalse:
+ return false;
+ }
+
+ private bool skipParensIf(Token* t, Token** pt)
+ {
+ if (t.value != TOK.leftParenthesis)
+ {
+ if (pt)
+ *pt = t;
+ return true;
+ }
+ return skipParens(t, pt);
+ }
+
+ //returns true if the next value (after optional matching parens) is expected
+ private bool hasOptionalParensThen(Token* t, TOK expected)
+ {
+ Token* tk;
+ if (!skipParensIf(t, &tk))
+ return false;
+ return tk.value == expected;
+ }
+
+ /*******************************************
+ * Skip attributes.
+ * Input:
+ * t is on a candidate attribute
+ * Output:
+ * *pt is set to first non-attribute token on success
+ * Returns:
+ * true successful
+ * false some parsing error
+ */
+ private bool skipAttributes(Token* t, Token** pt)
+ {
+ while (1)
+ {
+ switch (t.value)
+ {
+ case TOK.const_:
+ case TOK.immutable_:
+ case TOK.shared_:
+ case TOK.inout_:
+ case TOK.final_:
+ case TOK.auto_:
+ case TOK.scope_:
+ case TOK.override_:
+ case TOK.abstract_:
+ case TOK.synchronized_:
+ break;
+
+ case TOK.deprecated_:
+ if (peek(t).value == TOK.leftParenthesis)
+ {
+ t = peek(t);
+ if (!skipParens(t, &t))
+ goto Lerror;
+ // t is on the next of closing parenthesis
+ continue;
+ }
+ break;
+
+ case TOK.nothrow_:
+ case TOK.pure_:
+ case TOK.ref_:
+ case TOK.gshared:
+ case TOK.return_:
+ break;
+
+ case TOK.at:
+ t = peek(t);
+ if (t.value == TOK.identifier)
+ {
+ /* @identifier
+ * @identifier!arg
+ * @identifier!(arglist)
+ * any of the above followed by (arglist)
+ * @predefined_attribute
+ */
+ if (isBuiltinAtAttribute(t.ident))
+ break;
+ t = peek(t);
+ if (t.value == TOK.not)
+ {
+ t = peek(t);
+ if (t.value == TOK.leftParenthesis)
+ {
+ // @identifier!(arglist)
+ if (!skipParens(t, &t))
+ goto Lerror;
+ // t is on the next of closing parenthesis
+ }
+ else
+ {
+ // @identifier!arg
+ // Do low rent skipTemplateArgument
+ if (t.value == TOK.vector)
+ {
+ // identifier!__vector(type)
+ t = peek(t);
+ if (!skipParens(t, &t))
+ goto Lerror;
+ }
+ else
+ t = peek(t);
+ }
+ }
+ if (t.value == TOK.leftParenthesis)
+ {
+ if (!skipParens(t, &t))
+ goto Lerror;
+ // t is on the next of closing parenthesis
+ continue;
+ }
+ continue;
+ }
+ if (t.value == TOK.leftParenthesis)
+ {
+ // @( ArgumentList )
+ if (!skipParens(t, &t))
+ goto Lerror;
+ // t is on the next of closing parenthesis
+ continue;
+ }
+ goto Lerror;
+
+ default:
+ goto Ldone;
+ }
+ t = peek(t);
+ }
+ Ldone:
+ if (pt)
+ *pt = t;
+ return true;
+
+ Lerror:
+ return false;
+ }
+
+ AST.Expression parseExpression()
+ {
+ auto loc = token.loc;
+
+ //printf("Parser::parseExpression() loc = %d\n", loc.linnum);
+ auto e = parseAssignExp();
+ while (token.value == TOK.comma)
+ {
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.CommaExp(loc, e, e2, false);
+ loc = token.loc;
+ }
+ return e;
+ }
+
+ /********************************* Expression Parser ***************************/
+
+ AST.Expression parsePrimaryExp()
+ {
+ AST.Expression e;
+ AST.Type t;
+ Identifier id;
+ const loc = token.loc;
+
+ //printf("parsePrimaryExp(): loc = %d\n", loc.linnum);
+ switch (token.value)
+ {
+ case TOK.identifier:
+ {
+ if (peekNext() == TOK.arrow)
+ {
+ // skip `identifier ->`
+ nextToken();
+ nextToken();
+ error("use `.` for member lookup, not `->`");
+ goto Lerr;
+ }
+
+ if (peekNext() == TOK.goesTo)
+ goto case_delegate;
+
+ id = token.ident;
+ nextToken();
+ TOK save;
+ if (token.value == TOK.not && (save = peekNext()) != TOK.is_ && save != TOK.in_)
+ {
+ // identifier!(template-argument-list)
+ auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
+ e = new AST.ScopeExp(loc, tempinst);
+ }
+ else
+ e = new AST.IdentifierExp(loc, id);
+ break;
+ }
+ case TOK.dollar:
+ if (!inBrackets)
+ error("`$` is valid only inside [] of index or slice");
+ e = new AST.DollarExp(loc);
+ nextToken();
+ break;
+
+ case TOK.dot:
+ // Signal global scope '.' operator with "" identifier
+ e = new AST.IdentifierExp(loc, Id.empty);
+ break;
+
+ case TOK.this_:
+ e = new AST.ThisExp(loc);
+ nextToken();
+ break;
+
+ case TOK.super_:
+ e = new AST.SuperExp(loc);
+ nextToken();
+ break;
+
+ case TOK.int32Literal:
+ e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint32);
+ nextToken();
+ break;
+
+ case TOK.uns32Literal:
+ e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns32);
+ nextToken();
+ break;
+
+ case TOK.int64Literal:
+ e = new AST.IntegerExp(loc, token.intvalue, AST.Type.tint64);
+ nextToken();
+ break;
+
+ case TOK.uns64Literal:
+ e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tuns64);
+ nextToken();
+ break;
+
+ case TOK.float32Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32);
+ nextToken();
+ break;
+
+ case TOK.float64Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat64);
+ nextToken();
+ break;
+
+ case TOK.float80Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat80);
+ nextToken();
+ break;
+
+ case TOK.imaginary32Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary32);
+ nextToken();
+ break;
+
+ case TOK.imaginary64Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary64);
+ nextToken();
+ break;
+
+ case TOK.imaginary80Literal:
+ e = new AST.RealExp(loc, token.floatvalue, AST.Type.timaginary80);
+ nextToken();
+ break;
+
+ case TOK.null_:
+ e = new AST.NullExp(loc);
+ nextToken();
+ break;
+
+ case TOK.file:
+ {
+ const(char)* s = loc.filename ? loc.filename : mod.ident.toChars();
+ e = new AST.StringExp(loc, s.toDString());
+ nextToken();
+ break;
+ }
+ case TOK.fileFullPath:
+ {
+ assert(loc.isValid(), "__FILE_FULL_PATH__ does not work with an invalid location");
+ const s = FileName.toAbsolute(loc.filename);
+ e = new AST.StringExp(loc, s.toDString());
+ nextToken();
+ break;
+ }
+
+ case TOK.line:
+ e = new AST.IntegerExp(loc, loc.linnum, AST.Type.tint32);
+ nextToken();
+ break;
+
+ case TOK.moduleString:
+ {
+ const(char)* s = md ? md.toChars() : mod.toChars();
+ e = new AST.StringExp(loc, s.toDString());
+ nextToken();
+ break;
+ }
+ case TOK.functionString:
+ e = new AST.FuncInitExp(loc);
+ nextToken();
+ break;
+
+ case TOK.prettyFunction:
+ e = new AST.PrettyFuncInitExp(loc);
+ nextToken();
+ break;
+
+ case TOK.true_:
+ e = new AST.IntegerExp(loc, 1, AST.Type.tbool);
+ nextToken();
+ break;
+
+ case TOK.false_:
+ e = new AST.IntegerExp(loc, 0, AST.Type.tbool);
+ nextToken();
+ break;
+
+ case TOK.charLiteral:
+ e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tchar);
+ nextToken();
+ break;
+
+ case TOK.wcharLiteral:
+ e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.twchar);
+ nextToken();
+ break;
+
+ case TOK.dcharLiteral:
+ e = new AST.IntegerExp(loc, token.unsvalue, AST.Type.tdchar);
+ nextToken();
+ break;
+
+ case TOK.string_:
+ case TOK.hexadecimalString:
+ {
+ // cat adjacent strings
+ auto s = token.ustring;
+ auto len = token.len;
+ auto postfix = token.postfix;
+ while (1)
+ {
+ const prev = token;
+ nextToken();
+ if (token.value == TOK.string_ || token.value == TOK.hexadecimalString)
+ {
+ if (token.postfix)
+ {
+ if (token.postfix != postfix)
+ error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
+ postfix = token.postfix;
+ }
+
+ error("Implicit string concatenation is error-prone and disallowed in D");
+ errorSupplemental(token.loc, "Use the explicit syntax instead " ~
+ "(concatenating literals is `@nogc`): %s ~ %s",
+ prev.toChars(), token.toChars());
+
+ const len1 = len;
+ const len2 = token.len;
+ len = len1 + len2;
+ auto s2 = cast(char*)mem.xmalloc_noscan(len * char.sizeof);
+ memcpy(s2, s, len1 * char.sizeof);
+ memcpy(s2 + len1, token.ustring, len2 * char.sizeof);
+ s = s2;
+ }
+ else
+ break;
+ }
+ e = new AST.StringExp(loc, s[0 .. len], len, 1, postfix);
+ break;
+ }
+ case TOK.void_:
+ t = AST.Type.tvoid;
+ goto LabelX;
+
+ case TOK.int8:
+ t = AST.Type.tint8;
+ goto LabelX;
+
+ case TOK.uns8:
+ t = AST.Type.tuns8;
+ goto LabelX;
+
+ case TOK.int16:
+ t = AST.Type.tint16;
+ goto LabelX;
+
+ case TOK.uns16:
+ t = AST.Type.tuns16;
+ goto LabelX;
+
+ case TOK.int32:
+ t = AST.Type.tint32;
+ goto LabelX;
+
+ case TOK.uns32:
+ t = AST.Type.tuns32;
+ goto LabelX;
+
+ case TOK.int64:
+ t = AST.Type.tint64;
+ goto LabelX;
+
+ case TOK.uns64:
+ t = AST.Type.tuns64;
+ goto LabelX;
+
+ case TOK.int128:
+ t = AST.Type.tint128;
+ goto LabelX;
+
+ case TOK.uns128:
+ t = AST.Type.tuns128;
+ goto LabelX;
+
+ case TOK.float32:
+ t = AST.Type.tfloat32;
+ goto LabelX;
+
+ case TOK.float64:
+ t = AST.Type.tfloat64;
+ goto LabelX;
+
+ case TOK.float80:
+ t = AST.Type.tfloat80;
+ goto LabelX;
+
+ case TOK.imaginary32:
+ t = AST.Type.timaginary32;
+ goto LabelX;
+
+ case TOK.imaginary64:
+ t = AST.Type.timaginary64;
+ goto LabelX;
+
+ case TOK.imaginary80:
+ t = AST.Type.timaginary80;
+ goto LabelX;
+
+ case TOK.complex32:
+ t = AST.Type.tcomplex32;
+ goto LabelX;
+
+ case TOK.complex64:
+ t = AST.Type.tcomplex64;
+ goto LabelX;
+
+ case TOK.complex80:
+ t = AST.Type.tcomplex80;
+ goto LabelX;
+
+ case TOK.bool_:
+ t = AST.Type.tbool;
+ goto LabelX;
+
+ case TOK.char_:
+ t = AST.Type.tchar;
+ goto LabelX;
+
+ case TOK.wchar_:
+ t = AST.Type.twchar;
+ goto LabelX;
+
+ case TOK.dchar_:
+ t = AST.Type.tdchar;
+ goto LabelX;
+ LabelX:
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ {
+ e = new AST.TypeExp(loc, t);
+ e = new AST.CallExp(loc, e, parseArguments());
+ break;
+ }
+ check(TOK.dot, t.toChars());
+ if (token.value != TOK.identifier)
+ {
+ error("found `%s` when expecting identifier following `%s`.", token.toChars(), t.toChars());
+ goto Lerr;
+ }
+ e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
+ nextToken();
+ break;
+
+ case TOK.typeof_:
+ {
+ t = parseTypeof();
+ e = new AST.TypeExp(loc, t);
+ break;
+ }
+ case TOK.vector:
+ {
+ t = parseVector();
+ e = new AST.TypeExp(loc, t);
+ break;
+ }
+ case TOK.typeid_:
+ {
+ nextToken();
+ check(TOK.leftParenthesis, "`typeid`");
+ RootObject o = parseTypeOrAssignExp();
+ check(TOK.rightParenthesis);
+ e = new AST.TypeidExp(loc, o);
+ break;
+ }
+ case TOK.traits:
+ {
+ /* __traits(identifier, args...)
+ */
+ Identifier ident;
+ AST.Objects* args = null;
+
+ nextToken();
+ check(TOK.leftParenthesis);
+ if (token.value != TOK.identifier)
+ {
+ error("`__traits(identifier, args...)` expected");
+ goto Lerr;
+ }
+ ident = token.ident;
+ nextToken();
+ if (token.value == TOK.comma)
+ args = parseTemplateArgumentList(); // __traits(identifier, args...)
+ else
+ check(TOK.rightParenthesis); // __traits(identifier)
+
+ e = new AST.TraitsExp(loc, ident, args);
+ break;
+ }
+ case TOK.is_:
+ {
+ AST.Type targ;
+ Identifier ident = null;
+ AST.Type tspec = null;
+ TOK tok = TOK.reserved;
+ TOK tok2 = TOK.reserved;
+ AST.TemplateParameters* tpl = null;
+
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ {
+ nextToken();
+ if (token.value == TOK.identifier && peekNext() == TOK.leftParenthesis)
+ {
+ error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars());
+ nextToken();
+ Token* tempTok = peekPastParen(&token);
+ memcpy(&token, tempTok, Token.sizeof);
+ goto Lerr;
+ }
+ targ = parseType(&ident);
+ if (token.value == TOK.colon || token.value == TOK.equal)
+ {
+ tok = token.value;
+ nextToken();
+ if (tok == TOK.equal && (token.value == TOK.struct_ || token.value == TOK.union_
+ || token.value == TOK.class_ || token.value == TOK.super_ || token.value == TOK.enum_
+ || token.value == TOK.interface_ || token.value == TOK.package_ || token.value == TOK.module_
+ || token.value == TOK.argumentTypes || token.value == TOK.parameters
+ || token.value == TOK.const_ && peekNext() == TOK.rightParenthesis
+ || token.value == TOK.immutable_ && peekNext() == TOK.rightParenthesis
+ || token.value == TOK.shared_ && peekNext() == TOK.rightParenthesis
+ || token.value == TOK.inout_ && peekNext() == TOK.rightParenthesis || token.value == TOK.function_
+ || token.value == TOK.delegate_ || token.value == TOK.return_
+ || (token.value == TOK.vector && peekNext() == TOK.rightParenthesis)))
+ {
+ tok2 = token.value;
+ nextToken();
+ }
+ else
+ {
+ tspec = parseType();
+ }
+ }
+ if (tspec)
+ {
+ if (token.value == TOK.comma)
+ tpl = parseTemplateParameterList(1);
+ else
+ {
+ tpl = new AST.TemplateParameters();
+ check(TOK.rightParenthesis);
+ }
+ }
+ else
+ check(TOK.rightParenthesis);
+ }
+ else
+ {
+ error("`type identifier : specialization` expected following `is`");
+ goto Lerr;
+ }
+ e = new AST.IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
+ break;
+ }
+ case TOK.assert_:
+ {
+ // https://dlang.org/spec/expression.html#assert_expressions
+ AST.Expression msg = null;
+
+ nextToken();
+ check(TOK.leftParenthesis, "`assert`");
+ e = parseAssignExp();
+ if (token.value == TOK.comma)
+ {
+ nextToken();
+ if (token.value != TOK.rightParenthesis)
+ {
+ msg = parseAssignExp();
+ if (token.value == TOK.comma)
+ nextToken();
+ }
+ }
+ check(TOK.rightParenthesis);
+ e = new AST.AssertExp(loc, e, msg);
+ break;
+ }
+ case TOK.mixin_:
+ {
+ // https://dlang.org/spec/expression.html#mixin_expressions
+ nextToken();
+ if (token.value != TOK.leftParenthesis)
+ error("found `%s` when expecting `%s` following `mixin`", token.toChars(), Token.toChars(TOK.leftParenthesis));
+ auto exps = parseArguments();
+ e = new AST.MixinExp(loc, exps);
+ break;
+ }
+ case TOK.import_:
+ {
+ nextToken();
+ check(TOK.leftParenthesis, "`import`");
+ e = parseAssignExp();
+ check(TOK.rightParenthesis);
+ e = new AST.ImportExp(loc, e);
+ break;
+ }
+ case TOK.new_:
+ e = parseNewExp(null);
+ break;
+
+ case TOK.ref_:
+ {
+ if (peekNext() == TOK.leftParenthesis)
+ {
+ Token* tk = peekPastParen(peek(&token));
+ if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
+ {
+ // ref (arguments) => expression
+ // ref (arguments) { statements... }
+ goto case_delegate;
+ }
+ }
+ nextToken();
+ error("found `%s` when expecting function literal following `ref`", token.toChars());
+ goto Lerr;
+ }
+ case TOK.leftParenthesis:
+ {
+ Token* tk = peekPastParen(&token);
+ if (skipAttributes(tk, &tk) && (tk.value == TOK.goesTo || tk.value == TOK.leftCurly))
+ {
+ // (arguments) => expression
+ // (arguments) { statements... }
+ goto case_delegate;
+ }
+
+ // ( expression )
+ nextToken();
+ e = parseExpression();
+ e.parens = 1;
+ check(loc, TOK.rightParenthesis);
+ break;
+ }
+ case TOK.leftBracket:
+ {
+ /* Parse array literals and associative array literals:
+ * [ value, value, value ... ]
+ * [ key:value, key:value, key:value ... ]
+ */
+ auto values = new AST.Expressions();
+ AST.Expressions* keys = null;
+
+ nextToken();
+ while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
+ {
+ e = parseAssignExp();
+ if (token.value == TOK.colon && (keys || values.dim == 0))
+ {
+ nextToken();
+ if (!keys)
+ keys = new AST.Expressions();
+ keys.push(e);
+ e = parseAssignExp();
+ }
+ else if (keys)
+ {
+ error("`key:value` expected for associative array literal");
+ keys = null;
+ }
+ values.push(e);
+ if (token.value == TOK.rightBracket)
+ break;
+ check(TOK.comma);
+ }
+ check(loc, TOK.rightBracket);
+
+ if (keys)
+ e = new AST.AssocArrayLiteralExp(loc, keys, values);
+ else
+ e = new AST.ArrayLiteralExp(loc, null, values);
+ break;
+ }
+ case TOK.leftCurly:
+ case TOK.function_:
+ case TOK.delegate_:
+ case_delegate:
+ {
+ AST.Dsymbol s = parseFunctionLiteral();
+ e = new AST.FuncExp(loc, s);
+ break;
+ }
+ default:
+ error("expression expected, not `%s`", token.toChars());
+ Lerr:
+ // Anything for e, as long as it's not NULL
+ e = new AST.IntegerExp(loc, 0, AST.Type.tint32);
+ nextToken();
+ break;
+ }
+ return e;
+ }
+
+ private AST.Expression parseUnaryExp()
+ {
+ AST.Expression e;
+ const loc = token.loc;
+
+ switch (token.value)
+ {
+ case TOK.and:
+ nextToken();
+ e = parseUnaryExp();
+ e = new AST.AddrExp(loc, e);
+ break;
+
+ case TOK.plusPlus:
+ nextToken();
+ e = parseUnaryExp();
+ //e = new AddAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
+ e = new AST.PreExp(TOK.prePlusPlus, loc, e);
+ break;
+
+ case TOK.minusMinus:
+ nextToken();
+ e = parseUnaryExp();
+ //e = new MinAssignExp(loc, e, new IntegerExp(loc, 1, Type::tint32));
+ e = new AST.PreExp(TOK.preMinusMinus, loc, e);
+ break;
+
+ case TOK.mul:
+ nextToken();
+ e = parseUnaryExp();
+ e = new AST.PtrExp(loc, e);
+ break;
+
+ case TOK.min:
+ nextToken();
+ e = parseUnaryExp();
+ e = new AST.NegExp(loc, e);
+ break;
+
+ case TOK.add:
+ nextToken();
+ e = parseUnaryExp();
+ e = new AST.UAddExp(loc, e);
+ break;
+
+ case TOK.not:
+ nextToken();
+ e = parseUnaryExp();
+ e = new AST.NotExp(loc, e);
+ break;
+
+ case TOK.tilde:
+ nextToken();
+ e = parseUnaryExp();
+ e = new AST.ComExp(loc, e);
+ break;
+
+ case TOK.delete_:
+ nextToken();
+ e = parseUnaryExp();
+ e = new AST.DeleteExp(loc, e, false);
+ break;
+
+ case TOK.cast_: // cast(type) expression
+ {
+ nextToken();
+ check(TOK.leftParenthesis);
+ /* Look for cast(), cast(const), cast(immutable),
+ * cast(shared), cast(shared const), cast(wild), cast(shared wild)
+ */
+ ubyte m = 0;
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.const_:
+ if (peekNext() == TOK.leftParenthesis)
+ break; // const as type constructor
+ m |= MODFlags.const_; // const as storage class
+ nextToken();
+ continue;
+
+ case TOK.immutable_:
+ if (peekNext() == TOK.leftParenthesis)
+ break;
+ m |= MODFlags.immutable_;
+ nextToken();
+ continue;
+
+ case TOK.shared_:
+ if (peekNext() == TOK.leftParenthesis)
+ break;
+ m |= MODFlags.shared_;
+ nextToken();
+ continue;
+
+ case TOK.inout_:
+ if (peekNext() == TOK.leftParenthesis)
+ break;
+ m |= MODFlags.wild;
+ nextToken();
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ if (token.value == TOK.rightParenthesis)
+ {
+ nextToken();
+ e = parseUnaryExp();
+ e = new AST.CastExp(loc, e, m);
+ }
+ else
+ {
+ AST.Type t = parseType(); // cast( type )
+ t = t.addMod(m); // cast( const type )
+ check(TOK.rightParenthesis);
+ e = parseUnaryExp();
+ e = new AST.CastExp(loc, e, t);
+ }
+ break;
+ }
+ case TOK.inout_:
+ case TOK.shared_:
+ case TOK.const_:
+ case TOK.immutable_: // immutable(type)(arguments) / immutable(type).init
+ {
+ StorageClass stc = parseTypeCtor();
+
+ AST.Type t = parseBasicType();
+ t = t.addSTC(stc);
+
+ if (stc == 0 && token.value == TOK.dot)
+ {
+ nextToken();
+ if (token.value != TOK.identifier)
+ {
+ error("identifier expected following `(type)`.");
+ return null;
+ }
+ e = new AST.DotIdExp(loc, new AST.TypeExp(loc, t), token.ident);
+ nextToken();
+ e = parsePostExp(e);
+ }
+ else
+ {
+ e = new AST.TypeExp(loc, t);
+ if (token.value != TOK.leftParenthesis)
+ {
+ error("`(arguments)` expected following `%s`", t.toChars());
+ return e;
+ }
+ e = new AST.CallExp(loc, e, parseArguments());
+ }
+ break;
+ }
+ case TOK.leftParenthesis:
+ {
+ auto tk = peek(&token);
+ static if (CCASTSYNTAX)
+ {
+ // If cast
+ if (isDeclaration(tk, NeedDeclaratorId.no, TOK.rightParenthesis, &tk))
+ {
+ tk = peek(tk); // skip over right parenthesis
+ switch (tk.value)
+ {
+ case TOK.not:
+ tk = peek(tk);
+ if (tk.value == TOK.is_ || tk.value == TOK.in_) // !is or !in
+ break;
+ goto case;
+
+ case TOK.dot:
+ case TOK.plusPlus:
+ case TOK.minusMinus:
+ case TOK.delete_:
+ case TOK.new_:
+ case TOK.leftParenthesis:
+ case TOK.identifier:
+ case TOK.this_:
+ case TOK.super_:
+ case TOK.int32Literal:
+ case TOK.uns32Literal:
+ case TOK.int64Literal:
+ case TOK.uns64Literal:
+ case TOK.int128Literal:
+ case TOK.uns128Literal:
+ case TOK.float32Literal:
+ case TOK.float64Literal:
+ case TOK.float80Literal:
+ case TOK.imaginary32Literal:
+ case TOK.imaginary64Literal:
+ case TOK.imaginary80Literal:
+ case TOK.null_:
+ case TOK.true_:
+ case TOK.false_:
+ case TOK.charLiteral:
+ case TOK.wcharLiteral:
+ case TOK.dcharLiteral:
+ case TOK.string_:
+ version (none)
+ {
+ case TOK.tilde:
+ case TOK.and:
+ case TOK.mul:
+ case TOK.min:
+ case TOK.add:
+ }
+ case TOK.function_:
+ case TOK.delegate_:
+ case TOK.typeof_:
+ case TOK.traits:
+ case TOK.vector:
+ case TOK.file:
+ case TOK.fileFullPath:
+ case TOK.line:
+ case TOK.moduleString:
+ case TOK.functionString:
+ case TOK.prettyFunction:
+ case TOK.wchar_:
+ case TOK.dchar_:
+ case TOK.bool_:
+ case TOK.char_:
+ case TOK.int8:
+ case TOK.uns8:
+ case TOK.int16:
+ case TOK.uns16:
+ case TOK.int32:
+ case TOK.uns32:
+ case TOK.int64:
+ case TOK.uns64:
+ case TOK.int128:
+ case TOK.uns128:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.float80:
+ case TOK.imaginary32:
+ case TOK.imaginary64:
+ case TOK.imaginary80:
+ case TOK.complex32:
+ case TOK.complex64:
+ case TOK.complex80:
+ case TOK.void_:
+ {
+ // (type) una_exp
+ nextToken();
+ auto t = parseType();
+ check(TOK.rightParenthesis);
+
+ // if .identifier
+ // or .identifier!( ... )
+ if (token.value == TOK.dot)
+ {
+ if (peekNext() != TOK.identifier && peekNext() != TOK.new_)
+ {
+ error("identifier or new keyword expected following `(...)`.");
+ return null;
+ }
+ e = new AST.TypeExp(loc, t);
+ e.parens = true;
+ e = parsePostExp(e);
+ }
+ else
+ {
+ e = parseUnaryExp();
+ e = new AST.CastExp(loc, e, t);
+ error("C style cast illegal, use `%s`", e.toChars());
+ }
+ return e;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ e = parsePrimaryExp();
+ e = parsePostExp(e);
+ break;
+ }
+ default:
+ e = parsePrimaryExp();
+ e = parsePostExp(e);
+ break;
+ }
+ assert(e);
+
+ // ^^ is right associative and has higher precedence than the unary operators
+ while (token.value == TOK.pow)
+ {
+ nextToken();
+ AST.Expression e2 = parseUnaryExp();
+ e = new AST.PowExp(loc, e, e2);
+ }
+
+ return e;
+ }
+
+ private AST.Expression parsePostExp(AST.Expression e)
+ {
+ while (1)
+ {
+ const loc = token.loc;
+ switch (token.value)
+ {
+ case TOK.dot:
+ nextToken();
+ if (token.value == TOK.identifier)
+ {
+ Identifier id = token.ident;
+
+ nextToken();
+ if (token.value == TOK.not && peekNext() != TOK.is_ && peekNext() != TOK.in_)
+ {
+ AST.Objects* tiargs = parseTemplateArguments();
+ e = new AST.DotTemplateInstanceExp(loc, e, id, tiargs);
+ }
+ else
+ e = new AST.DotIdExp(loc, e, id);
+ continue;
+ }
+ if (token.value == TOK.new_)
+ {
+ e = parseNewExp(e);
+ continue;
+ }
+ error("identifier or `new` expected following `.`, not `%s`", token.toChars());
+ break;
+
+ case TOK.plusPlus:
+ e = new AST.PostExp(TOK.plusPlus, loc, e);
+ break;
+
+ case TOK.minusMinus:
+ e = new AST.PostExp(TOK.minusMinus, loc, e);
+ break;
+
+ case TOK.leftParenthesis:
+ e = new AST.CallExp(loc, e, parseArguments());
+ continue;
+
+ case TOK.leftBracket:
+ {
+ // array dereferences:
+ // array[index]
+ // array[]
+ // array[lwr .. upr]
+ AST.Expression index;
+ AST.Expression upr;
+ auto arguments = new AST.Expressions();
+
+ inBrackets++;
+ nextToken();
+ while (token.value != TOK.rightBracket && token.value != TOK.endOfFile)
+ {
+ index = parseAssignExp();
+ if (token.value == TOK.slice)
+ {
+ // array[..., lwr..upr, ...]
+ nextToken();
+ upr = parseAssignExp();
+ arguments.push(new AST.IntervalExp(loc, index, upr));
+ }
+ else
+ arguments.push(index);
+ if (token.value == TOK.rightBracket)
+ break;
+ check(TOK.comma);
+ }
+ check(TOK.rightBracket);
+ inBrackets--;
+ e = new AST.ArrayExp(loc, e, arguments);
+ continue;
+ }
+ default:
+ return e;
+ }
+ nextToken();
+ }
+ }
+
+ private AST.Expression parseMulExp()
+ {
+ const loc = token.loc;
+ auto e = parseUnaryExp();
+
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.mul:
+ nextToken();
+ auto e2 = parseUnaryExp();
+ e = new AST.MulExp(loc, e, e2);
+ continue;
+
+ case TOK.div:
+ nextToken();
+ auto e2 = parseUnaryExp();
+ e = new AST.DivExp(loc, e, e2);
+ continue;
+
+ case TOK.mod:
+ nextToken();
+ auto e2 = parseUnaryExp();
+ e = new AST.ModExp(loc, e, e2);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ return e;
+ }
+
+ private AST.Expression parseAddExp()
+ {
+ const loc = token.loc;
+ auto e = parseMulExp();
+
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.add:
+ nextToken();
+ auto e2 = parseMulExp();
+ e = new AST.AddExp(loc, e, e2);
+ continue;
+
+ case TOK.min:
+ nextToken();
+ auto e2 = parseMulExp();
+ e = new AST.MinExp(loc, e, e2);
+ continue;
+
+ case TOK.tilde:
+ nextToken();
+ auto e2 = parseMulExp();
+ e = new AST.CatExp(loc, e, e2);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ return e;
+ }
+
+ private AST.Expression parseShiftExp()
+ {
+ const loc = token.loc;
+ auto e = parseAddExp();
+
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOK.leftShift:
+ nextToken();
+ auto e2 = parseAddExp();
+ e = new AST.ShlExp(loc, e, e2);
+ continue;
+
+ case TOK.rightShift:
+ nextToken();
+ auto e2 = parseAddExp();
+ e = new AST.ShrExp(loc, e, e2);
+ continue;
+
+ case TOK.unsignedRightShift:
+ nextToken();
+ auto e2 = parseAddExp();
+ e = new AST.UshrExp(loc, e, e2);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ return e;
+ }
+
+ private AST.Expression parseCmpExp()
+ {
+ const loc = token.loc;
+
+ auto e = parseShiftExp();
+ TOK op = token.value;
+
+ switch (op)
+ {
+ case TOK.equal:
+ case TOK.notEqual:
+ nextToken();
+ auto e2 = parseShiftExp();
+ e = new AST.EqualExp(op, loc, e, e2);
+ break;
+
+ case TOK.is_:
+ op = TOK.identity;
+ goto L1;
+
+ case TOK.not:
+ {
+ // Attempt to identify '!is'
+ const tv = peekNext();
+ if (tv == TOK.in_)
+ {
+ nextToken();
+ nextToken();
+ auto e2 = parseShiftExp();
+ e = new AST.InExp(loc, e, e2);
+ e = new AST.NotExp(loc, e);
+ break;
+ }
+ if (tv != TOK.is_)
+ break;
+ nextToken();
+ op = TOK.notIdentity;
+ goto L1;
+ }
+ L1:
+ nextToken();
+ auto e2 = parseShiftExp();
+ e = new AST.IdentityExp(op, loc, e, e2);
+ break;
+
+ case TOK.lessThan:
+ case TOK.lessOrEqual:
+ case TOK.greaterThan:
+ case TOK.greaterOrEqual:
+ nextToken();
+ auto e2 = parseShiftExp();
+ e = new AST.CmpExp(op, loc, e, e2);
+ break;
+
+ case TOK.in_:
+ nextToken();
+ auto e2 = parseShiftExp();
+ e = new AST.InExp(loc, e, e2);
+ break;
+
+ default:
+ break;
+ }
+ return e;
+ }
+
+ private AST.Expression parseAndExp()
+ {
+ Loc loc = token.loc;
+ auto e = parseCmpExp();
+ while (token.value == TOK.and)
+ {
+ checkParens(TOK.and, e);
+ nextToken();
+ auto e2 = parseCmpExp();
+ checkParens(TOK.and, e2);
+ e = new AST.AndExp(loc, e, e2);
+ loc = token.loc;
+ }
+ return e;
+ }
+
+ private AST.Expression parseXorExp()
+ {
+ const loc = token.loc;
+
+ auto e = parseAndExp();
+ while (token.value == TOK.xor)
+ {
+ checkParens(TOK.xor, e);
+ nextToken();
+ auto e2 = parseAndExp();
+ checkParens(TOK.xor, e2);
+ e = new AST.XorExp(loc, e, e2);
+ }
+ return e;
+ }
+
+ private AST.Expression parseOrExp()
+ {
+ const loc = token.loc;
+
+ auto e = parseXorExp();
+ while (token.value == TOK.or)
+ {
+ checkParens(TOK.or, e);
+ nextToken();
+ auto e2 = parseXorExp();
+ checkParens(TOK.or, e2);
+ e = new AST.OrExp(loc, e, e2);
+ }
+ return e;
+ }
+
+ private AST.Expression parseAndAndExp()
+ {
+ const loc = token.loc;
+
+ auto e = parseOrExp();
+ while (token.value == TOK.andAnd)
+ {
+ nextToken();
+ auto e2 = parseOrExp();
+ e = new AST.LogicalExp(loc, TOK.andAnd, e, e2);
+ }
+ return e;
+ }
+
+ private AST.Expression parseOrOrExp()
+ {
+ const loc = token.loc;
+
+ auto e = parseAndAndExp();
+ while (token.value == TOK.orOr)
+ {
+ nextToken();
+ auto e2 = parseAndAndExp();
+ e = new AST.LogicalExp(loc, TOK.orOr, e, e2);
+ }
+ return e;
+ }
+
+ private AST.Expression parseCondExp()
+ {
+ const loc = token.loc;
+
+ auto e = parseOrOrExp();
+ if (token.value == TOK.question)
+ {
+ nextToken();
+ auto e1 = parseExpression();
+ check(TOK.colon);
+ auto e2 = parseCondExp();
+ e = new AST.CondExp(loc, e, e1, e2);
+ }
+ return e;
+ }
+
+ AST.Expression parseAssignExp()
+ {
+ AST.Expression e;
+ e = parseCondExp();
+ if (e is null)
+ return e;
+
+ // require parens for e.g. `t ? a = 1 : b = 2`
+ if (e.op == TOK.question && !e.parens && precedence[token.value] == PREC.assign)
+ dmd.errors.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`",
+ e.toChars(), Token.toChars(token.value));
+
+ const loc = token.loc;
+ switch (token.value)
+ {
+ case TOK.assign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.AssignExp(loc, e, e2);
+ break;
+
+ case TOK.addAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.AddAssignExp(loc, e, e2);
+ break;
+
+ case TOK.minAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.MinAssignExp(loc, e, e2);
+ break;
+
+ case TOK.mulAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.MulAssignExp(loc, e, e2);
+ break;
+
+ case TOK.divAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.DivAssignExp(loc, e, e2);
+ break;
+
+ case TOK.modAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.ModAssignExp(loc, e, e2);
+ break;
+
+ case TOK.powAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.PowAssignExp(loc, e, e2);
+ break;
+
+ case TOK.andAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.AndAssignExp(loc, e, e2);
+ break;
+
+ case TOK.orAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.OrAssignExp(loc, e, e2);
+ break;
+
+ case TOK.xorAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.XorAssignExp(loc, e, e2);
+ break;
+
+ case TOK.leftShiftAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.ShlAssignExp(loc, e, e2);
+ break;
+
+ case TOK.rightShiftAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.ShrAssignExp(loc, e, e2);
+ break;
+
+ case TOK.unsignedRightShiftAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.UshrAssignExp(loc, e, e2);
+ break;
+
+ case TOK.concatenateAssign:
+ nextToken();
+ auto e2 = parseAssignExp();
+ e = new AST.CatAssignExp(loc, e, e2);
+ break;
+
+ default:
+ break;
+ }
+
+ return e;
+ }
+
+ /*************************
+ * Collect argument list.
+ * Assume current token is ',', '$(LPAREN)' or '['.
+ */
+ private AST.Expressions* parseArguments()
+ {
+ // function call
+ AST.Expressions* arguments;
+
+ arguments = new AST.Expressions();
+ const endtok = token.value == TOK.leftBracket ? TOK.rightBracket : TOK.rightParenthesis;
+
+ nextToken();
+
+ while (token.value != endtok && token.value != TOK.endOfFile)
+ {
+ auto arg = parseAssignExp();
+ arguments.push(arg);
+ if (token.value != TOK.comma)
+ break;
+
+ nextToken(); //comma
+ }
+
+ check(endtok);
+
+ return arguments;
+ }
+
+ /*******************************************
+ */
+ private AST.Expression parseNewExp(AST.Expression thisexp)
+ {
+ const loc = token.loc;
+
+ nextToken();
+ AST.Expressions* newargs = null;
+ AST.Expressions* arguments = null;
+ if (token.value == TOK.leftParenthesis)
+ {
+ newargs = parseArguments();
+ }
+
+ // An anonymous nested class starts with "class"
+ if (token.value == TOK.class_)
+ {
+ nextToken();
+ if (token.value == TOK.leftParenthesis)
+ arguments = parseArguments();
+
+ AST.BaseClasses* baseclasses = null;
+ if (token.value != TOK.leftCurly)
+ baseclasses = parseBaseClasses();
+
+ Identifier id = null;
+ AST.Dsymbols* members = null;
+
+ if (token.value != TOK.leftCurly)
+ {
+ error("`{ members }` expected for anonymous class");
+ }
+ else
+ {
+ nextToken();
+ members = parseDeclDefs(0);
+ if (token.value != TOK.rightCurly)
+ error("class member expected");
+ nextToken();
+ }
+
+ auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false);
+ auto e = new AST.NewAnonClassExp(loc, thisexp, newargs, cd, arguments);
+ return e;
+ }
+
+ const stc = parseTypeCtor();
+ auto t = parseBasicType(true);
+ t = parseTypeSuffixes(t);
+ t = t.addSTC(stc);
+ if (t.ty == Taarray)
+ {
+ AST.TypeAArray taa = cast(AST.TypeAArray)t;
+ AST.Type index = taa.index;
+ auto edim = AST.typeToExpression(index);
+ if (!edim)
+ {
+ error("cannot create a `%s` with `new`", t.toChars);
+ return new AST.NullExp(loc);
+ }
+ t = new AST.TypeSArray(taa.next, edim);
+ }
+ else if (token.value == TOK.leftParenthesis && t.ty != Tsarray)
+ {
+ arguments = parseArguments();
+ }
+
+ auto e = new AST.NewExp(loc, thisexp, newargs, t, arguments);
+ return e;
+ }
+
+ /**********************************************
+ */
+ private void addComment(AST.Dsymbol s, const(char)* blockComment)
+ {
+ if (s !is null)
+ this.addComment(s, blockComment.toDString());
+ }
+
+ private void addComment(AST.Dsymbol s, const(char)[] blockComment)
+ {
+ if (s !is null)
+ {
+ s.addComment(combineComments(blockComment, token.lineComment, true));
+ token.lineComment = null;
+ }
+ }
+
+ /**********************************************
+ * Recognize builtin @ attributes
+ * Params:
+ * ident = identifier
+ * Returns:
+ * storage class for attribute, 0 if not
+ */
+ static StorageClass isBuiltinAtAttribute(Identifier ident)
+ {
+ return (ident == Id.property) ? STC.property :
+ (ident == Id.nogc) ? STC.nogc :
+ (ident == Id.safe) ? STC.safe :
+ (ident == Id.trusted) ? STC.trusted :
+ (ident == Id.system) ? STC.system :
+ (ident == Id.live) ? STC.live :
+ (ident == Id.future) ? STC.future :
+ (ident == Id.disable) ? STC.disable :
+ 0;
+ }
+
+ enum StorageClass atAttrGroup =
+ STC.property |
+ STC.nogc |
+ STC.safe |
+ STC.trusted |
+ STC.system |
+ STC.live |
+ /*STC.future |*/ // probably should be included
+ STC.disable;
+ }
+
+enum PREC : int
+{
+ zero,
+ expr,
+ assign,
+ cond,
+ oror,
+ andand,
+ or,
+ xor,
+ and,
+ equal,
+ rel,
+ shift,
+ add,
+ mul,
+ pow,
+ unary,
+ primary,
+}
diff --git a/gcc/d/dmd/parsetimevisitor.d b/gcc/d/dmd/parsetimevisitor.d
new file mode 100644
index 00000000000..d3e30861a8a
--- /dev/null
+++ b/gcc/d/dmd/parsetimevisitor.d
@@ -0,0 +1,297 @@
+/**
+ * Defines a visitor for the AST.
+ *
+ * Other visitors derive from this class.
+ *
+ * Documentation: https://dlang.org/phobos/dmd_parsetimevisitor.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parsetimevisitor.d
+ */
+
+module dmd.parsetimevisitor;
+
+/** Basic and dumm visitor which implements a visit method for each AST node
+ * implemented in AST. This visitor is the parent of strict, transitive
+ * and permissive visitors.
+ */
+extern (C++) class ParseTimeVisitor(AST)
+{
+public:
+ void visit(AST.Dsymbol) { assert(0); }
+ void visit(AST.Parameter) { assert(0); }
+ void visit(AST.Statement) { assert(0); }
+ void visit(AST.Type) { assert(0); }
+ void visit(AST.Expression) { assert(0); }
+ void visit(AST.TemplateParameter) { assert(0); }
+ void visit(AST.Condition) { assert(0); }
+ void visit(AST.Initializer) { assert(0); }
+
+ //=======================================================================================
+ // Dsymbols
+ void visit(AST.AliasThis s) { visit(cast(AST.Dsymbol)s); }
+ void visit(AST.Declaration s) { visit(cast(AST.Dsymbol)s); }
+ void visit(AST.ScopeDsymbol s) { visit(cast(AST.Dsymbol)s); }
+ void visit(AST.Import s) { visit(cast(AST.Dsymbol)s); }
+ void visit(AST.AttribDeclaration s) { visit(cast(AST.Dsymbol)s); }
+ void visit(AST.StaticAssert s) { visit(cast(AST.Dsymbol)s); }
+ void visit(AST.DebugSymbol s) { visit(cast(AST.Dsymbol)s); }
+ void visit(AST.VersionSymbol s) { visit(cast(AST.Dsymbol)s); }
+ void visit(AST.AliasAssign s) { visit(cast(AST.Dsymbol)s); }
+
+ // ScopeDsymbols
+ void visit(AST.Package s) { visit(cast(AST.ScopeDsymbol)s); }
+ void visit(AST.EnumDeclaration s) { visit(cast(AST.ScopeDsymbol)s); }
+ void visit(AST.AggregateDeclaration s) { visit(cast(AST.ScopeDsymbol)s); }
+ void visit(AST.TemplateDeclaration s) { visit(cast(AST.ScopeDsymbol)s); }
+ void visit(AST.TemplateInstance s) { visit(cast(AST.ScopeDsymbol)s); }
+ void visit(AST.Nspace s) { visit(cast(AST.ScopeDsymbol)s); }
+
+ //=========================================================================================
+ // Declarations
+ void visit(AST.VarDeclaration s) { visit(cast(AST.Declaration)s); }
+ void visit(AST.FuncDeclaration s) { visit(cast(AST.Declaration)s); }
+ void visit(AST.AliasDeclaration s) { visit(cast(AST.Declaration)s); }
+ void visit(AST.TupleDeclaration s) { visit(cast(AST.Declaration)s); }
+
+ // FuncDeclarations
+ void visit(AST.FuncLiteralDeclaration s) { visit(cast(AST.FuncDeclaration)s); }
+ void visit(AST.PostBlitDeclaration s) { visit(cast(AST.FuncDeclaration)s); }
+ void visit(AST.CtorDeclaration s) { visit(cast(AST.FuncDeclaration)s); }
+ void visit(AST.DtorDeclaration s) { visit(cast(AST.FuncDeclaration)s); }
+ void visit(AST.InvariantDeclaration s) { visit(cast(AST.FuncDeclaration)s); }
+ void visit(AST.UnitTestDeclaration s) { visit(cast(AST.FuncDeclaration)s); }
+ void visit(AST.NewDeclaration s) { visit(cast(AST.FuncDeclaration)s); }
+ void visit(AST.StaticCtorDeclaration s) { visit(cast(AST.FuncDeclaration)s); }
+ void visit(AST.StaticDtorDeclaration s) { visit(cast(AST.FuncDeclaration)s); }
+ void visit(AST.SharedStaticCtorDeclaration s) { visit(cast(AST.StaticCtorDeclaration)s); }
+ void visit(AST.SharedStaticDtorDeclaration s) { visit(cast(AST.StaticDtorDeclaration)s); }
+
+ // AttribDeclarations
+ void visit(AST.CompileDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.UserAttributeDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.LinkDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.AnonDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.AlignDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.CPPMangleDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.CPPNamespaceDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.VisibilityDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.PragmaDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.StorageClassDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.ConditionalDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+ void visit(AST.StaticForeachDeclaration s) { visit(cast(AST.AttribDeclaration)s); }
+
+ //==============================================================================================
+ // Miscellaneous
+ void visit(AST.DeprecatedDeclaration s) { visit(cast(AST.StorageClassDeclaration)s); }
+ void visit(AST.StaticIfDeclaration s) { visit(cast(AST.ConditionalDeclaration)s); }
+ void visit(AST.EnumMember s) { visit(cast(AST.VarDeclaration)s); }
+ void visit(AST.Module s) { visit(cast(AST.Package)s); }
+ void visit(AST.StructDeclaration s) { visit(cast(AST.AggregateDeclaration)s); }
+ void visit(AST.UnionDeclaration s) { visit(cast(AST.StructDeclaration)s); }
+ void visit(AST.ClassDeclaration s) { visit(cast(AST.AggregateDeclaration)s); }
+ void visit(AST.InterfaceDeclaration s) { visit(cast(AST.ClassDeclaration)s); }
+ void visit(AST.TemplateMixin s) { visit(cast(AST.TemplateInstance)s); }
+ void visit(AST.BitFieldDeclaration s) { visit(cast(AST.VarDeclaration)s); }
+
+ //============================================================================================
+ // Statements
+ void visit(AST.ImportStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ScopeStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ReturnStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.LabelStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.StaticAssertStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.CompileStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.WhileStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ForStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.DoStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ForeachRangeStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ForeachStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.IfStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ScopeGuardStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ConditionalStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.StaticForeachStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.PragmaStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.SwitchStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.CaseRangeStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.CaseStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.DefaultStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.BreakStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ContinueStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.GotoDefaultStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.GotoCaseStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.GotoStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.SynchronizedStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.WithStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.TryCatchStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.TryFinallyStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ThrowStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.AsmStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.ExpStatement s) { visit(cast(AST.Statement)s); }
+ void visit(AST.CompoundStatement s) { visit(cast(AST.Statement)s); }
+
+ // CompoundStatements
+ void visit(AST.CompoundDeclarationStatement s) { visit(cast(AST.CompoundStatement)s); }
+ void visit(AST.CompoundAsmStatement s) { visit(cast(AST.CompoundStatement)s); }
+
+ // AsmStatements
+ void visit(AST.InlineAsmStatement s) { visit(cast(AST.AsmStatement)s); }
+ void visit(AST.GccAsmStatement s) { visit(cast(AST.AsmStatement)s); }
+
+ //=========================================================================================
+ // Types
+ void visit(AST.TypeBasic t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeError t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeNull t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeNoreturn t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeVector t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeEnum t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeTuple t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeClass t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeStruct t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeNext t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeQualified t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeTraits t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeMixin t) { visit(cast(AST.Type)t); }
+ void visit(AST.TypeTag t) { visit(cast(AST.Type)t); }
+
+ // TypeNext
+ void visit(AST.TypeReference t) { visit(cast(AST.TypeNext)t); }
+ void visit(AST.TypeSlice t) { visit(cast(AST.TypeNext)t); }
+ void visit(AST.TypeDelegate t) { visit(cast(AST.TypeNext)t); }
+ void visit(AST.TypePointer t) { visit(cast(AST.TypeNext)t); }
+ void visit(AST.TypeFunction t) { visit(cast(AST.TypeNext)t); }
+ void visit(AST.TypeArray t) { visit(cast(AST.TypeNext)t); }
+
+ // TypeArray
+ void visit(AST.TypeDArray t) { visit(cast(AST.TypeArray)t); }
+ void visit(AST.TypeAArray t) { visit(cast(AST.TypeArray)t); }
+ void visit(AST.TypeSArray t) { visit(cast(AST.TypeArray)t); }
+
+ // TypeQualified
+ void visit(AST.TypeIdentifier t) { visit(cast(AST.TypeQualified)t); }
+ void visit(AST.TypeReturn t) { visit(cast(AST.TypeQualified)t); }
+ void visit(AST.TypeTypeof t) { visit(cast(AST.TypeQualified)t); }
+ void visit(AST.TypeInstance t) { visit(cast(AST.TypeQualified)t); }
+
+ //=================================================================================
+ // Expressions
+ void visit(AST.DeclarationExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.IntegerExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.NewAnonClassExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.IsExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.RealExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.NullExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.TypeidExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.TraitsExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.StringExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.NewExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.AssocArrayLiteralExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.ArrayLiteralExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.MixinExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.FuncExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.IntervalExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.TypeExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.ScopeExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.IdentifierExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.UnaExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.DefaultInitExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.BinExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.DsymbolExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.TemplateExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.SymbolExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.TupleExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.ThisExp e) { visit(cast(AST.Expression)e); }
+ void visit(AST.GenericExp e) { visit(cast(AST.Expression)e); }
+
+ // Miscellaneous
+ void visit(AST.VarExp e) { visit(cast(AST.SymbolExp)e); }
+ void visit(AST.DollarExp e) { visit(cast(AST.IdentifierExp)e); }
+ void visit(AST.SuperExp e) { visit(cast(AST.ThisExp)e); }
+
+ // UnaExp
+ void visit(AST.AddrExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.PreExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.PtrExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.NegExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.UAddExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.NotExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.ComExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.DeleteExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.CastExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.CallExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.DotIdExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.AssertExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.ImportExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.DotTemplateInstanceExp e) { visit(cast(AST.UnaExp)e); }
+ void visit(AST.ArrayExp e) { visit(cast(AST.UnaExp)e); }
+
+ // DefaultInitExp
+ void visit(AST.FuncInitExp e) { visit(cast(AST.DefaultInitExp)e); }
+ void visit(AST.PrettyFuncInitExp e) { visit(cast(AST.DefaultInitExp)e); }
+ void visit(AST.FileInitExp e) { visit(cast(AST.DefaultInitExp)e); }
+ void visit(AST.LineInitExp e) { visit(cast(AST.DefaultInitExp)e); }
+ void visit(AST.ModuleInitExp e) { visit(cast(AST.DefaultInitExp)e); }
+
+ // BinExp
+ void visit(AST.CommaExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.PostExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.PowExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.MulExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.DivExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.ModExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.AddExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.MinExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.CatExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.ShlExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.ShrExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.UshrExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.EqualExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.InExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.IdentityExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.CmpExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.AndExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.XorExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.OrExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.LogicalExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.CondExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.AssignExp e) { visit(cast(AST.BinExp)e); }
+ void visit(AST.BinAssignExp e) { visit(cast(AST.BinExp)e); }
+
+ // BinAssignExp
+ void visit(AST.AddAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.MinAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.MulAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.DivAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.ModAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.PowAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.AndAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.OrAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.XorAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.ShlAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.ShrAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.UshrAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+ void visit(AST.CatAssignExp e) { visit(cast(AST.BinAssignExp)e); }
+
+ //===============================================================================
+ // TemplateParameter
+ void visit(AST.TemplateAliasParameter tp) { visit(cast(AST.TemplateParameter)tp); }
+ void visit(AST.TemplateTypeParameter tp) { visit(cast(AST.TemplateParameter)tp); }
+ void visit(AST.TemplateTupleParameter tp) { visit(cast(AST.TemplateParameter)tp); }
+ void visit(AST.TemplateValueParameter tp) { visit(cast(AST.TemplateParameter)tp); }
+
+ void visit(AST.TemplateThisParameter tp) { visit(cast(AST.TemplateTypeParameter)tp); }
+
+ //===============================================================================
+ // Condition
+ void visit(AST.StaticIfCondition c) { visit(cast(AST.Condition)c); }
+ void visit(AST.DVCondition c) { visit(cast(AST.Condition)c); }
+ void visit(AST.DebugCondition c) { visit(cast(AST.DVCondition)c); }
+ void visit(AST.VersionCondition c) { visit(cast(AST.DVCondition)c); }
+
+ //===============================================================================
+ // Initializer
+ void visit(AST.ExpInitializer i) { visit(cast(AST.Initializer)i); }
+ void visit(AST.StructInitializer i) { visit(cast(AST.Initializer)i); }
+ void visit(AST.ArrayInitializer i) { visit(cast(AST.Initializer)i); }
+ void visit(AST.VoidInitializer i) { visit(cast(AST.Initializer)i); }
+ void visit(AST.CInitializer i) { visit(cast(AST.CInitializer)i); }
+}
diff --git a/gcc/d/dmd/permissivevisitor.d b/gcc/d/dmd/permissivevisitor.d
new file mode 100644
index 00000000000..5d7f3fcba2c
--- /dev/null
+++ b/gcc/d/dmd/permissivevisitor.d
@@ -0,0 +1,28 @@
+/**
+ * A visitor that facilitates the traversal of subsets of the AST.
+ *
+ * Documentation: https://dlang.org/phobos/dmd_permissivevisitor.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/permissivevisitor.d
+ */
+
+module dmd.permissivevisitor;
+
+import dmd.parsetimevisitor;
+
+/** PermissiveVisitor overrides all the visit methods in the parent class
+ * that assert(0) in order to facilitate the traversal of subsets of the AST.
+ * It does not implement any visiting logic.
+ */
+extern(C++) class PermissiveVisitor(AST): ParseTimeVisitor!AST
+{
+ alias visit = ParseTimeVisitor!AST.visit;
+
+ override void visit(AST.Dsymbol){}
+ override void visit(AST.Parameter){}
+ override void visit(AST.Statement){}
+ override void visit(AST.Type){}
+ override void visit(AST.Expression){}
+ override void visit(AST.TemplateParameter){}
+ override void visit(AST.Condition){}
+ override void visit(AST.Initializer){}
+}
diff --git a/gcc/d/dmd/printast.d b/gcc/d/dmd/printast.d
new file mode 100644
index 00000000000..3f12b173357
--- /dev/null
+++ b/gcc/d/dmd/printast.d
@@ -0,0 +1,173 @@
+/**
+ * Provides an AST printer for debugging.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/printast.d, _printast.d)
+ * Documentation: https://dlang.org/phobos/dmd_printast.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/printast.d
+ */
+
+module dmd.printast;
+
+import core.stdc.stdio;
+
+import dmd.expression;
+import dmd.tokens;
+import dmd.visitor;
+
+/********************
+ * Print AST data structure in a nice format.
+ * Params:
+ * e = expression AST to print
+ * indent = indentation level
+ */
+void printAST(Expression e, int indent = 0)
+{
+ scope PrintASTVisitor pav = new PrintASTVisitor(indent);
+ e.accept(pav);
+}
+
+private:
+
+extern (C++) final class PrintASTVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+
+ int indent;
+
+ extern (D) this(int indent)
+ {
+ this.indent = indent;
+ }
+
+ override void visit(Expression e)
+ {
+ printIndent(indent);
+ printf("%s %s\n", Token.toChars(e.op), e.type ? e.type.toChars() : "");
+ }
+
+ override void visit(IntegerExp e)
+ {
+ printIndent(indent);
+ printf("Integer %lld %s\n", e.toInteger(), e.type ? e.type.toChars() : "");
+ }
+
+ override void visit(RealExp e)
+ {
+ printIndent(indent);
+
+ import dmd.hdrgen : floatToBuffer;
+ import dmd.root.outbuffer : OutBuffer;
+ OutBuffer buf;
+ floatToBuffer(e.type, e.value, &buf, false);
+ printf("Real %s %s\n", buf.peekChars(), e.type ? e.type.toChars() : "");
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ printIndent(indent);
+ printf("%s %s, %s\n", Token.toChars(e.op), e.type ? e.type.toChars() : "", e.toChars());
+ }
+
+ override void visit(SymbolExp e)
+ {
+ printIndent(indent);
+ printf("Symbol %s\n", e.type ? e.type.toChars() : "");
+ printIndent(indent + 2);
+ printf(".var: %s\n", e.var ? e.var.toChars() : "");
+ }
+
+ override void visit(DsymbolExp e)
+ {
+ visit(cast(Expression)e);
+ printIndent(indent + 2);
+ printf(".s: %s\n", e.s ? e.s.toChars() : "");
+ }
+
+ override void visit(DotIdExp e)
+ {
+ printIndent(indent);
+ printf("DotId %s\n", e.type ? e.type.toChars() : "");
+ printIndent(indent + 2);
+ printf(".ident: %s\n", e.ident.toChars());
+ printAST(e.e1, indent + 2);
+ }
+
+ override void visit(UnaExp e)
+ {
+ visit(cast(Expression)e);
+ printAST(e.e1, indent + 2);
+ }
+
+ override void visit(VectorExp e)
+ {
+ printIndent(indent);
+ printf("Vector %s\n", e.type ? e.type.toChars() : "");
+ printIndent(indent + 2);
+ printf(".to: %s\n", e.to.toChars());
+ printAST(e.e1, indent + 2);
+ }
+
+ override void visit(VectorArrayExp e)
+ {
+ printIndent(indent);
+ printf("VectorArray %s\n", e.type ? e.type.toChars() : "");
+ printAST(e.e1, indent + 2);
+ }
+
+ override void visit(BinExp e)
+ {
+ visit(cast(Expression)e);
+ printAST(e.e1, indent + 2);
+ printAST(e.e2, indent + 2);
+ }
+
+ override void visit(AssignExp e)
+ {
+ printIndent(indent);
+ printf("Assign %s\n", e.type ? e.type.toChars() : "");
+ printAST(e.e1, indent + 2);
+ printAST(e.e2, indent + 2);
+ }
+
+ override void visit(ConstructExp e)
+ {
+ printIndent(indent);
+ printf("Construct %s\n", e.type ? e.type.toChars() : "");
+ printAST(e.e1, indent + 2);
+ printAST(e.e2, indent + 2);
+ }
+
+ override void visit(BlitExp e)
+ {
+ printIndent(indent);
+ printf("Blit %s\n", e.type ? e.type.toChars() : "");
+ printAST(e.e1, indent + 2);
+ printAST(e.e2, indent + 2);
+ }
+
+ override void visit(IndexExp e)
+ {
+ printIndent(indent);
+ printf("Index %s\n", e.type ? e.type.toChars() : "");
+ printAST(e.e1, indent + 2);
+ printAST(e.e2, indent + 2);
+ }
+
+ override void visit(DelegateExp e)
+ {
+ visit(cast(Expression)e);
+ printIndent(indent + 2);
+ printf(".func: %s\n", e.func ? e.func.toChars() : "");
+ }
+
+ static void printIndent(int indent)
+ {
+ foreach (i; 0 .. indent)
+ putc(' ', stdout);
+ }
+}
+
+
diff --git a/gcc/d/dmd/readme.txt b/gcc/d/dmd/readme.txt
deleted file mode 100644
index a9a31af1b6e..00000000000
--- a/gcc/d/dmd/readme.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This is the source code to the DMD compiler
-for the D Programming Language defined in the documents at
-http://dlang.org/
-
-These sources are free, they are redistributable and modifiable
-under the terms of the Boost Software License, Version 1.0.
-The terms of this license are in the file boostlicense.txt,
-or see http://www.boost.org/LICENSE_1_0.txt.
-
-If a particular file has a different license in it, that overrides
-this license for that file.
-
--Walter Bright
diff --git a/gcc/d/dmd/res/default_ddoc_theme.ddoc b/gcc/d/dmd/res/default_ddoc_theme.ddoc
new file mode 100644
index 00000000000..7ae0db898ef
--- /dev/null
+++ b/gcc/d/dmd/res/default_ddoc_theme.ddoc
@@ -0,0 +1,825 @@
+LPAREN = (
+RPAREN = )
+BACKTICK = `
+DOLLAR = $
+COMMA = ,
+QUOTE = &quot;
+LF =
+$(LF)
+
+ESCAPES =
+ /</&lt;/
+ />/&gt;/
+ /&/&amp;/
+
+H1 = <h1>$0</h1>
+H2 = <h2>$0</h2>
+H3 = <h3>$0</h3>
+H4 = <h4>$0</h4>
+H5 = <h5>$0</h5>
+H6 = <h6>$0</h6>
+B = <b>$0</b>
+I = <i>$0</i>
+EM = <em>$0</em>
+STRONG = <strong>$0</strong>
+U = <u>$0</u>
+P = <p>$0</p>
+DL = <dl>$0</dl>
+DT = <dt>$0</dt>
+DD = <dd>$0</dd>
+TABLE = <table>$0</table>
+THEAD = <thead>$0</thead>
+TBODY = <tbody>$0</tbody>
+TR = <tr>$0</tr>
+TH = <th>$0</th>
+TD = <td>$0</td>
+TH_ALIGN = <th align="$1">$+</th>
+TD_ALIGN = <td align="$1">$+</td>
+OL = <ol>$0</ol>
+OL_START = <ol start="$1">$2</ol>
+UL = <ul>$0</ul>
+LI = <li>$0</li>
+BIG = <span class="font_big">$0</span>
+SMALL = <small>$0</small>
+BR = <br>
+HR = <hr />
+LINK = <a href="$0">$0</a>
+LINK2 = <a href="$1">$+</a>
+LINK_TITLE = <a href="$1" title="$2">$3</a>
+SYMBOL_LINK = <a href="$1">$(DDOC_PSYMBOL $+)</a>
+PHOBOS_PATH = https://dlang.org/phobos/
+DOC_ROOT_std = $(PHOBOS_PATH)
+DOC_ROOT_core = $(PHOBOS_PATH)
+DOC_ROOT_etc = $(PHOBOS_PATH)
+DOC_ROOT_object = $(PHOBOS_PATH)
+DOC_EXTENSION = .html
+IMAGE = <img src="$1" alt="$+" />
+IMAGE_TITLE = <img src="$1" alt="$3" title="$2" />
+BLOCKQUOTE = <blockquote>$0</blockquote>
+DEPRECATED = $0
+
+RED = <span class="color_red">$0</span>
+BLUE = <span class="color_blue">$0</span>
+GREEN = <span class="color_green">$0</span>
+YELLOW = <span class="color_yellow">$0</span>
+BLACK = <span class="color_black">$0</span>
+WHITE = <span class="color_white">$0</span>
+
+D_CODE =
+<section class="code_listing">
+ <div class="code_sample">
+ <div class="dlang">
+ <ol class="code_lines">
+ <li><code class="code">$0</code></li>
+ </ol>
+ </div>
+ </div>
+</section>
+
+OTHER_CODE =
+<section class="code_listing">
+ <div class="code_sample">
+ <div class="dlang">
+ <ol class="code_lines">
+ <li><code class="code language-$1">$+</code></li>
+ </ol>
+ </div>
+ </div>
+</section>
+
+D_INLINECODE = <code class="code">$0</code>
+DDOC_BACKQUOTED = $(D_INLINECODE $0)
+D_COMMENT = <span class="comment">$0</span>
+D_STRING = <span class="string_literal">$0</span>
+D_KEYWORD = <span class="keyword">$0</span>
+D_PSYMBOL = <span class="psymbol">$0</span>
+D_PARAM = <span class="param">$0</span>
+
+DDOC_BLANKLINE = <br><br>
+DDOC_COMMENT = <!-- $0 -->
+
+DDOC =
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>$(TITLE)</title>
+ <style type="text/css" media="screen">
+ html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p,
+ blockquote, pre, a, abbr, address, cite, code, del, dfn, em, figure,
+ img, ins, kbd, q, s, samp, small, strong, sub, sup, var, b, u, i, dl,
+ dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption,
+ tbody, tfoot, thead, tr, th, td {
+ background: transparent none repeat scroll 0 0;
+ border: 0 none;
+ font-size: 100%;
+ margin: 0;
+ outline: 0 none;
+ padding: 0;
+ vertical-align: baseline;
+ }
+
+ h1 { font-size: 200%; }
+ h2 { font-size: 160%; }
+ h3 { font-size: 120%; }
+ h4 { font-size: 100%; }
+ h5 { font-size: 80%; }
+ h6 { font-size: 80%; font-weight: normal; }
+
+ ul, ol {
+ margin: 1.4em 0;
+ }
+ ul ul, ol ol, ul ol, ol ul {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+ ul, ol {
+ margin-left: 2.8em;
+ }
+
+ ol {
+ list-style: decimal;
+ }
+ ol ol {
+ list-style: lower-alpha;
+ }
+ ol ol ol {
+ list-style: lower-roman;
+ }
+ ol ol ol ol {
+ list-style: decimal;
+ }
+
+ blockquote {
+ margin: 0.1em;
+ margin-left: 1em;
+ border-left: 2px solid #cccccc;
+ padding-left: 0.7em;
+ }
+
+ .color_red { color: #dc322f; }
+ .color_blue { color: #268bd2; }
+ .color_green { color: #859901; }
+ .color_yellow { color: #b58901; }
+ .color_black { color: black; }
+ .color_white { color: white; }
+
+ .font_big {
+ font-size: 1.2em;
+ }
+
+ .ddoc_section_h {
+ font-weight: bold;
+ font-size: 13px;
+ line-height: 19.5px;
+ margin-top: 11px;
+ display: block;
+ }
+
+ body.dlang .dlang {
+ display: inline-block;
+ }
+
+ body.dlang .declaration .dlang {
+ display: block;
+ }
+
+ body.dlang .ddoc_header_anchor a.dlang {
+ display: block;
+ color: rgba(0, 136, 204, 1);
+ text-decoration: none;
+ }
+
+ body.dlang .ddoc_header_anchor .code {
+ color: rgba(0, 136, 204, 1);
+ }
+
+ #ddoc_main .module {
+ border-color: currentColor rgba(233, 233, 233, 1) rgba(233, 233, 233, 1);
+ border-style: none solid solid;
+ border-width: 0 1px 1px;
+ overflow-x: hidden;
+ padding: 15px;
+ }
+
+ #ddoc_main .section .section {
+ margin-top: 0;
+ }
+
+ #ddoc_main .ddoc_module_members_section {
+ padding: 1px 0 0;
+ transition: transform 0.3s ease 0s;
+ }
+
+ #ddoc_main .ddoc_member, #ddoc_main .ddoc_module_members section.intro {
+ background: #fff none repeat scroll 0 0;
+ list-style-type: none;
+ width: 100%;
+ }
+
+ #ddoc_main .ddoc_header_anchor {
+ font-size: 1.4em;
+ transition: transform 0.3s ease 0s;
+ }
+
+ #ddoc_main .ddoc_header_anchor > .code {
+ display: inline-block;
+
+ }
+
+ #ddoc_main .ddoc_decl {
+ background-color: transparent;
+ height: 100%;
+ left: 0;
+ top: 0;
+ padding: 0;
+ padding-left: 15px;
+ }
+
+ #ddoc_main .ddoc_decl .section, #ddoc_main .section.ddoc_sections {
+ background: white none repeat scroll 0 0;
+ margin: 0;
+ padding: 5px;
+ position: relative;
+ border-radius: 5px;
+ }
+
+ #ddoc_main .ddoc_decl .section h4:first-of-type, #ddoc_main .section.ddoc_sections h4:first-of-type {
+ font-size: 13px;
+ line-height: 1.5;
+ margin-top: 21px;
+ }
+
+ #ddoc_main .section .declaration {
+ margin-top: 21px;
+ }
+
+ #ddoc_main .section .declaration .code {
+ color: rgba(0, 0, 0, 1);
+ margin-bottom: 15px;
+ padding-bottom: 6px;
+ }
+
+ #ddoc_main .declaration div .para {
+ margin-bottom: 0;
+ }
+
+ #ddoc_main .ddoc_params .graybox tr td:first-of-type {
+ padding: 7px;
+ text-align: right;
+ vertical-align: top;
+ word-break: normal;
+ white-space: nowrap;
+ }
+
+ #ddoc_main .ddoc_params .graybox {
+ border: 0 none;
+ }
+
+ #ddoc_main .ddoc_params .graybox td {
+ border-color: rgba(214, 214, 214, 1);
+ }
+
+ #ddoc_main .ddoc_params .graybox tr:first-child > td {
+ border-top: 0 none;
+ }
+
+ #ddoc_main .ddoc_params .graybox tr:last-child > td {
+ border-bottom: 0 none;
+ }
+
+ #ddoc_main .ddoc_params .graybox tr > td:first-child {
+ border-left: 0 none;
+ }
+
+ #ddoc_main .ddoc_params .graybox tr > td:last-child {
+ border-right: 0 none;
+ width: 100%;
+ }
+
+ #ddoc_main em.term, #ddoc_main em.term .code {
+ color: rgba(65, 65, 65, 1);
+ font-size: 12px;
+ font-style: italic;
+ line-height: 1.5;
+ }
+
+ #ddoc_main .see-also {
+ cursor: pointer;
+ font-family: Menlo,monospace;
+ }
+
+ #ddoc_main .ddoc_decl .section > div:last-of-type {
+ margin-bottom: 15px;
+ }
+
+ #ddoc_main .ddoc_member, #ddoc_main .ddoc_module_members {
+ transition: transform 0.3s ease 0s;
+ }
+
+ #ddoc_main .code_sample {
+ background: inherit;
+ }
+
+ #ddoc_main .declaration .code-line {
+ display: block;
+ font: 1em Menlo,monospace;
+ }
+
+ #ddoc_main a[name] {
+ margin: -112px 0 0;
+ padding-top: 112px;
+ }
+
+ #ddoc_main .ddoc_decl td {
+ max-width: inherit;
+ }
+
+ #ddoc_main .declaration a {
+ color: inherit;
+ }
+
+ #ddoc_main .declaration a:hover {
+ color: rgba(0, 136, 204, 1);
+ text-decoration: underline;
+ }
+
+ body.ddoc {
+ background-color: transparent;
+ color: rgba(0, 0, 0, 1);
+ font-family: Helvetica,Arial,sans-serif;
+ font-size: 62.5%;
+ margin: 0;
+ border: 0;
+ left: 0;
+ top: 0;
+ padding: 0;
+ }
+
+ .ddoc a[name] {
+ display: block;
+ height: 0;
+ margin: -85px 0 0;
+ padding-top: 85px;
+ width: 0;
+ }
+
+ .ddoc .module {
+ border-color: transparent;
+ background-color: rgba(255, 255, 255, 1);
+ border-color: currentColor rgba(233, 233, 233, 1) rgba(233, 233, 233, 1);
+ border-image: none;
+ border-style: none solid solid;
+ border-width: 0 1px 1px;
+ box-shadow: 0 0 1px rgba(0, 0, 0, 0.07);
+ display: block;
+ margin-left: 0;
+ min-height: calc(100% - 173px);
+ overflow: auto;
+ padding-bottom: 100px;
+ }
+
+ .ddoc .content_wrapper {
+ background-color: rgba(242, 242, 242, 1);
+ margin: 0 auto;
+ max-width: 980px;
+ }
+
+ .ddoc .section {
+ padding: 15px 25px 30px;
+ }
+
+ .ddoc .section .section {
+ margin: 30px 0 0;
+ padding: 0;
+ }
+
+ .ddoc .para {
+ color: rgba(65, 65, 65, 1);
+ font-size: 1.4em;
+ line-height: 145%;
+ margin-bottom: 15px;
+ }
+
+ .ddoc .ddoc_examples .para {
+ margin-bottom: 0;
+ }
+
+ .ddoc .module_name {
+ color: rgba(0, 0, 0, 1);
+ display: block;
+ font-family: Helvetica;
+ font-size: 2.8em;
+ font-weight: 100;
+ margin-bottom: 0;
+ padding: 15px 0;
+ }
+
+ .ddoc .module a {
+ color: rgba(0, 136, 204, 1);
+ text-decoration: none;
+ }
+
+ .ddoc .code {
+ color: rgba(128, 128, 128, 1);
+ font-family: Menlo,monospace;
+ font-size: 0.85em;
+ word-wrap: break-word;
+ }
+
+ .ddoc .code i {
+ font-style: normal;
+ }
+
+ .ddoc .code .code {
+ font-size: 1em;
+ }
+
+ .ddoc .code_sample {
+ background-clip: padding-box;
+ margin: 1px 0;
+ text-align: left;
+ }
+
+ .ddoc .code_sample {
+ display: block;
+ font-size: 1.4em;
+ margin-left: 21px;
+ }
+
+ .ddoc ol .code_sample {
+ font-size: 1em;
+ }
+
+ .ddoc .code_lines {
+ counter-reset: li;
+ line-height: 1.6em;
+ list-style: outside none none;
+ margin: 0;
+ padding: 0;
+ }
+
+ .ddoc .code_listing .code_sample div {
+ margin-left: 13px;
+ width: 93%;
+ }
+
+ .ddoc .code_listing .code_sample div .code_lines li {
+ list-style-type: none;
+ margin: 0;
+ padding-right: 10px;
+ }
+
+ .ddoc .code_sample div .code_lines li::before {
+ margin-left: -33px;
+ margin-right: 25px;
+ }
+
+ .ddoc .code_sample div .code_lines li:nth-child(n+10)::before {
+ margin-left: -39px;
+ margin-right: 25px;
+ }
+
+ .ddoc .code_sample div .code_lines li:nth-child(n+100)::before {
+ margin-left: -46px;
+ margin-right: 25px;
+ }
+
+ .ddoc .code_sample .code_lines .code {
+ color: #000;
+ }
+
+ .ddoc div.dlang {
+ margin: 10px 0 21px;
+ padding: 4px 0 2px 10px;
+ }
+
+ .ddoc div.dlang {
+ margin: 10px 0 21px;
+ padding: 4px 0 2px 10px;
+ }
+
+ .ddoc div.dlang {
+ border-left: 5px solid rgba(0, 155, 51, 0.2);
+ }
+
+ .ddoc .code_lines li::before {
+ color: rgba(128, 128, 128, 1);
+ content: counter(li, decimal);
+ counter-increment: li;
+ font-family: Menlo,monospace;
+ font-size: 0.9em;
+ margin-right: 16px;
+ }
+
+ .ddoc .code_lines li {
+ padding-left: 0;
+ white-space: pre-wrap;
+ }
+
+ .ddoc .code_lines li:only-of-type::before {
+ color: rgba(255, 255, 255, 1);
+ content: " ";
+ }
+
+ .ddoc .code_lines li:only-of-type {
+ color: rgba(255, 255, 255, 1);
+ content: " ";
+ }
+
+ .ddoc .code_lines li:nth-child(n+10) {
+ text-indent: -17px;
+ }
+
+ .ddoc .code_lines li:nth-child(n+10)::before {
+ margin-right: 12px;
+ }
+
+ .ddoc .graybox {
+ border: 1px solid rgba(233, 233, 233, 1);
+ border-collapse: collapse;
+ border-spacing: 0;
+ empty-cells: hide;
+ margin: 20px 0 36px;
+ text-align: left;
+ }
+
+ .ddoc .graybox p {
+ margin: 0;
+ min-width: 50px;
+ }
+
+ .ddoc th {
+ margin: 0;
+ max-width: 260px;
+ padding: 5px 10px 5px 10px;
+ vertical-align: bottom;
+ }
+
+ .ddoc td {
+ border: 1px solid rgba(233, 233, 233, 1);
+ margin: 0;
+ max-width: 260px;
+ padding: 5px 10px 5px 10px;
+ vertical-align: middle;
+ }
+
+ .punctuation {
+ color: rgba(0, 0, 0, 1);
+ }
+
+ .comment {
+ color: rgba(0, 131, 18, 1);
+ }
+
+ .operator {
+ color: #000;
+ }
+
+ .keyword {
+ color: rgba(170, 13, 145, 1);
+ }
+
+ .keyword_type {
+ color: rgba(170, 51, 145, 1);
+ }
+
+ .string_literal {
+ color: rgba(196, 26, 22, 1);
+ }
+
+ .ddoc_psuper_symbol {
+ color: rgba(92, 38, 153, 1);
+ }
+
+ .param {
+ color: rgba(0, 0, 0, 1);
+ }
+
+ .psymbol {
+ color: rgba(0, 0, 0, 1);
+ }
+
+ .ddoc_member_header .ddoc_header_anchor .code {
+ font-size: 1em;
+ }
+ </style>
+ </head>
+ <body id="ddoc_main" class="ddoc dlang">
+ <div class="content_wrapper">
+ <article class="module">
+ <h1 class="module_name">$(TITLE)</h1>
+ <section id="module_content">$(BODY)</section>
+ </article>
+ </div>
+ </body>
+</html>$(LF)
+
+DDOC_MODULE_MEMBERS = <section class="section ddoc_module_members_section">
+ <div class="ddoc_module_members">
+ $(DDOC_MEMBERS $0)
+ </div>
+</section>$(LF)
+
+DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)$(LF)
+DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)$(LF)
+DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)$(LF)
+DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)$(LF)
+
+DDOC_MEMBERS = <ul class="ddoc_members">
+ $0
+</ul>
+
+DDOC_MEMBER = <li class="ddoc_member">
+ $0
+</li>
+
+DDOC_MEMBER_HEADER = <div class="ddoc_member_header">
+ $0
+</div>
+
+DDOC_HEADER_ANCHOR = <div class="ddoc_header_anchor">
+ <a href="#$1" id="$1"><code class="code">$2</code></a>
+</div>
+
+DDOC_DECL = <div class="ddoc_decl">
+ <section class="section">
+ <div class="declaration">
+ <h4>Declaration</h4>
+ <div class="dlang">
+ <p class="para">
+ <code class="code">
+ $0
+ </code>
+ </p>
+ </div>
+ </div>
+ </section>
+</div>
+
+DDOC_ANCHOR = <span class="ddoc_anchor" id="$1"></span>
+
+DDOC_DECL_DD = <div class="ddoc_decl">
+ $0
+</div>
+
+DDOC_SECTIONS = <section class="section ddoc_sections">
+ $0
+</section>$(LF)
+
+DDOC_SUMMARY = <div class="ddoc_summary">
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_DESCRIPTION = <div class="ddoc_description">
+ <h4>Discussion</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_EXAMPLES = <div class="ddoc_examples">
+ <h4>Examples</h4>
+ <p class="para">
+ $0
+ </p>
+</div>
+
+DDOC_RETURNS = <div class="ddoc_returns">
+ <h4>Return Value</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_PARAMS = <div class="ddoc_params">
+ <h4>Parameters</h4>
+ <table cellspacing="0" cellpadding="5" border="0" class="graybox">
+ <tbody>
+ $0
+ </tbody>
+ </table>
+</div>$(LF)
+
+DDOC_PARAM_ROW = <tr class="ddoc_param_row">
+ $0
+</tr>$(LF)
+
+DDOC_PARAM_ID = <td scope="ddoc_param_id">
+ <code class="code">
+ <em class="term">$0</em>
+ </code>
+</td>$(LF)
+
+DDOC_PARAM_DESC = <td>
+ <div class="ddoc_param_desc">
+ <p class="para">
+ $0
+ </p>
+ </div>
+</td>
+
+DDOC_LICENSE = <div class="ddoc_license">
+ <h4>License</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_AUTHORS = <div class="ddoc_authors">
+ <h4>Authors</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_BUGS = <div class="ddoc_bugs">
+ <h4>Bugs</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_COPYRIGHT = <div class="ddoc_copyright">
+ <h4>Copyright</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_DATE = <div class="ddoc_date">
+ <h4>Date</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_DEPRECATED = <div class="ddoc_deprecated">
+ <h4>Deprecated</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_HISTORY = <div class="ddoc_history">
+ <h4>History</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_SEE_ALSO = <div class="ddoc_see_also">
+ <h4>See Also</h4>
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_STANDARDS = <div class="ddoc_standards">
+ <h4>Standards</h4>
+ <p class="para">
+ $0
+ </p>
+</div>
+
+DDOC_THROWS = <div class="ddoc_throws">
+ <h4>Throws</h4>
+ <p class="para">
+ $0
+ </p>
+</div>
+
+DDOC_VERSION = <div class="ddoc_version">
+ <h4>Version</h4>
+ <p class="para">
+ $0
+ </p>
+</div>
+
+DDOC_SECTION = <div class="ddoc_section">
+ <p class="para">
+ $0
+ </p>
+</div>$(LF)
+
+DDOC_SECTION_H = <span class="ddoc_section_h">$0:</span>$(LF)
+
+DDOC_DITTO = <br>
+$0
+
+DDOC_PSYMBOL = <code class="code">$0</code>
+DDOC_ENUM_BASETYPE = $0
+DDOC_PSUPER_SYMBOL = <span class="ddoc_psuper_symbol">$0</span>
+DDOC_KEYWORD = <code class="code">$0</code>
+DDOC_PARAM = <code class="code">$0</code>
+DDOC_CONSTRAINT = $(DDOC_CONSTRAINT) if ($0)
+DDOC_OVERLOAD_SEPARATOR = $0
+DDOC_TEMPLATE_PARAM_LIST = $0
+DDOC_TEMPLATE_PARAM = $0
+DDOC_LINK_AUTODETECT = $(LINK $0)
+DDOC_AUTO_PSYMBOL = $(DDOC_PSYMBOL $0)
+DDOC_AUTO_KEYWORD = $(DDOC_KEYWORD $0)
+DDOC_AUTO_PARAM = $(DDOC_PARAM $0)
+DDOC_AUTO_PSYMBOL_SUPPRESS = $0
diff --git a/gcc/d/dmd/root/README.md b/gcc/d/dmd/root/README.md
new file mode 100644
index 00000000000..539b940d6fb
--- /dev/null
+++ b/gcc/d/dmd/root/README.md
@@ -0,0 +1,23 @@
+# Table of contents
+
+| File | Purpose |
+|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
+| [aav.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/aav.d) | An associative array implementation |
+| [array.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/array.d) | A dynamic array implementation |
+| [bitarray.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/bitarray.d) | A compact array of bits |
+| [ctfloat.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/ctfloat.d) | A floating point type for compile-time calculations |
+| [file.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/file.d) | Read a file from disk and store it in memory |
+| [filename.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/filename.d) | Encapsulate path and file names |
+| [hash.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/hash.d) | Calculate a hash for a byte array |
+| [longdouble.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/longdouble.d) | 80-bit floating point number implementation in case they are not natively supported |
+| [man.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/man.d) | Opens an online manual page |
+| [outbuffer.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/outbuffer.d) | An expandable buffer in which you can write text or binary data. |
+| [port.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/port.d) | Portable routines for functions that have different implementations on different platforms |
+| [region.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/region.d) | A region allocator |
+| [response.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/response.d) | Parse command line arguments from response files |
+| [rmem.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/rmem.d) | Allocate memory using `malloc` or the GC depending on the configuration |
+| [rootobject.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/rootobject.d) | A root object that classes in dmd inherit from |
+| [speller.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/speller.d) | Try to detect typos in identifiers |
+| [string.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/string.d) | Various string related functions |
+| [stringtable.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/stringtable.d) | Specialized associative array with string keys stored in a variable length structure |
+| [strtold.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/strtold.d) | D implementation of the standard C function `strtold` (String to long double) |
diff --git a/gcc/d/dmd/root/aav.c b/gcc/d/dmd/root/aav.c
deleted file mode 100644
index 992a117da2a..00000000000
--- a/gcc/d/dmd/root/aav.c
+++ /dev/null
@@ -1,171 +0,0 @@
-
-/* Copyright (C) 2010-2021 by The D Language Foundation, All Rights Reserved
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- * https://github.com/D-Programming-Language/dmd/blob/master/src/root/aav.c
- */
-
-/**
- * Implementation of associative arrays.
- *
- */
-
-#include "dsystem.h"
-#include "aav.h"
-#include "rmem.h"
-
-
-inline size_t hash(size_t a)
-{
- a ^= (a >> 20) ^ (a >> 12);
- return a ^ (a >> 7) ^ (a >> 4);
-}
-
-struct aaA
-{
- aaA *next;
- Key key;
- Value value;
-};
-
-struct AA
-{
- aaA* *b;
- size_t b_length;
- size_t nodes; // total number of aaA nodes
- aaA* binit[4]; // initial value of b[]
-
- aaA aafirst; // a lot of these AA's have only one entry
-};
-
-/****************************************************
- * Determine number of entries in associative array.
- */
-
-size_t dmd_aaLen(AA* aa)
-{
- return aa ? aa->nodes : 0;
-}
-
-
-/*************************************************
- * Get pointer to value in associative array indexed by key.
- * Add entry for key if it is not already there, returning a pointer to a null Value.
- * Create the associative array if it does not already exist.
- */
-
-Value* dmd_aaGet(AA** paa, Key key)
-{
- //printf("paa = %p\n", paa);
-
- if (!*paa)
- { AA *a = (AA *)mem.xmalloc(sizeof(AA));
- a->b = (aaA**)a->binit;
- a->b_length = 4;
- a->nodes = 0;
- a->binit[0] = NULL;
- a->binit[1] = NULL;
- a->binit[2] = NULL;
- a->binit[3] = NULL;
- *paa = a;
- assert((*paa)->b_length == 4);
- }
- //printf("paa = %p, *paa = %p\n", paa, *paa);
-
- assert((*paa)->b_length);
- size_t i = hash((size_t)key) & ((*paa)->b_length - 1);
- aaA** pe = &(*paa)->b[i];
- aaA *e;
- while ((e = *pe) != NULL)
- {
- if (key == e->key)
- return &e->value;
- pe = &e->next;
- }
-
- // Not found, create new elem
- //printf("create new one\n");
-
- size_t nodes = ++(*paa)->nodes;
- e = (nodes != 1) ? (aaA *)mem.xmalloc(sizeof(aaA)) : &(*paa)->aafirst;
- //e = new aaA();
- e->next = NULL;
- e->key = key;
- e->value = NULL;
- *pe = e;
-
- //printf("length = %d, nodes = %d\n", (*paa)->b_length, nodes);
- if (nodes > (*paa)->b_length * 2)
- {
- //printf("rehash\n");
- dmd_aaRehash(paa);
- }
-
- return &e->value;
-}
-
-
-/*************************************************
- * Get value in associative array indexed by key.
- * Returns NULL if it is not already there.
- */
-
-Value dmd_aaGetRvalue(AA* aa, Key key)
-{
- //printf("_aaGetRvalue(key = %p)\n", key);
- if (aa)
- {
- size_t i;
- size_t len = aa->b_length;
- i = hash((size_t)key) & (len-1);
- aaA* e = aa->b[i];
- while (e)
- {
- if (key == e->key)
- return e->value;
- e = e->next;
- }
- }
- return NULL; // not found
-}
-
-
-/********************************************
- * Rehash an array.
- */
-
-void dmd_aaRehash(AA** paa)
-{
- //printf("Rehash\n");
- if (*paa)
- {
- AA *aa = *paa;
- if (aa)
- {
- size_t len = aa->b_length;
- if (len == 4)
- len = 32;
- else
- len *= 4;
- aaA** newb = (aaA**)mem.xmalloc(sizeof(aaA)*len);
- memset(newb, 0, len * sizeof(aaA*));
-
- for (size_t k = 0; k < aa->b_length; k++)
- { aaA *e = aa->b[k];
- while (e)
- { aaA* enext = e->next;
- size_t j = hash((size_t)e->key) & (len-1);
- e->next = newb[j];
- newb[j] = e;
- e = enext;
- }
- }
- if (aa->b != (aaA**)aa->binit)
- mem.xfree(aa->b);
-
- aa->b = newb;
- aa->b_length = len;
- }
- }
-}
diff --git a/gcc/d/dmd/root/aav.d b/gcc/d/dmd/root/aav.d
new file mode 100644
index 00000000000..92b58ba5cd8
--- /dev/null
+++ b/gcc/d/dmd/root/aav.d
@@ -0,0 +1,339 @@
+/**
+ * Associative array implementation.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/aav.d, root/_aav.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_aav.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/aav.d
+ */
+
+module dmd.root.aav;
+
+import core.stdc.string;
+import dmd.root.rmem;
+
+private size_t hash(size_t a) pure nothrow @nogc @safe
+{
+ a ^= (a >> 20) ^ (a >> 12);
+ return a ^ (a >> 7) ^ (a >> 4);
+}
+
+struct KeyValueTemplate(K,V)
+{
+ K key;
+ V value;
+}
+
+alias Key = void*;
+alias Value = void*;
+
+alias KeyValue = KeyValueTemplate!(Key, Value);
+
+struct aaA
+{
+ aaA* next;
+ KeyValue keyValue;
+ alias keyValue this;
+}
+
+struct AA
+{
+ aaA** b;
+ size_t b_length;
+ size_t nodes; // total number of aaA nodes
+ aaA*[4] binit; // initial value of b[]
+ aaA aafirst; // a lot of these AA's have only one entry
+}
+
+/****************************************************
+ * Determine number of entries in associative array.
+ */
+private size_t dmd_aaLen(const AA* aa) pure nothrow @nogc @safe
+{
+ return aa ? aa.nodes : 0;
+}
+
+/*************************************************
+ * Get pointer to value in associative array indexed by key.
+ * Add entry for key if it is not already there, returning a pointer to a null Value.
+ * Create the associative array if it does not already exist.
+ */
+private Value* dmd_aaGet(AA** paa, Key key) pure nothrow
+{
+ //printf("paa = %p\n", paa);
+ if (!*paa)
+ {
+ AA* a = cast(AA*)mem.xmalloc(AA.sizeof);
+ a.b = cast(aaA**)a.binit;
+ a.b_length = 4;
+ a.nodes = 0;
+ a.binit[0] = null;
+ a.binit[1] = null;
+ a.binit[2] = null;
+ a.binit[3] = null;
+ *paa = a;
+ assert((*paa).b_length == 4);
+ }
+ //printf("paa = %p, *paa = %p\n", paa, *paa);
+ assert((*paa).b_length);
+ size_t i = hash(cast(size_t)key) & ((*paa).b_length - 1);
+ aaA** pe = &(*paa).b[i];
+ aaA* e;
+ while ((e = *pe) !is null)
+ {
+ if (key == e.key)
+ return &e.value;
+ pe = &e.next;
+ }
+ // Not found, create new elem
+ //printf("create new one\n");
+ size_t nodes = ++(*paa).nodes;
+ e = (nodes != 1) ? cast(aaA*)mem.xmalloc(aaA.sizeof) : &(*paa).aafirst;
+ //e = new aaA();
+ e.next = null;
+ e.key = key;
+ e.value = null;
+ *pe = e;
+ //printf("length = %d, nodes = %d\n", (*paa)->b_length, nodes);
+ if (nodes > (*paa).b_length * 2)
+ {
+ //printf("rehash\n");
+ dmd_aaRehash(paa);
+ }
+ return &e.value;
+}
+
+/*************************************************
+ * Get value in associative array indexed by key.
+ * Returns NULL if it is not already there.
+ */
+private Value dmd_aaGetRvalue(AA* aa, Key key) pure nothrow @nogc
+{
+ //printf("_aaGetRvalue(key = %p)\n", key);
+ if (aa)
+ {
+ size_t i;
+ size_t len = aa.b_length;
+ i = hash(cast(size_t)key) & (len - 1);
+ aaA* e = aa.b[i];
+ while (e)
+ {
+ if (key == e.key)
+ return e.value;
+ e = e.next;
+ }
+ }
+ return null; // not found
+}
+
+/**
+Gets a range of key/values for `aa`.
+
+Returns: a range of key/values for `aa`.
+*/
+@property auto asRange(AA* aa) pure nothrow @nogc
+{
+ return AARange!(Key, Value)(aa);
+}
+
+private struct AARange(K,V)
+{
+ AA* aa;
+ // current index into bucket array `aa.b`
+ size_t bIndex;
+ aaA* current;
+
+ this(AA* aa) pure nothrow @nogc
+ {
+ if (aa)
+ {
+ this.aa = aa;
+ toNext();
+ }
+ }
+
+ @property bool empty() const pure nothrow @nogc @safe
+ {
+ return current is null;
+ }
+
+ @property auto front() const pure nothrow @nogc
+ {
+ return cast(KeyValueTemplate!(K,V))current.keyValue;
+ }
+
+ void popFront() pure nothrow @nogc
+ {
+ if (current.next)
+ current = current.next;
+ else
+ {
+ bIndex++;
+ toNext();
+ }
+ }
+
+ private void toNext() pure nothrow @nogc
+ {
+ for (; bIndex < aa.b_length; bIndex++)
+ {
+ if (auto next = aa.b[bIndex])
+ {
+ current = next;
+ return;
+ }
+ }
+ current = null;
+ }
+}
+
+unittest
+{
+ AA* aa = null;
+ foreach(keyValue; aa.asRange)
+ assert(0);
+
+ enum totalKeyLength = 50;
+ foreach (i; 1 .. totalKeyLength + 1)
+ {
+ auto key = cast(void*)i;
+ {
+ auto valuePtr = dmd_aaGet(&aa, key);
+ assert(valuePtr);
+ *valuePtr = key;
+ }
+ bool[totalKeyLength] found;
+ size_t rangeCount = 0;
+ foreach (keyValue; aa.asRange)
+ {
+ assert(keyValue.key <= key);
+ assert(keyValue.key == keyValue.value);
+ rangeCount++;
+ assert(!found[cast(size_t)keyValue.key - 1]);
+ found[cast(size_t)keyValue.key - 1] = true;
+ }
+ assert(rangeCount == i);
+ }
+}
+
+/********************************************
+ * Rehash an array.
+ */
+private void dmd_aaRehash(AA** paa) pure nothrow
+{
+ //printf("Rehash\n");
+ if (*paa)
+ {
+ AA* aa = *paa;
+ if (aa)
+ {
+ size_t len = aa.b_length;
+ if (len == 4)
+ len = 32;
+ else
+ len *= 4;
+ aaA** newb = cast(aaA**)mem.xmalloc(aaA.sizeof * len);
+ memset(newb, 0, len * (aaA*).sizeof);
+ for (size_t k = 0; k < aa.b_length; k++)
+ {
+ aaA* e = aa.b[k];
+ while (e)
+ {
+ aaA* enext = e.next;
+ size_t j = hash(cast(size_t)e.key) & (len - 1);
+ e.next = newb[j];
+ newb[j] = e;
+ e = enext;
+ }
+ }
+ if (aa.b != cast(aaA**)aa.binit)
+ mem.xfree(aa.b);
+ aa.b = newb;
+ aa.b_length = len;
+ }
+ }
+}
+
+unittest
+{
+ AA* aa = null;
+ Value v = dmd_aaGetRvalue(aa, null);
+ assert(!v);
+ Value* pv = dmd_aaGet(&aa, null);
+ assert(pv);
+ *pv = cast(void*)3;
+ v = dmd_aaGetRvalue(aa, null);
+ assert(v == cast(void*)3);
+}
+
+struct AssocArray(K,V)
+{
+ private AA* aa;
+
+ /**
+ Returns: The number of key/value pairs.
+ */
+ @property size_t length() const pure nothrow @nogc @safe
+ {
+ return dmd_aaLen(aa);
+ }
+
+ /**
+ Lookup value associated with `key` and return the address to it. If the `key`
+ has not been added, it adds it and returns the address to the new value.
+
+ Params:
+ key = key to lookup the value for
+
+ Returns: the address to the value associated with `key`. If `key` does not exist, it
+ is added and the address to the new value is returned.
+ */
+ V* getLvalue(const(K) key) pure nothrow
+ {
+ return cast(V*)dmd_aaGet(&aa, cast(void*)key);
+ }
+
+ /**
+ Lookup and return the value associated with `key`, if the `key` has not been
+ added, it returns null.
+
+ Params:
+ key = key to lookup the value for
+
+ Returns: the value associated with `key` if present, otherwise, null.
+ */
+ V opIndex(const(K) key) pure nothrow @nogc
+ {
+ return cast(V)dmd_aaGetRvalue(aa, cast(void*)key);
+ }
+
+ /**
+ Gets a range of key/values for `aa`.
+
+ Returns: a range of key/values for `aa`.
+ */
+ @property auto asRange() pure nothrow @nogc
+ {
+ return AARange!(K,V)(aa);
+ }
+}
+
+///
+unittest
+{
+ auto foo = new Object();
+ auto bar = new Object();
+
+ AssocArray!(Object, Object) aa;
+
+ assert(aa[foo] is null);
+ assert(aa.length == 0);
+
+ auto fooValuePtr = aa.getLvalue(foo);
+ *fooValuePtr = bar;
+
+ assert(aa[foo] is bar);
+ assert(aa.length == 1);
+}
diff --git a/gcc/d/dmd/root/array.d b/gcc/d/dmd/root/array.d
new file mode 100644
index 00000000000..ed925c8f6e3
--- /dev/null
+++ b/gcc/d/dmd/root/array.d
@@ -0,0 +1,1121 @@
+
+/**
+ * Dynamic array implementation.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/array.d, root/_array.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_array.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/array.d
+ */
+
+module dmd.root.array;
+
+import core.stdc.stdlib : _compare_fp_t;
+import core.stdc.string;
+
+import dmd.root.rmem;
+import dmd.root.string;
+
+// `qsort` is only `nothrow` since 2.081.0
+private extern(C) void qsort(scope void* base, size_t nmemb, size_t size, _compare_fp_t compar) nothrow @nogc;
+
+
+debug
+{
+ debug = stomp; // flush out dangling pointer problems by stomping on unused memory
+}
+
+extern (C++) struct Array(T)
+{
+ size_t length;
+
+private:
+ T[] data;
+ enum SMALLARRAYCAP = 1;
+ T[SMALLARRAYCAP] smallarray; // inline storage for small arrays
+
+public:
+ /*******************
+ * Params:
+ * dim = initial length of array
+ */
+ this(size_t dim) pure nothrow
+ {
+ reserve(dim);
+ this.length = dim;
+ }
+
+ @disable this(this);
+
+ ~this() pure nothrow
+ {
+ debug (stomp) memset(data.ptr, 0xFF, data.length);
+ if (data.ptr != &smallarray[0])
+ mem.xfree(data.ptr);
+ }
+ ///returns elements comma separated in []
+ extern(D) const(char)[] toString() const
+ {
+ static const(char)[] toStringImpl(alias toStringFunc, Array)(Array* a, bool quoted = false)
+ {
+ const(char)[][] buf = (cast(const(char)[]*)mem.xcalloc((char[]).sizeof, a.length))[0 .. a.length];
+ size_t len = 2; // [ and ]
+ const seplen = quoted ? 3 : 1; // ',' or null terminator and optionally '"'
+ if (a.length == 0)
+ len += 1; // null terminator
+ else
+ {
+ foreach (u; 0 .. a.length)
+ {
+ buf[u] = toStringFunc(a.data[u]);
+ len += buf[u].length + seplen;
+ }
+ }
+ char[] str = (cast(char*)mem.xmalloc_noscan(len))[0..len];
+
+ str[0] = '[';
+ char* p = str.ptr + 1;
+ foreach (u; 0 .. a.length)
+ {
+ if (u)
+ *p++ = ',';
+ if (quoted)
+ *p++ = '"';
+ memcpy(p, buf[u].ptr, buf[u].length);
+ p += buf[u].length;
+ if (quoted)
+ *p++ = '"';
+ }
+ *p++ = ']';
+ *p = 0;
+ assert(p - str.ptr == str.length - 1); // null terminator
+ mem.xfree(buf.ptr);
+ return str[0 .. $-1];
+ }
+
+ static if (is(typeof(T.init.toString())))
+ {
+ return toStringImpl!(a => a.toString)(&this);
+ }
+ else static if (is(typeof(T.init.toDString())))
+ {
+ return toStringImpl!(a => a.toDString)(&this, true);
+ }
+ else
+ {
+ assert(0);
+ }
+ }
+ ///ditto
+ const(char)* toChars() const
+ {
+ return toString.ptr;
+ }
+
+ ref Array push(T ptr) return pure nothrow
+ {
+ reserve(1);
+ data[length++] = ptr;
+ return this;
+ }
+
+ extern (D) ref Array pushSlice(T[] a) return pure nothrow
+ {
+ const oldLength = length;
+ setDim(oldLength + a.length);
+ memcpy(data.ptr + oldLength, a.ptr, a.length * T.sizeof);
+ return this;
+ }
+
+ ref Array append(typeof(this)* a) return pure nothrow
+ {
+ insert(length, a);
+ return this;
+ }
+
+ void reserve(size_t nentries) pure nothrow
+ {
+ //printf("Array::reserve: length = %d, data.length = %d, nentries = %d\n", (int)length, (int)data.length, (int)nentries);
+
+ // Cold path
+ void enlarge(size_t nentries)
+ {
+ pragma(inline, false); // never inline cold path
+ if (data.length == 0)
+ {
+ // Not properly initialized, someone memset it to zero
+ if (nentries <= SMALLARRAYCAP)
+ {
+ data = SMALLARRAYCAP ? smallarray[] : null;
+ }
+ else
+ {
+ auto p = cast(T*)mem.xmalloc(nentries * T.sizeof);
+ data = p[0 .. nentries];
+ }
+ }
+ else if (data.length == SMALLARRAYCAP)
+ {
+ const allocdim = length + nentries;
+ auto p = cast(T*)mem.xmalloc(allocdim * T.sizeof);
+ memcpy(p, smallarray.ptr, length * T.sizeof);
+ data = p[0 .. allocdim];
+ }
+ else
+ {
+ /* Increase size by 1.5x to avoid excessive memory fragmentation
+ */
+ auto increment = length / 2;
+ if (nentries > increment) // if 1.5 is not enough
+ increment = nentries;
+ const allocdim = length + increment;
+ debug (stomp)
+ {
+ // always move using allocate-copy-stomp-free
+ auto p = cast(T*)mem.xmalloc(allocdim * T.sizeof);
+ memcpy(p, data.ptr, length * T.sizeof);
+ memset(data.ptr, 0xFF, data.length * T.sizeof);
+ mem.xfree(data.ptr);
+ }
+ else
+ auto p = cast(T*)mem.xrealloc(data.ptr, allocdim * T.sizeof);
+ data = p[0 .. allocdim];
+ }
+
+ debug (stomp)
+ {
+ if (length < data.length)
+ memset(data.ptr + length, 0xFF, (data.length - length) * T.sizeof);
+ }
+ else
+ {
+ if (mem.isGCEnabled)
+ if (length < data.length)
+ memset(data.ptr + length, 0xFF, (data.length - length) * T.sizeof);
+ }
+ }
+
+ if (data.length - length < nentries) // false means hot path
+ enlarge(nentries);
+ }
+
+ void remove(size_t i) pure nothrow @nogc
+ {
+ if (length - i - 1)
+ memmove(data.ptr + i, data.ptr + i + 1, (length - i - 1) * T.sizeof);
+ length--;
+ debug (stomp) memset(data.ptr + length, 0xFF, T.sizeof);
+ }
+
+ void insert(size_t index, typeof(this)* a) pure nothrow
+ {
+ if (a)
+ {
+ size_t d = a.length;
+ reserve(d);
+ if (length != index)
+ memmove(data.ptr + index + d, data.ptr + index, (length - index) * T.sizeof);
+ memcpy(data.ptr + index, a.data.ptr, d * T.sizeof);
+ length += d;
+ }
+ }
+
+ void insert(size_t index, T ptr) pure nothrow
+ {
+ reserve(1);
+ memmove(data.ptr + index + 1, data.ptr + index, (length - index) * T.sizeof);
+ data[index] = ptr;
+ length++;
+ }
+
+ void setDim(size_t newdim) pure nothrow
+ {
+ if (length < newdim)
+ {
+ reserve(newdim - length);
+ }
+ length = newdim;
+ }
+
+ size_t find(T ptr) const nothrow pure
+ {
+ foreach (i; 0 .. length)
+ if (data[i] is ptr)
+ return i;
+ return size_t.max;
+ }
+
+ bool contains(T ptr) const nothrow pure
+ {
+ return find(ptr) != size_t.max;
+ }
+
+ ref inout(T) opIndex(size_t i) inout nothrow pure
+ {
+ debug
+ // This is called so often the array bounds become expensive
+ return data[i];
+ else
+ return data.ptr[i];
+ }
+
+ inout(T)* tdata() inout pure nothrow @nogc @trusted
+ {
+ return data.ptr;
+ }
+
+ Array!T* copy() const pure nothrow
+ {
+ auto a = new Array!T();
+ a.setDim(length);
+ memcpy(a.data.ptr, data.ptr, length * T.sizeof);
+ return a;
+ }
+
+ void shift(T ptr) pure nothrow
+ {
+ reserve(1);
+ memmove(data.ptr + 1, data.ptr, length * T.sizeof);
+ data[0] = ptr;
+ length++;
+ }
+
+ void zero() nothrow pure @nogc
+ {
+ data[0 .. length] = T.init;
+ }
+
+ T pop() nothrow pure @nogc
+ {
+ debug (stomp)
+ {
+ assert(length);
+ auto result = data[length - 1];
+ remove(length - 1);
+ return result;
+ }
+ else
+ return data[--length];
+ }
+
+ extern (D) inout(T)[] opSlice() inout nothrow pure @nogc
+ {
+ return data[0 .. length];
+ }
+
+ extern (D) inout(T)[] opSlice(size_t a, size_t b) inout nothrow pure @nogc
+ {
+ assert(a <= b && b <= length);
+ return data[a .. b];
+ }
+
+ /**
+ * Sort the elements of an array
+ *
+ * This function relies on `qsort`.
+ *
+ * Params:
+ * pred = Predicate to use. Should be a function of type
+ * `int function(scope const T* e1, scope const T* e2) nothrow`.
+ * The return value of this function should follow the
+ * usual C rule: `e1 >= e2 ? (e1 > e2) : -1`.
+ * The function can have D linkage.
+ *
+ * Returns:
+ * A reference to this, for easy chaining.
+ */
+ extern(D) ref typeof(this) sort (alias pred) () nothrow
+ {
+ if (this.length < 2)
+ return this;
+ qsort(this.data.ptr, this.length, T.sizeof, &arraySortWrapper!(T, pred));
+ return this;
+ }
+
+ /// Ditto, but use `opCmp` by default
+ extern(D) ref typeof(this) sort () () nothrow
+ if (is(typeof(this.data[0].opCmp(this.data[1])) : int))
+ {
+ return this.sort!(function (scope const(T)* pe1, scope const(T)* pe2) => pe1.opCmp(*pe2));
+ }
+
+ alias opDollar = length;
+ alias dim = length;
+}
+
+unittest
+{
+ // Test for objects implementing toString()
+ static struct S
+ {
+ int s = -1;
+ string toString() const
+ {
+ return "S";
+ }
+ }
+ auto array = Array!S(4);
+ assert(array.toString() == "[S,S,S,S]");
+ array.setDim(0);
+ assert(array.toString() == "[]");
+
+ // Test for toDString()
+ auto strarray = Array!(const(char)*)(2);
+ strarray[0] = "hello";
+ strarray[1] = "world";
+ auto str = strarray.toString();
+ assert(str == `["hello","world"]`);
+ // Test presence of null terminator.
+ assert(str.ptr[str.length] == '\0');
+}
+
+unittest
+{
+ auto array = Array!double(4);
+ array.shift(10);
+ array.push(20);
+ array[2] = 15;
+ assert(array[0] == 10);
+ assert(array.find(10) == 0);
+ assert(array.find(20) == 5);
+ assert(!array.contains(99));
+ array.remove(1);
+ assert(array.length == 5);
+ assert(array[1] == 15);
+ assert(array.pop() == 20);
+ assert(array.length == 4);
+ array.insert(1, 30);
+ assert(array[1] == 30);
+ assert(array[2] == 15);
+}
+
+unittest
+{
+ auto arrayA = Array!int(0);
+ int[3] buf = [10, 15, 20];
+ arrayA.pushSlice(buf);
+ assert(arrayA[] == buf[]);
+ auto arrayPtr = arrayA.copy();
+ assert(arrayPtr);
+ assert((*arrayPtr)[] == arrayA[]);
+ assert(arrayPtr.tdata != arrayA.tdata);
+
+ arrayPtr.setDim(0);
+ int[2] buf2 = [100, 200];
+ arrayPtr.pushSlice(buf2);
+
+ arrayA.append(arrayPtr);
+ assert(arrayA[3..$] == buf2[]);
+ arrayA.insert(0, arrayPtr);
+ assert(arrayA[] == [100, 200, 10, 15, 20, 100, 200]);
+
+ arrayA.zero();
+ foreach(e; arrayA)
+ assert(e == 0);
+}
+
+/**
+ * Exposes the given root Array as a standard D array.
+ * Params:
+ * array = the array to expose.
+ * Returns:
+ * The given array exposed to a standard D array.
+ */
+@property inout(T)[] peekSlice(T)(inout(Array!T)* array) pure nothrow @nogc
+{
+ return array ? (*array)[] : null;
+}
+
+/**
+ * Splits the array at $(D index) and expands it to make room for $(D length)
+ * elements by shifting everything past $(D index) to the right.
+ * Params:
+ * array = the array to split.
+ * index = the index to split the array from.
+ * length = the number of elements to make room for starting at $(D index).
+ */
+void split(T)(ref Array!T array, size_t index, size_t length) pure nothrow
+{
+ if (length > 0)
+ {
+ auto previousDim = array.length;
+ array.setDim(array.length + length);
+ for (size_t i = previousDim; i > index;)
+ {
+ i--;
+ array[i + length] = array[i];
+ }
+ }
+}
+unittest
+{
+ auto array = Array!int();
+ array.split(0, 0);
+ assert([] == array[]);
+ array.push(1).push(3);
+ array.split(1, 1);
+ array[1] = 2;
+ assert([1, 2, 3] == array[]);
+ array.split(2, 3);
+ array[2] = 8;
+ array[3] = 20;
+ array[4] = 4;
+ assert([1, 2, 8, 20, 4, 3] == array[]);
+ array.split(0, 0);
+ assert([1, 2, 8, 20, 4, 3] == array[]);
+ array.split(0, 1);
+ array[0] = 123;
+ assert([123, 1, 2, 8, 20, 4, 3] == array[]);
+ array.split(0, 3);
+ array[0] = 123;
+ array[1] = 421;
+ array[2] = 910;
+ assert([123, 421, 910, 123, 1, 2, 8, 20, 4, 3] == (&array).peekSlice());
+}
+
+/**
+ * Reverse an array in-place.
+ * Params:
+ * a = array
+ * Returns:
+ * reversed a[]
+ */
+T[] reverse(T)(T[] a) pure nothrow @nogc @safe
+{
+ if (a.length > 1)
+ {
+ const mid = (a.length + 1) >> 1;
+ foreach (i; 0 .. mid)
+ {
+ T e = a[i];
+ a[i] = a[$ - 1 - i];
+ a[$ - 1 - i] = e;
+ }
+ }
+ return a;
+}
+
+unittest
+{
+ int[] a1 = [];
+ assert(reverse(a1) == []);
+ int[] a2 = [2];
+ assert(reverse(a2) == [2]);
+ int[] a3 = [2,3];
+ assert(reverse(a3) == [3,2]);
+ int[] a4 = [2,3,4];
+ assert(reverse(a4) == [4,3,2]);
+ int[] a5 = [2,3,4,5];
+ assert(reverse(a5) == [5,4,3,2]);
+}
+
+unittest
+{
+ //test toString/toChars. Identifier is a simple object that has a usable .toString
+ import dmd.identifier : Identifier;
+ import core.stdc.string : strcmp;
+
+ auto array = Array!Identifier();
+ array.push(new Identifier("id1"));
+ array.push(new Identifier("id2"));
+
+ string expected = "[id1,id2]";
+ assert(array.toString == expected);
+ assert(strcmp(array.toChars, expected.ptr) == 0);
+}
+
+/// Predicate to wrap a D function passed to `qsort`
+private template arraySortWrapper(T, alias fn)
+{
+ pragma(mangle, "arraySortWrapper_" ~ T.mangleof ~ "_" ~ fn.mangleof)
+ extern(C) int arraySortWrapper(scope const void* e1, scope const void* e2) nothrow
+ {
+ return fn(cast(const(T*))e1, cast(const(T*))e2);
+ }
+}
+
+// Test sorting
+unittest
+{
+ Array!(const(char)*) strings;
+ strings.push("World");
+ strings.push("Foo");
+ strings.push("baguette");
+ strings.push("Avocado");
+ strings.push("Hello");
+ // Newer frontend versions will work with (e1, e2) and infer the type
+ strings.sort!(function (scope const char** e1, scope const char** e2) => strcmp(*e1, *e2));
+ assert(strings[0] == "Avocado");
+ assert(strings[1] == "Foo");
+ assert(strings[2] == "Hello");
+ assert(strings[3] == "World");
+ assert(strings[4] == "baguette");
+
+ /// opCmp automatically supported
+ static struct MyStruct
+ {
+ int a;
+
+ int opCmp(const ref MyStruct other) const nothrow
+ {
+ // Reverse order
+ return other.a - this.a;
+ }
+ }
+
+ Array!MyStruct arr1;
+ arr1.push(MyStruct(2));
+ arr1.push(MyStruct(4));
+ arr1.push(MyStruct(256));
+ arr1.push(MyStruct(42));
+ arr1.sort();
+ assert(arr1[0].a == 256);
+ assert(arr1[1].a == 42);
+ assert(arr1[2].a == 4);
+ assert(arr1[3].a == 2);
+
+ /// But only if user defined
+ static struct OtherStruct
+ {
+ int a;
+
+ static int pred (scope const OtherStruct* pe1, scope const OtherStruct* pe2)
+ nothrow @nogc pure @safe
+ {
+ return pe1.a - pe2.a;
+ }
+ }
+
+ static assert (!is(typeof(Array!(OtherStruct).init.sort())));
+ static assert (!is(typeof(Array!(OtherStruct).init.sort!pred)));
+}
+
+/**
+ * Iterates the given array and calls the given callable for each element.
+ *
+ * Use this instead of `foreach` when the array may expand during iteration.
+ *
+ * Params:
+ * callable = the callable to call for each element
+ * array = the array to iterate
+ *
+ * See_Also: $(REF foreachDsymbol, dmd, dsymbol)
+ */
+template each(alias callable, T)
+if (is(ReturnType!(typeof((T t) => callable(t))) == void))
+{
+ void each(ref Array!T array)
+ {
+ // Do not use foreach, as the size of the array may expand during iteration
+ for (size_t i = 0; i < array.length; ++i)
+ callable(array[i]);
+ }
+
+ void each(Array!T* array)
+ {
+ if (array)
+ each!callable(*array);
+ }
+}
+
+///
+@("iterate over an Array") unittest
+{
+ static immutable expected = [2, 3, 4, 5];
+
+ Array!int array;
+
+ foreach (e ; expected)
+ array.push(e);
+
+ int[] result;
+ array.each!((e) {
+ result ~= e;
+ });
+
+ assert(result == expected);
+}
+
+@("iterate over a pointer to an Array") unittest
+{
+ static immutable expected = [2, 3, 4, 5];
+
+ auto array = new Array!int;
+
+ foreach (e ; expected)
+ array.push(e);
+
+ int[] result;
+ array.each!((e) {
+ result ~= e;
+ });
+
+ assert(result == expected);
+}
+
+@("iterate while appending to the array being iterated") unittest
+{
+ static immutable expected = [2, 3, 4, 5];
+
+ Array!int array;
+
+ foreach (e ; expected[0 .. $ - 1])
+ array.push(e);
+
+ int[] result;
+
+ array.each!((e) {
+ if (e == 2)
+ array.push(5);
+
+ result ~= e;
+ });
+
+ assert(array[] == expected);
+ assert(result == expected);
+}
+
+/**
+ * Iterates the given array and calls the given callable for each element.
+ *
+ * If `callable` returns `!= 0`, it will stop the iteration and return that
+ * value, otherwise it will return 0.
+ *
+ * Use this instead of `foreach` when the array may expand during iteration.
+ *
+ * Params:
+ * callable = the callable to call for each element
+ * array = the array to iterate
+ *
+ * Returns: the last value returned by `callable`
+ * See_Also: $(REF foreachDsymbol, dmd, dsymbol)
+ */
+template each(alias callable, T)
+if (is(ReturnType!(typeof((T t) => callable(t))) == int))
+{
+ int each(ref Array!T array)
+ {
+ // Do not use foreach, as the size of the array may expand during iteration
+ for (size_t i = 0; i < array.length; ++i)
+ {
+ if (const result = callable(array[i]))
+ return result;
+ }
+
+ return 0;
+ }
+
+ int each(Array!T* array)
+ {
+ return array ? each!callable(*array) : 0;
+ }
+}
+
+///
+@("iterate over an Array and stop the iteration") unittest
+{
+ Array!int array;
+
+ foreach (e ; [2, 3, 4, 5])
+ array.push(e);
+
+ int[] result;
+ const returnValue = array.each!((e) {
+ result ~= e;
+
+ if (e == 3)
+ return 8;
+
+ return 0;
+ });
+
+ assert(result == [2, 3]);
+ assert(returnValue == 8);
+}
+
+@("iterate over an Array") unittest
+{
+ static immutable expected = [2, 3, 4, 5];
+
+ Array!int array;
+
+ foreach (e ; expected)
+ array.push(e);
+
+ int[] result;
+ const returnValue = array.each!((e) {
+ result ~= e;
+ return 0;
+ });
+
+ assert(result == expected);
+ assert(returnValue == 0);
+}
+
+@("iterate over a pointer to an Array and stop the iteration") unittest
+{
+ auto array = new Array!int;
+
+ foreach (e ; [2, 3, 4, 5])
+ array.push(e);
+
+ int[] result;
+ const returnValue = array.each!((e) {
+ result ~= e;
+
+ if (e == 3)
+ return 9;
+
+ return 0;
+ });
+
+ assert(result == [2, 3]);
+ assert(returnValue == 9);
+}
+
+@("iterate while appending to the array being iterated and stop the iteration") unittest
+{
+ Array!int array;
+
+ foreach (e ; [2, 3])
+ array.push(e);
+
+ int[] result;
+
+ const returnValue = array.each!((e) {
+ if (e == 2)
+ array.push(1);
+
+ result ~= e;
+
+ if (e == 1)
+ return 7;
+
+ return 0;
+ });
+
+ static immutable expected = [2, 3, 1];
+
+ assert(array[] == expected);
+ assert(result == expected);
+ assert(returnValue == 7);
+}
+
+/// Returns: A static array constructed from `array`.
+pragma(inline, true) T[n] staticArray(T, size_t n)(auto ref T[n] array)
+{
+ return array;
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ enum a = [0, 1].staticArray;
+ static assert(is(typeof(a) == int[2]));
+ static assert(a == [0, 1]);
+}
+
+/// Returns: `true` if the two given ranges are equal
+bool equal(Range1, Range2)(Range1 range1, Range2 range2)
+{
+ template isArray(T)
+ {
+ static if (is(T U : U[]))
+ enum isArray = true;
+
+ else
+ enum isArray = false;
+ }
+
+ static if (isArray!Range1 && isArray!Range2 && is(typeof(range1 == range2)))
+ return range1 == range2;
+
+ else
+ {
+ static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length)))
+ {
+ if (range1.length != range2.length)
+ return false;
+ }
+
+ for (; !range1.empty; range1.popFront(), range2.popFront())
+ {
+ if (range2.empty)
+ return false;
+
+ if (range1.front != range2.front)
+ return false;
+ }
+
+ return range2.empty;
+ }
+}
+
+///
+pure nothrow @nogc @safe unittest
+{
+ enum a = [ 1, 2, 4, 3 ].staticArray;
+ static assert(!equal(a[], a[1..$]));
+ static assert(equal(a[], a[]));
+
+ // different types
+ enum b = [ 1.0, 2, 4, 3].staticArray;
+ static assert(!equal(a[], b[1..$]));
+ static assert(equal(a[], b[]));
+}
+
+pure nothrow @safe unittest
+{
+ static assert(equal([1, 2, 3].map!(x => x * 2), [1, 2, 3].map!(x => x * 2)));
+
+ static assert(!equal([1, 2].map!(x => x * 2), [1, 2, 3].map!(x => x * 2)));
+}
+
+/**
+ * Lazily filters the given range based on the given predicate.
+ *
+ * Returns: a range containing only elements for which the predicate returns
+ * `true`
+ */
+auto filter(alias predicate, Range)(Range range)
+if (isInputRange!(Unqual!Range) && isPredicateOf!(predicate, ElementType!Range))
+{
+ return Filter!(predicate, Range)(range);
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ enum a = [1, 2, 3, 4].staticArray;
+ enum result = a[].filter!(e => e > 2);
+
+ enum expected = [3, 4].staticArray;
+ static assert(result.equal(expected[]));
+}
+
+private struct Filter(alias predicate, Range)
+{
+ private Range range;
+ private bool primed;
+
+ private void prime()
+ {
+ if (primed)
+ return;
+
+ while (!range.empty && !predicate(range.front))
+ range.popFront();
+
+ primed = true;
+ }
+
+ @property bool empty()
+ {
+ prime();
+ return range.empty;
+ }
+
+ @property auto front()
+ {
+ assert(!range.empty);
+ prime();
+ return range.front;
+ }
+
+ void popFront()
+ {
+ assert(!range.empty);
+ prime();
+
+ do
+ {
+ range.popFront();
+ } while (!range.empty && !predicate(range.front));
+ }
+
+ auto opSlice()
+ {
+ return this;
+ }
+}
+
+/**
+ * Lazily iterates the given range and calls the given callable for each element.
+ *
+ * Returns: a range containing the result of each call to `callable`
+ */
+auto map(alias callable, Range)(Range range)
+if (isInputRange!(Unqual!Range) && isCallableWith!(callable, ElementType!Range))
+{
+ return Map!(callable, Range)(range);
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ enum a = [1, 2, 3, 4].staticArray;
+ enum expected = [2, 4, 6, 8].staticArray;
+
+ enum result = a[].map!(e => e * 2);
+ static assert(result.equal(expected[]));
+}
+
+private struct Map(alias callable, Range)
+{
+ private Range range;
+
+ @property bool empty()
+ {
+ return range.empty;
+ }
+
+ @property auto front()
+ {
+ assert(!range.empty);
+ return callable(range.front);
+ }
+
+ void popFront()
+ {
+ assert(!range.empty);
+ range.popFront();
+ }
+
+ static if (hasLength!Range)
+ {
+ @property auto length()
+ {
+ return range.length;
+ }
+
+ alias opDollar = length;
+ }
+}
+
+/// Returns: the length of the given range.
+auto walkLength(Range)(Range range)
+if (isInputRange!Range )
+{
+ static if (hasLength!Range)
+ return range.length;
+ else
+ {
+ size_t result;
+ for (; !range.empty; range.popFront())
+ ++result;
+ return result;
+ }
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ enum a = [1, 2, 3, 4].staticArray;
+ static assert(a[].walkLength == 4);
+
+ enum c = a[].filter!(e => e > 2);
+ static assert(c.walkLength == 2);
+}
+
+/// Evaluates to the element type of `R`.
+template ElementType(R)
+{
+ static if (is(typeof(R.init.front.init) T))
+ alias ElementType = T;
+ else
+ alias ElementType = void;
+}
+
+/// Evaluates to `true` if the given type satisfy the input range interface.
+enum isInputRange(R) =
+ is(typeof(R.init) == R)
+ && is(ReturnType!(typeof((R r) => r.empty)) == bool)
+ && is(typeof((return ref R r) => r.front))
+ && !is(ReturnType!(typeof((R r) => r.front)) == void)
+ && is(typeof((R r) => r.popFront));
+
+/// Evaluates to `true` if `func` can be called with a value of `T` and returns
+/// a value that is convertible to `bool`.
+enum isPredicateOf(alias func, T) = is(typeof((T t) => !func(t)));
+
+/// Evaluates to `true` if `func` be called withl a value of `T`.
+enum isCallableWith(alias func, T) =
+ __traits(compiles, { auto _ = (T t) => func(t); });
+
+private:
+
+template ReturnType(T)
+{
+ static if (is(T R == return))
+ alias ReturnType = R;
+ else
+ static assert(false, "argument is not a function");
+}
+
+alias Unqual(T) = ReturnType!(typeof((T t) => cast() t));
+
+template hasLength(Range)
+{
+ static if (is(typeof(((Range* r) => r.length)(null)) Length))
+ enum hasLength = is(Length == size_t);
+ else
+ enum hasLength = false;
+}
+
+/// Implements the range interface primitive `front` for built-in arrays.
+@property ref inout(T) front(T)(return scope inout(T)[] a) pure nothrow @nogc @safe
+{
+ assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof);
+ return a[0];
+}
+
+///
+pure nothrow @nogc @safe unittest
+{
+ enum a = [1, 2, 3].staticArray;
+ static assert(a[].front == 1);
+}
+
+/// Implements the range interface primitive `empty` for types that obey $(LREF hasLength) property
+@property bool empty(T)(auto ref scope T a)
+if (is(typeof(a.length) : size_t))
+{
+ return !a.length;
+}
+
+///
+pure nothrow @nogc @safe unittest
+{
+ enum a = [1, 2, 3].staticArray;
+
+ static assert(!a.empty);
+ static assert(a[3 .. $].empty);
+}
+
+pure nothrow @safe unittest
+{
+ int[string] b;
+ assert(b.empty);
+ b["zero"] = 0;
+ assert(!b.empty);
+}
+
+/// Implements the range interface primitive `popFront` for built-in arrays.
+void popFront(T)(/*scope*/ ref inout(T)[] array) pure nothrow @nogc @safe
+{ // does not compile with GDC 9 if this is `scope`
+ assert(array.length, "Attempting to popFront() past the end of an array of " ~ T.stringof);
+ array = array[1 .. $];
+}
+
+///
+pure nothrow @nogc @safe unittest
+{
+ auto a = [1, 2, 3].staticArray;
+ auto b = a[];
+ auto expected = [2, 3].staticArray;
+
+ b.popFront();
+ assert(b == expected[]);
+}
diff --git a/gcc/d/dmd/root/array.h b/gcc/d/dmd/root/array.h
index f7cb0c75184..f573dcaa3ee 100644
--- a/gcc/d/dmd/root/array.h
+++ b/gcc/d/dmd/root/array.h
@@ -9,14 +9,13 @@
#pragma once
#include "dsystem.h"
-#include "dcompat.h"
#include "object.h"
#include "rmem.h"
template <typename TYPE>
struct Array
{
- size_t length;
+ d_size_t length;
private:
DArray<TYPE> data;
@@ -42,8 +41,8 @@ struct Array
char *toChars() const
{
const char **buf = (const char **)mem.xmalloc(length * sizeof(const char *));
- size_t len = 2;
- for (size_t u = 0; u < length; u++)
+ d_size_t len = 2;
+ for (d_size_t u = 0; u < length; u++)
{
buf[u] = ((RootObject *)data.ptr[u])->toChars();
len += strlen(buf[u]) + 1;
@@ -52,7 +51,7 @@ struct Array
str[0] = '[';
char *p = str + 1;
- for (size_t u = 0; u < length; u++)
+ for (d_size_t u = 0; u < length; u++)
{
if (u)
*p++ = ',';
@@ -77,7 +76,7 @@ struct Array
insert(length, a);
}
- void reserve(size_t nentries)
+ void reserve(d_size_t nentries)
{
//printf("Array::reserve: length = %d, data.length = %d, nentries = %d\n", (int)length, (int)data.length, (int)nentries);
if (data.length - length < nentries)
@@ -106,7 +105,7 @@ struct Array
{
/* Increase size by 1.5x to avoid excessive memory fragmentation
*/
- size_t increment = length / 2;
+ d_size_t increment = length / 2;
if (nentries > increment) // if 1.5 is not enough
increment = nentries;
data.length = length + increment;
@@ -115,18 +114,18 @@ struct Array
}
}
- void remove(size_t i)
+ void remove(d_size_t i)
{
if (length - i - 1)
memmove(data.ptr + i, data.ptr + i + 1, (length - i - 1) * sizeof(TYPE));
length--;
}
- void insert(size_t index, Array *a)
+ void insert(d_size_t index, Array *a)
{
if (a)
{
- size_t d = a->length;
+ d_size_t d = a->length;
reserve(d);
if (length != index)
memmove(data.ptr + index + d, data.ptr + index, (length - index) * sizeof(TYPE));
@@ -135,7 +134,7 @@ struct Array
}
}
- void insert(size_t index, TYPE ptr)
+ void insert(d_size_t index, TYPE ptr)
{
reserve(1);
memmove(data.ptr + index + 1, data.ptr + index, (length - index) * sizeof(TYPE));
@@ -143,7 +142,7 @@ struct Array
length++;
}
- void setDim(size_t newdim)
+ void setDim(d_size_t newdim)
{
if (length < newdim)
{
@@ -152,9 +151,9 @@ struct Array
length = newdim;
}
- size_t find(TYPE ptr) const
+ d_size_t find(TYPE ptr) const
{
- for (size_t i = 0; i < length; i++)
+ for (d_size_t i = 0; i < length; i++)
{
if (data.ptr[i] == ptr)
return i;
@@ -167,7 +166,7 @@ struct Array
return find(ptr) != SIZE_MAX;
}
- TYPE& operator[] (size_t index)
+ TYPE& operator[] (d_size_t index)
{
#ifdef DEBUG
assert(index < length);
@@ -205,28 +204,5 @@ struct Array
{
return data.ptr[--length];
}
-
- void sort()
- {
- struct ArraySort
- {
- static int
- #if _WIN32
- __cdecl
- #endif
- Array_sort_compare(const void *x, const void *y)
- {
- RootObject *ox = *(RootObject **)const_cast<void *>(x);
- RootObject *oy = *(RootObject **)const_cast<void *>(y);
-
- return ox->compare(oy);
- }
- };
-
- if (length)
- {
- qsort(data.ptr, length, sizeof(RootObject *), &ArraySort::Array_sort_compare);
- }
- }
};
diff --git a/gcc/d/dmd/root/bitarray.d b/gcc/d/dmd/root/bitarray.d
new file mode 100644
index 00000000000..f9129611dd4
--- /dev/null
+++ b/gcc/d/dmd/root/bitarray.d
@@ -0,0 +1,192 @@
+/**
+ * Implementation of a bit array.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/bitarray.d, root/_bitarray.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_array.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/bitarray.d
+ */
+
+module dmd.root.bitarray;
+
+import core.stdc.stdio;
+import core.stdc.string;
+
+import dmd.root.rmem;
+
+struct BitArray
+{
+
+ alias Chunk_t = size_t;
+ enum ChunkSize = Chunk_t.sizeof;
+ enum BitsPerChunk = ChunkSize * 8;
+
+ size_t length() const @nogc nothrow pure @safe
+ {
+ return len;
+ }
+
+ void length(size_t nlen) nothrow pure
+ {
+ immutable ochunks = chunks(len);
+ immutable nchunks = chunks(nlen);
+ if (ochunks != nchunks)
+ {
+ ptr = cast(size_t*)mem.xrealloc_noscan(ptr, nchunks * ChunkSize);
+ }
+ if (nchunks > ochunks)
+ ptr[ochunks .. nchunks] = 0;
+ if (nlen & (BitsPerChunk - 1))
+ ptr[nchunks - 1] &= (cast(Chunk_t)1 << (nlen & (BitsPerChunk - 1))) - 1;
+ len = nlen;
+ }
+
+ void opAssign(const ref BitArray b) nothrow pure
+ {
+ if (!len)
+ length(b.len);
+ assert(len == b.len);
+ memcpy(ptr, b.ptr, bytes(len));
+ }
+
+ bool opIndex(size_t idx) const @nogc nothrow pure
+ {
+ import core.bitop : bt;
+
+ assert(idx < len);
+ return !!bt(ptr, idx);
+ }
+
+ void opIndexAssign(bool val, size_t idx) @nogc nothrow pure
+ {
+ import core.bitop : btc, bts;
+
+ assert(idx < len);
+ if (val)
+ bts(ptr, idx);
+ else
+ btc(ptr, idx);
+ }
+
+ bool opEquals(const ref BitArray b) const @nogc nothrow pure
+ {
+ return len == b.len && memcmp(ptr, b.ptr, bytes(len)) == 0;
+ }
+
+ void zero() @nogc nothrow pure
+ {
+ memset(ptr, 0, bytes(len));
+ }
+
+ /******
+ * Returns:
+ * true if no bits are set
+ */
+ bool isZero() @nogc nothrow pure
+ {
+ const nchunks = chunks(len);
+ foreach (i; 0 .. nchunks)
+ {
+ if (ptr[i])
+ return false;
+ }
+ return true;
+ }
+
+ void or(const ref BitArray b) @nogc nothrow pure
+ {
+ assert(len == b.len);
+ const nchunks = chunks(len);
+ foreach (i; 0 .. nchunks)
+ ptr[i] |= b.ptr[i];
+ }
+
+ /* Swap contents of `this` with `b`
+ */
+ void swap(ref BitArray b) @nogc nothrow pure
+ {
+ assert(len == b.len);
+ const nchunks = chunks(len);
+ foreach (i; 0 .. nchunks)
+ {
+ const chunk = ptr[i];
+ ptr[i] = b.ptr[i];
+ b.ptr[i] = chunk;
+ }
+ }
+
+ ~this() nothrow pure
+ {
+ debug
+ {
+ // Stomp the allocated memory
+ const nchunks = chunks(len);
+ foreach (i; 0 .. nchunks)
+ {
+ ptr[i] = cast(Chunk_t)0xFEFEFEFE_FEFEFEFE;
+ }
+ }
+ mem.xfree(ptr);
+ debug
+ {
+ // Set to implausible values
+ len = cast(size_t)0xFEFEFEFE_FEFEFEFE;
+ ptr = cast(size_t*)cast(size_t)0xFEFEFEFE_FEFEFEFE;
+ }
+ }
+
+private:
+ size_t len; // length in bits
+ size_t *ptr;
+
+ /// Returns: The amount of chunks used to store len bits
+ static size_t chunks(const size_t len) @nogc nothrow pure @safe
+ {
+ return (len + BitsPerChunk - 1) / BitsPerChunk;
+ }
+
+ /// Returns: The amount of bytes used to store len bits
+ static size_t bytes(const size_t len) @nogc nothrow pure @safe
+ {
+ return chunks(len) * ChunkSize;
+ }
+}
+
+nothrow pure unittest
+{
+ BitArray array;
+ array.length = 20;
+ assert(array[19] == 0);
+ array[10] = 1;
+ assert(array[10] == 1);
+ array[10] = 0;
+ assert(array[10] == 0);
+ assert(array.length == 20);
+
+ BitArray a,b;
+ assert(a != array);
+ a.length = 200;
+ assert(a != array);
+ assert(a.isZero());
+ a[100] = true;
+ b.length = 200;
+ b[100] = true;
+ assert(a == b);
+
+ a.length = 300;
+ b.length = 300;
+ assert(a == b);
+ b[299] = true;
+ assert(a != b);
+ assert(!a.isZero());
+ a.swap(b);
+ assert(a[299] == true);
+ assert(b[299] == false);
+ a = b;
+ assert(a == b);
+}
+
+
+
diff --git a/gcc/d/dmd/root/bitarray.h b/gcc/d/dmd/root/bitarray.h
index 004c43caa54..e773711e7f5 100644
--- a/gcc/d/dmd/root/bitarray.h
+++ b/gcc/d/dmd/root/bitarray.h
@@ -24,8 +24,8 @@ struct BitArray
mem.xfree(ptr);
}
- size_t len;
- size_t *ptr;
+ d_size_t len;
+ d_size_t *ptr;
private:
BitArray(const BitArray&);
diff --git a/gcc/d/dmd/root/checkedint.c b/gcc/d/dmd/root/checkedint.c
deleted file mode 100644
index af7b56f145c..00000000000
--- a/gcc/d/dmd/root/checkedint.c
+++ /dev/null
@@ -1,238 +0,0 @@
-
-/**********************************************
- * This module implements integral arithmetic primitives that check
- * for out-of-range results.
- * This is a translation to C++ of D's core.checkedint
- *
- * Integral arithmetic operators operate on fixed width types.
- * Results that are not representable in those fixed widths are silently
- * truncated to fit.
- * This module offers integral arithmetic primitives that produce the
- * same results, but set an 'overflow' flag when such truncation occurs.
- * The setting is sticky, meaning that numerous operations can be cascaded
- * and then the flag need only be checked at the end.
- * Whether the operation is signed or unsigned is indicated by an 's' or 'u'
- * suffix, respectively. While this could be achieved without such suffixes by
- * using overloading on the signedness of the types, the suffix makes it clear
- * which is happening without needing to examine the types.
- *
- * While the generic versions of these functions are computationally expensive
- * relative to the cost of the operation itself, compiler implementations are free
- * to recognize them and generate equivalent and faster code.
- *
- * References: $(LINK2 http://blog.regehr.org/archives/1139, Fast Integer Overflow Checks)
- * Copyright: Copyright (C) 2014-2021 by The D Language Foundation, All Rights Reserved
- * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
- * Authors: Walter Bright
- * Source: https://github.com/D-Programming-Language/dmd/blob/master/src/root/checkedint.c
- */
-
-#include "dsystem.h"
-#include "checkedint.h"
-
-
-/*******************************
- * Add two signed integers, checking for overflow.
- *
- * The overflow is sticky, meaning a sequence of operations can
- * be done and overflow need only be checked at the end.
- * Params:
- * x = left operand
- * y = right operand
- * overflow = set if an overflow occurs, is not affected otherwise
- * Returns:
- * the sum
- */
-
-int adds(int x, int y, bool& overflow)
-{
- int64_t r = (int64_t)x + (int64_t)y;
- if (r < INT32_MIN || r > INT32_MAX)
- overflow = true;
- return (int)r;
-}
-
-/// ditto
-int64_t adds(int64_t x, int64_t y, bool& overflow)
-{
- int64_t r = (uint64_t)x + (uint64_t)y;
- if ((x < 0 && y < 0 && r >= 0) ||
- (x >= 0 && y >= 0 && r < 0))
- overflow = true;
- return r;
-}
-
-/*******************************
- * Add two unsigned integers, checking for overflow (aka carry).
- *
- * The overflow is sticky, meaning a sequence of operations can
- * be done and overflow need only be checked at the end.
- * Params:
- * x = left operand
- * y = right operand
- * overflow = set if an overflow occurs, is not affected otherwise
- * Returns:
- * the sum
- */
-
-unsigned addu(unsigned x, unsigned y, bool& overflow)
-{
- unsigned r = x + y;
- if (r < x || r < y)
- overflow = true;
- return r;
-}
-
-/// ditto
-uint64_t addu(uint64_t x, uint64_t y, bool& overflow)
-{
- uint64_t r = x + y;
- if (r < x || r < y)
- overflow = true;
- return r;
-}
-
-/*******************************
- * Subtract two signed integers, checking for overflow.
- *
- * The overflow is sticky, meaning a sequence of operations can
- * be done and overflow need only be checked at the end.
- * Params:
- * x = left operand
- * y = right operand
- * overflow = set if an overflow occurs, is not affected otherwise
- * Returns:
- * the sum
- */
-
-int subs(int x, int y, bool& overflow)
-{
- int64_t r = (int64_t)x - (int64_t)y;
- if (r < INT32_MIN || r > INT32_MAX)
- overflow = true;
- return (int)r;
-}
-
-/// ditto
-int64_t subs(int64_t x, int64_t y, bool& overflow)
-{
- int64_t r = (uint64_t)x - (uint64_t)y;
- if ((x < 0 && y >= 0 && r >= 0) ||
- (x >= 0 && y < 0 && (r < 0 || y == INT64_MIN)))
- overflow = true;
- return r;
-}
-
-/*******************************
- * Subtract two unsigned integers, checking for overflow (aka borrow).
- *
- * The overflow is sticky, meaning a sequence of operations can
- * be done and overflow need only be checked at the end.
- * Params:
- * x = left operand
- * y = right operand
- * overflow = set if an overflow occurs, is not affected otherwise
- * Returns:
- * the sum
- */
-
-unsigned subu(unsigned x, unsigned y, bool& overflow)
-{
- if (x < y)
- overflow = true;
- return x - y;
-}
-
-/// ditto
-uint64_t subu(uint64_t x, uint64_t y, bool& overflow)
-{
- if (x < y)
- overflow = true;
- return x - y;
-}
-
-/***********************************************
- * Negate an integer.
- *
- * Params:
- * x = operand
- * overflow = set if x cannot be negated, is not affected otherwise
- * Returns:
- * the negation of x
- */
-
-int negs(int x, bool& overflow)
-{
- if (x == (int)INT32_MIN)
- overflow = true;
- return -x;
-}
-
-/// ditto
-int64_t negs(int64_t x, bool& overflow)
-{
- if (x == INT64_MIN)
- overflow = true;
- return -x;
-}
-
-/*******************************
- * Multiply two signed integers, checking for overflow.
- *
- * The overflow is sticky, meaning a sequence of operations can
- * be done and overflow need only be checked at the end.
- * Params:
- * x = left operand
- * y = right operand
- * overflow = set if an overflow occurs, is not affected otherwise
- * Returns:
- * the sum
- */
-
-int muls(int x, int y, bool& overflow)
-{
- int64_t r = (int64_t)x * (int64_t)y;
- if (r < INT32_MIN || r > INT32_MAX)
- overflow = true;
- return (int)r;
-}
-
-/// ditto
-int64_t muls(int64_t x, int64_t y, bool& overflow)
-{
- int64_t r = (uint64_t)x * (uint64_t)y;
- int64_t not0or1 = ~(int64_t)1;
- if ((x & not0or1) && ((r == y) ? r : (r / x) != y))
- overflow = true;
- return r;
-}
-
-/*******************************
- * Multiply two unsigned integers, checking for overflow (aka carry).
- *
- * The overflow is sticky, meaning a sequence of operations can
- * be done and overflow need only be checked at the end.
- * Params:
- * x = left operand
- * y = right operand
- * overflow = set if an overflow occurs, is not affected otherwise
- * Returns:
- * the sum
- */
-
-unsigned mulu(unsigned x, unsigned y, bool& overflow)
-{
- uint64_t r = (uint64_t)x * (uint64_t)y;
- if (r > UINT32_MAX)
- overflow = true;
- return (unsigned)r;
-}
-
-/// ditto
-uint64_t mulu(uint64_t x, uint64_t y, bool& overflow)
-{
- uint64_t r = x * y;
- if (x && (r / x) != y)
- overflow = true;
- return r;
-}
diff --git a/gcc/d/dmd/root/ctfloat.d b/gcc/d/dmd/root/ctfloat.d
new file mode 100644
index 00000000000..9b98742440d
--- /dev/null
+++ b/gcc/d/dmd/root/ctfloat.d
@@ -0,0 +1,63 @@
+/**
+ * Collects functions for compile-time floating-point calculations.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/ctfloat.d, root/_ctfloat.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_ctfloat.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/ctfloat.d
+ */
+
+module dmd.root.ctfloat;
+
+nothrow:
+
+// Type used by the front-end for compile-time reals
+public import dmd.root.longdouble : real_t = longdouble;
+
+// Compile-time floating-point helper
+extern (C++) struct CTFloat
+{
+ nothrow:
+ @nogc:
+ @safe:
+
+ version (GNU)
+ enum yl2x_supported = false;
+ else
+ enum yl2x_supported = __traits(compiles, core.math.yl2x(1.0L, 2.0L));
+ enum yl2xp1_supported = yl2x_supported;
+
+ pure static real_t fabs(real_t x);
+ pure static real_t ldexp(real_t n, int exp);
+
+ pure @trusted
+ static bool isIdentical(real_t a, real_t b);
+
+ pure @trusted
+ static size_t hash(real_t a);
+
+ pure
+ static bool isNaN(real_t r);
+
+ pure @trusted
+ static bool isSNaN(real_t r);
+
+ static bool isInfinity(real_t r) pure;
+
+ @system
+ static real_t parse(const(char)* literal, bool* isOutOfRange = null);
+
+ @system
+ static int sprint(char* str, char fmt, real_t x);
+
+ // Constant real values 0, 1, -1 and 0.5.
+ __gshared real_t zero;
+ __gshared real_t one;
+ __gshared real_t minusone;
+ __gshared real_t half;
+
+ @trusted
+ static void initialize();
+}
diff --git a/gcc/d/dmd/root/ctfloat.h b/gcc/d/dmd/root/ctfloat.h
index 0a829f3e051..1221b822188 100644
--- a/gcc/d/dmd/root/ctfloat.h
+++ b/gcc/d/dmd/root/ctfloat.h
@@ -1,5 +1,6 @@
/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
@@ -16,9 +17,6 @@ typedef longdouble real_t;
// Compile-time floating-point helper
struct CTFloat
{
- static bool yl2x_supported;
- static bool yl2xp1_supported;
-
static void yl2x(const real_t *x, const real_t *y, real_t *res);
static void yl2xp1(const real_t *x, const real_t *y, real_t *res);
@@ -62,4 +60,6 @@ struct CTFloat
static real_t one;
static real_t minusone;
static real_t half;
+
+ static void initialize();
};
diff --git a/gcc/d/dmd/root/dcompat.h b/gcc/d/dmd/root/dcompat.h
index 9fd176ebcb1..88f20952cc9 100644
--- a/gcc/d/dmd/root/dcompat.h
+++ b/gcc/d/dmd/root/dcompat.h
@@ -34,3 +34,15 @@ struct DString : public DArray<const char>
DString(size_t length, const char *ptr)
: DArray<const char>(length, ptr) { }
};
+
+/// Corresponding C++ type that maps to D size_t
+#if __APPLE__ && __i386__
+// size_t is 'unsigned long', which makes it mangle differently than D's 'uint'
+typedef unsigned d_size_t;
+#elif MARS && DMD_VERSION >= 2079 && DMD_VERSION <= 2081 && \
+ __APPLE__ && __SIZEOF_SIZE_T__ == 8
+// DMD versions between 2.079 and 2.081 mapped D ulong to uint64_t on OS X.
+typedef uint64_t d_size_t;
+#else
+typedef size_t d_size_t;
+#endif
diff --git a/gcc/d/dmd/root/file.c b/gcc/d/dmd/root/file.c
deleted file mode 100644
index 314b5b512c8..00000000000
--- a/gcc/d/dmd/root/file.c
+++ /dev/null
@@ -1,258 +0,0 @@
-
-/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- * https://github.com/D-Programming-Language/dmd/blob/master/src/root/file.c
- */
-
-#include "dsystem.h"
-#include "file.h"
-
-#if _WIN32
-#include <windows.h>
-#endif
-
-#if POSIX
-#include <utime.h>
-#endif
-
-#include "filename.h"
-#include "array.h"
-#include "rmem.h"
-
-/****************************** File ********************************/
-
-File::File(const FileName *n)
-{
- ref = 0;
- buffer = NULL;
- len = 0;
- name = const_cast<FileName *>(n);
-}
-
-File *File::create(const char *n)
-{
- return new File(n);
-}
-
-File::File(const char *n)
-{
- ref = 0;
- buffer = NULL;
- len = 0;
- name = new FileName(n);
-}
-
-File::~File()
-{
- if (buffer)
- {
- if (ref == 0)
- mem.xfree(buffer);
-#if _WIN32
- if (ref == 2)
- UnmapViewOfFile(buffer);
-#endif
- }
-}
-
-/*************************************
- */
-
-bool File::read()
-{
- if (len)
- return false; // already read the file
-#if POSIX
- size_t size;
- struct stat buf;
- ssize_t numread;
-
- const char *name = this->name->toChars();
- //printf("File::read('%s')\n",name);
- int fd = open(name, O_RDONLY);
- if (fd == -1)
- {
- //printf("\topen error, errno = %d\n",errno);
- goto err1;
- }
-
- if (!ref)
- ::free(buffer);
- ref = 0; // we own the buffer now
-
- //printf("\tfile opened\n");
- if (fstat(fd, &buf))
- {
- printf("\tfstat error, errno = %d\n",errno);
- goto err2;
- }
- size = (size_t)buf.st_size;
-#ifdef IN_GCC
- buffer = (unsigned char *) ::xmalloc(size + 2);
-#else
- buffer = (unsigned char *) ::malloc(size + 2);
-#endif
- if (!buffer)
- {
- printf("\tmalloc error, errno = %d\n",errno);
- goto err2;
- }
-
- numread = ::read(fd, buffer, size);
- if (numread != (ssize_t)size)
- {
- printf("\tread error, errno = %d\n",errno);
- goto err2;
- }
-
- if (close(fd) == -1)
- {
- printf("\tclose error, errno = %d\n",errno);
- goto err;
- }
-
- len = size;
-
- // Always store a wchar ^Z past end of buffer so scanner has a sentinel
- buffer[size] = 0; // ^Z is obsolete, use 0
- buffer[size + 1] = 0;
- return false;
-
-err2:
- close(fd);
-err:
- ::free(buffer);
- buffer = NULL;
- len = 0;
-
-err1:
- return true;
-#elif _WIN32
- DWORD size;
- DWORD numread;
-
- const char *name = this->name->toChars();
- HANDLE h = CreateFileA(name,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL);
- if (h == INVALID_HANDLE_VALUE)
- goto err1;
-
- if (!ref)
- ::free(buffer);
- ref = 0;
-
- size = GetFileSize(h,NULL);
-#ifdef IN_GCC
- buffer = (unsigned char *) ::xmalloc(size + 2);
-#else
- buffer = (unsigned char *) ::malloc(size + 2);
-#endif
- if (!buffer)
- goto err2;
-
- if (ReadFile(h,buffer,size,&numread,NULL) != TRUE)
- goto err2;
-
- if (numread != size)
- goto err2;
-
- if (!CloseHandle(h))
- goto err;
-
- len = size;
-
- // Always store a wchar ^Z past end of buffer so scanner has a sentinel
- buffer[size] = 0; // ^Z is obsolete, use 0
- buffer[size + 1] = 0;
- return 0;
-
-err2:
- CloseHandle(h);
-err:
- ::free(buffer);
- buffer = NULL;
- len = 0;
-
-err1:
- return true;
-#else
- assert(0);
-#endif
-}
-
-/*********************************************
- * Write a file.
- * Returns:
- * false success
- */
-
-bool File::write()
-{
-#if POSIX
- ssize_t numwritten;
-
- const char *name = this->name->toChars();
- int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4);
- if (fd == -1)
- goto err;
-
- numwritten = ::write(fd, buffer, len);
- if ((ssize_t)len != numwritten)
- goto err2;
-
- if (close(fd) == -1)
- goto err;
-
- return false;
-
-err2:
- close(fd);
- ::remove(name);
-err:
- return true;
-#elif _WIN32
- DWORD numwritten;
-
- const char *name = this->name->toChars();
- HANDLE h = CreateFileA(name,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL);
- if (h == INVALID_HANDLE_VALUE)
- goto err;
-
- if (WriteFile(h,buffer,len,&numwritten,NULL) != TRUE)
- goto err2;
-
- if (len != numwritten)
- goto err2;
-
- if (!CloseHandle(h))
- goto err;
- return false;
-
-err2:
- CloseHandle(h);
- DeleteFileA(name);
-err:
- return true;
-#else
- assert(0);
-#endif
-}
-
-void File::remove()
-{
-#if POSIX
- ::remove(this->name->toChars());
-#elif _WIN32
- DeleteFileA(this->name->toChars());
-#else
- assert(0);
-#endif
-}
-
-const char *File::toChars()
-{
- return name->toChars();
-}
diff --git a/gcc/d/dmd/root/file.d b/gcc/d/dmd/root/file.d
new file mode 100644
index 00000000000..ef6056cfed0
--- /dev/null
+++ b/gcc/d/dmd/root/file.d
@@ -0,0 +1,814 @@
+/**
+ * Read a file from disk and store it in memory.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/file.d, root/_file.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_file.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/file.d
+ */
+
+module dmd.root.file;
+
+import core.stdc.errno;
+import core.stdc.stdio;
+import core.stdc.stdlib;
+import core.stdc.string : strerror;
+import core.sys.posix.fcntl;
+import core.sys.posix.unistd;
+import core.sys.windows.winbase;
+import core.sys.windows.winnt;
+import dmd.root.filename;
+import dmd.root.rmem;
+import dmd.root.string;
+
+/**
+Encapsulated management of a memory-mapped file.
+
+Params:
+Datum = the mapped data type: Use a POD of size 1 for read/write mapping
+and a `const` version thereof for read-only mapping. Other primitive types
+should work, but have not been yet tested.
+*/
+struct FileMapping(Datum)
+{
+ static assert(__traits(isPOD, Datum) && Datum.sizeof == 1,
+ "Not tested with other data types yet. Add new types with care.");
+
+ version(Posix) enum invalidHandle = -1;
+ else version(Windows) enum invalidHandle = INVALID_HANDLE_VALUE;
+
+ // state {
+ /// Handle of underlying file
+ private auto handle = invalidHandle;
+ /// File mapping object needed on Windows
+ version(Windows) private HANDLE fileMappingObject = invalidHandle;
+ /// Memory-mapped array
+ private Datum[] data;
+ /// Name of underlying file, zero-terminated
+ private const(char)* name;
+ // state }
+
+ /**
+ Open `filename` and map it in memory. If `Datum` is `const`, opens for
+ read-only and maps the content in memory; no error is issued if the file
+ does not exist. This makes it easy to treat a non-existing file as empty.
+
+ If `Datum` is mutable, opens for read/write (creates file if it does not
+ exist) and fails fatally on any error.
+
+ Due to quirks in `mmap`, if the file is empty, `handle` is valid but `data`
+ is `null`. This state is valid and accounted for.
+
+ Params:
+ filename = the name of the file to be mapped in memory
+ */
+ this(const char* filename)
+ {
+ version (Posix)
+ {
+ import core.sys.posix.sys.mman;
+ import core.sys.posix.fcntl;
+
+ handle = .open(filename, is(Datum == const) ? O_RDONLY : (O_CREAT | O_RDWR),
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ if (handle == invalidHandle)
+ {
+ static if (is(Datum == const))
+ {
+ // No error, nonexisting file in read mode behaves like an empty file.
+ return;
+ }
+ else
+ {
+ fprintf(stderr, "open(\"%s\") failed: %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ }
+
+ const size = File.size(handle);
+
+ if (size > 0 && size != ulong.max && size <= size_t.max)
+ {
+ auto p = mmap(null, cast(size_t) size, is(Datum == const) ? PROT_READ : PROT_WRITE, MAP_SHARED, handle, 0);
+ if (p == MAP_FAILED)
+ {
+ fprintf(stderr, "mmap(null, %zu) for \"%s\" failed: %s\n", cast(size_t) size, filename, strerror(errno));
+ exit(1);
+ }
+ // The cast below will always work because it's gated by the `size <= size_t.max` condition.
+ data = cast(Datum[]) p[0 .. cast(size_t) size];
+ }
+ }
+ else version(Windows)
+ {
+ static if (is(Datum == const))
+ {
+ enum createFileMode = GENERIC_READ;
+ enum openFlags = OPEN_EXISTING;
+ }
+ else
+ {
+ enum createFileMode = GENERIC_READ | GENERIC_WRITE;
+ enum openFlags = CREATE_ALWAYS;
+ }
+
+ handle = CreateFileA(filename, createFileMode, 0, null, openFlags, FILE_ATTRIBUTE_NORMAL, null);
+ if (handle == invalidHandle)
+ {
+ static if (is(Datum == const))
+ {
+ return;
+ }
+ else
+ {
+ fprintf(stderr, "CreateFileA() failed for \"%s\": %d\n", filename, GetLastError());
+ exit(1);
+ }
+ }
+ createMapping(filename, File.size(handle));
+ }
+ else static assert(0);
+
+ // Save the name for later. Technically there's no need: on Linux one can use readlink on /proc/self/fd/NNN.
+ // On BSD and OSX one can use fcntl with F_GETPATH. On Windows one can use GetFileInformationByHandleEx.
+ // But just saving the name is simplest, fastest, and most portable...
+ import core.stdc.string : strlen;
+ name = filename[0 .. filename.strlen() + 1].idup.ptr;
+ }
+
+ /**
+ Common code factored opportunistically. Windows only. Assumes `handle` is
+ already pointing to an opened file. Initializes the `fileMappingObject`
+ and `data` members.
+
+ Params:
+ filename = the file to be mapped
+ size = the size of the file in bytes
+ */
+ version(Windows) private void createMapping(const char* filename, ulong size)
+ {
+ assert(size <= size_t.max || size == ulong.max);
+ assert(handle != invalidHandle);
+ assert(data is null);
+ assert(fileMappingObject == invalidHandle);
+
+ if (size == 0 || size == ulong.max)
+ return;
+
+ static if (is(Datum == const))
+ {
+ enum fileMappingFlags = PAGE_READONLY;
+ enum mapViewFlags = FILE_MAP_READ;
+ }
+ else
+ {
+ enum fileMappingFlags = PAGE_READWRITE;
+ enum mapViewFlags = FILE_MAP_WRITE;
+ }
+
+ fileMappingObject = CreateFileMappingA(handle, null, fileMappingFlags, 0, 0, null);
+ if (!fileMappingObject)
+ {
+ fprintf(stderr, "CreateFileMappingA(%p) failed for %llu bytes of \"%s\": %d\n",
+ handle, size, filename, GetLastError());
+ fileMappingObject = invalidHandle; // by convention always use invalidHandle, not null
+ exit(1);
+ }
+ auto p = MapViewOfFile(fileMappingObject, mapViewFlags, 0, 0, 0);
+ if (!p)
+ {
+ fprintf(stderr, "MapViewOfFile() failed for \"%s\": %d\n", filename, GetLastError());
+ exit(1);
+ }
+ data = cast(Datum[]) p[0 .. cast(size_t) size];
+ }
+
+ // Not copyable or assignable (for now).
+ @disable this(const FileMapping!Datum rhs);
+ @disable void opAssign(const ref FileMapping!Datum rhs);
+
+ /**
+ Frees resources associated with this mapping. However, it does not deallocate the name.
+ */
+ ~this() pure nothrow
+ {
+ if (!active)
+ return;
+ fakePure({
+ version (Posix)
+ {
+ import core.sys.posix.sys.mman : munmap;
+
+ // Cannot call fprintf from inside a destructor, so exiting silently.
+
+ if (data.ptr && munmap(cast(void*) data.ptr, data.length) != 0)
+ {
+ exit(1);
+ }
+ data = null;
+ if (handle != invalidHandle && .close(handle) != 0)
+ {
+ exit(1);
+ }
+ handle = invalidHandle;
+ }
+ else version(Windows)
+ {
+ if (data.ptr !is null && UnmapViewOfFile(cast(void*) data.ptr) == 0)
+ {
+ exit(1);
+ }
+ data = null;
+ if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
+ {
+ exit(1);
+ }
+ fileMappingObject = invalidHandle;
+ if (handle != invalidHandle && CloseHandle(handle) == 0)
+ {
+ exit(1);
+ }
+ handle = invalidHandle;
+ }
+ else static assert(0);
+ });
+ }
+
+ /**
+ Returns the zero-terminated file name associated with the mapping. Can
+ be saved beyond the lifetime of `this`.
+ */
+ const(char)* filename() const pure @nogc @safe nothrow { return name; }
+
+ /**
+ Frees resources associated with this mapping. However, it does not deallocate the name.
+ Reinitializes `this` as a fresh object that can be reused.
+ */
+ void close()
+ {
+ __dtor();
+ handle = invalidHandle;
+ version(Windows) fileMappingObject = invalidHandle;
+ data = null;
+ name = null;
+ }
+
+ /**
+ Deletes the underlying file and frees all resources associated.
+ Reinitializes `this` as a fresh object that can be reused.
+
+ This function does not abort if the file cannot be deleted, but does print
+ a message on `stderr` and returns `false` to the caller. The underlying
+ rationale is to give the caller the option to continue execution if
+ deleting the file is not important.
+
+ Returns: `true` iff the file was successfully deleted. If the file was not
+ deleted, prints a message to `stderr` and returns `false`.
+ */
+ static if (!is(Datum == const))
+ bool discard()
+ {
+ // Truncate file to zero so unflushed buffers are not flushed unnecessarily.
+ resize(0);
+ auto deleteme = name;
+ close();
+ // In-memory resource freed, now get rid of the underlying temp file.
+ version(Posix)
+ {
+ import core.sys.posix.unistd;
+ if (unlink(deleteme) != 0)
+ {
+ fprintf(stderr, "unlink(\"%s\") failed: %s\n", filename, strerror(errno));
+ return false;
+ }
+ }
+ else version(Windows)
+ {
+ import core.sys.windows.winbase;
+ if (DeleteFileA(deleteme) == 0)
+ {
+ fprintf(stderr, "DeleteFileA error %d\n", GetLastError());
+ return false;
+ }
+ }
+ else static assert(0);
+ return true;
+ }
+
+ /**
+ Queries whether `this` is currently associated with a file.
+
+ Returns: `true` iff there is an active mapping.
+ */
+ bool active() const pure @nogc nothrow
+ {
+ return handle !is invalidHandle;
+ }
+
+ /**
+ Queries the length of the file associated with this mapping. If not
+ active, returns 0.
+
+ Returns: the length of the file, or 0 if no file associated.
+ */
+ size_t length() const pure @nogc @safe nothrow { return data.length; }
+
+ /**
+ Get a slice to the contents of the entire file.
+
+ Returns: the contents of the file. If not active, returns the `null` slice.
+ */
+ auto opSlice() pure @nogc @safe nothrow { return data; }
+
+ /**
+ Resizes the file and mapping to the specified `size`.
+
+ Params:
+ size = new length requested
+ */
+ static if (!is(Datum == const))
+ void resize(size_t size) pure
+ {
+ assert(handle != invalidHandle);
+ fakePure({
+ version(Posix)
+ {
+ import core.sys.posix.unistd : ftruncate;
+ import core.sys.posix.sys.mman;
+
+ if (data.length)
+ {
+ assert(data.ptr, "Corrupt memory mapping");
+ // assert(0) here because it would indicate an internal error
+ munmap(cast(void*) data.ptr, data.length) == 0 || assert(0);
+ data = null;
+ }
+ if (ftruncate(handle, size) != 0)
+ {
+ fprintf(stderr, "ftruncate() failed for \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ if (size > 0)
+ {
+ auto p = mmap(null, size, PROT_WRITE, MAP_SHARED, handle, 0);
+ if (cast(ssize_t) p == -1)
+ {
+ fprintf(stderr, "mmap() failed for \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ data = cast(Datum[]) p[0 .. size];
+ }
+ }
+ else version(Windows)
+ {
+ // Per documentation, must unmap first.
+ if (data.length > 0 && UnmapViewOfFile(cast(void*) data.ptr) == 0)
+ {
+ fprintf(stderr, "UnmapViewOfFile(%p) failed for memory mapping of \"%s\": %d\n",
+ data.ptr, filename, GetLastError());
+ exit(1);
+ }
+ data = null;
+ if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
+ {
+ fprintf(stderr, "CloseHandle() failed for memory mapping of \"%s\": %d\n", filename, GetLastError());
+ exit(1);
+ }
+ fileMappingObject = invalidHandle;
+ LARGE_INTEGER biggie;
+ biggie.QuadPart = size;
+ if (SetFilePointerEx(handle, biggie, null, FILE_BEGIN) == 0 || SetEndOfFile(handle) == 0)
+ {
+ fprintf(stderr, "SetFilePointer() failed for \"%s\": %d\n", filename, GetLastError());
+ exit(1);
+ }
+ createMapping(name, size);
+ }
+ else static assert(0);
+ });
+ }
+
+ /**
+ Unconditionally and destructively moves the underlying file to `filename`.
+ If the operation succeds, returns true. Upon failure, prints a message to
+ `stderr` and returns `false`.
+
+ Params: filename = zero-terminated name of the file to move to.
+
+ Returns: `true` iff the operation was successful.
+ */
+ bool moveToFile(const char* filename)
+ {
+ auto oldname = name;
+
+ close();
+ // Rename the underlying file to the target, no copy necessary.
+ version(Posix)
+ {
+ if (.rename(oldname, filename) != 0)
+ {
+ fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", oldname, filename, strerror(errno));
+ return false;
+ }
+ }
+ else version(Windows)
+ {
+ import core.sys.windows.winbase;
+ if (MoveFileExA(oldname, filename, MOVEFILE_REPLACE_EXISTING) == 0)
+ {
+ fprintf(stderr, "MoveFileExA(\"%s\", \"%s\") failed: %d\n", oldname, filename, GetLastError());
+ return false;
+ }
+ }
+ else static assert(0);
+ return true;
+ }
+}
+
+/// Owns a (rmem-managed) file buffer.
+struct FileBuffer
+{
+ ubyte[] data;
+
+ this(this) @disable;
+
+ ~this() pure nothrow
+ {
+ mem.xfree(data.ptr);
+ }
+
+ /// Transfers ownership of the buffer to the caller.
+ ubyte[] extractSlice() pure nothrow @nogc @safe
+ {
+ auto result = data;
+ data = null;
+ return result;
+ }
+
+ extern (C++) static FileBuffer* create() pure nothrow @safe
+ {
+ return new FileBuffer();
+ }
+}
+
+///
+struct File
+{
+ ///
+ static struct ReadResult
+ {
+ bool success;
+ FileBuffer buffer;
+
+ /// Transfers ownership of the buffer to the caller.
+ ubyte[] extractSlice() pure nothrow @nogc @safe
+ {
+ return buffer.extractSlice();
+ }
+
+ /// ditto
+ /// Include the null-terminator at the end of the buffer in the returned array.
+ ubyte[] extractDataZ() @nogc nothrow pure
+ {
+ auto result = buffer.extractSlice();
+ return result.ptr[0 .. result.length + 1];
+ }
+ }
+
+nothrow:
+ /// Read the full content of a file.
+ extern (C++) static ReadResult read(const(char)* name)
+ {
+ return read(name.toDString());
+ }
+
+ /// Ditto
+ static ReadResult read(const(char)[] name)
+ {
+ ReadResult result;
+
+ version (Posix)
+ {
+ size_t size;
+ stat_t buf;
+ ssize_t numread;
+ //printf("File::read('%s')\n",name);
+ int fd = name.toCStringThen!(slice => open(slice.ptr, O_RDONLY));
+ if (fd == -1)
+ {
+ //printf("\topen error, errno = %d\n",errno);
+ return result;
+ }
+ //printf("\tfile opened\n");
+ if (fstat(fd, &buf))
+ {
+ perror("\tfstat error");
+ close(fd);
+ return result;
+ }
+ size = cast(size_t)buf.st_size;
+ ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 4);
+ numread = .read(fd, buffer, size);
+ if (numread != size)
+ {
+ perror("\tread error");
+ goto err2;
+ }
+ if (close(fd) == -1)
+ {
+ perror("\tclose error");
+ goto err;
+ }
+ // Always store a wchar ^Z past end of buffer so scanner has a sentinel
+ buffer[size] = 0; // ^Z is obsolete, use 0
+ buffer[size + 1] = 0;
+ buffer[size + 2] = 0; //add two more so lexer doesnt read pass the buffer
+ buffer[size + 3] = 0;
+
+ result.success = true;
+ result.buffer.data = buffer[0 .. size];
+ return result;
+ err2:
+ close(fd);
+ err:
+ mem.xfree(buffer);
+ return result;
+ }
+ else version (Windows)
+ {
+ DWORD size;
+ DWORD numread;
+
+ // work around Windows file path length limitation
+ // (see documentation for extendedPathThen).
+ HANDLE h = name.extendedPathThen!
+ (p => CreateFileW(p.ptr,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ null,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
+ null));
+ if (h == INVALID_HANDLE_VALUE)
+ return result;
+ size = GetFileSize(h, null);
+ ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 4);
+ if (ReadFile(h, buffer, size, &numread, null) != TRUE)
+ goto err2;
+ if (numread != size)
+ goto err2;
+ if (!CloseHandle(h))
+ goto err;
+ // Always store a wchar ^Z past end of buffer so scanner has a sentinel
+ buffer[size] = 0; // ^Z is obsolete, use 0
+ buffer[size + 1] = 0;
+ buffer[size + 2] = 0; //add two more so lexer doesnt read pass the buffer
+ buffer[size + 3] = 0;
+ result.success = true;
+ result.buffer.data = buffer[0 .. size];
+ return result;
+ err2:
+ CloseHandle(h);
+ err:
+ mem.xfree(buffer);
+ return result;
+ }
+ else
+ {
+ assert(0);
+ }
+ }
+
+ /// Write a file, returning `true` on success.
+ extern (D) static bool write(const(char)* name, const void[] data)
+ {
+ version (Posix)
+ {
+ ssize_t numwritten;
+ int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4);
+ if (fd == -1)
+ goto err;
+ numwritten = .write(fd, data.ptr, data.length);
+ if (numwritten != data.length)
+ goto err2;
+ if (close(fd) == -1)
+ goto err;
+ return true;
+ err2:
+ close(fd);
+ .remove(name);
+ err:
+ return false;
+ }
+ else version (Windows)
+ {
+ DWORD numwritten; // here because of the gotos
+ const nameStr = name.toDString;
+ // work around Windows file path length limitation
+ // (see documentation for extendedPathThen).
+ HANDLE h = nameStr.extendedPathThen!
+ (p => CreateFileW(p.ptr,
+ GENERIC_WRITE,
+ 0,
+ null,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
+ null));
+ if (h == INVALID_HANDLE_VALUE)
+ goto err;
+
+ if (WriteFile(h, data.ptr, cast(DWORD)data.length, &numwritten, null) != TRUE)
+ goto err2;
+ if (numwritten != data.length)
+ goto err2;
+ if (!CloseHandle(h))
+ goto err;
+ return true;
+ err2:
+ CloseHandle(h);
+ nameStr.extendedPathThen!(p => DeleteFileW(p.ptr));
+ err:
+ return false;
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+
+ ///ditto
+ extern(D) static bool write(const(char)[] name, const void[] data)
+ {
+ return name.toCStringThen!((fname) => write(fname.ptr, data));
+ }
+
+ /// ditto
+ extern (C++) static bool write(const(char)* name, const(void)* data, size_t size)
+ {
+ return write(name, data[0 .. size]);
+ }
+
+ /// Delete a file.
+ extern (C++) static void remove(const(char)* name)
+ {
+ version (Posix)
+ {
+ .remove(name);
+ }
+ else version (Windows)
+ {
+ name.toDString.extendedPathThen!(p => DeleteFileW(p.ptr));
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+
+ /***************************************************
+ * Update file
+ *
+ * If the file exists and is identical to what is to be written,
+ * merely update the timestamp on the file.
+ * Otherwise, write the file.
+ *
+ * The idea is writes are much slower than reads, and build systems
+ * often wind up generating identical files.
+ * Params:
+ * name = name of file to update
+ * data = updated contents of file
+ * Returns:
+ * `true` on success
+ */
+ extern (D) static bool update(const(char)* namez, const void[] data)
+ {
+ enum log = false;
+ if (log) printf("update %s\n", namez);
+
+ if (data.length != File.size(namez))
+ return write(namez, data); // write new file
+
+ if (log) printf("same size\n");
+
+ /* The file already exists, and is the same size.
+ * Read it in, and compare for equality.
+ */
+ //if (FileMapping!(const ubyte)(namez)[] != data[])
+ return write(namez, data); // contents not same, so write new file
+ //if (log) printf("same contents\n");
+
+ /* Contents are identical, so set timestamp of existing file to current time
+ */
+ //return touch(namez);
+ }
+
+ ///ditto
+ extern(D) static bool update(const(char)[] name, const void[] data)
+ {
+ return name.toCStringThen!(fname => update(fname.ptr, data));
+ }
+
+ /// ditto
+ extern (C++) static bool update(const(char)* name, const(void)* data, size_t size)
+ {
+ return update(name, data[0 .. size]);
+ }
+
+ /// Touch a file to current date
+ static bool touch(const char* namez)
+ {
+ version (Windows)
+ {
+ FILETIME ft = void;
+ SYSTEMTIME st = void;
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &ft);
+
+ import core.stdc.string : strlen;
+
+ // get handle to file
+ HANDLE h = namez[0 .. namez.strlen()].extendedPathThen!(p => CreateFile(p.ptr,
+ FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ null, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, null));
+ if (h == INVALID_HANDLE_VALUE)
+ return false;
+
+ const f = SetFileTime(h, null, null, &ft); // set last write time
+
+ if (!CloseHandle(h))
+ return false;
+
+ return f != 0;
+ }
+ else version (Posix)
+ {
+ import core.sys.posix.utime;
+ return utime(namez, null) == 0;
+ }
+ else
+ static assert(0);
+ }
+
+ /// Size of a file in bytes.
+ /// Params: namez = null-terminated filename
+ /// Returns: `ulong.max` on any error, the length otherwise.
+ static ulong size(const char* namez)
+ {
+ version (Posix)
+ {
+ stat_t buf;
+ if (stat(namez, &buf) == 0)
+ return buf.st_size;
+ }
+ else version (Windows)
+ {
+ const nameStr = namez.toDString();
+ import core.sys.windows.windows;
+ WIN32_FILE_ATTRIBUTE_DATA fad = void;
+ // Doesn't exist, not a regular file, different size
+ if (nameStr.extendedPathThen!(p => GetFileAttributesExW(p.ptr, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad)) != 0)
+ return (ulong(fad.nFileSizeHigh) << 32UL) | fad.nFileSizeLow;
+ }
+ else static assert(0);
+ // Error cases go here.
+ return ulong.max;
+ }
+
+ /// Ditto
+ version (Posix)
+ static ulong size(int fd)
+ {
+ stat_t buf;
+ if (fstat(fd, &buf) == 0)
+ return buf.st_size;
+ return ulong.max;
+ }
+
+ /// Ditto
+ version (Windows)
+ static ulong size(HANDLE fd)
+ {
+ ulong result;
+ if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result) == 0)
+ return result;
+ return ulong.max;
+ }
+}
+
+/**
+Runs a non-pure function or delegate as pure code. Use with caution.
+
+Params:
+fun = the delegate to run, usually inlined: `fakePure({ ... });`
+
+Returns: whatever `fun` returns.
+*/
+private auto ref fakePure(F)(scope F fun) pure
+{
+ mixin("alias PureFun = " ~ F.stringof ~ " pure;");
+ return (cast(PureFun) fun)();
+}
diff --git a/gcc/d/dmd/root/file.h b/gcc/d/dmd/root/file.h
index 51358182b7c..ee0d51e105c 100644
--- a/gcc/d/dmd/root/file.h
+++ b/gcc/d/dmd/root/file.h
@@ -1,5 +1,6 @@
/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
@@ -8,46 +9,33 @@
#pragma once
-#include "dsystem.h"
#include "array.h"
+#include "filename.h"
-typedef Array<struct File *> Files;
-
-struct FileName;
-
-struct File
+struct FileBuffer
{
- int ref; // != 0 if this is a reference to someone else's buffer
- unsigned char *buffer; // data for our file
- size_t len; // amount of data in buffer[]
-
- FileName *name; // name of our file
-
- File(const char *);
- static File *create(const char *);
- File(const FileName *);
- ~File();
-
- const char *toChars();
+ DArray<unsigned char> data;
- /* Read file, return true if error
- */
+ FileBuffer(const FileBuffer &) /* = delete */;
+ ~FileBuffer() { mem.xfree(data.ptr); }
- bool read();
-
- /* Write file, return true if error
- */
+ static FileBuffer *create();
+};
- bool write();
+struct File
+{
+ struct ReadResult
+ {
+ bool success;
+ FileBuffer buffer;
+ };
- /* Set buffer
- */
+ // Read the full content of a file.
+ static ReadResult read(const char *name);
- void setbuffer(void *buffer, size_t len)
- {
- this->buffer = (unsigned char *)buffer;
- this->len = len;
- }
+ // Write a file, returning `true` on success.
+ static bool write(const char *name, const void *data, d_size_t size);
- void remove(); // delete file
+ // Delete a file.
+ static void remove(const char *name);
};
diff --git a/gcc/d/dmd/root/filename.c b/gcc/d/dmd/root/filename.c
deleted file mode 100644
index 0c5138b6ddf..00000000000
--- a/gcc/d/dmd/root/filename.c
+++ /dev/null
@@ -1,671 +0,0 @@
-
-/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- * https://github.com/D-Programming-Language/dmd/blob/master/src/root/filename.c
- */
-
-#include "dsystem.h"
-#include "filename.h"
-#include "port.h"
-#include "outbuffer.h"
-#include "array.h"
-#include "file.h"
-#include "rmem.h"
-
-#if _WIN32
-#include <windows.h>
-#endif
-
-#if POSIX
-#include <utime.h>
-#endif
-
-/****************************** FileName ********************************/
-
-FileName::FileName(const char *str)
- : str(mem.xstrdup(str))
-{
-}
-
-const char *FileName::combine(const char *path, const char *name)
-{ char *f;
- size_t pathlen;
- size_t namelen;
-
- if (!path || !*path)
- return name;
- pathlen = strlen(path);
- namelen = strlen(name);
- f = (char *)mem.xmalloc(pathlen + 1 + namelen + 1);
- memcpy(f, path, pathlen);
-#if POSIX
- if (path[pathlen - 1] != '/')
- { f[pathlen] = '/';
- pathlen++;
- }
-#elif _WIN32
- if (path[pathlen - 1] != '\\' &&
- path[pathlen - 1] != '/' &&
- path[pathlen - 1] != ':')
- { f[pathlen] = '\\';
- pathlen++;
- }
-#else
- assert(0);
-#endif
- memcpy(f + pathlen, name, namelen + 1);
- return f;
-}
-
-// Split a path into an Array of paths
-Strings *FileName::splitPath(const char *path)
-{
- char c = 0; // unnecessary initializer is for VC /W4
- const char *p;
- OutBuffer buf;
- Strings *array;
-
- array = new Strings();
- if (path)
- {
- p = path;
- do
- { char instring = 0;
-
- while (isspace((utf8_t)*p)) // skip leading whitespace
- p++;
- buf.reserve(strlen(p) + 1); // guess size of path
- for (; ; p++)
- {
- c = *p;
- switch (c)
- {
- case '"':
- instring ^= 1; // toggle inside/outside of string
- continue;
-
-#if MACINTOSH
- case ',':
-#endif
-#if _WIN32
- case ';':
-#endif
-#if POSIX
- case ':':
-#endif
- p++;
- break; // note that ; cannot appear as part
- // of a path, quotes won't protect it
-
- case 0x1A: // ^Z means end of file
- case 0:
- break;
-
- case '\r':
- continue; // ignore carriage returns
-
-#if POSIX
- case '~':
- {
- char *home = getenv("HOME");
- // Expand ~ only if it is prefixing the rest of the path.
- if (!buf.length() && p[1] == '/' && home)
- buf.writestring(home);
- else
- buf.writestring("~");
- continue;
- }
-#endif
-
- default:
- buf.writeByte(c);
- continue;
- }
- break;
- }
- if (buf.length()) // if path is not empty
- {
- array->push(buf.extractChars());
- }
- } while (c);
- }
- return array;
-}
-
-int FileName::compare(RootObject *obj)
-{
- return compare(str, ((FileName *)obj)->str);
-}
-
-int FileName::compare(const char *name1, const char *name2)
-{
-#if _WIN32
- return stricmp(name1, name2);
-#else
- return strcmp(name1, name2);
-#endif
-}
-
-bool FileName::equals(RootObject *obj)
-{
- return compare(obj) == 0;
-}
-
-bool FileName::equals(const char *name1, const char *name2)
-{
- return compare(name1, name2) == 0;
-}
-
-/************************************
- * Return !=0 if absolute path name.
- */
-
-bool FileName::absolute(const char *name)
-{
-#if _WIN32
- return (*name == '\\') ||
- (*name == '/') ||
- (*name && name[1] == ':');
-#elif POSIX
- return (*name == '/');
-#else
- assert(0);
-#endif
-}
-
-/**
-Return the given name as an absolute path
-
-Params:
- name = path
- base = the absolute base to prefix name with if it is relative
-
-Returns: name as an absolute path relative to base
-*/
-const char *FileName::toAbsolute(const char *name, const char *base)
-{
- return absolute(name) ? name : combine(base ? base : getcwd(NULL, 0), name);
-}
-
-/********************************
- * Return filename extension (read-only).
- * Points past '.' of extension.
- * If there isn't one, return NULL.
- */
-
-const char *FileName::ext(const char *str)
-{
- size_t len = strlen(str);
-
- const char *e = str + len;
- for (;;)
- {
- switch (*e)
- { case '.':
- return e + 1;
-#if POSIX
- case '/':
- break;
-#endif
-#if _WIN32
- case '\\':
- case ':':
- case '/':
- break;
-#endif
- default:
- if (e == str)
- break;
- e--;
- continue;
- }
- return NULL;
- }
-}
-
-const char *FileName::ext()
-{
- return ext(str);
-}
-
-/********************************
- * Return mem.xmalloc'd filename with extension removed.
- */
-
-const char *FileName::removeExt(const char *str)
-{
- const char *e = ext(str);
- if (e)
- { size_t len = (e - str) - 1;
- char *n = (char *)mem.xmalloc(len + 1);
- memcpy(n, str, len);
- n[len] = 0;
- return n;
- }
- return mem.xstrdup(str);
-}
-
-/********************************
- * Return filename name excluding path (read-only).
- */
-
-const char *FileName::name(const char *str)
-{
- size_t len = strlen(str);
-
- const char *e = str + len;
- for (;;)
- {
- switch (*e)
- {
-#if POSIX
- case '/':
- return e + 1;
-#endif
-#if _WIN32
- case '/':
- case '\\':
- return e + 1;
- case ':':
- /* The ':' is a drive letter only if it is the second
- * character or the last character,
- * otherwise it is an ADS (Alternate Data Stream) separator.
- * Consider ADS separators as part of the file name.
- */
- if (e == str + 1 || e == str + len - 1)
- return e + 1;
-#endif
- /* falls through */
- default:
- if (e == str)
- break;
- e--;
- continue;
- }
- return e;
- }
-}
-
-const char *FileName::name()
-{
- return name(str);
-}
-
-/**************************************
- * Return path portion of str.
- * Path will does not include trailing path separator.
- */
-
-const char *FileName::path(const char *str)
-{
- const char *n = name(str);
- size_t pathlen;
-
- if (n > str)
- {
-#if POSIX
- if (n[-1] == '/')
- n--;
-#elif _WIN32
- if (n[-1] == '\\' || n[-1] == '/')
- n--;
-#else
- assert(0);
-#endif
- }
- pathlen = n - str;
- char *path = (char *)mem.xmalloc(pathlen + 1);
- memcpy(path, str, pathlen);
- path[pathlen] = 0;
- return path;
-}
-
-/**************************************
- * Replace filename portion of path.
- */
-
-const char *FileName::replaceName(const char *path, const char *name)
-{
- size_t pathlen;
- size_t namelen;
-
- if (absolute(name))
- return name;
-
- const char *n = FileName::name(path);
- if (n == path)
- return name;
- pathlen = n - path;
- namelen = strlen(name);
- char *f = (char *)mem.xmalloc(pathlen + 1 + namelen + 1);
- memcpy(f, path, pathlen);
-#if POSIX
- if (path[pathlen - 1] != '/')
- { f[pathlen] = '/';
- pathlen++;
- }
-#elif _WIN32
- if (path[pathlen - 1] != '\\' &&
- path[pathlen - 1] != '/' &&
- path[pathlen - 1] != ':')
- { f[pathlen] = '\\';
- pathlen++;
- }
-#else
- assert(0);
-#endif
- memcpy(f + pathlen, name, namelen + 1);
- return f;
-}
-
-/***************************
- * Free returned value with FileName::free()
- */
-
-const char *FileName::defaultExt(const char *name, const char *ext)
-{
- const char *e = FileName::ext(name);
- if (e) // if already has an extension
- return mem.xstrdup(name);
-
- size_t len = strlen(name);
- size_t extlen = strlen(ext);
- char *s = (char *)mem.xmalloc(len + 1 + extlen + 1);
- memcpy(s,name,len);
- s[len] = '.';
- memcpy(s + len + 1, ext, extlen + 1);
- return s;
-}
-
-/***************************
- * Free returned value with FileName::free()
- */
-
-const char *FileName::forceExt(const char *name, const char *ext)
-{
- const char *e = FileName::ext(name);
- if (e) // if already has an extension
- {
- size_t len = e - name;
- size_t extlen = strlen(ext);
-
- char *s = (char *)mem.xmalloc(len + extlen + 1);
- memcpy(s,name,len);
- memcpy(s + len, ext, extlen + 1);
- return s;
- }
- else
- return defaultExt(name, ext); // doesn't have one
-}
-
-/******************************
- * Return !=0 if extensions match.
- */
-
-bool FileName::equalsExt(const char *ext)
-{
- return equalsExt(str, ext);
-}
-
-bool FileName::equalsExt(const char *name, const char *ext)
-{
- const char *e = FileName::ext(name);
- if (!e && !ext)
- return true;
- if (!e || !ext)
- return false;
- return FileName::compare(e, ext) == 0;
-}
-
-/*************************************
- * Search Path for file.
- * Input:
- * cwd if true, search current directory before searching path
- */
-
-const char *FileName::searchPath(Strings *path, const char *name, bool cwd)
-{
- if (absolute(name))
- {
- return exists(name) ? name : NULL;
- }
- if (cwd)
- {
- if (exists(name))
- return name;
- }
- if (path)
- {
-
- for (size_t i = 0; i < path->length; i++)
- {
- const char *p = (*path)[i];
- const char *n = combine(p, name);
-
- if (exists(n))
- return n;
- }
- }
- return NULL;
-}
-
-
-/*************************************
- * Search Path for file in a safe manner.
- *
- * Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
- * ('Path Traversal') attacks.
- * http://cwe.mitre.org/data/definitions/22.html
- * More info:
- * https://www.securecoding.cert.org/confluence/display/c/FIO02-C.+Canonicalize+path+names+originating+from+tainted+sources
- * Returns:
- * NULL file not found
- * !=NULL mem.xmalloc'd file name
- */
-
-const char *FileName::safeSearchPath(Strings *path, const char *name)
-{
-#if _WIN32
- // don't allow leading / because it might be an absolute
- // path or UNC path or something we'd prefer to just not deal with
- if (*name == '/')
- {
- return NULL;
- }
- /* Disallow % \ : and .. in name characters
- * We allow / for compatibility with subdirectories which is allowed
- * on dmd/posix. With the leading / blocked above and the rest of these
- * conservative restrictions, we should be OK.
- */
- for (const char *p = name; *p; p++)
- {
- char c = *p;
- if (c == '\\' || c == ':' || c == '%' || (c == '.' && p[1] == '.'))
- {
- return NULL;
- }
- }
-
- return FileName::searchPath(path, name, false);
-#elif POSIX
- /* Even with realpath(), we must check for // and disallow it
- */
- for (const char *p = name; *p; p++)
- {
- char c = *p;
- if (c == '/' && p[1] == '/')
- {
- return NULL;
- }
- }
-
- if (path)
- {
- /* Each path is converted to a cannonical name and then a check is done to see
- * that the searched name is really a child one of the the paths searched.
- */
- for (size_t i = 0; i < path->length; i++)
- {
- const char *cname = NULL;
- const char *cpath = canonicalName((*path)[i]);
- //printf("FileName::safeSearchPath(): name=%s; path=%s; cpath=%s\n",
- // name, (char *)path->data[i], cpath);
- if (cpath == NULL)
- goto cont;
- cname = canonicalName(combine(cpath, name));
- //printf("FileName::safeSearchPath(): cname=%s\n", cname);
- if (cname == NULL)
- goto cont;
- //printf("FileName::safeSearchPath(): exists=%i "
- // "strncmp(cpath, cname, %i)=%i\n", exists(cname),
- // strlen(cpath), strncmp(cpath, cname, strlen(cpath)));
- // exists and name is *really* a "child" of path
- if (exists(cname) && strncmp(cpath, cname, strlen(cpath)) == 0)
- {
- ::free(const_cast<char *>(cpath));
- const char *p = mem.xstrdup(cname);
- ::free(const_cast<char *>(cname));
- return p;
- }
-cont:
- if (cpath)
- ::free(const_cast<char *>(cpath));
- if (cname)
- ::free(const_cast<char *>(cname));
- }
- }
- return NULL;
-#else
- assert(0);
-#endif
-}
-
-
-int FileName::exists(const char *name)
-{
-#if POSIX
- struct stat st;
-
- if (stat(name, &st) < 0)
- return 0;
- if (S_ISDIR(st.st_mode))
- return 2;
- return 1;
-#elif _WIN32
- DWORD dw;
- int result;
-
- dw = GetFileAttributesA(name);
- if (dw == INVALID_FILE_ATTRIBUTES)
- result = 0;
- else if (dw & FILE_ATTRIBUTE_DIRECTORY)
- result = 2;
- else
- result = 1;
- return result;
-#else
- assert(0);
-#endif
-}
-
-bool FileName::ensurePathExists(const char *path)
-{
- //printf("FileName::ensurePathExists(%s)\n", path ? path : "");
- if (path && *path)
- {
- if (!exists(path))
- {
- const char *p = FileName::path(path);
- if (*p)
- {
-#if _WIN32
- size_t len = strlen(path);
- if ((len > 2 && p[-1] == ':' && strcmp(path + 2, p) == 0) ||
- len == strlen(p))
- { mem.xfree(const_cast<char *>(p));
- return 0;
- }
-#endif
- bool r = ensurePathExists(p);
- mem.xfree(const_cast<char *>(p));
- if (r)
- return r;
- }
-#if _WIN32
- char sep = '\\';
-#elif POSIX
- char sep = '/';
-#endif
- if (path[strlen(path) - 1] != sep)
- {
- //printf("mkdir(%s)\n", path);
-#if _WIN32
- int r = _mkdir(path);
-#endif
-#if POSIX
- int r = mkdir(path, (7 << 6) | (7 << 3) | 7);
-#endif
- if (r)
- {
- /* Don't error out if another instance of dmd just created
- * this directory
- */
- if (errno != EEXIST)
- return true;
- }
- }
- }
- }
- return false;
-}
-
-/******************************************
- * Return canonical version of name in a malloc'd buffer.
- * This code is high risk.
- */
-const char *FileName::canonicalName(const char *name)
-{
-#if POSIX
- // NULL destination buffer is allowed and preferred
- return realpath(name, NULL);
-#elif _WIN32
- /* Apparently, there is no good way to do this on Windows.
- * GetFullPathName isn't it, but use it anyway.
- */
- DWORD result = GetFullPathNameA(name, 0, NULL, NULL);
- if (result)
- {
- char *buf = (char *)mem.xmalloc(result);
- result = GetFullPathNameA(name, result, buf, NULL);
- if (result == 0)
- {
- ::free(buf);
- return NULL;
- }
- return buf;
- }
- return NULL;
-#else
- assert(0);
- return NULL;
-#endif
-}
-
-/********************************
- * Free memory allocated by FileName routines
- */
-void FileName::free(const char *str)
-{
- if (str)
- { assert(str[0] != (char)0xAB);
- memset(const_cast<char *>(str), 0xAB, strlen(str) + 1); // stomp
- }
- mem.xfree(const_cast<char *>(str));
-}
-
-const char *FileName::toChars() const
-{
- return str;
-}
diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d
new file mode 100644
index 00000000000..1e4ccb5d344
--- /dev/null
+++ b/gcc/d/dmd/root/filename.d
@@ -0,0 +1,1273 @@
+/**
+ * Encapsulate path and file names.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/filename.d, root/_filename.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_filename.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/filename.d
+ */
+
+module dmd.root.filename;
+
+import core.stdc.ctype;
+import core.stdc.errno;
+import core.stdc.string;
+import dmd.root.array;
+import dmd.root.file;
+import dmd.root.outbuffer;
+import dmd.root.port;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.root.string;
+
+version (Posix)
+{
+ import core.sys.posix.stdlib;
+ import core.sys.posix.sys.stat;
+ import core.sys.posix.unistd : getcwd;
+}
+
+version (Windows)
+{
+ import core.sys.windows.winbase;
+ import core.sys.windows.windef;
+ import core.sys.windows.winnls;
+
+ extern (Windows) DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*) nothrow @nogc;
+ extern (Windows) void SetLastError(DWORD) nothrow @nogc;
+ extern (C) char* getcwd(char* buffer, size_t maxlen) nothrow;
+
+ // assume filenames encoded in system default Windows ANSI code page
+ private enum CodePage = CP_ACP;
+}
+
+version (CRuntime_Glibc)
+{
+ extern (C) char* canonicalize_file_name(const char*) nothrow;
+}
+
+alias Strings = Array!(const(char)*);
+
+
+// Check whether character is a directory separator
+private bool isDirSeparator(char c) pure nothrow @nogc @safe
+{
+ version (Windows)
+ {
+ return c == '\\' || c == '/';
+ }
+ else version (Posix)
+ {
+ return c == '/';
+ }
+ else
+ {
+ assert(0);
+ }
+}
+
+/***********************************************************
+ * Encapsulate path and file names.
+ */
+struct FileName
+{
+nothrow:
+ private const(char)[] str;
+
+ ///
+ extern (D) this(const(char)[] str) pure
+ {
+ this.str = str.xarraydup;
+ }
+
+ /// Compare two name according to the platform's rules (case sensitive or not)
+ extern (C++) static bool equals(const(char)* name1, const(char)* name2) pure @nogc
+ {
+ return equals(name1.toDString, name2.toDString);
+ }
+
+ /// Ditto
+ extern (D) static bool equals(const(char)[] name1, const(char)[] name2) pure @nogc
+ {
+ if (name1.length != name2.length)
+ return false;
+
+ version (Windows)
+ {
+ return name1.ptr == name2.ptr ||
+ Port.memicmp(name1.ptr, name2.ptr, name1.length) == 0;
+ }
+ else
+ {
+ return name1 == name2;
+ }
+ }
+
+ /************************************
+ * Determine if path is absolute.
+ * Params:
+ * name = path
+ * Returns:
+ * true if absolute path name.
+ */
+ extern (C++) static bool absolute(const(char)* name) pure @nogc
+ {
+ return absolute(name.toDString);
+ }
+
+ /// Ditto
+ extern (D) static bool absolute(const(char)[] name) pure @nogc
+ {
+ if (!name.length)
+ return false;
+
+ version (Windows)
+ {
+ return isDirSeparator(name[0])
+ || (name.length >= 2 && name[1] == ':');
+ }
+ else version (Posix)
+ {
+ return isDirSeparator(name[0]);
+ }
+ else
+ {
+ assert(0);
+ }
+ }
+
+ unittest
+ {
+ assert(absolute("/"[]) == true);
+ assert(absolute(""[]) == false);
+
+ version (Windows)
+ {
+ assert(absolute(r"\"[]) == true);
+ assert(absolute(r"\\"[]) == true);
+ assert(absolute(r"c:"[]) == true);
+ }
+ }
+
+ /**
+ Return the given name as an absolute path
+
+ Params:
+ name = path
+ base = the absolute base to prefix name with if it is relative
+
+ Returns: name as an absolute path relative to base
+ */
+ extern (C++) static const(char)* toAbsolute(const(char)* name, const(char)* base = null)
+ {
+ const name_ = name.toDString();
+ const base_ = base ? base.toDString() : getcwd(null, 0).toDString();
+ return absolute(name_) ? name : combine(base_, name_).ptr;
+ }
+
+ /********************************
+ * Determine file name extension as slice of input.
+ * Params:
+ * str = file name
+ * Returns:
+ * filename extension (read-only).
+ * Points past '.' of extension.
+ * If there isn't one, return null.
+ */
+ extern (C++) static const(char)* ext(const(char)* str) pure @nogc
+ {
+ return ext(str.toDString).ptr;
+ }
+
+ /// Ditto
+ extern (D) static const(char)[] ext(const(char)[] str) nothrow pure @safe @nogc
+ {
+ foreach_reverse (idx, char e; str)
+ {
+ switch (e)
+ {
+ case '.':
+ return str[idx + 1 .. $];
+ version (Posix)
+ {
+ case '/':
+ return null;
+ }
+ version (Windows)
+ {
+ case '\\':
+ case ':':
+ case '/':
+ return null;
+ }
+ default:
+ continue;
+ }
+ }
+ return null;
+ }
+
+ unittest
+ {
+ assert(ext("/foo/bar/dmd.conf"[]) == "conf");
+ assert(ext("object.o"[]) == "o");
+ assert(ext("/foo/bar/dmd"[]) == null);
+ assert(ext(".objdir.o/object"[]) == null);
+ assert(ext([]) == null);
+ }
+
+ extern (C++) const(char)* ext() const pure @nogc
+ {
+ return ext(str).ptr;
+ }
+
+ /********************************
+ * Return file name without extension.
+ *
+ * TODO:
+ * Once slice are used everywhere and `\0` is not assumed,
+ * this can be turned into a simple slicing.
+ *
+ * Params:
+ * str = file name
+ *
+ * Returns:
+ * mem.xmalloc'd filename with extension removed.
+ */
+ extern (C++) static const(char)* removeExt(const(char)* str)
+ {
+ return removeExt(str.toDString).ptr;
+ }
+
+ /// Ditto
+ extern (D) static const(char)[] removeExt(const(char)[] str)
+ {
+ auto e = ext(str);
+ if (e.length)
+ {
+ const len = (str.length - e.length) - 1; // -1 for the dot
+ char* n = cast(char*)mem.xmalloc(len + 1);
+ memcpy(n, str.ptr, len);
+ n[len] = 0;
+ return n[0 .. len];
+ }
+ return mem.xstrdup(str.ptr)[0 .. str.length];
+ }
+
+ unittest
+ {
+ assert(removeExt("/foo/bar/object.d"[]) == "/foo/bar/object");
+ assert(removeExt("/foo/bar/frontend.di"[]) == "/foo/bar/frontend");
+ }
+
+ /********************************
+ * Return filename name excluding path (read-only).
+ */
+ extern (C++) static const(char)* name(const(char)* str) pure @nogc
+ {
+ return name(str.toDString).ptr;
+ }
+
+ /// Ditto
+ extern (D) static const(char)[] name(const(char)[] str) pure @nogc
+ {
+ foreach_reverse (idx, char e; str)
+ {
+ switch (e)
+ {
+ version (Posix)
+ {
+ case '/':
+ return str[idx + 1 .. $];
+ }
+ version (Windows)
+ {
+ case '/':
+ case '\\':
+ return str[idx + 1 .. $];
+ case ':':
+ /* The ':' is a drive letter only if it is the second
+ * character or the last character,
+ * otherwise it is an ADS (Alternate Data Stream) separator.
+ * Consider ADS separators as part of the file name.
+ */
+ if (idx == 1 || idx == str.length - 1)
+ return str[idx + 1 .. $];
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return str;
+ }
+
+ extern (C++) const(char)* name() const pure @nogc
+ {
+ return name(str).ptr;
+ }
+
+ unittest
+ {
+ assert(name("/foo/bar/object.d"[]) == "object.d");
+ assert(name("/foo/bar/frontend.di"[]) == "frontend.di");
+ }
+
+ /**************************************
+ * Return path portion of str.
+ * returned string is newly allocated
+ * Path does not include trailing path separator.
+ */
+ extern (C++) static const(char)* path(const(char)* str)
+ {
+ return path(str.toDString).ptr;
+ }
+
+ /// Ditto
+ extern (D) static const(char)[] path(const(char)[] str)
+ {
+ const n = name(str);
+ bool hasTrailingSlash;
+ if (n.length < str.length)
+ {
+ if (isDirSeparator(str[$ - n.length - 1]))
+ hasTrailingSlash = true;
+ }
+ const pathlen = str.length - n.length - (hasTrailingSlash ? 1 : 0);
+ char* path = cast(char*)mem.xmalloc(pathlen + 1);
+ memcpy(path, str.ptr, pathlen);
+ path[pathlen] = 0;
+ return path[0 .. pathlen];
+ }
+
+ unittest
+ {
+ assert(path("/foo/bar"[]) == "/foo");
+ assert(path("foo"[]) == "");
+ }
+
+ /**************************************
+ * Replace filename portion of path.
+ */
+ extern (D) static const(char)[] replaceName(const(char)[] path, const(char)[] name)
+ {
+ if (absolute(name))
+ return name;
+ auto n = FileName.name(path);
+ if (n == path)
+ return name;
+ return combine(path[0 .. $ - n.length], name);
+ }
+
+ /**
+ Combine a `path` and a file `name`
+
+ Params:
+ path = Path to append to
+ name = Name to append to path
+
+ Returns:
+ The `\0` terminated string which is the combination of `path` and `name`
+ and a valid path.
+ */
+ extern (C++) static const(char)* combine(const(char)* path, const(char)* name)
+ {
+ if (!path)
+ return name;
+ return combine(path.toDString, name.toDString).ptr;
+ }
+
+ /// Ditto
+ extern(D) static const(char)[] combine(const(char)[] path, const(char)[] name)
+ {
+ return !path.length ? name : buildPath(path, name);
+ }
+
+ unittest
+ {
+ version (Windows)
+ assert(combine("foo"[], "bar"[]) == "foo\\bar");
+ else
+ assert(combine("foo"[], "bar"[]) == "foo/bar");
+ assert(combine("foo/"[], "bar"[]) == "foo/bar");
+ }
+
+ static const(char)[] buildPath(const(char)[][] fragments...)
+ {
+ size_t size;
+ foreach (f; fragments)
+ size += f.length ? f.length + 1 : 0;
+ if (size == 0)
+ size = 1;
+
+ char* p = cast(char*) mem.xmalloc_noscan(size);
+ size_t length;
+ foreach (f; fragments)
+ {
+ if (!f.length)
+ continue;
+
+ p[length .. length + f.length] = f;
+ length += f.length;
+
+ const last = p[length - 1];
+ version (Posix)
+ {
+ if (!isDirSeparator(last))
+ p[length++] = '/';
+ }
+ else version (Windows)
+ {
+ if (!isDirSeparator(last) && last != ':')
+ p[length++] = '\\';
+ }
+ else
+ assert(0);
+ }
+
+ // overwrite last slash with null terminator
+ p[length ? --length : 0] = 0;
+
+ return p[0 .. length];
+ }
+
+ unittest
+ {
+ assert(buildPath() == "");
+ assert(buildPath("foo") == "foo");
+ assert(buildPath("foo", null) == "foo");
+ assert(buildPath(null, "foo") == "foo");
+ version (Windows)
+ assert(buildPath("C:", r"a\", "bb/", "ccc", "d") == r"C:a\bb/ccc\d");
+ else
+ assert(buildPath("a/", "bb", "ccc") == "a/bb/ccc");
+ }
+
+ // Split a path into an Array of paths
+ extern (C++) static Strings* splitPath(const(char)* path)
+ {
+ auto array = new Strings();
+ int sink(const(char)* p) nothrow
+ {
+ array.push(p);
+ return 0;
+ }
+ splitPath(&sink, path);
+ return array;
+ }
+
+ /****
+ * Split path (such as that returned by `getenv("PATH")`) into pieces, each piece is mem.xmalloc'd
+ * Handle double quotes and ~.
+ * Pass the pieces to sink()
+ * Params:
+ * sink = send the path pieces here, end when sink() returns !=0
+ * path = the path to split up.
+ */
+ static void splitPath(int delegate(const(char)*) nothrow sink, const(char)* path)
+ {
+ if (!path)
+ return;
+
+ auto p = path;
+ OutBuffer buf;
+ char c;
+ do
+ {
+ const(char)* home;
+ bool instring = false;
+ while (isspace(*p)) // skip leading whitespace
+ ++p;
+ buf.reserve(8); // guess size of piece
+ for (;; ++p)
+ {
+ c = *p;
+ switch (c)
+ {
+ case '"':
+ instring ^= false; // toggle inside/outside of string
+ continue;
+
+ version (OSX)
+ {
+ case ',':
+ }
+ version (Windows)
+ {
+ case ';':
+ }
+ version (Posix)
+ {
+ case ':':
+ }
+ p++; // ; cannot appear as part of a
+ break; // path, quotes won't protect it
+
+ case 0x1A: // ^Z means end of file
+ case 0:
+ break;
+
+ case '\r':
+ continue; // ignore carriage returns
+
+ version (Posix)
+ {
+ case '~':
+ if (!home)
+ home = getenv("HOME");
+ // Expand ~ only if it is prefixing the rest of the path.
+ if (!buf.length && p[1] == '/' && home)
+ buf.writestring(home);
+ else
+ buf.writeByte('~');
+ continue;
+ }
+
+ version (none)
+ {
+ case ' ':
+ case '\t': // tabs in filenames?
+ if (!instring) // if not in string
+ break; // treat as end of path
+ }
+ default:
+ buf.writeByte(c);
+ continue;
+ }
+ break;
+ }
+ if (buf.length) // if path is not empty
+ {
+ if (sink(buf.extractChars()))
+ break;
+ }
+ } while (c);
+ }
+
+ /**
+ * Add the extension `ext` to `name`, regardless of the content of `name`
+ *
+ * Params:
+ * name = Path to append the extension to
+ * ext = Extension to add (should not include '.')
+ *
+ * Returns:
+ * A newly allocated string (free with `FileName.free`)
+ */
+ extern(D) static char[] addExt(const(char)[] name, const(char)[] ext) pure
+ {
+ const len = name.length + ext.length + 2;
+ auto s = cast(char*)mem.xmalloc(len);
+ s[0 .. name.length] = name[];
+ s[name.length] = '.';
+ s[name.length + 1 .. len - 1] = ext[];
+ s[len - 1] = '\0';
+ return s[0 .. len - 1];
+ }
+
+
+ /***************************
+ * Free returned value with FileName::free()
+ */
+ extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext)
+ {
+ return defaultExt(name.toDString, ext.toDString).ptr;
+ }
+
+ /// Ditto
+ extern (D) static const(char)[] defaultExt(const(char)[] name, const(char)[] ext)
+ {
+ auto e = FileName.ext(name);
+ if (e.length) // it already has an extension
+ return name.xarraydup;
+ return addExt(name, ext);
+ }
+
+ unittest
+ {
+ assert(defaultExt("/foo/object.d"[], "d") == "/foo/object.d");
+ assert(defaultExt("/foo/object"[], "d") == "/foo/object.d");
+ assert(defaultExt("/foo/bar.d"[], "o") == "/foo/bar.d");
+ }
+
+ /***************************
+ * Free returned value with FileName::free()
+ */
+ extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext)
+ {
+ return forceExt(name.toDString, ext.toDString).ptr;
+ }
+
+ /// Ditto
+ extern (D) static const(char)[] forceExt(const(char)[] name, const(char)[] ext)
+ {
+ if (auto e = FileName.ext(name))
+ return addExt(name[0 .. $ - e.length - 1], ext);
+ return defaultExt(name, ext); // doesn't have one
+ }
+
+ unittest
+ {
+ assert(forceExt("/foo/object.d"[], "d") == "/foo/object.d");
+ assert(forceExt("/foo/object"[], "d") == "/foo/object.d");
+ assert(forceExt("/foo/bar.d"[], "o") == "/foo/bar.o");
+ }
+
+ /// Returns:
+ /// `true` if `name`'s extension is `ext`
+ extern (C++) static bool equalsExt(const(char)* name, const(char)* ext) pure @nogc
+ {
+ return equalsExt(name.toDString, ext.toDString);
+ }
+
+ /// Ditto
+ extern (D) static bool equalsExt(const(char)[] name, const(char)[] ext) pure @nogc
+ {
+ auto e = FileName.ext(name);
+ if (!e.length && !ext.length)
+ return true;
+ if (!e.length || !ext.length)
+ return false;
+ return FileName.equals(e, ext);
+ }
+
+ unittest
+ {
+ assert(!equalsExt("foo.bar"[], "d"));
+ assert(equalsExt("foo.bar"[], "bar"));
+ assert(equalsExt("object.d"[], "d"));
+ assert(!equalsExt("object"[], "d"));
+ }
+
+ /******************************
+ * Return !=0 if extensions match.
+ */
+ extern (C++) bool equalsExt(const(char)* ext) const pure @nogc
+ {
+ return equalsExt(str, ext.toDString());
+ }
+
+ /*************************************
+ * Search paths for file.
+ * Params:
+ * path = array of path strings
+ * name = file to look for
+ * cwd = true means search current directory before searching path
+ * Returns:
+ * if found, filename combined with path, otherwise null
+ */
+ extern (C++) static const(char)* searchPath(Strings* path, const(char)* name, bool cwd)
+ {
+ return searchPath(path, name.toDString, cwd).ptr;
+ }
+
+ extern (D) static const(char)[] searchPath(Strings* path, const(char)[] name, bool cwd)
+ {
+ if (absolute(name))
+ {
+ return exists(name) ? name : null;
+ }
+ if (cwd)
+ {
+ if (exists(name))
+ return name;
+ }
+ if (path)
+ {
+ foreach (p; *path)
+ {
+ auto n = combine(p.toDString, name);
+ if (exists(n))
+ return n;
+ //combine might return name
+ if (n.ptr != name.ptr)
+ {
+ mem.xfree(cast(void*)n.ptr);
+ }
+ }
+ }
+ return null;
+ }
+
+ extern (D) static const(char)[] searchPath(const(char)* path, const(char)[] name, bool cwd)
+ {
+ if (absolute(name))
+ {
+ return exists(name) ? name : null;
+ }
+ if (cwd)
+ {
+ if (exists(name))
+ return name;
+ }
+ if (path && *path)
+ {
+ const(char)[] result;
+
+ int sink(const(char)* p) nothrow
+ {
+ auto n = combine(p.toDString, name);
+ mem.xfree(cast(void*)p);
+ if (exists(n))
+ {
+ result = n;
+ return 1; // done with splitPath() call
+ }
+ return 0;
+ }
+
+ splitPath(&sink, path);
+ return result;
+ }
+ return null;
+ }
+
+ /************************************
+ * Determine if path contains reserved character.
+ * Params:
+ * name = path
+ * Returns:
+ * index of the first reserved character in path if found, size_t.max otherwise
+ */
+ extern (D) static size_t findReservedChar(const(char)* name) pure @nogc
+ {
+ version (Windows)
+ {
+ size_t idx = 0;
+ // According to https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
+ // the following characters are not allowed in path: < > : " | ? *
+ for (const(char)* p = name; *p; p++, idx++)
+ {
+ char c = *p;
+ if (c == '<' || c == '>' || c == ':' || c == '"' || c == '|' || c == '?' || c == '*')
+ {
+ return idx;
+ }
+ }
+ return size_t.max;
+ }
+ else
+ {
+ return size_t.max;
+ }
+ }
+ unittest
+ {
+ assert(findReservedChar(r"") == size_t.max);
+ assert(findReservedChar(r" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.-_=+()") == size_t.max);
+
+ version (Windows)
+ {
+ assert(findReservedChar(` < `) == 1);
+ assert(findReservedChar(` >`) == 1);
+ assert(findReservedChar(`: `) == 0);
+ assert(findReservedChar(`"`) == 0);
+ assert(findReservedChar(`|`) == 0);
+ assert(findReservedChar(`?`) == 0);
+ assert(findReservedChar(`*`) == 0);
+ }
+ else
+ {
+ assert(findReservedChar(`<>:"|?*`) == size_t.max);
+ }
+ }
+
+ /************************************
+ * Determine if path has a reference to parent directory.
+ * Params:
+ * name = path
+ * Returns:
+ * true if path contains '..' reference to parent directory
+ */
+ extern (D) static bool refersToParentDir(const(char)* name) pure @nogc
+ {
+ if (name[0] == '.' && name[1] == '.' && (!name[2] || isDirSeparator(name[2])))
+ {
+ return true;
+ }
+
+ for (const(char)* p = name; *p; p++)
+ {
+ char c = *p;
+ if (isDirSeparator(c) && p[1] == '.' && p[2] == '.' && (!p[3] || isDirSeparator(p[3])))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ unittest
+ {
+ assert(!refersToParentDir(r""));
+ assert(!refersToParentDir(r"foo"));
+ assert(!refersToParentDir(r"foo.."));
+ assert(!refersToParentDir(r"foo..boo"));
+ assert(!refersToParentDir(r"foo/..boo"));
+ assert(!refersToParentDir(r"foo../boo"));
+ assert(refersToParentDir(r".."));
+ assert(refersToParentDir(r"../"));
+ assert(refersToParentDir(r"foo/.."));
+ assert(refersToParentDir(r"foo/../"));
+ assert(refersToParentDir(r"foo/../../boo"));
+
+ version (Windows)
+ {
+ // Backslash as directory separator
+ assert(!refersToParentDir(r"foo\..boo"));
+ assert(!refersToParentDir(r"foo..\boo"));
+ assert(refersToParentDir(r"..\"));
+ assert(refersToParentDir(r"foo\.."));
+ assert(refersToParentDir(r"foo\..\"));
+ assert(refersToParentDir(r"foo\..\..\boo"));
+ }
+ }
+
+
+ /**
+ Check if the file the `path` points to exists
+
+ Returns:
+ 0 if it does not exists
+ 1 if it exists and is not a directory
+ 2 if it exists and is a directory
+ */
+ extern (C++) static int exists(const(char)* name)
+ {
+ return exists(name.toDString);
+ }
+
+ /// Ditto
+ extern (D) static int exists(const(char)[] name)
+ {
+ if (!name.length)
+ return 0;
+ version (Posix)
+ {
+ stat_t st;
+ if (name.toCStringThen!((v) => stat(v.ptr, &st)) < 0)
+ return 0;
+ if (S_ISDIR(st.st_mode))
+ return 2;
+ return 1;
+ }
+ else version (Windows)
+ {
+ return name.toWStringzThen!((wname)
+ {
+ const dw = GetFileAttributesW(&wname[0]);
+ if (dw == -1)
+ return 0;
+ else if (dw & FILE_ATTRIBUTE_DIRECTORY)
+ return 2;
+ else
+ return 1;
+ });
+ }
+ else
+ {
+ assert(0);
+ }
+ }
+
+ /**
+ Ensure that the provided path exists
+
+ Accepts a path to either a file or a directory.
+ In the former case, the basepath (path to the containing directory)
+ will be checked for existence, and created if it does not exists.
+ In the later case, the directory pointed to will be checked for existence
+ and created if needed.
+
+ Params:
+ path = a path to a file or a directory
+
+ Returns:
+ `true` if the directory exists or was successfully created
+ */
+ extern (D) static bool ensurePathExists(const(char)[] path)
+ {
+ //printf("FileName::ensurePathExists(%s)\n", path ? path : "");
+ if (!path.length)
+ return true;
+ if (exists(path))
+ return true;
+
+ // We were provided with a file name
+ // We need to call ourselves recursively to ensure parent dir exist
+ const char[] p = FileName.path(path);
+ if (p.length)
+ {
+ version (Windows)
+ {
+ // Note: Windows filename comparison should be case-insensitive,
+ // however p is a subslice of path so we don't need it
+ if (path.length == p.length ||
+ (path.length > 2 && path[1] == ':' && path[2 .. $] == p))
+ {
+ mem.xfree(cast(void*)p.ptr);
+ return true;
+ }
+ }
+ const r = ensurePathExists(p);
+ mem.xfree(cast(void*)p);
+
+ if (!r)
+ return r;
+ }
+
+ version (Windows)
+ const r = _mkdir(path);
+ version (Posix)
+ {
+ errno = 0;
+ const r = path.toCStringThen!((pathCS) => mkdir(pathCS.ptr, (7 << 6) | (7 << 3) | 7));
+ }
+
+ if (r == 0)
+ return true;
+
+ // Don't error out if another instance of dmd just created
+ // this directory
+ version (Windows)
+ {
+ import core.sys.windows.winerror : ERROR_ALREADY_EXISTS;
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
+ return true;
+ }
+ version (Posix)
+ {
+ if (errno == EEXIST)
+ return true;
+ }
+
+ return false;
+ }
+
+ ///ditto
+ extern (C++) static bool ensurePathExists(const(char)* path)
+ {
+ return ensurePathExists(path.toDString);
+ }
+
+ /******************************************
+ * Return canonical version of name.
+ * This code is high risk.
+ */
+ extern (C++) static const(char)* canonicalName(const(char)* name)
+ {
+ return canonicalName(name.toDString).ptr;
+ }
+
+ /// Ditto
+ extern (D) static const(char)[] canonicalName(const(char)[] name)
+ {
+ version (Posix)
+ {
+ import core.stdc.limits; // PATH_MAX
+ import core.sys.posix.unistd; // _PC_PATH_MAX
+
+ // Older versions of druntime don't have PATH_MAX defined.
+ // i.e: dmd __VERSION__ < 2085, gdc __VERSION__ < 2076.
+ static if (!__traits(compiles, PATH_MAX))
+ {
+ version (DragonFlyBSD)
+ enum PATH_MAX = 1024;
+ else version (FreeBSD)
+ enum PATH_MAX = 1024;
+ else version (linux)
+ enum PATH_MAX = 4096;
+ else version (NetBSD)
+ enum PATH_MAX = 1024;
+ else version (OpenBSD)
+ enum PATH_MAX = 1024;
+ else version (OSX)
+ enum PATH_MAX = 1024;
+ else version (Solaris)
+ enum PATH_MAX = 1024;
+ }
+
+ // Have realpath(), passing a NULL destination pointer may return an
+ // internally malloc'd buffer, however it is implementation defined
+ // as to what happens, so cannot rely on it.
+ static if (__traits(compiles, PATH_MAX))
+ {
+ // Have compile time limit on filesystem path, use it with realpath.
+ char[PATH_MAX] buf = void;
+ auto path = name.toCStringThen!((n) => realpath(n.ptr, buf.ptr));
+ if (path !is null)
+ return xarraydup(path.toDString);
+ }
+ else static if (__traits(compiles, canonicalize_file_name))
+ {
+ // Have canonicalize_file_name, which malloc's memory.
+ // We need a dmd.root.rmem allocation though.
+ auto path = name.toCStringThen!((n) => canonicalize_file_name(n.ptr));
+ scope(exit) .free(path.ptr);
+ if (path !is null)
+ return xarraydup(path.toDString);
+ }
+ else static if (__traits(compiles, _PC_PATH_MAX))
+ {
+ // Panic! Query the OS for the buffer limit.
+ auto path_max = pathconf("/", _PC_PATH_MAX);
+ if (path_max > 0)
+ {
+ char *buf = cast(char*)mem.xmalloc(path_max);
+ scope(exit) mem.xfree(buf);
+ auto path = name.toCStringThen!((n) => realpath(n.ptr, buf));
+ if (path !is null)
+ return xarraydup(path.toDString);
+ }
+ }
+ // Give up trying to support this platform, just duplicate the filename
+ // unless there is nothing to copy from.
+ if (!name.length)
+ return null;
+ return xarraydup(name);
+ }
+ else version (Windows)
+ {
+ // Convert to wstring first since otherwise the Win32 APIs have a character limit
+ return name.toWStringzThen!((wname)
+ {
+ /* Apparently, there is no good way to do this on Windows.
+ * GetFullPathName isn't it, but use it anyway.
+ */
+ // First find out how long the buffer has to be, incl. terminating null.
+ const capacity = GetFullPathNameW(&wname[0], 0, null, null);
+ if (!capacity) return null;
+ auto buffer = cast(wchar*) mem.xmalloc_noscan(capacity * wchar.sizeof);
+ scope(exit) mem.xfree(buffer);
+
+ // Actually get the full path name. If the buffer is large enough,
+ // the returned length does NOT include the terminating null...
+ const length = GetFullPathNameW(&wname[0], capacity, buffer, null /*filePart*/);
+ assert(length == capacity - 1);
+
+ return toNarrowStringz(buffer[0 .. length]);
+ });
+ }
+ else
+ {
+ assert(0);
+ }
+ }
+
+ unittest
+ {
+ string filename = "foo.bar";
+ const path = canonicalName(filename);
+ scope(exit) free(path.ptr);
+ assert(path.length >= filename.length);
+ assert(path[$ - filename.length .. $] == filename);
+ }
+
+ /********************************
+ * Free memory allocated by FileName routines
+ */
+ extern (C++) static void free(const(char)* str) pure
+ {
+ if (str)
+ {
+ assert(str[0] != cast(char)0xAB);
+ memset(cast(void*)str, 0xAB, strlen(str) + 1); // stomp
+ }
+ mem.xfree(cast(void*)str);
+ }
+
+ extern (C++) const(char)* toChars() const pure nothrow @nogc @trusted
+ {
+ // Since we can return an empty slice (but '\0' terminated),
+ // we don't do bounds check (as `&str[0]` does)
+ return str.ptr;
+ }
+
+ const(char)[] toString() const pure nothrow @nogc @trusted
+ {
+ return str;
+ }
+
+ bool opCast(T)() const pure nothrow @nogc @safe
+ if (is(T == bool))
+ {
+ return str.ptr !is null;
+ }
+}
+
+version(Windows)
+{
+ /****************************************************************
+ * The code before used the POSIX function `mkdir` on Windows. That
+ * function is now deprecated and fails with long paths, so instead
+ * we use the newer `CreateDirectoryW`.
+ *
+ * `CreateDirectoryW` is the unicode version of the generic macro
+ * `CreateDirectory`. `CreateDirectoryA` has a file path
+ * limitation of 248 characters, `mkdir` fails with less and might
+ * fail due to the number of consecutive `..`s in the
+ * path. `CreateDirectoryW` also normally has a 248 character
+ * limit, unless the path is absolute and starts with `\\?\`. Note
+ * that this is different from starting with the almost identical
+ * `\\?`.
+ *
+ * Params:
+ * path = The path to create.
+ *
+ * Returns:
+ * 0 on success, 1 on failure.
+ *
+ * References:
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
+ */
+ private int _mkdir(const(char)[] path) nothrow
+ {
+ const createRet = path.extendedPathThen!(
+ p => CreateDirectoryW(&p[0], null /*securityAttributes*/));
+ // different conventions for CreateDirectory and mkdir
+ return createRet == 0 ? 1 : 0;
+ }
+
+ /**************************************
+ * Converts a path to one suitable to be passed to Win32 API
+ * functions that can deal with paths longer than 248
+ * characters then calls the supplied function on it.
+ *
+ * Params:
+ * path = The Path to call F on.
+ *
+ * Returns:
+ * The result of calling F on path.
+ *
+ * References:
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+ */
+ package auto extendedPathThen(alias F)(const(char)[] path)
+ {
+ if (!path.length)
+ return F((wchar[]).init);
+ return path.toWStringzThen!((wpath)
+ {
+ // GetFullPathNameW expects a sized buffer to store the result in. Since we don't
+ // know how large it has to be, we pass in null and get the needed buffer length
+ // as the return code.
+ const pathLength = GetFullPathNameW(&wpath[0],
+ 0 /*length8*/,
+ null /*output buffer*/,
+ null /*filePartBuffer*/);
+ if (pathLength == 0)
+ {
+ return F((wchar[]).init);
+ }
+
+ // wpath is the UTF16 version of path, but to be able to use
+ // extended paths, we need to prefix with `\\?\` and the absolute
+ // path.
+ static immutable prefix = `\\?\`w;
+
+ // prefix only needed for long names and non-UNC names
+ const needsPrefix = pathLength >= MAX_PATH && (wpath[0] != '\\' || wpath[1] != '\\');
+ const prefixLength = needsPrefix ? prefix.length : 0;
+
+ // +1 for the null terminator
+ const bufferLength = pathLength + prefixLength + 1;
+
+ wchar[1024] absBuf = void;
+ wchar[] absPath = bufferLength > absBuf.length
+ ? new wchar[bufferLength] : absBuf[0 .. bufferLength];
+
+ absPath[0 .. prefixLength] = prefix[0 .. prefixLength];
+
+ const absPathRet = GetFullPathNameW(&wpath[0],
+ cast(uint)(absPath.length - prefixLength - 1),
+ &absPath[prefixLength],
+ null /*filePartBuffer*/);
+
+ if (absPathRet == 0 || absPathRet > absPath.length - prefixLength)
+ {
+ return F((wchar[]).init);
+ }
+
+ absPath[$ - 1] = '\0';
+ // Strip null terminator from the slice
+ return F(absPath[0 .. $ - 1]);
+ });
+ }
+
+ /**********************************
+ * Converts a UTF-16 string to a (null-terminated) narrow string.
+ * Returns:
+ * If `buffer` is specified and the result fits, a slice of that buffer,
+ * otherwise a new buffer which can be released via `mem.xfree()`.
+ * Nulls are propagated, i.e., if `wide` is null, the returned slice is
+ * null too.
+ */
+ char[] toNarrowStringz(const(wchar)[] wide, char[] buffer = null) nothrow
+ {
+ if (wide is null)
+ return null;
+
+ const requiredLength = WideCharToMultiByte(CodePage, 0, wide.ptr, cast(int) wide.length, buffer.ptr, cast(int) buffer.length, null, null);
+ if (requiredLength < buffer.length)
+ {
+ buffer[requiredLength] = 0;
+ return buffer[0 .. requiredLength];
+ }
+
+ char* newBuffer = cast(char*) mem.xmalloc_noscan(requiredLength + 1);
+ const length = WideCharToMultiByte(CodePage, 0, wide.ptr, cast(int) wide.length, newBuffer, requiredLength, null, null);
+ assert(length == requiredLength);
+ newBuffer[length] = 0;
+ return newBuffer[0 .. length];
+ }
+
+ /**********************************
+ * Converts a narrow string to a (null-terminated) UTF-16 string.
+ * Returns:
+ * If `buffer` is specified and the result fits, a slice of that buffer,
+ * otherwise a new buffer which can be released via `mem.xfree()`.
+ * Nulls are propagated, i.e., if `narrow` is null, the returned slice is
+ * null too.
+ */
+ wchar[] toWStringz(const(char)[] narrow, wchar[] buffer = null) nothrow
+ {
+ if (narrow is null)
+ return null;
+
+ const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
+ if (requiredLength < buffer.length)
+ {
+ buffer[requiredLength] = 0;
+ return buffer[0 .. requiredLength];
+ }
+
+ wchar* newBuffer = cast(wchar*) mem.xmalloc_noscan((requiredLength + 1) * wchar.sizeof);
+ const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, newBuffer, requiredLength);
+ assert(length == requiredLength);
+ newBuffer[length] = 0;
+ return newBuffer[0 .. length];
+ }
+
+ /**********************************
+ * Converts a slice of UTF-8 characters to an array of wchar that's null
+ * terminated so it can be passed to Win32 APIs then calls the supplied
+ * function on it.
+ *
+ * Params:
+ * str = The string to convert.
+ *
+ * Returns:
+ * The result of calling F on the UTF16 version of str.
+ */
+ private auto toWStringzThen(alias F)(const(char)[] str) nothrow
+ {
+ if (!str.length) return F(""w.ptr);
+
+ wchar[1024] buf = void;
+ wchar[] wide = toWStringz(str, buf);
+ scope(exit) wide.ptr != buf.ptr && mem.xfree(wide.ptr);
+
+ return F(wide);
+ }
+}
diff --git a/gcc/d/dmd/root/filename.h b/gcc/d/dmd/root/filename.h
index 52cd963d708..9f773b5bb99 100644
--- a/gcc/d/dmd/root/filename.h
+++ b/gcc/d/dmd/root/filename.h
@@ -1,5 +1,6 @@
/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
@@ -9,20 +10,16 @@
#pragma once
#include "array.h"
+#include "dcompat.h"
-class RootObject;
-
-template <typename TYPE> struct Array;
typedef Array<const char *> Strings;
struct FileName
{
- const char *str;
- FileName(const char *str);
- bool equals(RootObject *obj);
+private:
+ DString str;
+public:
static bool equals(const char *name1, const char *name2);
- int compare(RootObject *obj);
- static int compare(const char *name1, const char *name2);
static bool absolute(const char *name);
static const char *toAbsolute(const char *name, const char *base = NULL);
static const char *ext(const char *);
@@ -31,7 +28,6 @@ struct FileName
static const char *name(const char *);
const char *name();
static const char *path(const char *);
- static const char *replaceName(const char *path, const char *name);
static const char *combine(const char *path, const char *name);
static Strings *splitPath(const char *path);
@@ -42,7 +38,6 @@ struct FileName
bool equalsExt(const char *ext);
static const char *searchPath(Strings *path, const char *name, bool cwd);
- static const char *safeSearchPath(Strings *path, const char *name);
static int exists(const char *name);
static bool ensurePathExists(const char *path);
static const char *canonicalName(const char *name);
diff --git a/gcc/d/dmd/root/hash.d b/gcc/d/dmd/root/hash.d
new file mode 100644
index 00000000000..f484819be55
--- /dev/null
+++ b/gcc/d/dmd/root/hash.d
@@ -0,0 +1,83 @@
+/**
+ * Hash functions for arbitrary binary data.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Martin Nowak, Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/hash.d, root/_hash.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_hash.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/hash.d
+ */
+
+module dmd.root.hash;
+
+// MurmurHash2 was written by Austin Appleby, and is placed in the public
+// domain. The author hereby disclaims copyright to this source code.
+// https://sites.google.com/site/murmurhash/
+uint calcHash(scope const(char)[] data) @nogc nothrow pure @safe
+{
+ return calcHash(cast(const(ubyte)[])data);
+}
+
+/// ditto
+uint calcHash(scope const(ubyte)[] data) @nogc nothrow pure @safe
+{
+ // 'm' and 'r' are mixing constants generated offline.
+ // They're not really 'magic', they just happen to work well.
+ enum uint m = 0x5bd1e995;
+ enum int r = 24;
+ // Initialize the hash to a 'random' value
+ uint h = cast(uint) data.length;
+ // Mix 4 bytes at a time into the hash
+ while (data.length >= 4)
+ {
+ uint k = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
+ k *= m;
+ k ^= k >> r;
+ h = (h * m) ^ (k * m);
+ data = data[4..$];
+ }
+ // Handle the last few bytes of the input array
+ switch (data.length & 3)
+ {
+ case 3:
+ h ^= data[2] << 16;
+ goto case;
+ case 2:
+ h ^= data[1] << 8;
+ goto case;
+ case 1:
+ h ^= data[0];
+ h *= m;
+ goto default;
+ default:
+ break;
+ }
+ // Do a few final mixes of the hash to ensure the last few
+ // bytes are well-incorporated.
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+ return h;
+}
+
+unittest
+{
+ char[10] data = "0123456789";
+ assert(calcHash(data[0..$]) == 439_272_720);
+ assert(calcHash(data[1..$]) == 3_704_291_687);
+ assert(calcHash(data[2..$]) == 2_125_368_748);
+ assert(calcHash(data[3..$]) == 3_631_432_225);
+}
+
+// combine and mix two words (boost::hash_combine)
+size_t mixHash(size_t h, size_t k) @nogc nothrow pure @safe
+{
+ return h ^ (k + 0x9e3779b9 + (h << 6) + (h >> 2));
+}
+
+unittest
+{
+ // & uint.max because mixHash output is truncated on 32-bit targets
+ assert((mixHash(0xDE00_1540, 0xF571_1A47) & uint.max) == 0x952D_FC10);
+}
diff --git a/gcc/d/dmd/root/longdouble.d b/gcc/d/dmd/root/longdouble.d
new file mode 100644
index 00000000000..a845c9768e8
--- /dev/null
+++ b/gcc/d/dmd/root/longdouble.d
@@ -0,0 +1,140 @@
+/**
+ * 80-bit floating point value implementation if the C/D compiler does not support them natively.
+ * Copyright (C) 2021 Free Software Foundation, Inc.
+ *
+ * GCC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GCC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GCC; see the file COPYING3. If not see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+module dmd.root.longdouble;
+
+import core.stdc.config;
+import core.stdc.stdint;
+
+extern(C++):
+nothrow:
+@nogc:
+
+// Type used by the front-end for compile-time reals
+struct longdouble
+{
+ extern (D) this(T)(T r)
+ {
+ this.set(cast(SetType!T)r);
+ }
+
+ // No constructor to be able to use this class in a union.
+ extern (D) longdouble opAssign(T)(T r)
+ if (is (T : longdouble))
+ {
+ this.realvalue = r.realvalue;
+ return this;
+ }
+
+ extern (D) longdouble opAssign(T)(T r)
+ if (!is (T : longdouble))
+ {
+ this.set(cast(SetType!T)r);
+ return this;
+ }
+
+ // Arithmetic operators.
+ extern (D) longdouble opBinary(string op, T)(T r) const
+ if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "%")
+ && is (T : longdouble))
+ {
+ static if (op == "+")
+ return this.add(r);
+ else static if (op == "-")
+ return this.sub(r);
+ else static if (op == "*")
+ return this.mul(r);
+ else static if (op == "/")
+ return this.div(r);
+ else static if (op == "%")
+ return this.mod(r);
+ }
+
+ extern (D) longdouble opUnary(string op)() const
+ if (op == "-")
+ {
+ return this.neg();
+ }
+
+ extern (D) int opCmp(longdouble r) const
+ {
+ return this.cmp(r);
+ }
+
+ extern (D) int opEquals(longdouble r) const
+ {
+ return this.equals(r);
+ }
+
+ extern (D) bool opCast(T : bool)() const
+ {
+ return this.to_bool();
+ }
+
+ extern (D) T opCast(T)() const
+ {
+ static if (__traits(isUnsigned, T))
+ return cast (T) this.to_uint();
+ else
+ return cast(T) this.to_int();
+ }
+
+ void set(int8_t d);
+ void set(int16_t d);
+ void set(int32_t d);
+ void set(int64_t d);
+ void set(uint8_t d);
+ void set(uint16_t d);
+ void set(uint32_t d);
+ void set(uint64_t d);
+ void set(bool d);
+
+ int64_t to_int() const;
+ uint64_t to_uint() const;
+ bool to_bool() const;
+
+ longdouble add(const ref longdouble r) const;
+ longdouble sub(const ref longdouble r) const;
+ longdouble mul(const ref longdouble r) const;
+ longdouble div(const ref longdouble r) const;
+ longdouble mod(const ref longdouble r) const;
+ longdouble neg() const;
+ int cmp(const ref longdouble t) const;
+ int equals(const ref longdouble t) const;
+
+private:
+ // Statically allocate enough space for REAL_VALUE_TYPE.
+ enum REALVALUE_SIZE = (2 + (16 + c_long.sizeof) / c_long.sizeof);
+ c_long [REALVALUE_SIZE] realvalue;
+}
+
+// Pick the corresponding (u)int64_t type for T, as int64_t may be
+// a special enum that requires casting to explicitly.
+private template SetType(T)
+{
+ static if (__traits(isIntegral, T) && T.sizeof == 8)
+ {
+ static if (__traits(isUnsigned, T))
+ alias SetType = uint64_t;
+ else
+ alias SetType = int64_t;
+ }
+ else
+ alias SetType = T;
+}
diff --git a/gcc/d/dmd/root/object.h b/gcc/d/dmd/root/object.h
index d5e3b2b090d..fb367bc1649 100644
--- a/gcc/d/dmd/root/object.h
+++ b/gcc/d/dmd/root/object.h
@@ -1,14 +1,16 @@
/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- * https://github.com/dlang/dmd/blob/master/src/root/object.h
+ * http://www.boost.org/LICENSE_1_0.txt
+ * https://github.com/dlang/dmd/blob/master/src/dmd/root/object.h
*/
#pragma once
#include "dsystem.h"
+#include "dcompat.h"
typedef size_t hash_t;
@@ -23,7 +25,8 @@ enum DYNCAST
DYNCAST_IDENTIFIER,
DYNCAST_TUPLE,
DYNCAST_PARAMETER,
- DYNCAST_STATEMENT
+ DYNCAST_STATEMENT,
+ DYNCAST_TEMPLATEPARAMETER
};
/*
@@ -34,25 +37,19 @@ class RootObject
public:
RootObject() { }
- virtual bool equals(RootObject *o);
-
- /**
- * Return <0, ==0, or >0 if this is less than, equal to, or greater than obj.
- * Useful for sorting Objects.
- */
- virtual int compare(RootObject *obj);
+ virtual bool equals(const RootObject *o) const;
/**
* Pretty-print an Object. Useful for debugging the old-fashioned way.
*/
- virtual void print();
-
- virtual const char *toChars();
- virtual void toBuffer(OutBuffer *buf);
+ virtual const char *toChars() const;
+ /// This function is `extern(D)` and should not be called from C++,
+ /// as the ABI does not match on some platforms
+ virtual DString toString();
/**
* Used as a replacement for dynamic_cast. Returns a unique number
* defined by the library user. For Object, the return value is 0.
*/
- virtual int dyncast() const;
+ virtual DYNCAST dyncast() const;
};
diff --git a/gcc/d/dmd/root/outbuffer.c b/gcc/d/dmd/root/outbuffer.c
deleted file mode 100644
index 7fbbfe57db3..00000000000
--- a/gcc/d/dmd/root/outbuffer.c
+++ /dev/null
@@ -1,417 +0,0 @@
-
-/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- * https://github.com/D-Programming-Language/dmd/blob/master/src/root/outbuffer.c
- */
-
-#include "dsystem.h"
-#include "outbuffer.h"
-#include "object.h"
-
-char *OutBuffer::extractData()
-{
- char *p;
-
- p = (char *)data.ptr;
- data = DArray<unsigned char>();
- offset = 0;
- return p;
-}
-
-void OutBuffer::reserve(size_t nbytes)
-{
- //printf("OutBuffer::reserve: size = %d, offset = %d, nbytes = %d\n", data.length, offset, nbytes);
- if (data.length - offset < nbytes)
- {
- data.length = (offset + nbytes) * 2;
- data.length = (data.length + 15) & ~15;
- data.ptr = (unsigned char *)mem.xrealloc(data.ptr, data.length);
- }
-}
-
-void OutBuffer::reset()
-{
- offset = 0;
-}
-
-void OutBuffer::setsize(size_t size)
-{
- offset = size;
-}
-
-void OutBuffer::write(const void *data, size_t nbytes)
-{
- if (doindent && !notlinehead)
- {
- if (level)
- {
- reserve(level);
- for (int i = 0; i < level; i++)
- {
- this->data.ptr[offset] = '\t';
- offset++;
- }
- }
- notlinehead = 1;
- }
- reserve(nbytes);
- memcpy(this->data.ptr + offset, data, nbytes);
- offset += nbytes;
-}
-
-void OutBuffer::writestring(const char *string)
-{
- write(string,strlen(string));
-}
-
-void OutBuffer::prependstring(const char *string)
-{
- size_t len = strlen(string);
- reserve(len);
- memmove(data.ptr + len, data.ptr, offset);
- memcpy(data.ptr, string, len);
- offset += len;
-}
-
-void OutBuffer::writenl()
-{
-#if _WIN32
- writeword(0x0A0D); // newline is CR,LF on Microsoft OS's
-#else
- writeByte('\n');
-#endif
- if (doindent)
- notlinehead = 0;
-}
-
-void OutBuffer::writeByte(unsigned b)
-{
- if (doindent && !notlinehead
- && b != '\n')
- {
- if (level)
- {
- reserve(level);
- for (int i = 0; i < level; i++)
- {
- this->data.ptr[offset] = '\t';
- offset++;
- }
- }
- notlinehead = 1;
- }
- reserve(1);
- this->data.ptr[offset] = (unsigned char)b;
- offset++;
-}
-
-void OutBuffer::writeUTF8(unsigned b)
-{
- reserve(6);
- if (b <= 0x7F)
- {
- this->data.ptr[offset] = (unsigned char)b;
- offset++;
- }
- else if (b <= 0x7FF)
- {
- this->data.ptr[offset + 0] = (unsigned char)((b >> 6) | 0xC0);
- this->data.ptr[offset + 1] = (unsigned char)((b & 0x3F) | 0x80);
- offset += 2;
- }
- else if (b <= 0xFFFF)
- {
- this->data.ptr[offset + 0] = (unsigned char)((b >> 12) | 0xE0);
- this->data.ptr[offset + 1] = (unsigned char)(((b >> 6) & 0x3F) | 0x80);
- this->data.ptr[offset + 2] = (unsigned char)((b & 0x3F) | 0x80);
- offset += 3;
- }
- else if (b <= 0x1FFFFF)
- {
- this->data.ptr[offset + 0] = (unsigned char)((b >> 18) | 0xF0);
- this->data.ptr[offset + 1] = (unsigned char)(((b >> 12) & 0x3F) | 0x80);
- this->data.ptr[offset + 2] = (unsigned char)(((b >> 6) & 0x3F) | 0x80);
- this->data.ptr[offset + 3] = (unsigned char)((b & 0x3F) | 0x80);
- offset += 4;
- }
- else if (b <= 0x3FFFFFF)
- {
- this->data.ptr[offset + 0] = (unsigned char)((b >> 24) | 0xF8);
- this->data.ptr[offset + 1] = (unsigned char)(((b >> 18) & 0x3F) | 0x80);
- this->data.ptr[offset + 2] = (unsigned char)(((b >> 12) & 0x3F) | 0x80);
- this->data.ptr[offset + 3] = (unsigned char)(((b >> 6) & 0x3F) | 0x80);
- this->data.ptr[offset + 4] = (unsigned char)((b & 0x3F) | 0x80);
- offset += 5;
- }
- else if (b <= 0x7FFFFFFF)
- {
- this->data.ptr[offset + 0] = (unsigned char)((b >> 30) | 0xFC);
- this->data.ptr[offset + 1] = (unsigned char)(((b >> 24) & 0x3F) | 0x80);
- this->data.ptr[offset + 2] = (unsigned char)(((b >> 18) & 0x3F) | 0x80);
- this->data.ptr[offset + 3] = (unsigned char)(((b >> 12) & 0x3F) | 0x80);
- this->data.ptr[offset + 4] = (unsigned char)(((b >> 6) & 0x3F) | 0x80);
- this->data.ptr[offset + 5] = (unsigned char)((b & 0x3F) | 0x80);
- offset += 6;
- }
- else
- assert(0);
-}
-
-void OutBuffer::prependbyte(unsigned b)
-{
- reserve(1);
- memmove(data.ptr + 1, data.ptr, offset);
- data.ptr[0] = (unsigned char)b;
- offset++;
-}
-
-void OutBuffer::writewchar(unsigned w)
-{
-#if _WIN32
- writeword(w);
-#else
- write4(w);
-#endif
-}
-
-void OutBuffer::writeword(unsigned w)
-{
-#if _WIN32
- unsigned newline = 0x0A0D;
-#else
- unsigned newline = '\n';
-#endif
- if (doindent && !notlinehead
- && w != newline)
- {
- if (level)
- {
- reserve(level);
- for (int i = 0; i < level; i++)
- {
- this->data.ptr[offset] = '\t';
- offset++;
- }
- }
- notlinehead = 1;
- }
- reserve(2);
- *(unsigned short *)(this->data.ptr + offset) = (unsigned short)w;
- offset += 2;
-}
-
-void OutBuffer::writeUTF16(unsigned w)
-{
- reserve(4);
- if (w <= 0xFFFF)
- {
- *(unsigned short *)(this->data.ptr + offset) = (unsigned short)w;
- offset += 2;
- }
- else if (w <= 0x10FFFF)
- {
- *(unsigned short *)(this->data.ptr + offset) = (unsigned short)((w >> 10) + 0xD7C0);
- *(unsigned short *)(this->data.ptr + offset + 2) = (unsigned short)((w & 0x3FF) | 0xDC00);
- offset += 4;
- }
- else
- assert(0);
-}
-
-void OutBuffer::write4(unsigned w)
-{
-#if _WIN32
- bool notnewline = w != 0x000A000D;
-#else
- bool notnewline = true;
-#endif
- if (doindent && !notlinehead && notnewline)
- {
- if (level)
- {
- reserve(level);
- for (int i = 0; i < level; i++)
- {
- this->data.ptr[offset] = '\t';
- offset++;
- }
- }
- notlinehead = 1;
- }
- reserve(4);
- *(unsigned *)(this->data.ptr + offset) = w;
- offset += 4;
-}
-
-void OutBuffer::write(OutBuffer *buf)
-{
- if (buf)
- { reserve(buf->offset);
- memcpy(data.ptr + offset, buf->data.ptr, buf->offset);
- offset += buf->offset;
- }
-}
-
-void OutBuffer::write(RootObject *obj)
-{
- if (obj)
- {
- writestring(obj->toChars());
- }
-}
-
-void OutBuffer::fill0(size_t nbytes)
-{
- reserve(nbytes);
- memset(data.ptr + offset,0,nbytes);
- offset += nbytes;
-}
-
-void OutBuffer::vprintf(const char *format, va_list args)
-{
- int count;
-
- if (doindent)
- write(NULL, 0); // perform indent
- int psize = 128;
- for (;;)
- {
- reserve(psize);
-#if _WIN32
- count = _vsnprintf((char *)data.ptr + offset,psize,format,args);
- if (count != -1)
- break;
- psize *= 2;
-#elif POSIX
- va_list va;
- va_copy(va, args);
-/*
- The functions vprintf(), vfprintf(), vsprintf(), vsnprintf()
- are equivalent to the functions printf(), fprintf(), sprintf(),
- snprintf(), respectively, except that they are called with a
- va_list instead of a variable number of arguments. These
- functions do not call the va_end macro. Consequently, the value
- of ap is undefined after the call. The application should call
- va_end(ap) itself afterwards.
- */
- count = vsnprintf((char *)data.ptr + offset,psize,format,va);
- va_end(va);
- if (count == -1)
- psize *= 2;
- else if (count >= psize)
- psize = count + 1;
- else
- break;
-#else
- assert(0);
-#endif
- }
- offset += count;
-}
-
-void OutBuffer::printf(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- vprintf(format,ap);
- va_end(ap);
-}
-
-/**************************************
- * Convert `u` to a string and append it to the buffer.
- * Params:
- * u = integral value to append
- */
-void OutBuffer::print(unsigned long long u)
-{
- unsigned long long value = u;
- char buf[20];
- const unsigned radix = 10;
-
- size_t i = sizeof(buf);
- do
- {
- if (value < radix)
- {
- unsigned char x = (unsigned char)value;
- buf[--i] = (char)(x + '0');
- break;
- }
- else
- {
- unsigned char x = (unsigned char)(value % radix);
- value = value / radix;
- buf[--i] = (char)(x + '0');
- }
- } while (value);
-
- write(buf + i, sizeof(buf) - i);
-}
-
-void OutBuffer::bracket(char left, char right)
-{
- reserve(2);
- memmove(data.ptr + 1, data.ptr, offset);
- data.ptr[0] = left;
- data.ptr[offset + 1] = right;
- offset += 2;
-}
-
-/******************
- * Insert left at i, and right at j.
- * Return index just past right.
- */
-
-size_t OutBuffer::bracket(size_t i, const char *left, size_t j, const char *right)
-{
- size_t leftlen = strlen(left);
- size_t rightlen = strlen(right);
- reserve(leftlen + rightlen);
- insert(i, left, leftlen);
- insert(j + leftlen, right, rightlen);
- return j + leftlen + rightlen;
-}
-
-void OutBuffer::spread(size_t offset, size_t nbytes)
-{
- reserve(nbytes);
- memmove(data.ptr + offset + nbytes, data.ptr + offset,
- this->offset - offset);
- this->offset += nbytes;
-}
-
-/****************************************
- * Returns: offset + nbytes
- */
-
-size_t OutBuffer::insert(size_t offset, const void *p, size_t nbytes)
-{
- spread(offset, nbytes);
- memmove(data.ptr + offset, p, nbytes);
- return offset + nbytes;
-}
-
-void OutBuffer::remove(size_t offset, size_t nbytes)
-{
- memmove(data.ptr + offset, data.ptr + offset + nbytes, this->offset - (offset + nbytes));
- this->offset -= nbytes;
-}
-
-char *OutBuffer::peekChars()
-{
- if (!offset || data.ptr[offset-1] != '\0')
- {
- writeByte(0);
- offset--; // allow appending more
- }
- return (char *)data.ptr;
-}
-
-char *OutBuffer::extractChars()
-{
- if (!offset || data.ptr[offset-1] != '\0')
- writeByte(0);
- return extractData();
-}
diff --git a/gcc/d/dmd/root/outbuffer.d b/gcc/d/dmd/root/outbuffer.d
new file mode 100644
index 00000000000..e756917b88d
--- /dev/null
+++ b/gcc/d/dmd/root/outbuffer.d
@@ -0,0 +1,720 @@
+/**
+ * An expandable buffer in which you can write text or binary data.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/outbuffer.d, root/_outbuffer.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_outbuffer.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/outbuffer.d
+ */
+
+module dmd.root.outbuffer;
+
+import core.stdc.stdarg;
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.root.string;
+
+debug
+{
+ debug = stomp; // flush out dangling pointer problems by stomping on unused memory
+}
+
+/**
+`OutBuffer` is a write-only output stream of untyped data. It is backed up by
+a contiguous array or a memory-mapped file.
+*/
+struct OutBuffer
+{
+ import dmd.root.file : FileMapping;
+
+ // IMPORTANT: PLEASE KEEP STATE AND DESTRUCTOR IN SYNC WITH DEFINITION IN ./outbuffer.h.
+ // state {
+ private ubyte[] data;
+ private size_t offset;
+ private bool notlinehead;
+ /// File mapping, if any. Use a pointer for ABI compatibility with the C++ counterpart.
+ /// If the pointer is non-null the store is a memory-mapped file, otherwise the store is RAM.
+ private FileMapping!ubyte* fileMapping;
+ /// Whether to indent
+ bool doindent;
+ /// Whether to indent by 4 spaces or by tabs;
+ bool spaces;
+ /// Current indent level
+ int level;
+ // state }
+
+ /**
+ Construct from filename. Will map the file into memory (or create it anew
+ if necessary) and start writing at the beginning of it.
+
+ Params:
+ filename = zero-terminated name of file to map into memory
+ */
+ @trusted this(const(char)* filename)
+ {
+ fileMapping = new FileMapping!ubyte(filename);
+ data = (*fileMapping)[];
+ }
+
+ /**
+ Frees resources associated automatically.
+ */
+ extern (C++) ~this() pure nothrow
+ {
+ if (fileMapping)
+ {
+ if (fileMapping.active)
+ fileMapping.close();
+ fileMapping = null;
+ }
+ else
+ {
+ debug (stomp) memset(data.ptr, 0xFF, data.length);
+ mem.xfree(data.ptr);
+ }
+ }
+
+ extern (C++) size_t length() const pure @nogc @safe nothrow { return offset; }
+
+ /**********************
+ * Transfer ownership of the allocated data to the caller.
+ * Returns:
+ * pointer to the allocated data
+ */
+ extern (C++) char* extractData() pure nothrow @nogc @trusted
+ {
+ char* p = cast(char*)data.ptr;
+ data = null;
+ offset = 0;
+ return p;
+ }
+
+ /**
+ Releases all resources associated with `this` and resets it as an empty
+ memory buffer. The config variables `notlinehead`, `doindent` etc. are
+ not changed.
+ */
+ extern (C++) void destroy() pure nothrow @trusted
+ {
+ if (fileMapping && fileMapping.active)
+ {
+ fileMapping.close();
+ data = null;
+ offset = 0;
+ }
+ else
+ {
+ debug (stomp) memset(data.ptr, 0xFF, data.length);
+ mem.xfree(extractData());
+ }
+ }
+
+ /**
+ Reserves `nbytes` bytes of additional memory (or file space) in advance.
+ The resulting capacity is at least the previous length plus `nbytes`.
+
+ Params:
+ nbytes = the number of additional bytes to reserve
+ */
+ extern (C++) void reserve(size_t nbytes) pure nothrow
+ {
+ //debug (stomp) printf("OutBuffer::reserve: size = %lld, offset = %lld, nbytes = %lld\n", data.length, offset, nbytes);
+ const minSize = offset + nbytes;
+ if (data.length >= minSize)
+ return;
+
+ /* Increase by factor of 1.5; round up to 16 bytes.
+ * The odd formulation is so it will map onto single x86 LEA instruction.
+ */
+ const size = ((minSize * 3 + 30) / 2) & ~15;
+
+ if (fileMapping && fileMapping.active)
+ {
+ fileMapping.resize(size);
+ data = (*fileMapping)[];
+ }
+ else
+ {
+ debug (stomp)
+ {
+ auto p = cast(ubyte*)mem.xmalloc(size);
+ memcpy(p, data.ptr, offset);
+ memset(data.ptr, 0xFF, data.length); // stomp old location
+ mem.xfree(data.ptr);
+ memset(p + offset, 0xff, size - offset); // stomp unused data
+ }
+ else
+ {
+ auto p = cast(ubyte*)mem.xrealloc(data.ptr, size);
+ if (mem.isGCEnabled) // clear currently unused data to avoid false pointers
+ memset(p + offset + nbytes, 0xff, size - offset - nbytes);
+ }
+ data = p[0 .. size];
+ }
+ }
+
+ /************************
+ * Shrink the size of the data to `size`.
+ * Params:
+ * size = new size of data, must be <= `.length`
+ */
+ extern (C++) void setsize(size_t size) pure nothrow @nogc @safe
+ {
+ assert(size <= offset);
+ offset = size;
+ }
+
+ extern (C++) void reset() pure nothrow @nogc @safe
+ {
+ offset = 0;
+ }
+
+ private void indent() pure nothrow
+ {
+ if (level)
+ {
+ const indentLevel = spaces ? level * 4 : level;
+ reserve(indentLevel);
+ data[offset .. offset + indentLevel] = (spaces ? ' ' : '\t');
+ offset += indentLevel;
+ }
+ notlinehead = true;
+ }
+
+ extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow
+ {
+ write(data[0 .. nbytes]);
+ }
+
+ void write(const(void)[] buf) pure nothrow
+ {
+ if (doindent && !notlinehead)
+ indent();
+ reserve(buf.length);
+ memcpy(this.data.ptr + offset, buf.ptr, buf.length);
+ offset += buf.length;
+ }
+
+ extern (C++) void writestring(const(char)* string) pure nothrow
+ {
+ write(string.toDString);
+ }
+
+ void writestring(const(char)[] s) pure nothrow
+ {
+ write(s);
+ }
+
+ void writestring(string s) pure nothrow
+ {
+ write(s);
+ }
+
+ void writestringln(const(char)[] s) pure nothrow
+ {
+ writestring(s);
+ writenl();
+ }
+
+ extern (C++) void prependstring(const(char)* string) pure nothrow
+ {
+ size_t len = strlen(string);
+ reserve(len);
+ memmove(data.ptr + len, data.ptr, offset);
+ memcpy(data.ptr, string, len);
+ offset += len;
+ }
+
+ /// write newline
+ extern (C++) void writenl() pure nothrow
+ {
+ version (Windows)
+ {
+ writeword(0x0A0D); // newline is CR,LF on Microsoft OS's
+ }
+ else
+ {
+ writeByte('\n');
+ }
+ if (doindent)
+ notlinehead = false;
+ }
+
+ extern (C++) void writeByte(uint b) pure nothrow
+ {
+ if (doindent && !notlinehead && b != '\n')
+ indent();
+ reserve(1);
+ this.data[offset] = cast(ubyte)b;
+ offset++;
+ }
+
+ extern (C++) void writeUTF8(uint b) pure nothrow
+ {
+ reserve(6);
+ if (b <= 0x7F)
+ {
+ this.data[offset] = cast(ubyte)b;
+ offset++;
+ }
+ else if (b <= 0x7FF)
+ {
+ this.data[offset + 0] = cast(ubyte)((b >> 6) | 0xC0);
+ this.data[offset + 1] = cast(ubyte)((b & 0x3F) | 0x80);
+ offset += 2;
+ }
+ else if (b <= 0xFFFF)
+ {
+ this.data[offset + 0] = cast(ubyte)((b >> 12) | 0xE0);
+ this.data[offset + 1] = cast(ubyte)(((b >> 6) & 0x3F) | 0x80);
+ this.data[offset + 2] = cast(ubyte)((b & 0x3F) | 0x80);
+ offset += 3;
+ }
+ else if (b <= 0x1FFFFF)
+ {
+ this.data[offset + 0] = cast(ubyte)((b >> 18) | 0xF0);
+ this.data[offset + 1] = cast(ubyte)(((b >> 12) & 0x3F) | 0x80);
+ this.data[offset + 2] = cast(ubyte)(((b >> 6) & 0x3F) | 0x80);
+ this.data[offset + 3] = cast(ubyte)((b & 0x3F) | 0x80);
+ offset += 4;
+ }
+ else
+ assert(0);
+ }
+
+ extern (C++) void prependbyte(uint b) pure nothrow
+ {
+ reserve(1);
+ memmove(data.ptr + 1, data.ptr, offset);
+ data[0] = cast(ubyte)b;
+ offset++;
+ }
+
+ extern (C++) void writewchar(uint w) pure nothrow
+ {
+ version (Windows)
+ {
+ writeword(w);
+ }
+ else
+ {
+ write4(w);
+ }
+ }
+
+ extern (C++) void writeword(uint w) pure nothrow
+ {
+ version (Windows)
+ {
+ uint newline = 0x0A0D;
+ }
+ else
+ {
+ uint newline = '\n';
+ }
+ if (doindent && !notlinehead && w != newline)
+ indent();
+
+ reserve(2);
+ *cast(ushort*)(this.data.ptr + offset) = cast(ushort)w;
+ offset += 2;
+ }
+
+ extern (C++) void writeUTF16(uint w) pure nothrow
+ {
+ reserve(4);
+ if (w <= 0xFFFF)
+ {
+ *cast(ushort*)(this.data.ptr + offset) = cast(ushort)w;
+ offset += 2;
+ }
+ else if (w <= 0x10FFFF)
+ {
+ *cast(ushort*)(this.data.ptr + offset) = cast(ushort)((w >> 10) + 0xD7C0);
+ *cast(ushort*)(this.data.ptr + offset + 2) = cast(ushort)((w & 0x3FF) | 0xDC00);
+ offset += 4;
+ }
+ else
+ assert(0);
+ }
+
+ extern (C++) void write4(uint w) pure nothrow
+ {
+ version (Windows)
+ {
+ bool notnewline = w != 0x000A000D;
+ }
+ else
+ {
+ bool notnewline = true;
+ }
+ if (doindent && !notlinehead && notnewline)
+ indent();
+ reserve(4);
+ *cast(uint*)(this.data.ptr + offset) = w;
+ offset += 4;
+ }
+
+ extern (C++) void write(const OutBuffer* buf) pure nothrow
+ {
+ if (buf)
+ {
+ reserve(buf.offset);
+ memcpy(data.ptr + offset, buf.data.ptr, buf.offset);
+ offset += buf.offset;
+ }
+ }
+
+ extern (C++) void write(RootObject obj) /*nothrow*/
+ {
+ if (obj)
+ {
+ writestring(obj.toChars());
+ }
+ }
+
+ extern (C++) void fill0(size_t nbytes) pure nothrow
+ {
+ reserve(nbytes);
+ memset(data.ptr + offset, 0, nbytes);
+ offset += nbytes;
+ }
+
+ /**
+ * Allocate space, but leave it uninitialized.
+ * Params:
+ * nbytes = amount to allocate
+ * Returns:
+ * slice of the allocated space to be filled in
+ */
+ extern (D) char[] allocate(size_t nbytes) pure nothrow
+ {
+ reserve(nbytes);
+ offset += nbytes;
+ return cast(char[])data[offset - nbytes .. offset];
+ }
+
+ extern (C++) void vprintf(const(char)* format, va_list args) nothrow
+ {
+ int count;
+ if (doindent && !notlinehead)
+ indent();
+ uint psize = 128;
+ for (;;)
+ {
+ reserve(psize);
+ va_list va;
+ va_copy(va, args);
+ /*
+ The functions vprintf(), vfprintf(), vsprintf(), vsnprintf()
+ are equivalent to the functions printf(), fprintf(), sprintf(),
+ snprintf(), respectively, except that they are called with a
+ va_list instead of a variable number of arguments. These
+ functions do not call the va_end macro. Consequently, the value
+ of ap is undefined after the call. The application should call
+ va_end(ap) itself afterwards.
+ */
+ count = vsnprintf(cast(char*)data.ptr + offset, psize, format, va);
+ va_end(va);
+ if (count == -1) // snn.lib and older libcmt.lib return -1 if buffer too small
+ psize *= 2;
+ else if (count >= psize)
+ psize = count + 1;
+ else
+ break;
+ }
+ offset += count;
+ if (mem.isGCEnabled)
+ memset(data.ptr + offset, 0xff, psize - count);
+ }
+
+ static if (__VERSION__ < 2092)
+ {
+ extern (C++) void printf(const(char)* format, ...) nothrow
+ {
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ va_end(ap);
+ }
+ }
+ else
+ {
+ pragma(printf) extern (C++) void printf(const(char)* format, ...) nothrow
+ {
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ va_end(ap);
+ }
+ }
+
+ /**************************************
+ * Convert `u` to a string and append it to the buffer.
+ * Params:
+ * u = integral value to append
+ */
+ extern (C++) void print(ulong u) pure nothrow
+ {
+ //import core.internal.string; // not available
+ UnsignedStringBuf buf = void;
+ writestring(unsignedToTempString(u, buf));
+ }
+
+ extern (C++) void bracket(char left, char right) pure nothrow
+ {
+ reserve(2);
+ memmove(data.ptr + 1, data.ptr, offset);
+ data[0] = left;
+ data[offset + 1] = right;
+ offset += 2;
+ }
+
+ /******************
+ * Insert left at i, and right at j.
+ * Return index just past right.
+ */
+ extern (C++) size_t bracket(size_t i, const(char)* left, size_t j, const(char)* right) pure nothrow
+ {
+ size_t leftlen = strlen(left);
+ size_t rightlen = strlen(right);
+ reserve(leftlen + rightlen);
+ insert(i, left, leftlen);
+ insert(j + leftlen, right, rightlen);
+ return j + leftlen + rightlen;
+ }
+
+ extern (C++) void spread(size_t offset, size_t nbytes) pure nothrow
+ {
+ reserve(nbytes);
+ memmove(data.ptr + offset + nbytes, data.ptr + offset, this.offset - offset);
+ this.offset += nbytes;
+ }
+
+ /****************************************
+ * Returns: offset + nbytes
+ */
+ extern (C++) size_t insert(size_t offset, const(void)* p, size_t nbytes) pure nothrow
+ {
+ spread(offset, nbytes);
+ memmove(data.ptr + offset, p, nbytes);
+ return offset + nbytes;
+ }
+
+ size_t insert(size_t offset, const(char)[] s) pure nothrow
+ {
+ return insert(offset, s.ptr, s.length);
+ }
+
+ extern (C++) void remove(size_t offset, size_t nbytes) pure nothrow @nogc
+ {
+ memmove(data.ptr + offset, data.ptr + offset + nbytes, this.offset - (offset + nbytes));
+ this.offset -= nbytes;
+ }
+
+ /**
+ * Returns:
+ * a non-owning const slice of the buffer contents
+ */
+ extern (D) const(char)[] opSlice() const pure nothrow @nogc @safe
+ {
+ return cast(const(char)[])data[0 .. offset];
+ }
+
+ extern (D) const(char)[] opSlice(size_t lwr, size_t upr) const pure nothrow @nogc @safe
+ {
+ return cast(const(char)[])data[lwr .. upr];
+ }
+
+ extern (D) char opIndex(size_t i) const pure nothrow @nogc @safe
+ {
+ return cast(char)data[i];
+ }
+
+ /***********************************
+ * Extract the data as a slice and take ownership of it.
+ *
+ * When `true` is passed as an argument, this function behaves
+ * like `dmd.utils.toDString(thisbuffer.extractChars())`.
+ *
+ * Params:
+ * nullTerminate = When `true`, the data will be `null` terminated.
+ * This is useful to call C functions or store
+ * the result in `Strings`. Defaults to `false`.
+ */
+ extern (D) char[] extractSlice(bool nullTerminate = false) pure nothrow
+ {
+ const length = offset;
+ if (!nullTerminate)
+ return extractData()[0 .. length];
+ // There's already a terminating `'\0'`
+ if (length && data[length - 1] == '\0')
+ return extractData()[0 .. length - 1];
+ writeByte(0);
+ return extractData()[0 .. length];
+ }
+
+ // Append terminating null if necessary and get view of internal buffer
+ extern (C++) char* peekChars() pure nothrow
+ {
+ if (!offset || data[offset - 1] != '\0')
+ {
+ writeByte(0);
+ offset--; // allow appending more
+ }
+ return cast(char*)data.ptr;
+ }
+
+ // Append terminating null if necessary and take ownership of data
+ extern (C++) char* extractChars() pure nothrow
+ {
+ if (!offset || data[offset - 1] != '\0')
+ writeByte(0);
+ return extractData();
+ }
+
+ /**
+ Destructively saves the contents of `this` to `filename`. As an
+ optimization, if the file already has identical contents with the buffer,
+ no copying is done. This is because on SSD drives reading is often much
+ faster than writing and because there's a high likelihood an identical
+ file is written during the build process.
+
+ Params:
+ filename = the name of the file to receive the contents
+
+ Returns: `true` iff the operation succeeded.
+ */
+ extern(D) bool moveToFile(const char* filename)
+ {
+ import dmd.root.file;
+ bool result = true;
+ const bool identical = this[] == FileMapping!(const ubyte)(filename)[];
+
+ if (fileMapping && fileMapping.active)
+ {
+ // Defer to corresponding functions in FileMapping.
+ if (identical)
+ {
+ result = fileMapping.discard();
+ }
+ else
+ {
+ // Resize to fit to get rid of the slack bytes at the end
+ fileMapping.resize(offset);
+ result = fileMapping.moveToFile(filename);
+ }
+ // Can't call destroy() here because the file mapping is already closed.
+ data = null;
+ offset = 0;
+ }
+ else
+ {
+ if (!identical)
+ File.write(filename, this[]);
+ destroy();
+ }
+
+ return identical
+ ? result && File.touch(filename)
+ : result;
+ }
+}
+
+/****** copied from core.internal.string *************/
+
+private:
+
+alias UnsignedStringBuf = char[20];
+
+char[] unsignedToTempString(ulong value, char[] buf, uint radix = 10) @safe pure nothrow @nogc
+{
+ size_t i = buf.length;
+ do
+ {
+ if (value < radix)
+ {
+ ubyte x = cast(ubyte)value;
+ buf[--i] = cast(char)((x < 10) ? x + '0' : x - 10 + 'a');
+ break;
+ }
+ else
+ {
+ ubyte x = cast(ubyte)(value % radix);
+ value = value / radix;
+ buf[--i] = cast(char)((x < 10) ? x + '0' : x - 10 + 'a');
+ }
+ } while (value);
+ return buf[i .. $];
+}
+
+/************* unit tests **************************************************/
+
+unittest
+{
+ OutBuffer buf;
+ buf.printf("betty");
+ buf.insert(1, "xx".ptr, 2);
+ buf.insert(3, "yy");
+ buf.remove(4, 1);
+ buf.bracket('(', ')');
+ const char[] s = buf[];
+ assert(s == "(bxxyetty)");
+ buf.destroy();
+}
+
+unittest
+{
+ OutBuffer buf;
+ buf.writestring("abc".ptr);
+ buf.prependstring("def");
+ buf.prependbyte('x');
+ OutBuffer buf2;
+ buf2.writestring("mmm");
+ buf.write(&buf2);
+ char[] s = buf.extractSlice();
+ assert(s == "xdefabcmmm");
+}
+
+unittest
+{
+ OutBuffer buf;
+ buf.writeByte('a');
+ char[] s = buf.extractSlice();
+ assert(s == "a");
+
+ buf.writeByte('b');
+ char[] t = buf.extractSlice();
+ assert(t == "b");
+}
+
+unittest
+{
+ OutBuffer buf;
+ char* p = buf.peekChars();
+ assert(*p == 0);
+
+ buf.writeByte('s');
+ char* q = buf.peekChars();
+ assert(strcmp(q, "s") == 0);
+}
+
+unittest
+{
+ char[10] buf;
+ char[] s = unsignedToTempString(278, buf[], 10);
+ assert(s == "278");
+
+ s = unsignedToTempString(1, buf[], 10);
+ assert(s == "1");
+
+ s = unsignedToTempString(8, buf[], 2);
+ assert(s == "1000");
+
+ s = unsignedToTempString(29, buf[], 16);
+ assert(s == "1d");
+}
diff --git a/gcc/d/dmd/root/outbuffer.h b/gcc/d/dmd/root/outbuffer.h
index 186fbb7eb84..b635373c183 100644
--- a/gcc/d/dmd/root/outbuffer.h
+++ b/gcc/d/dmd/root/outbuffer.h
@@ -1,5 +1,6 @@
/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
@@ -16,14 +17,16 @@ class RootObject;
struct OutBuffer
{
+ // IMPORTANT: PLEASE KEEP STATE AND DESTRUCTOR IN SYNC WITH DEFINITION IN ./outbuffer.d.
private:
DArray<unsigned char> data;
- size_t offset;
+ d_size_t offset;
bool notlinehead;
+ void* fileMapping; // pointer to a file mapping object not used on the C++ side
public:
-
- int level;
bool doindent;
+ bool spaces;
+ int level;
OutBuffer()
{
@@ -33,17 +36,18 @@ public:
doindent = 0;
level = 0;
notlinehead = 0;
+ fileMapping = 0;
}
~OutBuffer()
{
mem.xfree(data.ptr);
}
- const DArray<unsigned char> slice() const { return data; }
- size_t length() const { return offset; }
+ d_size_t length() const { return offset; }
char *extractData();
+ void destroy();
- void reserve(size_t nbytes);
- void setsize(size_t size);
+ void reserve(d_size_t nbytes);
+ void setsize(d_size_t size);
void reset();
void write(const void *data, size_t nbytes);
void writestring(const char *string);
@@ -56,17 +60,16 @@ public:
void writeword(unsigned w);
void writeUTF16(unsigned w);
void write4(unsigned w);
- void write(OutBuffer *buf);
+ void write(const OutBuffer *buf);
void write(RootObject *obj);
- void fill0(size_t nbytes);
+ void fill0(d_size_t nbytes);
void vprintf(const char *format, va_list args);
void printf(const char *format, ...);
- void print(unsigned long long u);
void bracket(char left, char right);
- size_t bracket(size_t i, const char *left, size_t j, const char *right);
- void spread(size_t offset, size_t nbytes);
- size_t insert(size_t offset, const void *data, size_t nbytes);
- void remove(size_t offset, size_t nbytes);
+ d_size_t bracket(d_size_t i, const char *left, d_size_t j, const char *right);
+ void spread(d_size_t offset, d_size_t nbytes);
+ d_size_t insert(d_size_t offset, const void *data, d_size_t nbytes);
+ void remove(d_size_t offset, d_size_t nbytes);
// Append terminating null if necessary and get view of internal buffer
char *peekChars();
// Append terminating null if necessary and take ownership of data
diff --git a/gcc/d/dmd/root/port.d b/gcc/d/dmd/root/port.d
new file mode 100644
index 00000000000..1bafa207077
--- /dev/null
+++ b/gcc/d/dmd/root/port.d
@@ -0,0 +1,49 @@
+/**
+ * Portable routines for functions that have different implementations on different platforms.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/port.d, root/_port.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_port.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/port.d
+ */
+
+module dmd.root.port;
+
+import core.stdc.stdint;
+
+nothrow @nogc:
+
+extern (C++) struct Port
+{
+ nothrow @nogc:
+
+ static int memicmp(scope const char* s1, scope const char* s2, size_t n) pure;
+
+ static char* strupr(char* s) pure;
+
+ static bool isFloat32LiteralOutOfRange(scope const(char)* s);
+
+ static bool isFloat64LiteralOutOfRange(scope const(char)* s);
+
+ // Little endian
+ static void writelongLE(uint value, scope void* buffer) pure;
+
+ // Little endian
+ static uint readlongLE(scope const void* buffer) pure;
+
+ // Big endian
+ static void writelongBE(uint value, scope void* buffer) pure;
+
+ // Big endian
+ static uint readlongBE(scope const void* buffer) pure;
+
+ // Little endian
+ static uint readwordLE(scope const void* buffer) pure;
+
+ // Big endian
+ static uint readwordBE(scope const void* buffer) pure;
+
+ static void valcpy(scope void *dst, uint64_t val, size_t size) pure;
+}
diff --git a/gcc/d/dmd/root/port.h b/gcc/d/dmd/root/port.h
index 94651cdd5a4..08cf66cf10e 100644
--- a/gcc/d/dmd/root/port.h
+++ b/gcc/d/dmd/root/port.h
@@ -1,5 +1,6 @@
/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
@@ -11,15 +12,7 @@
// Portable wrapper around compiler/system specific things.
// The idea is to minimize #ifdef's in the app code.
-#include "dsystem.h" // for alloca
-
-#if _MSC_VER
-typedef __int64 longlong;
-typedef unsigned __int64 ulonglong;
-#else
-typedef long long longlong;
-typedef unsigned long long ulonglong;
-#endif
+#include "dsystem.h"
typedef unsigned char utf8_t;
diff --git a/gcc/d/dmd/root/region.d b/gcc/d/dmd/root/region.d
new file mode 100644
index 00000000000..50689fe872d
--- /dev/null
+++ b/gcc/d/dmd/root/region.d
@@ -0,0 +1,161 @@
+/**
+ * Region storage allocator implementation.
+ *
+ * Copyright: Copyright (C) 2019-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/region.d, root/_region.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_region.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/region.d
+ */
+
+module dmd.root.region;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import core.stdc.stdlib;
+
+import dmd.root.rmem;
+import dmd.root.array;
+
+/*****
+ * Simple region storage allocator.
+ */
+struct Region
+{
+ nothrow:
+ private:
+
+ Array!(void*) array; // array of chunks
+ int used; // number of chunks used in array[]
+ void[] available; // slice of chunk that's available to allocate
+
+ enum ChunkSize = 4096 * 1024;
+ enum MaxAllocSize = ChunkSize;
+
+ struct RegionPos
+ {
+ int used;
+ void[] available;
+ }
+
+public:
+
+ /******
+ * Allocate nbytes. Aborts on failure.
+ * Params:
+ * nbytes = number of bytes to allocate, can be 0, must be <= than MaxAllocSize
+ * Returns:
+ * allocated data, null for nbytes==0
+ */
+ void* malloc(size_t nbytes)
+ {
+ if (!nbytes)
+ return null;
+
+ nbytes = (nbytes + 15) & ~15;
+ if (nbytes > available.length)
+ {
+ assert(nbytes <= MaxAllocSize);
+ if (used == array.length)
+ {
+ auto h = Mem.check(.malloc(ChunkSize));
+ array.push(h);
+ }
+
+ available = array[used][0 .. MaxAllocSize];
+ ++used;
+ }
+
+ auto p = available.ptr;
+ available = (p + nbytes)[0 .. available.length - nbytes];
+ return p;
+ }
+
+ /****************************
+ * Return stack position for allocations in this region.
+ * Returns:
+ * an opaque struct to be passed to `release()`
+ */
+ RegionPos savePos() pure @nogc @safe
+ {
+ return RegionPos(used, available);
+ }
+
+ /********************
+ * Release the memory that was allocated after the respective call to `savePos()`.
+ * Params:
+ * pos = position returned by `savePos()`
+ */
+ void release(RegionPos pos) pure @nogc @safe
+ {
+ version (all)
+ {
+ /* Recycle the memory. There better not be
+ * any live pointers to it.
+ */
+ used = pos.used;
+ available = pos.available;
+ }
+ else
+ {
+ /* Instead of recycling the memory, stomp on it
+ * to flush out any remaining live pointers to it.
+ */
+ (cast(ubyte[])pos.available)[] = 0xFF;
+ foreach (h; array[pos.used .. used])
+ (cast(ubyte*)h)[0 .. ChunkSize] = 0xFF;
+ }
+ }
+
+ /****************************
+ * If pointer points into Region.
+ * Params:
+ * p = pointer to check
+ * Returns:
+ * true if it points into the region
+ */
+ bool contains(void* p) pure @nogc
+ {
+ foreach (h; array[0 .. used])
+ {
+ if (h <= p && p < h + ChunkSize)
+ return true;
+ }
+ return false;
+ }
+
+ /*********************
+ * Returns: size of Region
+ */
+ size_t size() pure @nogc @safe
+ {
+ return used * MaxAllocSize - available.length;
+ }
+}
+
+
+unittest
+{
+ Region reg;
+ auto rgnpos = reg.savePos();
+
+ void* p = reg.malloc(0);
+ assert(p == null);
+ assert(!reg.contains(p));
+
+ p = reg.malloc(100);
+ assert(p !is null);
+ assert(reg.contains(p));
+ memset(p, 0, 100);
+
+ p = reg.malloc(100);
+ assert(p !is null);
+ assert(reg.contains(p));
+ memset(p, 0, 100);
+
+ assert(reg.size() > 0);
+ assert(!reg.contains(&reg));
+
+ reg.release(rgnpos);
+}
diff --git a/gcc/d/dmd/root/rmem.c b/gcc/d/dmd/root/rmem.c
deleted file mode 100644
index 768b75d2715..00000000000
--- a/gcc/d/dmd/root/rmem.c
+++ /dev/null
@@ -1,191 +0,0 @@
-
-/* Copyright (C) 2000-2021 by The D Language Foundation, All Rights Reserved
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- * https://github.com/D-Programming-Language/dmd/blob/master/src/root/rmem.c
- */
-
-#include "dsystem.h"
-#include "rmem.h"
-
-/* This implementation of the storage allocator uses the standard C allocation package.
- */
-
-Mem mem;
-
-char *Mem::xstrdup(const char *s)
-{
- char *p;
-
- if (s)
- {
-#ifdef IN_GCC
- p = ::xstrdup(s);
-#else
- p = strdup(s);
-#endif
- if (p)
- return p;
- error();
- }
- return NULL;
-}
-
-void *Mem::xmalloc(size_t size)
-{ void *p;
-
- if (!size)
- p = NULL;
- else
- {
-#ifdef IN_GCC
- p = ::xmalloc(size);
-#else
- p = malloc(size);
-#endif
- if (!p)
- error();
- }
- return p;
-}
-
-void *Mem::xcalloc(size_t size, size_t n)
-{ void *p;
-
- if (!size || !n)
- p = NULL;
- else
- {
-#ifdef IN_GCC
- p = ::xcalloc(size, n);
-#else
- p = calloc(size, n);
-#endif
- if (!p)
- error();
- }
- return p;
-}
-
-void *Mem::xrealloc(void *p, size_t size)
-{
- if (!size)
- { if (p)
- {
- free(p);
- p = NULL;
- }
- }
- else if (!p)
- {
-#ifdef IN_GCC
- p = ::xmalloc(size);
-#else
- p = malloc(size);
-#endif
- if (!p)
- error();
- }
- else
- {
- void *psave = p;
-#ifdef IN_GCC
- p = ::xrealloc(psave, size);
-#else
- p = realloc(psave, size);
-#endif
- if (!p)
- { xfree(psave);
- error();
- }
- }
- return p;
-}
-
-void Mem::xfree(void *p)
-{
- if (p)
- free(p);
-}
-
-void *Mem::xmallocdup(void *o, size_t size)
-{ void *p;
-
- if (!size)
- p = NULL;
- else
- {
-#ifdef IN_GCC
- p = ::xmalloc(size);
-#else
- p = malloc(size);
-#endif
- if (!p)
- error();
- else
- memcpy(p,o,size);
- }
- return p;
-}
-
-void Mem::error()
-{
- printf("Error: out of memory\n");
- exit(EXIT_FAILURE);
-}
-
-/* =================================================== */
-
-/* Allocate, but never release
- */
-
-// Allocate a little less than 1Mb because the C runtime adds some overhead that
-// causes the actual memory block to be larger than 1Mb otherwise.
-#define CHUNK_SIZE (256 * 4096 - 64)
-
-static size_t heapleft = 0;
-static void *heapp;
-
-extern "C" void *allocmemory(size_t m_size)
-{
- // 16 byte alignment is better (and sometimes needed) for doubles
- m_size = (m_size + 15) & ~15;
-
- // The layout of the code is selected so the most common case is straight through
- if (m_size <= heapleft)
- {
- L1:
- heapleft -= m_size;
- void *p = heapp;
- heapp = (void *)((char *)heapp + m_size);
- return p;
- }
-
- if (m_size > CHUNK_SIZE)
- {
-#ifdef IN_GCC
- void *p = xmalloc(m_size);
-#else
- void *p = malloc(m_size);
-#endif
- if (p)
- return p;
- printf("Error: out of memory\n");
- exit(EXIT_FAILURE);
- return p;
- }
-
- heapleft = CHUNK_SIZE;
-#ifdef IN_GCC
- heapp = xmalloc(CHUNK_SIZE);
-#else
- heapp = malloc(CHUNK_SIZE);
-#endif
- if (!heapp)
- {
- printf("Error: out of memory\n");
- exit(EXIT_FAILURE);
- }
- goto L1;
-}
diff --git a/gcc/d/dmd/root/rmem.d b/gcc/d/dmd/root/rmem.d
new file mode 100644
index 00000000000..198f3d01437
--- /dev/null
+++ b/gcc/d/dmd/root/rmem.d
@@ -0,0 +1,375 @@
+/**
+ * Allocate memory using `malloc` or the GC depending on the configuration.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/rmem.d, root/_rmem.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_rmem.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/rmem.d
+ */
+
+module dmd.root.rmem;
+
+import core.exception : onOutOfMemoryError;
+import core.stdc.stdio;
+import core.stdc.stdlib;
+import core.stdc.string;
+
+import core.memory : GC;
+
+extern (C++) struct Mem
+{
+ static char* xstrdup(const(char)* s) nothrow
+ {
+ if (isGCEnabled)
+ return s ? s[0 .. strlen(s) + 1].dup.ptr : null;
+
+ return s ? cast(char*)check(.strdup(s)) : null;
+ }
+
+ static void xfree(void* p) pure nothrow
+ {
+ if (isGCEnabled)
+ return GC.free(p);
+
+ pureFree(p);
+ }
+
+ static void* xmalloc(size_t size) pure nothrow
+ {
+ if (isGCEnabled)
+ return size ? GC.malloc(size) : null;
+
+ return size ? check(pureMalloc(size)) : null;
+ }
+
+ static void* xmalloc_noscan(size_t size) pure nothrow
+ {
+ if (isGCEnabled)
+ return size ? GC.malloc(size, GC.BlkAttr.NO_SCAN) : null;
+
+ return size ? check(pureMalloc(size)) : null;
+ }
+
+ static void* xcalloc(size_t size, size_t n) pure nothrow
+ {
+ if (isGCEnabled)
+ return size * n ? GC.calloc(size * n) : null;
+
+ return (size && n) ? check(pureCalloc(size, n)) : null;
+ }
+
+ static void* xcalloc_noscan(size_t size, size_t n) pure nothrow
+ {
+ if (isGCEnabled)
+ return size * n ? GC.calloc(size * n, GC.BlkAttr.NO_SCAN) : null;
+
+ return (size && n) ? check(pureCalloc(size, n)) : null;
+ }
+
+ static void* xrealloc(void* p, size_t size) pure nothrow
+ {
+ if (isGCEnabled)
+ return GC.realloc(p, size);
+
+ if (!size)
+ {
+ pureFree(p);
+ return null;
+ }
+
+ return check(pureRealloc(p, size));
+ }
+
+ static void* xrealloc_noscan(void* p, size_t size) pure nothrow
+ {
+ if (isGCEnabled)
+ return GC.realloc(p, size, GC.BlkAttr.NO_SCAN);
+
+ if (!size)
+ {
+ pureFree(p);
+ return null;
+ }
+
+ return check(pureRealloc(p, size));
+ }
+
+ static void* error() pure nothrow @nogc @safe
+ {
+ onOutOfMemoryError();
+ assert(0);
+ }
+
+ /**
+ * Check p for null. If it is, issue out of memory error
+ * and exit program.
+ * Params:
+ * p = pointer to check for null
+ * Returns:
+ * p if not null
+ */
+ static void* check(void* p) pure nothrow @nogc
+ {
+ return p ? p : error();
+ }
+
+ __gshared bool _isGCEnabled = true;
+
+ // fake purity by making global variable immutable (_isGCEnabled only modified before startup)
+ enum _pIsGCEnabled = cast(immutable bool*) &_isGCEnabled;
+
+ static bool isGCEnabled() pure nothrow @nogc @safe
+ {
+ return *_pIsGCEnabled;
+ }
+
+ static void disableGC() nothrow @nogc
+ {
+ _isGCEnabled = false;
+ }
+
+ static void addRange(const(void)* p, size_t size) nothrow @nogc
+ {
+ if (isGCEnabled)
+ GC.addRange(p, size);
+ }
+
+ static void removeRange(const(void)* p) nothrow @nogc
+ {
+ if (isGCEnabled)
+ GC.removeRange(p);
+ }
+}
+
+extern (C++) const __gshared Mem mem;
+
+enum CHUNK_SIZE = (256 * 4096 - 64);
+
+__gshared size_t heapleft = 0;
+__gshared void* heapp;
+
+extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc
+{
+ // 16 byte alignment is better (and sometimes needed) for doubles
+ m_size = (m_size + 15) & ~15;
+
+ // The layout of the code is selected so the most common case is straight through
+ if (m_size <= heapleft)
+ {
+ L1:
+ heapleft -= m_size;
+ auto p = heapp;
+ heapp = cast(void*)(cast(char*)heapp + m_size);
+ return p;
+ }
+
+ if (m_size > CHUNK_SIZE)
+ {
+ return Mem.check(malloc(m_size));
+ }
+
+ heapleft = CHUNK_SIZE;
+ heapp = Mem.check(malloc(CHUNK_SIZE));
+ goto L1;
+}
+
+extern (D) void* allocmemory(size_t m_size) nothrow
+{
+ if (mem.isGCEnabled)
+ return GC.malloc(m_size);
+
+ return allocmemoryNoFree(m_size);
+}
+
+version (DigitalMars)
+{
+ enum OVERRIDE_MEMALLOC = true;
+}
+else version (LDC)
+{
+ // Memory allocation functions gained weak linkage when the @weak attribute was introduced.
+ import ldc.attributes;
+ enum OVERRIDE_MEMALLOC = is(typeof(ldc.attributes.weak));
+}
+else version (GNU)
+{
+ version (IN_GCC)
+ enum OVERRIDE_MEMALLOC = false;
+ else
+ enum OVERRIDE_MEMALLOC = true;
+}
+else
+{
+ enum OVERRIDE_MEMALLOC = false;
+}
+
+static if (OVERRIDE_MEMALLOC)
+{
+ // Override the host druntime allocation functions in order to use the bump-
+ // pointer allocation scheme (`allocmemoryNoFree()` above) if the GC is disabled.
+ // That scheme is faster and comes with less memory overhead than using a
+ // disabled GC alone.
+
+ extern (C) void* _d_allocmemory(size_t m_size) nothrow
+ {
+ return allocmemory(m_size);
+ }
+
+ private void* allocClass(const ClassInfo ci) nothrow pure
+ {
+ alias BlkAttr = GC.BlkAttr;
+
+ assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass));
+
+ BlkAttr attr = BlkAttr.NONE;
+ if (ci.m_flags & TypeInfo_Class.ClassFlags.hasDtor
+ && !(ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass))
+ attr |= BlkAttr.FINALIZE;
+ if (ci.m_flags & TypeInfo_Class.ClassFlags.noPointers)
+ attr |= BlkAttr.NO_SCAN;
+ return GC.malloc(ci.initializer.length, attr, ci);
+ }
+
+ extern (C) void* _d_newitemU(const TypeInfo ti) nothrow;
+
+ extern (C) Object _d_newclass(const ClassInfo ci) nothrow
+ {
+ const initializer = ci.initializer;
+
+ auto p = mem.isGCEnabled ? allocClass(ci) : allocmemoryNoFree(initializer.length);
+ memcpy(p, initializer.ptr, initializer.length);
+ return cast(Object) p;
+ }
+
+ version (LDC)
+ {
+ extern (C) Object _d_allocclass(const ClassInfo ci) nothrow
+ {
+ if (mem.isGCEnabled)
+ return cast(Object) allocClass(ci);
+
+ return cast(Object) allocmemoryNoFree(ci.initializer.length);
+ }
+ }
+
+ extern (C) void* _d_newitemT(TypeInfo ti) nothrow
+ {
+ auto p = mem.isGCEnabled ? _d_newitemU(ti) : allocmemoryNoFree(ti.tsize);
+ memset(p, 0, ti.tsize);
+ return p;
+ }
+
+ extern (C) void* _d_newitemiT(TypeInfo ti) nothrow
+ {
+ auto p = mem.isGCEnabled ? _d_newitemU(ti) : allocmemoryNoFree(ti.tsize);
+ const initializer = ti.initializer;
+ memcpy(p, initializer.ptr, initializer.length);
+ return p;
+ }
+
+ // TypeInfo.initializer for compilers older than 2.070
+ static if(!__traits(hasMember, TypeInfo, "initializer"))
+ private const(void[]) initializer(T : TypeInfo)(const T t)
+ nothrow pure @safe @nogc
+ {
+ return t.init;
+ }
+}
+
+extern (C) pure @nogc nothrow
+{
+ /**
+ * Pure variants of C's memory allocation functions `malloc`, `calloc`, and
+ * `realloc` and deallocation function `free`.
+ *
+ * UNIX 98 requires that errno be set to ENOMEM upon failure.
+ * https://linux.die.net/man/3/malloc
+ * However, this is irrelevant for DMD's purposes, and best practice
+ * protocol for using errno is to treat it as an `out` parameter, and not
+ * something with state that can be relied on across function calls.
+ * So, we'll ignore it.
+ *
+ * See_Also:
+ * $(LINK2 https://dlang.org/spec/function.html#pure-functions, D's rules for purity),
+ * which allow for memory allocation under specific circumstances.
+ */
+ pragma(mangle, "malloc") void* pureMalloc(size_t size) @trusted;
+
+ /// ditto
+ pragma(mangle, "calloc") void* pureCalloc(size_t nmemb, size_t size) @trusted;
+
+ /// ditto
+ pragma(mangle, "realloc") void* pureRealloc(void* ptr, size_t size) @system;
+
+ /// ditto
+ pragma(mangle, "free") void pureFree(void* ptr) @system;
+
+}
+
+/**
+Makes a null-terminated copy of the given string on newly allocated memory.
+The null-terminator won't be part of the returned string slice. It will be
+at position `n` where `n` is the length of the input string.
+
+Params:
+ s = string to copy
+
+Returns: A null-terminated copy of the input array.
+*/
+extern (D) char[] xarraydup(const(char)[] s) pure nothrow
+{
+ if (!s)
+ return null;
+
+ auto p = cast(char*)mem.xmalloc_noscan(s.length + 1);
+ char[] a = p[0 .. s.length];
+ a[] = s[0 .. s.length];
+ p[s.length] = 0; // preserve 0 terminator semantics
+ return a;
+}
+
+///
+pure nothrow unittest
+{
+ auto s1 = "foo";
+ auto s2 = s1.xarraydup;
+ s2[0] = 'b';
+ assert(s1 == "foo");
+ assert(s2 == "boo");
+ assert(*(s2.ptr + s2.length) == '\0');
+ string sEmpty;
+ assert(sEmpty.xarraydup is null);
+}
+
+/**
+Makes a copy of the given array on newly allocated memory.
+
+Params:
+ s = array to copy
+
+Returns: A copy of the input array.
+*/
+extern (D) T[] arraydup(T)(const scope T[] s) pure nothrow
+{
+ if (!s)
+ return null;
+
+ const dim = s.length;
+ auto p = (cast(T*)mem.xmalloc(T.sizeof * dim))[0 .. dim];
+ p[] = s;
+ return p;
+}
+
+///
+pure nothrow unittest
+{
+ auto s1 = [0, 1, 2];
+ auto s2 = s1.arraydup;
+ s2[0] = 4;
+ assert(s1 == [0, 1, 2]);
+ assert(s2 == [4, 1, 2]);
+ string sEmpty;
+ assert(sEmpty.arraydup is null);
+}
diff --git a/gcc/d/dmd/root/rmem.h b/gcc/d/dmd/root/rmem.h
index 1f603b8c658..04d9e3fb9a2 100644
--- a/gcc/d/dmd/root/rmem.h
+++ b/gcc/d/dmd/root/rmem.h
@@ -1,5 +1,6 @@
/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
@@ -8,19 +9,25 @@
#pragma once
-#include "dsystem.h" // for size_t
+#include "dcompat.h" // for d_size_t
struct Mem
{
Mem() { }
static char *xstrdup(const char *s);
- static void *xmalloc(size_t size);
- static void *xcalloc(size_t size, size_t n);
- static void *xrealloc(void *p, size_t size);
static void xfree(void *p);
- static void *xmallocdup(void *o, size_t size);
+ static void *xmalloc(d_size_t size);
+ static void *xcalloc(d_size_t size, d_size_t n);
+ static void *xrealloc(void *p, d_size_t size);
static void error();
+
+ static bool _isGCEnabled;
+
+ static bool isGCEnabled();
+ static void disableGC();
+ static void addRange(const void *p, d_size_t size);
+ static void removeRange(const void *p);
};
extern Mem mem;
diff --git a/gcc/d/dmd/root/root.h b/gcc/d/dmd/root/root.h
index d998d95be76..667ce67fb7c 100644
--- a/gcc/d/dmd/root/root.h
+++ b/gcc/d/dmd/root/root.h
@@ -1,5 +1,6 @@
/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
diff --git a/gcc/d/dmd/root/rootobject.c b/gcc/d/dmd/root/rootobject.c
deleted file mode 100644
index 7fee0d7297b..00000000000
--- a/gcc/d/dmd/root/rootobject.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- * https://github.com/D-Programming-Language/dmd/blob/master/src/root/object.c
- */
-
-#include "dsystem.h"
-#include "object.h"
-#include "outbuffer.h"
-
-/****************************** Object ********************************/
-
-bool RootObject::equals(RootObject *o)
-{
- return o == this;
-}
-
-int RootObject::compare(RootObject *obj)
-{
- size_t lhs = (size_t)this;
- size_t rhs = (size_t)obj;
- if (lhs < rhs)
- return -1;
- else if (lhs > rhs)
- return 1;
- return 0;
-}
-
-void RootObject::print()
-{
- printf("%s %p\n", toChars(), this);
-}
-
-const char *RootObject::toChars()
-{
- return "Object";
-}
-
-int RootObject::dyncast() const
-{
- return DYNCAST_OBJECT;
-}
-
-void RootObject::toBuffer(OutBuffer *b)
-{
- b->writestring("Object");
-}
diff --git a/gcc/d/dmd/root/rootobject.d b/gcc/d/dmd/root/rootobject.d
new file mode 100644
index 00000000000..854ec1a65bc
--- /dev/null
+++ b/gcc/d/dmd/root/rootobject.d
@@ -0,0 +1,67 @@
+/**
+ * Provide the root object that classes in dmd inherit from.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/rootobject.d, root/_rootobject.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_rootobject.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/rootobject.d
+ */
+
+module dmd.root.rootobject;
+
+import core.stdc.stdio;
+
+import dmd.root.outbuffer;
+
+/***********************************************************
+ */
+
+enum DYNCAST : int
+{
+ object,
+ expression,
+ dsymbol,
+ type,
+ identifier,
+ tuple,
+ parameter,
+ statement,
+ condition,
+ templateparameter,
+ initializer,
+}
+
+/***********************************************************
+ */
+
+extern (C++) class RootObject
+{
+ this() nothrow pure @nogc @safe
+ {
+ }
+
+ bool equals(const RootObject o) const
+ {
+ return o is this;
+ }
+
+ const(char)* toChars() const
+ {
+ assert(0);
+ }
+
+ ///
+ extern(D) const(char)[] toString() const
+ {
+ import core.stdc.string : strlen;
+ auto p = this.toChars();
+ return p[0 .. strlen(p)];
+ }
+
+ DYNCAST dyncast() const nothrow pure @nogc @safe
+ {
+ return DYNCAST.object;
+ }
+}
diff --git a/gcc/d/dmd/root/speller.c b/gcc/d/dmd/root/speller.c
deleted file mode 100644
index 3957c112a07..00000000000
--- a/gcc/d/dmd/root/speller.c
+++ /dev/null
@@ -1,231 +0,0 @@
-
-/* Copyright (C) 2010-2021 by The D Language Foundation, All Rights Reserved
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- * https://github.com/D-Programming-Language/dmd/blob/master/src/root/speller.c
- */
-
-#include "dsystem.h"
-#include "speller.h"
-
-const char idchars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
-
-/**************************************************
- * combine a new result from the spell checker to
- * find the one with the closest symbol with
- * respect to the cost defined by the search function
- * Input/Output:
- * p best found spelling (NULL if none found yet)
- * cost cost of p (INT_MAX if none found yet)
- * Input:
- * np new found spelling (NULL if none found)
- * ncost cost of np if non-NULL
- * Returns:
- * true if the cost is less or equal 0
- * false otherwise
- */
-bool combineSpellerResult(void*& p, int& cost, void* np, int ncost)
-{
- if (np && ncost < cost)
- {
- p = np;
- cost = ncost;
- if (cost <= 0)
- return true;
- }
- return false;
-}
-
-void *spellerY(const char *seed, size_t seedlen, fp_speller_t fp, void *fparg,
- const char *charset, size_t index, int* cost)
-{
- if (!seedlen)
- return NULL;
- assert(seed[seedlen] == 0);
-
- char tmp[30];
- char *buf;
- if (seedlen <= sizeof(tmp) - 2)
- buf = tmp;
- else
- {
- buf = (char *)alloca(seedlen + 2); // leave space for extra char
- if (!buf)
- return NULL; // no matches
- }
-
- memcpy(buf, seed, index);
- *cost = INT_MAX;
- void* p = NULL;
- int ncost = 0;
-
- /* Delete at seed[index] */
- if (index < seedlen)
- {
- memcpy(buf + index, seed + index + 1, seedlen - index);
- assert(buf[seedlen - 1] == 0);
- void* np = (*fp)(fparg, buf, &ncost);
- if (combineSpellerResult(p, *cost, np, ncost))
- return p;
- }
-
- if (charset && *charset)
- {
- /* Substitutions */
- if (index < seedlen)
- {
- memcpy(buf, seed, seedlen + 1);
- for (const char *s = charset; *s; s++)
- {
- buf[index] = *s;
-
- //printf("sub buf = '%s'\n", buf);
- void* np = (*fp)(fparg, buf, &ncost);
- if (combineSpellerResult(p, *cost, np, ncost))
- return p;
- }
- assert(buf[seedlen] == 0);
- }
-
- /* Insertions */
- memcpy (buf + index + 1, seed + index, seedlen + 1 - index);
-
- for (const char *s = charset; *s; s++)
- {
- buf[index] = *s;
-
- //printf("ins buf = '%s'\n", buf);
- void* np = (*fp)(fparg, buf, &ncost);
- if (combineSpellerResult(p, *cost, np, ncost))
- return p;
- }
- assert(buf[seedlen + 1] == 0);
- }
-
- return p; // return "best" result
-}
-
-void *spellerX(const char *seed, size_t seedlen, fp_speller_t fp, void *fparg,
- const char *charset, int flag)
-{
- if (!seedlen)
- return NULL;
-
- char tmp[30];
- char *buf;
- if (seedlen <= sizeof(tmp) - 2)
- buf = tmp;
- else
- {
- buf = (char *)alloca(seedlen + 2); // leave space for extra char
- if (!buf)
- return NULL; // no matches
- }
- int cost = INT_MAX, ncost = 0;
- void *p = NULL, *np;
-
- /* Deletions */
- memcpy(buf, seed + 1, seedlen);
- for (size_t i = 0; i < seedlen; i++)
- {
- //printf("del buf = '%s'\n", buf);
- if (flag)
- np = spellerY(buf, seedlen - 1, fp, fparg, charset, i, &ncost);
- else
- np = (*fp)(fparg, buf, &ncost);
- if (combineSpellerResult(p, cost, np, ncost))
- return p;
-
- buf[i] = seed[i];
- }
-
- /* Transpositions */
- if (!flag)
- {
- memcpy(buf, seed, seedlen + 1);
- for (size_t i = 0; i + 1 < seedlen; i++)
- {
- // swap [i] and [i + 1]
- buf[i] = seed[i + 1];
- buf[i + 1] = seed[i];
-
- //printf("tra buf = '%s'\n", buf);
- if (combineSpellerResult(p, cost, (*fp)(fparg, buf, &ncost), ncost))
- return p;
-
- buf[i] = seed[i];
- }
- }
-
- if (charset && *charset)
- {
- /* Substitutions */
- memcpy(buf, seed, seedlen + 1);
- for (size_t i = 0; i < seedlen; i++)
- {
- for (const char *s = charset; *s; s++)
- {
- buf[i] = *s;
-
- //printf("sub buf = '%s'\n", buf);
- if (flag)
- np = spellerY(buf, seedlen, fp, fparg, charset, i + 1, &ncost);
- else
- np = (*fp)(fparg, buf, &ncost);
- if (combineSpellerResult(p, cost, np, ncost))
- return p;
- }
- buf[i] = seed[i];
- }
-
- /* Insertions */
- memcpy(buf + 1, seed, seedlen + 1);
- for (size_t i = 0; i <= seedlen; i++) // yes, do seedlen+1 iterations
- {
- for (const char *s = charset; *s; s++)
- {
- buf[i] = *s;
-
- //printf("ins buf = '%s'\n", buf);
- if (flag)
- np = spellerY(buf, seedlen + 1, fp, fparg, charset, i + 1, &ncost);
- else
- np = (*fp)(fparg, buf, &ncost);
- if (combineSpellerResult(p, cost, np, ncost))
- return p;
- }
- buf[i] = seed[i]; // going past end of seed[] is ok, as we hit the 0
- }
- }
-
- return p; // return "best" result
-}
-
-/**************************************************
- * Looks for correct spelling.
- * Currently only looks a 'distance' of one from the seed[].
- * This does an exhaustive search, so can potentially be very slow.
- * Input:
- * seed wrongly spelled word
- * fp search function
- * fparg argument to search function
- * charset character set
- * Returns:
- * NULL no correct spellings found
- * void* value returned by fp() for first possible correct spelling
- */
-
-void *speller(const char *seed, fp_speller_t fp, void *fparg, const char *charset)
-{
- size_t seedlen = strlen(seed);
- size_t maxdist = seedlen < 4 ? seedlen / 2 : 2;
- for (size_t distance = 0; distance < maxdist; distance++)
- { void *p = spellerX(seed, seedlen, fp, fparg, charset, distance);
- if (p)
- return p;
-// if (seedlen > 10)
-// break;
- }
- return NULL; // didn't find it
-}
diff --git a/gcc/d/dmd/root/speller.d b/gcc/d/dmd/root/speller.d
new file mode 100644
index 00000000000..543005b34df
--- /dev/null
+++ b/gcc/d/dmd/root/speller.d
@@ -0,0 +1,303 @@
+/**
+ * Spell checker
+ *
+ * Does not have any dependencies on the rest of DMD.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/speller.d, root/_speller.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_speller.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/speller.d
+ */
+
+module dmd.root.speller;
+
+/**************************************************
+ * Looks for correct spelling.
+ * Looks a distance of up to two.
+ * This does an exhaustive search, so can potentially be very slow.
+ * Params:
+ * seed = wrongly spelled word
+ * dg = search delegate of the form `T delegate(const(char)[] p, out int cost)`
+ * Returns:
+ * T.init = no correct spellings found,
+ * otherwise the value returned by dg() for first possible correct spelling
+ */
+auto speller(alias dg)(const(char)[] seed)
+if (isSearchFunction!dg)
+{
+ const size_t maxdist = seed.length < 4 ? seed.length / 2 : 2;
+ foreach (distance; 0 .. maxdist)
+ {
+ if (auto p = spellerX!dg(seed, distance != 0))
+ return p;
+ // if (seedlen > 10)
+ // break;
+ }
+ return null; // didn't find it
+}
+
+private:
+
+import core.stdc.stdlib;
+import core.stdc.string;
+
+enum isSearchFunction(alias fun) = is(searchFunctionType!fun);
+alias searchFunctionType(alias fun) = typeof(() {int x; return fun("", x);}());
+
+/*************************************
+ * Spell check level 1.
+ * Params:
+ * dg = delegate that looks up string in dictionary AA and returns value found
+ * seed = starting string
+ * flag = if true, do 2 level lookup otherwise 1 level
+ * Returns:
+ * whatever dg returns, null if no match
+ */
+auto spellerX(alias dg)(const(char)[] seed, bool flag)
+{
+ if (!seed.length)
+ return null;
+
+ /* Need buffer to store trial strings in
+ */
+ char[30] tmp = void;
+ char[] buf;
+ if (seed.length <= tmp.sizeof - 1)
+ buf = tmp;
+ else
+ {
+ buf = (cast(char*)alloca(seed.length + 1))[0 .. seed.length + 1]; // leave space for extra char
+ if (!buf.ptr)
+ return null; // no matches
+ }
+
+ int cost = int.max;
+ searchFunctionType!dg p = null;
+
+ /* Deletions */
+ buf[0 .. seed.length - 1] = seed[1 .. $];
+ foreach (i; 0 .. seed.length)
+ {
+ //printf("del buf = '%s'\n", buf);
+ int ncost;
+ auto np = flag ? spellerY!dg(buf[0 .. seed.length - 1], i, ncost)
+ : dg(buf[0 .. seed.length - 1], ncost);
+ if (combineSpellerResult(p, cost, np, ncost))
+ return p;
+ buf[i] = seed[i];
+ }
+
+ /* Transpositions */
+ if (!flag)
+ {
+ buf[0 .. seed.length] = seed;
+ for (size_t i = 0; i + 1 < seed.length; i++)
+ {
+ // swap [i] and [i + 1]
+ buf[i] = seed[i + 1];
+ buf[i + 1] = seed[i];
+ //printf("tra buf = '%s'\n", buf);
+ int ncost;
+ auto np = dg(buf[0 .. seed.length], ncost);
+ if (combineSpellerResult(p, cost, np, ncost))
+ return p;
+ buf[i] = seed[i];
+ }
+ }
+
+ /* Substitutions */
+ buf[0 .. seed.length] = seed;
+ foreach (i; 0 .. seed.length)
+ {
+ foreach (s; idchars)
+ {
+ buf[i] = s;
+ //printf("sub buf = '%s'\n", buf);
+ int ncost;
+ auto np = flag ? spellerY!dg(buf[0 .. seed.length], i + 1, ncost)
+ : dg(buf[0 .. seed.length], ncost);
+ if (combineSpellerResult(p, cost, np, ncost))
+ return p;
+ }
+ buf[i] = seed[i];
+ }
+
+ /* Insertions */
+ buf[1 .. seed.length + 1] = seed;
+ foreach (i; 0 .. seed.length + 1) // yes, do seed.length+1 iterations
+ {
+ foreach (s; idchars)
+ {
+ buf[i] = s;
+ //printf("ins buf = '%s'\n", buf);
+ int ncost;
+ auto np = flag ? spellerY!dg(buf[0 .. seed.length + 1], i + 1, ncost)
+ : dg(buf[0 .. seed.length + 1], ncost);
+ if (combineSpellerResult(p, cost, np, ncost))
+ return p;
+ }
+ if (i < seed.length)
+ buf[i] = seed[i];
+ }
+
+ return p; // return "best" result
+}
+
+/**********************************************
+ * Do second level of spell matching.
+ * Params:
+ * dg = delegate that looks up string in dictionary AA and returns value found
+ * seed = starting string
+ * index = index into seed[] that is where we will mutate it
+ * cost = set to cost of match
+ * Returns:
+ * whatever dg returns, null if no match
+ */
+auto spellerY(alias dg)(const(char)[] seed, size_t index, out int cost)
+{
+ if (!seed.length)
+ return null;
+
+ /* Allocate a buf to store the new string to play with, needs
+ * space for an extra char for insertions
+ */
+ char[30] tmp = void; // stack allocations are fastest
+ char[] buf;
+ if (seed.length <= tmp.sizeof - 1)
+ buf = tmp;
+ else
+ {
+ buf = (cast(char*)alloca(seed.length + 1))[0 .. seed.length + 1]; // leave space for extra char
+ if (!buf.ptr)
+ return null; // no matches
+ }
+ buf[0 .. index] = seed[0 .. index];
+
+ cost = int.max; // start with worst possible match
+ searchFunctionType!dg p = null;
+
+ /* Delete character at seed[index] */
+ if (index < seed.length)
+ {
+ buf[index .. seed.length - 1] = seed[index + 1 .. $]; // seed[] with deleted character
+ int ncost;
+ auto np = dg(buf[0 .. seed.length - 1], ncost); // look it up
+ if (combineSpellerResult(p, cost, np, ncost)) // compare with prev match
+ return p; // cannot get any better
+ }
+
+ /* Substitute character at seed[index] */
+ if (index < seed.length)
+ {
+ buf[0 .. seed.length] = seed;
+ foreach (s; idchars)
+ {
+ buf[index] = s; // seed[] with substituted character
+ //printf("sub buf = '%s'\n", buf);
+ int ncost;
+ auto np = dg(buf[0 .. seed.length], ncost);
+ if (combineSpellerResult(p, cost, np, ncost))
+ return p;
+ }
+ }
+
+ /* Insert character at seed[index] */
+ buf[index + 1 .. seed.length + 1] = seed[index .. $];
+ foreach (s; idchars)
+ {
+ buf[index] = s;
+ //printf("ins buf = '%s'\n", buf);
+ int ncost;
+ auto np = dg(buf[0 .. seed.length + 1], ncost);
+ if (combineSpellerResult(p, cost, np, ncost))
+ return p;
+ }
+ return p; // return "best" result
+}
+
+
+/* Characters used to substitute ones in the string we're checking
+ * the spelling on.
+ */
+immutable string idchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+
+/**************************************************
+ * Combine a new result from the spell checker to
+ * find the one with the closest symbol with
+ * respect to the cost defined by the search function
+ * Params:
+ * p = best found spelling so far, T.init if none found yet.
+ * If np is better, p is replaced with np
+ * cost = cost of p (int.max if none found yet).
+ * If np is better, cost is replaced with ncost
+ * np = current spelling to check against p, T.init if none
+ * ncost = cost of np if np is not T.init
+ * Returns:
+ * true if the cost is less or equal 0, meaning we can stop looking
+ * false otherwise
+ */
+bool combineSpellerResult(T)(ref T p, ref int cost, T np, int ncost)
+{
+ if (np && ncost < cost) // if np is better
+ {
+ p = np; // np is new best match
+ cost = ncost;
+ if (cost <= 0)
+ return true; // meaning we can stop looking
+ }
+ return false;
+}
+
+/************************************* Tests ***********************/
+
+unittest
+{
+ static immutable string[][] cases =
+ [
+ ["hello", "hell", "y"],
+ ["hello", "hel", "y"],
+ ["hello", "ello", "y"],
+ ["hello", "llo", "y"],
+ ["hello", "hellox", "y"],
+ ["hello", "helloxy", "y"],
+ ["hello", "xhello", "y"],
+ ["hello", "xyhello", "y"],
+ ["hello", "ehllo", "y"],
+ ["hello", "helol", "y"],
+ ["hello", "abcd", "n"],
+ ["hello", "helxxlo", "y"],
+ ["hello", "ehlxxlo", "n"],
+ ["hello", "heaao", "y"],
+ ["_123456789_123456789_123456789_123456789", "_123456789_123456789_123456789_12345678", "y"],
+ ];
+ //printf("unittest_speller()\n");
+
+ string dgarg;
+
+ string speller_test(const(char)[] s, ref int cost)
+ {
+ assert(s[$-1] != '\0');
+ //printf("speller_test(%s, %s)\n", dgarg, s);
+ cost = 0;
+ if (dgarg == s)
+ return dgarg;
+ return null;
+ }
+
+ dgarg = "hell";
+ auto p = speller!speller_test("hello");
+ assert(p !is null);
+ foreach (testCase; cases)
+ {
+ //printf("case [%d]\n", i);
+ dgarg = testCase[1];
+ auto p2 = speller!speller_test(testCase[0]);
+ if (p2)
+ assert(testCase[2][0] == 'y');
+ else
+ assert(testCase[2][0] == 'n');
+ }
+ //printf("unittest_speller() success\n");
+}
diff --git a/gcc/d/dmd/root/string.d b/gcc/d/dmd/root/string.d
new file mode 100644
index 00000000000..73fe562f356
--- /dev/null
+++ b/gcc/d/dmd/root/string.d
@@ -0,0 +1,293 @@
+/**
+ * Contains various string related functions.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/string.d, root/_string.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_string.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/string.d
+ */
+module dmd.root.string;
+
+/// Slices a `\0`-terminated C-string, excluding the terminator
+inout(char)[] toDString (inout(char)* s) pure nothrow @nogc
+{
+ import core.stdc.string : strlen;
+ return s ? s[0 .. strlen(s)] : null;
+}
+
+/**
+Compare two slices for equality, in a case-insensitive way
+
+Comparison is based on `char` and does not do decoding.
+As a result, it's only really accurate for plain ASCII strings.
+
+Params:
+s1 = string to compare
+s2 = string to compare
+
+Returns:
+`true` if `s1 == s2` regardless of case
+*/
+extern(D) static bool iequals(const(char)[] s1, const(char)[] s2) pure nothrow @nogc
+{
+ import core.stdc.ctype : toupper;
+
+ if (s1.length != s2.length)
+ return false;
+
+ foreach (idx, c1; s1)
+ {
+ // Since we did a length check, it is safe to bypass bounds checking
+ const c2 = s2.ptr[idx];
+ if (c1 != c2)
+ if (toupper(c1) != toupper(c2))
+ return false;
+ }
+ return true;
+}
+
+/**
+Copy the content of `src` into a C-string ('\0' terminated) then call `dg`
+
+The intent of this function is to provide an allocation-less
+way to call a C function using a D slice.
+The function internally allocates a buffer if needed, but frees it on exit.
+
+Note:
+The argument to `dg` is `scope`. To keep the data around after `dg` exits,
+one has to copy it.
+
+Params:
+src = Slice to use to call the C function
+dg = Delegate to call afterwards
+
+Returns:
+The return value of `T`
+*/
+auto toCStringThen(alias dg)(const(char)[] src) nothrow
+{
+ import dmd.root.rmem : mem;
+
+ const len = src.length + 1;
+ char[512] small = void;
+ scope ptr = (src.length < (small.length - 1))
+ ? small[0 .. len]
+ : (cast(char*)mem.xmalloc(len))[0 .. len];
+ scope (exit)
+ {
+ if (&ptr[0] != &small[0])
+ mem.xfree(&ptr[0]);
+ }
+ ptr[0 .. src.length] = src[];
+ ptr[src.length] = '\0';
+ return dg(ptr);
+}
+
+unittest
+{
+ assert("Hello world".toCStringThen!((v) => v == "Hello world\0"));
+ assert("Hello world\0".toCStringThen!((v) => v == "Hello world\0\0"));
+ assert(null.toCStringThen!((v) => v == "\0"));
+}
+
+/**
+ * Strips one leading line terminator of the given string.
+ *
+ * The following are what the Unicode standard considers as line terminators:
+ *
+ * | Name | D Escape Sequence | Unicode Code Point |
+ * |---------------------|-------------------|--------------------|
+ * | Line feed | `\n` | `U+000A` |
+ * | Line tabulation | `\v` | `U+000B` |
+ * | Form feed | `\f` | `U+000C` |
+ * | Carriage return | `\r` | `U+000D` |
+ * | Next line | | `U+0085` |
+ * | Line separator | | `U+2028` |
+ * | Paragraph separator | | `U+2029` |
+ *
+ * This function will also strip `\r\n`.
+ */
+string stripLeadingLineTerminator(string str) pure nothrow @nogc @safe
+{
+ enum nextLine = "\xC2\x85";
+ enum lineSeparator = "\xE2\x80\xA8";
+ enum paragraphSeparator = "\xE2\x80\xA9";
+
+ static assert(lineSeparator.length == paragraphSeparator.length);
+
+ if (str.length == 0)
+ return str;
+
+ switch (str[0])
+ {
+ case '\r':
+ {
+ if (str.length >= 2 && str[1] == '\n')
+ return str[2 .. $];
+ goto case;
+ }
+ case '\v', '\f', '\n': return str[1 .. $];
+
+ case nextLine[0]:
+ {
+ if (str.length >= 2 && str[0 .. 2] == nextLine)
+ return str[2 .. $];
+
+ return str;
+ }
+
+ case lineSeparator[0]:
+ {
+ if (str.length >= lineSeparator.length)
+ {
+ const prefix = str[0 .. lineSeparator.length];
+
+ if (prefix == lineSeparator || prefix == paragraphSeparator)
+ return str[lineSeparator.length .. $];
+ }
+
+ return str;
+ }
+
+ default: return str;
+ }
+}
+
+unittest
+{
+ assert("".stripLeadingLineTerminator == "");
+ assert("foo".stripLeadingLineTerminator == "foo");
+ assert("\xC2foo".stripLeadingLineTerminator == "\xC2foo");
+ assert("\xE2foo".stripLeadingLineTerminator == "\xE2foo");
+ assert("\nfoo".stripLeadingLineTerminator == "foo");
+ assert("\vfoo".stripLeadingLineTerminator == "foo");
+ assert("\ffoo".stripLeadingLineTerminator == "foo");
+ assert("\rfoo".stripLeadingLineTerminator == "foo");
+ assert("\u0085foo".stripLeadingLineTerminator == "foo");
+ assert("\u2028foo".stripLeadingLineTerminator == "foo");
+ assert("\u2029foo".stripLeadingLineTerminator == "foo");
+ assert("\n\rfoo".stripLeadingLineTerminator == "\rfoo");
+ assert("\r\nfoo".stripLeadingLineTerminator == "foo");
+}
+
+/**
+ * A string comparison functions that returns the same result as strcmp
+ *
+ * Note: Strings are compared based on their ASCII values, no UTF-8 decoding.
+ *
+ * Some C functions (e.g. `qsort`) require a `int` result for comparison.
+ * See_Also: Druntime's `core.internal.string`
+ */
+int dstrcmp()( scope const char[] s1, scope const char[] s2 ) @trusted
+{
+ immutable len = s1.length <= s2.length ? s1.length : s2.length;
+ if (__ctfe)
+ {
+ foreach (const u; 0 .. len)
+ {
+ if (s1[u] != s2[u])
+ return s1[u] > s2[u] ? 1 : -1;
+ }
+ }
+ else
+ {
+ import core.stdc.string : memcmp;
+
+ const ret = memcmp( s1.ptr, s2.ptr, len );
+ if ( ret )
+ return ret;
+ }
+ return s1.length < s2.length ? -1 : (s1.length > s2.length);
+}
+
+//
+unittest
+{
+ assert(dstrcmp("Fraise", "Fraise") == 0);
+ assert(dstrcmp("Baguette", "Croissant") == -1);
+ assert(dstrcmp("Croissant", "Baguette") == 1);
+
+ static assert(dstrcmp("Baguette", "Croissant") == -1);
+
+ // UTF-8 decoding for the CT variant
+ assert(dstrcmp("안녕하세요!", "안녕하세요!") == 0);
+ static assert(dstrcmp("안녕하세요!", "안녕하세요!") == 0);
+}
+
+/**
+ * Infers the length `N` of a string literal and coerces its type to a static
+ * array with length `N + 1`. Returns the string with a null character appended
+ * to the end.
+ *
+ * Params:
+ * literal = string literal
+ *
+ * Notes:
+ * - LDC produces quite optimal code for short strings:
+ * - https://d.godbolt.org/z/M69Z1g
+ * - https://gist.github.com/PetarKirov/338e4ab9292b6b2b311a3070572a07fb (backup URL)
+*/
+char[N + 1] toStaticArray(size_t N)(scope const(char)[N] literal)
+{
+ char[N+1] result = void;
+ result[0..N] = literal[0..N];
+ result[N] = 0;
+ return result;
+}
+
+///
+@safe pure nothrow @nogc
+unittest
+{
+ auto m = "123".toStaticArray;
+ const c = "123".toStaticArray;
+ immutable i = "123".toStaticArray;
+ enum e = "123".toStaticArray;
+
+ assert(m == "123\0");
+ assert(c == "123\0");
+ assert(i == "123\0");
+ static assert(e == "123\0");
+
+ const empty = "".toStaticArray;
+ static assert(empty.length == 1);
+ static assert(empty[0] == '\0');
+}
+
+/**
+ * Checks if C string `p` starts with `needle`.
+ * Params:
+ * p = the C string to check
+ * needle = the string to look for
+ * Returns:
+ * `true` if `p` starts with `needle`
+ */
+@system pure nothrow @nogc
+bool startsWith(scope const(char)* p, scope const(char)[] needle)
+in { assert(p && needle.ptr); }
+do
+{
+ foreach (const c; needle)
+ {
+ assert(c);
+ if (c != *p)
+ return false;
+ ++p;
+ }
+ return true;
+}
+
+///
+@system pure nothrow @nogc
+unittest
+{
+ const buf = "123".toStaticArray;
+ const ptr = &buf[0];
+ assert(ptr.startsWith(""));
+ assert(ptr.startsWith("1"));
+ assert(ptr.startsWith("12"));
+ assert(ptr.startsWith("123"));
+ assert(!ptr.startsWith("1234"));
+}
diff --git a/gcc/d/dmd/root/stringtable.c b/gcc/d/dmd/root/stringtable.c
deleted file mode 100644
index fe14807791d..00000000000
--- a/gcc/d/dmd/root/stringtable.c
+++ /dev/null
@@ -1,196 +0,0 @@
-
-/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- * https://github.com/D-Programming-Language/dmd/blob/master/src/root/stringtable.c
- */
-
-#include "dsystem.h" // uint{8|16|32}_t, memcpy()
-#include "root.h"
-#include "rmem.h" // mem
-#include "stringtable.h"
-#include "hash.h"
-
-#define POOL_BITS 12
-#define POOL_SIZE (1U << POOL_BITS)
-
-struct StringEntry
-{
- uint32_t hash;
- uint32_t vptr;
-};
-
-uint32_t StringTable::allocValue(const char *s, size_t length, void *ptrvalue)
-{
- const size_t nbytes = sizeof(StringValue) + length + 1;
-
- if (!npools || nfill + nbytes > POOL_SIZE)
- {
- pools = (uint8_t **)mem.xrealloc(pools, ++npools * sizeof(pools[0]));
- pools[npools - 1] = (uint8_t *)mem.xmalloc(nbytes > POOL_SIZE ? nbytes : POOL_SIZE);
- nfill = 0;
- }
-
- StringValue *sv = (StringValue *)&pools[npools - 1][nfill];
- sv->ptrvalue = ptrvalue;
- sv->length = length;
- ::memcpy(sv->lstring(), s, length);
- sv->lstring()[length] = 0;
-
- const uint32_t vptr = (uint32_t)(npools << POOL_BITS | nfill);
- nfill += nbytes + (-nbytes & 7); // align to 8 bytes
- return vptr;
-}
-
-StringValue *StringTable::getValue(uint32_t vptr)
-{
- if (!vptr) return NULL;
-
- const size_t idx = (vptr >> POOL_BITS) - 1;
- const size_t off = vptr & (POOL_SIZE - 1);
- return (StringValue *)&pools[idx][off];
-}
-
-static size_t nextpow2(size_t val)
-{
- size_t res = 1;
- while (res < val)
- res <<= 1;
- return res;
-}
-
-static const double loadFactor = 0.8;
-
-void StringTable::_init(size_t size)
-{
- size = nextpow2((size_t)(size / loadFactor));
- if (size < 32) size = 32;
- table = (StringEntry *)mem.xcalloc(size, sizeof(table[0]));
- tabledim = size;
- pools = NULL;
- npools = nfill = 0;
- count = 0;
-}
-
-void StringTable::reset(size_t size)
-{
- for (size_t i = 0; i < npools; ++i)
- mem.xfree(pools[i]);
-
- mem.xfree(table);
- mem.xfree(pools);
- table = NULL;
- pools = NULL;
- _init(size);
-}
-
-StringTable::~StringTable()
-{
- for (size_t i = 0; i < npools; ++i)
- mem.xfree(pools[i]);
-
- mem.xfree(table);
- mem.xfree(pools);
- table = NULL;
- pools = NULL;
-}
-
-size_t StringTable::findSlot(hash_t hash, const char *s, size_t length)
-{
- // quadratic probing using triangular numbers
- // http://stackoverflow.com/questions/2348187/moving-from-linear-probing-to-quadratic-probing-hash-collisons/2349774#2349774
- for (size_t i = hash & (tabledim - 1), j = 1; ;++j)
- {
- StringValue *sv;
- if (!table[i].vptr ||
- (table[i].hash == hash &&
- (sv = getValue(table[i].vptr))->length == length &&
- ::memcmp(s, sv->lstring(), length) == 0))
- return i;
- i = (i + j) & (tabledim - 1);
- }
-}
-
-StringValue *StringTable::lookup(const char *s, size_t length)
-{
- const hash_t hash = calcHash(s, length);
- const size_t i = findSlot(hash, s, length);
- // printf("lookup %.*s %p\n", (int)length, s, table[i].value ?: NULL);
- return getValue(table[i].vptr);
-}
-
-StringValue *StringTable::update(const char *s, size_t length)
-{
- const hash_t hash = calcHash(s, length);
- size_t i = findSlot(hash, s, length);
- if (!table[i].vptr)
- {
- if (++count > tabledim * loadFactor)
- {
- grow();
- i = findSlot(hash, s, length);
- }
- table[i].hash = hash;
- table[i].vptr = allocValue(s, length, NULL);
- }
- // printf("update %.*s %p\n", (int)length, s, table[i].value ?: NULL);
- return getValue(table[i].vptr);
-}
-
-StringValue *StringTable::insert(const char *s, size_t length, void *ptrvalue)
-{
- const hash_t hash = calcHash(s, length);
- size_t i = findSlot(hash, s, length);
- if (table[i].vptr)
- return NULL; // already in table
- if (++count > tabledim * loadFactor)
- {
- grow();
- i = findSlot(hash, s, length);
- }
- table[i].hash = hash;
- table[i].vptr = allocValue(s, length, ptrvalue);
- // printf("insert %.*s %p\n", (int)length, s, table[i].value ?: NULL);
- return getValue(table[i].vptr);
-}
-
-void StringTable::grow()
-{
- const size_t odim = tabledim;
- StringEntry *otab = table;
- tabledim *= 2;
- table = (StringEntry *)mem.xcalloc(tabledim, sizeof(table[0]));
-
- for (size_t i = 0; i < odim; ++i)
- {
- StringEntry *se = &otab[i];
- if (!se->vptr) continue;
- StringValue *sv = getValue(se->vptr);
- table[findSlot(se->hash, sv->lstring(), sv->length)] = *se;
- }
- mem.xfree(otab);
-}
-
-/********************************
- * Walk the contents of the string table,
- * calling fp for each entry.
- * Params:
- * fp = function to call. Returns !=0 to stop
- * Returns:
- * last return value of fp call
- */
-int StringTable::apply(int (*fp)(StringValue *))
-{
- for (size_t i = 0; i < tabledim; ++i)
- {
- StringEntry *se = &table[i];
- if (!se->vptr) continue;
- StringValue *sv = getValue(se->vptr);
- int result = (*fp)(sv);
- if (result)
- return result;
- }
- return 0;
-}
-
diff --git a/gcc/d/dmd/root/stringtable.d b/gcc/d/dmd/root/stringtable.d
new file mode 100644
index 00000000000..42b26e26c38
--- /dev/null
+++ b/gcc/d/dmd/root/stringtable.d
@@ -0,0 +1,411 @@
+/**
+ * A specialized associative array with string keys stored in a variable length structure.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/stringtable.d, root/_stringtable.d)
+ * Documentation: https://dlang.org/phobos/dmd_root_stringtable.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/stringtable.d
+ */
+
+module dmd.root.stringtable;
+
+import core.stdc.string;
+import dmd.root.rmem, dmd.root.hash;
+
+private enum POOL_BITS = 12;
+private enum POOL_SIZE = (1U << POOL_BITS);
+
+/*
+Returns the smallest integer power of 2 larger than val.
+if val > 2^^63 on 64-bit targets or val > 2^^31 on 32-bit targets it enters an
+endless loop because of overflow.
+*/
+private size_t nextpow2(size_t val) @nogc nothrow pure @safe
+{
+ size_t res = 1;
+ while (res < val)
+ res <<= 1;
+ return res;
+}
+
+unittest
+{
+ assert(nextpow2(0) == 1);
+ assert(nextpow2(0xFFFF) == (1 << 16));
+ assert(nextpow2(size_t.max / 2) == size_t.max / 2 + 1);
+ // note: nextpow2((1UL << 63) + 1) results in an endless loop
+}
+
+private enum loadFactorNumerator = 8;
+private enum loadFactorDenominator = 10; // for a load factor of 0.8
+
+private struct StringEntry
+{
+ uint hash;
+ uint vptr;
+}
+
+/********************************
+ * StringValue is a variable-length structure. It has neither proper c'tors nor a
+ * factory method because the only thing which should be creating these is StringTable.
+ * The string characters are stored in memory immediately after the StringValue struct.
+ */
+struct StringValue(T)
+{
+ T value; //T is/should typically be a pointer or a slice
+ private size_t length;
+ /+
+ char[length] chars; // the string characters are stored here
+ +/
+
+ char* lstring() @nogc nothrow pure return
+ {
+ return cast(char*)(&this + 1);
+ }
+
+ size_t len() const @nogc nothrow pure @safe
+ {
+ return length;
+ }
+
+ const(char)* toDchars() const @nogc nothrow pure return
+ {
+ return cast(const(char)*)(&this + 1);
+ }
+
+ /// Returns: The content of this entry as a D slice
+ const(char)[] toString() const @nogc nothrow pure
+ {
+ return (cast(inout(char)*)(&this + 1))[0 .. length];
+ }
+}
+
+struct StringTable(T)
+{
+private:
+ StringEntry[] table;
+ ubyte*[] pools;
+ size_t nfill;
+ size_t count;
+ size_t countTrigger; // amount which will trigger growing the table
+
+public:
+ void _init(size_t size = 0) nothrow pure
+ {
+ size = nextpow2((size * loadFactorDenominator) / loadFactorNumerator);
+ if (size < 32)
+ size = 32;
+ table = (cast(StringEntry*)mem.xcalloc(size, (table[0]).sizeof))[0 .. size];
+ countTrigger = (table.length * loadFactorNumerator) / loadFactorDenominator;
+ pools = null;
+ nfill = 0;
+ count = 0;
+ }
+
+ void reset(size_t size = 0) nothrow pure
+ {
+ freeMem();
+ _init(size);
+ }
+
+ ~this() nothrow pure
+ {
+ freeMem();
+ }
+
+ /**
+ Looks up the given string in the string table and returns its associated
+ value.
+
+ Params:
+ s = the string to look up
+ length = the length of $(D_PARAM s)
+ str = the string to look up
+
+ Returns: the string's associated value, or `null` if the string doesn't
+ exist in the string table
+ */
+ inout(StringValue!T)* lookup(scope const(char)[] str) inout @nogc nothrow pure
+ {
+ const(size_t) hash = calcHash(str);
+ const(size_t) i = findSlot(hash, str);
+ // printf("lookup %.*s %p\n", cast(int)str.length, str.ptr, table[i].value ?: null);
+ return getValue(table[i].vptr);
+ }
+
+ /// ditto
+ inout(StringValue!T)* lookup(scope const(char)* s, size_t length) inout @nogc nothrow pure
+ {
+ return lookup(s[0 .. length]);
+ }
+
+ /**
+ Inserts the given string and the given associated value into the string
+ table.
+
+ Params:
+ s = the string to insert
+ length = the length of $(D_PARAM s)
+ ptrvalue = the value to associate with the inserted string
+ str = the string to insert
+ value = the value to associate with the inserted string
+
+ Returns: the newly inserted value, or `null` if the string table already
+ contains the string
+ */
+ StringValue!(T)* insert(scope const(char)[] str, T value) nothrow pure
+ {
+ const(size_t) hash = calcHash(str);
+ size_t i = findSlot(hash, str);
+ if (table[i].vptr)
+ return null; // already in table
+ if (++count > countTrigger)
+ {
+ grow();
+ i = findSlot(hash, str);
+ }
+ table[i].hash = hash;
+ table[i].vptr = allocValue(str, value);
+ // printf("insert %.*s %p\n", cast(int)str.length, str.ptr, table[i].value ?: NULL);
+ return getValue(table[i].vptr);
+ }
+
+ /// ditto
+ StringValue!(T)* insert(scope const(char)* s, size_t length, T value) nothrow pure
+ {
+ return insert(s[0 .. length], value);
+ }
+
+ StringValue!(T)* update(scope const(char)[] str) nothrow pure
+ {
+ const(size_t) hash = calcHash(str);
+ size_t i = findSlot(hash, str);
+ if (!table[i].vptr)
+ {
+ if (++count > countTrigger)
+ {
+ grow();
+ i = findSlot(hash, str);
+ }
+ table[i].hash = hash;
+ table[i].vptr = allocValue(str, T.init);
+ }
+ // printf("update %.*s %p\n", cast(int)str.length, str.ptr, table[i].value ?: NULL);
+ return getValue(table[i].vptr);
+ }
+
+ StringValue!(T)* update(scope const(char)* s, size_t length) nothrow pure
+ {
+ return update(s[0 .. length]);
+ }
+
+ /********************************
+ * Walk the contents of the string table,
+ * calling fp for each entry.
+ * Params:
+ * fp = function to call. Returns !=0 to stop
+ * Returns:
+ * last return value of fp call
+ */
+ int apply(int function(const(StringValue!T)*) nothrow fp) nothrow
+ {
+ foreach (const se; table)
+ {
+ if (!se.vptr)
+ continue;
+ const sv = getValue(se.vptr);
+ int result = (*fp)(sv);
+ if (result)
+ return result;
+ }
+ return 0;
+ }
+
+ /// ditto
+ extern(D) int opApply(scope int delegate(const(StringValue!T)*) nothrow dg) nothrow
+ {
+ foreach (const se; table)
+ {
+ if (!se.vptr)
+ continue;
+ const sv = getValue(se.vptr);
+ int result = dg(sv);
+ if (result)
+ return result;
+ }
+ return 0;
+ }
+
+private:
+ /// Free all memory in use by this StringTable
+ void freeMem() nothrow pure
+ {
+ foreach (pool; pools)
+ mem.xfree(pool);
+ mem.xfree(table.ptr);
+ mem.xfree(pools.ptr);
+ table = null;
+ pools = null;
+ }
+
+ // Note that a copy is made of str
+ uint allocValue(scope const(char)[] str, T value) nothrow pure
+ {
+ const(size_t) nbytes = (StringValue!T).sizeof + str.length + 1;
+ if (!pools.length || nfill + nbytes > POOL_SIZE)
+ {
+ pools = (cast(ubyte**) mem.xrealloc(pools.ptr, (pools.length + 1) * (pools[0]).sizeof))[0 .. pools.length + 1];
+ pools[$-1] = cast(ubyte*) mem.xmalloc(nbytes > POOL_SIZE ? nbytes : POOL_SIZE);
+ if (mem.isGCEnabled)
+ memset(pools[$ - 1], 0xff, POOL_SIZE); // 0xff less likely to produce GC pointer
+ nfill = 0;
+ }
+ StringValue!(T)* sv = cast(StringValue!(T)*)&pools[$ - 1][nfill];
+ sv.value = value;
+ sv.length = str.length;
+ .memcpy(sv.lstring(), str.ptr, str.length);
+ sv.lstring()[str.length] = 0;
+ const(uint) vptr = cast(uint)(pools.length << POOL_BITS | nfill);
+ nfill += nbytes + (-nbytes & 7); // align to 8 bytes
+ return vptr;
+ }
+
+ inout(StringValue!T)* getValue(uint vptr) inout @nogc nothrow pure
+ {
+ if (!vptr)
+ return null;
+ const(size_t) idx = (vptr >> POOL_BITS) - 1;
+ const(size_t) off = vptr & POOL_SIZE - 1;
+ return cast(inout(StringValue!T)*)&pools[idx][off];
+ }
+
+ size_t findSlot(hash_t hash, scope const(char)[] str) const @nogc nothrow pure
+ {
+ // quadratic probing using triangular numbers
+ // http://stackoverflow.com/questions/2348187/moving-from-linear-probing-to-quadratic-probing-hash-collisons/2349774#2349774
+ for (size_t i = hash & (table.length - 1), j = 1;; ++j)
+ {
+ const(StringValue!T)* sv;
+ auto vptr = table[i].vptr;
+ if (!vptr || table[i].hash == hash && (sv = getValue(vptr)).length == str.length && .memcmp(str.ptr, sv.toDchars(), str.length) == 0)
+ return i;
+ i = (i + j) & (table.length - 1);
+ }
+ }
+
+ void grow() nothrow pure
+ {
+ const odim = table.length;
+ auto otab = table;
+ const ndim = table.length * 2;
+ countTrigger = (ndim * loadFactorNumerator) / loadFactorDenominator;
+ table = (cast(StringEntry*)mem.xcalloc_noscan(ndim, (table[0]).sizeof))[0 .. ndim];
+ foreach (const se; otab[0 .. odim])
+ {
+ if (!se.vptr)
+ continue;
+ const sv = getValue(se.vptr);
+ table[findSlot(se.hash, sv.toString())] = se;
+ }
+ mem.xfree(otab.ptr);
+ }
+}
+
+nothrow unittest
+{
+ StringTable!(const(char)*) tab;
+ tab._init(10);
+
+ // construct two strings with the same text, but a different pointer
+ const(char)[6] fooBuffer = "foofoo";
+ const(char)[] foo = fooBuffer[0 .. 3];
+ const(char)[] fooAltPtr = fooBuffer[3 .. 6];
+
+ assert(foo.ptr != fooAltPtr.ptr);
+
+ // first insertion returns value
+ assert(tab.insert(foo, foo.ptr).value == foo.ptr);
+
+ // subsequent insertion of same string return null
+ assert(tab.insert(foo.ptr, foo.length, foo.ptr) == null);
+ assert(tab.insert(fooAltPtr, foo.ptr) == null);
+
+ const lookup = tab.lookup("foo");
+ assert(lookup.value == foo.ptr);
+ assert(lookup.len == 3);
+ assert(lookup.toString() == "foo");
+
+ assert(tab.lookup("bar") == null);
+ tab.update("bar".ptr, "bar".length);
+ assert(tab.lookup("bar").value == null);
+
+ tab.reset(0);
+ assert(tab.lookup("foo".ptr, "foo".length) == null);
+ //tab.insert("bar");
+}
+
+nothrow unittest
+{
+ StringTable!(void*) tab;
+ tab._init(100);
+
+ enum testCount = 2000;
+
+ char[2 * testCount] buf;
+
+ foreach(i; 0 .. testCount)
+ {
+ buf[i * 2 + 0] = cast(char) (i % 256);
+ buf[i * 2 + 1] = cast(char) (i / 256);
+ auto toInsert = cast(const(char)[]) buf[i * 2 .. i * 2 + 2];
+ tab.insert(toInsert, cast(void*) i);
+ }
+
+ foreach(i; 0 .. testCount)
+ {
+ auto toLookup = cast(const(char)[]) buf[i * 2 .. i * 2 + 2];
+ assert(tab.lookup(toLookup).value == cast(void*) i);
+ }
+}
+
+nothrow unittest
+{
+ StringTable!(int) tab;
+ tab._init(10);
+ tab.insert("foo", 4);
+ tab.insert("bar", 6);
+
+ static int resultFp = 0;
+ int resultDg = 0;
+ static bool returnImmediately = false;
+
+ int function(const(StringValue!int)*) nothrow applyFunc = (const(StringValue!int)* s)
+ {
+ resultFp += s.value;
+ return returnImmediately;
+ };
+
+ scope int delegate(const(StringValue!int)*) nothrow applyDeleg = (const(StringValue!int)* s)
+ {
+ resultDg += s.value;
+ return returnImmediately;
+ };
+
+ tab.apply(applyFunc);
+ tab.opApply(applyDeleg);
+
+ assert(resultDg == 10);
+ assert(resultFp == 10);
+
+ returnImmediately = true;
+
+ tab.apply(applyFunc);
+ tab.opApply(applyDeleg);
+
+ // Order of string table iteration is not specified, either foo or bar could
+ // have been visited first.
+ assert(resultDg == 14 || resultDg == 16);
+ assert(resultFp == 14 || resultFp == 16);
+}
diff --git a/gcc/d/dmd/safe.c b/gcc/d/dmd/safe.c
deleted file mode 100644
index 7d83dd170ad..00000000000
--- a/gcc/d/dmd/safe.c
+++ /dev/null
@@ -1,168 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/safe.c
- */
-
-#include "mars.h"
-#include "expression.h"
-#include "scope.h"
-#include "aggregate.h"
-#include "target.h"
-
-bool MODimplicitConv(MOD modfrom, MOD modto);
-
-/*************************************************************
- * Check for unsafe access in @safe code:
- * 1. read overlapped pointers
- * 2. write misaligned pointers
- * 3. write overlapped storage classes
- * Print error if unsafe.
- * Params:
- * sc = scope
- * e = expression to check
- * readonly = if access is read-only
- * printmsg = print error message if true
- * Returns:
- * true if error
- */
-
-bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg)
-{
- if (e->op != TOKdotvar)
- return false;
- DotVarExp *dve = (DotVarExp *)e;
- if (VarDeclaration *v = dve->var->isVarDeclaration())
- {
- if (sc->intypeof || !sc->func || !sc->func->isSafeBypassingInference())
- return false;
-
- AggregateDeclaration *ad = v->toParent2()->isAggregateDeclaration();
- if (!ad)
- return false;
-
- if (v->overlapped && v->type->hasPointers() && sc->func->setUnsafe())
- {
- if (printmsg)
- e->error("field %s.%s cannot access pointers in @safe code that overlap other fields",
- ad->toChars(), v->toChars());
- return true;
- }
-
- if (readonly || !e->type->isMutable())
- return false;
-
- if (v->type->hasPointers() && v->type->toBasetype()->ty != Tstruct)
- {
- if ((ad->type->alignment() < target.ptrsize ||
- (v->offset & (target.ptrsize - 1))) &&
- sc->func->setUnsafe())
- {
- if (printmsg)
- e->error("field %s.%s cannot modify misaligned pointers in @safe code",
- ad->toChars(), v->toChars());
- return true;
- }
- }
-
- if (v->overlapUnsafe && sc->func->setUnsafe())
- {
- if (printmsg)
- e->error("field %s.%s cannot modify fields in @safe code that overlap fields with other storage classes",
- ad->toChars(), v->toChars());
- return true;
- }
- }
- return false;
-}
-
-
-/**********************************************
- * Determine if it is @safe to cast e from tfrom to tto.
- * Params:
- * e = expression to be cast
- * tfrom = type of e
- * tto = type to cast e to
- * Returns:
- * true if @safe
- */
-bool isSafeCast(Expression *e, Type *tfrom, Type *tto)
-{
- // Implicit conversions are always safe
- if (tfrom->implicitConvTo(tto))
- return true;
-
- if (!tto->hasPointers())
- return true;
-
- Type *ttob = tto->toBasetype();
-
- if (ttob->ty == Tclass && tfrom->ty == Tclass)
- {
- ClassDeclaration *cdfrom = tfrom->isClassHandle();
- ClassDeclaration *cdto = ttob->isClassHandle();
-
- int offset;
- if (!cdfrom->isBaseOf(cdto, &offset))
- return false;
-
- if (cdfrom->isCPPinterface() || cdto->isCPPinterface())
- return false;
-
- if (!MODimplicitConv(tfrom->mod, ttob->mod))
- return false;
- return true;
- }
-
- if (ttob->ty == Tarray && tfrom->ty == Tsarray) // Bugzilla 12502
- tfrom = tfrom->nextOf()->arrayOf();
-
- if ((ttob->ty == Tarray && tfrom->ty == Tarray) ||
- (ttob->ty == Tpointer && tfrom->ty == Tpointer))
- {
- Type *ttobn = ttob->nextOf()->toBasetype();
- Type *tfromn = tfrom->nextOf()->toBasetype();
-
- /* From void[] to anything mutable is unsafe because:
- * int*[] api;
- * void[] av = api;
- * int[] ai = cast(int[]) av;
- * ai[0] = 7;
- * *api[0] crash!
- */
- if (tfromn->ty == Tvoid && ttobn->isMutable())
- {
- if (ttob->ty == Tarray && e->op == TOKarrayliteral)
- return true;
- return false;
- }
-
- // If the struct is opaque we don't know about the struct members then the cast becomes unsafe
- if ((ttobn->ty == Tstruct && !((TypeStruct *)ttobn)->sym->members) ||
- (tfromn->ty == Tstruct && !((TypeStruct *)tfromn)->sym->members))
- return false;
-
- const bool frompointers = tfromn->hasPointers();
- const bool topointers = ttobn->hasPointers();
-
- if (frompointers && !topointers && ttobn->isMutable())
- return false;
-
- if (!frompointers && topointers)
- return false;
-
- if (!topointers &&
- ttobn->ty != Tfunction && tfromn->ty != Tfunction &&
- (ttob->ty == Tarray || ttobn->size() <= tfromn->size()) &&
- MODimplicitConv(tfromn->mod, ttobn->mod))
- {
- return true;
- }
- }
- return false;
-}
-
diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d
new file mode 100644
index 00000000000..35734b2caa5
--- /dev/null
+++ b/gcc/d/dmd/safe.d
@@ -0,0 +1,228 @@
+/**
+ * Checks whether member access or array casting is allowed in `@safe` code.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/function.html#function-safety, Function Safety)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/safe.d, _safe.d)
+ * Documentation: https://dlang.org/phobos/dmd_safe.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/safe.d
+ */
+
+module dmd.safe;
+
+import core.stdc.stdio;
+
+import dmd.aggregate;
+import dmd.astenums;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.expression;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.target;
+import dmd.tokens;
+
+
+/*************************************************************
+ * Check for unsafe access in @safe code:
+ * 1. read overlapped pointers
+ * 2. write misaligned pointers
+ * 3. write overlapped storage classes
+ * Print error if unsafe.
+ * Params:
+ * sc = scope
+ * e = expression to check
+ * readonly = if access is read-only
+ * printmsg = print error message if true
+ * Returns:
+ * true if error
+ */
+
+bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
+{
+ //printf("checkUnsafeAccess(e: '%s', readonly: %d, printmsg: %d)\n", e.toChars(), readonly, printmsg);
+ if (e.op != TOK.dotVariable)
+ return false;
+ DotVarExp dve = cast(DotVarExp)e;
+ if (VarDeclaration v = dve.var.isVarDeclaration())
+ {
+ if (sc.intypeof || !sc.func || !sc.func.isSafeBypassingInference())
+ return false;
+ auto ad = v.toParent2().isAggregateDeclaration();
+ if (!ad)
+ return false;
+
+ // needed to set v.overlapped and v.overlapUnsafe
+ if (ad.sizeok != Sizeok.done)
+ ad.determineSize(ad.loc);
+
+ const hasPointers = v.type.hasPointers();
+ if (hasPointers)
+ {
+ if (v.overlapped && sc.func.setUnsafe())
+ {
+ if (printmsg)
+ e.error("field `%s.%s` cannot access pointers in `@safe` code that overlap other fields",
+ ad.toChars(), v.toChars());
+ return true;
+ }
+ }
+
+ if (v.type.hasInvariant())
+ {
+ if (v.overlapped && sc.func.setUnsafe())
+ {
+ if (printmsg)
+ e.error("field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields",
+ ad.toChars(), v.toChars());
+ return true;
+ }
+ }
+
+ if (readonly || !e.type.isMutable())
+ return false;
+
+ if (hasPointers && v.type.toBasetype().ty != Tstruct)
+ {
+ if ((ad.type.alignment() < target.ptrsize ||
+ (v.offset & (target.ptrsize - 1))) &&
+ sc.func.setUnsafe())
+ {
+ if (printmsg)
+ e.error("field `%s.%s` cannot modify misaligned pointers in `@safe` code",
+ ad.toChars(), v.toChars());
+ return true;
+ }
+ }
+
+ if (v.overlapUnsafe && sc.func.setUnsafe())
+ {
+ if (printmsg)
+ e.error("field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes",
+ ad.toChars(), v.toChars());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**********************************************
+ * Determine if it is @safe to cast e from tfrom to tto.
+ * Params:
+ * e = expression to be cast
+ * tfrom = type of e
+ * tto = type to cast e to
+ * Returns:
+ * true if @safe
+ */
+bool isSafeCast(Expression e, Type tfrom, Type tto)
+{
+ // Implicit conversions are always safe
+ if (tfrom.implicitConvTo(tto))
+ return true;
+
+ if (!tto.hasPointers())
+ return true;
+
+ auto tfromb = tfrom.toBasetype();
+ auto ttob = tto.toBasetype();
+
+ if (ttob.ty == Tclass && tfromb.ty == Tclass)
+ {
+ ClassDeclaration cdfrom = tfromb.isClassHandle();
+ ClassDeclaration cdto = ttob.isClassHandle();
+
+ int offset;
+ if (!cdfrom.isBaseOf(cdto, &offset) &&
+ !((cdfrom.isInterfaceDeclaration() || cdto.isInterfaceDeclaration())
+ && cdfrom.classKind == ClassKind.d && cdto.classKind == ClassKind.d))
+ return false;
+
+ if (cdfrom.isCPPinterface() || cdto.isCPPinterface())
+ return false;
+
+ if (!MODimplicitConv(tfromb.mod, ttob.mod))
+ return false;
+ return true;
+ }
+
+ if (ttob.ty == Tarray && tfromb.ty == Tsarray) // https://issues.dlang.org/show_bug.cgi?id=12502
+ tfromb = tfromb.nextOf().arrayOf();
+
+ if (ttob.ty == Tarray && tfromb.ty == Tarray ||
+ ttob.ty == Tpointer && tfromb.ty == Tpointer)
+ {
+ Type ttobn = ttob.nextOf().toBasetype();
+ Type tfromn = tfromb.nextOf().toBasetype();
+
+ /* From void[] to anything mutable is unsafe because:
+ * int*[] api;
+ * void[] av = api;
+ * int[] ai = cast(int[]) av;
+ * ai[0] = 7;
+ * *api[0] crash!
+ */
+ if (tfromn.ty == Tvoid && ttobn.isMutable())
+ {
+ if (ttob.ty == Tarray && e.op == TOK.arrayLiteral)
+ return true;
+ return false;
+ }
+
+ // If the struct is opaque we don't know about the struct members then the cast becomes unsafe
+ if (ttobn.ty == Tstruct && !(cast(TypeStruct)ttobn).sym.members ||
+ tfromn.ty == Tstruct && !(cast(TypeStruct)tfromn).sym.members)
+ return false;
+
+ const frompointers = tfromn.hasPointers();
+ const topointers = ttobn.hasPointers();
+
+ if (frompointers && !topointers && ttobn.isMutable())
+ return false;
+
+ if (!frompointers && topointers)
+ return false;
+
+ if (!topointers &&
+ ttobn.ty != Tfunction && tfromn.ty != Tfunction &&
+ (ttob.ty == Tarray || ttobn.size() <= tfromn.size()) &&
+ MODimplicitConv(tfromn.mod, ttobn.mod))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*************************************************
+ * Check for unsafe use of `.ptr` or `.funcptr`
+ * Params:
+ * sc = context
+ * e = expression for error messages
+ * id = `ptr` or `funcptr`
+ * flag = DotExpFlag
+ * Returns:
+ * true if error
+ */
+bool checkUnsafeDotExp(Scope* sc, Expression e, Identifier id, int flag)
+{
+ if (!(flag & DotExpFlag.noDeref) && // this use is attempting a dereference
+ sc.func && // inside a function
+ !sc.intypeof && // allow unsafe code in typeof expressions
+ !(sc.flags & SCOPE.debug_) && // allow unsafe code in debug statements
+ sc.func.setUnsafe()) // infer this function to be unsafe
+ {
+ if (id == Id.ptr)
+ e.error("`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e.toChars(), e.toChars());
+ else
+ e.error("`%s.%s` cannot be used in `@safe` code", e.toChars(), id.toChars());
+ return true;
+ }
+ return false;
+}
diff --git a/gcc/d/dmd/sapply.c b/gcc/d/dmd/sapply.c
deleted file mode 100644
index ce0892606b8..00000000000
--- a/gcc/d/dmd/sapply.c
+++ /dev/null
@@ -1,155 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/sapply.c
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "statement.h"
-#include "visitor.h"
-
-
-/**************************************
- * A Statement tree walker that will visit each Statement s in the tree,
- * in depth-first evaluation order, and call fp(s,param) on it.
- * fp() signals whether the walking continues with its return value:
- * Returns:
- * 0 continue
- * 1 done
- * It's a bit slower than using virtual functions, but more encapsulated and less brittle.
- * Creating an iterator for this would be much more complex.
- */
-
-class PostorderStatementVisitor : public StoppableVisitor
-{
-public:
- StoppableVisitor *v;
- PostorderStatementVisitor(StoppableVisitor *v) : v(v) {}
-
- bool doCond(Statement *s)
- {
- if (!stop && s)
- s->accept(this);
- return stop;
- }
- bool applyTo(Statement *s)
- {
- s->accept(v);
- stop = v->stop;
- return true;
- }
-
- void visit(Statement *s)
- {
- applyTo(s);
- }
- void visit(PeelStatement *s)
- {
- doCond(s->s) || applyTo(s);
- }
- void visit(CompoundStatement *s)
- {
- for (size_t i = 0; i < s->statements->length; i++)
- if (doCond((*s->statements)[i]))
- return;
- applyTo(s);
- }
- void visit(UnrolledLoopStatement *s)
- {
- for (size_t i = 0; i < s->statements->length; i++)
- if (doCond((*s->statements)[i]))
- return;
- applyTo(s);
- }
- void visit(ScopeStatement *s)
- {
- doCond(s->statement) || applyTo(s);
- }
- void visit(WhileStatement *s)
- {
- doCond(s->_body) || applyTo(s);
- }
- void visit(DoStatement *s)
- {
- doCond(s->_body) || applyTo(s);
- }
- void visit(ForStatement *s)
- {
- doCond(s->_init) || doCond(s->_body) || applyTo(s);
- }
- void visit(ForeachStatement *s)
- {
- doCond(s->_body) || applyTo(s);
- }
- void visit(ForeachRangeStatement *s)
- {
- doCond(s->_body) || applyTo(s);
- }
- void visit(IfStatement *s)
- {
- doCond(s->ifbody) || doCond(s->elsebody) || applyTo(s);
- }
- void visit(PragmaStatement *s)
- {
- doCond(s->_body) || applyTo(s);
- }
- void visit(SwitchStatement *s)
- {
- doCond(s->_body) || applyTo(s);
- }
- void visit(CaseStatement *s)
- {
- doCond(s->statement) || applyTo(s);
- }
- void visit(DefaultStatement *s)
- {
- doCond(s->statement) || applyTo(s);
- }
- void visit(SynchronizedStatement *s)
- {
- doCond(s->_body) || applyTo(s);
- }
- void visit(WithStatement *s)
- {
- doCond(s->_body) || applyTo(s);
- }
- void visit(TryCatchStatement *s)
- {
- if (doCond(s->_body))
- return;
-
- for (size_t i = 0; i < s->catches->length; i++)
- if (doCond((*s->catches)[i]->handler))
- return;
- applyTo(s);
- }
- void visit(TryFinallyStatement *s)
- {
- doCond(s->_body) || doCond(s->finalbody) || applyTo(s);
- }
- void visit(ScopeGuardStatement *s)
- {
- doCond(s->statement) || applyTo(s);
- }
- void visit(DebugStatement *s)
- {
- doCond(s->statement) || applyTo(s);
- }
- void visit(LabelStatement *s)
- {
- doCond(s->statement) || applyTo(s);
- }
-};
-
-bool walkPostorder(Statement *s, StoppableVisitor *v)
-{
- PostorderStatementVisitor pv(v);
- s->accept(&pv);
- return v->stop;
-}
diff --git a/gcc/d/dmd/sapply.d b/gcc/d/dmd/sapply.d
new file mode 100644
index 00000000000..018b0462c84
--- /dev/null
+++ b/gcc/d/dmd/sapply.d
@@ -0,0 +1,180 @@
+/**
+ * Provides a depth-first statement visitor.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/sparse.d, _sparse.d)
+ * Documentation: https://dlang.org/phobos/dmd_sapply.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/sapply.d
+ */
+
+module dmd.sapply;
+
+import dmd.statement;
+import dmd.visitor;
+
+bool walkPostorder(Statement s, StoppableVisitor v)
+{
+ scope PostorderStatementVisitor pv = new PostorderStatementVisitor(v);
+ s.accept(pv);
+ return v.stop;
+}
+
+/**************************************
+ * A Statement tree walker that will visit each Statement s in the tree,
+ * in depth-first evaluation order, and call fp(s,param) on it.
+ * fp() signals whether the walking continues with its return value:
+ * Returns:
+ * 0 continue
+ * 1 done
+ * It's a bit slower than using virtual functions, but more encapsulated and less brittle.
+ * Creating an iterator for this would be much more complex.
+ */
+private extern (C++) final class PostorderStatementVisitor : StoppableVisitor
+{
+ alias visit = typeof(super).visit;
+public:
+ StoppableVisitor v;
+
+ extern (D) this(StoppableVisitor v)
+ {
+ this.v = v;
+ }
+
+ bool doCond(Statement s)
+ {
+ if (!stop && s)
+ s.accept(this);
+ return stop;
+ }
+
+ bool applyTo(Statement s)
+ {
+ s.accept(v);
+ stop = v.stop;
+ return true;
+ }
+
+ override void visit(Statement s)
+ {
+ applyTo(s);
+ }
+
+ override void visit(PeelStatement s)
+ {
+ doCond(s.s) || applyTo(s);
+ }
+
+ override void visit(CompoundStatement s)
+ {
+ for (size_t i = 0; i < s.statements.dim; i++)
+ if (doCond((*s.statements)[i]))
+ return;
+ applyTo(s);
+ }
+
+ override void visit(UnrolledLoopStatement s)
+ {
+ for (size_t i = 0; i < s.statements.dim; i++)
+ if (doCond((*s.statements)[i]))
+ return;
+ applyTo(s);
+ }
+
+ override void visit(ScopeStatement s)
+ {
+ doCond(s.statement) || applyTo(s);
+ }
+
+ override void visit(WhileStatement s)
+ {
+ doCond(s._body) || applyTo(s);
+ }
+
+ override void visit(DoStatement s)
+ {
+ doCond(s._body) || applyTo(s);
+ }
+
+ override void visit(ForStatement s)
+ {
+ doCond(s._init) || doCond(s._body) || applyTo(s);
+ }
+
+ override void visit(ForeachStatement s)
+ {
+ doCond(s._body) || applyTo(s);
+ }
+
+ override void visit(ForeachRangeStatement s)
+ {
+ doCond(s._body) || applyTo(s);
+ }
+
+ override void visit(IfStatement s)
+ {
+ doCond(s.ifbody) || doCond(s.elsebody) || applyTo(s);
+ }
+
+ override void visit(PragmaStatement s)
+ {
+ doCond(s._body) || applyTo(s);
+ }
+
+ override void visit(SwitchStatement s)
+ {
+ doCond(s._body) || applyTo(s);
+ }
+
+ override void visit(CaseStatement s)
+ {
+ doCond(s.statement) || applyTo(s);
+ }
+
+ override void visit(DefaultStatement s)
+ {
+ doCond(s.statement) || applyTo(s);
+ }
+
+ override void visit(SynchronizedStatement s)
+ {
+ doCond(s._body) || applyTo(s);
+ }
+
+ override void visit(WithStatement s)
+ {
+ doCond(s._body) || applyTo(s);
+ }
+
+ override void visit(TryCatchStatement s)
+ {
+ if (doCond(s._body))
+ return;
+ for (size_t i = 0; i < s.catches.dim; i++)
+ if (doCond((*s.catches)[i].handler))
+ return;
+ applyTo(s);
+ }
+
+ override void visit(TryFinallyStatement s)
+ {
+ doCond(s._body) || doCond(s.finalbody) || applyTo(s);
+ }
+
+ override void visit(ScopeGuardStatement s)
+ {
+ doCond(s.statement) || applyTo(s);
+ }
+
+ override void visit(DebugStatement s)
+ {
+ doCond(s.statement) || applyTo(s);
+ }
+
+ override void visit(LabelStatement s)
+ {
+ doCond(s.statement) || applyTo(s);
+ }
+}
+
diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h
index ea3061b8fa6..4d8c0bbef18 100644
--- a/gcc/d/dmd/scope.h
+++ b/gcc/d/dmd/scope.h
@@ -10,8 +10,6 @@
#pragma once
-class Dsymbol;
-class ScopeDsymbol;
class Identifier;
class Module;
class Statement;
@@ -26,49 +24,48 @@ class UserAttributeDeclaration;
struct DocComment;
struct AA;
class TemplateInstance;
+class CPPNamespaceDeclaration;
#include "dsymbol.h"
-#if __GNUC__
-// Requires a full definition for LINK
-#include "globals.h"
-#else
-enum LINK;
-enum PINLINE;
-#endif
-
-#define CSXthis_ctor 1 // called this()
-#define CSXsuper_ctor 2 // called super()
-#define CSXthis 4 // referenced this
-#define CSXsuper 8 // referenced super
-#define CSXlabel 0x10 // seen a label
-#define CSXreturn 0x20 // seen a return statement
-#define CSXany_ctor 0x40 // either this() or super() was called
-#define CSXhalt 0x80 // assert(0)
-
-// Flags that would not be inherited beyond scope nesting
-#define SCOPEctor 0x0001 // constructor type
-#define SCOPEcondition 0x0004 // inside static if/assert condition
-#define SCOPEdebug 0x0008 // inside debug conditional
-
-// Flags that would be inherited beyond scope nesting
-#define SCOPEnoaccesscheck 0x0002 // don't do access checks
-#define SCOPEconstraint 0x0010 // inside template constraint
-#define SCOPEinvariant 0x0020 // inside invariant code
-#define SCOPErequire 0x0040 // inside in contract code
-#define SCOPEensure 0x0060 // inside out contract code
-#define SCOPEcontract 0x0060 // [mask] we're inside contract code
-#define SCOPEctfe 0x0080 // inside a ctfe-only expression
-#define SCOPEcompile 0x0100 // inside __traits(compile)
-#define SCOPEignoresymbolvisibility 0x0200 // ignore symbol visibility (Bugzilla 15907)
-
-#define SCOPEfree 0x8000 // is on free list
-#define SCOPEfullinst 0x10000 // fully instantiate templates
-#define SCOPEalias 0x20000 // inside alias declaration
-
-// The following are mutually exclusive
-#define SCOPEprintf 0x40000 // printf-style function
-#define SCOPEscanf 0x80000 // scanf-style function
+enum
+{
+ CSXthis_ctor = 1, // called this()
+ CSXsuper_ctor = 2, // called super()
+ CSXthis = 4, // referenced this
+ CSXsuper = 8, // referenced super
+ CSXlabel = 0x10, // seen a label
+ CSXreturn = 0x20, // seen a return statement
+ CSXany_ctor = 0x40, // either this() or super() was called
+ CSXhalt = 0x80, // assert(0)
+};
+
+enum
+{
+ // Flags that would not be inherited beyond scope nesting
+ SCOPEctor = 0x0001, // constructor type
+ SCOPEcondition = 0x0004, // inside static if/assert condition
+ SCOPEdebug = 0x0008, // inside debug conditional
+
+ // Flags that would be inherited beyond scope nesting
+ SCOPEnoaccesscheck = 0x0002, // don't do access checks
+ SCOPEconstraint = 0x0010, // inside template constraint
+ SCOPEinvariant = 0x0020, // inside invariant code
+ SCOPErequire = 0x0040, // inside in contract code
+ SCOPEensure = 0x0060, // inside out contract code
+ SCOPEcontract = 0x0060, // [mask] we're inside contract code
+ SCOPEctfe = 0x0080, // inside a ctfe-only expression
+ SCOPEcompile = 0x0100, // inside __traits(compile)
+ SCOPEignoresymbolvisibility = 0x0200, // ignore symbol visibility (Bugzilla 15907)
+
+ SCOPEfree = 0x8000, // is on free list
+ SCOPEfullinst = 0x10000, // fully instantiate templates
+ SCOPEalias = 0x20000, // inside alias declaration
+
+ // The following are mutually exclusive
+ SCOPEprintf = 0x40000, // printf-style function
+ SCOPEscanf = 0x80000, // scanf-style function
+};
struct Scope
{
@@ -80,15 +77,16 @@ struct Scope
Dsymbol *parent; // parent to use
LabelStatement *slabel; // enclosing labelled statement
SwitchStatement *sw; // enclosing switch statement
+ Statement *tryBody; // enclosing _body of TryCatchStatement or TryFinallyStatement
TryFinallyStatement *tf; // enclosing try finally statement
ScopeGuardStatement *os; // enclosing scope(xxx) statement
Statement *sbreak; // enclosing statement that supports "break"
Statement *scontinue; // enclosing statement that supports "continue"
ForeachStatement *fes; // if nested function for ForeachStatement, this is it
Scope *callsc; // used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
- int inunion; // we're processing members of a union
- int nofree; // set if shouldn't free it
- int noctor; // set if constructor calls aren't allowed
+ Dsymbol *inunion; // !=null if processing members of a union
+ bool nofree; // true if shouldn't free it
+ bool inLoop; // true if inside a loop (where constructor calls aren't allowed)
int intypeof; // in typeof(exp)
VarDeclaration *lastVar; // Previous symbol used to prevent goto-skips-init
@@ -100,18 +98,21 @@ struct Scope
Module *minst; // root module where the instantiated templates should belong to
TemplateInstance *tinst; // enclosing template instance
- unsigned callSuper; // primitive flow analysis for constructors
- unsigned *fieldinit;
+ unsigned char callSuper; // primitive flow analysis for constructors
+ unsigned char *fieldinit;
size_t fieldinit_dim;
AlignDeclaration *aligndecl; // alignment for struct members
+ /// C++ namespace this symbol belongs to
+ CPPNamespaceDeclaration *namespace_;
+
LINK linkage; // linkage for external functions
CPPMANGLE cppmangle; // C++ mangle type
- PINLINE inlining; // inlining strategy for functions
+ PragmaDeclaration *inlining; // inlining strategy for functions
- Prot protection; // protection for class members
- int explicitProtection; // set if in an explicit protection attribute
+ Visibility visibility; // visibility for class members
+ int explicitVisibility; // set if in an explicit visibility attribute
StorageClass stc; // storage class
@@ -125,10 +126,8 @@ struct Scope
AA *anchorCounts; // lookup duplicate anchor name count
Identifier *prevAnchor; // qualified symbol name of last doc anchor
- static Scope *freelist;
- static Scope *alloc();
- static Scope *createGlobal(Module *module);
-
+ AliasDeclaration *aliasAsg; // if set, then aliasAsg is being assigned a new value,
+ // do not set wasRead for it
Scope();
Scope *copy();
@@ -140,21 +139,12 @@ struct Scope
Scope *startCTFE();
Scope *endCTFE();
- void mergeCallSuper(Loc loc, unsigned cs);
-
- unsigned *saveFieldInit();
- void mergeFieldInit(Loc loc, unsigned *cses);
-
- Module *instantiatingModule();
-
- Dsymbol *search(Loc loc, Identifier *ident, Dsymbol **pscopesym, int flags = IgnoreNone);
- Dsymbol *search_correct(Identifier *ident);
- static const char *search_correct_C(Identifier *ident);
- Dsymbol *insert(Dsymbol *s);
+ Dsymbol *search(const Loc &loc, Identifier *ident, Dsymbol **pscopesym, int flags = IgnoreNone);
ClassDeclaration *getClassScope();
AggregateDeclaration *getStructClassScope();
- void setNoFree();
structalign_t alignment();
+
+ bool isDeprecated() const;
};
diff --git a/gcc/d/dmd/semantic2.c b/gcc/d/dmd/semantic2.c
deleted file mode 100644
index 194a3fb9661..00000000000
--- a/gcc/d/dmd/semantic2.c
+++ /dev/null
@@ -1,430 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "dsymbol.h"
-#include "aggregate.h"
-#include "attrib.h"
-#include "declaration.h"
-#include "errors.h"
-#include "import.h"
-#include "init.h"
-#include "module.h"
-#include "nspace.h"
-#include "objc.h"
-#include "scope.h"
-#include "staticassert.h"
-#include "template.h"
-#include "visitor.h"
-
-bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors);
-void udaExpressionEval(Scope *sc, Expressions *exps);
-Objc *objc();
-
-class Semantic2Visitor : public Visitor
-{
-public:
- Scope *sc;
-
- Semantic2Visitor(Scope *sc)
- {
- this->sc = sc;
- }
-
- void visit(Dsymbol *)
- {
- // Most Dsymbols have no further semantic analysis needed
- }
-
- void visit(StaticAssert *sa)
- {
- //printf("StaticAssert::semantic2() %s\n", toChars());
- ScopeDsymbol *sds = new ScopeDsymbol();
- sc = sc->push(sds);
- sc->tinst = NULL;
- sc->minst = NULL;
-
- bool errors = false;
- bool result = evalStaticCondition(sc, sa->exp, sa->exp, errors);
- sc = sc->pop();
- if (errors)
- {
- errorSupplemental(sa->loc, "while evaluating: static assert(%s)", sa->exp->toChars());
- }
- else if (!result)
- {
- if (sa->msg)
- {
- sc = sc->startCTFE();
- sa->msg = expressionSemantic(sa->msg, sc);
- sa->msg = resolveProperties(sc, sa->msg);
- sc = sc->endCTFE();
- sa->msg = sa->msg->ctfeInterpret();
- if (StringExp * se = sa->msg->toStringExp())
- {
- // same with pragma(msg)
- se = se->toUTF8(sc);
- sa->error("\"%.*s\"", (int)se->len, (char *)se->string);
- }
- else
- sa->error("%s", sa->msg->toChars());
- }
- else
- sa->error("(%s) is false", sa->exp->toChars());
- if (sc->tinst)
- sc->tinst->printInstantiationTrace();
- if (!global.gag)
- fatal();
- }
- }
-
- void visit(TemplateInstance *tempinst)
- {
- if (tempinst->semanticRun >= PASSsemantic2)
- return;
- tempinst->semanticRun = PASSsemantic2;
- if (!tempinst->errors && tempinst->members)
- {
- TemplateDeclaration *tempdecl = tempinst->tempdecl->isTemplateDeclaration();
- assert(tempdecl);
-
- sc = tempdecl->_scope;
- assert(sc);
- sc = sc->push(tempinst->argsym);
- sc = sc->push(tempinst);
- sc->tinst = tempinst;
- sc->minst = tempinst->minst;
-
- int needGagging = (tempinst->gagged && !global.gag);
- unsigned int olderrors = global.errors;
- int oldGaggedErrors = -1; // dead-store to prevent spurious warning
- if (needGagging)
- oldGaggedErrors = global.startGagging();
-
- for (size_t i = 0; i < tempinst->members->length; i++)
- {
- Dsymbol *s = (*tempinst->members)[i];
- semantic2(s, sc);
- if (tempinst->gagged && global.errors != olderrors)
- break;
- }
-
- if (global.errors != olderrors)
- {
- if (!tempinst->errors)
- {
- if (!tempdecl->literal)
- tempinst->error(tempinst->loc, "error instantiating");
- if (tempinst->tinst)
- tempinst->tinst->printInstantiationTrace();
- }
- tempinst->errors = true;
- }
- if (needGagging)
- global.endGagging(oldGaggedErrors);
-
- sc = sc->pop();
- sc->pop();
- }
- }
-
- void visit(TemplateMixin *tmix)
- {
- if (tmix->semanticRun >= PASSsemantic2)
- return;
- tmix->semanticRun = PASSsemantic2;
- if (tmix->members)
- {
- assert(sc);
- sc = sc->push(tmix->argsym);
- sc = sc->push(tmix);
- for (size_t i = 0; i < tmix->members->length; i++)
- {
- Dsymbol *s = (*tmix->members)[i];
- semantic2(s, sc);
- }
- sc = sc->pop();
- sc->pop();
- }
- }
-
- void visit(VarDeclaration *vd)
- {
- if (vd->semanticRun < PASSsemanticdone && vd->inuse)
- return;
-
- //printf("VarDeclaration::semantic2('%s')\n", toChars());
-
- if (vd->_init && !vd->toParent()->isFuncDeclaration())
- {
- vd->inuse++;
-
- /* https://issues.dlang.org/show_bug.cgi?id=20280
- *
- * Template instances may import modules that have not
- * finished semantic1.
- */
- if (!vd->type)
- dsymbolSemantic(vd, sc);
-
- // Bugzilla 14166: Don't run CTFE for the temporary variables inside typeof
- vd->_init = initializerSemantic(vd->_init, sc, vd->type, sc->intypeof == 1 ? INITnointerpret : INITinterpret);
- vd->inuse--;
- }
- if (vd->_init && (vd->storage_class & STCmanifest))
- {
- /* Cannot initializer enums with CTFE classreferences and addresses of struct literals.
- * Scan initializer looking for them. Issue error if found.
- */
- if (ExpInitializer *ei = vd->_init->isExpInitializer())
- {
- struct EnumInitializer
- {
- static bool arrayHasInvalidEnumInitializer(Expressions *elems)
- {
- for (size_t i = 0; i < elems->length; i++)
- {
- Expression *e = (*elems)[i];
- if (e && hasInvalidEnumInitializer(e))
- return true;
- }
- return false;
- }
-
- static bool hasInvalidEnumInitializer(Expression *e)
- {
- if (e->op == TOKclassreference)
- return true;
- if (e->op == TOKaddress && ((AddrExp *)e)->e1->op == TOKstructliteral)
- return true;
- if (e->op == TOKarrayliteral)
- return arrayHasInvalidEnumInitializer(((ArrayLiteralExp *)e)->elements);
- if (e->op == TOKstructliteral)
- return arrayHasInvalidEnumInitializer(((StructLiteralExp *)e)->elements);
- if (e->op == TOKassocarrayliteral)
- {
- AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e;
- return arrayHasInvalidEnumInitializer(ae->values) ||
- arrayHasInvalidEnumInitializer(ae->keys);
- }
- return false;
- }
- };
- if (EnumInitializer::hasInvalidEnumInitializer(ei->exp))
- vd->error(": Unable to initialize enum with class or pointer to struct. Use static const variable instead.");
- }
- }
- else if (vd->_init && vd->isThreadlocal())
- {
- if ((vd->type->ty == Tclass) && vd->type->isMutable() && !vd->type->isShared())
- {
- ExpInitializer *ei = vd->_init->isExpInitializer();
- if (ei && ei->exp->op == TOKclassreference)
- vd->error("is mutable. Only const or immutable class thread local variable are allowed, not %s", vd->type->toChars());
- }
- else if (vd->type->ty == Tpointer && vd->type->nextOf()->ty == Tstruct && vd->type->nextOf()->isMutable() && !vd->type->nextOf()->isShared())
- {
- ExpInitializer *ei = vd->_init->isExpInitializer();
- if (ei && ei->exp->op == TOKaddress && ((AddrExp *)ei->exp)->e1->op == TOKstructliteral)
- {
- vd->error("is a pointer to mutable struct. Only pointers to const, immutable or shared struct thread local variable are allowed, not %s", vd->type->toChars());
- }
- }
- }
- vd->semanticRun = PASSsemantic2done;
- }
-
- void visit(Module *mod)
- {
- //printf("Module::semantic2('%s'): parent = %p\n", toChars(), mod->parent);
- if (mod->semanticRun != PASSsemanticdone) // semantic() not completed yet - could be recursive call
- return;
- mod->semanticRun = PASSsemantic2;
-
- // Note that modules get their own scope, from scratch.
- // This is so regardless of where in the syntax a module
- // gets imported, it is unaffected by context.
- Scope *sc = Scope::createGlobal(mod); // create root scope
- //printf("Module = %p\n", sc.scopesym);
-
- // Pass 2 semantic routines: do initializers and function bodies
- for (size_t i = 0; i < mod->members->length; i++)
- {
- Dsymbol *s = (*mod->members)[i];
- semantic2(s, sc);
- }
-
- if (mod->userAttribDecl)
- {
- semantic2(mod->userAttribDecl, sc);
- }
-
- sc = sc->pop();
- sc->pop();
- mod->semanticRun = PASSsemantic2done;
- //printf("-Module::semantic2('%s'): parent = %p\n", toChars(), mod->parent);
- }
-
- void visit(FuncDeclaration *fd)
- {
- if (fd->semanticRun >= PASSsemantic2done)
- return;
-
- if (fd->semanticRun < PASSsemanticdone && !fd->errors)
- {
- /* https://issues.dlang.org/show_bug.cgi?id=21614
- *
- * Template instances may import modules that have not
- * finished semantic1.
- */
- dsymbolSemantic(fd, sc);
- }
-
- assert(fd->semanticRun <= PASSsemantic2);
- fd->semanticRun = PASSsemantic2;
-
- objc()->setSelector(fd, sc);
- objc()->validateSelector(fd);
-
- if (fd->parent->isClassDeclaration())
- {
- objc()->checkLinkage(fd);
- }
- if (!fd->type || fd->type->ty != Tfunction)
- return;
- TypeFunction *f = fd->type->toTypeFunction();
- const size_t nparams = f->parameterList.length();
- // semantic for parameters' UDAs
- for (size_t i = 0; i < nparams; i++)
- {
- Parameter *param = f->parameterList[i];
- if (param && param->userAttribDecl)
- semantic2(param->userAttribDecl, sc);
- }
- }
-
- void visit(Import *i)
- {
- //printf("Import::semantic2('%s')\n", toChars());
- if (i->mod)
- {
- semantic2(i->mod, NULL);
- if (i->mod->needmoduleinfo)
- {
- //printf("module5 %s because of %s\n", sc->_module->toChars(), i->mod->toChars());
- if (sc)
- sc->_module->needmoduleinfo = 1;
- }
- }
- }
-
- void visit(Nspace *ns)
- {
- if (ns->semanticRun >= PASSsemantic2)
- return;
- ns->semanticRun = PASSsemantic2;
- if (ns->members)
- {
- assert(sc);
- sc = sc->push(ns);
- sc->linkage = LINKcpp;
- for (size_t i = 0; i < ns->members->length; i++)
- {
- Dsymbol *s = (*ns->members)[i];
- semantic2(s, sc);
- }
- sc->pop();
- }
- }
-
- void visit(AttribDeclaration *ad)
- {
- Dsymbols *d = ad->include(sc);
-
- if (d)
- {
- Scope *sc2 = ad->newScope(sc);
-
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- semantic2(s, sc2);
- }
-
- if (sc2 != sc)
- sc2->pop();
- }
- }
-
- /**
- * Run the DeprecatedDeclaration's semantic2 phase then its members.
- *
- * The message set via a `DeprecatedDeclaration` can be either of:
- * - a string literal
- * - an enum
- * - a static immutable
- * So we need to call ctfe to resolve it.
- * Afterward forwards to the members' semantic2.
- */
- void visit(DeprecatedDeclaration *dd)
- {
- dd->getMessage();
- visit((AttribDeclaration *)dd);
- }
-
- void visit(AlignDeclaration *ad)
- {
- ad->getAlignment(sc);
- visit((AttribDeclaration *)ad);
- }
-
- void visit(UserAttributeDeclaration *uad)
- {
- if (uad->decl && uad->atts && uad->atts->length && uad->_scope)
- {
- uad->_scope = NULL;
- udaExpressionEval(sc, uad->atts);
- }
- visit((AttribDeclaration *)uad);
- }
-
- void visit(AggregateDeclaration *ad)
- {
- //printf("AggregateDeclaration::semantic2(%s) type = %s, errors = %d\n", toChars(), ad->type->toChars(), ad->errors);
- if (!ad->members)
- return;
-
- if (ad->_scope)
- {
- ad->error("has forward references");
- return;
- }
-
- Scope *sc2 = ad->newScope(sc);
-
- ad->determineSize(ad->loc);
-
- for (size_t i = 0; i < ad->members->length; i++)
- {
- Dsymbol *s = (*ad->members)[i];
- //printf("\t[%d] %s\n", i, s->toChars());
- semantic2(s, sc2);
- }
-
- sc2->pop();
- }
-};
-
-/*************************************
- * Does semantic analysis on initializers and members of aggregates.
- */
-void semantic2(Dsymbol *dsym, Scope *sc)
-{
- Semantic2Visitor v(sc);
- dsym->accept(&v);
-}
diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d
new file mode 100644
index 00000000000..7b2fa5e93cd
--- /dev/null
+++ b/gcc/d/dmd/semantic2.d
@@ -0,0 +1,774 @@
+/**
+ * Performs the semantic2 stage, which deals with initializer expressions.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/semantic2.d, _semantic2.d)
+ * Documentation: https://dlang.org/phobos/dmd_semantic2.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic2.d
+ */
+
+module dmd.semantic2;
+
+import core.stdc.stdio;
+import core.stdc.string;
+
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.blockexit;
+import dmd.clone;
+import dmd.dcast;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dinterpret;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.dversion;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.initsem;
+import dmd.hdrgen;
+import dmd.mtype;
+import dmd.nogc;
+import dmd.nspace;
+import dmd.objc;
+import dmd.opover;
+import dmd.parse;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.sideeffect;
+import dmd.statementsem;
+import dmd.staticassert;
+import dmd.tokens;
+import dmd.utf;
+import dmd.statement;
+import dmd.target;
+import dmd.templateparamsem;
+import dmd.typesem;
+import dmd.visitor;
+
+enum LOG = false;
+
+
+/*************************************
+ * Does semantic analysis on initializers and members of aggregates.
+ */
+extern(C++) void semantic2(Dsymbol dsym, Scope* sc)
+{
+ scope v = new Semantic2Visitor(sc);
+ dsym.accept(v);
+}
+
+private extern(C++) final class Semantic2Visitor : Visitor
+{
+ alias visit = Visitor.visit;
+ Scope* sc;
+ this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ override void visit(Dsymbol) {}
+
+ override void visit(StaticAssert sa)
+ {
+ //printf("StaticAssert::semantic2() %s\n", sa.toChars());
+ auto sds = new ScopeDsymbol();
+ sc = sc.push(sds);
+ sc.tinst = null;
+ sc.minst = null;
+
+ import dmd.staticcond;
+ bool errors;
+ bool result = evalStaticCondition(sc, sa.exp, sa.exp, errors);
+ sc = sc.pop();
+ if (errors)
+ {
+ errorSupplemental(sa.loc, "while evaluating: `static assert(%s)`", sa.exp.toChars());
+ return;
+ }
+ else if (result)
+ return;
+
+ if (sa.msg)
+ {
+ sc = sc.startCTFE();
+ sa.msg = sa.msg.expressionSemantic(sc);
+ sa.msg = resolveProperties(sc, sa.msg);
+ sc = sc.endCTFE();
+ sa.msg = sa.msg.ctfeInterpret();
+ if (StringExp se = sa.msg.toStringExp())
+ {
+ // same with pragma(msg)
+ const slice = se.toUTF8(sc).peekString();
+ error(sa.loc, "static assert: \"%.*s\"", cast(int)slice.length, slice.ptr);
+ }
+ else
+ error(sa.loc, "static assert: %s", sa.msg.toChars());
+ }
+ else
+ error(sa.loc, "static assert: `%s` is false", sa.exp.toChars());
+ if (sc.tinst)
+ sc.tinst.printInstantiationTrace();
+ if (!global.gag)
+ fatal();
+ }
+
+ override void visit(TemplateInstance tempinst)
+ {
+ if (tempinst.semanticRun >= PASS.semantic2)
+ return;
+ tempinst.semanticRun = PASS.semantic2;
+ static if (LOG)
+ {
+ printf("+TemplateInstance.semantic2('%s')\n", tempinst.toChars());
+ scope(exit) printf("-TemplateInstance.semantic2('%s')\n", tempinst.toChars());
+ }
+ if (tempinst.errors || !tempinst.members)
+ return;
+
+ TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ sc = tempdecl._scope;
+ assert(sc);
+ sc = sc.push(tempinst.argsym);
+ sc = sc.push(tempinst);
+ sc.tinst = tempinst;
+ sc.minst = tempinst.minst;
+
+ int needGagging = (tempinst.gagged && !global.gag);
+ uint olderrors = global.errors;
+ int oldGaggedErrors = -1; // dead-store to prevent spurious warning
+ if (needGagging)
+ oldGaggedErrors = global.startGagging();
+
+ for (size_t i = 0; i < tempinst.members.dim; i++)
+ {
+ Dsymbol s = (*tempinst.members)[i];
+ static if (LOG)
+ {
+ printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind());
+ }
+ s.semantic2(sc);
+ if (tempinst.gagged && global.errors != olderrors)
+ break;
+ }
+
+ if (global.errors != olderrors)
+ {
+ if (!tempinst.errors)
+ {
+ if (!tempdecl.literal)
+ tempinst.error(tempinst.loc, "error instantiating");
+ if (tempinst.tinst)
+ tempinst.tinst.printInstantiationTrace();
+ }
+ tempinst.errors = true;
+ }
+ if (needGagging)
+ global.endGagging(oldGaggedErrors);
+
+ sc = sc.pop();
+ sc.pop();
+ }
+
+ override void visit(TemplateMixin tmix)
+ {
+ if (tmix.semanticRun >= PASS.semantic2)
+ return;
+ tmix.semanticRun = PASS.semantic2;
+ static if (LOG)
+ {
+ printf("+TemplateMixin.semantic2('%s')\n", tmix.toChars());
+ scope(exit) printf("-TemplateMixin.semantic2('%s')\n", tmix.toChars());
+ }
+ if (!tmix.members)
+ return;
+
+ assert(sc);
+ sc = sc.push(tmix.argsym);
+ sc = sc.push(tmix);
+ sc.tinst = tmix;
+ sc.minst = tmix.minst;
+ for (size_t i = 0; i < tmix.members.dim; i++)
+ {
+ Dsymbol s = (*tmix.members)[i];
+ static if (LOG)
+ {
+ printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind());
+ }
+ s.semantic2(sc);
+ }
+ sc = sc.pop();
+ sc.pop();
+ }
+
+ override void visit(VarDeclaration vd)
+ {
+ if (vd.semanticRun < PASS.semanticdone && vd.inuse)
+ return;
+
+ //printf("VarDeclaration::semantic2('%s')\n", toChars());
+
+ if (vd.aliassym) // if it's a tuple
+ {
+ vd.aliassym.accept(this);
+ vd.semanticRun = PASS.semantic2done;
+ return;
+ }
+
+ UserAttributeDeclaration.checkGNUABITag(vd, vd.linkage);
+
+ if (vd._init && !vd.toParent().isFuncDeclaration())
+ {
+ vd.inuse++;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=20280
+ *
+ * Template instances may import modules that have not
+ * finished semantic1.
+ */
+ if (!vd.type)
+ vd.dsymbolSemantic(sc);
+
+
+ // https://issues.dlang.org/show_bug.cgi?id=14166
+ // https://issues.dlang.org/show_bug.cgi?id=20417
+ // Don't run CTFE for the temporary variables inside typeof or __traits(compiles)
+ vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.flags & SCOPE.compile ? INITnointerpret : INITinterpret);
+ vd.inuse--;
+ }
+ if (vd._init && vd.storage_class & STC.manifest)
+ {
+ /* Cannot initializer enums with CTFE classreferences and addresses of struct literals.
+ * Scan initializer looking for them. Issue error if found.
+ */
+ if (ExpInitializer ei = vd._init.isExpInitializer())
+ {
+ static bool hasInvalidEnumInitializer(Expression e)
+ {
+ static bool arrayHasInvalidEnumInitializer(Expressions* elems)
+ {
+ foreach (e; *elems)
+ {
+ if (e && hasInvalidEnumInitializer(e))
+ return true;
+ }
+ return false;
+ }
+
+ if (e.op == TOK.classReference)
+ return true;
+ if (e.op == TOK.address && (cast(AddrExp)e).e1.op == TOK.structLiteral)
+ return true;
+ if (e.op == TOK.arrayLiteral)
+ return arrayHasInvalidEnumInitializer((cast(ArrayLiteralExp)e).elements);
+ if (e.op == TOK.structLiteral)
+ return arrayHasInvalidEnumInitializer((cast(StructLiteralExp)e).elements);
+ if (e.op == TOK.assocArrayLiteral)
+ {
+ AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e;
+ return arrayHasInvalidEnumInitializer(ae.values) ||
+ arrayHasInvalidEnumInitializer(ae.keys);
+ }
+ return false;
+ }
+
+ if (hasInvalidEnumInitializer(ei.exp))
+ vd.error(": Unable to initialize enum with class or pointer to struct. Use static const variable instead.");
+ }
+ }
+ else if (vd._init && vd.isThreadlocal())
+ {
+ // Cannot initialize a thread-local class or pointer to struct variable with a literal
+ // that itself is a thread-local reference and would need dynamic initialization also.
+ if ((vd.type.ty == Tclass) && vd.type.isMutable() && !vd.type.isShared())
+ {
+ ExpInitializer ei = vd._init.isExpInitializer();
+ if (ei && ei.exp.op == TOK.classReference)
+ vd.error("is a thread-local class and cannot have a static initializer. Use `static this()` to initialize instead.");
+ }
+ else if (vd.type.ty == Tpointer && vd.type.nextOf().ty == Tstruct && vd.type.nextOf().isMutable() && !vd.type.nextOf().isShared())
+ {
+ ExpInitializer ei = vd._init.isExpInitializer();
+ if (ei && ei.exp.op == TOK.address && (cast(AddrExp)ei.exp).e1.op == TOK.structLiteral)
+ vd.error("is a thread-local pointer to struct and cannot have a static initializer. Use `static this()` to initialize instead.");
+ }
+ }
+ vd.semanticRun = PASS.semantic2done;
+ }
+
+ override void visit(Module mod)
+ {
+ //printf("Module::semantic2('%s'): parent = %p\n", toChars(), parent);
+ if (mod.semanticRun != PASS.semanticdone) // semantic() not completed yet - could be recursive call
+ return;
+ mod.semanticRun = PASS.semantic2;
+ // Note that modules get their own scope, from scratch.
+ // This is so regardless of where in the syntax a module
+ // gets imported, it is unaffected by context.
+ Scope* sc = Scope.createGlobal(mod); // create root scope
+ //printf("Module = %p\n", sc.scopesym);
+ // Pass 2 semantic routines: do initializers and function bodies
+ for (size_t i = 0; i < mod.members.dim; i++)
+ {
+ Dsymbol s = (*mod.members)[i];
+ s.semantic2(sc);
+ }
+ if (mod.userAttribDecl)
+ {
+ mod.userAttribDecl.semantic2(sc);
+ }
+ sc = sc.pop();
+ sc.pop();
+ mod.semanticRun = PASS.semantic2done;
+ //printf("-Module::semantic2('%s'): parent = %p\n", toChars(), parent);
+ }
+
+ override void visit(FuncDeclaration fd)
+ {
+ if (fd.semanticRun >= PASS.semantic2done)
+ return;
+
+ if (fd.semanticRun < PASS.semanticdone && !fd.errors)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=21614
+ *
+ * Template instances may import modules that have not
+ * finished semantic1.
+ */
+ fd.dsymbolSemantic(sc);
+ }
+ assert(fd.semanticRun <= PASS.semantic2);
+ fd.semanticRun = PASS.semantic2;
+
+ //printf("FuncDeclaration::semantic2 [%s] fd0 = %s %s\n", loc.toChars(), toChars(), type.toChars());
+
+ // Only check valid functions which have a body to avoid errors
+ // for multiple declarations, e.g.
+ // void foo();
+ // void foo();
+ if (fd.fbody && fd.overnext && !fd.errors)
+ {
+ // Always starts the lookup from 'this', because the conflicts with
+ // previous overloads are already reported.
+ alias f1 = fd;
+ auto tf1 = cast(TypeFunction) f1.type;
+ auto parent1 = f1.toParent2();
+
+ overloadApply(f1, (Dsymbol s)
+ {
+ auto f2 = s.isFuncDeclaration();
+ if (!f2 || f1 == f2 || f2.errors)
+ return 0;
+
+ // Don't have to check conflict between declaration and definition.
+ if (f2.fbody is null)
+ return 0;
+
+ // Functions with different manglings can never conflict
+ if (f1.linkage != f2.linkage)
+ return 0;
+
+ // Functions with different names never conflict
+ // (they can form overloads sets introduced by an alias)
+ if (f1.ident != f2.ident)
+ return 0;
+
+ // Functions with different parents never conflict
+ // (E.g. when aliasing a free function into a struct)
+ if (parent1 != f2.toParent2())
+ return 0;
+
+ /* Check for overload merging with base class member functions.
+ *
+ * class B { void foo() {} }
+ * class D : B {
+ * override void foo() {} // B.foo appears as f2
+ * alias foo = B.foo;
+ * }
+ */
+ if (f1.overrides(f2))
+ return 0;
+
+ auto tf2 = cast(TypeFunction) f2.type;
+
+ // Overloading based on storage classes
+ if (tf1.mod != tf2.mod || ((f1.storage_class ^ f2.storage_class) & STC.static_))
+ return 0;
+
+ const sameAttr = tf1.attributesEqual(tf2);
+ const sameParams = tf1.parameterList == tf2.parameterList;
+
+ // Allow the hack to declare overloads with different parameters/STC's
+ // @@@DEPRECATED_2.094@@@
+ // Deprecated in 2020-08, make this an error in 2.104
+ if (parent1.isModule() &&
+ f1.linkage != LINK.d && f1.linkage != LINK.cpp &&
+ (!sameAttr || !sameParams)
+ )
+ {
+ f2.deprecation("cannot overload `extern(%s)` function at %s",
+ linkageToChars(f1.linkage),
+ f1.loc.toChars());
+ return 0;
+ }
+
+ // Different parameters don't conflict in extern(C++/D)
+ if (!sameParams)
+ return 0;
+
+ // Different attributes don't conflict in extern(D)
+ if (!sameAttr && f1.linkage == LINK.d)
+ return 0;
+
+ error(f2.loc, "%s `%s%s` conflicts with previous declaration at %s",
+ f2.kind(),
+ f2.toPrettyChars(),
+ parametersTypeToChars(tf2.parameterList),
+ f1.loc.toChars());
+ f2.type = Type.terror;
+ f2.errors = true;
+ return 0;
+ });
+ }
+ if (!fd.type || fd.type.ty != Tfunction)
+ return;
+ TypeFunction f = cast(TypeFunction) fd.type;
+
+ UserAttributeDeclaration.checkGNUABITag(fd, fd.linkage);
+ //semantic for parameters' UDAs
+ foreach (i, param; f.parameterList)
+ {
+ if (param && param.userAttribDecl)
+ param.userAttribDecl.semantic2(sc);
+ }
+ }
+
+ override void visit(Import i)
+ {
+ //printf("Import::semantic2('%s')\n", toChars());
+ if (!i.mod)
+ return;
+
+ i.mod.semantic2(null);
+ if (i.mod.needmoduleinfo)
+ {
+ //printf("module5 %s because of %s\n", sc.module.toChars(), mod.toChars());
+ if (sc)
+ sc._module.needmoduleinfo = 1;
+ }
+ }
+
+ override void visit(Nspace ns)
+ {
+ if (ns.semanticRun >= PASS.semantic2)
+ return;
+ ns.semanticRun = PASS.semantic2;
+ static if (LOG)
+ {
+ printf("+Nspace::semantic2('%s')\n", ns.toChars());
+ scope(exit) printf("-Nspace::semantic2('%s')\n", ns.toChars());
+ }
+ UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp);
+ if (!ns.members)
+ return;
+
+ assert(sc);
+ sc = sc.push(ns);
+ sc.linkage = LINK.cpp;
+ foreach (s; *ns.members)
+ {
+ static if (LOG)
+ {
+ printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind());
+ }
+ s.semantic2(sc);
+ }
+ sc.pop();
+ }
+
+ override void visit(AttribDeclaration ad)
+ {
+ Dsymbols* d = ad.include(sc);
+ if (!d)
+ return;
+
+ Scope* sc2 = ad.newScope(sc);
+ for (size_t i = 0; i < d.dim; i++)
+ {
+ Dsymbol s = (*d)[i];
+ s.semantic2(sc2);
+ }
+ if (sc2 != sc)
+ sc2.pop();
+ }
+
+ /**
+ * Run the DeprecatedDeclaration's semantic2 phase then its members.
+ *
+ * The message set via a `DeprecatedDeclaration` can be either of:
+ * - a string literal
+ * - an enum
+ * - a static immutable
+ * So we need to call ctfe to resolve it.
+ * Afterward forwards to the members' semantic2.
+ */
+ override void visit(DeprecatedDeclaration dd)
+ {
+ getMessage(dd);
+ visit(cast(AttribDeclaration)dd);
+ }
+
+ override void visit(AlignDeclaration ad)
+ {
+ ad.getAlignment(sc);
+ visit(cast(AttribDeclaration)ad);
+ }
+
+ override void visit(CPPNamespaceDeclaration decl)
+ {
+ UserAttributeDeclaration.checkGNUABITag(decl, LINK.cpp);
+ visit(cast(AttribDeclaration)decl);
+ }
+
+ override void visit(UserAttributeDeclaration uad)
+ {
+ if (!uad.decl || !uad.atts || !uad.atts.dim || !uad._scope)
+ return visit(cast(AttribDeclaration)uad);
+
+ Expression* lastTag;
+ static void eval(Scope* sc, Expressions* exps, ref Expression* lastTag)
+ {
+ foreach (ref Expression e; *exps)
+ {
+ if (!e)
+ continue;
+
+ e = e.expressionSemantic(sc);
+ if (definitelyValueParameter(e))
+ e = e.ctfeInterpret();
+ if (e.op == TOK.tuple)
+ {
+ TupleExp te = cast(TupleExp)e;
+ eval(sc, te.exps, lastTag);
+ }
+
+ // Handles compiler-recognized `core.attribute.gnuAbiTag`
+ if (UserAttributeDeclaration.isGNUABITag(e))
+ doGNUABITagSemantic(e, lastTag);
+ }
+ }
+
+ uad._scope = null;
+ eval(sc, uad.atts, lastTag);
+ visit(cast(AttribDeclaration)uad);
+ }
+
+ override void visit(AggregateDeclaration ad)
+ {
+ //printf("AggregateDeclaration::semantic2(%s) type = %s, errors = %d\n", ad.toChars(), ad.type.toChars(), ad.errors);
+ if (!ad.members)
+ return;
+
+ if (ad._scope)
+ {
+ ad.error("has forward references");
+ return;
+ }
+
+ UserAttributeDeclaration.checkGNUABITag(
+ ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d);
+
+ auto sc2 = ad.newScope(sc);
+
+ ad.determineSize(ad.loc);
+
+ for (size_t i = 0; i < ad.members.dim; i++)
+ {
+ Dsymbol s = (*ad.members)[i];
+ //printf("\t[%d] %s\n", i, s.toChars());
+ s.semantic2(sc2);
+ }
+
+ sc2.pop();
+ }
+
+ override void visit(ClassDeclaration cd)
+ {
+ /// Checks that the given class implements all methods of its interfaces.
+ static void checkInterfaceImplementations(ClassDeclaration cd)
+ {
+ foreach (base; cd.interfaces)
+ {
+ // first entry is ClassInfo reference
+ auto methods = base.sym.vtbl[base.sym.vtblOffset .. $];
+
+ foreach (m; methods)
+ {
+ auto ifd = m.isFuncDeclaration;
+ assert(ifd);
+
+ if (ifd.objc.isOptional)
+ continue;
+
+ auto type = ifd.type.toTypeFunction();
+ auto fd = cd.findFunc(ifd.ident, type);
+
+ if (fd && !fd.isAbstract)
+ {
+ //printf(" found\n");
+ // Check that calling conventions match
+ if (fd.linkage != ifd.linkage)
+ fd.error("linkage doesn't match interface function");
+
+ // Check that it is current
+ //printf("newinstance = %d fd.toParent() = %s ifd.toParent() = %s\n",
+ //newinstance, fd.toParent().toChars(), ifd.toParent().toChars());
+ if (fd.toParent() != cd && ifd.toParent() == base.sym)
+ cd.error("interface function `%s` is not implemented", ifd.toFullSignature());
+ }
+ else
+ {
+ //printf(" not found %p\n", fd);
+ // BUG: should mark this class as abstract?
+ if (!cd.isAbstract())
+ cd.error("interface function `%s` is not implemented", ifd.toFullSignature());
+ }
+ }
+ }
+ }
+
+ if (cd.semanticRun >= PASS.semantic2done)
+ return;
+ assert(cd.semanticRun <= PASS.semantic2);
+ cd.semanticRun = PASS.semantic2;
+
+ checkInterfaceImplementations(cd);
+ visit(cast(AggregateDeclaration) cd);
+ }
+
+ override void visit(InterfaceDeclaration cd)
+ {
+ visit(cast(AggregateDeclaration) cd);
+ }
+}
+
+/**
+ * Perform semantic analysis specific to the GNU ABI tags
+ *
+ * The GNU ABI tags are a feature introduced in C++11, specific to g++
+ * and the Itanium ABI.
+ * They are mandatory for C++ interfacing, simply because the templated struct
+ *`std::basic_string`, of which the ubiquitous `std::string` is a instantiation
+ * of, uses them.
+ *
+ * Params:
+ * e = Expression to perform semantic on
+ * See `Semantic2Visitor.visit(UserAttributeDeclaration)`
+ * lastTag = When `!is null`, we already saw an ABI tag.
+ * To simplify implementation and reflection code,
+ * only one ABI tag object is allowed per symbol
+ * (but it can have multiple tags as it's an array exp).
+ */
+private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag)
+{
+ import dmd.dmangle;
+
+ // When `@gnuAbiTag` is used, the type will be the UDA, not the struct literal
+ if (e.op == TOK.type)
+ {
+ e.error("`@%s` at least one argument expected", Id.udaGNUAbiTag.toChars());
+ return;
+ }
+
+ // Definition is in `core.attributes`. If it's not a struct literal,
+ // it shouldn't have passed semantic, hence the `assert`.
+ auto sle = e.isStructLiteralExp();
+ if (sle is null)
+ {
+ assert(global.errors);
+ return;
+ }
+ // The definition of `gnuAttributes` only have 1 member, `string[] tags`
+ assert(sle.elements && sle.elements.length == 1);
+ // `gnuAbiTag`'s constructor is defined as `this(string[] tags...)`
+ auto ale = (*sle.elements)[0].isArrayLiteralExp();
+ if (ale is null)
+ {
+ e.error("`@%s` at least one argument expected", Id.udaGNUAbiTag.toChars());
+ return;
+ }
+
+ // Check that it's the only tag on the symbol
+ if (lastTag !is null)
+ {
+ const str1 = (*lastTag.isStructLiteralExp().elements)[0].toString();
+ const str2 = ale.toString();
+ e.error("only one `@%s` allowed per symbol", Id.udaGNUAbiTag.toChars());
+ e.errorSupplemental("instead of `@%s @%s`, use `@%s(%.*s, %.*s)`",
+ lastTag.toChars(), e.toChars(), Id.udaGNUAbiTag.toChars(),
+ // Avoid [ ... ]
+ cast(int)str1.length - 2, str1.ptr + 1,
+ cast(int)str2.length - 2, str2.ptr + 1);
+ return;
+ }
+ lastTag = &e;
+
+ // We already know we have a valid array literal of strings.
+ // Now checks that elements are valid.
+ foreach (idx, elem; *ale.elements)
+ {
+ const str = elem.toStringExp().peekString();
+ if (!str.length)
+ {
+ e.error("argument `%d` to `@%s` cannot be %s", cast(int)(idx + 1),
+ Id.udaGNUAbiTag.toChars(),
+ elem.isNullExp() ? "`null`".ptr : "empty".ptr);
+ continue;
+ }
+
+ foreach (c; str)
+ {
+ if (!c.isValidMangling())
+ {
+ e.error("`@%s` char `0x%02x` not allowed in mangling",
+ Id.udaGNUAbiTag.toChars(), c);
+ break;
+ }
+ }
+ // Valid element
+ }
+ // Since ABI tags need to be sorted, we sort them in place
+ // It might be surprising for users that inspects the UDAs,
+ // but it's a concession to practicality.
+ // Casts are unfortunately necessary as `implicitConvTo` is not
+ // `const` (and nor is `StringExp`, by extension).
+ static int predicate(const scope Expression* e1, const scope Expression* e2) nothrow
+ {
+ scope(failure) assert(0, "An exception was thrown");
+ return (cast(Expression*)e1).toStringExp().compare((cast(Expression*)e2).toStringExp());
+ }
+ ale.elements.sort!predicate;
+}
diff --git a/gcc/d/dmd/semantic3.c b/gcc/d/dmd/semantic3.c
deleted file mode 100644
index 6bd9a6d2e27..00000000000
--- a/gcc/d/dmd/semantic3.c
+++ /dev/null
@@ -1,1399 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "dsymbol.h"
-#include "aggregate.h"
-#include "attrib.h"
-#include "declaration.h"
-#include "errors.h"
-#include "id.h"
-#include "init.h"
-#include "module.h"
-#include "nspace.h"
-#include "scope.h"
-#include "statement.h"
-#include "statement_rewrite_walker.h"
-#include "target.h"
-#include "template.h"
-#include "visitor.h"
-
-bool allowsContractWithoutBody(FuncDeclaration *funcdecl);
-int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow);
-bool checkReturnEscape(Scope *sc, Expression *e, bool gag);
-bool checkReturnEscapeRef(Scope *sc, Expression *e, bool gag);
-TypeIdentifier *getThrowable();
-char *MODtoChars(MOD mod);
-Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
-void allocFieldinit(Scope *sc, size_t dim);
-void freeFieldinit(Scope *sc);
-
-/* Determine if function should add `return 0;`
- */
-static bool addReturn0(FuncDeclaration *funcdecl)
-{
- TypeFunction *f = (TypeFunction *)funcdecl->type;
-
- return f->next->ty == Tvoid &&
- (funcdecl->isMain() || (global.params.betterC && funcdecl->isCMain()));
-}
-
-/********************************************************
- * Generate Expression to call the invariant.
- * Input:
- * ad aggregate with the invariant
- * vthis variable with 'this'
- * Returns:
- * void expression that calls the invariant
- */
-static Expression *addInvariant(AggregateDeclaration *ad, VarDeclaration *vthis)
-{
- Expression *e = NULL;
-
- // Call invariant directly only if it exists
- FuncDeclaration *inv = ad->inv;
- ClassDeclaration *cd = ad->isClassDeclaration();
-
- while (!inv && cd)
- {
- cd = cd->baseClass;
- if (!cd)
- break;
- inv = cd->inv;
- }
- if (inv)
- {
- #if 1
- // Workaround for bugzilla 13394: For the correct mangling,
- // run attribute inference on inv if needed.
- inv->functionSemantic();
- #endif
-
- //e = new DsymbolExp(Loc(), inv);
- //e = new CallExp(Loc(), e);
- //dsymbolSemantic(e, sc2);
-
- /* https://issues.dlang.org/show_bug.cgi?id=13113
- * Currently virtual invariant calls completely
- * bypass attribute enforcement.
- * Change the behavior of pre-invariant call by following it.
- */
- e = new ThisExp(Loc());
- e->type = vthis->type;
- e = new DotVarExp(Loc(), e, inv, false);
- e->type = inv->type;
- e = new CallExp(Loc(), e);
- e->type = Type::tvoid;
- }
- return e;
-}
-
-/* Tweak all return statements and dtor call for nrvo_var, for correct NRVO.
- */
-class NrvoWalker : public StatementRewriteWalker
-{
-public:
- FuncDeclaration *fd;
- Scope *sc;
-
- void visit(ReturnStatement *s)
- {
- // See if all returns are instead to be replaced with a goto returnLabel;
- if (fd->returnLabel)
- {
- /* Rewrite:
- * return exp;
- * as:
- * vresult = exp; goto Lresult;
- */
- GotoStatement *gs = new GotoStatement(s->loc, Id::returnLabel);
- gs->label = fd->returnLabel;
-
- Statement *s1 = gs;
- if (s->exp)
- s1 = new CompoundStatement(s->loc, new ExpStatement(s->loc, s->exp), gs);
-
- replaceCurrent(s1);
- }
- }
- void visit(TryFinallyStatement *s)
- {
- DtorExpStatement *des;
- if (fd->nrvo_can &&
- s->finalbody && (des = s->finalbody->isDtorExpStatement()) != NULL &&
- fd->nrvo_var == des->var)
- {
- if (!(global.params.useExceptions && ClassDeclaration::throwable))
- {
- /* Don't need to call destructor at all, since it is nrvo
- */
- replaceCurrent(s->_body);
- s->_body->accept(this);
- return;
- }
-
- /* Normally local variable dtors are called regardless exceptions.
- * But for nrvo_var, its dtor should be called only when exception is thrown.
- *
- * Rewrite:
- * try { s->body; } finally { nrvo_var->edtor; }
- * // equivalent with:
- * // s->body; scope(exit) nrvo_var->edtor;
- * as:
- * try { s->body; } catch(Throwable __o) { nrvo_var->edtor; throw __o; }
- * // equivalent with:
- * // s->body; scope(failure) nrvo_var->edtor;
- */
- Statement *sexception = new DtorExpStatement(Loc(), fd->nrvo_var->edtor, fd->nrvo_var);
- Identifier *id = Identifier::generateId("__o");
-
- Statement *handler = new PeelStatement(sexception);
- if (blockExit(sexception, fd, false) & BEfallthru)
- {
- ThrowStatement *ts = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id));
- ts->internalThrow = true;
- handler = new CompoundStatement(Loc(), handler, ts);
- }
-
- Catches *catches = new Catches();
- Catch *ctch = new Catch(Loc(), getThrowable(), id, handler);
- ctch->internalCatch = true;
- catchSemantic(ctch, sc); // Run semantic to resolve identifier '__o'
- catches->push(ctch);
-
- Statement *s2 = new TryCatchStatement(Loc(), s->_body, catches);
- replaceCurrent(s2);
- s2->accept(this);
- }
- else
- StatementRewriteWalker::visit(s);
- }
-};
-
-class Semantic3Visitor : public Visitor
-{
-public:
- Scope *sc;
-
- Semantic3Visitor(Scope *sc)
- {
- this->sc = sc;
- }
-
- void visit(Dsymbol *)
- {
- // Most Dsymbols have no further semantic analysis needed
- }
-
- void visit(TemplateInstance *tempinst)
- {
- //if (tempinst->toChars()[0] == 'D') *(char*)0=0;
- if (tempinst->semanticRun >= PASSsemantic3)
- return;
- tempinst->semanticRun = PASSsemantic3;
- if (!tempinst->errors && tempinst->members)
- {
- TemplateDeclaration *tempdecl = tempinst->tempdecl->isTemplateDeclaration();
- assert(tempdecl);
-
- sc = tempdecl->_scope;
- sc = sc->push(tempinst->argsym);
- sc = sc->push(tempinst);
- sc->tinst = tempinst;
- sc->minst = tempinst->minst;
-
- int needGagging = (tempinst->gagged && !global.gag);
- unsigned int olderrors = global.errors;
- int oldGaggedErrors = -1; // dead-store to prevent spurious warning
- /* If this is a gagged instantiation, gag errors.
- * Future optimisation: If the results are actually needed, errors
- * would already be gagged, so we don't really need to run semantic
- * on the members.
- */
- if (needGagging)
- oldGaggedErrors = global.startGagging();
-
- for (size_t i = 0; i < tempinst->members->length; i++)
- {
- Dsymbol *s = (*tempinst->members)[i];
- semantic3(s, sc);
- if (tempinst->gagged && global.errors != olderrors)
- break;
- }
-
- if (global.errors != olderrors)
- {
- if (!tempinst->errors)
- {
- if (!tempdecl->literal)
- tempinst->error(tempinst->loc, "error instantiating");
- if (tempinst->tinst)
- tempinst->tinst->printInstantiationTrace();
- }
- tempinst->errors = true;
- }
- if (needGagging)
- global.endGagging(oldGaggedErrors);
-
- sc = sc->pop();
- sc->pop();
- }
- }
-
- void visit(TemplateMixin *tmix)
- {
- if (tmix->semanticRun >= PASSsemantic3)
- return;
- tmix->semanticRun = PASSsemantic3;
- if (tmix->members)
- {
- sc = sc->push(tmix->argsym);
- sc = sc->push(tmix);
- for (size_t i = 0; i < tmix->members->length; i++)
- {
- Dsymbol *s = (*tmix->members)[i];
- semantic3(s, sc);
- }
- sc = sc->pop();
- sc->pop();
- }
- }
-
- void visit(Module *mod)
- {
- //printf("Module::semantic3('%s'): parent = %p\n", mod->toChars(), mod->parent);
- if (mod->semanticRun != PASSsemantic2done)
- return;
- mod->semanticRun = PASSsemantic3;
-
- // Note that modules get their own scope, from scratch.
- // This is so regardless of where in the syntax a module
- // gets imported, it is unaffected by context.
- Scope *sc = Scope::createGlobal(mod); // create root scope
- //printf("Module = %p\n", sc.scopesym);
-
- // Pass 3 semantic routines: do initializers and function bodies
- for (size_t i = 0; i < mod->members->length; i++)
- {
- Dsymbol *s = (*mod->members)[i];
- //printf("Module %s: %s.semantic3()\n", mod->toChars(), s->toChars());
- semantic3(s, sc);
-
- mod->runDeferredSemantic2();
- }
-
- if (mod->userAttribDecl)
- {
- semantic3(mod->userAttribDecl, sc);
- }
-
- sc = sc->pop();
- sc->pop();
- mod->semanticRun = PASSsemantic3done;
- }
-
- void visit(FuncDeclaration *funcdecl)
- {
- VarDeclaration *_arguments = NULL;
-
- if (!funcdecl->parent)
- {
- if (global.errors)
- return;
- //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", funcdecl->kind(), funcdecl->toChars(), sc);
- assert(0);
- }
- if (funcdecl->errors || isError(funcdecl->parent))
- {
- funcdecl->errors = true;
- return;
- }
- //printf("FuncDeclaration::semantic3('%s.%s', %p, sc = %p, loc = %s)\n", funcdecl->parent->toChars(), funcdecl->toChars(), funcdecl, sc, funcdecl->loc.toChars());
- //fflush(stdout);
- //printf("storage class = x%x %x\n", sc->stc, funcdecl->storage_class);
- //{ static int x; if (++x == 2) *(char*)0=0; }
- //printf("\tlinkage = %d\n", sc->linkage);
-
- if (funcdecl->ident == Id::assign && !funcdecl->inuse)
- {
- if (funcdecl->storage_class & STCinference)
- {
- /* Bugzilla 15044: For generated opAssign function, any errors
- * from its body need to be gagged.
- */
- unsigned oldErrors = global.startGagging();
- funcdecl->inuse++;
- semantic3(funcdecl, sc);
- funcdecl->inuse--;
- if (global.endGagging(oldErrors)) // if errors happened
- {
- // Disable generated opAssign, because some members forbid identity assignment.
- funcdecl->storage_class |= STCdisable;
- funcdecl->fbody = NULL; // remove fbody which contains the error
- funcdecl->semantic3Errors = false;
- }
- return;
- }
- }
-
- //printf(" sc->incontract = %d\n", (sc->flags & SCOPEcontract));
- if (funcdecl->semanticRun >= PASSsemantic3)
- return;
- funcdecl->semanticRun = PASSsemantic3;
- funcdecl->semantic3Errors = false;
-
- if (!funcdecl->type || funcdecl->type->ty != Tfunction)
- return;
- TypeFunction *f = (TypeFunction *)funcdecl->type;
- if (!funcdecl->inferRetType && f->next->ty == Terror)
- return;
-
- if (!funcdecl->fbody && funcdecl->inferRetType && !f->next)
- {
- funcdecl->error("has no function body with return type inference");
- return;
- }
-
- unsigned oldErrors = global.errors;
-
- if (funcdecl->frequires)
- {
- for (size_t i = 0; i < funcdecl->foverrides.length; i++)
- {
- FuncDeclaration *fdv = funcdecl->foverrides[i];
-
- if (fdv->fbody && !fdv->frequires)
- {
- funcdecl->error("cannot have an in contract when overriden function %s does not have an in contract", fdv->toPrettyChars());
- break;
- }
- }
- }
-
- // Remember whether we need to generate an 'out' contract.
- const bool needEnsure = FuncDeclaration::needsFensure(funcdecl);
-
- if (funcdecl->fbody || funcdecl->frequires || needEnsure)
- {
- /* Symbol table into which we place parameters and nested functions,
- * solely to diagnose name collisions.
- */
- funcdecl->localsymtab = new DsymbolTable();
-
- // Establish function scope
- ScopeDsymbol *ss = new ScopeDsymbol();
- // find enclosing scope symbol, might skip symbol-less CTFE and/or FuncExp scopes
- for (Scope *scx = sc; ; scx = scx->enclosing)
- {
- if (scx->scopesym)
- {
- ss->parent = scx->scopesym;
- break;
- }
- }
- ss->loc = funcdecl->loc;
- ss->endlinnum = funcdecl->endloc.linnum;
- Scope *sc2 = sc->push(ss);
- sc2->func = funcdecl;
- sc2->parent = funcdecl;
- sc2->callSuper = 0;
- sc2->sbreak = NULL;
- sc2->scontinue = NULL;
- sc2->sw = NULL;
- sc2->fes = funcdecl->fes;
- sc2->linkage = LINKd;
- sc2->stc &= ~(STCauto | STCscope | STCstatic | STCextern | STCabstract |
- STCdeprecated | STCoverride |
- STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref | STCreturn |
- STCproperty | STCnothrow | STCpure | STCsafe | STCtrusted | STCsystem);
- sc2->protection = Prot(Prot::public_);
- sc2->explicitProtection = 0;
- sc2->aligndecl = NULL;
- if (funcdecl->ident != Id::require && funcdecl->ident != Id::ensure)
- sc2->flags = sc->flags & ~SCOPEcontract;
- sc2->flags &= ~SCOPEcompile;
- sc2->tf = NULL;
- sc2->os = NULL;
- sc2->noctor = 0;
- sc2->userAttribDecl = NULL;
- if (sc2->intypeof == 1) sc2->intypeof = 2;
- sc2->fieldinit = NULL;
- sc2->fieldinit_dim = 0;
-
- /* Note: When a lambda is defined immediately under aggregate member
- * scope, it should be contextless due to prevent interior pointers.
- * e.g.
- * // dg points 'this' - it's interior pointer
- * class C { int x; void delegate() dg = (){ this.x = 1; }; }
- *
- * However, lambdas could be used inside typeof, in order to check
- * some expressions varidity at compile time. For such case the lambda
- * body can access aggregate instance members.
- * e.g.
- * class C { int x; static assert(is(typeof({ this.x = 1; }))); }
- *
- * To properly accept it, mark these lambdas as member functions.
- */
- if (FuncLiteralDeclaration *fld = funcdecl->isFuncLiteralDeclaration())
- {
- if (AggregateDeclaration *ad = funcdecl->isMember2())
- {
- if (!sc->intypeof)
- {
- if (fld->tok == TOKdelegate)
- funcdecl->error("cannot be %s members", ad->kind());
- else
- fld->tok = TOKfunction;
- }
- else
- {
- if (fld->tok != TOKfunction)
- fld->tok = TOKdelegate;
- }
- }
- }
-
- // Declare 'this'
- AggregateDeclaration *ad = funcdecl->isThis();
- funcdecl->vthis = funcdecl->declareThis(sc2, ad);
- //printf("[%s] ad = %p vthis = %p\n", funcdecl->loc.toChars(), ad, funcdecl->vthis);
- //if (funcdecl->vthis) printf("\tvthis->type = %s\n", funcdecl->vthis->type->toChars());
-
- // Declare hidden variable _arguments[] and _argptr
- if (f->parameterList.varargs == VARARGvariadic)
- {
- if (f->linkage == LINKd)
- {
- // Variadic arguments depend on Typeinfo being defined
- if (!global.params.useTypeInfo || !Type::dtypeinfo || !Type::typeinfotypelist)
- {
- if (!global.params.useTypeInfo)
- funcdecl->error("D-style variadic functions cannot be used with -betterC");
- else if (!Type::typeinfotypelist)
- funcdecl->error("`object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions");
- else
- funcdecl->error("`object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions");
- fatal();
- }
-
- // Declare _arguments[]
- funcdecl->v_arguments = new VarDeclaration(Loc(), Type::typeinfotypelist->type, Id::_arguments_typeinfo, NULL);
- funcdecl->v_arguments->storage_class |= STCtemp | STCparameter;
- dsymbolSemantic(funcdecl->v_arguments, sc2);
- sc2->insert(funcdecl->v_arguments);
- funcdecl->v_arguments->parent = funcdecl;
-
- //Type *t = Type::typeinfo->type->constOf()->arrayOf();
- Type *t = Type::dtypeinfo->type->arrayOf();
- _arguments = new VarDeclaration(Loc(), t, Id::_arguments, NULL);
- _arguments->storage_class |= STCtemp;
- dsymbolSemantic(_arguments, sc2);
- sc2->insert(_arguments);
- _arguments->parent = funcdecl;
- }
- if (f->linkage == LINKd || f->parameterList.length())
- {
- // Declare _argptr
- Type *t = target.va_listType(funcdecl->loc, sc);
- funcdecl->v_argptr = new VarDeclaration(Loc(), t, Id::_argptr, NULL);
- funcdecl->v_argptr->storage_class |= STCtemp;
- dsymbolSemantic(funcdecl->v_argptr, sc2);
- sc2->insert(funcdecl->v_argptr);
- funcdecl->v_argptr->parent = funcdecl;
- }
- }
-
- /* Declare all the function parameters as variables
- * and install them in parameters[]
- */
- size_t nparams = f->parameterList.length();
- if (nparams)
- {
- /* parameters[] has all the tuples removed, as the back end
- * doesn't know about tuples
- */
- funcdecl->parameters = new VarDeclarations();
- funcdecl->parameters->reserve(nparams);
- for (size_t i = 0; i < nparams; i++)
- {
- Parameter *fparam = f->parameterList[i];
- Identifier *id = fparam->ident;
- StorageClass stc = 0;
- if (!id)
- {
- /* Generate identifier for un-named parameter,
- * because we need it later on.
- */
- fparam->ident = id = Identifier::generateId("_param_", i);
- stc |= STCtemp;
- }
- Type *vtype = fparam->type;
- VarDeclaration *v = new VarDeclaration(funcdecl->loc, vtype, id, NULL);
- //printf("declaring parameter %s of type %s\n", v->toChars(), v->type->toChars());
- stc |= STCparameter;
- if (f->parameterList.varargs == VARARGtypesafe && i + 1 == nparams)
- stc |= STCvariadic;
- if (funcdecl->flags & FUNCFLAGinferScope && !(fparam->storageClass & STCscope))
- stc |= STCmaybescope;
- stc |= fparam->storageClass & (STCin | STCout | STCref | STCreturn | STCscope | STClazy | STCfinal | STC_TYPECTOR | STCnodtor);
- v->storage_class = stc;
- dsymbolSemantic(v, sc2);
- if (!sc2->insert(v))
- funcdecl->error("parameter %s.%s is already defined", funcdecl->toChars(), v->toChars());
- else
- funcdecl->parameters->push(v);
- funcdecl->localsymtab->insert(v);
- v->parent = funcdecl;
- if (fparam->userAttribDecl)
- v->userAttribDecl = fparam->userAttribDecl;
- }
- }
-
- // Declare the tuple symbols and put them in the symbol table,
- // but not in parameters[].
- if (f->parameterList.parameters)
- {
- for (size_t i = 0; i < f->parameterList.parameters->length; i++)
- {
- Parameter *fparam = (*f->parameterList.parameters)[i];
-
- if (!fparam->ident)
- continue; // never used, so ignore
- if (fparam->type->ty == Ttuple)
- {
- TypeTuple *t = (TypeTuple *)fparam->type;
- size_t dim = Parameter::dim(t->arguments);
- Objects *exps = new Objects();
- exps->setDim(dim);
- for (size_t j = 0; j < dim; j++)
- {
- Parameter *narg = Parameter::getNth(t->arguments, j);
- assert(narg->ident);
- VarDeclaration *v = sc2->search(Loc(), narg->ident, NULL)->isVarDeclaration();
- assert(v);
- Expression *e = new VarExp(v->loc, v);
- (*exps)[j] = e;
- }
- assert(fparam->ident);
- TupleDeclaration *v = new TupleDeclaration(funcdecl->loc, fparam->ident, exps);
- //printf("declaring tuple %s\n", v->toChars());
- v->isexp = true;
- if (!sc2->insert(v))
- funcdecl->error("parameter %s.%s is already defined", funcdecl->toChars(), v->toChars());
- funcdecl->localsymtab->insert(v);
- v->parent = funcdecl;
- }
- }
- }
-
- // Precondition invariant
- Statement *fpreinv = NULL;
- if (funcdecl->addPreInvariant())
- {
- Expression *e = addInvariant(ad, funcdecl->vthis);
- if (e)
- fpreinv = new ExpStatement(Loc(), e);
- }
-
- // Postcondition invariant
- Statement *fpostinv = NULL;
- if (funcdecl->addPostInvariant())
- {
- Expression *e = addInvariant(ad, funcdecl->vthis);
- if (e)
- fpostinv = new ExpStatement(Loc(), e);
- }
-
- // Pre/Postcondition contract
- if (!funcdecl->fbody)
- funcdecl->buildEnsureRequire();
-
- Scope *scout = NULL;
- if (needEnsure || funcdecl->addPostInvariant())
- {
- if ((needEnsure && global.params.useOut == CHECKENABLEon) || fpostinv)
- {
- funcdecl->returnLabel = new LabelDsymbol(Id::returnLabel);
- }
-
- // scope of out contract (need for vresult->semantic)
- ScopeDsymbol *sym = new ScopeDsymbol();
- sym->parent = sc2->scopesym;
- sym->loc = funcdecl->loc;
- sym->endlinnum = funcdecl->endloc.linnum;
- scout = sc2->push(sym);
- }
-
- if (funcdecl->fbody)
- {
- ScopeDsymbol *sym = new ScopeDsymbol();
- sym->parent = sc2->scopesym;
- sym->loc = funcdecl->loc;
- sym->endlinnum = funcdecl->endloc.linnum;
- sc2 = sc2->push(sym);
-
- AggregateDeclaration *ad2 = funcdecl->isMember2();
-
- /* If this is a class constructor
- */
- if (ad2 && funcdecl->isCtorDeclaration())
- {
- allocFieldinit(sc2, ad2->fields.length);
- for (size_t i = 0; i < ad2->fields.length; i++)
- {
- VarDeclaration *v = ad2->fields[i];
- v->ctorinit = 0;
- }
- }
-
- bool inferRef = (f->isref && (funcdecl->storage_class & STCauto));
-
- funcdecl->fbody = statementSemantic(funcdecl->fbody, sc2);
- if (!funcdecl->fbody)
- funcdecl->fbody = new CompoundStatement(Loc(), new Statements());
-
- if (funcdecl->naked)
- {
- fpreinv = NULL; // can't accommodate with no stack frame
- fpostinv = NULL;
- }
-
- assert(funcdecl->type == f ||
- (funcdecl->type->ty == Tfunction &&
- f->purity == PUREimpure &&
- ((TypeFunction *)funcdecl->type)->purity >= PUREfwdref));
- f = (TypeFunction *)funcdecl->type;
-
- if (funcdecl->inferRetType)
- {
- // If no return type inferred yet, then infer a void
- if (!f->next)
- f->next = Type::tvoid;
- if (f->checkRetType(funcdecl->loc))
- funcdecl->fbody = new ErrorStatement();
- }
- if (global.params.vcomplex && f->next != NULL)
- f->next->checkComplexTransition(funcdecl->loc);
-
- if (funcdecl->returns && !funcdecl->fbody->isErrorStatement())
- {
- for (size_t i = 0; i < funcdecl->returns->length; )
- {
- Expression *exp = (*funcdecl->returns)[i]->exp;
- if (exp->op == TOKvar && ((VarExp *)exp)->var == funcdecl->vresult)
- {
- if (addReturn0(funcdecl))
- exp->type = Type::tint32;
- else
- exp->type = f->next;
- // Remove `return vresult;` from returns
- funcdecl->returns->remove(i);
- continue;
- }
- if (inferRef && f->isref && !exp->type->constConv(f->next)) // Bugzilla 13336
- f->isref = false;
- i++;
- }
- }
- if (f->isref) // Function returns a reference
- {
- if (funcdecl->storage_class & STCauto)
- funcdecl->storage_class &= ~STCauto;
- }
- if (!target.isReturnOnStack(f, funcdecl->needThis()) || !funcdecl->checkNRVO())
- funcdecl->nrvo_can = 0;
-
- if (funcdecl->fbody->isErrorStatement())
- ;
- else if (funcdecl->isStaticCtorDeclaration())
- {
- /* It's a static constructor. Ensure that all
- * ctor consts were initialized.
- */
- ScopeDsymbol *pd = funcdecl->toParent()->isScopeDsymbol();
- for (size_t i = 0; i < pd->members->length; i++)
- {
- Dsymbol *s = (*pd->members)[i];
- s->checkCtorConstInit();
- }
- }
- else if (ad2 && funcdecl->isCtorDeclaration())
- {
- ClassDeclaration *cd = ad2->isClassDeclaration();
-
- // Verify that all the ctorinit fields got initialized
- if (!(sc2->callSuper & CSXthis_ctor))
- {
- for (size_t i = 0; i < ad2->fields.length; i++)
- {
- VarDeclaration *v = ad2->fields[i];
- if (v->isThisDeclaration())
- continue;
- if (v->ctorinit == 0)
- {
- /* Current bugs in the flow analysis:
- * 1. union members should not produce error messages even if
- * not assigned to
- * 2. structs should recognize delegating opAssign calls as well
- * as delegating calls to other constructors
- */
- if (v->isCtorinit() && !v->type->isMutable() && cd)
- funcdecl->error("missing initializer for %s field %s", MODtoChars(v->type->mod), v->toChars());
- else if (v->storage_class & STCnodefaultctor)
- error(funcdecl->loc, "field %s must be initialized in constructor", v->toChars());
- else if (v->type->needsNested())
- error(funcdecl->loc, "field %s must be initialized in constructor, because it is nested struct", v->toChars());
- }
- else
- {
- bool mustInit = (v->storage_class & STCnodefaultctor ||
- v->type->needsNested());
- if (mustInit && !(sc2->fieldinit[i] & CSXthis_ctor))
- {
- funcdecl->error("field %s must be initialized but skipped", v->toChars());
- }
- }
- }
- }
- freeFieldinit(sc2);
-
- if (cd &&
- !(sc2->callSuper & CSXany_ctor) &&
- cd->baseClass && cd->baseClass->ctor)
- {
- sc2->callSuper = 0;
-
- // Insert implicit super() at start of fbody
- FuncDeclaration *fd = resolveFuncCall(Loc(), sc2, cd->baseClass->ctor, NULL, funcdecl->vthis->type, NULL, 1);
- if (!fd)
- {
- funcdecl->error("no match for implicit super() call in constructor");
- }
- else if (fd->storage_class & STCdisable)
- {
- funcdecl->error("cannot call super() implicitly because it is annotated with @disable");
- }
- else
- {
- Expression *e1 = new SuperExp(Loc());
- Expression *e = new CallExp(Loc(), e1);
- e = expressionSemantic(e, sc2);
-
- Statement *s = new ExpStatement(Loc(), e);
- funcdecl->fbody = new CompoundStatement(Loc(), s, funcdecl->fbody);
- }
- }
- //printf("callSuper = x%x\n", sc2->callSuper);
- }
-
- /* https://issues.dlang.org/show_bug.cgi?id=17502
- * Wait until after the return type has been inferred before
- * generating the contracts for this function, and merging contracts
- * from overrides.
- *
- * https://issues.dlang.org/show_bug.cgi?id=17893
- * However should take care to generate this before inferered
- * function attributes are applied, such as 'nothrow'.
- *
- * This was originally at the end of the first semantic pass, but
- * required a fix-up to be done here for the '__result' variable
- * type of __ensure() inside auto functions, but this didn't work
- * if the out parameter was implicit.
- */
- funcdecl->buildEnsureRequire();
-
- int blockexit = BEnone;
- if (!funcdecl->fbody->isErrorStatement())
- {
- // Check for errors related to 'nothrow'.
- unsigned int nothrowErrors = global.errors;
- blockexit = blockExit(funcdecl->fbody, funcdecl, f->isnothrow);
- if (f->isnothrow && (global.errors != nothrowErrors))
- error(funcdecl->loc, "nothrow %s `%s` may throw", funcdecl->kind(), funcdecl->toPrettyChars());
- if (funcdecl->flags & FUNCFLAGnothrowInprocess)
- {
- if (funcdecl->type == f) f = (TypeFunction *)f->copy();
- f->isnothrow = !(blockexit & BEthrow);
- }
- }
-
- if (funcdecl->fbody->isErrorStatement())
- ;
- else if (ad2 && funcdecl->isCtorDeclaration())
- {
- /* Append:
- * return this;
- * to function body
- */
- if (blockexit & BEfallthru)
- {
- Statement *s = new ReturnStatement(funcdecl->loc, NULL);
- s = statementSemantic(s, sc2);
- funcdecl->fbody = new CompoundStatement(funcdecl->loc, funcdecl->fbody, s);
- funcdecl->hasReturnExp |= (funcdecl->hasReturnExp & 1 ? 16 : 1);
- }
- }
- else if (funcdecl->fes)
- {
- // For foreach(){} body, append a return 0;
- if (blockexit & BEfallthru)
- {
- Expression *e = new IntegerExp(0);
- Statement *s = new ReturnStatement(Loc(), e);
- funcdecl->fbody = new CompoundStatement(Loc(), funcdecl->fbody, s);
- funcdecl->hasReturnExp |= (funcdecl->hasReturnExp & 1 ? 16 : 1);
- }
- assert(!funcdecl->returnLabel);
- }
- else if (f->next->ty == Tnoreturn)
- {
- }
- else
- {
- const bool inlineAsm = (funcdecl->hasReturnExp & 8) != 0;
- if ((blockexit & BEfallthru) && f->next->ty != Tvoid && !inlineAsm)
- {
- if (!funcdecl->hasReturnExp)
- funcdecl->error("has no `return` statement, but is expected to return a value of type `%s`", f->next->toChars());
- else
- funcdecl->error("no `return exp;` or `assert(0);` at end of function");
- }
- }
-
- if (funcdecl->returns)
- {
- bool implicit0 = addReturn0(funcdecl);
- Type *tret = implicit0 ? Type::tint32 : f->next;
- assert(tret->ty != Tvoid);
- if (funcdecl->vresult || funcdecl->returnLabel)
- funcdecl->buildResultVar(scout ? scout : sc2, tret);
-
- /* Cannot move this loop into NrvoWalker, because
- * returns[i] may be in the nested delegate for foreach-body.
- */
- for (size_t i = 0; i < funcdecl->returns->length; i++)
- {
- ReturnStatement *rs = (*funcdecl->returns)[i];
- Expression *exp = rs->exp;
- if (exp->op == TOKerror)
- continue;
- if (tret->ty == Terror)
- {
- // Bugzilla 13702
- exp = checkGC(sc2, exp);
- continue;
- }
-
- if (!exp->implicitConvTo(tret) &&
- funcdecl->parametersIntersect(exp->type))
- {
- if (exp->type->immutableOf()->implicitConvTo(tret))
- exp = exp->castTo(sc2, exp->type->immutableOf());
- else if (exp->type->wildOf()->implicitConvTo(tret))
- exp = exp->castTo(sc2, exp->type->wildOf());
- }
- exp = exp->implicitCastTo(sc2, tret);
-
- if (f->isref)
- {
- // Function returns a reference
- exp = exp->toLvalue(sc2, exp);
- checkReturnEscapeRef(sc2, exp, false);
- }
- else
- {
- exp = exp->optimize(WANTvalue);
-
- /* Bugzilla 10789:
- * If NRVO is not possible, all returned lvalues should call their postblits.
- */
- if (!funcdecl->nrvo_can)
- exp = doCopyOrMove(sc2, exp);
-
- if (tret->hasPointers())
- checkReturnEscape(sc2, exp, false);
- }
-
- exp = checkGC(sc2, exp);
-
- if (funcdecl->vresult)
- {
- // Create: return vresult = exp;
- exp = new BlitExp(rs->loc, funcdecl->vresult, exp);
- exp->type = funcdecl->vresult->type;
-
- if (rs->caseDim)
- exp = Expression::combine(exp, new IntegerExp(rs->caseDim));
- }
- else if (funcdecl->tintro && !tret->equals(funcdecl->tintro->nextOf()))
- {
- exp = exp->implicitCastTo(sc2, funcdecl->tintro->nextOf());
- }
- rs->exp = exp;
- }
- }
- if (funcdecl->nrvo_var || funcdecl->returnLabel)
- {
- NrvoWalker nw;
- nw.fd = funcdecl;
- nw.sc = sc2;
- nw.visitStmt(funcdecl->fbody);
- }
-
- sc2 = sc2->pop();
- }
-
- funcdecl->frequire = funcdecl->mergeFrequire(funcdecl->frequire);
- funcdecl->fensure = funcdecl->mergeFensure(funcdecl->fensure, Id::result);
-
- Statement *freq = funcdecl->frequire;
- Statement *fens = funcdecl->fensure;
-
- /* Do the semantic analysis on the [in] preconditions and
- * [out] postconditions.
- */
- if (freq)
- {
- /* frequire is composed of the [in] contracts
- */
- ScopeDsymbol *sym = new ScopeDsymbol();
- sym->parent = sc2->scopesym;
- sym->loc = funcdecl->loc;
- sym->endlinnum = funcdecl->endloc.linnum;
- sc2 = sc2->push(sym);
- sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPErequire;
-
- // BUG: need to error if accessing out parameters
- // BUG: need to disallow returns and throws
- // BUG: verify that all in and ref parameters are read
- freq = statementSemantic(freq, sc2);
- blockExit(freq, funcdecl, false);
-
- sc2 = sc2->pop();
-
- if (global.params.useIn == CHECKENABLEoff)
- freq = NULL;
- }
-
- if (fens)
- {
- /* fensure is composed of the [out] contracts
- */
- if (f->next->ty == Tvoid && funcdecl->fensures)
- {
- for (size_t i = 0; i < funcdecl->fensures->length; i++)
- {
- Ensure e = (*funcdecl->fensures)[i];
- if (e.id)
- {
- funcdecl->error(e.ensure->loc, "`void` functions have no result");
- //fens = NULL;
- }
- }
- }
-
- sc2 = scout; //push
- sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPEensure;
-
- // BUG: need to disallow returns and throws
- if (funcdecl->fensure && f->next->ty != Tvoid)
- funcdecl->buildResultVar(scout, f->next);
-
- fens = statementSemantic(fens, sc2);
- blockExit(fens, funcdecl, false);
-
- sc2 = sc2->pop();
-
- if (global.params.useOut == CHECKENABLEoff)
- fens = NULL;
- }
-
- if (funcdecl->fbody && funcdecl->fbody->isErrorStatement())
- ;
- else
- {
- Statements *a = new Statements();
-
- // Merge in initialization of 'out' parameters
- if (funcdecl->parameters)
- {
- for (size_t i = 0; i < funcdecl->parameters->length; i++)
- {
- VarDeclaration *v = (*funcdecl->parameters)[i];
- if (v->storage_class & STCout)
- {
- assert(v->_init);
- ExpInitializer *ie = v->_init->isExpInitializer();
- assert(ie);
- if (ie->exp->op == TOKconstruct)
- ie->exp->op = TOKassign; // construction occured in parameter processing
- a->push(new ExpStatement(Loc(), ie->exp));
- }
- }
- }
-
- if (funcdecl->v_argptr)
- {
- // Handled in FuncDeclaration::toObjFile
- funcdecl->v_argptr->_init = new VoidInitializer(funcdecl->loc);
- }
-
- if (_arguments)
- {
- /* Advance to elements[] member of TypeInfo_Tuple with:
- * _arguments = v_arguments.elements;
- */
- Expression *e = new VarExp(Loc(), funcdecl->v_arguments);
- e = new DotIdExp(Loc(), e, Id::elements);
- e = new ConstructExp(Loc(), _arguments, e);
- e = expressionSemantic(e, sc2);
-
- _arguments->_init = new ExpInitializer(Loc(), e);
- DeclarationExp *de = new DeclarationExp(Loc(), _arguments);
- a->push(new ExpStatement(Loc(), de));
- }
-
- // Merge contracts together with body into one compound statement
-
- if (freq || fpreinv)
- {
- if (!freq)
- freq = fpreinv;
- else if (fpreinv)
- freq = new CompoundStatement(Loc(), freq, fpreinv);
-
- a->push(freq);
- }
-
- if (funcdecl->fbody)
- a->push(funcdecl->fbody);
-
- if (fens || fpostinv)
- {
- if (!fens)
- fens = fpostinv;
- else if (fpostinv)
- fens = new CompoundStatement(Loc(), fpostinv, fens);
-
- LabelStatement *ls = new LabelStatement(Loc(), Id::returnLabel, fens);
- funcdecl->returnLabel->statement = ls;
- a->push(funcdecl->returnLabel->statement);
-
- if (f->next->ty != Tvoid && funcdecl->vresult)
- {
- // Create: return vresult;
- Expression *e = new VarExp(Loc(), funcdecl->vresult);
- if (funcdecl->tintro)
- {
- e = e->implicitCastTo(sc, funcdecl->tintro->nextOf());
- e = expressionSemantic(e, sc);
- }
- ReturnStatement *s = new ReturnStatement(Loc(), e);
- a->push(s);
- }
- }
- if (addReturn0(funcdecl))
- {
- // Add a return 0; statement
- Statement *s = new ReturnStatement(Loc(), new IntegerExp(0));
- a->push(s);
- }
-
- Statement *sbody = new CompoundStatement(Loc(), a);
- /* Append destructor calls for parameters as finally blocks.
- */
- if (funcdecl->parameters)
- {
- for (size_t i = 0; i < funcdecl->parameters->length; i++)
- {
- VarDeclaration *v = (*funcdecl->parameters)[i];
-
- if (v->storage_class & (STCref | STCout | STClazy))
- continue;
-
- if (v->needsScopeDtor())
- {
- // same with ExpStatement.scopeCode()
- Statement *s = new DtorExpStatement(Loc(), v->edtor, v);
- v->storage_class |= STCnodtor;
-
- s = statementSemantic(s, sc2);
-
- bool isnothrow = f->isnothrow & !(funcdecl->flags & FUNCFLAGnothrowInprocess);
- int blockexit = blockExit(s, funcdecl, isnothrow);
- if (f->isnothrow && isnothrow && blockexit & BEthrow)
- error(funcdecl->loc, "nothrow %s `%s` may throw", funcdecl->kind(), funcdecl->toPrettyChars());
- if (funcdecl->flags & FUNCFLAGnothrowInprocess && blockexit & BEthrow)
- f->isnothrow = false;
- if (blockExit(sbody, funcdecl, f->isnothrow) == BEfallthru)
- sbody = new CompoundStatement(Loc(), sbody, s);
- else
- sbody = new TryFinallyStatement(Loc(), sbody, s);
- }
- }
- }
- // from this point on all possible 'throwers' are checked
- funcdecl->flags &= ~FUNCFLAGnothrowInprocess;
-
- if (funcdecl->isSynchronized())
- {
- /* Wrap the entire function body in a synchronized statement
- */
- ClassDeclaration *cd = funcdecl->isThis() ? funcdecl->isThis()->isClassDeclaration() : funcdecl->parent->isClassDeclaration();
-
- if (cd)
- {
- if (target.libraryObjectMonitors(funcdecl, sbody))
- {
- Expression *vsync;
- if (funcdecl->isStatic())
- {
- // The monitor is in the ClassInfo
- vsync = new DotIdExp(funcdecl->loc, resolve(funcdecl->loc, sc2, cd, false), Id::classinfo);
- }
- else
- {
- // 'this' is the monitor
- vsync = new VarExp(funcdecl->loc, funcdecl->vthis);
- }
- sbody = new PeelStatement(sbody); // don't redo semantic()
- sbody = new SynchronizedStatement(funcdecl->loc, vsync, sbody);
- sbody = statementSemantic(sbody, sc2);
- }
- }
- else
- {
- funcdecl->error("synchronized function %s must be a member of a class", funcdecl->toChars());
- }
- }
-
- // If declaration has no body, don't set sbody to prevent incorrect codegen.
- if (funcdecl->fbody || allowsContractWithoutBody(funcdecl))
- funcdecl->fbody = sbody;
- }
-
- // Fix up forward-referenced gotos
- if (funcdecl->gotos)
- {
- for (size_t i = 0; i < funcdecl->gotos->length; ++i)
- {
- (*funcdecl->gotos)[i]->checkLabel();
- }
- }
-
- if (funcdecl->naked && (funcdecl->fensures || funcdecl->frequires))
- funcdecl->error("naked assembly functions with contracts are not supported");
-
- sc2->callSuper = 0;
- sc2->pop();
- }
-
- if (funcdecl->checkClosure())
- {
- // We should be setting errors here instead of relying on the global error count.
- //errors = true;
- }
-
- /* If function survived being marked as impure, then it is pure
- */
- if (funcdecl->flags & FUNCFLAGpurityInprocess)
- {
- funcdecl->flags &= ~FUNCFLAGpurityInprocess;
- if (funcdecl->type == f)
- f = (TypeFunction *)f->copy();
- f->purity = PUREfwdref;
- }
-
- if (funcdecl->flags & FUNCFLAGsafetyInprocess)
- {
- funcdecl->flags &= ~FUNCFLAGsafetyInprocess;
- if (funcdecl->type == f)
- f = (TypeFunction *)f->copy();
- f->trust = TRUSTsafe;
- }
-
- if (funcdecl->flags & FUNCFLAGnogcInprocess)
- {
- funcdecl->flags &= ~FUNCFLAGnogcInprocess;
- if (funcdecl->type == f)
- f = (TypeFunction *)f->copy();
- f->isnogc = true;
- }
-
- if (funcdecl->flags & FUNCFLAGreturnInprocess)
- {
- funcdecl->flags &= ~FUNCFLAGreturnInprocess;
- if (funcdecl->storage_class & STCreturn)
- {
- if (funcdecl->type == f)
- f = (TypeFunction *)f->copy();
- f->isreturn = true;
- }
- }
-
- funcdecl->flags &= ~FUNCFLAGinferScope;
-
- // Infer STCscope
- if (funcdecl->parameters)
- {
- size_t nfparams = f->parameterList.length();
- assert(nfparams == funcdecl->parameters->length);
- for (size_t u = 0; u < funcdecl->parameters->length; u++)
- {
- VarDeclaration *v = (*funcdecl->parameters)[u];
- if (v->storage_class & STCmaybescope)
- {
- //printf("Inferring scope for %s\n", v->toChars());
- Parameter *p = f->parameterList[u];
- v->storage_class &= ~STCmaybescope;
- v->storage_class |= STCscope | STCscopeinferred;
- p->storageClass |= STCscope | STCscopeinferred;
- assert(!(p->storageClass & STCmaybescope));
- }
- }
- }
-
- if (funcdecl->vthis && funcdecl->vthis->storage_class & STCmaybescope)
- {
- funcdecl->vthis->storage_class &= ~STCmaybescope;
- funcdecl->vthis->storage_class |= STCscope | STCscopeinferred;
- f->isscope = true;
- f->isscopeinferred = true;
- }
-
- // reset deco to apply inference result to mangled name
- if (f != funcdecl->type)
- f->deco = NULL;
-
- // Do semantic type AFTER pure/nothrow inference.
- if (!f->deco && funcdecl->ident != Id::xopEquals && funcdecl->ident != Id::xopCmp)
- {
- sc = sc->push();
- if (funcdecl->isCtorDeclaration()) // Bugzilla #15665
- sc->flags |= SCOPEctor;
- sc->stc = 0;
- sc->linkage = funcdecl->linkage; // Bugzilla 8496
- funcdecl->type = typeSemantic(f, funcdecl->loc, sc);
- sc = sc->pop();
- }
-
- /* If this function had instantiated with gagging, error reproduction will be
- * done by TemplateInstance::semantic.
- * Otherwise, error gagging should be temporarily ungagged by functionSemantic3.
- */
- funcdecl->semanticRun = PASSsemantic3done;
- funcdecl->semantic3Errors = (global.errors != oldErrors) || (funcdecl->fbody && funcdecl->fbody->isErrorStatement());
- if (funcdecl->type->ty == Terror)
- funcdecl->errors = true;
- //printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", funcdecl->parent->toChars(), funcdecl->toChars(), sc, funcdecl->loc.toChars());
- //fflush(stdout);
- }
-
- void visit(Nspace *ns)
- {
- if (ns->semanticRun >= PASSsemantic3)
- return;
- ns->semanticRun = PASSsemantic3;
- if (ns->members)
- {
- sc = sc->push(ns);
- sc->linkage = LINKcpp;
- for (size_t i = 0; i < ns->members->length; i++)
- {
- Dsymbol *s = (*ns->members)[i];
- semantic3(s, sc);
- }
- sc->pop();
- }
- }
-
- void visit(AttribDeclaration *ad)
- {
- Dsymbols *d = ad->include(sc);
-
- if (d)
- {
- Scope *sc2 = ad->newScope(sc);
-
- for (size_t i = 0; i < d->length; i++)
- {
- Dsymbol *s = (*d)[i];
- semantic3(s, sc2);
- }
-
- if (sc2 != sc)
- sc2->pop();
- }
- }
-
- void visit(AggregateDeclaration *ad)
- {
- //printf("AggregateDeclaration::semantic3(%s) type = %s, errors = %d\n", ad->toChars(), ad->type->toChars(), ad->errors);
- if (!ad->members)
- return;
-
- StructDeclaration *sd = ad->isStructDeclaration();
- if (!sc) // from runDeferredSemantic3 for TypeInfo generation
- {
- assert(sd);
- sd->semanticTypeInfoMembers();
- return;
- }
-
- Scope *sc2 = ad->newScope(sc);
-
- for (size_t i = 0; i < ad->members->length; i++)
- {
- Dsymbol *s = (*ad->members)[i];
- semantic3(s, sc2);
- }
-
- sc2->pop();
-
- // don't do it for unused deprecated types
- // or error types
- if (!ad->getRTInfo && Type::rtinfo &&
- (!ad->isDeprecated() || global.params.useDeprecated != DIAGNOSTICerror) &&
- (ad->type && ad->type->ty != Terror))
- {
- // Evaluate: RTinfo!type
- Objects *tiargs = new Objects();
- tiargs->push(ad->type);
- TemplateInstance *ti = new TemplateInstance(ad->loc, Type::rtinfo, tiargs);
-
- Scope *sc3 = ti->tempdecl->_scope->startCTFE();
- sc3->tinst = sc->tinst;
- sc3->minst = sc->minst;
- if (ad->isDeprecated())
- sc3->stc |= STCdeprecated;
-
- dsymbolSemantic(ti, sc3);
- semantic2(ti, sc3);
- semantic3(ti, sc3);
- Expression *e = resolve(Loc(), sc3, ti->toAlias(), false);
-
- sc3->endCTFE();
-
- e = e->ctfeInterpret();
- ad->getRTInfo = e;
- }
-
- if (sd)
- sd->semanticTypeInfoMembers();
- ad->semanticRun = PASSsemantic3done;
- }
-};
-
-/*************************************
- * Does semantic analysis on function bodies.
- */
-void semantic3(Dsymbol *dsym, Scope *sc)
-{
- Semantic3Visitor v(sc);
- dsym->accept(&v);
-}
diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d
new file mode 100644
index 00000000000..ac2b239efaf
--- /dev/null
+++ b/gcc/d/dmd/semantic3.d
@@ -0,0 +1,1624 @@
+/**
+ * Performs the semantic3 stage, which deals with function bodies.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/semantic3.d, _semantic3.d)
+ * Documentation: https://dlang.org/phobos/dmd_semantic3.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic3.d
+ */
+
+module dmd.semantic3;
+
+import core.stdc.stdio;
+import core.stdc.string;
+
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.blockexit;
+import dmd.clone;
+import dmd.ctorflow;
+import dmd.dcast;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dinterpret;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.dversion;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.initsem;
+import dmd.hdrgen;
+import dmd.mtype;
+import dmd.nogc;
+import dmd.nspace;
+import dmd.ob;
+import dmd.objc;
+import dmd.opover;
+import dmd.parse;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.root.rootobject;
+import dmd.sideeffect;
+import dmd.statementsem;
+import dmd.staticassert;
+import dmd.tokens;
+import dmd.utf;
+import dmd.semantic2;
+import dmd.statement;
+import dmd.target;
+import dmd.templateparamsem;
+import dmd.typesem;
+import dmd.visitor;
+
+enum LOG = false;
+
+
+/*************************************
+ * Does semantic analysis on function bodies.
+ */
+extern(C++) void semantic3(Dsymbol dsym, Scope* sc)
+{
+ scope v = new Semantic3Visitor(sc);
+ dsym.accept(v);
+}
+
+private extern(C++) final class Semantic3Visitor : Visitor
+{
+ alias visit = Visitor.visit;
+
+ Scope* sc;
+ this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ override void visit(Dsymbol) {}
+
+ override void visit(TemplateInstance tempinst)
+ {
+ static if (LOG)
+ {
+ printf("TemplateInstance.semantic3('%s'), semanticRun = %d\n", tempinst.toChars(), tempinst.semanticRun);
+ }
+ //if (toChars()[0] == 'D') *(char*)0=0;
+ if (tempinst.semanticRun >= PASS.semantic3)
+ return;
+ tempinst.semanticRun = PASS.semantic3;
+ if (tempinst.errors || !tempinst.members)
+ return;
+
+ TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ sc = tempdecl._scope;
+ sc = sc.push(tempinst.argsym);
+ sc = sc.push(tempinst);
+ sc.tinst = tempinst;
+ sc.minst = tempinst.minst;
+
+ int needGagging = (tempinst.gagged && !global.gag);
+ uint olderrors = global.errors;
+ int oldGaggedErrors = -1; // dead-store to prevent spurious warning
+ /* If this is a gagged instantiation, gag errors.
+ * Future optimisation: If the results are actually needed, errors
+ * would already be gagged, so we don't really need to run semantic
+ * on the members.
+ */
+ if (needGagging)
+ oldGaggedErrors = global.startGagging();
+
+ for (size_t i = 0; i < tempinst.members.dim; i++)
+ {
+ Dsymbol s = (*tempinst.members)[i];
+ s.semantic3(sc);
+ if (tempinst.gagged && global.errors != olderrors)
+ break;
+ }
+
+ if (global.errors != olderrors)
+ {
+ if (!tempinst.errors)
+ {
+ if (!tempdecl.literal)
+ tempinst.error(tempinst.loc, "error instantiating");
+ if (tempinst.tinst)
+ tempinst.tinst.printInstantiationTrace();
+ }
+ tempinst.errors = true;
+ }
+ if (needGagging)
+ global.endGagging(oldGaggedErrors);
+
+ sc = sc.pop();
+ sc.pop();
+ }
+
+ override void visit(TemplateMixin tmix)
+ {
+ if (tmix.semanticRun >= PASS.semantic3)
+ return;
+ tmix.semanticRun = PASS.semantic3;
+ static if (LOG)
+ {
+ printf("TemplateMixin.semantic3('%s')\n", tmix.toChars());
+ }
+ if (!tmix.members)
+ return;
+
+ sc = sc.push(tmix.argsym);
+ sc = sc.push(tmix);
+ for (size_t i = 0; i < tmix.members.dim; i++)
+ {
+ Dsymbol s = (*tmix.members)[i];
+ s.semantic3(sc);
+ }
+ sc = sc.pop();
+ sc.pop();
+ }
+
+ override void visit(Module mod)
+ {
+ //printf("Module::semantic3('%s'): parent = %p\n", toChars(), parent);
+ if (mod.semanticRun != PASS.semantic2done)
+ return;
+ mod.semanticRun = PASS.semantic3;
+ // Note that modules get their own scope, from scratch.
+ // This is so regardless of where in the syntax a module
+ // gets imported, it is unaffected by context.
+ Scope* sc = Scope.createGlobal(mod); // create root scope
+ //printf("Module = %p\n", sc.scopesym);
+ // Pass 3 semantic routines: do initializers and function bodies
+ for (size_t i = 0; i < mod.members.dim; i++)
+ {
+ Dsymbol s = (*mod.members)[i];
+ //printf("Module %s: %s.semantic3()\n", toChars(), s.toChars());
+ s.semantic3(sc);
+
+ mod.runDeferredSemantic2();
+ }
+ if (mod.userAttribDecl)
+ {
+ mod.userAttribDecl.semantic3(sc);
+ }
+ sc = sc.pop();
+ sc.pop();
+ mod.semanticRun = PASS.semantic3done;
+ }
+
+ override void visit(FuncDeclaration funcdecl)
+ {
+ //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", funcdecl.kind(), funcdecl.toChars(), sc);
+ /* Determine if function should add `return 0;`
+ */
+ bool addReturn0()
+ {
+ //printf("addReturn0()\n");
+ auto f = funcdecl.type.isTypeFunction();
+
+ // C11 5.1.2.2.3
+ if (sc.flags & SCOPE.Cfile && funcdecl.isCMain() && f.next.ty == Tint32)
+ return true;
+
+ return f.next.ty == Tvoid &&
+ (funcdecl.isMain() || global.params.betterC && funcdecl.isCMain());
+ }
+
+ VarDeclaration _arguments = null;
+
+ if (!funcdecl.parent)
+ {
+ if (global.errors)
+ return;
+ //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc);
+ assert(0);
+ }
+ if (funcdecl.errors || isError(funcdecl.parent))
+ {
+ funcdecl.errors = true;
+ return;
+ }
+ //printf("FuncDeclaration::semantic3('%s.%s', %p, sc = %p, loc = %s)\n", funcdecl.parent.toChars(), funcdecl.toChars(), funcdecl, sc, funcdecl.loc.toChars());
+ //fflush(stdout);
+ //printf("storage class = x%x %x\n", sc.stc, storage_class);
+ //{ static int x; if (++x == 2) *(char*)0=0; }
+ //printf("\tlinkage = %d\n", sc.linkage);
+
+ if (funcdecl.ident == Id.assign && !funcdecl.inuse)
+ {
+ if (funcdecl.storage_class & STC.inference)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=15044
+ * For generated opAssign function, any errors
+ * from its body need to be gagged.
+ */
+ uint oldErrors = global.startGagging();
+ ++funcdecl.inuse;
+ funcdecl.semantic3(sc);
+ --funcdecl.inuse;
+ if (global.endGagging(oldErrors)) // if errors happened
+ {
+ // Disable generated opAssign, because some members forbid identity assignment.
+ funcdecl.storage_class |= STC.disable;
+ funcdecl.fbody = null; // remove fbody which contains the error
+ funcdecl.semantic3Errors = false;
+ }
+ return;
+ }
+ }
+
+ //printf(" sc.incontract = %d\n", (sc.flags & SCOPE.contract));
+ if (funcdecl.semanticRun >= PASS.semantic3)
+ return;
+ funcdecl.semanticRun = PASS.semantic3;
+ funcdecl.semantic3Errors = false;
+
+ if (!funcdecl.type || funcdecl.type.ty != Tfunction)
+ return;
+ TypeFunction f = cast(TypeFunction)funcdecl.type;
+ if (!funcdecl.inferRetType && f.next.ty == Terror)
+ return;
+
+ if (!funcdecl.fbody && funcdecl.inferRetType && !f.next)
+ {
+ funcdecl.error("has no function body with return type inference");
+ return;
+ }
+
+ uint oldErrors = global.errors;
+ auto fds = FuncDeclSem3(funcdecl,sc);
+
+ fds.checkInContractOverrides();
+
+ // Remember whether we need to generate an 'out' contract.
+ immutable bool needEnsure = FuncDeclaration.needsFensure(funcdecl);
+
+ if (funcdecl.fbody || funcdecl.frequires || needEnsure)
+ {
+ /* Symbol table into which we place parameters and nested functions,
+ * solely to diagnose name collisions.
+ */
+ funcdecl.localsymtab = new DsymbolTable();
+
+ // Establish function scope
+ auto ss = new ScopeDsymbol(funcdecl.loc, null);
+ // find enclosing scope symbol, might skip symbol-less CTFE and/or FuncExp scopes
+ ss.parent = sc.inner().scopesym;
+ ss.endlinnum = funcdecl.endloc.linnum;
+ Scope* sc2 = sc.push(ss);
+ sc2.func = funcdecl;
+ sc2.parent = funcdecl;
+ sc2.ctorflow.callSuper = CSX.none;
+ sc2.sbreak = null;
+ sc2.scontinue = null;
+ sc2.sw = null;
+ sc2.fes = funcdecl.fes;
+ sc2.linkage = LINK.d;
+ sc2.stc &= STC.flowThruFunction;
+ sc2.visibility = Visibility(Visibility.Kind.public_);
+ sc2.explicitVisibility = 0;
+ sc2.aligndecl = null;
+ if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure)
+ sc2.flags = sc.flags & ~SCOPE.contract;
+ sc2.flags &= ~SCOPE.compile;
+ sc2.tf = null;
+ sc2.os = null;
+ sc2.inLoop = false;
+ sc2.userAttribDecl = null;
+ if (sc2.intypeof == 1)
+ sc2.intypeof = 2;
+ sc2.ctorflow.fieldinit = null;
+
+ /* Note: When a lambda is defined immediately under aggregate member
+ * scope, it should be contextless due to prevent interior pointers.
+ * e.g.
+ * // dg points 'this' - its interior pointer
+ * class C { int x; void delegate() dg = (){ this.x = 1; }; }
+ *
+ * However, lambdas could be used inside typeof, in order to check
+ * some expressions validity at compile time. For such case the lambda
+ * body can access aggregate instance members.
+ * e.g.
+ * class C { int x; static assert(is(typeof({ this.x = 1; }))); }
+ *
+ * To properly accept it, mark these lambdas as member functions.
+ */
+ if (auto fld = funcdecl.isFuncLiteralDeclaration())
+ {
+ if (auto ad = funcdecl.isMember2())
+ {
+ if (!sc.intypeof)
+ {
+ if (fld.tok == TOK.delegate_)
+ funcdecl.error("cannot be %s members", ad.kind());
+ else
+ fld.tok = TOK.function_;
+ }
+ else
+ {
+ if (fld.tok != TOK.function_)
+ fld.tok = TOK.delegate_;
+ }
+ }
+ }
+
+ funcdecl.declareThis(sc2);
+
+ // Reverts: https://issues.dlang.org/show_bug.cgi?id=5710
+ // No compiler supports this, and there was never any spec for it.
+ if (funcdecl.isThis2)
+ {
+ funcdecl.deprecation("function requires a dual-context, which is deprecated");
+ if (auto ti = sc2.parent ? sc2.parent.isInstantiated() : null)
+ ti.printInstantiationTrace(Classification.deprecation);
+ }
+
+ //printf("[%s] ad = %p vthis = %p\n", loc.toChars(), ad, vthis);
+ //if (vthis) printf("\tvthis.type = %s\n", vthis.type.toChars());
+
+ // Declare hidden variable _arguments[] and _argptr
+ if (f.parameterList.varargs == VarArg.variadic)
+ {
+ if (f.linkage == LINK.d)
+ {
+ // Variadic arguments depend on Typeinfo being defined.
+ if (!global.params.useTypeInfo || !Type.dtypeinfo || !Type.typeinfotypelist)
+ {
+ if (!global.params.useTypeInfo)
+ funcdecl.error("D-style variadic functions cannot be used with -betterC");
+ else if (!Type.typeinfotypelist)
+ funcdecl.error("`object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions");
+ else
+ funcdecl.error("`object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions");
+ fatal();
+ }
+
+ // Declare _arguments[]
+ funcdecl.v_arguments = new VarDeclaration(funcdecl.loc, Type.typeinfotypelist.type, Id._arguments_typeinfo, null);
+ funcdecl.v_arguments.storage_class |= STC.temp | STC.parameter;
+ funcdecl.v_arguments.dsymbolSemantic(sc2);
+ sc2.insert(funcdecl.v_arguments);
+ funcdecl.v_arguments.parent = funcdecl;
+
+ //Type t = Type.dtypeinfo.type.constOf().arrayOf();
+ Type t = Type.dtypeinfo.type.arrayOf();
+ _arguments = new VarDeclaration(funcdecl.loc, t, Id._arguments, null);
+ _arguments.storage_class |= STC.temp;
+ _arguments.dsymbolSemantic(sc2);
+ sc2.insert(_arguments);
+ _arguments.parent = funcdecl;
+ }
+ if (f.linkage == LINK.d || f.parameterList.length)
+ {
+ // Declare _argptr
+ Type t = target.va_listType(funcdecl.loc, sc);
+ // Init is handled in FuncDeclaration_toObjFile
+ funcdecl.v_argptr = new VarDeclaration(funcdecl.loc, t, Id._argptr, new VoidInitializer(funcdecl.loc));
+ funcdecl.v_argptr.storage_class |= STC.temp;
+ funcdecl.v_argptr.dsymbolSemantic(sc2);
+ sc2.insert(funcdecl.v_argptr);
+ funcdecl.v_argptr.parent = funcdecl;
+ }
+ }
+
+ /* Declare all the function parameters as variables
+ * and install them in parameters[]
+ */
+ if (const nparams = f.parameterList.length)
+ {
+ /* parameters[] has all the tuples removed, as the back end
+ * doesn't know about tuples
+ */
+ funcdecl.parameters = new VarDeclarations();
+ funcdecl.parameters.reserve(nparams);
+ foreach (i, fparam; f.parameterList)
+ {
+ Identifier id = fparam.ident;
+ StorageClass stc = 0;
+ if (!id)
+ {
+ /* Generate identifier for un-named parameter,
+ * because we need it later on.
+ */
+ fparam.ident = id = Identifier.generateId("_param_", i);
+ stc |= STC.temp;
+ }
+ Type vtype = fparam.type;
+ auto v = new VarDeclaration(funcdecl.loc, vtype, id, null);
+ //printf("declaring parameter %s of type %s\n", v.toChars(), v.type.toChars());
+ stc |= STC.parameter;
+ if (f.parameterList.varargs == VarArg.typesafe && i + 1 == nparams)
+ {
+ stc |= STC.variadic;
+ auto vtypeb = vtype.toBasetype();
+ if (vtypeb.ty == Tarray)
+ {
+ /* Since it'll be pointing into the stack for the array
+ * contents, it needs to be `scope`
+ */
+ stc |= STC.scope_;
+ }
+ }
+
+ if ((funcdecl.flags & FUNCFLAG.inferScope) && !(fparam.storageClass & STC.scope_))
+ stc |= STC.maybescope;
+
+ stc |= fparam.storageClass & (STC.IOR | STC.return_ | STC.scope_ | STC.lazy_ | STC.final_ | STC.TYPECTOR | STC.nodtor | STC.returnScope);
+ v.storage_class = stc;
+ v.dsymbolSemantic(sc2);
+ if (!sc2.insert(v))
+ {
+ funcdecl.error("parameter `%s.%s` is already defined", funcdecl.toChars(), v.toChars());
+ funcdecl.errors = true;
+ }
+ else
+ funcdecl.parameters.push(v);
+ funcdecl.localsymtab.insert(v);
+ v.parent = funcdecl;
+ if (fparam.userAttribDecl)
+ v.userAttribDecl = fparam.userAttribDecl;
+ }
+ }
+
+ // Declare the tuple symbols and put them in the symbol table,
+ // but not in parameters[].
+ if (f.parameterList.parameters)
+ foreach (fparam; *f.parameterList.parameters)
+ {
+ if (!fparam.ident)
+ continue; // never used, so ignore
+ // expand any tuples
+ if (fparam.type.ty != Ttuple)
+ continue;
+
+ TypeTuple t = cast(TypeTuple)fparam.type;
+ size_t dim = Parameter.dim(t.arguments);
+ auto exps = new Objects(dim);
+ foreach (j; 0 .. dim)
+ {
+ Parameter narg = Parameter.getNth(t.arguments, j);
+ assert(narg.ident);
+ VarDeclaration v = sc2.search(Loc.initial, narg.ident, null).isVarDeclaration();
+ assert(v);
+ (*exps)[j] = new VarExp(v.loc, v);
+ }
+ assert(fparam.ident);
+ auto v = new TupleDeclaration(funcdecl.loc, fparam.ident, exps);
+ //printf("declaring tuple %s\n", v.toChars());
+ v.isexp = true;
+ if (!sc2.insert(v))
+ funcdecl.error("parameter `%s.%s` is already defined", funcdecl.toChars(), v.toChars());
+ funcdecl.localsymtab.insert(v);
+ v.parent = funcdecl;
+ }
+
+ // Precondition invariant
+ Statement fpreinv = null;
+ if (funcdecl.addPreInvariant())
+ {
+ Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis);
+ if (e)
+ fpreinv = new ExpStatement(Loc.initial, e);
+ }
+
+ // Postcondition invariant
+ Statement fpostinv = null;
+ if (funcdecl.addPostInvariant())
+ {
+ Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis);
+ if (e)
+ fpostinv = new ExpStatement(Loc.initial, e);
+ }
+
+ // Pre/Postcondition contract
+ if (!funcdecl.fbody)
+ funcdecl.buildEnsureRequire();
+
+ Scope* scout = null;
+ if (needEnsure || funcdecl.addPostInvariant())
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=3657
+ * Set the correct end line number for fensure scope.
+ */
+ uint fensure_endlin = funcdecl.endloc.linnum;
+ if (funcdecl.fensure)
+ if (auto s = funcdecl.fensure.isScopeStatement())
+ fensure_endlin = s.endloc.linnum;
+
+ if ((needEnsure && global.params.useOut == CHECKENABLE.on) || fpostinv)
+ {
+ funcdecl.returnLabel = funcdecl.searchLabel(Id.returnLabel);
+ }
+
+ // scope of out contract (need for vresult.semantic)
+ auto sym = new ScopeDsymbol(funcdecl.loc, null);
+ sym.parent = sc2.scopesym;
+ sym.endlinnum = fensure_endlin;
+ scout = sc2.push(sym);
+ }
+
+ if (funcdecl.fbody)
+ {
+ auto sym = new ScopeDsymbol(funcdecl.loc, null);
+ sym.parent = sc2.scopesym;
+ sym.endlinnum = funcdecl.endloc.linnum;
+ sc2 = sc2.push(sym);
+
+ auto ad2 = funcdecl.isMemberLocal();
+
+ /* If this is a class constructor
+ */
+ if (ad2 && funcdecl.isCtorDeclaration())
+ {
+ sc2.ctorflow.allocFieldinit(ad2.fields.dim);
+ foreach (v; ad2.fields)
+ {
+ v.ctorinit = 0;
+ }
+ }
+
+ bool inferRef = (f.isref && (funcdecl.storage_class & STC.auto_));
+
+ funcdecl.fbody = funcdecl.fbody.statementSemantic(sc2);
+ if (!funcdecl.fbody)
+ funcdecl.fbody = new CompoundStatement(Loc.initial, new Statements());
+
+ if (funcdecl.naked)
+ {
+ fpreinv = null; // can't accommodate with no stack frame
+ fpostinv = null;
+ }
+
+ assert(funcdecl.type == f || (funcdecl.type.ty == Tfunction && f.purity == PURE.impure && (cast(TypeFunction)funcdecl.type).purity >= PURE.fwdref));
+ f = cast(TypeFunction)funcdecl.type;
+
+ if (funcdecl.inferRetType)
+ {
+ // If no return type inferred yet, then infer a void
+ if (!f.next)
+ f.next = Type.tvoid;
+ if (f.checkRetType(funcdecl.loc))
+ funcdecl.fbody = new ErrorStatement();
+ }
+ if (global.params.vcomplex && f.next !is null)
+ f.next.checkComplexTransition(funcdecl.loc, sc);
+
+ if (funcdecl.returns && !funcdecl.fbody.isErrorStatement())
+ {
+ for (size_t i = 0; i < funcdecl.returns.dim;)
+ {
+ Expression exp = (*funcdecl.returns)[i].exp;
+ if (exp.op == TOK.variable && (cast(VarExp)exp).var == funcdecl.vresult)
+ {
+ if (addReturn0())
+ exp.type = Type.tint32;
+ else
+ exp.type = f.next;
+ // Remove `return vresult;` from returns
+ funcdecl.returns.remove(i);
+ continue;
+ }
+ if (inferRef && f.isref && !exp.type.constConv(f.next)) // https://issues.dlang.org/show_bug.cgi?id=13336
+ f.isref = false;
+ i++;
+ }
+ }
+ if (f.isref) // Function returns a reference
+ {
+ if (funcdecl.storage_class & STC.auto_)
+ funcdecl.storage_class &= ~STC.auto_;
+ }
+
+ // handle NRVO
+ if (!target.isReturnOnStack(f, funcdecl.needThis()) || !funcdecl.checkNRVO())
+ funcdecl.nrvo_can = 0;
+
+ if (funcdecl.fbody.isErrorStatement())
+ {
+ }
+ else if (funcdecl.isStaticCtorDeclaration())
+ {
+ /* It's a static constructor. Ensure that all
+ * ctor consts were initialized.
+ */
+ ScopeDsymbol pd = funcdecl.toParent().isScopeDsymbol();
+ for (size_t i = 0; i < pd.members.dim; i++)
+ {
+ Dsymbol s = (*pd.members)[i];
+ s.checkCtorConstInit();
+ }
+ }
+ else if (ad2 && funcdecl.isCtorDeclaration())
+ {
+ ClassDeclaration cd = ad2.isClassDeclaration();
+
+ // Verify that all the ctorinit fields got initialized
+ if (!(sc2.ctorflow.callSuper & CSX.this_ctor))
+ {
+ foreach (i, v; ad2.fields)
+ {
+ if (v.isThisDeclaration())
+ continue;
+ if (v.ctorinit == 0)
+ {
+ /* Current bugs in the flow analysis:
+ * 1. union members should not produce error messages even if
+ * not assigned to
+ * 2. structs should recognize delegating opAssign calls as well
+ * as delegating calls to other constructors
+ */
+ if (v.isCtorinit() && !v.type.isMutable() && cd)
+ funcdecl.error("missing initializer for %s field `%s`", MODtoChars(v.type.mod), v.toChars());
+ else if (v.storage_class & STC.nodefaultctor)
+ error(funcdecl.loc, "field `%s` must be initialized in constructor", v.toChars());
+ else if (v.type.needsNested())
+ error(funcdecl.loc, "field `%s` must be initialized in constructor, because it is nested struct", v.toChars());
+ }
+ else
+ {
+ bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
+ if (mustInit && !(sc2.ctorflow.fieldinit[i].csx & CSX.this_ctor))
+ {
+ funcdecl.error("field `%s` must be initialized but skipped", v.toChars());
+ }
+ }
+ }
+ }
+ sc2.ctorflow.freeFieldinit();
+
+ if (cd && !(sc2.ctorflow.callSuper & CSX.any_ctor) && cd.baseClass && cd.baseClass.ctor)
+ {
+ sc2.ctorflow.callSuper = CSX.none;
+
+ // Insert implicit super() at start of fbody
+ Type tthis = ad2.type.addMod(funcdecl.vthis.type.mod);
+ FuncDeclaration fd = resolveFuncCall(Loc.initial, sc2, cd.baseClass.ctor, null, tthis, null, FuncResolveFlag.quiet);
+ if (!fd)
+ {
+ funcdecl.error("no match for implicit `super()` call in constructor");
+ }
+ else if (fd.storage_class & STC.disable)
+ {
+ funcdecl.error("cannot call `super()` implicitly because it is annotated with `@disable`");
+ }
+ else
+ {
+ Expression e1 = new SuperExp(Loc.initial);
+ Expression e = new CallExp(Loc.initial, e1);
+ e = e.expressionSemantic(sc2);
+ Statement s = new ExpStatement(Loc.initial, e);
+ funcdecl.fbody = new CompoundStatement(Loc.initial, s, funcdecl.fbody);
+ }
+ }
+ //printf("ctorflow.callSuper = x%x\n", sc2.ctorflow.callSuper);
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=17502
+ * Wait until after the return type has been inferred before
+ * generating the contracts for this function, and merging contracts
+ * from overrides.
+ *
+ * https://issues.dlang.org/show_bug.cgi?id=17893
+ * However should take care to generate this before inferered
+ * function attributes are applied, such as 'nothrow'.
+ *
+ * This was originally at the end of the first semantic pass, but
+ * required a fix-up to be done here for the '__result' variable
+ * type of __ensure() inside auto functions, but this didn't work
+ * if the out parameter was implicit.
+ */
+ funcdecl.buildEnsureRequire();
+
+ // Check for errors related to 'nothrow'.
+ const blockexit = funcdecl.fbody.blockExit(funcdecl, f.isnothrow);
+ if (f.isnothrow && blockexit & BE.throw_)
+ error(funcdecl.loc, "`nothrow` %s `%s` may throw", funcdecl.kind(), funcdecl.toPrettyChars());
+
+ if (!(blockexit & (BE.throw_ | BE.halt) || funcdecl.flags & FUNCFLAG.hasCatches))
+ {
+ /* Don't generate unwind tables for this function
+ * https://issues.dlang.org/show_bug.cgi?id=17997
+ */
+ funcdecl.eh_none = true;
+ }
+
+ if (funcdecl.flags & FUNCFLAG.nothrowInprocess)
+ {
+ if (funcdecl.type == f)
+ f = cast(TypeFunction)f.copy();
+ f.isnothrow = !(blockexit & BE.throw_);
+ }
+
+ if (funcdecl.fbody.isErrorStatement())
+ {
+ }
+ else if (ad2 && funcdecl.isCtorDeclaration())
+ {
+ /* Append:
+ * return this;
+ * to function body
+ */
+ if (blockexit & BE.fallthru)
+ {
+ Statement s = new ReturnStatement(funcdecl.loc, null);
+ s = s.statementSemantic(sc2);
+ funcdecl.fbody = new CompoundStatement(funcdecl.loc, funcdecl.fbody, s);
+ funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1);
+ }
+ }
+ else if (funcdecl.fes)
+ {
+ // For foreach(){} body, append a return 0;
+ if (blockexit & BE.fallthru)
+ {
+ Expression e = IntegerExp.literal!0;
+ Statement s = new ReturnStatement(Loc.initial, e);
+ funcdecl.fbody = new CompoundStatement(Loc.initial, funcdecl.fbody, s);
+ funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1);
+ }
+ assert(!funcdecl.returnLabel);
+ }
+ else if (f.next.ty == Tnoreturn)
+ {
+ }
+ else
+ {
+ const(bool) inlineAsm = (funcdecl.hasReturnExp & 8) != 0;
+ if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !(sc.flags & SCOPE.Cfile))
+ {
+ if (!funcdecl.hasReturnExp)
+ funcdecl.error("has no `return` statement, but is expected to return a value of type `%s`", f.next.toChars());
+ else
+ funcdecl.error("no `return exp;` or `assert(0);` at end of function");
+ }
+ }
+
+ if (funcdecl.returns)
+ {
+ bool implicit0 = addReturn0();
+ Type tret = implicit0 ? Type.tint32 : f.next;
+ assert(tret.ty != Tvoid);
+ if (funcdecl.vresult || funcdecl.returnLabel)
+ funcdecl.buildResultVar(scout ? scout : sc2, tret);
+
+ /* Cannot move this loop into NrvoWalker, because
+ * returns[i] may be in the nested delegate for foreach-body.
+ */
+ for (size_t i = 0; i < funcdecl.returns.dim; i++)
+ {
+ ReturnStatement rs = (*funcdecl.returns)[i];
+ Expression exp = rs.exp;
+ if (exp.op == TOK.error)
+ continue;
+ if (tret.ty == Terror)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13702
+ exp = checkGC(sc2, exp);
+ continue;
+ }
+
+ /* If the expression in the return statement (exp) cannot be implicitly
+ * converted to the return type (tret) of the function and if the
+ * type of the expression is type isolated, then it may be possible
+ * that a promotion to `immutable` or `inout` (through a cast) will
+ * match the return type.
+ */
+ if (!exp.implicitConvTo(tret) && funcdecl.isTypeIsolated(exp.type))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=20073
+ *
+ * The problem is that if the type of the returned expression (exp.type)
+ * is an aggregated declaration with an alias this, the alias this may be
+ * used for the conversion testing without it being an isolated type.
+ *
+ * To make sure this does not happen, we can test here the implicit conversion
+ * only for the aggregated declaration type by using `implicitConvToWithoutAliasThis`.
+ * The implicit conversion with alias this is taken care of later.
+ */
+ AggregateDeclaration aggDecl = isAggregate(exp.type);
+ TypeStruct tstruct;
+ TypeClass tclass;
+ bool hasAliasThis;
+ if (aggDecl && aggDecl.aliasthis)
+ {
+ hasAliasThis = true;
+ tclass = exp.type.isTypeClass();
+ if (!tclass)
+ tstruct = exp.type.isTypeStruct();
+ assert(tclass || tstruct);
+ }
+ if (hasAliasThis)
+ {
+ if (tclass)
+ {
+ if ((cast(TypeClass)(exp.type.immutableOf())).implicitConvToWithoutAliasThis(tret))
+ exp = exp.castTo(sc2, exp.type.immutableOf());
+ else if ((cast(TypeClass)(exp.type.wildOf())).implicitConvToWithoutAliasThis(tret))
+ exp = exp.castTo(sc2, exp.type.wildOf());
+ }
+ else
+ {
+ if ((cast(TypeStruct)exp.type.immutableOf()).implicitConvToWithoutAliasThis(tret))
+ exp = exp.castTo(sc2, exp.type.immutableOf());
+ else if ((cast(TypeStruct)exp.type.immutableOf()).implicitConvToWithoutAliasThis(tret))
+ exp = exp.castTo(sc2, exp.type.wildOf());
+ }
+ }
+ else
+ {
+ if (exp.type.immutableOf().implicitConvTo(tret))
+ exp = exp.castTo(sc2, exp.type.immutableOf());
+ else if (exp.type.wildOf().implicitConvTo(tret))
+ exp = exp.castTo(sc2, exp.type.wildOf());
+ }
+ }
+
+ const hasCopyCtor = exp.type.ty == Tstruct && (cast(TypeStruct)exp.type).sym.hasCopyCtor;
+ // if a copy constructor is present, the return type conversion will be handled by it
+ if (!(hasCopyCtor && exp.isLvalue()))
+ {
+ if (f.isref && !MODimplicitConv(exp.type.mod, tret.mod) && !tret.isTypeSArray())
+ error(exp.loc, "expression `%s` of type `%s` is not implicitly convertible to return type `ref %s`",
+ exp.toChars(), exp.type.toChars(), tret.toChars());
+ else
+ exp = exp.implicitCastTo(sc2, tret);
+ }
+
+ if (f.isref)
+ {
+ // Function returns a reference
+ exp = exp.toLvalue(sc2, exp);
+ checkReturnEscapeRef(sc2, exp, false);
+ exp = exp.optimize(WANTvalue, /*keepLvalue*/ true);
+ }
+ else
+ {
+ exp = exp.optimize(WANTvalue);
+
+ /* https://issues.dlang.org/show_bug.cgi?id=10789
+ * If NRVO is not possible, all returned lvalues should call their postblits.
+ */
+ if (!funcdecl.nrvo_can)
+ exp = doCopyOrMove(sc2, exp, f.next);
+
+ if (tret.hasPointers())
+ checkReturnEscape(sc2, exp, false);
+ }
+
+ exp = checkGC(sc2, exp);
+
+ if (funcdecl.vresult)
+ {
+ // Create: return vresult = exp;
+ exp = new BlitExp(rs.loc, funcdecl.vresult, exp);
+ exp.type = funcdecl.vresult.type;
+
+ if (rs.caseDim)
+ exp = Expression.combine(exp, new IntegerExp(rs.caseDim));
+ }
+ else if (funcdecl.tintro && !tret.equals(funcdecl.tintro.nextOf()))
+ {
+ exp = exp.implicitCastTo(sc2, funcdecl.tintro.nextOf());
+ }
+ rs.exp = exp;
+ }
+ }
+ if (funcdecl.nrvo_var || funcdecl.returnLabel)
+ {
+ scope NrvoWalker nw = new NrvoWalker();
+ nw.fd = funcdecl;
+ nw.sc = sc2;
+ nw.visitStmt(funcdecl.fbody);
+ }
+
+ sc2 = sc2.pop();
+ }
+
+ if (global.params.inclusiveInContracts)
+ {
+ funcdecl.frequire = funcdecl.mergeFrequireInclusivePreview(
+ funcdecl.frequire, funcdecl.fdrequireParams);
+ }
+ else
+ {
+ funcdecl.frequire = funcdecl.mergeFrequire(funcdecl.frequire, funcdecl.fdrequireParams);
+ }
+ funcdecl.fensure = funcdecl.mergeFensure(funcdecl.fensure, Id.result, funcdecl.fdensureParams);
+
+ Statement freq = funcdecl.frequire;
+ Statement fens = funcdecl.fensure;
+
+ /* Do the semantic analysis on the [in] preconditions and
+ * [out] postconditions.
+ */
+ if (freq)
+ {
+ /* frequire is composed of the [in] contracts
+ */
+ auto sym = new ScopeDsymbol(funcdecl.loc, null);
+ sym.parent = sc2.scopesym;
+ sym.endlinnum = funcdecl.endloc.linnum;
+ sc2 = sc2.push(sym);
+ sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.require;
+
+ // BUG: need to error if accessing out parameters
+ // BUG: need to disallow returns and throws
+ // BUG: verify that all in and ref parameters are read
+ freq = freq.statementSemantic(sc2);
+ freq.blockExit(funcdecl, false);
+
+ funcdecl.eh_none = false;
+
+ sc2 = sc2.pop();
+
+ if (global.params.useIn == CHECKENABLE.off)
+ freq = null;
+ }
+ if (fens)
+ {
+ /* fensure is composed of the [out] contracts
+ */
+ if (f.next.ty == Tvoid && funcdecl.fensures)
+ {
+ foreach (e; *funcdecl.fensures)
+ {
+ if (e.id)
+ {
+ funcdecl.error(e.ensure.loc, "`void` functions have no result");
+ //fens = null;
+ }
+ }
+ }
+
+ sc2 = scout; //push
+ sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.ensure;
+
+ // BUG: need to disallow returns and throws
+
+ if (funcdecl.fensure && f.next.ty != Tvoid)
+ funcdecl.buildResultVar(scout, f.next);
+
+ fens = fens.statementSemantic(sc2);
+ fens.blockExit(funcdecl, false);
+
+ funcdecl.eh_none = false;
+
+ sc2 = sc2.pop();
+
+ if (global.params.useOut == CHECKENABLE.off)
+ fens = null;
+ }
+ if (funcdecl.fbody && funcdecl.fbody.isErrorStatement())
+ {
+ }
+ else
+ {
+ auto a = new Statements();
+ // Merge in initialization of 'out' parameters
+ if (funcdecl.parameters)
+ {
+ for (size_t i = 0; i < funcdecl.parameters.dim; i++)
+ {
+ VarDeclaration v = (*funcdecl.parameters)[i];
+ if (v.storage_class & STC.out_)
+ {
+ if (!v._init)
+ {
+ v.error("Zero-length `out` parameters are not allowed.");
+ return;
+ }
+ ExpInitializer ie = v._init.isExpInitializer();
+ assert(ie);
+ if (auto iec = ie.exp.isConstructExp())
+ {
+ // construction occurred in parameter processing
+ auto ec = new AssignExp(iec.loc, iec.e1, iec.e2);
+ ec.type = iec.type;
+ ie.exp = ec;
+ }
+ a.push(new ExpStatement(Loc.initial, ie.exp));
+ }
+ }
+ }
+
+ if (_arguments)
+ {
+ /* Advance to elements[] member of TypeInfo_Tuple with:
+ * _arguments = v_arguments.elements;
+ */
+ Expression e = new VarExp(Loc.initial, funcdecl.v_arguments);
+ e = new DotIdExp(Loc.initial, e, Id.elements);
+ e = new ConstructExp(Loc.initial, _arguments, e);
+ e = e.expressionSemantic(sc2);
+
+ _arguments._init = new ExpInitializer(Loc.initial, e);
+ auto de = new DeclarationExp(Loc.initial, _arguments);
+ a.push(new ExpStatement(Loc.initial, de));
+ }
+
+ // Merge contracts together with body into one compound statement
+
+ if (freq || fpreinv)
+ {
+ if (!freq)
+ freq = fpreinv;
+ else if (fpreinv)
+ freq = new CompoundStatement(Loc.initial, freq, fpreinv);
+
+ a.push(freq);
+ }
+
+ if (funcdecl.fbody)
+ a.push(funcdecl.fbody);
+
+ if (fens || fpostinv)
+ {
+ if (!fens)
+ fens = fpostinv;
+ else if (fpostinv)
+ fens = new CompoundStatement(Loc.initial, fpostinv, fens);
+
+ auto ls = new LabelStatement(Loc.initial, Id.returnLabel, fens);
+ funcdecl.returnLabel.statement = ls;
+ a.push(funcdecl.returnLabel.statement);
+
+ if (f.next.ty != Tvoid && funcdecl.vresult)
+ {
+ // Create: return vresult;
+ Expression e = new VarExp(Loc.initial, funcdecl.vresult);
+ if (funcdecl.tintro)
+ {
+ e = e.implicitCastTo(sc, funcdecl.tintro.nextOf());
+ e = e.expressionSemantic(sc);
+ }
+ auto s = new ReturnStatement(Loc.initial, e);
+ a.push(s);
+ }
+ }
+ if (addReturn0())
+ {
+ // Add a return 0; statement
+ Statement s = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
+ a.push(s);
+ }
+
+ Statement sbody = new CompoundStatement(Loc.initial, a);
+
+ /* Append destructor calls for parameters as finally blocks.
+ */
+ if (funcdecl.parameters)
+ {
+ // check if callee destroys arguments
+ const bool paramsNeedDtor = target.isCalleeDestroyingArgs(f);
+
+ foreach (v; *funcdecl.parameters)
+ {
+ if (v.isReference() || (v.storage_class & STC.lazy_))
+ continue;
+ if (v.needsScopeDtor())
+ {
+ v.storage_class |= STC.nodtor;
+ if (!paramsNeedDtor)
+ continue;
+
+ // same with ExpStatement.scopeCode()
+ Statement s = new DtorExpStatement(Loc.initial, v.edtor, v);
+
+ s = s.statementSemantic(sc2);
+
+ bool isnothrow = f.isnothrow & !(funcdecl.flags & FUNCFLAG.nothrowInprocess);
+ const blockexit = s.blockExit(funcdecl, isnothrow);
+ if (blockexit & BE.throw_)
+ funcdecl.eh_none = false;
+ if (f.isnothrow && isnothrow && blockexit & BE.throw_)
+ error(funcdecl.loc, "`nothrow` %s `%s` may throw", funcdecl.kind(), funcdecl.toPrettyChars());
+ if (funcdecl.flags & FUNCFLAG.nothrowInprocess && blockexit & BE.throw_)
+ f.isnothrow = false;
+
+ if (sbody.blockExit(funcdecl, f.isnothrow) == BE.fallthru)
+ sbody = new CompoundStatement(Loc.initial, sbody, s);
+ else
+ sbody = new TryFinallyStatement(Loc.initial, sbody, s);
+ }
+ }
+ }
+ // from this point on all possible 'throwers' are checked
+ funcdecl.flags &= ~FUNCFLAG.nothrowInprocess;
+
+ if (funcdecl.isSynchronized())
+ {
+ /* Wrap the entire function body in a synchronized statement
+ */
+ ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration();
+ if (cd)
+ {
+ if (target.libraryObjectMonitors(funcdecl, sbody))
+ {
+ Expression vsync;
+ if (funcdecl.isStatic())
+ {
+ // The monitor is in the ClassInfo
+ vsync = new DotIdExp(funcdecl.loc, symbolToExp(cd, funcdecl.loc, sc2, false), Id.classinfo);
+ }
+ else
+ {
+ // 'this' is the monitor
+ vsync = new VarExp(funcdecl.loc, funcdecl.vthis);
+ if (funcdecl.isThis2)
+ {
+ vsync = new PtrExp(funcdecl.loc, vsync);
+ vsync = new IndexExp(funcdecl.loc, vsync, IntegerExp.literal!0);
+ }
+ }
+ sbody = new PeelStatement(sbody); // don't redo semantic()
+ sbody = new SynchronizedStatement(funcdecl.loc, vsync, sbody);
+ sbody = sbody.statementSemantic(sc2);
+ }
+ }
+ else
+ {
+ funcdecl.error("synchronized function `%s` must be a member of a class", funcdecl.toChars());
+ }
+ }
+
+ // If declaration has no body, don't set sbody to prevent incorrect codegen.
+ if (funcdecl.fbody || funcdecl.allowsContractWithoutBody())
+ funcdecl.fbody = sbody;
+ }
+
+ // Check for undefined labels
+ if (funcdecl.labtab)
+ foreach (keyValue; funcdecl.labtab.tab.asRange)
+ {
+ //printf(" KV: %s = %s\n", keyValue.key.toChars(), keyValue.value.toChars());
+ LabelDsymbol label = cast(LabelDsymbol)keyValue.value;
+ if (!label.statement && (!label.deleted || label.iasm))
+ {
+ funcdecl.error(label.loc, "label `%s` is undefined", label.toChars());
+ }
+ }
+
+ // Fix up forward-referenced gotos
+ if (funcdecl.gotos)
+ {
+ for (size_t i = 0; i < funcdecl.gotos.dim; ++i)
+ {
+ (*funcdecl.gotos)[i].checkLabel();
+ }
+ }
+
+ if (funcdecl.naked && (funcdecl.fensures || funcdecl.frequires))
+ funcdecl.error("naked assembly functions with contracts are not supported");
+
+ sc2.ctorflow.callSuper = CSX.none;
+ sc2.pop();
+ }
+
+ if (funcdecl.checkClosure())
+ {
+ // We should be setting errors here instead of relying on the global error count.
+ //errors = true;
+ }
+
+ /* If function survived being marked as impure, then it is pure
+ */
+ if (funcdecl.flags & FUNCFLAG.purityInprocess)
+ {
+ funcdecl.flags &= ~FUNCFLAG.purityInprocess;
+ if (funcdecl.type == f)
+ f = cast(TypeFunction)f.copy();
+ f.purity = PURE.fwdref;
+ }
+
+ if (funcdecl.flags & FUNCFLAG.safetyInprocess)
+ {
+ funcdecl.flags &= ~FUNCFLAG.safetyInprocess;
+ if (funcdecl.type == f)
+ f = cast(TypeFunction)f.copy();
+ f.trust = TRUST.safe;
+ }
+
+ if (funcdecl.flags & FUNCFLAG.nogcInprocess)
+ {
+ funcdecl.flags &= ~FUNCFLAG.nogcInprocess;
+ if (funcdecl.type == f)
+ f = cast(TypeFunction)f.copy();
+ f.isnogc = true;
+ }
+
+ if (funcdecl.flags & FUNCFLAG.returnInprocess)
+ {
+ funcdecl.flags &= ~FUNCFLAG.returnInprocess;
+ if (funcdecl.storage_class & STC.return_)
+ {
+ if (funcdecl.type == f)
+ f = cast(TypeFunction)f.copy();
+ f.isreturn = true;
+ if (funcdecl.storage_class & STC.returninferred)
+ f.isreturninferred = true;
+ }
+ }
+
+ funcdecl.flags &= ~FUNCFLAG.inferScope;
+
+ // Eliminate maybescope's
+ {
+ // Create and fill array[] with maybe candidates from the `this` and the parameters
+ VarDeclaration[] array = void;
+
+ VarDeclaration[10] tmp = void;
+ size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.dim : 0);
+ if (dim <= tmp.length)
+ array = tmp[0 .. dim];
+ else
+ {
+ auto ptr = cast(VarDeclaration*)mem.xmalloc(dim * VarDeclaration.sizeof);
+ array = ptr[0 .. dim];
+ }
+ size_t n = 0;
+ if (funcdecl.vthis)
+ array[n++] = funcdecl.vthis;
+ if (funcdecl.parameters)
+ {
+ foreach (v; *funcdecl.parameters)
+ {
+ array[n++] = v;
+ }
+ }
+
+ eliminateMaybeScopes(array[0 .. n]);
+
+ if (dim > tmp.length)
+ mem.xfree(array.ptr);
+ }
+
+ // Infer STC.scope_
+ if (funcdecl.parameters && !funcdecl.errors)
+ {
+ assert(f.parameterList.length == funcdecl.parameters.dim);
+ foreach (u, p; f.parameterList)
+ {
+ auto v = (*funcdecl.parameters)[u];
+ if (v.storage_class & STC.maybescope)
+ {
+ //printf("Inferring scope for %s\n", v.toChars());
+ notMaybeScope(v);
+ v.storage_class |= STC.scope_ | STC.scopeinferred;
+ p.storageClass |= STC.scope_ | STC.scopeinferred;
+ assert(!(p.storageClass & STC.maybescope));
+ }
+ }
+ }
+
+ if (funcdecl.vthis && funcdecl.vthis.storage_class & STC.maybescope)
+ {
+ notMaybeScope(funcdecl.vthis);
+ funcdecl.vthis.storage_class |= STC.scope_ | STC.scopeinferred;
+ f.isScopeQual = true;
+ f.isscopeinferred = true;
+ }
+
+ // reset deco to apply inference result to mangled name
+ if (f != funcdecl.type)
+ f.deco = null;
+
+ // Do semantic type AFTER pure/nothrow inference.
+ if (!f.deco && funcdecl.ident != Id.xopEquals && funcdecl.ident != Id.xopCmp)
+ {
+ sc = sc.push();
+ if (funcdecl.isCtorDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=#15665
+ f.isctor = true;
+ sc.stc = 0;
+ sc.linkage = funcdecl.linkage; // https://issues.dlang.org/show_bug.cgi?id=8496
+ funcdecl.type = f.typeSemantic(funcdecl.loc, sc);
+ sc = sc.pop();
+ }
+
+ // Do live analysis
+ if (global.params.useDIP1021 && funcdecl.fbody && funcdecl.type.ty != Terror &&
+ funcdecl.type.isTypeFunction().islive)
+ {
+ oblive(funcdecl);
+ }
+
+ /* If this function had instantiated with gagging, error reproduction will be
+ * done by TemplateInstance::semantic.
+ * Otherwise, error gagging should be temporarily ungagged by functionSemantic3.
+ */
+ funcdecl.semanticRun = PASS.semantic3done;
+ funcdecl.semantic3Errors = (global.errors != oldErrors) || (funcdecl.fbody && funcdecl.fbody.isErrorStatement());
+ if (funcdecl.type.ty == Terror)
+ funcdecl.errors = true;
+ //printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent.toChars(), toChars(), sc, loc.toChars());
+ //fflush(stdout);
+ }
+
+ override void visit(CtorDeclaration ctor)
+ {
+ //printf("CtorDeclaration()\n%s\n", ctor.fbody.toChars());
+ if (ctor.semanticRun >= PASS.semantic3)
+ return;
+
+ /* If any of the fields of the aggregate have a destructor, add
+ * scope (failure) { this.fieldDtor(); }
+ * as the first statement of the constructor (unless the constructor
+ * doesn't define a body - @disable, extern)
+ *.It is not necessary to add it after
+ * each initialization of a field, because destruction of .init constructed
+ * structs should be benign.
+ * https://issues.dlang.org/show_bug.cgi?id=14246
+ */
+ AggregateDeclaration ad = ctor.isMemberDecl();
+ if (!ctor.fbody || !ad || !ad.fieldDtor || !global.params.dtorFields || global.params.betterC || ctor.type.toTypeFunction.isnothrow)
+ return visit(cast(FuncDeclaration)ctor);
+
+ /* Generate:
+ * this.fieldDtor()
+ */
+ Expression e = new ThisExp(ctor.loc);
+ e.type = ad.type.mutableOf();
+ e = new DotVarExp(ctor.loc, e, ad.fieldDtor, false);
+ auto ce = new CallExp(ctor.loc, e);
+ auto sexp = new ExpStatement(ctor.loc, ce);
+ auto ss = new ScopeStatement(ctor.loc, sexp, ctor.loc);
+
+ // @@@DEPRECATED_2096@@@
+ // Allow negligible attribute violations to allow for a smooth
+ // transition. Remove this after the usual deprecation period
+ // after 2.106.
+ if (global.params.dtorFields == FeatureState.default_)
+ {
+ auto ctf = cast(TypeFunction) ctor.type;
+ auto dtf = cast(TypeFunction) ad.fieldDtor.type;
+
+ const ngErr = ctf.isnogc && !dtf.isnogc;
+ const puErr = ctf.purity && !dtf.purity;
+ const saErr = ctf.trust == TRUST.safe && dtf.trust <= TRUST.system;
+
+ if (ngErr || puErr || saErr)
+ {
+ // storage_class is apparently not set for dtor & ctor
+ OutBuffer ob;
+ stcToBuffer(&ob,
+ (ngErr ? STC.nogc : 0) |
+ (puErr ? STC.pure_ : 0) |
+ (saErr ? STC.system : 0)
+ );
+ ctor.loc.deprecation("`%s` has stricter attributes than its destructor (`%s`)", ctor.toPrettyChars(), ob.peekChars());
+ ctor.loc.deprecationSupplemental("The destructor will be called if an exception is thrown");
+ ctor.loc.deprecationSupplemental("Either make the constructor `nothrow` or adjust the field destructors");
+
+ ce.ignoreAttributes = true;
+ }
+ }
+
+ version (all)
+ {
+ /* Generate:
+ * try { ctor.fbody; }
+ * catch (Exception __o)
+ * { this.fieldDtor(); throw __o; }
+ * This differs from the alternate scope(failure) version in that an Exception
+ * is caught rather than a Throwable. This enables the optimization whereby
+ * the try-catch can be removed if ctor.fbody is nothrow. (nothrow only
+ * applies to Exception.)
+ */
+ Identifier id = Identifier.generateId("__o");
+ auto ts = new ThrowStatement(ctor.loc, new IdentifierExp(ctor.loc, id));
+ auto handler = new CompoundStatement(ctor.loc, ss, ts);
+
+ auto catches = new Catches();
+ auto ctch = new Catch(ctor.loc, getException(), id, handler);
+ catches.push(ctch);
+
+ ctor.fbody = new TryCatchStatement(ctor.loc, ctor.fbody, catches);
+ }
+ else
+ {
+ /* Generate:
+ * scope (failure) { this.fieldDtor(); }
+ * Hopefully we can use this version someday when scope(failure) catches
+ * Exception instead of Throwable.
+ */
+ auto s = new ScopeGuardStatement(ctor.loc, TOK.onScopeFailure, ss);
+ ctor.fbody = new CompoundStatement(ctor.loc, s, ctor.fbody);
+ }
+ visit(cast(FuncDeclaration)ctor);
+ }
+
+
+ override void visit(Nspace ns)
+ {
+ if (ns.semanticRun >= PASS.semantic3)
+ return;
+ ns.semanticRun = PASS.semantic3;
+ static if (LOG)
+ {
+ printf("Nspace::semantic3('%s')\n", ns.toChars());
+ }
+ if (!ns.members)
+ return;
+
+ sc = sc.push(ns);
+ sc.linkage = LINK.cpp;
+ foreach (s; *ns.members)
+ {
+ s.semantic3(sc);
+ }
+ sc.pop();
+ }
+
+ override void visit(AttribDeclaration ad)
+ {
+ Dsymbols* d = ad.include(sc);
+ if (!d)
+ return;
+
+ Scope* sc2 = ad.newScope(sc);
+ for (size_t i = 0; i < d.dim; i++)
+ {
+ Dsymbol s = (*d)[i];
+ s.semantic3(sc2);
+ }
+ if (sc2 != sc)
+ sc2.pop();
+ }
+
+ override void visit(AggregateDeclaration ad)
+ {
+ //printf("AggregateDeclaration::semantic3(sc=%p, %s) type = %s, errors = %d\n", sc, toChars(), type.toChars(), errors);
+ if (!ad.members)
+ return;
+
+ StructDeclaration sd = ad.isStructDeclaration();
+ if (!sc) // from runDeferredSemantic3 for TypeInfo generation
+ {
+ assert(sd);
+ sd.semanticTypeInfoMembers();
+ return;
+ }
+
+ auto sc2 = ad.newScope(sc);
+
+ for (size_t i = 0; i < ad.members.dim; i++)
+ {
+ Dsymbol s = (*ad.members)[i];
+ s.semantic3(sc2);
+ }
+
+ sc2.pop();
+
+ // don't do it for unused deprecated types
+ // or error ypes
+ if (!ad.getRTInfo && Type.rtinfo && (!ad.isDeprecated() || global.params.useDeprecated != DiagnosticReporting.error) && (ad.type && ad.type.ty != Terror))
+ {
+ // Evaluate: RTinfo!type
+ auto tiargs = new Objects();
+ tiargs.push(ad.type);
+ auto ti = new TemplateInstance(ad.loc, Type.rtinfo, tiargs);
+
+ Scope* sc3 = ti.tempdecl._scope.startCTFE();
+ sc3.tinst = sc.tinst;
+ sc3.minst = sc.minst;
+ if (ad.isDeprecated())
+ sc3.stc |= STC.deprecated_;
+
+ ti.dsymbolSemantic(sc3);
+ ti.semantic2(sc3);
+ ti.semantic3(sc3);
+ auto e = symbolToExp(ti.toAlias(), Loc.initial, sc3, false);
+
+ sc3.endCTFE();
+
+ e = e.ctfeInterpret();
+ ad.getRTInfo = e;
+ }
+ if (sd)
+ sd.semanticTypeInfoMembers();
+ ad.semanticRun = PASS.semantic3done;
+ }
+}
+
+private struct FuncDeclSem3
+{
+ // The FuncDeclaration subject to Semantic analysis
+ FuncDeclaration funcdecl;
+
+ // Scope of analysis
+ Scope* sc;
+ this(FuncDeclaration fd,Scope* s)
+ {
+ funcdecl = fd;
+ sc = s;
+ }
+
+ /* Checks that the overriden functions (if any) have in contracts if
+ * funcdecl has an in contract.
+ */
+ void checkInContractOverrides()
+ {
+ if (funcdecl.frequires)
+ {
+ for (size_t i = 0; i < funcdecl.foverrides.dim; i++)
+ {
+ FuncDeclaration fdv = funcdecl.foverrides[i];
+ if (fdv.fbody && !fdv.frequires)
+ {
+ funcdecl.error("cannot have an in contract when overridden function `%s` does not have an in contract", fdv.toPrettyChars());
+ break;
+ }
+ }
+ }
+ }
+}
+
+private void semanticTypeInfoMembers(StructDeclaration sd)
+{
+ if (sd.xeq &&
+ sd.xeq._scope &&
+ sd.xeq.semanticRun < PASS.semantic3done)
+ {
+ uint errors = global.startGagging();
+ sd.xeq.semantic3(sd.xeq._scope);
+ if (global.endGagging(errors))
+ sd.xeq = sd.xerreq;
+ }
+
+ if (sd.xcmp &&
+ sd.xcmp._scope &&
+ sd.xcmp.semanticRun < PASS.semantic3done)
+ {
+ uint errors = global.startGagging();
+ sd.xcmp.semantic3(sd.xcmp._scope);
+ if (global.endGagging(errors))
+ sd.xcmp = sd.xerrcmp;
+ }
+
+ FuncDeclaration ftostr = search_toString(sd);
+ if (ftostr &&
+ ftostr._scope &&
+ ftostr.semanticRun < PASS.semantic3done)
+ {
+ ftostr.semantic3(ftostr._scope);
+ }
+
+ if (sd.xhash &&
+ sd.xhash._scope &&
+ sd.xhash.semanticRun < PASS.semantic3done)
+ {
+ sd.xhash.semantic3(sd.xhash._scope);
+ }
+
+ if (sd.postblit &&
+ sd.postblit._scope &&
+ sd.postblit.semanticRun < PASS.semantic3done)
+ {
+ sd.postblit.semantic3(sd.postblit._scope);
+ }
+
+ if (sd.dtor &&
+ sd.dtor._scope &&
+ sd.dtor.semanticRun < PASS.semantic3done)
+ {
+ sd.dtor.semantic3(sd.dtor._scope);
+ }
+}
diff --git a/gcc/d/dmd/sideeffect.c b/gcc/d/dmd/sideeffect.c
deleted file mode 100644
index 661bd43134c..00000000000
--- a/gcc/d/dmd/sideeffect.c
+++ /dev/null
@@ -1,432 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/sideeffect.c
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "init.h"
-#include "expression.h"
-#include "template.h"
-#include "statement.h"
-#include "mtype.h"
-#include "utf.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "scope.h"
-#include "attrib.h"
-#include "tokens.h"
-
-bool walkPostorder(Expression *e, StoppableVisitor *v);
-bool lambdaHasSideEffect(Expression *e);
-
-/**************************************************
- * Front-end expression rewriting should create temporary variables for
- * non trivial sub-expressions in order to:
- * 1. save evaluation order
- * 2. prevent sharing of sub-expression in AST
- */
-bool isTrivialExp(Expression *e)
-{
- class IsTrivialExp : public StoppableVisitor
- {
- public:
- IsTrivialExp() {}
-
- void visit(Expression *e)
- {
- /* Bugzilla 11201: CallExp is always non trivial expression,
- * especially for inlining.
- */
- if (e->op == TOKcall)
- {
- stop = true;
- return;
- }
-
- // stop walking if we determine this expression has side effects
- stop = lambdaHasSideEffect(e);
- }
- };
-
- IsTrivialExp v;
- return walkPostorder(e, &v) == false;
-}
-
-/********************************************
- * Determine if Expression has any side effects.
- */
-
-bool hasSideEffect(Expression *e)
-{
- class LambdaHasSideEffect : public StoppableVisitor
- {
- public:
- LambdaHasSideEffect() {}
-
- void visit(Expression *e)
- {
- // stop walking if we determine this expression has side effects
- stop = lambdaHasSideEffect(e);
- }
- };
-
- LambdaHasSideEffect v;
- return walkPostorder(e, &v);
-}
-
-/********************************************
- * Determine if the call of f, or function type or delegate type t1, has any side effects.
- * Returns:
- * 0 has any side effects
- * 1 nothrow + constant purity
- * 2 nothrow + strong purity
- */
-
-int callSideEffectLevel(FuncDeclaration *f)
-{
- /* Bugzilla 12760: ctor call always has side effects.
- */
- if (f->isCtorDeclaration())
- return 0;
-
- assert(f->type->ty == Tfunction);
- TypeFunction *tf = (TypeFunction *)f->type;
- if (tf->isnothrow)
- {
- PURE purity = f->isPure();
- if (purity == PUREstrong)
- return 2;
- if (purity == PUREconst)
- return 1;
- }
- return 0;
-}
-
-int callSideEffectLevel(Type *t)
-{
- t = t->toBasetype();
-
- TypeFunction *tf;
- if (t->ty == Tdelegate)
- tf = (TypeFunction *)((TypeDelegate *)t)->next;
- else
- {
- assert(t->ty == Tfunction);
- tf = (TypeFunction *)t;
- }
-
- tf->purityLevel();
- PURE purity = tf->purity;
- if (t->ty == Tdelegate && purity > PUREweak)
- {
- if (tf->isMutable())
- purity = PUREweak;
- else if (!tf->isImmutable())
- purity = PUREconst;
- }
-
- if (tf->isnothrow)
- {
- if (purity == PUREstrong)
- return 2;
- if (purity == PUREconst)
- return 1;
- }
- return 0;
-}
-
-bool lambdaHasSideEffect(Expression *e)
-{
- switch (e->op)
- {
- // Sort the cases by most frequently used first
- case TOKassign:
- case TOKplusplus:
- case TOKminusminus:
- case TOKdeclaration:
- case TOKconstruct:
- case TOKblit:
- case TOKaddass:
- case TOKminass:
- case TOKcatass:
- case TOKmulass:
- case TOKdivass:
- case TOKmodass:
- case TOKshlass:
- case TOKshrass:
- case TOKushrass:
- case TOKandass:
- case TOKorass:
- case TOKxorass:
- case TOKpowass:
- case TOKin:
- case TOKremove:
- case TOKassert:
- case TOKhalt:
- case TOKdelete:
- case TOKnew:
- case TOKnewanonclass:
- return true;
-
- case TOKcall:
- {
- CallExp *ce = (CallExp *)e;
- /* Calling a function or delegate that is pure nothrow
- * has no side effects.
- */
- if (ce->e1->type)
- {
- Type *t = ce->e1->type->toBasetype();
- if (t->ty == Tdelegate)
- t = ((TypeDelegate *)t)->next;
- if (t->ty == Tfunction &&
- (ce->f ? callSideEffectLevel(ce->f)
- : callSideEffectLevel(ce->e1->type)) > 0)
- {
- }
- else
- return true;
- }
- break;
- }
-
- case TOKcast:
- {
- CastExp *ce = (CastExp *)e;
- /* if:
- * cast(classtype)func() // because it may throw
- */
- if (ce->to->ty == Tclass && ce->e1->op == TOKcall && ce->e1->type->ty == Tclass)
- return true;
- break;
- }
-
- default:
- break;
- }
- return false;
-}
-
-
-/***********************************
- * The result of this expression will be discarded.
- * Print error messages if the operation has no side effects (and hence is meaningless).
- * Returns:
- * true if expression has no side effects
- */
-bool discardValue(Expression *e)
-{
- if (lambdaHasSideEffect(e)) // check side-effect shallowly
- return false;
- switch (e->op)
- {
- case TOKcast:
- {
- CastExp *ce = (CastExp *)e;
- if (ce->to->equals(Type::tvoid))
- {
- /*
- * Don't complain about an expression with no effect if it was cast to void
- */
- return false;
- }
- break; // complain
- }
-
- case TOKerror:
- return false;
-
- case TOKvar:
- {
- VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration();
- if (v && (v->storage_class & STCtemp))
- {
- // Bugzilla 5810: Don't complain about an internal generated variable.
- return false;
- }
- break;
- }
- case TOKcall:
- /* Issue 3882: */
- if (global.params.warnings != DIAGNOSTICoff && !global.gag)
- {
- CallExp *ce = (CallExp *)e;
- if (e->type->ty == Tvoid)
- {
- /* Don't complain about calling void-returning functions with no side-effect,
- * because purity and nothrow are inferred, and because some of the
- * runtime library depends on it. Needs more investigation.
- *
- * One possible solution is to restrict this message to only be called in hierarchies that
- * never call assert (and or not called from inside unittest blocks)
- */
- }
- else if (ce->e1->type)
- {
- Type *t = ce->e1->type->toBasetype();
- if (t->ty == Tdelegate)
- t = ((TypeDelegate *)t)->next;
- if (t->ty == Tfunction &&
- (ce->f ? callSideEffectLevel(ce->f)
- : callSideEffectLevel(ce->e1->type)) > 0)
- {
- const char *s;
- if (ce->f)
- s = ce->f->toPrettyChars();
- else if (ce->e1->op == TOKstar)
- {
- // print 'fp' if ce->e1 is (*fp)
- s = ((PtrExp *)ce->e1)->e1->toChars();
- }
- else
- s = ce->e1->toChars();
-
- e->warning("calling %s without side effects discards return value of type %s, prepend a cast(void) if intentional",
- s, e->type->toChars());
- }
- }
- }
- return false;
-
- case TOKscope:
- e->error("%s has no effect", e->toChars());
- return true;
-
- case TOKandand:
- case TOKoror:
- {
- LogicalExp *aae = (LogicalExp *)e;
- return discardValue(aae->e2);
- }
-
- case TOKquestion:
- {
- CondExp *ce = (CondExp *)e;
-
- /* Bugzilla 6178 & 14089: Either CondExp::e1 or e2 may have
- * redundant expression to make those types common. For example:
- *
- * struct S { this(int n); int v; alias v this; }
- * S[int] aa;
- * aa[1] = 0;
- *
- * The last assignment statement will be rewitten to:
- *
- * 1 in aa ? aa[1].value = 0 : (aa[1] = 0, aa[1].this(0)).value;
- *
- * The last DotVarExp is necessary to take assigned value.
- *
- * int value = (aa[1] = 0); // value = aa[1].value
- *
- * To avoid false error, discardValue() should be called only when
- * the both tops of e1 and e2 have actually no side effects.
- */
- if (!lambdaHasSideEffect(ce->e1) &&
- !lambdaHasSideEffect(ce->e2))
- {
- return discardValue(ce->e1) |
- discardValue(ce->e2);
- }
- return false;
- }
-
- case TOKcomma:
- {
- CommaExp *ce = (CommaExp *)e;
- /* Check for compiler-generated code of the form auto __tmp, e, __tmp;
- * In such cases, only check e for side effect (it's OK for __tmp to have
- * no side effect).
- * See Bugzilla 4231 for discussion
- */
- CommaExp *firstComma = ce;
- while (firstComma->e1->op == TOKcomma)
- firstComma = (CommaExp *)firstComma->e1;
- if (firstComma->e1->op == TOKdeclaration &&
- ce->e2->op == TOKvar &&
- ((DeclarationExp *)firstComma->e1)->declaration == ((VarExp*)ce->e2)->var)
- {
- return false;
- }
- // Don't check e1 until we cast(void) the a,b code generation
- //discardValue(ce->e1);
- return discardValue(ce->e2);
- }
-
- case TOKtuple:
- /* Pass without complaint if any of the tuple elements have side effects.
- * Ideally any tuple elements with no side effects should raise an error,
- * this needs more investigation as to what is the right thing to do.
- */
- if (!hasSideEffect(e))
- break;
- return false;
-
- default:
- break;
- }
- e->error("%s has no effect in expression (%s)", Token::toChars(e->op), e->toChars());
- return true;
-}
-
-/**************************************************
- * Build a temporary variable to copy the value of e into.
- * Params:
- * stc = storage classes will be added to the made temporary variable
- * name = name for temporary variable
- * e = original expression
- * Returns:
- * Newly created temporary variable.
- */
-VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e)
-{
- assert(name && name[0] == '_' && name[1] == '_');
- Identifier *id = Identifier::generateId(name);
- ExpInitializer *ez = new ExpInitializer(e->loc, e);
- VarDeclaration *vd = new VarDeclaration(e->loc, e->type, id, ez);
- vd->storage_class = stc;
- vd->storage_class |= STCtemp;
- vd->storage_class |= STCctfe; // temporary is always CTFEable
- return vd;
-}
-
-/**************************************************
- * Build a temporary variable to extract e's evaluation, if e is not trivial.
- * Params:
- * sc = scope
- * name = name for temporary variable
- * e0 = a new side effect part will be appended to it.
- * e = original expression
- * alwaysCopy = if true, build new temporary variable even if e is trivial.
- * Returns:
- * When e is trivial and alwaysCopy == false, e itself is returned.
- * Otherwise, a new VarExp is returned.
- * Note:
- * e's lvalue-ness will be handled well by STCref or STCrvalue.
- */
-Expression *extractSideEffect(Scope *sc, const char *name,
- Expression **e0, Expression *e, bool alwaysCopy = false)
-{
- if (!alwaysCopy && isTrivialExp(e))
- return e;
-
- VarDeclaration *vd = copyToTemp(0, name, e);
- if (e->isLvalue())
- vd->storage_class |= STCref;
- else
- vd->storage_class |= STCrvalue;
-
- Expression *de = new DeclarationExp(vd->loc, vd);
- Expression *ve = new VarExp(vd->loc, vd);
- de = expressionSemantic(de, sc);
- ve = expressionSemantic(ve, sc);
-
- *e0 = Expression::combine(*e0, de);
- return ve;
-}
diff --git a/gcc/d/dmd/sideeffect.d b/gcc/d/dmd/sideeffect.d
new file mode 100644
index 00000000000..d238150e75e
--- /dev/null
+++ b/gcc/d/dmd/sideeffect.d
@@ -0,0 +1,418 @@
+/**
+ * Find side-effects of expressions.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/sideeffect.d, _sideeffect.d)
+ * Documentation: https://dlang.org/phobos/dmd_sideeffect.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/sideeffect.d
+ */
+
+module dmd.sideeffect;
+
+import dmd.apply;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dscope;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.identifier;
+import dmd.init;
+import dmd.mtype;
+import dmd.tokens;
+import dmd.visitor;
+
+/**************************************************
+ * Front-end expression rewriting should create temporary variables for
+ * non trivial sub-expressions in order to:
+ * 1. save evaluation order
+ * 2. prevent sharing of sub-expression in AST
+ */
+extern (C++) bool isTrivialExp(Expression e)
+{
+ extern (C++) final class IsTrivialExp : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ public:
+ extern (D) this()
+ {
+ }
+
+ override void visit(Expression e)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=11201
+ * CallExp is always non trivial expression,
+ * especially for inlining.
+ */
+ if (e.op == TOK.call)
+ {
+ stop = true;
+ return;
+ }
+ // stop walking if we determine this expression has side effects
+ stop = lambdaHasSideEffect(e);
+ }
+ }
+
+ scope IsTrivialExp v = new IsTrivialExp();
+ return walkPostorder(e, v) == false;
+}
+
+/********************************************
+ * Determine if Expression has any side effects.
+ *
+ * Params:
+ * e = the expression
+ * assumeImpureCalls = whether function calls should always be assumed to
+ * be impure (e.g. debug is allowed to violate purity)
+ */
+extern (C++) bool hasSideEffect(Expression e, bool assumeImpureCalls = false)
+{
+ extern (C++) final class LambdaHasSideEffect : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ public:
+ extern (D) this()
+ {
+ }
+
+ override void visit(Expression e)
+ {
+ // stop walking if we determine this expression has side effects
+ stop = lambdaHasSideEffect(e, assumeImpureCalls);
+ }
+ }
+
+ scope LambdaHasSideEffect v = new LambdaHasSideEffect();
+ return walkPostorder(e, v);
+}
+
+/********************************************
+ * Determine if the call of f, or function type or delegate type t1, has any side effects.
+ * Returns:
+ * 0 has any side effects
+ * 1 nothrow + constant purity
+ * 2 nothrow + strong purity
+ */
+int callSideEffectLevel(FuncDeclaration f)
+{
+ /* https://issues.dlang.org/show_bug.cgi?id=12760
+ * ctor call always has side effects.
+ */
+ if (f.isCtorDeclaration())
+ return 0;
+ assert(f.type.ty == Tfunction);
+ TypeFunction tf = cast(TypeFunction)f.type;
+ if (tf.isnothrow)
+ {
+ PURE purity = f.isPure();
+ if (purity == PURE.strong)
+ return 2;
+ if (purity == PURE.const_)
+ return 1;
+ }
+ return 0;
+}
+
+int callSideEffectLevel(Type t)
+{
+ t = t.toBasetype();
+ TypeFunction tf;
+ if (t.ty == Tdelegate)
+ tf = cast(TypeFunction)(cast(TypeDelegate)t).next;
+ else
+ {
+ assert(t.ty == Tfunction);
+ tf = cast(TypeFunction)t;
+ }
+ if (!tf.isnothrow) // function can throw
+ return 0;
+
+ tf.purityLevel();
+ PURE purity = tf.purity;
+ if (t.ty == Tdelegate && purity > PURE.weak)
+ {
+ if (tf.isMutable())
+ purity = PURE.weak;
+ else if (!tf.isImmutable())
+ purity = PURE.const_;
+ }
+
+ if (purity == PURE.strong)
+ return 2;
+ if (purity == PURE.const_)
+ return 1;
+ return 0;
+}
+
+private bool lambdaHasSideEffect(Expression e, bool assumeImpureCalls = false)
+{
+ switch (e.op)
+ {
+ // Sort the cases by most frequently used first
+ case TOK.assign:
+ case TOK.plusPlus:
+ case TOK.minusMinus:
+ case TOK.declaration:
+ case TOK.construct:
+ case TOK.blit:
+ case TOK.addAssign:
+ case TOK.minAssign:
+ case TOK.concatenateAssign:
+ case TOK.concatenateElemAssign:
+ case TOK.concatenateDcharAssign:
+ case TOK.mulAssign:
+ case TOK.divAssign:
+ case TOK.modAssign:
+ case TOK.leftShiftAssign:
+ case TOK.rightShiftAssign:
+ case TOK.unsignedRightShiftAssign:
+ case TOK.andAssign:
+ case TOK.orAssign:
+ case TOK.xorAssign:
+ case TOK.powAssign:
+ case TOK.in_:
+ case TOK.remove:
+ case TOK.assert_:
+ case TOK.halt:
+ case TOK.delete_:
+ case TOK.new_:
+ case TOK.newAnonymousClass:
+ return true;
+ case TOK.call:
+ {
+ if (assumeImpureCalls)
+ return true;
+
+ if (e.type && e.type.ty == Tnoreturn)
+ return true;
+
+ CallExp ce = cast(CallExp)e;
+ /* Calling a function or delegate that is pure nothrow
+ * has no side effects.
+ */
+ if (ce.e1.type)
+ {
+ Type t = ce.e1.type.toBasetype();
+ if (t.ty == Tdelegate)
+ t = (cast(TypeDelegate)t).next;
+ if (t.ty == Tfunction && (ce.f ? callSideEffectLevel(ce.f) : callSideEffectLevel(ce.e1.type)) > 0)
+ {
+ }
+ else
+ return true;
+ }
+ break;
+ }
+ case TOK.cast_:
+ {
+ CastExp ce = cast(CastExp)e;
+ /* if:
+ * cast(classtype)func() // because it may throw
+ */
+ if (ce.to.ty == Tclass && ce.e1.op == TOK.call && ce.e1.type.ty == Tclass)
+ return true;
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+/***********************************
+ * The result of this expression will be discarded.
+ * Print error messages if the operation has no side effects (and hence is meaningless).
+ * Returns:
+ * true if expression has no side effects
+ */
+bool discardValue(Expression e)
+{
+ if (lambdaHasSideEffect(e)) // check side-effect shallowly
+ return false;
+ switch (e.op)
+ {
+ case TOK.cast_:
+ {
+ CastExp ce = cast(CastExp)e;
+ if (ce.to.equals(Type.tvoid))
+ {
+ /*
+ * Don't complain about an expression with no effect if it was cast to void
+ */
+ return false;
+ }
+ break; // complain
+ }
+ case TOK.error:
+ return false;
+ case TOK.variable:
+ {
+ VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
+ if (v && (v.storage_class & STC.temp))
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=5810
+ // Don't complain about an internal generated variable.
+ return false;
+ }
+ break;
+ }
+ case TOK.call:
+ /* Issue 3882: */
+ if (global.params.warnings != DiagnosticReporting.off && !global.gag)
+ {
+ CallExp ce = cast(CallExp)e;
+ if (e.type.ty == Tvoid)
+ {
+ /* Don't complain about calling void-returning functions with no side-effect,
+ * because purity and nothrow are inferred, and because some of the
+ * runtime library depends on it. Needs more investigation.
+ *
+ * One possible solution is to restrict this message to only be called in hierarchies that
+ * never call assert (and or not called from inside unittest blocks)
+ */
+ }
+ else if (ce.e1.type)
+ {
+ Type t = ce.e1.type.toBasetype();
+ if (t.ty == Tdelegate)
+ t = (cast(TypeDelegate)t).next;
+ if (t.ty == Tfunction && (ce.f ? callSideEffectLevel(ce.f) : callSideEffectLevel(ce.e1.type)) > 0)
+ {
+ const(char)* s;
+ if (ce.f)
+ s = ce.f.toPrettyChars();
+ else if (ce.e1.op == TOK.star)
+ {
+ // print 'fp' if ce.e1 is (*fp)
+ s = (cast(PtrExp)ce.e1).e1.toChars();
+ }
+ else
+ s = ce.e1.toChars();
+ e.warning("calling `%s` without side effects discards return value of type `%s`; prepend a `cast(void)` if intentional", s, e.type.toChars());
+ }
+ }
+ }
+ return false;
+ case TOK.andAnd:
+ case TOK.orOr:
+ {
+ LogicalExp aae = cast(LogicalExp)e;
+ return discardValue(aae.e2);
+ }
+ case TOK.question:
+ {
+ CondExp ce = cast(CondExp)e;
+ /* https://issues.dlang.org/show_bug.cgi?id=6178
+ * https://issues.dlang.org/show_bug.cgi?id=14089
+ * Either CondExp::e1 or e2 may have
+ * redundant expression to make those types common. For example:
+ *
+ * struct S { this(int n); int v; alias v this; }
+ * S[int] aa;
+ * aa[1] = 0;
+ *
+ * The last assignment statement will be rewitten to:
+ *
+ * 1 in aa ? aa[1].value = 0 : (aa[1] = 0, aa[1].this(0)).value;
+ *
+ * The last DotVarExp is necessary to take assigned value.
+ *
+ * int value = (aa[1] = 0); // value = aa[1].value
+ *
+ * To avoid false error, discardValue() should be called only when
+ * the both tops of e1 and e2 have actually no side effects.
+ */
+ if (!lambdaHasSideEffect(ce.e1) && !lambdaHasSideEffect(ce.e2))
+ {
+ return discardValue(ce.e1) |
+ discardValue(ce.e2);
+ }
+ return false;
+ }
+ case TOK.comma:
+ {
+ CommaExp ce = cast(CommaExp)e;
+ // Don't complain about compiler-generated comma expressions
+ if (ce.isGenerated)
+ return false;
+
+ // Don't check e1 until we cast(void) the a,b code generation.
+ // This is concretely done in expressionSemantic, if a CommaExp has Tvoid as type
+ return discardValue(ce.e2);
+ }
+ case TOK.tuple:
+ /* Pass without complaint if any of the tuple elements have side effects.
+ * Ideally any tuple elements with no side effects should raise an error,
+ * this needs more investigation as to what is the right thing to do.
+ */
+ if (!hasSideEffect(e))
+ break;
+ return false;
+ default:
+ break;
+ }
+ e.error("`%s` has no effect", e.toChars());
+ return true;
+}
+
+/**************************************************
+ * Build a temporary variable to copy the value of e into.
+ * Params:
+ * stc = storage classes will be added to the made temporary variable
+ * name = name for temporary variable
+ * e = original expression
+ * Returns:
+ * Newly created temporary variable.
+ */
+VarDeclaration copyToTemp(StorageClass stc, const char[] name, Expression e)
+{
+ assert(name[0] == '_' && name[1] == '_');
+ auto vd = new VarDeclaration(e.loc, e.type,
+ Identifier.generateId(name),
+ new ExpInitializer(e.loc, e));
+ vd.storage_class = stc | STC.temp | STC.ctfe; // temporary is always CTFEable
+ return vd;
+}
+
+/**************************************************
+ * Build a temporary variable to extract e's evaluation, if e is not trivial.
+ * Params:
+ * sc = scope
+ * name = name for temporary variable
+ * e0 = a new side effect part will be appended to it.
+ * e = original expression
+ * alwaysCopy = if true, build new temporary variable even if e is trivial.
+ * Returns:
+ * When e is trivial and alwaysCopy == false, e itself is returned.
+ * Otherwise, a new VarExp is returned.
+ * Note:
+ * e's lvalue-ness will be handled well by STC.ref_ or STC.rvalue.
+ */
+Expression extractSideEffect(Scope* sc, const char[] name,
+ ref Expression e0, Expression e, bool alwaysCopy = false)
+{
+ //printf("extractSideEffect(e: %s)\n", e.toChars());
+
+ /* The trouble here is that if CTFE is running, extracting the side effect
+ * results in an assignment, and then the interpreter says it cannot evaluate the
+ * side effect assignment variable. But we don't have to worry about side
+ * effects in function calls anyway, because then they won't CTFE.
+ * https://issues.dlang.org/show_bug.cgi?id=17145
+ */
+ if (!alwaysCopy &&
+ ((sc.flags & SCOPE.ctfe) ? !hasSideEffect(e) : isTrivialExp(e)))
+ return e;
+
+ auto vd = copyToTemp(0, name, e);
+ vd.storage_class |= e.isLvalue() ? STC.ref_ : STC.rvalue;
+
+ e0 = Expression.combine(e0, new DeclarationExp(vd.loc, vd)
+ .expressionSemantic(sc));
+
+ return new VarExp(vd.loc, vd)
+ .expressionSemantic(sc);
+}
diff --git a/gcc/d/dmd/statement.c b/gcc/d/dmd/statement.c
deleted file mode 100644
index 1f8e5122b1a..00000000000
--- a/gcc/d/dmd/statement.c
+++ /dev/null
@@ -1,1793 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/statement.c
- */
-
-#include "root/dsystem.h"
-
-#include "statement.h"
-#include "errors.h"
-#include "expression.h"
-#include "cond.h"
-#include "init.h"
-#include "staticassert.h"
-#include "scope.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "id.h"
-#include "hdrgen.h"
-#include "parse.h"
-#include "template.h"
-#include "attrib.h"
-#include "import.h"
-
-bool walkPostorder(Statement *s, StoppableVisitor *v);
-StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f);
-bool checkEscapeRef(Scope *sc, Expression *e, bool gag);
-VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e);
-Statement *makeTupleForeachStatic(Scope *sc, ForeachStatement *fs, bool needExpansion);
-bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps);
-
-Identifier *fixupLabelName(Scope *sc, Identifier *ident)
-{
- unsigned flags = (sc->flags & SCOPEcontract);
- const char *id = ident->toChars();
- if (flags && flags != SCOPEinvariant &&
- !(id[0] == '_' && id[1] == '_'))
- {
- /* CTFE requires FuncDeclaration::labtab for the interpretation.
- * So fixing the label name inside in/out contracts is necessary
- * for the uniqueness in labtab.
- */
- const char *prefix = flags == SCOPErequire ? "__in_" : "__out_";
- OutBuffer buf;
- buf.printf("%s%s", prefix, ident->toChars());
-
- const char *name = buf.extractChars();
- ident = Identifier::idPool(name);
- }
- return ident;
-}
-
-LabelStatement *checkLabeledLoop(Scope *sc, Statement *statement)
-{
- if (sc->slabel && sc->slabel->statement == statement)
- {
- return sc->slabel;
- }
- return NULL;
-}
-
-/***********************************************************
- * Check an assignment is used as a condition.
- * Intended to be use before the `semantic` call on `e`.
- * Params:
- * e = condition expression which is not yet run semantic analysis.
- * Returns:
- * `e` or ErrorExp.
- */
-Expression *checkAssignmentAsCondition(Expression *e)
-{
- Expression *ec = e;
- while (ec->op == TOKcomma)
- ec = ((CommaExp *)ec)->e2;
- if (ec->op == TOKassign)
- {
- ec->error("assignment cannot be used as a condition, perhaps == was meant?");
- return new ErrorExp();
- }
- return e;
-}
-
-/// Return a type identifier reference to 'object.Throwable'
-TypeIdentifier *getThrowable()
-{
- TypeIdentifier *tid = new TypeIdentifier(Loc(), Id::empty);
- tid->addIdent(Id::object);
- tid->addIdent(Id::Throwable);
- return tid;
-}
-
-/******************************** Statement ***************************/
-
-Statement::Statement(Loc loc)
- : loc(loc)
-{
- // If this is an in{} contract scope statement (skip for determining
- // inlineStatus of a function body for header content)
-}
-
-Statement *Statement::syntaxCopy()
-{
- assert(0);
- return NULL;
-}
-
-/*************************************
- * Do syntax copy of an array of Statement's.
- */
-Statements *Statement::arraySyntaxCopy(Statements *a)
-{
- Statements *b = NULL;
- if (a)
- {
- b = a->copy();
- for (size_t i = 0; i < a->length; i++)
- {
- Statement *s = (*a)[i];
- (*b)[i] = s ? s->syntaxCopy() : NULL;
- }
- }
- return b;
-}
-
-void Statement::print()
-{
- fprintf(stderr, "%s\n", toChars());
- fflush(stderr);
-}
-
-const char *Statement::toChars()
-{
- HdrGenState hgs;
-
- OutBuffer buf;
- ::toCBuffer(this, &buf, &hgs);
- return buf.extractChars();
-}
-
-
-void Statement::error(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::verror(loc, format, ap);
- va_end( ap );
-}
-
-void Statement::warning(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::vwarning(loc, format, ap);
- va_end( ap );
-}
-
-void Statement::deprecation(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- ::vdeprecation(loc, format, ap);
- va_end( ap );
-}
-
-bool Statement::hasBreak()
-{
- //printf("Statement::hasBreak()\n");
- return false;
-}
-
-bool Statement::hasContinue()
-{
- return false;
-}
-
-/* ============================================== */
-// true if statement uses exception handling
-
-bool Statement::usesEH()
-{
- class UsesEH : public StoppableVisitor
- {
- public:
- void visit(Statement *) {}
- void visit(TryCatchStatement *) { stop = true; }
- void visit(TryFinallyStatement *) { stop = true; }
- void visit(ScopeGuardStatement *) { stop = true; }
- void visit(SynchronizedStatement *) { stop = true; }
- };
-
- UsesEH ueh;
- return walkPostorder(this, &ueh);
-}
-
-/* ============================================== */
-// true if statement 'comes from' somewhere else, like a goto
-
-bool Statement::comeFrom()
-{
- class ComeFrom : public StoppableVisitor
- {
- public:
- void visit(Statement *) {}
- void visit(CaseStatement *) { stop = true; }
- void visit(DefaultStatement *) { stop = true; }
- void visit(LabelStatement *) { stop = true; }
- void visit(AsmStatement *) { stop = true; }
- };
-
- ComeFrom cf;
- return walkPostorder(this, &cf);
-}
-
-/* ============================================== */
-// Return true if statement has executable code.
-
-bool Statement::hasCode()
-{
- class HasCode : public StoppableVisitor
- {
- public:
- void visit(Statement *)
- {
- stop = true;
- }
-
- void visit(ExpStatement *s)
- {
- if (s->exp != NULL)
- {
- stop = s->exp->hasCode();
- }
- }
-
- void visit(CompoundStatement *) {}
- void visit(ScopeStatement *) {}
- void visit(ImportStatement *) {}
- };
-
- HasCode hc;
- return walkPostorder(this, &hc);
-}
-
-Statement *Statement::last()
-{
- return this;
-}
-
-/****************************************
- * If this statement has code that needs to run in a finally clause
- * at the end of the current scope, return that code in the form of
- * a Statement.
- * Output:
- * *sentry code executed upon entry to the scope
- * *sexception code executed upon exit from the scope via exception
- * *sfinally code executed in finally block
- */
-
-Statement *Statement::scopeCode(Scope *, Statement **sentry, Statement **sexception, Statement **sfinally)
-{
- //printf("Statement::scopeCode()\n");
- //print();
- *sentry = NULL;
- *sexception = NULL;
- *sfinally = NULL;
- return this;
-}
-
-/*********************************
- * Flatten out the scope by presenting the statement
- * as an array of statements.
- * Returns NULL if no flattening necessary.
- */
-
-Statements *Statement::flatten(Scope *)
-{
- return NULL;
-}
-
-
-/******************************** ErrorStatement ***************************/
-
-ErrorStatement::ErrorStatement()
- : Statement(Loc())
-{
- assert(global.gaggedErrors || global.errors);
-}
-
-Statement *ErrorStatement::syntaxCopy()
-{
- return this;
-}
-
-/******************************** PeelStatement ***************************/
-
-PeelStatement::PeelStatement(Statement *s)
- : Statement(s->loc)
-{
- this->s = s;
-}
-
-/******************************** ExpStatement ***************************/
-
-ExpStatement::ExpStatement(Loc loc, Expression *exp)
- : Statement(loc)
-{
- this->exp = exp;
-}
-
-ExpStatement::ExpStatement(Loc loc, Dsymbol *declaration)
- : Statement(loc)
-{
- this->exp = new DeclarationExp(loc, declaration);
-}
-
-ExpStatement *ExpStatement::create(Loc loc, Expression *exp)
-{
- return new ExpStatement(loc, exp);
-}
-
-Statement *ExpStatement::syntaxCopy()
-{
- return new ExpStatement(loc, exp ? exp->syntaxCopy() : NULL);
-}
-
-Statement *ExpStatement::scopeCode(Scope *, Statement **sentry, Statement **sexception, Statement **sfinally)
-{
- //printf("ExpStatement::scopeCode()\n");
- //print();
-
- *sentry = NULL;
- *sexception = NULL;
- *sfinally = NULL;
-
- if (exp)
- {
- if (exp->op == TOKdeclaration)
- {
- DeclarationExp *de = (DeclarationExp *)(exp);
- VarDeclaration *v = de->declaration->isVarDeclaration();
- if (v && !v->isDataseg())
- {
- if (v->needsScopeDtor())
- {
- //printf("dtor is: "); v->edtor->print();
- *sfinally = new DtorExpStatement(loc, v->edtor, v);
- v->storage_class |= STCnodtor; // don't add in dtor again
- }
- }
- }
- }
- return this;
-}
-
-/****************************************
- * Convert TemplateMixin members (== Dsymbols) to Statements.
- */
-Statement *toStatement(Dsymbol *s)
-{
- class ToStmt : public Visitor
- {
- public:
- Statement *result;
-
- ToStmt()
- {
- this->result = NULL;
- }
-
- Statement *visitMembers(Loc loc, Dsymbols *a)
- {
- if (!a)
- return NULL;
-
- Statements *statements = new Statements();
- for (size_t i = 0; i < a->length; i++)
- {
- statements->push(toStatement((*a)[i]));
- }
- return new CompoundStatement(loc, statements);
- }
-
- void visit(Dsymbol *s)
- {
- ::error(Loc(), "Internal Compiler Error: cannot mixin %s %s\n", s->kind(), s->toChars());
- result = new ErrorStatement();
- }
-
- void visit(TemplateMixin *tm)
- {
- Statements *a = new Statements();
- for (size_t i = 0; i < tm->members->length; i++)
- {
- Statement *s = toStatement((*tm->members)[i]);
- if (s)
- a->push(s);
- }
- result = new CompoundStatement(tm->loc, a);
- }
-
- /* An actual declaration symbol will be converted to DeclarationExp
- * with ExpStatement.
- */
- Statement *declStmt(Dsymbol *s)
- {
- DeclarationExp *de = new DeclarationExp(s->loc, s);
- de->type = Type::tvoid; // avoid repeated semantic
- return new ExpStatement(s->loc, de);
- }
- void visit(VarDeclaration *d) { result = declStmt(d); }
- void visit(AggregateDeclaration *d) { result = declStmt(d); }
- void visit(FuncDeclaration *d) { result = declStmt(d); }
- void visit(EnumDeclaration *d) { result = declStmt(d); }
- void visit(AliasDeclaration *d) { result = declStmt(d); }
- void visit(TemplateDeclaration *d) { result = declStmt(d); }
-
- /* All attributes have been already picked by the semantic analysis of
- * 'bottom' declarations (function, struct, class, etc).
- * So we don't have to copy them.
- */
- void visit(StorageClassDeclaration *d) { result = visitMembers(d->loc, d->decl); }
- void visit(DeprecatedDeclaration *d) { result = visitMembers(d->loc, d->decl); }
- void visit(LinkDeclaration *d) { result = visitMembers(d->loc, d->decl); }
- void visit(ProtDeclaration *d) { result = visitMembers(d->loc, d->decl); }
- void visit(AlignDeclaration *d) { result = visitMembers(d->loc, d->decl); }
- void visit(UserAttributeDeclaration *d) { result = visitMembers(d->loc, d->decl); }
- void visit(ForwardingAttribDeclaration *d) { result = visitMembers(d->loc, d->decl); }
-
- void visit(StaticAssert *) {}
- void visit(Import *) {}
- void visit(PragmaDeclaration *) {}
-
- void visit(ConditionalDeclaration *d)
- {
- result = visitMembers(d->loc, d->include(NULL));
- }
-
- void visit(StaticForeachDeclaration *d)
- {
- assert(d->sfe && !!d->sfe->aggrfe ^ !!d->sfe->rangefe);
- result = visitMembers(d->loc, d->include(NULL));
- }
-
- void visit(CompileDeclaration *d)
- {
- result = visitMembers(d->loc, d->include(NULL));
- }
- };
-
- if (!s)
- return NULL;
-
- ToStmt v;
- s->accept(&v);
- return v.result;
-}
-
-Statements *ExpStatement::flatten(Scope *sc)
-{
- /* Bugzilla 14243: expand template mixin in statement scope
- * to handle variable destructors.
- */
- if (exp && exp->op == TOKdeclaration)
- {
- Dsymbol *d = ((DeclarationExp *)exp)->declaration;
- if (TemplateMixin *tm = d->isTemplateMixin())
- {
- Expression *e = expressionSemantic(exp, sc);
- if (e->op == TOKerror || tm->errors)
- {
- Statements *a = new Statements();
- a->push(new ErrorStatement());
- return a;
- }
- assert(tm->members);
-
- Statement *s = toStatement(tm);
- Statements *a = new Statements();
- a->push(s);
- return a;
- }
- }
- return NULL;
-}
-
-/******************************** DtorExpStatement ***************************/
-
-DtorExpStatement::DtorExpStatement(Loc loc, Expression *exp, VarDeclaration *v)
- : ExpStatement(loc, exp)
-{
- this->var = v;
-}
-
-Statement *DtorExpStatement::syntaxCopy()
-{
- return new DtorExpStatement(loc, exp ? exp->syntaxCopy() : NULL, var);
-}
-
-/******************************** CompileStatement ***************************/
-
-CompileStatement::CompileStatement(Loc loc, Expression *exp)
- : Statement(loc)
-{
- this->exps = new Expressions();
- this->exps->push(exp);
-}
-
-CompileStatement::CompileStatement(Loc loc, Expressions *exps)
- : Statement(loc)
-{
- this->exps = exps;
-}
-
-Statement *CompileStatement::syntaxCopy()
-{
- return new CompileStatement(loc, Expression::arraySyntaxCopy(exps));
-}
-
-static Statements *errorStatements()
-{
- Statements *a = new Statements();
- a->push(new ErrorStatement());
- return a;
-}
-
-static Statements *compileIt(CompileStatement *cs, Scope *sc)
-{
- //printf("CompileStatement::compileIt() %s\n", exp->toChars());
- OutBuffer buf;
- if (expressionsToString(buf, sc, cs->exps))
- return errorStatements();
-
- unsigned errors = global.errors;
- const size_t len = buf.length();
- const char *str = buf.extractChars();
- Parser p(cs->loc, sc->_module, (const utf8_t *)str, len, false);
- p.nextToken();
-
- Statements *a = new Statements();
- while (p.token.value != TOKeof)
- {
- Statement *s = p.parseStatement(PSsemi | PScurlyscope);
- if (!s || global.errors != errors)
- return errorStatements();
- a->push(s);
- }
- return a;
-}
-
-Statements *CompileStatement::flatten(Scope *sc)
-{
- //printf("CompileStatement::flatten() %s\n", exp->toChars());
- return compileIt(this, sc);
-}
-
-/******************************** CompoundStatement ***************************/
-
-CompoundStatement::CompoundStatement(Loc loc, Statements *s)
- : Statement(loc)
-{
- statements = s;
-}
-
-CompoundStatement::CompoundStatement(Loc loc, Statement *s1, Statement *s2)
- : Statement(loc)
-{
- statements = new Statements();
- statements->reserve(2);
- statements->push(s1);
- statements->push(s2);
-}
-
-CompoundStatement::CompoundStatement(Loc loc, Statement *s1)
- : Statement(loc)
-{
- statements = new Statements();
- statements->push(s1);
-}
-
-CompoundStatement *CompoundStatement::create(Loc loc, Statement *s1, Statement *s2)
-{
- return new CompoundStatement(loc, s1, s2);
-}
-
-Statement *CompoundStatement::syntaxCopy()
-{
- return new CompoundStatement(loc, Statement::arraySyntaxCopy(statements));
-}
-
-Statements *CompoundStatement::flatten(Scope *)
-{
- return statements;
-}
-
-ReturnStatement *CompoundStatement::isReturnStatement()
-{
- ReturnStatement *rs = NULL;
-
- for (size_t i = 0; i < statements->length; i++)
- {
- Statement *s = (*statements)[i];
- if (s)
- {
- rs = s->isReturnStatement();
- if (rs)
- break;
- }
- }
- return rs;
-}
-
-Statement *CompoundStatement::last()
-{
- Statement *s = NULL;
-
- for (size_t i = statements->length; i; --i)
- { s = (*statements)[i - 1];
- if (s)
- {
- s = s->last();
- if (s)
- break;
- }
- }
- return s;
-}
-
-/******************************** CompoundDeclarationStatement ***************************/
-
-CompoundDeclarationStatement::CompoundDeclarationStatement(Loc loc, Statements *s)
- : CompoundStatement(loc, s)
-{
- statements = s;
-}
-
-Statement *CompoundDeclarationStatement::syntaxCopy()
-{
- Statements *a = new Statements();
- a->setDim(statements->length);
- for (size_t i = 0; i < statements->length; i++)
- {
- Statement *s = (*statements)[i];
- (*a)[i] = s ? s->syntaxCopy() : NULL;
- }
- return new CompoundDeclarationStatement(loc, a);
-}
-
-/**************************** UnrolledLoopStatement ***************************/
-
-UnrolledLoopStatement::UnrolledLoopStatement(Loc loc, Statements *s)
- : Statement(loc)
-{
- statements = s;
-}
-
-Statement *UnrolledLoopStatement::syntaxCopy()
-{
- Statements *a = new Statements();
- a->setDim(statements->length);
- for (size_t i = 0; i < statements->length; i++)
- {
- Statement *s = (*statements)[i];
- (*a)[i] = s ? s->syntaxCopy() : NULL;
- }
- return new UnrolledLoopStatement(loc, a);
-}
-
-bool UnrolledLoopStatement::hasBreak()
-{
- return true;
-}
-
-bool UnrolledLoopStatement::hasContinue()
-{
- return true;
-}
-
-/******************************** ScopeStatement ***************************/
-
-ScopeStatement::ScopeStatement(Loc loc, Statement *s, Loc endloc)
- : Statement(loc)
-{
- this->statement = s;
- this->endloc = endloc;
-}
-
-Statement *ScopeStatement::syntaxCopy()
-{
- return new ScopeStatement(loc, statement ? statement->syntaxCopy() : NULL, endloc);
-}
-
-ReturnStatement *ScopeStatement::isReturnStatement()
-{
- if (statement)
- return statement->isReturnStatement();
- return NULL;
-}
-
-bool ScopeStatement::hasBreak()
-{
- //printf("ScopeStatement::hasBreak() %s\n", toChars());
- return statement ? statement->hasBreak() : false;
-}
-
-bool ScopeStatement::hasContinue()
-{
- return statement ? statement->hasContinue() : false;
-}
-
-/******************************** ForwardingStatement **********************/
-
-/* Statement whose symbol table contains foreach index variables in a
- * local scope and forwards other members to the parent scope. This
- * wraps a statement.
- *
- * Also see: `ddmd.attrib.ForwardingAttribDeclaration`
- */
-
-ForwardingStatement::ForwardingStatement(Loc loc, ForwardingScopeDsymbol *sym, Statement *s)
- : Statement(loc)
-{
- this->sym = sym;
- assert(s);
- this->statement = s;
-}
-
-ForwardingStatement::ForwardingStatement(Loc loc, Statement *s)
- : Statement(loc)
-{
- this->sym = new ForwardingScopeDsymbol(NULL);
- this->sym->symtab = new DsymbolTable();
- assert(s);
- this->statement = s;
-}
-
-Statement *ForwardingStatement::syntaxCopy()
-{
- return new ForwardingStatement(loc, statement->syntaxCopy());
-}
-
-/***********************
- * ForwardingStatements are distributed over the flattened
- * sequence of statements. This prevents flattening to be
- * "blocked" by a ForwardingStatement and is necessary, for
- * example, to support generating scope guards with `static
- * foreach`:
- *
- * static foreach(i; 0 .. 10) scope(exit) writeln(i);
- * writeln("this is printed first");
- * // then, it prints 10, 9, 8, 7, ...
- */
-
-Statements *ForwardingStatement::flatten(Scope *sc)
-{
- if (!statement)
- {
- return NULL;
- }
- sc = sc->push(sym);
- Statements *a = statement->flatten(sc);
- sc = sc->pop();
- if (!a)
- {
- return a;
- }
- Statements *b = new Statements();
- b->setDim(a->length);
- for (size_t i = 0; i < a->length; i++)
- {
- Statement *s = (*a)[i];
- (*b)[i] = s ? new ForwardingStatement(s->loc, sym, s) : NULL;
- }
- return b;
-}
-
-/******************************** WhileStatement ***************************/
-
-WhileStatement::WhileStatement(Loc loc, Expression *c, Statement *b, Loc endloc)
- : Statement(loc)
-{
- condition = c;
- _body = b;
- this->endloc = endloc;
-}
-
-Statement *WhileStatement::syntaxCopy()
-{
- return new WhileStatement(loc,
- condition->syntaxCopy(),
- _body ? _body->syntaxCopy() : NULL,
- endloc);
-}
-
-bool WhileStatement::hasBreak()
-{
- return true;
-}
-
-bool WhileStatement::hasContinue()
-{
- return true;
-}
-
-/******************************** DoStatement ***************************/
-
-DoStatement::DoStatement(Loc loc, Statement *b, Expression *c, Loc endloc)
- : Statement(loc)
-{
- _body = b;
- condition = c;
- this->endloc = endloc;
-}
-
-Statement *DoStatement::syntaxCopy()
-{
- return new DoStatement(loc,
- _body ? _body->syntaxCopy() : NULL,
- condition->syntaxCopy(),
- endloc);
-}
-
-bool DoStatement::hasBreak()
-{
- return true;
-}
-
-bool DoStatement::hasContinue()
-{
- return true;
-}
-
-/******************************** ForStatement ***************************/
-
-ForStatement::ForStatement(Loc loc, Statement *init, Expression *condition, Expression *increment, Statement *body, Loc endloc)
- : Statement(loc)
-{
- this->_init = init;
- this->condition = condition;
- this->increment = increment;
- this->_body = body;
- this->endloc = endloc;
- this->relatedLabeled = NULL;
-}
-
-Statement *ForStatement::syntaxCopy()
-{
- return new ForStatement(loc,
- _init ? _init->syntaxCopy() : NULL,
- condition ? condition->syntaxCopy() : NULL,
- increment ? increment->syntaxCopy() : NULL,
- _body->syntaxCopy(),
- endloc);
-}
-
-Statement *ForStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally)
-{
- //printf("ForStatement::scopeCode()\n");
- Statement::scopeCode(sc, sentry, sexception, sfinally);
- return this;
-}
-
-bool ForStatement::hasBreak()
-{
- //printf("ForStatement::hasBreak()\n");
- return true;
-}
-
-bool ForStatement::hasContinue()
-{
- return true;
-}
-
-/******************************** ForeachStatement ***************************/
-
-ForeachStatement::ForeachStatement(Loc loc, TOK op, Parameters *parameters,
- Expression *aggr, Statement *body, Loc endloc)
- : Statement(loc)
-{
- this->op = op;
- this->parameters = parameters;
- this->aggr = aggr;
- this->_body = body;
- this->endloc = endloc;
-
- this->key = NULL;
- this->value = NULL;
-
- this->func = NULL;
-
- this->cases = NULL;
- this->gotos = NULL;
-}
-
-Statement *ForeachStatement::syntaxCopy()
-{
- return new ForeachStatement(loc, op,
- Parameter::arraySyntaxCopy(parameters),
- aggr->syntaxCopy(),
- _body ? _body->syntaxCopy() : NULL,
- endloc);
-}
-
-bool ForeachStatement::checkForArgTypes()
-{
- bool result = false;
-
- for (size_t i = 0; i < parameters->length; i++)
- {
- Parameter *p = (*parameters)[i];
- if (!p->type)
- {
- error("cannot infer type for %s", p->ident->toChars());
- p->type = Type::terror;
- result = true;
- }
- }
- return result;
-}
-
-bool ForeachStatement::hasBreak()
-{
- return true;
-}
-
-bool ForeachStatement::hasContinue()
-{
- return true;
-}
-
-/**************************** ForeachRangeStatement ***************************/
-
-
-ForeachRangeStatement::ForeachRangeStatement(Loc loc, TOK op, Parameter *prm,
- Expression *lwr, Expression *upr, Statement *body, Loc endloc)
- : Statement(loc)
-{
- this->op = op;
- this->prm = prm;
- this->lwr = lwr;
- this->upr = upr;
- this->_body = body;
- this->endloc = endloc;
-
- this->key = NULL;
-}
-
-Statement *ForeachRangeStatement::syntaxCopy()
-{
- return new ForeachRangeStatement(loc, op,
- prm->syntaxCopy(),
- lwr->syntaxCopy(),
- upr->syntaxCopy(),
- _body ? _body->syntaxCopy() : NULL,
- endloc);
-}
-
-bool ForeachRangeStatement::hasBreak()
-{
- return true;
-}
-
-bool ForeachRangeStatement::hasContinue()
-{
- return true;
-}
-
-/******************************** IfStatement ***************************/
-
-IfStatement::IfStatement(Loc loc, Parameter *prm, Expression *condition, Statement *ifbody, Statement *elsebody, Loc endloc)
- : Statement(loc)
-{
- this->prm = prm;
- this->condition = condition;
- this->ifbody = ifbody;
- this->elsebody = elsebody;
- this->endloc = endloc;
- this->match = NULL;
-}
-
-Statement *IfStatement::syntaxCopy()
-{
- return new IfStatement(loc,
- prm ? prm->syntaxCopy() : NULL,
- condition->syntaxCopy(),
- ifbody ? ifbody->syntaxCopy() : NULL,
- elsebody ? elsebody->syntaxCopy() : NULL,
- endloc);
-}
-
-/******************************** ConditionalStatement ***************************/
-
-ConditionalStatement::ConditionalStatement(Loc loc, Condition *condition, Statement *ifbody, Statement *elsebody)
- : Statement(loc)
-{
- this->condition = condition;
- this->ifbody = ifbody;
- this->elsebody = elsebody;
-}
-
-Statement *ConditionalStatement::syntaxCopy()
-{
- return new ConditionalStatement(loc,
- condition->syntaxCopy(),
- ifbody->syntaxCopy(),
- elsebody ? elsebody->syntaxCopy() : NULL);
-}
-
-Statements *ConditionalStatement::flatten(Scope *sc)
-{
- Statement *s;
-
- //printf("ConditionalStatement::flatten()\n");
- if (condition->include(sc))
- {
- DebugCondition *dc = condition->isDebugCondition();
- if (dc)
- s = new DebugStatement(loc, ifbody);
- else
- s = ifbody;
- }
- else
- s = elsebody;
-
- Statements *a = new Statements();
- a->push(s);
- return a;
-}
-
-/******************************** StaticForeachStatement ********************/
-
-/* Static foreach statements, like:
- * void main()
- * {
- * static foreach(i; 0 .. 10)
- * {
- * pragma(msg, i);
- * }
- * }
- */
-
-StaticForeachStatement::StaticForeachStatement(Loc loc, StaticForeach *sfe)
- : Statement(loc)
-{
- this->sfe = sfe;
-}
-
-Statement *StaticForeachStatement::syntaxCopy()
-{
- return new StaticForeachStatement(loc, sfe->syntaxCopy());
-}
-
-Statements *StaticForeachStatement::flatten(Scope *sc)
-{
- staticForeachPrepare(sfe, sc);
- if (staticForeachReady(sfe))
- {
- Statement *s = makeTupleForeachStatic(sc, sfe->aggrfe, sfe->needExpansion);
- Statements *result = s->flatten(sc);
- if (result)
- {
- return result;
- }
- result = new Statements();
- result->push(s);
- return result;
- }
- else
- {
- Statements *result = new Statements();
- result->push(new ErrorStatement());
- return result;
- }
-}
-
-/******************************** PragmaStatement ***************************/
-
-PragmaStatement::PragmaStatement(Loc loc, Identifier *ident, Expressions *args, Statement *body)
- : Statement(loc)
-{
- this->ident = ident;
- this->args = args;
- this->_body = body;
-}
-
-Statement *PragmaStatement::syntaxCopy()
-{
- return new PragmaStatement(loc, ident,
- Expression::arraySyntaxCopy(args),
- _body ? _body->syntaxCopy() : NULL);
-}
-
-/******************************** StaticAssertStatement ***************************/
-
-StaticAssertStatement::StaticAssertStatement(StaticAssert *sa)
- : Statement(sa->loc)
-{
- this->sa = sa;
-}
-
-Statement *StaticAssertStatement::syntaxCopy()
-{
- return new StaticAssertStatement((StaticAssert *)sa->syntaxCopy(NULL));
-}
-
-/******************************** SwitchStatement ***************************/
-
-SwitchStatement::SwitchStatement(Loc loc, Expression *c, Statement *b, bool isFinal)
- : Statement(loc)
-{
- this->condition = c;
- this->_body = b;
- this->isFinal = isFinal;
- sdefault = NULL;
- tf = NULL;
- cases = NULL;
- hasNoDefault = 0;
- hasVars = 0;
- lastVar = NULL;
-}
-
-Statement *SwitchStatement::syntaxCopy()
-{
- return new SwitchStatement(loc,
- condition->syntaxCopy(),
- _body->syntaxCopy(),
- isFinal);
-}
-
-bool SwitchStatement::hasBreak()
-{
- return true;
-}
-
-static bool checkVar(SwitchStatement *s, VarDeclaration *vd)
-{
- if (!vd || vd->isDataseg() || (vd->storage_class & STCmanifest))
- return false;
-
- VarDeclaration *last = s->lastVar;
- while (last && last != vd)
- last = last->lastVar;
- if (last == vd)
- {
- // All good, the label's scope has no variables
- }
- else if (vd->storage_class & STCexptemp)
- {
- // Lifetime ends at end of expression, so no issue with skipping the statement
- }
- else if (vd->ident == Id::withSym)
- {
- s->deprecation("`switch` skips declaration of `with` temporary at %s", vd->loc.toChars());
- return true;
- }
- else
- {
- s->deprecation("`switch` skips declaration of variable %s at %s", vd->toPrettyChars(), vd->loc.toChars());
- return true;
- }
-
- return false;
-}
-
-bool SwitchStatement::checkLabel()
-{
- const bool error = true;
-
- if (sdefault && checkVar(this, sdefault->lastVar))
- return !error; // return error once fully deprecated
-
- for (size_t i = 0; i < cases->length; i++)
- {
- CaseStatement *scase = (*cases)[i];
- if (scase && checkVar(this, scase->lastVar))
- return !error; // return error once fully deprecated
- }
- return !error;
-}
-
-/******************************** CaseStatement ***************************/
-
-CaseStatement::CaseStatement(Loc loc, Expression *exp, Statement *s)
- : Statement(loc)
-{
- this->exp = exp;
- this->statement = s;
- index = 0;
- lastVar = NULL;
-}
-
-Statement *CaseStatement::syntaxCopy()
-{
- return new CaseStatement(loc,
- exp->syntaxCopy(),
- statement->syntaxCopy());
-}
-
-int CaseStatement::compare(RootObject *obj)
-{
- // Sort cases so we can do an efficient lookup
- CaseStatement *cs2 = (CaseStatement *)(obj);
-
- return exp->compare(cs2->exp);
-}
-
-/******************************** CaseRangeStatement ***************************/
-
-
-CaseRangeStatement::CaseRangeStatement(Loc loc, Expression *first,
- Expression *last, Statement *s)
- : Statement(loc)
-{
- this->first = first;
- this->last = last;
- this->statement = s;
-}
-
-Statement *CaseRangeStatement::syntaxCopy()
-{
- return new CaseRangeStatement(loc,
- first->syntaxCopy(),
- last->syntaxCopy(),
- statement->syntaxCopy());
-}
-
-/******************************** DefaultStatement ***************************/
-
-DefaultStatement::DefaultStatement(Loc loc, Statement *s)
- : Statement(loc)
-{
- this->statement = s;
- this->lastVar = NULL;
-}
-
-Statement *DefaultStatement::syntaxCopy()
-{
- return new DefaultStatement(loc, statement->syntaxCopy());
-}
-
-/******************************** GotoDefaultStatement ***************************/
-
-GotoDefaultStatement::GotoDefaultStatement(Loc loc)
- : Statement(loc)
-{
- sw = NULL;
-}
-
-Statement *GotoDefaultStatement::syntaxCopy()
-{
- return new GotoDefaultStatement(loc);
-}
-
-/******************************** GotoCaseStatement ***************************/
-
-GotoCaseStatement::GotoCaseStatement(Loc loc, Expression *exp)
- : Statement(loc)
-{
- cs = NULL;
- this->exp = exp;
-}
-
-Statement *GotoCaseStatement::syntaxCopy()
-{
- return new GotoCaseStatement(loc, exp ? exp->syntaxCopy() : NULL);
-}
-
-/******************************** SwitchErrorStatement ***************************/
-
-SwitchErrorStatement::SwitchErrorStatement(Loc loc)
- : Statement(loc)
-{
-}
-
-/******************************** ReturnStatement ***************************/
-
-ReturnStatement::ReturnStatement(Loc loc, Expression *exp)
- : Statement(loc)
-{
- this->exp = exp;
- this->caseDim = 0;
-}
-
-Statement *ReturnStatement::syntaxCopy()
-{
- return new ReturnStatement(loc, exp ? exp->syntaxCopy() : NULL);
-}
-
-/******************************** BreakStatement ***************************/
-
-BreakStatement::BreakStatement(Loc loc, Identifier *ident)
- : Statement(loc)
-{
- this->ident = ident;
-}
-
-Statement *BreakStatement::syntaxCopy()
-{
- return new BreakStatement(loc, ident);
-}
-
-/******************************** ContinueStatement ***************************/
-
-ContinueStatement::ContinueStatement(Loc loc, Identifier *ident)
- : Statement(loc)
-{
- this->ident = ident;
-}
-
-Statement *ContinueStatement::syntaxCopy()
-{
- return new ContinueStatement(loc, ident);
-}
-
-/******************************** SynchronizedStatement ***************************/
-
-SynchronizedStatement::SynchronizedStatement(Loc loc, Expression *exp, Statement *body)
- : Statement(loc)
-{
- this->exp = exp;
- this->_body = body;
-}
-
-Statement *SynchronizedStatement::syntaxCopy()
-{
- return new SynchronizedStatement(loc,
- exp ? exp->syntaxCopy() : NULL,
- _body ? _body->syntaxCopy() : NULL);
-}
-
-bool SynchronizedStatement::hasBreak()
-{
- return false; //true;
-}
-
-bool SynchronizedStatement::hasContinue()
-{
- return false; //true;
-}
-
-/******************************** WithStatement ***************************/
-
-WithStatement::WithStatement(Loc loc, Expression *exp, Statement *body, Loc endloc)
- : Statement(loc)
-{
- this->exp = exp;
- this->_body = body;
- this->endloc = endloc;
- wthis = NULL;
-}
-
-Statement *WithStatement::syntaxCopy()
-{
- return new WithStatement(loc,
- exp->syntaxCopy(),
- _body ? _body->syntaxCopy() : NULL, endloc);
-}
-
-/******************************** TryCatchStatement ***************************/
-
-TryCatchStatement::TryCatchStatement(Loc loc, Statement *body, Catches *catches)
- : Statement(loc)
-{
- this->_body = body;
- this->catches = catches;
-}
-
-Statement *TryCatchStatement::syntaxCopy()
-{
- Catches *a = new Catches();
- a->setDim(catches->length);
- for (size_t i = 0; i < a->length; i++)
- {
- Catch *c = (*catches)[i];
- (*a)[i] = c->syntaxCopy();
- }
- return new TryCatchStatement(loc, _body->syntaxCopy(), a);
-}
-
-bool TryCatchStatement::hasBreak()
-{
- return false;
-}
-
-/******************************** Catch ***************************/
-
-Catch::Catch(Loc loc, Type *t, Identifier *id, Statement *handler)
-{
- //printf("Catch(%s, loc = %s)\n", id->toChars(), loc.toChars());
- this->loc = loc;
- this->type = t;
- this->ident = id;
- this->handler = handler;
- var = NULL;
- errors = false;
- internalCatch = false;
-}
-
-Catch *Catch::syntaxCopy()
-{
- Catch *c = new Catch(loc,
- type ? type->syntaxCopy() : getThrowable(),
- ident,
- (handler ? handler->syntaxCopy() : NULL));
- c->internalCatch = internalCatch;
- return c;
-}
-
-/****************************** TryFinallyStatement ***************************/
-
-TryFinallyStatement::TryFinallyStatement(Loc loc, Statement *body, Statement *finalbody)
- : Statement(loc)
-{
- this->_body = body;
- this->finalbody = finalbody;
-}
-
-TryFinallyStatement *TryFinallyStatement::create(Loc loc, Statement *body, Statement *finalbody)
-{
- return new TryFinallyStatement(loc, body, finalbody);
-}
-
-Statement *TryFinallyStatement::syntaxCopy()
-{
- return new TryFinallyStatement(loc,
- _body->syntaxCopy(), finalbody->syntaxCopy());
-}
-
-bool TryFinallyStatement::hasBreak()
-{
- return false; //true;
-}
-
-bool TryFinallyStatement::hasContinue()
-{
- return false; //true;
-}
-
-/****************************** ScopeGuardStatement ***************************/
-
-ScopeGuardStatement::ScopeGuardStatement(Loc loc, TOK tok, Statement *statement)
- : Statement(loc)
-{
- this->tok = tok;
- this->statement = statement;
-}
-
-Statement *ScopeGuardStatement::syntaxCopy()
-{
- return new ScopeGuardStatement(loc, tok, statement->syntaxCopy());
-}
-
-Statement *ScopeGuardStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally)
-{
- //printf("ScopeGuardStatement::scopeCode()\n");
- //print();
- *sentry = NULL;
- *sexception = NULL;
- *sfinally = NULL;
-
- Statement *s = new PeelStatement(statement);
-
- switch (tok)
- {
- case TOKon_scope_exit:
- *sfinally = s;
- break;
-
- case TOKon_scope_failure:
- *sexception = s;
- break;
-
- case TOKon_scope_success:
- {
- /* Create:
- * sentry: bool x = false;
- * sexception: x = true;
- * sfinally: if (!x) statement;
- */
- VarDeclaration *v = copyToTemp(0, "__os", new IntegerExp(Loc(), 0, Type::tbool));
- dsymbolSemantic(v, sc);
- *sentry = new ExpStatement(loc, v);
-
- Expression *e = new IntegerExp(Loc(), 1, Type::tbool);
- e = new AssignExp(Loc(), new VarExp(Loc(), v), e);
- *sexception = new ExpStatement(Loc(), e);
-
- e = new VarExp(Loc(), v);
- e = new NotExp(Loc(), e);
- *sfinally = new IfStatement(Loc(), NULL, e, s, NULL, Loc());
-
- break;
- }
-
- default:
- assert(0);
- }
- return NULL;
-}
-
-/******************************** ThrowStatement ***************************/
-
-ThrowStatement::ThrowStatement(Loc loc, Expression *exp)
- : Statement(loc)
-{
- this->exp = exp;
- this->internalThrow = false;
-}
-
-Statement *ThrowStatement::syntaxCopy()
-{
- ThrowStatement *s = new ThrowStatement(loc, exp->syntaxCopy());
- s->internalThrow = internalThrow;
- return s;
-}
-
-/******************************** DebugStatement **************************/
-
-DebugStatement::DebugStatement(Loc loc, Statement *statement)
- : Statement(loc)
-{
- this->statement = statement;
-}
-
-Statement *DebugStatement::syntaxCopy()
-{
- return new DebugStatement(loc,
- statement ? statement->syntaxCopy() : NULL);
-}
-
-Statements *DebugStatement::flatten(Scope *sc)
-{
- Statements *a = statement ? statement->flatten(sc) : NULL;
- if (a)
- {
- for (size_t i = 0; i < a->length; i++)
- { Statement *s = (*a)[i];
-
- s = new DebugStatement(loc, s);
- (*a)[i] = s;
- }
- }
-
- return a;
-}
-
-/******************************** GotoStatement ***************************/
-
-GotoStatement::GotoStatement(Loc loc, Identifier *ident)
- : Statement(loc)
-{
- this->ident = ident;
- this->label = NULL;
- this->tf = NULL;
- this->os = NULL;
- this->lastVar = NULL;
-}
-
-Statement *GotoStatement::syntaxCopy()
-{
- return new GotoStatement(loc, ident);
-}
-
-bool GotoStatement::checkLabel()
-{
- if (!label->statement)
- {
- error("label `%s` is undefined", label->toChars());
- return true;
- }
-
- if (label->statement->os != os)
- {
- if (os && os->tok == TOKon_scope_failure && !label->statement->os)
- {
- // Jump out from scope(failure) block is allowed.
- }
- else
- {
- if (label->statement->os)
- error("cannot goto in to %s block", Token::toChars(label->statement->os->tok));
- else
- error("cannot goto out of %s block", Token::toChars(os->tok));
- return true;
- }
- }
-
- if (label->statement->tf != tf)
- {
- error("cannot goto in or out of finally block");
- return true;
- }
-
- VarDeclaration *vd = label->statement->lastVar;
- if (!vd || vd->isDataseg() || (vd->storage_class & STCmanifest))
- return false;
-
- VarDeclaration *last = lastVar;
- while (last && last != vd)
- last = last->lastVar;
- if (last == vd)
- {
- // All good, the label's scope has no variables
- }
- else if (vd->ident == Id::withSym)
- {
- error("goto skips declaration of with temporary at %s", vd->loc.toChars());
- return true;
- }
- else
- {
- error("goto skips declaration of variable %s at %s", vd->toPrettyChars(), vd->loc.toChars());
- return true;
- }
-
- return false;
-}
-
-/******************************** LabelStatement ***************************/
-
-LabelStatement::LabelStatement(Loc loc, Identifier *ident, Statement *statement)
- : Statement(loc)
-{
- this->ident = ident;
- this->statement = statement;
- this->tf = NULL;
- this->os = NULL;
- this->lastVar = NULL;
- this->gotoTarget = NULL;
- this->breaks = false;
-}
-
-Statement *LabelStatement::syntaxCopy()
-{
- return new LabelStatement(loc, ident, statement ? statement->syntaxCopy() : NULL);
-}
-
-Statement *LabelStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally)
-{
- //printf("LabelStatement::scopeCode()\n");
- if (statement)
- statement = statement->scopeCode(sc, sentry, sexit, sfinally);
- else
- {
- *sentry = NULL;
- *sexit = NULL;
- *sfinally = NULL;
- }
- return this;
-}
-
-Statements *LabelStatement::flatten(Scope *sc)
-{
- Statements *a = NULL;
-
- if (statement)
- {
- a = statement->flatten(sc);
- if (a)
- {
- if (!a->length)
- {
- a->push(new ExpStatement(loc, (Expression *)NULL));
- }
-
- // reuse 'this' LabelStatement
- this->statement = (*a)[0];
- (*a)[0] = this;
- }
- }
-
- return a;
-}
-
-/******************************** LabelDsymbol ***************************/
-
-LabelDsymbol::LabelDsymbol(Identifier *ident)
- : Dsymbol(ident)
-{
- statement = NULL;
-}
-
-LabelDsymbol *LabelDsymbol::create(Identifier *ident)
-{
- return new LabelDsymbol(ident);
-}
-
-LabelDsymbol *LabelDsymbol::isLabel() // is this a LabelDsymbol()?
-{
- return this;
-}
-
-
-/************************ AsmStatement ***************************************/
-
-AsmStatement::AsmStatement(Loc loc, Token *tokens)
- : Statement(loc)
-{
- this->tokens = tokens;
-}
-
-Statement *AsmStatement::syntaxCopy()
-{
- return new AsmStatement(loc, tokens);
-}
-
-
-/************************ InlineAsmStatement **********************************/
-
-InlineAsmStatement::InlineAsmStatement(Loc loc, Token *tokens)
- : AsmStatement(loc, tokens)
-{
- asmcode = NULL;
- asmalign = 0;
- refparam = false;
- naked = false;
- regs = 0;
-}
-
-Statement *InlineAsmStatement::syntaxCopy()
-{
- return new InlineAsmStatement(loc, tokens);
-}
-
-
-/************************ GccAsmStatement ***************************************/
-
-GccAsmStatement::GccAsmStatement(Loc loc, Token *tokens)
- : AsmStatement(loc, tokens)
-{
- this->stc = STCundefined;
- this->insn = NULL;
- this->args = NULL;
- this->outputargs = 0;
- this->names = NULL;
- this->constraints = NULL;
- this->clobbers = NULL;
- this->labels = NULL;
- this->gotos = NULL;
-}
-
-Statement *GccAsmStatement::syntaxCopy()
-{
- return new GccAsmStatement(loc, tokens);
-}
-
-/************************ CompoundAsmStatement ***************************************/
-
-CompoundAsmStatement::CompoundAsmStatement(Loc loc, Statements *s, StorageClass stc)
- : CompoundStatement(loc, s)
-{
- this->stc = stc;
-}
-
-CompoundAsmStatement *CompoundAsmStatement::syntaxCopy()
-{
- Statements *a = new Statements();
- a->setDim(statements->length);
- for (size_t i = 0; i < statements->length; i++)
- {
- Statement *s = (*statements)[i];
- (*a)[i] = s ? s->syntaxCopy() : NULL;
- }
- return new CompoundAsmStatement(loc, a, stc);
-}
-
-Statements *CompoundAsmStatement::flatten(Scope *)
-{
- return NULL;
-}
-
-/************************ ImportStatement ***************************************/
-
-ImportStatement::ImportStatement(Loc loc, Dsymbols *imports)
- : Statement(loc)
-{
- this->imports = imports;
-}
-
-Statement *ImportStatement::syntaxCopy()
-{
- Dsymbols *m = new Dsymbols();
- m->setDim(imports->length);
- for (size_t i = 0; i < imports->length; i++)
- {
- Dsymbol *s = (*imports)[i];
- (*m)[i] = s->syntaxCopy(NULL);
- }
- return new ImportStatement(loc, m);
-}
diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d
new file mode 100644
index 00000000000..b49c9035d3f
--- /dev/null
+++ b/gcc/d/dmd/statement.d
@@ -0,0 +1,2053 @@
+/**
+ * Defines AST nodes for statements.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statement.d, _statement.d)
+ * Documentation: https://dlang.org/phobos/dmd_statement.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statement.d
+ */
+
+module dmd.statement;
+
+import core.stdc.stdarg;
+import core.stdc.stdio;
+
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.ast_node;
+import dmd.gluelayer;
+import dmd.canthrow;
+import dmd.cond;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.dinterpret;
+import dmd.mtype;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.sapply;
+import dmd.sideeffect;
+import dmd.staticassert;
+import dmd.tokens;
+import dmd.visitor;
+
+/**
+ * Returns:
+ * `TypeIdentifier` corresponding to `object.Throwable`
+ */
+TypeIdentifier getThrowable()
+{
+ auto tid = new TypeIdentifier(Loc.initial, Id.empty);
+ tid.addIdent(Id.object);
+ tid.addIdent(Id.Throwable);
+ return tid;
+}
+
+/**
+ * Returns:
+ * TypeIdentifier corresponding to `object.Exception`
+ */
+TypeIdentifier getException()
+{
+ auto tid = new TypeIdentifier(Loc.initial, Id.empty);
+ tid.addIdent(Id.object);
+ tid.addIdent(Id.Exception);
+ return tid;
+}
+
+/***********************************************************
+ * Specification: http://dlang.org/spec/statement.html
+ */
+extern (C++) abstract class Statement : ASTNode
+{
+ const Loc loc;
+ const STMT stmt;
+
+ override final DYNCAST dyncast() const
+ {
+ return DYNCAST.statement;
+ }
+
+ final extern (D) this(const ref Loc loc, STMT stmt)
+ {
+ this.loc = loc;
+ this.stmt = stmt;
+ // If this is an in{} contract scope statement (skip for determining
+ // inlineStatus of a function body for header content)
+ }
+
+ Statement syntaxCopy()
+ {
+ assert(0);
+ }
+
+ /*************************************
+ * Do syntax copy of an array of Statement's.
+ */
+ static Statements* arraySyntaxCopy(Statements* a)
+ {
+ Statements* b = null;
+ if (a)
+ {
+ b = a.copy();
+ foreach (i, s; *a)
+ {
+ (*b)[i] = s ? s.syntaxCopy() : null;
+ }
+ }
+ return b;
+ }
+
+ override final const(char)* toChars() const
+ {
+ HdrGenState hgs;
+ OutBuffer buf;
+ .toCBuffer(this, &buf, &hgs);
+ buf.writeByte(0);
+ return buf.extractSlice().ptr;
+ }
+
+ static if (__VERSION__ < 2092)
+ {
+ final void error(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .verror(loc, format, ap);
+ va_end(ap);
+ }
+
+ final void warning(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vwarning(loc, format, ap);
+ va_end(ap);
+ }
+
+ final void deprecation(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vdeprecation(loc, format, ap);
+ va_end(ap);
+ }
+ }
+ else
+ {
+ pragma(printf) final void error(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .verror(loc, format, ap);
+ va_end(ap);
+ }
+
+ pragma(printf) final void warning(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vwarning(loc, format, ap);
+ va_end(ap);
+ }
+
+ pragma(printf) final void deprecation(const(char)* format, ...)
+ {
+ va_list ap;
+ va_start(ap, format);
+ .vdeprecation(loc, format, ap);
+ va_end(ap);
+ }
+ }
+
+ Statement getRelatedLabeled()
+ {
+ return this;
+ }
+
+ /****************************
+ * Determine if an enclosed `break` would apply to this
+ * statement, such as if it is a loop or switch statement.
+ * Returns:
+ * `true` if it does
+ */
+ bool hasBreak() const pure nothrow
+ {
+ //printf("Statement::hasBreak()\n");
+ return false;
+ }
+
+ /****************************
+ * Determine if an enclosed `continue` would apply to this
+ * statement, such as if it is a loop statement.
+ * Returns:
+ * `true` if it does
+ */
+ bool hasContinue() const pure nothrow
+ {
+ return false;
+ }
+
+ /**********************************
+ * Returns:
+ * `true` if statement uses exception handling
+ */
+ final bool usesEH()
+ {
+ extern (C++) final class UsesEH : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ public:
+ override void visit(Statement s)
+ {
+ }
+
+ override void visit(TryCatchStatement s)
+ {
+ stop = true;
+ }
+
+ override void visit(TryFinallyStatement s)
+ {
+ stop = true;
+ }
+
+ override void visit(ScopeGuardStatement s)
+ {
+ stop = true;
+ }
+
+ override void visit(SynchronizedStatement s)
+ {
+ stop = true;
+ }
+ }
+
+ scope UsesEH ueh = new UsesEH();
+ return walkPostorder(this, ueh);
+ }
+
+ /**********************************
+ * Returns:
+ * `true` if statement 'comes from' somewhere else, like a goto
+ */
+ final bool comeFrom()
+ {
+ extern (C++) final class ComeFrom : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ public:
+ override void visit(Statement s)
+ {
+ }
+
+ override void visit(CaseStatement s)
+ {
+ stop = true;
+ }
+
+ override void visit(DefaultStatement s)
+ {
+ stop = true;
+ }
+
+ override void visit(LabelStatement s)
+ {
+ stop = true;
+ }
+
+ override void visit(AsmStatement s)
+ {
+ stop = true;
+ }
+ }
+
+ scope ComeFrom cf = new ComeFrom();
+ return walkPostorder(this, cf);
+ }
+
+ /**********************************
+ * Returns:
+ * `true` if statement has executable code.
+ */
+ final bool hasCode()
+ {
+ extern (C++) final class HasCode : StoppableVisitor
+ {
+ alias visit = typeof(super).visit;
+ public:
+ override void visit(Statement s)
+ {
+ stop = true;
+ }
+
+ override void visit(ExpStatement s)
+ {
+ if (s.exp !is null)
+ {
+ stop = s.exp.hasCode();
+ }
+ }
+
+ override void visit(CompoundStatement s)
+ {
+ }
+
+ override void visit(ScopeStatement s)
+ {
+ }
+
+ override void visit(ImportStatement s)
+ {
+ }
+ }
+
+ scope HasCode hc = new HasCode();
+ return walkPostorder(this, hc);
+ }
+
+ /*******************************
+ * Find last statement in a sequence of statements.
+ * Returns:
+ * the last statement, or `null` if there isn't one
+ */
+ inout(Statement) last() inout nothrow pure
+ {
+ return this;
+ }
+
+ /**************************
+ * Support Visitor Pattern
+ * Params:
+ * v = visitor
+ */
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+
+ /************************************
+ * Does this statement end with a return statement?
+ *
+ * I.e. is it a single return statement or some compound statement
+ * that unconditionally hits a return statement.
+ * Returns:
+ * return statement it ends with, otherwise null
+ */
+ pure nothrow @nogc
+ inout(ReturnStatement) endsWithReturnStatement() inout { return null; }
+
+ final pure inout nothrow @nogc @safe:
+
+ /********************
+ * A cheaper method of doing downcasting of Statements.
+ * Returns:
+ * the downcast statement if it can be downcasted, otherwise `null`
+ */
+ inout(ErrorStatement) isErrorStatement() { return stmt == STMT.Error ? cast(typeof(return))this : null; }
+ inout(ScopeStatement) isScopeStatement() { return stmt == STMT.Scope ? cast(typeof(return))this : null; }
+ inout(ExpStatement) isExpStatement() { return stmt == STMT.Exp ? cast(typeof(return))this : null; }
+ inout(CompoundStatement) isCompoundStatement() { return stmt == STMT.Compound ? cast(typeof(return))this : null; }
+ inout(ReturnStatement) isReturnStatement() { return stmt == STMT.Return ? cast(typeof(return))this : null; }
+ inout(IfStatement) isIfStatement() { return stmt == STMT.If ? cast(typeof(return))this : null; }
+ inout(ConditionalStatement) isConditionalStatement() { return stmt == STMT.Conditional ? cast(typeof(return))this : null; }
+ inout(StaticForeachStatement) isStaticForeachStatement() { return stmt == STMT.StaticForeach ? cast(typeof(return))this : null; }
+ inout(CaseStatement) isCaseStatement() { return stmt == STMT.Case ? cast(typeof(return))this : null; }
+ inout(DefaultStatement) isDefaultStatement() { return stmt == STMT.Default ? cast(typeof(return))this : null; }
+ inout(LabelStatement) isLabelStatement() { return stmt == STMT.Label ? cast(typeof(return))this : null; }
+ inout(GotoStatement) isGotoStatement() { return stmt == STMT.Goto ? cast(typeof(return))this : null; }
+ inout(GotoDefaultStatement) isGotoDefaultStatement() { return stmt == STMT.GotoDefault ? cast(typeof(return))this : null; }
+ inout(GotoCaseStatement) isGotoCaseStatement() { return stmt == STMT.GotoCase ? cast(typeof(return))this : null; }
+ inout(BreakStatement) isBreakStatement() { return stmt == STMT.Break ? cast(typeof(return))this : null; }
+ inout(DtorExpStatement) isDtorExpStatement() { return stmt == STMT.DtorExp ? cast(typeof(return))this : null; }
+ inout(CompileStatement) isCompileStatement() { return stmt == STMT.Compile ? cast(typeof(return))this : null; }
+ inout(ForwardingStatement) isForwardingStatement() { return stmt == STMT.Forwarding ? cast(typeof(return))this : null; }
+ inout(DoStatement) isDoStatement() { return stmt == STMT.Do ? cast(typeof(return))this : null; }
+ inout(WhileStatement) isWhileStatement() { return stmt == STMT.While ? cast(typeof(return))this : null; }
+ inout(ForStatement) isForStatement() { return stmt == STMT.For ? cast(typeof(return))this : null; }
+ inout(ForeachStatement) isForeachStatement() { return stmt == STMT.Foreach ? cast(typeof(return))this : null; }
+ inout(SwitchStatement) isSwitchStatement() { return stmt == STMT.Switch ? cast(typeof(return))this : null; }
+ inout(ContinueStatement) isContinueStatement() { return stmt == STMT.Continue ? cast(typeof(return))this : null; }
+ inout(WithStatement) isWithStatement() { return stmt == STMT.With ? cast(typeof(return))this : null; }
+ inout(TryCatchStatement) isTryCatchStatement() { return stmt == STMT.TryCatch ? cast(typeof(return))this : null; }
+ inout(ThrowStatement) isThrowStatement() { return stmt == STMT.Throw ? cast(typeof(return))this : null; }
+ inout(DebugStatement) isDebugStatement() { return stmt == STMT.Debug ? cast(typeof(return))this : null; }
+ inout(TryFinallyStatement) isTryFinallyStatement() { return stmt == STMT.TryFinally ? cast(typeof(return))this : null; }
+ inout(ScopeGuardStatement) isScopeGuardStatement() { return stmt == STMT.ScopeGuard ? cast(typeof(return))this : null; }
+ inout(SwitchErrorStatement) isSwitchErrorStatement() { return stmt == STMT.SwitchError ? cast(typeof(return))this : null; }
+ inout(UnrolledLoopStatement) isUnrolledLoopStatement() { return stmt == STMT.UnrolledLoop ? cast(typeof(return))this : null; }
+ inout(ForeachRangeStatement) isForeachRangeStatement() { return stmt == STMT.ForeachRange ? cast(typeof(return))this : null; }
+ inout(CompoundDeclarationStatement) isCompoundDeclarationStatement() { return stmt == STMT.CompoundDeclaration ? cast(typeof(return))this : null; }
+}
+
+/***********************************************************
+ * Any Statement that fails semantic() or has a component that is an ErrorExp or
+ * a TypeError should return an ErrorStatement from semantic().
+ */
+extern (C++) final class ErrorStatement : Statement
+{
+ extern (D) this()
+ {
+ super(Loc.initial, STMT.Error);
+ assert(global.gaggedErrors || global.errors);
+ }
+
+ override ErrorStatement syntaxCopy()
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class PeelStatement : Statement
+{
+ Statement s;
+
+ extern (D) this(Statement s)
+ {
+ super(s.loc, STMT.Peel);
+ this.s = s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#ExpressionStatement
+ */
+extern (C++) class ExpStatement : Statement
+{
+ Expression exp;
+
+ final extern (D) this(const ref Loc loc, Expression exp)
+ {
+ super(loc, STMT.Exp);
+ this.exp = exp;
+ }
+
+ final extern (D) this(const ref Loc loc, Expression exp, STMT stmt)
+ {
+ super(loc, stmt);
+ this.exp = exp;
+ }
+
+ final extern (D) this(const ref Loc loc, Dsymbol declaration)
+ {
+ super(loc, STMT.Exp);
+ this.exp = new DeclarationExp(loc, declaration);
+ }
+
+ static ExpStatement create(Loc loc, Expression exp)
+ {
+ return new ExpStatement(loc, exp);
+ }
+
+ override ExpStatement syntaxCopy()
+ {
+ return new ExpStatement(loc, exp ? exp.syntaxCopy() : null);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DtorExpStatement : ExpStatement
+{
+ // Wraps an expression that is the destruction of 'var'
+ VarDeclaration var;
+
+ extern (D) this(const ref Loc loc, Expression exp, VarDeclaration var)
+ {
+ super(loc, exp, STMT.DtorExp);
+ this.var = var;
+ }
+
+ override DtorExpStatement syntaxCopy()
+ {
+ return new DtorExpStatement(loc, exp ? exp.syntaxCopy() : null, var);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#mixin-statement
+ */
+extern (C++) final class CompileStatement : Statement
+{
+ Expressions* exps;
+
+ extern (D) this(const ref Loc loc, Expression exp)
+ {
+ Expressions* exps = new Expressions();
+ exps.push(exp);
+ this(loc, exps);
+ }
+
+ extern (D) this(const ref Loc loc, Expressions* exps)
+ {
+ super(loc, STMT.Compile);
+ this.exps = exps;
+ }
+
+ override CompileStatement syntaxCopy()
+ {
+ return new CompileStatement(loc, Expression.arraySyntaxCopy(exps));
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class CompoundStatement : Statement
+{
+ Statements* statements;
+
+ /**
+ * Construct a `CompoundStatement` using an already existing
+ * array of `Statement`s
+ *
+ * Params:
+ * loc = Instantiation information
+ * statements = An array of `Statement`s, that will referenced by this class
+ */
+ final extern (D) this(const ref Loc loc, Statements* statements)
+ {
+ super(loc, STMT.Compound);
+ this.statements = statements;
+ }
+
+ final extern (D) this(const ref Loc loc, Statements* statements, STMT stmt)
+ {
+ super(loc, stmt);
+ this.statements = statements;
+ }
+
+ /**
+ * Construct a `CompoundStatement` from an array of `Statement`s
+ *
+ * Params:
+ * loc = Instantiation information
+ * sts = A variadic array of `Statement`s, that will copied in this class
+ * The entries themselves will not be copied.
+ */
+ final extern (D) this(const ref Loc loc, Statement[] sts...)
+ {
+ super(loc, STMT.Compound);
+ statements = new Statements();
+ statements.reserve(sts.length);
+ foreach (s; sts)
+ statements.push(s);
+ }
+
+ static CompoundStatement create(Loc loc, Statement s1, Statement s2)
+ {
+ return new CompoundStatement(loc, s1, s2);
+ }
+
+ override CompoundStatement syntaxCopy()
+ {
+ return new CompoundStatement(loc, Statement.arraySyntaxCopy(statements));
+ }
+
+ override final inout(ReturnStatement) endsWithReturnStatement() inout nothrow pure
+ {
+ foreach (s; *statements)
+ {
+ if (s)
+ {
+ if (inout rs = s.endsWithReturnStatement())
+ return rs;
+ }
+ }
+ return null;
+ }
+
+ override final inout(Statement) last() inout nothrow pure
+ {
+ Statement s = null;
+ for (size_t i = statements.dim; i; --i)
+ {
+ s = cast(Statement)(*statements)[i - 1];
+ if (s)
+ {
+ s = cast(Statement)s.last();
+ if (s)
+ break;
+ }
+ }
+ return cast(inout)s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class CompoundDeclarationStatement : CompoundStatement
+{
+ extern (D) this(const ref Loc loc, Statements* statements)
+ {
+ super(loc, statements, STMT.CompoundDeclaration);
+ }
+
+ override CompoundDeclarationStatement syntaxCopy()
+ {
+ auto a = new Statements(statements.dim);
+ foreach (i, s; *statements)
+ {
+ (*a)[i] = s ? s.syntaxCopy() : null;
+ }
+ return new CompoundDeclarationStatement(loc, a);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * The purpose of this is so that continue will go to the next
+ * of the statements, and break will go to the end of the statements.
+ */
+extern (C++) final class UnrolledLoopStatement : Statement
+{
+ Statements* statements;
+
+ extern (D) this(const ref Loc loc, Statements* statements)
+ {
+ super(loc, STMT.UnrolledLoop);
+ this.statements = statements;
+ }
+
+ override UnrolledLoopStatement syntaxCopy()
+ {
+ auto a = new Statements(statements.dim);
+ foreach (i, s; *statements)
+ {
+ (*a)[i] = s ? s.syntaxCopy() : null;
+ }
+ return new UnrolledLoopStatement(loc, a);
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ return true;
+ }
+
+ override bool hasContinue() const pure nothrow
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) class ScopeStatement : Statement
+{
+ Statement statement;
+ Loc endloc; // location of closing curly bracket
+
+ extern (D) this(const ref Loc loc, Statement statement, Loc endloc)
+ {
+ super(loc, STMT.Scope);
+ this.statement = statement;
+ this.endloc = endloc;
+ }
+
+ override ScopeStatement syntaxCopy()
+ {
+ return new ScopeStatement(loc, statement ? statement.syntaxCopy() : null, endloc);
+ }
+
+ override inout(ReturnStatement) endsWithReturnStatement() inout nothrow pure
+ {
+ if (statement)
+ return statement.endsWithReturnStatement();
+ return null;
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ //printf("ScopeStatement::hasBreak() %s\n", toChars());
+ return statement ? statement.hasBreak() : false;
+ }
+
+ override bool hasContinue() const pure nothrow
+ {
+ return statement ? statement.hasContinue() : false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * Statement whose symbol table contains foreach index variables in a
+ * local scope and forwards other members to the parent scope. This
+ * wraps a statement.
+ *
+ * Also see: `dmd.attrib.ForwardingAttribDeclaration`
+ */
+extern (C++) final class ForwardingStatement : Statement
+{
+ /// The symbol containing the `static foreach` variables.
+ ForwardingScopeDsymbol sym = null;
+ /// The wrapped statement.
+ Statement statement;
+
+ extern (D) this(const ref Loc loc, ForwardingScopeDsymbol sym, Statement statement)
+ {
+ super(loc, STMT.Forwarding);
+ this.sym = sym;
+ assert(statement);
+ this.statement = statement;
+ }
+
+ extern (D) this(const ref Loc loc, Statement statement)
+ {
+ auto sym = new ForwardingScopeDsymbol(null);
+ sym.symtab = new DsymbolTable();
+ this(loc, sym, statement);
+ }
+
+ override ForwardingStatement syntaxCopy()
+ {
+ return new ForwardingStatement(loc, statement.syntaxCopy());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#while-statement
+ */
+extern (C++) final class WhileStatement : Statement
+{
+ Parameter param;
+ Expression condition;
+ Statement _body;
+ Loc endloc; // location of closing curly bracket
+
+ extern (D) this(const ref Loc loc, Expression condition, Statement _body, Loc endloc, Parameter param = null)
+ {
+ super(loc, STMT.While);
+ this.condition = condition;
+ this._body = _body;
+ this.endloc = endloc;
+ this.param = param;
+ }
+
+ override WhileStatement syntaxCopy()
+ {
+ return new WhileStatement(loc,
+ condition.syntaxCopy(),
+ _body ? _body.syntaxCopy() : null,
+ endloc, param ? param.syntaxCopy() : null);
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ return true;
+ }
+
+ override bool hasContinue() const pure nothrow
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#do-statement
+ */
+extern (C++) final class DoStatement : Statement
+{
+ Statement _body;
+ Expression condition;
+ Loc endloc; // location of ';' after while
+
+ extern (D) this(const ref Loc loc, Statement _body, Expression condition, Loc endloc)
+ {
+ super(loc, STMT.Do);
+ this._body = _body;
+ this.condition = condition;
+ this.endloc = endloc;
+ }
+
+ override DoStatement syntaxCopy()
+ {
+ return new DoStatement(loc,
+ _body ? _body.syntaxCopy() : null,
+ condition.syntaxCopy(),
+ endloc);
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ return true;
+ }
+
+ override bool hasContinue() const pure nothrow
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#for-statement
+ */
+extern (C++) final class ForStatement : Statement
+{
+ Statement _init;
+ Expression condition;
+ Expression increment;
+ Statement _body;
+ Loc endloc; // location of closing curly bracket
+
+ // When wrapped in try/finally clauses, this points to the outermost one,
+ // which may have an associated label. Internal break/continue statements
+ // treat that label as referring to this loop.
+ Statement relatedLabeled;
+
+ extern (D) this(const ref Loc loc, Statement _init, Expression condition, Expression increment, Statement _body, Loc endloc)
+ {
+ super(loc, STMT.For);
+ this._init = _init;
+ this.condition = condition;
+ this.increment = increment;
+ this._body = _body;
+ this.endloc = endloc;
+ }
+
+ override ForStatement syntaxCopy()
+ {
+ return new ForStatement(loc,
+ _init ? _init.syntaxCopy() : null,
+ condition ? condition.syntaxCopy() : null,
+ increment ? increment.syntaxCopy() : null,
+ _body.syntaxCopy(),
+ endloc);
+ }
+
+ override Statement getRelatedLabeled()
+ {
+ return relatedLabeled ? relatedLabeled : this;
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ //printf("ForStatement::hasBreak()\n");
+ return true;
+ }
+
+ override bool hasContinue() const pure nothrow
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#foreach-statement
+ */
+extern (C++) final class ForeachStatement : Statement
+{
+ TOK op; // TOK.foreach_ or TOK.foreach_reverse_
+ Parameters* parameters; // array of Parameters, one for each ForeachType
+ Expression aggr; // ForeachAggregate
+ Statement _body; // NoScopeNonEmptyStatement
+ Loc endloc; // location of closing curly bracket
+
+ VarDeclaration key;
+ VarDeclaration value;
+
+ FuncDeclaration func; // function we're lexically in
+
+ Statements* cases; // put breaks, continues, gotos and returns here
+ ScopeStatements* gotos; // forward referenced goto's go here
+
+ extern (D) this(const ref Loc loc, TOK op, Parameters* parameters, Expression aggr, Statement _body, Loc endloc)
+ {
+ super(loc, STMT.Foreach);
+ this.op = op;
+ this.parameters = parameters;
+ this.aggr = aggr;
+ this._body = _body;
+ this.endloc = endloc;
+ }
+
+ override ForeachStatement syntaxCopy()
+ {
+ return new ForeachStatement(loc, op,
+ Parameter.arraySyntaxCopy(parameters),
+ aggr.syntaxCopy(),
+ _body ? _body.syntaxCopy() : null,
+ endloc);
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ return true;
+ }
+
+ override bool hasContinue() const pure nothrow
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#foreach-range-statement
+ */
+extern (C++) final class ForeachRangeStatement : Statement
+{
+ TOK op; // TOK.foreach_ or TOK.foreach_reverse_
+ Parameter prm; // loop index variable
+ Expression lwr;
+ Expression upr;
+ Statement _body;
+ Loc endloc; // location of closing curly bracket
+
+ VarDeclaration key;
+
+ extern (D) this(const ref Loc loc, TOK op, Parameter prm, Expression lwr, Expression upr, Statement _body, Loc endloc)
+ {
+ super(loc, STMT.ForeachRange);
+ this.op = op;
+ this.prm = prm;
+ this.lwr = lwr;
+ this.upr = upr;
+ this._body = _body;
+ this.endloc = endloc;
+ }
+
+ override ForeachRangeStatement syntaxCopy()
+ {
+ return new ForeachRangeStatement(loc, op, prm.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc);
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ return true;
+ }
+
+ override bool hasContinue() const pure nothrow
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#if-statement
+ */
+extern (C++) final class IfStatement : Statement
+{
+ Parameter prm;
+ Expression condition;
+ Statement ifbody;
+ Statement elsebody;
+ VarDeclaration match; // for MatchExpression results
+ Loc endloc; // location of closing curly bracket
+
+ extern (D) this(const ref Loc loc, Parameter prm, Expression condition, Statement ifbody, Statement elsebody, Loc endloc)
+ {
+ super(loc, STMT.If);
+ this.prm = prm;
+ this.condition = condition;
+ this.ifbody = ifbody;
+ this.elsebody = elsebody;
+ this.endloc = endloc;
+ }
+
+ override IfStatement syntaxCopy()
+ {
+ return new IfStatement(loc,
+ prm ? prm.syntaxCopy() : null,
+ condition.syntaxCopy(),
+ ifbody ? ifbody.syntaxCopy() : null,
+ elsebody ? elsebody.syntaxCopy() : null,
+ endloc);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/version.html#ConditionalStatement
+ */
+extern (C++) final class ConditionalStatement : Statement
+{
+ Condition condition;
+ Statement ifbody;
+ Statement elsebody;
+
+ extern (D) this(const ref Loc loc, Condition condition, Statement ifbody, Statement elsebody)
+ {
+ super(loc, STMT.Conditional);
+ this.condition = condition;
+ this.ifbody = ifbody;
+ this.elsebody = elsebody;
+ }
+
+ override ConditionalStatement syntaxCopy()
+ {
+ return new ConditionalStatement(loc, condition.syntaxCopy(), ifbody.syntaxCopy(), elsebody ? elsebody.syntaxCopy() : null);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+
+/***********************************************************
+ * https://dlang.org/spec/version.html#StaticForeachStatement
+ * Static foreach statements, like:
+ * void main()
+ * {
+ * static foreach(i; 0 .. 10)
+ * {
+ * pragma(msg, i);
+ * }
+ * }
+ */
+extern (C++) final class StaticForeachStatement : Statement
+{
+ StaticForeach sfe;
+
+ extern (D) this(const ref Loc loc, StaticForeach sfe)
+ {
+ super(loc, STMT.StaticForeach);
+ this.sfe = sfe;
+ }
+
+ override StaticForeachStatement syntaxCopy()
+ {
+ return new StaticForeachStatement(loc, sfe.syntaxCopy());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#pragma-statement
+ */
+extern (C++) final class PragmaStatement : Statement
+{
+ const Identifier ident;
+ Expressions* args; // array of Expression's
+ Statement _body;
+
+ extern (D) this(const ref Loc loc, const Identifier ident, Expressions* args, Statement _body)
+ {
+ super(loc, STMT.Pragma);
+ this.ident = ident;
+ this.args = args;
+ this._body = _body;
+ }
+
+ override PragmaStatement syntaxCopy()
+ {
+ return new PragmaStatement(loc, ident, Expression.arraySyntaxCopy(args), _body ? _body.syntaxCopy() : null);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/version.html#StaticAssert
+ */
+extern (C++) final class StaticAssertStatement : Statement
+{
+ StaticAssert sa;
+
+ extern (D) this(StaticAssert sa)
+ {
+ super(sa.loc, STMT.StaticAssert);
+ this.sa = sa;
+ }
+
+ override StaticAssertStatement syntaxCopy()
+ {
+ return new StaticAssertStatement(sa.syntaxCopy(null));
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#switch-statement
+ */
+extern (C++) final class SwitchStatement : Statement
+{
+ Expression condition; /// switch(condition)
+ Statement _body; ///
+ bool isFinal; /// https://dlang.org/spec/statement.html#final-switch-statement
+
+ DefaultStatement sdefault; /// default:
+ Statement tryBody; /// set to TryCatchStatement or TryFinallyStatement if in _body portion
+ TryFinallyStatement tf; /// set if in the 'finally' block of a TryFinallyStatement
+ GotoCaseStatements gotoCases; /// array of unresolved GotoCaseStatement's
+ CaseStatements* cases; /// array of CaseStatement's
+ int hasNoDefault; /// !=0 if no default statement
+ int hasVars; /// !=0 if has variable case values
+ VarDeclaration lastVar; /// last observed variable declaration in this statement
+
+ extern (D) this(const ref Loc loc, Expression condition, Statement _body, bool isFinal)
+ {
+ super(loc, STMT.Switch);
+ this.condition = condition;
+ this._body = _body;
+ this.isFinal = isFinal;
+ }
+
+ override SwitchStatement syntaxCopy()
+ {
+ return new SwitchStatement(loc, condition.syntaxCopy(), _body.syntaxCopy(), isFinal);
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ return true;
+ }
+
+ /************************************
+ * Returns:
+ * true if error
+ */
+ extern (D) bool checkLabel()
+ {
+ /*
+ * Checks the scope of a label for existing variable declaration.
+ * Params:
+ * vd = last variable declared before this case/default label
+ * Returns: `true` if the variables declared in this label would be skipped.
+ */
+ bool checkVar(VarDeclaration vd)
+ {
+ for (auto v = vd; v && v != lastVar; v = v.lastVar)
+ {
+ if (v.isDataseg() || (v.storage_class & (STC.manifest | STC.temp) && vd.ident != Id.withSym) || v._init.isVoidInitializer())
+ continue;
+ if (vd.ident == Id.withSym)
+ error("`switch` skips declaration of `with` temporary at %s", v.loc.toChars());
+ else
+ error("`switch` skips declaration of variable `%s` at %s", v.toPrettyChars(), v.loc.toChars());
+ return true;
+ }
+ return false;
+ }
+
+ enum error = true;
+
+ if (sdefault && checkVar(sdefault.lastVar))
+ return !error; // return error once fully deprecated
+
+ foreach (scase; *cases)
+ {
+ if (scase && checkVar(scase.lastVar))
+ return !error; // return error once fully deprecated
+ }
+ return !error;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#CaseStatement
+ */
+extern (C++) final class CaseStatement : Statement
+{
+ Expression exp;
+ Statement statement;
+
+ int index; // which case it is (since we sort this)
+ VarDeclaration lastVar;
+ void* extra; // for use by Statement_toIR()
+
+ extern (D) this(const ref Loc loc, Expression exp, Statement statement)
+ {
+ super(loc, STMT.Case);
+ this.exp = exp;
+ this.statement = statement;
+ }
+
+ override CaseStatement syntaxCopy()
+ {
+ return new CaseStatement(loc, exp.syntaxCopy(), statement.syntaxCopy());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#CaseRangeStatement
+ */
+extern (C++) final class CaseRangeStatement : Statement
+{
+ Expression first;
+ Expression last;
+ Statement statement;
+
+ extern (D) this(const ref Loc loc, Expression first, Expression last, Statement statement)
+ {
+ super(loc, STMT.CaseRange);
+ this.first = first;
+ this.last = last;
+ this.statement = statement;
+ }
+
+ override CaseRangeStatement syntaxCopy()
+ {
+ return new CaseRangeStatement(loc, first.syntaxCopy(), last.syntaxCopy(), statement.syntaxCopy());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#DefaultStatement
+ */
+extern (C++) final class DefaultStatement : Statement
+{
+ Statement statement;
+
+ VarDeclaration lastVar;
+
+ extern (D) this(const ref Loc loc, Statement statement)
+ {
+ super(loc, STMT.Default);
+ this.statement = statement;
+ }
+
+ override DefaultStatement syntaxCopy()
+ {
+ return new DefaultStatement(loc, statement.syntaxCopy());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#GotoStatement
+ */
+extern (C++) final class GotoDefaultStatement : Statement
+{
+ SwitchStatement sw;
+
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, STMT.GotoDefault);
+ }
+
+ override GotoDefaultStatement syntaxCopy()
+ {
+ return new GotoDefaultStatement(loc);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#GotoStatement
+ */
+extern (C++) final class GotoCaseStatement : Statement
+{
+ Expression exp; // null, or which case to goto
+
+ CaseStatement cs; // case statement it resolves to
+
+ extern (D) this(const ref Loc loc, Expression exp)
+ {
+ super(loc, STMT.GotoCase);
+ this.exp = exp;
+ }
+
+ override GotoCaseStatement syntaxCopy()
+ {
+ return new GotoCaseStatement(loc, exp ? exp.syntaxCopy() : null);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class SwitchErrorStatement : Statement
+{
+ Expression exp;
+
+ extern (D) this(const ref Loc loc)
+ {
+ super(loc, STMT.SwitchError);
+ }
+
+ final extern (D) this(const ref Loc loc, Expression exp)
+ {
+ super(loc, STMT.SwitchError);
+ this.exp = exp;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#return-statement
+ */
+extern (C++) final class ReturnStatement : Statement
+{
+ Expression exp;
+ size_t caseDim;
+
+ extern (D) this(const ref Loc loc, Expression exp)
+ {
+ super(loc, STMT.Return);
+ this.exp = exp;
+ }
+
+ override ReturnStatement syntaxCopy()
+ {
+ return new ReturnStatement(loc, exp ? exp.syntaxCopy() : null);
+ }
+
+ override inout(ReturnStatement) endsWithReturnStatement() inout nothrow pure
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#break-statement
+ */
+extern (C++) final class BreakStatement : Statement
+{
+ Identifier ident;
+
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, STMT.Break);
+ this.ident = ident;
+ }
+
+ override BreakStatement syntaxCopy()
+ {
+ return new BreakStatement(loc, ident);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#continue-statement
+ */
+extern (C++) final class ContinueStatement : Statement
+{
+ Identifier ident;
+
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, STMT.Continue);
+ this.ident = ident;
+ }
+
+ override ContinueStatement syntaxCopy()
+ {
+ return new ContinueStatement(loc, ident);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#SynchronizedStatement
+ */
+extern (C++) final class SynchronizedStatement : Statement
+{
+ Expression exp;
+ Statement _body;
+
+ extern (D) this(const ref Loc loc, Expression exp, Statement _body)
+ {
+ super(loc, STMT.Synchronized);
+ this.exp = exp;
+ this._body = _body;
+ }
+
+ override SynchronizedStatement syntaxCopy()
+ {
+ return new SynchronizedStatement(loc, exp ? exp.syntaxCopy() : null, _body ? _body.syntaxCopy() : null);
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ return false; //true;
+ }
+
+ override bool hasContinue() const pure nothrow
+ {
+ return false; //true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#with-statement
+ */
+extern (C++) final class WithStatement : Statement
+{
+ Expression exp;
+ Statement _body;
+ VarDeclaration wthis;
+ Loc endloc;
+
+ extern (D) this(const ref Loc loc, Expression exp, Statement _body, Loc endloc)
+ {
+ super(loc, STMT.With);
+ this.exp = exp;
+ this._body = _body;
+ this.endloc = endloc;
+ }
+
+ override WithStatement syntaxCopy()
+ {
+ return new WithStatement(loc, exp.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#try-statement
+ */
+extern (C++) final class TryCatchStatement : Statement
+{
+ Statement _body;
+ Catches* catches;
+
+ Statement tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion
+
+ extern (D) this(const ref Loc loc, Statement _body, Catches* catches)
+ {
+ super(loc, STMT.TryCatch);
+ this._body = _body;
+ this.catches = catches;
+ }
+
+ override TryCatchStatement syntaxCopy()
+ {
+ auto a = new Catches(catches.dim);
+ foreach (i, c; *catches)
+ {
+ (*a)[i] = c.syntaxCopy();
+ }
+ return new TryCatchStatement(loc, _body.syntaxCopy(), a);
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ return false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#Catch
+ */
+extern (C++) final class Catch : RootObject
+{
+ const Loc loc;
+ Type type;
+ Identifier ident;
+ Statement handler;
+
+ VarDeclaration var;
+ bool errors; // set if semantic processing errors
+
+ // was generated by the compiler, wasn't present in source code
+ bool internalCatch;
+
+ extern (D) this(const ref Loc loc, Type type, Identifier ident, Statement handler)
+ {
+ //printf("Catch(%s, loc = %s)\n", id.toChars(), loc.toChars());
+ this.loc = loc;
+ this.type = type;
+ this.ident = ident;
+ this.handler = handler;
+ }
+
+ Catch syntaxCopy()
+ {
+ auto c = new Catch(loc, type ? type.syntaxCopy() : getThrowable(), ident, (handler ? handler.syntaxCopy() : null));
+ c.internalCatch = internalCatch;
+ return c;
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#try-statement
+ */
+extern (C++) final class TryFinallyStatement : Statement
+{
+ Statement _body;
+ Statement finalbody;
+
+ Statement tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion
+ bool bodyFallsThru; /// true if _body falls through to finally
+
+ extern (D) this(const ref Loc loc, Statement _body, Statement finalbody)
+ {
+ super(loc, STMT.TryFinally);
+ this._body = _body;
+ this.finalbody = finalbody;
+ this.bodyFallsThru = true; // assume true until statementSemantic()
+ }
+
+ static TryFinallyStatement create(Loc loc, Statement _body, Statement finalbody)
+ {
+ return new TryFinallyStatement(loc, _body, finalbody);
+ }
+
+ override TryFinallyStatement syntaxCopy()
+ {
+ return new TryFinallyStatement(loc, _body.syntaxCopy(), finalbody.syntaxCopy());
+ }
+
+ override bool hasBreak() const pure nothrow
+ {
+ return false; //true;
+ }
+
+ override bool hasContinue() const pure nothrow
+ {
+ return false; //true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#scope-guard-statement
+ */
+extern (C++) final class ScopeGuardStatement : Statement
+{
+ TOK tok;
+ Statement statement;
+
+ extern (D) this(const ref Loc loc, TOK tok, Statement statement)
+ {
+ super(loc, STMT.ScopeGuard);
+ this.tok = tok;
+ this.statement = statement;
+ }
+
+ override ScopeGuardStatement syntaxCopy()
+ {
+ return new ScopeGuardStatement(loc, tok, statement.syntaxCopy());
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#throw-statement
+ */
+extern (C++) final class ThrowStatement : Statement
+{
+ Expression exp;
+
+ // was generated by the compiler, wasn't present in source code
+ bool internalThrow;
+
+ extern (D) this(const ref Loc loc, Expression exp)
+ {
+ super(loc, STMT.Throw);
+ this.exp = exp;
+ }
+
+ override ThrowStatement syntaxCopy()
+ {
+ auto s = new ThrowStatement(loc, exp.syntaxCopy());
+ s.internalThrow = internalThrow;
+ return s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class DebugStatement : Statement
+{
+ Statement statement;
+
+ extern (D) this(const ref Loc loc, Statement statement)
+ {
+ super(loc, STMT.Debug);
+ this.statement = statement;
+ }
+
+ override DebugStatement syntaxCopy()
+ {
+ return new DebugStatement(loc, statement ? statement.syntaxCopy() : null);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#goto-statement
+ */
+extern (C++) final class GotoStatement : Statement
+{
+ Identifier ident;
+ LabelDsymbol label;
+ Statement tryBody; /// set to TryCatchStatement or TryFinallyStatement if in _body portion
+ TryFinallyStatement tf;
+ ScopeGuardStatement os;
+ VarDeclaration lastVar;
+
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, STMT.Goto);
+ this.ident = ident;
+ }
+
+ override GotoStatement syntaxCopy()
+ {
+ return new GotoStatement(loc, ident);
+ }
+
+ extern (D) bool checkLabel()
+ {
+ if (!label.statement)
+ return true; // error should have been issued for this already
+
+ if (label.statement.os != os)
+ {
+ if (os && os.tok == TOK.onScopeFailure && !label.statement.os)
+ {
+ // Jump out from scope(failure) block is allowed.
+ }
+ else
+ {
+ if (label.statement.os)
+ error("cannot `goto` in to `%s` block", Token.toChars(label.statement.os.tok));
+ else
+ error("cannot `goto` out of `%s` block", Token.toChars(os.tok));
+ return true;
+ }
+ }
+
+ if (label.statement.tf != tf)
+ {
+ error("cannot `goto` in or out of `finally` block");
+ return true;
+ }
+
+ Statement stbnext;
+ for (auto stb = tryBody; stb != label.statement.tryBody; stb = stbnext)
+ {
+ if (!stb)
+ {
+ error("cannot `goto` into `try` block");
+ return true;
+ }
+ if (auto stf = stb.isTryFinallyStatement())
+ stbnext = stf.tryBody;
+ else if (auto stc = stb.isTryCatchStatement())
+ stbnext = stc.tryBody;
+ else
+ assert(0);
+ }
+
+ VarDeclaration vd = label.statement.lastVar;
+ if (!vd || vd.isDataseg() || (vd.storage_class & STC.manifest))
+ return false;
+
+ VarDeclaration last = lastVar;
+ while (last && last != vd)
+ last = last.lastVar;
+ if (last == vd)
+ {
+ // All good, the label's scope has no variables
+ }
+ else if (vd.storage_class & STC.exptemp)
+ {
+ // Lifetime ends at end of expression, so no issue with skipping the statement
+ }
+ else if (vd.ident == Id.withSym)
+ {
+ error("`goto` skips declaration of `with` temporary at %s", vd.loc.toChars());
+ return true;
+ }
+ else
+ {
+ error("`goto` skips declaration of variable `%s` at %s", vd.toPrettyChars(), vd.loc.toChars());
+ return true;
+ }
+
+ return false;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#LabeledStatement
+ */
+extern (C++) final class LabelStatement : Statement
+{
+ Identifier ident;
+ Statement statement;
+
+ Statement tryBody; /// set to TryCatchStatement or TryFinallyStatement if in _body portion
+ TryFinallyStatement tf;
+ ScopeGuardStatement os;
+ VarDeclaration lastVar;
+ Statement gotoTarget; // interpret
+ void* extra; // used by Statement_toIR()
+ bool breaks; // someone did a 'break ident'
+
+ extern (D) this(const ref Loc loc, Identifier ident, Statement statement)
+ {
+ super(loc, STMT.Label);
+ this.ident = ident;
+ this.statement = statement;
+ }
+
+ override LabelStatement syntaxCopy()
+ {
+ return new LabelStatement(loc, ident, statement ? statement.syntaxCopy() : null);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ */
+extern (C++) final class LabelDsymbol : Dsymbol
+{
+ LabelStatement statement;
+
+ bool deleted; // set if rewritten to return in foreach delegate
+ bool iasm; // set if used by inline assembler
+
+ extern (D) this(Identifier ident, const ref Loc loc = Loc.initial)
+ {
+ super(loc, ident);
+ }
+
+ static LabelDsymbol create(Identifier ident)
+ {
+ return new LabelDsymbol(ident);
+ }
+
+ // is this a LabelDsymbol()?
+ override LabelDsymbol isLabel()
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/statement.html#asm
+ */
+extern (C++) class AsmStatement : Statement
+{
+ Token* tokens;
+
+ extern (D) this(const ref Loc loc, Token* tokens)
+ {
+ super(loc, STMT.Asm);
+ this.tokens = tokens;
+ }
+
+ extern (D) this(const ref Loc loc, Token* tokens, STMT stmt)
+ {
+ super(loc, stmt);
+ this.tokens = tokens;
+ }
+
+ override AsmStatement syntaxCopy()
+ {
+ return new AsmStatement(loc, tokens);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/iasm.html
+ */
+extern (C++) final class InlineAsmStatement : AsmStatement
+{
+ code* asmcode;
+ uint asmalign; // alignment of this statement
+ uint regs; // mask of registers modified (must match regm_t in back end)
+ bool refparam; // true if function parameter is referenced
+ bool naked; // true if function is to be naked
+
+ extern (D) this(const ref Loc loc, Token* tokens)
+ {
+ super(loc, tokens, STMT.InlineAsm);
+ }
+
+ override InlineAsmStatement syntaxCopy()
+ {
+ return new InlineAsmStatement(loc, tokens);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
+ * Assembler instructions with D expression operands.
+ */
+extern (C++) final class GccAsmStatement : AsmStatement
+{
+ StorageClass stc; // attributes of the asm {} block
+ Expression insn; // string expression that is the template for assembler code
+ Expressions* args; // input and output operands of the statement
+ uint outputargs; // of the operands in 'args', the number of output operands
+ Identifiers* names; // list of symbolic names for the operands
+ Expressions* constraints; // list of string constants specifying constraints on operands
+ Expressions* clobbers; // list of string constants specifying clobbers and scratch registers
+ Identifiers* labels; // list of goto labels
+ GotoStatements* gotos; // of the goto labels, the equivalent statements they represent
+
+ extern (D) this(const ref Loc loc, Token* tokens)
+ {
+ super(loc, tokens, STMT.GccAsm);
+ }
+
+ override GccAsmStatement syntaxCopy()
+ {
+ return new GccAsmStatement(loc, tokens);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * a complete asm {} block
+ */
+extern (C++) final class CompoundAsmStatement : CompoundStatement
+{
+ StorageClass stc; // postfix attributes like nothrow/pure/@trusted
+
+ extern (D) this(const ref Loc loc, Statements* statements, StorageClass stc)
+ {
+ super(loc, statements, STMT.CompoundAsm);
+ this.stc = stc;
+ }
+
+ override CompoundAsmStatement syntaxCopy()
+ {
+ auto a = new Statements(statements.dim);
+ foreach (i, s; *statements)
+ {
+ (*a)[i] = s ? s.syntaxCopy() : null;
+ }
+ return new CompoundAsmStatement(loc, a, stc);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/module.html#ImportDeclaration
+ */
+extern (C++) final class ImportStatement : Statement
+{
+ Dsymbols* imports; // Array of Import's
+
+ extern (D) this(const ref Loc loc, Dsymbols* imports)
+ {
+ super(loc, STMT.Import);
+ this.imports = imports;
+ }
+
+ override ImportStatement syntaxCopy()
+ {
+ auto m = new Dsymbols(imports.dim);
+ foreach (i, s; *imports)
+ {
+ (*m)[i] = s.syntaxCopy(null);
+ }
+ return new ImportStatement(loc, m);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h
index c64e51a5be7..7825762db9e 100644
--- a/gcc/d/dmd/statement.h
+++ b/gcc/d/dmd/statement.h
@@ -10,27 +10,21 @@
#pragma once
-#include "root/root.h"
-
#include "arraytypes.h"
#include "ast_node.h"
#include "dsymbol.h"
#include "visitor.h"
#include "tokens.h"
-struct OutBuffer;
struct Scope;
class Expression;
class LabelDsymbol;
class Identifier;
-class Statement;
class IfStatement;
class ExpStatement;
class DefaultStatement;
class VarDeclaration;
class Condition;
-class Module;
-struct Token;
class ErrorStatement;
class ReturnStatement;
class CompoundStatement;
@@ -39,7 +33,6 @@ class StaticAssert;
class AsmStatement;
class GotoStatement;
class ScopeStatement;
-class Catch;
class TryCatchStatement;
class TryFinallyStatement;
class CaseStatement;
@@ -50,14 +43,6 @@ class StaticForeach;
// Back end
struct code;
-Statement *statementSemantic(Statement *s, Scope *sc);
-Statement *semanticNoScope(Statement *s, Scope *sc);
-Statement *semanticScope(Statement *s, Scope *sc, Statement *sbreak, Statement *scontinue);
-void catchSemantic(Catch *c, Scope *sc);
-
-bool inferAggregate(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply);
-bool inferApplyArgTypes(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply);
-
/* How a statement exits; this is returned by blockExit()
*/
enum BE
@@ -74,46 +59,106 @@ enum BE
BEany = (BEfallthru | BEthrow | BEreturn | BEgoto | BEhalt)
};
+typedef unsigned char STMT;
+enum
+{
+ STMTerror,
+ STMTpeel,
+ STMTexp, STMTdtorExp,
+ STMTcompile,
+ STMTcompound, STMTcompoundDeclaration, STMTcompoundAsm,
+ STMTunrolledLoop,
+ STMTscope,
+ STMTforwarding,
+ STMTwhile,
+ STMTdo,
+ STMTfor,
+ STMTforeach,
+ STMTforeachRange,
+ STMTif,
+ STMTconditional,
+ STMTstaticForeach,
+ STMTpragma,
+ STMTstaticAssert,
+ STMTswitch,
+ STMTcase,
+ STMTcaseRange,
+ STMTdefault,
+ STMTgotoDefault,
+ STMTgotoCase,
+ STMTswitchError,
+ STMTreturn,
+ STMTbreak,
+ STMTcontinue,
+ STMTsynchronized,
+ STMTwith,
+ STMTtryCatch,
+ STMTtryFinally,
+ STMTscopeGuard,
+ STMTthrow,
+ STMTdebug,
+ STMTgoto,
+ STMTlabel,
+ STMTasm, STMTinlineAsm, STMTgccAsm,
+ STMTimport
+};
+
class Statement : public ASTNode
{
public:
Loc loc;
+ STMT stmt;
- Statement(Loc loc);
virtual Statement *syntaxCopy();
- static Statements *arraySyntaxCopy(Statements *a);
- void print();
- const char *toChars();
+ const char *toChars() const;
void error(const char *format, ...);
void warning(const char *format, ...);
void deprecation(const char *format, ...);
virtual Statement *getRelatedLabeled() { return this; }
- virtual bool hasBreak();
- virtual bool hasContinue();
+ virtual bool hasBreak() const;
+ virtual bool hasContinue() const;
bool usesEH();
bool comeFrom();
bool hasCode();
- virtual Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally);
- virtual Statements *flatten(Scope *sc);
virtual Statement *last();
- // Avoid dynamic_cast
- virtual ErrorStatement *isErrorStatement() { return NULL; }
- virtual ScopeStatement *isScopeStatement() { return NULL; }
- virtual ExpStatement *isExpStatement() { return NULL; }
- virtual CompoundStatement *isCompoundStatement() { return NULL; }
- virtual ReturnStatement *isReturnStatement() { return NULL; }
- virtual IfStatement *isIfStatement() { return NULL; }
- virtual CaseStatement *isCaseStatement() { return NULL; }
- virtual DefaultStatement *isDefaultStatement() { return NULL; }
- virtual LabelStatement *isLabelStatement() { return NULL; }
- virtual GotoDefaultStatement *isGotoDefaultStatement() { return NULL; }
- virtual GotoCaseStatement *isGotoCaseStatement() { return NULL; }
- virtual BreakStatement *isBreakStatement() { return NULL; }
- virtual DtorExpStatement *isDtorExpStatement() { return NULL; }
- virtual ForwardingStatement *isForwardingStatement() { return NULL; }
+ virtual ReturnStatement *endsWithReturnStatement() { return NULL; }
+
+ ErrorStatement *isErrorStatement() { return stmt == STMTerror ? (ErrorStatement*)this : NULL; }
+ ScopeStatement *isScopeStatement() { return stmt == STMTscope ? (ScopeStatement*)this : NULL; }
+ ExpStatement *isExpStatement() { return stmt == STMTexp ? (ExpStatement*)this : NULL; }
+ CompoundStatement *isCompoundStatement() { return stmt == STMTcompound ? (CompoundStatement*)this : NULL; }
+ ReturnStatement *isReturnStatement() { return stmt == STMTreturn ? (ReturnStatement*)this : NULL; }
+ IfStatement *isIfStatement() { return stmt == STMTif ? (IfStatement*)this : NULL; }
+ ConditionalStatement *isConditionalStatement() { return stmt == STMTconditional ? (ConditionalStatement*)this : NULL; }
+ StaticForeachStatement *isStaticForeachStatement() { return stmt == STMTstaticForeach ? (StaticForeachStatement*)this : NULL; }
+ CaseStatement *isCaseStatement() { return stmt == STMTcase ? (CaseStatement*)this : NULL; }
+ DefaultStatement *isDefaultStatement() { return stmt == STMTdefault ? (DefaultStatement*)this : NULL; }
+ LabelStatement *isLabelStatement() { return stmt == STMTlabel ? (LabelStatement*)this : NULL; }
+ GotoDefaultStatement *isGotoDefaultStatement() { return stmt == STMTgotoDefault ? (GotoDefaultStatement*)this : NULL; }
+ GotoCaseStatement *isGotoCaseStatement() { return stmt == STMTgotoCase ? (GotoCaseStatement*)this : NULL; }
+ BreakStatement *isBreakStatement() { return stmt == STMTbreak ? (BreakStatement*)this : NULL; }
+ DtorExpStatement *isDtorExpStatement() { return stmt == STMTdtorExp ? (DtorExpStatement*)this : NULL; }
+ CompileStatement *isCompileStatement() { return stmt == STMTcompile ? (CompileStatement*)this : NULL; }
+ ForwardingStatement *isForwardingStatement() { return stmt == STMTforwarding ? (ForwardingStatement*)this : NULL; }
+ DoStatement *isDoStatement() { return stmt == STMTdo ? (DoStatement*)this : NULL; }
+ ForStatement *isForStatement() { return stmt == STMTfor ? (ForStatement*)this : NULL; }
+ ForeachStatement *isForeachStatement() { return stmt == STMTforeach ? (ForeachStatement*)this : NULL; }
+ SwitchStatement *isSwitchStatement() { return stmt == STMTswitch ? (SwitchStatement*)this : NULL; }
+ ContinueStatement *isContinueStatement() { return stmt == STMTcontinue ? (ContinueStatement*)this : NULL; }
+ WithStatement *isWithStatement() { return stmt == STMTwith ? (WithStatement*)this : NULL; }
+ TryCatchStatement *isTryCatchStatement() { return stmt == STMTtryCatch ? (TryCatchStatement*)this : NULL; }
+ ThrowStatement *isThrowStatement() { return stmt == STMTthrow ? (ThrowStatement*)this : NULL; }
+ DebugStatement *isDebugStatement() { return stmt == STMTdebug ? (DebugStatement*)this : NULL; }
+ TryFinallyStatement *isTryFinallyStatement() { return stmt == STMTtryFinally ? (TryFinallyStatement*)this : NULL; }
+ ScopeGuardStatement *isScopeGuardStatement() { return stmt == STMTscopeGuard ? (ScopeGuardStatement*)this : NULL; }
+ SwitchErrorStatement *isSwitchErrorStatement() { return stmt == STMTswitchError ? (SwitchErrorStatement*)this : NULL; }
+ UnrolledLoopStatement *isUnrolledLoopStatement() { return stmt == STMTunrolledLoop ? (UnrolledLoopStatement*)this : NULL; }
+ ForeachRangeStatement *isForeachRangeStatement() { return stmt == STMTforeachRange ? (ForeachRangeStatement*)this : NULL; }
+ CompoundDeclarationStatement *isCompoundDeclarationStatement() { return stmt == STMTcompoundDeclaration ? (CompoundDeclarationStatement*)this : NULL; }
+
void accept(Visitor *v) { v->visit(this); }
};
@@ -123,10 +168,8 @@ public:
class ErrorStatement : public Statement
{
public:
- ErrorStatement();
- Statement *syntaxCopy();
+ ErrorStatement *syntaxCopy();
- ErrorStatement *isErrorStatement() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -135,7 +178,6 @@ class PeelStatement : public Statement
public:
Statement *s;
- PeelStatement(Statement *s);
void accept(Visitor *v) { v->visit(this); }
};
@@ -144,14 +186,9 @@ class ExpStatement : public Statement
public:
Expression *exp;
- ExpStatement(Loc loc, Expression *exp);
- ExpStatement(Loc loc, Dsymbol *s);
static ExpStatement *create(Loc loc, Expression *exp);
- Statement *syntaxCopy();
- Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally);
- Statements *flatten(Scope *sc);
+ ExpStatement *syntaxCopy();
- ExpStatement *isExpStatement() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -163,11 +200,8 @@ public:
VarDeclaration *var;
- DtorExpStatement(Loc loc, Expression *exp, VarDeclaration *v);
- Statement *syntaxCopy();
+ DtorExpStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
-
- DtorExpStatement *isDtorExpStatement() { return this; }
};
class CompileStatement : public Statement
@@ -175,10 +209,7 @@ class CompileStatement : public Statement
public:
Expressions *exps;
- CompileStatement(Loc loc, Expression *exp);
- CompileStatement(Loc loc, Expressions *exps);
- Statement *syntaxCopy();
- Statements *flatten(Scope *sc);
+ CompileStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -187,24 +218,18 @@ class CompoundStatement : public Statement
public:
Statements *statements;
- CompoundStatement(Loc loc, Statements *s);
- CompoundStatement(Loc loc, Statement *s1);
- CompoundStatement(Loc loc, Statement *s1, Statement *s2);
static CompoundStatement *create(Loc loc, Statement *s1, Statement *s2);
- Statement *syntaxCopy();
- Statements *flatten(Scope *sc);
- ReturnStatement *isReturnStatement();
+ CompoundStatement *syntaxCopy();
+ ReturnStatement *endsWithReturnStatement();
Statement *last();
- CompoundStatement *isCompoundStatement() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
class CompoundDeclarationStatement : public CompoundStatement
{
public:
- CompoundDeclarationStatement(Loc loc, Statements *s);
- Statement *syntaxCopy();
+ CompoundDeclarationStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -216,10 +241,9 @@ class UnrolledLoopStatement : public Statement
public:
Statements *statements;
- UnrolledLoopStatement(Loc loc, Statements *statements);
- Statement *syntaxCopy();
- bool hasBreak();
- bool hasContinue();
+ UnrolledLoopStatement *syntaxCopy();
+ bool hasBreak() const;
+ bool hasContinue() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -230,12 +254,10 @@ public:
Statement *statement;
Loc endloc; // location of closing curly bracket
- ScopeStatement(Loc loc, Statement *s, Loc endloc);
- Statement *syntaxCopy();
- ScopeStatement *isScopeStatement() { return this; }
- ReturnStatement *isReturnStatement();
- bool hasBreak();
- bool hasContinue();
+ ScopeStatement *syntaxCopy();
+ ReturnStatement *endsWithReturnStatement();
+ bool hasBreak() const;
+ bool hasContinue() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -246,25 +268,21 @@ public:
ForwardingScopeDsymbol *sym;
Statement *statement;
- ForwardingStatement(Loc loc, ForwardingScopeDsymbol *sym, Statement *s);
- ForwardingStatement(Loc loc, Statement *s);
- Statement *syntaxCopy();
- Statements *flatten(Scope *sc);
- ForwardingStatement *isForwardingStatement() { return this; }
+ ForwardingStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
class WhileStatement : public Statement
{
public:
+ Parameter *param;
Expression *condition;
Statement *_body;
Loc endloc; // location of closing curly bracket
- WhileStatement(Loc loc, Expression *c, Statement *b, Loc endloc);
- Statement *syntaxCopy();
- bool hasBreak();
- bool hasContinue();
+ WhileStatement *syntaxCopy();
+ bool hasBreak() const;
+ bool hasContinue() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -276,10 +294,9 @@ public:
Expression *condition;
Loc endloc; // location of ';' after while
- DoStatement(Loc loc, Statement *b, Expression *c, Loc endloc);
- Statement *syntaxCopy();
- bool hasBreak();
- bool hasContinue();
+ DoStatement *syntaxCopy();
+ bool hasBreak() const;
+ bool hasContinue() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -298,12 +315,10 @@ public:
// treat that label as referring to this loop.
Statement *relatedLabeled;
- ForStatement(Loc loc, Statement *init, Expression *condition, Expression *increment, Statement *body, Loc endloc);
- Statement *syntaxCopy();
- Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally);
+ ForStatement *syntaxCopy();
Statement *getRelatedLabeled() { return relatedLabeled ? relatedLabeled : this; }
- bool hasBreak();
- bool hasContinue();
+ bool hasBreak() const;
+ bool hasContinue() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -325,11 +340,9 @@ public:
Statements *cases; // put breaks, continues, gotos and returns here
ScopeStatements *gotos; // forward referenced goto's go here
- ForeachStatement(Loc loc, TOK op, Parameters *parameters, Expression *aggr, Statement *body, Loc endloc);
- Statement *syntaxCopy();
- bool checkForArgTypes();
- bool hasBreak();
- bool hasContinue();
+ ForeachStatement *syntaxCopy();
+ bool hasBreak() const;
+ bool hasContinue() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -346,11 +359,9 @@ public:
VarDeclaration *key;
- ForeachRangeStatement(Loc loc, TOK op, Parameter *prm,
- Expression *lwr, Expression *upr, Statement *body, Loc endloc);
- Statement *syntaxCopy();
- bool hasBreak();
- bool hasContinue();
+ ForeachRangeStatement *syntaxCopy();
+ bool hasBreak() const;
+ bool hasContinue() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -362,13 +373,10 @@ public:
Expression *condition;
Statement *ifbody;
Statement *elsebody;
- Loc endloc; // location of closing curly bracket
-
VarDeclaration *match; // for MatchExpression results
+ Loc endloc; // location of closing curly bracket
- IfStatement(Loc loc, Parameter *prm, Expression *condition, Statement *ifbody, Statement *elsebody, Loc endloc);
- Statement *syntaxCopy();
- IfStatement *isIfStatement() { return this; }
+ IfStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -380,9 +388,7 @@ public:
Statement *ifbody;
Statement *elsebody;
- ConditionalStatement(Loc loc, Condition *condition, Statement *ifbody, Statement *elsebody);
- Statement *syntaxCopy();
- Statements *flatten(Scope *sc);
+ ConditionalStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -392,9 +398,7 @@ class StaticForeachStatement : public Statement
public:
StaticForeach *sfe;
- StaticForeachStatement(Loc loc, StaticForeach *sfe);
- Statement *syntaxCopy();
- Statements *flatten(Scope *sc);
+ StaticForeachStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -406,8 +410,7 @@ public:
Expressions *args; // array of Expression's
Statement *_body;
- PragmaStatement(Loc loc, Identifier *ident, Expressions *args, Statement *body);
- Statement *syntaxCopy();
+ PragmaStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -417,8 +420,7 @@ class StaticAssertStatement : public Statement
public:
StaticAssert *sa;
- StaticAssertStatement(StaticAssert *sa);
- Statement *syntaxCopy();
+ StaticAssertStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -431,6 +433,7 @@ public:
bool isFinal;
DefaultStatement *sdefault;
+ Statement *tryBody; // set to TryCatchStatement or TryFinallyStatement if in _body portion
TryFinallyStatement *tf;
GotoCaseStatements gotoCases; // array of unresolved GotoCaseStatement's
CaseStatements *cases; // array of CaseStatement's
@@ -438,10 +441,8 @@ public:
int hasVars; // !=0 if has variable case values
VarDeclaration *lastVar;
- SwitchStatement(Loc loc, Expression *c, Statement *b, bool isFinal);
- Statement *syntaxCopy();
- bool hasBreak();
- bool checkLabel();
+ SwitchStatement *syntaxCopy();
+ bool hasBreak() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -454,11 +455,9 @@ public:
int index; // which case it is (since we sort this)
VarDeclaration *lastVar;
+ void* extra; // for use by Statement_toIR()
- CaseStatement(Loc loc, Expression *exp, Statement *s);
- Statement *syntaxCopy();
- int compare(RootObject *obj);
- CaseStatement *isCaseStatement() { return this; }
+ CaseStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -471,8 +470,7 @@ public:
Expression *last;
Statement *statement;
- CaseRangeStatement(Loc loc, Expression *first, Expression *last, Statement *s);
- Statement *syntaxCopy();
+ CaseRangeStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -483,9 +481,7 @@ public:
Statement *statement;
VarDeclaration *lastVar;
- DefaultStatement(Loc loc, Statement *s);
- Statement *syntaxCopy();
- DefaultStatement *isDefaultStatement() { return this; }
+ DefaultStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -495,9 +491,7 @@ class GotoDefaultStatement : public Statement
public:
SwitchStatement *sw;
- GotoDefaultStatement(Loc loc);
- Statement *syntaxCopy();
- GotoDefaultStatement *isGotoDefaultStatement() { return this; }
+ GotoDefaultStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -508,9 +502,7 @@ public:
Expression *exp; // NULL, or which case to goto
CaseStatement *cs; // case statement it resolves to
- GotoCaseStatement(Loc loc, Expression *exp);
- Statement *syntaxCopy();
- GotoCaseStatement *isGotoCaseStatement() { return this; }
+ GotoCaseStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -518,7 +510,7 @@ public:
class SwitchErrorStatement : public Statement
{
public:
- SwitchErrorStatement(Loc loc);
+ Expression *exp;
void accept(Visitor *v) { v->visit(this); }
};
@@ -529,10 +521,9 @@ public:
Expression *exp;
size_t caseDim;
- ReturnStatement(Loc loc, Expression *exp);
- Statement *syntaxCopy();
+ ReturnStatement *syntaxCopy();
- ReturnStatement *isReturnStatement() { return this; }
+ ReturnStatement *endsWithReturnStatement() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -541,10 +532,8 @@ class BreakStatement : public Statement
public:
Identifier *ident;
- BreakStatement(Loc loc, Identifier *ident);
- Statement *syntaxCopy();
+ BreakStatement *syntaxCopy();
- BreakStatement *isBreakStatement() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -553,8 +542,7 @@ class ContinueStatement : public Statement
public:
Identifier *ident;
- ContinueStatement(Loc loc, Identifier *ident);
- Statement *syntaxCopy();
+ ContinueStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -565,10 +553,9 @@ public:
Expression *exp;
Statement *_body;
- SynchronizedStatement(Loc loc, Expression *exp, Statement *body);
- Statement *syntaxCopy();
- bool hasBreak();
- bool hasContinue();
+ SynchronizedStatement *syntaxCopy();
+ bool hasBreak() const;
+ bool hasContinue() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -581,8 +568,7 @@ public:
VarDeclaration *wthis;
Loc endloc;
- WithStatement(Loc loc, Expression *exp, Statement *body, Loc endloc);
- Statement *syntaxCopy();
+ WithStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -593,9 +579,10 @@ public:
Statement *_body;
Catches *catches;
- TryCatchStatement(Loc loc, Statement *body, Catches *catches);
- Statement *syntaxCopy();
- bool hasBreak();
+ Statement *tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion
+
+ TryCatchStatement *syntaxCopy();
+ bool hasBreak() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -606,9 +593,9 @@ public:
Loc loc;
Type *type;
Identifier *ident;
- VarDeclaration *var;
Statement *handler;
+ VarDeclaration *var;
// set if semantic processing errors
bool errors;
@@ -616,7 +603,6 @@ public:
// wasn't present in source code
bool internalCatch;
- Catch(Loc loc, Type *t, Identifier *id, Statement *handler);
Catch *syntaxCopy();
};
@@ -626,11 +612,13 @@ public:
Statement *_body;
Statement *finalbody;
- TryFinallyStatement(Loc loc, Statement *body, Statement *finalbody);
+ Statement *tryBody; // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion
+ bool bodyFallsThru; // true if _body falls through to finally
+
static TryFinallyStatement *create(Loc loc, Statement *body, Statement *finalbody);
- Statement *syntaxCopy();
- bool hasBreak();
- bool hasContinue();
+ TryFinallyStatement *syntaxCopy();
+ bool hasBreak() const;
+ bool hasContinue() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -641,9 +629,7 @@ public:
TOK tok;
Statement *statement;
- ScopeGuardStatement(Loc loc, TOK tok, Statement *statement);
- Statement *syntaxCopy();
- Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally);
+ ScopeGuardStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -656,8 +642,7 @@ public:
// wasn't present in source code
bool internalThrow;
- ThrowStatement(Loc loc, Expression *exp);
- Statement *syntaxCopy();
+ ThrowStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -667,9 +652,7 @@ class DebugStatement : public Statement
public:
Statement *statement;
- DebugStatement(Loc loc, Statement *statement);
- Statement *syntaxCopy();
- Statements *flatten(Scope *sc);
+ DebugStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -678,13 +661,12 @@ class GotoStatement : public Statement
public:
Identifier *ident;
LabelDsymbol *label;
+ Statement *tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion
TryFinallyStatement *tf;
ScopeGuardStatement *os;
VarDeclaration *lastVar;
- GotoStatement(Loc loc, Identifier *ident);
- Statement *syntaxCopy();
- bool checkLabel();
+ GotoStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -694,19 +676,15 @@ class LabelStatement : public Statement
public:
Identifier *ident;
Statement *statement;
+ Statement *tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion
TryFinallyStatement *tf;
ScopeGuardStatement *os;
VarDeclaration *lastVar;
Statement *gotoTarget; // interpret
-
+ void* extra; // used by Statement_toIR()
bool breaks; // someone did a 'break ident'
- LabelStatement(Loc loc, Identifier *ident, Statement *statement);
- Statement *syntaxCopy();
- Statements *flatten(Scope *sc);
- Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally);
-
- LabelStatement *isLabelStatement() { return this; }
+ LabelStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -716,7 +694,9 @@ class LabelDsymbol : public Dsymbol
public:
LabelStatement *statement;
- LabelDsymbol(Identifier *ident);
+ bool deleted; // set if rewritten to return in foreach delegate
+ bool iasm; // set if used by inline assembler
+
static LabelDsymbol *create(Identifier *ident);
LabelDsymbol *isLabel();
void accept(Visitor *v) { v->visit(this); }
@@ -729,8 +709,7 @@ class AsmStatement : public Statement
public:
Token *tokens;
- AsmStatement(Loc loc, Token *tokens);
- Statement *syntaxCopy();
+ AsmStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -743,8 +722,7 @@ public:
bool refparam; // true if function parameter is referenced
bool naked; // true if function is to be naked
- InlineAsmStatement(Loc loc, Token *tokens);
- Statement *syntaxCopy();
+ InlineAsmStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -762,8 +740,7 @@ public:
Identifiers *labels; // list of goto labels
GotoStatements *gotos; // of the goto labels, the equivalent statements they represent
- GccAsmStatement(Loc loc, Token *tokens);
- Statement *syntaxCopy();
+ GccAsmStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -773,9 +750,7 @@ class CompoundAsmStatement : public CompoundStatement
public:
StorageClass stc; // postfix attributes like nothrow/pure/@trusted
- CompoundAsmStatement(Loc loc, Statements *s, StorageClass stc);
CompoundAsmStatement *syntaxCopy();
- Statements *flatten(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@@ -785,8 +760,7 @@ class ImportStatement : public Statement
public:
Dsymbols *imports; // Array of Import's
- ImportStatement(Loc loc, Dsymbols *imports);
- Statement *syntaxCopy();
+ ImportStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
diff --git a/gcc/d/dmd/statement_rewrite_walker.d b/gcc/d/dmd/statement_rewrite_walker.d
new file mode 100644
index 00000000000..2f6605c4354
--- /dev/null
+++ b/gcc/d/dmd/statement_rewrite_walker.d
@@ -0,0 +1,194 @@
+/**
+ * Provides a visitor for statements that allows rewriting the currently visited node.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statement_rewrite_walker.d, _statement_rewrite_walker.d)
+ * Documentation: https://dlang.org/phobos/dmd_statement_rewrite_walker.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statement_rewrite_walker.d
+ */
+
+module dmd.statement_rewrite_walker;
+
+import core.stdc.stdio;
+
+import dmd.statement;
+import dmd.visitor;
+
+
+/** A visitor to walk entire statements and provides ability to replace any sub-statements.
+ */
+extern (C++) class StatementRewriteWalker : SemanticTimePermissiveVisitor
+{
+ alias visit = SemanticTimePermissiveVisitor.visit;
+
+ /* Point the currently visited statement.
+ * By using replaceCurrent() method, you can replace AST during walking.
+ */
+ Statement* ps;
+
+public:
+ final void visitStmt(ref Statement s)
+ {
+ ps = &s;
+ s.accept(this);
+ }
+
+ final void replaceCurrent(Statement s)
+ {
+ *ps = s;
+ }
+
+ override void visit(PeelStatement s)
+ {
+ if (s.s)
+ visitStmt(s.s);
+ }
+
+ override void visit(CompoundStatement s)
+ {
+ if (s.statements && s.statements.dim)
+ {
+ for (size_t i = 0; i < s.statements.dim; i++)
+ {
+ if ((*s.statements)[i])
+ visitStmt((*s.statements)[i]);
+ }
+ }
+ }
+
+ override void visit(CompoundDeclarationStatement s)
+ {
+ visit(cast(CompoundStatement)s);
+ }
+
+ override void visit(UnrolledLoopStatement s)
+ {
+ if (s.statements && s.statements.dim)
+ {
+ for (size_t i = 0; i < s.statements.dim; i++)
+ {
+ if ((*s.statements)[i])
+ visitStmt((*s.statements)[i]);
+ }
+ }
+ }
+
+ override void visit(ScopeStatement s)
+ {
+ if (s.statement)
+ visitStmt(s.statement);
+ }
+
+ override void visit(WhileStatement s)
+ {
+ if (s._body)
+ visitStmt(s._body);
+ }
+
+ override void visit(DoStatement s)
+ {
+ if (s._body)
+ visitStmt(s._body);
+ }
+
+ override void visit(ForStatement s)
+ {
+ if (s._init)
+ visitStmt(s._init);
+ if (s._body)
+ visitStmt(s._body);
+ }
+
+ override void visit(ForeachStatement s)
+ {
+ if (s._body)
+ visitStmt(s._body);
+ }
+
+ override void visit(ForeachRangeStatement s)
+ {
+ if (s._body)
+ visitStmt(s._body);
+ }
+
+ override void visit(IfStatement s)
+ {
+ if (s.ifbody)
+ visitStmt(s.ifbody);
+ if (s.elsebody)
+ visitStmt(s.elsebody);
+ }
+
+ override void visit(SwitchStatement s)
+ {
+ if (s._body)
+ visitStmt(s._body);
+ }
+
+ override void visit(CaseStatement s)
+ {
+ if (s.statement)
+ visitStmt(s.statement);
+ }
+
+ override void visit(CaseRangeStatement s)
+ {
+ if (s.statement)
+ visitStmt(s.statement);
+ }
+
+ override void visit(DefaultStatement s)
+ {
+ if (s.statement)
+ visitStmt(s.statement);
+ }
+
+ override void visit(SynchronizedStatement s)
+ {
+ if (s._body)
+ visitStmt(s._body);
+ }
+
+ override void visit(WithStatement s)
+ {
+ if (s._body)
+ visitStmt(s._body);
+ }
+
+ override void visit(TryCatchStatement s)
+ {
+ if (s._body)
+ visitStmt(s._body);
+ if (s.catches && s.catches.dim)
+ {
+ for (size_t i = 0; i < s.catches.dim; i++)
+ {
+ Catch c = (*s.catches)[i];
+ if (c && c.handler)
+ visitStmt(c.handler);
+ }
+ }
+ }
+
+ override void visit(TryFinallyStatement s)
+ {
+ if (s._body)
+ visitStmt(s._body);
+ if (s.finalbody)
+ visitStmt(s.finalbody);
+ }
+
+ override void visit(DebugStatement s)
+ {
+ if (s.statement)
+ visitStmt(s.statement);
+ }
+
+ override void visit(LabelStatement s)
+ {
+ if (s.statement)
+ visitStmt(s.statement);
+ }
+}
diff --git a/gcc/d/dmd/statementsem.c b/gcc/d/dmd/statementsem.c
deleted file mode 100644
index 24e534e50a8..00000000000
--- a/gcc/d/dmd/statementsem.c
+++ /dev/null
@@ -1,3875 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-#include "root/checkedint.h"
-
-#include "errors.h"
-#include "statement.h"
-#include "attrib.h"
-#include "expression.h"
-#include "cond.h"
-#include "init.h"
-#include "staticassert.h"
-#include "module.h"
-#include "scope.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "id.h"
-#include "enum.h"
-#include "template.h"
-#include "import.h"
-#include "target.h"
-#include "visitor.h"
-
-StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f);
-bool checkReturnEscapeRef(Scope *sc, Expression *e, bool gag);
-bool checkThrowEscape(Scope *sc, Expression *e, bool gag);
-LabelStatement *checkLabeledLoop(Scope *sc, Statement *statement);
-Identifier *fixupLabelName(Scope *sc, Identifier *ident);
-FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL);
-VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e);
-Expression *checkAssignmentAsCondition(Expression *e);
-TypeIdentifier *getThrowable();
-
-int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow);
-
-class StatementSemanticVisitor : public Visitor
-{
-public:
- Statement *result;
- Scope *sc;
-
- StatementSemanticVisitor(Scope *sc)
- {
- this->result = NULL;
- this->sc = sc;
- }
-
-private:
- void setError()
- {
- result = new ErrorStatement();
- }
-
-public:
- void visit(Statement *s)
- {
- result = s;
- }
-
- void visit(ErrorStatement *s)
- {
- result = s;
- }
-
- void visit(PeelStatement *s)
- {
- /* "peel" off this wrapper, and don't run semantic()
- * on the result.
- */
- result = s->s;
- }
-
- void visit(ExpStatement *s)
- {
- if (s->exp)
- {
- //printf("ExpStatement::semantic() %s\n", s->exp->toChars());
-
- // Allow CommaExp in ExpStatement because return isn't used
- if (s->exp->op == TOKcomma)
- ((CommaExp *)s->exp)->allowCommaExp = true;
-
- s->exp = expressionSemantic(s->exp, sc);
- s->exp = resolveProperties(sc, s->exp);
- s->exp = s->exp->addDtorHook(sc);
- if (checkNonAssignmentArrayOp(s->exp))
- s->exp = new ErrorExp();
- if (FuncDeclaration *f = isFuncAddress(s->exp))
- {
- if (f->checkForwardRef(s->exp->loc))
- s->exp = new ErrorExp();
- }
- if (discardValue(s->exp))
- s->exp = new ErrorExp();
-
- s->exp = s->exp->optimize(WANTvalue);
- s->exp = checkGC(sc, s->exp);
- if (s->exp->op == TOKerror)
- return setError();
- }
- result = s;
- }
-
- void visit(CompileStatement *cs)
- {
- //printf("CompileStatement::semantic() %s\n", cs->exp->toChars());
- Statements *a = cs->flatten(sc);
- if (!a)
- return;
- Statement *s = new CompoundStatement(cs->loc, a);
- result = statementSemantic(s, sc);
- }
-
- void visit(CompoundStatement *cs)
- {
- //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
- for (size_t i = 0; i < cs->statements->length; )
- {
- Statement *s = (*cs->statements)[i];
- if (s)
- {
- Statements *flt = s->flatten(sc);
- if (flt)
- {
- cs->statements->remove(i);
- cs->statements->insert(i, flt);
- continue;
- }
- s = statementSemantic(s, sc);
- (*cs->statements)[i] = s;
- if (s)
- {
- Statement *sentry;
- Statement *sexception;
- Statement *sfinally;
-
- (*cs->statements)[i] = s->scopeCode(sc, &sentry, &sexception, &sfinally);
- if (sentry)
- {
- sentry = statementSemantic(sentry, sc);
- cs->statements->insert(i, sentry);
- i++;
- }
- if (sexception)
- sexception = statementSemantic(sexception, sc);
- if (sexception)
- {
- if (i + 1 == cs->statements->length && !sfinally)
- {
- }
- else
- {
- /* Rewrite:
- * s; s1; s2;
- * As:
- * s;
- * try { s1; s2; }
- * catch (Throwable __o)
- * { sexception; throw __o; }
- */
- Statements *a = new Statements();
- for (size_t j = i + 1; j < cs->statements->length; j++)
- {
- a->push((*cs->statements)[j]);
- }
- Statement *body = new CompoundStatement(Loc(), a);
- body = new ScopeStatement(Loc(), body, Loc());
-
- Identifier *id = Identifier::generateId("__o");
-
- Statement *handler = new PeelStatement(sexception);
- if (blockExit(sexception, sc->func, false) & BEfallthru)
- {
- ThrowStatement *ts = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id));
- ts->internalThrow = true;
- handler = new CompoundStatement(Loc(), handler, ts);
- }
-
- Catches *catches = new Catches();
- Catch *ctch = new Catch(Loc(), getThrowable(), id, handler);
- ctch->internalCatch = true;
- catches->push(ctch);
-
- s = new TryCatchStatement(Loc(), body, catches);
- if (sfinally)
- s = new TryFinallyStatement(Loc(), s, sfinally);
- s = statementSemantic(s, sc);
-
- cs->statements->setDim(i + 1);
- cs->statements->push(s);
- break;
- }
- }
- else if (sfinally)
- {
- if (0 && i + 1 == cs->statements->length)
- {
- cs->statements->push(sfinally);
- }
- else
- {
- /* Rewrite:
- * s; s1; s2;
- * As:
- * s; try { s1; s2; } finally { sfinally; }
- */
- Statements *a = new Statements();
- for (size_t j = i + 1; j < cs->statements->length; j++)
- {
- a->push((*cs->statements)[j]);
- }
- Statement *body = new CompoundStatement(Loc(), a);
- s = new TryFinallyStatement(Loc(), body, sfinally);
- s = statementSemantic(s, sc);
- cs->statements->setDim(i + 1);
- cs->statements->push(s);
- break;
- }
- }
- }
- else
- {
- /* Remove NULL statements from the list.
- */
- cs->statements->remove(i);
- continue;
- }
- }
- i++;
- }
- for (size_t i = 0; i < cs->statements->length; ++i)
- {
- Lagain:
- Statement *s = (*cs->statements)[i];
- if (!s)
- continue;
-
- Statement *se = s->isErrorStatement();
- if (se)
- {
- result = se;
- return;
- }
-
- /* Bugzilla 11653: 'semantic' may return another CompoundStatement
- * (eg. CaseRangeStatement), so flatten it here.
- */
- Statements *flt = s->flatten(sc);
- if (flt)
- {
- cs->statements->remove(i);
- cs->statements->insert(i, flt);
- if (cs->statements->length <= i)
- break;
- goto Lagain;
- }
- }
- if (cs->statements->length == 1)
- {
- result = (*cs->statements)[0];
- return;
- }
- result = cs;
- }
-
- void visit(UnrolledLoopStatement *uls)
- {
- //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
- Scope *scd = sc->push();
- scd->sbreak = uls;
- scd->scontinue = uls;
-
- Statement *serror = NULL;
- for (size_t i = 0; i < uls->statements->length; i++)
- {
- Statement *s = (*uls->statements)[i];
- if (s)
- {
- //printf("[%d]: %s\n", i, s->toChars());
- s = statementSemantic(s, scd);
- (*uls->statements)[i] = s;
-
- if (s && !serror)
- serror = s->isErrorStatement();
- }
- }
-
- scd->pop();
- result = serror ? serror : uls;
- }
-
- void visit(ScopeStatement *ss)
- {
- //printf("ScopeStatement::semantic(sc = %p)\n", sc);
- if (ss->statement)
- {
- ScopeDsymbol *sym = new ScopeDsymbol();
- sym->parent = sc->scopesym;
- sym->endlinnum = ss->endloc.linnum;
- sc = sc->push(sym);
-
- Statements *a = ss->statement->flatten(sc);
- if (a)
- {
- ss->statement = new CompoundStatement(ss->loc, a);
- }
-
- ss->statement = statementSemantic(ss->statement, sc);
- if (ss->statement)
- {
- if (ss->statement->isErrorStatement())
- {
- sc->pop();
- result = ss->statement;
- return;
- }
-
- Statement *sentry;
- Statement *sexception;
- Statement *sfinally;
-
- ss->statement = ss->statement->scopeCode(sc, &sentry, &sexception, &sfinally);
- assert(!sentry);
- assert(!sexception);
- if (sfinally)
- {
- //printf("adding sfinally\n");
- sfinally = statementSemantic(sfinally, sc);
- ss->statement = new CompoundStatement(ss->loc, ss->statement, sfinally);
- }
- }
-
- sc->pop();
- }
- result = ss;
- }
-
- void visit(ForwardingStatement *ss)
- {
- assert(ss->sym);
- for (Scope *csc = sc; !ss->sym->forward; csc = csc->enclosing)
- {
- assert(csc);
- ss->sym->forward = csc->scopesym;
- }
- sc = sc->push(ss->sym);
- sc->sbreak = ss;
- sc->scontinue = ss;
- ss->statement = statementSemantic(ss->statement, sc);
- sc = sc->pop();
- result = ss->statement;
- }
-
- void visit(WhileStatement *ws)
- {
- /* Rewrite as a for(;condition;) loop
- */
- Statement *s = new ForStatement(ws->loc, NULL, ws->condition, NULL, ws->_body, ws->endloc);
- s = statementSemantic(s, sc);
- result = s;
- }
-
- void visit(DoStatement *ds)
- {
- sc->noctor++;
- if (ds->_body)
- ds->_body = semanticScope(ds->_body, sc, ds, ds);
- sc->noctor--;
-
- if (ds->condition->op == TOKdotid)
- ((DotIdExp *)ds->condition)->noderef = true;
-
- // check in syntax level
- ds->condition = checkAssignmentAsCondition(ds->condition);
-
- ds->condition = expressionSemantic(ds->condition, sc);
- ds->condition = resolveProperties(sc, ds->condition);
- if (checkNonAssignmentArrayOp(ds->condition))
- ds->condition = new ErrorExp();
- ds->condition = ds->condition->optimize(WANTvalue);
- ds->condition = checkGC(sc, ds->condition);
-
- ds->condition = ds->condition->toBoolean(sc);
-
- if (ds->condition->op == TOKerror)
- return setError();
-
- if (ds->_body && ds->_body->isErrorStatement())
- {
- result = ds->_body;
- return;
- }
-
- result = ds;
- }
-
- void visit(ForStatement *fs)
- {
- //printf("ForStatement::semantic %s\n", toChars());
-
- if (fs->_init)
- {
- /* Rewrite:
- * for (auto v1 = i1, v2 = i2; condition; increment) { ... }
- * to:
- * { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
- * then lowered to:
- * auto v1 = i1;
- * try {
- * auto v2 = i2;
- * try {
- * for (; condition; increment) { ... }
- * } finally { v2.~this(); }
- * } finally { v1.~this(); }
- */
- Statements *ainit = new Statements();
- ainit->push(fs->_init);
- fs->_init = NULL;
- ainit->push(fs);
- Statement *s = new CompoundStatement(fs->loc, ainit);
- s = new ScopeStatement(fs->loc, s, fs->endloc);
- s = statementSemantic(s, sc);
- if (!s->isErrorStatement())
- {
- if (LabelStatement *ls = checkLabeledLoop(sc, fs))
- ls->gotoTarget = fs;
- fs->relatedLabeled = s;
- }
- result = s;
- return;
- }
- assert(fs->_init == NULL);
-
- ScopeDsymbol *sym = new ScopeDsymbol();
- sym->parent = sc->scopesym;
- sym->endlinnum = fs->endloc.linnum;
- sc = sc->push(sym);
-
- sc->noctor++;
- if (fs->condition)
- {
- if (fs->condition->op == TOKdotid)
- ((DotIdExp *)fs->condition)->noderef = true;
-
- // check in syntax level
- fs->condition = checkAssignmentAsCondition(fs->condition);
-
- fs->condition = expressionSemantic(fs->condition, sc);
- fs->condition = resolveProperties(sc, fs->condition);
- if (checkNonAssignmentArrayOp(fs->condition))
- fs->condition = new ErrorExp();
- fs->condition = fs->condition->optimize(WANTvalue);
- fs->condition = checkGC(sc, fs->condition);
- fs->condition = fs->condition->toBoolean(sc);
- }
- if (fs->increment)
- {
- if (fs->increment->op == TOKcomma)
- ((CommaExp *)fs->increment)->allowCommaExp = true;
- fs->increment = expressionSemantic(fs->increment, sc);
- fs->increment = resolveProperties(sc, fs->increment);
- if (checkNonAssignmentArrayOp(fs->increment))
- fs->increment = new ErrorExp();
- fs->increment = fs->increment->optimize(WANTvalue);
- fs->increment = checkGC(sc, fs->increment);
- }
-
- sc->sbreak = fs;
- sc->scontinue = fs;
- if (fs->_body)
- fs->_body = semanticNoScope(fs->_body, sc);
- sc->noctor--;
-
- sc->pop();
-
- if ((fs->condition && fs->condition->op == TOKerror) ||
- (fs->increment && fs->increment->op == TOKerror) ||
- (fs->_body && fs->_body->isErrorStatement()))
- return setError();
-
- result = fs;
- }
-
- /***********************
- * Declares a unrolled `foreach` loop variable or a `static foreach` variable.
- *
- * Params:
- * storageClass = The storage class of the variable.
- * type = The declared type of the variable.
- * ident = The name of the variable.
- * e = The initializer of the variable (i.e. the current element of the looped over aggregate).
- * t = The type of the initializer.
- * Returns:
- * `true` iff the declaration was successful.
- */
- bool declareVariable(ForeachStatement *fs, Type *paramtype, TupleExp *te,
- bool needExpansion, bool isStatic, Statements *statements, Dsymbols *declarations,
- StorageClass storageClass, Type *type, Identifier *ident, Expression *e, Type *t)
- {
- Loc loc = fs->loc;
- if (storageClass & (STCout | STClazy) ||
- (storageClass & STCref && !te))
- {
- fs->error("no storage class for value %s", ident->toChars());
- return false;
- }
- Declaration *var;
- if (e)
- {
- Type *tb = e->type->toBasetype();
- Dsymbol *ds = NULL;
- if (!(storageClass & STCmanifest))
- {
- if ((isStatic || tb->ty == Tfunction || tb->ty == Tsarray || storageClass & STCalias) && e->op == TOKvar)
- ds = ((VarExp *)e)->var;
- else if (e->op == TOKtemplate)
- ds = ((TemplateExp *)e)->td;
- else if (e->op == TOKscope)
- ds = ((ScopeExp *)e)->sds;
- else if (e->op == TOKfunction)
- {
- FuncExp *fe = (FuncExp *)e;
- ds = fe->td ? (Dsymbol *)fe->td : fe->fd;
- }
- }
- else if (storageClass & STCalias)
- {
- fs->error("foreach loop variable cannot be both enum and alias");
- return false;
- }
-
- if (ds)
- {
- var = new AliasDeclaration(loc, ident, ds);
- if (storageClass & STCref)
- {
- fs->error("symbol %s cannot be ref", ds->toChars());
- return false;
- }
- if (paramtype)
- {
- fs->error("cannot specify element type for symbol %s", ds->toChars());
- return false;
- }
- }
- else if (e->op == TOKtype)
- {
- var = new AliasDeclaration(loc, ident, e->type);
- if (paramtype)
- {
- fs->error("cannot specify element type for type %s", e->type->toChars());
- return false;
- }
- }
- else
- {
- e = resolveProperties(sc, e);
- Initializer *ie = new ExpInitializer(Loc(), e);
- VarDeclaration *v = new VarDeclaration(loc, type, ident, ie);
- if (storageClass & STCref)
- v->storage_class |= STCref | STCforeach;
- if (isStatic || storageClass & STCmanifest || e->isConst() ||
- e->op == TOKstring ||
- e->op == TOKstructliteral ||
- e->op == TOKarrayliteral)
- {
- if (v->storage_class & STCref)
- {
- if (!isStatic || !needExpansion)
- {
- fs->error("constant value %s cannot be ref", ie->toChars());
- }
- else
- {
- fs->error("constant value %s cannot be ref", ident->toChars());
- }
- return false;
- }
- else
- v->storage_class |= STCmanifest;
- }
- var = v;
- }
- }
- else
- {
- var = new AliasDeclaration(loc, ident, t);
- if (paramtype)
- {
- fs->error("cannot specify element type for symbol %s", fs->toChars());
- return false;
- }
- }
- if (isStatic)
- var->storage_class |= STClocal;
- if (statements)
- statements->push(new ExpStatement(loc, var));
- else if (declarations)
- declarations->push(var);
- else
- assert(0);
- return true;
- }
-
- bool makeTupleForeachBody(ForeachStatement *fs, size_t k,
- Type *paramtype, TupleExp *te, TypeTuple *tuple,
- bool needExpansion, bool isStatic, bool isDecl,
- Statements *statements, Dsymbols *declarations, Dsymbols *dbody)
- {
- Loc loc = fs->loc;
- Expression *e = NULL;
- Type *t = NULL;
- if (te)
- e = (*te->exps)[k];
- else
- t = Parameter::getNth(tuple->arguments, k)->type;
- Parameter *p = (*fs->parameters)[0];
- Statements *stmts = (isDecl) ? NULL : new Statements();
- Dsymbols *decls = (isDecl) ? new Dsymbols() : NULL;
-
- size_t dim = fs->parameters->length;
- if (!needExpansion && dim == 2)
- {
- // Declare key
- if (p->storageClass & (STCout | STCref | STClazy))
- {
- fs->error("no storage class for key %s", p->ident->toChars());
- return false;
- }
- if (isStatic)
- {
- if (!p->type)
- {
- p->type = Type::tsize_t;
- }
- }
- p->type = typeSemantic(p->type, loc, sc);
-
- if (!p->type->isintegral())
- {
- fs->error("foreach: key cannot be of non-integral type `%s`",
- p->type->toChars());
- return false;
- }
-
- unsigned length = te ? te->exps->length : tuple->arguments->length;
- IntRange dimrange = IntRange(SignExtendedNumber(length)).cast(Type::tsize_t);
- // https://issues.dlang.org/show_bug.cgi?id=12504
- dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
- if (!IntRange::fromType(p->type).contains(dimrange))
- {
- fs->error("index type `%s` cannot cover index range 0..%llu",
- p->type->toChars(), (ulonglong)length);
- return false;
- }
- Initializer *ie = new ExpInitializer(Loc(), new IntegerExp(k));
- VarDeclaration *var = new VarDeclaration(loc, p->type, p->ident, ie);
- var->storage_class |= STCmanifest;
- if (isStatic)
- var->storage_class |= STClocal;
- if (!isDecl)
- stmts->push(new ExpStatement(loc, var));
- else
- decls->push(var);
- p = (*fs->parameters)[1]; // value
- }
-
- if (!isStatic || !needExpansion)
- {
- // Declare value
- if (!declareVariable(fs, paramtype, te, needExpansion, isStatic, stmts, decls,
- p->storageClass, p->type, p->ident, e, t))
- {
- return false;
- }
- }
- else
- {
- // expand tuples into multiple `static foreach` variables.
- assert(e && !t);
- Identifier *ident = Identifier::generateId("__value");
- declareVariable(fs, paramtype, te, needExpansion, isStatic, stmts, decls,
- 0, e->type, ident, e, NULL);
- Identifier *field = Identifier::idPool("tuple");
- Expression *access = new DotIdExp(loc, e, field);
- access = expressionSemantic(access, sc);
- if (!tuple)
- return false;
- //printf("%s\n", tuple->toChars());
- for (size_t l = 0; l < dim; l++)
- {
- Parameter *cp = (*fs->parameters)[l];
- Expression *init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type::tsize_t));
- init_ = expressionSemantic(init_, sc);
- assert(init_->type);
- declareVariable(fs, paramtype, te, needExpansion, isStatic, stmts, decls,
- p->storageClass, init_->type, cp->ident, init_, NULL);
- }
- }
- Statement *fwdstmt = NULL;
- Dsymbol *fwddecl = NULL;
- if (!isDecl)
- {
- if (fs->_body)
- stmts->push(fs->_body->syntaxCopy());
- fwdstmt = new CompoundStatement(loc, stmts);
- }
- else
- {
- decls->append(Dsymbol::arraySyntaxCopy(dbody));
- }
- if (!isStatic)
- {
- fwdstmt = new ScopeStatement(loc, fwdstmt, fs->endloc);
- }
- else if (!isDecl)
- {
- fwdstmt = new ForwardingStatement(loc, fwdstmt);
- }
- else
- {
- fwddecl = new ForwardingAttribDeclaration(decls);
- }
-
- if (statements)
- statements->push(fwdstmt);
- else if (declarations)
- declarations->push(fwddecl);
- else
- assert(0);
- return true;
- }
-
- /*******************
- * Type check and unroll `foreach` over an expression tuple as well
- * as `static foreach` statements and `static foreach`
- * declarations. For `static foreach` statements and `static
- * foreach` declarations, the visitor interface is used (and the
- * result is written into the `result` field.) For `static
- * foreach` declarations, the resulting Dsymbols* are returned
- * directly.
- *
- * The unrolled body is wrapped into a
- * - UnrolledLoopStatement, for `foreach` over an expression tuple.
- * - ForwardingStatement, for `static foreach` statements.
- * - ForwardingAttribDeclaration, for `static foreach` declarations.
- *
- * `static foreach` variables are declared as `STClocal`, such
- * that they are inserted into the local symbol tables of the
- * forwarding constructs instead of forwarded. For `static
- * foreach` with multiple foreach loop variables whose aggregate
- * has been lowered into a sequence of tuples, this function
- * expands the tuples into multiple `STClocal` `static foreach`
- * variables.
- */
- bool makeTupleForeach(ForeachStatement *fs, bool needExpansion, bool isStatic, bool isDecl,
- Statements *statements, Dsymbols *declarations, Dsymbols *dbody)
- {
- Loc loc = fs->loc;
- size_t dim = fs->parameters->length;
- if (!needExpansion && (dim < 1 || dim > 2))
- {
- fs->error("only one (value) or two (key,value) arguments for tuple foreach");
- return false;
- }
-
- Type *paramtype = (*fs->parameters)[dim-1]->type;
- if (paramtype)
- {
- paramtype = typeSemantic(paramtype, loc, sc);
- if (paramtype->ty == Terror)
- return false;
- }
-
- Type *tab = fs->aggr->type->toBasetype();
- TypeTuple *tuple = (TypeTuple *)tab;
- //printf("aggr: op = %d, %s\n", fs->aggr->op, fs->aggr->toChars());
- size_t n;
- TupleExp *te = NULL;
- if (fs->aggr->op == TOKtuple) // expression tuple
- {
- te = (TupleExp *)fs->aggr;
- n = te->exps->length;
- }
- else if (fs->aggr->op == TOKtype) // type tuple
- {
- n = Parameter::dim(tuple->arguments);
- }
- else
- assert(0);
- for (size_t j = 0; j < n; j++)
- {
- size_t k = (fs->op == TOKforeach) ? j : n - 1 - j;
- if (!makeTupleForeachBody(fs, k, paramtype, te, tuple,
- needExpansion, isStatic, isDecl,
- statements, declarations, dbody))
- return false;
- }
- return true;
- }
-
- Dsymbols *makeTupleForeachStaticDecl(ForeachStatement *fs, Dsymbols *dbody, bool needExpansion)
- {
- assert(sc);
- Dsymbols *declarations = new Dsymbols();
- if (!makeTupleForeach(fs, needExpansion, true, true, NULL, declarations, dbody))
- return NULL;
-
- return declarations;
- }
-
- void makeTupleForeachStatic(ForeachStatement *fs, bool needExpansion)
- {
- Loc loc = fs->loc;
- assert(sc);
- Statements *statements = new Statements();
- if (!makeTupleForeach(fs, needExpansion, true, false, statements, NULL, NULL))
- return setError();
-
- result = new CompoundStatement(loc, statements);
- }
-
- void visit(ForeachStatement *fs)
- {
- //printf("ForeachStatement::semantic() %p\n", fs);
- ScopeDsymbol *sym;
- Statement *s = fs;
- Loc loc = fs->loc;
- size_t dim = fs->parameters->length;
- TypeAArray *taa = NULL;
- Dsymbol *sapply = NULL;
-
- Type *tn = NULL;
- Type *tnv = NULL;
-
- fs->func = sc->func;
- if (fs->func->fes)
- fs->func = fs->func->fes->func;
-
- VarDeclaration *vinit = NULL;
- fs->aggr = expressionSemantic(fs->aggr, sc);
- fs->aggr = resolveProperties(sc, fs->aggr);
- fs->aggr = fs->aggr->optimize(WANTvalue);
- if (fs->aggr->op == TOKerror)
- return setError();
-
- Expression *oaggr = fs->aggr;
- if (fs->aggr->type && fs->aggr->type->toBasetype()->ty == Tstruct &&
- ((TypeStruct *)(fs->aggr->type->toBasetype()))->sym->dtor &&
- fs->aggr->op != TOKtype && !fs->aggr->isLvalue())
- {
- // Bugzilla 14653: Extend the life of rvalue aggregate till the end of foreach.
- vinit = copyToTemp(STCrvalue, "__aggr", fs->aggr);
- dsymbolSemantic(vinit, sc);
- fs->aggr = new VarExp(fs->aggr->loc, vinit);
- }
-
- if (!inferAggregate(fs, sc, sapply))
- {
- const char *msg = "";
- if (fs->aggr->type && isAggregate(fs->aggr->type))
- {
- msg = ", define opApply(), range primitives, or use .tupleof";
- }
- fs->error("invalid foreach aggregate %s%s", oaggr->toChars(), msg);
- return setError();
- }
-
- Dsymbol* sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
-
- /* Check for inference errors
- */
- if (!inferApplyArgTypes(fs, sc, sapply))
- {
- /**
- Try and extract the parameter count of the opApply callback function, e.g.:
- int opApply(int delegate(int, float)) => 2 args
- */
- bool foundMismatch = false;
- size_t foreachParamCount = 0;
- if (sapplyOld)
- {
- if (FuncDeclaration *fd = sapplyOld->isFuncDeclaration())
- {
- ParameterList fparameters = fd->getParameterList();
-
- if (fparameters.length() == 1)
- {
- // first param should be the callback function
- Parameter *fparam = fparameters[0];
- if ((fparam->type->ty == Tpointer || fparam->type->ty == Tdelegate) &&
- fparam->type->nextOf()->ty == Tfunction)
- {
- TypeFunction *tf = (TypeFunction *)fparam->type->nextOf();
- foreachParamCount = tf->parameterList.length();
- foundMismatch = true;
- }
- }
- }
- }
-
- //printf("dim = %d, parameters->length = %d\n", dim, fs->parameters->length);
- if (foundMismatch && dim != foreachParamCount)
- {
- const char *plural = foreachParamCount > 1 ? "s" : "";
- fs->error("cannot infer argument types, expected %d argument%s, not %d",
- foreachParamCount, plural, dim);
- }
- else
- fs->error("cannot uniquely infer foreach argument types");
-
- return setError();
- }
-
- Type *tab = fs->aggr->type->toBasetype();
-
- if (tab->ty == Ttuple) // don't generate new scope for tuple loops
- {
- Statements *statements = new Statements();
- if (!makeTupleForeach(fs, false, false, false, statements, NULL, NULL))
- return setError();
-
- result = new UnrolledLoopStatement(loc, statements);
- if (LabelStatement *ls = checkLabeledLoop(sc, fs))
- ls->gotoTarget = result;
- if (fs->aggr->op == TOKtuple)
- {
- TupleExp *te = (TupleExp *)fs->aggr;
- if (te->e0)
- result = new CompoundStatement(loc, new ExpStatement(te->e0->loc, te->e0), result);
- }
- if (vinit)
- result = new CompoundStatement(loc, new ExpStatement(loc, vinit), result);
- result = statementSemantic(result, sc);
- return;
- }
-
- sym = new ScopeDsymbol();
- sym->parent = sc->scopesym;
- sym->endlinnum = fs->endloc.linnum;
- Scope *sc2 = sc->push(sym);
-
- sc2->noctor++;
-
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *p = (*fs->parameters)[i];
- if (p->storageClass & STCmanifest)
- {
- fs->error("cannot declare enum loop variables for non-unrolled foreach");
- }
- if (p->storageClass & STCalias)
- {
- fs->error("cannot declare alias loop variables for non-unrolled foreach");
- }
- }
-
- switch (tab->ty)
- {
- case Tarray:
- case Tsarray:
- {
- if (fs->checkForArgTypes())
- {
- result = fs;
- return;
- }
-
- if (dim < 1 || dim > 2)
- {
- fs->error("only one or two arguments for array foreach");
- goto Lerror2;
- }
-
- // Finish semantic on all foreach parameter types.
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *p = (*fs->parameters)[i];
- p->type = typeSemantic(p->type, loc, sc2);
- p->type = p->type->addStorageClass(p->storageClass);
- }
-
- tn = tab->nextOf()->toBasetype();
-
- if (dim == 2)
- {
- Type *tindex = (*fs->parameters)[0]->type;
- if (!tindex->isintegral())
- {
- fs->error("foreach: key cannot be of non-integral type `%s`",
- tindex->toChars());
- goto Lerror2;
- }
- /* What cases to deprecate implicit conversions for:
- * 1. foreach aggregate is a dynamic array
- * 2. foreach body is lowered to _aApply (see special case below).
- */
- Type *tv = (*fs->parameters)[1]->type->toBasetype();
- if ((tab->ty == Tarray ||
- (tn->ty != tv->ty &&
- (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar) &&
- (tv->ty == Tchar || tv->ty == Twchar || tv->ty == Tdchar))) &&
- !Type::tsize_t->implicitConvTo(tindex))
- {
- fs->deprecation("foreach: loop index implicitly converted from `size_t` to `%s`",
- tindex->toChars());
- }
- }
-
- /* Look for special case of parsing char types out of char type
- * array.
- */
- if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar)
- {
- int i = (dim == 1) ? 0 : 1; // index of value
- Parameter *p = (*fs->parameters)[i];
- tnv = p->type->toBasetype();
- if (tnv->ty != tn->ty &&
- (tnv->ty == Tchar || tnv->ty == Twchar || tnv->ty == Tdchar))
- {
- if (p->storageClass & STCref)
- {
- fs->error("foreach: value of UTF conversion cannot be ref");
- goto Lerror2;
- }
- if (dim == 2)
- {
- p = (*fs->parameters)[0];
- if (p->storageClass & STCref)
- {
- fs->error("foreach: key cannot be ref");
- goto Lerror2;
- }
- }
- goto Lapply;
- }
- }
-
- for (size_t i = 0; i < dim; i++)
- {
- // Declare parameterss
- Parameter *p = (*fs->parameters)[i];
- VarDeclaration *var;
-
- if (dim == 2 && i == 0)
- {
- var = new VarDeclaration(loc, p->type->mutableOf(), Identifier::generateId("__key"), NULL);
- var->storage_class |= STCtemp | STCforeach;
- if (var->storage_class & (STCref | STCout))
- var->storage_class |= STCnodtor;
-
- fs->key = var;
- if (p->storageClass & STCref)
- {
- if (var->type->constConv(p->type) <= MATCHnomatch)
- {
- fs->error("key type mismatch, %s to ref %s",
- var->type->toChars(), p->type->toChars());
- goto Lerror2;
- }
- }
- if (tab->ty == Tsarray)
- {
- TypeSArray *ta = (TypeSArray *)tab;
- IntRange dimrange = getIntRange(ta->dim);
- // https://issues.dlang.org/show_bug.cgi?id=12504
- dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
- if (!IntRange::fromType(var->type).contains(dimrange))
- {
- fs->error("index type `%s` cannot cover index range 0..%llu", p->type->toChars(), ta->dim->toInteger());
- goto Lerror2;
- }
- fs->key->range = new IntRange(SignExtendedNumber(0), dimrange.imax);
- }
- }
- else
- {
- var = new VarDeclaration(loc, p->type, p->ident, NULL);
- var->storage_class |= STCforeach;
- var->storage_class |= p->storageClass & (STCin | STCout | STCref | STC_TYPECTOR);
- if (var->storage_class & (STCref | STCout))
- var->storage_class |= STCnodtor;
-
- fs->value = var;
- if (var->storage_class & STCref)
- {
- if (fs->aggr->checkModifiable(sc2, 1) == 2)
- var->storage_class |= STCctorinit;
-
- Type *t = tab->nextOf();
- if (t->constConv(p->type) <= MATCHnomatch)
- {
- fs->error("argument type mismatch, %s to ref %s",
- t->toChars(), p->type->toChars());
- goto Lerror2;
- }
- }
- }
- }
-
- /* Convert to a ForStatement
- * foreach (key, value; a) body =>
- * for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
- * { T value = tmp[k]; body }
- *
- * foreach_reverse (key, value; a) body =>
- * for (T[] tmp = a[], size_t key = tmp.length; key--; )
- * { T value = tmp[k]; body }
- */
- Identifier *id = Identifier::generateId("__r");
- ExpInitializer *ie = new ExpInitializer(loc, new SliceExp(loc, fs->aggr, NULL, NULL));
- VarDeclaration *tmp;
- if (fs->aggr->op == TOKarrayliteral &&
- !((*fs->parameters)[dim - 1]->storageClass & STCref))
- {
- ArrayLiteralExp *ale = (ArrayLiteralExp *)fs->aggr;
- size_t edim = ale->elements ? ale->elements->length : 0;
- Type *telem = (*fs->parameters)[dim - 1]->type;
-
- // Bugzilla 12936: if telem has been specified explicitly,
- // converting array literal elements to telem might make it @nogc.
- fs->aggr = fs->aggr->implicitCastTo(sc, telem->sarrayOf(edim));
- if (fs->aggr->op == TOKerror)
- goto Lerror2;
-
- // for (T[edim] tmp = a, ...)
- tmp = new VarDeclaration(loc, fs->aggr->type, id, ie);
- }
- else
- tmp = new VarDeclaration(loc, tab->nextOf()->arrayOf(), id, ie);
- tmp->storage_class |= STCtemp;
- tmp->endlinnum = fs->endloc.linnum;
-
- Expression *tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id::length);
-
- if (!fs->key)
- {
- Identifier *idkey = Identifier::generateId("__key");
- fs->key = new VarDeclaration(loc, Type::tsize_t, idkey, NULL);
- fs->key->storage_class |= STCtemp;
- }
- else if (fs->key->type->ty != Type::tsize_t->ty)
- {
- tmp_length = new CastExp(loc, tmp_length, fs->key->type);
- }
- if (fs->op == TOKforeach_reverse)
- fs->key->_init = new ExpInitializer(loc, tmp_length);
- else
- fs->key->_init = new ExpInitializer(loc, new IntegerExp(loc, 0, fs->key->type));
-
- Statements *cs = new Statements();
- if (vinit)
- cs->push(new ExpStatement(loc, vinit));
- cs->push(new ExpStatement(loc, tmp));
- cs->push(new ExpStatement(loc, fs->key));
- Statement *forinit = new CompoundDeclarationStatement(loc, cs);
-
- Expression *cond;
- if (fs->op == TOKforeach_reverse)
- {
- // key--
- cond = new PostExp(TOKminusminus, loc, new VarExp(loc, fs->key));
- }
- else
- {
- // key < tmp.length
- cond = new CmpExp(TOKlt, loc, new VarExp(loc, fs->key), tmp_length);
- }
-
- Expression *increment = NULL;
- if (fs->op == TOKforeach)
- {
- // key += 1
- increment = new AddAssignExp(loc, new VarExp(loc, fs->key), new IntegerExp(loc, 1, fs->key->type));
- }
-
- // T value = tmp[key];
- IndexExp *indexExp = new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, fs->key));
- indexExp->indexIsInBounds = true; // disabling bounds checking in foreach statements.
- fs->value->_init = new ExpInitializer(loc, indexExp);
- Statement *ds = new ExpStatement(loc, fs->value);
-
- if (dim == 2)
- {
- Parameter *p = (*fs->parameters)[0];
- if ((p->storageClass & STCref) && p->type->equals(fs->key->type))
- {
- fs->key->range = NULL;
- AliasDeclaration *v = new AliasDeclaration(loc, p->ident, fs->key);
- fs->_body = new CompoundStatement(loc, new ExpStatement(loc, v), fs->_body);
- }
- else
- {
- ExpInitializer *ei = new ExpInitializer(loc, new IdentifierExp(loc, fs->key->ident));
- VarDeclaration *v = new VarDeclaration(loc, p->type, p->ident, ei);
- v->storage_class |= STCforeach | (p->storageClass & STCref);
- fs->_body = new CompoundStatement(loc, new ExpStatement(loc, v), fs->_body);
- if (fs->key->range && !p->type->isMutable())
- {
- /* Limit the range of the key to the specified range
- */
- v->range = new IntRange(fs->key->range->imin, fs->key->range->imax - SignExtendedNumber(1));
- }
- }
- }
- fs->_body = new CompoundStatement(loc, ds, fs->_body);
-
- s = new ForStatement(loc, forinit, cond, increment, fs->_body, fs->endloc);
- if (LabelStatement *ls = checkLabeledLoop(sc, fs)) // Bugzilla 15450: don't use sc2
- ls->gotoTarget = s;
- s = statementSemantic(s, sc2);
- break;
- }
-
- case Taarray:
- if (fs->op == TOKforeach_reverse)
- fs->warning("cannot use foreach_reverse with an associative array");
- if (fs->checkForArgTypes())
- {
- result = fs;
- return;
- }
-
- taa = (TypeAArray *)tab;
- if (dim < 1 || dim > 2)
- {
- fs->error("only one or two arguments for associative array foreach");
- goto Lerror2;
- }
- goto Lapply;
-
- case Tclass:
- case Tstruct:
- /* Prefer using opApply, if it exists
- */
- if (sapply)
- goto Lapply;
-
- {
- /* Look for range iteration, i.e. the properties
- * .empty, .popFront, .popBack, .front and .back
- * foreach (e; aggr) { ... }
- * translates to:
- * for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
- * auto e = __r.front;
- * ...
- * }
- */
- AggregateDeclaration *ad = (tab->ty == Tclass)
- ? (AggregateDeclaration *)((TypeClass *)tab)->sym
- : (AggregateDeclaration *)((TypeStruct *)tab)->sym;
- Identifier *idfront;
- Identifier *idpopFront;
- if (fs->op == TOKforeach)
- {
- idfront = Id::Ffront;
- idpopFront = Id::FpopFront;
- }
- else
- {
- idfront = Id::Fback;
- idpopFront = Id::FpopBack;
- }
- Dsymbol *sfront = ad->search(Loc(), idfront);
- if (!sfront)
- goto Lapply;
-
- /* Generate a temporary __r and initialize it with the aggregate.
- */
- VarDeclaration *r;
- Statement *init;
- if (vinit && fs->aggr->op == TOKvar && ((VarExp *)fs->aggr)->var == vinit)
- {
- r = vinit;
- init = new ExpStatement(loc, vinit);
- }
- else
- {
- r = copyToTemp(0, "__r", fs->aggr);
- dsymbolSemantic(r, sc);
- init = new ExpStatement(loc, r);
- if (vinit)
- init = new CompoundStatement(loc, new ExpStatement(loc, vinit), init);
- }
-
- // !__r.empty
- Expression *e = new VarExp(loc, r);
- e = new DotIdExp(loc, e, Id::Fempty);
- Expression *condition = new NotExp(loc, e);
-
- // __r.idpopFront()
- e = new VarExp(loc, r);
- Expression *increment = new CallExp(loc, new DotIdExp(loc, e, idpopFront));
-
- /* Declaration statement for e:
- * auto e = __r.idfront;
- */
- e = new VarExp(loc, r);
- Expression *einit = new DotIdExp(loc, e, idfront);
- Statement *makeargs, *forbody;
- if (dim == 1)
- {
- Parameter *p = (*fs->parameters)[0];
- VarDeclaration *ve = new VarDeclaration(loc, p->type, p->ident, new ExpInitializer(loc, einit));
- ve->storage_class |= STCforeach;
- ve->storage_class |= p->storageClass & (STCin | STCout | STCref | STC_TYPECTOR);
-
- makeargs = new ExpStatement(loc, ve);
- }
- else
- {
- VarDeclaration *vd = copyToTemp(STCref, "__front", einit);
- dsymbolSemantic(vd, sc);
- makeargs = new ExpStatement(loc, vd);
-
- Type *tfront = NULL;
- if (FuncDeclaration *fd = sfront->isFuncDeclaration())
- {
- if (!fd->functionSemantic())
- goto Lrangeerr;
- tfront = fd->type;
- }
- else if (TemplateDeclaration *td = sfront->isTemplateDeclaration())
- {
- Expressions a;
- if (FuncDeclaration *f = resolveFuncCall(loc, sc, td, NULL, tab, &a, 1))
- tfront = f->type;
- }
- else if (Declaration *d = sfront->isDeclaration())
- {
- tfront = d->type;
- }
- if (!tfront || tfront->ty == Terror)
- goto Lrangeerr;
-
- if (tfront->toBasetype()->ty == Tfunction)
- tfront = tfront->toBasetype()->nextOf();
- if (tfront->ty == Tvoid)
- {
- fs->error("%s.front is void and has no value", oaggr->toChars());
- goto Lerror2;
- }
-
- // Resolve inout qualifier of front type
- tfront = tfront->substWildTo(tab->mod);
-
- Expression *ve = new VarExp(loc, vd);
- ve->type = tfront;
-
- Expressions *exps = new Expressions();
- exps->push(ve);
- int pos = 0;
- while (exps->length < dim)
- {
- pos = expandAliasThisTuples(exps, pos);
- if (pos == -1)
- break;
- }
- if (exps->length != dim)
- {
- const char *plural = exps->length > 1 ? "s" : "";
- fs->error("cannot infer argument types, expected %d argument%s, not %d",
- exps->length, plural, dim);
- goto Lerror2;
- }
-
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *p = (*fs->parameters)[i];
- Expression *exp = (*exps)[i];
- if (!p->type)
- p->type = exp->type;
- p->type = typeSemantic(p->type->addStorageClass(p->storageClass), loc, sc2);
- if (!exp->implicitConvTo(p->type))
- goto Lrangeerr;
-
- VarDeclaration *var = new VarDeclaration(loc, p->type, p->ident, new ExpInitializer(loc, exp));
- var->storage_class |= STCctfe | STCref | STCforeach;
- makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, var));
- }
-
- }
-
- forbody = new CompoundStatement(loc,
- makeargs, fs->_body);
-
- s = new ForStatement(loc, init, condition, increment, forbody, fs->endloc);
- if (LabelStatement *ls = checkLabeledLoop(sc, fs))
- ls->gotoTarget = s;
- s = statementSemantic(s, sc2);
- break;
-
- Lrangeerr:
- fs->error("cannot infer argument types");
- goto Lerror2;
- }
- case Tdelegate:
- if (fs->op == TOKforeach_reverse)
- fs->deprecation("cannot use foreach_reverse with a delegate");
- Lapply:
- {
- if (fs->checkForArgTypes())
- {
- fs->_body = semanticNoScope(fs->_body, sc2);
- result = fs;
- return;
- }
-
- TypeFunction *tfld = NULL;
- if (sapply)
- {
- FuncDeclaration *fdapply = sapply->isFuncDeclaration();
- if (fdapply)
- {
- assert(fdapply->type && fdapply->type->ty == Tfunction);
- tfld = (TypeFunction *)typeSemantic(fdapply->type, loc, sc2);
- goto Lget;
- }
- else if (tab->ty == Tdelegate)
- {
- tfld = (TypeFunction *)tab->nextOf();
- Lget:
- //printf("tfld = %s\n", tfld->toChars());
- if (tfld->parameterList.parameters->length == 1)
- {
- Parameter *p = tfld->parameterList[0];
- if (p->type && p->type->ty == Tdelegate)
- {
- Type *t = typeSemantic(p->type, loc, sc2);
- assert(t->ty == Tdelegate);
- tfld = (TypeFunction *)t->nextOf();
- }
- }
- }
- }
-
- /* Turn body into the function literal:
- * int delegate(ref T param) { body }
- */
- Parameters *params = new Parameters();
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *p = (*fs->parameters)[i];
- StorageClass stc = STCref;
- Identifier *id;
-
- p->type = typeSemantic(p->type, loc, sc2);
- p->type = p->type->addStorageClass(p->storageClass);
- if (tfld)
- {
- Parameter *prm = tfld->parameterList[i];
- //printf("\tprm = %s%s\n", (prm->storageClass&STCref?"ref ":""), prm->ident->toChars());
- stc = prm->storageClass & STCref;
- id = p->ident; // argument copy is not need.
- if ((p->storageClass & STCref) != stc)
- {
- if (!stc)
- {
- fs->error("foreach: cannot make %s ref", p->ident->toChars());
- goto Lerror2;
- }
- goto LcopyArg;
- }
- }
- else if (p->storageClass & STCref)
- {
- // default delegate parameters are marked as ref, then
- // argument copy is not need.
- id = p->ident;
- }
- else
- {
- // Make a copy of the ref argument so it isn't
- // a reference.
- LcopyArg:
- id = Identifier::generateId("__applyArg", (int)i);
-
- Initializer *ie = new ExpInitializer(Loc(), new IdentifierExp(Loc(), id));
- VarDeclaration *v = new VarDeclaration(Loc(), p->type, p->ident, ie);
- v->storage_class |= STCtemp;
- s = new ExpStatement(Loc(), v);
- fs->_body = new CompoundStatement(loc, s, fs->_body);
- }
- params->push(new Parameter(stc, p->type, id, NULL, NULL));
- }
- // Bugzilla 13840: Throwable nested function inside nothrow function is acceptable.
- StorageClass stc = mergeFuncAttrs(STCsafe | STCpure | STCnogc, fs->func);
- tfld = new TypeFunction(ParameterList(params), Type::tint32, LINKd, stc);
- fs->cases = new Statements();
- fs->gotos = new ScopeStatements();
- FuncLiteralDeclaration *fld = new FuncLiteralDeclaration(loc, Loc(), tfld, TOKdelegate, fs);
- fld->fbody = fs->_body;
- Expression *flde = new FuncExp(loc, fld);
- flde = expressionSemantic(flde, sc2);
- fld->tookAddressOf = 0;
-
- // Resolve any forward referenced goto's
- for (size_t i = 0; i < fs->gotos->length; i++)
- {
- GotoStatement *gs = (GotoStatement *)(*fs->gotos)[i]->statement;
- if (!gs->label->statement)
- {
- // 'Promote' it to this scope, and replace with a return
- fs->cases->push(gs);
- s = new ReturnStatement(Loc(), new IntegerExp(fs->cases->length + 1));
- (*fs->gotos)[i]->statement = s;
- }
- }
-
- Expression *e = NULL;
- Expression *ec;
- if (vinit)
- {
- e = new DeclarationExp(loc, vinit);
- e = expressionSemantic(e, sc2);
- if (e->op == TOKerror)
- goto Lerror2;
- }
-
- if (taa)
- {
- // Check types
- Parameter *p = (*fs->parameters)[0];
- bool isRef = (p->storageClass & STCref) != 0;
- Type *ta = p->type;
- if (dim == 2)
- {
- Type *ti = (isRef ? taa->index->addMod(MODconst) : taa->index);
- if (isRef ? !ti->constConv(ta) : !ti->implicitConvTo(ta))
- {
- fs->error("foreach: index must be type %s, not %s", ti->toChars(), ta->toChars());
- goto Lerror2;
- }
- p = (*fs->parameters)[1];
- isRef = (p->storageClass & STCref) != 0;
- ta = p->type;
- }
- Type *taav = taa->nextOf();
- if (isRef ? !taav->constConv(ta) : !taav->implicitConvTo(ta))
- {
- fs->error("foreach: value must be type %s, not %s", taav->toChars(), ta->toChars());
- goto Lerror2;
- }
-
- /* Call:
- * extern(C) int _aaApply(void*, in size_t, int delegate(void*))
- * _aaApply(aggr, keysize, flde)
- *
- * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
- * _aaApply2(aggr, keysize, flde)
- */
- static const char *name[2] = { "_aaApply", "_aaApply2" };
- static FuncDeclaration *fdapply[2] = { NULL, NULL };
- static TypeDelegate *fldeTy[2] = { NULL, NULL };
-
- unsigned char i = (dim == 2 ? 1 : 0);
- if (!fdapply[i])
- {
- params = new Parameters();
- params->push(new Parameter(0, Type::tvoid->pointerTo(), NULL, NULL, NULL));
- params->push(new Parameter(STCin, Type::tsize_t, NULL, NULL, NULL));
- Parameters* dgparams = new Parameters;
- dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL, NULL));
- if (dim == 2)
- dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL, NULL));
- fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams),
- Type::tint32, LINKd));
- params->push(new Parameter(0, fldeTy[i], NULL, NULL, NULL));
- fdapply[i] = FuncDeclaration::genCfunc(params, Type::tint32, name[i]);
- }
-
- Expressions *exps = new Expressions();
- exps->push(fs->aggr);
- d_uns64 keysize = taa->index->size();
- if (keysize == SIZE_INVALID)
- goto Lerror2;
- assert(keysize < UINT64_MAX - target.ptrsize);
- keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
- // paint delegate argument to the type runtime expects
- if (!fldeTy[i]->equals(flde->type))
- {
- flde = new CastExp(loc, flde, flde->type);
- flde->type = fldeTy[i];
- }
- exps->push(new IntegerExp(Loc(), keysize, Type::tsize_t));
- exps->push(flde);
-
- ec = new VarExp(Loc(), fdapply[i], false);
- ec = new CallExp(loc, ec, exps);
- ec->type = Type::tint32; // don't run semantic() on ec
- }
- else if (tab->ty == Tarray || tab->ty == Tsarray)
- {
- /* Call:
- * _aApply(aggr, flde)
- */
- static const char fntab[9][3] =
- { "cc","cw","cd",
- "wc","cc","wd",
- "dc","dw","dd"
- };
- const int BUFFER_LEN = 7+1+2+ sizeof(dim)*3 + 1;
- char fdname[BUFFER_LEN];
- int flag;
-
- switch (tn->ty)
- {
- case Tchar: flag = 0; break;
- case Twchar: flag = 3; break;
- case Tdchar: flag = 6; break;
- default: assert(0);
- }
- switch (tnv->ty)
- {
- case Tchar: flag += 0; break;
- case Twchar: flag += 1; break;
- case Tdchar: flag += 2; break;
- default: assert(0);
- }
- const char *r = (fs->op == TOKforeach_reverse) ? "R" : "";
- int j = sprintf(fdname, "_aApply%s%.*s%llu", r, 2, fntab[flag], (ulonglong)dim);
- assert(j < BUFFER_LEN);
-
- FuncDeclaration *fdapply;
- TypeDelegate *dgty;
- params = new Parameters();
- params->push(new Parameter(STCin, tn->arrayOf(), NULL, NULL, NULL));
- Parameters* dgparams = new Parameters;
- dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL, NULL));
- if (dim == 2)
- dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL, NULL));
- dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams),
- Type::tint32, LINKd));
- params->push(new Parameter(0, dgty, NULL, NULL, NULL));
- fdapply = FuncDeclaration::genCfunc(params, Type::tint32, fdname);
-
- if (tab->ty == Tsarray)
- fs->aggr = fs->aggr->castTo(sc2, tn->arrayOf());
-
- // paint delegate argument to the type runtime expects
- if (!dgty->equals(flde->type)) {
- flde = new CastExp(loc, flde, flde->type);
- flde->type = dgty;
- }
-
- ec = new VarExp(Loc(), fdapply, false);
- ec = new CallExp(loc, ec, fs->aggr, flde);
- ec->type = Type::tint32; // don't run semantic() on ec
- }
- else if (tab->ty == Tdelegate)
- {
- /* Call:
- * aggr(flde)
- */
- if (fs->aggr->op == TOKdelegate &&
- ((DelegateExp *)fs->aggr)->func->isNested())
- {
- // See Bugzilla 3560
- fs->aggr = ((DelegateExp *)fs->aggr)->e1;
- }
- ec = new CallExp(loc, fs->aggr, flde);
- ec = expressionSemantic(ec, sc2);
- if (ec->op == TOKerror)
- goto Lerror2;
- if (ec->type != Type::tint32)
- {
- fs->error("opApply() function for %s must return an int", tab->toChars());
- goto Lerror2;
- }
- }
- else
- {
- if (global.params.vsafe)
- fld->tookAddressOf = 1; // allocate a closure unless the opApply() uses 'scope'
-
- assert(tab->ty == Tstruct || tab->ty == Tclass);
- assert(sapply);
- /* Call:
- * aggr.apply(flde)
- */
- ec = new DotIdExp(loc, fs->aggr, sapply->ident);
- ec = new CallExp(loc, ec, flde);
- ec = expressionSemantic(ec, sc2);
- if (ec->op == TOKerror)
- goto Lerror2;
- if (ec->type != Type::tint32)
- {
- fs->error("opApply() function for %s must return an int", tab->toChars());
- goto Lerror2;
- }
- }
- e = Expression::combine(e, ec);
-
- if (!fs->cases->length)
- {
- // Easy case, a clean exit from the loop
- e = new CastExp(loc, e, Type::tvoid); // Bugzilla 13899
- s = new ExpStatement(loc, e);
- }
- else
- {
- // Construct a switch statement around the return value
- // of the apply function.
- Statements *a = new Statements();
-
- // default: break; takes care of cases 0 and 1
- s = new BreakStatement(Loc(), NULL);
- s = new DefaultStatement(Loc(), s);
- a->push(s);
-
- // cases 2...
- for (size_t i = 0; i < fs->cases->length; i++)
- {
- s = (*fs->cases)[i];
- s = new CaseStatement(Loc(), new IntegerExp(i + 2), s);
- a->push(s);
- }
-
- s = new CompoundStatement(loc, a);
- s = new SwitchStatement(loc, e, s, false);
- }
- s = statementSemantic(s, sc2);
- break;
- }
- case Terror:
- Lerror2:
- s = new ErrorStatement();
- break;
-
- default:
- fs->error("foreach: %s is not an aggregate type", fs->aggr->type->toChars());
- goto Lerror2;
- }
- sc2->noctor--;
- sc2->pop();
- result = s;
- }
-
- void visit(ForeachRangeStatement *fs)
- {
- //printf("ForeachRangeStatement::semantic() %p\n", fs);
- Loc loc = fs->loc;
- fs->lwr = expressionSemantic(fs->lwr, sc);
- fs->lwr = resolveProperties(sc, fs->lwr);
- fs->lwr = fs->lwr->optimize(WANTvalue);
- if (!fs->lwr->type)
- {
- fs->error("invalid range lower bound %s", fs->lwr->toChars());
- Lerror:
- return setError();
- }
-
- fs->upr = expressionSemantic(fs->upr, sc);
- fs->upr = resolveProperties(sc, fs->upr);
- fs->upr = fs->upr->optimize(WANTvalue);
- if (!fs->upr->type)
- {
- fs->error("invalid range upper bound %s", fs->upr->toChars());
- goto Lerror;
- }
-
- if (fs->prm->type)
- {
- fs->prm->type = typeSemantic(fs->prm->type, loc, sc);
- fs->prm->type = fs->prm->type->addStorageClass(fs->prm->storageClass);
- fs->lwr = fs->lwr->implicitCastTo(sc, fs->prm->type);
-
- if (fs->upr->implicitConvTo(fs->prm->type) || (fs->prm->storageClass & STCref))
- {
- fs->upr = fs->upr->implicitCastTo(sc, fs->prm->type);
- }
- else
- {
- // See if upr-1 fits in prm->type
- Expression *limit = new MinExp(loc, fs->upr, new IntegerExp(1));
- limit = expressionSemantic(limit, sc);
- limit = limit->optimize(WANTvalue);
- if (!limit->implicitConvTo(fs->prm->type))
- {
- fs->upr = fs->upr->implicitCastTo(sc, fs->prm->type);
- }
- }
- }
- else
- {
- /* Must infer types from lwr and upr
- */
- Type *tlwr = fs->lwr->type->toBasetype();
- if (tlwr->ty == Tstruct || tlwr->ty == Tclass)
- {
- /* Just picking the first really isn't good enough.
- */
- fs->prm->type = fs->lwr->type;
- }
- else if (fs->lwr->type == fs->upr->type)
- {
- /* Same logic as CondExp ?lwr:upr
- */
- fs->prm->type = fs->lwr->type;
- }
- else
- {
- AddExp ea(loc, fs->lwr, fs->upr);
- if (typeCombine(&ea, sc))
- return setError();
- fs->prm->type = ea.type;
- fs->lwr = ea.e1;
- fs->upr = ea.e2;
- }
- fs->prm->type = fs->prm->type->addStorageClass(fs->prm->storageClass);
- }
- if (fs->prm->type->ty == Terror ||
- fs->lwr->op == TOKerror ||
- fs->upr->op == TOKerror)
- {
- return setError();
- }
-
- /* Convert to a for loop:
- * foreach (key; lwr .. upr) =>
- * for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
- *
- * foreach_reverse (key; lwr .. upr) =>
- * for (auto tmp = lwr, auto key = upr; key-- > tmp;)
- */
- ExpInitializer *ie = new ExpInitializer(loc, (fs->op == TOKforeach) ? fs->lwr : fs->upr);
- fs->key = new VarDeclaration(loc, fs->upr->type->mutableOf(), Identifier::generateId("__key"), ie);
- fs->key->storage_class |= STCtemp;
- SignExtendedNumber lower = getIntRange(fs->lwr).imin;
- SignExtendedNumber upper = getIntRange(fs->upr).imax;
- if (lower <= upper)
- {
- fs->key->range = new IntRange(lower, upper);
- }
-
- Identifier *id = Identifier::generateId("__limit");
- ie = new ExpInitializer(loc, (fs->op == TOKforeach) ? fs->upr : fs->lwr);
- VarDeclaration *tmp = new VarDeclaration(loc, fs->upr->type, id, ie);
- tmp->storage_class |= STCtemp;
-
- Statements *cs = new Statements();
- // Keep order of evaluation as lwr, then upr
- if (fs->op == TOKforeach)
- {
- cs->push(new ExpStatement(loc, fs->key));
- cs->push(new ExpStatement(loc, tmp));
- }
- else
- {
- cs->push(new ExpStatement(loc, tmp));
- cs->push(new ExpStatement(loc, fs->key));
- }
- Statement *forinit = new CompoundDeclarationStatement(loc, cs);
-
- Expression *cond;
- if (fs->op == TOKforeach_reverse)
- {
- cond = new PostExp(TOKminusminus, loc, new VarExp(loc, fs->key));
- if (fs->prm->type->isscalar())
- {
- // key-- > tmp
- cond = new CmpExp(TOKgt, loc, cond, new VarExp(loc, tmp));
- }
- else
- {
- // key-- != tmp
- cond = new EqualExp(TOKnotequal, loc, cond, new VarExp(loc, tmp));
- }
- }
- else
- {
- if (fs->prm->type->isscalar())
- {
- // key < tmp
- cond = new CmpExp(TOKlt, loc, new VarExp(loc, fs->key), new VarExp(loc, tmp));
- }
- else
- {
- // key != tmp
- cond = new EqualExp(TOKnotequal, loc, new VarExp(loc, fs->key), new VarExp(loc, tmp));
- }
- }
-
- Expression *increment = NULL;
- if (fs->op == TOKforeach)
- {
- // key += 1
- //increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(1));
- increment = new PreExp(TOKpreplusplus, loc, new VarExp(loc, fs->key));
- }
-
- if ((fs->prm->storageClass & STCref) && fs->prm->type->equals(fs->key->type))
- {
- fs->key->range = NULL;
- AliasDeclaration *v = new AliasDeclaration(loc, fs->prm->ident, fs->key);
- fs->_body = new CompoundStatement(loc, new ExpStatement(loc, v), fs->_body);
- }
- else
- {
- ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs->key), fs->prm->type));
- VarDeclaration *v = new VarDeclaration(loc, fs->prm->type, fs->prm->ident, ie);
- v->storage_class |= STCtemp | STCforeach | (fs->prm->storageClass & STCref);
- fs->_body = new CompoundStatement(loc, new ExpStatement(loc, v), fs->_body);
- if (fs->key->range && !fs->prm->type->isMutable())
- {
- /* Limit the range of the key to the specified range
- */
- v->range = new IntRange(fs->key->range->imin, fs->key->range->imax - SignExtendedNumber(1));
- }
- }
- if (fs->prm->storageClass & STCref)
- {
- if (fs->key->type->constConv(fs->prm->type) <= MATCHnomatch)
- {
- fs->error("prmument type mismatch, %s to ref %s",
- fs->key->type->toChars(), fs->prm->type->toChars());
- goto Lerror;
- }
- }
-
- ForStatement *s = new ForStatement(loc, forinit, cond, increment, fs->_body, fs->endloc);
- if (LabelStatement *ls = checkLabeledLoop(sc, fs))
- ls->gotoTarget = s;
- result = statementSemantic(s, sc);
- }
-
- void visit(IfStatement *ifs)
- {
- // Evaluate at runtime
- unsigned cs0 = sc->callSuper;
- unsigned cs1;
- unsigned *fi0 = sc->saveFieldInit();
- unsigned *fi1 = NULL;
-
- // check in syntax level
- ifs->condition = checkAssignmentAsCondition(ifs->condition);
-
- ScopeDsymbol *sym = new ScopeDsymbol();
- sym->parent = sc->scopesym;
- sym->endlinnum = ifs->endloc.linnum;
- Scope *scd = sc->push(sym);
- if (ifs->prm)
- {
- /* Declare prm, which we will set to be the
- * result of condition.
- */
- ExpInitializer *ei = new ExpInitializer(ifs->loc, ifs->condition);
- ifs->match = new VarDeclaration(ifs->loc, ifs->prm->type, ifs->prm->ident, ei);
- ifs->match->parent = sc->func;
- ifs->match->storage_class |= ifs->prm->storageClass;
- dsymbolSemantic(ifs->match, scd);
-
- DeclarationExp *de = new DeclarationExp(ifs->loc, ifs->match);
- VarExp *ve = new VarExp(ifs->loc, ifs->match);
- ifs->condition = new CommaExp(ifs->loc, de, ve);
- ifs->condition = expressionSemantic(ifs->condition, scd);
-
- if (ifs->match->edtor)
- {
- Statement *sdtor = new DtorExpStatement(ifs->loc, ifs->match->edtor, ifs->match);
- sdtor = new ScopeGuardStatement(ifs->loc, TOKon_scope_exit, sdtor);
- ifs->ifbody = new CompoundStatement(ifs->loc, sdtor, ifs->ifbody);
- ifs->match->storage_class |= STCnodtor;
- }
- }
- else
- {
- if (ifs->condition->op == TOKdotid)
- ((DotIdExp *)ifs->condition)->noderef = true;
-
- ifs->condition = expressionSemantic(ifs->condition, sc);
- ifs->condition = resolveProperties(sc, ifs->condition);
- ifs->condition = ifs->condition->addDtorHook(sc);
- }
- if (checkNonAssignmentArrayOp(ifs->condition))
- ifs->condition = new ErrorExp();
- ifs->condition = checkGC(sc, ifs->condition);
-
- // Convert to boolean after declaring prm so this works:
- // if (S prm = S()) {}
- // where S is a struct that defines opCast!bool.
- ifs->condition = ifs->condition->toBoolean(sc);
-
- // If we can short-circuit evaluate the if statement, don't do the
- // semantic analysis of the skipped code.
- // This feature allows a limited form of conditional compilation.
- ifs->condition = ifs->condition->optimize(WANTvalue);
- ifs->ifbody = semanticNoScope(ifs->ifbody, scd);
- scd->pop();
-
- cs1 = sc->callSuper;
- fi1 = sc->fieldinit;
- sc->callSuper = cs0;
- sc->fieldinit = fi0;
- if (ifs->elsebody)
- ifs->elsebody = semanticScope(ifs->elsebody, sc, NULL, NULL);
- sc->mergeCallSuper(ifs->loc, cs1);
- sc->mergeFieldInit(ifs->loc, fi1);
-
- if (ifs->condition->op == TOKerror ||
- (ifs->ifbody && ifs->ifbody->isErrorStatement()) ||
- (ifs->elsebody && ifs->elsebody->isErrorStatement()))
- {
- return setError();
- }
- result = ifs;
- }
-
- void visit(ConditionalStatement *cs)
- {
- //printf("ConditionalStatement::semantic()\n");
-
- // If we can short-circuit evaluate the if statement, don't do the
- // semantic analysis of the skipped code.
- // This feature allows a limited form of conditional compilation.
- if (cs->condition->include(sc))
- {
- DebugCondition *dc = cs->condition->isDebugCondition();
- if (dc)
- {
- sc = sc->push();
- sc->flags |= SCOPEdebug;
- cs->ifbody = statementSemantic(cs->ifbody, sc);
- sc->pop();
- }
- else
- cs->ifbody = statementSemantic(cs->ifbody, sc);
- result = cs->ifbody;
- }
- else
- {
- if (cs->elsebody)
- cs->elsebody = statementSemantic(cs->elsebody, sc);
- result = cs->elsebody;
- }
- }
-
- void visit(PragmaStatement *ps)
- {
- // Should be merged with PragmaDeclaration
- //printf("PragmaStatement::semantic() %s\n", ps->toChars());
- //printf("body = %p\n", ps->_body);
- if (ps->ident == Id::msg)
- {
- if (ps->args)
- {
- for (size_t i = 0; i < ps->args->length; i++)
- {
- Expression *e = (*ps->args)[i];
-
- sc = sc->startCTFE();
- e = expressionSemantic(e, sc);
- e = resolveProperties(sc, e);
- sc = sc->endCTFE();
- // pragma(msg) is allowed to contain types as well as expressions
- e = ctfeInterpretForPragmaMsg(e);
- if (e->op == TOKerror)
- {
- errorSupplemental(ps->loc, "while evaluating pragma(msg, %s)", (*ps->args)[i]->toChars());
- goto Lerror;
- }
- StringExp *se = e->toStringExp();
- if (se)
- {
- se = se->toUTF8(sc);
- fprintf(stderr, "%.*s", (int)se->len, (char *)se->string);
- }
- else
- fprintf(stderr, "%s", e->toChars());
- }
- fprintf(stderr, "\n");
- }
- }
- else if (ps->ident == Id::lib)
- {
- /* Should this be allowed?
- */
- ps->error("pragma(lib) not allowed as statement");
- goto Lerror;
- }
- else if (ps->ident == Id::startaddress)
- {
- if (!ps->args || ps->args->length != 1)
- ps->error("function name expected for start address");
- else
- {
- Expression *e = (*ps->args)[0];
-
- sc = sc->startCTFE();
- e = expressionSemantic(e, sc);
- e = resolveProperties(sc, e);
- sc = sc->endCTFE();
-
- e = e->ctfeInterpret();
- (*ps->args)[0] = e;
- Dsymbol *sa = getDsymbol(e);
- if (!sa || !sa->isFuncDeclaration())
- {
- ps->error("function name expected for start address, not `%s`", e->toChars());
- goto Lerror;
- }
- if (ps->_body)
- {
- ps->_body = statementSemantic(ps->_body, sc);
- if (ps->_body->isErrorStatement())
- {
- result = ps->_body;
- return;
- }
- }
- result = ps;
- return;
- }
- }
- else if (ps->ident == Id::Pinline)
- {
- PINLINE inlining = PINLINEdefault;
- if (!ps->args || ps->args->length == 0)
- inlining = PINLINEdefault;
- else if (!ps->args || ps->args->length != 1)
- {
- ps->error("boolean expression expected for pragma(inline)");
- goto Lerror;
- }
- else
- {
- Expression *e = (*ps->args)[0];
-
- if (e->op != TOKint64 || !e->type->equals(Type::tbool))
- {
- ps->error("pragma(inline, true or false) expected, not %s", e->toChars());
- goto Lerror;
- }
-
- if (e->isBool(true))
- inlining = PINLINEalways;
- else if (e->isBool(false))
- inlining = PINLINEnever;
-
- FuncDeclaration *fd = sc->func;
- if (!fd)
- {
- ps->error("pragma(inline) is not inside a function");
- goto Lerror;
- }
- fd->inlining = inlining;
- }
- }
- else
- {
- ps->error("unrecognized pragma(%s)", ps->ident->toChars());
- goto Lerror;
- }
-
- if (ps->_body)
- {
- if (ps->ident == Id::msg || ps->ident == Id::startaddress)
- {
- ps->error("`pragma(%s)` is missing a terminating `;`", ps->ident->toChars());
- return setError();
- }
- ps->_body = statementSemantic(ps->_body, sc);
- }
- result = ps->_body;
- return;
-
- Lerror:
- return setError();
- }
-
- void visit(StaticAssertStatement *s)
- {
- semantic2(s->sa, sc);
- }
-
- void visit(SwitchStatement *ss)
- {
- //printf("SwitchStatement::semantic(%p)\n", ss);
- ss->tf = sc->tf;
- if (ss->cases)
- {
- result = ss; // already run
- return;
- }
- bool conditionError = false;
- ss->condition = expressionSemantic(ss->condition, sc);
- ss->condition = resolveProperties(sc, ss->condition);
-
- Type *att = NULL;
- TypeEnum *te = NULL;
- while (ss->condition->op != TOKerror)
- {
- // preserve enum type for final switches
- if (ss->condition->type->ty == Tenum)
- te = (TypeEnum *)ss->condition->type;
- if (ss->condition->type->isString())
- {
- // If it's not an array, cast it to one
- if (ss->condition->type->ty != Tarray)
- {
- ss->condition = ss->condition->implicitCastTo(sc, ss->condition->type->nextOf()->arrayOf());
- }
- ss->condition->type = ss->condition->type->constOf();
- break;
- }
- ss->condition = integralPromotions(ss->condition, sc);
- if (ss->condition->op != TOKerror && ss->condition->type->isintegral())
- break;
-
- AggregateDeclaration *ad = isAggregate(ss->condition->type);
- if (ad && ad->aliasthis && ss->condition->type != att)
- {
- if (!att && ss->condition->type->checkAliasThisRec())
- att = ss->condition->type;
- if (Expression *e = resolveAliasThis(sc, ss->condition, true))
- {
- ss->condition = e;
- continue;
- }
- }
-
- if (ss->condition->op != TOKerror)
- {
- ss->error("`%s` must be of integral or string type, it is a %s",
- ss->condition->toChars(), ss->condition->type->toChars());
- conditionError = true;
- break;
- }
- }
- if (checkNonAssignmentArrayOp(ss->condition))
- ss->condition = new ErrorExp();
- ss->condition = ss->condition->optimize(WANTvalue);
- ss->condition = checkGC(sc, ss->condition);
- if (ss->condition->op == TOKerror)
- conditionError = true;
-
- bool needswitcherror = false;
-
- ss->lastVar = sc->lastVar;
-
- sc = sc->push();
- sc->sbreak = ss;
- sc->sw = ss;
-
- ss->cases = new CaseStatements();
- sc->noctor++; // BUG: should use Scope::mergeCallSuper() for each case instead
- ss->_body = statementSemantic(ss->_body, sc);
- sc->noctor--;
-
- if (conditionError || (ss->_body && ss->_body->isErrorStatement()))
- goto Lerror;
-
- // Resolve any goto case's with exp
- for (size_t i = 0; i < ss->gotoCases.length; i++)
- {
- GotoCaseStatement *gcs = ss->gotoCases[i];
-
- if (!gcs->exp)
- {
- gcs->error("no case statement following goto case;");
- goto Lerror;
- }
-
- for (Scope *scx = sc; scx; scx = scx->enclosing)
- {
- if (!scx->sw)
- continue;
- for (size_t j = 0; j < scx->sw->cases->length; j++)
- {
- CaseStatement *cs = (*scx->sw->cases)[j];
-
- if (cs->exp->equals(gcs->exp))
- {
- gcs->cs = cs;
- goto Lfoundcase;
- }
- }
- }
- gcs->error("case %s not found", gcs->exp->toChars());
- goto Lerror;
-
- Lfoundcase:
- ;
- }
-
- if (ss->isFinal)
- {
- Type *t = ss->condition->type;
- Dsymbol *ds;
- EnumDeclaration *ed = NULL;
- if (t && ((ds = t->toDsymbol(sc)) != NULL))
- ed = ds->isEnumDeclaration(); // typedef'ed enum
- if (!ed && te && ((ds = te->toDsymbol(sc)) != NULL))
- ed = ds->isEnumDeclaration();
- if (ed)
- {
- size_t dim = ed->members->length;
- for (size_t i = 0; i < dim; i++)
- {
- EnumMember *em = (*ed->members)[i]->isEnumMember();
- if (em)
- {
- for (size_t j = 0; j < ss->cases->length; j++)
- {
- CaseStatement *cs = (*ss->cases)[j];
- if (cs->exp->equals(em->value()) ||
- (!cs->exp->type->isString() && !em->value()->type->isString() &&
- cs->exp->toInteger() == em->value()->toInteger()))
- goto L1;
- }
- ss->error("enum member %s not represented in final switch", em->toChars());
- goto Lerror;
- }
- L1:
- ;
- }
- }
- else
- needswitcherror = true;
- }
-
- if (!sc->sw->sdefault && (!ss->isFinal || needswitcherror || global.params.useAssert == CHECKENABLEon))
- {
- ss->hasNoDefault = 1;
-
- if (!ss->isFinal && (!ss->_body || !ss->_body->isErrorStatement()))
- ss->error("switch statement without a default; use `final switch` or add `default: assert(0);` or add `default: break;`");
-
- // Generate runtime error if the default is hit
- Statements *a = new Statements();
- CompoundStatement *cs;
- Statement *s;
-
- if (global.params.useSwitchError == CHECKENABLEon &&
- global.params.checkAction != CHECKACTION_halt)
- {
- if (global.params.checkAction == CHECKACTION_C)
- {
- /* Rewrite as an assert(0) and let e2ir generate
- * the call to the C assert failure function
- */
- s = new ExpStatement(ss->loc, new AssertExp(ss->loc, new IntegerExp(ss->loc, 0, Type::tint32)));
- }
- else
- s = new SwitchErrorStatement(ss->loc);
- }
- else
- s = new ExpStatement(ss->loc, new HaltExp(ss->loc));
-
- a->reserve(2);
- sc->sw->sdefault = new DefaultStatement(ss->loc, s);
- a->push(ss->_body);
- if (blockExit(ss->_body, sc->func, false) & BEfallthru)
- a->push(new BreakStatement(Loc(), NULL));
- a->push(sc->sw->sdefault);
- cs = new CompoundStatement(ss->loc, a);
- ss->_body = cs;
- }
-
- if (ss->checkLabel())
- goto Lerror;
-
- sc->pop();
- result = ss;
- return;
-
- Lerror:
- sc->pop();
- result = new ErrorStatement();
- }
-
- void visit(CaseStatement *cs)
- {
- SwitchStatement *sw = sc->sw;
- bool errors = false;
-
- //printf("CaseStatement::semantic() %s\n", cs->toChars());
- sc = sc->startCTFE();
- cs->exp = expressionSemantic(cs->exp, sc);
- cs->exp = resolveProperties(sc, cs->exp);
- sc = sc->endCTFE();
- if (sw)
- {
- cs->exp = cs->exp->implicitCastTo(sc, sw->condition->type);
- cs->exp = cs->exp->optimize(WANTvalue | WANTexpand);
-
- Expression *e = cs->exp;
- // Remove all the casts the user and/or implicitCastTo may introduce
- // otherwise we'd sometimes fail the check below.
- while (e->op == TOKcast)
- e = ((CastExp *)e)->e1;
-
- /* This is where variables are allowed as case expressions.
- */
- if (e->op == TOKvar)
- {
- VarExp *ve = (VarExp *)e;
- VarDeclaration *v = ve->var->isVarDeclaration();
- Type *t = cs->exp->type->toBasetype();
- if (v && (t->isintegral() || t->ty == Tclass))
- {
- /* Flag that we need to do special code generation
- * for this, i.e. generate a sequence of if-then-else
- */
- sw->hasVars = 1;
-
- /* TODO check if v can be uninitialized at that point.
- */
- if (!v->isConst() && !v->isImmutable())
- {
- cs->deprecation("case variables have to be const or immutable");
- }
-
- if (sw->isFinal)
- {
- cs->error("case variables not allowed in final switch statements");
- errors = true;
- }
-
- /* Also check if the VarExp is declared in a scope outside of this one.
- * 'scx' is set to the scope of the switch statement.
- */
- for (Scope *scx = sc; scx; scx = scx->enclosing)
- {
- if (scx->enclosing && scx->enclosing->sw == sw)
- continue;
- assert(scx->sw == sw);
-
- if (!scx->search(cs->exp->loc, v->ident, NULL))
- {
- cs->error("case variable `%s` declared at %s cannot be declared in switch body",
- v->toChars(), v->loc.toChars());
- errors = true;
- }
- break;
- }
- goto L1;
- }
- }
- else
- cs->exp = cs->exp->ctfeInterpret();
-
- if (StringExp *se = cs->exp->toStringExp())
- cs->exp = se;
- else if (cs->exp->op != TOKint64 && cs->exp->op != TOKerror)
- {
- cs->error("case must be a string or an integral constant, not %s", cs->exp->toChars());
- errors = true;
- }
-
- L1:
- for (size_t i = 0; i < sw->cases->length; i++)
- {
- CaseStatement *cs2 = (*sw->cases)[i];
-
- //printf("comparing '%s' with '%s'\n", cs->exp->toChars(), cs2->exp->toChars());
- if (cs2->exp->equals(cs->exp))
- {
- cs->error("duplicate case %s in switch statement", cs->exp->toChars());
- errors = true;
- break;
- }
- }
-
- sw->cases->push(cs);
-
- // Resolve any goto case's with no exp to this case statement
- for (size_t i = 0; i < sw->gotoCases.length; )
- {
- GotoCaseStatement *gcs = sw->gotoCases[i];
-
- if (!gcs->exp)
- {
- gcs->cs = cs;
- sw->gotoCases.remove(i); // remove from array
- continue;
- }
- i++;
- }
-
- if (sc->sw->tf != sc->tf)
- {
- cs->error("switch and case are in different finally blocks");
- errors = true;
- }
- }
- else
- {
- cs->error("case not in switch statement");
- errors = true;
- }
- cs->statement = statementSemantic(cs->statement, sc);
- if (cs->statement->isErrorStatement())
- {
- result = cs->statement;
- return;
- }
- if (errors || cs->exp->op == TOKerror)
- return setError();
-
- cs->lastVar = sc->lastVar;
- result = cs;
- }
-
- void visit(CaseRangeStatement *crs)
- {
- SwitchStatement *sw = sc->sw;
- if (sw == NULL)
- {
- crs->error("case range not in switch statement");
- return setError();
- }
-
- //printf("CaseRangeStatement::semantic() %s\n", toChars());
- bool errors = false;
- if (sw->isFinal)
- {
- crs->error("case ranges not allowed in final switch");
- errors = true;
- }
-
- sc = sc->startCTFE();
- crs->first = expressionSemantic(crs->first, sc);
- crs->first = resolveProperties(sc, crs->first);
- sc = sc->endCTFE();
- crs->first = crs->first->implicitCastTo(sc, sw->condition->type);
- crs->first = crs->first->ctfeInterpret();
-
- sc = sc->startCTFE();
- crs->last = expressionSemantic(crs->last, sc);
- crs->last = resolveProperties(sc, crs->last);
- sc = sc->endCTFE();
- crs->last = crs->last->implicitCastTo(sc, sw->condition->type);
- crs->last = crs->last->ctfeInterpret();
-
- if (crs->first->op == TOKerror || crs->last->op == TOKerror || errors)
- {
- if (crs->statement)
- statementSemantic(crs->statement, sc);
- return setError();
- }
-
- uinteger_t fval = crs->first->toInteger();
- uinteger_t lval = crs->last->toInteger();
-
-
- if ( (crs->first->type->isunsigned() && fval > lval) ||
- (!crs->first->type->isunsigned() && (sinteger_t)fval > (sinteger_t)lval))
- {
- crs->error("first case %s is greater than last case %s",
- crs->first->toChars(), crs->last->toChars());
- errors = true;
- lval = fval;
- }
-
- if (lval - fval > 256)
- {
- crs->error("had %llu cases which is more than 256 cases in case range", lval - fval);
- errors = true;
- lval = fval + 256;
- }
-
- if (errors)
- return setError();
-
- /* This works by replacing the CaseRange with an array of Case's.
- *
- * case a: .. case b: s;
- * =>
- * case a:
- * [...]
- * case b:
- * s;
- */
-
- Statements *statements = new Statements();
- for (uinteger_t i = fval; i != lval + 1; i++)
- {
- Statement *s = crs->statement;
- if (i != lval) // if not last case
- s = new ExpStatement(crs->loc, (Expression *)NULL);
- Expression *e = new IntegerExp(crs->loc, i, crs->first->type);
- Statement *cs = new CaseStatement(crs->loc, e, s);
- statements->push(cs);
- }
- Statement *s = new CompoundStatement(crs->loc, statements);
- s = statementSemantic(s, sc);
- result = s;
- }
-
- void visit(DefaultStatement *ds)
- {
- //printf("DefaultStatement::semantic()\n");
- bool errors = false;
- if (sc->sw)
- {
- if (sc->sw->sdefault)
- {
- ds->error("switch statement already has a default");
- errors = true;
- }
- sc->sw->sdefault = ds;
-
- if (sc->sw->tf != sc->tf)
- {
- ds->error("switch and default are in different finally blocks");
- errors = true;
- }
- if (sc->sw->isFinal)
- {
- ds->error("default statement not allowed in final switch statement");
- errors = true;
- }
- }
- else
- {
- ds->error("default not in switch statement");
- errors = true;
- }
- ds->statement = statementSemantic(ds->statement, sc);
- if (errors || ds->statement->isErrorStatement())
- return setError();
-
- ds->lastVar = sc->lastVar;
- result = ds;
- }
-
- void visit(GotoDefaultStatement *gds)
- {
- gds->sw = sc->sw;
- if (!gds->sw)
- {
- gds->error("goto default not in switch statement");
- return setError();
- }
- if (gds->sw->isFinal)
- {
- gds->error("goto default not allowed in final switch statement");
- return setError();
- }
- result = gds;
- }
-
- void visit(GotoCaseStatement *gcs)
- {
- if (!sc->sw)
- {
- gcs->error("goto case not in switch statement");
- return setError();
- }
-
- if (gcs->exp)
- {
- gcs->exp = expressionSemantic(gcs->exp, sc);
- gcs->exp = gcs->exp->implicitCastTo(sc, sc->sw->condition->type);
- gcs->exp = gcs->exp->optimize(WANTvalue);
- if (gcs->exp->op == TOKerror)
- return setError();
- }
-
- sc->sw->gotoCases.push(gcs);
- result = gcs;
- }
-
- void visit(ReturnStatement *rs)
- {
- //printf("ReturnStatement::semantic() %s\n", toChars());
-
- FuncDeclaration *fd = sc->parent->isFuncDeclaration();
-
- if (fd->fes)
- fd = fd->fes->func; // fd is now function enclosing foreach
-
- TypeFunction *tf = (TypeFunction *)fd->type;
- assert(tf->ty == Tfunction);
-
- if (rs->exp && rs->exp->op == TOKvar && ((VarExp *)rs->exp)->var == fd->vresult)
- {
- // return vresult;
- if (sc->fes)
- {
- assert(rs->caseDim == 0);
- sc->fes->cases->push(rs);
- result = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->length + 1));
- return;
- }
- if (fd->returnLabel)
- {
- GotoStatement *gs = new GotoStatement(rs->loc, Id::returnLabel);
- gs->label = fd->returnLabel;
- result = gs;
- return;
- }
-
- if (!fd->returns)
- fd->returns = new ReturnStatements();
- fd->returns->push(rs);
- result = rs;
- return;
- }
-
- Type *tret = tf->next;
- Type *tbret = tret ? tret->toBasetype() : NULL;
-
- bool inferRef = (tf->isref && (fd->storage_class & STCauto));
- Expression *e0 = NULL;
-
- bool errors = false;
- if (sc->flags & SCOPEcontract)
- {
- rs->error("return statements cannot be in contracts");
- errors = true;
- }
- if (sc->os && sc->os->tok != TOKon_scope_failure)
- {
- rs->error("return statements cannot be in %s bodies", Token::toChars(sc->os->tok));
- errors = true;
- }
- if (sc->tf)
- {
- rs->error("return statements cannot be in finally bodies");
- errors = true;
- }
-
- if (fd->isCtorDeclaration())
- {
- if (rs->exp)
- {
- rs->error("cannot return expression from constructor");
- errors = true;
- }
-
- // Constructors implicitly do:
- // return this;
- rs->exp = new ThisExp(Loc());
- rs->exp->type = tret;
- }
- else if (rs->exp)
- {
- fd->hasReturnExp |= (fd->hasReturnExp & 1 ? 16 : 1);
-
- FuncLiteralDeclaration *fld = fd->isFuncLiteralDeclaration();
- if (tret)
- rs->exp = inferType(rs->exp, tret);
- else if (fld && fld->treq)
- rs->exp = inferType(rs->exp, fld->treq->nextOf()->nextOf());
- rs->exp = expressionSemantic(rs->exp, sc);
-
- // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
- if (rs->exp->op == TOKtype)
- rs->exp = resolveAliasThis(sc, rs->exp);
-
- rs->exp = resolveProperties(sc, rs->exp);
- if (rs->exp->checkType())
- rs->exp = new ErrorExp();
- if (FuncDeclaration *f = isFuncAddress(rs->exp))
- {
- if (fd->inferRetType && f->checkForwardRef(rs->exp->loc))
- rs->exp = new ErrorExp();
- }
- if (checkNonAssignmentArrayOp(rs->exp))
- rs->exp = new ErrorExp();
-
- // Extract side-effect part
- rs->exp = Expression::extractLast(rs->exp, &e0);
- if (rs->exp->op == TOKcall)
- rs->exp = valueNoDtor(rs->exp);
-
- if (e0)
- e0 = e0->optimize(WANTvalue);
-
- /* Void-return function can have void typed expression
- * on return statement.
- */
- if ((tbret && tbret->ty == Tvoid) || rs->exp->type->ty == Tvoid)
- {
- if (rs->exp->type->ty != Tvoid)
- {
- rs->error("cannot return non-void from void function");
- errors = true;
-
- rs->exp = new CastExp(rs->loc, rs->exp, Type::tvoid);
- rs->exp = expressionSemantic(rs->exp, sc);
- }
-
- /* Replace:
- * return exp;
- * with:
- * exp; return;
- */
- e0 = Expression::combine(e0, rs->exp);
- rs->exp = NULL;
- }
- if (e0)
- e0 = checkGC(sc, e0);
- }
-
- if (rs->exp)
- {
- if (fd->inferRetType) // infer return type
- {
- if (!tret)
- {
- tf->next = rs->exp->type;
- }
- else if (tret->ty != Terror && !rs->exp->type->equals(tret))
- {
- int m1 = rs->exp->type->implicitConvTo(tret);
- int m2 = tret->implicitConvTo(rs->exp->type);
- //printf("exp->type = %s m2<-->m1 tret %s\n", rs->exp->type->toChars(), tret->toChars());
- //printf("m1 = %d, m2 = %d\n", m1, m2);
-
- if (m1 && m2)
- ;
- else if (!m1 && m2)
- tf->next = rs->exp->type;
- else if (m1 && !m2)
- ;
- else if (rs->exp->op != TOKerror)
- {
- rs->error("mismatched function return type inference of %s and %s",
- rs->exp->type->toChars(), tret->toChars());
- errors = true;
- tf->next = Type::terror;
- }
- }
-
- tret = tf->next;
- tbret = tret->toBasetype();
- }
-
- if (inferRef) // deduce 'auto ref'
- {
- /* Determine "refness" of function return:
- * if it's an lvalue, return by ref, else return by value
- */
- if (rs->exp->isLvalue())
- {
- /* May return by ref
- */
- if (checkReturnEscapeRef(sc, rs->exp, true))
- tf->isref = false; // return by value
- }
- else
- tf->isref = false; // return by value
-
- /* The "refness" is determined by all of return statements.
- * This means:
- * return 3; return x; // ok, x can be a value
- * return x; return 3; // ok, x can be a value
- */
- }
- }
- else
- {
- // infer return type
- if (fd->inferRetType)
- {
- if (tf->next && tf->next->ty != Tvoid)
- {
- if (tf->next->ty != Terror)
- {
- rs->error("mismatched function return type inference of void and %s",
- tf->next->toChars());
- }
- errors = true;
- tf->next = Type::terror;
- }
- else
- tf->next = Type::tvoid;
-
- tret = tf->next;
- tbret = tret->toBasetype();
- }
-
- if (inferRef) // deduce 'auto ref'
- tf->isref = false;
-
- if (tbret->ty != Tvoid) // if non-void return
- {
- if (tbret->ty != Terror)
- rs->error("return expression expected");
- errors = true;
- }
- else if (fd->isMain())
- {
- // main() returns 0, even if it returns void
- rs->exp = new IntegerExp(0);
- }
- }
-
- // If any branches have called a ctor, but this branch hasn't, it's an error
- if (sc->callSuper & CSXany_ctor &&
- !(sc->callSuper & (CSXthis_ctor | CSXsuper_ctor)))
- {
- rs->error("return without calling constructor");
- errors = true;
- }
- sc->callSuper |= CSXreturn;
- if (sc->fieldinit)
- {
- AggregateDeclaration *ad = fd->isMember2();
- assert(ad);
- size_t dim = sc->fieldinit_dim;
- for (size_t i = 0; i < dim; i++)
- {
- VarDeclaration *v = ad->fields[i];
- bool mustInit = (v->storage_class & STCnodefaultctor ||
- v->type->needsNested());
- if (mustInit && !(sc->fieldinit[i] & CSXthis_ctor))
- {
- rs->error("an earlier return statement skips field %s initialization", v->toChars());
- errors = true;
- }
- sc->fieldinit[i] |= CSXreturn;
- }
- }
-
- if (errors)
- return setError();
-
- if (sc->fes)
- {
- if (!rs->exp)
- {
- // Send out "case receiver" statement to the foreach.
- // return exp;
- Statement *s = new ReturnStatement(Loc(), rs->exp);
- sc->fes->cases->push(s);
-
- // Immediately rewrite "this" return statement as:
- // return cases->length+1;
- rs->exp = new IntegerExp(sc->fes->cases->length + 1);
- if (e0)
- {
- result = new CompoundStatement(rs->loc, new ExpStatement(rs->loc, e0), rs);
- return;
- }
- result = rs;
- return;
- }
- else
- {
- fd->buildResultVar(NULL, rs->exp->type);
- bool r = fd->vresult->checkNestedReference(sc, Loc());
- assert(!r); // vresult should be always accessible
-
- // Send out "case receiver" statement to the foreach.
- // return vresult;
- Statement *s = new ReturnStatement(Loc(), new VarExp(Loc(), fd->vresult));
- sc->fes->cases->push(s);
-
- // Save receiver index for the later rewriting from:
- // return exp;
- // to:
- // vresult = exp; retrun caseDim;
- rs->caseDim = sc->fes->cases->length + 1;
- }
- }
- if (rs->exp)
- {
- if (!fd->returns)
- fd->returns = new ReturnStatements();
- fd->returns->push(rs);
- }
- if (e0)
- {
- result = new CompoundStatement(rs->loc, new ExpStatement(rs->loc, e0), rs);
- return;
- }
- result = rs;
- }
-
- void visit(BreakStatement *bs)
- {
- //printf("BreakStatement::semantic()\n");
- // If:
- // break Identifier;
- if (bs->ident)
- {
- bs->ident = fixupLabelName(sc, bs->ident);
-
- FuncDeclaration *thisfunc = sc->func;
-
- for (Scope *scx = sc; scx; scx = scx->enclosing)
- {
- if (scx->func != thisfunc) // if in enclosing function
- {
- if (sc->fes) // if this is the body of a foreach
- {
- /* Post this statement to the fes, and replace
- * it with a return value that caller will put into
- * a switch. Caller will figure out where the break
- * label actually is.
- * Case numbers start with 2, not 0, as 0 is continue
- * and 1 is break.
- */
- sc->fes->cases->push(bs);
- result = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->length + 1));
- return;
- }
- break; // can't break to it
- }
-
- LabelStatement *ls = scx->slabel;
- if (ls && ls->ident == bs->ident)
- {
- Statement *s = ls->statement;
-
- if (!s || !s->hasBreak())
- bs->error("label `%s` has no break", bs->ident->toChars());
- else if (ls->tf != sc->tf)
- bs->error("cannot break out of finally block");
- else
- {
- ls->breaks = true;
- result = bs;
- return;
- }
- return setError();
- }
- }
- bs->error("enclosing label `%s` for break not found", bs->ident->toChars());
- return setError();
- }
- else if (!sc->sbreak)
- {
- if (sc->os && sc->os->tok != TOKon_scope_failure)
- {
- bs->error("break is not inside %s bodies", Token::toChars(sc->os->tok));
- }
- else if (sc->fes)
- {
- // Replace break; with return 1;
- result = new ReturnStatement(Loc(), new IntegerExp(1));
- return;
- }
- else
- bs->error("break is not inside a loop or switch");
- return setError();
- }
- else if (sc->sbreak->isForwardingStatement())
- {
- bs->error("must use labeled `break` within `static foreach`");
- }
- result = bs;
- }
-
- void visit(ContinueStatement *cs)
- {
- //printf("ContinueStatement::semantic() %p\n", cs);
- if (cs->ident)
- {
- cs->ident = fixupLabelName(sc, cs->ident);
-
- Scope *scx;
- FuncDeclaration *thisfunc = sc->func;
-
- for (scx = sc; scx; scx = scx->enclosing)
- {
- LabelStatement *ls;
-
- if (scx->func != thisfunc) // if in enclosing function
- {
- if (sc->fes) // if this is the body of a foreach
- {
- for (; scx; scx = scx->enclosing)
- {
- ls = scx->slabel;
- if (ls && ls->ident == cs->ident && ls->statement == sc->fes)
- {
- // Replace continue ident; with return 0;
- result = new ReturnStatement(Loc(), new IntegerExp(0));
- return;
- }
- }
-
- /* Post this statement to the fes, and replace
- * it with a return value that caller will put into
- * a switch. Caller will figure out where the break
- * label actually is.
- * Case numbers start with 2, not 0, as 0 is continue
- * and 1 is break.
- */
- sc->fes->cases->push(cs);
- result = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->length + 1));
- return;
- }
- break; // can't continue to it
- }
-
- ls = scx->slabel;
- if (ls && ls->ident == cs->ident)
- {
- Statement *s = ls->statement;
-
- if (!s || !s->hasContinue())
- cs->error("label `%s` has no continue", cs->ident->toChars());
- else if (ls->tf != sc->tf)
- cs->error("cannot continue out of finally block");
- else
- {
- result = cs;
- return;
- }
- return setError();
- }
- }
- cs->error("enclosing label `%s` for continue not found", cs->ident->toChars());
- return setError();
- }
- else if (!sc->scontinue)
- {
- if (sc->os && sc->os->tok != TOKon_scope_failure)
- {
- cs->error("continue is not inside %s bodies", Token::toChars(sc->os->tok));
- }
- else if (sc->fes)
- {
- // Replace continue; with return 0;
- result = new ReturnStatement(Loc(), new IntegerExp(0));
- return;
- }
- else
- cs->error("continue is not inside a loop");
- return setError();
- }
- else if (sc->scontinue->isForwardingStatement())
- {
- cs->error("must use labeled `continue` within `static foreach`");
- }
- result = cs;
- }
-
- void visit(SynchronizedStatement *ss)
- {
- if (ss->exp)
- {
- ss->exp = expressionSemantic(ss->exp, sc);
- ss->exp = resolveProperties(sc, ss->exp);
- ss->exp = ss->exp->optimize(WANTvalue);
- ss->exp = checkGC(sc, ss->exp);
- if (ss->exp->op == TOKerror)
- goto Lbody;
- ClassDeclaration *cd = ss->exp->type->isClassHandle();
- if (!cd)
- {
- ss->error("can only synchronize on class objects, not `%s`", ss->exp->type->toChars());
- return setError();
- }
- else if (cd->isInterfaceDeclaration())
- {
- /* Cast the interface to an object, as the object has the monitor,
- * not the interface.
- */
- if (!ClassDeclaration::object)
- {
- ss->error("missing or corrupt object.d");
- fatal();
- }
-
- Type *t = ClassDeclaration::object->type;
- t = typeSemantic(t, Loc(), sc)->toBasetype();
- assert(t->ty == Tclass);
-
- ss->exp = new CastExp(ss->loc, ss->exp, t);
- ss->exp = expressionSemantic(ss->exp, sc);
- }
-
- /* Rewrite as:
- * auto tmp = exp;
- * _d_monitorenter(tmp);
- * try { body } finally { _d_monitorexit(tmp); }
- */
- VarDeclaration *tmp = copyToTemp(0, "__sync", ss->exp);
- dsymbolSemantic(tmp, sc);
-
- Statements *cs = new Statements();
- cs->push(new ExpStatement(ss->loc, tmp));
-
- Parameters* args = new Parameters;
- args->push(new Parameter(0, ClassDeclaration::object->type, NULL, NULL, NULL));
-
- FuncDeclaration *fdenter = FuncDeclaration::genCfunc(args, Type::tvoid, Id::monitorenter);
- Expression *e = new CallExp(ss->loc, new VarExp(ss->loc, fdenter, false), new VarExp(ss->loc, tmp));
- e->type = Type::tvoid; // do not run semantic on e
- cs->push(new ExpStatement(ss->loc, e));
-
- FuncDeclaration *fdexit = FuncDeclaration::genCfunc(args, Type::tvoid, Id::monitorexit);
- e = new CallExp(ss->loc, new VarExp(ss->loc, fdexit, false), new VarExp(ss->loc, tmp));
- e->type = Type::tvoid; // do not run semantic on e
- Statement *s = new ExpStatement(ss->loc, e);
- s = new TryFinallyStatement(ss->loc, ss->_body, s);
- cs->push(s);
-
- s = new CompoundStatement(ss->loc, cs);
- result = statementSemantic(s, sc);
- return;
- }
- else
- {
- /* Generate our own critical section, then rewrite as:
- * __gshared void* __critsec;
- * _d_criticalenter2(&__critsec);
- * try { body } finally { _d_criticalexit(__critsec); }
- */
- Identifier *id = Identifier::generateId("__critsec");
- Type *t = Type::tvoidptr;
- VarDeclaration *tmp = new VarDeclaration(ss->loc, t, id, NULL);
- tmp->storage_class |= STCtemp | STCgshared | STCstatic;
- Expression *tmpExp = new VarExp(ss->loc, tmp);
-
- Statements *cs = new Statements();
- cs->push(new ExpStatement(ss->loc, tmp));
-
- /* This is just a dummy variable for "goto skips declaration" error.
- * Backend optimizer could remove this unused variable.
- */
- VarDeclaration *v = new VarDeclaration(ss->loc, Type::tvoidptr, Identifier::generateId("__sync"), NULL);
- dsymbolSemantic(v, sc);
- cs->push(new ExpStatement(ss->loc, v));
-
- Parameters* args = new Parameters;
- args->push(new Parameter(0, t->pointerTo(), NULL, NULL, NULL));
-
- FuncDeclaration *fdenter = FuncDeclaration::genCfunc(args, Type::tvoid, Id::criticalenter, STCnothrow);
- Expression *e = new AddrExp(ss->loc, tmpExp);
- e = expressionSemantic(e, sc);
- e = new CallExp(ss->loc, new VarExp(ss->loc, fdenter, false), e);
- e->type = Type::tvoid; // do not run semantic on e
- cs->push(new ExpStatement(ss->loc, e));
-
- FuncDeclaration *fdexit = FuncDeclaration::genCfunc(args, Type::tvoid, Id::criticalexit, STCnothrow);
- e = expressionSemantic(tmpExp, sc);
- e = new CallExp(ss->loc, new VarExp(ss->loc, fdexit, false), e);
- e->type = Type::tvoid; // do not run semantic on e
- Statement *s = new ExpStatement(ss->loc, e);
- s = new TryFinallyStatement(ss->loc, ss->_body, s);
- cs->push(s);
-
- s = new CompoundStatement(ss->loc, cs);
- result = statementSemantic(s, sc);
- return;
- }
- Lbody:
- if (ss->_body)
- ss->_body = statementSemantic(ss->_body, sc);
- if (ss->_body && ss->_body->isErrorStatement())
- {
- result = ss->_body;
- return;
- }
- result = ss;
- }
-
- void visit(WithStatement *ws)
- {
- ScopeDsymbol *sym;
- Initializer *init;
-
- //printf("WithStatement::semantic()\n");
- ws->exp = expressionSemantic(ws->exp, sc);
- ws->exp = resolveProperties(sc, ws->exp);
- ws->exp = ws->exp->optimize(WANTvalue);
- ws->exp = checkGC(sc, ws->exp);
- if (ws->exp->op == TOKerror)
- return setError();
- if (ws->exp->op == TOKscope)
- {
- sym = new WithScopeSymbol(ws);
- sym->parent = sc->scopesym;
- sym->endlinnum = ws->endloc.linnum;
- }
- else if (ws->exp->op == TOKtype)
- {
- Dsymbol *s = ((TypeExp *)ws->exp)->type->toDsymbol(sc);
- if (!s || !s->isScopeDsymbol())
- {
- ws->error("with type %s has no members", ws->exp->toChars());
- return setError();
- }
- sym = new WithScopeSymbol(ws);
- sym->parent = sc->scopesym;
- sym->endlinnum = ws->endloc.linnum;
- }
- else
- {
- Type *t = ws->exp->type->toBasetype();
-
- Expression *olde = ws->exp;
- if (t->ty == Tpointer)
- {
- ws->exp = new PtrExp(ws->loc, ws->exp);
- ws->exp = expressionSemantic(ws->exp, sc);
- t = ws->exp->type->toBasetype();
- }
-
- assert(t);
- t = t->toBasetype();
- if (t->isClassHandle())
- {
- init = new ExpInitializer(ws->loc, ws->exp);
- ws->wthis = new VarDeclaration(ws->loc, ws->exp->type, Id::withSym, init);
- dsymbolSemantic(ws->wthis, sc);
-
- sym = new WithScopeSymbol(ws);
- sym->parent = sc->scopesym;
- sym->endlinnum = ws->endloc.linnum;
- }
- else if (t->ty == Tstruct)
- {
- if (!ws->exp->isLvalue())
- {
- /* Re-write to
- * {
- * auto __withtmp = exp
- * with(__withtmp)
- * {
- * ...
- * }
- * }
- */
- VarDeclaration *tmp = copyToTemp(0, "__withtmp", ws->exp);
- dsymbolSemantic(tmp, sc);
- ExpStatement *es = new ExpStatement(ws->loc, tmp);
- ws->exp = new VarExp(ws->loc, tmp);
- Statement *ss = new ScopeStatement(ws->loc, new CompoundStatement(ws->loc, es, ws), ws->endloc);
- result = statementSemantic(ss, sc);
- return;
- }
- Expression *e = ws->exp->addressOf();
- init = new ExpInitializer(ws->loc, e);
- ws->wthis = new VarDeclaration(ws->loc, e->type, Id::withSym, init);
- dsymbolSemantic(ws->wthis, sc);
- sym = new WithScopeSymbol(ws);
- // Need to set the scope to make use of resolveAliasThis
- sym->setScope(sc);
- sym->parent = sc->scopesym;
- sym->endlinnum = ws->endloc.linnum;
- }
- else
- {
- ws->error("with expressions must be aggregate types or pointers to them, not `%s`", olde->type->toChars());
- return setError();
- }
- }
-
- if (ws->_body)
- {
- sym->_scope = sc;
- sc = sc->push(sym);
- sc->insert(sym);
- ws->_body = statementSemantic(ws->_body, sc);
- sc->pop();
- if (ws->_body && ws->_body->isErrorStatement())
- {
- result = ws->_body;
- return;
- }
- }
-
- result = ws;
- }
-
- void visit(TryCatchStatement *tcs)
- {
- if (!global.params.useExceptions)
- {
- tcs->error("Cannot use try-catch statements with -betterC");
- return setError();
- }
-
- if (!ClassDeclaration::throwable)
- {
- tcs->error("Cannot use try-catch statements because `object.Throwable` was not declared");
- return setError();
- }
-
- unsigned flags = 0;
- const unsigned FLAGcpp = 1;
- const unsigned FLAGd = 2;
-
- tcs->_body = semanticScope(tcs->_body, sc, NULL, NULL);
- assert(tcs->_body);
-
- /* Even if body is empty, still do semantic analysis on catches
- */
- bool catchErrors = false;
- for (size_t i = 0; i < tcs->catches->length; i++)
- {
- Catch *c = (*tcs->catches)[i];
- catchSemantic(c, sc);
- if (c->errors)
- {
- catchErrors = true;
- continue;
- }
- ClassDeclaration *cd = c->type->toBasetype()->isClassHandle();
- flags |= cd->isCPPclass() ? FLAGcpp : FLAGd;
-
- // Determine if current catch 'hides' any previous catches
- for (size_t j = 0; j < i; j++)
- {
- Catch *cj = (*tcs->catches)[j];
- const char *si = c->loc.toChars();
- const char *sj = cj->loc.toChars();
-
- if (c->type->toBasetype()->implicitConvTo(cj->type->toBasetype()))
- {
- tcs->error("catch at %s hides catch at %s", sj, si);
- catchErrors = true;
- }
- }
- }
-
- if (sc->func)
- {
- if (flags == (FLAGcpp | FLAGd))
- {
- tcs->error("cannot mix catching D and C++ exceptions in the same try-catch");
- catchErrors = true;
- }
- }
-
- if (catchErrors)
- return setError();
-
- if (tcs->_body->isErrorStatement())
- {
- result = tcs->_body;
- return;
- }
-
- /* If the try body never throws, we can eliminate any catches
- * of recoverable exceptions.
- */
-
- if (!(blockExit(tcs->_body, sc->func, false) & BEthrow) && ClassDeclaration::exception)
- {
- for (size_t i = 0; i < tcs->catches->length; i++)
- {
- Catch *c = (*tcs->catches)[i];
-
- /* If catch exception type is derived from Exception
- */
- if (c->type->toBasetype()->implicitConvTo(ClassDeclaration::exception->type) &&
- (!c->handler || !c->handler->comeFrom()))
- {
- // Remove c from the array of catches
- tcs->catches->remove(i);
- --i;
- }
- }
- }
-
- if (tcs->catches->length == 0)
- {
- result = tcs->_body->hasCode() ? tcs->_body : NULL;
- return;
- }
-
- result = tcs;
- }
-
- void visit(TryFinallyStatement *tfs)
- {
- //printf("TryFinallyStatement::semantic()\n");
- tfs->_body = statementSemantic(tfs->_body, sc);
- sc = sc->push();
- sc->tf = tfs;
- sc->sbreak = NULL;
- sc->scontinue = NULL; // no break or continue out of finally block
- tfs->finalbody = semanticNoScope(tfs->finalbody, sc);
- sc->pop();
-
- if (!tfs->_body)
- {
- result = tfs->finalbody;
- return;
- }
-
- if (!tfs->finalbody)
- {
- result = tfs->_body;
- return;
- }
-
- int blockexit = blockExit(tfs->_body, sc->func, false);
-
- // if not worrying about exceptions
- if (!(global.params.useExceptions && ClassDeclaration::throwable))
- blockexit &= ~BEthrow; // don't worry about paths that otherwise may throw
-
- // Don't care about paths that halt, either
- if ((blockexit & ~BEhalt) == BEfallthru)
- {
- result = new CompoundStatement(tfs->loc, tfs->_body, tfs->finalbody);
- return;
- }
- result = tfs;
- }
-
- void visit(ScopeGuardStatement *oss)
- {
- if (oss->tok != TOKon_scope_exit)
- {
- // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement,
- // so the generated catch block cannot be placed in finally block.
- // See also Catch::semantic.
- if (sc->os && sc->os->tok != TOKon_scope_failure)
- {
- // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
- oss->error("cannot put %s statement inside %s", Token::toChars(oss->tok), Token::toChars(sc->os->tok));
- return setError();
- }
- if (sc->tf)
- {
- oss->error("cannot put %s statement inside finally block", Token::toChars(oss->tok));
- return setError();
- }
- }
-
- sc = sc->push();
- sc->tf = NULL;
- sc->os = oss;
- if (oss->tok != TOKon_scope_failure)
- {
- // Jump out from scope(failure) block is allowed.
- sc->sbreak = NULL;
- sc->scontinue = NULL;
- }
- oss->statement = semanticNoScope(oss->statement, sc);
- sc->pop();
-
- if (!oss->statement || oss->statement->isErrorStatement())
- {
- result = oss->statement;
- return;
- }
- result = oss;
- }
-
- void visit(ThrowStatement *ts)
- {
- //printf("ThrowStatement::semantic()\n");
-
- if (!global.params.useExceptions)
- {
- ts->error("Cannot use `throw` statements with -betterC");
- return setError();
- }
-
- if (!ClassDeclaration::throwable)
- {
- ts->error("Cannot use `throw` statements because `object.Throwable` was not declared");
- return setError();
- }
-
- FuncDeclaration *fd = sc->parent->isFuncDeclaration();
- fd->hasReturnExp |= 2;
-
- ts->exp = expressionSemantic(ts->exp, sc);
- ts->exp = resolveProperties(sc, ts->exp);
- ts->exp = checkGC(sc, ts->exp);
- if (ts->exp->op == TOKerror)
- return setError();
-
- checkThrowEscape(sc, ts->exp, false);
-
- ClassDeclaration *cd = ts->exp->type->toBasetype()->isClassHandle();
- if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL)))
- {
- ts->error("can only throw class objects derived from Throwable, not type %s", ts->exp->type->toChars());
- return setError();
- }
-
- result = ts;
- }
-
- void visit(DebugStatement *ds)
- {
- if (ds->statement)
- {
- sc = sc->push();
- sc->flags |= SCOPEdebug;
- ds->statement = statementSemantic(ds->statement, sc);
- sc->pop();
- }
- result = ds->statement;
- }
-
- void visit(GotoStatement *gs)
- {
- //printf("GotoStatement::semantic()\n");
- FuncDeclaration *fd = sc->func;
-
- gs->ident = fixupLabelName(sc, gs->ident);
- gs->label = fd->searchLabel(gs->ident);
- gs->tf = sc->tf;
- gs->os = sc->os;
- gs->lastVar = sc->lastVar;
-
- if (!gs->label->statement && sc->fes)
- {
- /* Either the goto label is forward referenced or it
- * is in the function that the enclosing foreach is in.
- * Can't know yet, so wrap the goto in a scope statement
- * so we can patch it later, and add it to a 'look at this later'
- * list.
- */
- ScopeStatement *ss = new ScopeStatement(gs->loc, gs, gs->loc);
- sc->fes->gotos->push(ss); // 'look at this later' list
- result = ss;
- return;
- }
-
- // Add to fwdref list to check later
- if (!gs->label->statement)
- {
- if (!fd->gotos)
- fd->gotos = new GotoStatements();
- fd->gotos->push(gs);
- }
- else if (gs->checkLabel())
- return setError();
-
- result = gs;
- }
-
- void visit(LabelStatement *ls)
- {
- //printf("LabelStatement::semantic()\n");
- FuncDeclaration *fd = sc->parent->isFuncDeclaration();
-
- ls->ident = fixupLabelName(sc, ls->ident);
- ls->tf = sc->tf;
- ls->os = sc->os;
- ls->lastVar = sc->lastVar;
-
- LabelDsymbol *ls2 = fd->searchLabel(ls->ident);
- if (ls2->statement)
- {
- ls->error("label `%s` already defined", ls2->toChars());
- return setError();
- }
- else
- ls2->statement = ls;
-
- sc = sc->push();
- sc->scopesym = sc->enclosing->scopesym;
- sc->callSuper |= CSXlabel;
- if (sc->fieldinit)
- {
- size_t dim = sc->fieldinit_dim;
- for (size_t i = 0; i < dim; i++)
- sc->fieldinit[i] |= CSXlabel;
- }
- sc->slabel = ls;
- if (ls->statement)
- ls->statement = statementSemantic(ls->statement, sc);
- sc->pop();
-
- result = ls;
- }
-
- void visit(AsmStatement *s)
- {
- result = asmSemantic(s, sc);
- }
-
- void visit(CompoundAsmStatement *cas)
- {
- // Apply postfix attributes of the asm block to each statement.
- sc = sc->push();
- sc->stc |= cas->stc;
-
- for (size_t i = 0; i < cas->statements->length; i++)
- {
- Statement *s = (*cas->statements)[i];
- (*cas->statements)[i] = s ? statementSemantic(s, sc) : NULL;
- }
-
- assert(sc->func);
- // use setImpure/setGC when the deprecation cycle is over
- PURE purity;
- if (!(cas->stc & STCpure) && (purity = sc->func->isPureBypassingInference()) != PUREimpure && purity != PUREfwdref)
- cas->deprecation("asm statement is assumed to be impure - mark it with `pure` if it is not");
- if (!(cas->stc & STCnogc) && sc->func->isNogcBypassingInference())
- cas->deprecation("asm statement is assumed to use the GC - mark it with `@nogc` if it does not");
- if (!(cas->stc & (STCtrusted|STCsafe)) && sc->func->setUnsafe())
- cas->error("asm statement is assumed to be @system - mark it with `@trusted` if it is not");
-
- sc->pop();
- result = cas;
- }
-
- void visit(ImportStatement *imps)
- {
- for (size_t i = 0; i < imps->imports->length; i++)
- {
- Import *s = (*imps->imports)[i]->isImport();
- assert(!s->aliasdecls.length);
- for (size_t j = 0; j < s->names.length; j++)
- {
- Identifier *name = s->names[j];
- Identifier *alias = s->aliases[j];
-
- if (!alias)
- alias = name;
-
- TypeIdentifier *tname = new TypeIdentifier(s->loc, name);
- AliasDeclaration *ad = new AliasDeclaration(s->loc, alias, tname);
- ad->_import = s;
- s->aliasdecls.push(ad);
- }
-
- dsymbolSemantic(s, sc);
- // https://issues.dlang.org/show_bug.cgi?id=19942
- // If the module that's being imported doesn't exist, don't add it to the symbol table
- // for the current scope.
- if (s->mod != NULL)
- {
- Module::addDeferredSemantic2(s); // Bugzilla 14666
- sc->insert(s);
-
- for (size_t j = 0; j < s->aliasdecls.length; j++)
- {
- sc->insert(s->aliasdecls[j]);
- }
- }
- }
- result = imps;
- }
-};
-
-Statement *statementSemantic(Statement *s, Scope *sc)
-{
- StatementSemanticVisitor v = StatementSemanticVisitor(sc);
- s->accept(&v);
- return v.result;
-}
-
-void catchSemantic(Catch *c, Scope *sc)
-{
- //printf("Catch::semantic(%s)\n", ident->toChars());
-
- if (sc->os && sc->os->tok != TOKon_scope_failure)
- {
- // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
- error(c->loc, "cannot put catch statement inside %s", Token::toChars(sc->os->tok));
- c->errors = true;
- }
- if (sc->tf)
- {
- /* This is because the _d_local_unwind() gets the stack munged
- * up on this. The workaround is to place any try-catches into
- * a separate function, and call that.
- * To fix, have the compiler automatically convert the finally
- * body into a nested function.
- */
- error(c->loc, "cannot put catch statement inside finally block");
- c->errors = true;
- }
-
- ScopeDsymbol *sym = new ScopeDsymbol();
- sym->parent = sc->scopesym;
- sc = sc->push(sym);
-
- if (!c->type)
- {
- deprecation(c->loc, "catch statement without an exception specification is deprecated; use catch(Throwable) for old behavior");
-
- // reference .object.Throwable
- c->type = getThrowable();
- }
- c->type = typeSemantic(c->type, c->loc, sc);
- if (c->type == Type::terror)
- c->errors = true;
- else
- {
- ClassDeclaration *cd = c->type->toBasetype()->isClassHandle();
- if (!cd)
- {
- error(c->loc, "can only catch class objects, not `%s`", c->type->toChars());
- c->errors = true;
- }
- else if (cd->isCPPclass())
- {
- if (!target.cpp.exceptions)
- {
- error(c->loc, "catching C++ class objects not supported for this target");
- c->errors = true;
- }
- if (sc->func && !sc->intypeof && !c->internalCatch && sc->func->setUnsafe())
- {
- error(c->loc, "cannot catch C++ class objects in @safe code");
- c->errors = true;
- }
- }
- else if (cd != ClassDeclaration::throwable && !ClassDeclaration::throwable->isBaseOf(cd, NULL))
- {
- error(c->loc, "can only catch class objects derived from Throwable, not `%s`", c->type->toChars());
- c->errors = true;
- }
- else if (sc->func && !sc->intypeof && !c->internalCatch &&
- cd != ClassDeclaration::exception && !ClassDeclaration::exception->isBaseOf(cd, NULL) &&
- sc->func->setUnsafe())
- {
- error(c->loc, "can only catch class objects derived from Exception in @safe code, not `%s`", c->type->toChars());
- c->errors = true;
- }
-
- if (c->ident)
- {
- c->var = new VarDeclaration(c->loc, c->type, c->ident, NULL);
- dsymbolSemantic(c->var, sc);
- sc->insert(c->var);
- }
- c->handler = statementSemantic(c->handler, sc);
- if (c->handler && c->handler->isErrorStatement())
- c->errors = true;
- }
- sc->pop();
-}
-
-Statement *semanticNoScope(Statement *s, Scope *sc)
-{
- //printf("Statement::semanticNoScope() %s\n", toChars());
- if (!s->isCompoundStatement() && !s->isScopeStatement())
- {
- s = new CompoundStatement(s->loc, s); // so scopeCode() gets called
- }
- s = statementSemantic(s, sc);
- return s;
-}
-
-// Same as semanticNoScope(), but do create a new scope
-Statement *semanticScope(Statement *s, Scope *sc, Statement *sbreak, Statement *scontinue)
-{
- ScopeDsymbol *sym = new ScopeDsymbol();
- sym->parent = sc->scopesym;
- Scope *scd = sc->push(sym);
- if (sbreak)
- scd->sbreak = sbreak;
- if (scontinue)
- scd->scontinue = scontinue;
- s = semanticNoScope(s, scd);
- scd->pop();
- return s;
-}
-
-/*******************
- * See StatementSemanticVisitor.makeTupleForeach. This is a simple
- * wrapper that returns the generated statements/declarations.
- */
-Statement *makeTupleForeachStatic(Scope *sc, ForeachStatement *fs, bool needExpansion)
-{
- StatementSemanticVisitor v = StatementSemanticVisitor(sc);
- v.makeTupleForeachStatic(fs, needExpansion);
- return v.result;
-}
-
-Dsymbols *makeTupleForeachStaticDecl(Scope *sc, ForeachStatement *fs, Dsymbols *dbody, bool needExpansion)
-{
- StatementSemanticVisitor v = StatementSemanticVisitor(sc);
- return v.makeTupleForeachStaticDecl(fs, dbody, needExpansion);
-}
diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d
new file mode 100644
index 00000000000..f067c91b316
--- /dev/null
+++ b/gcc/d/dmd/statementsem.d
@@ -0,0 +1,4995 @@
+/**
+ * Does semantic analysis for statements.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statementsem.d, _statementsem.d)
+ * Documentation: https://dlang.org/phobos/dmd_statementsem.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statementsem.d
+ */
+
+module dmd.statementsem;
+
+import core.stdc.stdio;
+
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arrayop;
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.ast_node;
+import dmd.attrib;
+import dmd.blockexit;
+import dmd.clone;
+import dmd.cond;
+import dmd.ctorflow;
+import dmd.dcast;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dinterpret;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.escape;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.gluelayer;
+import dmd.id;
+import dmd.identifier;
+import dmd.init;
+import dmd.intrange;
+import dmd.mtype;
+import dmd.nogc;
+import dmd.opover;
+import dmd.parse;
+import dmd.printast;
+import dmd.root.outbuffer;
+import dmd.root.string;
+import dmd.semantic2;
+import dmd.sideeffect;
+import dmd.statement;
+import dmd.staticassert;
+import dmd.target;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.visitor;
+import dmd.compiler;
+
+version (DMDLIB)
+{
+ version = CallbackAPI;
+}
+
+/*****************************************
+ * CTFE requires FuncDeclaration::labtab for the interpretation.
+ * So fixing the label name inside in/out contracts is necessary
+ * for the uniqueness in labtab.
+ * Params:
+ * sc = context
+ * ident = statement label name to be adjusted
+ * Returns:
+ * adjusted label name
+ */
+private Identifier fixupLabelName(Scope* sc, Identifier ident)
+{
+ uint flags = (sc.flags & SCOPE.contract);
+ const id = ident.toString();
+ if (flags && flags != SCOPE.invariant_ &&
+ !(id.length >= 2 && id[0] == '_' && id[1] == '_')) // does not start with "__"
+ {
+ OutBuffer buf;
+ buf.writestring(flags == SCOPE.require ? "__in_" : "__out_");
+ buf.writestring(ident.toString());
+
+ ident = Identifier.idPool(buf[]);
+ }
+ return ident;
+}
+
+/*******************************************
+ * Check to see if statement is the innermost labeled statement.
+ * Params:
+ * sc = context
+ * statement = Statement to check
+ * Returns:
+ * if `true`, then the `LabelStatement`, otherwise `null`
+ */
+private LabelStatement checkLabeledLoop(Scope* sc, Statement statement)
+{
+ if (sc.slabel && sc.slabel.statement == statement)
+ {
+ return sc.slabel;
+ }
+ return null;
+}
+
+/***********************************************************
+ * Check an assignment is used as a condition.
+ * Intended to be use before the `semantic` call on `e`.
+ * Params:
+ * e = condition expression which is not yet run semantic analysis.
+ * Returns:
+ * `e` or ErrorExp.
+ */
+private Expression checkAssignmentAsCondition(Expression e)
+{
+ auto ec = lastComma(e);
+ if (ec.op == TOK.assign)
+ {
+ ec.error("assignment cannot be used as a condition, perhaps `==` was meant?");
+ return ErrorExp.get();
+ }
+ return e;
+}
+
+// Performs semantic analysis in Statement AST nodes
+extern(C++) Statement statementSemantic(Statement s, Scope* sc)
+{
+ version (CallbackAPI)
+ Compiler.onStatementSemanticStart(s, sc);
+
+ scope v = new StatementSemanticVisitor(sc);
+ s.accept(v);
+
+ version (CallbackAPI)
+ Compiler.onStatementSemanticDone(s, sc);
+
+ return v.result;
+}
+
+private extern (C++) final class StatementSemanticVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+
+ Statement result;
+ Scope* sc;
+
+ this(Scope* sc)
+ {
+ this.sc = sc;
+ }
+
+ private void setError()
+ {
+ result = new ErrorStatement();
+ }
+
+ override void visit(Statement s)
+ {
+ result = s;
+ }
+
+ override void visit(ErrorStatement s)
+ {
+ result = s;
+ }
+
+ override void visit(PeelStatement s)
+ {
+ /* "peel" off this wrapper, and don't run semantic()
+ * on the result.
+ */
+ result = s.s;
+ }
+
+ override void visit(ExpStatement s)
+ {
+ /* https://dlang.org/spec/statement.html#expression-statement
+ */
+
+ if (!s.exp)
+ {
+ result = s;
+ return;
+ }
+ //printf("ExpStatement::semantic() %s\n", exp.toChars());
+
+ // Allow CommaExp in ExpStatement because return isn't used
+ CommaExp.allow(s.exp);
+
+ s.exp = s.exp.expressionSemantic(sc);
+ s.exp = resolveProperties(sc, s.exp);
+ s.exp = s.exp.addDtorHook(sc);
+ if (checkNonAssignmentArrayOp(s.exp))
+ s.exp = ErrorExp.get();
+ if (auto f = isFuncAddress(s.exp))
+ {
+ if (f.checkForwardRef(s.exp.loc))
+ s.exp = ErrorExp.get();
+ }
+ if (discardValue(s.exp))
+ s.exp = ErrorExp.get();
+
+ s.exp = s.exp.optimize(WANTvalue);
+ s.exp = checkGC(sc, s.exp);
+ if (s.exp.op == TOK.error)
+ return setError();
+ result = s;
+ }
+
+ override void visit(CompileStatement cs)
+ {
+ /* https://dlang.org/spec/statement.html#mixin-statement
+ */
+
+ //printf("CompileStatement::semantic() %s\n", exp.toChars());
+ Statements* a = cs.flatten(sc);
+ if (!a)
+ return;
+ Statement s = new CompoundStatement(cs.loc, a);
+ result = s.statementSemantic(sc);
+ }
+
+ override void visit(CompoundStatement cs)
+ {
+ //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
+ version (none)
+ {
+ foreach (i, s; cs.statements)
+ {
+ if (s)
+ printf("[%d]: %s", i, s.toChars());
+ }
+ }
+
+ for (size_t i = 0; i < cs.statements.dim;)
+ {
+ Statement s = (*cs.statements)[i];
+ if (!s)
+ {
+ ++i;
+ continue;
+ }
+
+ Statements* flt = s.flatten(sc);
+ if (flt)
+ {
+ cs.statements.remove(i);
+ cs.statements.insert(i, flt);
+ continue;
+ }
+ s = s.statementSemantic(sc);
+ (*cs.statements)[i] = s;
+ if (!s)
+ {
+ /* Remove NULL statements from the list.
+ */
+ cs.statements.remove(i);
+ continue;
+ }
+ if (s.isErrorStatement())
+ {
+ result = s; // propagate error up the AST
+ ++i;
+ continue; // look for errors in rest of statements
+ }
+ Statement sentry;
+ Statement sexception;
+ Statement sfinally;
+
+ (*cs.statements)[i] = s.scopeCode(sc, sentry, sexception, sfinally);
+ if (sentry)
+ {
+ sentry = sentry.statementSemantic(sc);
+ cs.statements.insert(i, sentry);
+ i++;
+ }
+ if (sexception)
+ sexception = sexception.statementSemantic(sc);
+ if (sexception)
+ {
+ /* Returns: true if statements[] are empty statements
+ */
+ static bool isEmpty(const Statement[] statements)
+ {
+ foreach (s; statements)
+ {
+ if (const cs = s.isCompoundStatement())
+ {
+ if (!isEmpty((*cs.statements)[]))
+ return false;
+ }
+ else
+ return false;
+ }
+ return true;
+ }
+
+ if (!sfinally && isEmpty((*cs.statements)[i + 1 .. cs.statements.dim]))
+ {
+ }
+ else
+ {
+ /* Rewrite:
+ * s; s1; s2;
+ * As:
+ * s;
+ * try { s1; s2; }
+ * catch (Throwable __o)
+ * { sexception; throw __o; }
+ */
+ auto a = new Statements();
+ a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
+ cs.statements.setDim(i + 1);
+
+ Statement _body = new CompoundStatement(Loc.initial, a);
+ _body = new ScopeStatement(Loc.initial, _body, Loc.initial);
+
+ Identifier id = Identifier.generateId("__o");
+
+ Statement handler = new PeelStatement(sexception);
+ if (sexception.blockExit(sc.func, false) & BE.fallthru)
+ {
+ auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id));
+ ts.internalThrow = true;
+ handler = new CompoundStatement(Loc.initial, handler, ts);
+ }
+
+ auto catches = new Catches();
+ auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
+ ctch.internalCatch = true;
+ catches.push(ctch);
+
+ Statement st = new TryCatchStatement(Loc.initial, _body, catches);
+ if (sfinally)
+ st = new TryFinallyStatement(Loc.initial, st, sfinally);
+ st = st.statementSemantic(sc);
+
+ cs.statements.push(st);
+ break;
+ }
+ }
+ else if (sfinally)
+ {
+ if (0 && i + 1 == cs.statements.dim)
+ {
+ cs.statements.push(sfinally);
+ }
+ else
+ {
+ /* Rewrite:
+ * s; s1; s2;
+ * As:
+ * s; try { s1; s2; } finally { sfinally; }
+ */
+ auto a = new Statements();
+ a.pushSlice((*cs.statements)[i + 1 .. cs.statements.length]);
+ cs.statements.setDim(i + 1);
+
+ auto _body = new CompoundStatement(Loc.initial, a);
+ Statement stf = new TryFinallyStatement(Loc.initial, _body, sfinally);
+ stf = stf.statementSemantic(sc);
+ cs.statements.push(stf);
+ break;
+ }
+ }
+ i++;
+ }
+
+ /* Flatten them in place
+ */
+ void flatten(Statements* statements)
+ {
+ for (size_t i = 0; i < statements.length;)
+ {
+ Statement s = (*statements)[i];
+ if (s)
+ {
+ if (auto flt = s.flatten(sc))
+ {
+ statements.remove(i);
+ statements.insert(i, flt);
+ continue;
+ }
+ }
+ ++i;
+ }
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=11653
+ * 'semantic' may return another CompoundStatement
+ * (eg. CaseRangeStatement), so flatten it here.
+ */
+ flatten(cs.statements);
+
+ foreach (s; *cs.statements)
+ {
+ if (!s)
+ continue;
+
+ if (auto se = s.isErrorStatement())
+ {
+ result = se;
+ return;
+ }
+ }
+
+ if (cs.statements.length == 1)
+ {
+ result = (*cs.statements)[0];
+ return;
+ }
+ result = cs;
+ }
+
+ override void visit(UnrolledLoopStatement uls)
+ {
+ //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc);
+ Scope* scd = sc.push();
+ scd.sbreak = uls;
+ scd.scontinue = uls;
+
+ Statement serror = null;
+ foreach (i, ref s; *uls.statements)
+ {
+ if (s)
+ {
+ //printf("[%d]: %s\n", i, s.toChars());
+ s = s.statementSemantic(scd);
+ if (s && !serror)
+ serror = s.isErrorStatement();
+ }
+ }
+
+ scd.pop();
+ result = serror ? serror : uls;
+ }
+
+ override void visit(ScopeStatement ss)
+ {
+ //printf("ScopeStatement::semantic(sc = %p)\n", sc);
+ if (!ss.statement)
+ {
+ result = ss;
+ return;
+ }
+
+ ScopeDsymbol sym = new ScopeDsymbol();
+ sym.parent = sc.scopesym;
+ sym.endlinnum = ss.endloc.linnum;
+ sc = sc.push(sym);
+
+ Statements* a = ss.statement.flatten(sc);
+ if (a)
+ {
+ ss.statement = new CompoundStatement(ss.loc, a);
+ }
+
+ ss.statement = ss.statement.statementSemantic(sc);
+ if (ss.statement)
+ {
+ if (ss.statement.isErrorStatement())
+ {
+ sc.pop();
+ result = ss.statement;
+ return;
+ }
+
+ Statement sentry;
+ Statement sexception;
+ Statement sfinally;
+ ss.statement = ss.statement.scopeCode(sc, sentry, sexception, sfinally);
+ assert(!sentry);
+ assert(!sexception);
+ if (sfinally)
+ {
+ //printf("adding sfinally\n");
+ sfinally = sfinally.statementSemantic(sc);
+ ss.statement = new CompoundStatement(ss.loc, ss.statement, sfinally);
+ }
+ }
+ sc.pop();
+ result = ss;
+ }
+
+ override void visit(ForwardingStatement ss)
+ {
+ assert(ss.sym);
+ for (Scope* csc = sc; !ss.sym.forward; csc = csc.enclosing)
+ {
+ assert(csc);
+ ss.sym.forward = csc.scopesym;
+ }
+ sc = sc.push(ss.sym);
+ sc.sbreak = ss;
+ sc.scontinue = ss;
+ ss.statement = ss.statement.statementSemantic(sc);
+ sc = sc.pop();
+ result = ss.statement;
+ }
+
+ override void visit(WhileStatement ws)
+ {
+ /* Rewrite as a for(;condition;) loop
+ * https://dlang.org/spec/statement.html#while-statement
+ */
+ Expression cond = ws.condition;
+ Statement _body = ws._body;
+ if (ws.param)
+ {
+ /**
+ * If the while loop is of form `while(auto a = exp) { loop_body }`,
+ * rewrite to:
+ *
+ * while(true)
+ * if (auto a = exp)
+ * { loop_body }
+ * else
+ * { break; }
+ */
+ _body = new IfStatement(ws.loc, ws.param, ws.condition, ws._body, new BreakStatement(ws.loc, null), ws.endloc);
+ cond = IntegerExp.createBool(true);
+ }
+ Statement s = new ForStatement(ws.loc, null, cond, null, _body, ws.endloc);
+ s = s.statementSemantic(sc);
+ result = s;
+ }
+
+ override void visit(DoStatement ds)
+ {
+ /* https://dlang.org/spec/statement.html#do-statement
+ */
+ const inLoopSave = sc.inLoop;
+ sc.inLoop = true;
+ if (ds._body)
+ ds._body = ds._body.semanticScope(sc, ds, ds, null);
+ sc.inLoop = inLoopSave;
+
+ if (ds.condition.op == TOK.dotIdentifier)
+ (cast(DotIdExp)ds.condition).noderef = true;
+
+ // check in syntax level
+ ds.condition = checkAssignmentAsCondition(ds.condition);
+
+ ds.condition = ds.condition.expressionSemantic(sc);
+ ds.condition = resolveProperties(sc, ds.condition);
+ if (checkNonAssignmentArrayOp(ds.condition))
+ ds.condition = ErrorExp.get();
+ ds.condition = ds.condition.optimize(WANTvalue);
+ ds.condition = checkGC(sc, ds.condition);
+
+ ds.condition = ds.condition.toBoolean(sc);
+
+ if (ds.condition.op == TOK.error)
+ return setError();
+ if (ds._body && ds._body.isErrorStatement())
+ {
+ result = ds._body;
+ return;
+ }
+
+ result = ds;
+ }
+
+ override void visit(ForStatement fs)
+ {
+ /* https://dlang.org/spec/statement.html#for-statement
+ */
+ //printf("ForStatement::semantic %s\n", fs.toChars());
+
+ if (fs._init)
+ {
+ /* Rewrite:
+ * for (auto v1 = i1, v2 = i2; condition; increment) { ... }
+ * to:
+ * { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
+ * then lowered to:
+ * auto v1 = i1;
+ * try {
+ * auto v2 = i2;
+ * try {
+ * for (; condition; increment) { ... }
+ * } finally { v2.~this(); }
+ * } finally { v1.~this(); }
+ */
+ auto ainit = new Statements();
+ ainit.push(fs._init);
+ fs._init = null;
+ ainit.push(fs);
+ Statement s = new CompoundStatement(fs.loc, ainit);
+ s = new ScopeStatement(fs.loc, s, fs.endloc);
+ s = s.statementSemantic(sc);
+ if (!s.isErrorStatement())
+ {
+ if (LabelStatement ls = checkLabeledLoop(sc, fs))
+ ls.gotoTarget = fs;
+ fs.relatedLabeled = s;
+ }
+ result = s;
+ return;
+ }
+ assert(fs._init is null);
+
+ auto sym = new ScopeDsymbol();
+ sym.parent = sc.scopesym;
+ sym.endlinnum = fs.endloc.linnum;
+ sc = sc.push(sym);
+ sc.inLoop = true;
+
+ if (fs.condition)
+ {
+ if (fs.condition.op == TOK.dotIdentifier)
+ (cast(DotIdExp)fs.condition).noderef = true;
+
+ // check in syntax level
+ fs.condition = checkAssignmentAsCondition(fs.condition);
+
+ fs.condition = fs.condition.expressionSemantic(sc);
+ fs.condition = resolveProperties(sc, fs.condition);
+ if (checkNonAssignmentArrayOp(fs.condition))
+ fs.condition = ErrorExp.get();
+ fs.condition = fs.condition.optimize(WANTvalue);
+ fs.condition = checkGC(sc, fs.condition);
+
+ fs.condition = fs.condition.toBoolean(sc);
+ }
+ if (fs.increment)
+ {
+ CommaExp.allow(fs.increment);
+ fs.increment = fs.increment.expressionSemantic(sc);
+ fs.increment = resolveProperties(sc, fs.increment);
+ if (checkNonAssignmentArrayOp(fs.increment))
+ fs.increment = ErrorExp.get();
+ fs.increment = fs.increment.optimize(WANTvalue);
+ fs.increment = checkGC(sc, fs.increment);
+ }
+
+ sc.sbreak = fs;
+ sc.scontinue = fs;
+ if (fs._body)
+ fs._body = fs._body.semanticNoScope(sc);
+
+ sc.pop();
+
+ if (fs.condition && fs.condition.op == TOK.error ||
+ fs.increment && fs.increment.op == TOK.error ||
+ fs._body && fs._body.isErrorStatement())
+ return setError();
+ result = fs;
+ }
+
+ /*******************
+ * Determines the return type of makeTupleForeach.
+ */
+ private static template MakeTupleForeachRet(bool isDecl)
+ {
+ static if(isDecl)
+ {
+ alias MakeTupleForeachRet = Dsymbols*;
+ }
+ else
+ {
+ alias MakeTupleForeachRet = void;
+ }
+ }
+
+ /*******************
+ * Type check and unroll `foreach` over an expression tuple as well
+ * as `static foreach` statements and `static foreach`
+ * declarations. For `static foreach` statements and `static
+ * foreach` declarations, the visitor interface is used (and the
+ * result is written into the `result` field.) For `static
+ * foreach` declarations, the resulting Dsymbols* are returned
+ * directly.
+ *
+ * The unrolled body is wrapped into a
+ * - UnrolledLoopStatement, for `foreach` over an expression tuple.
+ * - ForwardingStatement, for `static foreach` statements.
+ * - ForwardingAttribDeclaration, for `static foreach` declarations.
+ *
+ * `static foreach` variables are declared as `STC.local`, such
+ * that they are inserted into the local symbol tables of the
+ * forwarding constructs instead of forwarded. For `static
+ * foreach` with multiple foreach loop variables whose aggregate
+ * has been lowered into a sequence of tuples, this function
+ * expands the tuples into multiple `STC.local` `static foreach`
+ * variables.
+ */
+ MakeTupleForeachRet!isDecl makeTupleForeach(bool isStatic, bool isDecl)(ForeachStatement fs, TupleForeachArgs!(isStatic, isDecl) args)
+ {
+ auto returnEarly()
+ {
+ static if (isDecl)
+ {
+ return null;
+ }
+ else
+ {
+ result = new ErrorStatement();
+ return;
+ }
+ }
+ static if(isDecl)
+ {
+ static assert(isStatic);
+ auto dbody = args[0];
+ }
+ static if(isStatic)
+ {
+ auto needExpansion = args[$-1];
+ assert(sc);
+ }
+
+ auto loc = fs.loc;
+ size_t dim = fs.parameters.dim;
+ static if(isStatic) bool skipCheck = needExpansion;
+ else enum skipCheck = false;
+ if (!skipCheck && (dim < 1 || dim > 2))
+ {
+ fs.error("only one (value) or two (key,value) arguments for tuple `foreach`");
+ setError();
+ return returnEarly();
+ }
+
+ Type paramtype = (*fs.parameters)[dim - 1].type;
+ if (paramtype)
+ {
+ paramtype = paramtype.typeSemantic(loc, sc);
+ if (paramtype.ty == Terror)
+ {
+ setError();
+ return returnEarly();
+ }
+ }
+
+ Type tab = fs.aggr.type.toBasetype();
+ TypeTuple tuple = cast(TypeTuple)tab;
+ static if(!isDecl)
+ {
+ auto statements = new Statements();
+ }
+ else
+ {
+ auto declarations = new Dsymbols();
+ }
+ //printf("aggr: op = %d, %s\n", fs.aggr.op, fs.aggr.toChars());
+ size_t n;
+ TupleExp te = null;
+ if (fs.aggr.op == TOK.tuple) // expression tuple
+ {
+ te = cast(TupleExp)fs.aggr;
+ n = te.exps.dim;
+ }
+ else if (fs.aggr.op == TOK.type) // type tuple
+ {
+ n = Parameter.dim(tuple.arguments);
+ }
+ else
+ assert(0);
+ foreach (j; 0 .. n)
+ {
+ size_t k = (fs.op == TOK.foreach_) ? j : n - 1 - j;
+ Expression e = null;
+ Type t = null;
+ if (te)
+ e = (*te.exps)[k];
+ else
+ t = Parameter.getNth(tuple.arguments, k).type;
+ Parameter p = (*fs.parameters)[0];
+ static if(!isDecl)
+ {
+ auto st = new Statements();
+ }
+ else
+ {
+ auto st = new Dsymbols();
+ }
+
+ static if(isStatic) bool skip = needExpansion;
+ else enum skip = false;
+ if (!skip && dim == 2)
+ {
+ // Declare key
+ if (p.storageClass & (STC.out_ | STC.ref_ | STC.lazy_))
+ {
+ fs.error("no storage class for key `%s`", p.ident.toChars());
+ setError();
+ return returnEarly();
+ }
+ static if(isStatic)
+ {
+ if(!p.type)
+ {
+ p.type = Type.tsize_t;
+ }
+ }
+ p.type = p.type.typeSemantic(loc, sc);
+
+ if (!p.type.isintegral())
+ {
+ fs.error("foreach: key cannot be of non-integral type `%s`",
+ p.type.toChars());
+ setError();
+ return returnEarly();
+ }
+
+ const length = te ? te.exps.length : tuple.arguments.length;
+ IntRange dimrange = IntRange(SignExtendedNumber(length))._cast(Type.tsize_t);
+ // https://issues.dlang.org/show_bug.cgi?id=12504
+ dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
+ if (!IntRange.fromType(p.type).contains(dimrange))
+ {
+ fs.error("index type `%s` cannot cover index range 0..%llu",
+ p.type.toChars(), cast(ulong)length);
+ setError();
+ return returnEarly();
+ }
+ Initializer ie = new ExpInitializer(Loc.initial, new IntegerExp(k));
+ auto var = new VarDeclaration(loc, p.type, p.ident, ie);
+ var.storage_class |= STC.manifest;
+ static if(isStatic) var.storage_class |= STC.local;
+ static if(!isDecl)
+ {
+ st.push(new ExpStatement(loc, var));
+ }
+ else
+ {
+ st.push(var);
+ }
+ p = (*fs.parameters)[1]; // value
+ }
+ /***********************
+ * Declares a unrolled `foreach` loop variable or a `static foreach` variable.
+ *
+ * Params:
+ * storageClass = The storage class of the variable.
+ * type = The declared type of the variable.
+ * ident = The name of the variable.
+ * e = The initializer of the variable (i.e. the current element of the looped over aggregate).
+ * t = The type of the initializer.
+ * Returns:
+ * `true` iff the declaration was successful.
+ */
+ bool declareVariable(StorageClass storageClass, Type type, Identifier ident, Expression e, Type t)
+ {
+ if (storageClass & (STC.out_ | STC.lazy_) ||
+ storageClass & STC.ref_ && !te)
+ {
+ fs.error("no storage class for value `%s`", ident.toChars());
+ setError();
+ return false;
+ }
+ Declaration var;
+ if (e)
+ {
+ Type tb = e.type.toBasetype();
+ Dsymbol ds = null;
+ if (!(storageClass & STC.manifest))
+ {
+ if ((isStatic || tb.ty == Tfunction || storageClass&STC.alias_) && e.op == TOK.variable)
+ ds = (cast(VarExp)e).var;
+ else if (e.op == TOK.template_)
+ ds = (cast(TemplateExp)e).td;
+ else if (e.op == TOK.scope_)
+ ds = (cast(ScopeExp)e).sds;
+ else if (e.op == TOK.function_)
+ {
+ auto fe = cast(FuncExp)e;
+ ds = fe.td ? cast(Dsymbol)fe.td : fe.fd;
+ }
+ else if (e.op == TOK.overloadSet)
+ ds = (cast(OverExp)e).vars;
+ }
+ else if (storageClass & STC.alias_)
+ {
+ fs.error("`foreach` loop variable cannot be both `enum` and `alias`");
+ setError();
+ return false;
+ }
+
+ if (ds)
+ {
+ var = new AliasDeclaration(loc, ident, ds);
+ if (storageClass & STC.ref_)
+ {
+ fs.error("symbol `%s` cannot be `ref`", ds.toChars());
+ setError();
+ return false;
+ }
+ if (paramtype)
+ {
+ fs.error("cannot specify element type for symbol `%s`", ds.toChars());
+ setError();
+ return false;
+ }
+ }
+ else if (e.op == TOK.type)
+ {
+ var = new AliasDeclaration(loc, ident, e.type);
+ if (paramtype)
+ {
+ fs.error("cannot specify element type for type `%s`", e.type.toChars());
+ setError();
+ return false;
+ }
+ }
+ else
+ {
+ e = resolveProperties(sc, e);
+ Initializer ie = new ExpInitializer(Loc.initial, e);
+ auto v = new VarDeclaration(loc, type, ident, ie, storageClass);
+ if (storageClass & STC.ref_)
+ v.storage_class |= STC.ref_ | STC.foreach_;
+ if (isStatic || storageClass&STC.manifest || e.isConst() ||
+ e.op == TOK.string_ ||
+ e.op == TOK.structLiteral ||
+ e.op == TOK.arrayLiteral)
+ {
+ if (v.storage_class & STC.ref_)
+ {
+ static if (!isStatic)
+ {
+ fs.error("constant value `%s` cannot be `ref`", ie.toChars());
+ }
+ else
+ {
+ if (!needExpansion)
+ {
+ fs.error("constant value `%s` cannot be `ref`", ie.toChars());
+ }
+ else
+ {
+ fs.error("constant value `%s` cannot be `ref`", ident.toChars());
+ }
+ }
+ setError();
+ return false;
+ }
+ else
+ v.storage_class |= STC.manifest;
+ }
+ var = v;
+ }
+ }
+ else
+ {
+ var = new AliasDeclaration(loc, ident, t);
+ if (paramtype)
+ {
+ fs.error("cannot specify element type for symbol `%s`", fs.toChars());
+ setError();
+ return false;
+ }
+ }
+ static if (isStatic)
+ {
+ var.storage_class |= STC.local;
+ }
+ static if (!isDecl)
+ {
+ st.push(new ExpStatement(loc, var));
+ }
+ else
+ {
+ st.push(var);
+ }
+ return true;
+ }
+ static if (!isStatic)
+ {
+ // Declare value
+ if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
+ {
+ return returnEarly();
+ }
+ }
+ else
+ {
+ if (!needExpansion)
+ {
+ // Declare value
+ if (!declareVariable(p.storageClass, p.type, p.ident, e, t))
+ {
+ return returnEarly();
+ }
+ }
+ else
+ { // expand tuples into multiple `static foreach` variables.
+ assert(e && !t);
+ auto ident = Identifier.generateId("__value");
+ declareVariable(0, e.type, ident, e, null);
+ import dmd.cond: StaticForeach;
+ auto field = Identifier.idPool(StaticForeach.tupleFieldName.ptr,StaticForeach.tupleFieldName.length);
+ Expression access = new DotIdExp(loc, e, field);
+ access = expressionSemantic(access, sc);
+ if (!tuple) return returnEarly();
+ //printf("%s\n",tuple.toChars());
+ foreach (l; 0 .. dim)
+ {
+ auto cp = (*fs.parameters)[l];
+ Expression init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type.tsize_t));
+ init_ = init_.expressionSemantic(sc);
+ assert(init_.type);
+ declareVariable(p.storageClass, init_.type, cp.ident, init_, null);
+ }
+ }
+ }
+
+ static if (!isDecl)
+ {
+ if (fs._body) // https://issues.dlang.org/show_bug.cgi?id=17646
+ st.push(fs._body.syntaxCopy());
+ Statement res = new CompoundStatement(loc, st);
+ }
+ else
+ {
+ st.append(Dsymbol.arraySyntaxCopy(dbody));
+ }
+ static if (!isStatic)
+ {
+ res = new ScopeStatement(loc, res, fs.endloc);
+ }
+ else static if (!isDecl)
+ {
+ auto fwd = new ForwardingStatement(loc, res);
+ res = fwd;
+ }
+ else
+ {
+ import dmd.attrib: ForwardingAttribDeclaration;
+ auto res = new ForwardingAttribDeclaration(st);
+ }
+ static if (!isDecl)
+ {
+ statements.push(res);
+ }
+ else
+ {
+ declarations.push(res);
+ }
+ }
+
+ static if (!isStatic)
+ {
+ Statement res = new UnrolledLoopStatement(loc, statements);
+ if (LabelStatement ls = checkLabeledLoop(sc, fs))
+ ls.gotoTarget = res;
+ if (te && te.e0)
+ res = new CompoundStatement(loc, new ExpStatement(te.e0.loc, te.e0), res);
+ }
+ else static if (!isDecl)
+ {
+ Statement res = new CompoundStatement(loc, statements);
+ }
+ else
+ {
+ auto res = declarations;
+ }
+ static if (!isDecl)
+ {
+ result = res;
+ }
+ else
+ {
+ return res;
+ }
+ }
+
+ override void visit(ForeachStatement fs)
+ {
+ /* https://dlang.org/spec/statement.html#foreach-statement
+ */
+
+ //printf("ForeachStatement::semantic() %p\n", fs);
+
+ /******
+ * Issue error if any of the ForeachTypes were not supplied and could not be inferred.
+ * Returns:
+ * true if error issued
+ */
+ static bool checkForArgTypes(ForeachStatement fs)
+ {
+ bool result = false;
+ foreach (p; *fs.parameters)
+ {
+ if (!p.type)
+ {
+ fs.error("cannot infer type for `foreach` variable `%s`, perhaps set it explicitly", p.ident.toChars());
+ p.type = Type.terror;
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ const loc = fs.loc;
+ const dim = fs.parameters.dim;
+
+ fs.func = sc.func;
+ if (fs.func.fes)
+ fs.func = fs.func.fes.func;
+
+ VarDeclaration vinit = null;
+ fs.aggr = fs.aggr.expressionSemantic(sc);
+ fs.aggr = resolveProperties(sc, fs.aggr);
+ fs.aggr = fs.aggr.optimize(WANTvalue);
+ if (fs.aggr.op == TOK.error)
+ return setError();
+ Expression oaggr = fs.aggr; // remember original for error messages
+ if (fs.aggr.type && fs.aggr.type.toBasetype().ty == Tstruct &&
+ (cast(TypeStruct)(fs.aggr.type.toBasetype())).sym.dtor &&
+ fs.aggr.op != TOK.type && !fs.aggr.isLvalue())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14653
+ // Extend the life of rvalue aggregate till the end of foreach.
+ vinit = copyToTemp(STC.rvalue, "__aggr", fs.aggr);
+ vinit.endlinnum = fs.endloc.linnum;
+ vinit.dsymbolSemantic(sc);
+ fs.aggr = new VarExp(fs.aggr.loc, vinit);
+ }
+
+ /* If aggregate is a vector type, add the .array to make it a static array
+ */
+ if (fs.aggr.type)
+ if (auto tv = fs.aggr.type.toBasetype().isTypeVector())
+ {
+ auto vae = new VectorArrayExp(fs.aggr.loc, fs.aggr);
+ vae.type = tv.basetype;
+ fs.aggr = vae;
+ }
+
+ Dsymbol sapply = null; // the inferred opApply() or front() function
+ if (!inferForeachAggregate(sc, fs.op == TOK.foreach_, fs.aggr, sapply))
+ {
+ const(char)* msg = "";
+ if (fs.aggr.type && isAggregate(fs.aggr.type))
+ {
+ msg = ", define `opApply()`, range primitives, or use `.tupleof`";
+ }
+ fs.error("invalid `foreach` aggregate `%s`%s", oaggr.toChars(), msg);
+ return setError();
+ }
+
+ Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
+
+ /* Check for inference errors
+ */
+ if (!inferApplyArgTypes(fs, sc, sapply))
+ {
+ /**
+ Try and extract the parameter count of the opApply callback function, e.g.:
+ int opApply(int delegate(int, float)) => 2 args
+ */
+ bool foundMismatch = false;
+ size_t foreachParamCount = 0;
+ if (sapplyOld)
+ {
+ if (FuncDeclaration fd = sapplyOld.isFuncDeclaration())
+ {
+ auto fparameters = fd.getParameterList();
+
+ if (fparameters.length == 1)
+ {
+ // first param should be the callback function
+ Parameter fparam = fparameters[0];
+ if ((fparam.type.ty == Tpointer ||
+ fparam.type.ty == Tdelegate) &&
+ fparam.type.nextOf().ty == Tfunction)
+ {
+ TypeFunction tf = cast(TypeFunction)fparam.type.nextOf();
+ foreachParamCount = tf.parameterList.length;
+ foundMismatch = true;
+ }
+ }
+ }
+ }
+
+ //printf("dim = %d, parameters.dim = %d\n", dim, parameters.dim);
+ if (foundMismatch && dim != foreachParamCount)
+ {
+ const(char)* plural = foreachParamCount > 1 ? "s" : "";
+ fs.error("cannot infer argument types, expected %llu argument%s, not %llu",
+ cast(ulong) foreachParamCount, plural, cast(ulong) dim);
+ }
+ else
+ fs.error("cannot uniquely infer `foreach` argument types");
+
+ return setError();
+ }
+
+ Type tab = fs.aggr.type.toBasetype();
+
+ if (tab.ty == Ttuple) // don't generate new scope for tuple loops
+ {
+ makeTupleForeach!(false,false)(fs);
+ if (vinit)
+ result = new CompoundStatement(loc, new ExpStatement(loc, vinit), result);
+ result = result.statementSemantic(sc);
+ return;
+ }
+
+ auto sym = new ScopeDsymbol();
+ sym.parent = sc.scopesym;
+ sym.endlinnum = fs.endloc.linnum;
+ auto sc2 = sc.push(sym);
+ sc2.inLoop = true;
+
+ foreach (Parameter p; *fs.parameters)
+ {
+ if (p.storageClass & STC.manifest)
+ {
+ fs.error("cannot declare `enum` loop variables for non-unrolled foreach");
+ }
+ if (p.storageClass & STC.alias_)
+ {
+ fs.error("cannot declare `alias` loop variables for non-unrolled foreach");
+ }
+ }
+
+ void retError()
+ {
+ sc2.pop();
+ result = new ErrorStatement();
+ }
+
+ void rangeError()
+ {
+ fs.error("cannot infer argument types");
+ return retError();
+ }
+
+ void retStmt(Statement s)
+ {
+ if (!s)
+ return retError();
+ s = s.statementSemantic(sc2);
+ sc2.pop();
+ result = s;
+ }
+
+ TypeAArray taa = null;
+ Type tn = null;
+ Type tnv = null;
+ Statement apply()
+ {
+ if (checkForArgTypes(fs))
+ return null;
+
+ TypeFunction tfld = null;
+ if (sapply)
+ {
+ FuncDeclaration fdapply = sapply.isFuncDeclaration();
+ if (fdapply)
+ {
+ assert(fdapply.type && fdapply.type.ty == Tfunction);
+ tfld = cast(TypeFunction)fdapply.type.typeSemantic(loc, sc2);
+ goto Lget;
+ }
+ else if (tab.ty == Tdelegate)
+ {
+ tfld = cast(TypeFunction)tab.nextOf();
+ Lget:
+ //printf("tfld = %s\n", tfld.toChars());
+ if (tfld.parameterList.parameters.dim == 1)
+ {
+ Parameter p = tfld.parameterList[0];
+ if (p.type && p.type.ty == Tdelegate)
+ {
+ auto t = p.type.typeSemantic(loc, sc2);
+ assert(t.ty == Tdelegate);
+ tfld = cast(TypeFunction)t.nextOf();
+ }
+ //printf("tfld = %s\n", tfld.toChars());
+ }
+ }
+ }
+
+ FuncExp flde = foreachBodyToFunction(sc2, fs, tfld);
+ if (!flde)
+ return null;
+
+ // Resolve any forward referenced goto's
+ foreach (ScopeStatement ss; *fs.gotos)
+ {
+ GotoStatement gs = ss.statement.isGotoStatement();
+ if (!gs.label.statement)
+ {
+ // 'Promote' it to this scope, and replace with a return
+ fs.cases.push(gs);
+ ss.statement = new ReturnStatement(Loc.initial, new IntegerExp(fs.cases.dim + 1));
+ }
+ }
+
+ Expression e = null;
+ Expression ec;
+ if (vinit)
+ {
+ e = new DeclarationExp(loc, vinit);
+ e = e.expressionSemantic(sc2);
+ if (e.op == TOK.error)
+ return null;
+ }
+
+ if (taa)
+ ec = applyAssocArray(fs, flde, taa);
+ else if (tab.ty == Tarray || tab.ty == Tsarray)
+ ec = applyArray(fs, flde, sc2, tn, tnv, tab.ty);
+ else if (tab.ty == Tdelegate)
+ ec = applyDelegate(fs, flde, sc2, tab);
+ else
+ ec = applyOpApply(fs, tab, sapply, sc2, flde);
+ if (!ec)
+ return null;
+ e = Expression.combine(e, ec);
+ return loopReturn(e, fs.cases, loc);
+ }
+ switch (tab.ty)
+ {
+ case Tarray:
+ case Tsarray:
+ {
+ if (checkForArgTypes(fs))
+ return retError();
+
+ if (dim < 1 || dim > 2)
+ {
+ fs.error("only one or two arguments for array `foreach`");
+ return retError();
+ }
+
+ // Finish semantic on all foreach parameter types.
+ foreach (i; 0 .. dim)
+ {
+ Parameter p = (*fs.parameters)[i];
+ p.type = p.type.typeSemantic(loc, sc2);
+ p.type = p.type.addStorageClass(p.storageClass);
+ }
+
+ tn = tab.nextOf().toBasetype();
+
+ if (dim == 2)
+ {
+ Type tindex = (*fs.parameters)[0].type;
+ if (!tindex.isintegral())
+ {
+ fs.error("foreach: key cannot be of non-integral type `%s`", tindex.toChars());
+ return retError();
+ }
+ /* What cases to deprecate implicit conversions for:
+ * 1. foreach aggregate is a dynamic array
+ * 2. foreach body is lowered to _aApply (see special case below).
+ */
+ Type tv = (*fs.parameters)[1].type.toBasetype();
+ if ((tab.ty == Tarray ||
+ (tn.ty != tv.ty && tn.ty.isSomeChar && tv.ty.isSomeChar)) &&
+ !Type.tsize_t.implicitConvTo(tindex))
+ {
+ fs.deprecation("foreach: loop index implicitly converted from `size_t` to `%s`",
+ tindex.toChars());
+ }
+ }
+
+ /* Look for special case of parsing char types out of char type
+ * array.
+ */
+ if (tn.ty.isSomeChar)
+ {
+ int i = (dim == 1) ? 0 : 1; // index of value
+ Parameter p = (*fs.parameters)[i];
+ tnv = p.type.toBasetype();
+ if (tnv.ty != tn.ty && tnv.ty.isSomeChar)
+ {
+ if (p.storageClass & STC.ref_)
+ {
+ fs.error("`foreach`: value of UTF conversion cannot be `ref`");
+ return retError();
+ }
+ if (dim == 2)
+ {
+ p = (*fs.parameters)[0];
+ if (p.storageClass & STC.ref_)
+ {
+ fs.error("`foreach`: key cannot be `ref`");
+ return retError();
+ }
+ }
+ return retStmt(apply());
+ }
+ }
+
+ // Declare the key
+ if (dim == 2)
+ {
+ Parameter p = (*fs.parameters)[0];
+ auto var = new VarDeclaration(loc, p.type.mutableOf(), Identifier.generateId("__key"), null);
+ var.storage_class |= STC.temp | STC.foreach_;
+ if (var.storage_class & (STC.ref_ | STC.out_))
+ var.storage_class |= STC.nodtor;
+
+ fs.key = var;
+ if (p.storageClass & STC.ref_)
+ {
+ if (var.type.constConv(p.type) == MATCH.nomatch)
+ {
+ fs.error("key type mismatch, `%s` to `ref %s`",
+ var.type.toChars(), p.type.toChars());
+ return retError();
+ }
+ }
+ if (tab.ty == Tsarray)
+ {
+ TypeSArray ta = cast(TypeSArray)tab;
+ IntRange dimrange = getIntRange(ta.dim);
+ // https://issues.dlang.org/show_bug.cgi?id=12504
+ dimrange.imax = SignExtendedNumber(dimrange.imax.value-1);
+ if (!IntRange.fromType(var.type).contains(dimrange))
+ {
+ fs.error("index type `%s` cannot cover index range 0..%llu",
+ p.type.toChars(), ta.dim.toInteger());
+ return retError();
+ }
+ fs.key.range = new IntRange(SignExtendedNumber(0), dimrange.imax);
+ }
+ }
+ // Now declare the value
+ {
+ Parameter p = (*fs.parameters)[dim - 1];
+ auto var = new VarDeclaration(loc, p.type, p.ident, null);
+ var.storage_class |= STC.foreach_;
+ var.storage_class |= p.storageClass & (STC.scope_ | STC.IOR | STC.TYPECTOR);
+ if (var.isReference())
+ var.storage_class |= STC.nodtor;
+
+ fs.value = var;
+ if (var.storage_class & STC.ref_)
+ {
+ if (fs.aggr.checkModifiable(sc2, ModifyFlags.noError) == Modifiable.initialization)
+ var.storage_class |= STC.ctorinit;
+
+ Type t = tab.nextOf();
+ if (t.constConv(p.type) == MATCH.nomatch)
+ {
+ fs.error("argument type mismatch, `%s` to `ref %s`",
+ t.toChars(), p.type.toChars());
+ return retError();
+ }
+ }
+ }
+
+ /* Convert to a ForStatement
+ * foreach (key, value; a) body =>
+ * for (T[] tmp = a[], size_t key; key < tmp.length; ++key)
+ * { T value = tmp[k]; body }
+ *
+ * foreach_reverse (key, value; a) body =>
+ * for (T[] tmp = a[], size_t key = tmp.length; key--; )
+ * { T value = tmp[k]; body }
+ */
+ auto id = Identifier.generateId("__r");
+ auto ie = new ExpInitializer(loc, new SliceExp(loc, fs.aggr, null, null));
+ const valueIsRef = cast(bool) ((*fs.parameters)[dim - 1].storageClass & STC.ref_);
+ VarDeclaration tmp;
+ if (fs.aggr.op == TOK.arrayLiteral && !valueIsRef)
+ {
+ auto ale = cast(ArrayLiteralExp)fs.aggr;
+ size_t edim = ale.elements ? ale.elements.dim : 0;
+ auto telem = (*fs.parameters)[dim - 1].type;
+
+ // https://issues.dlang.org/show_bug.cgi?id=12936
+ // if telem has been specified explicitly,
+ // converting array literal elements to telem might make it @nogc.
+ fs.aggr = fs.aggr.implicitCastTo(sc, telem.sarrayOf(edim));
+ if (fs.aggr.op == TOK.error)
+ return retError();
+
+ // for (T[edim] tmp = a, ...)
+ tmp = new VarDeclaration(loc, fs.aggr.type, id, ie);
+ }
+ else
+ {
+ tmp = new VarDeclaration(loc, tab.nextOf().arrayOf(), id, ie);
+ if (!valueIsRef)
+ tmp.storage_class |= STC.scope_;
+ }
+ tmp.storage_class |= STC.temp;
+
+ Expression tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id.length);
+
+ if (!fs.key)
+ {
+ Identifier idkey = Identifier.generateId("__key");
+ fs.key = new VarDeclaration(loc, Type.tsize_t, idkey, null);
+ fs.key.storage_class |= STC.temp;
+ }
+ else if (fs.key.type.ty != Type.tsize_t.ty)
+ {
+ tmp_length = new CastExp(loc, tmp_length, fs.key.type);
+ }
+ if (fs.op == TOK.foreach_reverse_)
+ fs.key._init = new ExpInitializer(loc, tmp_length);
+ else
+ fs.key._init = new ExpInitializer(loc, new IntegerExp(loc, 0, fs.key.type));
+
+ auto cs = new Statements();
+ if (vinit)
+ cs.push(new ExpStatement(loc, vinit));
+ cs.push(new ExpStatement(loc, tmp));
+ cs.push(new ExpStatement(loc, fs.key));
+ Statement forinit = new CompoundDeclarationStatement(loc, cs);
+
+ Expression cond;
+ if (fs.op == TOK.foreach_reverse_)
+ {
+ // key--
+ cond = new PostExp(TOK.minusMinus, loc, new VarExp(loc, fs.key));
+ }
+ else
+ {
+ // key < tmp.length
+ cond = new CmpExp(TOK.lessThan, loc, new VarExp(loc, fs.key), tmp_length);
+ }
+
+ Expression increment = null;
+ if (fs.op == TOK.foreach_)
+ {
+ // key += 1
+ increment = new AddAssignExp(loc, new VarExp(loc, fs.key), new IntegerExp(loc, 1, fs.key.type));
+ }
+
+ // T value = tmp[key];
+ IndexExp indexExp = new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, fs.key));
+ indexExp.indexIsInBounds = true; // disabling bounds checking in foreach statements.
+ fs.value._init = new ExpInitializer(loc, indexExp);
+ Statement ds = new ExpStatement(loc, fs.value);
+
+ if (dim == 2)
+ {
+ Parameter p = (*fs.parameters)[0];
+ if ((p.storageClass & STC.ref_) && p.type.equals(fs.key.type))
+ {
+ fs.key.range = null;
+ auto v = new AliasDeclaration(loc, p.ident, fs.key);
+ fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
+ }
+ else
+ {
+ auto ei = new ExpInitializer(loc, new IdentifierExp(loc, fs.key.ident));
+ auto v = new VarDeclaration(loc, p.type, p.ident, ei);
+ v.storage_class |= STC.foreach_ | (p.storageClass & STC.ref_);
+ fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
+ if (fs.key.range && !p.type.isMutable())
+ {
+ /* Limit the range of the key to the specified range
+ */
+ v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
+ }
+ }
+ }
+ fs._body = new CompoundStatement(loc, ds, fs._body);
+
+ Statement s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
+ if (auto ls = checkLabeledLoop(sc, fs)) // https://issues.dlang.org/show_bug.cgi?id=15450
+ // don't use sc2
+ ls.gotoTarget = s;
+ return retStmt(s);
+ }
+ case Taarray:
+ if (fs.op == TOK.foreach_reverse_)
+ fs.warning("cannot use `foreach_reverse` with an associative array");
+ if (checkForArgTypes(fs))
+ return retError();
+
+ taa = cast(TypeAArray)tab;
+ if (dim < 1 || dim > 2)
+ {
+ fs.error("only one or two arguments for associative array `foreach`");
+ return retError();
+ }
+ return retStmt(apply());
+
+ case Tclass:
+ case Tstruct:
+ /* Prefer using opApply, if it exists
+ */
+ if (sapply)
+ return retStmt(apply());
+ {
+ /* Look for range iteration, i.e. the properties
+ * .empty, .popFront, .popBack, .front and .back
+ * foreach (e; aggr) { ... }
+ * translates to:
+ * for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
+ * auto e = __r.front;
+ * ...
+ * }
+ */
+ auto ad = (tab.ty == Tclass) ?
+ cast(AggregateDeclaration)(cast(TypeClass)tab).sym :
+ cast(AggregateDeclaration)(cast(TypeStruct)tab).sym;
+ Identifier idfront;
+ Identifier idpopFront;
+ if (fs.op == TOK.foreach_)
+ {
+ idfront = Id.Ffront;
+ idpopFront = Id.FpopFront;
+ }
+ else
+ {
+ idfront = Id.Fback;
+ idpopFront = Id.FpopBack;
+ }
+ auto sfront = ad.search(Loc.initial, idfront);
+ if (!sfront)
+ return retStmt(apply());
+
+ /* Generate a temporary __r and initialize it with the aggregate.
+ */
+ VarDeclaration r;
+ Statement _init;
+ if (vinit && fs.aggr.op == TOK.variable && (cast(VarExp)fs.aggr).var == vinit)
+ {
+ r = vinit;
+ _init = new ExpStatement(loc, vinit);
+ }
+ else
+ {
+ r = copyToTemp(0, "__r", fs.aggr);
+ r.dsymbolSemantic(sc);
+ _init = new ExpStatement(loc, r);
+ if (vinit)
+ _init = new CompoundStatement(loc, new ExpStatement(loc, vinit), _init);
+ }
+
+ // !__r.empty
+ Expression e = new VarExp(loc, r);
+ e = new DotIdExp(loc, e, Id.Fempty);
+ Expression condition = new NotExp(loc, e);
+
+ // __r.idpopFront()
+ e = new VarExp(loc, r);
+ Expression increment = new CallExp(loc, new DotIdExp(loc, e, idpopFront));
+
+ /* Declaration statement for e:
+ * auto e = __r.idfront;
+ */
+ e = new VarExp(loc, r);
+ Expression einit = new DotIdExp(loc, e, idfront);
+ Statement makeargs, forbody;
+ bool ignoreRef = false; // If a range returns a non-ref front we ignore ref on foreach
+
+ Type tfront;
+ if (auto fd = sfront.isFuncDeclaration())
+ {
+ if (!fd.functionSemantic())
+ return rangeError();
+ tfront = fd.type;
+ }
+ else if (auto td = sfront.isTemplateDeclaration())
+ {
+ Expressions a;
+ if (auto f = resolveFuncCall(loc, sc, td, null, tab, &a, FuncResolveFlag.quiet))
+ tfront = f.type;
+ }
+ else if (auto d = sfront.toAlias().isDeclaration())
+ {
+ tfront = d.type;
+ }
+ if (!tfront || tfront.ty == Terror)
+ return rangeError();
+ if (tfront.toBasetype().ty == Tfunction)
+ {
+ auto ftt = cast(TypeFunction)tfront.toBasetype();
+ tfront = tfront.toBasetype().nextOf();
+ if (!ftt.isref)
+ {
+ // .front() does not return a ref. We ignore ref on foreach arg.
+ // see https://issues.dlang.org/show_bug.cgi?id=11934
+ if (tfront.needsDestruction()) ignoreRef = true;
+ }
+ }
+ if (tfront.ty == Tvoid)
+ {
+ fs.error("`%s.front` is `void` and has no value", oaggr.toChars());
+ return retError();
+ }
+
+ if (dim == 1)
+ {
+ auto p = (*fs.parameters)[0];
+ auto ve = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, einit));
+ ve.storage_class |= STC.foreach_;
+ ve.storage_class |= p.storageClass & (STC.scope_ | STC.IOR | STC.TYPECTOR);
+
+ if (ignoreRef)
+ ve.storage_class &= ~STC.ref_;
+
+ makeargs = new ExpStatement(loc, ve);
+ }
+ else
+ {
+ auto vd = copyToTemp(STC.ref_, "__front", einit);
+ vd.dsymbolSemantic(sc);
+ makeargs = new ExpStatement(loc, vd);
+
+ // Resolve inout qualifier of front type
+ tfront = tfront.substWildTo(tab.mod);
+
+ Expression ve = new VarExp(loc, vd);
+ ve.type = tfront;
+
+ auto exps = new Expressions();
+ exps.push(ve);
+ int pos = 0;
+ while (exps.dim < dim)
+ {
+ pos = expandAliasThisTuples(exps, pos);
+ if (pos == -1)
+ break;
+ }
+ if (exps.dim != dim)
+ {
+ const(char)* plural = exps.dim > 1 ? "s" : "";
+ fs.error("cannot infer argument types, expected %llu argument%s, not %llu",
+ cast(ulong) exps.dim, plural, cast(ulong) dim);
+ return retError();
+ }
+
+ foreach (i; 0 .. dim)
+ {
+ auto p = (*fs.parameters)[i];
+ auto exp = (*exps)[i];
+ version (none)
+ {
+ printf("[%d] p = %s %s, exp = %s %s\n", i,
+ p.type ? p.type.toChars() : "?", p.ident.toChars(),
+ exp.type.toChars(), exp.toChars());
+ }
+ if (!p.type)
+ p.type = exp.type;
+
+ auto sc = p.storageClass;
+ if (ignoreRef) sc &= ~STC.ref_;
+ p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2);
+ if (!exp.implicitConvTo(p.type))
+ return rangeError();
+
+ auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp));
+ var.storage_class |= STC.ctfe | STC.ref_ | STC.foreach_;
+ makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, var));
+ }
+ }
+
+ forbody = new CompoundStatement(loc, makeargs, fs._body);
+
+ Statement s = new ForStatement(loc, _init, condition, increment, forbody, fs.endloc);
+ if (auto ls = checkLabeledLoop(sc, fs))
+ ls.gotoTarget = s;
+
+ version (none)
+ {
+ printf("init: %s\n", _init.toChars());
+ printf("condition: %s\n", condition.toChars());
+ printf("increment: %s\n", increment.toChars());
+ printf("body: %s\n", forbody.toChars());
+ }
+ return retStmt(s);
+ }
+ case Tdelegate:
+ if (fs.op == TOK.foreach_reverse_)
+ fs.deprecation("cannot use `foreach_reverse` with a delegate");
+ return retStmt(apply());
+ case Terror:
+ return retError();
+ default:
+ fs.error("`foreach`: `%s` is not an aggregate type", fs.aggr.type.toChars());
+ return retError();
+ }
+ }
+
+ private static extern(D) Expression applyOpApply(ForeachStatement fs, Type tab, Dsymbol sapply,
+ Scope* sc2, Expression flde)
+ {
+ version (none)
+ {
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ {
+ message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`");
+ }
+ (cast(FuncExp)flde).fd.tookAddressOf = 1;
+ }
+ else
+ {
+ if (global.params.useDIP1000 == FeatureState.enabled)
+ ++(cast(FuncExp)flde).fd.tookAddressOf; // allocate a closure unless the opApply() uses 'scope'
+ }
+ assert(tab.ty == Tstruct || tab.ty == Tclass);
+ assert(sapply);
+ /* Call:
+ * aggr.apply(flde)
+ */
+ Expression ec;
+ ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident);
+ ec = new CallExp(fs.loc, ec, flde);
+ ec = ec.expressionSemantic(sc2);
+ if (ec.op == TOK.error)
+ return null;
+ if (ec.type != Type.tint32)
+ {
+ fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
+ return null;
+ }
+ return ec;
+ }
+
+ private static extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde,
+ Scope* sc2, Type tab)
+ {
+ Expression ec;
+ /* Call:
+ * aggr(flde)
+ */
+ if (fs.aggr.op == TOK.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() &&
+ !(cast(DelegateExp)fs.aggr).func.needThis())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=3560
+ fs.aggr = (cast(DelegateExp)fs.aggr).e1;
+ }
+ ec = new CallExp(fs.loc, fs.aggr, flde);
+ ec = ec.expressionSemantic(sc2);
+ if (ec.op == TOK.error)
+ return null;
+ if (ec.type != Type.tint32)
+ {
+ fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars());
+ return null;
+ }
+ return ec;
+ }
+
+ private static extern(D) Expression applyArray(ForeachStatement fs, Expression flde,
+ Scope* sc2, Type tn, Type tnv, TY tabty)
+ {
+ Expression ec;
+ const dim = fs.parameters.dim;
+ const loc = fs.loc;
+ /* Call:
+ * _aApply(aggr, flde)
+ */
+ __gshared const(char)** fntab =
+ [
+ "cc", "cw", "cd",
+ "wc", "cc", "wd",
+ "dc", "dw", "dd"
+ ];
+
+ const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
+ char[BUFFER_LEN] fdname;
+ int flag;
+
+ switch (tn.ty)
+ {
+ case Tchar: flag = 0; break;
+ case Twchar: flag = 3; break;
+ case Tdchar: flag = 6; break;
+ default:
+ assert(0);
+ }
+ switch (tnv.ty)
+ {
+ case Tchar: flag += 0; break;
+ case Twchar: flag += 1; break;
+ case Tdchar: flag += 2; break;
+ default:
+ assert(0);
+ }
+ const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : "";
+ int j = sprintf(fdname.ptr, "_aApply%s%.*s%llu", r, 2, fntab[flag], cast(ulong)dim);
+ assert(j < BUFFER_LEN);
+
+ FuncDeclaration fdapply;
+ TypeDelegate dgty;
+ auto params = new Parameters();
+ params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null));
+ auto dgparams = new Parameters();
+ dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
+ if (dim == 2)
+ dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
+ dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
+ params.push(new Parameter(0, dgty, null, null, null));
+ fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
+
+ if (tabty == Tsarray)
+ fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf());
+ // paint delegate argument to the type runtime expects
+ Expression fexp = flde;
+ if (!dgty.equals(flde.type))
+ {
+ fexp = new CastExp(loc, flde, flde.type);
+ fexp.type = dgty;
+ }
+ ec = new VarExp(Loc.initial, fdapply, false);
+ ec = new CallExp(loc, ec, fs.aggr, fexp);
+ ec.type = Type.tint32; // don't run semantic() on ec
+ return ec;
+ }
+
+ private static extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, TypeAArray taa)
+ {
+ Expression ec;
+ const dim = fs.parameters.dim;
+ // Check types
+ Parameter p = (*fs.parameters)[0];
+ bool isRef = (p.storageClass & STC.ref_) != 0;
+ Type ta = p.type;
+ if (dim == 2)
+ {
+ Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index);
+ if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
+ {
+ fs.error("`foreach`: index must be type `%s`, not `%s`",
+ ti.toChars(), ta.toChars());
+ return null;
+ }
+ p = (*fs.parameters)[1];
+ isRef = (p.storageClass & STC.ref_) != 0;
+ ta = p.type;
+ }
+ Type taav = taa.nextOf();
+ if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
+ {
+ fs.error("`foreach`: value must be type `%s`, not `%s`",
+ taav.toChars(), ta.toChars());
+ return null;
+ }
+
+ /* Call:
+ * extern(C) int _aaApply(void*, in size_t, int delegate(void*))
+ * _aaApply(aggr, keysize, flde)
+ *
+ * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
+ * _aaApply2(aggr, keysize, flde)
+ */
+ __gshared FuncDeclaration* fdapply = [null, null];
+ __gshared TypeDelegate* fldeTy = [null, null];
+ ubyte i = (dim == 2 ? 1 : 0);
+ if (!fdapply[i])
+ {
+ auto params = new Parameters();
+ params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null));
+ params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null));
+ auto dgparams = new Parameters();
+ dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
+ if (dim == 2)
+ dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null));
+ fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
+ params.push(new Parameter(0, fldeTy[i], null, null, null));
+ fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply);
+ }
+
+ auto exps = new Expressions();
+ exps.push(fs.aggr);
+ auto keysize = taa.index.size();
+ if (keysize == SIZE_INVALID)
+ return null;
+ assert(keysize < keysize.max - target.ptrsize);
+ keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
+ // paint delegate argument to the type runtime expects
+ Expression fexp = flde;
+ if (!fldeTy[i].equals(flde.type))
+ {
+ fexp = new CastExp(fs.loc, flde, flde.type);
+ fexp.type = fldeTy[i];
+ }
+ exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t));
+ exps.push(fexp);
+ ec = new VarExp(Loc.initial, fdapply[i], false);
+ ec = new CallExp(fs.loc, ec, exps);
+ ec.type = Type.tint32; // don't run semantic() on ec
+ return ec;
+ }
+
+ private static extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc)
+ {
+ if (!cases.dim)
+ {
+ // Easy case, a clean exit from the loop
+ e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899
+ return new ExpStatement(loc, e);
+ }
+ // Construct a switch statement around the return value
+ // of the apply function.
+ Statement s;
+ auto a = new Statements();
+
+ // default: break; takes care of cases 0 and 1
+ s = new BreakStatement(Loc.initial, null);
+ s = new DefaultStatement(Loc.initial, s);
+ a.push(s);
+
+ // cases 2...
+ foreach (i, c; *cases)
+ {
+ s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c);
+ a.push(s);
+ }
+
+ s = new CompoundStatement(loc, a);
+ return new SwitchStatement(loc, e, s, false);
+ }
+ /*************************************
+ * Turn foreach body into the function literal:
+ * int delegate(ref T param) { body }
+ * Params:
+ * sc = context
+ * fs = ForeachStatement
+ * tfld = type of function literal to be created, can be null
+ * Returns:
+ * Function literal created, as an expression
+ * null if error.
+ */
+ static FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld)
+ {
+ auto params = new Parameters();
+ foreach (i; 0 .. fs.parameters.dim)
+ {
+ Parameter p = (*fs.parameters)[i];
+ StorageClass stc = STC.ref_;
+ Identifier id;
+
+ p.type = p.type.typeSemantic(fs.loc, sc);
+ p.type = p.type.addStorageClass(p.storageClass);
+ if (tfld)
+ {
+ Parameter prm = tfld.parameterList[i];
+ //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars());
+ stc = prm.storageClass & STC.ref_;
+ id = p.ident; // argument copy is not need.
+ if ((p.storageClass & STC.ref_) != stc)
+ {
+ if (!stc)
+ {
+ fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars());
+ return null;
+ }
+ goto LcopyArg;
+ }
+ }
+ else if (p.storageClass & STC.ref_)
+ {
+ // default delegate parameters are marked as ref, then
+ // argument copy is not need.
+ id = p.ident;
+ }
+ else
+ {
+ // Make a copy of the ref argument so it isn't
+ // a reference.
+ LcopyArg:
+ id = Identifier.generateId("__applyArg", cast(int)i);
+
+ Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id));
+ auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie);
+ v.storage_class |= STC.temp;
+ Statement s = new ExpStatement(fs.loc, v);
+ fs._body = new CompoundStatement(fs.loc, s, fs._body);
+ }
+ params.push(new Parameter(stc, p.type, id, null, null));
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=13840
+ // Throwable nested function inside nothrow function is acceptable.
+ StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func);
+ auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc);
+ fs.cases = new Statements();
+ fs.gotos = new ScopeStatements();
+ auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs);
+ fld.fbody = fs._body;
+ Expression flde = new FuncExp(fs.loc, fld);
+ flde = flde.expressionSemantic(sc);
+ fld.tookAddressOf = 0;
+ if (flde.op == TOK.error)
+ return null;
+ return cast(FuncExp)flde;
+ }
+
+ override void visit(ForeachRangeStatement fs)
+ {
+ /* https://dlang.org/spec/statement.html#foreach-range-statement
+ */
+
+ //printf("ForeachRangeStatement::semantic() %p\n", fs);
+ auto loc = fs.loc;
+ fs.lwr = fs.lwr.expressionSemantic(sc);
+ fs.lwr = resolveProperties(sc, fs.lwr);
+ fs.lwr = fs.lwr.optimize(WANTvalue);
+ if (!fs.lwr.type)
+ {
+ fs.error("invalid range lower bound `%s`", fs.lwr.toChars());
+ return setError();
+ }
+
+ fs.upr = fs.upr.expressionSemantic(sc);
+ fs.upr = resolveProperties(sc, fs.upr);
+ fs.upr = fs.upr.optimize(WANTvalue);
+ if (!fs.upr.type)
+ {
+ fs.error("invalid range upper bound `%s`", fs.upr.toChars());
+ return setError();
+ }
+
+ if (fs.prm.type)
+ {
+ fs.prm.type = fs.prm.type.typeSemantic(loc, sc);
+ fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
+ fs.lwr = fs.lwr.implicitCastTo(sc, fs.prm.type);
+
+ if (fs.upr.implicitConvTo(fs.prm.type) || (fs.prm.storageClass & STC.ref_))
+ {
+ fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
+ }
+ else
+ {
+ // See if upr-1 fits in prm.type
+ Expression limit = new MinExp(loc, fs.upr, IntegerExp.literal!1);
+ limit = limit.expressionSemantic(sc);
+ limit = limit.optimize(WANTvalue);
+ if (!limit.implicitConvTo(fs.prm.type))
+ {
+ fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type);
+ }
+ }
+ }
+ else
+ {
+ /* Must infer types from lwr and upr
+ */
+ Type tlwr = fs.lwr.type.toBasetype();
+ if (tlwr.ty == Tstruct || tlwr.ty == Tclass)
+ {
+ /* Just picking the first really isn't good enough.
+ */
+ fs.prm.type = fs.lwr.type;
+ }
+ else if (fs.lwr.type == fs.upr.type)
+ {
+ /* Same logic as CondExp ?lwr:upr
+ */
+ fs.prm.type = fs.lwr.type;
+ }
+ else
+ {
+ scope AddExp ea = new AddExp(loc, fs.lwr, fs.upr);
+ if (typeCombine(ea, sc))
+ return setError();
+ fs.prm.type = ea.type;
+ fs.lwr = ea.e1;
+ fs.upr = ea.e2;
+ }
+ fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass);
+ }
+ if (fs.prm.type.ty == Terror || fs.lwr.op == TOK.error || fs.upr.op == TOK.error)
+ {
+ return setError();
+ }
+
+ /* Convert to a for loop:
+ * foreach (key; lwr .. upr) =>
+ * for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
+ *
+ * foreach_reverse (key; lwr .. upr) =>
+ * for (auto tmp = lwr, auto key = upr; key-- > tmp;)
+ */
+ auto ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.lwr : fs.upr);
+ fs.key = new VarDeclaration(loc, fs.upr.type.mutableOf(), Identifier.generateId("__key"), ie);
+ fs.key.storage_class |= STC.temp;
+ SignExtendedNumber lower = getIntRange(fs.lwr).imin;
+ SignExtendedNumber upper = getIntRange(fs.upr).imax;
+ if (lower <= upper)
+ {
+ fs.key.range = new IntRange(lower, upper);
+ }
+
+ Identifier id = Identifier.generateId("__limit");
+ ie = new ExpInitializer(loc, (fs.op == TOK.foreach_) ? fs.upr : fs.lwr);
+ auto tmp = new VarDeclaration(loc, fs.upr.type, id, ie);
+ tmp.storage_class |= STC.temp;
+
+ auto cs = new Statements();
+ // Keep order of evaluation as lwr, then upr
+ if (fs.op == TOK.foreach_)
+ {
+ cs.push(new ExpStatement(loc, fs.key));
+ cs.push(new ExpStatement(loc, tmp));
+ }
+ else
+ {
+ cs.push(new ExpStatement(loc, tmp));
+ cs.push(new ExpStatement(loc, fs.key));
+ }
+ Statement forinit = new CompoundDeclarationStatement(loc, cs);
+
+ Expression cond;
+ if (fs.op == TOK.foreach_reverse_)
+ {
+ cond = new PostExp(TOK.minusMinus, loc, new VarExp(loc, fs.key));
+ if (fs.prm.type.isscalar())
+ {
+ // key-- > tmp
+ cond = new CmpExp(TOK.greaterThan, loc, cond, new VarExp(loc, tmp));
+ }
+ else
+ {
+ // key-- != tmp
+ cond = new EqualExp(TOK.notEqual, loc, cond, new VarExp(loc, tmp));
+ }
+ }
+ else
+ {
+ if (fs.prm.type.isscalar())
+ {
+ // key < tmp
+ cond = new CmpExp(TOK.lessThan, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
+ }
+ else
+ {
+ // key != tmp
+ cond = new EqualExp(TOK.notEqual, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp));
+ }
+ }
+
+ Expression increment = null;
+ if (fs.op == TOK.foreach_)
+ {
+ // key += 1
+ //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), IntegerExp.literal!1);
+ increment = new PreExp(TOK.prePlusPlus, loc, new VarExp(loc, fs.key));
+ }
+ if ((fs.prm.storageClass & STC.ref_) && fs.prm.type.equals(fs.key.type))
+ {
+ fs.key.range = null;
+ auto v = new AliasDeclaration(loc, fs.prm.ident, fs.key);
+ fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
+ }
+ else
+ {
+ ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.prm.type));
+ auto v = new VarDeclaration(loc, fs.prm.type, fs.prm.ident, ie);
+ v.storage_class |= STC.temp | STC.foreach_ | (fs.prm.storageClass & STC.ref_);
+ fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body);
+ if (fs.key.range && !fs.prm.type.isMutable())
+ {
+ /* Limit the range of the key to the specified range
+ */
+ v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1));
+ }
+ }
+ if (fs.prm.storageClass & STC.ref_)
+ {
+ if (fs.key.type.constConv(fs.prm.type) == MATCH.nomatch)
+ {
+ fs.error("argument type mismatch, `%s` to `ref %s`", fs.key.type.toChars(), fs.prm.type.toChars());
+ return setError();
+ }
+ }
+
+ auto s = new ForStatement(loc, forinit, cond, increment, fs._body, fs.endloc);
+ if (LabelStatement ls = checkLabeledLoop(sc, fs))
+ ls.gotoTarget = s;
+ result = s.statementSemantic(sc);
+ }
+
+ override void visit(IfStatement ifs)
+ {
+ /* https://dlang.org/spec/statement.html#IfStatement
+ */
+
+ // check in syntax level
+ ifs.condition = checkAssignmentAsCondition(ifs.condition);
+
+ auto sym = new ScopeDsymbol();
+ sym.parent = sc.scopesym;
+ sym.endlinnum = ifs.endloc.linnum;
+ Scope* scd = sc.push(sym);
+ if (ifs.prm)
+ {
+ /* Declare prm, which we will set to be the
+ * result of condition.
+ */
+ auto ei = new ExpInitializer(ifs.loc, ifs.condition);
+ ifs.match = new VarDeclaration(ifs.loc, ifs.prm.type, ifs.prm.ident, ei);
+ ifs.match.parent = scd.func;
+ ifs.match.storage_class |= ifs.prm.storageClass;
+ ifs.match.dsymbolSemantic(scd);
+
+ auto de = new DeclarationExp(ifs.loc, ifs.match);
+ auto ve = new VarExp(ifs.loc, ifs.match);
+ ifs.condition = new CommaExp(ifs.loc, de, ve);
+ ifs.condition = ifs.condition.expressionSemantic(scd);
+
+ if (ifs.match.edtor)
+ {
+ Statement sdtor = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
+ sdtor = new ScopeGuardStatement(ifs.loc, TOK.onScopeExit, sdtor);
+ ifs.ifbody = new CompoundStatement(ifs.loc, sdtor, ifs.ifbody);
+ ifs.match.storage_class |= STC.nodtor;
+
+ // the destructor is always called
+ // whether the 'ifbody' is executed or not
+ Statement sdtor2 = new DtorExpStatement(ifs.loc, ifs.match.edtor, ifs.match);
+ if (ifs.elsebody)
+ ifs.elsebody = new CompoundStatement(ifs.loc, sdtor2, ifs.elsebody);
+ else
+ ifs.elsebody = sdtor2;
+ }
+ }
+ else
+ {
+ if (ifs.condition.op == TOK.dotIdentifier)
+ (cast(DotIdExp)ifs.condition).noderef = true;
+
+ ifs.condition = ifs.condition.expressionSemantic(scd);
+ ifs.condition = resolveProperties(scd, ifs.condition);
+ ifs.condition = ifs.condition.addDtorHook(scd);
+ }
+ if (checkNonAssignmentArrayOp(ifs.condition))
+ ifs.condition = ErrorExp.get();
+ ifs.condition = checkGC(scd, ifs.condition);
+
+ // Convert to boolean after declaring prm so this works:
+ // if (S prm = S()) {}
+ // where S is a struct that defines opCast!bool.
+ ifs.condition = ifs.condition.toBoolean(scd);
+
+ // If we can short-circuit evaluate the if statement, don't do the
+ // semantic analysis of the skipped code.
+ // This feature allows a limited form of conditional compilation.
+ ifs.condition = ifs.condition.optimize(WANTvalue);
+
+ // Save 'root' of two branches (then and else) at the point where it forks
+ CtorFlow ctorflow_root = scd.ctorflow.clone();
+
+ ifs.ifbody = ifs.ifbody.semanticNoScope(scd);
+ scd.pop();
+
+ CtorFlow ctorflow_then = sc.ctorflow; // move flow results
+ sc.ctorflow = ctorflow_root; // reset flow analysis back to root
+ if (ifs.elsebody)
+ ifs.elsebody = ifs.elsebody.semanticScope(sc, null, null, null);
+
+ // Merge 'then' results into 'else' results
+ sc.merge(ifs.loc, ctorflow_then);
+
+ ctorflow_then.freeFieldinit(); // free extra copy of the data
+
+ if (ifs.condition.op == TOK.error ||
+ (ifs.ifbody && ifs.ifbody.isErrorStatement()) ||
+ (ifs.elsebody && ifs.elsebody.isErrorStatement()))
+ {
+ return setError();
+ }
+ result = ifs;
+ }
+
+ override void visit(ConditionalStatement cs)
+ {
+ //printf("ConditionalStatement::semantic()\n");
+
+ // If we can short-circuit evaluate the if statement, don't do the
+ // semantic analysis of the skipped code.
+ // This feature allows a limited form of conditional compilation.
+ if (cs.condition.include(sc))
+ {
+ DebugCondition dc = cs.condition.isDebugCondition();
+ if (dc)
+ {
+ sc = sc.push();
+ sc.flags |= SCOPE.debug_;
+ cs.ifbody = cs.ifbody.statementSemantic(sc);
+ sc.pop();
+ }
+ else
+ cs.ifbody = cs.ifbody.statementSemantic(sc);
+ result = cs.ifbody;
+ }
+ else
+ {
+ if (cs.elsebody)
+ cs.elsebody = cs.elsebody.statementSemantic(sc);
+ result = cs.elsebody;
+ }
+ }
+
+ override void visit(PragmaStatement ps)
+ {
+ /* https://dlang.org/spec/statement.html#pragma-statement
+ */
+ // Should be merged with PragmaDeclaration
+
+ //printf("PragmaStatement::semantic() %s\n", ps.toChars());
+ //printf("body = %p\n", ps._body);
+ if (ps.ident == Id.msg)
+ {
+ if (ps.args)
+ {
+ foreach (arg; *ps.args)
+ {
+ sc = sc.startCTFE();
+ auto e = arg.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+
+ // pragma(msg) is allowed to contain types as well as expressions
+ e = ctfeInterpretForPragmaMsg(e);
+ if (e.op == TOK.error)
+ {
+ errorSupplemental(ps.loc, "while evaluating `pragma(msg, %s)`", arg.toChars());
+ return setError();
+ }
+ if (auto se = e.toStringExp())
+ {
+ const slice = se.toUTF8(sc).peekString();
+ fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr);
+ }
+ else
+ fprintf(stderr, "%s", e.toChars());
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+ else if (ps.ident == Id.lib)
+ {
+ version (all)
+ {
+ /* Should this be allowed?
+ */
+ ps.error("`pragma(lib)` not allowed as statement");
+ return setError();
+ }
+ else
+ {
+ if (!ps.args || ps.args.dim != 1)
+ {
+ ps.error("`string` expected for library name");
+ return setError();
+ }
+ else
+ {
+ auto se = semanticString(sc, (*ps.args)[0], "library name");
+ if (!se)
+ return setError();
+
+ if (global.params.verbose)
+ {
+ message("library %.*s", cast(int)se.len, se.string);
+ }
+ }
+ }
+ }
+ else if (ps.ident == Id.linkerDirective)
+ {
+ /* Should this be allowed?
+ */
+ ps.error("`pragma(linkerDirective)` not allowed as statement");
+ return setError();
+ }
+ else if (ps.ident == Id.startaddress)
+ {
+ if (!ps.args || ps.args.dim != 1)
+ ps.error("function name expected for start address");
+ else
+ {
+ Expression e = (*ps.args)[0];
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+
+ e = e.ctfeInterpret();
+ (*ps.args)[0] = e;
+ Dsymbol sa = getDsymbol(e);
+ if (!sa || !sa.isFuncDeclaration())
+ {
+ ps.error("function name expected for start address, not `%s`", e.toChars());
+ return setError();
+ }
+ if (ps._body)
+ {
+ ps._body = ps._body.statementSemantic(sc);
+ if (ps._body.isErrorStatement())
+ {
+ result = ps._body;
+ return;
+ }
+ }
+ result = ps;
+ return;
+ }
+ }
+ else if (ps.ident == Id.Pinline)
+ {
+ PINLINE inlining = PINLINE.default_;
+ if (!ps.args || ps.args.dim == 0)
+ inlining = PINLINE.default_;
+ else if (!ps.args || ps.args.dim != 1)
+ {
+ ps.error("boolean expression expected for `pragma(inline)`");
+ return setError();
+ }
+ else
+ {
+ Expression e = (*ps.args)[0];
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = e.ctfeInterpret();
+ e = e.toBoolean(sc);
+ if (e.isErrorExp())
+ {
+ ps.error("pragma(`inline`, `true` or `false`) expected, not `%s`", (*ps.args)[0].toChars());
+ return setError();
+ }
+
+ if (e.isBool(true))
+ inlining = PINLINE.always;
+ else if (e.isBool(false))
+ inlining = PINLINE.never;
+
+ FuncDeclaration fd = sc.func;
+ if (!fd)
+ {
+ ps.error("`pragma(inline)` is not inside a function");
+ return setError();
+ }
+ fd.inlining = inlining;
+ }
+ }
+ else if (!global.params.ignoreUnsupportedPragmas)
+ {
+ ps.error("unrecognized `pragma(%s)`", ps.ident.toChars());
+ return setError();
+ }
+
+ if (ps._body)
+ {
+ if (ps.ident == Id.msg || ps.ident == Id.startaddress)
+ {
+ ps.error("`pragma(%s)` is missing a terminating `;`", ps.ident.toChars());
+ return setError();
+ }
+ ps._body = ps._body.statementSemantic(sc);
+ }
+ result = ps._body;
+ }
+
+ override void visit(StaticAssertStatement s)
+ {
+ s.sa.semantic2(sc);
+ if (s.sa.errors)
+ return setError();
+ }
+
+ override void visit(SwitchStatement ss)
+ {
+ /* https://dlang.org/spec/statement.html#switch-statement
+ */
+
+ //printf("SwitchStatement::semantic(%p)\n", ss);
+ ss.tryBody = sc.tryBody;
+ ss.tf = sc.tf;
+ if (ss.cases)
+ {
+ result = ss; // already run
+ return;
+ }
+
+ bool conditionError = false;
+ ss.condition = ss.condition.expressionSemantic(sc);
+ ss.condition = resolveProperties(sc, ss.condition);
+
+ Type att = null;
+ TypeEnum te = null;
+ while (ss.condition.op != TOK.error)
+ {
+ // preserve enum type for final switches
+ if (ss.condition.type.ty == Tenum)
+ te = cast(TypeEnum)ss.condition.type;
+ if (ss.condition.type.isString())
+ {
+ // If it's not an array, cast it to one
+ if (ss.condition.type.ty != Tarray)
+ {
+ ss.condition = ss.condition.implicitCastTo(sc, ss.condition.type.nextOf().arrayOf());
+ }
+ ss.condition.type = ss.condition.type.constOf();
+ break;
+ }
+ ss.condition = integralPromotions(ss.condition, sc);
+ if (ss.condition.op != TOK.error && ss.condition.type.isintegral())
+ break;
+
+ auto ad = isAggregate(ss.condition.type);
+ if (ad && ad.aliasthis && !isRecursiveAliasThis(att, ss.condition.type))
+ {
+ if (auto e = resolveAliasThis(sc, ss.condition, true))
+ {
+ ss.condition = e;
+ continue;
+ }
+ }
+
+ if (ss.condition.op != TOK.error)
+ {
+ ss.error("`%s` must be of integral or string type, it is a `%s`",
+ ss.condition.toChars(), ss.condition.type.toChars());
+ conditionError = true;
+ break;
+ }
+ }
+ if (checkNonAssignmentArrayOp(ss.condition))
+ ss.condition = ErrorExp.get();
+ ss.condition = ss.condition.optimize(WANTvalue);
+ ss.condition = checkGC(sc, ss.condition);
+ if (ss.condition.op == TOK.error)
+ conditionError = true;
+
+ bool needswitcherror = false;
+
+ ss.lastVar = sc.lastVar;
+
+ sc = sc.push();
+ sc.sbreak = ss;
+ sc.sw = ss;
+
+ ss.cases = new CaseStatements();
+ const inLoopSave = sc.inLoop;
+ sc.inLoop = true; // BUG: should use Scope::mergeCallSuper() for each case instead
+ ss._body = ss._body.statementSemantic(sc);
+ sc.inLoop = inLoopSave;
+
+ if (conditionError || (ss._body && ss._body.isErrorStatement()))
+ {
+ sc.pop();
+ return setError();
+ }
+
+ // Resolve any goto case's with exp
+ Lgotocase:
+ foreach (gcs; ss.gotoCases)
+ {
+ if (!gcs.exp)
+ {
+ gcs.error("no `case` statement following `goto case;`");
+ sc.pop();
+ return setError();
+ }
+
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (!scx.sw)
+ continue;
+ foreach (cs; *scx.sw.cases)
+ {
+ if (cs.exp.equals(gcs.exp))
+ {
+ gcs.cs = cs;
+ continue Lgotocase;
+ }
+ }
+ }
+ gcs.error("`case %s` not found", gcs.exp.toChars());
+ sc.pop();
+ return setError();
+ }
+
+ if (ss.isFinal)
+ {
+ Type t = ss.condition.type;
+ Dsymbol ds;
+ EnumDeclaration ed = null;
+ if (t && ((ds = t.toDsymbol(sc)) !is null))
+ ed = ds.isEnumDeclaration(); // typedef'ed enum
+ if (!ed && te && ((ds = te.toDsymbol(sc)) !is null))
+ ed = ds.isEnumDeclaration();
+ if (ed && ss.cases.length < ed.members.length)
+ {
+ int missingMembers = 0;
+ const maxShown = !global.params.verbose ? 6 : int.max;
+ Lmembers:
+ foreach (es; *ed.members)
+ {
+ EnumMember em = es.isEnumMember();
+ if (em)
+ {
+ foreach (cs; *ss.cases)
+ {
+ if (cs.exp.equals(em.value) || (!cs.exp.type.isString() &&
+ !em.value.type.isString() && cs.exp.toInteger() == em.value.toInteger()))
+ continue Lmembers;
+ }
+ if (missingMembers == 0)
+ ss.error("missing cases for `enum` members in `final switch`:");
+
+ if (missingMembers < maxShown)
+ errorSupplemental(ss.loc, "`%s`", em.toChars());
+ missingMembers++;
+ }
+ }
+ if (missingMembers > 0)
+ {
+ if (missingMembers > maxShown)
+ errorSupplemental(ss.loc, "... (%d more, -v to show) ...", missingMembers - maxShown);
+ sc.pop();
+ return setError();
+ }
+ }
+ else
+ needswitcherror = true;
+ }
+
+ if (!sc.sw.sdefault && (!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on))
+ {
+ ss.hasNoDefault = 1;
+
+ if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()))
+ ss.error("`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`");
+
+ // Generate runtime error if the default is hit
+ auto a = new Statements();
+ CompoundStatement cs;
+ Statement s;
+
+ if (global.params.useSwitchError == CHECKENABLE.on &&
+ global.params.checkAction != CHECKACTION.halt)
+ {
+ if (global.params.checkAction == CHECKACTION.C)
+ {
+ /* Rewrite as an assert(0) and let e2ir generate
+ * the call to the C assert failure function
+ */
+ s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0));
+ }
+ else
+ {
+ if (!verifyHookExist(ss.loc, *sc, Id.__switch_error, "generating assert messages"))
+ return setError();
+
+ Expression sl = new IdentifierExp(ss.loc, Id.empty);
+ sl = new DotIdExp(ss.loc, sl, Id.object);
+ sl = new DotIdExp(ss.loc, sl, Id.__switch_error);
+
+ Expressions* args = new Expressions(2);
+ (*args)[0] = new StringExp(ss.loc, ss.loc.filename.toDString());
+ (*args)[1] = new IntegerExp(ss.loc.linnum);
+
+ sl = new CallExp(ss.loc, sl, args);
+ sl = sl.expressionSemantic(sc);
+
+ s = new SwitchErrorStatement(ss.loc, sl);
+ }
+ }
+ else
+ s = new ExpStatement(ss.loc, new HaltExp(ss.loc));
+
+ a.reserve(2);
+ sc.sw.sdefault = new DefaultStatement(ss.loc, s);
+ a.push(ss._body);
+ if (ss._body.blockExit(sc.func, false) & BE.fallthru)
+ a.push(new BreakStatement(Loc.initial, null));
+ a.push(sc.sw.sdefault);
+ cs = new CompoundStatement(ss.loc, a);
+ ss._body = cs;
+ }
+
+ if (ss.checkLabel())
+ {
+ sc.pop();
+ return setError();
+ }
+
+
+ if (!ss.condition.type.isString())
+ {
+ sc.pop();
+ result = ss;
+ return;
+ }
+
+ // Transform a switch with string labels into a switch with integer labels.
+
+ // The integer value of each case corresponds to the index of each label
+ // string in the sorted array of label strings.
+
+ // The value of the integer condition is obtained by calling the druntime template
+ // switch(object.__switch(cond, options...)) {0: {...}, 1: {...}, ...}
+
+ // We sort a copy of the array of labels because we want to do a binary search in object.__switch,
+ // without modifying the order of the case blocks here in the compiler.
+
+ if (!verifyHookExist(ss.loc, *sc, Id.__switch, "switch cases on strings"))
+ return setError();
+
+ size_t numcases = 0;
+ if (ss.cases)
+ numcases = ss.cases.dim;
+
+ for (size_t i = 0; i < numcases; i++)
+ {
+ CaseStatement cs = (*ss.cases)[i];
+ cs.index = cast(int)i;
+ }
+
+ // Make a copy of all the cases so that qsort doesn't scramble the actual
+ // data we pass to codegen (the order of the cases in the switch).
+ CaseStatements *csCopy = (*ss.cases).copy();
+
+ if (numcases)
+ {
+ static int sort_compare(in CaseStatement* x, in CaseStatement* y) @trusted
+ {
+ auto se1 = x.exp.isStringExp();
+ auto se2 = y.exp.isStringExp();
+ return (se1 && se2) ? se1.compare(se2) : 0;
+ }
+ // Sort cases for efficient lookup
+ csCopy.sort!sort_compare;
+ }
+
+ // The actual lowering
+ auto arguments = new Expressions();
+ arguments.push(ss.condition);
+
+ auto compileTimeArgs = new Objects();
+
+ // The type & label no.
+ compileTimeArgs.push(new TypeExp(ss.loc, ss.condition.type.nextOf()));
+
+ // The switch labels
+ foreach (caseString; *csCopy)
+ {
+ compileTimeArgs.push(caseString.exp);
+ }
+
+ Expression sl = new IdentifierExp(ss.loc, Id.empty);
+ sl = new DotIdExp(ss.loc, sl, Id.object);
+ sl = new DotTemplateInstanceExp(ss.loc, sl, Id.__switch, compileTimeArgs);
+
+ sl = new CallExp(ss.loc, sl, arguments);
+ sl = sl.expressionSemantic(sc);
+ ss.condition = sl;
+
+ auto i = 0;
+ foreach (c; *csCopy)
+ {
+ (*ss.cases)[c.index].exp = new IntegerExp(i++);
+ }
+
+ //printf("%s\n", ss._body.toChars());
+ ss.statementSemantic(sc);
+
+ sc.pop();
+ result = ss;
+ }
+
+ override void visit(CaseStatement cs)
+ {
+ SwitchStatement sw = sc.sw;
+ bool errors = false;
+
+ //printf("CaseStatement::semantic() %s\n", toChars());
+ sc = sc.startCTFE();
+ cs.exp = cs.exp.expressionSemantic(sc);
+ cs.exp = resolveProperties(sc, cs.exp);
+ sc = sc.endCTFE();
+
+ if (sw)
+ {
+ Expression initialExp = cs.exp;
+
+ cs.exp = cs.exp.implicitCastTo(sc, sw.condition.type);
+ cs.exp = cs.exp.optimize(WANTvalue | WANTexpand);
+
+ Expression e = cs.exp;
+ // Remove all the casts the user and/or implicitCastTo may introduce
+ // otherwise we'd sometimes fail the check below.
+ while (e.op == TOK.cast_)
+ e = (cast(CastExp)e).e1;
+
+ /* This is where variables are allowed as case expressions.
+ */
+ if (e.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)e;
+ VarDeclaration v = ve.var.isVarDeclaration();
+ Type t = cs.exp.type.toBasetype();
+ if (v && (t.isintegral() || t.ty == Tclass))
+ {
+ /* Flag that we need to do special code generation
+ * for this, i.e. generate a sequence of if-then-else
+ */
+ sw.hasVars = 1;
+
+ /* TODO check if v can be uninitialized at that point.
+ */
+ if (!v.isConst() && !v.isImmutable())
+ {
+ cs.error("`case` variables have to be `const` or `immutable`");
+ }
+
+ if (sw.isFinal)
+ {
+ cs.error("`case` variables not allowed in `final switch` statements");
+ errors = true;
+ }
+
+ /* Find the outermost scope `scx` that set `sw`.
+ * Then search scope `scx` for a declaration of `v`.
+ */
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (scx.enclosing && scx.enclosing.sw == sw)
+ continue;
+ assert(scx.sw == sw);
+
+ if (!scx.search(cs.exp.loc, v.ident, null))
+ {
+ cs.error("`case` variable `%s` declared at %s cannot be declared in `switch` body",
+ v.toChars(), v.loc.toChars());
+ errors = true;
+ }
+ break;
+ }
+ goto L1;
+ }
+ }
+ else
+ cs.exp = cs.exp.ctfeInterpret();
+
+ if (StringExp se = cs.exp.toStringExp())
+ cs.exp = se;
+ else if (cs.exp.op != TOK.int64 && cs.exp.op != TOK.error)
+ {
+ cs.error("`case` must be a `string` or an integral constant, not `%s`", cs.exp.toChars());
+ errors = true;
+ }
+
+ L1:
+ foreach (cs2; *sw.cases)
+ {
+ //printf("comparing '%s' with '%s'\n", exp.toChars(), cs.exp.toChars());
+ if (cs2.exp.equals(cs.exp))
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=15909
+ cs.error("duplicate `case %s` in `switch` statement", initialExp.toChars());
+ errors = true;
+ break;
+ }
+ }
+
+ sw.cases.push(cs);
+
+ // Resolve any goto case's with no exp to this case statement
+ for (size_t i = 0; i < sw.gotoCases.dim;)
+ {
+ GotoCaseStatement gcs = sw.gotoCases[i];
+ if (!gcs.exp)
+ {
+ gcs.cs = cs;
+ sw.gotoCases.remove(i); // remove from array
+ continue;
+ }
+ i++;
+ }
+
+ if (sc.sw.tf != sc.tf)
+ {
+ cs.error("`switch` and `case` are in different `finally` blocks");
+ errors = true;
+ }
+ if (sc.sw.tryBody != sc.tryBody)
+ {
+ cs.error("case cannot be in different `try` block level from `switch`");
+ errors = true;
+ }
+ }
+ else
+ {
+ cs.error("`case` not in `switch` statement");
+ errors = true;
+ }
+
+ sc.ctorflow.orCSX(CSX.label);
+ cs.statement = cs.statement.statementSemantic(sc);
+ if (cs.statement.isErrorStatement())
+ {
+ result = cs.statement;
+ return;
+ }
+ if (errors || cs.exp.op == TOK.error)
+ return setError();
+
+ cs.lastVar = sc.lastVar;
+ result = cs;
+ }
+
+ override void visit(CaseRangeStatement crs)
+ {
+ SwitchStatement sw = sc.sw;
+ if (sw is null)
+ {
+ crs.error("case range not in `switch` statement");
+ return setError();
+ }
+
+ //printf("CaseRangeStatement::semantic() %s\n", toChars());
+ bool errors = false;
+ if (sw.isFinal)
+ {
+ crs.error("case ranges not allowed in `final switch`");
+ errors = true;
+ }
+
+ sc = sc.startCTFE();
+ crs.first = crs.first.expressionSemantic(sc);
+ crs.first = resolveProperties(sc, crs.first);
+ sc = sc.endCTFE();
+ crs.first = crs.first.implicitCastTo(sc, sw.condition.type);
+ crs.first = crs.first.ctfeInterpret();
+
+ sc = sc.startCTFE();
+ crs.last = crs.last.expressionSemantic(sc);
+ crs.last = resolveProperties(sc, crs.last);
+ sc = sc.endCTFE();
+ crs.last = crs.last.implicitCastTo(sc, sw.condition.type);
+ crs.last = crs.last.ctfeInterpret();
+
+ if (crs.first.op == TOK.error || crs.last.op == TOK.error || errors)
+ {
+ if (crs.statement)
+ crs.statement.statementSemantic(sc);
+ return setError();
+ }
+
+ uinteger_t fval = crs.first.toInteger();
+ uinteger_t lval = crs.last.toInteger();
+ if ((crs.first.type.isunsigned() && fval > lval) || (!crs.first.type.isunsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval))
+ {
+ crs.error("first `case %s` is greater than last `case %s`", crs.first.toChars(), crs.last.toChars());
+ errors = true;
+ lval = fval;
+ }
+
+ if (lval - fval > 256)
+ {
+ crs.error("had %llu cases which is more than 256 cases in case range", lval - fval);
+ errors = true;
+ lval = fval + 256;
+ }
+
+ if (errors)
+ return setError();
+
+ /* This works by replacing the CaseRange with an array of Case's.
+ *
+ * case a: .. case b: s;
+ * =>
+ * case a:
+ * [...]
+ * case b:
+ * s;
+ */
+
+ auto statements = new Statements();
+ for (uinteger_t i = fval; i != lval + 1; i++)
+ {
+ Statement s = crs.statement;
+ if (i != lval) // if not last case
+ s = new ExpStatement(crs.loc, cast(Expression)null);
+ Expression e = new IntegerExp(crs.loc, i, crs.first.type);
+ Statement cs = new CaseStatement(crs.loc, e, s);
+ statements.push(cs);
+ }
+ Statement s = new CompoundStatement(crs.loc, statements);
+ sc.ctorflow.orCSX(CSX.label);
+ s = s.statementSemantic(sc);
+ result = s;
+ }
+
+ override void visit(DefaultStatement ds)
+ {
+ //printf("DefaultStatement::semantic()\n");
+ bool errors = false;
+ if (sc.sw)
+ {
+ if (sc.sw.sdefault)
+ {
+ ds.error("`switch` statement already has a default");
+ errors = true;
+ }
+ sc.sw.sdefault = ds;
+
+ if (sc.sw.tf != sc.tf)
+ {
+ ds.error("`switch` and `default` are in different `finally` blocks");
+ errors = true;
+ }
+ if (sc.sw.tryBody != sc.tryBody)
+ {
+ ds.error("default cannot be in different `try` block level from `switch`");
+ errors = true;
+ }
+ if (sc.sw.isFinal)
+ {
+ ds.error("`default` statement not allowed in `final switch` statement");
+ errors = true;
+ }
+ }
+ else
+ {
+ ds.error("`default` not in `switch` statement");
+ errors = true;
+ }
+
+ sc.ctorflow.orCSX(CSX.label);
+ ds.statement = ds.statement.statementSemantic(sc);
+ if (errors || ds.statement.isErrorStatement())
+ return setError();
+
+ ds.lastVar = sc.lastVar;
+ result = ds;
+ }
+
+ override void visit(GotoDefaultStatement gds)
+ {
+ /* https://dlang.org/spec/statement.html#goto-statement
+ */
+
+ gds.sw = sc.sw;
+ if (!gds.sw)
+ {
+ gds.error("`goto default` not in `switch` statement");
+ return setError();
+ }
+ if (gds.sw.isFinal)
+ {
+ gds.error("`goto default` not allowed in `final switch` statement");
+ return setError();
+ }
+ result = gds;
+ }
+
+ override void visit(GotoCaseStatement gcs)
+ {
+ /* https://dlang.org/spec/statement.html#goto-statement
+ */
+
+ if (!sc.sw)
+ {
+ gcs.error("`goto case` not in `switch` statement");
+ return setError();
+ }
+
+ if (gcs.exp)
+ {
+ gcs.exp = gcs.exp.expressionSemantic(sc);
+ gcs.exp = gcs.exp.implicitCastTo(sc, sc.sw.condition.type);
+ gcs.exp = gcs.exp.optimize(WANTvalue);
+ if (gcs.exp.op == TOK.error)
+ return setError();
+ }
+
+ sc.sw.gotoCases.push(gcs);
+ result = gcs;
+ }
+
+ override void visit(ReturnStatement rs)
+ {
+ /* https://dlang.org/spec/statement.html#return-statement
+ */
+
+ //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars());
+
+ FuncDeclaration fd = sc.parent.isFuncDeclaration();
+ if (fd.fes)
+ fd = fd.fes.func; // fd is now function enclosing foreach
+
+ TypeFunction tf = cast(TypeFunction)fd.type;
+ assert(tf.ty == Tfunction);
+
+ if (rs.exp && rs.exp.op == TOK.variable && (cast(VarExp)rs.exp).var == fd.vresult)
+ {
+ // return vresult;
+ if (sc.fes)
+ {
+ assert(rs.caseDim == 0);
+ sc.fes.cases.push(rs);
+ result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.dim + 1));
+ return;
+ }
+ if (fd.returnLabel)
+ {
+ auto gs = new GotoStatement(rs.loc, Id.returnLabel);
+ gs.label = fd.returnLabel;
+ result = gs;
+ return;
+ }
+
+ if (!fd.returns)
+ fd.returns = new ReturnStatements();
+ fd.returns.push(rs);
+ result = rs;
+ return;
+ }
+
+ Type tret = tf.next;
+ Type tbret = tret ? tret.toBasetype() : null;
+
+ bool inferRef = (tf.isref && (fd.storage_class & STC.auto_));
+ Expression e0 = null;
+
+ bool errors = false;
+ if (sc.flags & SCOPE.contract)
+ {
+ rs.error("`return` statements cannot be in contracts");
+ errors = true;
+ }
+ if (sc.os && sc.os.tok != TOK.onScopeFailure)
+ {
+ rs.error("`return` statements cannot be in `%s` bodies", Token.toChars(sc.os.tok));
+ errors = true;
+ }
+ if (sc.tf)
+ {
+ rs.error("`return` statements cannot be in `finally` bodies");
+ errors = true;
+ }
+
+ if (fd.isCtorDeclaration())
+ {
+ if (rs.exp)
+ {
+ rs.error("cannot return expression from constructor");
+ errors = true;
+ }
+
+ // Constructors implicitly do:
+ // return this;
+ rs.exp = new ThisExp(Loc.initial);
+ rs.exp.type = tret;
+ }
+ else if (rs.exp)
+ {
+ fd.hasReturnExp |= (fd.hasReturnExp & 1 ? 16 : 1);
+
+ FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration();
+ if (tret)
+ rs.exp = inferType(rs.exp, tret);
+ else if (fld && fld.treq)
+ rs.exp = inferType(rs.exp, fld.treq.nextOf().nextOf());
+
+ rs.exp = rs.exp.expressionSemantic(sc);
+ // If we're returning by ref, allow the expression to be `shared`
+ const returnSharedRef = (tf.isref && (fd.inferRetType || tret.isShared()));
+ rs.exp.checkSharedAccess(sc, returnSharedRef);
+
+ // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+ if (rs.exp.op == TOK.type)
+ rs.exp = resolveAliasThis(sc, rs.exp);
+
+ rs.exp = resolveProperties(sc, rs.exp);
+ if (rs.exp.checkType())
+ rs.exp = ErrorExp.get();
+ if (auto f = isFuncAddress(rs.exp))
+ {
+ if (fd.inferRetType && f.checkForwardRef(rs.exp.loc))
+ rs.exp = ErrorExp.get();
+ }
+ if (checkNonAssignmentArrayOp(rs.exp))
+ rs.exp = ErrorExp.get();
+
+ // Extract side-effect part
+ rs.exp = Expression.extractLast(rs.exp, e0);
+ if (rs.exp.op == TOK.call)
+ rs.exp = valueNoDtor(rs.exp);
+
+ if (e0)
+ e0 = e0.optimize(WANTvalue);
+
+ /* Void-return function can have void typed expression
+ * on return statement.
+ */
+ if (tbret && tbret.ty == Tvoid || rs.exp.type.ty == Tvoid)
+ {
+ if (rs.exp.type.ty != Tvoid)
+ {
+ rs.error("cannot return non-void from `void` function");
+ errors = true;
+ rs.exp = new CastExp(rs.loc, rs.exp, Type.tvoid);
+ rs.exp = rs.exp.expressionSemantic(sc);
+ }
+
+ /* Replace:
+ * return exp;
+ * with:
+ * exp; return;
+ */
+ e0 = Expression.combine(e0, rs.exp);
+ rs.exp = null;
+ }
+ if (e0)
+ e0 = checkGC(sc, e0);
+ }
+
+ if (rs.exp)
+ {
+ if (fd.inferRetType) // infer return type
+ {
+ if (!tret)
+ {
+ tf.next = rs.exp.type;
+ }
+ else if (tret.ty != Terror && !rs.exp.type.equals(tret))
+ {
+ int m1 = rs.exp.type.implicitConvTo(tret);
+ int m2 = tret.implicitConvTo(rs.exp.type);
+ //printf("exp.type = %s m2<-->m1 tret %s\n", exp.type.toChars(), tret.toChars());
+ //printf("m1 = %d, m2 = %d\n", m1, m2);
+
+ if (m1 && m2)
+ {
+ }
+ else if (!m1 && m2)
+ tf.next = rs.exp.type;
+ else if (m1 && !m2)
+ {
+ }
+ else if (rs.exp.op != TOK.error)
+ {
+ rs.error("Expected return type of `%s`, not `%s`:",
+ tret.toChars(),
+ rs.exp.type.toChars());
+ errorSupplemental((fd.returns) ? (*fd.returns)[0].loc : fd.loc,
+ "Return type of `%s` inferred here.",
+ tret.toChars());
+
+ errors = true;
+ tf.next = Type.terror;
+ }
+ }
+
+ tret = tf.next;
+ tbret = tret.toBasetype();
+ }
+
+ if (inferRef) // deduce 'auto ref'
+ {
+ /* Determine "refness" of function return:
+ * if it's an lvalue, return by ref, else return by value
+ * https://dlang.org/spec/function.html#auto-ref-functions
+ */
+
+ void turnOffRef(scope void delegate() supplemental)
+ {
+ tf.isref = false; // return by value
+ tf.isreturn = false; // ignore 'return' attribute, whether explicit or inferred
+ fd.storage_class &= ~STC.return_;
+
+ // If we previously assumed the function could be ref when
+ // checking for `shared`, make sure we were right
+ if (global.params.noSharedAccess && rs.exp.type.isShared())
+ {
+ fd.error("function returns `shared` but cannot be inferred `ref`");
+ supplemental();
+ }
+ }
+
+ if (rs.exp.isLvalue())
+ {
+ /* May return by ref
+ */
+ if (checkReturnEscapeRef(sc, rs.exp, true))
+ turnOffRef(() { checkReturnEscapeRef(sc, rs.exp, false); });
+ else if (!rs.exp.type.constConv(tf.next))
+ turnOffRef(
+ () => rs.loc.errorSupplemental("cannot implicitly convert `%s` of type `%s` to `%s`",
+ rs.exp.toChars(), rs.exp.type.toChars(), tf.next.toChars())
+ );
+ }
+ else
+ turnOffRef(
+ () => rs.loc.errorSupplemental("return value `%s` is not an lvalue", rs.exp.toChars())
+ );
+
+ /* The "refness" is determined by all of return statements.
+ * This means:
+ * return 3; return x; // ok, x can be a value
+ * return x; return 3; // ok, x can be a value
+ */
+ }
+ }
+ else
+ {
+ // infer return type
+ if (fd.inferRetType)
+ {
+ if (tf.next && tf.next.ty != Tvoid)
+ {
+ if (tf.next.ty != Terror)
+ {
+ rs.error("mismatched function return type inference of `void` and `%s`", tf.next.toChars());
+ }
+ errors = true;
+ tf.next = Type.terror;
+ }
+ else
+ tf.next = Type.tvoid;
+
+ tret = tf.next;
+ tbret = tret.toBasetype();
+ }
+
+ if (inferRef) // deduce 'auto ref'
+ tf.isref = false;
+
+ if (tbret.ty != Tvoid) // if non-void return
+ {
+ if (tbret.ty != Terror)
+ rs.error("`return` expression expected");
+ errors = true;
+ }
+ else if (fd.isMain())
+ {
+ // main() returns 0, even if it returns void
+ rs.exp = IntegerExp.literal!0;
+ }
+ }
+
+ // If any branches have called a ctor, but this branch hasn't, it's an error
+ if (sc.ctorflow.callSuper & CSX.any_ctor && !(sc.ctorflow.callSuper & (CSX.this_ctor | CSX.super_ctor)))
+ {
+ rs.error("`return` without calling constructor");
+ errors = true;
+ }
+
+ if (sc.ctorflow.fieldinit.length) // if aggregate fields are being constructed
+ {
+ auto ad = fd.isMemberLocal();
+ assert(ad);
+ foreach (i, v; ad.fields)
+ {
+ bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
+ if (mustInit && !(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
+ {
+ rs.error("an earlier `return` statement skips field `%s` initialization", v.toChars());
+ errors = true;
+ }
+ }
+ }
+ sc.ctorflow.orCSX(CSX.return_);
+
+ if (errors)
+ return setError();
+
+ if (sc.fes)
+ {
+ if (!rs.exp)
+ {
+ // Send out "case receiver" statement to the foreach.
+ // return exp;
+ Statement s = new ReturnStatement(Loc.initial, rs.exp);
+ sc.fes.cases.push(s);
+
+ // Immediately rewrite "this" return statement as:
+ // return cases.dim+1;
+ rs.exp = new IntegerExp(sc.fes.cases.dim + 1);
+ if (e0)
+ {
+ result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs);
+ return;
+ }
+ result = rs;
+ return;
+ }
+ else
+ {
+ fd.buildResultVar(null, rs.exp.type);
+ bool r = fd.vresult.checkNestedReference(sc, Loc.initial);
+ assert(!r); // vresult should be always accessible
+
+ // Send out "case receiver" statement to the foreach.
+ // return vresult;
+ Statement s = new ReturnStatement(Loc.initial, new VarExp(Loc.initial, fd.vresult));
+ sc.fes.cases.push(s);
+
+ // Save receiver index for the later rewriting from:
+ // return exp;
+ // to:
+ // vresult = exp; retrun caseDim;
+ rs.caseDim = sc.fes.cases.dim + 1;
+ }
+ }
+ if (rs.exp)
+ {
+ if (!fd.returns)
+ fd.returns = new ReturnStatements();
+ fd.returns.push(rs);
+ }
+ if (e0)
+ {
+ if (e0.op == TOK.declaration || e0.op == TOK.comma)
+ {
+ rs.exp = Expression.combine(e0, rs.exp);
+ }
+ else
+ {
+ result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs);
+ return;
+ }
+ }
+ result = rs;
+ }
+
+ override void visit(BreakStatement bs)
+ {
+ /* https://dlang.org/spec/statement.html#break-statement
+ */
+
+ //printf("BreakStatement::semantic()\n");
+
+ // If:
+ // break Identifier;
+ if (bs.ident)
+ {
+ bs.ident = fixupLabelName(sc, bs.ident);
+
+ FuncDeclaration thisfunc = sc.func;
+
+ for (Scope* scx = sc; scx; scx = scx.enclosing)
+ {
+ if (scx.func != thisfunc) // if in enclosing function
+ {
+ if (sc.fes) // if this is the body of a foreach
+ {
+ /* Post this statement to the fes, and replace
+ * it with a return value that caller will put into
+ * a switch. Caller will figure out where the break
+ * label actually is.
+ * Case numbers start with 2, not 0, as 0 is continue
+ * and 1 is break.
+ */
+ sc.fes.cases.push(bs);
+ result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.dim + 1));
+ return;
+ }
+ break; // can't break to it
+ }
+
+ LabelStatement ls = scx.slabel;
+ if (ls && ls.ident == bs.ident)
+ {
+ Statement s = ls.statement;
+ if (!s || !s.hasBreak())
+ bs.error("label `%s` has no `break`", bs.ident.toChars());
+ else if (ls.tf != sc.tf)
+ bs.error("cannot break out of `finally` block");
+ else
+ {
+ ls.breaks = true;
+ result = bs;
+ return;
+ }
+ return setError();
+ }
+ }
+ bs.error("enclosing label `%s` for `break` not found", bs.ident.toChars());
+ return setError();
+ }
+ else if (!sc.sbreak)
+ {
+ if (sc.os && sc.os.tok != TOK.onScopeFailure)
+ {
+ bs.error("`break` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok));
+ }
+ else if (sc.fes)
+ {
+ // Replace break; with return 1;
+ result = new ReturnStatement(Loc.initial, IntegerExp.literal!1);
+ return;
+ }
+ else
+ bs.error("`break` is not inside a loop or `switch`");
+ return setError();
+ }
+ else if (sc.sbreak.isForwardingStatement())
+ {
+ bs.error("must use labeled `break` within `static foreach`");
+ }
+ result = bs;
+ }
+
+ override void visit(ContinueStatement cs)
+ {
+ /* https://dlang.org/spec/statement.html#continue-statement
+ */
+
+ //printf("ContinueStatement::semantic() %p\n", cs);
+ if (cs.ident)
+ {
+ cs.ident = fixupLabelName(sc, cs.ident);
+
+ Scope* scx;
+ FuncDeclaration thisfunc = sc.func;
+
+ for (scx = sc; scx; scx = scx.enclosing)
+ {
+ LabelStatement ls;
+ if (scx.func != thisfunc) // if in enclosing function
+ {
+ if (sc.fes) // if this is the body of a foreach
+ {
+ for (; scx; scx = scx.enclosing)
+ {
+ ls = scx.slabel;
+ if (ls && ls.ident == cs.ident && ls.statement == sc.fes)
+ {
+ // Replace continue ident; with return 0;
+ result = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
+ return;
+ }
+ }
+
+ /* Post this statement to the fes, and replace
+ * it with a return value that caller will put into
+ * a switch. Caller will figure out where the break
+ * label actually is.
+ * Case numbers start with 2, not 0, as 0 is continue
+ * and 1 is break.
+ */
+ sc.fes.cases.push(cs);
+ result = new ReturnStatement(Loc.initial, new IntegerExp(sc.fes.cases.dim + 1));
+ return;
+ }
+ break; // can't continue to it
+ }
+
+ ls = scx.slabel;
+ if (ls && ls.ident == cs.ident)
+ {
+ Statement s = ls.statement;
+ if (!s || !s.hasContinue())
+ cs.error("label `%s` has no `continue`", cs.ident.toChars());
+ else if (ls.tf != sc.tf)
+ cs.error("cannot continue out of `finally` block");
+ else
+ {
+ result = cs;
+ return;
+ }
+ return setError();
+ }
+ }
+ cs.error("enclosing label `%s` for `continue` not found", cs.ident.toChars());
+ return setError();
+ }
+ else if (!sc.scontinue)
+ {
+ if (sc.os && sc.os.tok != TOK.onScopeFailure)
+ {
+ cs.error("`continue` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok));
+ }
+ else if (sc.fes)
+ {
+ // Replace continue; with return 0;
+ result = new ReturnStatement(Loc.initial, IntegerExp.literal!0);
+ return;
+ }
+ else
+ cs.error("`continue` is not inside a loop");
+ return setError();
+ }
+ else if (sc.scontinue.isForwardingStatement())
+ {
+ cs.error("must use labeled `continue` within `static foreach`");
+ }
+ result = cs;
+ }
+
+ override void visit(SynchronizedStatement ss)
+ {
+ /* https://dlang.org/spec/statement.html#synchronized-statement
+ */
+
+ if (ss.exp)
+ {
+ ss.exp = ss.exp.expressionSemantic(sc);
+ ss.exp = resolveProperties(sc, ss.exp);
+ ss.exp = ss.exp.optimize(WANTvalue);
+ ss.exp = checkGC(sc, ss.exp);
+ if (ss.exp.op == TOK.error)
+ {
+ if (ss._body)
+ ss._body = ss._body.statementSemantic(sc);
+ return setError();
+ }
+
+ ClassDeclaration cd = ss.exp.type.isClassHandle();
+ if (!cd)
+ {
+ ss.error("can only `synchronize` on class objects, not `%s`", ss.exp.type.toChars());
+ return setError();
+ }
+ else if (cd.isInterfaceDeclaration())
+ {
+ /* Cast the interface to an object, as the object has the monitor,
+ * not the interface.
+ */
+ if (!ClassDeclaration.object)
+ {
+ ss.error("missing or corrupt object.d");
+ fatal();
+ }
+
+ Type t = ClassDeclaration.object.type;
+ t = t.typeSemantic(Loc.initial, sc).toBasetype();
+ assert(t.ty == Tclass);
+
+ ss.exp = new CastExp(ss.loc, ss.exp, t);
+ ss.exp = ss.exp.expressionSemantic(sc);
+ }
+ version (all)
+ {
+ /* Rewrite as:
+ * auto tmp = exp;
+ * _d_monitorenter(tmp);
+ * try { body } finally { _d_monitorexit(tmp); }
+ */
+ auto tmp = copyToTemp(0, "__sync", ss.exp);
+ tmp.dsymbolSemantic(sc);
+
+ auto cs = new Statements();
+ cs.push(new ExpStatement(ss.loc, tmp));
+
+ auto args = new Parameters();
+ args.push(new Parameter(0, ClassDeclaration.object.type, null, null, null));
+
+ FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
+ Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp));
+ e.type = Type.tvoid; // do not run semantic on e
+
+ cs.push(new ExpStatement(ss.loc, e));
+ FuncDeclaration fdexit = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorexit);
+ e = new CallExp(ss.loc, fdexit, new VarExp(ss.loc, tmp));
+ e.type = Type.tvoid; // do not run semantic on e
+ Statement s = new ExpStatement(ss.loc, e);
+ s = new TryFinallyStatement(ss.loc, ss._body, s);
+ cs.push(s);
+
+ s = new CompoundStatement(ss.loc, cs);
+ result = s.statementSemantic(sc);
+ }
+ }
+ else
+ {
+ /* Generate our own critical section, then rewrite as:
+ * static shared void* __critsec;
+ * _d_criticalenter2(&__critsec);
+ * try { body } finally { _d_criticalexit(__critsec); }
+ */
+ auto id = Identifier.generateId("__critsec");
+ auto t = Type.tvoidptr;
+ auto tmp = new VarDeclaration(ss.loc, t, id, null);
+ tmp.storage_class |= STC.temp | STC.shared_ | STC.static_;
+ Expression tmpExp = new VarExp(ss.loc, tmp);
+
+ auto cs = new Statements();
+ cs.push(new ExpStatement(ss.loc, tmp));
+
+ /* This is just a dummy variable for "goto skips declaration" error.
+ * Backend optimizer could remove this unused variable.
+ */
+ auto v = new VarDeclaration(ss.loc, Type.tvoidptr, Identifier.generateId("__sync"), null);
+ v.dsymbolSemantic(sc);
+ cs.push(new ExpStatement(ss.loc, v));
+
+ auto enterArgs = new Parameters();
+ enterArgs.push(new Parameter(0, t.pointerTo(), null, null, null));
+
+ FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_);
+ Expression e = new AddrExp(ss.loc, tmpExp);
+ e = e.expressionSemantic(sc);
+ e = new CallExp(ss.loc, fdenter, e);
+ e.type = Type.tvoid; // do not run semantic on e
+ cs.push(new ExpStatement(ss.loc, e));
+
+ auto exitArgs = new Parameters();
+ exitArgs.push(new Parameter(0, t, null, null, null));
+
+ FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_);
+ e = new CallExp(ss.loc, fdexit, tmpExp);
+ e.type = Type.tvoid; // do not run semantic on e
+ Statement s = new ExpStatement(ss.loc, e);
+ s = new TryFinallyStatement(ss.loc, ss._body, s);
+ cs.push(s);
+
+ s = new CompoundStatement(ss.loc, cs);
+ result = s.statementSemantic(sc);
+ }
+ }
+
+ override void visit(WithStatement ws)
+ {
+ /* https://dlang.org/spec/statement.html#with-statement
+ */
+
+ ScopeDsymbol sym;
+ Initializer _init;
+
+ //printf("WithStatement::semantic()\n");
+ ws.exp = ws.exp.expressionSemantic(sc);
+ ws.exp = resolveProperties(sc, ws.exp);
+ ws.exp = ws.exp.optimize(WANTvalue);
+ ws.exp = checkGC(sc, ws.exp);
+ if (ws.exp.op == TOK.error)
+ return setError();
+ if (ws.exp.op == TOK.scope_)
+ {
+ sym = new WithScopeSymbol(ws);
+ sym.parent = sc.scopesym;
+ sym.endlinnum = ws.endloc.linnum;
+ }
+ else if (ws.exp.op == TOK.type)
+ {
+ Dsymbol s = (cast(TypeExp)ws.exp).type.toDsymbol(sc);
+ if (!s || !s.isScopeDsymbol())
+ {
+ ws.error("`with` type `%s` has no members", ws.exp.toChars());
+ return setError();
+ }
+ sym = new WithScopeSymbol(ws);
+ sym.parent = sc.scopesym;
+ sym.endlinnum = ws.endloc.linnum;
+ }
+ else
+ {
+ Type t = ws.exp.type.toBasetype();
+
+ Expression olde = ws.exp;
+ if (t.ty == Tpointer)
+ {
+ ws.exp = new PtrExp(ws.loc, ws.exp);
+ ws.exp = ws.exp.expressionSemantic(sc);
+ t = ws.exp.type.toBasetype();
+ }
+
+ assert(t);
+ t = t.toBasetype();
+ if (t.isClassHandle())
+ {
+ _init = new ExpInitializer(ws.loc, ws.exp);
+ ws.wthis = new VarDeclaration(ws.loc, ws.exp.type, Id.withSym, _init);
+ ws.wthis.storage_class |= STC.temp;
+ ws.wthis.dsymbolSemantic(sc);
+
+ sym = new WithScopeSymbol(ws);
+ sym.parent = sc.scopesym;
+ sym.endlinnum = ws.endloc.linnum;
+ }
+ else if (t.ty == Tstruct)
+ {
+ if (!ws.exp.isLvalue())
+ {
+ /* Re-write to
+ * {
+ * auto __withtmp = exp
+ * with(__withtmp)
+ * {
+ * ...
+ * }
+ * }
+ */
+ auto tmp = copyToTemp(0, "__withtmp", ws.exp);
+ tmp.dsymbolSemantic(sc);
+ auto es = new ExpStatement(ws.loc, tmp);
+ ws.exp = new VarExp(ws.loc, tmp);
+ Statement ss = new ScopeStatement(ws.loc, new CompoundStatement(ws.loc, es, ws), ws.endloc);
+ result = ss.statementSemantic(sc);
+ return;
+ }
+ Expression e = ws.exp.addressOf();
+ _init = new ExpInitializer(ws.loc, e);
+ ws.wthis = new VarDeclaration(ws.loc, e.type, Id.withSym, _init);
+ ws.wthis.storage_class |= STC.temp;
+ ws.wthis.dsymbolSemantic(sc);
+ sym = new WithScopeSymbol(ws);
+ // Need to set the scope to make use of resolveAliasThis
+ sym.setScope(sc);
+ sym.parent = sc.scopesym;
+ sym.endlinnum = ws.endloc.linnum;
+ }
+ else
+ {
+ ws.error("`with` expressions must be aggregate types or pointers to them, not `%s`", olde.type.toChars());
+ return setError();
+ }
+ }
+
+ if (ws._body)
+ {
+ sym._scope = sc;
+ sc = sc.push(sym);
+ sc.insert(sym);
+ ws._body = ws._body.statementSemantic(sc);
+ sc.pop();
+ if (ws._body && ws._body.isErrorStatement())
+ {
+ result = ws._body;
+ return;
+ }
+ }
+
+ result = ws;
+ }
+
+ // https://dlang.org/spec/statement.html#TryStatement
+ override void visit(TryCatchStatement tcs)
+ {
+ //printf("TryCatchStatement.semantic()\n");
+
+ if (!global.params.useExceptions)
+ {
+ tcs.error("Cannot use try-catch statements with -betterC");
+ return setError();
+ }
+
+ if (!ClassDeclaration.throwable)
+ {
+ tcs.error("Cannot use try-catch statements because `object.Throwable` was not declared");
+ return setError();
+ }
+
+ uint flags;
+ enum FLAGcpp = 1;
+ enum FLAGd = 2;
+
+ tcs.tryBody = sc.tryBody; // chain on the in-flight tryBody
+ tcs._body = tcs._body.semanticScope(sc, null, null, tcs);
+ assert(tcs._body);
+
+ /* Even if body is empty, still do semantic analysis on catches
+ */
+ bool catchErrors = false;
+ foreach (i, c; *tcs.catches)
+ {
+ c.catchSemantic(sc);
+ if (c.errors)
+ {
+ catchErrors = true;
+ continue;
+ }
+ auto cd = c.type.toBasetype().isClassHandle();
+ flags |= cd.isCPPclass() ? FLAGcpp : FLAGd;
+
+ // Determine if current catch 'hides' any previous catches
+ foreach (j; 0 .. i)
+ {
+ Catch cj = (*tcs.catches)[j];
+ const si = c.loc.toChars();
+ const sj = cj.loc.toChars();
+ if (c.type.toBasetype().implicitConvTo(cj.type.toBasetype()))
+ {
+ tcs.error("`catch` at %s hides `catch` at %s", sj, si);
+ catchErrors = true;
+ }
+ }
+ }
+
+ if (sc.func)
+ {
+ sc.func.flags |= FUNCFLAG.hasCatches;
+ if (flags == (FLAGcpp | FLAGd))
+ {
+ tcs.error("cannot mix catching D and C++ exceptions in the same try-catch");
+ catchErrors = true;
+ }
+ }
+
+ if (catchErrors)
+ return setError();
+
+ if (tcs._body.isErrorStatement())
+ {
+ result = tcs._body;
+ return;
+ }
+
+ /* If the try body never throws, we can eliminate any catches
+ * of recoverable exceptions.
+ */
+ if (!(tcs._body.blockExit(sc.func, false) & BE.throw_) && ClassDeclaration.exception)
+ {
+ foreach_reverse (i; 0 .. tcs.catches.dim)
+ {
+ Catch c = (*tcs.catches)[i];
+
+ /* If catch exception type is derived from Exception
+ */
+ if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) &&
+ (!c.handler || !c.handler.comeFrom()))
+ {
+ // Remove c from the array of catches
+ tcs.catches.remove(i);
+ }
+ }
+ }
+
+ if (tcs.catches.dim == 0)
+ {
+ result = tcs._body.hasCode() ? tcs._body : null;
+ return;
+ }
+
+ result = tcs;
+ }
+
+ override void visit(TryFinallyStatement tfs)
+ {
+ //printf("TryFinallyStatement::semantic()\n");
+ tfs.tryBody = sc.tryBody; // chain on in-flight tryBody
+ tfs._body = tfs._body.semanticScope(sc, null, null, tfs);
+
+ sc = sc.push();
+ sc.tf = tfs;
+ sc.sbreak = null;
+ sc.scontinue = null; // no break or continue out of finally block
+ tfs.finalbody = tfs.finalbody.semanticNoScope(sc);
+ sc.pop();
+
+ if (!tfs._body)
+ {
+ result = tfs.finalbody;
+ return;
+ }
+ if (!tfs.finalbody)
+ {
+ result = tfs._body;
+ return;
+ }
+
+ auto blockexit = tfs._body.blockExit(sc.func, false);
+
+ // if not worrying about exceptions
+ if (!(global.params.useExceptions && ClassDeclaration.throwable))
+ blockexit &= ~BE.throw_; // don't worry about paths that otherwise may throw
+
+ // Don't care about paths that halt, either
+ if ((blockexit & ~BE.halt) == BE.fallthru)
+ {
+ result = new CompoundStatement(tfs.loc, tfs._body, tfs.finalbody);
+ return;
+ }
+ tfs.bodyFallsThru = (blockexit & BE.fallthru) != 0;
+ result = tfs;
+ }
+
+ override void visit(ScopeGuardStatement oss)
+ {
+ /* https://dlang.org/spec/statement.html#scope-guard-statement
+ */
+
+ if (oss.tok != TOK.onScopeExit)
+ {
+ // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement,
+ // so the generated catch block cannot be placed in finally block.
+ // See also Catch::semantic.
+ if (sc.os && sc.os.tok != TOK.onScopeFailure)
+ {
+ // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
+ oss.error("cannot put `%s` statement inside `%s`", Token.toChars(oss.tok), Token.toChars(sc.os.tok));
+ return setError();
+ }
+ if (sc.tf)
+ {
+ oss.error("cannot put `%s` statement inside `finally` block", Token.toChars(oss.tok));
+ return setError();
+ }
+ }
+
+ sc = sc.push();
+ sc.tf = null;
+ sc.os = oss;
+ if (oss.tok != TOK.onScopeFailure)
+ {
+ // Jump out from scope(failure) block is allowed.
+ sc.sbreak = null;
+ sc.scontinue = null;
+ }
+ oss.statement = oss.statement.semanticNoScope(sc);
+ sc.pop();
+
+ if (!oss.statement || oss.statement.isErrorStatement())
+ {
+ result = oss.statement;
+ return;
+ }
+ result = oss;
+ }
+
+ override void visit(ThrowStatement ts)
+ {
+ /* https://dlang.org/spec/statement.html#throw-statement
+ */
+
+ //printf("ThrowStatement::semantic()\n");
+
+ if (!global.params.useExceptions)
+ {
+ ts.error("Cannot use `throw` statements with -betterC");
+ return setError();
+ }
+
+ if (!ClassDeclaration.throwable)
+ {
+ ts.error("Cannot use `throw` statements because `object.Throwable` was not declared");
+ return setError();
+ }
+
+ FuncDeclaration fd = sc.parent.isFuncDeclaration();
+ fd.hasReturnExp |= 2;
+
+ if (ts.exp.op == TOK.new_)
+ {
+ NewExp ne = cast(NewExp)ts.exp;
+ ne.thrownew = true;
+ }
+
+ ts.exp = ts.exp.expressionSemantic(sc);
+ ts.exp = resolveProperties(sc, ts.exp);
+ ts.exp = checkGC(sc, ts.exp);
+ if (ts.exp.op == TOK.error)
+ return setError();
+
+ checkThrowEscape(sc, ts.exp, false);
+
+ ClassDeclaration cd = ts.exp.type.toBasetype().isClassHandle();
+ if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null)))
+ {
+ ts.error("can only throw class objects derived from `Throwable`, not type `%s`", ts.exp.type.toChars());
+ return setError();
+ }
+
+ result = ts;
+ }
+
+ override void visit(DebugStatement ds)
+ {
+ if (ds.statement)
+ {
+ sc = sc.push();
+ sc.flags |= SCOPE.debug_;
+ ds.statement = ds.statement.statementSemantic(sc);
+ sc.pop();
+ }
+ result = ds.statement;
+ }
+
+ override void visit(GotoStatement gs)
+ {
+ /* https://dlang.org/spec/statement.html#goto-statement
+ */
+
+ //printf("GotoStatement::semantic()\n");
+ FuncDeclaration fd = sc.func;
+
+ gs.ident = fixupLabelName(sc, gs.ident);
+ gs.label = fd.searchLabel(gs.ident, gs.loc);
+ gs.tryBody = sc.tryBody;
+ gs.tf = sc.tf;
+ gs.os = sc.os;
+ gs.lastVar = sc.lastVar;
+
+ if (!gs.label.statement && sc.fes)
+ {
+ /* Either the goto label is forward referenced or it
+ * is in the function that the enclosing foreach is in.
+ * Can't know yet, so wrap the goto in a scope statement
+ * so we can patch it later, and add it to a 'look at this later'
+ * list.
+ */
+ gs.label.deleted = true;
+ auto ss = new ScopeStatement(gs.loc, gs, gs.loc);
+ sc.fes.gotos.push(ss); // 'look at this later' list
+ result = ss;
+ return;
+ }
+
+ // Add to fwdref list to check later
+ if (!gs.label.statement)
+ {
+ if (!fd.gotos)
+ fd.gotos = new GotoStatements();
+ fd.gotos.push(gs);
+ }
+ else if (gs.checkLabel())
+ return setError();
+
+ result = gs;
+ }
+
+ override void visit(LabelStatement ls)
+ {
+ //printf("LabelStatement::semantic()\n");
+ FuncDeclaration fd = sc.parent.isFuncDeclaration();
+
+ ls.ident = fixupLabelName(sc, ls.ident);
+ ls.tryBody = sc.tryBody;
+ ls.tf = sc.tf;
+ ls.os = sc.os;
+ ls.lastVar = sc.lastVar;
+
+ LabelDsymbol ls2 = fd.searchLabel(ls.ident, ls.loc);
+ if (ls2.statement)
+ {
+ ls.error("label `%s` already defined", ls2.toChars());
+ return setError();
+ }
+ else
+ ls2.statement = ls;
+
+ sc = sc.push();
+ sc.scopesym = sc.enclosing.scopesym;
+
+ sc.ctorflow.orCSX(CSX.label);
+
+ sc.slabel = ls;
+ if (ls.statement)
+ ls.statement = ls.statement.statementSemantic(sc);
+ sc.pop();
+
+ result = ls;
+ }
+
+ override void visit(AsmStatement s)
+ {
+ /* https://dlang.org/spec/statement.html#asm
+ */
+
+ //printf("AsmStatement()::semantic()\n");
+ result = asmSemantic(s, sc);
+ }
+
+ override void visit(CompoundAsmStatement cas)
+ {
+ //printf("CompoundAsmStatement()::semantic()\n");
+ // Apply postfix attributes of the asm block to each statement.
+ sc = sc.push();
+ sc.stc |= cas.stc;
+
+ /* Go through the statements twice, first to declare any labels,
+ * second for anything else.
+ */
+
+ foreach (ref s; *cas.statements)
+ {
+ if (s)
+ {
+ if (auto ls = s.isLabelStatement())
+ {
+ sc.func.searchLabel(ls.ident, ls.loc);
+ }
+ }
+ }
+
+ foreach (ref s; *cas.statements)
+ {
+ s = s ? s.statementSemantic(sc) : null;
+ }
+
+ assert(sc.func);
+ // use setImpure/setGC when the deprecation cycle is over
+ PURE purity;
+ if (!(cas.stc & STC.pure_) && (purity = sc.func.isPureBypassingInference()) != PURE.impure && purity != PURE.fwdref)
+ cas.deprecation("`asm` statement is assumed to be impure - mark it with `pure` if it is not");
+ if (!(cas.stc & STC.nogc) && sc.func.isNogcBypassingInference())
+ cas.deprecation("`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not");
+ if (!(cas.stc & (STC.trusted | STC.safe)) && sc.func.setUnsafe())
+ cas.error("`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not");
+
+ sc.pop();
+ result = cas;
+ }
+
+ override void visit(ImportStatement imps)
+ {
+ /* https://dlang.org/spec/module.html#ImportDeclaration
+ */
+
+ foreach (i; 0 .. imps.imports.dim)
+ {
+ Import s = (*imps.imports)[i].isImport();
+ assert(!s.aliasdecls.dim);
+ foreach (j, name; s.names)
+ {
+ Identifier _alias = s.aliases[j];
+ if (!_alias)
+ _alias = name;
+
+ auto tname = new TypeIdentifier(s.loc, name);
+ auto ad = new AliasDeclaration(s.loc, _alias, tname);
+ ad._import = s;
+ s.aliasdecls.push(ad);
+ }
+
+ s.dsymbolSemantic(sc);
+
+ // https://issues.dlang.org/show_bug.cgi?id=19942
+ // If the module that's being imported doesn't exist, don't add it to the symbol table
+ // for the current scope.
+ if (s.mod !is null)
+ {
+ Module.addDeferredSemantic2(s); // https://issues.dlang.org/show_bug.cgi?id=14666
+ sc.insert(s);
+
+ foreach (aliasdecl; s.aliasdecls)
+ {
+ sc.insert(aliasdecl);
+ }
+ }
+ }
+ result = imps;
+ }
+}
+
+void catchSemantic(Catch c, Scope* sc)
+{
+ //printf("Catch::semantic(%s)\n", ident.toChars());
+
+ if (sc.os && sc.os.tok != TOK.onScopeFailure)
+ {
+ // If enclosing is scope(success) or scope(exit), this will be placed in finally block.
+ error(c.loc, "cannot put `catch` statement inside `%s`", Token.toChars(sc.os.tok));
+ c.errors = true;
+ }
+ if (sc.tf)
+ {
+ /* This is because the _d_local_unwind() gets the stack munged
+ * up on this. The workaround is to place any try-catches into
+ * a separate function, and call that.
+ * To fix, have the compiler automatically convert the finally
+ * body into a nested function.
+ */
+ error(c.loc, "cannot put `catch` statement inside `finally` block");
+ c.errors = true;
+ }
+
+ auto sym = new ScopeDsymbol();
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+
+ if (!c.type)
+ {
+ error(c.loc, "`catch` statement without an exception specification is deprecated");
+ errorSupplemental(c.loc, "use `catch(Throwable)` for old behavior");
+ c.errors = true;
+
+ // reference .object.Throwable
+ c.type = getThrowable();
+ }
+ c.type = c.type.typeSemantic(c.loc, sc);
+ if (c.type == Type.terror)
+ {
+ c.errors = true;
+ sc.pop();
+ return;
+ }
+
+ StorageClass stc;
+ auto cd = c.type.toBasetype().isClassHandle();
+ if (!cd)
+ {
+ error(c.loc, "can only catch class objects, not `%s`", c.type.toChars());
+ c.errors = true;
+ }
+ else if (cd.isCPPclass())
+ {
+ if (!target.cpp.exceptions)
+ {
+ error(c.loc, "catching C++ class objects not supported for this target");
+ c.errors = true;
+ }
+ if (sc.func && !sc.intypeof && !c.internalCatch && sc.func.setUnsafe())
+ {
+ error(c.loc, "cannot catch C++ class objects in `@safe` code");
+ c.errors = true;
+ }
+ }
+ else if (cd != ClassDeclaration.throwable && !ClassDeclaration.throwable.isBaseOf(cd, null))
+ {
+ error(c.loc, "can only catch class objects derived from `Throwable`, not `%s`", c.type.toChars());
+ c.errors = true;
+ }
+ else if (sc.func && !sc.intypeof && !c.internalCatch && ClassDeclaration.exception &&
+ cd != ClassDeclaration.exception && !ClassDeclaration.exception.isBaseOf(cd, null) &&
+ sc.func.setUnsafe())
+ {
+ error(c.loc, "can only catch class objects derived from `Exception` in `@safe` code, not `%s`", c.type.toChars());
+ c.errors = true;
+ }
+ else if (global.params.ehnogc)
+ {
+ stc |= STC.scope_;
+ }
+
+ // DIP1008 requires destruction of the Throwable, even if the user didn't specify an identifier
+ auto ident = c.ident;
+ if (!ident && global.params.ehnogc)
+ ident = Identifier.generateAnonymousId("var");
+
+ if (ident)
+ {
+ c.var = new VarDeclaration(c.loc, c.type, ident, null, stc);
+ c.var.iscatchvar = true;
+ c.var.dsymbolSemantic(sc);
+ sc.insert(c.var);
+
+ if (global.params.ehnogc && stc & STC.scope_)
+ {
+ /* Add a destructor for c.var
+ * try { handler } finally { if (!__ctfe) _d_delThrowable(var); }
+ */
+ assert(!c.var.edtor); // ensure we didn't create one in callScopeDtor()
+
+ Loc loc = c.loc;
+ Expression e = new VarExp(loc, c.var);
+ e = new CallExp(loc, new IdentifierExp(loc, Id._d_delThrowable), e);
+
+ Expression ec = new IdentifierExp(loc, Id.ctfe);
+ ec = new NotExp(loc, ec);
+ Statement s = new IfStatement(loc, null, ec, new ExpStatement(loc, e), null, loc);
+ c.handler = new TryFinallyStatement(loc, c.handler, s);
+ }
+
+ }
+ c.handler = c.handler.statementSemantic(sc);
+ if (c.handler && c.handler.isErrorStatement())
+ c.errors = true;
+
+ sc.pop();
+}
+
+Statement semanticNoScope(Statement s, Scope* sc)
+{
+ //printf("Statement::semanticNoScope() %s\n", toChars());
+ if (!s.isCompoundStatement() && !s.isScopeStatement())
+ {
+ s = new CompoundStatement(s.loc, s); // so scopeCode() gets called
+ }
+ s = s.statementSemantic(sc);
+ return s;
+}
+
+// Same as semanticNoScope(), but do create a new scope
+private Statement semanticScope(Statement s, Scope* sc, Statement sbreak, Statement scontinue, Statement tryBody)
+{
+ auto sym = new ScopeDsymbol();
+ sym.parent = sc.scopesym;
+ Scope* scd = sc.push(sym);
+ if (sbreak)
+ scd.sbreak = sbreak;
+ if (scontinue)
+ scd.scontinue = scontinue;
+ if (tryBody)
+ scd.tryBody = tryBody;
+ s = s.semanticNoScope(scd);
+ scd.pop();
+ return s;
+}
+
+
+/****************************************
+ * If `statement` has code that needs to run in a finally clause
+ * at the end of the current scope, return that code in the form of
+ * a Statement.
+ * Params:
+ * statement = the statement
+ * sc = context
+ * sentry = set to code executed upon entry to the scope
+ * sexception = set to code executed upon exit from the scope via exception
+ * sfinally = set to code executed in finally block
+ * Returns:
+ * code to be run in the finally clause
+ */
+Statement scopeCode(Statement statement, Scope* sc, out Statement sentry, out Statement sexception, out Statement sfinally)
+{
+ if (auto es = statement.isExpStatement())
+ {
+ if (es.exp && es.exp.op == TOK.declaration)
+ {
+ auto de = cast(DeclarationExp)es.exp;
+ auto v = de.declaration.isVarDeclaration();
+ if (v && !v.isDataseg())
+ {
+ if (v.needsScopeDtor())
+ {
+ sfinally = new DtorExpStatement(es.loc, v.edtor, v);
+ v.storage_class |= STC.nodtor; // don't add in dtor again
+ }
+ }
+ }
+ return es;
+
+ }
+ else if (auto sgs = statement.isScopeGuardStatement())
+ {
+ Statement s = new PeelStatement(sgs.statement);
+
+ switch (sgs.tok)
+ {
+ case TOK.onScopeExit:
+ sfinally = s;
+ break;
+
+ case TOK.onScopeFailure:
+ sexception = s;
+ break;
+
+ case TOK.onScopeSuccess:
+ {
+ /* Create:
+ * sentry: bool x = false;
+ * sexception: x = true;
+ * sfinally: if (!x) statement;
+ */
+ auto v = copyToTemp(0, "__os", IntegerExp.createBool(false));
+ v.dsymbolSemantic(sc);
+ sentry = new ExpStatement(statement.loc, v);
+
+ Expression e = IntegerExp.createBool(true);
+ e = new AssignExp(Loc.initial, new VarExp(Loc.initial, v), e);
+ sexception = new ExpStatement(Loc.initial, e);
+
+ e = new VarExp(Loc.initial, v);
+ e = new NotExp(Loc.initial, e);
+ sfinally = new IfStatement(Loc.initial, null, e, s, null, Loc.initial);
+
+ break;
+ }
+ default:
+ assert(0);
+ }
+ return null;
+ }
+ else if (auto ls = statement.isLabelStatement())
+ {
+ if (ls.statement)
+ ls.statement = ls.statement.scopeCode(sc, sentry, sexception, sfinally);
+ return ls;
+ }
+
+ return statement;
+}
+
+
+/*******************
+ * Determines additional argument types for makeTupleForeach.
+ */
+static template TupleForeachArgs(bool isStatic, bool isDecl)
+{
+ alias Seq(T...)=T;
+ static if(isStatic) alias T = Seq!(bool);
+ else alias T = Seq!();
+ static if(!isDecl) alias TupleForeachArgs = T;
+ else alias TupleForeachArgs = Seq!(Dsymbols*,T);
+}
+
+/*******************
+ * Determines the return type of makeTupleForeach.
+ */
+static template TupleForeachRet(bool isStatic, bool isDecl)
+{
+ alias Seq(T...)=T;
+ static if(!isDecl) alias TupleForeachRet = Statement;
+ else alias TupleForeachRet = Dsymbols*;
+}
+
+
+/*******************
+ * See StatementSemanticVisitor.makeTupleForeach. This is a simple
+ * wrapper that returns the generated statements/declarations.
+ */
+TupleForeachRet!(isStatic, isDecl) makeTupleForeach(bool isStatic, bool isDecl)(Scope* sc, ForeachStatement fs, TupleForeachArgs!(isStatic, isDecl) args)
+{
+ scope v = new StatementSemanticVisitor(sc);
+ static if(!isDecl)
+ {
+ v.makeTupleForeach!(isStatic, isDecl)(fs, args);
+ return v.result;
+ }
+ else
+ {
+ return v.makeTupleForeach!(isStatic, isDecl)(fs, args);
+ }
+}
+
+/*********************************
+ * Flatten out the scope by presenting `statement`
+ * as an array of statements.
+ * Params:
+ * statement = the statement to flatten
+ * sc = context
+ * Returns:
+ * The array of `Statements`, or `null` if no flattening necessary
+ */
+private Statements* flatten(Statement statement, Scope* sc)
+{
+ static auto errorStatements()
+ {
+ auto a = new Statements();
+ a.push(new ErrorStatement());
+ return a;
+ }
+
+
+ /*compound and expression statements have classes that inherit from them with the same
+ *flattening behavior, so the isXXX methods won't work
+ */
+ switch(statement.stmt)
+ {
+ case STMT.Compound:
+ case STMT.CompoundDeclaration:
+ return (cast(CompoundStatement)statement).statements;
+
+ case STMT.Exp:
+ case STMT.DtorExp:
+ auto es = cast(ExpStatement)statement;
+ /* https://issues.dlang.org/show_bug.cgi?id=14243
+ * expand template mixin in statement scope
+ * to handle variable destructors.
+ */
+ if (!es.exp || es.exp.op != TOK.declaration)
+ return null;
+
+ Dsymbol d = (cast(DeclarationExp)es.exp).declaration;
+ auto tm = d.isTemplateMixin();
+ if (!tm)
+ return null;
+
+ Expression e = es.exp.expressionSemantic(sc);
+ if (e.op == TOK.error || tm.errors)
+ return errorStatements();
+ assert(tm.members);
+
+ Statement s = toStatement(tm);
+ version (none)
+ {
+ OutBuffer buf;
+ buf.doindent = 1;
+ HdrGenState hgs;
+ hgs.hdrgen = true;
+ toCBuffer(s, &buf, &hgs);
+ printf("tm ==> s = %s\n", buf.peekChars());
+ }
+ auto a = new Statements();
+ a.push(s);
+ return a;
+
+ case STMT.Forwarding:
+ /***********************
+ * ForwardingStatements are distributed over the flattened
+ * sequence of statements. This prevents flattening to be
+ * "blocked" by a ForwardingStatement and is necessary, for
+ * example, to support generating scope guards with `static
+ * foreach`:
+ *
+ * static foreach(i; 0 .. 10) scope(exit) writeln(i);
+ * writeln("this is printed first");
+ * // then, it prints 10, 9, 8, 7, ...
+ */
+ auto fs = statement.isForwardingStatement();
+ if (!fs.statement)
+ {
+ return null;
+ }
+ sc = sc.push(fs.sym);
+ auto a = fs.statement.flatten(sc);
+ sc = sc.pop();
+ if (!a)
+ {
+ return a;
+ }
+ auto b = new Statements(a.dim);
+ foreach (i, s; *a)
+ {
+ (*b)[i] = s ? new ForwardingStatement(s.loc, fs.sym, s) : null;
+ }
+ return b;
+
+ case STMT.Conditional:
+ auto cs = statement.isConditionalStatement();
+ Statement s;
+
+ //printf("ConditionalStatement::flatten()\n");
+ if (cs.condition.include(sc))
+ {
+ DebugCondition dc = cs.condition.isDebugCondition();
+ if (dc)
+ {
+ s = new DebugStatement(cs.loc, cs.ifbody);
+ debugThrowWalker(cs.ifbody);
+ }
+ else
+ s = cs.ifbody;
+ }
+ else
+ s = cs.elsebody;
+
+ auto a = new Statements();
+ a.push(s);
+ return a;
+
+ case STMT.StaticForeach:
+ auto sfs = statement.isStaticForeachStatement();
+ sfs.sfe.prepare(sc);
+ if (sfs.sfe.ready())
+ {
+ auto s = makeTupleForeach!(true, false)(sc, sfs.sfe.aggrfe, sfs.sfe.needExpansion);
+ auto result = s.flatten(sc);
+ if (result)
+ {
+ return result;
+ }
+ result = new Statements();
+ result.push(s);
+ return result;
+ }
+ else
+ return errorStatements();
+
+ case STMT.Debug:
+ auto ds = statement.isDebugStatement();
+ Statements* a = ds.statement ? ds.statement.flatten(sc) : null;
+ if (!a)
+ return null;
+
+ foreach (ref s; *a)
+ {
+ s = new DebugStatement(ds.loc, s);
+ }
+ return a;
+
+ case STMT.Label:
+ auto ls = statement.isLabelStatement();
+ if (!ls.statement)
+ return null;
+
+ Statements* a = null;
+ a = ls.statement.flatten(sc);
+ if (!a)
+ return null;
+
+ if (!a.dim)
+ {
+ a.push(new ExpStatement(ls.loc, cast(Expression)null));
+ }
+
+ // reuse 'this' LabelStatement
+ ls.statement = (*a)[0];
+ (*a)[0] = ls;
+ return a;
+
+ case STMT.Compile:
+ auto cs = statement.isCompileStatement();
+
+
+ OutBuffer buf;
+ if (expressionsToString(buf, sc, cs.exps))
+ return errorStatements();
+
+ const errors = global.errors;
+ const len = buf.length;
+ buf.writeByte(0);
+ const str = buf.extractSlice()[0 .. len];
+ scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false);
+ p.nextToken();
+
+ auto a = new Statements();
+ while (p.token.value != TOK.endOfFile)
+ {
+ Statement s = p.parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope);
+ if (!s || global.errors != errors)
+ return errorStatements();
+ a.push(s);
+ }
+ return a;
+ default:
+ return null;
+ }
+}
+
+/***********************************************************
+ * Convert TemplateMixin members (== Dsymbols) to Statements.
+ */
+private Statement toStatement(Dsymbol s)
+{
+ extern (C++) final class ToStmt : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Statement result;
+
+ Statement visitMembers(Loc loc, Dsymbols* a)
+ {
+ if (!a)
+ return null;
+
+ auto statements = new Statements();
+ foreach (s; *a)
+ {
+ statements.push(toStatement(s));
+ }
+ return new CompoundStatement(loc, statements);
+ }
+
+ override void visit(Dsymbol s)
+ {
+ .error(Loc.initial, "Internal Compiler Error: cannot mixin %s `%s`\n", s.kind(), s.toChars());
+ result = new ErrorStatement();
+ }
+
+ override void visit(TemplateMixin tm)
+ {
+ auto a = new Statements();
+ foreach (m; *tm.members)
+ {
+ Statement s = toStatement(m);
+ if (s)
+ a.push(s);
+ }
+ result = new CompoundStatement(tm.loc, a);
+ }
+
+ /* An actual declaration symbol will be converted to DeclarationExp
+ * with ExpStatement.
+ */
+ Statement declStmt(Dsymbol s)
+ {
+ auto de = new DeclarationExp(s.loc, s);
+ de.type = Type.tvoid; // avoid repeated semantic
+ return new ExpStatement(s.loc, de);
+ }
+
+ override void visit(VarDeclaration d)
+ {
+ result = declStmt(d);
+ }
+
+ override void visit(AggregateDeclaration d)
+ {
+ result = declStmt(d);
+ }
+
+ override void visit(FuncDeclaration d)
+ {
+ result = declStmt(d);
+ }
+
+ override void visit(EnumDeclaration d)
+ {
+ result = declStmt(d);
+ }
+
+ override void visit(AliasDeclaration d)
+ {
+ result = declStmt(d);
+ }
+
+ override void visit(TemplateDeclaration d)
+ {
+ result = declStmt(d);
+ }
+
+ /* All attributes have been already picked by the semantic analysis of
+ * 'bottom' declarations (function, struct, class, etc).
+ * So we don't have to copy them.
+ */
+ override void visit(StorageClassDeclaration d)
+ {
+ result = visitMembers(d.loc, d.decl);
+ }
+
+ override void visit(DeprecatedDeclaration d)
+ {
+ result = visitMembers(d.loc, d.decl);
+ }
+
+ override void visit(LinkDeclaration d)
+ {
+ result = visitMembers(d.loc, d.decl);
+ }
+
+ override void visit(VisibilityDeclaration d)
+ {
+ result = visitMembers(d.loc, d.decl);
+ }
+
+ override void visit(AlignDeclaration d)
+ {
+ result = visitMembers(d.loc, d.decl);
+ }
+
+ override void visit(UserAttributeDeclaration d)
+ {
+ result = visitMembers(d.loc, d.decl);
+ }
+
+ override void visit(ForwardingAttribDeclaration d)
+ {
+ result = visitMembers(d.loc, d.decl);
+ }
+
+ override void visit(StaticAssert s)
+ {
+ }
+
+ override void visit(Import s)
+ {
+ }
+
+ override void visit(PragmaDeclaration d)
+ {
+ }
+
+ override void visit(ConditionalDeclaration d)
+ {
+ result = visitMembers(d.loc, d.include(null));
+ }
+
+ override void visit(StaticForeachDeclaration d)
+ {
+ assert(d.sfe && !!d.sfe.aggrfe ^ !!d.sfe.rangefe);
+ result = visitMembers(d.loc, d.include(null));
+ }
+
+ override void visit(CompileDeclaration d)
+ {
+ result = visitMembers(d.loc, d.include(null));
+ }
+ }
+
+ if (!s)
+ return null;
+
+ scope ToStmt v = new ToStmt();
+ s.accept(v);
+ return v.result;
+}
+
+/**
+Marks all occurring ThrowStatements as internalThrows.
+This is intended to be called from a DebugStatement as it allows
+to mark all its nodes as nothrow.
+
+Params:
+ s = AST Node to traverse
+*/
+private void debugThrowWalker(Statement s)
+{
+
+ extern(C++) final class DebugWalker : SemanticTimeTransitiveVisitor
+ {
+ alias visit = SemanticTimeTransitiveVisitor.visit;
+ public:
+
+ override void visit(ThrowStatement s)
+ {
+ s.internalThrow = true;
+ }
+
+ override void visit(CallExp s)
+ {
+ s.inDebugStatement = true;
+ }
+ }
+
+ scope walker = new DebugWalker();
+ s.accept(walker);
+}
diff --git a/gcc/d/dmd/staticassert.c b/gcc/d/dmd/staticassert.c
deleted file mode 100644
index c2d0f5b65c2..00000000000
--- a/gcc/d/dmd/staticassert.c
+++ /dev/null
@@ -1,55 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/staticassert.c
- */
-
-#include "root/dsystem.h"
-
-#include "mars.h"
-#include "dsymbol.h"
-#include "staticassert.h"
-#include "expression.h"
-#include "id.h"
-#include "scope.h"
-#include "template.h"
-#include "declaration.h"
-
-bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors);
-
-/********************************* AttribDeclaration ****************************/
-
-StaticAssert::StaticAssert(Loc loc, Expression *exp, Expression *msg)
- : Dsymbol(Id::empty)
-{
- this->loc = loc;
- this->exp = exp;
- this->msg = msg;
-}
-
-Dsymbol *StaticAssert::syntaxCopy(Dsymbol *s)
-{
- assert(!s);
- return new StaticAssert(loc, exp->syntaxCopy(), msg ? msg->syntaxCopy() : NULL);
-}
-
-void StaticAssert::addMember(Scope *, ScopeDsymbol *)
-{
- // we didn't add anything
-}
-
-bool StaticAssert::oneMember(Dsymbol **ps, Identifier *)
-{
- //printf("StaticAssert::oneMember())\n");
- *ps = NULL;
- return true;
-}
-
-const char *StaticAssert::kind() const
-{
- return "static assert";
-}
diff --git a/gcc/d/dmd/staticassert.d b/gcc/d/dmd/staticassert.d
new file mode 100644
index 00000000000..984dc42fbab
--- /dev/null
+++ b/gcc/d/dmd/staticassert.d
@@ -0,0 +1,66 @@
+/**
+ * Defines the `Dsymbol` representing a `static assert()`.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/version.html#static-assert, Static Assert)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/staticassert.d, _staticassert.d)
+ * Documentation: https://dlang.org/phobos/dmd_staticassert.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/staticassert.d
+ */
+
+module dmd.staticassert;
+
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.expression;
+import dmd.globals;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.visitor;
+
+/***********************************************************
+ */
+extern (C++) final class StaticAssert : Dsymbol
+{
+ Expression exp;
+ Expression msg;
+
+ extern (D) this(const ref Loc loc, Expression exp, Expression msg)
+ {
+ super(loc, Id.empty);
+ this.exp = exp;
+ this.msg = msg;
+ }
+
+ override StaticAssert syntaxCopy(Dsymbol s)
+ {
+ assert(!s);
+ return new StaticAssert(loc, exp.syntaxCopy(), msg ? msg.syntaxCopy() : null);
+ }
+
+ override void addMember(Scope* sc, ScopeDsymbol sds)
+ {
+ // we didn't add anything
+ }
+
+ override bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ //printf("StaticAssert::oneMember())\n");
+ *ps = null;
+ return true;
+ }
+
+ override const(char)* kind() const
+ {
+ return "static assert";
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
diff --git a/gcc/d/dmd/staticassert.h b/gcc/d/dmd/staticassert.h
index 6d43cb73a0d..8f880804454 100644
--- a/gcc/d/dmd/staticassert.h
+++ b/gcc/d/dmd/staticassert.h
@@ -5,7 +5,7 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/dlang/dmd/blob/master/src/staticassert.h
+ * https://github.com/dlang/dmd/blob/master/src/dmd/staticassert.h
*/
#pragma once
@@ -20,9 +20,7 @@ public:
Expression *exp;
Expression *msg;
- StaticAssert(Loc loc, Expression *exp, Expression *msg);
-
- Dsymbol *syntaxCopy(Dsymbol *s);
+ StaticAssert *syntaxCopy(Dsymbol *s);
void addMember(Scope *sc, ScopeDsymbol *sds);
bool oneMember(Dsymbol **ps, Identifier *ident);
const char *kind() const;
diff --git a/gcc/d/dmd/staticcond.c b/gcc/d/dmd/staticcond.c
deleted file mode 100644
index ef0a35ff382..00000000000
--- a/gcc/d/dmd/staticcond.c
+++ /dev/null
@@ -1,96 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/staticcond.c
- */
-
-#include "mars.h"
-#include "expression.h"
-#include "mtype.h"
-#include "scope.h"
-
-/********************************************
- * Semantically analyze and then evaluate a static condition at compile time.
- * This is special because short circuit operators &&, || and ?: at the top
- * level are not semantically analyzed if the result of the expression is not
- * necessary.
- * Params:
- * exp = original expression, for error messages
- * Returns:
- * true if evaluates to true
- */
-
-bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors)
-{
- if (e->op == TOKandand || e->op == TOKoror)
- {
- LogicalExp *aae = (LogicalExp *)e;
- bool result = evalStaticCondition(sc, exp, aae->e1, errors);
- if (errors)
- return false;
- if (e->op == TOKandand)
- {
- if (!result)
- return false;
- }
- else
- {
- if (result)
- return true;
- }
- result = evalStaticCondition(sc, exp, aae->e2, errors);
- return !errors && result;
- }
-
- if (e->op == TOKquestion)
- {
- CondExp *ce = (CondExp *)e;
- bool result = evalStaticCondition(sc, exp, ce->econd, errors);
- if (errors)
- return false;
- Expression *leg = result ? ce->e1 : ce->e2;
- result = evalStaticCondition(sc, exp, leg, errors);
- return !errors && result;
- }
-
- unsigned nerrors = global.errors;
-
- sc = sc->startCTFE();
- sc->flags |= SCOPEcondition;
-
- e = expressionSemantic(e, sc);
- e = resolveProperties(sc, e);
-
- sc = sc->endCTFE();
- e = e->optimize(WANTvalue);
-
- if (nerrors != global.errors ||
- e->op == TOKerror ||
- e->type->toBasetype() == Type::terror)
- {
- errors = true;
- return false;
- }
-
- if (!e->type->isBoolean())
- {
- exp->error("expression %s of type %s does not have a boolean value", exp->toChars(), e->type->toChars());
- errors = true;
- return false;
- }
-
- e = e->ctfeInterpret();
-
- if (e->isBool(true))
- return true;
- else if (e->isBool(false))
- return false;
-
- e->error("expression %s is not constant", e->toChars());
- errors = true;
- return false;
-}
diff --git a/gcc/d/dmd/staticcond.d b/gcc/d/dmd/staticcond.d
new file mode 100644
index 00000000000..2f27414a56c
--- /dev/null
+++ b/gcc/d/dmd/staticcond.d
@@ -0,0 +1,424 @@
+/**
+ * Lazily evaluate static conditions for `static if`, `static assert` and template constraints.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/staticcond.d, _staticcond.d)
+ * Documentation: https://dlang.org/phobos/dmd_staticcond.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/staticcond.d
+ */
+
+module dmd.staticcond;
+
+import dmd.arraytypes;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.globals;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.root.array;
+import dmd.root.outbuffer;
+import dmd.tokens;
+
+
+
+/********************************************
+ * Semantically analyze and then evaluate a static condition at compile time.
+ * This is special because short circuit operators &&, || and ?: at the top
+ * level are not semantically analyzed if the result of the expression is not
+ * necessary.
+ * Params:
+ * sc = instantiating scope
+ * original = original expression, for error messages
+ * e = resulting expression
+ * errors = set to `true` if errors occurred
+ * negatives = array to store negative clauses
+ * Returns:
+ * true if evaluates to true
+ */
+bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool errors, Expressions* negatives = null)
+{
+ if (negatives)
+ negatives.setDim(0);
+
+ bool impl(Expression e)
+ {
+ if (e.op == TOK.not)
+ {
+ NotExp ne = cast(NotExp)e;
+ return !impl(ne.e1);
+ }
+
+ if (e.op == TOK.andAnd || e.op == TOK.orOr)
+ {
+ LogicalExp aae = cast(LogicalExp)e;
+ bool result = impl(aae.e1);
+ if (errors)
+ return false;
+ if (e.op == TOK.andAnd)
+ {
+ if (!result)
+ return false;
+ }
+ else
+ {
+ if (result)
+ return true;
+ }
+ result = impl(aae.e2);
+ return !errors && result;
+ }
+
+ if (e.op == TOK.question)
+ {
+ CondExp ce = cast(CondExp)e;
+ bool result = impl(ce.econd);
+ if (errors)
+ return false;
+ Expression leg = result ? ce.e1 : ce.e2;
+ result = impl(leg);
+ return !errors && result;
+ }
+
+ Expression before = e;
+ const uint nerrors = global.errors;
+
+ sc = sc.startCTFE();
+ sc.flags |= SCOPE.condition;
+
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ e = e.toBoolean(sc);
+
+ sc = sc.endCTFE();
+ e = e.optimize(WANTvalue);
+
+ if (nerrors != global.errors ||
+ e.op == TOK.error ||
+ e.type.toBasetype() == Type.terror)
+ {
+ errors = true;
+ return false;
+ }
+
+ e = e.ctfeInterpret();
+
+ if (e.isBool(true))
+ return true;
+ else if (e.isBool(false))
+ {
+ if (negatives)
+ negatives.push(before);
+ return false;
+ }
+
+ e.error("expression `%s` is not constant", e.toChars());
+ errors = true;
+ return false;
+ }
+ return impl(e);
+}
+
+/********************************************
+ * Format a static condition as a tree-like structure, marking failed and
+ * bypassed expressions.
+ * Params:
+ * original = original expression
+ * instantiated = instantiated expression
+ * negatives = array with negative clauses from `instantiated` expression
+ * full = controls whether it shows the full output or only failed parts
+ * itemCount = returns the number of written clauses
+ * Returns:
+ * formatted string or `null` if the expressions were `null`, or if the
+ * instantiated expression is not based on the original one
+ */
+const(char)* visualizeStaticCondition(Expression original, Expression instantiated,
+ const Expression[] negatives, bool full, ref uint itemCount)
+{
+ if (!original || !instantiated || original.loc !is instantiated.loc)
+ return null;
+
+ OutBuffer buf;
+
+ if (full)
+ itemCount = visualizeFull(original, instantiated, negatives, buf);
+ else
+ itemCount = visualizeShort(original, instantiated, negatives, buf);
+
+ return buf.extractChars();
+}
+
+private uint visualizeFull(Expression original, Expression instantiated,
+ const Expression[] negatives, ref OutBuffer buf)
+{
+ // tree-like structure; traverse and format simultaneously
+ uint count;
+ uint indent;
+
+ static void printOr(uint indent, ref OutBuffer buf)
+ {
+ buf.reserve(indent * 4 + 8);
+ foreach (i; 0 .. indent)
+ buf.writestring(" ");
+ buf.writestring(" or:\n");
+ }
+
+ // returns true if satisfied
+ bool impl(Expression orig, Expression e, bool inverted, bool orOperand, bool unreached)
+ {
+ TOK op = orig.op;
+
+ // lower all 'not' to the bottom
+ // !(A && B) -> !A || !B
+ // !(A || B) -> !A && !B
+ if (inverted)
+ {
+ if (op == TOK.andAnd)
+ op = TOK.orOr;
+ else if (op == TOK.orOr)
+ op = TOK.andAnd;
+ }
+
+ if (op == TOK.not)
+ {
+ NotExp no = cast(NotExp)orig;
+ NotExp ne = cast(NotExp)e;
+ assert(ne);
+ return impl(no.e1, ne.e1, !inverted, orOperand, unreached);
+ }
+ else if (op == TOK.andAnd)
+ {
+ BinExp bo = cast(BinExp)orig;
+ BinExp be = cast(BinExp)e;
+ assert(be);
+ const r1 = impl(bo.e1, be.e1, inverted, false, unreached);
+ const r2 = impl(bo.e2, be.e2, inverted, false, unreached || !r1);
+ return r1 && r2;
+ }
+ else if (op == TOK.orOr)
+ {
+ if (!orOperand) // do not indent A || B || C twice
+ indent++;
+ BinExp bo = cast(BinExp)orig;
+ BinExp be = cast(BinExp)e;
+ assert(be);
+ const r1 = impl(bo.e1, be.e1, inverted, true, unreached);
+ printOr(indent, buf);
+ const r2 = impl(bo.e2, be.e2, inverted, true, unreached);
+ if (!orOperand)
+ indent--;
+ return r1 || r2;
+ }
+ else if (op == TOK.question)
+ {
+ CondExp co = cast(CondExp)orig;
+ CondExp ce = cast(CondExp)e;
+ assert(ce);
+ if (!inverted)
+ {
+ // rewrite (A ? B : C) as (A && B || !A && C)
+ if (!orOperand)
+ indent++;
+ const r1 = impl(co.econd, ce.econd, inverted, false, unreached);
+ const r2 = impl(co.e1, ce.e1, inverted, false, unreached || !r1);
+ printOr(indent, buf);
+ const r3 = impl(co.econd, ce.econd, !inverted, false, unreached);
+ const r4 = impl(co.e2, ce.e2, inverted, false, unreached || !r3);
+ if (!orOperand)
+ indent--;
+ return r1 && r2 || r3 && r4;
+ }
+ else
+ {
+ // rewrite !(A ? B : C) as (!A || !B) && (A || !C)
+ if (!orOperand)
+ indent++;
+ const r1 = impl(co.econd, ce.econd, inverted, false, unreached);
+ printOr(indent, buf);
+ const r2 = impl(co.e1, ce.e1, inverted, false, unreached);
+ const r12 = r1 || r2;
+ const r3 = impl(co.econd, ce.econd, !inverted, false, unreached || !r12);
+ printOr(indent, buf);
+ const r4 = impl(co.e2, ce.e2, inverted, false, unreached || !r12);
+ if (!orOperand)
+ indent--;
+ return (r1 || r2) && (r3 || r4);
+ }
+ }
+ else // 'primitive' expression
+ {
+ buf.reserve(indent * 4 + 4);
+ foreach (i; 0 .. indent)
+ buf.writestring(" ");
+
+ // find its value; it may be not computed, if there was a short circuit,
+ // but we handle this case with `unreached` flag
+ bool value = true;
+ if (!unreached)
+ {
+ foreach (fe; negatives)
+ {
+ if (fe is e)
+ {
+ value = false;
+ break;
+ }
+ }
+ }
+ // write the marks first
+ const satisfied = inverted ? !value : value;
+ if (!satisfied && !unreached)
+ buf.writestring(" > ");
+ else if (unreached)
+ buf.writestring(" - ");
+ else
+ buf.writestring(" ");
+ // then the expression itself
+ if (inverted)
+ buf.writeByte('!');
+ buf.writestring(orig.toChars);
+ buf.writenl();
+ count++;
+ return satisfied;
+ }
+ }
+
+ impl(original, instantiated, false, true, false);
+ return count;
+}
+
+private uint visualizeShort(Expression original, Expression instantiated,
+ const Expression[] negatives, ref OutBuffer buf)
+{
+ // simple list; somewhat similar to long version, so no comments
+ // one difference is that it needs to hold items to display in a stack
+
+ static struct Item
+ {
+ Expression orig;
+ bool inverted;
+ }
+
+ Array!Item stack;
+
+ bool impl(Expression orig, Expression e, bool inverted)
+ {
+ TOK op = orig.op;
+
+ if (inverted)
+ {
+ if (op == TOK.andAnd)
+ op = TOK.orOr;
+ else if (op == TOK.orOr)
+ op = TOK.andAnd;
+ }
+
+ if (op == TOK.not)
+ {
+ NotExp no = cast(NotExp)orig;
+ NotExp ne = cast(NotExp)e;
+ assert(ne);
+ return impl(no.e1, ne.e1, !inverted);
+ }
+ else if (op == TOK.andAnd)
+ {
+ BinExp bo = cast(BinExp)orig;
+ BinExp be = cast(BinExp)e;
+ assert(be);
+ bool r = impl(bo.e1, be.e1, inverted);
+ r = r && impl(bo.e2, be.e2, inverted);
+ return r;
+ }
+ else if (op == TOK.orOr)
+ {
+ BinExp bo = cast(BinExp)orig;
+ BinExp be = cast(BinExp)e;
+ assert(be);
+ const lbefore = stack.length;
+ bool r = impl(bo.e1, be.e1, inverted);
+ r = r || impl(bo.e2, be.e2, inverted);
+ if (r)
+ stack.setDim(lbefore); // purge added positive items
+ return r;
+ }
+ else if (op == TOK.question)
+ {
+ CondExp co = cast(CondExp)orig;
+ CondExp ce = cast(CondExp)e;
+ assert(ce);
+ if (!inverted)
+ {
+ const lbefore = stack.length;
+ bool a = impl(co.econd, ce.econd, inverted);
+ a = a && impl(co.e1, ce.e1, inverted);
+ bool b;
+ if (!a)
+ {
+ b = impl(co.econd, ce.econd, !inverted);
+ b = b && impl(co.e2, ce.e2, inverted);
+ }
+ const r = a || b;
+ if (r)
+ stack.setDim(lbefore);
+ return r;
+ }
+ else
+ {
+ bool a;
+ {
+ const lbefore = stack.length;
+ a = impl(co.econd, ce.econd, inverted);
+ a = a || impl(co.e1, ce.e1, inverted);
+ if (a)
+ stack.setDim(lbefore);
+ }
+ bool b;
+ if (a)
+ {
+ const lbefore = stack.length;
+ b = impl(co.econd, ce.econd, !inverted);
+ b = b || impl(co.e2, ce.e2, inverted);
+ if (b)
+ stack.setDim(lbefore);
+ }
+ return a && b;
+ }
+ }
+ else // 'primitive' expression
+ {
+ bool value = true;
+ foreach (fe; negatives)
+ {
+ if (fe is e)
+ {
+ value = false;
+ break;
+ }
+ }
+ const satisfied = inverted ? !value : value;
+ if (!satisfied)
+ stack.push(Item(orig, inverted));
+ return satisfied;
+ }
+ }
+
+ impl(original, instantiated, false);
+
+ foreach (i; 0 .. stack.length)
+ {
+ // write the expression only
+ buf.writestring(" ");
+ if (stack[i].inverted)
+ buf.writeByte('!');
+ buf.writestring(stack[i].orig.toChars);
+ // here with no trailing newline
+ if (i + 1 < stack.length)
+ buf.writenl();
+ }
+ return cast(uint)stack.length;
+}
diff --git a/gcc/d/dmd/stmtstate.d b/gcc/d/dmd/stmtstate.d
new file mode 100644
index 00000000000..bb13d7c0d06
--- /dev/null
+++ b/gcc/d/dmd/stmtstate.d
@@ -0,0 +1,142 @@
+/**
+ * Used to help transform statement AST into flow graph.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/stmtstate.d, _stmtstate.d)
+ * Documentation: https://dlang.org/phobos/dmd_stmtstate.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/stmtstate.d
+ */
+
+module dmd.stmtstate;
+
+import dmd.identifier;
+import dmd.statement;
+
+
+/************************************************
+ * Used to traverse the statement AST to transform it into
+ * a flow graph.
+ * Keeps track of things like "where does the `break` go".
+ * Params:
+ * block = type of the flow graph node
+ */
+struct StmtState(block)
+{
+ StmtState* prev;
+ Statement statement;
+
+ Identifier ident;
+ block* breakBlock;
+ block* contBlock;
+ block* switchBlock;
+ block* defaultBlock;
+ block* finallyBlock;
+ block* tryBlock;
+
+ this(StmtState* prev, Statement statement)
+ {
+ this.prev = prev;
+ this.statement = statement;
+ if (prev)
+ this.tryBlock = prev.tryBlock;
+ }
+
+ block* getBreakBlock(Identifier ident)
+ {
+ StmtState* bc;
+ if (ident)
+ {
+ Statement related = null;
+ block* ret = null;
+ for (bc = &this; bc; bc = bc.prev)
+ {
+ // The label for a breakBlock may actually be some levels up (e.g.
+ // on a try/finally wrapping a loop). We'll see if this breakBlock
+ // is the one to return once we reach that outer statement (which
+ // in many cases will be this same statement).
+ if (bc.breakBlock)
+ {
+ related = bc.statement.getRelatedLabeled();
+ ret = bc.breakBlock;
+ }
+ if (bc.statement == related && bc.prev.ident == ident)
+ return ret;
+ }
+ }
+ else
+ {
+ for (bc = &this; bc; bc = bc.prev)
+ {
+ if (bc.breakBlock)
+ return bc.breakBlock;
+ }
+ }
+ return null;
+ }
+
+ block* getContBlock(Identifier ident)
+ {
+ StmtState* bc;
+ if (ident)
+ {
+ block* ret = null;
+ for (bc = &this; bc; bc = bc.prev)
+ {
+ // The label for a contBlock may actually be some levels up (e.g.
+ // on a try/finally wrapping a loop). We'll see if this contBlock
+ // is the one to return once we reach that outer statement (which
+ // in many cases will be this same statement).
+ if (bc.contBlock)
+ {
+ ret = bc.contBlock;
+ }
+ if (bc.prev && bc.prev.ident == ident)
+ return ret;
+ }
+ }
+ else
+ {
+ for (bc = &this; bc; bc = bc.prev)
+ {
+ if (bc.contBlock)
+ return bc.contBlock;
+ }
+ }
+ return null;
+ }
+
+ block* getSwitchBlock()
+ {
+ StmtState* bc;
+ for (bc = &this; bc; bc = bc.prev)
+ {
+ if (bc.switchBlock)
+ return bc.switchBlock;
+ }
+ return null;
+ }
+
+ block* getDefaultBlock()
+ {
+ StmtState* bc;
+ for (bc = &this; bc; bc = bc.prev)
+ {
+ if (bc.defaultBlock)
+ return bc.defaultBlock;
+ }
+ return null;
+ }
+
+ block* getFinallyBlock()
+ {
+ StmtState* bc;
+ for (bc = &this; bc; bc = bc.prev)
+ {
+ if (bc.finallyBlock)
+ return bc.finallyBlock;
+ }
+ return null;
+ }
+}
diff --git a/gcc/d/dmd/target.d b/gcc/d/dmd/target.d
new file mode 100644
index 00000000000..d5b3de2c522
--- /dev/null
+++ b/gcc/d/dmd/target.d
@@ -0,0 +1,438 @@
+/**
+ * Handles target-specific parameters
+ *
+ * In order to allow for cross compilation, when the compiler produces a binary
+ * for a different platform than it is running on, target information needs
+ * to be abstracted. This is done in this module, primarily through `Target`.
+ *
+ * Note:
+ * While DMD itself does not support cross-compilation, GDC and LDC do.
+ * Hence, this module is (sometimes heavily) modified by them,
+ * and contributors should review how their changes affect them.
+ *
+ * See_Also:
+ * - $(LINK2 https://wiki.osdev.org/Target_Triplet, Target Triplets)
+ * - $(LINK2 https://github.com/ldc-developers/ldc, LDC repository)
+ * - $(LINK2 https://github.com/D-Programming-GDC/gcc, GDC repository)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/target.d, _target.d)
+ * Documentation: https://dlang.org/phobos/dmd_target.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/target.d
+ */
+
+module dmd.target;
+
+import dmd.globals : Param;
+
+enum CPU
+{
+ x87,
+ mmx,
+ sse,
+ sse2,
+ sse3,
+ ssse3,
+ sse4_1,
+ sse4_2,
+ avx, // AVX1 instruction set
+ avx2, // AVX2 instruction set
+ avx512, // AVX-512 instruction set
+
+ // Special values that don't survive past the command line processing
+ baseline, // (default) the minimum capability CPU
+ native // the machine the compiler is being run on
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Describes a back-end target. At present it is incomplete, but in the future
+ * it should grow to contain most or all target machine and target O/S specific
+ * information.
+ *
+ * In many cases, calls to sizeof() can't be used directly for getting data type
+ * sizes since cross compiling is supported and would end up using the host
+ * sizes rather than the target sizes.
+ */
+extern (C++) struct Target
+{
+ import dmd.dscope : Scope;
+ import dmd.expression : Expression;
+ import dmd.func : FuncDeclaration;
+ import dmd.globals : LINK, Loc, d_int64;
+ import dmd.astenums : TY;
+ import dmd.mtype : Type, TypeFunction, TypeTuple;
+ import dmd.root.ctfloat : real_t;
+ import dmd.statement : Statement;
+
+ /// Bit decoding of the Target.OS
+ enum OS : ubyte
+ {
+ /* These are mutually exclusive; one and only one is set.
+ * Match spelling and casing of corresponding version identifiers
+ */
+ Freestanding = 0,
+ linux = 1,
+ Windows = 2,
+ OSX = 4,
+ OpenBSD = 8,
+ FreeBSD = 0x10,
+ Solaris = 0x20,
+ DragonFlyBSD = 0x40,
+
+ // Combination masks
+ all = linux | Windows | OSX | OpenBSD | FreeBSD | Solaris | DragonFlyBSD,
+ Posix = linux | OSX | OpenBSD | FreeBSD | Solaris | DragonFlyBSD,
+ }
+
+ OS os;
+ ubyte osMajor;
+
+ // D ABI
+ ubyte ptrsize; /// size of a pointer in bytes
+ ubyte realsize; /// size a real consumes in memory
+ ubyte realpad; /// padding added to the CPU real size to bring it up to realsize
+ ubyte realalignsize; /// alignment for reals
+ ubyte classinfosize; /// size of `ClassInfo`
+ ulong maxStaticDataSize; /// maximum size of static data
+
+ /// C ABI
+ TargetC c;
+
+ /// C++ ABI
+ TargetCPP cpp;
+
+ /// Objective-C ABI
+ TargetObjC objc;
+
+ /// Architecture name
+ const(char)[] architectureName;
+ CPU cpu = CPU.baseline; // CPU instruction set to target
+ bool is64bit; // generate 64 bit code for x86_64; true by default for 64 bit dmd
+ bool isLP64; // pointers are 64 bits
+
+ // Environmental
+ const(char)[] obj_ext; /// extension for object files
+ const(char)[] lib_ext; /// extension for static library files
+ const(char)[] dll_ext; /// extension for dynamic library files
+ bool run_noext; /// allow -run sources without extensions
+ bool mscoff = false; // for Win32: write MsCoff object files instead of OMF
+ /**
+ * Values representing all properties for floating point types
+ */
+ extern (C++) struct FPTypeProperties(T)
+ {
+ real_t max; /// largest representable value that's not infinity
+ real_t min_normal; /// smallest representable normalized value that's not 0
+ real_t nan; /// NaN value
+ real_t infinity; /// infinity value
+ real_t epsilon; /// smallest increment to the value 1
+
+ d_int64 dig; /// number of decimal digits of precision
+ d_int64 mant_dig; /// number of bits in mantissa
+ d_int64 max_exp; /// maximum int value such that 2$(SUPERSCRIPT `max_exp-1`) is representable
+ d_int64 min_exp; /// minimum int value such that 2$(SUPERSCRIPT `min_exp-1`) is representable as a normalized value
+ d_int64 max_10_exp; /// maximum int value such that 10$(SUPERSCRIPT `max_10_exp` is representable)
+ d_int64 min_10_exp; /// minimum int value such that 10$(SUPERSCRIPT `min_10_exp`) is representable as a normalized value
+ }
+
+ FPTypeProperties!float FloatProperties; ///
+ FPTypeProperties!double DoubleProperties; ///
+ FPTypeProperties!real_t RealProperties; ///
+
+ private Type tvalist; // cached lazy result of va_listType()
+
+ private const(Param)* params; // cached reference to global.params
+
+ /**
+ * Initialize the Target
+ */
+ extern (C++) void _init(ref const Param params);
+
+
+ /**
+ * Deinitializes the global state of the compiler.
+ *
+ * This can be used to restore the state set by `_init` to its original
+ * state.
+ */
+ void deinitialize()
+ {
+ this = this.init;
+ }
+
+ /**
+ * Requested target memory alignment size of the given type.
+ * Params:
+ * type = type to inspect
+ * Returns:
+ * alignment in bytes
+ */
+ extern (C++) uint alignsize(Type type);
+
+ /**
+ * Requested target field alignment size of the given type.
+ * Params:
+ * type = type to inspect
+ * Returns:
+ * alignment in bytes
+ */
+ extern (C++) uint fieldalign(Type type);
+
+ /**
+ * Type for the `va_list` type for the target; e.g., required for `_argptr`
+ * declarations.
+ * NOTE: For Posix/x86_64 this returns the type which will really
+ * be used for passing an argument of type va_list.
+ * Returns:
+ * `Type` that represents `va_list`.
+ */
+ extern (C++) Type va_listType(const ref Loc loc, Scope* sc);
+
+ /**
+ * Checks whether the target supports a vector type.
+ * Params:
+ * sz = vector type size in bytes
+ * type = vector element type
+ * Returns:
+ * 0 vector type is supported,
+ * 1 vector type is not supported on the target at all
+ * 2 vector element type is not supported
+ * 3 vector size is not supported
+ */
+ extern (C++) int isVectorTypeSupported(int sz, Type type);
+
+ /**
+ * Checks whether the target supports the given operation for vectors.
+ * Params:
+ * type = target type of operation
+ * op = the unary or binary op being done on the `type`
+ * t2 = type of second operand if `op` is a binary operation
+ * Returns:
+ * true if the operation is supported or type is not a vector
+ */
+ extern (C++) bool isVectorOpSupported(Type type, uint op, Type t2 = null);
+
+ /**
+ * Default system linkage for the target.
+ * Returns:
+ * `LINK` to use for `extern(System)`
+ */
+ extern (C++) LINK systemLinkage();
+
+ /**
+ * Describes how an argument type is passed to a function on target.
+ * Params:
+ * t = type to break down
+ * Returns:
+ * tuple of types if type is passed in one or more registers
+ * empty tuple if type is always passed on the stack
+ * null if the type is a `void` or argtypes aren't supported by the target
+ */
+ extern (C++) TypeTuple toArgTypes(Type t);
+
+ /**
+ * Determine return style of function - whether in registers or
+ * through a hidden pointer to the caller's stack.
+ * Params:
+ * tf = function type to check
+ * needsThis = true if the function type is for a non-static member function
+ * Returns:
+ * true if return value from function is on the stack
+ */
+ extern (C++) bool isReturnOnStack(TypeFunction tf, bool needsThis);
+
+ /***
+ * Determine the size a value of type `t` will be when it
+ * is passed on the function parameter stack.
+ * Params:
+ * loc = location to use for error messages
+ * t = type of parameter
+ * Returns:
+ * size used on parameter stack
+ */
+ extern (C++) ulong parameterSize(const ref Loc loc, Type t);
+
+ /**
+ * Decides whether an `in` parameter of the specified POD type is to be
+ * passed by reference or by value. To be used with `-preview=in` only!
+ * Params:
+ * t = type of the `in` parameter, must be a POD
+ * Returns:
+ * `true` if the `in` parameter is to be passed by reference
+ */
+ extern(C++) bool preferPassByRef(Type t);
+
+ /**
+ * Get targetInfo by key
+ * Params:
+ * name = name of targetInfo to get
+ * loc = location to use for error messages
+ * Returns:
+ * Expression for the requested targetInfo
+ */
+ extern (C++) Expression getTargetInfo(const(char)* name, const ref Loc loc);
+
+ /**
+ * Params:
+ * tf = type of function being called
+ * Returns: `true` if the callee invokes destructors for arguments.
+ */
+ extern (C++) bool isCalleeDestroyingArgs(TypeFunction tf);
+
+ /**
+ * Returns true if the implementation for object monitors is always defined
+ * in the D runtime library (rt/monitor_.d).
+ * Params:
+ * fd = function with `synchronized` storage class.
+ * fbody = entire function body of `fd`
+ * Returns:
+ * `false` if the target backend handles synchronizing monitors.
+ */
+ extern (C++) bool libraryObjectMonitors(FuncDeclaration fd, Statement fbody);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Functions and variables specific to interfacing with extern(C) ABI.
+ */
+struct TargetC
+{
+ enum Runtime : ubyte
+ {
+ Unspecified,
+ Bionic,
+ DigitalMars,
+ Glibc,
+ Microsoft,
+ Musl,
+ Newlib,
+ UClibc,
+ WASI,
+ }
+
+ enum BitFieldStyle : ubyte
+ {
+ Unspecified,
+ Dm_Ms, /// Digital Mars and Microsoft C compilers
+ /// https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-160
+ /// https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields?view=msvc-160
+ Gcc_Clang, /// gcc and clang
+ }
+
+ ubyte longsize; /// size of a C `long` or `unsigned long` type
+ ubyte long_doublesize; /// size of a C `long double`
+ ubyte wchar_tsize; /// size of a C `wchar_t` type
+ Runtime runtime; /// vendor of the C runtime to link against
+ BitFieldStyle bitFieldStyle; /// different C compilers do it differently
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Functions and variables specific to interface with extern(C++) ABI.
+ */
+struct TargetCPP
+{
+ import dmd.dsymbol : Dsymbol;
+ import dmd.dclass : ClassDeclaration;
+ import dmd.func : FuncDeclaration;
+ import dmd.mtype : Parameter, Type;
+
+ enum Runtime : ubyte
+ {
+ Unspecified,
+ Clang,
+ DigitalMars,
+ Gcc,
+ Microsoft,
+ Sun
+ }
+ bool reverseOverloads; /// set if overloaded functions are grouped and in reverse order (such as in dmc and cl)
+ bool exceptions; /// set if catching C++ exceptions is supported
+ bool twoDtorInVtable; /// target C++ ABI puts deleting and non-deleting destructor into vtable
+ bool wrapDtorInExternD; /// set if C++ dtors require a D wrapper to be callable from runtime
+ Runtime runtime; /// vendor of the C++ runtime to link against
+
+ /**
+ * Mangle the given symbol for C++ ABI.
+ * Params:
+ * s = declaration with C++ linkage
+ * Returns:
+ * string mangling of symbol
+ */
+ extern (C++) const(char)* toMangle(Dsymbol s);
+
+ /**
+ * Get RTTI mangling of the given class declaration for C++ ABI.
+ * Params:
+ * cd = class with C++ linkage
+ * Returns:
+ * string mangling of C++ typeinfo
+ */
+ extern (C++) const(char)* typeInfoMangle(ClassDeclaration cd);
+
+ /**
+ * Get mangle name of a this-adjusting thunk to the given function
+ * declaration for C++ ABI.
+ * Params:
+ * fd = function with C++ linkage
+ * offset = call offset to the vptr
+ * Returns:
+ * string mangling of C++ thunk, or null if unhandled
+ */
+ extern (C++) const(char)* thunkMangle(FuncDeclaration fd, int offset);
+
+ /**
+ * Gets vendor-specific type mangling for C++ ABI.
+ * Params:
+ * t = type to inspect
+ * Returns:
+ * string if type is mangled specially on target
+ * null if unhandled
+ */
+ extern (C++) const(char)* typeMangle(Type t);
+
+ /**
+ * Get the type that will really be used for passing the given argument
+ * to an `extern(C++)` function.
+ * Params:
+ * p = parameter to be passed.
+ * Returns:
+ * `Type` to use for parameter `p`.
+ */
+ extern (C++) Type parameterType(Parameter p);
+
+ /**
+ * Checks whether type is a vendor-specific fundamental type.
+ * Params:
+ * t = type to inspect
+ * isFundamental = where to store result
+ * Returns:
+ * true if isFundamental was set by function
+ */
+ extern (C++) bool fundamentalType(const Type t, ref bool isFundamental);
+
+ /**
+ * Get the starting offset position for fields of an `extern(C++)` class
+ * that is derived from the given base class.
+ * Params:
+ * baseClass = base class with C++ linkage
+ * Returns:
+ * starting offset to lay out derived class fields
+ */
+ extern (C++) uint derivedClassOffset(ClassDeclaration baseClass);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Functions and variables specific to interface with extern(Objective-C) ABI.
+ */
+struct TargetObjC
+{
+ bool supported; /// set if compiler can interface with Objective-C
+}
+
+////////////////////////////////////////////////////////////////////////////////
+extern (C++) __gshared Target target;
diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h
index f8f977c9aea..83281a6358f 100644
--- a/gcc/d/dmd/target.h
+++ b/gcc/d/dmd/target.h
@@ -23,22 +23,75 @@ class FuncDeclaration;
class Parameter;
class Statement;
class Type;
-class TypeFunction;
class TypeTuple;
-struct OutBuffer;
+class TypeFunction;
+
+enum class CPU
+{
+ x87,
+ mmx,
+ sse,
+ sse2,
+ sse3,
+ ssse3,
+ sse4_1,
+ sse4_2,
+ avx, // AVX1 instruction set
+ avx2, // AVX2 instruction set
+ avx512, // AVX-512 instruction set
+
+ // Special values that don't survive past the command line processing
+ baseline, // (default) the minimum capability CPU
+ native // the machine the compiler is being run on
+};
struct TargetC
{
- unsigned longsize; // size of a C 'long' or 'unsigned long' type
- unsigned long_doublesize; // size of a C 'long double'
- Type *twchar_t; // C 'wchar_t' type
+ enum class Runtime : unsigned char
+ {
+ Unspecified,
+ Bionic,
+ DigitalMars,
+ Glibc,
+ Microsoft,
+ Musl,
+ Newlib,
+ UClibc,
+ WASI,
+ };
+
+ enum class BitFieldStyle : unsigned char
+ {
+ Unspecified,
+ Dm_Ms, // Digital Mars and Microsoft C compilers
+ // https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-160
+ // https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields?view=msvc-160
+ Gcc_Clang, // gcc and clang
+ };
+
+ uint8_t longsize; // size of a C 'long' or 'unsigned long' type
+ uint8_t long_doublesize; // size of a C 'long double'
+ uint8_t wchar_tsize; // size of a C 'wchar_t' type
+ Runtime runtime;
+ BitFieldStyle bitFieldStyle; // different C compilers do it differently
};
struct TargetCPP
{
+ enum class Runtime : unsigned char
+ {
+ Unspecified,
+ Clang,
+ DigitalMars,
+ Gcc,
+ Microsoft,
+ Sun
+ };
bool reverseOverloads; // with dmc and cl, overloaded functions are grouped and in reverse order
bool exceptions; // set if catching C++ exceptions is supported
bool twoDtorInVtable; // target C++ ABI puts deleting and non-deleting destructor into vtable
+ bool wrapDtorInExternD; // set if C++ dtors require a D wrapper to be callable from runtime
+ Runtime runtime;
const char *toMangle(Dsymbol *s);
const char *typeInfoMangle(ClassDeclaration *cd);
@@ -56,13 +109,35 @@ struct TargetObjC
struct Target
{
+ typedef unsigned char OS;
+ enum
+ {
+ /* These are mutually exclusive; one and only one is set.
+ * Match spelling and casing of corresponding version identifiers
+ */
+ OS_Freestanding = 0,
+ OS_linux = 1,
+ OS_Windows = 2,
+ OS_OSX = 4,
+ OS_OpenBSD = 8,
+ OS_FreeBSD = 0x10,
+ OS_Solaris = 0x20,
+ OS_DragonFlyBSD = 0x40,
+
+ // Combination masks
+ all = OS_linux | OS_Windows | OS_OSX | OS_OpenBSD | OS_FreeBSD | OS_Solaris | OS_DragonFlyBSD,
+ Posix = OS_linux | OS_OSX | OS_OpenBSD | OS_FreeBSD | OS_Solaris | OS_DragonFlyBSD,
+ };
+
+ OS os;
+ uint8_t osMajor;
// D ABI
- unsigned ptrsize;
- unsigned realsize; // size a real consumes in memory
- unsigned realpad; // 'padding' added to the CPU real size to bring it up to realsize
- unsigned realalignsize; // alignment for reals
- unsigned classinfosize; // size of 'ClassInfo'
- unsigned long long maxStaticDataSize; // maximum size of static data
+ uint8_t ptrsize;
+ uint8_t realsize; // size a real consumes in memory
+ uint8_t realpad; // 'padding' added to the CPU real size to bring it up to realsize
+ uint8_t realalignsize; // alignment for reals
+ uint8_t classinfosize; // size of 'ClassInfo'
+ uint64_t maxStaticDataSize; // maximum size of static data
// C ABI
TargetC c;
@@ -73,13 +148,24 @@ struct Target
// Objective-C ABI
TargetObjC objc;
+ DString architectureName; // name of the platform architecture (e.g. X86_64)
+ CPU cpu; // CPU instruction set to target
+ bool is64bit; // generate 64 bit code for x86_64; true by default for 64 bit dmd
+ bool isLP64; // pointers are 64 bits
+
+ // Environmental
+ DString obj_ext; /// extension for object files
+ DString lib_ext; /// extension for static library files
+ DString dll_ext; /// extension for dynamic library files
+ bool run_noext; /// allow -run sources without extensions
+ bool mscoff; /// for Win32: write COFF object files instead of OMF
+
template <typename T>
struct FPTypeProperties
{
real_t max;
real_t min_normal;
real_t nan;
- real_t snan;
real_t infinity;
real_t epsilon;
@@ -97,21 +183,27 @@ struct Target
private:
Type *tvalist;
+ const Param *params;
public:
void _init(const Param& params);
// Type sizes and support.
+ void setTriple(const char* _triple);
unsigned alignsize(Type *type);
unsigned fieldalign(Type *type);
Type *va_listType(const Loc &loc, Scope *sc); // get type of va_list
int isVectorTypeSupported(int sz, Type *type);
- bool isVectorOpSupported(Type *type, TOK op, Type *t2 = NULL);
+ bool isVectorOpSupported(Type *type, unsigned op, Type *t2 = NULL);
// ABI and backend.
LINK systemLinkage();
TypeTuple *toArgTypes(Type *t);
bool isReturnOnStack(TypeFunction *tf, bool needsThis);
+ d_uns64 parameterSize(const Loc& loc, Type *t);
+ bool preferPassByRef(Type *t);
Expression *getTargetInfo(const char* name, const Loc& loc);
+ bool isCalleeDestroyingArgs(TypeFunction* tf);
bool libraryObjectMonitors(FuncDeclaration *fd, Statement *fbody);
+ void addPredefinedGlobalIdentifiers() const;
};
extern Target target;
diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h
index 086ec72ba60..08ce9acef99 100644
--- a/gcc/d/dmd/template.h
+++ b/gcc/d/dmd/template.h
@@ -10,12 +10,9 @@
#pragma once
-#include "root/root.h"
#include "arraytypes.h"
#include "dsymbol.h"
-
-struct OutBuffer;
class Identifier;
class TemplateInstance;
class TemplateParameter;
@@ -26,18 +23,10 @@ class TemplateAliasParameter;
class TemplateTupleParameter;
class Type;
class TypeQualified;
-class TypeTypeof;
struct Scope;
class Expression;
-class AliasDeclaration;
class FuncDeclaration;
class Parameter;
-enum MATCH;
-enum PASS;
-
-bool tpsemantic(TemplateParameter *tp, Scope *sc, TemplateParameters *parameters);
-RootObject *aliasParameterSemantic(Loc loc, Scope *sc, RootObject *o, TemplateParameters *parameters);
-void templateInstanceSemantic(TemplateInstance *tempinst, Scope *sc, Expressions *fargs);
class Tuple : public RootObject
{
@@ -45,9 +34,9 @@ public:
Objects objects;
// kludge for template.isType()
- int dyncast() const { return DYNCAST_TUPLE; }
+ DYNCAST dyncast() const { return DYNCAST_TUPLE; }
- const char *toChars() { return objects.toChars(); }
+ const char *toChars() const { return objects.toChars(); }
};
struct TemplatePrevious
@@ -78,38 +67,29 @@ public:
bool ismixin; // template declaration is only to be used as a mixin
bool isstatic; // this is static template declaration
bool isTrivialAliasSeq; // matches `template AliasSeq(T...) { alias AliasSeq = T; }
- bool isTrivialAlias; // matches `template Alias(T) { alias Alias = T; }
- Prot protection;
+ bool isTrivialAlias; // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }`
+ bool deprecated_; // this template declaration is deprecated
+ Visibility visibility;
int inuse; // for recursive expansion detection
TemplatePrevious *previous; // threaded list of previous instantiation attempts on stack
- TemplateDeclaration(Loc loc, Identifier *id, TemplateParameters *parameters,
- Expression *constraint, Dsymbols *decldefs, bool ismixin = false, bool literal = false);
- Dsymbol *syntaxCopy(Dsymbol *);
+ TemplateDeclaration *syntaxCopy(Dsymbol *);
bool overloadInsert(Dsymbol *s);
bool hasStaticCtorOrDtor();
const char *kind() const;
- const char *toChars();
-
- Prot prot();
+ const char *toChars() const;
- bool evaluateConstraint(TemplateInstance *ti, Scope *sc, Scope *paramscope, Objects *dedtypes, FuncDeclaration *fd);
+ Visibility visible();
- MATCH matchWithInstance(Scope *sc, TemplateInstance *ti, Objects *atypes, Expressions *fargs, int flag);
MATCH leastAsSpecialized(Scope *sc, TemplateDeclaration *td2, Expressions *fargs);
-
- MATCH deduceFunctionTemplateMatch(TemplateInstance *ti, Scope *sc, FuncDeclaration *&fd, Type *tthis, Expressions *fargs);
RootObject *declareParameter(Scope *sc, TemplateParameter *tp, RootObject *o);
- FuncDeclaration *doHeaderInstantiation(TemplateInstance *ti, Scope *sc, FuncDeclaration *fd, Type *tthis, Expressions *fargs);
- TemplateInstance *findExistingInstance(TemplateInstance *tithis, Expressions *fargs);
- TemplateInstance *addInstance(TemplateInstance *ti);
- void removeInstance(TemplateInstance *handle);
TemplateDeclaration *isTemplateDeclaration() { return this; }
TemplateTupleParameter *isVariadic();
- bool isOverloadable();
+ bool isDeprecated() const;
+ bool isOverloadable() const;
void accept(Visitor *v) { v->visit(this); }
};
@@ -141,8 +121,6 @@ public:
*/
bool dependent;
- TemplateParameter(Loc loc, Identifier *ident);
-
virtual TemplateTypeParameter *isTemplateTypeParameter();
virtual TemplateValueParameter *isTemplateValueParameter();
virtual TemplateAliasParameter *isTemplateAliasParameter();
@@ -156,14 +134,9 @@ public:
virtual RootObject *defaultArg(Loc instLoc, Scope *sc) = 0;
virtual bool hasDefaultArg() = 0;
- /* Match actual argument against parameter.
- */
- virtual MATCH matchArg(Loc instLoc, Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam);
- virtual MATCH matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam) = 0;
-
/* Create dummy argument based on parameter.
*/
- virtual void *dummyArg() = 0;
+ virtual RootObject *dummyArg() = 0;
void accept(Visitor *v) { v->visit(this); }
};
@@ -172,24 +145,18 @@ public:
*/
class TemplateTypeParameter : public TemplateParameter
{
- using TemplateParameter::matchArg;
public:
Type *specType; // type parameter: if !=NULL, this is the type specialization
Type *defaultType;
- static Type *tdummy;
-
- TemplateTypeParameter(Loc loc, Identifier *ident, Type *specType, Type *defaultType);
-
TemplateTypeParameter *isTemplateTypeParameter();
- TemplateParameter *syntaxCopy();
+ TemplateTypeParameter *syntaxCopy();
bool declareParameter(Scope *sc);
void print(RootObject *oarg, RootObject *oded);
RootObject *specialization();
RootObject *defaultArg(Loc instLoc, Scope *sc);
bool hasDefaultArg();
- MATCH matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam);
- void *dummyArg();
+ RootObject *dummyArg();
void accept(Visitor *v) { v->visit(this); }
};
@@ -199,10 +166,8 @@ public:
class TemplateThisParameter : public TemplateTypeParameter
{
public:
- TemplateThisParameter(Loc loc, Identifier *ident, Type *specType, Type *defaultType);
-
TemplateThisParameter *isTemplateThisParameter();
- TemplateParameter *syntaxCopy();
+ TemplateThisParameter *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -211,25 +176,19 @@ public:
*/
class TemplateValueParameter : public TemplateParameter
{
- using TemplateParameter::matchArg;
public:
Type *valType;
Expression *specValue;
Expression *defaultValue;
- static AA *edummies;
-
- TemplateValueParameter(Loc loc, Identifier *ident, Type *valType, Expression *specValue, Expression *defaultValue);
-
TemplateValueParameter *isTemplateValueParameter();
- TemplateParameter *syntaxCopy();
+ TemplateValueParameter *syntaxCopy();
bool declareParameter(Scope *sc);
void print(RootObject *oarg, RootObject *oded);
RootObject *specialization();
RootObject *defaultArg(Loc instLoc, Scope *sc);
bool hasDefaultArg();
- MATCH matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam);
- void *dummyArg();
+ RootObject *dummyArg();
void accept(Visitor *v) { v->visit(this); }
};
@@ -238,25 +197,19 @@ public:
*/
class TemplateAliasParameter : public TemplateParameter
{
- using TemplateParameter::matchArg;
public:
Type *specType;
RootObject *specAlias;
RootObject *defaultAlias;
- static Dsymbol *sdummy;
-
- TemplateAliasParameter(Loc loc, Identifier *ident, Type *specType, RootObject *specAlias, RootObject *defaultAlias);
-
TemplateAliasParameter *isTemplateAliasParameter();
- TemplateParameter *syntaxCopy();
+ TemplateAliasParameter *syntaxCopy();
bool declareParameter(Scope *sc);
void print(RootObject *oarg, RootObject *oded);
RootObject *specialization();
RootObject *defaultArg(Loc instLoc, Scope *sc);
bool hasDefaultArg();
- MATCH matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam);
- void *dummyArg();
+ RootObject *dummyArg();
void accept(Visitor *v) { v->visit(this); }
};
@@ -266,18 +219,14 @@ public:
class TemplateTupleParameter : public TemplateParameter
{
public:
- TemplateTupleParameter(Loc loc, Identifier *ident);
-
TemplateTupleParameter *isTemplateTupleParameter();
- TemplateParameter *syntaxCopy();
+ TemplateTupleParameter *syntaxCopy();
bool declareParameter(Scope *sc);
void print(RootObject *oarg, RootObject *oded);
RootObject *specialization();
RootObject *defaultArg(Loc instLoc, Scope *sc);
bool hasDefaultArg();
- MATCH matchArg(Loc loc, Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam);
- MATCH matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam);
- void *dummyArg();
+ RootObject *dummyArg();
void accept(Visitor *v) { v->visit(this); }
};
@@ -300,16 +249,14 @@ public:
// [int, char, 100]
Objects tdtypes;
+ // Modules imported by this template instance
+ Modules importedModules;
+
Dsymbol *tempdecl; // referenced by foo.bar.abc
Dsymbol *enclosing; // if referencing local symbols, this is the context
Dsymbol *aliasdecl; // !=NULL if instance is an alias for its sole member
TemplateInstance *inst; // refer to existing instance
ScopeDsymbol *argsym; // argument symbol table
- int inuse; // for recursive expansion detection
- int nest; // for recursive pretty printing detection
- bool semantictiargsdone; // has semanticTiargs() been done?
- bool havetempdecl; // if used second constructor
- bool gagged; // if the instantiation is done with error gagging
hash_t hash; // cached result of toHash()
Expressions *fargs; // for function template, these are the function arguments
@@ -323,37 +270,22 @@ public:
TemplateInstance *tnext; // non-first instantiated instances
Module *minst; // the top module that instantiated this instance
- TemplateInstance(Loc loc, Identifier *temp_id);
- TemplateInstance(Loc loc, TemplateDeclaration *tempdecl, Objects *tiargs);
- static Objects *arraySyntaxCopy(Objects *objs);
- Dsymbol *syntaxCopy(Dsymbol *);
+private:
+ unsigned short _nest; // for recursive pretty printing detection, 3 MSBs reserved for flags
+public:
+ unsigned char inuse; // for recursive expansion detection
+
+ TemplateInstance *syntaxCopy(Dsymbol *);
Dsymbol *toAlias(); // resolve real symbol
const char *kind() const;
bool oneMember(Dsymbol **ps, Identifier *ident);
- const char *toChars();
+ const char *toChars() const;
const char* toPrettyCharsHelper();
- void printInstantiationTrace();
Identifier *getIdent();
- int compare(RootObject *o);
hash_t toHash();
bool needsCodegen();
- // Internal
- bool findTempDecl(Scope *sc, WithScopeSymbol **pwithsym);
- bool updateTempDecl(Scope *sc, Dsymbol *s);
- static bool semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags);
- bool semanticTiargs(Scope *sc);
- bool findBestMatch(Scope *sc, Expressions *fargs);
- bool needsTypeInference(Scope *sc, int flag = 0);
- bool hasNestedArgs(Objects *tiargs, bool isstatic);
- Dsymbols *appendToModuleMember();
- void declareParameters(Scope *sc);
- Identifier *genIdent(Objects *args);
- void expandMembers(Scope *sc);
- void tryExpandMembers(Scope *sc);
- void trySemantic3(Scope *sc2);
-
TemplateInstance *isTemplateInstance() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
@@ -363,16 +295,12 @@ class TemplateMixin : public TemplateInstance
public:
TypeQualified *tqual;
- TemplateMixin(Loc loc, Identifier *ident, TypeQualified *tqual, Objects *tiargs);
- Dsymbol *syntaxCopy(Dsymbol *s);
+ TemplateMixin *syntaxCopy(Dsymbol *s);
const char *kind() const;
bool oneMember(Dsymbol **ps, Identifier *ident);
- int apply(Dsymbol_apply_ft_t fp, void *param);
bool hasPointers();
- void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
- const char *toChars();
-
- bool findTempDecl(Scope *sc);
+ void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion);
+ const char *toChars() const;
TemplateMixin *isTemplateMixin() { return this; }
void accept(Visitor *v) { v->visit(this); }
@@ -383,9 +311,5 @@ Dsymbol *isDsymbol(RootObject *o);
Type *isType(RootObject *o);
Tuple *isTuple(RootObject *o);
Parameter *isParameter(RootObject *o);
-bool arrayObjectIsError(Objects *args);
-bool isError(RootObject *o);
-Type *getType(RootObject *o);
-Dsymbol *getDsymbol(RootObject *o);
-
-RootObject *objectSyntaxCopy(RootObject *o);
+TemplateParameter *isTemplateParameter(RootObject *o);
+bool isError(const RootObject *const o);
diff --git a/gcc/d/dmd/templateparamsem.c b/gcc/d/dmd/templateparamsem.c
deleted file mode 100644
index d3e9b2390e9..00000000000
--- a/gcc/d/dmd/templateparamsem.c
+++ /dev/null
@@ -1,116 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "template.h"
-#include "mtype.h"
-#include "scope.h"
-#include "visitor.h"
-
-bool reliesOnTident(Type *t, TemplateParameters *tparams = NULL, size_t iStart = 0);
-
-class TemplateParameterSemanticVisitor : public Visitor
-{
-public:
- Scope *sc;
- TemplateParameters *parameters;
- bool result;
-
- TemplateParameterSemanticVisitor(Scope *sc, TemplateParameters *parameters)
- {
- this->sc = sc;
- this->parameters = parameters;
- this->result = false;
- }
-
- void visit(TemplateTypeParameter *ttp)
- {
- //printf("TemplateTypeParameter::semantic('%s')\n", ident->toChars());
- if (ttp->specType && !reliesOnTident(ttp->specType, parameters))
- {
- ttp->specType = typeSemantic(ttp->specType, ttp->loc, sc);
- }
- result = !(ttp->specType && isError(ttp->specType));
- }
-
- void visit(TemplateValueParameter *tvp)
- {
- tvp->valType = typeSemantic(tvp->valType, tvp->loc, sc);
-
- result = !isError(tvp->valType);
- }
-
- void visit(TemplateAliasParameter *tap)
- {
- if (tap->specType && !reliesOnTident(tap->specType, parameters))
- {
- tap->specType = typeSemantic(tap->specType, tap->loc, sc);
- }
- tap->specAlias = aliasParameterSemantic(tap->loc, sc, tap->specAlias, parameters);
- result = !(tap->specType && isError(tap->specType)) &&
- !(tap->specAlias && isError(tap->specAlias));
- }
-
- void visit(TemplateTupleParameter *)
- {
- result = true;
- }
-};
-
-/************************************************
- * Performs semantic on TemplateParameter AST nodes.
- *
- * Params:
- * tp = element of `parameters` to be semantically analyzed
- * sc = context
- * parameters = array of `TemplateParameters` supplied to the `TemplateDeclaration`
- * Returns:
- * `true` if no errors
- */
-bool tpsemantic(TemplateParameter *tp, Scope *sc, TemplateParameters *parameters)
-{
- TemplateParameterSemanticVisitor v(sc, parameters);
- tp->accept(&v);
- return v.result;
-}
-
-/***********************************************
- * Support function for performing semantic analysis on `TemplateAliasParameter`.
- *
- * Params:
- * loc = location (for error messages)
- * sc = context
- * o = object to run semantic() on, the `TemplateAliasParameter`s `specAlias` or `defaultAlias`
- * parameters = array of `TemplateParameters` supplied to the `TemplateDeclaration`
- * Returns:
- * object resulting from running `semantic` on `o`
- */
-RootObject *aliasParameterSemantic(Loc loc, Scope *sc, RootObject *o, TemplateParameters *parameters)
-{
- if (o)
- {
- Expression *ea = isExpression(o);
- Type *ta = isType(o);
- if (ta && (!parameters || !reliesOnTident(ta, parameters)))
- {
- Dsymbol *s = ta->toDsymbol(sc);
- if (s)
- o = s;
- else
- o = typeSemantic(ta, loc, sc);
- }
- else if (ea)
- {
- sc = sc->startCTFE();
- ea = expressionSemantic(ea, sc);
- sc = sc->endCTFE();
- o = ea->ctfeInterpret();
- }
- }
- return o;
-}
diff --git a/gcc/d/dmd/templateparamsem.d b/gcc/d/dmd/templateparamsem.d
new file mode 100644
index 00000000000..620492fc0c7
--- /dev/null
+++ b/gcc/d/dmd/templateparamsem.d
@@ -0,0 +1,190 @@
+/**
+ * Semantic analysis of template parameters.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/templateparamsem.d, _templateparamsem.d)
+ * Documentation: https://dlang.org/phobos/dmd_templateparamsem.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/templateparamsem.d
+ */
+
+module dmd.templateparamsem;
+
+import dmd.arraytypes;
+import dmd.dsymbol;
+import dmd.dscope;
+import dmd.dtemplate;
+import dmd.globals;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.root.rootobject;
+import dmd.mtype;
+import dmd.typesem;
+import dmd.visitor;
+
+/************************************************
+ * Performs semantic on TemplateParameter AST nodes.
+ *
+ * Params:
+ * tp = element of `parameters` to be semantically analyzed
+ * sc = context
+ * parameters = array of `TemplateParameters` supplied to the `TemplateDeclaration`
+ * Returns:
+ * `true` if no errors
+ */
+extern(C++) bool tpsemantic(TemplateParameter tp, Scope* sc, TemplateParameters* parameters)
+{
+ scope v = new TemplateParameterSemanticVisitor(sc, parameters);
+ tp.accept(v);
+ return v.result;
+}
+
+
+private extern (C++) final class TemplateParameterSemanticVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+
+ Scope* sc;
+ TemplateParameters* parameters;
+ bool result;
+
+ this(Scope* sc, TemplateParameters* parameters)
+ {
+ this.sc = sc;
+ this.parameters = parameters;
+ }
+
+ override void visit(TemplateTypeParameter ttp)
+ {
+ //printf("TemplateTypeParameter.semantic('%s')\n", ident.toChars());
+ if (ttp.specType && !reliesOnTident(ttp.specType, parameters))
+ {
+ ttp.specType = ttp.specType.typeSemantic(ttp.loc, sc);
+ }
+ version (none)
+ {
+ // Don't do semantic() until instantiation
+ if (ttp.defaultType)
+ {
+ ttp.defaultType = ttp.defaultType.typeSemantic(ttp.loc, sc);
+ }
+ }
+ result = !(ttp.specType && isError(ttp.specType));
+ }
+
+ override void visit(TemplateValueParameter tvp)
+ {
+ tvp.valType = tvp.valType.typeSemantic(tvp.loc, sc);
+ version (none)
+ {
+ // defer semantic analysis to arg match
+ if (tvp.specValue)
+ {
+ Expression e = tvp.specValue;
+ sc = sc.startCTFE();
+ e = e.semantic(sc);
+ sc = sc.endCTFE();
+ e = e.implicitCastTo(sc, tvp.valType);
+ e = e.ctfeInterpret();
+ if (e.op == TOK.int64 || e.op == TOK.float64 ||
+ e.op == TOK.complex80 || e.op == TOK.null_ || e.op == TOK.string_)
+ tvp.specValue = e;
+ }
+
+ if (tvp.defaultValue)
+ {
+ Expression e = defaultValue;
+ sc = sc.startCTFE();
+ e = e.semantic(sc);
+ sc = sc.endCTFE();
+ e = e.implicitCastTo(sc, tvp.valType);
+ e = e.ctfeInterpret();
+ if (e.op == TOK.int64)
+ tvp.defaultValue = e;
+ }
+ }
+ result = !isError(tvp.valType);
+ }
+
+ override void visit(TemplateAliasParameter tap)
+ {
+ if (tap.specType && !reliesOnTident(tap.specType, parameters))
+ {
+ tap.specType = tap.specType.typeSemantic(tap.loc, sc);
+ }
+ tap.specAlias = aliasParameterSemantic(tap.loc, sc, tap.specAlias, parameters);
+ version (none)
+ {
+ // Don't do semantic() until instantiation
+ if (tap.defaultAlias)
+ tap.defaultAlias = tap.defaultAlias.semantic(tap.loc, sc);
+ }
+ result = !(tap.specType && isError(tap.specType)) && !(tap.specAlias && isError(tap.specAlias));
+ }
+
+ override void visit(TemplateTupleParameter ttp)
+ {
+ result = true;
+ }
+}
+
+/***********************************************
+ * Support function for performing semantic analysis on `TemplateAliasParameter`.
+ *
+ * Params:
+ * loc = location (for error messages)
+ * sc = context
+ * o = object to run semantic() on, the `TemplateAliasParameter`s `specAlias` or `defaultAlias`
+ * parameters = array of `TemplateParameters` supplied to the `TemplateDeclaration`
+ * Returns:
+ * object resulting from running `semantic` on `o`
+ */
+RootObject aliasParameterSemantic(Loc loc, Scope* sc, RootObject o, TemplateParameters* parameters)
+{
+ if (!o)
+ return null;
+
+ Expression ea = isExpression(o);
+ RootObject eaCTFE()
+ {
+ sc = sc.startCTFE();
+ ea = ea.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ return ea.ctfeInterpret();
+ }
+ Type ta = isType(o);
+ if (ta && (!parameters || !reliesOnTident(ta, parameters)))
+ {
+ Dsymbol s = ta.toDsymbol(sc);
+ if (s)
+ return s;
+ else if (TypeInstance ti = ta.isTypeInstance())
+ {
+ Type t;
+ const errors = global.errors;
+ ta.resolve(loc, sc, ea, t, s);
+ // if we had an error evaluating the symbol, suppress further errors
+ if (!t && errors != global.errors)
+ return Type.terror;
+ // We might have something that looks like a type
+ // but is actually an expression or a dsymbol
+ // see https://issues.dlang.org/show_bug.cgi?id=16472
+ if (t)
+ return t.typeSemantic(loc, sc);
+ else if (ea)
+ {
+ return eaCTFE();
+ }
+ else if (s)
+ return s;
+ else
+ assert(0);
+ }
+ else
+ return ta.typeSemantic(loc, sc);
+ }
+ else if (ea)
+ return eaCTFE();
+ return o;
+}
diff --git a/gcc/d/dmd/tokens.c b/gcc/d/dmd/tokens.c
deleted file mode 100644
index eb06beed9b6..00000000000
--- a/gcc/d/dmd/tokens.c
+++ /dev/null
@@ -1,476 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/lexer.c
- */
-
-#include "root/dsystem.h"
-
-#include "tokens.h"
-#include "root/rmem.h"
-#include "root/outbuffer.h"
-#include "id.h"
-#include "identifier.h"
-#include "utf.h"
-
-/************************* Token **********************************************/
-
-Token *Token::freelist = NULL;
-
-const char *Token::tochars[TOKMAX];
-
-Token *Token::alloc()
-{
- if (Token::freelist)
- {
- Token *t = freelist;
- freelist = t->next;
- t->next = NULL;
- return t;
- }
-
- return new Token();
-}
-
-void Token::free()
-{
- next = freelist;
- freelist = this;
-}
-
-const char *Token::toChars() const
-{
- static char buffer[3 + 3 * sizeof(floatvalue) + 1];
-
- const char *p = &buffer[0];
- switch (value)
- {
- case TOKint32v:
- sprintf(&buffer[0],"%d",(d_int32)int64value);
- break;
-
- case TOKuns32v:
- case TOKcharv:
- case TOKwcharv:
- case TOKdcharv:
- sprintf(&buffer[0],"%uU",(d_uns32)uns64value);
- break;
-
- case TOKint64v:
- sprintf(&buffer[0],"%lldL",(longlong)int64value);
- break;
-
- case TOKuns64v:
- sprintf(&buffer[0],"%lluUL",(ulonglong)uns64value);
- break;
-
- case TOKfloat32v:
- CTFloat::sprint(&buffer[0], 'g', floatvalue);
- strcat(&buffer[0], "f");
- break;
-
- case TOKfloat64v:
- CTFloat::sprint(&buffer[0], 'g', floatvalue);
- break;
-
- case TOKfloat80v:
- CTFloat::sprint(&buffer[0], 'g', floatvalue);
- strcat(&buffer[0], "L");
- break;
-
- case TOKimaginary32v:
- CTFloat::sprint(&buffer[0], 'g', floatvalue);
- strcat(&buffer[0], "fi");
- break;
-
- case TOKimaginary64v:
- CTFloat::sprint(&buffer[0], 'g', floatvalue);
- strcat(&buffer[0], "i");
- break;
-
- case TOKimaginary80v:
- CTFloat::sprint(&buffer[0], 'g', floatvalue);
- strcat(&buffer[0], "Li");
- break;
-
- case TOKstring:
- {
- OutBuffer buf;
- buf.writeByte('"');
- for (size_t i = 0; i < len; )
- {
- unsigned c;
- utf_decodeChar((utf8_t *)ustring, len, &i, &c);
- switch (c)
- {
- case 0:
- break;
-
- case '"':
- case '\\':
- buf.writeByte('\\');
- /* fall through */
- default:
- if (c <= 0x7F)
- {
- if (isprint(c))
- buf.writeByte(c);
- else
- buf.printf("\\x%02x", c);
- }
- else if (c <= 0xFFFF)
- buf.printf("\\u%04x", c);
- else
- buf.printf("\\U%08x", c);
- continue;
- }
- break;
- }
- buf.writeByte('"');
- if (postfix)
- buf.writeByte(postfix);
- p = buf.extractChars();
- }
- break;
-
- case TOKxstring:
- {
- OutBuffer buf;
- buf.writeByte('x');
- buf.writeByte('"');
- for (size_t i = 0; i < len; i++)
- {
- if (i)
- buf.writeByte(' ');
- buf.printf("%02x", ustring[i]);
- }
- buf.writeByte('"');
- if (postfix)
- buf.writeByte(postfix);
- buf.writeByte(0);
- p = (char *)buf.extractData();
- break;
- }
-
- case TOKidentifier:
- case TOKenum:
- case TOKstruct:
- case TOKimport:
- case TOKwchar: case TOKdchar:
- case TOKbool: case TOKchar:
- case TOKint8: case TOKuns8:
- case TOKint16: case TOKuns16:
- case TOKint32: case TOKuns32:
- case TOKint64: case TOKuns64:
- case TOKint128: case TOKuns128:
- case TOKfloat32: case TOKfloat64: case TOKfloat80:
- case TOKimaginary32: case TOKimaginary64: case TOKimaginary80:
- case TOKcomplex32: case TOKcomplex64: case TOKcomplex80:
- case TOKvoid:
- p = ident->toChars();
- break;
-
- default:
- p = toChars(value);
- break;
- }
- return p;
-}
-
-const char *Token::toChars(TOK value)
-{
- static char buffer[3 + 3 * sizeof(value) + 1];
-
- const char *p = tochars[value];
- if (!p)
- {
- sprintf(&buffer[0],"TOK%d",value);
- p = &buffer[0];
- }
- return p;
-}
-
-/****************************************
- */
-
-struct Keyword
-{
- const char *name;
- TOK value;
-};
-
-static size_t nkeywords;
-static Keyword keywords[] =
-{
- { "this", TOKthis },
- { "super", TOKsuper },
- { "assert", TOKassert },
- { "null", TOKnull },
- { "true", TOKtrue },
- { "false", TOKfalse },
- { "cast", TOKcast },
- { "new", TOKnew },
- { "delete", TOKdelete },
- { "throw", TOKthrow },
- { "module", TOKmodule },
- { "pragma", TOKpragma },
- { "typeof", TOKtypeof },
- { "typeid", TOKtypeid },
-
- { "template", TOKtemplate },
-
- { "void", TOKvoid },
- { "byte", TOKint8 },
- { "ubyte", TOKuns8 },
- { "short", TOKint16 },
- { "ushort", TOKuns16 },
- { "int", TOKint32 },
- { "uint", TOKuns32 },
- { "long", TOKint64 },
- { "ulong", TOKuns64 },
- { "cent", TOKint128, },
- { "ucent", TOKuns128, },
- { "float", TOKfloat32 },
- { "double", TOKfloat64 },
- { "real", TOKfloat80 },
-
- { "bool", TOKbool },
- { "char", TOKchar },
- { "wchar", TOKwchar },
- { "dchar", TOKdchar },
-
- { "ifloat", TOKimaginary32 },
- { "idouble", TOKimaginary64 },
- { "ireal", TOKimaginary80 },
-
- { "cfloat", TOKcomplex32 },
- { "cdouble", TOKcomplex64 },
- { "creal", TOKcomplex80 },
-
- { "delegate", TOKdelegate },
- { "function", TOKfunction },
-
- { "is", TOKis },
- { "if", TOKif },
- { "else", TOKelse },
- { "while", TOKwhile },
- { "for", TOKfor },
- { "do", TOKdo },
- { "switch", TOKswitch },
- { "case", TOKcase },
- { "default", TOKdefault },
- { "break", TOKbreak },
- { "continue", TOKcontinue },
- { "synchronized", TOKsynchronized },
- { "return", TOKreturn },
- { "goto", TOKgoto },
- { "try", TOKtry },
- { "catch", TOKcatch },
- { "finally", TOKfinally },
- { "with", TOKwith },
- { "asm", TOKasm },
- { "foreach", TOKforeach },
- { "foreach_reverse", TOKforeach_reverse },
- { "scope", TOKscope },
-
- { "struct", TOKstruct },
- { "class", TOKclass },
- { "interface", TOKinterface },
- { "union", TOKunion },
- { "enum", TOKenum },
- { "import", TOKimport },
- { "mixin", TOKmixin },
- { "static", TOKstatic },
- { "final", TOKfinal },
- { "const", TOKconst },
- { "alias", TOKalias },
- { "override", TOKoverride },
- { "abstract", TOKabstract },
- { "debug", TOKdebug },
- { "deprecated", TOKdeprecated },
- { "in", TOKin },
- { "out", TOKout },
- { "inout", TOKinout },
- { "lazy", TOKlazy },
- { "auto", TOKauto },
-
- { "align", TOKalign },
- { "extern", TOKextern },
- { "private", TOKprivate },
- { "package", TOKpackage },
- { "protected", TOKprotected },
- { "public", TOKpublic },
- { "export", TOKexport },
-
- { "invariant", TOKinvariant },
- { "unittest", TOKunittest },
- { "version", TOKversion },
-
- { "__argTypes", TOKargTypes },
- { "__parameters", TOKparameters },
- { "ref", TOKref },
- { "macro", TOKmacro },
-
- { "pure", TOKpure },
- { "nothrow", TOKnothrow },
- { "__gshared", TOKgshared },
- { "__traits", TOKtraits },
- { "__vector", TOKvector },
- { "__overloadset", TOKoverloadset },
- { "__FILE__", TOKfile },
- { "__FILE_FULL_PATH__", TOKfilefullpath },
- { "__LINE__", TOKline },
- { "__MODULE__", TOKmodulestring },
- { "__FUNCTION__", TOKfuncstring },
- { "__PRETTY_FUNCTION__", TOKprettyfunc },
- { "shared", TOKshared },
- { "immutable", TOKimmutable },
- { NULL, TOKreserved }
-};
-
-int Token::isKeyword()
-{
- for (size_t u = 0; u < nkeywords; u++)
- {
- if (keywords[u].value == value)
- return 1;
- }
- return 0;
-}
-
-struct TokenInitializer
-{
- TokenInitializer();
-};
-
-static TokenInitializer tokeninitializer;
-
-TokenInitializer::TokenInitializer()
-{
- Identifier::initTable();
- for (nkeywords = 0; keywords[nkeywords].name; nkeywords++)
- {
- //printf("keyword[%d] = '%s'\n",u, keywords[u].name);
- const char *s = keywords[nkeywords].name;
- size_t len = strlen(s);
- TOK v = keywords[nkeywords].value;
- Identifier::idPool(s, len, v);
-
- //printf("tochars[%d] = '%s'\n",v, s);
- Token::tochars[v] = s;
- }
-
- Token::tochars[TOKeof] = "EOF";
- Token::tochars[TOKlcurly] = "{";
- Token::tochars[TOKrcurly] = "}";
- Token::tochars[TOKlparen] = "(";
- Token::tochars[TOKrparen] = ")";
- Token::tochars[TOKlbracket] = "[";
- Token::tochars[TOKrbracket] = "]";
- Token::tochars[TOKsemicolon] = ";";
- Token::tochars[TOKcolon] = ":";
- Token::tochars[TOKcomma] = ",";
- Token::tochars[TOKdot] = ".";
- Token::tochars[TOKxor] = "^";
- Token::tochars[TOKxorass] = "^=";
- Token::tochars[TOKassign] = "=";
- Token::tochars[TOKconstruct] = "=";
- Token::tochars[TOKblit] = "=";
- Token::tochars[TOKlt] = "<";
- Token::tochars[TOKgt] = ">";
- Token::tochars[TOKle] = "<=";
- Token::tochars[TOKge] = ">=";
- Token::tochars[TOKequal] = "==";
- Token::tochars[TOKnotequal] = "!=";
- Token::tochars[TOKnotidentity] = "!is";
-
- Token::tochars[TOKunord] = "!<>=";
- Token::tochars[TOKue] = "!<>";
- Token::tochars[TOKlg] = "<>";
- Token::tochars[TOKleg] = "<>=";
- Token::tochars[TOKule] = "!>";
- Token::tochars[TOKul] = "!>=";
- Token::tochars[TOKuge] = "!<";
- Token::tochars[TOKug] = "!<=";
-
- Token::tochars[TOKnot] = "!";
- Token::tochars[TOKshl] = "<<";
- Token::tochars[TOKshr] = ">>";
- Token::tochars[TOKushr] = ">>>";
- Token::tochars[TOKadd] = "+";
- Token::tochars[TOKmin] = "-";
- Token::tochars[TOKmul] = "*";
- Token::tochars[TOKdiv] = "/";
- Token::tochars[TOKmod] = "%";
- Token::tochars[TOKslice] = "..";
- Token::tochars[TOKdotdotdot] = "...";
- Token::tochars[TOKand] = "&";
- Token::tochars[TOKandand] = "&&";
- Token::tochars[TOKor] = "|";
- Token::tochars[TOKoror] = "||";
- Token::tochars[TOKarray] = "[]";
- Token::tochars[TOKindex] = "[i]";
- Token::tochars[TOKaddress] = "&";
- Token::tochars[TOKstar] = "*";
- Token::tochars[TOKtilde] = "~";
- Token::tochars[TOKdollar] = "$";
- Token::tochars[TOKcast] = "cast";
- Token::tochars[TOKplusplus] = "++";
- Token::tochars[TOKminusminus] = "--";
- Token::tochars[TOKpreplusplus] = "++";
- Token::tochars[TOKpreminusminus] = "--";
- Token::tochars[TOKtype] = "type";
- Token::tochars[TOKquestion] = "?";
- Token::tochars[TOKneg] = "-";
- Token::tochars[TOKuadd] = "+";
- Token::tochars[TOKvar] = "var";
- Token::tochars[TOKaddass] = "+=";
- Token::tochars[TOKminass] = "-=";
- Token::tochars[TOKmulass] = "*=";
- Token::tochars[TOKdivass] = "/=";
- Token::tochars[TOKmodass] = "%=";
- Token::tochars[TOKshlass] = "<<=";
- Token::tochars[TOKshrass] = ">>=";
- Token::tochars[TOKushrass] = ">>>=";
- Token::tochars[TOKandass] = "&=";
- Token::tochars[TOKorass] = "|=";
- Token::tochars[TOKcatass] = "~=";
- Token::tochars[TOKcat] = "~";
- Token::tochars[TOKcall] = "call";
- Token::tochars[TOKidentity] = "is";
- Token::tochars[TOKnotidentity] = "!is";
-
- Token::tochars[TOKorass] = "|=";
- Token::tochars[TOKidentifier] = "identifier";
- Token::tochars[TOKat] = "@";
- Token::tochars[TOKpow] = "^^";
- Token::tochars[TOKpowass] = "^^=";
- Token::tochars[TOKgoesto] = "=>";
- Token::tochars[TOKpound] = "#";
-
- // For debugging
- Token::tochars[TOKerror] = "error";
- Token::tochars[TOKdotid] = "dotid";
- Token::tochars[TOKdottd] = "dottd";
- Token::tochars[TOKdotti] = "dotti";
- Token::tochars[TOKdotvar] = "dotvar";
- Token::tochars[TOKdottype] = "dottype";
- Token::tochars[TOKsymoff] = "symoff";
- Token::tochars[TOKarraylength] = "arraylength";
- Token::tochars[TOKarrayliteral] = "arrayliteral";
- Token::tochars[TOKassocarrayliteral] = "assocarrayliteral";
- Token::tochars[TOKstructliteral] = "structliteral";
- Token::tochars[TOKstring] = "string";
- Token::tochars[TOKdsymbol] = "symbol";
- Token::tochars[TOKtuple] = "tuple";
- Token::tochars[TOKdeclaration] = "declaration";
- Token::tochars[TOKon_scope_exit] = "scope(exit)";
- Token::tochars[TOKon_scope_success] = "scope(success)";
- Token::tochars[TOKon_scope_failure] = "scope(failure)";
- Token::tochars[TOKdelegateptr] = "delegateptr";
- Token::tochars[TOKvectorarray] = "vectorarray";
-}
diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d
new file mode 100644
index 00000000000..7680fb8500b
--- /dev/null
+++ b/gcc/d/dmd/tokens.d
@@ -0,0 +1,1022 @@
+/**
+ * Defines lexical tokens.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/lex.html#tokens, Tokens)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/tokens.d, _tokens.d)
+ * Documentation: https://dlang.org/phobos/dmd_tokens.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/tokens.d
+ */
+
+module dmd.tokens;
+
+import core.stdc.ctype;
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.globals;
+import dmd.identifier;
+import dmd.root.ctfloat;
+import dmd.root.outbuffer;
+import dmd.root.rmem;
+import dmd.utf;
+
+enum TOK : ushort
+{
+ reserved,
+
+ // Other
+ leftParenthesis,
+ rightParenthesis,
+ leftBracket,
+ rightBracket,
+ leftCurly,
+ rightCurly,
+ colon,
+ negate,
+ semicolon,
+ dotDotDot,
+ endOfFile,
+ cast_,
+ null_,
+ assert_,
+ true_,
+ false_,
+ array,
+ call,
+ address,
+ type,
+ throw_,
+ new_,
+ delete_,
+ star,
+ symbolOffset,
+ variable,
+ dotVariable,
+ dotIdentifier,
+ dotTemplateInstance,
+ dotType,
+ slice,
+ arrayLength,
+ version_,
+ module_,
+ dollar,
+ template_,
+ dotTemplateDeclaration,
+ declaration,
+ typeof_,
+ pragma_,
+ dSymbol,
+ typeid_,
+ uadd,
+ remove,
+ newAnonymousClass,
+ comment,
+ arrayLiteral,
+ assocArrayLiteral,
+ structLiteral,
+ classReference,
+ thrownException,
+ delegatePointer,
+ delegateFunctionPointer,
+
+ // Operators
+ lessThan = 54,
+ greaterThan,
+ lessOrEqual,
+ greaterOrEqual,
+ equal,
+ notEqual,
+ identity,
+ notIdentity,
+ index,
+ is_,
+
+ leftShift = 64,
+ rightShift,
+ leftShiftAssign,
+ rightShiftAssign,
+ unsignedRightShift,
+ unsignedRightShiftAssign,
+ concatenate,
+ concatenateAssign, // ~=
+ concatenateElemAssign,
+ concatenateDcharAssign,
+ add,
+ min,
+ addAssign,
+ minAssign,
+ mul,
+ div,
+ mod,
+ mulAssign,
+ divAssign,
+ modAssign,
+ and,
+ or,
+ xor,
+ andAssign,
+ orAssign,
+ xorAssign,
+ assign,
+ not,
+ tilde,
+ plusPlus,
+ minusMinus,
+ construct,
+ blit,
+ dot,
+ comma,
+ question,
+ andAnd,
+ orOr,
+ prePlusPlus,
+ preMinusMinus,
+
+ // Numeric literals
+ int32Literal = 104,
+ uns32Literal,
+ int64Literal,
+ uns64Literal,
+ int128Literal,
+ uns128Literal,
+ float32Literal,
+ float64Literal,
+ float80Literal,
+ imaginary32Literal,
+ imaginary64Literal,
+ imaginary80Literal,
+
+ // Char constants
+ charLiteral = 116,
+ wcharLiteral,
+ dcharLiteral,
+
+ // Leaf operators
+ identifier = 119,
+ string_,
+ hexadecimalString,
+ this_,
+ super_,
+ halt,
+ tuple,
+ error,
+
+ // Basic types
+ void_ = 127,
+ int8,
+ uns8,
+ int16,
+ uns16,
+ int32,
+ uns32,
+ int64,
+ uns64,
+ int128,
+ uns128,
+ float32,
+ float64,
+ float80,
+ imaginary32,
+ imaginary64,
+ imaginary80,
+ complex32,
+ complex64,
+ complex80,
+ char_,
+ wchar_,
+ dchar_,
+ bool_,
+
+ // Aggregates
+ struct_ = 151,
+ class_,
+ interface_,
+ union_,
+ enum_,
+ import_,
+ alias_,
+ override_,
+ delegate_,
+ function_,
+ mixin_,
+ align_,
+ extern_,
+ private_,
+ protected_,
+ public_,
+ export_,
+ static_,
+ final_,
+ const_,
+ abstract_,
+ debug_,
+ deprecated_,
+ in_,
+ out_,
+ inout_,
+ lazy_,
+ auto_,
+ package_,
+ immutable_,
+
+ // Statements
+ if_ = 181,
+ else_,
+ while_,
+ for_,
+ do_,
+ switch_,
+ case_,
+ default_,
+ break_,
+ continue_,
+ with_,
+ synchronized_,
+ return_,
+ goto_,
+ try_,
+ catch_,
+ finally_,
+ asm_,
+ foreach_,
+ foreach_reverse_,
+ scope_,
+ onScopeExit,
+ onScopeFailure,
+ onScopeSuccess,
+
+ // Contracts
+ invariant_ = 205,
+
+ // Testing
+ unittest_,
+
+ // Added after 1.0
+ argumentTypes,
+ ref_,
+ macro_,
+
+ parameters = 210,
+ traits,
+ overloadSet,
+ pure_,
+ nothrow_,
+ gshared,
+ line,
+ file,
+ fileFullPath,
+ moduleString, // __MODULE__
+ functionString, // __FUNCTION__
+ prettyFunction, // __PRETTY_FUNCTION__
+ shared_,
+ at,
+ pow,
+ powAssign,
+ goesTo,
+ vector,
+ pound,
+
+ interval = 229,
+ voidExpression,
+ cantExpression,
+ showCtfeContext,
+
+ objcClassReference,
+ vectorArray,
+
+ arrow, // ->
+ colonColon, // ::
+ wchar_tLiteral,
+ compoundLiteral, // ( type-name ) { initializer-list }
+
+ // C only keywords
+ inline,
+ register,
+ restrict,
+ signed,
+ sizeof_,
+ typedef_,
+ unsigned,
+ volatile,
+ _Alignas,
+ _Alignof,
+ _Atomic,
+ _Bool,
+ _Complex,
+ _Generic,
+ _Imaginary,
+ _Noreturn,
+ _Static_assert,
+ _Thread_local,
+
+ // C only extended keywords
+ __cdecl,
+ __declspec,
+ __attribute__,
+}
+
+enum FirstCKeyword = TOK.inline;
+
+// Assert that all token enum members have consecutive values and
+// that none of them overlap
+static assert(() {
+ foreach (idx, enumName; __traits(allMembers, TOK)) {
+ static if (idx != __traits(getMember, TOK, enumName)) {
+ pragma(msg, "Error: Expected TOK.", enumName, " to be ", idx, " but is ", __traits(getMember, TOK, enumName));
+ static assert(0);
+ }
+ }
+ return true;
+}());
+
+/****************************************
+ */
+
+private immutable TOK[] keywords =
+[
+ TOK.this_,
+ TOK.super_,
+ TOK.assert_,
+ TOK.null_,
+ TOK.true_,
+ TOK.false_,
+ TOK.cast_,
+ TOK.new_,
+ TOK.delete_,
+ TOK.throw_,
+ TOK.module_,
+ TOK.pragma_,
+ TOK.typeof_,
+ TOK.typeid_,
+ TOK.template_,
+ TOK.void_,
+ TOK.int8,
+ TOK.uns8,
+ TOK.int16,
+ TOK.uns16,
+ TOK.int32,
+ TOK.uns32,
+ TOK.int64,
+ TOK.uns64,
+ TOK.int128,
+ TOK.uns128,
+ TOK.float32,
+ TOK.float64,
+ TOK.float80,
+ TOK.bool_,
+ TOK.char_,
+ TOK.wchar_,
+ TOK.dchar_,
+ TOK.imaginary32,
+ TOK.imaginary64,
+ TOK.imaginary80,
+ TOK.complex32,
+ TOK.complex64,
+ TOK.complex80,
+ TOK.delegate_,
+ TOK.function_,
+ TOK.is_,
+ TOK.if_,
+ TOK.else_,
+ TOK.while_,
+ TOK.for_,
+ TOK.do_,
+ TOK.switch_,
+ TOK.case_,
+ TOK.default_,
+ TOK.break_,
+ TOK.continue_,
+ TOK.synchronized_,
+ TOK.return_,
+ TOK.goto_,
+ TOK.try_,
+ TOK.catch_,
+ TOK.finally_,
+ TOK.with_,
+ TOK.asm_,
+ TOK.foreach_,
+ TOK.foreach_reverse_,
+ TOK.scope_,
+ TOK.struct_,
+ TOK.class_,
+ TOK.interface_,
+ TOK.union_,
+ TOK.enum_,
+ TOK.import_,
+ TOK.mixin_,
+ TOK.static_,
+ TOK.final_,
+ TOK.const_,
+ TOK.alias_,
+ TOK.override_,
+ TOK.abstract_,
+ TOK.debug_,
+ TOK.deprecated_,
+ TOK.in_,
+ TOK.out_,
+ TOK.inout_,
+ TOK.lazy_,
+ TOK.auto_,
+ TOK.align_,
+ TOK.extern_,
+ TOK.private_,
+ TOK.package_,
+ TOK.protected_,
+ TOK.public_,
+ TOK.export_,
+ TOK.invariant_,
+ TOK.unittest_,
+ TOK.version_,
+ TOK.argumentTypes,
+ TOK.parameters,
+ TOK.ref_,
+ TOK.macro_,
+ TOK.pure_,
+ TOK.nothrow_,
+ TOK.gshared,
+ TOK.traits,
+ TOK.vector,
+ TOK.overloadSet,
+ TOK.file,
+ TOK.fileFullPath,
+ TOK.line,
+ TOK.moduleString,
+ TOK.functionString,
+ TOK.prettyFunction,
+ TOK.shared_,
+ TOK.immutable_,
+
+ // C only keywords
+ TOK.inline,
+ TOK.register,
+ TOK.restrict,
+ TOK.signed,
+ TOK.sizeof_,
+ TOK.typedef_,
+ TOK.unsigned,
+ TOK.volatile,
+ TOK._Alignas,
+ TOK._Alignof,
+ TOK._Atomic,
+ TOK._Bool,
+ TOK._Complex,
+ TOK._Generic,
+ TOK._Imaginary,
+ TOK._Noreturn,
+ TOK._Static_assert,
+ TOK._Thread_local,
+
+ // C only extended keywords
+ TOK.__cdecl,
+ TOK.__declspec,
+ TOK.__attribute__,
+];
+
+// Initialize the identifier pool
+shared static this() nothrow
+{
+ Identifier.initTable();
+ foreach (kw; keywords)
+ {
+ //printf("keyword[%d] = '%s'\n",kw, tochars[kw].ptr);
+ Identifier.idPool(Token.tochars[kw].ptr, Token.tochars[kw].length, cast(uint)kw);
+ }
+}
+
+/************************************
+ * This is used to pick the C keywords out of the tokens.
+ * If it's not a C keyword, then it's an identifier.
+ */
+static immutable TOK[TOK.max + 1] Ckeywords =
+() {
+ with (TOK)
+ {
+ TOK[TOK.max + 1] tab = identifier; // default to identifier
+ enum Ckwds = [ auto_, break_, case_, char_, const_, continue_, default_, do_, float64, else_,
+ enum_, extern_, float32, for_, goto_, if_, inline, int32, int64, register,
+ restrict, return_, int16, signed, sizeof_, static_, struct_, switch_, typedef_,
+ union_, unsigned, void_, volatile, while_, asm_,
+ _Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn,
+ _Static_assert, _Thread_local, __cdecl, __declspec, __attribute__ ];
+
+ foreach (kw; Ckwds)
+ tab[kw] = cast(TOK) kw;
+
+ return tab;
+ }
+} ();
+
+
+/***********************************************************
+ */
+extern (C++) struct Token
+{
+ Token* next;
+ Loc loc;
+ const(char)* ptr; // pointer to first character of this token within buffer
+ TOK value;
+ const(char)[] blockComment; // doc comment string prior to this token
+ const(char)[] lineComment; // doc comment for previous token
+
+ union
+ {
+ // Integers
+ sinteger_t intvalue;
+ uinteger_t unsvalue;
+ // Floats
+ real_t floatvalue;
+
+ struct
+ {
+ const(char)* ustring; // UTF8 string
+ uint len;
+ ubyte postfix; // 'c', 'w', 'd'
+ }
+
+ Identifier ident;
+ }
+
+ extern (D) private static immutable string[TOK.max + 1] tochars =
+ [
+ // Keywords
+ TOK.this_: "this",
+ TOK.super_: "super",
+ TOK.assert_: "assert",
+ TOK.null_: "null",
+ TOK.true_: "true",
+ TOK.false_: "false",
+ TOK.cast_: "cast",
+ TOK.new_: "new",
+ TOK.delete_: "delete",
+ TOK.throw_: "throw",
+ TOK.module_: "module",
+ TOK.pragma_: "pragma",
+ TOK.typeof_: "typeof",
+ TOK.typeid_: "typeid",
+ TOK.template_: "template",
+ TOK.void_: "void",
+ TOK.int8: "byte",
+ TOK.uns8: "ubyte",
+ TOK.int16: "short",
+ TOK.uns16: "ushort",
+ TOK.int32: "int",
+ TOK.uns32: "uint",
+ TOK.int64: "long",
+ TOK.uns64: "ulong",
+ TOK.int128: "cent",
+ TOK.uns128: "ucent",
+ TOK.float32: "float",
+ TOK.float64: "double",
+ TOK.float80: "real",
+ TOK.bool_: "bool",
+ TOK.char_: "char",
+ TOK.wchar_: "wchar",
+ TOK.dchar_: "dchar",
+ TOK.imaginary32: "ifloat",
+ TOK.imaginary64: "idouble",
+ TOK.imaginary80: "ireal",
+ TOK.complex32: "cfloat",
+ TOK.complex64: "cdouble",
+ TOK.complex80: "creal",
+ TOK.delegate_: "delegate",
+ TOK.function_: "function",
+ TOK.is_: "is",
+ TOK.if_: "if",
+ TOK.else_: "else",
+ TOK.while_: "while",
+ TOK.for_: "for",
+ TOK.do_: "do",
+ TOK.switch_: "switch",
+ TOK.case_: "case",
+ TOK.default_: "default",
+ TOK.break_: "break",
+ TOK.continue_: "continue",
+ TOK.synchronized_: "synchronized",
+ TOK.return_: "return",
+ TOK.goto_: "goto",
+ TOK.try_: "try",
+ TOK.catch_: "catch",
+ TOK.finally_: "finally",
+ TOK.with_: "with",
+ TOK.asm_: "asm",
+ TOK.foreach_: "foreach",
+ TOK.foreach_reverse_: "foreach_reverse",
+ TOK.scope_: "scope",
+ TOK.struct_: "struct",
+ TOK.class_: "class",
+ TOK.interface_: "interface",
+ TOK.union_: "union",
+ TOK.enum_: "enum",
+ TOK.import_: "import",
+ TOK.mixin_: "mixin",
+ TOK.static_: "static",
+ TOK.final_: "final",
+ TOK.const_: "const",
+ TOK.alias_: "alias",
+ TOK.override_: "override",
+ TOK.abstract_: "abstract",
+ TOK.debug_: "debug",
+ TOK.deprecated_: "deprecated",
+ TOK.in_: "in",
+ TOK.out_: "out",
+ TOK.inout_: "inout",
+ TOK.lazy_: "lazy",
+ TOK.auto_: "auto",
+ TOK.align_: "align",
+ TOK.extern_: "extern",
+ TOK.private_: "private",
+ TOK.package_: "package",
+ TOK.protected_: "protected",
+ TOK.public_: "public",
+ TOK.export_: "export",
+ TOK.invariant_: "invariant",
+ TOK.unittest_: "unittest",
+ TOK.version_: "version",
+ TOK.argumentTypes: "__argTypes",
+ TOK.parameters: "__parameters",
+ TOK.ref_: "ref",
+ TOK.macro_: "macro",
+ TOK.pure_: "pure",
+ TOK.nothrow_: "nothrow",
+ TOK.gshared: "__gshared",
+ TOK.traits: "__traits",
+ TOK.vector: "__vector",
+ TOK.overloadSet: "__overloadset",
+ TOK.file: "__FILE__",
+ TOK.fileFullPath: "__FILE_FULL_PATH__",
+ TOK.line: "__LINE__",
+ TOK.moduleString: "__MODULE__",
+ TOK.functionString: "__FUNCTION__",
+ TOK.prettyFunction: "__PRETTY_FUNCTION__",
+ TOK.shared_: "shared",
+ TOK.immutable_: "immutable",
+
+ TOK.endOfFile: "End of File",
+ TOK.leftCurly: "{",
+ TOK.rightCurly: "}",
+ TOK.leftParenthesis: "(",
+ TOK.rightParenthesis: ")",
+ TOK.leftBracket: "[",
+ TOK.rightBracket: "]",
+ TOK.semicolon: ";",
+ TOK.colon: ":",
+ TOK.comma: ",",
+ TOK.dot: ".",
+ TOK.xor: "^",
+ TOK.xorAssign: "^=",
+ TOK.assign: "=",
+ TOK.construct: "=",
+ TOK.blit: "=",
+ TOK.lessThan: "<",
+ TOK.greaterThan: ">",
+ TOK.lessOrEqual: "<=",
+ TOK.greaterOrEqual: ">=",
+ TOK.equal: "==",
+ TOK.notEqual: "!=",
+ TOK.not: "!",
+ TOK.leftShift: "<<",
+ TOK.rightShift: ">>",
+ TOK.unsignedRightShift: ">>>",
+ TOK.add: "+",
+ TOK.min: "-",
+ TOK.mul: "*",
+ TOK.div: "/",
+ TOK.mod: "%",
+ TOK.slice: "..",
+ TOK.dotDotDot: "...",
+ TOK.and: "&",
+ TOK.andAnd: "&&",
+ TOK.or: "|",
+ TOK.orOr: "||",
+ TOK.array: "[]",
+ TOK.index: "[i]",
+ TOK.address: "&",
+ TOK.star: "*",
+ TOK.tilde: "~",
+ TOK.dollar: "$",
+ TOK.plusPlus: "++",
+ TOK.minusMinus: "--",
+ TOK.prePlusPlus: "++",
+ TOK.preMinusMinus: "--",
+ TOK.type: "type",
+ TOK.question: "?",
+ TOK.negate: "-",
+ TOK.uadd: "+",
+ TOK.variable: "var",
+ TOK.addAssign: "+=",
+ TOK.minAssign: "-=",
+ TOK.mulAssign: "*=",
+ TOK.divAssign: "/=",
+ TOK.modAssign: "%=",
+ TOK.leftShiftAssign: "<<=",
+ TOK.rightShiftAssign: ">>=",
+ TOK.unsignedRightShiftAssign: ">>>=",
+ TOK.andAssign: "&=",
+ TOK.orAssign: "|=",
+ TOK.concatenateAssign: "~=",
+ TOK.concatenateElemAssign: "~=",
+ TOK.concatenateDcharAssign: "~=",
+ TOK.concatenate: "~",
+ TOK.call: "call",
+ TOK.identity: "is",
+ TOK.notIdentity: "!is",
+ TOK.identifier: "identifier",
+ TOK.at: "@",
+ TOK.pow: "^^",
+ TOK.powAssign: "^^=",
+ TOK.goesTo: "=>",
+ TOK.pound: "#",
+ TOK.arrow: "->",
+ TOK.colonColon: "::",
+
+ // For debugging
+ TOK.error: "error",
+ TOK.dotIdentifier: "dotid",
+ TOK.dotTemplateDeclaration: "dottd",
+ TOK.dotTemplateInstance: "dotti",
+ TOK.dotVariable: "dotvar",
+ TOK.dotType: "dottype",
+ TOK.symbolOffset: "symoff",
+ TOK.arrayLength: "arraylength",
+ TOK.arrayLiteral: "arrayliteral",
+ TOK.assocArrayLiteral: "assocarrayliteral",
+ TOK.structLiteral: "structliteral",
+ TOK.string_: "string",
+ TOK.dSymbol: "symbol",
+ TOK.tuple: "tuple",
+ TOK.declaration: "declaration",
+ TOK.onScopeExit: "scope(exit)",
+ TOK.onScopeSuccess: "scope(success)",
+ TOK.onScopeFailure: "scope(failure)",
+ TOK.delegatePointer: "delegateptr",
+
+ // Finish up
+ TOK.reserved: "reserved",
+ TOK.remove: "remove",
+ TOK.newAnonymousClass: "newanonclass",
+ TOK.comment: "comment",
+ TOK.classReference: "classreference",
+ TOK.thrownException: "thrownexception",
+ TOK.delegateFunctionPointer: "delegatefuncptr",
+ TOK.int32Literal: "int32v",
+ TOK.uns32Literal: "uns32v",
+ TOK.int64Literal: "int64v",
+ TOK.uns64Literal: "uns64v",
+ TOK.int128Literal: "int128v",
+ TOK.uns128Literal: "uns128v",
+ TOK.float32Literal: "float32v",
+ TOK.float64Literal: "float64v",
+ TOK.float80Literal: "float80v",
+ TOK.imaginary32Literal: "imaginary32v",
+ TOK.imaginary64Literal: "imaginary64v",
+ TOK.imaginary80Literal: "imaginary80v",
+ TOK.charLiteral: "charv",
+ TOK.wcharLiteral: "wcharv",
+ TOK.dcharLiteral: "dcharv",
+ TOK.wchar_tLiteral: "wchar_tv",
+ TOK.compoundLiteral: "compoundliteral",
+
+ TOK.halt: "halt",
+ TOK.hexadecimalString: "xstring",
+
+ TOK.interval: "interval",
+ TOK.voidExpression: "voidexp",
+ TOK.cantExpression: "cantexp",
+ TOK.showCtfeContext : "showCtfeContext",
+
+ TOK.objcClassReference: "class",
+ TOK.vectorArray: "vectorarray",
+
+ // C only keywords
+ TOK.inline : "inline",
+ TOK.register : "register",
+ TOK.restrict : "restrict",
+ TOK.signed : "signed",
+ TOK.sizeof_ : "sizeof",
+ TOK.typedef_ : "typedef",
+ TOK.unsigned : "unsigned",
+ TOK.volatile : "volatile",
+ TOK._Alignas : "_Alignas",
+ TOK._Alignof : "_Alignof",
+ TOK._Atomic : "_Atomic",
+ TOK._Bool : "_Bool",
+ TOK._Complex : "_Complex",
+ TOK._Generic : "_Generic",
+ TOK._Imaginary: "_Imaginary",
+ TOK._Noreturn : "_Noreturn",
+ TOK._Static_assert : "_Static_assert",
+ TOK._Thread_local : "_Thread_local",
+
+ // C only extended keywords
+ TOK.__cdecl : "__cdecl",
+ TOK.__declspec : "__declspec",
+ TOK.__attribute__ : "__attribute__",
+ ];
+
+ static assert(() {
+ foreach (s; tochars)
+ assert(s.length);
+ return true;
+ }());
+
+nothrow:
+
+ int isKeyword() const
+ {
+ foreach (kw; keywords)
+ {
+ if (kw == value)
+ return 1;
+ }
+ return 0;
+ }
+
+ /****
+ * Set to contents of ptr[0..length]
+ * Params:
+ * ptr = pointer to string
+ * length = length of string
+ */
+ void setString(const(char)* ptr, size_t length)
+ {
+ auto s = cast(char*)mem.xmalloc_noscan(length + 1);
+ memcpy(s, ptr, length);
+ s[length] = 0;
+ ustring = s;
+ len = cast(uint)length;
+ postfix = 0;
+ }
+
+ /****
+ * Set to contents of buf
+ * Params:
+ * buf = string (not zero terminated)
+ */
+ void setString(const ref OutBuffer buf)
+ {
+ setString(cast(const(char)*)buf[].ptr, buf.length);
+ }
+
+ /****
+ * Set to empty string
+ */
+ void setString()
+ {
+ ustring = "";
+ len = 0;
+ postfix = 0;
+ }
+
+ extern (C++) const(char)* toChars() const
+ {
+ __gshared char[3 + 3 * floatvalue.sizeof + 1] buffer;
+ const(char)* p = &buffer[0];
+ switch (value)
+ {
+ case TOK.int32Literal:
+ sprintf(&buffer[0], "%d", cast(d_int32)intvalue);
+ break;
+ case TOK.uns32Literal:
+ case TOK.charLiteral:
+ case TOK.wcharLiteral:
+ case TOK.dcharLiteral:
+ case TOK.wchar_tLiteral:
+ sprintf(&buffer[0], "%uU", cast(d_uns32)unsvalue);
+ break;
+ case TOK.int64Literal:
+ sprintf(&buffer[0], "%lldL", cast(long)intvalue);
+ break;
+ case TOK.uns64Literal:
+ sprintf(&buffer[0], "%lluUL", cast(ulong)unsvalue);
+ break;
+ case TOK.float32Literal:
+ CTFloat.sprint(&buffer[0], 'g', floatvalue);
+ strcat(&buffer[0], "f");
+ break;
+ case TOK.float64Literal:
+ CTFloat.sprint(&buffer[0], 'g', floatvalue);
+ break;
+ case TOK.float80Literal:
+ CTFloat.sprint(&buffer[0], 'g', floatvalue);
+ strcat(&buffer[0], "L");
+ break;
+ case TOK.imaginary32Literal:
+ CTFloat.sprint(&buffer[0], 'g', floatvalue);
+ strcat(&buffer[0], "fi");
+ break;
+ case TOK.imaginary64Literal:
+ CTFloat.sprint(&buffer[0], 'g', floatvalue);
+ strcat(&buffer[0], "i");
+ break;
+ case TOK.imaginary80Literal:
+ CTFloat.sprint(&buffer[0], 'g', floatvalue);
+ strcat(&buffer[0], "Li");
+ break;
+ case TOK.string_:
+ {
+ OutBuffer buf;
+ buf.writeByte('"');
+ for (size_t i = 0; i < len;)
+ {
+ dchar c;
+ utf_decodeChar(ustring[0 .. len], i, c);
+ switch (c)
+ {
+ case 0:
+ break;
+ case '"':
+ case '\\':
+ buf.writeByte('\\');
+ goto default;
+ default:
+ if (c <= 0x7F)
+ {
+ if (isprint(c))
+ buf.writeByte(c);
+ else
+ buf.printf("\\x%02x", c);
+ }
+ else if (c <= 0xFFFF)
+ buf.printf("\\u%04x", c);
+ else
+ buf.printf("\\U%08x", c);
+ continue;
+ }
+ break;
+ }
+ buf.writeByte('"');
+ if (postfix)
+ buf.writeByte(postfix);
+ buf.writeByte(0);
+ p = buf.extractSlice().ptr;
+ }
+ break;
+ case TOK.hexadecimalString:
+ {
+ OutBuffer buf;
+ buf.writeByte('x');
+ buf.writeByte('"');
+ foreach (size_t i; 0 .. len)
+ {
+ if (i)
+ buf.writeByte(' ');
+ buf.printf("%02x", ustring[i]);
+ }
+ buf.writeByte('"');
+ if (postfix)
+ buf.writeByte(postfix);
+ buf.writeByte(0);
+ p = buf.extractSlice().ptr;
+ break;
+ }
+ case TOK.identifier:
+ case TOK.enum_:
+ case TOK.struct_:
+ case TOK.import_:
+ case TOK.wchar_:
+ case TOK.dchar_:
+ case TOK.bool_:
+ case TOK.char_:
+ case TOK.int8:
+ case TOK.uns8:
+ case TOK.int16:
+ case TOK.uns16:
+ case TOK.int32:
+ case TOK.uns32:
+ case TOK.int64:
+ case TOK.uns64:
+ case TOK.int128:
+ case TOK.uns128:
+ case TOK.float32:
+ case TOK.float64:
+ case TOK.float80:
+ case TOK.imaginary32:
+ case TOK.imaginary64:
+ case TOK.imaginary80:
+ case TOK.complex32:
+ case TOK.complex64:
+ case TOK.complex80:
+ case TOK.void_:
+ p = ident.toChars();
+ break;
+ default:
+ p = toChars(value);
+ break;
+ }
+ return p;
+ }
+
+ static const(char)* toChars(uint value)
+ {
+ return toString(value).ptr;
+ }
+
+ extern (D) static string toString(uint value) pure nothrow @nogc @safe
+ {
+ return tochars[value];
+ }
+}
+
diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h
index f79d8419c90..0fd6634f2ce 100644
--- a/gcc/d/dmd/tokens.h
+++ b/gcc/d/dmd/tokens.h
@@ -10,6 +10,7 @@
#pragma once
+#include "root/dcompat.h"
#include "root/port.h"
#include "globals.h"
@@ -31,7 +32,8 @@ class Identifier;
? && ||
*/
-enum TOK
+typedef unsigned short TOK;
+enum
{
TOKreserved,
@@ -76,15 +78,10 @@ enum TOK
TOKindex, TOKis,
// 64
- // NCEG floating point compares
- // !<>= <> <>= !> !>= !< !<= !<>
- TOKunord,TOKlg,TOKleg,TOKule,TOKul,TOKuge,TOKug,TOKue,
-
-// 72
TOKshl, TOKshr,
TOKshlass, TOKshrass,
TOKushr, TOKushrass,
- TOKcat, TOKcatass, // ~ ~=
+ TOKcat, TOKcatass, TOKcatelemass, TOKcatdcharass, // ~ ~=
TOKadd, TOKmin, TOKaddass, TOKminass,
TOKmul, TOKdiv, TOKmod,
TOKmulass, TOKdivass, TOKmodass,
@@ -92,11 +89,11 @@ enum TOK
TOKandass, TOKorass, TOKxorass,
TOKassign, TOKnot, TOKtilde,
TOKplusplus, TOKminusminus, TOKconstruct, TOKblit,
- TOKdot, TOKarrow, TOKcomma,
+ TOKdot, TOKcomma,
TOKquestion, TOKandand, TOKoror,
TOKpreplusplus, TOKpreminusminus,
-// 111
+// 105
// Numeric literals
TOKint32v, TOKuns32v,
TOKint64v, TOKuns64v,
@@ -125,7 +122,7 @@ enum TOK
TOKcomplex32, TOKcomplex64, TOKcomplex80,
TOKchar, TOKwchar, TOKdchar, TOKbool,
-// 158
+// 152
// Aggregates
TOKstruct, TOKclass, TOKinterface, TOKunion, TOKenum, TOKimport,
TOKalias, TOKoverride, TOKdelegate, TOKfunction,
@@ -134,8 +131,9 @@ enum TOK
TOKalign, TOKextern, TOKprivate, TOKprotected, TOKpublic, TOKexport,
TOKstatic, TOKfinal, TOKconst, TOKabstract,
TOKdebug, TOKdeprecated, TOKin, TOKout, TOKinout, TOKlazy,
- TOKauto, TOKpackage, TOKmanifest, TOKimmutable,
+ TOKauto, TOKpackage, TOKimmutable,
+// 182
// Statements
TOKif, TOKelse, TOKwhile, TOKfor, TOKdo, TOKswitch,
TOKcase, TOKdefault, TOKbreak, TOKcontinue, TOKwith,
@@ -144,6 +142,7 @@ enum TOK
TOKscope,
TOKon_scope_exit, TOKon_scope_failure, TOKon_scope_success,
+// 206
// Contracts
TOKinvariant,
@@ -155,6 +154,7 @@ enum TOK
TOKref,
TOKmacro,
+// 211
TOKparameters,
TOKtraits,
TOKoverloadset,
@@ -175,12 +175,42 @@ enum TOK
TOKvector,
TOKpound,
+// 230
TOKinterval,
TOKvoidexp,
TOKcantexp,
+ TOKshowctfecontext,
+ TOKobjc_class_reference,
TOKvectorarray,
+ TOKarrow,
+ TOKcolonColon,
+ TOKwchar_tLiteral,
+
+ TOKinline,
+ TOKregister,
+ TOKrestrict,
+ TOKsigned,
+ TOKsizeof_,
+ TOKtypedef_,
+ TOKunsigned,
+ TOKvolatile,
+ TOK_Alignas,
+ TOK_Alignof,
+ TOK_Atomic,
+ TOK_Bool,
+ TOK_Complex,
+ TOK_Generic,
+ TOK_Imaginary,
+ TOK_Noreturn,
+ TOK_Static_assert,
+ TOK_Thread_local,
+
+ TOK__cdecl,
+ TOK__declspec,
+ TOK__attribute__,
+
TOKMAX
};
@@ -196,15 +226,15 @@ struct Token
{
Token *next;
Loc loc;
- const utf8_t *ptr; // pointer to first character of this token within buffer
+ const utf8_t *ptr; // pointer to first character of this token within buffer
TOK value;
- const utf8_t *blockComment; // doc comment string prior to this token
- const utf8_t *lineComment; // doc comment for previous token
+ DString blockComment; // doc comment string prior to this token
+ DString lineComment; // doc comment for previous token
union
{
// Integers
- d_int64 int64value;
- d_uns64 uns64value;
+ sinteger_t intvalue;
+ uinteger_t unsvalue;
// Floats
real_t floatvalue;
@@ -218,16 +248,13 @@ struct Token
Identifier *ident;
};
- static const char *tochars[TOKMAX];
-
- static Token *freelist;
- static Token *alloc();
void free();
Token() : next(NULL) {}
int isKeyword();
const char *toChars() const;
- static const char *toChars(TOK);
+
+ static const char *toChars(unsigned value);
};
#if defined(__GNUC__)
diff --git a/gcc/d/dmd/traits.c b/gcc/d/dmd/traits.c
deleted file mode 100644
index 5a9f58b79f5..00000000000
--- a/gcc/d/dmd/traits.c
+++ /dev/null
@@ -1,1973 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/traits.c
- */
-
-#include "root/dsystem.h"
-#include "root/rmem.h"
-#include "root/aav.h"
-#include "root/checkedint.h"
-
-#include "errors.h"
-#include "mtype.h"
-#include "init.h"
-#include "expression.h"
-#include "template.h"
-#include "utf.h"
-#include "enum.h"
-#include "scope.h"
-#include "hdrgen.h"
-#include "statement.h"
-#include "declaration.h"
-#include "aggregate.h"
-#include "import.h"
-#include "id.h"
-#include "dsymbol.h"
-#include "module.h"
-#include "attrib.h"
-#include "parse.h"
-#include "root/speller.h"
-#include "target.h"
-
-typedef int (*ForeachDg)(void *ctx, size_t idx, Dsymbol *s);
-int ScopeDsymbol_foreach(Scope *sc, Dsymbols *members, ForeachDg dg, void *ctx, size_t *pn = NULL);
-void freeFieldinit(Scope *sc);
-Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
-Package *resolveIsPackage(Dsymbol *sym);
-Expression *typeToExpression(Type *t);
-Type *decoToType(const char *deco);
-bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps);
-
-
-/************************************************
- * Delegate to be passed to overloadApply() that looks
- * for functions matching a trait.
- */
-
-struct Ptrait
-{
- Dsymbol *sym;
- Expression *e1;
- Expressions *exps; // collected results
- Identifier *ident; // which trait we're looking for
- bool includeTemplates;
- AA **funcTypeHash;
-};
-
-/* Compute the function signature and insert it in the
- * hashtable, if not present. This is needed so that
- * traits(getOverlods, F3, "visit") does not count `int visit(int)`
- * twice in the following example:
- *
- * =============================================
- * interface F1 { int visit(int);}
- * interface F2 { int visit(int); void visit(); }
- * interface F3 : F2, F1 {}
- *==============================================
- */
-static void insertInterfaceInheritedFunction(Ptrait *p, FuncDeclaration *fd, Expression *e)
-{
- Identifier *signature = Identifier::idPool(fd->type->toChars());
- //printf("%s - %s\n", fd->toChars, signature);
- if (!dmd_aaGetRvalue(*p->funcTypeHash, (void *)signature))
- {
- bool* value = (bool*) dmd_aaGet(p->funcTypeHash, (void *)signature);
- *value = true;
- p->exps->push(e);
- }
-}
-
-static int fptraits(void *param, Dsymbol *s)
-{
- Ptrait *p = (Ptrait *)param;
- if (p->includeTemplates)
- {
- p->exps->push(new DsymbolExp(Loc(),s, false));
- return 0;
- }
- FuncDeclaration *fd = s->isFuncDeclaration();
- if (!fd)
- return 0;
-
- if (p->ident == Id::getVirtualFunctions && !fd->isVirtual())
- return 0;
-
- if (p->ident == Id::getVirtualMethods && !fd->isVirtualMethod())
- return 0;
-
- Expression *e;
- FuncAliasDeclaration* ad = new FuncAliasDeclaration(fd->ident, fd, false);
- ad->protection = fd->protection;
- if (p->e1)
- e = new DotVarExp(Loc(), p->e1, ad, false);
- else
- e = new DsymbolExp(Loc(), ad, false);
- // if the parent is an interface declaration
- // we must check for functions with the same signature
- // in different inherited interfaces
- if (p->sym && p->sym->isInterfaceDeclaration())
- insertInterfaceInheritedFunction(p, fd, e);
- else
- p->exps->push(e);
- return 0;
-}
-
-/**
- * Collects all unit test functions from the given array of symbols.
- *
- * This is a helper function used by the implementation of __traits(getUnitTests).
- *
- * Input:
- * symbols array of symbols to collect the functions from
- * uniqueUnitTests an associative array (should actually be a set) to
- * keep track of already collected functions. We're
- * using an AA here to avoid doing a linear search of unitTests
- *
- * Output:
- * unitTests array of DsymbolExp's of the collected unit test functions
- * uniqueUnitTests updated with symbols from unitTests[ ]
- */
-static void collectUnitTests(Dsymbols *symbols, AA *uniqueUnitTests, Expressions *unitTests)
-{
- if (!symbols)
- return;
- for (size_t i = 0; i < symbols->length; i++)
- {
- Dsymbol *symbol = (*symbols)[i];
- UnitTestDeclaration *unitTest = symbol->isUnitTestDeclaration();
- if (unitTest)
- {
- if (!dmd_aaGetRvalue(uniqueUnitTests, (void *)unitTest))
- {
- FuncAliasDeclaration* ad = new FuncAliasDeclaration(unitTest->ident, unitTest, false);
- ad->protection = unitTest->protection;
- Expression* e = new DsymbolExp(Loc(), ad, false);
- unitTests->push(e);
- bool* value = (bool*) dmd_aaGet(&uniqueUnitTests, (void *)unitTest);
- *value = true;
- }
- }
- else
- {
- AttribDeclaration *attrDecl = symbol->isAttribDeclaration();
-
- if (attrDecl)
- {
- Dsymbols *decl = attrDecl->include(NULL);
- collectUnitTests(decl, uniqueUnitTests, unitTests);
- }
- }
- }
-}
-
-/***************************************************
- * Determine if type t is copyable.
- * Params:
- * t = type to check
- * Returns:
- * true if we can copy it
- */
-static bool isCopyable(Type *t)
-{
- //printf("isCopyable() %s\n", t->toChars());
- if (TypeStruct *ts = t->isTypeStruct())
- {
- if (ts->sym->postblit &&
- (ts->sym->postblit->storage_class & STCdisable))
- return false;
- }
- return true;
-}
-
-/************************ TraitsExp ************************************/
-
-static Expression *True(TraitsExp *e) { return new IntegerExp(e->loc, true, Type::tbool); }
-static Expression *False(TraitsExp *e) { return new IntegerExp(e->loc, false, Type::tbool); }
-
-/**************************************
- * Convert `Expression` or `Type` to corresponding `Dsymbol`,
- * additionally strip off expression contexts.
- *
- * Some symbol related `__traits` ignore arguments expression contexts.
- * For example:
- * struct S { void f() {} }
- * S s;
- * pragma(msg, __traits(isNested, s.f));
- * // s.f is DotVarExp, but __traits(isNested) needs a FuncDeclaration.
- *
- * This is used for that common `__traits` behavior.
- */
-static Dsymbol *getDsymbolWithoutExpCtx(RootObject *oarg)
-{
- if (Expression *e = isExpression(oarg))
- {
- if (e->op == TOKdotvar)
- return ((DotVarExp *)e)->var;
- if (e->op == TOKdottd)
- return ((DotTemplateExp *)e)->td;
- }
- return getDsymbol(oarg);
-}
-
-/**
- Gets the function type from a given AST node
- if the node is a function of some sort.
-
- Params:
- o = an AST node to check for a `TypeFunction`
- fdp = optional pointer to a function declararion, to be set
- if `o` is a function declarartion.
-
- Returns:
- a type node if `o` is a declaration of
- a delegate, function, function-pointer
- or a variable of the former. Otherwise, `null`.
-*/
-static TypeFunction *toTypeFunction(RootObject *o, FuncDeclaration **fdp = NULL)
-{
- Dsymbol *s = getDsymbolWithoutExpCtx(o);
- Type *t = isType(o);
- TypeFunction *tf = NULL;
-
- if (s)
- {
- FuncDeclaration *fd = s->isFuncDeclaration();
- if (fd)
- {
- t = fd->type;
- if (fdp)
- *fdp = fd;
- }
- else if (VarDeclaration *vd = s->isVarDeclaration())
- t = vd->type;
- }
- if (t)
- {
- if (t->ty == Tfunction)
- tf = (TypeFunction *)t;
- else if (t->ty == Tdelegate)
- tf = (TypeFunction *)t->nextOf();
- else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction)
- tf = (TypeFunction *)t->nextOf();
- }
-
- return tf;
-}
-
-static bool isTypeArithmetic(Type *t) { return t->isintegral() || t->isfloating(); }
-static bool isTypeFloating(Type *t) { return t->isfloating(); }
-static bool isTypeIntegral(Type *t) { return t->isintegral(); }
-static bool isTypeScalar(Type *t) { return t->isscalar(); }
-static bool isTypeUnsigned(Type *t) { return t->isunsigned(); }
-static bool isTypeAssociativeArray(Type *t) { return t->toBasetype()->ty == Taarray; }
-static bool isTypeStaticArray(Type *t) { return t->toBasetype()->ty == Tsarray; }
-static bool isTypeAbstractClass(Type *t) { return t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract(); }
-static bool isTypeFinalClass(Type *t) { return t->toBasetype()->ty == Tclass && (((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) != 0; }
-
-static Expression *isTypeX(TraitsExp *e, bool (*fp)(Type *t))
-{
- if (!e->args || !e->args->length)
- return False(e);
- for (size_t i = 0; i < e->args->length; i++)
- {
- Type *t = getType((*e->args)[i]);
- if (!t || !fp(t))
- return False(e);
- }
- return True(e);
-}
-
-static bool isDsymDeprecated(Dsymbol *s) { return s->isDeprecated(); }
-
-static int fpisTemplate(void *, Dsymbol *s)
-{
- if (s->isTemplateDeclaration())
- return 1;
-
- return 0;
-}
-
-bool isTemplate(Dsymbol *s)
-{
- if (!s->toAlias()->isOverloadable())
- return false;
-
- return overloadApply(s, NULL, &fpisTemplate) != 0;
-}
-
-static Expression *isDsymX(TraitsExp *e, bool (*fp)(Dsymbol *s))
-{
- if (!e->args || !e->args->length)
- return False(e);
- for (size_t i = 0; i < e->args->length; i++)
- {
- Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]);
- if (!s || !fp(s))
- return False(e);
- }
- return True(e);
-}
-
-static bool isFuncAbstractFunction(FuncDeclaration *f) { return f->isAbstract(); }
-static bool isFuncVirtualFunction(FuncDeclaration *f) { return f->isVirtual(); }
-static bool isFuncVirtualMethod(FuncDeclaration *f) { return f->isVirtualMethod(); }
-static bool isFuncFinalFunction(FuncDeclaration *f) { return f->isFinalFunc(); }
-static bool isFuncStaticFunction(FuncDeclaration *f) { return !f->needThis() && !f->isNested(); }
-static bool isFuncOverrideFunction(FuncDeclaration *f) { return f->isOverride(); }
-
-static Expression *isFuncX(TraitsExp *e, bool (*fp)(FuncDeclaration *f))
-{
- if (!e->args || !e->args->length)
- return False(e);
- for (size_t i = 0; i < e->args->length; i++)
- {
- Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]);
- if (!s)
- return False(e);
- FuncDeclaration *f = s->isFuncDeclaration();
- if (!f || !fp(f))
- return False(e);
- }
- return True(e);
-}
-
-static bool isDeclDisabled(Declaration *d) { return d->isDisabled(); }
-static bool isDeclFuture(Declaration *d) { return d->isFuture(); }
-static bool isDeclRef(Declaration *d) { return d->isRef(); }
-static bool isDeclOut(Declaration *d) { return d->isOut(); }
-static bool isDeclLazy(Declaration *d) { return (d->storage_class & STClazy) != 0; }
-
-static Expression *isDeclX(TraitsExp *e, bool (*fp)(Declaration *d))
-{
- if (!e->args || !e->args->length)
- return False(e);
- for (size_t i = 0; i < e->args->length; i++)
- {
- Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]);
- if (!s)
- return False(e);
- Declaration *d = s->isDeclaration();
- if (!d || !fp(d))
- return False(e);
- }
- return True(e);
-}
-
-static bool isPkgModule(Package *p) { return p->isModule() || p->isPackageMod(); }
-static bool isPkgPackage(Package *p) { return p->isModule() == NULL; }
-
-static Expression *isPkgX(TraitsExp *e, bool (*fp)(Package *p))
-{
- if (!e->args || !e->args->length)
- return False(e);
- for (size_t i = 0; i < e->args->length; i++)
- {
- Dsymbol *s = getDsymbolWithoutExpCtx((*e->args)[i]);
- if (!s)
- return False(e);
- Package *p = resolveIsPackage(s);
- if (!p || !fp(p))
- return False(e);
- }
- return True(e);
-}
-
-// callback for TypeFunction::attributesApply
-struct PushAttributes
-{
- Expressions *mods;
-
- static int fp(void *param, const char *str)
- {
- PushAttributes *p = (PushAttributes *)param;
- p->mods->push(new StringExp(Loc(), const_cast<char *>(str)));
- return 0;
- }
-};
-
-StringTable traitsStringTable;
-
-struct TraitsInitializer
-{
- TraitsInitializer();
-};
-
-static TraitsInitializer traitsinitializer;
-
-TraitsInitializer::TraitsInitializer()
-{
- const char* traits[] = {
- "isAbstractClass",
- "isArithmetic",
- "isAssociativeArray",
- "isDisabled",
- "isDeprecated",
- "isFuture",
- "isFinalClass",
- "isPOD",
- "isNested",
- "isFloating",
- "isIntegral",
- "isScalar",
- "isStaticArray",
- "isUnsigned",
- "isVirtualFunction",
- "isVirtualMethod",
- "isAbstractFunction",
- "isFinalFunction",
- "isOverrideFunction",
- "isStaticFunction",
- "isModule",
- "isPackage",
- "isRef",
- "isOut",
- "isLazy",
- "isReturnOnStack",
- "hasMember",
- "identifier",
- "getProtection",
- "getVisibility",
- "parent",
- "child",
- "getLinkage",
- "getMember",
- "getOverloads",
- "getVirtualFunctions",
- "getVirtualMethods",
- "classInstanceSize",
- "allMembers",
- "derivedMembers",
- "isSame",
- "compiles",
- "getAliasThis",
- "getAttributes",
- "getFunctionAttributes",
- "getFunctionVariadicStyle",
- "getParameterStorageClasses",
- "getUnitTests",
- "getVirtualIndex",
- "getPointerBitmap",
- "isZeroInit",
- "getTargetInfo",
- "getLocation",
- "hasPostblit",
- "isCopyable",
- NULL
- };
-
- traitsStringTable._init(56);
-
- for (size_t idx = 0;; idx++)
- {
- const char *s = traits[idx];
- if (!s) break;
- StringValue *sv = traitsStringTable.insert(s, strlen(s), const_cast<char *>(s));
- assert(sv);
- }
-}
-
-void *trait_search_fp(void *, const char *seed, int* cost)
-{
- //printf("trait_search_fp('%s')\n", seed);
- size_t len = strlen(seed);
- if (!len)
- return NULL;
-
- *cost = 0;
- StringValue *sv = traitsStringTable.lookup(seed, len);
- return sv ? (void*)sv->ptrvalue : NULL;
-}
-
-/**
- * get an array of size_t values that indicate possible pointer words in memory
- * if interpreted as the type given as argument
- * the first array element is the size of the type for independent interpretation
- * of the array
- * following elements bits represent one word (4/8 bytes depending on the target
- * architecture). If set the corresponding memory might contain a pointer/reference.
- *
- * [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...]
- */
-Expression *pointerBitmap(TraitsExp *e)
-{
- if (!e->args || e->args->length != 1)
- {
- error(e->loc, "a single type expected for trait pointerBitmap");
- return new ErrorExp();
- }
- Type *t = getType((*e->args)[0]);
- if (!t)
- {
- error(e->loc, "%s is not a type", (*e->args)[0]->toChars());
- return new ErrorExp();
- }
- d_uns64 sz;
- if (t->ty == Tclass && !((TypeClass*)t)->sym->isInterfaceDeclaration())
- sz = ((TypeClass*)t)->sym->AggregateDeclaration::size(e->loc);
- else
- sz = t->size(e->loc);
- if (sz == SIZE_INVALID)
- return new ErrorExp();
-
- const d_uns64 sz_size_t = Type::tsize_t->size(e->loc);
- if (sz > UINT64_MAX - sz_size_t)
- {
- error(e->loc, "size overflow for type %s", t->toChars());
- return new ErrorExp();
- }
-
- d_uns64 bitsPerWord = sz_size_t * 8;
- d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t;
- d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord;
- Array<d_uns64> data;
- data.setDim((size_t)cntdata);
- data.zero();
-
- class PointerBitmapVisitor : public Visitor
- {
- public:
- PointerBitmapVisitor(Array<d_uns64>* _data, d_uns64 _sz_size_t)
- : data(_data), offset(0), sz_size_t(_sz_size_t), error(false)
- {}
-
- void setpointer(d_uns64 off)
- {
- d_uns64 ptroff = off / sz_size_t;
- (*data)[(size_t)(ptroff / (8 * sz_size_t))] |= 1LL << (ptroff % (8 * sz_size_t));
- }
- virtual void visit(Type *t)
- {
- Type *tb = t->toBasetype();
- if (tb != t)
- tb->accept(this);
- }
- virtual void visit(TypeError *t) { visit((Type *)t); }
- virtual void visit(TypeNext *) { assert(0); }
- virtual void visit(TypeBasic *t)
- {
- if (t->ty == Tvoid)
- setpointer(offset);
- }
- virtual void visit(TypeVector *) { }
- virtual void visit(TypeArray *) { assert(0); }
- virtual void visit(TypeSArray *t)
- {
- d_uns64 arrayoff = offset;
- d_uns64 nextsize = t->next->size();
- if (nextsize == SIZE_INVALID)
- error = true;
- d_uns64 dim = t->dim->toInteger();
- for (d_uns64 i = 0; i < dim; i++)
- {
- offset = arrayoff + i * nextsize;
- t->next->accept(this);
- }
- offset = arrayoff;
- }
- virtual void visit(TypeDArray *) { setpointer(offset + sz_size_t); } // dynamic array is {length,ptr}
- virtual void visit(TypeAArray *) { setpointer(offset); }
- virtual void visit(TypePointer *t)
- {
- if (t->nextOf()->ty != Tfunction) // don't mark function pointers
- setpointer(offset);
- }
- virtual void visit(TypeReference *) { setpointer(offset); }
- virtual void visit(TypeClass *) { setpointer(offset); }
- virtual void visit(TypeFunction *) { }
- virtual void visit(TypeDelegate *) { setpointer(offset); } // delegate is {context, function}
- virtual void visit(TypeQualified *) { assert(0); } // assume resolved
- virtual void visit(TypeIdentifier *) { assert(0); }
- virtual void visit(TypeInstance *) { assert(0); }
- virtual void visit(TypeTypeof *) { assert(0); }
- virtual void visit(TypeReturn *) { assert(0); }
- virtual void visit(TypeEnum *t) { visit((Type *)t); }
- virtual void visit(TypeTuple *t) { visit((Type *)t); }
- virtual void visit(TypeSlice *) { assert(0); }
- virtual void visit(TypeNull *) { } // always a null pointer
-
- virtual void visit(TypeStruct *t)
- {
- d_uns64 structoff = offset;
- for (size_t i = 0; i < t->sym->fields.length; i++)
- {
- VarDeclaration *v = t->sym->fields[i];
- offset = structoff + v->offset;
- if (v->type->ty == Tclass)
- setpointer(offset);
- else
- v->type->accept(this);
- }
- offset = structoff;
- }
-
- // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
- void visitClass(TypeClass* t)
- {
- d_uns64 classoff = offset;
-
- // skip vtable-ptr and monitor
- if (t->sym->baseClass)
- visitClass((TypeClass*)t->sym->baseClass->type);
-
- for (size_t i = 0; i < t->sym->fields.length; i++)
- {
- VarDeclaration *v = t->sym->fields[i];
- offset = classoff + v->offset;
- v->type->accept(this);
- }
- offset = classoff;
- }
-
- Array<d_uns64>* data;
- d_uns64 offset;
- d_uns64 sz_size_t;
- bool error;
- };
-
- PointerBitmapVisitor pbv(&data, sz_size_t);
- if (t->ty == Tclass)
- pbv.visitClass((TypeClass*)t);
- else
- t->accept(&pbv);
- if (pbv.error)
- return new ErrorExp();
-
- Expressions* exps = new Expressions;
- exps->push(new IntegerExp(e->loc, sz, Type::tsize_t));
- for (d_uns64 i = 0; i < cntdata; i++)
- exps->push(new IntegerExp(e->loc, data[(size_t)i], Type::tsize_t));
-
- ArrayLiteralExp* ale = new ArrayLiteralExp(e->loc, Type::tsize_t->sarrayOf(cntdata + 1), exps);
- return ale;
-}
-
-static Expression *dimError(TraitsExp *e, int expected, int dim)
-{
- e->error("expected %d arguments for `%s` but had %d", expected, e->ident->toChars(), dim);
- return new ErrorExp();
-}
-
-Expression *semanticTraits(TraitsExp *e, Scope *sc)
-{
- if (e->ident != Id::compiles &&
- e->ident != Id::isSame &&
- e->ident != Id::identifier &&
- e->ident != Id::getProtection && e->ident != Id::getVisibility &&
- e->ident != Id::getAttributes)
- {
- // Pretend we're in a deprecated scope so that deprecation messages
- // aren't triggered when checking if a symbol is deprecated
- const StorageClass save = sc->stc;
- if (e->ident == Id::isDeprecated)
- sc->stc |= STCdeprecated;
- if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 1))
- {
- sc->stc = save;
- return new ErrorExp();
- }
- sc->stc = save;
- }
- size_t dim = e->args ? e->args->length : 0;
-
- if (e->ident == Id::isArithmetic)
- {
- return isTypeX(e, &isTypeArithmetic);
- }
- else if (e->ident == Id::isFloating)
- {
- return isTypeX(e, &isTypeFloating);
- }
- else if (e->ident == Id::isIntegral)
- {
- return isTypeX(e, &isTypeIntegral);
- }
- else if (e->ident == Id::isScalar)
- {
- return isTypeX(e, &isTypeScalar);
- }
- else if (e->ident == Id::isUnsigned)
- {
- return isTypeX(e, &isTypeUnsigned);
- }
- else if (e->ident == Id::isAssociativeArray)
- {
- return isTypeX(e, &isTypeAssociativeArray);
- }
- else if (e->ident == Id::isDeprecated)
- {
- return isDsymX(e, &isDsymDeprecated);
- }
- else if (e->ident == Id::isFuture)
- {
- return isDeclX(e, &isDeclFuture);
- }
- else if (e->ident == Id::isStaticArray)
- {
- return isTypeX(e, &isTypeStaticArray);
- }
- else if (e->ident == Id::isAbstractClass)
- {
- return isTypeX(e, &isTypeAbstractClass);
- }
- else if (e->ident == Id::isFinalClass)
- {
- return isTypeX(e, &isTypeFinalClass);
- }
- else if (e->ident == Id::isTemplate)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isDsymX(e, &isTemplate);
- }
- else if (e->ident == Id::isPOD)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Type *t = isType(o);
- if (!t)
- {
- e->error("type expected as second argument of __traits %s instead of %s",
- e->ident->toChars(), o->toChars());
- return new ErrorExp();
- }
-
- Type *tb = t->baseElemOf();
- if (StructDeclaration *sd = (tb->ty == Tstruct) ? ((TypeStruct *)tb)->sym : NULL)
- {
- return (sd->isPOD()) ? True(e) : False(e);
- }
- return True(e);
- }
- else if (e->ident == Id::hasPostblit)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Type *t = isType(o);
- if (!t)
- {
- e->error("type expected as second argument of __traits %s instead of %s",
- e->ident->toChars(), o->toChars());
- return new ErrorExp();
- }
-
- Type *tb = t->baseElemOf();
- if (StructDeclaration *sd = (tb->ty == Tstruct) ? ((TypeStruct *)tb)->sym : NULL)
- {
- return sd->postblit ? True(e) : False(e);
- }
- return False(e);
- }
- else if (e->ident == Id::isCopyable)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Type *t = isType(o);
- if (!t)
- {
- e->error("type expected as second argument of __traits %s instead of %s",
- e->ident->toChars(), o->toChars());
- return new ErrorExp();
- }
-
- return isCopyable(t) ? True(e) : False(e);
- }
- else if (e->ident == Id::isNested)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbolWithoutExpCtx(o);
- if (!s)
- {
- }
- else if (AggregateDeclaration *a = s->isAggregateDeclaration())
- {
- return a->isNested() ? True(e) : False(e);
- }
- else if (FuncDeclaration *f = s->isFuncDeclaration())
- {
- return f->isNested() ? True(e) : False(e);
- }
-
- e->error("aggregate or function expected instead of `%s`", o->toChars());
- return new ErrorExp();
- }
- else if (e->ident == Id::isDisabled)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isDeclX(e, &isDeclDisabled);
- }
- else if (e->ident == Id::isAbstractFunction)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isFuncX(e, &isFuncAbstractFunction);
- }
- else if (e->ident == Id::isVirtualFunction)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isFuncX(e, &isFuncVirtualFunction);
- }
- else if (e->ident == Id::isVirtualMethod)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isFuncX(e, &isFuncVirtualMethod);
- }
- else if (e->ident == Id::isFinalFunction)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isFuncX(e, &isFuncFinalFunction);
- }
- else if (e->ident == Id::isOverrideFunction)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isFuncX(e, &isFuncOverrideFunction);
- }
- else if (e->ident == Id::isStaticFunction)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isFuncX(e, &isFuncStaticFunction);
- }
- else if (e->ident == Id::isModule)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isPkgX(e, &isPkgModule);
- }
- else if (e->ident == Id::isPackage)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isPkgX(e, &isPkgPackage);
- }
- else if (e->ident == Id::isRef)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isDeclX(e, &isDeclRef);
- }
- else if (e->ident == Id::isOut)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isDeclX(e, &isDeclOut);
- }
- else if (e->ident == Id::isLazy)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- return isDeclX(e, &isDeclLazy);
- }
- else if (e->ident == Id::identifier)
- {
- // Get identifier for symbol as a string literal
- /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
- * a symbol should not be folded to a constant.
- * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
- */
- if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 2))
- return new ErrorExp();
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Identifier *id = NULL;
- if (Parameter *po = isParameter(o))
- {
- if (!po->ident)
- {
- e->error("argument `%s` has no identifier", po->type->toChars());
- return new ErrorExp();
- }
- id = po->ident;
- }
- else
- {
- Dsymbol *s = getDsymbolWithoutExpCtx(o);
- if (!s || !s->ident)
- {
- e->error("argument %s has no identifier", o->toChars());
- return new ErrorExp();
- }
- id = s->ident;
- }
-
- StringExp *se = new StringExp(e->loc, const_cast<char *>(id->toChars()));
- return expressionSemantic(se, sc);
- }
- else if (e->ident == Id::getProtection || e->ident == Id::getVisibility)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- Scope *sc2 = sc->push();
- sc2->flags = sc->flags | SCOPEnoaccesscheck | SCOPEignoresymbolvisibility;
- bool ok = TemplateInstance::semanticTiargs(e->loc, sc2, e->args, 1);
- sc2->pop();
- if (!ok)
- return new ErrorExp();
-
- RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbolWithoutExpCtx(o);
- if (!s)
- {
- if (!isError(o))
- e->error("argument %s has no protection", o->toChars());
- return new ErrorExp();
- }
- if (s->semanticRun == PASSinit)
- dsymbolSemantic(s, NULL);
-
- const char *protName = protectionToChars(s->prot().kind); // TODO: How about package(names)
- assert(protName);
- StringExp *se = new StringExp(e->loc, const_cast<char *>(protName));
- return expressionSemantic(se, sc);
- }
- else if (e->ident == Id::parent)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbolWithoutExpCtx(o);
- if (s)
- {
- if (FuncDeclaration *fd = s->isFuncDeclaration()) // Bugzilla 8943
- s = fd->toAliasFunc();
- if (!s->isImport()) // Bugzilla 8922
- s = s->toParent();
- }
- if (!s || s->isImport())
- {
- e->error("argument %s has no parent", o->toChars());
- return new ErrorExp();
- }
-
- if (FuncDeclaration *f = s->isFuncDeclaration())
- {
- if (TemplateDeclaration *td = getFuncTemplateDecl(f))
- {
- if (td->overroot) // if not start of overloaded list of TemplateDeclaration's
- td = td->overroot; // then get the start
- Expression *ex = new TemplateExp(e->loc, td, f);
- ex = expressionSemantic(ex, sc);
- return ex;
- }
-
- if (FuncLiteralDeclaration *fld = f->isFuncLiteralDeclaration())
- {
- // Directly translate to VarExp instead of FuncExp
- Expression *ex = new VarExp(e->loc, fld, true);
- return expressionSemantic(ex, sc);
- }
- }
-
- return resolve(e->loc, sc, s, false);
- }
- else if (e->ident == Id::child)
- {
- if (dim != 2)
- return dimError(e, 2, dim);
-
- Expression *ex;
- RootObject *op = (*e->args)[0];
- if (Dsymbol *symp = getDsymbol(op))
- ex = new DsymbolExp(e->loc, symp);
- else if (Expression *exp = isExpression(op))
- ex = exp;
- else
- {
- e->error("symbol or expression expected as first argument of __traits `child` instead of `%s`", op->toChars());
- return new ErrorExp();
- }
-
- ex = expressionSemantic(ex, sc);
- RootObject *oc = (*e->args)[1];
- Dsymbol *symc = getDsymbol(oc);
- if (!symc)
- {
- e->error("symbol expected as second argument of __traits `child` instead of `%s`", oc->toChars());
- return new ErrorExp();
- }
-
- if (Declaration *d = symc->isDeclaration())
- ex = new DotVarExp(e->loc, ex, d);
- else if (TemplateDeclaration *td = symc->isTemplateDeclaration())
- ex = new DotExp(e->loc, ex, new TemplateExp(e->loc, td));
- else if (ScopeDsymbol *ti = symc->isScopeDsymbol())
- ex = new DotExp(e->loc, ex, new ScopeExp(e->loc, ti));
- else
- assert(0);
-
- ex = expressionSemantic(ex, sc);
- return ex;
- }
- else if (e->ident == Id::toType)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- Expression *ex = isExpression((*e->args)[0]);
- if (!ex)
- {
- e->error("expression expected as second argument of __traits `%s`", e->ident->toChars());
- return new ErrorExp();
- }
- ex = ex->ctfeInterpret();
-
- StringExp *se = semanticString(sc, ex, "__traits(toType, string)");
- if (!se)
- {
- return new ErrorExp();
- }
- Type *t = decoToType(se->toUTF8(sc)->toPtr());
- if (!t)
- {
- e->error("cannot determine `%s`", e->toChars());
- return new ErrorExp();
- }
- ex = new TypeExp(e->loc, t);
- ex = expressionSemantic(ex, sc);
- return ex;
- }
- else if (e->ident == Id::hasMember ||
- e->ident == Id::getMember ||
- e->ident == Id::getOverloads ||
- e->ident == Id::getVirtualMethods ||
- e->ident == Id::getVirtualFunctions)
- {
- if (dim != 2 && !(dim == 3 && e->ident == Id::getOverloads))
- return dimError(e, 2, dim);
-
- RootObject *o = (*e->args)[0];
- Expression *ex = isExpression((*e->args)[1]);
- if (!ex)
- {
- e->error("expression expected as second argument of __traits %s", e->ident->toChars());
- return new ErrorExp();
- }
- ex = ex->ctfeInterpret();
-
- bool includeTemplates = false;
- if (dim == 3 && e->ident == Id::getOverloads)
- {
- Expression *b = isExpression((*e->args)[2]);
- b = b->ctfeInterpret();
- if (!b->type->equals(Type::tbool))
- {
- e->error("`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b->toChars(), b->type->toChars());
- return new ErrorExp();
- }
- includeTemplates = b->isBool(true);
- }
-
- StringExp *se = ex->toStringExp();
- if (!se || se->len == 0)
- {
- e->error("string expected as second argument of __traits %s instead of %s", e->ident->toChars(), ex->toChars());
- return new ErrorExp();
- }
- se = se->toUTF8(sc);
-
- if (se->sz != 1)
- {
- e->error("string must be chars");
- return new ErrorExp();
- }
- Identifier *id = Identifier::idPool((char *)se->string, se->len);
-
- /* Prefer dsymbol, because it might need some runtime contexts.
- */
- Dsymbol *sym = getDsymbol(o);
- if (sym)
- {
- if (e->ident == Id::hasMember)
- {
- if (sym->search(e->loc, id) != NULL)
- return True(e);
- }
- ex = new DsymbolExp(e->loc, sym);
- ex = new DotIdExp(e->loc, ex, id);
- }
- else if (Type *t = isType(o))
- ex = typeDotIdExp(e->loc, t, id);
- else if (Expression *ex2 = isExpression(o))
- ex = new DotIdExp(e->loc, ex2, id);
- else
- {
- e->error("invalid first argument");
- return new ErrorExp();
- }
-
- // ignore symbol visibility and disable access checks for these traits
- Scope *scx = sc->push();
- scx->flags |= SCOPEignoresymbolvisibility | SCOPEnoaccesscheck;
-
- if (e->ident == Id::hasMember)
- {
- /* Take any errors as meaning it wasn't found
- */
- ex = trySemantic(ex, scx);
- scx->pop();
- return ex ? True(e) : False(e);
- }
- else if (e->ident == Id::getMember)
- {
- if (ex->op == TOKdotid)
- // Prevent semantic() from replacing Symbol with its initializer
- ((DotIdExp *)ex)->wantsym = true;
- ex = expressionSemantic(ex, scx);
- scx->pop();
- return ex;
- }
- else if (e->ident == Id::getVirtualFunctions ||
- e->ident == Id::getVirtualMethods ||
- e->ident == Id::getOverloads)
- {
- unsigned errors = global.errors;
- Expression *eorig = ex;
- ex = expressionSemantic(ex, scx);
- if (errors < global.errors)
- e->error("%s cannot be resolved", eorig->toChars());
- //ex->print();
-
- /* Create tuple of functions of ex
- */
- Expressions *exps = new Expressions();
- Dsymbol *f;
- if (ex->op == TOKvar)
- {
- VarExp *ve = (VarExp *)ex;
- f = ve->var->isFuncDeclaration();
- ex = NULL;
- }
- else if (ex->op == TOKdotvar)
- {
- DotVarExp *dve = (DotVarExp *)ex;
- f = dve->var->isFuncDeclaration();
- if (dve->e1->op == TOKdottype || dve->e1->op == TOKthis)
- ex = NULL;
- else
- ex = dve->e1;
- }
- else if (ex->op == TOKtemplate)
- {
- TemplateExp *te = (TemplateExp *)ex;
- TemplateDeclaration *td = te->td;
- f = td;
- if (td && td->funcroot)
- f = td->funcroot;
- ex = NULL;
- }
- else
- f = NULL;
- Ptrait p;
- p.sym = sym;
- p.exps = exps;
- p.e1 = ex;
- p.ident = e->ident;
- p.includeTemplates = includeTemplates;
- AA *funcTypeHash = NULL;
- p.funcTypeHash = &funcTypeHash;
-
- InterfaceDeclaration *ifd = NULL;
- if (sym)
- ifd = sym->isInterfaceDeclaration();
- // If the symbol passed as a parameter is an
- // interface that inherits other interfaces
- if (ifd && ifd->interfaces.length)
- {
- // check the overloads of each inherited interface individually
- for (size_t i = 0; i < ifd->interfaces.length; i++)
- {
- BaseClass *bc = ifd->interfaces.ptr[i];
- if (Dsymbol *fd = bc->sym->search(e->loc, f->ident))
- overloadApply(fd, &p, &fptraits);
- }
- }
- else
- overloadApply(f, &p, &fptraits);
-
- ex = new TupleExp(e->loc, exps);
- ex = expressionSemantic(ex, scx);
- scx->pop();
- return ex;
- }
- else
- assert(0);
- }
- else if (e->ident == Id::classInstanceSize)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
- ClassDeclaration *cd = s ? s->isClassDeclaration() : NULL;
- if (!cd)
- {
- e->error("first argument is not a class");
- return new ErrorExp();
- }
- if (cd->sizeok != SIZEOKdone)
- {
- cd->size(cd->loc);
- }
- if (cd->sizeok != SIZEOKdone)
- {
- e->error("%s %s is forward referenced", cd->kind(), cd->toChars());
- return new ErrorExp();
- }
-
- return new IntegerExp(e->loc, cd->structsize, Type::tsize_t);
- }
- else if (e->ident == Id::getAliasThis)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
- AggregateDeclaration *ad = s ? s->isAggregateDeclaration() : NULL;
- if (!ad)
- {
- e->error("argument is not an aggregate type");
- return new ErrorExp();
- }
-
- Expressions *exps = new Expressions();
- if (ad->aliasthis)
- exps->push(new StringExp(e->loc, const_cast<char *>(ad->aliasthis->ident->toChars())));
- Expression *ex = new TupleExp(e->loc, exps);
- ex = expressionSemantic(ex, sc);
- return ex;
- }
- else if (e->ident == Id::getAttributes)
- {
- /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
- * a symbol should not be folded to a constant.
- * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
- */
- if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 3))
- return new ErrorExp();
-
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Parameter *po = isParameter(o);
- Dsymbol *s = getDsymbolWithoutExpCtx(o);
- UserAttributeDeclaration *udad = NULL;
- if (po)
- {
- udad = po->userAttribDecl;
- }
- else if (s)
- {
- if (Import *imp = s->isImport())
- {
- s = imp->mod;
- }
- //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttribDecl, s->_scope);
- udad = s->userAttribDecl;
- }
- else
- {
- e->error("first argument is not a symbol");
- return new ErrorExp();
- }
-
- Expressions *exps = udad ? udad->getAttributes() : new Expressions();
- TupleExp *tup = new TupleExp(e->loc, exps);
- return expressionSemantic(tup, sc);
- }
- else if (e->ident == Id::getFunctionAttributes)
- {
- /* extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
- * https://dlang.org/spec/traits.html#getFunctionAttributes
- */
- if (dim != 1)
- return dimError(e, 1, dim);
-
- TypeFunction *tf = toTypeFunction((*e->args)[0]);
-
- if (!tf)
- {
- e->error("first argument is not a function");
- return new ErrorExp();
- }
-
- Expressions *mods = new Expressions();
- PushAttributes pa;
- pa.mods = mods;
- tf->modifiersApply(&pa, &PushAttributes::fp);
- tf->attributesApply(&pa, &PushAttributes::fp, TRUSTformatSystem);
-
- TupleExp *tup = new TupleExp(e->loc, mods);
- return expressionSemantic(tup, sc);
- }
- else if (e->ident == Id::isReturnOnStack)
- {
- /* Extract as a boolean if function return value is on the stack
- * https://dlang.org/spec/traits.html#isReturnOnStack
- */
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- FuncDeclaration *fd = NULL;
- TypeFunction *tf = toTypeFunction(o, &fd);
-
- if (!tf)
- {
- e->error("argument to `__traits(isReturnOnStack, %s)` is not a function", o->toChars());
- return new ErrorExp();
- }
-
- bool value = target.isReturnOnStack(tf, fd && fd->needThis());
- return new IntegerExp(e->loc, value, Type::tbool);
- }
- else if (e->ident == Id::getFunctionVariadicStyle)
- {
- /* Accept a symbol or a type. Returns one of the following:
- * "none" not a variadic function
- * "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments`
- * "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg
- * "typesafe" void typesafe(T[] ...)
- */
- // get symbol linkage as a string
- if (dim != 1)
- return dimError(e, 1, dim);
-
- LINK link;
- VarArg varargs;
- RootObject *o = (*e->args)[0];
- FuncDeclaration *fd = NULL;
- TypeFunction *tf = toTypeFunction(o, &fd);
-
- if (tf)
- {
- link = tf->linkage;
- varargs = tf->parameterList.varargs;
- }
- else
- {
- if (!fd)
- {
- e->error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o->toChars());
- return new ErrorExp();
- }
- link = fd->linkage;
- varargs = fd->getParameterList().varargs;
- }
- const char *style;
- switch (varargs)
- {
- case 0: style = "none"; break;
- case 1: style = (link == LINKd) ? "argptr"
- : "stdarg"; break;
- case 2: style = "typesafe"; break;
- default:
- assert(0);
- }
- StringExp *se = new StringExp(e->loc, const_cast<char*>(style));
- return expressionSemantic(se, sc);
- }
- else if (e->ident == Id::getParameterStorageClasses)
- {
- /* Accept a function symbol or a type, followed by a parameter index.
- * Returns a tuple of strings of the parameter's storage classes.
- */
- // get symbol linkage as a string
- if (dim != 2)
- return dimError(e, 2, dim);
-
- RootObject *o = (*e->args)[0];
- RootObject *o1 = (*e->args)[1];
-
- FuncDeclaration *fd = NULL;
- TypeFunction *tf = toTypeFunction(o, &fd);
-
- ParameterList fparams;
- if (tf)
- {
- fparams = tf->parameterList;
- }
- else
- {
- if (!fd)
- {
- e->error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function",
- o->toChars(), o1->toChars());
- return new ErrorExp();
- }
- fparams = fd->getParameterList();
- }
-
- StorageClass stc;
-
- // Set stc to storage class of the ith parameter
- Expression *ex = isExpression((*e->args)[1]);
- if (!ex)
- {
- e->error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`",
- o->toChars(), o1->toChars());
- return new ErrorExp();
- }
- ex = ex->ctfeInterpret();
- uinteger_t ii = ex->toUInteger();
- if (ii >= fparams.length())
- {
- e->error("parameter index must be in range 0..%u not %s", (unsigned)fparams.length(), ex->toChars());
- return new ErrorExp();
- }
-
- unsigned n = (unsigned)ii;
- Parameter *p = fparams[n];
- stc = p->storageClass;
-
- // This mirrors hdrgen.visit(Parameter p)
- if (p->type && p->type->mod & MODshared)
- stc &= ~STCshared;
-
- Expressions *exps = new Expressions;
-
- if (stc & STCauto)
- exps->push(new StringExp(e->loc, const_cast<char *>("auto")));
- if (stc & STCreturn)
- exps->push(new StringExp(e->loc, const_cast<char *>("return")));
-
- if (stc & STCout)
- exps->push(new StringExp(e->loc, const_cast<char *>("out")));
- else if (stc & STCref)
- exps->push(new StringExp(e->loc, const_cast<char *>("ref")));
- else if (stc & STCin)
- exps->push(new StringExp(e->loc, const_cast<char *>("in")));
- else if (stc & STClazy)
- exps->push(new StringExp(e->loc, const_cast<char *>("lazy")));
- else if (stc & STCalias)
- exps->push(new StringExp(e->loc, const_cast<char *>("alias")));
-
- if (stc & STCconst)
- exps->push(new StringExp(e->loc, const_cast<char *>("const")));
- if (stc & STCimmutable)
- exps->push(new StringExp(e->loc, const_cast<char *>("immutable")));
- if (stc & STCwild)
- exps->push(new StringExp(e->loc, const_cast<char *>("inout")));
- if (stc & STCshared)
- exps->push(new StringExp(e->loc, const_cast<char *>("shared")));
- if (stc & STCscope && !(stc & STCscopeinferred))
- exps->push(new StringExp(e->loc, const_cast<char *>("scope")));
-
- TupleExp *tup = new TupleExp(e->loc, exps);
- return expressionSemantic(tup, sc);
- }
- else if (e->ident == Id::getLinkage)
- {
- // get symbol linkage as a string
- if (dim != 1)
- return dimError(e, 1, dim);
-
- LINK link;
- RootObject *o = (*e->args)[0];
-
- TypeFunction *tf = toTypeFunction(o);
-
- if (tf)
- link = tf->linkage;
- else
- {
- Dsymbol *s = getDsymbol(o);
- Declaration *d = NULL;
- AggregateDeclaration *ad = NULL;
- if (!s || ((d = s->isDeclaration()) == NULL
- && (ad = s->isAggregateDeclaration()) == NULL))
- {
- e->error("argument to `__traits(getLinkage, %s)` is not a declaration", o->toChars());
- return new ErrorExp();
- }
- if (d != NULL)
- link = d->linkage;
- else
- {
- switch (ad->classKind)
- {
- case ClassKind::d:
- link = LINKd;
- break;
- case ClassKind::cpp:
- link = LINKcpp;
- break;
- case ClassKind::objc:
- link = LINKobjc;
- break;
- default:
- assert(0);
- }
- }
- }
- const char *linkage = linkageToChars(link);
- StringExp *se = new StringExp(e->loc, const_cast<char *>(linkage));
- return expressionSemantic(se, sc);
- }
- else if (e->ident == Id::allMembers ||
- e->ident == Id::derivedMembers)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbol(o);
- if (!s)
- {
- e->error("argument has no members");
- return new ErrorExp();
- }
- if (Import *imp = s->isImport())
- {
- // Bugzilla 9692
- s = imp->mod;
- }
-
- // https://issues.dlang.org/show_bug.cgi?id=16044
- if (Package *p = s->isPackage())
- {
- if (Module *pm = p->isPackageMod())
- s = pm;
- }
-
- ScopeDsymbol *sds = s->isScopeDsymbol();
- if (!sds || sds->isTemplateDeclaration())
- {
- e->error("%s %s has no members", s->kind(), s->toChars());
- return new ErrorExp();
- }
-
- // use a struct as local function
- struct PushIdentsDg
- {
- ScopeDsymbol *sds;
- Identifiers *idents;
-
- static int dg(void *ctx, size_t, Dsymbol *sm)
- {
- if (!sm)
- return 1;
-
- // skip local symbols, such as static foreach loop variables
- if (Declaration *decl = sm->isDeclaration())
- {
- if (decl->storage_class & STClocal)
- {
- return 0;
- }
- }
-
- // https://issues.dlang.org/show_bug.cgi?id=20915
- // skip version and debug identifiers
- if (sm->isVersionSymbol() || sm->isDebugSymbol())
- return 0;
-
- //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars());
- if (sm->ident)
- {
- // https://issues.dlang.org/show_bug.cgi?id=10096
- // https://issues.dlang.org/show_bug.cgi?id=10100
- // Skip over internal members in __traits(allMembers)
- if ((sm->isCtorDeclaration() && sm->ident != Id::ctor) ||
- (sm->isDtorDeclaration() && sm->ident != Id::dtor) ||
- (sm->isPostBlitDeclaration() && sm->ident != Id::postblit) ||
- sm->isInvariantDeclaration() ||
- sm->isUnitTestDeclaration())
- {
- return 0;
- }
-
- if (sm->ident == Id::empty)
- {
- return 0;
- }
- if (sm->isTypeInfoDeclaration()) // Bugzilla 15177
- return 0;
- PushIdentsDg *pid = (PushIdentsDg *)ctx;
- if (!pid->sds->isModule() && sm->isImport()) // Bugzilla 17057
- return 0;
-
- //printf("\t%s\n", sm->ident->toChars());
- Identifiers *idents = pid->idents;
-
- /* Skip if already present in idents[]
- */
- for (size_t j = 0; j < idents->length; j++)
- {
- Identifier *id = (*idents)[j];
- if (id == sm->ident)
- return 0;
- }
-
- idents->push(sm->ident);
- }
- else
- {
- EnumDeclaration *ed = sm->isEnumDeclaration();
- if (ed)
- {
- ScopeDsymbol_foreach(NULL, ed->members, &PushIdentsDg::dg, ctx);
- }
- }
- return 0;
- }
- };
-
- Identifiers *idents = new Identifiers;
- PushIdentsDg ctx;
- ctx.sds = sds;
- ctx.idents = idents;
- ScopeDsymbol_foreach(sc, sds->members, &PushIdentsDg::dg, &ctx);
- ClassDeclaration *cd = sds->isClassDeclaration();
- if (cd && e->ident == Id::allMembers)
- {
- if (cd->semanticRun < PASSsemanticdone)
- dsymbolSemantic(cd, NULL); // Bugzilla 13668: Try to resolve forward reference
-
- struct PushBaseMembers
- {
- static void dg(ClassDeclaration *cd, PushIdentsDg *ctx)
- {
- for (size_t i = 0; i < cd->baseclasses->length; i++)
- {
- ClassDeclaration *cb = (*cd->baseclasses)[i]->sym;
- assert(cb);
- ScopeDsymbol_foreach(NULL, cb->members, &PushIdentsDg::dg, ctx);
- if (cb->baseclasses->length)
- dg(cb, ctx);
- }
- }
- };
- PushBaseMembers::dg(cd, &ctx);
- }
-
- // Turn Identifiers into StringExps reusing the allocated array
- assert(sizeof(Expressions) == sizeof(Identifiers));
- Expressions *exps = (Expressions *)idents;
- for (size_t i = 0; i < idents->length; i++)
- {
- Identifier *id = (*idents)[i];
- StringExp *se = new StringExp(e->loc, const_cast<char *>(id->toChars()));
- (*exps)[i] = se;
- }
-
- /* Making this a tuple is more flexible, as it can be statically unrolled.
- * To make an array literal, enclose __traits in [ ]:
- * [ __traits(allMembers, ...) ]
- */
- Expression *ex = new TupleExp(e->loc, exps);
- ex = expressionSemantic(ex, sc);
- return ex;
- }
- else if (e->ident == Id::compiles)
- {
- /* Determine if all the objects - types, expressions, or symbols -
- * compile without error
- */
- if (!dim)
- return False(e);
-
- for (size_t i = 0; i < dim; i++)
- {
- unsigned errors = global.startGagging();
- Scope *sc2 = sc->push();
- sc2->tinst = NULL;
- sc2->minst = NULL;
- sc2->flags = (sc->flags & ~(SCOPEctfe | SCOPEcondition)) | SCOPEcompile | SCOPEfullinst;
- bool err = false;
-
- RootObject *o = (*e->args)[i];
- Type *t = isType(o);
- while (t)
- {
- if (TypeMixin *tm = t->isTypeMixin())
- {
- /* The mixin string could be a type or an expression.
- * Have to try compiling it to see.
- */
- OutBuffer buf;
- if (expressionsToString(buf, sc, tm->exps))
- {
- err = true;
- break;
- }
- const size_t len = buf.length();
- const char *str = buf.extractChars();
- Parser p(e->loc, sc->_module, (const utf8_t *)str, len, false);
- p.nextToken();
- //printf("p.loc.linnum = %d\n", p.loc.linnum);
-
- o = p.parseTypeOrAssignExp(TOKeof);
- if (p.errors || p.token.value != TOKeof)
- {
- err = true;
- break;
- }
- t = isType(o);
- }
- else
- break;
- }
-
- if (!err)
- {
- Expression *ex = t ? typeToExpression(t) : isExpression(o);
- if (!ex && t)
- {
- Dsymbol *s;
- t->resolve(e->loc, sc2, &ex, &t, &s);
- if (t)
- {
- typeSemantic(t, e->loc, sc2);
- if (t->ty == Terror)
- err = true;
- }
- else if (s && s->errors)
- err = true;
- }
- if (ex)
- {
- ex = expressionSemantic(ex, sc2);
- ex = resolvePropertiesOnly(sc2, ex);
- ex = ex->optimize(WANTvalue);
- if (sc2->func && sc2->func->type->ty == Tfunction)
- {
- TypeFunction *tf = (TypeFunction *)sc2->func->type;
- canThrow(ex, sc2->func, tf->isnothrow);
- }
- ex = checkGC(sc2, ex);
- if (ex->op == TOKerror)
- err = true;
- }
- }
-
- // Carefully detach the scope from the parent and throw it away as
- // we only need it to evaluate the expression
- // https://issues.dlang.org/show_bug.cgi?id=15428
- freeFieldinit(sc2);
- sc2->enclosing = NULL;
- sc2->pop();
-
- if (global.endGagging(errors) || err)
- {
- return False(e);
- }
- }
- return True(e);
- }
- else if (e->ident == Id::isSame)
- {
- /* Determine if two symbols are the same
- */
- if (dim != 2)
- return dimError(e, 2, dim);
-
- if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 0))
- return new ErrorExp();
-
- RootObject *o1 = (*e->args)[0];
- RootObject *o2 = (*e->args)[1];
-
- // issue 12001, allow isSame, <BasicType>, <BasicType>
- Type *t1 = isType(o1);
- Type *t2 = isType(o2);
- if (t1 && t2 && t1->equals(t2))
- return True(e);
-
- Dsymbol *s1 = getDsymbol(o1);
- Dsymbol *s2 = getDsymbol(o2);
- //printf("isSame: %s, %s\n", o1->toChars(), o2->toChars());
- if (!s1 && !s2)
- {
- Expression *ea1 = isExpression(o1);
- Expression *ea2 = isExpression(o2);
- if (ea1 && ea2)
- {
- if (ea1->equals(ea2))
- return True(e);
- }
- }
- if (!s1 || !s2)
- return False(e);
- s1 = s1->toAlias();
- s2 = s2->toAlias();
-
- if (s1->isFuncAliasDeclaration())
- s1 = ((FuncAliasDeclaration *)s1)->toAliasFunc();
- if (s2->isFuncAliasDeclaration())
- s2 = ((FuncAliasDeclaration *)s2)->toAliasFunc();
-
- return (s1 == s2) ? True(e) : False(e);
- }
- else if (e->ident == Id::getUnitTests)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbolWithoutExpCtx(o);
- if (!s)
- {
- e->error("argument %s to __traits(getUnitTests) must be a module or aggregate",
- o->toChars());
- return new ErrorExp();
- }
- if (Import *imp = s->isImport()) // Bugzilla 10990
- s = imp->mod;
-
- ScopeDsymbol* sds = s->isScopeDsymbol();
- if (!sds)
- {
- e->error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s",
- s->toChars(), s->kind());
- return new ErrorExp();
- }
-
- Expressions *exps = new Expressions();
- if (global.params.useUnitTests)
- {
- // Should actually be a set
- AA* uniqueUnitTests = NULL;
- collectUnitTests(sds->members, uniqueUnitTests, exps);
- }
- TupleExp *te= new TupleExp(e->loc, exps);
- return expressionSemantic(te, sc);
- }
- else if (e->ident == Id::getVirtualIndex)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Dsymbol *s = getDsymbolWithoutExpCtx(o);
-
- FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL;
- if (!fd)
- {
- e->error("first argument to __traits(getVirtualIndex) must be a function");
- return new ErrorExp();
- }
-
- fd = fd->toAliasFunc(); // Neccessary to support multiple overloads.
- return new IntegerExp(e->loc, fd->vtblIndex, Type::tptrdiff_t);
- }
- else if (e->ident == Id::getPointerBitmap)
- {
- return pointerBitmap(e);
- }
- else if (e->ident == Id::isZeroInit)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- RootObject *o = (*e->args)[0];
- Type *t = isType(o);
- if (!t)
- {
- e->error("type expected as second argument of __traits `%s` instead of `%s`",
- e->ident->toChars(), o->toChars());
- return new ErrorExp();
- }
-
- Type *tb = t->baseElemOf();
- return tb->isZeroInit(e->loc) ? True(e) : False(e);
- }
- else if (e->ident == Id::getTargetInfo)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
-
- Expression *ex = isExpression((*e->args)[0]);
- StringExp *se = ex ? ex->ctfeInterpret()->toStringExp() : NULL;
- if (!ex || !se || se->len == 0)
- {
- e->error("string expected as argument of __traits `%s` instead of `%s`", e->ident->toChars(), ex->toChars());
- return new ErrorExp();
- }
- se = se->toUTF8(sc);
-
- Expression *r = target.getTargetInfo(se->toPtr(), e->loc);
- if (!r)
- {
- e->error("`getTargetInfo` key `\"%s\"` not supported by this implementation", se->toPtr());
- return new ErrorExp();
- }
- return expressionSemantic(r, sc);
- }
- else if (e->ident == Id::getLocation)
- {
- if (dim != 1)
- return dimError(e, 1, dim);
- RootObject *arg0 = (*e->args)[0];
- Dsymbol *s = getDsymbolWithoutExpCtx(arg0);
- if (!s || !s->loc.filename)
- {
- e->error("can only get the location of a symbol, not `%s`", arg0->toChars());
- return new ErrorExp();
- }
-
- const FuncDeclaration *fd = s->isFuncDeclaration();
- if (fd && fd->overnext)
- {
- e->error("cannot get location of an overload set, "
- "use `__traits(getOverloads, ..., \"%s\"%s)[N]` "
- "to get the Nth overload",
- arg0->toChars(), "");
- return new ErrorExp();
- }
-
- Expressions *exps = new Expressions();
- exps->setDim(3);
- (*exps)[0] = new StringExp(e->loc, const_cast<char *>(s->loc.filename), strlen(s->loc.filename));
- (*exps)[1] = new IntegerExp(e->loc, s->loc.linnum, Type::tint32);
- (*exps)[2] = new IntegerExp(e->loc, s->loc.charnum, Type::tint32);
- TupleExp *tup = new TupleExp(e->loc, exps);
- return expressionSemantic(tup, sc);
- }
-
- if (const char *sub = (const char *)speller(e->ident->toChars(), &trait_search_fp, NULL, idchars))
- e->error("unrecognized trait `%s`, did you mean `%s`?", e->ident->toChars(), sub);
- else
- e->error("unrecognized trait `%s`", e->ident->toChars());
- return new ErrorExp();
-
- e->error("wrong number of arguments %d", (int)dim);
- return new ErrorExp();
-}
diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d
new file mode 100644
index 00000000000..8f968ed0dc2
--- /dev/null
+++ b/gcc/d/dmd/traits.d
@@ -0,0 +1,2202 @@
+/**
+ * Handle introspection functionality of the `__traits()` construct.
+ *
+ * Specification: $(LINK2 https://dlang.org/spec/traits.html, Traits)
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/traits.d, _traits.d)
+ * Documentation: https://dlang.org/phobos/dmd_traits.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/traits.d
+ */
+
+module dmd.traits;
+
+import core.stdc.stdio;
+
+import dmd.aggregate;
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.attrib;
+import dmd.canthrow;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dimport;
+import dmd.dmangle;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.mtype;
+import dmd.nogc;
+import dmd.parse;
+import dmd.root.array;
+import dmd.root.speller;
+import dmd.root.stringtable;
+import dmd.target;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.visitor;
+import dmd.root.rootobject;
+import dmd.root.outbuffer;
+import dmd.root.string;
+
+enum LOGSEMANTIC = false;
+
+/************************ TraitsExp ************************************/
+
+/**************************************
+ * Convert `Expression` or `Type` to corresponding `Dsymbol`, additionally
+ * stripping off expression contexts.
+ *
+ * Some symbol related `__traits` ignore arguments expression contexts.
+ * For example:
+ * ----
+ * struct S { void f() {} }
+ * S s;
+ * pragma(msg, __traits(isNested, s.f));
+ * // s.f is `DotVarExp`, but `__traits(isNested)`` needs a `FuncDeclaration`.
+ * ----
+ *
+ * This is used for that common `__traits` behavior.
+ *
+ * Input:
+ * oarg object to get the symbol for
+ * Returns:
+ * Dsymbol the corresponding symbol for oarg
+ */
+private Dsymbol getDsymbolWithoutExpCtx(RootObject oarg)
+{
+ if (auto e = isExpression(oarg))
+ {
+ if (e.op == TOK.dotVariable)
+ return (cast(DotVarExp)e).var;
+ if (e.op == TOK.dotTemplateDeclaration)
+ return (cast(DotTemplateExp)e).td;
+ }
+ return getDsymbol(oarg);
+}
+
+private const StringTable!bool traitsStringTable;
+
+shared static this()
+{
+ static immutable string[] names =
+ [
+ "isAbstractClass",
+ "isArithmetic",
+ "isAssociativeArray",
+ "isDisabled",
+ "isDeprecated",
+ "isFuture",
+ "isFinalClass",
+ "isPOD",
+ "isNested",
+ "isFloating",
+ "isIntegral",
+ "isScalar",
+ "isStaticArray",
+ "isUnsigned",
+ "isVirtualFunction",
+ "isVirtualMethod",
+ "isAbstractFunction",
+ "isFinalFunction",
+ "isOverrideFunction",
+ "isStaticFunction",
+ "isModule",
+ "isPackage",
+ "isRef",
+ "isOut",
+ "isLazy",
+ "isReturnOnStack",
+ "hasMember",
+ "identifier",
+ "getProtection",
+ "getVisibility",
+ "parent",
+ "child",
+ "getLinkage",
+ "getMember",
+ "getOverloads",
+ "getVirtualFunctions",
+ "getVirtualMethods",
+ "classInstanceSize",
+ "allMembers",
+ "derivedMembers",
+ "isSame",
+ "compiles",
+ "getAliasThis",
+ "getAttributes",
+ "getFunctionAttributes",
+ "getFunctionVariadicStyle",
+ "getParameterStorageClasses",
+ "getUnitTests",
+ "getVirtualIndex",
+ "getPointerBitmap",
+ "isZeroInit",
+ "getTargetInfo",
+ "getLocation",
+ "hasPostblit",
+ "hasCopyConstructor",
+ "isCopyable",
+ ];
+
+ StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable;
+ stringTable._init(names.length);
+
+ foreach (s; names)
+ {
+ auto sv = stringTable.insert(s, true);
+ assert(sv);
+ }
+}
+
+/**
+ * get an array of size_t values that indicate possible pointer words in memory
+ * if interpreted as the type given as argument
+ * Returns: the size of the type in bytes, d_uns64.max on error
+ */
+d_uns64 getTypePointerBitmap(Loc loc, Type t, Array!(d_uns64)* data)
+{
+ d_uns64 sz;
+ if (t.ty == Tclass && !(cast(TypeClass)t).sym.isInterfaceDeclaration())
+ sz = (cast(TypeClass)t).sym.AggregateDeclaration.size(loc);
+ else
+ sz = t.size(loc);
+ if (sz == SIZE_INVALID)
+ return d_uns64.max;
+
+ const sz_size_t = Type.tsize_t.size(loc);
+ if (sz > sz.max - sz_size_t)
+ {
+ error(loc, "size overflow for type `%s`", t.toChars());
+ return d_uns64.max;
+ }
+
+ d_uns64 bitsPerWord = sz_size_t * 8;
+ d_uns64 cntptr = (sz + sz_size_t - 1) / sz_size_t;
+ d_uns64 cntdata = (cntptr + bitsPerWord - 1) / bitsPerWord;
+
+ data.setDim(cast(size_t)cntdata);
+ data.zero();
+
+ extern (C++) final class PointerBitmapVisitor : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ extern (D) this(Array!(d_uns64)* _data, d_uns64 _sz_size_t)
+ {
+ this.data = _data;
+ this.sz_size_t = _sz_size_t;
+ }
+
+ void setpointer(d_uns64 off)
+ {
+ d_uns64 ptroff = off / sz_size_t;
+ (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t));
+ }
+
+ override void visit(Type t)
+ {
+ Type tb = t.toBasetype();
+ if (tb != t)
+ tb.accept(this);
+ }
+
+ override void visit(TypeError t)
+ {
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeNext t)
+ {
+ assert(0);
+ }
+
+ override void visit(TypeBasic t)
+ {
+ if (t.ty == Tvoid)
+ setpointer(offset);
+ }
+
+ override void visit(TypeVector t)
+ {
+ }
+
+ override void visit(TypeArray t)
+ {
+ assert(0);
+ }
+
+ override void visit(TypeSArray t)
+ {
+ d_uns64 arrayoff = offset;
+ d_uns64 nextsize = t.next.size();
+ if (nextsize == SIZE_INVALID)
+ error = true;
+ d_uns64 dim = t.dim.toInteger();
+ for (d_uns64 i = 0; i < dim; i++)
+ {
+ offset = arrayoff + i * nextsize;
+ t.next.accept(this);
+ }
+ offset = arrayoff;
+ }
+
+ override void visit(TypeDArray t)
+ {
+ setpointer(offset + sz_size_t);
+ }
+
+ // dynamic array is {length,ptr}
+ override void visit(TypeAArray t)
+ {
+ setpointer(offset);
+ }
+
+ override void visit(TypePointer t)
+ {
+ if (t.nextOf().ty != Tfunction) // don't mark function pointers
+ setpointer(offset);
+ }
+
+ override void visit(TypeReference t)
+ {
+ setpointer(offset);
+ }
+
+ override void visit(TypeClass t)
+ {
+ setpointer(offset);
+ }
+
+ override void visit(TypeFunction t)
+ {
+ }
+
+ override void visit(TypeDelegate t)
+ {
+ setpointer(offset);
+ }
+
+ // delegate is {context, function}
+ override void visit(TypeQualified t)
+ {
+ assert(0);
+ }
+
+ // assume resolved
+ override void visit(TypeIdentifier t)
+ {
+ assert(0);
+ }
+
+ override void visit(TypeInstance t)
+ {
+ assert(0);
+ }
+
+ override void visit(TypeTypeof t)
+ {
+ assert(0);
+ }
+
+ override void visit(TypeReturn t)
+ {
+ assert(0);
+ }
+
+ override void visit(TypeEnum t)
+ {
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeTuple t)
+ {
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeSlice t)
+ {
+ assert(0);
+ }
+
+ override void visit(TypeNull t)
+ {
+ // always a null pointer
+ }
+
+ override void visit(TypeStruct t)
+ {
+ d_uns64 structoff = offset;
+ foreach (v; t.sym.fields)
+ {
+ offset = structoff + v.offset;
+ if (v.type.ty == Tclass)
+ setpointer(offset);
+ else
+ v.type.accept(this);
+ }
+ offset = structoff;
+ }
+
+ // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references
+ void visitClass(TypeClass t)
+ {
+ d_uns64 classoff = offset;
+ // skip vtable-ptr and monitor
+ if (t.sym.baseClass)
+ visitClass(cast(TypeClass)t.sym.baseClass.type);
+ foreach (v; t.sym.fields)
+ {
+ offset = classoff + v.offset;
+ v.type.accept(this);
+ }
+ offset = classoff;
+ }
+
+ Array!(d_uns64)* data;
+ d_uns64 offset;
+ d_uns64 sz_size_t;
+ bool error;
+ }
+
+ scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(data, sz_size_t);
+ if (t.ty == Tclass)
+ pbv.visitClass(cast(TypeClass)t);
+ else
+ t.accept(pbv);
+ return pbv.error ? d_uns64.max : sz;
+}
+
+/**
+ * get an array of size_t values that indicate possible pointer words in memory
+ * if interpreted as the type given as argument
+ * the first array element is the size of the type for independent interpretation
+ * of the array
+ * following elements bits represent one word (4/8 bytes depending on the target
+ * architecture). If set the corresponding memory might contain a pointer/reference.
+ *
+ * Returns: [T.sizeof, pointerbit0-31/63, pointerbit32/64-63/128, ...]
+ */
+private Expression pointerBitmap(TraitsExp e)
+{
+ if (!e.args || e.args.dim != 1)
+ {
+ error(e.loc, "a single type expected for trait pointerBitmap");
+ return ErrorExp.get();
+ }
+
+ Type t = getType((*e.args)[0]);
+ if (!t)
+ {
+ error(e.loc, "`%s` is not a type", (*e.args)[0].toChars());
+ return ErrorExp.get();
+ }
+
+ Array!(d_uns64) data;
+ d_uns64 sz = getTypePointerBitmap(e.loc, t, &data);
+ if (sz == d_uns64.max)
+ return ErrorExp.get();
+
+ auto exps = new Expressions(data.dim + 1);
+ (*exps)[0] = new IntegerExp(e.loc, sz, Type.tsize_t);
+ foreach (size_t i; 1 .. exps.dim)
+ (*exps)[i] = new IntegerExp(e.loc, data[cast(size_t) (i - 1)], Type.tsize_t);
+
+ auto ale = new ArrayLiteralExp(e.loc, Type.tsize_t.sarrayOf(data.dim + 1), exps);
+ return ale;
+}
+
+Expression semanticTraits(TraitsExp e, Scope* sc)
+{
+ static if (LOGSEMANTIC)
+ {
+ printf("TraitsExp::semantic() %s\n", e.toChars());
+ }
+
+ if (e.ident != Id.compiles &&
+ e.ident != Id.isSame &&
+ e.ident != Id.identifier &&
+ e.ident != Id.getProtection && e.ident != Id.getVisibility &&
+ e.ident != Id.getAttributes)
+ {
+ // Pretend we're in a deprecated scope so that deprecation messages
+ // aren't triggered when checking if a symbol is deprecated
+ const save = sc.stc;
+ if (e.ident == Id.isDeprecated)
+ sc.stc |= STC.deprecated_;
+ if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1))
+ {
+ sc.stc = save;
+ return ErrorExp.get();
+ }
+ sc.stc = save;
+ }
+ size_t dim = e.args ? e.args.dim : 0;
+
+ Expression dimError(int expected)
+ {
+ e.error("expected %d arguments for `%s` but had %d", expected, e.ident.toChars(), cast(int)dim);
+ return ErrorExp.get();
+ }
+
+ static IntegerExp True()
+ {
+ return IntegerExp.createBool(true);
+ }
+
+ static IntegerExp False()
+ {
+ return IntegerExp.createBool(false);
+ }
+
+ /********
+ * Gets the function type from a given AST node
+ * if the node is a function of some sort.
+ * Params:
+ * o = an AST node to check for a `TypeFunction`
+ * fdp = if `o` is a FuncDeclaration then fdp is set to that, otherwise `null`
+ * Returns:
+ * a type node if `o` is a declaration of
+ * a delegate, function, function-pointer or a variable of the former.
+ * Otherwise, `null`.
+ */
+ static TypeFunction toTypeFunction(RootObject o, out FuncDeclaration fdp)
+ {
+ Type t;
+ if (auto s = getDsymbolWithoutExpCtx(o))
+ {
+ if (auto fd = s.isFuncDeclaration())
+ {
+ t = fd.type;
+ fdp = fd;
+ }
+ else if (auto vd = s.isVarDeclaration())
+ t = vd.type;
+ else
+ t = isType(o);
+ }
+ else
+ t = isType(o);
+
+ if (t)
+ {
+ if (auto tf = t.isFunction_Delegate_PtrToFunction())
+ return tf;
+ }
+
+ return null;
+ }
+
+ IntegerExp isX(T)(bool delegate(T) fp)
+ {
+ if (!dim)
+ return False();
+ foreach (o; *e.args)
+ {
+ static if (is(T == Type))
+ auto y = getType(o);
+
+ static if (is(T : Dsymbol))
+ {
+ auto s = getDsymbolWithoutExpCtx(o);
+ if (!s)
+ return False();
+ }
+ static if (is(T == Dsymbol))
+ alias y = s;
+ static if (is(T == Declaration))
+ auto y = s.isDeclaration();
+ static if (is(T == FuncDeclaration))
+ auto y = s.isFuncDeclaration();
+
+ if (!y || !fp(y))
+ return False();
+ }
+ return True();
+ }
+
+ alias isTypeX = isX!Type;
+ alias isDsymX = isX!Dsymbol;
+ alias isDeclX = isX!Declaration;
+ alias isFuncX = isX!FuncDeclaration;
+
+ Expression isPkgX(bool function(Package) fp)
+ {
+ return isDsymX((Dsymbol sym) {
+ Package p = resolveIsPackage(sym);
+ return (p !is null) && fp(p);
+ });
+ }
+
+ if (e.ident == Id.isArithmetic)
+ {
+ return isTypeX(t => t.isintegral() || t.isfloating());
+ }
+ if (e.ident == Id.isFloating)
+ {
+ return isTypeX(t => t.isfloating());
+ }
+ if (e.ident == Id.isIntegral)
+ {
+ return isTypeX(t => t.isintegral());
+ }
+ if (e.ident == Id.isScalar)
+ {
+ return isTypeX(t => t.isscalar());
+ }
+ if (e.ident == Id.isUnsigned)
+ {
+ return isTypeX(t => t.isunsigned());
+ }
+ if (e.ident == Id.isAssociativeArray)
+ {
+ return isTypeX(t => t.toBasetype().ty == Taarray);
+ }
+ if (e.ident == Id.isDeprecated)
+ {
+ if (global.params.vcomplex)
+ {
+ if (isTypeX(t => t.iscomplex() || t.isimaginary()).isBool(true))
+ return True();
+ }
+ return isDsymX(t => t.isDeprecated());
+ }
+ if (e.ident == Id.isFuture)
+ {
+ return isDeclX(t => t.isFuture());
+ }
+ if (e.ident == Id.isStaticArray)
+ {
+ return isTypeX(t => t.toBasetype().ty == Tsarray);
+ }
+ if (e.ident == Id.isAbstractClass)
+ {
+ return isTypeX(t => t.toBasetype().ty == Tclass &&
+ (cast(TypeClass)t.toBasetype()).sym.isAbstract());
+ }
+ if (e.ident == Id.isFinalClass)
+ {
+ return isTypeX(t => t.toBasetype().ty == Tclass &&
+ ((cast(TypeClass)t.toBasetype()).sym.storage_class & STC.final_) != 0);
+ }
+ if (e.ident == Id.isTemplate)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isDsymX((s)
+ {
+ if (!s.toAlias().isOverloadable())
+ return false;
+ return overloadApply(s,
+ sm => sm.isTemplateDeclaration() !is null) != 0;
+ });
+ }
+ if (e.ident == Id.isPOD)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto t = isType(o);
+ if (!t)
+ {
+ e.error("type expected as second argument of __traits `%s` instead of `%s`",
+ e.ident.toChars(), o.toChars());
+ return ErrorExp.get();
+ }
+
+ Type tb = t.baseElemOf();
+ if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
+ {
+ return sd.isPOD() ? True() : False();
+ }
+ return True();
+ }
+ if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto t = isType(o);
+ if (!t)
+ {
+ e.error("type expected as second argument of __traits `%s` instead of `%s`",
+ e.ident.toChars(), o.toChars());
+ return ErrorExp.get();
+ }
+
+ Type tb = t.baseElemOf();
+ if (auto sd = tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null)
+ {
+ return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False())
+ : (sd.hasCopyCtor ? True() : False());
+ }
+ return False();
+ }
+ if (e.ident == Id.isCopyable)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto t = isType(o);
+ if (!t)
+ {
+ e.error("type expected as second argument of __traits `%s` instead of `%s`",
+ e.ident.toChars(), o.toChars());
+ return ErrorExp.get();
+ }
+
+ t = t.toBasetype(); // get the base in case `t` is an `enum`
+
+ if (auto ts = t.isTypeStruct())
+ {
+ ts.sym.dsymbolSemantic(sc);
+ }
+
+ return isCopyable(t) ? True() : False();
+ }
+
+ if (e.ident == Id.isNested)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbolWithoutExpCtx(o);
+ if (!s)
+ {
+ }
+ else if (auto ad = s.isAggregateDeclaration())
+ {
+ return ad.isNested() ? True() : False();
+ }
+ else if (auto fd = s.isFuncDeclaration())
+ {
+ return fd.isNested() ? True() : False();
+ }
+
+ e.error("aggregate or function expected instead of `%s`", o.toChars());
+ return ErrorExp.get();
+ }
+ if (e.ident == Id.isDisabled)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isDeclX(f => f.isDisabled());
+ }
+ if (e.ident == Id.isAbstractFunction)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isFuncX(f => f.isAbstract());
+ }
+ if (e.ident == Id.isVirtualFunction)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isFuncX(f => f.isVirtual());
+ }
+ if (e.ident == Id.isVirtualMethod)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isFuncX(f => f.isVirtualMethod());
+ }
+ if (e.ident == Id.isFinalFunction)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isFuncX(f => f.isFinalFunc());
+ }
+ if (e.ident == Id.isOverrideFunction)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isFuncX(f => f.isOverride());
+ }
+ if (e.ident == Id.isStaticFunction)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isFuncX(f => !f.needThis() && !f.isNested());
+ }
+ if (e.ident == Id.isModule)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isPkgX(p => p.isModule() || p.isPackageMod());
+ }
+ if (e.ident == Id.isPackage)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isPkgX(p => p.isModule() is null);
+ }
+ if (e.ident == Id.isRef)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isDeclX(d => d.isRef());
+ }
+ if (e.ident == Id.isOut)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isDeclX(d => d.isOut());
+ }
+ if (e.ident == Id.isLazy)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ return isDeclX(d => (d.storage_class & STC.lazy_) != 0);
+ }
+ if (e.ident == Id.identifier)
+ {
+ // Get identifier for symbol as a string literal
+ /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
+ * a symbol should not be folded to a constant.
+ * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
+ */
+ if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2))
+ return ErrorExp.get();
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ Identifier id;
+ if (auto po = isParameter(o))
+ {
+ if (!po.ident)
+ {
+ e.error("argument `%s` has no identifier", po.type.toChars());
+ return ErrorExp.get();
+ }
+ id = po.ident;
+ }
+ else
+ {
+ Dsymbol s = getDsymbolWithoutExpCtx(o);
+ if (!s || !s.ident)
+ {
+ e.error("argument `%s` has no identifier", o.toChars());
+ return ErrorExp.get();
+ }
+ id = s.ident;
+ }
+
+ auto se = new StringExp(e.loc, id.toString());
+ return se.expressionSemantic(sc);
+ }
+ if (e.ident == Id.getProtection || e.ident == Id.getVisibility)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ Scope* sc2 = sc.push();
+ sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility;
+ bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
+ sc2.pop();
+ if (!ok)
+ return ErrorExp.get();
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbolWithoutExpCtx(o);
+ if (!s)
+ {
+ if (!isError(o))
+ e.error("argument `%s` has no visibility", o.toChars());
+ return ErrorExp.get();
+ }
+ if (s.semanticRun == PASS.init)
+ s.dsymbolSemantic(null);
+
+ auto protName = visibilityToString(s.visible().kind); // TODO: How about package(names)
+ assert(protName);
+ auto se = new StringExp(e.loc, protName);
+ return se.expressionSemantic(sc);
+ }
+ if (e.ident == Id.parent)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbolWithoutExpCtx(o);
+ if (s)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=12496
+ // Consider:
+ // class T1
+ // {
+ // class C(uint value) { }
+ // }
+ // __traits(parent, T1.C!2)
+ if (auto ad = s.isAggregateDeclaration()) // `s` is `C`
+ {
+ if (ad.isNested()) // `C` is nested
+ {
+ if (auto p = s.toParent()) // `C`'s parent is `C!2`, believe it or not
+ {
+ if (p.isTemplateInstance()) // `C!2` is a template instance
+ {
+ s = p; // `C!2`'s parent is `T1`
+ auto td = (cast(TemplateInstance)p).tempdecl;
+ if (td)
+ s = td; // get the declaration context just in case there's two contexts
+ }
+ }
+ }
+ }
+
+ if (auto fd = s.isFuncDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=8943
+ s = fd.toAliasFunc();
+ if (!s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=8922
+ s = s.toParent();
+ }
+ if (!s || s.isImport())
+ {
+ e.error("argument `%s` has no parent", o.toChars());
+ return ErrorExp.get();
+ }
+
+ if (auto f = s.isFuncDeclaration())
+ {
+ if (auto td = getFuncTemplateDecl(f))
+ {
+ if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+ td = td.overroot; // then get the start
+ Expression ex = new TemplateExp(e.loc, td, f);
+ ex = ex.expressionSemantic(sc);
+ return ex;
+ }
+ if (auto fld = f.isFuncLiteralDeclaration())
+ {
+ // Directly translate to VarExp instead of FuncExp
+ Expression ex = new VarExp(e.loc, fld, true);
+ return ex.expressionSemantic(sc);
+ }
+ }
+ return symbolToExp(s, e.loc, sc, false);
+ }
+ if (e.ident == Id.child)
+ {
+ if (dim != 2)
+ return dimError(2);
+
+ Expression ex;
+ auto op = (*e.args)[0];
+ if (auto symp = getDsymbol(op))
+ ex = new DsymbolExp(e.loc, symp);
+ else if (auto exp = op.isExpression())
+ ex = exp;
+ else
+ {
+ e.error("symbol or expression expected as first argument of __traits `child` instead of `%s`", op.toChars());
+ return ErrorExp.get();
+ }
+
+ ex = ex.expressionSemantic(sc);
+ auto oc = (*e.args)[1];
+ auto symc = getDsymbol(oc);
+ if (!symc)
+ {
+ e.error("symbol expected as second argument of __traits `child` instead of `%s`", oc.toChars());
+ return ErrorExp.get();
+ }
+
+ if (auto d = symc.isDeclaration())
+ ex = new DotVarExp(e.loc, ex, d);
+ else if (auto td = symc.isTemplateDeclaration())
+ ex = new DotExp(e.loc, ex, new TemplateExp(e.loc, td));
+ else if (auto ti = symc.isScopeDsymbol())
+ ex = new DotExp(e.loc, ex, new ScopeExp(e.loc, ti));
+ else
+ assert(0);
+
+ ex = ex.expressionSemantic(sc);
+ return ex;
+ }
+ if (e.ident == Id.toType)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto ex = isExpression((*e.args)[0]);
+ if (!ex)
+ {
+ e.error("expression expected as second argument of __traits `%s`", e.ident.toChars());
+ return ErrorExp.get();
+ }
+ ex = ex.ctfeInterpret();
+
+ StringExp se = semanticString(sc, ex, "__traits(toType, string)");
+ if (!se)
+ {
+ return ErrorExp.get();
+ }
+ Type t = decoToType(se.toUTF8(sc).peekString());
+ if (!t)
+ {
+ e.error("cannot determine `%s`", e.toChars());
+ return ErrorExp.get();
+ }
+ return (new TypeExp(e.loc, t)).expressionSemantic(sc);
+ }
+ if (e.ident == Id.hasMember ||
+ e.ident == Id.getMember ||
+ e.ident == Id.getOverloads ||
+ e.ident == Id.getVirtualMethods ||
+ e.ident == Id.getVirtualFunctions)
+ {
+ if (dim != 2 && !(dim == 3 && e.ident == Id.getOverloads))
+ return dimError(2);
+
+ auto o = (*e.args)[0];
+ auto ex = isExpression((*e.args)[1]);
+ if (!ex)
+ {
+ e.error("expression expected as second argument of __traits `%s`", e.ident.toChars());
+ return ErrorExp.get();
+ }
+ ex = ex.ctfeInterpret();
+
+ bool includeTemplates = false;
+ if (dim == 3 && e.ident == Id.getOverloads)
+ {
+ auto b = isExpression((*e.args)[2]);
+ b = b.ctfeInterpret();
+ if (!b.type.equals(Type.tbool))
+ {
+ e.error("`bool` expected as third argument of `__traits(getOverloads)`, not `%s` of type `%s`", b.toChars(), b.type.toChars());
+ return ErrorExp.get();
+ }
+ includeTemplates = b.isBool(true);
+ }
+
+ StringExp se = ex.toStringExp();
+ if (!se || se.len == 0)
+ {
+ e.error("string expected as second argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars());
+ return ErrorExp.get();
+ }
+ se = se.toUTF8(sc);
+
+ if (se.sz != 1)
+ {
+ e.error("string must be chars");
+ return ErrorExp.get();
+ }
+ auto id = Identifier.idPool(se.peekString());
+
+ /* Prefer a Type, because getDsymbol(Type) can lose type modifiers.
+ Then a Dsymbol, because it might need some runtime contexts.
+ */
+
+ Dsymbol sym = getDsymbol(o);
+ if (auto t = isType(o))
+ ex = typeDotIdExp(e.loc, t, id);
+ else if (sym)
+ {
+ if (e.ident == Id.hasMember)
+ {
+ if (auto sm = sym.search(e.loc, id))
+ return True();
+ }
+ ex = new DsymbolExp(e.loc, sym);
+ ex = new DotIdExp(e.loc, ex, id);
+ }
+ else if (auto ex2 = isExpression(o))
+ ex = new DotIdExp(e.loc, ex2, id);
+ else
+ {
+ e.error("invalid first argument");
+ return ErrorExp.get();
+ }
+
+ // ignore symbol visibility and disable access checks for these traits
+ Scope* scx = sc.push();
+ scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck;
+ scope (exit) scx.pop();
+
+ if (e.ident == Id.hasMember)
+ {
+ /* Take any errors as meaning it wasn't found
+ */
+ ex = ex.trySemantic(scx);
+ return ex ? True() : False();
+ }
+ else if (e.ident == Id.getMember)
+ {
+ if (ex.op == TOK.dotIdentifier)
+ // Prevent semantic() from replacing Symbol with its initializer
+ (cast(DotIdExp)ex).wantsym = true;
+ ex = ex.expressionSemantic(scx);
+ return ex;
+ }
+ else if (e.ident == Id.getVirtualFunctions ||
+ e.ident == Id.getVirtualMethods ||
+ e.ident == Id.getOverloads)
+ {
+ uint errors = global.errors;
+ Expression eorig = ex;
+ ex = ex.expressionSemantic(scx);
+ if (errors < global.errors)
+ e.error("`%s` cannot be resolved", eorig.toChars());
+
+ /* Create tuple of functions of ex
+ */
+ auto exps = new Expressions();
+ Dsymbol f;
+ if (auto ve = ex.isVarExp)
+ {
+ if (ve.var.isFuncDeclaration() || ve.var.isOverDeclaration())
+ f = ve.var;
+ ex = null;
+ }
+ else if (auto dve = ex.isDotVarExp)
+ {
+ if (dve.var.isFuncDeclaration() || dve.var.isOverDeclaration())
+ f = dve.var;
+ if (dve.e1.op == TOK.dotType || dve.e1.op == TOK.this_)
+ ex = null;
+ else
+ ex = dve.e1;
+ }
+ else if (auto te = ex.isTemplateExp)
+ {
+ auto td = te.td;
+ f = td;
+ if (td && td.funcroot)
+ f = td.funcroot;
+ ex = null;
+ }
+ else if (auto dte = ex.isDotTemplateExp)
+ {
+ auto td = dte.td;
+ f = td;
+ if (td && td.funcroot)
+ f = td.funcroot;
+ ex = null;
+ if (dte.e1.op != TOK.dotType && dte.e1.op != TOK.this_)
+ ex = dte.e1;
+ }
+ bool[string] funcTypeHash;
+
+ /* Compute the function signature and insert it in the
+ * hashtable, if not present. This is needed so that
+ * traits(getOverlods, F3, "visit") does not count `int visit(int)`
+ * twice in the following example:
+ *
+ * =============================================
+ * interface F1 { int visit(int);}
+ * interface F2 { int visit(int); void visit(); }
+ * interface F3 : F2, F1 {}
+ *==============================================
+ */
+ void insertInterfaceInheritedFunction(FuncDeclaration fd, Expression e)
+ {
+ auto signature = fd.type.toString();
+ //printf("%s - %s\n", fd.toChars, signature);
+ if (signature !in funcTypeHash)
+ {
+ funcTypeHash[signature] = true;
+ exps.push(e);
+ }
+ }
+
+ int dg(Dsymbol s)
+ {
+ auto fd = s.isFuncDeclaration();
+ if (!fd)
+ {
+ if (includeTemplates)
+ {
+ if (auto td = s.isTemplateDeclaration())
+ {
+ // if td is part of an overload set we must take a copy
+ // which shares the same `instances` cache but without
+ // `overroot` and `overnext` set to avoid overload
+ // behaviour in the result.
+ if (td.overnext !is null)
+ {
+ if (td.instances is null)
+ {
+ // create an empty AA just to copy it
+ scope ti = new TemplateInstance(Loc.initial, Id.empty, null);
+ auto tib = TemplateInstanceBox(ti);
+ td.instances[tib] = null;
+ td.instances.clear();
+ }
+ td = td.syntaxCopy(null);
+ import core.stdc.string : memcpy;
+ memcpy(cast(void*) td, cast(void*) s,
+ __traits(classInstanceSize, TemplateDeclaration));
+ td.overroot = null;
+ td.overnext = null;
+ }
+
+ auto e = ex ? new DotTemplateExp(Loc.initial, ex, td)
+ : new DsymbolExp(Loc.initial, td);
+ exps.push(e);
+ }
+ }
+ return 0;
+ }
+ if (e.ident == Id.getVirtualFunctions && !fd.isVirtual())
+ return 0;
+ if (e.ident == Id.getVirtualMethods && !fd.isVirtualMethod())
+ return 0;
+
+ auto fa = new FuncAliasDeclaration(fd.ident, fd, false);
+ fa.visibility = fd.visibility;
+
+ auto e = ex ? new DotVarExp(Loc.initial, ex, fa, false)
+ : new DsymbolExp(Loc.initial, fa, false);
+
+ // if the parent is an interface declaration
+ // we must check for functions with the same signature
+ // in different inherited interfaces
+ if (sym && sym.isInterfaceDeclaration())
+ insertInterfaceInheritedFunction(fd, e);
+ else
+ exps.push(e);
+ return 0;
+ }
+
+ InterfaceDeclaration ifd = null;
+ if (sym)
+ ifd = sym.isInterfaceDeclaration();
+ // If the symbol passed as a parameter is an
+ // interface that inherits other interfaces
+ overloadApply(f, &dg);
+ if (ifd && ifd.interfaces && f)
+ {
+ // check the overloads of each inherited interface individually
+ foreach (bc; ifd.interfaces)
+ {
+ if (auto fd = bc.sym.search(e.loc, f.ident))
+ overloadApply(fd, &dg);
+ }
+ }
+
+ auto tup = new TupleExp(e.loc, exps);
+ return tup.expressionSemantic(scx);
+ }
+ else
+ assert(0);
+ }
+ if (e.ident == Id.classInstanceSize)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbol(o);
+ auto cd = s ? s.isClassDeclaration() : null;
+ if (!cd)
+ {
+ e.error("first argument is not a class");
+ return ErrorExp.get();
+ }
+ if (cd.sizeok != Sizeok.done)
+ {
+ cd.size(e.loc);
+ }
+ if (cd.sizeok != Sizeok.done)
+ {
+ e.error("%s `%s` is forward referenced", cd.kind(), cd.toChars());
+ return ErrorExp.get();
+ }
+
+ return new IntegerExp(e.loc, cd.structsize, Type.tsize_t);
+ }
+ if (e.ident == Id.getAliasThis)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbol(o);
+ auto ad = s ? s.isAggregateDeclaration() : null;
+
+ auto exps = new Expressions();
+ if (ad && ad.aliasthis)
+ exps.push(new StringExp(e.loc, ad.aliasthis.ident.toString()));
+ Expression ex = new TupleExp(e.loc, exps);
+ ex = ex.expressionSemantic(sc);
+ return ex;
+ }
+ if (e.ident == Id.getAttributes)
+ {
+ /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
+ * a symbol should not be folded to a constant.
+ * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
+ */
+ if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3))
+ return ErrorExp.get();
+
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto po = isParameter(o);
+ auto s = getDsymbolWithoutExpCtx(o);
+ UserAttributeDeclaration udad = null;
+ if (po)
+ {
+ udad = po.userAttribDecl;
+ }
+ else if (s)
+ {
+ if (s.isImport())
+ {
+ s = s.isImport().mod;
+ }
+ //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope);
+ udad = s.userAttribDecl;
+ }
+ else
+ {
+ version (none)
+ {
+ Expression x = isExpression(o);
+ Type t = isType(o);
+ if (x)
+ printf("e = %s %s\n", Token.toChars(x.op), x.toChars());
+ if (t)
+ printf("t = %d %s\n", t.ty, t.toChars());
+ }
+ e.error("first argument is not a symbol");
+ return ErrorExp.get();
+ }
+
+ auto exps = udad ? udad.getAttributes() : new Expressions();
+ auto tup = new TupleExp(e.loc, exps);
+ return tup.expressionSemantic(sc);
+ }
+ if (e.ident == Id.getFunctionAttributes)
+ {
+ /* Extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs.
+ * https://dlang.org/spec/traits.html#getFunctionAttributes
+ */
+ if (dim != 1)
+ return dimError(1);
+
+ FuncDeclaration fd;
+ TypeFunction tf = toTypeFunction((*e.args)[0], fd);
+
+ if (!tf)
+ {
+ e.error("first argument is not a function");
+ return ErrorExp.get();
+ }
+
+ auto mods = new Expressions();
+
+ void addToMods(string str)
+ {
+ mods.push(new StringExp(Loc.initial, str));
+ }
+ tf.modifiersApply(&addToMods);
+ tf.attributesApply(&addToMods, TRUSTformatSystem);
+
+ auto tup = new TupleExp(e.loc, mods);
+ return tup.expressionSemantic(sc);
+ }
+ if (e.ident == Id.isReturnOnStack)
+ {
+ /* Extract as a boolean if function return value is on the stack
+ * https://dlang.org/spec/traits.html#isReturnOnStack
+ */
+ if (dim != 1)
+ return dimError(1);
+
+ RootObject o = (*e.args)[0];
+ FuncDeclaration fd;
+ TypeFunction tf = toTypeFunction(o, fd);
+
+ if (!tf)
+ {
+ e.error("argument to `__traits(isReturnOnStack, %s)` is not a function", o.toChars());
+ return ErrorExp.get();
+ }
+
+ bool value = target.isReturnOnStack(tf, fd && fd.needThis());
+ return IntegerExp.createBool(value);
+ }
+ if (e.ident == Id.getFunctionVariadicStyle)
+ {
+ /* Accept a symbol or a type. Returns one of the following:
+ * "none" not a variadic function
+ * "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments`
+ * "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg
+ * "typesafe" void typesafe(T[] ...)
+ */
+ // get symbol linkage as a string
+ if (dim != 1)
+ return dimError(1);
+
+ LINK link;
+ VarArg varargs;
+ auto o = (*e.args)[0];
+
+ FuncDeclaration fd;
+ TypeFunction tf = toTypeFunction(o, fd);
+
+ if (tf)
+ {
+ link = tf.linkage;
+ varargs = tf.parameterList.varargs;
+ }
+ else
+ {
+ if (!fd)
+ {
+ e.error("argument to `__traits(getFunctionVariadicStyle, %s)` is not a function", o.toChars());
+ return ErrorExp.get();
+ }
+ link = fd.linkage;
+ varargs = fd.getParameterList().varargs;
+ }
+ string style;
+ final switch (varargs)
+ {
+ case VarArg.none: style = "none"; break;
+ case VarArg.variadic: style = (link == LINK.d)
+ ? "argptr"
+ : "stdarg"; break;
+ case VarArg.typesafe: style = "typesafe"; break;
+ }
+ auto se = new StringExp(e.loc, style);
+ return se.expressionSemantic(sc);
+ }
+ if (e.ident == Id.getParameterStorageClasses)
+ {
+ /* Accept a function symbol or a type, followed by a parameter index.
+ * Returns a tuple of strings of the parameter's storage classes.
+ */
+ // get symbol linkage as a string
+ if (dim != 2)
+ return dimError(2);
+
+ auto o = (*e.args)[0];
+ auto o1 = (*e.args)[1];
+
+ FuncDeclaration fd;
+ TypeFunction tf = toTypeFunction(o, fd);
+
+ ParameterList fparams;
+ if (tf)
+ fparams = tf.parameterList;
+ else if (fd)
+ fparams = fd.getParameterList();
+ else
+ {
+ e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function",
+ o.toChars(), o1.toChars());
+ return ErrorExp.get();
+ }
+
+ // Avoid further analysis for invalid functions leading to misleading error messages
+ if (!fparams.parameters)
+ return ErrorExp.get();
+
+ StorageClass stc;
+
+ // Set stc to storage class of the ith parameter
+ auto ex = isExpression((*e.args)[1]);
+ if (!ex)
+ {
+ e.error("expression expected as second argument of `__traits(getParameterStorageClasses, %s, %s)`",
+ o.toChars(), o1.toChars());
+ return ErrorExp.get();
+ }
+ ex = ex.ctfeInterpret();
+ auto ii = ex.toUInteger();
+ if (ii >= fparams.length)
+ {
+ e.error("parameter index must be in range 0..%u not %s", cast(uint)fparams.length, ex.toChars());
+ return ErrorExp.get();
+ }
+
+ uint n = cast(uint)ii;
+ Parameter p = fparams[n];
+ stc = p.storageClass;
+
+ // This mirrors hdrgen.visit(Parameter p)
+ if (p.type && p.type.mod & MODFlags.shared_)
+ stc &= ~STC.shared_;
+
+ auto exps = new Expressions;
+
+ void push(string s)
+ {
+ exps.push(new StringExp(e.loc, s));
+ }
+
+ if (stc & STC.auto_)
+ push("auto");
+ if (stc & STC.return_)
+ push("return");
+
+ if (stc & STC.out_)
+ push("out");
+ else if (stc & STC.in_)
+ push("in");
+ else if (stc & STC.ref_)
+ push("ref");
+ else if (stc & STC.lazy_)
+ push("lazy");
+ else if (stc & STC.alias_)
+ push("alias");
+
+ if (stc & STC.const_)
+ push("const");
+ if (stc & STC.immutable_)
+ push("immutable");
+ if (stc & STC.wild)
+ push("inout");
+ if (stc & STC.shared_)
+ push("shared");
+ if (stc & STC.scope_ && !(stc & STC.scopeinferred))
+ push("scope");
+
+ auto tup = new TupleExp(e.loc, exps);
+ return tup.expressionSemantic(sc);
+ }
+ if (e.ident == Id.getLinkage)
+ {
+ // get symbol linkage as a string
+ if (dim != 1)
+ return dimError(1);
+
+ LINK link;
+ auto o = (*e.args)[0];
+
+ FuncDeclaration fd;
+ TypeFunction tf = toTypeFunction(o, fd);
+
+ if (tf)
+ link = tf.linkage;
+ else
+ {
+ auto s = getDsymbol(o);
+ Declaration d;
+ AggregateDeclaration agg;
+ if (!s || ((d = s.isDeclaration()) is null && (agg = s.isAggregateDeclaration()) is null))
+ {
+ e.error("argument to `__traits(getLinkage, %s)` is not a declaration", o.toChars());
+ return ErrorExp.get();
+ }
+
+ if (d !is null)
+ link = d.linkage;
+ else
+ {
+ // Resolves forward references
+ if (agg.sizeok != Sizeok.done)
+ {
+ agg.size(e.loc);
+ if (agg.sizeok != Sizeok.done)
+ {
+ e.error("%s `%s` is forward referenced", agg.kind(), agg.toChars());
+ return ErrorExp.get();
+ }
+ }
+
+ final switch (agg.classKind)
+ {
+ case ClassKind.d:
+ link = LINK.d;
+ break;
+ case ClassKind.cpp:
+ link = LINK.cpp;
+ break;
+ case ClassKind.objc:
+ link = LINK.objc;
+ break;
+ case ClassKind.c:
+ link = LINK.c;
+ break;
+ }
+ }
+ }
+ auto linkage = linkageToChars(link);
+ auto se = new StringExp(e.loc, linkage.toDString());
+ return se.expressionSemantic(sc);
+ }
+ if (e.ident == Id.allMembers ||
+ e.ident == Id.derivedMembers)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbol(o);
+ if (!s)
+ {
+ e.error("In expression `%s` `%s` can't have members", e.toChars(), o.toChars());
+ e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", o.toChars());
+
+ return ErrorExp.get();
+ }
+ if (auto imp = s.isImport())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=9692
+ s = imp.mod;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=16044
+ if (auto p = s.isPackage())
+ {
+ if (auto pm = p.isPackageMod())
+ s = pm;
+ }
+
+ auto sds = s.isScopeDsymbol();
+ if (!sds || sds.isTemplateDeclaration())
+ {
+ e.error("In expression `%s` %s `%s` has no members", e.toChars(), s.kind(), s.toChars());
+ e.errorSupplemental("`%s` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation", s.toChars());
+ return ErrorExp.get();
+ }
+
+ auto idents = new Identifiers();
+
+ int pushIdentsDg(size_t n, Dsymbol sm)
+ {
+ if (!sm)
+ return 1;
+
+ // skip local symbols, such as static foreach loop variables
+ if (auto decl = sm.isDeclaration())
+ {
+ if (decl.storage_class & STC.local)
+ {
+ return 0;
+ }
+ // skip 'this' context pointers
+ else if (decl.isThisDeclaration())
+ return 0;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=20915
+ // skip version and debug identifiers
+ if (sm.isVersionSymbol() || sm.isDebugSymbol())
+ return 0;
+
+ //printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars());
+ if (sm.ident)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=10096
+ // https://issues.dlang.org/show_bug.cgi?id=10100
+ // Skip over internal members in __traits(allMembers)
+ if ((sm.isCtorDeclaration() && sm.ident != Id.ctor) ||
+ (sm.isDtorDeclaration() && sm.ident != Id.dtor) ||
+ (sm.isPostBlitDeclaration() && sm.ident != Id.postblit) ||
+ sm.isInvariantDeclaration() ||
+ sm.isUnitTestDeclaration())
+
+ {
+ return 0;
+ }
+ if (sm.ident == Id.empty)
+ {
+ return 0;
+ }
+ if (sm.isTypeInfoDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=15177
+ return 0;
+ if ((!sds.isModule() && !sds.isPackage()) && sm.isImport()) // https://issues.dlang.org/show_bug.cgi?id=17057
+ return 0;
+
+ //printf("\t%s\n", sm.ident.toChars());
+
+ /* Skip if already present in idents[]
+ */
+ foreach (id; *idents)
+ {
+ if (id == sm.ident)
+ return 0;
+
+ // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
+ debug
+ {
+ import core.stdc.string : strcmp;
+ assert(strcmp(id.toChars(), sm.ident.toChars()) != 0);
+ }
+ }
+ idents.push(sm.ident);
+ }
+ else if (auto ed = sm.isEnumDeclaration())
+ {
+ ScopeDsymbol._foreach(null, ed.members, &pushIdentsDg);
+ }
+ return 0;
+ }
+
+ ScopeDsymbol._foreach(sc, sds.members, &pushIdentsDg);
+ auto cd = sds.isClassDeclaration();
+ if (cd && e.ident == Id.allMembers)
+ {
+ if (cd.semanticRun < PASS.semanticdone)
+ cd.dsymbolSemantic(null); // https://issues.dlang.org/show_bug.cgi?id=13668
+ // Try to resolve forward reference
+
+ void pushBaseMembersDg(ClassDeclaration cd)
+ {
+ for (size_t i = 0; i < cd.baseclasses.dim; i++)
+ {
+ auto cb = (*cd.baseclasses)[i].sym;
+ assert(cb);
+ ScopeDsymbol._foreach(null, cb.members, &pushIdentsDg);
+ if (cb.baseclasses.dim)
+ pushBaseMembersDg(cb);
+ }
+ }
+
+ pushBaseMembersDg(cd);
+ }
+
+ // Turn Identifiers into StringExps reusing the allocated array
+ assert(Expressions.sizeof == Identifiers.sizeof);
+ auto exps = cast(Expressions*)idents;
+ foreach (i, id; *idents)
+ {
+ auto se = new StringExp(e.loc, id.toString());
+ (*exps)[i] = se;
+ }
+
+ /* Making this a tuple is more flexible, as it can be statically unrolled.
+ * To make an array literal, enclose __traits in [ ]:
+ * [ __traits(allMembers, ...) ]
+ */
+ Expression ex = new TupleExp(e.loc, exps);
+ ex = ex.expressionSemantic(sc);
+ return ex;
+ }
+ if (e.ident == Id.compiles)
+ {
+ /* Determine if all the objects - types, expressions, or symbols -
+ * compile without error
+ */
+ if (!dim)
+ return False();
+
+ foreach (o; *e.args)
+ {
+ uint errors = global.startGagging();
+ Scope* sc2 = sc.push();
+ sc2.tinst = null;
+ sc2.minst = null;
+ sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst;
+
+ bool err = false;
+
+ auto t = isType(o);
+ while (t)
+ {
+ if (auto tm = t.isTypeMixin())
+ {
+ /* The mixin string could be a type or an expression.
+ * Have to try compiling it to see.
+ */
+ OutBuffer buf;
+ if (expressionsToString(buf, sc, tm.exps))
+ {
+ err = true;
+ break;
+ }
+ const olderrors = global.errors;
+ const len = buf.length;
+ buf.writeByte(0);
+ const str = buf.extractSlice()[0 .. len];
+ scope p = new Parser!ASTCodegen(e.loc, sc._module, str, false);
+ p.nextToken();
+ //printf("p.loc.linnum = %d\n", p.loc.linnum);
+
+ o = p.parseTypeOrAssignExp(TOK.endOfFile);
+ if (olderrors != global.errors || p.token.value != TOK.endOfFile)
+ {
+ err = true;
+ break;
+ }
+ t = o.isType();
+ }
+ else
+ break;
+ }
+
+ if (!err)
+ {
+ auto ex = t ? t.typeToExpression() : isExpression(o);
+ if (!ex && t)
+ {
+ Dsymbol s;
+ t.resolve(e.loc, sc2, ex, t, s);
+ if (t)
+ {
+ t.typeSemantic(e.loc, sc2);
+ if (t.ty == Terror)
+ err = true;
+ }
+ else if (s && s.errors)
+ err = true;
+ }
+ if (ex)
+ {
+ ex = ex.expressionSemantic(sc2);
+ ex = resolvePropertiesOnly(sc2, ex);
+ ex = ex.optimize(WANTvalue);
+ if (sc2.func && sc2.func.type.ty == Tfunction)
+ {
+ const tf = cast(TypeFunction)sc2.func.type;
+ err |= tf.isnothrow && canThrow(ex, sc2.func, false);
+ }
+ ex = checkGC(sc2, ex);
+ if (ex.op == TOK.error)
+ err = true;
+ }
+ }
+
+ // Carefully detach the scope from the parent and throw it away as
+ // we only need it to evaluate the expression
+ // https://issues.dlang.org/show_bug.cgi?id=15428
+ sc2.detach();
+
+ if (global.endGagging(errors) || err)
+ {
+ return False();
+ }
+ }
+ return True();
+ }
+ if (e.ident == Id.isSame)
+ {
+ /* Determine if two symbols are the same
+ */
+ if (dim != 2)
+ return dimError(2);
+
+ // https://issues.dlang.org/show_bug.cgi?id=20761
+ // tiarg semantic may expand in place the list of arguments, for example:
+ //
+ // before tiarg sema: __traits(isSame, seq!(0,0), seq!(1,1))
+ // after : __traits(isSame, 0, 0, 1, 1)
+ //
+ // so we split in two lists
+ Objects ob1;
+ ob1.push((*e.args)[0]);
+ Objects ob2;
+ ob2.push((*e.args)[1]);
+ if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0))
+ return ErrorExp.get();
+ if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0))
+ return ErrorExp.get();
+ if (ob1.dim != ob2.dim)
+ return False();
+ foreach (immutable i; 0 .. ob1.dim)
+ if (!ob1[i].isSame(ob2[i], sc))
+ return False();
+ return True();
+ }
+ if (e.ident == Id.getUnitTests)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbolWithoutExpCtx(o);
+ if (!s)
+ {
+ e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate",
+ o.toChars());
+ return ErrorExp.get();
+ }
+ if (auto imp = s.isImport()) // https://issues.dlang.org/show_bug.cgi?id=10990
+ s = imp.mod;
+
+ auto sds = s.isScopeDsymbol();
+ if (!sds || sds.isTemplateDeclaration())
+ {
+ e.error("argument `%s` to __traits(getUnitTests) must be a module or aggregate, not a %s",
+ s.toChars(), s.kind());
+ return ErrorExp.get();
+ }
+
+ auto exps = new Expressions();
+ if (global.params.useUnitTests)
+ {
+ bool[void*] uniqueUnitTests;
+
+ void symbolDg(Dsymbol s)
+ {
+ if (auto ad = s.isAttribDeclaration())
+ {
+ ad.include(null).foreachDsymbol(&symbolDg);
+ }
+ else if (auto tm = s.isTemplateMixin())
+ {
+ tm.members.foreachDsymbol(&symbolDg);
+ }
+ else if (auto ud = s.isUnitTestDeclaration())
+ {
+ if (cast(void*)ud in uniqueUnitTests)
+ return;
+
+ uniqueUnitTests[cast(void*)ud] = true;
+
+ auto ad = new FuncAliasDeclaration(ud.ident, ud, false);
+ ad.visibility = ud.visibility;
+
+ auto e = new DsymbolExp(Loc.initial, ad, false);
+ exps.push(e);
+ }
+ }
+
+ sds.members.foreachDsymbol(&symbolDg);
+ }
+ auto te = new TupleExp(e.loc, exps);
+ return te.expressionSemantic(sc);
+ }
+ if (e.ident == Id.getVirtualIndex)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ auto s = getDsymbolWithoutExpCtx(o);
+
+ auto fd = s ? s.isFuncDeclaration() : null;
+ if (!fd)
+ {
+ e.error("first argument to __traits(getVirtualIndex) must be a function");
+ return ErrorExp.get();
+ }
+
+ fd = fd.toAliasFunc(); // Necessary to support multiple overloads.
+ return new IntegerExp(e.loc, fd.vtblIndex, Type.tptrdiff_t);
+ }
+ if (e.ident == Id.getPointerBitmap)
+ {
+ return pointerBitmap(e);
+ }
+ if (e.ident == Id.isZeroInit)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto o = (*e.args)[0];
+ Type t = isType(o);
+ if (!t)
+ {
+ e.error("type expected as second argument of __traits `%s` instead of `%s`",
+ e.ident.toChars(), o.toChars());
+ return ErrorExp.get();
+ }
+
+ Type tb = t.baseElemOf();
+ return tb.isZeroInit(e.loc) ? True() : False();
+ }
+ if (e.ident == Id.getTargetInfo)
+ {
+ if (dim != 1)
+ return dimError(1);
+
+ auto ex = isExpression((*e.args)[0]);
+ StringExp se = ex ? ex.ctfeInterpret().toStringExp() : null;
+ if (!ex || !se || se.len == 0)
+ {
+ e.error("string expected as argument of __traits `%s` instead of `%s`", e.ident.toChars(), ex.toChars());
+ return ErrorExp.get();
+ }
+ se = se.toUTF8(sc);
+
+ const slice = se.peekString();
+ Expression r = target.getTargetInfo(slice.ptr, e.loc); // BUG: reliance on terminating 0
+ if (!r)
+ {
+ e.error("`getTargetInfo` key `\"%.*s\"` not supported by this implementation",
+ cast(int)slice.length, slice.ptr);
+ return ErrorExp.get();
+ }
+ return r.expressionSemantic(sc);
+ }
+ if (e.ident == Id.getLocation)
+ {
+ if (dim != 1)
+ return dimError(1);
+ auto arg0 = (*e.args)[0];
+ Dsymbol s = getDsymbolWithoutExpCtx(arg0);
+ if (!s || !s.loc.isValid())
+ {
+ e.error("can only get the location of a symbol, not `%s`", arg0.toChars());
+ return ErrorExp.get();
+ }
+
+ const fd = s.isFuncDeclaration();
+ // FIXME:td.overnext is always set, even when using an index on it
+ //const td = s.isTemplateDeclaration();
+ if ((fd && fd.overnext) /*|| (td && td.overnext)*/)
+ {
+ e.error("cannot get location of an overload set, " ~
+ "use `__traits(getOverloads, ..., \"%s\"%s)[N]` " ~
+ "to get the Nth overload",
+ arg0.toChars(), /*td ? ", true".ptr :*/ "".ptr);
+ return ErrorExp.get();
+ }
+
+ auto exps = new Expressions(3);
+ (*exps)[0] = new StringExp(e.loc, s.loc.filename.toDString());
+ (*exps)[1] = new IntegerExp(e.loc, s.loc.linnum,Type.tint32);
+ (*exps)[2] = new IntegerExp(e.loc, s.loc.charnum,Type.tint32);
+ auto tup = new TupleExp(e.loc, exps);
+ return tup.expressionSemantic(sc);
+ }
+ if (e.ident == Id.getCppNamespaces)
+ {
+ auto o = (*e.args)[0];
+ auto s = getDsymbolWithoutExpCtx(o);
+ auto exps = new Expressions(0);
+ if (auto d = s.isDeclaration())
+ {
+ if (d.inuse)
+ {
+ d.error("circular reference in `__traits(GetCppNamespaces,...)`");
+ return ErrorExp.get();
+ }
+ d.inuse = 1;
+ }
+
+ /**
+ Prepend the namespaces in the linked list `ns` to `es`.
+
+ Returns: true if `ns` contains an `ErrorExp`.
+ */
+ bool prependNamespaces(Expressions* es, CPPNamespaceDeclaration ns)
+ {
+ // Semantic processing will convert `extern(C++, "a", "b", "c")`
+ // into `extern(C++, "a") extern(C++, "b") extern(C++, "c")`,
+ // creating a linked list what `a`'s `cppnamespace` points to `b`,
+ // and `b`'s points to `c`. Our entry point is `a`.
+ for (; ns !is null; ns = ns.cppnamespace)
+ {
+ ns.dsymbolSemantic(sc);
+
+ if (ns.exp.isErrorExp())
+ return true;
+
+ auto se = ns.exp.toStringExp();
+ // extern(C++, (emptyTuple))
+ // struct D {}
+ // will produce a blank ident
+ if (!se.len)
+ continue;
+ es.insert(0, se);
+ }
+ return false;
+ }
+ for (auto p = s; !p.isModule(); p = p.toParent())
+ {
+ p.dsymbolSemantic(sc);
+ auto pp = p.toParent();
+ if (pp.isTemplateInstance())
+ {
+ if (!p.cppnamespace)
+ continue;
+ //if (!p.toParent().cppnamespace)
+ // continue;
+ auto inner = new Expressions(0);
+ auto outer = new Expressions(0);
+ if (prependNamespaces(inner, p.cppnamespace)) return ErrorExp.get();
+ if (prependNamespaces(outer, pp.cppnamespace)) return ErrorExp.get();
+
+ size_t i = 0;
+ while(i < outer.dim && ((*inner)[i]) == (*outer)[i])
+ i++;
+
+ foreach_reverse (ns; (*inner)[][i .. $])
+ exps.insert(0, ns);
+ continue;
+ }
+
+ if (p.isNspace())
+ exps.insert(0, new StringExp(p.loc, p.ident.toString()));
+
+ if (prependNamespaces(exps, p.cppnamespace))
+ return ErrorExp.get();
+ }
+ if (auto d = s.isDeclaration())
+ d.inuse = 0;
+ auto tup = new TupleExp(e.loc, exps);
+ return tup.expressionSemantic(sc);
+ }
+
+ static const(char)[] trait_search_fp(const(char)[] seed, out int cost)
+ {
+ //printf("trait_search_fp('%s')\n", seed);
+ if (!seed.length)
+ return null;
+ cost = 0; // all the same cost
+ const sv = traitsStringTable.lookup(seed);
+ return sv ? sv.toString() : null;
+ }
+
+ if (auto sub = speller!trait_search_fp(e.ident.toString()))
+ e.error("unrecognized trait `%s`, did you mean `%.*s`?", e.ident.toChars(), cast(int) sub.length, sub.ptr);
+ else
+ e.error("unrecognized trait `%s`", e.ident.toChars());
+ return ErrorExp.get();
+}
+
+/// compare arguments of __traits(isSame)
+private bool isSame(RootObject o1, RootObject o2, Scope* sc)
+{
+ static FuncLiteralDeclaration isLambda(RootObject oarg)
+ {
+ if (auto t = isDsymbol(oarg))
+ {
+ if (auto td = t.isTemplateDeclaration())
+ {
+ if (td.members && td.members.dim == 1)
+ {
+ if (auto fd = (*td.members)[0].isFuncLiteralDeclaration())
+ return fd;
+ }
+ }
+ }
+ else if (auto ea = isExpression(oarg))
+ {
+ if (ea.op == TOK.function_)
+ {
+ if (auto fe = cast(FuncExp)ea)
+ return fe.fd;
+ }
+ }
+ return null;
+ }
+
+ auto l1 = isLambda(o1);
+ auto l2 = isLambda(o2);
+
+ if (l1 && l2)
+ {
+ import dmd.lambdacomp : isSameFuncLiteral;
+ if (isSameFuncLiteral(l1, l2, sc))
+ return true;
+ }
+
+ // issue 12001, allow isSame, <BasicType>, <BasicType>
+ Type t1 = isType(o1);
+ Type t2 = isType(o2);
+ if (t1 && t2 && t1.equals(t2))
+ return true;
+
+ auto s1 = getDsymbol(o1);
+ auto s2 = getDsymbol(o2);
+ //printf("isSame: %s, %s\n", o1.toChars(), o2.toChars());
+ version (none)
+ {
+ printf("o1: %p\n", o1);
+ printf("o2: %p\n", o2);
+ if (!s1)
+ {
+ if (auto ea = isExpression(o1))
+ printf("%s\n", ea.toChars());
+ if (auto ta = isType(o1))
+ printf("%s\n", ta.toChars());
+ return false;
+ }
+ else
+ printf("%s %s\n", s1.kind(), s1.toChars());
+ }
+ if (!s1 && !s2)
+ {
+ auto ea1 = isExpression(o1);
+ auto ea2 = isExpression(o2);
+ if (ea1 && ea2)
+ {
+ if (ea1.equals(ea2))
+ return true;
+ }
+ }
+ if (!s1 || !s2)
+ return false;
+
+ s1 = s1.toAlias();
+ s2 = s2.toAlias();
+
+ if (auto fa1 = s1.isFuncAliasDeclaration())
+ s1 = fa1.toAliasFunc();
+ if (auto fa2 = s2.isFuncAliasDeclaration())
+ s2 = fa2.toAliasFunc();
+
+ // https://issues.dlang.org/show_bug.cgi?id=11259
+ // compare import symbol to a package symbol
+ static bool cmp(Dsymbol s1, Dsymbol s2)
+ {
+ auto imp = s1.isImport();
+ return imp && imp.pkg && imp.pkg == s2.isPackage();
+ }
+
+ if (cmp(s1,s2) || cmp(s2,s1))
+ return true;
+
+ if (s1 == s2)
+ return true;
+
+ // https://issues.dlang.org/show_bug.cgi?id=18771
+ // OverloadSets are equal if they contain the same functions
+ auto overSet1 = s1.isOverloadSet();
+ if (!overSet1)
+ return false;
+
+ auto overSet2 = s2.isOverloadSet();
+ if (!overSet2)
+ return false;
+
+ if (overSet1.a.dim != overSet2.a.dim)
+ return false;
+
+ // OverloadSets contain array of Dsymbols => O(n*n)
+ // to compare for equality as the order of overloads
+ // might not be the same
+Lnext:
+ foreach(overload1; overSet1.a)
+ {
+ foreach(overload2; overSet2.a)
+ {
+ if (overload1 == overload2)
+ continue Lnext;
+ }
+ return false;
+ }
+ return true;
+}
diff --git a/gcc/d/dmd/transitivevisitor.d b/gcc/d/dmd/transitivevisitor.d
new file mode 100644
index 00000000000..0479d5ad045
--- /dev/null
+++ b/gcc/d/dmd/transitivevisitor.d
@@ -0,0 +1,1207 @@
+/**
+ * Documentation: https://dlang.org/phobos/dmd_transitivevisitor.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/transitivevisitor.d
+ */
+
+module dmd.transitivevisitor;
+
+import dmd.astenums;
+import dmd.permissivevisitor;
+import dmd.tokens;
+import dmd.root.rootobject;
+
+import core.stdc.stdio;
+
+/** Visitor that implements the AST traversal logic. The nodes just accept their children.
+ */
+extern(C++) class ParseTimeTransitiveVisitor(AST) : PermissiveVisitor!AST
+{
+ alias visit = PermissiveVisitor!AST.visit;
+ mixin ParseVisitMethods!AST;
+}
+
+/* This mixin implements the AST traversal logic for parse time AST nodes. The same code
+ * is used for semantic time AST node traversal, so in order to not duplicate the code,
+ * the template mixin is used.
+ */
+package mixin template ParseVisitMethods(AST)
+{
+
+// Statement Nodes
+//===========================================================
+ override void visit(AST.ExpStatement s)
+ {
+ //printf("Visiting ExpStatement\n");
+ if (s.exp)
+ {
+ if (auto de = s.exp.isDeclarationExp())
+ de.declaration.accept(this);
+ else
+ s.exp.accept(this);
+ }
+ }
+
+ override void visit(AST.CompileStatement s)
+ {
+ //printf("Visiting CompileStatement\n");
+ visitArgs(s.exps);
+ }
+
+ override void visit(AST.CompoundStatement s)
+ {
+ //printf("Visiting CompoundStatement\n");
+ foreach (sx; *s.statements)
+ {
+ if (sx)
+ sx.accept(this);
+ }
+ }
+
+ void visitVarDecl(AST.VarDeclaration v)
+ {
+ //printf("Visiting VarDeclaration\n");
+ if (v.type)
+ visitType(v.type);
+ if (v._init)
+ {
+ if (auto ie = v._init.isExpInitializer())
+ {
+ if (auto ce = ie.exp.isConstructExp())
+ ce.e2.accept(this);
+ else if (auto be = ie.exp.isBlitExp())
+ be.e2.accept(this);
+ else
+ v._init.accept(this);
+ }
+ else
+ v._init.accept(this);
+ }
+ }
+
+ override void visit(AST.CompoundDeclarationStatement s)
+ {
+ //printf("Visiting CompoundDeclarationStatement\n");
+ foreach (sx; *s.statements)
+ {
+ if (!sx)
+ continue;
+ if (auto ds = sx.isExpStatement())
+ {
+ if (auto de = ds.exp.isDeclarationExp())
+ {
+ auto d = de.declaration;
+ assert(d.isDeclaration());
+ if (auto v = d.isVarDeclaration())
+ visitVarDecl(v);
+ else
+ d.accept(this);
+ }
+ }
+ }
+ }
+
+ override void visit(AST.ScopeStatement s)
+ {
+ //printf("Visiting ScopeStatement\n");
+ if (s.statement)
+ s.statement.accept(this);
+ }
+
+ override void visit(AST.WhileStatement s)
+ {
+ //printf("Visiting WhileStatement\n");
+ s.condition.accept(this);
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(AST.DoStatement s)
+ {
+ //printf("Visiting DoStatement\n");
+ if (s._body)
+ s._body.accept(this);
+ s.condition.accept(this);
+ }
+
+ override void visit(AST.ForStatement s)
+ {
+ //printf("Visiting ForStatement\n");
+ if (s._init)
+ s._init.accept(this);
+ if (s.condition)
+ s.condition.accept(this);
+ if (s.increment)
+ s.increment.accept(this);
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(AST.ForeachStatement s)
+ {
+ //printf("Visiting ForeachStatement\n");
+ foreach (p; *s.parameters)
+ if (p.type)
+ visitType(p.type);
+ s.aggr.accept(this);
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(AST.ForeachRangeStatement s)
+ {
+ //printf("Visiting ForeachRangeStatement\n");
+ if (s.prm.type)
+ visitType(s.prm.type);
+ s.lwr.accept(this);
+ s.upr.accept(this);
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(AST.IfStatement s)
+ {
+ //printf("Visiting IfStatement\n");
+ if (s.prm && s.prm.type)
+ visitType(s.prm.type);
+ s.condition.accept(this);
+ s.ifbody.accept(this);
+ if (s.elsebody)
+ s.elsebody.accept(this);
+ }
+
+ override void visit(AST.ConditionalStatement s)
+ {
+ //printf("Visiting ConditionalStatement\n");
+ s.condition.accept(this);
+ if (s.ifbody)
+ s.ifbody.accept(this);
+ if (s.elsebody)
+ s.elsebody.accept(this);
+ }
+
+ void visitArgs(AST.Expressions* expressions, AST.Expression basis = null)
+ {
+ if (!expressions || !expressions.dim)
+ return;
+ foreach (el; *expressions)
+ {
+ if (!el)
+ el = basis;
+ if (el)
+ el.accept(this);
+ }
+ }
+
+ override void visit(AST.PragmaStatement s)
+ {
+ //printf("Visiting PragmaStatement\n");
+ if (s.args && s.args.dim)
+ visitArgs(s.args);
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(AST.StaticAssertStatement s)
+ {
+ //printf("Visiting StaticAssertStatement\n");
+ s.sa.accept(this);
+ }
+
+ override void visit(AST.SwitchStatement s)
+ {
+ //printf("Visiting SwitchStatement\n");
+ s.condition.accept(this);
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(AST.CaseStatement s)
+ {
+ //printf("Visiting CaseStatement\n");
+ s.exp.accept(this);
+ s.statement.accept(this);
+ }
+
+ override void visit(AST.CaseRangeStatement s)
+ {
+ //printf("Visiting CaseRangeStatement\n");
+ s.first.accept(this);
+ s.last.accept(this);
+ s.statement.accept(this);
+ }
+
+ override void visit(AST.DefaultStatement s)
+ {
+ //printf("Visiting DefaultStatement\n");
+ s.statement.accept(this);
+ }
+
+ override void visit(AST.GotoCaseStatement s)
+ {
+ //printf("Visiting GotoCaseStatement\n");
+ if (s.exp)
+ s.exp.accept(this);
+ }
+
+ override void visit(AST.ReturnStatement s)
+ {
+ //printf("Visiting ReturnStatement\n");
+ if (s.exp)
+ s.exp.accept(this);
+ }
+
+ override void visit(AST.SynchronizedStatement s)
+ {
+ //printf("Visiting SynchronizedStatement\n");
+ if (s.exp)
+ s.exp.accept(this);
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(AST.WithStatement s)
+ {
+ //printf("Visiting WithStatement\n");
+ s.exp.accept(this);
+ if (s._body)
+ s._body.accept(this);
+ }
+
+ override void visit(AST.TryCatchStatement s)
+ {
+ //printf("Visiting TryCatchStatement\n");
+ if (s._body)
+ s._body.accept(this);
+ foreach (c; *s.catches)
+ visit(c);
+ }
+
+ override void visit(AST.TryFinallyStatement s)
+ {
+ //printf("Visiting TryFinallyStatement\n");
+ s._body.accept(this);
+ s.finalbody.accept(this);
+ }
+
+ override void visit(AST.ScopeGuardStatement s)
+ {
+ //printf("Visiting ScopeGuardStatement\n");
+ s.statement.accept(this);
+ }
+
+ override void visit(AST.ThrowStatement s)
+ {
+ //printf("Visiting ThrowStatement\n");
+ s.exp.accept(this);
+ }
+
+ override void visit(AST.LabelStatement s)
+ {
+ //printf("Visiting LabelStatement\n");
+ if (s.statement)
+ s.statement.accept(this);
+ }
+
+ override void visit(AST.ImportStatement s)
+ {
+ //printf("Visiting ImportStatement\n");
+ foreach (imp; *s.imports)
+ imp.accept(this);
+ }
+
+ void visit(AST.Catch c)
+ {
+ //printf("Visiting Catch\n");
+ if (c.type)
+ visitType(c.type);
+ if (c.handler)
+ c.handler.accept(this);
+ }
+
+// Type Nodes
+//============================================================
+
+ void visitType(AST.Type t)
+ {
+ //printf("Visiting Type\n");
+ if (!t)
+ return;
+ if (auto tf = t.isTypeFunction())
+ {
+ visitFunctionType(tf, null);
+ return;
+ }
+ else
+ t.accept(this);
+ }
+
+ void visitFunctionType(AST.TypeFunction t, AST.TemplateDeclaration td)
+ {
+ if (t.next)
+ visitType(t.next);
+ if (td)
+ {
+ foreach (p; *td.origParameters)
+ p.accept(this);
+ }
+ visitParameters(t.parameterList.parameters);
+ }
+
+ void visitParameters(AST.Parameters* parameters)
+ {
+ if (parameters)
+ {
+ size_t dim = AST.Parameter.dim(parameters);
+ foreach(i; 0..dim)
+ {
+ AST.Parameter fparam = AST.Parameter.getNth(parameters, i);
+ fparam.accept(this);
+ }
+ }
+ }
+
+ override void visit(AST.TypeVector t)
+ {
+ //printf("Visiting TypeVector\n");
+ if (!t.basetype)
+ return;
+ t.basetype.accept(this);
+ }
+
+ override void visit(AST.TypeSArray t)
+ {
+ //printf("Visiting TypeSArray\n");
+ t.next.accept(this);
+ }
+
+ override void visit(AST.TypeDArray t)
+ {
+ //printf("Visiting TypeDArray\n");
+ t.next.accept(this);
+ }
+
+ override void visit(AST.TypeAArray t)
+ {
+ //printf("Visiting TypeAArray\n");
+ t.next.accept(this);
+ t.index.accept(this);
+ }
+
+ override void visit(AST.TypePointer t)
+ {
+ //printf("Visiting TypePointer\n");
+ if (auto tf = t.next.isTypeFunction())
+ {
+ visitFunctionType(tf, null);
+ }
+ else
+ t.next.accept(this);
+ }
+
+ override void visit(AST.TypeReference t)
+ {
+ //printf("Visiting TypeReference\n");
+ t.next.accept(this);
+ }
+
+ override void visit(AST.TypeFunction t)
+ {
+ //printf("Visiting TypeFunction\n");
+ visitFunctionType(t, null);
+ }
+
+ override void visit(AST.TypeDelegate t)
+ {
+ //printf("Visiting TypeDelegate\n");
+ visitFunctionType(t.next.isTypeFunction(), null);
+ }
+
+ void visitTypeQualified(AST.TypeQualified t)
+ {
+ //printf("Visiting TypeQualified\n");
+ foreach (id; t.idents)
+ {
+ if (id.dyncast() == DYNCAST.dsymbol)
+ (cast(AST.TemplateInstance)id).accept(this);
+ else if (id.dyncast() == DYNCAST.expression)
+ (cast(AST.Expression)id).accept(this);
+ else if (id.dyncast() == DYNCAST.type)
+ (cast(AST.Type)id).accept(this);
+ }
+ }
+
+ override void visit(AST.TypeIdentifier t)
+ {
+ //printf("Visiting TypeIdentifier\n");
+ visitTypeQualified(t);
+ }
+
+ override void visit(AST.TypeInstance t)
+ {
+ //printf("Visiting TypeInstance\n");
+ t.tempinst.accept(this);
+ visitTypeQualified(t);
+ }
+
+ override void visit(AST.TypeTypeof t)
+ {
+ //printf("Visiting TypeTypeof\n");
+ t.exp.accept(this);
+ visitTypeQualified(t);
+ }
+
+ override void visit(AST.TypeReturn t)
+ {
+ //printf("Visiting TypeReturn\n");
+ visitTypeQualified(t);
+ }
+
+ override void visit(AST.TypeTuple t)
+ {
+ //printf("Visiting TypeTuple\n");
+ visitParameters(t.arguments);
+ }
+
+ override void visit(AST.TypeSlice t)
+ {
+ //printf("Visiting TypeSlice\n");
+ t.next.accept(this);
+ t.lwr.accept(this);
+ t.upr.accept(this);
+ }
+
+ override void visit(AST.TypeTraits t)
+ {
+ t.exp.accept(this);
+ }
+
+ override void visit(AST.TypeMixin t)
+ {
+ visitArgs(t.exps);
+ }
+
+// Miscellaneous
+//========================================================
+
+ override void visit(AST.StaticAssert s)
+ {
+ //printf("Visiting StaticAssert\n");
+ s.exp.accept(this);
+ if (s.msg)
+ s.msg.accept(this);
+ }
+
+ override void visit(AST.EnumMember em)
+ {
+ //printf("Visiting EnumMember\n");
+ if (em.type)
+ visitType(em.type);
+ if (em.value)
+ em.value.accept(this);
+ }
+
+// Declarations
+//=========================================================
+ void visitAttribDeclaration(AST.AttribDeclaration d)
+ {
+ if (d.decl)
+ foreach (de; *d.decl)
+ de.accept(this);
+ }
+
+ override void visit(AST.AttribDeclaration d)
+ {
+ //printf("Visiting AttribDeclaration\n");
+ visitAttribDeclaration(d);
+ }
+
+ override void visit(AST.StorageClassDeclaration d)
+ {
+ //printf("Visiting StorageClassDeclaration\n");
+ visitAttribDeclaration(cast(AST.AttribDeclaration)d);
+ }
+
+ override void visit(AST.DeprecatedDeclaration d)
+ {
+ //printf("Visiting DeprecatedDeclaration\n");
+ d.msg.accept(this);
+ visitAttribDeclaration(cast(AST.AttribDeclaration)d);
+ }
+
+ override void visit(AST.LinkDeclaration d)
+ {
+ //printf("Visiting LinkDeclaration\n");
+ visitAttribDeclaration(cast(AST.AttribDeclaration)d);
+ }
+
+ override void visit(AST.CPPMangleDeclaration d)
+ {
+ //printf("Visiting CPPMangleDeclaration\n");
+ visitAttribDeclaration(cast(AST.AttribDeclaration)d);
+ }
+
+ override void visit(AST.VisibilityDeclaration d)
+ {
+ //printf("Visiting VisibilityDeclaration\n");
+ visitAttribDeclaration(cast(AST.AttribDeclaration)d);
+ }
+
+ override void visit(AST.AlignDeclaration d)
+ {
+ //printf("Visiting AlignDeclaration\n");
+ visitAttribDeclaration(cast(AST.AttribDeclaration)d);
+ }
+
+ override void visit(AST.AnonDeclaration d)
+ {
+ //printf("Visiting AnonDeclaration\n");
+ visitAttribDeclaration(cast(AST.AttribDeclaration)d);
+ }
+
+ override void visit(AST.PragmaDeclaration d)
+ {
+ //printf("Visiting PragmaDeclaration\n");
+ if (d.args && d.args.dim)
+ visitArgs(d.args);
+ visitAttribDeclaration(cast(AST.AttribDeclaration)d);
+ }
+
+ override void visit(AST.ConditionalDeclaration d)
+ {
+ //printf("Visiting ConditionalDeclaration\n");
+ d.condition.accept(this);
+ if (d.decl)
+ foreach (de; *d.decl)
+ de.accept(this);
+ if (d.elsedecl)
+ foreach (de; *d.elsedecl)
+ de.accept(this);
+ }
+
+ override void visit(AST.CompileDeclaration d)
+ {
+ //printf("Visiting compileDeclaration\n");
+ visitArgs(d.exps);
+ }
+
+ override void visit(AST.UserAttributeDeclaration d)
+ {
+ //printf("Visiting UserAttributeDeclaration\n");
+ visitArgs(d.atts);
+ visitAttribDeclaration(cast(AST.AttribDeclaration)d);
+ }
+
+ void visitFuncBody(AST.FuncDeclaration f)
+ {
+ //printf("Visiting funcBody\n");
+ if (f.frequires)
+ {
+ foreach (frequire; *f.frequires)
+ {
+ frequire.accept(this);
+ }
+ }
+ if (f.fensures)
+ {
+ foreach (fensure; *f.fensures)
+ {
+ fensure.ensure.accept(this);
+ }
+ }
+ if (f.fbody)
+ {
+ f.fbody.accept(this);
+ }
+ }
+
+ void visitBaseClasses(AST.ClassDeclaration d)
+ {
+ //printf("Visiting ClassDeclaration\n");
+ if (!d || !d.baseclasses.dim)
+ return;
+ foreach (b; *d.baseclasses)
+ visitType(b.type);
+ }
+
+ bool visitEponymousMember(AST.TemplateDeclaration d)
+ {
+ //printf("Visiting EponymousMember\n");
+ if (!d.members || d.members.dim != 1)
+ return false;
+ AST.Dsymbol onemember = (*d.members)[0];
+ if (onemember.ident != d.ident)
+ return false;
+
+ if (AST.FuncDeclaration fd = onemember.isFuncDeclaration())
+ {
+ assert(fd.type);
+ visitFunctionType(fd.type.isTypeFunction(), d);
+ if (d.constraint)
+ d.constraint.accept(this);
+ visitFuncBody(fd);
+
+ return true;
+ }
+
+ if (AST.AggregateDeclaration ad = onemember.isAggregateDeclaration())
+ {
+ visitTemplateParameters(d.parameters);
+ if (d.constraint)
+ d.constraint.accept(this);
+ visitBaseClasses(ad.isClassDeclaration());
+
+ if (ad.members)
+ foreach (s; *ad.members)
+ s.accept(this);
+
+ return true;
+ }
+
+ if (AST.VarDeclaration vd = onemember.isVarDeclaration())
+ {
+ if (d.constraint)
+ return false;
+ if (vd.type)
+ visitType(vd.type);
+ visitTemplateParameters(d.parameters);
+ if (vd._init)
+ {
+ // note similarity of this code with visitVarDecl()
+ if (auto ie = vd._init.isExpInitializer())
+ {
+ if (auto ce = ie.exp.isConstructExp())
+ ce.e2.accept(this);
+ else if (auto be = ie.exp.isBlitExp())
+ be.e2.accept(this);
+ else
+ vd._init.accept(this);
+ }
+ else
+ vd._init.accept(this);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void visitTemplateParameters(AST.TemplateParameters* parameters)
+ {
+ if (!parameters || !parameters.dim)
+ return;
+ foreach (p; *parameters)
+ p.accept(this);
+ }
+
+ override void visit(AST.TemplateDeclaration d)
+ {
+ //printf("Visiting TemplateDeclaration\n");
+ if (visitEponymousMember(d))
+ return;
+
+ visitTemplateParameters(d.parameters);
+ if (d.constraint)
+ d.constraint.accept(this);
+
+ foreach (s; *d.members)
+ s.accept(this);
+ }
+
+ void visitObject(RootObject oarg)
+ {
+ if (auto t = AST.isType(oarg))
+ {
+ visitType(t);
+ }
+ else if (auto e = AST.isExpression(oarg))
+ {
+ e.accept(this);
+ }
+ else if (auto v = AST.isTuple(oarg))
+ {
+ auto args = &v.objects;
+ foreach (arg; *args)
+ visitObject(arg);
+ }
+ }
+
+ void visitTiargs(AST.TemplateInstance ti)
+ {
+ //printf("Visiting tiargs\n");
+ if (!ti.tiargs)
+ return;
+ foreach (arg; *ti.tiargs)
+ {
+ visitObject(arg);
+ }
+ }
+
+ override void visit(AST.TemplateInstance ti)
+ {
+ //printf("Visiting TemplateInstance\n");
+ visitTiargs(ti);
+ }
+
+ override void visit(AST.TemplateMixin tm)
+ {
+ //printf("Visiting TemplateMixin\n");
+ visitType(tm.tqual);
+ visitTiargs(tm);
+ }
+
+ override void visit(AST.EnumDeclaration d)
+ {
+ //printf("Visiting EnumDeclaration\n");
+ if (d.memtype)
+ visitType(d.memtype);
+ if (!d.members)
+ return;
+ foreach (em; *d.members)
+ {
+ if (!em)
+ continue;
+ em.accept(this);
+ }
+ }
+
+ override void visit(AST.Nspace d)
+ {
+ //printf("Visiting Nspace\n");
+ foreach(s; *d.members)
+ s.accept(this);
+ }
+
+ override void visit(AST.StructDeclaration d)
+ {
+ //printf("Visiting StructDeclaration\n");
+ if (!d.members)
+ return;
+ foreach (s; *d.members)
+ s.accept(this);
+ }
+
+ override void visit(AST.ClassDeclaration d)
+ {
+ //printf("Visiting ClassDeclaration\n");
+ visitBaseClasses(d);
+ if (d.members)
+ foreach (s; *d.members)
+ s.accept(this);
+ }
+
+ override void visit(AST.AliasDeclaration d)
+ {
+ //printf("Visting AliasDeclaration\n");
+ if (d.aliassym)
+ d.aliassym.accept(this);
+ else
+ visitType(d.type);
+ }
+
+ override void visit(AST.AliasAssign d)
+ {
+ //printf("Visting AliasAssign\n");
+ if (d.aliassym)
+ d.aliassym.accept(this);
+ else
+ visitType(d.type);
+ }
+
+ override void visit(AST.VarDeclaration d)
+ {
+ //printf("Visiting VarDeclaration\n");
+ visitVarDecl(d);
+ }
+
+ override void visit(AST.FuncDeclaration f)
+ {
+ //printf("Visiting FuncDeclaration\n");
+ auto tf = f.type.isTypeFunction();
+ visitType(tf);
+ visitFuncBody(f);
+ }
+
+ override void visit(AST.FuncLiteralDeclaration f)
+ {
+ //printf("Visiting FuncLiteralDeclaration\n");
+ if (f.type.ty == Terror)
+ return;
+ auto tf = f.type.isTypeFunction();
+ if (!f.inferRetType && tf.next)
+ visitType(tf.next);
+ visitParameters(tf.parameterList.parameters);
+ AST.CompoundStatement cs = f.fbody.isCompoundStatement();
+ AST.Statement s = !cs ? f.fbody : null;
+ AST.ReturnStatement rs = s ? s.isReturnStatement() : null;
+ if (rs && rs.exp)
+ rs.exp.accept(this);
+ else
+ visitFuncBody(f);
+ }
+
+ override void visit(AST.PostBlitDeclaration d)
+ {
+ //printf("Visiting PostBlitDeclaration\n");
+ visitFuncBody(d);
+ }
+
+ override void visit(AST.DtorDeclaration d)
+ {
+ //printf("Visiting DtorDeclaration\n");
+ visitFuncBody(d);
+ }
+
+ override void visit(AST.StaticCtorDeclaration d)
+ {
+ //printf("Visiting StaticCtorDeclaration\n");
+ visitFuncBody(d);
+ }
+
+ override void visit(AST.StaticDtorDeclaration d)
+ {
+ //printf("Visiting StaticDtorDeclaration\n");
+ visitFuncBody(d);
+ }
+
+ override void visit(AST.InvariantDeclaration d)
+ {
+ //printf("Visiting InvariantDeclaration\n");
+ visitFuncBody(d);
+ }
+
+ override void visit(AST.UnitTestDeclaration d)
+ {
+ //printf("Visiting UnitTestDeclaration\n");
+ visitFuncBody(d);
+ }
+
+ override void visit(AST.NewDeclaration d)
+ {
+ //printf("Visiting NewDeclaration\n");
+ }
+
+// Initializers
+//============================================================
+
+ override void visit(AST.StructInitializer si)
+ {
+ //printf("Visiting StructInitializer\n");
+ foreach (i, const id; si.field)
+ if (auto iz = si.value[i])
+ iz.accept(this);
+ }
+
+ override void visit(AST.ArrayInitializer ai)
+ {
+ //printf("Visiting ArrayInitializer\n");
+ foreach (i, ex; ai.index)
+ {
+ if (ex)
+ ex.accept(this);
+ if (auto iz = ai.value[i])
+ iz.accept(this);
+ }
+ }
+
+ override void visit(AST.ExpInitializer ei)
+ {
+ //printf("Visiting ExpInitializer\n");
+ ei.exp.accept(this);
+ }
+
+ override void visit(AST.CInitializer ci)
+ {
+ //printf("Visiting CInitializer\n");
+ foreach (di; ci.initializerList)
+ {
+ foreach (des; (*di.designatorList)[])
+ {
+ if (des.exp)
+ des.exp.accept(this);
+ }
+ di.initializer.accept(this);
+ }
+ }
+
+// Expressions
+//===================================================
+
+ override void visit(AST.ArrayLiteralExp e)
+ {
+ //printf("Visiting ArrayLiteralExp\n");
+ visitArgs(e.elements, e.basis);
+ }
+
+ override void visit(AST.AssocArrayLiteralExp e)
+ {
+ //printf("Visiting AssocArrayLiteralExp\n");
+ foreach (i, key; *e.keys)
+ {
+ key.accept(this);
+ ((*e.values)[i]).accept(this);
+ }
+ }
+
+ override void visit(AST.TypeExp e)
+ {
+ //printf("Visiting TypeExp\n");
+ visitType(e.type);
+ }
+
+ override void visit(AST.ScopeExp e)
+ {
+ //printf("Visiting ScopeExp\n");
+ if (e.sds.isTemplateInstance())
+ e.sds.accept(this);
+ }
+
+ override void visit(AST.NewExp e)
+ {
+ //printf("Visiting NewExp\n");
+ if (e.thisexp)
+ e.thisexp.accept(this);
+ if (e.newargs && e.newargs.dim)
+ visitArgs(e.newargs);
+ visitType(e.newtype);
+ if (e.arguments && e.arguments.dim)
+ visitArgs(e.arguments);
+ }
+
+ override void visit(AST.NewAnonClassExp e)
+ {
+ //printf("Visiting NewAnonClassExp\n");
+ if (e.thisexp)
+ e.thisexp.accept(this);
+ if (e.newargs && e.newargs.dim)
+ visitArgs(e.newargs);
+ if (e.arguments && e.arguments.dim)
+ visitArgs(e.arguments);
+ if (e.cd)
+ e.cd.accept(this);
+ }
+
+ override void visit(AST.TupleExp e)
+ {
+ //printf("Visiting TupleExp\n");
+ if (e.e0)
+ e.e0.accept(this);
+ visitArgs(e.exps);
+ }
+
+ override void visit(AST.FuncExp e)
+ {
+ //printf("Visiting FuncExp\n");
+ e.fd.accept(this);
+ }
+
+ override void visit(AST.DeclarationExp e)
+ {
+ //printf("Visiting DeclarationExp\n");
+ if (auto v = e.declaration.isVarDeclaration())
+ visitVarDecl(v);
+ else
+ e.declaration.accept(this);
+ }
+
+ override void visit(AST.TypeidExp e)
+ {
+ //printf("Visiting TypeidExp\n");
+ visitObject(e.obj);
+ }
+
+ override void visit(AST.TraitsExp e)
+ {
+ //printf("Visiting TraitExp\n");
+ if (e.args)
+ foreach (arg; *e.args)
+ visitObject(arg);
+ }
+
+ override void visit(AST.IsExp e)
+ {
+ //printf("Visiting IsExp\n");
+ visitType(e.targ);
+ if (e.tspec)
+ visitType(e.tspec);
+ if (e.parameters && e.parameters.dim)
+ visitTemplateParameters(e.parameters);
+ }
+
+ override void visit(AST.UnaExp e)
+ {
+ //printf("Visiting UnaExp\n");
+ e.e1.accept(this);
+ }
+
+ override void visit(AST.BinExp e)
+ {
+ //printf("Visiting BinExp\n");
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+
+ override void visit(AST.MixinExp e)
+ {
+ //printf("Visiting MixinExp\n");
+ visitArgs(e.exps);
+ }
+
+ override void visit(AST.ImportExp e)
+ {
+ //printf("Visiting ImportExp\n");
+ e.e1.accept(this);
+ }
+
+ override void visit(AST.AssertExp e)
+ {
+ //printf("Visiting AssertExp\n");
+ e.e1.accept(this);
+ if (e.msg)
+ e.msg.accept(this);
+ }
+
+ override void visit(AST.DotIdExp e)
+ {
+ //printf("Visiting DotIdExp\n");
+ e.e1.accept(this);
+ }
+
+ override void visit(AST.DotTemplateInstanceExp e)
+ {
+ //printf("Visiting DotTemplateInstanceExp\n");
+ e.e1.accept(this);
+ e.ti.accept(this);
+ }
+
+ override void visit(AST.CallExp e)
+ {
+ //printf("Visiting CallExp\n");
+ e.e1.accept(this);
+ visitArgs(e.arguments);
+ }
+
+ override void visit(AST.PtrExp e)
+ {
+ //printf("Visiting PtrExp\n");
+ e.e1.accept(this);
+ }
+
+ override void visit(AST.DeleteExp e)
+ {
+ //printf("Visiting DeleteExp\n");
+ e.e1.accept(this);
+ }
+
+ override void visit(AST.CastExp e)
+ {
+ //printf("Visiting CastExp\n");
+ if (e.to)
+ visitType(e.to);
+ e.e1.accept(this);
+ }
+
+ override void visit(AST.IntervalExp e)
+ {
+ //printf("Visiting IntervalExp\n");
+ e.lwr.accept(this);
+ e.upr.accept(this);
+ }
+
+ override void visit(AST.ArrayExp e)
+ {
+ //printf("Visiting ArrayExp\n");
+ e.e1.accept(this);
+ visitArgs(e.arguments);
+ }
+
+ override void visit(AST.PostExp e)
+ {
+ //printf("Visiting PostExp\n");
+ e.e1.accept(this);
+ }
+
+ override void visit(AST.CondExp e)
+ {
+ //printf("Visiting CondExp\n");
+ e.econd.accept(this);
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+
+ override void visit(AST.GenericExp e)
+ {
+ //printf("Visiting GenericExp\n");
+ e.cntlExp.accept(this);
+ foreach (i; 0 .. (*e.types).length)
+ {
+ if (auto t = (*e.types)[i]) // null means default case
+ t.accept(this);
+ (*e.exps )[i].accept(this);
+ }
+ }
+
+// Template Parameter
+//===========================================================
+
+ override void visit(AST.TemplateTypeParameter tp)
+ {
+ //printf("Visiting TemplateTypeParameter\n");
+ if (tp.specType)
+ visitType(tp.specType);
+ if (tp.defaultType)
+ visitType(tp.defaultType);
+ }
+
+ override void visit(AST.TemplateThisParameter tp)
+ {
+ //printf("Visiting TemplateThisParameter\n");
+ visit(cast(AST.TemplateTypeParameter)tp);
+ }
+
+ override void visit(AST.TemplateAliasParameter tp)
+ {
+ //printf("Visiting TemplateAliasParameter\n");
+ if (tp.specType)
+ visitType(tp.specType);
+ if (tp.specAlias)
+ visitObject(tp.specAlias);
+ if (tp.defaultAlias)
+ visitObject(tp.defaultAlias);
+ }
+
+ override void visit(AST.TemplateValueParameter tp)
+ {
+ //printf("Visiting TemplateValueParameter\n");
+ visitType(tp.valType);
+ if (tp.specValue)
+ tp.specValue.accept(this);
+ if (tp.defaultValue)
+ tp.defaultValue.accept(this);
+ }
+
+//===========================================================
+
+ override void visit(AST.StaticIfCondition c)
+ {
+ //printf("Visiting StaticIfCondition\n");
+ c.exp.accept(this);
+ }
+
+ override void visit(AST.Parameter p)
+ {
+ //printf("Visiting Parameter\n");
+ visitType(p.type);
+ if (p.defaultArg)
+ p.defaultArg.accept(this);
+ }
+
+ override void visit(AST.Module m)
+ {
+ //printf("Visiting Module\n");
+ foreach (s; *m.members)
+ {
+ s.accept(this);
+ }
+ }
+}
diff --git a/gcc/d/dmd/typesem.c b/gcc/d/dmd/typesem.c
deleted file mode 100644
index 31e93c28bdb..00000000000
--- a/gcc/d/dmd/typesem.c
+++ /dev/null
@@ -1,1462 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "root/dsystem.h"
-#include "root/checkedint.h"
-
-#include "mtype.h"
-#include "aggregate.h"
-#include "enum.h"
-#include "errors.h"
-#include "expression.h"
-#include "hdrgen.h"
-#include "id.h"
-#include "init.h"
-#include "parse.h"
-#include "scope.h"
-#include "target.h"
-#include "template.h"
-#include "visitor.h"
-
-Expression *typeToExpression(Type *t);
-Expression *typeToExpressionHelper(TypeQualified *t, Expression *e, size_t i = 0);
-bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps);
-char *MODtoChars(MOD mod);
-
-class TypeToExpressionVisitor : public Visitor
-{
-public:
- Expression *result;
- Type *itype;
-
- TypeToExpressionVisitor(Type *itype)
- {
- this->result = NULL;
- this->itype = itype;
- }
-
- void visit(Type *)
- {
- result = NULL;
- }
-
- void visit(TypeSArray *t)
- {
- Expression *e = typeToExpression(t->next);
- if (e)
- e = new ArrayExp(t->dim->loc, e, t->dim);
- result = e;
- }
-
- void visit(TypeAArray *t)
- {
- Expression *e = typeToExpression(t->next);
- if (e)
- {
- Expression *ei = typeToExpression(t->index);
- if (ei)
- {
- result = new ArrayExp(t->loc, e, ei);
- return;
- }
- }
- result = NULL;
- }
-
- void visit(TypeIdentifier *t)
- {
- result = typeToExpressionHelper(t, new IdentifierExp(t->loc, t->ident));
- }
-
- void visit(TypeInstance *t)
- {
- result = typeToExpressionHelper(t, new ScopeExp(t->loc, t->tempinst));
- }
-
- void visit(TypeMixin *t)
- {
- result = new TypeExp(t->loc, t);
- }
-};
-
-/* We've mistakenly parsed this as a type.
- * Redo it as an Expression.
- * NULL if cannot.
- */
-Expression *typeToExpression(Type *t)
-{
- if (t->mod)
- return NULL;
- TypeToExpressionVisitor v = TypeToExpressionVisitor(t);
- t->accept(&v);
- return v.result;
-}
-
-/* Helper function for `typeToExpression`. Contains common code
- * for TypeQualified derived classes.
- */
-Expression *typeToExpressionHelper(TypeQualified *t, Expression *e, size_t i)
-{
- //printf("toExpressionHelper(e = %s %s)\n", Token::toChars(e->op), e->toChars());
- for (; i < t->idents.length; i++)
- {
- RootObject *id = t->idents[i];
- //printf("\t[%d] e: '%s', id: '%s'\n", i, e->toChars(), id->toChars());
-
- switch (id->dyncast())
- {
- case DYNCAST_IDENTIFIER:
- {
- // ... '. ident'
- e = new DotIdExp(e->loc, e, (Identifier *)id);
- break;
- }
- case DYNCAST_DSYMBOL:
- {
- // ... '. name!(tiargs)'
- TemplateInstance *ti = ((Dsymbol *)id)->isTemplateInstance();
- assert(ti);
- e = new DotTemplateInstanceExp(e->loc, e, ti->name, ti->tiargs);
- break;
- }
- case DYNCAST_TYPE: // Bugzilla 1215
- {
- // ... '[type]'
- e = new ArrayExp(t->loc, e, new TypeExp(t->loc, (Type *)id));
- break;
- }
- case DYNCAST_EXPRESSION: // Bugzilla 1215
- {
- // ... '[expr]'
- e = new ArrayExp(t->loc, e, (Expression *)id);
- break;
- }
- default:
- assert(0);
- }
- }
- return e;
-}
-
-/**************************
- * This evaluates exp while setting length to be the number
- * of elements in the tuple t.
- */
-static Expression *semanticLength(Scope *sc, Type *t, Expression *exp)
-{
- if (t->ty == Ttuple)
- {
- ScopeDsymbol *sym = new ArrayScopeSymbol(sc, (TypeTuple *)t);
- sym->parent = sc->scopesym;
- sc = sc->push(sym);
-
- sc = sc->startCTFE();
- exp = expressionSemantic(exp, sc);
- sc = sc->endCTFE();
-
- sc->pop();
- }
- else
- {
- sc = sc->startCTFE();
- exp = expressionSemantic(exp, sc);
- sc = sc->endCTFE();
- }
-
- return exp;
-}
-
-static Expression *semanticLength(Scope *sc, TupleDeclaration *s, Expression *exp)
-{
- ScopeDsymbol *sym = new ArrayScopeSymbol(sc, s);
- sym->parent = sc->scopesym;
- sc = sc->push(sym);
-
- sc = sc->startCTFE();
- exp = expressionSemantic(exp, sc);
- sc = sc->endCTFE();
-
- sc->pop();
- return exp;
-}
-
-/******************************************
- * Compile the MixinType, returning the type or expression AST.
- *
- * Doesn't run semantic() on the returned object.
- * Params:
- * tm = mixin to compile as a type or expression
- * loc = location for error messages
- * sc = context
- * Return:
- * null if error, else RootObject AST as parsed
- */
-RootObject *compileTypeMixin(TypeMixin *tm, Loc loc, Scope *sc)
-{
- OutBuffer buf;
- if (expressionsToString(buf, sc, tm->exps))
- return NULL;
-
- const unsigned errors = global.errors;
- const size_t len = buf.length();
- const char *str = buf.extractChars();
- Parser p(loc, sc->_module, (const utf8_t *)str, len, false);
- p.nextToken();
- //printf("p.loc.linnum = %d\n", p.loc.linnum);
-
- RootObject *o = p.parseTypeOrAssignExp(TOKeof);
- if (errors != global.errors)
- {
- assert(global.errors != errors); // should have caught all these cases
- return NULL;
- }
- if (p.token.value != TOKeof)
- {
- ::error(loc, "incomplete mixin type `%s`", str);
- return NULL;
- }
-
- Type *t = isType(o);
- Expression *e = t ? typeToExpression(t) : isExpression(o);
-
- return (!e && t) ? (RootObject *)t : (RootObject *)e;
-}
-
-/******************************************
- * Perform semantic analysis on a type.
- * Params:
- * type = Type AST node
- * loc = the location of the type
- * sc = context
- * Returns:
- * `Type` with completed semantic analysis, `Terror` if errors
- * were encountered
- */
-Type *typeSemantic(Type *type, const Loc &loc, Scope *sc)
-{
- class TypeSemanticVisitor : public Visitor
- {
- public:
- Loc loc;
- Scope *sc;
- Type *result;
-
- TypeSemanticVisitor(const Loc &loc, Scope *sc)
- {
- this->loc = loc;
- this->sc = sc;
- this->result = NULL;
- }
-
- private:
- void error()
- {
- result = Type::terror;
- }
-
- public:
- void visit(Type *t)
- {
- if (t->ty == Tint128 || t->ty == Tuns128)
- {
- ::error(loc, "cent and ucent types not implemented");
- return error();
- }
-
- result = t->merge();
- }
-
- void visit(TypeVector *mtype)
- {
- unsigned int errors = global.errors;
- mtype->basetype = typeSemantic(mtype->basetype, loc, sc);
- if (errors != global.errors)
- return error();
- mtype->basetype = mtype->basetype->toBasetype()->mutableOf();
- if (mtype->basetype->ty != Tsarray)
- {
- ::error(loc, "T in __vector(T) must be a static array, not %s", mtype->basetype->toChars());
- return error();
- }
- TypeSArray *t = (TypeSArray *)mtype->basetype;
- int sz = (int)t->size(loc);
- switch (target.isVectorTypeSupported(sz, t->nextOf()))
- {
- case 0: // valid
- break;
- case 1: // no support at all
- ::error(loc, "SIMD vector types not supported on this platform");
- return error();
- case 2: // invalid base type
- ::error(loc, "vector type %s is not supported on this platform", mtype->toChars());
- return error();
- case 3: // invalid size
- ::error(loc, "%d byte vector type %s is not supported on this platform", sz, mtype->toChars());
- return error();
- default:
- assert(0);
- }
- result = mtype->merge();
- }
-
- void visit(TypeSArray *mtype)
- {
- //printf("TypeSArray::semantic() %s\n", mtype->toChars());
-
- Type *t;
- Expression *e;
- Dsymbol *s;
- mtype->next->resolve(loc, sc, &e, &t, &s);
-
- if (mtype->dim && s && s->isTupleDeclaration())
- {
- TupleDeclaration *sd = s->isTupleDeclaration();
-
- mtype->dim = semanticLength(sc, sd, mtype->dim);
- mtype->dim = mtype->dim->ctfeInterpret();
- if(mtype->dim->op == TOKerror)
- return error();
-
- uinteger_t d = mtype->dim->toUInteger();
- if (d >= sd->objects->length)
- {
- ::error(loc, "tuple index %llu exceeds %llu", (unsigned long long)d, (unsigned long long)sd->objects->length);
- return error();
- }
-
- RootObject *o = (*sd->objects)[(size_t)d];
- if (o->dyncast() != DYNCAST_TYPE)
- {
- ::error(loc, "%s is not a type", mtype->toChars());
- return error();
- }
- result = ((Type *)o)->addMod(mtype->mod);
- return;
- }
-
- if (t && t->ty == Terror)
- return error();
-
- Type *tn = typeSemantic(mtype->next, loc, sc);
- if (tn->ty == Terror)
- return error();
-
- Type *tbn = tn->toBasetype();
- if (mtype->dim)
- {
- unsigned int errors = global.errors;
- mtype->dim = semanticLength(sc, tbn, mtype->dim);
- if (errors != global.errors)
- return error();
-
- mtype->dim = mtype->dim->optimize(WANTvalue);
- mtype->dim = mtype->dim->ctfeInterpret();
- if (mtype->dim->op == TOKerror)
- return error();
- errors = global.errors;
- dinteger_t d1 = mtype->dim->toInteger();
- if (errors != global.errors)
- return error();
-
- mtype->dim = mtype->dim->implicitCastTo(sc, Type::tsize_t);
- mtype->dim = mtype->dim->optimize(WANTvalue);
- if (mtype->dim->op == TOKerror)
- return error();
- errors = global.errors;
- dinteger_t d2 = mtype->dim->toInteger();
- if (errors != global.errors)
- return error();
-
- if (mtype->dim->op == TOKerror)
- return error();
-
- if (d1 != d2)
- {
- Loverflow:
- ::error(loc, "%s size %llu * %llu exceeds 0x%llx size limit for static array",
- mtype->toChars(), (unsigned long long)tbn->size(loc), (unsigned long long)d1, target.maxStaticDataSize);
- return error();
- }
-
- Type *tbx = tbn->baseElemOf();
- if ((tbx->ty == Tstruct && !((TypeStruct *)tbx)->sym->members) ||
- (tbx->ty == Tenum && !((TypeEnum *)tbx)->sym->members))
- {
- /* To avoid meaningless error message, skip the total size limit check
- * when the bottom of element type is opaque.
- */
- }
- else if (tbn->isTypeBasic() ||
- tbn->ty == Tpointer ||
- tbn->ty == Tarray ||
- tbn->ty == Tsarray ||
- tbn->ty == Taarray ||
- (tbn->ty == Tstruct && (((TypeStruct *)tbn)->sym->sizeok == SIZEOKdone)) ||
- tbn->ty == Tclass)
- {
- /* Only do this for types that don't need to have semantic()
- * run on them for the size, since they may be forward referenced.
- */
- bool overflow = false;
- if (mulu(tbn->size(loc), d2, overflow) >= target.maxStaticDataSize || overflow)
- goto Loverflow;
- }
- }
- switch (tbn->ty)
- {
- case Ttuple:
- {
- // Index the tuple to get the type
- assert(mtype->dim);
- TypeTuple *tt = (TypeTuple *)tbn;
- uinteger_t d = mtype->dim->toUInteger();
- if (d >= tt->arguments->length)
- {
- ::error(loc, "tuple index %llu exceeds %llu", (unsigned long long)d, (unsigned long long)tt->arguments->length);
- return error();
- }
- Type *telem = (*tt->arguments)[(size_t)d]->type;
- result = telem->addMod(mtype->mod);
- return;
- }
- case Tfunction:
- case Tnone:
- ::error(loc, "cannot have array of %s", tbn->toChars());
- return error();
- default:
- break;
- }
- if (tbn->isscope())
- {
- ::error(loc, "cannot have array of scope %s", tbn->toChars());
- return error();
- }
-
- /* Ensure things like const(immutable(T)[3]) become immutable(T[3])
- * and const(T)[3] become const(T[3])
- */
- mtype->next = tn;
- mtype->transitive();
- result = mtype->addMod(tn->mod)->merge();
- }
-
- void visit(TypeDArray *mtype)
- {
- Type *tn = typeSemantic(mtype->next, loc,sc);
- Type *tbn = tn->toBasetype();
- switch (tbn->ty)
- {
- case Ttuple:
- result = tbn;
- return;
- case Tfunction:
- case Tnone:
- ::error(loc, "cannot have array of %s", tbn->toChars());
- return error();
- case Terror:
- return error();
- default:
- break;
- }
- if (tn->isscope())
- {
- ::error(loc, "cannot have array of scope %s", tn->toChars());
- return error();
- }
- mtype->next = tn;
- mtype->transitive();
- result = mtype->merge();
- }
-
- void visit(TypeAArray *mtype)
- {
- //printf("TypeAArray::semantic() %s index->ty = %d\n", mtype->toChars(), mtype->index->ty);
- if (mtype->deco)
- {
- result = mtype;
- return;
- }
-
- mtype->loc = loc;
- mtype->sc = sc;
- if (sc)
- sc->setNoFree();
-
- // Deal with the case where we thought the index was a type, but
- // in reality it was an expression.
- if (mtype->index->ty == Tident || mtype->index->ty == Tinstance || mtype->index->ty == Tsarray ||
- mtype->index->ty == Ttypeof || mtype->index->ty == Treturn || mtype->index->ty == Tmixin)
- {
- Expression *e;
- Type *t;
- Dsymbol *s;
-
- mtype->index->resolve(loc, sc, &e, &t, &s);
- if (e)
- {
- // It was an expression -
- // Rewrite as a static array
- TypeSArray *tsa = new TypeSArray(mtype->next, e);
- result = typeSemantic(tsa, loc, sc);
- return;
- }
- else if (t)
- mtype->index = typeSemantic(t, loc, sc);
- else
- {
- mtype->index->error(loc, "index is not a type or an expression");
- return error();
- }
- }
- else
- mtype->index = typeSemantic(mtype->index, loc,sc);
- mtype->index = mtype->index->merge2();
-
- if (mtype->index->nextOf() && !mtype->index->nextOf()->isImmutable())
- {
- mtype->index = mtype->index->constOf()->mutableOf();
- }
-
- switch (mtype->index->toBasetype()->ty)
- {
- case Tfunction:
- case Tvoid:
- case Tnone:
- case Ttuple:
- ::error(loc, "cannot have associative array key of %s", mtype->index->toBasetype()->toChars());
- /* fall through */
- case Terror:
- return error();
- default:
- break;
- }
- Type *tbase = mtype->index->baseElemOf();
- while (tbase->ty == Tarray)
- tbase = tbase->nextOf()->baseElemOf();
- if (tbase->ty == Tstruct)
- {
- /* AA's need typeid(index).equals() and getHash(). Issue error if not correctly set up.
- */
- StructDeclaration *sd = ((TypeStruct *)tbase)->sym;
- if (sd->semanticRun < PASSsemanticdone)
- dsymbolSemantic(sd, NULL);
-
- // duplicate a part of StructDeclaration::semanticTypeInfoMembers
- //printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", mtype->toChars(), sd->xeq, sd->xerreq, sd->xhash);
- if (sd->xeq &&
- sd->xeq->_scope &&
- sd->xeq->semanticRun < PASSsemantic3done)
- {
- unsigned errors = global.startGagging();
- semantic3(sd->xeq, sd->xeq->_scope);
- if (global.endGagging(errors))
- sd->xeq = sd->xerreq;
- }
-
- const char *s = (mtype->index->toBasetype()->ty != Tstruct) ? "bottom of " : "";
- if (!sd->xeq)
- {
- // If sd->xhash != NULL:
- // sd or its fields have user-defined toHash.
- // AA assumes that its result is consistent with bitwise equality.
- // else:
- // bitwise equality & hashing
- }
- else if (sd->xeq == sd->xerreq)
- {
- if (search_function(sd, Id::eq))
- {
- ::error(loc, "%sAA key type %s does not have `bool opEquals(ref const %s) const`",
- s, sd->toChars(), sd->toChars());
- }
- else
- {
- ::error(loc, "%sAA key type %s does not support const equality",
- s, sd->toChars());
- }
- return error();
- }
- else if (!sd->xhash)
- {
- if (search_function(sd, Id::eq))
- {
- ::error(loc, "%sAA key type %s should have `size_t toHash() const nothrow @safe` if opEquals defined",
- s, sd->toChars());
- }
- else
- {
- ::error(loc, "%sAA key type %s supports const equality but doesn't support const hashing",
- s, sd->toChars());
- }
- return error();
- }
- else
- {
- // defined equality & hashing
- assert(sd->xeq && sd->xhash);
-
- /* xeq and xhash may be implicitly defined by compiler. For example:
- * struct S { int[] arr; }
- * With 'arr' field equality and hashing, compiler will implicitly
- * generate functions for xopEquals and xtoHash in TypeInfo_Struct.
- */
- }
- }
- else if (tbase->ty == Tclass && !((TypeClass *)tbase)->sym->isInterfaceDeclaration())
- {
- ClassDeclaration *cd = ((TypeClass *)tbase)->sym;
- if (cd->semanticRun < PASSsemanticdone)
- dsymbolSemantic(cd, NULL);
-
- if (!ClassDeclaration::object)
- {
- ::error(Loc(), "missing or corrupt object.d");
- fatal();
- }
-
- static FuncDeclaration *feq = NULL;
- static FuncDeclaration *fcmp = NULL;
- static FuncDeclaration *fhash = NULL;
- if (!feq) feq = search_function(ClassDeclaration::object, Id::eq)->isFuncDeclaration();
- if (!fcmp) fcmp = search_function(ClassDeclaration::object, Id::cmp)->isFuncDeclaration();
- if (!fhash) fhash = search_function(ClassDeclaration::object, Id::tohash)->isFuncDeclaration();
- assert(fcmp && feq && fhash);
-
- if (feq->vtblIndex < (int)cd->vtbl.length && cd->vtbl[feq ->vtblIndex] == feq)
- {
- if (fcmp->vtblIndex < (int)cd->vtbl.length && cd->vtbl[fcmp->vtblIndex] != fcmp)
- {
- const char *s = (mtype->index->toBasetype()->ty != Tclass) ? "bottom of " : "";
- ::error(loc, "%sAA key type %s now requires equality rather than comparison",
- s, cd->toChars());
- errorSupplemental(loc, "Please override Object.opEquals and toHash.");
- }
- }
- }
- mtype->next = typeSemantic(mtype->next, loc,sc)->merge2();
- mtype->transitive();
-
- switch (mtype->next->toBasetype()->ty)
- {
- case Tfunction:
- case Tvoid:
- case Tnone:
- case Ttuple:
- ::error(loc, "cannot have associative array of %s", mtype->next->toChars());
- /* fall through */
- case Terror:
- return error();
- }
- if (mtype->next->isscope())
- {
- ::error(loc, "cannot have array of scope %s", mtype->next->toChars());
- return error();
- }
- result = mtype->merge();
- }
-
- void visit(TypePointer *mtype)
- {
- //printf("TypePointer::semantic() %s\n", mtype->toChars());
- if (mtype->deco)
- {
- result = mtype;
- return;
- }
- Type *n = typeSemantic(mtype->next, loc, sc);
- switch (n->toBasetype()->ty)
- {
- case Ttuple:
- ::error(loc, "cannot have pointer to %s", n->toChars());
- /* fall through */
- case Terror:
- return error();
- default:
- break;
- }
- if (n != mtype->next)
- {
- mtype->deco = NULL;
- }
- mtype->next = n;
- if (mtype->next->ty != Tfunction)
- {
- mtype->transitive();
- result = mtype->merge();
- return;
- }
- mtype->deco = mtype->merge()->deco;
- /* Don't return merge(), because arg identifiers and default args
- * can be different
- * even though the types match
- */
- result = mtype;
- }
-
- void visit(TypeReference *mtype)
- {
- //printf("TypeReference::semantic()\n");
- Type *n = typeSemantic(mtype->next, loc, sc);
- if (n != mtype->next)
- mtype->deco = NULL;
- mtype->next = n;
- mtype->transitive();
- result = mtype->merge();
- }
-
-
- void visit(TypeFunction *mtype)
- {
- if (mtype->deco) // if semantic() already run
- {
- //printf("already done\n");
- result = mtype;
- return;
- }
- //printf("TypeFunction::semantic() this = %p\n", this);
- //printf("TypeFunction::semantic() %s, sc->stc = %llx, fargs = %p\n", mtype->toChars(), sc->stc, mtype->fargs);
-
- bool errors = false;
-
- if (mtype->inuse > global.recursionLimit)
- {
- mtype->inuse = 0;
- ::error(loc, "recursive type");
- return error();
- }
-
- /* Copy in order to not mess up original.
- * This can produce redundant copies if inferring return type,
- * as semantic() will get called again on this.
- */
- TypeFunction *tf = mtype->copy()->toTypeFunction();
- if (mtype->parameterList.parameters)
- {
- tf->parameterList.parameters = mtype->parameterList.parameters->copy();
- for (size_t i = 0; i < mtype->parameterList.parameters->length; i++)
- {
- void *pp = mem.xmalloc(sizeof(Parameter));
- Parameter *p = (Parameter *)memcpy(pp, (void *)(*mtype->parameterList.parameters)[i],
- sizeof(Parameter));
- (*tf->parameterList.parameters)[i] = p;
- }
- }
-
- if (sc->stc & STCpure)
- tf->purity = PUREfwdref;
- if (sc->stc & STCnothrow)
- tf->isnothrow = true;
- if (sc->stc & STCnogc)
- tf->isnogc = true;
- if (sc->stc & STCref)
- tf->isref = true;
- if (sc->stc & STCreturn)
- tf->isreturn = true;
- if (sc->stc & STCscope)
- tf->isscope = true;
- if (sc->stc & STCscopeinferred)
- tf->isscopeinferred = true;
- //if ((sc->stc & (STCreturn | STCref)) == STCreturn)
- // tf->isscope = true; // return by itself means 'return scope'
-
- if (tf->trust == TRUSTdefault)
- {
- if (sc->stc & STCsafe)
- tf->trust = TRUSTsafe;
- if (sc->stc & STCsystem)
- tf->trust = TRUSTsystem;
- if (sc->stc & STCtrusted)
- tf->trust = TRUSTtrusted;
- }
-
- if (sc->stc & STCproperty)
- tf->isproperty = true;
-
- tf->linkage = sc->linkage;
- bool wildreturn = false;
- if (tf->next)
- {
- sc = sc->push();
- sc->stc &= ~(STC_TYPECTOR | STC_FUNCATTR);
- tf->next = typeSemantic(tf->next, loc, sc);
- sc = sc->pop();
- errors |= tf->checkRetType(loc);
- if (tf->next->isscope() && !(sc->flags & SCOPEctor))
- {
- ::error(loc, "functions cannot return scope %s", tf->next->toChars());
- errors = true;
- }
- if (tf->next->hasWild())
- wildreturn = true;
-
- if (tf->isreturn && !tf->isref && !tf->next->hasPointers())
- {
- tf->isreturn = false;
- }
- }
-
- unsigned char wildparams = 0;
- if (tf->parameterList.parameters)
- {
- /* Create a scope for evaluating the default arguments for the parameters
- */
- Scope *argsc = sc->push();
- argsc->stc = 0; // don't inherit storage class
- argsc->protection = Prot(Prot::public_);
- argsc->func = NULL;
-
- size_t dim = tf->parameterList.length();
- for (size_t i = 0; i < dim; i++)
- {
- Parameter *fparam = tf->parameterList[i];
- mtype->inuse++;
- fparam->type = typeSemantic(fparam->type, loc, argsc);
- mtype->inuse--;
-
- if (fparam->type->ty == Terror)
- {
- errors = true;
- continue;
- }
-
- fparam->type = fparam->type->addStorageClass(fparam->storageClass);
-
- if (fparam->storageClass & (STCauto | STCalias | STCstatic))
- {
- if (!fparam->type)
- continue;
- }
-
- Type *t = fparam->type->toBasetype();
-
- if (t->ty == Tfunction)
- {
- ::error(loc, "cannot have parameter of function type %s", fparam->type->toChars());
- errors = true;
- }
- else if (!(fparam->storageClass & (STCref | STCout)) &&
- (t->ty == Tstruct || t->ty == Tsarray || t->ty == Tenum))
- {
- Type *tb2 = t->baseElemOf();
- if ((tb2->ty == Tstruct && !((TypeStruct *)tb2)->sym->members) ||
- (tb2->ty == Tenum && !((TypeEnum *)tb2)->sym->memtype))
- {
- ::error(loc, "cannot have parameter of opaque type %s by value", fparam->type->toChars());
- errors = true;
- }
- }
- else if (!(fparam->storageClass & STClazy) && t->ty == Tvoid)
- {
- ::error(loc, "cannot have parameter of type %s", fparam->type->toChars());
- errors = true;
- }
-
- if ((fparam->storageClass & (STCref | STCwild)) == (STCref | STCwild))
- {
- // 'ref inout' implies 'return'
- fparam->storageClass |= STCreturn;
- }
-
- if (fparam->storageClass & STCreturn)
- {
- if (fparam->storageClass & (STCref | STCout))
- {
- // Disabled for the moment awaiting improvement to allow return by ref
- // to be transformed into return by scope.
- if (0 && !tf->isref)
- {
- StorageClass stc = fparam->storageClass & (STCref | STCout);
- ::error(loc, "parameter `%s` is `return %s` but function does not return by `ref`",
- fparam->ident ? fparam->ident->toChars() : "",
- stcToChars(stc));
- errors = true;
- }
- }
- else
- {
- fparam->storageClass |= STCscope; // 'return' implies 'scope'
- if (tf->isref)
- {
- }
- else if (!tf->isref && tf->next && !tf->next->hasPointers())
- {
- fparam->storageClass &= STCreturn; // https://issues.dlang.org/show_bug.cgi?id=18963
- }
- }
- }
-
- if (fparam->storageClass & (STCref | STClazy))
- {
- }
- else if (fparam->storageClass & STCout)
- {
- if (unsigned char m = fparam->type->mod & (MODimmutable | MODconst | MODwild))
- {
- ::error(loc, "cannot have %s out parameter of type %s", MODtoChars(m), t->toChars());
- errors = true;
- }
- else
- {
- Type *tv = t;
- while (tv->ty == Tsarray)
- tv = tv->nextOf()->toBasetype();
- if (tv->ty == Tstruct && ((TypeStruct *)tv)->sym->noDefaultCtor)
- {
- ::error(loc, "cannot have out parameter of type %s because the default construction is disabled",
- fparam->type->toChars());
- errors = true;
- }
- }
- }
-
- if (fparam->storageClass & STCscope && !fparam->type->hasPointers() && fparam->type->ty != Ttuple)
- {
- fparam->storageClass &= ~STCscope;
- if (!(fparam->storageClass & STCref))
- fparam->storageClass &= ~STCreturn;
- }
-
- if (t->hasWild())
- {
- wildparams |= 1;
- //if (tf->next && !wildreturn)
- // ::error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with `ref`)");
- }
-
- if (fparam->defaultArg)
- {
- Expression *e = fparam->defaultArg;
- if (fparam->storageClass & (STCref | STCout))
- {
- e = expressionSemantic(e, argsc);
- e = resolveProperties(argsc, e);
- }
- else
- {
- e = inferType(e, fparam->type);
- Initializer *iz = new ExpInitializer(e->loc, e);
- iz = initializerSemantic(iz, argsc, fparam->type, INITnointerpret);
- e = initializerToExpression(iz);
- }
- if (e->op == TOKfunction) // see Bugzilla 4820
- {
- FuncExp *fe = (FuncExp *)e;
- // Replace function literal with a function symbol,
- // since default arg expression must be copied when used
- // and copying the literal itself is wrong.
- e = new VarExp(e->loc, fe->fd, false);
- e = new AddrExp(e->loc, e);
- e = expressionSemantic(e, argsc);
- }
- e = e->implicitCastTo(argsc, fparam->type);
-
- // default arg must be an lvalue
- if (fparam->storageClass & (STCout | STCref))
- e = e->toLvalue(argsc, e);
-
- fparam->defaultArg = e;
- if (e->op == TOKerror)
- errors = true;
- }
-
- /* If fparam after semantic() turns out to be a tuple, the number of parameters may
- * change.
- */
- if (t->ty == Ttuple)
- {
- /* TypeFunction::parameter also is used as the storage of
- * Parameter objects for FuncDeclaration. So we should copy
- * the elements of TypeTuple::arguments to avoid unintended
- * sharing of Parameter object among other functions.
- */
- TypeTuple *tt = (TypeTuple *)t;
- if (tt->arguments && tt->arguments->length)
- {
- /* Propagate additional storage class from tuple parameters to their
- * element-parameters.
- * Make a copy, as original may be referenced elsewhere.
- */
- size_t tdim = tt->arguments->length;
- Parameters *newparams = new Parameters();
- newparams->setDim(tdim);
- for (size_t j = 0; j < tdim; j++)
- {
- Parameter *narg = (*tt->arguments)[j];
-
- // Bugzilla 12744: If the storage classes of narg
- // conflict with the ones in fparam, it's ignored.
- StorageClass stc = fparam->storageClass | narg->storageClass;
- StorageClass stc1 = fparam->storageClass & (STCref | STCout | STClazy);
- StorageClass stc2 = narg->storageClass & (STCref | STCout | STClazy);
- if (stc1 && stc2 && stc1 != stc2)
- {
- OutBuffer buf1; stcToBuffer(&buf1, stc1 | ((stc1 & STCref) ? (fparam->storageClass & STCauto) : 0));
- OutBuffer buf2; stcToBuffer(&buf2, stc2);
-
- ::error(loc, "incompatible parameter storage classes `%s` and `%s`",
- buf1.peekChars(), buf2.peekChars());
- errors = true;
- stc = stc1 | (stc & ~(STCref | STCout | STClazy));
- }
-
- (*newparams)[j] = new Parameter(
- stc, narg->type, narg->ident, narg->defaultArg, narg->userAttribDecl);
- }
- fparam->type = new TypeTuple(newparams);
- }
- fparam->storageClass = 0;
-
- /* Reset number of parameters, and back up one to do this fparam again,
- * now that it is a tuple
- */
- dim = tf->parameterList.length();
- i--;
- continue;
- }
-
- /* Resolve "auto ref" storage class to be either ref or value,
- * based on the argument matching the parameter
- */
- if (fparam->storageClass & STCauto)
- {
- if (mtype->fargs && i < mtype->fargs->length && (fparam->storageClass & STCref))
- {
- Expression *farg = (*mtype->fargs)[i];
- if (farg->isLvalue())
- ; // ref parameter
- else
- fparam->storageClass &= ~STCref; // value parameter
- fparam->storageClass &= ~STCauto; // Bugzilla 14656
- fparam->storageClass |= STCautoref;
- }
- else
- {
- ::error(loc, "`auto` can only be used as part of `auto ref` for template function parameters");
- errors = true;
- }
- }
-
- // Remove redundant storage classes for type, they are already applied
- fparam->storageClass &= ~(STC_TYPECTOR | STCin);
- }
- argsc->pop();
- }
- if (tf->isWild())
- wildparams |= 2;
-
- if (wildreturn && !wildparams)
- {
- ::error(loc, "inout on return means inout must be on a parameter as well for %s", mtype->toChars());
- errors = true;
- }
- tf->iswild = wildparams;
-
- if (tf->isproperty && (tf->parameterList.varargs != VARARGnone || tf->parameterList.length() > 2))
- {
- ::error(loc, "properties can only have zero, one, or two parameter");
- errors = true;
- }
-
- if (tf->parameterList.varargs == VARARGvariadic && tf->linkage != LINKd && tf->parameterList.length() == 0)
- {
- ::error(loc, "variadic functions with non-D linkage must have at least one parameter");
- errors = true;
- }
-
- if (errors)
- return error();
-
- if (tf->next)
- tf->deco = tf->merge()->deco;
-
- /* Don't return merge(), because arg identifiers and default args
- * can be different
- * even though the types match
- */
- result = tf;
- }
-
- void visit(TypeDelegate *mtype)
- {
- //printf("TypeDelegate::semantic() %s\n", mtype->toChars());
- if (mtype->deco) // if semantic() already run
- {
- //printf("already done\n");
- result = mtype;
- return;
- }
- mtype->next = typeSemantic(mtype->next, loc,sc);
- if (mtype->next->ty != Tfunction)
- return error();
-
- /* In order to deal with Bugzilla 4028, perhaps default arguments should
- * be removed from next before the merge.
- */
-
- /* Don't return merge(), because arg identifiers and default args
- * can be different
- * even though the types match
- */
- mtype->deco = mtype->merge()->deco;
- result = mtype;
- }
-
- void visit(TypeTraits *mtype)
- {
- if (mtype->ty == Terror)
- {
- result = mtype;
- return;
- }
-
- const int inAlias = (sc->flags & SCOPEalias) != 0;
- if (mtype->exp->ident != Id::allMembers &&
- mtype->exp->ident != Id::derivedMembers &&
- mtype->exp->ident != Id::getMember &&
- mtype->exp->ident != Id::parent &&
- mtype->exp->ident != Id::child &&
- mtype->exp->ident != Id::toType &&
- mtype->exp->ident != Id::getOverloads &&
- mtype->exp->ident != Id::getVirtualFunctions &&
- mtype->exp->ident != Id::getVirtualMethods &&
- mtype->exp->ident != Id::getAttributes &&
- mtype->exp->ident != Id::getUnitTests &&
- mtype->exp->ident != Id::getAliasThis)
- {
- static const char *ctxt[2] = {"as type", "in alias"};
- ::error(loc, "trait `%s` is either invalid or not supported %s",
- mtype->exp->ident->toChars(), ctxt[inAlias]);
- mtype->ty = Terror;
- result = mtype;
- return;
- }
-
- if (Expression *e = semanticTraits(mtype->exp, sc))
- {
- switch (e->op)
- {
- case TOKdotvar:
- mtype->sym = ((DotVarExp *)e)->var;
- break;
- case TOKvar:
- mtype->sym = ((VarExp *)e)->var;
- break;
- case TOKfunction:
- {
- FuncExp *fe = (FuncExp *)e;
- if (fe->td)
- mtype->sym = fe->td;
- else
- mtype->sym = fe->fd;
- break;
- }
- case TOKdottd:
- mtype->sym = ((DotTemplateExp*)e)->td;
- break;
- case TOKdsymbol:
- mtype->sym = ((DsymbolExp *)e)->s;
- break;
- case TOKtemplate:
- mtype->sym = ((TemplateExp *)e)->td;
- break;
- case TOKscope:
- mtype->sym = ((ScopeExp *)e)->sds;
- break;
- case TOKtuple:
- {
- TupleExp *te = e->toTupleExp();
- Objects *elems = new Objects;
- elems->setDim(te->exps->length);
- for (size_t i = 0; i < elems->length; i++)
- {
- Expression *src = (*te->exps)[i];
- switch (src->op)
- {
- case TOKtype:
- (*elems)[i] = ((TypeExp *)src)->type;
- break;
- case TOKdottype:
- (*elems)[i] = ((DotTypeExp *)src)->type;
- break;
- case TOKoverloadset:
- (*elems)[i] = ((OverExp *)src)->type;
- break;
- default:
- if (Dsymbol *sym = isDsymbol(src))
- (*elems)[i] = sym;
- else
- (*elems)[i] = src;
- }
- }
- TupleDeclaration *td = new TupleDeclaration(e->loc,
- Identifier::generateId("__aliastup"), elems);
- mtype->sym = td;
- break;
- }
- case TOKdottype:
- result = isType(((DotTypeExp *)e)->sym);
- break;
- case TOKtype:
- result = ((TypeExp *)e)->type;
- break;
- case TOKoverloadset:
- result = ((OverExp *)e)->type;
- break;
- default:
- break;
- }
- }
-
- if (result)
- result = result->addMod(mtype->mod);
- if (!inAlias && !result)
- {
- if (!global.errors)
- ::error(loc, "`%s` does not give a valid type", mtype->toChars());
- return error();
- }
- }
-
- void visit(TypeIdentifier *mtype)
- {
- Type *t;
- Expression *e;
- Dsymbol *s;
-
- //printf("TypeIdentifier::semantic(%s)\n", mtype->toChars());
- mtype->resolve(loc, sc, &e, &t, &s);
- if (t)
- {
- //printf("\tit's a type %d, %s, %s\n", t->ty, t->toChars(), t->deco);
- t = t->addMod(mtype->mod);
- }
- else
- {
- if (s)
- {
- s->error(loc, "is used as a type");
- //halt();
- }
- else
- ::error(loc, "%s is used as a type", mtype->toChars());
- return error();
- }
- //t->print();
- result = t;
- }
-
- void visit(TypeInstance *mtype)
- {
- Type *t;
- Expression *e;
- Dsymbol *s;
-
- //printf("TypeInstance::semantic(%p, %s)\n", this, mtype->toChars());
- {
- unsigned errors = global.errors;
- mtype->resolve(loc, sc, &e, &t, &s);
- // if we had an error evaluating the symbol, suppress further errors
- if (!t && errors != global.errors)
- return error();
- }
-
- if (!t)
- {
- if (!e && s && s->errors)
- {
- // if there was an error evaluating the symbol, it might actually
- // be a type. Avoid misleading error messages.
- ::error(loc, "%s had previous errors", mtype->toChars());
- }
- else
- ::error(loc, "%s is used as a type", mtype->toChars());
- return error();
- }
- result = t;
- }
-
- void visit(TypeTypeof *mtype)
- {
- //printf("TypeTypeof::semantic() %s\n", mtype->toChars());
-
- Expression *e;
- Type *t;
- Dsymbol *s;
- mtype->resolve(loc, sc, &e, &t, &s);
- if (s && (t = s->getType()) != NULL)
- t = t->addMod(mtype->mod);
- if (!t)
- {
- ::error(loc, "%s is used as a type", mtype->toChars());
- return error();
- }
- result = t;
- }
-
- void visit(TypeReturn *mtype)
- {
- //printf("TypeReturn::semantic() %s\n", mtype->toChars());
-
- Expression *e;
- Type *t;
- Dsymbol *s;
- mtype->resolve(loc, sc, &e, &t, &s);
- if (s && (t = s->getType()) != NULL)
- t = t->addMod(mtype->mod);
- if (!t)
- {
- ::error(loc, "%s is used as a type", mtype->toChars());
- return error();
- }
- result = t;
- }
-
- void visit(TypeEnum *mtype)
- {
- //printf("TypeEnum::semantic() %s\n", mtype->toChars());
- result = mtype->deco ? mtype : mtype->merge();
- }
-
- void visit(TypeStruct *mtype)
- {
- //printf("TypeStruct::semantic('%s')\n", mtype->toChars());
- if (mtype->deco)
- {
- if (sc && sc->cppmangle != CPPMANGLEdefault)
- {
- if (mtype->cppmangle == CPPMANGLEdefault)
- mtype->cppmangle = sc->cppmangle;
- else
- assert(mtype->cppmangle == sc->cppmangle);
- }
- result = mtype;
- return;
- }
-
- /* Don't semantic for sym because it should be deferred until
- * sizeof needed or its members accessed.
- */
- // instead, parent should be set correctly
- assert(mtype->sym->parent);
-
- if (mtype->sym->type->ty == Terror)
- return error();
- if (sc)
- mtype->cppmangle = sc->cppmangle;
- result = mtype->merge();
- }
-
- void visit(TypeClass *mtype)
- {
- //printf("TypeClass::semantic(%s)\n", mtype->toChars());
- if (mtype->deco)
- {
- if (sc && sc->cppmangle != CPPMANGLEdefault)
- {
- if (mtype->cppmangle == CPPMANGLEdefault)
- mtype->cppmangle = sc->cppmangle;
- else
- assert(mtype->cppmangle == sc->cppmangle);
- }
- result = mtype;
- return;
- }
-
- /* Don't semantic for sym because it should be deferred until
- * sizeof needed or its members accessed.
- */
- // instead, parent should be set correctly
- assert(mtype->sym->parent);
-
- if (mtype->sym->type->ty == Terror)
- return error();
- if (sc)
- mtype->cppmangle = sc->cppmangle;
- result = mtype->merge();
- }
-
- void visit(TypeTuple *mtype)
- {
- //printf("TypeTuple::semantic(this = %p)\n", this);
- //printf("TypeTuple::semantic() %p, %s\n", this, mtype->toChars());
- if (!mtype->deco)
- mtype->deco = mtype->merge()->deco;
-
- /* Don't return merge(), because a tuple with one type has the
- * same deco as that type.
- */
- result = mtype;
- }
-
- void visit(TypeSlice *mtype)
- {
- //printf("TypeSlice::semantic() %s\n", mtype->toChars());
- Type *tn = typeSemantic(mtype->next, loc, sc);
- //printf("next: %s\n", tn->toChars());
-
- Type *tbn = tn->toBasetype();
- if (tbn->ty != Ttuple)
- {
- ::error(loc, "can only slice tuple types, not %s", tbn->toChars());
- return error();
- }
- TypeTuple *tt = (TypeTuple *)tbn;
-
- mtype->lwr = semanticLength(sc, tbn, mtype->lwr);
- mtype->lwr = mtype->lwr->ctfeInterpret();
- uinteger_t i1 = mtype->lwr->toUInteger();
-
- mtype->upr = semanticLength(sc, tbn, mtype->upr);
- mtype->upr = mtype->upr->ctfeInterpret();
- uinteger_t i2 = mtype->upr->toUInteger();
-
- if (!(i1 <= i2 && i2 <= tt->arguments->length))
- {
- ::error(loc, "slice `[%llu..%llu]` is out of range of [0..%llu]",
- (unsigned long long)i1, (unsigned long long)i2, (unsigned long long)tt->arguments->length);
- return error();
- }
-
- mtype->next = tn;
- mtype->transitive();
-
- Parameters *args = new Parameters;
- args->reserve((size_t)(i2 - i1));
- for (size_t i = (size_t)i1; i < (size_t)i2; i++)
- {
- Parameter *arg = (*tt->arguments)[i];
- args->push(arg);
- }
- Type *t = new TypeTuple(args);
- result = typeSemantic(t, loc, sc);
- }
-
- void visit(TypeMixin *mtype)
- {
- //printf("TypeMixin::semantic() %s\n", mtype->toChars());
-
- Expression *e = NULL;
- Type *t = NULL;
- Dsymbol *s = NULL;
- mtype->resolve(loc, sc, &e, &t, &s);
-
- if (t && t->ty != Terror)
- {
- result = t;
- return;
- }
-
- ::error(mtype->loc, "`mixin(%s)` does not give a valid type", mtype->obj->toChars());
- return error();
- }
- };
- TypeSemanticVisitor v(loc, sc);
- type->accept(&v);
- return v.result;
-}
diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d
new file mode 100644
index 00000000000..ace4e423bcc
--- /dev/null
+++ b/gcc/d/dmd/typesem.d
@@ -0,0 +1,4896 @@
+/**
+ * Semantic analysis for D types.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/typesem.d, _typesem.d)
+ * Documentation: https://dlang.org/phobos/dmd_typesem.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typesem.d
+ */
+
+module dmd.typesem;
+
+import core.checkedint;
+import core.stdc.string;
+import core.stdc.stdio;
+
+import dmd.access;
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arrayop;
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.complex;
+import dmd.dcast;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.denum;
+import dmd.dimport;
+import dmd.dmangle;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dstruct;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.dtemplate;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.imphint;
+import dmd.init;
+import dmd.initsem;
+import dmd.visitor;
+import dmd.mtype;
+import dmd.objc;
+import dmd.opover;
+import dmd.parse;
+import dmd.root.ctfloat;
+import dmd.root.rmem;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.root.string;
+import dmd.root.stringtable;
+import dmd.safe;
+import dmd.semantic3;
+import dmd.sideeffect;
+import dmd.target;
+import dmd.tokens;
+
+/**************************
+ * This evaluates exp while setting length to be the number
+ * of elements in the tuple t.
+ */
+private Expression semanticLength(Scope* sc, Type t, Expression exp)
+{
+ if (auto tt = t.isTypeTuple())
+ {
+ ScopeDsymbol sym = new ArrayScopeSymbol(sc, tt);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+ sc = sc.startCTFE();
+ exp = exp.expressionSemantic(sc);
+ exp = resolveProperties(sc, exp);
+ sc = sc.endCTFE();
+ sc.pop();
+ }
+ else
+ {
+ sc = sc.startCTFE();
+ exp = exp.expressionSemantic(sc);
+ exp = resolveProperties(sc, exp);
+ sc = sc.endCTFE();
+ }
+ return exp;
+}
+
+private Expression semanticLength(Scope* sc, TupleDeclaration tup, Expression exp)
+{
+ ScopeDsymbol sym = new ArrayScopeSymbol(sc, tup);
+ sym.parent = sc.scopesym;
+
+ sc = sc.push(sym);
+ sc = sc.startCTFE();
+ exp = exp.expressionSemantic(sc);
+ exp = resolveProperties(sc, exp);
+ sc = sc.endCTFE();
+ sc.pop();
+
+ return exp;
+}
+
+/*************************************
+ * Resolve a tuple index, `s[oindex]`, by figuring out what `s[oindex]` represents.
+ * Setting one of pe/pt/ps.
+ * Params:
+ * loc = location for error messages
+ * sc = context
+ * s = symbol being indexed - could be a tuple, could be an expression
+ * pe = set if s[oindex] is an Expression, otherwise null
+ * pt = set if s[oindex] is a Type, otherwise null
+ * ps = set if s[oindex] is a Dsymbol, otherwise null
+ * oindex = index into s
+ */
+private void resolveTupleIndex(const ref Loc loc, Scope* sc, Dsymbol s, out Expression pe, out Type pt, out Dsymbol ps, RootObject oindex)
+{
+ auto tup = s.isTupleDeclaration();
+
+ auto eindex = isExpression(oindex);
+ auto tindex = isType(oindex);
+ auto sindex = isDsymbol(oindex);
+
+ if (!tup)
+ {
+ // It's really an index expression
+ if (tindex)
+ eindex = new TypeExp(loc, tindex);
+ else if (sindex)
+ eindex = symbolToExp(sindex, loc, sc, false);
+ Expression e = new IndexExp(loc, symbolToExp(s, loc, sc, false), eindex);
+ e = e.expressionSemantic(sc);
+ resolveExp(e, pt, pe, ps);
+ return;
+ }
+
+ // Convert oindex to Expression, then try to resolve to constant.
+ if (tindex)
+ tindex.resolve(loc, sc, eindex, tindex, sindex);
+ if (sindex)
+ eindex = symbolToExp(sindex, loc, sc, false);
+ if (!eindex)
+ {
+ .error(loc, "index `%s` is not an expression", oindex.toChars());
+ pt = Type.terror;
+ return;
+ }
+
+ eindex = semanticLength(sc, tup, eindex);
+ eindex = eindex.ctfeInterpret();
+ if (eindex.op == TOK.error)
+ {
+ pt = Type.terror;
+ return;
+ }
+ const(uinteger_t) d = eindex.toUInteger();
+ if (d >= tup.objects.dim)
+ {
+ .error(loc, "tuple index `%llu` exceeds length %zu", d, tup.objects.dim);
+ pt = Type.terror;
+ return;
+ }
+
+ RootObject o = (*tup.objects)[cast(size_t)d];
+ ps = isDsymbol(o);
+ if (auto t = isType(o))
+ pt = t.typeSemantic(loc, sc);
+ if (auto e = isExpression(o))
+ resolveExp(e, pt, pe, ps);
+}
+
+/*************************************
+ * Takes an array of Identifiers and figures out if
+ * it represents a Type, Expression, or Dsymbol.
+ * Params:
+ * mt = array of identifiers
+ * loc = location for error messages
+ * sc = context
+ * s = symbol to start search at
+ * scopesym = unused
+ * pe = set if expression otherwise null
+ * pt = set if type otherwise null
+ * ps = set if symbol otherwise null
+ * typeid = set if in TypeidExpression https://dlang.org/spec/expression.html#TypeidExpression
+ */
+private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym,
+ out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false)
+{
+ version (none)
+ {
+ printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, mt.toChars());
+ if (scopesym)
+ printf("\tscopesym = '%s'\n", scopesym.toChars());
+ }
+
+ if (!s)
+ {
+ /* Look for what user might have intended
+ */
+ const p = mt.mutableOf().unSharedOf().toChars();
+ auto id = Identifier.idPool(p, cast(uint)strlen(p));
+ if (const n = importHint(id.toString()))
+ error(loc, "`%s` is not defined, perhaps `import %.*s;` ?", p, cast(int)n.length, n.ptr);
+ else if (auto s2 = sc.search_correct(id))
+ error(loc, "undefined identifier `%s`, did you mean %s `%s`?", p, s2.kind(), s2.toChars());
+ else if (const q = Scope.search_correct_C(id))
+ error(loc, "undefined identifier `%s`, did you mean `%s`?", p, q);
+ else
+ error(loc, "undefined identifier `%s`", p);
+
+ pt = Type.terror;
+ return;
+ }
+
+ //printf("\t1: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind());
+ Declaration d = s.isDeclaration();
+ if (d && (d.storage_class & STC.templateparameter))
+ s = s.toAlias();
+ else
+ {
+ // check for deprecated or disabled aliases
+ // functions are checked after overloading
+ // templates are checked after matching constraints
+ if (!s.isFuncDeclaration() && !s.isTemplateDeclaration())
+ s.checkDeprecated(loc, sc);
+ if (d)
+ d.checkDisabled(loc, sc, true);
+ }
+ s = s.toAlias();
+ //printf("\t2: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind());
+ for (size_t i = 0; i < mt.idents.dim; i++)
+ {
+ RootObject id = mt.idents[i];
+ if (id.dyncast() == DYNCAST.expression ||
+ id.dyncast() == DYNCAST.type)
+ {
+ Type tx;
+ Expression ex;
+ Dsymbol sx;
+ resolveTupleIndex(loc, sc, s, ex, tx, sx, id);
+ if (sx)
+ {
+ s = sx.toAlias();
+ continue;
+ }
+ if (tx)
+ ex = new TypeExp(loc, tx);
+ assert(ex);
+
+ ex = typeToExpressionHelper(mt, ex, i + 1);
+ ex = ex.expressionSemantic(sc);
+ resolveExp(ex, pt, pe, ps);
+ return;
+ }
+
+ Type t = s.getType(); // type symbol, type alias, or type tuple?
+ uint errorsave = global.errors;
+ int flags = t is null ? SearchLocalsOnly : IgnorePrivateImports;
+
+ Dsymbol sm = s.searchX(loc, sc, id, flags);
+ if (sm)
+ {
+ if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, sm))
+ {
+ .error(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars());
+ sm = null;
+ }
+ // Same check as in Expression.semanticY(DotIdExp)
+ else if (sm.isPackage() && checkAccess(sc, cast(Package)sm))
+ {
+ // @@@DEPRECATED_2.096@@@
+ // Should be an error in 2.106. Just remove the deprecation call
+ // and uncomment the null assignment
+ deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", sm.kind(), sm.toPrettyChars(), sm.toPrettyChars());
+ //sm = null;
+ }
+ }
+ if (global.errors != errorsave)
+ {
+ pt = Type.terror;
+ return;
+ }
+
+ void helper3()
+ {
+ Expression e;
+ VarDeclaration v = s.isVarDeclaration();
+ FuncDeclaration f = s.isFuncDeclaration();
+ if (intypeid || !v && !f)
+ e = symbolToExp(s, loc, sc, true);
+ else
+ e = new VarExp(loc, s.isDeclaration(), true);
+
+ e = typeToExpressionHelper(mt, e, i);
+ e = e.expressionSemantic(sc);
+ resolveExp(e, pt, pe, ps);
+ }
+
+ //printf("\t3: s = %p %s %s, sm = %p\n", s, s.kind(), s.toChars(), sm);
+ if (intypeid && !t && sm && sm.needThis())
+ return helper3();
+
+ if (VarDeclaration v = s.isVarDeclaration())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=19913
+ // v.type would be null if it is a forward referenced member.
+ if (v.type is null)
+ v.dsymbolSemantic(sc);
+ if (v.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) ||
+ v.type.isConst() || v.type.isImmutable())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=13087
+ // this.field is not constant always
+ if (!v.isThisDeclaration())
+ return helper3();
+ }
+ }
+ if (!sm)
+ {
+ if (!t)
+ {
+ if (s.isDeclaration()) // var, func, or tuple declaration?
+ {
+ t = s.isDeclaration().type;
+ if (!t && s.isTupleDeclaration()) // expression tuple?
+ return helper3();
+ }
+ else if (s.isTemplateInstance() ||
+ s.isImport() || s.isPackage() || s.isModule())
+ {
+ return helper3();
+ }
+ }
+ if (t)
+ {
+ sm = t.toDsymbol(sc);
+ if (sm && id.dyncast() == DYNCAST.identifier)
+ {
+ sm = sm.search(loc, cast(Identifier)id, IgnorePrivateImports);
+ if (!sm)
+ return helper3();
+ }
+ else
+ return helper3();
+ }
+ else
+ {
+ if (id.dyncast() == DYNCAST.dsymbol)
+ {
+ // searchX already handles errors for template instances
+ assert(global.errors);
+ }
+ else
+ {
+ assert(id.dyncast() == DYNCAST.identifier);
+ sm = s.search_correct(cast(Identifier)id);
+ if (sm)
+ error(loc, "identifier `%s` of `%s` is not defined, did you mean %s `%s`?", id.toChars(), mt.toChars(), sm.kind(), sm.toChars());
+ else
+ error(loc, "identifier `%s` of `%s` is not defined", id.toChars(), mt.toChars());
+ }
+ pe = ErrorExp.get();
+ return;
+ }
+ }
+ s = sm.toAlias();
+ }
+
+ if (auto em = s.isEnumMember())
+ {
+ // It's not a type, it's an expression
+ pe = em.getVarExp(loc, sc);
+ return;
+ }
+ if (auto v = s.isVarDeclaration())
+ {
+ /* This is mostly same with DsymbolExp::semantic(), but we cannot use it
+ * because some variables used in type context need to prevent lowering
+ * to a literal or contextful expression. For example:
+ *
+ * enum a = 1; alias b = a;
+ * template X(alias e){ alias v = e; } alias x = X!(1);
+ * struct S { int v; alias w = v; }
+ * // TypeIdentifier 'a', 'e', and 'v' should be TOK.variable,
+ * // because getDsymbol() need to work in AliasDeclaration::semantic().
+ */
+ if (!v.type ||
+ !v.type.deco && v.inuse)
+ {
+ if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494
+ error(loc, "circular reference to %s `%s`", v.kind(), v.toPrettyChars());
+ else
+ error(loc, "forward reference to %s `%s`", v.kind(), v.toPrettyChars());
+ pt = Type.terror;
+ return;
+ }
+ if (v.type.ty == Terror)
+ pt = Type.terror;
+ else
+ pe = new VarExp(loc, v);
+ return;
+ }
+ if (auto fld = s.isFuncLiteralDeclaration())
+ {
+ //printf("'%s' is a function literal\n", fld.toChars());
+ auto e = new FuncExp(loc, fld);
+ pe = e.expressionSemantic(sc);
+ return;
+ }
+ version (none)
+ {
+ if (FuncDeclaration fd = s.isFuncDeclaration())
+ {
+ pe = new DsymbolExp(loc, fd);
+ return;
+ }
+ }
+
+ Type t;
+ while (1)
+ {
+ t = s.getType();
+ if (t)
+ break;
+ ps = s;
+ return;
+ }
+
+ if (auto ti = t.isTypeInstance())
+ if (ti != mt && !ti.deco)
+ {
+ if (!ti.tempinst.errors)
+ error(loc, "forward reference to `%s`", ti.toChars());
+ pt = Type.terror;
+ return;
+ }
+
+ if (t.ty == Ttuple)
+ pt = t;
+ else
+ pt = t.merge();
+}
+
+/************************************
+ * Transitively search a type for all function types.
+ * If any function types with parameters are found that have parameter identifiers
+ * or default arguments, remove those and create a new type stripped of those.
+ * This is used to determine the "canonical" version of a type which is useful for
+ * comparisons.
+ * Params:
+ * t = type to scan
+ * Returns:
+ * `t` if no parameter identifiers or default arguments found, otherwise a new type that is
+ * the same as t but with no parameter identifiers or default arguments.
+ */
+private Type stripDefaultArgs(Type t)
+{
+ static Parameters* stripParams(Parameters* parameters)
+ {
+ static Parameter stripParameter(Parameter p)
+ {
+ Type t = stripDefaultArgs(p.type);
+ return (t != p.type || p.defaultArg || p.ident || p.userAttribDecl)
+ ? new Parameter(p.storageClass, t, null, null, null)
+ : null;
+ }
+
+ if (parameters)
+ {
+ foreach (i, p; *parameters)
+ {
+ Parameter ps = stripParameter(p);
+ if (ps)
+ {
+ // Replace params with a copy we can modify
+ Parameters* nparams = new Parameters(parameters.dim);
+
+ foreach (j, ref np; *nparams)
+ {
+ Parameter pj = (*parameters)[j];
+ if (j < i)
+ np = pj;
+ else if (j == i)
+ np = ps;
+ else
+ {
+ Parameter nps = stripParameter(pj);
+ np = nps ? nps : pj;
+ }
+ }
+ return nparams;
+ }
+ }
+ }
+ return parameters;
+ }
+
+ if (t is null)
+ return t;
+
+ if (auto tf = t.isTypeFunction())
+ {
+ Type tret = stripDefaultArgs(tf.next);
+ Parameters* params = stripParams(tf.parameterList.parameters);
+ if (tret == tf.next && params == tf.parameterList.parameters)
+ return t;
+ TypeFunction tr = cast(TypeFunction)tf.copy();
+ tr.parameterList.parameters = params;
+ tr.next = tret;
+ //printf("strip %s\n <- %s\n", tr.toChars(), t.toChars());
+ return tr;
+ }
+ else if (auto tt = t.isTypeTuple())
+ {
+ Parameters* args = stripParams(tt.arguments);
+ if (args == tt.arguments)
+ return t;
+ TypeTuple tr = cast(TypeTuple)t.copy();
+ tr.arguments = args;
+ return tr;
+ }
+ else if (t.ty == Tenum)
+ {
+ // TypeEnum::nextOf() may be != NULL, but it's not necessary here.
+ return t;
+ }
+ else
+ {
+ Type tn = t.nextOf();
+ Type n = stripDefaultArgs(tn);
+ if (n == tn)
+ return t;
+ TypeNext tr = cast(TypeNext)t.copy();
+ tr.next = n;
+ return tr;
+ }
+}
+
+/******************************************
+ * We've mistakenly parsed `t` as a type.
+ * Redo `t` as an Expression only if there are no type modifiers.
+ * Params:
+ * t = mistaken type
+ * Returns:
+ * t redone as Expression, null if cannot
+ */
+Expression typeToExpression(Type t)
+{
+ static Expression visitSArray(TypeSArray t)
+ {
+ if (auto e = t.next.typeToExpression())
+ return new ArrayExp(t.dim.loc, e, t.dim);
+ return null;
+ }
+
+ static Expression visitAArray(TypeAArray t)
+ {
+ if (auto e = t.next.typeToExpression())
+ {
+ if (auto ei = t.index.typeToExpression())
+ return new ArrayExp(t.loc, e, ei);
+ }
+ return null;
+ }
+
+ static Expression visitIdentifier(TypeIdentifier t)
+ {
+ return typeToExpressionHelper(t, new IdentifierExp(t.loc, t.ident));
+ }
+
+ static Expression visitInstance(TypeInstance t)
+ {
+ return typeToExpressionHelper(t, new ScopeExp(t.loc, t.tempinst));
+ }
+
+ // easy way to enable 'auto v = new int[mixin("exp")];' in 2.088+
+ static Expression visitMixin(TypeMixin t)
+ {
+ return new TypeExp(t.loc, t);
+ }
+
+ if (t.mod)
+ return null;
+ switch (t.ty)
+ {
+ case Tsarray: return visitSArray(cast(TypeSArray) t);
+ case Taarray: return visitAArray(cast(TypeAArray) t);
+ case Tident: return visitIdentifier(cast(TypeIdentifier) t);
+ case Tinstance: return visitInstance(cast(TypeInstance) t);
+ case Tmixin: return visitMixin(cast(TypeMixin) t);
+ default: return null;
+ }
+}
+
+/* Helper function for `typeToExpression`. Contains common code
+ * for TypeQualified derived classes.
+ */
+Expression typeToExpressionHelper(TypeQualified t, Expression e, size_t i = 0)
+{
+ //printf("toExpressionHelper(e = %s %s)\n", Token.toChars(e.op), e.toChars());
+ foreach (id; t.idents[i .. t.idents.dim])
+ {
+ //printf("\t[%d] e: '%s', id: '%s'\n", i, e.toChars(), id.toChars());
+
+ final switch (id.dyncast())
+ {
+ // ... '. ident'
+ case DYNCAST.identifier:
+ e = new DotIdExp(e.loc, e, cast(Identifier)id);
+ break;
+
+ // ... '. name!(tiargs)'
+ case DYNCAST.dsymbol:
+ auto ti = (cast(Dsymbol)id).isTemplateInstance();
+ assert(ti);
+ e = new DotTemplateInstanceExp(e.loc, e, ti.name, ti.tiargs);
+ break;
+
+ // ... '[type]'
+ case DYNCAST.type: // https://issues.dlang.org/show_bug.cgi?id=1215
+ e = new ArrayExp(t.loc, e, new TypeExp(t.loc, cast(Type)id));
+ break;
+
+ // ... '[expr]'
+ case DYNCAST.expression: // https://issues.dlang.org/show_bug.cgi?id=1215
+ e = new ArrayExp(t.loc, e, cast(Expression)id);
+ break;
+
+ case DYNCAST.object:
+ case DYNCAST.tuple:
+ case DYNCAST.parameter:
+ case DYNCAST.statement:
+ case DYNCAST.condition:
+ case DYNCAST.templateparameter:
+ case DYNCAST.initializer:
+ assert(0);
+ }
+ }
+ return e;
+}
+
+/******************************************
+ * Perform semantic analysis on a type.
+ * Params:
+ * type = Type AST node
+ * loc = the location of the type
+ * sc = context
+ * Returns:
+ * `Type` with completed semantic analysis, `Terror` if errors
+ * were encountered
+ */
+extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
+{
+ static Type error()
+ {
+ return Type.terror;
+ }
+
+ Type visitType(Type t)
+ {
+ if (t.ty == Tint128 || t.ty == Tuns128)
+ {
+ .error(loc, "`cent` and `ucent` types not implemented");
+ return error();
+ }
+
+ return t.merge();
+ }
+
+ Type visitVector(TypeVector mtype)
+ {
+ const errors = global.errors;
+ mtype.basetype = mtype.basetype.typeSemantic(loc, sc);
+ if (errors != global.errors)
+ return error();
+ mtype.basetype = mtype.basetype.toBasetype().mutableOf();
+ if (mtype.basetype.ty != Tsarray)
+ {
+ .error(loc, "T in __vector(T) must be a static array, not `%s`", mtype.basetype.toChars());
+ return error();
+ }
+ TypeSArray t = cast(TypeSArray)mtype.basetype;
+ const sz = cast(int)t.size(loc);
+ final switch (target.isVectorTypeSupported(sz, t.nextOf()))
+ {
+ case 0:
+ // valid
+ break;
+
+ case 1:
+ // no support at all
+ .error(loc, "SIMD vector types not supported on this platform");
+ return error();
+
+ case 2:
+ // invalid base type
+ .error(loc, "vector type `%s` is not supported on this platform", mtype.toChars());
+ return error();
+
+ case 3:
+ // invalid size
+ .error(loc, "%d byte vector type `%s` is not supported on this platform", sz, mtype.toChars());
+ return error();
+ }
+ return merge(mtype);
+ }
+
+ Type visitSArray(TypeSArray mtype)
+ {
+ //printf("TypeSArray::semantic() %s\n", toChars());
+ Type t;
+ Expression e;
+ Dsymbol s;
+ mtype.next.resolve(loc, sc, e, t, s);
+
+ if (auto tup = s ? s.isTupleDeclaration() : null)
+ {
+ mtype.dim = semanticLength(sc, tup, mtype.dim);
+ mtype.dim = mtype.dim.ctfeInterpret();
+ if (mtype.dim.op == TOK.error)
+ return error();
+
+ uinteger_t d = mtype.dim.toUInteger();
+ if (d >= tup.objects.dim)
+ {
+ .error(loc, "tuple index %llu exceeds %llu", cast(ulong)d, cast(ulong)tup.objects.dim);
+ return error();
+ }
+
+ RootObject o = (*tup.objects)[cast(size_t)d];
+ if (o.dyncast() != DYNCAST.type)
+ {
+ .error(loc, "`%s` is not a type", mtype.toChars());
+ return error();
+ }
+ return (cast(Type)o).addMod(mtype.mod);
+ }
+
+ if (t && t.ty == Terror)
+ return error();
+
+ Type tn = mtype.next.typeSemantic(loc, sc);
+ if (tn.ty == Terror)
+ return error();
+
+ Type tbn = tn.toBasetype();
+ if (mtype.dim)
+ {
+ auto errors = global.errors;
+ mtype.dim = semanticLength(sc, tbn, mtype.dim);
+ if (errors != global.errors)
+ return error();
+
+ mtype.dim = mtype.dim.optimize(WANTvalue);
+ mtype.dim = mtype.dim.ctfeInterpret();
+ if (mtype.dim.op == TOK.error)
+ return error();
+
+ errors = global.errors;
+ dinteger_t d1 = mtype.dim.toInteger();
+ if (errors != global.errors)
+ return error();
+
+ mtype.dim = mtype.dim.implicitCastTo(sc, Type.tsize_t);
+ mtype.dim = mtype.dim.optimize(WANTvalue);
+ if (mtype.dim.op == TOK.error)
+ return error();
+
+ errors = global.errors;
+ dinteger_t d2 = mtype.dim.toInteger();
+ if (errors != global.errors)
+ return error();
+
+ if (mtype.dim.op == TOK.error)
+ return error();
+
+ Type overflowError()
+ {
+ .error(loc, "`%s` size %llu * %llu exceeds 0x%llx size limit for static array",
+ mtype.toChars(), cast(ulong)tbn.size(loc), cast(ulong)d1, target.maxStaticDataSize);
+ return error();
+ }
+
+ if (d1 != d2)
+ return overflowError();
+
+ Type tbx = tbn.baseElemOf();
+ if (tbx.ty == Tstruct && !(cast(TypeStruct)tbx).sym.members ||
+ tbx.ty == Tenum && !(cast(TypeEnum)tbx).sym.members)
+ {
+ /* To avoid meaningless error message, skip the total size limit check
+ * when the bottom of element type is opaque.
+ */
+ }
+ else if (tbn.isTypeBasic() ||
+ tbn.ty == Tpointer ||
+ tbn.ty == Tarray ||
+ tbn.ty == Tsarray ||
+ tbn.ty == Taarray ||
+ (tbn.ty == Tstruct && ((cast(TypeStruct)tbn).sym.sizeok == Sizeok.done)) ||
+ tbn.ty == Tclass)
+ {
+ /* Only do this for types that don't need to have semantic()
+ * run on them for the size, since they may be forward referenced.
+ */
+ bool overflow = false;
+ if (mulu(tbn.size(loc), d2, overflow) >= target.maxStaticDataSize || overflow)
+ return overflowError();
+ }
+ }
+ switch (tbn.ty)
+ {
+ case Ttuple:
+ {
+ // Index the tuple to get the type
+ assert(mtype.dim);
+ TypeTuple tt = cast(TypeTuple)tbn;
+ uinteger_t d = mtype.dim.toUInteger();
+ if (d >= tt.arguments.dim)
+ {
+ .error(loc, "tuple index %llu exceeds %llu", cast(ulong)d, cast(ulong)tt.arguments.dim);
+ return error();
+ }
+ Type telem = (*tt.arguments)[cast(size_t)d].type;
+ return telem.addMod(mtype.mod);
+ }
+
+ case Tfunction:
+ case Tnone:
+ .error(loc, "cannot have array of `%s`", tbn.toChars());
+ return error();
+
+ default:
+ break;
+ }
+ if (tbn.isscope())
+ {
+ .error(loc, "cannot have array of scope `%s`", tbn.toChars());
+ return error();
+ }
+
+ /* Ensure things like const(immutable(T)[3]) become immutable(T[3])
+ * and const(T)[3] become const(T[3])
+ */
+ mtype.next = tn;
+ mtype.transitive();
+ return mtype.addMod(tn.mod).merge();
+ }
+
+ Type visitDArray(TypeDArray mtype)
+ {
+ Type tn = mtype.next.typeSemantic(loc, sc);
+ Type tbn = tn.toBasetype();
+ switch (tbn.ty)
+ {
+ case Ttuple:
+ return tbn;
+
+ case Tfunction:
+ case Tnone:
+ .error(loc, "cannot have array of `%s`", tbn.toChars());
+ return error();
+
+ case Terror:
+ return error();
+
+ default:
+ break;
+ }
+ if (tn.isscope())
+ {
+ .error(loc, "cannot have array of scope `%s`", tn.toChars());
+ return error();
+ }
+ mtype.next = tn;
+ mtype.transitive();
+ return merge(mtype);
+ }
+
+ Type visitAArray(TypeAArray mtype)
+ {
+ //printf("TypeAArray::semantic() %s index.ty = %d\n", mtype.toChars(), mtype.index.ty);
+ if (mtype.deco)
+ {
+ return mtype;
+ }
+
+ mtype.loc = loc;
+ if (sc)
+ sc.setNoFree();
+
+ // Deal with the case where we thought the index was a type, but
+ // in reality it was an expression.
+ if (mtype.index.ty == Tident || mtype.index.ty == Tinstance || mtype.index.ty == Tsarray || mtype.index.ty == Ttypeof || mtype.index.ty == Treturn || mtype.index.ty == Tmixin)
+ {
+ Expression e;
+ Type t;
+ Dsymbol s;
+ mtype.index.resolve(loc, sc, e, t, s);
+
+ // https://issues.dlang.org/show_bug.cgi?id=15478
+ if (s)
+ e = symbolToExp(s, loc, sc, false);
+
+ if (e)
+ {
+ // It was an expression -
+ // Rewrite as a static array
+ auto tsa = new TypeSArray(mtype.next, e);
+ return tsa.typeSemantic(loc, sc);
+ }
+ else if (t)
+ mtype.index = t.typeSemantic(loc, sc);
+ else
+ {
+ .error(loc, "index is not a type or an expression");
+ return error();
+ }
+ }
+ else
+ mtype.index = mtype.index.typeSemantic(loc, sc);
+ mtype.index = mtype.index.merge2();
+
+ if (mtype.index.nextOf() && !mtype.index.nextOf().isImmutable())
+ {
+ mtype.index = mtype.index.constOf().mutableOf();
+ version (none)
+ {
+ printf("index is %p %s\n", mtype.index, mtype.index.toChars());
+ mtype.index.check();
+ printf("index.mod = x%x\n", mtype.index.mod);
+ printf("index.ito = x%p\n", mtype.index.getMcache().ito);
+ if (mtype.index.getMcache().ito)
+ {
+ printf("index.ito.mod = x%x\n", mtype.index.getMcache().ito.mod);
+ printf("index.ito.ito = x%p\n", mtype.index.getMcache().ito.getMcache().ito);
+ }
+ }
+ }
+
+ switch (mtype.index.toBasetype().ty)
+ {
+ case Tfunction:
+ case Tvoid:
+ case Tnone:
+ case Ttuple:
+ .error(loc, "cannot have associative array key of `%s`", mtype.index.toBasetype().toChars());
+ goto case Terror;
+ case Terror:
+ return error();
+
+ default:
+ break;
+ }
+ Type tbase = mtype.index.baseElemOf();
+ while (tbase.ty == Tarray)
+ tbase = tbase.nextOf().baseElemOf();
+ if (auto ts = tbase.isTypeStruct())
+ {
+ /* AA's need typeid(index).equals() and getHash(). Issue error if not correctly set up.
+ */
+ StructDeclaration sd = ts.sym;
+ if (sd.semanticRun < PASS.semanticdone)
+ sd.dsymbolSemantic(null);
+
+ // duplicate a part of StructDeclaration::semanticTypeInfoMembers
+ //printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", toChars(), sd.xeq, sd.xerreq, sd.xhash);
+
+ if (sd.xeq && sd.xeq.generated && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done)
+ {
+ uint errors = global.startGagging();
+ sd.xeq.semantic3(sd.xeq._scope);
+ if (global.endGagging(errors))
+ sd.xeq = sd.xerreq;
+ }
+
+
+ //printf("AA = %s, key: xeq = %p, xhash = %p\n", toChars(), sd.xeq, sd.xhash);
+ const(char)* s = (mtype.index.toBasetype().ty != Tstruct) ? "bottom of " : "";
+ if (!sd.xeq)
+ {
+ // If sd.xhash != NULL:
+ // sd or its fields have user-defined toHash.
+ // AA assumes that its result is consistent with bitwise equality.
+ // else:
+ // bitwise equality & hashing
+ }
+ else if (sd.xeq == sd.xerreq)
+ {
+ if (search_function(sd, Id.eq))
+ {
+ .error(loc, "%sAA key type `%s` does not have `bool opEquals(ref const %s) const`", s, sd.toChars(), sd.toChars());
+ }
+ else
+ {
+ .error(loc, "%sAA key type `%s` does not support const equality", s, sd.toChars());
+ }
+ return error();
+ }
+ else if (!sd.xhash)
+ {
+ if (search_function(sd, Id.eq))
+ {
+ .error(loc, "%sAA key type `%s` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined", s, sd.toChars());
+ }
+ else
+ {
+ .error(loc, "%sAA key type `%s` supports const equality but doesn't support const hashing", s, sd.toChars());
+ }
+ return error();
+ }
+ else
+ {
+ // defined equality & hashing
+ assert(sd.xeq && sd.xhash);
+
+ /* xeq and xhash may be implicitly defined by compiler. For example:
+ * struct S { int[] arr; }
+ * With 'arr' field equality and hashing, compiler will implicitly
+ * generate functions for xopEquals and xtoHash in TypeInfo_Struct.
+ */
+ }
+ }
+ else if (tbase.ty == Tclass && !(cast(TypeClass)tbase).sym.isInterfaceDeclaration())
+ {
+ ClassDeclaration cd = (cast(TypeClass)tbase).sym;
+ if (cd.semanticRun < PASS.semanticdone)
+ cd.dsymbolSemantic(null);
+
+ if (!ClassDeclaration.object)
+ {
+ .error(Loc.initial, "missing or corrupt object.d");
+ fatal();
+ }
+
+ __gshared FuncDeclaration feq = null;
+ __gshared FuncDeclaration fcmp = null;
+ __gshared FuncDeclaration fhash = null;
+ if (!feq)
+ feq = search_function(ClassDeclaration.object, Id.eq).isFuncDeclaration();
+ if (!fcmp)
+ fcmp = search_function(ClassDeclaration.object, Id.cmp).isFuncDeclaration();
+ if (!fhash)
+ fhash = search_function(ClassDeclaration.object, Id.tohash).isFuncDeclaration();
+ assert(fcmp && feq && fhash);
+
+ if (feq.vtblIndex < cd.vtbl.dim && cd.vtbl[feq.vtblIndex] == feq)
+ {
+ version (all)
+ {
+ if (fcmp.vtblIndex < cd.vtbl.dim && cd.vtbl[fcmp.vtblIndex] != fcmp)
+ {
+ const(char)* s = (mtype.index.toBasetype().ty != Tclass) ? "bottom of " : "";
+ .error(loc, "%sAA key type `%s` now requires equality rather than comparison", s, cd.toChars());
+ errorSupplemental(loc, "Please override `Object.opEquals` and `Object.toHash`.");
+ }
+ }
+ }
+ }
+ mtype.next = mtype.next.typeSemantic(loc, sc).merge2();
+ mtype.transitive();
+
+ switch (mtype.next.toBasetype().ty)
+ {
+ case Tfunction:
+ case Tvoid:
+ case Tnone:
+ case Ttuple:
+ .error(loc, "cannot have associative array of `%s`", mtype.next.toChars());
+ goto case Terror;
+ case Terror:
+ return error();
+ default:
+ break;
+ }
+ if (mtype.next.isscope())
+ {
+ .error(loc, "cannot have array of scope `%s`", mtype.next.toChars());
+ return error();
+ }
+ return merge(mtype);
+ }
+
+ Type visitPointer(TypePointer mtype)
+ {
+ //printf("TypePointer::semantic() %s\n", toChars());
+ if (mtype.deco)
+ {
+ return mtype;
+ }
+ Type n = mtype.next.typeSemantic(loc, sc);
+ switch (n.toBasetype().ty)
+ {
+ case Ttuple:
+ .error(loc, "cannot have pointer to `%s`", n.toChars());
+ goto case Terror;
+ case Terror:
+ return error();
+ default:
+ break;
+ }
+ if (n != mtype.next)
+ {
+ mtype.deco = null;
+ }
+ mtype.next = n;
+ if (mtype.next.ty != Tfunction)
+ {
+ mtype.transitive();
+ return merge(mtype);
+ }
+ version (none)
+ {
+ return merge(mtype);
+ }
+ else
+ {
+ mtype.deco = merge(mtype).deco;
+ /* Don't return merge(), because arg identifiers and default args
+ * can be different
+ * even though the types match
+ */
+ return mtype;
+ }
+ }
+
+ Type visitReference(TypeReference mtype)
+ {
+ //printf("TypeReference::semantic()\n");
+ Type n = mtype.next.typeSemantic(loc, sc);
+ if (n != mtype.next)
+ mtype.deco = null;
+ mtype.next = n;
+ mtype.transitive();
+ return merge(mtype);
+ }
+
+ Type visitFunction(TypeFunction mtype)
+ {
+ if (mtype.deco) // if semantic() already run
+ {
+ //printf("already done\n");
+ return mtype;
+ }
+ //printf("TypeFunction::semantic() this = %p\n", this);
+ //printf("TypeFunction::semantic() %s, sc.stc = %llx, fargs = %p\n", toChars(), sc.stc, fargs);
+
+ bool errors = false;
+
+ if (mtype.inuse > global.recursionLimit)
+ {
+ mtype.inuse = 0;
+ .error(loc, "recursive type");
+ return error();
+ }
+
+ /* Copy in order to not mess up original.
+ * This can produce redundant copies if inferring return type,
+ * as semantic() will get called again on this.
+ */
+ TypeFunction tf = mtype.copy().toTypeFunction();
+ if (mtype.parameterList.parameters)
+ {
+ tf.parameterList.parameters = mtype.parameterList.parameters.copy();
+ for (size_t i = 0; i < mtype.parameterList.parameters.dim; i++)
+ {
+ Parameter p = cast(Parameter)mem.xmalloc(__traits(classInstanceSize, Parameter));
+ memcpy(cast(void*)p, cast(void*)(*mtype.parameterList.parameters)[i], __traits(classInstanceSize, Parameter));
+ (*tf.parameterList.parameters)[i] = p;
+ }
+ }
+
+ if (sc.stc & STC.pure_)
+ tf.purity = PURE.fwdref;
+ if (sc.stc & STC.nothrow_)
+ tf.isnothrow = true;
+ if (sc.stc & STC.nogc)
+ tf.isnogc = true;
+ if (sc.stc & STC.ref_)
+ tf.isref = true;
+ if (sc.stc & STC.return_)
+ tf.isreturn = true;
+ if (sc.stc & STC.returninferred)
+ tf.isreturninferred = true;
+ if (sc.stc & STC.scope_)
+ tf.isScopeQual = true;
+ if (sc.stc & STC.scopeinferred)
+ tf.isscopeinferred = true;
+
+// if (tf.isreturn && !tf.isref)
+// tf.isScopeQual = true; // return by itself means 'return scope'
+
+ if (tf.trust == TRUST.default_)
+ {
+ if (sc.stc & STC.safe)
+ tf.trust = TRUST.safe;
+ else if (sc.stc & STC.system)
+ tf.trust = TRUST.system;
+ else if (sc.stc & STC.trusted)
+ tf.trust = TRUST.trusted;
+ }
+
+ if (sc.stc & STC.property)
+ tf.isproperty = true;
+ if (sc.stc & STC.live)
+ tf.islive = true;
+
+ tf.linkage = sc.linkage;
+ version (none)
+ {
+ /* If the parent is @safe, then this function defaults to safe
+ * too.
+ * If the parent's @safe-ty is inferred, then this function's @safe-ty needs
+ * to be inferred first.
+ */
+ if (tf.trust == TRUST.default_)
+ for (Dsymbol p = sc.func; p; p = p.toParent2())
+ {
+ FuncDeclaration fd = p.isFuncDeclaration();
+ if (fd)
+ {
+ if (fd.isSafeBypassingInference())
+ tf.trust = TRUST.safe; // default to @safe
+ break;
+ }
+ }
+ }
+
+ bool wildreturn = false;
+ if (tf.next)
+ {
+ sc = sc.push();
+ sc.stc &= ~(STC.TYPECTOR | STC.FUNCATTR);
+ tf.next = tf.next.typeSemantic(loc, sc);
+ sc = sc.pop();
+ errors |= tf.checkRetType(loc);
+ if (tf.next.isscope() && !tf.isctor)
+ {
+ .error(loc, "functions cannot return `scope %s`", tf.next.toChars());
+ errors = true;
+ }
+ if (tf.next.hasWild())
+ wildreturn = true;
+
+ if (tf.isreturn && !tf.isref && !tf.next.hasPointers())
+ {
+ tf.isreturn = false;
+ }
+ }
+
+ /// Perform semantic on the default argument to a parameter
+ /// Modify the `defaultArg` field of `fparam`, which must not be `null`
+ /// Returns `false` whether an error was encountered.
+ static bool defaultArgSemantic (ref Parameter fparam, Scope* sc)
+ {
+ Expression e = fparam.defaultArg;
+ const isRefOrOut = fparam.isReference();
+ const isAuto = fparam.storageClass & (STC.auto_ | STC.autoref);
+ if (isRefOrOut && !isAuto)
+ {
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ }
+ else
+ {
+ e = inferType(e, fparam.type);
+ Initializer iz = new ExpInitializer(e.loc, e);
+ iz = iz.initializerSemantic(sc, fparam.type, INITnointerpret);
+ e = iz.initializerToExpression();
+ }
+ if (e.op == TOK.function_) // https://issues.dlang.org/show_bug.cgi?id=4820
+ {
+ FuncExp fe = cast(FuncExp)e;
+ // Replace function literal with a function symbol,
+ // since default arg expression must be copied when used
+ // and copying the literal itself is wrong.
+ e = new VarExp(e.loc, fe.fd, false);
+ e = new AddrExp(e.loc, e);
+ e = e.expressionSemantic(sc);
+ }
+ if (isRefOrOut && (!isAuto || e.isLvalue())
+ && !MODimplicitConv(e.type.mod, fparam.type.mod))
+ {
+ const(char)* errTxt = fparam.storageClass & STC.ref_ ? "ref" : "out";
+ .error(e.loc, "expression `%s` of type `%s` is not implicitly convertible to type `%s %s` of parameter `%s`",
+ e.toChars(), e.type.toChars(), errTxt, fparam.type.toChars(), fparam.toChars());
+ }
+ e = e.implicitCastTo(sc, fparam.type);
+
+ // default arg must be an lvalue
+ if (isRefOrOut && !isAuto &&
+ !(global.params.previewIn && (fparam.storageClass & STC.in_)) &&
+ !(global.params.rvalueRefParam))
+ e = e.toLvalue(sc, e);
+
+ fparam.defaultArg = e;
+ return (e.op != TOK.error);
+ }
+
+ ubyte wildparams = 0;
+ if (tf.parameterList.parameters)
+ {
+ /* Create a scope for evaluating the default arguments for the parameters
+ */
+ Scope* argsc = sc.push();
+ argsc.stc = 0; // don't inherit storage class
+ argsc.visibility = Visibility(Visibility.Kind.public_);
+ argsc.func = null;
+
+ size_t dim = tf.parameterList.length;
+ for (size_t i = 0; i < dim; i++)
+ {
+ Parameter fparam = tf.parameterList[i];
+ fparam.storageClass |= STC.parameter;
+ mtype.inuse++;
+ fparam.type = fparam.type.typeSemantic(loc, argsc);
+ mtype.inuse--;
+
+ if (fparam.type.ty == Terror)
+ {
+ errors = true;
+ continue;
+ }
+
+ fparam.type = fparam.type.addStorageClass(fparam.storageClass);
+
+ if (fparam.storageClass & (STC.auto_ | STC.alias_ | STC.static_))
+ {
+ if (!fparam.type)
+ continue;
+ }
+
+ Type t = fparam.type.toBasetype();
+
+ /* If fparam after semantic() turns out to be a tuple, the number of parameters may
+ * change.
+ */
+ if (auto tt = t.isTypeTuple())
+ {
+ /* TypeFunction::parameter also is used as the storage of
+ * Parameter objects for FuncDeclaration. So we should copy
+ * the elements of TypeTuple::arguments to avoid unintended
+ * sharing of Parameter object among other functions.
+ */
+ if (tt.arguments && tt.arguments.dim)
+ {
+ /* Propagate additional storage class from tuple parameters to their
+ * element-parameters.
+ * Make a copy, as original may be referenced elsewhere.
+ */
+ size_t tdim = tt.arguments.dim;
+ auto newparams = new Parameters(tdim);
+ for (size_t j = 0; j < tdim; j++)
+ {
+ Parameter narg = (*tt.arguments)[j];
+
+ // https://issues.dlang.org/show_bug.cgi?id=12744
+ // If the storage classes of narg
+ // conflict with the ones in fparam, it's ignored.
+ StorageClass stc = fparam.storageClass | narg.storageClass;
+ StorageClass stc1 = fparam.storageClass & (STC.ref_ | STC.out_ | STC.lazy_);
+ StorageClass stc2 = narg.storageClass & (STC.ref_ | STC.out_ | STC.lazy_);
+ if (stc1 && stc2 && stc1 != stc2)
+ {
+ OutBuffer buf1; stcToBuffer(&buf1, stc1 | ((stc1 & STC.ref_) ? (fparam.storageClass & STC.auto_) : 0));
+ OutBuffer buf2; stcToBuffer(&buf2, stc2);
+
+ .error(loc, "incompatible parameter storage classes `%s` and `%s`",
+ buf1.peekChars(), buf2.peekChars());
+ errors = true;
+ stc = stc1 | (stc & ~(STC.ref_ | STC.out_ | STC.lazy_));
+ }
+ (*newparams)[j] = new Parameter(
+ stc, narg.type, narg.ident, narg.defaultArg, narg.userAttribDecl);
+ }
+ fparam.type = new TypeTuple(newparams);
+ }
+ fparam.storageClass = STC.parameter;
+
+ /* Reset number of parameters, and back up one to do this fparam again,
+ * now that it is a tuple
+ */
+ dim = tf.parameterList.length;
+ i--;
+ continue;
+ }
+
+ if (t.ty == Tfunction)
+ {
+ .error(loc, "cannot have parameter of function type `%s`", fparam.type.toChars());
+ errors = true;
+ }
+ else if (!fparam.isReference() &&
+ (t.ty == Tstruct || t.ty == Tsarray || t.ty == Tenum))
+ {
+ Type tb2 = t.baseElemOf();
+ if (tb2.ty == Tstruct && !(cast(TypeStruct)tb2).sym.members ||
+ tb2.ty == Tenum && !(cast(TypeEnum)tb2).sym.memtype)
+ {
+ if (global.params.previewIn && (fparam.storageClass & STC.in_))
+ {
+ .error(loc, "cannot infer `ref` for `in` parameter `%s` of opaque type `%s`",
+ fparam.toChars(), fparam.type.toChars());
+ }
+ else
+ .error(loc, "cannot have parameter of opaque type `%s` by value",
+ fparam.type.toChars());
+ errors = true;
+ }
+ }
+ else if (!(fparam.storageClass & STC.lazy_) && t.ty == Tvoid)
+ {
+ .error(loc, "cannot have parameter of type `%s`", fparam.type.toChars());
+ errors = true;
+ }
+
+ if ((fparam.storageClass & (STC.ref_ | STC.wild)) == (STC.ref_ | STC.wild))
+ {
+ // 'ref inout' implies 'return'
+ fparam.storageClass |= STC.return_;
+ }
+
+ if (fparam.storageClass & STC.return_)
+ {
+ if (fparam.isReference())
+ {
+ // Disabled for the moment awaiting improvement to allow return by ref
+ // to be transformed into return by scope.
+ if (0 && !tf.isref)
+ {
+ auto stc = fparam.storageClass & (STC.ref_ | STC.out_);
+ .error(loc, "parameter `%s` is `return %s` but function does not return by `ref`",
+ fparam.ident ? fparam.ident.toChars() : "",
+ stcToString(stc).ptr);
+ errors = true;
+ }
+ }
+ else
+ {
+ if (!(fparam.storageClass & STC.scope_))
+ fparam.storageClass |= STC.scope_ | STC.scopeinferred; // 'return' implies 'scope'
+ if (tf.isref)
+ {
+ }
+ else if (tf.next && !tf.next.hasPointers() && tf.next.toBasetype().ty != Tvoid)
+ {
+ fparam.storageClass &= ~STC.return_; // https://issues.dlang.org/show_bug.cgi?id=18963
+ }
+ }
+ }
+
+ if (fparam.storageClass & STC.out_)
+ {
+ if (ubyte m = fparam.type.mod & (MODFlags.immutable_ | MODFlags.const_ | MODFlags.wild))
+ {
+ .error(loc, "cannot have `%s out` parameter of type `%s`", MODtoChars(m), t.toChars());
+ errors = true;
+ }
+ else
+ {
+ Type tv = t.baseElemOf();
+ if (tv.ty == Tstruct && (cast(TypeStruct)tv).sym.noDefaultCtor)
+ {
+ .error(loc, "cannot have `out` parameter of type `%s` because the default construction is disabled", fparam.type.toChars());
+ errors = true;
+ }
+ }
+ }
+
+ if (t.hasWild())
+ {
+ wildparams |= 1;
+ //if (tf.next && !wildreturn)
+ // error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with `ref`)");
+ }
+
+ /* Scope attribute is not necessary if the parameter type does not have pointers
+ */
+ /* Constructors are treated as if they are being returned through the hidden parameter,
+ * which is by ref, and the ref there is ignored.
+ */
+ const returnByRef = tf.isref && !tf.isctor;
+ if (!returnByRef && isRefReturnScope(fparam.storageClass))
+ {
+ /* if `ref return scope`, evaluate to `ref` `return scope`
+ */
+ fparam.storageClass |= STC.returnScope;
+ }
+ const sr = buildScopeRef(fparam.storageClass);
+ switch (sr)
+ {
+ case ScopeRef.Scope:
+ case ScopeRef.RefScope:
+ case ScopeRef.ReturnRef_Scope:
+ if (!fparam.type.hasPointers())
+ fparam.storageClass &= ~STC.scope_;
+ break;
+
+ case ScopeRef.ReturnScope:
+ case ScopeRef.Ref_ReturnScope:
+ if (!fparam.type.hasPointers())
+ fparam.storageClass &= ~(STC.return_ | STC.scope_ | STC.returnScope);
+ break;
+
+ default:
+ break;
+ }
+
+ /* now set STC.returnScope based only on tf.isref. This is inconsistent, as mentioned above,
+ * but necessary for compatibility for now.
+ */
+ fparam.storageClass &= ~STC.returnScope;
+ if (!tf.isref && isRefReturnScope(fparam.storageClass))
+ {
+ /* if `ref return scope`, evaluate to `ref` `return scope`
+ */
+ fparam.storageClass |= STC.returnScope;
+ }
+
+ // Remove redundant storage classes for type, they are already applied
+ fparam.storageClass &= ~(STC.TYPECTOR);
+
+ // -preview=in: add `ref` storage class to suited `in` params
+ if (global.params.previewIn && (fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_)
+ {
+ auto ts = t.baseElemOf().isTypeStruct();
+ const isPOD = !ts || ts.sym.isPOD();
+ if (!isPOD || target.preferPassByRef(t))
+ fparam.storageClass |= STC.ref_;
+ }
+ }
+
+ // Now that we completed semantic for the argument types,
+ // run semantic on their default values,
+ // bearing in mind tuples have been expanded.
+ // We need to keep a pair of [oidx, eidx] (original index,
+ // extended index), as we need to run semantic when `oidx` changes.
+ size_t tupleOrigIdx = size_t.max;
+ size_t tupleExtIdx = size_t.max;
+ foreach (oidx, oparam, eidx, eparam; tf.parameterList)
+ {
+ // oparam (original param) will always have the default arg
+ // if there's one, but `eparam` will not if it's an expanded
+ // tuple. When we see an expanded tuple, we need to save its
+ // position to get the offset in it later on.
+ if (oparam.defaultArg)
+ {
+ // Get the obvious case out of the way
+ if (oparam is eparam)
+ errors |= !defaultArgSemantic(eparam, argsc);
+ // We're seeing a new tuple
+ else if (tupleOrigIdx == size_t.max || tupleOrigIdx < oidx)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=18572
+ *
+ * If a tuple parameter has a default argument, when expanding the parameter
+ * tuple the default argument tuple must also be expanded.
+ */
+ tupleOrigIdx = oidx;
+ tupleExtIdx = eidx;
+ errors |= !defaultArgSemantic(oparam, argsc);
+ TupleExp te = oparam.defaultArg.isTupleExp();
+ if (te && te.exps && te.exps.length)
+ eparam.defaultArg = (*te.exps)[0];
+ }
+ // Processing an already-seen tuple
+ else
+ {
+ TupleExp te = oparam.defaultArg.isTupleExp();
+ if (te && te.exps && te.exps.length)
+ eparam.defaultArg = (*te.exps)[eidx - tupleExtIdx];
+ }
+ }
+
+ // We need to know the default argument to resolve `auto ref`,
+ // hence why this has to take place as the very last step.
+ /* Resolve "auto ref" storage class to be either ref or value,
+ * based on the argument matching the parameter
+ */
+ if (eparam.storageClass & STC.auto_)
+ {
+ Expression farg = mtype.fargs && eidx < mtype.fargs.dim ?
+ (*mtype.fargs)[eidx] : eparam.defaultArg;
+ if (farg && (eparam.storageClass & STC.ref_))
+ {
+ if (!farg.isLvalue())
+ eparam.storageClass &= ~STC.ref_; // value parameter
+ eparam.storageClass &= ~STC.auto_; // https://issues.dlang.org/show_bug.cgi?id=14656
+ eparam.storageClass |= STC.autoref;
+ }
+ else if (mtype.incomplete && (eparam.storageClass & STC.ref_))
+ {
+ // the default argument may have been temporarily removed,
+ // see usage of `TypeFunction.incomplete`.
+ // https://issues.dlang.org/show_bug.cgi?id=19891
+ eparam.storageClass &= ~STC.auto_;
+ eparam.storageClass |= STC.autoref;
+ }
+ else
+ {
+ .error(loc, "`auto` can only be used as part of `auto ref` for template function parameters");
+ errors = true;
+ }
+ }
+ }
+
+ argsc.pop();
+ }
+ if (tf.isWild())
+ wildparams |= 2;
+
+ if (wildreturn && !wildparams)
+ {
+ .error(loc, "`inout` on `return` means `inout` must be on a parameter as well for `%s`", mtype.toChars());
+ errors = true;
+ }
+ tf.isInOutParam = (wildparams & 1) != 0;
+ tf.isInOutQual = (wildparams & 2) != 0;
+
+ if (tf.isproperty && (tf.parameterList.varargs != VarArg.none || tf.parameterList.length > 2))
+ {
+ .error(loc, "properties can only have zero, one, or two parameter");
+ errors = true;
+ }
+
+ if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0)
+ {
+ .error(loc, "variadic functions with non-D linkage must have at least one parameter");
+ errors = true;
+ }
+
+ if (errors)
+ return error();
+
+ if (tf.next)
+ tf.deco = tf.merge().deco;
+
+ /* Don't return merge(), because arg identifiers and default args
+ * can be different
+ * even though the types match
+ */
+ return tf;
+ }
+
+ Type visitDelegate(TypeDelegate mtype)
+ {
+ //printf("TypeDelegate::semantic() %s\n", mtype.toChars());
+ if (mtype.deco) // if semantic() already run
+ {
+ //printf("already done\n");
+ return mtype;
+ }
+ mtype.next = mtype.next.typeSemantic(loc, sc);
+ if (mtype.next.ty != Tfunction)
+ return error();
+
+ /* In order to deal with https://issues.dlang.org/show_bug.cgi?id=4028
+ * perhaps default arguments should
+ * be removed from next before the merge.
+ */
+ version (none)
+ {
+ return mtype.merge();
+ }
+ else
+ {
+ /* Don't return merge(), because arg identifiers and default args
+ * can be different
+ * even though the types match
+ */
+ mtype.deco = mtype.merge().deco;
+ return mtype;
+ }
+ }
+
+ Type visitIdentifier(TypeIdentifier mtype)
+ {
+ Type t;
+ Expression e;
+ Dsymbol s;
+ //printf("TypeIdentifier::semantic(%s)\n", mtype.toChars());
+ mtype.resolve(loc, sc, e, t, s);
+ if (t)
+ {
+ //printf("\tit's a type %d, %s, %s\n", t.ty, t.toChars(), t.deco);
+ return t.addMod(mtype.mod);
+ }
+ else
+ {
+ if (s)
+ {
+ auto td = s.isTemplateDeclaration;
+ if (td && td.onemember && td.onemember.isAggregateDeclaration)
+ .error(loc, "template %s `%s` is used as a type without instantiation"
+ ~ "; to instantiate it use `%s!(arguments)`",
+ s.kind, s.toPrettyChars, s.ident.toChars);
+ else
+ .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars);
+ //assert(0);
+ }
+ else if (e.op == TOK.variable) // special case: variable is used as a type
+ {
+ Dsymbol varDecl = mtype.toDsymbol(sc);
+ const(Loc) varDeclLoc = varDecl.getLoc();
+ Module varDeclModule = varDecl.getModule();
+
+ .error(loc, "variable `%s` is used as a type", mtype.toChars());
+
+ if (varDeclModule != sc._module) // variable is imported
+ {
+ const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc();
+ .errorSupplemental(
+ varDeclModuleImportLoc,
+ "variable `%s` is imported here from: `%s`",
+ varDecl.toChars,
+ varDeclModule.toPrettyChars,
+ );
+ }
+
+ .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars);
+ }
+ else
+ .error(loc, "`%s` is used as a type", mtype.toChars());
+ return error();
+ }
+ }
+
+ Type visitInstance(TypeInstance mtype)
+ {
+ Type t;
+ Expression e;
+ Dsymbol s;
+
+ //printf("TypeInstance::semantic(%p, %s)\n", this, toChars());
+ {
+ const errors = global.errors;
+ mtype.resolve(loc, sc, e, t, s);
+ // if we had an error evaluating the symbol, suppress further errors
+ if (!t && errors != global.errors)
+ return error();
+ }
+
+ if (!t)
+ {
+ if (!e && s && s.errors)
+ {
+ // if there was an error evaluating the symbol, it might actually
+ // be a type. Avoid misleading error messages.
+ .error(loc, "`%s` had previous errors", mtype.toChars());
+ }
+ else
+ .error(loc, "`%s` is used as a type", mtype.toChars());
+ return error();
+ }
+ return t;
+ }
+
+ Type visitTypeof(TypeTypeof mtype)
+ {
+ //printf("TypeTypeof::semantic() %s\n", mtype.toChars());
+ Expression e;
+ Type t;
+ Dsymbol s;
+ mtype.resolve(loc, sc, e, t, s);
+ if (s && (t = s.getType()) !is null)
+ t = t.addMod(mtype.mod);
+ if (!t)
+ {
+ .error(loc, "`%s` is used as a type", mtype.toChars());
+ return error();
+ }
+ return t;
+ }
+
+ Type visitTraits(TypeTraits mtype)
+ {
+ if (mtype.ty == Terror)
+ return mtype;
+
+ const inAlias = (sc.flags & SCOPE.alias_) != 0;
+ if (mtype.exp.ident != Id.allMembers &&
+ mtype.exp.ident != Id.derivedMembers &&
+ mtype.exp.ident != Id.getMember &&
+ mtype.exp.ident != Id.parent &&
+ mtype.exp.ident != Id.child &&
+ mtype.exp.ident != Id.toType &&
+ mtype.exp.ident != Id.getOverloads &&
+ mtype.exp.ident != Id.getVirtualFunctions &&
+ mtype.exp.ident != Id.getVirtualMethods &&
+ mtype.exp.ident != Id.getAttributes &&
+ mtype.exp.ident != Id.getUnitTests &&
+ mtype.exp.ident != Id.getAliasThis)
+ {
+ static immutable (const(char)*)[2] ctxt = ["as type", "in alias"];
+ .error(mtype.loc, "trait `%s` is either invalid or not supported %s",
+ mtype.exp.ident.toChars, ctxt[inAlias]);
+ mtype.ty = Terror;
+ return mtype;
+ }
+
+ import dmd.traits : semanticTraits;
+ Type result;
+
+ if (Expression e = semanticTraits(mtype.exp, sc))
+ {
+ switch (e.op)
+ {
+ case TOK.dotVariable:
+ mtype.sym = (cast(DotVarExp)e).var;
+ break;
+ case TOK.variable:
+ mtype.sym = (cast(VarExp)e).var;
+ break;
+ case TOK.function_:
+ auto fe = cast(FuncExp)e;
+ mtype.sym = fe.td ? fe.td : fe.fd;
+ break;
+ case TOK.dotTemplateDeclaration:
+ mtype.sym = (cast(DotTemplateExp)e).td;
+ break;
+ case TOK.dSymbol:
+ mtype.sym = (cast(DsymbolExp)e).s;
+ break;
+ case TOK.template_:
+ mtype.sym = (cast(TemplateExp)e).td;
+ break;
+ case TOK.scope_:
+ mtype.sym = (cast(ScopeExp)e).sds;
+ break;
+ case TOK.tuple:
+ TupleExp te = e.toTupleExp();
+ Objects* elems = new Objects(te.exps.dim);
+ foreach (i; 0 .. elems.dim)
+ {
+ auto src = (*te.exps)[i];
+ switch (src.op)
+ {
+ case TOK.type:
+ (*elems)[i] = (cast(TypeExp)src).type;
+ break;
+ case TOK.dotType:
+ (*elems)[i] = (cast(DotTypeExp)src).sym.isType();
+ break;
+ case TOK.overloadSet:
+ (*elems)[i] = (cast(OverExp)src).type;
+ break;
+ default:
+ if (auto sym = isDsymbol(src))
+ (*elems)[i] = sym;
+ else
+ (*elems)[i] = src;
+ }
+ }
+ TupleDeclaration td = new TupleDeclaration(e.loc, Identifier.generateId("__aliastup"), elems);
+ mtype.sym = td;
+ break;
+ case TOK.dotType:
+ result = (cast(DotTypeExp)e).sym.isType();
+ break;
+ case TOK.type:
+ result = (cast(TypeExp)e).type;
+ break;
+ case TOK.overloadSet:
+ result = (cast(OverExp)e).type;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (result)
+ result = result.addMod(mtype.mod);
+ if (!inAlias && !result)
+ {
+ if (!global.errors)
+ .error(mtype.loc, "`%s` does not give a valid type", mtype.toChars);
+ return error();
+ }
+
+ return result;
+ }
+
+ Type visitReturn(TypeReturn mtype)
+ {
+ //printf("TypeReturn::semantic() %s\n", toChars());
+ Expression e;
+ Type t;
+ Dsymbol s;
+ mtype.resolve(loc, sc, e, t, s);
+ if (s && (t = s.getType()) !is null)
+ t = t.addMod(mtype.mod);
+ if (!t)
+ {
+ .error(loc, "`%s` is used as a type", mtype.toChars());
+ return error();
+ }
+ return t;
+ }
+
+ Type visitStruct(TypeStruct mtype)
+ {
+ //printf("TypeStruct::semantic('%s')\n", mtype.toChars());
+ if (mtype.deco)
+ return mtype;
+
+ /* Don't semantic for sym because it should be deferred until
+ * sizeof needed or its members accessed.
+ */
+ // instead, parent should be set correctly
+ assert(mtype.sym.parent);
+
+ if (mtype.sym.type.ty == Terror)
+ return error();
+
+ return merge(mtype);
+ }
+
+ Type visitEnum(TypeEnum mtype)
+ {
+ //printf("TypeEnum::semantic() %s\n", toChars());
+ return mtype.deco ? mtype : merge(mtype);
+ }
+
+ Type visitClass(TypeClass mtype)
+ {
+ //printf("TypeClass::semantic(%s)\n", mtype.toChars());
+ if (mtype.deco)
+ return mtype;
+
+ /* Don't semantic for sym because it should be deferred until
+ * sizeof needed or its members accessed.
+ */
+ // instead, parent should be set correctly
+ assert(mtype.sym.parent);
+
+ if (mtype.sym.type.ty == Terror)
+ return error();
+
+ return merge(mtype);
+ }
+
+ Type visitTuple(TypeTuple mtype)
+ {
+ //printf("TypeTuple::semantic(this = %p)\n", this);
+ //printf("TypeTuple::semantic() %p, %s\n", this, toChars());
+ if (!mtype.deco)
+ mtype.deco = merge(mtype).deco;
+
+ /* Don't return merge(), because a tuple with one type has the
+ * same deco as that type.
+ */
+ return mtype;
+ }
+
+ Type visitSlice(TypeSlice mtype)
+ {
+ //printf("TypeSlice::semantic() %s\n", toChars());
+ Type tn = mtype.next.typeSemantic(loc, sc);
+ //printf("next: %s\n", tn.toChars());
+
+ Type tbn = tn.toBasetype();
+ if (tbn.ty != Ttuple)
+ {
+ .error(loc, "can only slice tuple types, not `%s`", tbn.toChars());
+ return error();
+ }
+ TypeTuple tt = cast(TypeTuple)tbn;
+
+ mtype.lwr = semanticLength(sc, tbn, mtype.lwr);
+ mtype.upr = semanticLength(sc, tbn, mtype.upr);
+ mtype.lwr = mtype.lwr.ctfeInterpret();
+ mtype.upr = mtype.upr.ctfeInterpret();
+ if (mtype.lwr.op == TOK.error || mtype.upr.op == TOK.error)
+ return error();
+
+ uinteger_t i1 = mtype.lwr.toUInteger();
+ uinteger_t i2 = mtype.upr.toUInteger();
+ if (!(i1 <= i2 && i2 <= tt.arguments.dim))
+ {
+ .error(loc, "slice `[%llu..%llu]` is out of range of `[0..%llu]`",
+ cast(ulong)i1, cast(ulong)i2, cast(ulong)tt.arguments.dim);
+ return error();
+ }
+
+ mtype.next = tn;
+ mtype.transitive();
+
+ auto args = new Parameters();
+ args.reserve(cast(size_t)(i2 - i1));
+ foreach (arg; (*tt.arguments)[cast(size_t)i1 .. cast(size_t)i2])
+ {
+ args.push(arg);
+ }
+ Type t = new TypeTuple(args);
+ return t.typeSemantic(loc, sc);
+ }
+
+ Type visitMixin(TypeMixin mtype)
+ {
+ //printf("TypeMixin::semantic() %s\n", toChars());
+
+ Expression e;
+ Type t;
+ Dsymbol s;
+ mtype.resolve(loc, sc, e, t, s);
+
+ if (t && t.ty != Terror)
+ return t;
+
+ .error(mtype.loc, "`mixin(%s)` does not give a valid type", mtype.obj.toChars);
+ return error();
+ }
+
+ Type visitTag(TypeTag mtype)
+ {
+ //printf("TypeTag.semantic() %s\n", mtype.toChars());
+ if (mtype.resolved)
+ {
+ /* struct S s, *p;
+ */
+ //printf("already resolved\n");
+ return mtype.resolved;
+ }
+
+ /* Declare mtype as a struct/union/enum declaration
+ */
+ void declareTag()
+ {
+ void declare(ScopeDsymbol sd)
+ {
+ sd.members = mtype.members;
+ auto scopesym = sc.inner().scopesym;
+ if (scopesym.members)
+ scopesym.members.push(sd);
+ if (scopesym.symtab && !scopesym.symtabInsert(sd))
+ {
+ Dsymbol s2 = scopesym.symtabLookup(sd, mtype.id);
+ handleTagSymbols(*sc, sd, s2, scopesym);
+ }
+ sd.parent = sc.parent;
+ sd.dsymbolSemantic(sc);
+ }
+
+ switch (mtype.tok)
+ {
+ case TOK.enum_:
+ auto ed = new EnumDeclaration(mtype.loc, mtype.id, Type.tint32);
+ declare(ed);
+ mtype.resolved = visitEnum(new TypeEnum(ed));
+ break;
+
+ case TOK.struct_:
+ auto sd = new StructDeclaration(mtype.loc, mtype.id, false);
+ declare(sd);
+ mtype.resolved = visitStruct(new TypeStruct(sd));
+ break;
+
+ case TOK.union_:
+ auto ud = new UnionDeclaration(mtype.loc, mtype.id);
+ declare(ud);
+ mtype.resolved = visitStruct(new TypeStruct(ud));
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+
+ /* If it doesn't have a tag by now, supply one.
+ * It'll be unique, and therefore introducing.
+ * Declare it, and done.
+ */
+ if (!mtype.id)
+ {
+ mtype.id = Identifier.generateId("__tag"[]);
+ declareTag();
+ return mtype.resolved;
+ }
+
+ /* look for pre-existing declaration
+ */
+ Dsymbol scopesym;
+ auto s = sc.search(mtype.loc, mtype.id, &scopesym, IgnoreErrors | TagNameSpace);
+ if (!s || s.isModule())
+ {
+ // no pre-existing declaration, so declare it
+ if (mtype.tok == TOK.enum_ && !mtype.members)
+ .error(mtype.loc, "`enum %s` is incomplete without members", mtype.id.toChars()); // C11 6.7.2.3-3
+ declareTag();
+ return mtype.resolved;
+ }
+
+ /* A redeclaration only happens if both declarations are in
+ * the same scope
+ */
+ const bool redeclar = (scopesym == sc.inner().scopesym);
+
+ if (redeclar)
+ {
+ if (mtype.tok == TOK.enum_ && s.isEnumDeclaration())
+ {
+ auto ed = s.isEnumDeclaration();
+ if (mtype.members && ed.members)
+ .error(mtype.loc, "`%s` already has members", mtype.id.toChars());
+ else if (!ed.members)
+ {
+ ed.members = mtype.members;
+ }
+ else
+ {
+ }
+ mtype.resolved = ed.type;
+ }
+ else if (mtype.tok == TOK.union_ && s.isUnionDeclaration() ||
+ mtype.tok == TOK.struct_ && s.isStructDeclaration())
+ {
+ // Add members to original declaration
+ auto sd = s.isStructDeclaration();
+ if (mtype.members && sd.members)
+ {
+ /* struct S { int b; };
+ * struct S { int a; } *s;
+ */
+ .error(mtype.loc, "`%s` already has members", mtype.id.toChars());
+ }
+ else if (!sd.members)
+ {
+ /* struct S;
+ * struct S { int a; } *s;
+ */
+ sd.members = mtype.members;
+ }
+ else
+ {
+ /* struct S { int a; };
+ * struct S *s;
+ */
+ }
+ mtype.resolved = sd.type;
+ }
+ else
+ {
+ /* int S;
+ * struct S { int a; } *s;
+ */
+ .error(mtype.loc, "redeclaration of `%s`", mtype.id.toChars());
+ mtype.resolved = error();
+ }
+ }
+ else if (mtype.members)
+ {
+ /* struct S;
+ * { struct S { int a; } *s; }
+ */
+ declareTag();
+ }
+ else
+ {
+ if (mtype.tok == TOK.enum_ && s.isEnumDeclaration())
+ {
+ mtype.resolved = s.isEnumDeclaration().type;
+ }
+ else if (mtype.tok == TOK.union_ && s.isUnionDeclaration() ||
+ mtype.tok == TOK.struct_ && s.isStructDeclaration())
+ {
+ /* struct S;
+ * { struct S *s; }
+ */
+ mtype.resolved = s.isStructDeclaration().type;
+ }
+ else
+ {
+ /* union S;
+ * { struct S *s; }
+ */
+ .error(mtype.loc, "redeclaring `%s %s` as `%s %s`",
+ s.kind(), s.toChars(), Token.toChars(mtype.tok), mtype.id.toChars());
+ declareTag();
+ }
+ }
+ return mtype.resolved;
+ }
+
+ switch (type.ty)
+ {
+ default: return visitType(type);
+ case Tvector: return visitVector(cast(TypeVector)type);
+ case Tsarray: return visitSArray(cast(TypeSArray)type);
+ case Tarray: return visitDArray(cast(TypeDArray)type);
+ case Taarray: return visitAArray(cast(TypeAArray)type);
+ case Tpointer: return visitPointer(cast(TypePointer)type);
+ case Treference: return visitReference(cast(TypeReference)type);
+ case Tfunction: return visitFunction(cast(TypeFunction)type);
+ case Tdelegate: return visitDelegate(cast(TypeDelegate)type);
+ case Tident: return visitIdentifier(cast(TypeIdentifier)type);
+ case Tinstance: return visitInstance(cast(TypeInstance)type);
+ case Ttypeof: return visitTypeof(cast(TypeTypeof)type);
+ case Ttraits: return visitTraits(cast(TypeTraits)type);
+ case Treturn: return visitReturn(cast(TypeReturn)type);
+ case Tstruct: return visitStruct(cast(TypeStruct)type);
+ case Tenum: return visitEnum(cast(TypeEnum)type);
+ case Tclass: return visitClass(cast(TypeClass)type);
+ case Ttuple: return visitTuple (cast(TypeTuple)type);
+ case Tslice: return visitSlice(cast(TypeSlice)type);
+ case Tmixin: return visitMixin(cast(TypeMixin)type);
+ case Ttag: return visitTag(cast(TypeTag)type);
+ }
+}
+
+/******************************************
+ * Compile the MixinType, returning the type or expression AST.
+ *
+ * Doesn't run semantic() on the returned object.
+ * Params:
+ * tm = mixin to compile as a type or expression
+ * loc = location for error messages
+ * sc = context
+ * Return:
+ * null if error, else RootObject AST as parsed
+ */
+RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc)
+{
+ OutBuffer buf;
+ if (expressionsToString(buf, sc, tm.exps))
+ return null;
+
+ const errors = global.errors;
+ const len = buf.length;
+ buf.writeByte(0);
+ const str = buf.extractSlice()[0 .. len];
+ scope p = new Parser!ASTCodegen(loc, sc._module, str, false);
+ p.nextToken();
+ //printf("p.loc.linnum = %d\n", p.loc.linnum);
+
+ auto o = p.parseTypeOrAssignExp(TOK.endOfFile);
+ if (errors != global.errors)
+ {
+ assert(global.errors != errors); // should have caught all these cases
+ return null;
+ }
+ if (p.token.value != TOK.endOfFile)
+ {
+ .error(loc, "incomplete mixin type `%s`", str.ptr);
+ return null;
+ }
+
+ Type t = o.isType();
+ Expression e = t ? t.typeToExpression() : o.isExpression();
+
+ return (!e && t) ? t : e;
+}
+
+
+/************************************
+ * If an identical type to `type` is in `type.stringtable`, return
+ * the latter one. Otherwise, add it to `type.stringtable`.
+ * Some types don't get merged and are returned as-is.
+ * Params:
+ * type = Type to check against existing types
+ * Returns:
+ * the type that was merged
+ */
+Type merge(Type type)
+{
+ switch (type.ty)
+ {
+ case Terror:
+ case Ttypeof:
+ case Tident:
+ case Tinstance:
+ case Tmixin:
+ return type;
+
+ case Tsarray:
+ // prevents generating the mangle if the array dim is not yet known
+ if (!(cast(TypeSArray) type).dim.isIntegerExp())
+ return type;
+ goto default;
+
+ case Tenum:
+ break;
+
+ case Taarray:
+ if (!(cast(TypeAArray)type).index.merge().deco)
+ return type;
+ goto default;
+
+ default:
+ if (type.nextOf() && !type.nextOf().deco)
+ return type;
+ break;
+ }
+
+ //printf("merge(%s)\n", toChars());
+ if (!type.deco)
+ {
+ OutBuffer buf;
+ buf.reserve(32);
+
+ mangleToBuffer(type, &buf);
+
+ auto sv = type.stringtable.update(buf[]);
+ if (sv.value)
+ {
+ Type t = sv.value;
+ debug
+ {
+ import core.stdc.stdio;
+ if (!t.deco)
+ printf("t = %s\n", t.toChars());
+ }
+ assert(t.deco);
+ //printf("old value, deco = '%s' %p\n", t.deco, t.deco);
+ return t;
+ }
+ else
+ {
+ Type t = stripDefaultArgs(type);
+ sv.value = t;
+ type.deco = t.deco = cast(char*)sv.toDchars();
+ //printf("new value, deco = '%s' %p\n", t.deco, t.deco);
+ return t;
+ }
+ }
+ return type;
+}
+
+/***************************************
+ * Calculate built-in properties which just the type is necessary.
+ *
+ * Params:
+ * t = the type for which the property is calculated
+ * scope_ = the scope from which the property is being accessed. Used for visibility checks only.
+ * loc = the location where the property is encountered
+ * ident = the identifier of the property
+ * flag = if flag & 1, don't report "not a property" error and just return NULL.
+ * Returns:
+ * expression representing the property, or null if not a property and (flag & 1)
+ */
+Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier ident, int flag)
+{
+ Expression visitType(Type mt)
+ {
+ Expression e;
+ static if (LOGDOTEXP)
+ {
+ printf("Type::getProperty(type = '%s', ident = '%s')\n", mt.toChars(), ident.toChars());
+ }
+ if (ident == Id.__sizeof)
+ {
+ d_uns64 sz = mt.size(loc);
+ if (sz == SIZE_INVALID)
+ return ErrorExp.get();
+ e = new IntegerExp(loc, sz, Type.tsize_t);
+ }
+ else if (ident == Id.__xalignof)
+ {
+ const explicitAlignment = mt.alignment();
+ const naturalAlignment = mt.alignsize();
+ const actualAlignment = (explicitAlignment == STRUCTALIGN_DEFAULT ? naturalAlignment : explicitAlignment);
+ e = new IntegerExp(loc, actualAlignment, Type.tsize_t);
+ }
+ else if (ident == Id._init)
+ {
+ Type tb = mt.toBasetype();
+ e = mt.defaultInitLiteral(loc);
+ if (tb.ty == Tstruct && tb.needsNested())
+ {
+ e.isStructLiteralExp().useStaticInit = true;
+ }
+ }
+ else if (ident == Id._mangleof)
+ {
+ if (!mt.deco)
+ {
+ error(loc, "forward reference of type `%s.mangleof`", mt.toChars());
+ e = ErrorExp.get();
+ }
+ else
+ {
+ e = new StringExp(loc, mt.deco.toDString());
+ Scope sc;
+ e = e.expressionSemantic(&sc);
+ }
+ }
+ else if (ident == Id.stringof)
+ {
+ const s = mt.toChars();
+ e = new StringExp(loc, s.toDString());
+ Scope sc;
+ e = e.expressionSemantic(&sc);
+ }
+ else if (flag && mt != Type.terror)
+ {
+ return null;
+ }
+ else
+ {
+ Dsymbol s = null;
+ if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum)
+ s = mt.toDsymbol(null);
+ if (s)
+ s = s.search_correct(ident);
+ if (s && !symbolIsVisible(scope_, s))
+ s = null;
+ if (mt != Type.terror)
+ {
+ if (s)
+ error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars());
+ else if (ident == Id.call && mt.ty == Tclass)
+ error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars());
+
+ else if (const n = importHint(ident.toString()))
+ error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr);
+ else
+ {
+ error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true));
+ if (auto dsym = mt.toDsymbol(scope_))
+ if (auto sym = dsym.isAggregateDeclaration())
+ {
+ if (auto fd = search_function(sym, Id.opDispatch))
+ errorSupplemental(loc, "potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message");
+ else if (!sym.members)
+ errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true));
+ }
+ }
+ }
+ e = ErrorExp.get();
+ }
+ return e;
+ }
+
+ Expression visitError(TypeError)
+ {
+ return ErrorExp.get();
+ }
+
+ Expression visitBasic(TypeBasic mt)
+ {
+ Expression integerValue(dinteger_t i)
+ {
+ return new IntegerExp(loc, i, mt);
+ }
+
+ Expression intValue(dinteger_t i)
+ {
+ return new IntegerExp(loc, i, Type.tint32);
+ }
+
+ Expression floatValue(real_t r)
+ {
+ if (mt.isreal() || mt.isimaginary())
+ return new RealExp(loc, r, mt);
+ else
+ {
+ return new ComplexExp(loc, complex_t(r, r), mt);
+ }
+ }
+
+ //printf("TypeBasic::getProperty('%s')\n", ident.toChars());
+ if (ident == Id.max)
+ {
+ switch (mt.ty)
+ {
+ case Tint8: return integerValue(byte.max);
+ case Tuns8: return integerValue(ubyte.max);
+ case Tint16: return integerValue(short.max);
+ case Tuns16: return integerValue(ushort.max);
+ case Tint32: return integerValue(int.max);
+ case Tuns32: return integerValue(uint.max);
+ case Tint64: return integerValue(long.max);
+ case Tuns64: return integerValue(ulong.max);
+ case Tbool: return integerValue(bool.max);
+ case Tchar: return integerValue(char.max);
+ case Twchar: return integerValue(wchar.max);
+ case Tdchar: return integerValue(dchar.max);
+ case Tcomplex32:
+ case Timaginary32:
+ case Tfloat32: return floatValue(target.FloatProperties.max);
+ case Tcomplex64:
+ case Timaginary64:
+ case Tfloat64: return floatValue(target.DoubleProperties.max);
+ case Tcomplex80:
+ case Timaginary80:
+ case Tfloat80: return floatValue(target.RealProperties.max);
+ default: break;
+ }
+ }
+ else if (ident == Id.min)
+ {
+ switch (mt.ty)
+ {
+ case Tint8: return integerValue(byte.min);
+ case Tuns8:
+ case Tuns16:
+ case Tuns32:
+ case Tuns64:
+ case Tbool:
+ case Tchar:
+ case Twchar:
+ case Tdchar: return integerValue(0);
+ case Tint16: return integerValue(short.min);
+ case Tint32: return integerValue(int.min);
+ case Tint64: return integerValue(long.min);
+ default: break;
+ }
+ }
+ else if (ident == Id.min_normal)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Timaginary32:
+ case Tfloat32: return floatValue(target.FloatProperties.min_normal);
+ case Tcomplex64:
+ case Timaginary64:
+ case Tfloat64: return floatValue(target.DoubleProperties.min_normal);
+ case Tcomplex80:
+ case Timaginary80:
+ case Tfloat80: return floatValue(target.RealProperties.min_normal);
+ default: break;
+ }
+ }
+ else if (ident == Id.nan)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ case Timaginary32:
+ case Timaginary64:
+ case Timaginary80:
+ case Tfloat32:
+ case Tfloat64:
+ case Tfloat80: return floatValue(target.RealProperties.nan);
+ default: break;
+ }
+ }
+ else if (ident == Id.infinity)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ case Timaginary32:
+ case Timaginary64:
+ case Timaginary80:
+ case Tfloat32:
+ case Tfloat64:
+ case Tfloat80: return floatValue(target.RealProperties.infinity);
+ default: break;
+ }
+ }
+ else if (ident == Id.dig)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Timaginary32:
+ case Tfloat32: return intValue(target.FloatProperties.dig);
+ case Tcomplex64:
+ case Timaginary64:
+ case Tfloat64: return intValue(target.DoubleProperties.dig);
+ case Tcomplex80:
+ case Timaginary80:
+ case Tfloat80: return intValue(target.RealProperties.dig);
+ default: break;
+ }
+ }
+ else if (ident == Id.epsilon)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Timaginary32:
+ case Tfloat32: return floatValue(target.FloatProperties.epsilon);
+ case Tcomplex64:
+ case Timaginary64:
+ case Tfloat64: return floatValue(target.DoubleProperties.epsilon);
+ case Tcomplex80:
+ case Timaginary80:
+ case Tfloat80: return floatValue(target.RealProperties.epsilon);
+ default: break;
+ }
+ }
+ else if (ident == Id.mant_dig)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Timaginary32:
+ case Tfloat32: return intValue(target.FloatProperties.mant_dig);
+ case Tcomplex64:
+ case Timaginary64:
+ case Tfloat64: return intValue(target.DoubleProperties.mant_dig);
+ case Tcomplex80:
+ case Timaginary80:
+ case Tfloat80: return intValue(target.RealProperties.mant_dig);
+ default: break;
+ }
+ }
+ else if (ident == Id.max_10_exp)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Timaginary32:
+ case Tfloat32: return intValue(target.FloatProperties.max_10_exp);
+ case Tcomplex64:
+ case Timaginary64:
+ case Tfloat64: return intValue(target.DoubleProperties.max_10_exp);
+ case Tcomplex80:
+ case Timaginary80:
+ case Tfloat80: return intValue(target.RealProperties.max_10_exp);
+ default: break;
+ }
+ }
+ else if (ident == Id.max_exp)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Timaginary32:
+ case Tfloat32: return intValue(target.FloatProperties.max_exp);
+ case Tcomplex64:
+ case Timaginary64:
+ case Tfloat64: return intValue(target.DoubleProperties.max_exp);
+ case Tcomplex80:
+ case Timaginary80:
+ case Tfloat80: return intValue(target.RealProperties.max_exp);
+ default: break;
+ }
+ }
+ else if (ident == Id.min_10_exp)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Timaginary32:
+ case Tfloat32: return intValue(target.FloatProperties.min_10_exp);
+ case Tcomplex64:
+ case Timaginary64:
+ case Tfloat64: return intValue(target.DoubleProperties.min_10_exp);
+ case Tcomplex80:
+ case Timaginary80:
+ case Tfloat80: return intValue(target.RealProperties.min_10_exp);
+ default: break;
+ }
+ }
+ else if (ident == Id.min_exp)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ case Timaginary32:
+ case Tfloat32: return intValue(target.FloatProperties.min_exp);
+ case Tcomplex64:
+ case Timaginary64:
+ case Tfloat64: return intValue(target.DoubleProperties.min_exp);
+ case Tcomplex80:
+ case Timaginary80:
+ case Tfloat80: return intValue(target.RealProperties.min_exp);
+ default: break;
+ }
+ }
+ return visitType(mt);
+ }
+
+ Expression visitVector(TypeVector mt)
+ {
+ return visitType(mt);
+ }
+
+ Expression visitEnum(TypeEnum mt)
+ {
+ Expression e;
+ if (ident == Id.max || ident == Id.min)
+ {
+ return mt.sym.getMaxMinValue(loc, ident);
+ }
+ else if (ident == Id._init)
+ {
+ e = mt.defaultInitLiteral(loc);
+ }
+ else if (ident == Id.stringof)
+ {
+ e = new StringExp(loc, mt.toString());
+ Scope sc;
+ e = e.expressionSemantic(&sc);
+ }
+ else if (ident == Id._mangleof)
+ {
+ e = visitType(mt);
+ }
+ else
+ {
+ e = mt.toBasetype().getProperty(scope_, loc, ident, flag);
+ }
+ return e;
+ }
+
+ Expression visitTuple(TypeTuple mt)
+ {
+ Expression e;
+ static if (LOGDOTEXP)
+ {
+ printf("TypeTuple::getProperty(type = '%s', ident = '%s')\n", mt.toChars(), ident.toChars());
+ }
+ if (ident == Id.length)
+ {
+ e = new IntegerExp(loc, mt.arguments.dim, Type.tsize_t);
+ }
+ else if (ident == Id._init)
+ {
+ e = mt.defaultInitLiteral(loc);
+ }
+ else if (flag)
+ {
+ e = null;
+ }
+ else
+ {
+ error(loc, "no property `%s` for tuple `%s`", ident.toChars(), mt.toChars());
+ e = ErrorExp.get();
+ }
+ return e;
+ }
+
+ switch (t.ty)
+ {
+ default: return t.isTypeBasic() ?
+ visitBasic(cast(TypeBasic)t) :
+ visitType(t);
+
+ case Terror: return visitError (cast(TypeError)t);
+ case Tvector: return visitVector(cast(TypeVector)t);
+ case Tenum: return visitEnum (cast(TypeEnum)t);
+ case Ttuple: return visitTuple (cast(TypeTuple)t);
+ }
+}
+
+/***************************************
+ * Determine if Expression `exp` should instead be a Type, a Dsymbol, or remain an Expression.
+ * Params:
+ * exp = Expression to look at
+ * t = if exp should be a Type, set t to that Type else null
+ * s = if exp should be a Dsymbol, set s to that Dsymbol else null
+ * e = if exp should remain an Expression, set e to that Expression else null
+ *
+ */
+private void resolveExp(Expression exp, out Type t, out Expression e, out Dsymbol s)
+{
+ if (exp.isTypeExp())
+ t = exp.type;
+ else if (auto ve = exp.isVarExp())
+ {
+ if (auto v = ve.var.isVarDeclaration())
+ e = exp;
+ else
+ s = ve.var;
+ }
+ else if (auto te = exp.isTemplateExp())
+ s = te.td;
+ else if (auto se = exp.isScopeExp())
+ s = se.sds;
+ else if (exp.isFuncExp())
+ s = getDsymbol(exp);
+ else if (auto dte = exp.isDotTemplateExp())
+ s = dte.td;
+ else if (exp.isErrorExp())
+ t = Type.terror;
+ else
+ e = exp;
+}
+
+/************************************
+ * Resolve type 'mt' to either type, symbol, or expression.
+ * If errors happened, resolved to Type.terror.
+ *
+ * Params:
+ * mt = type to be resolved
+ * loc = the location where the type is encountered
+ * sc = the scope of the type
+ * pe = is set if t is an expression
+ * pt = is set if t is a type
+ * ps = is set if t is a symbol
+ * intypeid = true if in type id
+ */
+void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false)
+{
+ void returnExp(Expression e)
+ {
+ pe = e;
+ pt = null;
+ ps = null;
+ }
+
+ void returnType(Type t)
+ {
+ pe = null;
+ pt = t;
+ ps = null;
+ }
+
+ void returnSymbol(Dsymbol s)
+ {
+ pe = null;
+ pt = null;
+ ps = s;
+ }
+
+ void returnError()
+ {
+ returnType(Type.terror);
+ }
+
+ void visitType(Type mt)
+ {
+ //printf("Type::resolve() %s, %d\n", mt.toChars(), mt.ty);
+ Type t = typeSemantic(mt, loc, sc);
+ assert(t);
+ returnType(t);
+ }
+
+ void visitSArray(TypeSArray mt)
+ {
+ //printf("TypeSArray::resolve() %s\n", mt.toChars());
+ mt.next.resolve(loc, sc, pe, pt, ps, intypeid);
+ //printf("s = %p, e = %p, t = %p\n", ps, pe, pt);
+ if (pe)
+ {
+ // It's really an index expression
+ if (Dsymbol s = getDsymbol(pe))
+ pe = new DsymbolExp(loc, s);
+ returnExp(new ArrayExp(loc, pe, mt.dim));
+ }
+ else if (ps)
+ {
+ Dsymbol s = ps;
+ if (auto tup = s.isTupleDeclaration())
+ {
+ mt.dim = semanticLength(sc, tup, mt.dim);
+ mt.dim = mt.dim.ctfeInterpret();
+ if (mt.dim.op == TOK.error)
+ return returnError();
+
+ const d = mt.dim.toUInteger();
+ if (d >= tup.objects.dim)
+ {
+ error(loc, "tuple index `%llu` exceeds length %llu", d, cast(ulong) tup.objects.dim);
+ return returnError();
+ }
+
+ RootObject o = (*tup.objects)[cast(size_t)d];
+ if (o.dyncast() == DYNCAST.dsymbol)
+ {
+ return returnSymbol(cast(Dsymbol)o);
+ }
+ if (o.dyncast() == DYNCAST.expression)
+ {
+ Expression e = cast(Expression)o;
+ if (e.op == TOK.dSymbol)
+ return returnSymbol((cast(DsymbolExp)e).s);
+ else
+ return returnExp(e);
+ }
+ if (o.dyncast() == DYNCAST.type)
+ {
+ return returnType((cast(Type)o).addMod(mt.mod));
+ }
+
+ /* Create a new TupleDeclaration which
+ * is a slice [d..d+1] out of the old one.
+ * Do it this way because TemplateInstance::semanticTiargs()
+ * can handle unresolved Objects this way.
+ */
+ auto objects = new Objects(1);
+ (*objects)[0] = o;
+ return returnSymbol(new TupleDeclaration(loc, tup.ident, objects));
+ }
+ else
+ return visitType(mt);
+ }
+ else
+ {
+ if (pt.ty != Terror)
+ mt.next = pt; // prevent re-running semantic() on 'next'
+ visitType(mt);
+ }
+
+ }
+
+ void visitDArray(TypeDArray mt)
+ {
+ //printf("TypeDArray::resolve() %s\n", mt.toChars());
+ mt.next.resolve(loc, sc, pe, pt, ps, intypeid);
+ //printf("s = %p, e = %p, t = %p\n", ps, pe, pt);
+ if (pe)
+ {
+ // It's really a slice expression
+ if (Dsymbol s = getDsymbol(pe))
+ pe = new DsymbolExp(loc, s);
+ returnExp(new ArrayExp(loc, pe));
+ }
+ else if (ps)
+ {
+ if (auto tup = ps.isTupleDeclaration())
+ {
+ // keep ps
+ }
+ else
+ visitType(mt);
+ }
+ else
+ {
+ if (pt.ty != Terror)
+ mt.next = pt; // prevent re-running semantic() on 'next'
+ visitType(mt);
+ }
+ }
+
+ void visitAArray(TypeAArray mt)
+ {
+ //printf("TypeAArray::resolve() %s\n", mt.toChars());
+ // Deal with the case where we thought the index was a type, but
+ // in reality it was an expression.
+ if (mt.index.ty == Tident || mt.index.ty == Tinstance || mt.index.ty == Tsarray)
+ {
+ Expression e;
+ Type t;
+ Dsymbol s;
+ mt.index.resolve(loc, sc, e, t, s, intypeid);
+ if (e)
+ {
+ // It was an expression -
+ // Rewrite as a static array
+ auto tsa = new TypeSArray(mt.next, e);
+ tsa.mod = mt.mod; // just copy mod field so tsa's semantic is not yet done
+ return tsa.resolve(loc, sc, pe, pt, ps, intypeid);
+ }
+ else if (t)
+ mt.index = t;
+ else
+ .error(loc, "index is not a type or an expression");
+ }
+ visitType(mt);
+ }
+
+ /*************************************
+ * Takes an array of Identifiers and figures out if
+ * it represents a Type or an Expression.
+ * Output:
+ * if expression, pe is set
+ * if type, pt is set
+ */
+ void visitIdentifier(TypeIdentifier mt)
+ {
+ //printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, mt.toChars());
+ if ((mt.ident.equals(Id._super) || mt.ident.equals(Id.This)) && !hasThis(sc))
+ {
+ // @@@DEPRECATED_v2.091@@@.
+ // Made an error in 2.086.
+ // Eligible for removal in 2.091.
+ if (mt.ident.equals(Id._super))
+ {
+ error(mt.loc, "Using `super` as a type is obsolete. Use `typeof(super)` instead");
+ }
+ // @@@DEPRECATED_v2.091@@@.
+ // Made an error in 2.086.
+ // Eligible for removal in 2.091.
+ if (mt.ident.equals(Id.This))
+ {
+ error(mt.loc, "Using `this` as a type is obsolete. Use `typeof(this)` instead");
+ }
+ if (AggregateDeclaration ad = sc.getStructClassScope())
+ {
+ if (ClassDeclaration cd = ad.isClassDeclaration())
+ {
+ if (mt.ident.equals(Id.This))
+ mt.ident = cd.ident;
+ else if (cd.baseClass && mt.ident.equals(Id._super))
+ mt.ident = cd.baseClass.ident;
+ }
+ else
+ {
+ StructDeclaration sd = ad.isStructDeclaration();
+ if (sd && mt.ident.equals(Id.This))
+ mt.ident = sd.ident;
+ }
+ }
+ }
+ if (mt.ident == Id.ctfe)
+ {
+ error(loc, "variable `__ctfe` cannot be read at compile time");
+ return returnError();
+ }
+
+ Dsymbol scopesym;
+ Dsymbol s = sc.search(loc, mt.ident, &scopesym);
+ /*
+ * https://issues.dlang.org/show_bug.cgi?id=1170
+ * https://issues.dlang.org/show_bug.cgi?id=10739
+ *
+ * If a symbol is not found, it might be declared in
+ * a mixin-ed string or a mixin-ed template, so before
+ * issuing an error semantically analyze all string/template
+ * mixins that are members of the current ScopeDsymbol.
+ */
+ if (!s && sc.enclosing)
+ {
+ ScopeDsymbol sds = sc.enclosing.scopesym;
+ if (sds && sds.members)
+ {
+ void semanticOnMixin(Dsymbol member)
+ {
+ if (auto compileDecl = member.isCompileDeclaration())
+ compileDecl.dsymbolSemantic(sc);
+ else if (auto mixinTempl = member.isTemplateMixin())
+ mixinTempl.dsymbolSemantic(sc);
+ }
+ sds.members.foreachDsymbol( s => semanticOnMixin(s) );
+ s = sc.search(loc, mt.ident, &scopesym);
+ }
+ }
+
+ if (s)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=16042
+ // If `f` is really a function template, then replace `f`
+ // with the function template declaration.
+ if (auto f = s.isFuncDeclaration())
+ {
+ if (auto td = getFuncTemplateDecl(f))
+ {
+ // If not at the beginning of the overloaded list of
+ // `TemplateDeclaration`s, then get the beginning
+ if (td.overroot)
+ td = td.overroot;
+ s = td;
+ }
+ }
+ }
+
+ mt.resolveHelper(loc, sc, s, scopesym, pe, pt, ps, intypeid);
+ if (pt)
+ pt = pt.addMod(mt.mod);
+ }
+
+ void visitInstance(TypeInstance mt)
+ {
+ // Note close similarity to TypeIdentifier::resolve()
+
+ //printf("TypeInstance::resolve(sc = %p, tempinst = '%s')\n", sc, mt.tempinst.toChars());
+ mt.tempinst.dsymbolSemantic(sc);
+ if (!global.gag && mt.tempinst.errors)
+ return returnError();
+
+ mt.resolveHelper(loc, sc, mt.tempinst, null, pe, pt, ps, intypeid);
+ if (pt)
+ pt = pt.addMod(mt.mod);
+ //if (pt) printf("pt = %d '%s'\n", pt.ty, pt.toChars());
+ }
+
+ void visitTypeof(TypeTypeof mt)
+ {
+ //printf("TypeTypeof::resolve(this = %p, sc = %p, idents = '%s')\n", mt, sc, mt.toChars());
+ //static int nest; if (++nest == 50) *(char*)0=0;
+ if (sc is null)
+ {
+ error(loc, "Invalid scope.");
+ return returnError();
+ }
+ if (mt.inuse)
+ {
+ mt.inuse = 2;
+ error(loc, "circular `typeof` definition");
+ Lerr:
+ mt.inuse--;
+ return returnError();
+ }
+ mt.inuse++;
+
+ /* Currently we cannot evaluate 'exp' in speculative context, because
+ * the type implementation may leak to the final execution. Consider:
+ *
+ * struct S(T) {
+ * string toString() const { return "x"; }
+ * }
+ * void main() {
+ * alias X = typeof(S!int());
+ * assert(typeid(X).toString() == "x");
+ * }
+ */
+ Scope* sc2 = sc.push();
+
+ if (!mt.exp.isTypeidExp())
+ /* Treat typeof(typeid(exp)) as needing
+ * the full semantic analysis of the typeid.
+ * https://issues.dlang.org/show_bug.cgi?id=20958
+ */
+ sc2.intypeof = 1;
+
+ auto exp2 = mt.exp.expressionSemantic(sc2);
+ exp2 = resolvePropertiesOnly(sc2, exp2);
+ sc2.pop();
+
+ if (exp2.op == TOK.error)
+ {
+ if (!global.gag)
+ mt.exp = exp2;
+ goto Lerr;
+ }
+ mt.exp = exp2;
+
+ if (mt.exp.op == TOK.type ||
+ mt.exp.op == TOK.scope_)
+ {
+ if (mt.exp.checkType())
+ goto Lerr;
+
+ /* Today, 'typeof(func)' returns void if func is a
+ * function template (TemplateExp), or
+ * template lambda (FuncExp).
+ * It's actually used in Phobos as an idiom, to branch code for
+ * template functions.
+ */
+ }
+ if (auto f = mt.exp.op == TOK.variable ? (cast( VarExp)mt.exp).var.isFuncDeclaration()
+ : mt.exp.op == TOK.dotVariable ? (cast(DotVarExp)mt.exp).var.isFuncDeclaration() : null)
+ {
+ // f might be a unittest declaration which is incomplete when compiled
+ // without -unittest. That causes a segfault in checkForwardRef, see
+ // https://issues.dlang.org/show_bug.cgi?id=20626
+ if ((!f.isUnitTestDeclaration() || global.params.useUnitTests) && f.checkForwardRef(loc))
+ goto Lerr;
+ }
+ if (auto f = isFuncAddress(mt.exp))
+ {
+ if (f.checkForwardRef(loc))
+ goto Lerr;
+ }
+
+ Type t = mt.exp.type;
+ if (!t)
+ {
+ error(loc, "expression `%s` has no type", mt.exp.toChars());
+ goto Lerr;
+ }
+ if (t.ty == Ttypeof)
+ {
+ error(loc, "forward reference to `%s`", mt.toChars());
+ goto Lerr;
+ }
+ if (mt.idents.dim == 0)
+ {
+ returnType(t.addMod(mt.mod));
+ }
+ else
+ {
+ if (Dsymbol s = t.toDsymbol(sc))
+ mt.resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid);
+ else
+ {
+ auto e = typeToExpressionHelper(mt, new TypeExp(loc, t));
+ e = e.expressionSemantic(sc);
+ resolveExp(e, pt, pe, ps);
+ }
+ if (pt)
+ pt = pt.addMod(mt.mod);
+ }
+ mt.inuse--;
+ }
+
+ void visitReturn(TypeReturn mt)
+ {
+ //printf("TypeReturn::resolve(sc = %p, idents = '%s')\n", sc, mt.toChars());
+ Type t;
+ {
+ FuncDeclaration func = sc.func;
+ if (!func)
+ {
+ error(loc, "`typeof(return)` must be inside function");
+ return returnError();
+ }
+ if (func.fes)
+ func = func.fes.func;
+ t = func.type.nextOf();
+ if (!t)
+ {
+ error(loc, "cannot use `typeof(return)` inside function `%s` with inferred return type", sc.func.toChars());
+ return returnError();
+ }
+ }
+ if (mt.idents.dim == 0)
+ {
+ return returnType(t.addMod(mt.mod));
+ }
+ else
+ {
+ if (Dsymbol s = t.toDsymbol(sc))
+ mt.resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid);
+ else
+ {
+ auto e = typeToExpressionHelper(mt, new TypeExp(loc, t));
+ e = e.expressionSemantic(sc);
+ resolveExp(e, pt, pe, ps);
+ }
+ if (pt)
+ pt = pt.addMod(mt.mod);
+ }
+ }
+
+ void visitSlice(TypeSlice mt)
+ {
+ mt.next.resolve(loc, sc, pe, pt, ps, intypeid);
+ if (pe)
+ {
+ // It's really a slice expression
+ if (Dsymbol s = getDsymbol(pe))
+ pe = new DsymbolExp(loc, s);
+ return returnExp(new ArrayExp(loc, pe, new IntervalExp(loc, mt.lwr, mt.upr)));
+ }
+ else if (ps)
+ {
+ Dsymbol s = ps;
+ TupleDeclaration td = s.isTupleDeclaration();
+ if (td)
+ {
+ /* It's a slice of a TupleDeclaration
+ */
+ ScopeDsymbol sym = new ArrayScopeSymbol(sc, td);
+ sym.parent = sc.scopesym;
+ sc = sc.push(sym);
+ sc = sc.startCTFE();
+ mt.lwr = mt.lwr.expressionSemantic(sc);
+ mt.upr = mt.upr.expressionSemantic(sc);
+ sc = sc.endCTFE();
+ sc = sc.pop();
+
+ mt.lwr = mt.lwr.ctfeInterpret();
+ mt.upr = mt.upr.ctfeInterpret();
+ const i1 = mt.lwr.toUInteger();
+ const i2 = mt.upr.toUInteger();
+ if (!(i1 <= i2 && i2 <= td.objects.dim))
+ {
+ error(loc, "slice `[%llu..%llu]` is out of range of [0..%llu]", i1, i2, cast(ulong) td.objects.dim);
+ return returnError();
+ }
+
+ if (i1 == 0 && i2 == td.objects.dim)
+ {
+ return returnSymbol(td);
+ }
+
+ /* Create a new TupleDeclaration which
+ * is a slice [i1..i2] out of the old one.
+ */
+ auto objects = new Objects(cast(size_t)(i2 - i1));
+ for (size_t i = 0; i < objects.dim; i++)
+ {
+ (*objects)[i] = (*td.objects)[cast(size_t)i1 + i];
+ }
+
+ return returnSymbol(new TupleDeclaration(loc, td.ident, objects));
+ }
+ else
+ visitType(mt);
+ }
+ else
+ {
+ if (pt.ty != Terror)
+ mt.next = pt; // prevent re-running semantic() on 'next'
+ visitType(mt);
+ }
+ }
+
+ void visitMixin(TypeMixin mt)
+ {
+ RootObject o = mt.obj;
+
+ // if already resolved just set pe/pt/ps and return.
+ if (o)
+ {
+ pe = o.isExpression();
+ pt = o.isType();
+ ps = o.isDsymbol();
+ return;
+ }
+
+ o = mt.compileTypeMixin(loc, sc);
+ if (auto t = o.isType())
+ {
+ resolve(t, loc, sc, pe, pt, ps, intypeid);
+ if (pt)
+ pt = pt.addMod(mt.mod);
+ }
+ else if (auto e = o.isExpression())
+ {
+ e = e.expressionSemantic(sc);
+ if (auto et = e.isTypeExp())
+ returnType(et.type.addMod(mt.mod));
+ else
+ returnExp(e);
+ }
+ else
+ returnError();
+
+ // save the result
+ mt.obj = pe ? pe : (pt ? pt : ps);
+ }
+
+ void visitTraits(TypeTraits tt)
+ {
+ if (Type t = typeSemantic(tt, loc, sc))
+ returnType(t);
+ else if (tt.sym)
+ returnSymbol(tt.sym);
+ else
+ return returnError();
+ }
+
+ switch (mt.ty)
+ {
+ default: visitType (mt); break;
+ case Tsarray: visitSArray (cast(TypeSArray)mt); break;
+ case Tarray: visitDArray (cast(TypeDArray)mt); break;
+ case Taarray: visitAArray (cast(TypeAArray)mt); break;
+ case Tident: visitIdentifier(cast(TypeIdentifier)mt); break;
+ case Tinstance: visitInstance (cast(TypeInstance)mt); break;
+ case Ttypeof: visitTypeof (cast(TypeTypeof)mt); break;
+ case Treturn: visitReturn (cast(TypeReturn)mt); break;
+ case Tslice: visitSlice (cast(TypeSlice)mt); break;
+ case Tmixin: visitMixin (cast(TypeMixin)mt); break;
+ case Ttraits: visitTraits (cast(TypeTraits)mt); break;
+ }
+}
+
+/************************
+ * Access the members of the object e. This type is same as e.type.
+ * Params:
+ * mt = type for which the dot expression is used
+ * sc = instantiating scope
+ * e = expression to convert
+ * ident = identifier being used
+ * flag = DotExpFlag bit flags
+ *
+ * Returns:
+ * resulting expression with e.ident resolved
+ */
+Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
+{
+ Expression visitType(Type mt)
+ {
+ VarDeclaration v = null;
+ static if (LOGDOTEXP)
+ {
+ printf("Type::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ Expression ex = e.lastComma();
+ if (ex.op == TOK.dotVariable)
+ {
+ DotVarExp dv = cast(DotVarExp)ex;
+ v = dv.var.isVarDeclaration();
+ }
+ else if (ex.op == TOK.variable)
+ {
+ VarExp ve = cast(VarExp)ex;
+ v = ve.var.isVarDeclaration();
+ }
+ if (v)
+ {
+ if (ident == Id.offsetof)
+ {
+ v.dsymbolSemantic(null);
+ if (v.isField())
+ {
+ auto ad = v.toParent().isAggregateDeclaration();
+ objc.checkOffsetof(e, ad);
+ ad.size(e.loc);
+ if (ad.sizeok != Sizeok.done)
+ return ErrorExp.get();
+ return new IntegerExp(e.loc, v.offset, Type.tsize_t);
+ }
+ }
+ else if (ident == Id._init)
+ {
+ Type tb = mt.toBasetype();
+ e = mt.defaultInitLiteral(e.loc);
+ if (tb.ty == Tstruct && tb.needsNested())
+ {
+ e.isStructLiteralExp().useStaticInit = true;
+ }
+ goto Lreturn;
+ }
+ }
+ if (ident == Id.stringof)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=3796
+ * this should demangle e.type.deco rather than
+ * pretty-printing the type.
+ */
+ e = new StringExp(e.loc, e.toString());
+ }
+ else
+ e = mt.getProperty(sc, e.loc, ident, flag & DotExpFlag.gag);
+
+ Lreturn:
+ if (e)
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ Expression visitError(TypeError)
+ {
+ return ErrorExp.get();
+ }
+
+ Expression visitBasic(TypeBasic mt)
+ {
+ static if (LOGDOTEXP)
+ {
+ printf("TypeBasic::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ Type t;
+ if (ident == Id.re)
+ {
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ t = mt.tfloat32;
+ goto L1;
+
+ case Tcomplex64:
+ t = mt.tfloat64;
+ goto L1;
+
+ case Tcomplex80:
+ t = mt.tfloat80;
+ goto L1;
+ L1:
+ e = e.castTo(sc, t);
+ break;
+
+ case Tfloat32:
+ case Tfloat64:
+ case Tfloat80:
+ break;
+
+ case Timaginary32:
+ t = mt.tfloat32;
+ goto L2;
+
+ case Timaginary64:
+ t = mt.tfloat64;
+ goto L2;
+
+ case Timaginary80:
+ t = mt.tfloat80;
+ goto L2;
+ L2:
+ e = new RealExp(e.loc, CTFloat.zero, t);
+ break;
+
+ default:
+ e = mt.Type.getProperty(sc, e.loc, ident, flag);
+ break;
+ }
+ }
+ else if (ident == Id.im)
+ {
+ Type t2;
+ switch (mt.ty)
+ {
+ case Tcomplex32:
+ t = mt.timaginary32;
+ t2 = mt.tfloat32;
+ goto L3;
+
+ case Tcomplex64:
+ t = mt.timaginary64;
+ t2 = mt.tfloat64;
+ goto L3;
+
+ case Tcomplex80:
+ t = mt.timaginary80;
+ t2 = mt.tfloat80;
+ goto L3;
+ L3:
+ e = e.castTo(sc, t);
+ e.type = t2;
+ break;
+
+ case Timaginary32:
+ t = mt.tfloat32;
+ goto L4;
+
+ case Timaginary64:
+ t = mt.tfloat64;
+ goto L4;
+
+ case Timaginary80:
+ t = mt.tfloat80;
+ goto L4;
+ L4:
+ e = e.copy();
+ e.type = t;
+ break;
+
+ case Tfloat32:
+ case Tfloat64:
+ case Tfloat80:
+ e = new RealExp(e.loc, CTFloat.zero, mt);
+ break;
+
+ default:
+ e = mt.Type.getProperty(sc, e.loc, ident, flag);
+ break;
+ }
+ }
+ else
+ {
+ return visitType(mt);
+ }
+ if (!(flag & 1) || e)
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ Expression visitVector(TypeVector mt)
+ {
+ static if (LOGDOTEXP)
+ {
+ printf("TypeVector::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ if (ident == Id.ptr && e.op == TOK.call)
+ {
+ /* The trouble with TOK.call is the return ABI for float[4] is different from
+ * __vector(float[4]), and a type paint won't do.
+ */
+ e = new AddrExp(e.loc, e);
+ e = e.expressionSemantic(sc);
+ return e.castTo(sc, mt.basetype.nextOf().pointerTo());
+ }
+ if (ident == Id.array)
+ {
+ //e = e.castTo(sc, basetype);
+ // Keep lvalue-ness
+ e = new VectorArrayExp(e.loc, e);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (ident == Id._init || ident == Id.offsetof || ident == Id.stringof || ident == Id.__xalignof)
+ {
+ // init should return a new VectorExp
+ // https://issues.dlang.org/show_bug.cgi?id=12776
+ // offsetof does not work on a cast expression, so use e directly
+ // stringof should not add a cast to the output
+ return visitType(mt);
+ }
+
+ // Properties based on the vector element type and are values of the element type
+ if (ident == Id.max || ident == Id.min || ident == Id.min_normal ||
+ ident == Id.nan || ident == Id.infinity || ident == Id.epsilon)
+ {
+ auto vet = mt.basetype.isTypeSArray().next; // vector element type
+ if (auto ev = getProperty(vet, sc, e.loc, ident, DotExpFlag.gag))
+ return ev.castTo(sc, mt); // 'broadcast' ev to the vector elements
+ }
+
+ return mt.basetype.dotExp(sc, e.castTo(sc, mt.basetype), ident, flag);
+ }
+
+ Expression visitArray(TypeArray mt)
+ {
+ static if (LOGDOTEXP)
+ {
+ printf("TypeArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+
+ e = visitType(mt);
+
+ if (!(flag & 1) || e)
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ Expression visitSArray(TypeSArray mt)
+ {
+ static if (LOGDOTEXP)
+ {
+ printf("TypeSArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ if (ident == Id.length)
+ {
+ Loc oldLoc = e.loc;
+ e = mt.dim.copy();
+ e.loc = oldLoc;
+ }
+ else if (ident == Id.ptr)
+ {
+ if (e.op == TOK.type)
+ {
+ e.error("`%s` is not an expression", e.toChars());
+ return ErrorExp.get();
+ }
+ else if (checkUnsafeDotExp(sc, e, ident, flag))
+ {
+ return ErrorExp.get();
+ }
+ e = e.castTo(sc, e.type.nextOf().pointerTo());
+ }
+ else
+ {
+ e = visitArray(mt);
+ }
+ if (!(flag & 1) || e)
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ Expression visitDArray(TypeDArray mt)
+ {
+ static if (LOGDOTEXP)
+ {
+ printf("TypeDArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ if (e.op == TOK.type && (ident == Id.length || ident == Id.ptr))
+ {
+ e.error("`%s` is not an expression", e.toChars());
+ return ErrorExp.get();
+ }
+ if (ident == Id.length)
+ {
+ if (e.op == TOK.string_)
+ {
+ StringExp se = cast(StringExp)e;
+ return new IntegerExp(se.loc, se.len, Type.tsize_t);
+ }
+ if (e.op == TOK.null_)
+ {
+ return new IntegerExp(e.loc, 0, Type.tsize_t);
+ }
+ if (checkNonAssignmentArrayOp(e))
+ {
+ return ErrorExp.get();
+ }
+ e = new ArrayLengthExp(e.loc, e);
+ e.type = Type.tsize_t;
+ return e;
+ }
+ else if (ident == Id.ptr)
+ {
+ if (checkUnsafeDotExp(sc, e, ident, flag))
+ return ErrorExp.get();
+ return e.castTo(sc, mt.next.pointerTo());
+ }
+ else
+ {
+ return visitArray(mt);
+ }
+ }
+
+ Expression visitAArray(TypeAArray mt)
+ {
+ static if (LOGDOTEXP)
+ {
+ printf("TypeAArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ if (ident == Id.length)
+ {
+ __gshared FuncDeclaration fd_aaLen = null;
+ if (fd_aaLen is null)
+ {
+ auto fparams = new Parameters();
+ fparams.push(new Parameter(STC.const_ | STC.scope_, mt, null, null, null));
+ fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen);
+ TypeFunction tf = fd_aaLen.type.toTypeFunction();
+ tf.purity = PURE.const_;
+ tf.isnothrow = true;
+ tf.isnogc = false;
+ }
+ Expression ev = new VarExp(e.loc, fd_aaLen, false);
+ e = new CallExp(e.loc, ev, e);
+ e.type = fd_aaLen.type.toTypeFunction().next;
+ return e;
+ }
+ else
+ {
+ return visitType(mt);
+ }
+ }
+
+ Expression visitReference(TypeReference mt)
+ {
+ static if (LOGDOTEXP)
+ {
+ printf("TypeReference::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ // References just forward things along
+ return mt.next.dotExp(sc, e, ident, flag);
+ }
+
+ Expression visitDelegate(TypeDelegate mt)
+ {
+ static if (LOGDOTEXP)
+ {
+ printf("TypeDelegate::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ if (ident == Id.ptr)
+ {
+ e = new DelegatePtrExp(e.loc, e);
+ e = e.expressionSemantic(sc);
+ }
+ else if (ident == Id.funcptr)
+ {
+ if (checkUnsafeDotExp(sc, e, ident, flag))
+ {
+ return ErrorExp.get();
+ }
+ e = new DelegateFuncptrExp(e.loc, e);
+ e = e.expressionSemantic(sc);
+ }
+ else
+ {
+ return visitType(mt);
+ }
+ return e;
+ }
+
+ /***************************************
+ * Figures out what to do with an undefined member reference
+ * for classes and structs.
+ *
+ * If flag & 1, don't report "not a property" error and just return NULL.
+ */
+ Expression noMember(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
+ {
+ //printf("Type.noMember(e: %s ident: %s flag: %d)\n", e.toChars(), ident.toChars(), flag);
+
+ bool gagError = flag & 1;
+
+ __gshared int nest; // https://issues.dlang.org/show_bug.cgi?id=17380
+
+ static Expression returnExp(Expression e)
+ {
+ --nest;
+ return e;
+ }
+
+ if (++nest > global.recursionLimit)
+ {
+ .error(e.loc, "cannot resolve identifier `%s`", ident.toChars());
+ return returnExp(gagError ? null : ErrorExp.get());
+ }
+
+
+ assert(mt.ty == Tstruct || mt.ty == Tclass);
+ auto sym = mt.toDsymbol(sc).isAggregateDeclaration();
+ assert(sym);
+ if (// https://issues.dlang.org/show_bug.cgi?id=22054
+ // if a class or struct does not have a body
+ // there is no point in searching for its members
+ sym.members &&
+ ident != Id.__sizeof &&
+ ident != Id.__xalignof &&
+ ident != Id._init &&
+ ident != Id._mangleof &&
+ ident != Id.stringof &&
+ ident != Id.offsetof &&
+ // https://issues.dlang.org/show_bug.cgi?id=15045
+ // Don't forward special built-in member functions.
+ ident != Id.ctor &&
+ ident != Id.dtor &&
+ ident != Id.__xdtor &&
+ ident != Id.postblit &&
+ ident != Id.__xpostblit)
+ {
+ /* Look for overloaded opDot() to see if we should forward request
+ * to it.
+ */
+ if (auto fd = search_function(sym, Id.opDot))
+ {
+ /* Rewrite e.ident as:
+ * e.opDot().ident
+ */
+ e = build_overload(e.loc, sc, e, null, fd);
+ // @@@DEPRECATED_2.087@@@.
+ e.deprecation("`opDot` is deprecated. Use `alias this`");
+ e = new DotIdExp(e.loc, e, ident);
+ return returnExp(e.expressionSemantic(sc));
+ }
+
+ /* Look for overloaded opDispatch to see if we should forward request
+ * to it.
+ */
+ if (auto fd = search_function(sym, Id.opDispatch))
+ {
+ /* Rewrite e.ident as:
+ * e.opDispatch!("ident")
+ */
+ TemplateDeclaration td = fd.isTemplateDeclaration();
+ if (!td)
+ {
+ fd.error("must be a template `opDispatch(string s)`, not a %s", fd.kind());
+ return returnExp(ErrorExp.get());
+ }
+ auto se = new StringExp(e.loc, ident.toString());
+ auto tiargs = new Objects();
+ tiargs.push(se);
+ auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs);
+ dti.ti.tempdecl = td;
+ /* opDispatch, which doesn't need IFTI, may occur instantiate error.
+ * e.g.
+ * template opDispatch(name) if (isValid!name) { ... }
+ */
+ uint errors = gagError ? global.startGagging() : 0;
+ e = dti.semanticY(sc, 0);
+ if (gagError && global.endGagging(errors))
+ e = null;
+ return returnExp(e);
+ }
+
+ /* See if we should forward to the alias this.
+ */
+ auto alias_e = resolveAliasThis(sc, e, gagError);
+ if (alias_e && alias_e != e)
+ {
+ /* Rewrite e.ident as:
+ * e.aliasthis.ident
+ */
+ auto die = new DotIdExp(e.loc, alias_e, ident);
+
+ auto errors = gagError ? 0 : global.startGagging();
+ auto exp = die.semanticY(sc, gagError);
+ if (!gagError)
+ {
+ global.endGagging(errors);
+ if (exp && exp.op == TOK.error)
+ exp = null;
+ }
+
+ if (exp && gagError)
+ // now that we know that the alias this leads somewhere useful,
+ // go back and print deprecations/warnings that we skipped earlier due to the gag
+ resolveAliasThis(sc, e, false);
+
+ return returnExp(exp);
+ }
+ }
+ return returnExp(visitType(mt));
+ }
+
+ Expression visitStruct(TypeStruct mt)
+ {
+ Dsymbol s;
+ static if (LOGDOTEXP)
+ {
+ printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ assert(e.op != TOK.dot);
+
+ // https://issues.dlang.org/show_bug.cgi?id=14010
+ if (ident == Id._mangleof)
+ {
+ return mt.getProperty(sc, e.loc, ident, flag & 1);
+ }
+
+ /* If e.tupleof
+ */
+ if (ident == Id._tupleof)
+ {
+ /* Create a TupleExp out of the fields of the struct e:
+ * (e.field0, e.field1, e.field2, ...)
+ */
+ e = e.expressionSemantic(sc); // do this before turning on noaccesscheck
+
+ if (!mt.sym.determineFields())
+ {
+ error(e.loc, "unable to determine fields of `%s` because of forward references", mt.toChars());
+ }
+
+ Expression e0;
+ Expression ev = e.op == TOK.type ? null : e;
+ if (ev)
+ ev = extractSideEffect(sc, "__tup", e0, ev);
+
+ auto exps = new Expressions();
+ exps.reserve(mt.sym.fields.dim);
+ for (size_t i = 0; i < mt.sym.fields.dim; i++)
+ {
+ VarDeclaration v = mt.sym.fields[i];
+ Expression ex;
+ if (ev)
+ ex = new DotVarExp(e.loc, ev, v);
+ else
+ {
+ ex = new VarExp(e.loc, v);
+ ex.type = ex.type.addMod(e.type.mod);
+ }
+ exps.push(ex);
+ }
+
+ e = new TupleExp(e.loc, e0, exps);
+ Scope* sc2 = sc.push();
+ sc2.flags |= global.params.useDIP1000 == FeatureState.enabled ? SCOPE.onlysafeaccess : SCOPE.noaccesscheck;
+ e = e.expressionSemantic(sc2);
+ sc2.pop();
+ return e;
+ }
+
+ immutable flags = sc.flags & SCOPE.ignoresymbolvisibility ? IgnoreSymbolVisibility : 0;
+ s = mt.sym.search(e.loc, ident, flags | IgnorePrivateImports);
+ L1:
+ if (!s)
+ {
+ return noMember(mt, sc, e, ident, flag);
+ }
+ if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s))
+ {
+ return noMember(mt, sc, e, ident, flag);
+ }
+ s = s.toAlias();
+
+ if (auto em = s.isEnumMember())
+ {
+ return em.getVarExp(e.loc, sc);
+ }
+ if (auto v = s.isVarDeclaration())
+ {
+ v.checkDeprecated(e.loc, sc);
+ v.checkDisabled(e.loc, sc);
+ if (!v.type ||
+ !v.type.deco && v.inuse)
+ {
+ if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494
+ e.error("circular reference to %s `%s`", v.kind(), v.toPrettyChars());
+ else
+ e.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ if (v.type.ty == Terror)
+ {
+ return ErrorExp.get();
+ }
+
+ if ((v.storage_class & STC.manifest) && v._init)
+ {
+ if (v.inuse)
+ {
+ e.error("circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ checkAccess(e.loc, sc, null, v);
+ Expression ve = new VarExp(e.loc, v);
+ if (!isTrivialExp(e))
+ {
+ ve = new CommaExp(e.loc, e, ve);
+ }
+ return ve.expressionSemantic(sc);
+ }
+ }
+
+ if (auto t = s.getType())
+ {
+ return (new TypeExp(e.loc, t)).expressionSemantic(sc);
+ }
+
+ TemplateMixin tm = s.isTemplateMixin();
+ if (tm)
+ {
+ return new DotExp(e.loc, e, new ScopeExp(e.loc, tm)).expressionSemantic(sc);
+ }
+
+ TemplateDeclaration td = s.isTemplateDeclaration();
+ if (td)
+ {
+ if (e.op == TOK.type)
+ e = new TemplateExp(e.loc, td);
+ else
+ e = new DotTemplateExp(e.loc, e, td);
+ return e.expressionSemantic(sc);
+ }
+
+ TemplateInstance ti = s.isTemplateInstance();
+ if (ti)
+ {
+ if (!ti.semanticRun)
+ {
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors) // if template failed to expand
+ {
+ return ErrorExp.get();
+ }
+ }
+ s = ti.inst.toAlias();
+ if (!s.isTemplateInstance())
+ goto L1;
+ if (e.op == TOK.type)
+ e = new ScopeExp(e.loc, ti);
+ else
+ e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti));
+ return e.expressionSemantic(sc);
+ }
+
+ if (s.isImport() || s.isModule() || s.isPackage())
+ {
+ return symbolToExp(s, e.loc, sc, false);
+ }
+
+ OverloadSet o = s.isOverloadSet();
+ if (o)
+ {
+ auto oe = new OverExp(e.loc, o);
+ if (e.op == TOK.type)
+ {
+ return oe;
+ }
+ return new DotExp(e.loc, e, oe);
+ }
+
+ Declaration d = s.isDeclaration();
+ if (!d)
+ {
+ e.error("`%s.%s` is not a declaration", e.toChars(), ident.toChars());
+ return ErrorExp.get();
+ }
+
+ if (e.op == TOK.type)
+ {
+ /* It's:
+ * Struct.d
+ */
+ if (TupleDeclaration tup = d.isTupleDeclaration())
+ {
+ e = new TupleExp(e.loc, tup);
+ return e.expressionSemantic(sc);
+ }
+ if (d.needThis() && sc.intypeof != 1)
+ {
+ /* Rewrite as:
+ * this.d
+ */
+ if (hasThis(sc))
+ {
+ e = new DotVarExp(e.loc, new ThisExp(e.loc), d);
+ return e.expressionSemantic(sc);
+ }
+ }
+ if (d.semanticRun == PASS.init)
+ d.dsymbolSemantic(null);
+ checkAccess(e.loc, sc, e, d);
+ auto ve = new VarExp(e.loc, d);
+ if (d.isVarDeclaration() && d.needThis())
+ ve.type = d.type.addMod(e.type.mod);
+ return ve;
+ }
+
+ bool unreal = e.op == TOK.variable && (cast(VarExp)e).var.isField();
+ if (d.isDataseg() || unreal && d.isField())
+ {
+ // (e, d)
+ checkAccess(e.loc, sc, e, d);
+ Expression ve = new VarExp(e.loc, d);
+ e = unreal ? ve : new CommaExp(e.loc, e, ve);
+ return e.expressionSemantic(sc);
+ }
+
+ e = new DotVarExp(e.loc, e, d);
+ return e.expressionSemantic(sc);
+ }
+
+ Expression visitEnum(TypeEnum mt)
+ {
+ static if (LOGDOTEXP)
+ {
+ printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e.toChars(), ident.toChars(), mt.toChars());
+ }
+ // https://issues.dlang.org/show_bug.cgi?id=14010
+ if (ident == Id._mangleof)
+ {
+ return mt.getProperty(sc, e.loc, ident, flag & 1);
+ }
+
+ if (mt.sym.semanticRun < PASS.semanticdone)
+ mt.sym.dsymbolSemantic(null);
+
+ Dsymbol s = mt.sym.search(e.loc, ident);
+ if (!s)
+ {
+ if (ident == Id.max || ident == Id.min || ident == Id._init)
+ {
+ return mt.getProperty(sc, e.loc, ident, flag & 1);
+ }
+
+ Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, 1);
+ if (!(flag & 1) && !res)
+ {
+ if (auto ns = mt.sym.search_correct(ident))
+ e.error("no property `%s` for type `%s`. Did you mean `%s.%s` ?", ident.toChars(), mt.toChars(), mt.toChars(),
+ ns.toChars());
+ else
+ e.error("no property `%s` for type `%s`", ident.toChars(),
+ mt.toChars());
+
+ return ErrorExp.get();
+ }
+ return res;
+ }
+ EnumMember m = s.isEnumMember();
+ return m.getVarExp(e.loc, sc);
+ }
+
+ Expression visitClass(TypeClass mt)
+ {
+ Dsymbol s;
+ static if (LOGDOTEXP)
+ {
+ printf("TypeClass::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+ }
+ assert(e.op != TOK.dot);
+
+ // https://issues.dlang.org/show_bug.cgi?id=12543
+ if (ident == Id.__sizeof || ident == Id.__xalignof || ident == Id._mangleof)
+ {
+ return mt.Type.getProperty(sc, e.loc, ident, 0);
+ }
+
+ /* If e.tupleof
+ */
+ if (ident == Id._tupleof)
+ {
+ objc.checkTupleof(e, mt);
+
+ /* Create a TupleExp
+ */
+ e = e.expressionSemantic(sc); // do this before turning on noaccesscheck
+
+ mt.sym.size(e.loc); // do semantic of type
+
+ Expression e0;
+ Expression ev = e.op == TOK.type ? null : e;
+ if (ev)
+ ev = extractSideEffect(sc, "__tup", e0, ev);
+
+ auto exps = new Expressions();
+ exps.reserve(mt.sym.fields.dim);
+ for (size_t i = 0; i < mt.sym.fields.dim; i++)
+ {
+ VarDeclaration v = mt.sym.fields[i];
+ // Don't include hidden 'this' pointer
+ if (v.isThisDeclaration())
+ continue;
+ Expression ex;
+ if (ev)
+ ex = new DotVarExp(e.loc, ev, v);
+ else
+ {
+ ex = new VarExp(e.loc, v);
+ ex.type = ex.type.addMod(e.type.mod);
+ }
+ exps.push(ex);
+ }
+
+ e = new TupleExp(e.loc, e0, exps);
+ Scope* sc2 = sc.push();
+ sc2.flags |= global.params.useDIP1000 == FeatureState.enabled ? SCOPE.onlysafeaccess : SCOPE.noaccesscheck;
+ e = e.expressionSemantic(sc2);
+ sc2.pop();
+ return e;
+ }
+
+ int flags = sc.flags & SCOPE.ignoresymbolvisibility ? IgnoreSymbolVisibility : 0;
+ s = mt.sym.search(e.loc, ident, flags | IgnorePrivateImports);
+
+ L1:
+ if (!s)
+ {
+ // See if it's a 'this' class or a base class
+ if (mt.sym.ident == ident)
+ {
+ if (e.op == TOK.type)
+ {
+ return mt.Type.getProperty(sc, e.loc, ident, 0);
+ }
+ e = new DotTypeExp(e.loc, e, mt.sym);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (auto cbase = mt.sym.searchBase(ident))
+ {
+ if (e.op == TOK.type)
+ {
+ return mt.Type.getProperty(sc, e.loc, ident, 0);
+ }
+ if (auto ifbase = cbase.isInterfaceDeclaration())
+ e = new CastExp(e.loc, e, ifbase.type);
+ else
+ e = new DotTypeExp(e.loc, e, cbase);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ if (ident == Id.classinfo)
+ {
+ if (!Type.typeinfoclass)
+ {
+ error(e.loc, "`object.TypeInfo_Class` could not be found, but is implicitly used");
+ return ErrorExp.get();
+ }
+
+ Type t = Type.typeinfoclass.type;
+ if (e.op == TOK.type || e.op == TOK.dotType)
+ {
+ /* For type.classinfo, we know the classinfo
+ * at compile time.
+ */
+ if (!mt.sym.vclassinfo)
+ mt.sym.vclassinfo = new TypeInfoClassDeclaration(mt.sym.type);
+ e = new VarExp(e.loc, mt.sym.vclassinfo);
+ e = e.addressOf();
+ e.type = t; // do this so we don't get redundant dereference
+ }
+ else
+ {
+ /* For class objects, the classinfo reference is the first
+ * entry in the vtbl[]
+ */
+ e = new PtrExp(e.loc, e);
+ e.type = t.pointerTo();
+ if (mt.sym.isInterfaceDeclaration())
+ {
+ if (mt.sym.isCPPinterface())
+ {
+ /* C++ interface vtbl[]s are different in that the
+ * first entry is always pointer to the first virtual
+ * function, not classinfo.
+ * We can't get a .classinfo for it.
+ */
+ error(e.loc, "no `.classinfo` for C++ interface objects");
+ }
+ /* For an interface, the first entry in the vtbl[]
+ * is actually a pointer to an instance of struct Interface.
+ * The first member of Interface is the .classinfo,
+ * so add an extra pointer indirection.
+ */
+ e.type = e.type.pointerTo();
+ e = new PtrExp(e.loc, e);
+ e.type = t.pointerTo();
+ }
+ e = new PtrExp(e.loc, e, t);
+ }
+ return e;
+ }
+
+ if (ident == Id.__vptr)
+ {
+ /* The pointer to the vtbl[]
+ * *cast(immutable(void*)**)e
+ */
+ e = e.castTo(sc, mt.tvoidptr.immutableOf().pointerTo().pointerTo());
+ e = new PtrExp(e.loc, e);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ if (ident == Id.__monitor && mt.sym.hasMonitor())
+ {
+ /* The handle to the monitor (call it a void*)
+ * *(cast(void**)e + 1)
+ */
+ e = e.castTo(sc, mt.tvoidptr.pointerTo());
+ e = new AddExp(e.loc, e, IntegerExp.literal!1);
+ e = new PtrExp(e.loc, e);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ if (ident == Id.outer && mt.sym.vthis)
+ {
+ if (mt.sym.vthis.semanticRun == PASS.init)
+ mt.sym.vthis.dsymbolSemantic(null);
+
+ if (auto cdp = mt.sym.toParentLocal().isClassDeclaration())
+ {
+ auto dve = new DotVarExp(e.loc, e, mt.sym.vthis);
+ dve.type = cdp.type.addMod(e.type.mod);
+ return dve;
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=15839
+ * Find closest parent class through nested functions.
+ */
+ for (auto p = mt.sym.toParentLocal(); p; p = p.toParentLocal())
+ {
+ auto fd = p.isFuncDeclaration();
+ if (!fd)
+ break;
+ auto ad = fd.isThis();
+ if (!ad && fd.isNested())
+ continue;
+ if (!ad)
+ break;
+ if (auto cdp = ad.isClassDeclaration())
+ {
+ auto ve = new ThisExp(e.loc);
+
+ ve.var = fd.vthis;
+ const nestedError = fd.vthis.checkNestedReference(sc, e.loc);
+ assert(!nestedError);
+
+ ve.type = cdp.type.addMod(fd.vthis.type.mod).addMod(e.type.mod);
+ return ve;
+ }
+ break;
+ }
+
+ // Continue to show enclosing function's frame (stack or closure).
+ auto dve = new DotVarExp(e.loc, e, mt.sym.vthis);
+ dve.type = mt.sym.vthis.type.addMod(e.type.mod);
+ return dve;
+ }
+
+ return noMember(mt, sc, e, ident, flag & 1);
+ }
+ if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s))
+ {
+ return noMember(mt, sc, e, ident, flag);
+ }
+ if (!s.isFuncDeclaration()) // because of overloading
+ {
+ s.checkDeprecated(e.loc, sc);
+ if (auto d = s.isDeclaration())
+ d.checkDisabled(e.loc, sc);
+ }
+ s = s.toAlias();
+
+ if (auto em = s.isEnumMember())
+ {
+ return em.getVarExp(e.loc, sc);
+ }
+ if (auto v = s.isVarDeclaration())
+ {
+ if (!v.type ||
+ !v.type.deco && v.inuse)
+ {
+ if (v.inuse) // https://issues.dlang.org/show_bug.cgi?id=9494
+ e.error("circular reference to %s `%s`", v.kind(), v.toPrettyChars());
+ else
+ e.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ if (v.type.ty == Terror)
+ {
+ return ErrorExp.get();
+ }
+
+ if ((v.storage_class & STC.manifest) && v._init)
+ {
+ if (v.inuse)
+ {
+ e.error("circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
+ return ErrorExp.get();
+ }
+ checkAccess(e.loc, sc, null, v);
+ Expression ve = new VarExp(e.loc, v);
+ ve = ve.expressionSemantic(sc);
+ return ve;
+ }
+ }
+
+ if (auto t = s.getType())
+ {
+ return (new TypeExp(e.loc, t)).expressionSemantic(sc);
+ }
+
+ TemplateMixin tm = s.isTemplateMixin();
+ if (tm)
+ {
+ return new DotExp(e.loc, e, new ScopeExp(e.loc, tm)).expressionSemantic(sc);
+ }
+
+ TemplateDeclaration td = s.isTemplateDeclaration();
+
+ Expression toTemplateExp(TemplateDeclaration td)
+ {
+ if (e.op == TOK.type)
+ e = new TemplateExp(e.loc, td);
+ else
+ e = new DotTemplateExp(e.loc, e, td);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ if (td)
+ {
+ return toTemplateExp(td);
+ }
+
+ TemplateInstance ti = s.isTemplateInstance();
+ if (ti)
+ {
+ if (!ti.semanticRun)
+ {
+ ti.dsymbolSemantic(sc);
+ if (!ti.inst || ti.errors) // if template failed to expand
+ {
+ return ErrorExp.get();
+ }
+ }
+ s = ti.inst.toAlias();
+ if (!s.isTemplateInstance())
+ goto L1;
+ if (e.op == TOK.type)
+ e = new ScopeExp(e.loc, ti);
+ else
+ e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti));
+ return e.expressionSemantic(sc);
+ }
+
+ if (s.isImport() || s.isModule() || s.isPackage())
+ {
+ e = symbolToExp(s, e.loc, sc, false);
+ return e;
+ }
+
+ OverloadSet o = s.isOverloadSet();
+ if (o)
+ {
+ auto oe = new OverExp(e.loc, o);
+ if (e.op == TOK.type)
+ {
+ return oe;
+ }
+ return new DotExp(e.loc, e, oe);
+ }
+
+ Declaration d = s.isDeclaration();
+ if (!d)
+ {
+ e.error("`%s.%s` is not a declaration", e.toChars(), ident.toChars());
+ return ErrorExp.get();
+ }
+
+ if (e.op == TOK.type)
+ {
+ /* It's:
+ * Class.d
+ */
+ if (TupleDeclaration tup = d.isTupleDeclaration())
+ {
+ e = new TupleExp(e.loc, tup);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ if (mt.sym.classKind == ClassKind.objc
+ && d.isFuncDeclaration()
+ && d.isFuncDeclaration().isStatic
+ && d.isFuncDeclaration().objc.selector)
+ {
+ auto classRef = new ObjcClassReferenceExp(e.loc, mt.sym);
+ return new DotVarExp(e.loc, classRef, d).expressionSemantic(sc);
+ }
+ else if (d.needThis() && sc.intypeof != 1)
+ {
+ /* Rewrite as:
+ * this.d
+ */
+ AggregateDeclaration ad = d.isMemberLocal();
+ if (auto f = hasThis(sc))
+ {
+ // This is almost same as getRightThis() in expressionsem.d
+ Expression e1;
+ Type t;
+ /* returns: true to continue, false to return */
+ if (f.isThis2)
+ {
+ if (f.followInstantiationContext(ad))
+ {
+ e1 = new VarExp(e.loc, f.vthis);
+ e1 = new PtrExp(e1.loc, e1);
+ e1 = new IndexExp(e1.loc, e1, IntegerExp.literal!1);
+ auto pd = f.toParent2().isDeclaration();
+ assert(pd);
+ t = pd.type.toBasetype();
+ e1 = getThisSkipNestedFuncs(e1.loc, sc, f.toParent2(), ad, e1, t, d, true);
+ if (!e1)
+ {
+ e = new VarExp(e.loc, d);
+ return e;
+ }
+ goto L2;
+ }
+ }
+ e1 = new ThisExp(e.loc);
+ e1 = e1.expressionSemantic(sc);
+ L2:
+ t = e1.type.toBasetype();
+ ClassDeclaration cd = e.type.isClassHandle();
+ ClassDeclaration tcd = t.isClassHandle();
+ if (cd && tcd && (tcd == cd || cd.isBaseOf(tcd, null)))
+ {
+ e = new DotTypeExp(e1.loc, e1, cd);
+ e = new DotVarExp(e.loc, e, d);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+ if (tcd && tcd.isNested())
+ {
+ /* e1 is the 'this' pointer for an inner class: tcd.
+ * Rewrite it as the 'this' pointer for the outer class.
+ */
+ auto vthis = tcd.followInstantiationContext(ad) ? tcd.vthis2 : tcd.vthis;
+ e1 = new DotVarExp(e.loc, e1, vthis);
+ e1.type = vthis.type;
+ e1.type = e1.type.addMod(t.mod);
+ // Do not call ensureStaticLinkTo()
+ //e1 = e1.expressionSemantic(sc);
+
+ // Skip up over nested functions, and get the enclosing
+ // class type.
+ e1 = getThisSkipNestedFuncs(e1.loc, sc, tcd.toParentP(ad), ad, e1, t, d, true);
+ if (!e1)
+ {
+ e = new VarExp(e.loc, d);
+ return e;
+ }
+ goto L2;
+ }
+ }
+ }
+ //printf("e = %s, d = %s\n", e.toChars(), d.toChars());
+ if (d.semanticRun == PASS.init)
+ d.dsymbolSemantic(null);
+
+ // If static function, get the most visible overload.
+ // Later on the call is checked for correctness.
+ // https://issues.dlang.org/show_bug.cgi?id=12511
+ Dsymbol d2 = d;
+ if (auto fd = d.isFuncDeclaration())
+ {
+ import dmd.access : mostVisibleOverload;
+ d2 = mostVisibleOverload(fd, sc._module);
+ }
+
+ checkAccess(e.loc, sc, e, d2);
+ if (d2.isDeclaration())
+ {
+ d = cast(Declaration)d2;
+ auto ve = new VarExp(e.loc, d);
+ if (d.isVarDeclaration() && d.needThis())
+ ve.type = d.type.addMod(e.type.mod);
+ return ve;
+ }
+ else if (d2.isTemplateDeclaration())
+ {
+ return toTemplateExp(cast(TemplateDeclaration)d2);
+ }
+ else
+ assert(0);
+ }
+
+ bool unreal = e.op == TOK.variable && (cast(VarExp)e).var.isField();
+ if (d.isDataseg() || unreal && d.isField())
+ {
+ // (e, d)
+ checkAccess(e.loc, sc, e, d);
+ Expression ve = new VarExp(e.loc, d);
+ e = unreal ? ve : new CommaExp(e.loc, e, ve);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ e = new DotVarExp(e.loc, e, d);
+ e = e.expressionSemantic(sc);
+ return e;
+ }
+
+ switch (mt.ty)
+ {
+ case Tvector: return visitVector (cast(TypeVector)mt);
+ case Tsarray: return visitSArray (cast(TypeSArray)mt);
+ case Tstruct: return visitStruct (cast(TypeStruct)mt);
+ case Tenum: return visitEnum (cast(TypeEnum)mt);
+ case Terror: return visitError (cast(TypeError)mt);
+ case Tarray: return visitDArray (cast(TypeDArray)mt);
+ case Taarray: return visitAArray (cast(TypeAArray)mt);
+ case Treference: return visitReference(cast(TypeReference)mt);
+ case Tdelegate: return visitDelegate (cast(TypeDelegate)mt);
+ case Tclass: return visitClass (cast(TypeClass)mt);
+
+ default: return mt.isTypeBasic()
+ ? visitBasic(cast(TypeBasic)mt)
+ : visitType(mt);
+ }
+}
+
+
+/************************
+ * Get the the default initialization expression for a type.
+ * Params:
+ * mt = the type for which the init expression is returned
+ * loc = the location where the expression needs to be evaluated
+ *
+ * Returns:
+ * The initialization expression for the type.
+ */
+Expression defaultInit(Type mt, const ref Loc loc)
+{
+ Expression visitBasic(TypeBasic mt)
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeBasic::defaultInit() '%s'\n", mt.toChars());
+ }
+ dinteger_t value = 0;
+
+ switch (mt.ty)
+ {
+ case Tchar:
+ value = 0xFF;
+ break;
+
+ case Twchar:
+ case Tdchar:
+ value = 0xFFFF;
+ break;
+
+ case Timaginary32:
+ case Timaginary64:
+ case Timaginary80:
+ case Tfloat32:
+ case Tfloat64:
+ case Tfloat80:
+ return new RealExp(loc, target.RealProperties.nan, mt);
+
+ case Tcomplex32:
+ case Tcomplex64:
+ case Tcomplex80:
+ {
+ // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN).
+ const cvalue = complex_t(target.RealProperties.nan, target.RealProperties.nan);
+ return new ComplexExp(loc, cvalue, mt);
+ }
+
+ case Tvoid:
+ error(loc, "`void` does not have a default initializer");
+ return ErrorExp.get();
+
+ default:
+ break;
+ }
+ return new IntegerExp(loc, value, mt);
+ }
+
+ Expression visitVector(TypeVector mt)
+ {
+ //printf("TypeVector::defaultInit()\n");
+ assert(mt.basetype.ty == Tsarray);
+ Expression e = mt.basetype.defaultInit(loc);
+ auto ve = new VectorExp(loc, e, mt);
+ ve.type = mt;
+ ve.dim = cast(int)(mt.basetype.size(loc) / mt.elementType().size(loc));
+ return ve;
+ }
+
+ Expression visitSArray(TypeSArray mt)
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeSArray::defaultInit() '%s'\n", mt.toChars());
+ }
+ if (mt.next.ty == Tvoid)
+ return mt.tuns8.defaultInit(loc);
+ else
+ return mt.next.defaultInit(loc);
+ }
+
+ Expression visitFunction(TypeFunction mt)
+ {
+ error(loc, "`function` does not have a default initializer");
+ return ErrorExp.get();
+ }
+
+ Expression visitStruct(TypeStruct mt)
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeStruct::defaultInit() '%s'\n", mt.toChars());
+ }
+ Declaration d = new SymbolDeclaration(mt.sym.loc, mt.sym);
+ assert(d);
+ d.type = mt;
+ d.storage_class |= STC.rvalue; // https://issues.dlang.org/show_bug.cgi?id=14398
+ return new VarExp(mt.sym.loc, d);
+ }
+
+ Expression visitEnum(TypeEnum mt)
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeEnum::defaultInit() '%s'\n", mt.toChars());
+ }
+ // Initialize to first member of enum
+ Expression e = mt.sym.getDefaultValue(loc);
+ e = e.copy();
+ e.loc = loc;
+ e.type = mt; // to deal with const, immutable, etc., variants
+ return e;
+ }
+
+ Expression visitTuple(TypeTuple mt)
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeTuple::defaultInit() '%s'\n", mt.toChars());
+ }
+ auto exps = new Expressions(mt.arguments.dim);
+ for (size_t i = 0; i < mt.arguments.dim; i++)
+ {
+ Parameter p = (*mt.arguments)[i];
+ assert(p.type);
+ Expression e = p.type.defaultInitLiteral(loc);
+ if (e.op == TOK.error)
+ {
+ return e;
+ }
+ (*exps)[i] = e;
+ }
+ return new TupleExp(loc, exps);
+ }
+
+ Expression visitNoreturn(TypeNoreturn mt)
+ {
+ static if (LOGDEFAULTINIT)
+ {
+ printf("TypeNoreturn::defaultInit() '%s'\n", mt.toChars());
+ }
+ auto cond = IntegerExp.createBool(false);
+ auto msg = new StringExp(loc, "Accessed expression of type `noreturn`");
+ auto ae = new AssertExp(loc, cond, msg);
+ ae.type = mt;
+ return ae;
+ }
+
+ switch (mt.ty)
+ {
+ case Tvector: return visitVector (cast(TypeVector)mt);
+ case Tsarray: return visitSArray (cast(TypeSArray)mt);
+ case Tfunction: return visitFunction(cast(TypeFunction)mt);
+ case Tstruct: return visitStruct (cast(TypeStruct)mt);
+ case Tenum: return visitEnum (cast(TypeEnum)mt);
+ case Ttuple: return visitTuple (cast(TypeTuple)mt);
+
+ case Tnull: return new NullExp(Loc.initial, Type.tnull);
+
+ case Terror: return ErrorExp.get();
+
+ case Tarray:
+ case Taarray:
+ case Tpointer:
+ case Treference:
+ case Tdelegate:
+ case Tclass: return new NullExp(loc, mt);
+ case Tnoreturn: return visitNoreturn(cast(TypeNoreturn) mt);
+
+ default: return mt.isTypeBasic() ?
+ visitBasic(cast(TypeBasic)mt) :
+ null;
+ }
+}
+
+
+/******************************
+ * Get the value of the .max/.min property of `ed` as an Expression.
+ * Lazily computes the value and caches it in maxval/minval.
+ * Reports any errors.
+ * Params:
+ * ed = the EnumDeclaration being examined
+ * loc = location to use for error messages
+ * id = Id::max or Id::min
+ * Returns:
+ * corresponding value of .max/.min
+ */
+private Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id)
+{
+ //printf("EnumDeclaration::getMaxValue()\n");
+
+ static Expression pvalToResult(Expression e, const ref Loc loc)
+ {
+ if (e.op != TOK.error)
+ {
+ e = e.copy();
+ e.loc = loc;
+ }
+ return e;
+ }
+
+ Expression* pval = (id == Id.max) ? &ed.maxval : &ed.minval;
+
+ Expression errorReturn()
+ {
+ *pval = ErrorExp.get();
+ return *pval;
+ }
+
+ if (ed.inuse)
+ {
+ ed.error(loc, "recursive definition of `.%s` property", id.toChars());
+ return errorReturn();
+ }
+ if (*pval)
+ return pvalToResult(*pval, loc);
+
+ if (ed._scope)
+ dsymbolSemantic(ed, ed._scope);
+ if (ed.errors)
+ return errorReturn();
+ if (!ed.members)
+ {
+ if (ed.isSpecial())
+ {
+ /* Allow these special enums to not need a member list
+ */
+ return ed.memtype.getProperty(ed._scope, loc, id, 0);
+ }
+
+ ed.error(loc, "is opaque and has no `.%s`", id.toChars());
+ return errorReturn();
+ }
+ if (!(ed.memtype && ed.memtype.isintegral()))
+ {
+ ed.error(loc, "has no `.%s` property because base type `%s` is not an integral type",
+ id.toChars(), ed.memtype ? ed.memtype.toChars() : "");
+ return errorReturn();
+ }
+
+ bool first = true;
+ for (size_t i = 0; i < ed.members.dim; i++)
+ {
+ EnumMember em = (*ed.members)[i].isEnumMember();
+ if (!em)
+ continue;
+ if (em.errors)
+ {
+ ed.errors = true;
+ continue;
+ }
+
+ if (em.semanticRun < PASS.semanticdone)
+ {
+ em.error("is forward referenced looking for `.%s`", id.toChars());
+ ed.errors = true;
+ continue;
+ }
+
+ if (first)
+ {
+ *pval = em.value;
+ first = false;
+ }
+ else
+ {
+ /* In order to work successfully with UDTs,
+ * build expressions to do the comparisons,
+ * and let the semantic analyzer and constant
+ * folder give us the result.
+ */
+
+ /* Compute:
+ * if (e > maxval)
+ * maxval = e;
+ */
+ Expression e = em.value;
+ Expression ec = new CmpExp(id == Id.max ? TOK.greaterThan : TOK.lessThan, em.loc, e, *pval);
+ ed.inuse++;
+ ec = ec.expressionSemantic(em._scope);
+ ed.inuse--;
+ ec = ec.ctfeInterpret();
+ if (ec.op == TOK.error)
+ {
+ ed.errors = true;
+ continue;
+ }
+ if (ec.toInteger())
+ *pval = e;
+ }
+ }
+ return ed.errors ? errorReturn() : pvalToResult(*pval, loc);
+}
diff --git a/gcc/d/dmd/typinf.d b/gcc/d/dmd/typinf.d
new file mode 100644
index 00000000000..d8160f0d633
--- /dev/null
+++ b/gcc/d/dmd/typinf.d
@@ -0,0 +1,28 @@
+/**
+ * Generate `TypeInfo` objects, which are needed for run-time introspection of classes.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/typeinf.d, _typeinf.d)
+ * Documentation: https://dlang.org/phobos/dmd_typinf.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typinf.d
+ */
+
+module dmd.typinf;
+
+import dmd.dscope;
+import dmd.globals;
+import dmd.mtype;
+
+/****************************************************
+ * Gets the type of the `TypeInfo` object associated with `t`
+ * Params:
+ * loc = the location for reporting line nunbers in errors
+ * t = the type to get the type of the `TypeInfo` object for
+ * sc = the scope
+ * Returns:
+ * The type of the `TypeInfo` object associated with `t`
+ */
+extern (C++) Type getTypeInfoType(Loc loc, Type t, Scope* sc);
+
diff --git a/gcc/d/dmd/utf.c b/gcc/d/dmd/utf.c
deleted file mode 100644
index f6b543590f2..00000000000
--- a/gcc/d/dmd/utf.c
+++ /dev/null
@@ -1,306 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 2003-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/D-Programming-Language/dmd/blob/master/src/utf.c
- */
-
-/// Description of UTF-8 in [1]. Unicode non-characters and private-use
-/// code points described in [2],[4].
-///
-/// References:
-/// [1] http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-/// [2] http://en.wikipedia.org/wiki/Unicode
-/// [3] http://unicode.org/faq/utf_bom.html
-/// [4] http://www.unicode.org/versions/Unicode6.1.0/ch03.pdf
-
-#include "utf.h"
-
-/* The following encodings are valid, except for the 5 and 6 byte
- * combinations:
- * 0xxxxxxx
- * 110xxxxx 10xxxxxx
- * 1110xxxx 10xxxxxx 10xxxxxx
- * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- */
-const unsigned UTF8_STRIDE[256] =
-{
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
- 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0xFF,0xFF,
-};
-
-// UTF-8 decoding errors
-char const UTF8_DECODE_OUTSIDE_CODE_SPACE[] = "Outside Unicode code space";
-char const UTF8_DECODE_TRUNCATED_SEQUENCE[] = "Truncated UTF-8 sequence";
-char const UTF8_DECODE_OVERLONG[] = "Overlong UTF-8 sequence";
-char const UTF8_DECODE_INVALID_TRAILER[] = "Invalid trailing code unit";
-char const UTF8_DECODE_INVALID_CODE_POINT[] = "Invalid code point decoded";
-
-// UTF-16 decoding errors
-char const UTF16_DECODE_TRUNCATED_SEQUENCE[]= "Truncated UTF-16 sequence";
-char const UTF16_DECODE_INVALID_SURROGATE[] = "Invalid low surrogate";
-char const UTF16_DECODE_UNPAIRED_SURROGATE[]= "Unpaired surrogate";
-char const UTF16_DECODE_INVALID_CODE_POINT[]= "Invalid code point decoded";
-
-/// The Unicode code space is the range of code points [0x000000,0x10FFFF]
-/// except the UTF-16 surrogate pairs in the range [0xD800,0xDFFF]
-/// and non-characters (which end in 0xFFFE or 0xFFFF).
-bool utf_isValidDchar(dchar_t c)
-{
- // TODO: Whether non-char code points should be rejected is pending review
- // largest character code point
- if (c > 0x10FFFF)
- return false;
- // surrogate pairs
- if (0xD800 <= c && c <= 0xDFFF)
- return false;
- // non-characters
- if ((c & 0xFFFFFE) == 0x00FFFE)
- return false;
- return true;
-}
-
-/*******************************
- * Return !=0 if unicode alpha.
- * Use table from C99 Appendix D.
- */
-
-bool isUniAlpha(dchar_t c)
-{
- size_t high = ALPHA_TABLE_LENGTH - 1;
- // Shortcut search if c is out of range
- size_t low
- = (c < ALPHA_TABLE[0][0] || ALPHA_TABLE[high][1] < c) ? high + 1 : 0;
- // Binary search
- while (low <= high)
- {
- size_t mid = (low + high) >> 1;
- if (c < ALPHA_TABLE[mid][0])
- high = mid - 1;
- else if (ALPHA_TABLE[mid][1] < c)
- low = mid + 1;
- else
- {
- assert(ALPHA_TABLE[mid][0] <= c && c <= ALPHA_TABLE[mid][1]);
- return true;
- }
- }
- return false;
-}
-
-/**
- * Returns the code length of c in code units.
- */
-
-int utf_codeLengthChar(dchar_t c)
-{
- if (c <= 0x7F)
- return 1;
- if (c <= 0x7FF)
- return 2;
- if (c <= 0xFFFF)
- return 3;
- if (c <= 0x10FFFF)
- return 4;
- assert(false);
- return 6;
-}
-
-int utf_codeLengthWchar(dchar_t c)
-{
- return c <= 0xFFFF ? 1 : 2;
-}
-
-/**
- * Returns the code length of c in code units for the encoding.
- * sz is the encoding: 1 = utf8, 2 = utf16, 4 = utf32.
- */
-
-int utf_codeLength(int sz, dchar_t c)
-{
- if (sz == 1)
- return utf_codeLengthChar(c);
- if (sz == 2)
- return utf_codeLengthWchar(c);
- assert(sz == 4);
- return 1;
-}
-
-void utf_encodeChar(utf8_t *s, dchar_t c)
-{
- assert(s != NULL);
- assert(utf_isValidDchar(c));
- if (c <= 0x7F)
- {
- s[0] = static_cast<utf8_t>(c);
- }
- else if (c <= 0x07FF)
- {
- s[0] = static_cast<utf8_t>(0xC0 | (c >> 6));
- s[1] = static_cast<utf8_t>(0x80 | (c & 0x3F));
- }
- else if (c <= 0xFFFF)
- {
- s[0] = static_cast<utf8_t>(0xE0 | (c >> 12));
- s[1] = static_cast<utf8_t>(0x80 | ((c >> 6) & 0x3F));
- s[2] = static_cast<utf8_t>(0x80 | (c & 0x3F));
- }
- else if (c <= 0x10FFFF)
- {
- s[0] = static_cast<utf8_t>(0xF0 | (c >> 18));
- s[1] = static_cast<utf8_t>(0x80 | ((c >> 12) & 0x3F));
- s[2] = static_cast<utf8_t>(0x80 | ((c >> 6) & 0x3F));
- s[3] = static_cast<utf8_t>(0x80 | (c & 0x3F));
- }
- else
- assert(0);
-}
-
-void utf_encodeWchar(utf16_t *s, dchar_t c)
-{
- assert(s != NULL);
- assert(utf_isValidDchar(c));
- if (c <= 0xFFFF)
- {
- s[0] = static_cast<utf16_t>(c);
- }
- else
- {
- s[0] = static_cast<utf16_t>((((c - 0x010000) >> 10) & 0x03FF) + 0xD800);
- s[1] = static_cast<utf16_t>(((c - 0x010000) & 0x03FF) + 0xDC00);
- }
-}
-
-void utf_encode(int sz, void *s, dchar_t c)
-{
- if (sz == 1)
- utf_encodeChar((utf8_t *)s, c);
- else if (sz == 2)
- utf_encodeWchar((utf16_t *)s, c);
- else
- {
- assert(sz == 4);
- *((utf32_t *)s) = c;
- }
-}
-
-/********************************************
- * Decode a UTF-8 sequence as a single UTF-32 code point.
- * Returns:
- * NULL success
- * !=NULL error message string
- */
-
-const char *utf_decodeChar(utf8_t const *s, size_t len, size_t *pidx, dchar_t *presult)
-{
- assert(s != NULL);
- assert(pidx != NULL);
- assert(presult != NULL);
- size_t i = (*pidx)++;
- assert(i < len);
- utf8_t u = s[i];
- // Pre-stage results for ASCII and error cases
- *presult = u;
-
- //printf("utf_decodeChar(s = %02x, %02x, %02x len = %d)\n", u, s[1], s[2], len);
-
- // Get expected sequence length
- size_t n = UTF8_STRIDE[u];
- switch (n)
- {
- case 1: // ASCII
- return UTF8_DECODE_OK;
- case 2: case 3: case 4: // multi-byte UTF-8
- break;
- default: // 5- or 6-byte sequence
- return UTF8_DECODE_OUTSIDE_CODE_SPACE;
- }
- if (len < i + n) // source too short
- return UTF8_DECODE_TRUNCATED_SEQUENCE;
-
- // Pick off 7 - n low bits from first code unit
- utf32_t c = u & ((1 << (7 - n)) - 1);
- /* The following combinations are overlong, and illegal:
- * 1100000x (10xxxxxx)
- * 11100000 100xxxxx (10xxxxxx)
- * 11110000 1000xxxx (10xxxxxx 10xxxxxx)
- * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx)
- * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)
- */
- utf8_t u2 = s[++i];
- // overlong combination
- if ((u & 0xFE) == 0xC0 ||
- (u == 0xE0 && (u2 & 0xE0) == 0x80) ||
- (u == 0xF0 && (u2 & 0xF0) == 0x80) ||
- (u == 0xF8 && (u2 & 0xF8) == 0x80) ||
- (u == 0xFC && (u2 & 0xFC) == 0x80))
- return UTF8_DECODE_OVERLONG;
- // Decode remaining bits
- for (n += i - 1; i != n; ++i)
- {
- u = s[i];
- if ((u & 0xC0) != 0x80) // trailing bytes are 10xxxxxx
- return UTF8_DECODE_INVALID_TRAILER;
- c = (c << 6) | (u & 0x3F);
- }
- if (!utf_isValidDchar(c))
- return UTF8_DECODE_INVALID_CODE_POINT;
- *pidx = i;
- *presult = c;
- return UTF8_DECODE_OK;
-}
-
-/********************************************
- * Decode a UTF-16 sequence as a single UTF-32 code point.
- * Returns:
- * NULL success
- * !=NULL error message string
- */
-
-const char *utf_decodeWchar(utf16_t const *s, size_t len, size_t *pidx, dchar_t *presult)
-{
- assert(s != NULL);
- assert(pidx != NULL);
- assert(presult != NULL);
- size_t i = (*pidx)++;
- assert(i < len);
- // Pre-stage results for ASCII and error cases
- utf32_t u = *presult = s[i];
-
- if (u < 0x80) // ASCII
- return UTF16_DECODE_OK;
- if (0xD800 <= u && u <= 0xDBFF) // Surrogate pair
- { if (len <= i + 1)
- return UTF16_DECODE_TRUNCATED_SEQUENCE;
- utf16_t u2 = s[i + 1];
- if (u2 < 0xDC00 || 0xDFFF < u)
- return UTF16_DECODE_INVALID_SURROGATE;
- u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00);
- ++*pidx;
- }
- else if (0xDC00 <= u && u <= 0xDFFF)
- return UTF16_DECODE_UNPAIRED_SURROGATE;
- if (!utf_isValidDchar(u))
- return UTF16_DECODE_INVALID_CODE_POINT;
- *presult = u;
- return UTF16_DECODE_OK;
-}
diff --git a/gcc/d/dmd/utf.d b/gcc/d/dmd/utf.d
new file mode 100644
index 00000000000..1125c212122
--- /dev/null
+++ b/gcc/d/dmd/utf.d
@@ -0,0 +1,561 @@
+/**
+ * Functions related to UTF encoding.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/utf.d, _utf.d)
+ * Documentation: https://dlang.org/phobos/dmd_utf.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/utf.d
+ */
+
+module dmd.utf;
+
+nothrow pure @nogc:
+
+/// The Unicode code space is the range of code points [0x000000,0x10FFFF]
+/// except the UTF-16 surrogate pairs in the range [0xD800,0xDFFF]
+bool utf_isValidDchar(dchar c)
+{
+ // TODO: Whether non-char code points should be rejected is pending review.
+ // 0xFFFE and 0xFFFF are valid for internal use, like Phobos std.utf.isValidDChar
+ // See also https://issues.dlang.org/show_bug.cgi?id=1357
+ if (c < 0xD800) // Almost all characters in a typical document.
+ return true;
+ if (c > 0xDFFF && c <= 0x10FFFF)
+ return true;
+ return false;
+}
+
+/*******************************
+ * Return !=0 if unicode alpha.
+ * Use table from C99 Appendix D.
+ */
+bool isUniAlpha(dchar c)
+{
+ static immutable wchar[2][] ALPHA_TABLE =
+ [
+ [0x00AA, 0x00AA],
+ [0x00B5, 0x00B5],
+ [0x00B7, 0x00B7],
+ [0x00BA, 0x00BA],
+ [0x00C0, 0x00D6],
+ [0x00D8, 0x00F6],
+ [0x00F8, 0x01F5],
+ [0x01FA, 0x0217],
+ [0x0250, 0x02A8],
+ [0x02B0, 0x02B8],
+ [0x02BB, 0x02BB],
+ [0x02BD, 0x02C1],
+ [0x02D0, 0x02D1],
+ [0x02E0, 0x02E4],
+ [0x037A, 0x037A],
+ [0x0386, 0x0386],
+ [0x0388, 0x038A],
+ [0x038C, 0x038C],
+ [0x038E, 0x03A1],
+ [0x03A3, 0x03CE],
+ [0x03D0, 0x03D6],
+ [0x03DA, 0x03DA],
+ [0x03DC, 0x03DC],
+ [0x03DE, 0x03DE],
+ [0x03E0, 0x03E0],
+ [0x03E2, 0x03F3],
+ [0x0401, 0x040C],
+ [0x040E, 0x044F],
+ [0x0451, 0x045C],
+ [0x045E, 0x0481],
+ [0x0490, 0x04C4],
+ [0x04C7, 0x04C8],
+ [0x04CB, 0x04CC],
+ [0x04D0, 0x04EB],
+ [0x04EE, 0x04F5],
+ [0x04F8, 0x04F9],
+ [0x0531, 0x0556],
+ [0x0559, 0x0559],
+ [0x0561, 0x0587],
+ [0x05B0, 0x05B9],
+ [0x05BB, 0x05BD],
+ [0x05BF, 0x05BF],
+ [0x05C1, 0x05C2],
+ [0x05D0, 0x05EA],
+ [0x05F0, 0x05F2],
+ [0x0621, 0x063A],
+ [0x0640, 0x0652],
+ [0x0660, 0x0669],
+ [0x0670, 0x06B7],
+ [0x06BA, 0x06BE],
+ [0x06C0, 0x06CE],
+ [0x06D0, 0x06DC],
+ [0x06E5, 0x06E8],
+ [0x06EA, 0x06ED],
+ [0x06F0, 0x06F9],
+ [0x0901, 0x0903],
+ [0x0905, 0x0939],
+ [0x093D, 0x094D],
+ [0x0950, 0x0952],
+ [0x0958, 0x0963],
+ [0x0966, 0x096F],
+ [0x0981, 0x0983],
+ [0x0985, 0x098C],
+ [0x098F, 0x0990],
+ [0x0993, 0x09A8],
+ [0x09AA, 0x09B0],
+ [0x09B2, 0x09B2],
+ [0x09B6, 0x09B9],
+ [0x09BE, 0x09C4],
+ [0x09C7, 0x09C8],
+ [0x09CB, 0x09CD],
+ [0x09DC, 0x09DD],
+ [0x09DF, 0x09E3],
+ [0x09E6, 0x09F1],
+ [0x0A02, 0x0A02],
+ [0x0A05, 0x0A0A],
+ [0x0A0F, 0x0A10],
+ [0x0A13, 0x0A28],
+ [0x0A2A, 0x0A30],
+ [0x0A32, 0x0A33],
+ [0x0A35, 0x0A36],
+ [0x0A38, 0x0A39],
+ [0x0A3E, 0x0A42],
+ [0x0A47, 0x0A48],
+ [0x0A4B, 0x0A4D],
+ [0x0A59, 0x0A5C],
+ [0x0A5E, 0x0A5E],
+ [0x0A66, 0x0A6F],
+ [0x0A74, 0x0A74],
+ [0x0A81, 0x0A83],
+ [0x0A85, 0x0A8B],
+ [0x0A8D, 0x0A8D],
+ [0x0A8F, 0x0A91],
+ [0x0A93, 0x0AA8],
+ [0x0AAA, 0x0AB0],
+ [0x0AB2, 0x0AB3],
+ [0x0AB5, 0x0AB9],
+ [0x0ABD, 0x0AC5],
+ [0x0AC7, 0x0AC9],
+ [0x0ACB, 0x0ACD],
+ [0x0AD0, 0x0AD0],
+ [0x0AE0, 0x0AE0],
+ [0x0AE6, 0x0AEF],
+ [0x0B01, 0x0B03],
+ [0x0B05, 0x0B0C],
+ [0x0B0F, 0x0B10],
+ [0x0B13, 0x0B28],
+ [0x0B2A, 0x0B30],
+ [0x0B32, 0x0B33],
+ [0x0B36, 0x0B39],
+ [0x0B3D, 0x0B43],
+ [0x0B47, 0x0B48],
+ [0x0B4B, 0x0B4D],
+ [0x0B5C, 0x0B5D],
+ [0x0B5F, 0x0B61],
+ [0x0B66, 0x0B6F],
+ [0x0B82, 0x0B83],
+ [0x0B85, 0x0B8A],
+ [0x0B8E, 0x0B90],
+ [0x0B92, 0x0B95],
+ [0x0B99, 0x0B9A],
+ [0x0B9C, 0x0B9C],
+ [0x0B9E, 0x0B9F],
+ [0x0BA3, 0x0BA4],
+ [0x0BA8, 0x0BAA],
+ [0x0BAE, 0x0BB5],
+ [0x0BB7, 0x0BB9],
+ [0x0BBE, 0x0BC2],
+ [0x0BC6, 0x0BC8],
+ [0x0BCA, 0x0BCD],
+ [0x0BE7, 0x0BEF],
+ [0x0C01, 0x0C03],
+ [0x0C05, 0x0C0C],
+ [0x0C0E, 0x0C10],
+ [0x0C12, 0x0C28],
+ [0x0C2A, 0x0C33],
+ [0x0C35, 0x0C39],
+ [0x0C3E, 0x0C44],
+ [0x0C46, 0x0C48],
+ [0x0C4A, 0x0C4D],
+ [0x0C60, 0x0C61],
+ [0x0C66, 0x0C6F],
+ [0x0C82, 0x0C83],
+ [0x0C85, 0x0C8C],
+ [0x0C8E, 0x0C90],
+ [0x0C92, 0x0CA8],
+ [0x0CAA, 0x0CB3],
+ [0x0CB5, 0x0CB9],
+ [0x0CBE, 0x0CC4],
+ [0x0CC6, 0x0CC8],
+ [0x0CCA, 0x0CCD],
+ [0x0CDE, 0x0CDE],
+ [0x0CE0, 0x0CE1],
+ [0x0CE6, 0x0CEF],
+ [0x0D02, 0x0D03],
+ [0x0D05, 0x0D0C],
+ [0x0D0E, 0x0D10],
+ [0x0D12, 0x0D28],
+ [0x0D2A, 0x0D39],
+ [0x0D3E, 0x0D43],
+ [0x0D46, 0x0D48],
+ [0x0D4A, 0x0D4D],
+ [0x0D60, 0x0D61],
+ [0x0D66, 0x0D6F],
+ [0x0E01, 0x0E3A],
+ [0x0E40, 0x0E5B],
+ [0x0E81, 0x0E82],
+ [0x0E84, 0x0E84],
+ [0x0E87, 0x0E88],
+ [0x0E8A, 0x0E8A],
+ [0x0E8D, 0x0E8D],
+ [0x0E94, 0x0E97],
+ [0x0E99, 0x0E9F],
+ [0x0EA1, 0x0EA3],
+ [0x0EA5, 0x0EA5],
+ [0x0EA7, 0x0EA7],
+ [0x0EAA, 0x0EAB],
+ [0x0EAD, 0x0EAE],
+ [0x0EB0, 0x0EB9],
+ [0x0EBB, 0x0EBD],
+ [0x0EC0, 0x0EC4],
+ [0x0EC6, 0x0EC6],
+ [0x0EC8, 0x0ECD],
+ [0x0ED0, 0x0ED9],
+ [0x0EDC, 0x0EDD],
+ [0x0F00, 0x0F00],
+ [0x0F18, 0x0F19],
+ [0x0F20, 0x0F33],
+ [0x0F35, 0x0F35],
+ [0x0F37, 0x0F37],
+ [0x0F39, 0x0F39],
+ [0x0F3E, 0x0F47],
+ [0x0F49, 0x0F69],
+ [0x0F71, 0x0F84],
+ [0x0F86, 0x0F8B],
+ [0x0F90, 0x0F95],
+ [0x0F97, 0x0F97],
+ [0x0F99, 0x0FAD],
+ [0x0FB1, 0x0FB7],
+ [0x0FB9, 0x0FB9],
+ [0x10A0, 0x10C5],
+ [0x10D0, 0x10F6],
+ [0x1E00, 0x1E9B],
+ [0x1EA0, 0x1EF9],
+ [0x1F00, 0x1F15],
+ [0x1F18, 0x1F1D],
+ [0x1F20, 0x1F45],
+ [0x1F48, 0x1F4D],
+ [0x1F50, 0x1F57],
+ [0x1F59, 0x1F59],
+ [0x1F5B, 0x1F5B],
+ [0x1F5D, 0x1F5D],
+ [0x1F5F, 0x1F7D],
+ [0x1F80, 0x1FB4],
+ [0x1FB6, 0x1FBC],
+ [0x1FBE, 0x1FBE],
+ [0x1FC2, 0x1FC4],
+ [0x1FC6, 0x1FCC],
+ [0x1FD0, 0x1FD3],
+ [0x1FD6, 0x1FDB],
+ [0x1FE0, 0x1FEC],
+ [0x1FF2, 0x1FF4],
+ [0x1FF6, 0x1FFC],
+ [0x203F, 0x2040],
+ [0x207F, 0x207F],
+ [0x2102, 0x2102],
+ [0x2107, 0x2107],
+ [0x210A, 0x2113],
+ [0x2115, 0x2115],
+ [0x2118, 0x211D],
+ [0x2124, 0x2124],
+ [0x2126, 0x2126],
+ [0x2128, 0x2128],
+ [0x212A, 0x2131],
+ [0x2133, 0x2138],
+ [0x2160, 0x2182],
+ [0x3005, 0x3007],
+ [0x3021, 0x3029],
+ [0x3041, 0x3093],
+ [0x309B, 0x309C],
+ [0x30A1, 0x30F6],
+ [0x30FB, 0x30FC],
+ [0x3105, 0x312C],
+ [0x4E00, 0x9FA5],
+ [0xAC00, 0xD7A3]
+ ];
+
+ size_t high = ALPHA_TABLE.length - 1;
+ // Shortcut search if c is out of range
+ size_t low = (c < ALPHA_TABLE[0][0] || ALPHA_TABLE[high][1] < c) ? high + 1 : 0;
+ // Binary search
+ while (low <= high)
+ {
+ size_t mid = (low + high) >> 1;
+ if (c < ALPHA_TABLE[mid][0])
+ high = mid - 1;
+ else if (ALPHA_TABLE[mid][1] < c)
+ low = mid + 1;
+ else
+ {
+ assert(ALPHA_TABLE[mid][0] <= c && c <= ALPHA_TABLE[mid][1]);
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns the code length of c in code units.
+ */
+int utf_codeLengthChar(dchar c)
+{
+ if (c <= 0x7F)
+ return 1;
+ if (c <= 0x7FF)
+ return 2;
+ if (c <= 0xFFFF)
+ return 3;
+ if (c <= 0x10FFFF)
+ return 4;
+ assert(false);
+}
+
+int utf_codeLengthWchar(dchar c)
+{
+ return c <= 0xFFFF ? 1 : 2;
+}
+
+/**
+ * Returns the code length of c in code units for the encoding.
+ * sz is the encoding: 1 = utf8, 2 = utf16, 4 = utf32.
+ */
+int utf_codeLength(int sz, dchar c)
+{
+ if (sz == 1)
+ return utf_codeLengthChar(c);
+ if (sz == 2)
+ return utf_codeLengthWchar(c);
+ assert(sz == 4);
+ return 1;
+}
+
+void utf_encodeChar(char* s, dchar c)
+{
+ assert(s !is null);
+ assert(utf_isValidDchar(c));
+ if (c <= 0x7F)
+ {
+ s[0] = cast(char)c;
+ }
+ else if (c <= 0x07FF)
+ {
+ s[0] = cast(char)(0xC0 | (c >> 6));
+ s[1] = cast(char)(0x80 | (c & 0x3F));
+ }
+ else if (c <= 0xFFFF)
+ {
+ s[0] = cast(char)(0xE0 | (c >> 12));
+ s[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ s[2] = cast(char)(0x80 | (c & 0x3F));
+ }
+ else if (c <= 0x10FFFF)
+ {
+ s[0] = cast(char)(0xF0 | (c >> 18));
+ s[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
+ s[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ s[3] = cast(char)(0x80 | (c & 0x3F));
+ }
+ else
+ assert(0);
+}
+
+void utf_encodeWchar(wchar* s, dchar c)
+{
+ assert(s !is null);
+ assert(utf_isValidDchar(c));
+ if (c <= 0xFFFF)
+ {
+ s[0] = cast(wchar)c;
+ }
+ else
+ {
+ s[0] = cast(wchar)((((c - 0x010000) >> 10) & 0x03FF) + 0xD800);
+ s[1] = cast(wchar)(((c - 0x010000) & 0x03FF) + 0xDC00);
+ }
+}
+
+void utf_encode(int sz, void* s, dchar c)
+{
+ if (sz == 1)
+ utf_encodeChar(cast(char*)s, c);
+ else if (sz == 2)
+ utf_encodeWchar(cast(wchar*)s, c);
+ else
+ {
+ assert(sz == 4);
+ *(cast(dchar*)s) = c;
+ }
+}
+
+/********************************************
+ * Decode a UTF-8 sequence as a single UTF-32 code point.
+ * Params:
+ * s = UTF-8 sequence
+ * ridx = starting index in s[], updated to reflect number of code units decoded
+ * rresult = set to character decoded
+ * Returns:
+ * null on success, otherwise error message string
+ */
+string utf_decodeChar(const(char)[] s, ref size_t ridx, out dchar rresult)
+{
+ // UTF-8 decoding errors
+ static immutable string UTF8_DECODE_OK = null; // no error
+ static immutable string UTF8_DECODE_OUTSIDE_CODE_SPACE = "Outside Unicode code space";
+ static immutable string UTF8_DECODE_TRUNCATED_SEQUENCE = "Truncated UTF-8 sequence";
+ static immutable string UTF8_DECODE_OVERLONG = "Overlong UTF-8 sequence";
+ static immutable string UTF8_DECODE_INVALID_TRAILER = "Invalid trailing code unit";
+ static immutable string UTF8_DECODE_INVALID_CODE_POINT = "Invalid code point decoded";
+
+ /* The following encodings are valid, except for the 5 and 6 byte
+ * combinations:
+ * 0xxxxxxx
+ * 110xxxxx 10xxxxxx
+ * 1110xxxx 10xxxxxx 10xxxxxx
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ */
+ static immutable ubyte[256] UTF8_STRIDE =
+ [
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+ 1,1,1,1, 1,1,1,1,
+
+ 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
+
+ 2,2,2,2, 2,2,2,2,
+ 2,2,2,2, 2,2,2,2,
+ 2,2,2,2, 2,2,2,2,
+ 2,2,2,2, 2,2,2,2,
+
+ 3,3,3,3, 3,3,3,3,
+ 3,3,3,3, 3,3,3,3,
+
+ 4,4,4,4, 4,4,4,4,
+ 5,5,5,5, 6,6,0xFF,0xFF
+ ];
+
+ assert(s !is null);
+ size_t i = ridx++;
+
+ const char u = s[i];
+ // Pre-stage results for ASCII and error cases
+ rresult = u;
+ //printf("utf_decodeChar(s = %02x, %02x, %02x len = %d)\n", u, s[1], s[2], len);
+ // Get expected sequence length
+ const size_t n = UTF8_STRIDE[u];
+ switch (n)
+ {
+ case 1:
+ // ASCII
+ return UTF8_DECODE_OK;
+ case 2:
+ case 3:
+ case 4:
+ // multi-byte UTF-8
+ break;
+ default:
+ // 5- or 6-byte sequence
+ return UTF8_DECODE_OUTSIDE_CODE_SPACE;
+ }
+ if (s.length < i + n) // source too short
+ return UTF8_DECODE_TRUNCATED_SEQUENCE;
+ // Pick off 7 - n low bits from first code unit
+ dchar c = u & ((1 << (7 - n)) - 1);
+ /* The following combinations are overlong, and illegal:
+ * 1100000x (10xxxxxx)
+ * 11100000 100xxxxx (10xxxxxx)
+ * 11110000 1000xxxx (10xxxxxx 10xxxxxx)
+ * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx)
+ * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)
+ */
+ const char u2 = s[++i];
+ // overlong combination
+ if ((u & 0xFE) == 0xC0 || (u == 0xE0 && (u2 & 0xE0) == 0x80) || (u == 0xF0 && (u2 & 0xF0) == 0x80) || (u == 0xF8 && (u2 & 0xF8) == 0x80) || (u == 0xFC && (u2 & 0xFC) == 0x80))
+ return UTF8_DECODE_OVERLONG;
+ // Decode remaining bits
+ for (const m = n + i - 1; i != m; ++i)
+ {
+ const u3 = s[i];
+ if ((u3 & 0xC0) != 0x80) // trailing bytes are 10xxxxxx
+ return UTF8_DECODE_INVALID_TRAILER;
+ c = (c << 6) | (u3 & 0x3F);
+ }
+ if (!utf_isValidDchar(c))
+ return UTF8_DECODE_INVALID_CODE_POINT;
+ ridx = i;
+ rresult = c;
+ return UTF8_DECODE_OK;
+}
+
+/********************************************
+ * Decode a UTF-16 sequence as a single UTF-32 code point.
+ * Params:
+ * s = UTF-16 sequence
+ * ridx = starting index in s[], updated to reflect number of code units decoded
+ * rresult = set to character decoded
+ * Returns:
+ * null on success, otherwise error message string
+ */
+string utf_decodeWchar(const(wchar)[] s, ref size_t ridx, out dchar rresult)
+{
+ // UTF-16 decoding errors
+ static immutable string UTF16_DECODE_OK = null; // no error
+ static immutable string UTF16_DECODE_TRUNCATED_SEQUENCE = "Truncated UTF-16 sequence";
+ static immutable string UTF16_DECODE_INVALID_SURROGATE = "Invalid low surrogate";
+ static immutable string UTF16_DECODE_UNPAIRED_SURROGATE = "Unpaired surrogate";
+ static immutable string UTF16_DECODE_INVALID_CODE_POINT = "Invalid code point decoded";
+
+ assert(s !is null);
+ size_t i = ridx++;
+
+ // Pre-stage results for single wchar and error cases
+ dchar u = rresult = s[i];
+ if (u < 0xD800) // Single wchar codepoint
+ return UTF16_DECODE_OK;
+ if (0xD800 <= u && u <= 0xDBFF) // Surrogate pair
+ {
+ if (s.length <= i + 1)
+ return UTF16_DECODE_TRUNCATED_SEQUENCE;
+ wchar u2 = s[i + 1];
+ if (u2 < 0xDC00 || 0xDFFF < u)
+ return UTF16_DECODE_INVALID_SURROGATE;
+ u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00);
+ ++ridx;
+ }
+ else if (0xDC00 <= u && u <= 0xDFFF)
+ return UTF16_DECODE_UNPAIRED_SURROGATE;
+ if (!utf_isValidDchar(u))
+ return UTF16_DECODE_INVALID_CODE_POINT;
+ rresult = u;
+ return UTF16_DECODE_OK;
+}
diff --git a/gcc/d/dmd/utils.c b/gcc/d/dmd/utils.c
deleted file mode 100644
index c9e632234ef..00000000000
--- a/gcc/d/dmd/utils.c
+++ /dev/null
@@ -1,123 +0,0 @@
-
-/* Compiler implementation of the D programming language
- * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
- * written by Walter Bright
- * http://www.digitalmars.com
- * Distributed under the Boost Software License, Version 1.0.
- * http://www.boost.org/LICENSE_1_0.txt
- */
-
-#include "root/dsystem.h"
-#include "mars.h"
-#include "globals.h"
-#include "root/file.h"
-#include "root/filename.h"
-#include "root/outbuffer.h"
-#include "root/rmem.h"
-
-/**
- * Normalize path by turning forward slashes into backslashes
- *
- * Params:
- * src = Source path, using unix-style ('/') path separators
- *
- * Returns:
- * A newly-allocated string with '/' turned into backslashes
- */
-const char * toWinPath(const char *src)
-{
- if (src == NULL)
- return NULL;
-
- char *result = mem.xstrdup(src);
- char *p = result;
- while (*p != '\0')
- {
- if (*p == '/')
- *p = '\\';
- p++;
- }
- return result;
-}
-
-/**
- * Reads a file, terminate the program on error
- *
- * Params:
- * loc = The line number information from where the call originates
- * f = a `ddmd.root.file.File` handle to read
- */
-void readFile(Loc loc, File *f)
-{
- if (f->read())
- {
- error(loc, "Error reading file '%s'", f->name->toChars());
- fatal();
- }
-}
-
-/**
- * Writes a file, terminate the program on error
- *
- * Params:
- * loc = The line number information from where the call originates
- * f = a `ddmd.root.file.File` handle to write
- */
-void writeFile(Loc loc, File *f)
-{
- if (f->write())
- {
- error(loc, "Error writing file '%s'", f->name->toChars());
- fatal();
- }
-}
-
-/**
- * Ensure the root path (the path minus the name) of the provided path
- * exists, and terminate the process if it doesn't.
- *
- * Params:
- * loc = The line number information from where the call originates
- * name = a path to check (the name is stripped)
- */
-void ensurePathToNameExists(Loc loc, const char *name)
-{
- const char *pt = FileName::path(name);
- if (*pt)
- {
- if (FileName::ensurePathExists(pt))
- {
- error(loc, "cannot create directory %s", pt);
- fatal();
- }
- }
- FileName::free(pt);
-}
-
-/**
- * Takes a path, and escapes '(', ')' and backslashes
- *
- * Params:
- * buf = Buffer to write the escaped path to
- * fname = Path to escape
- */
-void escapePath(OutBuffer *buf, const char *fname)
-{
- while (1)
- {
- switch (*fname)
- {
- case 0:
- return;
- case '(':
- case ')':
- case '\\':
- buf->writeByte('\\');
- /* fall through */
- default:
- buf->writeByte(*fname);
- break;
- }
- fname++;
- }
-}
diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d
new file mode 100644
index 00000000000..600521fbd49
--- /dev/null
+++ b/gcc/d/dmd/utils.d
@@ -0,0 +1,298 @@
+/**
+ * This module defines some utility functions for DMD.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/utils.d, _utils.d)
+ * Documentation: https://dlang.org/phobos/dmd_utils.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/utils.d
+ */
+
+module dmd.utils;
+
+import core.stdc.string;
+import dmd.errors;
+import dmd.globals;
+import dmd.root.file;
+import dmd.root.filename;
+import dmd.root.outbuffer;
+import dmd.root.string;
+
+
+/**
+ * Normalize path by turning forward slashes into backslashes
+ *
+ * Params:
+ * src = Source path, using unix-style ('/') path separators
+ *
+ * Returns:
+ * A newly-allocated string with '/' turned into backslashes
+ */
+const(char)* toWinPath(const(char)* src)
+{
+ if (src is null)
+ return null;
+ char* result = strdup(src);
+ char* p = result;
+ while (*p != '\0')
+ {
+ if (*p == '/')
+ *p = '\\';
+ p++;
+ }
+ return result;
+}
+
+
+/**
+ * Reads a file, terminate the program on error
+ *
+ * Params:
+ * loc = The line number information from where the call originates
+ * filename = Path to file
+ */
+FileBuffer readFile(Loc loc, const(char)* filename)
+{
+ return readFile(loc, filename.toDString());
+}
+
+/// Ditto
+FileBuffer readFile(Loc loc, const(char)[] filename)
+{
+ auto result = File.read(filename);
+ if (!result.success)
+ {
+ error(loc, "Error reading file `%.*s`", cast(int)filename.length, filename.ptr);
+ fatal();
+ }
+ return FileBuffer(result.extractSlice());
+}
+
+
+/**
+ * Writes a file, terminate the program on error
+ *
+ * Params:
+ * loc = The line number information from where the call originates
+ * filename = Path to file
+ * data = Full content of the file to be written
+ */
+extern (D) void writeFile(Loc loc, const(char)[] filename, const void[] data)
+{
+ ensurePathToNameExists(Loc.initial, filename);
+ if (!File.update(filename, data))
+ {
+ error(loc, "Error writing file '%*.s'", cast(int) filename.length, filename.ptr);
+ fatal();
+ }
+}
+
+
+/**
+ * Ensure the root path (the path minus the name) of the provided path
+ * exists, and terminate the process if it doesn't.
+ *
+ * Params:
+ * loc = The line number information from where the call originates
+ * name = a path to check (the name is stripped)
+ */
+void ensurePathToNameExists(Loc loc, const(char)[] name)
+{
+ const char[] pt = FileName.path(name);
+ if (pt.length)
+ {
+ if (!FileName.ensurePathExists(pt))
+ {
+ error(loc, "cannot create directory %*.s", cast(int) pt.length, pt.ptr);
+ fatal();
+ }
+ }
+ FileName.free(pt.ptr);
+}
+
+
+/**
+ * Takes a path, and escapes '(', ')' and backslashes
+ *
+ * Params:
+ * buf = Buffer to write the escaped path to
+ * fname = Path to escape
+ */
+void escapePath(OutBuffer* buf, const(char)* fname)
+{
+ while (1)
+ {
+ switch (*fname)
+ {
+ case 0:
+ return;
+ case '(':
+ case ')':
+ case '\\':
+ buf.writeByte('\\');
+ goto default;
+ default:
+ buf.writeByte(*fname);
+ break;
+ }
+ fname++;
+ }
+}
+
+/**
+ * Takes a path, and make it compatible with GNU Makefile format.
+ *
+ * GNU make uses a weird quoting scheme for white space.
+ * A space or tab preceded by 2N+1 backslashes represents N backslashes followed by space;
+ * a space or tab preceded by 2N backslashes represents N backslashes at the end of a file name;
+ * and backslashes in other contexts should not be doubled.
+ *
+ * Params:
+ * buf = Buffer to write the escaped path to
+ * fname = Path to escape
+ */
+void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname)
+{
+ uint slashes;
+
+ while (*fname)
+ {
+ switch (*fname)
+ {
+ case '\\':
+ slashes++;
+ break;
+ case '$':
+ buf.writeByte('$');
+ goto default;
+ case ' ':
+ case '\t':
+ while (slashes--)
+ buf.writeByte('\\');
+ goto case;
+ case '#':
+ buf.writeByte('\\');
+ goto default;
+ case ':':
+ // ':' not escaped on Windows because it can
+ // create problems with absolute paths (e.g. C:\Project)
+ version (Windows) {}
+ else
+ {
+ buf.writeByte('\\');
+ }
+ goto default;
+ default:
+ slashes = 0;
+ break;
+ }
+
+ buf.writeByte(*fname);
+ fname++;
+ }
+}
+
+///
+unittest
+{
+ version (Windows)
+ {
+ enum input = `C:\My Project\file#4$.ext`;
+ enum expected = `C:\My\ Project\file\#4$$.ext`;
+ }
+ else
+ {
+ enum input = `/foo\bar/weird$.:name#\ with spaces.ext`;
+ enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`;
+ }
+
+ OutBuffer buf;
+ buf.writeEscapedMakePath(input);
+ assert(buf[] == expected);
+}
+
+/**
+ * Convert string to integer.
+ *
+ * Params:
+ * T = Type of integer to parse
+ * val = Variable to store the result in
+ * p = slice to start of string digits
+ * max = max allowable value (inclusive), defaults to `T.max`
+ *
+ * Returns:
+ * `false` on error, `true` on success
+ */
+bool parseDigits(T)(ref T val, const(char)[] p, const T max = T.max)
+ @safe pure @nogc nothrow
+{
+ import core.checkedint : mulu, addu, muls, adds;
+
+ // mul* / add* doesn't support types < int
+ static if (T.sizeof < int.sizeof)
+ {
+ int value;
+ alias add = adds;
+ alias mul = muls;
+ }
+ // unsigned
+ else static if (T.min == 0)
+ {
+ T value;
+ alias add = addu;
+ alias mul = mulu;
+ }
+ else
+ {
+ T value;
+ alias add = adds;
+ alias mul = muls;
+ }
+
+ bool overflow;
+ foreach (char c; p)
+ {
+ if (c > '9' || c < '0')
+ return false;
+ value = mul(value, 10, overflow);
+ value = add(value, uint(c - '0'), overflow);
+ }
+ // If it overflows, value must be > to `max` (since `max` is `T`)
+ val = cast(T) value;
+ return !overflow && value <= max;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ byte b;
+ ubyte ub;
+ short s;
+ ushort us;
+ int i;
+ uint ui;
+ long l;
+ ulong ul;
+
+ assert(b.parseDigits("42") && b == 42);
+ assert(ub.parseDigits("42") && ub == 42);
+
+ assert(s.parseDigits("420") && s == 420);
+ assert(us.parseDigits("42000") && us == 42_000);
+
+ assert(i.parseDigits("420000") && i == 420_000);
+ assert(ui.parseDigits("420000") && ui == 420_000);
+
+ assert(l.parseDigits("42000000000") && l == 42_000_000_000);
+ assert(ul.parseDigits("82000000000") && ul == 82_000_000_000);
+
+ assert(!b.parseDigits(ubyte.max.stringof));
+ assert(!b.parseDigits("WYSIWYG"));
+ assert(!b.parseDigits("-42"));
+ assert(!b.parseDigits("200"));
+ assert(ub.parseDigits("200") && ub == 200);
+ assert(i.parseDigits(int.max.stringof) && i == int.max);
+ assert(i.parseDigits("420", 500) && i == 420);
+ assert(!i.parseDigits("420", 400));
+}
diff --git a/gcc/d/dmd/version.h b/gcc/d/dmd/version.h
index 33811eef762..6c5e2f0f236 100644
--- a/gcc/d/dmd/version.h
+++ b/gcc/d/dmd/version.h
@@ -5,7 +5,7 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/dlang/dmd/blob/master/src/version.h
+ * https://github.com/dlang/dmd/blob/master/src/dmd/version.h
*/
#pragma once
@@ -17,14 +17,12 @@ class DebugSymbol : public Dsymbol
public:
unsigned level;
- DebugSymbol(Loc loc, Identifier *ident);
- DebugSymbol(Loc loc, unsigned level);
- Dsymbol *syntaxCopy(Dsymbol *);
+ DebugSymbol *syntaxCopy(Dsymbol *);
- const char *toChars();
+ const char *toChars() const;
void addMember(Scope *sc, ScopeDsymbol *sds);
const char *kind() const;
- DebugSymbol *isDebugSymbol() { return this; }
+ DebugSymbol *isDebugSymbol();
void accept(Visitor *v) { v->visit(this); }
};
@@ -33,13 +31,11 @@ class VersionSymbol : public Dsymbol
public:
unsigned level;
- VersionSymbol(Loc loc, Identifier *ident);
- VersionSymbol(Loc loc, unsigned level);
- Dsymbol *syntaxCopy(Dsymbol *);
+ VersionSymbol *syntaxCopy(Dsymbol *);
- const char *toChars();
+ const char *toChars() const;
void addMember(Scope *sc, ScopeDsymbol *sds);
const char *kind() const;
- VersionSymbol *isVersionSymbol() { return this; }
+ VersionSymbol *isVersionSymbol();
void accept(Visitor *v) { v->visit(this); }
};
diff --git a/gcc/d/dmd/visitor.d b/gcc/d/dmd/visitor.d
new file mode 100644
index 00000000000..8b9c01253de
--- /dev/null
+++ b/gcc/d/dmd/visitor.d
@@ -0,0 +1,254 @@
+/**
+ * Provides a visitor class visiting all AST nodes present in the compiler.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/visitor.d, _visitor.d)
+ * Documentation: https://dlang.org/phobos/dmd_visitor.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/visitor.d
+ */
+
+module dmd.visitor;
+
+import dmd.astcodegen;
+import dmd.astenums;
+import dmd.parsetimevisitor;
+import dmd.tokens;
+import dmd.transitivevisitor;
+import dmd.expression;
+import dmd.root.rootobject;
+
+/**
+ * Classic Visitor class which implements visit methods for all the AST
+ * nodes present in the compiler. The visit methods for AST nodes
+ * created at parse time are inherited while the visiting methods
+ * for AST nodes created at semantic time are implemented.
+ */
+extern (C++) class Visitor : ParseTimeVisitor!ASTCodegen
+{
+ alias visit = ParseTimeVisitor!ASTCodegen.visit;
+public:
+ void visit(ASTCodegen.ErrorStatement s) { visit(cast(ASTCodegen.Statement)s); }
+ void visit(ASTCodegen.PeelStatement s) { visit(cast(ASTCodegen.Statement)s); }
+ void visit(ASTCodegen.UnrolledLoopStatement s) { visit(cast(ASTCodegen.Statement)s); }
+ void visit(ASTCodegen.SwitchErrorStatement s) { visit(cast(ASTCodegen.Statement)s); }
+ void visit(ASTCodegen.DebugStatement s) { visit(cast(ASTCodegen.Statement)s); }
+ void visit(ASTCodegen.DtorExpStatement s) { visit(cast(ASTCodegen.ExpStatement)s); }
+ void visit(ASTCodegen.ForwardingStatement s) { visit(cast(ASTCodegen.Statement)s); }
+ void visit(ASTCodegen.OverloadSet s) { visit(cast(ASTCodegen.Dsymbol)s); }
+ void visit(ASTCodegen.LabelDsymbol s) { visit(cast(ASTCodegen.Dsymbol)s); }
+ void visit(ASTCodegen.WithScopeSymbol s) { visit(cast(ASTCodegen.ScopeDsymbol)s); }
+ void visit(ASTCodegen.ArrayScopeSymbol s) { visit(cast(ASTCodegen.ScopeDsymbol)s); }
+ void visit(ASTCodegen.OverDeclaration s) { visit(cast(ASTCodegen.Declaration)s); }
+ void visit(ASTCodegen.SymbolDeclaration s) { visit(cast(ASTCodegen.Declaration)s); }
+ void visit(ASTCodegen.ForwardingAttribDeclaration s) { visit(cast(ASTCodegen.AttribDeclaration)s); }
+ void visit(ASTCodegen.ThisDeclaration s) { visit(cast(ASTCodegen.VarDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoDeclaration s) { visit(cast(ASTCodegen.VarDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoStructDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoClassDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoInterfaceDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoPointerDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoArrayDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoStaticArrayDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoAssociativeArrayDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoEnumDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoFunctionDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoDelegateDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoTupleDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoConstDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoInvariantDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoSharedDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoWildDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.TypeInfoVectorDeclaration s) { visit(cast(ASTCodegen.TypeInfoDeclaration)s); }
+ void visit(ASTCodegen.FuncAliasDeclaration s) { visit(cast(ASTCodegen.FuncDeclaration)s); }
+ void visit(ASTCodegen.ErrorInitializer i) { visit(cast(ASTCodegen.Initializer)i); }
+ void visit(ASTCodegen.ErrorExp e) { visit(cast(ASTCodegen.Expression)e); }
+ void visit(ASTCodegen.ComplexExp e) { visit(cast(ASTCodegen.Expression)e); }
+ void visit(ASTCodegen.StructLiteralExp e) { visit(cast(ASTCodegen.Expression)e); }
+ void visit(ASTCodegen.CompoundLiteralExp e) { visit(cast(ASTCodegen.Expression)e); }
+ void visit(ASTCodegen.ObjcClassReferenceExp e) { visit(cast(ASTCodegen.Expression)e); }
+ void visit(ASTCodegen.SymOffExp e) { visit(cast(ASTCodegen.SymbolExp)e); }
+ void visit(ASTCodegen.OverExp e) { visit(cast(ASTCodegen.Expression)e); }
+ void visit(ASTCodegen.HaltExp e) { visit(cast(ASTCodegen.Expression)e); }
+ void visit(ASTCodegen.DotTemplateExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.DotVarExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.DelegateExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.DotTypeExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.VectorExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.VectorArrayExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.SliceExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.ArrayLengthExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.DelegatePtrExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.DelegateFuncptrExp e) { visit(cast(ASTCodegen.UnaExp)e); }
+ void visit(ASTCodegen.DotExp e) { visit(cast(ASTCodegen.BinExp)e); }
+ void visit(ASTCodegen.IndexExp e) { visit(cast(ASTCodegen.BinExp)e); }
+ void visit(ASTCodegen.ConstructExp e) { visit(cast(ASTCodegen.AssignExp)e); }
+ void visit(ASTCodegen.BlitExp e) { visit(cast(ASTCodegen.AssignExp)e); }
+ void visit(ASTCodegen.RemoveExp e) { visit(cast(ASTCodegen.BinExp)e); }
+ void visit(ASTCodegen.ClassReferenceExp e) { visit(cast(ASTCodegen.Expression)e); }
+ void visit(ASTCodegen.VoidInitExp e) { visit(cast(ASTCodegen.Expression)e); }
+ void visit(ASTCodegen.ThrownExceptionExp e) { visit(cast(ASTCodegen.Expression)e); }
+}
+
+/**
+ * The PermissiveVisitor overrides the root AST nodes with
+ * empty visiting methods.
+ */
+extern (C++) class SemanticTimePermissiveVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+
+ override void visit(ASTCodegen.Dsymbol){}
+ override void visit(ASTCodegen.Parameter){}
+ override void visit(ASTCodegen.Statement){}
+ override void visit(ASTCodegen.Type){}
+ override void visit(ASTCodegen.Expression){}
+ override void visit(ASTCodegen.TemplateParameter){}
+ override void visit(ASTCodegen.Condition){}
+ override void visit(ASTCodegen.Initializer){}
+}
+
+/**
+ * The TransitiveVisitor implements the AST traversal logic for all AST nodes.
+ */
+extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor
+{
+ alias visit = SemanticTimePermissiveVisitor.visit;
+
+ mixin ParseVisitMethods!ASTCodegen __methods;
+ alias visit = __methods.visit;
+
+ override void visit(ASTCodegen.PeelStatement s)
+ {
+ if (s.s)
+ s.s.accept(this);
+ }
+
+ override void visit(ASTCodegen.UnrolledLoopStatement s)
+ {
+ foreach(sx; *s.statements)
+ {
+ if (sx)
+ sx.accept(this);
+ }
+ }
+
+ override void visit(ASTCodegen.DebugStatement s)
+ {
+ if (s.statement)
+ s.statement.accept(this);
+ }
+
+ override void visit(ASTCodegen.ForwardingStatement s)
+ {
+ if (s.statement)
+ s.statement.accept(this);
+ }
+
+ override void visit(ASTCodegen.StructLiteralExp e)
+ {
+ // CTFE can generate struct literals that contain an AddrExp pointing to themselves,
+ // need to avoid infinite recursion.
+ if (!(e.stageflags & stageToCBuffer))
+ {
+ int old = e.stageflags;
+ e.stageflags |= stageToCBuffer;
+ foreach (el; *e.elements)
+ if (el)
+ el.accept(this);
+ e.stageflags = old;
+ }
+ }
+
+ override void visit(ASTCodegen.CompoundLiteralExp e)
+ {
+ if (e.initializer)
+ e.initializer.accept(this);
+ }
+
+ override void visit(ASTCodegen.DotTemplateExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(ASTCodegen.DotVarExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(ASTCodegen.DelegateExp e)
+ {
+ if (!e.func.isNested() || e.func.needThis())
+ e.e1.accept(this);
+ }
+
+ override void visit(ASTCodegen.DotTypeExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(ASTCodegen.VectorExp e)
+ {
+ visitType(e.to);
+ e.e1.accept(this);
+ }
+
+ override void visit(ASTCodegen.VectorArrayExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(ASTCodegen.SliceExp e)
+ {
+ e.e1.accept(this);
+ if (e.upr)
+ e.upr.accept(this);
+ if (e.lwr)
+ e.lwr.accept(this);
+ }
+
+ override void visit(ASTCodegen.ArrayLengthExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(ASTCodegen.DelegatePtrExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(ASTCodegen.DelegateFuncptrExp e)
+ {
+ e.e1.accept(this);
+ }
+
+ override void visit(ASTCodegen.DotExp e)
+ {
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+
+ override void visit(ASTCodegen.IndexExp e)
+ {
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+
+ override void visit(ASTCodegen.RemoveExp e)
+ {
+ e.e1.accept(this);
+ e.e2.accept(this);
+ }
+}
+
+extern (C++) class StoppableVisitor : Visitor
+{
+ alias visit = Visitor.visit;
+public:
+ bool stop;
+
+ final extern (D) this()
+ {
+ }
+}
diff --git a/gcc/d/dmd/visitor.h b/gcc/d/dmd/visitor.h
index 09ba2024e30..e61f16d7c8b 100644
--- a/gcc/d/dmd/visitor.h
+++ b/gcc/d/dmd/visitor.h
@@ -84,6 +84,7 @@ class TypeNull;
class TypeNoreturn;
class TypeTraits;
class TypeMixin;
+class TypeTag;
class Dsymbol;
@@ -101,7 +102,8 @@ class StorageClassDeclaration;
class DeprecatedDeclaration;
class LinkDeclaration;
class CPPMangleDeclaration;
-class ProtDeclaration;
+class CPPNamespaceDeclaration;
+class VisibilityDeclaration;
class AlignDeclaration;
class AnonDeclaration;
class PragmaDeclaration;
@@ -122,6 +124,7 @@ class Module;
class WithScopeSymbol;
class ArrayScopeSymbol;
class Nspace;
+class AliasAssign;
class AggregateDeclaration;
class StructDeclaration;
@@ -136,6 +139,7 @@ class OverDeclaration;
class VarDeclaration;
class SymbolDeclaration;
class ThisDeclaration;
+class BitFieldDeclaration;
class TypeInfoDeclaration;
class TypeInfoStructDeclaration;
@@ -168,7 +172,6 @@ class SharedStaticDtorDeclaration;
class InvariantDeclaration;
class UnitTestDeclaration;
class NewDeclaration;
-class DeleteDeclaration;
class Initializer;
class VoidInitializer;
@@ -176,6 +179,7 @@ class ErrorInitializer;
class StructInitializer;
class ArrayInitializer;
class ExpInitializer;
+class CInitializer;
class Expression;
class IntegerExp;
@@ -193,6 +197,8 @@ class TupleExp;
class ArrayLiteralExp;
class AssocArrayLiteralExp;
class StructLiteralExp;
+class CompoundLiteralExp;
+class ObjcClassReferenceExp;
class TypeExp;
class ScopeExp;
class TemplateExp;
@@ -211,7 +217,7 @@ class IsExp;
class UnaExp;
class BinExp;
class BinAssignExp;
-class CompileExp;
+class MixinExp;
class ImportExp;
class AssertExp;
class DotIdExp;
@@ -287,6 +293,7 @@ class PrettyFuncInitExp;
class ClassReferenceExp;
class VoidInitExp;
class ThrownExceptionExp;
+class GenericExp;
class TemplateParameter;
class TemplateTypeParameter;
@@ -303,135 +310,301 @@ class StaticIfCondition;
class Parameter;
-class Visitor
+class ParseTimeVisitor
{
public:
+ virtual void visit(Dsymbol *) { assert(0); }
+ virtual void visit(Parameter *) { assert(0); }
virtual void visit(Statement *) { assert(0); }
- virtual void visit(ErrorStatement *s) { visit((Statement *)s); }
- virtual void visit(PeelStatement *s) { visit((Statement *)s); }
- virtual void visit(ExpStatement *s) { visit((Statement *)s); }
- virtual void visit(DtorExpStatement *s) { visit((ExpStatement *)s); }
- virtual void visit(CompileStatement *s) { visit((Statement *)s); }
- virtual void visit(CompoundStatement *s) { visit((Statement *)s); }
- virtual void visit(CompoundDeclarationStatement *s) { visit((CompoundStatement *)s); }
- virtual void visit(UnrolledLoopStatement *s) { visit((Statement *)s); }
+ virtual void visit(Type *) { assert(0); }
+ virtual void visit(Expression *) { assert(0); }
+ virtual void visit(TemplateParameter *) { assert(0); }
+ virtual void visit(Condition *) { assert(0); }
+ virtual void visit(Initializer *) { assert(0); }
+
+ // Dsymbols
+ virtual void visit(AliasThis *s) { visit((Dsymbol *)s); }
+ virtual void visit(Declaration *s) { visit((Dsymbol *)s); }
+ virtual void visit(ScopeDsymbol *s) { visit((Dsymbol *)s); }
+ virtual void visit(Import *s) { visit((Dsymbol *)s); }
+ virtual void visit(AttribDeclaration *s) { visit((Dsymbol *)s); }
+ virtual void visit(StaticAssert *s) { visit((Dsymbol *)s); }
+ virtual void visit(DebugSymbol *s) { visit((Dsymbol *)s); }
+ virtual void visit(VersionSymbol *s) { visit((Dsymbol *)s); }
+ virtual void visit(AliasAssign *s) { visit((Dsymbol *)s); }
+
+ // ScopeDsymbols
+ virtual void visit(Package *s) { visit((ScopeDsymbol *)s); }
+ virtual void visit(EnumDeclaration *s) { visit((ScopeDsymbol *)s); }
+ virtual void visit(AggregateDeclaration *s) { visit((ScopeDsymbol *)s); }
+ virtual void visit(TemplateDeclaration *s) { visit((ScopeDsymbol *)s); }
+ virtual void visit(TemplateInstance *s) { visit((ScopeDsymbol *)s); }
+ virtual void visit(Nspace *s) { visit((ScopeDsymbol *)s); }
+
+ // Declarations
+ virtual void visit(VarDeclaration *s) { visit((Declaration *)s); }
+ virtual void visit(FuncDeclaration *s) { visit((Declaration *)s); }
+ virtual void visit(AliasDeclaration *s) { visit((Declaration *)s); }
+ virtual void visit(TupleDeclaration *s) { visit((Declaration *)s); }
+
+ // FuncDeclarations
+ virtual void visit(FuncLiteralDeclaration *s) { visit((FuncDeclaration *)s); }
+ virtual void visit(PostBlitDeclaration *s) { visit((FuncDeclaration *)s); }
+ virtual void visit(CtorDeclaration *s) { visit((FuncDeclaration *)s); }
+ virtual void visit(DtorDeclaration *s) { visit((FuncDeclaration *)s); }
+ virtual void visit(InvariantDeclaration *s) { visit((FuncDeclaration *)s); }
+ virtual void visit(UnitTestDeclaration *s) { visit((FuncDeclaration *)s); }
+ virtual void visit(NewDeclaration *s) { visit((FuncDeclaration *)s); }
+ virtual void visit(StaticCtorDeclaration *s) { visit((FuncDeclaration *)s); }
+ virtual void visit(StaticDtorDeclaration *s) { visit((FuncDeclaration *)s); }
+ virtual void visit(SharedStaticCtorDeclaration *s) { visit((StaticCtorDeclaration *)s); }
+ virtual void visit(SharedStaticDtorDeclaration *s) { visit((StaticDtorDeclaration *)s); }
+
+ // AttribDeclarations
+ virtual void visit(CompileDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(UserAttributeDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(LinkDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(AnonDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(AlignDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(CPPMangleDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(CPPNamespaceDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(VisibilityDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(PragmaDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(StorageClassDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(ConditionalDeclaration *s) { visit((AttribDeclaration *)s); }
+ virtual void visit(StaticForeachDeclaration *s) { visit((AttribDeclaration *)s); }
+
+ // Miscellaneous
+ virtual void visit(DeprecatedDeclaration *s) { visit((StorageClassDeclaration *)s); }
+ virtual void visit(StaticIfDeclaration *s) { visit((ConditionalDeclaration *)s); }
+ virtual void visit(EnumMember *s) { visit((VarDeclaration *)s); }
+ virtual void visit(Module *s) { visit((Package *)s); }
+ virtual void visit(StructDeclaration *s) { visit((AggregateDeclaration *)s); }
+ virtual void visit(UnionDeclaration *s) { visit((StructDeclaration *)s); }
+ virtual void visit(ClassDeclaration *s) { visit((AggregateDeclaration *)s); }
+ virtual void visit(InterfaceDeclaration *s) { visit((ClassDeclaration *)s); }
+ virtual void visit(TemplateMixin *s) { visit((TemplateInstance *)s); }
+ virtual void visit(BitFieldDeclaration *s) { visit((VarDeclaration *)s); }
+
+ // Statements
+ virtual void visit(ImportStatement *s) { visit((Statement *)s); }
virtual void visit(ScopeStatement *s) { visit((Statement *)s); }
- virtual void visit(ForwardingStatement *s) { visit((Statement *)s); }
+ virtual void visit(ReturnStatement *s) { visit((Statement *)s); }
+ virtual void visit(LabelStatement *s) { visit((Statement *)s); }
+ virtual void visit(StaticAssertStatement *s) { visit((Statement *)s); }
+ virtual void visit(CompileStatement *s) { visit((Statement *)s); }
virtual void visit(WhileStatement *s) { visit((Statement *)s); }
- virtual void visit(DoStatement *s) { visit((Statement *)s); }
virtual void visit(ForStatement *s) { visit((Statement *)s); }
- virtual void visit(ForeachStatement *s) { visit((Statement *)s); }
+ virtual void visit(DoStatement *s) { visit((Statement *)s); }
virtual void visit(ForeachRangeStatement *s) { visit((Statement *)s); }
- virtual void visit(StaticForeachStatement *s) { visit((Statement *)s); }
+ virtual void visit(ForeachStatement *s) { visit((Statement *)s); }
virtual void visit(IfStatement *s) { visit((Statement *)s); }
+ virtual void visit(ScopeGuardStatement *s) { visit((Statement *)s); }
virtual void visit(ConditionalStatement *s) { visit((Statement *)s); }
+ virtual void visit(StaticForeachStatement *s) { visit((Statement *)s); }
virtual void visit(PragmaStatement *s) { visit((Statement *)s); }
- virtual void visit(StaticAssertStatement *s) { visit((Statement *)s); }
virtual void visit(SwitchStatement *s) { visit((Statement *)s); }
- virtual void visit(CaseStatement *s) { visit((Statement *)s); }
virtual void visit(CaseRangeStatement *s) { visit((Statement *)s); }
+ virtual void visit(CaseStatement *s) { visit((Statement *)s); }
virtual void visit(DefaultStatement *s) { visit((Statement *)s); }
- virtual void visit(GotoDefaultStatement *s) { visit((Statement *)s); }
- virtual void visit(GotoCaseStatement *s) { visit((Statement *)s); }
- virtual void visit(SwitchErrorStatement *s) { visit((Statement *)s); }
- virtual void visit(ReturnStatement *s) { visit((Statement *)s); }
virtual void visit(BreakStatement *s) { visit((Statement *)s); }
virtual void visit(ContinueStatement *s) { visit((Statement *)s); }
+ virtual void visit(GotoDefaultStatement *s) { visit((Statement *)s); }
+ virtual void visit(GotoCaseStatement *s) { visit((Statement *)s); }
+ virtual void visit(GotoStatement *s) { visit((Statement *)s); }
virtual void visit(SynchronizedStatement *s) { visit((Statement *)s); }
virtual void visit(WithStatement *s) { visit((Statement *)s); }
virtual void visit(TryCatchStatement *s) { visit((Statement *)s); }
virtual void visit(TryFinallyStatement *s) { visit((Statement *)s); }
- virtual void visit(ScopeGuardStatement *s) { visit((Statement *)s); }
virtual void visit(ThrowStatement *s) { visit((Statement *)s); }
- virtual void visit(DebugStatement *s) { visit((Statement *)s); }
- virtual void visit(GotoStatement *s) { visit((Statement *)s); }
- virtual void visit(LabelStatement *s) { visit((Statement *)s); }
virtual void visit(AsmStatement *s) { visit((Statement *)s); }
+ virtual void visit(ExpStatement *s) { visit((Statement *)s); }
+ virtual void visit(CompoundStatement *s) { visit((Statement *)s); }
+
+ // CompoundStatements
+ virtual void visit(CompoundDeclarationStatement *s) { visit((CompoundStatement *)s); }
+ virtual void visit(CompoundAsmStatement *s) { visit((CompoundStatement *)s); }
+
+ // AsmStatements
virtual void visit(InlineAsmStatement *s) { visit((AsmStatement *)s); }
virtual void visit(GccAsmStatement *s) { visit((AsmStatement *)s); }
- virtual void visit(CompoundAsmStatement *s) { visit((CompoundStatement *)s); }
- virtual void visit(ImportStatement *s) { visit((Statement *)s); }
- virtual void visit(Type *) { assert(0); }
- virtual void visit(TypeError *t) { visit((Type *)t); }
- virtual void visit(TypeNext *t) { visit((Type *)t); }
+ // Types
virtual void visit(TypeBasic *t) { visit((Type *)t); }
+ virtual void visit(TypeError *t) { visit((Type *)t); }
+ virtual void visit(TypeNull *t) { visit((Type *)t); }
+ virtual void visit(TypeNoreturn *t) { visit((Type *)t); }
virtual void visit(TypeVector *t) { visit((Type *)t); }
+ virtual void visit(TypeEnum *t) { visit((Type *)t); }
+ virtual void visit(TypeTuple *t) { visit((Type *)t); }
+ virtual void visit(TypeClass *t) { visit((Type *)t); }
+ virtual void visit(TypeStruct *t) { visit((Type *)t); }
+ virtual void visit(TypeNext *t) { visit((Type *)t); }
+ virtual void visit(TypeQualified *t) { visit((Type *)t); }
+ virtual void visit(TypeTraits *t) { visit((Type *)t); }
+ virtual void visit(TypeMixin *t) { visit((Type *)t); }
+ virtual void visit(TypeTag *t) { visit((Type *)t); }
+
+ // TypeNext
+ virtual void visit(TypeReference *t) { visit((TypeNext *)t); }
+ virtual void visit(TypeSlice *t) { visit((TypeNext *)t); }
+ virtual void visit(TypeDelegate *t) { visit((TypeNext *)t); }
+ virtual void visit(TypePointer *t) { visit((TypeNext *)t); }
+ virtual void visit(TypeFunction *t) { visit((TypeNext *)t); }
virtual void visit(TypeArray *t) { visit((TypeNext *)t); }
- virtual void visit(TypeSArray *t) { visit((TypeArray *)t); }
+
+ // TypeArray
virtual void visit(TypeDArray *t) { visit((TypeArray *)t); }
virtual void visit(TypeAArray *t) { visit((TypeArray *)t); }
- virtual void visit(TypePointer *t) { visit((TypeNext *)t); }
- virtual void visit(TypeReference *t) { visit((TypeNext *)t); }
- virtual void visit(TypeFunction *t) { visit((TypeNext *)t); }
- virtual void visit(TypeDelegate *t) { visit((TypeNext *)t); }
- virtual void visit(TypeQualified *t) { visit((Type *)t); }
+ virtual void visit(TypeSArray *t) { visit((TypeArray *)t); }
+
+ // TypeQualified
virtual void visit(TypeIdentifier *t) { visit((TypeQualified *)t); }
- virtual void visit(TypeInstance *t) { visit((TypeQualified *)t); }
- virtual void visit(TypeTypeof *t) { visit((TypeQualified *)t); }
virtual void visit(TypeReturn *t) { visit((TypeQualified *)t); }
- virtual void visit(TypeStruct *t) { visit((Type *)t); }
- virtual void visit(TypeEnum *t) { visit((Type *)t); }
- virtual void visit(TypeClass *t) { visit((Type *)t); }
- virtual void visit(TypeTuple *t) { visit((Type *)t); }
- virtual void visit(TypeSlice *t) { visit((TypeNext *)t); }
- virtual void visit(TypeNull *t) { visit((Type *)t); }
- virtual void visit(TypeNoreturn *t) { visit((Type *)t); }
- virtual void visit(TypeTraits *t) { visit((Type *)t); }
- virtual void visit(TypeMixin *t) { visit((Type *)t); }
+ virtual void visit(TypeTypeof *t) { visit((TypeQualified *)t); }
+ virtual void visit(TypeInstance *t) { visit((TypeQualified *)t); }
- virtual void visit(Dsymbol *) { assert(0); }
+ // Expressions
+ virtual void visit(DeclarationExp *e) { visit((Expression *)e); }
+ virtual void visit(IntegerExp *e) { visit((Expression *)e); }
+ virtual void visit(NewAnonClassExp *e) { visit((Expression *)e); }
+ virtual void visit(IsExp *e) { visit((Expression *)e); }
+ virtual void visit(RealExp *e) { visit((Expression *)e); }
+ virtual void visit(NullExp *e) { visit((Expression *)e); }
+ virtual void visit(TypeidExp *e) { visit((Expression *)e); }
+ virtual void visit(TraitsExp *e) { visit((Expression *)e); }
+ virtual void visit(StringExp *e) { visit((Expression *)e); }
+ virtual void visit(NewExp *e) { visit((Expression *)e); }
+ virtual void visit(AssocArrayLiteralExp *e) { visit((Expression *)e); }
+ virtual void visit(ArrayLiteralExp *e) { visit((Expression *)e); }
+ virtual void visit(MixinExp *e) { visit((Expression *)e); }
+ virtual void visit(FuncExp *e) { visit((Expression *)e); }
+ virtual void visit(IntervalExp *e) { visit((Expression *)e); }
+ virtual void visit(TypeExp *e) { visit((Expression *)e); }
+ virtual void visit(ScopeExp *e) { visit((Expression *)e); }
+ virtual void visit(IdentifierExp *e) { visit((Expression *)e); }
+ virtual void visit(UnaExp *e) { visit((Expression *)e); }
+ virtual void visit(DefaultInitExp *e) { visit((Expression *)e); }
+ virtual void visit(BinExp *e) { visit((Expression *)e); }
+ virtual void visit(DsymbolExp *e) { visit((Expression *)e); }
+ virtual void visit(TemplateExp *e) { visit((Expression *)e); }
+ virtual void visit(SymbolExp *e) { visit((Expression *)e); }
+ virtual void visit(TupleExp *e) { visit((Expression *)e); }
+ virtual void visit(ThisExp *e) { visit((Expression *)e); }
+ virtual void visit(GenericExp *e) { visit((Expression *)e); }
- virtual void visit(StaticAssert *s) { visit((Dsymbol *)s); }
- virtual void visit(DebugSymbol *s) { visit((Dsymbol *)s); }
- virtual void visit(VersionSymbol *s) { visit((Dsymbol *)s); }
- virtual void visit(EnumMember *s) { visit((VarDeclaration *)s); }
- virtual void visit(Import *s) { visit((Dsymbol *)s); }
- virtual void visit(OverloadSet *s) { visit((Dsymbol *)s); }
- virtual void visit(LabelDsymbol *s) { visit((Dsymbol *)s); }
- virtual void visit(AliasThis *s) { visit((Dsymbol *)s); }
+ // Miscellaneous
+ virtual void visit(VarExp *e) { visit((SymbolExp *)e); }
+ virtual void visit(DollarExp *e) { visit((IdentifierExp *)e); }
+ virtual void visit(SuperExp *e) { visit((ThisExp *)e); }
- virtual void visit(AttribDeclaration *s) { visit((Dsymbol *)s); }
- virtual void visit(StorageClassDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(DeprecatedDeclaration *s) { visit((StorageClassDeclaration *)s); }
- virtual void visit(LinkDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(CPPMangleDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(ProtDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(AlignDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(AnonDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(PragmaDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(ConditionalDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(StaticIfDeclaration *s) { visit((ConditionalDeclaration *)s); }
- virtual void visit(StaticForeachDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(CompileDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(UserAttributeDeclaration *s) { visit((AttribDeclaration *)s); }
- virtual void visit(ForwardingAttribDeclaration *s) { visit((AttribDeclaration *)s); }
+ // UnaExp
+ virtual void visit(AddrExp *e) { visit((UnaExp *)e); }
+ virtual void visit(PreExp *e) { visit((UnaExp *)e); }
+ virtual void visit(PtrExp *e) { visit((UnaExp *)e); }
+ virtual void visit(NegExp *e) { visit((UnaExp *)e); }
+ virtual void visit(UAddExp *e) { visit((UnaExp *)e); }
+ virtual void visit(NotExp *e) { visit((UnaExp *)e); }
+ virtual void visit(ComExp *e) { visit((UnaExp *)e); }
+ virtual void visit(DeleteExp *e) { visit((UnaExp *)e); }
+ virtual void visit(CastExp *e) { visit((UnaExp *)e); }
+ virtual void visit(CallExp *e) { visit((UnaExp *)e); }
+ virtual void visit(DotIdExp *e) { visit((UnaExp *)e); }
+ virtual void visit(AssertExp *e) { visit((UnaExp *)e); }
+ virtual void visit(ImportExp *e) { visit((UnaExp *)e); }
+ virtual void visit(DotTemplateInstanceExp *e) { visit((UnaExp *)e); }
+ virtual void visit(ArrayExp *e) { visit((UnaExp *)e); }
- virtual void visit(ScopeDsymbol *s) { visit((Dsymbol *)s); }
- virtual void visit(TemplateDeclaration *s) { visit((ScopeDsymbol *)s); }
- virtual void visit(TemplateInstance *s) { visit((ScopeDsymbol *)s); }
- virtual void visit(TemplateMixin *s) { visit((TemplateInstance *)s); }
- virtual void visit(EnumDeclaration *s) { visit((ScopeDsymbol *)s); }
- virtual void visit(Package *s) { visit((ScopeDsymbol *)s); }
- virtual void visit(Module *s) { visit((Package *)s); }
- virtual void visit(WithScopeSymbol *s) { visit((ScopeDsymbol *)s); }
- virtual void visit(ArrayScopeSymbol *s) { visit((ScopeDsymbol *)s); }
- virtual void visit(Nspace *s) { visit((ScopeDsymbol *)s); }
+ // DefaultInitExp
+ virtual void visit(FuncInitExp *e) { visit((DefaultInitExp *)e); }
+ virtual void visit(PrettyFuncInitExp *e) { visit((DefaultInitExp *)e); }
+ virtual void visit(FileInitExp *e) { visit((DefaultInitExp *)e); }
+ virtual void visit(LineInitExp *e) { visit((DefaultInitExp *)e); }
+ virtual void visit(ModuleInitExp *e) { visit((DefaultInitExp *)e); }
- virtual void visit(AggregateDeclaration *s) { visit((ScopeDsymbol *)s); }
- virtual void visit(StructDeclaration *s) { visit((AggregateDeclaration *)s); }
- virtual void visit(UnionDeclaration *s) { visit((StructDeclaration *)s); }
- virtual void visit(ClassDeclaration *s) { visit((AggregateDeclaration *)s); }
- virtual void visit(InterfaceDeclaration *s) { visit((ClassDeclaration *)s); }
+ // BinExp
+ virtual void visit(CommaExp *e) { visit((BinExp *)e); }
+ virtual void visit(PostExp *e) { visit((BinExp *)e); }
+ virtual void visit(PowExp *e) { visit((BinExp *)e); }
+ virtual void visit(MulExp *e) { visit((BinExp *)e); }
+ virtual void visit(DivExp *e) { visit((BinExp *)e); }
+ virtual void visit(ModExp *e) { visit((BinExp *)e); }
+ virtual void visit(AddExp *e) { visit((BinExp *)e); }
+ virtual void visit(MinExp *e) { visit((BinExp *)e); }
+ virtual void visit(CatExp *e) { visit((BinExp *)e); }
+ virtual void visit(ShlExp *e) { visit((BinExp *)e); }
+ virtual void visit(ShrExp *e) { visit((BinExp *)e); }
+ virtual void visit(UshrExp *e) { visit((BinExp *)e); }
+ virtual void visit(EqualExp *e) { visit((BinExp *)e); }
+ virtual void visit(InExp *e) { visit((BinExp *)e); }
+ virtual void visit(IdentityExp *e) { visit((BinExp *)e); }
+ virtual void visit(CmpExp *e) { visit((BinExp *)e); }
+ virtual void visit(AndExp *e) { visit((BinExp *)e); }
+ virtual void visit(XorExp *e) { visit((BinExp *)e); }
+ virtual void visit(OrExp *e) { visit((BinExp *)e); }
+ virtual void visit(LogicalExp *e) { visit((BinExp *)e); }
+ virtual void visit(CondExp *e) { visit((BinExp *)e); }
+ virtual void visit(AssignExp *e) { visit((BinExp *)e); }
+ virtual void visit(BinAssignExp *e) { visit((BinExp *)e); }
- virtual void visit(Declaration *s) { visit((Dsymbol *)s); }
- virtual void visit(TupleDeclaration *s) { visit((Declaration *)s); }
- virtual void visit(AliasDeclaration *s) { visit((Declaration *)s); }
+ // BinAssignExp
+ virtual void visit(AddAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(MinAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(MulAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(DivAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(ModAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(PowAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(AndAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(OrAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(XorAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(ShlAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(ShrAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(UshrAssignExp *e) { visit((BinAssignExp *)e); }
+ virtual void visit(CatAssignExp *e) { visit((BinAssignExp *)e); }
+
+ // TemplateParameter
+ virtual void visit(TemplateAliasParameter *tp) { visit((TemplateParameter *)tp); }
+ virtual void visit(TemplateTypeParameter *tp) { visit((TemplateParameter *)tp); }
+ virtual void visit(TemplateTupleParameter *tp) { visit((TemplateParameter *)tp); }
+ virtual void visit(TemplateValueParameter *tp) { visit((TemplateParameter *)tp); }
+
+ virtual void visit(TemplateThisParameter *tp) { visit((TemplateTypeParameter *)tp); }
+
+ // Condition
+ virtual void visit(StaticIfCondition *c) { visit((Condition *)c); }
+ virtual void visit(DVCondition *c) { visit((Condition *)c); }
+ virtual void visit(DebugCondition *c) { visit((DVCondition *)c); }
+ virtual void visit(VersionCondition *c) { visit((DVCondition *)c); }
+
+ // Initializer
+ virtual void visit(ExpInitializer *i) { visit((Initializer *)i); }
+ virtual void visit(StructInitializer *i) { visit((Initializer *)i); }
+ virtual void visit(ArrayInitializer *i) { visit((Initializer *)i); }
+ virtual void visit(VoidInitializer *i) { visit((Initializer *)i); }
+ virtual void visit(CInitializer *i) { visit((Initializer *)i); }
+};
+
+class Visitor : public ParseTimeVisitor
+{
+public:
+ using ParseTimeVisitor::visit;
+
+ // Miscellaneous
+ virtual void visit(ErrorStatement *s) { visit((Statement *)s); }
+ virtual void visit(PeelStatement *s) { visit((Statement *)s); }
+ virtual void visit(UnrolledLoopStatement *s) { visit((Statement *)s); }
+ virtual void visit(SwitchErrorStatement *s) { visit((Statement *)s); }
+ virtual void visit(DebugStatement *s) { visit((Statement *)s); }
+ virtual void visit(DtorExpStatement *s) { visit((ExpStatement *)s); }
+ virtual void visit(ForwardingStatement *s) { visit((Statement *)s); }
+ virtual void visit(OverloadSet *s) { visit((Dsymbol *)s); }
+ virtual void visit(LabelDsymbol *s) { visit((Dsymbol *)s); }
+ virtual void visit(WithScopeSymbol *s) { visit((ScopeDsymbol *)s); }
+ virtual void visit(ArrayScopeSymbol *s) { visit((ScopeDsymbol *)s); }
virtual void visit(OverDeclaration *s) { visit((Declaration *)s); }
- virtual void visit(VarDeclaration *s) { visit((Declaration *)s); }
virtual void visit(SymbolDeclaration *s) { visit((Declaration *)s); }
+ virtual void visit(ForwardingAttribDeclaration *s) { visit((AttribDeclaration *)s); }
virtual void visit(ThisDeclaration *s) { visit((VarDeclaration *)s); }
-
virtual void visit(TypeInfoDeclaration *s) { visit((VarDeclaration *)s); }
virtual void visit(TypeInfoStructDeclaration *s) { visit((TypeInfoDeclaration *)s); }
virtual void visit(TypeInfoClassDeclaration *s) { visit((TypeInfoDeclaration *)s); }
@@ -449,154 +622,34 @@ public:
virtual void visit(TypeInfoSharedDeclaration *s) { visit((TypeInfoDeclaration *)s); }
virtual void visit(TypeInfoWildDeclaration *s) { visit((TypeInfoDeclaration *)s); }
virtual void visit(TypeInfoVectorDeclaration *s) { visit((TypeInfoDeclaration *)s); }
-
- virtual void visit(FuncDeclaration *s) { visit((Declaration *)s); }
virtual void visit(FuncAliasDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(FuncLiteralDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(CtorDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(PostBlitDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(DtorDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(StaticCtorDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(SharedStaticCtorDeclaration *s) { visit((StaticCtorDeclaration *)s); }
- virtual void visit(StaticDtorDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(SharedStaticDtorDeclaration *s) { visit((StaticDtorDeclaration *)s); }
- virtual void visit(InvariantDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(UnitTestDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(NewDeclaration *s) { visit((FuncDeclaration *)s); }
- virtual void visit(DeleteDeclaration *s) { visit((FuncDeclaration *)s); }
-
- virtual void visit(Initializer *) { assert(0); }
- virtual void visit(VoidInitializer *i) { visit((Initializer *)i); }
virtual void visit(ErrorInitializer *i) { visit((Initializer *)i); }
- virtual void visit(StructInitializer *i) { visit((Initializer *)i); }
- virtual void visit(ArrayInitializer *i) { visit((Initializer *)i); }
- virtual void visit(ExpInitializer *i) { visit((Initializer *)i); }
-
- virtual void visit(Expression *) { assert(0); }
- virtual void visit(IntegerExp *e) { visit((Expression *)e); }
virtual void visit(ErrorExp *e) { visit((Expression *)e); }
- virtual void visit(RealExp *e) { visit((Expression *)e); }
virtual void visit(ComplexExp *e) { visit((Expression *)e); }
- virtual void visit(IdentifierExp *e) { visit((Expression *)e); }
- virtual void visit(DollarExp *e) { visit((IdentifierExp *)e); }
- virtual void visit(DsymbolExp *e) { visit((Expression *)e); }
- virtual void visit(ThisExp *e) { visit((Expression *)e); }
- virtual void visit(SuperExp *e) { visit((ThisExp *)e); }
- virtual void visit(NullExp *e) { visit((Expression *)e); }
- virtual void visit(StringExp *e) { visit((Expression *)e); }
- virtual void visit(TupleExp *e) { visit((Expression *)e); }
- virtual void visit(ArrayLiteralExp *e) { visit((Expression *)e); }
- virtual void visit(AssocArrayLiteralExp *e) { visit((Expression *)e); }
virtual void visit(StructLiteralExp *e) { visit((Expression *)e); }
- virtual void visit(TypeExp *e) { visit((Expression *)e); }
- virtual void visit(ScopeExp *e) { visit((Expression *)e); }
- virtual void visit(TemplateExp *e) { visit((Expression *)e); }
- virtual void visit(NewExp *e) { visit((Expression *)e); }
- virtual void visit(NewAnonClassExp *e) { visit((Expression *)e); }
- virtual void visit(SymbolExp *e) { visit((Expression *)e); }
+ virtual void visit(CompoundLiteralExp *e) { visit((Expression *)e); }
+ virtual void visit(ObjcClassReferenceExp *e) { visit((Expression *)e); }
virtual void visit(SymOffExp *e) { visit((SymbolExp *)e); }
- virtual void visit(VarExp *e) { visit((SymbolExp *)e); }
virtual void visit(OverExp *e) { visit((Expression *)e); }
- virtual void visit(FuncExp *e) { visit((Expression *)e); }
- virtual void visit(DeclarationExp *e) { visit((Expression *)e); }
- virtual void visit(TypeidExp *e) { visit((Expression *)e); }
- virtual void visit(TraitsExp *e) { visit((Expression *)e); }
virtual void visit(HaltExp *e) { visit((Expression *)e); }
- virtual void visit(IsExp *e) { visit((Expression *)e); }
- virtual void visit(UnaExp *e) { visit((Expression *)e); }
- virtual void visit(BinExp *e) { visit((Expression *)e); }
- virtual void visit(BinAssignExp *e) { visit((BinExp *)e); }
- virtual void visit(CompileExp *e) { visit((UnaExp *)e); }
- virtual void visit(ImportExp *e) { visit((UnaExp *)e); }
- virtual void visit(AssertExp *e) { visit((UnaExp *)e); }
- virtual void visit(DotIdExp *e) { visit((UnaExp *)e); }
virtual void visit(DotTemplateExp *e) { visit((UnaExp *)e); }
virtual void visit(DotVarExp *e) { visit((UnaExp *)e); }
- virtual void visit(DotTemplateInstanceExp *e) { visit((UnaExp *)e); }
virtual void visit(DelegateExp *e) { visit((UnaExp *)e); }
virtual void visit(DotTypeExp *e) { visit((UnaExp *)e); }
- virtual void visit(CallExp *e) { visit((UnaExp *)e); }
- virtual void visit(AddrExp *e) { visit((UnaExp *)e); }
- virtual void visit(PtrExp *e) { visit((UnaExp *)e); }
- virtual void visit(NegExp *e) { visit((UnaExp *)e); }
- virtual void visit(UAddExp *e) { visit((UnaExp *)e); }
- virtual void visit(ComExp *e) { visit((UnaExp *)e); }
- virtual void visit(NotExp *e) { visit((UnaExp *)e); }
- virtual void visit(DeleteExp *e) { visit((UnaExp *)e); }
- virtual void visit(CastExp *e) { visit((UnaExp *)e); }
virtual void visit(VectorExp *e) { visit((UnaExp *)e); }
virtual void visit(VectorArrayExp *e) { visit((UnaExp *)e); }
virtual void visit(SliceExp *e) { visit((UnaExp *)e); }
virtual void visit(ArrayLengthExp *e) { visit((UnaExp *)e); }
- virtual void visit(IntervalExp *e) { visit((Expression *)e); }
virtual void visit(DelegatePtrExp *e) { visit((UnaExp *)e); }
virtual void visit(DelegateFuncptrExp *e) { visit((UnaExp *)e); }
- virtual void visit(ArrayExp *e) { visit((UnaExp *)e); }
virtual void visit(DotExp *e) { visit((BinExp *)e); }
- virtual void visit(CommaExp *e) { visit((BinExp *)e); }
virtual void visit(IndexExp *e) { visit((BinExp *)e); }
- virtual void visit(PostExp *e) { visit((BinExp *)e); }
- virtual void visit(PreExp *e) { visit((UnaExp *)e); }
- virtual void visit(AssignExp *e) { visit((BinExp *)e); }
virtual void visit(ConstructExp *e) { visit((AssignExp *)e); }
virtual void visit(BlitExp *e) { visit((AssignExp *)e); }
- virtual void visit(AddAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(MinAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(MulAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(DivAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(ModAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(AndAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(OrAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(XorAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(PowAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(ShlAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(ShrAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(UshrAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(CatAssignExp *e) { visit((BinAssignExp *)e); }
- virtual void visit(AddExp *e) { visit((BinExp *)e); }
- virtual void visit(MinExp *e) { visit((BinExp *)e); }
- virtual void visit(CatExp *e) { visit((BinExp *)e); }
- virtual void visit(MulExp *e) { visit((BinExp *)e); }
- virtual void visit(DivExp *e) { visit((BinExp *)e); }
- virtual void visit(ModExp *e) { visit((BinExp *)e); }
- virtual void visit(PowExp *e) { visit((BinExp *)e); }
- virtual void visit(ShlExp *e) { visit((BinExp *)e); }
- virtual void visit(ShrExp *e) { visit((BinExp *)e); }
- virtual void visit(UshrExp *e) { visit((BinExp *)e); }
- virtual void visit(AndExp *e) { visit((BinExp *)e); }
- virtual void visit(OrExp *e) { visit((BinExp *)e); }
- virtual void visit(XorExp *e) { visit((BinExp *)e); }
- virtual void visit(LogicalExp *e) { visit((BinExp *)e); }
- virtual void visit(CmpExp *e) { visit((BinExp *)e); }
- virtual void visit(InExp *e) { visit((BinExp *)e); }
virtual void visit(RemoveExp *e) { visit((BinExp *)e); }
- virtual void visit(EqualExp *e) { visit((BinExp *)e); }
- virtual void visit(IdentityExp *e) { visit((BinExp *)e); }
- virtual void visit(CondExp *e) { visit((BinExp *)e); }
- virtual void visit(DefaultInitExp *e) { visit((Expression *)e); }
- virtual void visit(FileInitExp *e) { visit((DefaultInitExp *)e); }
- virtual void visit(LineInitExp *e) { visit((DefaultInitExp *)e); }
- virtual void visit(ModuleInitExp *e) { visit((DefaultInitExp *)e); }
- virtual void visit(FuncInitExp *e) { visit((DefaultInitExp *)e); }
- virtual void visit(PrettyFuncInitExp *e) { visit((DefaultInitExp *)e); }
virtual void visit(ClassReferenceExp *e) { visit((Expression *)e); }
virtual void visit(VoidInitExp *e) { visit((Expression *)e); }
virtual void visit(ThrownExceptionExp *e) { visit((Expression *)e); }
-
- virtual void visit(TemplateParameter *) { assert(0); }
- virtual void visit(TemplateTypeParameter *tp) { visit((TemplateParameter *)tp); }
- virtual void visit(TemplateThisParameter *tp) { visit((TemplateTypeParameter *)tp); }
- virtual void visit(TemplateValueParameter *tp) { visit((TemplateParameter *)tp); }
- virtual void visit(TemplateAliasParameter *tp) { visit((TemplateParameter *)tp); }
- virtual void visit(TemplateTupleParameter *tp) { visit((TemplateParameter *)tp); }
-
- virtual void visit(Condition *) { assert(0); }
- virtual void visit(DVCondition *c) { visit((Condition *)c); }
- virtual void visit(DebugCondition *c) { visit((DVCondition *)c); }
- virtual void visit(VersionCondition *c) { visit((DVCondition *)c); }
- virtual void visit(StaticIfCondition *c) { visit((Condition *)c); }
-
- virtual void visit(Parameter *) { assert(0); }
};
class StoppableVisitor : public Visitor
diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc
index ea21bd5a8a1..31680564bdd 100644
--- a/gcc/d/expr.cc
+++ b/gcc/d/expr.cc
@@ -249,7 +249,7 @@ public:
tree t1 = build_expr (e->e1);
tree t2 = build_expr (e->e2);
- if (e->type->ty != Tvoid)
+ if (e->type->ty != TY::Tvoid)
{
t1 = convert_expr (t1, e->e1->type, e->type);
t2 = convert_expr (t2, e->e2->type, e->type);
@@ -268,8 +268,8 @@ public:
Type *tb1 = e->e1->type->toBasetype ();
Type *tb2 = e->e2->type->toBasetype ();
- if ((tb1->ty == Tsarray || tb1->ty == Tarray)
- && (tb2->ty == Tsarray || tb2->ty == Tarray))
+ if ((tb1->ty == TY::Tsarray || tb1->ty == TY::Tarray)
+ && (tb2->ty == TY::Tsarray || tb2->ty == TY::Tarray))
{
/* For static and dynamic arrays, identity is defined as referring to
the same array elements and the same number of elements. */
@@ -278,7 +278,7 @@ public:
this->result_ = d_convert (build_ctype (e->type),
build_boolop (code, t1, t2));
}
- else if (tb1->isfloating () && tb1->ty != Tvector)
+ else if (tb1->isfloating () && tb1->ty != TY::Tvector)
{
/* For floating-point values, identity is defined as the bits in the
operands being identical. */
@@ -333,8 +333,8 @@ public:
Type *tb2 = e->e2->type->toBasetype ();
tree_code code = (e->op == TOKequal) ? EQ_EXPR : NE_EXPR;
- if ((tb1->ty == Tsarray || tb1->ty == Tarray)
- && (tb2->ty == Tsarray || tb2->ty == Tarray))
+ if ((tb1->ty == TY::Tsarray || tb1->ty == TY::Tarray)
+ && (tb2->ty == TY::Tsarray || tb2->ty == TY::Tarray))
{
/* For static and dynamic arrays, equality is defined as the lengths of
the arrays matching, and all the elements are equal. */
@@ -346,8 +346,9 @@ public:
e1.length == e2.length && memcmp(e1.ptr, e2.ptr, size) == 0;
Or when generating a NE expression:
e1.length != e2.length || memcmp(e1.ptr, e2.ptr, size) != 0; */
- if ((t1elem->isintegral () || t1elem->ty == Tvoid
- || (t1elem->ty == Tstruct && !t1elem->isTypeStruct ()->sym->xeq))
+ if ((t1elem->isintegral () || t1elem->ty == TY::Tvoid
+ || (t1elem->ty == TY::Tstruct
+ && !t1elem->isTypeStruct ()->sym->xeq))
&& t1elem->ty == t2elem->ty)
{
tree t1 = d_array_convert (e->e1);
@@ -368,7 +369,7 @@ public:
/* Compare arrays using memcmp if possible, otherwise for structs,
each field is compared inline. */
- if (t1elem->ty != Tstruct
+ if (t1elem->ty != TY::Tstruct
|| identity_compare_p (t1elem->isTypeStruct ()->sym))
{
tree size = size_mult_expr (t1len, size_int (t1elem->size ()));
@@ -398,7 +399,7 @@ public:
/* Finally, check if lengths of both arrays match if dynamic.
The frontend should have already guaranteed that static arrays
have same size. */
- if (tb1->ty == Tsarray && tb2->ty == Tsarray)
+ if (tb1->ty == TY::Tsarray && tb2->ty == TY::Tsarray)
gcc_assert (tb1->size () == tb2->size ());
else
{
@@ -444,7 +445,7 @@ public:
this->result_ = build_struct_comparison (code, ts->sym, t1, t2);
}
- else if (tb1->ty == Taarray && tb2->ty == Taarray)
+ else if (tb1->ty == TY::Taarray && tb2->ty == TY::Taarray)
{
/* Use _aaEqual() for associative arrays. */
tree result = build_libcall (LIBCALL_AAEQUAL, e->type, 3,
@@ -518,23 +519,14 @@ public:
gcc_unreachable ();
}
- if ((tb1->ty == Tsarray || tb1->ty == Tarray)
- && (tb2->ty == Tsarray || tb2->ty == Tarray))
+ /* For static and dynamic arrays, the relational op is turned into a
+ library call. It is not lowered during codegen. */
+ if ((tb1->ty == TY::Tsarray || tb1->ty == TY::Tarray)
+ && (tb2->ty == TY::Tsarray || tb2->ty == TY::Tarray))
{
- /* For static and dynamic arrays, the result of the relational op is
- the result of the operator applied to the first non-equal element
- of the array. If two arrays compare equal, but are of different
- lengths, the shorter array compares as less than the longer. */
- Type *telem = tb1->nextOf ()->toBasetype ();
-
- tree call = build_libcall (LIBCALL_ADCMP2, Type::tint32, 3,
- d_array_convert (e->e1),
- d_array_convert (e->e2),
- build_typeinfo (e->loc, telem->arrayOf ()));
- result = build_boolop (code, call, integer_zero_node);
-
- this->result_ = d_convert (build_ctype (e->type), result);
- return;
+ error ("cannot handle comparison of type %<%s == %s%>",
+ tb1->toChars (), tb2->toChars ());
+ gcc_unreachable ();
}
/* Simple comparison. */
@@ -550,7 +542,7 @@ public:
{
tree_code code = (e->op == TOKandand) ? TRUTH_ANDIF_EXPR : TRUTH_ORIF_EXPR;
- if (e->e2->type->toBasetype ()->ty != Tvoid)
+ if (e->e2->type->toBasetype ()->ty != TY::Tvoid)
{
tree t1 = build_expr (e->e1);
tree t2 = build_expr (e->e2);
@@ -618,7 +610,8 @@ public:
The front-end rewrites `(p1 - p2)' into `(p1 - p2) / stride'. */
if (MinExp *me = e->e1->isMinExp ())
{
- if (me->e1->type->ty == Tpointer && me->e2->type->ty == Tpointer
+ if (me->e1->type->ty == TY::Tpointer
+ && me->e2->type->ty == TY::Tpointer
&& e->e2->op == TOKint64)
{
code = EXACT_DIV_EXPR;
@@ -678,7 +671,7 @@ public:
Type *tb2 = e->e2->type->toBasetype ();
Type *etype;
- if (tb1->ty == Tarray || tb1->ty == Tsarray)
+ if (tb1->ty == TY::Tarray || tb1->ty == TY::Tsarray)
etype = tb1->nextOf ();
else
etype = tb2->nextOf ();
@@ -839,13 +832,13 @@ public:
tree ptr = d_save_expr (build_address (lhs));
tree result = NULL_TREE;
- if (tb1->ty == Tarray && tb2->ty == Tdchar
- && (etype->ty == Tchar || etype->ty == Twchar))
+ if (tb1->ty == TY::Tarray && tb2->ty == TY::Tdchar
+ && (etype->ty == TY::Tchar || etype->ty == TY::Twchar))
{
/* Append a dchar to a char[] or wchar[]:
The assignment is handled by the D run-time library, so only
need to call `_d_arrayappend[cw]d(&e1, e2)' */
- libcall_fn libcall = (etype->ty == Tchar)
+ libcall_fn libcall = (etype->ty == TY::Tchar)
? LIBCALL_ARRAYAPPENDCD : LIBCALL_ARRAYAPPENDWD;
result = build_libcall (libcall, e->type, 2,
@@ -853,9 +846,9 @@ public:
}
else
{
- gcc_assert (tb1->ty == Tarray || tb2->ty == Tsarray);
+ gcc_assert (tb1->ty == TY::Tarray || tb2->ty == TY::Tsarray);
- if ((tb2->ty == Tarray || tb2->ty == Tsarray)
+ if ((tb2->ty == TY::Tarray || tb2->ty == TY::Tsarray)
&& same_type_p (etype, tb2->nextOf ()->toBasetype ()))
{
/* Append an array to another array:
@@ -917,23 +910,9 @@ public:
/* Look for array.length = n; */
if (e->e1->op == TOKarraylength)
{
- /* Assignment to an array's length property; resize the array. */
- ArrayLengthExp *ale = e->e1->isArrayLengthExp ();
- tree newlength = convert_expr (build_expr (e->e2), e->e2->type,
- Type::tsize_t);
- tree ptr = build_address (build_expr (ale->e1));
-
- /* Don't want the basetype for the element type. */
- Type *etype = ale->e1->type->toBasetype ()->nextOf ();
- libcall_fn libcall = etype->isZeroInit ()
- ? LIBCALL_ARRAYSETLENGTHT : LIBCALL_ARRAYSETLENGTHIT;
-
- tree result = build_libcall (libcall, ale->e1->type, 3,
- build_typeinfo (ale->loc, ale->e1->type),
- newlength, ptr);
-
- this->result_ = d_array_length (result);
- return;
+ /* This case should have been rewritten to `_d_arraysetlengthT` in the
+ semantic phase. */
+ gcc_unreachable ();
}
/* Look for array[] = n; */
@@ -947,13 +926,17 @@ public:
bool postblit = needs_postblit (etype) && lvalue_p (e->e2);
bool destructor = needs_dtor (etype);
- if (e->memset & blockAssign)
+ if (e->memset == MemorySet::blockAssign)
{
/* Set a range of elements to one value. */
- tree t1 = d_save_expr (build_expr (e->e1));
+ tree t1 = build_expr (e->e1);
tree t2 = build_expr (e->e2);
tree result;
+ /* Extract any array bounds checks from the slice expression. */
+ tree init = stabilize_expr (&t1);
+ t1 = d_save_expr (t1);
+
if ((postblit || destructor) && e->op != TOKblit)
{
libcall_fn libcall = (e->op == TOKconstruct)
@@ -962,15 +945,12 @@ public:
Type *tm = etype->unSharedOf ()->mutableOf ();
tree ti = build_typeinfo (e->loc, tm);
- tree result = build_libcall (libcall, Type::tvoid, 4,
- d_array_ptr (t1),
- build_address (t2),
- d_array_length (t1), ti);
- this->result_ = compound_expr (result, t1);
- return;
+ result = build_libcall (libcall, Type::tvoid, 4,
+ d_array_ptr (t1),
+ build_address (t2),
+ d_array_length (t1), ti);
}
-
- if (integer_zerop (t2))
+ else if (integer_zerop (t2))
{
tree size = size_mult_expr (d_array_length (t1),
size_int (etype->size ()));
@@ -980,12 +960,13 @@ public:
result = build_array_set (d_array_ptr (t1),
d_array_length (t1), t2);
+ result = compound_expr (init, result);
this->result_ = compound_expr (result, t1);
}
else
{
/* Perform a memcpy operation. */
- gcc_assert (e->e2->type->ty != Tpointer);
+ gcc_assert (e->e2->type->ty != TY::Tpointer);
if (!postblit && !destructor)
{
@@ -1056,7 +1037,7 @@ public:
}
/* Look for reference initializations. */
- if (e->memset & referenceInit)
+ if (e->memset == MemorySet::referenceInit)
{
gcc_assert (e->op == TOKconstruct || e->op == TOKblit);
gcc_assert (e->e1->op == TOKvar);
@@ -1082,7 +1063,7 @@ public:
tree_code modifycode = (e->op == TOKconstruct) ? INIT_EXPR : MODIFY_EXPR;
/* Look for struct assignment. */
- if (tb1->ty == Tstruct)
+ if (tb1->ty == TY::Tstruct)
{
tree t1 = build_expr (e->e1);
tree t2 = convert_for_assignment (build_expr (e->e2, false, true),
@@ -1136,7 +1117,7 @@ public:
}
/* Look for static array assignment. */
- if (tb1->ty == Tsarray)
+ if (tb1->ty == TY::Tsarray)
{
/* Look for array = 0. */
if (e->e2->op == TOKint64)
@@ -1148,7 +1129,7 @@ public:
}
Type *etype = tb1->nextOf ();
- gcc_assert (e->e2->type->toBasetype ()->ty == Tsarray);
+ gcc_assert (e->e2->type->toBasetype ()->ty == TY::Tsarray);
/* Determine if we need to run postblit. */
bool postblit = needs_postblit (etype);
@@ -1171,7 +1152,8 @@ public:
return;
}
- Type *arrtype = (e->type->ty == Tsarray) ? etype->arrayOf () : e->type;
+ Type *arrtype = (e->type->ty == TY::Tsarray)
+ ? etype->arrayOf () : e->type;
tree result;
if (e->op == TOKconstruct)
@@ -1198,7 +1180,7 @@ public:
}
/* Cast the libcall result back to a static array. */
- if (e->type->ty == Tsarray)
+ if (e->type->ty == TY::Tsarray)
result = indirect_ref (build_ctype (e->type),
d_array_ptr (result));
@@ -1243,7 +1225,7 @@ public:
{
Type *tb1 = e->e1->type->toBasetype ();
- if (tb1->ty == Taarray)
+ if (tb1->ty == TY::Taarray)
{
/* Get the key for the associative array. */
Type *tkey = tb1->isTypeAArray ()->index->toBasetype ();
@@ -1289,7 +1271,7 @@ public:
tree ptr = convert_expr (array, tb1, tb1->nextOf ()->pointerTo ());
tree length = NULL_TREE;
- if (tb1->ty != Tpointer)
+ if (tb1->ty != TY::Tpointer)
length = get_array_length (array, tb1);
else
gcc_assert (e->lengthVar == NULL);
@@ -1304,7 +1286,7 @@ public:
/* If it's a static array and the index is constant, the front end has
already checked the bounds. */
- if (tb1->ty != Tpointer)
+ if (tb1->ty != TY::Tpointer)
index = build_bounds_index_condition (e, index, length);
/* Index the .ptr. */
@@ -1330,7 +1312,7 @@ public:
void visit (ArrayLengthExp *e)
{
- if (e->e1->type->toBasetype ()->ty == Tarray)
+ if (e->e1->type->toBasetype ()->ty == TY::Tarray)
this->result_ = d_array_length (build_expr (e->e1));
else
{
@@ -1364,13 +1346,13 @@ public:
{
Type *tb = e->type->toBasetype ();
Type *tb1 = e->e1->type->toBasetype ();
- gcc_assert (tb->ty == Tarray || tb->ty == Tsarray);
+ gcc_assert (tb->ty == TY::Tarray || tb->ty == TY::Tsarray);
/* Use convert-to-dynamic-array code if possible. */
if (!e->lwr)
{
tree result = build_expr (e->e1);
- if (e->e1->type->toBasetype ()->ty == Tsarray)
+ if (e->e1->type->toBasetype ()->ty == TY::Tsarray)
result = convert_expr (result, e->e1->type, e->type);
this->result_ = result;
@@ -1386,7 +1368,7 @@ public:
/* Our array is already a SAVE_EXPR if necessary, so we don't make length
a SAVE_EXPR which is, at most, a COMPONENT_REF on top of array. */
- if (tb1->ty != Tpointer)
+ if (tb1->ty != TY::Tpointer)
length = get_array_length (array, tb1);
else
gcc_assert (e->lengthVar == NULL);
@@ -1415,13 +1397,13 @@ public:
/* Nothing more to do for static arrays, their bounds checking has been
done at compile-time. */
- if (tb->ty == Tsarray)
+ if (tb->ty == TY::Tsarray)
{
this->result_ = indirect_ref (build_ctype (e->type), ptr);
return;
}
else
- gcc_assert (tb->ty == Tarray);
+ gcc_assert (tb->ty == TY::Tarray);
/* Generate bounds checking code. */
tree newlength = build_bounds_slice_condition (e, lwr_tree, upr_tree,
@@ -1440,7 +1422,7 @@ public:
tree result = build_expr (e->e1, this->constp_, this->literalp_);
/* Just evaluate e1 if it has any side effects. */
- if (tbtype->ty == Tvoid)
+ if (tbtype->ty == TY::Tvoid)
this->result_ = build_nop (build_ctype (tbtype), result);
else
this->result_ = convert_for_rvalue (result, ebtype, tbtype);
@@ -1453,7 +1435,7 @@ public:
tree t1 = build_expr (e->e1);
Type *tb1 = e->e1->type->toBasetype ();
- if (tb1->ty == Tclass)
+ if (tb1->ty == TY::Tclass)
{
/* For class object references, if there is a destructor for that class,
the destructor is called for the object instance. */
@@ -1480,7 +1462,7 @@ public:
t1 = build_address (t1);
this->result_ = build_libcall (libcall, Type::tvoid, 1, t1);
}
- else if (tb1->ty == Tarray)
+ else if (tb1->ty == TY::Tarray)
{
/* For dynamic arrays, the garbage collector is called to immediately
release the memory. */
@@ -1498,7 +1480,7 @@ public:
this->result_ = build_libcall (LIBCALL_DELARRAYT, Type::tvoid, 2,
build_address (t1), ti);
}
- else if (tb1->ty == Tpointer)
+ else if (tb1->ty == TY::Tpointer)
{
/* For pointers to a struct instance, if the struct has overloaded
operator delete, then that operator is called. */
@@ -1533,7 +1515,7 @@ public:
void visit (RemoveExp *e)
{
/* Check that the array is actually an associative array. */
- if (e->e1->type->toBasetype ()->ty == Taarray)
+ if (e->e1->type->toBasetype ()->ty == TY::Taarray)
{
Type *tb = e->e1->type->toBasetype ();
Type *tkey = tb->isTypeAArray ()->index->toBasetype ();
@@ -1569,7 +1551,7 @@ public:
void visit (ComExp *e)
{
TY ty1 = e->e1->type->toBasetype ()->ty;
- gcc_assert (ty1 != Tarray && ty1 != Tsarray);
+ gcc_assert (ty1 != TY::Tarray && ty1 != TY::Tsarray);
this->result_ = fold_build1 (BIT_NOT_EXPR, build_ctype (e->type),
build_expr (e->e1));
@@ -1580,7 +1562,7 @@ public:
void visit (NegExp *e)
{
TY ty1 = e->e1->type->toBasetype ()->ty;
- gcc_assert (ty1 != Tarray && ty1 != Tsarray);
+ gcc_assert (ty1 != TY::Tarray && ty1 != TY::Tsarray);
tree type = build_ctype (e->type);
tree expr = build_expr (e->e1);
@@ -1630,7 +1612,7 @@ public:
/* Produce better code by converting *(#record + n) to
COMPONENT_REFERENCE. Otherwise, the variable will always be
allocated in memory because its address is taken. */
- if (tnext && tnext->ty == Tstruct)
+ if (tnext && tnext->ty == TY::Tstruct)
{
StructDeclaration *sd = tnext->isTypeStruct ()->sym;
@@ -1716,7 +1698,7 @@ public:
gcc_assert (var->isFuncDeclaration () && !var->needThis ());
}
- if (e1b->op == TOKdotvar && tb->ty != Tdelegate)
+ if (e1b->op == TOKdotvar && tb->ty != TY::Tdelegate)
{
DotVarExp *dve = e1b->isDotVarExp ();
@@ -1775,7 +1757,7 @@ public:
/* C++ constructors return void, even though front-end semantic
treats them as implicitly returning `this'. Set returnvalue
to override the result of this expression. */
- if (fd->isCtorDeclaration () && fd->linkage == LINKcpp)
+ if (fd->isCtorDeclaration () && fd->linkage == LINK::cpp)
{
thisexp = d_save_expr (thisexp);
returnvalue = thisexp;
@@ -1805,7 +1787,7 @@ public:
extract_from_method_call (callee, callee, object);
}
- else if (tb->ty == Tdelegate)
+ else if (tb->ty == TY::Tdelegate)
{
/* Delegate call, extract .object and .funcptr from var. */
callee = d_save_expr (callee);
@@ -1850,7 +1832,7 @@ public:
if (returnvalue != NULL_TREE)
exp = compound_expr (exp, returnvalue);
- if (tf->isref)
+ if (tf->isref ())
exp = build_deref (exp);
/* Some library calls are defined to return a generic type.
@@ -1888,7 +1870,7 @@ public:
tree fndecl;
tree object;
- if (e->func->isNested ())
+ if (e->func->isNested () && !e->func->isThis ())
{
if (e->e1->op == TOKnull)
object = build_expr (e->e1);
@@ -1909,12 +1891,12 @@ public:
object = build_expr (e->e1);
/* Want reference to `this' object. */
- if (e->e1->type->ty != Tclass && e->e1->type->ty != Tpointer)
+ if (e->e1->type->ty != TY::Tclass && e->e1->type->ty != TY::Tpointer)
object = build_address (object);
/* Object reference could be the outer `this' field of a class or
closure of type `void*'. Cast it to the right type. */
- if (e->e1->type->ty == Tclass)
+ if (e->e1->type->ty == TY::Tclass)
object = d_convert (build_ctype (e->e1->type), object);
fndecl = get_symbol_decl (e->func);
@@ -1958,7 +1940,7 @@ public:
{
tree object = build_expr (e->e1);
- if (e->e1->type->toBasetype ()->ty != Tstruct)
+ if (e->e1->type->toBasetype ()->ty != TY::Tstruct)
object = build_deref (object);
this->result_ = component_ref (object, get_symbol_decl (vd));
@@ -2005,7 +1987,7 @@ public:
{
/* If the condition is a D class or struct object with an invariant,
call it if the condition result is true. */
- if (tb1->ty == Tclass)
+ if (tb1->ty == TY::Tclass)
{
ClassDeclaration *cd = tb1->isClassHandle ();
if (!cd->isInterfaceDeclaration () && !cd->isCPPclass ())
@@ -2015,7 +1997,8 @@ public:
Type::tvoid, 1, arg);
}
}
- else if (tb1->ty == Tpointer && tb1->nextOf ()->ty == Tstruct)
+ else if (tb1->ty == TY::Tpointer
+ && tb1->nextOf ()->ty == TY::Tstruct)
{
StructDeclaration *sd = tb1->nextOf ()->isTypeStruct ()->sym;
if (sd->inv != NULL)
@@ -2093,7 +2076,7 @@ public:
else if (Expression *tid = isExpression (e->obj))
{
Type *type = tid->type->toBasetype ();
- assert (type->ty == Tclass);
+ assert (type->ty == TY::Tclass);
/* Generate **classptr to get the classinfo. */
tree ci = build_expr (tid);
@@ -2117,7 +2100,7 @@ public:
Type *ftype = e->type->toBasetype ();
/* This check is for lambda's, remove `vthis' as function isn't nested. */
- if (e->fd->tok == TOKreserved && ftype->ty == Tpointer)
+ if (e->fd->tok == TOKreserved && ftype->ty == TY::Tpointer)
{
e->fd->tok = TOKfunction;
e->fd->vthis = NULL;
@@ -2226,7 +2209,7 @@ public:
tree init = NULL_TREE;
if (var && (var->isConst () || var->isImmutable ())
- && e->type->toBasetype ()->ty != Tsarray && var->_init)
+ && e->type->toBasetype ()->ty != TY::Tsarray && var->_init)
{
if (var->inuse)
error_at (make_location_t (e->loc), "recursive reference %qs",
@@ -2279,7 +2262,7 @@ public:
result = get_decl_tree (fd->vthis);
}
- if (e->type->ty == Tstruct)
+ if (e->type->ty == TY::Tstruct)
result = build_deref (result);
this->result_ = result;
@@ -2293,10 +2276,7 @@ public:
Type *tb = e->type->toBasetype ();
tree result;
- if (e->allocator)
- gcc_assert (e->newargs);
-
- if (tb->ty == Tclass)
+ if (tb->ty == TY::Tclass)
{
/* Allocating a new class. */
tb = e->newtype->toBasetype ();
@@ -2315,20 +2295,13 @@ public:
new_call = build_nop (type, build_address (var));
setup_exp = modify_expr (var, aggregate_initializer_decl (cd));
}
- else if (e->allocator)
- {
- /* Call class allocator, and copy the initializer into memory. */
- new_call = d_build_call_expr (e->allocator, NULL_TREE, e->newargs);
- new_call = d_save_expr (new_call);
- new_call = build_nop (type, new_call);
- setup_exp = modify_expr (build_deref (new_call),
- aggregate_initializer_decl (cd));
- }
else
{
/* Generate: _d_newclass() */
tree arg = build_address (get_classinfo_decl (cd));
- new_call = build_libcall (LIBCALL_NEWCLASS, tb, 1, arg);
+ libcall_fn libcall = (global.params.ehnogc && e->thrownew)
+ ? LIBCALL_NEWTHROW : LIBCALL_NEWCLASS;
+ new_call = build_libcall (libcall, tb, 1, arg);
}
/* Set the context pointer for nested classes. */
@@ -2340,13 +2313,15 @@ public:
if (e->thisexp)
{
ClassDeclaration *tcd = e->thisexp->type->isClassHandle ();
- Dsymbol *outer = cd->toParent2 ();
- int offset = 0;
+ /* The class or function we're nested in. */
+ Dsymbol *outer = cd->toParentLocal ();
value = build_expr (e->thisexp);
+
if (outer != tcd)
{
ClassDeclaration *ocd = outer->isClassDeclaration ();
+ int offset = 0;
gcc_assert (ocd->isBaseOf (tcd, &offset));
/* Could just add offset... */
value = convert_expr (value, e->thisexp->type, ocd->type);
@@ -2375,7 +2350,8 @@ public:
if (e->argprefix)
result = compound_expr (build_expr (e->argprefix), result);
}
- else if (tb->ty == Tpointer && tb->nextOf ()->toBasetype ()->ty == Tstruct)
+ else if (tb->ty == TY::Tpointer
+ && tb->nextOf ()->toBasetype ()->ty == TY::Tstruct)
{
/* Allocating memory for a new struct. */
Type *htype = e->newtype->toBasetype ();
@@ -2393,20 +2369,11 @@ public:
return;
}
- if (e->allocator)
- {
- /* Call struct allocator. */
- new_call = d_build_call_expr (e->allocator, NULL_TREE, e->newargs);
- new_call = build_nop (build_ctype (tb), new_call);
- }
- else
- {
- /* Generate: _d_newitemT() */
- libcall_fn libcall = htype->isZeroInit ()
- ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT;
- tree arg = build_typeinfo (e->loc, e->newtype);
- new_call = build_libcall (libcall, tb, 1, arg);
- }
+ /* Generate: _d_newitemT() */
+ libcall_fn libcall = htype->isZeroInit ()
+ ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT;
+ tree arg = build_typeinfo (e->loc, e->newtype);
+ new_call = build_libcall (libcall, tb, 1, arg);
if (e->member || !e->arguments)
{
@@ -2449,13 +2416,12 @@ public:
if (e->argprefix)
result = compound_expr (build_expr (e->argprefix), result);
}
- else if (tb->ty == Tarray)
+ else if (tb->ty == TY::Tarray)
{
/* Allocating memory for a new D array. */
tb = e->newtype->toBasetype ();
TypeDArray *tarray = tb->isTypeDArray ();
- gcc_assert (!e->allocator);
gcc_assert (e->arguments && e->arguments->length >= 1);
if (e->arguments->length == 1)
@@ -2492,7 +2458,7 @@ public:
Expression *arg = (*e->arguments)[i];
CONSTRUCTOR_APPEND_ELT (elms, size_int (i), build_expr (arg));
- gcc_assert (telem->ty == Tarray);
+ gcc_assert (telem->ty == TY::Tarray);
telem = telem->toBasetype ()->nextOf ();
gcc_assert (telem);
}
@@ -2517,7 +2483,7 @@ public:
if (e->argprefix)
result = compound_expr (build_expr (e->argprefix), result);
}
- else if (tb->ty == Tpointer)
+ else if (tb->ty == TY::Tpointer)
{
/* Allocating memory for a new pointer. */
TypePointer *tpointer = tb->isTypePointer ();
@@ -2576,15 +2542,15 @@ public:
switch (e->type->toBasetype ()->ty)
{
- case Tcomplex32:
+ case TY::Tcomplex32:
tnext = (TypeBasic *) Type::tfloat32;
break;
- case Tcomplex64:
+ case TY::Tcomplex64:
tnext = (TypeBasic *) Type::tfloat64;
break;
- case Tcomplex80:
+ case TY::Tcomplex80:
tnext = (TypeBasic *) Type::tfloat80;
break;
@@ -2605,7 +2571,7 @@ public:
Type *tb = e->type->toBasetype ();
tree type = build_ctype (e->type);
- if (tb->ty == Tsarray)
+ if (tb->ty == TY::Tsarray)
{
/* Turn the string into a constructor for the static array. */
vec <constructor_elt, va_gc> *elms = NULL;
@@ -2635,7 +2601,7 @@ public:
TREE_TYPE (value) = make_array_type (tb->nextOf (), length + 1);
value = build_address (value);
- if (tb->ty == Tarray)
+ if (tb->ty == TY::Tarray)
value = d_array_value (type, size_int (e->len), value);
TREE_CONSTANT (value) = 1;
@@ -2674,15 +2640,16 @@ public:
Type *tb = e->type->toBasetype ();
/* Implicitly convert void[n] to ubyte[n]. */
- if (tb->ty == Tsarray && tb->nextOf ()->toBasetype ()->ty == Tvoid)
+ if (tb->ty == TY::Tsarray && tb->nextOf ()->toBasetype ()->ty == TY::Tvoid)
tb = Type::tuns8->sarrayOf (tb->isTypeSArray ()->dim->toUInteger ());
- gcc_assert (tb->ty == Tarray || tb->ty == Tsarray || tb->ty == Tpointer);
+ gcc_assert (tb->ty == TY::Tarray || tb->ty == TY::Tsarray
+ || tb->ty == TY::Tpointer);
/* Handle empty array literals. */
if (e->elements->length == 0)
{
- if (tb->ty == Tarray)
+ if (tb->ty == TY::Tarray)
this->result_ = d_array_value (build_ctype (e->type),
size_int (0), null_pointer_node);
else
@@ -2732,15 +2699,15 @@ public:
tree type = build_ctype (e->type);
/* Nothing else to do for static arrays. */
- if (tb->ty == Tsarray || this->constp_)
+ if (tb->ty == TY::Tsarray || this->constp_)
{
/* Can't take the address of the constructor, so create an anonymous
static symbol, and then refer to it. */
- if (tb->ty != Tsarray)
+ if (tb->ty != TY::Tsarray)
{
tree decl = build_artificial_decl (TREE_TYPE (ctor), ctor, "A");
ctor = build_address (decl);
- if (tb->ty == Tarray)
+ if (tb->ty == TY::Tarray)
ctor = d_array_value (type, size_int (e->elements->length), ctor);
d_pushdecl (decl);
@@ -2789,7 +2756,7 @@ public:
/* Return the array pointed to by MEM. */
result = compound_expr (result, mem);
- if (tb->ty == Tarray)
+ if (tb->ty == TY::Tarray)
result = d_array_value (type, size_int (e->elements->length), result);
this->result_ = compound_expr (saved_elems, result);
@@ -2882,7 +2849,7 @@ public:
gcc_assert (e->elements->length <= e->sd->fields.length);
Type *tb = e->type->toBasetype ();
- gcc_assert (tb->ty == Tstruct);
+ gcc_assert (tb->ty == TY::Tstruct);
for (size_t i = 0; i < e->elements->length; i++)
{
@@ -2895,7 +2862,7 @@ public:
Type *ftype = field->type->toBasetype ();
tree value = NULL_TREE;
- if (ftype->ty == Tsarray && !same_type_p (type, ftype))
+ if (ftype->ty == TY::Tsarray && !same_type_p (type, ftype))
{
/* Initialize a static array with a single element. */
tree elem = build_expr (exp, this->constp_, true);
@@ -3126,7 +3093,7 @@ build_return_dtor (Expression *e, Type *type, TypeFunction *tf)
tree result = build_expr (e);
/* Convert for initializing the DECL_RESULT. */
- if (tf->isref)
+ if (tf->isref ())
{
/* If we are returning a reference, take the address. */
result = convert_expr (result, e->type, type);
diff --git a/gcc/d/imports.cc b/gcc/d/imports.cc
index 2288843c61a..740a020321c 100644
--- a/gcc/d/imports.cc
+++ b/gcc/d/imports.cc
@@ -68,7 +68,7 @@ public:
void visit (Module *m)
{
Loc loc = (m->md != NULL) ? m->md->loc
- : Loc (m->srcfile->toChars (), 1, 0);
+ : Loc (m->srcfile.toChars (), 1, 0);
m->isym = build_decl (make_location_t (loc), NAMESPACE_DECL,
get_identifier (m->toPrettyChars ()),
@@ -130,11 +130,11 @@ public:
/* Type imports should really be part of their own visit method. */
if (type != NULL)
{
- if (type->ty == Tenum)
+ if (type->ty == TY::Tenum)
dsym = type->isTypeEnum ()->sym;
- else if (type->ty == Tstruct)
+ else if (type->ty == TY::Tstruct)
dsym = type->isTypeStruct ()->sym;
- else if (type->ty == Tclass)
+ else if (type->ty == TY::Tclass)
dsym = type->isTypeClass ()->sym;
}
}
diff --git a/gcc/d/intrinsics.cc b/gcc/d/intrinsics.cc
index 539dc0c1f37..b14b0ca5111 100644
--- a/gcc/d/intrinsics.cc
+++ b/gcc/d/intrinsics.cc
@@ -20,9 +20,9 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "dmd/declaration.h"
+#include "dmd/expression.h"
#include "dmd/identifier.h"
#include "dmd/mangle.h"
-#include "dmd/mangle.h"
#include "dmd/module.h"
#include "dmd/template.h"
@@ -76,12 +76,12 @@ static const intrinsic_decl intrinsic_decls[] =
void
maybe_set_intrinsic (FuncDeclaration *decl)
{
- if (!decl->ident || decl->builtin != BUILTINunknown)
+ if (!decl->ident || decl->builtin != BUILTIN::unknown)
return;
/* The builtin flag is updated only if we can evaluate the intrinsic
at compile-time. Such as the math or bitop intrinsics. */
- decl->builtin = BUILTINunimp;
+ decl->builtin = BUILTIN::unimp;
/* Check if it's a compiler intrinsic. We only require that any
internally recognised intrinsics are declared in a module with
@@ -177,12 +177,12 @@ maybe_set_intrinsic (FuncDeclaration *decl)
built-in function. It could be `int pow(int, int)'. */
tree rettype = TREE_TYPE (TREE_TYPE (decl->csym));
if (mathfn_built_in (rettype, BUILT_IN_POW) != NULL_TREE)
- decl->builtin = BUILTINgcc;
+ decl->builtin = BUILTIN::gcc;
break;
}
default:
- decl->builtin = BUILTINgcc;
+ decl->builtin = BUILTIN::gcc;
break;
}
diff --git a/gcc/d/intrinsics.def b/gcc/d/intrinsics.def
index f5af2a5fe5c..1f350f3c236 100644
--- a/gcc/d/intrinsics.def
+++ b/gcc/d/intrinsics.def
@@ -77,12 +77,12 @@ DEF_D_BUILTIN (INTRINSIC_POPCNT32, BUILT_IN_NONE, "popcnt", "core.bitop",
DEF_D_BUILTIN (INTRINSIC_POPCNT64, BUILT_IN_NONE, "popcnt", "core.bitop",
"FNaNbNiNfmZi")
-DEF_D_BUILTIN (INTRINSIC_ROL, BUILT_IN_NONE, "rol", "core.bitop", "FNaI1TkZI1T")
+DEF_D_BUILTIN (INTRINSIC_ROL, BUILT_IN_NONE, "rol", "core.bitop", "FNa@1TkZ@1T")
DEF_D_BUILTIN (INTRINSIC_ROL_TIARG, BUILT_IN_NONE, "rol", "core.bitop",
- "FNaI1TZI1T")
-DEF_D_BUILTIN (INTRINSIC_ROR, BUILT_IN_NONE, "ror", "core.bitop", "FNaI1TkZI1T")
+ "FNa@1TZ@1T")
+DEF_D_BUILTIN (INTRINSIC_ROR, BUILT_IN_NONE, "ror", "core.bitop", "FNa@1TkZ@1T")
DEF_D_BUILTIN (INTRINSIC_ROR_TIARG, BUILT_IN_NONE, "ror", "core.bitop",
- "FNaI1TZI1T")
+ "FNa@1TZ@1T")
/* core.volatile intrinsics. */
@@ -183,75 +183,74 @@ DEF_D_BUILTIN (INTRINSIC_SQRT, BUILT_IN_SQRT, "sqrt", "core.math",
DEF_D_BUILTIN (INTRINSIC_SQRTL, BUILT_IN_SQRTL, "sqrt", "core.math",
"FNaNbNiNfeZe")
DEF_D_BUILTIN (INTRINSIC_TOPRECF, BUILT_IN_NONE, "toPrec", "core.math",
- "FfZI1T")
-DEF_D_BUILTIN (INTRINSIC_TOPREC, BUILT_IN_NONE, "toPrec", "core.math", "FdZI1T")
+ "FfZ@1T")
+DEF_D_BUILTIN (INTRINSIC_TOPREC, BUILT_IN_NONE, "toPrec", "core.math", "FdZ@1T")
DEF_D_BUILTIN (INTRINSIC_TOPRECL, BUILT_IN_NONE, "toPrec", "core.math",
- "FeZI1T")
+ "FeZ@1T")
/* std.math intrinsics. */
-DEF_CTFE_BUILTIN (INTRINSIC_TAN, BUILT_IN_TANL, "tan", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_TAN, BUILT_IN_TANL, "tan", "std.math.trigonometry",
"FNaNbNiNeeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_ISNAN, BUILT_IN_ISNAN, "isNaN", "std.math",
- "FNaNbNiNeI1XZb")
+
+DEF_CTFE_BUILTIN (INTRINSIC_ISNAN, BUILT_IN_ISNAN, "isNaN", "std.math.traits",
+ "FNaNbNiNe@1XZb")
DEF_CTFE_BUILTIN (INTRINSIC_ISINFINITY, BUILT_IN_ISINF, "isInfinity",
- "std.math", "FNaNbNiNeI1XZb")
-DEF_CTFE_BUILTIN (INTRINSIC_ISFINITE, BUILT_IN_ISFINITE, "isFinite", "std.math",
- "FNaNbNiNeI1XZb")
+ "std.math.traits", "FNaNbNiNe@1XZb")
+DEF_CTFE_BUILTIN (INTRINSIC_ISFINITE, BUILT_IN_ISFINITE, "isFinite",
+ "std.math.traits", "FNaNbNiNe@1XZb")
+DEF_CTFE_BUILTIN (INTRINSIC_COPYSIGN, BUILT_IN_NONE, "copysign",
+ "std.math.traits", "FNaNbNiNe@1R@1XZ@1R")
+DEF_CTFE_BUILTIN (INTRINSIC_COPYSIGNI, BUILT_IN_NONE, "copysign",
+ "std.math.traits", "FNaNbNiNe@1X@1RZ@1R")
-DEF_CTFE_BUILTIN (INTRINSIC_EXP, BUILT_IN_EXPL, "exp", "std.math",
- "FNaNbNiNeeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_EXPM1, BUILT_IN_EXPM1L, "expm1", "std.math",
- "FNaNbNiNeeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_EXP2, BUILT_IN_EXP2L, "exp2", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_EXP, BUILT_IN_EXPL, "exp", "std.math.exponential",
"FNaNbNiNeeZe")
-
-DEF_CTFE_BUILTIN (INTRINSIC_LOG, BUILT_IN_LOGL, "log", "std.math",
- "FNaNbNiNfeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_LOG2, BUILT_IN_LOG2L, "log2", "std.math",
- "FNaNbNiNfeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_LOG10, BUILT_IN_LOG10L, "log10", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_EXPM1, BUILT_IN_EXPM1L, "expm1",
+ "std.math.exponential", "FNaNbNiNeeZe")
+DEF_CTFE_BUILTIN (INTRINSIC_EXP2, BUILT_IN_EXP2L, "exp2",
+ "std.math.exponential", "FNaNbNiNeeZe")
+DEF_CTFE_BUILTIN (INTRINSIC_LOG, BUILT_IN_LOGL, "log", "std.math.exponential",
"FNaNbNiNfeZe")
+DEF_CTFE_BUILTIN (INTRINSIC_LOG2, BUILT_IN_LOG2L, "log2",
+ "std.math.exponential", "FNaNbNiNfeZe")
+DEF_CTFE_BUILTIN (INTRINSIC_LOG10, BUILT_IN_LOG10L, "log10",
+ "std.math.exponential", "FNaNbNiNfeZe")
+DEF_CTFE_BUILTIN (INTRINSIC_POW, BUILT_IN_NONE, "pow", "std.math.exponential",
+ "FNaNbNiNe@1F@1GZ@")
-DEF_CTFE_BUILTIN (INTRINSIC_ROUND, BUILT_IN_ROUNDL, "round", "std.math",
- "FNbNiNeeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_FLOORF, BUILT_IN_FLOORF, "floor", "std.math",
- "FNaNbNiNefZf")
-DEF_CTFE_BUILTIN (INTRINSIC_FLOOR, BUILT_IN_FLOOR, "floor", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_ROUND, BUILT_IN_ROUNDL, "round",
+ "std.math.rounding", "FNaNbNiNeeZe")
+DEF_CTFE_BUILTIN (INTRINSIC_FLOORF, BUILT_IN_FLOORF, "floor",
+ "std.math.rounding", "FNaNbNiNefZf")
+DEF_CTFE_BUILTIN (INTRINSIC_FLOOR, BUILT_IN_FLOOR, "floor", "std.math.rounding",
"FNaNbNiNedZd")
-DEF_CTFE_BUILTIN (INTRINSIC_FLOORL, BUILT_IN_FLOORL, "floor", "std.math",
- "FNaNbNiNeeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_CEILF, BUILT_IN_CEILF, "ceil", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_FLOORL, BUILT_IN_FLOORL, "floor",
+ "std.math.rounding", "FNaNbNiNeeZe")
+DEF_CTFE_BUILTIN (INTRINSIC_CEILF, BUILT_IN_CEILF, "ceil", "std.math.rounding",
"FNaNbNiNefZf")
-DEF_CTFE_BUILTIN (INTRINSIC_CEIL, BUILT_IN_CEIL, "ceil", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_CEIL, BUILT_IN_CEIL, "ceil", "std.math.rounding",
"FNaNbNiNedZd")
-DEF_CTFE_BUILTIN (INTRINSIC_CEILL, BUILT_IN_CEILL, "ceil", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_CEILL, BUILT_IN_CEILL, "ceil", "std.math.rounding",
"FNaNbNiNeeZe")
+DEF_CTFE_BUILTIN (INTRINSIC_TRUNC, BUILT_IN_TRUNCL, "trunc",
+ "std.math.rounding", "FNaNbNiNeeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_TRUNC, BUILT_IN_TRUNCL, "trunc", "std.math",
- "FNbNiNeeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_FMIN, BUILT_IN_FMINL, "fmin", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_FMIN, BUILT_IN_FMINL, "fmin", "std.math.operations",
"FNaNbNiNfeeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_FMAX, BUILT_IN_FMAXL, "fmax", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_FMAX, BUILT_IN_FMAXL, "fmax", "std.math.operations",
"FNaNbNiNfeeZe")
-DEF_CTFE_BUILTIN (INTRINSIC_COPYSIGN, BUILT_IN_NONE, "copysign", "std.math",
- "FNaNbNiNeI1RI1XZI1R")
-DEF_CTFE_BUILTIN (INTRINSIC_COPYSIGNI, BUILT_IN_NONE, "copysign", "std.math",
- "FNaNbNiNeI1XI1RZI1R")
-
-DEF_CTFE_BUILTIN (INTRINSIC_POW, BUILT_IN_NONE, "pow", "std.math",
- "FNaNbNiNeI1FI1GZ@")
-DEF_CTFE_BUILTIN (INTRINSIC_FMA, BUILT_IN_FMAL, "fma", "std.math",
+DEF_CTFE_BUILTIN (INTRINSIC_FMA, BUILT_IN_FMAL, "fma", "std.math.operations",
"FNaNbNiNfeeeZe")
/* core.stdc.stdarg intrinsics. */
DEF_D_BUILTIN (INTRINSIC_VA_ARG, BUILT_IN_NONE, "va_arg", "core.stdc.stdarg",
- "FKI7va_listKI1TZv")
+ "FK@7va_listK@1TZv")
DEF_D_BUILTIN (INTRINSIC_C_VA_ARG, BUILT_IN_NONE, "va_arg", "core.stdc.stdarg",
- "FKI7va_listZI1T")
+ "FK@7va_listZ@1T")
DEF_D_BUILTIN (INTRINSIC_VASTART, BUILT_IN_NONE, "va_start", "core.stdc.stdarg",
- "FJI7va_listKI1TZv")
+ "FJ@7va_listK@1TZv")
#undef DEF_D_BUILTIN
#undef DEF_CTFE_BUILTIN
diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt
index ded218fc5e3..f7f90fb08c3 100644
--- a/gcc/d/lang.opt
+++ b/gcc/d/lang.opt
@@ -209,6 +209,40 @@ fbuiltin
D Var(flag_no_builtin, 0)
; Documented in C
+fcheck=assert
+D Alias(fassert)
+
+fcheck=bounds
+D Alias(fbounds-check)
+
+fcheck=in
+D Alias(fpreconditions)
+
+fcheck=invariant
+D Alias(finvariants)
+
+fcheck=out
+D Alias(fpostconditions)
+
+fcheck=switch
+D Alias(fswitch-errors)
+
+fcheckaction=
+D Joined RejectNegative Enum(check_action) Var(flag_check_action)
+-fcheckaction=[throw,halt,context] Behavior on contract failure.
+
+Enum
+Name(check_action) Type(int) UnknownError(unknown checkaction setting %qs)
+
+EnumValue
+Enum(check_action) String(throw) Value(0)
+
+EnumValue
+Enum(check_action) String(halt) Value(1)
+
+EnumValue
+Enum(check_action) String(context) Value(2)
+
fdebug
D
Compile in debug code.
@@ -237,10 +271,43 @@ fdruntime
D
Assume that standard D runtime libraries and \"D main\" exist.
+fdump-c++-spec-verbose
+D RejectNegative
+Add comments for ignored declarations in the generated C++ header.
+
+fdump-c++-spec=
+D RejectNegative Joined
+-fdump-cxx-spec=<filename> Write all declarations as C++ code to <file>.
+
fdump-d-original
D
Display the frontend AST after parsing and semantic passes.
+fextern-std=
+D Joined RejectNegative Enum(extern_stdcpp) Var(flag_extern_stdcpp)
+-fextern-std=<standard> Set C++ name mangling compatibility with <standard>.
+
+Enum
+Name(extern_stdcpp) Type(int) UnknownError(unknown C++ standard %qs)
+
+EnumValue
+Enum(extern_stdcpp) String(c++98) Value(199711)
+
+EnumValue
+Enum(extern_stdcpp) String(c++03) Value(199711)
+
+EnumValue
+Enum(extern_stdcpp) String(c++11) Value(201103)
+
+EnumValue
+Enum(extern_stdcpp) String(c++14) Value(201402)
+
+EnumValue
+Enum(extern_stdcpp) String(c++17) Value(201703)
+
+EnumValue
+Enum(extern_stdcpp) String(c++20) Value(202002)
+
fignore-unknown-pragmas
D
Ignore unsupported pragmas.
@@ -273,33 +340,97 @@ fpreconditions
D Var(flag_preconditions)
Generate code for precondition contracts.
+fpreview=all
+D RejectNegative
+Turn on all upcoming D language features.
+
+fpreview=dip1000
+D RejectNegative
+Implement DIP1000: Scoped pointers.
+
+fpreview=dip1008
+D RejectNegative
+Implement DIP1008: Allow exceptions in @nogc code.
+
+fpreview=dip1021
+D RejectNegative
+Implement DIP1021: Mutable function arguments.
+
+fpreview=dip25
+D RejectNegative
+Implement DIP25: Sealed references.
+
+fpreview=dtorfields
+D RejectNegative
+Destruct fields of partially constructed objects.
+
+fpreview=fieldwise
+D RejectNegative
+Use field-wise comparisons for struct equality.
+
+fpreview=fixaliasthis
+D RejectNegative
+When a symbol is resolved, check alias this scope before going to upper scopes.
+
+fpreview=in
+D RejectNegative
+Implement 'in' parameters to mean scope const.
+
+fpreview=inclusiveincontracts
+D RejectNegative
+Implement 'in' contracts of overridden methods to be a superset of parent contract.
+
+fpreview=intpromote
+D RejectNegative
+Use C-style integral promotion for unary '+', '-' and '~'.
+
+fpreview=nosharedaccess
+D RejectNegative
+Disable access to shared memory objects.
+
+fpreview=rvaluerefparam
+D RejectNegative
+Enable rvalue arguments to ref parameters.
+
+fpreview=shortenedmethods
+D RejectNegative
+Allow use of '=>' for methods and top-level functions in addition to lambdas.
+
frelease
D
Compile release version.
+frevert=all
+D RejectNegative
+Turn off all revertable D language features.
+
+frevert=dip25
+D RejectNegative
+Revert DIP25: Sealed references.
+
+frevert=dtorfields
+D RejectNegative
+Don't destruct fields of partially constructed objects.
+
+frevert=markdown
+D RejectNegative
+Disable Markdown replacements in Ddoc.
+
frtti
D
; Documented in C
+fsave-mixins=
+D Joined RejectNegative
+-fsave-mixins=<filename> Expand and save mixins to file specified by <filename>.
+
fswitch-errors
D Var(flag_switch_errors)
Generate code for switches without a default case.
ftransition=all
D RejectNegative
-List information on all language changes.
-
-ftransition=complex
-D RejectNegative
-List all usages of complex or imaginary types.
-
-ftransition=dip1000
-D RejectNegative
-Implement DIP1000: Scoped pointers (experimental).
-
-ftransition=dip25
-D RejectNegative
-Implement DIP25: Sealed references (experimental).
+List information on all D language transitions.
ftransition=field
D RejectNegative
@@ -309,10 +440,18 @@ ftransition=nogc
D RejectNegative
List all hidden GC allocations.
+ftransition=templates
+D RejectNegative
+List statistics on template instantiations.
+
ftransition=tls
D RejectNegative
List all variables going into thread local storage.
+ftransition=vmarkdown
+D RejectNegative
+List instances of Markdown replacements in Ddoc.
+
funittest
D
Compile in unittest code.
diff --git a/gcc/d/modules.cc b/gcc/d/modules.cc
index 8786344d954..06eb5ae3424 100644
--- a/gcc/d/modules.cc
+++ b/gcc/d/modules.cc
@@ -125,7 +125,7 @@ static Module *current_module_decl;
by both module initialization and dso handlers. */
static FuncDeclaration *
-get_internal_fn (tree ident, const Prot &prot)
+get_internal_fn (tree ident, const Visibility &visibility)
{
Module *mod = current_module_decl;
const char *name = IDENTIFIER_POINTER (ident);
@@ -142,9 +142,9 @@ get_internal_fn (tree ident, const Prot &prot)
FuncDeclaration *fd = FuncDeclaration::genCfunc (NULL, Type::tvoid,
Identifier::idPool (name));
fd->generated = true;
- fd->loc = Loc (mod->srcfile->toChars (), 1, 0);
+ fd->loc = Loc (mod->srcfile.toChars (), 1, 0);
fd->parent = mod;
- fd->protection = prot;
+ fd->visibility = visibility;
fd->semanticRun = PASSsemantic3done;
return fd;
@@ -156,7 +156,9 @@ get_internal_fn (tree ident, const Prot &prot)
static tree
build_internal_fn (tree ident, tree expr)
{
- FuncDeclaration *fd = get_internal_fn (ident, Prot (Prot::private_));
+ Visibility visibility;
+ visibility.kind = Visibility::private_;
+ FuncDeclaration *fd = get_internal_fn (ident, visibility);
tree decl = get_symbol_decl (fd);
tree old_context = start_function (fd);
@@ -338,8 +340,9 @@ build_dso_cdtor_fn (bool ctor_p)
}
}
*/
- FuncDeclaration *fd = get_internal_fn (get_identifier (name),
- Prot (Prot::public_));
+ Visibility visibility;
+ visibility.kind = Visibility::public_;
+ FuncDeclaration *fd = get_internal_fn (get_identifier (name), visibility);
tree decl = get_symbol_decl (fd);
TREE_PUBLIC (decl) = 1;
@@ -740,7 +743,8 @@ build_module_tree (Module *decl)
/* Associate the module info symbol with a mock module. */
const char *name = concat (GDC_PREFIX ("modtest__"),
decl->ident->toChars (), NULL);
- Module *tm = Module::create (decl->arg, Identifier::idPool (name), 0, 0);
+ Module *tm = Module::create (decl->arg.ptr, Identifier::idPool (name),
+ 0, 0);
Dsymbols members;
/* Setting parent puts module in the same package as the current, to
@@ -780,9 +784,7 @@ build_module_tree (Module *decl)
/* Default behavior is to always generate module info because of templates.
Can be switched off for not compiling against runtime library. */
- if (global.params.useModuleInfo
- && Module::moduleinfo != NULL
- && decl->ident != Identifier::idPool ("__entrypoint"))
+ if (global.params.useModuleInfo && Module::moduleinfo != NULL)
{
if (mi.ctors || mi.ctorgates)
decl->sctor = build_funcs_gates_fn (get_identifier ("*__modctor"),
diff --git a/gcc/d/runtime.def b/gcc/d/runtime.def
index 0296233f4e2..fdff6db0626 100644
--- a/gcc/d/runtime.def
+++ b/gcc/d/runtime.def
@@ -34,6 +34,8 @@ along with GCC; see the file COPYING3. If not see
3, LCT_ ## T1, LCT_ ## T2, LCT_ ## T3
#define P4(T1, T2, T3, T4) \
4, LCT_ ## T1, LCT_ ## T2, LCT_ ## T3, LCT_ ## T4
+#define P5(T1, T2, T3, T4, T5) \
+ 5, LCT_ ## T1, LCT_ ## T2, LCT_ ## T3, LCT_ ## T4, LCT_ ## T5
#define RT(T1) LCT_ ## T1
/* Used when an assert() contract fails. */
@@ -51,9 +53,15 @@ DEF_D_RUNTIME (UNITTEST_MSG, "_d_unittest_msg", RT(VOID),
/* Used when an array index outside the bounds of its range. */
DEF_D_RUNTIME (ARRAYBOUNDSP, "_d_arrayboundsp", RT(VOID),
P2(IMMUTABLE_CHARPTR, UINT), ECF_NORETURN)
+DEF_D_RUNTIME (ARRAYBOUNDS_SLICEP, "_d_arraybounds_slicep", RT(VOID),
+ P5(IMMUTABLE_CHARPTR, UINT, SIZE_T, SIZE_T, SIZE_T),
+ ECF_NORETURN)
+DEF_D_RUNTIME (ARRAYBOUNDS_INDEXP, "_d_arraybounds_indexp", RT(VOID),
+ P4(IMMUTABLE_CHARPTR, UINT, SIZE_T, SIZE_T), ECF_NORETURN)
/* Used when calling new on a class. */
DEF_D_RUNTIME (NEWCLASS, "_d_newclass", RT(OBJECT), P1(CONST_CLASSINFO), 0)
+DEF_D_RUNTIME (NEWTHROW, "_d_newThrowable", RT(OBJECT), P1(CONST_CLASSINFO), 0)
/* Used when calling delete on a class or interface. */
DEF_D_RUNTIME (DELCLASS, "_d_delclass", RT(VOID), P1(VOIDPTR), 0)
@@ -104,13 +112,6 @@ DEF_D_RUNTIME (DELARRAYT, "_d_delarray_t", RT(VOID),
arrays. Such as an array of structs or classes. */
DEF_D_RUNTIME (ADEQ2, "_adEq2", RT(INT),
P3(ARRAY_VOID, ARRAY_VOID, CONST_TYPEINFO), 0)
-DEF_D_RUNTIME (ADCMP2, "_adCmp2", RT(INT),
- P3(ARRAY_VOID, ARRAY_VOID, CONST_TYPEINFO), 0)
-
-/* Used when casting from one array type to another where the index type
- sizes differ. Such as from int[] to short[]. */
-DEF_D_RUNTIME (ARRAYCAST, "_d_arraycast", RT(ARRAY_VOID),
- P3(SIZE_T, SIZE_T, ARRAY_VOID), 0)
/* Used for (array.length = n) expressions. The `i' variant is for when the
initializer is nonzero. */
@@ -206,23 +207,10 @@ DEF_D_RUNTIME (CXA_END_CATCH, "__cxa_end_catch", RT(VOID), P0(), 0)
DEF_D_RUNTIME (INVARIANT, "_D9invariant12_d_invariantFC6ObjectZv", RT(VOID),
P1(OBJECT), 0)
-/* Used when performing a switch/cases on a string. The `u' and `d' variants
- are for UTF-16 and UTF-32 strings respectively. */
-DEF_D_RUNTIME (SWITCH_STRING, "_d_switch_string", RT(INT),
- P2(ARRAY_STRING, STRING), 0)
-DEF_D_RUNTIME (SWITCH_USTRING, "_d_switch_ustring", RT(INT),
- P2(ARRAY_WSTRING, WSTRING), 0)
-DEF_D_RUNTIME (SWITCH_DSTRING, "_d_switch_dstring", RT(INT),
- P2(ARRAY_DSTRING, DSTRING), 0)
-
-/* Used when throwing an error that a switch statement has no default case,
- and yet none of the existing cases matched. */
-DEF_D_RUNTIME (SWITCH_ERROR, "_d_switch_error", RT(VOID), P2(STRING, UINT),
- ECF_NORETURN)
-
#undef P0
#undef P1
#undef P2
#undef P3
#undef P4
+#undef P5
#undef RT
diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc
index 1c9da101f39..55d63f89cb7 100644
--- a/gcc/d/toir.cc
+++ b/gcc/d/toir.cc
@@ -413,18 +413,6 @@ public:
else
error_at (location, "cannot %<goto%> into %<catch%> block");
}
- else if (s->isCaseStatement ())
- {
- location = make_location_t (s->loc);
- error_at (location, "case cannot be in different "
- "%<try%> block level from %<switch%>");
- }
- else if (s->isDefaultStatement ())
- {
- location = make_location_t (s->loc);
- error_at (location, "default cannot be in different "
- "%<try%> block level from %<switch%>");
- }
else
gcc_unreachable ();
}
@@ -709,7 +697,8 @@ public:
{
/* The break label may actually be some levels up.
eg: on a try/finally wrapping a loop. */
- LabelStatement *label = this->func_->searchLabel (s->ident)->statement;
+ LabelDsymbol *sym = this->func_->searchLabel (s->ident, s->loc);
+ LabelStatement *label = sym->statement;
gcc_assert (label != NULL);
Statement *stmt = label->statement->getRelatedLabeled ();
this->do_jump (this->lookup_bc_label (stmt, bc_break));
@@ -725,7 +714,8 @@ public:
{
if (s->ident)
{
- LabelStatement *label = this->func_->searchLabel (s->ident)->statement;
+ LabelDsymbol *sym = this->func_->searchLabel (s->ident, s->loc);
+ LabelStatement *label = sym->statement;
gcc_assert (label != NULL);
this->do_jump (this->lookup_bc_label (label->statement,
bc_continue));
@@ -759,7 +749,7 @@ public:
if (this->is_return_label (s->ident))
sym = this->func_->returnLabel;
else
- sym = this->func_->searchLabel (s->ident);
+ sym = this->func_->searchLabel (s->ident, s->loc);
/* If no label found, there was an error. */
tree label = this->define_label (sym->statement, sym->ident);
@@ -784,69 +774,9 @@ public:
tree condition = build_expr_dtor (s->condition);
Type *condtype = s->condition->type->toBasetype ();
- /* A switch statement on a string gets turned into a library call,
- which does a binary lookup on list of string cases. */
- if (s->condition->type->isString ())
- {
- Type *etype = condtype->nextOf ()->toBasetype ();
- libcall_fn libcall;
-
- switch (etype->ty)
- {
- case Tchar:
- libcall = LIBCALL_SWITCH_STRING;
- break;
-
- case Twchar:
- libcall = LIBCALL_SWITCH_USTRING;
- break;
-
- case Tdchar:
- libcall = LIBCALL_SWITCH_DSTRING;
- break;
-
- default:
- ::error ("switch statement value must be an array of "
- "some character type, not %s", etype->toChars ());
- gcc_unreachable ();
- }
-
- /* Apparently the backend is supposed to sort and set the indexes
- on the case array, have to change them to be usable. */
- Type *satype = condtype->sarrayOf (s->cases->length);
- vec <constructor_elt, va_gc> *elms = NULL;
-
- s->cases->sort ();
-
- for (size_t i = 0; i < s->cases->length; i++)
- {
- CaseStatement *cs = (*s->cases)[i];
- cs->index = i;
-
- if (cs->exp->op != TOKstring)
- s->error ("case '%s' is not a string", cs->exp->toChars ());
- else
- {
- tree exp = build_expr (cs->exp, true);
- CONSTRUCTOR_APPEND_ELT (elms, size_int (i), exp);
- }
- }
-
- /* Build static declaration to reference constructor. */
- tree ctor = build_constructor (build_ctype (satype), elms);
- tree decl = build_artificial_decl (TREE_TYPE (ctor), ctor);
- TREE_READONLY (decl) = 1;
- d_pushdecl (decl);
- rest_of_decl_compilation (decl, 1, 0);
-
- /* Pass it as a dynamic array. */
- decl = d_array_value (build_ctype (condtype->arrayOf ()),
- size_int (s->cases->length),
- build_address (decl));
-
- condition = build_libcall (libcall, Type::tint32, 2, decl, condition);
- }
- else if (!condtype->isscalar ())
+ /* A switch statement on a string gets turned into a library call.
+ It is not lowered during codegen. */
+ if (!condtype->isscalar ())
{
error ("cannot handle switch condition of type %s",
condtype->toChars ());
@@ -992,7 +922,10 @@ public:
void visit (SwitchErrorStatement *s)
{
- add_stmt (build_assert_call (s->loc, LIBCALL_SWITCH_ERROR));
+ /* A throw SwitchError statement gets turned into a library call.
+ The call is wrapped in the enclosed expression. */
+ gcc_assert (s->exp != NULL);
+ add_stmt (build_expr (s->exp));
}
/* A return statement exits the current function and supplies its return
@@ -1000,7 +933,7 @@ public:
void visit (ReturnStatement *s)
{
- if (s->exp == NULL || s->exp->type->toBasetype ()->ty == Tvoid)
+ if (s->exp == NULL || s->exp->type->toBasetype ()->ty == TY::Tvoid)
{
/* Return has no value. */
add_stmt (return_expr (NULL_TREE));
@@ -1012,7 +945,7 @@ public:
? this->func_->tintro->nextOf () : tf->nextOf ();
if ((this->func_->isMain () || this->func_->isCMain ())
- && type->toBasetype ()->ty == Tvoid)
+ && type->toBasetype ()->ty == TY::Tvoid)
type = Type::tint32;
if (this->func_->shidden)
@@ -1020,7 +953,7 @@ public:
/* Returning by hidden reference, store the result into the retval decl.
The result returned then becomes the retval reference itself. */
tree decl = DECL_RESULT (get_symbol_decl (this->func_));
- gcc_assert (!tf->isref);
+ gcc_assert (!tf->isref ());
/* If returning via NRVO, just refer to the DECL_RESULT; this differs
from using NULL_TREE in that it indicates that we care about the
@@ -1096,7 +1029,7 @@ public:
add_stmt (return_expr (decl));
}
- else if (tf->next->ty == Tnoreturn)
+ else if (tf->next->ty == TY::Tnoreturn)
{
/* Returning an expression that has no value, but has a side effect
that should never return. */
@@ -1514,7 +1447,7 @@ public:
/* If the function has been annotated with `pragma(inline)', then mark
the asm expression as being inline as well. */
- if (this->func_->inlining == PINLINEalways)
+ if (this->func_->inlining == PINLINE::always)
ASM_INLINE_P (exp) = 1;
add_stmt (exp);
diff --git a/gcc/d/typeinfo.cc b/gcc/d/typeinfo.cc
index fd8c746a307..065f6b3de80 100644
--- a/gcc/d/typeinfo.cc
+++ b/gcc/d/typeinfo.cc
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
#include "stor-layout.h"
#include "d-tree.h"
+#include "d-frontend.h"
#include "d-target.h"
@@ -110,37 +111,37 @@ get_typeinfo_kind (Type *type)
switch (type->ty)
{
- case Tpointer:
+ case TY::Tpointer:
return TK_POINTER_TYPE;
- case Tarray:
+ case TY::Tarray:
return TK_ARRAY_TYPE;
- case Tsarray:
+ case TY::Tsarray:
return TK_STATICARRAY_TYPE;
- case Taarray:
+ case TY::Taarray:
return TK_ASSOCIATIVEARRAY_TYPE;
- case Tstruct:
+ case TY::Tstruct:
return TK_STRUCT_TYPE;
- case Tvector:
+ case TY::Tvector:
return TK_VECTOR_TYPE;
- case Tenum:
+ case TY::Tenum:
return TK_ENUMERAL_TYPE;
- case Tfunction:
+ case TY::Tfunction:
return TK_FUNCTION_TYPE;
- case Tdelegate:
+ case TY::Tdelegate:
return TK_DELEGATE_TYPE;
- case Ttuple:
+ case TY::Ttuple:
return TK_TYPELIST_TYPE;
- case Tclass:
+ case TY::Tclass:
if (type->isTypeClass ()->sym->isInterfaceDeclaration ())
return TK_INTERFACE_TYPE;
else
@@ -216,7 +217,7 @@ make_frontend_typeinfo (Identifier *ident, ClassDeclaration *base = NULL)
= ClassDeclaration::create (loc, Identifier::idPool ("Object"),
NULL, NULL, true);
object->parent = object_module;
- object->members = new Dsymbols;
+ object->members = d_gc_malloc<Dsymbols> ();
object->storage_class |= STCtemp;
}
@@ -228,7 +229,7 @@ make_frontend_typeinfo (Identifier *ident, ClassDeclaration *base = NULL)
ClassDeclaration *tinfo = ClassDeclaration::create (loc, ident, NULL, NULL,
true);
tinfo->parent = object_module;
- tinfo->members = new Dsymbols;
+ tinfo->members = d_gc_malloc<Dsymbols> ();
dsymbolSemantic (tinfo, object_module->_scope);
tinfo->baseClass = base;
/* This is a compiler generated class, and shouldn't be mistaken for being
@@ -836,7 +837,7 @@ public:
/* Name of the class declaration. */
const char *name = cd->ident->toChars ();
if (!(strlen (name) > 9 && memcmp (name, "TypeInfo_", 9) == 0))
- name = cd->toPrettyChars ();
+ name = cd->toPrettyChars (true);
this->layout_string (name);
/* The vtable of the class declaration. */
@@ -857,7 +858,7 @@ public:
this->layout_field (base);
/* void *destructor; */
- tree dtor = (cd->dtor) ? build_address (get_symbol_decl (cd->dtor))
+ tree dtor = (cd->tidtor) ? build_address (get_symbol_decl (cd->tidtor))
: null_pointer_node;
this->layout_field (dtor);
@@ -911,10 +912,7 @@ public:
this->layout_field (build_integer_cst (flags, d_uint_type));
/* void *deallocator; */
- tree ddtor = (cd->aggDelete)
- ? build_address (get_symbol_decl (cd->aggDelete))
- : null_pointer_node;
- this->layout_field (ddtor);
+ this->layout_field (null_pointer_node);
/* OffsetTypeInfo[] m_offTi; (not implemented) */
this->layout_field (null_array_node);
@@ -942,7 +940,7 @@ public:
this->layout_field (null_array_node);
/* Name of the interface declaration. */
- this->layout_string (cd->toPrettyChars ());
+ this->layout_string (cd->toPrettyChars (true));
/* No vtable for interface declaration. */
this->layout_field (null_array_node);
@@ -1024,7 +1022,7 @@ public:
/* Layout of TypeInfo_Struct is:
void **__vptr;
void *__monitor;
- string name;
+ string mangledName;
void[] m_init;
hash_t function(in void*) xtoHash;
bool function(in void*, in void*) xopEquals;
@@ -1047,8 +1045,8 @@ public:
if (!sd->members)
return;
- /* Name of the struct declaration. */
- this->layout_string (sd->toPrettyChars ());
+ /* Mangled name of the struct declaration. */
+ this->layout_string (ti->deco);
/* Default initializer for struct. */
tree ptr = (sd->zeroInit) ? null_pointer_node
@@ -1064,7 +1062,7 @@ public:
if (sd->xhash)
{
TypeFunction *tf = sd->xhash->type->toTypeFunction ();
- if (!tf->isnothrow || tf->trust == TRUSTsystem)
+ if (!tf->isnothrow () || tf->trust == TRUST::system)
{
warning (sd->xhash->loc, "toHash() must be declared as "
"extern (D) size_t toHash() const nothrow @safe, "
@@ -1096,7 +1094,7 @@ public:
this->layout_field (build_integer_cst (m_flags, d_uint_type));
/* void function(void*) xdtor; */
- tree dtor = (sd->dtor) ? build_address (get_symbol_decl (sd->dtor))
+ tree dtor = (sd->tidtor) ? build_address (get_symbol_decl (sd->tidtor))
: null_pointer_node;
this->layout_field (dtor);
@@ -1298,17 +1296,17 @@ layout_classinfo_interfaces (ClassDeclaration *decl)
static bool
builtin_typeinfo_p (Type *type)
{
- if (type->isTypeBasic () || type->ty == Tclass || type->ty == Tnull)
+ if (type->isTypeBasic () || type->ty == TY::Tclass || type->ty == TY::Tnull)
return !type->mod;
- if (type->ty == Tarray)
+ if (type->ty == TY::Tarray)
{
/* Strings are so common, make them builtin. */
Type *next = type->nextOf ();
return !type->mod
&& ((next->isTypeBasic () != NULL && !next->mod)
- || (next->ty == Tchar && next->mod == MODimmutable)
- || (next->ty == Tchar && next->mod == MODconst));
+ || (next->ty == TY::Tchar && next->mod == MODimmutable)
+ || (next->ty == TY::Tchar && next->mod == MODconst));
}
return false;
@@ -1361,7 +1359,7 @@ get_typeinfo_decl (TypeInfoDeclaration *decl)
if (decl->csym)
return decl->csym;
- gcc_assert (decl->tinfo->ty != Terror);
+ gcc_assert (decl->tinfo->ty != TY::Terror);
TypeInfoDeclVisitor v = TypeInfoDeclVisitor ();
decl->accept (&v);
@@ -1436,7 +1434,7 @@ check_typeinfo_type (const Loc &loc, Scope *sc)
tree
build_typeinfo (const Loc &loc, Type *type)
{
- gcc_assert (type->ty != Terror);
+ gcc_assert (type->ty != TY::Terror);
check_typeinfo_type (loc, NULL);
create_typeinfo (type, NULL);
return build_address (get_typeinfo_decl (type->vtinfo));
diff --git a/gcc/d/types.cc b/gcc/d/types.cc
index fc8a1330696..db500ee2efe 100644
--- a/gcc/d/types.cc
+++ b/gcc/d/types.cc
@@ -94,7 +94,7 @@ bool
valist_array_p (Type *type)
{
Type *tvalist = target.va_listType (Loc (), NULL);
- if (tvalist->ty == Tsarray)
+ if (tvalist->ty == TY::Tsarray)
{
Type *tb = type->toBasetype ();
if (same_type_p (tb, tvalist))
@@ -170,7 +170,7 @@ make_array_type (Type *type, unsigned HOST_WIDE_INT size)
{
/* In [arrays/void-arrays], void arrays can also be static, the length is
specified in bytes. */
- if (type->toBasetype ()->ty == Tvoid)
+ if (type->toBasetype ()->ty == TY::Tvoid)
type = Type::tuns8;
/* In [arrays/static-arrays], a static array with a dimension of 0 is allowed,
@@ -546,9 +546,9 @@ merge_aggregate_types (Type *type, tree deco)
{
AggregateDeclaration *sym;
- if (type->ty == Tstruct)
+ if (type->ty == TY::Tstruct)
sym = type->isTypeStruct ()->sym;
- else if (type->ty == Tclass)
+ else if (type->ty == TY::Tclass)
sym = type->isTypeClass ()->sym;
else
gcc_unreachable ();
@@ -646,31 +646,31 @@ public:
switch (t->ty)
{
- case Tvoid: t->ctype = void_type_node; break;
- case Tbool: t->ctype = d_bool_type; break;
- case Tint8: t->ctype = d_byte_type; break;
- case Tuns8: t->ctype = d_ubyte_type; break;
- case Tint16: t->ctype = d_short_type; break;
- case Tuns16: t->ctype = d_ushort_type; break;
- case Tint32: t->ctype = d_int_type; break;
- case Tuns32: t->ctype = d_uint_type; break;
- case Tint64: t->ctype = d_long_type; break;
- case Tuns64: t->ctype = d_ulong_type; break;
- case Tint128: t->ctype = d_cent_type; break;
- case Tuns128: t->ctype = d_ucent_type; break;
- case Tfloat32: t->ctype = float_type_node; break;
- case Tfloat64: t->ctype = double_type_node; break;
- case Tfloat80: t->ctype = long_double_type_node; break;
- case Timaginary32: t->ctype = ifloat_type_node; break;
- case Timaginary64: t->ctype = idouble_type_node; break;
- case Timaginary80: t->ctype = ireal_type_node; break;
- case Tcomplex32: t->ctype = complex_float_type_node; break;
- case Tcomplex64: t->ctype = complex_double_type_node; break;
- case Tcomplex80: t->ctype = complex_long_double_type_node; break;
- case Tchar: t->ctype = char8_type_node; break;
- case Twchar: t->ctype = char16_type_node; break;
- case Tdchar: t->ctype = char32_type_node; break;
- default: gcc_unreachable ();
+ case TY::Tvoid: t->ctype = void_type_node; break;
+ case TY::Tbool: t->ctype = d_bool_type; break;
+ case TY::Tint8: t->ctype = d_byte_type; break;
+ case TY::Tuns8: t->ctype = d_ubyte_type; break;
+ case TY::Tint16: t->ctype = d_short_type; break;
+ case TY::Tuns16: t->ctype = d_ushort_type; break;
+ case TY::Tint32: t->ctype = d_int_type; break;
+ case TY::Tuns32: t->ctype = d_uint_type; break;
+ case TY::Tint64: t->ctype = d_long_type; break;
+ case TY::Tuns64: t->ctype = d_ulong_type; break;
+ case TY::Tint128: t->ctype = d_cent_type; break;
+ case TY::Tuns128: t->ctype = d_ucent_type; break;
+ case TY::Tfloat32: t->ctype = float_type_node; break;
+ case TY::Tfloat64: t->ctype = double_type_node; break;
+ case TY::Tfloat80: t->ctype = long_double_type_node; break;
+ case TY::Timaginary32: t->ctype = ifloat_type_node; break;
+ case TY::Timaginary64: t->ctype = idouble_type_node; break;
+ case TY::Timaginary80: t->ctype = ireal_type_node; break;
+ case TY::Tcomplex32: t->ctype = complex_float_type_node; break;
+ case TY::Tcomplex64: t->ctype = complex_double_type_node; break;
+ case TY::Tcomplex80: t->ctype = complex_long_double_type_node; break;
+ case TY::Tchar: t->ctype = char8_type_node; break;
+ case TY::Twchar: t->ctype = char16_type_node; break;
+ case TY::Tdchar: t->ctype = char32_type_node; break;
+ default: gcc_unreachable ();
}
TYPE_NAME (t->ctype) = get_identifier (t->toChars ());
@@ -786,7 +786,7 @@ public:
if (t->next != NULL)
{
fntype = build_ctype (t->next);
- if (t->isref)
+ if (t->isref ())
fntype = build_reference_type (fntype);
}
else
@@ -800,7 +800,7 @@ public:
/* Handle any special support for calling conventions. */
switch (t->linkage)
{
- case LINKwindows:
+ case LINK::windows:
{
/* [attribute/linkage]
@@ -815,10 +815,10 @@ public:
break;
}
- case LINKc:
- case LINKcpp:
- case LINKd:
- case LINKobjc:
+ case LINK::c:
+ case LINK::cpp:
+ case LINK::d:
+ case LINK::objc:
/* [abi/function-calling-conventions]
The extern (C) and extern (D) calling convention matches
@@ -1013,8 +1013,8 @@ public:
TYPE_CONTEXT (t->ctype) = d_decl_context (t->sym);
build_type_decl (t->ctype, t->sym);
- /* For structs with a user defined postblit or a destructor,
- also set TREE_ADDRESSABLE on the type and all variants.
+ /* For structs with a user defined postblit, copy constructor, or a
+ destructor, also set TREE_ADDRESSABLE on the type and all variants.
This will make the struct be passed around by reference. */
if (!t->sym->isPOD ())
{
diff --git a/gcc/d/verstr.h b/gcc/d/verstr.h
deleted file mode 100644
index 0dd41eeaebe..00000000000
--- a/gcc/d/verstr.h
+++ /dev/null
@@ -1 +0,0 @@
-"2.076.1"
diff --git a/gcc/po/EXCLUDES b/gcc/po/EXCLUDES
index 29ecd063206..d2c5cbf3f2a 100644
--- a/gcc/po/EXCLUDES
+++ b/gcc/po/EXCLUDES
@@ -53,46 +53,3 @@ genrecog.c
gensupport.c
gensupport.h
read-md.c
-
-# These files are part of the front end to D, and have no i18n support.
-d/dmd/arrayop.c
-d/dmd/attrib.c
-d/dmd/blockexit.c
-d/dmd/canthrow.c
-d/dmd/constfold.c
-d/dmd/cppmangle.c
-d/dmd/ctfeexpr.c
-d/dmd/dcast.c
-d/dmd/dclass.c
-d/dmd/declaration.c
-d/dmd/denum.c
-d/dmd/dimport.c
-d/dmd/dinterpret.c
-d/dmd/dmangle.c
-d/dmd/dmodule.c
-d/dmd/doc.c
-d/dmd/dscope.c
-d/dmd/dstruct.c
-d/dmd/dsymbol.c
-d/dmd/dtemplate.c
-d/dmd/dversion.c
-d/dmd/expression.c
-d/dmd/expressionsem.c
-d/dmd/func.c
-d/dmd/iasmgcc.c
-d/dmd/initsem.c
-d/dmd/lexer.c
-d/dmd/mtype.c
-d/dmd/nogc.c
-d/dmd/nspace.c
-d/dmd/objc.c
-d/dmd/opover.c
-d/dmd/optimize.c
-d/dmd/parse.c
-d/dmd/safe.c
-d/dmd/sideeffect.c
-d/dmd/statement.c
-d/dmd/statementsem.c
-d/dmd/staticassert.c
-d/dmd/staticcond.c
-d/dmd/traits.c
diff --git a/gcc/testsuite/gdc.dg/Wcastresult2.d b/gcc/testsuite/gdc.dg/Wcastresult2.d
index 56d2dd20e82..83d189a6adf 100644
--- a/gcc/testsuite/gdc.dg/Wcastresult2.d
+++ b/gcc/testsuite/gdc.dg/Wcastresult2.d
@@ -1,5 +1,5 @@
// { dg-do compile }
-// { dg-options "-Wcast-result" }
+// { dg-options "-Wcast-result -Wno-deprecated" }
void test()
{
diff --git a/gcc/testsuite/gdc.dg/asm1.d b/gcc/testsuite/gdc.dg/asm1.d
index dce36769370..1b249ee7b35 100644
--- a/gcc/testsuite/gdc.dg/asm1.d
+++ b/gcc/testsuite/gdc.dg/asm1.d
@@ -24,9 +24,9 @@ void parse3()
{
asm { "" [; }
// { dg-error "expression expected, not ';'" "" { target *-*-* } .-1 }
- // { dg-error "found 'EOF' when expecting ','" "" { target *-*-* } .-2 }
- // { dg-error "found 'EOF' when expecting ']'" "" { target *-*-* } .-3 }
- // { dg-error "found 'EOF' when expecting ';'" "" { target *-*-* } .-4 }
+ // { dg-error "found 'End of File' when expecting ','" "" { target *-*-* } .-2 }
+ // { dg-error "found 'End of File' when expecting ']'" "" { target *-*-* } .-3 }
+ // { dg-error "found 'End of File' when expecting ';'" "" { target *-*-* } .-4 }
}
void parse4()
@@ -46,8 +46,8 @@ void semantic1()
;
}
asm { "" : : : : L1, L2; }
- // { dg-error "goto skips declaration of variable asm1.semantic1.one" "" { target *-*-* } .-1 }
- // { dg-error "goto skips declaration of variable asm1.semantic1.two" "" { target *-*-* } .-2 }
+ // { dg-error "'goto' skips declaration of variable 'asm1.semantic1.one'" "" { target *-*-* } .-1 }
+ // { dg-error "'goto' skips declaration of variable 'asm1.semantic1.two'" "" { target *-*-* } .-2 }
{
int two;
L2:
@@ -58,19 +58,19 @@ void semantic1()
void semantic2a(X...)(X expr)
{
alias X[0] var1;
- asm { "%0" : "=m" (var1); } // { dg-error "double 'double' is a type, not an lvalue" }
+ asm { "%0" : "=m" (var1); } // { dg-error "double' is a 'double' definition and cannot be modified" }
}
void semantic2()
{
- semantic2a(3.6); // { dg-error "template instance asm1.semantic2a!double error instantiating" }
+ semantic2a(3.6); // { dg-error "template instance 'asm1.semantic2a!double' error instantiating" }
}
void semantic3()
{
asm
{
- unknown; // { dg-error "undefined identifier" }
+ unknown; // { dg-error "undefined identifier 'unknown'" }
}
}
@@ -86,6 +86,6 @@ void semantic4()
{
asm
{
- "%0" : : "m" (S4.foo); // { dg-error "template instance opDispatch!\"foo\" has no value" }
+ "%0" : : "m" (S4.foo); // { dg-error "template instance 'opDispatch!\"foo\"' has no value" }
}
}
diff --git a/gcc/testsuite/gdc.dg/asm2.d b/gcc/testsuite/gdc.dg/asm2.d
index bce0e41a60f..5b86e3564ef 100644
--- a/gcc/testsuite/gdc.dg/asm2.d
+++ b/gcc/testsuite/gdc.dg/asm2.d
@@ -3,6 +3,6 @@ module asm2;
void test()
{
- asm const shared { } // { dg-error "const/immutable/shared/inout attributes are not allowed on asm blocks" }
+ asm const shared { } // { dg-error "'const'/'immutable'/'shared'/'inout' attributes are not allowed on 'asm' blocks" }
}
diff --git a/gcc/testsuite/gdc.dg/asm3.d b/gcc/testsuite/gdc.dg/asm3.d
index 333d83ec99b..d792b2474be 100644
--- a/gcc/testsuite/gdc.dg/asm3.d
+++ b/gcc/testsuite/gdc.dg/asm3.d
@@ -2,23 +2,23 @@
// { dg-options "-Wall -Wdeprecated -Werror" }
module asm3;
-void test1() nothrow // { dg-error "nothrow function 'asm3.test1' may throw" }
+void test1() nothrow
{
- asm { } // { dg-error "asm statement is assumed to throw - mark it with 'nothrow' if it does not" }
+ asm { } // { dg-error "'asm' statement is assumed to throw - mark it with 'nothrow' if it does not" }
}
void test2() pure
{
- asm { } // { dg-error "asm statement is assumed to be impure - mark it with 'pure' if it is not" }
+ asm { } // { dg-error "'asm' statement is assumed to be impure - mark it with 'pure' if it is not" }
}
void test3() @nogc
{
- asm { } // { dg-error "asm statement is assumed to use the GC - mark it with '@nogc' if it does not" }
+ asm { } // { dg-error "'asm' statement is assumed to use the GC - mark it with '@nogc' if it does not" }
}
void test4() @safe
{
- asm { } // { dg-error "asm statement is assumed to be @system - mark it with '@trusted' if it is not" }
+ asm { } // { dg-error "'asm' statement is assumed to be '@system' - mark it with '@trusted' if it is not" }
}
diff --git a/gcc/testsuite/gdc.dg/gdc282.d b/gcc/testsuite/gdc.dg/gdc282.d
index ce840501d64..93e11fa0cbc 100644
--- a/gcc/testsuite/gdc.dg/gdc282.d
+++ b/gcc/testsuite/gdc.dg/gdc282.d
@@ -12,7 +12,7 @@ class C282a
{
}
- void f282() // { dg-error "conflicts with gdc282.C282a.f282" }
+ void f282() // { dg-error "conflicts with previous declaration" }
{
}
}
@@ -27,7 +27,7 @@ class C282b
{
}
- void f282() // { dg-error "conflicts with gdc282.C282b.f282" }
+ void f282() // { dg-error "conflicts with previous declaration" }
{
}
}
@@ -42,7 +42,7 @@ class C282c
{
}
- void f282() // { dg-error "conflicts with gdc282.C282c.f282" }
+ void f282() // { dg-error "conflicts with previous declaration" }
{
}
}
diff --git a/gcc/testsuite/gdc.dg/imports/gdc170.d b/gcc/testsuite/gdc.dg/imports/gdc170.d
index f9fea1f483c..aedef60c6b4 100644
--- a/gcc/testsuite/gdc.dg/imports/gdc170.d
+++ b/gcc/testsuite/gdc.dg/imports/gdc170.d
@@ -7,12 +7,12 @@ class bar(T)
template foo(T)
{
- bar!T foo1(T2)() if (true) body { return null; }
+ bar!T foo1(T2)() if (true) do { return null; }
bar!T foo2(T2)() { return null; }
- bar!T foo3(T2 = void)() if (true) body { return null; }
+ bar!T foo3(T2 = void)() if (true) do { return null; }
bar!T foo4(T2 = void)() { return null; }
- void foo5(T2)(bar!T x) if (true) body {}
+ void foo5(T2)(bar!T x) if (true) do {}
void foo6(T2)(bar!T x) {}
- void foo7(T2 = void)(bar!T x) if (true) body {}
+ void foo7(T2 = void)(bar!T x) if (true) do {}
void foo8(T2 = void)(bar!T x) {}
}
diff --git a/gcc/testsuite/gdc.dg/intrinsics.d b/gcc/testsuite/gdc.dg/intrinsics.d
index d9ccc0ec5ce..dca40d2cf69 100644
--- a/gcc/testsuite/gdc.dg/intrinsics.d
+++ b/gcc/testsuite/gdc.dg/intrinsics.d
@@ -35,22 +35,6 @@ ulong test_bswap(ulong a) { return bswap(a); }
int test_popcnt(uint a) { return popcnt(a); }
// { dg-final { scan-tree-dump " __builtin_popcount(l|ll) " "original" } }
int test_popcnt(ulong a) { return popcnt(a); }
-// { dg-final { scan-tree-dump "\\(volatile ubyte \\*\\) a;" "original" } }
-ubyte test_volatileLoad(ubyte *a) { return volatileLoad(a); }
-// { dg-final { scan-tree-dump "\\(volatile ushort \\*\\) a;" "original" } }
-ushort test_volatileLoad(ushort *a) { return volatileLoad(a); }
-// { dg-final { scan-tree-dump "\\(volatile uint \\*\\) a;" "original" } }
-uint test_volatileLoad(uint *a) { return volatileLoad(a); }
-// { dg-final { scan-tree-dump "\\(volatile ulong \\*\\) a;" "original" } }
-ulong test_volatileLoad(ulong *a) { return volatileLoad(a); }
-// { dg-final { scan-tree-dump "\\(volatile ubyte \\*\\) a = b" "original" } }
-void test_volatileStore(ubyte *a, ubyte b) { return volatileStore(a, b); }
-// { dg-final { scan-tree-dump "\\(volatile ushort \\*\\) a = b" "original" } }
-void test_volatileStore(ushort *a, ushort b) { return volatileStore(a, b); }
-// { dg-final { scan-tree-dump "\\(volatile uint \\*\\) a = b" "original" } }
-void test_volatileStore(uint *a, uint b) { return volatileStore(a, b); }
-// { dg-final { scan-tree-dump "\\(volatile ulong \\*\\) a = b" "original" } }
-void test_volatileStore(ulong *a, ulong b) { return volatileStore(a, b); }
// { dg-final { scan-tree-dump " a r<< b;" "original" } }
ubyte test_rol(ubyte a, uint b) { return rol!ubyte(a, b); }
// { dg-final { scan-tree-dump " a r>> 31;" "original" } }
@@ -141,6 +125,26 @@ real test_toPrecl(double a) { return toPrec!real(a); }
real test_toPrecl(real a) { return toPrec!real(a); }
//////////////////////////////////////////////////////
+// core.volatile
+
+// { dg-final { scan-tree-dump "\\(volatile ubyte \\*\\) a;" "original" } }
+ubyte test_volatileLoad(ubyte *a) { return volatileLoad(a); }
+// { dg-final { scan-tree-dump "\\(volatile ushort \\*\\) a;" "original" } }
+ushort test_volatileLoad(ushort *a) { return volatileLoad(a); }
+// { dg-final { scan-tree-dump "\\(volatile uint \\*\\) a;" "original" } }
+uint test_volatileLoad(uint *a) { return volatileLoad(a); }
+// { dg-final { scan-tree-dump "\\(volatile ulong \\*\\) a;" "original" } }
+ulong test_volatileLoad(ulong *a) { return volatileLoad(a); }
+// { dg-final { scan-tree-dump "\\(volatile ubyte \\*\\) a = b" "original" } }
+void test_volatileStore(ubyte *a, ubyte b) { return volatileStore(a, b); }
+// { dg-final { scan-tree-dump "\\(volatile ushort \\*\\) a = b" "original" } }
+void test_volatileStore(ushort *a, ushort b) { return volatileStore(a, b); }
+// { dg-final { scan-tree-dump "\\(volatile uint \\*\\) a = b" "original" } }
+void test_volatileStore(uint *a, uint b) { return volatileStore(a, b); }
+// { dg-final { scan-tree-dump "\\(volatile ulong \\*\\) a = b" "original" } }
+void test_volatileStore(ulong *a, ulong b) { return volatileStore(a, b); }
+
+//////////////////////////////////////////////////////
// core.stdc.stdarg
// { dg-final { scan-tree-dump-not " va_arg " "original" } }
diff --git a/gcc/testsuite/gdc.dg/pr101672.d b/gcc/testsuite/gdc.dg/pr101672.d
index 292fd761fb1..8b337a4cf19 100644
--- a/gcc/testsuite/gdc.dg/pr101672.d
+++ b/gcc/testsuite/gdc.dg/pr101672.d
@@ -8,7 +8,7 @@ interface I101672
static int i101672;
}
-class A101672 : I101672 // { dg-error "class object.A101672 missing or corrupt object.d" }
+class A101672 : I101672
{
static int a101672;
}
diff --git a/gcc/testsuite/gdc.dg/pr90650a.d b/gcc/testsuite/gdc.dg/pr90650a.d
index 57228cab19f..62b79941f20 100644
--- a/gcc/testsuite/gdc.dg/pr90650a.d
+++ b/gcc/testsuite/gdc.dg/pr90650a.d
@@ -10,5 +10,5 @@ class c
void g ()
{
- if (0 & [0] & c.f()) {} // { dg-error "array operation \\\[0\\\] & 0 & f\\(\\) without destination memory not allowed" }
+ if (0 & [0] & c.f()) {} // { dg-error "array operation .\\\[0\\\] & 0 & f\\(\\). without destination memory not allowed" }
}
diff --git a/gcc/testsuite/gdc.dg/pr90650b.d b/gcc/testsuite/gdc.dg/pr90650b.d
index 2b3192ed2b6..11a02259481 100644
--- a/gcc/testsuite/gdc.dg/pr90650b.d
+++ b/gcc/testsuite/gdc.dg/pr90650b.d
@@ -9,5 +9,5 @@ class c
}
void g ()
{
- if ([0] & c.f()) {} // { dg-error "array operation \\\[0\\\] & f\\(\\) without destination memory not allowed" }
+ if ([0] & c.f()) {} // { dg-error "array operation .\\\[0\\\] & f\\(\\). without destination memory not allowed" }
}
diff --git a/gcc/testsuite/gdc.dg/pr94777a.d b/gcc/testsuite/gdc.dg/pr94777a.d
index a58fa557e35..d0cb5569e46 100644
--- a/gcc/testsuite/gdc.dg/pr94777a.d
+++ b/gcc/testsuite/gdc.dg/pr94777a.d
@@ -11,5 +11,5 @@ void f94777()
this(this) { }
}
auto var = S94777(0);
- variadic(var, S94777(1));
+ variadic(var, S94777(1)); // { dg-error "cannot pass types with postblits or copy constructors as variadic arguments" }
}
diff --git a/gcc/testsuite/gdc.dg/pr94777c.d b/gcc/testsuite/gdc.dg/pr94777c.d
new file mode 100644
index 00000000000..9b725c052c3
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/pr94777c.d
@@ -0,0 +1,62 @@
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94777
+// { dg-additional-options "-funittest" }
+// { dg-do compile }
+
+void testVariadic(T)(int nargs, ...)
+{
+ import core.stdc.stdarg;
+ foreach(i; 0 .. nargs)
+ {
+ auto arg = va_arg!T(_argptr);
+ static if (__traits(compiles, arg.value))
+ {
+ assert(arg.value == i);
+ }
+ else static if (__traits(compiles, arg[0]))
+ {
+ foreach (value; arg)
+ assert(value == i);
+ }
+ else
+ {
+ assert(arg == T.init);
+ }
+ }
+}
+
+/******************************************/
+
+struct Postblit
+{
+ static int count = 0;
+ int value;
+ this(this) { count++; }
+}
+
+unittest
+{
+ auto a0 = Postblit(0);
+ auto a1 = Postblit(1);
+ auto a2 = Postblit(2);
+ testVariadic!Postblit(3, a0, a1, a2); // { dg-error "cannot pass types with postblits or copy constructors as variadic arguments" }
+ assert(Postblit.count == 3);
+}
+
+/******************************************/
+
+struct CopyConstructor
+{
+ static int count = 0;
+ int value;
+ this(int v) { this.value = v; }
+ this(ref typeof(this) other) { count++; this.value = other.value; }
+}
+
+unittest
+{
+ auto a0 = CopyConstructor(0);
+ auto a1 = CopyConstructor(1);
+ auto a2 = CopyConstructor(2);
+ testVariadic!CopyConstructor(3, a0, a1, a2); // { dg-error "cannot pass types with postblits or copy constructors as variadic arguments" }
+ assert(CopyConstructor.count == 3);
+}
diff --git a/gcc/testsuite/gdc.dg/pr95250.d b/gcc/testsuite/gdc.dg/pr95250.d
index dfb8abb732f..ba0adeac521 100644
--- a/gcc/testsuite/gdc.dg/pr95250.d
+++ b/gcc/testsuite/gdc.dg/pr95250.d
@@ -15,4 +15,4 @@ void* f(T)(T a, T b)
}
static assert(is(typeof(f!(void*)(null, null)) == void*));
-// { dg-error "static assert \(.*\) is false" "" { target *-*-* } .-1 }
+// { dg-error "static assert: \(.*\) is false" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/gdc.dg/pr96156b.d b/gcc/testsuite/gdc.dg/pr96156b.d
new file mode 100644
index 00000000000..ae79d568ec6
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/pr96156b.d
@@ -0,0 +1,17 @@
+@safe unittest
+{
+ struct CustomString
+ {
+ @safe:
+ string _impl;
+ @property bool empty() const { return !_impl.length; }
+ }
+
+ CustomString find(CustomString a, CustomString b)
+ {
+ return CustomString.init;
+ }
+
+ auto r = find(CustomString("a"), CustomString("b"));
+ assert(r.empty);
+}
diff --git a/gcc/testsuite/gdc.dg/pr96157c.d b/gcc/testsuite/gdc.dg/pr96157c.d
new file mode 100644
index 00000000000..8f48cbdc1b4
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/pr96157c.d
@@ -0,0 +1,40 @@
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96157
+// { dg-options "-fno-moduleinfo -fno-rtti" }
+// { dg-do compile }
+
+struct CodepointSet
+{
+ CowArray!string data;
+}
+
+struct CowArray(SP)
+{
+ ~this()
+ {
+ if (data.length)
+ refCount;
+ }
+ @property refCount() { return data[$-1]; }
+
+ uint[] data;
+}
+
+int ucmp() { return 1; }
+
+bool loadProperty () {
+
+ CodepointSet target;
+ if (ucmp)
+ CodepointSet();
+ else if (ucmp|| ucmp)
+ target = CodepointSet();
+ else if (ucmp|| ucmp)
+ target = CodepointSet();
+ else if (ucmp|| ucmp)
+ target = CodepointSet();
+ else if (ucmp)
+ target = CodepointSet();
+ else if (ucmp)
+ target = CodepointSet();
+ return true;
+}
diff --git a/gcc/testsuite/gdc.dg/pr96869.d b/gcc/testsuite/gdc.dg/pr96869.d
index c4ace30a546..348da1bcb3d 100644
--- a/gcc/testsuite/gdc.dg/pr96869.d
+++ b/gcc/testsuite/gdc.dg/pr96869.d
@@ -2,31 +2,31 @@
// { dg-do compile }
__vector(float[0]) var01;
-// { dg-error "0 byte vector type __vector\\\(float\\\[0\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "0 byte vector type '__vector\\\(float\\\[0\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
__vector(float[3]) var02;
-// { dg-error "12 byte vector type __vector\\\(float\\\[3\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "12 byte vector type '__vector\\\(float\\\[3\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
__vector(float[][4]) var03;
-// { dg-error "vector type __vector\\\(float\\\[\\\]\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(float\\\[\\\]\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
__vector(float[4][4]) var04;
-// { dg-error "vector type __vector\\\(float\\\[4\\\]\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(float\\\[4\\\]\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
__vector(float[float][4]) var05;
-// { dg-error "vector type __vector\\\(float\\\[float\\\]\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(float\\\[float\\\]\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
__vector(float function()[4]) var06;
-// { dg-error "vector type __vector\\\(float function\\\(\\\)\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(float function\\\(\\\)\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
__vector(float delegate()[4]) var07;
-// { dg-error "vector type __vector\\\(float delegate\\\(\\\)\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(float delegate\\\(\\\)\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
enum E { a, b, c }
__vector(E[4]) var08;
-// { dg-error "vector type __vector\\\(E\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(E\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
struct S { float a; }
__vector(S[4]) var09;
-// { dg-error "vector type __vector\\\(S\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(S\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
class C { float a; }
__vector(C[4]) var10;
-// { dg-error "vector type __vector\\\(C\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(C\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
__vector(cfloat[4]) var11;
-// { dg-error "vector type __vector\\\(cfloat\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(cfloat\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
__vector(bool[4]) var12;
-// { dg-error "vector type __vector\\\(bool\\\[4\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(bool\\\[4\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
__vector(real[128]) var13;
-// { dg-error "vector type __vector\\\(real\\\[128\\\]\\\) is not supported on this platform" "" { target *-*-* } .-1 }
+// { dg-error "vector type '__vector\\\(real\\\[128\\\]\\\)' is not supported on this platform" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/gdc.dg/pr98277.d b/gcc/testsuite/gdc.dg/pr98277.d
index d3b859fcbda..0dff142a6ef 100644
--- a/gcc/testsuite/gdc.dg/pr98277.d
+++ b/gcc/testsuite/gdc.dg/pr98277.d
@@ -7,7 +7,7 @@ enum Side
right
}
-ref int getSide(Side side, ref int left, ref int right)
+ref int getSide(Side side, return ref int left, return ref int right)
{
return side == Side.left ? left : right;
}
diff --git a/gcc/testsuite/gdc.dg/pr98457.d b/gcc/testsuite/gdc.dg/pr98457.d
index bc0d8af5d4a..0cc83aca064 100644
--- a/gcc/testsuite/gdc.dg/pr98457.d
+++ b/gcc/testsuite/gdc.dg/pr98457.d
@@ -3,7 +3,7 @@
void main()
{
- writef!"%s"; // { dg-error "template instance writef!\"%s\" template .writef. is not defined" }
- writef!"`%s"; // { dg-error "template instance writef!\"`%s\" template .writef. is not defined" }
- writef!"%%s`"; // { dg-error "template instance writef!\"%%s`\" template .writef. is not defined" }
+ writef!"%s"; // { dg-error "template instance .writef!\"%s\". template .writef. is not defined" }
+ writef!"`%s"; // { dg-error "template instance .writef!\"`%s\". template .writef. is not defined" }
+ writef!"%%s`"; // { dg-error "template instance .writef!\"%%s`\". template .writef. is not defined" }
}
diff --git a/gcc/testsuite/gdc.dg/simd1.d b/gcc/testsuite/gdc.dg/simd1.d
index 374dcae5e94..b25b99e3e1d 100644
--- a/gcc/testsuite/gdc.dg/simd1.d
+++ b/gcc/testsuite/gdc.dg/simd1.d
@@ -26,14 +26,6 @@ void test1()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
static assert(!__traits(compiles, v1 << 1));
static assert(!__traits(compiles, v1 >> 1));
static assert(!__traits(compiles, v1 >>> 1));
diff --git a/gcc/testsuite/gdc.dg/simd2a.d b/gcc/testsuite/gdc.dg/simd2a.d
index b630a473b18..0fb391cc057 100644
--- a/gcc/testsuite/gdc.dg/simd2a.d
+++ b/gcc/testsuite/gdc.dg/simd2a.d
@@ -24,14 +24,6 @@ void test2a()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
v1 = v2 << 1;
v1 = v2 >> 1;
v1 = v2 >>> 1;
diff --git a/gcc/testsuite/gdc.dg/simd2b.d b/gcc/testsuite/gdc.dg/simd2b.d
index 35c42880ce2..41a4eb3c34a 100644
--- a/gcc/testsuite/gdc.dg/simd2b.d
+++ b/gcc/testsuite/gdc.dg/simd2b.d
@@ -24,14 +24,6 @@ void test2b()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
v1 = v2 << 1;
v1 = v2 >> 1;
v1 = v2 >>> 1;
diff --git a/gcc/testsuite/gdc.dg/simd2c.d b/gcc/testsuite/gdc.dg/simd2c.d
index 2f19e75a35b..a9957094998 100644
--- a/gcc/testsuite/gdc.dg/simd2c.d
+++ b/gcc/testsuite/gdc.dg/simd2c.d
@@ -24,14 +24,6 @@ void test2c()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
v1 = v2 << 1;
v1 = v2 >> 1;
v1 = v2 >>> 1;
diff --git a/gcc/testsuite/gdc.dg/simd2d.d b/gcc/testsuite/gdc.dg/simd2d.d
index 9d378e4cdba..d578734368e 100644
--- a/gcc/testsuite/gdc.dg/simd2d.d
+++ b/gcc/testsuite/gdc.dg/simd2d.d
@@ -24,14 +24,6 @@ void test2d()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
v1 = v2 << 1;
v1 = v2 >> 1;
v1 = v2 >>> 1;
diff --git a/gcc/testsuite/gdc.dg/simd2e.d b/gcc/testsuite/gdc.dg/simd2e.d
index 30d7c1aa165..d33574ad005 100644
--- a/gcc/testsuite/gdc.dg/simd2e.d
+++ b/gcc/testsuite/gdc.dg/simd2e.d
@@ -24,14 +24,6 @@ void test2e()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
v1 = v2 << 1;
v1 = v2 >> 1;
v1 = v2 >>> 1;
diff --git a/gcc/testsuite/gdc.dg/simd2f.d b/gcc/testsuite/gdc.dg/simd2f.d
index f8448801dba..5845249e693 100644
--- a/gcc/testsuite/gdc.dg/simd2f.d
+++ b/gcc/testsuite/gdc.dg/simd2f.d
@@ -24,14 +24,6 @@ void test2f()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
v1 = v2 << 1;
v1 = v2 >> 1;
v1 = v2 >>> 1;
diff --git a/gcc/testsuite/gdc.dg/simd2g.d b/gcc/testsuite/gdc.dg/simd2g.d
index 8e8bc15623c..ce438f2c3db 100644
--- a/gcc/testsuite/gdc.dg/simd2g.d
+++ b/gcc/testsuite/gdc.dg/simd2g.d
@@ -24,14 +24,6 @@ void test2g()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
v1 = v2 << 1;
v1 = v2 >> 1;
v1 = v2 >>> 1;
diff --git a/gcc/testsuite/gdc.dg/simd2h.d b/gcc/testsuite/gdc.dg/simd2h.d
index f7542e83b8c..c631c760ffb 100644
--- a/gcc/testsuite/gdc.dg/simd2h.d
+++ b/gcc/testsuite/gdc.dg/simd2h.d
@@ -24,14 +24,6 @@ void test2h()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
v1 = v2 << 1;
v1 = v2 >> 1;
v1 = v2 >>> 1;
diff --git a/gcc/testsuite/gdc.dg/simd2i.d b/gcc/testsuite/gdc.dg/simd2i.d
index 2e3587dce77..6946c79987a 100644
--- a/gcc/testsuite/gdc.dg/simd2i.d
+++ b/gcc/testsuite/gdc.dg/simd2i.d
@@ -24,14 +24,6 @@ void test2i()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
static assert(!__traits(compiles, v1 << 1));
static assert(!__traits(compiles, v1 >> 1));
static assert(!__traits(compiles, v1 >>> 1));
diff --git a/gcc/testsuite/gdc.dg/simd2j.d b/gcc/testsuite/gdc.dg/simd2j.d
index 7b607848580..ecfdbf3bbc1 100644
--- a/gcc/testsuite/gdc.dg/simd2j.d
+++ b/gcc/testsuite/gdc.dg/simd2j.d
@@ -24,14 +24,6 @@ void test2j()
static assert(!__traits(compiles, v1 > v2));
static assert(!__traits(compiles, v1 <= v2));
static assert(!__traits(compiles, v1 >= v2));
- static assert(!__traits(compiles, v1 <> v2));
- static assert(!__traits(compiles, v1 !< v2));
- static assert(!__traits(compiles, v1 !> v2));
- static assert(!__traits(compiles, v1 !<> v2));
- static assert(!__traits(compiles, v1 <>= v2));
- static assert(!__traits(compiles, v1 !<= v2));
- static assert(!__traits(compiles, v1 !>= v2));
- static assert(!__traits(compiles, v1 !<>= v2));
static assert(!__traits(compiles, v1 << 1));
static assert(!__traits(compiles, v1 >> 1));
static assert(!__traits(compiles, v1 >>> 1));
diff --git a/gcc/testsuite/gdc.dg/simd7951.d b/gcc/testsuite/gdc.dg/simd7951.d
index 99ce1510f8f..4e467ef0f96 100644
--- a/gcc/testsuite/gdc.dg/simd7951.d
+++ b/gcc/testsuite/gdc.dg/simd7951.d
@@ -19,4 +19,5 @@ void test7951_2()
f1.array = v1;
f2.array = v2;
f3 = f1 + f2;
+ assert((cast(float[4])f3)[2] == 6);
}
diff --git a/gcc/testsuite/gdc.dg/simd_ctfe.d b/gcc/testsuite/gdc.dg/simd_ctfe.d
new file mode 100644
index 00000000000..b254cf312cb
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/simd_ctfe.d
@@ -0,0 +1,87 @@
+// { dg-do compile }
+import core.simd;
+
+// https://issues.dlang.org/show_bug.cgi?id=19627
+enum int[4] fail19627 = cast(int[4])int4(0);
+
+// https://issues.dlang.org/show_bug.cgi?id=19628
+enum ice19628a = int4.init[0];
+enum ice19628b = int4.init.array[0];
+enum ice19628c = (cast(int[4])int4.init.array)[0];
+enum ice19628d = (cast(int[4])int4.init)[0];
+
+// https://issues.dlang.org/show_bug.cgi?id=19629
+enum fail19629a = int4(0)[0];
+enum fail19629b = int4(0).array[0];
+enum fail19629c = (cast(int[4])int4(0).array)[0];
+enum fail19628d = (cast(int[4])int4(0))[0];
+
+// https://issues.dlang.org/show_bug.cgi?id=19630
+enum fail19630a = int4.init[1..2];
+enum fail19630b = int4.init.array[1..2];
+enum fail19630c = (cast(int[4])int4.init.array)[1..2];
+enum fail19630d = int4(0)[1..2];
+enum fail19630e = int4(0).array[1..2];
+enum fail19630f = (cast(int[4])int4(0).array)[1..2];
+enum fail19630g = (cast(int[4])int4.init)[1..2];
+enum fail19630h = (cast(int[4])int4(0))[1..2];
+
+// Same tests as above, but use access via enum.
+enum int4 V1 = int4.init;
+enum int[4] V2 = int4.init.array;
+enum int[4] V3 = cast(int[4])int4.init;
+enum int[4] V4 = cast(int[4])int4.init.array;
+enum int4 V5 = int4(0);
+enum int[4] V6 = int4(0).array;
+enum int[4] V7 = cast(int[4])int4(0);
+enum int[4] V8 = cast(int[4])int4(0).array;
+
+// CTFE index tests
+enum I1 = V1[0]; static assert(I1 == 0);
+enum I2 = V2[0]; static assert(I2 == 0);
+enum I3 = V3[0]; static assert(I3 == 0);
+enum I4 = V4[0]; static assert(I4 == 0);
+enum I5 = V5[0]; static assert(I5 == 0);
+enum I6 = V6[0]; static assert(I6 == 0);
+enum I7 = V7[0]; static assert(I7 == 0);
+enum I8 = V8[0]; static assert(I8 == 0);
+
+// CTFE slice tests
+enum S1 = V1[1..2]; static assert(S1 == [0]);
+enum S2 = V2[1..2]; static assert(S2 == [0]);
+enum S3 = V3[1..2]; static assert(S3 == [0]);
+enum S4 = V4[1..2]; static assert(S4 == [0]);
+enum S5 = V5[1..2]; static assert(S5 == [0]);
+enum S6 = V6[1..2]; static assert(S6 == [0]);
+enum S7 = V7[1..2]; static assert(S7 == [0]);
+enum S8 = V8[1..2]; static assert(S8 == [0]);
+
+// Same tests as above, but use access via immutable.
+//immutable int4 v1 = int4.init; // Cannot cast to immutable at compile time
+immutable int[4] v2 = int4.init.array;
+immutable int[4] v3 = cast(int[4])int4.init;
+immutable int[4] v4 = cast(int[4])int4.init.array;
+//immutable int4 v5 = int4(0); // Cannot cast to immutable at compile time
+immutable int[4] v6 = int4(0).array;
+immutable int[4] v7 = cast(int[4])int4(0);
+immutable int[4] v8 = cast(int[4])int4(0).array;
+
+// CTFE index tests
+//immutable i1 = v1[0]; static assert(i1 == 0);
+immutable i2 = v2[0]; static assert(i2 == 0);
+immutable i3 = v3[0]; static assert(i3 == 0);
+immutable i4 = v4[0]; static assert(i4 == 0);
+//immutable i5 = v5[0]; static assert(i5 == 0);
+immutable i6 = v6[0]; static assert(i6 == 0);
+immutable i7 = v7[0]; static assert(i7 == 0);
+immutable i8 = v8[0]; static assert(i8 == 0);
+
+// CTFE slice tests
+//immutable s1 = v1[1..2]; static assert(s1 == [0]);
+immutable s2 = v2[1..2]; static assert(s2 == [0]);
+immutable s3 = v3[1..2]; static assert(s3 == [0]);
+immutable s4 = v4[1..2]; static assert(s4 == [0]);
+//immutable s5 = v5[1..2]; static assert(s5 == [0]);
+immutable s6 = v6[1..2]; static assert(s6 == [0]);
+immutable s7 = v7[1..2]; static assert(s7 == [0]);
+immutable s8 = v8[1..2]; static assert(s8 == [0]);
diff --git a/gcc/testsuite/gdc.dg/torture/gdc309.d b/gcc/testsuite/gdc.dg/torture/gdc309.d
index d14634a2f1f..acda2bb8f5a 100644
--- a/gcc/testsuite/gdc.dg/torture/gdc309.d
+++ b/gcc/testsuite/gdc.dg/torture/gdc309.d
@@ -1,4 +1,5 @@
// https://bugzilla.gdcproject.org/show_bug.cgi?id=309
+// { dg-additional-options "-Wno-deprecated" }
// { dg-do run }
// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
diff --git a/gcc/testsuite/gdc.dg/torture/pr94424.d b/gcc/testsuite/gdc.dg/torture/pr94424.d
index 2910c9ae139..dc30639ab2a 100644
--- a/gcc/testsuite/gdc.dg/torture/pr94424.d
+++ b/gcc/testsuite/gdc.dg/torture/pr94424.d
@@ -17,3 +17,19 @@
assert(__cmp([c2, c2], [c1, c1]) > 0);
assert(__cmp([c2, c2], [c2, c1]) > 0);
}
+
+@safe unittest
+{
+ struct C
+ {
+ char i;
+ this(char i) { this.i = i; }
+ }
+
+ auto c1 = C(1);
+ auto c2 = C(2);
+
+ assert(__cmp([c1, c1][], [c2, c2][]) < 0);
+ assert(__cmp([c2, c2], [c1, c1]) > 0);
+ assert(__cmp([c2, c2], [c2, c1]) > 0);
+}
diff --git a/gcc/testsuite/gdc.dg/torture/pr94777b.d b/gcc/testsuite/gdc.dg/torture/pr94777b.d
index 2f0f9d9affb..e9da63fdc9d 100644
--- a/gcc/testsuite/gdc.dg/torture/pr94777b.d
+++ b/gcc/testsuite/gdc.dg/torture/pr94777b.d
@@ -27,42 +27,6 @@ void testVariadic(T)(int nargs, ...)
/******************************************/
-struct Constructor
-{
- static int count;
- int value;
- this(int v) { count++; this.value = v; }
-}
-
-unittest
-{
- auto a0 = Constructor(0);
- auto a1 = Constructor(1);
- auto a2 = Constructor(2);
- testVariadic!Constructor(3, a0, a1, a2);
- assert(Constructor.count == 3);
-}
-
-/******************************************/
-
-struct Postblit
-{
- static int count = 0;
- int value;
- this(this) { count++; }
-}
-
-unittest
-{
- auto a0 = Postblit(0);
- auto a1 = Postblit(1);
- auto a2 = Postblit(2);
- testVariadic!Postblit(3, a0, a1, a2);
- assert(Postblit.count == 3);
-}
-
-/******************************************/
-
struct Destructor
{
static int count = 0;
@@ -80,102 +44,3 @@ unittest
}
assert(Destructor.count == 3);
}
-
-/******************************************/
-
-struct CopyConstructor
-{
- static int count = 0;
- int value;
- this(int v) { this.value = v; }
- this(ref typeof(this) other) { count++; this.value = other.value; }
-}
-
-unittest
-{
- auto a0 = CopyConstructor(0);
- auto a1 = CopyConstructor(1);
- auto a2 = CopyConstructor(2);
- testVariadic!CopyConstructor(3, a0, a1, a2);
- // NOTE: Cpctors are not implemented yet.
- assert(CopyConstructor.count == 0 || CopyConstructor.count == 3);
-}
-
-/******************************************/
-
-unittest
-{
- struct Nested
- {
- int value;
- }
-
- auto a0 = Nested(0);
- auto a1 = Nested(1);
- auto a2 = Nested(2);
- testVariadic!Nested(3, a0, a1, a2);
-}
-
-/******************************************/
-
-unittest
-{
- struct Nested2
- {
- int value;
- }
-
- void testVariadic2(int nargs, ...)
- {
- import core.stdc.stdarg;
- foreach(i; 0 .. nargs)
- {
- auto arg = va_arg!Nested2(_argptr);
- assert(arg.value == i);
- }
- }
-
- auto a0 = Nested2(0);
- auto a1 = Nested2(1);
- auto a2 = Nested2(2);
- testVariadic2(3, a0, a1, a2);
-}
-
-/******************************************/
-
-struct EmptyStruct
-{
-}
-
-unittest
-{
- auto a0 = EmptyStruct();
- auto a1 = EmptyStruct();
- auto a2 = EmptyStruct();
- testVariadic!EmptyStruct(3, a0, a1, a2);
-}
-
-/******************************************/
-
-alias StaticArray = int[4];
-
-unittest
-{
- StaticArray a0 = 0;
- StaticArray a1 = 1;
- StaticArray a2 = 2;
- // XBUG: Front-end rewrites static arrays as dynamic arrays.
- //testVariadic!StaticArray(3, a0, a1, a2);
-}
-
-/******************************************/
-
-alias EmptyArray = void[0];
-
-unittest
-{
- auto a0 = EmptyArray.init;
- auto a1 = EmptyArray.init;
- auto a2 = EmptyArray.init;
- testVariadic!EmptyArray(3, a0, a1, a2);
-}
diff --git a/gcc/testsuite/gdc.dg/torture/simd17344.d b/gcc/testsuite/gdc.dg/torture/simd17344.d
new file mode 100644
index 00000000000..fd5ffbe8a3a
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/torture/simd17344.d
@@ -0,0 +1,11 @@
+// https://issues.dlang.org/show_bug.cgi?id=17344
+// { dg-additional-options "-mavx" { target avx_runtime } }
+// { dg-do run { target { avx_runtime || vect_sizes_16B_8B } } }
+// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
+
+void main()
+{
+ __vector(int[4]) vec1 = 2, vec2 = vec1++;
+ assert(cast(int[4])vec1 == [3, 3, 3, 3]);
+ assert(cast(int[4])vec2 == [2, 2, 2, 2]);
+}
diff --git a/gcc/testsuite/gdc.dg/torture/simd20052.d b/gcc/testsuite/gdc.dg/torture/simd20052.d
new file mode 100644
index 00000000000..4587351a851
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/torture/simd20052.d
@@ -0,0 +1,17 @@
+// { dg-additional-options "-mavx2" { target avx2_runtime } }
+// { dg-do run { target { avx2_runtime || vect_sizes_32B_16B } } }
+// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
+import core.simd;
+
+auto test20052()
+{
+ struct S { long4 v; }
+ S s;
+ return s;
+}
+
+void main()
+{
+ test20052();
+}
+
diff --git a/gcc/testsuite/gdc.dg/torture/simd6.d b/gcc/testsuite/gdc.dg/torture/simd6.d
new file mode 100644
index 00000000000..d8de02edf72
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/torture/simd6.d
@@ -0,0 +1,26 @@
+// { dg-additional-options "-mavx2" { target avx2_runtime } }
+// { dg-do run { target { avx2_runtime || vect_sizes_32B_16B } } }
+// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
+import core.simd;
+
+void test6a()
+{
+ // stack occasionally misaligned
+ float f = 0;
+ long4 v;
+ assert((cast(size_t)&v) % 32 == 0);
+ v += 1;
+}
+
+void test6b()
+{
+ struct S {long4 v;}
+ S s;
+ assert((cast(size_t)&s) % 32 == 0);
+}
+
+void main()
+{
+ test6a();
+ test6b();
+}
diff --git a/gcc/testsuite/gdc.dg/torture/simd7.d b/gcc/testsuite/gdc.dg/torture/simd7.d
new file mode 100644
index 00000000000..6e890defca7
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/torture/simd7.d
@@ -0,0 +1,18 @@
+// { dg-additional-options "-mavx2" { target avx2_runtime } }
+// { dg-do run { target { avx2_runtime || vect_sizes_32B_16B } } }
+// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
+import core.simd;
+
+double4 test7r(double4 v)
+{
+ return v;
+}
+
+void main()
+{
+ // 32 bytes sliced down to 16 bytes
+ double4 v = 1;
+ double4 r = test7r(v);
+ assert(v[2] == r[2]);
+ assert(v[3] == r[3]);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/a3682.d b/gcc/testsuite/gdc.test/compilable/a3682.d
index 69191ec20a2..4be63a35525 100644
--- a/gcc/testsuite/gdc.test/compilable/a3682.d
+++ b/gcc/testsuite/gdc.test/compilable/a3682.d
@@ -1,7 +1,7 @@
-// EXTRA_SOURCES: imports/b3682.d
+// COMPILED_IMPORTS: imports/b3682.d
// PERMUTE_ARGS:
-// 3682
+// https://issues.dlang.org/show_bug.cgi?id=3682
struct Tuple(Types...)
{
diff --git a/gcc/testsuite/gdc.test/compilable/aliasassign.d b/gcc/testsuite/gdc.test/compilable/aliasassign.d
new file mode 100644
index 00000000000..e355e49837f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/aliasassign.d
@@ -0,0 +1,41 @@
+template AliasSeq(T...) { alias AliasSeq = T; }
+
+template Unqual(T)
+{
+ static if (is(T U == const U))
+ alias Unqual = U;
+ else static if (is(T U == immutable U))
+ alias Unqual = U;
+ else
+ alias Unqual = T;
+}
+
+template staticMap(alias F, T...)
+{
+ alias A = AliasSeq!();
+ static foreach (t; T)
+ A = AliasSeq!(A, F!t); // what's tested
+ alias staticMap = A;
+}
+
+alias TK = staticMap!(Unqual, int, const uint);
+//pragma(msg, TK);
+static assert(is(TK == AliasSeq!(int, uint)));
+
+/**************************************************/
+
+template reverse(T...)
+{
+ alias A = AliasSeq!();
+ static foreach (t; T)
+ A = AliasSeq!(t, A); // what's tested
+ alias reverse = A;
+}
+
+enum X2 = 3;
+alias TK2 = reverse!(int, const uint, X2);
+//pragma(msg, TK2);
+static assert(TK2[0] == 3);
+static assert(is(TK2[1] == const(uint)));
+static assert(is(TK2[2] == int));
+
diff --git a/gcc/testsuite/gdc.test/compilable/aliasdecl.d b/gcc/testsuite/gdc.test/compilable/aliasdecl.d
index 90e21effac3..5bacbc62347 100644
--- a/gcc/testsuite/gdc.test/compilable/aliasdecl.d
+++ b/gcc/testsuite/gdc.test/compilable/aliasdecl.d
@@ -29,6 +29,13 @@ void main()
static assert(is(Y4 == void delegate() const));
static assert(is(Y5.Type == int));
+ // https://issues.dlang.org/show_bug.cgi?id=18429
+ struct S
+ {
+ alias a this;
+ enum a = 1;
+ }
+
/+ struct S
{
int value;
diff --git a/gcc/testsuite/gdc.test/compilable/art4769.d b/gcc/testsuite/gdc.test/compilable/art4769.d
index b9620588eaa..34adcda927b 100644
--- a/gcc/testsuite/gdc.test/compilable/art4769.d
+++ b/gcc/testsuite/gdc.test/compilable/art4769.d
@@ -1,6 +1,6 @@
// http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.bugs&article_id=4769
-// EXTRA_SOURCES: imports/art4769a.d imports/art4769b.d
+// COMPILED_IMPORTS: imports/art4769a.d imports/art4769b.d
// PERMUTE_ARGS:
module art4769;
diff --git a/gcc/testsuite/gdc.test/compilable/b1215.d b/gcc/testsuite/gdc.test/compilable/b1215.d
index d3b0ccd2e4a..682881969ed 100644
--- a/gcc/testsuite/gdc.test/compilable/b1215.d
+++ b/gcc/testsuite/gdc.test/compilable/b1215.d
@@ -70,7 +70,7 @@ struct C(Args...)
alias Z = A!(B,B,C!(B,B));
/***************************************************/
-// 14889
+// https://issues.dlang.org/show_bug.cgi?id=14889
struct A14889(alias Exc)
{
@@ -85,7 +85,7 @@ alias X14889b = TT14889!(A14889!Throwable);
alias Y14889b = X14889b[0].ExceptionType;
/***************************************************/
-// 14889
+// https://issues.dlang.org/show_bug.cgi?id=14889
alias TypeTuple14900(T...) = T;
@@ -107,7 +107,7 @@ void test14900()
}
/***************************************************/
-// 14911
+// https://issues.dlang.org/show_bug.cgi?id=14911
void test14911()
{
@@ -119,7 +119,7 @@ void test14911()
}
/***************************************************/
-// 14986
+// https://issues.dlang.org/show_bug.cgi?id=14986
alias Id14986(alias a) = a;
diff --git a/gcc/testsuite/gdc.test/compilable/b12504.d b/gcc/testsuite/gdc.test/compilable/b12504.d
new file mode 100644
index 00000000000..944ff6efa46
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b12504.d
@@ -0,0 +1,44 @@
+// https://issues.dlang.org/show_bug.cgi?id=12504
+void main()
+{
+ {
+ int[0xFF + 1] sta;
+ foreach (ubyte i; 0 .. sta.length) {}
+ foreach (ubyte i, x; sta) {}
+ }
+ {
+ int[0x7F + 1] sta;
+ foreach (byte i; 0 .. sta.length) {}
+ foreach (byte i, x; sta) {}
+ }
+ {
+ int[0xFFFF + 1] sta;
+ foreach (ushort i; 0 .. sta.length) {}
+ foreach (ushort i, x; sta) {}
+ }
+ {
+ int[0x7FFF + 1] sta;
+ foreach (short i; 0 .. sta.length) {}
+ foreach (short i, x; sta) {}
+ }
+ {
+ immutable int[0xFF + 1] sta;
+ static foreach (ubyte i; 0 .. sta.length) {}
+ static foreach (ubyte i, x; sta) {}
+ }
+ {
+ immutable int[0x7F + 1] sta;
+ static foreach (byte i; 0 .. sta.length) {}
+ static foreach (byte i, x; sta) {}
+ }
+ {
+ immutable int[0xFFFF + 1] sta;
+ static foreach (ushort i; 0 .. sta.length) {}
+ static foreach (ushort i, x; sta) {}
+ }
+ {
+ immutable int[0x7FFF + 1] sta;
+ static foreach (short i; 0 .. sta.length) {}
+ static foreach (short i, x; sta) {}
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b15206.d b/gcc/testsuite/gdc.test/compilable/b15206.d
new file mode 100644
index 00000000000..13e7c0061c8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b15206.d
@@ -0,0 +1,19 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -O
+
+void main()
+{
+}
+
+struct Line
+{
+ double slope;
+ double intercept;
+}
+
+Line nLineProjection(double[] historicData)
+{
+ Line projLine;
+ projLine.intercept = historicData[$-1] + projLine.slope;
+ return projLine;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b16360.d b/gcc/testsuite/gdc.test/compilable/b16360.d
new file mode 100644
index 00000000000..11415791b65
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b16360.d
@@ -0,0 +1,39 @@
+/*
+REQUIRED_ARGS: -inline -wi
+
+TEST_OUTPUT:
+---
+compilable/b16360.d(12): Warning: cannot inline function `b16360.foo`
+compilable/b16360.d(25): Warning: cannot inline function `b16360.bar`
+---
+*/
+
+pragma(inline, true)
+auto foo()
+{
+ static struct U
+ {
+ int a = 42;
+ float b;
+ ~this(){} // __dtor: inline not allowed
+ }
+ U u;
+ return u.a;
+}
+
+pragma(inline, true)
+auto bar()
+{
+ class U // class : inline not allowed
+ {
+ int a = 42;
+ float b;
+ }
+ return (new U).a;
+}
+
+void main()
+{
+ auto f = foo();
+ auto b = bar();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b16697.d b/gcc/testsuite/gdc.test/compilable/b16697.d
index 78c9a2b1560..680a9d407f2 100644
--- a/gcc/testsuite/gdc.test/compilable/b16697.d
+++ b/gcc/testsuite/gdc.test/compilable/b16697.d
@@ -1,13 +1,18 @@
-version(D_SIMD)
+static assert(!is( float == __vector));
+static assert(!is( float[1] == __vector));
+static assert(!is( float[4] == __vector));
+static assert(!is(__vector(float[3]) == __vector));
+static assert(!is(__vector(float[5]) == __vector));
+
+static if (__traits(compiles, __vector(float[4])))
{
- static assert(!is( float == __vector));
- static assert(!is( float[1] == __vector));
- static assert(!is( float[4] == __vector));
- static assert( is(__vector(float[4]) == __vector));
- static assert(!is(__vector(float[3]) == __vector));
- static assert(!is(__vector(float[5]) == __vector));
- static assert( is(__vector(float[4]) X == __vector) &&
- is(X == float[4]));
- static assert( is(__vector(byte[16]) X == __vector) &&
- is(X == byte[16]));
+ static assert(is(__vector(float[4]) == __vector));
+ static assert(is(__vector(float[4]) X == __vector) &&
+ is(X == float[4]));
+}
+
+static if (__traits(compiles, __vector(byte[16])))
+{
+ static assert(is(__vector(byte[16]) X == __vector) &&
+ is(X == byte[16]));
}
diff --git a/gcc/testsuite/gdc.test/compilable/b16967.d b/gcc/testsuite/gdc.test/compilable/b16967.d
index 2b02fc3abb8..57bc1c1f258 100644
--- a/gcc/testsuite/gdc.test/compilable/b16967.d
+++ b/gcc/testsuite/gdc.test/compilable/b16967.d
@@ -1,4 +1,4 @@
-/*
+/*
* REQUIRED_ARGS: -c
* TEST_OUTPUT:
---
@@ -27,7 +27,7 @@ out(v)
break;
}
}
-body
+do
{
return 42;
}
diff --git a/gcc/testsuite/gdc.test/compilable/b17111.d b/gcc/testsuite/gdc.test/compilable/b17111.d
index 6bf5da9f3e2..9e79a20780c 100644
--- a/gcc/testsuite/gdc.test/compilable/b17111.d
+++ b/gcc/testsuite/gdc.test/compilable/b17111.d
@@ -1,16 +1,7 @@
-/*
-TEST_OUTPUT:
----
-compilable/b17111.d(16): Deprecation: `case` variables have to be `const` or `immutable`
-compilable/b17111.d(17): Deprecation: `case` variables have to be `const` or `immutable`
----
-*/
alias TestType = ubyte;
-void test()
+void test(immutable TestType a, immutable TestType b, TestType c)
{
- TestType a,b,c;
-
switch(c)
{
case a: break;
diff --git a/gcc/testsuite/gdc.test/compilable/b17651.d b/gcc/testsuite/gdc.test/compilable/b17651.d
new file mode 100644
index 00000000000..9b41b9ef932
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b17651.d
@@ -0,0 +1,6 @@
+// REQUIRED_ARGS: -c -D -Ddtest_results/compilable
+/**
+Macros:
+Escapes=/a/b/
+*/
+void foo() {}
diff --git a/gcc/testsuite/gdc.test/compilable/b18197.d b/gcc/testsuite/gdc.test/compilable/b18197.d
new file mode 100644
index 00000000000..c6f92e11459
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b18197.d
@@ -0,0 +1,17 @@
+// REQUIRED_ARGS: -c -m32 -O -inline
+
+struct A
+{
+ double a;
+}
+
+A makeA(double value)
+{
+ return A(value);
+}
+
+double test(double x)
+{
+ ulong p = *cast(ulong *)&x;
+ return makeA(x).a;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b18242.d b/gcc/testsuite/gdc.test/compilable/b18242.d
new file mode 100644
index 00000000000..5dcaeca73b4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b18242.d
@@ -0,0 +1,19 @@
+// REQUIRED_ARGS: -c
+// PERMUTE_ARGS:
+module object;
+
+class Object { }
+
+class TypeInfo { }
+class TypeInfo_Class : TypeInfo
+{
+ version(D_LP64) { ubyte[136] _x; } else { ubyte[68] _x; }
+}
+
+class Throwable { }
+
+int _d_run_main()
+{
+ try { } catch(Throwable e) { return 1; }
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b18489.d b/gcc/testsuite/gdc.test/compilable/b18489.d
new file mode 100644
index 00000000000..2cc386f307a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b18489.d
@@ -0,0 +1,8 @@
+// REQUIRED_ARGS: -O -m64
+import core.simd;
+
+double dot (double2 a) {
+ return a.ptr[0] * a.ptr[1];
+}
+
+void main () { }
diff --git a/gcc/testsuite/gdc.test/compilable/b19432.d b/gcc/testsuite/gdc.test/compilable/b19432.d
new file mode 100644
index 00000000000..cf0dd876b2d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b19432.d
@@ -0,0 +1,5 @@
+
+void main(){
+ enum a = 18446744073709551615; // 2^^64 - 1
+ ulong b = 18446744073709551615; // 2^^64 - 1
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b19442.d b/gcc/testsuite/gdc.test/compilable/b19442.d
new file mode 100644
index 00000000000..4e7e933182d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b19442.d
@@ -0,0 +1,11 @@
+// https://issues.dlang.org/show_bug.cgi?id=19442
+enum x1 = 42;
+enum x2 = mixin('x', 1);
+enum x3 = mixin(wchar('x'), 2);
+enum x4 = mixin(dchar('x'), 3);
+static assert(x2 == 42);
+static assert(x3 == 42);
+static assert(x4 == 42);
+
+mixin(`enum string s = "`, wchar('ö'), dchar('ðŸº'), `";`);
+static assert(s == "öðŸº");
diff --git a/gcc/testsuite/gdc.test/compilable/b19775.d b/gcc/testsuite/gdc.test/compilable/b19775.d
new file mode 100644
index 00000000000..ec504d46d9a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b19775.d
@@ -0,0 +1,14 @@
+// https://issues.dlang.org/show_bug.cgi?id=19775
+enum x1 = 42;
+enum ident(alias a, args...) = mixin(a, args);
+
+//enum x2 = ident!("x1"); //FIXME - empty args
+enum x2 = x1;
+enum x3 = ident!("x", 2);
+enum x4 = ident!('x', "", 3);
+enum x5 = ident!("", 'x', 4);
+
+//static assert(x2 == 42);
+static assert(x3 == 42);
+static assert(x4 == 42);
+static assert(x5 == 42);
diff --git a/gcc/testsuite/gdc.test/compilable/b19829.d b/gcc/testsuite/gdc.test/compilable/b19829.d
new file mode 100644
index 00000000000..7f4b282d243
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b19829.d
@@ -0,0 +1,4 @@
+static assert(!__traits(isSame, (i){ return i,x.value; }, a => a.value));
+static assert(!__traits(isSame, i => x[i].value, a => a.value));
+static assert(!__traits(isSame, i => [i].value, a => a.value));
+static assert(__traits(isSame, i => i.value, a => a.value));
diff --git a/gcc/testsuite/gdc.test/compilable/b20045.d b/gcc/testsuite/gdc.test/compilable/b20045.d
new file mode 100644
index 00000000000..988bbca5d96
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b20045.d
@@ -0,0 +1,2 @@
+alias U = const ubyte[uint.sizeof]*;
+static assert (is(U == const(ubyte[4]*)));
diff --git a/gcc/testsuite/gdc.test/compilable/b20067.d b/gcc/testsuite/gdc.test/compilable/b20067.d
new file mode 100644
index 00000000000..af79cabfed0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b20067.d
@@ -0,0 +1,23 @@
+struct S1
+{
+ int i;
+ @property int ii(){return 0;}
+ @property bool b(){return true;}
+ alias empty = b;
+ alias front = ii;
+ void popFront(){}
+}
+struct S2
+{
+ int i;
+ bool b;
+ alias empty = b;
+ alias front = i;
+ void popFront(){}
+}
+
+void main()
+{
+ foreach(n; S1()) { } // 2.086: Error: cannot infer argument types
+ foreach(n; S2()) { } // 2.086: Error: cannot infer argument types
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b20758.d b/gcc/testsuite/gdc.test/compilable/b20758.d
new file mode 100644
index 00000000000..58b30b1372e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b20758.d
@@ -0,0 +1,15 @@
+module b20758;
+
+template foo(A...) { }
+
+int attr() {return 1;}
+@attr int y;
+
+alias A = __traits(getAttributes, y);
+alias B = __traits(getOverloads, b20758, "attr");
+static assert(__traits(isSame, foo!(A[0]), foo!(attr)));
+static assert(__traits(isSame, foo!(A), foo!(attr)));
+static assert(__traits(isSame, foo!(attr), foo!(B[0])));
+static assert(__traits(isSame, foo!(attr), foo!(B)));
+
+void main() { }
diff --git a/gcc/testsuite/gdc.test/compilable/b20780.d b/gcc/testsuite/gdc.test/compilable/b20780.d
new file mode 100644
index 00000000000..366a2d9d1e0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b20780.d
@@ -0,0 +1,13 @@
+void main()
+{
+ struct A;
+ struct B { struct CD;}
+ alias V = void;
+ alias I = int;
+ V test0(@A I) {}
+ V test1(@A I p) {}
+ V test2(@A @(B) I) {}
+ V test3(@(B.CD) @B I) {}
+ V test4(@A I, @B @A I) {}
+ V test5(@A I p, @(B.CD) @A I ) {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b20833.d b/gcc/testsuite/gdc.test/compilable/b20833.d
new file mode 100644
index 00000000000..429d1f51ba2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b20833.d
@@ -0,0 +1,20 @@
+struct A
+{
+ void foo(T)(T t) {}
+ void foo(long l) {}
+
+ void bar(long l) {}
+ void bar(T)(T t) {}
+}
+
+static assert(__traits(getOverloads, A, "foo").length == 1);
+static assert(__traits(getOverloads, A.init, "foo").length == 1);
+
+static assert(__traits(getOverloads, A, "foo", true).length == 2);
+static assert(__traits(getOverloads, A.init, "foo", true).length == 2);
+
+static assert(__traits(getOverloads, A, "bar").length == 1);
+static assert(__traits(getOverloads, A.init, "bar").length == 1);
+
+static assert(__traits(getOverloads, A, "bar", true).length == 2);
+static assert(__traits(getOverloads, A.init, "bar", true).length == 2);
diff --git a/gcc/testsuite/gdc.test/compilable/b20885.d b/gcc/testsuite/gdc.test/compilable/b20885.d
new file mode 100644
index 00000000000..6e871845d0f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b20885.d
@@ -0,0 +1,16 @@
+module b20885;
+
+struct S
+{
+ alias P = void*;
+}
+
+void main()
+{
+ alias P = void*;
+ alias PP = void**;
+ PP[1] a = null;
+ if (const void** b = a[0]){} // OK
+ if (const P* b = a[0]){} // NG
+ if (const S.P* b = a[0]){} // NG
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b20938.d b/gcc/testsuite/gdc.test/compilable/b20938.d
new file mode 100644
index 00000000000..efcf4aab06f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b20938.d
@@ -0,0 +1,22 @@
+// issue 20938 - Cannot create const arrays mixing immutable and mutable structs with indirections
+struct S { int[] a; }
+enum A { a }
+enum B { b }
+
+void fun() {
+ int* pi;
+ immutable int* ipi;
+ int[] ai;
+ immutable int[] iai;
+ S s;
+ immutable S _is;
+ Object o;
+ immutable Object io;
+
+ auto a = [pi, ipi];
+ auto b = [ai, iai];
+ auto c = [s, _is];
+ auto d = [o, io];
+
+ auto e = [A.a, B.b];
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b21285.d b/gcc/testsuite/gdc.test/compilable/b21285.d
new file mode 100644
index 00000000000..11eea74b669
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/b21285.d
@@ -0,0 +1,27 @@
+// REQUIRED_ARGS: -unittest
+// Issue 21285 - Delegate covariance broken between 2.092 and 2.094 (git master).
+unittest
+{
+ string path;
+ int bank;
+ static string path2;
+ static int bank2;
+
+ // delegates
+ auto a = [
+ (string arg) { path = arg; },
+ (string arg) { bank = 1; throw new Exception(""); }
+ ];
+
+ // functions
+ auto ab = [
+ (string arg) { path2 = arg; },
+ (string arg) { bank2 = 1; throw new Exception(""); }
+ ];
+
+ alias dg = void delegate(string) pure @safe;
+ alias fn = void function(string) @safe;
+
+ static assert(is(typeof(a[0]) == dg));
+ static assert(is(typeof(ab[0]) == fn));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/b33.d b/gcc/testsuite/gdc.test/compilable/b33.d
index d422959d51e..cba6fe2272f 100644
--- a/gcc/testsuite/gdc.test/compilable/b33.d
+++ b/gcc/testsuite/gdc.test/compilable/b33.d
@@ -1,4 +1,4 @@
-// EXTRA_SOURCES: imports/b33a.d
+// COMPILED_IMPORTS: imports/b33a.d
// PERMUTE_ARGS:
module b33;
diff --git a/gcc/testsuite/gdc.test/compilable/b6227.d b/gcc/testsuite/gdc.test/compilable/b6227.d
index 6ec2dd0a3de..2a7700a0965 100644
--- a/gcc/testsuite/gdc.test/compilable/b6227.d
+++ b/gcc/testsuite/gdc.test/compilable/b6227.d
@@ -1,9 +1,3 @@
-/* TEST_OUTPUT:
----
-compilable/b6227.d(17): Deprecation: Comparison between different enumeration types `X` and `Y`; If this behavior is intended consider using `std.conv.asOriginalType`
-compilable/b6227.d(18): Deprecation: Comparison between different enumeration types `X` and `Y`; If this behavior is intended consider using `std.conv.asOriginalType`
----
-*/
enum X {
O,
R
@@ -14,5 +8,3 @@ enum Y {
static assert( (X.O == cast(const)X.O));
static assert( (X.O == X.O));
static assert( (X.O != X.R));
-static assert(!(X.O != Y.U));
-static assert( (X.O == Y.U));
diff --git a/gcc/testsuite/gdc.test/compilable/b6395.d b/gcc/testsuite/gdc.test/compilable/b6395.d
index afbe3f19b9b..94f4bc3a622 100644
--- a/gcc/testsuite/gdc.test/compilable/b6395.d
+++ b/gcc/testsuite/gdc.test/compilable/b6395.d
@@ -1,7 +1,7 @@
// REQUIRED_ARGS: -Icompilable/extra-files
// EXTRA_FILES: extra-files/c6395.d
-// 6395
+// https://issues.dlang.org/show_bug.cgi?id=6395
import c6395;
diff --git a/gcc/testsuite/gdc.test/compilable/b6400.d b/gcc/testsuite/gdc.test/compilable/b6400.d
deleted file mode 100644
index 2c71107e913..00000000000
--- a/gcc/testsuite/gdc.test/compilable/b6400.d
+++ /dev/null
@@ -1,37 +0,0 @@
-/* TEST_OUTPUT:
----
-Foo
-Bar
----
-*/
-class Foo
-{
- void opDispatch(string name)() { pragma(msg, "Foo"); }
-}
-class Bar
-{
- void opDispatch(string name)() { pragma(msg, "Bar"); }
-}
-class Baz
-{
-}
-
-void main()
-{
- auto foo = new Foo;
- auto bar = new Bar;
- auto baz = new Baz;
-
- with (foo)
- {
- f0();
- with (bar)
- {
- f1();
- }
- with (baz)
- {
- static assert(!__traits(compiles, f2()));
- }
- }
-}
diff --git a/gcc/testsuite/gdc.test/compilable/betterc.d b/gcc/testsuite/gdc.test/compilable/betterc.d
new file mode 100644
index 00000000000..7df9bc22a4b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/betterc.d
@@ -0,0 +1,27 @@
+/* REQUIRED_ARGS: -betterC
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=17787
+version (D_BetterC)
+{
+}
+else
+{
+ static assert(0);
+}
+
+// -betterC does not support `ModuleInfo`, `TypeInfo`, or exception handling
+version (D_ModuleInfo)
+{
+ static assert(0);
+}
+
+version (D_Exceptions)
+{
+ static assert(0);
+}
+
+version (D_TypeInfo)
+{
+ static assert(0);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/bug21196.d b/gcc/testsuite/gdc.test/compilable/bug21196.d
new file mode 100644
index 00000000000..c2f9a43d336
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/bug21196.d
@@ -0,0 +1,31 @@
+/*
+REQUIRED_ARGS: -de
+*/
+
+// This test can be removed once the deprecation period is over
+deprecated void appendSlices ( Types ... ) ( ref void[][] slices, ref Types x )
+{
+ foreach (i, T; Types)
+ {
+ static if (is(T Element: Element[]))
+ {
+ static if (is(T == Element[]))
+ {
+ slices ~= (cast(void*)(&x[i]))[0 .. size_t.sizeof];
+ }
+ // Append a slice to the array content.
+ slices ~= x[i];
+ }
+ else
+ {
+ slices ~= (cast(void*)(&x[i]))[0 .. x[i].sizeof];
+ }
+ }
+}
+
+deprecated void myTest()
+{
+ void[][] slices;
+ char[] str = "Hello World!".dup;
+ appendSlices(slices, str);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/callconv.d b/gcc/testsuite/gdc.test/compilable/callconv.d
index 145ebb1420a..42c47d8f576 100644
--- a/gcc/testsuite/gdc.test/compilable/callconv.d
+++ b/gcc/testsuite/gdc.test/compilable/callconv.d
@@ -1,10 +1,9 @@
// PERMUTE_ARGS:
-
import core.stdc.stdarg;
struct ABC
{
- int x[4];
+ int[4] x;
}
ABC abc;
diff --git a/gcc/testsuite/gdc.test/compilable/ccompile.d b/gcc/testsuite/gdc.test/compilable/ccompile.d
new file mode 100644
index 00000000000..22c749cc5b9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ccompile.d
@@ -0,0 +1,36 @@
+
+/* REQUIRED_ARGS: -O
+ */
+
+// Adapted from DMC++ test file test3/ccompile.c
+
+
+ struct HDS {
+ char state;
+ uint done;
+ uint retry;
+ uint[15] tests;
+ }
+
+void funchds(char *p_adults)
+{
+ int cupx, chemx;
+ HDS *p_cup;
+
+ for (cupx = 1, p_cup = null; cupx <= 48 ; cupx ++, p_cup ++)
+ {
+ for (chemx = 0; chemx < 15 ; chemx++)
+ {
+ if (p_cup.done) {
+ if (p_cup.tests [chemx]) {
+ *p_adults++ = 3;
+ }
+ if (p_cup.done && (p_cup.tests [chemx])) {
+ *p_adults++ = 4;
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/gcc/testsuite/gdc.test/compilable/cdcmp.d b/gcc/testsuite/gdc.test/compilable/cdcmp.d
new file mode 100644
index 00000000000..614bdc8c984
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/cdcmp.d
@@ -0,0 +1,148 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -O
+// POST_SCRIPT: compilable/extra-files/objdump-postscript.sh
+// only testing on SYSV-ABI, but backend code is identical across platforms
+// DISABLED: win32 win64 osx linux32 freebsd32 freebsd64
+
+bool test_ltz(ubyte x) { return x < 0; }
+bool test_lez(ubyte x) { return x <= 0; }
+bool test_eqz(ubyte x) { return x == 0; }
+bool test_nez(ubyte x) { return x != 0; }
+bool test_gez(ubyte x) { return x >= 0; }
+bool test_gtz(ubyte x) { return x > 0; }
+
+bool test_ltz(byte x) { return x < 0; }
+bool test_lez(byte x) { return x <= 0; }
+bool test_eqz(byte x) { return x == 0; }
+bool test_nez(byte x) { return x != 0; }
+bool test_gez(byte x) { return x >= 0; }
+bool test_gtz(byte x) { return x > 0; }
+
+bool test_ltz(ushort x) { return x < 0; }
+bool test_lez(ushort x) { return x <= 0; }
+bool test_eqz(ushort x) { return x == 0; }
+bool test_nez(ushort x) { return x != 0; }
+bool test_gez(ushort x) { return x >= 0; }
+bool test_gtz(ushort x) { return x > 0; }
+
+bool test_ltz(short x) { return x < 0; }
+bool test_lez(short x) { return x <= 0; }
+bool test_eqz(short x) { return x == 0; }
+bool test_nez(short x) { return x != 0; }
+bool test_gez(short x) { return x >= 0; }
+bool test_gtz(short x) { return x > 0; }
+
+bool test_ltz(uint x) { return x < 0; }
+bool test_lez(uint x) { return x <= 0; }
+bool test_eqz(uint x) { return x == 0; }
+bool test_nez(uint x) { return x != 0; }
+bool test_gez(uint x) { return x >= 0; }
+bool test_gtz(uint x) { return x > 0; }
+
+bool test_ltz(int x) { return x < 0; }
+bool test_lez(int x) { return x <= 0; }
+bool test_eqz(int x) { return x == 0; }
+bool test_nez(int x) { return x != 0; }
+bool test_gez(int x) { return x >= 0; }
+bool test_gtz(int x) { return x > 0; }
+
+bool test_ltz(ulong x) { return x < 0; }
+bool test_lez(ulong x) { return x <= 0; }
+bool test_eqz(ulong x) { return x == 0; }
+bool test_nez(ulong x) { return x != 0; }
+bool test_gez(ulong x) { return x >= 0; }
+bool test_gtz(ulong x) { return x > 0; }
+
+bool test_ltz(long x) { return x < 0; }
+bool test_lez(long x) { return x <= 0; }
+bool test_eqz(long x) { return x == 0; }
+bool test_nez(long x) { return x != 0; }
+bool test_gez(long x) { return x >= 0; }
+bool test_gtz(long x) { return x > 0; }
+
+bool test_ltz(float x) { return x < 0; }
+bool test_lez(float x) { return x <= 0; }
+bool test_eqz(float x) { return x == 0; }
+bool test_nez(float x) { return x != 0; }
+bool test_gez(float x) { return x >= 0; }
+bool test_gtz(float x) { return x > 0; }
+
+bool test_ltz(double x) { return x < 0; }
+bool test_lez(double x) { return x <= 0; }
+bool test_eqz(double x) { return x == 0; }
+bool test_nez(double x) { return x != 0; }
+bool test_gez(double x) { return x >= 0; }
+bool test_gtz(double x) { return x > 0; }
+
+/* ----------------------------------- */
+
+bool test_lt(ubyte x, ubyte y) { return x < y; }
+bool test_le(ubyte x, ubyte y) { return x <= y; }
+bool test_eq(ubyte x, ubyte y) { return x == y; }
+bool test_ne(ubyte x, ubyte y) { return x != y; }
+bool test_ge(ubyte x, ubyte y) { return x >= y; }
+bool test_gt(ubyte x, ubyte y) { return x > y; }
+
+bool test_lt(byte x, byte y) { return x < y; }
+bool test_le(byte x, byte y) { return x <= y; }
+bool test_eq(byte x, byte y) { return x == y; }
+bool test_ne(byte x, byte y) { return x != y; }
+bool test_ge(byte x, byte y) { return x >= y; }
+bool test_gt(byte x, byte y) { return x > y; }
+
+bool test_lt(ushort x, ushort y) { return x < y; }
+bool test_le(ushort x, ushort y) { return x <= y; }
+bool test_eq(ushort x, ushort y) { return x == y; }
+bool test_ne(ushort x, ushort y) { return x != y; }
+bool test_ge(ushort x, ushort y) { return x >= y; }
+bool test_gt(ushort x, ushort y) { return x > y; }
+
+bool test_lt(short x, short y) { return x < y; }
+bool test_le(short x, short y) { return x <= y; }
+bool test_eq(short x, short y) { return x == y; }
+bool test_ne(short x, short y) { return x != y; }
+bool test_ge(short x, short y) { return x >= y; }
+bool test_gt(short x, short y) { return x > y; }
+
+bool test_lt(uint x, uint y) { return x < y; }
+bool test_le(uint x, uint y) { return x <= y; }
+bool test_eq(uint x, uint y) { return x == y; }
+bool test_ne(uint x, uint y) { return x != y; }
+bool test_ge(uint x, uint y) { return x >= y; }
+bool test_gt(uint x, uint y) { return x > y; }
+
+bool test_lt(int x, int y) { return x < y; }
+bool test_le(int x, int y) { return x <= y; }
+bool test_eq(int x, int y) { return x == y; }
+bool test_ne(int x, int y) { return x != y; }
+bool test_ge(int x, int y) { return x >= y; }
+bool test_gt(int x, int y) { return x > y; }
+
+bool test_lt(ulong x, ulong y) { return x < y; }
+bool test_le(ulong x, ulong y) { return x <= y; }
+bool test_eq(ulong x, ulong y) { return x == y; }
+bool test_ne(ulong x, ulong y) { return x != y; }
+bool test_ge(ulong x, ulong y) { return x >= y; }
+bool test_gt(ulong x, ulong y) { return x > y; }
+
+bool test_lt(long x, long y) { return x < y; }
+bool test_le(long x, long y) { return x <= y; }
+bool test_eq(long x, long y) { return x == y; }
+bool test_ne(long x, long y) { return x != y; }
+bool test_ge(long x, long y) { return x >= y; }
+bool test_gt(long x, long y) { return x > y; }
+
+bool test_lt(float x, float y) { return x < y; }
+bool test_le(float x, float y) { return x <= y; }
+bool test_eq(float x, float y) { return x == y; }
+bool test_ne(float x, float y) { return x != y; }
+bool test_ge(float x, float y) { return x >= y; }
+bool test_gt(float x, float y) { return x > y; }
+
+bool test_lt(double x, double y) { return x < y; }
+bool test_le(double x, double y) { return x <= y; }
+bool test_eq(double x, double y) { return x == y; }
+bool test_ne(double x, double y) { return x != y; }
+bool test_ge(double x, double y) { return x >= y; }
+bool test_gt(double x, double y) { return x > y; }
+
diff --git a/gcc/testsuite/gdc.test/compilable/chkformat.d b/gcc/testsuite/gdc.test/compilable/chkformat.d
new file mode 100644
index 00000000000..ccbe974f400
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/chkformat.d
@@ -0,0 +1,27 @@
+// https://issues.dlang.org/show_bug.cgi?id=20643
+// https://issues.dlang.org/show_bug.cgi?id=20644
+/*
+TEST_OUTPUT:
+----
+compilable/chkformat.d(14): Deprecation: more format specifiers than 0 arguments
+----
+*/
+import core.stdc.stdio;
+
+void main()
+{
+ // b20643
+ printf("%d \n");
+
+ // b20644
+ ubyte b;
+ printf("%hhu \n", b);
+
+ char c = '-';
+ printf("%c", c);
+
+ short s;
+ printf("%hd", s);
+
+ printf("%hn", &s);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/commontype.d b/gcc/testsuite/gdc.test/compilable/commontype.d
new file mode 100644
index 00000000000..076e29baece
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/commontype.d
@@ -0,0 +1,486 @@
+// REQUIRED_ARGS: -mcpu=avx2
+
+import core.simd;
+
+
+version = CondExp;
+//version = ReturnInfer;
+
+T rvalueOf(T)();
+
+version (CondExp)
+{
+ alias X(T, U) = typeof(0 ? rvalueOf!T : rvalueOf!U);
+}
+else version (ReturnInfer)
+{
+ alias X(T, U) = typeof({ if (0) return rvalueOf!T; else return rvalueOf!U;}());
+}
+else
+ static assert(0);
+
+enum Error(T, U) = !__traits(compiles, X!(T, U));
+
+
+interface I {}
+class B {}
+class C : B, I {}
+class D : B {}
+class K {}
+struct SI { I o; alias o this; }
+struct SC { C o; alias o this; }
+struct SB { B o; alias o this; }
+struct SD { D o; alias o this; }
+struct SK { K o; alias o this; }
+struct SiC { immutable(C) o; alias o this; }
+
+struct S { int a; }
+struct Si { int a; alias a this; }
+struct Si2 { int a; alias a this; }
+struct Sl { long a; alias a this; }
+
+/******************************
+ * Basic types
+ */
+
+static assert(is( X!( byte, byte ) == byte ));
+static assert(is( X!( ubyte, ubyte ) == ubyte ));
+static assert(is( X!( byte, ubyte ) == int ));
+static assert(is( X!( byte, short ) == int ));
+static assert(is( X!( byte, ushort ) == int ));
+static assert(is( X!( byte, const(byte) ) == int ));
+static assert(is( X!( ubyte, const(ubyte) ) == int ));
+
+static assert(is( X!( short, short ) == short ));
+static assert(is( X!( ushort, ushort ) == ushort ));
+static assert(is( X!( short, ushort ) == int ));
+static assert(is( X!( short, const(short) ) == int ));
+static assert(is( X!( ushort, const(ushort) ) == int ));
+
+static assert(is( X!( int, int ) == int ));
+static assert(is( X!( int, uint ) == uint ));
+static assert(is( X!( uint, uint ) == uint ));
+
+static assert(is( X!( int, long ) == long ));
+static assert(is( X!( int, ulong ) == ulong ));
+
+static assert(is( X!( float, float ) == float ));
+static assert(is( X!( float, int ) == float ));
+static assert(is( X!( float, uint ) == float ));
+static assert(is( X!( float, long ) == float ));
+static assert(is( X!( float, ulong ) == float ));
+static assert(is( X!( float, double ) == double ));
+static assert(is( X!( float, real ) == real ));
+
+static assert(is( X!( char, char ) == char ));
+static assert(is( X!( char, byte ) == int ));
+static assert(is( X!( char, ubyte ) == int ));
+static assert(is( X!( char, wchar ) == dchar ));
+static assert(is( X!( char, dchar ) == dchar ));
+static assert(is( X!( char, const(char) ) == const(char) ));
+static assert(is( X!( wchar, const(wchar) ) == const(wchar) ));
+static assert(is( X!( dchar, const(dchar) ) == const(dchar) ));
+static assert(is( X!( char, immutable(char) ) == const(char) ));
+static assert(Error!( char, shared(char) ));
+
+static assert(is( X!( char, float ) == float ));
+
+static assert(is( X!( immutable(int), int ) == int ));
+static assert(is( X!( const(int), int ) == int ));
+static assert(is( X!( shared(int), int ) == int ));
+static assert(is( X!( immutable(int), const(shared(int)) ) == int ));
+static assert(is( X!( shared(int), const(int) ) == int ));
+
+/******************************
+ * Strings
+ */
+
+static assert(is( X!( string, string ) == string ));
+static assert(Error!( wstring, string ));
+static assert(Error!( dstring, string ));
+static assert(Error!( dstring, wstring ));
+static assert(is( X!( const(char)[], string ) == const(char)[] ));
+static assert(is( X!( char[], string ) == const(char)[] ));
+static assert(is( X!( string, immutable(string) ) == immutable(string) )); // `const`
+static assert(is( X!( immutable(string), string ) == string )); // not commutative
+
+/******************************
+ * Enums
+ */
+
+enum Ei : int { a, }
+enum Eb : byte { a, }
+enum Ec : char { a, }
+enum Ew : wchar { a, }
+
+static assert(is( X!( Ei, Ei ) == Ei ));
+static assert(is( X!( Ei, const(Ei) ) == const(Ei) ));
+static assert(is( X!( Ei, immutable(Ei) ) == const(Ei) ));
+static assert(is( X!( Eb, Eb ) == Eb ));
+static assert(is( X!( Eb, const(Eb) ) == int ));
+static assert(is( X!( Eb, immutable(Eb) ) == int ));
+static assert(is( X!( Ei, Eb ) == int ));
+static assert(is( X!( Ei, const(Eb) ) == int ));
+static assert(is( X!( Ei, immutable(Eb) ) == int ));
+
+static assert(is( X!( Ec, Ec ) == Ec ));
+static assert(is( X!( Ec, const(Ec) ) == const(char) ));
+static assert(is( X!( Ec, immutable(Ec) ) == const(char) ));
+static assert(is( X!( Ew, Ew ) == Ew ));
+static assert(is( X!( Ew, const(Ew) ) == const(wchar) ));
+static assert(is( X!( Ew, immutable(Ew) ) == const(wchar) ));
+static assert(is( X!( Ew, Ec ) == dchar ));
+static assert(is( X!( Ew, const(Ec) ) == dchar ));
+static assert(is( X!( Ew, immutable(Ec) ) == dchar ));
+
+/******************************
+ * Tuple
+ */
+
+alias Tuple(Args...) = Args;
+static assert(!__traits(compiles ,typeof(0 ? Tuple!1 : Tuple!1)));
+
+/******************************
+ * Pointers
+ */
+
+static assert(is( X!( int*, int* ) == int* ));
+static assert(is( X!( const(int*), const(int)* ) == const(int*) ));
+static assert(is( X!( const(int)*, const(int*) ) == const(int)* )); // not commutative
+static assert(Error!( uint*, int* ));
+static assert(is( X!( int function(), int function() ) == int function() ));
+
+// void pointer
+static assert(is( X!( void*, int* ) == int* ));
+static assert(is( X!( int*, void* ) == int* ));
+static assert(is( X!( const(int)*, void* ) == const(int)* ));
+static assert(is( X!( const(int*), void* ) == const(int*) ));
+static assert(is( X!( int*, const(void)* ) == int* )); // `const`
+static assert(is( X!( int*, const(void*) ) == int* )); // `const`
+static assert(is( X!( int*, shared(void*) ) == int* )); // should fail
+static assert(is( X!( int*, shared(void)* ) == int* )); // should fail
+
+static assert(Error!( int**, void** )); // should work
+
+static assert(is( X!( void*, int function() ) == int function() ));
+static assert(is( X!( immutable(void*), int function() ) == int function() )); // `const`
+
+// implicit conversion
+static assert(is( X!( int*, const(int*) ) == const(int*) ));
+static assert(is( X!( int*, const(int)* ) == const(int)* ));
+static assert(is( X!( int***, const(int)*** ) == const(int**)* ));
+static assert(is( X!( immutable(int)***, const(int)*** ) == const(int**)* ));
+static assert(is( X!( immutable(int)***, const(shared(int))*** ) == const(shared(int)**)* ));
+
+// common modifier
+static assert(is( X!( immutable(int)*, int* ) == const(int)* ));
+static assert(is( X!( immutable(int*), int* ) == const(int)* ));
+static assert(is( X!( immutable(int)*, shared(int)* ) == shared(const(int))* ));
+static assert(Error!( shared(int)*, int* ));
+
+static assert(is( X!( immutable(int)***, int*** ) == const(int**)* )); // `const(int)***`
+static assert(is( X!( shared(const(int)***), shared(int***) ) == const(shared(int*)*)* )); // `shared(const(int)***)`
+static assert(is( X!( shared(const(int)***), shared(int**)* ) == const(shared(int*)*)* ));
+static assert(Error!( shared(const(int)***), shared(int*)** ));
+
+
+// class pointer
+static assert(is( X!( C*, B* ) == B* ));
+static assert(is( X!( const(C)*, B* ) == const(B)* ));
+static assert(Error!( shared(C)*, B* ));
+static assert(is( X!( immutable(C)*, B* ) == const(B)* ));
+static assert(is( X!( immutable(C*), B* ) == const(B)* ));
+static assert(is( X!( C**, B** ) == const(B*)* )); // `B**`
+static assert(is( X!( B**, C** ) == const(B*)* )); // `B**`
+static assert(is( X!( C***, B*** ) == const(B**)* )); // `B***`
+
+static assert(is( X!( C*, I* ) == I* ));
+static assert(is( X!( I*, C* ) == I* ));
+static assert(Error!( C**, I** ));
+
+static assert(Error!( C*, D* )); // should work
+
+// function pointer
+static assert(is( X!( immutable(int function()), int function() ) == immutable(int function()) ));
+static assert(is( X!( int function(), immutable(int function()) ) == int function() )); // not commutative
+static assert(is( X!( int function(), shared(int function()) ) == int function() ));
+
+static assert(is( X!( int function(), const(int) function() ) == const(int) function() ));
+static assert(is( X!( int function(), immutable(int) function() ) == immutable(int) function() )); // `const`
+static assert(is( X!( immutable(int) function(), int function() ) == int function() )); // not commutative
+
+static assert(Error!( uint function(), int function() ));
+
+static assert(is( X!( C function(), B function() ) == B function() ));
+static assert(is( X!( B function(), C function() ) == B function() ));
+static assert(is( X!( C function(), const(B) function() ) == const(B) function() ));
+static assert(Error!( const(C) function(), B function() )); // should work
+static assert(Error!( C* function(), B* function() )); // should work
+static assert(Error!( C function(), I function() ));
+static assert(Error!( C* function(), I* function() ));
+
+static assert(is( X!( C delegate(), B delegate() ) == B delegate() ));
+static assert(Error!( C delegate(), I delegate() ));
+
+static assert(Error!( C function(), D function() )); // should work
+
+static assert(Error!( void function(int), void function(const(int)) )); // should work
+
+static assert(Error!( void function(C), void function(B) ));
+static assert(Error!( void function(C*), void function(B*) ));
+static assert(Error!( void function(const(C)*), void function(B*) ));
+static assert(is( X!( void function(C*), void function(const(B*)) ) == void function(C*) )); // !?
+
+static assert(is( X!( void function(C), void function(const(C)) ) == void function(C) ));
+static assert(is( X!( void function(C), void function(const(C)) ) == void function(C) ));
+
+static assert(is( X!( void function() pure nothrow @nogc @safe, void function() ) == void function() ));
+static assert(is( X!( void function() pure @safe, void function() @nogc ) == void function() ));
+static assert(is( X!( void function() pure @trusted, void function() nothrow @safe ) == void function() @trusted ));
+static assert(is( X!( void function() @trusted, void function() ) == void function()));
+static assert(is( X!( void function() @trusted, void function() @trusted ) == void function() @trusted ));
+static assert(is( X!( void function() @safe, void function() @trusted ) == void function() @trusted ));
+static assert(is( X!( void function() @trusted, void function() @safe ) == void function() @safe )); // not commutative
+
+static assert(is( X!( const(int function())*, int function()* ) == const(int function())* ));
+static assert(is( X!( immutable(int function())*, int function()* ) == const(int function())* ));
+static assert(Error!( shared(int function())*, int function()* ));
+static assert(is( X!( shared(int function())*, immutable(int function())* ) == shared(const(int function()))* ));
+
+
+/******************************
+ * Arrays
+ */
+
+static assert(is( X!( int[4], int[4] ) == int[4] ));
+static assert(is( X!( int[], int[] ) == int[] ));
+
+// static array modifier conversion
+static assert(is( X!( const(int)[4], int[4] ) == int[4] ));
+static assert(is( X!( int[4], const(int)[4] ) == const(int)[4] )); // not commutative
+static assert(is( X!( const(int)[4], immutable(int)[4] ) == immutable(int)[4] ));
+static assert(is( X!( immutable(int)[4], const(int)[4] ) == const(int)[4] )); // not commutative
+static assert(is( X!( int[4], immutable(int)[4] ) == immutable(int)[4] ));
+static assert(is( X!( immutable(int)[4], int[4] ) == int[4] )); // not commutative
+static assert(Error!( shared(int)[4], int[4] )); // should work
+static assert(Error!( int[4], shared(int)[4] )); // should work
+static assert(Error!( shared(int)[4], const(int)[4] )); // should work
+static assert(Error!( const(int)[4] , shared(int)[4])); // should work
+static assert(is( X!( shared(const(int))[4], shared(int)[4] ) == shared(int)[4] ));
+static assert(is( X!( shared(int)[4], shared(const(int))[4] ) == shared(const(int))[4] )); // not commutative
+static assert(is( X!( shared(const(int))[4], immutable(int)[4] ) == shared(const(int))[4] ));
+static assert(is( X!( immutable(int)[4], shared(const(int))[4] ) == shared(const(int))[4] ));
+
+static assert(is( X!( immutable(int)[4], shared(int)[4] ) == shared(const(int))[] )); // `[4]`
+static assert(is( X!( shared(int)[4], immutable(int)[4] ) == shared(const(int))[] )); // `[4]`
+
+static assert(is( X!( int*[4], const(int)*[4] ) == const(int)*[4]));
+static assert(is( X!( const(int)*[4], int*[4] ) == const(int)*[4]));
+static assert(is( X!( immutable(int)*[4], const(int)*[4] ) == const(int)*[4]));
+static assert(is( X!( const(int)*[4], immutable(int)*[4] ) == const(int)*[4]));
+static assert(Error!( int*[4], immutable(int)*[4] )); // should work
+static assert(Error!( immutable(int)*[4], int*[4] )); // should work
+static assert(Error!( int*[4], shared(int)*[4] ));
+static assert(Error!( shared(int)*[4], int*[4] ));
+static assert(Error!( shared(int)*[4], immutable(int)*[4] )); // should work
+static assert(Error!( immutable(int)*[4], shared(int)*[4] )); // should work
+static assert(is( X!( shared(const(int))*[4], shared(int)*[4] ) == shared(const(int))*[4] ));
+static assert(is( X!( shared(int)*[4], shared(const(int))*[4] ) == shared(const(int))*[4] ));
+
+static assert(is( X!( C[4], const(C)[4] ) == const(C)[4] ));
+static assert(is( X!( const(C)[4], C[4] ) == const(C)[4] ));
+static assert(is( X!( C[4], immutable(C)[4] ) == const(C)[] )); // `[4]`
+static assert(is( X!( immutable(C)[4], C[4] ) == const(C)[] )); // `[4]`
+static assert(Error!( shared(C)[4], C[4] ));
+static assert(Error!( C[4], shared(C)[4] ));
+static assert(is( X!( shared(C)[4], immutable(C)[4] ) == shared(const(C))[] )); // `[4]`
+static assert(is( X!( immutable(C)[4], shared(C)[4] ) == shared(const(C))[] )); // `[4]`
+static assert(is( X!( shared(const(C))[4], shared(C)[4] ) == shared(const(C))[4] ));
+static assert(is( X!( shared(C)[4], shared(const(C))[4] ) == shared(const(C))[4] ));
+
+// base class conversion
+static assert(is( X!(C[4], B[4]) ));
+static assert(Error!( C[4], I[4] ));
+static assert(Error!( C[4], D[4] ));
+static assert(is( X!( C[4], const(B)[4] ) == const(B)[4] ));
+static assert(Error!( C[4], const(I)[4] ));
+static assert(Error!( C[4], const(D)[4] ));
+static assert(Error!( C*[4], B*[4] ));
+static assert(Error!( C*[4], I*[4] ));
+static assert(Error!( C*[4], D*[4] ));
+static assert(is( X!( C*[4], const(B*)[4] ) == const(B*)[] )); // !?
+static assert(Error!( C*[4], const(I*)[4] ));
+static assert(Error!( C*[4], const(D*)[4] ));
+static assert(Error!( C*[4], B**[4] ));
+static assert(Error!( C*[4], const(B*)*[4] ));
+static assert(Error!( C*[4], const(B**)[4] ));
+
+// static to dynamic
+static assert(is( X!( int[4], void[4] ) == void[] ));
+static assert(is( X!( void[4], int[4] ) == void[] ));
+static assert(is( X!( int[4], int[3] ) == int[] ));
+static assert(is( X!( int[4], const(int)[3] ) == const(int)[] ));
+static assert(is( X!( int[4], int[] ) == int[] ));
+static assert(is( X!( const(int)[4], int[] ) == const(int)[] ));
+static assert(is( X!( int[4], const(int)[] ) == const(int)[] ));
+static assert(is( X!( int[4], void[] ) == void[] ));
+static assert(is( X!( const(int)[4], void[] ) == const(void)[] ));
+static assert(Error!( int*[4], void*[4] )); // should work
+static assert(Error!( int*[4], void*[] )); // should work
+static assert(is( X!( int*[4], int*[] ) == int*[] ));
+static assert(is( X!( const(int*)[4], int*[] ) == const(int*)[] ));
+static assert(is( X!( int*[4], const(int*)[] ) == const(int*)[] ));
+static assert(Error!( const(int)*[4], int*[] )); // should work
+static assert(Error!( int*[4], const(int)*[] )); // should work
+static assert(Error!( int[4], long[] ));
+static assert(Error!( int[4], uint[] ));
+static assert(Error!( int[4], short[] ));
+static assert(Error!( C[4], B[] ));
+static assert(Error!( C[4], I[] ));
+static assert(Error!( C[4], D[] ));
+static assert(Error!( C*[4], B*[] ));
+static assert(Error!( C*[4], I*[] ));
+static assert(Error!( C*[4], D*[] ));
+
+// dynamic arrays
+static assert(is( X!( int[], int[] ) == int[] ));
+static assert(is( X!( int[], const(int)[] ) == const(int)[] ));
+static assert(is( X!( const(int)[], int[] ) == const(int)[] ));
+static assert(is( X!( int[], immutable(int)[] ) == const(int)[] ));
+static assert(is( X!( immutable(int)[], int[] ) == const(int)[] ));
+static assert(Error!( int[], shared(int)[] ));
+static assert(Error!( shared(int)[], int[] ));
+static assert(is( X!( shared(int)[], immutable(int)[] ) == shared(const(int))[] ));
+static assert(is( X!( immutable(int)[], shared(int)[] ) == shared(const(int))[] ));
+static assert(Error!( const(int)[], shared(int)[] ));
+static assert(Error!( shared(int)[], const(int)[] ));
+
+static assert(is( X!( int[], void[] ) == void[] ));
+static assert(is( X!( void[], int[] ) == void[] ));
+static assert(is( X!( int[], const(void)[] ) == const(void)[] ));
+static assert(is( X!( const(int)[], void[] ) == const(void)[] ));
+
+static assert(is( X!( int*[], const(int*)[] ) == const(int*)[] ));
+static assert(is( X!( const(int*)[], int*[] ) == const(int*)[] ));
+static assert(Error!( int*[], const(int)*[] )); // should work
+static assert(Error!( const(int)*[], int*[] )); // should work
+
+static assert(is( X!( C[], const(C)[] ) == const(C)[] ));
+static assert(is( X!( const(C)[], C[] ) == const(C)[] ));
+
+static assert(Error!( int[], long[] ));
+static assert(Error!( int[], uint[] ));
+static assert(Error!( int[], short[] ));
+
+static assert(Error!( C[], B[] ));
+static assert(Error!( C[], I[] ));
+static assert(Error!( C[], D[] ));
+static assert(Error!( C*[], B*[] ));
+static assert(Error!( C*[], I*[] ));
+static assert(Error!( C*[], D*[] ));
+
+/******************************
+ * Associative arrays
+ */
+
+static assert(is( X!( int[int], int[int] ) == int[int] ));
+static assert(Error!( const(int[int]), int[int] )); // should work
+static assert(Error!( immutable(int[int]), int[int] )); // should work
+static assert(Error!( shared(int[int]), int[int] ));
+
+/******************************
+ * Classes
+ */
+
+static assert(Error!( C, void* ));
+static assert(Error!( void*, C ));
+
+static assert(is( X!( C, C ) == C ));
+static assert(is( X!( C, B ) == B ));
+static assert(is( X!( C, I ) == I ));
+static assert(is( X!( C, D ) == B ));
+static assert(is( X!( C, K ) == Object ));
+
+static assert(is( X!( C, SC ) == C ));
+static assert(is( X!( C, SB ) == B ));
+static assert(is( X!( C, SI ) == I ));
+static assert(is( X!( C, SD ) == B ));
+static assert(is( X!( C, SK ) == Object ));
+
+static assert(is( X!( C, immutable(C) ) == const(C) ));
+static assert(is( X!( C, immutable(I) ) == const(I) ));
+static assert(is( X!( C, immutable(B) ) == const(B) ));
+static assert(is( X!( C, immutable(D) ) == const(B) ));
+static assert(is( X!( C, immutable(K) ) == const(Object) ));
+
+static assert(Error!( C, immutable(SC) )); // should work
+static assert(Error!( C, immutable(SI) )); // should work
+static assert(Error!( immutable(SI), C )); // should work
+static assert(Error!( C, immutable(SB) )); // should work
+static assert(Error!( C, immutable(SD) )); // should work
+static assert(Error!( C, immutable(SK) )); // should work
+
+static assert(is( X!( const(C), C ) == const(C) ));
+static assert(is( X!( const(C), I ) == const(I) ));
+static assert(is( X!( const(C), B ) == const(B) ));
+static assert(is( X!( const(C), D ) == const(B) ));
+static assert(is( X!( const(C), K ) == const(Object) ));
+
+static assert(is( X!( const(C), SC ) == const(C)));
+static assert(Error!( const(C), SI )); // should work
+static assert(is( X!( const(SI), const(C) ) == const(I) )); // should work
+static assert(is( X!( const(C), SB ) == const(B)));
+static assert(is( X!( const(C), SD ) == const(B)));
+static assert(is( X!( const(C), SK ) == Object)); // `const`
+
+static assert(is( X!( SiC, SC ) == const(C) ));
+
+/******************************
+ * Structs
+ */
+
+static assert(is( X!( S, S ) == S ));
+static assert(is( X!( S, immutable(S) ) == const(S) ));
+static assert(Error!( S, shared(S) ));
+static assert(is( X!( Si, Si ) == Si ));
+static assert(is( X!( Si, int ) == int ));
+static assert(is( X!( int, Si ) == int ));
+static assert(is( X!( Si, Si2 ) == int ));
+
+static assert(is( X!( int, Sl ) == long ));
+static assert(is( X!( Si, Sl ) == long ));
+
+
+/******************************
+ * Vectors
+ */
+
+static if (__traits(compiles, int4))
+{
+ static assert(is( X!( int4, int4 ) == int4));
+ static assert(is( X!( int4, const(int4) ) == const(int4)));
+ static assert(is( X!( int4, immutable(int4) ) == const(int4)));
+ static assert(is( X!( __vector(const(int)[4]), int4 ) == int4));
+ static assert(is( X!( int4, __vector(const(int)[4]) ) == int4));
+
+ static assert(Error!( int[4], int4 ));
+ static assert(Error!( int4, int[4] ));
+}
+
+static if (__traits(compiles, { byte16 a; void16 b; })) static assert(Error!( byte16, void16 ));
+static if (__traits(compiles, { int4 a; void16 b; })) static assert(Error!( int4, void16 ));
+static if (__traits(compiles, { float4 a; void16 b; })) static assert(Error!( float4, void16 ));
+static if (__traits(compiles, { byte16 a; ubyte16 b; })) static assert(Error!( byte16, ubyte16 ));
+static if (__traits(compiles, { short8 a; ushort8 b; })) static assert(Error!( short8, ushort8 ));
+static if (__traits(compiles, { int4 a; uint4 b; })) static assert(Error!( int4, uint4 ));
+static if (__traits(compiles, { int4 a; float4 b; })) static assert(Error!( int4, float4 ));
+static if (__traits(compiles, { long4 a; ulong4 b; })) static assert(Error!( long4, ulong4 ));
+static if (__traits(compiles, { double4 a; float8 b; })) static assert(Error!( double4, float8 ));
+
+/******************************
+ * Null
+ */
+
+static assert(is( X!( typeof(null), int* ) == int*));
+static assert(is( X!( typeof(null), int[] ) == int[]));
+static assert(is( X!( typeof(null), int[int] ) == int[int]));
diff --git a/gcc/testsuite/gdc.test/compilable/compile1.d b/gcc/testsuite/gdc.test/compilable/compile1.d
index 86d84af617a..40fba4815ca 100644
--- a/gcc/testsuite/gdc.test/compilable/compile1.d
+++ b/gcc/testsuite/gdc.test/compilable/compile1.d
@@ -1,8 +1,15 @@
+// COMPILABLE_MATH_TEST
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/a12506.d
+/* TEST_OUTPUT:
+---
+compilable/compile1.d(229): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead
+---
+*/
-/**************************************************
- 1748 class template with stringof
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=1748
+// class template with stringof
struct S1748(T) {}
static assert(S1748!int.stringof == "S1748!int");
@@ -10,18 +17,17 @@ static assert(S1748!int.stringof == "S1748!int");
class C1748(T) {}
static assert(C1748!int.stringof == "C1748!int");
-/**************************************************
- 2354 pragma + single semicolon DeclarationBlock
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=2354
+// pragma + single semicolon DeclarationBlock
version(all)
pragma(inline, true);
else
pragma(inline, false);
-/**************************************************
- 2438
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=2438
alias void delegate() Dg2438;
@@ -35,9 +41,8 @@ alias typeof(Dg2438.init.funcptr) FP2438b;
static assert(is(CP2438b == void*));
static assert(is(FP2438b == void function()));
-/**************************************************
- 4225
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4225
struct Foo4225
{
@@ -49,9 +54,9 @@ struct Foo4225
}
}
-/**************************************************
- 5996 ICE(expression.c)
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5996
+// ICE(expression.c)
template T5996(T)
{
@@ -62,17 +67,19 @@ template T5996(T)
}
static assert(!is(typeof(T5996!(int).bug5996())));
-/**************************************************
- 8532 segfault(mtype.c) - type inference + pure
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8532
+// segfault(mtype.c) - type inference + pure
+
auto segfault8532(Y, R ...)(R r, Y val) pure
{ return segfault8532(r, val); }
static assert(!is(typeof( segfault8532(1,2,3))));
-/**************************************************
- 8982 ICE(ctfeexpr.c) __parameters with error in default value
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8982
+// ICE(ctfeexpr.c) __parameters with error in default value
+
template ice8982(T)
{
void bug8982(ref const int v = 7){}
@@ -85,16 +92,17 @@ template ice8982(T)
static assert(!is(ice8982!(int)));
-/**************************************************
- 8801 ICE assigning to __ctfe
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8801
+// ICE assigning to __ctfe
+
static assert(!is(typeof( { bool __ctfe= true; })));
static assert(!is(typeof( { __ctfe |= true; })));
-/**************************************************
- 5932 ICE(s2ir.c)
- 6675 ICE(glue.c)
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5932
+// https://issues.dlang.org/show_bug.cgi?id=6675
+// ICE(s2ir.c), ICE(glue.c)
void bug3932(T)() {
static assert( 0 );
@@ -113,9 +121,9 @@ static assert(!is(typeof(
}()
)));
-/**************************************************
- 6650 ICE(glue.c) or wrong-code
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6650
+// ICE(glue.c) or wrong-code
auto bug6650(X)(X y)
{
@@ -127,9 +135,9 @@ auto bug6650(X)(X y)
static assert(!is(typeof(bug6650!(int)(6))));
static assert(!is(typeof(bug6650!(int)(18))));
-/**************************************************
- 14710 VC-built DMD crashes on templated variadic function IFTI
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14710
+// VC-built DMD crashes on templated variadic function IFTI
void bug14710a(T)(T val, T[] arr...)
{
@@ -140,9 +148,9 @@ void bug14710b()
bug14710a("", "");
}
-/**************************************************
- 6661 Templates instantiated only through is(typeof()) shouldn't cause errors
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6661
+// Templates instantiated only through is(typeof()) shouldn't cause errors
template bug6661(Q)
{
@@ -168,9 +176,9 @@ template bug6661x(Q)
// should pass, but doesn't in current
//static assert(!is(typeof(bug6661x!(int))));
-/**************************************************
- 6599 ICE(constfold.c) or segfault
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6599
+// ICE(constfold.c) or segfault
string bug6599extraTest(string x) { return x ~ "abc"; }
@@ -191,9 +199,9 @@ template Bug6599(X)
static assert(!is(typeof(Bug6599!int)));
-/**************************************************
- 8422 TypeTuple of tuples can't be read at compile time
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8422
+// TypeTuple of tuples can't be read at compile time
template TypeTuple8422(TList...)
{
@@ -215,9 +223,9 @@ void test8422()
}
}
-/**************************************************
- 6096 ICE(el.c) with -O
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6096
+// ICE(el.c) with -O
cdouble c6096;
@@ -227,35 +235,35 @@ int bug6096()
return 1;
}
-/**************************************************
- 7681 Segfault
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7681
+// Segfault
static assert( !is(typeof( (){
undefined ~= delegate(){}; return 7;
}())));
-/**************************************************
- 8639 Buffer overflow
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8639
+// Buffer overflow
void t8639(alias a)() {}
void bug8639() {
t8639!({auto r = -real.max;})();
}
-/**************************************************
- 7751 Segfault
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7751
+// Segfault
static assert( !is(typeof( (){
bar[]r; r ~= [];
return 7;
}())));
-/**************************************************
- 7639 Segfault
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7639
+// Segfault
static assert( !is(typeof( (){
enum foo =
@@ -264,9 +272,8 @@ static assert( !is(typeof( (){
];
})));
-/**************************************************
- 11991
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11991
void main()
{
@@ -280,9 +287,8 @@ void main()
}
}
-/**************************************************
- 11939
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11939
void test11939()
{
@@ -293,9 +299,8 @@ void test11939()
throw new Exception("");
}
-/**************************************************
- 5796
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5796
template A(B) {
pragma(lib, "missing ;")
@@ -304,18 +309,17 @@ template A(B) {
static assert(!is(typeof(A!(int))));
-/**************************************************
- 6720
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6720
+
void bug6720() { }
static assert(!is(typeof(
cast(bool)bug6720()
)));
-/**************************************************
- 1099
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=1099
template Mix1099(int a) {
alias typeof(this) ThisType;
@@ -331,9 +335,9 @@ struct Foo1099 {
mixin Mix1099!(2);
}
-/**************************************************
- 8788 - super() and return
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8788
+// super() and return
class B8788 {
this ( ) { }
@@ -435,9 +439,9 @@ static assert(!is(typeof( { new C8788!(7)(0); } )));
static assert(!is(typeof( { new C8788!(8)(0); } )));
static assert(!is(typeof( { new C8788!(9)(0); } )));
-/**************************************************
- 4967, 7058
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4967
+// https://issues.dlang.org/show_bug.cgi?id=7058
enum Bug7058 bug7058 = { 1.5f, 2};
static assert(bug7058.z == 99);
@@ -498,7 +502,7 @@ alias test8163!(ubyte, ubyte, ushort, float) _BBSf;
/***************************************************/
-// 4757
+// https://issues.dlang.org/show_bug.cgi?id=4757
auto foo4757(T)(T)
{
@@ -518,7 +522,7 @@ void test4757()
}
/***************************************************/
-// 9348
+// https://issues.dlang.org/show_bug.cgi?id=9348
void test9348()
{
@@ -529,7 +533,7 @@ void test9348()
}
/***************************************************/
-// 9690
+// https://issues.dlang.org/show_bug.cgi?id=9690
@disable
{
@@ -545,7 +549,7 @@ void test9348()
}
/***************************************************/
-// 9987
+// https://issues.dlang.org/show_bug.cgi?id=9987
static if (is(object.ModuleInfo == struct))
{
@@ -564,7 +568,7 @@ static if (is(object.ModuleInfo == class))
}
/***************************************************/
-// 10158
+// https://issues.dlang.org/show_bug.cgi?id=10158
class Outer10158
{
@@ -586,7 +590,7 @@ void test10158()
}
/***************************************************/
-// 10326
+// https://issues.dlang.org/show_bug.cgi?id=10326
class C10326
{
@@ -596,7 +600,7 @@ class C10326
}
/***************************************************/
-// 11042
+// https://issues.dlang.org/show_bug.cgi?id=11042
static if ((true || error) == true ) {} else { static assert(0); }
static if ((false && error) == false) {} else { static assert(0); }
@@ -620,7 +624,7 @@ int f11042a3()() if (__traits(compiles, true || error) == false) { return 0; }
int f11042b3()() if (__traits(compiles, false && error) == false) { return 0; } enum x11042b3 = f11042b3();
/***************************************************/
-// 11554
+// https://issues.dlang.org/show_bug.cgi?id=11554
enum E11554;
static assert(is(E11554 == enum));
@@ -629,7 +633,7 @@ struct Bro11554(N...) {}
static assert(!is(E11554 unused : Bro11554!M, M...));
/***************************************************/
-// 12302
+// https://issues.dlang.org/show_bug.cgi?id=12302
template isCallable12302(T...)
if (T.length == 1)
@@ -657,7 +661,7 @@ A12302 func12302() { return null; }
enum b12302 = isCallable12302!func12302;
/***************************************************/
-// 12476
+// https://issues.dlang.org/show_bug.cgi?id=12476
template A12476(T) { }
@@ -686,14 +690,14 @@ static assert(__traits(isSame, sb12476, A12476!int));
static assert(__traits(isSame, cb12476, A12476!int));
/***************************************************/
-// 12506
+// https://issues.dlang.org/show_bug.cgi?id=12506
import imports.a12506;
private bool[9] r12506a = f12506!(i => true)(); // OK
private immutable bool[9] r12506b = f12506!(i => true)(); // OK <- error
/***************************************************/
-// 12555
+// https://issues.dlang.org/show_bug.cgi?id=12555
class A12555(T)
{
@@ -705,7 +709,7 @@ static assert(!__traits(compiles, {
}));
/***************************************************/
-// 11622
+// https://issues.dlang.org/show_bug.cgi?id=11622
class A11622(T)
{
@@ -724,7 +728,7 @@ static assert(!__traits(compiles, {
}));
/***************************************************/
-// 12688
+// https://issues.dlang.org/show_bug.cgi?id=12688
void writeln12688(A...)(A) {}
@@ -741,7 +745,7 @@ void test12688()
}
/***************************************************/
-// 12703
+// https://issues.dlang.org/show_bug.cgi?id=12703
struct S12703
{
@@ -754,7 +758,7 @@ final class C12703
}
/***************************************************/
-// 12799
+// https://issues.dlang.org/show_bug.cgi?id=12799
struct A12799
{
@@ -764,12 +768,12 @@ struct A12799
}
/***************************************************/
-// 13236
+// https://issues.dlang.org/show_bug.cgi?id=13236
enum bug13286 = is(typeof({ struct S { S x; } }));
/***************************************************/
-// 13280
+// https://issues.dlang.org/show_bug.cgi?id=13280
struct S13280
{
@@ -780,7 +784,7 @@ struct S13280
}
/***************************************************/
-// 13481
+// https://issues.dlang.org/show_bug.cgi?id=13481
mixin template Mix13481(void function() callback)
{
@@ -791,7 +795,7 @@ mixin template Mix13481(void function() callback)
}
/***************************************************/
-// 13564
+// https://issues.dlang.org/show_bug.cgi?id=13564
class E13564(T)
{
@@ -818,7 +822,7 @@ void test13564()
}
/***************************************************/
-// 14166
+// https://issues.dlang.org/show_bug.cgi?id=14166
struct Proxy14166(T)
{
@@ -868,7 +872,7 @@ static assert(is(typeof(makeAA14166()[0] = 1) == X14166)); // ok <- error
static assert(is(typeof(tup14166.field = makeTup14166()) == TT14166!(int, int))); // ok <- error
/***************************************************/
-// 14388
+// https://issues.dlang.org/show_bug.cgi?id=14388
@property immutable(T)[] idup14388(T)(T[] a)
{
@@ -913,7 +917,7 @@ void test14388()
}
/***************************************************/
-// 15163
+// https://issues.dlang.org/show_bug.cgi?id=15163
void function() func15164(int[] arr)
{
@@ -926,9 +930,9 @@ void test15163()
func15164(arr[0])();
}
-/**************************************************
- 3438
-**************************************************/
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=3438
+
import core.vararg;
struct S3438_1 { this(int x, int y = 1) { } }
struct S3438_2 { this(int x, ...) { } }
@@ -937,7 +941,7 @@ struct S3438_4 { this(...) { } }
struct S3438_5 { this(int[] arr...) { } }
/***************************************************/
-// 15362
+// https://issues.dlang.org/show_bug.cgi?id=15362
void func15362()
{
@@ -952,7 +956,7 @@ void func15362()
}
/***************************************************/
-// 15799
+// https://issues.dlang.org/show_bug.cgi?id=15799
interface I15799
{
@@ -963,3 +967,28 @@ interface I15799
assert(n);
}; // Semicolon is not a part of function declaration. It's an empty declaration.
}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=21163
+
+struct B21163
+{
+ void function(scope int) fp;
+}
+
+B21163 b21163 = {
+ (scope int x){}
+};
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11624
+
+interface I11624
+{
+ void foo();
+}
+
+static assert(!__traits(compiles,
+{
+ static class C11624 : I11624 { }
+}));
diff --git a/gcc/testsuite/gdc.test/compilable/cpp_abi_tag_unused.d b/gcc/testsuite/gdc.test/compilable/cpp_abi_tag_unused.d
new file mode 100644
index 00000000000..bac39cb3823
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/cpp_abi_tag_unused.d
@@ -0,0 +1,21 @@
+/* DISABLED: win32 win64
+REQUIRED_ARGS: -extern-std=c++11
+*/
+
+#line 100
+
+// Make sure that bad things don't happen if the user isn't using
+// `core.attribute`'s definition
+struct gnuAbiTag
+{
+ string[] args;
+}
+
+extern(C++):
+
+@gnuAbiTag(["42"])
+struct A {}
+
+__gshared A a;
+
+static assert(a.mangleof == "a");
diff --git a/gcc/testsuite/gdc.test/compilable/cppmangle.d b/gcc/testsuite/gdc.test/compilable/cppmangle.d
index 954a9bee72e..1820de90991 100644
--- a/gcc/testsuite/gdc.test/compilable/cppmangle.d
+++ b/gcc/testsuite/gdc.test/compilable/cppmangle.d
@@ -1,7 +1,9 @@
-
+// EXTRA_FILES: cppmangle2.d
// Test C++ name mangling.
-// See Bugs 4059, 5148, 7024, 10058
-
+// https://issues.dlang.org/show_bug.cgi?id=4059
+// https://issues.dlang.org/show_bug.cgi?id=5148
+// https://issues.dlang.org/show_bug.cgi?id=7024
+// https://issues.dlang.org/show_bug.cgi?id=10058
import core.stdc.stdio;
@@ -43,7 +45,7 @@ void test1()
c.bar(4, 5, 6);
}
-version (linux)
+version (Posix)
{
static assert(foo.mangleof == "_Z3fooiii");
static assert(foob.mangleof == "_Z4foobiii");
@@ -79,7 +81,7 @@ void test2()
assert(i == 8);
}
-version (linux)
+version (Posix)
{
static assert (getD.mangleof == "_Z4getDv");
static assert (D.bar.mangleof == "_ZN1D3barEiii");
@@ -116,7 +118,7 @@ void test3()
assert(i == 8);
}
-version (linux)
+version (Posix)
{
static assert (callE.mangleof == "_Z5callEP1E");
static assert (E.bar.mangleof == "_ZN1E3barEiii");
@@ -132,7 +134,7 @@ void test4()
foo4(null);
}
-version (linux)
+version (Posix)
{
static assert(foo4.mangleof == "_Z4foo4Pc");
}
@@ -158,7 +160,7 @@ void test5()
assert(f.p == cast(void*)b);
}
-version (linux)
+version (Posix)
{
static assert(bar5.getFoo.mangleof == "_ZN4bar56getFooEi");
static assert (newBar.mangleof == "_Z6newBarv");
@@ -181,14 +183,14 @@ extern (C) int foosize6();
void test6()
{
S6 f = foo6();
- printf("%d %d\n", foosize6(), S6.sizeof);
+ printf("%d %d\n", foosize6(), cast(int)S6.sizeof);
assert(foosize6() == S6.sizeof);
assert(f.i == 42);
printf("f.d = %g\n", f.d);
assert(f.d == 2.5);
}
-version (linux)
+version (Posix)
{
static assert (foo6.mangleof == "_Z4foo6v");
}
@@ -205,7 +207,7 @@ struct S
void test7()
{
- printf("%d %d\n", foo7(), S.sizeof);
+ printf("%d %d\n", foo7(), cast(int)S.sizeof);
assert(foo7() == S.sizeof);
}
@@ -219,13 +221,13 @@ void test8()
foo8(&c);
}
-version (linux)
+version (Posix)
{
static assert(foo8.mangleof == "_Z4foo8PKc");
}
/****************************************/
-// 4059
+// https://issues.dlang.org/show_bug.cgi?id=4059
struct elem9 { }
@@ -237,13 +239,13 @@ void test9()
foobar9(a, a);
}
-version (linux)
+version (Posix)
{
static assert(foobar9.mangleof == "_Z7foobar9P5elem9S0_");
}
/****************************************/
-// 5148
+// https://issues.dlang.org/show_bug.cgi?id=5148
extern (C++)
{
@@ -270,8 +272,15 @@ void test10()
foo10(e,e);
}
+// https://issues.dlang.org/show_bug.cgi?id=19504
+extern(C++) struct Class19504 {
+ pragma(mangle, "HOHOHO")
+ ~this();
+}
+static assert(Class19504.__xdtor.mangleof == "HOHOHO");
+
/**************************************/
-// 10058
+// https://issues.dlang.org/show_bug.cgi?id=10058
extern (C++)
{
@@ -289,7 +298,7 @@ extern (C++)
void test10058l(void* function(void*), void* function(const (void)*), const(void)* function(void*)) { }
}
-version (linux)
+version (Posix)
{
static assert(test10058a.mangleof == "_Z10test10058aPv");
static assert(test10058b.mangleof == "_Z10test10058bPFvPvE");
@@ -306,7 +315,7 @@ version (linux)
}
/**************************************/
-// 11696
+// https://issues.dlang.org/show_bug.cgi?id=11696
class Expression;
struct Loc {}
@@ -320,7 +329,7 @@ class CallExp
static void test11696d(Loc, Expression*, Expression*);
}
-version (linux)
+version (Posix)
{
static assert(CallExp.test11696a.mangleof == "_ZN7CallExp10test11696aE3LocP10ExpressionS2_");
static assert(CallExp.test11696b.mangleof == "_ZN7CallExp10test11696bE3LocP10ExpressionPS2_");
@@ -329,7 +338,7 @@ version (linux)
}
/**************************************/
-// 13337
+// https://issues.dlang.org/show_bug.cgi?id=13337
extern(C++, N13337a.N13337b.N13337c)
{
@@ -337,13 +346,22 @@ extern(C++, N13337a.N13337b.N13337c)
void foo13337(S13337 s);
}
-version (linux)
+extern(C++, `N13337a`, `N13337b`, `N13337c`)
+{
+ struct S13337_2{}
+ void foo13337_2(S13337 s);
+ void foo13337_3(S13337_2 s);
+}
+
+version (Posix)
{
static assert(foo13337.mangleof == "_ZN7N13337a7N13337b7N13337c8foo13337ENS1_6S13337E");
+ static assert(foo13337_2.mangleof == "_ZN7N13337a7N13337b7N13337c10foo13337_2ENS1_6S13337E");
+ static assert(foo13337_3.mangleof == "_ZN7N13337a7N13337b7N13337c10foo13337_3ENS1_8S13337_2E");
}
/**************************************/
-// 15789
+// https://issues.dlang.org/show_bug.cgi?id=15789
extern (C++) void test15789a(T...)(T args);
@@ -353,11 +371,11 @@ void test15789()
}
/**************************************/
-// 7030
+// https://issues.dlang.org/show_bug.cgi?id=7030
extern(C++)
{
- struct T
+ struct Struct7030
{
void foo(int) const;
void bar(int);
@@ -367,9 +385,895 @@ extern(C++)
version (Posix)
{
- static assert(T.foo.mangleof == "_ZNK1T3fooEi");
- static assert(T.bar.mangleof == "_ZN1T3barEi");
- static assert(T.boo.mangleof == "_ZN1T3booE");
+ static assert(Struct7030.foo.mangleof == "_ZNK10Struct70303fooEi");
+ static assert(Struct7030.bar.mangleof == "_ZN10Struct70303barEi");
+ static assert(Struct7030.boo.mangleof == "_ZN10Struct70303booE");
+}
+
+/****************************************/
+
+// Special cases of Itanium mangling
+
+extern (C++, std)
+{
+ struct pair(T1, T2)
+ {
+ void swap(ref pair other);
+ }
+
+ struct allocator(T)
+ {
+ uint fooa() const;
+ uint foob();
+ }
+
+ struct basic_string(T1, T2, T3)
+ {
+ uint fooa();
+ }
+
+ struct basic_istream(T1, T2)
+ {
+ uint fooc();
+ }
+
+ struct basic_ostream(T1, T2)
+ {
+ uint food();
+ }
+
+ struct basic_iostream(T1, T2)
+ {
+ uint fooe();
+ }
+
+ struct char_traits(T)
+ {
+ uint foof();
+ }
+
+ struct vector (T);
+
+ struct test18957 {}
+}
+
+extern (C++, `std`)
+{
+ struct pair(T1, T2)
+ {
+ void swap(ref pair other);
+ }
+
+ struct allocator(T)
+ {
+ uint fooa() const;
+ uint foob();
+ }
+
+ struct basic_string(T1, T2, T3)
+ {
+ uint fooa();
+ }
+
+ struct basic_istream(T1, T2)
+ {
+ uint fooc();
+ }
+
+ struct basic_ostream(T1, T2)
+ {
+ uint food();
+ }
+
+ struct basic_iostream(T1, T2)
+ {
+ uint fooe();
+ }
+
+ struct char_traits(T)
+ {
+ uint foof();
+ }
+
+ struct vector (T);
+
+ struct Struct18957 {}
+}
+
+extern(C++)
+{
+ // Nspace
+ std.allocator!int func_18957_1(std.allocator!(int)* v);
+ // CPPNamespaceAttribute
+ allocator!int func_18957_2(allocator!(int)* v);
+ X func_18957_2(X)(X* v);
+}
+
+extern (C++)
+{
+ void func_20413(pair!(int, float), pair!(float, int));
+}
+
+version (Posix)
+{
+ // https://issues.dlang.org/show_bug.cgi?id=17947
+ static assert(std.pair!(void*, void*).swap.mangleof == "_ZNSt4pairIPvS0_E4swapERS1_");
+ static assert(std.allocator!int.fooa.mangleof == "_ZNKSaIiE4fooaEv");
+ static assert(std.allocator!int.foob.mangleof == "_ZNSaIiE4foobEv");
+ static assert(std.basic_string!(char,int,uint).fooa.mangleof == "_ZNSbIcijE4fooaEv");
+ static assert(std.basic_string!(char, std.char_traits!char, std.allocator!char).fooa.mangleof == "_ZNSs4fooaEv");
+ static assert(std.basic_istream!(char, std.char_traits!char).fooc.mangleof == "_ZNSi4foocEv");
+ static assert(std.basic_ostream!(char, std.char_traits!char).food.mangleof == "_ZNSo4foodEv");
+ static assert(std.basic_iostream!(char, std.char_traits!char).fooe.mangleof == "_ZNSd4fooeEv");
+
+ static assert(func_18957_1.mangleof == `_Z12func_18957_1PSaIiE`);
+ static assert(func_18957_2!(std.allocator!int).mangleof == `_Z12func_18957_2ISaIiEET_PS1_`);
+
+
+ static assert(pair!(void*, void*).swap.mangleof == "_ZNSt4pairIPvS0_E4swapERS1_");
+ static assert(allocator!int.fooa.mangleof == "_ZNKSaIiE4fooaEv");
+ static assert(allocator!int.foob.mangleof == "_ZNSaIiE4foobEv");
+ static assert(basic_string!(char,int,uint).fooa.mangleof == "_ZNSbIcijE4fooaEv");
+ static assert(basic_string!(char, char_traits!char, allocator!char).fooa.mangleof == "_ZNSs4fooaEv");
+ static assert(basic_istream!(char, char_traits!char).fooc.mangleof == "_ZNSi4foocEv");
+ static assert(basic_ostream!(char, char_traits!char).food.mangleof == "_ZNSo4foodEv");
+ static assert(basic_iostream!(char, char_traits!char).fooe.mangleof == "_ZNSd4fooeEv");
+
+ static assert(func_18957_2.mangleof == `_Z12func_18957_2PSaIiE`);
+ static assert(func_18957_2!(allocator!int).mangleof == `_Z12func_18957_2ISaIiEET_PS1_`);
+
+ static assert(func_20413.mangleof == `_Z10func_20413St4pairIifES_IfiE`);
+}
+
+/**************************************/
+
+alias T36 = int ********** ********** ********** **********;
+
+extern (C++) void test36(T36, T36*) { }
+
+version (Posix)
+{
+ static assert(test36.mangleof == "_Z6test36PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPiPS12_");
+}
+
+/*****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=17772
+
+extern(C++, SPACE)
+int test37(T)(){ return 0;}
+
+extern(C++, `SPACE`)
+int test37(T)(){ return 0;}
+
+version (Posix) // all non-Windows machines
+{
+ static assert(SPACE.test37!int.mangleof == "_ZN5SPACE6test37IiEEiv");
+ static assert(test37!int.mangleof == "_ZN5SPACE6test37IiEEiv");
+}
+
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15388
+
+extern (C++) void test15388(typeof(null));
+
+version (Posix)
+{
+ static assert(test15388.mangleof == "_Z9test15388Dn");
+}
+version (Windows)
+{
+ static assert(test15388.mangleof == "?test15388@@YAX$$T@Z");
+}
+
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14086
+
+extern (C++) class Test14086
+{
+ this();
+ ~this();
+}
+extern (C++) class Test14086_2
+{
+ final ~this();
+}
+extern (C++) struct Test14086_S
+{
+ this(int);
+ ~this();
+}
+
+version(Posix)
+{
+ static assert(Test14086.__ctor.mangleof == "_ZN9Test14086C1Ev");
+ static assert(Test14086.__dtor.mangleof == "_ZN9Test14086D1Ev");
+ static assert(Test14086_2.__dtor.mangleof == "_ZN11Test14086_2D1Ev");
+ static assert(Test14086_S.__ctor.mangleof == "_ZN11Test14086_SC1Ei");
+ static assert(Test14086_S.__dtor.mangleof == "_ZN11Test14086_SD1Ev");
+}
+version(Win32)
+{
+ static assert(Test14086.__ctor.mangleof == "??0Test14086@@QAE@XZ");
+ static assert(Test14086.__dtor.mangleof == "??1Test14086@@UAE@XZ");
+ static assert(Test14086_2.__dtor.mangleof == "??1Test14086_2@@QAE@XZ");
+ static assert(Test14086_S.__ctor.mangleof == "??0Test14086_S@@QAE@H@Z");
+ static assert(Test14086_S.__dtor.mangleof == "??1Test14086_S@@QAE@XZ");
+}
+version(Win64)
+{
+ static assert(Test14086.__ctor.mangleof == "??0Test14086@@QEAA@XZ");
+ static assert(Test14086.__dtor.mangleof == "??1Test14086@@UEAA@XZ");
+ static assert(Test14086_2.__dtor.mangleof == "??1Test14086_2@@QEAA@XZ");
+ static assert(Test14086_S.__ctor.mangleof == "??0Test14086_S@@QEAA@H@Z");
+ static assert(Test14086_S.__dtor.mangleof == "??1Test14086_S@@QEAA@XZ");
+}
+
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18888
+
+extern (C++)
+struct T18888(T)
+{
+ void fun();
+}
+
+extern (C++)
+struct S18888(alias arg = T18888)
+{
+ alias I = T18888!(arg!int);
+}
+
+version(Posix)
+{
+ static assert(S18888!().I.fun.mangleof == "_ZN6T18888IS_IiEE3funEv");
+}
+version(Win32)
+{
+ static assert(S18888!().I.fun.mangleof == "?fun@?$T18888@U?$T18888@H@@@@QAEXXZ");
+}
+version(Win64)
+{
+ static assert(S18888!().I.fun.mangleof == "?fun@?$T18888@U?$T18888@H@@@@QEAAXXZ");
+}
+
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18890
+
+extern (C++) class C18890
+{
+ ~this() {}
+}
+extern (C++) class C18890_2
+{
+ ~this() {}
+ extern (C++) struct Agg
+ {
+ ~this() {}
+ }
+ Agg s;
+}
+
+version (Posix)
+{
+ static assert(C18890.__dtor.mangleof == "_ZN6C18890D1Ev");
+ static assert(C18890.__xdtor.mangleof == "_ZN6C18890D1Ev");
+ static assert(C18890_2.__dtor.mangleof == "_ZN8C18890_26__dtorEv");
+ static assert(C18890_2.__xdtor.mangleof == "_ZN8C18890_2D1Ev");
+}
+version (Win32)
+{
+ static assert(C18890.__dtor.mangleof == "??1C18890@@UAE@XZ");
+ static assert(C18890.__xdtor.mangleof == "??_GC18890@@UAEPAXI@Z");
+ static assert(C18890_2.__dtor.mangleof == "?__dtor@C18890_2@@UAEXXZ");
+ static assert(C18890_2.__xdtor.mangleof == "??_GC18890_2@@UAEPAXI@Z");
+}
+version (Win64)
+{
+ static assert(C18890.__dtor.mangleof == "??1C18890@@UEAA@XZ");
+ static assert(C18890.__xdtor.mangleof == "??_GC18890@@UEAAPEAXI@Z");
+ static assert(C18890_2.__dtor.mangleof == "?__dtor@C18890_2@@UEAAXXZ");
+ static assert(C18890_2.__xdtor.mangleof == "??_GC18890_2@@UEAAPEAXI@Z");
+}
+
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18891
+
+extern (C++) class C18891
+{
+ ~this();
+ extern (C++) struct Agg
+ {
+ ~this() {}
+ }
+ Agg s;
+}
+
+version (Posix)
+{
+ static assert(C18891.__dtor.mangleof == "_ZN6C18891D1Ev");
+ static assert(C18891.__xdtor.mangleof == "_ZN6C18891D1Ev");
+}
+version (Win32)
+{
+ static assert(C18891.__dtor.mangleof == "??1C18891@@UAE@XZ");
+ static assert(C18891.__xdtor.mangleof == "??_GC18891@@UAEPAXI@Z");
+}
+version (Win64)
+{
+ static assert(C18891.__dtor.mangleof == "??1C18891@@UEAA@XZ");
+ static assert(C18891.__xdtor.mangleof == "??_GC18891@@UEAAPEAXI@Z");
+}
+
+/**************************************/
+// Test C++ operator mangling
+
+extern (C++) struct TestOperators
+{
+ int opCast(T)();
+ int opBinary(string op)(int x);
+ int opUnary(string op)();
+ int opOpAssign(string op)(int x);
+ int opIndex(int x);
+ bool opEquals(int x);
+ int opCall(int, float);
+ int opAssign(int);
+}
+
+version (Posix)
+{
+ static assert(TestOperators.opUnary!"*".mangleof == "_ZN13TestOperatorsdeEv");
+ static assert(TestOperators.opUnary!"++".mangleof == "_ZN13TestOperatorsppEv");
+ static assert(TestOperators.opUnary!"--".mangleof == "_ZN13TestOperatorsmmEv");
+ static assert(TestOperators.opUnary!"-".mangleof == "_ZN13TestOperatorsngEv");
+ static assert(TestOperators.opUnary!"+".mangleof == "_ZN13TestOperatorspsEv");
+ static assert(TestOperators.opUnary!"~".mangleof == "_ZN13TestOperatorscoEv");
+ static assert(TestOperators.opBinary!">>".mangleof == "_ZN13TestOperatorsrsEi");
+ static assert(TestOperators.opBinary!"<<".mangleof == "_ZN13TestOperatorslsEi");
+ static assert(TestOperators.opBinary!"*".mangleof == "_ZN13TestOperatorsmlEi");
+ static assert(TestOperators.opBinary!"-".mangleof == "_ZN13TestOperatorsmiEi");
+ static assert(TestOperators.opBinary!"+".mangleof == "_ZN13TestOperatorsplEi");
+ static assert(TestOperators.opBinary!"&".mangleof == "_ZN13TestOperatorsanEi");
+ static assert(TestOperators.opBinary!"/".mangleof == "_ZN13TestOperatorsdvEi");
+ static assert(TestOperators.opBinary!"%".mangleof == "_ZN13TestOperatorsrmEi");
+ static assert(TestOperators.opBinary!"^".mangleof == "_ZN13TestOperatorseoEi");
+ static assert(TestOperators.opBinary!"|".mangleof == "_ZN13TestOperatorsorEi");
+ static assert(TestOperators.opOpAssign!"*".mangleof == "_ZN13TestOperatorsmLEi");
+ static assert(TestOperators.opOpAssign!"+".mangleof == "_ZN13TestOperatorspLEi");
+ static assert(TestOperators.opOpAssign!"-".mangleof == "_ZN13TestOperatorsmIEi");
+ static assert(TestOperators.opOpAssign!"/".mangleof == "_ZN13TestOperatorsdVEi");
+ static assert(TestOperators.opOpAssign!"%".mangleof == "_ZN13TestOperatorsrMEi");
+ static assert(TestOperators.opOpAssign!">>".mangleof == "_ZN13TestOperatorsrSEi");
+ static assert(TestOperators.opOpAssign!"<<".mangleof == "_ZN13TestOperatorslSEi");
+ static assert(TestOperators.opOpAssign!"&".mangleof == "_ZN13TestOperatorsaNEi");
+ static assert(TestOperators.opOpAssign!"|".mangleof == "_ZN13TestOperatorsoREi");
+ static assert(TestOperators.opOpAssign!"^".mangleof == "_ZN13TestOperatorseOEi");
+ static assert(TestOperators.opCast!int.mangleof == "_ZN13TestOperatorscviEv");
+ static assert(TestOperators.opAssign.mangleof == "_ZN13TestOperatorsaSEi");
+ static assert(TestOperators.opEquals.mangleof == "_ZN13TestOperatorseqEi");
+ static assert(TestOperators.opIndex.mangleof == "_ZN13TestOperatorsixEi");
+ static assert(TestOperators.opCall.mangleof == "_ZN13TestOperatorsclEif");
+}
+version (Win32)
+{
+ static assert(TestOperators.opUnary!"*".mangleof == "??DTestOperators@@QAEHXZ");
+ static assert(TestOperators.opUnary!"++".mangleof == "??ETestOperators@@QAEHXZ");
+ static assert(TestOperators.opUnary!"--".mangleof == "??FTestOperators@@QAEHXZ");
+ static assert(TestOperators.opUnary!"-".mangleof == "??GTestOperators@@QAEHXZ");
+ static assert(TestOperators.opUnary!"+".mangleof == "??HTestOperators@@QAEHXZ");
+ static assert(TestOperators.opUnary!"~".mangleof == "??STestOperators@@QAEHXZ");
+ static assert(TestOperators.opBinary!">>".mangleof == "??5TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opBinary!"<<".mangleof == "??6TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opBinary!"*".mangleof == "??DTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opBinary!"-".mangleof == "??GTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opBinary!"+".mangleof == "??HTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opBinary!"&".mangleof == "??ITestOperators@@QAEHH@Z");
+ static assert(TestOperators.opBinary!"/".mangleof == "??KTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opBinary!"%".mangleof == "??LTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opBinary!"^".mangleof == "??TTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opBinary!"|".mangleof == "??UTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!"*".mangleof == "??XTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!"+".mangleof == "??YTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!"-".mangleof == "??ZTestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!"/".mangleof == "??_0TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!"%".mangleof == "??_1TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!">>".mangleof == "??_2TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!"<<".mangleof == "??_3TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!"&".mangleof == "??_4TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!"|".mangleof == "??_5TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opOpAssign!"^".mangleof == "??_6TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opCast!int.mangleof == "??BTestOperators@@QAEHXZ");
+ static assert(TestOperators.opAssign.mangleof == "??4TestOperators@@QAEHH@Z");
+ static assert(TestOperators.opEquals.mangleof == "??8TestOperators@@QAE_NH@Z");
+ static assert(TestOperators.opIndex.mangleof == "??ATestOperators@@QAEHH@Z");
+ static assert(TestOperators.opCall.mangleof == "??RTestOperators@@QAEHHM@Z");
+}
+version (Win64)
+{
+ static assert(TestOperators.opUnary!"*".mangleof == "??DTestOperators@@QEAAHXZ");
+ static assert(TestOperators.opUnary!"++".mangleof == "??ETestOperators@@QEAAHXZ");
+ static assert(TestOperators.opUnary!"--".mangleof == "??FTestOperators@@QEAAHXZ");
+ static assert(TestOperators.opUnary!"-".mangleof == "??GTestOperators@@QEAAHXZ");
+ static assert(TestOperators.opUnary!"+".mangleof == "??HTestOperators@@QEAAHXZ");
+ static assert(TestOperators.opUnary!"~".mangleof == "??STestOperators@@QEAAHXZ");
+ static assert(TestOperators.opBinary!">>".mangleof == "??5TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opBinary!"<<".mangleof == "??6TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opBinary!"*".mangleof == "??DTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opBinary!"-".mangleof == "??GTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opBinary!"+".mangleof == "??HTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opBinary!"&".mangleof == "??ITestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opBinary!"/".mangleof == "??KTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opBinary!"%".mangleof == "??LTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opBinary!"^".mangleof == "??TTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opBinary!"|".mangleof == "??UTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!"*".mangleof == "??XTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!"+".mangleof == "??YTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!"-".mangleof == "??ZTestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!"/".mangleof == "??_0TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!"%".mangleof == "??_1TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!">>".mangleof == "??_2TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!"<<".mangleof == "??_3TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!"&".mangleof == "??_4TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!"|".mangleof == "??_5TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opOpAssign!"^".mangleof == "??_6TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opCast!int.mangleof == "??BTestOperators@@QEAAHXZ");
+ static assert(TestOperators.opAssign.mangleof == "??4TestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opEquals.mangleof == "??8TestOperators@@QEAA_NH@Z");
+ static assert(TestOperators.opIndex.mangleof == "??ATestOperators@@QEAAHH@Z");
+ static assert(TestOperators.opCall.mangleof == "??RTestOperators@@QEAAHHM@Z");
+}
+
+import cppmangle2;
+extern(C++, Namespace18922)
+{
+ // Nspace
+ void func18922(cppmangle2.Namespace18922.Struct18922) {}
+ // CPPNamespaceAttribute
+ void func18922_1(Struct18922) {}
+}
+
+extern(C++, `Namespace18922`)
+{
+ // Nspace
+ void func18922_2(cppmangle2.Namespace18922.Struct18922) {}
+ // CPPNamespaceAttribute
+ void func18922_3(Struct18922) {}
+}
+
+version (Posix)
+{
+ static assert(func18922.mangleof == "_ZN14Namespace189229func18922ENS_11Struct18922E");
+ static assert(func18922_1.mangleof == "_ZN14Namespace1892211func18922_1ENS_11Struct18922E");
+ static assert(func18922_2.mangleof == "_ZN14Namespace1892211func18922_2ENS_11Struct18922E");
+ static assert(func18922_3.mangleof == "_ZN14Namespace1892211func18922_3ENS_11Struct18922E");
+}
+else version(Windows)
+{
+ static assert(func18922.mangleof == "?func18922@Namespace18922@@YAXUStruct18922@1@@Z");
+ static assert(func18922_1.mangleof == "?func18922_1@Namespace18922@@YAXUStruct18922@1@@Z");
+ static assert(func18922_2.mangleof == "?func18922_2@Namespace18922@@YAXUStruct18922@1@@Z");
+ static assert(func18922_3.mangleof == "?func18922_3@Namespace18922@@YAXUStruct18922@1@@Z");
+}
+
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18957
+// extern(C++) doesn't mangle 'std' correctly on posix systems
+
+version (Posix)
+{
+ // https://godbolt.org/z/C5T2LQ
+ /+
+ namespace std
+ {
+ struct test18957 {};
+ }
+ void test18957(const std::test18957& t) {}
+ +/
+ extern (C++) void test18957(ref const(Struct18957) t) {}
+
+ static assert(test18957.mangleof == "_Z9test18957RKSt11Struct18957");
+}
+
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19043
+// Incorrect mangling for extern(C++) const template parameter on windows
+
+extern(C++) struct test19043(T) {}
+
+extern(C++) void test19043a(test19043!(const(char)) a) {}
+extern(C++) void test19043b(T)(T a) {}
+version(Windows)
+{
+ static assert(test19043a.mangleof == "?test19043a@@YAXU?$test19043@$$CBD@@@Z");
+ static assert(test19043b!(test19043!(const(char))).mangleof ==
+ "??$test19043b@U?$test19043@$$CBD@@@@YAXU?$test19043@$$CBD@@@Z");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=16479
+// Missing substitution while mangling C++ template parameter for functions
+version (Posix) extern (C++)
+{
+ // Make sure aliases are still resolved
+ alias Alias16479 = int;
+ Alias16479 func16479_0 (FuncT1) (FuncT1, Alias16479);
+ static assert(func16479_0!(int).mangleof == `_Z11func16479_0IiEiT_i`);
+
+ // Simple substitution on return type
+ FuncT1* func16479_1 (FuncT1) ();
+ static assert(func16479_1!(int).mangleof == `_Z11func16479_1IiEPT_v`);
+
+ // Simple substitution on parameter
+ void func16479_2 (FuncT1) (FuncT1);
+ static assert(func16479_2!(int).mangleof == `_Z11func16479_2IiEvT_`);
+
+ // Make sure component substition is prefered over template parameter
+ FuncT1* func16479_3 (FuncT1) (FuncT1);
+ static assert(func16479_3!(int).mangleof == `_Z11func16479_3IiEPT_S0_`);
+
+ struct Array16479 (Arg) { Arg* data; }
+ struct Array16479_2 (Arg, int Size) { Arg[Size] data; }
+ struct Value16479 (int Value1, int Value2) { int data; }
+
+ // Make sure template parameter substitution happens on templated return
+ Array16479!(FuncT2) func16479_4 (FuncT1, FuncT2) (FuncT1);
+ static assert(func16479_4!(int, float).mangleof
+ == `_Z11func16479_4IifE10Array16479IT0_ET_`);
+
+ // Make sure template parameter substitution happens with values
+ Value16479!(Value2, Value1)* func16479_5 (int Value1, int Value2) ();
+ static assert(func16479_5!(1, 1).mangleof
+ == `_Z11func16479_5ILi1ELi1EEP10Value16479IXT0_EXT_EEv`);
+
+ // But make sure it's not substituting *too many* values
+ Value16479!(1, 1)* func16479_6 (int Value1, int Value2) ();
+ static assert(func16479_6!(1, 1).mangleof
+ == `_Z11func16479_6ILi1ELi1EEP10Value16479ILi1ELi1EEv`);
+
+ // Or too many types
+ Array16479!(int) func16479_7 (FuncT1, FuncT2) (FuncT1);
+ static assert(func16479_7!(int, int).mangleof
+ == `_Z11func16479_7IiiE10Array16479IiET_`);
+
+ // Also must check the parameters for template param substitution
+ void func16479_8 (FuncT1) (Array16479!(FuncT1));
+ static assert(func16479_8!(int).mangleof
+ == `_Z11func16479_8IiEv10Array16479IT_E`);
+
+ // And non-substitution
+ void func16479_9 (FuncT1) (Array16479!(int));
+ static assert(func16479_9!(int).mangleof
+ == `_Z11func16479_9IiEv10Array16479IiE`);
+
+ // Now let's have a bit of fun with alias parameters,
+ // starting with C functions
+ // TODO: Why is this mangled by g++:
+ /*
+ extern "C"
+ {
+ void externC16479 (int);
+ }
+
+ template<void (*Print)(int)>
+ void func16479_10 ();
+
+ void foo () { func16479_10<externC16479>(); }
+ */
+ extern (C) void externC16479 (int);
+ void func16479_10 (alias Print) ();
+ static assert(func16479_10!(externC16479).mangleof
+ == `_Z12func16479_10IXadL_Z12externC16479EEEvv`);
+
+ /**
+ * Let's not exclude C++ functions
+ * Note:
+ * Passing a function as template parameter has an implicit
+ * `&` operator prepended to it, so the following code:
+ * ---
+ * void CPPPrinter16479(const char*);
+ * template<void (*Print)(const char*)> void func16479_11 ();
+ * void foo () { func16479_11<CPPPrinter16479>(); }
+ * ---
+ * Gets mangled as `func16479_11<&CPPPrinter16479>()` would,
+ * which means the expression part of the template argument is
+ * mangled as `XadL_Z[...]E` not `XL_Z[...]E`
+ * (expressions always begin with a code anyway).
+ */
+ extern(C++) void CPPPrinter16479(const(char)*);
+ extern(C++, Namespace16479) void CPPPrinterNS16479(const(char)*);
+ extern(C++, `Namespace16479`) void CPPPrinterNS16479_1(const(char)*);
+ void func16479_11 (alias Print) ();
+ static assert(func16479_11!(CPPPrinter16479).mangleof
+ == `_Z12func16479_11IXadL_Z15CPPPrinter16479PKcEEEvv`);
+ static assert(func16479_11!(CPPPrinterNS16479).mangleof
+ == `_Z12func16479_11IXadL_ZN14Namespace1647917CPPPrinterNS16479EPKcEEEvv`);
+ static assert(func16479_11!(CPPPrinterNS16479_1).mangleof
+ == `_Z12func16479_11IXadL_ZN14Namespace1647919CPPPrinterNS16479_1EPKcEEEvv`);
+
+ // Functions are fine, but templates are finer
+ // ---
+ // template<template<typename, int> class Container, typename T, int Val>
+ // Container<T, Val> func16479_12 ();
+ // ---
+ Container!(T, Val) func16479_12 (alias Container, T, int Val) ();
+ static assert(func16479_12!(Array16479_2, int, 42).mangleof
+ == `_Z12func16479_12I12Array16479_2iLi42EET_IT0_XT1_EEv`);
+
+ // Substitution needs to happen on the most specialized type
+ // Iow, `ref T identity (T) (ref T v);` should be mangled as
+ // `_Z8identityIiET_*S1_*`, not as `_Z8identityIiET_*RS0_*`
+ ref FuncT1 func16479_13_1 (FuncT1) (ref FuncT1);
+ FuncT1* func16479_13_2 (FuncT1) (FuncT1*);
+ void func16479_13_3 (FuncT1) (FuncT1*, FuncT1*);
+ FuncT1** func16479_13_4 (FuncT1) (FuncT1*, FuncT1);
+ FuncT1 func16479_13_5 (FuncT1) (FuncT1*, FuncT1**);
+ static assert(func16479_13_1!(int).mangleof == `_Z14func16479_13_1IiERT_S1_`);
+ static assert(func16479_13_2!(float).mangleof == `_Z14func16479_13_2IfEPT_S1_`);
+ static assert(func16479_13_3!(int).mangleof == `_Z14func16479_13_3IiEvPT_S1_`);
+ static assert(func16479_13_4!(int).mangleof == `_Z14func16479_13_4IiEPPT_S1_S0_`);
+ static assert(func16479_13_5!(int).mangleof == `_Z14func16479_13_5IiET_PS0_PS1_`);
+
+ // Opaque types result in a slightly different AST
+ vector!T* func16479_14 (T) (T v);
+ static assert(func16479_14!(int).mangleof == `_Z12func16479_14IiEPSt6vectorIT_ES1_`);
+
+ struct Foo16479_15 (T);
+ struct Baguette16479_15 (T);
+ struct Bar16479_15 (T);
+ struct FooBar16479_15 (A, B);
+ void inst16479_15_2 (A, B) ();
+ void inst16479_15_3 (A, B, C) ();
+
+ static assert(inst16479_15_2!(Bar16479_15!int, int).mangleof
+ == `_Z14inst16479_15_2I11Bar16479_15IiEiEvv`);
+ static assert(inst16479_15_2!(int, Bar16479_15!int).mangleof
+ == `_Z14inst16479_15_2Ii11Bar16479_15IiEEvv`);
+ static assert(inst16479_15_2!(Bar16479_15!int, FooBar16479_15!(Bar16479_15!int, Foo16479_15!(Bar16479_15!(Foo16479_15!int)))).mangleof
+ == `_Z14inst16479_15_2I11Bar16479_15IiE14FooBar16479_15IS1_11Foo16479_15IS0_IS3_IiEEEEEvv`);
+ static assert(inst16479_15_3!(int, Bar16479_15!int, FooBar16479_15!(Bar16479_15!int, Foo16479_15!(Bar16479_15!(Foo16479_15!int)))).mangleof
+ == `_Z14inst16479_15_3Ii11Bar16479_15IiE14FooBar16479_15IS1_11Foo16479_15IS0_IS3_IiEEEEEvv`);
+
+ static import cppmangle2;
+ cppmangle2.Struct18922* func16479_16_1 (T) (T*);
+ static assert(func16479_16_1!int.mangleof == `_Z14func16479_16_1IiEPN14Namespace1892211Struct18922EPT_`);
+ T* func16479_16_2 (T) (T*);
+ static assert(func16479_16_2!int.mangleof == `_Z14func16479_16_2IiEPT_S1_`);
+ static assert(func16479_16_2!(cppmangle2.vector!int).mangleof == `_Z14func16479_16_2ISt6vectorIiEEPT_S3_`);
+ static assert(func16479_16_2!(cppmangle2.vector!int).mangleof
+ == func16479_16_2!(cppmangle2.vector!int).mangleof);
+ cppmangle2.vector!T* func16479_16_3 (T) (T*);
+ static assert(func16479_16_3!int.mangleof == `_Z14func16479_16_3IiEPSt6vectorIiEPT_`);
+
+ extern(C++, `fakestd`) {
+ extern (C++, `__1`) {
+ struct allocator16479 (T);
+ struct vector16479(T, alloc = allocator16479!T);
+ }
+ }
+ vector16479!(T, allocator16479!T)* func16479_17_1(T)();
+ vector16479!(T)* func16479_17_2(T)();
+ static assert(func16479_17_1!int.mangleof == `_Z14func16479_17_1IiEPN7fakestd3__111vector16479IT_NS1_14allocator16479IS3_EEEEv`);
+ static assert(func16479_17_2!int.mangleof == `_Z14func16479_17_2IiEPN7fakestd3__111vector16479IT_NS1_14allocator16479IS3_EEEEv`);
+
+ // Make sure substitution takes place everywhere in template arg list
+ extern(C++, "ns") void func16479_18_1(T, X)(int, X, T, float);
+ extern(C++, "ns") void func16479_18_2(T, X)(X, int, T, float);
+ static assert(func16479_18_1!(double, char).mangleof == `_ZN2ns14func16479_18_1IdcEEviT0_T_f`);
+ static assert(func16479_18_2!(double, char).mangleof == `_ZN2ns14func16479_18_2IdcEEvT0_iT_f`);
+}
+
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19278
+// extern(C++, "name") doesn't accept expressions
+
+extern(C++, "hello" ~ "world")
+{
+ void test19278();
+}
+enum NS = "lookup";
+extern(C++, (NS))
+{
+ void test19278_2();
+}
+alias AliasSeq(Args...) = Args;
+alias Tup = AliasSeq!("hello", "world");
+extern(C++, (Tup))
+{
+ void test19278_3();
+ __gshared size_t test19278_var;
+}
+extern(C++, (AliasSeq!(Tup, "yay")))
+{
+ void test19278_4();
+}
+version(Win64)
+{
+ static assert(test19278.mangleof == "?test19278@helloworld@@YAXXZ");
+ static assert(test19278_2.mangleof == "?test19278_2@lookup@@YAXXZ");
+ static assert(test19278_3.mangleof == "?test19278_3@world@hello@@YAXXZ");
+ static assert(test19278_4.mangleof == "?test19278_4@yay@world@hello@@YAXXZ");
+ static assert(test19278_var.mangleof == "?test19278_var@world@hello@@3_KA");
+}
+else version(Posix)
+{
+ static assert(test19278.mangleof == "_ZN10helloworld9test19278Ev");
+ static assert(test19278_2.mangleof == "_ZN6lookup11test19278_2Ev");
+ static assert(test19278_3.mangleof == "_ZN5hello5world11test19278_3Ev");
+ static assert(test19278_4.mangleof == "_ZN5hello5world3yay11test19278_4Ev");
+ static assert(test19278_var.mangleof == "_ZN5hello5world13test19278_varE");
+}
+
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18958
+// Issue 18958 - extern(C++) wchar, dchar mangling not correct
+
+version(Posix)
+ enum __c_wchar_t : dchar;
+else version(Windows)
+ enum __c_wchar_t : wchar;
+alias wchar_t = __c_wchar_t;
+extern (C++) void test_char_mangling(char, wchar, dchar, wchar_t);
+version (Posix)
+{
+ static assert(test_char_mangling.mangleof == "_Z18test_char_manglingcDsDiw");
+}
+version (Win64)
+{
+ static assert(test_char_mangling.mangleof == "?test_char_mangling@@YAXD_S_U_W@Z");
+}
+
+// https://github.com/dlang/dmd/pull/10021/files#r294055424
+version (Posix)
+{
+ extern(C++, PR10021_NS) struct PR10021_Struct(T){}
+ extern(C++) void PR10021_fun(int i)(PR10021_Struct!int);
+ static assert(PR10021_fun!0.mangleof == `_Z11PR10021_funILi0EEvN10PR10021_NS14PR10021_StructIiEE`);
+}
+
+// https://github.com/dlang/dmd/pull/10021#discussion_r294095749
+version (Posix)
+{
+ extern(C++, "a", "b")
+ struct PR10021_Struct2
+ {
+ void func();
+ void func2(PR10021_Struct2*);
+ }
+ static assert(PR10021_Struct2.func.mangleof == `_ZN1a1b15PR10021_Struct24funcEv`);
+ static assert(PR10021_Struct2.func2.mangleof == `_ZN1a1b15PR10021_Struct25func2EPS1_`);
+}
+
+/// https://issues.dlang.org/show_bug.cgi?id=20022
+version (Posix)
+{
+ extern(C++, `ns20022`) enum Enum20022_1 { A = 1, }
+ extern(C++) void fun20022_1(Enum20022_1);
+ extern(C++, `ns20022`) void fun20022_2(Enum20022_1);
+
+ extern(C++, ns20022)
+ {
+ enum Enum20022_2 { A = 1, }
+ void fun20022_5(Enum20022_1);
+ void fun20022_6(Enum20022_2);
+ }
+ extern(C++) void fun20022_3(Enum20022_2);
+ extern(C++, `ns20022`) void fun20022_4(Enum20022_2);
+
+ static assert(fun20022_1.mangleof == `_Z10fun20022_1N7ns2002211Enum20022_1E`);
+ static assert(fun20022_2.mangleof == `_ZN7ns2002210fun20022_2ENS_11Enum20022_1E`);
+
+ static assert(fun20022_3.mangleof == `_Z10fun20022_3N7ns2002211Enum20022_2E`);
+ static assert(fun20022_4.mangleof == `_ZN7ns2002210fun20022_4ENS_11Enum20022_2E`);
+ static assert(fun20022_5.mangleof == `_ZN7ns2002210fun20022_5ENS_11Enum20022_1E`);
+ static assert(fun20022_6.mangleof == `_ZN7ns2002210fun20022_6ENS_11Enum20022_2E`);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20094
+version (Posix)
+{
+ extern(C++, "ns20094")
+ {
+ struct xvector20094 (T) {}
+ alias V20094 = xvector20094!(ubyte);
+ }
+
+ extern(C++) void test20094(xvector20094!(V20094)* v);
+ static assert(test20094.mangleof == `_Z9test20094PN7ns2009412xvector20094INS0_IhEEEE`);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20223
+version (Posix)
+{
+ extern(C++)
+ {
+ int test20223_1(T)(int function(const(T)* value));
+ int test20223_2(T)(int function(ref const(T) value));
+
+ struct Struct20223_1 {}
+ struct Struct20223_2 {}
+ int test20223_3(ref const Struct20223_1, Struct20223_2*, Struct20223_2*);
+ int test20223_4(ref const Struct20223_1, const ref Struct20223_2, Struct20223_2*);
+
+ struct Struct20223_3 (T) {}
+ void test20223_5(ref Struct20223_1, ref Struct20223_3!(const(char)*),
+ ref Struct20223_3!(const(char)*));
+ }
+ static assert(test20223_1!int.mangleof == `_Z11test20223_1IiEiPFiPKT_E`);
+ static assert(test20223_2!int.mangleof == `_Z11test20223_2IiEiPFiRKT_E`);
+ static assert(test20223_1!(int*).mangleof == `_Z11test20223_1IPiEiPFiPKT_E`);
+ static assert(test20223_2!(int*).mangleof == `_Z11test20223_2IPiEiPFiRKT_E`);
+ static assert(test20223_3.mangleof == `_Z11test20223_3RK13Struct20223_1P13Struct20223_2S3_`);
+ static assert(test20223_4.mangleof == `_Z11test20223_4RK13Struct20223_1RK13Struct20223_2PS2_`);
+ static assert(test20223_5.mangleof == `_Z11test20223_5R13Struct20223_1R13Struct20223_3IPKcES5_`);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20224
+version (Posix)
+{
+ extern(C++) public int test20224_1(T)(set20224!T set); // ok
+ extern(C++) public int test20224_2(T)(ref set20224!T set); // segfault
+
+ extern(C++) struct set20224 (T)
+ {
+ void test ()
+ {
+ test20224_1!T(this);
+ test20224_2!T(this); // segfaults
+ }
+ }
+
+ extern(D) void func20224 ()
+ {
+ set20224!int x;
+ }
+}
+
+/**************************************/
+
+version (Posix)
+{
+ extern (C++) struct Loc2 {};
+ extern (C++) class FuncDeclaration
+ {
+ static FuncDeclaration create(ref const Loc2, ref const Loc2);
+ };
+ extern (C++) FuncDeclaration FuncDeclaration_create(ref const Loc2, ref const Loc2);
+
+ static assert(FuncDeclaration_create.mangleof == `_Z22FuncDeclaration_createRK4Loc2S1_`);
+ static assert(FuncDeclaration.create.mangleof == `_ZN15FuncDeclaration6createERK4Loc2S2_`);
+}
+
+enum Enum19542 = func19542!(int).mangleof;
+
+extern(C++, `bar`)
+{
+ void func19542(T)();
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20700
+// Only testing on WIn64 because the mangling includes 'E',
+// and the bug can be tested on either platform
+version (Win64) extern(C++)
+{
+ void test20700_1(Struct20700);
+ extern(C++, class) struct Struct20700 {}
+ void test20700_2(Struct20700);
+
+ // Note: Needs to be `V` (`class`), not `U` (`struct`)
+ static assert(test20700_1.mangleof == `?test20700_1@@YAXVStruct20700@@@Z`);
+ static assert(test20700_2.mangleof == `?test20700_2@@YAXVStruct20700@@@Z`);
+
+ // Test that the scope is not "sticky" on the arguments
+ void test20700_3(TStruct20700_1!DefaultClass20700_1);
+ extern(C++, class) struct TStruct20700_1 (T1, T2 = DefaultStruct20700_1) {}
+ extern(C++, class) struct DefaultStruct20700_1 {}
+ extern(C++, struct) class DefaultClass20700_1 {}
+ static assert(test20700_3.mangleof == `?test20700_3@@YAXV?$TStruct20700_1@PEAUDefaultClass20700_1@@VDefaultStruct20700_1@@@@@Z`);
+
+ // Each test needs to be independent symbol to trigger a new semantic pass
+ void test20700_4(TStruct20700_2!(DefaultClass20700_2, DefaultStruct20700_2));
+ extern(C++, struct) class TStruct20700_2 (T1, T2 = DefaultClass20700_2) {}
+ extern(C++, class) struct DefaultStruct20700_2 {}
+ extern(C++, struct) class DefaultClass20700_2 {}
+ static assert(test20700_4.mangleof == `?test20700_4@@YAXPEAU?$TStruct20700_2@PEAUDefaultClass20700_2@@VDefaultStruct20700_2@@@@@Z`);
}
/*****************************************/
diff --git a/gcc/testsuite/gdc.test/compilable/cppmangle2.d b/gcc/testsuite/gdc.test/compilable/cppmangle2.d
new file mode 100644
index 00000000000..d507d367db1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/cppmangle2.d
@@ -0,0 +1,21 @@
+module cppmangle2;
+
+extern(C++, Namespace18922)
+{
+ struct Struct18922 { int i; }
+}
+
+extern(C++, std)
+{
+ struct vector (T);
+}
+
+extern(C++, `Namespace18922`)
+{
+ struct Struct18922 { int i; }
+}
+
+extern(C++, `std`)
+{
+ struct vector (T);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/cppmangle3.d b/gcc/testsuite/gdc.test/compilable/cppmangle3.d
index 4c48012a3d6..531509cf4c6 100644
--- a/gcc/testsuite/gdc.test/compilable/cppmangle3.d
+++ b/gcc/testsuite/gdc.test/compilable/cppmangle3.d
@@ -1,3 +1,6 @@
+// https://issues.dlang.org/show_bug.cgi?id=15512
+// https://issues.dlang.org/show_bug.cgi?id=19893
+// https://issues.dlang.org/show_bug.cgi?id=19920
module cppmangle3;
@@ -34,4 +37,22 @@ struct Foo
alias Alias(alias a) = a;
alias Alias(T) = T;
-static assert(is(Alias!(__traits(parent, bar)) == Foo));
+static assert(is(Alias!(__traits(parent, Foo.bar)) == Foo));
+
+extern(C++, "std"):
+debug = 456;
+debug = def;
+version = 456;
+version = def;
+
+extern(C++, "std")
+{
+ debug = 456;
+ debug = def;
+ version = 456;
+ version = def;
+}
+
+extern(C++, "foo")
+extern(C++, "bar")
+version = baz;
diff --git a/gcc/testsuite/gdc.test/compilable/cppmangle_abitag.d b/gcc/testsuite/gdc.test/compilable/cppmangle_abitag.d
new file mode 100644
index 00000000000..f681174430e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/cppmangle_abitag.d
@@ -0,0 +1,106 @@
+// DISABLED: win32 win64
+// REQUIRED_ARGS: -extern-std=c++11
+/*
+ * Test C++ abi-tag name mangling.
+ * https://issues.dlang.org/show_bug.cgi?id=19949
+ */
+
+import core.attribute;
+
+extern(C++):
+
+alias Tuple(A...) = A;
+enum foo_bar = gnuAbiTag("foo", "bar");
+
+@foo_bar
+struct S
+{
+ int i;
+ this(int);
+}
+
+@foo_bar
+extern __gshared int a;
+static assert(a.mangleof == "_Z1aB3barB3foo");
+
+extern __gshared S b;
+static assert(b.mangleof == "_Z1bB3barB3foo");
+
+@foo_bar
+int f();
+static assert(f.mangleof == "_Z1fB3barB3foov");
+
+S gs(int);
+S gss(S, int);
+static assert(gs.mangleof == "_Z2gsB3barB3fooi");
+static assert(gss.mangleof == "_Z3gss1SB3barB3fooi");
+
+@foo_bar
+S fss(S, int);
+static assert(gs.mangleof == "_Z2gsB3barB3fooi");
+
+T gt(T)(int);
+T gtt(T)(T, int);
+static assert(gt!S.mangleof == "_Z2gtI1SB3barB3fooET_i");
+static assert(gtt!S.mangleof == "_Z3gttI1SB3barB3fooET_S1_i");
+
+@foo_bar
+T ft(T)(int);
+// matches Clang and GCC <= 6
+static assert(ft!S.mangleof == "_Z2ftB3barB3fooI1SB3barB3fooET_i");
+
+@foo_bar
+T ftt(T)(T, int);
+// matches Clang and GCC <= 6
+static assert(ftt!S.mangleof == "_Z3fttB3barB3fooI1SB3barB3fooET_S1_i");
+
+// GCC >= 6 only
+@gnuAbiTag("ENN")
+enum E0 { a = 0xa, }
+E0 fe();
+E0 fei(int i)();
+static assert(fe.mangleof == "_Z2feB3ENNv");
+static assert(fei!0.mangleof == "_Z3feiILi0EE2E0B3ENNv");
+
+// Linux std::string
+// https://issues.dlang.org/show_bug.cgi?id=14956#c13
+extern(C++, "std")
+{
+ struct allocator(T);
+ struct char_traits(CharT);
+
+ extern(C++, "__cxx11")
+ {
+ @gnuAbiTag("cxx11")
+ struct basic_string(CharT, Traits=char_traits!CharT, Allocator=allocator!CharT)
+ {
+ const char* data();
+ size_t length() const;
+ }
+ }
+ alias string_ = basic_string!char;
+}
+string_* toString(const char*);
+static assert(toString.mangleof == "_Z8toStringB5cxx11PKc");
+
+@gnuAbiTag("A", "B")
+{
+ void fun0();
+ static assert(fun0.mangleof == "_Z4fun0B1AB1Bv");
+}
+
+@gnuAbiTag("C", "D"):
+
+void fun1();
+static assert(fun1.mangleof == "_Z4fun1B1CB1Dv");
+
+void fun2();
+static assert(fun2.mangleof == "_Z4fun2B1CB1Dv");
+
+auto fun3()
+{
+ @gnuAbiTag("Nested")
+ extern(C++) struct T {}
+ return T();
+}
+static assert(fun3.mangleof == "_Z4fun3B1CB1DB6Nestedv");
diff --git a/gcc/testsuite/gdc.test/compilable/ctfe_math.d b/gcc/testsuite/gdc.test/compilable/ctfe_math.d
index 334b75ac941..65c99855b68 100644
--- a/gcc/testsuite/gdc.test/compilable/ctfe_math.d
+++ b/gcc/testsuite/gdc.test/compilable/ctfe_math.d
@@ -5,10 +5,10 @@ import std.math;
void main()
{
- static assert(approxEqual(sin(2.0L), 0.9092974L));
- static assert(approxEqual(cos(2.0), -0.4161468));
- static assert(approxEqual(tan(2.0f), -2.185040f));
- static assert(approxEqual(sqrt(2.0L), 1.414214L));
+ static assert(isClose(sin(2.0L), 0.9092974268));
+ static assert(isClose(cos(2.0), -0.4161468365));
+ static assert(isClose(tan(2.0f), -2.185040f, 1e-5));
+ static assert(isClose(sqrt(2.0L), 1.4142135623));
static assert(fabs(-2.0) == 2.0);
static assert(ldexp(2.5f, 3) == 20.0f);
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc1.d b/gcc/testsuite/gdc.test/compilable/ddoc1.d
index fa5042a4780..23e721dc170 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc1.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc1.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 1
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
// REQUIRED_ARGS: -d
/** This module is for ABC
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10.d b/gcc/testsuite/gdc.test/compilable/ddoc10.d
index 90ab5a19ab2..6a7a4812c60 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc10.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc10.d
@@ -1,8 +1,8 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 10
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
-// 294
+// https://issues.dlang.org/show_bug.cgi?id=294
/// The foo
struct Foo(T) { }
@@ -142,7 +142,7 @@ struct S
/****
*/
- const pure nothrow this(this) { }
+ pure nothrow this(this) { }
/****
*/
@@ -177,7 +177,7 @@ struct T
}
-// 14547
+// https://issues.dlang.org/show_bug.cgi?id=14547
/// doc-comment
int x14547 = 1;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10236.d b/gcc/testsuite/gdc.test/compilable/ddoc10236.d
index 1c547613c44..c27289472eb 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc10236.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc10236.d
@@ -4,10 +4,12 @@
/*
TEST_OUTPUT:
---
-compilable/ddoc10236.d(33): Warning: Ddoc: parameter count mismatch
-compilable/ddoc10236.d(45): Warning: Ddoc: function declaration has no parameter 'y'
-compilable/ddoc10236.d(57): Warning: Ddoc: function declaration has no parameter 'y'
-compilable/ddoc10236.d(57): Warning: Ddoc: parameter count mismatch
+compilable/ddoc10236.d(35): Warning: Ddoc: parameter count mismatch, expected 2, got 1
+compilable/ddoc10236.d(47): Warning: Ddoc: function declaration has no parameter 'y'
+compilable/ddoc10236.d(59): Warning: Ddoc: function declaration has no parameter 'y'
+compilable/ddoc10236.d(59): Warning: Ddoc: parameter count mismatch, expected 1, got 2
+compilable/ddoc10236.d(71): Warning: Ddoc: parameter count mismatch, expected 2, got 0
+compilable/ddoc10236.d(71): Note that the format is `param = description`
---
*/
@@ -57,3 +59,15 @@ void foo_no_param_y(int x, int z) // Warning: Ddoc: function declaration has no
void foo_count_mismatch_no_param_y(int x)
{
}
+
+/***********************************
+ * foo_count_mismatch_wrong_format does this.
+ * Params:
+ * x : is for this
+ * and not for that
+ * y : is for that
+ */
+
+void foo_count_mismatch_wrong_format(int x, int y)
+{
+}
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10236b.d b/gcc/testsuite/gdc.test/compilable/ddoc10236b.d
index 065ced0936c..85783e8d147 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc10236b.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc10236b.d
@@ -4,10 +4,11 @@
/*
TEST_OUTPUT:
---
-compilable/ddoc10236b.d(43): Warning: Ddoc: parameter count mismatch
-compilable/ddoc10236b.d(55): Warning: Ddoc: function declaration has no parameter 'y'
-compilable/ddoc10236b.d(67): Warning: Ddoc: function declaration has no parameter 'y'
-compilable/ddoc10236b.d(67): Warning: Ddoc: parameter count mismatch
+compilable/ddoc10236b.d(44): Warning: Ddoc: parameter count mismatch, expected 1, got 0
+compilable/ddoc10236b.d(44): Note that the format is `param = description`
+compilable/ddoc10236b.d(56): Warning: Ddoc: function declaration has no parameter 'y'
+compilable/ddoc10236b.d(68): Warning: Ddoc: function declaration has no parameter 'y'
+compilable/ddoc10236b.d(68): Warning: Ddoc: parameter count mismatch, expected 0, got 1
---
*/
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10325.d b/gcc/testsuite/gdc.test/compilable/ddoc10325.d
index 4f0068af58d..a976f5ab068 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc10325.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc10325.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 10325
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc10325;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10334.d b/gcc/testsuite/gdc.test/compilable/ddoc10334.d
index 3ff148da430..dd7a0f4c190 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc10334.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc10334.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 10334
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc10334;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10366.d b/gcc/testsuite/gdc.test/compilable/ddoc10366.d
index 5bc5f641f40..a5fc9b2eb54 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc10366.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc10366.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 10366
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
///
struct S(T)
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10367.d b/gcc/testsuite/gdc.test/compilable/ddoc10367.d
index 836c7d0cc50..7e2dedb00a5 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc10367.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc10367.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 10367
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
// REQUIRED_ARGS: -m32
// EXTRA_SOURCES: extra-files/ddoc10367.ddoc
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10869.d b/gcc/testsuite/gdc.test/compilable/ddoc10869.d
index 11b485453da..8745fbd3485 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc10869.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc10869.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 10869
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc10869;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10870.d b/gcc/testsuite/gdc.test/compilable/ddoc10870.d
index 95a82ed9ffd..ad168373e70 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc10870.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc10870.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 10870
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
///
interface I
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc11.d b/gcc/testsuite/gdc.test/compilable/ddoc11.d
index cf6070edfef..3fcf2ca00da 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc11.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc11.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 11
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/// The various floating point exceptions
enum
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc11479.d b/gcc/testsuite/gdc.test/compilable/ddoc11479.d
index d2ddb1973e8..5b00dc8523d 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc11479.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc11479.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 11479
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc11479;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc11511.d b/gcc/testsuite/gdc.test/compilable/ddoc11511.d
index ba5829ec6d9..117eba463d9 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc11511.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc11511.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -w -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 11511
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc11511;
/**
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc11823.d b/gcc/testsuite/gdc.test/compilable/ddoc11823.d
index dfde4b68cc5..c9af38f5ee3 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc11823.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc11823.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 11823
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc11823;
/// file function name is _file, arg defaults to __FILE__ but not __something__
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc12.d b/gcc/testsuite/gdc.test/compilable/ddoc12.d
index 4fdf9cff1ef..337f37abbf8 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc12.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc12.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 12
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
int ruhred; /// This documents correctly.
int rühred; /// This should too
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc12706.d b/gcc/testsuite/gdc.test/compilable/ddoc12706.d
index 399583cf8b2..a19ba219bca 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc12706.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc12706.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 12706
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
///
void test()(string[] args) if (args[$])
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc12745.d b/gcc/testsuite/gdc.test/compilable/ddoc12745.d
index 2bfaee68e9b..a2cf74b184e 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc12745.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc12745.d
@@ -1,7 +1,7 @@
// EXTRA_SOURCES:
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 12745
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
i underlined $(BR)
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc13.d b/gcc/testsuite/gdc.test/compilable/ddoc13.d
index eafc8b289be..9985bb01a04 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc13.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc13.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 13
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/// struct doc
struct Bug4107(T)
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc13270.d b/gcc/testsuite/gdc.test/compilable/ddoc13270.d
index 6b04922d45c..3d2f47e52bd 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc13270.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc13270.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS: -w
// REQUIRED_ARGS: -o- -D -Dd${RESULTS_DIR}/compilable
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 13270
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc13270;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc13645.d b/gcc/testsuite/gdc.test/compilable/ddoc13645.d
index f12b3769ae4..35bb7f08569 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc13645.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc13645.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 13645
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
Documentation comment on module
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc14.d b/gcc/testsuite/gdc.test/compilable/ddoc14.d
index fe1fcc1cff2..a8b6d4d9d4c 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc14.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc14.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 14
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
alias void V;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc14383.d b/gcc/testsuite/gdc.test/compilable/ddoc14383.d
index 273b058ad52..0f8d51cd7b1 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc14383.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc14383.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 14383
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
* Module docs.
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc14413.d b/gcc/testsuite/gdc.test/compilable/ddoc14413.d
index dace15fa155..35864766cd8 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc14413.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc14413.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 14413
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc14413;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc14778.d b/gcc/testsuite/gdc.test/compilable/ddoc14778.d
index 6bb23534ff2..a3c1728569e 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc14778.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc14778.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 14778
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc14778;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc15475.d b/gcc/testsuite/gdc.test/compilable/ddoc15475.d
index f1923525104..64c07ff42fe 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc15475.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc15475.d
@@ -1,6 +1,7 @@
-// PERMUTE_ARGS:
-// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 15475
+/* PERMUTE_ARGS:
+REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
+*/
/**
My module
@@ -8,5 +9,25 @@ My module
// Computes the interval [x,y)
auto interval = computeInterval(x, y);
----
+
+Backslash-escape parentheses with `\(` and `\)`.
+
+---
+(
+---
+
+---
+)
+---
+
+---
+ Here are some nested `backticks`
+ // Another interval [x,y)
+---
+
+---
+ This won't end the code block: --- )
+ // Yet another interval [x,y)
+---
*/
module ddoc15475;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc17697.d b/gcc/testsuite/gdc.test/compilable/ddoc17697.d
index 3d97c2c96f4..8edff321a6c 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc17697.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc17697.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 17697
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/***
* See:
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc18361.d b/gcc/testsuite/gdc.test/compilable/ddoc18361.d
new file mode 100644
index 00000000000..584b995501d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc18361.d
@@ -0,0 +1,27 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
+// REQUIRED_ARGS: -d
+
+// Test notes: 'main' is the symbol being documented (DDOC_AUTO_PSYMBOL),
+// 'arguments' is a parameter (DDOC_AUTO_PARAM), and 'false' is a keyword
+// (DDOC_AUTO_KEYWORD).
+/**
+ * The main thing this program does is nothing, and I do _not want to hear any
+ * false arguments about that!
+ *
+ * Macros:
+ * DDOC_AUTO_PSYMBOL = $0
+ * DDOC_AUTO_KEYWORD = $0
+ * DDOC_AUTO_PARAM = $0
+ * DDOC_AUTO_PSYMBOL_SUPPRESS = HALPIMBEINGSUPPRESSED $0
+ *
+ * DDOC = $(BODY)
+ * DDOC_DECL = $0
+ * DDOC_MEMBER_HEADER =
+ * DDOC_MODULE_MEMBERS = $0
+ * DDOC_MEMBER = $0
+ */
+void main(string[] arguments)
+{
+}
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc198.d b/gcc/testsuite/gdc.test/compilable/ddoc198.d
index 16485b78b72..d5bc8480f48 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc198.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc198.d
@@ -1,7 +1,7 @@
// EXTRA_SOURCES: extra-files/ddoc198.ddoc
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 198
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc198;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc19814.d b/gcc/testsuite/gdc.test/compilable/ddoc19814.d
new file mode 100644
index 00000000000..b1fd15e083e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc19814.d
@@ -0,0 +1,23 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
+
+/*
+TEST_OUTPUT:
+----
+----
+*/
+
+/++
+ + Nested Code
+ +
+ + ------------------------------------
+ + /**
+ + * Examples:
+ + * --------------------
+ + * writeln("3"); // writes '3' to stdout
+ + * --------------------
+ + */
+ + ------------------------------------
+ +/
+module test.compilable.ddoc_markdown_nested_code;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc2.d b/gcc/testsuite/gdc.test/compilable/ddoc2.d
index 54e492f1011..2a7e457cf67 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc2.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc2.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 2
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
* Summary
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc2273.d b/gcc/testsuite/gdc.test/compilable/ddoc2273.d
index 413ba71055e..ec3b74a7939 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc2273.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc2273.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 2273
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
// REQUIRED_ARGS: -m32
module ddoc2273;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc4.d b/gcc/testsuite/gdc.test/compilable/ddoc4.d
index d0677363afb..e52067b9151 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc4.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc4.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 4
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
a
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc4162.d b/gcc/testsuite/gdc.test/compilable/ddoc4162.d
index 3946eca85a6..dd2447580d1 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc4162.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc4162.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 4162
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
///
interface A
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc5.d b/gcc/testsuite/gdc.test/compilable/ddoc5.d
index 4a8c396c66c..4ddc123b064 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc5.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc5.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 5
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc5446.d b/gcc/testsuite/gdc.test/compilable/ddoc5446.d
index 2e33617ede9..0596088b2e6 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc5446.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc5446.d
@@ -1,6 +1,7 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 5446
+// EXTRA_FILES: ddoc5446a.d ddoc5446b.d
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc5446;
import ddoc5446a;
private import ddoc5446b;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc6.d b/gcc/testsuite/gdc.test/compilable/ddoc6.d
index 6d130901602..69bd64a8567 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc6.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc6.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 6
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
*
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc648.d b/gcc/testsuite/gdc.test/compilable/ddoc648.d
index 49c90973a85..ce8654303ea 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc648.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc648.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 648
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc648;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc6491.d b/gcc/testsuite/gdc.test/compilable/ddoc6491.d
index 028792accef..5fe37a33ea6 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc6491.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc6491.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 6491
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc6491;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc7.d b/gcc/testsuite/gdc.test/compilable/ddoc7.d
index 81851832b8c..9c28d1b4803 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc7.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc7.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 7
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
//-----------------------------------------------
/// my enum
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc7555.d b/gcc/testsuite/gdc.test/compilable/ddoc7555.d
index efbb54785e1..ec3001c500c 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc7555.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc7555.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 7555
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc7555;
/**
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc7656.d b/gcc/testsuite/gdc.test/compilable/ddoc7656.d
index 7a0ef0df3ce..82766dc868b 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc7656.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc7656.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 7656
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc7656;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc7715.d b/gcc/testsuite/gdc.test/compilable/ddoc7715.d
index ee33b28fe3a..ddffaf2cb8a 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc7715.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc7715.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 7715
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc7656;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc7795.d b/gcc/testsuite/gdc.test/compilable/ddoc7795.d
index c100f5d70b9..2a6ba02787e 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc7795.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc7795.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 7795
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc7795;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc8.d b/gcc/testsuite/gdc.test/compilable/ddoc8.d
index 6683b1bb90b..f3da3c09d76 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc8.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc8.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 8
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/** foo */
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc8271.d b/gcc/testsuite/gdc.test/compilable/ddoc8271.d
index 45aca90c7c7..983c0cab09a 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc8271.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc8271.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 8271
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc8271;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc8739.d b/gcc/testsuite/gdc.test/compilable/ddoc8739.d
index cf7da98eda2..005f93d4df4 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc8739.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc8739.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 8739
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc8739;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9.d b/gcc/testsuite/gdc.test/compilable/ddoc9.d
index 21f312db44a..09d54ef73ab 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9.d
@@ -1,8 +1,8 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
-// 273
+// https://issues.dlang.org/show_bug.cgi?id=273
/// Template Documentation (OK)
template Template(T) { }
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9037.d b/gcc/testsuite/gdc.test/compilable/ddoc9037.d
index ac4ace249e1..7f113ea2378 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9037.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9037.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9037
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc9037;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9155.d b/gcc/testsuite/gdc.test/compilable/ddoc9155.d
index 82b7f63ed9d..9f5a59a434c 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9155.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9155.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9155
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc9155;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9305.d b/gcc/testsuite/gdc.test/compilable/ddoc9305.d
index 9c9b890657c..a28fe10b1c9 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9305.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9305.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9305
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc9305;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9369.d b/gcc/testsuite/gdc.test/compilable/ddoc9369.d
index 13bce824443..ba448912410 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9369.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9369.d
@@ -1,7 +1,7 @@
// PERMUTE_ARGS:
// EXTRA_SOURCES: extra-files/ddoc9369.ddoc
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9369
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
Sample:
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9475.d b/gcc/testsuite/gdc.test/compilable/ddoc9475.d
index 460269ce676..262fa7dafb5 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9475.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9475.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -w -o- -c -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9475
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc9475;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9497a.d b/gcc/testsuite/gdc.test/compilable/ddoc9497a.d
index 6617434b988..422ed08dd1e 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9497a.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9497a.d
@@ -1,7 +1,7 @@
// EXTRA_SOURCES: extra-files/ddoc9497a.ddoc
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9497a
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
foo function.
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9497b.d b/gcc/testsuite/gdc.test/compilable/ddoc9497b.d
index 62ab44c8928..d059af812f1 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9497b.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9497b.d
@@ -1,7 +1,7 @@
// EXTRA_SOURCES: extra-files/ddoc9497b.ddoc
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9497b
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
foo function.
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9497c.d b/gcc/testsuite/gdc.test/compilable/ddoc9497c.d
index da9e3df11c3..fb32aefbb3f 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9497c.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9497c.d
@@ -1,7 +1,7 @@
// EXTRA_SOURCES: extra-files/ddoc9497c.ddoc
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9497c
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
foo function.
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9497d.d b/gcc/testsuite/gdc.test/compilable/ddoc9497d.d
index c42f013e41c..a8aa26bba3b 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9497d.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9497d.d
@@ -1,7 +1,7 @@
// EXTRA_SOURCES: extra-files/ddoc9497d.ddoc
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9497d
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/**
foo function.
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9676a.d b/gcc/testsuite/gdc.test/compilable/ddoc9676a.d
index 326c2cf5325..c5f0f79d223 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9676a.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9676a.d
@@ -1,7 +1,7 @@
// PERMUTE_ARGS:
// EXTRA_SOURCES: extra-files/ddoc9676a.ddoc
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9676a
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc9676a;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9676b.d b/gcc/testsuite/gdc.test/compilable/ddoc9676b.d
index 63145495ce8..433ec719dd7 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9676b.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9676b.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9676b
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc9676b;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9727.d b/gcc/testsuite/gdc.test/compilable/ddoc9727.d
index 14e5fd2ab71..d15a3bf720a 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9727.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9727.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9727
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc9727;
/** The function foo. */
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9789.d b/gcc/testsuite/gdc.test/compilable/ddoc9789.d
index b220c7f8e76..ddbb7639b62 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9789.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9789.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
-// REQUIRED_ARGS: -D -w -o- -c -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9789
+// REQUIRED_ARGS: -D -w -o- -Dd${RESULTS_DIR}/compilable -o-
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddoc9789;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc9903.d b/gcc/testsuite/gdc.test/compilable/ddoc9903.d
index 00b6a7982a2..2e0d1ae370d 100644
--- a/gcc/testsuite/gdc.test/compilable/ddoc9903.d
+++ b/gcc/testsuite/gdc.test/compilable/ddoc9903.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 9903
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/// sss
struct S9903X {}
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_breaks.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_breaks.d
new file mode 100644
index 00000000000..8242b98e89a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_breaks.d
@@ -0,0 +1,30 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_breaks.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_breaks.html
+
+/++
+# Thematic Breaks
+
+Some text before
+***
+Some text in between
+____________________
+Some text after
+
+---
+This is a code block
+---
+
+But this is a thematic break:
+
+- - -
+
+## Not Thematic Breaks
+
+- -
+__
+**
+
++/
+module ddoc_markdown_lists;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_breaks_verbose.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_breaks_verbose.d
new file mode 100644
index 00000000000..1ff26b07499
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_breaks_verbose.d
@@ -0,0 +1,13 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -wi -o- -transition=vmarkdown
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_breaks_verbose.html
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_breaks_verbose.html
+
+/++
+Thematic Breaks
+
+___
+- - -
+***
++/
+module ddoc_markdown_breaks;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_code.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_code.d
new file mode 100644
index 00000000000..56e6be36538
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_code.d
@@ -0,0 +1,46 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_code.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_code.html
+
+/++
+# Code Blocks
+
+---
+A regular ol' code block (note the uneven delimiter lengths)
+----
+
+```
+A backtick-fenced code block
+```
+
+~~~
+A tilde-fenced code block
+~~~
+
+--- d
+A hyphen-fenced D code block
+---
+
+--- d delish
+A backtick-fenced D code block
+---
+
+~~~ d
+A tilde-fenced D code block
+~~~
+
+--- ruby
+A hyphen-fenced ruby code block
+---
+
+--- ruby delish
+A backtick-fenced ruby code block
+---
+
+~~~ ruby
+A tilde-fenced ruby code block
+~~~
+
++/
+module test.compilable.ddoc_markdown_code;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_code_verbose.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_code_verbose.d
new file mode 100644
index 00000000000..eb64c04363d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_code_verbose.d
@@ -0,0 +1,13 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- -transition=vmarkdown
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_code_verbose.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_code_verbose.html
+
+/++
+Code:
+
+``` ruby red
+RWBY
+```
++/
+module test.compilable.ddoc_markdown_code_verbose;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_emphasis.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_emphasis.d
new file mode 100644
index 00000000000..8bbcbdf91a3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_emphasis.d
@@ -0,0 +1,45 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_emphasis.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_emphasis.html
+
+/++
+Markdown Emphasis:
+
+*emphasized text*
+
+*emphasized text
+on more than one line*
+
+**strongly emphasized text**
+
+*in*seperable
+
+***tricky** emphasis*
+
+*more **tricky emphasis***
+
+*tricky**unspaced***
+
+*more**tricky**unspaced*
+
+*highly **nested *emphasis* in this** line*
+
+$(B *inside a macro*)
+
+*$(B outside a macro)*
+
+**The following aren't Markdown emphasis:**
+
+a * not emphasis*
+
+a*"not either"*
+
+*nor this *
+
+*jumping $(B into a macro*)
+
+$(B *jumping) out of a macro*
+
++/
+module ddoc_markdown_emphasis;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_emphasis_verbose.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_emphasis_verbose.d
new file mode 100644
index 00000000000..07904c1e42f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_emphasis_verbose.d
@@ -0,0 +1,13 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -wi -o- -transition=vmarkdown
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_emphasis_verbose.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_emphasis_verbose.html
+
+/++
+Markdown Emphasis:
+
+*emphasized text*
+
+**strongly emphasized text**
++/
+module ddoc_markdown_emphasis;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_escapes.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_escapes.d
new file mode 100644
index 00000000000..4d8c8aeb945
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_escapes.d
@@ -0,0 +1,27 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_escapes.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_escapes.html
+
+/++
+Backslash Escapes:
+
+\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}
+
+But not in code:
+
+---
+\{\}
+---
+
+`\{\}`
+
+Nor in HTML:
+
+<tag attr="\{\}"></tag>
+
+Nor before things that aren't punctuation:
+
+C:\dlang\dmd
++/
+module ddoc_markdown_escapes;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_headings.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_headings.d
new file mode 100644
index 00000000000..e7191f89769
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_headings.d
@@ -0,0 +1,40 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_headings.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_headings.html
+
+/++
+# ATX-Style Headings
+
+# H1
+## H2
+### H3
+#### H4
+##### H5
+###### H6
+
+ ### headings
+ ## with initial
+ # spaces
+
+## heading with *emphasis*
+
+## heading with trailing `#`'s #######
+## heading with trailing literal ##'s
+## heading with another trailing literal#
+## heading with backslash-escaped trailing #\##
+
+## Some empty headers:
+##
+#
+### ###
+
+# Not Headings
+
+#hashtag not a heading because there's no space after the `#`
+
+####### Not a heading because it has more than 6 `#`'s
+
+\## Not a heading because of the preceeding backslash
++/
+module ddoc_markdown_headings;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_headings_verbose.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_headings_verbose.d
new file mode 100644
index 00000000000..64484632b4e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_headings_verbose.d
@@ -0,0 +1,9 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- -transition=vmarkdown
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_headings_verbose.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_headings_verbose.html
+
+/++
+# Heading
++/
+module ddoc_markdown_headings_verbose;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_links.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_links.d
new file mode 100644
index 00000000000..349175b0484
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_links.d
@@ -0,0 +1,42 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_links.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_links.html
+
+/++
+# Links
+
+[D Site]: https://dlang.org 'D lives here'
+[unused reference]: https://nowhere.com
+
+A link to [printf].
+
+A link to [the base object][Object].
+
+Not a link because it's an associative array: int[Object].
+
+An inline link to [the D homepage](https://dlang.org).
+
+A reference link to [the **D** homepage][d site].
+
+Not a reference link because it [links to nothing][nonexistent].
+
+A simple link to [dub].
+
+A slightly less simple link to [dub][].
+
+An image: ![D-Man](https://dlang.org/images/d3.png)
+Another image: ![D-Man again][dman-error]
+
+[dub]: <https://code.dlang.org>
+[dman-error]: https://dlang.org/images/dman-error.jpg
+
+$(P
+ [tour]: https://tour.dlang.org
+
+ Here's a [reference to the tour][tour] inside a macro.
+)
++/
+module test.compilable.ddoc_markdown_links;
+
+import core.stdc.stdio;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_links_verbose.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_links_verbose.d
new file mode 100644
index 00000000000..435b426e03e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_links_verbose.d
@@ -0,0 +1,17 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- -transition=vmarkdown
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_links_verbose.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_links_verbose.html
+
+/++
+Links:
+
+A link to [Object].
+An inline link to [the D homepage](https://dlang.org).
+A simple link to [dub].
+A slightly less simple link to [dub][].
+An image: ![D-Man](https://dlang.org/images/d3.png)
+
+[dub]: https://code.dlang.org
++/
+module test.compilable.ddoc_markdown_links_verbose;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_lists.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_lists.d
new file mode 100644
index 00000000000..1e5ff560d64
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_lists.d
@@ -0,0 +1,68 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_lists.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_lists.html
+
+/++
+# Lists
+
+## Unordered
+
+- item one
+
+ *part of* item one
+
+ ---
+ // code in item one
+ ---
+
+ **not** part of item one
+
++ + three
+- different
+* lists
+
+- list with
+-
+- empty item
+
+- parent item
+ - child item
+
+- sibling item
+ - sibling item
+
+After text:
+- ### heading
+- and item
+
+## Ordered
+
+0. zero
+1. one
+
+List separator text
+
+3. list that starts with three
+
+1. parent item
+ 1. child item
+
+1. sibling item
+ 2. sibling item
+
+## Not Lists
+
+-no initial space
+
+2.no initial space
+
+1234567890. too many numbers
+
+-1. negative
+
+New lists must start with 1, not
+6. So this isn't a list.
+
++/
+module ddoc_markdown_lists;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_lists_verbose.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_lists_verbose.d
new file mode 100644
index 00000000000..4fd1a80c58b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_lists_verbose.d
@@ -0,0 +1,9 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- -transition=vmarkdown
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_lists_verbose.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_lists_verbose.html
+
+/++
+- list item
++/
+module ddoc_markdown_lists_verbose;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_quote.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_quote.d
new file mode 100644
index 00000000000..27cfc430b8d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_quote.d
@@ -0,0 +1,53 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_quote.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_quote.html
+
+/++
+# Quote Blocks
+
+> “It seems to me that most of the ‘new’ programming languages fall into one of
+two categories: Those from academia with radical new paradigms and those from
+large corporations with a focus on RAD and the web. Maybe it’s time for a new
+language born out of practical experience implementing compilers.†-- Michael
+
+> Great, just what I need.. another D in programming. -- Segfault
+
+> To D, or not to D. -- Willeam NerdSpeare
+
+> "What I am going to tell you about is what we teach our programming students in the third or fourth year of graduate school... It is my task to convince you not to turn away because you don't understand it. You see my programming students don't understand it... That is because I don't understand it. Nobody does."
+-- Richard Deeman
+
+Here's a bit of text between quotes.
+
+> This is a quote
+> > And this is a nested quote
+
+> This is a quote with a > symbol in it
+
+> This is a quote
+with a continuation
+
+> This quote
+- is ended by this list
+
+> This quote
+### is ended by this heading
+
+> This quote is ended by a thematic break:
+____
+
+> This quote
+```
+is ended by this code
+```
+
+> ### Some things inside a quote block:
+> ___
+> ---
+> Some code
+> ---
+> - a list
+
++/
+module test.compilable.ddoc_markdown_code;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_quote_verbose.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_quote_verbose.d
new file mode 100644
index 00000000000..f16e5390ece
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_quote_verbose.d
@@ -0,0 +1,11 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- -transition=vmarkdown
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_quote_verbose.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_quote_verbose.html
+
+/++
+Quote Block:
+
+> Great, just what I need.. another D in programming. -- Segfault
++/
+module test.compilable.ddoc_markdown_code_verbose;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables.d
new file mode 100644
index 00000000000..231364acc96
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables.d
@@ -0,0 +1,42 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_tables.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_tables.html
+
+/++
+# Tables
+
+| Rounding mode | rndint(4.5) | rndint(5.5) | rndint(-4.5) | Notes |
+| ------------- | ----------: | ----------: | -----------: | ----- |
+| Round to nearest | 4 | 6 | -4 | Ties round to an even number |
+| Round down | 4 | 5 | -5 | &nbsp; |
+| Round up | 5 | 6 | -4 | &nbsp; |
+| Round to zero | 4 | 5 | -4 | &nbsp; |
+
+ this|that
+ ----|----
+ cell|cell<br>sell
+
+| abc | def |
+| --- | --- |
+| bar |
+| *bar* | baz | boo |
+
+> | quote |
+> | ----- |
+> | table |
+
+* | list |
+ | ---- |
+ | table |
+
+| default | left | center | right |
+| --- | :-- | :--: | --: |
+
+Look Ma, a table without a body!
+
+| not | a | table |
+| -- |
+| wrong number of header columns |
++/
+module test.compilable.ddoc_markdown_tables;
diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables_verbose.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables_verbose.d
new file mode 100644
index 00000000000..d1aac1cedf8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables_verbose.d
@@ -0,0 +1,13 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- -transition=vmarkdown
+// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_tables_verbose.html
+// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_tables_verbose.html
+
+/++
+Table:
+
+| this | that |
+| ---- | ---- |
+| cell | cell |
++/
+module test.compilable.ddoc_markdown_tables_verbose;
diff --git a/gcc/testsuite/gdc.test/compilable/ddocbackticks.d b/gcc/testsuite/gdc.test/compilable/ddocbackticks.d
index 8249c4b00d3..533ff0c1bb2 100644
--- a/gcc/testsuite/gdc.test/compilable/ddocbackticks.d
+++ b/gcc/testsuite/gdc.test/compilable/ddocbackticks.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh backticks
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
/++
Closely related to std.datetime is <a href="core_time.html">`core.time`</a>,
diff --git a/gcc/testsuite/gdc.test/compilable/ddocunittest.d b/gcc/testsuite/gdc.test/compilable/ddocunittest.d
index 8c691b6ffd9..27620642ee4 100644
--- a/gcc/testsuite/gdc.test/compilable/ddocunittest.d
+++ b/gcc/testsuite/gdc.test/compilable/ddocunittest.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS: -unittest
-// REQUIRED_ARGS: -D -w -o- -c -Dd${RESULTS_DIR}/compilable -o-
-// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh unittest
+// REQUIRED_ARGS: -D -w -o- -Dd${RESULTS_DIR}/compilable -o-
+// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh
module ddocunittest;
@@ -189,12 +189,6 @@ static import core.stdc.stdlib;
unittest { fooStaticImport(); }
///
-void fooPublicImport() {}
-public import core.stdc.string;
-/// test
-unittest { fooPublicImport(); }
-
-///
void fooSelectiveImport() {}
import core.stdc.ctype : isalpha;
/// test
@@ -206,6 +200,32 @@ import io = core.stdc.stdio;
/// test
unittest { fooRenamedImport(); }
+/// This is a public import
+public import core.stdc.string;
+
+/// This is a mutiple public import
+public import core.stdc.stdarg, core.stdc.stdlib;
+
+/// This is a public selective import
+public import core.stdc.string : memcpy;
+
+/// This is a public selective renamed import
+public import core.stdc.string : copy = memcpy;
+
+/// This is a public multiple selective import
+public import core.stdc.string : memcpy, memcmp;
+
+/// This is a public multiple selective renamed import
+public import core.stdc.string : copy = memcpy, compare = memcmp;
+
+/// This is a public renamed import
+public import str = core.stdc.string;
+
+// This is a public import without a DDoc comment.
+// It should not be emitted to the documentation file.
+public import core.stdc.stdlib;
+
+
// ------------------------------------
// documented unittest after conditional declarations
@@ -276,7 +296,7 @@ else
unittest { int x6b; }
// ------------------------------------
-// 9474
+// https://issues.dlang.org/show_bug.cgi?id=9474
///
void foo9474() { }
@@ -323,7 +343,7 @@ template Template9474()
unittest { alias Template9474!() T; }
// ------------------------------------
-// 9713
+// https://issues.dlang.org/show_bug.cgi?id=9713
///
void fooNoDescription() {}
@@ -334,7 +354,7 @@ unittest { if (true) {fooNoDescription(); } /* comment */ }
// ------------------------------------
-/// test for bugzilla 9757
+/// test for https://issues.dlang.org/show_bug.cgi?id=9757
void foo9757() {}
/// ditto
void bar9757() {}
@@ -371,7 +391,7 @@ unittest
}
// ------------------------------------
-// Issue 9758
+// https://issues.dlang.org/show_bug.cgi?id=9758
/// test
void foo(){}
@@ -380,7 +400,7 @@ void foo(){}
unittest { }
// ------------------------------------
-// Issue 10519
+// https://issues.dlang.org/show_bug.cgi?id=10519
///
bool balancedParens10519(string, char, char) { return true; }
@@ -392,7 +412,7 @@ unittest
}
// ------------------------------------
-// Issue 12097
+// https://issues.dlang.org/show_bug.cgi?id=12097
/// declaration
struct S12097
@@ -420,7 +440,7 @@ unittest
}
// ------------------------------------
-// 14594
+// https://issues.dlang.org/show_bug.cgi?id=14594
/*******************
* testA
diff --git a/gcc/testsuite/gdc.test/compilable/debugInference.d b/gcc/testsuite/gdc.test/compilable/debugInference.d
new file mode 100644
index 00000000000..1d4f157d788
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/debugInference.d
@@ -0,0 +1,55 @@
+/*
+REQUIRED_ARGS: -debug
+TEST_OUTPUT:
+---
+compilable/debugInference.d(35): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+https://issues.dlang.org/show_bug.cgi?id=20507
+*/
+
+
+void main() pure nothrow @safe @nogc
+{
+ debug foo();
+ bar!()();
+}
+
+void foo() @system
+{
+ // Just to be sure its neither @nogc, pure or nothrow
+ __gshared int counter = 0;
+
+ if (counter++)
+ throw new Exception(new immutable(char)[counter]);
+}
+
+void bar()()
+{
+ debug {
+ foo();
+
+ auto fPtr = &S.f;
+ auto f2Ptr = &f2;
+
+ S s;
+ delete s;
+
+ int* ptr = cast(int*) 0;
+ int[] slice = ptr[0 .. 4];
+ int val = ptr[1];
+
+ void[] bytes = slice;
+ bytes[] = bytes[];
+
+ scope int n;
+ int* pn = &n;
+ }
+}
+
+class S {
+ void f() @safe {}
+}
+
+ref int f2(return ref int i) {
+ return i;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/defa.d b/gcc/testsuite/gdc.test/compilable/defa.d
index 4fb700bed44..a9222074ce4 100644
--- a/gcc/testsuite/gdc.test/compilable/defa.d
+++ b/gcc/testsuite/gdc.test/compilable/defa.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/defaa.d imports/defab.d imports/defac.d imports/defad.d
module defa;
private import imports.defaa;
diff --git a/gcc/testsuite/gdc.test/compilable/depmsg.d b/gcc/testsuite/gdc.test/compilable/depmsg.d
index c841916a5ff..9b005d8ff73 100644
--- a/gcc/testsuite/gdc.test/compilable/depmsg.d
+++ b/gcc/testsuite/gdc.test/compilable/depmsg.d
@@ -1,6 +1,22 @@
-// REQUIRED_ARGS: -d
-// PERMUTE_ARGS: -dw
-
+/*
+REQUIRED_ARGS: -dw
+TEST_OUTPUT:
+---
+compilable/depmsg.d(39): Deprecation: struct `depmsg.main.Inner.A` is deprecated - With message!
+compilable/depmsg.d(39): Deprecation: struct `depmsg.main.Inner.A` is deprecated - With message!
+compilable/depmsg.d(40): Deprecation: class `depmsg.main.Inner.B` is deprecated - With message!
+compilable/depmsg.d(40): Deprecation: class `depmsg.main.Inner.B` is deprecated - With message!
+compilable/depmsg.d(41): Deprecation: interface `depmsg.main.Inner.C` is deprecated - With message!
+compilable/depmsg.d(41): Deprecation: interface `depmsg.main.Inner.C` is deprecated - With message!
+compilable/depmsg.d(42): Deprecation: union `depmsg.main.Inner.D` is deprecated - With message!
+compilable/depmsg.d(42): Deprecation: union `depmsg.main.Inner.D` is deprecated - With message!
+compilable/depmsg.d(43): Deprecation: enum `depmsg.main.Inner.E` is deprecated - With message!
+compilable/depmsg.d(43): Deprecation: enum `depmsg.main.Inner.E` is deprecated - With message!
+compilable/depmsg.d(45): Deprecation: alias `depmsg.main.Inner.G` is deprecated - With message!
+compilable/depmsg.d(46): Deprecation: variable `depmsg.main.Inner.H` is deprecated - With message!
+compilable/depmsg.d(47): Deprecation: class `depmsg.main.Inner.I()` is deprecated - With message!
+---
+*/
void main()
{
class Inner
diff --git a/gcc/testsuite/gdc.test/compilable/depsOutput9948.d b/gcc/testsuite/gdc.test/compilable/depsOutput9948.d
deleted file mode 100644
index 0876094d4bb..00000000000
--- a/gcc/testsuite/gdc.test/compilable/depsOutput9948.d
+++ /dev/null
@@ -1,12 +0,0 @@
-// PERMUTE_ARGS:
-// REQUIRED_ARGS: -deps=${RESULTS_DIR}/compilable/depsOutput9948.deps
-// POST_SCRIPT: compilable/extra-files/depsOutput.sh
-// EXTRA_SOURCES: extra-files/depsOutput9948a.d
-
-module depsOutput9948;
-import depsOutput9948a;
-
-void main()
-{
- templateFunc!("import std.string;")();
-}
diff --git a/gcc/testsuite/gdc.test/compilable/dip22.d b/gcc/testsuite/gdc.test/compilable/dip22.d
index 5c0201a070f..0471218853c 100644
--- a/gcc/testsuite/gdc.test/compilable/dip22.d
+++ b/gcc/testsuite/gdc.test/compilable/dip22.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/dip22.d
import imports.dip22;
class Foo : Base1, Base2
diff --git a/gcc/testsuite/gdc.test/compilable/dip22d.d b/gcc/testsuite/gdc.test/compilable/dip22d.d
index 1becf0c4c7e..cfb27faedb4 100644
--- a/gcc/testsuite/gdc.test/compilable/dip22d.d
+++ b/gcc/testsuite/gdc.test/compilable/dip22d.d
@@ -1,6 +1,5 @@
-/*
-REQUIRED_ARGS:
-*/
+// REQUIRED_ARGS:
+// EXTRA_FILES: imports/dip22d.d imports/dip22e.d
// https://github.com/dlang/DIPs/blob/master/DIPs/archive/DIP22.md
diff --git a/gcc/testsuite/gdc.test/compilable/disable_new.d b/gcc/testsuite/gdc.test/compilable/disable_new.d
new file mode 100644
index 00000000000..647d47f487b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/disable_new.d
@@ -0,0 +1,11 @@
+class C
+{
+ // force user of a type to use an external allocation strategy
+ @disable new();
+}
+
+struct S
+{
+ // force user of a type to use an external allocation strategy
+ @disable new();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_21217.d b/gcc/testsuite/gdc.test/compilable/dtoh_21217.d
new file mode 100644
index 00000000000..8836ad1f469
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_21217.d
@@ -0,0 +1,91 @@
+/*
+REQUIRED_ARGS: -HC=verbose -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler v$n$
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+struct Foo final
+{
+ int32_t a;
+ enum : int32_t { b = 2 };
+
+ // Ignored enum `dtoh_21217.Foo.c` because it is `private`.
+protected:
+ enum : int32_t { d = 4 };
+
+ enum : int32_t { e = 5 };
+
+public:
+ enum : int32_t { f = 6 };
+
+ enum : int32_t { g = 7 };
+
+private:
+ enum class Bar
+ {
+ a = 1,
+ b = 2,
+ };
+
+ // Ignored enum `dtoh_21217.Foo.h` because it is `private`.
+public:
+ Foo() :
+ a(1)
+ {
+ }
+ Foo(int32_t a) :
+ a(a)
+ {}
+};
+
+---
+*/
+
+
+extern(C++) struct Foo {
+ int a = 1;
+ enum b = 2;
+ private enum c = 3;
+ protected enum d = 4;
+ package enum e = 5;
+ public enum f = 6;
+ export enum g = 7;
+
+ private enum Bar { a = 1, b = 2 }
+ private enum h = Bar.a;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_AliasDeclaration.d b/gcc/testsuite/gdc.test/compilable/dtoh_AliasDeclaration.d
new file mode 100644
index 00000000000..e0a96d4dfa3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_AliasDeclaration.d
@@ -0,0 +1,217 @@
+/+
+REQUIRED_ARGS: -HC=silent -c -o- -Icompilable/extra-files
+PERMUTE_ARGS:
+EXTRA_FILES: extra-files/dtoh_imports.d extra-files/dtoh_imports2.d
+
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+class C;
+
+extern void importFunc();
+
+class ImportsC
+{
+};
+
+typedef int32_t MyStdcInt;
+
+enum
+{
+ IgnoreErrors = 0,
+};
+
+typedef int32_t T;
+
+extern "C" int32_t x;
+
+extern "C" int32_t foo(int32_t x);
+
+extern int32_t foo2(int32_t x);
+
+struct S;
+
+typedef S aliasS;
+
+struct S2;
+
+typedef S2 aliasS2;
+
+typedef C* aliasC;
+
+class C2;
+
+typedef C2* aliasC2;
+
+typedef size_t(*F)(size_t x);
+
+template <typename T, typename U>
+struct TS final
+{
+ TS()
+ {
+ }
+};
+
+template <typename T, typename U>
+using TSD = TS<T, U>;
+typedef TSD<int32_t, int16_t > TSI;
+
+using aliasName = ImportsC;
+
+using MyStdc = MyStdcInt;
+
+struct FullImport final
+{
+ using ImportsC = ::ImportsC;
+ using MyStdcInt = ::MyStdcInt;
+ FullImport()
+ {
+ }
+};
+
+struct SelectiveImports final
+{
+ using aliasName = ::ImportsC;
+ SelectiveImports()
+ {
+ }
+};
+
+struct PrivateImport final
+{
+ PrivateImport()
+ {
+ }
+};
+
+typedef /* noreturn */ char Impossible[0];
+
+template <typename T>
+struct Array final
+{
+ // Ignoring var length alignment 0
+ uint32_t length;
+ Array()
+ {
+ }
+};
+
+typedef Array<char > DString;
+---
++/
+
+extern (C++):
+
+alias T = int;
+
+extern (C) int x;
+
+alias u = x;
+
+extern (C) int foo(int x)
+{
+ return x * 42;
+}
+
+alias fun = foo;
+
+extern (C++) int foo2(int x)
+{
+ return x * 42;
+}
+
+alias fun2 = foo2;
+
+extern (C) struct S;
+
+alias aliasS = S;
+
+extern (C++) struct S2;
+
+alias aliasS2 = S2;
+
+extern (C) class C;
+
+alias aliasC = C;
+
+extern (C++) class C2;
+
+alias aliasC2 = C2;
+
+alias F = size_t function (size_t x);
+
+extern(C++) struct TS(T, U) {}
+alias TSD = TS;
+alias TSI = TSD!(int, short);
+
+public import dtoh_imports :
+ importFunc,
+ aliasName = ImportsC,
+ MyStdc = MyStdcInt;
+
+struct FullImport
+{
+ public import dtoh_imports;
+
+}
+
+struct SelectiveImports
+{
+ public import dtoh_imports :
+ importFunc,
+ aliasName = ImportsC;
+
+ public static import dtoh_imports;
+}
+
+struct PrivateImport
+{
+ import dtoh_imports;
+
+}
+
+alias Impossible = noreturn;
+
+struct Array(T)
+{
+ uint length;
+ alias opDollar = length;
+ alias dim = length;
+}
+alias DString = Array!(char);
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_AliasDeclaration_98.d b/gcc/testsuite/gdc.test/compilable/dtoh_AliasDeclaration_98.d
new file mode 100644
index 00000000000..1499d043351
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_AliasDeclaration_98.d
@@ -0,0 +1,56 @@
+/*
+REQUIRED_ARGS: -HC=verbose -extern-std=c++98 -o-
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler v$n$
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+template <typename T, typename U>
+struct TS final
+{
+ TS()
+ {
+ }
+};
+
+// Ignored dtoh_AliasDeclaration_98.TSD because `using` declarations require C++ 11
+---
+*/
+
+extern(C++):
+
+struct TS(T, U) {}
+alias TSD = TS;
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_AnonDeclaration.d b/gcc/testsuite/gdc.test/compilable/dtoh_AnonDeclaration.d
new file mode 100644
index 00000000000..bcf5558054d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_AnonDeclaration.d
@@ -0,0 +1,106 @@
+/*
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+struct S final
+{
+ union
+ {
+ int32_t x;
+ char c[4$?:32=u|64=LLU$];
+ };
+ struct
+ {
+ int32_t y;
+ double z;
+ extern "C" void foo();
+ void bar();
+ };
+ struct
+ {
+ int32_t outerPrivate;
+ };
+ struct
+ {
+ int32_t innerPrivate;
+ int32_t innerBar;
+ };
+ S()
+ {
+ }
+};
+
+extern void foo();
+---
+*/
+
+extern (C++) struct S
+{
+ union
+ {
+ int x;
+ char[4] c;
+ }
+
+ struct
+ {
+ int y;
+ double z;
+ extern(C) void foo() {}
+ extern(C++) void bar() {}
+ }
+
+ // Private not emitted because AnonDeclaration has no protection
+ private struct
+ {
+ int outerPrivate;
+ }
+
+ public struct {
+ // Private cannot be exported to C++
+ private int innerPrivate;
+ int innerBar;
+ }
+}
+
+extern (D)
+{
+ extern(C++) void foo() {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_CPPNamespaceDeclaration.d b/gcc/testsuite/gdc.test/compilable/dtoh_CPPNamespaceDeclaration.d
new file mode 100644
index 00000000000..6995a675256
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_CPPNamespaceDeclaration.d
@@ -0,0 +1,67 @@
+/*
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+namespace nameSpace
+{
+ extern void fn();
+
+ namespace nameSpace2
+ {
+ extern void fn2();
+
+ }
+ extern double identity(double _param_0);
+
+}
+---
+*/
+
+extern(C++, "nameSpace")
+{
+ void fn() {}
+
+ extern(C++, nameSpace2)
+ {
+ void fn2() {}
+ }
+
+ double identity(double) { return _param_0; }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_ClassDeclaration.d b/gcc/testsuite/gdc.test/compilable/dtoh_ClassDeclaration.d
new file mode 100644
index 00000000000..6274e9fb8dd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_ClassDeclaration.d
@@ -0,0 +1,347 @@
+/++
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+class ForwardClass;
+
+class BaseClass
+{
+public:
+ virtual void memberFun(ForwardClass* sds);
+};
+
+class C
+{
+public:
+ int8_t a;
+ int32_t b;
+ int64_t c;
+};
+
+class C2
+{
+public:
+ int32_t a;
+ int32_t b;
+ int64_t c;
+ C2(int32_t a);
+ virtual const C* const constRet();
+ virtual void constPar(const C* const c);
+ virtual void constThis() const;
+};
+
+class Aligned
+{
+public:
+ int8_t a;
+ int32_t b;
+ int64_t c;
+ Aligned(int32_t a);
+};
+
+class A
+{
+public:
+ int32_t a;
+ C* c;
+ virtual void foo();
+ extern "C" virtual void bar();
+ virtual void baz(int32_t x = 42);
+ struct
+ {
+ int32_t x;
+ int32_t y;
+ };
+ union
+ {
+ int32_t u1;
+ char u2[4$?:32=u|64=LLU$];
+ };
+ struct Inner final
+ {
+ int32_t x;
+ Inner() :
+ x()
+ {
+ }
+ Inner(int32_t x) :
+ x(x)
+ {}
+ };
+
+ class InnerC
+ {
+ public:
+ int32_t x;
+ };
+
+ class NonStaticInnerC
+ {
+ public:
+ int32_t x;
+ A* this;
+ };
+
+ typedef Inner I;
+ class CC;
+
+};
+
+class I1
+{
+public:
+ virtual void foo() = 0;
+};
+
+class I2 : public I1
+{
+public:
+ virtual void bar() = 0;
+};
+
+class B : public A, public I1, public I2
+{
+public:
+ using A::bar;
+ void foo();
+ void bar();
+};
+
+class Parent
+{
+ virtual void __vtable_slot_0();
+ virtual void __vtable_slot_1();
+public:
+ virtual void foo();
+};
+
+class Child final : public Parent
+{
+public:
+ void foo() /* const */;
+};
+
+class VisitorBase
+{
+public:
+ virtual void vir();
+ void stat();
+};
+
+class VisitorInter : public VisitorBase
+{
+public:
+ using VisitorBase::vir;
+ virtual void vir(int32_t i);
+ using VisitorBase::stat;
+ void stat(int32_t i);
+};
+
+class Visitor : public VisitorInter
+{
+public:
+ using VisitorInter::vir;
+ using VisitorInter::stat;
+ virtual void vir(bool b);
+ virtual void vir(char d);
+};
+
+class ForwardClass : public BaseClass
+{
+};
+---
++/
+
+/*
+ClassDeclaration has the following issues:
+ * align(n) does nothing. You can use align on classes in C++, though It is generally regarded as bad practice and should be avoided
+*/
+
+extern (C++) class C
+{
+ byte a;
+ int b;
+ long c;
+}
+
+extern (C++) class C2
+{
+ int a = 42;
+ int b;
+ long c;
+
+ this(int a) {}
+
+ const(C) constRet() { return null; }
+ void constPar(const C c) {}
+ void constThis() const {}
+}
+
+extern (C) class C3
+{
+ int a = 42;
+ int b;
+ long c;
+
+ this(int a) {}
+}
+
+extern (C++) align(1) class Aligned
+{
+ byte a;
+ int b;
+ long c;
+
+ this(int a) {}
+}
+
+extern (C++) class A
+{
+ int a;
+ C c;
+
+ void foo();
+ extern (C) void bar() {}
+ extern (C++) void baz(int x = 42) {}
+
+ struct
+ {
+ int x;
+ int y;
+ }
+
+ union
+ {
+ int u1;
+ char[4] u2;
+ }
+
+ struct Inner
+ {
+ int x;
+ }
+
+ static extern(C++) class InnerC
+ {
+ int x;
+ }
+
+ class NonStaticInnerC
+ {
+ int x;
+ }
+
+ alias I = Inner;
+
+ extern(C++) class CC;
+
+}
+
+extern(C++):
+interface I1
+{
+ void foo();
+}
+interface I2 : I1
+{
+ void bar();
+}
+
+class B : A, I1, I2
+{
+ alias bar = A.bar;
+ override void foo() {}
+ override void bar() {}
+}
+
+class Parent
+{
+ extern(D) void over() {}
+ extern(D) void over(int) {}
+ void foo() {}
+}
+
+final class Child : Parent
+{
+ extern(D) override void over() {}
+ override void foo() const {}
+}
+
+class VisitorBase
+{
+ void vir() {}
+
+ final void stat() {}
+}
+
+class VisitorInter : VisitorBase
+{
+ alias vir = VisitorBase.vir;
+ void vir(int i) {}
+
+ alias stat = VisitorBase.stat;
+ final void stat(int i) {}
+}
+
+class Visitor : VisitorInter
+{
+ alias vir = VisitorInter.vir;
+
+ alias stat = VisitorInter.stat;
+
+ mixin Methods!() m;
+ alias vir = m.vir;
+}
+
+mixin template Methods()
+{
+ extern(C++) void vir(bool b) {}
+
+ extern(C++) void vir(char d) {}
+}
+
+class ForwardClass : BaseClass
+{
+}
+
+class BaseClass
+{
+ void memberFun(ForwardClass sds);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_StructDeclaration.d b/gcc/testsuite/gdc.test/compilable/dtoh_StructDeclaration.d
new file mode 100644
index 00000000000..1bfc25bd683
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_StructDeclaration.d
@@ -0,0 +1,286 @@
+/*
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+struct S final
+{
+ int8_t a;
+ int32_t b;
+ int64_t c;
+ _d_dynamicArray< int32_t > arr;
+ S() :
+ a(),
+ b(),
+ c(),
+ arr()
+ {
+ }
+ S(int8_t a, int32_t b = 0, int64_t c = 0LL, _d_dynamicArray< int32_t > arr = {}) :
+ a(a),
+ b(b),
+ c(c),
+ arr(arr)
+ {}
+};
+
+struct S2 final
+{
+ int32_t a;
+ int32_t b;
+ int64_t c;
+ S d;
+ S2(int32_t a);
+ S2(char ) = delete;
+ S2() :
+ a(42),
+ b(),
+ c()
+ {
+ }
+};
+
+struct S3 final
+{
+ int32_t a;
+ int32_t b;
+ int64_t c;
+ extern "C" S3(int32_t a);
+ S3() :
+ a(42),
+ b(),
+ c()
+ {
+ }
+};
+
+struct S4 final
+{
+ int32_t a;
+ int64_t b;
+ int32_t c;
+ int8_t d;
+ S4() :
+ a(),
+ b(),
+ c(),
+ d()
+ {
+ }
+ S4(int32_t a, int64_t b = 0LL, int32_t c = 0, int8_t d = 0) :
+ a(a),
+ b(b),
+ c(c),
+ d(d)
+ {}
+};
+
+#pragma pack(push, 1)
+struct Aligned final
+{
+ int8_t a;
+ int32_t b;
+ int64_t c;
+ Aligned(int32_t a);
+ Aligned() :
+ a(),
+ b(),
+ c()
+ {
+ }
+};
+#pragma pack(pop)
+
+struct Null final
+{
+ void* field;
+ Null() :
+ field(nullptr)
+ {
+ }
+ Null(void* field) :
+ field(field)
+ {}
+};
+
+struct A final
+{
+ int32_t a;
+ S s;
+ extern "C" void bar();
+ void baz(int32_t x = 42);
+ struct
+ {
+ int32_t x;
+ int32_t y;
+ };
+ union
+ {
+ int32_t u1;
+ char u2[4$?:32=u|64=LLU$];
+ };
+ struct Inner final
+ {
+ int32_t x;
+ Inner() :
+ x()
+ {
+ }
+ Inner(int32_t x) :
+ x(x)
+ {}
+ };
+
+ typedef Inner I;
+ class C;
+
+ A() :
+ a(),
+ s()
+ {
+ }
+ A(int32_t a, S s = S(0, 0, 0LL, {})) :
+ a(a),
+ s(s)
+ {}
+};
+
+union U
+{
+ int32_t i;
+ char c;
+};
+---
+*/
+
+/*
+StructDeclaration has the following issues:
+ * align different than 1 does nothing; we should support align(n), where `n` in [1, 2, 4, 8, 16]
+ * align(n): inside struct definition doesn’t add alignment, but breaks generation of default ctors
+*/
+
+extern (C++) struct S
+{
+ byte a;
+ int b;
+ long c;
+ int[] arr;
+}
+
+extern (C++) struct S2
+{
+ int a = 42;
+ int b;
+ long c;
+ S d = void;
+
+ this(int a) {}
+ extern(D) this(int, int, long) {}
+ @disable this(char);
+}
+
+extern (C) struct S3
+{
+ int a = 42;
+ int b;
+ long c;
+
+ this(int a) {}
+}
+
+extern (C) struct S4
+{
+ int a;
+ long b;
+ int c;
+ byte d;
+}
+
+extern (C++) align(1) struct Aligned
+{
+ //align(1):
+ byte a;
+ int b;
+ long c;
+
+ this(int a) {}
+}
+
+extern (C++) struct Null
+{
+ void* field = null;
+}
+
+extern (C++) struct A
+{
+ int a;
+ S s;
+
+ extern (D) void foo();
+ extern (C) void bar() {}
+ extern (C++) void baz(int x = 42) {}
+
+ struct
+ {
+ int x;
+ int y;
+ }
+
+ union
+ {
+ int u1;
+ char[4] u2;
+ }
+
+ struct Inner
+ {
+ int x;
+ }
+
+ alias I = Inner;
+
+ extern(C++) class C;
+
+}
+
+extern(C++) union U
+{
+ int i;
+ char c;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_TemplateDeclaration.d b/gcc/testsuite/gdc.test/compilable/dtoh_TemplateDeclaration.d
new file mode 100644
index 00000000000..5f747fda8c8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_TemplateDeclaration.d
@@ -0,0 +1,401 @@
+/*
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+typedef uint$?:32=32|64=64$_t size_t;
+
+struct Outer final
+{
+ int32_t a;
+ struct Member final
+ {
+ typedef int32_t Nested;
+ Member()
+ {
+ }
+ };
+
+ Outer() :
+ a()
+ {
+ }
+ Outer(int32_t a) :
+ a(a)
+ {}
+};
+
+enum : int32_t { SomeOtherLength = 1 };
+
+struct ActualBuffer final
+{
+ ActualBuffer()
+ {
+ }
+};
+
+template <typename T>
+struct A final
+{
+ // Ignoring var x alignment 0
+ T x;
+ // Ignoring var Enum alignment 0
+ enum : int32_t { Enum = 42 };
+
+ // Ignoring var GsharedNum alignment 0
+ static int32_t GsharedNum;
+ // Ignoring var MemNum alignment 0
+ const int32_t MemNum;
+ void foo();
+ A()
+ {
+ }
+};
+
+template <typename T>
+struct NotInstantiated final
+{
+ // Ignoring var noInit alignment 0
+ // Ignoring var missingSem alignment 0
+ NotInstantiated()
+ {
+ }
+};
+
+struct B final
+{
+ A<int32_t > x;
+ B() :
+ x()
+ {
+ }
+ B(A<int32_t > x) :
+ x(x)
+ {}
+};
+
+template <typename T>
+struct Foo final
+{
+ // Ignoring var val alignment 0
+ T val;
+ Foo()
+ {
+ }
+};
+
+template <typename T>
+struct Bar final
+{
+ // Ignoring var v alignment 0
+ Foo<T > v;
+ Bar()
+ {
+ }
+};
+
+template <typename T>
+struct Array final
+{
+ typedef Array This;
+ typedef typeof(1 + 2) Int;
+ typedef typeof(T::a) IC;
+ Array(size_t dim);
+ ~Array();
+ void get() const;
+ template <typename T>
+ bool opCast() const;
+ // Ignoring var i alignment 0
+ typename T::Member i;
+ // Ignoring var j alignment 0
+ typename Outer::Member::Nested j;
+ void visit(typename T::Member::Nested i);
+ Array()
+ {
+ }
+};
+
+template <typename T, typename U>
+extern T foo(U u);
+
+extern A<A<int32_t > > aaint;
+
+template <typename T>
+class Parent
+{
+ // Ignoring var parentMember alignment 0
+public:
+ T parentMember;
+ void parentFinal();
+ virtual void parentVirtual();
+};
+
+template <typename T>
+class Child final : public Parent<T >
+{
+ // Ignoring var childMember alignment 0
+public:
+ T childMember;
+ void parentVirtual();
+ T childFinal();
+};
+
+extern void withDefTempl(A<int32_t > a = A<int32_t >(2, 13));
+
+template <typename T>
+extern void withDefTempl2(A<T > a = static_cast<A<T >>(A<T >(2)));
+
+class ChildInt : public Parent<int32_t >
+{
+};
+
+struct HasMixins final
+{
+ void foo(int32_t t);
+ HasMixins()
+ {
+ }
+};
+
+template <typename T>
+struct HasMixinsTemplate final
+{
+ void foo(T t);
+ HasMixinsTemplate()
+ {
+ }
+};
+
+extern HasMixinsTemplate<bool > hmti;
+
+template <typename T>
+struct NotAA final
+{
+ // Ignoring var length alignment 0
+ enum : int32_t { length = 12 };
+
+ // Ignoring var buffer alignment 0
+ T buffer[length];
+ // Ignoring var otherBuffer alignment 0
+ T otherBuffer[SomeOtherLength];
+ // Ignoring var calcBuffer alignment 0
+ T calcBuffer[foo(1)];
+ NotAA()
+ {
+ }
+};
+
+template <typename Buffer>
+struct BufferTmpl final
+{
+ // Ignoring var buffer alignment 0
+ Buffer buffer;
+ // Ignoring var buffer2 alignment 0
+ Buffer buffer2;
+ BufferTmpl()
+ {
+ }
+};
+
+struct ImportedBuffer final
+{
+ typedef ActualBuffer Buffer;
+ ActualBuffer buffer2;
+ ImportedBuffer()
+ {
+ }
+};
+---
+*/
+
+extern (C++) struct A(T)
+{
+ T x;
+ enum Enum = 42;
+ __gshared GsharedNum = 43;
+ immutable MemNum = 13;
+ void foo() {}
+}
+
+// Invalid declarations accepted because it's not instantiated
+extern (C++) struct NotInstantiated(T)
+{
+ enum T noInit;
+ enum missingSem = T.init;
+}
+
+extern (C++) struct B
+{
+ A!int x;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20604
+extern(C++)
+{
+ struct Foo (T)
+ {
+ T val;
+ }
+
+ struct Bar (T)
+ {
+ Foo!T v;
+ }
+}
+
+extern (C++) struct Array(T)
+{
+ alias This = typeof(this);
+ alias Int = typeof(1 + 2);
+ alias IC = typeof(T.a);
+
+ this(size_t dim) pure nothrow {}
+ @disable this(this);
+ ~this() {}
+ void get() const {}
+
+ bool opCast(T)() const pure nothrow @nogc @safe
+ if (is(T == bool))
+ {
+ return str.ptr !is null;
+ }
+
+ T.Member i;
+ Outer.Member.Nested j;
+ void visit(T.Member.Nested i) {}
+}
+
+struct Outer
+{
+ int a;
+ static struct Member
+ {
+ alias Nested = int;
+ }
+}
+
+// alias AO = Array!Outer;
+
+extern(C++) T foo(T, U)(U u) { return T.init; }
+
+extern(C++) __gshared A!(A!int) aaint;
+
+extern(C++) class Parent(T)
+{
+ T parentMember;
+ final void parentFinal() {}
+ void parentVirtual() {}
+}
+
+extern(C++) final class Child(T) : Parent!T
+{
+ T childMember;
+ override void parentVirtual() {}
+ T childFinal() { return T.init; }
+}
+
+extern(C++) void withDefTempl(A!int a = A!int(2)) {}
+
+extern(C++) void withDefTempl2(T)(A!T a = A!T(2)) {}
+
+extern(C++) alias withDefTempl2Inst = withDefTempl2!int;
+
+extern(C++) class ChildInt : Parent!int {}
+
+/******************************************************
+ * Mixins
+ */
+
+extern (C++):
+
+mixin template MixinA(T)
+{
+ void foo(T t) {}
+}
+
+mixin template MixinB() {}
+
+struct HasMixins
+{
+ mixin MixinA!int;
+ mixin MixinB;
+}
+
+struct HasMixinsTemplate(T)
+{
+ mixin MixinA!T;
+ mixin MixinB;
+}
+
+__gshared HasMixinsTemplate!bool hmti;
+
+/// Declarations that look like associative arrays
+
+extern(D) enum SomeOtherLength = 1;
+
+struct NotAA(T)
+{
+ private:
+ enum length = 12;
+ public:
+ T[length] buffer;
+ T[SomeOtherLength] otherBuffer;
+ T[foo(1)] calcBuffer;
+}
+
+// Same name but hidden by the template paramter
+extern (D) struct Buffer {}
+extern (D) struct ActualBuffer {}
+
+struct BufferTmpl(Buffer)
+{
+ Buffer buffer;
+ mixin BufferMixin!();
+}
+
+struct ImportedBuffer
+{
+ alias Buffer = ActualBuffer;
+ mixin BufferMixin!();
+}
+
+mixin template BufferMixin()
+{
+ Buffer buffer2;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_UnionDeclaration.d b/gcc/testsuite/gdc.test/compilable/dtoh_UnionDeclaration.d
new file mode 100644
index 00000000000..b609cc28a44
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_UnionDeclaration.d
@@ -0,0 +1,93 @@
+/+
+REQUIRED_ARGS: -o- -HC
+
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+union U1
+{
+ int32_t a;
+ double d;
+};
+
+union U2
+{
+ double d;
+ char* ptr;
+};
+
+struct S final
+{
+ int32_t i;
+ U1 u1;
+ U2 u2;
+ S() :
+ i(),
+ u1(),
+ u2()
+ {
+ }
+ S(int32_t i, U1 u1 = U1(0), U2 u2 = U2(NAN)) :
+ i(i),
+ u1(u1),
+ u2(u2)
+ {}
+};
+---
++/
+
+extern (C++):
+
+union U1
+{
+ int a;
+ double d;
+}
+
+union U2
+{
+ double d;
+ char* ptr;
+}
+
+struct S
+{
+ int i;
+ U1 u1;
+ U2 u2;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_VarDeclaration.d b/gcc/testsuite/gdc.test/compilable/dtoh_VarDeclaration.d
new file mode 100644
index 00000000000..f9e57f9abac
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_VarDeclaration.d
@@ -0,0 +1,116 @@
+/*
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+#if !defined(_d_real)
+# define _d_real long double
+#endif
+
+extern "C" int32_t z;
+
+extern int32_t t;
+
+struct S;
+
+struct S2;
+
+class C2;
+
+union U;
+
+union U2;
+
+extern "C" size_t v;
+
+extern nullptr_t typeof_null;
+
+extern nullptr_t inferred_null;
+
+extern int32_t i;
+
+extern _d_real r;
+
+extern int32_t si[4$?:32=u|64=LLU$];
+
+extern const _d_dynamicArray< const int32_t > di;
+
+extern void* ii;
+
+extern const int32_t* const pi;
+
+extern int16_t(*func)(float , bool , ...);
+---
+*/
+
+int x = 42;
+
+extern int y;
+
+extern (C) int z;
+
+extern (C++) __gshared int t;
+
+extern (C) struct S;
+
+extern (C++) struct S2;
+
+extern (C) class C;
+
+extern (C++) class C2;
+
+extern (C) union U;
+
+extern (C++) union U2;
+
+extern (C) size_t v;
+
+extern (C++) __gshared typeof(null) typeof_null = null;
+extern (C++) __gshared inferred_null = null;
+
+extern(C++):
+__gshared
+{
+ int i;
+ real r;
+ int[4] si;
+ const int[] di;
+ int[int] ii;
+ const int* pi;
+ short function(float, bool, ...) func;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_cpp98_compat.d b/gcc/testsuite/gdc.test/compilable/dtoh_cpp98_compat.d
new file mode 100644
index 00000000000..b291cf6e295
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_cpp98_compat.d
@@ -0,0 +1,142 @@
+/*
+REQUIRED_ARGS: -extern-std=c++98 -HC -c -o- -Icompilable/extra-files
+PERMUTE_ARGS:
+EXTRA_FILES: extra-files/dtoh_imports.d extra-files/dtoh_imports2.d
+
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+class ImportsC
+{
+};
+
+struct Null final
+{
+ void* field;
+ _d_dynamicArray< const char > null_;
+private:
+ Null(int32_t );
+public:
+ Null() :
+ field(NULL),
+ null_()
+ {
+ }
+ Null(void* field, _d_dynamicArray< const char > null_ = _d_dynamicArray< const char >()) :
+ field(field),
+ null_(null_)
+ {}
+};
+
+extern void* typeof_null;
+
+extern void* inferred_null;
+
+struct MyString final
+{
+ _d_dynamicArray< const char > str;
+ MyString() :
+ str()
+ {
+ }
+ MyString(_d_dynamicArray< const char > str) :
+ str(str)
+ {}
+};
+
+struct Wrapper final
+{
+ MyString s1;
+ MyString s2;
+ Wrapper() :
+ s1(MyString(_d_dynamicArray< const char >( 5, "Hello" ))),
+ s2(MyString(_d_dynamicArray< const char >()))
+ {
+ }
+ Wrapper(MyString s1, MyString s2 = MyString(_d_dynamicArray< const char >())) :
+ s1(s1),
+ s2(s2)
+ {}
+};
+
+class UsingBase
+{
+public:
+ virtual void foo();
+};
+
+class UsingChild : public UsingBase
+{
+public:
+};
+---
+*/
+
+extern (C++) struct Null
+{
+ void* field = null;
+ string null_ = null;
+
+ @disable this(int);
+}
+
+extern (C++) __gshared typeof(null) typeof_null = null;
+extern (C++) __gshared inferred_null = null;
+
+extern (C++) struct MyString
+{
+ string str;
+}
+
+extern (C++) struct Wrapper
+{
+ MyString s1 = MyString("Hello");
+ MyString s2 = MyString(null);
+}
+
+extern (C++) class UsingBase
+{
+ void foo() {}
+}
+
+extern (C++) class UsingChild : UsingBase
+{
+ alias foo = UsingBase.foo;
+}
+
+public import dtoh_imports : aliasName = ImportsC;
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_enum.d b/gcc/testsuite/gdc.test/compilable/dtoh_enum.d
new file mode 100644
index 00000000000..6a0dfd92f65
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_enum.d
@@ -0,0 +1,271 @@
+/+
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+enum : int32_t { Anon = 10 };
+
+enum : bool { Anon2 = true };
+
+static const char* const Anon3 = "wow";
+
+enum class Enum
+{
+ One = 0,
+ Two = 1,
+};
+
+extern const Enum constEnum;
+
+enum class EnumDefaultType
+{
+ One = 1,
+ Two = 2,
+};
+
+enum class EnumWithType : int8_t
+{
+ One = 1,
+ Two = 2,
+};
+
+enum
+{
+ AnonOne = 1,
+ AnonTwo = 2,
+};
+
+enum : int64_t
+{
+ AnonWithTypeOne = 1LL,
+ AnonWithTypeTwo = 2LL,
+};
+
+namespace EnumWithStringType
+{
+ static const char* const One = "1";
+ static const char* const Two = "2";
+};
+
+namespace EnumWStringType
+{
+ static const char16_t* const One = u"1";
+};
+
+namespace EnumDStringType
+{
+ static const char32_t* const One = U"1";
+};
+
+namespace EnumWithImplicitType
+{
+ static const char* const One = "1";
+ static const char* const Two = "2";
+};
+
+namespace
+{
+ static const char* const AnonWithStringOne = "1";
+ static const char* const AnonWithStringTwo = "2";
+};
+
+enum : int32_t { AnonMixedOne = 1 };
+enum : int64_t { AnonMixedTwo = 2LL };
+static const char* const AnonMixedA = "a";
+
+
+enum class STC
+{
+ a = 1,
+ b = 2,
+};
+
+static STC const STC_D = (STC)3;
+
+struct Foo final
+{
+ int32_t i;
+ Foo() :
+ i()
+ {
+ }
+ Foo(int32_t i) :
+ i(i)
+ {}
+};
+
+namespace MyEnum
+{
+ static Foo const A = Foo(42);
+ static Foo const B = Foo(84);
+};
+
+static /* MyEnum */ Foo const test = Foo(42);
+
+struct FooCpp final
+{
+ int32_t i;
+ FooCpp() :
+ i()
+ {
+ }
+ FooCpp(int32_t i) :
+ i(i)
+ {}
+};
+
+namespace MyEnumCpp
+{
+ static FooCpp const A = FooCpp(42);
+ static FooCpp const B = FooCpp(84);
+};
+
+static /* MyEnum */ Foo const testCpp = Foo(42);
+
+extern const bool e_b;
+
+enum class opaque;
+enum class typedOpaque : int64_t;
+---
++/
+
+extern(C++):
+
+enum Anon = 10;
+extern(C++) enum Anon2 = true;
+extern(C++) enum Anon3 = "wow";
+
+enum Enum
+{
+ One,
+ Two
+}
+
+extern(C++) __gshared const(Enum) constEnum;
+
+enum EnumDefaultType : int
+{
+ One = 1,
+ Two = 2
+}
+
+enum EnumWithType : byte
+{
+ One = 1,
+ Two = 2
+}
+
+enum
+{
+ AnonOne = 1,
+ AnonTwo = 2
+}
+
+enum : long
+{
+ AnonWithTypeOne = 1,
+ AnonWithTypeTwo = 2
+}
+
+enum EnumWithStringType : string
+{
+ One = "1",
+ Two = "2"
+}
+
+enum EnumWStringType : wstring
+{
+ One = "1"
+}
+
+enum EnumDStringType : dstring
+{
+ One = "1"
+}
+
+enum EnumWithImplicitType
+{
+ One = "1",
+ Two = "2"
+}
+
+enum : string
+{
+ AnonWithStringOne = "1",
+ AnonWithStringTwo = "2"
+}
+
+enum
+{
+ AnonMixedOne = 1,
+ long AnonMixedTwo = 2,
+ string AnonMixedA = "a"
+}
+
+enum STC
+{
+ a = 1,
+ b = 2,
+}
+
+extern(C++) enum STC_D = STC.a | STC.b;
+
+struct Foo { int i; }
+enum MyEnum { A = Foo(42), B = Foo(84) }
+extern(C++) enum test = MyEnum.A;
+
+extern(C++) struct FooCpp { int i; }
+enum MyEnumCpp { A = FooCpp(42), B = FooCpp(84) }
+extern(C++) enum testCpp = MyEnum.A;
+
+// currently unsupported enums
+extern(C++) enum b = [1, 2, 3];
+extern(C++) enum c = [2: 3];
+
+extern(C) void foo();
+extern(C++) enum d = &foo;
+
+__gshared immutable bool e_b;
+extern(C++) enum e = &e_b;
+
+enum opaque;
+enum typedOpaque : long;
+enum arrayOpaque : int[4]; // Cannot be exported to C++
+
+extern(D) enum hidden_d = 42; // Linkage prevents being exported to C++
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_enum_cpp98.d b/gcc/testsuite/gdc.test/compilable/dtoh_enum_cpp98.d
new file mode 100644
index 00000000000..2330d76c670
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_enum_cpp98.d
@@ -0,0 +1,244 @@
+/+
+REQUIRED_ARGS: -extern-std=c++98 -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+static int32_t const Anon = 10;
+
+static bool const Anon2 = true;
+
+static const char* const Anon3 = "wow";
+
+enum Enum
+{
+ Enum_One = 0,
+ Enum_Two = 1,
+};
+
+enum EnumDefaultType
+{
+ EnumDefaultType_One = 1,
+ EnumDefaultType_Two = 2,
+};
+
+enum EnumWithType
+{
+ EnumWithType_One = 1,
+ EnumWithType_Two = 2,
+};
+
+enum
+{
+ AnonOne = 1,
+ AnonTwo = 2,
+};
+
+enum
+{
+ AnonWithTypeOne = 1LL,
+ AnonWithTypeTwo = 2LL,
+};
+
+namespace EnumWithStringType
+{
+ static const char* const One = "1";
+ static const char* const Two = "2";
+};
+
+namespace EnumWithImplicitType
+{
+ static const char* const One = "1";
+ static const char* const Two = "2";
+};
+
+namespace
+{
+ static const char* const AnonWithStringOne = "1";
+ static const char* const AnonWithStringTwo = "2";
+};
+
+static int32_t const AnonMixedOne = 1;
+static int64_t const AnonMixedTwo = 2LL;
+static const char* const AnonMixedA = "a";
+
+
+enum STC
+{
+ STC_a = 1,
+ STC_b = 2,
+};
+
+static STC const STC_D = (STC)3;
+
+struct Foo final
+{
+ int32_t i;
+ Foo() :
+ i()
+ {
+ }
+ Foo(int32_t i) :
+ i(i)
+ {}
+};
+
+namespace MyEnum
+{
+ static Foo const A = Foo(42);
+ static Foo const B = Foo(84);
+};
+
+static /* MyEnum */ Foo const test = Foo(42);
+
+struct FooCpp final
+{
+ int32_t i;
+ FooCpp() :
+ i()
+ {
+ }
+ FooCpp(int32_t i) :
+ i(i)
+ {}
+};
+
+namespace MyEnumCpp
+{
+ static FooCpp const A = FooCpp(42);
+ static FooCpp const B = FooCpp(84);
+};
+
+static /* MyEnum */ Foo const testCpp = Foo(42);
+
+extern const bool e_b;
+---
++/
+
+extern(C++):
+enum Anon = 10;
+extern(C++) enum Anon2 = true;
+extern(C++) enum Anon3 = "wow";
+
+enum Enum
+{
+ One,
+ Two
+}
+
+enum EnumDefaultType : int
+{
+ One = 1,
+ Two = 2
+}
+
+enum EnumWithType : byte
+{
+ One = 1,
+ Two = 2
+}
+
+enum
+{
+ AnonOne = 1,
+ AnonTwo = 2
+}
+
+enum : long
+{
+ AnonWithTypeOne = 1,
+ AnonWithTypeTwo = 2
+}
+
+enum EnumWithStringType : string
+{
+ One = "1",
+ Two = "2"
+}
+
+enum EnumWithImplicitType
+{
+ One = "1",
+ Two = "2"
+}
+
+enum : string
+{
+ AnonWithStringOne = "1",
+ AnonWithStringTwo = "2"
+}
+
+enum
+{
+ AnonMixedOne = 1,
+ long AnonMixedTwo = 2,
+ string AnonMixedA = "a"
+}
+
+enum STC
+{
+ a = 1,
+ b = 2,
+}
+
+extern(C++) enum STC_D = STC.a | STC.b;
+
+struct Foo { int i; }
+enum MyEnum { A = Foo(42), B = Foo(84) }
+extern(C++) enum test = MyEnum.A;
+
+extern(C++) struct FooCpp { int i; }
+enum MyEnumCpp { A = FooCpp(42), B = FooCpp(84) }
+extern(C++) enum testCpp = MyEnum.A;
+
+// currently unsupported enums
+extern(C++) enum b = [1, 2, 3];
+extern(C++) enum c = [2: 3];
+
+extern(C) void foo();
+extern(C++) enum d = &foo;
+
+__gshared immutable bool e_b;
+extern(C++) enum e = &e_b;
+
+// Opaque enums require C++ 11
+enum opaque;
+enum typedOpaque : long;
+enum arrayOpaque : int[4];
+
+extern(D) enum hidden_d = 42; // Linkage prevents being exported to C++
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_expressions.d b/gcc/testsuite/gdc.test/compilable/dtoh_expressions.d
new file mode 100644
index 00000000000..7919c6771ee
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_expressions.d
@@ -0,0 +1,127 @@
+/+
+REQUIRED_ARGS: -o- -HC
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+extern int32_t foo();
+
+extern int32_t* somePtr;
+
+extern void insertedCast(double a = (double) foo());
+
+extern void explicitCast(int32_t b = foo());
+
+extern void requiredCast(void* ptr = (void*) foo());
+
+extern void stcCast(const int32_t* const ci = (const int32_t* const) somePtr);
+
+---
++/
+
+extern (C++):
+
+int foo() { return 0; }
+
+__gshared int* somePtr;
+
+void insertedCast(double a = foo()) {}
+
+void explicitCast(int b = cast(int) foo()) {}
+
+void requiredCast(void* ptr = cast(void*) foo()) {}
+
+void stcCast(const int* ci = cast(immutable) somePtr) {}
+
+/+
+TEST_OUTPUT:
+---
+template <typename T>
+extern T insertedTmpl(double a = static_cast<double>(foo()));
+
+template <typename T>
+extern T explicitTmpl(int32_t b = (int32_t) foo());
+
+template <typename T>
+extern T requiredTmpl(void* ptr = (void*) foo());
+
+template <typename T>
+extern T stcCastTmpl(int32_t* ci = (int32_t*) somePtr);
+
+template <typename T>
+extern T paramCastTmpl(int32_t* ci = (T) somePtr);
+---
++/
+
+T insertedTmpl(T)(double a = foo()) {}
+
+T explicitTmpl(T)(int b = cast(int) foo()) {}
+
+T requiredTmpl(T)(void* ptr = cast(void*) foo()) {}
+
+T stcCastTmpl(T)(const int* ci = cast(immutable) somePtr) {}
+
+T paramCastTmpl(T)(const int* ci = cast(T) somePtr) {}
+
+/+
+TEST_OUTPUT:
+---
+
+struct Data final
+{
+ static Data* pt;
+ static int32_t* bar();
+ Data()
+ {
+ }
+};
+
+extern void useData(bool a = !Data::pt, bool b = Data::bar() == nullptr, bool c = Data::bar() != nullptr);
+---
++/
+
+struct Data
+{
+ __gshared Data* pt;
+ static int* bar() { return null; }
+}
+
+void useData(
+ bool a = !Data.pt,
+ bool b = Data.bar() is null,
+ bool c = Data.bar() !is null
+) {}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_extern_type.d b/gcc/testsuite/gdc.test/compilable/dtoh_extern_type.d
new file mode 100644
index 00000000000..91641a6f998
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_extern_type.d
@@ -0,0 +1,174 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=21219
+
+REQUIRED_ARGS: -o- -HC
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+#if !defined(_d_real)
+# define _d_real long double
+#endif
+
+class ClassFromStruct final
+{
+public:
+ void foo();
+ ClassFromStruct()
+ {
+ }
+};
+
+class ClassFromClass
+{
+public:
+ virtual void foo();
+};
+
+struct StructFromStruct final
+{
+ void foo();
+ StructFromStruct()
+ {
+ }
+};
+
+struct StructFromClass
+{
+ virtual void foo();
+};
+
+struct Floats final
+{
+ float f;
+ double d;
+ _d_real r;
+ double nan;
+ double inf;
+ double nInf;
+ Floats() :
+ f(1.23F),
+ d(4.56),
+ r(7.89L),
+ nan(NAN),
+ inf(INFINITY),
+ nInf(-INFINITY)
+ {
+ }
+ Floats(float f, double d = 4.56, _d_real r = 7.89L, double nan = NAN, double inf = INFINITY, double nInf = -INFINITY) :
+ f(f),
+ d(d),
+ r(r),
+ nan(nan),
+ inf(inf),
+ nInf(nInf)
+ {}
+};
+
+struct Null final
+{
+ _d_dynamicArray< const char > null_;
+ Null() :
+ null_()
+ {
+ }
+ Null(_d_dynamicArray< const char > null_) :
+ null_(null_)
+ {}
+};
+
+struct Wrapper final
+{
+ Null n1;
+ Null n2;
+ Wrapper() :
+ n1(Null({ 5, "Hello" })),
+ n2(Null({}))
+ {
+ }
+ Wrapper(Null n1, Null n2 = Null({})) :
+ n1(n1),
+ n2(n2)
+ {}
+};
+
+extern const _d_dynamicArray< const char > helloWorld;
+---
+*/
+
+extern (C++, class) struct ClassFromStruct
+{
+ void foo() {}
+}
+
+extern (C++, class) class ClassFromClass
+{
+ void foo() {}
+}
+
+extern (C++, struct) struct StructFromStruct
+{
+ void foo() {}
+}
+
+extern (C++, struct) class StructFromClass
+{
+ void foo() {}
+}
+
+extern(C++) struct Floats
+{
+ float f = 1.23f;
+ double d = 4.56;
+ real r = 7.89L;
+
+ double nan = double.nan;
+ double inf = double.infinity;
+ double nInf = -double.infinity;
+}
+
+extern (C++) struct Null
+{
+ string null_ = null;
+}
+
+extern (C++) struct Wrapper
+{
+ Null n1 = Null("Hello");
+ Null n2 = Null(null);
+}
+
+extern(C++) __gshared immutable string helloWorld = `"Hello\World!"`;
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_forwarding.d b/gcc/testsuite/gdc.test/compilable/dtoh_forwarding.d
new file mode 100644
index 00000000000..483e58f6ae5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_forwarding.d
@@ -0,0 +1,265 @@
+/*
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+struct Child;
+class Struct;
+enum class Enum;
+class ExternDClass;
+struct ExternDStruct;
+template <typename T>
+class TemplClass;
+template <typename T>
+class TemplStruct;
+template <typename T>
+class ExternDTemplClass;
+struct OnlyByRef;
+
+struct Parent
+{
+ virtual void bar();
+};
+
+struct OuterStruct final
+{
+ struct NestedStruct final
+ {
+ NestedStruct()
+ {
+ }
+ };
+
+ OuterStruct()
+ {
+ }
+};
+
+struct ExternDStructRequired final
+{
+ int32_t member;
+ ExternDStructRequired() :
+ member()
+ {
+ }
+ ExternDStructRequired(int32_t member) :
+ member(member)
+ {}
+};
+
+template <typename T>
+struct ExternDTemplStruct final
+{
+ // Ignoring var member alignment 0
+ T member;
+ ExternDTemplStruct()
+ {
+ }
+};
+
+extern Child* child;
+
+struct Child : public Parent
+{
+};
+
+extern Struct* strPtr;
+
+class Struct final
+{
+public:
+ Struct()
+ {
+ }
+};
+
+extern Enum* enumPtr;
+
+enum class Enum
+{
+ foo = 0,
+};
+
+extern OuterStruct::NestedStruct* nestedStrPtr;
+
+extern ExternDClass* externDClassPtr;
+
+extern ExternDStruct* externDStrPtr;
+
+extern ExternDStructRequired externDStr2;
+
+extern TemplClass<int32_t >* templClass;
+
+template <typename T>
+class TemplClass
+{
+ // Ignoring var member alignment 0
+public:
+ T member;
+};
+
+extern TemplStruct<int32_t >* templStruct;
+
+template <typename T>
+class TemplStruct
+{
+ // Ignoring var member alignment 0
+public:
+ T member;
+};
+
+extern ExternDTemplClass<int32_t >* externTemplClass;
+
+extern ExternDTemplStruct<int32_t > externTemplStruct;
+
+extern void foo(OnlyByRef& obr);
+
+---
+*/
+
+extern (C++):
+
+__gshared Child child;
+
+extern (C++, struct)
+class Child : Parent {}
+
+extern (C++, struct)
+class Parent {
+ void bar() {}
+}
+
+//******************************************************
+
+__gshared Struct* strPtr;
+
+extern (C++, class)
+struct Struct {}
+
+//******************************************************
+
+__gshared Enum* enumPtr;
+
+enum Enum
+{
+ foo
+}
+
+//******************************************************
+
+__gshared OuterStruct.NestedStruct* nestedStrPtr;
+
+struct OuterStruct
+{
+ static struct NestedStruct {}
+}
+
+//******************************************************
+
+__gshared ExternDClass externDClassPtr;
+
+// Not emitted because the forward declaration suffices
+extern(D) class ExternDClass
+{
+ int member;
+}
+
+//******************************************************
+
+__gshared ExternDStruct* externDStrPtr;
+
+// Not emitted because the forward declaration suffices
+extern(D) struct ExternDStruct
+{
+ int member;
+}
+
+//******************************************************
+
+__gshared ExternDStructRequired externDStr2;
+
+// Emitted because the forward declaration is not sufficient when declaring an instance
+extern(D) struct ExternDStructRequired
+{
+ int member;
+}
+
+//******************************************************
+
+__gshared TemplClass!int templClass;
+
+class TemplClass(T)
+{
+ T member;
+}
+
+//******************************************************
+
+__gshared TemplStruct!int templStruct;
+
+class TemplStruct(T)
+{
+ T member;
+}
+
+//******************************************************
+
+__gshared ExternDTemplClass!int externTemplClass;
+
+// Not emitted because the forward declaration suffices
+extern(D) class ExternDTemplClass(T)
+{
+ T member;
+}
+
+//******************************************************
+
+__gshared ExternDTemplStruct!int externTemplStruct;
+
+// Required
+extern(D) struct ExternDTemplStruct(T)
+{
+ T member;
+}
+
+//******************************************************
+
+extern(D) struct OnlyByRef {}
+
+void foo(ref OnlyByRef obr) {}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_functions.d b/gcc/testsuite/gdc.test/compilable/dtoh_functions.d
new file mode 100644
index 00000000000..1ee6ce62c48
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_functions.d
@@ -0,0 +1,276 @@
+/+
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+struct S final
+{
+ int32_t i;
+ int32_t get(int32_t , int32_t );
+ static int32_t get();
+ static const int32_t staticVar;
+ void useVars(int32_t pi = i, int32_t psv = staticVar);
+ struct Nested final
+ {
+ void useStaticVar(int32_t i = staticVar);
+ Nested()
+ {
+ }
+ };
+
+ S() :
+ i()
+ {
+ }
+ S(int32_t i) :
+ i(i)
+ {}
+};
+
+extern "C" int32_t bar(int32_t x);
+
+extern "C" int32_t bar2(int32_t x);
+
+extern "C" int32_t bar4(int32_t x = 42);
+
+extern int32_t baz(int32_t x);
+
+extern int32_t baz2(int32_t x);
+
+extern int32_t baz4(int32_t x = 42);
+
+extern size_t baz5(size_t x = 42);
+
+extern size_t& bazRef(size_t& x);
+
+enum class E : int64_t
+{
+ m = 1LL,
+};
+
+enum class MS : uint8_t
+{
+ dm = 0u,
+};
+
+namespace MSN
+{
+ static S const s = S(42);
+};
+
+struct W1 final
+{
+ MS ms;
+ /* MSN */ S msn;
+ W1()
+ {
+ }
+ W1(MS ms, /* MSN */ S msn = S(42)) :
+ ms(ms),
+ msn(msn)
+ {}
+};
+
+struct W2 final
+{
+ W1 w1;
+ W2() :
+ w1()
+ {
+ }
+ W2(W1 w1) :
+ w1(w1)
+ {}
+};
+
+extern W2 w2;
+
+extern void enums(uint64_t e = $?:32=1LLU|64=static_cast<uint64_t>(E::m)$, uint8_t e2 = static_cast<uint8_t>(w2.w1.ms), S s = static_cast<S>(w2.w1.msn));
+
+extern S s;
+
+extern void aggregates(int32_t a = s.i, int32_t b = s.get(1, 2), int32_t c = S::get(), int32_t d = S::staticVar);
+
+struct S2 final
+{
+ S s;
+ struct S3 final
+ {
+ static int32_t i;
+ S3()
+ {
+ }
+ };
+
+ S2() :
+ s()
+ {
+ }
+ S2(S s) :
+ s(s)
+ {}
+};
+
+extern S2 s2;
+
+extern void chains(int32_t a = s2.s.i, int32_t b = S2::S3::i);
+
+extern S* ptr;
+
+extern int32_t(*f)(int32_t );
+
+extern void special(int32_t a = ptr->i, int32_t b = ptr->get(1, 2), int32_t j = (*f)(1));
+
+extern void variadic(int32_t _param_0, ...);
+---
++/
+
+int foo(int x)
+{
+ return x * 42;
+}
+
+extern (C) int fun();
+extern (C++) int fun2();
+
+extern (C) int bar(int x)
+{
+ return x * 42;
+}
+
+extern (C) static int bar2(int x)
+{
+ return x * 42;
+}
+
+extern (C) private int bar3(int x)
+{
+ return x * 42;
+}
+
+extern (C) int bar4(int x = 42)
+{
+ return x * 42;
+}
+
+extern (C++) int baz(int x)
+{
+ return x * 42;
+}
+
+extern (C++) static int baz2(int x)
+{
+ return x * 42;
+}
+
+extern (C++) private int baz3(int x)
+{
+ return x * 42;
+}
+
+extern (C++) int baz4(int x = 42)
+{
+ return x * 42;
+}
+
+extern (C++) size_t baz5(size_t x = 42)
+{
+ return x * 42;
+}
+
+extern (C++) ref size_t bazRef(return ref size_t x)
+{
+ return x;
+}
+
+extern (C++):
+
+enum E : long
+{
+ m = 1
+}
+
+enum MS : ubyte { dm }
+enum MSN : S { s = S(42) }
+struct W1 { MS ms; MSN msn; }
+struct W2 { W1 w1; }
+__gshared W2 w2;
+
+void enums(ulong e = E.m, ubyte e2 = w2.w1.ms, S s = w2.w1.msn) {}
+
+struct S
+{
+ int i;
+ int get(int, int);
+ static int get();
+ __gshared const int staticVar;
+
+ void useVars(int pi = i, int psv = staticVar) {}
+
+ struct Nested
+ {
+ void useStaticVar(int i = staticVar) {}
+ }
+}
+
+__gshared S s;
+
+void aggregates(int a = s.i, int b = s.get(1, 2), int c = S.get(), int d = S.staticVar) {}
+
+struct S2
+{
+
+ S s;
+ static struct S3
+ {
+ __gshared int i = 3;
+ }
+}
+
+__gshared S2 s2;
+
+void chains(int a = s2.s.i, int b = S2.S3.i) {}
+
+__gshared S* ptr;
+__gshared int function(int) f;
+
+void special(int a = ptr.i, int b = ptr.get(1, 2), int j = f(1)) {}
+
+import core.stdc.stdarg;
+void variadic(int, ...) {}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_ignored.d b/gcc/testsuite/gdc.test/compilable/dtoh_ignored.d
new file mode 100644
index 00000000000..54bbc79bf46
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_ignored.d
@@ -0,0 +1,147 @@
+/++
+REQUIRED_ARGS: -HC=verbose -c -o- -d
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler v$n$
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+#if !defined(_d_real)
+# define _d_real long double
+#endif
+
+class WithImaginary
+{
+public:
+ float memberIf;
+ double memberId;
+ _d_real memberIr;
+ _Complex float memberCf;
+ _Complex double memberCd;
+ _Complex _d_real memberCr;
+ _d_dynamicArray< float > nested;
+ // Ignored function dtoh_ignored.WithImaginary.onReturn because its return type cannot be mapped to C++
+private:
+ virtual void __vtable_slot_0();
+ // Ignored function dtoh_ignored.WithImaginary.onParam because one of its parameters has type `ifloat` which cannot be mapped to C++
+ virtual void __vtable_slot_1();
+};
+
+template <typename T>
+struct WithImaginaryTemplate final
+{
+ // Ignoring var member alignment 0
+ float member;
+ // Ignored function onReturn because its return type cannot be mapped to C++
+ // Ignored function onParam because one of its parameters has type `ifloat` which cannot be mapped to C++
+ // Ignoring var onVariable alignment 0
+ // Ignored variable onVariable because its type cannot be mapped to C++
+ WithImaginaryTemplate()
+ {
+ }
+};
+
+extern WithImaginaryTemplate<int32_t > instance;
+
+// Ignored variable dtoh_ignored.onVariable because its type cannot be mapped to C++
+// Ignored variable dtoh_ignored.onVariablePointer because its type cannot be mapped to C++
+// Ignored variable dtoh_ignored.onVariableSlice because its type cannot be mapped to C++
+// Ignored variable dtoh_ignored.onVariableArray because its type cannot be mapped to C++
+extern void* onVariableAssocArray;
+
+// Ignored variable dtoh_ignored.onVariableFunction because its type cannot be mapped to C++
+// Ignored variable dtoh_ignored.onVariableFunctionParam because its type cannot be mapped to C++
+// Ignored variable dtoh_ignored.onVariableDelegate because its type cannot be mapped to C++
+// Ignored function dtoh_ignored.myExit because its return type cannot be mapped to C++
+---
++/
+
+extern (C++):
+
+class WithImaginary
+{
+ ifloat memberIf;
+ idouble memberId;
+ ireal memberIr;
+
+ cfloat memberCf;
+ cdouble memberCd;
+ creal memberCr;
+
+ ifloat[] nested;
+
+ ifloat onReturn()
+ {
+ return 0i;
+ }
+
+ void onParam(ifloat) {}
+}
+
+struct WithImaginaryTemplate(T)
+{
+ ifloat member;
+
+ ifloat onReturn()
+ {
+ return 0i;
+ }
+
+ void onParam(ifloat)
+ {
+ }
+
+ __gshared ifloat onVariable;
+}
+
+__gshared WithImaginaryTemplate!int instance;
+
+__gshared ifloat onVariable;
+
+__gshared ifloat** onVariablePointer;
+
+__gshared ifloat[] onVariableSlice;
+
+__gshared ifloat[2] onVariableArray;
+
+__gshared ifloat[int] onVariableAssocArray;
+
+__gshared ifloat function() onVariableFunction;
+
+__gshared void function(ifloat) onVariableFunctionParam;
+
+__gshared ifloat delegate() onVariableDelegate;
+
+noreturn myExit() {}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_invalid_identifiers.d b/gcc/testsuite/gdc.test/compilable/dtoh_invalid_identifiers.d
new file mode 100644
index 00000000000..821c37c6d53
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_invalid_identifiers.d
@@ -0,0 +1,170 @@
+/+
+REQUIRED_ARGS: -HC -c -o- -wi -extern-std=c++20
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+compilable/dtoh_invalid_identifiers.d(103): Warning: function `register` is a keyword in C++
+compilable/dtoh_invalid_identifiers.d(103): The generated C++ header will contain identifiers that are keywords in C++
+compilable/dtoh_invalid_identifiers.d(105): Warning: namespace `const_cast` is a keyword in C++
+compilable/dtoh_invalid_identifiers.d(116): Warning: function `and` is a special operator in C++
+compilable/dtoh_invalid_identifiers.d(121): Warning: enum `mutable` is a keyword in C++
+compilable/dtoh_invalid_identifiers.d(123): Warning: alias `char8_t` is a keyword in C++20
+compilable/dtoh_invalid_identifiers.d(141): Warning: function `offsetof` is a default macro in C++
+compilable/dtoh_invalid_identifiers.d(143): Warning: function `wchar_t` is a keyword in C++11
+compilable/dtoh_invalid_identifiers.d(145): Warning: function `__attribute__` is a reserved identifier in C++
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+extern void register(int32_t* ptr);
+
+namespace const_cast
+{
+ extern void bar();
+
+}
+template <typename register_>
+struct S final
+{
+ // Ignoring var x alignment 0
+ register_ x;
+ S()
+ {
+ }
+};
+
+struct S2 final
+{
+ int32_t register_;
+ void and();
+ S2() :
+ register_()
+ {
+ }
+ S2(int32_t register_) :
+ register_(register_)
+ {}
+};
+
+extern void f(int32_t alignas_);
+
+enum class mutable
+{
+ yes = 0,
+ no = 1,
+};
+
+typedef S<char > char8_t;
+
+class Base
+{
+public:
+ virtual void foo();
+};
+
+template <typename typename_>
+class Alias : public typename_
+{
+};
+
+extern void user(Alias<Base* >* i);
+
+template <typename typename_>
+struct InvalidNames final
+{
+ // Ignoring var register alignment 0
+ typename_ register_;
+ void foo(typename_ and_);
+ InvalidNames()
+ {
+ }
+};
+
+extern void useInvalid(InvalidNames<int32_t > _param_0);
+
+extern size_t offsetof();
+
+extern void wchar_t();
+
+extern void __attribute__();
+---
++/
+#line 100
+extern(C++):
+
+__gshared bool and;
+void register(int* ptr) {}
+
+extern(C++, const_cast)
+void bar() {}
+
+struct S(register)
+{
+ register x;
+}
+
+struct S2
+{
+ int register;
+ void and() {}
+}
+
+void f(int alignas) {}
+
+enum mutable { yes, no }
+
+alias char8_t = S!char;
+
+class Base {
+ void foo() {}
+}
+class Alias(typename) : typename {}
+
+void user(Alias!Base i) {}
+
+struct InvalidNames(typename)
+{
+ typename register;
+
+ void foo(typename and) {}
+}
+
+void useInvalid(InvalidNames!int) {}
+
+size_t offsetof() { return 0; }
+
+void wchar_t() {}
+
+void __attribute__() {}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_names.d b/gcc/testsuite/gdc.test/compilable/dtoh_names.d
new file mode 100644
index 00000000000..068fc68ef51
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_names.d
@@ -0,0 +1,260 @@
+/+
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+struct Outer final
+{
+ static Outer* outerPtr;
+ static Middle::Inner* outerToInnerPtr;
+ static Middle::InnerTmpl<int32_t >* outerToInnerTmplPtr;
+ struct Middle final
+ {
+ static Outer* middleOuterPtr;
+ static Middle* middlePtr;
+ static Inner* middleInnerPtr;
+ struct Inner final
+ {
+ static Outer* innerOuterPtr;
+ static Middle* innerPtr;
+ static Inner* innerInnerPtr;
+ static InnerTmpl<int32_t >* innerInnerTmplPtr;
+ Inner()
+ {
+ }
+ };
+
+ template <typename U>
+ struct InnerTmpl final
+ {
+ // Ignoring var innerTmplOuterPtr alignment 0
+ static Outer* innerTmplOuterPtr;
+ // Ignoring var innerTmplPtr alignment 0
+ static Middle* innerTmplPtr;
+ // Ignoring var innerTmplInnerPtr alignment 0
+ static Inner* innerTmplInnerPtr;
+ // Ignoring var innerTmplInnerTmplPtr alignment 0
+ static InnerTmpl* innerTmplInnerTmplPtr;
+ InnerTmpl()
+ {
+ }
+ };
+
+ Middle()
+ {
+ }
+ };
+
+ template <typename T>
+ struct MiddleTmpl final
+ {
+ // Ignoring var middleTmplPtr alignment 0
+ static MiddleTmpl<T >* middleTmplPtr;
+ // Ignoring var middleTmplInnerTmplPtr alignment 0
+ static MiddleTmpl<T >* middleTmplInnerTmplPtr;
+ struct Inner final
+ {
+ // Ignoring var ptr alignment 0
+ static Inner* ptr;
+ // Ignoring var ptr2 alignment 0
+ static MiddleTmpl<T >* ptr2;
+ Inner()
+ {
+ }
+ };
+
+ template <typename U>
+ struct InnerTmpl final
+ {
+ // Ignoring var innerTmplPtr alignment 0
+ static InnerTmpl* innerTmplPtr;
+ // Ignoring var innerTmplPtrDiff alignment 0
+ static InnerTmpl<char >* innerTmplPtrDiff;
+ // Ignoring var middleTmplInnerTmplPtr alignment 0
+ static MiddleTmpl<T >* middleTmplInnerTmplPtr;
+ // Ignoring var a alignment 0
+ static T a;
+ static U bar();
+ InnerTmpl()
+ {
+ }
+ };
+
+ MiddleTmpl()
+ {
+ }
+ };
+
+ Outer()
+ {
+ }
+};
+
+---
++/
+
+extern(C++):
+
+struct Outer
+{
+ __gshared Outer* outerPtr;
+ __gshared Middle.Inner* outerToInnerPtr;
+ __gshared Middle.InnerTmpl!int* outerToInnerTmplPtr;
+
+ static struct Middle
+ {
+ __gshared Outer* middleOuterPtr;
+ __gshared Middle* middlePtr;
+ __gshared Inner* middleInnerPtr;
+
+ static struct Inner
+ {
+ __gshared Outer* innerOuterPtr;
+ __gshared Middle* innerPtr;
+ __gshared Inner* innerInnerPtr;
+ __gshared InnerTmpl!int* innerInnerTmplPtr;
+ }
+
+ static struct InnerTmpl(U)
+ {
+ __gshared Outer* innerTmplOuterPtr;
+ __gshared Middle* innerTmplPtr;
+ __gshared Inner* innerTmplInnerPtr;
+ __gshared InnerTmpl* innerTmplInnerTmplPtr;
+ }
+ }
+
+ static struct MiddleTmpl(T)
+ {
+ __gshared MiddleTmpl!T* middleTmplPtr;
+ __gshared MiddleTmpl!T.Inner* middleTmplInnerTmplPtr;
+
+ static struct Inner
+ {
+ __gshared Inner* ptr;
+ __gshared MiddleTmpl!T.Inner* ptr2;
+ }
+
+ static struct InnerTmpl(U)
+ {
+ __gshared InnerTmpl* innerTmplPtr;
+ __gshared InnerTmpl!char* innerTmplPtrDiff;
+ __gshared MiddleTmpl!T.Inner* middleTmplInnerTmplPtr;
+
+ __gshared T a;
+ static U bar() { return U.init; }
+ }
+ }
+}
+
+/+
+TEST_OUTPUT:
+---
+extern Outer::Middle::Inner inner;
+
+extern Outer::Middle::InnerTmpl<int32_t > innerTmpl;
+
+extern Outer::MiddleTmpl<int32_t >::Inner middleTmpl;
+
+extern Outer::MiddleTmpl<int32_t >::InnerTmpl<double > bothTmpl;
+
+---
++/
+
+__gshared Outer.Middle.Inner inner;
+
+__gshared Outer.Middle.InnerTmpl!int innerTmpl;
+
+__gshared Outer.MiddleTmpl!int.Inner middleTmpl;
+
+__gshared Outer.MiddleTmpl!int.InnerTmpl!double bothTmpl;
+
+/+
+TEST_OUTPUT:
+---
+typedef Outer::MiddleTmpl<int32_t >::InnerTmpl<double > FullTmplInst;
+
+template <typename U>
+using FullTmpl = Outer::MiddleTmpl<int32_t >::InnerTmpl<U>;
+---
++/
+
+alias FullTmplInst = Outer.MiddleTmpl!int.InnerTmpl!double;
+
+alias FullTmpl = Outer.MiddleTmpl!int.InnerTmpl;
+
+/+
+TEST_OUTPUT:
+---
+extern void dotId(int32_t a = Outer::MiddleTmpl<int32_t >::InnerTmpl<double >::a);
+
+---
++/
+
+void dotId( int a = Outer.MiddleTmpl!int.InnerTmpl!double.a ) {}
+
+/+
+TEST_OUTPUT:
+---
+extern void castExp(double a = (double) Outer::MiddleTmpl<int32_t >::InnerTmpl<double >::a);
+
+---
++/
+
+void castExp( double a = Outer.MiddleTmpl!int.InnerTmpl!double.a ) {}
+
+/+
+TEST_OUTPUT:
+---
+extern void structLit(Outer::MiddleTmpl<int32_t >::InnerTmpl<double > a = Outer::MiddleTmpl<int32_t >::InnerTmpl<double >());
+
+---
++/
+
+void structLit( Outer.MiddleTmpl!int.InnerTmpl!double a = Outer.MiddleTmpl!int.InnerTmpl!double() ) {}
+
+/+
+TEST_OUTPUT:
+---
+extern void callExp(double a = Outer::MiddleTmpl<int32_t >::InnerTmpl<double >::bar());
+
+---
++/
+
+void callExp( double a = Outer.MiddleTmpl!int.InnerTmpl!double.bar() ) {}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_protection.d b/gcc/testsuite/gdc.test/compilable/dtoh_protection.d
new file mode 100644
index 00000000000..2180c4134a3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_protection.d
@@ -0,0 +1,218 @@
+/**
+https://issues.dlang.org/show_bug.cgi?id=21218
+
+REQUIRED_ARGS: -HC -o-
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+struct S1 final
+{
+ int32_t a;
+protected:
+ int32_t b;
+ int32_t c;
+ int32_t d;
+private:
+ int32_t e;
+public:
+ S1()
+ {
+ }
+};
+
+class S2 final
+{
+public:
+ int32_t af();
+protected:
+ int32_t bf();
+ int32_t cf();
+ int32_t df();
+public:
+ S2()
+ {
+ }
+};
+
+class C1
+{
+public:
+ int32_t a;
+protected:
+ int32_t b;
+ int32_t c;
+ int32_t d;
+private:
+ int32_t e;
+};
+
+struct C2
+{
+ virtual int32_t af();
+protected:
+ virtual int32_t bf();
+ int32_t cf();
+ int32_t df();
+};
+
+struct Outer final
+{
+private:
+ int32_t privateOuter;
+public:
+ struct PublicInnerStruct final
+ {
+ private:
+ int32_t privateInner;
+ public:
+ int32_t publicInner;
+ PublicInnerStruct() :
+ publicInner()
+ {
+ }
+ PublicInnerStruct(int32_t publicInner) :
+ publicInner(publicInner)
+ {}
+ };
+
+private:
+ struct PrivateInnerClass final
+ {
+ private:
+ int32_t privateInner;
+ public:
+ int32_t publicInner;
+ PrivateInnerClass() :
+ publicInner()
+ {
+ }
+ PrivateInnerClass(int32_t publicInner) :
+ publicInner(publicInner)
+ {}
+ };
+
+public:
+ class PublicInnerInterface
+ {
+ public:
+ virtual void foo() = 0;
+ };
+
+private:
+ enum class PrivateInnerEnum
+ {
+ A = 0,
+ B = 1,
+ };
+
+public:
+ typedef PrivateInnerEnum PublicAlias;
+ Outer()
+ {
+ }
+};
+---
+*/
+
+module compilable.dtoh_protection;
+
+extern(C++) struct S1
+{
+ public int a;
+ protected int b;
+ package int c;
+ package(compilable) int d;
+ private int e;
+}
+
+extern(C++, class) struct S2
+{
+ public int af();
+ protected int bf();
+ package int cf();
+ package(compilable) int df();
+ private int ef();
+}
+
+extern(C++) class C1
+{
+ public int a;
+ protected int b;
+ package int c;
+ package(compilable) int d;
+ private int e;
+}
+
+extern(C++, struct) class C2
+{
+ public int af();
+ protected int bf();
+ package int cf();
+ package(compilable) int df();
+ private int ef();
+}
+
+extern(C++) struct Outer
+{
+ private int privateOuter;
+
+ static struct PublicInnerStruct
+ {
+ private int privateInner;
+ int publicInner;
+ }
+
+ private static struct PrivateInnerClass
+ {
+ private int privateInner;
+ int publicInner;
+ }
+
+ static interface PublicInnerInterface
+ {
+ void foo();
+ }
+
+ private static enum PrivateInnerEnum
+ {
+ A,
+ B
+ }
+
+ public alias PublicAlias = PrivateInnerEnum;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_required_symbols.d b/gcc/testsuite/gdc.test/compilable/dtoh_required_symbols.d
new file mode 100644
index 00000000000..6e2d2f1e38c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_required_symbols.d
@@ -0,0 +1,225 @@
+/+
+REQUIRED_ARGS: -o- -HC
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+class ExternDClass;
+struct ExternDStruct2;
+struct ExternDStruct3;
+
+struct ExternDStruct final
+{
+ int32_t i;
+ double d;
+ ExternDStruct() :
+ i(),
+ d()
+ {
+ }
+ ExternDStruct(int32_t i, double d = NAN) :
+ i(i),
+ d(d)
+ {}
+};
+
+enum class ExternDEnum
+{
+ A = 0,
+};
+
+template <>
+struct ExternDStructTemplate final
+{
+ // Ignoring var i alignment 0
+ int32_t i;
+ // Ignoring var d alignment 0
+ double d;
+ ExternDStructTemplate()
+ {
+ }
+};
+
+class Object
+{
+ virtual void __vtable_slot_0();
+ virtual void __vtable_slot_1();
+ virtual void __vtable_slot_2();
+ virtual void __vtable_slot_3();
+public:
+ class Monitor
+ {
+ virtual void __vtable_slot_4();
+ virtual void __vtable_slot_5();
+ };
+
+};
+
+class ExternDClass : public Object
+{
+public:
+ int32_t i;
+ double d;
+};
+
+struct ExternDStruct2 final
+{
+ int32_t doStuff();
+ ExternDStruct2()
+ {
+ }
+};
+
+struct ExternDStruct3 final
+{
+ int32_t a;
+ ExternDStruct3() :
+ a()
+ {
+ }
+ ExternDStruct3(int32_t a) :
+ a(a)
+ {}
+};
+
+namespace ExternDEnum2
+{
+ static ExternDStruct3 const A = ExternDStruct3(1);
+};
+
+struct ExternCppStruct final
+{
+ ExternDStruct s;
+ ExternDEnum e;
+ ExternDStructTemplate< > st;
+ ExternCppStruct() :
+ s(),
+ st()
+ {
+ }
+ ExternCppStruct(ExternDStruct s, ExternDEnum e = (ExternDEnum)0, ExternDStructTemplate< > st = ExternDStructTemplate< >(0, NAN)) :
+ s(s),
+ e(e),
+ st(st)
+ {}
+};
+
+extern ExternDClass* globalC;
+
+extern void foo(int32_t arg = globalC.i);
+
+extern ExternDStruct2* globalS2;
+
+extern void bar(int32_t arg = globalS2->doStuff());
+
+extern /* ExternDEnum2 */ ExternDStruct3* globalE2;
+
+extern void baz(int32_t arg = globalE2->a);
+---
+
+Known issues:
+- class declarations must be emitted on member access
++/
+
+// extern(D) symbols are ignored upon first visit but required later
+
+struct ExternDStruct
+{
+ int i;
+ double d;
+
+ // None of these can be emitted due to the mismatched mangling
+ static double staticDouble;
+ static shared double staticSharedDouble;
+ __gshared double sharedDouble;
+}
+
+struct ExternDStruct2
+{
+ extern(C++) int doStuff()
+ {
+ return 1;
+ }
+}
+
+struct ExternDStruct3
+{
+ int a;
+}
+
+class ExternDClass
+{
+ int i;
+ double d;
+}
+
+enum ExternDEnum
+{
+ A
+}
+
+enum ExternDEnum2 : ExternDStruct3
+{
+ A = ExternDStruct3(1)
+}
+
+struct ExternDStructTemplate()
+{
+ int i;
+ double d;
+}
+
+extern (C++):
+
+struct ExternCppStruct
+{
+ ExternDStruct s;
+ ExternDEnum e;
+ ExternDStructTemplate!() st;
+}
+
+__gshared ExternDClass globalC;
+
+void foo(int arg = globalC.i) {}
+
+__gshared ExternDStruct2* globalS2;
+
+void bar(int arg = globalS2.doStuff()) {}
+
+__gshared ExternDEnum2* globalE2;
+
+void baz(int arg = globalE2.a) {}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_special_enum.d b/gcc/testsuite/gdc.test/compilable/dtoh_special_enum.d
new file mode 100644
index 00000000000..a7c0a0d3f0a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_special_enum.d
@@ -0,0 +1,90 @@
+/+
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+enum class __c_not_special;
+extern "C" void fn_long(long _param_0);
+
+extern "C" void fn_ulong(unsigned long _param_0);
+
+extern "C" void fn_longlong(long long _param_0);
+
+extern "C" void fn_ulonglong(unsigned long long _param_0);
+
+extern "C" void fn_long_double(long double _param_0);
+
+extern "C" void fn_wchar_t(wchar_t _param_0);
+
+extern "C" void fn_complex_float(_Complex float _param_0);
+
+extern "C" void fn_complex_double(_Complex double _param_0);
+
+extern "C" void fn_complex_real(_Complex long double _param_0);
+
+extern "C" void fn_not_special(__c_not_special _param_0);
+
+---
++/
+
+enum __c_long : int;
+enum __c_ulong : int;
+enum __c_longlong : int;
+enum __c_ulonglong : int;
+enum __c_long_double : int;
+enum __c_wchar_t : int;
+enum __c_complex_float : int;
+enum __c_complex_double : int;
+enum __c_complex_real : int;
+enum __c_not_special : int;
+
+extern(C) void fn_long(__c_long) {}
+extern(C) void fn_ulong(__c_ulong) {}
+extern(C) void fn_longlong(__c_longlong) {}
+extern(C) void fn_ulonglong(__c_ulonglong) {}
+
+extern(C) void fn_long_double(__c_long_double) {}
+
+extern(C) void fn_wchar_t(__c_wchar_t) {}
+
+extern(C) void fn_complex_float(__c_complex_float) {}
+extern(C) void fn_complex_double(__c_complex_double) {}
+extern(C) void fn_complex_real(__c_complex_real) {}
+
+extern(C) void fn_not_special(__c_not_special) {}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_unittest_block.d b/gcc/testsuite/gdc.test/compilable/dtoh_unittest_block.d
new file mode 100644
index 00000000000..ac58d0e803f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_unittest_block.d
@@ -0,0 +1,52 @@
+/*
+REQUIRED_ARGS: -HC -c -o-
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+
+---
+*/
+
+unittest
+{
+ extern (C++) int foo(int x)
+ {
+ return x * 42;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_verbose.d b/gcc/testsuite/gdc.test/compilable/dtoh_verbose.d
new file mode 100644
index 00000000000..505ffdc4998
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtoh_verbose.d
@@ -0,0 +1,172 @@
+/*
+REQUIRED_ARGS: -HC=verbose -o- -Icompilable/extra-files
+PERMUTE_ARGS:
+EXTRA_FILES: extra-files/dtoh_imports.d extra-files/dtoh_imports2.d
+
+TEST_OUTPUT:
+---
+// Automatically generated by Digital Mars D Compiler v$n$
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <math.h>
+
+#ifdef CUSTOM_D_ARRAY_TYPE
+#define _d_dynamicArray CUSTOM_D_ARRAY_TYPE
+#else
+/// Represents a D [] array
+template<typename T>
+struct _d_dynamicArray final
+{
+ size_t length;
+ T *ptr;
+
+ _d_dynamicArray() : length(0), ptr(NULL) { }
+
+ _d_dynamicArray(size_t length_in, T *ptr_in)
+ : length(length_in), ptr(ptr_in) { }
+
+ T& operator[](const size_t idx) {
+ assert(idx < length);
+ return ptr[idx];
+ }
+
+ const T& operator[](const size_t idx) const {
+ assert(idx < length);
+ return ptr[idx];
+ }
+};
+#endif
+
+extern void importFunc();
+
+// Ignored function dtoh_verbose.foo because of linkage
+// Ignored variable dtoh_verbose.i because of linkage
+// Ignored function dtoh_verbose.bar because of linkage
+// Ignored struct dtoh_verbose.S because of linkage
+// Ignored class dtoh_verbose.C because of linkage
+// Ignored function dtoh_verbose.bar because it is extern
+// Ignored variable dtoh_verbose.i1 because of linkage
+// Ignored template dtoh_verbose.templ(T)(T t) because of linkage
+// Ignored alias dtoh_verbose.inst because of linkage
+// Ignored enum dtoh_verbose.arrayOpaque because of its base type
+// Ignored renamed import `myFunc = importFunc` because `using` only supports types
+struct A final
+{
+ // Ignored renamed import `myFunc = importFunc` because `using` only supports types
+ A()
+ {
+ }
+};
+
+struct Hidden final
+{
+ // Ignored function dtoh_verbose.Hidden.hidden because it is private
+ Hidden()
+ {
+ }
+};
+
+// Ignored alias dtoh_verbose.D because of linkage
+class Visitor
+{
+public:
+ virtual void stat();
+ // Ignored dtoh_verbose.Visitor.bar because `using` cannot rename functions in aggregates
+ // Ignored dtoh_verbose.Visitor.unused because free functions cannot be aliased in C++
+};
+
+extern void unused();
+
+// Ignored variable dtoh_verbose.and because its name is a special operator in C++
+template <typename T>
+struct FullImportTmpl final
+{
+ // Ignored `dtoh_imports` because it's inside of a template declaration
+ FullImportTmpl()
+ {
+ }
+};
+
+template <typename T>
+struct SelectiveImportsTmpl final
+{
+ // Ignored `__anonymous` because it's inside of a template declaration
+ SelectiveImportsTmpl()
+ {
+ }
+};
+---
+*/
+
+void foo() {}
+
+extern (D) {
+ int i;
+}
+
+void bar();
+
+struct S {}
+
+class C {}
+
+extern(C++) void bar();
+
+int i1;
+
+void templ(T)(T t) {}
+
+alias inst = templ!int;
+
+extern(C++)
+enum arrayOpaque : int[4];
+
+public import dtoh_imports : myFunc = importFunc;
+
+extern(C++) struct A
+{
+ public import dtoh_imports : myFunc = importFunc;
+}
+
+extern(C++) struct Hidden
+{
+ private void hidden() {}
+}
+
+private {
+ enum PI = 4;
+}
+
+alias D = size_t delegate (size_t x);
+
+extern(C++) T foo(T) = T.init;
+
+extern(C++) class Visitor
+{
+ void stat() {}
+
+ // Ignored because those cannot be represented in C++
+ alias bar = stat;
+ alias unused = .unused;
+}
+
+extern(C++) void unused() {}
+
+extern(C++) __gshared bool and;
+
+extern(C++) struct FullImportTmpl(T)
+{
+ public import dtoh_imports;
+
+}
+
+extern(C++) struct SelectiveImportsTmpl(T)
+{
+ public import dtoh_imports :
+ importFunc,
+ aliasName = ImportsC;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtorfields.d b/gcc/testsuite/gdc.test/compilable/dtorfields.d
new file mode 100644
index 00000000000..1622f3e6bd7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtorfields.d
@@ -0,0 +1,52 @@
+// REQUIRED_ARGS: -preview=dtorfields
+//
+// https://issues.dlang.org/show_bug.cgi?id=21709
+// PERMUTE_ARGS: -betterC
+
+/******************************************
+ * https://issues.dlang.org/show_bug.cgi?id=20934
+ */
+struct HasDtor
+{
+ ~this() {}
+}
+
+struct Disable
+{
+ HasDtor member;
+ this() @disable;
+}
+
+extern(C++) class Extern
+{
+ HasDtor member;
+ this();
+}
+
+/******************************************
+ * https://issues.dlang.org/show_bug.cgi?id=21213
+ */
+class Parent
+{
+ this() nothrow pure @nogc @safe {}
+}
+
+class Child : Parent
+{
+ HasDtor member;
+}
+
+/******************************************
+ * https://issues.dlang.org/show_bug.cgi?id=21225
+ */
+
+struct NothrowConstructed
+{
+ ~this() {}
+}
+
+struct NothrowConstructor
+{
+ NothrowConstructed member;
+ this(int) pure nothrow {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/dtorfields_deprecation.d b/gcc/testsuite/gdc.test/compilable/dtorfields_deprecation.d
new file mode 100644
index 00000000000..83014e38e47
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/dtorfields_deprecation.d
@@ -0,0 +1,49 @@
+/**
+Checks that code still compiles when -preview=dtorfields is enabled by default
+but issues an appropriate deprecation message.
+
+Remove this test when the deprecations period ends, see visit(CtorDeclaration)
+in semantic3.d
+
+TEST_OUTPUT:
+---
+compilable/dtorfields_deprecation.d(30): Deprecation: `dtorfields_deprecation.Pure.this` has stricter attributes than its destructor (`pure`)
+compilable/dtorfields_deprecation.d(30): The destructor will be called if an exception is thrown
+compilable/dtorfields_deprecation.d(30): Either make the constructor `nothrow` or adjust the field destructors
+compilable/dtorfields_deprecation.d(42): Deprecation: `dtorfields_deprecation.NoGC.this` has stricter attributes than its destructor (`@nogc`)
+compilable/dtorfields_deprecation.d(42): The destructor will be called if an exception is thrown
+compilable/dtorfields_deprecation.d(42): Either make the constructor `nothrow` or adjust the field destructors
+compilable/dtorfields_deprecation.d(48): Deprecation: `dtorfields_deprecation.Safe.this` has stricter attributes than its destructor (`@system`)
+compilable/dtorfields_deprecation.d(48): The destructor will be called if an exception is thrown
+compilable/dtorfields_deprecation.d(48): Either make the constructor `nothrow` or adjust the field destructors
+---
+**/
+
+struct HasDtor
+{
+ ~this() {}
+}
+
+struct Pure
+{
+ HasDtor member;
+ this(int) pure {}
+}
+
+struct Nothrow
+{
+ HasDtor member;
+ this(int) nothrow {}
+}
+
+struct NoGC
+{
+ HasDtor member;
+ this(int) @nogc {}
+}
+
+struct Safe
+{
+ HasDtor member;
+ this(int) @safe {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/c6395.d b/gcc/testsuite/gdc.test/compilable/extra-files/c6395.d
index 47e9c9c3523..58c76bc0806 100644
--- a/gcc/testsuite/gdc.test/compilable/extra-files/c6395.d
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/c6395.d
@@ -1,4 +1,4 @@
-// 6395
+// https://issues.dlang.org/show_bug.cgi?id=6395
template map(alias fun) {
auto map(Range)(Range r) {
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/depsOutput9948a.d b/gcc/testsuite/gdc.test/compilable/extra-files/depsOutput9948a.d
deleted file mode 100644
index 3f4e3a3d3b6..00000000000
--- a/gcc/testsuite/gdc.test/compilable/extra-files/depsOutput9948a.d
+++ /dev/null
@@ -1,6 +0,0 @@
-module depsOutput9948a;
-
-void templateFunc(string myImport)()
-{
- mixin(myImport);
-} \ No newline at end of file
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/dtoh_imports.d b/gcc/testsuite/gdc.test/compilable/extra-files/dtoh_imports.d
new file mode 100644
index 00000000000..917ac298929
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/dtoh_imports.d
@@ -0,0 +1,13 @@
+extern(C++):
+
+public import dtoh_imports2 : MyStdcInt = customInt;
+
+class ImportsC {}
+
+void importFunc() {}
+
+private struct HiddenData {}
+
+enum : int {
+ IgnoreErrors
+}
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/dtoh_imports2.d b/gcc/testsuite/gdc.test/compilable/extra-files/dtoh_imports2.d
new file mode 100644
index 00000000000..6ff5d8e15c2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/dtoh_imports2.d
@@ -0,0 +1,3 @@
+extern(C++):
+
+alias customInt = int;
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/emptymain.d b/gcc/testsuite/gdc.test/compilable/extra-files/emptymain.d
new file mode 100644
index 00000000000..5d1cc79a9fd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/emptymain.d
@@ -0,0 +1 @@
+void main() { } \ No newline at end of file
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d
index 5eb6afd1787..209d4a0557e 100644
--- a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d
@@ -1,10 +1,14 @@
+// REQUIRED_ARGS: -ignore
module foo.bar;
import core.vararg;
-import std.stdio;
+
+// Only compilable, declare inline
+void writeln(T...)(T) {}
pragma(lib, "test");
pragma(msg, "Hello World");
+pragma(linkerDirective, "/DEFAULTLIB:test2");
static assert(true, "message");
@@ -43,11 +47,11 @@ out (result)
{
assert(result == 0);
}
-body
+do
{
float f = float.infinity;
int i = cast(int) f;
- writeln((i,1),2);
+ writeln(i,1,2);
writeln(cast(int)float.max);
assert(i == cast(int)float.max);
assert(i == 0x80000000);
@@ -60,7 +64,7 @@ template Foo(T, int V)
{
void foo(...)
{
- static if (is(Object _ : X!TL, alias X, TL...)) {} // Bugzilla 10044
+ static if (is(Object _ : X!TL, alias X, TL...)) {} // https://issues.dlang.org/show_bug.cgi?id=10044
auto x = __traits(hasMember, Object, "noMember");
auto y = is(Object : X!TL, alias X, TL...);
@@ -82,7 +86,7 @@ template Foo(T, int V)
d--;
asm
- { naked ;
+ { naked ;
mov EAX, 3;
}
@@ -157,11 +161,11 @@ template Foo(T, int V)
}
try
- bar(1, 2);
+ bar(1, 2);
catch(Object o)
- x++;
+ x++;
finally
- x--;
+ x--;
Object o;
synchronized (o)
@@ -269,9 +273,6 @@ class Test
pure nothrow @safe @nogc unittest {}
pure nothrow @safe @nogc invariant {}
pure nothrow @safe @nogc invariant (true);
-
- pure nothrow @safe @nogc new (size_t sz) { return null; }
- pure nothrow @safe @nogc delete (void* p) { }
}
template templ( T )
@@ -410,16 +411,16 @@ struct T12
}
-// 6591
-import std.stdio : writeln, F = File;
+// https://issues.dlang.org/show_bug.cgi?id=6591
+import core.stdc.stdio : printf, F = FILE;
void foo6591()()
{
- import std.stdio : writeln, F = File;
+ import core.stdc.stdio : printf, F = FILE;
}
-// 8081
+// https://issues.dlang.org/show_bug.cgi?id=8081
version(unittest) {
pure nothrow unittest {}
pure nothrow unittest {}
@@ -430,7 +431,7 @@ version(unittest) {
}
-// 10334
+// https://issues.dlang.org/show_bug.cgi?id=10334
template Foo10334(T) if (Bar10334!()) {} ///
template Foo10334(T) if (Bar10334!100) {} ///
@@ -459,7 +460,7 @@ mixin Test10334!int a; ///
mixin Test10334!(int,long) b; ///
mixin Test10334!"str" c; ///
-// 12266
+// https://issues.dlang.org/show_bug.cgi?id=12266
auto clamp12266a(T1, T2, T3)(T1 x, T2 min_val, T3 max_val)
{
return 0;
@@ -473,10 +474,10 @@ pure clamp12266b(T1, T2, T3)(T1 x, T2 min_val, T3 max_val)
return 0;
}
-// 13832
+// https://issues.dlang.org/show_bug.cgi?id=13832
alias Dg13832 = ref int delegate();
-// 16590
+// https://issues.dlang.org/show_bug.cgi?id=16590
class TestClass {
int aa;
int b1, b2;
@@ -538,13 +539,50 @@ class Foo2A {
}
-// bugzilla 15676
+// https://issues.dlang.org/show_bug.cgi?id=15676
struct Foo3A(T)
{
@disable this(this);
@disable this();
}
+// return ref, return scope, return ref scope
+ref int foo(return ref int a) @safe
+{
+ return a;
+}
+
+int* foo(return scope int* a) @safe
+{
+ return a;
+}
+
+ref int* foo(scope return ref int* a) @safe
+{
+ return a;
+}
+
+struct SafeS
+{
+@safe:
+ ref SafeS foo() return
+ {
+ return this;
+ }
+
+ SafeS foo2() return scope
+ {
+ return this;
+ }
+
+ ref SafeS foo3() return scope
+ {
+ return this;
+ }
+
+ int* p;
+}
+
void test13x(@(10) int a, @(20) int, @(30) @(40) int[] arr...) {}
enum Test14UDA1;
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header17125.d b/gcc/testsuite/gdc.test/compilable/extra-files/header17125.d
new file mode 100644
index 00000000000..1b979ab3c15
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/header17125.d
@@ -0,0 +1,5 @@
+void func1(real value = 10.35e4L);
+void func2(real value = 520.199e3F);
+void func3(real value = 9.70e5);
+void func4(real value = 1024.5e2F);
+void func5(real value = 41250.2e1L);
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header18365.d b/gcc/testsuite/gdc.test/compilable/extra-files/header18365.d
new file mode 100644
index 00000000000..64d8ad073cf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/header18365.d
@@ -0,0 +1,6 @@
+module foo.bar.ba;
+nothrow pure @nogc @safe package(foo)
+{
+ void foo();
+ nothrow pure @nogc @safe package(foo.bar) void foo2();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header2.d b/gcc/testsuite/gdc.test/compilable/extra-files/header2.d
index f8c52ff1541..7dc4659165d 100644
--- a/gcc/testsuite/gdc.test/compilable/extra-files/header2.d
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/header2.d
@@ -18,7 +18,7 @@ void foo2(const C2 c);
struct Foo3
{
int k;
- ~this() @trusted @disable @nogc { k = 1; }
+ ~this() @trusted @disable @nogc @live { k = 1; }
this(this) { k = 2; }
}
@@ -91,7 +91,7 @@ template templateVariableBar(T) if (is(T == int))
auto flit = 3 / 2.0;
-// 11217
+// https://issues.dlang.org/show_bug.cgi?id=11217
void foo11217()( const int[] arr) {}
void foo11217()(immutable int[] arr) {}
void foo11217()( ref int[] arr) {}
@@ -101,7 +101,7 @@ void foo11217()( scope int[] arr) {}
void foo11217()( in int[] arr) {}
void foo11217()( inout int[] arr) {}
-// 13275
+// https://issues.dlang.org/show_bug.cgi?id=13275
void test13275()
{
if ( auto n = 1) {}
@@ -141,12 +141,69 @@ void test13275()
foreach (shared const(int) e; [1,2]) {}
}
-// 9766
+// https://issues.dlang.org/show_bug.cgi?id=9766
align (1) struct S9766
{
+align {}
align (true ? 2 : 3):
int var1;
align:
int var2;
}
+
+align(2) struct S12200_1
+{
+align:
+}
+
+align(2) struct S12200_2
+{
+align(1):
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=16140
+void gun()()
+{
+ int[] res;
+ while (auto va = fun()) {} // expression expected, not 'auto'
+
+ while (true)
+ if (auto va = fun()) {}
+ else break;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=16649
+void leFoo()()
+{
+ sign = a == 2 ? false : (y < 0) ^ sign;
+ sign = a == 2 ? false : sign ^ (y < 0);
+ sign = 2 + 3 | 7 + 5;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17371
+interface LeInterface
+{}
+class LeClass
+{
+ this()
+ {
+ auto foo = new class () LeInterface {};
+ }
+}
+const levar = new class LeClass, LeInterface {};
+
+// https://issues.dlang.org/show_bug.cgi?id=20074
+class CC
+{
+ void fun()() @safe
+ {
+ () @trusted pure
+ {
+ } ();
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17663
+private:
+public struct Export {}
diff --git a/gcc/testsuite/gdc.test/compilable/fail137.d b/gcc/testsuite/gdc.test/compilable/fail137.d
new file mode 100644
index 00000000000..c6876d16e9f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/fail137.d
@@ -0,0 +1,26 @@
+// https://issues.dlang.org/show_bug.cgi?id=751
+// Compiler segfault on template expansion
+
+extern(C) int printf(const char*, ...);
+
+template TypeTuple( TList... )
+{
+ alias TList TypeTuple;
+}
+
+template IndexOf( T, TList... )
+{
+ static if( TList.length == 0 )
+ const size_t IndexOf = 1;
+ else static if( is( T == typeof( TList[0] ) ) )
+ const size_t IndexOf = 0;
+ else
+ const size_t IndexOf = 1 + IndexOf!( T, (TList[1 .. $]) );
+}
+
+void main()
+{
+ TypeTuple!(int, long) T;
+ printf( "%u\n", cast(uint)IndexOf!(long, T) );
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/fieldwise.d b/gcc/testsuite/gdc.test/compilable/fieldwise.d
new file mode 100644
index 00000000000..dfa76b4ab3f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/fieldwise.d
@@ -0,0 +1,11 @@
+// REQUIRED_ARGS: -preview=dip1000 -preview=fieldwise
+// EXTRA_FILES: imports/impfieldwise.d
+
+import imports.impfieldwise;
+
+@safe:
+
+bool test(S s, S t)
+{
+ return s == t; // comparison can access fields for ==
+}
diff --git a/gcc/testsuite/gdc.test/compilable/filefullpath_18911.d b/gcc/testsuite/gdc.test/compilable/filefullpath_18911.d
new file mode 100644
index 00000000000..3d88ac24342
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/filefullpath_18911.d
@@ -0,0 +1,9 @@
+// REQUIRED_ARGS: -Icompilable/imports -c -o-
+// EXTRA_FILES: imports/a18911.d
+
+import a18911;
+
+enum THIS_FILE = __FILE_FULL_PATH__;
+enum suffix_this = "filefullpath_18911.d";
+
+static assert(THIS_FILE[0..$-suffix_this.length-1] == A_FILE[0..$-suffix_a.length-1]);
diff --git a/gcc/testsuite/gdc.test/compilable/fix13165.d b/gcc/testsuite/gdc.test/compilable/fix13165.d
new file mode 100644
index 00000000000..a26cfd6a965
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/fix13165.d
@@ -0,0 +1,12 @@
+/* REQUIRED_ARGS: -w -profile
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=13165
+
+void main()
+{
+ int i;
+ if (!i)
+ throw new Exception("Error");
+ assert(0);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/fix17145.d b/gcc/testsuite/gdc.test/compilable/fix17145.d
new file mode 100644
index 00000000000..ab34c71dc45
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/fix17145.d
@@ -0,0 +1,13 @@
+// https://issues.dlang.org/show_bug.cgi?id=17145
+
+auto tuple(T...)(T t) {
+ struct Result {
+ T expand;
+ }
+ return Result(t);
+}
+
+void baz()
+{
+ enum zoo = tuple(1, 2).expand; // Error: value of __tup1847 is not known at compile time
+}
diff --git a/gcc/testsuite/gdc.test/compilable/fix17349.d b/gcc/testsuite/gdc.test/compilable/fix17349.d
deleted file mode 100644
index 2222c355927..00000000000
--- a/gcc/testsuite/gdc.test/compilable/fix17349.d
+++ /dev/null
@@ -1,40 +0,0 @@
-/* REQUIRED_ARGS: -dw
- * PERMUTE_ARGS:
- * TEST_OUTPUT:
----
-compilable/fix17349.d(37): Deprecation: cannot implicitly override base class method `fix17349.E.foo` with `fix17349.F.foo`; add `override` attribute
----
- */
-
-// https://issues.dlang.org/show_bug.cgi?id=17349
-
-struct S { }
-
-class C {
- void bar();
- void foo(void* p);
- void abc(Object);
- void def(S);
-}
-
-class D : C {
- override void bar() const;
- override void foo(const void*);
- override void abc(const Object);
- override void def(const S);
-}
-
-alias fp_t = void function(int*);
-@safe void abc(const int*);
-fp_t fp = &abc;
-
-
-class E {
- void foo(void*);
-}
-
-class F : E {
- void foo(const void*);
-}
-
-
diff --git a/gcc/testsuite/gdc.test/compilable/fix20416.d b/gcc/testsuite/gdc.test/compilable/fix20416.d
new file mode 100644
index 00000000000..19ad74df1cf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/fix20416.d
@@ -0,0 +1,36 @@
+/* REQUIRED_ARGS: -preview=dip1000
+*/
+
+/********************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=20416
+
+alias P = int*;
+
+ref P foo(return ref P);
+
+P bar()
+{
+ P result;
+ return foo(result);
+}
+
+
+/********************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=20416
+
+
+struct S
+{
+ string x;
+ ref S foo() return;
+}
+
+
+S bar2()
+{
+ S result;
+ return result.foo();
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/fix21647.d b/gcc/testsuite/gdc.test/compilable/fix21647.d
index c129fa013a1..c1e1c482c3c 100644
--- a/gcc/testsuite/gdc.test/compilable/fix21647.d
+++ b/gcc/testsuite/gdc.test/compilable/fix21647.d
@@ -1,5 +1,5 @@
/*
-REQUIRED_ARGS:
+REQUIRED_ARGS: -preview=rvaluerefparam
TEST_OUTPUT:
---
cast(void)0
@@ -28,3 +28,11 @@ void test3() { pragma(msg, V); }
pragma(msg, foo());
pragma(msg, main());
pragma(msg, V);
+
+/*************************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8255
+
+struct G {}
+struct F(T) { void f(ref T) {} }
+pragma(msg, F!G().f(G.init));
+
diff --git a/gcc/testsuite/gdc.test/compilable/fix21684.d b/gcc/testsuite/gdc.test/compilable/fix21684.d
new file mode 100644
index 00000000000..116f1e467d8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/fix21684.d
@@ -0,0 +1,7 @@
+// https://issues.dlang.org/show_bug.cgi?id=21684
+
+
+struct S
+{
+ int[100_000] a;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/fix22180.d b/gcc/testsuite/gdc.test/compilable/fix22180.d
new file mode 100644
index 00000000000..a4c32e0dc91
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/fix22180.d
@@ -0,0 +1,5 @@
+// https://issues.dlang.org/show_bug.cgi?id=22180
+
+align(8) { int x; }
+//pragma(msg, x.alignof);
+static assert(x.alignof == 8);
diff --git a/gcc/testsuite/gdc.test/compilable/forward1.d b/gcc/testsuite/gdc.test/compilable/forward1.d
index 7417b037bed..7c5f76df4b6 100644
--- a/gcc/testsuite/gdc.test/compilable/forward1.d
+++ b/gcc/testsuite/gdc.test/compilable/forward1.d
@@ -1,6 +1,7 @@
// REQUIRED_ARGS: -g
-// 104. fails only with -g
+// https://issues.dlang.org/show_bug.cgi?id=104
+// fails only with -g
Foofunc f;
alias int Foo;
diff --git a/gcc/testsuite/gdc.test/compilable/future.d b/gcc/testsuite/gdc.test/compilable/future.d
index 89a13965d1b..30439847ef4 100644
--- a/gcc/testsuite/gdc.test/compilable/future.d
+++ b/gcc/testsuite/gdc.test/compilable/future.d
@@ -1,7 +1,7 @@
/* PERMUTE_ARGS:
* TEST_OUTPUT:
---
-compilable/future.d(15): Deprecation: @future base class method future.A.msg is being overridden by future.B.msg; rename the latter
+compilable/future.d(15): Deprecation: `@__future` base class method `future.A.msg` is being overridden by `future.B.msg`; rename the latter
---
*/
diff --git a/gcc/testsuite/gdc.test/compilable/futurexf.d b/gcc/testsuite/gdc.test/compilable/futurexf.d
index c550b68d30f..0a2df69281e 100644
--- a/gcc/testsuite/gdc.test/compilable/futurexf.d
+++ b/gcc/testsuite/gdc.test/compilable/futurexf.d
@@ -1,5 +1,5 @@
/* PERMUTE_ARGS:
- REQUIRED_ARGS: -Xffuture.json
+ REQUIRED_ARGS: -Xf${RESULTS_DIR}/compilable/futurexf.json
*/
class A
diff --git a/gcc/testsuite/gdc.test/compilable/fwdref21063.d b/gcc/testsuite/gdc.test/compilable/fwdref21063.d
new file mode 100644
index 00000000000..2d0ca7fd774
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/fwdref21063.d
@@ -0,0 +1,14 @@
+template Info(T, int line) {
+ static assert(__traits(getLinkage, T) == "C++");
+ alias Info = void;
+}
+
+// Forward reference
+static assert(__traits(getLinkage, Klass) == "C++");
+alias info1 = Info!(Klass, __LINE__);
+
+extern (C++) class Klass { void derp() {} }
+
+// Backward reference
+static assert(__traits(getLinkage, Klass) == "C++");
+alias info2 = Info!(Klass, __LINE__);
diff --git a/gcc/testsuite/gdc.test/compilable/header18364.d b/gcc/testsuite/gdc.test/compilable/header18364.d
new file mode 100644
index 00000000000..c7e1e67c7ae
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/header18364.d
@@ -0,0 +1,24 @@
+/*
+REQUIRED_ARGS: -o- -Hf${RESULTS_DIR}/compilable/header18364.di
+PERMUTE_ARGS:
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/header18364.di
+
+TEST_OUTPUT:
+---
+=== ${RESULTS_DIR}/compilable/header18364.di
+// D import file generated from 'compilable/header18364.d'
+module foo.bar.ba;
+nothrow pure @nogc @safe package(foo)
+{
+ void foo();
+ nothrow pure @nogc @safe package(foo.bar) void foo2();
+}
+---
+*/
+
+module foo.bar.ba;
+@safe pure nothrow @nogc package(foo):
+void foo();
+
+@safe pure nothrow @nogc package(foo.bar):
+void foo2();
diff --git a/gcc/testsuite/gdc.test/compilable/header18365.d b/gcc/testsuite/gdc.test/compilable/header18365.d
new file mode 100644
index 00000000000..7e51fb26cc7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/header18365.d
@@ -0,0 +1,34 @@
+/*
+REQUIRED_ARGS: -c -o- -Hf${RESULTS_DIR}/compilable/header18365.di
+PERMUTE_ARGS:
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/header18365.di
+
+TEST_OUTPUT:
+---
+=== ${RESULTS_DIR}/compilable/header18365.di
+// D import file generated from 'compilable/header18365.d'
+struct FullCaseEntry
+{
+ dchar[3] seq;
+ ubyte n;
+ ubyte size;
+ ubyte entry_len;
+ auto const pure nothrow @nogc @property @trusted value() return
+ {
+ return seq[0..entry_len];
+ }
+}
+---
+*/
+
+struct FullCaseEntry
+{
+ dchar[3] seq;
+ ubyte n, size;
+ ubyte entry_len;
+
+ @property auto value() const @trusted pure nothrow @nogc return
+ {
+ return seq[0 .. entry_len];
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/ice11054.d b/gcc/testsuite/gdc.test/compilable/ice11054.d
index 1b8c63bdc1a..5e35d5cd715 100644
--- a/gcc/testsuite/gdc.test/compilable/ice11054.d
+++ b/gcc/testsuite/gdc.test/compilable/ice11054.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/ice11054a.d
import imports.ice11054a;
static assert(!__traits(compiles, tuple()));
diff --git a/gcc/testsuite/gdc.test/compilable/ice11300.d b/gcc/testsuite/gdc.test/compilable/ice11300.d
index 473a1c2cba3..6fccb158b8a 100644
--- a/gcc/testsuite/gdc.test/compilable/ice11300.d
+++ b/gcc/testsuite/gdc.test/compilable/ice11300.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/ice11300a.d
module ice11300;
import imports.ice11300a;
enum value = 42;
diff --git a/gcc/testsuite/gdc.test/compilable/ice11925.d b/gcc/testsuite/gdc.test/compilable/ice11925.d
deleted file mode 100644
index 630af429a47..00000000000
--- a/gcc/testsuite/gdc.test/compilable/ice11925.d
+++ /dev/null
@@ -1,38 +0,0 @@
-void test11925a()
-{
- try
- {
- try
- {
- L1: {}
- }
- finally
- {
- }
- }
- finally
- {
- }
- goto L1;
-}
-
-void test11925b()
-{
- switch (1)
- {
- case 1:
- goto L1;
- break;
-
- default:
- break;
- }
-
- try
- {
- L1: { }
- }
- finally
- {
- }
-}
diff --git a/gcc/testsuite/gdc.test/compilable/ice13403.d b/gcc/testsuite/gdc.test/compilable/ice13403.d
index bb7c4e5955e..e45289614f6 100644
--- a/gcc/testsuite/gdc.test/compilable/ice13403.d
+++ b/gcc/testsuite/gdc.test/compilable/ice13403.d
@@ -1,5 +1,6 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS: -o-
+// EXTRA_FILES: imports/ice13403a.d
import imports.ice13403a;
void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/ice13819.d b/gcc/testsuite/gdc.test/compilable/ice13819.d
new file mode 100644
index 00000000000..88e93c60f5c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ice13819.d
@@ -0,0 +1,7 @@
+// REQUIRED_ARGS: -O
+auto f (double a)
+{
+ return a % 2 != 0;
+}
+
+void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/ice1524.d b/gcc/testsuite/gdc.test/compilable/ice1524.d
index ffcf81bcbc3..6e593668ef7 100644
--- a/gcc/testsuite/gdc.test/compilable/ice1524.d
+++ b/gcc/testsuite/gdc.test/compilable/ice1524.d
@@ -1,4 +1,5 @@
-// Issue 1524 - ICE(constfold.c) on using "is" with strings in CTFE
+// https://issues.dlang.org/show_bug.cgi?id=1524
+// ICE(constfold.c) on using "is" with strings in CTFE
/* 1524 PATCH Assertion failure: '0' on line 863 in file 'constfold.c'
constfold.c
diff --git a/gcc/testsuite/gdc.test/compilable/ice20044.d b/gcc/testsuite/gdc.test/compilable/ice20044.d
new file mode 100644
index 00000000000..f85342c71fb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ice20044.d
@@ -0,0 +1,10 @@
+struct Algebraic(T...)
+{
+ T t;
+}
+
+struct This;
+
+struct While(T) { T[] body; }
+
+alias Stmt = Algebraic!(While!(This));
diff --git a/gcc/testsuite/gdc.test/compilable/ice20415.d b/gcc/testsuite/gdc.test/compilable/ice20415.d
new file mode 100644
index 00000000000..5eaafec342e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ice20415.d
@@ -0,0 +1,16 @@
+// REQUIRED_ARGS: -O
+void t()
+{
+ auto a = A(false ? B().p : null);
+}
+
+struct A
+{
+ void* p;
+}
+
+struct B
+{
+ void* p;
+ ~this() {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/ice6538.d b/gcc/testsuite/gdc.test/compilable/ice6538.d
index 12d1ed0fa91..2329f1648cf 100644
--- a/gcc/testsuite/gdc.test/compilable/ice6538.d
+++ b/gcc/testsuite/gdc.test/compilable/ice6538.d
@@ -1,7 +1,7 @@
/**************************************/
-// 6538
+// https://issues.dlang.org/show_bug.cgi?id=6538
template allSatisfy(alias F, T...) { enum bool allSatisfy = true; }
template isIntegral(T) { enum bool isIntegral = true; }
@@ -23,7 +23,7 @@ void test6538b()
}
/**************************************/
-// 9361
+// https://issues.dlang.org/show_bug.cgi?id=9361
template Sym(alias A)
{
diff --git a/gcc/testsuite/gdc.test/compilable/ice854.d b/gcc/testsuite/gdc.test/compilable/ice854.d
index 3ede52eeb59..13a5aff8cb4 100644
--- a/gcc/testsuite/gdc.test/compilable/ice854.d
+++ b/gcc/testsuite/gdc.test/compilable/ice854.d
@@ -1,4 +1,5 @@
-// Issue 854 - TypeTuple in anonymous delegate causes ice in glue.c
+// https://issues.dlang.org/show_bug.cgi?id=854
+// TypeTuple in anonymous delegate causes ice in glue.c
/* 854 VOTE PATCH (=2863, =2251?) Assertion failure: '0' on line 935 in file 'glue.c'
I haven't checked this patch myself.
diff --git a/gcc/testsuite/gdc.test/compilable/implicitconv.d b/gcc/testsuite/gdc.test/compilable/implicitconv.d
new file mode 100644
index 00000000000..6fc7abf5700
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/implicitconv.d
@@ -0,0 +1,33 @@
+enum __c_wchar_t : wchar;
+
+alias wchar_t = __c_wchar_t;
+
+immutable(wchar_t)[] a = "somestring";
+const(wchar_t)[] b = "somestring";
+immutable(wchar_t)* c = "somestring";
+const(wchar_t)* d = "somestring";
+
+string foo = "foo";
+
+static assert(!__traits(compiles, { immutable(wchar_t)[] bar = foo; } ));
+static assert(!__traits(compiles, { const(wchar_t)[] bar = foo; } ));
+static assert(!__traits(compiles, { immutable(wchar_t)* bar = foo; } ));
+static assert(!__traits(compiles, { const(wchar_t)* bar = foo; } ));
+
+// https://issues.dlang.org/show_bug.cgi?id=17141
+static assert(is(typeof(true ? char.init : char.init) == char));
+static assert(is(typeof(true ? char.init : wchar.init) == dchar));
+static assert(is(typeof(true ? char.init : dchar.init) == dchar));
+static assert(is(typeof(true ? wchar.init : wchar.init) == wchar));
+static assert(is(typeof(true ? wchar.init : dchar.init) == dchar));
+static assert(is(typeof(true ? dchar.init : dchar.init) == dchar));
+
+enum cenum : char { a }
+enum wenum : wchar{ b }
+enum denum : dchar{ c }
+
+static assert(is(typeof(true ? char.init : cenum.init) == char));
+static assert(is(typeof(true ? wchar.init : cenum.init) == dchar));
+static assert(is(typeof(true ? char.init : wenum.init) == dchar));
+static assert(is(typeof(true ? dchar.init : wenum.init) == dchar));
+static assert(is(typeof(true ? cenum.init : wenum.init) == dchar));
diff --git a/gcc/testsuite/gdc.test/compilable/imports/a12511.d b/gcc/testsuite/gdc.test/compilable/imports/a12511.d
new file mode 100644
index 00000000000..262bb61859c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/a12511.d
@@ -0,0 +1,7 @@
+module a12511;
+
+public class A
+{
+ private static void foo() {}
+ public static void foo(int) {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/a18911.d b/gcc/testsuite/gdc.test/compilable/imports/a18911.d
new file mode 100644
index 00000000000..81b46e0ba32
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/a18911.d
@@ -0,0 +1,2 @@
+enum A_FILE = __FILE_FULL_PATH__;
+enum suffix_a = "imports/a18911.d";
diff --git a/gcc/testsuite/gdc.test/compilable/imports/cstuff3.c b/gcc/testsuite/gdc.test/compilable/imports/cstuff3.c
new file mode 100644
index 00000000000..f6aaf3b2a4a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/cstuff3.c
@@ -0,0 +1,6 @@
+// check bugs in importing C files
+
+int squared(int a)
+{
+ return a * a;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/g313.d b/gcc/testsuite/gdc.test/compilable/imports/g313.d
index 92e703a10a9..0a9d26b1408 100644
--- a/gcc/testsuite/gdc.test/compilable/imports/g313.d
+++ b/gcc/testsuite/gdc.test/compilable/imports/g313.d
@@ -1,6 +1,6 @@
module imports.g313;
-// adds public package imports (see Bugzilla 15900)
+// adds public package imports (see https://issues.dlang.org/show_bug.cgi?id=15900)
public import imports.g313public;
// same w/ deferred semantics
static if (true)
diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp16088.d b/gcc/testsuite/gdc.test/compilable/imports/imp16088.d
new file mode 100644
index 00000000000..5c5bc5edf58
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/imp16088.d
@@ -0,0 +1 @@
+module imports.imp16088;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp21832.d b/gcc/testsuite/gdc.test/compilable/imports/imp21832.d
new file mode 100644
index 00000000000..ee4a1d6296d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/imp21832.d
@@ -0,0 +1,24 @@
+module imports.imp21832;
+static if(1)
+{
+ int fun(int a)
+ {
+ return a;
+ }
+ int tpl()(int a)
+ {
+ return a;
+ }
+}
+
+deprecated
+{
+ int fun(char a)
+ {
+ return a;
+ }
+ int tpl()(char a)
+ {
+ return a;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp22122.d b/gcc/testsuite/gdc.test/compilable/imports/imp22122.d
new file mode 100644
index 00000000000..b29bae0bb6f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/imp22122.d
@@ -0,0 +1,5 @@
+module imports.imp22122;
+
+package struct Imp22122
+{
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/impfieldwise.d b/gcc/testsuite/gdc.test/compilable/imports/impfieldwise.d
new file mode 100644
index 00000000000..df9b987aa93
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/impfieldwise.d
@@ -0,0 +1,8 @@
+@safe:
+
+struct S
+{
+ private:
+ @safe:
+ int a, b;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/pkg11847/mod11847.d b/gcc/testsuite/gdc.test/compilable/imports/pkg11847/mod11847.d
new file mode 100644
index 00000000000..5f8ef761844
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/pkg11847/mod11847.d
@@ -0,0 +1,3 @@
+module pkg11847.mod11847;
+
+int func() { return 2; }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/pkg11847/package.d b/gcc/testsuite/gdc.test/compilable/imports/pkg11847/package.d
new file mode 100644
index 00000000000..0a6c1ecfc34
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/pkg11847/package.d
@@ -0,0 +1,3 @@
+module pkg11847;
+
+int func() { return 1; }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/protectionimp.d b/gcc/testsuite/gdc.test/compilable/imports/protectionimp.d
index 6c99cf325d9..2f3c71194d0 100644
--- a/gcc/testsuite/gdc.test/compilable/imports/protectionimp.d
+++ b/gcc/testsuite/gdc.test/compilable/imports/protectionimp.d
@@ -22,7 +22,7 @@ private alias privC privA;
public mixin template publMT() {}
/***************************************************/
-// 14169
+// https://issues.dlang.org/show_bug.cgi?id=14169
template GetName14169(TemplateParam)
{
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test11563std_traits.d b/gcc/testsuite/gdc.test/compilable/imports/test11563std_traits.d
index 983fd11be4f..0ee4643ebe8 100644
--- a/gcc/testsuite/gdc.test/compilable/imports/test11563std_traits.d
+++ b/gcc/testsuite/gdc.test/compilable/imports/test11563std_traits.d
@@ -16,7 +16,7 @@ template moduleName(alias T)
}
else
{
- pragma(msg, "--error--");
+ enum moduleName = "--error--";
}
}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test13582.d b/gcc/testsuite/gdc.test/compilable/imports/test13582.d
new file mode 100644
index 00000000000..493dbc313a0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test13582.d
@@ -0,0 +1 @@
+deprecated module imports.test13582;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test16709a.d b/gcc/testsuite/gdc.test/compilable/imports/test16709a.d
new file mode 100644
index 00000000000..99d1fef556a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test16709a.d
@@ -0,0 +1,2 @@
+module test16709a;
+public import imports.test16709b:to;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test16709b.d b/gcc/testsuite/gdc.test/compilable/imports/test16709b.d
new file mode 100644
index 00000000000..8fa00abd0cc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test16709b.d
@@ -0,0 +1,5 @@
+module test16709b;
+public:
+
+import imports.test16709c;
+import imports.test16709d;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test16709c.d b/gcc/testsuite/gdc.test/compilable/imports/test16709c.d
new file mode 100644
index 00000000000..1b4516350f0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test16709c.d
@@ -0,0 +1,2 @@
+void to(string units, D)(D td) { }
+
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test16709d.d b/gcc/testsuite/gdc.test/compilable/imports/test16709d.d
new file mode 100644
index 00000000000..9f928af34f6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test16709d.d
@@ -0,0 +1,2 @@
+void to(T, D)(D td) { }
+
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test17441foo/bar.d b/gcc/testsuite/gdc.test/compilable/imports/test17441foo/bar.d
new file mode 100644
index 00000000000..5b46b9e72a6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test17441foo/bar.d
@@ -0,0 +1 @@
+module imports.test17441foo.bar;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test17441foo/package.d b/gcc/testsuite/gdc.test/compilable/imports/test17441foo/package.d
new file mode 100644
index 00000000000..d795f450991
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test17441foo/package.d
@@ -0,0 +1 @@
+module imports.test17441foo;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test17541_2.d b/gcc/testsuite/gdc.test/compilable/imports/test17541_2.d
new file mode 100644
index 00000000000..4554bf2c6b9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test17541_2.d
@@ -0,0 +1,20 @@
+module two;
+
+import one;
+
+struct ET(bool a)
+{
+ enum e = BB.MAX_NUM_FIBERS;
+}
+
+alias Event = ET!false;
+
+struct TWOR(size_t M)
+{
+ Event e;
+
+ void open()
+ {
+ bb.foo();
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test17541_3.d b/gcc/testsuite/gdc.test/compilable/imports/test17541_3.d
new file mode 100644
index 00000000000..5ddec9eee14
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test17541_3.d
@@ -0,0 +1,15 @@
+module three;
+
+void aaa() @nogc
+{
+
+}
+
+struct TT(T)
+{
+ void insertabcdefg(T) // @nogc <-- deduction problem
+ {
+ //static assert(insertabcdefg.mangleof == "_D5three__T2TTTiZQg13insertabcdefgMFiZv");
+ aaa();
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test18651/algorithm.d b/gcc/testsuite/gdc.test/compilable/imports/test18651/algorithm.d
new file mode 100644
index 00000000000..970f072fa7f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test18651/algorithm.d
@@ -0,0 +1,14 @@
+struct SortedRange(Range)
+{
+ Range _input;
+}
+
+auto sort(Range)(Range)
+{
+ return SortedRange!Range();
+}
+
+auto uniq(Range)(Range)
+{
+ return SortedRange!Range();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test18651/b.d b/gcc/testsuite/gdc.test/compilable/imports/test18651/b.d
new file mode 100644
index 00000000000..8ca3ca24a02
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test18651/b.d
@@ -0,0 +1 @@
+import imports.test18651.c;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test18651/c.d b/gcc/testsuite/gdc.test/compilable/imports/test18651/c.d
new file mode 100644
index 00000000000..93b1d95798b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test18651/c.d
@@ -0,0 +1,4 @@
+module imports.test18651.c;
+import imports.test18651.algorithm;
+
+const var = sort(string[].init);
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test18651/datetime.d b/gcc/testsuite/gdc.test/compilable/imports/test18651/datetime.d
new file mode 100644
index 00000000000..43d5d66f47b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test18651/datetime.d
@@ -0,0 +1,7 @@
+void parseTZConversions()
+{
+ import imports.test18651.algorithm;
+
+ string[] value;
+ value.sort.uniq;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test18771a.d b/gcc/testsuite/gdc.test/compilable/imports/test18771a.d
new file mode 100644
index 00000000000..7527c311538
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test18771a.d
@@ -0,0 +1,3 @@
+module imports.test18771a;
+
+void foo(int) {}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test18771b.d b/gcc/testsuite/gdc.test/compilable/imports/test18771b.d
new file mode 100644
index 00000000000..f48dc5f1d57
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test18771b.d
@@ -0,0 +1,3 @@
+module imports.test18771b;
+
+void foo(string) {}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test18771c.d b/gcc/testsuite/gdc.test/compilable/imports/test18771c.d
new file mode 100644
index 00000000000..dc986d2f11f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test18771c.d
@@ -0,0 +1,4 @@
+module imports.test18771c;
+
+import imports.test18771a, imports.test18771b;
+alias fooC = foo;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test18771d.d b/gcc/testsuite/gdc.test/compilable/imports/test18771d.d
new file mode 100644
index 00000000000..fbf20346456
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test18771d.d
@@ -0,0 +1,4 @@
+module imports.test18771d;
+
+import imports.test18771b, imports.test18771a;
+alias fooD = foo;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19187.d b/gcc/testsuite/gdc.test/compilable/imports/test19187.d
new file mode 100644
index 00000000000..3bf435ec028
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19187.d
@@ -0,0 +1,4 @@
+module imports.test19187;
+void test()() { }
+alias foo = test;
+alias foo = test;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19344.d b/gcc/testsuite/gdc.test/compilable/imports/test19344.d
new file mode 100644
index 00000000000..437968aa428
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19344.d
@@ -0,0 +1,6 @@
+module imports.test19344;
+
+template getUDAs(alias symbol, alias attribute)
+{
+ alias getUDAs = __traits(getAttributes, symbol);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19656a.d b/gcc/testsuite/gdc.test/compilable/imports/test19656a.d
new file mode 100644
index 00000000000..14642eaaf40
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19656a.d
@@ -0,0 +1,3 @@
+import test19656;
+
+class Corge: Foo { }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19656b.d b/gcc/testsuite/gdc.test/compilable/imports/test19656b.d
new file mode 100644
index 00000000000..ffcf17a648d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19656b.d
@@ -0,0 +1,13 @@
+import test19656;
+
+class Bar { }
+
+class Qux(T): Foo
+{
+ override void thunk() { }
+}
+
+class Fred
+{
+ Qux!Bar _q;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19656c.d b/gcc/testsuite/gdc.test/compilable/imports/test19656c.d
new file mode 100644
index 00000000000..e4ccfee0d0c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19656c.d
@@ -0,0 +1,3 @@
+import imports.test19656b;
+
+class Thud { }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19657b.d b/gcc/testsuite/gdc.test/compilable/imports/test19657b.d
new file mode 100644
index 00000000000..406cf6df7e1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19657b.d
@@ -0,0 +1,6 @@
+import test19657g;
+import test19657a;
+import test19657e;
+class Frop: Seq {
+ override bool func(Foo rhs, Bar bee) { return false; }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19657c.d b/gcc/testsuite/gdc.test/compilable/imports/test19657c.d
new file mode 100644
index 00000000000..e715ef01dda
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19657c.d
@@ -0,0 +1,2 @@
+import test19657a;
+class Pol: Foo {}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19657d.d b/gcc/testsuite/gdc.test/compilable/imports/test19657d.d
new file mode 100644
index 00000000000..59c9a5413de
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19657d.d
@@ -0,0 +1,2 @@
+import test19657a;
+class Trump: Foo {}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19657e.d b/gcc/testsuite/gdc.test/compilable/imports/test19657e.d
new file mode 100644
index 00000000000..9707b2b90e1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19657e.d
@@ -0,0 +1,2 @@
+import test19657f;
+class Bar { }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19657f.d b/gcc/testsuite/gdc.test/compilable/imports/test19657f.d
new file mode 100644
index 00000000000..b2910188812
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19657f.d
@@ -0,0 +1,3 @@
+class Baz {
+ import test19657d;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19657g.d b/gcc/testsuite/gdc.test/compilable/imports/test19657g.d
new file mode 100644
index 00000000000..d641b201b21
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19657g.d
@@ -0,0 +1,2 @@
+import test19657d;
+class Seq: Trump {}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19746a.d b/gcc/testsuite/gdc.test/compilable/imports/test19746a.d
new file mode 100644
index 00000000000..d0c6a8f989e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19746a.d
@@ -0,0 +1,2 @@
+import test19746;
+class Bar: Foo { }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19746b.d b/gcc/testsuite/gdc.test/compilable/imports/test19746b.d
new file mode 100644
index 00000000000..1ceacb45f73
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19746b.d
@@ -0,0 +1,2 @@
+import test19746d;
+class Frop { }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19746c.d b/gcc/testsuite/gdc.test/compilable/imports/test19746c.d
new file mode 100644
index 00000000000..a90b048bec4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19746c.d
@@ -0,0 +1,2 @@
+import test19746a;
+class Qux: Bar { }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19746d.d b/gcc/testsuite/gdc.test/compilable/imports/test19746d.d
new file mode 100644
index 00000000000..36a3f82d356
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19746d.d
@@ -0,0 +1,10 @@
+import test19746;
+class Baz(T): Foo { }
+class Dap(T): Baz!T
+{
+ override void thunk() {}
+}
+class Zoo
+{
+ Dap!int _dap;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19750a.d b/gcc/testsuite/gdc.test/compilable/imports/test19750a.d
new file mode 100644
index 00000000000..6677e2a276d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19750a.d
@@ -0,0 +1,2 @@
+import test19750c;
+class Bar {}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19750b.d b/gcc/testsuite/gdc.test/compilable/imports/test19750b.d
new file mode 100644
index 00000000000..f0d8f9095e9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19750b.d
@@ -0,0 +1,2 @@
+import test19750d;
+class Frop {}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19750c.d b/gcc/testsuite/gdc.test/compilable/imports/test19750c.d
new file mode 100644
index 00000000000..8de5797884e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19750c.d
@@ -0,0 +1,4 @@
+import test19750d;
+class Qux: Thud {
+ override void thunk() {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test19750d.d b/gcc/testsuite/gdc.test/compilable/imports/test19750d.d
new file mode 100644
index 00000000000..06cf58b9966
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test19750d.d
@@ -0,0 +1,6 @@
+import test19750;
+class Dap(T) {}
+class Thud: Foo {
+ Dap!int _dap;
+ void thunk() { }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test21227/..foo/a.txt b/gcc/testsuite/gdc.test/compilable/imports/test21227/..foo/a.txt
new file mode 100644
index 00000000000..ce013625030
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test21227/..foo/a.txt
@@ -0,0 +1 @@
+hello
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test21227/a..b.txt b/gcc/testsuite/gdc.test/compilable/imports/test21227/a..b.txt
new file mode 100644
index 00000000000..ce013625030
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test21227/a..b.txt
@@ -0,0 +1 @@
+hello
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test21227/a.txt b/gcc/testsuite/gdc.test/compilable/imports/test21227/a.txt
new file mode 100644
index 00000000000..ce013625030
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test21227/a.txt
@@ -0,0 +1 @@
+hello
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test21464a.d b/gcc/testsuite/gdc.test/compilable/imports/test21464a.d
new file mode 100644
index 00000000000..e3468f8bf28
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/test21464a.d
@@ -0,0 +1,4 @@
+struct Mallocator
+{
+ static shared Mallocator instance;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/test63a.d b/gcc/testsuite/gdc.test/compilable/imports/test63a.d
index a8edbd8ae0f..d53b034a960 100644
--- a/gcc/testsuite/gdc.test/compilable/imports/test63a.d
+++ b/gcc/testsuite/gdc.test/compilable/imports/test63a.d
@@ -4,7 +4,7 @@ private import test63;
struct s {
- char a[ SIZE ];
+ char[SIZE] a;
}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/testcontracts.d b/gcc/testsuite/gdc.test/compilable/imports/testcontracts.d
index f7414cefbee..5d309650720 100644
--- a/gcc/testsuite/gdc.test/compilable/imports/testcontracts.d
+++ b/gcc/testsuite/gdc.test/compilable/imports/testcontracts.d
@@ -11,7 +11,7 @@ class Base3602
assert(x > 0);
assert(y > 0);
}
- body
+ do
{
}
}
@@ -25,7 +25,7 @@ class Base5230
out (res)
{
}
- body
+ do
{
return 42;
}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/testlambda1.d b/gcc/testsuite/gdc.test/compilable/imports/testlambda1.d
new file mode 100644
index 00000000000..c0a9696337d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/testlambda1.d
@@ -0,0 +1,3 @@
+module imports.testlambda1;
+
+int bar() { return 7; }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/testlambda2.d b/gcc/testsuite/gdc.test/compilable/imports/testlambda2.d
new file mode 100644
index 00000000000..e96ef9fe1a0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/testlambda2.d
@@ -0,0 +1,3 @@
+module imports.testlambda2;
+
+int bar() { return 7; }
diff --git a/gcc/testsuite/gdc.test/compilable/imports/u20958.d b/gcc/testsuite/gdc.test/compilable/imports/u20958.d
new file mode 100644
index 00000000000..df67583541e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/u20958.d
@@ -0,0 +1,6 @@
+struct W()
+{
+ int[] J;
+}
+
+typeof(typeid(W!())) OB;
diff --git a/gcc/testsuite/gdc.test/compilable/interpret3.d b/gcc/testsuite/gdc.test/compilable/interpret3.d
index a2830ea11c2..ff85856b62e 100644
--- a/gcc/testsuite/gdc.test/compilable/interpret3.d
+++ b/gcc/testsuite/gdc.test/compilable/interpret3.d
@@ -2,8 +2,7 @@
/*
TEST_OUTPUT:
---
-compilable/interpret3.d(2914): Deprecation: `case` variables have to be `const` or `immutable`
-compilable/interpret3.d(6351): Deprecation: identity comparison of static arrays implicitly coerces them to slices, which are compared by reference
+compilable/interpret3.d(6350): Deprecation: identity comparison of static arrays implicitly coerces them to slices, which are compared by reference
---
*/
@@ -14,9 +13,9 @@ template compiles(int T)
alias TypeTuple(T...) = T;
-/**************************************************
- 3901 Arbitrary struct assignment, ref return
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=3901
+// Arbitrary struct assignment, ref return
struct ArrayRet
{
@@ -31,7 +30,7 @@ int arrayRetTest(int z)
}
static assert(arrayRetTest(51) == 51);
-// Bugzilla 3842 -- must not segfault
+// https://issues.dlang.org/show_bug.cgi?id=3842 -- must not segfault
int ice3842(int z)
{
ArrayRet w;
@@ -85,24 +84,24 @@ struct RetRefStruct
// Return value reference tests, for D2 only.
-ref RetRefStruct reffunc1(ref RetRefStruct a)
+ref RetRefStruct reffunc1(return ref RetRefStruct a)
{
int y = a.x;
return a;
}
-ref RetRefStruct reffunc2(ref RetRefStruct a)
+ref RetRefStruct reffunc2(return ref RetRefStruct a)
{
RetRefStruct z = a;
return reffunc1(a);
}
-ref int reffunc7(ref RetRefStruct aa)
+ref int reffunc7(return ref RetRefStruct aa)
{
return reffunc1(aa).x;
}
-ref int reffunc3(ref int a)
+ref int reffunc3(return ref int a)
{
return a;
}
@@ -111,18 +110,18 @@ struct RefTestStruct
{
RetRefStruct r;
- ref RefTestStruct reffunc4(ref RetRefStruct[3] a)
+ ref RefTestStruct reffunc4(ref RetRefStruct[3] a) return
{
return this;
}
- ref int reffunc6()
+ ref int reffunc6() return
{
return this.r.x;
}
}
-ref RetRefStruct reffunc5(ref RetRefStruct[3] a)
+ref RetRefStruct reffunc5(return ref RetRefStruct[3] a)
{
int t = 1;
for (int i = 0; i < 10; ++i)
@@ -189,9 +188,9 @@ static assert(retRefTest2() == 2);
static assert(retRefTest3() == 26);
static assert(retRefTest4() == 218);
-/**************************************************
- Bug 7887 assign to returned reference
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7887
+// assign to returned reference
bool test7887()
{
@@ -202,9 +201,9 @@ bool test7887()
}
static assert(test7887());
-/**************************************************
- Bug 7473 struct non-ref
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7473
+// struct non-ref
struct S7473
{
@@ -242,9 +241,8 @@ void bug7473b(S7473b s)
s.m.i = 2;
}
-/**************************************************
- Bug 4389
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4389
int bug4389()
{
@@ -296,7 +294,8 @@ string ice4390()
static assert(mixin(ice4390()) == ``);
-// bug 5248 (D1 + D2)
+// https://issues.dlang.org/show_bug.cgi?id=5248
+// (D1 + D2)
struct Leaf5248
{
string Compile_not_ovloaded()
@@ -316,9 +315,9 @@ struct Matrix5248
static assert(Matrix5248().Compile());
-/**************************************************
- 4837 >>>=
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4837
+// >>>=
bool bug4837()
{
@@ -333,9 +332,9 @@ bool bug4837()
static assert(bug4837());
-/**************************************************
- 10252 shift out of range
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10252
+// shift out of range
int lshr10252(int shift)
{
@@ -362,9 +361,9 @@ static assert(!is(typeof(compiles!(rshr10252(80)))));
static assert( is(typeof(compiles!(ushr10252( 2)))));
static assert(!is(typeof(compiles!(ushr10252(60)))));
-/**************************************************
- 1982 CTFE null problems
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=1982
+// CTFE null problems
enum a1982 = [1, 2, 3];
static assert(a1982 !is null);
@@ -375,16 +374,16 @@ static assert(!foo1982().length);
static assert(null is null);
-/**************************************************
- 7988 CTFE return values should be allowed in compile-time expressions
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7988
+// CTFE return values should be allowed in compile-time expressions
class X7988 { int y; this() { y = 2; } }
static assert((new X7988).y == 2);
-/**************************************************
- 8253 ICE: calling of member function of non-CTFE class variable
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8253
+// ICE: calling of member function of non-CTFE class variable
class Bug8253
{
@@ -396,9 +395,9 @@ class Bug8253
Bug8253 m8253;
static assert(!is(typeof(compiles!(m8253.j()))));
-/**************************************************
- 8285 Issue with slice returned from CTFE function
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8285
+// Issue with slice returned from CTFE function
string foo8285()
{
@@ -475,9 +474,9 @@ int thisbug2()
static assert(thisbug2());
-/**************************************************
- 6972 ICE with cast()cast()assign
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6972
+// ICE with cast()cast()assign
int bug6972()
{
@@ -487,9 +486,8 @@ int bug6972()
}
static assert(bug6972() == 3);
-/**************************************************
- Bug 6164
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6164
size_t bug6164()
{
@@ -641,7 +639,7 @@ size_t bug5524(int x, int[] more...)
static assert(bug5524(3) == 10);
-// 5722
+// https://issues.dlang.org/show_bug.cgi?id=5722
static assert(("" ~ "\&copy;"[0]).length == 1);
const char[] null5722 = null;
@@ -650,8 +648,13 @@ static assert(("\&copy;"[0] ~ null5722).length == 1);
/*******************************************
* Tests for CTFE Array support.
- * Including bugs 1330, 3801, 3835, 4050,
- * 4051, 5147, and major functionality
+ * https://issues.dlang.org/show_bug.cgi?id=1330
+ * https://issues.dlang.org/show_bug.cgi?id=3801
+ * https://issues.dlang.org/show_bug.cgi?id=3835
+ * https://issues.dlang.org/show_bug.cgi?id=4050
+ * https://issues.dlang.org/show_bug.cgi?id=4051
+ * https://issues.dlang.org/show_bug.cgi?id=5147
+ * and major functionality
*******************************************/
char[] bug1330StringIndex()
@@ -971,7 +974,7 @@ auto bug5852(const(string) s)
}
static assert(bug5852("abc") == 3);
-// 7217
+// https://issues.dlang.org/show_bug.cgi?id=7217
struct S7217 { int[] arr; }
@@ -998,7 +1001,8 @@ static assert(
return true;
}());
-// 7185 char[].length = n
+// https://issues.dlang.org/show_bug.cgi?id=7185
+// char[].length = n
bool bug7185()
{
@@ -1021,9 +1025,8 @@ bool bug9908()
}
static assert(bug9908());
-/*******************************************
- 6934
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6934
struct Struct6934
{
@@ -1049,15 +1052,13 @@ int bug6934()
}
static assert(bug6934());
-/*******************************************
- Bug 5671
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5671
static assert(['a', 'b'] ~ "c" == "abc");
-/*******************************************
- 8624
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8624
int evil8624()
{
@@ -1070,9 +1071,9 @@ int evil8624()
}
static assert(evil8624());
-/*******************************************
- 8644 array literal >,<
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8644
+// array literal >,<
int bug8644()
{
@@ -1090,18 +1091,16 @@ int bug8644()
}
static assert(bug8644());
-/*******************************************
- Bug 6159
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6159
struct A6159 {}
static assert({ return A6159.init is A6159.init; }());
static assert({ return [1] is [1]; }());
-/*******************************************
- Bug 5685
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5685
string bug5685()
{
@@ -1115,9 +1114,9 @@ struct Bug5865
}
}
-/*******************************************
- 6235 - Regression ICE on $ in template
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6235
+// Regression ICE on $ in template
struct Bug6235(R)
{
@@ -1126,15 +1125,14 @@ struct Bug6235(R)
Bug6235!(ubyte[]) bug6235;
-/*******************************************
- 8673 ICE
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8673
+// ICE
enum dollar8673 = [0][(() => $ - 1)()];
-/*******************************************
- Bug 5840
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5840
struct Bug5840
{
@@ -1164,9 +1162,8 @@ int bug5840(int u)
}
static assert(bug5840(1) == 56);
-/*******************************************
- 7810
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7810
int bug7810()
{
@@ -1207,9 +1204,8 @@ public:
}
const testTODsThrownZ = TimeOfDayZ(0);
-/*******************************************
- Bug 5954
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5954
struct Bug5954
{
@@ -1225,9 +1221,8 @@ void bug5954()
static assert(f.x == 10);
}
-/*******************************************
- Bug 5972
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5972
int bug5972()
{
@@ -1258,16 +1253,16 @@ int wconcat(wstring replace)
}
static assert(wconcat("X"w));
-/*******************************************
- 10397 string concat
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10397
+// string concat
static assert(!is(typeof(compiles!("abc" ~ undefined))));
static assert(!is(typeof(compiles!(otherundefined ~ "abc"))));
-/*******************************************
- 9634 struct concat
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9634
+// struct concat
struct Bug9634
{
@@ -1292,9 +1287,8 @@ bool bug9634()
static assert(bug9634());
-/*******************************************
- Bug 4001: A Space Oddity
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4001: A Space Oddity
int space() { return 4001; }
@@ -1305,15 +1299,14 @@ void oddity4001(int q)
static assert(bowie == 4001);
}
-/*******************************************
- Bug 3779
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=3779
static const bug3779 = ["123"][0][$ - 1];
-/*******************************************
- Bug 8893 ICE with bad struct literal
-*******************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8893
+// ICE with bad struct literal
struct Foo8893
{
@@ -1333,7 +1326,7 @@ struct Zadok
{
int[3] z;
char[4] s = void;
- ref int[] fog(ref int[] q) { return q; }
+ ref int[] fog(return ref int[] q) { return q; }
int bfg()
{
z[0] = 56;
@@ -1394,9 +1387,9 @@ int quop()
static assert(quop() == 8);
static assert(quop() == 8); // check for clobbering
-/**************************************************
- Bug 5676 tuple assign of struct that has void opAssign
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5676
+// tuple assign of struct that has void opAssign
struct S5676
{
@@ -1420,9 +1413,9 @@ bool ice5676()
static assert(ice5676());
-/**************************************************
- Bug 5682 Wrong CTFE with operator overloading
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5682
+// Wrong CTFE with operator overloading
struct A
{
@@ -1555,9 +1548,9 @@ int sdfgasf()
}
static assert(sdfgasf() == 1);
-/**************************************************
- 8830 slice of slice.ptr
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8830
+// slice of slice.ptr
string bug8830(string s)
{
@@ -1566,9 +1559,9 @@ string bug8830(string s)
}
static assert(bug8830("hello") == "el");
-/**************************************************
- 8608 ICE
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8608
+// ICE
void bug8608(ref int m) {}
void test8608()
@@ -1584,9 +1577,8 @@ void test8608()
static assert(!is(typeof(compiles!(foo(true) ))));
}
-/**************************************************
- Bug 7770
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7770
immutable char[] foo7770 = "abcde";
@@ -1609,9 +1601,9 @@ void baz7770()
static assert(bug7770b(foo7770[$ - 2]));
}
-/**************************************************
- 8601 ICE
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8601
+// ICE
dchar bug8601(dstring s)
{
@@ -1622,9 +1614,8 @@ dchar bug8601(dstring s)
enum dstring e8601 = [cast(dchar)'o', 'n'];
static assert(bug8601(e8601) == 'n');
-/**************************************************
- Bug 6015
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6015
struct Foo6015
{
@@ -1644,9 +1635,8 @@ bool func6015(string input)
static assert(func6015("test"));
-/**************************************************
- Bug 6001
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6001
void bug6001e(ref int[] s)
{
@@ -1694,10 +1684,11 @@ bool bug6001h()
}
static assert(bug6001h());
-/**************************************************
- 10243 wrong code *&arr as ref parameter
- 10551 wrong code (&arr)[0] as ref parameter
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10243
+// wrong code *&arr as ref parameter
+// https://issues.dlang.org/show_bug.cgi?id=10551
+// wrong code (&arr)[0] as ref parameter
void bug10243(ref int n)
{
@@ -1725,9 +1716,8 @@ bool test10243()
static assert(test10243());
-/**************************************************
- Bug 4910
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4910
int bug4910(int a)
{
@@ -1739,9 +1729,9 @@ static assert(!is(typeof(Compiles!(bug4910(var4910)))));
static assert(bug4910(123));
-/**************************************************
- Bug 5845 - Regression(2.041)
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5845
+// Regression(2.041)
void test5845(ulong cols) {}
@@ -1762,9 +1752,8 @@ ulong nqueen(int n)
static assert(nqueen(2) == 65);
-/**************************************************
- Bug 5258
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5258
struct Foo5258 { int x; }
void bar5258(int n, ref Foo5258 fong)
@@ -1819,9 +1808,8 @@ size_t bug5258c()
}
static assert(bug5258c() == 20);
-/**************************************************
- Bug 6049
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6049
struct Bug6049
{
@@ -1834,9 +1822,8 @@ const Bug6049[] foo6049 = [Bug6049(6), Bug6049(17)];
static assert(foo6049[0].m == 6);
-/**************************************************
- Bug 6052
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6052
struct Bug6052
{
@@ -1939,9 +1926,8 @@ static assert({
return true;
}());
-/**************************************************
- Bug 6749
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6749
struct CtState
{
@@ -1989,9 +1975,9 @@ int keyAssign()
}
static assert(keyAssign() == 5);
-/**************************************************
- Bug 6054 -- AA literals
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6054
+// AA literals
enum x6054 = {
auto p = {
@@ -2002,9 +1988,8 @@ enum x6054 = {
return p;
}();
-/**************************************************
- Bug 6077
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6077
enum bug6077 = {
string s;
@@ -2012,9 +1997,9 @@ enum bug6077 = {
return s ~ t;
}();
-/**************************************************
- Bug 6078 -- Pass null array by ref
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6078
+// Pass null array by ref
struct Foo6078
{
@@ -2042,9 +2027,9 @@ static assert({
return bug6078(f.bar);
}() == 2);
-/**************************************************
- Bug 6079 -- Array bounds checking
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6079
+// Array bounds checking
static assert(!is(typeof(compiles!({
int[] x = [1, 2, 3, 4];
@@ -2053,9 +2038,8 @@ static assert(!is(typeof(compiles!({
}()
))));
-/**************************************************
- Bug 6100
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6100
struct S6100
{
@@ -2071,9 +2055,9 @@ S6100 init6100(int x)
static const S6100[2] s6100a = [init6100(1), init6100(2)];
static assert(s6100a[0].a == 1);
-/**************************************************
- Bug 4825 -- failed with -inline
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4825
+// failed with -inline
int a4825()
{
@@ -2095,9 +2079,9 @@ void c4825()
static const int f = b4825();
}
-/**************************************************
- Bug 5708 -- failed with -inline
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5708
+// failed with -inline
string b5708(string s) { return s; }
string a5708(string s) { return b5708(s); }
@@ -2109,9 +2093,9 @@ void bug5708()
static assert(a5708("bar") == "bar");
}
-/**************************************************
- Bug 6120 -- failed with -inline
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6120
+// failed with -inline
struct Bug6120(T)
{
@@ -2122,9 +2106,9 @@ static assert({
return true;
}());
-/**************************************************
- Bug 6123 -- failed with -inline
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6123
+// failed with -inline
struct Bug6123(T)
{
@@ -2137,9 +2121,9 @@ static assert({
return true;
}());
-/**************************************************
- Bug 6053 -- ICE involving pointers
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6053
+// ICE involving pointers
static assert({
int* a = null;
@@ -2328,9 +2312,9 @@ bool nullptrcmp()
}
static assert(nullptrcmp());
-/**************************************************
- 10840 null pointer in dotvar
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10840
+// null pointer in dotvar
struct Data10840
{
@@ -2357,9 +2341,9 @@ bool bug10840(int n)
static assert(bug10840(0));
static assert(!is(typeof(Compileable!(bug10840(1)))));
-/**************************************************
- 8216 ptr inside a pointer range
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8216
+// ptr inside a pointer range
// Four-pointer relations. Return true if [p1 .. p2] points inside [q1 .. q2]
// (where the end points don't coincide).
@@ -2405,9 +2389,9 @@ bool bug8216()
}
static assert(bug8216());
-/**************************************************
- 6517 ptr++, ptr--
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6517
+// ptr++, ptr--
int bug6517()
{
@@ -2468,16 +2452,15 @@ static assert({
return 6;
}() == 6);
-/**************************************************
- Reduced version of bug 5615
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5615
const(char)[] passthrough(const(char)[] x)
{
return x;
}
-sizediff_t checkPass(Char1)(const(Char1)[] s)
+ptrdiff_t checkPass(Char1)(const(Char1)[] s)
{
const(Char1)[] balance = s[1 .. $];
return passthrough(balance).ptr - s.ptr;
@@ -2588,9 +2571,9 @@ struct AList
static assert(AList.checkList() == 2);
-/**************************************************
- 7194 pointers as struct members
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7194
+// pointers as struct members
struct S7194 { int* p, p2; }
@@ -2620,9 +2603,9 @@ int g7194()
static assert(f7194() == 0);
static assert(!is(typeof(compiles!(g7194()))));
-/**************************************************
- 7248 recursive struct pointers in array
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7248
+// recursive struct pointers in array
struct S7248 { S7248* ptr; }
@@ -2636,9 +2619,9 @@ bool bug7248()
}
static assert(bug7248());
-/**************************************************
- 7216 calling a struct pointer member
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7216
+// calling a struct pointer member
struct S7216
{
@@ -2662,9 +2645,9 @@ bool bug7216()
static assert(bug7216());
-/**************************************************
- 10858 Wrong code with array of pointers
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10858
+// Wrong code with array of pointers
bool bug10858()
{
@@ -2675,9 +2658,9 @@ bool bug10858()
}
static assert(bug10858());
-/**************************************************
- 12528 - painting inout type for value type literals
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12528
+// painting inout type for value type literals
inout(T)[] dup12528(T)(inout(T)[] a)
{
@@ -2692,9 +2675,9 @@ enum arr12528V2 = dup12528([0, 1]);
static assert(arr12528V1 == [0]);
static assert(arr12528V2 == [0, 1]);
-/**************************************************
- 9745 Allow pointers to static variables
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9745
+// Allow pointers to static variables
shared int x9745;
shared int[5] y9745;
@@ -2751,9 +2734,9 @@ bool test9745b()
}
static assert(test9745b());
-/**************************************************
- 9364 ICE with pointer to local struct
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9364
+// ICE with pointer to local struct
struct S9364
{
@@ -2769,9 +2752,9 @@ bool bug9364()
static assert(bug9364());
-/**************************************************
- 10251 Pointers to const globals
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10251
+// Pointers to const globals
static const int glob10251 = 7;
@@ -2783,9 +2766,9 @@ const(int)* bug10251()
static a10251 = &glob10251; // OK
static b10251 = bug10251();
-/**************************************************
- 4065 [CTFE] AA "in" operator doesn't work
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4065
+// [CTFE] AA "in" operator doesn't work
bool bug4065(string s)
{
@@ -2807,9 +2790,9 @@ static assert(!bug4065("xx"));
static assert( bug4065("aa"));
static assert( bug4065("bb"));
-/**************************************************
- 12689 - assigning via pointer from 'in' expression
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12689
+// assigning via pointer from 'in' expression
int g12689()
{
@@ -2855,9 +2838,9 @@ int ptrSlice()
}
static assert(ptrSlice() == 2);
-/**************************************************
- 6344 - create empty slice from null pointer
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6344
+// create empty slice from null pointer
static assert({
char* c = null;
@@ -2865,18 +2848,18 @@ static assert({
return true;
}());
-/**************************************************
- 8365 - block assignment of enum arrays
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8365
+// block assignment of enum arrays
enum E8365 { first = 7, second, third, fourth }
static assert({ E8365[2] x; return x[0]; }() == E8365.first);
static assert({ E8365[2][2] x; return x[0][0]; }() == E8365.first);
static assert({ E8365[2][2][2] x; return x[0][0][0]; }() == E8365.first);
-/**************************************************
- 4448 - labelled break + continue
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4448
+// labelled break + continue
int bug4448()
{
@@ -2918,27 +2901,27 @@ L1:
}
static assert(bug4448b() == 3);
-/**************************************************
- 6985 - non-constant case
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6985
+// Formerly, non-constant case, but switch cases with mutable cases now error
+// Currently: run-time constant variable case
int bug6985(int z)
{
- int q = z * 2 - 6;
+ const int q = z * 2 - 6;
switch(z)
{
case q:
- q = 87;
- break;
+ return 87;
default:
}
return q;
}
static assert(bug6985(6) == 87);
-/**************************************************
- 6281 - [CTFE] A null pointer '!is null' returns 'true'
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6281
+// [CTFE] A null pointer '!is null' returns 'true'
static assert(!{
auto p = null;
@@ -2950,9 +2933,9 @@ static assert(!{
return p != null;
}());
-/**************************************************
- 6331 - evaluate SliceExp on if condition
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6331
+// evaluate SliceExp on if condition
bool bug6331(string s)
{
@@ -2962,9 +2945,9 @@ bool bug6331(string s)
}
static assert(bug6331("str"));
-/**************************************************
- 6283 - assign to AA with slice as index
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6283
+// assign to AA with slice as index
static assert({
immutable p = "pp";
@@ -2992,9 +2975,9 @@ static assert({
return true;
}());
-/**************************************************
- 6282 - dereference 'in' of an AA
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6282
+// dereference 'in' of an AA
static assert({
int[] w = new int[4];
@@ -3008,9 +2991,9 @@ static assert({
return n;
}() == "1");
-/**************************************************
- 6337 - member function call on struct literal
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6337
+// member function call on struct literal
struct Bug6337
{
@@ -3027,17 +3010,16 @@ struct Bug6337
}
static assert(Bug6337().ctfe() == 6);
-/**************************************************
- 6603 call manifest function pointer
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6603
+// call manifest function pointer
int f6603(int a) { return a + 5; }
enum bug6603 = &f6603;
static assert(bug6603(6) == 11);
-/**************************************************
- 6375
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6375
struct D6375
{
@@ -3067,9 +3049,9 @@ static assert({
return true;
}());
-/**************************************************
- 6280 Converting pointers to bool
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6280
+// Converting pointers to bool
static assert({
if ((0 in [0:0])) {}
@@ -3077,9 +3059,9 @@ static assert({
return true;
}());
-/**************************************************
- 6276 ~=
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6276
+// ~=
struct Bug6276
{
@@ -3092,9 +3074,9 @@ static assert({
return true;
}());
-/**************************************************
- 6374 ptr[n] = x, x = ptr[n]
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6374
+// ptr[n] = x, x = ptr[n]
static assert({
int[] arr = [1];
@@ -3104,9 +3086,9 @@ static assert({
return arr[0];
}() == 2);
-/**************************************************
- 6306 recursion and local variables
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6306
+// recursion and local variables
void recurse6306()
{
@@ -3124,9 +3106,9 @@ bool bug6306(bool b)
}
static assert(bug6306(true));
-/**************************************************
- 6386 ICE on unsafe pointer cast
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6386
+// ICE on unsafe pointer cast
static assert(!is(typeof(compiles!({
int x = 123;
@@ -3145,9 +3127,9 @@ static assert({
return *q;
}());
-/**************************************************
- 6420 ICE on dereference of invalid pointer
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6420
+// ICE on dereference of invalid pointer
static assert({
// Should compile, but pointer can't be dereferenced
@@ -3202,9 +3184,9 @@ static assert(!is(typeof(compiles!({
}()
))));
-/**************************************************
- 6250 deref pointers to array
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6250
+// deref pointers to array
int[]* simple6250(int[]* x) { return x; }
@@ -3280,9 +3262,9 @@ long ctfeSort6250b()
static assert(ctfeSort6250b() == 57);
-/**************************************************
- 6672 circular references in array
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6672
+// circular references in array
void bug6672(ref string lhs, ref string rhs)
{
@@ -3334,9 +3316,9 @@ static assert({
return true;
}());
-/**************************************************
- 6399 (*p).length = n
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6399
+// (*p).length = n
struct A6399
{
@@ -3354,9 +3336,9 @@ static assert({
return a.subLen();
}() == 4);
-/**************************************************
- 7789 (*p).length++ where *p is null
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7789
+// (*p).length++ where *p is null
struct S7789
{
@@ -3371,9 +3353,9 @@ struct S7789
static assert(S7789().foo());
-/**************************************************
- 6418 member named 'length'
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6418
+// member named 'length'
struct Bug6418
{
@@ -3381,9 +3363,9 @@ struct Bug6418
}
static assert(Bug6418.init.length == 189);
-/**************************************************
- 4021 rehash
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4021
+// rehash
bool bug4021()
{
@@ -3393,9 +3375,9 @@ bool bug4021()
}
static assert(bug4021());
-/**************************************************
- 11629 crash on AA.rehash
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11629
+// crash on AA.rehash
struct Base11629
{
@@ -3413,10 +3395,11 @@ struct Base11629
}
enum ct11629 = Base11629(4);
-/**************************************************
- 3512 foreach (dchar; string)
- 6558 foreach (int, dchar; string)
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=3512
+// foreach (dchar; string)
+// https://issues.dlang.org/show_bug.cgi?id=6558
+// foreach (int, dchar; string)
bool test3512()
{
@@ -3552,9 +3535,9 @@ bool test3512()
}
static assert(test3512());
-/**************************************************
- 6510 ICE only with -inline
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6510
+// ICE only with -inline
struct Stack6510
{
@@ -3585,9 +3568,9 @@ void test6510()
static assert(bug6510() == 3);
}
-/**************************************************
- 6511 arr[] shouldn't make a copy
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6511
+// arr[] shouldn't make a copy
T bug6511(T)()
{
@@ -3598,9 +3581,9 @@ T bug6511(T)()
static assert(bug6511!ulong() == 2);
static assert(bug6511!long() == 2);
-/**************************************************
- 6512 new T[][]
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6512
+// new T[][]
bool bug6512(int m)
{
@@ -3620,9 +3603,9 @@ bool bug6512(int m)
}
static assert(bug6512(3));
-/**************************************************
- 6516 ICE(constfold.c)
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6516
+// ICE(constfold.c)
dstring bug6516()
{
@@ -3631,9 +3614,9 @@ dstring bug6516()
static assert(bug6516() == ""d);
-/**************************************************
- 6727 ICE(interpret.c)
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6727
+// ICE(interpret.c)
const(char)* ice6727(const(char)* z) { return z; }
static assert({
@@ -3641,9 +3624,9 @@ static assert({
return true;
}());
-/**************************************************
- 6721 Cannot get pointer to start of char[]
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6721
+// Cannot get pointer to start of char[]
static assert({
char[] c1 = "".dup;
@@ -3653,9 +3636,9 @@ static assert({
return 6;
}() == 6);
-/**************************************************
- 6693 Assign to null AA
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6693
+// Assign to null AA
struct S6693
{
@@ -3672,9 +3655,9 @@ static assert({
return 6693;
}() == 6693);
-/**************************************************
- 7602 Segfault AA.keys on null AA
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7602
+// Segfault AA.keys on null AA
string[] test7602()
{
@@ -3684,9 +3667,9 @@ string[] test7602()
enum bug7602 = test7602();
-/**************************************************
- 6739 Nested AA assignment
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6739
+// Nested AA assignment
static assert({
int[int][int][int] aaa;
@@ -3703,9 +3686,9 @@ static assert({
return kk;
}() == 9);
-/**************************************************
- 6751 ref AA assignment
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6751
+// ref AA assignment
void bug6751(ref int[int] aa)
{
@@ -3763,9 +3746,9 @@ void bug6751c(ref int[int][int] aa)
aa = [38: [56 : 77]];
}
-/**************************************************
- 7790 AA foreach ref
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7790
+// AA foreach ref
struct S7790
{
@@ -3781,18 +3764,18 @@ size_t bug7790(S7790[string] tree)
static assert(bug7790(["a":S7790(0)]) == 1);
-/**************************************************
- 6765 null AA.length
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6765
+// null AA.length
static assert({
int[int] w;
return w.length;
}() == 0);
-/**************************************************
- 6769 AA.keys, AA.values with -inline
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6769
+// AA.keys, AA.values with -inline
static assert({
double[char[3]] w = ["abc" : 2.3];
@@ -3800,9 +3783,9 @@ static assert({
return w.keys.length;
}() == 1);
-/**************************************************
- 4022 AA.get
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4022
+// AA.get
static assert({
int[int] aa = [58: 13];
@@ -3812,9 +3795,9 @@ static assert({
return r;
}() == 1000);
-/**************************************************
- 6775 AA.opApply
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6775
+// AA.opApply
static assert({
int[int] aa = [58: 17, 45:6];
@@ -3844,9 +3827,9 @@ static assert({
return true;
}());
-/**************************************************
- 7890 segfault struct with AA field
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7890
+// segfault struct with AA field
struct S7890
{
@@ -3907,9 +3890,9 @@ static assert({
return true;
}());
-/**************************************************
- 6800 bad pointer casts
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6800
+// bad pointer casts
bool badpointer(int k)
{
@@ -3974,9 +3957,9 @@ static assert(!is(typeof(compiles!(badpointer(6)))));
static assert(!is(typeof(compiles!(badpointer(7)))));
static assert(!is(typeof(compiles!(badpointer(8)))));
-/**************************************************
- 10211 Allow casts S**->D**, when S*->D* is OK
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10211
+// Allow casts S**->D**, when S*->D* is OK
int bug10211()
{
@@ -3991,9 +3974,9 @@ int bug10211()
static assert(bug10211());
-/**************************************************
- 10568 CTFE rejects function pointer safety casts
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10568
+// CTFE rejects function pointer safety casts
@safe void safetyDance() {}
@@ -4006,9 +3989,9 @@ int isItSafeToDance()
static assert(isItSafeToDance());
-/**************************************************
- 12296 CTFE rejects const compatible AA pointer cast
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12296
+// CTFE rejects const compatible AA pointer cast
int test12296()
{
@@ -4019,9 +4002,9 @@ int test12296()
}
static assert(test12296());
-/**************************************************
- 9170 Allow reinterpret casts float<->int
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9170
+// Allow reinterpret casts float<->int
int f9170(float x)
{
@@ -4077,9 +4060,9 @@ bool bug9170()
static assert(bug9170());
-/**************************************************
- 6792 ICE with pointer cast of indexed array
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6792
+// ICE with pointer cast of indexed array
struct S6792
{
@@ -4139,9 +4122,9 @@ static assert({
return true;
}());
-/**************************************************
- 7780 array cast
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7780
+// array cast
int bug7780(int testnum)
{
@@ -4164,9 +4147,9 @@ static assert( is(typeof(compiles!(bug7780(0)))));
static assert(!is(typeof(compiles!(bug7780(1)))));
static assert(!is(typeof(compiles!(bug7780(2)))));
-/**************************************************
- 14028 - static array pointer that refers existing array elements.
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14028
+// static array pointer that refers existing array elements.
int test14028a(size_t ofs)(bool ct)
{
@@ -4244,9 +4227,9 @@ static assert(!is(typeof(compiles!(test14028b(3)))));
static assert(test14028b(4));
static assert(!is(typeof(compiles!(test14028b(5)))));
-/**************************************************
- 10275 cast struct literals to immutable
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10275
+// cast struct literals to immutable
struct Bug10275
{
@@ -4266,9 +4249,9 @@ int test10275()
static assert(test10275());
-/**************************************************
- 6851 passing pointer by argument
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6851
+// passing pointer by argument
void set6851(int* pn)
{
@@ -4284,9 +4267,8 @@ void bug6851()
}
static assert({ bug6851(); return true; }());
-/**************************************************
- 7876
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7876
int* bug7876(int n) @system
{
@@ -4328,9 +4310,8 @@ static assert(!is(typeof(compiles!(test7876(0)))));
static assert( is(typeof(compiles!(test7876(11)))));
static assert(!is(typeof(compiles!(test7876(10)))));
-/**************************************************
- 11824
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11824
int f11824(T)()
{
@@ -4345,9 +4326,9 @@ int f11824(T)()
static assert(f11824!int()); // OK
static assert(f11824!(int[])()); // OK <- NG
-/**************************************************
- 6817 if converted to &&, only with -inline
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6817
+// if converted to &&, only with -inline
static assert({
void toggle()
@@ -4369,9 +4350,9 @@ static assert({
return true;
}());
-/**************************************************
- 6816 nested function can't access this
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6816
+// nested function can't access this
struct S6816
{
@@ -4384,9 +4365,9 @@ struct S6816
enum s6816 = S6816().foo();
-/**************************************************
- 7277 ICE nestedstruct.init.tupleof
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7277
+// ICE nestedstruct.init.tupleof
struct Foo7277
{
@@ -4406,9 +4387,9 @@ struct Foo7277
static assert(Foo7277().func() == 17);
-/**************************************************
- 10217 ICE. CTFE version of 9315
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10217
+// ICE. CTFE version of 9315
bool bug10217()
{
@@ -4424,9 +4405,9 @@ bool bug10217()
static assert(bug10217());
-/**************************************************
- 8276 ICE
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8276
+// ICE
void bug8676(int n)
{
@@ -4525,7 +4506,7 @@ auto classtest1(int n)
}
static assert(classtest1(1));
static assert(classtest1(2));
-static assert(classtest1(7)); // bug 7154
+static assert(classtest1(7)); // https://issues.dlang.org/show_bug.cgi?id=7154
// can't initialize enum with not null class
SomeClass classtest2(int n)
@@ -4555,9 +4536,9 @@ int classtest3()
static assert(classtest3());
-/**************************************************
- 12016 class cast and qualifier reinterpret
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12016
+// class cast and qualifier reinterpret
class B12016 { }
@@ -4571,9 +4552,9 @@ bool f12016(immutable B12016 b)
static assert(f12016(new immutable C12016));
-/**************************************************
- 10610 ice immutable implicit conversion
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10610
+// ice immutable implicit conversion
class Bug10610(T)
{
@@ -4590,9 +4571,9 @@ void ice10610()
static assert (T10610.min.baz());
}
-/**************************************************
- 13141 regression fix caused by 10610
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13141
+// regression fix caused by 10610
struct MapResult13141(alias pred)
{
@@ -4613,24 +4594,24 @@ string[] array13141(R)(R r)
//immutable string[] splitterNames = [4].map!(e => "4").array();
immutable string[] splitterNames13141 = MapResult13141!(e => "4")([4]).array13141();
-/**************************************************
- 11587 AA compare
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11587
+// AA compare
static assert([1:2, 3:4] == [3:4, 1:2]);
-/**************************************************
- 14325 more AA comparisons
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14325
+// more AA comparisons
static assert([1:1] != [1:2, 2:1]); // OK
static assert([1:1] != [1:2]); // OK
static assert([1:1] != [2:1]); // OK <- Error
static assert([1:1, 2:2] != [3:3, 4:4]); // OK <- Error
-/**************************************************
- 7147 typeid()
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7147
+// typeid()
static assert({
TypeInfo xxx = typeid(Object);
@@ -4650,9 +4631,9 @@ static assert(!is(typeof(compiles!(bug7147(0)))));
static assert( is(typeof(compiles!(bug7147(1)))));
-/**************************************************
- 14123 - identity TypeInfo objects
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14123
+// identity TypeInfo objects
static assert({
bool eq(TypeInfo t1, TypeInfo t2)
@@ -4679,9 +4660,9 @@ static assert({
return 1;
}());
-/**************************************************
- 6885 wrong code with new array
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6885
+// wrong code with new array
struct S6885
{
@@ -4704,9 +4685,9 @@ int bug6885()
static assert(bug6885());
-/**************************************************
- 6886 ICE with new array of dynamic arrays
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6886
+// ICE with new array of dynamic arrays
int bug6886()
{
@@ -4719,13 +4700,13 @@ int bug6886()
static assert(bug6886());
-/**************************************************
- 10198 Multidimensional struct block initializer
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10198
+// Multidimensional struct block initializer
struct Block10198
{
- int val[3][4];
+ int[4][3] val;
}
int bug10198()
@@ -4737,9 +4718,9 @@ int bug10198()
}
static assert(bug10198());
-/**************************************************
- 14440 Multidimensional block initialization should create distinct arrays for each elements
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14440
+// Multidimensional block initialization should create distinct arrays for each elements
struct Matrix14440(E, size_t row, size_t col)
{
@@ -5112,9 +5093,9 @@ int testsFromEH()
}
static assert(testsFromEH());
-/**************************************************
- With + synchronized statements + bug 6901
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6901
+// With + synchronized statements
struct With1
{
@@ -5197,9 +5178,9 @@ int testwith()
static assert(testwith());
-/**************************************************
- 9236 ICE switch with(EnumType)
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9236
+// ICE switch with(EnumType)
enum Command9236
{
@@ -5231,9 +5212,9 @@ bool bug9236(Command9236 cmd)
static assert(bug9236(Command9236.Any));
-/**************************************************
- 6416 static struct declaration
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6416
+// static struct declaration
static assert({
static struct S { int y = 7; }
@@ -5243,18 +5224,18 @@ static assert({
return true;
}());
-/**************************************************
- 10499 static template struct declaration
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10499
+// static template struct declaration
static assert({
static struct Result() {}
return true;
}());
-/**************************************************
- 13757 extern(C) alias declaration
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13757
+// extern(C) alias declaration
static assert({
alias FP1 = extern(C) int function();
@@ -5262,9 +5243,9 @@ static assert({
return true;
}());
-/**************************************************
- 6522 opAssign + foreach ref
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6522
+// opAssign + foreach ref
struct Foo6522
{
@@ -5285,9 +5266,9 @@ bool foo6522()
static assert(foo6522());
-/**************************************************
- 7245 pointers + foreach ref
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7245
+// pointers + foreach ref
int bug7245(int testnum)
{
@@ -5311,11 +5292,13 @@ int bug7245(int testnum)
static assert(bug7245(0) == 6);
static assert(bug7245(1) == 5);
-/**************************************************
- 8498 modifying foreach
- 7658 foreach ref
- 8539 nested funcs, ref param, -inline
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8498
+// modifying foreach
+// https://issues.dlang.org/show_bug.cgi?id=7658
+// foreach ref
+// https://issues.dlang.org/show_bug.cgi?id=8539
+// nested funcs, ref param, -inline
int bug8498()
{
@@ -5356,9 +5339,11 @@ int bug8539()
static assert(bug8539());
-/**************************************************
- 7874, 13297, 13740 - better lvalue handling
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7874
+// https://issues.dlang.org/show_bug.cgi?id=13297
+// https://issues.dlang.org/show_bug.cgi?id=13740
+// better lvalue handling
int bug7874(int x){ return ++x = 1; }
static assert(bug7874(0) == 1);
@@ -5399,9 +5384,8 @@ static assert({
return true;
}());
-/**************************************************
- 6919
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6919
void bug6919(int* val)
{
@@ -5428,9 +5412,8 @@ void test6919b()
}
static assert({ test6919b(); return true; }());
-/**************************************************
- 6995
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6995
struct Foo6995
{
@@ -5442,9 +5425,9 @@ struct Foo6995
static assert(Foo6995.index!(27)() == 27);
-/**************************************************
- 7043 ref with -inline
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7043
+// ref with -inline
int bug7043(S)(ref int x)
{
@@ -5456,9 +5439,9 @@ static assert({
return bug7043!(char)(i);
}() == 416);
-/**************************************************
- 6037 recursive ref
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6037
+// recursive ref
void bug6037(ref int x, bool b)
{
@@ -5484,9 +5467,9 @@ int bug6037outer()
static assert(bug6037outer() == 401);
-/**************************************************
- 14299 - [REG2.067a], more than one depth of recursive call with ref
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14299
+// [REG2.067a], more than one depth of recursive call with ref
string gen14299(int max, int idx, ref string name)
{
@@ -5512,9 +5495,9 @@ static assert(test14299(3) == "01233210");
static assert(test14299(4) == "0123443210");
static assert(test14299(5) == "012345543210");
-/**************************************************
- 7940 wrong code for complicated assign
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7940
+// wrong code for complicated assign
struct Bug7940
{
@@ -5539,9 +5522,9 @@ int bug7940()
static assert(bug7940());
-/**************************************************
- 10298 wrong code for struct array literal init
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10298
+// wrong code for struct array literal init
struct Bug10298
{
@@ -5563,9 +5546,9 @@ int bug10298()
static assert(bug10298());
-/**************************************************
- 7266 dotvar ref parameters
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7266
+// dotvar ref parameters
struct S7266 { int a; }
@@ -5594,9 +5577,9 @@ void out7266(out int b)
static assert(bug7266());
-/**************************************************
- 9982 dotvar assign through pointer
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9982
+// dotvar assign through pointer
struct Bug9982
{
@@ -5614,7 +5597,8 @@ int test9982()
static assert(test9982());
-// 9982, rejects-valid case
+// https://issues.dlang.org/show_bug.cgi?id=9982
+// rejects-valid case
struct SS9982
{
@@ -5635,9 +5619,9 @@ void emplace9982(Bug9982* chunk, Bug9982 arg)
enum s9982 = Bug9982(3);
enum p9982 = SS9982(s9982);
-/**************************************************
- 11618 dotvar assign through casted pointer
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11618
+// dotvar assign through casted pointer
struct Tuple11618(T...)
{
@@ -5652,9 +5636,9 @@ static assert({
return (result[0] == dchar.init);
}());
-/**************************************************
- 7143 'is' for classes
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7143
+// 'is' for classes
class C7143
{
@@ -5708,9 +5692,9 @@ static assert(bug7143(4) == 48);
static assert(bug7143(5) == 48);
static assert(bug7143(6) == 188);
-/**************************************************
- 7147 virtual function calls from base class
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7147
+// virtual function calls from base class
class A7147
{
@@ -5735,9 +5719,8 @@ int test7147()
static assert(test7147() == 1);
-/**************************************************
- 7158
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7158
class C7158
{
@@ -5755,9 +5738,8 @@ bool test7158()
}
static assert(test7158());
-/**************************************************
- 8484
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8484
class C8484
{
@@ -5783,9 +5765,8 @@ int test8484()
}
static assert(test8484() == 7);
-/**************************************************
- 7419
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7419
struct X7419
{
@@ -5805,9 +5786,9 @@ void bug7419()
static assert(x == 3);
}
-/**************************************************
- 9445 ice
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9445
+// ice
template c9445(T...) { }
@@ -5817,9 +5798,9 @@ void ice9445(void delegate() expr, void function() f2)
static assert(!is(typeof(c9445!(expr()))));
}
-/**************************************************
- 10452 delegate ==
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10452
+// delegate ==
struct S10452
{
@@ -5836,7 +5817,7 @@ class C10452
bool func() { return true; }
}
-bool delegate() ref10452(ref S10452 s)
+bool delegate() ref10452(return ref S10452 s)
{
return &s.func;
}
@@ -5884,9 +5865,9 @@ bool test10452()
}
static assert(test10452());
-/**************************************************
- 7162 and 4711
-**************************************************/
+/**************************************************/
+//https://issues.dlang.org/show_bug.cgi?id=7162
+// https://issues.dlang.org/show_bug.cgi?id=4711
void f7162() { }
@@ -5894,16 +5875,16 @@ bool ice7162()
{
false && f7162();
false || f7162();
- false && f7162(); // bug 4711
+ false && f7162(); // https://issues.dlang.org/show_bug.cgi?id=4711
true && f7162();
return true;
}
static assert(ice7162());
-/**************************************************
- 8857, only with -inline (creates an &&)
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8857
+// only with -inline (creates an &&)
struct Result8857 { char[] next; }
@@ -5921,9 +5902,8 @@ static assert({
return true;
}());
-/**************************************************
- 7527
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7527
struct Bug7527
{
@@ -5940,9 +5920,8 @@ int bug7527()
static assert(!is(typeof(compiles!(bug7527()))));
-/**************************************************
- 7527
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7527
int bug7380;
@@ -5952,9 +5931,8 @@ static assert(!is(typeof( compiles!(
}()
))));
-/**************************************************
- 7165
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7165
struct S7165
{
@@ -5964,9 +5942,8 @@ struct S7165
static assert(!S7165().f());
-/**************************************************
- 7187
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7187
int[] f7187() { return [0]; }
int[] f7187b(int n) { return [0]; }
@@ -5997,9 +5974,9 @@ bool g7187c(const(int)[] r)
static assert(g7187c(f7187c()));
-/**************************************************
- 6933 struct destructors
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6933
+// struct destructors
struct Bug6933
{
@@ -6016,9 +5993,8 @@ int test6933()
static assert(test6933());
-/**************************************************
- 7197
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7197
int foo7197(int[] x...)
{
@@ -6046,9 +6022,8 @@ bool testEScmp()
static assert(testEScmp());
-/**************************************************
- 7667
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7667
bool baz7667(int[] vars...)
{
@@ -6071,9 +6046,8 @@ bool bug7667()
}
enum e7667 = bug7667();
-/**************************************************
- 7536
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7536
bool bug7536(string expr)
{
@@ -6086,9 +6060,9 @@ void vop()
static assert(bug7536(x7536));
}
-/**************************************************
- 6681 unions
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6681
+// unions
struct S6681
{
@@ -6130,9 +6104,9 @@ static assert(!is(typeof(compiles!(bug6681(1)))));
static assert(!is(typeof(compiles!(bug6681(3)))));
static assert(!is(typeof(compiles!(bug6681(4)))));
-/**************************************************
- 9113 ICE with struct in union
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9113
+// ICE with struct in union
union U9113
{
@@ -6170,9 +6144,9 @@ int uniontest1()
static assert(uniontest1());
-/**************************************************
- 6438 void
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=6438
+// void
struct S6438
{
@@ -6209,9 +6183,9 @@ static assert( is(typeof(compiles!(bug6438(1)))));
static assert(!is(typeof(compiles!(bug6438(2)))));
static assert(!is(typeof(compiles!(bug6438(3)))));
-/**************************************************
- 10994 void static array members
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10994
+// void static array members
struct Bug10994
{
@@ -6220,9 +6194,9 @@ struct Bug10994
static bug10994 = Bug10994.init;
-/**************************************************
- 10937 struct inside union
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10937
+// struct inside union
struct S10937
{
@@ -6247,9 +6221,8 @@ struct S10937
enum test10937 = S10937(7);
enum west10937 = S10937(2);
-/**************************************************
- 13831
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13831
struct Vector13831()
{
@@ -6276,9 +6249,8 @@ struct Chunk13831
static const Chunk13831* unknownChunk = new Chunk13831(Coord13831());
}
-/**************************************************
- 7732
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7732
struct AssociativeArray
{
@@ -6299,9 +6271,8 @@ int test7732()
static assert(test7732());
-/**************************************************
- 7784
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7784
struct Foo7784
{
void bug()
@@ -6329,15 +6300,13 @@ bool ctfe7784()
static assert(ctfe7784());
-/**************************************************
- 7781
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7781
-static assert(({ return; }(), true));
+static assert(({ return true; }()));
-/**************************************************
- 7785
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7785
bool bug7785(int n)
{
@@ -6359,9 +6328,8 @@ static assert(bug7785(1));
static assert(!is(typeof(compiles!(bug7785(2)))));
static assert(!is(typeof(compiles!(bug7785(3)))));
-/**************************************************
- 7987
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7987
class C7987
{
@@ -6400,9 +6368,9 @@ bool bug7987()
static assert(bug7987());
-/**************************************************
- 10579 typeinfo.func() must not segfault
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10579
+// typeinfo.func() must not segfault
static assert(!is(typeof(compiles!(typeid(int).toString.length))));
@@ -6414,9 +6382,9 @@ Bug10579 uninitialized10579;
static assert(!is(typeof(compiles!(uninitialized10579.foo()))));
-/**************************************************
- 10804 mixin ArrayLiteralExp typed string
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10804
+// mixin ArrayLiteralExp typed string
void test10804()
{
@@ -6476,7 +6444,7 @@ C73 test73 = func73();
struct S74
{
- int n[1];
+ int[1] n;
static S74 test(){ S74 ret = void; ret.n[0] = 0; return ret; }
}
@@ -6509,7 +6477,7 @@ label:
break label; // doesn't work.
}
}
-body
+do
{
int x = 0;
label:
@@ -6526,7 +6494,8 @@ label:
static assert(bug8865());
/******************************************************/
-// 15450 labeled foreach + continue/break
+// https://issues.dlang.org/show_bug.cgi?id=15450
+// labeled foreach + continue/break
static assert({
L1:
@@ -6590,7 +6559,8 @@ static assert( __traits(compiles, { static const Test76 t76 = new const(Test
static assert( __traits(compiles, { static immutable Test76 t76 = new immutable Test76(0); return t76; }));
static assert(!__traits(compiles, { static Test76 t76 = new Test76(0); return t76; }));
-/***** Bug 5678 *********************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5678
struct Bug5678
{
@@ -6599,9 +6569,9 @@ struct Bug5678
static assert(!__traits(compiles, { enum const(Bug5678)* b5678 = new const(Bug5678)(0); return b5678; }));
-/**************************************************
- 10782 run semantic2 for class field
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10782
+// run semantic2 for class field
enum e10782 = 0;
class C10782 { int x = e10782; }
@@ -6612,9 +6582,9 @@ string f10782()
}
mixin(f10782());
-/**************************************************
- 10929 NRVO support in CTFE
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10929
+// NRVO support in CTFE
struct S10929
{
@@ -6646,9 +6616,9 @@ bool test10929()
};
static assert(test10929());
-/**************************************************
- 9245 - support postblit call on array assignments
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9245
+// support postblit call on array assignments
bool test9245()
{
@@ -6712,9 +6682,9 @@ bool test9245()
}
static assert(test9245());
-/**************************************************
- 12906 don't call postblit on blit initializing
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12906
+// don't call postblit on blit initializing
struct S12906 { this(this) { assert(0); } }
@@ -6723,9 +6693,9 @@ static assert({
return true;
}());
-/**************************************************
- 11510 support overlapped field access in CTFE
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11510
+// support overlapped field access in CTFE
struct S11510
{
@@ -6750,9 +6720,9 @@ bool test11510()
}
static assert(test11510());
-/**************************************************
- 11534 - subtitude inout
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11534
+// subtitude inout
struct MultiArray11534
{
@@ -6776,9 +6746,9 @@ enum test11534 = () {
return 0;
}();
-/**************************************************
- 11941 - Regression of 11534 fix
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11941
+// Regression of 11534 fix
void takeConst11941(const string[]) {}
string[] identity11941(string[] x) { return x; }
@@ -6808,9 +6778,9 @@ bool test11941b()
}
static assert(test11941b());
-/**************************************************
- 11535 - element-wise assignment from string to ubyte array literal
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11535
+// element-wise assignment from string to ubyte array literal
struct Hash11535
{
@@ -6834,33 +6804,12 @@ auto md5_digest11535(T...)(scope const T data)
static assert(md5_digest11535(`TEST`) == [84, 69, 83, 84, 0, 0]);
-/**************************************************
- 11540 - goto label + try-catch-finally / with statement
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11540
+// goto label + try-catch-finally / with statement
static assert(()
{
- // enter to TryCatchStatement.body
- {
- bool c = false;
- try
- {
- if (c) // need to bypass front-end optimization
- throw new Exception("");
- else
- {
- goto Lx;
- L1:
- c = true;
- }
- }
- catch (Exception e) {}
-
- Lx:
- if (!c)
- goto L1;
- }
-
// jump inside TryCatchStatement.body
{
bool c = false;
@@ -6952,37 +6901,6 @@ static assert(()
static assert(()
{
- // enter forward to TryFinallyStatement.body
- {
- bool c = false;
- goto L0;
- c = true;
- try
- {
- L0:
- ;
- }
- finally {}
- assert(!c);
- }
-
- // enter back to TryFinallyStatement.body
- {
- bool c = false;
- try
- {
- goto Lx;
- L1:
- c = true;
- }
- finally {
- }
-
- Lx:
- if (!c)
- goto L1;
- }
-
// jump inside TryFinallyStatement.body
{
try
@@ -7055,9 +6973,9 @@ static assert(()
return 1;
}());
-/**************************************************
- 11627 - cast dchar to char at compile time on AA assignment
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11627
+//cast dchar to char at compile time on AA assignment
bool test11627()
{
@@ -7071,9 +6989,9 @@ bool test11627()
}
static assert(test11627());
-/**************************************************
- 11664 - ignore function local static variables
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11664
+// ignore function local static variables
bool test11664()
{
@@ -7083,9 +7001,9 @@ bool test11664()
}
static assert(test11664());
-/**************************************************
- 12110 - operand of dereferencing does not need to be an lvalue
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12110
+// operand of dereferencing does not need to be an lvalue
struct SliceOverIndexed12110
{
@@ -7117,9 +7035,9 @@ struct Uint24Array12110
static m12110 = Uint24Array12110([0x80]);
-/**************************************************
- 12310 - heap allocation for built-in sclar types
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12310
+// heap allocation for built-in sclar types
bool test12310()
{
@@ -7141,9 +7059,9 @@ bool test12310()
}
static assert(test12310());
-/**************************************************
- 12499 - initialize TupleDeclaraion in CTFE
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12499
+// initialize TupleDeclaraion in CTFE
auto f12499()
{
@@ -7153,9 +7071,9 @@ auto f12499()
}
static assert(f12499() == 5);
-/**************************************************
- 12602 - slice in struct literal members
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12602
+// slice in struct literal members
struct Result12602
{
@@ -7251,9 +7169,9 @@ static assert(testWrap12602b() == [1,2,1,2]);
static assert(testWrap12602c() == [1,2,1,2]);
static assert(testWrap12602d() == [1,2,1,2]);
-/**************************************************
- 12677 - class type initializing from DotVarExp
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12677
+// class type initializing from DotVarExp
final class C12677
{
@@ -7272,9 +7190,9 @@ struct S12677
auto f = new C12677();
}
-/**************************************************
- 12851 - interpret function local const static array
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12851
+// interpret function local const static array
void test12851()
{
@@ -7282,9 +7200,9 @@ void test12851()
alias staticZip = TypeTuple!(arr[0]);
}
-/**************************************************
- 13630 - indexing and setting array element via pointer
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13630
+// indexing and setting array element via pointer
struct S13630(T)
{
@@ -7303,15 +7221,14 @@ struct S13630(T)
enum s13630 = S13630!float(1);
-/**************************************************
- 13827
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13827
struct Matrix13827(T, uint N)
{
private static defaultMatrix()
{
- T arr[N];
+ T[N] arr;
return arr;
}
@@ -7331,9 +7248,9 @@ struct Matrix13827(T, uint N)
}
enum m13827 = Matrix13827!(int, 3)(1, 2, 3);
-/**************************************************
- 13847 - support DotTypeExp
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13847
+// support DotTypeExp
class B13847
{
@@ -7378,9 +7295,9 @@ static assert({
return true;
}());
-/**************************************************
- 12495 - cast from string to immutable(ubyte)[]
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12495
+// cast from string to immutable(ubyte)[]
string getStr12495()
{
@@ -7401,9 +7318,9 @@ auto indexOf12495(string s)
}
static assert(indexOf12495(getStr12495()) == 0);
-/**************************************************
- 13992 - Repainting pointer arithmetic result
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13992
+// Repainting pointer arithmetic result
enum hash13992 = hashOf13992("abcd".ptr);
@@ -7416,9 +7333,9 @@ enum hash13992 = hashOf13992("abcd".ptr);
return hash;
}
-/**************************************************
- 13739 - Precise copy for ArrayLiteralExp elements
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13739
+// Precise copy for ArrayLiteralExp elements
static assert(
{
@@ -7436,9 +7353,9 @@ static assert(
return 1;
}());
-/**************************************************
- 14463 - ICE on slice assignment without postblit
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14463
+// ICE on slice assignment without postblit
struct Boo14463
{
@@ -7450,9 +7367,9 @@ struct Boo14463
}
immutable Boo14463 a14463 = Boo14463([1]);
-/**************************************************
- 13295 - Don't copy struct literal in VarExp::interpret()
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13295
+// Don't copy struct literal in VarExp::interpret()
struct S13295
{
@@ -7483,9 +7400,9 @@ int foo14061(int[] a)
}
static assert(foo14061([1]));
-/**************************************************
- 14024 - CTFE version
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14024
+// CTFE version
bool test14024()
{
@@ -7535,9 +7452,9 @@ bool test14024()
}
static assert(test14024());
-/**************************************************
- 14304 - cache of static immutable value
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14304
+// cache of static immutable value
immutable struct Bug14304
{
@@ -7565,9 +7482,9 @@ void test14304()
static assert(bt == "fun");
}
-/**************************************************
- 14371 - evaluate BinAssignExp as lvalue
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14371
+// evaluate BinAssignExp as lvalue
int test14371()
{
@@ -7577,9 +7494,9 @@ int test14371()
}
static assert(test14371() == 2);
-/**************************************************
- 7151 - [CTFE] cannot compare classes with ==
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7151
+// [CTFE] cannot compare classes with ==
bool test7151()
{
@@ -7589,9 +7506,9 @@ bool test7151()
static assert(test7151());
-/**************************************************
- 12603 - [CTFE] goto does not correctly call dtors
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12603
+// [CTFE] goto does not correctly call dtors
struct S12603
{
@@ -7702,9 +7619,9 @@ auto structInCaseScope()
static assert(!structInCaseScope());
-/**************************************************
- 15233 - ICE in TupleExp, Copy On Write bug
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15233
+// ICE in TupleExp, Copy On Write bug
alias TT15233(stuff ...) = stuff;
@@ -7713,23 +7630,23 @@ enum tup15233 = TT15233!(Tok15233(), "foo");
static assert(tup15233[0] == Tok15233());
static assert(tup15233[1] == "foo");
-/**************************************************
- 15251 - void cast in ForStatement.increment
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15251
+// void cast in ForStatement.increment
int test15251()
{
for (ubyte lwr = 19;
lwr != 20;
- cast(void)++lwr) // have to to be evaluated with ctfeNeedNothing
+ cast(void)++lwr) // have to to be evaluated with CTFEGoal.Nothing
{}
return 1;
}
static assert(test15251());
-/**************************************************
- 15998 - Sagfault caused by memory corruption
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15998
+// Sagfault caused by memory corruption
immutable string[2] foo15998 = ["",""];
immutable string[2][] bar15998a = foo15998 ~ baz15998;
@@ -7744,9 +7661,9 @@ auto baz15998()
static assert(bar15998a == [["", ""]]);
static assert(bar15998b == [["", ""]]);
-/**************************************************
- 16094 - Non-overlapped slice assignment on an aggregate
-**************************************************/
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=16094
+// Non-overlapped slice assignment on an aggregate
char[] f16094a()
{
@@ -7787,6 +7704,94 @@ struct RBNode(T)
static assert(!__traits(compiles, { alias bug18057 = RBNode!int; }));
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19140
+
+void test19140()
+{
+ real f19140();
+ static if (__traits(compiles, (){ enum real r = f19140(); })) {}
+}
+
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19074
+
+struct S19074a { }
+
+struct S19074b
+{
+ S19074a field;
+ this(S19074a) { }
+
+ static const S19074b* data = new S19074b(S19074a());
+}
+
+void test19074()
+{
+ auto var = S19074b.data;
+}
+
+/************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19447
+
+bool f19447()
+{
+ int[3] c=1;
+ assert(c[0]==1);
+ g19447(c[0..2]);
+ assert(c[0]!=1); //fails
+ assert(c[0]==2);
+ return true;
+}
+void g19447(ref int[2] a)
+{
+ int[2] b=2;
+ a=b;
+ //a[]=b; // works
+ //a[] = b[]; // works
+ assert(a[0]==2);
+}
+static assert(f19447());
+
+/***/
+
+char[] mangle19447(char[] dst)
+{
+ dst.length = 10;
+ size_t i = "_D".length;
+ dst[0 .. i] = "_D";
+ return dst;
+}
+
+static char[] x19447 = mangle19447(null);
+
+/***/
+enum KindEnum
+{
+ integer,
+ arrayOf
+}
+
+struct FullKind
+{
+ KindEnum[] contents;
+
+ this(KindEnum ) { opAssign; }
+
+ this(KindEnum , FullKind contentKind)
+ {
+ contents = contentKind.contents;
+ }
+
+ void opAssign()
+ {
+ contents = [];
+ }
+}
+
+enum fk = FullKind(KindEnum.integer);
+enum fk2 = FullKind(KindEnum.arrayOf, fk);
+
/************************************************/
// https://issues.dlang.org/show_bug.cgi?id=9937
diff --git a/gcc/testsuite/gdc.test/compilable/interpret4.d b/gcc/testsuite/gdc.test/compilable/interpret4.d
new file mode 100644
index 00000000000..019ff226ea8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/interpret4.d
@@ -0,0 +1,31 @@
+// https://issues.dlang.org/show_bug.cgi?id=11717
+
+enum int[4] A = [1,2,3,4];
+enum int[4] B = [1,2,3,4];
+enum int[4] C = A[] + B[];
+static assert(C == [2, 4, 6, 8]);
+
+enum int[2] D1 = A[1..3] * B[2..4];
+static assert(D1 == [6, 12]);
+
+enum int[2] D2 = A[1..3] * 6;
+static assert(D2 == [12, 18]);
+
+enum int[2] D3 = 5 - A[1..3];
+static assert(D3 == [3, 2]);
+
+enum int[2][2] D4 = [D1, D2] + [D2, D3];
+static assert(D4 == [[18, 30], [15, 20]]);
+
+enum int[2][2] D5 = [[18, 30], [15, 20]] + [12, 18];
+static assert(D5 == [[30, 48], [27, 38]]);
+
+import core.simd;
+
+static if (__traits(compiles, int4))
+{
+ enum int4 D = [1,2,3,4];
+ enum int4 E = [1,2,3,4];
+ enum int4 F = D * E;
+ static assert(F.array == [1, 4, 9, 16]);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/isZeroInit.d b/gcc/testsuite/gdc.test/compilable/isZeroInit.d
index b5423cfb636..a64420febc0 100644
--- a/gcc/testsuite/gdc.test/compilable/isZeroInit.d
+++ b/gcc/testsuite/gdc.test/compilable/isZeroInit.d
@@ -72,7 +72,9 @@ struct S5
}
static assert(__traits(isZeroInit, S5));
-version(D_SIMD):
-import core.simd : int4;
-static assert(__traits(isZeroInit, Holder!(int4, 0)));
-static assert(!__traits(isZeroInit, Holder!(int4, 1)));
+template Vector(T) { alias __vector(T) Vector; }
+static if (is(Vector!(int[4])))
+{
+ static assert(__traits(isZeroInit, Holder!(Vector!(int[4]), 0)));
+ static assert(!__traits(isZeroInit, Holder!(Vector!(int[4]), 1)));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/isreturnonstack.d b/gcc/testsuite/gdc.test/compilable/isreturnonstack.d
index 8bdb97de13f..920f0ec5011 100644
--- a/gcc/testsuite/gdc.test/compilable/isreturnonstack.d
+++ b/gcc/testsuite/gdc.test/compilable/isreturnonstack.d
@@ -1,3 +1,4 @@
+
struct S { int[10] a; }
int test1();
S test2();
diff --git a/gcc/testsuite/gdc.test/compilable/issue12520.d b/gcc/testsuite/gdc.test/compilable/issue12520.d
new file mode 100644
index 00000000000..90077248d21
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue12520.d
@@ -0,0 +1,21 @@
+module issue12520;
+
+// https://issues.dlang.org/show_bug.cgi?id=12520
+
+alias Seq(T...) = T;
+
+static assert( Seq!() == Seq!() );
+static assert( Seq!(1) == Seq!(1) );
+
+static assert((Seq!() != Seq!()) == false);
+static assert((Seq!(1) != Seq!(1)) == false);
+
+static assert( Seq!() != Seq!(1) );
+static assert( Seq!(1) != Seq!() );
+static assert( Seq!(0) != Seq!(1) );
+static assert( Seq!(0,1) != Seq!() );
+
+static assert((Seq!() == Seq!(1)) == false);
+static assert((Seq!(1) == Seq!()) == false);
+static assert((Seq!(0) == Seq!(1)) == false);
+static assert((Seq!(0,1) == Seq!()) == false);
diff --git a/gcc/testsuite/gdc.test/compilable/issue15478.d b/gcc/testsuite/gdc.test/compilable/issue15478.d
new file mode 100644
index 00000000000..ad24e7f5156
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue15478.d
@@ -0,0 +1,55 @@
+////////////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=15478
+
+void test15478_1()
+{
+ struct Foo(N)
+ {
+ this(N value) { }
+ static int bug() { return 0; }
+ }
+ enum Foo!int foo = 0;
+ Foo!int[foo.bug] bar;
+}
+
+void test15478_2()
+{
+ int getLength() { return 42; }
+ struct Get {static int length() { return 42; }}
+
+ int[getLength] i1;
+ int[Get.length] i2;
+ static assert (is(typeof(i1) == int[42]));
+ static assert (is(typeof(i2) == int[42]));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=21870
+struct S21870
+{
+ @property size_t count() const
+ {
+ return 1;
+ }
+}
+
+int[S21870.init.count()] x; // OK
+int[S21870.init.count ] y; // error
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct Foo15478(N)
+{
+ this(N value) { }
+ auto bug() { return 0; }
+}
+
+void test15478_3()
+{
+ enum Foo15478!int foo = 0;
+ Foo15478!int[foo.bug] bar; // Error: integer constant expression expected instead of Foo().bug
+
+ enum foo_bug = foo.bug;
+ Foo15478!int[foo_bug] baz; // OK
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/issue15795.d b/gcc/testsuite/gdc.test/compilable/issue15795.d
new file mode 100644
index 00000000000..08ed49555af
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue15795.d
@@ -0,0 +1,9 @@
+module issue15795;
+
+template Foo(T : int) {}
+template Bar(T) {}
+alias Bar = Foo;
+
+alias bi = Bar!int;
+alias fi = Foo!int;
+static assert(__traits(isSame, bi, fi));
diff --git a/gcc/testsuite/gdc.test/compilable/issue15818.d b/gcc/testsuite/gdc.test/compilable/issue15818.d
new file mode 100644
index 00000000000..4aed650fb29
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue15818.d
@@ -0,0 +1,15 @@
+module issue15818;
+
+void f(int);
+void f(int);
+void f(int);
+void f(int);
+void f(int);
+
+pragma(mangle, "_D10issue158181fFiZv")
+void theVeritableF(int){}
+
+void main()
+{
+ f(1);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue18097.d b/gcc/testsuite/gdc.test/compilable/issue18097.d
new file mode 100644
index 00000000000..0bc16135129
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue18097.d
@@ -0,0 +1,12 @@
+// REQUIRED_ARGS: -unittest
+module issue18097;
+
+unittest // this first unittest is needed to trigger the bug
+{
+}
+
+unittest // second unittest
+{
+ auto a = &mixin(__traits(identifier, __traits(parent, { })));
+ auto b = &__traits(parent, { });
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue19925.d b/gcc/testsuite/gdc.test/compilable/issue19925.d
new file mode 100644
index 00000000000..aed1688adee
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue19925.d
@@ -0,0 +1,14 @@
+// REQUIRED_ARGS: -unittest
+module issue19925;
+
+unittest {
+ with (S) {
+ a(); // Compiles!
+ b(); // Fails!
+ }
+}
+
+struct S {
+ static void a() {}
+ static void opDispatch(string name)() {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue20362.d b/gcc/testsuite/gdc.test/compilable/issue20362.d
new file mode 100644
index 00000000000..b24bb8da354
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue20362.d
@@ -0,0 +1,8 @@
+void main() {
+ string str;
+ stringify((chars) {str ~= chars; });
+}
+
+void stringify(scope void delegate(scope const char[]) sink) {
+ sink("oops");
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue20599.d b/gcc/testsuite/gdc.test/compilable/issue20599.d
new file mode 100644
index 00000000000..d3b614a0459
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue20599.d
@@ -0,0 +1,10 @@
+import core.stdc.config;
+
+enum A : cpp_long;
+enum B : cpp_longlong;
+
+enum C : cpp_long { a,b,c };
+enum D : cpp_longlong { a,b,c };
+
+enum : cpp_long { a,b,c };
+enum : cpp_longlong { d,e,f };
diff --git a/gcc/testsuite/gdc.test/compilable/issue20704.d b/gcc/testsuite/gdc.test/compilable/issue20704.d
new file mode 100644
index 00000000000..ec95828007f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue20704.d
@@ -0,0 +1,29 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=20704
+REQUIRED_ARGS: -preview=rvaluerefparam
+ */
+
+void f1(T)(const auto ref T arg = T.init) {}
+void f2(T)(const ref T arg = T.init) {}
+void f3(T)(const auto ref T arg = 0) {}
+void f4(T)(const ref T arg = 0) {}
+
+struct S { int _; }
+class C { int _; }
+
+void main ()
+{
+ int i;
+ f1!int(i);
+ f2!int(i);
+ f3!int(i);
+ f4!int(i);
+ f1!int();
+ f2!int();
+ f3!int();
+ f4!int();
+ f1!S();
+ f2!S();
+ f1!C();
+ f2!C();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue20705.d b/gcc/testsuite/gdc.test/compilable/issue20705.d
new file mode 100644
index 00000000000..76a364e129b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue20705.d
@@ -0,0 +1,16 @@
+// REQUIRED_ARGS: -preview=rvaluerefparam
+struct Foo
+{
+ int[] a;
+}
+
+void bar (T) (const ref T arg) {}
+T foo (T) (ref T arg) { return arg; }
+void goo()(ref long x) { x = 1; }
+void main ()
+{
+ bar(Foo([42]));
+ auto x = foo(Foo([42]));
+ int y;
+ static assert(!__traits(compiles, goo(y)));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue20995.d b/gcc/testsuite/gdc.test/compilable/issue20995.d
new file mode 100644
index 00000000000..7b29587dbc7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue20995.d
@@ -0,0 +1,12 @@
+/*
+REQUIRED_ARGS: -preview=dip1021
+
+https://issues.dlang.org/show_bug.cgi?id=20995
+*/
+
+void foo() @live
+{
+ throw new Exception("");
+}
+
+void main () {}
diff --git a/gcc/testsuite/gdc.test/compilable/issue21328.d b/gcc/testsuite/gdc.test/compilable/issue21328.d
new file mode 100644
index 00000000000..91dc0ba0e2c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue21328.d
@@ -0,0 +1,8 @@
+
+static foreach(i; 1 .. 5)
+void foo(float[i] arr)
+{
+ () {
+ float x = arr[0];
+ } ();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue21378.d b/gcc/testsuite/gdc.test/compilable/issue21378.d
new file mode 100644
index 00000000000..007f39bef86
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue21378.d
@@ -0,0 +1,33 @@
+// https://issues.dlang.org/show_bug.cgi?id=21378
+
+version(all)
+ enum do_inline = true;
+
+pragma(inline, do_inline)
+void stuff(){}
+
+void stuff2()
+{
+ pragma(inline, do_inline);
+}
+
+pragma(inline, canInline(1))
+void stuff3(){}
+
+void stuff4()
+{
+ pragma(inline, canInline(1));
+}
+
+void main()
+{
+ stuff();
+ stuff2();
+ stuff3();
+ stuff4();
+}
+
+int canInline(int x)
+{
+ return x*x;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue21662.d b/gcc/testsuite/gdc.test/compilable/issue21662.d
new file mode 100644
index 00000000000..bb2e7412e36
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue21662.d
@@ -0,0 +1,4 @@
+struct S { @disable this(); }
+extern __gshared S a;
+extern S[2] b;
+void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/issue21726.d b/gcc/testsuite/gdc.test/compilable/issue21726.d
index c8a86752a56..f687751aa7a 100644
--- a/gcc/testsuite/gdc.test/compilable/issue21726.d
+++ b/gcc/testsuite/gdc.test/compilable/issue21726.d
@@ -1,2 +1,3 @@
// EXTRA_SOURCES: protection/issue21726/typecons.d
+// EXTRA_FILES: protection/issue21726/format/package.d protection/issue21726/package.d
// https://issues.dlang.org/show_bug.cgi?id=21726
diff --git a/gcc/testsuite/gdc.test/compilable/issue21880.d b/gcc/testsuite/gdc.test/compilable/issue21880.d
new file mode 100644
index 00000000000..183f3499853
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue21880.d
@@ -0,0 +1,23 @@
+// REQUIRED_ARGS: -preview=dip1000
+// https://issues.dlang.org/show_bug.cgi?id=21880
+extern(C++):
+void spawnProcess(scope const(char*)*, File = File()) @safe
+{
+}
+
+void pipeProcess(scope const(char*)* args) @safe
+{
+ pipeProcessImpl!spawnProcess(args);
+}
+
+void pipeProcessImpl(alias spawnFunc, Cmd)(Cmd command) @trusted
+{
+ spawnFunc(command);
+}
+
+struct File
+{
+ ~this() @safe
+ {
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue21882.d b/gcc/testsuite/gdc.test/compilable/issue21882.d
new file mode 100644
index 00000000000..87a166a690c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue21882.d
@@ -0,0 +1,14 @@
+// REQUIRED_ARGS: -preview=dip1021
+// https://issues.dlang.org/show_bug.cgi?id=21882
+bool buildPath()
+{
+ struct ByCodeUnitImpl
+ {
+ auto opIndex(size_t) { }
+ }
+ return isRandomAccessRange!ByCodeUnitImpl;
+}
+
+enum isRandomAccessRange(R) = is(typeof(lvalueOf!R[1]));
+
+ref T lvalueOf(T)();
diff --git a/gcc/testsuite/gdc.test/compilable/issue21905.d b/gcc/testsuite/gdc.test/compilable/issue21905.d
new file mode 100644
index 00000000000..76a04d8d6d3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue21905.d
@@ -0,0 +1,24 @@
+module issue21905;
+
+struct Conv
+{
+ StaticIterable b;
+ alias b this;
+}
+
+struct StaticIterable
+{
+ static Conv b;
+ alias b this;
+}
+
+void each(ref StaticIterable r)
+{
+ return ;
+}
+
+void main()
+{
+ StaticIterable s;
+ each(s);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/issue9884.d b/gcc/testsuite/gdc.test/compilable/issue9884.d
new file mode 100644
index 00000000000..e799c49c884
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue9884.d
@@ -0,0 +1,25 @@
+// https://issues.dlang.org/show_bug.cgi?id=9884
+module issue9884;
+
+const(int)[] data;
+
+static this()
+{
+ data = new int[10];
+ foreach (ref x; data) x = 1;
+ data[] = 1;
+}
+
+struct Foo
+{
+ static const(int)[] data;
+
+ static this()
+ {
+ this.data = new int[10];
+ foreach (ref x; this.data) x = 1;
+ this.data[] = 1;
+ }
+}
+
+void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/json.d b/gcc/testsuite/gdc.test/compilable/json.d
index e2b08605893..f79639d6b3a 100644
--- a/gcc/testsuite/gdc.test/compilable/json.d
+++ b/gcc/testsuite/gdc.test/compilable/json.d
@@ -1,28 +1,47 @@
// PERMUTE_ARGS:
-// REQUIRED_ARGS: -o- -X -Xf${RESULTS_DIR}/compilable/json.out
-// POST_SCRIPT: compilable/extra-files/json-postscript.sh
+// REQUIRED_ARGS: -d -preview=dip1000 -o- -X -Xf-
// EXTRA_FILES: imports/jsonimport1.d imports/jsonimport2.d imports/jsonimport3.d imports/jsonimport4.d
-
+// TRANSFORM_OUTPUT: sanitize_json
+// TEST_OUTPUT_FILE: extra-files/json.json
module json;
-
+shared static this() {}
static this() {}
-
+shared static ~this() {}
static ~this() {}
+template X(T)
+{
+ shared static this() {}
+ static this() {}
+ shared static ~this() {}
+ static ~this() {}
+}
+
+alias SSCDX = X!int;
+
+class SSCDClass
+{
+ shared static this() {}
+ static this() {}
+ shared static ~this() {}
+ static ~this() {}
+}
+
+#line 17
alias int myInt;
-myInt x; // bug 3404
+myInt x; // https://issues.dlang.org/show_bug.cgi?id=3404
struct Foo(T) { T t; }
class Bar(int T) { int t = T; }
-interface Baz(T...) { T[0] t() const; } // bug 3466
+interface Baz(T...) { T[0] t() const; } // https://issues.dlang.org/show_bug.cgi?id=3466
template P(alias T) {}
class Bar2 : Bar!1, Baz!(int, 2, null) {
this() {}
- ~this() {} // bug 4178
+ ~this() {} // https://issues.dlang.org/show_bug.cgi?id=4178
static foo() {}
protected abstract Foo!int baz();
@@ -30,48 +49,65 @@ class Bar2 : Bar!1, Baz!(int, 2, null) {
}
class Bar3 : Bar2 {
- private int val;
+ private int val;
this(int i) { val = i; }
protected override Foo!int baz() { return Foo!int(val); }
}
struct Foo2 {
- Bar2 bar2;
- union U {
- struct {
- short s;
- int i;
- }
- Object o;
- }
+ Bar2 bar2;
+ union U {
+ struct {
+ short s;
+ int i;
+ }
+ Object o;
+ }
+}
+
+struct Foo3(bool b) {
+ version(D_Ddoc) {
+ /// Doc 1
+ void method1();
+ }
+ static if (b) {
+ /// Doc 2
+ void method2();
+ } else {
+ /// Doc 3
+ void method3();
+ }
+
+ /// Doc 4
+ void method4();
}
/++
+ Documentation test
+/
-@trusted myInt bar(ref uint blah, Bar2 foo = new Bar3(7)) // bug 4477
+@trusted myInt bar(ref uint blah, Bar2 foo = new Bar3(7)) // https://issues.dlang.org/show_bug.cgi?id=4477
{
- return -1;
+ return -1;
}
@property int outer() nothrow
in {
- assert(true);
+ assert(true);
}
out(result) {
- assert(result == 18);
+ assert(result == 18);
}
-body {
- int x = 8;
- int inner(void* v) nothrow
- {
- int y = 2;
- assert(true);
- return x + y;
- }
- int z = inner(null);
- return x + z;
+do {
+ int x = 8;
+ int inner(void* v) nothrow
+ {
+ int y = 2;
+ assert(true);
+ return x + y;
+ }
+ int z = inner(null);
+ return x + z;
}
/** Issue 9484 - selective and renamed imports */
@@ -121,9 +157,60 @@ alias Seq(T...) = T;
static foreach(int i, alias a; Seq!(a0, a1, a2))
{
- mixin("alias b" ~ i.stringof ~ " = a;");
+ mixin("alias b" ~ i.stringof ~ " = a;");
+}
+
+// return ref, return scope, return ref scope
+ref int foo(return ref int a) @safe
+{
+ return a;
}
+int* foo(return scope int* a) @safe
+{
+ return a;
+}
+
+ref int* foo(scope return ref int* a) @safe
+{
+ return a;
+}
+
+struct SafeS
+{
+@safe:
+ ref SafeS foo() return
+ {
+ return this;
+ }
+
+ SafeS foo2() return scope
+ {
+ return this;
+ }
+
+ ref SafeS foo3() return scope
+ {
+ return this;
+ }
+
+ int* p;
+}
+
+extern int vlinkageDefault;
+extern(D) int vlinkageD;
+extern(C) int vlinakgeC;
+extern(C++) __gshared int vlinkageCpp;
+extern(Windows) int vlinkageWindows;
+extern(Objective-C) int vlinkageObjc;
+
+extern int flinkageDefault();
+extern(D) int flinkageD();
+extern(C) int linakgeC();
+extern(C++) int flinkageCpp();
+extern(Windows) int flinkageWindows();
+extern(Objective-C) int flinkageObjc();
+
mixin template test18211(int n)
{
static foreach (i; 0 .. n>10 ? 10 : n)
@@ -132,3 +219,5 @@ mixin template test18211(int n)
}
static if (true) {}
}
+
+alias F = size_t function (size_t a);
diff --git a/gcc/testsuite/gdc.test/compilable/json20742.d b/gcc/testsuite/gdc.test/compilable/json20742.d
new file mode 100644
index 00000000000..3e91dee40cf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/json20742.d
@@ -0,0 +1,69 @@
+/*
+REQUIRED_ARGS: -Xf- -o- -version=Showme
+PERMUTE_ARGS:
+TEST_OUTPUT:
+----
+[
+ {
+ "kind" : "module",
+ "file" : "compilable$?:windows=\\|/$json20742.d",
+ "members" : [
+ {
+ "name" : "X1",
+ "kind" : "struct",
+ "protection" : "private",
+ "line" : 52,
+ "char" : 13,
+ "members" : []
+ },
+ {
+ "name" : "Y2",
+ "kind" : "struct",
+ "protection" : "private",
+ "line" : 59,
+ "char" : 13,
+ "members" : []
+ },
+ {
+ "name" : "A1",
+ "kind" : "struct",
+ "protection" : "private",
+ "line" : 62,
+ "char" : 13,
+ "members" : []
+ },
+ {
+ "name" : "B2",
+ "kind" : "struct",
+ "protection" : "private",
+ "line" : 69,
+ "char" : 13,
+ "members" : []
+ }
+ ]
+ }
+]
+----
+
+https://issues.dlang.org/show_bug.cgi?id=20742
+*/
+
+version(Showme)
+ private struct X1 {}
+else
+ private struct X2 {}
+
+version(Hideme)
+ private struct Y1 {}
+else
+ private struct Y2 {}
+
+static if (true)
+ private struct A1 {}
+else
+ private struct A2 {}
+
+static if (false)
+ private struct B1 {}
+else
+ private struct B2 {}
diff --git a/gcc/testsuite/gdc.test/compilable/minimal.d b/gcc/testsuite/gdc.test/compilable/minimal.d
new file mode 100644
index 00000000000..63983288f30
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/minimal.d
@@ -0,0 +1,19 @@
+// DFLAGS:
+// PERMUTE_ARGS:
+// POST_SCRIPT: compilable/extra-files/minimal/verify_symbols.sh
+// REQUIRED_ARGS: -defaultlib=
+// EXTRA_SOURCES: extra-files/minimal/object.d
+
+// This test ensures an empty main with a struct and enum, built with a minimal
+// runtime, does not generate ModuleInfo or exception handling code, and does not
+// require TypeInfo
+
+struct S { }
+
+enum E
+{
+ e0 = 0,
+ e1 = 1
+}
+
+void main() { }
diff --git a/gcc/testsuite/gdc.test/compilable/minimal2.d b/gcc/testsuite/gdc.test/compilable/minimal2.d
new file mode 100644
index 00000000000..d69eb92c16b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/minimal2.d
@@ -0,0 +1,31 @@
+// DFLAGS:
+// REQUIRED_ARGS: -defaultlib=
+// EXTRA_SOURCES: extra-files/minimal/object.d
+
+// This test ensures that interfaces and classes can be used in a minimal
+// runtime as long as they only contain static members.
+
+// This should compile, but will not link and run properly without
+// a thread-local storage (TLS) implementation.
+
+interface I
+{
+ static int i;
+}
+
+class A : I
+{
+ static int a;
+}
+
+class B : A
+{
+ static int b;
+}
+
+void main()
+{
+ B.i = 32;
+ B.a = 42;
+ B.b = 52;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/mixin.d b/gcc/testsuite/gdc.test/compilable/mixin.d
new file mode 100644
index 00000000000..d96eae14093
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/mixin.d
@@ -0,0 +1,38 @@
+/*
+REQUIRED_ARGS: -mixin=${RESULTS_DIR}/runnable/mixin.mixin -o-
+OUTPUT_FILES: ${RESULTS_DIR}/runnable/mixin.mixin
+
+TEST_OUTPUT:
+----
+=== ${RESULTS_DIR}/runnable/mixin.mixin
+// expansion at compilable/mixin.d(14)
+int x =
+ 123;
+
+ int y;
+
+
+
+ int z = x + y;
+----
+
+https://issues.dlang.org/show_bug.cgi?id=1870
+https://issues.dlang.org/show_bug.cgi?id=12790
+*/
+
+#line 1
+string get()
+{
+ return "int x =\n 123;\r\n" ~
+ q{
+ int y;
+
+
+
+ int z = x + y;};
+}
+
+void main()
+{
+ mixin(get());
+}
diff --git a/gcc/testsuite/gdc.test/compilable/mixinTemplateMangling.d b/gcc/testsuite/gdc.test/compilable/mixinTemplateMangling.d
new file mode 100644
index 00000000000..3596a95c037
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/mixinTemplateMangling.d
@@ -0,0 +1,35 @@
+// https://issues.dlang.org/show_bug.cgi?id=20012
+
+mixin template mixinFoo() {
+
+ extern(C) void cFoo() {}
+
+ extern(C) int cVar;
+ extern(D) int dVar;
+
+ void dFoo() {}
+
+ mixin(`mixin mixinBar;`); // test nesting and interaction with string mixins
+}
+
+mixin mixinFoo;
+
+mixin template mixinBar() {
+ extern(C) void cBar() {}
+ void dBar() {}
+}
+
+static assert(cFoo.mangleof == "cFoo");
+static assert(dFoo.mangleof == "_D21mixinTemplateMangling8__mixin54dFooFZv");
+static assert(cVar.mangleof == "cVar");
+static assert(dVar.mangleof == "_D21mixinTemplateMangling8__mixin54dVari");
+static assert(cBar.mangleof == "cBar");
+static assert(dBar.mangleof == "_D21mixinTemplateMangling8__mixin5Qj4dBarFZv");
+
+struct S {
+ mixin mixinFoo;
+ static assert(cFoo.mangleof == "_D21mixinTemplateMangling1S8__mixin14cFooMUZv");
+ static assert(cBar.mangleof == "_D21mixinTemplateMangling1S8__mixin18__mixin54cBarMUZv");
+ static assert(dBar.mangleof == "_D21mixinTemplateMangling1S8__mixin18__mixin54dBarMFZv");
+ static assert(dFoo.mangleof == "_D21mixinTemplateMangling1S8__mixin14dFooMFZv");
+}
diff --git a/gcc/testsuite/gdc.test/compilable/mixintempl.d b/gcc/testsuite/gdc.test/compilable/mixintempl.d
new file mode 100644
index 00000000000..d897a17d529
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/mixintempl.d
@@ -0,0 +1,22 @@
+
+struct TypeObj
+{
+ alias This = typeof(this);
+
+ mixin template MixinTempl()
+ {
+ int value;
+ }
+}
+
+ref TypeObj Obj()
+{
+ static TypeObj a;
+ return a;
+}
+
+void func()
+{
+ mixin Obj.This.MixinTempl; // ok
+ mixin Obj.MixinTempl; // fixed: "MixinTempl!()" is not defined
+}
diff --git a/gcc/testsuite/gdc.test/compilable/nestedtempl0.d b/gcc/testsuite/gdc.test/compilable/nestedtempl0.d
new file mode 100644
index 00000000000..aa6826b5039
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/nestedtempl0.d
@@ -0,0 +1,13 @@
+class C2
+{
+ class N(alias a) {}
+}
+
+void test2()
+{
+ int a;
+ static assert(__traits(isSame, __traits(parent, C2.N!0), C2));
+ static assert(__traits(isSame, __traits(parent, C2.N!a), C2));
+ static assert(__traits(classInstanceSize, C2.N!0) == size_t.sizeof * 3);
+ static assert(__traits(classInstanceSize, C2.N!a) == size_t.sizeof * 4);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/nestedtempl1.d b/gcc/testsuite/gdc.test/compilable/nestedtempl1.d
new file mode 100644
index 00000000000..ecb8e833ee8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/nestedtempl1.d
@@ -0,0 +1,25 @@
+class K
+{
+ class B(alias a)
+ {
+
+ }
+
+ class D(alias a) : B!a
+ {
+
+ }
+
+ class E(alias a) : B!1
+ {
+
+ }
+}
+
+void main()
+{
+ int a;
+ auto k = new K;
+ auto d = k.new K.D!a;
+ auto e = k.new K.E!a;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/nogc.d b/gcc/testsuite/gdc.test/compilable/nogc.d
index c2581da9c26..27518019d6d 100644
--- a/gcc/testsuite/gdc.test/compilable/nogc.d
+++ b/gcc/testsuite/gdc.test/compilable/nogc.d
@@ -52,7 +52,7 @@ void foo_compiles() {}
}
/******************************************/
-// 12630
+// https://issues.dlang.org/show_bug.cgi?id=12630
void test12630() @nogc
{
@@ -86,7 +86,7 @@ void test12630() @nogc
}
/******************************************/
-// 12642
+// https://issues.dlang.org/show_bug.cgi?id=12642
static if (is(__vector(ulong[2])))
{
@@ -99,7 +99,7 @@ static if (is(__vector(ulong[2])))
}
/******************************************/
-// 13550
+// https://issues.dlang.org/show_bug.cgi?id=13550
auto foo13550() @nogc
{
diff --git a/gcc/testsuite/gdc.test/compilable/noreturn1.d b/gcc/testsuite/gdc.test/compilable/noreturn1.d
index e21adc476c9..7517bb26e0d 100644
--- a/gcc/testsuite/gdc.test/compilable/noreturn1.d
+++ b/gcc/testsuite/gdc.test/compilable/noreturn1.d
@@ -1,21 +1,66 @@
/*
+REQUIRED_ARGS: -w
TEST_OUTPUT:
---
noreturn
---
+
+Basic properties and usage mentioned in the DIP:
+https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1034.md
*/
alias noreturn = typeof(*null);
pragma(msg, noreturn);
-noreturn exits(int* p) { *p = 3; }
+static assert(!is(noreturn == void));
+
+// Fails
+// static assert(is( typeof([]) == noreturn[] ));
+// static assert(is( typeof([][0]) == noreturn ));
+
+static assert(is( typeof(assert(0)) == noreturn ));
+
+// Does not parse yet
+// static assert(is( typeof(throw new Exception()) == noreturn ));
+
+static assert(is(noreturn == noreturn));
+static assert(!is(noreturn == const noreturn));
+static assert(is(noreturn : const noreturn));
+
+static assert(!is(noreturn == int));
+static assert(is(noreturn : int));
+
+// Covariance
+static assert(is(noreturn[] : int[]));
+static assert(is(noreturn* : int*));
+static assert(is(noreturn function() : int function()));
+static assert(is(noreturn delegate() : int delegate()));
+
+// Reject inverse conversions
+static assert(!is(int[] : noreturn[]));
+static assert(!is(int* : noreturn*));
+static assert(!is(int function() : noreturn function()));
+static assert(!is(int delegate() : noreturn delegate()));
+
+static assert(noreturn.mangleof == "Nn"); // Changed from b due to conflicts with bool
+static assert(noreturn.sizeof == 0);
+static assert(noreturn.alignof == 0);
+
+static assert((noreturn*).sizeof == (int*).sizeof);
+static assert((noreturn[]).sizeof == (int[]).sizeof);
+
+version (DigitalMars)
+ noreturn exits(int* p) { *p = 3; }
noreturn exit();
+noreturn pureexits() @nogc nothrow pure @safe { assert(0); }
+
+noreturn callpureexits() { pureexits(); }
+
int test1(int i)
{
if (exit())
return i + 1;
return i - 1;
}
-
diff --git a/gcc/testsuite/gdc.test/compilable/ob1.d b/gcc/testsuite/gdc.test/compilable/ob1.d
new file mode 100644
index 00000000000..720c765eda3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/ob1.d
@@ -0,0 +1,149 @@
+// REQUIRED_ARGS: -preview=dip1021
+
+/* Should compile successfully
+ */
+
+
+struct Allocation {
+ int* ptr;
+ size_t length;
+}
+
+void canFind(scope Allocation);
+
+int* malloc();
+void free(int*);
+void pitcher();
+void borrow(scope int*);
+void borrow2c(const scope int*, const scope int*);
+void out1(out int*);
+
+
+/*****************************/
+
+@live int* foo1(int* p)
+{
+ return p; // consumes owner
+}
+
+@live int* foo2()
+{
+ int* p = null;
+ return p; // consumes owner
+}
+
+@live int* foo3(int* p)
+{
+ scope int* q = p; // borrows from p
+ return p; // use of p ends borrow in q
+}
+
+@live int* foo4(int* p)
+{
+ scope int* bq = p; // borrow
+ scope const int* cq = p; // const borrow
+ return p; // ends both borrows
+}
+
+/*******************************/
+
+@live void foo5()
+{
+ auto p = malloc();
+ scope(exit) free(p);
+ pitcher();
+}
+
+/*******************************/
+
+void deallocate(int* ptr, size_t length) @live
+{
+ canFind(Allocation(ptr, length)); // canFind() borrows ptr
+ free(ptr);
+}
+
+
+/*******************************/
+
+
+@live int* test1()
+{
+ auto p = malloc();
+ scope b = p;
+ return p;
+}
+
+@live int* test2()
+{
+ auto p = malloc();
+ auto q = p;
+ return q;
+}
+
+@live void test3()
+{
+ auto p = malloc();
+ free(p);
+}
+
+@live void test4()
+{
+ auto p = malloc();
+ borrow(p);
+ free(p);
+}
+
+@live void test5()
+{
+ auto p = malloc();
+ scope q = p;
+ borrow2c(p, p);
+ free(p);
+}
+
+@live void test6()
+{
+ int* p = void;
+ out1(p); // initialize
+ free(p); // consume
+}
+
+
+/*******************************/
+
+void zoo1(int);
+
+@live void zoo2() {
+ int* p = malloc();
+ zoo1(*p); // does not consume p
+ free(p);
+}
+
+@live void zoo3() {
+ int** p = cast(int**)malloc();
+ free(*p); // consumes p
+}
+
+@live void zoo4() {
+ int[] a = malloc()[0 .. 1];
+ zoo1(a[0]); // does not consume a
+ free(a.ptr); // consumes a
+}
+
+@live void zoo5() {
+ int*[] a = (cast(int**)malloc())[0 .. 1];
+ free(a[0]); // consumes a
+}
+
+struct S { int i; int* p; }
+
+@live void zoo6() {
+ S* s = cast(S*)malloc();
+ zoo1(s.i); // does not consume s
+ free(cast(int*)s);
+}
+
+@live void zoo7() {
+ S* s = cast(S*)malloc();
+ free(s.p); // consumes s
+}
diff --git a/gcc/testsuite/gdc.test/compilable/pr9374.d b/gcc/testsuite/gdc.test/compilable/pr9374.d
new file mode 100644
index 00000000000..912639d40d6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/pr9374.d
@@ -0,0 +1,19 @@
+// REQUIRED_ARGS: -preview=dip1000
+
+// https://github.com/dlang/dmd/pull/9374
+
+struct OnlyResult
+{
+ this(return scope ref int v2);
+
+ void* data;
+}
+
+OnlyResult foo(return scope ref int v2);
+
+OnlyResult only(int y)
+{
+ if (y)
+ return OnlyResult(y);
+ return foo(y);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/pr9383.d b/gcc/testsuite/gdc.test/compilable/pr9383.d
new file mode 100644
index 00000000000..ca6d70fa2e6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/pr9383.d
@@ -0,0 +1,25 @@
+// REQUIRED_ARGS: -preview=dip1000
+// https://github.com/dlang/dmd/pull/9383
+
+void test() @safe
+{
+ int[1] a = [1];
+ cartesianProduct(a[]);
+}
+
+auto cartesianProduct(RR...)(RR ranges)
+{
+ static struct Result
+ {
+ RR current;
+
+ void popFront() scope @safe
+ {
+ foreach (ref r; current)
+ {
+ }
+ }
+ }
+
+ return Result(ranges);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/previewall.d b/gcc/testsuite/gdc.test/compilable/previewall.d
new file mode 100644
index 00000000000..e5ed7a8bafb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/previewall.d
@@ -0,0 +1,10 @@
+// ARG_SETS: -preview=all
+// ARG_SETS: -transition=all
+// ARG_SETS: -revert=all
+import core.stdc.stdio;
+
+void main (string[] args)
+{
+ if (args.length == 42)
+ printf("Hello World\n");
+}
diff --git a/gcc/testsuite/gdc.test/compilable/previewin.d b/gcc/testsuite/gdc.test/compilable/previewin.d
new file mode 100644
index 00000000000..8926fbd6aa7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/previewin.d
@@ -0,0 +1,116 @@
+/* REQUIRED_ARGS: -preview=dip1000 -preview=in -mcpu=native
+ */
+
+import core.stdc.time;
+
+void fun(in int* inParam) @safe;
+static assert([__traits(getParameterStorageClasses, fun, 0)] == ["in"]);
+static assert (is(typeof(fun) P == __parameters) && is(P[0] == const int*));
+
+struct Foo
+{
+ int a;
+ double[100] b;
+}
+void fun2(in Foo inParam) @safe;
+static assert([__traits(getParameterStorageClasses, fun2, 0)] == ["in"]);
+static assert (is(typeof(fun2) P == __parameters) && is(P[0] == const Foo));
+
+void test()
+{
+ withDefaultValue(42);
+ withDefaultValue();
+ withDefaultRef(TimeRef.init);
+ withDefaultRef();
+
+ withInitDefaultValue();
+ withInitDefaultRef();
+}
+
+struct FooBar
+{
+ string toString() const
+ {
+ string result;
+ // Type is const
+ this.toString((in char[] buf) {
+ static assert(is(typeof(buf) == const(char[])));
+ result ~= buf;
+ });
+ // Type inference works
+ this.toString((in buf) { result ~= buf; });
+ return result;
+ }
+
+ void toString(scope void delegate(in char[]) sink) const
+ {
+ sink("Hello world");
+ }
+}
+
+// Ensure that default parameter works even if non CTFEable
+void withDefaultValue(in time_t currTime = time(null)) {}
+struct TimeRef { time_t now; ulong[4] bloat; }
+void withDefaultRef(in TimeRef currTime = TimeRef(time(null))) {}
+
+// Ensure that default parameters work with `.init`
+void withInitDefaultValue(in size_t defVal = size_t.init) {}
+void withInitDefaultRef(in TimeRef defVal = TimeRef.init) {}
+
+// Ensure that temporary aren't needlessly created
+// (if they are, it'll trigger the "goto skips declaration" error)
+void checkNotIdentity(in void* p1, in void* p2) { assert(p1 !is p2); }
+void checkTemporary()
+{
+ int* p = new int;
+ if (p is null)
+ goto LError;
+ // Should not generate temporary, pass the pointers by value
+ checkNotIdentity(/*lvalue*/ p, /*rvalue*/ null);
+ checkNotIdentity(new int, null);
+LError:
+ assert(0);
+}
+
+
+// Some ABI-specific tests:
+
+version (Win64)
+{
+ void checkReal(in real p)
+ {
+ // ref for x87 real, value for double-precision real
+ static assert(__traits(isRef, p) == (real.sizeof > 8));
+ }
+
+ struct RGB { ubyte r, g, b; }
+ void checkNonPowerOf2(in RGB p)
+ {
+ static assert(__traits(isRef, p));
+ }
+}
+else version (X86_64) // Posix x86_64
+{
+ struct Empty {} // 1 dummy byte passed on the stack
+ void checkEmptyStruct(in Empty p)
+ {
+ static assert(!__traits(isRef, p));
+ }
+
+ static if (is(__vector(double[4])))
+ {
+ struct AvxVectorWrapper { __vector(double[4]) a; } // 256 bits
+ void checkAvxVector(in AvxVectorWrapper p)
+ {
+ static assert(!__traits(isRef, p));
+ }
+ }
+}
+else version (AArch64)
+{
+ alias HVA = __vector(float[4])[4]; // can be passed in 4 vector registers
+ void checkHVA(in HVA p)
+ {
+ static assert(!__traits(isRef, p));
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/protattr.d b/gcc/testsuite/gdc.test/compilable/protattr.d
index ca53a5e7ec4..5922a79730d 100644
--- a/gcc/testsuite/gdc.test/compilable/protattr.d
+++ b/gcc/testsuite/gdc.test/compilable/protattr.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -o-
+// EXTRA_FILES: protection/basic/mod1.d protection/basic/tests.d protection/subpkg/explicit.d protection/subpkg/tests.d protection/subpkg2/tests.d
// PERMUTE_ARGS:
import protection.basic.tests;
import protection.subpkg.tests;
diff --git a/gcc/testsuite/gdc.test/compilable/protection.d b/gcc/testsuite/gdc.test/compilable/protection.d
index 3d6198d757c..bf906eb3670 100644
--- a/gcc/testsuite/gdc.test/compilable/protection.d
+++ b/gcc/testsuite/gdc.test/compilable/protection.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/protectionimp.d
import imports.protectionimp;
alias TypeTuple(T...) = T;
@@ -76,7 +77,7 @@ void main()
}
/***************************************************/
-// 14169
+// https://issues.dlang.org/show_bug.cgi?id=14169
template staticMap14169(alias fun, T...)
{
diff --git a/gcc/testsuite/gdc.test/compilable/quadratic.d b/gcc/testsuite/gdc.test/compilable/quadratic.d
new file mode 100644
index 00000000000..4619e829fcb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/quadratic.d
@@ -0,0 +1,42 @@
+/* PERMUTE_ARGS: -O
+ * If not careful, this can produce exponential tree traversal times
+ * when compiling the generated opEquals() function.
+ */
+
+struct Param
+{
+ bool verbose;
+ bool vcg_ast;
+ bool showColumns;
+ bool vtls;
+ bool vtemplates;
+ bool vgc;
+ bool vfield;
+ bool vcomplex;
+ int useDeprecated;
+ bool stackstomp;
+ bool useUnitTests;
+ bool useInline;
+ bool useDIP25;
+ bool noDIP25;
+ bool useDIP1021;
+ bool release;
+ bool a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
+ uint debuglevel;
+ void* debugids;
+
+ uint versionlevel;
+ void* versionids;
+
+ const(char)[] defaultlibname;
+ const(char)[] debuglibname;
+ const(char) mscrtlib;
+
+ void* moduleDeps;
+ int messageStyle = 1;
+}
+
+struct Global
+{
+ Param params;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/readmodify_structclass.d b/gcc/testsuite/gdc.test/compilable/readmodify_structclass.d
new file mode 100644
index 00000000000..ba55e54280d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/readmodify_structclass.d
@@ -0,0 +1,30 @@
+// REQUIRED_ARGS:
+shared struct S
+{
+ int x = 0;
+
+ int opUnary(string s)() if (s == "++")
+ {
+ import core.atomic : atomicOp;
+ return atomicOp!"+="(x, 1);
+ }
+}
+
+shared class C
+{
+ int x = 0;
+
+ int opUnary(string s)() if (s == "++")
+ {
+ import core.atomic : atomicOp;
+ return atomicOp!"+="(x, 1);
+ }
+}
+
+void main()
+{
+ S s;
+ s++;
+ shared(C) c = new C();
+ c++;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/reinterpretctfe.d b/gcc/testsuite/gdc.test/compilable/reinterpretctfe.d
new file mode 100644
index 00000000000..1d6fd4f39c7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/reinterpretctfe.d
@@ -0,0 +1,34 @@
+// https://issues.dlang.org/show_bug.cgi?id=21997
+
+int nonPureFunc(int i)
+{
+ return 2 * i;
+}
+
+int mainCtfe()
+{
+ auto pureFunc = cast(int function(int) pure) &nonPureFunc;
+ assert(pureFunc(2) == 4);
+
+ auto baseFunc = cast(int function(int)) pureFunc;
+ assert(baseFunc(3) == 6);
+
+ /*
+ Still missing delegates: https://issues.dlang.org/show_bug.cgi?id=17487
+
+ static struct S {
+ int i;
+ int f(int j) { return i * j; }
+ }
+
+ S s = S(5);
+ auto pureDel = cast(int delegate(int) pure) &s.f;
+ assert(pureDel(3) == 15);
+
+ auto baseDel = cast(int delegate(int)) pureDel;
+ assert(baseDel(4) == 20);
+ */
+ return 0;
+}
+
+enum forceCTFE = mainCtfe();
diff --git a/gcc/testsuite/gdc.test/compilable/riia_ctor.d b/gcc/testsuite/gdc.test/compilable/riia_ctor.d
index 1c8e142b796..459ca53e451 100644
--- a/gcc/testsuite/gdc.test/compilable/riia_ctor.d
+++ b/gcc/testsuite/gdc.test/compilable/riia_ctor.d
@@ -1,4 +1,5 @@
// https://issues.dlang.org/show_bug.cgi?id=17494
+// REQUIRED_ARGS: -revert=dtorfields
struct S
{
~this() {}
diff --git a/gcc/testsuite/gdc.test/compilable/rvalueref.d b/gcc/testsuite/gdc.test/compilable/rvalueref.d
new file mode 100644
index 00000000000..1c96c36f1f5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/rvalueref.d
@@ -0,0 +1,13 @@
+/* REQUIRED_ARGS: -preview=rvaluerefparam
+ */
+
+struct AS
+{
+ string get() @safe @nogc pure nothrow { return _s; }
+ alias get this;
+ @disable this(this);
+ string _s;
+}
+
+void popFront(ref string) { }
+static assert(!is(typeof((R r) => r.popFront)));
diff --git a/gcc/testsuite/gdc.test/compilable/scope.d b/gcc/testsuite/gdc.test/compilable/scope.d
index 4f53730df1f..7e81028d278 100644
--- a/gcc/testsuite/gdc.test/compilable/scope.d
+++ b/gcc/testsuite/gdc.test/compilable/scope.d
@@ -1,14 +1,255 @@
/*
- currently fails with extra safety checks
- PERMUTE_FIXME_ARGS: -dip1000
+REQUIRED_ARGS: -preview=dip1000
*/
struct Cache
{
ubyte[1] v;
- ubyte[] set(ubyte[1] v)
+ ubyte[] set(ubyte[1] v) return
{
return this.v[] = v[];
}
}
+
+/*********************************/
+
+// https://github.com/dlang/dmd/pull/9220
+
+@safe:
+
+struct OnlyResult
+{
+ private this(Values)(scope ref Values values)
+ {
+ this.s = values;
+ }
+
+ string s;
+}
+
+auto only(Values)(Values vv)
+{
+ return OnlyResult(vv);
+}
+
+
+void test() @nogc @safe pure
+{
+ only(null);
+}
+
+/************************************/
+
+// https://github.com/dlang/dmd/pull/9220
+
+auto callWrappedOops(scope string dArgs) {
+
+ string callWrappedImpl() {
+ return dArgs;
+ }
+}
+
+/************************************/
+
+struct Constant
+{
+ int* member;
+
+ this(Repeat!(int*) grid) @safe
+ {
+ foreach(ref x; grid)
+ member = x;
+
+ foreach(ref x; grid)
+ x = member;
+ }
+
+ int* foo(return scope Repeat!(int*) grid) @safe
+ {
+ foreach(ref x; grid)
+ x = member;
+
+ foreach(ref x; grid)
+ return x;
+
+ return null;
+ }
+
+ alias Repeat(T...) = T;
+}
+
+
+/************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=19387
+
+struct C
+{
+ void* u;
+ this(this) @safe
+ {
+ }
+}
+
+struct S
+{
+ C c;
+}
+
+void foo(scope S s) @safe
+{
+}
+
+void bar(scope S s) @safe
+{
+ foo(s);
+}
+
+/************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=20675
+
+struct D
+{
+ int pos;
+ char* p;
+}
+
+void test(scope ref D d) @safe
+{
+ D[] da;
+ da ~= D(d.pos, null);
+}
+
+/************************************/
+
+void withEscapes()
+{
+ static D get() @safe;
+
+ with (get())
+ {
+ }
+}
+
+/************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=20682
+
+int f1_20682(return scope ref D d) @safe
+{
+ return d.pos;
+}
+
+ref int f2_20682(return scope ref D d) @safe
+{
+ return d.pos;
+}
+
+void test_20682(scope ref D d) @safe
+{
+ int[] a;
+ a ~= f1_20682(d);
+ a ~= f2_20682(d);
+ a ~= cast(int) d.p;
+}
+
+// Phobos failure
+void formattedWrite(immutable char[2] args) @safe
+{
+ scope immutable char[] val = args;
+}
+
+void ctfeRead(const ubyte[2] array) @safe
+{
+ short result;
+
+ foreach_reverse (b; array)
+ result = cast(short) ((result << 8) | b);
+
+ foreach (b; array)
+ result = cast(short) ((result << 8) | b);
+}
+
+void demangle() @safe
+{
+ static struct DotSplitter
+ {
+ const(char)[] s;
+
+ @safe:
+ @property bool empty() const { return !s.length; }
+
+ @property const(char)[] front() const return
+ {
+ immutable i = indexOfDot();
+ return s;
+ }
+
+ void popFront() {}
+
+ private ptrdiff_t indexOfDot() const
+ {
+ return -1;
+ }
+ }
+
+ foreach (comp; DotSplitter(""))
+ {
+ const copy = comp;
+ }
+}
+
+void fileCtor() @safe
+{
+ static struct S
+ {
+ int i;
+ }
+
+ // static S get()
+ // {
+ // return S();
+ // }
+
+ with (S())
+ {
+ int* ptr = &i;
+ }
+}
+
+// Missing test coverage
+int*[4] testArray() @safe
+{
+ return typeof(return).init;
+}
+
+/************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=21209
+void testForeach(T)(const(T)[] ts)
+{
+ static int g;
+ g++; // force impure for https://issues.dlang.org/show_bug.cgi?id=20150
+ foreach (c; ts)
+ {
+
+ }
+ foreach_reverse(c0; ts)
+ {
+ foreach(c1; ts)
+ {
+
+ }
+ }
+}
+
+@safe
+void main21209()
+{
+ char[10] cs;
+ float[10] fs;
+ testForeach(cs);
+ testForeach(fs);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/scopeinfer.d b/gcc/testsuite/gdc.test/compilable/scopeinfer.d
new file mode 100644
index 00000000000..1ed4c6a7893
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/scopeinfer.d
@@ -0,0 +1,30 @@
+// PERMUTE_ARGS: -preview=dip1000
+
+// Mangling should be the same with or without inference of `return scope`
+
+@safe:
+
+auto foo(void* p) { return 0; }
+static assert(typeof(foo).mangleof == "FNaNbNiNfPvZi");
+
+auto bar(void* p) { return p; }
+static assert(typeof(bar).mangleof == "FNaNbNiNfPvZQd");
+
+// https://issues.dlang.org/show_bug.cgi?id=19857
+
+struct Stack()
+{
+@safe:
+ int** data;
+ ref int* top()
+ {
+ return *data;
+ }
+}
+
+alias S = Stack!();
+
+//pragma(msg, S.top.mangleof);
+
+version (Win32)
+static assert(S.top.mangleof == "_D10scopeinfer__T5StackZQh3topMFNaNbNcNiNfZPi");
diff --git a/gcc/testsuite/gdc.test/compilable/shared.d b/gcc/testsuite/gdc.test/compilable/shared.d
new file mode 100644
index 00000000000..bfa84224d79
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/shared.d
@@ -0,0 +1,125 @@
+/* REQUIRED_ARGS: -preview=nosharedaccess
+TEST_OUTPUT:
+---
+pure nothrow @nogc ref @safe shared(C1)(return ref shared(C1) c)
+pure nothrow @nogc ref @safe shared(int)(return ref shared(C3) c)
+---
+*/
+ref shared(int) f(return shared ref int y)
+{
+ return y;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20908
+void test20908()
+{
+ // shared locals (or struct members) should be able to be initialised:
+ shared int x;
+
+ ref shared(int) fun()
+ {
+ static shared(int) val;
+
+ // return by reference
+ return val;
+ }
+
+ ref shared(int) fun2()
+ {
+ static shared(int)* val;
+
+ // transfer pointer to reference
+ return *val;
+ }
+
+ ref shared(int) fun3()
+ {
+ static shared(int)*** val;
+
+ // Multiple indirections
+ return ***val;
+ }
+}
+
+// Simple tests for `DotVarExp`
+// A `DotVarExp` is `a.b`. If `a` is a `shared ref`,
+// it is of type `shared(T)*` (as opposed to `shared(T*)`).
+// We should allow arbitrarily nested `DotVarExp` as long
+// as no shared memory is read, as in the case above
+// (we're just offsetting a pointer).
+struct C1
+{
+ int value;
+}
+
+struct C2
+{
+ C1 c1;
+}
+
+struct C3
+{
+ C2 c1;
+ C2 c2;
+}
+
+ref shared(int) test_dotvarexp_1(return ref shared C1 c)
+{
+ return c.value;
+}
+
+shared(int)* test_dotvarexp_2(return ref shared C1 c)
+{
+ return &c.value;
+}
+
+shared(C2)* test_dotvarexp_3(return ref shared C3 c)
+{
+ return &c.c1;
+}
+
+shared(C2)* test_dotvarexp_4(return ref shared C3 c)
+{
+ return &c.c2;
+}
+
+ref shared(int) test_dotvarexp_5(return shared ref C3 c)
+{
+ return c.c1.c1.value;
+}
+
+ref shared(int) test_dotvarexp_5(return ref shared(C3)[] c)
+{
+ return c[0].c1.c1.value;
+}
+
+// Test `auto` inference
+auto ref test_inference_1(return ref shared C1 c)
+{
+ return c;
+}
+
+pragma(msg, typeof(test_inference_1));
+
+auto ref test_inference_2(return ref shared C3 c)
+{
+ return c.c2.c1.value;
+}
+
+pragma(msg, typeof(test_inference_2));
+
+// https://issues.dlang.org/show_bug.cgi?id=21793
+
+struct Child
+{
+ this(int) shared {}
+}
+
+struct Parent
+{
+ shared Child ch;
+ this(int i) shared
+ {
+ ch = shared Child(i);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/sharedopt.d b/gcc/testsuite/gdc.test/compilable/sharedopt.d
new file mode 100644
index 00000000000..bac0ce03b5a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/sharedopt.d
@@ -0,0 +1,19 @@
+// REQUIRED_ARGS: -O
+
+void _d_critical_term()
+{
+ for (auto p = head; p; p = p.next)
+ destroyMutex(p.i);
+}
+
+shared S* head;
+
+struct S
+{
+ S* next;
+ int i;
+}
+
+void destroyMutex(int i);
+
+struct Mutex { int i; }
diff --git a/gcc/testsuite/gdc.test/compilable/shortened_methods.d b/gcc/testsuite/gdc.test/compilable/shortened_methods.d
new file mode 100644
index 00000000000..5a7ac8749d8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/shortened_methods.d
@@ -0,0 +1,33 @@
+// REQUIRED_ARGS: -preview=shortenedMethods
+class A {
+ int _x = 34;
+ // short syntax works in all contexts
+ @property x() => _x;
+ @property x(int v) => _x = v;
+
+ // including with contracts
+ @property y() in(true) => _x;
+
+ // or other auto returns
+ auto foo() @safe => assert(0);
+
+ // or normal method defintions
+ bool isNull() => this is null;
+}
+
+class B : A{
+ // short syntax also overrides the same as long syntax
+ override bool isNull() => this !is null;
+}
+
+static assert((new A).x == 34);
+
+string test() => "hello"; // works at any scope
+
+static assert(test() == "hello"); // works normally
+static assert(is(typeof(&test) == string function())); // same normal type
+
+void func() {
+ int a;
+ int nested() => a; // and at nested scopes too
+}
diff --git a/gcc/testsuite/gdc.test/compilable/staticforeach.d b/gcc/testsuite/gdc.test/compilable/staticforeach.d
index 8a54f32de57..ce9eb74a1ef 100644
--- a/gcc/testsuite/gdc.test/compilable/staticforeach.d
+++ b/gcc/testsuite/gdc.test/compilable/staticforeach.d
@@ -1,6 +1,6 @@
// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
// EXTRA_FILES: imports/imp12242a1.d imports/imp12242a2.d
+// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/compilable/sw_transition_complex.d b/gcc/testsuite/gdc.test/compilable/sw_transition_complex.d
index f034c69e99e..b6dbc8a3460 100644
--- a/gcc/testsuite/gdc.test/compilable/sw_transition_complex.d
+++ b/gcc/testsuite/gdc.test/compilable/sw_transition_complex.d
@@ -1,15 +1,15 @@
// PERMUTE_ARGS:
-// REQUIRED_ARGS: -c -transition=complex
+// REQUIRED_ARGS: -unittest
/*
TEST_OUTPUT:
---
-compilable/sw_transition_complex.d(15): use of complex type 'creal' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
-compilable/sw_transition_complex.d(16): use of complex type 'cdouble' is scheduled for deprecation, use 'std.complex.Complex!(double)' instead
-compilable/sw_transition_complex.d(17): use of complex type 'cfloat' is scheduled for deprecation, use 'std.complex.Complex!(float)' instead
-compilable/sw_transition_complex.d(19): use of imaginary type 'ireal' is scheduled for deprecation, use 'real' instead
-compilable/sw_transition_complex.d(20): use of imaginary type 'idouble' is scheduled for deprecation, use 'double' instead
-compilable/sw_transition_complex.d(21): use of imaginary type 'ifloat' is scheduled for deprecation, use 'float' instead
+compilable/sw_transition_complex.d(15): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/sw_transition_complex.d(16): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/sw_transition_complex.d(17): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead
+compilable/sw_transition_complex.d(19): Deprecation: use of imaginary type `ireal` is deprecated, use `real` instead
+compilable/sw_transition_complex.d(20): Deprecation: use of imaginary type `idouble` is deprecated, use `double` instead
+compilable/sw_transition_complex.d(21): Deprecation: use of imaginary type `ifloat` is deprecated, use `float` instead
---
*/
creal c80value;
@@ -23,12 +23,12 @@ ifloat i32value;
/*
TEST_OUTPUT:
---
-compilable/sw_transition_complex.d(34): use of complex type 'creal*' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
-compilable/sw_transition_complex.d(35): use of complex type 'cdouble*' is scheduled for deprecation, use 'std.complex.Complex!(double)' instead
-compilable/sw_transition_complex.d(36): use of complex type 'cfloat*' is scheduled for deprecation, use 'std.complex.Complex!(float)' instead
-compilable/sw_transition_complex.d(38): use of imaginary type 'ireal*' is scheduled for deprecation, use 'real' instead
-compilable/sw_transition_complex.d(39): use of imaginary type 'idouble*' is scheduled for deprecation, use 'double' instead
-compilable/sw_transition_complex.d(40): use of imaginary type 'ifloat*' is scheduled for deprecation, use 'float' instead
+compilable/sw_transition_complex.d(34): Deprecation: use of complex type `creal*` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/sw_transition_complex.d(35): Deprecation: use of complex type `cdouble*` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/sw_transition_complex.d(36): Deprecation: use of complex type `cfloat*` is deprecated, use `std.complex.Complex!(float)` instead
+compilable/sw_transition_complex.d(38): Deprecation: use of imaginary type `ireal*` is deprecated, use `real` instead
+compilable/sw_transition_complex.d(39): Deprecation: use of imaginary type `idouble*` is deprecated, use `double` instead
+compilable/sw_transition_complex.d(40): Deprecation: use of imaginary type `ifloat*` is deprecated, use `float` instead
---
*/
creal* c80pointer;
@@ -42,12 +42,12 @@ ifloat* i32pointer;
/*
TEST_OUTPUT:
---
-compilable/sw_transition_complex.d(53): use of complex type 'creal[]*' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
-compilable/sw_transition_complex.d(54): use of complex type 'cdouble[]*' is scheduled for deprecation, use 'std.complex.Complex!(double)' instead
-compilable/sw_transition_complex.d(55): use of complex type 'cfloat[]*' is scheduled for deprecation, use 'std.complex.Complex!(float)' instead
-compilable/sw_transition_complex.d(57): use of imaginary type 'ireal[]*' is scheduled for deprecation, use 'real' instead
-compilable/sw_transition_complex.d(58): use of imaginary type 'idouble[]*' is scheduled for deprecation, use 'double' instead
-compilable/sw_transition_complex.d(59): use of imaginary type 'ifloat[]*' is scheduled for deprecation, use 'float' instead
+compilable/sw_transition_complex.d(53): Deprecation: use of complex type `creal[]*` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/sw_transition_complex.d(54): Deprecation: use of complex type `cdouble[]*` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/sw_transition_complex.d(55): Deprecation: use of complex type `cfloat[]*` is deprecated, use `std.complex.Complex!(float)` instead
+compilable/sw_transition_complex.d(57): Deprecation: use of imaginary type `ireal[]*` is deprecated, use `real` instead
+compilable/sw_transition_complex.d(58): Deprecation: use of imaginary type `idouble[]*` is deprecated, use `double` instead
+compilable/sw_transition_complex.d(59): Deprecation: use of imaginary type `ifloat[]*` is deprecated, use `float` instead
---
*/
creal[]* c80arrayp;
@@ -61,12 +61,12 @@ ifloat[]* i32arrayp;
/*
TEST_OUTPUT:
---
-compilable/sw_transition_complex.d(72): use of complex type 'creal[4][]*' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
-compilable/sw_transition_complex.d(73): use of complex type 'cdouble[4][]*' is scheduled for deprecation, use 'std.complex.Complex!(double)' instead
-compilable/sw_transition_complex.d(74): use of complex type 'cfloat[4][]*' is scheduled for deprecation, use 'std.complex.Complex!(float)' instead
-compilable/sw_transition_complex.d(76): use of imaginary type 'ireal[4][]*' is scheduled for deprecation, use 'real' instead
-compilable/sw_transition_complex.d(77): use of imaginary type 'idouble[4][]*' is scheduled for deprecation, use 'double' instead
-compilable/sw_transition_complex.d(78): use of imaginary type 'ifloat[4][]*' is scheduled for deprecation, use 'float' instead
+compilable/sw_transition_complex.d(72): Deprecation: use of complex type `creal[4][]*` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/sw_transition_complex.d(73): Deprecation: use of complex type `cdouble[4][]*` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/sw_transition_complex.d(74): Deprecation: use of complex type `cfloat[4][]*` is deprecated, use `std.complex.Complex!(float)` instead
+compilable/sw_transition_complex.d(76): Deprecation: use of imaginary type `ireal[4][]*` is deprecated, use `real` instead
+compilable/sw_transition_complex.d(77): Deprecation: use of imaginary type `idouble[4][]*` is deprecated, use `double` instead
+compilable/sw_transition_complex.d(78): Deprecation: use of imaginary type `ifloat[4][]*` is deprecated, use `float` instead
---
*/
creal[4][]* c80sarrayp;
@@ -80,14 +80,14 @@ ifloat[4][]* i32sarrayp;
/*
TEST_OUTPUT:
---
-compilable/sw_transition_complex.d(96): use of complex type 'creal' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
-compilable/sw_transition_complex.d(97): use of complex type 'creal*' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
-compilable/sw_transition_complex.d(98): use of complex type 'creal[]' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
-compilable/sw_transition_complex.d(99): use of complex type 'creal[4]' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
-compilable/sw_transition_complex.d(101): use of imaginary type 'ireal' is scheduled for deprecation, use 'real' instead
-compilable/sw_transition_complex.d(102): use of imaginary type 'ireal*' is scheduled for deprecation, use 'real' instead
-compilable/sw_transition_complex.d(103): use of imaginary type 'ireal[]' is scheduled for deprecation, use 'real' instead
-compilable/sw_transition_complex.d(104): use of imaginary type 'ireal[4]' is scheduled for deprecation, use 'real' instead
+compilable/sw_transition_complex.d(96): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/sw_transition_complex.d(97): Deprecation: use of complex type `creal*` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/sw_transition_complex.d(98): Deprecation: use of complex type `creal[]` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/sw_transition_complex.d(99): Deprecation: use of complex type `creal[4]` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/sw_transition_complex.d(101): Deprecation: use of imaginary type `ireal` is deprecated, use `real` instead
+compilable/sw_transition_complex.d(102): Deprecation: use of imaginary type `ireal*` is deprecated, use `real` instead
+compilable/sw_transition_complex.d(103): Deprecation: use of imaginary type `ireal[]` is deprecated, use `real` instead
+compilable/sw_transition_complex.d(104): Deprecation: use of imaginary type `ireal[4]` is deprecated, use `real` instead
---
*/
alias C14488 = creal;
@@ -106,10 +106,10 @@ I14488[4] ialias4;
/*
TEST_OUTPUT:
---
-compilable/sw_transition_complex.d(115): use of complex type 'cdouble' is scheduled for deprecation, use 'std.complex.Complex!(double)' instead
-compilable/sw_transition_complex.d(116): use of imaginary type 'idouble' is scheduled for deprecation, use 'double' instead
-compilable/sw_transition_complex.d(117): use of complex type 'cdouble' is scheduled for deprecation, use 'std.complex.Complex!(double)' instead
-compilable/sw_transition_complex.d(118): use of complex type 'cdouble[]' is scheduled for deprecation, use 'std.complex.Complex!(double)' instead
+compilable/sw_transition_complex.d(115): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/sw_transition_complex.d(116): Deprecation: use of imaginary type `idouble` is deprecated, use `double` instead
+compilable/sw_transition_complex.d(117): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/sw_transition_complex.d(118): Deprecation: use of complex type `cdouble[]` is deprecated, use `std.complex.Complex!(double)` instead
---
*/
auto cauto = 1 + 0i;
@@ -120,9 +120,9 @@ TypeInfo c64ti = typeid(cdouble[]);
/*
TEST_OUTPUT:
---
-compilable/sw_transition_complex.d(128): use of complex type 'creal*' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
-compilable/sw_transition_complex.d(128): use of imaginary type 'ireal' is scheduled for deprecation, use 'real' instead
-compilable/sw_transition_complex.d(132): use of complex type 'creal' is scheduled for deprecation, use 'std.complex.Complex!(real)' instead
+compilable/sw_transition_complex.d(128): Deprecation: use of complex type `creal*` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/sw_transition_complex.d(128): Deprecation: use of imaginary type `ireal` is deprecated, use `real` instead
+compilable/sw_transition_complex.d(132): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead
---
*/
void test14488a(creal *p, real r, ireal i)
@@ -134,3 +134,41 @@ creal test14488b()
return 1 + 0i;
}
+// Forward referenced types shouldn't cause errors during test for complex or imaginary.
+enum E;
+struct S;
+
+void test14488c(E *e, S *s)
+{
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18212
+// Usage of cfloat,cdouble,cfloat,ifloat,idouble,ireal shouldn't trigger an error in deprecated code
+deprecated void test18212(creal c){}
+deprecated unittest
+{
+ ireal a = 2i;
+ creal b = 2 + 3i;
+}
+deprecated struct Foo
+{
+ ifloat a = 2i;
+ cfloat b = 2f + 2i;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18218
+static assert(__traits(isDeprecated, cfloat));
+static assert(__traits(isDeprecated, cdouble));
+static assert(__traits(isDeprecated, creal));
+static assert(__traits(isDeprecated, ifloat));
+static assert(__traits(isDeprecated, idouble));
+static assert(__traits(isDeprecated, ireal));
+static assert(!__traits(isDeprecated, float));
+static assert(!__traits(isDeprecated, double));
+static assert(!__traits(isDeprecated, real));
+static assert(!__traits(isDeprecated, int));
+static assert(!__traits(isDeprecated, long));
+static assert(!__traits(isDeprecated, ubyte));
+static assert(!__traits(isDeprecated, char));
+static assert(!__traits(isDeprecated, bool));
+static assert(!__traits(isDeprecated, S));
diff --git a/gcc/testsuite/gdc.test/compilable/sw_transition_field.d b/gcc/testsuite/gdc.test/compilable/sw_transition_field.d
index 83dd2617459..97c8ee55856 100644
--- a/gcc/testsuite/gdc.test/compilable/sw_transition_field.d
+++ b/gcc/testsuite/gdc.test/compilable/sw_transition_field.d
@@ -1,12 +1,12 @@
// PERMUTE_ARGS:
-// REQUIRED_ARGS: -c -transition=field
+// REQUIRED_ARGS: -transition=field
/*
TEST_OUTPUT:
---
-compilable/sw_transition_field.d(15): sw_transition_field.S1.ix is immutable field
-compilable/sw_transition_field.d(16): sw_transition_field.S1.cx is const field
-compilable/sw_transition_field.d(21): sw_transition_field.S2!(immutable(int)).S2.f is immutable field
-compilable/sw_transition_field.d(21): sw_transition_field.S2!(const(int)).S2.f is const field
+compilable/sw_transition_field.d(15): `sw_transition_field.S1.ix` is `immutable` field
+compilable/sw_transition_field.d(16): `sw_transition_field.S1.cx` is `const` field
+compilable/sw_transition_field.d(21): `sw_transition_field.S2!(immutable(int)).S2.f` is `immutable` field
+compilable/sw_transition_field.d(21): `sw_transition_field.S2!(const(int)).S2.f` is `const` field
---
*/
diff --git a/gcc/testsuite/gdc.test/compilable/sw_transition_tls.d b/gcc/testsuite/gdc.test/compilable/sw_transition_tls.d
index 1085e80c401..7954d5008b0 100644
--- a/gcc/testsuite/gdc.test/compilable/sw_transition_tls.d
+++ b/gcc/testsuite/gdc.test/compilable/sw_transition_tls.d
@@ -1,10 +1,10 @@
// PERMUTE_ARGS:
-// REQUIRED_ARGS: -c -transition=tls
+// REQUIRED_ARGS: -transition=tls
/*
TEST_OUTPUT:
---
-compilable/sw_transition_tls.d(11): x is thread local
-compilable/sw_transition_tls.d(15): y is thread local
+compilable/sw_transition_tls.d(11): `x` is thread local
+compilable/sw_transition_tls.d(15): `y` is thread local
---
*/
diff --git a/gcc/testsuite/gdc.test/compilable/test1.d b/gcc/testsuite/gdc.test/compilable/test1.d
index 80c382b5e71..d20d0a9f849 100644
--- a/gcc/testsuite/gdc.test/compilable/test1.d
+++ b/gcc/testsuite/gdc.test/compilable/test1.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/test1imp.d
class File
{
import imports.test1imp;
diff --git a/gcc/testsuite/gdc.test/compilable/test10312.d b/gcc/testsuite/gdc.test/compilable/test10312.d
index db56bc2b5e2..7cb50f294b1 100644
--- a/gcc/testsuite/gdc.test/compilable/test10312.d
+++ b/gcc/testsuite/gdc.test/compilable/test10312.d
@@ -1,5 +1,5 @@
-version(D_SIMD)
+static if (__traits(compiles, __vector(float[4])))
{
const __vector(float[4]) si = [1f, 1f, 1f, 1f];
diff --git a/gcc/testsuite/gdc.test/compilable/test10375.d b/gcc/testsuite/gdc.test/compilable/test10375.d
index b9704f6a244..ba90fa51960 100644
--- a/gcc/testsuite/gdc.test/compilable/test10375.d
+++ b/gcc/testsuite/gdc.test/compilable/test10375.d
@@ -1,5 +1,5 @@
// REQUIRED_ARGS: -o-
-
+// EXTRA_FILES: imports/test10375a.d
import imports.test10375a;
void packIt(Pack)(Pack p){ } //3
diff --git a/gcc/testsuite/gdc.test/compilable/test10520.d b/gcc/testsuite/gdc.test/compilable/test10520.d
index 9a24d840527..d03b903d571 100644
--- a/gcc/testsuite/gdc.test/compilable/test10520.d
+++ b/gcc/testsuite/gdc.test/compilable/test10520.d
@@ -1,11 +1,12 @@
// REQUIRED_ARGS: -debug -profile
-// Issue 10520 [profile+nothrow] Building with profiler results in "is not nothrow" error on some contracts
+// https://issues.dlang.org/show_bug.cgi?id=10520
+// [profile+nothrow] Building with profiler results in "is not nothrow" error on some contracts
void f() { }
void g()()
in { f(); } // OK <- Error: 'main.f' is not nothrow
-body { }
+do { }
alias gi = g!();
diff --git a/gcc/testsuite/gdc.test/compilable/test10752.d b/gcc/testsuite/gdc.test/compilable/test10752.d
index 449377e3f06..1017e0ff995 100644
--- a/gcc/testsuite/gdc.test/compilable/test10752.d
+++ b/gcc/testsuite/gdc.test/compilable/test10752.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test10752.d
import imports.test10752;
void main()
{
diff --git a/gcc/testsuite/gdc.test/compilable/test10981.d b/gcc/testsuite/gdc.test/compilable/test10981.d
index f0a6820fa7e..961e07d6b1f 100644
--- a/gcc/testsuite/gdc.test/compilable/test10981.d
+++ b/gcc/testsuite/gdc.test/compilable/test10981.d
@@ -6,7 +6,7 @@ in
void in_nested() pure
in { assert(i); } // OK <- NG
out { assert(i); } // OK <- NG
- body {}
+ do {}
}
}
out
@@ -16,9 +16,9 @@ out
void out_nested() pure
in { assert(i); } // OK <- NG
out { assert(i); } // OK <- NG
- body {}
+ do {}
}
}
-body
+do
{
}
diff --git a/gcc/testsuite/gdc.test/compilable/test10993.d b/gcc/testsuite/gdc.test/compilable/test10993.d
index e5a1b847976..a69d0c616cf 100644
--- a/gcc/testsuite/gdc.test/compilable/test10993.d
+++ b/gcc/testsuite/gdc.test/compilable/test10993.d
@@ -1,5 +1,7 @@
module test10993;
+import core.demangle : demangleType;
+
auto foo(T)(T a)
{
static immutable typeof(a) q;
@@ -29,5 +31,7 @@ void main()
auto y = cast()x;
enum mangle_y = typeof(y).mangleof;
// pragma(msg, "y : " ~ mangle_y);
- static assert (mangle_y == mangle_x[1..$]);
+ enum demangle_x = demangleType(mangle_x);
+ enum demangle_y = demangleType(mangle_y);
+ static assert ("immutable(" ~ demangle_y ~ ")" == demangle_x);
}
diff --git a/gcc/testsuite/gdc.test/compilable/test11169.d b/gcc/testsuite/gdc.test/compilable/test11169.d
index 79863e11f47..edf137636f2 100644
--- a/gcc/testsuite/gdc.test/compilable/test11169.d
+++ b/gcc/testsuite/gdc.test/compilable/test11169.d
@@ -47,7 +47,7 @@ void main()
class B : A
{
// __traits(isAbstractClass) is not usable in static if condition.
- static assert (!__traits(isAbstractClass, typeof(this)));
+ static assert (!__traits(isAbstractClass, typeof(this)));
override void foo()
{
diff --git a/gcc/testsuite/gdc.test/compilable/test11225a.d b/gcc/testsuite/gdc.test/compilable/test11225a.d
index 58df82793e3..33339457516 100644
--- a/gcc/testsuite/gdc.test/compilable/test11225a.d
+++ b/gcc/testsuite/gdc.test/compilable/test11225a.d
@@ -1,4 +1,5 @@
/*
+EXTRA_FILES: imports/test11225b.d imports/test11225c.d
TEST_OUTPUT:
---
WORKS
diff --git a/gcc/testsuite/gdc.test/compilable/test11237.d b/gcc/testsuite/gdc.test/compilable/test11237.d
deleted file mode 100644
index 1700af426f7..00000000000
--- a/gcc/testsuite/gdc.test/compilable/test11237.d
+++ /dev/null
@@ -1,4 +0,0 @@
-// PERMUTE_ARGS:
-// POST_SCRIPT: compilable/extra-files/test11237.sh
-
-struct Buffer { ubyte[64 * 1024] buffer; }
diff --git a/gcc/testsuite/gdc.test/compilable/test11259.d b/gcc/testsuite/gdc.test/compilable/test11259.d
new file mode 100644
index 00000000000..209d5e1011a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test11259.d
@@ -0,0 +1,25 @@
+// https://issues.dlang.org/show_bug.cgi?id=11259
+
+version (Posix)
+{
+ // smallest druntime module without imports on posix
+ import core.sys.posix.libgen;
+ static assert(__traits(isSame, __traits(parent, core.sys.posix.libgen), core.sys.posix));
+ static assert(__traits(isSame, core.sys.posix, __traits(parent, core.sys.posix.libgen)));
+
+ static assert(__traits(isSame, __traits(parent, core.sys.posix), core.sys));
+ static assert(__traits(isSame, core.sys, __traits(parent, core.sys.posix)));
+}
+else
+{
+ // smallest module without imports for windows
+ import core.sys.windows.lmuseflg;
+ static assert(__traits(isSame, __traits(parent, core.sys.windows.lmuseflg), core.sys.windows));
+ static assert(__traits(isSame, core.sys.windows, __traits(parent, core.sys.windows.lmuseflg)));
+
+ static assert(__traits(isSame, __traits(parent, core.sys.windows), core.sys));
+ static assert(__traits(isSame, core.sys, __traits(parent, core.sys.windows)));
+}
+
+static assert(__traits(isSame, __traits(parent, core.sys), core));
+static assert(__traits(isSame, core, __traits(parent, core.sys)));
diff --git a/gcc/testsuite/gdc.test/compilable/test11371.d b/gcc/testsuite/gdc.test/compilable/test11371.d
index c5229292a62..97ca2b5f53c 100644
--- a/gcc/testsuite/gdc.test/compilable/test11371.d
+++ b/gcc/testsuite/gdc.test/compilable/test11371.d
@@ -1,5 +1,5 @@
-version(D_SIMD)
+static if (__traits(compiles, __vector(long[2])))
{
__vector(long[2]) f()
{
diff --git a/gcc/testsuite/gdc.test/compilable/test11563.d b/gcc/testsuite/gdc.test/compilable/test11563.d
index 6fb39fed44d..3b4af9a2201 100644
--- a/gcc/testsuite/gdc.test/compilable/test11563.d
+++ b/gcc/testsuite/gdc.test/compilable/test11563.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test11563core_bitop.d imports/test11563std_array.d imports/test11563std_range.d imports/test11563std_traits.d
import imports.test11563std_traits;
interface J : I {} // comment out to let compilation succeed
diff --git a/gcc/testsuite/gdc.test/compilable/test11656.d b/gcc/testsuite/gdc.test/compilable/test11656.d
index 4de3065088d..226144f66f9 100644
--- a/gcc/testsuite/gdc.test/compilable/test11656.d
+++ b/gcc/testsuite/gdc.test/compilable/test11656.d
@@ -1,5 +1,5 @@
-version(D_SIMD)
+static if (__traits(compiles, __vector(float[4])))
{
struct Foo
{
diff --git a/gcc/testsuite/gdc.test/compilable/test1170.d b/gcc/testsuite/gdc.test/compilable/test1170.d
new file mode 100644
index 00000000000..767101fa5b5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test1170.d
@@ -0,0 +1,11 @@
+// https://issues.dlang.org/show_bug.cgi?id=1170
+type x;
+mixin("alias int type;");
+
+// https://issues.dlang.org/show_bug.cgi?id=10739
+template DECLARE_HANDLE() {
+ struct HINTERNET { int h; }
+}
+const INTERNET_INVALID_STATUS_CALLBACK = cast(INTERNET_STATUS_CALLBACK) -1;
+mixin DECLARE_HANDLE;
+alias void function(HINTERNET) INTERNET_STATUS_CALLBACK;
diff --git a/gcc/testsuite/gdc.test/compilable/test11847.d b/gcc/testsuite/gdc.test/compilable/test11847.d
new file mode 100644
index 00000000000..d07d9954b06
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test11847.d
@@ -0,0 +1,14 @@
+// REQUIRED_ARGS: -Icompilable/imports
+// EXTRA_FILES: imports/pkg11847/mod11847.d imports/pkg11847/package.d
+import pkg11847;
+import pkg11847.mod11847;
+
+void main() {
+ static assert(pkg11847.func() == 1);
+ static assert(pkg11847.mod11847.func() == 2);
+
+ // This correctly won't compile.
+ // Error: pkg11847.mod11847.func at imports/pkg11847/mod11847.d(3) conflicts with pkg11847.func at imports/pkg11847/package.d(3)
+ static assert(!__traits(compiles, func()));
+
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test1238.d b/gcc/testsuite/gdc.test/compilable/test1238.d
index fa19d619b04..0cbd84d823f 100644
--- a/gcc/testsuite/gdc.test/compilable/test1238.d
+++ b/gcc/testsuite/gdc.test/compilable/test1238.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test1238a.d imports/test1238b.d
module test1238;
import imports.test1238a;
diff --git a/gcc/testsuite/gdc.test/compilable/test12496.d b/gcc/testsuite/gdc.test/compilable/test12496.d
new file mode 100644
index 00000000000..4de2b7150f0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test12496.d
@@ -0,0 +1,13 @@
+// https://issues.dlang.org/show_bug.cgi?id=12496
+
+final abstract class T1
+{
+ final abstract class C(uint value) { }
+
+ alias Child = C!2;
+}
+
+void main()
+{
+ static assert(__traits(isSame, __traits(parent, T1.Child), T1));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test12511.d b/gcc/testsuite/gdc.test/compilable/test12511.d
new file mode 100644
index 00000000000..0a2fc75dc11
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test12511.d
@@ -0,0 +1,15 @@
+// EXTRA_FILES: imports/a12511.d
+module test12511;
+
+import imports.a12511;
+
+public class B
+{
+ static void bar()
+ {
+ A.foo(0);
+ }
+}
+
+void main()
+{}
diff --git a/gcc/testsuite/gdc.test/compilable/test12558.d b/gcc/testsuite/gdc.test/compilable/test12558.d
deleted file mode 100644
index 580cf60cb42..00000000000
--- a/gcc/testsuite/gdc.test/compilable/test12558.d
+++ /dev/null
@@ -1,39 +0,0 @@
-// REQUIRED_ARGS:
-/*
-TEST_OUTPUT:
----
-compilable/test12558.d(16): Deprecation: catch statement without an exception specification is deprecated; use catch(Throwable) for old behavior
-compilable/test12558.d(21): Deprecation: catch statement without an exception specification is deprecated; use catch(Throwable) for old behavior
----
-*/
-
-void main()
-{
- auto handler = () { };
-
- try {
- assert(0);
- } catch
- handler();
-
- try {
- assert(0);
- } catch {
- handler();
- }
-
- // ensure diagnostics are not emitted for verioned-out blocks
- version (none)
- {
- try {
- assert(0);
- } catch // should not emit diagnostics
- handler();
-
- try {
- assert(0);
- } catch { // ditto
- handler();
- }
- }
-}
diff --git a/gcc/testsuite/gdc.test/compilable/test12567c.d b/gcc/testsuite/gdc.test/compilable/test12567c.d
index cadc375bf0b..9a686df555e 100644
--- a/gcc/testsuite/gdc.test/compilable/test12567c.d
+++ b/gcc/testsuite/gdc.test/compilable/test12567c.d
@@ -1,9 +1,10 @@
// REQUIRED_ARGS:
+// EXTRA_FILES: imports/a12567.d
// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-compilable/test12567c.d(9): Deprecation: module imports.a12567 is deprecated - This module will be removed in future release.
+compilable/test12567c.d(10): Deprecation: module `imports.a12567` is deprecated - This module will be removed in future release.
---
*/
import imports.a12567;
diff --git a/gcc/testsuite/gdc.test/compilable/test12567d.d b/gcc/testsuite/gdc.test/compilable/test12567d.d
index ee7bc294d6c..5ad9b5cff24 100644
--- a/gcc/testsuite/gdc.test/compilable/test12567d.d
+++ b/gcc/testsuite/gdc.test/compilable/test12567d.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -d
+// EXTRA_FILES: imports/a12567.d
// PERMUTE_ARGS:
import imports.a12567;
diff --git a/gcc/testsuite/gdc.test/compilable/test12807.d b/gcc/testsuite/gdc.test/compilable/test12807.d
new file mode 100644
index 00000000000..72de65835ee
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test12807.d
@@ -0,0 +1,16 @@
+void foo(T)(ref T t)
+{
+}
+
+struct S
+{
+ int impure() {assert(0);}
+ alias impure this;
+}
+
+void main() pure
+{
+ S s;
+ foo(s);
+ s.foo(); // triggering alias this violates purity, but ufcs matches
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test13226.d b/gcc/testsuite/gdc.test/compilable/test13226.d
index 65bf3356630..10407bdef8a 100644
--- a/gcc/testsuite/gdc.test/compilable/test13226.d
+++ b/gcc/testsuite/gdc.test/compilable/test13226.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -o-
+// EXTRA_FILES: imports/a13226.d
// PERMUTE_ARGS: -version=bug
import imports.a13226;
diff --git a/gcc/testsuite/gdc.test/compilable/test13242.d b/gcc/testsuite/gdc.test/compilable/test13242.d
index 0d6ef276aa0..86f763ff447 100644
--- a/gcc/testsuite/gdc.test/compilable/test13242.d
+++ b/gcc/testsuite/gdc.test/compilable/test13242.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -o-
+// EXTRA_FILES: imports/test13242a.d imports/test13242b.d
/*
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/compilable/test13512.d b/gcc/testsuite/gdc.test/compilable/test13512.d
index bc78bd4dff3..64841889d2f 100644
--- a/gcc/testsuite/gdc.test/compilable/test13512.d
+++ b/gcc/testsuite/gdc.test/compilable/test13512.d
@@ -1,8 +1,8 @@
#!/opt/dmd/ÐÒÏÂÙ/rdmd
-import std.stdio;
+import core.stdc.stdio;
void main () {
- writeln("we are here!");
+ printf("we are here!\n");
}
diff --git a/gcc/testsuite/gdc.test/compilable/test13582a.d b/gcc/testsuite/gdc.test/compilable/test13582a.d
new file mode 100644
index 00000000000..4e3f2bbe6cd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test13582a.d
@@ -0,0 +1,7 @@
+// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/test13582.d
+deprecated module test13582a;
+
+import imports.test13582;
+
+void main() { }
diff --git a/gcc/testsuite/gdc.test/compilable/test13582b.d b/gcc/testsuite/gdc.test/compilable/test13582b.d
new file mode 100644
index 00000000000..89ec251ce2c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test13582b.d
@@ -0,0 +1,15 @@
+// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/test13582.d
+module test13582b;
+
+deprecated void foo()
+{
+ import imports.test13582;
+}
+
+deprecated struct S
+{
+ import imports.test13582;
+}
+
+void main() { }
diff --git a/gcc/testsuite/gdc.test/compilable/test13858.d b/gcc/testsuite/gdc.test/compilable/test13858.d
index 5a0432c59ce..98b07966eb3 100644
--- a/gcc/testsuite/gdc.test/compilable/test13858.d
+++ b/gcc/testsuite/gdc.test/compilable/test13858.d
@@ -1,5 +1,5 @@
// REQUIRED_ARGS: -w
-// 13858
+// https://issues.dlang.org/show_bug.cgi?id=13858
void foo() { assert(0); }
diff --git a/gcc/testsuite/gdc.test/compilable/test13953.d b/gcc/testsuite/gdc.test/compilable/test13953.d
new file mode 100644
index 00000000000..cde7d91c26f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test13953.d
@@ -0,0 +1,14 @@
+// https://issues.dlang.org/show_bug.cgi?id=13953
+
+struct S
+{
+ string[string] aa;
+ alias aa this;
+}
+
+void main()
+{
+ S s;
+ s["foo"] = "bar";
+ s.remove("foo");
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test14114.d b/gcc/testsuite/gdc.test/compilable/test14114.d
new file mode 100644
index 00000000000..246277b7cc9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test14114.d
@@ -0,0 +1,10 @@
+// REQUIRED_ARGS: -O
+
+// https://issues.dlang.org/show_bug.cgi?id=14114
+
+import core.volatile;
+
+struct Ports {
+ static ubyte B() { return volatileLoad(cast(ubyte *)0x0025); }
+ static void B(ubyte value) { volatileStore(cast(ubyte *)0x0025, value); }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test14275.d b/gcc/testsuite/gdc.test/compilable/test14275.d
index 66f899c11a9..7ce66aa10f0 100644
--- a/gcc/testsuite/gdc.test/compilable/test14275.d
+++ b/gcc/testsuite/gdc.test/compilable/test14275.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -o-
+// EXTRA_FILES: protection/bug/bug14275.d protection/aggregate/mod14275.d
// PERMUTE_ARGS:
import protection.bug.bug14275;
diff --git a/gcc/testsuite/gdc.test/compilable/test14528.d b/gcc/testsuite/gdc.test/compilable/test14528.d
index badab92006f..32d99db82a9 100644
--- a/gcc/testsuite/gdc.test/compilable/test14528.d
+++ b/gcc/testsuite/gdc.test/compilable/test14528.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -o-
+// EXTRA_FILES: imports/a14528.d
// PERMTE_ARGS:
import imports.a14528;
diff --git a/gcc/testsuite/gdc.test/compilable/test14666.d b/gcc/testsuite/gdc.test/compilable/test14666.d
index 6162dd9ea6b..198b7178906 100644
--- a/gcc/testsuite/gdc.test/compilable/test14666.d
+++ b/gcc/testsuite/gdc.test/compilable/test14666.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -o-
+// EXTRA_FILES: imports/test14666a.d imports/test14666b.d
// PERMUTE_ARGS:
module test14666;
diff --git a/gcc/testsuite/gdc.test/compilable/test14740.d b/gcc/testsuite/gdc.test/compilable/test14740.d
new file mode 100644
index 00000000000..29c34591b23
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test14740.d
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug.cgi?id=14740
+
+void test()
+{
+ struct S
+ {
+ void fun() {}
+ }
+ static assert([__traits(allMembers, S)] == ["fun"]);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test14831.d b/gcc/testsuite/gdc.test/compilable/test14831.d
new file mode 100644
index 00000000000..c825e695c24
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test14831.d
@@ -0,0 +1,60 @@
+// https://issues.dlang.org/show_bug.cgi?id=14831
+
+void main()
+{
+ {
+ int x;
+ static assert(x.mangleof == "_D9test148314mainFZ1xi");
+ }
+ {
+ int x;
+ static assert(x.mangleof == "_D9test148314mainFZ4__S11xi");
+ }
+
+ {
+ static int y = 0;
+ static assert(y.mangleof == "_D9test148314mainFZ1yi");
+ }
+ {
+ static int y = 0;
+ static assert(y.mangleof == "_D9test148314mainFZ4__S11yi");
+ }
+
+ {
+ void f() {}
+ static assert(f.mangleof == "_D9test148314mainFZ1fMFNaNbNiNfZv");
+ }
+ {
+ void f() {}
+ static assert(f.mangleof == "_D9test148314mainFZ4__S11fMFNaNbNiNfZv");
+ }
+
+ {
+ struct S {}
+ static assert(S.mangleof == "S9test148314mainFZ1S");
+ }
+ {
+ struct S {}
+ static assert(S.mangleof == "S9test148314mainFZ4__S11S");
+ }
+
+ {
+ class C {}
+ static assert(C.mangleof == "C9test148314mainFZ1C");
+ }
+ {
+ class C {}
+ static assert(C.mangleof == "C9test148314mainFZ4__S11C");
+ }
+
+ {
+ enum E { a }
+ static assert(E.mangleof == "E9test148314mainFZ1E");
+ static assert(E.a.mangleof == "_D9test148314mainFZ1E1aEQwQoFZQl");
+ }
+ {
+ enum E { a }
+ static assert(E.mangleof == "E9test148314mainFZ4__S11E");
+ static assert(E.a.mangleof == "_D9test148314mainFZ4__S11E1aEQBbQuFZ4__S1Qr");
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test14929.d b/gcc/testsuite/gdc.test/compilable/test14929.d
new file mode 100644
index 00000000000..1b794a66248
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test14929.d
@@ -0,0 +1,85 @@
+struct HashMap(K, V)
+{
+ V* opBinaryRight(string op)(K key) const if (op == "in")
+ {
+ size_t index;
+ foreach (ref node; buckets[index].range)
+ {
+ return &(node.value);
+ }
+ return null;
+ }
+
+ struct Node
+ {
+ K key;
+ V value;
+ }
+
+ alias Bucket = UnrolledList!(Node);
+ Bucket[] buckets;
+}
+
+struct UnrolledList(T)
+{
+ Range range() const pure
+ {
+ return Range(_front);
+ }
+
+ static struct Range
+ {
+ ref T front() const @property
+ {
+ return cast(T) current.items[index];
+ }
+ void popFront() pure
+ {
+ }
+ bool empty() const pure @property
+ {
+ return true;
+ }
+ const(Node)* current;
+ size_t index;
+ }
+
+ Node* _front;
+
+ static struct Node
+ {
+ ContainerStorageType!T[10] items;
+ }
+}
+
+template ContainerStorageType(T)
+{
+ alias ContainerStorageType = T;
+}
+
+template isComponentStorage(CS, C)
+{
+ enum bool isComponentStorage = is(typeof(
+ (inout int = 0)
+ {
+ CS cs = CS.init;
+ ulong eid;
+ cs.add(eid, C.init);
+ }));
+}
+
+struct HashmapComponentStorage(ComponentType)
+{
+ private HashMap!(ulong, ComponentType) components;
+
+ void add(ulong eid, ComponentType component)
+ {
+ assert(eid !in components);
+ }
+}
+
+static assert(isComponentStorage!(HashmapComponentStorage!int, int));
+
+void main()
+{
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test15019.d b/gcc/testsuite/gdc.test/compilable/test15019.d
index 963dba8fc37..41444ce7508 100644
--- a/gcc/testsuite/gdc.test/compilable/test15019.d
+++ b/gcc/testsuite/gdc.test/compilable/test15019.d
@@ -1,3 +1,4 @@
+// COMPILABLE_MATH_TEST
// https://issues.dlang.org/show_bug.cgi?id=15019
// dmd -m32 -c all.d
@@ -24,7 +25,7 @@ struct Matrix(T, int R, int C)
{
try
return format("%s", v);
- catch
+ catch (Throwable)
assert(false); // should not happen since format is right
}
}
@@ -67,7 +68,7 @@ struct Vector(T, int N)
{
try
return format("%s", v);
- catch
+ catch (Throwable)
assert(false);
}
}
diff --git a/gcc/testsuite/gdc.test/compilable/test15150.d b/gcc/testsuite/gdc.test/compilable/test15150.d
index 3a00b80348b..5c2d4212127 100644
--- a/gcc/testsuite/gdc.test/compilable/test15150.d
+++ b/gcc/testsuite/gdc.test/compilable/test15150.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/test15150a.d imports/test15150b.d
module test15150;
import imports.test15150a;
diff --git a/gcc/testsuite/gdc.test/compilable/test15225.d b/gcc/testsuite/gdc.test/compilable/test15225.d
new file mode 100644
index 00000000000..d60fba1e05b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test15225.d
@@ -0,0 +1,9 @@
+// https://issues.dlang.org/show_bug.cgi?id=15225
+
+alias foo = (int x) => x + 1;
+alias foo = (string x) => x ~ x;
+alias foo = (float x) => x - x;
+
+static assert( foo(1) == 2 );
+static assert( foo("a") == "aa");
+static assert(foo(2.0f) == 0.0f);
diff --git a/gcc/testsuite/gdc.test/compilable/test15292.d b/gcc/testsuite/gdc.test/compilable/test15292.d
new file mode 100644
index 00000000000..38c727b6e49
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test15292.d
@@ -0,0 +1,20 @@
+struct NullableRef15292(T)
+{
+ inout(T) get() inout
+ {
+ assert(false);
+ }
+
+ alias get this;
+}
+
+struct S15292
+{
+ NullableRef15292!S15292 n;
+}
+
+void main()
+{
+ S15292 s;
+ assert(s == s);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test1537.d b/gcc/testsuite/gdc.test/compilable/test1537.d
index a1717abfc00..0878b97230c 100644
--- a/gcc/testsuite/gdc.test/compilable/test1537.d
+++ b/gcc/testsuite/gdc.test/compilable/test1537.d
@@ -1,4 +1,4 @@
-// 1537
+// https://issues.dlang.org/show_bug.cgi?id=1537
void foo(char[] s)
{
@@ -56,10 +56,10 @@ int bug5245d(U)()
}
-static assert(!is(typeof(Compileable!(bug5245a!(int)()).OK)));
-static assert(!is(typeof(Compileable!(bug5245b!(int)()).OK)));
-static assert(!is(typeof(Compileable!(bug5245c!(int)()).OK)));
-static assert(!is(typeof(Compileable!(bug5245d!(int)()).OK)));
+static assert(is(typeof(Compileable!(bug5245a!(int)()).OK)));
+static assert(is(typeof(Compileable!(bug5245b!(int)()).OK)));
+static assert(is(typeof(Compileable!(bug5245c!(int)()).OK)));
+static assert(is(typeof(Compileable!(bug5245d!(int)()).OK)));
/**************************************/
diff --git a/gcc/testsuite/gdc.test/compilable/test15389_x.d b/gcc/testsuite/gdc.test/compilable/test15389_x.d
index 896f81a2a42..4cfabac252d 100644
--- a/gcc/testsuite/gdc.test/compilable/test15389_x.d
+++ b/gcc/testsuite/gdc.test/compilable/test15389_x.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: test15389_y.d
import test15389_y;
//struct ns
diff --git a/gcc/testsuite/gdc.test/compilable/test15389_y.d b/gcc/testsuite/gdc.test/compilable/test15389_y.d
index 35e6a2511fa..78f4779b956 100644
--- a/gcc/testsuite/gdc.test/compilable/test15389_y.d
+++ b/gcc/testsuite/gdc.test/compilable/test15389_y.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: test15389_x.d
import test15389_x;
//struct ns
diff --git a/gcc/testsuite/gdc.test/compilable/test1547.d b/gcc/testsuite/gdc.test/compilable/test1547.d
new file mode 100644
index 00000000000..30c6564946f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test1547.d
@@ -0,0 +1,20 @@
+// https://issues.dlang.org/show_bug.cgi?id=1547
+
+struct A
+{
+ int b;
+ static A opCall(int k)
+ {
+ A a;
+ a.b = k;
+ return a;
+ }
+}
+
+void fun(A k = 2) {}
+
+void main()
+{
+ A a = 7;
+ fun();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test15490.d b/gcc/testsuite/gdc.test/compilable/test15490.d
index eab5c61a74c..bd8e6686a7c 100644
--- a/gcc/testsuite/gdc.test/compilable/test15490.d
+++ b/gcc/testsuite/gdc.test/compilable/test15490.d
@@ -1,5 +1,6 @@
// REQUIRED_ARGS: -o- -inline
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/imp15490a.d imports/imp15490b.d
module test15490;
import imports.imp15490a;
diff --git a/gcc/testsuite/gdc.test/compilable/test15519_x.d b/gcc/testsuite/gdc.test/compilable/test15519_x.d
index 53cfe920a7a..d647b04a0a3 100644
--- a/gcc/testsuite/gdc.test/compilable/test15519_x.d
+++ b/gcc/testsuite/gdc.test/compilable/test15519_x.d
@@ -1,4 +1,4 @@
-
+// EXTRA_FILES: test15519_y.d
import test15519_y;
extern(C++, ns)
diff --git a/gcc/testsuite/gdc.test/compilable/test15519_y.d b/gcc/testsuite/gdc.test/compilable/test15519_y.d
index 58db30b2141..e87a4e187fc 100644
--- a/gcc/testsuite/gdc.test/compilable/test15519_y.d
+++ b/gcc/testsuite/gdc.test/compilable/test15519_y.d
@@ -1,4 +1,4 @@
-
+// EXTRA_FILES: test15519_x.d
import test15519_x: NS = ns; // fails
//import test15519_x; alias test15519_x.ns NS; // works
diff --git a/gcc/testsuite/gdc.test/compilable/test15780.d b/gcc/testsuite/gdc.test/compilable/test15780.d
index 25c64cde333..047d5f6b502 100644
--- a/gcc/testsuite/gdc.test/compilable/test15780.d
+++ b/gcc/testsuite/gdc.test/compilable/test15780.d
@@ -1,17 +1,24 @@
// PERMUTE_ARGS:
// https://issues.dlang.org/show_bug.cgi?id=15780
-import std.typecons;
-//import std.stdio;
-
void foo(alias fields)() {
foreach(i, field; fields) {
enum string a = fields[i]; // OK
enum string b = field; // not OK with 2.069.2 ???
- //writeln(field);
}
}
void main() {
foo!(tuple("H", "I"))();
}
+
+Tuple!T tuple(T...)(T values)
+{
+ return Tuple!T(values);
+}
+
+struct Tuple(T...)
+{
+ T values;
+ alias values this;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test15785.d b/gcc/testsuite/gdc.test/compilable/test15785.d
index 483c080861c..e7c66170e74 100644
--- a/gcc/testsuite/gdc.test/compilable/test15785.d
+++ b/gcc/testsuite/gdc.test/compilable/test15785.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/test15785.d
// PERMUTE_ARGS:
import imports.test15785;
@@ -13,5 +14,5 @@ class Derived : Base, IBase2
// IBase2.faz(); // doesn't work yet due to a bug in checkAccess
}
- super.T t;
+ typeof(super).T t;
}
diff --git a/gcc/testsuite/gdc.test/compilable/test15856.d b/gcc/testsuite/gdc.test/compilable/test15856.d
index 8a5c4257893..3fc1752d43e 100644
--- a/gcc/testsuite/gdc.test/compilable/test15856.d
+++ b/gcc/testsuite/gdc.test/compilable/test15856.d
@@ -1,11 +1,6 @@
// REQUIRED_ARGS: -de
// PERMUTE_ARGS:
-/*
-TEST_PUTPUT:
----
----
-*/
-
+// EXTRA_FILES: imports/a15856.d
class Foo
{
import imports.a15856;
diff --git a/gcc/testsuite/gdc.test/compilable/test15907.d b/gcc/testsuite/gdc.test/compilable/test15907.d
index c362e04dbc4..aae4b4d256d 100644
--- a/gcc/testsuite/gdc.test/compilable/test15907.d
+++ b/gcc/testsuite/gdc.test/compilable/test15907.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/imp15907.d
// PERMUTE_ARGS:
import imports.imp15907;
diff --git a/gcc/testsuite/gdc.test/compilable/test16002.d b/gcc/testsuite/gdc.test/compilable/test16002.d
index f7b4c2e96a8..51c7338cd67 100644
--- a/gcc/testsuite/gdc.test/compilable/test16002.d
+++ b/gcc/testsuite/gdc.test/compilable/test16002.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/plainpackage/plainmodule.d imports/pkgmodule/package.d imports/pkgmodule/plainmodule.d
module test.compilable.test16002;
import imports.plainpackage.plainmodule;
diff --git a/gcc/testsuite/gdc.test/compilable/test16013a.d b/gcc/testsuite/gdc.test/compilable/test16013a.d
new file mode 100644
index 00000000000..15eda38e768
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16013a.d
@@ -0,0 +1,13 @@
+// https://issues.dlang.org/show_bug.cgi?id=16013
+
+struct Impl { S _payload; } /* Only this line has changed from above. */
+
+struct RefCounted
+{
+ void opAssign(RefCounted rhs) {}
+ void opAssign(S rhs) {}
+ S refCountedPayload() { return S.init; }
+ alias refCountedPayload this;
+}
+
+struct S { RefCounted s; }
diff --git a/gcc/testsuite/gdc.test/compilable/test16013b.d b/gcc/testsuite/gdc.test/compilable/test16013b.d
new file mode 100644
index 00000000000..97a58e32c59
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16013b.d
@@ -0,0 +1,13 @@
+// https://issues.dlang.org/show_bug.cgi?id=16013
+
+S s; /* Only this line has changed from above. */
+
+struct RefCounted
+{
+ void opAssign(RefCounted rhs) {}
+ void opAssign(S rhs) {}
+ S refCountedPayload() { return S.init; }
+ alias refCountedPayload this;
+}
+
+struct S { RefCounted s; }
diff --git a/gcc/testsuite/gdc.test/compilable/test16037.d b/gcc/testsuite/gdc.test/compilable/test16037.d
new file mode 100644
index 00000000000..12c672abe4e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16037.d
@@ -0,0 +1,23 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=16037
+
+@safe:
+
+void testXXX () @nogc
+{
+ Object o;
+ scope bool delegate (Object) alwaysFalse = (Object y) { return false; };
+ scope c1 = o !is null ? (Object y) { return o is y; } : alwaysFalse;
+}
+
+auto f() @nogc
+{
+ int a;
+ void g(){ a=1; }
+ scope h=&g;
+ h();
+}
+
+
diff --git a/gcc/testsuite/gdc.test/compilable/test16085.d b/gcc/testsuite/gdc.test/compilable/test16085.d
index 936a1fbdaca..4f6d8015311 100644
--- a/gcc/testsuite/gdc.test/compilable/test16085.d
+++ b/gcc/testsuite/gdc.test/compilable/test16085.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/imp16085.d imports/imp16085b.d
// PERMUTE_ARGS:
import imports.imp16085;
diff --git a/gcc/testsuite/gdc.test/compilable/test16088.d b/gcc/testsuite/gdc.test/compilable/test16088.d
new file mode 100644
index 00000000000..97326f1fcd7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16088.d
@@ -0,0 +1,10 @@
+// REQUIRED_ARGS: -Jcompilable/imports/
+// EXTRA_FILES: imports/imp16088.d
+
+// https://issues.dlang.org/show_bug.cgi?id=16088
+
+void bar(string x) {}
+auto foo()
+{
+ import("imp16088.d").bar;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16107.d b/gcc/testsuite/gdc.test/compilable/test16107.d
new file mode 100644
index 00000000000..2267be3339f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16107.d
@@ -0,0 +1,14 @@
+// https://issues.dlang.org/show_bug.cgi?id=16107
+
+bool check()
+{
+ bool result = false;
+
+ result |= false;
+ if (result) goto ret;
+
+ result |= false;
+ if (result) {}
+
+ ret: return true;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16183.d b/gcc/testsuite/gdc.test/compilable/test16183.d
new file mode 100644
index 00000000000..ba741100ce6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16183.d
@@ -0,0 +1,7 @@
+// https://issues.dlang.org/show_bug.cgi?id=16183
+
+void main()
+{
+ const string g(const string s) { return s; }
+ enum string y = ['f'] ~ g("g");
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16214a.d b/gcc/testsuite/gdc.test/compilable/test16214a.d
index 2ea64d90329..f29c5d2bd7f 100644
--- a/gcc/testsuite/gdc.test/compilable/test16214a.d
+++ b/gcc/testsuite/gdc.test/compilable/test16214a.d
@@ -1,5 +1,5 @@
-// EXTRA_SOURCES: imports/test16214b.d
// REQUIRED_ARGS: -Icompilable/imports
+// EXTRA_FILES: imports/test16214b.d
module test16214a;
import test16214b;
diff --git a/gcc/testsuite/gdc.test/compilable/test16273.d b/gcc/testsuite/gdc.test/compilable/test16273.d
new file mode 100644
index 00000000000..c079f0a552b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16273.d
@@ -0,0 +1,22 @@
+// https://issues.dlang.org/show_bug.cgi?id=16273
+
+class A()
+{
+ alias MyD = D!();
+}
+
+class B
+{
+ void f() {}
+ alias MyA = A!();
+}
+
+class C : B
+{
+ override void f() {}
+}
+
+class D() : A!()
+{
+ void g() { new C; }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16460.d b/gcc/testsuite/gdc.test/compilable/test16460.d
index 868e7ecec4a..77193a4d567 100644
--- a/gcc/testsuite/gdc.test/compilable/test16460.d
+++ b/gcc/testsuite/gdc.test/compilable/test16460.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/imp16460.d
module imports.test16460;
void bug()
diff --git a/gcc/testsuite/gdc.test/compilable/test16492.d b/gcc/testsuite/gdc.test/compilable/test16492.d
new file mode 100644
index 00000000000..833be1d8cf3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16492.d
@@ -0,0 +1,87 @@
+// ARG_SETS: -debug; -o-; -debug -preview=dip1000
+// https://issues.dlang.org/show_bug.cgi?id=16492
+
+void mayCallGC();
+
+void test() @nogc pure
+{
+ debug new int(1);
+ debug
+ {
+ mayCallGC();
+ auto b = [1, 2, 3];
+ b ~= 4;
+ }
+}
+
+void debugSafe() @safe
+{
+ debug unsafeSystem();
+ debug unsafeTemplated();
+}
+
+void unsafeSystem() @system {}
+void unsafeTemplated()() {
+ int[] arr;
+ auto b = arr.ptr;
+}
+
+void debugSafe2() @safe
+{
+ char[] arr1, arr2;
+ debug unsafeDIP1000Lifetime(arr1, arr2);
+
+ char* ptr;
+ char[] arr;
+ debug ptr = arr.ptr;
+}
+
+void unsafeDIP1000Lifetime()(ref char[] p, scope char[] s)
+{
+ p = s;
+}
+
+
+void test2() nothrow
+{
+ debug throw new Exception("");
+}
+
+void test3() nothrow
+{
+ debug {
+ foreach (_; 0 .. 10) {
+ if (1) {
+ throw new Exception("");
+ }
+ }
+ }
+}
+
+void test4() nothrow
+{
+ debug throwException();
+}
+
+void test5() nothrow
+{
+ debug willThrowException();
+}
+
+void willThrowException()()
+{
+ throwException();
+}
+
+void throwException()
+{
+ throw new Exception("");
+}
+
+void test6() nothrow
+{
+ debug
+ {
+ () {throw new Exception("");}();
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16570.d b/gcc/testsuite/gdc.test/compilable/test16570.d
index 20b84857605..de8b78f4dde 100644
--- a/gcc/testsuite/gdc.test/compilable/test16570.d
+++ b/gcc/testsuite/gdc.test/compilable/test16570.d
@@ -5,4 +5,4 @@ enum Regression
a = _a,
}
-static assert(is(typeof(Regression.a) == Regression));
+static assert(is(typeof(Regression.a) == immutable(Regression)));
diff --git a/gcc/testsuite/gdc.test/compilable/test16578a.d b/gcc/testsuite/gdc.test/compilable/test16578a.d
new file mode 100644
index 00000000000..f781feec1ba
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16578a.d
@@ -0,0 +1,16 @@
+// REQUIRED_ARGS: -debug
+
+// https://issues.dlang.org/show_bug.cgi?id=16578
+
+string[string] opts;
+
+void main()
+{
+ string arg;
+ switch (arg)
+ {
+ case "-f": opts["fore"] = ""; break;
+ debug { case "-throw": opts["throw"] = ""; break; }
+ default:
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16578b.d b/gcc/testsuite/gdc.test/compilable/test16578b.d
new file mode 100644
index 00000000000..eccc949b9c2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16578b.d
@@ -0,0 +1,16 @@
+// https://issues.dlang.org/show_bug.cgi?id=16578
+
+void main()
+{
+ string[string] opts;
+ switch (2)
+ {
+ case 0:
+ opts["a"] = "";
+ {
+ case 1:
+ break;
+ }
+ default:
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16621.d b/gcc/testsuite/gdc.test/compilable/test16621.d
new file mode 100644
index 00000000000..d5800b12401
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16621.d
@@ -0,0 +1,23 @@
+// https://issues.dlang.org/show_bug.cgi?id=16621
+
+template xxx()
+{
+ Vector2f xxx()
+ {
+ return this;
+ }
+}
+
+struct Vector2f
+{
+ mixin xxx!();
+ alias xxx this;
+}
+
+void foo(ref Vector2f pos);
+
+void test()
+{
+ Vector2f v;
+ foo(v);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16635.d b/gcc/testsuite/gdc.test/compilable/test16635.d
new file mode 100644
index 00000000000..5f3d0ba9b6c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16635.d
@@ -0,0 +1,56 @@
+// https://issues.dlang.org/show_bug.cgi?id=16635
+
+struct A
+{
+ alias get this;
+
+ const(A) get() const
+ {
+ return A();
+ }
+}
+
+static assert(!__traits(compiles, A() + A()));
+
+// Original test (covers another path)
+
+struct Vector2
+{
+ float x;
+ float y;
+
+ alias byRef this;
+
+ ref const(Vector2) byRef() const
+ {
+ static Vector2 v;
+ return v;
+ }
+
+ Vector2 opBinary(string op : "+")(ref const(Vector2) a) const
+ {
+ return Vector2(x + a.x, y + a.y);
+ }
+}
+
+void test16635_1()
+{
+ Vector2 a = Vector2(1, 2);
+ Vector2 b = Vector2(3, 4);
+
+ // this line causes application to run infinitely
+ // Already fixed. It was issue 16621
+ Vector2 c = a + b;
+
+ // OK <- this line seg faults without the above line
+ Vector2 d = a + Vector2(5, 6);
+}
+
+void test16635_2()
+{
+ Vector2 a = Vector2(1, 2);
+ Vector2 b = Vector2(3, 4);
+
+ // just this line alone
+ Vector2 d = a + Vector2(5, 6);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16657.d b/gcc/testsuite/gdc.test/compilable/test16657.d
new file mode 100644
index 00000000000..3143ecd60dc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16657.d
@@ -0,0 +1,17 @@
+struct A
+{
+ int x;
+}
+
+struct B
+{
+ A a, b;
+}
+static assert(B(A(1), A(1)) != B(A(1), A(2))); // Works
+
+struct C
+{
+ A a, b;
+ alias a this;
+}
+static assert(C(A(1), A(1)) != C(A(1), A(2))); // Fails!
diff --git a/gcc/testsuite/gdc.test/compilable/test16685.d b/gcc/testsuite/gdc.test/compilable/test16685.d
new file mode 100644
index 00000000000..0be2367a3e3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16685.d
@@ -0,0 +1,6 @@
+// https://issues.dlang.org/show_bug.cgi?id=16685
+
+struct Id { ushort value; }
+enum Id x = Id(5);
+struct S(ushort A) {}
+alias CannotCreateFromValue = S!(x.value);
diff --git a/gcc/testsuite/gdc.test/compilable/test16709.d b/gcc/testsuite/gdc.test/compilable/test16709.d
new file mode 100644
index 00000000000..695c92ea2b6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test16709.d
@@ -0,0 +1,9 @@
+// EXTRA_FILES: imports/test16709a.d imports/test16709b.d imports/test16709c.d imports/test16709d.d
+// https://issues.dlang.org/show_bug.cgi?id=16709
+
+import imports.test16709a;
+import imports.test16709b;
+
+void test(){
+ 1.to!int;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test16798.d b/gcc/testsuite/gdc.test/compilable/test16798.d
index 41e09e0134a..fe0f128f60b 100644
--- a/gcc/testsuite/gdc.test/compilable/test16798.d
+++ b/gcc/testsuite/gdc.test/compilable/test16798.d
@@ -1,5 +1,6 @@
/*
REQUIRED_ARGS: -mv=its.a.dessert.topping=imports/imp16798.d -mv=its.a.floorwax=imports/
+EXTRA_FILES: imports/imp16798.d imports/wax16798.d
PERMUTE_ARGS:
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/compilable/test17057.d b/gcc/testsuite/gdc.test/compilable/test17057.d
index c90e35f99f5..677fe38dcdf 100644
--- a/gcc/testsuite/gdc.test/compilable/test17057.d
+++ b/gcc/testsuite/gdc.test/compilable/test17057.d
@@ -2,7 +2,7 @@
// PERMUTE_ARGS:
class LeClass {
- import std.stdio;
+ import core.stdc.stdio;
}
void main()
diff --git a/gcc/testsuite/gdc.test/compilable/test17143.d b/gcc/testsuite/gdc.test/compilable/test17143.d
index 98e31f4c324..4ec029572f4 100644
--- a/gcc/testsuite/gdc.test/compilable/test17143.d
+++ b/gcc/testsuite/gdc.test/compilable/test17143.d
@@ -1,4 +1,16 @@
-import std.typecons : tuple;
+// https://issues.dlang.org/show_bug.cgi?id=17143
+
+struct Tuple(T...)
+{
+ T values;
+ alias expand = values;
+}
+
+Tuple!T tuple(T...)(T args)
+{
+ return Tuple!T(args);
+}
+
enum foo = tuple(1, 2).expand;
static assert(typeof(foo).stringof == "(int, int)");
static assert(foo.stringof == "tuple(1, 2)");
diff --git a/gcc/testsuite/gdc.test/compilable/test17146.d b/gcc/testsuite/gdc.test/compilable/test17146.d
new file mode 100644
index 00000000000..098bfe4c5d0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17146.d
@@ -0,0 +1,13 @@
+// REQUIRED_ARGS: -O -inline
+
+// https://issues.dlang.org/show_bug.cgi?id=17146
+
+struct S { int[] a; int b; }
+
+void foo()
+{
+ S[] s;
+ if (s[$-1] == S.init) {}
+}
+
+void bar() { foo(); }
diff --git a/gcc/testsuite/gdc.test/compilable/test17351.d b/gcc/testsuite/gdc.test/compilable/test17351.d
new file mode 100644
index 00000000000..fffe92c4d58
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17351.d
@@ -0,0 +1,17 @@
+bool fun(S)(ref S[3] a) { assert(a == [42, 84, 169]); return true; }
+bool fun2(S)(ref S a) { return true; }
+void main()
+{
+ static const int[3] sa = [42, 84, 169];
+ static const double sa2 = 42.42;
+ static assert(fun(sa));
+ static assert(fun2(sa2));
+}
+
+int f1(ref const int p) { return p; }
+int f2(ref const int[2] p) { return p[0] + p[1]; }
+void test2()
+{
+ static immutable int[2] P = [ 0, 1 ];
+ static assert(f2(P) == 1);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17373.d b/gcc/testsuite/gdc.test/compilable/test17373.d
new file mode 100644
index 00000000000..9a5ee6431a2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17373.d
@@ -0,0 +1,32 @@
+// https://issues.dlang.org/show_bug.cgi?id=17373
+interface Foo { void visit (int); }
+interface Bar { void visit(double); }
+interface FooBar : Foo, Bar {}
+static assert(__traits(getOverloads, FooBar, "visit").length == 2);
+
+interface Fbar { void visit(char); void visit(double); }
+interface Triple : Foo, Bar, Fbar {}
+static assert(__traits(getOverloads, Triple, "visit").length == 3);
+
+interface InheritanceMadness : FooBar, Triple {}
+static assert(__traits(getOverloads, Triple, "visit").length == 3);
+
+interface Simple
+{
+ int square(int);
+ real square(real);
+}
+static assert(__traits(getOverloads, Simple, "square").length == 2);
+
+// https://issues.dlang.org/show_bug.cgi?id=19064
+interface InputStream {}
+interface OutputStream{}
+interface Stream : InputStream, OutputStream{}
+interface ConnectionStream : Stream
+{
+ @property bool connected() const;
+ void close();
+}
+
+static assert(__traits(getOverloads, ConnectionStream, "connected").stringof == "tuple(connected)");
+static assert(__traits(getOverloads, ConnectionStream, "close").stringof == "tuple(close)");
diff --git a/gcc/testsuite/gdc.test/compilable/test17419.d b/gcc/testsuite/gdc.test/compilable/test17419.d
index 295c563081a..e34e7da5444 100644
--- a/gcc/testsuite/gdc.test/compilable/test17419.d
+++ b/gcc/testsuite/gdc.test/compilable/test17419.d
@@ -1,6 +1,6 @@
+// REQUIRED_ARGS: -d
// https://issues.dlang.org/show_bug.cgi?id=17419
-
extern (C) int fooc();
alias aliasc = fooc;
diff --git a/gcc/testsuite/gdc.test/compilable/test17441.d b/gcc/testsuite/gdc.test/compilable/test17441.d
new file mode 100644
index 00000000000..2c356fdc03b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17441.d
@@ -0,0 +1,9 @@
+// EXTRA_FILES: imports/test17441foo/package.d imports/test17441foo/bar.d
+import m1 = imports.test17441foo.bar;
+import m2 = imports.test17441foo;
+alias p = __traits(parent, m1);
+enum e(alias thing) = thing.stringof;
+
+static assert(e!m1 == m1.stringof);
+static assert(e!m2 == m2.stringof);
+static assert( e!p == p.stringof );
diff --git a/gcc/testsuite/gdc.test/compilable/test17512.d b/gcc/testsuite/gdc.test/compilable/test17512.d
new file mode 100644
index 00000000000..4bf96b7b1a3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17512.d
@@ -0,0 +1,26 @@
+// https://issues.dlang.org/show_bug.cgi?id=17512
+
+struct A
+{
+ int _value;
+
+ bool _hasValue;
+
+ auto ref getOr(int alternativeValue)
+ {
+ return _hasValue ? _value : alternativeValue;
+ }
+}
+
+A a;
+
+// https://issues.dlang.org/show_bug.cgi?id=18661
+
+struct S0(T)
+{
+ int a;
+ auto ref immutable(int) getA() { return a; }
+}
+
+alias B = S0!int;
+
diff --git a/gcc/testsuite/gdc.test/compilable/test1754.d b/gcc/testsuite/gdc.test/compilable/test1754.d
index 3193477d309..c2d8ebb0e0a 100644
--- a/gcc/testsuite/gdc.test/compilable/test1754.d
+++ b/gcc/testsuite/gdc.test/compilable/test1754.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test1754a.d imports/test1754b.d
module test1754;
import imports.test1754a;
diff --git a/gcc/testsuite/gdc.test/compilable/test17541.d b/gcc/testsuite/gdc.test/compilable/test17541.d
new file mode 100644
index 00000000000..2fe43430cd3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17541.d
@@ -0,0 +1,28 @@
+/* EXTRA_SOURCES: imports/test17541_2.d imports/test17541_3.d
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=17541
+
+module one;
+
+import two;
+import three;
+
+struct BB
+{
+ enum MAX_NUM_FIBERS = 4096;
+
+ TWOR!1 t;
+ TT!(int) tt;
+ auto foo()
+ {
+ tt.insertabcdefg(1);
+ }
+}
+
+BB bb;
+
+@nogc bar()
+{
+ bb.foo();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17548.d b/gcc/testsuite/gdc.test/compilable/test17548.d
index 67b32d7a3ad..8e2a3907a59 100644
--- a/gcc/testsuite/gdc.test/compilable/test17548.d
+++ b/gcc/testsuite/gdc.test/compilable/test17548.d
@@ -1,5 +1,5 @@
// REQUIRED_ARGS: -c
-
+// EXTRA_FILES: imports/fwdref2_test17548.d
module test17548;
struct S1 {
diff --git a/gcc/testsuite/gdc.test/compilable/test17752.d b/gcc/testsuite/gdc.test/compilable/test17752.d
new file mode 100644
index 00000000000..71f324cc6cd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17752.d
@@ -0,0 +1,12 @@
+// https://issues.dlang.org/show_bug.cgi?id=17752
+// REQUIRED_ARGS: -de
+void main (string[] args)
+{
+ switch (args.length)
+ {
+ // initialization not done on purpose is allowed
+ int x = void;
+ default:
+ break;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17782.d b/gcc/testsuite/gdc.test/compilable/test17782.d
new file mode 100644
index 00000000000..588bc27635f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17782.d
@@ -0,0 +1,6 @@
+void main() {
+ string str = q"_DLANG
+123
+_DLANG";
+ assert( str == "123\n" );
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17793.d b/gcc/testsuite/gdc.test/compilable/test17793.d
new file mode 100644
index 00000000000..43c992bce75
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17793.d
@@ -0,0 +1,13 @@
+// REQUIRED_ARGS: -mcpu=avx2
+import core.simd;
+
+static if (__traits(compiles, double4))
+{
+ double4 foo();
+ void test(double[4]);
+
+ void main()
+ {
+ test(foo().array);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17807.d b/gcc/testsuite/gdc.test/compilable/test17807.d
new file mode 100644
index 00000000000..b8c5518c3b0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17807.d
@@ -0,0 +1,17 @@
+// REQUIRED_ARGS: -o- -w
+
+int bug17807(){
+ int y=0;
+ Lswitch: switch(2){
+ { case 0: break; }
+ enum x=0;
+ struct S{ enum x=0; }
+ int foo(){
+ return 0;
+ }
+ default: y=x+S.x+foo();
+ static foreach(i;1..5)
+ case i: break Lswitch;
+ }
+ return y;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17853.d b/gcc/testsuite/gdc.test/compilable/test17853.d
new file mode 100644
index 00000000000..d0e602546cf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17853.d
@@ -0,0 +1,11 @@
+// Switch with no braces & empty case should compile
+
+int main() {
+ int ob = 1;
+
+ final switch (ob)
+ case 0: case 1:
+ break;
+
+ return ob;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17906.d b/gcc/testsuite/gdc.test/compilable/test17906.d
new file mode 100644
index 00000000000..9c4a547a3e7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17906.d
@@ -0,0 +1,7 @@
+// REQUIRED_ARGS: -de
+// https://issues.dlang.org/show_bug.cgi?id=18647
+deprecated void main ()
+{
+ Object o = new Object;
+ delete o;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17942.d b/gcc/testsuite/gdc.test/compilable/test17942.d
new file mode 100644
index 00000000000..8fbc169e496
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17942.d
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=17942
+
+alias AliasSeq(TList...) = TList;
+
+void test()
+{
+ enum A = AliasSeq!(1);
+ static assert(A[0] == 1);
+ static assert(B[0] == 2);
+}
+
+enum B = AliasSeq!(2);
+
+enum C = AliasSeq!();
+
diff --git a/gcc/testsuite/gdc.test/compilable/test17970.d b/gcc/testsuite/gdc.test/compilable/test17970.d
new file mode 100644
index 00000000000..b7880e2bf74
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17970.d
@@ -0,0 +1,28 @@
+shared struct Shared
+{
+ static Shared make()
+ {
+ return Shared();
+ }
+
+ ~this()
+ {
+ }
+}
+
+shared struct Foo
+{
+ ~this()
+ {
+ }
+}
+
+struct Inner { ~this() {} }
+struct Outer { shared(Inner) inner; }
+
+void main()
+{
+ Foo x = Foo();
+ auto s = Shared.make();
+ Outer _;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18000.d b/gcc/testsuite/gdc.test/compilable/test18000.d
new file mode 100644
index 00000000000..9de33242e90
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18000.d
@@ -0,0 +1,19 @@
+// REQUIRED_ARGS: -preview=dip1000
+
+// https://issues.dlang.org/show_bug.cgi?id=18000
+
+struct File
+{
+@safe @nogc:
+ ~this() scope
+ {
+ }
+
+ void* f;
+}
+
+void test() @safe @nogc
+{
+ scope File x;
+ x = File();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18020.d b/gcc/testsuite/gdc.test/compilable/test18020.d
new file mode 100644
index 00000000000..d5999200c54
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18020.d
@@ -0,0 +1,8 @@
+// https://issues.dlang.org/show_bug.cgi?id=18020
+
+void bug(T)(T t)
+{
+ t.opCmp(t);
+}
+
+alias bugi = bug!(typeof(new class{})); \ No newline at end of file
diff --git a/gcc/testsuite/gdc.test/compilable/test18030.d b/gcc/testsuite/gdc.test/compilable/test18030.d
new file mode 100644
index 00000000000..f742a401afb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18030.d
@@ -0,0 +1,14 @@
+// https://issues.dlang.org/show_bug.cgi?id=18030
+
+struct S(T)
+{
+ T var;
+ static assert(__traits(getProtection, __traits(getMember, T, "func")) == "public");
+}
+
+class C
+{
+ alias Al = S!C;
+
+ static void func(U)(U var) { }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18099.d b/gcc/testsuite/gdc.test/compilable/test18099.d
new file mode 100644
index 00000000000..860f6a0f6e7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18099.d
@@ -0,0 +1,19 @@
+
+/* REQUIRED_ARGS: -betterC
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=18099
+
+struct D
+{
+ static struct V
+ {
+ ~this() { }
+ }
+
+ V get()
+ {
+ V v;
+ return v;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18115.d b/gcc/testsuite/gdc.test/compilable/test18115.d
new file mode 100644
index 00000000000..3ad5b4872e2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18115.d
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug.cgi?id=18115
+
+int test()
+{
+ if (test.stringof.length > 6 &&
+ test.stringof[$-7..$] == "1234567") {}
+ return 0;
+}
+
+enum a = test();
diff --git a/gcc/testsuite/gdc.test/compilable/test18199.d b/gcc/testsuite/gdc.test/compilable/test18199.d
new file mode 100644
index 00000000000..a3a0885970c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18199.d
@@ -0,0 +1,87 @@
+// https://issues.dlang.org/show_bug.cgi?id=18199
+
+//
+// struct initializer cases
+//
+
+// original error report
+struct Bla
+{
+ int delegate(int, int) fun;
+}
+Bla bla1 = Bla((int a, int b) { return a + b; });
+Bla bla2 = {(int a, int b) { return a + b; }}; // yielded error
+
+// additional error report with memberName:expression syntax
+struct Foo
+{
+ int function(int) bar;
+ int function(int) bar2;
+}
+Foo foo =
+{
+ bar : function(x) { return 2 * x; }, // yielded error
+ bar2 : (x) => 2 * x,
+};
+
+struct MyStruct
+{
+ int function() f;
+ int delegate() d;
+}
+
+// confirm that ambiguous cases assume struct initializer
+MyStruct ambiguous_1 = {};
+MyStruct ambiguous_2 =
+{
+ { return 1 + 1; }
+};
+
+// statement-holding function literal variants not covered above
+static MyStruct function_and_delegate_keywords =
+{
+ function () { return 1 + 1; },
+ delegate () { return 1 + 1; }
+};
+
+//
+// function literal initializer cases
+//
+
+alias IntFun = int function();
+alias VoidFun = void function();
+
+IntFun colon_at_top_level =
+{
+ return 1 + 1;
+};
+
+IntFun block_statement_only_with_nested_statement =
+{
+ if (true)
+ {
+ return 1 + 1;
+ }
+};
+
+struct SomeStruct {}
+
+// previously these cases were incorrectly parsed as struct initializer
+VoidFun[] no_semicolon_statements = [
+ { asm {} },
+ { class Foo {} },
+ { debug(foo) {} },
+ { enum Foo { A } },
+ { final switch(5) {} },
+ { if (true) {} },
+ { interface Foo {} },
+ { pragma(inline) {} },
+ { scope(exit) {} },
+ { struct Foo {} },
+ { synchronized {} },
+ { try {} finally {} },
+ { union Foo {} },
+ { version(foo) {} },
+ { while (false) {} },
+ { with (SomeStruct) {} },
+];
diff --git a/gcc/testsuite/gdc.test/compilable/test18251.d b/gcc/testsuite/gdc.test/compilable/test18251.d
new file mode 100644
index 00000000000..79408501a5d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18251.d
@@ -0,0 +1,23 @@
+// REQUIRED_ARGS: -unittest -de
+
+auto test18251(T)(T t)
+if (!__traits(isDeprecated, T))
+{
+ return T.init;
+}
+
+unittest
+{
+ auto b = test18251(2);
+}
+
+deprecated auto test18251(T)(T t) // deprecated storage class got lost when expanding.
+if (__traits(isDeprecated, T))
+{
+ return T.init;
+}
+
+deprecated unittest
+{
+ auto b = test18251(2 + 2i);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18385b.d b/gcc/testsuite/gdc.test/compilable/test18385b.d
new file mode 100644
index 00000000000..cf0fedcd859
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18385b.d
@@ -0,0 +1,29 @@
+/*
+Previous implementation raised errors because it was not
+aware of the special treatment for extern(C) member funtions:
+
+Member functions receive D name mangling...
+Some arguments in favour of this inconsistencies
+- static => useful for callbacks
+- non-static => doesn't exists in C anyways
+*/
+
+extern(C) struct ExternC
+{
+ void foo() {}
+ void foo(int) {}
+
+ static void bar() {}
+ static void bar(int) {}
+}
+
+#line 100
+void main()
+{
+ ExternC.bar();
+ ExternC.bar(1);
+
+ ExternC c;
+ c.foo();
+ c.foo(1);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18430.d b/gcc/testsuite/gdc.test/compilable/test18430.d
new file mode 100644
index 00000000000..381ceeb0c32
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18430.d
@@ -0,0 +1,11 @@
+template Alias(alias a)
+{
+ alias Alias = a;
+}
+
+void main()
+{
+ auto scale = 4;
+ alias edentity = a => a * scale;
+ static assert(__traits(isSame, Alias!edentity, edentity)); // fails in dmd-nightly
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18468.d b/gcc/testsuite/gdc.test/compilable/test18468.d
new file mode 100644
index 00000000000..8febc9bf7dd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18468.d
@@ -0,0 +1,5 @@
+// https://issues.dlang.org/show_bug.cgi?id=18468
+@safe void main()
+{
+ synchronized {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18474.d b/gcc/testsuite/gdc.test/compilable/test18474.d
new file mode 100644
index 00000000000..454e9c507f3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18474.d
@@ -0,0 +1,15 @@
+shared struct A
+{
+ this(this);
+}
+
+struct B
+{
+ A a;
+}
+
+void main()
+{
+ shared B b1;
+ auto b2 = b1;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18572.d b/gcc/testsuite/gdc.test/compilable/test18572.d
new file mode 100644
index 00000000000..5edc1a72d30
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18572.d
@@ -0,0 +1,16 @@
+// https://issues.dlang.org/show_bug.cgi?id=18572
+
+alias Seq(T...) = T;
+void func(Seq!(int, int, int) args = Seq!(1, 2, 3)) {}
+void func2(Seq!(int, int, int) args = Seq!(1,2,3), Seq!(int, int) args2 = Seq!(4, 5)) {}
+
+void main(){
+ Seq!(int,int,int) args = Seq!(1, 2, 3); // ok
+ func(); // error
+ func(args); // ok
+
+ Seq!(int, int) args2 = Seq!(4, 5);
+ func2();
+ func2(args);
+ func2(args, args2);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18578.d b/gcc/testsuite/gdc.test/compilable/test18578.d
new file mode 100644
index 00000000000..d75a063d09e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18578.d
@@ -0,0 +1,5 @@
+enum Foo { foo1 }
+enum Bar : Foo { bar }
+
+void main()
+{}
diff --git a/gcc/testsuite/gdc.test/compilable/test18584.d b/gcc/testsuite/gdc.test/compilable/test18584.d
new file mode 100644
index 00000000000..e447cc265f4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18584.d
@@ -0,0 +1,11 @@
+// https://issues.dlang.org/show_bug.cgi?id=18584
+
+struct S {
+ int n;
+ auto fun() { return tmp!(a => n)(); }
+}
+
+struct tmp(alias fns) {
+ alias fun = fns!int;
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/test18645.d b/gcc/testsuite/gdc.test/compilable/test18645.d
new file mode 100644
index 00000000000..acb55863f20
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18645.d
@@ -0,0 +1,9 @@
+// https://issues.dlang.org/show_bug.cgi?id=18645
+
+immutable INIT = 42;
+
+enum A
+{
+ x = INIT,
+ y
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18651a.d b/gcc/testsuite/gdc.test/compilable/test18651a.d
new file mode 100644
index 00000000000..a5ab9386b93
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18651a.d
@@ -0,0 +1,5 @@
+// REQUIRED_ARGS: -deps=${RESULTS_DIR}/compilable/test18651a.deps
+// EXTRA_SOURCES: imports/test18651/b.d
+// EXTRA_FILES: imports/test18651/c.d imports/test18651/algorithm.d imports/test18651/datetime.d
+
+import imports.test18651.datetime;
diff --git a/gcc/testsuite/gdc.test/compilable/test18670.d b/gcc/testsuite/gdc.test/compilable/test18670.d
new file mode 100644
index 00000000000..602396cf825
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18670.d
@@ -0,0 +1,12 @@
+// REQUIRED_ARGS: -preview=dip1000
+
+// https://issues.dlang.org/show_bug.cgi?id=18670
+
+void foo() {
+ new OVERLAPPED;
+}
+
+union OVERLAPPED {
+ uint OffsetHigh;
+ uint Pointer;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18694.d b/gcc/testsuite/gdc.test/compilable/test18694.d
new file mode 100644
index 00000000000..9b2018bba46
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18694.d
@@ -0,0 +1,9 @@
+struct S { enum int x = 42; }
+static S dummy;
+pure int fun(int x)
+{
+ return dummy.x + x;
+}
+
+void main()
+{}
diff --git a/gcc/testsuite/gdc.test/compilable/test18737.d b/gcc/testsuite/gdc.test/compilable/test18737.d
new file mode 100644
index 00000000000..17a27dfd092
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18737.d
@@ -0,0 +1,32 @@
+/* REQUIRED_ARGS:
+ * PERMUTE_ARGS:
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=18737
+
+struct S
+{
+ this(char);
+
+ this(int j)
+ {
+ this('a');
+ assert(0);
+ this('b');
+ }
+
+ this(long j)
+ {
+ if (j)
+ {
+ this('c');
+ assert(0);
+ }
+ else if (j + 1)
+ {
+ this('d');
+ return;
+ }
+ this('e');
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18771.d b/gcc/testsuite/gdc.test/compilable/test18771.d
new file mode 100644
index 00000000000..a71361f8ada
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18771.d
@@ -0,0 +1,7 @@
+// REQUIRED_ARGS : -c
+// EXTRA_FILES: imports/test18771a.d imports/test18771b.d imports/test18771c.d imports/test18771d.d
+// https://issues.dlang.org/show_bug.cgi?id=18771
+
+import imports.test18771c, imports.test18771d;
+
+static assert(__traits(isSame, fooC, fooD));
diff --git a/gcc/testsuite/gdc.test/compilable/test18775.d b/gcc/testsuite/gdc.test/compilable/test18775.d
new file mode 100644
index 00000000000..9cf1b3ed163
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18775.d
@@ -0,0 +1,20 @@
+// REQUIRED_ARGS: -de
+
+struct Foo { }
+
+struct Bar {
+ deprecated
+ @property Foo foo() { return Foo.init; }
+
+ alias foo this;
+}
+
+void test(Bar bar) { }
+
+void main()
+{
+ Bar bar;
+
+ // test lookup will be satisfied via ufcs, not alias, so it must not deprecation warn foo!
+ bar.test;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18821.d b/gcc/testsuite/gdc.test/compilable/test18821.d
new file mode 100644
index 00000000000..28bdecab9ff
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18821.d
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug.cgi?id=18821
+
+align(1) struct epoll_event
+{
+ void* ptr;
+}
+template isAllZeroBits(epoll_event value) {}
+alias isInitAllZeroBits = isAllZeroBits!(epoll_event.init);
+
+epoll_event e = { null };
diff --git a/gcc/testsuite/gdc.test/compilable/test18905.d b/gcc/testsuite/gdc.test/compilable/test18905.d
new file mode 100644
index 00000000000..c54c60c3477
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18905.d
@@ -0,0 +1,6 @@
+/* REQUIRED_ARGS: -betterC
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=18905
+
+extern (C++) class C { } // Error: TypeInfo cannot be used with -betterC
diff --git a/gcc/testsuite/gdc.test/compilable/test18936.d b/gcc/testsuite/gdc.test/compilable/test18936.d
new file mode 100644
index 00000000000..12f4b4bc9b6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18936.d
@@ -0,0 +1,31 @@
+// REQUIRED_ARGS: -fPIC -O -release -inline -m64 -betterC
+// DISABLED: win32 win64
+
+// https://issues.dlang.org/show_bug.cgi?id=18936
+// produces assert failure cgxmm.c line 684
+
+import core.stdc.math;
+
+struct S
+{
+ double re, im;
+
+
+ static S sqrtcx(S* z)
+ {
+ S c;
+ real x,y,w,r;
+
+ {
+ x = fabs(z.re);
+ y = fabs(z.im);
+ if (z.re >= 0)
+ {
+ c.im = (z.im >= 0) ? w : -w;
+ c.re = z.im / (c.im + c.im);
+ }
+ }
+ return c;
+ }
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/test18951a.d b/gcc/testsuite/gdc.test/compilable/test18951a.d
new file mode 100644
index 00000000000..8aedc8d6986
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18951a.d
@@ -0,0 +1,7 @@
+module compilable.test18951a;
+
+public class A
+{
+ package static void foo(Object) {}
+ public static void foo() {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18951b.d b/gcc/testsuite/gdc.test/compilable/test18951b.d
new file mode 100644
index 00000000000..fbf6b1d524e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18951b.d
@@ -0,0 +1,9 @@
+// EXTRA_FILES: test18951a.d
+module compilable.test18951b;
+
+import test18951a;
+
+void test()
+{
+ A.foo(new Object);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test18976.d b/gcc/testsuite/gdc.test/compilable/test18976.d
new file mode 100644
index 00000000000..57a531f8041
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test18976.d
@@ -0,0 +1,32 @@
+// https://issues.dlang.org/show_bug.cgi?id=18976
+
+class Expression : Statement {}
+class Statement {}
+
+class AssertSemanticVisitor
+{
+ void visit (const Statement node) { }
+}
+
+class ExpressionVisitor : AssertSemanticVisitor
+{
+ public void visit (Expression) { }
+
+ alias visit = typeof(super).visit;
+}
+
+class ExpressionVisitor2 : AssertSemanticVisitor
+{
+ public void visit (Expression) { }
+
+ alias visit = AssertSemanticVisitor.visit;
+}
+
+void main ()
+{
+ scope x1 = new ExpressionVisitor;
+ scope x2 = new ExpressionVisitor;
+ scope y = new Statement;
+ x1.visit(y);
+ x2.visit(y);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19014.d b/gcc/testsuite/gdc.test/compilable/test19014.d
new file mode 100644
index 00000000000..7bbbc941abe
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19014.d
@@ -0,0 +1,12 @@
+// https://issues.dlang.org/show_bug.cgi?id=19014
+
+import core.stdc.config;
+
+void main()
+{
+ if (true)
+ {
+ static import core.stdc.math;
+ }
+ static assert(!__traits(compiles, core.stdc.math.cos(0)));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19066.d b/gcc/testsuite/gdc.test/compilable/test19066.d
new file mode 100644
index 00000000000..53d5005b364
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19066.d
@@ -0,0 +1,13 @@
+class C {}
+
+int Object;
+
+struct S
+{
+ int object;
+ C Object;
+}
+
+void main()
+{
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19081.d b/gcc/testsuite/gdc.test/compilable/test19081.d
new file mode 100644
index 00000000000..ba5fd9c77d4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19081.d
@@ -0,0 +1,14 @@
+void main() {
+ @(1) enum { A }
+ /// comment
+ @(1) enum X { A }
+ @(2) enum Y;
+ @(1) @(2) enum Z { A }
+
+ struct Test { int test; }
+ @Test(1) enum W { A }
+ @(1) enum V: int { A }
+ X a;
+ static assert(__traits(getAttributes, X).length == 1);
+ static assert(__traits(getAttributes, X)[0] == 1);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19097.d b/gcc/testsuite/gdc.test/compilable/test19097.d
new file mode 100644
index 00000000000..19e189caf5d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19097.d
@@ -0,0 +1,23 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ */
+
+// Related to: https://github.com/dlang/dmd/pull/8504
+
+@safe:
+
+void betty()(ref int* r, return scope int* p)
+{
+ r = p; // infer `scope` for r
+}
+
+void boop()(ref int* r, scope int* p)
+{
+ r = p; // infer `scope` for r, `return` for p
+}
+
+void foo(scope int* pf)
+{
+ scope int* rf;
+ betty(rf, pf);
+ boop(rf, pf);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19108.d b/gcc/testsuite/gdc.test/compilable/test19108.d
new file mode 100644
index 00000000000..46207509da1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19108.d
@@ -0,0 +1,9 @@
+// REQUIRED_ARGS: -ignore
+
+// https://issues.dlang.org/show_bug.cgi?id=19108
+
+pragma(unknown_global);
+void main()
+{
+ pragma(unknown_local); // Error: unrecognized pragma
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19187.d b/gcc/testsuite/gdc.test/compilable/test19187.d
new file mode 100644
index 00000000000..9d0630a639c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19187.d
@@ -0,0 +1,6 @@
+// EXTRA_FILES: imports/test19187.d
+import imports.test19187;
+void main()
+{
+ enum test = __traits(compiles, imports.test19187.foo);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19203.d b/gcc/testsuite/gdc.test/compilable/test19203.d
new file mode 100644
index 00000000000..206d662b431
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19203.d
@@ -0,0 +1,27 @@
+//https://issues.dlang.org/show_bug.cgi?id=19203
+struct BoolWithErr {
+ bool b;
+ string error;
+ alias b this;
+}
+
+struct Foo {
+ int popBack() { return 0; }
+}
+
+struct Bar {}
+
+template hasPopBack(T) {
+ static if (!is(typeof(T.init.popBack)))
+ enum hasPopBack = BoolWithErr(false, T.stringof~" does not have popBack");
+ else
+ enum hasPopBack = BoolWithErr(true,"");
+}
+
+void test()
+{
+ static assert( hasPopBack!Foo);
+ static assert(!hasPopBack!Bar);
+ static assert( hasPopBack!Foo && !hasPopBack!Bar);
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/test19224.d b/gcc/testsuite/gdc.test/compilable/test19224.d
new file mode 100644
index 00000000000..4020e0daea0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19224.d
@@ -0,0 +1,18 @@
+// https://issues.dlang.org/show_bug.cgi?id=19224
+static if (__traits(compiles, __vector(float[4])))
+{
+ float sum(const float[4] val)
+ {
+ float sum = 0;
+ foreach (x; val) sum += x;
+ return sum;
+ }
+
+ alias float4 = __vector(float[4]);
+
+ enum x = sum(float4.init.array);
+ static assert(x is float.nan);
+
+ enum y = sum(float4(1).array);
+ static assert(y == 4);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19227.d b/gcc/testsuite/gdc.test/compilable/test19227.d
new file mode 100644
index 00000000000..27dab9ec19f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19227.d
@@ -0,0 +1,29 @@
+// https://issues.dlang.org/show_bug.cgi?id=19227
+/* TEST_OUTPUT:
+---
+compilable/test19227.d(18): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead
+Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead
+Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead
+Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead
+---
+*/
+
+struct S
+{
+ float f;
+}
+
+struct T
+{
+ cfloat cf;
+}
+
+void main()
+{
+ static assert(S.init is S.init);
+ static assert(S.init != S.init);
+
+ static assert(T.init is T.init);
+ static assert(T.init != T.init);
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/test19315.d b/gcc/testsuite/gdc.test/compilable/test19315.d
new file mode 100644
index 00000000000..0c31ab84341
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19315.d
@@ -0,0 +1,20 @@
+//https://issues.dlang.org/show_bug.cgi?id=19315
+void main()
+{
+ #line 100 "file.d"
+ enum code = q{
+ #line 10
+ };
+ static assert(__LINE__ == 103);
+ static assert(__FILE__ == "file.d");
+
+ #line 200 "file2.d"
+ enum code2 = q{
+ q{
+ #line 5
+ }
+ #line 9 "foo.d"
+ };
+ static assert(__LINE__ == 206);
+ static assert(__FILE__ == "file2.d");
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19409.d b/gcc/testsuite/gdc.test/compilable/test19409.d
new file mode 100644
index 00000000000..94915e8477c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19409.d
@@ -0,0 +1,6 @@
+// https://issues.dlang.org/show_bug.cgi?id=19409
+
+module test.foo;
+
+static if (__traits(compiles, __traits(identifier, test.foo))) {} // fails
+else { static assert(0); }
diff --git a/gcc/testsuite/gdc.test/compilable/test19464.d b/gcc/testsuite/gdc.test/compilable/test19464.d
new file mode 100644
index 00000000000..d0d38958f49
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19464.d
@@ -0,0 +1,5 @@
+// https://issues.dlang.org/show_bug.cgi?id=19464
+
+typeof(a0) b0 = 3;
+immutable int a0 = 4;
+static assert(is(typeof(b0) == immutable(int)));
diff --git a/gcc/testsuite/gdc.test/compilable/test19491.d b/gcc/testsuite/gdc.test/compilable/test19491.d
new file mode 100644
index 00000000000..3a341b3ad26
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19491.d
@@ -0,0 +1,11 @@
+// https://issues.dlang.org/show_bug.cgi?id=19491
+
+class Foo
+{
+ shared this();
+}
+
+void test()
+{
+ scope foo = new shared Foo();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19499.d b/gcc/testsuite/gdc.test/compilable/test19499.d
new file mode 100644
index 00000000000..c3ff73efe63
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19499.d
@@ -0,0 +1,6 @@
+// https://issues.dlang.org/show_bug.cgi?id=19499
+
+enum __c_long_double : double;
+enum A(T : double) = true;
+enum A(T : __c_long_double) = false;
+static assert(A!double == true);
diff --git a/gcc/testsuite/gdc.test/compilable/test19519.d b/gcc/testsuite/gdc.test/compilable/test19519.d
new file mode 100644
index 00000000000..37999666ad9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19519.d
@@ -0,0 +1,15 @@
+struct S { char[1] e = void; }
+
+void init(ref char[1] e)
+{
+ e[0 .. 1] = "A";
+}
+
+int foo()
+{
+ auto s = S();
+ init(s.e);
+ return __ctfe;
+}
+
+static assert(foo() == 1);
diff --git a/gcc/testsuite/gdc.test/compilable/test19540.d b/gcc/testsuite/gdc.test/compilable/test19540.d
new file mode 100644
index 00000000000..51328971a14
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19540.d
@@ -0,0 +1,2 @@
+alias inst = templ!();
+template templ(T = typeof(new class {})) {}
diff --git a/gcc/testsuite/gdc.test/compilable/test19557.d b/gcc/testsuite/gdc.test/compilable/test19557.d
new file mode 100644
index 00000000000..f107e228a48
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19557.d
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug.cgi?id=19557
+// Error: redundant linkage `extern (C++)`
+
+extern(C++, "ns")
+extern(C++, class)
+struct test {}
+
+extern(C++, class)
+extern(C++, "ns")
+struct test2 {}
diff --git a/gcc/testsuite/gdc.test/compilable/test19609.d b/gcc/testsuite/gdc.test/compilable/test19609.d
index a3d69233249..2646bd44894 100644
--- a/gcc/testsuite/gdc.test/compilable/test19609.d
+++ b/gcc/testsuite/gdc.test/compilable/test19609.d
@@ -1,10 +1,11 @@
// https://issues.dlang.org/show_bug.cgi?id=19609
+// EXTRA_FILES: imports/test19609a.d imports/test19609b.d imports/test19609c.d
/*
TEST_OUTPUT
---
-compilable/test19609.d(10): Deprecation: module `imports.test19609a` is deprecated -
-compilable/test19609.d(11): Deprecation: module `imports.test19609b` is deprecated - hello
-compilable/test19609.d(12): Deprecation: module `imports.test19609c` is deprecated -
+compilable/test19609.d(11): Deprecation: module `imports.test19609a` is deprecated -
+compilable/test19609.d(12): Deprecation: module `imports.test19609b` is deprecated - hello
+compilable/test19609.d(13): Deprecation: module `imports.test19609c` is deprecated -
---
*/
import imports.test19609a;
diff --git a/gcc/testsuite/gdc.test/compilable/test19631.d b/gcc/testsuite/gdc.test/compilable/test19631.d
new file mode 100644
index 00000000000..3a4dd8e51e3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19631.d
@@ -0,0 +1,11 @@
+struct Field(int _w, int _h)
+{
+ bool[_h][_w] s;
+}
+
+struct Life(int w, int h)
+{
+ auto a = new Field!(w, h); // ICE
+}
+
+alias T = Life!(100, 100);
diff --git a/gcc/testsuite/gdc.test/compilable/test19652.d b/gcc/testsuite/gdc.test/compilable/test19652.d
new file mode 100644
index 00000000000..193c717bd2c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19652.d
@@ -0,0 +1,22 @@
+struct Base {
+ int i;
+}
+
+struct A {
+ Base base;
+ alias base this;
+}
+
+struct B {
+ A a;
+ alias a this;
+}
+
+auto asGeneric(inout ref Base block) @nogc {
+ return &block;
+}
+
+B* thingie;
+auto foo() {
+ return asGeneric(*thingie);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19656.d b/gcc/testsuite/gdc.test/compilable/test19656.d
new file mode 100644
index 00000000000..bcfd36ebb5b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19656.d
@@ -0,0 +1,14 @@
+/*
+PERMUTE_ARGS:
+EXTRA_FILES: imports/test19656a.d imports/test19656b.d imports/test19656c.d
+*/
+
+import imports.test19656a;
+import imports.test19656c: Thud;
+
+class Foo
+{
+ Foo[Foo] _map;
+ void func (Thud ) { }
+ void thunk () { }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19657a.d b/gcc/testsuite/gdc.test/compilable/test19657a.d
new file mode 100644
index 00000000000..1aefb5e27a0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19657a.d
@@ -0,0 +1,11 @@
+/*
+REQUIRED_ARGS: -Icompilable/imports
+EXTRA_SOURCES: imports/test19657b.d imports/test19657c.d imports/test19657d.d imports/test19657e.d imports/test19657f.d imports/test19657g.d
+*/
+
+import test19657c;
+import test19657e: Bar;
+class Foo {
+ int[Foo] _map;
+ bool func (Foo rhs, Bar bee) { return true; }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19713.d b/gcc/testsuite/gdc.test/compilable/test19713.d
new file mode 100644
index 00000000000..5201404e204
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19713.d
@@ -0,0 +1,14 @@
+extern (C++)
+{
+ double twice(double d)
+ {
+ return d * 2;
+ }
+
+ void* createFunction(R)(R function(double));
+}
+
+void main()
+{
+ const f = createFunction(&twice);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19728.d b/gcc/testsuite/gdc.test/compilable/test19728.d
new file mode 100644
index 00000000000..551ac0f6a89
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19728.d
@@ -0,0 +1,52 @@
+/*
+TEST_OUTPUT:
+---
+tuple((A), (B))
+tuple((A), (B), 0)
+tuple((A), (B), (A))
+tuple((A), (B), (A), (B))
+tuple((A), (B), (A), (B))
+tuple((A), (B), (A), (B), (A), (B), (A), (B))
+tuple((Attr))
+---
+*/
+
+// Issue 19728
+enum A; enum B; enum Dummy = 0;
+
+alias Seq(T...) = T;
+
+@Seq!(A,B) struct Foo1 {}
+@Seq!(A,B, Dummy) struct Foo2 {}
+@Seq!(A,B,A) struct Foo3 {}
+@Seq!(Seq!(A,B,A,B)) struct Foo4 {}
+@Seq!(A,Seq!(B,A),B) struct Foo5 {}
+@Seq!(A,Seq!(B,A),B) @Seq!(A,B,A,B) struct Foo6 {}
+
+pragma(msg, __traits(getAttributes, Foo1));
+pragma(msg, __traits(getAttributes, Foo2));
+pragma(msg, __traits(getAttributes, Foo3));
+pragma(msg, __traits(getAttributes, Foo4));
+pragma(msg, __traits(getAttributes, Foo5));
+pragma(msg, __traits(getAttributes, Foo6));
+
+struct S(T...) {}
+static assert(is( S!(A,B) == S!(__traits(getAttributes, Foo1))));
+static assert(is( S!(A,B,Dummy) == S!(__traits(getAttributes, Foo2))));
+static assert(is( S!(A,B,A) == S!(__traits(getAttributes, Foo3))));
+static assert(is( S!(A,B,A,B) == S!(__traits(getAttributes, Foo4))));
+static assert(is( S!(A,B,A,B) == S!(__traits(getAttributes, Foo5))));
+static assert(is(S!(A,B,A,B,A,B,A,B) == S!(__traits(getAttributes, Foo6))));
+
+// Issue 20093
+mixin template MakeProperty(Attributes...) {
+ @(Attributes) void bug() {}
+}
+
+struct Attr { }
+
+struct Test {
+ mixin MakeProperty!(Attr);
+}
+
+pragma(msg, __traits(getAttributes, Test.bug));
diff --git a/gcc/testsuite/gdc.test/compilable/test19731.d b/gcc/testsuite/gdc.test/compilable/test19731.d
new file mode 100644
index 00000000000..09c51026cf8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19731.d
@@ -0,0 +1,78 @@
+// https://issues.dlang.org/show_bug.cgi?id=19731
+
+class Out19731
+{
+ static struct State
+ {
+ int flags_;
+ }
+ Object obj_;
+
+ invariant (obj_ !is null);
+
+ auto obj7(out State state)
+ {
+ return this.obj_;
+ }
+
+ enum compiles = __traits(compiles, &Out19731.init.obj7);
+}
+
+class Arguments19731
+{
+ Object obj_;
+
+ invariant (obj_ !is null);
+
+ import core.stdc.stdarg;
+ auto obj7(...)
+ {
+ return this.obj_;
+ }
+
+ enum compiles = __traits(compiles, &Arguments19731.init.obj7);
+}
+
+class Require19731
+{
+ Object obj_;
+
+ invariant (obj_ !is null);
+
+ auto obj7(int a)
+ in(a != 0)
+ {
+ return this.obj_;
+ }
+
+ enum compiles = __traits(compiles, &Require19731.init.obj7);
+}
+
+class Ensure19731
+{
+ Object obj_;
+
+ invariant (obj_ !is null);
+
+ auto obj7(int a)
+ out(result; result is obj_)
+ {
+ return this.obj_;
+ }
+
+ enum compiles = __traits(compiles, &Ensure19731.init.obj7);
+}
+
+class Sync19731
+{
+ Object obj_;
+
+ invariant (obj_ !is null);
+
+ synchronized auto obj7()
+ {
+ return this.obj_;
+ }
+
+ enum compiles = __traits(compiles, &Sync19731.init.obj7);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19746.d b/gcc/testsuite/gdc.test/compilable/test19746.d
new file mode 100644
index 00000000000..28677b09e73
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19746.d
@@ -0,0 +1,16 @@
+// REQUIRED_ARGS: -Icompilable/imports
+// EXTRA_FILES: imports/test19746a.d imports/test19746b.d imports/test19746c.d imports/test19746d.d
+import test19746c;
+import test19746b: Frop;
+
+template Base(T)
+{
+ static if (is(T == super)) alias Base = Object;
+}
+
+class Foo
+{
+ class Nested: Base!Foo { }
+ void func(Frop) { }
+ void thunk() { }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19750.d b/gcc/testsuite/gdc.test/compilable/test19750.d
new file mode 100644
index 00000000000..0d57749c955
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19750.d
@@ -0,0 +1,7 @@
+// REQUIRED_ARGS: -Icompilable/imports
+// EXTRA_FILES: imports/test19750a.d imports/test19750b.d imports/test19750c.d imports/test19750d.d
+import test19750b;
+class Foo {
+ import test19750a;
+ void func (Bar ) {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19754.d b/gcc/testsuite/gdc.test/compilable/test19754.d
new file mode 100644
index 00000000000..a7ad4ac651e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19754.d
@@ -0,0 +1,45 @@
+// https://issues.dlang.org/show_bug.cgi?id=19754
+
+void test19754()
+{
+ shared int x;
+ static assert((cast(int*) &x) == &(cast() x));
+ (cast() x) = 5;
+ (cast() x) += 3;
+
+ const int x1;
+ static assert(&x1 == &(cast() x1));
+ (cast() x1) = 5;
+ (cast() x1) *= 3;
+
+ immutable int x2;
+ static assert(&x2 == &(cast() x2));
+ (cast() x2) = 5;
+ (cast() x2) &= 3;
+
+ int[4] a;
+ (cast(long[2]) a)[0] = 5;
+ (cast(long[2]) a)[0] += 3;
+
+ static if (is(__vector(int[4])))
+ {
+ __vector(int[4]) v;
+ (cast(int[4]) v)[0] = 5;
+ (cast(int[4]) v)[0] += 3;
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20608
+
+void foo(T...)(auto ref T args) {}
+
+struct Tuple
+{
+ int expand;
+}
+
+void test20608()
+{
+ enum tup = Tuple();
+ foo(tup.expand);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19804.d b/gcc/testsuite/gdc.test/compilable/test19804.d
new file mode 100644
index 00000000000..136e89ffda2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19804.d
@@ -0,0 +1,9 @@
+// https://issues.dlang.org/show_bug.cgi?id=19804
+
+struct A { float e; }
+
+void foo(A[1] a)
+{
+ void bar(A[1] a) { a[] = null; }
+ bar(a);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19809.d b/gcc/testsuite/gdc.test/compilable/test19809.d
new file mode 100644
index 00000000000..3344ef11059
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19809.d
@@ -0,0 +1,14 @@
+// https://issues.dlang.org/show_bug.cgi?id=19809
+mixin template Impl(M...)
+{
+ int opCmp(Object o) { return 0; }
+}
+
+class C
+{
+ override
+ {
+ int function(int) fp = ((int x) => x);
+ mixin Impl!("x", "y", ((int x) => x));
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19833.d b/gcc/testsuite/gdc.test/compilable/test19833.d
new file mode 100644
index 00000000000..6ffa5c48b78
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19833.d
@@ -0,0 +1,27 @@
+struct S
+{
+ template Temp(int x)
+ {
+ enum xxx = x;
+ }
+}
+
+alias TT = __traits(getMember, S, "Temp");
+enum x = TT!2.xxx;
+static assert(x == 2);
+
+class A
+{
+ mixin temp!("uint");
+ mixin temp!("float");
+
+ mixin template temp(string source)
+ {
+ private enum inner(string s) = s;
+ }
+}
+
+class B
+{
+ alias member = __traits(getMember, A, __traits(allMembers, A)[0]);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19840.d b/gcc/testsuite/gdc.test/compilable/test19840.d
new file mode 100644
index 00000000000..e0c7b2814b3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19840.d
@@ -0,0 +1,21 @@
+// https://issues.dlang.org/show_bug.cgi?id=19840
+struct G
+{
+ ubyte[] I;
+ alias I this;
+}
+
+auto M(ubyte[])
+{
+ G N;
+ return N;
+}
+
+struct U { int V; }
+
+void X()
+{
+ func((cast(U[])[].M));
+}
+
+void func(U[]) {}
diff --git a/gcc/testsuite/gdc.test/compilable/test19895.d b/gcc/testsuite/gdc.test/compilable/test19895.d
new file mode 100644
index 00000000000..b2d8e68fe08
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19895.d
@@ -0,0 +1,7 @@
+// https://issues.dlang.org/show_bug.cgi?id=19895
+
+void fn()
+{
+ void[] a;
+ auto b = cast(byte[0][]) a;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19936.d b/gcc/testsuite/gdc.test/compilable/test19936.d
new file mode 100644
index 00000000000..248e28a7060
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19936.d
@@ -0,0 +1,17 @@
+// REQUIRED_ARGS: -de
+
+struct Bla
+{
+ deprecated("bla")
+ int get() { return 5; }
+
+ alias get this;
+}
+
+void main()
+{
+ Bla[] blaArray;
+ // ~= should not try to call `.get`, because there's no indication that
+ // `blaArray` has any kind of opAppendAssign related overload in the first place.
+ blaArray ~= Bla();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19954.d b/gcc/testsuite/gdc.test/compilable/test19954.d
new file mode 100644
index 00000000000..6ccc8beca89
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19954.d
@@ -0,0 +1,14 @@
+// https://issues.dlang.org/show_bug.cgi?id=19954
+
+template AliasSeq(TList...)
+{
+ alias AliasSeq = TList;
+}
+
+void fun(string[]){}
+
+void main()
+{
+ fun(cast(string[])AliasSeq!"");
+ auto a = cast(char[][])"";
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19970.d b/gcc/testsuite/gdc.test/compilable/test19970.d
new file mode 100644
index 00000000000..52f8e0c3493
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test19970.d
@@ -0,0 +1,16 @@
+// https://issues.dlang.org/show_bug.cgi?id=19970
+
+enum void* p = cast(void*)0;
+static assert(p is null);
+static assert(ctfeLocal(p));
+static assert(ctfeGlobal());
+
+bool ctfeGlobal ()
+{
+ return p is null;
+}
+
+bool ctfeLocal (const void* ptr) pure
+{
+ return ptr is null;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20000.d b/gcc/testsuite/gdc.test/compilable/test20000.d
new file mode 100644
index 00000000000..11548cbedc4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20000.d
@@ -0,0 +1,9 @@
+// https://issues.dlang.org/show_bug.cgi?id=20000
+
+interface A { int a(); }
+interface B { int b(); }
+class C {}
+
+bool isA(Object x) @safe { return cast(A) x !is null; }
+bool isA(B x) @safe { return cast(A) x !is null; }
+bool isA(C x) @safe { return cast(A) x !is null; }
diff --git a/gcc/testsuite/gdc.test/compilable/test20021.d b/gcc/testsuite/gdc.test/compilable/test20021.d
new file mode 100644
index 00000000000..26ed1acf6bb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20021.d
@@ -0,0 +1,22 @@
+// https://issues.dlang.org/show_bug.cgi?id=20021
+struct S
+{
+ bool opCast(T : bool)() { return true; }
+ S prop() {return this;}
+ S prop(S newThis) {return this;}
+}
+
+struct T
+{
+ bool opCast(T : bool)() { return false; }
+}
+
+void test20021()
+{
+ static if (T.init)
+ static assert(false);
+
+ // ensure properties are resolved
+ static if (!(true && T.init || (S.init.prop = S.init).prop))
+ static assert(false);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20039.d b/gcc/testsuite/gdc.test/compilable/test20039.d
new file mode 100644
index 00000000000..d912139d803
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20039.d
@@ -0,0 +1,13 @@
+void foo()() { }
+class bar { }
+
+alias bug = foo;
+alias bug = bar;
+
+template Identity(T...) { }
+
+void main()
+{
+ alias member1 = Identity!(__traits(getMember, mixin(__MODULE__), "bug"));
+ alias member2 = Identity!(__traits(getMember, mixin(__MODULE__), "bug"));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20051.d b/gcc/testsuite/gdc.test/compilable/test20051.d
new file mode 100644
index 00000000000..20c1188489f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20051.d
@@ -0,0 +1,18 @@
+// https://issues.dlang.org/show_bug.cgi?id=20051
+struct Templ2(Args...)
+{
+}
+
+struct WillAlsoWork(alias T : Templ!Args, Args...)
+{
+ alias A = Args[0];
+}
+
+void main()
+{
+ alias C2 = Templ2!int;
+ static assert(!__traits(compiles, {
+ alias B2 = WillAlsoWork!C2;
+ B2.A a2;
+ }));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20065.d b/gcc/testsuite/gdc.test/compilable/test20065.d
new file mode 100644
index 00000000000..c1be44dda8e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20065.d
@@ -0,0 +1,12 @@
+alias AliasSeq(T...) = T;
+
+void main()
+{
+ enum string[] array1 = [AliasSeq!("foo")];
+
+ static assert(array1 == ["foo"]);
+
+ enum string[] array2 = [AliasSeq!()];
+
+ static assert(array2 == []);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20100.d b/gcc/testsuite/gdc.test/compilable/test20100.d
new file mode 100644
index 00000000000..bc8ad2c40e6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20100.d
@@ -0,0 +1,50 @@
+// REQUIRED_ARGS: -checkaction=context
+struct STuple {
+ bool opEquals(STuple) { return false; }
+}
+
+class CTuple {
+}
+
+void testStruct() {
+ STuple t1;
+ assert(t1 == t1);
+}
+
+void testClass() {
+ CTuple t1 = new CTuple();
+ assert(t1 == t1);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20331
+void testAnonymousFunction()
+{
+ bool function() check = () => true;
+ assert(check());
+
+ bool result = true;
+ assert((() => result)());
+}
+
+void main() {
+ testStruct();
+ testClass();
+ testAnonymousFunction();
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20989
+ void test20989() @safe
+{
+ uint[] arr = [1, 2, 3];
+ assert(arr.ptr);
+ assert(!arr.ptr);
+ assert(arr.ptr is arr.ptr);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21765
+ref int func21765(int);
+void test21765()
+{
+ assert((func21765(1) = 2) == 2);
+ assert((1.func21765 = 2) == 2);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20136.d b/gcc/testsuite/gdc.test/compilable/test20136.d
new file mode 100644
index 00000000000..a5fb8e3d504
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20136.d
@@ -0,0 +1,18 @@
+// https://issues.dlang.org/show_bug.cgi?id=20136
+class Context
+{
+ size_t[const(Key)] aa;
+ bool checkAll;
+}
+
+struct Key
+{
+ Context context;
+ int i;
+ bool opEquals(ref const Key other) const
+ {
+ if(context.checkAll && i != other.i)
+ return false;
+ return true;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20138.d b/gcc/testsuite/gdc.test/compilable/test20138.d
new file mode 100644
index 00000000000..f4626aefbe5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20138.d
@@ -0,0 +1,16 @@
+alias C = const int;
+static assert(is(shared(C) U == shared U) && is(U == C));
+static assert(is(shared(C) == shared U, U) && is(U == C));
+
+alias I = inout int;
+static assert(is(shared(I) U == shared U) && is(U == I));
+static assert(is(shared(I) == shared U, U) && is(U == I));
+
+alias IC = inout const int;
+static assert(is(shared(IC) U == shared U) && is(U == IC));
+static assert(is(shared(IC) == shared U, U) && is(U == IC));
+
+alias S = shared int;
+static assert(is(const(S) U == const U) && is(U == shared int));
+static assert(is(inout(S) U == inout U) && is(U == shared int));
+static assert(is(inout(const S) U == inout(const U)) && is(U == shared int));
diff --git a/gcc/testsuite/gdc.test/compilable/test20181.d b/gcc/testsuite/gdc.test/compilable/test20181.d
new file mode 100644
index 00000000000..e71b4784f7c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20181.d
@@ -0,0 +1,11 @@
+module test20181;
+
+struct InversionList
+{
+ ubyte[] byCodepoint() { return null; }
+}
+
+void main()
+{
+ static foreach (ch; InversionList().byCodepoint) { }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20296.d b/gcc/testsuite/gdc.test/compilable/test20296.d
new file mode 100644
index 00000000000..2b16fa540dd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20296.d
@@ -0,0 +1,8 @@
+// https://issues.dlang.org/show_bug.cgi?id=20296
+
+extern (C++)
+void foo(T...)(T args)
+{
+}
+
+alias F = foo!(char*);
diff --git a/gcc/testsuite/gdc.test/compilable/test20318.d b/gcc/testsuite/gdc.test/compilable/test20318.d
new file mode 100644
index 00000000000..58d39697515
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20318.d
@@ -0,0 +1,7 @@
+// https://issues.dlang.org/show_bug.cgi?id=20318
+// REQUIRED_ARGS: -dip1008 -profile=gc
+
+void main()
+{
+ throw new Exception("msg");
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20326.d b/gcc/testsuite/gdc.test/compilable/test20326.d
new file mode 100644
index 00000000000..1da3eb6e6dc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20326.d
@@ -0,0 +1,11 @@
+class C;
+static assert(C.stringof == "C");
+
+interface I;
+static assert(I.stringof == "I");
+
+union U;
+static assert(U.stringof == "U");
+
+struct S;
+static assert(S.stringof == "S");
diff --git a/gcc/testsuite/gdc.test/compilable/test20367.d b/gcc/testsuite/gdc.test/compilable/test20367.d
new file mode 100644
index 00000000000..ebdf9ee5b8d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20367.d
@@ -0,0 +1,14 @@
+// https://issues.dlang.org/show_bug.cgi?id=20367
+
+struct A
+{
+ int x;
+ this(ref return scope A rhs) {}
+ @disable this(this) {}
+}
+
+void main()
+{
+ A a;
+ A b = a; // copy constructor gets called
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20388.d b/gcc/testsuite/gdc.test/compilable/test20388.d
new file mode 100644
index 00000000000..e0beac9159d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20388.d
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug.cgi?id=20388
+
+void main() {
+ foo!(mixin("(int a) { return a; }"))();
+ foo!(mixin("1+1"))();
+ foo!(mixin(0))();
+}
+
+void foo(alias t)() {
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20406.d b/gcc/testsuite/gdc.test/compilable/test20406.d
new file mode 100644
index 00000000000..c3d494f3525
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20406.d
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=20406
+struct S
+{
+ @disable this();
+ this(int) {}
+ this(ref S other) {}
+}
+
+void foo(S s) {}
+
+void main()
+{
+ S s = S(3);
+ foo(s);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20410.d b/gcc/testsuite/gdc.test/compilable/test20410.d
new file mode 100644
index 00000000000..bed8017822b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20410.d
@@ -0,0 +1,8 @@
+// https://issues.dlang.org/show_bug.cgi?id=20410
+
+enum E : string { foo = "foo" }
+
+static assert( is(E : string));
+static assert( is(E : T[], T));
+static assert(!is(E == string));
+static assert(!is(E == T[], T));
diff --git a/gcc/testsuite/gdc.test/compilable/test20417.d b/gcc/testsuite/gdc.test/compilable/test20417.d
new file mode 100644
index 00000000000..230ad317db9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20417.d
@@ -0,0 +1,12 @@
+// https://issues.dlang.org/show_bug.cgi?id=20417
+
+struct A { ~this(); }
+void f(A, int);
+A a();
+int i();
+
+static assert(__traits(compiles, { f(a, i); }));
+static assert(__traits(compiles, f(a, i)));
+
+static assert(is(typeof({ f(a, i); })));
+static assert(is(typeof(f(a, i))));
diff --git a/gcc/testsuite/gdc.test/compilable/test20420.d b/gcc/testsuite/gdc.test/compilable/test20420.d
new file mode 100644
index 00000000000..5d99e9c0852
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20420.d
@@ -0,0 +1,22 @@
+// REQUIRED_ARGS: -inline
+
+// https://issues.dlang.org/show_bug.cgi?id=20420
+
+struct S { ~this(); }
+
+class C
+{
+ this(S, int) {}
+}
+
+int i();
+
+C create()
+{
+ return new C(S(), i());
+}
+
+auto test()
+{
+ auto c = create();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20488.d b/gcc/testsuite/gdc.test/compilable/test20488.d
new file mode 100644
index 00000000000..7b15ee15158
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20488.d
@@ -0,0 +1,11 @@
+module test20488;
+
+// https://issues.dlang.org/show_bug.cgi?id=20488
+struct Bar {
+ void opDispatch(string s, Args...) (Args args) {
+ }
+ void fun() {
+ (bool[int]).init.length;
+ this.f((int[int]).init.length);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20596.d b/gcc/testsuite/gdc.test/compilable/test20596.d
new file mode 100644
index 00000000000..cd059c962ea
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20596.d
@@ -0,0 +1,31 @@
+// PERMUTE_ARGS: -preview=dip1000
+
+// https://issues.dlang.org/show_bug.cgi?id=20596
+
+struct S(T)
+{
+ void delegate() dg;
+
+ this(scope void delegate() dg)
+ {
+ this.dg = dg;
+ }
+}
+
+@nogc void fooTemplate()
+{
+ int num;
+
+ void foo() { int dummy = num; }
+
+ scope s = S!int(&foo);
+}
+
+void test3032() @nogc
+{
+ int n = 1;
+ scope fp = (){ n = 10; }; // no closure
+ fp();
+}
+
+
diff --git a/gcc/testsuite/gdc.test/compilable/test20653.d b/gcc/testsuite/gdc.test/compilable/test20653.d
new file mode 100644
index 00000000000..c7f329d777f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20653.d
@@ -0,0 +1,20 @@
+// LINK:
+enum x = 10;
+enum Thing { A, B }
+enum IsThing(T) = is(T == enum) || (__traits(getLinkage, T) == "C++");
+enum Is = IsThing!Thing;
+
+static assert(Is);
+
+enum Another(T) = is(T == int) && (__traits(getLinkage, T) == "C++");
+
+static assert(!Another!Thing);
+
+T myFunc(T)() { return T.init; }
+enum Foo(T) = is(T == int) || myFunc!T();
+
+void main ()
+{
+ assert(myFunc!int() == 0);
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/test20656.d b/gcc/testsuite/gdc.test/compilable/test20656.d
new file mode 100644
index 00000000000..f68d6547026
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20656.d
@@ -0,0 +1,11 @@
+// https://issues.dlang.org/show_bug.cgi?id=20656
+
+import core.stdc.stdlib : free, malloc;
+
+@live
+void main()
+{
+ auto p = malloc(1);
+ free(p);
+ free(p);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20661.d b/gcc/testsuite/gdc.test/compilable/test20661.d
new file mode 100644
index 00000000000..c755fa851ac
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20661.d
@@ -0,0 +1,17 @@
+// https://issues.dlang.org/show_bug.cgi?id=20661
+
+class Context
+{
+ size_t[const(Key)] aa; /* Error: AA key type Key does not have bool opEquals(ref const Key) const */
+ bool* checkAll;
+}
+
+struct Key
+{
+ Context context;
+ bool opEquals(ref const Key other) @safe const
+ {
+ auto c = context.checkAll;
+ return true;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20695.d b/gcc/testsuite/gdc.test/compilable/test20695.d
new file mode 100644
index 00000000000..fe4074b1c11
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20695.d
@@ -0,0 +1,35 @@
+// https://issues.dlang.org/show_bug.cgi?id=20695
+struct Bar
+{
+ this(const ref Bar o) {}
+
+ string a;
+ uint b;
+}
+
+struct Bar1
+{
+ @disable this(int a);
+ this(const ref Bar1 o) {}
+
+ string a;
+ uint b;
+}
+
+struct Bar2
+{
+ this(const ref Bar2 o) {}
+ @disable this(T)(T a) {}
+
+ string a;
+ uint b;
+}
+void main ()
+{
+ Bar b = { a: "Hello", b: 42 };
+ Bar c = Bar("Hello", 42);
+
+ Bar1 b1 = { a: "Hello", b: 42 };
+
+ Bar2 b2 = { a: "Hello", b: 42 };
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20710.d b/gcc/testsuite/gdc.test/compilable/test20710.d
new file mode 100644
index 00000000000..277a3e56df8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20710.d
@@ -0,0 +1,24 @@
+// https://issues.dlang.org/show_bug.cgi?id=20710
+
+// with empty array literal
+immutable A a = A(B([]));
+immutable B b = a.b;
+
+struct A {
+ B b;
+}
+
+struct B {
+ int[] c;
+}
+
+// with empty struct literal
+immutable C c = C(D());
+immutable D d = c.d;
+
+struct C {
+ D d;
+}
+
+struct D {
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20744.d b/gcc/testsuite/gdc.test/compilable/test20744.d
new file mode 100644
index 00000000000..ab9a9c87b8b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20744.d
@@ -0,0 +1,13 @@
+// https://issues.dlang.org/show_bug.cgi?id=20744
+
+struct A {
+ struct S {}
+ void f(@S int = 3);
+ alias fun = Issue20744!f;
+}
+
+template Issue20744(func...) {
+ static if (is(typeof(func[0]) PT == __parameters)) {
+ alias Issue20744 = (PT args) {};
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20789.d b/gcc/testsuite/gdc.test/compilable/test20789.d
new file mode 100644
index 00000000000..185042e86d2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20789.d
@@ -0,0 +1,34 @@
+// REQUIRED_ARGS: -de
+module compilable.test20789;
+
+struct S(bool deprecateFunction, bool deprecateAlias)
+{
+ static if (deprecateFunction)
+ deprecated string get() { return "foo"; }
+ else
+ string get() { return "foo"; }
+
+ static if (deprecateAlias)
+ deprecated alias get this;
+ else
+ alias get this;
+}
+
+void main()
+{
+ void normalFun()
+ {
+ static assert( is(S!(false, false) : string));
+ static assert(!is(S!(false, true ) : string));
+ static assert(!is(S!(true , false) : string));
+ static assert(!is(S!(true , true ) : string));
+ }
+ deprecated void deprecatedFun()
+ {
+ // deprecations are allowed in a deprecated scope.
+ static assert(is(S!(false, false) : string));
+ static assert(is(S!(false, true ) : string));
+ static assert(is(S!(true , false) : string));
+ static assert(is(S!(true , true ) : string));
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20795.d b/gcc/testsuite/gdc.test/compilable/test20795.d
new file mode 100644
index 00000000000..4b5cb178016
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20795.d
@@ -0,0 +1,35 @@
+// https://issues.dlang.org/show_bug.cgi?id=20795
+
+// REQUIRED_ARGS: -preview=dip1000
+
+struct Foo
+{
+ void opEquals(T)(T rhs) if (T.init.opCast!string) {}
+}
+
+struct Bar
+{
+ void opEquals()(Bar)
+ {
+ Gun() == Foo();
+ }
+}
+
+class Baz
+{
+ void opCast(T)() {}
+}
+
+struct Gun
+{
+ void[24] buff;
+
+ auto underlying()
+ {
+ return cast(Baz) buff.ptr;
+ }
+
+ alias underlying this;
+
+ void opEquals(R)(R) if (Bar.init == R.init) {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20821.d b/gcc/testsuite/gdc.test/compilable/test20821.d
new file mode 100644
index 00000000000..c0182977c3d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20821.d
@@ -0,0 +1,17 @@
+// https://issues.dlang.org/show_bug.cgi?id=20821
+
+struct S
+{
+ int gun()(int i) { return 0; }
+ alias fun = gun;
+ int fun() { return 1; }
+
+ static int sgun()(int i) { return 0; }
+ alias sfun = sgun;
+ static int sfun() { return 1; }
+}
+
+static assert(S().fun == 1); // expressionsem.d changes
+static assert(S.sfun == 1); // ditto
+
+static assert(__traits(getOverloads, S, "fun", true).length == 2); // traits.d changes
diff --git a/gcc/testsuite/gdc.test/compilable/test20835.d b/gcc/testsuite/gdc.test/compilable/test20835.d
new file mode 100644
index 00000000000..d1c01397f53
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20835.d
@@ -0,0 +1,59 @@
+// EXTRA_FILES: imports/test19344.d
+
+// https://issues.dlang.org/show_bug.cgi?id=20835
+
+template T(E) {
+ alias T = __traits(getAttributes, E.a);
+}
+
+void main()
+{
+ class C {}
+ enum E {
+ @C a
+ }
+
+ alias b = T!E;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19344
+
+import imports.test19344;
+
+struct Struct {
+ int value;
+}
+
+enum Enum {
+ @Struct(42) first,
+}
+
+static assert(getUDAs!(Enum.first, Struct)[0] == Struct(42));
+static assert(__traits(getAttributes, Enum.first)[0] == Struct(42));
+
+// https://issues.dlang.org/show_bug.cgi?id=21122
+
+void test21122()
+{
+ enum A;
+ enum E { @A a }
+
+ static assert(is(getAllUDAs!(E.a)[0] == A));
+}
+
+alias getAllUDAs(A...) = __traits(getAttributes, A);
+
+// https://issues.dlang.org/show_bug.cgi?id=21352
+
+@("aaa") enum Hoge {
+ @("bbb") foo, // tuple("aaa", "bbb") -> should be only tuple("bbb")
+ bar, // tuple()
+}
+@("aaa") struct Fuga {
+ @("bbb") int foo; // tuple("bbb")
+ int bar; // tuple()
+}
+static assert([__traits(getAttributes, Hoge.foo)] == ["bbb"]); //NG -> fixed
+static assert([__traits(getAttributes, Hoge.bar)] == []);
+static assert([__traits(getAttributes, Fuga.foo)] == ["bbb"]);
+static assert([__traits(getAttributes, Fuga.bar)] == []);
diff --git a/gcc/testsuite/gdc.test/compilable/test20842.d b/gcc/testsuite/gdc.test/compilable/test20842.d
new file mode 100644
index 00000000000..86049e6db48
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20842.d
@@ -0,0 +1,33 @@
+// https://issues.dlang.org/show_bug.cgi?id=20842
+
+struct A
+{
+ int i;
+ @disable this(ref A);
+}
+
+A a = { i: 123 };
+
+struct B
+{
+ int i;
+ @disable this();
+}
+
+B b = { i: 123 };
+
+union C
+{
+ int i;
+ @disable this(ref C);
+}
+
+C c = { i: 123 };
+
+union D
+{
+ int i;
+ @disable this();
+}
+
+D d = { i: 123 };
diff --git a/gcc/testsuite/gdc.test/compilable/test20868.d b/gcc/testsuite/gdc.test/compilable/test20868.d
new file mode 100644
index 00000000000..90c3d321f93
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20868.d
@@ -0,0 +1,8 @@
+// https://issues.dlang.org/show_bug.cgi?id=20868
+// REQUIRED_ARGS: -preview=dip1000
+
+void scoped (scope void delegate() dg)
+{
+ static void delegate()[] dgs;
+ dgs ~= dg; // error
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20906.d b/gcc/testsuite/gdc.test/compilable/test20906.d
new file mode 100644
index 00000000000..86ffedaa794
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20906.d
@@ -0,0 +1,15 @@
+/* REQUIRED_ARGS: -O
+ * No divide-by-zero constant folding errors
+ * https://issues.dlang.org/show_bug.cgi?id=20906
+ */
+
+int test12()
+{
+ int x = 0;
+ int a = x && 1 / x;
+ int b = !x || 1 / x;
+ int c = x ? 1 / x : 1;
+ int d = !x ? 1 : 1 / x;
+ return a | b | c;
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/test20909.d b/gcc/testsuite/gdc.test/compilable/test20909.d
new file mode 100644
index 00000000000..743c979ba19
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20909.d
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug.cgi?id=20909
+
+struct S
+{
+ long a;
+ static assert(x.sizeof == 4);
+ enum offset = x.offsetof;
+ static assert(offset == 8); // OK now
+ int x;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20923.d b/gcc/testsuite/gdc.test/compilable/test20923.d
new file mode 100644
index 00000000000..bae1eb33f55
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20923.d
@@ -0,0 +1,13 @@
+version (D_LP64)
+{
+ alias size_t = uint;
+}
+else
+{
+ alias size_t = ulong;
+}
+
+struct S
+{
+ real not_reproduceable_without_this_variable;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test20958.d b/gcc/testsuite/gdc.test/compilable/test20958.d
new file mode 100644
index 00000000000..d4ea92f722e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20958.d
@@ -0,0 +1,2 @@
+// EXTRA_FILES: imports/u20958.d
+import imports.u20958;
diff --git a/gcc/testsuite/gdc.test/compilable/test20990.d b/gcc/testsuite/gdc.test/compilable/test20990.d
new file mode 100644
index 00000000000..aeae11d6fdf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test20990.d
@@ -0,0 +1,19 @@
+// REQUIRED_ARGS: -O
+// https://issues.dlang.org/show_bug.cgi?id=20990
+
+// foo() and bar() should produce the same code when
+// optimized.
+
+void foo(int* ptr)
+{
+ if (ptr is null)
+ assert(false);
+ *ptr = 42;
+}
+
+void bar(int* ptr)
+{
+ assert(ptr);
+ *ptr = 42;
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/test21050.d b/gcc/testsuite/gdc.test/compilable/test21050.d
new file mode 100644
index 00000000000..5e81d2382bf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21050.d
@@ -0,0 +1,24 @@
+/*
+TEST_OUTPUT:
+----
+instantiated:long
+instantiated:int
+----
+*/
+struct S {
+ static int foo(T)(int i) { pragma(msg, "instantiated:", T.stringof); return 0; }
+ static int foo(T)(string s) { return 1; }
+}
+
+alias foo0 = __traits(getOverloads, S, "foo", true)[0];
+alias bar0 = foo0!long; // prints "instantiated:long"
+enum x = S.foo!long(0); // should not print "instantiated:long" again.
+static assert(bar0(3) == 0);
+alias bar0int = foo0!int; // prints "instantiated:int"
+enum y = S.foo!int(0); // should not print "instantiated:int" again.
+static assert(!__traits(compiles, bar0("hi")));
+
+alias foo1 = __traits(getOverloads, S, "foo", true)[1];
+alias bar1 = foo1!long;
+static assert(bar1("hi") == 1);
+static assert(!__traits(compiles, bar1(3)));
diff --git a/gcc/testsuite/gdc.test/compilable/test21058.d b/gcc/testsuite/gdc.test/compilable/test21058.d
new file mode 100644
index 00000000000..8da9346c67a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21058.d
@@ -0,0 +1,25 @@
+struct A {
+ int foo(string s) { return 0; }
+}
+
+static assert(__traits(getOverloads, A.init, "foo")[0]("hi") == 0);
+static assert(__traits(getOverloads, A.init, "foo", true)[0]("hi") == 0);
+
+struct B {
+ int foo()(int i) { return 1; }
+ int foo(string s) { return 0; }
+}
+
+alias a = __traits(getOverloads, B.init, "foo", true);
+
+static assert(__traits(getOverloads, B.init, "foo")[0]("hi") == 0);
+static assert(__traits(getOverloads, B.init, "foo", true)[0]("hi") == 0);
+
+struct C {
+ static int foo()(int i) { return 1; }
+ int foo(string s) { return 0; }
+}
+
+static assert(__traits(getOverloads, C.init, "foo")[0]("hi") == 0);
+static assert(__traits(getOverloads, C.init, "foo", true)[0]("hi") == 0);
+static assert(__traits(getOverloads, C.init, "foo", true)[1](7) == 1);
diff --git a/gcc/testsuite/gdc.test/compilable/test21227.d b/gcc/testsuite/gdc.test/compilable/test21227.d
new file mode 100644
index 00000000000..29dd2af29c3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21227.d
@@ -0,0 +1,19 @@
+// REQUIRED_ARGS: -Jcompilable/imports
+// EXTRA_FILES: imports/test21227/a..b.txt imports/test21227/a.txt imports/test21227/..foo/a.txt
+
+// https://issues.dlang.org/show_bug.cgi?id=21227
+
+void bar(string x) {}
+void test21227()
+{
+ import("./test21227/a.txt").bar;
+ import("test21227//a..b.txt").bar;
+ import("test21227/..foo/a.txt").bar;
+
+ version(Windows)
+ {
+ import(r".\test21227\a.txt").bar;
+ import(r"test21227\\a..b.txt").bar;
+ import(r"test21227\..foo\a.txt").bar;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21255.d b/gcc/testsuite/gdc.test/compilable/test21255.d
new file mode 100644
index 00000000000..99a4dfb6b13
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21255.d
@@ -0,0 +1,11 @@
+module test21255;
+
+void decodeA()(int i) { }
+void decodeB()(string s) { }
+
+alias decode = decodeA;
+alias decode = decodeB;
+
+void foo(alias A)() { A(1); A("hello"); }
+
+void main() { foo!decode; }
diff --git a/gcc/testsuite/gdc.test/compilable/test21282.d b/gcc/testsuite/gdc.test/compilable/test21282.d
new file mode 100644
index 00000000000..761a6a60dd7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21282.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+tuple(func)
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=21282
+
+template I(T...) { alias I = T; }
+
+template Bug(T...) {
+ alias Bug = mixin("I!(T[0])");
+}
+void func() {}
+pragma(msg, Bug!func);
diff --git a/gcc/testsuite/gdc.test/compilable/test21299a.d b/gcc/testsuite/gdc.test/compilable/test21299a.d
index 049ee6ae35b..3c6d8ec3c11 100644
--- a/gcc/testsuite/gdc.test/compilable/test21299a.d
+++ b/gcc/testsuite/gdc.test/compilable/test21299a.d
@@ -1,4 +1,4 @@
-// EXTRA_SOURCES: imports/test21299/mtype.d imports/test21299/rootstringtable.d
+// EXTRA_SOURCES: imports/test21299/mtype.d imports/test21299/func.d imports/test21299/rootstringtable.d
// REQUIRED_ARGS: -main
// LINK
module test21299a;
diff --git a/gcc/testsuite/gdc.test/compilable/test21299b.d b/gcc/testsuite/gdc.test/compilable/test21299b.d
index b9d992acfb9..9291c481bc7 100644
--- a/gcc/testsuite/gdc.test/compilable/test21299b.d
+++ b/gcc/testsuite/gdc.test/compilable/test21299b.d
@@ -1,4 +1,4 @@
-// EXTRA_SOURCES: imports/test21299/func.d imports/test21299/rootstringtable.d
+// EXTRA_SOURCES: imports/test21299/mtype.d imports/test21299/func.d imports/test21299/rootstringtable.d
// REQUIRED_ARGS: -main
// LINK:
module test21299b;
diff --git a/gcc/testsuite/gdc.test/compilable/test21330.d b/gcc/testsuite/gdc.test/compilable/test21330.d
new file mode 100644
index 00000000000..4abf36717b5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21330.d
@@ -0,0 +1,22 @@
+/*
+REQUIRED_ARGS: -unittest
+TEST_OUTPUT:
+---
+tuple(__unittest_L14_C5_1, __unittest_L14_C5_2)
+tuple(__unittest_L14_C5_2)
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=21330
+
+module test21330;
+
+mixin template Test() {
+ unittest {
+ }
+}
+
+mixin Test;
+mixin Test tm;
+
+pragma(msg, __traits(getUnitTests, test21330));
+pragma(msg, __traits(getUnitTests, tm));
diff --git a/gcc/testsuite/gdc.test/compilable/test21372.d b/gcc/testsuite/gdc.test/compilable/test21372.d
new file mode 100644
index 00000000000..8d867214834
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21372.d
@@ -0,0 +1,24 @@
+// REQUIRED_ARGS: -de
+struct S
+{
+ deprecated void foo(T)(int) { }
+ void foo(T)(string) { }
+}
+
+// just to be safe, check this order too
+// (there were some issues where naive checks of overloads were order dependent)
+struct T
+{
+ void foo(T)(string) { }
+ deprecated void foo(T)(int) { }
+}
+
+void main()
+{
+ // this should not hit the deprecation
+ // because the parameter type doesn't match it
+ S().foo!int("hi");
+
+ // likewise
+ T().foo!int("hi");
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21398.d b/gcc/testsuite/gdc.test/compilable/test21398.d
new file mode 100644
index 00000000000..951bd91eea0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21398.d
@@ -0,0 +1,31 @@
+// https://issues.dlang.org/show_bug.cgi?id=21398
+
+module test21398;
+
+void free(void* ptr);
+
+class MAlloc(T)
+{
+ import test21398: free;
+
+ void free(T)(T* value)
+ {
+ free(value);
+ }
+}
+
+struct Box(T)
+{
+ private T* __ptr;
+ alias A = MAlloc!T;
+
+ ~this()
+ {
+ A.free(__ptr);
+ }
+}
+
+void main()
+{
+ auto b = Box!(char)();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21464.d b/gcc/testsuite/gdc.test/compilable/test21464.d
new file mode 100644
index 00000000000..752cb5713e1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21464.d
@@ -0,0 +1,7 @@
+// https://issues.dlang.org/show_bug.cgi?id=21464
+// EXTRA_FILES: imports/test21464a.d
+void foo() pure
+{
+ import imports.test21464a : Mallocator;
+ auto a = Mallocator.instance; // mutable static, but empty struct
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21514.d b/gcc/testsuite/gdc.test/compilable/test21514.d
new file mode 100644
index 00000000000..4eb80509f32
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21514.d
@@ -0,0 +1,20 @@
+// https://issues.dlang.org/show_bug.cgi?id=21514
+// DISABLED: win32 win64
+/* TEST_OUTPUT:
+---
+compilable/test21514.d(16): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/test21514.d(16): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/test21514.d(17): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/test21514.d(17): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/test21514.d(19): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/test21514.d(19): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead
+compilable/test21514.d(20): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead
+compilable/test21514.d(20): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead
+---
+*/
+
+extern(C++) cdouble cpp_cadd1(cdouble c) { return c + 1; }
+extern(C++) creal cpp_cadd1l(creal c) { return c + 1; }
+
+cdouble cadd1(cdouble c) { return cpp_cadd1(c); }
+creal cadd1(creal c) { return cpp_cadd1l(c); }
diff --git a/gcc/testsuite/gdc.test/compilable/test21543.d b/gcc/testsuite/gdc.test/compilable/test21543.d
new file mode 100644
index 00000000000..4914264aaf9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21543.d
@@ -0,0 +1,116 @@
+// https://issues.dlang.org/show_bug.cgi?id=21543
+
+class B
+{
+ Nullable!B data;
+ alias data this;
+}
+
+void test1()
+{
+ B b;
+ Nullable!B n;
+}
+
+struct Nullable(T)
+{
+ T payload;
+
+ void opAssign()(T)
+ {
+ move(payload);
+ }
+
+ inout(T) get_() inout
+ {
+ return payload;
+ }
+
+ alias get_ this;
+}
+
+// another version with chain of 3 alias this
+
+struct C
+{
+ Nullable2 data;
+ alias data this;
+}
+
+void test2()
+{
+ C c;
+ Nullable2 n2 = &c;
+ Nullable3 n3 = &c;
+
+ // these are to check a sane -vcg-ast output
+ fn1(c);
+ fn1(n2);
+ fn1(n3);
+ fn2(c);
+ fn2(n2);
+ fn2(n3);
+ fn3(c);
+ fn3(n2);
+ fn3(n3);
+}
+
+void fn1(C x) {}
+
+void fn2(Nullable2 x) {}
+
+void fn3(Nullable3 x) {}
+
+struct Nullable2
+{
+ Nullable3 payload;
+
+ this(C* c)
+ {
+ payload = Nullable3(c);
+ }
+
+ void opAssign()(Nullable3)
+ {
+ move(payload);
+ }
+
+ inout(Nullable3) get_() inout
+ {
+ return payload;
+ }
+
+ alias get_ this;
+}
+
+struct Nullable3
+{
+ C* payload;
+
+ this(C* c)
+ {
+ payload = c;
+ }
+
+ void opAssign()(C)
+ {
+ move(payload);
+ }
+
+ inout(C) get_() inout
+ {
+ return *payload;
+ }
+
+ alias get_ this;
+}
+
+T move(T)(ref T source)
+{
+ return source;
+}
+
+T move(T)(T source)
+{
+ return source;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21591.d b/gcc/testsuite/gdc.test/compilable/test21591.d
new file mode 100644
index 00000000000..54ffd15eb56
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21591.d
@@ -0,0 +1,46 @@
+// https://issues.dlang.org/show_bug.cgi?id=21591
+
+alias F = void function();
+
+void fn1(void function(), void function());
+void fr1(F, F);
+static assert(fn1.mangleof == "_D9test215913fn1FPFZvQeZv");
+static assert(fr1.mangleof == "_D9test215913fr1FPFZvQeZv");
+
+void fn2(void function()*, void function()*);
+void fr2(F*, F*);
+static assert(fn2.mangleof == "_D9test215913fn2FPPFZvQfZv");
+static assert(fr2.mangleof == "_D9test215913fr2FPPFZvQfZv");
+
+void function() fn3(void function()**, void function()*);
+F fr3(F**, F*);
+static assert(fn3.mangleof == "_D9test215913fn3FPPPFZvQfZQh");
+static assert(fr3.mangleof == "_D9test215913fr3FPPPFZvQfZQh");
+
+void function()** fn4(ref void function(), ref void function()*);
+F** fr4(ref F, ref F*);
+static assert(fn4.mangleof == "_D9test215913fn4FKPFZvKPQgZPQf");
+static assert(fr4.mangleof == "_D9test215913fr4FKPFZvKPQgZPQf");
+
+
+alias D = void delegate();
+
+void dg1(void delegate(), void delegate());
+void dr1(D, D);
+static assert(dg1.mangleof == "_D9test215913dg1FDFZvQeZv");
+static assert(dr1.mangleof == "_D9test215913dr1FDFZvQeZv");
+
+void dg2(void delegate()*, void delegate()*);
+void dr2(D*, D*);
+static assert(dg2.mangleof == "_D9test215913dg2FPDFZvQfZv");
+static assert(dr2.mangleof == "_D9test215913dr2FPDFZvQfZv");
+
+void delegate() dg3(void delegate()**, void delegate()*);
+D dr3(D**, D*);
+static assert(dg3.mangleof == "_D9test215913dg3FPPDFZvQfZQh");
+static assert(dr3.mangleof == "_D9test215913dr3FPPDFZvQfZQh");
+
+void delegate()** dg4(ref void delegate(), ref void delegate()*);
+D** dr4(ref D, ref D*);
+static assert(dg4.mangleof == "_D9test215913dg4FKDFZvKPQgZPQf");
+static assert(dr4.mangleof == "_D9test215913dr4FKDFZvKPQgZPQf");
diff --git a/gcc/testsuite/gdc.test/compilable/test21659.d b/gcc/testsuite/gdc.test/compilable/test21659.d
new file mode 100644
index 00000000000..28dfbcaa36a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21659.d
@@ -0,0 +1,16 @@
+// https://issues.dlang.org/show_bug.cgi?id=21659
+
+// Compiler-recognized ident
+enum __c_ulonglong : ulong;
+
+private union EndianSwapper(T)
+{
+ T value;
+ ubyte[T.sizeof] array;
+ static assert(T.sizeof == ulong.sizeof);
+}
+
+void main ()
+{
+ EndianSwapper!(__c_ulonglong) val;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21661.d b/gcc/testsuite/gdc.test/compilable/test21661.d
new file mode 100644
index 00000000000..f982d44ad05
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21661.d
@@ -0,0 +1,24 @@
+// https://issues.dlang.org/show_bug.cgi?id=21661
+
+module pkg.pkg2.mod;
+
+immutable x = 1;
+enum e = pkg.pkg2.mod.x;
+
+// Some checks
+static assert( pkg.stringof == "package pkg" );
+static assert( pkg.pkg2.stringof == "package pkg2");
+static assert( pkg.pkg2.mod.stringof == "module mod" );
+static assert(pkg.pkg2.mod.x.stringof == "x" );
+
+alias p1 = pkg;
+alias p2 = pkg.pkg2;
+alias m = pkg.pkg2.mod;
+alias v = pkg.pkg2.mod.x;
+
+static assert( p1.stringof == "package pkg" );
+static assert( p2.stringof == "package pkg2");
+static assert( m.stringof == "module mod" );
+static assert(p1.pkg2.mod.stringof == "module mod" );
+static assert( p2.mod.stringof == "module mod" );
+static assert( v.stringof == "x" );
diff --git a/gcc/testsuite/gdc.test/compilable/test21668.d b/gcc/testsuite/gdc.test/compilable/test21668.d
new file mode 100644
index 00000000000..a7c301aae85
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21668.d
@@ -0,0 +1,7 @@
+// https://issues.dlang.org/show_bug.cgi?id=21668
+
+struct Opaque;
+
+void byPtr(Opaque*) {}
+void byRef(ref Opaque) {} // Fails
+void bySlice(Opaque[]) {}
diff --git a/gcc/testsuite/gdc.test/compilable/test21680.d b/gcc/testsuite/gdc.test/compilable/test21680.d
new file mode 100644
index 00000000000..caa4df4233f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21680.d
@@ -0,0 +1,9 @@
+// https://issues.dlang.org/show_bug.cgi?id=21680
+
+struct Unique
+{
+ alias ValueType = typeof({ return field; }()); /* Error: need `this` for
+`field` of type `int` */
+ int field;
+ static assert(is(ValueType == int));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21743.d b/gcc/testsuite/gdc.test/compilable/test21743.d
new file mode 100644
index 00000000000..dc3b7090336
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21743.d
@@ -0,0 +1,16 @@
+// https://issues.dlang.org/show_bug.cgi?id=21743
+
+struct A
+{
+ int foo(int a) { return a; }
+ string foo()(string b) { return b; }
+}
+
+alias ov = __traits(getOverloads, A.init, "foo", true);
+
+// member function works
+static assert(ov[0](1) == 1);
+
+// member template used to fail with the gagged error:
+// 'need this for foo of type pure nothrow @nogc @safe string(string b)'
+static assert(ov[1]("a") == "a");
diff --git a/gcc/testsuite/gdc.test/compilable/test21753.d b/gcc/testsuite/gdc.test/compilable/test21753.d
new file mode 100644
index 00000000000..91ccc2047dd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21753.d
@@ -0,0 +1,21 @@
+// https://issues.dlang.org/show_bug.cgi?id=21753
+
+struct Sample {
+ int function() func1;
+ int function() func2;
+}
+
+void noth(Sample smpl)() {
+ static assert(smpl.func1() == 0);
+ static assert(smpl.func2() == 1);
+}
+
+void main() {
+ enum s = Sample(
+ { return 0; },
+ { return 1; }
+ );
+ static assert(s.func1() == 0);
+ static assert(s.func2() == 1);
+ noth!(s)();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21802.d b/gcc/testsuite/gdc.test/compilable/test21802.d
new file mode 100644
index 00000000000..dc65a14217a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21802.d
@@ -0,0 +1,38 @@
+// https://issues.dlang.org/show_bug.cgi?id=21802
+
+struct A
+{
+ auto opAssign(lazy void foo)
+ {
+ foo();
+ }
+ auto opOpAssign(string op)(lazy void foo)
+ {
+ foo();
+ }
+}
+
+class C
+{
+ auto opAssign(lazy void foo)
+ {
+ foo();
+ }
+ auto opOpAssign(string op)(lazy void foo)
+ {
+ foo();
+ }
+}
+
+void bar(int x) { }
+
+void main ()
+{
+ A a;
+ a ~= bar (1); // OK
+ a = bar (1); // Error: expression bar(1) is void and has no value
+
+ C c = new C;
+ c ~= bar(1);
+ c = bar(1);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21806.d b/gcc/testsuite/gdc.test/compilable/test21806.d
new file mode 100644
index 00000000000..4b7a1592048
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21806.d
@@ -0,0 +1,24 @@
+// https://issues.dlang.org/show_bug.cgi?id=21806
+
+void main()
+{
+ ubyte[16] arr;
+ static assert(is(typeof( fun(arr[])) == char));
+ static assert(is(typeof(funtp(arr[])) == char));
+ static assert(is(typeof( bar(arr[])) == char));
+}
+
+// functions
+char fun(ubyte[] arr) { return 'X'; }
+
+int fun(ubyte[16] arr) { return 123; }
+
+// function templates
+char funtp()(ubyte[] arr) { return 'X'; }
+
+int funtp(size_t N)(ubyte[N] arr) { return 123; }
+
+// original case with 'in'
+char bar()(in ubyte[] arr) { return 'X'; }
+
+int bar(size_t N)(in ubyte[N] arr) { return 123; }
diff --git a/gcc/testsuite/gdc.test/compilable/test21828.d b/gcc/testsuite/gdc.test/compilable/test21828.d
new file mode 100644
index 00000000000..06a67caefb1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21828.d
@@ -0,0 +1,27 @@
+// https://issues.dlang.org/show_bug.cgi?id=21828
+
+struct S
+{
+ enum E
+ {
+ e1 = 0,
+ }
+ E e;
+ enum S s1 = S(E.e1);
+}
+
+SE se;
+
+enum SE
+{
+ e1 = S.s1
+}
+
+// reduced case, forward references just assume int value
+
+E e;
+
+enum E
+{
+ a = "x"
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21830.d b/gcc/testsuite/gdc.test/compilable/test21830.d
new file mode 100644
index 00000000000..d1ead010d88
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21830.d
@@ -0,0 +1,25 @@
+// REQUIRED_ARGS: -de -unittest
+
+deprecated struct OldS21830 { }
+
+struct NewS21830 { }
+
+static if (1)
+{
+ auto test21830(T)(T t)
+ if (is(T == NewS21830))
+ {
+ return T.init;
+ }
+}
+
+deprecated auto test21830(T)(T t)
+if (is(T == OldS21830))
+{
+ return T.init;
+}
+
+unittest
+{
+ auto b = test21830(NewS21830()); // error here about using test21830!OldS21830
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21831.d b/gcc/testsuite/gdc.test/compilable/test21831.d
new file mode 100644
index 00000000000..aaae1356cb8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21831.d
@@ -0,0 +1,20 @@
+// REQUIRED_ARGS: -de -unittest
+
+deprecated struct S21831 { }
+
+auto test21831(T)(T t) // error: struct `S21831` is deprecated
+if (!__traits(isDeprecated, T))
+{
+ return T.init;
+}
+
+deprecated auto test21831(T)(T t)
+if (__traits(isDeprecated, T))
+{
+ return T.init;
+}
+
+deprecated unittest
+{
+ auto b = test21831(S21831()); // instantiated from here
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21832.d b/gcc/testsuite/gdc.test/compilable/test21832.d
new file mode 100644
index 00000000000..e034f2ce07d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21832.d
@@ -0,0 +1,13 @@
+// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/imp21832.d
+int test21832a()
+{
+ import imports.imp21832 : fun; // function 'fun' is deprecated
+ return fun(0);
+}
+
+int test21832b()
+{
+ import imports.imp21832 : tpl; // template 'tpl' is deprecated
+ return tpl(0);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21861.d b/gcc/testsuite/gdc.test/compilable/test21861.d
new file mode 100644
index 00000000000..875636b3124
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21861.d
@@ -0,0 +1,38 @@
+// https://issues.dlang.org/show_bug.cgi?id=21861
+
+int f() {
+ @("S")
+ struct S {}
+
+ @("U")
+ union U {}
+
+ @("C")
+ class C {}
+
+ // OK <- CTFE fails on this:
+ @("E")
+ enum E { X }
+
+ // OK <- CTFE fails on this:
+ @("f")
+ int f(int x) { return x + 2; }
+
+ // OK <- CTFE fails on this:
+ @(&f) int a;
+ @(1) @(2) int b = 4, c;
+ @(3) extern(C) int d = 3;
+
+ enum uda1 = __traits(getAttributes, a);
+ enum uda2 = __traits(getAttributes, b);
+ enum uda3 = __traits(getAttributes, c);
+
+ // These are to trigger a compiler assert if parser is updated in the future
+ static assert(!__traits(compiles, mixin("{ @(1) { int x; int y; } }")));
+ static assert(!__traits(compiles, mixin("{ @(1): int x; int y; }")));
+
+ // 3+2 1 2 4 1 3
+ return uda1[0](3) + uda2[0] + uda2[1] + b + uda3[0] + d;
+}
+
+static assert(f() == 16);
diff --git a/gcc/testsuite/gdc.test/compilable/test21876.d b/gcc/testsuite/gdc.test/compilable/test21876.d
new file mode 100644
index 00000000000..a0cef99ab40
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test21876.d
@@ -0,0 +1,17 @@
+// https://issues.dlang.org/show_bug.cgi?id=21876
+
+auto test1()
+{
+ int[0] a;
+ return a;
+}
+
+auto test2()
+{
+ static int[0] a;
+ return a;
+}
+
+enum x = test1();
+enum y = test2();
+static assert(x == y && y == []);
diff --git a/gcc/testsuite/gdc.test/compilable/test22122.d b/gcc/testsuite/gdc.test/compilable/test22122.d
new file mode 100644
index 00000000000..a06d06c787d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test22122.d
@@ -0,0 +1,53 @@
+// EXTRA_FILES: imports/imp22122.d
+module imports.test22122;
+
+struct S22122
+{
+ import imports.imp22122;
+ Variant!(Imp22122)[] array;
+}
+
+void test22122_catch(S22122 s)
+{
+ try
+ {
+ foreach(elem; s.array)
+ {
+ import imports.imp22122;
+ with(elem.get!Imp22122)
+ {
+ }
+ }
+ }
+ catch (Exception)
+ {
+ }
+}
+
+void test22122_finally(S22122 s)
+{
+ try
+ {
+ foreach(elem; s.array)
+ {
+ import imports.imp22122;
+ with(elem.get!Imp22122)
+ {
+ }
+ }
+ }
+ finally
+ {
+ }
+}
+
+private struct Variant(T)
+{
+ union Impl
+ {
+ }
+ auto get(E)()
+ {
+ return Impl();
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test22226.d b/gcc/testsuite/gdc.test/compilable/test22226.d
new file mode 100644
index 00000000000..77b37971dfe
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test22226.d
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=22226
+
+struct A {}
+
+A move(A a) { return A.init; }
+
+struct SumType
+{
+ A a;
+
+ this(A value)
+ {
+ a = __ctfe ? value : move(value);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test25.d b/gcc/testsuite/gdc.test/compilable/test25.d
index 42fc8141b47..449159cb86d 100644
--- a/gcc/testsuite/gdc.test/compilable/test25.d
+++ b/gcc/testsuite/gdc.test/compilable/test25.d
@@ -1,10 +1,10 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/test25a.d imports/test25b.d
import imports.test25a, imports.test25b;
-import std.stdio;
+import core.stdc.stdio;
void main()
{
- std.stdio.writefln("hello");
+ printf("hello\n");
}
diff --git a/gcc/testsuite/gdc.test/compilable/test2991.d b/gcc/testsuite/gdc.test/compilable/test2991.d
index 61281b83444..12cdb2f8a8a 100644
--- a/gcc/testsuite/gdc.test/compilable/test2991.d
+++ b/gcc/testsuite/gdc.test/compilable/test2991.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test2991.d
module test2991;
void foo()
diff --git a/gcc/testsuite/gdc.test/compilable/test3004.d b/gcc/testsuite/gdc.test/compilable/test3004.d
new file mode 100644
index 00000000000..baa0cd76a35
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test3004.d
@@ -0,0 +1,19 @@
+// https://issues.dlang.org/show_bug.cgi?id=3004
+/*
+REQUIRED_ARGS: -ignore -v
+TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|import|semantic|entry|function object|\s*$)")
+TEST_OUTPUT:
+---
+pragma GNU_attribute (__error)
+pragma GNU_attribute (__error)
+code test3004
+function test3004.test
+---
+*/
+
+extern(C) int printf(char*, ...);
+
+pragma(GNU_attribute, flatten)
+void test() { printf("Hello GNU world!\n".dup.ptr); }
+
+pragma(GNU_attribute, flatten);
diff --git a/gcc/testsuite/gdc.test/compilable/test313a.d b/gcc/testsuite/gdc.test/compilable/test313a.d
index daa6afb3a0e..5ec02111614 100644
--- a/gcc/testsuite/gdc.test/compilable/test313a.d
+++ b/gcc/testsuite/gdc.test/compilable/test313a.d
@@ -1,5 +1,6 @@
/*
REQUIRED_ARGS: -de
+EXTRA_FILES: imports/a313.d imports/a313templatemixin1.d imports/a313templatemixin2.d imports/b313.d imports/pkg313/c313.d
*/
module test313;
diff --git a/gcc/testsuite/gdc.test/compilable/test313c.d b/gcc/testsuite/gdc.test/compilable/test313c.d
index 3b075bd6ebf..0110f62eaea 100644
--- a/gcc/testsuite/gdc.test/compilable/test313c.d
+++ b/gcc/testsuite/gdc.test/compilable/test313c.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/pkgmod313/mod.d imports/pkgmod313/package.d
import imports.pkgmod313;
void test()
diff --git a/gcc/testsuite/gdc.test/compilable/test313d.d b/gcc/testsuite/gdc.test/compilable/test313d.d
index 7545d8012d9..903cedf26d9 100644
--- a/gcc/testsuite/gdc.test/compilable/test313d.d
+++ b/gcc/testsuite/gdc.test/compilable/test313d.d
@@ -1,5 +1,6 @@
// first imported as package
-// EXTRA_SOURCES: imports/pkgmod313/mod.d
+// COMPILED_IMPORTS: imports/pkgmod313/mod.d
+// EXTRA_FILES: imports/pkgmod313/package.d
// REQUIRED_ARGS: -de
import imports.pkgmod313; // then as package module
diff --git a/gcc/testsuite/gdc.test/compilable/test313e.d b/gcc/testsuite/gdc.test/compilable/test313e.d
index b24f24ef6af..9ed89920e62 100644
--- a/gcc/testsuite/gdc.test/compilable/test313e.d
+++ b/gcc/testsuite/gdc.test/compilable/test313e.d
@@ -1,5 +1,5 @@
// first resolved as package, then created as module (with name package)
-// EXTRA_SOURCES: imports/pkgmod313/mod.d imports/pkgmod313/package.d
+// COMPILED_IMPORTS: imports/pkgmod313/mod.d imports/pkgmod313/package.d
// REQUIRED_ARGS: -de
import imports.pkgmod313; // then imported as package module
diff --git a/gcc/testsuite/gdc.test/compilable/test313f.d b/gcc/testsuite/gdc.test/compilable/test313f.d
index e758e37c9db..c8ec63df70c 100644
--- a/gcc/testsuite/gdc.test/compilable/test313f.d
+++ b/gcc/testsuite/gdc.test/compilable/test313f.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/f313.d
import imports.f313;
void test()
diff --git a/gcc/testsuite/gdc.test/compilable/test313g.d b/gcc/testsuite/gdc.test/compilable/test313g.d
index f2052147d1a..91e320d8953 100644
--- a/gcc/testsuite/gdc.test/compilable/test313g.d
+++ b/gcc/testsuite/gdc.test/compilable/test313g.d
@@ -1,5 +1,6 @@
// REQUIRED_ARGS: -de
-// EXTRA_SOURCES: imports/g313.d
+// COMPILED_IMPORTS: imports/g313.d
+// EXTRA_FILES: imports/g313public.d imports/g313staticif.d imports/g313stringmixin.d imports/g313templatemixin.d
import imports.g313;
void test15900()
diff --git a/gcc/testsuite/gdc.test/compilable/test314.d b/gcc/testsuite/gdc.test/compilable/test314.d
index b7c15e39ca5..0435228a62e 100644
--- a/gcc/testsuite/gdc.test/compilable/test314.d
+++ b/gcc/testsuite/gdc.test/compilable/test314.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/a314.d imports/c314.d
module imports.test314; // package imports
import imports.a314;
diff --git a/gcc/testsuite/gdc.test/compilable/test3775.d b/gcc/testsuite/gdc.test/compilable/test3775.d
index 47b9b88660c..a34b3293863 100644
--- a/gcc/testsuite/gdc.test/compilable/test3775.d
+++ b/gcc/testsuite/gdc.test/compilable/test3775.d
@@ -1,4 +1,4 @@
-// 3775
+// https://issues.dlang.org/show_bug.cgi?id=3775
struct Bug3775 {
static int byLine()() { return 1; }
diff --git a/gcc/testsuite/gdc.test/compilable/test4003.d b/gcc/testsuite/gdc.test/compilable/test4003.d
index 8a401f219bd..f51310edf34 100644
--- a/gcc/testsuite/gdc.test/compilable/test4003.d
+++ b/gcc/testsuite/gdc.test/compilable/test4003.d
@@ -1,4 +1,5 @@
-// EXTRA_SOURCES: imports/test4003a.d
+// COMPILED_IMPORTS: imports/test4003a.d
+// EXTRA_FILES: imports/stdio4003.d imports/typecons4003.d
// PERMUTE_ARGS:
import imports.stdio4003;
diff --git a/gcc/testsuite/gdc.test/compilable/test4375.d b/gcc/testsuite/gdc.test/compilable/test4375.d
index 234895e24ce..f5c4e4a9b40 100644
--- a/gcc/testsuite/gdc.test/compilable/test4375.d
+++ b/gcc/testsuite/gdc.test/compilable/test4375.d
@@ -1,5 +1,6 @@
// REQUIRED_ARGS: -unittest
-// 4375: disallow dangling else
+// https://issues.dlang.org/show_bug.cgi?id=4375
+// disallow dangling else
void main() {
@@ -368,7 +369,7 @@ class C {
else
assert(63);
}
- body {
+ do {
if (true)
assert(64);
else
diff --git a/gcc/testsuite/gdc.test/compilable/test50.d b/gcc/testsuite/gdc.test/compilable/test50.d
index f05c3e4bc06..a5dfac0e79b 100644
--- a/gcc/testsuite/gdc.test/compilable/test50.d
+++ b/gcc/testsuite/gdc.test/compilable/test50.d
@@ -1,4 +1,4 @@
-// EXTRA_SOURCES: imports/test50a.d
+// COMPILED_IMPORTS: imports/test50a.d
// PERMUTE_ARGS:
import imports.test50a;
diff --git a/gcc/testsuite/gdc.test/compilable/test5227.d b/gcc/testsuite/gdc.test/compilable/test5227.d
index 63ca752f4fb..b7131916778 100644
--- a/gcc/testsuite/gdc.test/compilable/test5227.d
+++ b/gcc/testsuite/gdc.test/compilable/test5227.d
@@ -11,17 +11,19 @@ log2()
log10()
0.740363L
round()
-6.00000L
+6.0L
floor()
-5.00000F
-5.00000
-5.00000L
+5.0F
+5.0
+5.0L
ceil()
-6.00000F
-6.00000
-6.00000L
+6.0F
+6.0
+6.0L
trunc()
-5.00000L
+5.0L
+exp()
+244.692L
expm1()
243.692L
exp2()
@@ -83,6 +85,11 @@ enum truncf = trunc(5.5f); //pragma(msg, truncf);
enum truncd = trunc(5.5 ); //pragma(msg, truncd);
enum truncr = trunc(5.5L); pragma(msg, truncr);
+pragma(msg, "exp()");
+enum expf = exp(5.5f); //pragma(msg, expf);
+enum expd = exp(5.5 ); //pragma(msg, expd);
+enum expr = exp(5.5L); pragma(msg, expr);
+
pragma(msg, "expm1()");
enum expm1f = expm1(5.5f); //pragma(msg, expm1f);
enum expm1d = expm1(5.5 ); //pragma(msg, expm1d);
diff --git a/gcc/testsuite/gdc.test/compilable/test55.d b/gcc/testsuite/gdc.test/compilable/test55.d
index 0dd7b7b4136..6dfc665191e 100644
--- a/gcc/testsuite/gdc.test/compilable/test55.d
+++ b/gcc/testsuite/gdc.test/compilable/test55.d
@@ -1,5 +1,5 @@
// COMPILE_SEPARATELY
-// EXTRA_SOURCES: imports/test55a.d
+// COMPILED_IMPORTS: imports/test55a.d
// PERMUTE_ARGS: -dw
// REQUIRED_ARGS: -d
diff --git a/gcc/testsuite/gdc.test/compilable/test59.d b/gcc/testsuite/gdc.test/compilable/test59.d
index c9f4edfba18..de10c910e5b 100644
--- a/gcc/testsuite/gdc.test/compilable/test59.d
+++ b/gcc/testsuite/gdc.test/compilable/test59.d
@@ -1,4 +1,4 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/test59a.d imports/test59b.d
public import imports.test59a;
public import imports.test59b;
diff --git a/gcc/testsuite/gdc.test/compilable/test5973.d b/gcc/testsuite/gdc.test/compilable/test5973.d
new file mode 100644
index 00000000000..a54b0ae173a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test5973.d
@@ -0,0 +1,41 @@
+// https://issues.dlang.org/show_bug.cgi?id=5973
+
+class A { int a = 1; }
+class B { int b = 2; }
+class C : A
+{
+ B obj;
+ alias obj this;
+ this(){ obj = new B(); }
+}
+class X : C {}
+
+class D
+{
+ int i;
+}
+
+class E
+{
+ D x;
+ alias x this;
+}
+
+class F : E
+{
+ void test()
+ {
+ i = 5;
+ }
+}
+
+void main()
+{
+ auto c = new C();
+ assert(c.a == 1); // lookup C -> A, OK
+ assert(c.b == 2); // lookup C => B, OK
+
+ auto x = new X();
+ assert(x.a == 1); // lookup X -> C -> A, OK
+ assert(x.b == 2); // lookup X -> C => B, NG (Line 17)
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test6013.d b/gcc/testsuite/gdc.test/compilable/test6013.d
index 5824c9155b9..2856eebaab2 100644
--- a/gcc/testsuite/gdc.test/compilable/test6013.d
+++ b/gcc/testsuite/gdc.test/compilable/test6013.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/test6013.d
import imports.test6013;
static assert(__traits(compiles, public_alias_value));
diff --git a/gcc/testsuite/gdc.test/compilable/test602.d b/gcc/testsuite/gdc.test/compilable/test602.d
index 979af91e5ef..a17aa160d9f 100644
--- a/gcc/testsuite/gdc.test/compilable/test602.d
+++ b/gcc/testsuite/gdc.test/compilable/test602.d
@@ -42,7 +42,7 @@ static assert(!__traits(compiles, (bool b)
label: {}
assert(!x);
}
- catch
+ catch(Throwable)
{
}
}));
@@ -54,7 +54,7 @@ static assert(!__traits(compiles, (bool b)
try
{
}
- catch
+ catch(Throwable)
{
int x;
label: {}
@@ -383,7 +383,7 @@ static assert(!__traits(compiles, (bool b)
}));
/***************************************************/
-// 11659
+// https://issues.dlang.org/show_bug.cgi?id=11659
int test11659()
{
@@ -394,7 +394,7 @@ int test11659()
}
/***************************************************/
-// 13321
+// https://issues.dlang.org/show_bug.cgi?id=13321
void test13321(bool b)
{
diff --git a/gcc/testsuite/gdc.test/compilable/test61.d b/gcc/testsuite/gdc.test/compilable/test61.d
index e4d3d659367..e2700d6586d 100644
--- a/gcc/testsuite/gdc.test/compilable/test61.d
+++ b/gcc/testsuite/gdc.test/compilable/test61.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/test61a.d
import imports.test61a;
alias imports.test61a.bar bar;
diff --git a/gcc/testsuite/gdc.test/compilable/test62.d b/gcc/testsuite/gdc.test/compilable/test62.d
index c723a96b788..fa42145653a 100644
--- a/gcc/testsuite/gdc.test/compilable/test62.d
+++ b/gcc/testsuite/gdc.test/compilable/test62.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/test62a.d
import imports.test62a;
struct S { }
diff --git a/gcc/testsuite/gdc.test/compilable/test63.d b/gcc/testsuite/gdc.test/compilable/test63.d
index 70d49646f77..9cf986d48e0 100644
--- a/gcc/testsuite/gdc.test/compilable/test63.d
+++ b/gcc/testsuite/gdc.test/compilable/test63.d
@@ -1,4 +1,4 @@
-// EXTRA_SOURCES: imports/test63a.d
+// COMPILED_IMPORTS: imports/test63a.d
// PERMUTE_ARGS:
private import imports.test63a;
diff --git a/gcc/testsuite/gdc.test/compilable/test6395.d b/gcc/testsuite/gdc.test/compilable/test6395.d
index a1bac8e48f1..3b80203b880 100644
--- a/gcc/testsuite/gdc.test/compilable/test6395.d
+++ b/gcc/testsuite/gdc.test/compilable/test6395.d
@@ -2,7 +2,7 @@
// EXTRA_SOURCES: b6395.d
// EXTRA_FILES: extra-files/c6395.d
-// 6395
+// https://issues.dlang.org/show_bug.cgi?id=6395
import c6395;
diff --git a/gcc/testsuite/gdc.test/compilable/test6541.d b/gcc/testsuite/gdc.test/compilable/test6541.d
new file mode 100644
index 00000000000..b4922159f61
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test6541.d
@@ -0,0 +1,10 @@
+class C
+{
+ static synchronized func(alias a)() {}
+}
+
+void main()
+{
+ int a;
+ C.func!a();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test66.d b/gcc/testsuite/gdc.test/compilable/test66.d
index 1213884f72f..9db082e0522 100644
--- a/gcc/testsuite/gdc.test/compilable/test66.d
+++ b/gcc/testsuite/gdc.test/compilable/test66.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/test66a.d
import imports.test66a;
alias int TOK;
@@ -11,7 +11,7 @@ enum
struct Token
{
- static char[] tochars[TOKmax];
+ static char[][TOKmax] tochars;
}
class Lexer
diff --git a/gcc/testsuite/gdc.test/compilable/test67.d b/gcc/testsuite/gdc.test/compilable/test67.d
index bcd48b20ddf..9f45c64b790 100644
--- a/gcc/testsuite/gdc.test/compilable/test67.d
+++ b/gcc/testsuite/gdc.test/compilable/test67.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/test67a.d
import imports.test67a;
interface I
diff --git a/gcc/testsuite/gdc.test/compilable/test6777.d b/gcc/testsuite/gdc.test/compilable/test6777.d
new file mode 100644
index 00000000000..161a94a179d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test6777.d
@@ -0,0 +1,11 @@
+struct S {}
+
+class C {
+ S s;
+ alias s this;
+}
+
+void main() {
+ auto c = new C;
+ auto p = cast(void*) c;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test68.d b/gcc/testsuite/gdc.test/compilable/test68.d
index bfac489342d..55a799589cd 100644
--- a/gcc/testsuite/gdc.test/compilable/test68.d
+++ b/gcc/testsuite/gdc.test/compilable/test68.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
-
-// Bugzilla 4278
+// EXTRA_FILES: imports/test68a.d
+// https://issues.dlang.org/show_bug.cgi?id=4278
import imports.test68a;
diff --git a/gcc/testsuite/gdc.test/compilable/test69.d b/gcc/testsuite/gdc.test/compilable/test69.d
index fb8e1ba30c8..66662dcd5f2 100644
--- a/gcc/testsuite/gdc.test/compilable/test69.d
+++ b/gcc/testsuite/gdc.test/compilable/test69.d
@@ -16,7 +16,7 @@ void fromFail49()
}
}
-// Bugzilla 5735
+// https://issues.dlang.org/show_bug.cgi?id=5735
struct A {}
void b() {}
diff --git a/gcc/testsuite/gdc.test/compilable/test6999.d b/gcc/testsuite/gdc.test/compilable/test6999.d
index c4e916f75cf..c9f414feaa2 100644
--- a/gcc/testsuite/gdc.test/compilable/test6999.d
+++ b/gcc/testsuite/gdc.test/compilable/test6999.d
@@ -1,4 +1,4 @@
-// 6999: inout in front of return type
+// https://issues.dlang.org/show_bug.cgi?id=6999: inout in front of return type
struct A
{
diff --git a/gcc/testsuite/gdc.test/compilable/test70.d b/gcc/testsuite/gdc.test/compilable/test70.d
index 9a821b23202..f4aa900c76a 100644
--- a/gcc/testsuite/gdc.test/compilable/test70.d
+++ b/gcc/testsuite/gdc.test/compilable/test70.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test70.d
import imports.test70 : foo;
void foo(int) // overloads with selective import
diff --git a/gcc/testsuite/gdc.test/compilable/test71.d b/gcc/testsuite/gdc.test/compilable/test71.d
index 83b24a01b32..bc26f76fe03 100644
--- a/gcc/testsuite/gdc.test/compilable/test71.d
+++ b/gcc/testsuite/gdc.test/compilable/test71.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test71.d
import imports.test71;
void bar()
diff --git a/gcc/testsuite/gdc.test/compilable/test7172.d b/gcc/testsuite/gdc.test/compilable/test7172.d
index a4cf663066e..859f29a13e5 100644
--- a/gcc/testsuite/gdc.test/compilable/test7172.d
+++ b/gcc/testsuite/gdc.test/compilable/test7172.d
@@ -7,7 +7,7 @@ void main()
static assert(!__traits(compiles, { class D : FinalC{} }));
scope class ScopeC{}
- static assert(!__traits(compiles, { auto sc = new ScopeC(); }));
+// static assert(!__traits(compiles, { auto sc = new ScopeC(); }));
static assert( __traits(compiles, { scope sc = new ScopeC(); }));
synchronized class SyncC{ void f(){} }
diff --git a/gcc/testsuite/gdc.test/compilable/test72.d b/gcc/testsuite/gdc.test/compilable/test72.d
index 5de9d42a72d..6e6890fb8d3 100644
--- a/gcc/testsuite/gdc.test/compilable/test72.d
+++ b/gcc/testsuite/gdc.test/compilable/test72.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test72a.d imports/test72b.d imports/test72c.d
module test72;
import imports.test72a, imports.test72c;
diff --git a/gcc/testsuite/gdc.test/compilable/test7399.d b/gcc/testsuite/gdc.test/compilable/test7399.d
index 76ac9202c68..dba0741ac05 100644
--- a/gcc/testsuite/gdc.test/compilable/test7399.d
+++ b/gcc/testsuite/gdc.test/compilable/test7399.d
@@ -1,6 +1,6 @@
-// 7399
+// https://issues.dlang.org/show_bug.cgi?id=7399
static assert(!__traits(compiles, { import non.existing.file; }));
-// 7400
+// https://issues.dlang.org/show_bug.cgi?id=7400
static assert(!is(typeof({import non_existing_file;})));
diff --git a/gcc/testsuite/gdc.test/compilable/test7491.d b/gcc/testsuite/gdc.test/compilable/test7491.d
index eb742e23598..ce0e5e455f9 100644
--- a/gcc/testsuite/gdc.test/compilable/test7491.d
+++ b/gcc/testsuite/gdc.test/compilable/test7491.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test7491a.d imports/test7491b.d
struct Struct
{
import object;
diff --git a/gcc/testsuite/gdc.test/compilable/test7524.d b/gcc/testsuite/gdc.test/compilable/test7524.d
index 6ff491a7159..a0bb918170e 100644
--- a/gcc/testsuite/gdc.test/compilable/test7524.d
+++ b/gcc/testsuite/gdc.test/compilable/test7524.d
@@ -1,3 +1,3 @@
-// 7524
+// https://issues.dlang.org/show_bug.cgi?id=7524
#line __LINE__ "y.d"
diff --git a/gcc/testsuite/gdc.test/compilable/test7754.d b/gcc/testsuite/gdc.test/compilable/test7754.d
index 1ce9b448995..724dce40c93 100644
--- a/gcc/testsuite/gdc.test/compilable/test7754.d
+++ b/gcc/testsuite/gdc.test/compilable/test7754.d
@@ -1,6 +1,23 @@
-// REQUIRED_ARGS: -H -Hd${RESULTS_DIR}/compilable
-// POST_SCRIPT: compilable/extra-files/test7754-postscript.sh
-// PERMUTE_ARGS: -d -dw
+/*
+REQUIRED_ARGS: -H -Hd${RESULTS_DIR}/compilable
+PERMUTE_ARGS: -d -dw
+
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/test7754.di
+TEST_OUTPUT:
+---
+=== ${RESULTS_DIR}/compilable/test7754.di
+// D import file generated from 'compilable/test7754.d'
+struct Foo(T)
+{
+ shared static this()
+ {
+ }
+ static this()
+ {
+ }
+}
+---
+*/
struct Foo(T)
{
diff --git a/gcc/testsuite/gdc.test/compilable/test8509.d b/gcc/testsuite/gdc.test/compilable/test8509.d
index a9072231fe8..57902236d9a 100644
--- a/gcc/testsuite/gdc.test/compilable/test8509.d
+++ b/gcc/testsuite/gdc.test/compilable/test8509.d
@@ -1,6 +1,10 @@
module test8509;
enum E : string { a = "hello", b = "world" }
-struct S { E opCat(S s) { return E.a; } E opCat(string s) { return E.a; } }
+struct S
+{
+ E opBinary(string s : "~")(S s) { return E.a; }
+ E opBinary(string s : "~")(string s) { return E.a; }
+}
void main()
{
diff --git a/gcc/testsuite/gdc.test/compilable/test8543.d b/gcc/testsuite/gdc.test/compilable/test8543.d
index 4b29542647e..c72b0d927d9 100644
--- a/gcc/testsuite/gdc.test/compilable/test8543.d
+++ b/gcc/testsuite/gdc.test/compilable/test8543.d
@@ -1,5 +1,5 @@
-version (D_SIMD)
+static if (__traits(compiles, __vector(float[4])))
{
struct vfloat
{
diff --git a/gcc/testsuite/gdc.test/compilable/test8696.d b/gcc/testsuite/gdc.test/compilable/test8696.d
index 755d52996a3..dd58c8f1a4a 100644
--- a/gcc/testsuite/gdc.test/compilable/test8696.d
+++ b/gcc/testsuite/gdc.test/compilable/test8696.d
@@ -1,6 +1,7 @@
// REQUIRED_ARGS: -w
-// 8696: incorrect dangling else with version():
+// https://issues.dlang.org/show_bug.cgi?id=8696
+// incorrect dangling else with version():
version (all):
version (linux)
diff --git a/gcc/testsuite/gdc.test/compilable/test8922a.d b/gcc/testsuite/gdc.test/compilable/test8922a.d
index e2f3d5c2c67..8980c937ac0 100644
--- a/gcc/testsuite/gdc.test/compilable/test8922a.d
+++ b/gcc/testsuite/gdc.test/compilable/test8922a.d
@@ -1,4 +1,5 @@
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/bug8922.d
import imports.bug8922;
void test()
diff --git a/gcc/testsuite/gdc.test/compilable/test8922b.d b/gcc/testsuite/gdc.test/compilable/test8922b.d
index a91601b3219..007f9b6c52a 100644
--- a/gcc/testsuite/gdc.test/compilable/test8922b.d
+++ b/gcc/testsuite/gdc.test/compilable/test8922b.d
@@ -1,4 +1,5 @@
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/bug8922.d
void test()
{
import imports.bug8922;
diff --git a/gcc/testsuite/gdc.test/compilable/test8922c.d b/gcc/testsuite/gdc.test/compilable/test8922c.d
index da5ad3a6267..fa208a0fe93 100644
--- a/gcc/testsuite/gdc.test/compilable/test8922c.d
+++ b/gcc/testsuite/gdc.test/compilable/test8922c.d
@@ -1,4 +1,5 @@
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/bug8922.d
static import imports.bug8922;
void test()
diff --git a/gcc/testsuite/gdc.test/compilable/test8922d.d b/gcc/testsuite/gdc.test/compilable/test8922d.d
index 4a56dd7f190..4898cf623ad 100644
--- a/gcc/testsuite/gdc.test/compilable/test8922d.d
+++ b/gcc/testsuite/gdc.test/compilable/test8922d.d
@@ -1,4 +1,5 @@
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/bug8922.d
void test()
{
static import imports.bug8922;
diff --git a/gcc/testsuite/gdc.test/compilable/test8922e.d b/gcc/testsuite/gdc.test/compilable/test8922e.d
index 3c52d006b49..b6d2d9253d3 100644
--- a/gcc/testsuite/gdc.test/compilable/test8922e.d
+++ b/gcc/testsuite/gdc.test/compilable/test8922e.d
@@ -1,4 +1,5 @@
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/bug8922.d
import renamed = imports.bug8922;
void test()
diff --git a/gcc/testsuite/gdc.test/compilable/test8922f.d b/gcc/testsuite/gdc.test/compilable/test8922f.d
index 2b2eb0fb9c7..2008e891527 100644
--- a/gcc/testsuite/gdc.test/compilable/test8922f.d
+++ b/gcc/testsuite/gdc.test/compilable/test8922f.d
@@ -1,4 +1,5 @@
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/bug8922.d
void test()
{
import renamed = imports.bug8922;
diff --git a/gcc/testsuite/gdc.test/compilable/test9209.d b/gcc/testsuite/gdc.test/compilable/test9209.d
index 03c2b79904a..8eca799a653 100644
--- a/gcc/testsuite/gdc.test/compilable/test9209.d
+++ b/gcc/testsuite/gdc.test/compilable/test9209.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
-// 9209
+// https://issues.dlang.org/show_bug.cgi?id=9209
auto array(T)(T t){ return t; }
diff --git a/gcc/testsuite/gdc.test/compilable/test9274.d b/gcc/testsuite/gdc.test/compilable/test9274.d
new file mode 100644
index 00000000000..7ad5cdab1d3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test9274.d
@@ -0,0 +1,26 @@
+// https://issues.dlang.org/show_bug.cgi?id=9274
+struct S
+{
+ float[] arr;
+ alias arr this;
+}
+
+static assert(!is(S == float[])); // ok
+static assert(!is(S == T[], T)); // fails
+static assert(is(S : float[]));
+static assert(is(S : T[], T));
+
+//https://issues.dlang.org/show_bug.cgi?id=9274
+struct A(T)
+{}
+
+struct B
+{
+ A!int _a;
+ alias _a this;
+}
+
+static assert(!is(B == A!int)); // OK
+static assert(!is(B == A!X, X)); // assertion fails
+static assert(is(B : A!int));
+static assert(is(B : A!X, X));
diff --git a/gcc/testsuite/gdc.test/compilable/test9276.d b/gcc/testsuite/gdc.test/compilable/test9276.d
index f6cefa5b48c..14857a8e887 100644
--- a/gcc/testsuite/gdc.test/compilable/test9276.d
+++ b/gcc/testsuite/gdc.test/compilable/test9276.d
@@ -1,6 +1,6 @@
+// EXTRA_FILES: imports/test9276decl.d imports/test9276expr.d imports/test9276hash.d imports/test9276sem.d imports/test9276type.d imports/test9276util.d imports/test9276visitors.d
// EXTRA_SOURCES: imports/test9276parser.d
-
// This is a dummy module for compilable test
void main()
{}
diff --git a/gcc/testsuite/gdc.test/compilable/test9278a.d b/gcc/testsuite/gdc.test/compilable/test9278a.d
index 932411fc656..e0f483bd63d 100644
--- a/gcc/testsuite/gdc.test/compilable/test9278a.d
+++ b/gcc/testsuite/gdc.test/compilable/test9278a.d
@@ -1,4 +1,4 @@
-// PREMUTE_ARGS:
+// PERMUTE_ARGS:
// Works fine here
struct datum { float num = 0.0; }
diff --git a/gcc/testsuite/gdc.test/compilable/test9278b.d b/gcc/testsuite/gdc.test/compilable/test9278b.d
index 0b4fee2f491..cca9000721e 100644
--- a/gcc/testsuite/gdc.test/compilable/test9278b.d
+++ b/gcc/testsuite/gdc.test/compilable/test9278b.d
@@ -1,4 +1,4 @@
-// PREMUTE_ARGS:
+// PERMUTE_ARGS:
// Works fine here
//struct datum { float num = 0.0; }
diff --git a/gcc/testsuite/gdc.test/compilable/test930.d b/gcc/testsuite/gdc.test/compilable/test930.d
new file mode 100644
index 00000000000..fa444af3185
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test930.d
@@ -0,0 +1,22 @@
+// https://issues.dlang.org/show_bug.cgi?id=930
+template ATemplate(T)
+{
+ template ATemplate()
+ {
+ auto foo()
+ {
+ T x = 2; // this line causes an error
+ }
+ }
+}
+
+class TheClass(alias MixIt)
+{
+ mixin MixIt!();
+}
+
+void main()
+{
+ auto val = new TheClass!(ATemplate!(int));
+ val.foo();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test9434.d b/gcc/testsuite/gdc.test/compilable/test9434.d
index 26c711731bf..a03b3917f0f 100644
--- a/gcc/testsuite/gdc.test/compilable/test9434.d
+++ b/gcc/testsuite/gdc.test/compilable/test9434.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: test9435.d
import test9435;//semantic;
template Visitors()
diff --git a/gcc/testsuite/gdc.test/compilable/test9435.d b/gcc/testsuite/gdc.test/compilable/test9435.d
index 7046fdd8a15..5ef507961ad 100644
--- a/gcc/testsuite/gdc.test/compilable/test9435.d
+++ b/gcc/testsuite/gdc.test/compilable/test9435.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: test9434.d
import test9434;//expression;
enum TokenType { Dot }
diff --git a/gcc/testsuite/gdc.test/compilable/test9436.d b/gcc/testsuite/gdc.test/compilable/test9436.d
index 2baee7c216b..5b650bd2db0 100644
--- a/gcc/testsuite/gdc.test/compilable/test9436.d
+++ b/gcc/testsuite/gdc.test/compilable/test9436.d
@@ -1,4 +1,4 @@
// EXTRA_SOURCES: imports/test9436interp.d
-
+// EXTRA_FILES: imports/test9436aggr.d imports/test9436node.d imports/test9436type.d
// this is a dummy module for test 9436.
diff --git a/gcc/testsuite/gdc.test/compilable/test9613.d b/gcc/testsuite/gdc.test/compilable/test9613.d
index c49c293282f..6bf87936b37 100644
--- a/gcc/testsuite/gdc.test/compilable/test9613.d
+++ b/gcc/testsuite/gdc.test/compilable/test9613.d
@@ -1,4 +1,4 @@
-// PREMUTE_ARGS:
+// PERMUTE_ARGS:
struct S9613
{
int f(
diff --git a/gcc/testsuite/gdc.test/compilable/test9672.d b/gcc/testsuite/gdc.test/compilable/test9672.d
index bb10e3a33f4..8bcd92feb28 100644
--- a/gcc/testsuite/gdc.test/compilable/test9672.d
+++ b/gcc/testsuite/gdc.test/compilable/test9672.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/test9672a.d
module test9672; // node
import imports.test9672a; // interpret
diff --git a/gcc/testsuite/gdc.test/compilable/test9692.d b/gcc/testsuite/gdc.test/compilable/test9692.d
index 765b657c1de..57131b8080c 100644
--- a/gcc/testsuite/gdc.test/compilable/test9692.d
+++ b/gcc/testsuite/gdc.test/compilable/test9692.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: test9692a.d imports/test9692b.d
module test9692;
import test9692a;
diff --git a/gcc/testsuite/gdc.test/compilable/test9701.d b/gcc/testsuite/gdc.test/compilable/test9701.d
index 8f822add531..68055c4dd46 100644
--- a/gcc/testsuite/gdc.test/compilable/test9701.d
+++ b/gcc/testsuite/gdc.test/compilable/test9701.d
@@ -1,4 +1,11 @@
// https://issues.dlang.org/show_bug.cgi?id=9701
+/*
+TEST_OUTPUT:
+---
+compilable/test9701.d(68): Deprecation: enum member `test9701.Enum.value7` is deprecated
+compilable/test9701.d(68): Deprecation: enum member `test9701.Enum.value8` is deprecated - message
+---
+*/
template AliasSeq(TList...)
{
@@ -56,3 +63,6 @@ static assert(__traits(getAttributes, value3) == AliasSeq!("uda0", uda4));
static assert(__traits(getAttributes, value4) == AliasSeq!("uda0", uda5, uda6));
static assert(__traits(getAttributes, value5) == AliasSeq!("uda0", "uda7", uda8));
static assert(__traits(getAttributes, value6) == AliasSeq!("uda0", uda9, "uda10"));
+
+// Test that messages are correctly displayed
+static assert(Enum.value7 != Enum.value8);
diff --git a/gcc/testsuite/gdc.test/compilable/test9818.d b/gcc/testsuite/gdc.test/compilable/test9818.d
index 779b1cf0fe6..dbf19011d2b 100644
--- a/gcc/testsuite/gdc.test/compilable/test9818.d
+++ b/gcc/testsuite/gdc.test/compilable/test9818.d
@@ -1,5 +1,5 @@
/************************************/
-// 9818
+// https://issues.dlang.org/show_bug.cgi?id=9818
/*
TEST_OUTPUT:
diff --git a/gcc/testsuite/gdc.test/compilable/test9919.d b/gcc/testsuite/gdc.test/compilable/test9919.d
index 5cf8bc99cce..06f41d88711 100644
--- a/gcc/testsuite/gdc.test/compilable/test9919.d
+++ b/gcc/testsuite/gdc.test/compilable/test9919.d
@@ -1,5 +1,5 @@
// REQUIRED_ARGS: -o-
-
+// EXTRA_FILES: imports/test9919a.d imports/test9919b.d imports/test9919c.d
module test9919;
public
diff --git a/gcc/testsuite/gdc.test/compilable/testAliasLookup.d b/gcc/testsuite/gdc.test/compilable/testAliasLookup.d
new file mode 100644
index 00000000000..786c2f43dbb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/testAliasLookup.d
@@ -0,0 +1,60 @@
+// REQUIRED_ARGS: -preview=fixAliasThis
+
+// https://issues.dlang.org/show_bug.cgi?id=16086
+struct A
+{
+ void tail() {}
+}
+
+struct S16086
+{
+ struct Inner2
+ {
+ Inner a;
+ alias a this;
+ }
+
+ struct Inner
+ {
+ int unique_identifier_name;
+ int tail = 2;
+ }
+
+ Inner2 inner;
+ alias inner this;
+
+ auto works()
+ {
+ return unique_identifier_name;
+ }
+
+ auto fails()
+ {
+ int a = tail;
+ return tail; // Line 22
+ // The workaround: return this.tail;
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=16082
+struct S16082
+{
+ struct Inner
+ {
+ int any_name_but_modulename;
+ int aliasthis = 5;
+ }
+
+ Inner inner;
+ alias inner this;
+
+ auto works()
+ {
+ return any_name_but_modulename;
+ }
+ auto fails()
+ {
+ return aliasthis; // Line 20
+ }
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/testCpCtor.d b/gcc/testsuite/gdc.test/compilable/testCpCtor.d
new file mode 100644
index 00000000000..847c0f43693
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/testCpCtor.d
@@ -0,0 +1,21 @@
+// https://issues.dlang.org/show_bug.cgi?id=19870
+struct T
+{
+ int i;
+ this(ref return scope inout typeof(this) src)
+ inout @safe pure nothrow @nogc
+ {
+ i = src.i;
+ }
+}
+
+struct S
+{
+ T t;
+}
+
+void main()
+{
+ T a;
+ S b = S(a);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/testDIP37a.d b/gcc/testsuite/gdc.test/compilable/testDIP37a.d
index 8bd8b2d3397..5aa67f279ee 100644
--- a/gcc/testsuite/gdc.test/compilable/testDIP37a.d
+++ b/gcc/testsuite/gdc.test/compilable/testDIP37a.d
@@ -1,7 +1,7 @@
// PERMUTE_ARGS:
-// REQUIRED_ARGS: -c -Icompilable/extra-files
-// EXTRA_SOURCES: extra-files/pkgDIP37/datetime/package.d
-// EXTRA_SOURCES: extra-files/pkgDIP37/datetime/common.d
+// REQUIRED_ARGS: -Icompilable/extra-files
+// COMPILED_IMPORTS: extra-files/pkgDIP37/datetime/package.d
+// COMPILED_IMPORTS: extra-files/pkgDIP37/datetime/common.d
void main()
{
diff --git a/gcc/testsuite/gdc.test/compilable/testInference.d b/gcc/testsuite/gdc.test/compilable/testInference.d
index 3248f3e1035..9ffecc13a0a 100644
--- a/gcc/testsuite/gdc.test/compilable/testInference.d
+++ b/gcc/testsuite/gdc.test/compilable/testInference.d
@@ -1,6 +1,6 @@
/***************************************************/
-// 6265.
+// https://issues.dlang.org/show_bug.cgi?id=6265
pure nothrow @safe int h6265() {
return 1;
@@ -63,7 +63,7 @@ void fECPb() {
}
/***************************************************/
-// 5635
+// https://issues.dlang.org/show_bug.cgi?id=5635
pure bool foo5635(R = int)(string x)
{
@@ -79,7 +79,7 @@ void test5635()
}
/***************************************************/
-// 5936
+// https://issues.dlang.org/show_bug.cgi?id=5936
auto bug5936c(R)(R i) @safe pure nothrow {
return true;
@@ -87,7 +87,7 @@ auto bug5936c(R)(R i) @safe pure nothrow {
static assert( bug5936c(0) );
/***************************************************/
-// 6351
+// https://issues.dlang.org/show_bug.cgi?id=6351
void bug6351(alias dg)()
{
@@ -101,7 +101,7 @@ void test6351()
}
/***************************************************/
-// 6359
+// https://issues.dlang.org/show_bug.cgi?id=6359
void impure6359() nothrow @safe @nogc {}
void throwable6359() pure @safe @nogc {}
@@ -164,7 +164,7 @@ void test6359() pure nothrow @safe @nogc
}
/***************************************************/
-// 7017
+// https://issues.dlang.org/show_bug.cgi?id=7017
template map7017(fun...) if (fun.length >= 1)
{
@@ -190,7 +190,7 @@ void test7017a() pure
}
/***************************************************/
-// 7017 (little simpler cases)
+// https://issues.dlang.org/show_bug.cgi?id=7017 (little simpler cases)
auto map7017a(alias fun)() { return fun(); } // depends on purity of fun
auto map7017b(alias fun)() { return; } // always pure
@@ -239,7 +239,7 @@ pure string escapeShellArguments()
}
/***************************************************/
-// 8234
+// https://issues.dlang.org/show_bug.cgi?id=8234
void test8234()
{
@@ -259,7 +259,7 @@ void test8234()
}
/***************************************************/
-// 8504
+// https://issues.dlang.org/show_bug.cgi?id=8504
import core.demangle : demangle;
@@ -267,7 +267,7 @@ void foo8504()()
{
static assert(typeof(foo8504!()).stringof == "void()");
static assert(typeof(foo8504!()).mangleof == "FZv");
- static assert(demangle(foo8504!().mangleof) == "void testInference.foo8504!().foo8504()");
+// static assert(demangle(foo8504!().mangleof) == "void testInference.foo8504!().foo8504()");
}
auto toDelegate8504a(F)(auto ref F fp) { return fp; }
@@ -291,7 +291,7 @@ void test8504()
}
/***************************************************/
-// 8751
+// https://issues.dlang.org/show_bug.cgi?id=8751
alias bool delegate(in int) pure Bar8751;
Bar8751 foo8751a(immutable int x) pure
@@ -304,7 +304,7 @@ Bar8751 foo8751b(const int x) pure
}
/***************************************************/
-// 8793
+// https://issues.dlang.org/show_bug.cgi?id=8793
alias bool delegate(in int) pure Dg8793;
alias bool function(in int) pure Fp8793;
@@ -327,7 +327,7 @@ Dg8793 foo8793ptr1(immutable int* p) pure { return x => *p == x; } // OK
Dg8793 foo8793ptr2(const int* p) pure { return x => *p == x; } // OK <- error
/***************************************************/
-// 9072
+// https://issues.dlang.org/show_bug.cgi?id=9072
struct A9072(T)
{
@@ -340,7 +340,9 @@ void test9072()
}
/***************************************************/
-// 5933 + Issue 8504 - Template attribute inferrence doesn't work
+// https://issues.dlang.org/show_bug.cgi?id=5933
+// https://issues.dlang.org/show_bug.cgi?id=8504
+// Template attribute inferrence doesn't work
int foo5933()(int a) { return a*a; }
struct S5933
@@ -359,7 +361,7 @@ void test5933()
}
/***************************************************/
-// 9148
+// https://issues.dlang.org/show_bug.cgi?id=9148
void test9148a() pure
{
@@ -517,7 +519,7 @@ void test9148e()
}
/***************************************************/
-// 12912
+// https://issues.dlang.org/show_bug.cgi?id=12912
struct S12912(alias fun)
{
@@ -539,7 +541,7 @@ class C12912
}
/***************************************************/
-// 10002
+// https://issues.dlang.org/show_bug.cgi?id=10002
void impure10002() {}
void remove10002(alias pred, bool impure = false, Range)(Range range)
@@ -568,7 +570,7 @@ class Node10002
}
/***************************************************/
-// 10148
+// https://issues.dlang.org/show_bug.cgi?id=10148
void fa10148() {} // fa is @system
@@ -604,7 +606,7 @@ void test10148()
}
/***************************************************/
-// 10289
+// https://issues.dlang.org/show_bug.cgi?id=10289
void test10289()
{
@@ -645,7 +647,7 @@ void test10289()
}
/***************************************************/
-// 10296
+// https://issues.dlang.org/show_bug.cgi?id=10296
void foo10296()()
{
@@ -661,7 +663,7 @@ pure void test10296()
}
/***************************************************/
-// 12025
+// https://issues.dlang.org/show_bug.cgi?id=12025
struct Foo12025
{
@@ -689,7 +691,7 @@ void test12025b() pure
}
/***************************************************/
-// 12542
+// https://issues.dlang.org/show_bug.cgi?id=12542
int logOf12542(T)(T n)
{
@@ -704,14 +706,14 @@ void test12542() @safe nothrow pure
}
/***************************************************/
-// 12704
+// https://issues.dlang.org/show_bug.cgi?id=12704
void foo12704() @system;
alias FP12704 = typeof(function() { foo12704(); });
static assert(is(FP12704 == void function() @system));
/***************************************************/
-// 12970
+// https://issues.dlang.org/show_bug.cgi?id=12970
@system { @safe void f12970a() {} }
@system { void f12970b() @safe {} }
@@ -769,7 +771,7 @@ static assert(AliasDecl_FP2.stringof == "void function()");
static assert(AliasDecl_FP3.stringof == "void function() @safe");
/***************************************************/
-// 13217
+// https://issues.dlang.org/show_bug.cgi?id=13217
void writeln13217(string) {}
@@ -788,7 +790,7 @@ void test13217()
}
/***************************************************/
-// 13840
+// https://issues.dlang.org/show_bug.cgi?id=13840
struct Foo13840
{
diff --git a/gcc/testsuite/gdc.test/compilable/testVRP.d b/gcc/testsuite/gdc.test/compilable/testVRP.d
index 954b5019b83..bdd72d0962b 100644
--- a/gcc/testsuite/gdc.test/compilable/testVRP.d
+++ b/gcc/testsuite/gdc.test/compilable/testVRP.d
@@ -371,7 +371,7 @@ void bug1977_comment20()
}
/******************************************/
-// 9617
+// https://issues.dlang.org/show_bug.cgi?id=9617
void test9617()
{
diff --git a/gcc/testsuite/gdc.test/compilable/testcontracts.d b/gcc/testsuite/gdc.test/compilable/testcontracts.d
index 21c0e4b35cc..6a361903f59 100644
--- a/gcc/testsuite/gdc.test/compilable/testcontracts.d
+++ b/gcc/testsuite/gdc.test/compilable/testcontracts.d
@@ -1,4 +1,4 @@
-// EXTRA_SOURCES: imports/testcontracts.d
+// COMPILED_IMPORTS: imports/testcontracts.d
import imports.testcontracts;
@@ -13,7 +13,7 @@ class Derived3602 : Base3602
assert(x > 0);
assert(y > 0);
}
- body
+ do
{
}
}
@@ -35,31 +35,31 @@ class Foo17502
{
auto foo()
out {}
- body {}
+ do {}
auto bar()
out { assert (__result > 5); }
- body { return 6; }
+ do { return 6; }
auto bar_2()
out (res) { assert (res > 5); }
- body { return 6; }
+ do { return 6; }
int concrete()
out { assert(__result > 5); }
- body { return 6; }
+ do { return 6; }
int concrete_2()
out(res) { assert (res > 5); }
- body { return 6; }
+ do { return 6; }
void void_foo()
out {}
- body {}
+ do {}
auto void_auto()
out {}
- body {}
+ do {}
}
/***************************************************/
@@ -76,7 +76,7 @@ class A17502
{
assert(res > 5);
}
- body
+ do
{
return p;
}
@@ -89,7 +89,7 @@ class C17502 : B17502
{
assert(p > 3);
}
- body
+ do
{
return p * 2;
}
@@ -102,7 +102,7 @@ class B17502 : A17502
{
assert(p > 2);
}
- body
+ do
{
return p * 3;
}
@@ -118,7 +118,7 @@ class X17502 : Y17502
{
assert(p > 3);
}
- body
+ do
{
return p * 2;
}
@@ -131,7 +131,7 @@ class Y17502 : Z17502
{
assert(p > 2);
}
- body
+ do
{
return p * 3;
}
@@ -148,7 +148,7 @@ class Z17502
{
assert(res > 5);
}
- body
+ do
{
return p;
}
@@ -166,9 +166,21 @@ final class Foo17893(T)
{
maythrow();
}
- body
+ do
{
}
}
Foo17893!int foo17893;
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15984
+
+alias Bar15984 = extern (C) void function(void*);
+
+final class C15984
+{
+ void foo(Bar15984 bar)
+ in { assert(bar); }
+ do {}
+}
diff --git a/gcc/testsuite/gdc.test/compilable/testcstuff3.d b/gcc/testsuite/gdc.test/compilable/testcstuff3.d
new file mode 100644
index 00000000000..89228a9bc4a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/testcstuff3.d
@@ -0,0 +1,4 @@
+// EXTRA_FILES: imports/cstuff3.c
+import imports.cstuff3;
+
+static assert(squared(4) == 16);
diff --git a/gcc/testsuite/gdc.test/compilable/testdip1008.d b/gcc/testsuite/gdc.test/compilable/testdip1008.d
new file mode 100644
index 00000000000..5e024fe68ec
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/testdip1008.d
@@ -0,0 +1,21 @@
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -preview=dip1008
+
+int bar()
+{
+ try
+ {
+ throw new Exception("message");
+ }
+ catch (Exception e)
+ {
+ return 7;
+ }
+}
+
+
+void foo()
+{
+ enum r = bar();
+ static assert(r == 7);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/testexpression.d b/gcc/testsuite/gdc.test/compilable/testexpression.d
index b581df7bdca..df491321d48 100644
--- a/gcc/testsuite/gdc.test/compilable/testexpression.d
+++ b/gcc/testsuite/gdc.test/compilable/testexpression.d
@@ -54,8 +54,6 @@ void TestOpAndAssign(Tx, Ux, ops)()
struct boolean { alias TT!(bool) x; }
struct integral { alias TT!(byte, ubyte, short, ushort, int, uint, long, ulong) x; }
struct floating { alias TT!(float, double, real) x; }
-struct imaginary { alias TT!(ifloat, idouble, ireal) x; }
-struct complex { alias TT!(cfloat, cdouble, creal) x; }
struct all { alias TT!("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=") x; }
struct arith { alias TT!("+=", "-=", "*=", "/=", "%=") x; }
@@ -76,17 +74,6 @@ void OpAssignCases(alias X)()
X!(floating, boolean, arith)();
X!(floating, integral, arith)();
X!(floating, floating, arith)();
-
- X!(imaginary, boolean, muldivmod)();
- X!(imaginary, integral, muldivmod)();
- X!(imaginary, floating, muldivmod)();
- X!(imaginary, imaginary, addsub)();
-
- X!(complex, boolean, arith)();
- X!(complex, integral, arith)();
- X!(complex, floating, arith)();
- X!(complex, imaginary, arith)();
- X!(complex, complex, nomod)();
}
void OpReAssignCases(alias X)()
@@ -99,23 +86,12 @@ void OpReAssignCases(alias X)()
X!(floating, boolean, arith)();
X!(floating, integral, arith)();
X!(floating, floating, arith)();
-
- X!(imaginary, boolean, muldivmod)();
- X!(imaginary, integral, muldivmod)();
- X!(imaginary, floating, muldivmod)();
- X!(imaginary, imaginary, addsub)();
-
- X!(complex, boolean, arith)();
- X!(complex, integral, arith)();
- X!(complex, floating, arith)();
- X!(complex, imaginary, arith)();
- X!(complex, complex, nomod)();
}
void main()
{
OpAssignCases!TestOpAssign();
OpAssignCases!TestOpAssignAssign(); // was once disabled due to bug 7436
- OpAssignCases!TestOpAssignAuto(); // 5181
+ OpAssignCases!TestOpAssignAuto(); // https://issues.dlang.org/show_bug.cgi?id=5181
OpReAssignCases!TestOpAndAssign();
}
diff --git a/gcc/testsuite/gdc.test/compilable/testfwdref.d b/gcc/testsuite/gdc.test/compilable/testfwdref.d
index 12b5cc85279..d5ad5fa3894 100644
--- a/gcc/testsuite/gdc.test/compilable/testfwdref.d
+++ b/gcc/testsuite/gdc.test/compilable/testfwdref.d
@@ -1,7 +1,8 @@
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/fwdref9514.d imports/fwdref12201a.d
/***************************************************/
-// 6766
+// https://issues.dlang.org/show_bug.cgi?id=6766
class Foo6766
{
@@ -16,7 +17,7 @@ struct Bar6766
}
/***************************************************/
-// 8609
+// https://issues.dlang.org/show_bug.cgi?id=8609
struct Tuple8609(T)
{
@@ -48,7 +49,7 @@ struct Bar8609b
}
/***************************************************/
-// 8698
+// https://issues.dlang.org/show_bug.cgi?id=8698
interface IRoot8698a {}
interface IClass8698a : IRoot8698a { }
@@ -67,7 +68,7 @@ void test8698b(Class8698b.Value) { }
interface IRoot8698b {}
/***************************************************/
-// 9514
+// https://issues.dlang.org/show_bug.cgi?id=9514
template TStructHelpers9514a()
{
@@ -112,7 +113,7 @@ template FieldNames9514b()
}
/***************************************************/
-// 10015
+// https://issues.dlang.org/show_bug.cgi?id=10015
struct S10015(T) { alias X = int; }
@@ -120,7 +121,7 @@ alias Y10015 = s10015.X;
S10015!int s10015;
/***************************************************/
-// 10101
+// https://issues.dlang.org/show_bug.cgi?id=10101
int front10101(int);
@@ -147,7 +148,7 @@ void test10101()
}
/***************************************************/
-// 11019
+// https://issues.dlang.org/show_bug.cgi?id=11019
class A11019
{
@@ -162,7 +163,7 @@ class B11019 : A11019
class D11019 : B11019 {}
/***************************************************/
-// 11166
+// https://issues.dlang.org/show_bug.cgi?id=11166
template Tup11166(T...) { alias Tup11166 = T; }
@@ -193,7 +194,7 @@ struct S11166b
}
/***************************************************/
-// 12152
+// https://issues.dlang.org/show_bug.cgi?id=12152
class A12152
{
@@ -208,7 +209,7 @@ class B12152 : A12152
static assert(is(A12152.Y == int));
/***************************************************/
-// 12201
+// https://issues.dlang.org/show_bug.cgi?id=12201
template T12201()
{
@@ -258,7 +259,7 @@ struct S12201b
}
/***************************************************/
-// 12531
+// https://issues.dlang.org/show_bug.cgi?id=12531
struct Node12531(T)
{
@@ -274,7 +275,7 @@ void test12531()
}
/***************************************************/
-// 12543
+// https://issues.dlang.org/show_bug.cgi?id=12543
class C12543;
static assert(C12543.sizeof == (void*).sizeof);
@@ -282,7 +283,7 @@ static assert(C12543.alignof == (void*).sizeof);
static assert(C12543.mangleof == "C10testfwdref6C12543");
/***************************************************/
-// 14010
+// https://issues.dlang.org/show_bug.cgi?id=14010
enum E14010;
static assert(E14010.mangleof == "E10testfwdref6E14010");
@@ -291,7 +292,7 @@ struct S14010;
static assert(S14010.mangleof == "S10testfwdref6S14010");
/***************************************************/
-// 12983
+// https://issues.dlang.org/show_bug.cgi?id=12983
alias I12983 = int;
class B12983(T) { alias MyC = C12983!string; }
@@ -307,7 +308,7 @@ void f12983();
void f12983(I12983);
/***************************************************/
-// 12984
+// https://issues.dlang.org/show_bug.cgi?id=12984
class B12984a { alias MyD = D12984a!int; }
class C12984a : B12984a { }
@@ -343,7 +344,7 @@ static assert(__traits(classInstanceSize, B12984b) == (void*).sizeof * 2 + int.s
static assert(__traits(classInstanceSize, C12984b) == (void*).sizeof * 2 + int.sizeof * 2);
/***************************************************/
-// 14390
+// https://issues.dlang.org/show_bug.cgi?id=14390
class B14390a { alias MyD = D14390a!int; }
class C14390a : B14390a { void f(int) {} }
@@ -356,7 +357,7 @@ class D14390b(T) { alias MyE = E14390b!float; }
class E14390b(T) : D14390b!int { void m() { auto c = new C14390b(); } }
/***************************************************/
-// 13860
+// https://issues.dlang.org/show_bug.cgi?id=13860
/*
TEST_OUTPUT:
@@ -386,7 +387,7 @@ void test13860()
}
/***************************************************/
-// 14083
+// https://issues.dlang.org/show_bug.cgi?id=14083
class NBase14083
{
@@ -436,7 +437,7 @@ static assert(
}());
/***************************************************/
-// 14549
+// https://issues.dlang.org/show_bug.cgi?id=14549
string foo14549(T)()
{
@@ -480,7 +481,8 @@ class Bar14549
}
// ----
-// 14609 - regression case
+// https://issues.dlang.org/show_bug.cgi?id=14609
+// regression case
interface Foo14609(T)
{
@@ -651,7 +653,7 @@ void test15726y()
}
/***************************************************/
-// 15726
+// https://issues.dlang.org/show_bug.cgi?id=15726
struct RC15726(T)
{
diff --git a/gcc/testsuite/gdc.test/compilable/testheader1.d b/gcc/testsuite/gdc.test/compilable/testheader1.d
index 256a1fe0bd9..9b14f7b0d34 100644
--- a/gcc/testsuite/gdc.test/compilable/testheader1.d
+++ b/gcc/testsuite/gdc.test/compilable/testheader1.d
@@ -1,12 +1,9 @@
-// EXTRA_SOURCES: extra-files/header1.d
-// REQUIRED_ARGS: -o- -unittest -H -Hf${RESULTS_DIR}/compilable/header1.di
-// PERMUTE_ARGS: -d -dw
-// POST_SCRIPT: compilable/extra-files/header-postscript.sh
/*
-TEST_OUTPUT:
----
-Hello World
----
+EXTRA_SOURCES: extra-files/header1.d
+REQUIRED_ARGS: -o- -unittest -H -Hf${RESULTS_DIR}/compilable/testheader1.di -ignore
+PERMUTE_ARGS: -d -dw
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/testheader1.di
+TEST_OUTPUT_FILE: extra-files/header1.di
*/
void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/testheader12567a.d b/gcc/testsuite/gdc.test/compilable/testheader12567a.d
index 27c14756880..3372d871ea6 100644
--- a/gcc/testsuite/gdc.test/compilable/testheader12567a.d
+++ b/gcc/testsuite/gdc.test/compilable/testheader12567a.d
@@ -1,6 +1,16 @@
-// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header12567a.di
-// PERMUTE_ARGS:
-// POST_SCRIPT: compilable/extra-files/header-postscript.sh header12567a
+/*
+REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/testheader12567a.di
+PERMUTE_ARGS:
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/testheader12567a.di
+
+TEST_OUTPUT:
+---
+=== ${RESULTS_DIR}/compilable/testheader12567a.di
+// D import file generated from 'compilable/testheader12567a.d'
+deprecated module header12567a;
+void main();
+---
+*/
deprecated module header12567a;
diff --git a/gcc/testsuite/gdc.test/compilable/testheader12567b.d b/gcc/testsuite/gdc.test/compilable/testheader12567b.d
index 93393c50928..f17a37eeea5 100644
--- a/gcc/testsuite/gdc.test/compilable/testheader12567b.d
+++ b/gcc/testsuite/gdc.test/compilable/testheader12567b.d
@@ -1,6 +1,16 @@
-// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header12567b.di
-// PERMUTE_ARGS:
-// POST_SCRIPT: compilable/extra-files/header-postscript.sh header12567b
+/*
+REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/testheader12567b.di
+PERMUTE_ARGS:
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/testheader12567b.di
+
+TEST_OUTPUT:
+---
+=== ${RESULTS_DIR}/compilable/testheader12567b.di
+// D import file generated from 'compilable/testheader12567b.d'
+deprecated("message") module header12567b;
+void main();
+---
+*/
deprecated("message") module header12567b;
diff --git a/gcc/testsuite/gdc.test/compilable/testheader17125.d b/gcc/testsuite/gdc.test/compilable/testheader17125.d
new file mode 100644
index 00000000000..7549f617aa0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/testheader17125.d
@@ -0,0 +1,19 @@
+/*
+EXTRA_SOURCES: extra-files/header17125.d
+PERMUTE_ARGS:
+REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/testheader17125.di
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/testheader17125.di
+
+TEST_OUTPUT:
+---
+=== ${RESULTS_DIR}/compilable/testheader17125.di
+// D import file generated from 'compilable/extra-files/header17125.d'
+void func1(real value = 103500.0L);
+void func2(real value = 520199.0F);
+void func3(real value = 970000.0);
+void func4(real value = 102450.0F);
+void func5(real value = 412502.0L);
+---
+*/
+
+void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/testheader1i.d b/gcc/testsuite/gdc.test/compilable/testheader1i.d
index 0eca3c44c19..f79a1af4027 100644
--- a/gcc/testsuite/gdc.test/compilable/testheader1i.d
+++ b/gcc/testsuite/gdc.test/compilable/testheader1i.d
@@ -1,12 +1,9 @@
-// EXTRA_SOURCES: extra-files/header1.d
-// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header1i.di -inline
-// PERMUTE_ARGS: -d -dw
-// POST_SCRIPT: compilable/extra-files/header-postscript.sh
/*
-TEST_OUTPUT:
----
-Hello World
----
+EXTRA_SOURCES: extra-files/header1.d
+REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/testheader1i.di -inline -ignore
+PERMUTE_ARGS: -d -dw
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/testheader1i.di
+TEST_OUTPUT_FILE: extra-files/header1i.di
*/
void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/testheader2.d b/gcc/testsuite/gdc.test/compilable/testheader2.d
index 003d56467a9..25cc3c933da 100644
--- a/gcc/testsuite/gdc.test/compilable/testheader2.d
+++ b/gcc/testsuite/gdc.test/compilable/testheader2.d
@@ -1,6 +1,9 @@
-// EXTRA_SOURCES: extra-files/header2.d
-// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header2.di
-// PERMUTE_ARGS:
-// POST_SCRIPT: compilable/extra-files/header-postscript.sh header2
+/*
+EXTRA_SOURCES: extra-files/header2.d
+REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/testheader2.di
+PERMUTE_ARGS:
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/testheader2.di
+TEST_OUTPUT_FILE: extra-files/header2.di
+*/
void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/testheader2i.d b/gcc/testsuite/gdc.test/compilable/testheader2i.d
index 79662edd184..517fae41d53 100644
--- a/gcc/testsuite/gdc.test/compilable/testheader2i.d
+++ b/gcc/testsuite/gdc.test/compilable/testheader2i.d
@@ -1,6 +1,9 @@
-// EXTRA_SOURCES: extra-files/header2.d
-// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header2i.di -inline
-// PERMUTE_ARGS:
-// POST_SCRIPT: compilable/extra-files/header-postscript.sh header2i
+/*
+EXTRA_SOURCES: extra-files/header2.d
+REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/testheader2i.di -inline
+PERMUTE_ARGS:
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/testheader2i.di
+TEST_OUTPUT_FILE: extra-files/header2i.di
+*/
void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/testheader3.d b/gcc/testsuite/gdc.test/compilable/testheader3.d
index f5fdbc1d43e..be38d7c76ee 100644
--- a/gcc/testsuite/gdc.test/compilable/testheader3.d
+++ b/gcc/testsuite/gdc.test/compilable/testheader3.d
@@ -1,8 +1,27 @@
-// EXTRA_SOURCES: extra-files/header3.d
-// REQUIRED_ARGS: -o- -unittest -H -Hf${RESULTS_DIR}/compilable/header3.di
-// PERMUTE_ARGS: -d -dw
-// POST_SCRIPT: compilable/extra-files/header-postscript.sh header3
-
-void main() {}
+/*
+EXTRA_SOURCES: extra-files/header3.d
+REQUIRED_ARGS: -o- -unittest -H -Hf${RESULTS_DIR}/compilable/testheader3.di
+PERMUTE_ARGS: -d -dw
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/testheader3.di
+TEST_OUTPUT:
+---
+=== ${RESULTS_DIR}/compilable/testheader3.di
+// D import file generated from 'compilable/extra-files/header3.d'
+auto elseifchain()
+{
+ bool a, b, c;
+ if (a)
+ {
+ }
+ else if (b)
+ {
+ }
+ else if (c)
+ {
+ }
+}
+---
+*/
+void main() {}
diff --git a/gcc/testsuite/gdc.test/compilable/testheaderudamodule.d b/gcc/testsuite/gdc.test/compilable/testheaderudamodule.d
index 5ee94868993..b6b0ed1a278 100644
--- a/gcc/testsuite/gdc.test/compilable/testheaderudamodule.d
+++ b/gcc/testsuite/gdc.test/compilable/testheaderudamodule.d
@@ -1,6 +1,22 @@
-// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/testheaderudamodule.di
-// PERMUTE_ARGS:
-// POST_SCRIPT: compilable/extra-files/header-postscript.sh testheaderudamodule
+/*
+REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/testheaderudamodule.di
+PERMUTE_ARGS:
+OUTPUT_FILES: ${RESULTS_DIR}/compilable/testheaderudamodule.di
+
+TEST_OUTPUT:
+---
+=== ${RESULTS_DIR}/compilable/testheaderudamodule.di
+// D import file generated from 'compilable/testheaderudamodule.d'
+@(1, UDA(2))
+module testheaderudamodule;
+struct UDA
+{
+ int a;
+}
+void main();
+void foo(@(1) int bar, @UDA(2) string bebe);
+---
+*/
@(1, UDA(2))
module testheaderudamodule;
@@ -12,4 +28,4 @@ struct UDA
void main() {}
-void foo(@(1) int bar, @UDA(2) string bebe);
+void foo(@(1) int bar, @UDA(2) string bebe) {}
diff --git a/gcc/testsuite/gdc.test/compilable/testimport12242.d b/gcc/testsuite/gdc.test/compilable/testimport12242.d
index 1d1cccd0100..39e0c16ac43 100644
--- a/gcc/testsuite/gdc.test/compilable/testimport12242.d
+++ b/gcc/testsuite/gdc.test/compilable/testimport12242.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/imp12242a.d imports/imp12242a1.d imports/imp12242a2.d imports/imp12242b.d imports/imp12242b1.d imports/imp12242b2.d
module testimport12242;
import imports.imp12242a; // test // stripA == OverloadSet
diff --git a/gcc/testsuite/gdc.test/compilable/testlambdacomp.d b/gcc/testsuite/gdc.test/compilable/testlambdacomp.d
new file mode 100644
index 00000000000..e2efdb02515
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/testlambdacomp.d
@@ -0,0 +1,216 @@
+// EXTRA_FILES: imports/testlambda1.d imports/testlambda2.d
+module testlambdacomp;
+
+void test1()
+{
+ static assert(__traits(isSame, (a, b) => a + b, (c, d) => c + d));
+ static assert(__traits(isSame, a => ++a, b => ++b));
+ static assert(!__traits(isSame, (int a, int b) => a + b, (a, b) => a + b));
+ static assert(__traits(isSame, (a, b) => a + b + 10, (c, d) => c + d + 10));
+}
+
+class Y
+{
+ static int r = 5;
+ int x;
+ this(int x)
+ {
+ this.x = x;
+ }
+}
+
+class A
+{
+ Y a;
+ this(Y a)
+ {
+ this.a = a;
+ }
+}
+
+void foo3(alias pred)()
+{
+ static assert(!__traits(isSame, pred, (A x, A y) => ++x.a.x + (--y.a.x)));
+}
+
+void test2()
+{
+
+ int b;
+ static assert(!__traits(isSame, a => a + b, a => a + b));
+
+ int f() { return 3;}
+ static assert(__traits(isSame, a => a + f(), a => a + f()));
+
+ class A
+ {
+ Y a;
+ this(Y a)
+ {
+ this.a = a;
+ }
+ }
+
+ class B
+ {
+ int a;
+ this(int a)
+ {
+ this.a = a;
+ }
+ }
+
+ B q = new B(7);
+ alias pred = (A a, A b) => ++a.a.x + (--b.a.x);
+ foo3!pred();
+ static assert(!__traits(isSame, (A a) => ++a.a.x + 2, (A b) => ++b.a.x + 3));
+ static assert(__traits(isSame, pred, (A x, A y) => ++x.a.x + (--y.a.x)));
+ static assert(!__traits(isSame, (B a) => ++a.a + 2, (B b) => ++b.a + 3));
+ static assert(__traits(isSame, (B a) => ++a.a, (B a) => ++a.a));
+
+ B cl = new B(7);
+ static assert(!__traits(isSame, a => a + q.a, c => c + cl.a));
+
+ class C(G)
+ {
+ G a;
+ this(int a)
+ {
+ this.a = a;
+ }
+ }
+ static assert(!__traits(isSame, (C!int a) => ++a.a, (C!int a) => ++a.a));
+
+ struct X
+ {
+ int a;
+ }
+ static assert(__traits(isSame, (X a) => a.a + 2, (X b) => b.a + 2));
+
+ struct T(G)
+ {
+ G a;
+ }
+ static assert(!__traits(isSame, (T!int a) => ++a.a, (T!int a) => ++a.a));
+
+}
+
+void test3()
+{
+ enum q = 10;
+ static assert(__traits(isSame, (a, b) => a + b + q, (c, d) => c + d + 10));
+
+ struct Bar
+ {
+ int a;
+ }
+ enum r1 = Bar(1);
+ enum r2 = Bar(1);
+ static assert(__traits(isSame, a => a + r1.a, b => b + r2.a));
+
+ enum X { A, B, C}
+ static assert(__traits(isSame, a => a + X.A, a => a + 0));
+}
+
+void foo(alias pred)()
+{
+ static assert(__traits(isSame, pred, (c, d) => c + d));
+ static assert(__traits(isSame, (c, d) => c + d, pred));
+}
+
+void bar(alias pred)()
+{
+ static assert(__traits(isSame, pred, (c, d) => c < d + 7));
+
+ enum q = 7;
+ static assert(__traits(isSame, pred, (c, d) => c < d + q));
+
+ int r = 7;
+ static assert(!__traits(isSame, pred, (c, d) => c < d + r));
+}
+void test4()
+{
+ foo!((a, b) => a + b)();
+ bar!((a, b) => a < b + 7);
+}
+
+int bar()
+{
+ return 2;
+}
+
+void testImportedFunctions(alias pred)()
+{
+ // imports.testalambda1.bar != imports.testlambda2.bar
+ import imports.testlambda2 : bar;
+ static assert(!__traits(isSame, pred, (int a) => a + bar()));
+}
+
+void testLocalGlobalFunctionScopes(alias pred)()
+{
+ // testlambdacomp.bar != testlambdacomp.test5.bar
+ static assert(!__traits(isSame, pred, (int a) => a + bar()));
+
+ // imports.testlambda1.bar != testlambdacomp.test5.bar
+ import imports.testlambda1 : bar;
+ static assert(!__traits(isSame, pred, (int a) => a + bar()));
+
+ // functions imported from different modules are not equal
+ testImportedFunctions!((int a) => a + bar())();
+}
+
+// lambda functions which contain function calls
+void test5()
+{
+
+ int bar()
+ {
+ return 3;
+ }
+
+ // functions in the same scope
+ alias pred = a => a + bar();
+ alias pred2 = b => b + bar();
+ static assert(__traits(isSame, pred, pred2));
+
+ // functions in different scopes
+ testLocalGlobalFunctionScopes!((int a) => a + bar())();
+
+ int foo(int a, int b)
+ {
+ return 2 + a + b;
+ }
+
+ // functions with different kind of parameters
+ alias preda23 = a => a + foo(2, 3);
+ alias predb23 = b => b + foo(2, 3);
+ alias predc24 = c => c + foo(2, 4);
+ alias predd23 = (int d) => d + foo(2, 3);
+ alias prede23 = (int e) => e + foo(2, 3);
+ alias predf24 = (int f) => f + foo(2, 4);
+ static assert(__traits(isSame, preda23, predb23));
+ static assert(!__traits(isSame, predc24, predd23));
+ static assert(__traits(isSame, predd23, prede23));
+ static assert(!__traits(isSame, prede23, predf24));
+
+ // functions with function calls as parameters
+ static assert(!__traits(isSame, (int a, int b) => foo(foo(1, a), foo(1, b)), (int a, int b) => foo(foo(1, a), foo(2, b))));
+ static assert(!__traits(isSame, (a, b) => foo(foo(1, a), foo(1, b)), (int a, int b) => foo(foo(1, a), foo(2, b))));
+
+ float floatFunc(float a, float b)
+ {
+ return a + b;
+ }
+
+ static assert(__traits(isSame, a => floatFunc(a, 1.0), b => floatFunc(b, 1.0)));
+ static assert(!__traits(isSame, a => floatFunc(a, 1.0), b => floatFunc(b, 2.0)));
+}
+
+void main()
+{
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/testparse.d b/gcc/testsuite/gdc.test/compilable/testparse.d
index f5141464692..99378a9df53 100644
--- a/gcc/testsuite/gdc.test/compilable/testparse.d
+++ b/gcc/testsuite/gdc.test/compilable/testparse.d
@@ -2,12 +2,12 @@
// REQUIRED_ARGS: -o-
/***************************************************/
-// 6719
+// https://issues.dlang.org/show_bug.cgi?id=6719
static assert(__traits(compiles, mixin("(const(A))[0..0]")) == false);
/***************************************************/
-// 9232
+// https://issues.dlang.org/show_bug.cgi?id=9232
struct Foo9232
{
@@ -25,7 +25,7 @@ void test9232()
}
/***************************************************/
-// 9401
+// https://issues.dlang.org/show_bug.cgi?id=9401
struct S9401a
{
@@ -44,7 +44,7 @@ void test9401() nothrow pure @safe
}
/***************************************************/
-// 9649
+// https://issues.dlang.org/show_bug.cgi?id=9649
class Outer9649
{
@@ -60,7 +60,7 @@ void test9649()
}
/***************************************************/
-// 9679
+// https://issues.dlang.org/show_bug.cgi?id=9679
void test9679(inout int = 0)
{
@@ -90,7 +90,7 @@ void test9679(inout int = 0)
}
/***************************************************/
-// 9901
+// https://issues.dlang.org/show_bug.cgi?id=9901
template isGood9901(T)
{
@@ -106,7 +106,7 @@ void test9901()
}
/***************************************************/
-// 10199
+// https://issues.dlang.org/show_bug.cgi?id=10199
void test10199()
{
@@ -115,7 +115,7 @@ label:
}
/***************************************************/
-// 12460
+// https://issues.dlang.org/show_bug.cgi?id=12460
void f12460(T)()
{
@@ -132,7 +132,7 @@ void test12460()
}
/***************************************************/
-// 11689
+// https://issues.dlang.org/show_bug.cgi?id=11689
void test11689()
{
@@ -140,12 +140,12 @@ void test11689()
}
/***************************************************/
-// 11751
+// https://issues.dlang.org/show_bug.cgi?id=11751
static assert(is(float == typeof(0x0.1p1F)));
/***************************************************/
-// 11957
+// https://issues.dlang.org/show_bug.cgi?id=11957
extern(C++) class C11957
{
@@ -161,8 +161,33 @@ void test11957()
}
/***************************************************/
-// 13049
+// https://issues.dlang.org/show_bug.cgi?id=13049
enum mangle13049(T) = T.mangleof;
alias FP13049 = void function(scope int); // OK
static assert(mangle13049!FP13049 == mangle13049!(void function(scope int))); // OK <- NG
+
+/***************************************************/
+// was not covered until the **12th of March 2019**
+void testIfConditionWithSTCandType()
+{
+ auto call(){return 0;}
+ if (const size_t i = call()) {}
+}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20791
+extern(C++, "foo", )
+struct S {}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=22019
+void test22019()
+{
+ final switch (1)
+ {
+ case 1,:
+ case 2,3,:
+ break;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/testsctreturn.d b/gcc/testsuite/gdc.test/compilable/testsctreturn.d
new file mode 100644
index 00000000000..96b82da922f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/testsctreturn.d
@@ -0,0 +1,19 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ * This case winds up calling buildScopeRef() with stc having only STC.return_ set.
+ */
+
+struct PackedPtrImpl(size_t bits)
+{
+pure nothrow:
+ this(inout(size_t)* ptr) inout @safe @nogc
+ {
+ origin = ptr;
+ }
+ size_t* origin;
+}
+
+void test()
+{
+ size_t* p;
+ const ppi = const(PackedPtrImpl!(3))(p);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/testtempl2.d b/gcc/testsuite/gdc.test/compilable/testtempl2.d
new file mode 100644
index 00000000000..57832d9694a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/testtempl2.d
@@ -0,0 +1,24 @@
+class C
+{
+ int m;
+ auto fun()
+ {
+ inc!m();
+ new N!m;
+ }
+}
+
+auto inc(alias m)()
+{
+ ++m;
+}
+
+class N(alias m)
+{
+}
+
+void main()
+{
+ auto c = new C;
+ c.new N!(c.m);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/traits.d b/gcc/testsuite/gdc.test/compilable/traits.d
index 4d8a5e140c4..3c65dac0932 100644
--- a/gcc/testsuite/gdc.test/compilable/traits.d
+++ b/gcc/testsuite/gdc.test/compilable/traits.d
@@ -1,4 +1,4 @@
-// REQUIRED_ARGS:
+// REQUIRED_ARGS: -extern-std=c++98
// EXTRA_FILES: imports/plainpackage/plainmodule.d imports/pkgmodule/package.d imports/pkgmodule/plainmodule.d
// This file is intended to contain all compilable traits-related tests in an
@@ -19,9 +19,22 @@ class C19152
static assert(is(typeof(__traits(getTargetInfo, "cppRuntimeLibrary")) == string));
version (CppRuntime_Microsoft)
{
- static assert(__traits(getTargetInfo, "cppRuntimeLibrary") == "libcmt");
+ static assert(__traits(getTargetInfo, "cppRuntimeLibrary") == "libcmt" ||
+ __traits(getTargetInfo, "cppRuntimeLibrary")[0..6] == "msvcrt"); // includes mingw import libs
}
+version (D_HardFloat)
+ static assert(__traits(getTargetInfo, "floatAbi") == "hard");
+
+version (Win64)
+ static assert(__traits(getTargetInfo, "objectFormat") == "coff");
+version (OSX)
+ static assert(__traits(getTargetInfo, "objectFormat") == "macho");
+version (linux)
+ static assert(__traits(getTargetInfo, "objectFormat") == "elf");
+
+static assert(__traits(getTargetInfo, "cppStd") == 199711);
+
import imports.plainpackage.plainmodule;
import imports.pkgmodule.plainmodule;
@@ -103,6 +116,7 @@ void foo(T)()
{
this (ref S rhs) {}
}
+ static assert (__traits(hasCopyConstructor, S!int));
}
struct U(T)
@@ -123,18 +137,182 @@ struct DisabledPostblit
struct NoCpCtor { }
class C19902 { }
+static assert(__traits(hasCopyConstructor, S));
+static assert(__traits(hasCopyConstructor, OuterS.S));
+static assert(__traits(hasCopyConstructor, OuterS));
static assert(__traits(compiles, foo!int));
static assert(__traits(compiles, foo!S));
+static assert(__traits(hasCopyConstructor, U!int));
+static assert(__traits(hasCopyConstructor, U!S));
static assert(!__traits(hasPostblit, U!S));
static assert(__traits(hasPostblit, SPostblit));
+static assert(!__traits(hasCopyConstructor, SPostblit));
+static assert(!__traits(hasCopyConstructor, NoCpCtor));
+static assert(!__traits(hasCopyConstructor, C19902));
+static assert(!__traits(hasCopyConstructor, int));
static assert(!__traits(hasPostblit, NoCpCtor));
static assert(!__traits(hasPostblit, C19902));
static assert(!__traits(hasPostblit, int));
-// Check that invalid use cases don't compile
-static assert(!__traits(compiles, __traits(hasPostblit)));
-static assert(!__traits(compiles, __traits(hasPostblit, S())));
-
static assert(__traits(isCopyable, int));
static assert(!__traits(isCopyable, DisabledPostblit));
+struct S1 {} // Fine. Can be copied
+struct S2 { this(this) {} } // Fine. Can be copied
+struct S3 { @disable this(this); } // Not fine. Copying is disabled.
+struct S4 { S3 s; } // Not fine. A field has copying disabled.
+class C1 {}
+static assert( __traits(isCopyable, S1));
+static assert( __traits(isCopyable, S2));
+static assert(!__traits(isCopyable, S3));
+static assert(!__traits(isCopyable, S4));
+static assert(__traits(isCopyable, C1));
+static assert(__traits(isCopyable, int));
+static assert(__traits(isCopyable, int[]));
+
+enum E1 : S1 { a = S1(), }
+enum E2 : S2 { a = S2(), }
+enum E3 : S3 { a = S3(), }
+enum E4 : S4 { a = S4(), }
+
+static assert(__traits(isCopyable, E1));
+static assert(__traits(isCopyable, E2));
+static assert(!__traits(isCopyable, E3));
+static assert(!__traits(isCopyable, E4));
+
+struct S5
+{
+ @disable this(ref S5);
+}
+static assert(!__traits(isCopyable, S5));
+
+/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20884
+
+struct S20884
+{
+ int x;
+}
+
+alias T20884 = immutable(S20884);
+enum m20884 = "x";
+
+static assert(is(typeof(__traits(getMember, T20884, m20884)) == immutable(int))); // OK now
+static assert(is( typeof(mixin("T20884." ~ m20884)) == immutable(int)));
+static assert(is( typeof(T20884.x) == immutable(int)));
+
+/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20761
+
+alias Seq(T...) = T;
+
+static assert(__traits(isSame, Seq!(1, 2), Seq!(1, 2)));
+static assert(!__traits(isSame, Seq!(1, 1), Seq!(2, 2)));
+static assert(!__traits(isSame, Seq!(1, 1, 2), Seq!(1, 1)));
+static assert(!__traits(isSame, Seq!(1, 1), Seq!(1, 1, 2)));
+
+static assert(__traits(isSame,
+ Seq!(string, wstring),
+ Seq!(immutable(char)[], immutable(wchar)[]))
+);
+
+static assert(__traits(isSame,
+ Seq!(i => i.value, (a, b) => a + b),
+ Seq!(a => a.value, (x, y) => x + y)
+));
+
+static assert(__traits(isSame,
+ Seq!(float, Seq!(double, Seq!real)),
+ Seq!(Seq!(Seq!float, double), real)
+));
+
+static assert(!__traits(isSame,
+ Seq!(int, Seq!(a => a + a)),
+ Seq!(int, Seq!(a => a * a))
+));
+
+// Do these out of order to ensure there are no forward refencing bugs
+
+extern(C++, __traits(getCppNamespaces,GetNamespaceTest1)) struct GetNamespaceTest4 {}
+static assert (__traits(getCppNamespaces,GetNamespaceTest1) ==
+ __traits(getCppNamespaces,GetNamespaceTest4));
+
+extern(C++, "ns") struct GetNamespaceTest1 {}
+extern(C++, "multiple", "namespaces") struct GetNamespaceTest2 {}
+extern(C++, mixin("Seq!(`ns`, `nt`)")) struct GetNamespaceTest3 {}
+static assert(__traits(getCppNamespaces,GetNamespaceTest1)[0] == "ns");
+static assert(__traits(getCppNamespaces,GetNamespaceTest2) == Seq!("multiple","namespaces"));
+static assert(__traits(getCppNamespaces,GetNamespaceTest3) == Seq!("ns", "nt"));
+
+extern(C++, __traits(getCppNamespaces,GetNamespaceTest5)) struct GetNamespaceTest8 {}
+static assert (__traits(getCppNamespaces,GetNamespaceTest5) ==
+ __traits(getCppNamespaces,GetNamespaceTest8));
+
+extern(C++, ns) struct GetNamespaceTest5 {}
+extern(C++, multiple) extern(C++, namespaces) struct GetNamespaceTest6 {}
+static assert(__traits(getCppNamespaces,GetNamespaceTest5)[0] == "ns");
+static assert(__traits(getCppNamespaces,GetNamespaceTest6) == Seq!("multiple","namespaces"));
+
+extern(C++, NS)
+{
+ struct GetNamespaceTest9 {}
+ extern(C++, nested)
+ {
+ struct GetNamespaceTest10 {}
+ extern(C++,"nested2")
+ struct GetNamespaceTest11 {}
+ }
+ extern (C++, "nested3")
+ {
+ extern(C++, nested4)
+ struct GetNamespaceTest12 {}
+ }
+}
+static assert (__traits(getCppNamespaces,NS.GetNamespaceTest9)[0] == "NS");
+static assert (__traits(getCppNamespaces,NS.GetNamespaceTest10) == Seq!("NS", "nested"));
+static assert (__traits(getCppNamespaces,NS.GetNamespaceTest11) == Seq!("NS", "nested", "nested2"));
+static assert (__traits(getCppNamespaces,NS.GetNamespaceTest12) == Seq!("NS", "nested4", "nested3"));
+
+extern(C++, `ns`) struct GetNamespaceTestTemplated(T) {}
+extern(C++, `ns`)
+template GetNamespaceTestTemplated2(T)
+{
+ struct GetNamespaceTestTemplated2 {}
+}
+
+template GetNamespaceTestTemplated3(T)
+{
+ extern(C++, `ns`)
+ struct GetNamespaceTestTemplated3 {}
+}
+
+static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated!int) == Seq!("ns"));
+static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated2!int) == Seq!("ns"));
+static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated3!int) == Seq!("ns"));
+extern(C++, `ns2`)
+template GetNamespaceTestTemplated4(T)
+{
+ extern(C++, `ns`)
+ struct GetNamespaceTestTemplated4
+ {
+ struct GetNamespaceTestTemplated5 {}
+ struct GetNamespaceTestTemplated6(T) {}
+ }
+}
+
+static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated4!int) == Seq!("ns2","ns"));
+static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated4!int.GetNamespaceTestTemplated5) == Seq!("ns2","ns"));
+static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated4!int.GetNamespaceTestTemplated6!int) == Seq!("ns2","ns"));
+
+// Currently ignored due to https://issues.dlang.org/show_bug.cgi?id=21373
+extern(C++, `decl`)
+mixin template GetNamespaceTestTemplatedMixin()
+{
+ extern(C++, `f`)
+ void foo() {}
+}
+
+extern(C++, `inst`)
+mixin GetNamespaceTestTemplatedMixin!() GNTT;
+
+static assert (__traits(getCppNamespaces, GNTT.foo) == Seq!(`inst`,/*`decl`,*/ `f`));
diff --git a/gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d b/gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d
new file mode 100644
index 00000000000..1f25b269053
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d
@@ -0,0 +1,120 @@
+
+module traits_getFunctionAttributes;
+
+void test_getFunctionAttributes()
+{
+ alias tuple(T...) = T;
+
+ struct S
+ {
+ int noF() { return 0; }
+ int constF() const { return 0; }
+ int immutableF() immutable { return 0; }
+ int inoutF() inout { return 0; }
+ int sharedF() shared { return 0; }
+
+ int x;
+ ref int refF() return { return x; }
+ int propertyF() @property { return 0; }
+ int nothrowF() nothrow { return 0; }
+ int nogcF() @nogc { return 0; }
+
+ int systemF() @system { return 0; }
+ int trustedF() @trusted { return 0; }
+ int safeF() @safe { return 0; }
+
+ int pureF() pure { return 0; }
+
+ int liveF() @live { return 0; }
+ }
+
+ static assert(__traits(getFunctionAttributes, S.noF) == tuple!("@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.noF)) == tuple!("@system"));
+
+ static assert(__traits(getFunctionAttributes, S.constF) == tuple!("const", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.constF)) == tuple!("const", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S.immutableF) == tuple!("immutable", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.immutableF)) == tuple!("immutable", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S.inoutF) == tuple!("inout", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.inoutF)) == tuple!("inout", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S.sharedF) == tuple!("shared", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.sharedF)) == tuple!("shared", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S.refF) == tuple!("ref", "return", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.refF)) == tuple!("ref", "return", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S.propertyF) == tuple!("@property", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(&S.propertyF)) == tuple!("@property", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S.nothrowF) == tuple!("nothrow", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.nothrowF)) == tuple!("nothrow", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S.nogcF) == tuple!("@nogc", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.nogcF)) == tuple!("@nogc", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S.systemF) == tuple!("@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.systemF)) == tuple!("@system"));
+
+ static assert(__traits(getFunctionAttributes, S.trustedF) == tuple!("@trusted"));
+ static assert(__traits(getFunctionAttributes, typeof(S.trustedF)) == tuple!("@trusted"));
+
+ static assert(__traits(getFunctionAttributes, S.safeF) == tuple!("@safe"));
+ static assert(__traits(getFunctionAttributes, typeof(S.safeF)) == tuple!("@safe"));
+
+ static assert(__traits(getFunctionAttributes, S.pureF) == tuple!("pure", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.pureF)) == tuple!("pure", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S.liveF) == tuple!("@live", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S.liveF)) == tuple!("@live", "@system"));
+
+ int pure_nothrow() nothrow pure { return 0; }
+ static ref int static_ref_property() @property { return *(new int); }
+ ref int ref_property() @property { return *(new int); }
+ void safe_nothrow() @safe nothrow { }
+ void live_nothrow() nothrow @live { }
+
+ static assert(__traits(getFunctionAttributes, pure_nothrow) == tuple!("pure", "nothrow", "@nogc", "@safe"));
+ static assert(__traits(getFunctionAttributes, typeof(pure_nothrow)) == tuple!("pure", "nothrow", "@nogc", "@safe"));
+
+ static assert(__traits(getFunctionAttributes, static_ref_property) == tuple!("pure", "nothrow", "@property", "ref", "@safe"));
+ static assert(__traits(getFunctionAttributes, typeof(&static_ref_property)) == tuple!("pure", "nothrow", "@property", "ref", "@safe"));
+
+ static assert(__traits(getFunctionAttributes, ref_property) == tuple!("pure", "nothrow", "@property", "ref", "@safe"));
+ static assert(__traits(getFunctionAttributes, typeof(&ref_property)) == tuple!("pure", "nothrow", "@property", "ref", "@safe"));
+
+ static assert(__traits(getFunctionAttributes, safe_nothrow) == tuple!("pure", "nothrow", "@nogc", "@safe"));
+ static assert(__traits(getFunctionAttributes, typeof(safe_nothrow)) == tuple!("pure", "nothrow", "@nogc", "@safe"));
+
+ static assert(__traits(getFunctionAttributes, live_nothrow) == tuple!("pure", "nothrow", "@nogc", "@live", "@safe"));
+ static assert(__traits(getFunctionAttributes, typeof(live_nothrow)) == tuple!("pure", "nothrow", "@nogc", "@live", "@safe"));
+
+ struct S2
+ {
+ int pure_const() const pure { return 0; }
+ int pure_sharedconst() const shared pure { return 0; }
+ }
+
+ static assert(__traits(getFunctionAttributes, S2.pure_const) == tuple!("const", "pure", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S2.pure_const)) == tuple!("const", "pure", "@system"));
+
+ static assert(__traits(getFunctionAttributes, S2.pure_sharedconst) == tuple!("const", "shared", "pure", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(S2.pure_sharedconst)) == tuple!("const", "shared", "pure", "@system"));
+
+ static assert(__traits(getFunctionAttributes, (int a) { }) == tuple!("pure", "nothrow", "@nogc", "@safe"));
+ static assert(__traits(getFunctionAttributes, typeof((int a) { })) == tuple!("pure", "nothrow", "@nogc", "@safe"));
+
+ auto safeDel = delegate() @safe { };
+ static assert(__traits(getFunctionAttributes, safeDel) == tuple!("pure", "nothrow", "@nogc", "@safe"));
+ static assert(__traits(getFunctionAttributes, typeof(safeDel)) == tuple!("pure", "nothrow", "@nogc", "@safe"));
+
+ auto trustedDel = delegate() @trusted { };
+ static assert(__traits(getFunctionAttributes, trustedDel) == tuple!("pure", "nothrow", "@nogc", "@trusted"));
+ static assert(__traits(getFunctionAttributes, typeof(trustedDel)) == tuple!("pure", "nothrow", "@nogc", "@trusted"));
+
+ auto systemDel = delegate() @system { };
+ static assert(__traits(getFunctionAttributes, systemDel) == tuple!("pure", "nothrow", "@nogc", "@system"));
+ static assert(__traits(getFunctionAttributes, typeof(systemDel)) == tuple!("pure", "nothrow", "@nogc", "@system"));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/typeid_name.d b/gcc/testsuite/gdc.test/compilable/typeid_name.d
new file mode 100644
index 00000000000..e77d5c80a75
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/typeid_name.d
@@ -0,0 +1,14 @@
+
+
+string classname(Object o)
+{
+ return typeid(o).name;
+}
+
+class Panzer {}
+class Tiger : Panzer {}
+
+static assert (() {
+ Panzer p = new Tiger(); return classname(p);
+} () == "Tiger");
+
diff --git a/gcc/testsuite/gdc.test/compilable/uda.d b/gcc/testsuite/gdc.test/compilable/uda.d
index cb9413b06f6..ac66c2fbd2a 100644
--- a/gcc/testsuite/gdc.test/compilable/uda.d
+++ b/gcc/testsuite/gdc.test/compilable/uda.d
@@ -1,5 +1,6 @@
/************************************************/
-// 15180: [REG2.069.0-b1] Segfault with empty struct used as UDA
+// https://issues.dlang.org/show_bug.cgi?id=15180
+// [REG2.069.0-b1] Segfault with empty struct used as UDA
struct foo { }
@foo bar () { }
diff --git a/gcc/testsuite/gdc.test/compilable/udamodule1.d b/gcc/testsuite/gdc.test/compilable/udamodule1.d
index 4631642afb3..434cf5125b7 100644
--- a/gcc/testsuite/gdc.test/compilable/udamodule1.d
+++ b/gcc/testsuite/gdc.test/compilable/udamodule1.d
@@ -1,9 +1,10 @@
// REQUIRED_ARGS:
// PERMUTE_ARGS:
+// EXTRA_FILES: imports/udamodule1.d
/*
TEST_OUTPUT:
---
-compilable/udamodule1.d(9): Deprecation: module imports.udamodule1 is deprecated - This module will be removed.
+compilable/udamodule1.d(10): Deprecation: module `imports.udamodule1` is deprecated - This module will be removed.
---
*/
import imports.udamodule1;
diff --git a/gcc/testsuite/gdc.test/compilable/udamodule2.d b/gcc/testsuite/gdc.test/compilable/udamodule2.d
index f2c0794e582..5002ae02986 100644
--- a/gcc/testsuite/gdc.test/compilable/udamodule2.d
+++ b/gcc/testsuite/gdc.test/compilable/udamodule2.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/udamodule2.d imports/udamodule2a.d
import imports.udamodule2;
import imports.udamodule2a;
diff --git a/gcc/testsuite/gdc.test/compilable/union_initialization.d b/gcc/testsuite/gdc.test/compilable/union_initialization.d
new file mode 100644
index 00000000000..96c2bf45999
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/union_initialization.d
@@ -0,0 +1,43 @@
+// https://issues.dlang.org/show_bug.cgi?id=20068
+
+union B
+{
+ int i;
+ int* p;
+ @safe this(int* p)
+ {
+ // Error: cannot access pointers in @safe code that overlap other fields
+ this.p = p;
+ }
+}
+
+/**************************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=21229
+
+struct NeedsInit
+{
+ int var;
+ @disable this();
+}
+
+union Union
+{
+ NeedsInit ni;
+}
+
+union Proxy
+{
+ Union union_;
+}
+
+struct S
+{
+ Union union_;
+ Proxy proxy;
+
+ this(NeedsInit arg)
+ {
+ union_.ni = arg;
+ proxy.union_.ni = arg;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/vcg-ast.d b/gcc/testsuite/gdc.test/compilable/vcg-ast.d
index 7cc376fbec5..61767cc60d6 100644
--- a/gcc/testsuite/gdc.test/compilable/vcg-ast.d
+++ b/gcc/testsuite/gdc.test/compilable/vcg-ast.d
@@ -1,6 +1,11 @@
+/*
+REQUIRED_ARGS: -vcg-ast -o-
+PERMUTE_ARGS:
+OUTPUT_FILES: compilable/vcg-ast.d.cg
+TEST_OUTPUT_FILE: extra-files/vcg-ast.d.cg
+*/
+
module vcg;
-// REQUIRED_ARGS: -vcg-ast -o-
-// PERMUTE_ARGS:
template Seq(A...)
{
@@ -41,3 +46,19 @@ class C
return 2;
}
}
+
+enum __c_wchar_t : dchar;
+alias wchar_t = __c_wchar_t;
+
+T[] values(T)()
+{
+ T[] values;
+ values ~= T();
+ return values;
+}
+
+void main()
+{
+ values!wchar_t;
+}
+
diff --git a/gcc/testsuite/gdc.test/compilable/version.d b/gcc/testsuite/gdc.test/compilable/version.d
new file mode 100644
index 00000000000..00c5c8084ea
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/version.d
@@ -0,0 +1,23 @@
+/* REQUIRED_ARGS:
+*/
+
+version (D_ModuleInfo)
+{ }
+else
+{
+ static assert(0);
+}
+
+version (D_Exceptions)
+{ }
+else
+{
+ static assert(0);
+}
+
+version (D_TypeInfo)
+{ }
+else
+{
+ static assert(0);
+} \ No newline at end of file
diff --git a/gcc/testsuite/gdc.test/compilable/vgc1.d b/gcc/testsuite/gdc.test/compilable/vgc1.d
index 87b35a41cbc..8a11657ac3d 100644
--- a/gcc/testsuite/gdc.test/compilable/vgc1.d
+++ b/gcc/testsuite/gdc.test/compilable/vgc1.d
@@ -6,19 +6,17 @@
struct S1 { }
struct S2 { this(int); }
struct S3 { this(int) @nogc; }
-struct S4 { new(size_t); }
-struct S5 { @nogc new(size_t); }
/*
TEST_OUTPUT:
---
-compilable/vgc1.d(27): vgc: 'new' causes GC allocation
-compilable/vgc1.d(29): vgc: 'new' causes GC allocation
-compilable/vgc1.d(30): vgc: 'new' causes GC allocation
-compilable/vgc1.d(32): vgc: 'new' causes GC allocation
-compilable/vgc1.d(33): vgc: 'new' causes GC allocation
-compilable/vgc1.d(34): vgc: 'new' causes GC allocation
-compilable/vgc1.d(38): vgc: 'new' causes GC allocation
+compilable/vgc1.d(25): vgc: `new` causes a GC allocation
+compilable/vgc1.d(27): vgc: `new` causes a GC allocation
+compilable/vgc1.d(28): vgc: `new` causes a GC allocation
+compilable/vgc1.d(30): vgc: `new` causes a GC allocation
+compilable/vgc1.d(31): vgc: `new` causes a GC allocation
+compilable/vgc1.d(32): vgc: `new` causes a GC allocation
+compilable/vgc1.d(34): vgc: `new` causes a GC allocation
---
*/
@@ -32,8 +30,6 @@ void testNew()
S1* ps1 = new S1();
S2* ps2 = new S2(1);
S3* ps3 = new S3(1);
- S4* ps4 = new S4; // no error
- S5* ps5 = new S5; // no error
Object o1 = new Object();
}
@@ -41,12 +37,12 @@ void testNew()
/*
TEST_OUTPUT:
---
-compilable/vgc1.d(55): vgc: 'new' causes GC allocation
-compilable/vgc1.d(57): vgc: 'new' causes GC allocation
-compilable/vgc1.d(58): vgc: 'new' causes GC allocation
-compilable/vgc1.d(60): vgc: 'new' causes GC allocation
-compilable/vgc1.d(61): vgc: 'new' causes GC allocation
-compilable/vgc1.d(62): vgc: 'new' causes GC allocation
+compilable/vgc1.d(51): vgc: `new` causes a GC allocation
+compilable/vgc1.d(53): vgc: `new` causes a GC allocation
+compilable/vgc1.d(54): vgc: `new` causes a GC allocation
+compilable/vgc1.d(56): vgc: `new` causes a GC allocation
+compilable/vgc1.d(57): vgc: `new` causes a GC allocation
+compilable/vgc1.d(58): vgc: `new` causes a GC allocation
---
*/
@@ -60,8 +56,6 @@ void testNewScope()
scope S1* ps1 = new S1();
scope S2* ps2 = new S2(1);
scope S3* ps3 = new S3(1);
- scope S4* ps4 = new S4; // no error
- scope S5* ps5 = new S5; // no error
scope Object o1 = new Object(); // no error
scope o2 = new Object(); // no error
@@ -74,9 +68,12 @@ void testNewScope()
/*
TEST_OUTPUT:
---
-compilable/vgc1.d(84): vgc: 'delete' requires GC
-compilable/vgc1.d(85): vgc: 'delete' requires GC
-compilable/vgc1.d(86): vgc: 'delete' requires GC
+compilable/vgc1.d(81): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+compilable/vgc1.d(81): vgc: `delete` requires the GC
+compilable/vgc1.d(82): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+compilable/vgc1.d(82): vgc: `delete` requires the GC
+compilable/vgc1.d(83): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+compilable/vgc1.d(83): vgc: `delete` requires the GC
---
*/
void testDelete(int* p, Object o, S1* s)
diff --git a/gcc/testsuite/gdc.test/compilable/vgc2.d b/gcc/testsuite/gdc.test/compilable/vgc2.d
index b1a7f187a23..c8959140da0 100644
--- a/gcc/testsuite/gdc.test/compilable/vgc2.d
+++ b/gcc/testsuite/gdc.test/compilable/vgc2.d
@@ -6,14 +6,14 @@
/*
TEST_OUTPUT:
---
-compilable/vgc2.d(21): vgc: operator ~ may cause GC allocation
-compilable/vgc2.d(22): vgc: operator ~ may cause GC allocation
-compilable/vgc2.d(23): vgc: operator ~ may cause GC allocation
-compilable/vgc2.d(25): vgc: operator ~ may cause GC allocation
-compilable/vgc2.d(26): vgc: operator ~ may cause GC allocation
-compilable/vgc2.d(27): vgc: operator ~ may cause GC allocation
-compilable/vgc2.d(28): vgc: operator ~ may cause GC allocation
-compilable/vgc2.d(29): vgc: operator ~ may cause GC allocation
+compilable/vgc2.d(21): vgc: operator `~` may cause a GC allocation
+compilable/vgc2.d(22): vgc: operator `~` may cause a GC allocation
+compilable/vgc2.d(23): vgc: operator `~` may cause a GC allocation
+compilable/vgc2.d(25): vgc: operator `~` may cause a GC allocation
+compilable/vgc2.d(26): vgc: operator `~` may cause a GC allocation
+compilable/vgc2.d(27): vgc: operator `~` may cause a GC allocation
+compilable/vgc2.d(28): vgc: operator `~` may cause a GC allocation
+compilable/vgc2.d(29): vgc: operator `~` may cause a GC allocation
---
*/
void testCat(int[] a, string s)
@@ -38,9 +38,9 @@ void testCat(int[] a, string s)
/*
TEST_OUTPUT:
---
-compilable/vgc2.d(48): vgc: operator ~= may cause GC allocation
-compilable/vgc2.d(50): vgc: operator ~= may cause GC allocation
-compilable/vgc2.d(51): vgc: operator ~= may cause GC allocation
+compilable/vgc2.d(48): vgc: operator `~=` may cause a GC allocation
+compilable/vgc2.d(50): vgc: operator `~=` may cause a GC allocation
+compilable/vgc2.d(51): vgc: operator `~=` may cause a GC allocation
---
*/
void testCatAssign(int[] a, string s)
@@ -58,8 +58,8 @@ int* barA();
/*
TEST_OUTPUT:
---
-compilable/vgc2.d(70): vgc: array literal may cause GC allocation
-compilable/vgc2.d(71): vgc: array literal may cause GC allocation
+compilable/vgc2.d(70): vgc: array literal may cause a GC allocation
+compilable/vgc2.d(71): vgc: array literal may cause a GC allocation
---
*/
void testArray()
@@ -76,8 +76,8 @@ void testArray()
/*
TEST_OUTPUT:
---
-compilable/vgc2.d(87): vgc: associative array literal may cause GC allocation
-compilable/vgc2.d(88): vgc: associative array literal may cause GC allocation
+compilable/vgc2.d(87): vgc: associative array literal may cause a GC allocation
+compilable/vgc2.d(88): vgc: associative array literal may cause a GC allocation
---
*/
void testAssocArray()
@@ -93,8 +93,8 @@ void testAssocArray()
/*
TEST_OUTPUT:
---
-compilable/vgc2.d(102): vgc: indexing an associative array may cause GC allocation
-compilable/vgc2.d(103): vgc: indexing an associative array may cause GC allocation
+compilable/vgc2.d(102): vgc: indexing an associative array may cause a GC allocation
+compilable/vgc2.d(103): vgc: indexing an associative array may cause a GC allocation
---
*/
void testIndex(int[int] aa)
diff --git a/gcc/testsuite/gdc.test/compilable/vgc3.d b/gcc/testsuite/gdc.test/compilable/vgc3.d
index 4bf889ce997..efdc5cd5eb3 100644
--- a/gcc/testsuite/gdc.test/compilable/vgc3.d
+++ b/gcc/testsuite/gdc.test/compilable/vgc3.d
@@ -6,9 +6,9 @@
/*
TEST_OUTPUT:
---
-compilable/vgc3.d(16): vgc: setting 'length' may cause GC allocation
-compilable/vgc3.d(17): vgc: setting 'length' may cause GC allocation
-compilable/vgc3.d(18): vgc: setting 'length' may cause GC allocation
+compilable/vgc3.d(16): vgc: setting `length` may cause a GC allocation
+compilable/vgc3.d(17): vgc: setting `length` may cause a GC allocation
+compilable/vgc3.d(18): vgc: setting `length` may cause a GC allocation
---
*/
void testArrayLength(int[] a)
diff --git a/gcc/testsuite/gdc.test/compilable/vtemplates.d b/gcc/testsuite/gdc.test/compilable/vtemplates.d
new file mode 100644
index 00000000000..578d6c08c7a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/vtemplates.d
@@ -0,0 +1,28 @@
+/* REQUIRED_ARGS: -vtemplates
+TEST_OUTPUT:
+---
+compilable/vtemplates.d(10): vtemplate: 4 (3 distinct) instantiation(s) of template `foo(int I)()` found
+compilable/vtemplates.d(11): vtemplate: 5 (2 distinct) instantiation(s) of template `goo1(int I)()` found
+compilable/vtemplates.d(12): vtemplate: 3 (2 distinct) instantiation(s) of template `goo2(int I)()` found
+---
+*/
+
+void foo(int I)() { }
+void goo1(int I)() { }
+void goo2(int I)() { goo1!(I); }
+
+void test()
+{
+ foo!(1)();
+ foo!(1)();
+ foo!(2)();
+ foo!(3)();
+
+ goo1!(1)();
+ goo1!(1)();
+ goo1!(2)();
+
+ goo2!(1)();
+ goo2!(2)();
+ goo2!(2)();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/vtemplates_list.d b/gcc/testsuite/gdc.test/compilable/vtemplates_list.d
new file mode 100644
index 00000000000..dd02e630d98
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/vtemplates_list.d
@@ -0,0 +1,46 @@
+/* REQUIRED_ARGS: -vtemplates=list-instances
+TEST_OUTPUT:
+---
+compilable/vtemplates_list.d(19): vtemplate: 4 (3 distinct) instantiation(s) of template `foo(int I)()` found, they are:
+compilable/vtemplates_list.d(25): vtemplate: explicit instance `foo!1`
+compilable/vtemplates_list.d(26): vtemplate: explicit instance `foo!1`
+compilable/vtemplates_list.d(27): vtemplate: explicit instance `foo!2`
+compilable/vtemplates_list.d(28): vtemplate: explicit instance `foo!3`
+compilable/vtemplates_list.d(20): vtemplate: 3 (1 distinct) instantiation(s) of template `goo1(int I)()` found, they are:
+compilable/vtemplates_list.d(30): vtemplate: explicit instance `goo1!1`
+compilable/vtemplates_list.d(31): vtemplate: explicit instance `goo1!1`
+compilable/vtemplates_list.d(21): vtemplate: implicit instance `goo1!1`
+compilable/vtemplates_list.d(21): vtemplate: 2 (1 distinct) instantiation(s) of template `goo2(int I)()` found, they are:
+compilable/vtemplates_list.d(33): vtemplate: explicit instance `goo2!1`
+compilable/vtemplates_list.d(34): vtemplate: explicit instance `goo2!1`
+compilable/vtemplates_list.d(52): vtemplate: 1 (1 distinct) instantiation(s) of template `A()` found, they are:
+compilable/vtemplates_list.d-mixin-53(53): vtemplate: explicit instance `A!()`
+---
+*/
+
+#line 19
+void foo(int I)() { }
+void goo1(int I)() { }
+void goo2(int I)() { goo1!(I); }
+
+void test()
+{
+ foo!(1)();
+ foo!(1)();
+ foo!(2)();
+ foo!(3)();
+
+ goo1!(1)();
+ goo1!(1)();
+
+ goo2!(1)();
+ goo2!(1)();
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21489
+#line 50
+void test2()
+{
+ template A() {}
+ alias ta = mixin("A!()");
+}
diff --git a/gcc/testsuite/gdc.test/compilable/warn3882.d b/gcc/testsuite/gdc.test/compilable/warn3882.d
index d981a47acdf..f02a87bd04b 100644
--- a/gcc/testsuite/gdc.test/compilable/warn3882.d
+++ b/gcc/testsuite/gdc.test/compilable/warn3882.d
@@ -10,7 +10,7 @@ void test3882()
}
/******************************************/
-// 12619
+// https://issues.dlang.org/show_bug.cgi?id=12619
extern (C) @system nothrow pure void* memcpy(void* s1, in void* s2, size_t n);
// -> weakly pure
@@ -22,7 +22,7 @@ void test12619() pure
}
/******************************************/
-// 12760
+// https://issues.dlang.org/show_bug.cgi?id=12760
struct S12760(T)
{
@@ -41,11 +41,11 @@ struct K12760
}
/******************************************/
-// 12909
+// https://issues.dlang.org/show_bug.cgi?id=12909
int f12909(immutable(int[])[int] aa) pure nothrow
{
- //aa[0] = []; // fix for issue 13701
+ //aa[0] = []; // fix for https://issues.dlang.org/show_bug.cgi?id=13701
return 0;
}
@@ -60,7 +60,7 @@ void test12909()
}
/******************************************/
-// 13899
+// https://issues.dlang.org/show_bug.cgi?id=13899
const struct Foo13899
{
diff --git a/gcc/testsuite/gdc.test/compilable/zerosize.d b/gcc/testsuite/gdc.test/compilable/zerosize.d
new file mode 100644
index 00000000000..3c0062ead30
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/zerosize.d
@@ -0,0 +1,12 @@
+
+extern (C) struct S { }
+
+static assert(S.sizeof == 0);
+static assert(S.alignof == 1);
+
+extern (C++) struct T { }
+
+static assert(T.sizeof == 1);
+static assert(T.alignof == 1);
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/aacmp10381.d b/gcc/testsuite/gdc.test/fail_compilation/aacmp10381.d
index 397f36d0346..b0888911951 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/aacmp10381.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/aacmp10381.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/aacmp10381.d(12): Error: > is not defined for associative arrays
+fail_compilation/aacmp10381.d(12): Error: `>` is not defined for associative arrays
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/aliasassign.d b/gcc/testsuite/gdc.test/fail_compilation/aliasassign.d
new file mode 100644
index 00000000000..499bef1e712
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/aliasassign.d
@@ -0,0 +1,21 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/aliasassign.d(13): Error: `B` must have same parent `Swap!(int, string)` as alias `B`
+fail_compilation/aliasassign.d(14): Error: `A` must have same parent `Swap!(int, string)` as alias `A`
+fail_compilation/aliasassign.d(21): Error: template instance `aliasassign.Swap!(int, string)` error instantiating
+fail_compilation/aliasassign.d(21): while evaluating: `static assert(Swap!(int, string))`
+---
+*/
+
+template Swap (alias A, alias B)
+{
+ alias C = A;
+ B = A;
+ A = B;
+ enum Swap = true;
+}
+
+alias A = int;
+alias B = string;
+
+static assert(Swap!(A, B));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/aliasassign1.d b/gcc/testsuite/gdc.test/fail_compilation/aliasassign1.d
new file mode 100644
index 00000000000..df3f43a18b3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/aliasassign1.d
@@ -0,0 +1,34 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/aliasassign1.d(106): Error: A was read, so cannot reassign
+fail_compilation/aliasassign1.d(110): Error: template instance `aliasassign1.staticMap!(Unqual, int, const(uint))` error instantiating
+fail_compilation/aliasassign1.d(112): Error: static assert: `is(TK == AliasSeq!(int, uint))` is false
+---
+ */
+
+template AliasSeq(T...) { alias AliasSeq = T; }
+
+template Unqual(T)
+{
+ static if (is(T U == const U))
+ alias Unqual = U;
+ else static if (is(T U == immutable U))
+ alias Unqual = U;
+ else
+ alias Unqual = T;
+}
+
+#line 100
+
+template staticMap(alias F, T...)
+{
+ alias A = AliasSeq!();
+ alias B = A;
+ static foreach (t; T)
+ A = AliasSeq!(A, F!t); // what's tested
+ alias staticMap = A;
+}
+
+alias TK = staticMap!(Unqual, int, const uint);
+//pragma(msg, TK);
+static assert(is(TK == AliasSeq!(int, uint)));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/already_defined.d b/gcc/testsuite/gdc.test/fail_compilation/already_defined.d
new file mode 100644
index 00000000000..7a97e09e685
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/already_defined.d
@@ -0,0 +1,76 @@
+/**
+TEST_OUTPUT:
+---
+fail_compilation/already_defined.d(4): Error: declaration `already_defined.func1.a` is already defined
+fail_compilation/already_defined.d(3): `variable` `a` is defined here
+fail_compilation/already_defined.d(10): Error: declaration `already_defined.func2.core` is already defined
+fail_compilation/already_defined.d(9): `import` `core` is defined here
+fail_compilation/already_defined.d(28): Error: declaration `Ident(T)` is already defined
+fail_compilation/already_defined.d(27): `template` `Ident(T)` is defined here
+fail_compilation/already_defined.d(36): Error: declaration `Tstring` is already defined
+fail_compilation/already_defined.d(35): `alias` `Tstring` is defined here
+fail_compilation/already_defined.d(42): Error: declaration `T` is already defined
+fail_compilation/already_defined.d(41): `alias` `T` is defined here
+fail_compilation/already_defined.d(48): Error: declaration `core` is already defined
+fail_compilation/already_defined.d(47): `import` `core` is defined here
+fail_compilation/already_defined.d(54): Error: declaration `core` is already defined
+fail_compilation/already_defined.d(53): `import` `core` is defined here
+---
+*/
+
+#line 1
+void func1 ()
+{
+ int a;
+ bool a;
+}
+
+void func2 ()
+{
+ import core.stdc.stdio;
+ string core;
+}
+
+void func3 ()
+{
+ {
+ import core.stdc.stdio;
+ }
+
+ {
+ // No conflict
+ string core;
+ }
+}
+
+void func4 ()
+{
+ template Ident (T) { alias Ident = T; }
+ template Ident (T) { alias Ident = T; }
+}
+
+void func5 ()
+{
+ template Ident (T) { alias Ident = T; }
+
+ alias Tstring = Ident!string;
+ alias Tstring = Ident!string;
+}
+
+void func6 ()
+{
+ static if (is(int T == int)) {}
+ static if (is(int T == int)) {}
+}
+
+void func7 ()
+{
+ import core.stdc.stdio;
+ static if (is(int core == int)) {}
+}
+
+void func8 ()
+{
+ import core.stdc.stdio;
+ static if (is(string : core[], core)) {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b15069.d b/gcc/testsuite/gdc.test/fail_compilation/b15069.d
new file mode 100644
index 00000000000..4ff42665826
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b15069.d
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/b15069.d(15): Error: template instance `T!int` `T` is not a template declaration, it is a alias
+fail_compilation/b15069.d(10): Error: template instance `b15069.Stuff!(Thing!float)` error instantiating
+---
+*/
+void main()
+{
+ Stuff!(Thing!(float)) s;
+}
+
+struct Stuff(T)
+{
+ T!(int) var;
+}
+
+struct Thing(T)
+{
+ T varling;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b15909.d b/gcc/testsuite/gdc.test/fail_compilation/b15909.d
new file mode 100644
index 00000000000..22c58da610b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b15909.d
@@ -0,0 +1,15 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/b15909.d(12): Error: duplicate `case 'a'` in `switch` statement
+---
+*/
+
+void main()
+{
+ switch ('a')
+ {
+ case 'a':
+ case 'a':
+ break;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b17918.d b/gcc/testsuite/gdc.test/fail_compilation/b17918.d
new file mode 100644
index 00000000000..00f2b76b8c6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b17918.d
@@ -0,0 +1,13 @@
+/*
+EXTRA_FILES: imports/b17918a.d
+TEST_OUTPUT:
+---
+fail_compilation/imports/b17918a.d(7): Error: undefined identifier `_listMap`
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=17918
+import imports.b17918a;
+
+class Derived : Base
+{
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b19523.d b/gcc/testsuite/gdc.test/fail_compilation/b19523.d
new file mode 100644
index 00000000000..36266669a32
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b19523.d
@@ -0,0 +1,18 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/b19523.d(12): Error: undefined identifier `SomeStruct`
+fail_compilation/b19523.d(13): Error: function `b19523.foo(int delegate() arg)` is not callable using argument types `(_error_)`
+fail_compilation/b19523.d(13): cannot pass argument `__lambda2` of type `_error_` to parameter `int delegate() arg`
+---
+*/
+module b19523;
+
+void bar () {
+ SomeStruct s;
+ foo({
+ return s;
+ });
+}
+
+void foo (int delegate() arg) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b19685.d b/gcc/testsuite/gdc.test/fail_compilation/b19685.d
new file mode 100644
index 00000000000..99addd83924
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b19685.d
@@ -0,0 +1,19 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/b19685.d(6): Error: overlapping default initialization for field `b` and `a`
+---
+*/
+struct S
+{
+ union
+ {
+ struct { int a = 123; }
+ struct { int b = 456; }
+ }
+}
+
+void main()
+{
+ S s;
+ assert(s.b == 123);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b19691.d b/gcc/testsuite/gdc.test/fail_compilation/b19691.d
index 8663512fd2a..d01c76490ab 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/b19691.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/b19691.d
@@ -1,8 +1,7 @@
// REQUIRED_ARGS: -de
/* TEST_OUTPUT:
---
-fail_compilation/b19691.d(13): Error: forward reference to template `this`
-fail_compilation/b19691.d(19): Deprecation: constructor `b19691.S2.this` all parameters have default arguments, but structs cannot have default constructors.
+fail_compilation/b19691.d(12): Error: forward reference to template `this`
---
*/
// https://issues.dlang.org/show_bug.cgi?id=19691
@@ -10,11 +9,11 @@ module b19691;
struct S1 {
this(T...)(T) {
- S2("");
+ S2(42, "");
}
}
struct S2 {
- this(string) {}
- this(S1 s = null) {}
+ this(int a, string) {}
+ this(int a, S1 s = null) {}
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b19691e.d b/gcc/testsuite/gdc.test/fail_compilation/b19691e.d
index 21d0e908025..1b8aa1c4f18 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/b19691e.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/b19691e.d
@@ -1,10 +1,9 @@
// REQUIRED_ARGS: -de
/* TEST_OUTPUT:
---
-fail_compilation/b19691e.d(17): Error: forward reference to template `this`
-fail_compilation/b19691e.d(17): Error: constructor `b19691e.S2.this(S1 s = "")` is not callable using argument types `(string)`
-fail_compilation/b19691e.d(17): Error: forward reference to template `this`
-fail_compilation/b19691e.d(23): Deprecation: constructor `b19691e.S2.this` all parameters have default arguments, but structs cannot have default constructors.
+fail_compilation/b19691e.d(16): Error: forward reference to template `this`
+fail_compilation/b19691e.d(16): Error: constructor `b19691e.S2.this(int a, S1 s = "")` is not callable using argument types `(int, string)`
+fail_compilation/b19691e.d(16): Error: forward reference to template `this`
---
*/
// https://issues.dlang.org/show_bug.cgi?id=19691
@@ -14,11 +13,11 @@ struct S1
{
this(T)(T)
{
- S2("");
+ S2(42, "");
}
}
struct S2
{
- this(S1 s = ""){}
+ this(int a, S1 s = ""){}
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b19717a.d b/gcc/testsuite/gdc.test/fail_compilation/b19717a.d
index 79a9de0ad7c..d10b3f94070 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/b19717a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/b19717a.d
@@ -3,9 +3,9 @@
---
fail_compilation/b19717a.d(14): Error: forward reference to template `a`
fail_compilation/b19717a.d(14): Error: forward reference to template `a`
-fail_compilation/b19717a.d(14): Error: none of the overloads of `a` are callable using argument types `()`, candidates are:
-fail_compilation/b19717a.d(13): `b19717a.a(int b)`
-fail_compilation/b19717a.d(14): `b19717a.a(int b = a)`
+fail_compilation/b19717a.d(14): Error: none of the overloads of `a` are callable using argument types `()`
+fail_compilation/b19717a.d(13): Candidates are: `b19717a.a(int b)`
+fail_compilation/b19717a.d(14): `b19717a.a(int b = a)`
---
*/
module b19717a;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b19730.d b/gcc/testsuite/gdc.test/fail_compilation/b19730.d
new file mode 100644
index 00000000000..903d6297889
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b19730.d
@@ -0,0 +1,12 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/b19730.d(10): Error: found `)` while expecting `=` or identifier
+fail_compilation/b19730.d(11): Error: found `)` while expecting `=` or identifier
+---
+*/
+void func() {
+ bool x;
+ if (const x) {}
+ if (auto x) {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b20011.d b/gcc/testsuite/gdc.test/fail_compilation/b20011.d
new file mode 100644
index 00000000000..669dd1073c9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b20011.d
@@ -0,0 +1,40 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/b20011.d(25): Error: `S1(cast(ubyte)0u).member` is not an lvalue and cannot be modified
+fail_compilation/b20011.d(28): Error: `S2(null).member` is not an lvalue and cannot be modified
+fail_compilation/b20011.d(29): Error: `S2(null).member` is not an lvalue and cannot be modified
+fail_compilation/b20011.d(32): Error: `U1(cast(ubyte)0u, ).m2` is not an lvalue and cannot be modified
+fail_compilation/b20011.d(37): Error: function `b20011.main.assignableByRef(ref ubyte p)` is not callable using argument types `(ubyte)`
+fail_compilation/b20011.d(37): cannot pass rvalue argument `S1(cast(ubyte)0u).member` of type `ubyte` to parameter `ref ubyte p`
+fail_compilation/b20011.d(38): Error: function `b20011.main.assignableByOut(out ubyte p)` is not callable using argument types `(ubyte)`
+fail_compilation/b20011.d(38): cannot pass rvalue argument `S1(cast(ubyte)0u).member` of type `ubyte` to parameter `out ubyte p`
+fail_compilation/b20011.d(39): Error: function `b20011.main.assignableByConstRef(ref const(ubyte) p)` is not callable using argument types `(ubyte)`
+fail_compilation/b20011.d(39): cannot pass rvalue argument `S1(cast(ubyte)0u).member` of type `ubyte` to parameter `ref const(ubyte) p`
+---
+*/
+module b20011;
+
+struct S1 { ubyte member; }
+struct S2 { ubyte[] member; }
+union U1 { ubyte m1; int m2; }
+
+void main()
+{
+ enum S1 s1 = {};
+ s1.member = 42;
+
+ enum S2 s2 = {};
+ s2.member = [];
+ s2.member ~= [];
+
+ enum U1 u1 = {m1 : 0};
+ u1.m2 = 42;
+
+ void assignableByRef(ref ubyte p){ p = 42; }
+ void assignableByOut(out ubyte p){ p = 42; }
+ void assignableByConstRef(ref const ubyte p){}
+ assignableByRef(s1.member);
+ assignableByOut(s1.member);
+ assignableByConstRef(s1.member);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b20780.d b/gcc/testsuite/gdc.test/fail_compilation/b20780.d
new file mode 100644
index 00000000000..ec6917bb58a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b20780.d
@@ -0,0 +1,11 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/b20780.d(10): Error: `@identifier` or `@(ArgumentList)` expected, not `@)`
+fail_compilation/b20780.d(11): Error: `@identifier` or `@(ArgumentList)` expected, not `@,`
+fail_compilation/b20780.d(11): Error: basic type expected, not `,`
+---
+*/
+
+void f(@){}
+void g(@,){}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b20875.d b/gcc/testsuite/gdc.test/fail_compilation/b20875.d
new file mode 100644
index 00000000000..8fd85055b30
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b20875.d
@@ -0,0 +1,27 @@
+module b20875;
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/b20875.d(10): Error: template instance `Foo!int` does not match template declaration `Foo(alias T : None!U, U...)`
+fail_compilation/b20875.d(10): while evaluating: `static assert(Foo!int)`
+fail_compilation/b20875.d(11): Error: template instance `Bar!int` does not match template declaration `Bar(alias T : None!U, U...)`
+fail_compilation/b20875.d(11): while evaluating: `static assert(!Bar!int)`
+fail_compilation/b20875.d(14): Error: template parameter specialization for a type must be a type and not `NotAType()`
+fail_compilation/b20875.d(15): while looking for match for `Baz!int`
+fail_compilation/b20875.d(15): while evaluating: `static assert(!Baz!int)`
+---
+*/
+
+#line 7
+
+enum Foo(alias T : None!U, U...) = true;
+enum Bar(alias T : None!U, U...) = false;
+static assert( Foo!(int));
+static assert(!Bar!(int));
+
+template NotAType(){}
+enum Baz(alias T : NotAType) = false;
+static assert(!Baz!(int));
+
+void main(){}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b3841.d b/gcc/testsuite/gdc.test/fail_compilation/b3841.d
index 4a99e9a6963..ceddc87ddd8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/b3841.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/b3841.d
@@ -1,24 +1,25 @@
-// PERMUTE_ARGS:
// REQUIRED_ARGS: -w -o-
/*
TEST_OUTPUT:
---
-fail_compilation/b3841.d-mixin-31(31): Warning: char += float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: int += float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: long += double is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: char -= float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: int -= float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: long -= double is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: char *= float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: int *= float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: long *= double is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: char /= float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: int /= float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: long /= double is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: char %= float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: int %= float is performing truncating conversion
-fail_compilation/b3841.d-mixin-31(31): Warning: long %= double is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `char += float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `int += float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `long += double` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `char -= float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `int -= float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `long -= double` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `char *= float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `int *= float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `long *= double` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `char /= float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `int /= float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `long /= double` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `char %= float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `int %= float` is performing truncating conversion
+fail_compilation/b3841.d-mixin-32(32): Warning: `long %= double` is performing truncating conversion
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
@@ -45,7 +46,6 @@ void main()
f!(op, long, int)();
f!(op, long, short)();
f!(op, float, long)();
- f!(op, cfloat, long)();
f!(op, double, float)();
// Should that really be OK ?
@@ -58,15 +58,6 @@ void main()
f!(op, long, double)();
}
- foreach (string op; Ops!("+=", "-="))
- {
- // OK
- f!(op, idouble, ifloat)();
-
- // Should that really be OK ?
- f!(op, ifloat, idouble)();
- }
-
// OK
f!("^^=", int, int)();
f!("^^=", long, int)();
@@ -75,4 +66,4 @@ void main()
f!("^^=", double, float)();
// Should that really be OK ?
f!("^^=", float, double)();
-} \ No newline at end of file
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/b6227.d b/gcc/testsuite/gdc.test/fail_compilation/b6227.d
new file mode 100644
index 00000000000..a9b2a505b89
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/b6227.d
@@ -0,0 +1,17 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/b6227.d(16): Error: Comparison between different enumeration types `X` and `Y`; If this behavior is intended consider using `std.conv.asOriginalType`
+fail_compilation/b6227.d(16): while evaluating: `static assert(!(X.O != Y.U))`
+fail_compilation/b6227.d(17): Error: Comparison between different enumeration types `X` and `Y`; If this behavior is intended consider using `std.conv.asOriginalType`
+fail_compilation/b6227.d(17): while evaluating: `static assert(X.O == Y.U)`
+---
+*/
+enum X {
+ O,
+ R
+}
+enum Y {
+ U
+}
+static assert(!(X.O != Y.U));
+static assert( (X.O == Y.U));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/betterc.d b/gcc/testsuite/gdc.test/fail_compilation/betterc.d
new file mode 100644
index 00000000000..e1cc4cf04d2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/betterc.d
@@ -0,0 +1,30 @@
+/* REQUIRED_ARGS: -betterC
+ * TEST_OUTPUT:
+---
+fail_compilation/betterc.d(12): Error: Cannot use `throw` statements with -betterC
+fail_compilation/betterc.d(17): Error: Cannot use try-catch statements with -betterC
+fail_compilation/betterc.d(29): Error: `TypeInfo` cannot be used with -betterC
+---
+*/
+
+void test()
+{
+ throw new Exception("msg");
+}
+
+void test2()
+{
+ try
+ {
+ test();
+ }
+ catch (Exception e)
+ {
+ }
+}
+
+void test3()
+{
+ int i;
+ auto ti = typeid(i);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug15613.d b/gcc/testsuite/gdc.test/fail_compilation/bug15613.d
index e8072fdbaf8..5b16f72ca23 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/bug15613.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/bug15613.d
@@ -16,3 +16,18 @@ void main()
f(null);
g(8);
}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/bug15613.d(32): Error: function `bug15613.h(int[]...)` is not callable using argument types `(int, void function(int[]...))`
+fail_compilation/bug15613.d(32): cannot pass argument `& h` of type `void function(int[]...)` to parameter `int[]...`
+---
+*/
+
+void h(int[]...);
+
+void test()
+{
+ h(7, &h);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug16165.d b/gcc/testsuite/gdc.test/fail_compilation/bug16165.d
index fdfbf73f415..1818e0d68f6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/bug16165.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/bug16165.d
@@ -2,9 +2,9 @@ void f(int x, Object y);
void g()
{
- Object o;
- f(o, o, 404);
- f(5, 6, 404);
+ Object o;
+ f(o, o, 404);
+ f(5, 6, 404);
}
/*
diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug18743.d b/gcc/testsuite/gdc.test/fail_compilation/bug18743.d
new file mode 100644
index 00000000000..9d2f8886481
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/bug18743.d
@@ -0,0 +1,22 @@
+// REQUIRED_ARGS:
+/*
+TEST_OUTPUT:
+---
+fail_compilation/bug18743.d(18): Error: `a ? a = 4 : a` must be surrounded by parentheses when next to operator `=`
+fail_compilation/bug18743.d(19): Error: `a ? --a : a` must be surrounded by parentheses when next to operator `+=`
+---
+*/
+
+void main()
+{
+ int a;
+
+ // ok
+ (a ? a = 4 : a) = 5;
+ a ? a = 4 : (a = 5);
+
+ a ? a = 4 : a = 5;
+ a ? --a : a += 1;
+
+ a ? a = 4 : a++; // ok
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug19569.d b/gcc/testsuite/gdc.test/fail_compilation/bug19569.d
new file mode 100644
index 00000000000..a314142a53d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/bug19569.d
@@ -0,0 +1,90 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/bug19569.d(70): Error: `bug19569.test0` called with argument types `()` matches both:
+fail_compilation/bug19569.d(56): `bug19569.test0()`
+and:
+fail_compilation/bug19569.d(57): `bug19569.test0()`
+fail_compilation/bug19569.d(71): Error: `bug19569.test1` called with argument types `()` matches both:
+fail_compilation/bug19569.d(59): `bug19569.test1()`
+and:
+fail_compilation/bug19569.d(60): `bug19569.test1()`
+fail_compilation/bug19569.d(72): Error: `bug19569.test2` called with argument types `()` matches both:
+fail_compilation/bug19569.d(62): `bug19569.test2!().test2()`
+and:
+fail_compilation/bug19569.d(63): `bug19569.test2!().test2()`
+fail_compilation/bug19569.d(73): Error: `bug19569.test3` called with argument types `()` matches both:
+fail_compilation/bug19569.d(65): `bug19569.test3!().test3()`
+and:
+fail_compilation/bug19569.d(66): `bug19569.test3!().test3()`
+fail_compilation/bug19569.d(78): Error: `bug19569.test0` called with argument types `()` matches both:
+fail_compilation/bug19569.d(56): `bug19569.test0()`
+and:
+fail_compilation/bug19569.d(57): `bug19569.test0()`
+fail_compilation/bug19569.d(79): Error: `bug19569.test1` called with argument types `()` matches both:
+fail_compilation/bug19569.d(59): `bug19569.test1()`
+and:
+fail_compilation/bug19569.d(60): `bug19569.test1()`
+fail_compilation/bug19569.d(80): Error: `bug19569.test2` called with argument types `()` matches both:
+fail_compilation/bug19569.d(62): `bug19569.test2!().test2()`
+and:
+fail_compilation/bug19569.d(63): `bug19569.test2!().test2()`
+fail_compilation/bug19569.d(81): Error: `bug19569.test3` called with argument types `()` matches both:
+fail_compilation/bug19569.d(65): `bug19569.test3!().test3()`
+and:
+fail_compilation/bug19569.d(66): `bug19569.test3!().test3()`
+fail_compilation/bug19569.d(86): Error: `bug19569.test0` called with argument types `()` matches both:
+fail_compilation/bug19569.d(56): `bug19569.test0()`
+and:
+fail_compilation/bug19569.d(57): `bug19569.test0()`
+fail_compilation/bug19569.d(87): Error: `bug19569.test1` called with argument types `()` matches both:
+fail_compilation/bug19569.d(59): `bug19569.test1()`
+and:
+fail_compilation/bug19569.d(60): `bug19569.test1()`
+fail_compilation/bug19569.d(88): Error: `bug19569.test2` called with argument types `()` matches both:
+fail_compilation/bug19569.d(62): `bug19569.test2!().test2()`
+and:
+fail_compilation/bug19569.d(63): `bug19569.test2!().test2()`
+fail_compilation/bug19569.d(89): Error: `bug19569.test3` called with argument types `()` matches both:
+fail_compilation/bug19569.d(65): `bug19569.test3!().test3()`
+and:
+fail_compilation/bug19569.d(66): `bug19569.test3!().test3()`
+---
+*/
+
+
+void test0();
+void test0() nothrow;
+
+void test1();
+void test1() @nogc;
+
+void test2()();
+void test2()() nothrow;
+
+void test3()();
+void test3()() @nogc;
+
+void attr0()
+{
+ test0();
+ test1();
+ test2();
+ test3();
+}
+
+void attr1() @nogc
+{
+ test0();
+ test1();
+ test2();
+ test3();
+}
+
+void attr3() nothrow @nogc
+{
+ test0();
+ test1();
+ test2();
+ test3();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug8891.d b/gcc/testsuite/gdc.test/fail_compilation/bug8891.d
index b58256eae2b..e7316c31723 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/bug8891.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/bug8891.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/bug8891.d(21): Error: need 'this' for 'opCall' of type 'S(int n)'
+fail_compilation/bug8891.d(21): Error: need `this` for `opCall` of type `S(int n)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/bug9631.d b/gcc/testsuite/gdc.test/fail_compilation/bug9631.d
index 852eaae7479..33ea0cd7a50 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/bug9631.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/bug9631.d
@@ -23,8 +23,7 @@ void main()
/*
TEST_OUTPUT:
---
-fail_compilation/bug9631.d(41): Error: incompatible types for ((x) == (y)): 'bug9631.S' and 'bug9631.tem!
-).S'
+fail_compilation/bug9631.d(41): Error: incompatible types for `(x) == (y)`: `bug9631.S` and `bug9631.tem!().S`
---
*/
@@ -45,18 +44,30 @@ void equal()
/*
TEST_OUTPUT:
---
-fail_compilation/bug9631.d(79): Error: function `bug9631.arg.f(int i, S s)` is not callable using argumen
- types `(int, S)`
-fail_compilation/bug9631.d(79): cannot pass argument `y` of type `bug9631.tem!().S` to parameter `
-ug9631.S s`
-fail_compilation/bug9631.d(80): Error: function literal `__lambda2(S s)` is not callable using argument t
-pes `(S)`
-fail_compilation/bug9631.d(80): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631
-tem!().S s`
-fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S _param_0)` is not callable using
-argument types `(S)`
-fail_compilation/bug9631.d(86): cannot pass argument `S(0)` of type `bug9631.tem!().S` to paramete
- `bug9631.S _param_0`
+fail_compilation/bug9631.d(55): Error: cannot cast expression `x` of type `bug9631.S` to `bug9631.tem!().S` because of different sizes
+fail_compilation/bug9631.d(58): Error: cannot cast expression `ta` of type `bug9631.tem!().S[1]` to `bug9631.S[1]` because of different sizes
+fail_compilation/bug9631.d(59): Error: cannot cast expression `sa` of type `S[1]` to `S[]` since sizes don't line up
+---
+*/
+void test3()
+{
+ S x;
+ auto y = cast(tem!().S)x;
+
+ tem!().S[1] ta;
+ S[1] sa = cast(S[1])ta;
+ auto t2 = cast(tem!().S[])sa;
+}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/bug9631.d(79): Error: function `bug9631.arg.f(int i, S s)` is not callable using argument types `(int, S)`
+fail_compilation/bug9631.d(79): cannot pass argument `y` of type `bug9631.tem!().S` to parameter `bug9631.S s`
+fail_compilation/bug9631.d(80): Error: function literal `__lambda4(S s)` is not callable using argument types `(S)`
+fail_compilation/bug9631.d(80): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S s`
+fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S _param_0)` is not callable using argument types `(S)`
+fail_compilation/bug9631.d(86): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S _param_0`
---
*/
void arg()
@@ -80,10 +91,10 @@ TEST_OUTPUT:
---
fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S _param_0)` is not callable using argument types `(S)`
fail_compilation/bug9631.d(106): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S _param_0`
-fail_compilation/bug9631.d(107): Error: template `bug9631.targ.ft` cannot deduce function from argument types `!()(S)`, candidates are:
-fail_compilation/bug9631.d(105): `bug9631.targ.ft()(tem!().S)`
-fail_compilation/bug9631.d(109): Error: template `bug9631.targ.ft2` cannot deduce function from argument types `!()(S, int)`, candidates are:
-fail_compilation/bug9631.d(108): `bug9631.targ.ft2(T)(S, T)`
+fail_compilation/bug9631.d(107): Error: template `bug9631.targ.ft` cannot deduce function from argument types `!()(S)`
+fail_compilation/bug9631.d(105): Candidate is: `ft()(tem!().S)`
+fail_compilation/bug9631.d(109): Error: template `bug9631.targ.ft2` cannot deduce function from argument types `!()(S, int)`
+fail_compilation/bug9631.d(108): Candidate is: `ft2(T)(S, T)`
---
*/
void targ()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ccast.d b/gcc/testsuite/gdc.test/fail_compilation/ccast.d
new file mode 100644
index 00000000000..b4897d469f3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ccast.d
@@ -0,0 +1,9 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ccast.d(9): Error: C style cast illegal, use `cast(byte)i`
+---
+*/
+
+int i;
+byte b = (byte)i;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/checkimports2.d b/gcc/testsuite/gdc.test/fail_compilation/checkimports2.d
new file mode 100644
index 00000000000..44c237f144f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/checkimports2.d
@@ -0,0 +1,30 @@
+// EXTRA_FILES: imports/imp1.d imports/imp2.d
+/*
+TEST_OUTPUT:
+---
+fail_compilation/checkimports2.d(25): Error: no property `X` for type `checkimports2.B`, did you mean `imports.imp2.X`?
+fail_compilation/checkimports2.d(25): while evaluating: `static assert((B).X == 0)`
+fail_compilation/checkimports2.d(26): Error: no property `Y` for type `checkimports2.B`, did you mean `imports.imp2.Y`?
+fail_compilation/checkimports2.d(26): while evaluating: `static assert((B).Y == 2)`
+---
+*/
+
+import imports.imp1;
+
+enum X = 0;
+
+class B
+{
+ import imports.imp2;
+ static assert(X == 0); // imp2.X --> .X
+ int[Y] aa; // imp2.Y
+}
+
+class C : B
+{
+ static assert(B.X == 0); // imp2.X --> error
+ static assert(B.Y == 2); // imp2.Y --> error
+
+ static assert(X == 0); // imp2.X --> .X
+ static assert(Y == 1); // imp2.Y --> imp1.Y
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/checkimports2a.d b/gcc/testsuite/gdc.test/fail_compilation/checkimports2a.d
deleted file mode 100644
index abec4242eec..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/checkimports2a.d
+++ /dev/null
@@ -1,32 +0,0 @@
-// REQUIRED_ARGS:
-/*
-TEST_OUTPUT:
----
-fail_compilation/checkimports2a.d(27): Error: no property `X` for type `checkimports2a.B`, did you mean `imports.imp2.X`?
-fail_compilation/checkimports2a.d(27): while evaluating: `static assert((B).X == 0)`
-fail_compilation/checkimports2a.d(28): Error: no property `Y` for type `checkimports2a.B`, did you mean `imports.imp2.Y`?
-fail_compilation/checkimports2a.d(28): while evaluating: `static assert((B).Y == 2)`
----
-*/
-
-// new lookup + information
-
-import imports.imp1;
-
-enum X = 0;
-
-class B
-{
- import imports.imp2;
- static assert(X == 0); // imp2.X --> .X
- int[Y] aa; // imp2.Y
-}
-
-class C : B
-{
- static assert(B.X == 0); // imp2.X --> error
- static assert(B.Y == 2); // imp2.Y --> error
-
- static assert(X == 0); // imp2.X --> .X
- static assert(Y == 1); // imp2.Y --> imp1.Y
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/checkimports2b.d b/gcc/testsuite/gdc.test/fail_compilation/checkimports2b.d
deleted file mode 100644
index 8b99fdc5405..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/checkimports2b.d
+++ /dev/null
@@ -1,29 +0,0 @@
-// REQUIRED_ARGS:
-/*
-TEST_OUTPUT:
----
-fail_compilation/checkimports2b.d(18): Error: static assert: `0 == 2` is false
----
-*/
-
-// old lookup + information
-
-import imports.imp1;
-
-enum X = 0;
-
-class B
-{
- import imports.imp2;
- static assert(X == 2); // imp2.X --> .X (information)
- int[Y] aa; // imp2.Y
-}
-
-class C : B
-{
- static assert(B.X == 2); // imp2.X --> error (keep old lookup rule)
- static assert(B.Y == 2); // imp2.Y --> error (keep old lookup rule)
-
- static assert(X == 2); // imp2.X --> .X (information)
- static assert(Y == 2); // imp2.Y --> imp1.Y (information)
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/checkimports2c.d b/gcc/testsuite/gdc.test/fail_compilation/checkimports2c.d
deleted file mode 100644
index 4208dcdf6b2..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/checkimports2c.d
+++ /dev/null
@@ -1,29 +0,0 @@
-// REQUIRED_ARGS:
-/*
-TEST_OUTPUT:
----
-fail_compilation/checkimports2c.d(18): Error: static assert: `0 == 2` is false
----
-*/
-
-// old lookup + information (the order of switches is reverse)
-
-import imports.imp1;
-
-enum X = 0;
-
-class B
-{
- import imports.imp2;
- static assert(X == 2); // imp2.X --> .X (information)
- int[Y] aa; // imp2.Y
-}
-
-class C : B
-{
- static assert(B.X == 2); // imp2.X --> error (keep old lookup rule)
- static assert(B.Y == 2); // imp2.Y --> error (keep old lookup rule)
-
- static assert(X == 2); // imp2.X --> .X (information)
- static assert(Y == 2); // imp2.Y --> imp1.Y (information)
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/circ10280.d b/gcc/testsuite/gdc.test/fail_compilation/circ10280.d
index b839b9f4702..76a7764fb76 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/circ10280.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/circ10280.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/circ10280.d(11): Error: circular initialization of variable 'circ10280.q10280'
-fail_compilation/circ10280.d(10): called from here: foo10280()
+fail_compilation/circ10280.d(11): Error: circular initialization of variable `circ10280.q10280`
+fail_compilation/circ10280.d(10): called from here: `foo10280()`
---
*/
-// 10280
+// https://issues.dlang.org/show_bug.cgi?id=10280
const int q10280 = foo10280();
int foo10280() { return q10280; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/class1.d b/gcc/testsuite/gdc.test/fail_compilation/class1.d
index 261652c6412..a8907ef30c2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/class1.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/class1.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/class1.d(11): Error: class class1.C identity assignment operator overload is illegal
+fail_compilation/class1.d(11): Error: class `class1.C` identity assignment operator overload is illegal
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/class2.d b/gcc/testsuite/gdc.test/fail_compilation/class2.d
index f4894ee7ba3..a35591761a7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/class2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/class2.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/class2.d(11): Error: class class2.C identity assignment operator overload is illegal
+fail_compilation/class2.d(11): Error: class `class2.C` identity assignment operator overload is illegal
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/commaexp.d b/gcc/testsuite/gdc.test/fail_compilation/commaexp.d
index 3296046e234..7d50223ed6e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/commaexp.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/commaexp.d
@@ -1,13 +1,16 @@
-/* REQUIRED_ARGS: -o- -de
+/* REQUIRED_ARGS: -o-
TEST_OUTPUT:
---
-fail_compilation/commaexp.d(24): Deprecation: Using the result of a comma expression is deprecated
-fail_compilation/commaexp.d(36): Deprecation: Using the result of a comma expression is deprecated
-fail_compilation/commaexp.d(37): Deprecation: Using the result of a comma expression is deprecated
-fail_compilation/commaexp.d(38): Deprecation: Using the result of a comma expression is deprecated
-fail_compilation/commaexp.d(39): Deprecation: Using the result of a comma expression is deprecated
-fail_compilation/commaexp.d(41): Deprecation: Using the result of a comma expression is deprecated
-fail_compilation/commaexp.d(42): Deprecation: Using the result of a comma expression is deprecated
+fail_compilation/commaexp.d(27): Error: Using the result of a comma expression is not allowed
+fail_compilation/commaexp.d(39): Error: Using the result of a comma expression is not allowed
+fail_compilation/commaexp.d(40): Error: Using the result of a comma expression is not allowed
+fail_compilation/commaexp.d(41): Error: Using the result of a comma expression is not allowed
+fail_compilation/commaexp.d(42): Error: Using the result of a comma expression is not allowed
+fail_compilation/commaexp.d(44): Error: Using the result of a comma expression is not allowed
+fail_compilation/commaexp.d(45): Error: Using the result of a comma expression is not allowed
+fail_compilation/commaexp.d(56): Error: Using the result of a comma expression is not allowed
+fail_compilation/commaexp.d(69): Error: Using the result of a comma expression is not allowed
+fail_compilation/commaexp.d(81): Error: Using the result of a comma expression is not allowed
---
*/
@@ -19,7 +22,7 @@ int main () {
size_t aggr;
MyContainerClass mc;
- // Bug 15997
+ // https://issues.dlang.org/show_bug.cgi?id=15997
enum WINHTTP_ERROR_BASE = 4200;
enum ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED = (WINHTTP_ERROR_BASE, + 44);
@@ -41,3 +44,39 @@ int main () {
ok = true, (ok = (true, false));
return 42, 0;
}
+
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=16022
+
+bool test16022()
+{
+ enum Type { Colon, Comma }
+ Type type;
+ return type == Type.Colon, type == Type.Comma;
+}
+
+bool test16022_structs()
+{
+ struct A
+ {
+ int i;
+ string s;
+ }
+
+ enum Type { Colon = A(0, "zero"), Comma = A(1, "one") }
+ Type type;
+ return type == Type.Colon, type == Type.Comma;
+}
+
+/********************************************/
+
+
+void bar11(int*, int*) { }
+
+void test11()
+{
+ static int* p;
+ static int i;
+ bar11((i,p), &i);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/constraints_aggr.d b/gcc/testsuite/gdc.test/fail_compilation/constraints_aggr.d
new file mode 100755
index 00000000000..a60ea8c57ae
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/constraints_aggr.d
@@ -0,0 +1,45 @@
+/*
+EXTRA_FILES: imports/constraints.d
+TEST_OUTPUT:
+---
+fail_compilation/constraints_aggr.d(32): Error: template `imports.constraints.C.f` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(60): Candidate is: `f(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` !P!T`
+fail_compilation/constraints_aggr.d(33): Error: template `imports.constraints.C.g` cannot deduce function from argument types `!()()`
+fail_compilation/imports/constraints.d(63): Candidate is: `g(this T)()`
+ with `T = imports.constraints.C`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_aggr.d(35): Error: template instance `imports.constraints.S!int` does not match template declaration `S(T)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_aggr.d(44): Error: template instance `imports.constraints.BitFlags!(Enum)` does not match template declaration `BitFlags(E, bool unsafe = false)`
+ with `E = Enum`
+ must satisfy one of the following constraints:
+` unsafe
+ N!E`
+---
+*/
+
+void main()
+{
+ import imports.constraints;
+
+ C c = new C;
+ c.f(0);
+ c.g();
+
+ S!int;
+
+ enum Enum
+ {
+ A = 1,
+ B = 2,
+ C = 4,
+ BC = B|C
+ }
+ BitFlags!Enum flags;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/constraints_defs.d b/gcc/testsuite/gdc.test/fail_compilation/constraints_defs.d
new file mode 100755
index 00000000000..75152086437
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/constraints_defs.d
@@ -0,0 +1,56 @@
+/*
+EXTRA_FILES: imports/constraints.d
+TEST_OUTPUT:
+---
+fail_compilation/constraints_defs.d(49): Error: template instance `constraints_defs.main.def!(int, 0, (a) => a)` does not match template declaration `def(T, int i = 5, alias R)()`
+ with `T = int,
+ i = 0,
+ R = __lambda1`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_defs.d(50): Error: template instance `imports.constraints.defa!int` does not match template declaration `defa(T, U = int)()`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_defs.d(51): Error: template instance `imports.constraints.defv!()` does not match template declaration `defv(T = bool, int i = 5, Ts...)()`
+ with `Ts = ()`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_defs.d(52): Error: template instance `imports.constraints.defv!int` does not match template declaration `defv(T = bool, int i = 5, Ts...)()`
+ with `T = int,
+ Ts = ()`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_defs.d(53): Error: template instance `imports.constraints.defv!(int, 0)` does not match template declaration `defv(T = bool, int i = 5, Ts...)()`
+ with `T = int,
+ i = 0,
+ Ts = ()`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_defs.d(54): Error: template instance `imports.constraints.defv!(int, 0, bool)` does not match template declaration `defv(T = bool, int i = 5, Ts...)()`
+ with `T = int,
+ i = 0,
+ Ts = (bool)`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_defs.d(55): Error: template instance `imports.constraints.defv!(int, 0, bool, float)` does not match template declaration `defv(T = bool, int i = 5, Ts...)()`
+ with `T = int,
+ i = 0,
+ Ts = (bool, float)`
+ must satisfy the following constraint:
+` N!T`
+---
+*/
+
+void main()
+{
+ import imports.constraints;
+
+ def!(int, 0, a => a)();
+ defa!(int)();
+ defv!()();
+ defv!(int)();
+ defv!(int, 0)();
+ defv!(int, 0, bool)();
+ defv!(int, 0, bool, float)();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/constraints_func1.d b/gcc/testsuite/gdc.test/fail_compilation/constraints_func1.d
new file mode 100755
index 00000000000..91dd4055e77
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/constraints_func1.d
@@ -0,0 +1,93 @@
+/*
+EXTRA_FILES: imports/constraints.d
+TEST_OUTPUT:
+---
+fail_compilation/constraints_func1.d(79): Error: template `imports.constraints.test1` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(9): Candidate is: `test1(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_func1.d(80): Error: template `imports.constraints.test2` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(10): Candidate is: `test2(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` !P!T`
+fail_compilation/constraints_func1.d(81): Error: template `imports.constraints.test3` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(11): Candidate is: `test3(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_func1.d(82): Error: template `imports.constraints.test4` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(12): Candidate is: `test4(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_func1.d(83): Error: template `imports.constraints.test5` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(13): Candidate is: `test5(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` N!T
+ N!T`
+fail_compilation/constraints_func1.d(84): Error: template `imports.constraints.test6` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(14): Candidate is: `test6(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` N!T
+ N!T
+ !P!T`
+fail_compilation/constraints_func1.d(85): Error: template `imports.constraints.test7` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(15): Candidate is: `test7(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` N!T
+ N!T`
+fail_compilation/constraints_func1.d(86): Error: template `imports.constraints.test8` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(16): Candidate is: `test8(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_func1.d(87): Error: template `imports.constraints.test9` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(17): Candidate is: `test9(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` !P!T`
+fail_compilation/constraints_func1.d(88): Error: template `imports.constraints.test10` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(18): Candidate is: `test10(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` !P!T`
+fail_compilation/constraints_func1.d(89): Error: template `imports.constraints.test11` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(19): Candidate is: `test11(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` N!T
+ !P!T`
+fail_compilation/constraints_func1.d(90): Error: template `imports.constraints.test12` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(20): Candidate is: `test12(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` !P!T`
+fail_compilation/constraints_func1.d(92): Error: template `imports.constraints.test1` cannot deduce function from argument types `!()(int, int)`
+fail_compilation/imports/constraints.d(9): Candidate is: `test1(T)(T v)`
+---
+*/
+
+void main()
+{
+ import imports.constraints;
+
+ test1(0);
+ test2(0);
+ test3(0);
+ test4(0);
+ test5(0);
+ test6(0);
+ test7(0);
+ test8(0);
+ test9(0);
+ test10(0);
+ test11(0);
+ test12(0);
+
+ test1(0, 0);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/constraints_func2.d b/gcc/testsuite/gdc.test/fail_compilation/constraints_func2.d
new file mode 100755
index 00000000000..67aa78c1991
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/constraints_func2.d
@@ -0,0 +1,108 @@
+/*
+EXTRA_FILES: imports/constraints.d
+TEST_OUTPUT:
+---
+fail_compilation/constraints_func2.d(94): Error: template `imports.constraints.test13` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(23): Candidate is: `test13(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` N!T
+ !P!T`
+fail_compilation/constraints_func2.d(95): Error: template `imports.constraints.test14` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(24): Candidate is: `test14(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` !P!T
+ N!T`
+fail_compilation/constraints_func2.d(96): Error: template `imports.constraints.test15` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(25): Candidate is: `test15(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` !P!T
+ !P!T`
+fail_compilation/constraints_func2.d(97): Error: template `imports.constraints.test16` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(26): Candidate is: `test16(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` N!T
+ N!T`
+fail_compilation/constraints_func2.d(98): Error: template `imports.constraints.test17` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(27): Candidate is: `test17(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_func2.d(99): Error: template `imports.constraints.test18` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(28): Candidate is: `test18(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` N!T
+ N!T`
+fail_compilation/constraints_func2.d(100): Error: template `imports.constraints.test19` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(29): Candidate is: `test19(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` N!T
+ !P!T
+ N!T`
+fail_compilation/constraints_func2.d(101): Error: template `imports.constraints.test20` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(30): Candidate is: `test20(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_func2.d(102): Error: template `imports.constraints.test21` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(31): Candidate is: `test21(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` N!T
+ N!T`
+fail_compilation/constraints_func2.d(103): Error: template `imports.constraints.test22` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(32): Candidate is: `test22(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` !P!T
+ !P!T`
+fail_compilation/constraints_func2.d(104): Error: template `imports.constraints.test23` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(33): Candidate is: `test23(T)(T v)`
+ with `T = int`
+ must satisfy one of the following constraints:
+` !P!T
+ N!T
+ !P!T`
+fail_compilation/constraints_func2.d(105): Error: template `imports.constraints.test24` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(34): Candidate is: `test24(R)(R r)`
+ with `R = int`
+ must satisfy the following constraint:
+` __traits(hasMember, R, "stuff")`
+fail_compilation/constraints_func2.d(106): Error: template `imports.constraints.test25` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(35): Candidate is: `test25(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_func2.d(107): Error: template `imports.constraints.test26` cannot deduce function from argument types `!(float)(int)`
+fail_compilation/imports/constraints.d(36): Candidate is: `test26(T, U)(U u)`
+ with `T = float,
+ U = int`
+ must satisfy the following constraint:
+` N!U`
+---
+*/
+
+void main()
+{
+ import imports.constraints;
+
+ test13(0);
+ test14(0);
+ test15(0);
+ test16(0);
+ test17(0);
+ test18(0);
+ test19(0);
+ test20(0);
+ test21(0);
+ test22(0);
+ test23(0);
+ test24(0);
+ test25(0);
+ test26!float(5);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/constraints_func3.d b/gcc/testsuite/gdc.test/fail_compilation/constraints_func3.d
new file mode 100755
index 00000000000..f0a5099358f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/constraints_func3.d
@@ -0,0 +1,60 @@
+/*
+EXTRA_FILES: imports/constraints.d
+TEST_OUTPUT:
+---
+fail_compilation/constraints_func3.d(53): Error: template `imports.constraints.overload` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(39): Candidates are: `overload(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/imports/constraints.d(40): `overload(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` !P!T`
+fail_compilation/imports/constraints.d(41): `overload(T)(T v1, T v2)`
+fail_compilation/imports/constraints.d(42): `overload(T, V)(T v1, V v2)`
+fail_compilation/constraints_func3.d(54): Error: template `imports.constraints.overload` cannot deduce function from argument types `!()(int, string)`
+fail_compilation/imports/constraints.d(39): Candidates are: `overload(T)(T v)`
+fail_compilation/imports/constraints.d(40): `overload(T)(T v)`
+fail_compilation/imports/constraints.d(41): `overload(T)(T v1, T v2)`
+fail_compilation/imports/constraints.d(42): `overload(T, V)(T v1, V v2)`
+ with `T = int,
+ V = string`
+ must satisfy one of the following constraints:
+` N!T
+ N!V`
+fail_compilation/constraints_func3.d(56): Error: template `imports.constraints.variadic` cannot deduce function from argument types `!()()`
+fail_compilation/imports/constraints.d(43): Candidate is: `variadic(A, T...)(A a, T v)`
+fail_compilation/constraints_func3.d(57): Error: template `imports.constraints.variadic` cannot deduce function from argument types `!()(int)`
+fail_compilation/imports/constraints.d(43): Candidate is: `variadic(A, T...)(A a, T v)`
+ with `A = int,
+ T = ()`
+ must satisfy the following constraint:
+` N!int`
+fail_compilation/constraints_func3.d(58): Error: template `imports.constraints.variadic` cannot deduce function from argument types `!()(int, int)`
+fail_compilation/imports/constraints.d(43): Candidate is: `variadic(A, T...)(A a, T v)`
+ with `A = int,
+ T = (int)`
+ must satisfy the following constraint:
+` N!int`
+fail_compilation/constraints_func3.d(59): Error: template `imports.constraints.variadic` cannot deduce function from argument types `!()(int, int, int)`
+fail_compilation/imports/constraints.d(43): Candidate is: `variadic(A, T...)(A a, T v)`
+ with `A = int,
+ T = (int, int)`
+ must satisfy the following constraint:
+` N!int`
+---
+*/
+
+void main()
+{
+ import imports.constraints;
+
+ overload(0);
+ overload(0, "");
+
+ variadic();
+ variadic(0);
+ variadic(0, 1);
+ variadic(0, 1, 2);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/constraints_func4.d b/gcc/testsuite/gdc.test/fail_compilation/constraints_func4.d
new file mode 100755
index 00000000000..751e618a40c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/constraints_func4.d
@@ -0,0 +1,97 @@
+/*
+EXTRA_FILES: imports/constraints.d
+REQUIRED_ARGS: -verrors=context
+TEST_OUTPUT:
+---
+fail_compilation/constraints_func4.d(90): Error: template `imports.constraints.overload` cannot deduce function from argument types `!()(int)`
+ overload(0);
+ ^
+fail_compilation/imports/constraints.d(39): Candidates are: `overload(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+void overload(T)(T v) if (N!T);
+ ^
+fail_compilation/imports/constraints.d(40): `overload(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` !P!T`
+void overload(T)(T v) if (!P!T);
+ ^
+fail_compilation/imports/constraints.d(41): `overload(T)(T v1, T v2)`
+void overload(T)(T v1, T v2) if (N!T);
+ ^
+fail_compilation/imports/constraints.d(42): `overload(T, V)(T v1, V v2)`
+void overload(T, V)(T v1, V v2) if (N!T || N!V);
+ ^
+fail_compilation/constraints_func4.d(91): Error: template `imports.constraints.overload` cannot deduce function from argument types `!()(int, string)`
+ overload(0, "");
+ ^
+fail_compilation/imports/constraints.d(39): Candidates are: `overload(T)(T v)`
+void overload(T)(T v) if (N!T);
+ ^
+fail_compilation/imports/constraints.d(40): `overload(T)(T v)`
+void overload(T)(T v) if (!P!T);
+ ^
+fail_compilation/imports/constraints.d(41): `overload(T)(T v1, T v2)`
+void overload(T)(T v1, T v2) if (N!T);
+ ^
+fail_compilation/imports/constraints.d(42): `overload(T, V)(T v1, V v2)`
+ with `T = int,
+ V = string`
+ must satisfy one of the following constraints:
+` N!T
+ N!V`
+void overload(T, V)(T v1, V v2) if (N!T || N!V);
+ ^
+fail_compilation/constraints_func4.d(93): Error: template `imports.constraints.variadic` cannot deduce function from argument types `!()()`
+ variadic();
+ ^
+fail_compilation/imports/constraints.d(43): Candidate is: `variadic(A, T...)(A a, T v)`
+void variadic(A, T...)(A a, T v) if (N!int);
+ ^
+fail_compilation/constraints_func4.d(94): Error: template `imports.constraints.variadic` cannot deduce function from argument types `!()(int)`
+ variadic(0);
+ ^
+fail_compilation/imports/constraints.d(43): Candidate is: `variadic(A, T...)(A a, T v)`
+ with `A = int,
+ T = ()`
+ must satisfy the following constraint:
+` N!int`
+void variadic(A, T...)(A a, T v) if (N!int);
+ ^
+fail_compilation/constraints_func4.d(95): Error: template `imports.constraints.variadic` cannot deduce function from argument types `!()(int, int)`
+ variadic(0, 1);
+ ^
+fail_compilation/imports/constraints.d(43): Candidate is: `variadic(A, T...)(A a, T v)`
+ with `A = int,
+ T = (int)`
+ must satisfy the following constraint:
+` N!int`
+void variadic(A, T...)(A a, T v) if (N!int);
+ ^
+fail_compilation/constraints_func4.d(96): Error: template `imports.constraints.variadic` cannot deduce function from argument types `!()(int, int, int)`
+ variadic(0, 1, 2);
+ ^
+fail_compilation/imports/constraints.d(43): Candidate is: `variadic(A, T...)(A a, T v)`
+ with `A = int,
+ T = (int, int)`
+ must satisfy the following constraint:
+` N!int`
+void variadic(A, T...)(A a, T v) if (N!int);
+ ^
+---
+*/
+
+void main()
+{
+ import imports.constraints;
+
+ overload(0);
+ overload(0, "");
+
+ variadic();
+ variadic(0);
+ variadic(0, 1);
+ variadic(0, 1, 2);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/constraints_tmpl.d b/gcc/testsuite/gdc.test/fail_compilation/constraints_tmpl.d
new file mode 100755
index 00000000000..06caa52493d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/constraints_tmpl.d
@@ -0,0 +1,44 @@
+/*
+EXTRA_FILES: imports/constraints.d
+TEST_OUTPUT:
+---
+fail_compilation/constraints_tmpl.d(35): Error: template instance `imports.constraints.dummy!()` does not match template declaration `dummy()()`
+ must satisfy the following constraint:
+` false`
+fail_compilation/constraints_tmpl.d(37): Error: template instance `imports.constraints.message_nice!(int, int)` does not match template declaration `message_nice(T, U)()`
+ with `T = int,
+ U = int`
+ must satisfy the following constraint:
+` N!U`
+fail_compilation/constraints_tmpl.d(38): Error: template instance `imports.constraints.message_ugly!int` does not match template declaration `message_ugly(T)(T v)`
+ with `T = int`
+ must satisfy the following constraint:
+` N!T`
+fail_compilation/constraints_tmpl.d(40): Error: template instance `args!int` does not match template declaration `args(T, U)()`
+fail_compilation/constraints_tmpl.d(41): Error: template instance `imports.constraints.args!(int, float)` does not match template declaration `args(T, U)()`
+ with `T = int,
+ U = float`
+ must satisfy one of the following constraints:
+` N!T
+ N!U`
+fail_compilation/constraints_tmpl.d(43): Error: template instance `constraints_tmpl.main.lambda!((a) => a)` does not match template declaration `lambda(alias pred)()`
+ with `pred = __lambda1`
+ must satisfy the following constraint:
+` N!int`
+---
+*/
+
+void main()
+{
+ import imports.constraints;
+
+ dummy!();
+
+ message_nice!(int, int);
+ message_ugly!int;
+
+ args!int;
+ args!(int, float);
+
+ lambda!(a => a)();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/cpp_abi_tag.d b/gcc/testsuite/gdc.test/fail_compilation/cpp_abi_tag.d
new file mode 100644
index 00000000000..542bc432311
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/cpp_abi_tag.d
@@ -0,0 +1,57 @@
+/* DISABLED: win32 win64
+REQUIRED_ARGS: -extern-std=c++11
+TEST_OUTPUT:
+---
+fail_compilation/cpp_abi_tag.d(111): Error: `@gnuAbiTag` can only apply to C++ symbols
+fail_compilation/cpp_abi_tag.d(131): Error: `@gnuAbiTag` cannot be applied to namespaces
+fail_compilation/cpp_abi_tag.d(102): Error: `@gnuAbiTag` at least one argument expected
+fail_compilation/cpp_abi_tag.d(105): Error: `@gnuAbiTag` at least one argument expected
+fail_compilation/cpp_abi_tag.d(108): Error: `@gnuAbiTag` char `0x99` not allowed in mangling
+fail_compilation/cpp_abi_tag.d(114): Error: argument `2` to `@gnuAbiTag` cannot be `null`
+fail_compilation/cpp_abi_tag.d(114): Error: argument `3` to `@gnuAbiTag` cannot be empty
+fail_compilation/cpp_abi_tag.d(117): Error: `@gnuAbiTag` at least one argument expected
+fail_compilation/cpp_abi_tag.d(137): Error: only one `@gnuAbiTag` allowed per symbol
+fail_compilation/cpp_abi_tag.d(137): instead of `@gnuAbiTag(["x"]) @gnuAbiTag(["a"])`, use `@gnuAbiTag("x", "a")`
+---
+*/
+
+#line 100
+import core.attribute;
+
+@gnuAbiTag
+extern(C++) struct A {}
+
+@gnuAbiTag()
+extern(C++) struct B {}
+
+@gnuAbiTag("a\x99")
+extern(C++) struct D {}
+
+@gnuAbiTag("a")
+struct F {}
+
+@gnuAbiTag("a", null, "")
+extern(C++) struct G {}
+
+@gnuAbiTag((string[]).init)
+extern(C++) struct H {}
+
+// Note: There is no way to distinguish between
+// `extern(C++, "ns") { ... }` and `extern(C++, "ns") ...;`
+// So ABI tags have to be on the inside
+extern(C++, "ns") @gnuAbiTag("x") void func1();
+extern(C++, ns2) @gnuAbiTag("x") void func2();
+
+@gnuAbiTag("x")
+extern(C++, "ns3")
+{
+ void func3();
+}
+@gnuAbiTag("x")
+extern(C++, ns4)
+{
+ void func4();
+}
+
+@gnuAbiTag("x") @gnuAbiTag("a")
+extern(C++) void func5();
diff --git a/gcc/testsuite/gdc.test/fail_compilation/cpp_abi_tag2.d b/gcc/testsuite/gdc.test/fail_compilation/cpp_abi_tag2.d
new file mode 100644
index 00000000000..2739e2b05d6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/cpp_abi_tag2.d
@@ -0,0 +1,19 @@
+/* DISABLED: win32 win64
+REQUIRED_ARGS: -extern-std=c++11
+TEST_OUTPUT:
+---
+fail_compilation/cpp_abi_tag2.d(102): Error: constructor `core.attribute.gnuAbiTag.this(string[] tags...)` is not callable using argument types `(string, wstring, dstring)`
+fail_compilation/cpp_abi_tag2.d(102): cannot pass argument `"b"w` of type `wstring` to parameter `string[] tags...`
+fail_compilation/cpp_abi_tag2.d(105): Error: constructor `core.attribute.gnuAbiTag.this(string[] tags...)` is not callable using argument types `(string, int, double)`
+fail_compilation/cpp_abi_tag2.d(105): cannot pass argument `2` of type `int` to parameter `string[] tags...`
+---
+*/
+
+#line 100
+import core.attribute;
+
+@gnuAbiTag("a", "b"w, "c"d)
+extern(C++) struct C {}
+
+@gnuAbiTag("a", 2, 3.3)
+extern(C++) struct E {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/cppeh1.d b/gcc/testsuite/gdc.test/fail_compilation/cppeh1.d
index e60368c1506..9ef0ab3c79b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/cppeh1.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/cppeh1.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/cppeh1.d(26): Error: cannot catch C++ class objects in @safe code
+fail_compilation/cppeh1.d(26): Error: cannot catch C++ class objects in `@safe` code
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/cppmangle.d b/gcc/testsuite/gdc.test/fail_compilation/cppmangle.d
index ca9ed9a71a7..8134afb18d2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/cppmangle.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/cppmangle.d
@@ -1,9 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/cppmangle.d(10): Error: invalid zero length C++ namespace
-fail_compilation/cppmangle.d(14): Error: expected valid identifier for C++ namespace but got `0num`
-fail_compilation/cppmangle.d(18): Error: string expected following `,` for C++ namespace, not `)`
+fail_compilation/cppmangle.d(11): Error: expected valid identifier for C++ namespace but got ``
+fail_compilation/cppmangle.d(15): Error: expected valid identifier for C++ namespace but got `0num`
+fail_compilation/cppmangle.d(19): Error: compile time string constant (or tuple) expected, not `2`
+fail_compilation/cppmangle.d(23): Error: expected valid identifier for C++ namespace but got `invalid@namespace`
---
*/
@@ -15,6 +16,10 @@ extern(C++, "0num")
{
}
-extern(C++, "std", )
+extern(C++, 1+1)
+{
+}
+
+extern(C++, "invalid@namespace")
{
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/cppmangle2.d b/gcc/testsuite/gdc.test/fail_compilation/cppmangle2.d
new file mode 100644
index 00000000000..67f0647c8a8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/cppmangle2.d
@@ -0,0 +1,11 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/cppmangle2.d(9): Error: namespace `cppmangle2.ns` conflicts with variable `cppmangle2.ns` at fail_compilation/cppmangle2.d(8)
+---
+*/
+
+enum ns = "ns";
+extern(C++, ns)
+{
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ctfe10989.d b/gcc/testsuite/gdc.test/fail_compilation/ctfe10989.d
index 22b6dbb6c44..a5da609318f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ctfe10989.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ctfe10989.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ctfe10989.d(11): Error: uncaught CTFE exception object.Exception("abc"c)
-fail_compilation/ctfe10989.d(14): called from here: throwing()
+fail_compilation/ctfe10989.d(11): Error: uncaught CTFE exception `object.Exception("abc"c)`
+fail_compilation/ctfe10989.d(14): called from here: `throwing()`
fail_compilation/ctfe10989.d(14): while evaluating: `static assert(throwing())`
---
*/
@@ -16,8 +16,8 @@ static assert(throwing());
/*
TEST_OUTPUT:
---
-fail_compilation/ctfe10989.d(33): Error: uncaught CTFE exception object.Exception("abc"c)
-fail_compilation/ctfe10989.d(36): called from here: throwing2()
+fail_compilation/ctfe10989.d(33): Error: uncaught CTFE exception `object.Exception("abc"c)`
+fail_compilation/ctfe10989.d(36): called from here: `throwing2()`
fail_compilation/ctfe10989.d(36): while evaluating: `static assert(throwing2())`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ctfe10995.d b/gcc/testsuite/gdc.test/fail_compilation/ctfe10995.d
index ca424956c78..829fb5328db 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ctfe10995.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ctfe10995.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ctfe10995.d(19): Error: cannot read uninitialized variable a in CTFE
-fail_compilation/ctfe10995.d(25): Error: cannot read uninitialized variable a in CTFE
+fail_compilation/ctfe10995.d(19): Error: cannot read uninitialized variable `a` in CTFE
+fail_compilation/ctfe10995.d(25): Error: cannot read uninitialized variable `a` in CTFE
---
*/
struct T
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ctfe11467.d b/gcc/testsuite/gdc.test/fail_compilation/ctfe11467.d
index f7b25778893..dcde98f12b8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ctfe11467.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ctfe11467.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ctfe11467.d(15): Error: overlapping slice assignment [0..4] = [1..5]
-fail_compilation/ctfe11467.d(24): called from here: test11467a()
+fail_compilation/ctfe11467.d(15): Error: overlapping slice assignment `[0..4] = [1..5]`
+fail_compilation/ctfe11467.d(24): called from here: `test11467a()`
fail_compilation/ctfe11467.d(24): while evaluating: `static assert(test11467a())`
-fail_compilation/ctfe11467.d(21): Error: overlapping slice assignment [1..5] = [0..4]
-fail_compilation/ctfe11467.d(25): called from here: test11467b()
+fail_compilation/ctfe11467.d(21): Error: overlapping slice assignment `[1..5] = [0..4]`
+fail_compilation/ctfe11467.d(25): called from here: `test11467b()`
fail_compilation/ctfe11467.d(25): while evaluating: `static assert(test11467b())`
---
*/
@@ -27,11 +27,11 @@ static assert(test11467b());
/*
TEST_OUTPUT:
---
-fail_compilation/ctfe11467.d(41): Error: overlapping slice assignment [0..4] = [1..5]
-fail_compilation/ctfe11467.d(50): called from here: test11467c()
+fail_compilation/ctfe11467.d(41): Error: overlapping slice assignment `[0..4] = [1..5]`
+fail_compilation/ctfe11467.d(50): called from here: `test11467c()`
fail_compilation/ctfe11467.d(50): while evaluating: `static assert(test11467c())`
-fail_compilation/ctfe11467.d(47): Error: overlapping slice assignment [1..5] = [0..4]
-fail_compilation/ctfe11467.d(51): called from here: test11467d()
+fail_compilation/ctfe11467.d(47): Error: overlapping slice assignment `[1..5] = [0..4]`
+fail_compilation/ctfe11467.d(51): called from here: `test11467d()`
fail_compilation/ctfe11467.d(51): while evaluating: `static assert(test11467d())`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ctfe13612.d b/gcc/testsuite/gdc.test/fail_compilation/ctfe13612.d
index b7ab79e507f..150f2ac5d91 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ctfe13612.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ctfe13612.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ctfe13612.d(15): Error: function ctfe13612.S.recurse CTFE recursion limit exceeded
-fail_compilation/ctfe13612.d(20): called from here: s.recurse()
-fail_compilation/ctfe13612.d(15): 1000 recursive calls to function recurse
-fail_compilation/ctfe13612.d(23): called from here: (new S).recurse()
+fail_compilation/ctfe13612.d(15): Error: function `ctfe13612.S.recurse` CTFE recursion limit exceeded
+fail_compilation/ctfe13612.d(20): called from here: `s.recurse()`
+fail_compilation/ctfe13612.d(15): 1000 recursive calls to function `recurse`
+fail_compilation/ctfe13612.d(23): called from here: `(new S).recurse()`
fail_compilation/ctfe13612.d(23): while evaluating: `static assert((new S).recurse())`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ctfe14207.d b/gcc/testsuite/gdc.test/fail_compilation/ctfe14207.d
index 511f7553c2d..becc068b0c6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ctfe14207.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ctfe14207.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ctfe14207.d(13): Error: cannot convert &immutable(ulong) to ubyte[8]* at compile time
-fail_compilation/ctfe14207.d(18): called from here: nativeToBigEndian()
-fail_compilation/ctfe14207.d(22): called from here: digest()
+fail_compilation/ctfe14207.d(13): Error: cannot convert `&immutable(ulong)` to `ubyte[8]*` at compile time
+fail_compilation/ctfe14207.d(18): called from here: `nativeToBigEndian()`
+fail_compilation/ctfe14207.d(22): called from here: `digest()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ctfe14465.d b/gcc/testsuite/gdc.test/fail_compilation/ctfe14465.d
index 9106d3daa5b..52e1ad3c1ca 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ctfe14465.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ctfe14465.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ctfe14465.d(19): Error: uncaught CTFE exception ctfe14465.E("message")
-fail_compilation/ctfe14465.d(22): called from here: foo()
+fail_compilation/ctfe14465.d(19): Error: uncaught CTFE exception `ctfe14465.E("message")`
+fail_compilation/ctfe14465.d(22): called from here: `foo()`
fail_compilation/ctfe14465.d(22): while evaluating: `static assert(foo())`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/dassert.d b/gcc/testsuite/gdc.test/fail_compilation/dassert.d
new file mode 100644
index 00000000000..8a813177c35
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/dassert.d
@@ -0,0 +1,43 @@
+/*
+REQUIRED_ARGS: -checkaction=context
+TEST_OUTPUT:
+---
+fail_compilation/dassert.d(14): Error: expression `tuple(0, 0)` of type `(int, int)` does not have a boolean value
+fail_compilation/dassert.d(21): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/dassert.d(29): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/dassert.d(40): Error: expression `issue()` of type `void` does not have a boolean value
+---
+*/
+#line 10
+
+struct Baguette { int bread, floor; }
+void main ()
+{
+ assert(Baguette.init.tupleof);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21590
+void issue21590()
+{
+ int a, b = 1;
+ assert (a = b);
+
+ static ref int get()
+ {
+ static int i;
+ return i;
+ }
+
+ assert(get() = 1);
+
+ // No errors for binary assignments (regardless of -checkaction=context)
+ int[] arr;
+ assert(arr ~= 1);
+ assert(a += b);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21798
+void issue()
+{
+ assert(issue());
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ddoc_18083.d b/gcc/testsuite/gdc.test/fail_compilation/ddoc_18083.d
new file mode 100644
index 00000000000..234c280e424
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ddoc_18083.d
@@ -0,0 +1,17 @@
+// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/fail_compilation -o- -w -c
+/* TEST_OUTPUT:
+---
+fail_compilation/ddoc_18083.d(14): Warning: Ddoc: function declaration has no parameter 'this'
+fail_compilation/ddoc_18083.d(14): Warning: Ddoc: parameter count mismatch, expected 0, got 1
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
+---
+*/
+/**
+Params:
+ this = non-existent parameter
+*/
+int foo()
+{
+ return 1;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/debugCaseDeclaration.d b/gcc/testsuite/gdc.test/fail_compilation/debugCaseDeclaration.d
new file mode 100644
index 00000000000..0a06bca93a0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/debugCaseDeclaration.d
@@ -0,0 +1,39 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=21739
+
+REQUIRED_ARGS: -debug
+TEST_OUTPUT:
+---
+fail_compilation/debugCaseDeclaration.d(22): Error: undefined identifier `x`
+fail_compilation/debugCaseDeclaration.d(33): Error: undefined identifier `y`
+---
+*/
+
+void main()
+{
+ int i, k;
+ switch (i)
+ {
+ case 0:
+ int x;
+ break;
+
+ case 1:
+ x = 1;
+ break;
+
+ case 2:
+ int y;
+ break;
+
+ debug
+ {
+ case 3:
+ k = 1; // Valid
+ y = 1; // Invalid but accepted
+ break;
+ }
+
+ default:
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/dep_d1_ops.d b/gcc/testsuite/gdc.test/fail_compilation/dep_d1_ops.d
new file mode 100644
index 00000000000..230fc4bae3e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/dep_d1_ops.d
@@ -0,0 +1,191 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/dep_d1_ops.d(105): Deprecation: `opAdd` is deprecated. Use `opBinary(string op)(...) if (op == "+")` instead.
+fail_compilation/dep_d1_ops.d(106): Deprecation: `opAdd_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == "+")` instead.
+fail_compilation/dep_d1_ops.d(107): Deprecation: `opSub` is deprecated. Use `opBinary(string op)(...) if (op == "-")` instead.
+fail_compilation/dep_d1_ops.d(108): Deprecation: `opSub_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == "-")` instead.
+fail_compilation/dep_d1_ops.d(109): Deprecation: `opMul` is deprecated. Use `opBinary(string op)(...) if (op == "*")` instead.
+fail_compilation/dep_d1_ops.d(110): Deprecation: `opMul_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == "*")` instead.
+fail_compilation/dep_d1_ops.d(111): Deprecation: `opDiv` is deprecated. Use `opBinary(string op)(...) if (op == "/")` instead.
+fail_compilation/dep_d1_ops.d(112): Deprecation: `opDiv_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == "/")` instead.
+fail_compilation/dep_d1_ops.d(113): Deprecation: `opMod` is deprecated. Use `opBinary(string op)(...) if (op == "%")` instead.
+fail_compilation/dep_d1_ops.d(114): Deprecation: `opMod_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == "%")` instead.
+fail_compilation/dep_d1_ops.d(116): Deprecation: `opAnd` is deprecated. Use `opBinary(string op)(...) if (op == "&")` instead.
+fail_compilation/dep_d1_ops.d(117): Deprecation: `opOr` is deprecated. Use `opBinary(string op)(...) if (op == "|")` instead.
+fail_compilation/dep_d1_ops.d(118): Deprecation: `opXor` is deprecated. Use `opBinary(string op)(...) if (op == "^")` instead.
+fail_compilation/dep_d1_ops.d(120): Deprecation: `opShl` is deprecated. Use `opBinary(string op)(...) if (op == "<<")` instead.
+fail_compilation/dep_d1_ops.d(121): Deprecation: `opShl_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == "<<")` instead.
+fail_compilation/dep_d1_ops.d(122): Deprecation: `opShr` is deprecated. Use `opBinary(string op)(...) if (op == ">>")` instead.
+fail_compilation/dep_d1_ops.d(123): Deprecation: `opShr_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == ">>")` instead.
+fail_compilation/dep_d1_ops.d(124): Deprecation: `opUShr` is deprecated. Use `opBinary(string op)(...) if (op == ">>>")` instead.
+fail_compilation/dep_d1_ops.d(125): Deprecation: `opUShr_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == ">>>")` instead.
+fail_compilation/dep_d1_ops.d(127): Deprecation: `opCat` is deprecated. Use `opBinary(string op)(...) if (op == "~")` instead.
+fail_compilation/dep_d1_ops.d(128): Deprecation: `opCat_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == "~")` instead.
+fail_compilation/dep_d1_ops.d(130): Deprecation: `opNeg` is deprecated. Use `opUnary(string op)() if (op == "-")` instead.
+fail_compilation/dep_d1_ops.d(131): Deprecation: `opCom` is deprecated. Use `opUnary(string op)() if (op == "~")` instead.
+fail_compilation/dep_d1_ops.d(132): Deprecation: `opPostInc` is deprecated. Use `opUnary(string op)() if (op == "++")` instead.
+fail_compilation/dep_d1_ops.d(133): Deprecation: `opPostDec` is deprecated. Use `opUnary(string op)() if (op == "--")` instead.
+fail_compilation/dep_d1_ops.d(134): Deprecation: `opStar` is deprecated. Use `opUnary(string op)() if (op == "*")` instead.
+fail_compilation/dep_d1_ops.d(136): Deprecation: `opIn` is deprecated. Use `opBinary(string op)(...) if (op == "in")` instead.
+fail_compilation/dep_d1_ops.d(137): Deprecation: `opIn_r` is deprecated. Use `opBinaryRight(string op)(...) if (op == "in")` instead.
+fail_compilation/dep_d1_ops.d(139): Deprecation: `opAddAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "+")` instead.
+fail_compilation/dep_d1_ops.d(140): Deprecation: `opSubAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "-")` instead.
+fail_compilation/dep_d1_ops.d(141): Deprecation: `opMulAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "*")` instead.
+fail_compilation/dep_d1_ops.d(142): Deprecation: `opDivAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "/")` instead.
+fail_compilation/dep_d1_ops.d(143): Deprecation: `opModAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "%")` instead.
+fail_compilation/dep_d1_ops.d(144): Deprecation: `opAndAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "&")` instead.
+fail_compilation/dep_d1_ops.d(145): Deprecation: `opOrAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "|")` instead.
+fail_compilation/dep_d1_ops.d(146): Deprecation: `opXorAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "^")` instead.
+fail_compilation/dep_d1_ops.d(147): Deprecation: `opShlAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "<<")` instead.
+fail_compilation/dep_d1_ops.d(148): Deprecation: `opShrAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == ">>")` instead.
+fail_compilation/dep_d1_ops.d(149): Deprecation: `opUShrAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == ">>>")` instead.
+fail_compilation/dep_d1_ops.d(150): Deprecation: `opCatAssign` is deprecated. Use `opOpAssign(string op)(...) if (op == "~")` instead.
+fail_compilation/dep_d1_ops.d(158): Deprecation: `opCom` is deprecated. Use `opUnary(string op)() if (op == "~")` instead.
+---
+*/
+
+#line 50
+struct S
+{
+ int opAdd(int i) { return 0; }
+ int opAdd_r(int i) { return 0; }
+ int opSub(int i) { return 0; }
+ int opSub_r(int i) { return 0; }
+ int opMul(int i) { return 0; }
+ int opMul_r(int i) { return 0; }
+ int opDiv(int i) { return 0; }
+ int opDiv_r(int i) { return 0; }
+ int opMod(int i) { return 0; }
+ int opMod_r(int i) { return 0; }
+
+ int opAnd(int i) { return 0; }
+ int opOr(int i) { return 0; }
+ int opXor(int i) { return 0; }
+
+ int opShl(int i) { return 0; }
+ int opShl_r(int i) { return 0; }
+ int opShr(int i) { return 0; }
+ int opShr_r(int i) { return 0; }
+ int opUShr(int i) { return 0; }
+ int opUShr_r(int i) { return 0; }
+
+ int opCat(int i) { return 0; }
+ int opCat_r(int i) { return 0; }
+
+ int opNeg() { return 0; }
+ int opCom() { return 0; }
+ int opPostInc() { return 0; }
+ int opPostDec() { return 0; }
+ int opStar() { return 0; }
+
+ int opIn(int i) { return 0; }
+ int opIn_r(int i) { return 0; }
+
+ int opAddAssign(int i) { return 0; }
+ int opSubAssign(int i) { return 0; }
+ int opMulAssign(int i) { return 0; }
+ int opDivAssign(int i) { return 0; }
+ int opModAssign(int i) { return 0; }
+ int opAndAssign(int i) { return 0; }
+ int opOrAssign(int i) { return 0; }
+ int opXorAssign(int i) { return 0; }
+ int opShlAssign(int i) { return 0; }
+ int opShrAssign(int i) { return 0; }
+ int opUShrAssign(int i) { return 0; }
+ int opCatAssign(int i) { return 0; }
+}
+
+void main()
+{
+ S s;
+ int i;
+
+ i = s + 1;
+ i = 1 + s;
+ i = s - 1;
+ i = 1 - s;
+ i = s * 1;
+ i = 1 * s;
+ i = s / 1;
+ i = 1 / s;
+ i = s % 1;
+ i = 1 % s;
+
+ i = s & 1;
+ i = s | 1;
+ i = s ^ 1;
+
+ i = s << 1;
+ i = 1 << s;
+ i = s >> 1;
+ i = 1 >> s;
+ i = s >>> 1;
+ i = 1 >>> s;
+
+ i = s ~ 1;
+ i = 1 ~ s;
+
+ i = -s;
+ i = ~s;
+ s++;
+ s--;
+ i = *s;
+
+ i = s in 1;
+ i = 1 in s;
+
+ s += 1;
+ s -= 1;
+ s *= 1;
+ s /= 1;
+ s %= 1;
+ s &= 1;
+ s |= 1;
+ s ^= 1;
+ s <<= 1;
+ s >>= 1;
+ s >>>= 1;
+ s ~= 1;
+
+ scope nd = new NoDeprecation;
+ assert((42 in nd) == 0);
+ assert((nd in 42) == 0);
+ assert((nd ~ 42) == 0);
+ assert((42 ~ nd) == 0);
+
+ ~nd;
+}
+
+/// See https://github.com/dlang/dmd/pull/10716
+class NoDeprecation
+{
+ int opIn(int i) { return 0; }
+ int opIn_r(int i) { return 0; }
+ int opCat(int i) { return 0; }
+ int opCat_r(int i) { return 0; }
+
+ /// This is considered because there is no `opUnary`
+ /// However, the other overloads (`opBinary` / `opBinaryRight`)
+ /// means that other operator overloads would not be considered.
+ int opCom() { return 0; }
+
+ int opBinary(string op)(int arg)
+ if (op == "in" || op == "~")
+ {
+ static if (op == "in")
+ return this.opIn(arg);
+ else static if (op == "~")
+ return this.opCat(arg);
+ }
+
+ int opBinaryRight(string op)(int arg)
+ if (op == "in" || op == "~")
+ {
+ static if (op == "in")
+ return this.opIn_r(arg);
+ else static if (op == "~")
+ return this.opCat_r(arg);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/dephexstrings.d b/gcc/testsuite/gdc.test/fail_compilation/dephexstrings.d
new file mode 100644
index 00000000000..a97afdcdf75
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/dephexstrings.d
@@ -0,0 +1,9 @@
+// REQUIRED_ARGS: -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/dephexstrings.d(8): Error: Built-in hex string literals are obsolete, use `std.conv.hexString!"60"` instead.
+---
+*/
+enum xstr = x"60";
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/depmsg.d b/gcc/testsuite/gdc.test/fail_compilation/depmsg.d
index 186576a5473..b0c2b2e6a8e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/depmsg.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/depmsg.d
@@ -2,19 +2,19 @@
/*
TEST_OUTPUT:
---
-fail_compilation/depmsg.d(40): Deprecation: struct depmsg.main.Inner.A is deprecated - With message!
-fail_compilation/depmsg.d(40): Deprecation: struct depmsg.main.Inner.A is deprecated - With message!
-fail_compilation/depmsg.d(41): Deprecation: class depmsg.main.Inner.B is deprecated - With message!
-fail_compilation/depmsg.d(41): Deprecation: class depmsg.main.Inner.B is deprecated - With message!
-fail_compilation/depmsg.d(42): Deprecation: interface depmsg.main.Inner.C is deprecated - With message!
-fail_compilation/depmsg.d(42): Deprecation: interface depmsg.main.Inner.C is deprecated - With message!
-fail_compilation/depmsg.d(43): Deprecation: union depmsg.main.Inner.D is deprecated - With message!
-fail_compilation/depmsg.d(43): Deprecation: union depmsg.main.Inner.D is deprecated - With message!
-fail_compilation/depmsg.d(44): Deprecation: enum depmsg.main.Inner.E is deprecated - With message!
-fail_compilation/depmsg.d(44): Deprecation: enum depmsg.main.Inner.E is deprecated - With message!
-fail_compilation/depmsg.d(46): Deprecation: alias depmsg.main.Inner.G is deprecated - With message!
-fail_compilation/depmsg.d(47): Deprecation: variable depmsg.main.Inner.H is deprecated - With message!
-fail_compilation/depmsg.d(48): Deprecation: class depmsg.main.Inner.I!().I is deprecated - With message!
+fail_compilation/depmsg.d(40): Deprecation: struct `depmsg.main.Inner.A` is deprecated - With message!
+fail_compilation/depmsg.d(40): Deprecation: struct `depmsg.main.Inner.A` is deprecated - With message!
+fail_compilation/depmsg.d(41): Deprecation: class `depmsg.main.Inner.B` is deprecated - With message!
+fail_compilation/depmsg.d(41): Deprecation: class `depmsg.main.Inner.B` is deprecated - With message!
+fail_compilation/depmsg.d(42): Deprecation: interface `depmsg.main.Inner.C` is deprecated - With message!
+fail_compilation/depmsg.d(42): Deprecation: interface `depmsg.main.Inner.C` is deprecated - With message!
+fail_compilation/depmsg.d(43): Deprecation: union `depmsg.main.Inner.D` is deprecated - With message!
+fail_compilation/depmsg.d(43): Deprecation: union `depmsg.main.Inner.D` is deprecated - With message!
+fail_compilation/depmsg.d(44): Deprecation: enum `depmsg.main.Inner.E` is deprecated - With message!
+fail_compilation/depmsg.d(44): Deprecation: enum `depmsg.main.Inner.E` is deprecated - With message!
+fail_compilation/depmsg.d(46): Deprecation: alias `depmsg.main.Inner.G` is deprecated - With message!
+fail_compilation/depmsg.d(47): Deprecation: variable `depmsg.main.Inner.H` is deprecated - With message!
+fail_compilation/depmsg.d(48): Deprecation: class `depmsg.main.Inner.I()` is deprecated - With message!
---
*/
@@ -52,13 +52,13 @@ void main()
/*
TEST_OUTPUT:
---
-fail_compilation/depmsg.d(94): Deprecation: function depmsg.test12954.Foo.bar1 is deprecated - [C] Use Foo.bar42 instead
-fail_compilation/depmsg.d(95): Deprecation: function depmsg.test12954.Foo.bar2 is deprecated - [E] Use Foo.bar42 instead
-fail_compilation/depmsg.d(96): Deprecation: function depmsg.test12954.Foo.bar3 is deprecated - [S] Use Foo.bar42 instead
-fail_compilation/depmsg.d(97): Deprecation: function depmsg.test12954.Foo.bar4 is deprecated - [F] Use Foo.bar42 instead
-fail_compilation/depmsg.d(98): Deprecation: variable depmsg.test12954.Foo.v2 is deprecated - Forward reference
-fail_compilation/depmsg.d(105): Deprecation: class depmsg.test12954.Obsolete is deprecated
-fail_compilation/depmsg.d(105): Deprecation: function depmsg.test12954.Obsolete.obs is deprecated - Function is obsolete
+fail_compilation/depmsg.d(94): Deprecation: function `depmsg.test12954.Foo.bar1` is deprecated - [C] Use Foo.bar42 instead
+fail_compilation/depmsg.d(95): Deprecation: function `depmsg.test12954.Foo.bar2` is deprecated - [E] Use Foo.bar42 instead
+fail_compilation/depmsg.d(96): Deprecation: function `depmsg.test12954.Foo.bar3` is deprecated - [S] Use Foo.bar42 instead
+fail_compilation/depmsg.d(97): Deprecation: function `depmsg.test12954.Foo.bar4` is deprecated - [F] Use Foo.bar42 instead
+fail_compilation/depmsg.d(98): Deprecation: variable `depmsg.test12954.Foo.v2` is deprecated - Forward reference
+fail_compilation/depmsg.d(105): Deprecation: class `depmsg.test12954.Obsolete` is deprecated
+fail_compilation/depmsg.d(105): Deprecation: function `depmsg.test12954.Obsolete.obs` is deprecated - Function is obsolete
---
*/
void test12954()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/depmsg15814.d b/gcc/testsuite/gdc.test/fail_compilation/depmsg15814.d
index 1a2b9f89d39..613576c256f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/depmsg15814.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/depmsg15814.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/depmsg15814.d(9): Deprecation: function depmsg15814.get15814 is deprecated - bug15814
+fail_compilation/depmsg15814.d(9): Deprecation: function `depmsg15814.get15814` is deprecated - bug15814
---
*/
deprecated("bug15814") int get15814() { return 0; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/depmsg15815.d b/gcc/testsuite/gdc.test/fail_compilation/depmsg15815.d
index 72abbd77306..0b19687ad52 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/depmsg15815.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/depmsg15815.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/depmsg15815.d(23): Deprecation: alias depmsg15815.Alias!(const(Foo)).Alias is deprecated - message
+fail_compilation/depmsg15815.d(23): Deprecation: template `depmsg15815.Alias(T)` is deprecated - message
Foo
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecate1553.d b/gcc/testsuite/gdc.test/fail_compilation/deprecate1553.d
index 4a03a6fa41b..18a7152001a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/deprecate1553.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/deprecate1553.d
@@ -1,10 +1,9 @@
// REQUIRED_ARGS: -de
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/deprecate1553.d(19): Deprecation: cannot use foreach_reverse with a delegate
+fail_compilation/deprecate1553.d(18): Deprecation: cannot use `foreach_reverse` with a delegate
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecated6760.d b/gcc/testsuite/gdc.test/fail_compilation/deprecated6760.d
index 07b9527e087..8faa88b4270 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/deprecated6760.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/deprecated6760.d
@@ -3,8 +3,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/deprecated6760.d(13): Deprecation: function deprecated6760.Foo.opEquals overridden functions cannot be annotated @disable
-fail_compilation/deprecated6760.d(18): Deprecation: function deprecated6760.Bar.opEquals deprecated functions cannot be annotated @disable
+fail_compilation/deprecated6760.d(13): Deprecation: `deprecated6760.Foo.opEquals` cannot be annotated with `@disable` because it is overriding a function in the base class
+fail_compilation/deprecated6760.d(18): Deprecation: `deprecated6760.Bar.opEquals` cannot be marked as `deprecated` because it is overriding a function in the base class
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecatedImports.d b/gcc/testsuite/gdc.test/fail_compilation/deprecatedImports.d
new file mode 100644
index 00000000000..bd68a9d4e3c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/deprecatedImports.d
@@ -0,0 +1,31 @@
+/*
+REQUIRED_ARGS: -de
+EXTRA_FILES: imports/deprecatedImporta.d imports/deprecatedImportb.d
+
+TEST_OUTPUT:
+----
+fail_compilation/deprecatedImports.d(19): Deprecation: alias `deprecatedImporta.foo` is deprecated - Please import deprecatedImportb directly!
+fail_compilation/deprecatedImports.d(21): Deprecation: alias `deprecatedImporta.bar` is deprecated - Please import deprecatedImportb directly!
+fail_compilation/deprecatedImports.d(23): Deprecation: alias `deprecatedImporta.AliasSeq` is deprecated - Please import deprecatedImportb directly!
+fail_compilation/deprecatedImports.d(27): Deprecation: alias `deprecatedImporta.S` is deprecated - Please import deprecatedImportb directly!
+fail_compilation/deprecatedImports.d(29): Deprecation: alias `deprecatedImporta.C` is deprecated - Please import deprecatedImportb directly!
+fail_compilation/deprecatedImports.d(31): Deprecation: alias `deprecatedImporta.I` is deprecated - Please import deprecatedImportb directly!
+fail_compilation/deprecatedImports.d(25): Deprecation: alias `deprecatedImporta.E` is deprecated - Please import deprecatedImportb directly!
+----
+*/
+
+import imports.deprecatedImporta;
+
+alias f = foo;
+
+alias b = bar!(int);
+
+alias Types = AliasSeq!(int);
+
+int x = E;
+
+S s;
+
+C c;
+
+I i;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecatedTemplates.d b/gcc/testsuite/gdc.test/fail_compilation/deprecatedTemplates.d
new file mode 100644
index 00000000000..d9054798661
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/deprecatedTemplates.d
@@ -0,0 +1,63 @@
+/*
+REQUIRED_ARGS: -de
+
+TEST_OUTPUT:
+----
+fail_compilation/deprecatedTemplates.d(103): Deprecation: template `deprecatedTemplates.AliasSeq(V...)` is deprecated
+fail_compilation/deprecatedTemplates.d(107): Deprecation: struct `deprecatedTemplates.S1(V...)` is deprecated
+fail_compilation/deprecatedTemplates.d(115): Deprecation: template `deprecatedTemplates.C(V...)` is deprecated
+----
+*/
+#line 100
+
+deprecated alias AliasSeq(V...) = V;
+
+alias x = AliasSeq!(1, 2, 3);
+
+deprecated struct S1(V...) {}
+
+alias T1 = S1!();
+
+deprecated template C(V...)
+{
+ int i;
+ int j;
+}
+
+alias D = C!();
+
+/*
+TEST_OUTPUT:
+----
+fail_compilation/deprecatedTemplates.d(202): Deprecation: template `deprecatedTemplates.AliasSeqMsg(V...)` is deprecated - Reason
+----
+*/
+#line 200
+deprecated("Reason") alias AliasSeqMsg(V...) = V;
+
+alias xMsg = AliasSeqMsg!(1, 2, 3);
+
+deprecated struct DS()
+{
+ S1!() s;
+}
+
+deprecated struct DS2()
+{
+ static struct DS3()
+ {
+ S1!() s;
+ }
+
+ static struct DS4
+ {
+ S1!() s;
+ }
+}
+
+deprecated void foo()
+{
+ DS!() d1;
+ DS2!().DS3!() d2;
+ DS2!().DS4 d3;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecateopdot.d b/gcc/testsuite/gdc.test/fail_compilation/deprecateopdot.d
new file mode 100644
index 00000000000..5aaab6a2b17
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/deprecateopdot.d
@@ -0,0 +1,30 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/deprecateopdot.d(27): Deprecation: `opDot` is deprecated. Use `alias this`
+fail_compilation/deprecateopdot.d(28): Deprecation: `opDot` is deprecated. Use `alias this`
+fail_compilation/deprecateopdot.d(29): Deprecation: `opDot` is deprecated. Use `alias this`
+---
+*/
+struct S6
+{
+ int a, b;
+}
+struct T6
+{
+ S6 s;
+
+ S6* opDot() return
+ {
+ return &s;
+ }
+}
+
+void test6()
+{
+ T6 t;
+ t.a = 4;
+ assert(t.a == 4);
+ t.b = 5;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecations.d b/gcc/testsuite/gdc.test/fail_compilation/deprecations.d
new file mode 100644
index 00000000000..19adab7e3ac
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/deprecations.d
@@ -0,0 +1,66 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/deprecations.d(43): Deprecation: struct `deprecations.S` is deprecated
+fail_compilation/deprecations.d(64): instantiated from here: `otherPar!()`
+fail_compilation/deprecations.d(55): Deprecation: struct `deprecations.S` is deprecated
+fail_compilation/deprecations.d(65): instantiated from here: `otherVar!()`
+fail_compilation/deprecations.d(55): Deprecation: struct `deprecations.S` is deprecated
+fail_compilation/deprecations.d(65): instantiated from here: `otherVar!()`
+---
+
+https://issues.dlang.org/show_bug.cgi?id=20474
+*/
+
+deprecated struct S {}
+
+deprecated void foo()(S par) if (is(S == S))
+{
+ S var;
+}
+
+deprecated template bar() if (is(S == S))
+{
+ void bar(S par)
+ {
+ S var;
+ }
+}
+
+deprecated void foobar (T) (T par) if (is(T == S))
+{
+ T inst;
+}
+
+template otherPar()
+{
+ deprecated void otherPar(S par)
+ {
+ S var;
+ }
+
+ void par(S par) {}
+}
+
+template otherVar()
+{
+ deprecated void otherVar(S par)
+ {
+ S var;
+ }
+
+ void var()
+ {
+ S var;
+ }
+}
+
+deprecated void main()
+{
+ foo(S.init);
+ bar(S.init);
+ foobar(S.init);
+ otherPar(S.init);
+ otherVar(S.init);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10089.d b/gcc/testsuite/gdc.test/fail_compilation/diag10089.d
index be0fddcad34..d79a49e427a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10089.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10089.d
@@ -1,8 +1,9 @@
/*
+EXTRA_FILES: imports/diag10089a.d imports/diag10089b.d
TEST_OUTPUT:
---
-fail_compilation/diag10089.d(15): Error: undefined identifier `chunks` in package `imports`
-fail_compilation/diag10089.d(17): Error: template Foo() does not have property 'chunks'
+fail_compilation/diag10089.d(16): Error: undefined identifier `chunks` in package `imports`
+fail_compilation/diag10089.d(18): Error: template `Foo()` does not have property `chunks`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10099.d b/gcc/testsuite/gdc.test/fail_compilation/diag10099.d
index a26ca85e19d..cf1b6459436 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10099.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10099.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10099.d(15): Error: variable diag10099.main.s default construction is disabled for type S
+fail_compilation/diag10099.d(15): Error: variable `diag10099.main.s` default construction is disabled for type `S`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10141.d b/gcc/testsuite/gdc.test/fail_compilation/diag10141.d
index e9ca913767f..a2ea0732702 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10141.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10141.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: imports/diag10141a.d imports/diag10141b.d
TEST_OUTPUT:
---
-fail_compilation/diag10141.d(9): Error: module imports.diag10141a import 'unexisting_symbol' not found
+fail_compilation/diag10141.d(10): Error: module `imports.diag10141a` import `unexisting_symbol` not found
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10169.d b/gcc/testsuite/gdc.test/fail_compilation/diag10169.d
index 3c35b66c045..72becf2b53a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10169.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10169.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: imports/a10169.d
TEST_OUTPUT:
---
-fail_compilation/diag10169.d(11): Error: no property `x` for type `B`, did you mean `imports.a10169.B.x`?
+fail_compilation/diag10169.d(12): Error: no property `x` for type `imports.a10169.B`
---
*/
import imports.a10169;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10319.d b/gcc/testsuite/gdc.test/fail_compilation/diag10319.d
index a11e120fa8e..4a01c5435b6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10319.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10319.d
@@ -1,13 +1,15 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10319.d(25): Error: pure function 'D main' cannot call impure function 'diag10319.foo'
-fail_compilation/diag10319.d(25): Error: @safe function 'D main' cannot call @system function 'diag10319.foo'
-fail_compilation/diag10319.d(26): Error: pure function 'D main' cannot call impure function 'diag10319.bar!int.bar'
-fail_compilation/diag10319.d(26): Error: @safe function 'D main' cannot call @system function 'diag10319.bar!int.bar'
-fail_compilation/diag10319.d(25): Error: function `diag10319.foo` is not nothrow
-fail_compilation/diag10319.d(26): Error: function `diag10319.bar!int.bar` is not nothrow
-fail_compilation/diag10319.d(23): Error: nothrow function `D main` may throw
+fail_compilation/diag10319.d(27): Error: `pure` function `D main` cannot call impure function `diag10319.foo`
+fail_compilation/diag10319.d(27): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo`
+fail_compilation/diag10319.d(16): `diag10319.foo` is declared here
+fail_compilation/diag10319.d(28): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar`
+fail_compilation/diag10319.d(28): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar`
+fail_compilation/diag10319.d(18): `diag10319.bar!int.bar` is declared here
+fail_compilation/diag10319.d(27): Error: function `diag10319.foo` is not `nothrow`
+fail_compilation/diag10319.d(28): Error: function `diag10319.bar!int.bar` is not `nothrow`
+fail_compilation/diag10319.d(25): Error: `nothrow` function `D main` may throw
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10405.d b/gcc/testsuite/gdc.test/fail_compilation/diag10405.d
index c80c821ebd0..28da8af93ad 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10405.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10405.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10405.d(10): Error: cannot return non-void from void function
+fail_compilation/diag10405.d(10): Error: cannot return non-void from `void` function
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10415.d b/gcc/testsuite/gdc.test/fail_compilation/diag10415.d
index a92b7a474ff..1fde171b713 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10415.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10415.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10415.d(36): Error: none of the overloads of 'x' are callable using argument types (int) const, candidates are:
-fail_compilation/diag10415.d(13): diag10415.C.x()
-fail_compilation/diag10415.d(18): diag10415.C.x(int _param_0)
+fail_compilation/diag10415.d(36): Error: none of the overloads of `x` are callable using argument types `(int) const`
+fail_compilation/diag10415.d(13): Candidates are: `diag10415.C.x()`
+fail_compilation/diag10415.d(18): `diag10415.C.x(int _param_0)`
fail_compilation/diag10415.d(39): Error: d.x is not an lvalue
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10688.d b/gcc/testsuite/gdc.test/fail_compilation/diag10688.d
index 70db7f96fe0..f7d7479b4df 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10688.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10688.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10688.d(12): Error: function diag10688.Bar.foo private method is not virtual and cannot override
-fail_compilation/diag10688.d(14): Error: function diag10688.Bar.bar package method is not virtual and cannot override
+fail_compilation/diag10688.d(12): Error: function `diag10688.Bar.foo` `private` method is not virtual and cannot override
+fail_compilation/diag10688.d(14): Error: function `diag10688.Bar.bar` `package` method is not virtual and cannot override
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10768.d b/gcc/testsuite/gdc.test/fail_compilation/diag10768.d
index 03b18a10689..bea52eae5ba 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10768.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10768.d
@@ -1,15 +1,14 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/diag10768.d(36): Error: cannot implicitly override base class method diag10768.Frop.frop with diag10768.Foo.frop; add 'override' attribute
+fail_compilation/diag10768.d(35): Error: cannot implicitly override base class method `diag10768.Frop.frop` with `diag10768.Foo.frop`; add `override` attribute
---
*/
struct CirBuff(T)
{
- import std.traits: isArray;
- CirBuff!T opAssign(R)(R) if (isArray!R)
+
+ CirBuff!T opAssign(R)(R)
{}
T[] toArray()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10783.d b/gcc/testsuite/gdc.test/fail_compilation/diag10783.d
index d74a66d3cee..f18341f0d03 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10783.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10783.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10783.d(14): Error: no property 'type' for type 'Event'
+fail_compilation/diag10783.d(14): Error: no property `type` for type `diag10783.Event`
fail_compilation/diag10783.d(14): Error: undefined identifier `En`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10792.d b/gcc/testsuite/gdc.test/fail_compilation/diag10792.d
index d54e50fee5a..3c514dd098a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10792.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10792.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10792.d(9): Error: semicolon expected following auto declaration, not `EOF`
+fail_compilation/diag10792.d(9): Error: semicolon expected following auto declaration, not `End of File`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10805.d b/gcc/testsuite/gdc.test/fail_compilation/diag10805.d
index 627fe825b53..3b5df6ebccf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10805.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10805.d
@@ -1,10 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10805.d(11): Error: delimited string must end in FOO"
-fail_compilation/diag10805.d(13): Error: unterminated string constant starting at fail_compilation/diag10805.d(13)
-fail_compilation/diag10805.d(13): Deprecation: Implicit string concatenation is deprecated, use "" ~ "" instead
-fail_compilation/diag10805.d(14): Error: semicolon expected following auto declaration, not `EOF`
+fail_compilation/diag10805.d(12): Error: delimited string must end in FOO"
+fail_compilation/diag10805.d(14): Error: unterminated string constant starting at fail_compilation/diag10805.d(14)
+fail_compilation/diag10805.d(14): Error: Implicit string concatenation is error-prone and disallowed in D
+fail_compilation/diag10805.d(14): Use the explicit syntax instead (concatenating literals is `@nogc`): "" ~ ""
+fail_compilation/diag10805.d(15): Error: semicolon expected following auto declaration, not `End of File`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10862.d b/gcc/testsuite/gdc.test/fail_compilation/diag10862.d
index 62968f28e43..00949f1d995 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10862.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10862.d
@@ -1,24 +1,36 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10862.d(28): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(29): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(30): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(31): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(32): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(34): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(35): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(36): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(37): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(39): Error: assignment cannot be used as a condition, perhaps `==` was meant?
fail_compilation/diag10862.d(40): Error: assignment cannot be used as a condition, perhaps `==` was meant?
fail_compilation/diag10862.d(41): Error: assignment cannot be used as a condition, perhaps `==` was meant?
fail_compilation/diag10862.d(42): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(43): Error: assignment cannot be used as a condition, perhaps `==` was meant?
fail_compilation/diag10862.d(44): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(45): Error: assignment cannot be used as a condition, perhaps `==` was meant?
fail_compilation/diag10862.d(46): Error: assignment cannot be used as a condition, perhaps `==` was meant?
fail_compilation/diag10862.d(47): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(49): Error: undefined identifier `semanticError`
+fail_compilation/diag10862.d(48): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(49): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(51): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(52): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(53): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(54): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(56): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(57): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(58): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(59): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(61): Error: undefined identifier `semanticError`
+fail_compilation/diag10862.d(71): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d(74): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d-mixin-77(77): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d-mixin-78(78): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d-mixin-79(79): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d-mixin-80(80): Error: Using the result of a comma expression is not allowed
+fail_compilation/diag10862.d-mixin-80(80): Error: assignment cannot be used as a condition, perhaps `==` was meant?
+fail_compilation/diag10862.d-mixin-83(83): Error: `a + b` is not an lvalue and cannot be modified
+fail_compilation/diag10862.d-mixin-84(84): Error: undefined identifier `c`
+fail_compilation/diag10862.d(86): Error: undefined identifier `semanticError`
+fail_compilation/diag10862.d(93): Error: lazy variable `bar` cannot be modified
+fail_compilation/diag10862.d(95): Error: template instance `diag10862.test3.foo!int` error instantiating
---
*/
void test1()
@@ -28,42 +40,27 @@ void test1()
if (a = b) {}
if ((a = b) = 0) {}
if ((a = b) = (a = b)) {}
- if (a = 0, b = 0) {} // Bugzilla 15384
+ if (a = 0, b = 0) {} // https://issues.dlang.org/show_bug.cgi?id=15384
if (auto x = a = b) {} // this is error, today
while (a = b) {}
while ((a = b) = 0) {}
while ((a = b) = (a = b)) {}
- while (a = 0, b = 0) {} // Bugzilla 15384
+ while (a = 0, b = 0) {} // https://issues.dlang.org/show_bug.cgi?id=15384
do {} while (a = b);
do {} while ((a = b) = 0);
do {} while ((a = b) = (a = b));
- do {} while (a = 0, b = 0); // Bugzilla 15384
+ do {} while (a = 0, b = 0); // https://issues.dlang.org/show_bug.cgi?id=15384
for (; a = b; ) {}
for (; (a = b) = 0; ) {}
for (; (a = b) = (a = b); ) {}
- for (; a = 0, b = 0; ) {} // Bugzilla 15384
+ for (; a = 0, b = 0; ) {} // https://issues.dlang.org/show_bug.cgi?id=15384
semanticError;
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/diag10862.d(74): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d(77): Error: assignment cannot be used as a condition, perhaps `==` was meant?
-fail_compilation/diag10862.d-mixin-80(80): Error: assignment cannot be used as a condition, perhaps == was meant?
-fail_compilation/diag10862.d-mixin-81(81): Error: assignment cannot be used as a condition, perhaps == was meant?
-fail_compilation/diag10862.d-mixin-82(82): Error: assignment cannot be used as a condition, perhaps == was meant?
-fail_compilation/diag10862.d-mixin-83(83): Deprecation: Using the result of a comma expression is deprecated
-fail_compilation/diag10862.d-mixin-83(83): Error: assignment cannot be used as a condition, perhaps == was meant?
-fail_compilation/diag10862.d-mixin-86(86): Error: a + b is not an lvalue
-fail_compilation/diag10862.d-mixin-87(87): Error: undefined identifier `c`
-fail_compilation/diag10862.d(89): Error: undefined identifier `semanticError`
----
-*/
void test2()
{
int a, b;
@@ -88,3 +85,12 @@ void test2()
semanticError;
}
+
+void test3()
+{
+ void foo(T)(lazy T bar)
+ {
+ bar = 2;
+ }
+ foo(1 + 1);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10926.d b/gcc/testsuite/gdc.test/fail_compilation/diag10926.d
index bd590ba2151..f98a5b27dea 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10926.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10926.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10926.d(11): Error: cast(const(int)[])c is not an lvalue
+fail_compilation/diag10926.d(11): Error: `cast(const(int)[])c` is not an lvalue and cannot be modified
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10984.d b/gcc/testsuite/gdc.test/fail_compilation/diag10984.d
index 33e0eaf25d9..c3d835c0433 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag10984.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag10984.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag10984.d(11): Error: static function diag10984.f.n cannot access frame of function diag10984.f
+fail_compilation/diag10984.d(12): Error: `static` function `diag10984.f.n` cannot access variable `x` in frame of function `diag10984.f`
+fail_compilation/diag10984.d(11): `x` declared here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11078.d b/gcc/testsuite/gdc.test/fail_compilation/diag11078.d
index 7507875c947..334ce16651f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag11078.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag11078.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag11078.d(19): Error: none of the overloads of 'value' are callable using argument types (double), candidates are:
-fail_compilation/diag11078.d(12): diag11078.S1.value()
-fail_compilation/diag11078.d(13): diag11078.S1.value(int n)
+fail_compilation/diag11078.d(19): Error: none of the overloads of `value` are callable using argument types `(double)`
+fail_compilation/diag11078.d(12): Candidates are: `diag11078.S1.value()`
+fail_compilation/diag11078.d(13): `diag11078.S1.value(int n)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11132.d b/gcc/testsuite/gdc.test/fail_compilation/diag11132.d
index 74062c2dc2e..64db64d1e97 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag11132.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag11132.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag11132.d(22): Error: overlapping initialization for field a and b
+fail_compilation/diag11132.d(22): Error: overlapping initialization for field `a` and `b`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11425.d b/gcc/testsuite/gdc.test/fail_compilation/diag11425.d
index 16c99cc4116..f8edc5c7867 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag11425.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag11425.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag11425.d(13): Error: variable x is shadowing variable diag11425.main.x
+fail_compilation/diag11425.d(13): Error: variable `x` is shadowing variable `diag11425.main.x`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11727.d b/gcc/testsuite/gdc.test/fail_compilation/diag11727.d
index 907c9bb404d..44cd65ae8aa 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag11727.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag11727.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag11727.d(10): Error: type n is not an expression
+fail_compilation/diag11727.d(10): Error: type `n` is not an expression
---
*/
auto returnEnum()
@@ -17,7 +17,7 @@ void main()
/*
TEST_OUTPUT:
---
-fail_compilation/diag11727.d(26): Error: type void is not an expression
+fail_compilation/diag11727.d(26): Error: type `void` is not an expression
---
*/
auto returnVoid()
@@ -29,7 +29,7 @@ auto returnVoid()
/*
TEST_OUTPUT:
---
-fail_compilation/diag11727.d(38): Error: template t() has no type
+fail_compilation/diag11727.d(38): Error: template `t()` has no type
---
*/
auto returnTemplate()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11756.d b/gcc/testsuite/gdc.test/fail_compilation/diag11756.d
index 3d56b99564d..3a0724792c6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag11756.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag11756.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag11756.d(15): Error: cannot read uninitialized variable cnt in CTFE
-fail_compilation/diag11756.d(34): called from here: foo.ptr2.opAssign(Ptr(& n))
-fail_compilation/diag11756.d(39): called from here: test()
+fail_compilation/diag11756.d(15): Error: cannot read uninitialized variable `cnt` in CTFE
+fail_compilation/diag11756.d(34): called from here: `foo.ptr2.opAssign(Ptr(& n))`
+fail_compilation/diag11756.d(39): called from here: `test()`
fail_compilation/diag11756.d(39): while evaluating: `static assert(test())`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11769.d b/gcc/testsuite/gdc.test/fail_compilation/diag11769.d
index bfe4e1b4c8d..2717de4a6e7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag11769.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag11769.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag11769.d(18): Error: diag11769.foo!string.bar called with argument types (string) matches both:
-fail_compilation/diag11769.d(13): diag11769.foo!string.bar(wstring _param_0)
+fail_compilation/diag11769.d(18): Error: `diag11769.foo!string.bar` called with argument types `(string)` matches both:
+fail_compilation/diag11769.d(13): `diag11769.foo!string.bar(wstring _param_0)`
and:
-fail_compilation/diag11769.d(14): diag11769.foo!string.bar(dstring _param_0)
+fail_compilation/diag11769.d(14): `diag11769.foo!string.bar(dstring _param_0)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12063.d b/gcc/testsuite/gdc.test/fail_compilation/diag12063.d
index e029810b1dc..882a8091076 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12063.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12063.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag12063.d(11): Error: no property 'max' for type 'Foo'
-fail_compilation/diag12063.d(14): Error: incompatible types for ((Foo()) + (1)): 'Bar' and 'int'
+fail_compilation/diag12063.d(11): Error: no property `max` for type `Foo`, perhaps `import std.algorithm;` is needed?
+fail_compilation/diag12063.d(14): Error: incompatible types for `(Foo()) + (1)`: `Bar` and `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12124.d b/gcc/testsuite/gdc.test/fail_compilation/diag12124.d
index 320cfb2291e..f9f165f06b0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12124.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12124.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag12124.d(14): Error: struct diag12124.S1 static opCall is hidden by constructors and can never be called
-fail_compilation/diag12124.d(14): Please use a factory method instead, or replace all constructors with static opCall.
-fail_compilation/diag12124.d(20): Error: struct diag12124.S2 static opCall is hidden by constructors and can never be called
-fail_compilation/diag12124.d(20): Please use a factory method instead, or replace all constructors with static opCall.
+fail_compilation/diag12124.d(14): Error: struct `diag12124.S1` `static opCall` is hidden by constructors and can never be called
+fail_compilation/diag12124.d(14): Please use a factory method instead, or replace all constructors with `static opCall`.
+fail_compilation/diag12124.d(20): Error: struct `diag12124.S2` `static opCall` is hidden by constructors and can never be called
+fail_compilation/diag12124.d(20): Please use a factory method instead, or replace all constructors with `static opCall`.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12280.d b/gcc/testsuite/gdc.test/fail_compilation/diag12280.d
index f125ff31b81..8fba6151bbe 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12280.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12280.d
@@ -2,8 +2,8 @@
TEST_OUTPUT:
---
fail_compilation/diag12280.d(15): Error: undefined identifier `nonexistent`
-fail_compilation/diag12280.d(13): Error: template instance diag12280.f!10 error instantiating
-fail_compilation/diag12280.d(18): 11 recursive instantiations from here: f!0
+fail_compilation/diag12280.d(13): Error: template instance `diag12280.f!10` error instantiating
+fail_compilation/diag12280.d(18): 11 recursive instantiations from here: `f!0`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12312.d b/gcc/testsuite/gdc.test/fail_compilation/diag12312.d
index 7120a8f0d21..e015cfea7ac 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12312.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12312.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag12312.d(10): Error: variable diag12312.main.arr void[16] does not have a default initializer
+fail_compilation/diag12312.d(10): Error: variable `diag12312.main.arr` `void[16]` does not have a default initializer
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12380.d b/gcc/testsuite/gdc.test/fail_compilation/diag12380.d
index f6434f05d96..7f59f275e22 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12380.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12380.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag12380.d(12): Error: cannot implicitly convert expression `cast(E)0` of type `E` to `void*`
+fail_compilation/diag12380.d(12): Error: cannot implicitly convert expression `E.a` of type `E` to `void*`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12480.d b/gcc/testsuite/gdc.test/fail_compilation/diag12480.d
index 1989874d200..01c2e4e4650 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12480.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12480.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag12480.d(12): Error: static assert `2u == 3u` is false
+fail_compilation/diag12480.d(12): Error: static assert: `2u == 3u` is false
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12487.d b/gcc/testsuite/gdc.test/fail_compilation/diag12487.d
index b9193231e8a..8ea103eef41 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12487.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12487.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag12487.d(15): Error: recursive expansion of template instance 'diag12487.recTemplate!int'
-fail_compilation/diag12487.d(25): Error: template instance diag12487.recTemplate!int error instantiating
-fail_compilation/diag12487.d(18): Error: function diag12487.recFunction CTFE recursion limit exceeded
-fail_compilation/diag12487.d(20): called from here: recFunction(i)
-fail_compilation/diag12487.d(18): 1000 recursive calls to function recFunction
-fail_compilation/diag12487.d(27): called from here: recFunction(0)
+fail_compilation/diag12487.d(15): Error: recursive expansion of template instance `diag12487.recTemplate!int`
+fail_compilation/diag12487.d(25): Error: template instance `diag12487.recTemplate!int` error instantiating
+fail_compilation/diag12487.d(18): Error: function `diag12487.recFunction` CTFE recursion limit exceeded
+fail_compilation/diag12487.d(20): called from here: `recFunction(i)`
+fail_compilation/diag12487.d(18): 1000 recursive calls to function `recFunction`
+fail_compilation/diag12487.d(27): called from here: `recFunction(0)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12678.d b/gcc/testsuite/gdc.test/fail_compilation/diag12678.d
index afe56fb51b3..8b17968fdbd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12678.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12678.d
@@ -1,9 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag12678.d(19): Error: const field 'cf1' initialized multiple times
-fail_compilation/diag12678.d(22): Error: immutable field 'if1' initialized multiple times
-fail_compilation/diag12678.d(25): Error: const field 'cf2' initialization is not allowed in loops or after labels
+fail_compilation/diag12678.d(21): Error: const field `cf1` initialized multiple times
+fail_compilation/diag12678.d(20): Previous initialization is here.
+fail_compilation/diag12678.d(24): Error: immutable field `if1` initialized multiple times
+fail_compilation/diag12678.d(23): Previous initialization is here.
+fail_compilation/diag12678.d(27): Error: const field `cf2` initialization is not allowed in loops or after labels
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12777.d b/gcc/testsuite/gdc.test/fail_compilation/diag12777.d
index 1eefd29bf05..dd4321bb3ff 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12777.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12777.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag12777.d(14): Error: cannot modify this.v in const function
-fail_compilation/diag12777.d(15): Error: cannot modify this.v in immutable function
-fail_compilation/diag12777.d(21): Error: cannot modify this.v in const function
-fail_compilation/diag12777.d(22): Error: cannot modify this.v in immutable function
+fail_compilation/diag12777.d(14): Error: cannot modify `this.v` in `const` function
+fail_compilation/diag12777.d(15): Error: cannot modify `this.v` in `immutable` function
+fail_compilation/diag12777.d(21): Error: cannot modify `this.v` in `const` function
+fail_compilation/diag12777.d(22): Error: cannot modify `this.v` in `immutable` function
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12829.d b/gcc/testsuite/gdc.test/fail_compilation/diag12829.d
index dcaa9f04b82..f6e764bd98d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag12829.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag12829.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag12829.d(12): Error: function diag12829.test1 is @nogc yet allocates closures with the GC
-fail_compilation/diag12829.d(15): diag12829.test1.__lambda1 closes over variable x at fail_compilation/diag12829.d(14)
+fail_compilation/diag12829.d(12): Error: function `diag12829.test1` is `@nogc` yet allocates closures with the GC
+fail_compilation/diag12829.d(15): diag12829.test1.__lambda2 closes over variable x at fail_compilation/diag12829.d(14)
fail_compilation/diag12829.d(19): diag12829.test1.bar closes over variable x at fail_compilation/diag12829.d(14)
-fail_compilation/diag12829.d(26): Error: function diag12829.test2 is @nogc yet allocates closures with the GC
+fail_compilation/diag12829.d(26): Error: function `diag12829.test2` is `@nogc` yet allocates closures with the GC
fail_compilation/diag12829.d(31): diag12829.test2.S.foo closes over variable x at fail_compilation/diag12829.d(28)
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13028.d b/gcc/testsuite/gdc.test/fail_compilation/diag13028.d
index d26cb946d7e..6ef11aa1268 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag13028.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag13028.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag13028.d(15): Error: variable dg cannot be read at compile time
-fail_compilation/diag13028.d(22): Error: variable a cannot be read at compile time
-fail_compilation/diag13028.d(28): Error: CTFE failed because of previous errors in foo
-fail_compilation/diag13028.d(28): while evaluating: `static assert(foo(() => 1) == 1)`
-fail_compilation/diag13028.d(29): Error: CTFE failed because of previous errors in bar
-fail_compilation/diag13028.d(29): while evaluating: `static assert(bar(delegate int() => 1) == 1)`
+fail_compilation/diag13028.d(15): Error: variable `dg` cannot be read at compile time
+fail_compilation/diag13028.d(22): Error: variable `a` cannot be read at compile time
+fail_compilation/diag13028.d(28): Error: CTFE failed because of previous errors in `foo`
+fail_compilation/diag13028.d(28): while evaluating: `static assert(foo(() pure nothrow @nogc @safe => 1) == 1)`
+fail_compilation/diag13028.d(29): Error: CTFE failed because of previous errors in `bar`
+fail_compilation/diag13028.d(29): while evaluating: `static assert(bar(delegate int() pure nothrow @nogc @safe => 1) == 1)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13215.d b/gcc/testsuite/gdc.test/fail_compilation/diag13215.d
new file mode 100644
index 00000000000..3ae50c9ae1c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag13215.d
@@ -0,0 +1,12 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag13215.d(11): Error: cannot implicitly convert expression `[1, 2, 3]` of type `int[]` to `immutable(uint[2])[]`
+---
+*/
+
+enum uint N = 10;
+immutable uint[2][3] arr2;
+shared static this() {
+ arr2 = [1, 2, 3];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13320.d b/gcc/testsuite/gdc.test/fail_compilation/diag13320.d
index 46a00320aaa..2808606bdca 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag13320.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag13320.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag13320.d(13): Error: 'f += 1' is not a scalar, it is a Foo
+fail_compilation/diag13320.d(13): Error: `f` is not a scalar, it is a `Foo`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13333.d b/gcc/testsuite/gdc.test/fail_compilation/diag13333.d
index f318a31ae72..34eeaebedd7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag13333.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag13333.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT*
---
-fail_compilation/diag13333.d(29): Error: template instance VariantN!(maxSize!(S), T) recursive template expansion
-fail_compilation/diag13333.d(29): Error: template instance diag13333.maxSize!(S) error instantiating
-fail_compilation/diag13333.d(34): instantiated from here: Algebraic!(S)
+fail_compilation/diag13333.d(29): Error: template instance `VariantN!(maxSize!(S), T)` recursive template expansion
+fail_compilation/diag13333.d(29): Error: template instance `diag13333.maxSize!(S)` error instantiating
+fail_compilation/diag13333.d(34): instantiated from here: `Algebraic!(S)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13528.d b/gcc/testsuite/gdc.test/fail_compilation/diag13528.d
index 493cbc7fff3..5d908f7cbf3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag13528.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag13528.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag13528.d(13): Error: value of 'this' is not known at compile time
-fail_compilation/diag13528.d(13): while evaluating pragma(msg, __traits(getMember, A, "foo"))
+fail_compilation/diag13528.d(13): Error: value of `this` is not known at compile time
+fail_compilation/diag13528.d(13): while evaluating `pragma(msg, __traits(getMember, A, "foo"))`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13609b.d b/gcc/testsuite/gdc.test/fail_compilation/diag13609b.d
index dccb9c7cb3c..86690fcfad3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag13609b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag13609b.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/diag13609b.d(10): Error: base classes are not allowed for `struct`, did you mean `;`?
-fail_compilation/diag13609b.d(11): Error: basic type expected, not `EOF`
+fail_compilation/diag13609b.d(11): Error: basic type expected, not `End of File`
fail_compilation/diag13609b.d(11): Error: { } expected following `struct` declaration
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13787.d b/gcc/testsuite/gdc.test/fail_compilation/diag13787.d
index 99162edc144..3a3a2c965b8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag13787.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag13787.d
@@ -2,8 +2,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag13787.d(12): Error: cannot slice function pointer & main
-fail_compilation/diag13787.d(13): Error: cannot index function pointer & main
+fail_compilation/diag13787.d(12): Error: cannot slice function pointer `& main`
+fail_compilation/diag13787.d(13): Error: cannot index function pointer `& main`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13884.d b/gcc/testsuite/gdc.test/fail_compilation/diag13884.d
index 71909e28869..fe47c838dd9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag13884.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag13884.d
@@ -2,8 +2,8 @@
TEST_OUTPUT:
---
fail_compilation/diag13884.d(14): Error: functions cannot return a tuple
-fail_compilation/diag13884.d(21): instantiated from here: MapResult!((t) => t.tupleof, Foo[])
-fail_compilation/diag13884.d(14): instantiated from here: map!(Foo[])
+fail_compilation/diag13884.d(21): instantiated from here: `MapResult!((t) => t.tupleof, Foo[])`
+fail_compilation/diag13884.d(14): instantiated from here: `map!(Foo[])`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13942.d b/gcc/testsuite/gdc.test/fail_compilation/diag13942.d
index 9abea3c0309..992d5b8aadc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag13942.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag13942.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag13942.d(18): Error: template instance isRawStaticArray!() does not match template declaration isRawStaticArray(T, A...)
-fail_compilation/diag13942.d(26): Error: template diag13942.to!double.to cannot deduce function from argument types !()(), candidates are:
-fail_compilation/diag13942.d(17): diag13942.to!double.to(A...)(A args) if (!isRawStaticArray!A)
+fail_compilation/diag13942.d(18): Error: template instance `isRawStaticArray!()` does not match template declaration `isRawStaticArray(T, A...)`
+fail_compilation/diag13942.d(26): Error: template `diag13942.to!double.to` cannot deduce function from argument types `!()()`
+fail_compilation/diag13942.d(17): Candidate is: `to(A...)(A args)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag14102.d b/gcc/testsuite/gdc.test/fail_compilation/diag14102.d
index c142b9727f5..e93d40b224e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag14102.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag14102.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag14102.d(14): Error: -x is not an lvalue
-fail_compilation/diag14102.d(15): Error: -(x -= 1) is not an lvalue
-fail_compilation/diag14102.d(16): Error: -(x -= 1 -= 1) is not an lvalue
-fail_compilation/diag14102.d(17): Error: -(x -= 1 -= 1 -= 1) is not an lvalue
+fail_compilation/diag14102.d(14): Error: `-x` is not an lvalue and cannot be modified
+fail_compilation/diag14102.d(15): Error: `-(x -= 1)` is not an lvalue and cannot be modified
+fail_compilation/diag14102.d(16): Error: `-(x -= 1 -= 1)` is not an lvalue and cannot be modified
+fail_compilation/diag14102.d(17): Error: `-(x -= 1 -= 1 -= 1)` is not an lvalue and cannot be modified
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag14145.d b/gcc/testsuite/gdc.test/fail_compilation/diag14145.d
new file mode 100644
index 00000000000..d292f768f75
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag14145.d
@@ -0,0 +1,38 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag14145.d(15): Error: no property `i` for type `diag14145.main.Capture!(i)`
+fail_compilation/diag14145.d(15): potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message
+fail_compilation/diag14145.d(34): Error: expression `*this.ptr` of type `shared(int)` is not implicitly convertible to return type `ref int`
+fail_compilation/diag14145.d(16): Error: template instance `diag14145.main.Capture!(i).Capture.opDispatch!"i"` error instantiating
+---
+*/
+
+int main()
+{
+ int i;
+ auto _ = capture!i;
+ _.i;
+ _.opDispatch!"i";
+ return 0;
+}
+
+auto capture(alias c)()
+{
+ return Capture!c(c);
+}
+
+struct Capture(alias c)
+{
+ shared typeof(c)* ptr;
+ this(ref typeof(c) _c)
+ {
+ ptr = cast(shared)&c;
+ }
+ ref shared typeof(c) opDispatch(string s)()
+ {
+ return *ptr;
+ }
+}
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag14163.d b/gcc/testsuite/gdc.test/fail_compilation/diag14163.d
index 8fe63433eb6..eaafc075680 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag14163.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag14163.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag14163.d(16): Error: constructor diag14163.Bar.this cannot call super() implicitly because it is annotated with @disable
+fail_compilation/diag14163.d(16): Error: constructor `diag14163.Bar.this` cannot call `super()` implicitly because it is annotated with `@disable`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag14235.d b/gcc/testsuite/gdc.test/fail_compilation/diag14235.d
index 4880bdf3e05..3c8a98e6e35 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag14235.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag14235.d
@@ -1,9 +1,10 @@
/*
+EXTRA_FILES: imports/a14235.d
TEST_OUTPUT:
---
-fail_compilation/diag14235.d(11): Error: template identifier 'Undefined' is not a member of module 'imports.a14235'
-fail_compilation/diag14235.d(12): Error: template identifier 'Something' is not a member of module 'imports.a14235', did you mean struct 'SomeThing(T...)'?
-fail_compilation/diag14235.d(13): Error: imports.a14235.SomeClass is not a template, it is a class
+fail_compilation/diag14235.d(12): Error: template identifier `Undefined` is not a member of module `imports.a14235`
+fail_compilation/diag14235.d(13): Error: template identifier `Something` is not a member of module `imports.a14235`, did you mean struct `SomeThing(T...)`?
+fail_compilation/diag14235.d(14): Error: `imports.a14235.SomeClass` is not a template, it is a class
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag14818.d b/gcc/testsuite/gdc.test/fail_compilation/diag14818.d
index 4eef7487692..660066af555 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag14818.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag14818.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag14818.d(34): Error: none of the overloads of 'func' are callable using argument types (string), candidates are:
-fail_compilation/diag14818.d(12): diag14818.foo(int _param_0)
-fail_compilation/diag14818.d(13): diag14818.bar(double _param_0)
-fail_compilation/diag14818.d(35): Error: overload alias diag14818.X does not match any template declaration
-fail_compilation/diag14818.d(36): Error: overloadset diag14818.M does not match any template declaration
+fail_compilation/diag14818.d(34): Error: none of the overloads of `func` are callable using argument types `(string)`
+fail_compilation/diag14818.d(12): Candidate is: `diag14818.foo(int _param_0)`
+fail_compilation/diag14818.d(13): `diag14818.bar(double _param_0)`
+fail_compilation/diag14818.d(35): Error: overload alias `diag14818.X` does not match any template declaration
+fail_compilation/diag14818.d(36): Error: overloadset `diag14818.M` does not match any template declaration
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag14875.d b/gcc/testsuite/gdc.test/fail_compilation/diag14875.d
index 52a6127b021..a4d4abe9e19 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag14875.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag14875.d
@@ -6,7 +6,7 @@ deprecated immutable int depVar = 10;
/*
TEST_OUTPUT:
---
-fail_compilation/diag14875.d(16): Deprecation: class diag14875.Dep is deprecated
+fail_compilation/diag14875.d(16): Deprecation: class `diag14875.Dep` is deprecated
1: Dep
2: Dep
3: Dep
@@ -36,11 +36,16 @@ template Baz(T)
/*
TEST_OUTPUT:
---
-fail_compilation/diag14875.d(47): Deprecation: class diag14875.Dep is deprecated
-fail_compilation/diag14875.d(51): Deprecation: variable diag14875.depVar is deprecated
+fail_compilation/diag14875.d(52): Deprecation: class `diag14875.Dep` is deprecated
+fail_compilation/diag14875.d(56): Deprecation: variable `diag14875.depVar` is deprecated
+fail_compilation/diag14875.d(52): instantiated from here: `Voo!(Dep)`
4: Dep
-fail_compilation/diag14875.d(58): Deprecation: variable diag14875.depVar is deprecated
-fail_compilation/diag14875.d(59): Deprecation: variable diag14875.Vaz!(Dep).Vaz is deprecated
+fail_compilation/diag14875.d(63): Deprecation: variable `diag14875.depVar` is deprecated
+fail_compilation/diag14875.d(59): instantiated from here: `Var!(Dep)`
+fail_compilation/diag14875.d(52): instantiated from here: `Voo!(Dep)`
+fail_compilation/diag14875.d(64): Deprecation: template `diag14875.Vaz(T)` is deprecated
+fail_compilation/diag14875.d(59): instantiated from here: `Var!(Dep)`
+fail_compilation/diag14875.d(52): instantiated from here: `Voo!(Dep)`
---
*/
@@ -67,7 +72,7 @@ deprecated template Vaz(T)
/*
TEST_OUTPUT:
---
-fail_compilation/diag14875.d(75): Error: static assert `0` is false
+fail_compilation/diag14875.d(80): Error: static assert: `0` is false
---
*/
void main()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag14876.d b/gcc/testsuite/gdc.test/fail_compilation/diag14876.d
index dcc440f003b..0ca03602f97 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag14876.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag14876.d
@@ -1,14 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag14876.d(17): Deprecation: class diag14876.Dep is deprecated
-fail_compilation/diag14876.d(18): Deprecation: class diag14876.Dep is deprecated
-fail_compilation/diag14876.d(19): Deprecation: class diag14876.Dep is deprecated
-fail_compilation/diag14876.d(20): Deprecation: class diag14876.Dep is deprecated
-fail_compilation/diag14876.d(21): Deprecation: class diag14876.Dep is deprecated
-fail_compilation/diag14876.d(22): Deprecation: class diag14876.Dep is deprecated
-fail_compilation/diag14876.d(23): Deprecation: class diag14876.Dep is deprecated
-fail_compilation/diag14876.d(23): Error: can only slice tuple types, not diag14876.Dep
+fail_compilation/diag14876.d(17): Deprecation: class `diag14876.Dep` is deprecated
+fail_compilation/diag14876.d(18): Deprecation: class `diag14876.Dep` is deprecated
+fail_compilation/diag14876.d(19): Deprecation: class `diag14876.Dep` is deprecated
+fail_compilation/diag14876.d(20): Deprecation: class `diag14876.Dep` is deprecated
+fail_compilation/diag14876.d(21): Deprecation: class `diag14876.Dep` is deprecated
+fail_compilation/diag14876.d(22): Deprecation: class `diag14876.Dep` is deprecated
+fail_compilation/diag14876.d(23): Deprecation: class `diag14876.Dep` is deprecated
+fail_compilation/diag14876.d(23): Error: can only slice tuple types, not `diag14876.Dep`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag15209.d b/gcc/testsuite/gdc.test/fail_compilation/diag15209.d
index 341e02653b3..9a4f396d957 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag15209.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag15209.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag15209.d(18): Error: need 'this' for 'x' of type 'int'
-fail_compilation/diag15209.d(21): Error: need 'this' for 'x' of type 'int'
+fail_compilation/diag15209.d(18): Error: need `this` for `x` of type `int`
+fail_compilation/diag15209.d(21): Error: need `this` for `x` of type `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag15411.d b/gcc/testsuite/gdc.test/fail_compilation/diag15411.d
index bc77d81a8ba..8b18c19d027 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag15411.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag15411.d
@@ -2,8 +2,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag15411.d(13): Error: function diag15411.test15411.__funcliteral1 cannot access frame of function diag15411.test15411
-fail_compilation/diag15411.d(14): Error: function diag15411.test15411.__funcliteral2 cannot access frame of function diag15411.test15411
+fail_compilation/diag15411.d(17): Error: function `diag15411.test15411.__funcliteral2` cannot access variable `i` in frame of function `diag15411.test15411`
+fail_compilation/diag15411.d(16): `i` declared here
+fail_compilation/diag15411.d(18): Error: function `diag15411.test15411.__funcliteral4` cannot access variable `i` in frame of function `diag15411.test15411`
+fail_compilation/diag15411.d(16): `i` declared here
+fail_compilation/diag15411.d(26): Error: `static` function `diag15411.testNestedFunction.myFunc2` cannot access function `myFunc1` in frame of function `diag15411.testNestedFunction`
+fail_compilation/diag15411.d(25): `myFunc1` declared here
---
*/
@@ -13,3 +17,11 @@ void test15411()
auto j = (function() { return i; })();
auto f = function() { return i; };
}
+
+void testNestedFunction ()
+{
+ int i = 42;
+
+ void myFunc1() { assert(i == 42); }
+ static void myFunc2 () { myFunc1(); }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag15669.d b/gcc/testsuite/gdc.test/fail_compilation/diag15669.d
index a2ab4aa2d17..682768528d2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag15669.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag15669.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag15669.d(14): Error: variable __b_field_0 cannot be read at compile time
+fail_compilation/diag15669.d(14): Error: variable `__b_field_0` cannot be read at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag15713.d b/gcc/testsuite/gdc.test/fail_compilation/diag15713.d
index a7714c9d852..34fc645061b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag15713.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag15713.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag15713.d(18): Error: no property 'widthSign' for type 'Data'
-fail_compilation/diag15713.d(38): Error: template instance test.conwritefImpl!("parse-int", "width", "\x0a", Data()) error instantiating
-fail_compilation/diag15713.d(43): instantiated from here: conwritefImpl!("main", "\x0a", Data())
-fail_compilation/diag15713.d(48): instantiated from here: fdwritef!()
+fail_compilation/diag15713.d(19): Error: no property `widthSign` for type `diag15713.WrData.Data`
+fail_compilation/diag15713.d(39): Error: template instance `diag15713.conwritefImpl!("parse-int", "width", "\x0a", Data(null))` error instantiating
+fail_compilation/diag15713.d(44): instantiated from here: `conwritefImpl!("main", "\x0a", Data(null))`
+fail_compilation/diag15713.d(49): instantiated from here: `fdwritef!()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag15974.d b/gcc/testsuite/gdc.test/fail_compilation/diag15974.d
index a6b30773180..03f63f4c21a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag15974.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag15974.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag15974.d(21): Error: variable f cannot be read at compile time
-fail_compilation/diag15974.d(21): called from here: format("%s", f)
-fail_compilation/diag15974.d(26): Error: variable f cannot be read at compile time
-fail_compilation/diag15974.d(26): called from here: format("%s", f)
+fail_compilation/diag15974.d(21): Error: variable `f` cannot be read at compile time
+fail_compilation/diag15974.d(21): called from here: `format("%s", f)`
+fail_compilation/diag15974.d(26): Error: variable `f` cannot be read at compile time
+fail_compilation/diag15974.d(26): called from here: `format("%s", f)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag16499.d b/gcc/testsuite/gdc.test/fail_compilation/diag16499.d
index 5d0c6ff581f..63b4b3c5279 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag16499.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag16499.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag16499.d(22): Error: incompatible types for ((2) in (foo)): 'int' and 'A'
-fail_compilation/diag16499.d(24): Error: incompatible types for ((1.00000) in (bar)): 'double' and 'B'
+fail_compilation/diag16499.d(22): Error: incompatible types for `(2) in (foo)`: `int` and `A`
+fail_compilation/diag16499.d(24): Error: incompatible types for `(1.0) in (bar)`: `double` and `B`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag16977.d b/gcc/testsuite/gdc.test/fail_compilation/diag16977.d
index 0754fc6a8e2..8f991a71edd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag16977.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag16977.d
@@ -1,12 +1,15 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag16977.d(22): Error: undefined identifier `undefined`, did you mean function `undefinedId`?
-fail_compilation/diag16977.d(23): Error: cannot implicitly convert expression `"\x01string"` of type `string` to `int`
-fail_compilation/diag16977.d(24): Error: template diag16977.templ cannot deduce function from argument types !()(int), candidates are:
-fail_compilation/diag16977.d(17): diag16977.templ(S)(S s) if (false)
-fail_compilation/diag16977.d(25): Error: cannot implicitly convert expression `5` of type `int` to `string`
-fail_compilation/diag16977.d(27): Error: template instance diag16977.test.funcTemplate!string error instantiating
+fail_compilation/diag16977.d(25): Error: undefined identifier `undefined`, did you mean function `undefinedId`?
+fail_compilation/diag16977.d(26): Error: cannot implicitly convert expression `"\x01string"` of type `string` to `int`
+fail_compilation/diag16977.d(27): Error: template `diag16977.templ` cannot deduce function from argument types `!()(int)`
+fail_compilation/diag16977.d(20): Candidate is: `templ(S)(S s)`
+ with `S = int`
+ must satisfy the following constraint:
+` false`
+fail_compilation/diag16977.d(28): Error: cannot implicitly convert expression `5` of type `int` to `string`
+fail_compilation/diag16977.d(30): Error: template instance `diag16977.test.funcTemplate!string` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag1730.d b/gcc/testsuite/gdc.test/fail_compilation/diag1730.d
index 697cd5af294..a17479c2a5c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag1730.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag1730.d
@@ -1,28 +1,41 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag1730.d(38): Error: mutable method diag1730.S.func is not callable using a inout object
-fail_compilation/diag1730.d(40): Error: immutable method diag1730.S.iFunc is not callable using a inout object
-fail_compilation/diag1730.d(41): Error: shared mutable method diag1730.S.sFunc is not callable using a non-shared inout object
-fail_compilation/diag1730.d(42): Error: shared const method diag1730.S.scFunc is not callable using a non-shared inout object
-fail_compilation/diag1730.d(57): Error: immutable method diag1730.S.iFunc is not callable using a mutable object
-fail_compilation/diag1730.d(58): Error: shared method diag1730.S.sFunc is not callable using a non-shared object
-fail_compilation/diag1730.d(59): Error: shared const method diag1730.S.scFunc is not callable using a non-shared mutable object
-fail_compilation/diag1730.d(62): Error: mutable method diag1730.S.func is not callable using a const object
-fail_compilation/diag1730.d(64): Error: immutable method diag1730.S.iFunc is not callable using a const object
-fail_compilation/diag1730.d(65): Error: shared mutable method diag1730.S.sFunc is not callable using a non-shared const object
-fail_compilation/diag1730.d(66): Error: shared const method diag1730.S.scFunc is not callable using a non-shared const object
-fail_compilation/diag1730.d(69): Error: mutable method diag1730.S.func is not callable using a immutable object
-fail_compilation/diag1730.d(72): Error: shared mutable method diag1730.S.sFunc is not callable using a immutable object
-fail_compilation/diag1730.d(76): Error: non-shared method diag1730.S.func is not callable using a shared object
-fail_compilation/diag1730.d(77): Error: non-shared const method diag1730.S.cFunc is not callable using a shared mutable object
-fail_compilation/diag1730.d(78): Error: immutable method diag1730.S.iFunc is not callable using a shared mutable object
-fail_compilation/diag1730.d(81): Error: non-shared inout method diag1730.S.wFunc is not callable using a shared mutable object
-fail_compilation/diag1730.d(83): Error: non-shared mutable method diag1730.S.func is not callable using a shared const object
-fail_compilation/diag1730.d(84): Error: non-shared const method diag1730.S.cFunc is not callable using a shared const object
-fail_compilation/diag1730.d(85): Error: immutable method diag1730.S.iFunc is not callable using a shared const object
-fail_compilation/diag1730.d(86): Error: shared mutable method diag1730.S.sFunc is not callable using a shared const object
-fail_compilation/diag1730.d(88): Error: non-shared inout method diag1730.S.wFunc is not callable using a shared const object
+fail_compilation/diag1730.d(51): Error: mutable method `diag1730.S.func` is not callable using a `inout` object
+fail_compilation/diag1730.d(43): Consider adding `const` or `inout` here
+fail_compilation/diag1730.d(53): Error: `immutable` method `diag1730.S.iFunc` is not callable using a `inout` object
+fail_compilation/diag1730.d(54): Error: `shared` mutable method `diag1730.S.sFunc` is not callable using a non-shared `inout` object
+fail_compilation/diag1730.d(46): Consider adding `const` or `inout` here
+fail_compilation/diag1730.d(55): Error: `shared` `const` method `diag1730.S.scFunc` is not callable using a non-shared `inout` object
+fail_compilation/diag1730.d(70): Error: `immutable` method `diag1730.S.iFunc` is not callable using a mutable object
+fail_compilation/diag1730.d(71): Error: `shared` method `diag1730.S.sFunc` is not callable using a non-shared object
+fail_compilation/diag1730.d(72): Error: `shared` `const` method `diag1730.S.scFunc` is not callable using a non-shared mutable object
+fail_compilation/diag1730.d(75): Error: mutable method `diag1730.S.func` is not callable using a `const` object
+fail_compilation/diag1730.d(43): Consider adding `const` or `inout` here
+fail_compilation/diag1730.d(77): Error: `immutable` method `diag1730.S.iFunc` is not callable using a `const` object
+fail_compilation/diag1730.d(78): Error: `shared` mutable method `diag1730.S.sFunc` is not callable using a non-shared `const` object
+fail_compilation/diag1730.d(46): Consider adding `const` or `inout` here
+fail_compilation/diag1730.d(79): Error: `shared` `const` method `diag1730.S.scFunc` is not callable using a non-shared `const` object
+fail_compilation/diag1730.d(82): Error: mutable method `diag1730.S.func` is not callable using a `immutable` object
+fail_compilation/diag1730.d(43): Consider adding `const` or `inout` here
+fail_compilation/diag1730.d(85): Error: `shared` mutable method `diag1730.S.sFunc` is not callable using a `immutable` object
+fail_compilation/diag1730.d(46): Consider adding `const` or `inout` here
+fail_compilation/diag1730.d(89): Error: non-shared method `diag1730.S.func` is not callable using a `shared` object
+fail_compilation/diag1730.d(43): Consider adding `shared` here
+fail_compilation/diag1730.d(90): Error: non-shared `const` method `diag1730.S.cFunc` is not callable using a `shared` mutable object
+fail_compilation/diag1730.d(44): Consider adding `shared` here
+fail_compilation/diag1730.d(91): Error: `immutable` method `diag1730.S.iFunc` is not callable using a `shared` mutable object
+fail_compilation/diag1730.d(94): Error: non-shared `inout` method `diag1730.S.wFunc` is not callable using a `shared` mutable object
+fail_compilation/diag1730.d(48): Consider adding `shared` here
+fail_compilation/diag1730.d(96): Error: non-shared mutable method `diag1730.S.func` is not callable using a `shared` `const` object
+fail_compilation/diag1730.d(43): Consider adding `shared` here
+fail_compilation/diag1730.d(97): Error: non-shared `const` method `diag1730.S.cFunc` is not callable using a `shared` `const` object
+fail_compilation/diag1730.d(44): Consider adding `shared` here
+fail_compilation/diag1730.d(98): Error: `immutable` method `diag1730.S.iFunc` is not callable using a `shared` `const` object
+fail_compilation/diag1730.d(99): Error: `shared` mutable method `diag1730.S.sFunc` is not callable using a `shared` `const` object
+fail_compilation/diag1730.d(46): Consider adding `const` or `inout` here
+fail_compilation/diag1730.d(101): Error: non-shared `inout` method `diag1730.S.wFunc` is not callable using a `shared` `const` object
+fail_compilation/diag1730.d(48): Consider adding `shared` here
---
*/
struct S
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag18460.d b/gcc/testsuite/gdc.test/fail_compilation/diag18460.d
new file mode 100644
index 00000000000..148b64be468
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag18460.d
@@ -0,0 +1,13 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag18460.d(12): Error: no property `opCall` for type `diag18460.Foo`, did you mean `new Foo`?
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=18460
+
+class Foo {}
+
+void main() {
+ auto f = Foo();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag18574.d b/gcc/testsuite/gdc.test/fail_compilation/diag18574.d
new file mode 100644
index 00000000000..fef98295290
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag18574.d
@@ -0,0 +1,17 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag18574.d(16): Error: `diag18574.Test`: multiple class inheritance is not supported. Use multiple interface inheritance and/or composition.
+fail_compilation/diag18574.d(16): `diag18574.Bar` has no fields, consider making it an `interface`
+fail_compilation/diag18574.d(16): `diag18574.Baz` has fields, consider making it a member of `diag18574.Test`
+fail_compilation/diag18574.d(16): Error: `diag18574.Test`: base type must be `interface`, not `int`
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=18574
+
+class Foo {}
+class Bar {}
+class Baz { int a; }
+
+class Test : Foo, Bar, Baz, int {}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag19022.d b/gcc/testsuite/gdc.test/fail_compilation/diag19022.d
new file mode 100644
index 00000000000..0aa26f53794
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag19022.d
@@ -0,0 +1,18 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag19022.d(16): Error: immutable field `b` initialized multiple times
+fail_compilation/diag19022.d(15): Previous initialization is here.
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=19022
+
+struct Foo
+{
+ immutable int b;
+ this(int a)
+ {
+ b = 2;
+ b = 2;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag19225.d b/gcc/testsuite/gdc.test/fail_compilation/diag19225.d
new file mode 100644
index 00000000000..bbb825159f2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag19225.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag19225.d(14): Error: basic type expected, not `else`
+fail_compilation/diag19225.d(14): There's no `static else`, use `else` instead.
+fail_compilation/diag19225.d(14): Error: found `else` without a corresponding `if`, `version` or `debug` statement
+fail_compilation/diag19225.d(15): Error: unrecognized declaration
+---
+*/
+
+void main()
+{
+ static if (true) {}
+ static else {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag20059.d b/gcc/testsuite/gdc.test/fail_compilation/diag20059.d
new file mode 100644
index 00000000000..a7a5914cfce
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag20059.d
@@ -0,0 +1,16 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag20059.d(15): Error: Expected return type of `string`, not `string[]`:
+fail_compilation/diag20059.d(13): Return type of `string` inferred here.
+---
+*/
+
+auto fail()
+{
+ string ret;
+ if (true)
+ return ret;
+ else
+ return [ret];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag20518.d b/gcc/testsuite/gdc.test/fail_compilation/diag20518.d
new file mode 100644
index 00000000000..eb0a9328ad6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag20518.d
@@ -0,0 +1,11 @@
+// EXTRA_FILES: imports/diag20518a.d imports/diag20518a/b.d
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag20518.d(11): Error: module `diag20518a` from file fail_compilation/imports/diag20518a.d conflicts with package `imports.diag20518a`
+---
+*/
+
+import imports.diag20518a.b; // from here 'imports.diag20518a' represents a package and you can optionally
+ // import its package.d with 'import imports.diag20518a;', but anyway
+import imports.diag20518a; // if 'imports/diag20518a.d' exists it will conflict with it.
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag21883.d b/gcc/testsuite/gdc.test/fail_compilation/diag21883.d
new file mode 100644
index 00000000000..4823167ee58
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag21883.d
@@ -0,0 +1,16 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag21883.d(15): Error: `diag21883.ClassB`: base class must be specified first, before any interfaces.
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=21883
+
+interface InterfaceA {
+}
+
+class ClassA {
+}
+
+class ClassB: InterfaceA, ClassA {
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag2452.d b/gcc/testsuite/gdc.test/fail_compilation/diag2452.d
index 0c12e801531..e68e21746c6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag2452.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag2452.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag2452.d(14): Error: class diag2452.C interface function 'void f(float p)' is not implemented
+fail_compilation/diag2452.d(14): Error: class `diag2452.C` interface function `void f(float p)` is not implemented
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag3013.d b/gcc/testsuite/gdc.test/fail_compilation/diag3013.d
index c57b4b9c84b..41c98b0c519 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag3013.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag3013.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag3013.d(11): Error: cannot pass type string as a function argument
+fail_compilation/diag3013.d(11): Error: cannot pass type `string` as a function argument
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag3438.d b/gcc/testsuite/gdc.test/fail_compilation/diag3438.d
index 3c22ca8acbc..445f6d5dddf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag3438.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag3438.d
@@ -2,12 +2,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag3438.d(16): Deprecation: constructor diag3438.F1.this all parameters have default arguments, but structs cannot have default constructors.
-fail_compilation/diag3438.d(17): Deprecation: constructor diag3438.F2.this all parameters have default arguments, but structs cannot have default constructors.
-fail_compilation/diag3438.d(20): Deprecation: constructor diag3438.F5.this @disable'd constructor cannot have default arguments for all parameters.
-fail_compilation/diag3438.d(20): Use @disable this(); if you want to disable default initialization.
-fail_compilation/diag3438.d(21): Deprecation: constructor diag3438.F6.this @disable'd constructor cannot have default arguments for all parameters.
-fail_compilation/diag3438.d(21): Use @disable this(); if you want to disable default initialization.
+fail_compilation/diag3438.d(16): Error: constructor `diag3438.F1.this` all parameters have default arguments, but structs cannot have default constructors.
+fail_compilation/diag3438.d(17): Error: constructor `diag3438.F2.this` all parameters have default arguments, but structs cannot have default constructors.
+fail_compilation/diag3438.d(20): Error: constructor `diag3438.F5.this` is marked `@disable`, so it cannot have default arguments for all parameters.
+fail_compilation/diag3438.d(20): Use `@disable this();` if you want to disable default initialization.
+fail_compilation/diag3438.d(21): Error: constructor `diag3438.F6.this` is marked `@disable`, so it cannot have default arguments for all parameters.
+fail_compilation/diag3438.d(21): Use `@disable this();` if you want to disable default initialization.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag3672.d b/gcc/testsuite/gdc.test/fail_compilation/diag3672.d
index ab3c2248c94..41b1415f27e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag3672.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag3672.d
@@ -1,31 +1,53 @@
-// PERMUTE_ARGS:
// REQUIRED_ARGS: -de
/*
TEST_OUTPUT:
---
-fail_compilation/diag3672.d(36): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(x, 1) instead.
-fail_compilation/diag3672.d(37): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(x, 1) instead.
-fail_compilation/diag3672.d(38): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(x, 1) instead.
-fail_compilation/diag3672.d(39): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(x, 1) instead.
-fail_compilation/diag3672.d(40): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(x, 1) instead.
-fail_compilation/diag3672.d(41): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(x, 2) instead.
-fail_compilation/diag3672.d(42): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(x, 3) instead.
-fail_compilation/diag3672.d(43): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"|="(x, y) instead.
-fail_compilation/diag3672.d(44): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"*="(x, y) instead.
-fail_compilation/diag3672.d(45): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"/="(x, y) instead.
-fail_compilation/diag3672.d(46): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"%="(x, y) instead.
-fail_compilation/diag3672.d(47): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"&="(x, y) instead.
-fail_compilation/diag3672.d(48): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"^="(x, y) instead.
-fail_compilation/diag3672.d(49): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"<<="(x, y) instead.
-fail_compilation/diag3672.d(50): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!">>="(x, y) instead.
-fail_compilation/diag3672.d(51): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!">>>="(x, y) instead.
-fail_compilation/diag3672.d(52): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"^^="(x, y) instead.
-fail_compilation/diag3672.d(53): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(ptr, 1) instead.
-fail_compilation/diag3672.d(54): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(ptr, 1) instead.
-fail_compilation/diag3672.d(55): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(ptr, 1) instead.
-fail_compilation/diag3672.d(56): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(ptr, 1) instead.
+fail_compilation/diag3672.d(8): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(8): Use `core.atomic.atomicOp!"+="(x, 1)` instead
+fail_compilation/diag3672.d(9): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(9): Use `core.atomic.atomicOp!"+="(x, 1)` instead
+fail_compilation/diag3672.d(10): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(10): Use `core.atomic.atomicOp!"-="(x, 1)` instead
+fail_compilation/diag3672.d(11): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(11): Use `core.atomic.atomicOp!"-="(x, 1)` instead
+fail_compilation/diag3672.d(12): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(12): Use `core.atomic.atomicOp!"+="(x, 1)` instead
+fail_compilation/diag3672.d(13): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(13): Use `core.atomic.atomicOp!"+="(x, 2)` instead
+fail_compilation/diag3672.d(14): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(14): Use `core.atomic.atomicOp!"-="(x, 3)` instead
+fail_compilation/diag3672.d(15): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(15): Use `core.atomic.atomicOp!"|="(x, y)` instead
+fail_compilation/diag3672.d(16): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(16): Use `core.atomic.atomicOp!"*="(x, y)` instead
+fail_compilation/diag3672.d(17): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(17): Use `core.atomic.atomicOp!"/="(x, y)` instead
+fail_compilation/diag3672.d(18): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(18): Use `core.atomic.atomicOp!"%="(x, y)` instead
+fail_compilation/diag3672.d(19): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(19): Use `core.atomic.atomicOp!"&="(x, y)` instead
+fail_compilation/diag3672.d(20): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(20): Use `core.atomic.atomicOp!"^="(x, y)` instead
+fail_compilation/diag3672.d(21): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(21): Use `core.atomic.atomicOp!"<<="(x, y)` instead
+fail_compilation/diag3672.d(22): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(22): Use `core.atomic.atomicOp!">>="(x, y)` instead
+fail_compilation/diag3672.d(23): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(23): Use `core.atomic.atomicOp!">>>="(x, y)` instead
+fail_compilation/diag3672.d(24): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(24): Use `core.atomic.atomicOp!"^^="(x, y)` instead
+fail_compilation/diag3672.d(25): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(25): Use `core.atomic.atomicOp!"+="(ptr, 1)` instead
+fail_compilation/diag3672.d(26): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(26): Use `core.atomic.atomicOp!"+="(ptr, 1)` instead
+fail_compilation/diag3672.d(27): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(27): Use `core.atomic.atomicOp!"-="(ptr, 1)` instead
+fail_compilation/diag3672.d(28): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672.d(28): Use `core.atomic.atomicOp!"-="(ptr, 1)` instead
---
*/
+
+#line 1
shared int x;
shared int y;
shared int* ptr;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag3672a.d b/gcc/testsuite/gdc.test/fail_compilation/diag3672a.d
index 6c9f701c9b2..66e9c49abca 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag3672a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag3672a.d
@@ -1,10 +1,11 @@
-// PERMUTE_ARGS:
// REQUIRED_ARGS: -de
/*
TEST_OUTPUT:
---
-fail_compilation/diag3672a.d(16): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(ns.x, 1) instead.
-fail_compilation/diag3672a.d(18): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(s.sx, 1) instead.
+fail_compilation/diag3672a.d(17): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672a.d(17): Use `core.atomic.atomicOp!"+="(ns.x, 1)` instead
+fail_compilation/diag3672a.d(19): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672a.d(19): Use `core.atomic.atomicOp!"+="(s.sx, 1)` instead
---
*/
class NS { shared int x; }
@@ -21,8 +22,10 @@ void main()
/*
TEST_OUTPUT:
---
-fail_compilation/diag3672a.d(32): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(s.var, 1) instead.
-fail_compilation/diag3672a.d(33): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(s.var, 2) instead.
+fail_compilation/diag3672a.d(35): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672a.d(35): Use `core.atomic.atomicOp!"+="(s.var, 1)` instead
+fail_compilation/diag3672a.d(36): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/diag3672a.d(36): Use `core.atomic.atomicOp!"-="(s.var, 2)` instead
---
*/
void test13003()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag3869.d b/gcc/testsuite/gdc.test/fail_compilation/diag3869.d
index 62e8993fc48..6cb92c44c38 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag3869.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag3869.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag3869.d(10): Error: template instance diag3869.sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!int)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) recursive expansion
+fail_compilation/diag3869.d(10): Error: template instance `diag3869.sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!(sum!int))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))` recursive expansion
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag3913.d b/gcc/testsuite/gdc.test/fail_compilation/diag3913.d
index e176e9dc207..abf70b84ce7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag3913.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag3913.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag3913.d(12): Error: no property 'foobardoo' for type 'Foo'
-fail_compilation/diag3913.d(13): Error: no property 'secon' for type 'Foo'. Did you mean 'Foo.second' ?
+fail_compilation/diag3913.d(12): Error: no property `foobardoo` for type `Foo`
+fail_compilation/diag3913.d(13): Error: no property `secon` for type `Foo`. Did you mean `Foo.second` ?
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag4479.d b/gcc/testsuite/gdc.test/fail_compilation/diag4479.d
index 553f9efdd87..7ce9bd89673 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag4479.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag4479.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag4479.d(10): Error: module imports.fail4479mod from file fail_compilation/imports/fail4479.d must be imported with 'import imports.fail4479mod;'
+fail_compilation/diag4479.d(10): Error: module `imports.fail4479mod` from file fail_compilation/imports/fail4479.d must be imported with 'import imports.fail4479mod;'
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag4528.d b/gcc/testsuite/gdc.test/fail_compilation/diag4528.d
index accadbcad88..ab7b2cf0fc7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag4528.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag4528.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag4528.d(14): Error: function diag4528.Foo.pva private functions cannot be abstract
-fail_compilation/diag4528.d(15): Error: function diag4528.Foo.pka package functions cannot be abstract
-fail_compilation/diag4528.d(16): Error: function diag4528.Foo.pvsa static functions cannot be abstract
-fail_compilation/diag4528.d(17): Error: function diag4528.Foo.pksa static functions cannot be abstract
-fail_compilation/diag4528.d(18): Error: function diag4528.Foo.pbsa static functions cannot be abstract
+fail_compilation/diag4528.d(14): Error: function `diag4528.Foo.pva` `private` functions cannot be `abstract`
+fail_compilation/diag4528.d(15): Error: function `diag4528.Foo.pka` `package` functions cannot be `abstract`
+fail_compilation/diag4528.d(16): Error: function `diag4528.Foo.pvsa` `static` functions cannot be `abstract`
+fail_compilation/diag4528.d(17): Error: function `diag4528.Foo.pksa` `static` functions cannot be `abstract`
+fail_compilation/diag4528.d(18): Error: function `diag4528.Foo.pbsa` `static` functions cannot be `abstract`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag4596.d b/gcc/testsuite/gdc.test/fail_compilation/diag4596.d
index 368f67ec439..f6b49d6bd13 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag4596.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag4596.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag4596.d(15): Error: this is not an lvalue
-fail_compilation/diag4596.d(16): Error: 1 ? this : this is not an lvalue
-fail_compilation/diag4596.d(18): Error: super is not an lvalue
-fail_compilation/diag4596.d(19): Error: 1 ? super : super is not an lvalue
+fail_compilation/diag4596.d(15): Error: `this` is not an lvalue and cannot be modified
+fail_compilation/diag4596.d(16): Error: conditional expression `1 ? this : this` is not a modifiable lvalue
+fail_compilation/diag4596.d(18): Error: `super` is not an lvalue and cannot be modified
+fail_compilation/diag4596.d(19): Error: conditional expression `1 ? super : super` is not a modifiable lvalue
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag5385.d b/gcc/testsuite/gdc.test/fail_compilation/diag5385.d
index 7c81b168c70..60455eca206 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag5385.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag5385.d
@@ -1,14 +1,15 @@
/*
+EXTRA_FILES: imports/fail5385.d
TEST_OUTPUT:
---
-fail_compilation/diag5385.d(19): Error: no property `privX` for type `imports.fail5385.C`, did you mean `imports.fail5385.C.privX`?
-fail_compilation/diag5385.d(20): Error: no property `packX` for type `imports.fail5385.C`, did you mean `imports.fail5385.C.packX`?
-fail_compilation/diag5385.d(21): Error: no property `privX2` for type `imports.fail5385.C`, did you mean `imports.fail5385.C.privX2`?
-fail_compilation/diag5385.d(22): Error: no property `packX2` for type `imports.fail5385.C`, did you mean `imports.fail5385.C.packX2`?
-fail_compilation/diag5385.d(23): Error: no property `privX` for type `S`, did you mean `imports.fail5385.S.privX`?
-fail_compilation/diag5385.d(24): Error: no property `packX` for type `S`, did you mean `imports.fail5385.S.packX`?
-fail_compilation/diag5385.d(25): Error: no property `privX2` for type `S`, did you mean `imports.fail5385.S.privX2`?
-fail_compilation/diag5385.d(26): Error: no property `packX2` for type `S`, did you mean `imports.fail5385.S.packX2`?
+fail_compilation/diag5385.d(20): Error: no property `privX` for type `imports.fail5385.C`
+fail_compilation/diag5385.d(21): Error: no property `packX` for type `imports.fail5385.C`
+fail_compilation/diag5385.d(22): Error: no property `privX2` for type `imports.fail5385.C`
+fail_compilation/diag5385.d(23): Error: no property `packX2` for type `imports.fail5385.C`
+fail_compilation/diag5385.d(24): Error: no property `privX` for type `imports.fail5385.S`
+fail_compilation/diag5385.d(25): Error: no property `packX` for type `imports.fail5385.S`
+fail_compilation/diag5385.d(26): Error: no property `privX2` for type `imports.fail5385.S`
+fail_compilation/diag5385.d(27): Error: no property `packX2` for type `imports.fail5385.S`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag5450.d b/gcc/testsuite/gdc.test/fail_compilation/diag5450.d
index 495fea9c955..f544240d497 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag5450.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag5450.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag5450.d(18): Error: class diag5450.C cannot implicitly generate a default ctor when base class diag5450.B is missing a default ctor
+fail_compilation/diag5450.d(18): Error: class `diag5450.C` cannot implicitly generate a default constructor when base class `diag5450.B` is missing a default constructor
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag6373.d b/gcc/testsuite/gdc.test/fail_compilation/diag6373.d
index d5e396d2c07..8711b90f6ab 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag6373.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag6373.d
@@ -2,7 +2,7 @@
REQUIRED_ARGS: -de
TEST_OUTPUT:
---
-fail_compilation/diag6373.d(15): Error: class diag6373.Bar use of `diag6373.Foo.method(double x)` is hidden by `Bar`; use `alias method = Foo.method;` to introduce base class overload set
+fail_compilation/diag6373.d(15): Error: class `diag6373.Bar` use of `diag6373.Foo.method(double x)` is hidden by `Bar`; use `alias method = Foo.method;` to introduce base class overload set
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag6539.d b/gcc/testsuite/gdc.test/fail_compilation/diag6539.d
index eddb5d2ef83..c202548aac5 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag6539.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag6539.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag6539.d(21): Error: overloadset diag6539.Rectangle is used as a type
+fail_compilation/diag6539.d(21): Error: overloadset `diag6539.Rectangle` is used as a type
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag6677.d b/gcc/testsuite/gdc.test/fail_compilation/diag6677.d
index 9f9c6dca868..aef65d26c46 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag6677.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag6677.d
@@ -1,17 +1,16 @@
-// REQUIRED_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/diag6677.d(18): Error: static constructor cannot be `const`
-fail_compilation/diag6677.d(19): Error: static constructor cannot be `inout`
-fail_compilation/diag6677.d(20): Error: static constructor cannot be `immutable`
+fail_compilation/diag6677.d(17): Error: static constructor cannot be `const`
+fail_compilation/diag6677.d(18): Error: static constructor cannot be `inout`
+fail_compilation/diag6677.d(19): Error: static constructor cannot be `immutable`
+fail_compilation/diag6677.d(20): Error: use `shared static this()` to declare a shared static constructor
fail_compilation/diag6677.d(21): Error: use `shared static this()` to declare a shared static constructor
-fail_compilation/diag6677.d(22): Error: use `shared static this()` to declare a shared static constructor
-fail_compilation/diag6677.d(24): Error: shared static constructor cannot be `const`
-fail_compilation/diag6677.d(25): Error: shared static constructor cannot be `inout`
-fail_compilation/diag6677.d(26): Error: shared static constructor cannot be `immutable`
+fail_compilation/diag6677.d(23): Error: shared static constructor cannot be `const`
+fail_compilation/diag6677.d(24): Error: shared static constructor cannot be `inout`
+fail_compilation/diag6677.d(25): Error: shared static constructor cannot be `immutable`
+fail_compilation/diag6677.d(26): Error: redundant attribute `shared`
fail_compilation/diag6677.d(27): Error: redundant attribute `shared`
-fail_compilation/diag6677.d(28): Error: redundant attribute `shared`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag6699.d b/gcc/testsuite/gdc.test/fail_compilation/diag6699.d
index 34b2c7716f6..755d0fdd16a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag6699.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag6699.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag6699.d(8): Error: no property 'x' for type 'int'
+fail_compilation/diag6699.d(8): Error: no property `x` for type `int`
---
*/
alias int b6699;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag6707.d b/gcc/testsuite/gdc.test/fail_compilation/diag6707.d
index cabdec3f1ad..70fea4fcf68 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag6707.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag6707.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag6707.d(16): Error: mutable method diag6707.Foo.value is not callable using a const object
+fail_compilation/diag6707.d(17): Error: mutable method `diag6707.Foo.value` is not callable using a `const` object
+fail_compilation/diag6707.d(13): Consider adding `const` or `inout` here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7050a.d b/gcc/testsuite/gdc.test/fail_compilation/diag7050a.d
index ddee70cf412..abd0d182509 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag7050a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag7050a.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag7050a.d(14): Error: @safe function 'diag7050a.foo' cannot call @system constructor 'diag7050a.Foo.this'
+fail_compilation/diag7050a.d(15): Error: `@safe` function `diag7050a.foo` cannot call `@system` constructor `diag7050a.Foo.this`
+fail_compilation/diag7050a.d(11): `diag7050a.Foo.this` is declared here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7050b.d b/gcc/testsuite/gdc.test/fail_compilation/diag7050b.d
index ecbfd8127e7..78912cdf1d8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag7050b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag7050b.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag7050b.d(12): Error: pure function 'diag7050b.f.g' cannot call impure function 'diag7050b.f'
+fail_compilation/diag7050b.d(12): Error: `pure` function `diag7050b.f.g` cannot call impure function `diag7050b.f`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7050c.d b/gcc/testsuite/gdc.test/fail_compilation/diag7050c.d
index 3fa75fc524c..3b366df7af3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag7050c.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag7050c.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag7050c.d(13): Error: @safe destructor 'diag7050c.B.~this' cannot call @system destructor 'diag7050c.A.~this'
+fail_compilation/diag7050c.d(14): Error: `@safe` destructor `diag7050c.B.~this` cannot call `@system` destructor `diag7050c.A.~this`
+fail_compilation/diag7050c.d(11): `diag7050c.A.~this` is declared here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7420.d b/gcc/testsuite/gdc.test/fail_compilation/diag7420.d
index 80077eb4ec9..3267e669243 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag7420.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag7420.d
@@ -2,16 +2,17 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag7420.d(20): Error: static variable x cannot be read at compile time
-fail_compilation/diag7420.d(20): while evaluating: `static assert(x < 4)`
-fail_compilation/diag7420.d(21): Error: static variable y cannot be read at compile time
-fail_compilation/diag7420.d(21): while evaluating: `static assert(y == "abc")`
-fail_compilation/diag7420.d(22): Error: static variable y cannot be read at compile time
-fail_compilation/diag7420.d(22): while evaluating: `static assert(cast(ubyte[])y != null)`
-fail_compilation/diag7420.d(23): Error: static variable y cannot be read at compile time
-fail_compilation/diag7420.d(23): while evaluating: `static assert(cast(int)y[0] == 1)`
-fail_compilation/diag7420.d(24): Error: static variable y cannot be read at compile time
-fail_compilation/diag7420.d(24): while evaluating: `static assert(y[0..1].length == 1u)`
+fail_compilation/diag7420.d(21): Error: static variable `x` cannot be read at compile time
+fail_compilation/diag7420.d(21): while evaluating: `static assert(x < 4)`
+fail_compilation/diag7420.d(22): Error: static variable `y` cannot be read at compile time
+fail_compilation/diag7420.d(22): called from here: `__equals(y, "abc")`
+fail_compilation/diag7420.d(22): while evaluating: `static assert(y == "abc")`
+fail_compilation/diag7420.d(23): Error: static variable `y` cannot be read at compile time
+fail_compilation/diag7420.d(23): while evaluating: `static assert(cast(ubyte[])y != null)`
+fail_compilation/diag7420.d(24): Error: static variable `y` cannot be read at compile time
+fail_compilation/diag7420.d(24): while evaluating: `static assert(cast(int)y[0] == 1)`
+fail_compilation/diag7420.d(25): Error: static variable `y` cannot be read at compile time
+fail_compilation/diag7420.d(25): while evaluating: `static assert(y[0..1].length == 1u)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7477.d b/gcc/testsuite/gdc.test/fail_compilation/diag7477.d
index 1c971847bc8..b82b33de946 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag7477.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag7477.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag7477.d(13): Error: cannot implicitly convert expression `0` of type `int` to `Foo`
-fail_compilation/diag7477.d(20): Error: cannot implicitly convert expression `0` of type `int` to `string`
+fail_compilation/diag7477.d(13): Error: integral constant must be scalar type, not `Foo`
+fail_compilation/diag7477.d(20): Error: integral constant must be scalar type, not `string`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7747.d b/gcc/testsuite/gdc.test/fail_compilation/diag7747.d
index 0756911cab3..e2e59929f6a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag7747.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag7747.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag7747.d(8): Error: forward reference to inferred return type of function call 'fact(n - 1)'
+fail_compilation/diag7747.d(8): Error: forward reference to inferred return type of function call `fact(n - 1)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7998.d b/gcc/testsuite/gdc.test/fail_compilation/diag7998.d
index 68f61f3361c..a245b4061f1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag7998.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag7998.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag7998.d(10): Error: static assert "abcxe"
+fail_compilation/diag7998.d(10): Error: static assert: "abcxe"
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8044.d b/gcc/testsuite/gdc.test/fail_compilation/diag8044.d
new file mode 100644
index 00000000000..a8c767ae7a8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8044.d
@@ -0,0 +1,19 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag8044.d(18): Error: template instance `diag8044.test!(Enum.Bar)` does not match template declaration `test(Enum en)()`
+ with `en = Bar`
+ must satisfy the following constraint:
+` 0`
+---
+ */
+enum Enum { Foo, Bar }
+void test(Enum en)()
+ if(0)
+{
+}
+
+void main()
+{
+ test!(Enum.Bar)();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8101.d b/gcc/testsuite/gdc.test/fail_compilation/diag8101.d
index 3b2401ad8cf..a0d245b370a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8101.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8101.d
@@ -3,27 +3,27 @@ TEST_OUTPUT:
---
fail_compilation/diag8101.d(57): Error: function `diag8101.f_0(int)` is not callable using argument types `()`
fail_compilation/diag8101.d(57): missing argument for parameter #1: `int`
-fail_compilation/diag8101.d(58): Error: none of the overloads of `f_1` are callable using argument types `()`, candidates are:
-fail_compilation/diag8101.d(33): `diag8101.f_1(int)`
-fail_compilation/diag8101.d(34): `diag8101.f_1(int, int)`
-fail_compilation/diag8101.d(59): Error: none of the overloads of `f_2` are callable using argument types `()`, candidates are:
-fail_compilation/diag8101.d(36): `diag8101.f_2(int)`
-fail_compilation/diag8101.d(37): `diag8101.f_2(int, int)`
-fail_compilation/diag8101.d(38): `diag8101.f_2(int, int, int)`
-fail_compilation/diag8101.d(39): `diag8101.f_2(int, int, int, int)`
-fail_compilation/diag8101.d(40): `diag8101.f_2(int, int, int, int, int)`
+fail_compilation/diag8101.d(58): Error: none of the overloads of `f_1` are callable using argument types `()`
+fail_compilation/diag8101.d(33): Candidates are: `diag8101.f_1(int)`
+fail_compilation/diag8101.d(34): `diag8101.f_1(int, int)`
+fail_compilation/diag8101.d(59): Error: none of the overloads of `f_2` are callable using argument types `()`
+fail_compilation/diag8101.d(36): Candidates are: `diag8101.f_2(int)`
+fail_compilation/diag8101.d(37): `diag8101.f_2(int, int)`
+fail_compilation/diag8101.d(38): `diag8101.f_2(int, int, int)`
+fail_compilation/diag8101.d(39): `diag8101.f_2(int, int, int, int)`
+fail_compilation/diag8101.d(40): `diag8101.f_2(int, int, int, int, int)`
fail_compilation/diag8101.d(59): ... (1 more, -v to show) ...
-fail_compilation/diag8101.d(61): Error: template `diag8101.t_0` cannot deduce function from argument types `!()()`, candidates are:
-fail_compilation/diag8101.d(43): `diag8101.t_0(T1)()`
-fail_compilation/diag8101.d(62): Error: template `diag8101.t_1` cannot deduce function from argument types `!()()`, candidates are:
-fail_compilation/diag8101.d(45): `diag8101.t_1(T1)()`
-fail_compilation/diag8101.d(46): `diag8101.t_1(T1, T2)()`
-fail_compilation/diag8101.d(63): Error: template `diag8101.t_2` cannot deduce function from argument types `!()()`, candidates are:
-fail_compilation/diag8101.d(48): `diag8101.t_2(T1)()`
-fail_compilation/diag8101.d(49): `diag8101.t_2(T1, T2)()`
-fail_compilation/diag8101.d(50): `diag8101.t_2(T1, T2, T3)()`
-fail_compilation/diag8101.d(51): `diag8101.t_2(T1, T2, T3, T4)()`
-fail_compilation/diag8101.d(52): `diag8101.t_2(T1, T2, T3, T4, T5)()`
+fail_compilation/diag8101.d(61): Error: template `diag8101.t_0` cannot deduce function from argument types `!()()`
+fail_compilation/diag8101.d(43): Candidate is: `t_0(T1)()`
+fail_compilation/diag8101.d(62): Error: template `diag8101.t_1` cannot deduce function from argument types `!()()`
+fail_compilation/diag8101.d(45): Candidates are: `t_1(T1)()`
+fail_compilation/diag8101.d(46): `t_1(T1, T2)()`
+fail_compilation/diag8101.d(63): Error: template `diag8101.t_2` cannot deduce function from argument types `!()()`
+fail_compilation/diag8101.d(48): Candidates are: `t_2(T1)()`
+fail_compilation/diag8101.d(49): `t_2(T1, T2)()`
+fail_compilation/diag8101.d(50): `t_2(T1, T2, T3)()`
+fail_compilation/diag8101.d(51): `t_2(T1, T2, T3, T4)()`
+fail_compilation/diag8101.d(52): `t_2(T1, T2, T3, T4, T5)()`
fail_compilation/diag8101.d(63): ... (1 more, -v to show) ...
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8101b.d b/gcc/testsuite/gdc.test/fail_compilation/diag8101b.d
index 228ba169e8e..bc0ee9d2fb7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8101b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8101b.d
@@ -1,15 +1,16 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8101b.d(27): Error: none of the overloads of `foo` are callable using argument types `(double)`, candidates are:
-fail_compilation/diag8101b.d(18): `diag8101b.S.foo(int _param_0)`
-fail_compilation/diag8101b.d(19): `diag8101b.S.foo(int _param_0, int _param_1)`
-fail_compilation/diag8101b.d(29): Error: function `diag8101b.S.bar(int _param_0)` is not callable using argument types `(double)`
-fail_compilation/diag8101b.d(29): cannot pass argument `1.00000` of type `double` to parameter `int _param_0`
-fail_compilation/diag8101b.d(32): Error: none of the overloads of `foo` are callable using a `const` object, candidates are:
-fail_compilation/diag8101b.d(18): `diag8101b.S.foo(int _param_0)`
-fail_compilation/diag8101b.d(19): `diag8101b.S.foo(int _param_0, int _param_1)`
-fail_compilation/diag8101b.d(34): Error: mutable method `diag8101b.S.bar` is not callable using a `const` object
+fail_compilation/diag8101b.d(28): Error: none of the overloads of `foo` are callable using argument types `(double)`
+fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int _param_0)`
+fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int _param_0, int _param_1)`
+fail_compilation/diag8101b.d(30): Error: function `diag8101b.S.bar(int _param_0)` is not callable using argument types `(double)`
+fail_compilation/diag8101b.d(30): cannot pass argument `1.0` of type `double` to parameter `int _param_0`
+fail_compilation/diag8101b.d(33): Error: none of the overloads of `foo` are callable using a `const` object
+fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int _param_0)`
+fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int _param_0, int _param_1)`
+fail_compilation/diag8101b.d(35): Error: mutable method `diag8101b.S.bar` is not callable using a `const` object
+fail_compilation/diag8101b.d(22): Consider adding `const` or `inout` here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8178.d b/gcc/testsuite/gdc.test/fail_compilation/diag8178.d
index 491a6255742..00cac985b13 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8178.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8178.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8178.d(14): Error: cannot modify manifest constant 's'
+fail_compilation/diag8178.d(14): Error: cannot modify manifest constant `s`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8318.d b/gcc/testsuite/gdc.test/fail_compilation/diag8318.d
index d319532fd5c..99dc6c41d11 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8318.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8318.d
@@ -1,7 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8318.d(13): Error: function diag8318.Bar8318.foo return type inference is not supported if may override base class function
+fail_compilation/diag8318.d(18): Error: function `diag8318.Bar8318.foo` return type inference is not supported if may override base class function
+fail_compilation/diag8318.d(23): Error: function `diag8318.C10021.makeI` return type inference is not supported if may override base class function
+fail_compilation/diag8318.d(31): Error: function `diag8318.Bar10195.baz` return type inference is not supported if may override base class function
+fail_compilation/diag8318.d(37): Error: function `diag8318.B14173.foo` does not override any function
+fail_compilation/diag8318.d(23): Error: class `diag8318.C10021` interface function `I10021 makeI()` is not implemented
+fail_compilation/diag8318.d(29): Error: class `diag8318.Bar10195` interface function `int baz()` is not implemented
---
*/
class Foo8318
@@ -13,22 +18,10 @@ class Bar8318 : Foo8318
override auto foo() { return "Bar.foo"; }
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/diag8318.d(24): Error: function diag8318.C10021.makeI return type inference is not supported if may override base class function
----
-*/
interface I10021 { I10021 makeI(); }
class D10021 : I10021 { D10021 makeI() { return this; } }
class C10021 : I10021 { auto makeI() { return this; } }
-/*
-TEST_OUTPUT:
----
-fail_compilation/diag8318.d(38): Error: function diag8318.Bar10195.baz return type inference is not supported if may override base class function
----
-*/
interface Foo10195
{
int baz();
@@ -38,12 +31,6 @@ class Bar10195 : Foo10195
override auto baz() { return 1; }
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/diag8318.d(50): Error: function diag8318.B14173.foo does not override any function
----
-*/
class A14173 {}
class B14173 : A14173
{
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8425.d b/gcc/testsuite/gdc.test/fail_compilation/diag8425.d
index 14dbb1cf1ad..a30881b6df1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8425.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8425.d
@@ -1,12 +1,11 @@
/*
REQUIRED_ARGS: -m64 -o-
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/diag8425.d(13): Error: T in __vector(T) must be a static array, not void
-fail_compilation/diag8425.d(14): Error: 1 byte vector type __vector(void[1]) is not supported on this platform
-fail_compilation/diag8425.d(15): Error: 99 byte vector type __vector(void[99]) is not supported on this platform
-fail_compilation/diag8425.d(16): Error: vector type __vector(void*[4]) is not supported on this platform
+fail_compilation/diag8425.d(12): Error: T in __vector(T) must be a static array, not `void`
+fail_compilation/diag8425.d(13): Error: 1 byte vector type `__vector(void[1])` is not supported on this platform
+fail_compilation/diag8425.d(14): Error: 99 byte vector type `__vector(void[99])` is not supported on this platform
+fail_compilation/diag8425.d(15): Error: vector type `__vector(void*[4])` is not supported on this platform
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8510.d b/gcc/testsuite/gdc.test/fail_compilation/diag8510.d
index d1a897a40c6..77660d0449a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8510.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8510.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8510.d(10): Error: alias diag8510.a conflicts with alias diag8510.a at fail_compilation/diag8510.d(9)
-fail_compilation/diag8510.d(15): Error: alias diag8510.S.a conflicts with alias diag8510.S.a at fail_compilation/diag8510.d(14)
+fail_compilation/diag8510.d(10): Error: alias `diag8510.a` conflicts with alias `diag8510.a` at fail_compilation/diag8510.d(9)
+fail_compilation/diag8510.d(15): Error: alias `diag8510.S.a` conflicts with alias `diag8510.S.a` at fail_compilation/diag8510.d(14)
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8559.d b/gcc/testsuite/gdc.test/fail_compilation/diag8559.d
index 5dc2b9d3a26..5da50f41443 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8559.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8559.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8559.d(12): Error: void does not have a default initializer
-fail_compilation/diag8559.d(13): Error: function does not have a default initializer
+fail_compilation/diag8559.d(12): Error: `void` does not have a default initializer
+fail_compilation/diag8559.d(13): Error: `function` does not have a default initializer
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8648.d b/gcc/testsuite/gdc.test/fail_compilation/diag8648.d
index fa96d5cfd3e..f202fb33c40 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8648.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8648.d
@@ -2,14 +2,14 @@
TEST_OUTPUT:
---
fail_compilation/diag8648.d(18): Error: undefined identifier `X`
-fail_compilation/diag8648.d(29): Error: template diag8648.foo cannot deduce function from argument types !()(Foo!(int, 1)), candidates are:
-fail_compilation/diag8648.d(18): diag8648.foo(T, n)(X!(T, n))
+fail_compilation/diag8648.d(29): Error: template `diag8648.foo` cannot deduce function from argument types `!()(Foo!(int, 1))`
+fail_compilation/diag8648.d(18): Candidate is: `foo(T, n)(X!(T, n))`
fail_compilation/diag8648.d(20): Error: undefined identifier `a`
-fail_compilation/diag8648.d(31): Error: template diag8648.bar cannot deduce function from argument types !()(Foo!(int, 1)), candidates are:
-fail_compilation/diag8648.d(20): diag8648.bar(T)(Foo!(T, a))
+fail_compilation/diag8648.d(31): Error: template `diag8648.bar` cannot deduce function from argument types `!()(Foo!(int, 1))`
+fail_compilation/diag8648.d(20): Candidate is: `bar(T)(Foo!(T, a))`
fail_compilation/diag8648.d(20): Error: undefined identifier `a`
-fail_compilation/diag8648.d(32): Error: template diag8648.bar cannot deduce function from argument types !()(Foo!(int, f)), candidates are:
-fail_compilation/diag8648.d(20): diag8648.bar(T)(Foo!(T, a))
+fail_compilation/diag8648.d(32): Error: template `diag8648.bar` cannot deduce function from argument types `!()(Foo!(int, f))`
+fail_compilation/diag8648.d(20): Candidate is: `bar(T)(Foo!(T, a))`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8684.d b/gcc/testsuite/gdc.test/fail_compilation/diag8684.d
new file mode 100644
index 00000000000..acfee71d22d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8684.d
@@ -0,0 +1,16 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag8684.d(11): Error: found `;` when expecting `)`
+fail_compilation/diag8684.d(12): Error: semicolon expected, not `for`
+---
+*/
+
+int foo(int n, int m)
+{
+ int x = foo( 5, m;
+ for (int q=0; q<10; ++q){
+ ++q;
+ }
+ return 2;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8697.d b/gcc/testsuite/gdc.test/fail_compilation/diag8697.d
index b1a10089997..a2abad58764 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8697.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8697.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8697.d(10): Error: no property 'Invalid' for type 'diag8697.Base'
+fail_compilation/diag8697.d(10): Error: no property `Invalid` for type `diag8697.Base`
---
*/
interface InterBase : InterRoot { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8714.d b/gcc/testsuite/gdc.test/fail_compilation/diag8714.d
index 3a7ffd87e0d..5a9d8d9cc70 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8714.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8714.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8714.d(9): Error: function diag8714.foo circular dependency. Functions cannot be interpreted while being compiled
-fail_compilation/diag8714.d(15): called from here: foo("somestring")
+fail_compilation/diag8714.d(9): Error: function `diag8714.foo` circular dependency. Functions cannot be interpreted while being compiled
+fail_compilation/diag8714.d(15): called from here: `foo("somestring")`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8777.d b/gcc/testsuite/gdc.test/fail_compilation/diag8777.d
index f289da92a85..8dfac75bfcb 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8777.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8777.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8777.d(12): Error: constructor diag8777.Foo1.this missing initializer for immutable field x
-fail_compilation/diag8777.d(12): Error: constructor diag8777.Foo1.this missing initializer for const field y
+fail_compilation/diag8777.d(12): Error: constructor `diag8777.Foo1.this` missing initializer for immutable field `x`
+fail_compilation/diag8777.d(12): Error: constructor `diag8777.Foo1.this` missing initializer for const field `y`
---
*/
class Foo1
@@ -15,8 +15,8 @@ class Foo1
/*
TEST_OUTPUT:
---
-fail_compilation/diag8777.d(25): Error: cannot modify immutable expression x
-fail_compilation/diag8777.d(28): Error: cannot modify const expression y
+fail_compilation/diag8777.d(25): Error: cannot modify `immutable` expression `x`
+fail_compilation/diag8777.d(28): Error: cannot modify `const` expression `y`
---
*/
void test2()
@@ -31,8 +31,8 @@ void test2()
/*
TEST_OUTPUT:
---
-fail_compilation/diag8777.d(42): Error: cannot remove key from immutable associative array hashx
-fail_compilation/diag8777.d(43): Error: cannot remove key from const associative array hashy
+fail_compilation/diag8777.d(42): Error: cannot remove key from `immutable` associative array `hashx`
+fail_compilation/diag8777.d(43): Error: cannot remove key from `const` associative array `hashy`
---
*/
immutable(int[int]) hashx;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8787.d b/gcc/testsuite/gdc.test/fail_compilation/diag8787.d
index dde80abc9af..c4ff6d2bb5f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8787.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8787.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8787.d(10): Error: function diag8787.I.f function body only allowed in final functions in interface I
+fail_compilation/diag8787.d(10): Error: function `diag8787.I.f` function body only allowed in `final` functions in interface `I`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8894.d b/gcc/testsuite/gdc.test/fail_compilation/diag8894.d
index 9b66bf0f032..9e0dadd1482 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8894.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8894.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8894.d(16): Error: no property 'x' for type 'Foo'
-fail_compilation/diag8894.d(17): Error: no property 'y' for type 'Foo'
-fail_compilation/diag8894.d(18): Error: no property 'x' for type 'Foo'
-fail_compilation/diag8894.d(19): Error: no property 'x' for type 'Foo'
+fail_compilation/diag8894.d(16): Error: no property `x` for type `diag8894.Foo`
+fail_compilation/diag8894.d(17): Error: no property `y` for type `diag8894.Foo`
+fail_compilation/diag8894.d(18): Error: no property `x` for type `diag8894.Foo`
+fail_compilation/diag8894.d(19): Error: no property `x` for type `diag8894.Foo`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8928.d b/gcc/testsuite/gdc.test/fail_compilation/diag8928.d
index bdd1ae8b61e..36bfc967b22 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8928.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8928.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag8928.d(18): Error: class diag8928.Z cannot implicitly generate a default ctor when base class diag8928.X is missing a default ctor
+fail_compilation/diag8928.d(18): Error: class `diag8928.Z` cannot implicitly generate a default constructor when base class `diag8928.X` is missing a default constructor
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9004.d b/gcc/testsuite/gdc.test/fail_compilation/diag9004.d
index 19852758252..62fce32bbac 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9004.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9004.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9004.d(21): Error: template diag9004.bar cannot deduce function from argument types !()(Foo!int, int), candidates are:
-fail_compilation/diag9004.d(14): diag9004.bar(FooT)(FooT foo, FooT.T x)
+fail_compilation/diag9004.d(21): Error: template `diag9004.bar` cannot deduce function from argument types `!()(Foo!int, int)`
+fail_compilation/diag9004.d(14): Candidate is: `bar(FooT)(FooT foo, FooT.T x)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9148.d b/gcc/testsuite/gdc.test/fail_compilation/diag9148.d
index 0a7707e659f..2e5cbc671ba 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9148.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9148.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9148.d(19): Error: pure function 'diag9148.test9148a.foo' cannot access mutable static data 'g'
-fail_compilation/diag9148.d(23): Error: pure function 'diag9148.test9148a.bar' cannot access mutable static data 'g'
-fail_compilation/diag9148.d(24): Error: immutable function 'diag9148.test9148a.bar' cannot access mutable data 'x'
-fail_compilation/diag9148.d(31): Error: pure function 'diag9148.test9148a.S.foo' cannot access mutable static data 'g'
-fail_compilation/diag9148.d(35): Error: pure function 'diag9148.test9148a.S.bar' cannot access mutable static data 'g'
-fail_compilation/diag9148.d(36): Error: immutable function 'diag9148.test9148a.S.bar' cannot access mutable data 'x'
+fail_compilation/diag9148.d(19): Error: `pure` function `diag9148.test9148a.foo` cannot access mutable static data `g`
+fail_compilation/diag9148.d(23): Error: `pure` function `diag9148.test9148a.bar` cannot access mutable static data `g`
+fail_compilation/diag9148.d(24): Error: `immutable` function `diag9148.test9148a.bar` cannot access mutable data `x`
+fail_compilation/diag9148.d(31): Error: `pure` function `diag9148.test9148a.S.foo` cannot access mutable static data `g`
+fail_compilation/diag9148.d(35): Error: `pure` function `diag9148.test9148a.S.bar` cannot access mutable static data `g`
+fail_compilation/diag9148.d(36): Error: `immutable` function `diag9148.test9148a.S.bar` cannot access mutable data `x`
---
*/
void test9148a() pure
@@ -41,7 +41,8 @@ void test9148a() pure
/*
TEST_OUTPUT:
---
-fail_compilation/diag9148.d(53): Error: static function diag9148.test9148b.foo cannot access frame of function diag9148.test9148b
+fail_compilation/diag9148.d(54): Error: `static` function `diag9148.test9148b.foo` cannot access variable `x` in frame of function `diag9148.test9148b`
+fail_compilation/diag9148.d(51): `x` declared here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9191.d b/gcc/testsuite/gdc.test/fail_compilation/diag9191.d
index 50e5445d3e0..889d1fada2b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9191.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9191.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9191.d(16): Error: function diag9191.C1.aaa does not override any function, did you mean to override 'diag9191.B1.aa'?
-fail_compilation/diag9191.d(21): Error: function diag9191.C2.aaa does not override any function
-fail_compilation/diag9191.d(31): Error: function diag9191.C3.foo does not override any function, did you mean to override 'diag9191.B2._foo'?
-fail_compilation/diag9191.d(36): Error: function diag9191.C4.toStringa does not override any function, did you mean to override 'object.Object.toString'?
+fail_compilation/diag9191.d(16): Error: function `void diag9191.C1.aaa()` does not override any function, did you mean to override `void diag9191.B1.aa()`?
+fail_compilation/diag9191.d(22): Error: function `diag9191.C2.aaa` does not override any function
+fail_compilation/diag9191.d(33): Error: function `void diag9191.C3.foo()` does not override any function, did you mean to override `void diag9191.B2._foo()`?
+fail_compilation/diag9191.d(38): Error: function `void diag9191.C4.toStringa()` does not override any function, did you mean to override `string object.Object.toString()`?
---
*/
@@ -14,11 +14,13 @@ class B1 { void aa(); }
class C1 : B1, I1
{
override void aaa();
+ void a() {}
}
class C2 : I1
{
override void aaa();
+ void a() {}
}
class B2
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9210a.d b/gcc/testsuite/gdc.test/fail_compilation/diag9210a.d
index ac3609f48f1..d70f4f7d211 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9210a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9210a.d
@@ -1,6 +1,5 @@
// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/diag9210b.d imports/diag9210c.d imports/diag9210stdcomplex.d imports/diag9210stdtraits.d
/*
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9247.d b/gcc/testsuite/gdc.test/fail_compilation/diag9247.d
index e22634559cb..1519b10f827 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9247.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9247.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9247.d(11): Error: functions cannot return opaque type S by value
-fail_compilation/diag9247.d(12): Error: functions cannot return opaque type S by value
+fail_compilation/diag9247.d(11): Error: functions cannot return opaque type `S` by value
+fail_compilation/diag9247.d(12): Error: functions cannot return opaque type `S` by value
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9312.d b/gcc/testsuite/gdc.test/fail_compilation/diag9312.d
index 41c136090b0..94e3d3ffd43 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9312.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9312.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9312.d(10): Error: with expressions must be aggregate types or pointers to them, not `int`
+fail_compilation/diag9312.d(10): Error: `with` expressions must be aggregate types or pointers to them, not `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9357.d b/gcc/testsuite/gdc.test/fail_compilation/diag9357.d
index f26b1c4482e..097c8dee250 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9357.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9357.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9357.d(14): Error: cannot implicitly convert expression `1.00000` of type `double` to `int`
-fail_compilation/diag9357.d(15): Error: cannot implicitly convert expression `10.0000` of type `double` to `int`
-fail_compilation/diag9357.d(16): Error: cannot implicitly convert expression `11.0000` of type `double` to `int`
-fail_compilation/diag9357.d(17): Error: cannot implicitly convert expression `99.0000` of type `double` to `int`
+fail_compilation/diag9357.d(14): Error: cannot implicitly convert expression `1.0` of type `double` to `int`
+fail_compilation/diag9357.d(15): Error: cannot implicitly convert expression `10.0` of type `double` to `int`
+fail_compilation/diag9357.d(16): Error: cannot implicitly convert expression `11.0` of type `double` to `int`
+fail_compilation/diag9357.d(17): Error: cannot implicitly convert expression `99.0` of type `double` to `int`
fail_compilation/diag9357.d(18): Error: cannot implicitly convert expression `1.04858e+06L` of type `real` to `int`
fail_compilation/diag9357.d(19): Error: cannot implicitly convert expression `1.04858e+06L` of type `real` to `int`
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9358.d b/gcc/testsuite/gdc.test/fail_compilation/diag9358.d
index d368ae2dd7e..5aea6b5f9c1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9358.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9358.d
@@ -2,8 +2,8 @@
TEST_OUTPUT:
---
fail_compilation/diag9358.d(12): Error: `x` must be of integral or string type, it is a `double`
-fail_compilation/diag9358.d(14): Error: case must be a string or an integral constant, not `1.1`
-fail_compilation/diag9358.d(15): Error: case must be a string or an integral constant, not `2.1`
+fail_compilation/diag9358.d(14): Error: `case` must be a `string` or an integral constant, not `1.1`
+fail_compilation/diag9358.d(15): Error: `case` must be a `string` or an integral constant, not `2.1`
---
*/
void main()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9398.d b/gcc/testsuite/gdc.test/fail_compilation/diag9398.d
index 67900c9bc6c..fe7e40081f3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9398.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9398.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9398.d(11): Error: incompatible types for ((f) : (s)): 'float' and 'string'
+fail_compilation/diag9398.d(11): Error: incompatible types for `(f) : (s)`: `float` and `string`
---
*/
void main()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9451.d b/gcc/testsuite/gdc.test/fail_compilation/diag9451.d
index a9121fc338f..ffec627f02f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9451.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9451.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9451.d(26): Error: cannot create instance of abstract class C2
-fail_compilation/diag9451.d(26): function 'void f1()' is not implemented
-fail_compilation/diag9451.d(26): function 'void f2(int)' is not implemented
-fail_compilation/diag9451.d(26): function 'void f2(float) const' is not implemented
-fail_compilation/diag9451.d(26): function 'int f2(float) pure' is not implemented
+fail_compilation/diag9451.d(26): Error: cannot create instance of abstract class `C2`
+fail_compilation/diag9451.d(26): function `void f1()` is not implemented
+fail_compilation/diag9451.d(26): function `void f2(int)` is not implemented
+fail_compilation/diag9451.d(26): function `void f2(float) const` is not implemented
+fail_compilation/diag9451.d(26): function `int f2(float) pure` is not implemented
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9620.d b/gcc/testsuite/gdc.test/fail_compilation/diag9620.d
index 5d50cb8c7ac..d99290c6a39 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9620.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9620.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9620.d(18): Error: pure function 'diag9620.main.bar' cannot call impure function 'diag9620.foo1'
-fail_compilation/diag9620.d(19): Error: pure function 'diag9620.main.bar' cannot call impure function 'diag9620.foo2!().foo2'
+fail_compilation/diag9620.d(18): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1`
+fail_compilation/diag9620.d(19): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9635.d b/gcc/testsuite/gdc.test/fail_compilation/diag9635.d
index 0e15aa2b9a5..fe142adc847 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9635.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9635.d
@@ -2,8 +2,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9635.d(17): Error: need 'this' for 'i' of type 'int'
-fail_compilation/diag9635.d(18): Error: need 'this' for 'foo' of type 'pure nothrow @nogc @safe void()'
+fail_compilation/diag9635.d(17): Error: need `this` for `i` of type `int`
+fail_compilation/diag9635.d(18): Error: need `this` for `foo` of type `pure nothrow @nogc @safe void()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9679.d b/gcc/testsuite/gdc.test/fail_compilation/diag9679.d
index a19d99debd0..4496f0c8920 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9679.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9679.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9679.d(11): Error: variable diag9679.main.n only parameters or foreach declarations can be ref
-fail_compilation/diag9679.d(12): Error: variable diag9679.main.n storage class 'auto' has no effect if type is not inferred, did you mean 'scope'?
+fail_compilation/diag9679.d(11): Error: variable `diag9679.main.n` only parameters or `foreach` declarations can be `ref`
+fail_compilation/diag9679.d(12): Error: variable `diag9679.main.n` storage class `auto` has no effect if type is not inferred, did you mean `scope`?
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9831.d b/gcc/testsuite/gdc.test/fail_compilation/diag9831.d
index 8882f66efc8..b990ced0522 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9831.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9831.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9831.d(12): Error: function diag9831.main.__lambda1 cannot access frame of function D main
+fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3` cannot access variable `c` in frame of function `D main`
+fail_compilation/diag9831.d(11): `c` declared here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9861.d b/gcc/testsuite/gdc.test/fail_compilation/diag9861.d
index 6c7d89f8cc1..53faea189e9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9861.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9861.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9861.d(8): Error: no property 'epsilon' for type 'int'
-fail_compilation/diag9861.d(9): while looking for match for Foo!int
+fail_compilation/diag9861.d(8): Error: no property `epsilon` for type `int`
+fail_compilation/diag9861.d(9): while looking for match for `Foo!int`
---
*/
struct Foo(T, real x = T.epsilon) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9880.d b/gcc/testsuite/gdc.test/fail_compilation/diag9880.d
index 9d893b8d5de..597b94eb13d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9880.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9880.d
@@ -1,7 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/diag9880.d(9): Error: template instance diag9880.foo!string does not match template declaration foo(T)(int) if (is(T == int))
+fail_compilation/diag9880.d(12): Error: template instance `diag9880.foo!string` does not match template declaration `foo(T)(int)`
+ with `T = string`
+ must satisfy the following constraint:
+` is(T == int)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9961.d b/gcc/testsuite/gdc.test/fail_compilation/diag9961.d
index 6b758e5d78d..3eba7352041 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag9961.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag9961.d
@@ -2,9 +2,9 @@
TEST_OUTPUT:
---
fail_compilation/diag9961.d(11): Error: cannot implicitly convert expression `""` of type `string` to `int`
-fail_compilation/diag9961.d(14): Error: template instance diag9961.foo!int error instantiating
+fail_compilation/diag9961.d(14): Error: template instance `diag9961.foo!int` error instantiating
fail_compilation/diag9961.d(11): Error: cannot implicitly convert expression `""` of type `string` to `int`
-fail_compilation/diag9961.d(15): Error: template instance diag9961.foo!char error instantiating
+fail_compilation/diag9961.d(15): Error: template instance `diag9961.foo!char` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_class_alloc.d b/gcc/testsuite/gdc.test/fail_compilation/diag_class_alloc.d
new file mode 100644
index 00000000000..326d82e0718
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag_class_alloc.d
@@ -0,0 +1,19 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/diag_class_alloc.d(15): Error: `new` allocator must be annotated with `@disabled`
+fail_compilation/diag_class_alloc.d(16): Deprecation: `new` allocator with non-empty parameter list is deprecated
+fail_compilation/diag_class_alloc.d(16): Deprecation: `new` allocator with function definition is deprecated
+---
+*/
+
+// This test exists to ensure class allocators and deallocators emit an appropriate error message.
+// This test can be deleted when class allocators and deallocators are removed from the language.
+
+class C
+{
+ new(size_t size) // error message
+ {
+ return malloc(size);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_cstyle.d b/gcc/testsuite/gdc.test/fail_compilation/diag_cstyle.d
index 1b1cd0c458a..72343e77605 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag_cstyle.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag_cstyle.d
@@ -1,13 +1,12 @@
-// REQUIRED_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/diag_cstyle.d(14): Error: instead of C-style syntax, use D-style `int function(int) fp1`
-fail_compilation/diag_cstyle.d(15): Error: instead of C-style syntax, use D-style `int function(int)* fp3`
-fail_compilation/diag_cstyle.d(17): Error: instead of C-style syntax, use D-style `int function(int) FP`
-fail_compilation/diag_cstyle.d(19): Error: instead of C-style syntax, use D-style `int function() fp`
-fail_compilation/diag_cstyle.d(19): Deprecation: instead of C-style syntax, use D-style syntax `int[] arr`
-fail_compilation/diag_cstyle.d(21): Deprecation: instead of C-style syntax, use D-style syntax `string[] result`
+fail_compilation/diag_cstyle.d(13): Error: instead of C-style syntax, use D-style `int function(int) fp1`
+fail_compilation/diag_cstyle.d(14): Error: instead of C-style syntax, use D-style `int function(int)* fp3`
+fail_compilation/diag_cstyle.d(16): Error: instead of C-style syntax, use D-style `int function(int) FP`
+fail_compilation/diag_cstyle.d(18): Error: instead of C-style syntax, use D-style `int function() fp`
+fail_compilation/diag_cstyle.d(18): Error: instead of C-style syntax, use D-style `int[] arr`
+fail_compilation/diag_cstyle.d(20): Error: instead of C-style syntax, use D-style `string[] result`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_debug_conditional.d b/gcc/testsuite/gdc.test/fail_compilation/diag_debug_conditional.d
new file mode 100644
index 00000000000..99884c70c42
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag_debug_conditional.d
@@ -0,0 +1,11 @@
+/**
+TEST_OUTPUT:
+---
+fail_compilation/diag_debug_conditional.d(1): Error: identifier or integer expected inside `debug(...)`, not `alias`
+fail_compilation/diag_debug_conditional.d(2): Error: identifier or integer expected inside `version(...)`, not `alias`
+fail_compilation/diag_debug_conditional.d(3): Error: declaration expected following attribute, not end of file
+---
+ */
+#line 1
+debug(alias)
+version(alias)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_err1.d b/gcc/testsuite/gdc.test/fail_compilation/diag_err1.d
index 50e16b43759..d1659c401cf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag_err1.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag_err1.d
@@ -2,14 +2,14 @@
TEST_OUTPUT:
---
fail_compilation/diag_err1.d(21): Error: undefined identifier `x`
-fail_compilation/diag_err1.d(21): while evaluating pragma(msg, [1, 2, x].length)
+fail_compilation/diag_err1.d(21): while evaluating `pragma(msg, [1, 2, x].length)`
fail_compilation/diag_err1.d(22): Error: undefined identifier `x`
fail_compilation/diag_err1.d(22): Error: undefined identifier `y`
-fail_compilation/diag_err1.d(22): while evaluating pragma(msg, (x + y).sizeof)
+fail_compilation/diag_err1.d(22): while evaluating `pragma(msg, (x + y).sizeof)`
fail_compilation/diag_err1.d(23): Error: undefined identifier `x`
-fail_compilation/diag_err1.d(23): while evaluating pragma(msg, (n += x).sizeof)
-fail_compilation/diag_err1.d(24): Error: incompatible types for ((s) ~ (n)): 'string' and 'int'
-fail_compilation/diag_err1.d(24): while evaluating pragma(msg, (s ~ n).sizeof)
+fail_compilation/diag_err1.d(23): while evaluating `pragma(msg, (n += x).sizeof)`
+fail_compilation/diag_err1.d(24): Error: incompatible types for `(s) ~ (n)`: `string` and `int`
+fail_compilation/diag_err1.d(24): while evaluating `pragma(msg, (s ~ n).sizeof)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d b/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d
new file mode 100644
index 00000000000..b46c562e87d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d
@@ -0,0 +1,40 @@
+/**
+TEST_OUTPUT:
+---
+fail_compilation/diag_funclit.d(103): Error: function literal `__lambda1(x, y, z)` is not callable using argument types `()`
+fail_compilation/diag_funclit.d(103): missing argument for parameter #1: `x`
+fail_compilation/diag_funclit.d(106): Error: function literal `__lambda2(x, y, z)` is not callable using argument types `(int, string, int, int)`
+fail_compilation/diag_funclit.d(106): too many arguments, expected `3`, got `4`
+fail_compilation/diag_funclit.d(108): Error: function literal `__lambda3(x, y, string z = "Hello")` is not callable using argument types `(int, int, string, string)`
+fail_compilation/diag_funclit.d(108): too many arguments, expected `3`, got `4`
+fail_compilation/diag_funclit.d(110): Error: function literal `__lambda4(x, y, string z = "Hello")` is not callable using argument types `(int)`
+fail_compilation/diag_funclit.d(110): too few arguments, expected `3`, got `1`
+fail_compilation/diag_funclit.d(112): Error: function literal `__lambda5(x, y, z)` is not callable using argument types `(int)`
+fail_compilation/diag_funclit.d(112): too few arguments, expected `3`, got `1`
+fail_compilation/diag_funclit.d(115): Error: function literal `__lambda6(x, y, ...)` is not callable using argument types `(int)`
+fail_compilation/diag_funclit.d(115): too few arguments, expected `2`, got `1`
+fail_compilation/diag_funclit.d(117): Error: function literal `__lambda7(x, y, string z = "Hey", ...)` is not callable using argument types `(int)`
+fail_compilation/diag_funclit.d(117): too few arguments, expected `3`, got `1`
+---
+ */
+
+#line 100
+void main()
+{
+ // No argument
+ (x, y, z) { return 42; }();
+
+ // Too many args, non-variadic
+ (x, y, z) { return 42; }(42, "Hello", 42, 42);
+ // Too many args, non-variadic, default param
+ (x, y, string z = "Hello") { return x; }(42, 42, "Nope", "Noooope");
+ // Too few args, non-variadic
+ (x, y, string z = "Hello") { return x; }(42);
+ // Too few args, non-variadic, default param
+ (x, y, z) { return x; }(42);
+
+ // Too few args, variadic
+ (x, y, ...) { return x; }(42);
+ // Too few args, variadic, default param
+ (x, y, string z = "Hey", ...) { return x; }(42);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_template_alias.d b/gcc/testsuite/gdc.test/fail_compilation/diag_template_alias.d
new file mode 100644
index 00000000000..bbfb5a0e9d2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag_template_alias.d
@@ -0,0 +1,11 @@
+/**
+TEST_OUTPUT:
+---
+fail_compilation/diag_template_alias.d(1): Error: identifier expected for template `alias` parameter
+fail_compilation/diag_template_alias.d(1): Error: found `alias` when expecting `(`
+fail_compilation/diag_template_alias.d(1): Error: semicolon expected following function declaration
+fail_compilation/diag_template_alias.d(1): Error: declaration expected, not `(`
+---
+ */
+#line 1
+void func1(alias alias)() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_template_this.d b/gcc/testsuite/gdc.test/fail_compilation/diag_template_this.d
new file mode 100644
index 00000000000..778f68e8842
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag_template_this.d
@@ -0,0 +1,11 @@
+/**
+TEST_OUTPUT:
+---
+fail_compilation/diag_template_this.d(1): Error: identifier expected for template `this` parameter
+fail_compilation/diag_template_this.d(1): Error: found `this` when expecting `(`
+fail_compilation/diag_template_this.d(1): Error: semicolon expected following function declaration
+fail_compilation/diag_template_this.d(1): Error: declaration expected, not `(`
+---
+ */
+#line 1
+void func1(this this)() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diagin.d b/gcc/testsuite/gdc.test/fail_compilation/diagin.d
new file mode 100644
index 00000000000..a4dabee073a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diagin.d
@@ -0,0 +1,25 @@
+/*
+PERMUTE_ARGS: -preview=in
+TEST_OUTPUT:
+---
+fail_compilation/diagin.d(14): Error: function `diagin.foo(in int)` is not callable using argument types `()`
+fail_compilation/diagin.d(14): missing argument for parameter #1: `in int`
+fail_compilation/diagin.d(16): Error: template `diagin.foo1` cannot deduce function from argument types `!()(bool[])`
+fail_compilation/diagin.d(20): Candidate is: `foo1(T)(in T v, string)`
+---
+ */
+
+void main ()
+{
+ foo();
+ bool[] lvalue;
+ foo1(lvalue);
+}
+
+void foo(in int) {}
+void foo1(T)(in T v, string) {}
+
+// Ensure that `in` has a unique mangling
+static assert(foo.mangleof == `_D6diagin3fooFIiZv`);
+static assert(foo1!int.mangleof == `_D6diagin__T4foo1TiZQiFNaNbNiNfIiAyaZv`);
+static assert(foo1!char.mangleof == `_D6diagin__T4foo1TaZQiFNaNbNiNfIaAyaZv`);
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diaginref.d b/gcc/testsuite/gdc.test/fail_compilation/diaginref.d
new file mode 100644
index 00000000000..2e83d76b562
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/diaginref.d
@@ -0,0 +1,13 @@
+/*
+REQUIRED_ARGS: -preview=in
+TEST_OUTPUT:
+---
+fail_compilation/diaginref.d(11): Error: attribute `ref` is redundant with previously-applied `in`
+fail_compilation/diaginref.d(13): Error: attribute `in` cannot be added after `ref`: remove `ref`
+---
+ */
+
+void foo(in string) {}
+void foo1(in ref string) {}
+void foo2(T)(in T v, string) {}
+void foo3(T)(ref in T v, string) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/dip22a.d b/gcc/testsuite/gdc.test/fail_compilation/dip22a.d
index 5bd2b5b0304..bf04a517c71 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/dip22a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/dip22a.d
@@ -1,10 +1,10 @@
/*
-REQUIRED_ARGS:
+EXTRA_FILES: imports/dip22a.d
TEST_OUTPUT:
---
-fail_compilation/dip22a.d(16): Error: no property `bar` for type `imports.dip22a.Klass`, did you mean `imports.dip22a.Klass.bar`?
-fail_compilation/dip22a.d(17): Error: no property `bar` for type `Struct`, did you mean `imports.dip22a.Struct.bar`?
-fail_compilation/dip22a.d(18): Error: undefined identifier `bar` in module `imports.dip22a`, did you mean function `bar`?
+fail_compilation/dip22a.d(16): Error: no property `bar` for type `imports.dip22a.Klass`
+fail_compilation/dip22a.d(17): Error: no property `bar` for type `imports.dip22a.Struct`
+fail_compilation/dip22a.d(18): Error: undefined identifier `bar` in module `imports.dip22a`
fail_compilation/dip22a.d(19): Error: no property `bar` for type `void`
fail_compilation/dip22a.d(20): Error: no property `bar` for type `int`
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/dip22b.d b/gcc/testsuite/gdc.test/fail_compilation/dip22b.d
index 952018022de..0cd7247a85a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/dip22b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/dip22b.d
@@ -1,5 +1,5 @@
/*
-REQUIRED_ARGS:
+EXTRA_FILES: imports/dip22b.d imports/dip22c.d
TEST_OUTPUT:
---
fail_compilation/dip22b.d(12): Error: undefined identifier `Foo`, did you mean variable `foo`?
diff --git a/gcc/testsuite/gdc.test/fail_compilation/dip22e.d b/gcc/testsuite/gdc.test/fail_compilation/dip22e.d
index b1411b2f10c..f118cf12340 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/dip22e.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/dip22e.d
@@ -1,5 +1,5 @@
/*
-REQUIRED_ARGS:
+EXTRA_FILES: imports/dip22d.d imports/dip22e.d
TEST_OUTPUT:
---
fail_compilation/dip22e.d(14): Error: undefined identifier `foo`, did you mean struct `Foo`?
diff --git a/gcc/testsuite/gdc.test/fail_compilation/dip25.d b/gcc/testsuite/gdc.test/fail_compilation/dip25.d
new file mode 100644
index 00000000000..44fec37a002
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/dip25.d
@@ -0,0 +1,29 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/dip25.d(17): Deprecation: returning `this.buffer[]` escapes a reference to parameter `this`
+fail_compilation/dip25.d(17): perhaps annotate the parameter with `return`
+fail_compilation/dip25.d(22): Error: returning `identity(x)` escapes a reference to local variable `x`
+fail_compilation/dip25.d(23): Deprecation: returning `identity(x)` escapes a reference to parameter `x`
+fail_compilation/dip25.d(23): perhaps annotate the parameter with `return`
+---
+*/
+struct Data
+{
+ char[256] buffer;
+ @property const(char)[] filename() const pure nothrow
+ {
+ return buffer[];
+ }
+}
+
+ref int identity(return ref int x) { return x; }
+ref int fun(return int x) { return identity(x); }
+ref int fun2(ref int x) { return identity(x); }
+
+void main()
+{
+ Data d;
+ const f = d.filename;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/disable_new.d b/gcc/testsuite/gdc.test/fail_compilation/disable_new.d
new file mode 100644
index 00000000000..33ae32c2d8a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/disable_new.d
@@ -0,0 +1,25 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/disable_new.d(23): Error: cannot allocate `class C` with `new` because it is annotated with `@disable new()`
+fail_compilation/disable_new.d(24): Error: cannot allocate `struct S` with `new` because it is annotated with `@disable new()`
+---
+*/
+
+class C
+{
+ // force user of a type to use an external allocation strategy
+ @disable new();
+}
+
+struct S
+{
+ // force user of a type to use an external allocation strategy
+ @disable new();
+}
+
+void main()
+{
+ auto c = new C();
+ auto s = new S();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/dtor_attributes.d b/gcc/testsuite/gdc.test/fail_compilation/dtor_attributes.d
new file mode 100644
index 00000000000..05c5d30c5ed
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/dtor_attributes.d
@@ -0,0 +1,190 @@
+/*
+Informative error messages if the compiler generated destructor overrides a user-defined one.
+
+TEST_OUTPUT:
+---
+fail_compilation/dtor_attributes.d(118): Error: `pure` function `dtor_attributes.test1` cannot call impure destructor `dtor_attributes.Strict.~this`
+fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is impure because of the following field's destructors:
+fail_compilation/dtor_attributes.d(111): - HasDtor member
+fail_compilation/dtor_attributes.d(103): impure `HasDtor.~this` is declared here
+fail_compilation/dtor_attributes.d(118): Error: `@safe` function `dtor_attributes.test1` cannot call `@system` destructor `dtor_attributes.Strict.~this`
+fail_compilation/dtor_attributes.d(113): `dtor_attributes.Strict.~this` is declared here
+fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is @system because of the following field's destructors:
+fail_compilation/dtor_attributes.d(111): - HasDtor member
+fail_compilation/dtor_attributes.d(103): @system `HasDtor.~this` is declared here
+fail_compilation/dtor_attributes.d(118): Error: `@nogc` function `dtor_attributes.test1` cannot call non-@nogc destructor `dtor_attributes.Strict.~this`
+fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is non-@nogc because of the following field's destructors:
+fail_compilation/dtor_attributes.d(111): - HasDtor member
+fail_compilation/dtor_attributes.d(103): non-@nogc `HasDtor.~this` is declared here
+fail_compilation/dtor_attributes.d(118): Error: destructor `dtor_attributes.Strict.~this` is not `nothrow`
+fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is not nothrow because of the following field's destructors:
+fail_compilation/dtor_attributes.d(111): - HasDtor member
+fail_compilation/dtor_attributes.d(103): not nothrow `HasDtor.~this` is declared here
+fail_compilation/dtor_attributes.d(116): Error: `nothrow` function `dtor_attributes.test1` may throw
+---
+*/
+#line 100
+
+struct HasDtor
+{
+ ~this() {}
+}
+
+// The user-defined dtor is overriden by a generated dtor calling both
+// - HasDtor.~this
+// - Strict.~this
+struct Strict
+{
+ HasDtor member;
+
+ ~this() pure nothrow @nogc @safe {}
+}
+
+void test1() pure nothrow @nogc @safe
+{
+ Strict s;
+}
+
+/*
+Works for clases as well.
+
+TEST_OUTPUT:
+---
+fail_compilation/dtor_attributes.d(209): Error: `pure` function `dtor_attributes.test2` cannot call impure destructor `dtor_attributes.StrictClass.~this`
+fail_compilation/dtor_attributes.d(204): generated `StrictClass.~this` is impure because of the following field's destructors:
+fail_compilation/dtor_attributes.d(203): - HasDtor member
+fail_compilation/dtor_attributes.d(103): impure `HasDtor.~this` is declared here
+---
+*/
+#line 200
+
+class StrictClass
+{
+ HasDtor member;
+ ~this() pure {}
+}
+
+void test2() pure
+{
+ scope instance = new StrictClass();
+}
+
+/*
+Ignores members whose destructors are not called.
+
+TEST_OUTPUT:
+---
+fail_compilation/dtor_attributes.d(321): Error: `pure` function `dtor_attributes.test3` cannot call impure destructor `dtor_attributes.StrictStructRef.~this`
+fail_compilation/dtor_attributes.d(316): generated `StrictStructRef.~this` is impure because of the following field's destructors:
+fail_compilation/dtor_attributes.d(310): - HasDtor structMember
+fail_compilation/dtor_attributes.d(103): impure `HasDtor.~this` is declared here
+---
+*/
+#line 300
+
+class HasDtorClass
+{
+ ~this() {}
+}
+
+struct Empty {}
+
+struct StrictStructRef
+{
+ HasDtor structMember;
+ HasDtorClass classMember;
+ int intMember;
+ int[2] arrayMember;
+ Empty e;
+
+ ~this() pure {}
+}
+
+void test3() pure
+{
+ StrictStructRef structInstance;
+}
+
+/*
+Types from nested types work as well.
+
+TEST_OUTPUT:
+---
+fail_compilation/dtor_attributes.d(411): Error: `pure` function `dtor_attributes.test4` cannot call impure destructor `dtor_attributes.StrictNested.~this`
+fail_compilation/dtor_attributes.d(406): generated `StrictNested.~this` is impure because of the following field's destructors:
+fail_compilation/dtor_attributes.d(403): - HasDtor[4] arrayMember
+fail_compilation/dtor_attributes.d(103): impure `HasDtor.~this` is declared here
+---
+*/
+#line 400
+
+struct StrictNested
+{
+ HasDtor[4] arrayMember;
+ HasDtorClass[4] classMember;
+
+ ~this() pure {}
+}
+
+void test4() pure
+{
+ StrictNested structInstance;
+}
+
+/*
+Ignores member destructors when the user-defined one is permissive enough (e.g. both impure)
+
+TEST_OUTPUT:
+---
+fail_compilation/dtor_attributes.d(509): Error: `pure` function `dtor_attributes.test5` cannot call impure destructor `dtor_attributes.Permissive.~this`
+---
+*/
+#line 500
+
+struct Permissive
+{
+ HasDtor[4] arrayMember;
+ ~this() {}
+}
+
+void test5() pure
+{
+ Permissive structInstance;
+}
+
+/*
+Works with destructors generated through multiple layers
+
+TEST_OUTPUT:
+---
+fail_compilation/dtor_attributes.d(618): Error: `pure` function `dtor_attributes.test6` cannot call impure destructor `dtor_attributes.HasNestedDtor3.~this`
+fail_compilation/dtor_attributes.d(611): generated `HasNestedDtor3.~this` is impure because of the following field's destructors:
+fail_compilation/dtor_attributes.d(613): - HasNestedDtor2 member3
+fail_compilation/dtor_attributes.d(606): generated `HasNestedDtor2.~this` is impure because of the following field's destructors:
+fail_compilation/dtor_attributes.d(608): - HasNestedDtor1 member2
+fail_compilation/dtor_attributes.d(601): generated `HasNestedDtor1.~this` is impure because of the following field's destructors:
+fail_compilation/dtor_attributes.d(603): - HasDtor member1
+fail_compilation/dtor_attributes.d(103): impure `HasDtor.~this` is declared here
+---
+*/
+#line 600
+
+struct HasNestedDtor1
+{
+ HasDtor member1;
+}
+
+struct HasNestedDtor2
+{
+ HasNestedDtor1 member2;
+}
+
+struct HasNestedDtor3
+{
+ HasNestedDtor2 member3;
+}
+
+void test6() pure
+{
+ HasNestedDtor3 instance;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/dtorfields_attributes.d b/gcc/testsuite/gdc.test/fail_compilation/dtorfields_attributes.d
new file mode 100644
index 00000000000..f6cab893bb4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/dtorfields_attributes.d
@@ -0,0 +1,43 @@
+/*
+Informative error messages if the compiler inserted an optional destructor call into the constructor.
+
+REQUIRED_ARGS: -preview=dtorfields
+TEST_OUTPUT:
+---
+fail_compilation/dtorfields_attributes.d(117): Error: `pure` constructor `dtorfields_attributes.Strict.this` cannot call impure destructor `dtorfields_attributes.Strict.~this`
+fail_compilation/dtorfields_attributes.d(119): generated `Strict.~this` is impure because of the following field's destructors:
+fail_compilation/dtorfields_attributes.d(115): - HasDtor member
+fail_compilation/dtorfields_attributes.d(103): impure `HasDtor.~this` is declared here
+fail_compilation/dtorfields_attributes.d(117): Error: `@safe` constructor `dtorfields_attributes.Strict.this` cannot call `@system` destructor `dtorfields_attributes.Strict.~this`
+fail_compilation/dtorfields_attributes.d(119): `dtorfields_attributes.Strict.~this` is declared here
+fail_compilation/dtorfields_attributes.d(119): generated `Strict.~this` is @system because of the following field's destructors:
+fail_compilation/dtorfields_attributes.d(115): - HasDtor member
+fail_compilation/dtorfields_attributes.d(103): @system `HasDtor.~this` is declared here
+fail_compilation/dtorfields_attributes.d(117): Error: `@nogc` constructor `dtorfields_attributes.Strict.this` cannot call non-@nogc destructor `dtorfields_attributes.Strict.~this`
+fail_compilation/dtorfields_attributes.d(119): generated `Strict.~this` is non-@nogc because of the following field's destructors:
+fail_compilation/dtorfields_attributes.d(115): - HasDtor member
+fail_compilation/dtorfields_attributes.d(103): non-@nogc `HasDtor.~this` is declared here
+---
+*/
+#line 100
+
+struct HasDtor
+{
+ ~this()
+ {
+ // Enforce @system, ... just to be sure
+ __gshared int i;
+ if (++i)
+ throw new Exception(new immutable(char)[](10));
+ }
+}
+
+// The user-defined dtor matches the ctor attributes
+struct Strict
+{
+ HasDtor member;
+
+ this(int) pure @nogc @safe {} // nothrow doesn't generate dtor call
+
+ ~this() pure @nogc @safe {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/e15876_1.d b/gcc/testsuite/gdc.test/fail_compilation/e15876_1.d
new file mode 100644
index 00000000000..33bee254589
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/e15876_1.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/e15876_1.d(15): Error: valid scope identifiers are `exit`, `failure`, or `success`, not `x`
+fail_compilation/e15876_1.d(16): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_1.d(16): Error: found `End of File` instead of statement
+fail_compilation/e15876_1.d(16): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/e15876_1.d(16): Error: found `End of File` when expecting `]`
+fail_compilation/e15876_1.d(16): Error: no identifier for declarator `o[()
+{
+scope(exit) }
+]`
+---
+*/
+o[{scope(x
diff --git a/gcc/testsuite/gdc.test/fail_compilation/e15876_2.d b/gcc/testsuite/gdc.test/fail_compilation/e15876_2.d
new file mode 100644
index 00000000000..10ff7d53a98
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/e15876_2.d
@@ -0,0 +1,14 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/e15876_2.d(15): Error: identifier expected following `template`
+fail_compilation/e15876_2.d(15): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/e15876_2.d(15): Error: found `End of File` when expecting `]`
+fail_compilation/e15876_2.d(15): Error: no identifier for declarator `o[()
+{
+;
+}
+]`
+---
+*/
+o[{template
diff --git a/gcc/testsuite/gdc.test/fail_compilation/e15876_3.d b/gcc/testsuite/gdc.test/fail_compilation/e15876_3.d
new file mode 100644
index 00000000000..ae5f77a33e8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/e15876_3.d
@@ -0,0 +1,25 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/e15876_3.d(25): Error: unexpected `(` in declarator
+fail_compilation/e15876_3.d(25): Error: basic type expected, not `=`
+fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `(`
+fail_compilation/e15876_3.d(26): Error: found `End of File` instead of statement
+fail_compilation/e15876_3.d(26): Error: expression expected, not `End of File`
+fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `;` following `for` condition
+fail_compilation/e15876_3.d(26): Error: expression expected, not `End of File`
+fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_3.d(26): Error: found `End of File` instead of statement
+fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/e15876_3.d(26): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_3.d(26): Error: no identifier for declarator `d(_error_ = ()
+{
+for (; 0; 0)
+{
+}
+}
+)`
+fail_compilation/e15876_3.d(26): Error: semicolon expected following function declaration
+---
+*/
+d(={for
diff --git a/gcc/testsuite/gdc.test/fail_compilation/e15876_4.d b/gcc/testsuite/gdc.test/fail_compilation/e15876_4.d
new file mode 100644
index 00000000000..6f46633f002
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/e15876_4.d
@@ -0,0 +1,23 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/e15876_4.d(23): Error: found `)` when expecting `(`
+fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `(`
+fail_compilation/e15876_4.d(24): Error: found `End of File` instead of statement
+fail_compilation/e15876_4.d(24): Error: expression expected, not `End of File`
+fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `;` following `for` condition
+fail_compilation/e15876_4.d(24): Error: expression expected, not `End of File`
+fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_4.d(24): Error: found `End of File` instead of statement
+fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/e15876_4.d(24): Error: found `End of File` when expecting `)`
+fail_compilation/e15876_4.d(24): Error: no identifier for declarator `typeof(()
+{
+for (; 0; 0)
+{
+}
+}
+)`
+---
+*/
+typeof){for
diff --git a/gcc/testsuite/gdc.test/fail_compilation/e15876_5.d b/gcc/testsuite/gdc.test/fail_compilation/e15876_5.d
new file mode 100644
index 00000000000..5b65b1b345a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/e15876_5.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/e15876_5.d(16): Error: basic type expected, not `End of File`
+fail_compilation/e15876_5.d(16): Error: semicolon expected to close `alias` declaration
+fail_compilation/e15876_5.d(16): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/e15876_5.d(16): Error: found `End of File` when expecting `]`
+fail_compilation/e15876_5.d(16): Error: no identifier for declarator `p[()
+{
+alias ;
+}
+]`
+---
+*/
+p[{alias
diff --git a/gcc/testsuite/gdc.test/fail_compilation/e15876_6.d b/gcc/testsuite/gdc.test/fail_compilation/e15876_6.d
new file mode 100644
index 00000000000..7547b387dff
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/e15876_6.d
@@ -0,0 +1,7 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/e15876_6.d(7): Error: identifier expected following `(type)`.
+---
+*/
+auto unaryExParseError = immutable(int).;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/enum9921.d b/gcc/testsuite/gdc.test/fail_compilation/enum9921.d
index 54c76b9f5cd..90d8802d6d8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/enum9921.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/enum9921.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/enum9921.d(9): Error: enum enum9921.X base type must not be void
-fail_compilation/enum9921.d(11): Error: enum enum9921.Z base type must not be void
+fail_compilation/enum9921.d(9): Error: enum `enum9921.X` base type must not be `void`
+fail_compilation/enum9921.d(11): Error: enum `enum9921.Z` base type must not be `void`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/extra-files/a14446.d b/gcc/testsuite/gdc.test/fail_compilation/extra-files/a14446.d
deleted file mode 100644
index 5e19c1e7501..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/extra-files/a14446.d
+++ /dev/null
@@ -1,6 +0,0 @@
-module a14446;
-
-struct CDBMaker
-{
- import ice14446;
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10.d b/gcc/testsuite/gdc.test/fail_compilation/fail10.d
index 9d73537216d..48901a1b4e4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10.d(18): Error: mixin Foo!y cannot resolve forward reference
+fail_compilation/fail10.d(18): Error: mixin `Foo!y` cannot resolve forward reference
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail100.d b/gcc/testsuite/gdc.test/fail_compilation/fail100.d
index 1dd80508a6a..a8189ecb0af 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail100.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail100.d
@@ -5,8 +5,8 @@ fail_compilation/fail100.d(24): Error: cannot implicitly convert expression `f`
---
*/
-// Issue 85 - Array of classes doesn't function as array of interfaces
-
+// https://issues.dlang.org/show_bug.cgi?id=85
+// Array of classes doesn't function as array of interfaces
interface I
{
I[] foo();
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10082.d b/gcc/testsuite/gdc.test/fail_compilation/fail10082.d
index fd3080109d4..8a3d2fbf566 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10082.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10082.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10082.d(24): Error: cannot infer type from overloaded function symbol &foo
+fail_compilation/fail10082.d(24): Error: cannot infer type from overloaded function symbol `&foo`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail101.d b/gcc/testsuite/gdc.test/fail_compilation/fail101.d
index 35d23e3edd2..0f6e0b3ee49 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail101.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail101.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail101.d(8): Error: cannot implicitly convert expression `1` of type `int` to `creal`
+fail_compilation/fail101.d(9): Deprecation: use of complex type `creal` is deprecated, use `std.complex.Complex!(real)` instead
+fail_compilation/fail101.d(9): Error: cannot implicitly convert expression `1` of type `int` to `creal`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10102.d b/gcc/testsuite/gdc.test/fail_compilation/fail10102.d
index 2c974ea90c9..4847413ffea 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10102.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10102.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10102.d(48): Error: variable fail10102.main.m default construction is disabled for type NotNull!(int*)
-fail_compilation/fail10102.d(49): Error: variable fail10102.main.a default construction is disabled for type NotNull!(int*)[3]
-fail_compilation/fail10102.d(50): Error: default construction is disabled for type NotNull!(int*)
+fail_compilation/fail10102.d(48): Error: variable `fail10102.main.m` default construction is disabled for type `NotNull!(int*)`
+fail_compilation/fail10102.d(49): Error: variable `fail10102.main.a` default construction is disabled for type `NotNull!(int*)[3]`
+fail_compilation/fail10102.d(50): Error: default construction is disabled for type `NotNull!(int*)`
fail_compilation/fail10102.d(51): Error: field `S.m` must be initialized because it has no default constructor
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10115.d b/gcc/testsuite/gdc.test/fail_compilation/fail10115.d
index e94ae876e8e..4d766cf7956 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10115.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10115.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10115.d(35): Error: cannot have out parameter of type S because the default construction is disabled
-fail_compilation/fail10115.d(35): Error: cannot have out parameter of type E because the default construction is disabled
-fail_compilation/fail10115.d(35): Error: cannot have out parameter of type U because the default construction is disabled
-fail_compilation/fail10115.d(40): Error: struct fail10115.S default construction is disabled
-fail_compilation/fail10115.d(41): Error: struct fail10115.S default construction is disabled
-fail_compilation/fail10115.d(42): Error: union fail10115.U default construction is disabled
+fail_compilation/fail10115.d(35): Error: cannot have `out` parameter of type `S` because the default construction is disabled
+fail_compilation/fail10115.d(35): Error: cannot have `out` parameter of type `E` because the default construction is disabled
+fail_compilation/fail10115.d(35): Error: cannot have `out` parameter of type `U` because the default construction is disabled
+fail_compilation/fail10115.d(40): Error: struct `fail10115.S` default construction is disabled
+fail_compilation/fail10115.d(41): Error: struct `fail10115.S` default construction is disabled
+fail_compilation/fail10115.d(42): Error: union `fail10115.U` default construction is disabled
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10254.d b/gcc/testsuite/gdc.test/fail_compilation/fail10254.d
index 0d402e12b85..8ad8fded58b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10254.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10254.d
@@ -1,10 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10254.d(18): Error: pure function 'fail10254.foo' cannot call impure constructor 'fail10254.C.this'
-fail_compilation/fail10254.d(18): Error: @safe function 'fail10254.foo' cannot call @system constructor 'fail10254.C.this'
-fail_compilation/fail10254.d(19): Error: pure function 'fail10254.foo' cannot call impure constructor 'fail10254.S.this'
-fail_compilation/fail10254.d(19): Error: @safe function 'fail10254.foo' cannot call @system constructor 'fail10254.S.this'
+fail_compilation/fail10254.d(20): Error: `pure` function `fail10254.foo` cannot call impure constructor `fail10254.C.this`
+fail_compilation/fail10254.d(20): Error: `@safe` function `fail10254.foo` cannot call `@system` constructor `fail10254.C.this`
+fail_compilation/fail10254.d(15): `fail10254.C.this` is declared here
+fail_compilation/fail10254.d(21): Error: `pure` function `fail10254.foo` cannot call impure constructor `fail10254.S.this`
+fail_compilation/fail10254.d(21): Error: `@safe` function `fail10254.foo` cannot call `@system` constructor `fail10254.S.this`
+fail_compilation/fail10254.d(16): `fail10254.S.this` is declared here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10277.d b/gcc/testsuite/gdc.test/fail_compilation/fail10277.d
index 6173d37c2e7..11ad9d5a75f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10277.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10277.d
@@ -1,28 +1,28 @@
+// EXTRA_FILES: imports/fail10277.d
module fail10227;
-
/*
TEST_OUTPUT:
---
-fail_compilation/imports/fail10277.d(3): Error: class TypeInfo only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(4): Error: class TypeInfo_Class only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(5): Error: class TypeInfo_Interface only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(6): Error: class TypeInfo_Struct only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(8): Error: class TypeInfo_Pointer only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(9): Error: class TypeInfo_Array only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(10): Error: class TypeInfo_AssociativeArray only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(11): Error: class TypeInfo_Enum only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(12): Error: class TypeInfo_Function only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(13): Error: class TypeInfo_Delegate only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(14): Error: class TypeInfo_Tuple only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(15): Error: class TypeInfo_Const only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(16): Error: class TypeInfo_Invariant only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(17): Error: class TypeInfo_Shared only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(18): Error: class TypeInfo_Inout only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(19): Error: class TypeInfo_Vector only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(20): Error: class Object only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(21): Error: class Throwable only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(22): Error: class Exception only object.d can define this reserved class name
-fail_compilation/imports/fail10277.d(23): Error: class Error only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(3): Error: class `TypeInfo` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(4): Error: class `TypeInfo_Class` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(5): Error: class `TypeInfo_Interface` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(6): Error: class `TypeInfo_Struct` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(8): Error: class `TypeInfo_Pointer` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(9): Error: class `TypeInfo_Array` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(10): Error: class `TypeInfo_AssociativeArray` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(11): Error: class `TypeInfo_Enum` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(12): Error: class `TypeInfo_Function` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(13): Error: class `TypeInfo_Delegate` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(14): Error: class `TypeInfo_Tuple` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(15): Error: class `TypeInfo_Const` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(16): Error: class `TypeInfo_Invariant` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(17): Error: class `TypeInfo_Shared` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(18): Error: class `TypeInfo_Inout` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(19): Error: class `TypeInfo_Vector` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(20): Error: class `Object` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(21): Error: class `Throwable` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(22): Error: class `Exception` only object.d can define this reserved class name
+fail_compilation/imports/fail10277.d(23): Error: class `Error` only object.d can define this reserved class name
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10299.d b/gcc/testsuite/gdc.test/fail_compilation/fail10299.d
index 29475aa849a..f0eaeba821a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10299.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10299.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10299.d(11): Error: foo!string is not an lvalue
+fail_compilation/fail10299.d(11): Error: `foo!string` is not an lvalue and cannot be modified
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail104.d b/gcc/testsuite/gdc.test/fail_compilation/fail104.d
index 2111844d56d..e9190de6206 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail104.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail104.d
@@ -1,4 +1,5 @@
-// Issue 76 - Using a non-template struct as a template
+// https://issues.dlang.org/show_bug.cgi?id=76
+// Using a non-template struct as a template
// Compiling leads to "Assertion failure: 's->parent' on line 1694 in file
// 'template.c'"
/*
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10481.d b/gcc/testsuite/gdc.test/fail_compilation/fail10481.d
index 878cbe8f05e..a6657c04b39 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10481.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10481.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/fail10481.d(11): Error: undefined identifier `T1`, did you mean alias `T0`?
-fail_compilation/fail10481.d(15): Error: cannot infer type from template instance get!(A)
+fail_compilation/fail10481.d(15): Error: cannot infer type from template instance `get!(A)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail105.d b/gcc/testsuite/gdc.test/fail_compilation/fail105.d
index 8702cac2f12..0e13e3677eb 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail105.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail105.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail105.d(11): Error: cannot cast "bar" to int at compile time
+fail_compilation/fail105.d(11): Error: cannot cast `"bar"` to `int` at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10528.d b/gcc/testsuite/gdc.test/fail_compilation/fail10528.d
index 067e83c035d..38c5a23a529 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10528.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10528.d
@@ -1,14 +1,15 @@
/*
+EXTRA_FILES: imports/a10528.d
TEST_OUTPUT:
---
-fail_compilation/fail10528.d(19): Error: undefined identifier `a`
-fail_compilation/fail10528.d(20): Error: undefined identifier `a` in module `a10528`, did you mean variable `a`?
-fail_compilation/fail10528.d(22): Error: undefined identifier `b`
-fail_compilation/fail10528.d(23): Error: undefined identifier `b` in module `a10528`, did you mean enum member `b`?
-fail_compilation/fail10528.d(25): Error: no property `c` for type `S`, did you mean `a10528.S.c`?
-fail_compilation/fail10528.d(26): Error: no property `c` for type `S`, did you mean `a10528.S.c`?
-fail_compilation/fail10528.d(28): Error: no property `d` for type `a10528.C`, did you mean `a10528.C.d`?
-fail_compilation/fail10528.d(29): Error: no property `d` for type `a10528.C`, did you mean `a10528.C.d`?
+fail_compilation/fail10528.d(20): Error: undefined identifier `a`
+fail_compilation/fail10528.d(21): Error: undefined identifier `a` in module `a10528`
+fail_compilation/fail10528.d(23): Error: undefined identifier `b`
+fail_compilation/fail10528.d(24): Error: undefined identifier `b` in module `a10528`
+fail_compilation/fail10528.d(26): Error: no property `c` for type `a10528.S`
+fail_compilation/fail10528.d(27): Error: no property `c` for type `a10528.S`
+fail_compilation/fail10528.d(29): Error: no property `d` for type `a10528.C`
+fail_compilation/fail10528.d(30): Error: no property `d` for type `a10528.C`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10534.d b/gcc/testsuite/gdc.test/fail_compilation/fail10534.d
index 2516135089e..fac37f444e3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10534.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10534.d
@@ -1,22 +1,22 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10534.d(28): Error: 'a' is not of arithmetic type, it is a int delegate()
-fail_compilation/fail10534.d(28): Error: 'b' is not of arithmetic type, it is a int delegate()
-fail_compilation/fail10534.d(29): Error: 'a' is not of arithmetic type, it is a int delegate()
-fail_compilation/fail10534.d(29): Error: 'b' is not of arithmetic type, it is a int delegate()
-fail_compilation/fail10534.d(30): Error: 'a' is not of arithmetic type, it is a int delegate()
-fail_compilation/fail10534.d(30): Error: 'b' is not of arithmetic type, it is a int delegate()
-fail_compilation/fail10534.d(31): Error: 'a' is not of arithmetic type, it is a int delegate()
-fail_compilation/fail10534.d(31): Error: 'b' is not of arithmetic type, it is a int delegate()
-fail_compilation/fail10534.d(36): Error: 'a' is not of arithmetic type, it is a int function()
-fail_compilation/fail10534.d(36): Error: 'b' is not of arithmetic type, it is a int function()
-fail_compilation/fail10534.d(37): Error: 'a' is not of arithmetic type, it is a int function()
-fail_compilation/fail10534.d(37): Error: 'b' is not of arithmetic type, it is a int function()
-fail_compilation/fail10534.d(38): Error: 'a' is not of arithmetic type, it is a int function()
-fail_compilation/fail10534.d(38): Error: 'b' is not of arithmetic type, it is a int function()
-fail_compilation/fail10534.d(39): Error: 'a' is not of arithmetic type, it is a int function()
-fail_compilation/fail10534.d(39): Error: 'b' is not of arithmetic type, it is a int function()
+fail_compilation/fail10534.d(28): Error: `a` is not of arithmetic type, it is a `int delegate()`
+fail_compilation/fail10534.d(28): Error: `b` is not of arithmetic type, it is a `int delegate()`
+fail_compilation/fail10534.d(29): Error: `a` is not of arithmetic type, it is a `int delegate()`
+fail_compilation/fail10534.d(29): Error: `b` is not of arithmetic type, it is a `int delegate()`
+fail_compilation/fail10534.d(30): Error: `a` is not of arithmetic type, it is a `int delegate()`
+fail_compilation/fail10534.d(30): Error: `b` is not of arithmetic type, it is a `int delegate()`
+fail_compilation/fail10534.d(31): Error: `a` is not of arithmetic type, it is a `int delegate()`
+fail_compilation/fail10534.d(31): Error: `b` is not of arithmetic type, it is a `int delegate()`
+fail_compilation/fail10534.d(36): Error: `a` is not of arithmetic type, it is a `int function()`
+fail_compilation/fail10534.d(36): Error: `b` is not of arithmetic type, it is a `int function()`
+fail_compilation/fail10534.d(37): Error: `a` is not of arithmetic type, it is a `int function()`
+fail_compilation/fail10534.d(37): Error: `b` is not of arithmetic type, it is a `int function()`
+fail_compilation/fail10534.d(38): Error: `a` is not of arithmetic type, it is a `int function()`
+fail_compilation/fail10534.d(38): Error: `b` is not of arithmetic type, it is a `int function()`
+fail_compilation/fail10534.d(39): Error: `a` is not of arithmetic type, it is a `int function()`
+fail_compilation/fail10534.d(39): Error: `b` is not of arithmetic type, it is a `int function()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail106.d b/gcc/testsuite/gdc.test/fail_compilation/fail106.d
index f6dc2eaadd6..ba9f7dd69cd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail106.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail106.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail106.d(12): Error: cannot modify immutable expression 'C'
+fail_compilation/fail106.d(12): Error: cannot modify `immutable` expression `"ABC"[2]`
---
*/
-// Issue 239 - Internal error: changing string literal elements
-
+// https://issues.dlang.org/show_bug.cgi?id=239
+// Internal error: changing string literal elements
void main()
{
"ABC"[2] = 's';
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10630.d b/gcc/testsuite/gdc.test/fail_compilation/fail10630.d
index 738bdd14e34..35feaea2424 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10630.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10630.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10630.d(12): Error: cannot have out parameter of type S because the default construction is disabled
+fail_compilation/fail10630.d(12): Error: cannot have `out` parameter of type `S` because the default construction is disabled
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10666.d b/gcc/testsuite/gdc.test/fail_compilation/fail10666.d
index 2e1747d4d2b..9fe28d02ffd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10666.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10666.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10666.d(16): Error: variable fail10666.foo10666.s1 has scoped destruction, cannot build closure
+fail_compilation/fail10666.d(16): Error: variable `fail10666.foo10666.s1` has scoped destruction, cannot build closure
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10806.d b/gcc/testsuite/gdc.test/fail_compilation/fail10806.d
new file mode 100644
index 00000000000..48f35371ddc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10806.d
@@ -0,0 +1,18 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail10806.d(12): Error: function `fail10806.Class.clone` incompatible covariant types `First()` and `Second()`
+---
+*/
+
+interface First { First clone(); }
+interface Second { Second clone(); void call(); }
+
+class Class : First, Second {
+ override Class clone() { return this; }
+ override void call() { }
+}
+
+void main() {
+ (cast(Second) new Class).clone().call();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail109.d b/gcc/testsuite/gdc.test/fail_compilation/fail109.d
index 3e379c3fbf0..3419079f70e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail109.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail109.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail109.d(12): Error: enum member fail109.Bool.Unknown initialization with `Bool.True+1` causes overflow for type `bool`
+fail_compilation/fail109.d(12): Error: enum member `fail109.Bool.Unknown` initialization with `Bool.True+1` causes overflow for type `bool`
---
*/
@@ -12,11 +12,11 @@ enum Bool : bool
Unknown
}
-/* Bugzilla 11088
+/* https://issues.dlang.org/show_bug.cgi?id=11088
TEST_OUTPUT:
---
-fail_compilation/fail109.d(25): Error: enum member fail109.E.B initialization with `E.A+1` causes overflow for type `int`
-fail_compilation/fail109.d(31): Error: enum member fail109.E1.B initialization with `E1.A+1` causes overflow for type `short`
+fail_compilation/fail109.d(25): Error: enum member `fail109.E.B` initialization with `E.A+1` causes overflow for type `int`
+fail_compilation/fail109.d(31): Error: enum member `fail109.E1.B` initialization with `E1.A+1` causes overflow for type `short`
---
*/
enum E
@@ -31,11 +31,11 @@ enum E1 : short
B
}
-/* Bugzilla 14950
+/* https://issues.dlang.org/show_bug.cgi?id=14950
TEST_OUTPUT:
---
-fail_compilation/fail109.d(50): Deprecation: Comparison between different enumeration types `B` and `C`; If this behavior is intended consider using `std.conv.asOriginalType`
-fail_compilation/fail109.d(50): Error: enum member fail109.B.end initialization with `B.start+1` causes overflow for type `C`
+fail_compilation/fail109.d(50): Error: Comparison between different enumeration types `B` and `C`; If this behavior is intended consider using `std.conv.asOriginalType`
+fail_compilation/fail109.d(50): Error: enum member `fail109.B.end` initialization with `B.start+1` causes overflow for type `C`
---
*/
enum C
@@ -50,7 +50,7 @@ enum B
end
}
-/* Bugzilla 11849
+/* https://issues.dlang.org/show_bug.cgi?id=11849
TEST_OUTPUT:
---
fail_compilation/fail109.d(72): Error: enum member `fail109.RegValueType1a.Unknown` is forward referenced looking for `.max`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10905.d b/gcc/testsuite/gdc.test/fail_compilation/fail10905.d
index 6181d946bd5..4f243ff122b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10905.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10905.d
@@ -1,4 +1,5 @@
/*
+REQUIRED_ARGS: -m64
TEST_OUTPUT:
---
fail_compilation/fail10905.d(20): Error: incompatible types for `(this.x) == (cast(const(__vector(long[2])))cast(__vector(long[2]))1L)`: both operands are of type `const(__vector(long[2]))`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10947.d b/gcc/testsuite/gdc.test/fail_compilation/fail10947.d
index da847809a22..9b2de96ff2a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10947.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10947.d
@@ -1,15 +1,15 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10947.d(21): Error: cannot have immutable out parameter of type immutable(S)
-fail_compilation/fail10947.d(22): Error: cannot have immutable out parameter of type immutable(S)
-fail_compilation/fail10947.d(23): Error: cannot have immutable out parameter of type immutable(S)
-fail_compilation/fail10947.d(25): Error: cannot have const out parameter of type const(S)
-fail_compilation/fail10947.d(26): Error: cannot have const out parameter of type const(S)
-fail_compilation/fail10947.d(27): Error: cannot have const out parameter of type const(S)
-fail_compilation/fail10947.d(29): Error: cannot have inout out parameter of type inout(S)
-fail_compilation/fail10947.d(30): Error: cannot have inout out parameter of type inout(S)
-fail_compilation/fail10947.d(31): Error: cannot have inout out parameter of type inout(S)
+fail_compilation/fail10947.d(21): Error: cannot have `immutable out` parameter of type `immutable(S)`
+fail_compilation/fail10947.d(22): Error: cannot have `immutable out` parameter of type `immutable(S)`
+fail_compilation/fail10947.d(23): Error: cannot have `immutable out` parameter of type `immutable(S)`
+fail_compilation/fail10947.d(25): Error: cannot have `const out` parameter of type `const(S)`
+fail_compilation/fail10947.d(26): Error: cannot have `const out` parameter of type `const(S)`
+fail_compilation/fail10947.d(27): Error: cannot have `const out` parameter of type `const(S)`
+fail_compilation/fail10947.d(29): Error: cannot have `inout out` parameter of type `inout(S)`
+fail_compilation/fail10947.d(30): Error: cannot have `inout out` parameter of type `inout(S)`
+fail_compilation/fail10947.d(31): Error: cannot have `inout out` parameter of type `inout(S)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10964.d b/gcc/testsuite/gdc.test/fail_compilation/fail10964.d
index 7b7c826d354..de3673f7860 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10964.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10964.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10964.d(28): Error: function `fail10964.S.__postblit` is not nothrow
-fail_compilation/fail10964.d(29): Error: function `fail10964.S.__postblit` is not nothrow
-fail_compilation/fail10964.d(30): Error: function `fail10964.S.__postblit` is not nothrow
-fail_compilation/fail10964.d(33): Error: function `fail10964.S.__postblit` is not nothrow
-fail_compilation/fail10964.d(34): Error: function `fail10964.S.__postblit` is not nothrow
-fail_compilation/fail10964.d(35): Error: function `fail10964.S.__postblit` is not nothrow
-fail_compilation/fail10964.d(22): Error: nothrow function `fail10964.foo` may throw
+fail_compilation/fail10964.d(28): Error: function `fail10964.S.__postblit` is not `nothrow`
+fail_compilation/fail10964.d(29): Error: function `fail10964.S.__postblit` is not `nothrow`
+fail_compilation/fail10964.d(30): Error: function `fail10964.S.__postblit` is not `nothrow`
+fail_compilation/fail10964.d(33): Error: function `fail10964.S.__postblit` is not `nothrow`
+fail_compilation/fail10964.d(34): Error: function `fail10964.S.__postblit` is not `nothrow`
+fail_compilation/fail10964.d(35): Error: function `fail10964.S.__postblit` is not `nothrow`
+fail_compilation/fail10964.d(22): Error: `nothrow` function `fail10964.foo` may throw
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10968.d b/gcc/testsuite/gdc.test/fail_compilation/fail10968.d
index 257d739bbd3..06e0687ce39 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10968.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10968.d
@@ -1,18 +1,24 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10968.d(33): Error: pure function 'fail10968.bar' cannot call impure function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(33): Error: @safe function 'fail10968.bar' cannot call @system function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(34): Error: pure function 'fail10968.bar' cannot call impure function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(34): Error: @safe function 'fail10968.bar' cannot call @system function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(35): Error: pure function 'fail10968.bar' cannot call impure function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(35): Error: @safe function 'fail10968.bar' cannot call @system function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(38): Error: pure function 'fail10968.bar' cannot call impure function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(38): Error: @safe function 'fail10968.bar' cannot call @system function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(39): Error: pure function 'fail10968.bar' cannot call impure function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(39): Error: @safe function 'fail10968.bar' cannot call @system function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(40): Error: pure function 'fail10968.bar' cannot call impure function 'fail10968.SA.__postblit'
-fail_compilation/fail10968.d(40): Error: @safe function 'fail10968.bar' cannot call @system function 'fail10968.SA.__postblit'
+fail_compilation/fail10968.d(39): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(39): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here
+fail_compilation/fail10968.d(40): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(40): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here
+fail_compilation/fail10968.d(41): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(41): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here
+fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here
+fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(45): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here
+fail_compilation/fail10968.d(46): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(46): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit`
+fail_compilation/fail10968.d(27): `fail10968.SA.__postblit` is declared here
---
*/
@@ -43,12 +49,12 @@ void bar() pure @safe
/*
TEST_OUTPUT:
---
-fail_compilation/fail10968.d(66): Error: struct fail10968.SD is not copyable because it is annotated with `@disable`
-fail_compilation/fail10968.d(67): Error: struct fail10968.SD is not copyable because it is annotated with `@disable`
-fail_compilation/fail10968.d(68): Error: struct fail10968.SD is not copyable because it is annotated with `@disable`
-fail_compilation/fail10968.d(71): Error: struct fail10968.SD is not copyable because it is annotated with `@disable`
-fail_compilation/fail10968.d(72): Error: struct fail10968.SD is not copyable because it is annotated with `@disable`
-fail_compilation/fail10968.d(73): Error: struct fail10968.SD is not copyable because it is annotated with `@disable`
+fail_compilation/fail10968.d(72): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
+fail_compilation/fail10968.d(73): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
+fail_compilation/fail10968.d(74): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
+fail_compilation/fail10968.d(77): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
+fail_compilation/fail10968.d(78): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
+fail_compilation/fail10968.d(79): Error: struct `fail10968.SD` is not copyable because it has a disabled postblit
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10980.d b/gcc/testsuite/gdc.test/fail_compilation/fail10980.d
index eb50de34ebe..4869d27954f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail10980.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail10980.d
@@ -1,14 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail10980.d(22): Error: variable fail10980.s1b of type struct immutable(S1) uses this(this), which is not allowed in static initialization
-fail_compilation/fail10980.d(28): Error: variable fail10980.s1d of type struct immutable(S1) uses this(this), which is not allowed in static initialization
-fail_compilation/fail10980.d(27): Error: static variable s1x cannot be read at compile time
-fail_compilation/fail10980.d(28): called from here: bar1()
-fail_compilation/fail10980.d(38): Error: variable fail10980.s2b of type struct immutable(S2) uses this(this), which is not allowed in static initialization
-fail_compilation/fail10980.d(44): Error: variable fail10980.s2d of type struct immutable(S2) uses this(this), which is not allowed in static initialization
-fail_compilation/fail10980.d(43): Error: static variable s2x cannot be read at compile time
-fail_compilation/fail10980.d(44): called from here: bar2()
+fail_compilation/fail10980.d(22): Error: variable `fail10980.s1b` of type struct `immutable(S1)` uses `this(this)`, which is not allowed in static initialization
+fail_compilation/fail10980.d(28): Error: variable `fail10980.s1d` of type struct `immutable(S1)` uses `this(this)`, which is not allowed in static initialization
+fail_compilation/fail10980.d(27): Error: static variable `s1x` cannot be read at compile time
+fail_compilation/fail10980.d(28): called from here: `bar1()`
+fail_compilation/fail10980.d(38): Error: variable `fail10980.s2b` of type struct `immutable(S2)` uses `this(this)`, which is not allowed in static initialization
+fail_compilation/fail10980.d(44): Error: variable `fail10980.s2d` of type struct `immutable(S2)` uses `this(this)`, which is not allowed in static initialization
+fail_compilation/fail10980.d(43): Error: static variable `s2x` cannot be read at compile time
+fail_compilation/fail10980.d(44): called from here: `bar2()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11.d b/gcc/testsuite/gdc.test/fail_compilation/fail11.d
index 524e6154805..8ba05df2bf1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11.d(12): Error: `type` has no effect in expression `int*`
+fail_compilation/fail11.d(12): Error: `int*` has no effect
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail110.d b/gcc/testsuite/gdc.test/fail_compilation/fail110.d
index 47034019ea8..a13922b0fde 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail110.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail110.d
@@ -1,14 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail110.d(16): Error: variable i is shadowing variable fail110.main.i
-fail_compilation/fail110.d(17): Error: variable i is shadowing variable fail110.main.i
-fail_compilation/fail110.d(18): Error: variable i is shadowing variable fail110.main.i
+fail_compilation/fail110.d(16): Error: variable `i` is shadowing variable `fail110.main.i`
+fail_compilation/fail110.d(17): Error: variable `i` is shadowing variable `fail110.main.i`
+fail_compilation/fail110.d(18): Error: variable `i` is shadowing variable `fail110.main.i`
---
*/
-// Issue 297 - Shadowing declarations allowed in foreach type lists
-
+// https://issues.dlang.org/show_bug.cgi?id=297
+// Shadowing declarations allowed in foreach type lists
void main()
{
int i;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11038.d b/gcc/testsuite/gdc.test/fail_compilation/fail11038.d
index 8f39ccc51eb..331c3fc5892 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11038.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11038.d
@@ -2,16 +2,16 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11038.d(16): Error: `writeln` is not defined, perhaps `import std.stdio;` is needed?
+fail_compilation/fail11038.d(16): Error: `printf` is not defined, perhaps `import core.stdc.stdio;` is needed?
---
*/
static
{
- import std.stdio;
+ import core.stdc.stdio;
}
void main()
{
- writeln("foo"); // compiles
+ printf("foo"); // compiles
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail111.d b/gcc/testsuite/gdc.test/fail_compilation/fail111.d
index 3a5fed4b06f..b5d16691cb8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail111.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail111.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail111.d(12): Error: can't have array of int(int)
+fail_compilation/fail111.d(12): Error: cannot have array of `int(int)`
---
*/
-// Issue 289 - Compiler allows (and crashes on) dynamic arrays of typedefs of "immediate"-function types
-
+// https://issues.dlang.org/show_bug.cgi?id=289
+// Compiler allows (and crashes on) dynamic arrays of typedefs of "immediate"-function types
alias int ft(int);
ft[] x; // is allowed
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11125.d b/gcc/testsuite/gdc.test/fail_compilation/fail11125.d
index b42cb88d1eb..1a682cd197c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11125.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11125.d
@@ -1,8 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11125.d(20): Error: template instance fail11125.filter!(function (int a) => a + 1) does not match template declaration filter(alias predfun) if (is(ReturnType!predfun == bool))
-fail_compilation/fail11125.d(21): Error: template instance fail11125.filter!(function (int a) => a + 1) does not match template declaration filter(alias predfun) if (is(ReturnType!predfun == bool))
+fail_compilation/fail11125.d(26): Error: template instance `fail11125.filter!(function (int a) pure nothrow @nogc @safe => a + 1)` does not match template declaration `filter(alias predfun)`
+ with `predfun = __lambda1`
+ must satisfy the following constraint:
+` is(ReturnType!predfun == bool)`
+fail_compilation/fail11125.d(27): Error: template instance `fail11125.filter!(function (int a) pure nothrow @nogc @safe => a + 1)` does not match template declaration `filter(alias predfun)`
+ with `predfun = __lambda2`
+ must satisfy the following constraint:
+` is(ReturnType!predfun == bool)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11151.d b/gcc/testsuite/gdc.test/fail_compilation/fail11151.d
index 61843174c57..e5c262272a2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11151.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11151.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11151.d(30): Error: overlapping initialization for field a and y
+fail_compilation/fail11151.d(30): Error: overlapping initialization for field `a` and `y`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11163.d b/gcc/testsuite/gdc.test/fail_compilation/fail11163.d
index 3966c05e5e9..d8867403f1e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11163.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11163.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/fail11163.d(12): Error: cannot implicitly convert expression `foo()` of type `int[]` to `immutable(int[])`
-fail_compilation/fail11163.d(13): while evaluating pragma(msg, a)
+fail_compilation/fail11163.d(13): while evaluating `pragma(msg, a)`
---
*/
int[] foo() {
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail113.d b/gcc/testsuite/gdc.test/fail_compilation/fail113.d
index 8271b025d40..43ee2d842e2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail113.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail113.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail113.d(10): Error: forward reference to 'test'
+fail_compilation/fail113.d(10): Error: forward reference to `test`
---
*/
-// Issue 370 - Compiler stack overflow on recursive typeof in function declaration.
-
+// https://issues.dlang.org/show_bug.cgi?id=370
+// Compiler stack overflow on recursive typeof in function declaration.
void test(typeof(test) p) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11355.d b/gcc/testsuite/gdc.test/fail_compilation/fail11355.d
index c41a4c492e3..50897ad802a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11355.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11355.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11355.d(28): Error: struct fail11355.A is not copyable because it is annotated with `@disable`
+fail_compilation/fail11355.d(28): Error: struct `fail11355.A` is not copyable because it has a disabled postblit
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11375.d b/gcc/testsuite/gdc.test/fail_compilation/fail11375.d
index ba27a08641f..4f221bfd5e5 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11375.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11375.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11375.d(17): Error: constructor `fail11375.D!().D.this` is not nothrow
-fail_compilation/fail11375.d(15): Error: nothrow function `D main` may throw
+fail_compilation/fail11375.d(17): Error: constructor `fail11375.D!().D.this` is not `nothrow`
+fail_compilation/fail11375.d(15): Error: `nothrow` function `D main` may throw
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail114.d b/gcc/testsuite/gdc.test/fail_compilation/fail114.d
index 5bb9cec8a81..65eeef701a0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail114.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail114.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail114.d(12): Error: forward reference to 'funcA'
+fail_compilation/fail114.d(12): Error: forward reference to `funcA`
---
*/
-// Issue 371 - ICE on mutual recursive typeof in function declarations
-
+// https://issues.dlang.org/show_bug.cgi?id=371
+// ICE on mutual recursive typeof in function declarations
void funcA(typeof(&funcB) p) {}
void funcB(typeof(&funcA) p) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11445.d b/gcc/testsuite/gdc.test/fail_compilation/fail11445.d
index ed3f226e4f2..3295b243815 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11445.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11445.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11445.d(11): Error: incompatible types for ((a) + (b)): both operands are of type 'double[string]'
+fail_compilation/fail11445.d(11): Error: incompatible types for `(a) + (b)`: both operands are of type `double[string]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11453b.d b/gcc/testsuite/gdc.test/fail_compilation/fail11453b.d
index c7bdce5ef83..c9d7b965d03 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11453b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11453b.d
@@ -3,7 +3,7 @@
/*
TEST_OUTPUT
---
-fail_compilation/extra-files/foo11453.d(1): Error: module foo11453 from file fail_compilation/extra-files/foo11453.d conflicts with package name foo11453
+fail_compilation/extra-files/foo11453.d(1): Error: module `foo11453` from file fail_compilation/extra-files/foo11453.d conflicts with package name foo11453
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11503c.d b/gcc/testsuite/gdc.test/fail_compilation/fail11503c.d
index ad3963f4e15..88565d37134 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11503c.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11503c.d
@@ -7,7 +7,7 @@ fail_compilation/fail11503c.d(19): Error: cannot implicitly convert expression `
struct Data
{
char[256] buffer;
- @property const(char)[] filename() const pure nothrow
+ @property const(char)[] filename() const pure nothrow return
{
return buffer[];
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11503d.d b/gcc/testsuite/gdc.test/fail_compilation/fail11503d.d
index d1605b27822..30efa8d4258 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11503d.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11503d.d
@@ -10,7 +10,7 @@ struct Data2
char buffer;
}
-@property const(char)[] filename(const ref Data2 d) pure nothrow
+@property const(char)[] filename(const return ref Data2 d) pure nothrow
{
return (&d.buffer)[0 .. 1];
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11510.d b/gcc/testsuite/gdc.test/fail_compilation/fail11510.d
index 13913a0a46d..de08b5560d7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11510.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11510.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11510.d(25): Error: reinterpretation through overlapped field y is not allowed in CTFE
-fail_compilation/fail11510.d(29): called from here: test11510a()
-fail_compilation/fail11510.d(36): Error: reinterpretation through overlapped field y is not allowed in CTFE
-fail_compilation/fail11510.d(40): called from here: test11510b()
+fail_compilation/fail11510.d(25): Error: reinterpretation through overlapped field `y` is not allowed in CTFE
+fail_compilation/fail11510.d(29): called from here: `test11510a()`
+fail_compilation/fail11510.d(36): Error: reinterpretation through overlapped field `y` is not allowed in CTFE
+fail_compilation/fail11510.d(40): called from here: `test11510b()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11532.d b/gcc/testsuite/gdc.test/fail_compilation/fail11532.d
index 7f4c770527e..144dec76b00 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11532.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11532.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11532.d(17): Error: cannot pass static arrays to extern(C) vararg functions
-fail_compilation/fail11532.d(18): Error: cannot pass dynamic arrays to extern(C) vararg functions
-fail_compilation/fail11532.d(19): Error: cannot pass static arrays to extern(C++) vararg functions
-fail_compilation/fail11532.d(20): Error: cannot pass dynamic arrays to extern(C++) vararg functions
+fail_compilation/fail11532.d(17): Error: cannot pass static arrays to `extern(C)` vararg functions
+fail_compilation/fail11532.d(18): Error: cannot pass dynamic arrays to `extern(C)` vararg functions
+fail_compilation/fail11532.d(19): Error: cannot pass static arrays to `extern(C++)` vararg functions
+fail_compilation/fail11532.d(20): Error: cannot pass dynamic arrays to `extern(C++)` vararg functions
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11542.d b/gcc/testsuite/gdc.test/fail_compilation/fail11542.d
index 22d29ac5570..0198f64a871 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11542.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11542.d
@@ -1,13 +1,12 @@
// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail11542.d(16): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail11542.d(13): Error: nothrow function `fail11542.test_success1` may throw
-fail_compilation/fail11542.d(26): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail11542.d(23): Error: nothrow function `fail11542.test_success3` may throw
+fail_compilation/fail11542.d(15): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail11542.d(12): Error: `nothrow` function `fail11542.test_success1` may throw
+fail_compilation/fail11542.d(25): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail11542.d(22): Error: `nothrow` function `fail11542.test_success3` may throw
---
*/
void test_success1() nothrow
@@ -29,8 +28,8 @@ void test_success3() nothrow
/*
TEST_OUTPUT:
---
-fail_compilation/fail11542.d(39): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail11542.d(36): Error: nothrow function `fail11542.test_failure1` may throw
+fail_compilation/fail11542.d(38): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail11542.d(35): Error: `nothrow` function `fail11542.test_failure1` may throw
---
*/
void test_failure1() nothrow
@@ -52,8 +51,8 @@ void est_failure3() nothrow
/*
TEST_OUTPUT:
---
-fail_compilation/fail11542.d(62): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail11542.d(59): Error: nothrow function `fail11542.test_exit1` may throw
+fail_compilation/fail11542.d(61): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail11542.d(58): Error: `nothrow` function `fail11542.test_exit1` may throw
---
*/
void test_exit1() nothrow
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11545.d b/gcc/testsuite/gdc.test/fail_compilation/fail11545.d
index 514cb87854a..a576817238f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11545.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11545.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11545.d(14): Error: need 'this' for 'x' of type 'int'
-fail_compilation/fail11545.d(18): Error: need 'this' for 'x' of type 'int'
+fail_compilation/fail11545.d(14): Error: need `this` for `x` of type `int`
+fail_compilation/fail11545.d(18): Error: need `this` for `x` of type `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11552.d b/gcc/testsuite/gdc.test/fail_compilation/fail11552.d
index 51165227fbd..252022598af 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11552.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11552.d
@@ -1,9 +1,8 @@
/*
REQUIRED_ARGS: -o-
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/fail11552.d(12): Error: label `label` is undefined
+fail_compilation/fail11552.d(11): Error: function `D main` label `label` is undefined
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11562.d b/gcc/testsuite/gdc.test/fail_compilation/fail11562.d
index 0377456a5b3..dcae1f51566 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11562.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11562.d
@@ -1,12 +1,11 @@
/*
REQUIRED_ARGS: -o-
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/fail11562.d(16): Error: cannot goto in or out of `finally` block
-fail_compilation/fail11562.d(37): Error: cannot goto in or out of `finally` block
-fail_compilation/fail11562.d(49): Error: cannot goto in or out of `finally` block
-fail_compilation/fail11562.d(64): Error: cannot goto in or out of `finally` block
+fail_compilation/fail11562.d(15): Error: cannot `goto` in or out of `finally` block
+fail_compilation/fail11562.d(36): Error: cannot `goto` in or out of `finally` block
+fail_compilation/fail11562.d(48): Error: cannot `goto` in or out of `finally` block
+fail_compilation/fail11562.d(63): Error: cannot `goto` in or out of `finally` block
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11591b.d b/gcc/testsuite/gdc.test/fail_compilation/fail11591b.d
index 42e89c96459..ef4fe16a2c8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11591b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11591b.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11591b.d(16): Error: AA key type S11591 does not have 'bool opEquals(ref const S11591) const'
+fail_compilation/fail11591b.d(16): Error: AA key type `S11591` does not have `bool opEquals(ref const S11591) const`
---
*/
@@ -19,8 +19,8 @@ void test11591()
/*
TEST_OUTPUT:
---
-fail_compilation/fail11591b.d(30): Error: AA key type S12307a does not have 'bool opEquals(ref const S12307a) const'
-fail_compilation/fail11591b.d(31): Error: AA key type S12307b does not have 'bool opEquals(ref const S12307b) const'
+fail_compilation/fail11591b.d(30): Error: AA key type `S12307a` does not have `bool opEquals(ref const S12307a) const`
+fail_compilation/fail11591b.d(31): Error: AA key type `S12307b` does not have `bool opEquals(ref const S12307b) const`
---
*/
struct S12307a { bool opEquals(T : typeof(this))(T) { return false; } }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail116.d b/gcc/testsuite/gdc.test/fail_compilation/fail116.d
index 87e451bcf09..66a01c68ac0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail116.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail116.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail116.d(11): Error: circular typeof definition
-fail_compilation/fail116.d(16): Error: template instance square!1.2 does not match template declaration square(_error_ x)
+fail_compilation/fail116.d(11): Error: circular `typeof` definition
+fail_compilation/fail116.d(16): Error: template instance `square!1.2` does not match template declaration `square(_error_ x)`
---
*/
-// Issue 405 - typeof in TemplateParameterList causes compiletime segmentfault
-
+// https://issues.dlang.org/show_bug.cgi?id=405
+// typeof in TemplateParameterList causes compiletime segmentfault
template square(typeof(x) x)
{
const square = x * x;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail117.d b/gcc/testsuite/gdc.test/fail_compilation/fail117.d
index 9279d54276c..b0e1b120c9c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail117.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail117.d
@@ -8,8 +8,8 @@ fail_compilation/fail117.d(38): Error: expression `foo.mixin MGettor!(b) getb;
---
*/
-// Issue 420 - mixin make dmd break
-
+// https://issues.dlang.org/show_bug.cgi?id=420
+// mixin make dmd break
//import std.stdio;
template MGettor(alias Fld)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11714.d b/gcc/testsuite/gdc.test/fail_compilation/fail11714.d
new file mode 100644
index 00000000000..abc47087ffa
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11714.d
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail11714.d(14): Error: variable `fail11714.c` is a thread-local class and cannot have a static initializer. Use `static this()` to initialize instead.
+fail_compilation/fail11714.d(21): Error: variable `fail11714.s` is a thread-local pointer to struct and cannot have a static initializer. Use `static this()` to initialize instead.
+---
+*/
+
+class C11714
+{
+ int data;
+};
+
+C11714 c = new C11714; // mutable class reference.
+
+struct S11714
+{
+ int data;
+};
+
+S11714* s = new S11714; // mutable pointer to struct.
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11717.d b/gcc/testsuite/gdc.test/fail_compilation/fail11717.d
deleted file mode 100644
index c6d505ca02d..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11717.d
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail11717.d(13): Error: cannot interpret array literal expression [1, 2, 3, 4] + [1, 2, 3, 4] at compile time
----
-*/
-
-// https://issues.dlang.org/show_bug.cgi?id=11717
-
-enum int[4] A = [1,2,3,4];
-enum int[4] B = [1,2,3,4];
-// Internal Compiler Error: non-constant value [1, 2, 3, 4]
-enum int[4] C = A[] + B[];
-
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11720.d b/gcc/testsuite/gdc.test/fail_compilation/fail11720.d
deleted file mode 100644
index 8ad1d862d6b..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11720.d
+++ /dev/null
@@ -1,33 +0,0 @@
-// REQUIRED_ARGS: -o-
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail11720.d(23): Error: declaration fail11720.foo!().foo.temp is already defined in another scope in foo
-fail_compilation/fail11720.d(13): Error: template instance fail11720.foo!() error instantiating
-fail_compilation/fail11720.d(31): Error: declaration fail11720.bar.temp is already defined in another scope in bar
----
-*/
-
-void main()
-{
- foo();
- bar();
-}
-
-alias TypeTuple(T...) = T;
-
-void foo()()
-{
- foreach (T; TypeTuple!(int, double))
- {
- static temp = T.stringof;
- }
-}
-
-void bar()
-{
- foreach (T; TypeTuple!(int, double))
- {
- static temp = T.stringof;
- }
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11748.d b/gcc/testsuite/gdc.test/fail_compilation/fail11748.d
index 95b78fa0340..7b6a6c8a7d1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail11748.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail11748.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail11748.d(12): Error: expression my_function(0) is void and has no value
+fail_compilation/fail11748.d(12): Error: expression `my_function(0)` is `void` and has no value
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail118.d b/gcc/testsuite/gdc.test/fail_compilation/fail118.d
index e17b954e5d8..263570abd65 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail118.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail118.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail118.d(26): Error: invalid foreach aggregate `Iter`, define opApply(), range primitives, or use .tupleof
-fail_compilation/fail118.d(27): Error: invalid foreach aggregate `Iter`, define opApply(), range primitives, or use .tupleof
+fail_compilation/fail118.d(26): Error: invalid `foreach` aggregate `Iter`, define `opApply()`, range primitives, or use `.tupleof`
+fail_compilation/fail118.d(27): Error: invalid `foreach` aggregate `Iter`, define `opApply()`, range primitives, or use `.tupleof`
---
*/
-// Issue 441 - Crash on foreach of mixed-in aggregate.
-
+// https://issues.dlang.org/show_bug.cgi?id=441
+// Crash on foreach of mixed-in aggregate.
template opHackedApply()
{
struct Iter
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail120.d b/gcc/testsuite/gdc.test/fail_compilation/fail120.d
index ae0f5b1093b..c9d67a491d8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail120.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail120.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail120.d(12): Error: need 'this' for 'nodes' of type 'int[2]'
-fail_compilation/fail120.d(13): Error: need 'this' for 'nodes' of type 'int[2]'
+fail_compilation/fail120.d(12): Error: need `this` for `nodes` of type `int[2]`
+fail_compilation/fail120.d(13): Error: need `this` for `nodes` of type `int[2]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail122.d b/gcc/testsuite/gdc.test/fail_compilation/fail122.d
index f4ba30198c4..da355d50ed3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail122.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail122.d
@@ -5,8 +5,8 @@ fail_compilation/fail122.d(12): Error: undefined identifier `y`
---
*/
-// Issue 228 - Crash on inferring function literal return type after prior errors
-
+// https://issues.dlang.org/show_bug.cgi?id=228
+// Crash on inferring function literal return type after prior errors
void main()
{
y = 2;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12236.d b/gcc/testsuite/gdc.test/fail_compilation/fail12236.d
index ea950667976..738864cca35 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12236.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12236.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12236.d(16): Error: forward reference to inferred return type of function 'f1'
-fail_compilation/fail12236.d(16): while evaluating pragma(msg, f1.mangleof)
-fail_compilation/fail12236.d(21): Error: forward reference to inferred return type of function 'f2'
-fail_compilation/fail12236.d(21): while evaluating pragma(msg, f2(T)(T).mangleof)
-fail_compilation/fail12236.d(27): Error: template instance fail12236.f2!int error instantiating
-fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function '__lambda1'
-fail_compilation/fail12236.d(31): while evaluating pragma(msg, __lambda1.mangleof)
+fail_compilation/fail12236.d(16): Error: forward reference to inferred return type of function `f1`
+fail_compilation/fail12236.d(16): while evaluating `pragma(msg, f1.mangleof)`
+fail_compilation/fail12236.d(21): Error: forward reference to inferred return type of function `f2`
+fail_compilation/fail12236.d(21): while evaluating `pragma(msg, f2(T)(T).mangleof)`
+fail_compilation/fail12236.d(27): Error: template instance `fail12236.f2!int` error instantiating
+fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function `__lambda1`
+fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda1.mangleof)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12255.d b/gcc/testsuite/gdc.test/fail_compilation/fail12255.d
index 4531e86e8c0..945212bf8dc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12255.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12255.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12255.d(29): Error: AA key type SC1 does not have 'bool opEquals(ref const SC1) const'
-fail_compilation/fail12255.d(30): Error: AA key type SC2 does not support const equality
-fail_compilation/fail12255.d(35): Error: AA key type SD1 should have 'size_t toHash() const nothrow @safe' if opEquals defined
-fail_compilation/fail12255.d(36): Error: AA key type SD2 supports const equality but doesn't support const hashing
-fail_compilation/fail12255.d(40): Error: AA key type SE1 should have 'size_t toHash() const nothrow @safe' if opEquals defined
-fail_compilation/fail12255.d(41): Error: AA key type SE2 supports const equality but doesn't support const hashing
+fail_compilation/fail12255.d(29): Error: AA key type `SC1` does not have `bool opEquals(ref const SC1) const`
+fail_compilation/fail12255.d(30): Error: AA key type `SC2` does not support const equality
+fail_compilation/fail12255.d(35): Error: AA key type `SD1` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined
+fail_compilation/fail12255.d(36): Error: AA key type `SD2` supports const equality but doesn't support const hashing
+fail_compilation/fail12255.d(40): Error: AA key type `SE1` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined
+fail_compilation/fail12255.d(41): Error: AA key type `SE2` supports const equality but doesn't support const hashing
---
*/
@@ -91,12 +91,12 @@ struct SE2
/*
TEST_OUTPUT:
---
-fail_compilation/fail12255.d(108): Error: bottom of AA key type SC1 does not have 'bool opEquals(ref const SC1) const'
-fail_compilation/fail12255.d(109): Error: bottom of AA key type SC2 does not support const equality
-fail_compilation/fail12255.d(110): Error: bottom of AA key type SD1 should have 'size_t toHash() const nothrow @safe' if opEquals defined
-fail_compilation/fail12255.d(111): Error: bottom of AA key type SD2 supports const equality but doesn't support const hashing
-fail_compilation/fail12255.d(112): Error: bottom of AA key type SE1 should have 'size_t toHash() const nothrow @safe' if opEquals defined
-fail_compilation/fail12255.d(113): Error: bottom of AA key type SE2 supports const equality but doesn't support const hashing
+fail_compilation/fail12255.d(108): Error: bottom of AA key type `SC1` does not have `bool opEquals(ref const SC1) const`
+fail_compilation/fail12255.d(109): Error: bottom of AA key type `SC2` does not support const equality
+fail_compilation/fail12255.d(110): Error: bottom of AA key type `SD1` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined
+fail_compilation/fail12255.d(111): Error: bottom of AA key type `SD2` supports const equality but doesn't support const hashing
+fail_compilation/fail12255.d(112): Error: bottom of AA key type `SE1` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined
+fail_compilation/fail12255.d(113): Error: bottom of AA key type `SE2` supports const equality but doesn't support const hashing
---
*/
void testSArray()
@@ -116,12 +116,12 @@ void testSArray()
/*
TEST_OUTPUT:
---
-fail_compilation/fail12255.d(133): Error: bottom of AA key type SC1 does not have 'bool opEquals(ref const SC1) const'
-fail_compilation/fail12255.d(134): Error: bottom of AA key type SC2 does not support const equality
-fail_compilation/fail12255.d(135): Error: bottom of AA key type SD1 should have 'size_t toHash() const nothrow @safe' if opEquals defined
-fail_compilation/fail12255.d(136): Error: bottom of AA key type SD2 supports const equality but doesn't support const hashing
-fail_compilation/fail12255.d(137): Error: bottom of AA key type SE1 should have 'size_t toHash() const nothrow @safe' if opEquals defined
-fail_compilation/fail12255.d(138): Error: bottom of AA key type SE2 supports const equality but doesn't support const hashing
+fail_compilation/fail12255.d(133): Error: bottom of AA key type `SC1` does not have `bool opEquals(ref const SC1) const`
+fail_compilation/fail12255.d(134): Error: bottom of AA key type `SC2` does not support const equality
+fail_compilation/fail12255.d(135): Error: bottom of AA key type `SD1` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined
+fail_compilation/fail12255.d(136): Error: bottom of AA key type `SD2` supports const equality but doesn't support const hashing
+fail_compilation/fail12255.d(137): Error: bottom of AA key type `SE1` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined
+fail_compilation/fail12255.d(138): Error: bottom of AA key type `SE2` supports const equality but doesn't support const hashing
---
*/
void testDArray()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail123.d b/gcc/testsuite/gdc.test/fail_compilation/fail123.d
index fd1aef0e87c..1566b91bf2b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail123.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail123.d
@@ -2,12 +2,12 @@
TEST_OUTPUT:
---
fail_compilation/fail123.d(11): Error: undefined identifier `type`
-fail_compilation/fail123.d(17): Error: enum fail123.foo2 base type must not be void
+fail_compilation/fail123.d(17): Error: enum `fail123.foo2` base type must not be `void`
---
*/
-// Issue 355 - ICE from enum : nonexistent type
-
+// https://issues.dlang.org/show_bug.cgi?id=355
+// ICE from enum : nonexistent type
enum foo : type
{
blah1,
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12378.d b/gcc/testsuite/gdc.test/fail_compilation/fail12378.d
index 6f787305a71..77678ebc0fc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12378.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12378.d
@@ -3,11 +3,11 @@ TEST_OUTPUT:
---
fail_compilation/fail12378.d(18): Error: undefined identifier `ANYTHING`
fail_compilation/fail12378.d(18): Error: undefined identifier `GOES`
-fail_compilation/fail12378.d(91): instantiated from here: MapResultS!((x0) => ANYTHING - GOES, Result)
-fail_compilation/fail12378.d(17): instantiated from here: mapS!(Result)
-fail_compilation/fail12378.d(100): instantiated from here: __lambda1!int
-fail_compilation/fail12378.d(91): instantiated from here: MapResultS!((y0) => iota(2).mapS!((x0) => ANYTHING - GOES), Result)
-fail_compilation/fail12378.d(16): instantiated from here: mapS!(Result)
+fail_compilation/fail12378.d(91): instantiated from here: `MapResultS!((x0) => ANYTHING - GOES, Result)`
+fail_compilation/fail12378.d(17): instantiated from here: `mapS!(Result)`
+fail_compilation/fail12378.d(100): instantiated from here: `__lambda1!int`
+fail_compilation/fail12378.d(91): instantiated from here: `MapResultS!((y0) => iota(2).mapS!((x0) => ANYTHING - GOES), Result)`
+fail_compilation/fail12378.d(16): instantiated from here: `mapS!(Result)`
---
*/
void testS()
@@ -25,11 +25,11 @@ TEST_OUTPUT:
---
fail_compilation/fail12378.d(40): Error: undefined identifier `ANYTHING`
fail_compilation/fail12378.d(40): Error: undefined identifier `GOES`
-fail_compilation/fail12378.d(112): instantiated from here: MapResultC!((x0) => ANYTHING - GOES, Result)
-fail_compilation/fail12378.d(39): instantiated from here: mapC!(Result)
-fail_compilation/fail12378.d(123): instantiated from here: __lambda1!int
-fail_compilation/fail12378.d(112): instantiated from here: MapResultC!((y0) => iota(2).mapC!((x0) => ANYTHING - GOES), Result)
-fail_compilation/fail12378.d(38): instantiated from here: mapC!(Result)
+fail_compilation/fail12378.d(112): instantiated from here: `MapResultC!((x0) => ANYTHING - GOES, Result)`
+fail_compilation/fail12378.d(39): instantiated from here: `mapC!(Result)`
+fail_compilation/fail12378.d(123): instantiated from here: `__lambda1!int`
+fail_compilation/fail12378.d(112): instantiated from here: `MapResultC!((y0) => iota(2).mapC!((x0) => ANYTHING - GOES), Result)`
+fail_compilation/fail12378.d(38): instantiated from here: `mapC!(Result)`
---
*/
void testC()
@@ -47,11 +47,11 @@ TEST_OUTPUT:
---
fail_compilation/fail12378.d(64): Error: undefined identifier `ANYTHING`
fail_compilation/fail12378.d(64): Error: undefined identifier `GOES`
-fail_compilation/fail12378.d(135): instantiated from here: MapResultI!((x0) => ANYTHING - GOES, Result)
-fail_compilation/fail12378.d(63): instantiated from here: mapI!(Result)
-fail_compilation/fail12378.d(143): instantiated from here: __lambda1!int
-fail_compilation/fail12378.d(135): instantiated from here: MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result)
-fail_compilation/fail12378.d(62): instantiated from here: mapI!(Result)
+fail_compilation/fail12378.d(135): instantiated from here: `MapResultI!((x0) => ANYTHING - GOES, Result)`
+fail_compilation/fail12378.d(63): instantiated from here: `mapI!(Result)`
+fail_compilation/fail12378.d(143): instantiated from here: `__lambda1!int`
+fail_compilation/fail12378.d(135): instantiated from here: `MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result)`
+fail_compilation/fail12378.d(62): instantiated from here: `mapI!(Result)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12390.d b/gcc/testsuite/gdc.test/fail_compilation/fail12390.d
index cb1eb8a6bf1..dd28163adb7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12390.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12390.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12390.d(14): Error: `==` has no effect in expression `fun().i == 4`
+fail_compilation/fail12390.d(14): Error: `fun().i == 4` has no effect
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail124.d b/gcc/testsuite/gdc.test/fail_compilation/fail124.d
index 62b5894f68e..7eeb29f672a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail124.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail124.d
@@ -1,7 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail124.d(15): Error: class fail124.CC inherits from duplicate interface C
+fail_compilation/fail124.d(17): Error: class `fail124.CC` inherits from duplicate interface `C`
+fail_compilation/fail124.d(31): Error: class `fail124.D` inherits from duplicate interface `T`
+fail_compilation/fail124.d(31): Error: class `fail124.D` inherits from duplicate interface `T`
---
*/
@@ -22,3 +24,8 @@ void main()
CC cc = new CC();
cc.f();
}
+
+// https://issues.dlang.org/show_bug.cgi?id=20830
+interface T { }
+
+class D : T, T, T { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12436.d b/gcc/testsuite/gdc.test/fail_compilation/fail12436.d
index 605ab054e9e..5bdf0d5a891 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12436.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12436.d
@@ -21,8 +21,8 @@ TupleType test2();
/*
TEST_OUTPUT:
---
-fail_compilation/fail12436.d(28): Error: functions cannot return opaque type Opaque by value
-fail_compilation/fail12436.d(29): Error: functions cannot return opaque type Opaque[1] by value
+fail_compilation/fail12436.d(28): Error: functions cannot return opaque type `Opaque` by value
+fail_compilation/fail12436.d(29): Error: functions cannot return opaque type `Opaque[1]` by value
---
*/
Opaque ret12436a(); // error
@@ -40,7 +40,7 @@ ref Opaque[1] ret12436g(); // no error
/*
TEST_OUTPUT:
---
-fail_compilation/fail12436.d(46): Error: cannot have parameter of function type void()
+fail_compilation/fail12436.d(46): Error: cannot have parameter of function type `void()`
---
*/
void test3(FuncType) {}
@@ -48,8 +48,8 @@ void test3(FuncType) {}
/*
TEST_OUTPUT:
---
-fail_compilation/fail12436.d(55): Error: cannot have parameter of opaque type Opaque by value
-fail_compilation/fail12436.d(56): Error: cannot have parameter of opaque type Opaque[1] by value
+fail_compilation/fail12436.d(55): Error: cannot have parameter of opaque type `Opaque` by value
+fail_compilation/fail12436.d(56): Error: cannot have parameter of opaque type `Opaque[1]` by value
---
*/
void param12436a(Opaque); // error
@@ -66,9 +66,9 @@ void param12436i(out Opaque[1]); // no error
/*
TEST_OUTPUT:
---
-fail_compilation/fail12436.d(75): Error: cannot have parameter of opaque type A14906 by value
-fail_compilation/fail12436.d(76): Error: cannot have parameter of opaque type A14906[3] by value
-fail_compilation/fail12436.d(77): Error: cannot have parameter of opaque type A14906[3][3] by value
+fail_compilation/fail12436.d(75): Error: cannot have parameter of opaque type `A14906` by value
+fail_compilation/fail12436.d(76): Error: cannot have parameter of opaque type `A14906[3]` by value
+fail_compilation/fail12436.d(77): Error: cannot have parameter of opaque type `A14906[3][3]` by value
---
*/
enum A14906;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail125.d b/gcc/testsuite/gdc.test/fail_compilation/fail125.d
index 0e0f5ea8a7e..93d176dd4e2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail125.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail125.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail125.d(15): Error: array index [2] is outside array bounds [0 .. 2]
-fail_compilation/fail125.d(18): Error: template instance fail125.main.recMove!(1, a, b) error instantiating
-fail_compilation/fail125.d(25): instantiated from here: recMove!(0, a, b)
+fail_compilation/fail125.d(15): Error: array index `[2]` is outside array bounds `[0 .. 2]`
+fail_compilation/fail125.d(18): Error: template instance `fail125.main.recMove!(1, a, b)` error instantiating
+fail_compilation/fail125.d(25): instantiated from here: `recMove!(0, a, b)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail126.d b/gcc/testsuite/gdc.test/fail_compilation/fail126.d
index 280fde90aaa..b851c507a99 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail126.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail126.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail126.d(8): Error: forward reference to 'test'
+fail_compilation/fail126.d(8): Error: forward reference to `test`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12622.d b/gcc/testsuite/gdc.test/fail_compilation/fail12622.d
index 04bb8d6c020..1a8b18511c1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12622.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12622.d
@@ -1,15 +1,16 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12622.d(25): Error: pure function 'fail12622.foo' cannot call impure function pointer 'fp'
-fail_compilation/fail12622.d(25): Error: @nogc function 'fail12622.foo' cannot call non-@nogc function pointer 'fp'
-fail_compilation/fail12622.d(25): Error: @safe function 'fail12622.foo' cannot call @system function pointer 'fp'
-fail_compilation/fail12622.d(27): Error: pure function 'fail12622.foo' cannot call impure function pointer 'fp'
-fail_compilation/fail12622.d(27): Error: @nogc function 'fail12622.foo' cannot call non-@nogc function pointer 'fp'
-fail_compilation/fail12622.d(27): Error: @safe function 'fail12622.foo' cannot call @system function pointer 'fp'
-fail_compilation/fail12622.d(29): Error: pure function 'fail12622.foo' cannot call impure function 'fail12622.bar'
-fail_compilation/fail12622.d(29): Error: @safe function 'fail12622.foo' cannot call @system function 'fail12622.bar'
-fail_compilation/fail12622.d(29): Error: @nogc function 'fail12622.foo' cannot call non-@nogc function 'fail12622.bar'
+fail_compilation/fail12622.d(26): Error: `pure` function `fail12622.foo` cannot call impure function pointer `fp`
+fail_compilation/fail12622.d(26): Error: `@nogc` function `fail12622.foo` cannot call non-@nogc function pointer `fp`
+fail_compilation/fail12622.d(26): Error: `@safe` function `fail12622.foo` cannot call `@system` function pointer `fp`
+fail_compilation/fail12622.d(28): Error: `pure` function `fail12622.foo` cannot call impure function pointer `fp`
+fail_compilation/fail12622.d(28): Error: `@nogc` function `fail12622.foo` cannot call non-@nogc function pointer `fp`
+fail_compilation/fail12622.d(28): Error: `@safe` function `fail12622.foo` cannot call `@system` function pointer `fp`
+fail_compilation/fail12622.d(30): Error: `pure` function `fail12622.foo` cannot call impure function `fail12622.bar`
+fail_compilation/fail12622.d(30): Error: `@safe` function `fail12622.foo` cannot call `@system` function `fail12622.bar`
+fail_compilation/fail12622.d(20): `fail12622.bar` is declared here
+fail_compilation/fail12622.d(30): Error: `@nogc` function `fail12622.foo` cannot call non-@nogc function `fail12622.bar`
---
*/
// Note that, today nothrow violation errors are accidentally hidden.
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12636.d b/gcc/testsuite/gdc.test/fail_compilation/fail12636.d
index 9a243a5c716..42e78554716 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12636.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12636.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12636.d(13): Error: C++ class 'fail12636.C' cannot implement D interface 'fail12636.D'
+fail_compilation/fail12636.d(13): Error: C++ class `fail12636.C` cannot implement D interface `fail12636.D`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail127.d b/gcc/testsuite/gdc.test/fail_compilation/fail127.d
index 449dbc5de68..ad9ddd09978 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail127.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail127.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail127.d(9): Error: a struct is not a valid initializer for a char[][]
-fail_compilation/fail127.d(10): Error: a struct is not a valid initializer for a string[]
+fail_compilation/fail127.d(9): Error: a struct is not a valid initializer for a `char[][]`
+fail_compilation/fail127.d(10): Error: a struct is not a valid initializer for a `string[]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12744.d b/gcc/testsuite/gdc.test/fail_compilation/fail12744.d
index f1216b58d7a..46ed9f6db6c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12744.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12744.d
@@ -1,24 +1,24 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12744.d(38): Error: incompatible parameter storage classes 'ref' and 'out'
-fail_compilation/fail12744.d(52): Error: template instance fail12744.bar12744R!(foo12744O) error instantiating
-fail_compilation/fail12744.d(38): Error: incompatible parameter storage classes 'ref' and 'lazy'
-fail_compilation/fail12744.d(53): Error: template instance fail12744.bar12744R!(foo12744L) error instantiating
-fail_compilation/fail12744.d(39): Error: incompatible parameter storage classes 'out' and 'ref'
-fail_compilation/fail12744.d(56): Error: template instance fail12744.bar12744O!(foo12744R) error instantiating
-fail_compilation/fail12744.d(39): Error: incompatible parameter storage classes 'out' and 'lazy'
-fail_compilation/fail12744.d(58): Error: template instance fail12744.bar12744O!(foo12744L) error instantiating
-fail_compilation/fail12744.d(40): Error: incompatible parameter storage classes 'lazy' and 'ref'
-fail_compilation/fail12744.d(61): Error: template instance fail12744.bar12744L!(foo12744R) error instantiating
-fail_compilation/fail12744.d(40): Error: incompatible parameter storage classes 'lazy' and 'out'
-fail_compilation/fail12744.d(62): Error: template instance fail12744.bar12744L!(foo12744O) error instantiating
-fail_compilation/fail12744.d(41): Error: incompatible parameter storage classes 'auto ref' and 'out'
-fail_compilation/fail12744.d(67): Error: template fail12744.bar12744A cannot deduce function from argument types !(foo12744O)(int), candidates are:
-fail_compilation/fail12744.d(41): fail12744.bar12744A(alias f)(auto ref PTT12744!f args)
-fail_compilation/fail12744.d(41): Error: incompatible parameter storage classes 'auto ref' and 'lazy'
-fail_compilation/fail12744.d(68): Error: template fail12744.bar12744A cannot deduce function from argument types !(foo12744L)(int), candidates are:
-fail_compilation/fail12744.d(41): fail12744.bar12744A(alias f)(auto ref PTT12744!f args)
+fail_compilation/fail12744.d(38): Error: incompatible parameter storage classes `ref` and `out`
+fail_compilation/fail12744.d(52): Error: template instance `fail12744.bar12744R!(foo12744O)` error instantiating
+fail_compilation/fail12744.d(38): Error: incompatible parameter storage classes `ref` and `lazy`
+fail_compilation/fail12744.d(53): Error: template instance `fail12744.bar12744R!(foo12744L)` error instantiating
+fail_compilation/fail12744.d(39): Error: incompatible parameter storage classes `out` and `ref`
+fail_compilation/fail12744.d(56): Error: template instance `fail12744.bar12744O!(foo12744R)` error instantiating
+fail_compilation/fail12744.d(39): Error: incompatible parameter storage classes `out` and `lazy`
+fail_compilation/fail12744.d(58): Error: template instance `fail12744.bar12744O!(foo12744L)` error instantiating
+fail_compilation/fail12744.d(40): Error: incompatible parameter storage classes `lazy` and `ref`
+fail_compilation/fail12744.d(61): Error: template instance `fail12744.bar12744L!(foo12744R)` error instantiating
+fail_compilation/fail12744.d(40): Error: incompatible parameter storage classes `lazy` and `out`
+fail_compilation/fail12744.d(62): Error: template instance `fail12744.bar12744L!(foo12744O)` error instantiating
+fail_compilation/fail12744.d(41): Error: incompatible parameter storage classes `auto ref` and `out`
+fail_compilation/fail12744.d(67): Error: template `fail12744.bar12744A` cannot deduce function from argument types `!(foo12744O)(int)`
+fail_compilation/fail12744.d(41): Candidate is: `bar12744A(alias f)(auto ref PTT12744!f args)`
+fail_compilation/fail12744.d(41): Error: incompatible parameter storage classes `auto ref` and `lazy`
+fail_compilation/fail12744.d(68): Error: template `fail12744.bar12744A` cannot deduce function from argument types `!(foo12744L)(int)`
+fail_compilation/fail12744.d(41): Candidate is: `bar12744A(alias f)(auto ref PTT12744!f args)`
---
*/
template PTT12744(func...)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12749.d b/gcc/testsuite/gdc.test/fail_compilation/fail12749.d
index 149d1209829..231b21e8091 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12749.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12749.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12749.d(19): Error: immutable field 'inum' initialization is not allowed in foreach loop
-fail_compilation/fail12749.d(20): Error: const field 'cnum' initialization is not allowed in foreach loop
-fail_compilation/fail12749.d(25): Error: immutable field 'inum' initialization is not allowed in nested function 'set'
-fail_compilation/fail12749.d(26): Error: const field 'cnum' initialization is not allowed in nested function 'set'
+fail_compilation/fail12749.d(19): Error: immutable field `inum` initialization is not allowed in foreach loop
+fail_compilation/fail12749.d(20): Error: const field `cnum` initialization is not allowed in foreach loop
+fail_compilation/fail12749.d(25): Error: immutable field `inum` initialization is not allowed in nested function `set`
+fail_compilation/fail12749.d(26): Error: const field `cnum` initialization is not allowed in nested function `set`
---
*/
struct S
@@ -31,10 +31,10 @@ struct S
/*
TEST_OUTPUT:
---
-fail_compilation/fail12749.d(48): Error: immutable variable 'inum' initialization is not allowed in foreach loop
-fail_compilation/fail12749.d(49): Error: const variable 'cnum' initialization is not allowed in foreach loop
-fail_compilation/fail12749.d(54): Error: immutable variable 'inum' initialization is not allowed in nested function 'set'
-fail_compilation/fail12749.d(55): Error: const variable 'cnum' initialization is not allowed in nested function 'set'
+fail_compilation/fail12749.d(48): Error: immutable variable `inum` initialization is not allowed in foreach loop
+fail_compilation/fail12749.d(49): Error: const variable `cnum` initialization is not allowed in foreach loop
+fail_compilation/fail12749.d(54): Error: immutable variable `inum` initialization is not allowed in nested function `set`
+fail_compilation/fail12749.d(55): Error: const variable `cnum` initialization is not allowed in nested function `set`
---
*/
immutable int inum;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12764.d b/gcc/testsuite/gdc.test/fail_compilation/fail12764.d
new file mode 100644
index 00000000000..f889cb72d37
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12764.d
@@ -0,0 +1,26 @@
+// https://issues.dlang.org/show_bug.cgi?id=12764
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail12764.d(20): Error: field `s` must be initialized in constructor
+---
+*/
+
+struct S
+{
+ @disable this();
+
+ this(string) { }
+ int f;
+}
+
+class C
+{
+ this(int)
+ {
+ s.f = 1; // circumvents default ctor!
+ }
+
+ S s;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12809.d b/gcc/testsuite/gdc.test/fail_compilation/fail12809.d
index 7c086839b0c..a5e9f721580 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12809.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12809.d
@@ -1,16 +1,15 @@
// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
bool cond;
/*
TEST_OUTPUT:
---
-fail_compilation/fail12809.d(19): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail12809.d(16): Error: nothrow function `fail12809.test_finally1` may throw
-fail_compilation/fail12809.d(35): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail12809.d(39): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail12809.d(32): Error: nothrow function `fail12809.test_finally3` may throw
+fail_compilation/fail12809.d(18): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail12809.d(15): Error: `nothrow` function `fail12809.test_finally1` may throw
+fail_compilation/fail12809.d(34): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail12809.d(38): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail12809.d(31): Error: `nothrow` function `fail12809.test_finally3` may throw
---
*/
void test_finally1() nothrow
@@ -44,11 +43,11 @@ void test_finally3() nothrow
/*
TEST_OUTPUT:
---
-fail_compilation/fail12809.d(59): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail12809.d(54): Error: nothrow function `fail12809.test_finally4` may throw
-fail_compilation/fail12809.d(75): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail12809.d(79): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail12809.d(70): Error: nothrow function `fail12809.test_finally6` may throw
+fail_compilation/fail12809.d(58): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail12809.d(53): Error: `nothrow` function `fail12809.test_finally4` may throw
+fail_compilation/fail12809.d(74): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail12809.d(78): Error: `object.Exception` is thrown but not caught
+fail_compilation/fail12809.d(69): Error: `nothrow` function `fail12809.test_finally6` may throw
---
*/
void test_finally4() nothrow
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail129.d b/gcc/testsuite/gdc.test/fail_compilation/fail129.d
index 2bf436964e7..dc0ca072b54 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail129.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail129.d
@@ -3,7 +3,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail129.d: Error: module fail129 source file must start with BOM or ASCII character, not \xC3
+fail_compilation/fail129.d: Error: module `fail129` source file must start with BOM or ASCII character, not \xC3
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12901.d b/gcc/testsuite/gdc.test/fail_compilation/fail12901.d
index 8d62c3e04ca..31c90c1df61 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12901.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12901.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12901.d(11): Error: constructor fail12901.S.this in and out contracts require function body
+fail_compilation/fail12901.d(11): Error: constructor `fail12901.S.this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12908.d b/gcc/testsuite/gdc.test/fail_compilation/fail12908.d
index c238028309e..67ea6cefb22 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12908.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12908.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12908.d(14): Error: pure delegate 'fail12908.main.__foreachbody1' cannot call impure function 'fail12908.g'
+fail_compilation/fail12908.d(14): Error: `pure` delegate `fail12908.main.__foreachbody1` cannot call impure function `fail12908.g`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12932.d b/gcc/testsuite/gdc.test/fail_compilation/fail12932.d
index 871abfea02c..fe68fea01ca 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail12932.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail12932.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail12932.d(11): Error: array literal in @nogc function 'fail12932.foo' may cause GC allocation
-fail_compilation/fail12932.d(15): Error: array literal in @nogc function 'fail12932.foo' may cause GC allocation
+fail_compilation/fail12932.d(11): Error: array literal in `@nogc` function `fail12932.foo` may cause a GC allocation
+fail_compilation/fail12932.d(15): Error: array literal in `@nogc` function `fail12932.foo` may cause a GC allocation
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13064.d b/gcc/testsuite/gdc.test/fail_compilation/fail13064.d
index be434600510..a59de72e9dd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13064.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13064.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail13064.d(8): Error: function fail13064.f storage class 'auto' has no effect if return type is not inferred
+fail_compilation/fail13064.d(8): Error: function `fail13064.f` storage class `auto` has no effect if return type is not inferred
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail131.d b/gcc/testsuite/gdc.test/fail_compilation/fail131.d
index e0f568fcf64..15b6dc992ab 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail131.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail131.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail131.d(8): Error: function D main parameters must be main() or main(string[] args)
+fail_compilation/fail131.d(8): Error: function `D main` parameters must be `main()` or `main(string[] args)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13116.d b/gcc/testsuite/gdc.test/fail_compilation/fail13116.d
index fe1180caff2..ac520d79997 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13116.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13116.d
@@ -1,12 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail13116.d(13): Error: this is not an lvalue
+fail_compilation/fail13116.d(14): Error: `this` is not an lvalue and cannot be modified
+fail_compilation/fail13116.d(23): Error: `super` is not an lvalue and cannot be modified
---
*/
struct S
{
- ref S notEvil() { return this; } // this should be accepted
+ ref S notEvil() return { return this; } // this should be accepted
}
class C
{
@@ -16,12 +17,6 @@ void main()
{
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail13116.d(28): Error: super is not an lvalue
----
-*/
class Base { }
class Derived : Base
{
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13120.d b/gcc/testsuite/gdc.test/fail_compilation/fail13120.d
index 1acda7b2db1..f1cf340b6a0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13120.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13120.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail13120.d(13): Error: pure delegate 'fail13120.g1.__foreachbody2' cannot call impure function 'fail13120.f1'
-fail_compilation/fail13120.d(13): Error: @nogc delegate 'fail13120.g1.__foreachbody2' cannot call non-@nogc function 'fail13120.f1'
+fail_compilation/fail13120.d(13): Error: `pure` delegate `fail13120.g1.__foreachbody2` cannot call impure function `fail13120.f1`
+fail_compilation/fail13120.d(13): Error: `@nogc` delegate `fail13120.g1.__foreachbody2` cannot call non-@nogc function `fail13120.f1`
---
*/
void f1() {}
@@ -16,9 +16,10 @@ void g1(char[] s) pure @nogc
/*
TEST_OUTPUT:
---
-fail_compilation/fail13120.d(34): Error: pure function 'fail13120.h2' cannot call impure function 'fail13120.g2!().g2'
-fail_compilation/fail13120.d(34): Error: @safe function 'fail13120.h2' cannot call @system function 'fail13120.g2!().g2'
-fail_compilation/fail13120.d(34): Error: @nogc function 'fail13120.h2' cannot call non-@nogc function 'fail13120.g2!().g2'
+fail_compilation/fail13120.d(35): Error: `pure` function `fail13120.h2` cannot call impure function `fail13120.g2!().g2`
+fail_compilation/fail13120.d(35): Error: `@safe` function `fail13120.h2` cannot call `@system` function `fail13120.g2!().g2`
+fail_compilation/fail13120.d(27): `fail13120.g2!().g2` is declared here
+fail_compilation/fail13120.d(35): Error: `@nogc` function `fail13120.h2` cannot call non-@nogc function `fail13120.g2!().g2`
---
*/
void f2() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13187.d b/gcc/testsuite/gdc.test/fail_compilation/fail13187.d
index 3799d7711e4..2972c279262 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13187.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13187.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail13187.d(12): Error: pure function 'fail13187.test' cannot access mutable static data 'my_func_ptr'
+fail_compilation/fail13187.d(12): Error: `pure` function `fail13187.test` cannot access mutable static data `my_func_ptr`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail132.d b/gcc/testsuite/gdc.test/fail_compilation/fail132.d
index 34631840b23..2c271d95ae6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail132.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail132.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail132.d(19): Error: outer class A 'this' needed to 'new' nested class B
+fail_compilation/fail132.d(19): Error: outer class `A` `this` needed to `new` nested class `B`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13203.d b/gcc/testsuite/gdc.test/fail_compilation/fail13203.d
index 86d30a4b093..e24234954c6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13203.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13203.d
@@ -3,10 +3,10 @@ int v1, v2;
/*
TEST_OUTPUT:
---
-fail_compilation/fail13203.d(15): Error: alias fail13203.FA1!1.T conflicts with alias fail13203.FA1!1.T at fail_compilation/fail13203.d(14)
-fail_compilation/fail13203.d(22): Error: template instance fail13203.FA1!1 error instantiating
-fail_compilation/fail13203.d(20): Error: alias fail13203.FA2!1.T conflicts with alias fail13203.FA2!1.T at fail_compilation/fail13203.d(19)
-fail_compilation/fail13203.d(23): Error: template instance fail13203.FA2!1 error instantiating
+fail_compilation/fail13203.d(15): Error: alias `fail13203.FA1!1.T` conflicts with alias `fail13203.FA1!1.T` at fail_compilation/fail13203.d(14)
+fail_compilation/fail13203.d(22): Error: template instance `fail13203.FA1!1` error instantiating
+fail_compilation/fail13203.d(20): Error: alias `fail13203.FA2!1.T` conflicts with alias `fail13203.FA2!1.T` at fail_compilation/fail13203.d(19)
+fail_compilation/fail13203.d(23): Error: template instance `fail13203.FA2!1` error instantiating
---
*/
template FA1(int b)
@@ -25,10 +25,10 @@ alias A2 = FA2!1; // variable symbol is not overloadable
/*
TEST_OUTPUT:
---
-fail_compilation/fail13203.d(36): Error: alias fail13203.FB1!1.T conflicts with alias fail13203.FB1!1.T at fail_compilation/fail13203.d(37)
-fail_compilation/fail13203.d(44): Error: template instance fail13203.FB1!1 error instantiating
-fail_compilation/fail13203.d(41): Error: alias fail13203.FB2!1.T conflicts with alias fail13203.FB2!1.T at fail_compilation/fail13203.d(42)
-fail_compilation/fail13203.d(45): Error: template instance fail13203.FB2!1 error instantiating
+fail_compilation/fail13203.d(36): Error: alias `fail13203.FB1!1.T` conflicts with alias `fail13203.FB1!1.T` at fail_compilation/fail13203.d(37)
+fail_compilation/fail13203.d(44): Error: template instance `fail13203.FB1!1` error instantiating
+fail_compilation/fail13203.d(41): Error: alias `fail13203.FB2!1.T` conflicts with alias `fail13203.FB2!1.T` at fail_compilation/fail13203.d(42)
+fail_compilation/fail13203.d(45): Error: template instance `fail13203.FB2!1` error instantiating
---
*/
template FB1(int b)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail133.d b/gcc/testsuite/gdc.test/fail_compilation/fail133.d
index e189ad5a8c0..29b7d40975c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail133.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail133.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail133.d(13): Error: function D main circular dependency. Functions cannot be interpreted while being compiled
-fail_compilation/fail133.d(15): called from here: main()
+fail_compilation/fail133.d(13): Error: function `D main` circular dependency. Functions cannot be interpreted while being compiled
+fail_compilation/fail133.d(15): called from here: `main()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13336a.d b/gcc/testsuite/gdc.test/fail_compilation/fail13336a.d
index 5ab7e6c56f1..e3f990c2fdf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13336a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13336a.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail13336a.d(28): Error: choose(true) is not an lvalue
+fail_compilation/fail13336a.d(28): Error: `choose(true)` is not an lvalue and cannot be modified
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13336b.d b/gcc/testsuite/gdc.test/fail_compilation/fail13336b.d
index 9d30d2c9f30..b8fb12d1427 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13336b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13336b.d
@@ -1,5 +1,4 @@
// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
int sx;
double sy;
@@ -7,7 +6,8 @@ double sy;
/*
TEST_OUTPUT:
---
-fail_compilation/fail13336b.d(16): Error: cast(double)sx is not an lvalue
+fail_compilation/fail13336b.d(16): Error: `cast(double)sx` is not an lvalue and cannot be modified
+fail_compilation/fail13336b.d(24): Error: `cast(double)sx` is not an lvalue and cannot be modified
---
*/
ref f1(bool f)
@@ -17,12 +17,6 @@ ref f1(bool f)
return sy;
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail13336b.d(30): Error: cast(double)sx is not an lvalue
----
-*/
ref f2(bool f)
{
if (f)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail134.d b/gcc/testsuite/gdc.test/fail_compilation/fail134.d
index d7b4a36ffc8..d8ae89cdf8e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail134.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail134.d
@@ -1,13 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail134.d(13): Error: template instance foo!(f) does not match template declaration foo(T)
-fail_compilation/fail134.d(14): Error: template instance fail134.bar!(f) error instantiating
+fail_compilation/fail134.d(14): Error: template instance `foo!(f)` does not match template declaration `foo(T)`
+fail_compilation/fail134.d(14): `f` is not a type
+fail_compilation/fail134.d(15): Error: template instance `fail134.bar!(f)` error instantiating
---
*/
-// Issue 651 - Assertion failure: 'global.errors' on line 2622 in file 'template.c'
-
+// https://issues.dlang.org/show_bug.cgi?id=651
+// Assertion failure: 'global.errors' on line 2622 in file 'template.c'
void f() {}
template foo(T) {}
template bar(T...) { alias foo!(T) buz; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13424.d b/gcc/testsuite/gdc.test/fail_compilation/fail13424.d
index 31bed563ded..dcec523a013 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13424.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13424.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail13424.d(12): Error: delegate fail13424.S.__lambda2 cannot be struct members
-fail_compilation/fail13424.d(17): Error: delegate fail13424.U.__lambda2 cannot be union members
-fail_compilation/fail13424.d(22): Error: delegate fail13424.C.__lambda2 cannot be class members
+fail_compilation/fail13424.d(12): Error: delegate `fail13424.S.__lambda2` cannot be struct members
+fail_compilation/fail13424.d(17): Error: delegate `fail13424.U.__lambda2` cannot be union members
+fail_compilation/fail13424.d(22): Error: delegate `fail13424.C.__lambda2` cannot be class members
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13435.d b/gcc/testsuite/gdc.test/fail_compilation/fail13435.d
new file mode 100644
index 00000000000..a3ef5e4e465
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13435.d
@@ -0,0 +1,27 @@
+// https://issues.dlang.org/show_bug.cgi?id=13435
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail13435.d(22): Error: cannot implicitly convert expression `d` of type `int[]` to `S!int`
+fail_compilation/fail13435.d(22): `this._a = d` is the first assignment of `this._a` therefore it represents its initialization
+fail_compilation/fail13435.d(22): `opAssign` methods are not used for initialization, but for subsequent assignments
+---
+*/
+
+struct S(T)
+{
+ void opAssign(T[] arg) {}
+}
+
+class B
+{
+ this(int[] d)
+ {
+ S!int c;
+ _a = d; // Error: cannot implicitly convert expression (d) of type int[] to S!int
+ c = d; // compiles OK
+ }
+
+ S!int _a;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13498.d b/gcc/testsuite/gdc.test/fail_compilation/fail13498.d
index c18d2e31c69..27f47b3f7ab 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13498.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13498.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/fail13498.d(11): Error: cannot implicitly convert expression `"foo"` of type `string` to `int`
-fail_compilation/fail13498.d(16): Error: template instance fail13498.foo!() error instantiating
+fail_compilation/fail13498.d(16): Error: template instance `fail13498.foo!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13574.d b/gcc/testsuite/gdc.test/fail_compilation/fail13574.d
index 081f8b57b6b..2a9336bc182 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13574.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13574.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail13574.d(21): Error: '$' is not an lvalue
-fail_compilation/fail13574.d(27): Error: '$' is not an lvalue
+fail_compilation/fail13574.d(21): Error: cannot modify operator `$`
+fail_compilation/fail13574.d(27): Error: cannot modify operator `$`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail136.d b/gcc/testsuite/gdc.test/fail_compilation/fail136.d
index bba0c511e8c..299b994d919 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail136.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail136.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail136.d(10): Error: `string` has no effect in expression `"\xef\xbb\xbf"`
+fail_compilation/fail136.d(10): Error: Built-in hex string literals are obsolete, use `std.conv.hexString!"EF BB BF"` instead.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13601.d b/gcc/testsuite/gdc.test/fail_compilation/fail13601.d
index e1aa2d5db99..51291f4dc1b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13601.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13601.d
@@ -4,7 +4,7 @@ TEST_OUTPUT:
fail_compilation/fail13601.d(13): Error: variable `__ctfe` cannot be read at compile time
fail_compilation/fail13601.d(14): Error: variable `__ctfe` cannot be read at compile time
fail_compilation/fail13601.d(15): Error: variable `__ctfe` cannot be read at compile time
-fail_compilation/fail13601.d(16): Error: variable __ctfe cannot be read at compile time
+fail_compilation/fail13601.d(16): Error: variable `__ctfe` cannot be read at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13701.d b/gcc/testsuite/gdc.test/fail_compilation/fail13701.d
index c0bdfccb3ea..88ed2d99a22 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13701.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13701.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail13701.d(16): Error: cannot modify immutable expression this.aa[10]
-fail_compilation/fail13701.d(23): Error: cannot modify immutable expression aa[10]
-fail_compilation/fail13701.d(24): Error: cannot modify immutable expression aa[10]
+fail_compilation/fail13701.d(16): Error: cannot modify `immutable` expression `this.aa[10]`
+fail_compilation/fail13701.d(23): Error: cannot modify `immutable` expression `aa[10]`
+fail_compilation/fail13701.d(24): Error: cannot modify `immutable` expression `aa[10]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13756.d b/gcc/testsuite/gdc.test/fail_compilation/fail13756.d
index 38dfeb87825..cdf0e854d54 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13756.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13756.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail13756.d(11): Error: foreach: index must be type `const(int)`, not `int`
+fail_compilation/fail13756.d(11): Error: `foreach`: index must be type `const(int)`, not `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail139.d b/gcc/testsuite/gdc.test/fail_compilation/fail139.d
index 22fc616c87c..d5cdb99aa33 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail139.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail139.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail139.d(8): Error: forward reference to 'test'
+fail_compilation/fail139.d(8): Error: forward reference to `test`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13902.d b/gcc/testsuite/gdc.test/fail_compilation/fail13902.d
index fbe4b4a6713..5e3b6374f20 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail13902.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail13902.d
@@ -4,9 +4,11 @@ struct S1 { int v; }
struct S2 { int* p; }
class C { int v; }
+#line 6
/*
TEST_OUTPUT:
---
+fail_compilation/fail13902.d(45): Error: Using the result of a comma expression is not allowed
fail_compilation/fail13902.d(32): Error: returning `& x` escapes a reference to local variable `x`
fail_compilation/fail13902.d(33): Error: returning `&s1.v` escapes a reference to local variable `s1`
fail_compilation/fail13902.d(38): Error: returning `& sa1` escapes a reference to local variable `sa1`
@@ -47,17 +49,19 @@ int* testEscape1()
return null; // ok
}
+#line 49
/*
TEST_OUTPUT:
---
-fail_compilation/fail13902.d(75): Error: returning `& x` escapes a reference to parameter `x`, perhaps annotate with `return`
-fail_compilation/fail13902.d(76): Error: returning `&s1.v` escapes a reference to parameter `s1`, perhaps annotate with `return`
-fail_compilation/fail13902.d(81): Error: returning `& sa1` escapes a reference to parameter `sa1`, perhaps annotate with `return`
-fail_compilation/fail13902.d(82): Error: returning `&sa2[0][0]` escapes a reference to parameter `sa2`, perhaps annotate with `return`
-fail_compilation/fail13902.d(83): Error: returning `& x` escapes a reference to parameter `x`, perhaps annotate with `return`
-fail_compilation/fail13902.d(84): Error: returning `(& x+4)` escapes a reference to parameter `x`, perhaps annotate with `return`
-fail_compilation/fail13902.d(85): Error: returning `& x + cast(long)x * 4L` escapes a reference to parameter `x`, perhaps annotate with `return`
-fail_compilation/fail13902.d(88): Error: returning `& y` escapes a reference to parameter `y`, perhaps annotate with `return`
+fail_compilation/fail13902.d(88): Error: Using the result of a comma expression is not allowed
+fail_compilation/fail13902.d(75): Error: returning `& x` escapes a reference to local variable `x`
+fail_compilation/fail13902.d(76): Error: returning `&s1.v` escapes a reference to local variable `s1`
+fail_compilation/fail13902.d(81): Error: returning `& sa1` escapes a reference to local variable `sa1`
+fail_compilation/fail13902.d(82): Error: returning `&sa2[0][0]` escapes a reference to local variable `sa2`
+fail_compilation/fail13902.d(83): Error: returning `& x` escapes a reference to local variable `x`
+fail_compilation/fail13902.d(84): Error: returning `(& x+4)` escapes a reference to local variable `x`
+fail_compilation/fail13902.d(85): Error: returning `& x + cast(long)x * 4L` escapes a reference to local variable `x`
+fail_compilation/fail13902.d(88): Error: returning `& y` escapes a reference to local variable `y`
---
*/
int* testEscape2(
@@ -90,19 +94,21 @@ int* testEscape2(
return null; // ok
}
+#line 92
/*
TEST_OUTPUT:
---
+fail_compilation/fail13902.d(123): Error: Using the result of a comma expression is not allowed
---
*/
int* testEscape3(
- ref int x, ref int y,
+ return ref int x, return ref int y,
ref int[] da1,
ref int[][] da2,
- ref int[1] sa1,
- ref int[1][1] sa2,
+ return ref int[1] sa1,
+ return ref int[1][1] sa2,
ref int* ptr,
- ref S1 s1,
+ return ref S1 s1,
ref S2 s2,
ref C c,
)
@@ -128,9 +134,9 @@ int* testEscape3(
/*
TEST_OUTPUT:
---
-fail_compilation/fail13902.d(150): Error: returning `cast(int[])sa1` escapes a reference to parameter `sa1`, perhaps annotate with `return`
-fail_compilation/fail13902.d(151): Error: returning `cast(int[])sa1` escapes a reference to parameter `sa1`, perhaps annotate with `return`
-fail_compilation/fail13902.d(152): Error: returning `sa1[]` escapes a reference to parameter `sa1`, perhaps annotate with `return`
+fail_compilation/fail13902.d(150): Error: returning `cast(int[])sa1` escapes a reference to local variable `sa1`
+fail_compilation/fail13902.d(151): Error: returning `cast(int[])sa1` escapes a reference to local variable `sa1`
+fail_compilation/fail13902.d(152): Error: returning `sa1[]` escapes a reference to local variable `sa1`
fail_compilation/fail13902.d(155): Error: returning `cast(int[])sa2` escapes a reference to local variable `sa2`
fail_compilation/fail13902.d(156): Error: returning `cast(int[])sa2` escapes a reference to local variable `sa2`
fail_compilation/fail13902.d(157): Error: returning `sa2[]` escapes a reference to local variable `sa2`
@@ -145,7 +151,7 @@ fail_compilation/fail13902.d(172): Error: escaping reference to stack allocated
fail_compilation/fail13902.d(173): Error: escaping reference to stack allocated value returned by `makeS()`
---
*/
-int[] testEscape4(int[3] sa1) // Bugzilla 9279
+int[] testEscape4(int[3] sa1) // https://issues.dlang.org/show_bug.cgi?id=9279
{
if (0) return sa1; // error <- no error
if (0) return cast(int[])sa1; // error <- no error
@@ -217,14 +223,14 @@ ref int testEscapeRef1()
/*
TEST_OUTPUT:
---
-fail_compilation/fail13902.d(240): Error: returning `x` escapes a reference to parameter `x`, perhaps annotate with `return`
-fail_compilation/fail13902.d(241): Error: returning `s1.v` escapes a reference to parameter `s1`, perhaps annotate with `return`
-fail_compilation/fail13902.d(245): Error: returning `sa1[0]` escapes a reference to parameter `sa1`, perhaps annotate with `return`
-fail_compilation/fail13902.d(246): Error: returning `sa2[0][0]` escapes a reference to parameter `sa2`, perhaps annotate with `return`
-fail_compilation/fail13902.d(247): Error: returning `x = 1` escapes a reference to parameter `x`, perhaps annotate with `return`
-fail_compilation/fail13902.d(248): Error: returning `x += 1` escapes a reference to parameter `x`, perhaps annotate with `return`
-fail_compilation/fail13902.d(249): Error: returning `s1.v = 1` escapes a reference to parameter `s1`, perhaps annotate with `return`
-fail_compilation/fail13902.d(250): Error: returning `s1.v += 1` escapes a reference to parameter `s1`, perhaps annotate with `return`
+fail_compilation/fail13902.d(240): Error: returning `x` escapes a reference to local variable `x`
+fail_compilation/fail13902.d(241): Error: returning `s1.v` escapes a reference to local variable `s1`
+fail_compilation/fail13902.d(245): Error: returning `sa1[0]` escapes a reference to local variable `sa1`
+fail_compilation/fail13902.d(246): Error: returning `sa2[0][0]` escapes a reference to local variable `sa2`
+fail_compilation/fail13902.d(247): Error: returning `x = 1` escapes a reference to local variable `x`
+fail_compilation/fail13902.d(248): Error: returning `x += 1` escapes a reference to local variable `x`
+fail_compilation/fail13902.d(249): Error: returning `s1.v = 1` escapes a reference to local variable `s1`
+fail_compilation/fail13902.d(250): Error: returning `s1.v += 1` escapes a reference to local variable `s1`
---
*/
ref int testEscapeRef2(
@@ -259,12 +265,12 @@ TEST_OUTPUT:
---
*/
ref int testEscapeRef2(
- ref int x,
+ return ref int x,
ref int[] da1,
ref int[][] da2,
- ref int[1] sa1,
- ref int[1][1] sa2,
- ref S1 s1,
+ return ref int[1] sa1,
+ return ref int[1][1] sa2,
+ return ref S1 s1,
ref C c,
)
{
@@ -317,18 +323,18 @@ int[] testSlice2() { int[3] sa; int n; return sa[n..2][1..2]; }
/*
TEST_OUTPUT:
---
-fail_compilation/fail13902.d(324): Error: returning `vda[0]` escapes a reference to parameter `vda`, perhaps annotate with `return`
-fail_compilation/fail13902.d(325): Error: returning `vda[]` escapes a reference to variadic parameter `vda`
+fail_compilation/fail13902.d(324): Error: returning `vda[0]` escapes a reference to parameter `vda`
+fail_compilation/fail13902.d(324): perhaps annotate the parameter with `return`
---
*/
ref int testDynamicArrayVariadic1(int[] vda...) { return vda[0]; }
-int[] testDynamicArrayVariadic2(int[] vda...) { return vda[]; }
+@safe int[] testDynamicArrayVariadic2(int[] vda...) { return vda[]; }
int[3] testDynamicArrayVariadic3(int[] vda...) { return vda[0..3]; } // no error
/*
TEST_OUTPUT:
---
-fail_compilation/fail13902.d(335): Error: returning `vsa[0]` escapes a reference to parameter `vsa`, perhaps annotate with `return`
+fail_compilation/fail13902.d(335): Error: returning `vsa[0]` escapes a reference to local variable `vsa`
fail_compilation/fail13902.d(336): Error: returning `vsa[]` escapes a reference to variadic parameter `vsa`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14089.d b/gcc/testsuite/gdc.test/fail_compilation/fail14089.d
index df1221a2cb4..f60910432de 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14089.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14089.d
@@ -1,16 +1,16 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail14089.d(41): Error: `long` has no effect in expression `1`
-fail_compilation/fail14089.d(41): Error: `long` has no effect in expression `1`
-fail_compilation/fail14089.d(42): Error: `long` has no effect in expression `1`
-fail_compilation/fail14089.d(42): Error: `var` has no effect in expression `n`
-fail_compilation/fail14089.d(43): Error: `long` has no effect in expression `1`
-fail_compilation/fail14089.d(43): Error: `dotvar` has no effect in expression `s.val`
-fail_compilation/fail14089.d(44): Error: `var` has no effect in expression `n`
-fail_compilation/fail14089.d(44): Error: `long` has no effect in expression `1`
-fail_compilation/fail14089.d(45): Error: `dotvar` has no effect in expression `s.val`
-fail_compilation/fail14089.d(45): Error: `long` has no effect in expression `1`
+fail_compilation/fail14089.d(41): Error: `1` has no effect
+fail_compilation/fail14089.d(41): Error: `1` has no effect
+fail_compilation/fail14089.d(42): Error: `1` has no effect
+fail_compilation/fail14089.d(42): Error: `n` has no effect
+fail_compilation/fail14089.d(43): Error: `1` has no effect
+fail_compilation/fail14089.d(43): Error: `s.val` has no effect
+fail_compilation/fail14089.d(44): Error: `n` has no effect
+fail_compilation/fail14089.d(44): Error: `1` has no effect
+fail_compilation/fail14089.d(45): Error: `s.val` has no effect
+fail_compilation/fail14089.d(45): Error: `1` has no effect
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail142.d b/gcc/testsuite/gdc.test/fail_compilation/fail142.d
index e89b576adb4..343b2e3c731 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail142.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail142.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail142.d(20): Error: cannot create instance of abstract class B
-fail_compilation/fail142.d(20): function 'void test()' is not implemented
+fail_compilation/fail142.d(20): Error: cannot create instance of abstract class `B`
+fail_compilation/fail142.d(20): function `void test()` is not implemented
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14249.d b/gcc/testsuite/gdc.test/fail_compilation/fail14249.d
index c895c5504f6..8d66a75c5c1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14249.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14249.d
@@ -2,20 +2,18 @@
REQUIRED_ARGS: -unittest
TEST_OUTPUT:
---
-fail_compilation/fail14249.d(23): Error: shared static constructor can only be member of module/aggregate/template, not function main
-fail_compilation/fail14249.d(24): Error: shared static destructor can only be member of module/aggregate/template, not function main
-fail_compilation/fail14249.d(25): Error: static constructor can only be member of module/aggregate/template, not function main
-fail_compilation/fail14249.d(26): Error: static destructor can only be member of module/aggregate/template, not function main
-fail_compilation/fail14249.d(27): Error: unittest can only be a member of module/aggregate/template, not function main
-fail_compilation/fail14249.d(28): Error: invariant can only be a member of aggregate, not function main
-fail_compilation/fail14249.d(29): Error: alias this can only be a member of aggregate, not function `main`
-fail_compilation/fail14249.d(30): Error: allocator can only be a member of aggregate, not function main
-fail_compilation/fail14249.d(31): Error: deallocator can only be a member of aggregate, not function main
-fail_compilation/fail14249.d(32): Error: constructor can only be a member of aggregate, not function main
-fail_compilation/fail14249.d(33): Error: destructor can only be a member of aggregate, not function main
-fail_compilation/fail14249.d(34): Error: postblit can only be a member of struct/union, not function main
-fail_compilation/fail14249.d(35): Error: anonymous union can only be a part of an aggregate, not function `main`
-fail_compilation/fail14249.d(39): Error: mixin fail14249.main.Mix!() error instantiating
+fail_compilation/fail14249.d(21): Error: `shared static` constructor can only be member of module/aggregate/template, not function `main`
+fail_compilation/fail14249.d(22): Error: `shared static` destructor can only be member of module/aggregate/template, not function `main`
+fail_compilation/fail14249.d(23): Error: `static` constructor can only be member of module/aggregate/template, not function `main`
+fail_compilation/fail14249.d(24): Error: `static` destructor can only be member of module/aggregate/template, not function `main`
+fail_compilation/fail14249.d(25): Error: `unittest` can only be a member of module/aggregate/template, not function `main`
+fail_compilation/fail14249.d(26): Error: `invariant` can only be a member of aggregate, not function `main`
+fail_compilation/fail14249.d(27): Error: alias this can only be a member of aggregate, not function `main`
+fail_compilation/fail14249.d(28): Error: constructor can only be a member of aggregate, not function `main`
+fail_compilation/fail14249.d(29): Error: destructor can only be a member of aggregate, not function `main`
+fail_compilation/fail14249.d(30): Error: postblit can only be a member of struct, not function `main`
+fail_compilation/fail14249.d(31): Error: anonymous union can only be a part of an aggregate, not function `main`
+fail_compilation/fail14249.d(35): Error: mixin `fail14249.main.Mix!()` error instantiating
---
*/
mixin template Mix()
@@ -27,8 +25,6 @@ mixin template Mix()
unittest {}
invariant {}
alias a this;
- new(size_t sz) { return null; }
- delete(void* p) { }
this() {} // from fail268.d
~this() {} // from fail268.d
this(this) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail143.d b/gcc/testsuite/gdc.test/fail_compilation/fail143.d
index 0a0986ac996..6df232f68a1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail143.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail143.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail143.d(23): Error: need 'this' for 'next' of type 'uint()'
-fail_compilation/fail143.d(30): Error: template instance fail143.Foo!int error instantiating
+fail_compilation/fail143.d(23): Error: need `this` for `next` of type `uint()`
+fail_compilation/fail143.d(30): Error: template instance `fail143.Foo!int` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14304.d b/gcc/testsuite/gdc.test/fail_compilation/fail14304.d
index 472b33d1543..8321401bcaf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14304.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14304.d
@@ -1,16 +1,16 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail14304.d(26): Error: cannot modify read-only constant S14304(1)
-fail_compilation/fail14304.d(58): called from here: sle14304.modify()
-fail_compilation/fail14304.d(35): Error: cannot modify read-only constant [1:1, 2:2]
-fail_compilation/fail14304.d(61): called from here: modify14304(aae14304)
-fail_compilation/fail14304.d(41): Error: cannot modify read-only constant [1, 2, 3]
-fail_compilation/fail14304.d(64): called from here: modify14304(cast(const(int)[])index14304)
-fail_compilation/fail14304.d(47): Error: cannot modify read-only constant [1.414, 1.732, 2.00000]
-fail_compilation/fail14304.d(67): called from here: modify14304(cast(const(double)[])slice14304)
-fail_compilation/fail14304.d(53): Error: cannot modify read-only string literal "abc"
-fail_compilation/fail14304.d(70): called from here: modify14304(cast(const(char)[])str14304)
+fail_compilation/fail14304.d(26): Error: reinterpreting cast from `const(S14304)*` to `S14304*` is not supported in CTFE
+fail_compilation/fail14304.d(58): called from here: `sle14304.modify()`
+fail_compilation/fail14304.d(35): Error: cannot modify read-only constant `[1:1, 2:2]`
+fail_compilation/fail14304.d(61): called from here: `modify14304(aae14304)`
+fail_compilation/fail14304.d(41): Error: cannot modify read-only constant `[1, 2, 3]`
+fail_compilation/fail14304.d(64): called from here: `modify14304(cast(const(int)[])index14304)`
+fail_compilation/fail14304.d(46): Error: array cast from `immutable(double[])` to `double[]` is not supported at compile time
+fail_compilation/fail14304.d(67): called from here: `modify14304(cast(const(double)[])slice14304)`
+fail_compilation/fail14304.d(53): Error: cannot modify read-only string literal `"abc"`
+fail_compilation/fail14304.d(70): called from here: `modify14304(cast(const(char)[])str14304)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail144.d b/gcc/testsuite/gdc.test/fail_compilation/fail144.d
index 574d1675049..6e73d3bea86 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail144.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail144.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail144.d(13): Error: "message"
-fail_compilation/fail144.d(26): called from here: bar(7)
+fail_compilation/fail144.d(13): Error: `"message"`
+fail_compilation/fail144.d(26): called from here: `bar(7)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14406.d b/gcc/testsuite/gdc.test/fail_compilation/fail14406.d
index 09f481691e9..3725a913c5b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14406.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14406.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail14406.d-mixin-20(20): Error: variable fail14406.CFrop.bar_obj cannot be further field because it will change the determined CFrop size
-fail_compilation/fail14406.d-mixin-25(25): Error: variable fail14406.IFrop.bar_obj field not allowed in interface
+fail_compilation/fail14406.d-mixin-20(20): Error: variable `fail14406.CFrop.bar_obj` cannot be further field because it will change the determined CFrop size
+fail_compilation/fail14406.d-mixin-25(25): Error: variable `fail14406.IFrop.bar_obj` field not allowed in interface
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14407.d b/gcc/testsuite/gdc.test/fail_compilation/fail14407.d
deleted file mode 100644
index 341c5cad1d3..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14407.d
+++ /dev/null
@@ -1,47 +0,0 @@
-import imports.a14407;
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail14407.d(23): Deprecation: class imports.a14407.C is deprecated
-fail_compilation/fail14407.d(23): Deprecation: allocator imports.a14407.C.new is deprecated
-fail_compilation/fail14407.d(23): Error: pure function 'fail14407.testC' cannot call impure allocator 'imports.a14407.C.new'
-fail_compilation/fail14407.d(23): Error: @safe function 'fail14407.testC' cannot call @system allocator 'imports.a14407.C.new'
-fail_compilation/fail14407.d(23): Error: @nogc function 'fail14407.testC' cannot call non-@nogc allocator 'imports.a14407.C.new'
-fail_compilation/fail14407.d(23): Error: class imports.a14407.C member `new` is not accessible
-fail_compilation/fail14407.d(23): Error: pure function 'fail14407.testC' cannot call impure constructor 'imports.a14407.C.this'
-fail_compilation/fail14407.d(23): Error: @safe function 'fail14407.testC' cannot call @system constructor 'imports.a14407.C.this'
-fail_compilation/fail14407.d(23): Error: @nogc function 'fail14407.testC' cannot call non-@nogc constructor 'imports.a14407.C.this'
-fail_compilation/fail14407.d(23): Error: class imports.a14407.C member `this` is not accessible
-fail_compilation/fail14407.d(23): Error: allocator `imports.a14407.C.new` is not nothrow
-fail_compilation/fail14407.d(23): Error: constructor `imports.a14407.C.this` is not nothrow
-fail_compilation/fail14407.d(21): Error: nothrow function `fail14407.testC` may throw
----
-*/
-void testC() pure nothrow @safe @nogc
-{
- new("arg") C(0);
-}
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail14407.d(46): Deprecation: struct imports.a14407.S is deprecated
-fail_compilation/fail14407.d(46): Deprecation: allocator imports.a14407.S.new is deprecated
-fail_compilation/fail14407.d(46): Error: pure function 'fail14407.testS' cannot call impure allocator 'imports.a14407.S.new'
-fail_compilation/fail14407.d(46): Error: @safe function 'fail14407.testS' cannot call @system allocator 'imports.a14407.S.new'
-fail_compilation/fail14407.d(46): Error: @nogc function 'fail14407.testS' cannot call non-@nogc allocator 'imports.a14407.S.new'
-fail_compilation/fail14407.d(46): Error: struct imports.a14407.S member `new` is not accessible
-fail_compilation/fail14407.d(46): Error: pure function 'fail14407.testS' cannot call impure constructor 'imports.a14407.S.this'
-fail_compilation/fail14407.d(46): Error: @safe function 'fail14407.testS' cannot call @system constructor 'imports.a14407.S.this'
-fail_compilation/fail14407.d(46): Error: @nogc function 'fail14407.testS' cannot call non-@nogc constructor 'imports.a14407.S.this'
-fail_compilation/fail14407.d(46): Error: struct imports.a14407.S member `this` is not accessible
-fail_compilation/fail14407.d(46): Error: allocator `imports.a14407.S.new` is not nothrow
-fail_compilation/fail14407.d(46): Error: constructor `imports.a14407.S.this` is not nothrow
-fail_compilation/fail14407.d(44): Error: nothrow function `fail14407.testS` may throw
----
-*/
-void testS() pure nothrow @safe @nogc
-{
- new("arg") S(0);
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14416.d b/gcc/testsuite/gdc.test/fail_compilation/fail14416.d
index b0518c85e75..9d0b3e8f402 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14416.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14416.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail14416.d(13): Error: template S(T) does not have property 'sizeof'
+fail_compilation/fail14416.d(13): Error: template `S(T)` does not have property `sizeof`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14486.d b/gcc/testsuite/gdc.test/fail_compilation/fail14486.d
index 84af9cfbbd4..07152f4bb45 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14486.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14486.d
@@ -41,148 +41,48 @@ fail_compilation/fail14486.d(84): Error: `nothrow` function `fail14486.test3b` m
class C0a { }
class C1a { ~this() {} }
-class C2a { ~this() {} @nogc pure @safe delete(void* p) {} }
-class C3a { @nogc pure @safe ~this() {} delete(void* p) {} }
-class C4a { @nogc pure @safe ~this() {} @nogc pure @safe delete(void* p) {} }
class C0b { }
class C1b { ~this() {} }
-class C2b { ~this() {} nothrow delete(void* p) {} }
-class C3b { nothrow ~this() {} delete(void* p) {} }
-class C4b { nothrow ~this() {} nothrow delete(void* p) {} }
struct S0a { }
struct S1a { ~this() {} }
-struct S2a { ~this() {} @nogc pure @safe delete(void* p) {} }
-struct S3a { @nogc pure @safe ~this() {} delete(void* p) {} }
-struct S4a { @nogc pure @safe ~this() {} @nogc pure @safe delete(void* p) {} }
struct S0b { }
struct S1b { ~this() {} }
-struct S2b { ~this() {} nothrow delete(void* p) {} }
-struct S3b { nothrow ~this() {} delete(void* p) {} }
-struct S4b { nothrow ~this() {} nothrow delete(void* p) {} }
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail14486.d(44): Error: delete c0 is not @safe but is used in @safe function test1a
-fail_compilation/fail14486.d(45): Error: pure function 'fail14486.test1a' cannot call impure destructor 'fail14486.C1a.~this'
-fail_compilation/fail14486.d(45): Error: @safe function 'fail14486.test1a' cannot call @system destructor 'fail14486.C1a.~this'
-fail_compilation/fail14486.d(45): Error: @nogc function 'fail14486.test1a' cannot call non-@nogc destructor 'fail14486.C1a.~this'
-fail_compilation/fail14486.d(46): Error: pure function 'fail14486.test1a' cannot call impure destructor 'fail14486.C2a.~this'
-fail_compilation/fail14486.d(46): Error: @safe function 'fail14486.test1a' cannot call @system destructor 'fail14486.C2a.~this'
-fail_compilation/fail14486.d(46): Error: @nogc function 'fail14486.test1a' cannot call non-@nogc destructor 'fail14486.C2a.~this'
-fail_compilation/fail14486.d(47): Error: pure function 'fail14486.test1a' cannot call impure deallocator 'fail14486.C3a.delete'
-fail_compilation/fail14486.d(47): Error: @safe function 'fail14486.test1a' cannot call @system deallocator 'fail14486.C3a.delete'
-fail_compilation/fail14486.d(47): Error: @nogc function 'fail14486.test1a' cannot call non-@nogc deallocator 'fail14486.C3a.delete'
-fail_compilation/fail14486.d(48): Error: delete c4 is not @safe but is used in @safe function test1a
----*/
void test1a() @nogc pure @safe
{
C0a c0; delete c0; // error
C1a c1; delete c1; // error
- C2a c2; delete c2; // error
- C3a c3; delete c3; // error
- C4a c4; delete c4; // no error
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail14486.d(63): Error: destructor `fail14486.C1b.~this` is not nothrow
-fail_compilation/fail14486.d(64): Error: destructor `fail14486.C2b.~this` is not nothrow
-fail_compilation/fail14486.d(65): Error: deallocator `fail14486.C3b.delete` is not nothrow
-fail_compilation/fail14486.d(60): Error: nothrow function `fail14486.test1b` may throw
----
-*/
void test1b() nothrow
{
C0b c0; delete c0; // no error
C1b c1; delete c1; // error
- C2b c2; delete c2; // error
- C3b c3; delete c3; // error
- C4b c4; delete c4; // no error
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail14486.d(86): Error: delete s0 is not @safe but is used in @safe function test2a
-fail_compilation/fail14486.d(87): Error: pure function 'fail14486.test2a' cannot call impure destructor 'fail14486.S1a.~this'
-fail_compilation/fail14486.d(87): Error: @safe function 'fail14486.test2a' cannot call @system destructor 'fail14486.S1a.~this'
-fail_compilation/fail14486.d(87): Error: @nogc function 'fail14486.test2a' cannot call non-@nogc destructor 'fail14486.S1a.~this'
-fail_compilation/fail14486.d(88): Error: pure function 'fail14486.test2a' cannot call impure destructor 'fail14486.S2a.~this'
-fail_compilation/fail14486.d(88): Error: @safe function 'fail14486.test2a' cannot call @system destructor 'fail14486.S2a.~this'
-fail_compilation/fail14486.d(88): Error: @nogc function 'fail14486.test2a' cannot call non-@nogc destructor 'fail14486.S2a.~this'
-fail_compilation/fail14486.d(89): Error: pure function 'fail14486.test2a' cannot call impure deallocator 'fail14486.S3a.delete'
-fail_compilation/fail14486.d(89): Error: @safe function 'fail14486.test2a' cannot call @system deallocator 'fail14486.S3a.delete'
-fail_compilation/fail14486.d(89): Error: @nogc function 'fail14486.test2a' cannot call non-@nogc deallocator 'fail14486.S3a.delete'
----
-*/
void test2a() @nogc pure @safe
{
S0a* s0; delete s0; // error
S1a* s1; delete s1; // error
- S2a* s2; delete s2; // error
- S3a* s3; delete s3; // error
- S4a* s4; delete s4; // no error
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail14486.d(105): Error: destructor `fail14486.S1b.~this` is not nothrow
-fail_compilation/fail14486.d(106): Error: destructor `fail14486.S2b.~this` is not nothrow
-fail_compilation/fail14486.d(107): Error: deallocator `fail14486.S3b.delete` is not nothrow
-fail_compilation/fail14486.d(102): Error: nothrow function `fail14486.test2b` may throw
----
-*/
void test2b() nothrow
{
S0b* s0; delete s0; // no error
S1b* s1; delete s1; // error
- S2b* s2; delete s2; // error
- S3b* s3; delete s3; // error
- S4b* s4; delete s4; // no error
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail14486.d(127): Error: delete a0 is not @safe but is used in @safe function test3a
-fail_compilation/fail14486.d(128): Error: pure function 'fail14486.test3a' cannot call impure destructor 'fail14486.S1a.~this'
-fail_compilation/fail14486.d(128): Error: @safe function 'fail14486.test3a' cannot call @system destructor 'fail14486.S1a.~this'
-fail_compilation/fail14486.d(128): Error: @nogc function 'fail14486.test3a' cannot call non-@nogc destructor 'fail14486.S1a.~this'
-fail_compilation/fail14486.d(129): Error: pure function 'fail14486.test3a' cannot call impure destructor 'fail14486.S2a.~this'
-fail_compilation/fail14486.d(129): Error: @safe function 'fail14486.test3a' cannot call @system destructor 'fail14486.S2a.~this'
-fail_compilation/fail14486.d(129): Error: @nogc function 'fail14486.test3a' cannot call non-@nogc destructor 'fail14486.S2a.~this'
-fail_compilation/fail14486.d(130): Error: delete a3 is not @safe but is used in @safe function test3a
-fail_compilation/fail14486.d(131): Error: delete a4 is not @safe but is used in @safe function test3a
----
-*/
void test3a() @nogc pure @safe
{
S0a[] a0; delete a0; // error
S1a[] a1; delete a1; // error
- S2a[] a2; delete a2; // error
- S3a[] a3; delete a3; // error
- S4a[] a4; delete a4; // error
}
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail14486.d(145): Error: destructor `fail14486.S1b.~this` is not nothrow
-fail_compilation/fail14486.d(146): Error: destructor `fail14486.S2b.~this` is not nothrow
-fail_compilation/fail14486.d(142): Error: nothrow function `fail14486.test3b` may throw
----
-*/
void test3b() nothrow
{
S0b[] a0; delete a0; // no error
S1b[] a1; delete a1; // error
- S2b[] a2; delete a2; // error
- S3b[] a3; delete a3; // no error
- S4b[] a4; delete a4; // no error
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail145.d b/gcc/testsuite/gdc.test/fail_compilation/fail145.d
index 9d285dc1525..5a7a4ca2228 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail145.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail145.d
@@ -1,8 +1,9 @@
/*
+REQUIRED_ARGS: -checkaction=context
TEST_OUTPUT:
---
-fail_compilation/fail145.d(13): Error: assert(i < 0) failed
-fail_compilation/fail145.d(26): called from here: bar(7)
+fail_compilation/fail145.d(14): Error: `"assert(i && (i < 0)) failed"`
+fail_compilation/fail145.d(27): called from here: `bar(7)`
---
*/
@@ -10,7 +11,7 @@ fail_compilation/fail145.d(26): called from here: bar(7)
int bar(int i)
{
- assert(i < 0);
+ assert(i && i < 0);
foreach_reverse (k, v; "hello")
{
i <<= 1;
@@ -26,5 +27,5 @@ void main()
static b = bar(7);
auto c = bar(7);
//printf("b = %d, %d\n", b, c);
- assert(b == 674);
+ assert(b && b == 674);
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14554.d b/gcc/testsuite/gdc.test/fail_compilation/fail14554.d
index 5d4e96cdc64..73b0a780023 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14554.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14554.d
@@ -3,14 +3,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail14554.d(28): Error: fail14554.issue14554_1.foo called with argument types (int) matches both:
-fail_compilation/fail14554.d(17): fail14554.issue14554_1.foo!bool.foo(int j)
+fail_compilation/fail14554.d(28): Error: `fail14554.issue14554_1.foo` called with argument types `(int)` matches both:
+fail_compilation/fail14554.d(17): `fail14554.issue14554_1.foo!bool.foo(int j)`
and:
-fail_compilation/fail14554.d(18): fail14554.issue14554_1.foo!bool.foo(int j)
-fail_compilation/fail14554.d(29): Error: fail14554.issue14554_2.foo called with argument types (int) matches both:
-fail_compilation/fail14554.d(22): fail14554.issue14554_2.foo!bool.foo(int j)
+fail_compilation/fail14554.d(18): `fail14554.issue14554_1.foo!bool.foo(int j)`
+fail_compilation/fail14554.d(29): Error: `fail14554.issue14554_2.foo` called with argument types `(int)` matches both:
+fail_compilation/fail14554.d(22): `fail14554.issue14554_2.foo!bool.foo(int j)`
and:
-fail_compilation/fail14554.d(23): fail14554.issue14554_2.foo!bool.foo(int j)
+fail_compilation/fail14554.d(23): `fail14554.issue14554_2.foo!bool.foo(int j)`
---
*/
struct issue14554_1 {
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14669.d b/gcc/testsuite/gdc.test/fail_compilation/fail14669.d
index 89840010eb4..c5ae8e7a70e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14669.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14669.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail14669.d(11): Error: 'auto' can only be used as part of 'auto ref' for template function parameters
-fail_compilation/fail14669.d(16): Error: template instance fail14669.foo1!() error instantiating
-fail_compilation/fail14669.d(12): Error: 'auto' can only be used as part of 'auto ref' for template function parameters
-fail_compilation/fail14669.d(17): Error: template fail14669.foo2 cannot deduce function from argument types !()(int), candidates are:
-fail_compilation/fail14669.d(12): fail14669.foo2()(auto int a)
+fail_compilation/fail14669.d(11): Error: `auto` can only be used as part of `auto ref` for template function parameters
+fail_compilation/fail14669.d(16): Error: template instance `fail14669.foo1!()` error instantiating
+fail_compilation/fail14669.d(12): Error: `auto` can only be used as part of `auto ref` for template function parameters
+fail_compilation/fail14669.d(17): Error: template `fail14669.foo2` cannot deduce function from argument types `!()(int)`
+fail_compilation/fail14669.d(12): Candidate is: `foo2()(auto int a)`
---
*/
void foo1()(auto int a) {}
@@ -20,10 +20,10 @@ void test1()
/*
TEST_OUTPUT:
---
-fail_compilation/fail14669.d(29): Error: 'auto' can only be used as part of 'auto ref' for template function parameters
-fail_compilation/fail14669.d(38): Error: template instance fail14669.bar1!int error instantiating
-fail_compilation/fail14669.d(30): Error: 'auto' can only be used as part of 'auto ref' for template function parameters
-fail_compilation/fail14669.d(40): Error: template instance fail14669.bar2!int error instantiating
+fail_compilation/fail14669.d(29): Error: `auto` can only be used as part of `auto ref` for template function parameters
+fail_compilation/fail14669.d(38): Error: template instance `fail14669.bar1!int` error instantiating
+fail_compilation/fail14669.d(30): Error: `auto` can only be used as part of `auto ref` for template function parameters
+fail_compilation/fail14669.d(40): Error: template instance `fail14669.bar2!int` error instantiating
---
*/
void bar1(T)(auto ref T x) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14965.d b/gcc/testsuite/gdc.test/fail_compilation/fail14965.d
index f1a1ec152e1..37fc0fa0cc6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail14965.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14965.d
@@ -1,18 +1,18 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail14965.d(19): Error: forward reference to inferred return type of function 'foo1'
-fail_compilation/fail14965.d(20): Error: forward reference to inferred return type of function 'foo2'
-fail_compilation/fail14965.d(22): Error: forward reference to inferred return type of function 'bar1'
-fail_compilation/fail14965.d(23): Error: forward reference to inferred return type of function 'bar2'
-fail_compilation/fail14965.d(25): Error: forward reference to inferred return type of function 'baz1'
-fail_compilation/fail14965.d(26): Error: forward reference to inferred return type of function 'baz2'
-fail_compilation/fail14965.d(30): Error: forward reference to inferred return type of function 'foo1'
-fail_compilation/fail14965.d(31): Error: forward reference to inferred return type of function 'foo2'
-fail_compilation/fail14965.d(33): Error: forward reference to inferred return type of function 'bar1'
-fail_compilation/fail14965.d(34): Error: forward reference to inferred return type of function 'bar2'
-fail_compilation/fail14965.d(36): Error: forward reference to inferred return type of function 'baz1'
-fail_compilation/fail14965.d(37): Error: forward reference to inferred return type of function 'baz2'
+fail_compilation/fail14965.d(19): Error: forward reference to inferred return type of function `foo1`
+fail_compilation/fail14965.d(20): Error: forward reference to inferred return type of function `foo2`
+fail_compilation/fail14965.d(22): Error: forward reference to inferred return type of function `bar1`
+fail_compilation/fail14965.d(23): Error: forward reference to inferred return type of function `bar2`
+fail_compilation/fail14965.d(25): Error: forward reference to inferred return type of function `baz1`
+fail_compilation/fail14965.d(26): Error: forward reference to inferred return type of function `baz2`
+fail_compilation/fail14965.d(30): Error: forward reference to inferred return type of function `foo1`
+fail_compilation/fail14965.d(31): Error: forward reference to inferred return type of function `foo2`
+fail_compilation/fail14965.d(33): Error: forward reference to inferred return type of function `bar1`
+fail_compilation/fail14965.d(34): Error: forward reference to inferred return type of function `bar2`
+fail_compilation/fail14965.d(36): Error: forward reference to inferred return type of function `baz1`
+fail_compilation/fail14965.d(37): Error: forward reference to inferred return type of function `baz2`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail14997.d b/gcc/testsuite/gdc.test/fail_compilation/fail14997.d
new file mode 100644
index 00000000000..3654f041429
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail14997.d
@@ -0,0 +1,20 @@
+// https://issues.dlang.org/show_bug.cgi?id=14997
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail14997.d(19): Error: none of the overloads of `this` are callable using argument types `()`
+fail_compilation/fail14997.d(14): Candidates are: `fail14997.Foo.this(int a)`
+fail_compilation/fail14997.d(15): `fail14997.Foo.this(string a)`
+---
+*/
+
+class Foo
+{
+ this (int a) {}
+ this (string a) {}
+}
+void main()
+{
+ auto a = new Foo;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail150.d b/gcc/testsuite/gdc.test/fail_compilation/fail150.d
index 4a53053af66..6ba04c181bf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail150.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail150.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail150.d(22): Error: e.new is only for allocating nested classes
+fail_compilation/fail150.d(22): Error: `.new` is only for allocating nested classes
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15068.d b/gcc/testsuite/gdc.test/fail_compilation/fail15068.d
new file mode 100644
index 00000000000..ca555b0a2f2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail15068.d
@@ -0,0 +1,18 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail15068.d(17): Error: `T!int` is not a valid template instance, because `T` is not a template declaration but a type (`T == int`)
+fail_compilation/fail15068.d(13): Error: template instance `fail15068.Stuff!int` error instantiating
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=15068
+
+void main()
+{
+ Stuff!int s;
+}
+struct Stuff(T)
+{
+ T!int var;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15292.d b/gcc/testsuite/gdc.test/fail_compilation/fail15292.d
deleted file mode 100644
index 3b3602fc502..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail15292.d
+++ /dev/null
@@ -1,28 +0,0 @@
-// REQUIRED_ARGS: -o-
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail15292.d(27): Error: cannot compare S15292 because its auto generated member-wise equality has recursive definition
----
-*/
-
-struct NullableRef15292(T)
-{
- inout(T) get() inout
- {
- assert(false);
- }
-
- alias get this;
-}
-
-struct S15292
-{
- NullableRef15292!S15292 n;
-}
-
-void main()
-{
- S15292 s;
- assert(s == s);
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail153.d b/gcc/testsuite/gdc.test/fail_compilation/fail153.d
index 8e397e3d10c..96cd383b506 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail153.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail153.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail153.d(10): Error: class fail153.Bar cannot inherit from final class Foo
+fail_compilation/fail153.d(10): Error: class `fail153.Bar` cannot inherit from class `Foo` because it is `final`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail154.d b/gcc/testsuite/gdc.test/fail_compilation/fail154.d
index 8b5fefc3820..ee9eb42c3a1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail154.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail154.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail154.d(18): Error: template instance X!(MYP!int) does not match template declaration X(T : Policy!T, alias Policy)
+fail_compilation/fail154.d(18): Error: template instance `X!(MYP!int)` does not match template declaration `X(T : Policy!T, alias Policy)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail155.d b/gcc/testsuite/gdc.test/fail_compilation/fail155.d
index cc1e03c3a5c..6d8f184c9ea 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail155.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail155.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail155.d(19): Error: overlapping initialization for y
+fail_compilation/fail155.d(20): Error: overlapping initialization for `y`
+fail_compilation/fail155.d(20): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15535.d b/gcc/testsuite/gdc.test/fail_compilation/fail15535.d
index d53e4d7f2d1..6f71a927fb2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail15535.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail15535.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail15535.d(17): Error: goto default not allowed in final switch statement
+fail_compilation/fail15535.d(17): Error: `goto default` not allowed in `final switch` statement
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15550.d b/gcc/testsuite/gdc.test/fail_compilation/fail15550.d
index e20a7f294f6..f7b910c3c91 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail15550.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail15550.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail15550.d(25): Error: partial template instance foo!int has no type
-fail_compilation/fail15550.d(26): Error: partial template instance opDispatch!"_isMatrix" has no type
-fail_compilation/fail15550.d(27): Error: partial template instance baz!"_isMatrix" has no type
+fail_compilation/fail15550.d(25): Error: partial template instance `foo!int` has no type
+fail_compilation/fail15550.d(26): Error: partial template instance `opDispatch!"_isMatrix"` has no type
+fail_compilation/fail15550.d(27): Error: partial template instance `baz!"_isMatrix"` has no type
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail156.d b/gcc/testsuite/gdc.test/fail_compilation/fail156.d
index cfd5b83a01b..bfc2383b590 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail156.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail156.d
@@ -2,8 +2,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail156.d(33): Error: overlapping initialization for y
-fail_compilation/fail156.d(40): Error: overlapping initialization for y
+fail_compilation/fail156.d(35): Error: overlapping initialization for `y`
+fail_compilation/fail156.d(35): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
+fail_compilation/fail156.d(42): Error: overlapping initialization for `y`
+fail_compilation/fail156.d(42): `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15616a.d b/gcc/testsuite/gdc.test/fail_compilation/fail15616a.d
index 9726e3df339..e047365fbe3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail15616a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail15616a.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail15616a.d(41): Error: none of the overloads of 'foo' are callable using argument types (double), candidates are:
-fail_compilation/fail15616a.d(14): fail15616a.foo(int a)
-fail_compilation/fail15616a.d(17): fail15616a.foo(int a, int b)
-fail_compilation/fail15616a.d(26): fail15616a.foo(int a, int b, int c)
-fail_compilation/fail15616a.d(29): fail15616a.foo(string a)
-fail_compilation/fail15616a.d(32): fail15616a.foo(string a, string b)
+fail_compilation/fail15616a.d(41): Error: none of the overloads of `foo` are callable using argument types `(double)`
+fail_compilation/fail15616a.d(14): Candidates are: `fail15616a.foo(int a)`
+fail_compilation/fail15616a.d(17): `fail15616a.foo(int a, int b)`
+fail_compilation/fail15616a.d(26): `fail15616a.foo(int a, int b, int c)`
+fail_compilation/fail15616a.d(29): `fail15616a.foo(string a)`
+fail_compilation/fail15616a.d(32): `fail15616a.foo(string a, string b)`
fail_compilation/fail15616a.d(41): ... (3 more, -v to show) ...
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15616b.d b/gcc/testsuite/gdc.test/fail_compilation/fail15616b.d
index 4776f40439c..faad0f1dffc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail15616b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail15616b.d
@@ -1,19 +1,33 @@
/*
REQUIRED_ARGS: -v
+TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|import|semantic|entry|\s*$)")
TEST_OUTPUT:
---
-fail_compilation/fail15616b.d(43): Error: none of the overloads of 'foo' are callable using argument types (double), candidates are:
-fail_compilation/fail15616b.d(16): fail15616b.foo(int a)
-fail_compilation/fail15616b.d(19): fail15616b.foo(int a, int b)
-fail_compilation/fail15616b.d(28): fail15616b.foo(int a, int b, int c)
-fail_compilation/fail15616b.d(31): fail15616b.foo(string a)
-fail_compilation/fail15616b.d(34): fail15616b.foo(string a, string b)
-fail_compilation/fail15616b.d(37): fail15616b.foo(string a, string b, string c)
-fail_compilation/fail15616b.d(22): fail15616b.foo(T)(T a) if (is(T == float))
-fail_compilation/fail15616b.d(25): fail15616b.foo(T)(T a) if (is(T == char))
+fail_compilation/fail15616b.d(44): Error: none of the overloads of `foo` are callable using argument types `(double)`
+fail_compilation/fail15616b.d(17): Candidates are: `fail15616b.foo(int a)`
+fail_compilation/fail15616b.d(20): `fail15616b.foo(int a, int b)`
+fail_compilation/fail15616b.d(29): `fail15616b.foo(int a, int b, int c)`
+fail_compilation/fail15616b.d(32): `fail15616b.foo(string a)`
+fail_compilation/fail15616b.d(35): `fail15616b.foo(string a, string b)`
+fail_compilation/fail15616b.d(38): `fail15616b.foo(string a, string b, string c)`
+fail_compilation/fail15616b.d(23): `foo(T)(T a)`
+ with `T = double`
+ whose parameters have the following constraints:
+ `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
+` > is(T == float)
+` `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
+fail_compilation/fail15616b.d(26): `foo(T)(T a)`
+ with `T = double`
+ whose parameters have the following constraints:
+ `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
+` > is(T == char)
+` `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
+fail_compilation/fail15616b.d(44): All possible candidates are marked as `deprecated` or `@disable`
+ Tip: not satisfied constraints are marked with `>`
---
*/
+#line 17
void foo(int a)
{}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15626.d b/gcc/testsuite/gdc.test/fail_compilation/fail15626.d
index 3b7020539c3..8ef14cd5aae 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail15626.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail15626.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail15626.d(12): Error: class fail15626.D C++ base class C needs at least one virtual function
+fail_compilation/fail15626.d(12): Error: class `fail15626.D` C++ base class `C` needs at least one virtual function
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15667.d b/gcc/testsuite/gdc.test/fail_compilation/fail15667.d
index f23963f0a8c..ce4940ffa31 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail15667.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail15667.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -o-
+// EXTRA_FILES: imports/a15667.d
/*
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15691.d b/gcc/testsuite/gdc.test/fail_compilation/fail15691.d
new file mode 100644
index 00000000000..a20e1b56203
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail15691.d
@@ -0,0 +1,24 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail15691.d(15): Error: `c` is not a member of `Foo`
+fail_compilation/fail15691.d(20): Error: `bc` is not a member of `Foo`, did you mean variable `abc`?
+---
+*/
+
+struct Foo { int a; int abc; }
+
+void main()
+{
+ Foo z = { // line 13
+ a: 3,
+ c: 4, // line 15
+ };
+
+ Foo z2 = { // line 18
+ a: 3,
+ bc: 4, // line 20
+ };
+}
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail15755.d b/gcc/testsuite/gdc.test/fail_compilation/fail15755.d
new file mode 100644
index 00000000000..8fd2b51b60a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail15755.d
@@ -0,0 +1,29 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail15755.d(28): Error: `tuple(123)` has no effect
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=15755
+
+struct Foo
+{
+ @(123)
+ int a;
+}
+
+template Attributes(As...)
+{
+ alias Attributes = As;
+}
+
+template getattribute(alias member, alias attrs = Attributes!(__traits(getAttributes, member)))
+{
+ alias getattribute = attrs;
+}
+
+void main()
+{
+ getattribute!(__traits(getMember, Foo, "a"));
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail158.d b/gcc/testsuite/gdc.test/fail_compilation/fail158.d
index 353874f598b..6f09f658487 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail158.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail158.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail158.d(17): Error: more initializers than fields (2) of S
+fail_compilation/fail158.d(17): Error: more initializers than fields (2) of `S`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail159.d b/gcc/testsuite/gdc.test/fail_compilation/fail159.d
index 13be8eeefac..a1006066204 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail159.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail159.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail159.d(24): Error: static assert `foo(S(1, 5), S(1, 4)) == 0` is false
+fail_compilation/fail159.d(24): Error: static assert: `foo(S(1, 5), S(1, 4)) == 0` is false
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail160.d b/gcc/testsuite/gdc.test/fail_compilation/fail160.d
index 1e2414cf74b..c07c8d36091 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail160.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail160.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail160.d(22): Error: typeid(fail160.Foo).vtbl is not yet implemented at compile time
+fail_compilation/fail160.d(22): Error: `typeid(fail160.Foo).vtbl` is not yet implemented at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail16001.d b/gcc/testsuite/gdc.test/fail_compilation/fail16001.d
new file mode 100644
index 00000000000..ef5fc7bdb7f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail16001.d
@@ -0,0 +1,13 @@
+// REQUIRED_ARGS: -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail16001.d(10): Deprecation: Using `(args) => { ... }` to create a delegate that returns a delegate is error-prone.
+fail_compilation/fail16001.d(10): Use `(args) { ... }` for a multi-statement function literal or use `(args) => () { }` if you intended for the lambda to return a delegate.
+---
+*/
+void main() {
+ auto fail = () => {};
+ auto ok = () => () {};
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail161.d b/gcc/testsuite/gdc.test/fail_compilation/fail161.d
index d0aa940c043..ecbf69199c7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail161.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail161.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail161.d(15): Error: template instance MetaString!"2 == 1" does not match template declaration MetaString(String)
+fail_compilation/fail161.d(15): Error: template instance `MetaString!"2 == 1"` does not match template declaration `MetaString(String)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail162.d b/gcc/testsuite/gdc.test/fail_compilation/fail162.d
index f49fbac6497..663e0e1fcbb 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail162.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail162.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail162.d(25): Error: template fail162.testHelper cannot deduce function from argument types !()(string, string), candidates are:
-fail_compilation/fail162.d(10): fail162.testHelper(A...)()
-fail_compilation/fail162.d(30): Error: template instance fail162.test!("hello", "world") error instantiating
+fail_compilation/fail162.d(25): Error: template `fail162.testHelper` cannot deduce function from argument types `!()(string, string)`
+fail_compilation/fail162.d(10): Candidate is: `testHelper(A...)()`
+fail_compilation/fail162.d(30): Error: template instance `fail162.test!("hello", "world")` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail16206a.d b/gcc/testsuite/gdc.test/fail_compilation/fail16206a.d
index 3c1cc56782e..d28a8c33731 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail16206a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail16206a.d
@@ -1,12 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail16206a.d(12): Error: `bool` expected as third argument of `__traits(getOverloads)`, not `"Not a bool"` of type `string`
+fail_compilation/fail16206a.d(14): Error: `bool` expected as third argument of `__traits(getOverloads)`, not `"Not a bool"` of type `string`
---
*/
-struct S {
+struct S
+{
static int foo()() { return 0; }
}
+
alias AliasSeq(T...) = T;
-alias allFoos = AliasSeq!(__traits(getOverloads, S, "foo", "Not a bool")); \ No newline at end of file
+alias allFoos = AliasSeq!(__traits(getOverloads, S, "foo", "Not a bool"));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail16206b.d b/gcc/testsuite/gdc.test/fail_compilation/fail16206b.d
index 9b3a69c0914..0f20ad59a89 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail16206b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail16206b.d
@@ -1,12 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail16206b.d(12): Error: expected 2 arguments for `hasMember` but had 3
+fail_compilation/fail16206b.d(14): Error: expected 2 arguments for `hasMember` but had 3
---
*/
-struct S {
+struct S
+{
static int foo()() { return 0; }
}
+
alias AliasSeq(T...) = T;
-alias allFoos = AliasSeq!(__traits(hasMember, S, "foo", true)); \ No newline at end of file
+alias allFoos = AliasSeq!(__traits(hasMember, S, "foo", true));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail163.d b/gcc/testsuite/gdc.test/fail_compilation/fail163.d
index c2eb1fdd142..7f8f028fa52 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail163.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail163.d
@@ -27,7 +27,7 @@ void test2()
/*
TEST_OUTPUT:
---
-fail_compilation/fail163.d(37): Error: cannot modify const expression p
+fail_compilation/fail163.d(37): Error: cannot modify `const` expression `p`
---
*/
void test3()
@@ -53,7 +53,7 @@ void test4()
/*
TEST_OUTPUT:
---
-fail_compilation/fail163.d(63): Error: cannot modify const expression *p
+fail_compilation/fail163.d(63): Error: cannot modify `const` expression `*p`
---
*/
void test5()
@@ -67,7 +67,7 @@ void test5()
TEST_OUTPUT:
---
fail_compilation/fail163.d(76): Error: cannot implicitly convert expression `& x` of type `int*` to `immutable(int)*`
-fail_compilation/fail163.d(77): Error: cannot modify immutable expression *p
+fail_compilation/fail163.d(77): Error: cannot modify `immutable` expression `*p`
---
*/
void test6()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail16600.d b/gcc/testsuite/gdc.test/fail_compilation/fail16600.d
index a7f30d9187c..a4bc7397a32 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail16600.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail16600.d
@@ -1,9 +1,9 @@
/* TEST_OUTPUT:
---
-fail_compilation/fail16600.d(22): Error: fail16600.S.__ctor called with argument types (string) const matches both:
-fail_compilation/fail16600.d(16): fail16600.S.this(string _param_0)
+fail_compilation/fail16600.d(22): Error: `fail16600.S.__ctor` called with argument types `(string) const` matches both:
+fail_compilation/fail16600.d(16): `fail16600.S.this(string _param_0)`
and:
-fail_compilation/fail16600.d(17): fail16600.S.this(string _param_0) immutable
+fail_compilation/fail16600.d(17): `fail16600.S.this(string _param_0) immutable`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail16689.d b/gcc/testsuite/gdc.test/fail_compilation/fail16689.d
new file mode 100644
index 00000000000..f8e0bae50d9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail16689.d
@@ -0,0 +1,14 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail16689.d(3): Error: static assert: "false"
+fail_compilation/fail16689.d(6): instantiated from here: `Issue16689!()`
+---
+*/
+#line 1
+mixin template Issue16689()
+{
+ static assert(false, "false");
+}
+
+mixin Issue16689!();
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail169.d b/gcc/testsuite/gdc.test/fail_compilation/fail169.d
index bd1da0bf6b2..a8ad1021ee8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail169.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail169.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail169.d(8): Error: cannot have const out parameter of type const(int)
+fail_compilation/fail169.d(8): Error: cannot have `const out` parameter of type `const(int)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail16997.d b/gcc/testsuite/gdc.test/fail_compilation/fail16997.d
new file mode 100644
index 00000000000..a8f3ae453db
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail16997.d
@@ -0,0 +1,59 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/fail16997.d(31): Deprecation: integral promotion not done for `~c`, use '-preview=intpromote' switch or `~cast(int)(c)`
+fail_compilation/fail16997.d(32): Deprecation: integral promotion not done for `-c`, use '-preview=intpromote' switch or `-cast(int)(c)`
+fail_compilation/fail16997.d(33): Deprecation: integral promotion not done for `+c`, use '-preview=intpromote' switch or `+cast(int)(c)`
+fail_compilation/fail16997.d(36): Deprecation: integral promotion not done for `~w`, use '-preview=intpromote' switch or `~cast(int)(w)`
+fail_compilation/fail16997.d(37): Deprecation: integral promotion not done for `-w`, use '-preview=intpromote' switch or `-cast(int)(w)`
+fail_compilation/fail16997.d(38): Deprecation: integral promotion not done for `+w`, use '-preview=intpromote' switch or `+cast(int)(w)`
+fail_compilation/fail16997.d(41): Deprecation: integral promotion not done for `~sb`, use '-preview=intpromote' switch or `~cast(int)(sb)`
+fail_compilation/fail16997.d(42): Deprecation: integral promotion not done for `-sb`, use '-preview=intpromote' switch or `-cast(int)(sb)`
+fail_compilation/fail16997.d(43): Deprecation: integral promotion not done for `+sb`, use '-preview=intpromote' switch or `+cast(int)(sb)`
+fail_compilation/fail16997.d(46): Deprecation: integral promotion not done for `~ub`, use '-preview=intpromote' switch or `~cast(int)(ub)`
+fail_compilation/fail16997.d(47): Deprecation: integral promotion not done for `-ub`, use '-preview=intpromote' switch or `-cast(int)(ub)`
+fail_compilation/fail16997.d(48): Deprecation: integral promotion not done for `+ub`, use '-preview=intpromote' switch or `+cast(int)(ub)`
+fail_compilation/fail16997.d(51): Deprecation: integral promotion not done for `~s`, use '-preview=intpromote' switch or `~cast(int)(s)`
+fail_compilation/fail16997.d(52): Deprecation: integral promotion not done for `-s`, use '-preview=intpromote' switch or `-cast(int)(s)`
+fail_compilation/fail16997.d(53): Deprecation: integral promotion not done for `+s`, use '-preview=intpromote' switch or `+cast(int)(s)`
+fail_compilation/fail16997.d(56): Deprecation: integral promotion not done for `~us`, use '-preview=intpromote' switch or `~cast(int)(us)`
+fail_compilation/fail16997.d(57): Deprecation: integral promotion not done for `-us`, use '-preview=intpromote' switch or `-cast(int)(us)`
+fail_compilation/fail16997.d(58): Deprecation: integral promotion not done for `+us`, use '-preview=intpromote' switch or `+cast(int)(us)`
+---
+*/
+
+void test()
+{
+ int x;
+
+ char c;
+ x = ~c;
+ x = -c;
+ x = +c;
+
+ wchar w;
+ x = ~w;
+ x = -w;
+ x = +w;
+
+ byte sb;
+ x = ~sb;
+ x = -sb;
+ x = +sb;
+
+ ubyte ub;
+ x = ~ub;
+ x = -ub;
+ x = +ub;
+
+ short s;
+ x = ~s;
+ x = -s;
+ x = +s;
+
+ ushort us;
+ x = ~us;
+ x = -us;
+ x = +us;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail170.d b/gcc/testsuite/gdc.test/fail_compilation/fail170.d
index a78fc035dde..61c7ae6b0ad 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail170.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail170.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail170.d(8): Error: variable fail170.foo.x cannot be final, perhaps you meant const?
+fail_compilation/fail170.d(8): Error: variable `fail170.foo.x` cannot be `final`, perhaps you meant `const`?
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail172.d b/gcc/testsuite/gdc.test/fail_compilation/fail172.d
index c7ccdc16b8d..9862fe8e698 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail172.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail172.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail172.d(25): Error: cannot modify const expression c1.x
-fail_compilation/fail172.d(26): Error: cannot modify const expression c2.x
-fail_compilation/fail172.d(30): Error: cannot modify const expression s1.x
-fail_compilation/fail172.d(31): Error: cannot modify const expression s2.x
+fail_compilation/fail172.d(25): Error: cannot modify `const` expression `c1.x`
+fail_compilation/fail172.d(26): Error: cannot modify `const` expression `c2.x`
+fail_compilation/fail172.d(30): Error: cannot modify `const` expression `s1.x`
+fail_compilation/fail172.d(31): Error: cannot modify `const` expression `s2.x`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17275.d b/gcc/testsuite/gdc.test/fail_compilation/fail17275.d
index fcccdf2eecd..fd7623f8d76 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17275.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17275.d
@@ -1,7 +1,7 @@
/* TEST_OUTPUT:
---
fail_compilation/fail17275.d(12): Error: undefined identifier `ModuleGroup`, did you mean function `moduleGroup`?
-fail_compilation/fail17275.d(12): Error: inout on return means inout must be on a parameter as well for inout(ModuleGroup)()
+fail_compilation/fail17275.d(12): Error: `inout` on `return` means `inout` must be on a parameter as well for `inout(ModuleGroup)()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17354.d b/gcc/testsuite/gdc.test/fail_compilation/fail17354.d
index e09f1e57914..fd44e650f34 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17354.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17354.d
@@ -1,8 +1,7 @@
-/* REQUIRED_ARGS: -de
- * TEST_OUTPUT:
+/* TEST_OUTPUT:
---
-fail_compilation/fail17354.d(13): Deprecation: cannot implicitly override base class method `object.Object.opEquals` with `fail17354.Foo.opEquals`; add `override` attribute
-fail_compilation/fail17354.d(18): Deprecation: cannot implicitly override base class method `object.Object.opEquals` with `fail17354.Bar.opEquals`; add `override` attribute
+fail_compilation/fail17354.d(12): Error: cannot implicitly override base class method `object.Object.opEquals` with `fail17354.Foo.opEquals`; add `override` attribute
+fail_compilation/fail17354.d(17): Error: cannot implicitly override base class method `object.Object.opEquals` with `fail17354.Bar.opEquals`; add `override` attribute
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17491.d b/gcc/testsuite/gdc.test/fail_compilation/fail17491.d
index 4902392cf0f..0fb9708b430 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17491.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17491.d
@@ -1,18 +1,16 @@
/* TEST_OUTPUT:
---
-fail_compilation/fail17491.d(24): Error: (S17491).init is not an lvalue
-fail_compilation/fail17491.d(25): Error: S17491(0) is not an lvalue
-fail_compilation/fail17491.d(27): Error: constant S17491(0).field is not an lvalue
-fail_compilation/fail17491.d(28): Error: constant *&S17491(0).field is not an lvalue
-fail_compilation/fail17491.d(33): Error: S17491(0) is not an lvalue
-fail_compilation/fail17491.d(34): Error: S17491(0) is not an lvalue
-fail_compilation/fail17491.d(36): Error: constant S17491(0).field is not an lvalue
-fail_compilation/fail17491.d(37): Error: constant *&S17491(0).field is not an lvalue
+fail_compilation/fail17491.d(22): Error: `(S17491).init` is not an lvalue and cannot be modified
+fail_compilation/fail17491.d(23): Error: `S17491(0)` is not an lvalue and cannot be modified
+fail_compilation/fail17491.d(25): Error: `S17491(0).field` is not an lvalue and cannot be modified
+fail_compilation/fail17491.d(26): Error: `S17491(0).field` is not an lvalue and cannot be modified
+fail_compilation/fail17491.d(31): Error: `S17491(0)` is not an lvalue and cannot be modified
+fail_compilation/fail17491.d(32): Error: `S17491(0)` is not an lvalue and cannot be modified
+fail_compilation/fail17491.d(34): Error: `S17491(0).field` is not an lvalue and cannot be modified
+fail_compilation/fail17491.d(35): Error: `S17491(0).field` is not an lvalue and cannot be modified
---
*/
-
// https://issues.dlang.org/show_bug.cgi?id=17491
-
struct S17491
{
int field;
@@ -25,7 +23,7 @@ void test17491()
*&S17491.init = S17491(42); // NG
S17491.init.field = 42; // NG
- *&S17491.init.field = 42; // Should be NG
+ *&S17491.init.field = 42; // NG
S17491.init.var = 42; // OK
*&S17491.init.var = 42; // OK
@@ -34,7 +32,7 @@ void test17491()
*&S17491(0) = S17491(42); // NG
S17491(0).field = 42; // NG
- *&S17491(0).field = 42; // Should be NG
+ *&S17491(0).field = 42; // NG
S17491(0).var = 42; // OK
*&S17491(0).var = 42; // OK
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17492.d b/gcc/testsuite/gdc.test/fail_compilation/fail17492.d
index 80e9e2a2c4a..7236c22a8bc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17492.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17492.d
@@ -1,7 +1,9 @@
-/* TEST_OUTPUT:
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
---
-fail_compilation/fail17492.d(20): Error: class `fail17492.C.testE.I` already exists at fail17492.d(13). Perhaps in another function with the same name?
-fail_compilation/fail17492.d(37): Error: struct `fail17492.S.testE.I` already exists at fail17492.d(30). Perhaps in another function with the same name?
+fail_compilation/fail17492.d(20): Error: function `fail17492.C.testE()` conflicts with previous declaration at fail_compilation/fail17492.d(13)
+fail_compilation/fail17492.d(37): Error: function `fail17492.S.testE()` conflicts with previous declaration at fail_compilation/fail17492.d(30)
---
https://issues.dlang.org/show_bug.cgi?id=17492
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17502.d b/gcc/testsuite/gdc.test/fail_compilation/fail17502.d
index b1366d136b9..49db4fc9839 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17502.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17502.d
@@ -11,9 +11,9 @@ class Foo
{
void foo()
out (res) { assert(res > 5); }
- body {}
+ do {}
auto bar()
out (res) { assert (res > 5); }
- body { return; }
+ do { return; }
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17518.d b/gcc/testsuite/gdc.test/fail_compilation/fail17518.d
new file mode 100644
index 00000000000..385483c048d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17518.d
@@ -0,0 +1,22 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail17518.d(21): Error: constructor `fail17518.S.this(inout(Correct) _param_0) inout` is not callable using argument types `(Wrong)`
+fail_compilation/fail17518.d(21): cannot pass argument `Wrong()` of type `Wrong` to parameter `inout(Correct) _param_0`
+---
+*/
+
+struct S
+{
+ this(inout Correct) inout
+ {
+ }
+}
+
+struct Correct {}
+struct Wrong {}
+
+S bug()
+{
+ return S(Wrong());
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17570.d b/gcc/testsuite/gdc.test/fail_compilation/fail17570.d
new file mode 100644
index 00000000000..bee61cf0b22
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17570.d
@@ -0,0 +1,13 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail17570.d(11): Error: cannot use function constraints for non-template functions. Use `static if` instead
+fail_compilation/fail17570.d(11): Error: declaration expected, not `if`
+fail_compilation/fail17570.d(14): Error: `}` expected following members in `struct` declaration at fail_compilation/fail17570.d(10)
+---
+*/
+
+struct S(T) {
+ void func() if(isIntegral!T)
+ {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail176.d b/gcc/testsuite/gdc.test/fail_compilation/fail176.d
index 908d08cd3aa..797407ed2c4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail176.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail176.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail176.d(13): Error: cannot modify immutable expression a[1]
-fail_compilation/fail176.d(16): Error: cannot modify immutable expression b[1]
-fail_compilation/fail176.d(19): Error: cannot modify const expression c[1]
+fail_compilation/fail176.d(13): Error: cannot modify `immutable` expression `a[1]`
+fail_compilation/fail176.d(16): Error: cannot modify `immutable` expression `b[1]`
+fail_compilation/fail176.d(19): Error: cannot modify `const` expression `c[1]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17602.d b/gcc/testsuite/gdc.test/fail_compilation/fail17602.d
new file mode 100644
index 00000000000..6423d1b3424
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17602.d
@@ -0,0 +1,18 @@
+/*
+EXTRA_FILES: imports/imp17602.d
+TEST_OUTPUT:
+---
+fail_compilation/fail17602.d(17): Error: cannot implicitly convert expression `Status.on` of type `imports.imp17602.Status` to `fail17602.Status`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=17602
+
+import imports.imp17602;
+
+enum Status { off }
+
+void main()
+{
+ Status status = imports.imp17602.Status.on;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17612.d b/gcc/testsuite/gdc.test/fail_compilation/fail17612.d
index 4ae1e0ad616..d39dd51cb6e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17612.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17612.d
@@ -1,7 +1,7 @@
/* TEST_OUTPUT:
---
fail_compilation/fail17612.d(14): Error: undefined identifier `string`
-fail_compilation/fail17612.d(17): Error: class object.TypeInfo missing or corrupt object.d
+fail_compilation/fail17612.d(17): Error: class `object.TypeInfo` missing or corrupt object.d
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17625.d b/gcc/testsuite/gdc.test/fail_compilation/fail17625.d
new file mode 100644
index 00000000000..bb69462f485
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17625.d
@@ -0,0 +1,17 @@
+/*
+EXTRA_FILES: imports/a17625.d imports/b17625.d
+TEST_OUTPUT:
+---
+fail_compilation/fail17625.d(16): Error: undefined identifier `boo`
+---
+*/
+
+module fail17625;
+
+import imports.a17625;
+import imports.b17625;
+
+void main()
+{
+ boo();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17646.d b/gcc/testsuite/gdc.test/fail_compilation/fail17646.d
index 416b0d4afd6..3571e38b29e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17646.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17646.d
@@ -1,12 +1,12 @@
/*
REQUIRED_ARGS: -o-
-PERMUTE_ARGS:
+EXTRA_FILES: imports/fail17646.d
TEST_OUTPUT:
---
fail_compilation/imports/fail17646.d(10): Error: found `}` instead of statement
-fail_compilation/imports/fail17646.d(7): Error: function imports.fail17646.allTestData!"".allTestData has no return statement, but is expected to return a value of type const(TestData)[]
-fail_compilation/fail17646.d(16): Error: template instance imports.fail17646.allTestData!"" error instantiating
-fail_compilation/fail17646.d(19): instantiated from here: runTests!""
+fail_compilation/imports/fail17646.d(7): Error: function `imports.fail17646.allTestData!"".allTestData` has no `return` statement, but is expected to return a value of type `const(TestData)[]`
+fail_compilation/fail17646.d(16): Error: template instance `imports.fail17646.allTestData!""` error instantiating
+fail_compilation/fail17646.d(19): instantiated from here: `runTests!""`
---
*/
int runTests(Modules...)()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail177.d b/gcc/testsuite/gdc.test/fail_compilation/fail177.d
index 1e1207732a0..49edf4da40c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail177.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail177.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail177.d(22): Error: cannot modify immutable expression j
-fail_compilation/fail177.d(24): Error: cannot modify const expression i
-fail_compilation/fail177.d(26): Error: cannot modify const expression s1.x
-fail_compilation/fail177.d(27): Error: cannot modify const expression *s1.p
-fail_compilation/fail177.d(29): Error: cannot modify const expression s2.x
-fail_compilation/fail177.d(30): Error: cannot modify const expression *s2.p
+fail_compilation/fail177.d(22): Error: cannot modify `immutable` expression `j`
+fail_compilation/fail177.d(24): Error: cannot modify `const` expression `i`
+fail_compilation/fail177.d(26): Error: cannot modify `const` expression `s1.x`
+fail_compilation/fail177.d(27): Error: cannot modify `const` expression `*s1.p`
+fail_compilation/fail177.d(29): Error: cannot modify `const` expression `s2.x`
+fail_compilation/fail177.d(30): Error: cannot modify `const` expression `*s2.p`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17722a.d b/gcc/testsuite/gdc.test/fail_compilation/fail17722a.d
index b6ede294a03..7e0b9114dee 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17722a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17722a.d
@@ -1,6 +1,6 @@
/* TEST_OUTPUT:
---
-fail_compilation/fail17722a.d(12): Error: static assert `__traits(compiles, a1 && a2)` is false
+fail_compilation/fail17722a.d(12): Error: static assert: `__traits(compiles, a1 && a2)` is false
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17722b.d b/gcc/testsuite/gdc.test/fail_compilation/fail17722b.d
index 848db15f3a1..171c49fc500 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail17722b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17722b.d
@@ -1,6 +1,6 @@
/* TEST_OUTPUT:
---
-fail_compilation/fail17722b.d(12): Error: static assert `__traits(compiles, a1 || a2)` is false
+fail_compilation/fail17722b.d(12): Error: static assert: `__traits(compiles, a1 || a2)` is false
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17842.d b/gcc/testsuite/gdc.test/fail_compilation/fail17842.d
new file mode 100644
index 00000000000..ef66858bc0d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17842.d
@@ -0,0 +1,29 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ * TEST_OUTPUT:
+---
+fail_compilation/fail17842.d(14): Error: scope variable `p` assigned to non-scope `*q`
+fail_compilation/fail17842.d(23): Error: scope variable `obj` may not be copied into allocated memory
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=17842
+
+void* testp(scope void* p) @safe
+{
+ scope void** q;
+ *q = p; // error
+ void** t;
+ *t = *q;
+ return *t;
+}
+
+Object testobj(scope Object obj) @safe
+{
+ scope Object[] arr;
+ arr ~= obj; // error
+ Object[] array;
+ array ~= arr;
+ return array[0];
+}
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail179.d b/gcc/testsuite/gdc.test/fail_compilation/fail179.d
index bd0e1557246..0c9c2489ab4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail179.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail179.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail179.d(11): Error: variable fail179.main.px cannot be final, perhaps you meant const?
+fail_compilation/fail179.d(11): Error: variable `fail179.main.px` cannot be `final`, perhaps you meant `const`?
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17927.d b/gcc/testsuite/gdc.test/fail_compilation/fail17927.d
new file mode 100644
index 00000000000..348d473ec27
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17927.d
@@ -0,0 +1,24 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ * TEST_OUTPUT:
+---
+fail_compilation/fail17927.d(13): Error: scope variable `this` may not be returned
+fail_compilation/fail17927.d(21): Error: scope variable `ptr` may not be returned
+fail_compilation/fail17927.d(23): Error: scope variable `ptr` may not be returned
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=17927
+
+struct String {
+ const(char)* mem1() const scope @safe { return ptr; }
+
+ inout(char)* mem2() inout scope @safe { return ptr; } // no error because `ref inout` implies `return`
+
+ char* ptr;
+}
+
+
+const(char)* foo1(scope const(char)* ptr) @safe { return ptr; }
+
+inout(char)* foo2(scope inout(char)* ptr) @safe { return ptr; }
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17955.d b/gcc/testsuite/gdc.test/fail_compilation/fail17955.d
new file mode 100644
index 00000000000..f33149ea94d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17955.d
@@ -0,0 +1,102 @@
+// https://issues.dlang.org/show_bug.cgi?id=17955
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail17955.d(81): Error: cannot create instance of abstract class `SimpleTimeZone`
+fail_compilation/fail17955.d(81): function `bool hasDST()` is not implemented
+fail_compilation/fail17955.d(93): Error: template instance `fail17955.SimpleTimeZone.fromISOExtString!dstring` error instantiating
+fail_compilation/fail17955.d(25): instantiated from here: `fromISOExtString!string`
+fail_compilation/fail17955.d(56): instantiated from here: `isISOExtStringSerializable!(SysTime)`
+fail_compilation/fail17955.d(49): instantiated from here: `toRedis!(SysTime)`
+fail_compilation/fail17955.d(40): ... (2 instantiations, -v to show) ...
+fail_compilation/fail17955.d(32): instantiated from here: `indicesOf!(isRedisType, resetCodeExpireTime)`
+fail_compilation/fail17955.d(67): instantiated from here: `RedisStripped!(User, true)`
+fail_compilation/fail17955.d(93): Error: need `this` for `fromISOExtString` of type `pure nothrow @nogc @safe immutable(SimpleTimeZone)(dstring _param_0)`
+fail_compilation/fail17955.d(95): Error: undefined identifier `DateTimeException`
+fail_compilation/fail17955.d(25): Error: variable `fail17955.isISOExtStringSerializable!(SysTime).isISOExtStringSerializable` type `void` is inferred from initializer `fromISOExtString("")`, and variables cannot be of type `void`
+fail_compilation/fail17955.d(54): Error: function `fail17955.toRedis!(SysTime).toRedis` has no `return` statement, but is expected to return a value of type `string`
+---
+*/
+
+alias Alias(alias a) = a;
+
+template isISOExtStringSerializable(T)
+{
+ enum isISOExtStringSerializable = T.fromISOExtString("");
+}
+
+template RedisObjectCollection(){}
+
+struct RedisStripped(T, bool strip_id = true)
+{
+ alias unstrippedMemberIndices = indicesOf!(Select!(strip_id,
+ isRedisTypeAndNotID, isRedisType), T.tupleof);
+}
+
+template indicesOf(alias PRED, T...)
+{
+ template impl(size_t i)
+ {
+ static if (PRED!T)
+ impl TypeTuple;
+ }
+
+ alias indicesOf = impl!0;
+}
+
+template isRedisType(alias F)
+{
+ enum isRedisType = toRedis!(typeof(F));
+}
+
+template isRedisTypeAndNotID(){}
+
+string toRedis(T)()
+{
+ static if (isISOExtStringSerializable!T)
+ return;
+}
+
+struct User
+{
+ SysTime resetCodeExpireTime;
+}
+
+class RedisUserManController
+{
+ RedisObjectCollection!(RedisStripped!User) m_users;
+}
+
+class TimeZone
+{
+ abstract bool hasDST();
+}
+
+class SimpleTimeZone : TimeZone
+{
+ unittest {}
+
+ immutable(SimpleTimeZone) fromISOExtString(S)(S)
+ {
+ new SimpleTimeZone;
+ }
+}
+
+struct SysTime
+{
+
+ static fromISOExtString(S)(S)
+ {
+ dstring zoneStr;
+
+ try
+ SimpleTimeZone.fromISOExtString(zoneStr);
+
+ catch (DateTimeException e) {}
+ }
+}
+
+template Select(bool condition, T...)
+{
+ alias Select = Alias!(T[condition]);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17969.d b/gcc/testsuite/gdc.test/fail_compilation/fail17969.d
new file mode 100644
index 00000000000..57fabbb7ec8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17969.d
@@ -0,0 +1,18 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/fail17969.d(9): Error: no property `sum` for type `fail17969.__lambda6!(int[]).__lambda6.MapResult2!((b) => b)`
+---
+ * https://issues.dlang.org/show_bug.cgi?id=17969
+ */
+
+
+alias fun = a => MapResult2!(b => b).sum;
+
+int[] e;
+static assert(!is(typeof(fun(e)) == void));
+void foo() { fun(e); }
+
+struct MapResult2(alias fun)
+{
+ int[] _input;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17976.d b/gcc/testsuite/gdc.test/fail_compilation/fail17976.d
new file mode 100644
index 00000000000..4ecc092eae6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail17976.d
@@ -0,0 +1,18 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail17976.d(11): Error: constructor `fail17976.S.this` parameter `this.a` is already defined
+fail_compilation/fail17976.d(11): Error: constructor `fail17976.S.this` parameter `this.a` is already defined
+---
+*/
+
+struct S
+{
+ this(string a, string a, string a)
+ {
+ }
+}
+
+void main()
+{
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail180.d b/gcc/testsuite/gdc.test/fail_compilation/fail180.d
index 64ba3ef4271..ef4ffaa6088 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail180.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail180.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail180.d(23): Error: cannot modify this.x in const function
-fail_compilation/fail180.d(24): Error: cannot modify this.x in const function
-fail_compilation/fail180.d(38): Error: cannot modify this.x in const function
-fail_compilation/fail180.d(39): Error: cannot modify this.x in const function
-fail_compilation/fail180.d(50): Error: variable fail180.main.t cannot be final, perhaps you meant const?
-fail_compilation/fail180.d(62): Error: variable fail180.test.d cannot be final, perhaps you meant const?
+fail_compilation/fail180.d(23): Error: cannot modify `this.x` in `const` function
+fail_compilation/fail180.d(24): Error: cannot modify `this.x` in `const` function
+fail_compilation/fail180.d(38): Error: cannot modify `this.x` in `const` function
+fail_compilation/fail180.d(39): Error: cannot modify `this.x` in `const` function
+fail_compilation/fail180.d(50): Error: variable `fail180.main.t` cannot be `final`, perhaps you meant `const`?
+fail_compilation/fail180.d(62): Error: variable `fail180.test.d` cannot be `final`, perhaps you meant `const`?
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18057.d b/gcc/testsuite/gdc.test/fail_compilation/fail18057.d
index 5e2bab7f796..19e8eb47569 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail18057.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18057.d
@@ -1,8 +1,8 @@
/**
TEST_OUTPUT:
---
-fail_compilation/fail18057.d(16): Error: template instance RBNode!int `RBNode` is not a template declaration, it is a struct
-fail_compilation/fail18057.d(13): Error: variable fail18057.RBNode.copy recursive initialization of field
+fail_compilation/fail18057.d(16): Error: template instance `RBNode!int` `RBNode` is not a template declaration, it is a struct
+fail_compilation/fail18057.d(13): Error: variable `fail18057.RBNode.copy` recursive initialization of field
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18093.d b/gcc/testsuite/gdc.test/fail_compilation/fail18093.d
new file mode 100644
index 00000000000..4eb3663afc6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18093.d
@@ -0,0 +1,27 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/fail18093.d(19): Error: function `void fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen).visit()` does not override any function, did you mean to override `extern (C++) void fail18093.ParseTimeVisitor!(ASTCodegen).ParseTimeVisitor.visit()`?
+fail_compilation/fail18093.d(24): Error: mixin `fail18093.GenericTransitiveVisitor!(ASTCodegen).GenericTransitiveVisitor.ParseVisitMethods!(ASTCodegen)` error instantiating
+fail_compilation/fail18093.d(27): Error: template instance `fail18093.GenericTransitiveVisitor!(ASTCodegen)` error instantiating
+---
+ * https://issues.dlang.org/show_bug.cgi?id=18093
+ */
+
+
+struct ASTCodegen {}
+
+extern (C++) class ParseTimeVisitor(AST)
+{
+ void visit() {}
+}
+template ParseVisitMethods(AST)
+{
+ override void visit() {}
+}
+
+class GenericTransitiveVisitor(AST) : ParseTimeVisitor!AST
+{
+ mixin ParseVisitMethods!AST;
+}
+
+alias SemanticTimeTransitiveVisitor = GenericTransitiveVisitor!ASTCodegen;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18228.d b/gcc/testsuite/gdc.test/fail_compilation/fail18228.d
new file mode 100644
index 00000000000..983719a728b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18228.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail18228.d(12): Error: Using `this` as a type is obsolete. Use `typeof(this)` instead
+fail_compilation/fail18228.d(13): Error: Using `this` as a type is obsolete. Use `typeof(this)` instead
+fail_compilation/fail18228.d(14): Error: Using `super` as a type is obsolete. Use `typeof(super)` instead
+---
+*/
+
+class C
+{
+ this(this a) {}
+ this(int a, this b) {}
+ this(super a) {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18236.d b/gcc/testsuite/gdc.test/fail_compilation/fail18236.d
new file mode 100644
index 00000000000..5a696f38a4f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18236.d
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail18236.d(20): Error: cannot cast expression `V(12)` of type `V` to `int`
+---
+*/
+
+struct V
+{
+ int a;
+}
+
+struct S
+{
+ enum A = V(12);
+}
+
+void main()
+{
+ int b = cast(int)S.A;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18243.d b/gcc/testsuite/gdc.test/fail_compilation/fail18243.d
new file mode 100644
index 00000000000..f31319b0d35
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18243.d
@@ -0,0 +1,16 @@
+/*
+EXTRA_FILES: imports/a18243.d
+TEST_OUTPUT:
+---
+fail_compilation/fail18243.d(15): Error: none of the overloads of `isNaN` are callable using argument types `!()(float)`
+---
+*/
+
+module fail18243;
+
+import imports.a18243;
+
+void main()
+{
+ bool b = isNaN(float.nan);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail183.d b/gcc/testsuite/gdc.test/fail_compilation/fail183.d
index c43d377e9f9..362213c1f64 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail183.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail183.d
@@ -1,11 +1,25 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail183.d(10): Error: redundant attribute `const`
-fail_compilation/fail183.d(10): Error: redundant attribute `scope`
-fail_compilation/fail183.d(11): Error: redundant attribute `in`
+fail_compilation/fail183.d(17): Error: attribute `const` is redundant with previously-applied `in`
+fail_compilation/fail183.d(18): Error: attribute `scope` cannot be applied with `in`, use `-preview=in` instead
+fail_compilation/fail183.d(19): Error: attribute `const` is redundant with previously-applied `in`
+fail_compilation/fail183.d(19): Error: attribute `scope` cannot be applied with `in`, use `-preview=in` instead
+fail_compilation/fail183.d(20): Error: attribute `scope` cannot be applied with `in`, use `-preview=in` instead
+fail_compilation/fail183.d(20): Error: attribute `const` is redundant with previously-applied `in`
+fail_compilation/fail183.d(22): Error: attribute `in` cannot be added after `const`: remove `const`
+fail_compilation/fail183.d(23): Error: attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`
+fail_compilation/fail183.d(24): Error: attribute `in` cannot be added after `const`: remove `const`
+fail_compilation/fail183.d(25): Error: attribute `in` cannot be added after `const`: remove `const`
---
*/
-void f(in final const scope int x) {}
-void g(final const scope in int x) {}
+void f1(in const int x) {}
+void f2(in scope int x) {}
+void f3(in const scope int x) {}
+void f4(in scope const int x) {}
+
+void f5(const in int x) {}
+void f6(scope in int x) {}
+void f7(const scope in int x) {}
+void f8(scope const in int x) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18417.d b/gcc/testsuite/gdc.test/fail_compilation/fail18417.d
new file mode 100644
index 00000000000..b32a99b49b6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18417.d
@@ -0,0 +1,13 @@
+// REQUIRED_ARGS : -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail18417.d(11): Deprecation: `const` postblit is deprecated. Please use an unqualified postblit.
+fail_compilation/fail18417.d(12): Deprecation: `immutable` postblit is deprecated. Please use an unqualified postblit.
+fail_compilation/fail18417.d(13): Deprecation: `shared` postblit is deprecated. Please use an unqualified postblit.
+---
+*/
+
+struct A { this(this) const {} }
+struct B { this(this) immutable {} }
+struct C { this(this) shared {} }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail185.d b/gcc/testsuite/gdc.test/fail_compilation/fail185.d
index 7197531d8e5..8d3ffc9db84 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail185.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail185.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail185.d(10): Error: static assert "An error message
+fail_compilation/fail185.d(10): Error: static assert: "An error message
that spans multiple lines, and also contains such characters as a tab,
\ and "."
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18620.d b/gcc/testsuite/gdc.test/fail_compilation/fail18620.d
new file mode 100644
index 00000000000..ce202b88464
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18620.d
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail18620.d(14): Error: `strlen` cannot be interpreted at compile time, because it has no available source code
+fail_compilation/fail18620.d(19): compile time context created here
+fail_compilation/fail18620.d(14): Error: `strlen` cannot be interpreted at compile time, because it has no available source code
+fail_compilation/fail18620.d(20): compile time context created here
+---
+*/
+class A{
+ this(const(char)* s)
+ {
+ import core.stdc.string;
+ auto a=strlen(s);
+ }
+}
+
+void main(){
+ static a = new A("a");
+ __gshared b = new A("b");
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail187.d b/gcc/testsuite/gdc.test/fail_compilation/fail187.d
index b985493d5fd..b23a95d4dfd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail187.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail187.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail187.d(16): Error: catch at fail_compilation/fail187.d(20) hides catch at fail_compilation/fail187.d(24)
+fail_compilation/fail187.d(16): Error: `catch` at fail_compilation/fail187.d(20) hides `catch` at fail_compilation/fail187.d(24)
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18719.d b/gcc/testsuite/gdc.test/fail_compilation/fail18719.d
index 7d993d13485..7ed513a3dfc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail18719.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18719.d
@@ -1,10 +1,9 @@
// https://issues.dlang.org/show_bug.cgi?id=18719
-// REQUIRED_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail18719.d(30): Error: immutable field `x` initialized multiple times
+fail_compilation/fail18719.d(29): Error: immutable field `x` initialized multiple times
Previous initialization is here.
---
*/
@@ -15,8 +14,8 @@ struct S
this(int y) immutable
{
x = y;
- import std.stdio;
- writeln("Ctor called with ", y);
+ import core.stdc.stdio;
+ printf("Ctor called with %d\n", y);
}
void opAssign(int) immutable;
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail188.d b/gcc/testsuite/gdc.test/fail_compilation/fail188.d
index cd201d025bc..6809e10087b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail188.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail188.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail188.d(15): Error: function fail188.Derived.foo cannot override final function fail188.Base.foo
+fail_compilation/fail188.d(15): Error: function `fail188.Derived.foo` cannot override `final` function `fail188.Base.foo`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18892.d b/gcc/testsuite/gdc.test/fail_compilation/fail18892.d
new file mode 100644
index 00000000000..531d1ed17ef
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18892.d
@@ -0,0 +1,22 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail18892.d(20): Error: no property `foo` for type `fail18892.MT`
+fail_compilation/fail18892.d(21): Error: no property `foo` for type `fail18892.MT`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18892
+
+struct MT
+{
+ int _payload;
+ alias _payload this;
+}
+
+void main()
+{
+ MT a;
+ a.foo = 3;
+ MT.foo = 3;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18938.d b/gcc/testsuite/gdc.test/fail_compilation/fail18938.d
new file mode 100644
index 00000000000..f7ece6daf80
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18938.d
@@ -0,0 +1,11 @@
+// REQUIRED_ARGS: -c -Ifail_compilation/imports/
+// EXTRA_SOURCES: imports/test18938a/cache.d imports/test18938a/file.d
+// EXTRA_FILES: imports/test18938b/file.d
+/*
+TEST_OUTPUT:
+---
+fail_compilation/imports/test18938b/file.d(20): Error: undefined identifier `No`
+---
+*/
+
+void main() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18970.d b/gcc/testsuite/gdc.test/fail_compilation/fail18970.d
index 0ec53c897d6..09732171b86 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail18970.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18970.d
@@ -1,8 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail18970.d(22): Error: no property `y` for type `S`
-fail_compilation/fail18970.d(29): Error: no property `yyy` for type `S2`
+fail_compilation/fail18970.d(24): Error: no property `y` for type `fail18970.S`
+fail_compilation/fail18970.d(24): potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message
+fail_compilation/fail18970.d(31): Error: no property `yyy` for type `fail18970.S2`
+fail_compilation/fail18970.d(31): potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18979.d b/gcc/testsuite/gdc.test/fail_compilation/fail18979.d
new file mode 100644
index 00000000000..97565705221
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18979.d
@@ -0,0 +1,14 @@
+// EXTRA_FILES: imports/imp18979.d
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail18979.d(13): Error: no property `__ctor` for type `imports.imp18979.Foo`
+----
+*/
+
+import imports.imp18979;
+
+void main()
+{
+ auto f = Foo(42);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18985.d b/gcc/testsuite/gdc.test/fail_compilation/fail18985.d
new file mode 100644
index 00000000000..830a6792ad2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18985.d
@@ -0,0 +1,18 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail18985.d(16): Error: `foo` is not a scalar, it is a `object.Object`
+fail_compilation/fail18985.d(17): Error: `bar` is not a scalar, it is a `shared(Object)`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18985
+
+Object foo;
+shared Object bar;
+
+void main()
+{
+ foo += 1;
+ bar += 1;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18994.d b/gcc/testsuite/gdc.test/fail_compilation/fail18994.d
new file mode 100644
index 00000000000..14935a72671
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18994.d
@@ -0,0 +1,20 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail18994.d(19): Error: struct `fail18994.Type1` is not copyable because it has a disabled postblit
+---
+*/
+struct Type2
+{
+ int opApply(int delegate(ref Type1)) { return 0; }
+}
+
+struct Type1
+{
+ @disable this(this);
+}
+
+void test()
+{
+ foreach(b; Type2()) {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail190.d b/gcc/testsuite/gdc.test/fail_compilation/fail190.d
index 6f4046a4eec..59e11d71dac 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail190.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail190.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail190.d(9): Error: can't have pointer to (int, int, int)
-fail_compilation/fail190.d(16): Error: template instance fail190.f!(int, int, int) error instantiating
+fail_compilation/fail190.d(9): Error: cannot have pointer to `(int, int, int)`
+fail_compilation/fail190.d(16): Error: template instance `fail190.f!(int, int, int)` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail1900.d b/gcc/testsuite/gdc.test/fail_compilation/fail1900.d
index cabfbdff8dc..fd0d6ac887f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail1900.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail1900.d
@@ -1,10 +1,11 @@
/*
+EXTRA_FILES: imports/fail1900a.d imports/fail1900b.d
TEST_OUTPUT:
---
-fail_compilation/fail1900.d(26): Error: template fail1900.Mix1a!().Foo matches more than one template declaration:
-fail_compilation/fail1900.d(13): Foo(ubyte x)
+fail_compilation/fail1900.d(27): Error: template `fail1900.Mix1a!().Foo` matches more than one template declaration:
+fail_compilation/fail1900.d(14): `Foo(ubyte x)`
and
-fail_compilation/fail1900.d(14): Foo(byte x)
+fail_compilation/fail1900.d(15): `Foo(byte x)`
---
*/
@@ -29,7 +30,7 @@ void test1900a()
/*
TEST_OUTPUT:
---
-fail_compilation/fail1900.d(41): Error: imports.fail1900b.Bar(short n) at fail_compilation/imports/fail1900b.d(2) conflicts with imports.fail1900a.Bar(int n) at fail_compilation/imports/fail1900a.d(2)
+fail_compilation/fail1900.d(42): Error: template `imports.fail1900b.Bar(short n)` at fail_compilation/imports/fail1900b.d(2) conflicts with template `imports.fail1900a.Bar(int n)` at fail_compilation/imports/fail1900a.d(2)
---
*/
@@ -44,7 +45,7 @@ void test1900b()
/*
TEST_OUTPUT:
---
-fail_compilation/fail1900.d(65): Error: fail1900.Mix2b!().Baz(int x) at fail_compilation/fail1900.d(57) conflicts with fail1900.Mix2a!().Baz(byte x) at fail_compilation/fail1900.d(53)
+fail_compilation/fail1900.d(66): Error: template `fail1900.Mix2b!().Baz(int x)` at fail_compilation/fail1900.d(58) conflicts with template `fail1900.Mix2a!().Baz(byte x)` at fail_compilation/fail1900.d(54)
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19038.d b/gcc/testsuite/gdc.test/fail_compilation/fail19038.d
new file mode 100644
index 00000000000..ef1a8b767e5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19038.d
@@ -0,0 +1,29 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/fail19038.d(21): Error: cannot implicitly convert expression `a` of type `string[][]` to `const(string)[][]`
+fail_compilation/fail19038.d(23): Error: cannot modify `const` expression `c[0]`
+---
+ * Credit: yshui
+ * https://github.com/dlang/dmd/pull/8413#issuecomment-401104961
+ * https://issues.dlang.org/show_bug.cgi?id=19038
+ */
+
+
+void test()
+{
+ /* string[][] is not implicitly converible to const(string)[][],
+ * and there is good reason why:
+ *
+ * https://stackoverflow.com/questions/5055655/double-pointer-const-correctness-warnings-in-c
+ */
+
+ string[][] a = [["Lord"]];
+ const(string)[][] b = a; // assume this works (and it should not)
+ const(string)[] c = ["Sauron"];
+ c[0] = "Mordor"; // invalid, because c[0] is const(string)
+
+ b[0] = c; // valid, b[0] is const(string)[]
+ // But now, a[0] has become c
+ a[0][0] = "Nazgul"; // valid, because a[0][0] is string
+ // But this also changes c[0], which shouldn't be possible
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19076.d b/gcc/testsuite/gdc.test/fail_compilation/fail19076.d
new file mode 100644
index 00000000000..9bfc0a564eb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19076.d
@@ -0,0 +1,11 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19076.d(11): Error: no property `V` for type `fail19076.I`
+fail_compilation/fail19076.d(11): Error: `(I).V` cannot be resolved
+---
+*/
+
+interface P { }
+interface I : P { }
+auto F = __traits(getVirtualFunctions, I, "V");
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19098.d b/gcc/testsuite/gdc.test/fail_compilation/fail19098.d
new file mode 100644
index 00000000000..c4f879f342d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19098.d
@@ -0,0 +1,19 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19098.d(18): Error: cannot modify struct instance `a` of type `A` because it contains `const` or `immutable` members
+---
+*/
+
+struct A
+{
+ const int a;
+ this(int) {}
+}
+
+void main()
+{
+ A a = A(2);
+ A b = A(3);
+ a = b;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19099.d b/gcc/testsuite/gdc.test/fail_compilation/fail19099.d
new file mode 100644
index 00000000000..6fbc61476d3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19099.d
@@ -0,0 +1,27 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19099.d(26): Error: cannot modify struct instance `a` of type `A` because it contains `const` or `immutable` members
+---
+*/
+
+struct B
+{
+ this(this) {}
+ ~this() {}
+ int a;
+}
+
+struct A
+{
+ B b;
+ immutable int a;
+ this(int b) { a = b;}
+}
+
+void main()
+{
+ A a = A(2);
+ A b = A(3);
+ a = b;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19103.d b/gcc/testsuite/gdc.test/fail_compilation/fail19103.d
new file mode 100644
index 00000000000..6b740ac6945
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19103.d
@@ -0,0 +1,36 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19103.d(12): Error: no property `puts` for type `fail19103.C`
+fail_compilation/fail19103.d(14): Error: no property `puts` for type `fail19103.S1`
+fail_compilation/fail19103.d(16): Error: no property `puts` for type `S2`, did you mean `core.stdc.stdio.puts`?
+---
+*/
+
+void main()
+{
+ (new C).puts("OK."); // Error: no property puts for type test.C, did you mean core.stdc.stdio.puts(T...)(T args)?
+ S1 s1;
+ s1.puts("Hey?"); // It can be compiled and runs!
+ S2 s2;
+ s2.puts("OK."); // Error: no property puts for type S2, did you mean core.stdc.stdio.puts(T...)(T args)?
+}
+
+mixin template T()
+{
+ import core.stdc.stdio;
+}
+
+class C
+{
+ mixin T;
+}
+struct S1
+{
+ mixin T;
+}
+
+struct S2
+{
+ import core.stdc.stdio;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19107.d b/gcc/testsuite/gdc.test/fail_compilation/fail19107.d
deleted file mode 100644
index c748650b2e4..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19107.d
+++ /dev/null
@@ -1,21 +0,0 @@
-// REQUIRED_ARGS:
-/*
-TEST_OUTPUT:
----
-fail_compilation/test19107.d(20): Error: template `test19107.all` cannot deduce function from argument types `!((c) => c)(string[])`, candidates are:
-fail_compilation/test19107.d(14): `test19107.all(alias pred, T)(T t) if (is(typeof(I!pred(t))))`
----
-*/
-
-// https://issues.dlang.org/show_bug.cgi?id=19107
-
-import imports.test19107b;
-
-void all(alias pred, T)(T t)
- if (is(typeof(I!pred(t))))
-{ }
-
-void main(string[] args)
-{
- args.all!(c => c);
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19181.d b/gcc/testsuite/gdc.test/fail_compilation/fail19181.d
new file mode 100644
index 00000000000..873e2929efe
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19181.d
@@ -0,0 +1,16 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19181.d(15): Error: undefined identifier `LanguageError`
+---
+*/
+struct S
+{
+ void opDispatch(string name, T)(T arg) { }
+}
+
+void main()
+{
+ S s;
+ s.foo(LanguageError);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail192.d b/gcc/testsuite/gdc.test/fail_compilation/fail192.d
index 3c485e6948e..96232a5758b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail192.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail192.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail192.d(15): Error: outer function context of fail192.foo is needed to 'new' nested class fail192.foo.DummyClass
-fail_compilation/fail192.d(26): Error: template instance fail192.X!(DummyClass) error instantiating
+fail_compilation/fail192.d(15): Error: outer function context of `fail192.foo` is needed to `new` nested class `fail192.foo.DummyClass`
+fail_compilation/fail192.d(26): Error: template instance `fail192.X!(DummyClass)` error instantiating
---
*/
-// 1336 Internal error when trying to construct a class declared within a unittest from a templated class.
-
+// https://issues.dlang.org/show_bug.cgi?id=1336
+// Internal error when trying to construct a class declared within a unittest from a templated class.
class X(T)
{
void bar()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19202.d b/gcc/testsuite/gdc.test/fail_compilation/fail19202.d
new file mode 100644
index 00000000000..f19bc181f41
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19202.d
@@ -0,0 +1,17 @@
+// REQUIRED_ARGS: -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19202.d(11): Deprecation: variable `fail19202.X!().X` is deprecated
+---
+*/
+
+void main()
+{
+ auto b = X!();
+}
+
+template X()
+{
+ deprecated enum X = true;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19209.d b/gcc/testsuite/gdc.test/fail_compilation/fail19209.d
new file mode 100644
index 00000000000..ceede5e0eed
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19209.d
@@ -0,0 +1,17 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19209.d(16): Error: function `fail19209.Spammer.method()` does not override any function, did you mean to override variable `fail19209.Spam.method`?
+fail_compilation/fail19209.d(16): Functions are the only declarations that may be overriden
+---
+*/
+
+class Spam
+{
+ int method;
+}
+
+class Spammer : Spam
+{
+ override method() {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail193.d b/gcc/testsuite/gdc.test/fail_compilation/fail193.d
index c3093c74bac..9fcae5b9cb9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail193.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail193.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail193.d(14): Error: cannot infer type from overloaded function symbol & foo
+fail_compilation/fail193.d(14): Error: cannot infer type from overloaded function symbol `& foo`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19319a.d b/gcc/testsuite/gdc.test/fail_compilation/fail19319a.d
new file mode 100644
index 00000000000..f3efe1f94f9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19319a.d
@@ -0,0 +1,17 @@
+/*
+DFLAGS:
+REQUIRED_ARGS: -conf= -Ifail_compilation/extra-files/minimal
+TEST_OUTPUT:
+---
+fail_compilation/fail19319a.d(16): Error: `7 ^^ g19319` requires `std.math` for `^^` operators
+fail_compilation/fail19319a.d(17): Error: `g19319 ^^ 7` requires `std.math` for `^^` operators
+---
+*/
+
+__gshared int g19319 = 0;
+
+static assert(!__traits(compiles, 7 ^^ g19319));
+static assert(!__traits(compiles, g19319 ^^= 7));
+
+__gshared int e19319 = 7 ^^ g19319;
+__gshared int a19319 = g19319 ^^= 7;;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19319b.d b/gcc/testsuite/gdc.test/fail_compilation/fail19319b.d
new file mode 100644
index 00000000000..933dc16e01a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19319b.d
@@ -0,0 +1,18 @@
+/*
+DFLAGS:
+REQUIRED_ARGS: -conf= -Ifail_compilation/extra-files/minimal
+TEST_OUTPUT:
+---
+fail_compilation/fail19319b.d(16): Error: `7 ^^ x` requires `std.math` for `^^` operators
+fail_compilation/fail19319b.d(17): Error: `x ^^ 7` requires `std.math` for `^^` operators
+---
+*/
+
+void test19319(int x)
+{
+ static assert(!__traits(compiles, 7 ^^ x));
+ static assert(!__traits(compiles, x ^^= 7));
+
+ int i = 7 ^^ x;
+ x ^^= 7;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail194.d b/gcc/testsuite/gdc.test/fail_compilation/fail194.d
index 2ce4ab86881..f2270f11012 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail194.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail194.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail194.d(18): Error: function & foo is overloaded
+fail_compilation/fail194.d(18): Error: function `& foo` is overloaded
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19441.d b/gcc/testsuite/gdc.test/fail_compilation/fail19441.d
new file mode 100644
index 00000000000..f1f17699c90
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19441.d
@@ -0,0 +1,49 @@
+// REQUIRED_ARGS: -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19441.d(44): Deprecation: Cannot use `alias this` to partially initialize variable `wrap[0]` of type `Wrap10595`. Use `wrap[0].i`
+---
+*/
+
+struct S10595
+{
+ bool b = true;
+
+ bool test()
+ {
+ if (!b) // note: must be a check, not 'return b;'
+ return false;
+
+ return true;
+ }
+}
+
+struct Wrap10595
+{
+ int i;
+ alias i this;
+ S10595 s;
+}
+
+void main()
+{
+ {
+ Wrap10595[int] wrap;
+
+ wrap[0] = Wrap10595();
+ wrap[0].i = 0;
+
+ assert(wrap[0].s.test()); // ok
+ }
+
+ {
+ Wrap10595[int] wrap;
+
+ wrap[0] = Wrap10595();
+ wrap[0] = 0; // note: using 'alias this' to assign
+
+ assert(wrap[0].s.test()); // failure
+ }
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19447.d b/gcc/testsuite/gdc.test/fail_compilation/fail19447.d
new file mode 100644
index 00000000000..23f4921fc6a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19447.d
@@ -0,0 +1,19 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/fail19447.d(110): Error: static variable `mh` cannot be read at compile time
+fail_compilation/fail19447.d(110): called from here: `g19447(mh)`
+---
+ */
+
+#line 100
+
+int [2] mh = [1, 2];
+int g19447(ref int[2] a)
+{
+ int[2] b=2;
+ a=b;
+ assert(a[0]==2);
+ return 1;
+}
+
+immutable int i = g19447(mh);
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail195.d b/gcc/testsuite/gdc.test/fail_compilation/fail195.d
index 57a6bdc2918..5f067a9e932 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail195.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail195.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail195.d(22): Error: struct Foo does not overload ()
+fail_compilation/fail195.d(22): Error: struct `Foo` does not overload ()
---
*/
-// 1384 Compiler segfaults when using struct variable like a function with no opCall member.
-
+// https://issues.dlang.org/show_bug.cgi?id=1384
+// Compiler segfaults when using struct variable like a function with no opCall member.
struct Foo
{
union
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19609.d b/gcc/testsuite/gdc.test/fail_compilation/fail19609.d
index 26ef57693b7..c68f1994163 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19609.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19609.d
@@ -1,15 +1,16 @@
// https://issues.dlang.org/show_bug.cgi?id=19609
/*
+EXTRA_FILES: imports/fail19609a.d imports/fail19609b.d imports/fail19609c.d imports/fail19609d.d
TEST_OUTPUT
---
fail_compilation/imports/fail19609a.d(1): Error: `string` expected for deprecation message, not `([""])` of type `string[]`
-fail_compilation/fail19609.d(15): Deprecation: module `imports.fail19609a` is deprecated
+fail_compilation/fail19609.d(16): Deprecation: module `imports.fail19609a` is deprecated
fail_compilation/imports/fail19609b.d(1): Error: `string` expected for deprecation message, not `([1])` of type `int[]`
-fail_compilation/fail19609.d(16): Deprecation: module `imports.fail19609b` is deprecated
+fail_compilation/fail19609.d(17): Deprecation: module `imports.fail19609b` is deprecated
fail_compilation/imports/fail19609c.d(1): Error: `string` expected for deprecation message, not `(123.4F)` of type `float`
-fail_compilation/fail19609.d(17): Deprecation: module `imports.fail19609c` is deprecated
+fail_compilation/fail19609.d(18): Deprecation: module `imports.fail19609c` is deprecated
fail_compilation/imports/fail19609d.d(1): Error: undefined identifier `msg`
-fail_compilation/fail19609.d(19): Deprecation: module `imports.fail19609d` is deprecated
+fail_compilation/fail19609.d(20): Deprecation: module `imports.fail19609d` is deprecated
---
*/
import imports.fail19609a;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19687.d b/gcc/testsuite/gdc.test/fail_compilation/fail19687.d
new file mode 100644
index 00000000000..7d1c6e516e1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19687.d
@@ -0,0 +1,18 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19687.d(17): Error: no property `nonexisting` for type `string`
+---
+*/
+
+struct S
+{
+ void opDispatch(string name)() {}
+ void opDispatch(string name)(string value) {}
+}
+
+void main()
+{
+ S n;
+ n.foo = "".nonexisting();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19729.d b/gcc/testsuite/gdc.test/fail_compilation/fail19729.d
new file mode 100644
index 00000000000..5943d086872
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19729.d
@@ -0,0 +1,37 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19729.d(35): Error: `fail19729.C.__ctor` called with argument types `(string)` matches both:
+fail_compilation/fail19729.d(18): `fail19729.C.Templ!string.this(string t)`
+and:
+fail_compilation/fail19729.d(18): `fail19729.C.Templ!string.this(string t)`
+fail_compilation/fail19729.d(36): Error: `fail19729.D.__ctor` called with argument types `(string)` matches both:
+fail_compilation/fail19729.d(18): `fail19729.D.Templ!(const(char)[]).this(const(char)[] t)`
+and:
+fail_compilation/fail19729.d(18): `fail19729.D.Templ!(const(char)*).this(const(char)* t)`
+---
+*/
+module fail19729;
+
+mixin template Templ(T)
+{
+ this(T t) { }
+}
+
+class C
+{
+ mixin Templ!string;
+ mixin Templ!string;
+}
+
+class D
+{
+ mixin Templ!(const(char)*);
+ mixin Templ!(const(char)[]);
+}
+
+void main()
+{
+ new C("conflict");
+ new D("conflict");
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19744.d b/gcc/testsuite/gdc.test/fail_compilation/fail19744.d
new file mode 100644
index 00000000000..9115b4e7e7f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19744.d
@@ -0,0 +1,11 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19744.d(8): Error: Top-level function `test` has no `this` to which `return` can apply
+---
+*/
+
+int* test(return scope int* n) return
+{
+ return n;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19757_m32.d b/gcc/testsuite/gdc.test/fail_compilation/fail19757_m32.d
new file mode 100644
index 00000000000..6ddb456d564
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19757_m32.d
@@ -0,0 +1,9 @@
+// REQUIRED_ARGS: -m32
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19757_m32.d(9): Error: cannot implicitly convert expression `"oops"` of type `string` to `uint`
+---
+*/
+
+auto s = new string("oops");
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19757_m64.d b/gcc/testsuite/gdc.test/fail_compilation/fail19757_m64.d
new file mode 100644
index 00000000000..e1f9afc34c2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19757_m64.d
@@ -0,0 +1,9 @@
+// REQUIRED_ARGS: -m64
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19757_m64.d(9): Error: cannot implicitly convert expression `"oops"` of type `string` to `ulong`
+---
+*/
+
+auto s = new string("oops");
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail198.d b/gcc/testsuite/gdc.test/fail_compilation/fail198.d
index 4cb63b4870e..1a096f4c386 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail198.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail198.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail198.d(8): Error: template instance test!42 template 'test' is not defined
+fail_compilation/fail198.d(8): Error: template instance `test!42` template `test` is not defined
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19871.d b/gcc/testsuite/gdc.test/fail_compilation/fail19871.d
new file mode 100644
index 00000000000..ad458df2001
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19871.d
@@ -0,0 +1,20 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19871.d(10): Error: `struct Struct` may not define both a rvalue constructor and a copy constructor
+fail_compilation/fail19871.d(19): rvalue constructor defined here
+fail_compilation/fail19871.d(13): copy constructor defined here
+---
+*/
+
+struct Struct
+{
+ @disable this();
+ this(ref Struct other)
+ {
+ const Struct s = void;
+ this(s);
+ }
+
+ this(Struct) {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19881.d b/gcc/testsuite/gdc.test/fail_compilation/fail19881.d
new file mode 100644
index 00000000000..f4a4d760653
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19881.d
@@ -0,0 +1,15 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ * TEST_OUTPUT:
+---
+fail_compilation/fail19881.d(12): Error: address of local variable `local` assigned to return scope `input`
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=19881
+
+@safe int* test(return scope int* input) {
+ int local = 42;
+ input = &local;
+
+ return input;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19890a.d b/gcc/testsuite/gdc.test/fail_compilation/fail19890a.d
index 2120dc56997..d4da11604fa 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19890a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19890a.d
@@ -1,8 +1,8 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19890a.d(8): Error: `void[/^[0-9]+(LU)?$/]` size 1 * /^[0-9]+$/ exceeds 0x7fffffff size limit for static array
+fail_compilation/fail19890a.d(8): Error: `void[$n$$?:64=LU$]` size 1 * $n$ exceeds $?:windows+32=0x1000000|0x7fffffff$ size limit for static array
---
*/
+
void[] f = cast(void[-1]) "a";
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19890b.d b/gcc/testsuite/gdc.test/fail_compilation/fail19890b.d
index c3ee67793a3..f4a5dada2c8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19890b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19890b.d
@@ -1,8 +1,8 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19890b.d(8): Error: `void[/^[0-9]+(LU)?$/]` size 1 * /^[0-9]+$/ exceeds 0x7fffffff size limit for static array
+fail_compilation/fail19890b.d(8): Error: `void[$n$$?:64=LU$]` size 1 * $n$ exceeds $?:windows+32=0x1000000|0x7fffffff$ size limit for static array
---
*/
+
void[] f = cast(void[-2]) "a";
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19897.d b/gcc/testsuite/gdc.test/fail_compilation/fail19897.d
index 8dd4e149a87..d5e6f57d20f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19897.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19897.d
@@ -1,8 +1,7 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT
---
-fail_compilation/fail19897.d(10): Error: cannot implicitly convert expression `[]` of type `const(char[0])` to `const(char)`
+fail_compilation/fail19897.d(9): Error: cannot implicitly convert expression `[]` of type `const(char[0])` to `const(char)`
---
*/
struct S
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19898a.d b/gcc/testsuite/gdc.test/fail_compilation/fail19898a.d
index 406e468ca00..f4aa8482001 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19898a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19898a.d
@@ -1,9 +1,8 @@
/*
-PERMUTE_ARGS:
REQUIRED_ARGS: -m64
TEST_OUTPUT:
---
-fail_compilation/fail19898a.d(11): Error: incompatible types for `(__key2) < (__limit3)`: both operands are of type `__vector(int[4])`
+fail_compilation/fail19898a.d(10): Error: incompatible types for `(__key2) < (__limit3)`: both operands are of type `__vector(int[4])`
---
*/
void f (__vector(int[4]) n)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19898b.d b/gcc/testsuite/gdc.test/fail_compilation/fail19898b.d
index 0b47fb78944..82d92ebc7be 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19898b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19898b.d
@@ -1,11 +1,10 @@
/*
-PERMUTE_ARGS:
REQUIRED_ARGS: -m64
TEST_OUTPUT:
---
-fail_compilation/fail19898b.d(18): Error: cannot implicitly convert expression `m` of type `S` to `__vector(int[4])`
-fail_compilation/fail19898b.d(18): Error: incompatible types for `(__key2) != (__limit3)`: both operands are of type `__vector(int[4])`
-fail_compilation/fail19898b.d(18): Error: cannot cast expression `__key2` of type `__vector(int[4])` to `S`
+fail_compilation/fail19898b.d(17): Error: cannot implicitly convert expression `m` of type `S` to `__vector(int[4])`
+fail_compilation/fail19898b.d(17): Error: incompatible types for `(__key2) != (__limit3)`: both operands are of type `__vector(int[4])`
+fail_compilation/fail19898b.d(17): Error: cannot cast expression `__key2` of type `__vector(int[4])` to `S`
---
*/
struct S
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19911b.d b/gcc/testsuite/gdc.test/fail_compilation/fail19911b.d
index b4ad22b0896..c5d6a8ac47d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19911b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19911b.d
@@ -1,6 +1,5 @@
/*
DFLAGS:
-REQUIRED_ARGS:
EXTRA_SOURCES: extra-files/minimal/object.d
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19911c.d b/gcc/testsuite/gdc.test/fail_compilation/fail19911c.d
index d1e954ed394..fbd74064547 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19911c.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19911c.d
@@ -1,9 +1,8 @@
/*
DFLAGS:
-REQUIRED_ARGS:
TEST_OUTPUT:
---
-fail_compilation/fail19911c.d(15): Error: function `object.fun` `object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions
+fail_compilation/fail19911c.d(14): Error: function `object.fun` `object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19912a.d b/gcc/testsuite/gdc.test/fail_compilation/fail19912a.d
index 47d3cf2e5bc..31a508a4954 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19912a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19912a.d
@@ -1,8 +1,7 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19912a.d(8): Error: struct `fail19912a.object` conflicts with import `fail19912a.object` at fail_compilation/fail19912a.d
+fail_compilation/fail19912a.d(7): Error: struct `fail19912a.object` conflicts with import `fail19912a.object` at fail_compilation/fail19912a.d
---
*/
struct object { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19912b.d b/gcc/testsuite/gdc.test/fail_compilation/fail19912b.d
index b3bd56dde46..1e528140032 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19912b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19912b.d
@@ -1,8 +1,7 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19912b.d(8): Error: class `fail19912b.object` conflicts with import `fail19912b.object` at fail_compilation/fail19912b.d
+fail_compilation/fail19912b.d(7): Error: class `fail19912b.object` conflicts with import `fail19912b.object` at fail_compilation/fail19912b.d
---
*/
class object { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19912c.d b/gcc/testsuite/gdc.test/fail_compilation/fail19912c.d
index a4656cc856b..38d7a5aba1e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19912c.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19912c.d
@@ -1,8 +1,7 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19912c.d(8): Error: alias `fail19912c.object` conflicts with import `fail19912c.object` at fail_compilation/fail19912c.d
+fail_compilation/fail19912c.d(7): Error: alias `fail19912c.object` conflicts with import `fail19912c.object` at fail_compilation/fail19912c.d
---
*/
alias object = int;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19912d.d b/gcc/testsuite/gdc.test/fail_compilation/fail19912d.d
index fdc8dcb9244..c0fe4899810 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19912d.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19912d.d
@@ -1,8 +1,7 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19912d.d(8): Error: enum `fail19912d.object` conflicts with import `fail19912d.object` at fail_compilation/fail19912d.d
+fail_compilation/fail19912d.d(7): Error: enum `fail19912d.object` conflicts with import `fail19912d.object` at fail_compilation/fail19912d.d
---
*/
enum object { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19912e.d b/gcc/testsuite/gdc.test/fail_compilation/fail19912e.d
index 19cc9a619d1..0aa343319dd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19912e.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19912e.d
@@ -1,8 +1,7 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19912e.d(8): Error: function `fail19912e.object` conflicts with import `fail19912e.object` at fail_compilation/fail19912e.d
+fail_compilation/fail19912e.d(7): Error: function `fail19912e.object` conflicts with import `fail19912e.object` at fail_compilation/fail19912e.d
---
*/
void object() { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19913.d b/gcc/testsuite/gdc.test/fail_compilation/fail19913.d
index b0f31b5aea4..fe2655e1eda 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19913.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19913.d
@@ -1,10 +1,10 @@
-/* PERMUTE_ARGS:
- * TEST_OUTPUT:
+/*
+TEST_OUTPUT:
---
fail_compilation/fail19913.d(11): Error: no property `b` for type `int`
fail_compilation/fail19913.d(11): Error: mixin `fail19913.S.b!()` is not defined
---
- */
+*/
struct S
{
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19914.d b/gcc/testsuite/gdc.test/fail_compilation/fail19914.d
index a890d35e85b..2fd3da75540 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19914.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19914.d
@@ -1,9 +1,8 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19914.d(9): Error: undefined identifier `c` in module `fail19914`
-fail_compilation/fail19914.d(10): Error: mixin `fail19914.a!string` error instantiating
+fail_compilation/fail19914.d(8): Error: undefined identifier `c` in module `fail19914`
+fail_compilation/fail19914.d(9): Error: mixin `fail19914.a!string` error instantiating
---
*/
class a(b) { align.c d; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19915.d b/gcc/testsuite/gdc.test/fail_compilation/fail19915.d
index 17e05ee32c2..e12fe0f08cc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19915.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19915.d
@@ -1,9 +1,8 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19915.d(9): Error: undefined identifier `c` in module `fail19915`
-fail_compilation/fail19915.d(10): Error: template instance `fail19915.a!int` error instantiating
+fail_compilation/fail19915.d(8): Error: undefined identifier `c` in module `fail19915`
+fail_compilation/fail19915.d(9): Error: template instance `fail19915.a!int` error instantiating
---
*/
class a (b) { align.c d; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19917.d b/gcc/testsuite/gdc.test/fail_compilation/fail19917.d
new file mode 100644
index 00000000000..c6ad83b89a7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19917.d
@@ -0,0 +1,49 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19917.d(22): Error: overlapping default initialization for field `c` and `a`
+fail_compilation/fail19917.d(22): Error: overlapping default initialization for field `d` and `b`
+fail_compilation/fail19917.d(39): Error: overlapping default initialization for field `b` and `a`
+---
+*/
+
+struct S
+{
+ union
+ {
+ struct
+ {
+ int a = 3;
+ int b = 4;
+ }
+ }
+}
+
+struct X
+{
+ union
+ {
+ struct
+ {
+ int a = 3;
+ int b = 4;
+ }
+ struct
+ {
+ int c = 3;
+ int d = 4;
+ }
+ }
+}
+
+struct Y
+{
+ union
+ {
+ struct
+ {
+ union { int a = 3; }
+ }
+ int b = 4;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19919.d b/gcc/testsuite/gdc.test/fail_compilation/fail19919.d
new file mode 100644
index 00000000000..3c65cbf2f40
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19919.d
@@ -0,0 +1,25 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19919.d(16): Error: union field `f` with default initialization `3.14F` must be before field `n`
+fail_compilation/fail19919.d(23): Error: union field `f` with default initialization `3.14F` must be before field `n`
+---
+*/
+
+void main()
+{
+ struct X
+ {
+ union
+ {
+ int n;
+ float f = 3.14f;
+ }
+ }
+
+ union U
+ {
+ int n;
+ float f = 3.14f;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19922.d b/gcc/testsuite/gdc.test/fail_compilation/fail19922.d
index 5c9e2bbe0ab..201b1247246 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19922.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19922.d
@@ -1,9 +1,8 @@
/*
DFLAGS:
-REQUIRED_ARGS:
TEST_OUTPUT:
---
-fail_compilation/fail19922.d(17): Error: `object.TypeInfo_Class` could not be found, but is implicitly used
+fail_compilation/fail19922.d(16): Error: `object.TypeInfo_Class` could not be found, but is implicitly used
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19923.d b/gcc/testsuite/gdc.test/fail_compilation/fail19923.d
index 042cf8af11a..1438151329b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19923.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19923.d
@@ -1,9 +1,8 @@
/*
DFLAGS:
-REQUIRED_ARGS:
TEST_OUTPUT:
---
-fail_compilation/fail19923.d(17): Error: `object.TypeInfo_Class` could not be found, but is implicitly used
+fail_compilation/fail19923.d(16): Error: `object.TypeInfo_Class` could not be found, but is implicitly used
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19931.d b/gcc/testsuite/gdc.test/fail_compilation/fail19931.d
new file mode 100644
index 00000000000..940a1faee02
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19931.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail19931.d(10): Error: `struct S` may not define both a rvalue constructor and a copy constructor
+fail_compilation/fail19931.d(12): rvalue constructor defined here
+fail_compilation/fail19931.d(13): copy constructor defined here
+---
+*/
+
+struct S
+{
+ this(S s) {}
+ this(ref S s) {}
+ this(this) {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail1995.d b/gcc/testsuite/gdc.test/fail_compilation/fail1995.d
new file mode 100644
index 00000000000..7dfddec3264
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail1995.d
@@ -0,0 +1,12 @@
+/*
+REQUIRED_ARGS: -Jdoes_not_exists -Jfail_compilation/fail1995.d -Jfail_compilation/
+TEST_OUTPUT:
+---
+fail_compilation/fail1995.d(12): Error: file `"SomeFile.txt"` cannot be found or not in a path specified with `-J`
+fail_compilation/fail1995.d(12): Path(s) searched (as provided by `-J`):
+fail_compilation/fail1995.d(12): [0]: `does_not_exists` (path not found)
+fail_compilation/fail1995.d(12): [1]: `fail_compilation/fail1995.d` (not a directory)
+fail_compilation/fail1995.d(12): [2]: `fail_compilation/`
+---
+ */
+immutable string Var = import("SomeFile.txt");
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19955.d b/gcc/testsuite/gdc.test/fail_compilation/fail19955.d
index 7cdce2c676a..e2af71f2d91 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19955.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19955.d
@@ -1,8 +1,7 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/fail19955.d(8): Error: `switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`
+fail_compilation/fail19955.d(7): Error: `switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`
---
*/
void f() { switch(1) static assert(1); }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19965.d b/gcc/testsuite/gdc.test/fail_compilation/fail19965.d
new file mode 100644
index 00000000000..b76a3a08c09
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail19965.d
@@ -0,0 +1,37 @@
+/*
+REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fail19965.d(36): Error: address of variable `f` assigned to `a` with longer lifetime
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=19965
+
+struct Buffer
+{
+ int[10] data;
+
+ int[] getData() @safe return
+ {
+ return data[];
+ }
+}
+
+struct Foo()
+{
+ Buffer buffer;
+
+ int[] toArray() @safe return
+ {
+ return buffer.getData;
+ }
+}
+
+int[] a;
+
+void main() @safe
+{
+ Foo!() f;
+ a = f.toArray;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20.d b/gcc/testsuite/gdc.test/fail_compilation/fail20.d
index 821cc843e07..6cc4d22559b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail20.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail20.d(16): Error: need member function opCmp() for struct FOO to compare
+fail_compilation/fail20.d(16): Error: need member function `opCmp()` for struct `FOO` to compare
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20000.d b/gcc/testsuite/gdc.test/fail_compilation/fail20000.d
new file mode 100644
index 00000000000..fe481216be6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20000.d
@@ -0,0 +1,39 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20000.d(25): Error: cast from `fail20000.DClass` to `fail20000.CppClass` not allowed in safe code
+fail_compilation/fail20000.d(26): Error: cast from `fail20000.DInterface` to `fail20000.CppClass` not allowed in safe code
+fail_compilation/fail20000.d(27): Error: cast from `fail20000.CppClass2` to `fail20000.CppClass` not allowed in safe code
+fail_compilation/fail20000.d(28): Error: cast from `fail20000.CppInterface2` to `fail20000.CppClass` not allowed in safe code
+fail_compilation/fail20000.d(30): Error: cast from `fail20000.DClass` to `fail20000.CppInterface` not allowed in safe code
+fail_compilation/fail20000.d(31): Error: cast from `fail20000.DInterface` to `fail20000.CppInterface` not allowed in safe code
+fail_compilation/fail20000.d(32): Error: cast from `fail20000.CppClass2` to `fail20000.CppInterface` not allowed in safe code
+fail_compilation/fail20000.d(33): Error: cast from `fail20000.CppInterface2` to `fail20000.CppInterface` not allowed in safe code
+fail_compilation/fail20000.d(35): Error: cast from `fail20000.CppClass` to `fail20000.DClass` not allowed in safe code
+fail_compilation/fail20000.d(36): Error: cast from `fail20000.CppInterface` to `fail20000.DClass` not allowed in safe code
+fail_compilation/fail20000.d(38): Error: cast from `fail20000.CppClass` to `fail20000.DInterface` not allowed in safe code
+fail_compilation/fail20000.d(39): Error: cast from `fail20000.CppInterface` to `fail20000.DInterface` not allowed in safe code
+---
+*/
+extern(C++) class CppClass { int a; }
+extern(C++) class CppClass2 { void* a; }
+extern(C++) interface CppInterface { int b(); }
+extern(C++) interface CppInterface2 { void* b(); }
+class DClass { int c; }
+interface DInterface { int d(); }
+
+bool isCppClass(DClass a) @safe { return cast(CppClass) a !is null; }
+bool isCppClass(DInterface a) @safe { return cast(CppClass) a !is null; }
+bool isCppClass(CppClass2 a) @safe { return cast(CppClass) a !is null; }
+bool isCppClass(CppInterface2 a) @safe { return cast(CppClass) a !is null; }
+
+bool isCppInterface(DClass a) @safe { return cast(CppInterface) a !is null; }
+bool isCppInterface(DInterface a) @safe { return cast(CppInterface) a !is null; }
+bool isCppInterface(CppClass2 a) @safe { return cast(CppInterface) a !is null; }
+bool isCppInterface(CppInterface2 a) @safe { return cast(CppInterface) a !is null; }
+
+bool isDClass(CppClass a) @safe { return cast(DClass) a !is null; }
+bool isDClass(CppInterface a) @safe { return cast(DClass) a !is null; }
+
+bool isDInterface(CppClass a) @safe { return cast(DInterface) a !is null; }
+bool isDInterface(CppInterface a) @safe { return cast(DInterface) a !is null; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20033.d b/gcc/testsuite/gdc.test/fail_compilation/fail20033.d
new file mode 100644
index 00000000000..f641469c3dc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20033.d
@@ -0,0 +1,54 @@
+// REQUIRED_ARGS: -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20033.d(38): Deprecation: `alias byKeyValue this` is deprecated - This was a bad idea
+fail_compilation/fail20033.d(39): Deprecation: `alias byKeyValue this` is deprecated
+fail_compilation/fail20033.d(41): Deprecation: `alias byKeyValue this` is deprecated - This was a bad idea
+fail_compilation/fail20033.d(42): Deprecation: `alias byKeyValue this` is deprecated
+---
+*/
+#line 1
+struct Tuple(T...)
+{
+ T values;
+ alias values this;
+}
+
+alias KVT = Tuple!(string, string);
+
+struct Test {
+ struct Range {
+ bool empty () { return false; }
+ KVT front() { return KVT.init; }
+ void popFront() {}
+ }
+
+ auto byKeyValue () { return Range.init; }
+
+ deprecated("This was a bad idea")
+ alias byKeyValue this;
+}
+
+struct Test2 {
+ struct Range {
+ bool empty () { return false; }
+ KVT front() { return KVT.init; }
+ void popFront() {}
+ }
+
+ auto byKeyValue () { return Range.init; }
+
+ deprecated alias byKeyValue this;
+}
+
+void main ()
+{
+ foreach (k, v; Test.init.byKeyValue) {} // Fine
+ foreach (k, v; Test2.init.byKeyValue) {} // Fine
+ foreach (k, v; Test.init) {} // Fails
+ foreach (k, v; Test2.init) {} // Fails
+
+ auto f1 = Test.init.front(); // Fails
+ auto f2 = Test2.init.front(); // Fails
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20040.d b/gcc/testsuite/gdc.test/fail_compilation/fail20040.d
new file mode 100644
index 00000000000..67bdd317431
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20040.d
@@ -0,0 +1,16 @@
+/*
+REQUIRED_ARGS: -o-
+TEST_OUTPUT:
+---
+fail_compilation/fail20040.d(13): Error: no property `joiner` for type `string[]`, perhaps `import std.algorithm;` is needed?
+fail_compilation/fail20040.d(14): Error: no property `split` for type `string[]`, perhaps `import std.array;` is needed?
+fail_compilation/fail20040.d(15): Error: no property `startsWith` for type `string[]`, perhaps `import std.algorithm;` is needed?
+---
+*/
+void main()
+{
+ auto x = ["a","b","c"];
+ x.joiner();
+ x.split();
+ x.startsWith;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20073.d b/gcc/testsuite/gdc.test/fail_compilation/fail20073.d
new file mode 100644
index 00000000000..01a9ede4622
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20073.d
@@ -0,0 +1,22 @@
+// https://issues.dlang.org/show_bug.cgi?id=20073
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20073.d(20): Error: cannot implicitly convert expression `s` of type `S` to `string`
+fail_compilation/fail20073.d(21): Error: cannot implicitly convert expression `s` of type `S` to `string`
+---
+*/
+
+struct S
+{
+ char[10] x;
+ auto slice() inout { return x[0 .. 10]; }
+ alias slice this;
+}
+
+
+string test() {
+ S s;
+ string str = s; // cannot implicitly convert expression `s` of type `S` to `string`
+ return s; // and suddenly we can!
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20084.d b/gcc/testsuite/gdc.test/fail_compilation/fail20084.d
new file mode 100644
index 00000000000..dcc73a9c5fc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20084.d
@@ -0,0 +1,19 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fail20084.d(109): Error: returning `v.front()` escapes a reference to local variable `v`
+---
+*/
+
+#line 100
+
+// https://issues.dlang.org/show_bug.cgi?id=20084
+
+struct W() {
+ int value;
+ @safe ref int front() return { return value; }
+}
+
+@safe ref int get(W!() v) {
+ return v.front;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20108.d b/gcc/testsuite/gdc.test/fail_compilation/fail20108.d
new file mode 100644
index 00000000000..f768b89e6a4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20108.d
@@ -0,0 +1,31 @@
+// REQUIRED_ARGS: -preview=dip1000
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20108.d(15): Error: address of variable `y` assigned to `x` with longer lifetime
+fail_compilation/fail20108.d(16): Error: scope variable `x` may not be returned
+fail_compilation/fail20108.d(23): Error: address of variable `y` assigned to `x` with longer lifetime
+fail_compilation/fail20108.d(24): Error: scope variable `x` may not be returned
+---
+*/
+
+@safe auto test(scope int* x)
+{
+ int y = 69;
+ x = &y; //bad
+ return x;
+}
+
+@safe auto test2()
+{
+ scope int* x;
+ int y = 69;
+ x = &y; //bad
+ return x;
+}
+
+void main()
+{
+ auto y = test(null);
+ auto z = test2();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20163.d b/gcc/testsuite/gdc.test/fail_compilation/fail20163.d
new file mode 100644
index 00000000000..67df48b0fc2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20163.d
@@ -0,0 +1,11 @@
+// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/fail20164.d
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20163.d-mixin-11(11): Deprecation: module `imports.fail20164` is deprecated
+---
+*/
+module fail20163;
+
+mixin("import imports.fail20164;");
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20164.d b/gcc/testsuite/gdc.test/fail_compilation/fail20164.d
new file mode 100644
index 00000000000..c70947c7f1e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20164.d
@@ -0,0 +1,14 @@
+// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/fail20164.d
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20164.d(13): Deprecation: module `imports.fail20164` is deprecated
+---
+*/
+module fail20164;
+
+void foo()
+{
+ import imports.fail20164;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20183.d b/gcc/testsuite/gdc.test/fail_compilation/fail20183.d
new file mode 100644
index 00000000000..8a08844f843
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20183.d
@@ -0,0 +1,47 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fail20183.d(1016): Error: function `fail20183.addr(return ref int b)` is not callable using argument types `(int)`
+fail_compilation/fail20183.d(1016): cannot pass rvalue argument `S(0).i` of type `int` to parameter `return ref int b`
+fail_compilation/fail20183.d(1017): Error: address of struct temporary returned by `s()` assigned to longer lived variable `q`
+---
+ */
+
+#line 1000
+
+// https://issues.dlang.org/show_bug.cgi?id=20183
+
+@safe:
+
+int* addr(return ref int b) { return &b; }
+
+struct S
+{
+ int i;
+}
+
+S s() { return S(); }
+
+void test()
+{
+ int* p = addr(S().i); // struct literal
+ int* q = addr(s().i); // struct temporary
+}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20183.d(1107): Error: address of struct temporary returned by `s()` assigned to longer lived variable `this.ptr`
+---
+ */
+#line 1100
+
+class Foo
+{
+ int* ptr;
+
+ this() @safe
+ {
+ ptr = addr(s().i); // struct literal
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20376.d b/gcc/testsuite/gdc.test/fail_compilation/fail20376.d
new file mode 100644
index 00000000000..8410af535e2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20376.d
@@ -0,0 +1,20 @@
+// https://issues.dlang.org/show_bug.cgi?id=20376
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20376.d(17): Error: cannot implicitly convert expression `Foo()` of type `Foo` to `ubyte`
+---
+*/
+
+struct Foo
+{
+ this(ref scope Foo);
+}
+
+ubyte fun()
+{
+ return Foo();
+}
+
+void main(){}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20448.d b/gcc/testsuite/gdc.test/fail_compilation/fail20448.d
new file mode 100644
index 00000000000..6ef3a4a2d83
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20448.d
@@ -0,0 +1,23 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20448.d(16): Error: returning `p.x` escapes a reference to local variable `p`
+fail_compilation/fail20448.d(22): Error: template instance `fail20448.member!"x"` error instantiating
+---
+*/
+
+struct S
+{
+ int x, y;
+}
+
+ref int member(string mem)(S p)
+{
+ return p.x;
+}
+
+void main()
+{
+ S p;
+ p.member!"x" = 2;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20461.d b/gcc/testsuite/gdc.test/fail_compilation/fail20461.d
new file mode 100644
index 00000000000..77c7178462a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20461.d
@@ -0,0 +1,16 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fail20461.d(106): Error: reference to local variable `buffer` assigned to non-scope parameter calling `assert()`
+---
+*/
+
+#line 100
+
+// https://issues.dlang.org/show_bug.cgi?id=20461
+
+void test() @safe
+{
+ char[10] buffer = "0123456789";
+ assert(false, buffer[]);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20538.d b/gcc/testsuite/gdc.test/fail_compilation/fail20538.d
new file mode 100644
index 00000000000..df7f142e275
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20538.d
@@ -0,0 +1,14 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20538.d(12): Error: assignment must be preceded by an identifier
+fail_compilation/fail20538.d(12): Error: found `1` when expecting `,`
+---
+*/
+
+enum smth
+{
+ a,
+ = 1,
+ @disable b
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20547.d b/gcc/testsuite/gdc.test/fail_compilation/fail20547.d
new file mode 100644
index 00000000000..c14977d0f12
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20547.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20547.d(12): Error: cannot create a `string[string]` with `new`
+fail_compilation/fail20547.d(14): Error: cannot create a `string[string]` with `new`
+---
+*/
+
+void main()
+{
+ //https://issues.dlang.org/show_bug.cgi?id=11790
+ string[string] crash = new string[string];
+ //https://issues.dlang.org/show_bug.cgi?id=20547
+ int[string] c = new typeof(crash);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20551.d b/gcc/testsuite/gdc.test/fail_compilation/fail20551.d
new file mode 100644
index 00000000000..633be0a596d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20551.d
@@ -0,0 +1,27 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20551.d(15): Error: cannot take address of lazy parameter `e` in `@safe` function `opAssign`
+fail_compilation/fail20551.d(26): Error: template instance `fail20551.LazyStore!int.LazyStore.opAssign!int` error instantiating
+---
+*/
+
+struct LazyStore(T)
+{
+ T delegate() @safe dg;
+
+ void opAssign(E)(lazy E e) @safe
+ {
+ dg = cast(typeof(dg)) &e;
+ }
+
+ T test() @safe{ return dg(); }
+}
+
+static LazyStore!int f;
+
+void main(string[] args) @safe
+{
+ int x = 1;
+ f = x + x + 20 + x * 20;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20609.d b/gcc/testsuite/gdc.test/fail_compilation/fail20609.d
new file mode 100644
index 00000000000..05b7c85375a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20609.d
@@ -0,0 +1,45 @@
+/*
+ TEST_OUTPUT:
+ ---
+fail_compilation/fail20609.d(26): Error: none of the overloads of `this` are callable using argument types `(int)`
+fail_compilation/fail20609.d(23): Candidate is: `fail20609.Foo.this(string[] args)`
+fail_compilation/fail20609.d(27): Error: none of the overloads of `this` are callable using argument types `(int)`
+fail_compilation/fail20609.d(22): Candidates are: `fail20609.Foo.this(Object _param_0)`
+fail_compilation/fail20609.d(23): `fail20609.Foo.this(string[] args)`
+fail_compilation/fail20609.d(37): Error: none of the overloads of `this` are callable using argument types `(int)`
+fail_compilation/fail20609.d(37): All possible candidates are marked as `deprecated` or `@disable`
+fail_compilation/fail20609.d(43): Error: undefined identifier `deprecatedTypo_`
+fail_compilation/fail20609.d(44): Error: undefined identifier `deprecatedTypo_`, did you mean function `deprecatedTypo`?
+fail_compilation/fail20609.d(45): Error: undefined identifier `disabledTypo_`
+---
+ */
+
+// Only show `this(string[])` in non-deprecated context.
+// Show both `this(string[])` and ` this(Object)` in deprecated context.
+struct Foo
+{
+ @disable this();
+ deprecated this(Object) {}
+ this(string[] args) {}
+}
+
+void test1() { auto f = Foo(42); }
+deprecated void test2() { auto f = Foo(42); }
+
+// Make sure we do not show a message promising candidates,
+// then no candidates in the special case where nothing
+// would be usable
+struct WhoDoesThat
+{
+ @disable this();
+ deprecated this(Object) {}
+}
+void test3() { auto f = WhoDoesThat(42); }
+
+// Make sure we don't suggest disabled or deprecated functions
+deprecated void deprecatedTypo () {}
+@disable void disabledTypo () {}
+
+void test4 () { deprecatedTypo_("42"); }
+deprecated void test5 () { deprecatedTypo_("42"); }
+void test6 () { disabledTypo_("42"); }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20637.d b/gcc/testsuite/gdc.test/fail_compilation/fail20637.d
new file mode 100644
index 00000000000..77c69eaa6e6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20637.d
@@ -0,0 +1,12 @@
+/*
+EXTRA_FILES: imports/fail20637b.d
+TEST_OUTPUT:
+---
+fail_compilation/fail20637.d(12): Error: no property `foo` for type `imports.fail20637b.A`
+---
+*/
+module fail20637;
+
+import imports.fail20637b;
+
+void main() { A.foo; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20638.d b/gcc/testsuite/gdc.test/fail_compilation/fail20638.d
new file mode 100644
index 00000000000..9954d37c233
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20638.d
@@ -0,0 +1,14 @@
+/*
+EXTRA_FILES: imports/fail20638b.d
+TEST_OUTPUT:
+---
+fail_compilation/fail20638.d(13): Error: undefined identifier `foo` in module `imports.fail20638b`
+---
+*/
+module fail20638;
+
+import imports.fail20638b;
+
+void main() {
+ imports.fail20638b.foo;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20658.d b/gcc/testsuite/gdc.test/fail_compilation/fail20658.d
new file mode 100644
index 00000000000..fd422aaa168
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20658.d
@@ -0,0 +1,14 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20658.d(14): Error: field `U.m` cannot modify fields in `@safe` code that overlap fields with other storage classes
+---
+*/
+
+union U
+{
+ int m;
+ immutable int i;
+}
+U u;
+enum e = () @safe { u.m = 13; };
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20691.d b/gcc/testsuite/gdc.test/fail_compilation/fail20691.d
new file mode 100644
index 00000000000..7a43232658d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20691.d
@@ -0,0 +1,25 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fail20691.d(106): Error: cannot take address of `scope` local `sa` in `@safe` function `bar`
+fail_compilation/fail20691.d(106): Error: cannot cast expression `sa` of type `char[][2]` to `char[][]`
+fail_compilation/fail20691.d(107): Error: cannot take address of `scope` local `sa` in `@safe` function `bar`
+fail_compilation/fail20691.d(107): Error: cannot cast expression `sa` of type `char[][2]` to `char[][]`
+fail_compilation/fail20691.d(108): Error: cannot take address of `scope` local `sa` in `@safe` function `bar`
+fail_compilation/fail20691.d(108): Error: cannot cast expression `sa` of type `char[][2]` to `char[][]`
+---
+*/
+
+#line 100
+
+// https://issues.dlang.org/show_bug.cgi?id=20691
+
+void bar() @safe
+{
+ scope char[][2] sa;
+ scope char[][] da = cast(char[][])sa;
+ scope char[][] ca = sa;
+ foo(sa);
+}
+
+void foo(scope char[][] a) @safe;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail207.d b/gcc/testsuite/gdc.test/fail_compilation/fail207.d
index becf49bfeaf..2a00f7fa907 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail207.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail207.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/fail207.d(10): Error: found end of file instead of initializer
-fail_compilation/fail207.d(10): Error: semicolon expected, not `EOF`
+fail_compilation/fail207.d(10): Error: semicolon expected, not `End of File`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20714.d b/gcc/testsuite/gdc.test/fail_compilation/fail20714.d
new file mode 100644
index 00000000000..6bf6b07853e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20714.d
@@ -0,0 +1,32 @@
+// https://issues.dlang.org/show_bug.cgi?id=20714
+// REQUIRED_ARGS: -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20714.d(19): Deprecation: `struct Adder` implicitly-generated postblit hides copy constructor.
+fail_compilation/fail20714.d(19): The field postblit will have priority over the copy constructor.
+fail_compilation/fail20714.d(19): To change this, the postblit should be disabled for `struct Adder`
+---
+*/
+
+
+struct Blitter
+{
+ int payload;
+ this(this){}
+}
+
+struct Adder
+{
+ Blitter blitter;
+ this(int payload) {this.blitter.payload = payload;}
+ this(ref Adder rhs) {this.blitter.payload = rhs.blitter.payload + 1;}
+}
+
+void main()
+{
+ Adder piece1 = 1;
+ auto piece2 = piece1;
+
+ assert(piece2.blitter.payload == 2);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20730a.d b/gcc/testsuite/gdc.test/fail_compilation/fail20730a.d
new file mode 100644
index 00000000000..dcf8960c62c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20730a.d
@@ -0,0 +1,39 @@
+/*
+REQUIRED_ARGS: -o-
+TEST_OUTPUT:
+---
+fail_compilation/fail20730a.d(11): Error: undefined identifier `undef20730`
+---
+*/
+void test20730()
+{
+ auto f = File().byLine;
+ undef20730();
+}
+
+struct File
+{
+ shared uint refs;
+
+ this(this)
+ {
+ atomicOp!"+="(refs, 1);
+ }
+
+ struct ByLineImpl(Char)
+ {
+ File file;
+ char[] line;
+ }
+
+ auto byLine()
+ {
+ return ByLineImpl!char();
+ }
+}
+
+T atomicOp(string op, T, V1)(ref shared T val, V1 mod)
+ if (__traits(compiles, mixin("*cast(T*)&val" ~ op ~ "mod")))
+{
+ return val;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20730b.d b/gcc/testsuite/gdc.test/fail_compilation/fail20730b.d
new file mode 100644
index 00000000000..9320423fe48
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20730b.d
@@ -0,0 +1,46 @@
+/*
+REQUIRED_ARGS: -verrors=spec -o-
+TEST_OUTPUT:
+---
+(spec:1) fail_compilation/fail20730b.d-mixin-43(43): Error: C style cast illegal, use `cast(int)mod`
+fail_compilation/fail20730b.d(26): Error: template `fail20730b.atomicOp` cannot deduce function from argument types `!("+=")(shared(uint), int)`
+fail_compilation/fail20730b.d(41): Candidate is: `atomicOp(string op, T, V1)(shared ref T val, V1 mod)`
+ with `op = "+=",
+ T = uint,
+ V1 = int`
+ must satisfy the following constraint:
+` __traits(compiles, mixin("(int)mod"))`
+---
+*/
+void test20730()
+{
+ auto f = File().byLine;
+}
+
+struct File
+{
+ shared uint refs;
+
+ this(this)
+ {
+ atomicOp!"+="(refs, 1);
+ }
+
+ struct ByLineImpl(Char)
+ {
+ File file;
+ char[] line;
+ }
+
+ auto byLine()
+ {
+ return ByLineImpl!char();
+ }
+}
+
+T atomicOp(string op, T, V1)(ref shared T val, V1 mod)
+ // C-style cast causes raises a parser error whilst gagged.
+ if (__traits(compiles, mixin("(int)mod")))
+{
+ return val;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20771.d b/gcc/testsuite/gdc.test/fail_compilation/fail20771.d
new file mode 100644
index 00000000000..18e1c87980d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20771.d
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20771.d(19): Error: cannot pass types with postblits or copy constructors as variadic arguments
+fail_compilation/fail20771.d(20): Error: cannot pass types with postblits or copy constructors as variadic arguments
+---
+*/
+extern void variadic(...);
+
+struct S20771
+{
+ int field;
+ this(this) { }
+}
+
+void test()
+{
+ auto v = S20771(0);
+ variadic(v,
+ S20771(1));
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20772.d b/gcc/testsuite/gdc.test/fail_compilation/fail20772.d
new file mode 100644
index 00000000000..a2fdac68798
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20772.d
@@ -0,0 +1,22 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20772.d(20): Error: cannot pass types with postblits or copy constructors as variadic arguments
+fail_compilation/fail20772.d(21): Error: cannot pass types with postblits or copy constructors as variadic arguments
+---
+*/
+extern void variadic(...);
+
+struct S20772
+{
+ int field;
+ this(int) { }
+ this(ref S20772 o) { }
+}
+
+void test()
+{
+ auto v = S20772(0);
+ variadic(v,
+ S20772(1));
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20775.d b/gcc/testsuite/gdc.test/fail_compilation/fail20775.d
new file mode 100644
index 00000000000..3e050960a18
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20775.d
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20775.d(19): Error: cannot pass types that need destruction as variadic arguments
+fail_compilation/fail20775.d(20): Error: cannot pass types that need destruction as variadic arguments
+---
+*/
+extern void variadic(...);
+
+struct S20775
+{
+ int field;
+ ~this() { }
+}
+
+void test()
+{
+ auto v = S20775(0);
+ variadic(v,
+ S20775(1));
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20779.d b/gcc/testsuite/gdc.test/fail_compilation/fail20779.d
new file mode 100644
index 00000000000..945803d53f1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20779.d
@@ -0,0 +1,17 @@
+// https://issues.dlang.org/show_bug.cgi?id=20779
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20779.d(12): Error: struct `fail20779.X` cannot have field `x` with same struct type
+---
+*/
+
+module fail20779;
+
+struct X
+{
+ X x;
+
+ enum e = __traits(compiles, X.init);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail208.d b/gcc/testsuite/gdc.test/fail_compilation/fail208.d
index 8abcc3e4af3..f81774b2869 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail208.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail208.d
@@ -1,14 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail208.d(18): Error: return expression expected
-fail_compilation/fail208.d(21): called from here: MakeA()
+fail_compilation/fail208.d(18): Error: `return` expression expected
+fail_compilation/fail208.d(21): called from here: `MakeA()`
---
*/
-// Issue 1593 - ICE compiler crash empty return statement in function
-
+// https://issues.dlang.org/show_bug.cgi?id=1593
+// ICE compiler crash empty return statement in function
struct A
{
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20800.d b/gcc/testsuite/gdc.test/fail_compilation/fail20800.d
new file mode 100644
index 00000000000..1184b8e6ee8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20800.d
@@ -0,0 +1,24 @@
+// https://issues.dlang.org/show_bug.cgi?id=20800
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20800.d(22): Error: function `fail20800.fun(int a)` is not callable using argument types `(string)`
+fail_compilation/fail20800.d(22): cannot pass argument `(m()).index()` of type `string` to parameter `int a`
+---
+*/
+
+struct RegexMatch
+{
+ string index() { return null; }
+ ~this() { }
+}
+static m() { return RegexMatch(); }
+
+void fun(int a);
+
+void initCommands()
+{
+ fun(m.index);
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail209.d b/gcc/testsuite/gdc.test/fail_compilation/fail209.d
index a0211aaf4b4..689fd7bc542 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail209.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail209.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail209.d(20): Error: incompatible types for ((a) -= (x)): 'float' and 'fail209.X'
+fail_compilation/fail209.d(20): Error: incompatible types for `(a) -= (x)`: `float` and `fail209.X`
---
*/
-// Issue 725 - expression.c:6516: virtual Expression* MinAssignExp::semantic(Scope*): Assertion `e2->type->isfloating()' failed
-
+// https://issues.dlang.org/show_bug.cgi?id=725
+// expression.c:6516: virtual Expression* MinAssignExp::semantic(Scope*): Assertion `e2->type->isfloating()' failed
class X
{
float a;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20965.d b/gcc/testsuite/gdc.test/fail_compilation/fail20965.d
new file mode 100644
index 00000000000..192b84e8619
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail20965.d
@@ -0,0 +1,27 @@
+// https://issues.dlang.org/show_bug.cgi?id=20965
+// REQUIRED_ARGS: -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail20965.d(17): Deprecation: `struct S` implicitly-generated postblit hides copy constructor.
+fail_compilation/fail20965.d(17): The field postblit will have priority over the copy constructor.
+fail_compilation/fail20965.d(17): To change this, the postblit should be disabled for `struct S`
+---
+*/
+
+struct C
+{
+ this(this) {}
+}
+
+struct S
+{
+ C c;
+ @disable this(ref typeof(this));
+}
+
+void main()
+{
+ S s1;
+ auto s2 = s1; // problem
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21091a.d b/gcc/testsuite/gdc.test/fail_compilation/fail21091a.d
new file mode 100644
index 00000000000..74f40c2eeb5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21091a.d
@@ -0,0 +1,17 @@
+// https://issues.dlang.org/show_bug.cgi?id=21091
+
+/*
+TEST_OUTPUT:
+----
+fail_compilation/fail21091a.d(15): Error: module `Ternary` is in file 'Ternary.d' which cannot be read
+import path[0] = fail_compilation
+import path[1] = $p:druntime/import$
+import path[2] = $p:phobos$
+----
+*/
+
+struct NullAllocator
+{
+ import Ternary;
+ Ternary owns() { }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21091b.d b/gcc/testsuite/gdc.test/fail_compilation/fail21091b.d
new file mode 100644
index 00000000000..d9467aafdb3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21091b.d
@@ -0,0 +1,17 @@
+// https://issues.dlang.org/show_bug.cgi?id=21091
+
+/*
+TEST_OUTPUT:
+----
+fail_compilation/fail21091b.d(15): Error: module `Tid` is in file 'Tid.d' which cannot be read
+import path[0] = fail_compilation
+import path[1] = $p:druntime/import$
+import path[2] = $p:phobos$
+----
+*/
+
+class Logger
+{
+ import Tid;
+ Tid threadId;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21092.d b/gcc/testsuite/gdc.test/fail_compilation/fail21092.d
new file mode 100644
index 00000000000..2ca826e70ab
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21092.d
@@ -0,0 +1,27 @@
+// https://issues.dlang.org/show_bug.cgi?id=21092
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21092.d(19): Error: Using the result of a comma expression is not allowed
+fail_compilation/fail21092.d(19): Error: using `*` on an array is no longer supported; use `*(T , U).ptr` instead
+fail_compilation/fail21092.d(19): Error: `*(T , cast(real*)U)` has no effect
+fail_compilation/fail21092.d(26): Error: Using the result of a comma expression is not allowed
+fail_compilation/fail21092.d(26): Error: using `*` on an array is no longer supported; use `*(w , SmallStirlingCoeffs).ptr` instead
+fail_compilation/fail21092.d(26): Error: `*(w , cast(real*)SmallStirlingCoeffs)` has no effect
+---
+*/
+
+real[] T;
+real[] U = [];
+real erf()
+{
+ *(T, U);
+}
+
+real gammaStirling()
+{
+ static real[] SmallStirlingCoeffs = [];
+ real w;
+ *(w, SmallStirlingCoeffs);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail212.d b/gcc/testsuite/gdc.test/fail_compilation/fail212.d
index 63c573eb63d..5f308638b9c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail212.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail212.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail212.d(14): Error: function fail212.S.bar without 'this' cannot be const
+fail_compilation/fail212.d(14): Error: function `fail212.S.bar` without `this` cannot be `const`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21275.d b/gcc/testsuite/gdc.test/fail_compilation/fail21275.d
new file mode 100644
index 00000000000..dbdedb3873f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21275.d
@@ -0,0 +1,22 @@
+// https://issues.dlang.org/show_bug.cgi?id=21275
+// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/fail21275a.d
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21275.d(18): Deprecation: Function `imports.fail21275a.Foo.x` of type `ref int() return` is not accessible from module `fail21275`
+fail_compilation/fail21275.d(21): Deprecation: Function `imports.fail21275a.Bar.x` of type `int(int)` is not accessible from module `fail21275`
+---
+*/
+
+void main()
+{
+ import imports.fail21275a;
+
+ auto f = Foo();
+ f.x = 3;
+
+ auto b = Bar();
+ b.x = 3;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail213.d b/gcc/testsuite/gdc.test/fail_compilation/fail213.d
index d00238273a9..fee91ec9b3e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail213.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail213.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail213.d(18): Error: template instance Foo!int does not match template declaration Foo(T : immutable(T))
-fail_compilation/fail213.d(25): Error: template instance Foo!(const(int)) does not match template declaration Foo(T : immutable(T))
+fail_compilation/fail213.d(18): Error: template instance `Foo!int` does not match template declaration `Foo(T : immutable(T))`
+fail_compilation/fail213.d(25): Error: template instance `Foo!(const(int))` does not match template declaration `Foo(T : immutable(T))`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail215.d b/gcc/testsuite/gdc.test/fail_compilation/fail215.d
index f9427230751..292f07156ad 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail215.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail215.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail215.d(10): Error: function fail215.b.k cannot be both final and abstract
+fail_compilation/fail215.d(10): Error: function `fail215.b.k` cannot be both `final` and `abstract`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21508.d b/gcc/testsuite/gdc.test/fail_compilation/fail21508.d
new file mode 100644
index 00000000000..da5d8f98f91
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21508.d
@@ -0,0 +1,18 @@
+/*
+REQUIRED_ARGS: -Ifail_compilation/imports/
+EXTRA_FILES: imports/import21508.d
+TEST_OUTPUT:
+---
+fail_compilation/fail21508.d(17): Error: import `fail21508.import21508` is used as a type
+---
+*/
+import import21508;
+
+// import21508 is a private class, code should not compile
+// The compiler used to "helpfully" look inside the import,
+// bypassing the shadowing that this introduces.
+
+void main ()
+{
+ auto c = new import21508();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21508_2.d b/gcc/testsuite/gdc.test/fail_compilation/fail21508_2.d
new file mode 100644
index 00000000000..af986b7b26c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21508_2.d
@@ -0,0 +1,11 @@
+/*
+REQUIRED_ARGS: -Ifail_compilation/imports/
+EXTRA_FILES: imports/import21508.d
+TEST_OUTPUT:
+---
+fail_compilation/fail21508_2.d(11): Error: import `fail21508_2.import21508` is used as a type
+---
+*/
+import import21508;
+
+class Other : import21508 { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21547.d b/gcc/testsuite/gdc.test/fail_compilation/fail21547.d
new file mode 100644
index 00000000000..7a6a44a4a13
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21547.d
@@ -0,0 +1,34 @@
+// https://issues.dlang.org/show_bug.cgi?id=21547
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21547.d(32): Error: struct `Bar` has constructors, cannot use `{ initializers }`, use `Bar( initializers )` instead
+fail_compilation/fail21547.d(33): Error: struct `Bar1` has constructors, cannot use `{ initializers }`, use `Bar1( initializers )` instead
+---
+*/
+
+struct Bar
+{
+ @disable this(int a) {}
+ this(int a, int b) {}
+
+ string a;
+ uint b;
+}
+
+struct Bar1
+{
+ @disable this(int a) {}
+ this(const ref Bar1 o) {}
+ this(int a, int b) {}
+
+ string a;
+ uint b;
+}
+
+void main ()
+{
+ Bar b = { a: "Hello", b: 42 };
+ Bar1 b1 = { a: "Hello", b: 42 };
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail216.d b/gcc/testsuite/gdc.test/fail_compilation/fail216.d
index 98dcbb8dcfb..eb736e64b94 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail216.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail216.d
@@ -1,14 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail216.d(16): Error: expression foo() is void and has no value
-fail_compilation/fail216.d(14): Error: function fail216.bar has no return statement, but is expected to return a value of type int
-fail_compilation/fail216.d(19): called from here: bar()
+fail_compilation/fail216.d(16): Error: expression `foo()` is `void` and has no value
+fail_compilation/fail216.d(14): Error: function `fail216.bar` has no `return` statement, but is expected to return a value of type `int`
+fail_compilation/fail216.d(19): called from here: `bar()`
---
*/
-// Issue 1744 - CTFE: crash on assigning void-returning function to variable
-
+// https://issues.dlang.org/show_bug.cgi?id=1744
+// CTFE: crash on assigning void-returning function to variable
void foo() {}
int bar()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail218.d b/gcc/testsuite/gdc.test/fail_compilation/fail218.d
index e180e77c7ec..a4686113306 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail218.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail218.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail218.d(15): Error: cannot modify string literal ", "
+fail_compilation/fail218.d(15): Error: cannot modify string literal `", "`
---
*/
-// Issue 1788 - dmd segfaults without info
-
+// https://issues.dlang.org/show_bug.cgi?id=1788
+// dmd segfaults without info
void main()
{
string a = "abc";
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21830.d b/gcc/testsuite/gdc.test/fail_compilation/fail21830.d
new file mode 100644
index 00000000000..9955c65b696
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21830.d
@@ -0,0 +1,34 @@
+/* REQUIRED_ARGS: -de -unittest
+TEST_OUTPUT
+---
+fail_compilation/fail21830.d(24): Deprecation: struct `fail21830.OldS21830` is deprecated - Deprecated type
+fail_compilation/fail21830.d(24): Deprecation: template `fail21830.test21830(T)(T t) if (is(T == OldS21830))` is deprecated - Deprecated template
+fail_compilation/fail21830.d(24): Deprecation: struct `fail21830.OldS21830` is deprecated - Deprecated type
+---
+*/
+#line 1
+deprecated("Deprecated type")
+struct OldS21830 { }
+
+struct NewS21830 { }
+
+static if (1)
+{
+ auto test21830(T)(T t)
+ if (is(T == NewS21830))
+ {
+ return T.init;
+ }
+}
+
+deprecated("Deprecated template")
+auto test21830(T)(T t)
+if (is(T == OldS21830))
+{
+ return T.init;
+}
+
+unittest
+{
+ auto b = test21830(OldS21830());
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21831.d b/gcc/testsuite/gdc.test/fail_compilation/fail21831.d
new file mode 100644
index 00000000000..699ef0bd39b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21831.d
@@ -0,0 +1,29 @@
+/* REQUIRED_ARGS: -de -unittest
+TEST_OUTPUT
+---
+fail_compilation/fail21831.d(19): Deprecation: struct `fail21831.S21831` is deprecated - Deprecated type
+fail_compilation/fail21831.d(19): Deprecation: template `fail21831.test21831(T)(T t) if (__traits(isDeprecated, T))` is deprecated - Deprecated template
+fail_compilation/fail21831.d(19): Deprecation: struct `fail21831.S21831` is deprecated - Deprecated type
+---
+*/
+#line 1
+deprecated("Deprecated type")
+struct S21831 { }
+
+auto test21831(T)(T t)
+if (!__traits(isDeprecated, T))
+{
+ return T.init;
+}
+
+deprecated("Deprecated template")
+auto test21831(T)(T t)
+if (__traits(isDeprecated, T))
+{
+ return T.init;
+}
+
+unittest
+{
+ auto b = test21831(S21831());
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21832.d b/gcc/testsuite/gdc.test/fail_compilation/fail21832.d
new file mode 100644
index 00000000000..03753a489a5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21832.d
@@ -0,0 +1,21 @@
+// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/imp21832.d
+/*
+TEST_OUTPUT
+---
+fail_compilation/fail21832.d(4): Deprecation: function `imports.imp21832.fun` is deprecated
+fail_compilation/fail21832.d(10): Deprecation: template `imports.imp21832.tpl()(char a)` is deprecated
+---
+*/
+#line 1
+int test21832a()
+{
+ import imports.imp21832 : fun;
+ return fun('a');
+}
+
+int test21832b()
+{
+ import imports.imp21832 : tpl;
+ return tpl('a');
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21849.d b/gcc/testsuite/gdc.test/fail_compilation/fail21849.d
new file mode 100644
index 00000000000..75821f69eab
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21849.d
@@ -0,0 +1,36 @@
+// https://issues.dlang.org/show_bug.cgi?id=21849
+// REQUIRED_ARGS: -verrors=context -vcolumns
+/* TEST_OUTPUT:
+---
+fail_compilation/fail21849.d(21,17): Error: cannot implicitly convert expression `1` of type `int` to `string`
+ string ß = 1;
+ ^
+fail_compilation/fail21849.d(25,42): Error: cannot implicitly convert expression `cast(ushort)65535u` of type `ushort` to `byte`
+ string s = "ß☺-oneline"; byte S = ushort.max;
+ ^
+fail_compilation/fail21849.d(30,10): Error: undefined identifier `undefined_identifier`
+ß-utf"; undefined_identifier;
+ ^
+fail_compilation/fail21849.d(35,15): Error: `s[0..9]` has no effect
+☺-smiley"; s[0 .. 9];
+ ^
+---
+*/
+void fail21849a()
+{
+ string ß = 1;
+}
+void fail21849b()
+{
+ string s = "ß☺-oneline"; byte S = ushort.max;
+}
+void fail21849c()
+{
+ string s = "
+ß-utf"; undefined_identifier;
+}
+void fail21849d()
+{
+ string s = "
+☺-smiley"; s[0 .. 9];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21868b.d b/gcc/testsuite/gdc.test/fail_compilation/fail21868b.d
new file mode 100644
index 00000000000..6f34029db10
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21868b.d
@@ -0,0 +1,22 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fail21868b.d(19): Error: returning `&s.x` escapes a reference to parameter `s`
+fail_compilation/fail21868b.d(19): perhaps remove `scope` parameter annotation so `return` applies to `ref`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=21868
+
+struct S
+{
+ int x;
+ int* y;
+}
+
+int* test(ref return scope S s)
+{
+ return &s.x;
+}
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21885.d b/gcc/testsuite/gdc.test/fail_compilation/fail21885.d
new file mode 100644
index 00000000000..4052efd2dfd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21885.d
@@ -0,0 +1,25 @@
+// https://issues.dlang.org/show_bug.cgi?id=21885
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21885.d(24): Error: struct `fail21885.Outer` is not copyable because field `i` is not copyable
+---
+*/
+
+struct Outer
+{
+ Inner i;
+}
+
+struct Inner
+{
+ @disable this(this);
+}
+
+void main()
+{
+ Outer o1;
+ Outer o2;
+ o1 = o2;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21928.d b/gcc/testsuite/gdc.test/fail_compilation/fail21928.d
new file mode 100644
index 00000000000..c9fac133ddb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21928.d
@@ -0,0 +1,19 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21928.d(18): Error: array literal in `@nogc` function `D main` may cause a GC allocation
+---
+*/
+
+@nogc:
+
+
+struct Shape
+{
+ immutable size_t[] dims = [];
+}
+
+void main()
+{
+ auto s = Shape(2 ~ Shape.init.dims);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21928b.d b/gcc/testsuite/gdc.test/fail_compilation/fail21928b.d
new file mode 100644
index 00000000000..3ce93e0a097
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail21928b.d
@@ -0,0 +1,19 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21928b.d(18): Error: array literal in `@nogc` function `D main` may cause a GC allocation
+---
+*/
+
+@nogc:
+
+
+struct Shape
+{
+ immutable size_t[] dims = [];
+}
+
+void main()
+{
+ auto s = Shape(Shape.init.dims ~ 2);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail2195.d b/gcc/testsuite/gdc.test/fail_compilation/fail2195.d
index b6d53042109..0eff0663d4b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail2195.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail2195.d
@@ -16,3 +16,19 @@ void main()
int variable; // shadowing is disallowed but not detected
}
}
+
+void fun()
+{
+ int var1, var2, var3;
+
+ void gun()
+ {
+ int var1; // OK?
+
+ int[] arr;
+ foreach (i, var2; arr) {} // OK?
+
+ int[int] aa;
+ foreach (k, var3; aa) {} // Not OK??
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22035.d b/gcc/testsuite/gdc.test/fail_compilation/fail22035.d
new file mode 100644
index 00000000000..ba03be68124
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22035.d
@@ -0,0 +1,11 @@
+// https://issues.dlang.org/show_bug.cgi?id=22035
+/* TEST_OUTPUT
+---
+fail_compilation/fail22035.d(10): Error: found `2` when expecting `:`
+fail_compilation/fail22035.d(10): Error: found `:` instead of statement
+---
+*/
+int test22035()
+{
+ case 1 2:
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22054.d b/gcc/testsuite/gdc.test/fail_compilation/fail22054.d
new file mode 100644
index 00000000000..c172f089d9c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22054.d
@@ -0,0 +1,23 @@
+// https://issues.dlang.org/show_bug.cgi?id=22054
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail22054.d(21): Error: no property `what` for type `fail22054.exception`
+fail_compilation/fail22054.d(16): `class fail22054.exception` is opaque and has no members.
+fail_compilation/fail22054.d(22): Error: no property `what` for type `fail22054.exception2`
+fail_compilation/fail22054.d(17): `struct fail22054.exception2` is opaque and has no members.
+---
+*/
+
+
+
+
+class exception;
+struct exception2;
+
+void main ()
+{
+ assert(exception.what() == "Hello");
+ assert(exception2.what() == "Hello");
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22075.d b/gcc/testsuite/gdc.test/fail_compilation/fail22075.d
new file mode 100644
index 00000000000..9b857c2f1d5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22075.d
@@ -0,0 +1,30 @@
+// https://issues.dlang.org/show_bug.cgi?id=22075
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail22075.d(25): Error: AA key type `S` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined
+fail_compilation/fail22075.d(26): Error: AA key type `S` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined
+---
+*/
+
+struct HasAliasThis { int a; alias a this; }
+
+struct LacksAliasThis { int a; }
+
+struct S(T)
+{
+ private T a;
+
+ bool opEquals(const S rhs) const @nogc nothrow @safe
+ {
+ return rhs is this;
+ }
+}
+
+int[S!HasAliasThis] aa1; // Compiles but should not.
+int[S!LacksAliasThis] aa2; // Correctly fails to compile with "Error: AA key
+ // type `S` should have `extern (D) size_t toHash() const nothrow @safe`
+ // if `opEquals` defined"".
+
+void main() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22084.d b/gcc/testsuite/gdc.test/fail_compilation/fail22084.d
new file mode 100644
index 00000000000..02fff2f6cc6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22084.d
@@ -0,0 +1,23 @@
+// https://issues.dlang.org/show_bug.cgi?id=22084
+/*
+TEST_OUTPUT
+---
+fail_compilation/fail22084.d(22): Error: cannot pass types that need destruction as variadic arguments
+---
+*/
+import core.stdc.stdarg;
+
+extern(C++) void testVariadic(int a, ...)
+{
+}
+
+struct Destructor
+{
+ ~this() { }
+}
+
+void test()
+{
+ auto a0 = Destructor;
+ testVariadic(1, a0);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail221.d b/gcc/testsuite/gdc.test/fail_compilation/fail221.d
index 9bada5b95cc..14c3e7bf801 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail221.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail221.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail221.d(11): Error: expression cast(void)0 is void and has no value
+fail_compilation/fail221.d(11): Error: expression `cast(void)0` is `void` and has no value
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22118.d b/gcc/testsuite/gdc.test/fail_compilation/fail22118.d
new file mode 100644
index 00000000000..74086614fe7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22118.d
@@ -0,0 +1,36 @@
+// https://issues.dlang.org/show_bug.cgi?id=22118
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail22118.d(33): Error: cannot modify `this.v.a` in `const` function
+---
+*/
+
+struct NeedsInit
+{
+ int n;
+ @disable this();
+}
+
+union U
+{
+ NeedsInit a;
+}
+
+struct V
+{
+ NeedsInit a;
+}
+
+struct S
+{
+ U u;
+ V v;
+ this(const NeedsInit arg) const
+ {
+ u.a = arg; // this should compile
+ v.a = arg; // this should not
+ }
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22121.d b/gcc/testsuite/gdc.test/fail_compilation/fail22121.d
new file mode 100644
index 00000000000..f45cf12e061
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22121.d
@@ -0,0 +1,11 @@
+// https://issues.dlang.org/show_bug.cgi?id=22121
+// EXTRA_FILES: fail22121/imports/test22121/package.d
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail22121/imports/test22121/package.d(1): Error: package name 'fail22121' conflicts with usage as a module name in file fail_compilation/fail22121.d
+---
+*/
+
+module fail22121;
+import fail22121.imports.test22121;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22121/imports/test22121/package.d b/gcc/testsuite/gdc.test/fail_compilation/fail22121/imports/test22121/package.d
new file mode 100644
index 00000000000..9277fabd0c5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22121/imports/test22121/package.d
@@ -0,0 +1 @@
+module fail22121.imports.test22121;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22138.d b/gcc/testsuite/gdc.test/fail_compilation/fail22138.d
new file mode 100644
index 00000000000..4fee96f14d6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22138.d
@@ -0,0 +1,21 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fail22138.d(107): Error: scope variable `e` may not be returned
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=22138
+
+#line 100
+
+@safe
+int* test()
+{
+ int*[] a;
+ foreach (scope e; a)
+ {
+ return e;
+ }
+ return null;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22157.d b/gcc/testsuite/gdc.test/fail_compilation/fail22157.d
new file mode 100644
index 00000000000..d25aaada2c5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail22157.d
@@ -0,0 +1,34 @@
+// https://issues.dlang.org/show_bug.cgi?id=22157
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail22157.d(32): Error: `fail22157.S!true.S.foo` called with argument types `()` matches both:
+fail_compilation/fail22157.d(21): `fail22157.S!true.S.foo()`
+and:
+fail_compilation/fail22157.d(22): `fail22157.S!true.S.foo()`
+fail_compilation/fail22157.d(33): Error: `fail22157.S!false.S.foo` called with argument types `()` matches both:
+fail_compilation/fail22157.d(26): `fail22157.S!false.S.foo()`
+and:
+fail_compilation/fail22157.d(27): `fail22157.S!false.S.foo()`
+---
+*/
+
+struct S(bool b)
+{
+ static if(b)
+ {
+ void foo() {}
+ static void foo() {}
+ }
+ else
+ {
+ static void foo() {}
+ void foo() {}
+ }
+}
+
+void main() {
+ S!true.foo;
+ S!false.foo;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail222.d b/gcc/testsuite/gdc.test/fail_compilation/fail222.d
index c377239fe96..30eb0144d99 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail222.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail222.d
@@ -1,9 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail222.d(10): Error: template fail222.getMixin(TArg..., int i = 0)() template tuple parameter must be last one
-fail_compilation/fail222.d(17): Error: template instance getMixin!() does not match template declaration getMixin(TArg..., int i = 0)()
-fail_compilation/fail222.d(20): Error: template instance fail222.Thing!() error instantiating
+fail_compilation/fail222.d(11): Error: template `fail222.getMixin(TArg..., int i = 0)()` template tuple parameter must be last one
+fail_compilation/fail222.d(18): Error: template instance `getMixin!()` does not match template declaration `getMixin(TArg..., int i = 0)()`
+fail_compilation/fail222.d(21): Error: template instance `fail222.Thing!()` error instantiating
+fail_compilation/fail222.d(23): Error: template `fail222.fooBar(A..., B...)()` template tuple parameter must be last one
---
*/
@@ -18,3 +19,5 @@ class Thing(TArg...)
}
public Thing!() stuff;
+
+void fooBar (A..., B...)() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail223.d b/gcc/testsuite/gdc.test/fail_compilation/fail223.d
index 99e26ea7d36..804b53574b7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail223.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail223.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail223.d(14): Error: cannot modify this.x in const function
+fail_compilation/fail223.d(14): Error: cannot modify `this.x` in `const` function
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail224.d b/gcc/testsuite/gdc.test/fail_compilation/fail224.d
index 4bde3700482..c52cb431dc7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail224.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail224.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail224.d(22): Error: need 'this' of type A to access member x from static function f
+fail_compilation/fail224.d(22): Error: need `this` of type `A` to access member `x` from static function `f`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail229.d b/gcc/testsuite/gdc.test/fail_compilation/fail229.d
index 6cf8676cc99..5b42cca9ea0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail229.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail229.d
@@ -6,6 +6,6 @@ fail_compilation/fail229.d(11): Error: array dimension overflow
---
*/
-// Issue 1936 - Error with no line number (array dimension overflow)
-
+// https://issues.dlang.org/show_bug.cgi?id=1936
+// Error with no line number (array dimension overflow)
static int[] x = [-1: 1];
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail23.d b/gcc/testsuite/gdc.test/fail_compilation/fail23.d
index 29e8ed47f19..67d02eb3afc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail23.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail23.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail23.d(14): Error: break is not inside a loop or switch
+fail_compilation/fail23.d(14): Error: `break` is not inside a loop or `switch`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail231.d b/gcc/testsuite/gdc.test/fail_compilation/fail231.d
index 95f25eea3c3..fc6cfa1f2ff 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail231.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail231.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail231.d(15): Error: class fail231.Derived cannot implicitly generate a default ctor when base class fail231.Base is missing a default ctor
+fail_compilation/fail231.d(15): Error: class `fail231.Derived` cannot implicitly generate a default constructor when base class `fail231.Base` is missing a default constructor
---
*/
-// Issue 951 - Missing line number: no constructor provided for a class derived from a class with no default constructor
-
+// https://issues.dlang.org/show_bug.cgi?id=951
+// Missing line number: no constructor provided for a class derived from a class with no default constructor
class Base
{
this(int x) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail233.d b/gcc/testsuite/gdc.test/fail_compilation/fail233.d
index e2274abf172..7d4d97892dd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail233.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail233.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail233.d(11): Error: variable fail233.bug1176.v void[1] does not have a default initializer
+fail_compilation/fail233.d(11): Error: variable `fail233.bug1176.v` `void[1]` does not have a default initializer
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail235.d b/gcc/testsuite/gdc.test/fail_compilation/fail235.d
index d0d9deb1d4f..47f302d5b4b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail235.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail235.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail235.d(12): Error: expression typeid(char) is not a valid template value argument
+fail_compilation/fail235.d(12): Error: expression `typeid(char)` is not a valid template value argument
---
*/
template Tuple(TPL...)
@@ -14,7 +14,7 @@ auto K = Tuple!(typeid(char));
/*
TEST_OUTPUT:
---
-fail_compilation/fail235.d(24): Error: expression typeid(char) is not a valid template value argument
+fail_compilation/fail235.d(24): Error: expression `typeid(char)` is not a valid template value argument
---
*/
template Alias(alias A)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail236.d b/gcc/testsuite/gdc.test/fail_compilation/fail236.d
index 255a88157ef..f63eb21a11c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail236.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail236.d
@@ -2,13 +2,13 @@
TEST_OUTPUT:
---
fail_compilation/fail236.d(14): Error: undefined identifier `x`
-fail_compilation/fail236.d(22): Error: template fail236.Templ2 cannot deduce function from argument types !()(int), candidates are:
-fail_compilation/fail236.d(12): fail236.Templ2(alias a)(x)
+fail_compilation/fail236.d(22): Error: template `fail236.Templ2` cannot deduce function from argument types `!()(int)`
+fail_compilation/fail236.d(12): Candidate is: `Templ2(alias a)(x)`
---
*/
-// Issue 870 - contradictory error messages for templates
-
+// https://issues.dlang.org/show_bug.cgi?id=870
+// contradictory error messages for templates
template Templ2(alias a)
{
void Templ2(x)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail2361.d b/gcc/testsuite/gdc.test/fail_compilation/fail2361.d
index 7cc402ee84b..a969495ccd2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail2361.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail2361.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail2361.d(13): Error: cannot modify immutable expression c
+fail_compilation/fail2361.d(14): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+fail_compilation/fail2361.d(14): Error: cannot modify `immutable` expression `c`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail237.d b/gcc/testsuite/gdc.test/fail_compilation/fail237.d
index 09ca94c9a37..c9300512c17 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail237.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail237.d
@@ -6,6 +6,6 @@ fail_compilation/fail237.d(11): while evaluating: `static assert(module f
---
*/
-// Issue 581 - Error message w/o line number in dot-instantiated template
-
+// https://issues.dlang.org/show_bug.cgi?id=581
+// Error message w/o line number in dot-instantiated template
static assert(.a!().b);
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail238_m32.d b/gcc/testsuite/gdc.test/fail_compilation/fail238_m32.d
index a312dc07290..b4cfbc91048 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail238_m32.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail238_m32.d
@@ -10,8 +10,8 @@ fail_compilation/fail238_m32.d(35): while evaluating `pragma(msg, M!(q))`
---
*/
-// Issue 581 - Error message w/o line number in dot-instantiated template
-
+// https://issues.dlang.org/show_bug.cgi?id=581
+// Error message w/o line number in dot-instantiated template
template X(){}
template D(string str){}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail238_m64.d b/gcc/testsuite/gdc.test/fail_compilation/fail238_m64.d
index dc7a50ea546..10e1da451e2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail238_m64.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail238_m64.d
@@ -10,8 +10,8 @@ fail_compilation/fail238_m64.d(35): while evaluating `pragma(msg, M!(q))`
---
*/
-// Issue 581 - Error message w/o line number in dot-instantiated template
-
+// https://issues.dlang.org/show_bug.cgi?id=581
+// Error message w/o line number in dot-instantiated template
template X(){}
template D(string str){}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail24.d b/gcc/testsuite/gdc.test/fail_compilation/fail24.d
index 84cf8e1814b..3b30175e52a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail24.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail24.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail24.d(11): Error: alias fail24.strtype conflicts with alias fail24.strtype at fail_compilation/fail24.d(10)
-fail_compilation/fail24.d(12): Error: alias fail24.strtype conflicts with alias fail24.strtype at fail_compilation/fail24.d(11)
-fail_compilation/fail24.d(13): Error: alias fail24.strtype conflicts with alias fail24.strtype at fail_compilation/fail24.d(12)
+fail_compilation/fail24.d(11): Error: alias `fail24.strtype` conflicts with alias `fail24.strtype` at fail_compilation/fail24.d(10)
+fail_compilation/fail24.d(12): Error: alias `fail24.strtype` conflicts with alias `fail24.strtype` at fail_compilation/fail24.d(11)
+fail_compilation/fail24.d(13): Error: alias `fail24.strtype` conflicts with alias `fail24.strtype` at fail_compilation/fail24.d(12)
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail240.d b/gcc/testsuite/gdc.test/fail_compilation/fail240.d
index 8a06b43401c..e32768adbf2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail240.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail240.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail240.d(9): Error: type F is not an expression
+fail_compilation/fail240.d(9): Error: type `F` is not an expression
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail241.d b/gcc/testsuite/gdc.test/fail_compilation/fail241.d
index 97cad211e2b..babd193033b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail241.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail241.d
@@ -1,8 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail241.d(16): Error: mutable method fail241.Foo.f is not callable using a const object
-fail_compilation/fail241.d(17): Error: mutable method fail241.Foo.g is not callable using a const object
+fail_compilation/fail241.d(18): Error: mutable method `fail241.Foo.f` is not callable using a `const` object
+fail_compilation/fail241.d(13): Consider adding `const` or `inout` here
+fail_compilation/fail241.d(19): Error: mutable method `fail241.Foo.g` is not callable using a `const` object
+fail_compilation/fail241.d(14): Consider adding `const` or `inout` here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail243.d b/gcc/testsuite/gdc.test/fail_compilation/fail243.d
index c8970ba915d..d9852ffc471 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail243.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail243.d
@@ -2,11 +2,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail243.d(23): Deprecation: class fail243.DepClass is deprecated
-fail_compilation/fail243.d(24): Deprecation: struct fail243.DepStruct is deprecated
-fail_compilation/fail243.d(25): Deprecation: union fail243.DepUnion is deprecated
-fail_compilation/fail243.d(26): Deprecation: enum fail243.DepEnum is deprecated
-fail_compilation/fail243.d(27): Deprecation: alias fail243.DepAlias is deprecated
+fail_compilation/fail243.d(23): Deprecation: class `fail243.DepClass` is deprecated
+fail_compilation/fail243.d(24): Deprecation: struct `fail243.DepStruct` is deprecated
+fail_compilation/fail243.d(25): Deprecation: union `fail243.DepUnion` is deprecated
+fail_compilation/fail243.d(26): Deprecation: enum `fail243.DepEnum` is deprecated
+fail_compilation/fail243.d(27): Deprecation: alias `fail243.DepAlias` is deprecated
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail244.d b/gcc/testsuite/gdc.test/fail_compilation/fail244.d
index d688e9ad2d0..757eb2bdbb1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail244.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail244.d
@@ -2,16 +2,16 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail244.d(27): Deprecation: variable fail244.StructWithDeps.value is deprecated
-fail_compilation/fail244.d(28): Deprecation: variable fail244.StructWithDeps.value is deprecated
-fail_compilation/fail244.d(29): Deprecation: variable fail244.StructWithDeps.value is deprecated
-fail_compilation/fail244.d(30): Deprecation: variable fail244.StructWithDeps.value is deprecated
-fail_compilation/fail244.d(32): Deprecation: variable fail244.StructWithDeps.staticValue is deprecated
-fail_compilation/fail244.d(33): Deprecation: variable fail244.StructWithDeps.staticValue is deprecated
-fail_compilation/fail244.d(34): Deprecation: variable fail244.StructWithDeps.staticValue is deprecated
-fail_compilation/fail244.d(35): Deprecation: variable fail244.StructWithDeps.staticValue is deprecated
-fail_compilation/fail244.d(36): Deprecation: variable fail244.StructWithDeps.staticValue is deprecated
-fail_compilation/fail244.d(37): Deprecation: variable fail244.StructWithDeps.staticValue is deprecated
+fail_compilation/fail244.d(27): Deprecation: variable `fail244.StructWithDeps.value` is deprecated
+fail_compilation/fail244.d(28): Deprecation: variable `fail244.StructWithDeps.value` is deprecated
+fail_compilation/fail244.d(29): Deprecation: variable `fail244.StructWithDeps.value` is deprecated
+fail_compilation/fail244.d(30): Deprecation: variable `fail244.StructWithDeps.value` is deprecated
+fail_compilation/fail244.d(32): Deprecation: variable `fail244.StructWithDeps.staticValue` is deprecated
+fail_compilation/fail244.d(33): Deprecation: variable `fail244.StructWithDeps.staticValue` is deprecated
+fail_compilation/fail244.d(34): Deprecation: variable `fail244.StructWithDeps.staticValue` is deprecated
+fail_compilation/fail244.d(35): Deprecation: variable `fail244.StructWithDeps.staticValue` is deprecated
+fail_compilation/fail244.d(36): Deprecation: variable `fail244.StructWithDeps.staticValue` is deprecated
+fail_compilation/fail244.d(37): Deprecation: variable `fail244.StructWithDeps.staticValue` is deprecated
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail245.d b/gcc/testsuite/gdc.test/fail_compilation/fail245.d
index 79e663dab1e..927f9413205 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail245.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail245.d
@@ -2,16 +2,16 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail245.d(27): Deprecation: variable fail245.ClassWithDeps.value is deprecated
-fail_compilation/fail245.d(28): Deprecation: variable fail245.ClassWithDeps.value is deprecated
-fail_compilation/fail245.d(29): Deprecation: variable fail245.ClassWithDeps.value is deprecated
-fail_compilation/fail245.d(30): Deprecation: variable fail245.ClassWithDeps.value is deprecated
-fail_compilation/fail245.d(32): Deprecation: variable fail245.ClassWithDeps.staticValue is deprecated
-fail_compilation/fail245.d(33): Deprecation: variable fail245.ClassWithDeps.staticValue is deprecated
-fail_compilation/fail245.d(34): Deprecation: variable fail245.ClassWithDeps.staticValue is deprecated
-fail_compilation/fail245.d(35): Deprecation: variable fail245.ClassWithDeps.staticValue is deprecated
-fail_compilation/fail245.d(36): Deprecation: variable fail245.ClassWithDeps.staticValue is deprecated
-fail_compilation/fail245.d(37): Deprecation: variable fail245.ClassWithDeps.staticValue is deprecated
+fail_compilation/fail245.d(27): Deprecation: variable `fail245.ClassWithDeps.value` is deprecated
+fail_compilation/fail245.d(28): Deprecation: variable `fail245.ClassWithDeps.value` is deprecated
+fail_compilation/fail245.d(29): Deprecation: variable `fail245.ClassWithDeps.value` is deprecated
+fail_compilation/fail245.d(30): Deprecation: variable `fail245.ClassWithDeps.value` is deprecated
+fail_compilation/fail245.d(32): Deprecation: variable `fail245.ClassWithDeps.staticValue` is deprecated
+fail_compilation/fail245.d(33): Deprecation: variable `fail245.ClassWithDeps.staticValue` is deprecated
+fail_compilation/fail245.d(34): Deprecation: variable `fail245.ClassWithDeps.staticValue` is deprecated
+fail_compilation/fail245.d(35): Deprecation: variable `fail245.ClassWithDeps.staticValue` is deprecated
+fail_compilation/fail245.d(36): Deprecation: variable `fail245.ClassWithDeps.staticValue` is deprecated
+fail_compilation/fail245.d(37): Deprecation: variable `fail245.ClassWithDeps.staticValue` is deprecated
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail2450.d b/gcc/testsuite/gdc.test/fail_compilation/fail2450.d
new file mode 100644
index 00000000000..82118d5de45
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail2450.d
@@ -0,0 +1,27 @@
+// https://issues.dlang.org/show_bug.cgi?id=2450
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail2450.d(22): Error: function expected before `()`, not `this.mixin Event!() clicked;
+` of type `void`
+fail_compilation/fail2450.d(25): Error: function expected before `()`, not `b.mixin Event!() clicked;
+` of type `void`
+---
+*/
+
+template Event()
+{
+ void opCall() { }
+ void opAddAssign(int i) { }
+}
+class Button {
+ mixin Event clicked;
+ void func()
+ {
+ clicked.opCall(); // works
+ this.clicked(); // works
+
+ auto b = new Button();
+ b.clicked(); // works
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail2456.d b/gcc/testsuite/gdc.test/fail_compilation/fail2456.d
index e8cf5abbbf5..08e11da2017 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail2456.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail2456.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail2456.d(14): Error: cannot put `scope(success)` statement inside finally block
+fail_compilation/fail2456.d(14): Error: cannot put `scope(success)` statement inside `finally` block
---
*/
void test_success()
@@ -18,7 +18,7 @@ void test_success()
/*
TEST_OUTPUT:
---
-fail_compilation/fail2456.d(31): Error: cannot put `scope(failure)` statement inside finally block
+fail_compilation/fail2456.d(31): Error: cannot put `scope(failure)` statement inside `finally` block
---
*/
void test_failure()
@@ -84,8 +84,8 @@ void test2456a()
/*
TEST_OUTPUT:
---
-fail_compilation/fail2456.d(96): Error: cannot put catch statement inside `scope(success)`
-fail_compilation/fail2456.d(108): Error: cannot put catch statement inside `scope(exit)`
+fail_compilation/fail2456.d(96): Error: cannot put `catch` statement inside `scope(success)`
+fail_compilation/fail2456.d(108): Error: cannot put `catch` statement inside `scope(exit)`
---
*/
void test2456b()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail246.d b/gcc/testsuite/gdc.test/fail_compilation/fail246.d
index c6214ff88dd..f48ddbb69f1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail246.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail246.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail246.d-mixin-11(11): Error: identifier expected, not `EOF`
-fail_compilation/fail246.d-mixin-11(11): Error: `;` expected after mixin
+fail_compilation/fail246.d-mixin-11(11): Error: identifier expected, not `End of File`
+fail_compilation/fail246.d-mixin-11(11): Error: `;` expected after `mixin`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail247.d b/gcc/testsuite/gdc.test/fail_compilation/fail247.d
index 1acf1a4105c..4805d9da057 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail247.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail247.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail247.d-mixin-9(9): Error: identifier expected, not `EOF`
-fail_compilation/fail247.d-mixin-9(9): Error: `;` expected after mixin
+fail_compilation/fail247.d-mixin-9(9): Error: identifier expected, not `End of File`
+fail_compilation/fail247.d-mixin-9(9): Error: `;` expected after `mixin`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail248.d b/gcc/testsuite/gdc.test/fail_compilation/fail248.d
index 185de133ec0..a15ec967040 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail248.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail248.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail248.d(9): Error: type int is not an expression
+fail_compilation/fail248.d(9): Error: type `int` is not an expression
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail249.d b/gcc/testsuite/gdc.test/fail_compilation/fail249.d
index 79a42891d03..82b291fe6ce 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail249.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail249.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail249.d(16): Error: invalid foreach aggregate `bar()`
+fail_compilation/fail249.d(16): Error: invalid `foreach` aggregate `bar()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail25.d b/gcc/testsuite/gdc.test/fail_compilation/fail25.d
index 7393a6c4a67..11c0f0be3d8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail25.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail25.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail25.d(14): Error: need 'this' for 'yuiop' of type 'int'
+fail_compilation/fail25.d(14): Error: need `this` for `yuiop` of type `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail250.d b/gcc/testsuite/gdc.test/fail_compilation/fail250.d
index c3fd7631aec..c6c08f79b81 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail250.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail250.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail250.d(10): Error: constructor fail250.A.this default constructor for structs only allowed with @disable, no body, and no parameters
+fail_compilation/fail250.d(10): Error: constructor `fail250.A.this` default constructor for structs only allowed with `@disable`, no body, and no parameters
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail251.d b/gcc/testsuite/gdc.test/fail_compilation/fail251.d
index 083f3a4b9c3..75cf6e28342 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail251.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail251.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/fail251.d(12): Error: undefined identifier `xs`
-fail_compilation/fail251.d(16): called from here: foo()
+fail_compilation/fail251.d(16): called from here: `foo()`
fail_compilation/fail251.d(16): while evaluating: `static assert(foo())`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail253.d b/gcc/testsuite/gdc.test/fail_compilation/fail253.d
index 14e88581870..bee7e31eb3d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail253.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail253.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail253.d(13): Error: variable fail253.main.x inout variables can only be declared inside inout functions
-fail_compilation/fail253.d(16): Error: cannot modify inout expression x
+fail_compilation/fail253.d(13): Error: variable `fail253.main.x` `inout` variables can only be declared inside `inout` functions
+fail_compilation/fail253.d(16): Error: cannot modify `inout` expression `x`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail256.d b/gcc/testsuite/gdc.test/fail_compilation/fail256.d
index 87120e2644f..ce182e47349 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail256.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail256.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail256.d(8): Error: incompatible types for (("foo"d) ~ ("bar"c)): 'dstring' and 'string'
+fail_compilation/fail256.d(8): Error: incompatible types for `("foo"d) ~ ("bar"c)`: `dstring` and `string`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail259.d b/gcc/testsuite/gdc.test/fail_compilation/fail259.d
index 802e037ae0c..0c31b23569c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail259.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail259.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail259.d(11): Error: function fail259.C.foo does not override any function
+fail_compilation/fail259.d(11): Error: function `fail259.C.foo` does not override any function
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail261.d b/gcc/testsuite/gdc.test/fail_compilation/fail261.d
index e25722c1d6d..73e062ce286 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail261.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail261.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail261.d(18): Error: invalid foreach aggregate `range`, define opApply(), range primitives, or use .tupleof
+fail_compilation/fail261.d(18): Error: invalid `foreach` aggregate `range`, define `opApply()`, range primitives, or use `.tupleof`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail262.d b/gcc/testsuite/gdc.test/fail_compilation/fail262.d
index 6d15e1a54d6..7c92c7c1826 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail262.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail262.d
@@ -5,8 +5,8 @@ fail_compilation/fail262.d(23): Error: function `const void fail262.B.f()` does
---
*/
-// Issue 1645 - can override base class' const method with non-const method
-
+// https://issues.dlang.org/show_bug.cgi?id=1645
+// can override base class' const method with non-const method
import core.stdc.stdio;
class A
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail263.d b/gcc/testsuite/gdc.test/fail_compilation/fail263.d
index 8cf9b20ac2b..0fa2f86a14a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail263.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail263.d
@@ -6,8 +6,8 @@ fail_compilation/fail263.d(19): cannot pass argument `cast(const(byte)*)A
---
*/
-// Issue 2766 - DMD hangs with 0%cpu
-
+// https://issues.dlang.org/show_bug.cgi?id=2766
+// DMD hangs with 0%cpu
const byte[] A = [ cast(byte)0 ];
void f(byte* p)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail265.d b/gcc/testsuite/gdc.test/fail_compilation/fail265.d
index 476ff17989e..55851098493 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail265.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail265.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail265.d-mixin-10(10): Error: found `EOF` instead of statement
+fail_compilation/fail265.d-mixin-10(10): Error: found `End of File` instead of statement
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail267.d b/gcc/testsuite/gdc.test/fail_compilation/fail267.d
index a6ebfac320b..fe0299404cf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail267.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail267.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail267.d(15): Error: template Bar() does not have property 'foo'
+fail_compilation/fail267.d(15): Error: template `Bar()` does not have property `foo`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail270.d b/gcc/testsuite/gdc.test/fail_compilation/fail270.d
index 8f31fd3b2f0..188fab87ac0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail270.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail270.d
@@ -2,8 +2,8 @@
TEST_OUTPUT:
---
fail_compilation/fail270.d(12): Error: string slice `[1 .. 0]` is out of bounds
-fail_compilation/fail270.d(12): Error: mixin fail270.Tuple!int.Tuple.Tuple!() error instantiating
-fail_compilation/fail270.d(14): Error: mixin fail270.Tuple!int error instantiating
+fail_compilation/fail270.d(12): Error: mixin `fail270.Tuple!int.Tuple.Tuple!()` error instantiating
+fail_compilation/fail270.d(14): Error: mixin `fail270.Tuple!int` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail272.d b/gcc/testsuite/gdc.test/fail_compilation/fail272.d
index 15325ad187f..508599a5dab 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail272.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail272.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail272.d(9): Error: circular reference to variable 'fail272.Ins!(Ins).Ins'
-fail_compilation/fail272.d(10): Error: template instance fail272.Ins!(Ins) error instantiating
+fail_compilation/fail272.d(9): Error: circular reference to variable `fail272.Ins!(Ins).Ins`
+fail_compilation/fail272.d(10): Error: template instance `fail272.Ins!(Ins)` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail273.d b/gcc/testsuite/gdc.test/fail_compilation/fail273.d
index 9393c85df32..bf93276b547 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail273.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail273.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail273.d(10): Error: alias fail273.b recursive alias declaration
+fail_compilation/fail273.d(10): Error: alias `fail273.b` recursive alias declaration
---
*/
-// Issue 1054 - regression: circular aliases cause compiler stack overflow
-
+// https://issues.dlang.org/show_bug.cgi?id=1054
+// regression: circular aliases cause compiler stack overflow
alias a b;
alias b a;
b x; // ICE #1
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail275.d b/gcc/testsuite/gdc.test/fail_compilation/fail275.d
index df3eca158f0..ba4bd75251f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail275.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail275.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail275.d(10): Error: circular reference to variable 'fail275.C.x'
+fail_compilation/fail275.d(10): Error: circular reference to variable `fail275.C.x`
---
*/
// REQUIRED_ARGS: -d
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail278.d b/gcc/testsuite/gdc.test/fail_compilation/fail278.d
index 8437acd6424..39b4e4e5c26 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail278.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail278.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail278.d(11): Error: template instance NONEXISTENT!() template 'NONEXISTENT' is not defined
-fail_compilation/fail278.d(12): Error: template instance fail278.F!() error instantiating
-fail_compilation/fail278.d(13): instantiated from here: Bar!(Foo)
+fail_compilation/fail278.d(11): Error: template instance `NONEXISTENT!()` template `NONEXISTENT` is not defined
+fail_compilation/fail278.d(12): Error: template instance `fail278.F!()` error instantiating
+fail_compilation/fail278.d(13): instantiated from here: `Bar!(Foo)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail2789.d b/gcc/testsuite/gdc.test/fail_compilation/fail2789.d
new file mode 100644
index 00000000000..2d6c8e2651a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail2789.d
@@ -0,0 +1,109 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=18385
+
+TEST_OUTPUT:
+---
+fail_compilation/fail2789.d(15): Error: function `fail2789.A2789.m()` conflicts with previous declaration at fail_compilation/fail2789.d(10)
+---
+*/
+#line 7
+
+class A2789
+{
+ int m()
+ {
+ return 1;
+ }
+
+ float m() // conflict
+ {
+ return 2.0;
+ }
+
+ float m() const // doen't conflict
+ {
+ return 3.0;
+ }
+
+ static void m() // no conflict
+ {
+ }
+}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail2789.d(49): Error: function `fail2789.f4()` conflicts with previous declaration at fail_compilation/fail2789.d(48)
+fail_compilation/fail2789.d(55): Error: function `fail2789.f6()` conflicts with previous declaration at fail_compilation/fail2789.d(54)
+---
+*/
+
+
+void f1();
+void f1() {} // ok
+
+void f2() {}
+void f2(); // ok
+
+void f3();
+void f3(); // ok
+
+void f4() {}
+void f4() {} // conflict
+
+void f5() @safe {}
+void f5() @system {} // no conflict because of attribute based overloading in in extern(D)
+
+auto f6() { return 10; } // int()
+auto f6() { return ""; } // string(), conflict
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail2789.d(67): Error: function `fail2789.f_ExternC1()` conflicts with previous declaration at fail_compilation/fail2789.d(66)
+fail_compilation/fail2789.d(70): Deprecation: function `fail2789.f_ExternC2` cannot overload `extern(C)` function at fail_compilation/fail2789.d(69)
+fail_compilation/fail2789.d(73): Deprecation: function `fail2789.f_ExternC3` cannot overload `extern(C)` function at fail_compilation/fail2789.d(72)
+---
+*/
+
+extern(C) void f_ExternC1() {}
+extern(C) void f_ExternC1() {} // conflict
+
+extern(C) void f_ExternC2() {}
+extern(C) void f_ExternC2(int) {} // conflict
+
+extern(C) void f_ExternC3(int) {}
+extern(C) void f_ExternC3() {} // conflict
+
+extern (D) void f_MixExtern1() {}
+extern (C) void f_MixExtern1() {} // no conflict because of different mangling
+
+extern (D) void f_MixExtern2(int) {}
+extern (C) void f_MixExtern2() {} // no error
+
+extern (C) void f_ExternC4(int sig);
+extern (C) void f_ExternC4(int sig) @nogc; // no error
+
+extern (C) void f_ExternC5(int sig) {}
+extern (C) void f_ExternC5(int sig) @nogc; // no error
+
+extern (C) void f_ExternC6(int sig);
+extern (C) void f_ExternC6(int sig) @nogc {} // no error
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail2789.d(103): Error: function `fail2789.mul14147(const(int[]) left, const(int[]) right)` conflicts with previous declaration at fail_compilation/fail2789.d(99)
+---
+*/
+struct S14147(alias func)
+{
+}
+pure auto mul14147(const int[] left, const int[] right)
+{
+ S14147!(a => a) s;
+}
+pure auto mul14147(const int[] left, const int[] right)
+{
+ S14147!(a => a) s;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail282.d b/gcc/testsuite/gdc.test/fail_compilation/fail282.d
index 0b1391bbd52..a25aa5cf531 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail282.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail282.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail282.d(13): Error: template instance fail282.Template!500 recursive expansion
+fail_compilation/fail282.d(13): Error: template instance `fail282.Template!500` recursive expansion exceeded allowed nesting limit
---
*/
-// Issue 2920 - recursive templates blow compiler stack
+// https://issues.dlang.org/show_bug.cgi?id=2920
+// recursive templates blow compiler stack
// template_class_09.
-
template Template(int i)
{
class Class : Template!(i + 1).Class
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail284.d b/gcc/testsuite/gdc.test/fail_compilation/fail284.d
index a33f2c597fb..abfad672701 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail284.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail284.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail284.d(19): Error: pure function 'fail284.foo' cannot call impure function pointer 'a'
+fail_compilation/fail284.d(19): Error: `pure` function `fail284.foo` cannot call impure function pointer `a`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail288.d b/gcc/testsuite/gdc.test/fail_compilation/fail288.d
index d6b68c995a4..4fdaddd1df1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail288.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail288.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail288.d(14): Error: case ranges not allowed in final switch
+fail_compilation/fail288.d(14): Error: case ranges not allowed in `final switch`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail291.d b/gcc/testsuite/gdc.test/fail_compilation/fail291.d
index 79ff2b0bdce..e8d9f3778f9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail291.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail291.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail291.d(9): Error: variable fail291.X cannot be declared to be a function
+fail_compilation/fail291.d(9): Error: variable `fail291.X` cannot be declared to be a function
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail296.d b/gcc/testsuite/gdc.test/fail_compilation/fail296.d
index 3f1e5d3f679..920854f0bf0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail296.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail296.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail296.d(10): Error: can only * a pointer, not a 'int'
+fail_compilation/fail296.d(10): Error: can only `*` a pointer, not a `int`
---
*/
-// Issue 3117 - dmd crash by *1
-
+// https://issues.dlang.org/show_bug.cgi?id=3117
+// dmd crash by *1
void main(){ *1; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail2962.d b/gcc/testsuite/gdc.test/fail_compilation/fail2962.d
index 06caadce3b1..59e86c1e17c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail2962.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail2962.d
@@ -4,9 +4,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail2962.d(14): Error: variable y cannot be read at compile time
-fail_compilation/fail2962.d(14): while looking for match for baz6!(int, y)
-fail_compilation/fail2962.d(22): Error: template instance fail2962.bar6!int error instantiating
+fail_compilation/fail2962.d(14): Error: variable `y` cannot be read at compile time
+fail_compilation/fail2962.d(14): while looking for match for `baz6!(int, y)`
+fail_compilation/fail2962.d(22): Error: template instance `fail2962.bar6!int` error instantiating
---
*/
T bar6(T)(T y)
@@ -26,9 +26,9 @@ void test6()
/*
TEST_OUTPUT:
---
-fail_compilation/fail2962.d(36): Error: variable x cannot be read at compile time
-fail_compilation/fail2962.d(36): while looking for match for baz4!(int, x)
-fail_compilation/imports/fail2962a.d(6): Error: template instance fail2962.bar4!int error instantiating
+fail_compilation/fail2962.d(36): Error: variable `x` cannot be read at compile time
+fail_compilation/fail2962.d(36): while looking for match for `baz4!(int, x)`
+fail_compilation/imports/fail2962a.d(6): Error: template instance `fail2962.bar4!int` error instantiating
---
*/
T bar4(T)(T x)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail297.d b/gcc/testsuite/gdc.test/fail_compilation/fail297.d
index fd2249db2ca..5fc3bbf8e57 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail297.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail297.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail297.d(30): Error: incompatible types for ((Bar()) + (baz())): 'Bar' and 'const(Bar)'
+fail_compilation/fail297.d(30): Error: incompatible types for `(Bar()) + (baz())`: `Bar` and `const(Bar)`
---
*/
-// Issue 1969 - ICE(cod1.c) using undefined operator with one const operand
-
-// 1969 ICE or wrong-code. D2 only. Internal error: backend\cod1.c 1673
+// https://issues.dlang.org/show_bug.cgi?id=1969
+// ICE(cod1.c) using undefined operator with one const operand
+// ICE or wrong-code. D2 only. Internal error: backend\cod1.c 1673
/* Root cause: BinExp::typeCombine() is checking for an _exact_ match, but
typeMerge() will return success.
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail299.d b/gcc/testsuite/gdc.test/fail_compilation/fail299.d
index 1bfdd82a4a2..ffe50679282 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail299.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail299.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail299.d(14): Error: more initializers than fields (0) of Foo
+fail_compilation/fail299.d(14): Error: more initializers than fields (0) of `Foo`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3.d b/gcc/testsuite/gdc.test/fail_compilation/fail3.d
index 0b7516cbc06..5c1ea9178bd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail3.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail3.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail3.d(41): Error: incompatible types for ((a) + (b)): both operands are of type 'vec2'
+fail_compilation/fail3.d(41): Error: incompatible types for `(a) + (b)`: both operands are of type `vec2`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail301.d b/gcc/testsuite/gdc.test/fail_compilation/fail301.d
index b012fc8c0f6..bf90f55507b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail301.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail301.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail301.d(11): Error: need 'this' for 'guard' of type 'int'
-fail_compilation/fail301.d(22): Error: template instance fail301.bug3305!0 error instantiating
+fail_compilation/fail301.d(11): Error: need `this` for `guard` of type `int`
+fail_compilation/fail301.d(22): Error: template instance `fail301.bug3305!0` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail302.d b/gcc/testsuite/gdc.test/fail_compilation/fail302.d
index 02c6b242c59..d1133b0eef0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail302.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail302.d
@@ -1,7 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail302.d(21): Error: cannot implicitly convert expression `1` of type `int` to `Bar`
+fail_compilation/fail302.d(23): Error: cannot implicitly convert expression `1` of type `int` to `Bar`
+fail_compilation/fail302.d(23): `bar = 1` is the first assignment of `bar` therefore it represents its initialization
+fail_compilation/fail302.d(23): `opAssign` methods are not used for initialization, but for subsequent assignments
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail303.d b/gcc/testsuite/gdc.test/fail_compilation/fail303.d
index 98223fa3b17..2c825fe3435 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail303.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail303.d
@@ -1,13 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail303.d(19): Error: double /= cdouble is undefined. Did you mean double /= cdouble.re ?
-fail_compilation/fail303.d(20): Error: ireal *= ireal is an undefined operation
-fail_compilation/fail303.d(21): Error: ireal *= creal is undefined. Did you mean ireal *= creal.im ?
-fail_compilation/fail303.d(22): Error: ireal %= creal is undefined. Did you mean ireal %= creal.im ?
-fail_compilation/fail303.d(23): Error: ireal += real is undefined (result is complex)
-fail_compilation/fail303.d(24): Error: ireal -= creal is undefined (result is complex)
-fail_compilation/fail303.d(25): Error: double -= idouble is undefined (result is complex)
+fail_compilation/fail303.d(18): Deprecation: use of imaginary type `ireal` is deprecated, use `real` instead
+fail_compilation/fail303.d(20): Error: `double /= cdouble` is undefined. Did you mean `double /= cdouble.re`?
+fail_compilation/fail303.d(21): Error: `ireal *= ireal` is an undefined operation
+fail_compilation/fail303.d(22): Error: `ireal *= creal` is undefined. Did you mean `ireal *= creal.im`?
+fail_compilation/fail303.d(23): Error: `ireal %= creal` is undefined. Did you mean `ireal %= creal.im`?
+fail_compilation/fail303.d(24): Error: `ireal += real` is undefined (result is complex)
+fail_compilation/fail303.d(25): Error: `ireal -= creal` is undefined (result is complex)
+fail_compilation/fail303.d(26): Error: `double -= idouble` is undefined (result is complex)
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail305.d b/gcc/testsuite/gdc.test/fail_compilation/fail305.d
index 3c9dc76854c..aef1624a07c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail305.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail305.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail305.d(10): Error: cannot return non-void from void function
+fail_compilation/fail305.d(10): Error: cannot return non-void from `void` function
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail309.d b/gcc/testsuite/gdc.test/fail_compilation/fail309.d
index e1c9bfab889..0fbfd590f9e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail309.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail309.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail309.d(10): Error: circular reference to variable 'fail309.S.x'
+fail_compilation/fail309.d(10): Error: circular reference to variable `fail309.S.x`
---
*/
// REQUIRED_ARGS: -d
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail310.d b/gcc/testsuite/gdc.test/fail_compilation/fail310.d
index 1dc3d5b0ee5..5b3c99ae656 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail310.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail310.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/fail310.d(10): Error: undefined identifier `Foo`, did you mean function `foo`?
-fail_compilation/fail310.d(14): Error: template instance fail310.foo!(1, 2) error instantiating
+fail_compilation/fail310.d(14): Error: template instance `fail310.foo!(1, 2)` error instantiating
fail_compilation/fail310.d(14): while evaluating: `static assert(foo!(1, 2)())`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail311.d b/gcc/testsuite/gdc.test/fail_compilation/fail311.d
index 1713069e5a0..eaacfabf6a6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail311.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail311.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/fail311.d(16): Error: undefined identifier `undefined`
-fail_compilation/fail311.d(25): Error: template instance fail311.foo!() error instantiating
+fail_compilation/fail311.d(25): Error: template instance `fail311.foo!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail312.d b/gcc/testsuite/gdc.test/fail_compilation/fail312.d
index 3823ee414c5..7260697e0ab 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail312.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail312.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail312.d(13): Error: incompatible types for ((a[]) == (b)): 'int[]' and 'short'
-fail_compilation/fail312.d(14): Error: incompatible types for ((a[]) <= (b)): 'int[]' and 'short'
+fail_compilation/fail312.d(13): Error: incompatible types for `(a[]) == (b)`: `int[]` and `short`
+fail_compilation/fail312.d(14): Error: incompatible types for `(a[]) <= (b)`: `int[]` and `short`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail313.d b/gcc/testsuite/gdc.test/fail_compilation/fail313.d
index a2c289059be..3a2c7eca507 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail313.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail313.d
@@ -1,10 +1,10 @@
/*
-REQUIRED_ARGS: -de
+EXTRA_FILES: imports/a313.d imports/b313.d imports/pkg313/package.d
TEST_OUTPUT:
---
-fail_compilation/fail313.d(15): Error: undefined identifier `b313` in package `imports`, perhaps add `static import imports.b313;`
-fail_compilation/fail313.d(22): Error: undefined identifier `core`
-fail_compilation/fail313.d(27): Error: undefined identifier `pkg313` in package `imports`, perhaps add `static import imports.pkg313;`
+fail_compilation/fail313.d(16): Error: undefined identifier `b313` in package `imports`, perhaps add `static import imports.b313;`
+fail_compilation/fail313.d(23): Error: undefined identifier `core`
+fail_compilation/fail313.d(28): Error: undefined identifier `pkg313` in package `imports`, perhaps add `static import imports.pkg313;`
---
*/
module test313;
@@ -23,7 +23,7 @@ void test2()
core.stdc.stdio.printf("");
}
-void test2()
+void test3()
{
imports.pkg313.bug();
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail314.d b/gcc/testsuite/gdc.test/fail_compilation/fail314.d
deleted file mode 100644
index cfbb0308f23..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail314.d
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail314.d(11): Error: declaration T is already defined
----
-*/
-
-struct foo
-{
- static if (is(int T == int)) {}
- static if (is(int T == int)) {}
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3144.d b/gcc/testsuite/gdc.test/fail_compilation/fail3144.d
index 04e808fc72d..ff01a032d7b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail3144.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail3144.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail3144.d(12): Error: break is not inside a loop or switch
-fail_compilation/fail3144.d(15): Error: break is not inside a loop or switch
+fail_compilation/fail3144.d(12): Error: `break` is not inside a loop or `switch`
+fail_compilation/fail3144.d(15): Error: `break` is not inside a loop or `switch`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail315.d b/gcc/testsuite/gdc.test/fail_compilation/fail315.d
index 7ce4a80f031..c7fd78f8fc1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail315.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail315.d
@@ -3,11 +3,11 @@ TEST_OUTPUT:
---
fail_compilation/fail315.d-mixin-16(16): Error: found `;` when expecting `,`
fail_compilation/fail315.d-mixin-16(16): Error: expression expected, not `}`
-fail_compilation/fail315.d-mixin-16(16): Error: found `EOF` when expecting `,`
-fail_compilation/fail315.d-mixin-16(16): Error: found `EOF` when expecting `]`
-fail_compilation/fail315.d-mixin-16(16): Error: found `EOF` when expecting `;` following return statement
-fail_compilation/fail315.d-mixin-16(16): Error: found `EOF` when expecting `}` following compound statement
-fail_compilation/fail315.d(21): Error: template instance fail315.foo!() error instantiating
+fail_compilation/fail315.d-mixin-16(16): Error: found `End of File` when expecting `,`
+fail_compilation/fail315.d-mixin-16(16): Error: found `End of File` when expecting `]`
+fail_compilation/fail315.d-mixin-16(16): Error: found `End of File` when expecting `;` following `return` statement
+fail_compilation/fail315.d-mixin-16(16): Error: found `End of File` when expecting `}` following compound statement
+fail_compilation/fail315.d(21): Error: template instance `fail315.foo!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail317.d b/gcc/testsuite/gdc.test/fail_compilation/fail317.d
index 05e7edb0598..d6a0f4d6e8b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail317.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail317.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail317.d(10): Error: function fail317.I.f has no function body with return type inference
+fail_compilation/fail317.d(10): Error: function `fail317.I.f` has no function body with return type inference
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail318.d b/gcc/testsuite/gdc.test/fail_compilation/fail318.d
index dd93803eacf..d99175e6eff 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail318.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail318.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail318.d(8): Error: function D main must return int or void
+fail_compilation/fail318.d(8): Error: function `D main` must return `int` or `void`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail319.d b/gcc/testsuite/gdc.test/fail_compilation/fail319.d
index 3c4459a89bd..cf56feecc53 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail319.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail319.d
@@ -1,7 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail319.d(13): Error: template instance fail319.f!(int, int) does not match template declaration f(T...)() if (T.length > 20)
+fail_compilation/fail319.d(16): Error: template instance `fail319.f!(int, int)` does not match template declaration `f(T...)()`
+ with `T = (int, int)`
+ must satisfy the following constraint:
+` T.length > 20`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail320.d b/gcc/testsuite/gdc.test/fail_compilation/fail320.d
index 3a80dd55ce3..24020b43b98 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail320.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail320.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: imports/fail320a.d imports/fail320b.d
TEST_OUTPUT:
---
-fail_compilation/fail320.d(10): Error: no overload matches for foo
+fail_compilation/fail320.d(11): Error: no overload matches for `foo`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail324.d b/gcc/testsuite/gdc.test/fail_compilation/fail324.d
deleted file mode 100644
index 931cb8dd820..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail324.d
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail324.d(16): Error: template instance doStuff!((i){ return i; }) cannot use local '__lambda1' as parameter to non-global template doStuff(alias fun)()
----
-*/
-
-struct Foo
-{
- void doStuff(alias fun)() {}
-}
-
-void main()
-{
- Foo foo;
- foo.doStuff!( (i) { return i; })();
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail325.d b/gcc/testsuite/gdc.test/fail_compilation/fail325.d
index e75a1c1326c..bf7f305b197 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail325.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail325.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail325.d(12): Error: template fun(T = int)(int w, int z) has no type
+fail_compilation/fail325.d(12): Error: template `fun(T = int)(int w, int z)` has no type
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail328.d b/gcc/testsuite/gdc.test/fail_compilation/fail328.d
index 7e9791339ac..12347e57909 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail328.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail328.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail328.d(12): Error: @safe function 'fail328.foo' cannot call @system function 'fail328.bar'
+fail_compilation/fail328.d(13): Error: `@safe` function `fail328.foo` cannot call `@system` function `fail328.bar`
+fail_compilation/fail328.d(9): `fail328.bar` is declared here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail329.d b/gcc/testsuite/gdc.test/fail_compilation/fail329.d
index f28304e35da..95aaaf445a9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail329.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail329.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail329.d(28): Error: variable fail329.A.foo.__ensure.result cannot modify result 'result' in contract
+fail_compilation/fail329.d(28): Error: variable `fail329.A.foo.__ensure.result` cannot modify result `result` in contract
---
*/
@@ -27,7 +27,7 @@ class A
assert(x == 7);
result++;
}
- body
+ do
{
return i;
}
@@ -49,7 +49,7 @@ class B : A
assert(result < 8);
assert(x == 7);
}
- body
+ do
{
return i - 1;
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail330.d b/gcc/testsuite/gdc.test/fail_compilation/fail330.d
index a3409db5d1a..94e91bbad3c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail330.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail330.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail330.d(9): Error: variable fail330.fun.result cannot modify result 'result' in contract
+fail_compilation/fail330.d(9): Error: variable `fail330.fun.result` cannot modify result `result` in contract
---
*/
int fun()
out(result) { result = 2; }
-body { return 1; }
+do { return 1; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail331.d b/gcc/testsuite/gdc.test/fail_compilation/fail331.d
index 8b79ea2a2c5..7f978016e41 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail331.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail331.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail331.d(10): Error: cannot use typeof(return) inside function foo with inferred return type
+fail_compilation/fail331.d(10): Error: cannot use `typeof(return)` inside function `foo` with inferred return type
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail332.d b/gcc/testsuite/gdc.test/fail_compilation/fail332.d
index 91f80464705..12164b92e9a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail332.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail332.d
@@ -21,7 +21,7 @@ void test()
{
foo();
foo(null);
-
+
baz("");
baz(3, null);
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail333.d b/gcc/testsuite/gdc.test/fail_compilation/fail333.d
index 717de24e608..89f9478f175 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail333.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail333.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail333.d(8): Error: forward reference to 'test'
+fail_compilation/fail333.d(8): Error: forward reference to `test`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail336.d b/gcc/testsuite/gdc.test/fail_compilation/fail336.d
index 2a6be957052..9df207120b3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail336.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail336.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail336.d(16): Error: struct S has constructors, cannot use { initializers }, use S( initializers ) instead
+fail_compilation/fail336.d(16): Error: struct `S` has constructors, cannot use `{ initializers }`, use `S( initializers )` instead
---
*/
-// Issue 3476 - C-style initializer for structs must be disallowed for structs with a constructor
-
+// https://issues.dlang.org/show_bug.cgi?id=3476
+// C-style initializer for structs must be disallowed for structs with a constructor
struct S
{
int a;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail337.d b/gcc/testsuite/gdc.test/fail_compilation/fail337.d
index cc86bd20f28..0a411e65f40 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail337.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail337.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail337.d(13): Error: static assert `0` is false
-fail_compilation/fail337.d(26): instantiated from here: bar!()
-fail_compilation/fail337.d(33): 100 recursive instantiations from here: foo!196
-fail_compilation/fail337.d(41): 253 recursive instantiations from here: baz!300
+fail_compilation/fail337.d(13): Error: static assert: `0` is false
+fail_compilation/fail337.d(26): instantiated from here: `bar!()`
+fail_compilation/fail337.d(33): 100 recursive instantiations from here: `foo!196`
+fail_compilation/fail337.d(41): 253 recursive instantiations from here: `baz!300`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail34.d b/gcc/testsuite/gdc.test/fail_compilation/fail34.d
index f910a2d167a..7a3c8071867 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail34.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail34.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail34.d(31): Error: duplicate `case "123"` in switch statement
+fail_compilation/fail34.d(31): Error: duplicate `case "123"` in `switch` statement
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail340.d b/gcc/testsuite/gdc.test/fail_compilation/fail340.d
index 57d40a7d8fd..3d83e6896ed 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail340.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail340.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail340.d(18): Error: variable fail340.w of type struct const(CopyTest) uses this(this), which is not allowed in static initialization
-fail_compilation/fail340.d(19): while evaluating: `static assert(w.x == 55.0000)`
+fail_compilation/fail340.d(18): Error: variable `fail340.w` of type struct `const(CopyTest)` uses `this(this)`, which is not allowed in static initialization
+fail_compilation/fail340.d(19): while evaluating: `static assert(w.x == 55.0)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail341.d b/gcc/testsuite/gdc.test/fail_compilation/fail341.d
index 8677d485a40..38bb6dcbc61 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail341.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail341.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail341.d(26): Error: struct fail341.S is not copyable because it is annotated with `@disable`
+fail_compilation/fail341.d(26): Error: struct `fail341.S` is not copyable because field `t` is not copyable
fail_compilation/fail341.d(27): Error: function `fail341.foo` cannot be used because it is annotated with `@disable`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail343.d b/gcc/testsuite/gdc.test/fail_compilation/fail343.d
index 19601a71fa5..5b7e9bfac61 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail343.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail343.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail343.d(22): Error: function fail343.TimedApp.run cannot override final function I.fail343.Timer.run
-fail_compilation/fail343.d(22): Error: function fail343.TimedApp.run cannot override final function Application.fail343.Application.run
+fail_compilation/fail343.d(22): Error: function `fail343.TimedApp.run` cannot override `final` function `I.fail343.Timer.run`
+fail_compilation/fail343.d(22): Error: function `fail343.TimedApp.run` cannot override `final` function `Application.fail343.Application.run`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail347.d b/gcc/testsuite/gdc.test/fail_compilation/fail347.d
index 61718df59e2..8d061f96fdb 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail347.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail347.d
@@ -1,9 +1,10 @@
/*
+EXTRA_FILES: imports/fail347a.d
TEST_OUTPUT:
---
-fail_compilation/fail347.d(21): Error: undefined identifier `bbr`, did you mean variable `bar`?
-fail_compilation/fail347.d(22): Error: no property 'ofo' for type 'S', did you mean 'fail347.S.foo'?
-fail_compilation/fail347.d(23): Error: undefined identifier `strlenx`, did you mean function `strlen`?
+fail_compilation/fail347.d(22): Error: undefined identifier `bbr`, did you mean variable `bar`?
+fail_compilation/fail347.d(23): Error: no property `ofo` for type `S`, did you mean `fail347.S.foo`?
+fail_compilation/fail347.d(24): Error: undefined identifier `strlenx`, did you mean function `strlen`?
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail349.d b/gcc/testsuite/gdc.test/fail_compilation/fail349.d
index 8100ab7fd2b..11a392c158c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail349.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail349.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail349.d(15): Error: function `fail349.bug6109throwing` is not nothrow
-fail_compilation/fail349.d(13): Error: nothrow function `fail349.bug6109noThrow` may throw
+fail_compilation/fail349.d(15): Error: function `fail349.bug6109throwing` is not `nothrow`
+fail_compilation/fail349.d(13): Error: `nothrow` function `fail349.bug6109noThrow` may throw
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail35.d b/gcc/testsuite/gdc.test/fail_compilation/fail35.d
index c56f2781d4c..c35dfe11fc2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail35.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail35.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail35.d(15): Error: variable t cannot be read at compile time
+fail_compilation/fail35.d(15): Error: variable `t` cannot be read at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail351.d b/gcc/testsuite/gdc.test/fail_compilation/fail351.d
index 04f21b2e9be..1762ec06a43 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail351.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail351.d
@@ -1,16 +1,16 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail351.d(14): Error: cast(uint)this.num[index] is not an lvalue
+fail_compilation/fail351.d(14): Error: expression `this.num[index]` of type `immutable(uint)` is not implicitly convertible to return type `ref uint`
---
*/
-// 2780
+// https://issues.dlang.org/show_bug.cgi?id=2780
struct Immutable {
immutable uint[2] num;
- ref uint opIndex(size_t index) immutable {
+ ref uint opIndex(size_t index) immutable return {
return num[index];
}
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail354.d b/gcc/testsuite/gdc.test/fail_compilation/fail354.d
index 55dbf52953b..8689f619857 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail354.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail354.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail354.d(11): Error: template instance T!N template 'T' is not defined
-fail_compilation/fail354.d(13): Error: template instance fail354.S!1 error instantiating
+fail_compilation/fail354.d(11): Error: template instance `T!N` template `T` is not defined
+fail_compilation/fail354.d(13): Error: template instance `fail354.S!1` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail355.d b/gcc/testsuite/gdc.test/fail_compilation/fail355.d
index d342d12df0f..1237801a51b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail355.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail355.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: imports/fail355.d
TEST_OUTPUT:
---
-fail_compilation/fail355.d(8): Error: module imports.fail355 import 'nonexistent' not found
+fail_compilation/fail355.d(9): Error: module `imports.fail355` import `nonexistent` not found
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail36.d b/gcc/testsuite/gdc.test/fail_compilation/fail36.d
index 47fb88477ea..96e022e33f2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail36.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail36.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail36.d(13): Error: template t(int L) does not have property 'a'
-fail_compilation/fail36.d(18): Error: mixin fail36.func.t!10 error instantiating
+fail_compilation/fail36.d(13): Error: template `t(int L)` does not have property `a`
+fail_compilation/fail36.d(18): Error: mixin `fail36.func.t!10` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3672.d b/gcc/testsuite/gdc.test/fail_compilation/fail3672.d
index 1622bd212e1..2f9bdf742b7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail3672.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail3672.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail3672.d(28): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(*p, 1) instead.
-fail_compilation/fail3672.d(32): Deprecation: read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(*sfp, 1) instead.
-fail_compilation/fail3672.d(32): Error: '*sfp += 1' is not a scalar, it is a shared(SF)
+fail_compilation/fail3672.d(28): Error: read-modify-write operations are not allowed for `shared` variables
+fail_compilation/fail3672.d(28): Use `core.atomic.atomicOp!"+="(*p, 1)` instead
+fail_compilation/fail3672.d(32): Error: none of the `opOpAssign` overloads of `SF` are callable for `*sfp` of type `shared(SF)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3703.d b/gcc/testsuite/gdc.test/fail_compilation/fail3703.d
index 0b4e260dccd..6b4edd5b629 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail3703.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail3703.d
@@ -1,5 +1,5 @@
-// Issue 3703 - static array assignment
-
+// https://issues.dlang.org/show_bug.cgi?id=3703
+// static array assignment
/*
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3753.d b/gcc/testsuite/gdc.test/fail_compilation/fail3753.d
deleted file mode 100644
index d20d8e9fc77..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail3753.d
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-TEST_OUTPUT:
----
-Error: cannot mix core.std.stdlib.alloca() and exception handling in _Dmain()
----
-*/
-
-import core.stdc.stdlib : alloca;
-import core.stdc.stdio;
-
-struct TheStruct
-{
- ~this()
- {
- printf("dtor()\n");
- }
-}
-
-void bar()
-{
- printf("bar()\n");
-}
-
-void main()
-{
- auto s = TheStruct();
- bar();
- auto a = alloca(16);
- printf("test()\n");
- version (DigitalMars)
- {
- version (Win32) static assert(0);
- version (linux)
- {
- static assert(0);
- }
- version (FreeBSD)
- {
- static assert(0);
- }
- version (OSX)
- {
- static assert(0);
- }
- }
- else
- static assert(0);
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail37_m32.d b/gcc/testsuite/gdc.test/fail_compilation/fail37_m32.d
index 996f98fcdf7..900d80da45f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail37_m32.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail37_m32.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail37_m32.d(9): Error: 'cast(float)4u / cast(float)8u - cast(float)2147483647' is not of integral type, it is a float
+fail_compilation/fail37_m32.d(9): Error: `cast(float)4u / cast(float)8u - cast(float)2147483647` is not of integral type, it is a `float`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail37_m64.d b/gcc/testsuite/gdc.test/fail_compilation/fail37_m64.d
index 2e979e78c67..5c0d1697a20 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail37_m64.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail37_m64.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail37_m64.d(9): Error: 'cast(float)4LU / cast(float)8LU - cast(float)2147483647' is not of integral type, it is a float
+fail_compilation/fail37_m64.d(9): Error: `cast(float)4LU / cast(float)8LU - cast(float)2147483647` is not of integral type, it is a `float`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail38.d b/gcc/testsuite/gdc.test/fail_compilation/fail38.d
index 0f2cd5f3570..344a075a708 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail38.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail38.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail38.d(12): Error: 'super' is only allowed in non-static class member functions
+fail_compilation/fail38.d(12): Error: `super` is only allowed in non-static class member functions
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3882.d b/gcc/testsuite/gdc.test/fail_compilation/fail3882.d
index b7d8a9789a9..27ddad41604 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail3882.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail3882.d
@@ -2,13 +2,13 @@
// PERMUTE_ARGS: -debug
/******************************************/
-// 3882
+// https://issues.dlang.org/show_bug.cgi?id=3882
/*
TEST_OUTPUT:
---
-fail_compilation/fail3882.d(23): Warning: calling fail3882.strictlyPure!int.strictlyPure without side effects discards return value of type int, prepend a cast(void) if intentional
-fail_compilation/fail3882.d(27): Warning: calling fp without side effects discards return value of type int, prepend a cast(void) if intentional
+fail_compilation/fail3882.d(23): Warning: calling `fail3882.strictlyPure!int.strictlyPure` without side effects discards return value of type `int`; prepend a `cast(void)` if intentional
+fail_compilation/fail3882.d(27): Warning: calling `fp` without side effects discards return value of type `int`; prepend a `cast(void)` if intentional
---
*/
@@ -22,7 +22,7 @@ void main()
int x = 3;
strictlyPure(x);
- // 12649
+ // https://issues.dlang.org/show_bug.cgi?id=12649
auto fp = &strictlyPure!int;
fp(x);
}
@@ -33,8 +33,10 @@ void main()
/*
TEST_OUTPUT:
---
-fail_compilation/fail3882.d(46): Warning: calling fail3882.f1 without side effects discards return value of type int, prepend a cast(void) if intentional
-fail_compilation/fail3882.d(47): Warning: calling fail3882.f2 without side effects discards return value of type int, prepend a cast(void) if intentional
+fail_compilation/fail3882.d(48): Warning: calling `fail3882.f1` without side effects discards return value of type `int`; prepend a `cast(void)` if intentional
+fail_compilation/fail3882.d(49): Warning: calling `fail3882.f2` without side effects discards return value of type `int`; prepend a `cast(void)` if intentional
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail39.d b/gcc/testsuite/gdc.test/fail_compilation/fail39.d
index 65472cf614e..c0bb0e16a07 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail39.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail39.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail39.d(11): Error: function fail39.main.__funcliteral2 cannot access frame of function D main
+fail_compilation/fail39.d(12): Error: function `fail39.main.__funcliteral2` cannot access function `foo` in frame of function `D main`
+fail_compilation/fail39.d(11): `foo` declared here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3990.d b/gcc/testsuite/gdc.test/fail_compilation/fail3990.d
index 6d17a7dea0e..aa0fa816b6f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail3990.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail3990.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail3990.d(12): Error: using * on an array is no longer supported; use *(arr1).ptr instead
-fail_compilation/fail3990.d(14): Error: using * on an array is no longer supported; use *(arr2).ptr instead
+fail_compilation/fail3990.d(12): Error: using `*` on an array is no longer supported; use `*(arr1).ptr` instead
+fail_compilation/fail3990.d(14): Error: using `*` on an array is no longer supported; use `*(arr2).ptr` instead
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail40.d b/gcc/testsuite/gdc.test/fail_compilation/fail40.d
index aa15db319b1..947a3afb795 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail40.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail40.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail40.d(11): Error: variable yuiop cannot be read at compile time
+fail_compilation/fail40.d(11): Error: variable `yuiop` cannot be read at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4082.d b/gcc/testsuite/gdc.test/fail_compilation/fail4082.d
index fc6ba7874f7..6f9ee613223 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4082.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4082.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4082.d(14): Error: destructor `fail4082.Foo.~this` is not nothrow
-fail_compilation/fail4082.d(12): Error: nothrow function `fail4082.test1` may throw
+fail_compilation/fail4082.d(14): Error: destructor `fail4082.Foo.~this` is not `nothrow`
+fail_compilation/fail4082.d(12): Error: `nothrow` function `fail4082.test1` may throw
---
*/
struct Foo
@@ -21,8 +21,8 @@ NEXT:
/*
TEST_OUTPUT:
---
-fail_compilation/fail4082.d(32): Error: destructor `fail4082.Bar.~this` is not nothrow
-fail_compilation/fail4082.d(32): Error: nothrow function `fail4082.test2` may throw
+fail_compilation/fail4082.d(32): Error: destructor `fail4082.Bar.~this` is not `nothrow`
+fail_compilation/fail4082.d(32): Error: `nothrow` function `fail4082.test2` may throw
---
*/
struct Bar
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail41.d b/gcc/testsuite/gdc.test/fail_compilation/fail41.d
index fa0e0c3cdcf..6d893950539 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail41.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail41.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail41.d(17): Error: cannot return non-void from void function
+fail_compilation/fail41.d(17): Error: cannot return non-void from `void` function
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail42.d b/gcc/testsuite/gdc.test/fail_compilation/fail42.d
index 5101ca142a2..c153d5b255c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail42.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail42.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail42.d(22): Error: struct fail42.Qwert no size because of forward reference
+fail_compilation/fail42.d(22): Error: struct `fail42.Qwert` no size because of forward reference
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375a.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375a.d
index 5ee2a311b30..a07f78f5e07 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375a.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375a.d(14): Warning: else is dangling, add { } after condition at fail_compilation/fail4375a.d(11)
+fail_compilation/fail4375a.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375a.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375b.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375b.d
index 0d16446cdc6..dbc299f8b48 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375b.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375b.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375b.d(12)
+fail_compilation/fail4375b.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375b.d(14)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375c.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375c.d
index 4e578aac95d..230c6563981 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375c.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375c.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375c.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375c.d(11)
+fail_compilation/fail4375c.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375c.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375d.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375d.d
index 8e9b4e776a9..75927798d63 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375d.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375d.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375d.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375d.d(11)
+fail_compilation/fail4375d.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375d.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375e.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375e.d
index ae809bc9367..3ccd4fb0a4d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375e.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375e.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375e.d(14): Warning: else is dangling, add { } after condition at fail_compilation/fail4375e.d(11)
+fail_compilation/fail4375e.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375e.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375f.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375f.d
index 5855b1b0eec..867fbffdb6a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375f.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375f.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375f.d(14): Warning: else is dangling, add { } after condition at fail_compilation/fail4375f.d(11)
+fail_compilation/fail4375f.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375f.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375g.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375g.d
index 4b9d12b9b46..6f452f539a4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375g.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375g.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375g.d(14): Warning: else is dangling, add { } after condition at fail_compilation/fail4375g.d(11)
+fail_compilation/fail4375g.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375g.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375h.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375h.d
index 72e3e14bef9..c0f1c5b8a8f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375h.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375h.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375h.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375h.d(13)
+fail_compilation/fail4375h.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375h.d(15)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375i.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375i.d
index 5801c61dcbc..b13c9e4e584 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375i.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375i.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375i.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375i.d(11)
+fail_compilation/fail4375i.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375i.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375j.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375j.d
index 9a87540cdcb..8f247005cd1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375j.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375j.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375j.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375j.d(11)
+fail_compilation/fail4375j.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375j.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375k.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375k.d
index bb8b7b2d6df..8c21feaff62 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375k.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375k.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375k.d-mixin-11(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375k.d-mixin-11(12)
+fail_compilation/fail4375k.d-mixin-13(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375k.d-mixin-13(14)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375l.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375l.d
index 965b0e72cbe..1c815eafb1e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375l.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375l.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375l.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375l.d(11)
+fail_compilation/fail4375l.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375l.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375m.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375m.d
index a6bdf1e80db..7cbf20f8023 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375m.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375m.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375m.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375m.d(12)
+fail_compilation/fail4375m.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375m.d(14)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375o.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375o.d
index c37b2196308..fbfde4ee2cf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375o.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375o.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375o.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375o.d(11)
+fail_compilation/fail4375o.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375o.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375r.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375r.d
index 61923e207d7..dfa77312dd9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375r.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375r.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375r.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375r.d(11)
+fail_compilation/fail4375r.d(19): Warning: else is dangling, add { } after condition at fail_compilation/fail4375r.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375s.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375s.d
index cd4e5a07e47..f85a1dbd80f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375s.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375s.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375s.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375s.d(11)
+fail_compilation/fail4375s.d(19): Warning: else is dangling, add { } after condition at fail_compilation/fail4375s.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375t.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375t.d
index 557761b7100..0fd1f4d9c1d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375t.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375t.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375t.d(14): Warning: else is dangling, add { } after condition at fail_compilation/fail4375t.d(11)
+fail_compilation/fail4375t.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375t.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375u.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375u.d
index 1028b787c20..8e2ea136146 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375u.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375u.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375u.d(13): Warning: else is dangling, add { } after condition at fail_compilation/fail4375u.d(11)
+fail_compilation/fail4375u.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375u.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375v.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375v.d
index f4a6089c0db..8b99a9c04f3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375v.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375v.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375v.d(13): Warning: else is dangling, add { } after condition at fail_compilation/fail4375v.d(11)
+fail_compilation/fail4375v.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375v.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375w.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375w.d
index ae1ac21839c..ecfffb6923b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375w.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375w.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375w.d(13): Warning: else is dangling, add { } after condition at fail_compilation/fail4375w.d(11)
+fail_compilation/fail4375w.d(15): Warning: else is dangling, add { } after condition at fail_compilation/fail4375w.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375x.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375x.d
index 33c8eb2a899..f29a6e33553 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375x.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375x.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375x.d(14): Warning: else is dangling, add { } after condition at fail_compilation/fail4375x.d(11)
+fail_compilation/fail4375x.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375x.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4375y.d b/gcc/testsuite/gdc.test/fail_compilation/fail4375y.d
index bc796312b1c..3111623983d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4375y.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4375y.d
@@ -3,7 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4375y.d(16): Warning: else is dangling, add { } after condition at fail_compilation/fail4375y.d(11)
+fail_compilation/fail4375y.d(18): Warning: else is dangling, add { } after condition at fail_compilation/fail4375y.d(13)
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail44.d b/gcc/testsuite/gdc.test/fail_compilation/fail44.d
index b8916a00991..e2ea40b2ede 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail44.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail44.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail44.d(18): Error: expression bar[i] is void and has no value
+fail_compilation/fail44.d(18): Error: expression `bar[i]` is `void` and has no value
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4421.d b/gcc/testsuite/gdc.test/fail_compilation/fail4421.d
index b82d70150ae..3aedfc31f3e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4421.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4421.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4421.d(16): Error: function fail4421.U1.__postblit destructors, postblits and invariants are not allowed in union U1
-fail_compilation/fail4421.d(17): Error: destructor fail4421.U1.~this destructors, postblits and invariants are not allowed in union U1
-fail_compilation/fail4421.d(18): Error: function fail4421.U1.__invariant1 destructors, postblits and invariants are not allowed in union U1
+fail_compilation/fail4421.d(16): Error: function `fail4421.U1.__postblit` destructors, postblits and invariants are not allowed in union `U1`
+fail_compilation/fail4421.d(17): Error: destructor `fail4421.U1.~this` destructors, postblits and invariants are not allowed in union `U1`
+fail_compilation/fail4421.d(18): Error: function `fail4421.U1.__invariant1` destructors, postblits and invariants are not allowed in union `U1`
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4448.d b/gcc/testsuite/gdc.test/fail_compilation/fail4448.d
index f8b2dbb2871..d5a0d4eb257 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4448.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4448.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4448.d(19): Error: label `L1` has no break
-fail_compilation/fail4448.d(26): called from here: bug4448()
+fail_compilation/fail4448.d(19): Error: label `L1` has no `break`
+fail_compilation/fail4448.d(26): called from here: `bug4448()`
fail_compilation/fail4448.d(26): while evaluating: `static assert(bug4448() == 3)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail45.d b/gcc/testsuite/gdc.test/fail_compilation/fail45.d
index 3380fe4fd5d..503ac610d6a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail45.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail45.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail45.d(10): Error: variable fail45.main.O cannot be declared to be a function
+fail_compilation/fail45.d(10): Error: variable `fail45.main.O` cannot be declared to be a function
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4517.d b/gcc/testsuite/gdc.test/fail_compilation/fail4517.d
deleted file mode 100644
index f3fe031a147..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4517.d
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail4517.d(16): Error: `enum` member `B` not represented in `final switch`
----
-*/
-
-enum E : ushort
-{
- A, B
-}
-
-void main()
-{
- E e;
- final switch(e)
- {
- case E.A:
- break;
- }
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4544.d b/gcc/testsuite/gdc.test/fail_compilation/fail4544.d
new file mode 100644
index 00000000000..cf1554da235
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4544.d
@@ -0,0 +1,23 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail4544.d(15): Error: character constant has multiple characters
+fail_compilation/fail4544.d(16): Error: `0x` isn't a valid integer literal, use `0x0` instead
+fail_compilation/fail4544.d(16): Error: no identifier for declarator `int`
+fail_compilation/fail4544.d(17): Error: unterminated character constant
+fail_compilation/fail4544.d(18): Error: character constant has multiple characters
+---
+*/
+
+int foo(char n, int m)
+{
+ int k = 5;
+ char c = 'asd';
+ int 0x = 'k';
+ foo('dasadasdaasdasdaslkdhasdlashdsalk, xxx);
+ goo('asdasdsa');
+ for (int i = 0; i < 10; i++)
+ {
+ k++;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail46.d b/gcc/testsuite/gdc.test/fail_compilation/fail46.d
index a62d1631678..9401b921862 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail46.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail46.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail46.d(19): Error: need 'this' for 'bug' of type 'int()'
+fail_compilation/fail46.d(19): Error: need `this` for `bug` of type `int()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4611.d b/gcc/testsuite/gdc.test/fail_compilation/fail4611.d
index 02fcc2ebb6f..030c47c69ca 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail4611.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4611.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail4611.d(15): Error: Vec[2147483647] size 4 * 2147483647 exceeds 0x7fffffff size limit for static array
+fail_compilation/fail4611.d(15): Error: `Vec[$n$]` size 4 * $n$ exceeds $?:windows+32=0x1000000|0x7fffffff$ size limit for static array
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4923.d b/gcc/testsuite/gdc.test/fail_compilation/fail4923.d
new file mode 100644
index 00000000000..3239cff0cf0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail4923.d
@@ -0,0 +1,13 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail4923.d(4): Error: immutable variable `bar` initialization is not allowed in `static this`
+fail_compilation/fail4923.d(4): Use `shared static this` instead.
+---
+*/
+#line 1
+immutable int bar;
+static this()
+{
+ bar = 42;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail50.d b/gcc/testsuite/gdc.test/fail_compilation/fail50.d
index 9cd29833877..36bf382af88 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail50.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail50.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail50.d(12): Error: need 'this' for address of a
-fail_compilation/fail50.d(12): Error: variable a cannot be read at compile time
+fail_compilation/fail50.d(12): Error: need `this` for address of `a`
+fail_compilation/fail50.d(12): Error: variable `a` cannot be read at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail51.d b/gcc/testsuite/gdc.test/fail_compilation/fail51.d
index 41a27ca7a22..b5a11d45c3c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail51.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail51.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail51.d(11): Error: interface fail51.B circular inheritance of interface
+fail_compilation/fail51.d(11): Error: interface `fail51.B` circular inheritance of interface
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail5153.d b/gcc/testsuite/gdc.test/fail_compilation/fail5153.d
new file mode 100644
index 00000000000..21de06e036a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail5153.d
@@ -0,0 +1,28 @@
+// https://issues.dlang.org/show_bug.cgi?id=5153
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail5153.d(26): Error: cannot implicitly convert expression `new Foo(0)` of type `Foo*` to `Foo`
+fail_compilation/fail5153.d(26): Perhaps remove the `new` keyword?
+---
+*/
+
+class Foo2
+{
+ this(int) {}
+}
+
+struct Foo {
+ int x;
+ this(int x_)
+ {
+ this.x = x_;
+ }
+
+ this(Foo2) {}
+}
+void main() {
+ Foo f = new Foo(0);
+ Foo f2 = new Foo2(0);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail52.d b/gcc/testsuite/gdc.test/fail_compilation/fail52.d
index 9b662de1348..9743be25721 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail52.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail52.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail52.d(10): Error: class fail52.C circular inheritance
+fail_compilation/fail52.d(10): Error: class `fail52.C` circular inheritance
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail54.d b/gcc/testsuite/gdc.test/fail_compilation/fail54.d
index aa95b1d15d6..e52c5336be6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail54.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail54.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail54.d(22): Error: incompatible types for ((0) == (Exception)): cannot use '==' with types
+fail_compilation/fail54.d(22): Error: incompatible types for `(0) == (Exception)`: cannot use `==` with types
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail59.d b/gcc/testsuite/gdc.test/fail_compilation/fail59.d
index 63fab5e0879..8c51311acce 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail59.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail59.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail59.d(50): Error: outer class C1 'this' needed to 'new' nested class C2
+fail_compilation/fail59.d(50): Error: outer class `C1` `this` needed to `new` nested class `C2`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail61.d b/gcc/testsuite/gdc.test/fail_compilation/fail61.d
index e9daa6163b9..90c3b39977d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail61.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail61.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail61.d(22): Error: no property 'B' for type 'fail61.A.B'
-fail_compilation/fail61.d(23): Error: no property 'B' for type 'fail61.A.B'
-fail_compilation/fail61.d(32): Error: no property 'A2' for type 'fail61.B2'
-fail_compilation/fail61.d(41): Error: this for foo needs to be type B3 not type fail61.C3
+fail_compilation/fail61.d(22): Error: no property `B` for type `fail61.A.B`
+fail_compilation/fail61.d(23): Error: no property `B` for type `fail61.A.B`
+fail_compilation/fail61.d(32): Error: no property `A2` for type `fail61.B2`
+fail_compilation/fail61.d(41): Error: `this` for `foo` needs to be type `B3` not type `fail61.C3`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6107.d b/gcc/testsuite/gdc.test/fail_compilation/fail6107.d
index 4d79b0c2590..656b3cf01f7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail6107.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail6107.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail6107.d(10): Error: variable fail6107.Foo.__ctor is not a constructor; identifiers starting with __ are reserved for the implementation
-fail_compilation/fail6107.d(14): Error: variable fail6107.Bar.__ctor is not a constructor; identifiers starting with __ are reserved for the implementation
+fail_compilation/fail6107.d(10): Error: variable `fail6107.Foo.__ctor` is not a constructor; identifiers starting with `__` are reserved for the implementation
+fail_compilation/fail6107.d(14): Error: variable `fail6107.Bar.__ctor` is not a constructor; identifiers starting with `__` are reserved for the implementation
---
*/
struct Foo
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail62.d b/gcc/testsuite/gdc.test/fail_compilation/fail62.d
index caa784300ed..6e939dbf91e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail62.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail62.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail62.d(11): Error: version Foo defined after use
+fail_compilation/fail62.d(11): Error: version `Foo` defined after use
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6242.d b/gcc/testsuite/gdc.test/fail_compilation/fail6242.d
index 08f5de3e299..cb2de2c539e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail6242.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail6242.d
@@ -6,4 +6,4 @@ fail_compilation/fail6242.d(9): Error: cannot implicitly override base class met
*/
class A { void fun(int) {} }
-class B : A { void fun(int x) in { assert(x > 0); } body {} }
+class B : A { void fun(int x) in { assert(x > 0); } do {} }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail63.d b/gcc/testsuite/gdc.test/fail_compilation/fail63.d
index 4af83ec0a09..e4080ebeac9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail63.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail63.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail63.d(11): Error: debug Foo defined after use
+fail_compilation/fail63.d(11): Error: debug `Foo` defined after use
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6334.d b/gcc/testsuite/gdc.test/fail_compilation/fail6334.d
index 3bb6b771c35..7abdb9e26a3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail6334.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail6334.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail6334.d(12): Error: static assert `0` is false
+fail_compilation/fail6334.d(13): Error: static assert: `0` is false
+fail_compilation/fail6334.d(11): instantiated from here: `T2!()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6453.d b/gcc/testsuite/gdc.test/fail_compilation/fail6453.d
index e1db9f4d5f6..f6ce89f73dc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail6453.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail6453.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail6453.d(13): Error: struct fail6453.S6453x mixing invariants with shared/synchronized differene is not supported
-fail_compilation/fail6453.d(18): Error: class fail6453.C6453y mixing invariants with shared/synchronized differene is not supported
-fail_compilation/fail6453.d(23): Error: class fail6453.C6453z mixing invariants with shared/synchronized differene is not supported
+fail_compilation/fail6453.d(13): Error: struct `fail6453.S6453x` mixing invariants with different `shared`/`synchronized` qualifiers is not supported
+fail_compilation/fail6453.d(18): Error: class `fail6453.C6453y` mixing invariants with different `shared`/`synchronized` qualifiers is not supported
+fail_compilation/fail6453.d(23): Error: class `fail6453.C6453z` mixing invariants with different `shared`/`synchronized` qualifiers is not supported
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail66.d b/gcc/testsuite/gdc.test/fail_compilation/fail66.d
index 5c352a52093..5820ca519b0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail66.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail66.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail66.d(11): Error: constructor fail66.C1.this missing initializer for const field y
+fail_compilation/fail66.d(11): Error: constructor `fail66.C1.this` missing initializer for const field `y`
---
*/
@@ -14,7 +14,7 @@ class C1
/*
TEST_OUTPUT:
---
-fail_compilation/fail66.d(28): Error: cannot modify const expression c.y
+fail_compilation/fail66.d(28): Error: cannot modify `const` expression `c.y`
---
*/
class C2
@@ -31,7 +31,7 @@ void test2()
/*
TEST_OUTPUT:
---
-fail_compilation/fail66.d(43): Error: cannot modify const expression this.y
+fail_compilation/fail66.d(43): Error: cannot modify `const` expression `this.y`
---
*/
class C3
@@ -47,7 +47,7 @@ class C3
/*
TEST_OUTPUT:
---
-fail_compilation/fail66.d(59): Error: cannot modify const expression x
+fail_compilation/fail66.d(59): Error: cannot modify `const` expression `x`
---
*/
class C4
@@ -63,7 +63,7 @@ class C4
/*
TEST_OUTPUT:
---
-fail_compilation/fail66.d(73): Error: cannot modify const expression z5
+fail_compilation/fail66.d(73): Error: cannot modify `const` expression `z5`
---
*/
const int z5;
@@ -76,7 +76,7 @@ void test5()
/*
TEST_OUTPUT:
---
-fail_compilation/fail66.d(89): Error: cannot modify const expression c.y
+fail_compilation/fail66.d(89): Error: cannot modify `const` expression `c.y`
---
*/
class C6
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6652.d b/gcc/testsuite/gdc.test/fail_compilation/fail6652.d
index 45a3f5a3fec..78cea67723c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail6652.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail6652.d
@@ -1,15 +1,15 @@
// PERMUTE_ARGS: -w -dw -de -d
/******************************************/
-// 6652
+// https://issues.dlang.org/show_bug.cgi?id=6652
/*
TEST_OUTPUT:
---
-fail_compilation/fail6652.d(20): Error: cannot modify const expression i
-fail_compilation/fail6652.d(25): Error: cannot modify const expression i
-fail_compilation/fail6652.d(30): Error: cannot modify const expression i
-fail_compilation/fail6652.d(35): Error: cannot modify const expression i
+fail_compilation/fail6652.d(20): Error: cannot modify `const` expression `i`
+fail_compilation/fail6652.d(25): Error: cannot modify `const` expression `i`
+fail_compilation/fail6652.d(30): Error: cannot modify `const` expression `i`
+fail_compilation/fail6652.d(35): Error: cannot modify `const` expression `i`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6795.d b/gcc/testsuite/gdc.test/fail_compilation/fail6795.d
index d714f2035a1..584a4679ccd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail6795.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail6795.d
@@ -1,34 +1,27 @@
-// 6795
+// https://issues.dlang.org/show_bug.cgi?id=6795
/*
TEST_OUTPUT:
---
-fail_compilation/fail6795.d(12): Error: constant 0 is not an lvalue
-fail_compilation/fail6795.d(13): Error: constant 0 is not an lvalue
----
-*/
-
-void main() {
- enum int[] array = [0];
- array[0]++;
- array[0] += 3;
-}
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail6795.d(31): Error: constant 0 is not an lvalue
-fail_compilation/fail6795.d(32): Error: constant 0 is not an lvalue
-fail_compilation/fail6795.d(33): Error: constant 0 is not an lvalue
+fail_compilation/fail6795.d(19): Error: `[0][0]` is not an lvalue and cannot be modified
+fail_compilation/fail6795.d(20): Error: `[0:0][0]` is not an lvalue and cannot be modified
+fail_compilation/fail6795.d(22): Error: `[0][0]` is not an lvalue and cannot be modified
+fail_compilation/fail6795.d(23): Error: `[0:0][0]` is not an lvalue and cannot be modified
+fail_compilation/fail6795.d(25): Error: `[0][0]` is not an lvalue and cannot be modified
+fail_compilation/fail6795.d(26): Error: `[0:0][0]` is not an lvalue and cannot be modified
---
*/
void test_wrong_line_num()
{
- enum int[] da = [0];
enum int[1] sa = [0];
enum int[int] aa = [0:0];
- da[0] += 3;
- sa[0] += 3;
- aa[0] += 3;
+ sa[0]++;
+ --aa[0];
+
+ sa[0] *= 3;
+ aa[0] /= 3;
+
+ auto ps = &sa[0];
+ auto pa = &aa[0];
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6889.d b/gcc/testsuite/gdc.test/fail_compilation/fail6889.d
index 4d86256b0bd..aa189770903 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail6889.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail6889.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail6889.d(16): Error: cannot goto out of `scope(success)` block
-fail_compilation/fail6889.d(17): Error: cannot goto in to `scope(success)` block
-fail_compilation/fail6889.d(19): Error: return statements cannot be in `scope(success)` bodies
-fail_compilation/fail6889.d(23): Error: continue is not inside `scope(success)` bodies
-fail_compilation/fail6889.d(24): Error: break is not inside `scope(success)` bodies
-fail_compilation/fail6889.d(29): Error: continue is not inside `scope(success)` bodies
-fail_compilation/fail6889.d(30): Error: break is not inside `scope(success)` bodies
+fail_compilation/fail6889.d(16): Error: cannot `goto` out of `scope(success)` block
+fail_compilation/fail6889.d(17): Error: cannot `goto` in to `scope(success)` block
+fail_compilation/fail6889.d(19): Error: `return` statements cannot be in `scope(success)` bodies
+fail_compilation/fail6889.d(23): Error: `continue` is not allowed inside `scope(success)` bodies
+fail_compilation/fail6889.d(24): Error: `break` is not allowed inside `scope(success)` bodies
+fail_compilation/fail6889.d(29): Error: `continue` is not allowed inside `scope(success)` bodies
+fail_compilation/fail6889.d(30): Error: `break` is not allowed inside `scope(success)` bodies
---
*/
void test_success()
@@ -46,7 +46,7 @@ L1:
/*
TEST_OUTPUT:
---
-fail_compilation/fail6889.d(56): Error: cannot goto in to `scope(failure)` block
+fail_compilation/fail6889.d(56): Error: cannot `goto` in to `scope(failure)` block
---
*/
void test_failure()
@@ -85,13 +85,13 @@ L1:
/*
TEST_OUTPUT:
---
-fail_compilation/fail6889.d(100): Error: cannot goto out of `scope(exit)` block
-fail_compilation/fail6889.d(101): Error: cannot goto in to `scope(exit)` block
-fail_compilation/fail6889.d(103): Error: return statements cannot be in `scope(exit)` bodies
-fail_compilation/fail6889.d(107): Error: continue is not inside `scope(exit)` bodies
-fail_compilation/fail6889.d(108): Error: break is not inside `scope(exit)` bodies
-fail_compilation/fail6889.d(113): Error: continue is not inside `scope(exit)` bodies
-fail_compilation/fail6889.d(114): Error: break is not inside `scope(exit)` bodies
+fail_compilation/fail6889.d(100): Error: cannot `goto` out of `scope(exit)` block
+fail_compilation/fail6889.d(101): Error: cannot `goto` in to `scope(exit)` block
+fail_compilation/fail6889.d(103): Error: `return` statements cannot be in `scope(exit)` bodies
+fail_compilation/fail6889.d(107): Error: `continue` is not allowed inside `scope(exit)` bodies
+fail_compilation/fail6889.d(108): Error: `break` is not allowed inside `scope(exit)` bodies
+fail_compilation/fail6889.d(113): Error: `continue` is not allowed inside `scope(exit)` bodies
+fail_compilation/fail6889.d(114): Error: `break` is not allowed inside `scope(exit)` bodies
---
*/
void test_exit()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7173.d b/gcc/testsuite/gdc.test/fail_compilation/fail7173.d
index 05ba7f9fc0a..2a2e46bb9f0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail7173.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail7173.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail7173.d(23): Error: expression `b1._a.opBinary(b2._a).fun()` is `void` and has no value
+fail_compilation/fail7173.d(23): Error: cannot implicitly convert expression `b1._a.opBinary(b2._a).fun()` of type `void` to `B`
---
*/
struct A{
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail73.d b/gcc/testsuite/gdc.test/fail_compilation/fail73.d
index dc83e5d3228..b9b480d6cda 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail73.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail73.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail73.d(20): Error: case not in switch statement
+fail_compilation/fail73.d(20): Error: `case` not in `switch` statement
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7352.d b/gcc/testsuite/gdc.test/fail_compilation/fail7352.d
new file mode 100644
index 00000000000..f5e8f9b3759
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail7352.d
@@ -0,0 +1,52 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail7352.d(42): Error: template instance `Type!(1)` does not match template declaration `Type(T)`
+fail_compilation/fail7352.d(43): Error: template instance `Type!(b)` does not match template declaration `Type(T)`
+fail_compilation/fail7352.d(43): `b` is not a type
+fail_compilation/fail7352.d(44): Error: template instance `Type!(function () pure nothrow @nogc @safe => 1)` does not match template declaration `Type(T)`
+fail_compilation/fail7352.d(45): Error: template instance `Type!(fun)` does not match template declaration `Type(T)`
+fail_compilation/fail7352.d(45): `fun` is not a type
+fail_compilation/fail7352.d(47): Error: template instance `Immutable!int` does not match template declaration `Immutable(T : immutable(T))`
+fail_compilation/fail7352.d(49): Error: template instance `Value!int` does not match template declaration `Value(string s)`
+fail_compilation/fail7352.d(50): Error: template instance `Value!(1)` does not match template declaration `Value(string s)`
+fail_compilation/fail7352.d(51): Error: template instance `Value!(fun)` does not match template declaration `Value(string s)`
+fail_compilation/fail7352.d(51): `fun` is not of a value of type `string`
+---
+*/
+
+template Type(T)
+{
+}
+
+template Immutable(T : immutable(T))
+{
+ alias Immutable = T;
+}
+
+template Value(string s)
+{
+ auto x = s;
+}
+
+int fun(int i)
+{
+ return i;
+}
+
+void main()
+{
+ enum a = 1;
+ int b;
+
+ Type!a testTypeValue;
+ Type!b testTypeVar;
+ Type!(() => 1) testTypeFuncLiteral;
+ Type!fun testTypeFunc;
+
+ Immutable!int testImmutable;
+
+ auto testValueType = Value!int.x;
+ auto testValueWrongType = Value!a.x;
+ auto testValueFunc = Value!fun.x;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail74.d b/gcc/testsuite/gdc.test/fail_compilation/fail74.d
index c038b20a5c1..79449eb1db3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail74.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail74.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail74.d(13): Error: cannot append type C[1] to type C[1]
+fail_compilation/fail74.d(13): Error: cannot append type `C[1]` to type `C[1]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7443.d b/gcc/testsuite/gdc.test/fail_compilation/fail7443.d
new file mode 100644
index 00000000000..2c5c9400746
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail7443.d
@@ -0,0 +1,14 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail7443.d(11): Error: `static` has no effect on a constructor inside a `static` block. Use `static this()`
+fail_compilation/fail7443.d(12): Error: `shared static` has no effect on a constructor inside a `shared static` block. Use `shared static this()`
+---
+*/
+
+class Foo
+{
+ public static { this() {}}
+ public shared static { this() {}}
+ public {this(int) {}}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail75.d b/gcc/testsuite/gdc.test/fail_compilation/fail75.d
index 1059e36bd62..0b0b8e09dde 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail75.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail75.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail75.d(13): Error: cannot append type fail75.C to type C[1]
+fail_compilation/fail75.d(13): Error: cannot append type `fail75.C` to type `C[1]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail76.d b/gcc/testsuite/gdc.test/fail_compilation/fail76.d
index c2b7e59064f..673fc7a13ff 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail76.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail76.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail76.d(9): Error: alias fail76.a conflicts with alias fail76.a at fail_compilation/fail76.d(8)
+fail_compilation/fail76.d(9): Error: alias `fail76.a` conflicts with alias `fail76.a` at fail_compilation/fail76.d(8)
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7848.d b/gcc/testsuite/gdc.test/fail_compilation/fail7848.d
index 77fcdfa3501..4fc269eed4c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail7848.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail7848.d
@@ -3,26 +3,18 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail7848.d(35): Error: pure function 'fail7848.C.__unittestL33_$n$' cannot call impure function 'fail7848.func'
-fail_compilation/fail7848.d(35): Error: @safe function 'fail7848.C.__unittestL33_$n$' cannot call @system function 'fail7848.func'
-fail_compilation/fail7848.d(35): Error: @nogc function 'fail7848.C.__unittestL33_$n$' cannot call non-@nogc function 'fail7848.func'
-fail_compilation/fail7848.d(35): Error: function `fail7848.func` is not nothrow
-fail_compilation/fail7848.d(33): Error: nothrow function `fail7848.C.__unittestL33_$n$` may throw
-fail_compilation/fail7848.d(40): Error: pure function 'fail7848.C.__invariant2' cannot call impure function 'fail7848.func'
-fail_compilation/fail7848.d(40): Error: @safe function 'fail7848.C.__invariant2' cannot call @system function 'fail7848.func'
-fail_compilation/fail7848.d(40): Error: @nogc function 'fail7848.C.__invariant2' cannot call non-@nogc function 'fail7848.func'
-fail_compilation/fail7848.d(40): Error: function `fail7848.func` is not nothrow
-fail_compilation/fail7848.d(38): Error: nothrow function `fail7848.C.__invariant2` may throw
-fail_compilation/fail7848.d(45): Error: pure allocator 'fail7848.C.new' cannot call impure function 'fail7848.func'
-fail_compilation/fail7848.d(45): Error: @safe allocator 'fail7848.C.new' cannot call @system function 'fail7848.func'
-fail_compilation/fail7848.d(45): Error: @nogc allocator 'fail7848.C.new' cannot call non-@nogc function 'fail7848.func'
-fail_compilation/fail7848.d(45): Error: function `fail7848.func` is not nothrow
-fail_compilation/fail7848.d(43): Error: nothrow allocator `fail7848.C.new` may throw
-fail_compilation/fail7848.d(51): Error: pure deallocator 'fail7848.C.delete' cannot call impure function 'fail7848.func'
-fail_compilation/fail7848.d(51): Error: @safe deallocator 'fail7848.C.delete' cannot call @system function 'fail7848.func'
-fail_compilation/fail7848.d(51): Error: @nogc deallocator 'fail7848.C.delete' cannot call non-@nogc function 'fail7848.func'
-fail_compilation/fail7848.d(51): Error: function `fail7848.func` is not nothrow
-fail_compilation/fail7848.d(49): Error: nothrow deallocator `fail7848.C.delete` may throw
+fail_compilation/fail7848.d(27): Error: `pure` function `fail7848.C.__unittest_L25_C30` cannot call impure function `fail7848.func`
+fail_compilation/fail7848.d(27): Error: `@safe` function `fail7848.C.__unittest_L25_C30` cannot call `@system` function `fail7848.func`
+fail_compilation/fail7848.d(21): `fail7848.func` is declared here
+fail_compilation/fail7848.d(27): Error: `@nogc` function `fail7848.C.__unittest_L25_C30` cannot call non-@nogc function `fail7848.func`
+fail_compilation/fail7848.d(27): Error: function `fail7848.func` is not `nothrow`
+fail_compilation/fail7848.d(25): Error: `nothrow` function `fail7848.C.__unittest_L25_C30` may throw
+fail_compilation/fail7848.d(32): Error: `pure` function `fail7848.C.__invariant1` cannot call impure function `fail7848.func`
+fail_compilation/fail7848.d(32): Error: `@safe` function `fail7848.C.__invariant1` cannot call `@system` function `fail7848.func`
+fail_compilation/fail7848.d(21): `fail7848.func` is declared here
+fail_compilation/fail7848.d(32): Error: `@nogc` function `fail7848.C.__invariant1` cannot call non-@nogc function `fail7848.func`
+fail_compilation/fail7848.d(32): Error: function `fail7848.func` is not `nothrow`
+fail_compilation/fail7848.d(30): Error: `nothrow` function `fail7848.C.__invariant1` may throw
---
*/
@@ -39,15 +31,4 @@ class C
{
func();
}
-
- @safe pure nothrow @nogc new (size_t sz)
- {
- func();
- return null;
- }
-
- @safe pure nothrow @nogc delete (void* p)
- {
- func();
- }
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7862.d b/gcc/testsuite/gdc.test/fail_compilation/fail7862.d
index a81bc650b85..b408b44692d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail7862.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail7862.d
@@ -3,12 +3,12 @@ TEST_OUTPUT:
---
A: false
A: false
-fail_compilation/fail7862.d(26): Error: template instance nonExistent!() template 'nonExistent' is not defined
-fail_compilation/fail7862.d(25): Error: template instance fail7862.B!(A) error instantiating
+fail_compilation/fail7862.d(26): Error: template instance `nonExistent!()` template `nonExistent` is not defined
+fail_compilation/fail7862.d(25): Error: template instance `fail7862.B!(A)` error instantiating
---
*/
-// 7862
+// https://issues.dlang.org/show_bug.cgi?id=7862
template B(T) {
mixin(
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail79.d b/gcc/testsuite/gdc.test/fail_compilation/fail79.d
index da06984e9ef..93fb7cbf3a6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail79.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail79.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail79.d(13): Error: incompatible types for ((& a) + (& b)): both operands are of type 'int*'
+fail_compilation/fail79.d(13): Error: incompatible types for `(& a) + (& b)`: both operands are of type `int*`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7903.d b/gcc/testsuite/gdc.test/fail_compilation/fail7903.d
index 7759f1ada17..18168ea2361 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail7903.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail7903.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail7903.d(21): Error: variable fail7903.F1.x Field members of a synchronized class cannot be public
-fail_compilation/fail7903.d(22): Error: variable fail7903.F1.y Field members of a synchronized class cannot be export
-fail_compilation/fail7903.d(27): Error: variable fail7903.F2.x Field members of a synchronized class cannot be public
+fail_compilation/fail7903.d(21): Error: variable `fail7903.F1.x` Field members of a `synchronized` class cannot be `public`
+fail_compilation/fail7903.d(22): Error: variable `fail7903.F1.y` Field members of a `synchronized` class cannot be `export`
+fail_compilation/fail7903.d(27): Error: variable `fail7903.F2.x` Field members of a `synchronized` class cannot be `public`
---
*/
synchronized class K1
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail8009.d b/gcc/testsuite/gdc.test/fail_compilation/fail8009.d
index 19a0712f838..a06dec58664 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail8009.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail8009.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail8009.d(9): Error: template `fail8009.filter` cannot deduce function from argument types `!()(void)`, candidates are:
-fail_compilation/fail8009.d(8): `filter(R)(scope bool delegate(ref BAD!R) func)`
+fail_compilation/fail8009.d(9): Error: template `fail8009.filter` cannot deduce function from argument types `!()(void)`
+fail_compilation/fail8009.d(8): Candidate is: `filter(R)(scope bool delegate(ref BAD!R) func)`
---
*/
void filter(R)(scope bool delegate(ref BAD!R) func) { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail809.d b/gcc/testsuite/gdc.test/fail_compilation/fail809.d
new file mode 100644
index 00000000000..b83639b0d08
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail809.d
@@ -0,0 +1,12 @@
+// REQUIRED_ARGS: -preview=dip1000
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail809.d(11): Error: scope variable `dg_` may not be returned
+---
+*/
+int delegate() test(lazy int dg)
+{
+ int delegate() dg_ = &dg;
+ return dg_;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail80_m32.d b/gcc/testsuite/gdc.test/fail_compilation/fail80_m32.d
index fb2f6fa2b90..11facfd9fef 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail80_m32.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail80_m32.d
@@ -18,8 +18,8 @@ class ResourceManager
class Test
{
- import std.file;
- import std.path;
+
+
static Image[] images;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail80_m64.d b/gcc/testsuite/gdc.test/fail_compilation/fail80_m64.d
index 52d23d305fb..59c890ed60d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail80_m64.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail80_m64.d
@@ -18,8 +18,8 @@ class ResourceManager
class Test
{
- import std.file;
- import std.path;
+
+
static Image[] images;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail8217.d b/gcc/testsuite/gdc.test/fail_compilation/fail8217.d
index e74b7c35f2f..477ca63a9e8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail8217.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail8217.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail8217.d(22): Error: this for foo needs to be type D not type fail8217.D.C
+fail_compilation/fail8217.d(22): Error: `this` for `foo` needs to be type `D` not type `fail8217.D.C`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail8262.d b/gcc/testsuite/gdc.test/fail_compilation/fail8262.d
new file mode 100644
index 00000000000..2df1bca4fe3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail8262.d
@@ -0,0 +1,33 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/fail8262.d(32): Error: initializer must be an expression, not `Tuple8262!1`
+fail_compilation/fail8262.d(27): Error: template instance `fail8262.T8262!(Tuple8262!1)` error instantiating
+fail_compilation/fail8262.d(19): Error: cannot implicitly convert expression `S(0)` of type `S` to `int`
+---
+ * https://issues.dlang.org/show_bug.cgi?id=8262
+ */
+
+template Seq(T...) { alias T Seq; }
+
+struct S
+{
+ int s;
+ alias Seq!s _;
+ alias _ this;
+}
+
+int si = S.init;
+
+struct Tuple8262(T...)
+{
+ alias T expand;
+ alias expand this;
+}
+
+auto data = T8262!(Tuple8262!1);
+//pragma(msg, data);
+
+template T8262(T)
+{
+ immutable(int) T8262 = T;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail8373.d b/gcc/testsuite/gdc.test/fail_compilation/fail8373.d
index d9217078bd6..2fb478d7acf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail8373.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail8373.d
@@ -1,14 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail8373.d(21): Error: fail8373.fun1 called with argument types (int) matches both:
-fail_compilation/fail8373.d(15): fail8373.fun1!().fun1!int.fun1(int)
+fail_compilation/fail8373.d(21): Error: `fail8373.fun1` called with argument types `(int)` matches both:
+fail_compilation/fail8373.d(15): `fail8373.fun1!().fun1!int.fun1(int)`
and:
-fail_compilation/fail8373.d(16): fail8373.fun1!int.fun1(int)
-fail_compilation/fail8373.d(22): Error: fail8373.fun2 called with argument types (int) matches both:
-fail_compilation/fail8373.d(18): fail8373.fun2!int.fun2(int)
+fail_compilation/fail8373.d(16): `fail8373.fun1!int.fun1(int)`
+fail_compilation/fail8373.d(22): Error: `fail8373.fun2` called with argument types `(int)` matches both:
+fail_compilation/fail8373.d(18): `fail8373.fun2!int.fun2(int)`
and:
-fail_compilation/fail8373.d(19): fail8373.fun2!().fun2!int.fun2(int)
+fail_compilation/fail8373.d(19): `fail8373.fun2!().fun2!int.fun2(int)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail86.d b/gcc/testsuite/gdc.test/fail_compilation/fail86.d
index a0ccb7c2718..4f56e95ca6f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail86.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail86.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail86.d(12): Error: alias Foo recursive alias declaration
+fail_compilation/fail86.d(12): Error: alias `Foo` recursive alias declaration
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail8631.d b/gcc/testsuite/gdc.test/fail_compilation/fail8631.d
index 3aada74c42a..4f5b0763d48 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail8631.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail8631.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail8631.d(14): Error: function fail8631.D.foo does not override any function, did you mean to override 'fail8631.B.foo'?
+fail_compilation/fail8631.d(14): Error: function `shared const int fail8631.D.foo()` does not override any function, did you mean to override `immutable int fail8631.B.foo()`?
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail8724.d b/gcc/testsuite/gdc.test/fail_compilation/fail8724.d
index 7f9cba2bc4a..c5ca98b4738 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail8724.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail8724.d
@@ -3,7 +3,7 @@
TEST_OUTPUT:
---
fail_compilation/fail8724.d(14): Error: `object.Exception` is thrown but not caught
-fail_compilation/fail8724.d(12): Error: nothrow constructor `fail8724.Foo.this` may throw
+fail_compilation/fail8724.d(12): Error: `nothrow` constructor `fail8724.Foo.this` may throw
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9.d b/gcc/testsuite/gdc.test/fail_compilation/fail9.d
index 6253774c823..08789954bb1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9.d(23): Error: no property 'Vector' for type 'fail9.Vector!int'
+fail_compilation/fail9.d(23): Error: no property `Vector` for type `fail9.Vector!int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9063.d b/gcc/testsuite/gdc.test/fail_compilation/fail9063.d
index 962fb04ab2a..36f04ade23c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9063.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9063.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9063.d(9): Error: static assert "msg"
+fail_compilation/fail9063.d(9): Error: static assert: "msg"
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9081.d b/gcc/testsuite/gdc.test/fail_compilation/fail9081.d
index 86c888742ce..3d65beaeef7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9081.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9081.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9081.d(12): Error: package core has no type
-fail_compilation/fail9081.d(13): Error: package stdc has no type
-fail_compilation/fail9081.d(14): Error: module stdio has no type
+fail_compilation/fail9081.d(12): Error: package `core` has no type
+fail_compilation/fail9081.d(13): Error: package `stdc` has no type
+fail_compilation/fail9081.d(14): Error: module `stdio` has no type
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail91.d b/gcc/testsuite/gdc.test/fail_compilation/fail91.d
index 59f80fd891d..41be80140ef 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail91.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail91.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail91.d(12): Error: struct fail91.S unknown size
+fail_compilation/fail91.d(12): Error: struct `fail91.S` unknown size
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9199.d b/gcc/testsuite/gdc.test/fail_compilation/fail9199.d
index 00a87c39907..2a5cd012e31 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9199.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9199.d
@@ -2,12 +2,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9199.d(13): Error: function fail9199.fc without 'this' cannot be const
-fail_compilation/fail9199.d(14): Error: function fail9199.fi without 'this' cannot be immutable
-fail_compilation/fail9199.d(15): Error: function fail9199.fw without 'this' cannot be inout
-fail_compilation/fail9199.d(16): Error: function fail9199.fs without 'this' cannot be shared
-fail_compilation/fail9199.d(17): Error: function fail9199.fsc without 'this' cannot be shared const
-fail_compilation/fail9199.d(18): Error: function fail9199.fsw without 'this' cannot be shared inout
+fail_compilation/fail9199.d(13): Error: function `fail9199.fc` without `this` cannot be `const`
+fail_compilation/fail9199.d(14): Error: function `fail9199.fi` without `this` cannot be `immutable`
+fail_compilation/fail9199.d(15): Error: function `fail9199.fw` without `this` cannot be `inout`
+fail_compilation/fail9199.d(16): Error: function `fail9199.fs` without `this` cannot be `shared`
+fail_compilation/fail9199.d(17): Error: function `fail9199.fsc` without `this` cannot be `shared const`
+fail_compilation/fail9199.d(18): Error: function `fail9199.fsw` without `this` cannot be `shared inout`
---
*/
void fc() const {}
@@ -20,12 +20,12 @@ void fsw() shared inout {}
/*
TEST_OUTPUT:
---
-fail_compilation/fail9199.d(33): Error: function fail9199.C.fc without 'this' cannot be const
-fail_compilation/fail9199.d(34): Error: function fail9199.C.fi without 'this' cannot be immutable
-fail_compilation/fail9199.d(35): Error: function fail9199.C.fw without 'this' cannot be inout
-fail_compilation/fail9199.d(36): Error: function fail9199.C.fs without 'this' cannot be shared
-fail_compilation/fail9199.d(37): Error: function fail9199.C.fsc without 'this' cannot be shared const
-fail_compilation/fail9199.d(38): Error: function fail9199.C.fsw without 'this' cannot be shared inout
+fail_compilation/fail9199.d(33): Error: function `fail9199.C.fc` without `this` cannot be `const`
+fail_compilation/fail9199.d(34): Error: function `fail9199.C.fi` without `this` cannot be `immutable`
+fail_compilation/fail9199.d(35): Error: function `fail9199.C.fw` without `this` cannot be `inout`
+fail_compilation/fail9199.d(36): Error: function `fail9199.C.fs` without `this` cannot be `shared`
+fail_compilation/fail9199.d(37): Error: function `fail9199.C.fsc` without `this` cannot be `shared const`
+fail_compilation/fail9199.d(38): Error: function `fail9199.C.fsw` without `this` cannot be `shared inout`
---
*/
class C
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail92.d b/gcc/testsuite/gdc.test/fail_compilation/fail92.d
index 8fa69bb4a2c..8e341996d99 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail92.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail92.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail92.d(15): Error: invalid foreach aggregate `t`
-fail_compilation/fail92.d(23): Error: template instance fail92.crash!(typeof(null)) error instantiating
+fail_compilation/fail92.d(15): Error: invalid `foreach` aggregate `t`
+fail_compilation/fail92.d(23): Error: template instance `fail92.crash!(typeof(null))` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9290.d b/gcc/testsuite/gdc.test/fail_compilation/fail9290.d
new file mode 100644
index 00000000000..6b51b2b902a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9290.d
@@ -0,0 +1,17 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail9290.d(15): Error: slice `s1[]` is not mutable, struct `S` has immutable members
+fail_compilation/fail9290.d(16): Error: array `s1` is not mutable, struct `S` has immutable members
+---
+*/
+
+struct S { immutable int i; }
+
+void main()
+{
+ S[1] s1 = S(1);
+ S[1] s2 = S(2);
+ s1 = S(3);
+ s1 = s2;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail93.d b/gcc/testsuite/gdc.test/fail_compilation/fail93.d
index 4784038e0c4..b9ad2941be7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail93.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail93.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail93.d(13): Error: variable i is shadowing variable fail93.main.i
+fail_compilation/fail93.d(13): Error: variable `i` is shadowing variable `fail93.main.i`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9346.d b/gcc/testsuite/gdc.test/fail_compilation/fail9346.d
index 57d420f76ef..19d0baa65f8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9346.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9346.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9346.d(26): Error: struct fail9346.S is not copyable because it is annotated with `@disable`
-fail_compilation/fail9346.d(27): Error: struct fail9346.S is not copyable because it is annotated with `@disable`
+fail_compilation/fail9346.d(26): Error: struct `fail9346.S` is not copyable because it has a disabled postblit
+fail_compilation/fail9346.d(27): Error: struct `fail9346.S` is not copyable because it has a disabled postblit
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9368.d b/gcc/testsuite/gdc.test/fail_compilation/fail9368.d
deleted file mode 100644
index 25e13600ddb..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9368.d
+++ /dev/null
@@ -1,49 +0,0 @@
-// PERMUTE_ARGS:
-// REQUIRED_ARGS: -d
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail9368.d(20): Error: enum member `b` not represented in final switch
----
-*/
-
-enum E
-{
- a,
- b
-}
-
-void main()
-{
- alias E F;
- F f;
- final switch (f)
- {
- case F.a:
- }
-}
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail9368.d(41): Error: enum member `B` not represented in final switch
----
-*/
-
-enum G
-{
- A,B,C
-}
-
-void test286()
-{
- G e;
- final switch (e)
- {
- case G.A:
-// case G.B:
- case G.C:
- {}
- }
-}
-
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail94.d b/gcc/testsuite/gdc.test/fail_compilation/fail94.d
index 335b6952270..aa722bf24a0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail94.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail94.d
@@ -30,7 +30,7 @@ class B : A
{
printf("B.clone()\n");
}
- body { return ia; }
+ do { return ia; }
}
void main()
@@ -59,4 +59,3 @@ void main()
void bar(IA delegate() dg)
{
}
-
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9413.d b/gcc/testsuite/gdc.test/fail_compilation/fail9413.d
index 617c9950409..c17e7136dee 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9413.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9413.d
@@ -1,24 +1,24 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9413.d(45): Error: variable fail9413.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9413.d(32): Error: variable fail9413.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9413.d(33): Error: variable fail9413.foo.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9413.d(38): Error: variable fail9413.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9413.d(39): Error: variable fail9413.foo.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9413.d(40): Error: variable fail9413.foo.bar.s cannot modify result 's' in contract
-fail_compilation/fail9413.d(50): Error: variable fail9413.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9413.d(73): Error: variable fail9413.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9413.d(74): Error: variable fail9413.foo.r cannot modify result 'r' in contract
-fail_compilation/fail9413.d(58): Error: variable fail9413.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9413.d(59): Error: variable fail9413.foo.r cannot modify result 'r' in contract
-fail_compilation/fail9413.d(60): Error: variable fail9413.foo.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9413.d(65): Error: variable fail9413.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9413.d(66): Error: variable fail9413.foo.r cannot modify result 'r' in contract
-fail_compilation/fail9413.d(67): Error: variable fail9413.foo.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9413.d(68): Error: variable fail9413.foo.baz.s cannot modify result 's' in contract
-fail_compilation/fail9413.d(79): Error: variable fail9413.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9413.d(80): Error: variable fail9413.foo.r cannot modify result 'r' in contract
+fail_compilation/fail9413.d(45): Error: variable `fail9413.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9413.d(32): Error: variable `fail9413.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9413.d(33): Error: variable `fail9413.foo.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9413.d(38): Error: variable `fail9413.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9413.d(39): Error: variable `fail9413.foo.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9413.d(40): Error: variable `fail9413.foo.bar.s` cannot modify result `s` in contract
+fail_compilation/fail9413.d(50): Error: variable `fail9413.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9413.d(73): Error: variable `fail9413.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9413.d(74): Error: variable `fail9413.foo.r` cannot modify result `r` in contract
+fail_compilation/fail9413.d(58): Error: variable `fail9413.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9413.d(59): Error: variable `fail9413.foo.r` cannot modify result `r` in contract
+fail_compilation/fail9413.d(60): Error: variable `fail9413.foo.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9413.d(65): Error: variable `fail9413.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9413.d(66): Error: variable `fail9413.foo.r` cannot modify result `r` in contract
+fail_compilation/fail9413.d(67): Error: variable `fail9413.foo.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9413.d(68): Error: variable `fail9413.foo.baz.s` cannot modify result `s` in contract
+fail_compilation/fail9413.d(79): Error: variable `fail9413.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9413.d(80): Error: variable `fail9413.foo.r` cannot modify result `r` in contract
---
*/
@@ -40,7 +40,7 @@ in
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
y = 1; // OK
@@ -68,7 +68,7 @@ out(r)
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
r = 10; // err
@@ -79,7 +79,7 @@ out(r)
x = 10; // err
r = 10; // err
}
-body
+do
{
return 1;
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9414a.d b/gcc/testsuite/gdc.test/fail_compilation/fail9414a.d
index 4fd98d3cc55..b6e2a6f25aa 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9414a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9414a.d
@@ -1,24 +1,24 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9414a.d(47): Error: variable fail9414a.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414a.d(34): Error: variable fail9414a.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414a.d(35): Error: variable fail9414a.C.foo.__require.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414a.d(40): Error: variable fail9414a.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414a.d(41): Error: variable fail9414a.C.foo.__require.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414a.d(42): Error: variable fail9414a.C.foo.__require.bar.s cannot modify result 's' in contract
-fail_compilation/fail9414a.d(52): Error: variable fail9414a.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414a.d(75): Error: variable fail9414a.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414a.d(76): Error: variable fail9414a.C.foo.__ensure.r cannot modify result 'r' in contract
-fail_compilation/fail9414a.d(60): Error: variable fail9414a.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414a.d(61): Error: variable fail9414a.C.foo.__ensure.r cannot modify result 'r' in contract
-fail_compilation/fail9414a.d(62): Error: variable fail9414a.C.foo.__ensure.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414a.d(67): Error: variable fail9414a.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414a.d(68): Error: variable fail9414a.C.foo.__ensure.r cannot modify result 'r' in contract
-fail_compilation/fail9414a.d(69): Error: variable fail9414a.C.foo.__ensure.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414a.d(70): Error: variable fail9414a.C.foo.__ensure.baz.s cannot modify result 's' in contract
-fail_compilation/fail9414a.d(81): Error: variable fail9414a.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414a.d(82): Error: variable fail9414a.C.foo.__ensure.r cannot modify result 'r' in contract
+fail_compilation/fail9414a.d(47): Error: variable `fail9414a.C.foo.__require.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414a.d(34): Error: variable `fail9414a.C.foo.__require.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414a.d(35): Error: variable `fail9414a.C.foo.__require.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414a.d(40): Error: variable `fail9414a.C.foo.__require.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414a.d(41): Error: variable `fail9414a.C.foo.__require.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414a.d(42): Error: variable `fail9414a.C.foo.__require.bar.s` cannot modify result `s` in contract
+fail_compilation/fail9414a.d(52): Error: variable `fail9414a.C.foo.__require.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414a.d(75): Error: variable `fail9414a.C.foo.__ensure.x` cannot modify result `x` in contract
+fail_compilation/fail9414a.d(76): Error: variable `fail9414a.C.foo.__ensure.r` cannot modify result `r` in contract
+fail_compilation/fail9414a.d(60): Error: variable `fail9414a.C.foo.__ensure.x` cannot modify result `x` in contract
+fail_compilation/fail9414a.d(61): Error: variable `fail9414a.C.foo.__ensure.r` cannot modify result `r` in contract
+fail_compilation/fail9414a.d(62): Error: variable `fail9414a.C.foo.__ensure.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414a.d(67): Error: variable `fail9414a.C.foo.__ensure.x` cannot modify result `x` in contract
+fail_compilation/fail9414a.d(68): Error: variable `fail9414a.C.foo.__ensure.r` cannot modify result `r` in contract
+fail_compilation/fail9414a.d(69): Error: variable `fail9414a.C.foo.__ensure.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414a.d(70): Error: variable `fail9414a.C.foo.__ensure.baz.s` cannot modify result `s` in contract
+fail_compilation/fail9414a.d(81): Error: variable `fail9414a.C.foo.__ensure.x` cannot modify result `x` in contract
+fail_compilation/fail9414a.d(82): Error: variable `fail9414a.C.foo.__ensure.r` cannot modify result `r` in contract
---
*/
@@ -42,7 +42,7 @@ class C
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
y = 1; // OK
@@ -70,7 +70,7 @@ class C
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
r = 10; // err
@@ -81,7 +81,7 @@ class C
x = 10; // err
r = 10; // err
}
- body
+ do
{
return 1;
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9414b.d b/gcc/testsuite/gdc.test/fail_compilation/fail9414b.d
index 37bd12fc429..dbb76da1348 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9414b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9414b.d
@@ -1,24 +1,24 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9414b.d(47): Error: variable fail9414b.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414b.d(34): Error: variable fail9414b.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414b.d(35): Error: variable fail9414b.C.foo.__require.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414b.d(40): Error: variable fail9414b.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414b.d(41): Error: variable fail9414b.C.foo.__require.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414b.d(42): Error: variable fail9414b.C.foo.__require.bar.s cannot modify result 's' in contract
-fail_compilation/fail9414b.d(52): Error: variable fail9414b.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414b.d(75): Error: variable fail9414b.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414b.d(76): Error: variable fail9414b.C.foo.__ensure.r cannot modify result 'r' in contract
-fail_compilation/fail9414b.d(60): Error: variable fail9414b.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414b.d(61): Error: variable fail9414b.C.foo.__ensure.r cannot modify result 'r' in contract
-fail_compilation/fail9414b.d(62): Error: variable fail9414b.C.foo.__ensure.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414b.d(67): Error: variable fail9414b.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414b.d(68): Error: variable fail9414b.C.foo.__ensure.r cannot modify result 'r' in contract
-fail_compilation/fail9414b.d(69): Error: variable fail9414b.C.foo.__ensure.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414b.d(70): Error: variable fail9414b.C.foo.__ensure.baz.s cannot modify result 's' in contract
-fail_compilation/fail9414b.d(81): Error: variable fail9414b.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414b.d(82): Error: variable fail9414b.C.foo.__ensure.r cannot modify result 'r' in contract
+fail_compilation/fail9414b.d(47): Error: variable `fail9414b.C.foo.__require.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414b.d(34): Error: variable `fail9414b.C.foo.__require.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414b.d(35): Error: variable `fail9414b.C.foo.__require.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414b.d(40): Error: variable `fail9414b.C.foo.__require.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414b.d(41): Error: variable `fail9414b.C.foo.__require.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414b.d(42): Error: variable `fail9414b.C.foo.__require.bar.s` cannot modify result `s` in contract
+fail_compilation/fail9414b.d(52): Error: variable `fail9414b.C.foo.__require.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414b.d(75): Error: variable `fail9414b.C.foo.__ensure.x` cannot modify result `x` in contract
+fail_compilation/fail9414b.d(76): Error: variable `fail9414b.C.foo.__ensure.r` cannot modify result `r` in contract
+fail_compilation/fail9414b.d(60): Error: variable `fail9414b.C.foo.__ensure.x` cannot modify result `x` in contract
+fail_compilation/fail9414b.d(61): Error: variable `fail9414b.C.foo.__ensure.r` cannot modify result `r` in contract
+fail_compilation/fail9414b.d(62): Error: variable `fail9414b.C.foo.__ensure.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414b.d(67): Error: variable `fail9414b.C.foo.__ensure.x` cannot modify result `x` in contract
+fail_compilation/fail9414b.d(68): Error: variable `fail9414b.C.foo.__ensure.r` cannot modify result `r` in contract
+fail_compilation/fail9414b.d(69): Error: variable `fail9414b.C.foo.__ensure.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414b.d(70): Error: variable `fail9414b.C.foo.__ensure.baz.s` cannot modify result `s` in contract
+fail_compilation/fail9414b.d(81): Error: variable `fail9414b.C.foo.__ensure.x` cannot modify result `x` in contract
+fail_compilation/fail9414b.d(82): Error: variable `fail9414b.C.foo.__ensure.r` cannot modify result `r` in contract
---
*/
@@ -42,7 +42,7 @@ class C
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
y = 1; // OK
@@ -70,7 +70,7 @@ class C
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
r = 10; // err
@@ -81,7 +81,7 @@ class C
x = 10; // err
r = 10; // err
}
- body
+ do
{
return 1;
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9414c.d b/gcc/testsuite/gdc.test/fail_compilation/fail9414c.d
index efbff9c42f6..3e0311aea7b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9414c.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9414c.d
@@ -1,24 +1,24 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9414c.d(47): Error: variable fail9414c.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414c.d(34): Error: variable fail9414c.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414c.d(35): Error: variable fail9414c.C.foo.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414c.d(40): Error: variable fail9414c.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414c.d(41): Error: variable fail9414c.C.foo.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414c.d(42): Error: variable fail9414c.C.foo.bar.s cannot modify result 's' in contract
-fail_compilation/fail9414c.d(52): Error: variable fail9414c.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414c.d(75): Error: variable fail9414c.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414c.d(76): Error: variable fail9414c.C.foo.r cannot modify result 'r' in contract
-fail_compilation/fail9414c.d(60): Error: variable fail9414c.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414c.d(61): Error: variable fail9414c.C.foo.r cannot modify result 'r' in contract
-fail_compilation/fail9414c.d(62): Error: variable fail9414c.C.foo.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414c.d(67): Error: variable fail9414c.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414c.d(68): Error: variable fail9414c.C.foo.r cannot modify result 'r' in contract
-fail_compilation/fail9414c.d(69): Error: variable fail9414c.C.foo.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414c.d(70): Error: variable fail9414c.C.foo.baz.s cannot modify result 's' in contract
-fail_compilation/fail9414c.d(81): Error: variable fail9414c.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414c.d(82): Error: variable fail9414c.C.foo.r cannot modify result 'r' in contract
+fail_compilation/fail9414c.d(47): Error: variable `fail9414c.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414c.d(34): Error: variable `fail9414c.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414c.d(35): Error: variable `fail9414c.C.foo.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414c.d(40): Error: variable `fail9414c.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414c.d(41): Error: variable `fail9414c.C.foo.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414c.d(42): Error: variable `fail9414c.C.foo.bar.s` cannot modify result `s` in contract
+fail_compilation/fail9414c.d(52): Error: variable `fail9414c.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414c.d(75): Error: variable `fail9414c.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414c.d(76): Error: variable `fail9414c.C.foo.r` cannot modify result `r` in contract
+fail_compilation/fail9414c.d(60): Error: variable `fail9414c.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414c.d(61): Error: variable `fail9414c.C.foo.r` cannot modify result `r` in contract
+fail_compilation/fail9414c.d(62): Error: variable `fail9414c.C.foo.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414c.d(67): Error: variable `fail9414c.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414c.d(68): Error: variable `fail9414c.C.foo.r` cannot modify result `r` in contract
+fail_compilation/fail9414c.d(69): Error: variable `fail9414c.C.foo.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414c.d(70): Error: variable `fail9414c.C.foo.baz.s` cannot modify result `s` in contract
+fail_compilation/fail9414c.d(81): Error: variable `fail9414c.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414c.d(82): Error: variable `fail9414c.C.foo.r` cannot modify result `r` in contract
---
*/
@@ -42,7 +42,7 @@ class C
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
y = 1; // OK
@@ -70,7 +70,7 @@ class C
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
r = 10; // err
@@ -81,7 +81,7 @@ class C
x = 10; // err
r = 10; // err
}
- body
+ do
{
return 1;
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9414d.d b/gcc/testsuite/gdc.test/fail_compilation/fail9414d.d
index 60b588678f6..06d37ce8c60 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9414d.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9414d.d
@@ -1,24 +1,24 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9414d.d(47): Error: variable fail9414d.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414d.d(34): Error: variable fail9414d.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414d.d(35): Error: variable fail9414d.C.foo.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414d.d(40): Error: variable fail9414d.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414d.d(41): Error: variable fail9414d.C.foo.bar.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414d.d(42): Error: variable fail9414d.C.foo.bar.s cannot modify result 's' in contract
-fail_compilation/fail9414d.d(52): Error: variable fail9414d.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414d.d(75): Error: variable fail9414d.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414d.d(76): Error: variable fail9414d.C.foo.r cannot modify result 'r' in contract
-fail_compilation/fail9414d.d(60): Error: variable fail9414d.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414d.d(61): Error: variable fail9414d.C.foo.r cannot modify result 'r' in contract
-fail_compilation/fail9414d.d(62): Error: variable fail9414d.C.foo.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414d.d(67): Error: variable fail9414d.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414d.d(68): Error: variable fail9414d.C.foo.r cannot modify result 'r' in contract
-fail_compilation/fail9414d.d(69): Error: variable fail9414d.C.foo.baz.y cannot modify parameter 'y' in contract
-fail_compilation/fail9414d.d(70): Error: variable fail9414d.C.foo.baz.s cannot modify result 's' in contract
-fail_compilation/fail9414d.d(81): Error: variable fail9414d.C.foo.x cannot modify parameter 'x' in contract
-fail_compilation/fail9414d.d(82): Error: variable fail9414d.C.foo.r cannot modify result 'r' in contract
+fail_compilation/fail9414d.d(47): Error: variable `fail9414d.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414d.d(34): Error: variable `fail9414d.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414d.d(35): Error: variable `fail9414d.C.foo.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414d.d(40): Error: variable `fail9414d.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414d.d(41): Error: variable `fail9414d.C.foo.bar.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414d.d(42): Error: variable `fail9414d.C.foo.bar.s` cannot modify result `s` in contract
+fail_compilation/fail9414d.d(52): Error: variable `fail9414d.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414d.d(75): Error: variable `fail9414d.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414d.d(76): Error: variable `fail9414d.C.foo.r` cannot modify result `r` in contract
+fail_compilation/fail9414d.d(60): Error: variable `fail9414d.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414d.d(61): Error: variable `fail9414d.C.foo.r` cannot modify result `r` in contract
+fail_compilation/fail9414d.d(62): Error: variable `fail9414d.C.foo.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414d.d(67): Error: variable `fail9414d.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414d.d(68): Error: variable `fail9414d.C.foo.r` cannot modify result `r` in contract
+fail_compilation/fail9414d.d(69): Error: variable `fail9414d.C.foo.baz.y` cannot modify parameter `y` in contract
+fail_compilation/fail9414d.d(70): Error: variable `fail9414d.C.foo.baz.s` cannot modify result `s` in contract
+fail_compilation/fail9414d.d(81): Error: variable `fail9414d.C.foo.x` cannot modify parameter `x` in contract
+fail_compilation/fail9414d.d(82): Error: variable `fail9414d.C.foo.r` cannot modify result `r` in contract
---
*/
@@ -42,7 +42,7 @@ class C
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
y = 1; // OK
@@ -70,7 +70,7 @@ class C
s = 10; // err
a = 1; // OK
}
- body
+ do
{
x = 10; // err
r = 10; // err
@@ -81,7 +81,7 @@ class C
x = 10; // err
r = 10; // err
}
- body
+ do
{
return 1;
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail95.d b/gcc/testsuite/gdc.test/fail_compilation/fail95.d
index 439e55d8b1f..b1f046ac233 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail95.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail95.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail95.d(19): Error: template fail95.A cannot deduce function from argument types !()(int), candidates are:
-fail_compilation/fail95.d(11): fail95.A(alias T)(T)
+fail_compilation/fail95.d(19): Error: template `fail95.A` cannot deduce function from argument types `!()(int)`
+fail_compilation/fail95.d(11): Candidate is: `A(alias T)(T)`
---
*/
-// Issue 142 - Assertion failure: '0' on line 610 in file 'template.c'
-
+// https://issues.dlang.org/show_bug.cgi?id=142
+// Assertion failure: '0' on line 610 in file 'template.c'
template A(alias T)
{
void A(T) { T = 2; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9537.d b/gcc/testsuite/gdc.test/fail_compilation/fail9537.d
index bf6e4db60b6..e08badf34a9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9537.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9537.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9537.d(26): Error: foo(tuple(1, 2)) is not an lvalue
+fail_compilation/fail9537.d(26): Error: `foo(tuple(1, 2))` is not an lvalue and cannot be modified
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9562.d b/gcc/testsuite/gdc.test/fail_compilation/fail9562.d
index 46aafee6094..7c40c41bc44 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9562.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9562.d
@@ -1,13 +1,12 @@
/*
REQUIRED_ARGS: -o-
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/fail9562.d(17): Error: int[] is not an expression
-fail_compilation/fail9562.d(18): Error: no property 'reverse' for type 'int[]'
-fail_compilation/fail9562.d(19): Error: no property 'sort' for type 'int[]'
-fail_compilation/fail9562.d(20): Error: no property 'dup' for type 'int[]'
-fail_compilation/fail9562.d(21): Error: no property 'idup' for type 'int[]'
+fail_compilation/fail9562.d(16): Error: `int[]` is not an expression
+fail_compilation/fail9562.d(17): Error: no property `reverse` for type `int[]`
+fail_compilation/fail9562.d(18): Error: no property `sort` for type `int[]`, perhaps `import std.algorithm;` is needed?
+fail_compilation/fail9562.d(19): Error: no property `dup` for type `int[]`
+fail_compilation/fail9562.d(20): Error: no property `idup` for type `int[]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail96.d b/gcc/testsuite/gdc.test/fail_compilation/fail96.d
index 1bdc8417b6f..639fb6c08e0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail96.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail96.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail96.d(21): Error: template instance foo!long foo is not a template declaration, it is a function alias
+fail_compilation/fail96.d(21): Error: template instance `foo!long` `foo` is not a template declaration, it is a function alias
---
*/
-// 153
+// https://issues.dlang.org/show_bug.cgi?id=153
template bar(T)
{
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9613.d b/gcc/testsuite/gdc.test/fail_compilation/fail9613.d
index 7f9d007af45..31ca808181a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9613.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9613.d
@@ -1,4 +1,4 @@
-// PREMUTE_ARGS:
+// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d b/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d
index 64602f875c2..abfe914d54f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d
@@ -1,15 +1,39 @@
-// REQUIRED_ARGS:
-// PERMUTE_ARGS:
-
-/***************************************************/
-// immutable field
-
/+
TEST_OUTPUT:
---
-fail_compilation/fail9665a.d(19): Error: immutable field 'v' initialized multiple times
+fail_compilation/fail9665a.d(43): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(42): Previous initialization is here.
+fail_compilation/fail9665a.d(53): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(52): Previous initialization is here.
+fail_compilation/fail9665a.d(58): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(57): Previous initialization is here.
+fail_compilation/fail9665a.d(63): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(62): Previous initialization is here.
+fail_compilation/fail9665a.d(73): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(72): Previous initialization is here.
+fail_compilation/fail9665a.d(78): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(77): Previous initialization is here.
+fail_compilation/fail9665a.d(83): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(82): Previous initialization is here.
+fail_compilation/fail9665a.d(96): Error: immutable field `v` initialization is not allowed in loops or after labels
+fail_compilation/fail9665a.d(101): Error: immutable field `v` initialization is not allowed in loops or after labels
+fail_compilation/fail9665a.d(106): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(105): Previous initialization is here.
+fail_compilation/fail9665a.d(111): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(110): Previous initialization is here.
+fail_compilation/fail9665a.d(116): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(115): Previous initialization is here.
+fail_compilation/fail9665a.d(130): Error: immutable field `v` initialized multiple times
+fail_compilation/fail9665a.d(129): Previous initialization is here.
+fail_compilation/fail9665a.d(134): Error: immutable field `w` initialized multiple times
+fail_compilation/fail9665a.d(133): Previous initialization is here.
+fail_compilation/fail9665a.d(148): Error: static assert: `__traits(compiles, this.v = 1)` is false
---
+/
+
+/***************************************************/
+// immutable field
+
struct S1A
{
immutable int v;
@@ -20,14 +44,6 @@ struct S1A
}
}
-/+
-TEST_OUTPUT:
----
-fail_compilation/fail9665a.d(37): Error: immutable field 'v' initialized multiple times
-fail_compilation/fail9665a.d(42): Error: immutable field 'v' initialized multiple times
-fail_compilation/fail9665a.d(47): Error: immutable field 'v' initialized multiple times
----
-+/
struct S1B
{
immutable int v;
@@ -48,14 +64,6 @@ struct S1B
}
}
-/+
-TEST_OUTPUT:
----
-fail_compilation/fail9665a.d(65): Error: immutable field 'v' initialized multiple times
-fail_compilation/fail9665a.d(70): Error: immutable field 'v' initialized multiple times
-fail_compilation/fail9665a.d(75): Error: immutable field 'v' initialized multiple times
----
-+/
struct S1C
{
immutable int v;
@@ -79,16 +87,6 @@ struct S1C
/***************************************************/
// with control flow
-/+
-TEST_OUTPUT:
----
-fail_compilation/fail9665a.d(98): Error: immutable field 'v' initialization is not allowed in loops or after labels
-fail_compilation/fail9665a.d(103): Error: immutable field 'v' initialization is not allowed in loops or after labels
-fail_compilation/fail9665a.d(108): Error: immutable field 'v' initialized multiple times
-fail_compilation/fail9665a.d(113): Error: immutable field 'v' initialized multiple times
-fail_compilation/fail9665a.d(118): Error: immutable field 'v' initialized multiple times
----
-+/
struct S2
{
immutable int v;
@@ -122,13 +120,6 @@ struct S2
/***************************************************/
// with immutable constructor
-/+
-TEST_OUTPUT:
----
-fail_compilation/fail9665a.d(139): Error: immutable field 'v' initialized multiple times
-fail_compilation/fail9665a.d(143): Error: immutable field 'w' initialized multiple times
----
-+/
struct S3
{
int v;
@@ -147,12 +138,6 @@ struct S3
/***************************************************/
// in __traits(compiles)
-/+
-TEST_OUTPUT:
----
-fail_compilation/fail9665a.d(163): Error: static assert `__traits(compiles, this.v = 1)` is false
----
-+/
struct S4
{
immutable int v;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9665b.d b/gcc/testsuite/gdc.test/fail_compilation/fail9665b.d
index 8f7d79c705d..898054643cf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9665b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9665b.d
@@ -11,12 +11,12 @@ struct X
/+
TEST_OUTPUT:
---
-fail_compilation/fail9665b.d(32): Error: one path skips field x2
-fail_compilation/fail9665b.d(33): Error: one path skips field x3
-fail_compilation/fail9665b.d(35): Error: one path skips field x5
-fail_compilation/fail9665b.d(36): Error: one path skips field x6
-fail_compilation/fail9665b.d(30): Error: field x1 must be initialized in constructor
-fail_compilation/fail9665b.d(30): Error: field x4 must be initialized in constructor
+fail_compilation/fail9665b.d(32): Error: one path skips field `x2`
+fail_compilation/fail9665b.d(33): Error: one path skips field `x3`
+fail_compilation/fail9665b.d(35): Error: one path skips field `x5`
+fail_compilation/fail9665b.d(36): Error: one path skips field `x6`
+fail_compilation/fail9665b.d(30): Error: field `x1` must be initialized in constructor
+fail_compilation/fail9665b.d(30): Error: field `x4` must be initialized in constructor
---
+/
struct S1
@@ -43,13 +43,13 @@ struct S1
/+
TEST_OUTPUT:
---
-fail_compilation/fail9665b.d(65): Error: one path skips field x2
-fail_compilation/fail9665b.d(66): Error: one path skips field x3
-fail_compilation/fail9665b.d(68): Error: one path skips field x5
-fail_compilation/fail9665b.d(69): Error: one path skips field x6
-fail_compilation/fail9665b.d(63): Error: field x1 must be initialized in constructor, because it is nested struct
-fail_compilation/fail9665b.d(63): Error: field x4 must be initialized in constructor, because it is nested struct
-fail_compilation/fail9665b.d(76): Error: template instance fail9665b.S2!(X) error instantiating
+fail_compilation/fail9665b.d(65): Error: one path skips field `x2`
+fail_compilation/fail9665b.d(66): Error: one path skips field `x3`
+fail_compilation/fail9665b.d(68): Error: one path skips field `x5`
+fail_compilation/fail9665b.d(69): Error: one path skips field `x6`
+fail_compilation/fail9665b.d(63): Error: field `x1` must be initialized in constructor, because it is nested struct
+fail_compilation/fail9665b.d(63): Error: field `x4` must be initialized in constructor, because it is nested struct
+fail_compilation/fail9665b.d(76): Error: template instance `fail9665b.S2!(X)` error instantiating
---
+/
struct S2(X)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail97.d b/gcc/testsuite/gdc.test/fail_compilation/fail97.d
index 33d0c5fb924..cec9819abc9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail97.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail97.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail97.d(11): Error: pragma lib pragma is missing closing `;`
+fail_compilation/fail97.d(11): Error: pragma `lib` is missing a terminating `;`
---
*/
-// 151
+// https://issues.dlang.org/show_bug.cgi?id=151
-import std.stdio;
+import core.stdc.stdio;
pragma(lib,"ws2_32.lib")//;
class bla{}
void main(){}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9710.d b/gcc/testsuite/gdc.test/fail_compilation/fail9710.d
index 98306b26cac..dc772cde53c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9710.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9710.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9710.d(9): Error: static variable e cannot be read at compile time
+fail_compilation/fail9710.d(9): Error: static variable `e` cannot be read at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9766.d b/gcc/testsuite/gdc.test/fail_compilation/fail9766.d
index 58cabe3825b..03a94cb795f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9766.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9766.d
@@ -2,11 +2,11 @@
TEST_OUTPUT:
---
fail_compilation/fail9766.d(14): Error: integer constant expression expected instead of `Foo!int`
-fail_compilation/fail9766.d(14): Error: alignment must be an integer positive power of 2, not Foo!int
-fail_compilation/fail9766.d(17): Error: alignment must be an integer positive power of 2, not -1
-fail_compilation/fail9766.d(20): Error: alignment must be an integer positive power of 2, not 0
-fail_compilation/fail9766.d(23): Error: alignment must be an integer positive power of 2, not 3
-fail_compilation/fail9766.d(26): Error: alignment must be an integer positive power of 2, not 2147483649u
+fail_compilation/fail9766.d(14): Error: alignment must be an integer positive power of 2, not 0x0
+fail_compilation/fail9766.d(17): Error: alignment must be an integer positive power of 2, not 0xffffffffffffffff
+fail_compilation/fail9766.d(20): Error: alignment must be an integer positive power of 2, not 0x0
+fail_compilation/fail9766.d(23): Error: alignment must be an integer positive power of 2, not 0x3
+fail_compilation/fail9766.d(26): Error: alignment must be an integer positive power of 2, not 0x80000001
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9773.d b/gcc/testsuite/gdc.test/fail_compilation/fail9773.d
index 18da406beaa..b49ffe13dfc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9773.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9773.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9773.d(7): Error: "" is not an lvalue
+fail_compilation/fail9773.d(7): Error: `""` is not an lvalue and cannot be modified
---
*/
void f(ref string a = "")
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9790.d b/gcc/testsuite/gdc.test/fail_compilation/fail9790.d
index 0becddd88f4..cae33c28b65 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9790.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9790.d
@@ -2,9 +2,9 @@
TEST_OUTPUT:
---
fail_compilation/fail9790.d(13): Error: undefined identifier `_Unused_`
-fail_compilation/fail9790.d(20): Error: template instance fail9790.foo!() error instantiating
+fail_compilation/fail9790.d(20): Error: template instance `fail9790.foo!()` error instantiating
fail_compilation/fail9790.d(18): Error: undefined identifier `_Unused_`
-fail_compilation/fail9790.d(21): Error: template instance fail9790.bar!() error instantiating
+fail_compilation/fail9790.d(21): Error: template instance `fail9790.bar!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail98.d b/gcc/testsuite/gdc.test/fail_compilation/fail98.d
index ded3624f988..7541d3718c7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail98.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail98.d
@@ -5,7 +5,7 @@ fail_compilation/fail98.d(17): Error: cannot implicitly convert expression `256`
---
*/
-// 139
+// https://issues.dlang.org/show_bug.cgi?id=139
E foo(int index)
{
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9891.d b/gcc/testsuite/gdc.test/fail_compilation/fail9891.d
index 99d5f11f019..791e734349f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9891.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9891.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9891.d(13): Error: cast(int)i is not an lvalue
-fail_compilation/fail9891.d(18): Error: cast(int)i is not an lvalue
-fail_compilation/fail9891.d(23): Error: prop() is not an lvalue
+fail_compilation/fail9891.d(13): Error: expression `i` of type `immutable(int)` is not implicitly convertible to type `ref int` of parameter `n`
+fail_compilation/fail9891.d(18): Error: expression `i` of type `immutable(int)` is not implicitly convertible to type `out int` of parameter `n`
+fail_compilation/fail9891.d(23): Error: `prop()` is not an lvalue and cannot be modified
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9892.d b/gcc/testsuite/gdc.test/fail_compilation/fail9892.d
index 32eef128d20..c3fbf7a10a9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9892.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9892.d
@@ -1,8 +1,8 @@
-// 9892
+// https://issues.dlang.org/show_bug.cgi?id=9892
/*
TEST_OUTPUT:
---
-fail_compilation/fail9892.d(11): Error: enum member fail9892.a circular reference to enum member
+fail_compilation/fail9892.d(11): Error: enum member `fail9892.a` circular reference to `enum` member
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9936.d b/gcc/testsuite/gdc.test/fail_compilation/fail9936.d
index 36178f48c21..0d7d44ae439 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail9936.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail9936.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail9936.d(25): Error: S().opBinary isn't a template
-fail_compilation/fail9936.d(26): Error: S().opBinaryRight isn't a template
-fail_compilation/fail9936.d(27): Error: S().opOpAssign isn't a template
-fail_compilation/fail9936.d(29): Error: S().opIndexUnary isn't a template
-fail_compilation/fail9936.d(30): Error: S().opUnary isn't a template
+fail_compilation/fail9936.d(25): Error: `S().opBinary` isn't a template
+fail_compilation/fail9936.d(26): Error: `S().opBinaryRight` isn't a template
+fail_compilation/fail9936.d(27): Error: `S().opOpAssign` isn't a template
+fail_compilation/fail9936.d(29): Error: `S().opIndexUnary` isn't a template
+fail_compilation/fail9936.d(30): Error: `S().opUnary` isn't a template
---
*/
struct S
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failCopyCtor.d b/gcc/testsuite/gdc.test/fail_compilation/failCopyCtor.d
new file mode 100644
index 00000000000..f341675f328
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/failCopyCtor.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/failCopyCtor.d(10): Error: `struct A` may not define both a rvalue constructor and a copy constructor
+fail_compilation/failCopyCtor.d(12): rvalue constructor defined here
+fail_compilation/failCopyCtor.d(13): copy constructor defined here
+---
+*/
+
+struct A
+{
+ this(immutable A a) {}
+ this(ref shared A a) immutable {}
+ this(ref A a) {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failCopyCtor2.d b/gcc/testsuite/gdc.test/fail_compilation/failCopyCtor2.d
new file mode 100644
index 00000000000..5e8f8c40f7d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/failCopyCtor2.d
@@ -0,0 +1,19 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/failCopyCtor2.d(15): Error: `struct B` may not define a rvalue constructor and have fields with copy constructors
+fail_compilation/failCopyCtor2.d(18): rvalue constructor defined here
+fail_compilation/failCopyCtor2.d(17): field with copy constructor defined here
+---
+*/
+
+struct A
+{
+ this (ref shared A a) immutable {}
+}
+
+struct B
+{
+ A a;
+ this(immutable B b) shared {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d
new file mode 100644
index 00000000000..1a766ff80dd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d
@@ -0,0 +1,30 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail_arrayexp.d(24): Error: cannot use `[]` operator on expression of type `int`
+fail_compilation/fail_arrayexp.d(25): Error: cannot use `[]` operator on expression of type `void`
+fail_compilation/fail_arrayexp.d(26): Error: static array of `const(int)[]` with multiple lengths not allowed
+fail_compilation/fail_arrayexp.d(27): Error: only one index allowed to index `string`
+fail_compilation/fail_arrayexp.d(28): Error: no `[]` operator overload for type `U`
+fail_compilation/fail_arrayexp.d(29): Error: only one index allowed to index `(int, string)`
+---
+*/
+
+int i;
+string str;
+union U {}
+alias typeAlias = const(int)[];
+void getVoid();
+alias getTuple(T...) = T;
+
+void test19534() // https://issues.dlang.org/show_bug.cgi?id=19534
+{
+ U agg;
+#line 24
+ auto p = i[0];
+ auto q = getVoid()[0];
+ alias r = getTuple!(typeAlias[0, 1]);
+ auto s = str[0, 1, 2];
+ auto t = agg[];
+ auto u = getTuple!(int, string)[1, 2];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop1.d b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop1.d
index 3498df7cb06..7527639a061 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop1.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop1.d
@@ -4,10 +4,12 @@
TEST_OUTPUT:
---
fail_compilation/fail_arrayop1.d(11): Error: invalid array operation `a + a` (possible missing [])
+fail_compilation/fail_arrayop1.d(11): did you mean to concatenate (`a ~ a`) instead ?
---
*/
-void test2199(int[] a) // Issue 2199 - Segfault using array operation in function call (from fail266.d)
+void test2199(int[] a) // https://issues.dlang.org/show_bug.cgi?id=2199 - Segfault using array operation in function call (from fail266.d)
{
+#line 11
test2199(a + a);
}
@@ -17,7 +19,7 @@ TEST_OUTPUT:
fail_compilation/fail_arrayop1.d(29): Error: invalid array operation `-a` (possible missing [])
---
*/
-void fail323() // from fail323.d, maybe was a part of issue 3471 fix?
+void fail323() // from fail323.d, maybe was a part of https://issues.dlang.org/show_bug.cgi?id=3471 fix?
{
void foo(double[]) {}
@@ -25,7 +27,7 @@ void fail323() // from fail323.d, maybe was a part of issue 3471 fix?
b = a.dup,
c = a.dup,
d = a.dup;
-
+#line 29
foo(-a);
// a[] = -(b[] * (c[] + 4)) + 5 * d[]; // / 3;
}
@@ -36,6 +38,7 @@ TEST_OUTPUT:
fail_compilation/fail_arrayop1.d(54): Error: invalid array operation `-a` (possible missing [])
fail_compilation/fail_arrayop1.d(55): Error: invalid array operation `~a` (possible missing [])
fail_compilation/fail_arrayop1.d(56): Error: invalid array operation `a + a` (possible missing [])
+fail_compilation/fail_arrayop1.d(56): did you mean to concatenate (`a ~ a`) instead ?
fail_compilation/fail_arrayop1.d(57): Error: invalid array operation `a - a` (possible missing [])
fail_compilation/fail_arrayop1.d(58): Error: invalid array operation `a * a` (possible missing [])
fail_compilation/fail_arrayop1.d(59): Error: invalid array operation `a / a` (possible missing [])
@@ -50,7 +53,7 @@ void test3903()
{
int[] a = [1, 2];
int[] r;
-
+#line 54
r = -a;
r = ~a;
r = a + a;
@@ -68,6 +71,7 @@ void test3903()
TEST_OUTPUT:
---
fail_compilation/fail_arrayop1.d(85): Error: invalid array operation `a += a[]` (possible missing [])
+fail_compilation/fail_arrayop1.d(85): did you mean to concatenate (`a ~= a[]`) instead ?
fail_compilation/fail_arrayop1.d(86): Error: invalid array operation `a -= a[]` (possible missing [])
fail_compilation/fail_arrayop1.d(87): Error: invalid array operation `a *= a[]` (possible missing [])
fail_compilation/fail_arrayop1.d(88): Error: invalid array operation `a /= a[]` (possible missing [])
@@ -81,7 +85,7 @@ fail_compilation/fail_arrayop1.d(93): Error: invalid array operation `a ^^= a[]`
void test9459()
{
int[] a = [1, 2, 3];
-
+#line 85
a += a[];
a -= a[];
a *= a[];
@@ -96,47 +100,25 @@ void test9459()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop1.d(111): Error: invalid array operation `x1[] = x2[] * x3[]` because `X` doesn't support necessary arithmetic operations
-fail_compilation/fail_arrayop1.d(115): Error: invalid array operation `s2[] += s1[]` because `string` is not a scalar type
-fail_compilation/fail_arrayop1.d(119): Error: invalid array operation `pa1[] *= pa2[]` for element type `int*`
----
-*/
-void test11376()
-{
- struct X { }
-
- auto x1 = [X()];
- auto x2 = [X()];
- auto x3 = [X()];
- x1[] = x2[] * x3[];
-
- string[] s1;
- string[] s2;
- s2[] += s1[];
-
- int*[] pa1;
- int*[] pa2;
- pa1[] *= pa2[];
-}
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail_arrayop1.d(131): Error: invalid array operation `a[] <<= 1` (possible missing [])
+fail_compilation/fail_arrayop1.d(105): Error: invalid array operation `a[] <<= 1` (possible missing [])
---
*/
void test11566()
{
int[] a;
+#line 105
a[] <<= 1;
}
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop1.d(147): Error: invalid array operation `a + b` (possible missing [])
-fail_compilation/fail_arrayop1.d(148): Error: invalid array operation `x + y` (possible missing [])
-fail_compilation/fail_arrayop1.d(149): Error: invalid array operation `"hel" + "lo."` (possible missing [])
+fail_compilation/fail_arrayop1.d(121): Error: invalid array operation `a + b` (possible missing [])
+fail_compilation/fail_arrayop1.d(121): did you mean to concatenate (`a ~ b`) instead ?
+fail_compilation/fail_arrayop1.d(122): Error: invalid array operation `x + y` (possible missing [])
+fail_compilation/fail_arrayop1.d(122): did you mean to concatenate (`x ~ y`) instead ?
+fail_compilation/fail_arrayop1.d(123): Error: invalid array operation `"hel" + "lo."` (possible missing [])
+fail_compilation/fail_arrayop1.d(123): did you mean to concatenate (`"hel" ~ "lo."`) instead ?
---
*/
void test14649()
@@ -144,6 +126,7 @@ void test14649()
char[] a, b, r;
string x, y;
+#line 121
r[] = a + b;
r[] = x + y;
r[] = "hel" + "lo.";
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop2.d b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop2.d
index 8f654b0ec81..ed228a94c35 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop2.d
@@ -1,13 +1,14 @@
// REQUIRED_ARGS: -o-
+
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(12): Error: array operation `[1, 2, 3] - [1, 2, 3]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(15): Error: invalid array operation `"a" - "b"` (possible missing [])
+fail_compilation/fail_arrayop2.d(13): Error: array operation `[1, 2, 3] - [1, 2, 3]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(16): Error: invalid array operation `"a" - "b"` (possible missing [])
---
*/
-void test2603() // Issue 2603 - ICE(cgcs.c) on subtracting string literals
+void test2603() // https://issues.dlang.org/show_bug.cgi?id=2603 - ICE(cgcs.c) on subtracting string literals
{
auto c1 = [1,2,3] - [1,2,3];
@@ -18,17 +19,17 @@ void test2603() // Issue 2603 - ICE(cgcs.c) on subtracting string literals
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(37): Error: array operation `-a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(38): Error: array operation `~a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(40): Error: array operation `a[] + a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(41): Error: array operation `a[] - a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(42): Error: array operation `a[] * a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(43): Error: array operation `a[] / a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(44): Error: array operation `a[] % a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(45): Error: array operation `a[] ^ a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(46): Error: array operation `a[] & a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(47): Error: array operation `a[] | a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(48): Error: array operation `a[] ^^ a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(38): Error: array operation `-a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(39): Error: array operation `~a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(41): Error: array operation `a[] + a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(42): Error: array operation `a[] - a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(43): Error: array operation `a[] * a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(44): Error: array operation `a[] / a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(45): Error: array operation `a[] % a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(46): Error: array operation `a[] ^ a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(47): Error: array operation `a[] & a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(48): Error: array operation `a[] | a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(49): Error: array operation `a[] ^^ a[]` without destination memory not allowed (possible missing [])
---
*/
void test9459()
@@ -51,19 +52,19 @@ void test9459()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(74): Error: array operation `a[] + a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(75): Error: array operation `a[] - a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(76): Error: array operation `a[] * a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(77): Error: array operation `a[] / a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(78): Error: array operation `a[] % a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(79): Error: array operation `a[] ^ a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(80): Error: array operation `a[] & a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(81): Error: array operation `a[] | a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(82): Error: array operation `a[] ^^ 10` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(83): Error: array operation `-a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(84): Error: array operation `~a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(89): Error: array operation `[1] + a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(75): Error: array operation `a[] + a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(76): Error: array operation `a[] - a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(77): Error: array operation `a[] * a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(78): Error: array operation `a[] / a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(79): Error: array operation `a[] % a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(80): Error: array operation `a[] ^ a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(81): Error: array operation `a[] & a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(82): Error: array operation `a[] | a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(83): Error: array operation `a[] ^^ 10` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(84): Error: array operation `-a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(85): Error: array operation `~a[]` without destination memory not allowed
fail_compilation/fail_arrayop2.d(90): Error: array operation `[1] + a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(91): Error: array operation `[1] + a[]` without destination memory not allowed
---
*/
void test12179()
@@ -83,7 +84,7 @@ void test12179()
foo(-a[]);
foo(~a[]);
- // from issue 11992
+ // from https://issues.dlang.org/show_bug.cgi?id=11992
int[] arr1;
int[][] arr2;
arr1 ~= [1] + a[]; // NG
@@ -93,7 +94,7 @@ void test12179()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(104): Error: array operation `h * y[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(105): Error: array operation `h * y[]` without destination memory not allowed
---
*/
void test12381()
@@ -107,8 +108,8 @@ void test12381()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(117): Error: array operation `-a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(119): Error: array operation `(-a[])[0..4]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(118): Error: array operation `-a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(120): Error: array operation `(-a[])[0..4]` without destination memory not allowed
---
*/
float[] test12769(float[] a)
@@ -122,11 +123,11 @@ float[] test12769(float[] a)
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(136): Error: array operation `a[] - a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(138): Error: array operation `a[] - a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(137): Error: array operation `a[] - a[]` without destination memory not allowed
fail_compilation/fail_arrayop2.d(139): Error: array operation `a[] - a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(142): Error: array operation `a[] - a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(144): Error: array operation `a[] - a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(140): Error: array operation `a[] - a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(143): Error: array operation `a[] - a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(145): Error: array operation `a[] - a[]` without destination memory not allowed
---
*/
void test13208()
@@ -147,10 +148,10 @@ void test13208()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(159): Error: array operation `a[] * a[]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(160): Error: array operation `(a[] * a[])[0..1]` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(163): Error: array operation `a[] * a[]` without destination memory not allowed (possible missing [])
-fail_compilation/fail_arrayop2.d(164): Error: array operation `(a[] * a[])[0..1]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(160): Error: array operation `a[] * a[]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(161): Error: array operation `(a[] * a[])[0..1]` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(164): Error: array operation `a[] * a[]` without destination memory not allowed (possible missing [])
+fail_compilation/fail_arrayop2.d(165): Error: array operation `(a[] * a[])[0..1]` without destination memory not allowed (possible missing [])
---
*/
void test13497()
@@ -167,7 +168,7 @@ void test13497()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(180): Error: array operation `data[segmentId][28..29] & cast(ubyte)(1 << 0)` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(181): Error: array operation `data[segmentId][28..29] & cast(ubyte)(1 << 0)` without destination memory not allowed
---
*/
void test13910()
@@ -184,8 +185,8 @@ void test13910()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(194): Error: array operation `a[] + 1` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(194): Error: array operation `a[] * 2` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(195): Error: array operation `a[] + 1` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(195): Error: array operation `a[] * 2` without destination memory not allowed
---
*/
void test14895()
@@ -197,40 +198,41 @@ void test14895()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(245): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(246): Error: array operation `[1] * 6` without destination memory not allowed
fail_compilation/fail_arrayop2.d(247): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(252): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(255): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(264): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(267): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(268): Error: array operation `"abc"[] + '\x01'` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(271): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(274): Error: ([1] * 6)[0..2] is not an lvalue
-fail_compilation/fail_arrayop2.d(277): Error: can only * a pointer, not a 'int[]'
-fail_compilation/fail_arrayop2.d(280): Error: [1] * 6 is not an lvalue
-fail_compilation/fail_arrayop2.d(283): Error: array operation `da[] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(286): Error: array operation `da[] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(289): Error: [1] * 6 is not an lvalue
-fail_compilation/fail_arrayop2.d(290): Error: invalid array operation `[1] * 6 -= 1` for element type `int`
-fail_compilation/fail_arrayop2.d(293): Error: [1] * 6 is not an lvalue
-fail_compilation/fail_arrayop2.d(294): Error: ([1] * 6)[] is not an lvalue
-fail_compilation/fail_arrayop2.d(297): Error: invalid array operation `[1] * 6 += 1` for element type `int`
-fail_compilation/fail_arrayop2.d(298): Error: invalid array operation `[1] * 6 *= 2` for element type `int`
-fail_compilation/fail_arrayop2.d(299): Error: invalid array operation `[1] * 6 ^^= 3` for element type `int`
-fail_compilation/fail_arrayop2.d(302): Error: [1] * 6 is not an lvalue
-fail_compilation/fail_arrayop2.d(303): Error: [1] * 6 is not an lvalue
-fail_compilation/fail_arrayop2.d(306): Error: '[1] * 6' is not of integral type, it is a int[]
-fail_compilation/fail_arrayop2.d(307): Error: '[1] * 6' is not of integral type, it is a int[]
-fail_compilation/fail_arrayop2.d(308): Error: '[1] * 6' is not of integral type, it is a int[]
-fail_compilation/fail_arrayop2.d(311): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(248): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(249): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(253): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(256): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(265): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(268): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(269): Error: array operation `"abc"[] + '\x01'` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(272): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(275): Error: `([1] * 6)[0..2]` is not an lvalue and cannot be modified
+fail_compilation/fail_arrayop2.d(278): Error: can only `*` a pointer, not a `int[]`
+fail_compilation/fail_arrayop2.d(281): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+fail_compilation/fail_arrayop2.d(281): Error: `[1] * 6` is not an lvalue and cannot be modified
+fail_compilation/fail_arrayop2.d(284): Error: array operation `da[] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(287): Error: array operation `da[] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(290): Error: `[1] * 6` is not an lvalue and cannot be modified
+fail_compilation/fail_arrayop2.d(291): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(294): Error: `[1] * 6` is not an lvalue and cannot be modified
+fail_compilation/fail_arrayop2.d(295): Error: `([1] * 6)[]` is not an lvalue and cannot be modified
+fail_compilation/fail_arrayop2.d(298): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(299): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(300): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(303): Error: `[1] * 6` is not an lvalue and cannot be modified
+fail_compilation/fail_arrayop2.d(304): Error: `[1] * 6` is not an lvalue and cannot be modified
+fail_compilation/fail_arrayop2.d(307): Error: `[1] * 6` is not of integral type, it is a `int[]`
+fail_compilation/fail_arrayop2.d(308): Error: `[1] * 6` is not of integral type, it is a `int[]`
+fail_compilation/fail_arrayop2.d(309): Error: `[1] * 6` is not of integral type, it is a `int[]`
fail_compilation/fail_arrayop2.d(312): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(315): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(313): Error: array operation `[1] * 6` without destination memory not allowed
fail_compilation/fail_arrayop2.d(316): Error: array operation `[1] * 6` without destination memory not allowed
fail_compilation/fail_arrayop2.d(317): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(320): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(320): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(320): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(318): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(321): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(321): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(321): Error: array operation `[1] * 6` without destination memory not allowed
---
*/
// Test all expressions, which can take arrays as their operands but cannot be a part of array operation.
@@ -247,7 +249,6 @@ void test15407exp()
[1] * 6]; } // AssocArrayLiteralExp
//TupleExp
-
// StructLiteralExp.elements <- preFunctionParameters in CallExp
{ auto r = S([1] * 6); }
@@ -320,18 +321,18 @@ void test15407exp()
{ auto r = [1] * 6 ? [1] * 6 : [1] * 6; }
}
-/*
-TEST_OUTPUT:
+/* TEST_OUTPUT:
---
-fail_compilation/fail_arrayop2.d(341): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(344): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(347): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(342): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(345): Error: array operation `[1] * 6` without destination memory not allowed
fail_compilation/fail_arrayop2.d(348): Error: array operation `[1] * 6` without destination memory not allowed
fail_compilation/fail_arrayop2.d(349): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(352): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(355): Error: array operation `[1] * 6` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(358): Error: array operation `"str"[] + cast(immutable(char))1` without destination memory not allowed
-fail_compilation/fail_arrayop2.d(366): Error: CTFE internal error: non-constant value "uvt"[]
+fail_compilation/fail_arrayop2.d(350): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(353): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(356): Error: array operation `[1] * 6` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(359): Error: array operation `"str"[] + cast(immutable(char))1` without destination memory not allowed
+fail_compilation/fail_arrayop2.d(367): Error: CTFE internal error: non-constant value `"uvt"`
+fail_compilation/fail_arrayop2.d(367): Error: `"uvt"[] - '\x01'` cannot be interpreted at compile time
---
*/
// Test all statements, which can take arrays as their operands.
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3a.d b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3a.d
new file mode 100644
index 00000000000..55898d1096b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3a.d
@@ -0,0 +1,28 @@
+/*
+REQUIRED_ARGS: -o-
+TEST_OUTPUT:
+----
+$p:druntime/import/core/internal/array/operations.d$($n$): Error: static assert: "Binary `*` not supported for types `X` and `X`."
+$p:druntime/import/core/internal/array/operations.d$($n$): instantiated from here: `typeCheck!(true, X, X, X, "*", "=")`
+$p:druntime/import/object.d$($n$): instantiated from here: `arrayOp!(X[], X[], X[], "*", "=")`
+fail_compilation/fail_arrayop3a.d(19): instantiated from here: `_arrayOp!(X[], X[], X[], "*", "=")`
+----
+*/
+
+void test11376()
+{
+ struct X { }
+
+ auto x1 = [X()];
+ auto x2 = [X()];
+ auto x3 = [X()];
+ x1[] = x2[] * x3[];
+
+ string[] s1;
+ string[] s2;
+ s2[] += s1[];
+
+ int*[] pa1;
+ int*[] pa2;
+ pa1[] *= pa2[];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3b.d b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3b.d
new file mode 100644
index 00000000000..87f29932b52
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3b.d
@@ -0,0 +1,16 @@
+/*
+REQUIRED_ARGS: -o-
+TEST_OUTPUT:
+----
+$p:druntime/import/core/internal/array/operations.d$($n$): Error: static assert: "Binary op `+=` not supported for types `string` and `string`."
+$p:druntime/import/core/internal/array/operations.d$($n$): instantiated from here: `typeCheck!(true, string, string, "+=")`
+$p:druntime/import/object.d$($n$): instantiated from here: `arrayOp!(string[], string[], "+=")`
+fail_compilation/fail_arrayop3b.d(15): instantiated from here: `_arrayOp!(string[], string[], "+=")`
+---
+*/
+void test11376()
+{
+ string[] s1;
+ string[] s2;
+ s2[] += s1[];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3c.d b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3c.d
new file mode 100644
index 00000000000..2e9d61a57ad
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayop3c.d
@@ -0,0 +1,16 @@
+/*
+REQUIRED_ARGS: -o-
+TEST_OUTPUT:
+----
+$p:druntime/import/core/internal/array/operations.d$($n$): Error: static assert: "Binary op `*=` not supported for types `int*` and `int*`."
+$p:druntime/import/core/internal/array/operations.d$($n$): instantiated from here: `typeCheck!(true, int*, int*, "*=")`
+$p:druntime/import/object.d$($n$): instantiated from here: `arrayOp!(int*[], int*[], "*=")`
+fail_compilation/fail_arrayop3c.d(15): instantiated from here: `_arrayOp!(int*[], int*[], "*=")`
+----
+*/
+void test11376()
+{
+ int*[] pa1;
+ int*[] pa2;
+ pa1[] *= pa2[];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_casting.d b/gcc/testsuite/gdc.test/fail_compilation/fail_casting.d
index 74337c305de..88d579b4dff 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_casting.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_casting.d
@@ -93,11 +93,11 @@ void test11485()
class C {}
interface I {}
- // 11485 TypeBasic --> Tclass
+ // https://issues.dlang.org/show_bug.cgi?id=11485 TypeBasic --> Tclass
{ int x; auto y = cast(C)x; }
{ int x; auto y = cast(I)x; }
- // 7472 TypeBasic <-- Tclass
+ // https://issues.dlang.org/show_bug.cgi?id=7472 TypeBasic <-- Tclass
{ C x; auto y = cast(int)x; }
{ I x; auto y = cast(int)x; }
}
@@ -133,7 +133,7 @@ void test13959()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_casting.d(144): Error: cannot cast expression `mi.x` of type `int` to `MyUbyte14154`
+fail_compilation/fail_casting.d(144): Error: cannot cast expression `mi` of type `MyInt14154` to `MyUbyte14154` because of different sizes
---
*/
struct MyUbyte14154 { ubyte x; alias x this; }
@@ -147,10 +147,10 @@ void test14154()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_casting.d(179): Error: cannot cast expression `__tup$n$.__expand_field_0` of type `int` to `object.Object`
-fail_compilation/fail_casting.d(179): Error: cannot cast expression `__tup$n$.__expand_field_1` of type `int` to `object.Object`
+fail_compilation/fail_casting.d(179): Error: cannot cast expression `point` of type `Tuple14093!(int, "x", int, "y")` to `object.Object`
---
*/
+
alias TypeTuple14093(T...) = T;
struct Tuple14093(T...)
{
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_casting2.d b/gcc/testsuite/gdc.test/fail_compilation/fail_casting2.d
index a45d6d30bae..16bc5bcead2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_casting2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_casting2.d
@@ -3,9 +3,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail_casting2.d(15): Error: type int is not an expression
+fail_compilation/fail_casting2.d(15): Error: type `int` is not an expression
fail_compilation/fail_casting2.d(17): Error: template lambda has no type
-fail_compilation/fail_casting2.d(20): Error: template Templ() has no type
+fail_compilation/fail_casting2.d(20): Error: template `Templ()` has no type
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_circular.d b/gcc/testsuite/gdc.test/fail_compilation/fail_circular.d
index dd958af5acf..186444e459a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_circular.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_circular.d
@@ -1,16 +1,16 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail_circular.d(16): Error: circular reference to variable 'fail_circular.a1'
-fail_compilation/fail_circular.d(17): Error: circular reference to variable 'fail_circular.a2'
-fail_compilation/fail_circular.d(19): Error: circular reference to variable 'fail_circular.b1'
-fail_compilation/fail_circular.d(20): Error: circular reference to variable 'fail_circular.b2'
-fail_compilation/fail_circular.d(22): Error: circular reference to variable 'fail_circular.c1'
-fail_compilation/fail_circular.d(23): Error: circular reference to variable 'fail_circular.c2'
-fail_compilation/fail_circular.d(25): Error: circular initialization of variable 'fail_circular.d1'
-fail_compilation/fail_circular.d(26): Error: circular initialization of variable 'fail_circular.d2'
-fail_compilation/fail_circular.d(28): Error: circular initialization of variable 'fail_circular.e1'
-fail_compilation/fail_circular.d(29): Error: circular initialization of variable 'fail_circular.e2'
+fail_compilation/fail_circular.d(16): Error: circular reference to variable `fail_circular.a1`
+fail_compilation/fail_circular.d(17): Error: circular reference to variable `fail_circular.a2`
+fail_compilation/fail_circular.d(19): Error: circular reference to variable `fail_circular.b1`
+fail_compilation/fail_circular.d(20): Error: circular reference to variable `fail_circular.b2`
+fail_compilation/fail_circular.d(22): Error: circular reference to variable `fail_circular.c1`
+fail_compilation/fail_circular.d(23): Error: circular reference to variable `fail_circular.c2`
+fail_compilation/fail_circular.d(25): Error: circular initialization of variable `fail_circular.d1`
+fail_compilation/fail_circular.d(26): Error: circular initialization of variable `fail_circular.d2`
+fail_compilation/fail_circular.d(28): Error: circular initialization of variable `fail_circular.e1`
+fail_compilation/fail_circular.d(29): Error: circular initialization of variable `fail_circular.e2`
---
*/
auto a1 = a1; // semantic error (cannot determine expression type)
@@ -31,16 +31,16 @@ enum int e2 = .e2; // CTFE error
/*
TEST_OUTPUT:
---
-fail_compilation/fail_circular.d(47): Error: circular reference to variable 'fail_circular.a1a'
-fail_compilation/fail_circular.d(49): Error: circular reference to variable 'fail_circular.a2a'
-fail_compilation/fail_circular.d(52): Error: circular reference to variable 'fail_circular.b1a'
-fail_compilation/fail_circular.d(54): Error: circular reference to variable 'fail_circular.b2a'
-fail_compilation/fail_circular.d(57): Error: circular reference to variable 'fail_circular.c1a'
-fail_compilation/fail_circular.d(59): Error: circular reference to variable 'fail_circular.c2a'
-fail_compilation/fail_circular.d(62): Error: circular initialization of variable 'fail_circular.d1a'
-fail_compilation/fail_circular.d(64): Error: circular initialization of variable 'fail_circular.d2a'
-fail_compilation/fail_circular.d(67): Error: circular initialization of variable 'fail_circular.e1a'
-fail_compilation/fail_circular.d(69): Error: circular initialization of variable 'fail_circular.e2a'
+fail_compilation/fail_circular.d(47): Error: circular reference to variable `fail_circular.a1a`
+fail_compilation/fail_circular.d(49): Error: circular reference to variable `fail_circular.a2a`
+fail_compilation/fail_circular.d(52): Error: circular reference to variable `fail_circular.b1a`
+fail_compilation/fail_circular.d(54): Error: circular reference to variable `fail_circular.b2a`
+fail_compilation/fail_circular.d(57): Error: circular reference to variable `fail_circular.c1a`
+fail_compilation/fail_circular.d(59): Error: circular reference to variable `fail_circular.c2a`
+fail_compilation/fail_circular.d(62): Error: circular initialization of variable `fail_circular.d1a`
+fail_compilation/fail_circular.d(64): Error: circular initialization of variable `fail_circular.d2a`
+fail_compilation/fail_circular.d(67): Error: circular initialization of variable `fail_circular.e1a`
+fail_compilation/fail_circular.d(69): Error: circular initialization of variable `fail_circular.e2a`
---
*/
auto a1a = a1b;
@@ -71,12 +71,12 @@ enum int e2b = .e2a; // CTFE error
/*
TEST_OUTPUT:
---
-fail_compilation/fail_circular.d(84): Error: circular reference to variable 'fail_circular.S1.a1'
-fail_compilation/fail_circular.d(88): Error: circular reference to variable 'fail_circular.S2.b1'
-fail_compilation/fail_circular.d(92): Error: circular reference to variable 'fail_circular.S3.c1'
-fail_compilation/fail_circular.d(97): Error: circular reference to variable 'fail_circular.S4.a1a'
-fail_compilation/fail_circular.d(102): Error: circular reference to variable 'fail_circular.S5.b1a'
-fail_compilation/fail_circular.d(107): Error: circular reference to variable 'fail_circular.S6.c1a'
+fail_compilation/fail_circular.d(84): Error: circular reference to variable `fail_circular.S1.a1`
+fail_compilation/fail_circular.d(88): Error: circular reference to variable `fail_circular.S2.b1`
+fail_compilation/fail_circular.d(92): Error: circular reference to variable `fail_circular.S3.c1`
+fail_compilation/fail_circular.d(97): Error: circular reference to variable `fail_circular.S4.a1a`
+fail_compilation/fail_circular.d(102): Error: circular reference to variable `fail_circular.S5.b1a`
+fail_compilation/fail_circular.d(107): Error: circular reference to variable `fail_circular.S6.c1a`
---
*/
struct S1
@@ -110,12 +110,12 @@ struct S6
/*
TEST_OUTPUT:
---
-fail_compilation/fail_circular.d(123): Error: circular reference to variable 'fail_circular.C.a1'
-fail_compilation/fail_circular.d(125): Error: circular reference to variable 'fail_circular.C.b1'
-fail_compilation/fail_circular.d(127): Error: circular reference to variable 'fail_circular.C.c1'
-fail_compilation/fail_circular.d(130): Error: circular reference to variable 'fail_circular.C.a1a'
-fail_compilation/fail_circular.d(133): Error: circular reference to variable 'fail_circular.C.b1a'
-fail_compilation/fail_circular.d(136): Error: circular reference to variable 'fail_circular.C.c1a'
+fail_compilation/fail_circular.d(123): Error: circular reference to variable `fail_circular.C.a1`
+fail_compilation/fail_circular.d(125): Error: circular reference to variable `fail_circular.C.b1`
+fail_compilation/fail_circular.d(127): Error: circular reference to variable `fail_circular.C.c1`
+fail_compilation/fail_circular.d(130): Error: circular reference to variable `fail_circular.C.a1a`
+fail_compilation/fail_circular.d(133): Error: circular reference to variable `fail_circular.C.b1a`
+fail_compilation/fail_circular.d(136): Error: circular reference to variable `fail_circular.C.c1a`
---
*/
class C
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_circular2.d b/gcc/testsuite/gdc.test/fail_compilation/fail_circular2.d
index f04d272563a..6db68c6efd4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_circular2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_circular2.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail_circular2.d(10): Error: circular initialization of variable 'fail_circular2.S.d1'
-fail_compilation/fail_circular2.d(12): Error: circular initialization of variable 'fail_circular2.S.e1'
+fail_compilation/fail_circular2.d(10): Error: circular initialization of variable `fail_circular2.S.d1`
+fail_compilation/fail_circular2.d(12): Error: circular initialization of variable `fail_circular2.S.e1`
---
*/
struct S
@@ -15,8 +15,8 @@ struct S
/*
TEST_OUTPUT:
---
-fail_compilation/fail_circular2.d(24): Error: circular initialization of variable 'fail_circular2.C.d1'
-fail_compilation/fail_circular2.d(26): Error: circular initialization of variable 'fail_circular2.C.e1'
+fail_compilation/fail_circular2.d(24): Error: circular initialization of variable `fail_circular2.C.d1`
+fail_compilation/fail_circular2.d(26): Error: circular initialization of variable `fail_circular2.C.e1`
---
*/
class C
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_contracts3.d b/gcc/testsuite/gdc.test/fail_compilation/fail_contracts3.d
index de3e9bb7081..b0b366b3b7b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_contracts3.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_contracts3.d
@@ -6,9 +6,9 @@ fail_compilation/fail_contracts3.d(13): Error: function `fail_contracts3.D.foo`
*/
class C {
- void foo(){}
+ void foo(){}
}
class D : C {
- override void foo()in{}do{}
+ override void foo()in{}do{}
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d b/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d
index ba930f01a02..2a62f53bbd3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d
@@ -3,8 +3,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/fail_opover.d(13): Error: no [] operator overload for type object.Object
-fail_compilation/fail_opover.d(17): Error: no [] operator overload for type TestS
+fail_compilation/fail_opover.d(13): Error: no `[]` operator overload for type `object.Object`
+fail_compilation/fail_opover.d(17): Error: no `[]` operator overload for type `TestS`
---
*/
void test1()
@@ -20,18 +20,18 @@ void test1()
/*
TEST_OUTPUT:
---
-fail_compilation/fail_opover.d(46): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(47): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(48): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(49): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(50): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(51): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(52): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(53): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(54): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(55): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(56): Error: no [] operator overload for type S
-fail_compilation/fail_opover.d(57): Error: no [] operator overload for type S
+fail_compilation/fail_opover.d(46): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(47): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(49): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(50): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(51): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(52): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(53): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(54): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(55): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(56): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(57): Error: no `[]` operator overload for type `S`
---
*/
void test2()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_pretty_errors.d b/gcc/testsuite/gdc.test/fail_compilation/fail_pretty_errors.d
new file mode 100644
index 00000000000..d25e8f77e05
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_pretty_errors.d
@@ -0,0 +1,36 @@
+/*
+REQUIRED_ARGS: -verrors=context
+TEST_OUTPUT:
+---
+fail_compilation/fail_pretty_errors.d(20): Error: undefined identifier `a`
+ a = 1;
+ ^
+fail_compilation/fail_pretty_errors.d-mixin-25(25): Error: undefined identifier `b`
+fail_compilation/fail_pretty_errors.d(30): Error: cannot implicitly convert expression `5` of type `int` to `string`
+ string x = 5;
+ ^
+fail_compilation/fail_pretty_errors.d(35): Error: mixin `fail_pretty_errors.testMixin2.mixinTemplate!()` error instantiating
+ mixin mixinTemplate;
+ ^
+---
+*/
+
+void foo()
+{
+ a = 1;
+}
+
+void testMixin1()
+{
+ mixin("b = 1;");
+}
+
+mixin template mixinTemplate()
+{
+ string x = 5;
+}
+
+void testMixin2()
+{
+ mixin mixinTemplate;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_scope.d b/gcc/testsuite/gdc.test/fail_compilation/fail_scope.d
index c6342534692..41a8c2d85e3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_scope.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_scope.d
@@ -1,16 +1,19 @@
/*
-PERMUTE_ARGS:
-REQUIRED_ARGS: -dip25
+REQUIRED_ARGS:
TEST_OUTPUT:
---
-fail_compilation/fail_scope.d(45): Error: returning `cast(char[])string` escapes a reference to local variable `string`
-fail_compilation/fail_scope.d(63): Error: returning `s.bar()` escapes a reference to local variable `s`
-fail_compilation/fail_scope.d(82): Error: returning `& string` escapes a reference to local variable `string`
-fail_compilation/fail_scope.d(92): Error: returning `cast(int[])a` escapes a reference to local variable `a`
-fail_compilation/fail_scope.d(100): Error: returning `cast(int[])a` escapes a reference to local variable `a`
-fail_compilation/fail_scope.d(108): Error: escaping reference to outer local variable `x`
-fail_compilation/fail_scope.d(127): Error: returning `s.bar()` escapes a reference to local variable `s`
-fail_compilation/fail_scope.d(137): Error: returning `foo16226(i)` escapes a reference to local variable `i`
+fail_compilation/fail_scope.d(44): Error: returning `cast(char[])string` escapes a reference to local variable `string`
+fail_compilation/fail_scope.d(62): Error: returning `s.bar()` escapes a reference to local variable `s`
+fail_compilation/fail_scope.d(73): Error: `fail_scope.foo8` called with argument types `(int)` matches both:
+fail_compilation/fail_scope.d(67): `fail_scope.foo8(ref int x)`
+and:
+fail_compilation/fail_scope.d(68): `fail_scope.foo8(return ref int x)`
+fail_compilation/fail_scope.d(81): Error: returning `& string` escapes a reference to local variable `string`
+fail_compilation/fail_scope.d(91): Error: returning `cast(int[])a` escapes a reference to local variable `a`
+fail_compilation/fail_scope.d(99): Error: returning `cast(int[])a` escapes a reference to local variable `a`
+fail_compilation/fail_scope.d(107): Deprecation: escaping reference to outer local variable `x`
+fail_compilation/fail_scope.d(126): Error: returning `s.bar()` escapes a reference to local variable `s`
+fail_compilation/fail_scope.d(136): Error: returning `foo16226(i)` escapes a reference to local variable `i`
---
//fail_compilation/fail_scope.d(30): Error: scope variable `da` may not be returned
//fail_compilation/fail_scope.d(32): Error: scope variable `o` may not be returned
@@ -21,10 +24,6 @@ fail_compilation/fail_scope.d(137): Error: returning `foo16226(i)` escapes a ref
//fail_compilation/fail_scope.d(40): Error: scope variable `p` may not be returned
*/
-
-
-
-
alias int delegate() dg_t;
int[] checkEscapeScope1(scope int[] da) { return da; }
@@ -86,7 +85,7 @@ char* fail141()
int[] test1313b()
out{}
-body
+do
{
int[2] a;
return a;
@@ -94,7 +93,7 @@ body
int[] test1313a()
//out{}
-body
+do
{
int[2] a;
return a;
@@ -142,4 +141,3 @@ ref foo16226(ref int bar) @safe
{
return bar;
}
-
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failattr.d b/gcc/testsuite/gdc.test/fail_compilation/failattr.d
index e0b4562bcad..c7f0f31d068 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/failattr.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/failattr.d
@@ -3,12 +3,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/failattr.d(16): Error: variable failattr.C2901.v1 cannot be synchronized
-fail_compilation/failattr.d(17): Error: variable failattr.C2901.v2 cannot be override
-fail_compilation/failattr.d(18): Error: variable failattr.C2901.v3 cannot be abstract
-fail_compilation/failattr.d(19): Error: variable failattr.C2901.v4 cannot be final, perhaps you meant const?
-fail_compilation/failattr.d(31): Error: variable failattr.C2901.v13 cannot be final abstract synchronized override
-fail_compilation/failattr.d(33): Error: variable failattr.C2901.v14 cannot be final, perhaps you meant const?
+fail_compilation/failattr.d(16): Error: variable `failattr.C2901.v1` cannot be `synchronized`
+fail_compilation/failattr.d(17): Error: variable `failattr.C2901.v2` cannot be `override`
+fail_compilation/failattr.d(18): Error: variable `failattr.C2901.v3` cannot be `abstract`
+fail_compilation/failattr.d(19): Error: variable `failattr.C2901.v4` cannot be `final`, perhaps you meant `const`?
+fail_compilation/failattr.d(31): Error: variable `failattr.C2901.v13` cannot be `final abstract synchronized override`
+fail_compilation/failattr.d(33): Error: variable `failattr.C2901.v14` cannot be `final`, perhaps you meant `const`?
---
*/
class C2901
diff --git a/gcc/testsuite/gdc.test/fail_compilation/faildeleteaa.d b/gcc/testsuite/gdc.test/fail_compilation/faildeleteaa.d
index f190141aabe..95663ea7790 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/faildeleteaa.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/faildeleteaa.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/faildeleteaa.d(11): Error: cannot delete type int
+fail_compilation/faildeleteaa.d(12): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+fail_compilation/faildeleteaa.d(12): Error: cannot delete type `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/faildottypeinfo.d b/gcc/testsuite/gdc.test/fail_compilation/faildottypeinfo.d
index af238f71dcf..d7853e6b346 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/faildottypeinfo.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/faildottypeinfo.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/faildottypeinfo.d(11): Error: no property 'typeinfo' for type 'int'
-fail_compilation/faildottypeinfo.d(12): Error: no property 'typeinfo' for type 'object.Object'
+fail_compilation/faildottypeinfo.d(11): Error: no property `typeinfo` for type `int`
+fail_compilation/faildottypeinfo.d(12): Error: no property `typeinfo` for type `object.Object`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failmemalloc.d b/gcc/testsuite/gdc.test/fail_compilation/failmemalloc.d
deleted file mode 100644
index 713c180ec9e..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/failmemalloc.d
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/failmemalloc.d(13): Error: member allocators not supported by CTFE
----
-*/
-
-struct S
-{
- new(size_t sz) { return null; }
-}
-
-S* s = new S();
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failob1.d b/gcc/testsuite/gdc.test/fail_compilation/failob1.d
new file mode 100644
index 00000000000..7fe73d3d179
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/failob1.d
@@ -0,0 +1,34 @@
+/*
+REQUIRED_ARGS:-preview=dip1021
+TEST_OUTPUT:
+---
+fail_compilation/failob1.d(104): Error: variable `failob1.test1.a1` is left dangling at return
+fail_compilation/failob1.d(105): Error: variable `failob1.test2.a2` is left dangling at return
+fail_compilation/failob1.d(107): Error: variable `failob1.test4.s4` is left dangling at return
+fail_compilation/failob1.d(108): Error: variable `failob1.test5.dg5` is left dangling at return
+fail_compilation/failob1.d(115): Error: variable `failob1.test12.p12` is left dangling at return
+---
+*/
+
+struct S { int i; int* f; }
+struct T { int i; const(int)* f; }
+class C { int i; int* f; }
+
+#line 100
+
+@live
+{
+ // Test what is and is not a trackable variable
+ void test1(int[] a1) { } // error
+ void test2(int*[3] a2) { } // error
+ void test3(const int*[3] a) { } // ok
+ void test4(S s4) { } // error
+ void test5(int delegate() dg5) { } // error
+ void test6(const(int*)[3] a) { } // ok
+ void test7(const(int)*[3] a) { } // ok
+ void test8(const(int)* p) { } // ok
+ void test9(T t) { } // ok
+ void test10(C c) { } // ok
+ void test11(int i) { } // ok
+ void test12(int* p12) { } // error
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failob2.d b/gcc/testsuite/gdc.test/fail_compilation/failob2.d
new file mode 100644
index 00000000000..bd526485409
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/failob2.d
@@ -0,0 +1,67 @@
+// REQUIRED_ARGS: -preview=dip1021
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/failob2.d(105): Error: variable `failob2.foo1!int.foo1.p` has undefined state and cannot be read
+fail_compilation/failob2.d(105): Error: variable `failob2.foo1!int.foo1.p` is returned but is Undefined
+fail_compilation/failob2.d(124): Error: template instance `failob2.foo1!int` error instantiating
+fail_compilation/failob2.d(111): Error: variable `failob2.foo2!int.foo2.p` has undefined state and cannot be read
+fail_compilation/failob2.d(111): Error: variable `failob2.foo2!int.foo2.p` is returned but is Undefined
+fail_compilation/failob2.d(125): Error: template instance `failob2.foo2!int` error instantiating
+fail_compilation/failob2.d(119): Error: variable `failob2.foo3!int.foo3.p` has undefined state and cannot be read
+fail_compilation/failob2.d(119): Error: variable `failob2.foo3!int.foo3.p` is returned but is Undefined
+fail_compilation/failob2.d(126): Error: template instance `failob2.foo3!int` error instantiating
+---
+*/
+
+#line 100
+
+@live
+T* foo1(T)()
+{
+ T* p = void;
+ return p;
+}
+
+template foo2(T) {
+ @live T* foo2() {
+ T* p = void;
+ return p;
+ }
+}
+
+@live
+template foo3(T) {
+ T* foo3() {
+ T* p = void;
+ return p;
+ }
+}
+
+void test1() {
+ foo1!int();
+ foo2!int();
+ foo3!int();
+}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/failob2.d(205): Error: variable `failob2.foo4!int.foo4.p` is left dangling at return
+fail_compilation/failob2.d(209): Error: template instance `failob2.foo4!int` error instantiating
+---
+*/
+
+#line 200
+
+void* alloc(size_t);
+
+@live void foo4(T)()
+{
+ auto p = alloc(4);
+}
+
+void test2() {
+ foo4!int();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/failoffset.d b/gcc/testsuite/gdc.test/fail_compilation/failoffset.d
index c991d4752ee..bbec698a740 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/failoffset.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/failoffset.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/failoffset.d(12): Error: no property 'offset' for type 'int'
+fail_compilation/failoffset.d(12): Error: no property `offset` for type `int`
fail_compilation/failoffset.d(12): while evaluating: `static assert(b.offset == 4)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix17349.d b/gcc/testsuite/gdc.test/fail_compilation/fix17349.d
new file mode 100644
index 00000000000..493d30f8f17
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fix17349.d
@@ -0,0 +1,36 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/fix17349.d(35): Error: cannot implicitly override base class method `fix17349.E.foo` with `fix17349.F.foo`; add `override` attribute
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=17349
+
+struct S { }
+
+class C {
+ void bar();
+ void foo(void* p);
+ void abc(Object);
+ void def(S);
+}
+
+class D : C {
+ override void bar() const;
+ override void foo(const void*);
+ override void abc(const Object);
+ override void def(const S);
+}
+
+alias fp_t = void function(int*);
+@safe void abc(const int*);
+fp_t fp = &abc;
+
+
+class E {
+ void foo(void*);
+}
+
+class F : E {
+ void foo(const void*);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix17635.d b/gcc/testsuite/gdc.test/fail_compilation/fix17635.d
new file mode 100644
index 00000000000..27f55e06507
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fix17635.d
@@ -0,0 +1,23 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fix17635.d(22): Error: cannot implicitly convert expression `f(& p)` of type `immutable(int)**` to `immutable(int**)`
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=17635
+// https://issues.dlang.org/show_bug.cgi?id=15660
+
+alias T = immutable int;
+
+T** f(const T** input) pure
+{
+ T** output;
+ return output;
+}
+
+void main()
+{
+ T i;
+ T* p = &i;
+ immutable T** r = f(&p);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix17751.d b/gcc/testsuite/gdc.test/fail_compilation/fix17751.d
new file mode 100644
index 00000000000..11b9c548993
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fix17751.d
@@ -0,0 +1,22 @@
+/* REQUIRED_ARGS: -m64
+ * TEST_OUTPUT:
+---
+fail_compilation/fix17751.d(15): Error: last parameter to `__simd()` must be a constant
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=17751
+
+import core.simd;
+
+pure @safe V1 simd(XMM opcode, V1, V2)(V1 op1, V2 op2, ubyte imm8)
+ if (is(V1 == __vector) && is(V2 == __vector))
+{
+ return cast(V1)__simd(opcode, op1, op2, imm8);
+}
+
+void main()
+{
+ float4 a, b;
+ a = simd!(XMM.CMPPD)(a, b, 0x7A);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix18575.d b/gcc/testsuite/gdc.test/fail_compilation/fix18575.d
new file mode 100644
index 00000000000..7b8f287b28e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fix18575.d
@@ -0,0 +1,41 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fix18575.d(27): Error: returning `s.foo()` escapes a reference to local variable `s`
+fail_compilation/fix18575.d(31): Error: returning `s.foo()` escapes a reference to local variable `s`
+fail_compilation/fix18575.d(35): Error: returning `s.abc()` escapes a reference to local variable `s`
+fail_compilation/fix18575.d(39): Error: returning `s.ghi(t)` escapes a reference to local variable `t`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18575
+
+@safe:
+
+struct S {
+@safe:
+ int x;
+
+ void bar() { }
+ auto foo() { return &this.bar; }
+ auto def() { return &bar; }
+ auto abc() { return &x; }
+ auto ghi(ref S s) { return &s.bar; }
+}
+
+auto f(S s) {
+ return s.foo();
+}
+
+auto g(S s) {
+ return s.foo();
+}
+
+auto h(S s) {
+ return s.abc();
+}
+
+auto j(S s, S t) {
+ return s.ghi(t);
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix19018.d b/gcc/testsuite/gdc.test/fail_compilation/fix19018.d
new file mode 100644
index 00000000000..b2a2a304bb7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fix19018.d
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fix19018.d(16): Error: `0b` isn't a valid integer literal, use `0b0` instead
+fail_compilation/fix19018.d(17): Error: `0B` isn't a valid integer literal, use `0B0` instead
+fail_compilation/fix19018.d(18): Error: `0x` isn't a valid integer literal, use `0x0` instead
+fail_compilation/fix19018.d(19): Error: `0X` isn't a valid integer literal, use `0X0` instead
+fail_compilation/fix19018.d(20): Error: `0x_` isn't a valid integer literal, use `0x0` instead
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=19018
+
+void foo()
+{
+ auto a = 0b;
+ auto b = 0B;
+ auto c = 0x;
+ auto d = 0X;
+ auto e = 0x_;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix19059.d b/gcc/testsuite/gdc.test/fail_compilation/fix19059.d
new file mode 100644
index 00000000000..0be003a2f3b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fix19059.d
@@ -0,0 +1,19 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fix19059.d(16): Error: octal digit expected, not `8`
+fail_compilation/fix19059.d(16): Error: octal literals larger than 7 are no longer supported
+fail_compilation/fix19059.d(17): Error: octal digit expected, not `9`
+fail_compilation/fix19059.d(17): Error: octal literals larger than 7 are no longer supported
+fail_compilation/fix19059.d(18): Error: octal literals `010` are no longer supported, use `std.conv.octal!10` instead
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=19059
+
+void foo()
+{
+ auto a = 08;
+ auto b = 09;
+ auto c = 010;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix19246.d b/gcc/testsuite/gdc.test/fail_compilation/fix19246.d
new file mode 100644
index 00000000000..55b9650b6b6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fix19246.d
@@ -0,0 +1,19 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fix19246.d(15): Error: `0b_` isn't a valid integer literal, use `0b0` instead
+fail_compilation/fix19246.d(16): Error: `0B_` isn't a valid integer literal, use `0B0` instead
+fail_compilation/fix19246.d(17): Error: `0b` isn't a valid integer literal, use `0b0` instead
+fail_compilation/fix19246.d(18): Error: `0B` isn't a valid integer literal, use `0B0` instead
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=19246
+
+void foo()
+{
+ auto a = 0b_;
+ auto b = 0B_;
+ auto c = 0b;
+ auto d = 0B;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix5212.d b/gcc/testsuite/gdc.test/fail_compilation/fix5212.d
new file mode 100644
index 00000000000..faf2e2988ef
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fix5212.d
@@ -0,0 +1,17 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/fix5212.d(14): Error: scope variable `args_` assigned to non-scope `this.args`
+---
+*/
+
+
+// https://issues.dlang.org/show_bug.cgi?id=5212
+
+class Foo {
+ int[] args;
+ @safe this(int[] args_...) {
+ args = args_;
+ }
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fob1.d b/gcc/testsuite/gdc.test/fail_compilation/fob1.d
new file mode 100644
index 00000000000..1f2312281b4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fob1.d
@@ -0,0 +1,63 @@
+// REQUIRED_ARGS: -preview=dip1021
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob1.d(104): Error: variable `fob1.foo1.p` has undefined state and cannot be read
+fail_compilation/fob1.d(104): Error: variable `fob1.foo1.p` is returned but is Undefined
+---
+*/
+
+#line 100
+
+@live int* foo1()
+{
+ int* p = void;
+ return p;
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob1.d(203): Error: variable `fob1.foo2.p` is left dangling at return
+---
+*/
+
+#line 200
+
+@live void foo2()
+{
+ int* p;
+ p = null;
+}
+
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob1.d(304): Error: variable `fob1.foo3.p` has undefined state and cannot be read
+fail_compilation/fob1.d(304): Error: variable `fob1.foo3.p` is returned but is Undefined
+fail_compilation/fob1.d(303): Error: variable `fob1.foo3.q` is left dangling at return
+---
+*/
+
+#line 300
+
+@live int* foo3(int* p)
+{
+ int* q = p;
+ return p;
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob1.d(405): Error: variable `fob1.foo4.bq` has undefined state and cannot be read
+---
+*/
+
+#line 400
+
+@live int* foo4(int* p)
+{
+ scope int* bq = p;
+ scope const int* cq = p;
+ *bq = 1;
+ return p;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fob2.d b/gcc/testsuite/gdc.test/fail_compilation/fob2.d
new file mode 100644
index 00000000000..dbe8ea2f883
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/fob2.d
@@ -0,0 +1,178 @@
+/* Testing Ownership/Borrowing system
+REQUIRED_ARGS: -preview=dip1021
+ */
+
+int* malloc();
+void free(int*);
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob2.d(110): Error: variable `fob2.foo1.b1` has undefined state and cannot be read
+fail_compilation/fob2.d(103): Error: variable `fob2.foo1.p` is left dangling at return
+---
+*/
+
+#line 100
+
+@live int foo1(int i)
+{
+ int* p = malloc();
+ scope const(int)* b1, b2;
+ if (i)
+ b1 = p;
+ else
+ b2 = p;
+ *p = 3;
+ return *b1;
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob2.d(203): Error: variable `fob2.zoo2.p` is passed as Owner more than once
+fail_compilation/fob2.d(202): Error: variable `fob2.zoo2.p` is left dangling at return
+---
+*/
+
+#line 200
+
+@live void zoo2() {
+ int* p = malloc();
+ foo2(p, p + 1);
+}
+
+@live void foo2( scope int* p, scope int* q );
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob2.d(303): Error: variable `fob2.foo3.b` is left dangling at return
+---
+*/
+
+#line 300
+
+@live void foo3()
+{
+ scope int* b = malloc();
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob2.d(427): Error: variable `fob2.test43.p` is both Owner and Undefined
+fail_compilation/fob2.d(429): Error: variable `fob2.test43.p` has undefined state and cannot be read
+fail_compilation/fob2.d(429): Error: variable `fob2.test43.p` is not Owner, cannot consume its value
+fail_compilation/fob2.d(432): Error: variable `fob2.test43.p` has undefined state and cannot be read
+fail_compilation/fob2.d(432): Error: variable `fob2.test43.p` is not Owner, cannot consume its value
+---
+*/
+#line 400
+
+
+bool f();
+
+@live void test41(int* p, int i)
+{
+ for (; f(); ++i)
+ {
+ --i;
+ free(p);
+ p = null;
+ }
+ free(p);
+}
+
+@live void test42(int* p, int i)
+{
+ for (; f(); ++i)
+ {
+ --i;
+ }
+ free(p);
+}
+
+
+@live void test43(int* p, int i)
+{
+ for (; f(); ++i)
+ {
+ free(p);
+ --i;
+ }
+ free(p);
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob2.d(506): Error: variable `fob2.test51.p` has undefined state and cannot be read
+fail_compilation/fob2.d(515): Error: variable `fob2.test52.p` has undefined state and cannot be read
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=20747
+
+#line 500
+
+@live test51()
+{
+ int x;
+ scope p = &x;
+ x = 3;
+ *p = 4;
+}
+
+
+@live void test52()
+{
+ int x = 5;
+ auto p = &x;
+ auto q = &x;
+ *p = 3;
+}
+
+
+@live void test53()
+{
+ scope int x;
+ scope int y;
+ y = x;
+ x = 3;
+ y = 4;
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob2.d(603): Error: variable `fob2.test6.p` is left dangling at return
+---
+*/
+
+#line 600
+
+@live extern (C) void foo6(int, scope ...);
+
+@live void test6(int* p)
+{
+ foo6(1, p);
+}
+
+@live extern (C) void foo6b(int, scope const ...);
+
+@live int* test6b(return int* p)
+{
+ foo6b(1, p, p);
+ return p;
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/fob2.d(705): Error: variable `fob2.test7.p` is not Owner, cannot consume its value
+---
+*/
+
+#line 700
+
+void free7(int*);
+
+@live void test7(scope int* p)
+{
+ free7(p);
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/gag4269f.d b/gcc/testsuite/gdc.test/fail_compilation/gag4269f.d
index eaf8593fc1b..84d39ebae50 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/gag4269f.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/gag4269f.d
@@ -3,7 +3,7 @@
TEST_OUTPUT:
---
fail_compilation/gag4269f.d(11): Error: undefined identifier `Y9`, did you mean interface `X9`?
-fail_compilation/gag4269f.d(11): Error: variable gag4269f.X9.y field not allowed in interface
+fail_compilation/gag4269f.d(11): Error: variable `gag4269f.X9.y` field not allowed in interface
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/goto1.d b/gcc/testsuite/gdc.test/fail_compilation/goto1.d
new file mode 100644
index 00000000000..b35a1c3a0dc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/goto1.d
@@ -0,0 +1,26 @@
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/goto1.d(1010): Error: `return` statements cannot be in `finally` bodies
+---
+ */
+
+void foo();
+void bar();
+
+#line 1000
+
+void test2()
+{
+ try
+ {
+ foo();
+ }
+ finally
+ {
+ bar();
+ return;
+ }
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/goto2.d b/gcc/testsuite/gdc.test/fail_compilation/goto2.d
new file mode 100644
index 00000000000..72e8bcb6d07
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/goto2.d
@@ -0,0 +1,143 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/goto2.d(1024): Error: case cannot be in different `try` block level from `switch`
+fail_compilation/goto2.d(1026): Error: default cannot be in different `try` block level from `switch`
+fail_compilation/goto2.d(1003): Error: cannot `goto` into `try` block
+---
+ */
+
+
+void foo();
+void bar();
+
+#line 1000
+
+void test1()
+{
+ goto L1;
+ try
+ {
+ foo();
+ L1:
+ { }
+ }
+ finally
+ {
+ bar();
+ }
+
+ /********************************/
+
+ int i;
+ switch (i)
+ {
+ case 1:
+ try
+ {
+ foo();
+ case 2:
+ { }
+ default:
+ { }
+ }
+ finally
+ {
+ bar();
+ }
+ break;
+ }
+}
+
+/**************************************************
+https://issues.dlang.org/show_bug.cgi?id=11540
+goto label + try-catch-finally / with statement
+
+TEST_OUTPUT:
+---
+fail_compilation/goto2.d(1121): Error: cannot `goto` into `try` block
+---
+*/
+#line 1100
+
+int interpret3a()
+{
+ // enter to TryCatchStatement.body
+ {
+ bool c = false;
+ try
+ {
+ if (c) // need to bypass front-end optimization
+ throw new Exception("");
+ else
+ {
+ goto Lx;
+ L1:
+ c = true;
+ }
+ }
+ catch (Exception e) {}
+
+ Lx:
+ if (!c)
+ goto L1;
+ }
+ return 1;
+}
+
+/**************************************************
+https://issues.dlang.org/show_bug.cgi?id=11540
+goto label + try-catch-finally / with statement
+
+TEST_OUTPUT:
+---
+fail_compilation/goto2.d(1217): Error: cannot `goto` into `try` block
+---
+*/
+#line 1200
+
+int interpret3b()
+{
+ // enter back to TryFinallyStatement.body
+ {
+ bool c = false;
+ try
+ {
+ goto Lx;
+ L1:
+ c = true;
+ }
+ finally {
+ }
+
+ Lx:
+ if (!c)
+ goto L1;
+ }
+
+ return 1;
+}
+
+/**************************************************
+https://issues.dlang.org/show_bug.cgi?id=13815
+
+TEST_OUTPUT:
+---
+fail_compilation/goto2.d(1234): Error: cannot `goto` into `try` block
+---
+*/
+
+bool f()
+{
+ goto L;
+ try
+ {
+L: // line 7
+ throw new Exception(""); // line 8
+ }
+ catch (Exception e)
+ {
+ return true;
+ }
+ return false;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/goto3.d b/gcc/testsuite/gdc.test/fail_compilation/goto3.d
new file mode 100644
index 00000000000..4b811fa898a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/goto3.d
@@ -0,0 +1,37 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/goto3.d(1010): Error: case cannot be in different `try` block level from `switch`
+fail_compilation/goto3.d(1012): Error: default cannot be in different `try` block level from `switch`
+---
+ */
+
+
+void foo();
+void bar();
+
+#line 1000
+
+void test1()
+{
+ int i;
+ switch (i)
+ {
+ case 1:
+ try
+ {
+ foo();
+ case 2:
+ { }
+ default:
+ { }
+ }
+ finally
+ {
+ bar();
+ }
+ break;
+ }
+}
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10016.d b/gcc/testsuite/gdc.test/fail_compilation/ice10016.d
index ff721318e50..2f444f11713 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10016.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10016.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/ice10016.d(33): Error: undefined identifier `unknownIdentifier`
-fail_compilation/ice10016.d(47): Error: template instance ice10016.RefCounted!(S) error instantiating
+fail_compilation/ice10016.d(47): Error: template instance `ice10016.RefCounted!(S)` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10076.d b/gcc/testsuite/gdc.test/fail_compilation/ice10076.d
index 8337d7f459e..24f92d738e2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10076.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10076.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10076.d(18): Error: template instance getMembersAndAttributesWhere!() template 'getMembersAndAttributesWhere' is not defined
-fail_compilation/ice10076.d(23): Error: template instance ice10076.getValidaterAttrs!string error instantiating
-fail_compilation/ice10076.d(13): instantiated from here: validate!string
+fail_compilation/ice10076.d(18): Error: template instance `getMembersAndAttributesWhere!()` template `getMembersAndAttributesWhere` is not defined
+fail_compilation/ice10076.d(23): Error: template instance `ice10076.getValidaterAttrs!string` error instantiating
+fail_compilation/ice10076.d(13): instantiated from here: `validate!string`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10212.d b/gcc/testsuite/gdc.test/fail_compilation/ice10212.d
index ce4a671f12f..b9fe2aa6557 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10212.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10212.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10212.d(12): Error: mismatched function return type inference of `int function() pure nothrow @nogc @safe` and `int`
+fail_compilation/ice10212.d(13): Error: Expected return type of `int`, not `int function() pure nothrow @nogc @safe`:
+fail_compilation/ice10212.d(13): Return type of `int` inferred here.
---
*/
@@ -9,7 +10,7 @@ int delegate() foo()
{
// returns "int function() pure nothrow @safe function() pure nothrow @safe"
// and it mismatches to "int delegate()"
- return () => {
+ return () => () {
return 1;
};
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10259.d b/gcc/testsuite/gdc.test/fail_compilation/ice10259.d
index e12df7dd5e5..4a7b0df2ed1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10259.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10259.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10259.d(11): Error: circular reference to 'ice10259.D.d'
-fail_compilation/ice10259.d(11): called from here: (*function () => x)()
+fail_compilation/ice10259.d(11): Error: circular reference to `ice10259.D.d`
+fail_compilation/ice10259.d(11): called from here: `(*function () pure nothrow @safe => x)()`
---
*/
class D
@@ -15,8 +15,8 @@ enum x = new D;
/*
TEST_OUTPUT:
---
-fail_compilation/ice10259.d(25): Error: circular reference to 'ice10259.D2.d'
-fail_compilation/ice10259.d(25): called from here: (*function () => x)()
+fail_compilation/ice10259.d(25): Error: circular reference to `ice10259.D2.d`
+fail_compilation/ice10259.d(25): called from here: `(*function () pure nothrow @safe => x)()`
---
*/
class D2
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10341.d b/gcc/testsuite/gdc.test/fail_compilation/ice10341.d
index ed6b6cbc48c..56be4b38f1a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10341.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10341.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10341.d(10): Error: case range not in switch statement
+fail_compilation/ice10341.d(10): Error: case range not in `switch` statement
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10419.d b/gcc/testsuite/gdc.test/fail_compilation/ice10419.d
index 47d1f733548..827f0451824 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10419.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10419.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10419.d(12): Error: arr().length is not an lvalue
+fail_compilation/ice10419.d(12): Error: `arr().length` is not an lvalue and cannot be modified
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10600.d b/gcc/testsuite/gdc.test/fail_compilation/ice10600.d
index b1ee9067b9b..a240045ce67 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10600.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10600.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: imports/ice10600a.d imports/ice10600b.d
TEST_OUTPUT:
---
-fail_compilation/ice10600.d(30): Error: template instance to!(int, double) does not match template declaration to(T)
+fail_compilation/ice10600.d(31): Error: template instance `to!(int, double)` does not match template declaration `to(T)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10616.d b/gcc/testsuite/gdc.test/fail_compilation/ice10616.d
index 6ccd1c31b60..654e16fb2b9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10616.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10616.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10616.d(8): Error: class ice10616.A is forward referenced when looking for 'B'
+fail_compilation/ice10616.d(8): Error: class `ice10616.A` is forward referenced when looking for `B`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10624.d b/gcc/testsuite/gdc.test/fail_compilation/ice10624.d
index 883b98ed118..3bc3c7d5028 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10624.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10624.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10624.d(38): Error: need member function opCmp() for struct Tuple!(Msg) to compare
-fail_compilation/ice10624.d(48): Error: template instance ice10624.Variant.handler!(Tuple!(Msg)) error instantiating
-fail_compilation/ice10624.d(21): instantiated from here: opAssign!(Tuple!(Msg))
+fail_compilation/ice10624.d(38): Error: need member function `opCmp()` for struct `Tuple!(Msg)` to compare
+fail_compilation/ice10624.d(48): Error: template instance `ice10624.Variant.handler!(Tuple!(Msg))` error instantiating
+fail_compilation/ice10624.d(21): instantiated from here: `opAssign!(Tuple!(Msg))`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10651.d b/gcc/testsuite/gdc.test/fail_compilation/ice10651.d
index 51e0a728ca0..1f87955b959 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10651.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10651.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10651.d(11): Error: can only throw class objects derived from Throwable, not type `int*`
+fail_compilation/ice10651.d(11): Error: can only throw class objects derived from `Throwable`, not type `int*`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10713.d b/gcc/testsuite/gdc.test/fail_compilation/ice10713.d
index 9ef07b34027..f3680329941 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10713.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10713.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10713.d(10): Error: no property 'nonExistingField' for type 'S'
+fail_compilation/ice10713.d(10): Error: no property `nonExistingField` for type `ice10713.S`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10727a.d b/gcc/testsuite/gdc.test/fail_compilation/ice10727a.d
index a7529dc63cd..ebefe33f2db 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10727a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10727a.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -c
+// EXTRA_FILES: imports/foo10727a.d imports/stdtraits10727.d
/*
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10727b.d b/gcc/testsuite/gdc.test/fail_compilation/ice10727b.d
index 958b5069329..125ac12b193 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10727b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10727b.d
@@ -1,4 +1,5 @@
// REQUIRED_ARGS: -c
+// EXTRA_FILES: imports/foo10727b.d imports/stdtraits10727.d
/*
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10922.d b/gcc/testsuite/gdc.test/fail_compilation/ice10922.d
index 9eeb622300a..c227ee55eb4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10922.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10922.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10922.d(10): Error: function `ice10922.__lambda4(const(uint) n)` is not callable using argument types `()`
-fail_compilation/ice10922.d(10): missing argument for parameter #1: `const(uint) n`
+fail_compilation/ice10922.d(10): Error: function `ice10922.__lambda4(in uint n)` is not callable using argument types `()`
+fail_compilation/ice10922.d(10): missing argument for parameter #1: `in uint n`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10938.d b/gcc/testsuite/gdc.test/fail_compilation/ice10938.d
index 2b3eab52ea3..2084e32d6b8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10938.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10938.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10938.d(12): Error: no property 'opts' for type 'ice10938.C'
+fail_compilation/ice10938.d(13): Error: no property `opts` for type `ice10938.C`
+fail_compilation/ice10938.d(13): potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10949.d b/gcc/testsuite/gdc.test/fail_compilation/ice10949.d
index e81cf979549..45b18e0de5a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10949.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10949.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice10949.d(12): Deprecation: Using the result of a comma expression is deprecated
+fail_compilation/ice10949.d(12): Error: Using the result of a comma expression is not allowed
fail_compilation/ice10949.d(12): Error: array index 3 is out of bounds `[5, 5][0 .. 2]`
fail_compilation/ice10949.d(12): Error: array index 17 is out of bounds `[2, 3][0 .. 2]`
fail_compilation/ice10949.d(12): while evaluating: `static assert((((([5, 5][3] + global - global) * global / global % global >> global & global | global) ^ global) == 9 , [2, 3][17]) || [3, 3, 3][9] is 4 && [[1, 2, 3]][4].length)`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11086.d b/gcc/testsuite/gdc.test/fail_compilation/ice11086.d
index 0b2ee3060e3..fee958ddcbf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11086.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11086.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice11086.d(10): Error: template instance foo!A template 'foo' is not defined
+fail_compilation/ice11086.d(10): Error: template instance `foo!A` template `foo` is not defined
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11404.d b/gcc/testsuite/gdc.test/fail_compilation/ice11404.d
index c3f1ce608bc..f445903179f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11404.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11404.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice11404.d(10): Error: can't have associative array of (int, int)
+fail_compilation/ice11404.d(10): Error: cannot have associative array of `(int, int)`
---
*/
template TypeTuple(TL...) { alias TL TypeTuple; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice1144.d b/gcc/testsuite/gdc.test/fail_compilation/ice1144.d
index 68563b67127..cd6486436ea 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice1144.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice1144.d
@@ -2,12 +2,12 @@
TEST_OUTPUT:
---
fail_compilation/ice1144.d(14): Error: undefined identifier `a`
-fail_compilation/ice1144.d(23): Error: template instance ice1144.testHelper!("hello", "world") error instantiating
+fail_compilation/ice1144.d(23): Error: template instance `ice1144.testHelper!("hello", "world")` error instantiating
---
*/
-// Issue 1144 - ICE(template.c) template mixin causes DMD crash
-
+// https://issues.dlang.org/show_bug.cgi?id=1144
+// ICE(template.c) template mixin causes DMD crash
char[] testHelper(A ...)()
{
char[] result;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11472.d b/gcc/testsuite/gdc.test/fail_compilation/ice11472.d
index f5b767f2a25..4e12490e35f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11472.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11472.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice11472.d(13): Error: template instance fun2!fun fun2 is not a template declaration, it is a function
-fail_compilation/ice11472.d(18): Error: template instance ice11472.fun1!(fun3) error instantiating
+fail_compilation/ice11472.d(13): Error: template instance `fun2!fun` `fun2` is not a template declaration, it is a function
+fail_compilation/ice11472.d(18): Error: template instance `ice11472.fun1!(fun3)` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11513a.d b/gcc/testsuite/gdc.test/fail_compilation/ice11513a.d
index 576c84550cf..87ce0e13be4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11513a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11513a.d
@@ -1,4 +1,5 @@
/*
+EXTRA_FILES: imports/ice11513x.d
TEST_OUTPUT:
---
fail_compilation/imports/ice11513x.d(1): Error: package name 'ice11513a' conflicts with usage as a module name in file fail_compilation/ice11513a.d
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11513b.d b/gcc/testsuite/gdc.test/fail_compilation/ice11513b.d
index dab09b87e87..4f933f486f5 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11513b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11513b.d
@@ -1,4 +1,5 @@
/*
+EXTRA_FILES: imports/ice11513y.d
TEST_OUTPUT:
---
fail_compilation/imports/ice11513y.d(1): Error: package name 'ice11513b' conflicts with usage as a module name in file fail_compilation/ice11513b.d
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11518.d b/gcc/testsuite/gdc.test/fail_compilation/ice11518.d
index cfceb64ed6b..c8542f77252 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11518.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11518.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice11518.d(17): Error: class ice11518.B matches more than one template declaration:
-fail_compilation/ice11518.d(12): B(T : A!T)
+fail_compilation/ice11518.d(17): Error: class `ice11518.B` matches more than one template declaration:
+fail_compilation/ice11518.d(12): `B(T : A!T)`
and
-fail_compilation/ice11518.d(13): B(T : A!T)
+fail_compilation/ice11518.d(13): `B(T : A!T)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11552.d b/gcc/testsuite/gdc.test/fail_compilation/ice11552.d
index 43fba406133..28d8be1007b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11552.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11552.d
@@ -1,11 +1,10 @@
/*
REQUIRED_ARGS: -o-
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/ice11552.d(14): Error: label `label` is undefined
-fail_compilation/ice11552.d(17): called from here: test11552()
-fail_compilation/ice11552.d(17): while evaluating: `static assert(test11552())`
+fail_compilation/ice11552.d(13): Error: function `ice11552.test11552` label `label` is undefined
+fail_compilation/ice11552.d(16): called from here: `test11552()`
+fail_compilation/ice11552.d(16): while evaluating: `static assert(test11552())`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11553.d b/gcc/testsuite/gdc.test/fail_compilation/ice11553.d
index 8fd0975399f..7497aad6e6a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11553.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11553.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice11553.d(22): Error: recursive template expansion while looking for A!().A()
+fail_compilation/ice11553.d(22): Error: recursive template expansion while looking for `A!().A()`
+fail_compilation/ice11553.d(22): Error: expression `A()` of type `void` does not have a boolean value
---
*/
-
template A(alias T)
{
template A()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11755.d b/gcc/testsuite/gdc.test/fail_compilation/ice11755.d
deleted file mode 100644
index 86ce6f69979..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11755.d
+++ /dev/null
@@ -1,30 +0,0 @@
-// REQUIRED_ARGS: -w
-/*
-TEST_OUTPUT:
----
-fail_compilation/ice11755.d(20): Error: '!<>=' is not defined for array comparisons
-fail_compilation/ice11755.d(21): Error: use '==' for non-floating comparisons rather than floating point operator '!<>'
-fail_compilation/ice11755.d(22): Error: use '!=' for non-floating comparisons rather than floating point operator '<>'
-fail_compilation/ice11755.d(23): Error: '<>=' is not defined for array comparisons
-fail_compilation/ice11755.d(24): Error: use '<=' for non-floating comparisons rather than floating point operator '!>'
-fail_compilation/ice11755.d(25): Error: use '<' for non-floating comparisons rather than floating point operator '!>='
-fail_compilation/ice11755.d(26): Error: use '>=' for non-floating comparisons rather than floating point operator '!<'
-fail_compilation/ice11755.d(27): Error: use '>' for non-floating comparisons rather than floating point operator '!<='
-fail_compilation/ice11755.d(28): Error: floating point operator '<>=' always returns true for non-floating comparisons
-fail_compilation/ice11755.d(29): Error: floating point operator '!<>=' always returns false for non-floating comparisons
----
-*/
-void main()
-{
- int[] a, b;
- auto r4 = a !<>= b; // TOKunord
- auto r2 = a !<> b; // TOKue
- auto r1 = a <> b; // TOKlg
- auto r3 = a <>= b; // TOKleg
- auto r8 = a !> b; // TOKule
- auto r7 = a !>= b; // TOKul
- auto r6 = a !< b; // TOKuge
- auto r5 = a !<= b; // TOKug
- assert((5 <>= 3) == 1);
- assert((5 !<>= 3) == 0);
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11790.d b/gcc/testsuite/gdc.test/fail_compilation/ice11790.d
deleted file mode 100644
index 34b2002d950..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11790.d
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/ice11790.d(8): Error: cannot pass type string as a function argument
----
-*/
-
-string[string] crash = new string[string];
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11793.d b/gcc/testsuite/gdc.test/fail_compilation/ice11793.d
index e9453a30b44..2d997caac4d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11793.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11793.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice11793.d(11): Error: circular reference to 'ice11793.Outer.outer'
+fail_compilation/ice11793.d(11): Error: circular reference to `ice11793.Outer.outer`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11822.d b/gcc/testsuite/gdc.test/fail_compilation/ice11822.d
index 4ec46c3a2f5..830af212cb7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11822.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11822.d
@@ -4,9 +4,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice11822.d(32): Deprecation: function ice11822.d is deprecated
-fail_compilation/ice11822.d(21): instantiated from here: S!(__lambda1)
-fail_compilation/ice11822.d(32): instantiated from here: g!((n) => d(i))
+fail_compilation/ice11822.d(33): Deprecation: function `ice11822.d` is deprecated
+fail_compilation/ice11822.d(16): instantiated from here: `__lambda2!int`
+fail_compilation/ice11822.d(22): instantiated from here: `S!(__lambda2)`
+fail_compilation/ice11822.d(33): instantiated from here: `g!((n) => d(i))`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11849b.d b/gcc/testsuite/gdc.test/fail_compilation/ice11849b.d
index ef9380f7689..f879496a971 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11849b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11849b.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/ice11849b.d(11): Error: circular reference to enum base type `DWORD1`
-fail_compilation/ice11849b.d(11): Error: DWORD1 is used as a type
+fail_compilation/ice11849b.d(11): Error: `DWORD1` is used as a type
fail_compilation/ice11849b.d(16): Error: circular reference to enum base type `typeof(DWORD2)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11850.d b/gcc/testsuite/gdc.test/fail_compilation/ice11850.d
index 9d1f172a50e..d33549a27f2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11850.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11850.d
@@ -1,9 +1,10 @@
/*
+EXTRA_FILES: imports/a11850.d
TEST_OUTPUT:
---
-fail_compilation/ice11850.d(14): Error: incompatible types for ((a) < ([0])): 'uint[]' and 'int[]'
-fail_compilation/imports/a11850.d(9): instantiated from here: FilterResult!(__lambda1, uint[][])
-fail_compilation/ice11850.d(14): instantiated from here: filter!(uint[][])
+fail_compilation/ice11850.d(15): Error: incompatible types for `(a) < ([0])`: `uint[]` and `int[]`
+fail_compilation/imports/a11850.d(9): instantiated from here: `FilterResult!(__lambda1, uint[][])`
+fail_compilation/ice11850.d(15): instantiated from here: `filter!(uint[][])`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11856_0.d b/gcc/testsuite/gdc.test/fail_compilation/ice11856_0.d
new file mode 100644
index 00000000000..36bc2bd0508
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11856_0.d
@@ -0,0 +1,19 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice11856_0.d(19): Error: template `ice11856_0.f` cannot deduce function from argument types `!()(int)`
+fail_compilation/ice11856_0.d(13): Candidates are: `f(T)(T t)`
+fail_compilation/ice11856_0.d(16): `f(T)(T t)`
+ with `T = int`
+ must satisfy the following constraint:
+` !__traits(compiles, .f!T)`
+---
+*/
+
+int f(T)(T t) if(!__traits(compiles,.f!T)) {
+ return 0;
+}
+int f(T)(T t) if(!__traits(compiles,.f!T)) {
+ return 1;
+}
+enum x=f(2);
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11856_1.d b/gcc/testsuite/gdc.test/fail_compilation/ice11856_1.d
new file mode 100644
index 00000000000..70991ae4f00
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11856_1.d
@@ -0,0 +1,13 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice11856_1.d(13): Error: template `ice11856_1.g` cannot deduce function from argument types `!()(A)`
+fail_compilation/ice11856_1.d(11): Candidate is: `g(T)(T x)`
+---
+*/
+struct A {}
+
+void f(T)(T x) if (is(typeof(x.g()))) {}
+void g(T)(T x) if (is(typeof(x.f()))) {}
+
+void main() { A().g(); }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11919.d b/gcc/testsuite/gdc.test/fail_compilation/ice11919.d
index 700561043d4..e6b4f708846 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11919.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11919.d
@@ -1,10 +1,11 @@
/*
+EXTRA_FILES: imports/a11919.d
TEST_OUTPUT:
---
-fail_compilation/ice11919.d(17): Error: cannot interpret foo at compile time
-fail_compilation/imports/a11919.d(4): Error: template instance a11919.doBar!(Foo).doBar.zoo!(t) error instantiating
-fail_compilation/imports/a11919.d(11): instantiated from here: doBar!(Foo)
-fail_compilation/ice11919.d(25): instantiated from here: doBar!(Bar)
+fail_compilation/ice11919.d(18): Error: initializer must be an expression, not `foo`
+fail_compilation/imports/a11919.d(4): Error: template instance `a11919.doBar!(Foo).doBar.zoo!(t)` error instantiating
+fail_compilation/imports/a11919.d(11): instantiated from here: `doBar!(Foo)`
+fail_compilation/ice11919.d(26): instantiated from here: `doBar!(Bar)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11922.d b/gcc/testsuite/gdc.test/fail_compilation/ice11922.d
index 0999aeac6b5..bca0bbedeb0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11922.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11922.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/ice11922.d(11): Error: undefined identifier `a`
-fail_compilation/ice11922.d(17): Error: template instance ice11922.S.f!int error instantiating
+fail_compilation/ice11922.d(17): Error: template instance `ice11922.S.f!int` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11925.d b/gcc/testsuite/gdc.test/fail_compilation/ice11925.d
new file mode 100644
index 00000000000..2627f2b276c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11925.d
@@ -0,0 +1,45 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/ice11925.d(23): Error: cannot `goto` into `try` block
+fail_compilation/ice11925.d(31): Error: cannot `goto` into `try` block
+---
+*/
+
+void test11925a()
+{
+ try
+ {
+ try
+ {
+ L1: {}
+ }
+ finally
+ {
+ }
+ }
+ finally
+ {
+ }
+ goto L1;
+}
+
+void test11925b()
+{
+ switch (1)
+ {
+ case 1:
+ goto L1;
+ break;
+
+ default:
+ break;
+ }
+
+ try
+ {
+ L1: { }
+ }
+ finally
+ {
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11944.d b/gcc/testsuite/gdc.test/fail_compilation/ice11944.d
index c72d9f0de5c..9b0326bf936 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice11944.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice11944.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice11944.d(12): Error: template instance doCommand!(func) does not match template declaration doCommand(f, T)(f, T arg)
+fail_compilation/ice11944.d(12): Error: template instance `doCommand!(func)` does not match template declaration `doCommand(f, T)(f, T arg)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12040.d b/gcc/testsuite/gdc.test/fail_compilation/ice12040.d
index 2307e9422aa..e43079a3316 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12040.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12040.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12040.d(8): Error: circular reference to 'ice12040.lol'
+fail_compilation/ice12040.d(8): Error: circular reference to `ice12040.lol`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12158.d b/gcc/testsuite/gdc.test/fail_compilation/ice12158.d
index 7b8d38d4cec..50a0b609b7a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12158.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12158.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12158.d(7): Error: module object import 'nonexisting' not found
+fail_compilation/ice12158.d(7): Error: module `object` import `nonexisting` not found
---
*/
import object : nonexisting;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12174.d b/gcc/testsuite/gdc.test/fail_compilation/ice12174.d
index 81fef2d7f57..019722a063e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12174.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12174.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12174.d(12): Error: no property 'sum' for type 'int[]'
-fail_compilation/ice12174.d(20): Error: CTFE failed because of previous errors in this
-fail_compilation/ice12174.d(13): called from here: filter([1, 2, 3])
+fail_compilation/ice12174.d(12): Error: no property `sum` for type `int[]`
+fail_compilation/ice12174.d(20): Error: CTFE failed because of previous errors in `this`
+fail_compilation/ice12174.d(13): called from here: `filter([1, 2, 3])`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12235.d b/gcc/testsuite/gdc.test/fail_compilation/ice12235.d
index 1bf55b90ac3..8f2fefd099e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12235.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12235.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12235.d(14): Error: forward reference to inferred return type of function '__lambda1'
-fail_compilation/ice12235.d(15): Error: forward reference to inferred return type of function '__lambda1'
-fail_compilation/ice12235.d(15): while evaluating pragma(msg, __lambda1.mangleof)
+fail_compilation/ice12235.d(14): Error: forward reference to inferred return type of function `__lambda1`
+fail_compilation/ice12235.d(15): Error: forward reference to inferred return type of function `__lambda1`
+fail_compilation/ice12235.d(15): while evaluating `pragma(msg, __lambda1.mangleof)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12350.d b/gcc/testsuite/gdc.test/fail_compilation/ice12350.d
index 3ac3751d2c2..73ed39dd4b9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12350.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12350.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12350.d(15): Error: type MyUDC has no value
-fail_compilation/ice12350.d(30): Error: template instance ice12350.testAttrs!(MyStruct) error instantiating
+fail_compilation/ice12350.d(15): Error: type `MyUDC` has no value
+fail_compilation/ice12350.d(30): Error: template instance `ice12350.testAttrs!(MyStruct)` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12362.d b/gcc/testsuite/gdc.test/fail_compilation/ice12362.d
index 2c662c050d2..d0853b9b20d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12362.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12362.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12362.d(12): Error: cannot interpret foo at compile time
+fail_compilation/ice12362.d(12): Error: initializer must be an expression, not `foo`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12534.d b/gcc/testsuite/gdc.test/fail_compilation/ice12534.d
index 6820f394a75..da9c021f138 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12534.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12534.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12534.d(14): Error: static assert `is(exprs[0 .. 0])` is false
+fail_compilation/ice12534.d(14): Error: static assert: `is(exprs[0 .. 0])` is false
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12539.d b/gcc/testsuite/gdc.test/fail_compilation/ice12539.d
index e1ad9941b1d..8fab042266c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12539.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12539.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12539.d(15): Error: array index [0] is outside array bounds [0 .. 0]
+fail_compilation/ice12539.d(15): Error: array index `[0]` is outside array bounds `[0 .. 0]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12574.d b/gcc/testsuite/gdc.test/fail_compilation/ice12574.d
index 362c359de59..420b6b70078 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12574.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12574.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12574.d(40): Error: tuple index 2 exceeds length 2
-fail_compilation/ice12574.d(53): Error: template instance ice12574.reduce!("a", "a").reduce!(Tuple!(int, int, int)) error instantiating
+fail_compilation/ice12574.d(40): Error: tuple index `2` exceeds length 2
+fail_compilation/ice12574.d(53): Error: template instance `ice12574.reduce!("a", "a").reduce!(Tuple!(int, int, int))` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12727.d b/gcc/testsuite/gdc.test/fail_compilation/ice12727.d
index 027894c51e9..bf6af7b404f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12727.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12727.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
----
-fail_compilation/ice12727.d(16): Error: alias ice12727.IndexTuple!(1, 0).IndexTuple recursive alias declaration
-fail_compilation/ice12727.d(23): Error: template instance ice12727.IndexTuple!(1, 0) error instantiating
-fail_compilation/ice12727.d(27): instantiated from here: Matrix!(float, 3)
-fail_compilation/ice12727.d(28): instantiated from here: Vector!(float, 3)
+fail_compilation/ice12727.d(16): Error: alias `ice12727.IndexTuple!(1, 0).IndexTuple` recursive alias declaration
+fail_compilation/ice12727.d(23): Error: template instance `ice12727.IndexTuple!(1, 0)` error instantiating
+fail_compilation/ice12727.d(27): instantiated from here: `Matrix!(float, 3)`
+fail_compilation/ice12727.d(28): instantiated from here: `Vector!(float, 3)`
----
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12827.d b/gcc/testsuite/gdc.test/fail_compilation/ice12827.d
index 8270c1fe85b..20974e4a184 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12827.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12827.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12827.d(10): Error: circular initialization of variable 'ice12827.Test.i'
+fail_compilation/ice12827.d(10): Error: circular initialization of variable `ice12827.Test.i`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12841.d b/gcc/testsuite/gdc.test/fail_compilation/ice12841.d
index 564b661babc..c5894d2c7ee 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12841.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12841.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12841.d(23): Error: taskPool().amap(Args...)(Args args) is not an lvalue
-fail_compilation/ice12841.d(24): Error: amap(Args...)(Args args) is not an lvalue
+fail_compilation/ice12841.d(23): Error: `taskPool().amap(Args...)(Args args)` is not an lvalue and cannot be modified
+fail_compilation/ice12841.d(24): Error: `amap(Args...)(Args args)` is not an lvalue and cannot be modified
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12902.d b/gcc/testsuite/gdc.test/fail_compilation/ice12902.d
index e5ada0947de..ac430120d49 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice12902.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice12902.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice12902.d(20): Error: variable ice12902.main.__dollar type void is inferred from initializer s.opDollar(), and variables cannot be of type void
-fail_compilation/ice12902.d(20): Error: expression s.opDollar() is void and has no value
+fail_compilation/ice12902.d(20): Error: variable `ice12902.main.__dollar` type `void` is inferred from initializer `s.opDollar()`, and variables cannot be of type `void`
+fail_compilation/ice12902.d(20): Error: expression `s.opDollar()` is `void` and has no value
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13024.d b/gcc/testsuite/gdc.test/fail_compilation/ice13024.d
deleted file mode 100644
index bf826775b90..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13024.d
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/ice13024.d(15): Error: cannot implicitly convert expression `t.x` of type `A` to `B`
----
-*/
-
-enum A { a }
-enum B { b }
-struct T { A x; B y; }
-void main()
-{
- T t;
- auto r1 = [cast(int)(t.x), cast(int)(t.y)]; // OK
- auto r3 = [t.x, t.y]; // crash
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13027.d b/gcc/testsuite/gdc.test/fail_compilation/ice13027.d
index 04ccdf3e4f7..03c6820cc20 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13027.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13027.d
@@ -1,10 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13027.d(9): Error: template instance b!"c" template 'b' is not defined
+fail_compilation/ice13027.d(9): Error: template instance `b!"c"` template `b` is not defined
---
*/
void main()
{
scope a = b!"c";
}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13081.d b/gcc/testsuite/gdc.test/fail_compilation/ice13081.d
index 126ce9fe5e7..64474bae1a4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13081.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13081.d
@@ -4,7 +4,7 @@ TEST_OUTPUT:
fail_compilation/ice13081.d(17): Error: undefined identifier `node`
fail_compilation/ice13081.d(17): Error: undefined identifier `data`
fail_compilation/ice13081.d(17): Error: undefined identifier `node`
-fail_compilation/ice13081.d(28): Error: template instance ice13081.Cube!(SparseDataStore) error instantiating
+fail_compilation/ice13081.d(28): Error: template instance `ice13081.Cube!(SparseDataStore)` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13131.d b/gcc/testsuite/gdc.test/fail_compilation/ice13131.d
index 13b5aff9d5b..d4a6b91c0c2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13131.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13131.d
@@ -1,10 +1,11 @@
// EXTRA_SOURCES: imports/a13131parameters.d imports/a13131elec.d
+// EXTRA_FILES: imports/a13131checkpoint.d
/*
TEST_OUTPUT:
---
+A
+B
-fail_compilation/imports/a13131elec.d(10): Error: template instance elecConnOf!gconn template 'elecConnOf' is not defined
+fail_compilation/imports/a13131elec.d(10): Error: template instance `elecConnOf!gconn` template `elecConnOf` is not defined
-B
-A
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13220.d b/gcc/testsuite/gdc.test/fail_compilation/ice13220.d
index 3affd54858f..65b661be14f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13220.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13220.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13220.d(22): Error: template instance test!0 does not match template declaration test(T)()
+fail_compilation/ice13220.d(22): Error: template instance `test!0` does not match template declaration `test(T)()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13221.d b/gcc/testsuite/gdc.test/fail_compilation/ice13221.d
index b00169e4098..3c550541779 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13221.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13221.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13221.d(20): Error: variable r cannot be read at compile time
+fail_compilation/ice13221.d(20): Error: variable `r` cannot be read at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13225.d b/gcc/testsuite/gdc.test/fail_compilation/ice13225.d
index b042431ed70..6988cd7c18d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13225.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13225.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13225.d(12): Error: mixin ice13225.S.M!(function (S _param_0) => 0) does not match template declaration M(T)
+fail_compilation/ice13225.d(12): Error: mixin `ice13225.S.M!(function (S _param_0) pure nothrow @nogc @safe => 0)` does not match template declaration `M(T)`
fail_compilation/ice13225.d(16): Error: undefined identifier `undefined`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13311.d b/gcc/testsuite/gdc.test/fail_compilation/ice13311.d
index b86681b21f5..2aa01adcd49 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13311.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13311.d
@@ -1,4 +1,5 @@
/*
+EXTRA_FILES: imports/a13311.d
TEST_OUTPUT:
---
fail_compilation/imports/a13311.d(8): Error: undefined identifier `PieceTree`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13356.d b/gcc/testsuite/gdc.test/fail_compilation/ice13356.d
index 9225d55147f..3bac9b5f1b9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13356.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13356.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13356.d(32): Error: template instance Algebraic!(Tuple!(List)) recursive template expansion
-fail_compilation/ice13356.d(15): Error: template instance ice13356.isPrintable!(List) error instantiating
-fail_compilation/ice13356.d(33): instantiated from here: Tuple!(List)
+fail_compilation/ice13356.d(32): Error: template instance `Algebraic!(Tuple!(List))` recursive template expansion
+fail_compilation/ice13356.d(15): Error: template instance `ice13356.isPrintable!(List)` error instantiating
+fail_compilation/ice13356.d(33): instantiated from here: `Tuple!(List)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13382.d b/gcc/testsuite/gdc.test/fail_compilation/ice13382.d
index e5f1ac0f3f9..87c70d9afdc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13382.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13382.d
@@ -1,14 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13382.d(18): Error: incompatible types for ((a) == (0)): 'int[]' and 'int'
-fail_compilation/ice13382.d(19): Error: incompatible types for ((a) >= (0)): 'int[]' and 'int'
-fail_compilation/ice13382.d(20): Error: incompatible types for ((0) == (a)): 'int' and 'int[]'
-fail_compilation/ice13382.d(21): Error: incompatible types for ((0) >= (a)): 'int' and 'int[]'
-fail_compilation/ice13382.d(22): Error: incompatible types for ((a) is (0)): 'int[]' and 'int'
-fail_compilation/ice13382.d(23): Error: incompatible types for ((a) !is (0)): 'int[]' and 'int'
-fail_compilation/ice13382.d(24): Error: incompatible types for ((0) is (a)): 'int' and 'int[]'
-fail_compilation/ice13382.d(25): Error: incompatible types for ((0) !is (a)): 'int' and 'int[]'
+fail_compilation/ice13382.d(18): Error: incompatible types for `(a) == (0)`: `int[]` and `int`
+fail_compilation/ice13382.d(19): Error: incompatible types for `(a) >= (0)`: `int[]` and `int`
+fail_compilation/ice13382.d(20): Error: incompatible types for `(0) == (a)`: `int` and `int[]`
+fail_compilation/ice13382.d(21): Error: incompatible types for `(0) >= (a)`: `int` and `int[]`
+fail_compilation/ice13382.d(22): Error: incompatible types for `(a) is (0)`: `int[]` and `int`
+fail_compilation/ice13382.d(23): Error: incompatible types for `(a) !is (0)`: `int[]` and `int`
+fail_compilation/ice13382.d(24): Error: incompatible types for `(0) is (a)`: `int` and `int[]`
+fail_compilation/ice13382.d(25): Error: incompatible types for `(0) !is (a)`: `int` and `int[]`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13385.d b/gcc/testsuite/gdc.test/fail_compilation/ice13385.d
index b2039b279b8..cf114d05951 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13385.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13385.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13385.d(9): Error: protection attribute 'package(a)' does not bind to one of ancestor packages of module `ice13385`
+fail_compilation/ice13385.d(9): Error: visibility attribute `package(a)` does not bind to one of ancestor packages of module `ice13385`
---
*/
module ice13385;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13459.d b/gcc/testsuite/gdc.test/fail_compilation/ice13459.d
index 35420fdad17..2c42dd32fdc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13459.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13459.d
@@ -2,13 +2,13 @@
TEST_OUTPUT:
---
fail_compilation/ice13459.d(12): Error: undefined identifier `B`
-fail_compilation/ice13459.d(18): Error: none of the overloads of 'opSlice' are callable using argument types (int, int), candidates are:
-fail_compilation/ice13459.d(11): ice13459.A.opSlice()
+fail_compilation/ice13459.d(18): Error: none of the overloads of `opSlice` are callable using argument types `(int, int)`
+fail_compilation/ice13459.d(11): Candidates are: `ice13459.A.opSlice()`
---
*/
struct A
{
- auto opSlice() {}
+ auto opSlice() const {}
auto opSlice() { return B; }
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13465a.d b/gcc/testsuite/gdc.test/fail_compilation/ice13465a.d
index 23f73b78ce5..16c43a672ec 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13465a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13465a.d
@@ -3,8 +3,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/imports/a13465.d(10): Error: cannot infer type from template instance isMaskField!()
-fail_compilation/ice13465a.d(17): Error: template instance imports.a13465.isMatchingMaskField!() error instantiating
+fail_compilation/imports/a13465.d(10): Error: cannot infer type from template instance `isMaskField!()`
+fail_compilation/ice13465a.d(17): Error: template instance `imports.a13465.isMatchingMaskField!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13465b.d b/gcc/testsuite/gdc.test/fail_compilation/ice13465b.d
index db0cce1a56a..60c85055c60 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13465b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13465b.d
@@ -3,8 +3,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/imports/b13465.d(10): Error: cannot infer type from template instance isMaskField!()
-fail_compilation/ice13465b.d(17): Error: template instance imports.b13465.isMatchingMaskField!() error instantiating
+fail_compilation/imports/b13465.d(10): Error: cannot infer type from template instance `isMaskField!()`
+fail_compilation/ice13465b.d(17): Error: template instance `imports.b13465.isMatchingMaskField!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice1358.d b/gcc/testsuite/gdc.test/fail_compilation/ice1358.d
index ee71f8cb345..2c334f95ca2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice1358.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice1358.d
@@ -5,8 +5,8 @@ fail_compilation/ice1358.d(29): Error: invalid UTF character \U80000000
---
*/
-// Issue 1358 - ICE(root.c) on Unicode codepoints greater than 0x7FFFFFFF
-
+// https://issues.dlang.org/show_bug.cgi?id=1358
+// ICE(root.c) on Unicode codepoints greater than 0x7FFFFFFF
/* 1358. Assertion failure: '0' on line 1548 in file '..\root\root.c'
This one is trivial.
PATCH(lexer.c, Lexer::escapeSequence()).
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13788.d b/gcc/testsuite/gdc.test/fail_compilation/ice13788.d
index 7d04d9db705..99f3c4a6c35 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13788.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13788.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13788.d(11): Error: pragma mangle string expected for mangled name
-fail_compilation/ice13788.d(12): Error: string expected for mangled name, not (1) of type int
-fail_compilation/ice13788.d(13): Error: pragma mangle zero-length string not allowed for mangled name
-fail_compilation/ice13788.d(14): Error: pragma mangle mangled name characters can only be of type char
+fail_compilation/ice13788.d(11): Error: pragma `mangle` string expected for mangled name
+fail_compilation/ice13788.d(12): Error: `string` expected for mangled name, not `(1)` of type `int`
+fail_compilation/ice13788.d(13): Error: pragma `mangle` zero-length string not allowed for mangled name
+fail_compilation/ice13788.d(14): Error: pragma `mangle` mangled name characters can only be of type `char`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13816.d b/gcc/testsuite/gdc.test/fail_compilation/ice13816.d
index 6745d7b68e7..e683e339e61 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13816.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13816.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13816.d(15): Error: alias ice13816.ItemProperty!().ItemProperty recursive alias declaration
-fail_compilation/ice13816.d(20): Error: template instance ice13816.ItemProperty!() error instantiating
+fail_compilation/ice13816.d(15): Error: alias `ice13816.ItemProperty!().ItemProperty` recursive alias declaration
+fail_compilation/ice13816.d(20): Error: template instance `ice13816.ItemProperty!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13835.d b/gcc/testsuite/gdc.test/fail_compilation/ice13835.d
index 6bacdb04e48..dcb17579d9c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13835.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13835.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13835.d(15): Error: value of 'this' is not known at compile time
-fail_compilation/ice13835.d(21): Error: template instance ice13835.Foo!int error instantiating
+fail_compilation/ice13835.d(15): Error: value of `this` is not known at compile time
+fail_compilation/ice13835.d(21): Error: template instance `ice13835.Foo!int` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13921.d b/gcc/testsuite/gdc.test/fail_compilation/ice13921.d
index 8793e5cb10b..96c11ededaf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13921.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13921.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/ice13921.d(13): Error: undefined identifier `undefined_identifier`
-fail_compilation/ice13921.d(25): Error: template instance ice13921.S!string error instantiating
+fail_compilation/ice13921.d(25): Error: template instance `ice13921.S!string` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13987.d b/gcc/testsuite/gdc.test/fail_compilation/ice13987.d
index 26d3d300a89..28a57c088bd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice13987.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice13987.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice13987.d(9): Error: cannot use array to initialize S
+fail_compilation/ice13987.d(9): Error: cannot use array to initialize `S`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14055.d b/gcc/testsuite/gdc.test/fail_compilation/ice14055.d
index 5e60e88cdf8..13cdf34473f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14055.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14055.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice14055.d(16): Error: uninitialized variable 'foo' cannot be returned from CTFE
+fail_compilation/ice14055.d(16): Error: uninitialized variable `foo` cannot be returned from CTFE
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14096.d b/gcc/testsuite/gdc.test/fail_compilation/ice14096.d
index 654cfcc7989..05171adcb46 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14096.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14096.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice14096.d(29): Error: cannot access frame pointer of ice14096.main.Baz!((i) => i).Baz
-fail_compilation/ice14096.d(23): Error: template instance ice14096.foo!(Tuple!(Baz!((i) => i))).foo.bar!(t) error instantiating
-fail_compilation/ice14096.d(40): instantiated from here: foo!(Tuple!(Baz!((i) => i)))
+fail_compilation/ice14096.d(29): Error: cannot access frame pointer of `ice14096.main.Baz!((i) => i).Baz`
+fail_compilation/ice14096.d(23): Error: template instance `ice14096.foo!(Tuple!(Baz!((i) => i))).foo.bar!(t)` error instantiating
+fail_compilation/ice14096.d(40): instantiated from here: `foo!(Tuple!(Baz!((i) => i)))`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14116.d b/gcc/testsuite/gdc.test/fail_compilation/ice14116.d
index 52cd8ce5a10..c3339c7aca7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14116.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14116.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/imports/a14116.d(3): Error: module ice14116.ice14116 from file fail_compilation/ice14116.d must be imported with 'import ice14116.ice14116;'
+fail_compilation/imports/a14116.d(3): Error: module `ice14116.ice14116` from file fail_compilation/ice14116.d must be imported with 'import ice14116.ice14116;'
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14130.d b/gcc/testsuite/gdc.test/fail_compilation/ice14130.d
index 916a7b97a09..53e35a61a44 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14130.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14130.d
@@ -2,8 +2,8 @@
TEST_OUTPUT:
---
fail_compilation/ice14130.d(10): Error: undefined identifier `Undef`
-fail_compilation/ice14130.d(14): Error: template ice14130.foo cannot deduce function from argument types !()(int), candidates are:
-fail_compilation/ice14130.d(10): ice14130.foo(R, F = Undef)(R r, F s = 0)
+fail_compilation/ice14130.d(14): Error: template `ice14130.foo` cannot deduce function from argument types `!()(int)`
+fail_compilation/ice14130.d(10): Candidate is: `foo(R, F = Undef)(R r, F s = 0)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14146.d b/gcc/testsuite/gdc.test/fail_compilation/ice14146.d
index 1e4f9a976e5..f058d1cc591 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14146.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14146.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice14146.d(15): Error: constructor ice14146.Array.this default constructor for structs only allowed with @disable, no body, and no parameters
+fail_compilation/ice14146.d(15): Error: constructor `ice14146.Array.this` default constructor for structs only allowed with `@disable`, no body, and no parameters
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14177.d b/gcc/testsuite/gdc.test/fail_compilation/ice14177.d
index b487c2b347e..c7968158fa7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14177.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14177.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
----
-fail_compilation/ice14177.d(8): Error: alias ice14177.Primitive recursive alias declaration
+fail_compilation/ice14177.d(8): Error: alias `ice14177.Primitive` recursive alias declaration
----
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14272.d b/gcc/testsuite/gdc.test/fail_compilation/ice14272.d
index d9f642dd3cc..ee102fc18d5 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14272.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14272.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice14272.d(11): Error: circular initialization of variable 'ice14272.A14272!1.A14272.tag'
-fail_compilation/ice14272.d(14): Error: template instance ice14272.A14272!1 error instantiating
+fail_compilation/ice14272.d(11): Error: circular initialization of variable `ice14272.A14272!1.A14272.tag`
+fail_compilation/ice14272.d(14): Error: template instance `ice14272.A14272!1` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14424.d b/gcc/testsuite/gdc.test/fail_compilation/ice14424.d
index 29fe6661201..c99522dceb6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14424.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14424.d
@@ -1,8 +1,9 @@
// REQUIRED_ARGS: -o- -unittest
+// EXTRA_FILES: imports/a14424.d
/*
TEST_OUTPUT:
---
-fail_compilation/ice14424.d(12): Error: `tuple` has no effect in expression `tuple(__unittestL3_$n$)`
+fail_compilation/ice14424.d(13): Error: `tuple(__unittest_L3_C1)` has no effect
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14446.d b/gcc/testsuite/gdc.test/fail_compilation/ice14446.d
deleted file mode 100644
index ee0bfc450b2..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14446.d
+++ /dev/null
@@ -1,14 +0,0 @@
-// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
-// EXTRA_SOURCES: extra-files/a14446.d
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/extra-files/a14446.d(5): Error: module x14446 from file fail_compilation/ice14446.d must be imported with 'import x14446;'
----
-*/
-
-module x14446;
-
-struct CDB {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14621.d b/gcc/testsuite/gdc.test/fail_compilation/ice14621.d
index 15690b743d6..b3d26b8d7fc 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14621.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14621.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice14621.d(22): Error: static assert `false` is false
-fail_compilation/ice14621.d(28): instantiated from here: erroneousTemplateInstantiation!()
+fail_compilation/ice14621.d(22): Error: static assert: `false` is false
+fail_compilation/ice14621.d(28): instantiated from here: `erroneousTemplateInstantiation!()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14642.d b/gcc/testsuite/gdc.test/fail_compilation/ice14642.d
index 07d496311fd..90b9867527b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14642.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14642.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/ice14642.d(47): Error: undefined identifier `errorValue`
-fail_compilation/ice14642.d(23): Error: template instance ice14642.X.NA!() error instantiating
+fail_compilation/ice14642.d(23): Error: template instance `ice14642.X.NA!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14844.d b/gcc/testsuite/gdc.test/fail_compilation/ice14844.d
index d49a1d1dc47..9f602a53156 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14844.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14844.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice14844.d(20): Error: template `opDispatch(string name)` has no members
+fail_compilation/ice14844.d(21): Error: In expression `__traits(allMembers, opDispatch)` template `opDispatch(string name)` has no members
+fail_compilation/ice14844.d(21): `opDispatch(string name)` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14907.d b/gcc/testsuite/gdc.test/fail_compilation/ice14907.d
index 6d9edf0c903..ac4ba68a858 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14907.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14907.d
@@ -1,13 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice14907.d(14): Error: struct ice14907.S(int v = S) recursive template expansion
+fail_compilation/ice14907.d(14): Error: struct `ice14907.S(int v = S)` recursive template expansion
fail_compilation/ice14907.d(19): while looking for match for `S!()`
-fail_compilation/ice14907.d(15): Error: template ice14907.f(int v = f)() recursive template expansion
+fail_compilation/ice14907.d(15): Error: template `ice14907.f(int v = f)()` recursive template expansion
fail_compilation/ice14907.d(20): while looking for match for `f!()`
-fail_compilation/ice14907.d(15): Error: template ice14907.f(int v = f)() recursive template expansion
-fail_compilation/ice14907.d(21): Error: template `ice14907.f` cannot deduce function from argument types `!()()`, candidates are:
-fail_compilation/ice14907.d(15): `ice14907.f(int v = f)()`
+fail_compilation/ice14907.d(15): Error: template `ice14907.f(int v = f)()` recursive template expansion
+fail_compilation/ice14907.d(21): Error: template `ice14907.f` cannot deduce function from argument types `!()()`
+fail_compilation/ice14907.d(15): Candidate is: `f(int v = f)()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14929.d b/gcc/testsuite/gdc.test/fail_compilation/ice14929.d
deleted file mode 100644
index 01edc0d5e2b..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14929.d
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/ice14929.d(45): Error: cast(Node)(*this.current).items[this.index] is not an lvalue
-fail_compilation/ice14929.d(88): Error: template instance ice14929.HashMap!(ulong, int).HashMap.opBinaryRight!"in" error instantiating
-fail_compilation/ice14929.d(92): instantiated from here: HashmapComponentStorage!int
-fail_compilation/ice14929.d(92): Error: template instance ice14929.isComponentStorage!(HashmapComponentStorage!int, int) error instantiating
-fail_compilation/ice14929.d(92): while evaluating: `static assert(isComponentStorage!(HashmapComponentStorage!int, int))`
----
-*/
-
-struct HashMap(K, V)
-{
- V* opBinaryRight(string op)(K key) const if (op == "in")
- {
- size_t index;
- foreach (ref node; buckets[index].range)
- {
- return &(node.value);
- }
- return null;
- }
-
- struct Node
- {
- K key;
- V value;
- }
-
- alias Bucket = UnrolledList!(Node);
- Bucket[] buckets;
-}
-
-struct UnrolledList(T)
-{
- Range range() const pure
- {
- return Range(_front);
- }
-
- static struct Range
- {
- ref T front() const @property
- {
- return cast(T) current.items[index];
- }
- void popFront() pure
- {
- }
- bool empty() const pure @property
- {
- return true;
- }
- const(Node)* current;
- size_t index;
- }
-
- Node* _front;
-
- static struct Node
- {
- ContainerStorageType!T[10] items;
- }
-}
-
-template ContainerStorageType(T)
-{
- alias ContainerStorageType = T;
-}
-
-template isComponentStorage(CS, C)
-{
- enum bool isComponentStorage = is(typeof(
- (inout int = 0)
- {
- CS cs = CS.init;
- ulong eid;
- cs.add(eid, c);
- }));
-}
-
-struct HashmapComponentStorage(ComponentType)
-{
- private HashMap!(ulong, ComponentType) components;
-
- void add(ulong eid, ComponentType component)
- {
- assert(eid !in components);
- }
-}
-
-static assert(isComponentStorage!(HashmapComponentStorage!int, int));
-
-void main()
-{
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15092.d b/gcc/testsuite/gdc.test/fail_compilation/ice15092.d
index 0064ae37d97..6824cc1f918 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice15092.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice15092.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice15092.d(13): Error: struct ice15092.A.S conflicts with struct ice15092.A.S at fail_compilation/ice15092.d(12)
-fail_compilation/ice15092.d(16): Error: class ice15092.A.C conflicts with class ice15092.A.C at fail_compilation/ice15092.d(15)
-fail_compilation/ice15092.d(19): Error: interface ice15092.A.I conflicts with interface ice15092.A.I at fail_compilation/ice15092.d(18)
+fail_compilation/ice15092.d(13): Error: struct `ice15092.A.S` conflicts with struct `ice15092.A.S` at fail_compilation/ice15092.d(12)
+fail_compilation/ice15092.d(16): Error: class `ice15092.A.C` conflicts with class `ice15092.A.C` at fail_compilation/ice15092.d(15)
+fail_compilation/ice15092.d(19): Error: interface `ice15092.A.I` conflicts with interface `ice15092.A.I` at fail_compilation/ice15092.d(18)
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15172.d b/gcc/testsuite/gdc.test/fail_compilation/ice15172.d
index dc8ae9444fa..aa070f6db1b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice15172.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice15172.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice15172.d(14): Error: constructor ice15172.ThreadError.this no match for implicit super() call in constructor
+fail_compilation/ice15172.d(14): Error: constructor `ice15172.ThreadError.this` no match for implicit `super()` call in constructor
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15332.d b/gcc/testsuite/gdc.test/fail_compilation/ice15332.d
index 41411782bf4..dbedc73a6e4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice15332.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice15332.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice15332.d(16): Error: need 'this' for 'fun' of type 'int()'
-fail_compilation/ice15332.d(17): Error: need 'this' for 'var' of type 'int'
+fail_compilation/ice15332.d(16): Error: need `this` for `fun` of type `int()`
+fail_compilation/ice15332.d(17): Error: need `this` for `var` of type `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15441.d b/gcc/testsuite/gdc.test/fail_compilation/ice15441.d
index ef4369bac17..97fd1702dcb 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice15441.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice15441.d
@@ -1,10 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice15441.d(24): Error: variable ice15441.main.__front$n$ type void is inferred from initializer __r$n$.front(), and variables cannot be of type void
-fail_compilation/ice15441.d(24): Error: expression __r$n$.front() is void and has no value
-fail_compilation/ice15441.d(24): Error: `s1.front` is void and has no value
-fail_compilation/ice15441.d(27): Error: cannot infer argument types, expected 1 argument, not 2
+fail_compilation/ice15441.d(22): Error: `s1.front` is `void` and has no value
+fail_compilation/ice15441.d(25): Error: cannot infer argument types, expected 1 argument, not 2
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15688.d b/gcc/testsuite/gdc.test/fail_compilation/ice15688.d
index 92810e68888..e086e316fc1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice15688.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice15688.d
@@ -3,7 +3,7 @@
TEST_OUTPUT:
---
fail_compilation/ice15688.d(12): Error: undefined identifier `mappings`
-fail_compilation/ice15688.d(12): Error: function expected before (), not 0 of type int
+fail_compilation/ice15688.d(12): Error: function expected before `()`, not `0` of type `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15788.d b/gcc/testsuite/gdc.test/fail_compilation/ice15788.d
index e81f25bd8b4..941b179fb7b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice15788.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice15788.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: imports/range15788.d
TEST_OUTPUT:
---
-fail_compilation/ice15788.d(17): Error: none of the overloads of 'iota' are callable using argument types !()(S, S)
+fail_compilation/ice15788.d(18): Error: none of the overloads of `iota` are callable using argument types `!()(S, S)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15816.d b/gcc/testsuite/gdc.test/fail_compilation/ice15816.d
index b9e1e475b69..9576b564bb1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice15816.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice15816.d
@@ -1,4 +1,5 @@
/*
+EXTRA_FILES: imports/a15816.d
TEST_OUTPUT:
---
fail_compilation/imports/a15816.d(3): Error: anonymous classes not allowed
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15922.d b/gcc/testsuite/gdc.test/fail_compilation/ice15922.d
index c9739a178e1..d98404ca34a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice15922.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice15922.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice15922.d(23): Error: function ice15922.ValidSparseDataStore!int.ValidSparseDataStore.correctedInsert!false.correctedInsert has no return statement, but is expected to return a value of type int
-fail_compilation/ice15922.d(21): Error: template instance ice15922.ValidSparseDataStore!int.ValidSparseDataStore.correctedInsert!false error instantiating
-fail_compilation/ice15922.d(26): instantiated from here: ValidSparseDataStore!int
-fail_compilation/ice15922.d(14): Error: need 'this' for 'insert' of type 'pure @nogc @safe int()'
-fail_compilation/ice15922.d(26): Error: template instance ice15922.StorageAttributes!(ValidSparseDataStore!int) error instantiating
+fail_compilation/ice15922.d(23): Error: function `ice15922.ValidSparseDataStore!int.ValidSparseDataStore.correctedInsert!false.correctedInsert` has no `return` statement, but is expected to return a value of type `int`
+fail_compilation/ice15922.d(21): Error: template instance `ice15922.ValidSparseDataStore!int.ValidSparseDataStore.correctedInsert!false` error instantiating
+fail_compilation/ice15922.d(26): instantiated from here: `ValidSparseDataStore!int`
+fail_compilation/ice15922.d(14): Error: need `this` for `insert` of type `pure nothrow @nogc @safe int()`
+fail_compilation/ice15922.d(26): Error: template instance `ice15922.StorageAttributes!(ValidSparseDataStore!int)` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice16035.d b/gcc/testsuite/gdc.test/fail_compilation/ice16035.d
index 9ad1a4e8535..8361c42cf2e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice16035.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice16035.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice16035.d(18): Error: forward reference to inferred return type of function call 'this.a[0].toString()'
-fail_compilation/ice16035.d(13): Error: template instance ice16035.Value.get!string error instantiating
+fail_compilation/ice16035.d(18): Error: forward reference to inferred return type of function call `this.a[0].toString()`
+fail_compilation/ice16035.d(13): Error: template instance `ice16035.Value.get!string` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice16657.d b/gcc/testsuite/gdc.test/fail_compilation/ice16657.d
new file mode 100644
index 00000000000..19231c28e34
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice16657.d
@@ -0,0 +1,16 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice16657.d(9): Error: function `ice16657.RefCounted.refCountedPayload` has no `return` statement, but is expected to return a value of type `inout(RefCounted)`
+---
+*/
+struct RefCounted
+{
+ inout(RefCounted) refCountedPayload() inout { }
+ alias refCountedPayload this;
+}
+
+struct Store
+{
+ RefCounted p;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice17074.d b/gcc/testsuite/gdc.test/fail_compilation/ice17074.d
index b41b25abb0b..53e75e40934 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice17074.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice17074.d
@@ -2,38 +2,18 @@
TEST_OUTPUT:
---
fail_compilation/ice17074.d(9): Error: identifier expected for C++ namespace
-fail_compilation/ice17074.d(9): Error: found `cast` when expecting `)`
+fail_compilation/ice17074.d(9): Error: found `__overloadset` when expecting `)`
fail_compilation/ice17074.d(9): Error: declaration expected, not `)`
---
*/
-extern(C++, cast) void ice_keyword();
+extern(C++, std.__overloadset) void ice_std_keyword();
/*
TEST_OUTPUT:
---
fail_compilation/ice17074.d(19): Error: identifier expected for C++ namespace
-fail_compilation/ice17074.d(19): Error: found `__overloadset` when expecting `)`
+fail_compilation/ice17074.d(19): Error: found `*` when expecting `)`
fail_compilation/ice17074.d(19): Error: declaration expected, not `)`
---
*/
-extern(C++, std.__overloadset) void ice_std_keyword();
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/ice17074.d(29): Error: identifier expected for C++ namespace
-fail_compilation/ice17074.d(29): Error: found `...` when expecting `)`
-fail_compilation/ice17074.d(29): Error: declaration expected, not `)`
----
-*/
-extern(C++, ...) void ice_token();
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/ice17074.d(39): Error: identifier expected for C++ namespace
-fail_compilation/ice17074.d(39): Error: found `*` when expecting `)`
-fail_compilation/ice17074.d(39): Error: declaration expected, not `)`
----
-*/
extern(C++, std.*) void ice_std_token();
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice17831.d b/gcc/testsuite/gdc.test/fail_compilation/ice17831.d
index 15b8502172b..41abbf77a82 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice17831.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice17831.d
@@ -1,12 +1,16 @@
-// REQUIRED_ARGS: -d
/*
TEST_OUTPUT:
---
-fail_compilation/ice17831.d(19): Error: case variable `i` declared at fail_compilation/ice17831.d(17) cannot be declared in switch body
-fail_compilation/ice17831.d(33): Error: case variable `i` declared at fail_compilation/ice17831.d(31) cannot be declared in switch body
-fail_compilation/ice17831.d(48): Error: case variable `i` declared at fail_compilation/ice17831.d(45) cannot be declared in switch body
-fail_compilation/ice17831.d(61): Error: case variable `i` declared at fail_compilation/ice17831.d(60) cannot be declared in switch body
-fail_compilation/ice17831.d(73): Error: case variable `i` declared at fail_compilation/ice17831.d(72) cannot be declared in switch body
+fail_compilation/ice17831.d(23): Error: `case` variables have to be `const` or `immutable`
+fail_compilation/ice17831.d(23): Error: `case` variable `i` declared at fail_compilation/ice17831.d(21) cannot be declared in `switch` body
+fail_compilation/ice17831.d(37): Error: `case` variables have to be `const` or `immutable`
+fail_compilation/ice17831.d(37): Error: `case` variable `i` declared at fail_compilation/ice17831.d(35) cannot be declared in `switch` body
+fail_compilation/ice17831.d(52): Error: `case` variables have to be `const` or `immutable`
+fail_compilation/ice17831.d(52): Error: `case` variable `i` declared at fail_compilation/ice17831.d(49) cannot be declared in `switch` body
+fail_compilation/ice17831.d(65): Error: `case` variables have to be `const` or `immutable`
+fail_compilation/ice17831.d(65): Error: `case` variable `i` declared at fail_compilation/ice17831.d(64) cannot be declared in `switch` body
+fail_compilation/ice17831.d(77): Error: `case` variables have to be `const` or `immutable`
+fail_compilation/ice17831.d(77): Error: `case` variable `i` declared at fail_compilation/ice17831.d(76) cannot be declared in `switch` body
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice18469.d b/gcc/testsuite/gdc.test/fail_compilation/ice18469.d
new file mode 100644
index 00000000000..8803956f6c5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice18469.d
@@ -0,0 +1,13 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice18469.d(10): Error: no property `opCall` for type `void`
+---
+*/
+class Bar
+{
+ ~this(){}
+ this(){alias T = typeof(Bar.__dtor.opCall);}
+}
+
+void main() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice18753.d b/gcc/testsuite/gdc.test/fail_compilation/ice18753.d
new file mode 100644
index 00000000000..300eab27071
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice18753.d
@@ -0,0 +1,39 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice18753.d(21): Error: variable `ice18753.isInputRange!(Group).isInputRange` type `void` is inferred from initializer `ReturnType(func...)`, and variables cannot be of type `void`
+fail_compilation/ice18753.d(23): Error: template instance `ice18753.isInputRange!(Group)` error instantiating
+fail_compilation/ice18753.d(18): instantiated from here: `isForwardRange!(Group)`
+fail_compilation/ice18753.d(18): while evaluating: `static assert(isForwardRange!(Group))`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18753
+
+struct ChunkByImpl
+{
+ struct Group
+ { }
+
+ static assert(isForwardRange!Group);
+}
+
+enum isInputRange(R) = ReturnType;
+
+enum isForwardRange(R) = isInputRange!R is ReturnType!(() => r);
+
+template ReturnType(func...)
+{
+ static if (is(FunctionTypeOf!func R == return))
+ ReturnType R;
+}
+
+template FunctionTypeOf(func...)
+{
+ static if (is(typeof(func[0]) T))
+ static if (is(T Fptr ) )
+ alias FunctionTypeOf = Fptr;
+}
+
+template Select()
+{ }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice18803a.d b/gcc/testsuite/gdc.test/fail_compilation/ice18803a.d
new file mode 100644
index 00000000000..5ec5a816e33
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice18803a.d
@@ -0,0 +1,9 @@
+/*
+EXTRA_FILES: ice18803b.d
+TEST_OUTPUT:
+---
+fail_compilation/ice18803b.d(9): Error: (expression) expected following `static if`
+fail_compilation/ice18803b.d(9): Error: declaration expected following attribute, not end of file
+---
+*/
+void main() { import ice18803b; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice18803b.d b/gcc/testsuite/gdc.test/fail_compilation/ice18803b.d
new file mode 100644
index 00000000000..cfc38c5f368
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice18803b.d
@@ -0,0 +1,8 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice18803b.d(9): Error: (expression) expected following `static if`
+fail_compilation/ice18803b.d(9): Error: declaration expected following attribute, not end of file
+---
+*/
+static if
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice19295.d b/gcc/testsuite/gdc.test/fail_compilation/ice19295.d
new file mode 100644
index 00000000000..a92f5f8384c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice19295.d
@@ -0,0 +1,18 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice19295.d(11): Error: `this` for `gun` needs to be type `S2` not type `S1!(gun)`
+fail_compilation/ice19295.d(11): while evaluating `pragma(msg, &gun)`
+fail_compilation/ice19295.d(17): Error: template instance `ice19295.S1!(gun)` error instantiating
+---
+*/
+struct S1(T...) {
+ auto fun() {
+ pragma(msg, &T[0]);
+ }
+}
+
+struct S2 {
+ void gun() {}
+ S1!gun overloaded;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice19755.d b/gcc/testsuite/gdc.test/fail_compilation/ice19755.d
new file mode 100644
index 00000000000..f9484777831
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice19755.d
@@ -0,0 +1,16 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/ice19755.d(11): Error: no property `x` for type `ice19755.Thunk!int*`
+fail_compilation/ice19755.d(16): Error: template instance `ice19755.Thunk!int` error instantiating
+---
+*/
+struct Thunk(Dummy) {
+ void opAssign(int dlg) {}
+ auto get() inout {
+ Thunk* self;
+ self.x = 0;
+ }
+ alias get this;
+}
+
+alias T = Thunk!int;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice19762.d b/gcc/testsuite/gdc.test/fail_compilation/ice19762.d
new file mode 100644
index 00000000000..a484327426b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice19762.d
@@ -0,0 +1,17 @@
+// EXTRA_FILES: imports/b19762.d imports/c19762.d
+// PERMUTE_ARGS: -g
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice19762.d(13): Error: struct `ice19762.X` had semantic errors when compiling
+---
+*/
+
+module ice19762;
+
+struct X
+{
+ import imports.b19762 : Baz;
+ Err err;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice19887.d b/gcc/testsuite/gdc.test/fail_compilation/ice19887.d
new file mode 100644
index 00000000000..a323d0858bc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice19887.d
@@ -0,0 +1,14 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice19887.d(9): Error: initializer must be an expression, not `(void)`
+---
+*/
+module ice19887;
+
+void func(AliasSeq!(int) params = AliasSeq!(void)) {}
+
+template AliasSeq(TList...)
+{
+ alias AliasSeq = TList;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice19950.d b/gcc/testsuite/gdc.test/fail_compilation/ice19950.d
new file mode 100644
index 00000000000..dc930d5aa8e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice19950.d
@@ -0,0 +1,13 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice19950.d(8): Error: undefined identifier `NotHere`
+fail_compilation/ice19950.d(9): Error: template instance `ice19950.baz!()` does not match template declaration `baz()(Foo)`
+---
+*/
+alias Foo = NotHere;
+alias Bar = baz!();
+
+void baz()(Foo)
+ if (true)
+{}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice20042.d b/gcc/testsuite/gdc.test/fail_compilation/ice20042.d
new file mode 100644
index 00000000000..6b71903c9ca
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice20042.d
@@ -0,0 +1,29 @@
+/*
+DISABLED: freebsd32 linux32 osx32 win32
+TEST_OUTPUT:
+---
+fail_compilation/ice20042.d(18): Error: slice operation `cast(__vector(float[4]))[nanF, nanF, nanF, nanF] = [1.0F, 2.0F, 3.0F, 4.0F][0..4]` cannot be evaluated at compile time
+fail_compilation/ice20042.d(25): called from here: `Vec4(cast(__vector(float[4]))[nanF, nanF, nanF, nanF]).this([1.0F, 2.0F, 3.0F, 4.0F])`
+---
+*/
+void write(T...)(T t){}
+
+struct Vec4
+{
+ __vector(float[4]) raw;
+
+ this(const(float[4]) value...) inout pure @safe nothrow @nogc
+ {
+ __vector(float[4]) raw;
+ raw[] = value[];
+ this.raw = raw;
+ }
+}
+
+void main()
+{
+ static immutable Vec4 v = Vec4( 1.0f, 2.0f, 3.0f, 4.0f );
+
+ static foreach(d; 0 .. 4)
+ write(v.raw[d], " ");
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice20056.d b/gcc/testsuite/gdc.test/fail_compilation/ice20056.d
new file mode 100644
index 00000000000..1b991cae66d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice20056.d
@@ -0,0 +1,25 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice20056.d(19): Error: need `this` for `iter` of type `void()`
+---
+*/
+struct Def(alias fn)
+{
+ alias func = alias_selector!(fn).VOverloads[0];
+}
+
+template alias_selector(alias fn)
+{
+ alias VOverloads = __traits(getOverloads, __traits(parent, fn), __traits(identifier, fn));
+}
+
+void init_rangewrapper()
+{
+ Def!(RangeWrapper.iter).func;
+}
+
+struct RangeWrapper
+{
+ void iter() { }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice20057.d b/gcc/testsuite/gdc.test/fail_compilation/ice20057.d
new file mode 100644
index 00000000000..617e07cba0b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice20057.d
@@ -0,0 +1,17 @@
+/*
+EXTRA_FILES: imports/i20057.d
+TEST_OUTPUT:
+---
+fail_compilation/ice20057.d(10): Error: alias `ice20057.BlackHole` conflicts with struct `ice20057.BlackHole(alias T)` at fail_compilation/ice20057.d(9)
+---
+*/
+
+struct BlackHole(alias T){T t;}
+import imports.i20057: BlackHole;
+
+extern(C++) interface Inter
+{
+ void func();
+}
+
+BlackHole!Inter var;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice20264.d b/gcc/testsuite/gdc.test/fail_compilation/ice20264.d
new file mode 100644
index 00000000000..0d697e22c9f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice20264.d
@@ -0,0 +1,13 @@
+/*
+DISABLED: freebsd32 linux32 osx32 win32
+TEST_OUTPUT:
+---
+fail_compilation/ice20264.d(12): Error: `cast(__vector(float[4]))a` is not an lvalue and cannot be modified
+---
+*/
+
+void foo(float *a)
+{
+ alias float4 = __vector(float[4]);
+ cast(float4)(a) = 1.0f;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice20545.d b/gcc/testsuite/gdc.test/fail_compilation/ice20545.d
new file mode 100644
index 00000000000..26bd320bfbd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice20545.d
@@ -0,0 +1,8 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice20545.d(8): Error: initializer expression expected following colon, not `]`
+---
+*/
+
+static initial = [{ }: ];
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice20709.d b/gcc/testsuite/gdc.test/fail_compilation/ice20709.d
new file mode 100644
index 00000000000..cc36de94967
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice20709.d
@@ -0,0 +1,14 @@
+/*
+EXTRA_FILES: imports/imp20709.d
+TEST_OUTPUT:
+---
+fail_compilation/ice20709.d(10): Error: module `imp20709` import `Point` not found
+---
+*/
+module ice20709;
+
+import imports.imp20709 : Point;
+
+immutable Point aPoint = somePoint;
+
+Point somePoint() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice21095.d b/gcc/testsuite/gdc.test/fail_compilation/ice21095.d
new file mode 100644
index 00000000000..0d87d63bb01
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice21095.d
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=21095
+/*
+TEST_OUTPUT:
+---
+fail_compilation/ice21095.d(14): Error: constructor `ice21095.Mutex.__ctor!().this` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract
+fail_compilation/ice21095.d(12): Error: template instance `ice21095.Mutex.__ctor!()` error instantiating
+---
+*/
+class Mutex
+{
+ this(Object obj) {
+ this(obj, true);
+ }
+ this()(Object, bool) in { }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice2843.d b/gcc/testsuite/gdc.test/fail_compilation/ice2843.d
index c0c379767e1..cf53e5d5f5b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice2843.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice2843.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice2843.d(22): Error: incompatible types for ((1) is (typeid(int))): 'int' and 'object.TypeInfo'
+fail_compilation/ice2843.d(22): Error: incompatible types for `(1) is (typeid(int))`: `int` and `object.TypeInfo`
---
*/
-// Issue 2843 - ICE(constfold.c) with is-expression with invalid dot-expression in is-expression involving typeid expression
-
+// https://issues.dlang.org/show_bug.cgi?id=2843
+// ICE(constfold.c) with is-expression with invalid dot-expression in is-expression involving typeid expression
/* 2843 Assertion failure: '0' on line 863 in file 'constfold.c'
PATCH: constfold.c, line 861:
OLD:
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice4094.d b/gcc/testsuite/gdc.test/fail_compilation/ice4094.d
index cd41e651121..61108c4d7e0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice4094.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice4094.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice4094.d(11): Error: circular reference to variable 'ice4094.Zug!0.Zug.bahn'
-fail_compilation/ice4094.d(19): Error: template instance ice4094.Zug!0 error instantiating
+fail_compilation/ice4094.d(11): Error: circular reference to variable `ice4094.Zug!0.Zug.bahn`
+fail_compilation/ice4094.d(19): Error: template instance `ice4094.Zug!0` error instantiating
---
*/
// REQUIRED_ARGS: -d
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice4983.d b/gcc/testsuite/gdc.test/fail_compilation/ice4983.d
index 1925c174dd9..00fa95cf532 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice4983.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice4983.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice4983.d(14): Error: circular reference to 'ice4983.Foo.dg'
+fail_compilation/ice4983.d(14): Error: circular reference to `ice4983.Foo.dg`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice6538.d b/gcc/testsuite/gdc.test/fail_compilation/ice6538.d
index dad6491c5e9..0715db5b8a7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice6538.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice6538.d
@@ -1,14 +1,14 @@
/**************************************/
-// 9361
+// https://issues.dlang.org/show_bug.cgi?id=9361
/*
TEST_OUTPUT:
---
-fail_compilation/ice6538.d(23): Error: expression super is not a valid template value argument
-fail_compilation/ice6538.d(28): Error: template ice6538.D.foo cannot deduce function from argument types !()(), candidates are:
-fail_compilation/ice6538.d(23): ice6538.D.foo()() if (Sym!(super))
+fail_compilation/ice6538.d(23): Error: expression `super` is not a valid template value argument
+fail_compilation/ice6538.d(28): Error: template `ice6538.D.foo` cannot deduce function from argument types `!()()`
+fail_compilation/ice6538.d(23): Candidate is: `foo()()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice7645.d b/gcc/testsuite/gdc.test/fail_compilation/ice7645.d
index 1aa3fad67a0..379ac67d0c5 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice7645.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice7645.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice7645.d(28): Error: need 'this' for 't' of type 'char'
-fail_compilation/ice7645.d(31): Error: need 'this' for 'fn' of type 'pure nothrow @nogc @safe void()'
+fail_compilation/ice7645.d(28): Error: need `this` for `t` of type `char`
+fail_compilation/ice7645.d(31): Error: need `this` for `fn` of type `pure nothrow @nogc @safe void()`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8100.d b/gcc/testsuite/gdc.test/fail_compilation/ice8100.d
index 1e7d56ba94f..dc68cfcce2b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice8100.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice8100.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice8100.d(10): Error: no property 'Q' for type 'ice8100.Bar!bool'
-fail_compilation/ice8100.d(11): Error: template instance ice8100.Foo!(Bar!bool) error instantiating
-fail_compilation/ice8100.d(12): instantiated from here: Bar!bool
+fail_compilation/ice8100.d(10): Error: no property `Q` for type `ice8100.Bar!bool`
+fail_compilation/ice8100.d(11): Error: template instance `ice8100.Foo!(Bar!bool)` error instantiating
+fail_compilation/ice8100.d(12): instantiated from here: `Bar!bool`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8255.d b/gcc/testsuite/gdc.test/fail_compilation/ice8255.d
deleted file mode 100644
index 0db3abc395f..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/ice8255.d
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/ice8255.d(11): Error: function `ice8255.F!(G).F.f(ref G _param_0)` is not callable using argument types `(G)`
-fail_compilation/ice8255.d(11): cannot pass rvalue argument `G()` of type `G` to parameter `ref G _param_0`
-fail_compilation/ice8255.d(11): while evaluating `pragma(msg, F().f(G()))`
----
-*/
-struct G {}
-struct F(T) { void f(ref T) {} }
-pragma(msg, F!G().f(G.init));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8309.d b/gcc/testsuite/gdc.test/fail_compilation/ice8309.d
index 5d5bb83298d..2446914477a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice8309.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice8309.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice8309.d(10): Error: incompatible types for ((__lambda1) : (__lambda2)): 'double function() pure nothrow @nogc @safe' and 'int function() pure nothrow @nogc @safe'
+fail_compilation/ice8309.d(10): Error: incompatible types for `(__lambda1) : (__lambda2)`: `double function() pure nothrow @nogc @safe` and `int function() pure nothrow @nogc @safe`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8711.d b/gcc/testsuite/gdc.test/fail_compilation/ice8711.d
index c1439297f09..d6674c4a743 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice8711.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice8711.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice8711.d(8): Error: cannot use array to initialize int function(int)
+fail_compilation/ice8711.d(8): Error: cannot use array to initialize `int function(int)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8795.d b/gcc/testsuite/gdc.test/fail_compilation/ice8795.d
index 29eabbfff46..5d7d6dd17a7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice8795.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice8795.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice8795.d-mixin-14(14): Error: found `EOF` when expecting `(`
-fail_compilation/ice8795.d-mixin-14(14): Error: expression expected, not `EOF`
-fail_compilation/ice8795.d-mixin-14(14): Error: found `EOF` when expecting `)`
-fail_compilation/ice8795.d-mixin-14(14): Error: found `EOF` instead of statement
+fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` when expecting `(`
+fail_compilation/ice8795.d-mixin-14(14): Error: expression expected, not `End of File`
+fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` when expecting `)`
+fail_compilation/ice8795.d-mixin-14(14): Error: found `End of File` instead of statement
fail_compilation/ice8795.d-mixin-15(15): Error: { } expected following `interface` declaration
fail_compilation/ice8795.d-mixin-15(15): Error: anonymous interfaces not allowed
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9273a.d b/gcc/testsuite/gdc.test/fail_compilation/ice9273a.d
index 91f22d42f35..c2f44a344ad 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9273a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9273a.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice9273a.d(19): Error: constructor ice9273a.C.__ctor!().this no match for implicit super() call in constructor
-fail_compilation/ice9273a.d(23): Error: template instance ice9273a.C.__ctor!() error instantiating
+fail_compilation/ice9273a.d(19): Error: constructor `ice9273a.C.__ctor!().this` no match for implicit `super()` call in constructor
+fail_compilation/ice9273a.d(23): Error: template instance `ice9273a.C.__ctor!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9273b.d b/gcc/testsuite/gdc.test/fail_compilation/ice9273b.d
index 1a779facbbf..2d057246cbd 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9273b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9273b.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice9273b.d(14): Error: constructor ice9273b.B.this no match for implicit super() call in constructor
+fail_compilation/ice9273b.d(14): Error: constructor `ice9273b.B.this` no match for implicit `super()` call in constructor
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9284.d b/gcc/testsuite/gdc.test/fail_compilation/ice9284.d
index 0c95bd87257..a6d84aef241 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9284.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9284.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice9284.d(14): Error: template ice9284.C.__ctor cannot deduce function from argument types !()(int), candidates are:
-fail_compilation/ice9284.d(12): ice9284.C.__ctor()(string)
-fail_compilation/ice9284.d(20): Error: template instance ice9284.C.__ctor!() error instantiating
+fail_compilation/ice9284.d(14): Error: template `ice9284.C.__ctor` cannot deduce function from argument types `!()(int)`
+fail_compilation/ice9284.d(12): Candidate is: `__ctor()(string)`
+fail_compilation/ice9284.d(20): Error: template instance `ice9284.C.__ctor!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9338.d b/gcc/testsuite/gdc.test/fail_compilation/ice9338.d
index 292c05964fd..5f77817d7e3 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9338.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9338.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice9338.d(13): Error: value of 'this' is not known at compile time
-fail_compilation/ice9338.d(14): Error: value of 'this' is not known at compile time
+fail_compilation/ice9338.d(13): Error: value of `this` is not known at compile time
+fail_compilation/ice9338.d(14): Error: value of `this` is not known at compile time
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9439.d b/gcc/testsuite/gdc.test/fail_compilation/ice9439.d
index 51b84e82d71..a9e70083802 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9439.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9439.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice9439.d(12): Error: this for foo needs to be type Derived not type ice9439.Base
+fail_compilation/ice9439.d(12): Error: `this` for `foo` needs to be type `Derived` not type `ice9439.Base`
fail_compilation/ice9439.d(12): while evaluating: `static assert((__error).foo())`
-fail_compilation/ice9439.d(19): Error: template instance ice9439.Base.boo!(foo) error instantiating
+fail_compilation/ice9439.d(19): Error: template instance `ice9439.Base.boo!(foo)` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9494.d b/gcc/testsuite/gdc.test/fail_compilation/ice9494.d
index b2743e5a3d9..732b88a9926 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9494.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9494.d
@@ -1,9 +1,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice9494.d(10): Error: circular reference to variable 'ice9494.test'
-fail_compilation/ice9494.d(14): Error: circular reference to variable 'ice9494.Foo.test'
-fail_compilation/ice9494.d(19): Error: circular reference to variable 'ice9494.Bar.test'
+fail_compilation/ice9494.d(10): Error: circular reference to variable `ice9494.test`
+fail_compilation/ice9494.d(14): Error: circular reference to variable `ice9494.Foo.test`
+fail_compilation/ice9494.d(19): Error: circular reference to variable `ice9494.Bar.test`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9545.d b/gcc/testsuite/gdc.test/fail_compilation/ice9545.d
index 846975afd3b..7360bc534f2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9545.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9545.d
@@ -2,7 +2,7 @@
/*
TEST_OUTPUT:
----
-fail_compilation/ice9545.d(13): Error: type int has no value
+fail_compilation/ice9545.d(13): Error: type `int` has no value
----
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9759.d b/gcc/testsuite/gdc.test/fail_compilation/ice9759.d
index 300540cfeed..6608b58fc0b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9759.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9759.d
@@ -1,7 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/ice9759.d(24): Error: mutable method ice9759.Json.opAssign is not callable using a const object
+fail_compilation/ice9759.d(25): Error: mutable method `ice9759.Json.opAssign` is not callable using a `const` object
+fail_compilation/ice9759.d(17): Consider adding `const` or `inout` here
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9806.d b/gcc/testsuite/gdc.test/fail_compilation/ice9806.d
index 5f00fccf597..40e6273e11f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9806.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9806.d
@@ -2,11 +2,11 @@
TEST_OUTPUT:
---
fail_compilation/ice9806.d(12): Error: undefined identifier `undefined_expr`
-fail_compilation/ice9806.d(17): Error: template instance ice9806.S1!() error instantiating
+fail_compilation/ice9806.d(17): Error: template instance `ice9806.S1!()` error instantiating
fail_compilation/ice9806.d(13): Error: undefined identifier `undefined_expr`
-fail_compilation/ice9806.d(19): Error: template instance ice9806.C1!() error instantiating
+fail_compilation/ice9806.d(19): Error: template instance `ice9806.C1!()` error instantiating
fail_compilation/ice9806.d(14): Error: undefined identifier `undefined_expr`
-fail_compilation/ice9806.d(21): Error: template instance ice9806.I1!() error instantiating
+fail_compilation/ice9806.d(21): Error: template instance `ice9806.I1!()` error instantiating
---
*/
struct S1() { enum x = undefined_expr; }
@@ -26,11 +26,11 @@ void test1() {
TEST_OUTPUT:
---
fail_compilation/ice9806.d(36): Error: undefined identifier `undefined_expr`
-fail_compilation/ice9806.d(44): Error: template instance ice9806.S2!() error instantiating
+fail_compilation/ice9806.d(44): Error: template instance `ice9806.S2!()` error instantiating
fail_compilation/ice9806.d(37): Error: undefined identifier `undefined_expr`
-fail_compilation/ice9806.d(46): Error: template instance ice9806.C2!() error instantiating
+fail_compilation/ice9806.d(46): Error: template instance `ice9806.C2!()` error instantiating
fail_compilation/ice9806.d(38): Error: undefined identifier `undefined_expr`
-fail_compilation/ice9806.d(48): Error: template instance ice9806.I2!() error instantiating
+fail_compilation/ice9806.d(48): Error: template instance `ice9806.I2!()` error instantiating
---
*/
int foo2()() { return undefined_expr; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9865.d b/gcc/testsuite/gdc.test/fail_compilation/ice9865.d
index 846ba2e5914..3d8e997946f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9865.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9865.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: imports/ice9865b.d
TEST_OUTPUT:
---
-fail_compilation/ice9865.d(8): Error: alias ice9865.Baz recursive alias declaration
+fail_compilation/ice9865.d(9): Error: alias `ice9865.Baz` recursive alias declaration
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/impconv.d b/gcc/testsuite/gdc.test/fail_compilation/impconv.d
new file mode 100644
index 00000000000..cb1cb8e21ee
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/impconv.d
@@ -0,0 +1,40 @@
+/*
+FIXME: DMD host compilers < 2.073 with faulty optimization
+lead to unfortunate test failures, see
+https://github.com/dlang/dmd/pull/6831#issuecomment-304495842.
+
+DISABLED: win32 win64 linux osx freebsd
+*/
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/impconv.d(30): Error: function `impconv.foo_float(float)` is not callable using argument types `(int)`
+fail_compilation/impconv.d(30): cannot pass argument `-2147483647` of type `int` to parameter `float`
+fail_compilation/impconv.d(31): Error: function `impconv.foo_float(float)` is not callable using argument types `(uint)`
+fail_compilation/impconv.d(31): cannot pass argument `4294967295u` of type `uint` to parameter `float`
+fail_compilation/impconv.d(34): Error: function `impconv.foo_double(double)` is not callable using argument types `(long)`
+fail_compilation/impconv.d(34): cannot pass argument `-9223372036854775807L` of type `long` to parameter `double`
+fail_compilation/impconv.d(35): Error: function `impconv.foo_double(double)` is not callable using argument types `(ulong)`
+fail_compilation/impconv.d(35): cannot pass argument `18446744073709551615LU` of type `ulong` to parameter `double`
+---
+*/
+
+void foo_float(float);
+void foo_double(double);
+void foo_real(real);
+
+void main()
+{
+ foo_float(1); // implicitly convertible to float
+ foo_float(-int.max); // -(2^31 - 1)
+ foo_float(uint.max); // 2^32 - 1
+
+ foo_double(int.max); // implicitly convertible to double
+ foo_double(-long.max); // -(2^63 - 1)
+ foo_double(ulong.max); // 2^64 - 1
+
+ foo_real(0xffff_ffff_ffffL); // 2^48 - 1, implicitly convertible to real
+ static assert(__traits(compiles, foo_real(-long.max)) == (real.mant_dig >= 63));
+ static assert(__traits(compiles, foo_real(ulong.max)) == (real.mant_dig >= 64));
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imphint.d b/gcc/testsuite/gdc.test/fail_compilation/imphint.d
index 2b3abeb45df..101e7862f3a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/imphint.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/imphint.d
@@ -1,12 +1,50 @@
/*
TEST_OUTPUT:
---
-fail_compilation/imphint.d(14): Error: 'printf' is not defined, perhaps you need to import core.stdc.stdio; ?
-fail_compilation/imphint.d(15): Error: 'writeln' is not defined, perhaps you need to import std.stdio; ?
-fail_compilation/imphint.d(16): Error: 'sin' is not defined, perhaps you need to import std.math; ?
-fail_compilation/imphint.d(17): Error: 'cos' is not defined, perhaps you need to import std.math; ?
-fail_compilation/imphint.d(18): Error: 'sqrt' is not defined, perhaps you need to import std.math; ?
-fail_compilation/imphint.d(19): Error: 'fabs' is not defined, perhaps you need to import std.math; ?
+fail_compilation/imphint.d(53): Error: `printf` is not defined, perhaps `import core.stdc.stdio;` is needed?
+fail_compilation/imphint.d(54): Error: `writeln` is not defined, perhaps `import std.stdio;` is needed?
+fail_compilation/imphint.d(55): Error: `sin` is not defined, perhaps `import std.math;` is needed?
+fail_compilation/imphint.d(56): Error: `cos` is not defined, perhaps `import std.math;` is needed?
+fail_compilation/imphint.d(57): Error: `sqrt` is not defined, perhaps `import std.math;` is needed?
+fail_compilation/imphint.d(58): Error: `fabs` is not defined, perhaps `import std.math;` is needed?
+fail_compilation/imphint.d(61): Error: `AliasSeq` is not defined, perhaps `import std.meta;` is needed?
+fail_compilation/imphint.d(62): Error: `appender` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(63): Error: `array` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(64): Error: `calloc` is not defined, perhaps `import core.stdc.stdlib;` is needed?
+fail_compilation/imphint.d(65): Error: `chdir` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(66): Error: `dirEntries` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(67): Error: `drop` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(68): Error: `each` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(69): Error: `empty` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(70): Error: `enumerate` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(71): Error: `endsWith` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(72): Error: `enforce` is not defined, perhaps `import std.exception;` is needed?
+fail_compilation/imphint.d(73): Error: `equal` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(74): Error: `exists` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(75): Error: `filter` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(76): Error: `format` is not defined, perhaps `import std.format;` is needed?
+fail_compilation/imphint.d(77): Error: `free` is not defined, perhaps `import core.stdc.stdlib;` is needed?
+fail_compilation/imphint.d(78): Error: `front` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(79): Error: `iota` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(80): Error: `isDir` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(81): Error: `isFile` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(82): Error: `join` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(83): Error: `joiner` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(84): Error: `malloc` is not defined, perhaps `import core.stdc.stdlib;` is needed?
+fail_compilation/imphint.d(85): Error: `map` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(86): Error: `max` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(87): Error: `min` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(88): Error: `mkdir` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(89): Error: `popFront` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(90): Error: `realloc` is not defined, perhaps `import core.stdc.stdlib;` is needed?
+fail_compilation/imphint.d(91): Error: `replace` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(92): Error: `rmdir` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(93): Error: `sort` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(94): Error: `split` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(95): Error: `startsWith` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(96): Error: `take` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(97): Error: `text` is not defined, perhaps `import std.conv;` is needed?
+fail_compilation/imphint.d(98): Error: `to` is not defined, perhaps `import std.conv;` is needed?
---
*/
@@ -18,4 +56,44 @@ void foo()
cos(1.2);
sqrt(2.0);
fabs(-3);
+
+
+ AliasSeq();
+ appender();
+ array();
+ calloc();
+ chdir();
+ dirEntries();
+ drop();
+ each();
+ empty();
+ enumerate();
+ endsWith();
+ enforce();
+ equal();
+ exists();
+ filter();
+ format();
+ free();
+ front();
+ iota();
+ isDir();
+ isFile();
+ join();
+ joiner();
+ malloc();
+ map();
+ max();
+ min();
+ mkdir();
+ popFront();
+ realloc();
+ replace();
+ rmdir();
+ sort();
+ split();
+ startsWith();
+ take();
+ text();
+ to();
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a14407.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a14407.d
deleted file mode 100644
index d906bc74404..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/imports/a14407.d
+++ /dev/null
@@ -1,19 +0,0 @@
-module imports.a14407;
-
-deprecated class C
-{
- private deprecated new (size_t, string)
- {
- return null;
- }
- private this(int) {}
-}
-
-deprecated struct S
-{
- private deprecated new (size_t, string)
- {
- return null;
- }
- private this(int) {}
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a17625.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a17625.d
new file mode 100644
index 00000000000..d8a457c3e8d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/a17625.d
@@ -0,0 +1,3 @@
+module a17625;
+
+private int boo() { return 69; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d
new file mode 100644
index 00000000000..73df7511ceb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d
@@ -0,0 +1,5 @@
+module a18243;
+
+import std.math : isNaN;
+
+public bool isNaN() { return false; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/b17625.d b/gcc/testsuite/gdc.test/fail_compilation/imports/b17625.d
new file mode 100644
index 00000000000..02946b0c506
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/b17625.d
@@ -0,0 +1,3 @@
+module b17625;
+
+private int boo() { return 45; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/b17918a.d b/gcc/testsuite/gdc.test/fail_compilation/imports/b17918a.d
new file mode 100644
index 00000000000..03cf9a94569
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/b17918a.d
@@ -0,0 +1,9 @@
+module imports.b17918a;
+
+class Base
+{
+ auto byNode()
+ {
+ return _listMap;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/b19762.d b/gcc/testsuite/gdc.test/fail_compilation/imports/b19762.d
new file mode 100644
index 00000000000..f089354f521
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/b19762.d
@@ -0,0 +1,7 @@
+module imports.b19762;
+
+struct Baz {}
+struct Qux
+{
+ import imports.c19762;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/c19762.d b/gcc/testsuite/gdc.test/fail_compilation/imports/c19762.d
new file mode 100644
index 00000000000..ec4d389dca0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/c19762.d
@@ -0,0 +1,27 @@
+module imports.c19762;
+
+struct Foo
+{
+ import ice19762 : X;
+ X[] x;
+}
+
+Nullable!Foo foo()
+{
+ Nullable!Foo output;
+ return output;
+}
+
+struct Nullable(T)
+{
+ bool opEquals(U)(const(U) rhs) const
+ if (is(typeof(this.get == rhs)))
+ {
+ return true;
+ }
+
+ inout(T) get() inout
+ {
+ return T.init;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/constraints.d b/gcc/testsuite/gdc.test/fail_compilation/imports/constraints.d
new file mode 100755
index 00000000000..a19e89ac00c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/constraints.d
@@ -0,0 +1,73 @@
+module imports.constraints;
+
+// can be shared between usual and verbose output versions
+
+enum P(T) = true;
+enum N(T) = false;
+
+// constraints_func1
+void test1(T)(T v) if (N!T);
+void test2(T)(T v) if (!P!T);
+void test3(T)(T v) if (P!T && N!T);
+void test4(T)(T v) if (P!T && N!T && P!T);
+void test5(T)(T v) if (N!T || N!T);
+void test6(T)(T v) if (N!T || N!T || !P!T);
+void test7(T)(T v) if (N!T || P!T && N!T);
+void test8(T)(T v) if ((N!T || P!T) && N!T);
+void test9(T)(T v) if (!P!T && !N!T);
+void test10(T)(T v) if (!N!T && !P!T);
+void test11(T)(T v) if (!(!N!T && P!T));
+void test12(T)(T v) if (!(N!T || P!T));
+
+// constraints_func2
+void test13(T)(T v) if (P!T ? N!T : P!T); // P!T && N!T || !P!T && P!T
+void test14(T)(T v) if (!P!T ? P!T : N!T);
+void test15(T)(T v) if (!(P!T ? P!T : N!T)); // (!P!T || !P!T) && (P!T || !N!T)
+void test16(T)(T v) if (N!T && P!T || N!T);
+void test17(T)(T v) if (N!T && P!T && (P!T || P!T));
+void test18(T)(T v) if ((N!T || P!T && N!T) && P!T);
+void test19(T)(T v) if ((N!T ? P!T : !P!T) ? P!T : N!T); // (N!T && P!T || !N!T && !P!T) && P!T || (!N!T || !P!T) && (N!T || P!T) && N!T
+void test20(T)(T v) if (N!T && (P!T && N!T || N!T));
+void test21(T)(T v) if (P!T && (N!T && P!T || N!T));
+void test22(T)(T v) if ((!P!T || !P!T && P!T) && (N!T || !P!T));
+void test23(T)(T v) if (!P!T || P!T && N!T || !P!T);
+void test24(R)(R r) if (__traits(hasMember, R, "stuff"));
+int test25(T)(T v) if (N!T);
+float test26(T, U)(U u) if (N!U);
+
+// constraints_func3
+void overload(T)(T v) if (N!T);
+void overload(T)(T v) if (!P!T);
+void overload(T)(T v1, T v2) if (N!T);
+void overload(T, V)(T v1, V v2) if (N!T || N!V);
+void variadic(A, T...)(A a, T v) if (N!int);
+
+// constraints_tmpl
+void dummy()() if (false);
+void message_nice(T, U)() if (P!T && "message 1" && N!U && "message 2");
+void message_ugly(T)(T v) if (!N!T && (T.stringof ~ " must be that") && N!T);
+void args(T, U)() if (N!T || N!U);
+void lambda(alias pred)() if (N!int);
+
+// constraints_defs
+void def(T, int i = 5, alias R)() if (N!T);
+void defa(T, U = int)() if (N!T);
+void defv(T = bool, int i = 5, Ts...)() if (N!T);
+
+// constraints_aggr
+class C
+{
+ void f(T)(T v) if (P!T && !P!T)
+ {}
+
+ void g(this T)() if (N!T)
+ {}
+}
+
+template S(T) if (N!T)
+{
+ alias S = T;
+}
+
+struct BitFlags(E, bool unsafe = false) if (unsafe || N!E)
+{}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/deprecatedImporta.d b/gcc/testsuite/gdc.test/fail_compilation/imports/deprecatedImporta.d
new file mode 100644
index 00000000000..ef64878abbf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/deprecatedImporta.d
@@ -0,0 +1,2 @@
+deprecated("Please import deprecatedImportb directly!")
+public import imports.deprecatedImportb : AliasSeq, foo, bar, E, S, C, I;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/deprecatedImportb.d b/gcc/testsuite/gdc.test/fail_compilation/imports/deprecatedImportb.d
new file mode 100644
index 00000000000..4a53e7f24e7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/deprecatedImportb.d
@@ -0,0 +1,13 @@
+alias AliasSeq(T...) = T;
+
+void foo() {}
+
+void bar(T)(T t) {}
+
+enum E = 2;
+
+struct S {}
+
+class C {}
+
+interface I {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/diag20518a.d b/gcc/testsuite/gdc.test/fail_compilation/imports/diag20518a.d
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/diag20518a.d
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/diag20518a/b.d b/gcc/testsuite/gdc.test/fail_compilation/imports/diag20518a/b.d
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/diag20518a/b.d
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/diag9210stdcomplex.d b/gcc/testsuite/gdc.test/fail_compilation/imports/diag9210stdcomplex.d
index 58aa99a740f..76a2ceec8b9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/imports/diag9210stdcomplex.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/diag9210stdcomplex.d
@@ -8,7 +8,7 @@ struct Complex(T) if (isFloatingPoint!T)
T im;
}
-// Bugzilla 9210: Complex!real instantiation is incomplete in here,
+// https://issues.dlang.org/show_bug.cgi?id=9210: Complex!real instantiation is incomplete in here,
// because its completion is deferred by an "undefined identifier" error in imports.diag9210b.
Complex!real expi(real y)
{
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/fail20164.d b/gcc/testsuite/gdc.test/fail_compilation/imports/fail20164.d
new file mode 100644
index 00000000000..15d6359329f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/fail20164.d
@@ -0,0 +1 @@
+deprecated module imports.fail20164;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/fail20637b.d b/gcc/testsuite/gdc.test/fail_compilation/imports/fail20637b.d
new file mode 100644
index 00000000000..d6fbdf5ec67
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/fail20637b.d
@@ -0,0 +1,3 @@
+module imports.fail20637b;
+
+class A { private static void foo() { } }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/fail20638b.d b/gcc/testsuite/gdc.test/fail_compilation/imports/fail20638b.d
new file mode 100644
index 00000000000..71218d96e18
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/fail20638b.d
@@ -0,0 +1,3 @@
+module imports.fail20638b;
+
+private void foo() { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/fail21275a.d b/gcc/testsuite/gdc.test/fail_compilation/imports/fail21275a.d
new file mode 100644
index 00000000000..3fee203858c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/fail21275a.d
@@ -0,0 +1,34 @@
+module imports.fail21275a;
+
+struct Foo
+{
+ private int _x;
+
+ private ref int x() return
+ {
+ return _x;
+ }
+
+ int x() const
+ {
+ return _x;
+ }
+
+}
+
+struct Bar
+{
+
+ private int _x;
+
+ private int x(int)
+ {
+ return _x;
+ }
+
+ int x() const
+ {
+ return _x;
+ }
+
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/i20057.d b/gcc/testsuite/gdc.test/fail_compilation/imports/i20057.d
new file mode 100644
index 00000000000..9c9c6669a91
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/i20057.d
@@ -0,0 +1,13 @@
+template generateEmptyFunction(C, func...)
+{
+}
+
+template isAbstractFunction(T...)
+if (T.length == 1)
+{
+ enum bool isAbstractFunction = __traits(isAbstractFunction, T[0]);
+}
+
+alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFunction);
+
+class AutoImplement(Base, alias how, alias what = isAbstractFunction) : Base {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/imp17602.d b/gcc/testsuite/gdc.test/fail_compilation/imports/imp17602.d
new file mode 100644
index 00000000000..c1e6790283f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/imp17602.d
@@ -0,0 +1,3 @@
+module imports.imp17602;
+
+enum Status { on }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/imp18554.d b/gcc/testsuite/gdc.test/fail_compilation/imports/imp18554.d
new file mode 100644
index 00000000000..9f2a855b9a6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/imp18554.d
@@ -0,0 +1,4 @@
+struct S
+{
+ private int i;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/imp18979.d b/gcc/testsuite/gdc.test/fail_compilation/imports/imp18979.d
new file mode 100644
index 00000000000..9403f696d1d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/imp18979.d
@@ -0,0 +1,6 @@
+module imports.imp18979;
+
+struct Foo
+{
+ private this(A)(A a) {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/imp19661.d b/gcc/testsuite/gdc.test/fail_compilation/imports/imp19661.d
new file mode 100644
index 00000000000..3a968037538
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/imp19661.d
@@ -0,0 +1,17 @@
+template isFunction(X...)
+if (X.length == 1)
+{
+ static if (is(typeof(&X[0]) U : U*) && is(U == function) ||
+ is(typeof(&X[0]) U == delegate))
+ {
+ // x is a (nested) function symbol.
+ enum isFunction = true;
+ }
+ else static if (is(X[0] T))
+ {
+ // x is a type. Take the type of it and examine.
+ enum isFunction = is(T == function);
+ }
+ else
+ enum isFunction = false;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/imp20709.d b/gcc/testsuite/gdc.test/fail_compilation/imports/imp20709.d
new file mode 100644
index 00000000000..a2329743cba
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/imp20709.d
@@ -0,0 +1 @@
+module imp20709;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/imp21832.d b/gcc/testsuite/gdc.test/fail_compilation/imports/imp21832.d
new file mode 100644
index 00000000000..ee4a1d6296d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/imp21832.d
@@ -0,0 +1,24 @@
+module imports.imp21832;
+static if(1)
+{
+ int fun(int a)
+ {
+ return a;
+ }
+ int tpl()(int a)
+ {
+ return a;
+ }
+}
+
+deprecated
+{
+ int fun(char a)
+ {
+ return a;
+ }
+ int tpl()(char a)
+ {
+ return a;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/import21508.d b/gcc/testsuite/gdc.test/fail_compilation/imports/import21508.d
new file mode 100644
index 00000000000..07aa66e52a7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/import21508.d
@@ -0,0 +1,2 @@
+module import21508;
+private class import21508 {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/issue21685.d b/gcc/testsuite/gdc.test/fail_compilation/imports/issue21685.d
new file mode 100644
index 00000000000..eef95bf9ade
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/issue21685.d
@@ -0,0 +1,6 @@
+module issue21685;
+
+class E
+{
+ private this() {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test18480a.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test18480a.d
new file mode 100644
index 00000000000..f20cf8a42bc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test18480a.d
@@ -0,0 +1,3 @@
+public import imports.test18480b : TestTemplate;
+alias TestTemplate = TestTemplate;
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test18480b.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test18480b.d
new file mode 100644
index 00000000000..aa3ebd1be8e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test18480b.d
@@ -0,0 +1 @@
+template TestTemplate() { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test18938a/cache.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test18938a/cache.d
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test18938a/cache.d
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test18938a/file.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test18938a/file.d
new file mode 100644
index 00000000000..ee3199dd8ae
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test18938a/file.d
@@ -0,0 +1,13 @@
+import imports.test18938b.file;
+class IconThemeGroup : IniLikeGroup
+{
+ this()
+ {
+ super("Icon Theme");
+ }
+
+ ///setter
+ string inherits()() {
+ }
+
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test18938b/file.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test18938b/file.d
new file mode 100644
index 00000000000..977fe7a278c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test18938b/file.d
@@ -0,0 +1,28 @@
+module imports.test18938b.file;
+import std.algorithm;
+
+class IniLikeGroup
+{
+ this(string ) {}
+ @trusted byNode()
+ {
+ map!(node => lineNode);
+ }
+}
+
+
+class IniLikeFile
+{
+ struct WriteOptions
+ {
+ static exact()
+ {
+ return WriteOptions(No.lineBetweenGroups);
+ }
+
+ this(Args)(Args ){}
+
+ }
+ void saveToFile(WriteOptions = WriteOptions.exact) {}
+ final save() {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test19107a.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test19107a.d
new file mode 100644
index 00000000000..d270e3b497b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test19107a.d
@@ -0,0 +1,3 @@
+module imports.test19107a.d;
+
+alias I(alias A) = A;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test19107b.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test19107b.d
new file mode 100644
index 00000000000..8fd8087ef93
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test19107b.d
@@ -0,0 +1,3 @@
+module imports.test19107b;
+
+import imports.test19107a : I;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test20267.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test20267.d
new file mode 100644
index 00000000000..05059f6c5e5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test20267.d
@@ -0,0 +1,3 @@
+module imports.test20267;
+
+int[1] array;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test21246.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test21246.d
new file mode 100644
index 00000000000..5294c41ae28
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test21246.d
@@ -0,0 +1,8 @@
+module imports.test21246;
+
+class Clock {}
+
+class B
+{
+ void set (Clock clock) { }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue15103.d b/gcc/testsuite/gdc.test/fail_compilation/issue15103.d
new file mode 100644
index 00000000000..c4a67eb8f71
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue15103.d
@@ -0,0 +1,25 @@
+/*
+TEST_OUTPUT:
+----
+fail_compilation/issue15103.d(22): Error: found `(` when expecting `;` or `=`, did you mean `Foo foo = 42`?
+fail_compilation/issue15103.d(23): Error: found `(` when expecting `;` or `=`, did you mean `Boo boo = 43`?
+fail_compilation/issue15103.d(24): Error: found `(` when expecting `;` or `=`, did you mean `string y = "a string"`?
+---
+*/
+
+struct Foo
+{
+ this(int x) {}
+}
+
+class Boo
+{
+ this(int x) {}
+}
+
+void main ()
+{
+ Foo foo(42);
+ Boo boo(43);
+ string y("a string");
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue20422.d b/gcc/testsuite/gdc.test/fail_compilation/issue20422.d
new file mode 100644
index 00000000000..1964f8ac888
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue20422.d
@@ -0,0 +1,12 @@
+// https://issues.dlang.org/show_bug.cgi?id=20422
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/issue20422.d(11): Error: missing length argument for array
+---
+*/
+
+void main() {
+ new int[];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue20627.d b/gcc/testsuite/gdc.test/fail_compilation/issue20627.d
new file mode 100644
index 00000000000..ff08c6a2d70
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue20627.d
@@ -0,0 +1,67 @@
+/**
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/issue20627.d(38): Deprecation: `shared static` constructor can only be of D linkage
+fail_compilation/issue20627.d(39): Deprecation: `shared static` destructor can only be of D linkage
+fail_compilation/issue20627.d(40): Deprecation: `static` constructor can only be of D linkage
+fail_compilation/issue20627.d(41): Deprecation: `static` destructor can only be of D linkage
+fail_compilation/issue20627.d(55): Deprecation: `shared static` constructor can only be of D linkage
+fail_compilation/issue20627.d(56): Deprecation: `shared static` destructor can only be of D linkage
+fail_compilation/issue20627.d(57): Deprecation: `static` constructor can only be of D linkage
+fail_compilation/issue20627.d(58): Deprecation: `static` destructor can only be of D linkage
+fail_compilation/issue20627.d(63): Deprecation: `shared static` constructor can only be of D linkage
+fail_compilation/issue20627.d(64): Deprecation: `shared static` destructor can only be of D linkage
+fail_compilation/issue20627.d(65): Deprecation: `static` constructor can only be of D linkage
+fail_compilation/issue20627.d(66): Deprecation: `static` destructor can only be of D linkage
+---
+*/
+
+// OK, default linkage
+shared static this () {}
+shared static ~this () {}
+static this () {}
+static ~this () {}
+
+// Still okay
+extern(D)
+{
+ shared static this () {}
+ shared static ~this () {}
+ static this () {}
+ static ~this () {}
+}
+
+// No!
+extern(C)
+{
+ shared static this () {}
+ shared static ~this () {}
+ static this () {}
+ static ~this () {}
+}
+
+// Disabled because platform specific
+version (none) extern(Objective-C)
+{
+ shared static this () {}
+ shared static ~this () {}
+ static this () {}
+ static ~this () {}
+}
+
+extern(C++)
+{
+ shared static this () {}
+ shared static ~this () {}
+ static this () {}
+ static ~this () {}
+}
+
+extern(System)
+{
+ shared static this () {}
+ shared static ~this () {}
+ static this () {}
+ static ~this () {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue20704.d b/gcc/testsuite/gdc.test/fail_compilation/issue20704.d
new file mode 100644
index 00000000000..1e1f2e6f34b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue20704.d
@@ -0,0 +1,39 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/issue20704.d(17): Error: cannot modify constant `0`
+fail_compilation/issue20704.d(28): Error: template instance `issue20704.f2!int` error instantiating
+fail_compilation/issue20704.d(19): Error: cannot modify constant `0`
+fail_compilation/issue20704.d(30): Error: template instance `issue20704.f4!int` error instantiating
+fail_compilation/issue20704.d(17): Error: `S(0)` is not an lvalue and cannot be modified
+fail_compilation/issue20704.d(36): Error: template instance `issue20704.f2!(S)` error instantiating
+fail_compilation/issue20704.d(17): Error: `null` is not an lvalue and cannot be modified
+fail_compilation/issue20704.d(38): Error: template instance `issue20704.f2!(C)` error instantiating
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=20704
+
+void f1(T)(const auto ref T arg = T.init) {}
+void f2(T)(const ref T arg = T.init) {}
+void f3(T)(const auto ref T arg = 0) {}
+void f4(T)(const ref T arg = 0) {}
+
+struct S { int _; }
+class C { int _; }
+
+void main ()
+{
+ int i;
+ f1!int(i);
+ f2!int(i);
+ f3!int(i);
+ f4!int(i);
+ f1!int();
+ f2!int();
+ f3!int();
+ f4!int();
+ f1!S();
+ f2!S();
+ f1!C();
+ f2!C();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue21203.d b/gcc/testsuite/gdc.test/fail_compilation/issue21203.d
new file mode 100644
index 00000000000..7679d673c9e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue21203.d
@@ -0,0 +1,17 @@
+// Ideally this should work, at least give a nice error messae
+/**
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/issue21203.d(12): Error: pragma `mangle` cannot apply to a template declaration
+fail_compilation/issue21203.d(12): use `template Class(Args...){ pragma(mangle, "other_name") class Class {} }`
+---
+*/
+
+extern(C++)
+pragma(mangle,"gdkfjgh")
+class F(T)
+{
+
+}
+void use(F!int a) {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue21295.d b/gcc/testsuite/gdc.test/fail_compilation/issue21295.d
index 9916e9ba2a7..24d282cd8ab 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/issue21295.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue21295.d
@@ -1,8 +1,7 @@
/*
-REQUIRED_ARGS: -de
TEST_OUTPUT:
---
-fail_compilation/issue21295.d(9): Deprecation: imports.issue21295ast_node.Visitor is not visible from module issue21295
+fail_compilation/issue21295.d(8): Error: undefined identifier `Visitor`
---
*/
import imports.issue21295ast_node;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue21378.d b/gcc/testsuite/gdc.test/fail_compilation/issue21378.d
new file mode 100644
index 00000000000..22c60a37a4c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue21378.d
@@ -0,0 +1,16 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/issue21378.d(13): Error: function `issue21378.fn` circular dependency. Functions cannot be interpreted while being compiled
+fail_compilation/issue21378.d(12): called from here: `fn()`
+fail_compilation/issue21378.d(12): Error: pragma `inline` pragma(`inline`, `true` or `false`) expected, not `fn()`
+---
+*/
+
+// Cannot call the same function linked to the pragma
+// Really hard to fix this limitation in the implementation
+pragma(inline, fn())
+int fn()
+{
+ return 1;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue21685_main.d b/gcc/testsuite/gdc.test/fail_compilation/issue21685_main.d
new file mode 100644
index 00000000000..c6e29c3eb17
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue21685_main.d
@@ -0,0 +1,12 @@
+/* REQUIRED_ARGS: -preview=dip1000 -Ifail_compilation/imports
+TEST_OUTPUT:
+---
+fail_compilation/issue21685_main.d(11): Error: class `issue21685.E` constructor `this` is not accessible
+---
+*/
+import issue21685;
+
+void main()
+{
+ new E;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue21936.d b/gcc/testsuite/gdc.test/fail_compilation/issue21936.d
new file mode 100644
index 00000000000..a37aa042e1f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue21936.d
@@ -0,0 +1,32 @@
+/* REQUIRED_ARGS: -preview=dip1000 -Ifail_compilation/imports
+TEST_OUTPUT:
+---
+fail_compilation/issue21936.d(15): Error: struct `issue21936s.S` variable `field` is not accessible from `@safe` code
+fail_compilation/issue21936.d(15): Error: struct `issue21936s.S` variable `field` is not accessible from `@safe` code
+fail_compilation/issue21936.d(11): Error: template instance `issue21936.constructImplicit!(S)` error instantiating
+fail_compilation/issue21936.d(7): instantiated from here: `registerConstructors!(S)`
+fail_compilation/issue21936.d(21): instantiated from here: `registerType!(S)`
+---
+*/
+#line 2
+module issue21936;
+import issue21936s;
+struct Handlers {
+ void registerType(T)()
+ {
+ registerConstructors!T;
+ }
+ void registerConstructors(T)()
+ {
+ constructImplicit!T;
+ }
+}
+
+auto constructImplicit(T)(typeof(T.init.tupleof) x = T.init.tupleof)
+{
+}
+
+void registerHandlersDateTime(Handlers handlers)
+{
+ handlers.registerType!(S);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue3827.d b/gcc/testsuite/gdc.test/fail_compilation/issue3827.d
index 76d90ed7141..d17cee83584 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/issue3827.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/issue3827.d
@@ -2,8 +2,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/issue3827.d(12): Deprecation: Implicit string concatenation is deprecated, use "Hello" ~ "World" instead
-fail_compilation/issue3827.d(13): Deprecation: Implicit string concatenation is deprecated, use "A" ~ "B" instead
+fail_compilation/issue3827.d(14): Error: Implicit string concatenation is error-prone and disallowed in D
+fail_compilation/issue3827.d(14): Use the explicit syntax instead (concatenating literals is `@nogc`): "Hello" ~ "World"
+fail_compilation/issue3827.d(15): Error: Implicit string concatenation is error-prone and disallowed in D
+fail_compilation/issue3827.d(15): Use the explicit syntax instead (concatenating literals is `@nogc`): "A" ~ "B"
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/lexer1.d b/gcc/testsuite/gdc.test/fail_compilation/lexer1.d
index 31246ce6173..088e89718cf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/lexer1.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/lexer1.d
@@ -1,6 +1,7 @@
/*
TEST_OUTPUT:
---
+fail_compilation/lexer1.d(30): Error: Built-in hex string literals are obsolete, use `std.conv.hexString!"01 02 03"w` instead.
fail_compilation/lexer1.d(30): Error: declaration expected, not `x"01 02 03"w`
fail_compilation/lexer1.d(31): Error: declaration expected, not `2147483649U`
fail_compilation/lexer1.d(32): Error: declaration expected, not `0.1`
@@ -15,7 +16,7 @@ fail_compilation/lexer1.d(40): Error: declaration expected, not `65536U`
fail_compilation/lexer1.d(41): Error: declaration expected, not `"ab\\c\"\u1234a\U00011100a"d`
fail_compilation/lexer1.d(43): Error: declaration expected, not `module`
fail_compilation/lexer1.d(45): Error: escape hex sequence has 1 hex digits instead of 2
-fail_compilation/lexer1.d(46): Error: undefined escape hex sequence \G
+fail_compilation/lexer1.d(46): Error: undefined escape hex sequence \xG
fail_compilation/lexer1.d(47): Error: unnamed character entity &unnamedentity;
fail_compilation/lexer1.d(48): Error: unterminated named entity &1;
fail_compilation/lexer1.d(49): Error: unterminated named entity &*;
@@ -26,7 +27,6 @@ fail_compilation/lexer1.d(52): Error: escape octal sequence \400 is larger than
*/
// https://dlang.dawg.eu/coverage/src/lexer.c.gcov.html
-
x"01 02 03"w;
0x80000001;
0.1;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/lexer2.d b/gcc/testsuite/gdc.test/fail_compilation/lexer2.d
index d574b07d9b9..2386da597a8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/lexer2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/lexer2.d
@@ -1,11 +1,13 @@
/*
TEST_OUTPUT:
---
-fail_compilation/lexer2.d(14): Error: odd number (3) of hex characters in hex string
-fail_compilation/lexer2.d(15): Error: non-hex character 'G' in hex string
-fail_compilation/lexer2.d(16): Error: heredoc rest of line should be blank
-fail_compilation/lexer2.d(18): Error: unterminated delimited string constant starting at fail_compilation/lexer2.d(18)
-fail_compilation/lexer2.d(20): Error: semicolon expected following auto declaration, not `EOF`
+fail_compilation/lexer2.d(16): Error: odd number (3) of hex characters in hex string
+fail_compilation/lexer2.d(16): Error: Built-in hex string literals are obsolete, use `std.conv.hexString!"123"` instead.
+fail_compilation/lexer2.d(17): Error: non-hex character 'G' in hex string
+fail_compilation/lexer2.d(17): Error: Built-in hex string literals are obsolete, use `std.conv.hexString!"123G"` instead.
+fail_compilation/lexer2.d(18): Error: heredoc rest of line should be blank
+fail_compilation/lexer2.d(20): Error: unterminated delimited string constant starting at fail_compilation/lexer2.d(20)
+fail_compilation/lexer2.d(22): Error: semicolon expected following auto declaration, not `End of File`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/lexer3.d b/gcc/testsuite/gdc.test/fail_compilation/lexer3.d
index f2bcda4ba8a..dc6ad640767 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/lexer3.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/lexer3.d
@@ -2,7 +2,7 @@
TEST_OUTPUT:
---
fail_compilation/lexer3.d(9): Error: unterminated token string constant starting at fail_compilation/lexer3.d(9)
-fail_compilation/lexer3.d(10): Error: semicolon expected following auto declaration, not `EOF`
+fail_compilation/lexer3.d(10): Error: semicolon expected following auto declaration, not `End of File`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/lexer4.d b/gcc/testsuite/gdc.test/fail_compilation/lexer4.d
index 374f69d5a13..ecc72085fb8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/lexer4.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/lexer4.d
@@ -4,10 +4,10 @@ TEST_OUTPUT:
fail_compilation/lexer4.d(22): Error: unterminated character constant
fail_compilation/lexer4.d(24): Error: unterminated character constant
fail_compilation/lexer4.d(25): Error: unterminated character constant
-fail_compilation/lexer4.d(26): Error: binary digit expected
-fail_compilation/lexer4.d(27): Error: radix 8 digit expected, not `8`
-fail_compilation/lexer4.d(27): Error: octal literals `0130` are no longer supported, use `std.conv.octal!130` instead
-fail_compilation/lexer4.d(28): Error: radix 10 digit expected, not `a`
+fail_compilation/lexer4.d(26): Error: binary digit expected, not `2`
+fail_compilation/lexer4.d(27): Error: octal digit expected, not `8`
+fail_compilation/lexer4.d(27): Error: octal literals larger than 7 are no longer supported
+fail_compilation/lexer4.d(28): Error: decimal digit expected, not `a`
fail_compilation/lexer4.d(29): Error: unrecognized token
fail_compilation/lexer4.d(30): Error: exponent required for hex float
fail_compilation/lexer4.d(31): Error: lower case integer suffix 'l' is not allowed. Please use 'L' instead
diff --git a/gcc/testsuite/gdc.test/fail_compilation/lookup.d b/gcc/testsuite/gdc.test/fail_compilation/lookup.d
index aedb44e65fd..fe752f2f4d7 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/lookup.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/lookup.d
@@ -1,10 +1,11 @@
/*
+EXTRA_FILES: imports/imp1.d imports/imp2.d
TEST_OUTPUT:
---
-fail_compilation/lookup.d(23): Error: no property `X` for type `lookup.B`, did you mean `imports.imp2.X`?
-fail_compilation/lookup.d(23): while evaluating: `static assert((B).X == 0)`
-+fail_compilation/lookup.d(24): Error: no property `Y` for type `lookup.B`, did you mean `imports.imp2.Y`?
-fail_compilation/lookup.d(24): while evaluating: `static assert((B).Y == 2)`
+fail_compilation/lookup.d(24): Error: no property `X` for type `lookup.B`, did you mean `imports.imp2.X`?
+fail_compilation/lookup.d(24): while evaluating: `static assert((B).X == 0)`
+fail_compilation/lookup.d(25): Error: no property `Y` for type `lookup.B`, did you mean `imports.imp2.Y`?
+fail_compilation/lookup.d(25): while evaluating: `static assert((B).Y == 2)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/mangle1.d b/gcc/testsuite/gdc.test/fail_compilation/mangle1.d
index bc2bc3dda89..a97a55b121a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/mangle1.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/mangle1.d
@@ -1,7 +1,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/mangle1.d(8): Error: pragma mangle can only apply to a single declaration
+fail_compilation/mangle1.d(8): Error: pragma `mangle` can only apply to a single declaration
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/mangle2.d b/gcc/testsuite/gdc.test/fail_compilation/mangle2.d
index e68c92acebb..415e719f473 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/mangle2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/mangle2.d
@@ -1,18 +1,18 @@
/*
TEST_OUTPUT:
---
-fail_compilation/mangle2.d(20): Error: pragma mangle char 0x20 not allowed in mangled name
-fail_compilation/mangle2.d(21): Error: pragma mangle char 0x20 not allowed in mangled name
-fail_compilation/mangle2.d(24): Error: pragma mangle char 0x0a not allowed in mangled name
-fail_compilation/mangle2.d(25): Error: pragma mangle char 0x0a not allowed in mangled name
-fail_compilation/mangle2.d(28): Error: pragma mangle char 0x07 not allowed in mangled name
-fail_compilation/mangle2.d(29): Error: pragma mangle char 0x07 not allowed in mangled name
-fail_compilation/mangle2.d(32): Error: pragma mangle char 0x01 not allowed in mangled name
-fail_compilation/mangle2.d(33): Error: pragma mangle char 0x01 not allowed in mangled name
-fail_compilation/mangle2.d(36): Error: pragma mangle char 0x00 not allowed in mangled name
-fail_compilation/mangle2.d(37): Error: pragma mangle char 0x00 not allowed in mangled name
-fail_compilation/mangle2.d(40): Error: pragma mangle Outside Unicode code space
-fail_compilation/mangle2.d(41): Error: pragma mangle Outside Unicode code space
+fail_compilation/mangle2.d(20): Error: pragma `mangle` char 0x20 not allowed in mangled name
+fail_compilation/mangle2.d(21): Error: pragma `mangle` char 0x20 not allowed in mangled name
+fail_compilation/mangle2.d(24): Error: pragma `mangle` char 0x0a not allowed in mangled name
+fail_compilation/mangle2.d(25): Error: pragma `mangle` char 0x0a not allowed in mangled name
+fail_compilation/mangle2.d(28): Error: pragma `mangle` char 0x07 not allowed in mangled name
+fail_compilation/mangle2.d(29): Error: pragma `mangle` char 0x07 not allowed in mangled name
+fail_compilation/mangle2.d(32): Error: pragma `mangle` char 0x01 not allowed in mangled name
+fail_compilation/mangle2.d(33): Error: pragma `mangle` char 0x01 not allowed in mangled name
+fail_compilation/mangle2.d(36): Error: pragma `mangle` char 0x00 not allowed in mangled name
+fail_compilation/mangle2.d(37): Error: pragma `mangle` char 0x00 not allowed in mangled name
+fail_compilation/mangle2.d(40): Error: pragma `mangle` Outside Unicode code space
+fail_compilation/mangle2.d(41): Error: pragma `mangle` Outside Unicode code space
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/misc1.d b/gcc/testsuite/gdc.test/fail_compilation/misc1.d
new file mode 100644
index 00000000000..9a319ebac28
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/misc1.d
@@ -0,0 +1,20 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/misc1.d(108): Error: `5` has no effect
+fail_compilation/misc1.d(109): Error: `1 + 2` has no effect
+---
+*/
+
+#line 100
+
+/***************************************************/
+//https://issues.dlang.org/show_bug.cgi?id=12490
+
+void hasSideEffect12490(){}
+
+void issue12490()
+{
+ 5, hasSideEffect12490();
+ 1 + 2, hasSideEffect12490();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d b/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d
new file mode 100644
index 00000000000..df169e198cf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d
@@ -0,0 +1,51 @@
+/*
+REQUIRED_ARGS: -verrors=0
+TEST_OUTPUT:
+---
+fail_compilation/misc_parser_err_cov1.d(29): Error: basic type expected, not `)`
+fail_compilation/misc_parser_err_cov1.d(30): Error: basic type expected, not `)`
+fail_compilation/misc_parser_err_cov1.d(31): Error: `__traits(identifier, args...)` expected
+fail_compilation/misc_parser_err_cov1.d(31): Error: semicolon expected following auto declaration, not `o`
+fail_compilation/misc_parser_err_cov1.d(31): Error: expression expected, not `)`
+fail_compilation/misc_parser_err_cov1.d(32): Error: `type identifier : specialization` expected following `is`
+fail_compilation/misc_parser_err_cov1.d(33): Error: semicolon expected following auto declaration, not `auto`
+fail_compilation/misc_parser_err_cov1.d(33): Error: found `+` when expecting `(` following `mixin`
+fail_compilation/misc_parser_err_cov1.d(34): Error: cannot create a `char[float]` with `new`
+fail_compilation/misc_parser_err_cov1.d(35): Error: `key:value` expected for associative array literal
+fail_compilation/misc_parser_err_cov1.d(36): Error: basic type expected, not `;`
+fail_compilation/misc_parser_err_cov1.d(36): Error: `{ members }` expected for anonymous class
+fail_compilation/misc_parser_err_cov1.d(38): Error: template argument expected following `!`
+fail_compilation/misc_parser_err_cov1.d(38): Error: found `if` when expecting `)`
+fail_compilation/misc_parser_err_cov1.d(38): Error: found `)` instead of statement
+fail_compilation/misc_parser_err_cov1.d(39): Error: identifier expected following `(type)`.
+fail_compilation/misc_parser_err_cov1.d(39): Error: expression expected, not `;`
+fail_compilation/misc_parser_err_cov1.d(40): Error: semicolon expected following auto declaration, not `auto`
+fail_compilation/misc_parser_err_cov1.d(40): Error: identifier or `new` expected following `.`, not `+`
+fail_compilation/misc_parser_err_cov1.d(41): Error: declaration expected, not `(`
+fail_compilation/misc_parser_err_cov1.d(42): Error: unrecognized declaration
+---
+*/
+module misc_parser_err_cov1;
+
+
+//https://issues.dlang.org/show_bug.cgi?id=19995
+#line 29
+void foo(in);
+void bar(int, const @tation);
+
+void main()
+{
+ // cover errors from line 7930 to EOF
+ #line 31
+ auto tt = __traits(<o<);
+ auto b = is ;
+ auto mx1 = mixin +);
+ auto aa1 = new char[float];
+ aa += [key:value, key];
+ auto anon1 = new class;
+ auto anon2 = new class {};
+ if (parseShift !if){}
+ auto unaryExParseError = immutable(int).+;
+ auto postFixParseError = int.max.+;
+ (int).+;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/mixin.d b/gcc/testsuite/gdc.test/fail_compilation/mixin.d
new file mode 100644
index 00000000000..1db12067d3d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/mixin.d
@@ -0,0 +1,25 @@
+// REQUIRED_ARGS: -mixin=${RESULTS_DIR}/fail_compilation/mixin_test.mixin
+/*
+TEST_OUTPUT:
+---
+{{RESULTS_DIR}}/fail_compilation/mixin_test.mixin(7): Error: undefined identifier `b`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=1870
+// https://issues.dlang.org/show_bug.cgi?id=12790
+string get()
+{
+ return
+ q{int x;
+ int y;
+
+
+
+ int z = x + b;};
+}
+
+void main()
+{
+ mixin(get());
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/mixin_gc.d b/gcc/testsuite/gdc.test/fail_compilation/mixin_gc.d
new file mode 100644
index 00000000000..6c2d8ded400
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/mixin_gc.d
@@ -0,0 +1,25 @@
+// REQUIRED_ARGS: -mixin=${RESULTS_DIR}/fail_compilation/mixin_test.mixin -lowmem
+/*
+TEST_OUTPUT:
+---
+{{RESULTS_DIR}}/fail_compilation/mixin_test.mixin(7): Error: undefined identifier `b`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=1870
+// https://issues.dlang.org/show_bug.cgi?id=12790
+string get()
+{
+ return
+ q{int x;
+ int y;
+
+
+
+ int z = x + b;};
+}
+
+void main()
+{
+ mixin(get());
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/nestedtempl0.d b/gcc/testsuite/gdc.test/fail_compilation/nestedtempl0.d
new file mode 100644
index 00000000000..d323c34949c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/nestedtempl0.d
@@ -0,0 +1,35 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/nestedtempl0.d(18): Error: class `nestedtempl0.K.D!(1, B!(a)).D` doesn't need a frame pointer, but super class `B` needs the frame pointer of `main`
+fail_compilation/nestedtempl0.d(28): Error: template instance `nestedtempl0.K.D!(1, B!(a))` error instantiating
+fail_compilation/nestedtempl0.d(18): Error: class `nestedtempl0.main.fun.D!(b, B!(a)).D` needs the frame pointer of `fun`, but super class `B` needs the frame pointer of `main`
+fail_compilation/nestedtempl0.d(33): Error: template instance `nestedtempl0.main.fun.D!(b, B!(a))` error instantiating
+---
+*/
+
+class K
+{
+ class B(alias a)
+ {
+
+ }
+
+ class D(alias a, T) : T
+ {
+
+ }
+}
+
+void main()
+{
+ int a;
+ auto k = new K;
+ auto d = k.new K.D!(1, K.B!a);
+
+ auto fun()
+ {
+ int b;
+ auto o = k.new K.D!(b, K.B!a);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/nestedtempl1.d b/gcc/testsuite/gdc.test/fail_compilation/nestedtempl1.d
new file mode 100644
index 00000000000..c34d70b53e1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/nestedtempl1.d
@@ -0,0 +1,27 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/nestedtempl1.d(14): Deprecation: function `nestedtempl1.main.bar!(a).bar` function requires a dual-context, which is deprecated
+fail_compilation/nestedtempl1.d(26): instantiated from here: `bar!(a)`
+fail_compilation/nestedtempl1.d(26): Error: modify `inout` to `mutable` is not allowed inside `inout` function
+---
+*/
+
+auto foo(ref inout(int) x)
+{
+ struct S
+ {
+ ref inout(int) bar(alias a)() inout
+ {
+ return x;
+ }
+ }
+ return S();
+}
+
+void main()
+{
+ int a;
+ auto o = foo(a);
+ o.bar!a() = 1; // bad!
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/nestedtempl2.d b/gcc/testsuite/gdc.test/fail_compilation/nestedtempl2.d
new file mode 100644
index 00000000000..afc8a29969c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/nestedtempl2.d
@@ -0,0 +1,38 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/nestedtempl2.d(22): Deprecation: function `nestedtempl2.B.func!(n).func` function requires a dual-context, which is deprecated
+fail_compilation/nestedtempl2.d(34): instantiated from here: `func!(n)`
+fail_compilation/nestedtempl2.d(34): Error: `this` is only defined in non-static member functions, not `test`
+fail_compilation/nestedtempl2.d(34): Error: need `this` of type `B` to call function `func`
+fail_compilation/nestedtempl2.d(35): Error: `this` is only defined in non-static member functions, not `test`
+fail_compilation/nestedtempl2.d(35): Error: need `this` of type `B` to make delegate from function `func`
+fail_compilation/nestedtempl2.d(37): Error: `this` is only defined in non-static member functions, not `test`
+fail_compilation/nestedtempl2.d(37): Error: need `this` of type `B` needed to `new` nested class `N`
+---
+*/
+
+class B
+{
+ int n;
+}
+
+void test()
+{
+ auto func(alias a)()
+ {
+ return a;
+ }
+
+ class N(alias a)
+ {
+ }
+
+ auto b = new B();
+ b.n = 1;
+
+ func!(b.n)();
+ auto dg = &func!(b.n);
+
+ new N!(b.n)();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/nestedtempl3.d b/gcc/testsuite/gdc.test/fail_compilation/nestedtempl3.d
new file mode 100644
index 00000000000..a95bda0a68d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/nestedtempl3.d
@@ -0,0 +1,24 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/nestedtempl3.d(23): Error: cannot access frame pointer of `nestedtempl3.test.S!(i).S`
+---
+*/
+
+void test()
+{
+ int i;
+
+ auto f0()
+ {
+ int j = 10;
+ struct S(alias a)
+ {
+ auto get() { return j; }
+ }
+ return S!i();
+ }
+
+ alias S = typeof(f0());
+ auto s = S();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/no_Throwable.d b/gcc/testsuite/gdc.test/fail_compilation/no_Throwable.d
new file mode 100644
index 00000000000..29481e3ae26
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/no_Throwable.d
@@ -0,0 +1,26 @@
+/*
+DFLAGS:
+REQUIRED_ARGS: -c
+EXTRA_SOURCES: extra-files/minimal/object.d
+TEST_OUTPUT:
+---
+fail_compilation/no_Throwable.d(14): Error: Cannot use `throw` statements because `object.Throwable` was not declared
+fail_compilation/no_Throwable.d(19): Error: Cannot use try-catch statements because `object.Throwable` was not declared
+---
+*/
+
+void test()
+{
+ throw new Exception("msg");
+}
+
+void test2()
+{
+ try
+ {
+ test();
+ }
+ catch (Exception e)
+ {
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/no_TypeInfo.d b/gcc/testsuite/gdc.test/fail_compilation/no_TypeInfo.d
new file mode 100644
index 00000000000..328f9b9b28d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/no_TypeInfo.d
@@ -0,0 +1,16 @@
+/*
+DFLAGS:
+REQUIRED_ARGS: -c
+EXTRA_SOURCES: extra-files/minimal/object.d
+TEST_OUTPUT:
+---
+fail_compilation/no_TypeInfo.d(14): Error: `object.TypeInfo` could not be found, but is implicitly used
+---
+*/
+
+void test()
+{
+ int i;
+ auto ti = typeid(i);
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/nogc1.d b/gcc/testsuite/gdc.test/fail_compilation/nogc1.d
index 8a66ac3e0ba..a862e528750 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/nogc1.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/nogc1.d
@@ -1,25 +1,21 @@
// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
/***************** NewExp *******************/
struct S1 { }
struct S2 { this(int); }
struct S3 { this(int) @nogc; }
-struct S4 { new(size_t); }
-struct S5 { @nogc new(size_t); }
/*
TEST_OUTPUT:
---
-fail_compilation/nogc1.d(27): Error: cannot use 'new' in @nogc function 'nogc1.testNew'
-fail_compilation/nogc1.d(29): Error: cannot use 'new' in @nogc function 'nogc1.testNew'
-fail_compilation/nogc1.d(30): Error: cannot use 'new' in @nogc function 'nogc1.testNew'
-fail_compilation/nogc1.d(32): Error: cannot use 'new' in @nogc function 'nogc1.testNew'
-fail_compilation/nogc1.d(33): Error: @nogc function 'nogc1.testNew' cannot call non-@nogc constructor 'nogc1.S2.this'
-fail_compilation/nogc1.d(34): Error: cannot use 'new' in @nogc function 'nogc1.testNew'
-fail_compilation/nogc1.d(35): Error: @nogc function 'nogc1.testNew' cannot call non-@nogc allocator 'nogc1.S4.new'
-fail_compilation/nogc1.d(38): Error: cannot use 'new' in @nogc function 'nogc1.testNew'
+fail_compilation/nogc1.d(23): Error: cannot use `new` in `@nogc` function `nogc1.testNew`
+fail_compilation/nogc1.d(25): Error: cannot use `new` in `@nogc` function `nogc1.testNew`
+fail_compilation/nogc1.d(26): Error: cannot use `new` in `@nogc` function `nogc1.testNew`
+fail_compilation/nogc1.d(28): Error: cannot use `new` in `@nogc` function `nogc1.testNew`
+fail_compilation/nogc1.d(29): Error: `@nogc` function `nogc1.testNew` cannot call non-@nogc constructor `nogc1.S2.this`
+fail_compilation/nogc1.d(30): Error: cannot use `new` in `@nogc` function `nogc1.testNew`
+fail_compilation/nogc1.d(32): Error: cannot use `new` in `@nogc` function `nogc1.testNew`
---
*/
@nogc void testNew()
@@ -32,8 +28,6 @@ fail_compilation/nogc1.d(38): Error: cannot use 'new' in @nogc function 'nogc1.t
S1* ps1 = new S1();
S2* ps2 = new S2(1);
S3* ps3 = new S3(1);
- S4* ps4 = new S4;
- S5* ps5 = new S5; // no error
Object o1 = new Object();
}
@@ -41,13 +35,12 @@ fail_compilation/nogc1.d(38): Error: cannot use 'new' in @nogc function 'nogc1.t
/*
TEST_OUTPUT:
---
-fail_compilation/nogc1.d(55): Error: cannot use 'new' in @nogc function 'nogc1.testNewScope'
-fail_compilation/nogc1.d(57): Error: cannot use 'new' in @nogc function 'nogc1.testNewScope'
-fail_compilation/nogc1.d(58): Error: cannot use 'new' in @nogc function 'nogc1.testNewScope'
-fail_compilation/nogc1.d(60): Error: cannot use 'new' in @nogc function 'nogc1.testNewScope'
-fail_compilation/nogc1.d(61): Error: @nogc function 'nogc1.testNewScope' cannot call non-@nogc constructor 'nogc1.S2.this'
-fail_compilation/nogc1.d(62): Error: cannot use 'new' in @nogc function 'nogc1.testNewScope'
-fail_compilation/nogc1.d(63): Error: @nogc function 'nogc1.testNewScope' cannot call non-@nogc allocator 'nogc1.S4.new'
+fail_compilation/nogc1.d(48): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope`
+fail_compilation/nogc1.d(50): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope`
+fail_compilation/nogc1.d(51): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope`
+fail_compilation/nogc1.d(53): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope`
+fail_compilation/nogc1.d(54): Error: `@nogc` function `nogc1.testNewScope` cannot call non-@nogc constructor `nogc1.S2.this`
+fail_compilation/nogc1.d(55): Error: cannot use `new` in `@nogc` function `nogc1.testNewScope`
---
*/
@nogc void testNewScope()
@@ -60,8 +53,6 @@ fail_compilation/nogc1.d(63): Error: @nogc function 'nogc1.testNewScope' cannot
scope S1* ps1 = new S1();
scope S2* ps2 = new S2(1);
scope S3* ps3 = new S3(1);
- scope S4* ps4 = new S4;
- scope S5* ps5 = new S5; // no error
scope Object o1 = new Object(); // no error
scope o2 = new Object(); // no error
@@ -72,9 +63,12 @@ fail_compilation/nogc1.d(63): Error: @nogc function 'nogc1.testNewScope' cannot
/*
TEST_OUTPUT:
---
-fail_compilation/nogc1.d(82): Error: cannot use 'delete' in @nogc function 'nogc1.testDelete'
-fail_compilation/nogc1.d(83): Error: cannot use 'delete' in @nogc function 'nogc1.testDelete'
-fail_compilation/nogc1.d(84): Error: cannot use 'delete' in @nogc function 'nogc1.testDelete'
+fail_compilation/nogc1.d(76): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+fail_compilation/nogc1.d(76): Error: cannot use `delete` in `@nogc` function `nogc1.testDelete`
+fail_compilation/nogc1.d(77): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+fail_compilation/nogc1.d(77): Error: cannot use `delete` in `@nogc` function `nogc1.testDelete`
+fail_compilation/nogc1.d(78): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+fail_compilation/nogc1.d(78): Error: cannot use `delete` in `@nogc` function `nogc1.testDelete`
---
*/
@nogc void testDelete(int* p, Object o, S1* s)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/nogc2.d b/gcc/testsuite/gdc.test/fail_compilation/nogc2.d
index 1af413ac98c..2a3ea8a99e0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/nogc2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/nogc2.d
@@ -1,19 +1,18 @@
// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
/***************** CatExp *******************/
/*
TEST_OUTPUT:
---
-fail_compilation/nogc2.d(21): Error: cannot use operator ~ in @nogc function 'nogc2.testCat'
-fail_compilation/nogc2.d(22): Error: cannot use operator ~ in @nogc function 'nogc2.testCat'
-fail_compilation/nogc2.d(23): Error: cannot use operator ~ in @nogc function 'nogc2.testCat'
-fail_compilation/nogc2.d(25): Error: cannot use operator ~ in @nogc function 'nogc2.testCat'
-fail_compilation/nogc2.d(26): Error: cannot use operator ~ in @nogc function 'nogc2.testCat'
-fail_compilation/nogc2.d(27): Error: cannot use operator ~ in @nogc function 'nogc2.testCat'
-fail_compilation/nogc2.d(28): Error: cannot use operator ~ in @nogc function 'nogc2.testCat'
-fail_compilation/nogc2.d(29): Error: cannot use operator ~ in @nogc function 'nogc2.testCat'
+fail_compilation/nogc2.d(20): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat`
+fail_compilation/nogc2.d(21): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat`
+fail_compilation/nogc2.d(22): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat`
+fail_compilation/nogc2.d(24): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat`
+fail_compilation/nogc2.d(25): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat`
+fail_compilation/nogc2.d(26): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat`
+fail_compilation/nogc2.d(27): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat`
+fail_compilation/nogc2.d(28): Error: cannot use operator `~` in `@nogc` function `nogc2.testCat`
---
*/
@nogc void testCat(int[] a, string s)
@@ -38,9 +37,9 @@ fail_compilation/nogc2.d(29): Error: cannot use operator ~ in @nogc function 'no
/*
TEST_OUTPUT:
---
-fail_compilation/nogc2.d(48): Error: cannot use operator ~= in @nogc function 'nogc2.testCatAssign'
-fail_compilation/nogc2.d(50): Error: cannot use operator ~= in @nogc function 'nogc2.testCatAssign'
-fail_compilation/nogc2.d(51): Error: cannot use operator ~= in @nogc function 'nogc2.testCatAssign'
+fail_compilation/nogc2.d(47): Error: cannot use operator `~=` in `@nogc` function `nogc2.testCatAssign`
+fail_compilation/nogc2.d(49): Error: cannot use operator `~=` in `@nogc` function `nogc2.testCatAssign`
+fail_compilation/nogc2.d(50): Error: cannot use operator `~=` in `@nogc` function `nogc2.testCatAssign`
---
*/
@nogc void testCatAssign(int[] a, string s)
@@ -58,8 +57,8 @@ fail_compilation/nogc2.d(51): Error: cannot use operator ~= in @nogc function 'n
/*
TEST_OUTPUT:
---
-fail_compilation/nogc2.d(70): Error: array literal in @nogc function 'nogc2.testArray' may cause GC allocation
-fail_compilation/nogc2.d(71): Error: array literal in @nogc function 'nogc2.testArray' may cause GC allocation
+fail_compilation/nogc2.d(69): Error: array literal in `@nogc` function `nogc2.testArray` may cause a GC allocation
+fail_compilation/nogc2.d(70): Error: array literal in `@nogc` function `nogc2.testArray` may cause a GC allocation
---
*/
@nogc void testArray()
@@ -76,8 +75,8 @@ fail_compilation/nogc2.d(71): Error: array literal in @nogc function 'nogc2.test
/*
TEST_OUTPUT:
---
-fail_compilation/nogc2.d(87): Error: associative array literal in @nogc function 'nogc2.testAssocArray' may cause GC allocation
-fail_compilation/nogc2.d(88): Error: associative array literal in @nogc function 'nogc2.testAssocArray' may cause GC allocation
+fail_compilation/nogc2.d(86): Error: associative array literal in `@nogc` function `nogc2.testAssocArray` may cause a GC allocation
+fail_compilation/nogc2.d(87): Error: associative array literal in `@nogc` function `nogc2.testAssocArray` may cause a GC allocation
---
*/
@nogc void testAssocArray()
@@ -93,8 +92,8 @@ fail_compilation/nogc2.d(88): Error: associative array literal in @nogc function
/*
TEST_OUTPUT:
---
-fail_compilation/nogc2.d(102): Error: indexing an associative array in @nogc function 'nogc2.testIndex' may cause GC allocation
-fail_compilation/nogc2.d(103): Error: indexing an associative array in @nogc function 'nogc2.testIndex' may cause GC allocation
+fail_compilation/nogc2.d(101): Error: indexing an associative array in `@nogc` function `nogc2.testIndex` may cause a GC allocation
+fail_compilation/nogc2.d(102): Error: indexing an associative array in `@nogc` function `nogc2.testIndex` may cause a GC allocation
---
*/
@nogc void testIndex(int[int] aa)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/nogc3.d b/gcc/testsuite/gdc.test/fail_compilation/nogc3.d
index 7c9e6d2219e..fdc3cde9784 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/nogc3.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/nogc3.d
@@ -1,14 +1,13 @@
// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
/***************** AssignExp *******************/
/*
TEST_OUTPUT:
---
-fail_compilation/nogc3.d(16): Error: setting 'length' in @nogc function 'nogc3.testArrayLength' may cause GC allocation
-fail_compilation/nogc3.d(17): Error: setting 'length' in @nogc function 'nogc3.testArrayLength' may cause GC allocation
-fail_compilation/nogc3.d(18): Error: setting 'length' in @nogc function 'nogc3.testArrayLength' may cause GC allocation
+fail_compilation/nogc3.d(15): Error: setting `length` in `@nogc` function `nogc3.testArrayLength` may cause a GC allocation
+fail_compilation/nogc3.d(16): Error: setting `length` in `@nogc` function `nogc3.testArrayLength` may cause a GC allocation
+fail_compilation/nogc3.d(17): Error: setting `length` in `@nogc` function `nogc3.testArrayLength` may cause a GC allocation
---
*/
@nogc void testArrayLength(int[] a)
@@ -25,8 +24,8 @@ void barCall();
/*
TEST_OUTPUT:
---
-fail_compilation/nogc3.d(35): Error: @nogc function 'nogc3.testCall' cannot call non-@nogc function pointer 'fp'
-fail_compilation/nogc3.d(36): Error: @nogc function 'nogc3.testCall' cannot call non-@nogc function 'nogc3.barCall'
+fail_compilation/nogc3.d(34): Error: `@nogc` function `nogc3.testCall` cannot call non-@nogc function pointer `fp`
+fail_compilation/nogc3.d(35): Error: `@nogc` function `nogc3.testCall` cannot call non-@nogc function `nogc3.barCall`
---
*/
@nogc void testCall()
@@ -44,10 +43,10 @@ fail_compilation/nogc3.d(36): Error: @nogc function 'nogc3.testCall' cannot call
/*
TEST_OUTPUT:
---
-fail_compilation/nogc3.d(53): Error: function nogc3.testClosure1 is @nogc yet allocates closures with the GC
-fail_compilation/nogc3.d(56): nogc3.testClosure1.bar closes over variable x at fail_compilation/nogc3.d(55)
-fail_compilation/nogc3.d(65): Error: function nogc3.testClosure3 is @nogc yet allocates closures with the GC
-fail_compilation/nogc3.d(68): nogc3.testClosure3.bar closes over variable x at fail_compilation/nogc3.d(67)
+fail_compilation/nogc3.d(52): Error: function `nogc3.testClosure1` is `@nogc` yet allocates closures with the GC
+fail_compilation/nogc3.d(55): nogc3.testClosure1.bar closes over variable x at fail_compilation/nogc3.d(54)
+fail_compilation/nogc3.d(64): Error: function `nogc3.testClosure3` is `@nogc` yet allocates closures with the GC
+fail_compilation/nogc3.d(67): nogc3.testClosure3.bar closes over variable x at fail_compilation/nogc3.d(66)
---
*/
@nogc auto testClosure1()
@@ -74,10 +73,10 @@ fail_compilation/nogc3.d(68): nogc3.testClosure3.bar closes over variable
/*
TEST_OUTPUT:
---
-fail_compilation/nogc3.d(86): Error: array literal in @nogc function 'nogc3.foo13702' may cause GC allocation
-fail_compilation/nogc3.d(87): Error: array literal in @nogc function 'nogc3.foo13702' may cause GC allocation
-fail_compilation/nogc3.d(93): Error: array literal in @nogc function 'nogc3.bar13702' may cause GC allocation
-fail_compilation/nogc3.d(92): Error: array literal in @nogc function 'nogc3.bar13702' may cause GC allocation
+fail_compilation/nogc3.d(85): Error: array literal in `@nogc` function `nogc3.foo13702` may cause a GC allocation
+fail_compilation/nogc3.d(86): Error: array literal in `@nogc` function `nogc3.foo13702` may cause a GC allocation
+fail_compilation/nogc3.d(92): Error: array literal in `@nogc` function `nogc3.bar13702` may cause a GC allocation
+fail_compilation/nogc3.d(91): Error: array literal in `@nogc` function `nogc3.bar13702` may cause a GC allocation
---
*/
int[] foo13702(bool b) @nogc
diff --git a/gcc/testsuite/gdc.test/fail_compilation/noreturn.d b/gcc/testsuite/gdc.test/fail_compilation/noreturn.d
new file mode 100644
index 00000000000..3b340e84c4d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/noreturn.d
@@ -0,0 +1,118 @@
+/*
+REQUIRED_ARGS: -w -o-
+
+TEST_OUTPUT:
+---
+fail_compilation\noreturn.d(38): Error: `"Accessed expression of type `noreturn`"`
+fail_compilation\noreturn.d(42): called from here: `assign()`
+fail_compilation\noreturn.d(49): Error: `"Accessed expression of type `noreturn`"`
+fail_compilation\noreturn.d(49): called from here: `foo(n)`
+fail_compilation\noreturn.d(53): called from here: `calling()`
+fail_compilation\noreturn.d(59): Error: `"Accessed expression of type `noreturn`"`
+fail_compilation\noreturn.d(62): called from here: `nested()`
+fail_compilation\noreturn.d(68): Error: `"Accessed expression of type `noreturn`"`
+fail_compilation\noreturn.d(78): called from here: `casting(0)`
+fail_compilation\noreturn.d(69): Error: `"Accessed expression of type `noreturn`"`
+fail_compilation\noreturn.d(79): called from here: `casting(1)`
+fail_compilation\noreturn.d(72): Error: `"Accessed expression of type `noreturn`"`
+fail_compilation\noreturn.d(80): called from here: `casting(2)`
+---
+
+https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1034.md
+*/
+
+alias noreturn = typeof(*null);
+
+int pass()
+{
+ noreturn n;
+ noreturn m;
+ return 0;
+}
+
+enum forcePass = pass();
+
+int assign()
+{
+ noreturn n;
+ noreturn m = n;
+ return 0;
+}
+
+enum forceAss = assign();
+
+void foo(const noreturn) {}
+
+int calling()
+{
+ noreturn n;
+ foo(n);
+ return 0;
+}
+
+enum forceCall = calling();
+
+int nested()
+{
+ int[4] arr;
+ noreturn n;
+ return arr[n ? n : n];
+}
+
+enum forceNested = nested();
+
+noreturn casting(int i)
+{
+ final switch (i)
+ {
+ case 0: return cast(noreturn) i;
+ case 1: return cast(typeof(assert(0))) cast(double) i;
+ case 2, 3: {
+ noreturn n;
+ return cast() n;
+ }
+ }
+
+}
+
+enum forceCasting0 = casting(0);
+enum forceCasting1 = casting(1);
+enum forceCasting2 = casting(2);
+
+/*
+struct HasNoreturnStruct
+{
+ noreturn n;
+}
+
+int inStruct()
+{
+ HasNoreturnStruct hn;
+ return hn.n;
+}
+
+enum forceInStruct = inStruct();
+
+class HasNoreturnClass
+{
+ noreturn n;
+}
+
+int inClass()
+{
+ HasNoreturnClass hn = new HasNoreturnClass();
+ return hn.n;
+}
+
+enum forceInClass = inClass();
+
+int inClassRef()
+{
+ static void byRef(ref noreturn n) {}
+ HasNoreturnClass hn = new HasNoreturnClass();
+ byRef(hn.n);
+ return 0;
+}
+
+enum forceInClassRef = inClassRef();
+*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/notype.d b/gcc/testsuite/gdc.test/fail_compilation/notype.d
new file mode 100644
index 00000000000..1825968517e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/notype.d
@@ -0,0 +1,31 @@
+struct S(int var = 3) {
+ int a;
+}
+S s;
+
+alias A() = int;
+A a;
+
+enum e() = 5;
+e val;
+
+interface I()
+{
+}
+I i;
+
+template t()
+{
+}
+t tv;
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/notype.d(4): Error: template struct `notype.S(int var = 3)` is used as a type without instantiation; to instantiate it use `S!(arguments)`
+fail_compilation/notype.d(7): Error: template `notype.A()` is used as a type
+fail_compilation/notype.d(10): Error: template `notype.e()` is used as a type
+fail_compilation/notype.d(15): Error: template interface `notype.I()` is used as a type without instantiation; to instantiate it use `I!(arguments)`
+fail_compilation/notype.d(20): Error: template `notype.t()` is used as a type
+---
+*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/objc_class2.d b/gcc/testsuite/gdc.test/fail_compilation/objc_class2.d
new file mode 100644
index 00000000000..9d6658c7d24
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/objc_class2.d
@@ -0,0 +1,15 @@
+// EXTRA_OBJC_SOURCES
+/*
+TEST_OUTPUT:
+---
+fail_compilation/objc_class2.d(14): Error: function `objc_class2.A.test` number of colons in Objective-C selector must match number of parameters
+---
+*/
+
+import core.attribute : selector;
+
+extern (Objective-C)
+extern class A
+{
+ void test(int a, int b, int c) @selector("test:"); // non-matching number of colon
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/objc_class3.d b/gcc/testsuite/gdc.test/fail_compilation/objc_class3.d
new file mode 100644
index 00000000000..f76443d0045
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/objc_class3.d
@@ -0,0 +1,22 @@
+// EXTRA_OBJC_SOURCES
+/*
+TEST_OUTPUT:
+---
+fail_compilation/objc_class3.d(15): Error: function `objc_class3.A.test!int.test` template cannot have an Objective-C selector attached
+fail_compilation/objc_class3.d(21): Error: template instance `objc_class3.A.test!int` error instantiating
+---
+*/
+
+import core.attribute : selector;
+
+extern (Objective-C)
+extern class A
+{
+ void test(T)(T a) @selector("test:"); // selector defined for template
+}
+
+void test()
+{
+ A a;
+ a.test(3);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/objc_non_objc_base.d b/gcc/testsuite/gdc.test/fail_compilation/objc_non_objc_base.d
new file mode 100644
index 00000000000..6e2b0782bea
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/objc_non_objc_base.d
@@ -0,0 +1,12 @@
+// EXTRA_OBJC_SOURCES
+/*
+TEST_OUTPUT:
+---
+fail_compilation/objc_non_objc_base.d(12): Error: class `objc_non_objc_base.A` base class for an Objective-C class must be `extern (Objective-C)`
+---
+*/
+
+interface Base {}
+
+extern (Objective-C)
+class A : Base {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/parse12967a.d b/gcc/testsuite/gdc.test/fail_compilation/parse12967a.d
index 90bccb783ac..5282232557d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/parse12967a.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/parse12967a.d
@@ -1,14 +1,14 @@
/*
TEST_OUTPUT:
---
-fail_compilation/parse12967a.d(14): Error: function parse12967a.pre_i1 without 'this' cannot be immutable
-fail_compilation/parse12967a.d(15): Error: function parse12967a.pre_i2 without 'this' cannot be immutable
-fail_compilation/parse12967a.d(16): Error: function parse12967a.pre_c1 without 'this' cannot be const
-fail_compilation/parse12967a.d(17): Error: function parse12967a.pre_c2 without 'this' cannot be const
-fail_compilation/parse12967a.d(18): Error: function parse12967a.pre_w1 without 'this' cannot be inout
-fail_compilation/parse12967a.d(19): Error: function parse12967a.pre_w2 without 'this' cannot be inout
-fail_compilation/parse12967a.d(20): Error: function parse12967a.pre_s1 without 'this' cannot be shared
-fail_compilation/parse12967a.d(21): Error: function parse12967a.pre_s2 without 'this' cannot be shared
+fail_compilation/parse12967a.d(14): Error: function `parse12967a.pre_i1` without `this` cannot be `immutable`
+fail_compilation/parse12967a.d(15): Error: function `parse12967a.pre_i2` without `this` cannot be `immutable`
+fail_compilation/parse12967a.d(16): Error: function `parse12967a.pre_c1` without `this` cannot be `const`
+fail_compilation/parse12967a.d(17): Error: function `parse12967a.pre_c2` without `this` cannot be `const`
+fail_compilation/parse12967a.d(18): Error: function `parse12967a.pre_w1` without `this` cannot be `inout`
+fail_compilation/parse12967a.d(19): Error: function `parse12967a.pre_w2` without `this` cannot be `inout`
+fail_compilation/parse12967a.d(20): Error: function `parse12967a.pre_s1` without `this` cannot be `shared`
+fail_compilation/parse12967a.d(21): Error: function `parse12967a.pre_s2` without `this` cannot be `shared`
---
*/
immutable pre_i1() {}
@@ -23,14 +23,14 @@ shared void pre_s2() {}
/*
TEST_OUTPUT:
---
-fail_compilation/parse12967a.d(36): Error: function parse12967a.post_i1 without 'this' cannot be immutable
-fail_compilation/parse12967a.d(37): Error: function parse12967a.post_i2 without 'this' cannot be immutable
-fail_compilation/parse12967a.d(38): Error: function parse12967a.post_c1 without 'this' cannot be const
-fail_compilation/parse12967a.d(39): Error: function parse12967a.post_c2 without 'this' cannot be const
-fail_compilation/parse12967a.d(40): Error: function parse12967a.post_w1 without 'this' cannot be inout
-fail_compilation/parse12967a.d(41): Error: function parse12967a.post_w2 without 'this' cannot be inout
-fail_compilation/parse12967a.d(42): Error: function parse12967a.post_s1 without 'this' cannot be shared
-fail_compilation/parse12967a.d(43): Error: function parse12967a.post_s2 without 'this' cannot be shared
+fail_compilation/parse12967a.d(36): Error: function `parse12967a.post_i1` without `this` cannot be `immutable`
+fail_compilation/parse12967a.d(37): Error: function `parse12967a.post_i2` without `this` cannot be `immutable`
+fail_compilation/parse12967a.d(38): Error: function `parse12967a.post_c1` without `this` cannot be `const`
+fail_compilation/parse12967a.d(39): Error: function `parse12967a.post_c2` without `this` cannot be `const`
+fail_compilation/parse12967a.d(40): Error: function `parse12967a.post_w1` without `this` cannot be `inout`
+fail_compilation/parse12967a.d(41): Error: function `parse12967a.post_w2` without `this` cannot be `inout`
+fail_compilation/parse12967a.d(42): Error: function `parse12967a.post_s1` without `this` cannot be `shared`
+fail_compilation/parse12967a.d(43): Error: function `parse12967a.post_s2` without `this` cannot be `shared`
---
*/
auto post_i1() immutable {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/parse12967b.d b/gcc/testsuite/gdc.test/fail_compilation/parse12967b.d
index 60d9d09948b..064f297ec79 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/parse12967b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/parse12967b.d
@@ -1,41 +1,45 @@
/*
TEST_OUTPUT:
---
-fail_compilation/parse12967b.d(24): Error: function parse12967b.C.pre_c without 'this' cannot be const
-fail_compilation/parse12967b.d(25): Error: function parse12967b.C.pre_c without 'this' cannot be const
-fail_compilation/parse12967b.d(26): Error: function parse12967b.C.pre_i without 'this' cannot be immutable
-fail_compilation/parse12967b.d(27): Error: function parse12967b.C.pre_i without 'this' cannot be immutable
-fail_compilation/parse12967b.d(28): Error: function parse12967b.C.pre_w without 'this' cannot be inout
-fail_compilation/parse12967b.d(29): Error: function parse12967b.C.pre_w without 'this' cannot be inout
-fail_compilation/parse12967b.d(30): Error: function parse12967b.C.pre_s without 'this' cannot be shared
-fail_compilation/parse12967b.d(31): Error: function parse12967b.C.pre_s without 'this' cannot be shared
-fail_compilation/parse12967b.d(33): Error: function parse12967b.C.post_c without 'this' cannot be const
-fail_compilation/parse12967b.d(34): Error: function parse12967b.C.post_c without 'this' cannot be const
-fail_compilation/parse12967b.d(35): Error: function parse12967b.C.post_i without 'this' cannot be immutable
-fail_compilation/parse12967b.d(36): Error: function parse12967b.C.post_i without 'this' cannot be immutable
-fail_compilation/parse12967b.d(37): Error: function parse12967b.C.post_w without 'this' cannot be inout
-fail_compilation/parse12967b.d(38): Error: function parse12967b.C.post_w without 'this' cannot be inout
-fail_compilation/parse12967b.d(39): Error: function parse12967b.C.post_s without 'this' cannot be shared
-fail_compilation/parse12967b.d(40): Error: function parse12967b.C.post_s without 'this' cannot be shared
+fail_compilation/parse12967b.d(24): Error: function `parse12967b.C.pre_c` without `this` cannot be `const`
+fail_compilation/parse12967b.d(25): Error: function `parse12967b.C.pre_i` without `this` cannot be `immutable`
+fail_compilation/parse12967b.d(26): Error: function `parse12967b.C.pre_w` without `this` cannot be `inout`
+fail_compilation/parse12967b.d(27): Error: function `parse12967b.C.pre_s` without `this` cannot be `shared`
+fail_compilation/parse12967b.d(29): Error: function `parse12967b.C.post_c` without `this` cannot be `const`
+fail_compilation/parse12967b.d(30): Error: function `parse12967b.C.post_i` without `this` cannot be `immutable`
+fail_compilation/parse12967b.d(31): Error: function `parse12967b.C.post_w` without `this` cannot be `inout`
+fail_compilation/parse12967b.d(32): Error: function `parse12967b.C.post_s` without `this` cannot be `shared`
+fail_compilation/parse12967b.d(37): Error: function `parse12967b.D.pre_c` without `this` cannot be `const`
+fail_compilation/parse12967b.d(38): Error: function `parse12967b.D.pre_i` without `this` cannot be `immutable`
+fail_compilation/parse12967b.d(39): Error: function `parse12967b.D.pre_w` without `this` cannot be `inout`
+fail_compilation/parse12967b.d(40): Error: function `parse12967b.D.pre_s` without `this` cannot be `shared`
+fail_compilation/parse12967b.d(41): Error: function `parse12967b.D.post_c` without `this` cannot be `const`
+fail_compilation/parse12967b.d(42): Error: function `parse12967b.D.post_i` without `this` cannot be `immutable`
+fail_compilation/parse12967b.d(43): Error: function `parse12967b.D.post_w` without `this` cannot be `inout`
+fail_compilation/parse12967b.d(44): Error: function `parse12967b.D.post_s` without `this` cannot be `shared`
---
*/
class C
{
const static pre_c() {}
- const static void pre_c() {}
immutable static pre_i() {}
- immutable static void pre_i() {}
inout static pre_w() {}
- inout static void pre_w() {}
shared static pre_s() {}
- shared static void pre_s() {}
static post_c() const {}
- static void post_c() const {}
static post_i() immutable {}
- static void post_i() immutable {}
static post_w() inout {}
- static void post_w() inout {}
static post_s() shared {}
+}
+
+class D
+{
+ const static void pre_c() {}
+ immutable static void pre_i() {}
+ inout static void pre_w() {}
+ shared static void pre_s() {}
+ static void post_c() const {}
+ static void post_i() immutable {}
+ static void post_w() inout {}
static void post_s() shared {}
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/parse19277.d b/gcc/testsuite/gdc.test/fail_compilation/parse19277.d
new file mode 100644
index 00000000000..19dd7470a80
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/parse19277.d
@@ -0,0 +1,20 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/parse19277.d(13): Deprecation: storage class `ref` has no effect in type aliases
+fail_compilation/parse19277.d(14): Deprecation: storage class `__gshared` has no effect in type aliases
+fail_compilation/parse19277.d(15): Deprecation: storage class `static` has no effect in type aliases
+fail_compilation/parse19277.d(16): Deprecation: storage class `extern` has no effect in type aliases
+fail_compilation/parse19277.d(17): Deprecation: storage class `scope` has no effect in type aliases
+---
+*/
+
+alias T = ref int;
+alias U = __gshared int;
+alias V = static int;
+alias W = extern int;
+alias Dg = scope void delegate();
+
+alias F = ref pure nothrow @nogc @safe @live int function();
+alias G = ref pure nothrow @nogc @system @live int delegate();
diff --git a/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d b/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d
index 5e9c4461626..936769a59d0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d
@@ -43,8 +43,8 @@ extern(C++, foo) extern(C++, bar) void f8() {} // OK
/*
TEST_OUTPUT:
---
-fail_compilation/parseStc2.d(50): Error: redundant protection attribute `public`
-fail_compilation/parseStc2.d(51): Error: conflicting protection attribute `public` and `private`
+fail_compilation/parseStc2.d(50): Error: redundant visibility attribute `public`
+fail_compilation/parseStc2.d(51): Error: conflicting visibility attribute `public` and `private`
---
*/
public public void f9() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/parseStc3.d b/gcc/testsuite/gdc.test/fail_compilation/parseStc3.d
index 1417f9432d2..d4c0aa0fedb 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/parseStc3.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/parseStc3.d
@@ -1,10 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/parseStc3.d(10): Deprecation: redundant attribute `pure`
-fail_compilation/parseStc3.d(11): Deprecation: redundant attribute `nothrow`
-fail_compilation/parseStc3.d(12): Deprecation: redundant attribute `@nogc`
-fail_compilation/parseStc3.d(13): Deprecation: redundant attribute `@property`
+fail_compilation/parseStc3.d(10): Error: redundant attribute `pure`
+fail_compilation/parseStc3.d(11): Error: redundant attribute `nothrow`
+fail_compilation/parseStc3.d(12): Error: redundant attribute `@nogc`
+fail_compilation/parseStc3.d(13): Error: redundant attribute `@property`
---
*/
pure void f1() pure {}
@@ -16,9 +16,9 @@ nothrow void f2() nothrow {}
/*
TEST_OUTPUT:
---
-fail_compilation/parseStc3.d(24): Deprecation: redundant attribute `@safe`
-fail_compilation/parseStc3.d(25): Deprecation: redundant attribute `@system`
-fail_compilation/parseStc3.d(26): Deprecation: redundant attribute `@trusted`
+fail_compilation/parseStc3.d(24): Error: redundant attribute `@safe`
+fail_compilation/parseStc3.d(25): Error: redundant attribute `@system`
+fail_compilation/parseStc3.d(26): Error: redundant attribute `@trusted`
---
*/
@safe void f6() @safe {}
@@ -49,11 +49,11 @@ TEST_OUTPUT:
fail_compilation/parseStc3.d(59): Error: conflicting attribute `@system`
fail_compilation/parseStc3.d(59): Error: conflicting attribute `@trusted`
fail_compilation/parseStc3.d(60): Error: conflicting attribute `@system`
-fail_compilation/parseStc3.d(60): Deprecation: redundant attribute `@system`
+fail_compilation/parseStc3.d(60): Error: redundant attribute `@system`
fail_compilation/parseStc3.d(61): Error: conflicting attribute `@safe`
-fail_compilation/parseStc3.d(61): Deprecation: redundant attribute `@system`
+fail_compilation/parseStc3.d(61): Error: redundant attribute `@system`
fail_compilation/parseStc3.d(62): Error: conflicting attribute `@safe`
-fail_compilation/parseStc3.d(62): Deprecation: redundant attribute `@trusted`
+fail_compilation/parseStc3.d(62): Error: redundant attribute `@trusted`
---
*/
@safe @system void f15() @trusted {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/parseStc4.d b/gcc/testsuite/gdc.test/fail_compilation/parseStc4.d
index fee3e085cdb..4bd3a0497aa 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/parseStc4.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/parseStc4.d
@@ -2,11 +2,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/parseStc4.d(14): Deprecation: redundant attribute `pure`
-fail_compilation/parseStc4.d(14): Deprecation: redundant attribute `nothrow`
+fail_compilation/parseStc4.d(14): Error: redundant attribute `pure`
+fail_compilation/parseStc4.d(14): Error: redundant attribute `nothrow`
fail_compilation/parseStc4.d(14): Error: conflicting attribute `@system`
-fail_compilation/parseStc4.d(14): Deprecation: redundant attribute `@nogc`
-fail_compilation/parseStc4.d(14): Deprecation: redundant attribute `@property`
+fail_compilation/parseStc4.d(14): Error: redundant attribute `@nogc`
+fail_compilation/parseStc4.d(14): Error: redundant attribute `@property`
---
*/
pure nothrow @safe @nogc @property
@@ -19,13 +19,14 @@ pure nothrow @system @nogc @property
/*
TEST_OUTPUT:
---
-fail_compilation/parseStc4.d(34): Deprecation: redundant attribute `const`
-fail_compilation/parseStc4.d(35): Deprecation: redundant attribute `const`
-fail_compilation/parseStc4.d(36): Deprecation: redundant attribute `const`
-fail_compilation/parseStc4.d(38): Deprecation: redundant attribute `pure`
-fail_compilation/parseStc4.d(39): Deprecation: redundant attribute `@safe`
-fail_compilation/parseStc4.d(40): Deprecation: redundant attribute `nothrow`
-fail_compilation/parseStc4.d(41): Error: conflicting attribute `@trusted`
+fail_compilation/parseStc4.d(35): Error: redundant attribute `const`
+fail_compilation/parseStc4.d(36): Error: redundant attribute `const`
+fail_compilation/parseStc4.d(36): Deprecation: `const` postblit is deprecated. Please use an unqualified postblit.
+fail_compilation/parseStc4.d(37): Error: redundant attribute `const`
+fail_compilation/parseStc4.d(39): Error: redundant attribute `pure`
+fail_compilation/parseStc4.d(40): Error: redundant attribute `@safe`
+fail_compilation/parseStc4.d(41): Error: redundant attribute `nothrow`
+fail_compilation/parseStc4.d(42): Error: conflicting attribute `@trusted`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/parseStc5.d b/gcc/testsuite/gdc.test/fail_compilation/parseStc5.d
index a274c6e3bbc..8c3718b6de6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/parseStc5.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/parseStc5.d
@@ -1,9 +1,8 @@
-// REQUIRED_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/parseStc5.d(11): Error: constructor cannot be static
-fail_compilation/parseStc5.d(12): Error: postblit cannot be static
+fail_compilation/parseStc5.d(10): Error: constructor cannot be static
+fail_compilation/parseStc5.d(11): Error: postblit cannot be `static`
---
*/
class C1
@@ -15,12 +14,12 @@ class C1
/*
TEST_OUTPUT:
---
+fail_compilation/parseStc5.d(27): Error: use `shared static this()` to declare a shared static constructor
fail_compilation/parseStc5.d(28): Error: use `shared static this()` to declare a shared static constructor
-fail_compilation/parseStc5.d(29): Error: use `shared static this()` to declare a shared static constructor
-fail_compilation/parseStc5.d(31): Error: use `shared static this()` to declare a shared static constructor
+fail_compilation/parseStc5.d(30): Error: use `shared static this()` to declare a shared static constructor
+fail_compilation/parseStc5.d(32): Error: use `shared static ~this()` to declare a shared static destructor
fail_compilation/parseStc5.d(33): Error: use `shared static ~this()` to declare a shared static destructor
-fail_compilation/parseStc5.d(34): Error: use `shared static ~this()` to declare a shared static destructor
-fail_compilation/parseStc5.d(36): Error: use `shared static ~this()` to declare a shared static destructor
+fail_compilation/parseStc5.d(35): Error: use `shared static ~this()` to declare a shared static destructor
---
*/
class C2 // wrong combinations of `shared`, `static`, and `~?this()`
@@ -39,8 +38,8 @@ class C2 // wrong combinations of `shared`, `static`, and `~?this()`
/*
TEST_OUTPUT:
---
-fail_compilation/parseStc5.d(48): Error: use `static this()` to declare a static constructor
-fail_compilation/parseStc5.d(49): Error: use `static ~this()` to declare a static destructor
+fail_compilation/parseStc5.d(47): Error: use `static this()` to declare a static constructor
+fail_compilation/parseStc5.d(48): Error: use `static ~this()` to declare a static destructor
---
*/
class C3 // wrong combinations of `static` and `~?this()`
@@ -52,11 +51,11 @@ class C3 // wrong combinations of `static` and `~?this()`
/*
TEST_OUTPUT:
---
+fail_compilation/parseStc5.d(63): Error: redundant attribute `shared`
fail_compilation/parseStc5.d(64): Error: redundant attribute `shared`
-fail_compilation/parseStc5.d(65): Error: redundant attribute `shared`
-fail_compilation/parseStc5.d(67): Error: redundant attribute `static`
+fail_compilation/parseStc5.d(66): Error: redundant attribute `static`
+fail_compilation/parseStc5.d(68): Error: redundant attribute `static shared`
fail_compilation/parseStc5.d(69): Error: redundant attribute `static shared`
-fail_compilation/parseStc5.d(70): Error: redundant attribute `static shared`
---
*/
class C4 // redundancy of `shared` and/or `static`
@@ -73,10 +72,10 @@ class C4 // redundancy of `shared` and/or `static`
/*
TEST_OUTPUT:
---
-fail_compilation/parseStc5.d(84): Error: static constructor cannot be `const`
-fail_compilation/parseStc5.d(85): Error: static destructor cannot be `const`
-fail_compilation/parseStc5.d(86): Error: shared static constructor cannot be `const`
-fail_compilation/parseStc5.d(87): Error: shared static destructor cannot be `const`
+fail_compilation/parseStc5.d(83): Error: static constructor cannot be `const`
+fail_compilation/parseStc5.d(84): Error: static destructor cannot be `const`
+fail_compilation/parseStc5.d(85): Error: shared static constructor cannot be `const`
+fail_compilation/parseStc5.d(86): Error: shared static destructor cannot be `const`
---
*/
class C5 // wrong MemberFunctionAttributes on `shared? static (con|de)structor`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/pragmainline.d b/gcc/testsuite/gdc.test/fail_compilation/pragmainline.d
index 030f19a39a9..6d935a03ce0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/pragmainline.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/pragmainline.d
@@ -1,11 +1,10 @@
/*
TEST_OUTPUT:
---
-fail_compilation/pragmainline.d(9): Error: pragma inline one boolean expression expected for `pragma(inline)`, not 3
-fail_compilation/pragmainline.d(10): Error: pragma inline pragma(`inline`, `true` or `false`) expected, not `"string"`
+fail_compilation/pragmainline.d(8): Error: pragma `inline` one boolean expression expected for `pragma(inline)`, not 3
---
*/
pragma(inline, 1,2,3) void bar();
-pragma(inline, "string") void baz();
+pragma(inline, "string") void baz(); // works now
diff --git a/gcc/testsuite/gdc.test/fail_compilation/pragmas.d b/gcc/testsuite/gdc.test/fail_compilation/pragmas.d
index 33a89218172..25ab15123bf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/pragmas.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/pragmas.d
@@ -1,16 +1,10 @@
-/*
-REQUIRED_ARGS:
-PERMUTE_ARGS:
-*/
-
/************************************************************/
/*
TEST_OUTPUT:
---
-fail_compilation/pragmas.d(103): Error: boolean expression expected for pragma(inline)
-fail_compilation/pragmas.d(108): Error: boolean expression expected for pragma(inline)
-fail_compilation/pragmas.d(113): Error: pragma(inline, true or false) expected, not `"string"`
+fail_compilation/pragmas.d(103): Error: boolean expression expected for `pragma(inline)`
+fail_compilation/pragmas.d(108): Error: boolean expression expected for `pragma(inline)`
fail_compilation/pragmas.d(118): Error: unrecognized `pragma(unrecognized)`
---
*/
@@ -29,7 +23,7 @@ void test2()
void test3()
{
- pragma(inline, "string");
+ pragma(inline, "string"); // works now
}
void test4()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/previewin.d b/gcc/testsuite/gdc.test/fail_compilation/previewin.d
new file mode 100644
index 00000000000..b3beaf4c9d8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/previewin.d
@@ -0,0 +1,42 @@
+/*
+REQUIRED_ARGS: -preview=in -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/previewin.d(4): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)`
+fail_compilation/previewin.d(4): cannot pass argument `__lambda1` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
+fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(const(real) x) pure nothrow @nogc @safe)`
+fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
+fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref const(real) x) pure nothrow @nogc @safe)`
+fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f`
+fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to non-scope `myGlobal`
+fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to non-scope `myGlobal`
+fail_compilation/previewin.d(17): Error: scope variable `arg` may not be returned
+fail_compilation/previewin.d(18): Error: scope variable `arg` assigned to `escape` with longer lifetime
+fail_compilation/previewin.d(22): Error: returning `arg` escapes a reference to parameter `arg`
+fail_compilation/previewin.d(22): perhaps annotate the parameter with `return`
+---
+ */
+
+#line 1
+void main ()
+{
+ // No covariance without explicit `in`
+ takeFunction((real x) {});
+ takeFunction((const scope real x) {});
+ takeFunction((const scope ref real x) {});
+
+ tryEscape("Hello World"); // Yes by `tryEscape` is NG
+}
+
+void takeFunction(void function(in real) f);
+
+// Make sure things cannot be escaped (`scope` is applied)
+const(char)[] myGlobal;
+void tryEscape(in char[] arg) @safe { myGlobal = arg; }
+void tryEscape2(scope const char[] arg) @safe { myGlobal = arg; }
+const(char)[] tryEscape3(in char[] arg) @safe { return arg; }
+void tryEscape4(in char[] arg, ref const(char)[] escape) @safe { escape = arg; }
+// Okay: value type
+ulong[8] tryEscape5(in ulong[8] arg) @safe { return arg; }
+// NG: Ref
+ref const(ulong[8]) tryEscape6(in ulong[8] arg) @safe { return arg; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/protattr1.d b/gcc/testsuite/gdc.test/fail_compilation/protattr1.d
index dbd2efa43a2..f5976ff2aba 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/protattr1.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/protattr1.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: protection/subpkg/test1.d
TEST_OUTPUT:
---
-fail_compilation/protection/subpkg/test1.d(3): Error: protection attribute 'package(undefined)' does not bind to one of ancestor packages of module `protection.subpkg.test1`
+fail_compilation/protection/subpkg/test1.d(3): Error: visibility attribute `package(undefined)` does not bind to one of ancestor packages of module `protection.subpkg.test1`
---
*/
import protection.subpkg.test1;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/protattr2.d b/gcc/testsuite/gdc.test/fail_compilation/protattr2.d
index cc36cc00afb..7b6704f231d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/protattr2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/protattr2.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: protection/subpkg/test2.d
TEST_OUTPUT:
---
-fail_compilation/protection/subpkg/test2.d(3): Error: protection attribute 'package(protection.subpkg2)' does not bind to one of ancestor packages of module `protection.subpkg.test2`
+fail_compilation/protection/subpkg/test2.d(3): Error: visibility attribute `package(protection.subpkg2)` does not bind to one of ancestor packages of module `protection.subpkg.test2`
---
*/
import protection.subpkg.test2;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/protattr3.d b/gcc/testsuite/gdc.test/fail_compilation/protattr3.d
index 8f40b2e2c0e..acfc9f9c163 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/protattr3.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/protattr3.d
@@ -1,4 +1,5 @@
/*
+EXTRA_FILES: protection/subpkg/test3.d
TEST_OUTPUT:
---
fail_compilation/protection/subpkg/test3.d(3): Error: `protection package` expected as dot-separated identifiers, got `123`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/pull12941.d b/gcc/testsuite/gdc.test/fail_compilation/pull12941.d
new file mode 100644
index 00000000000..b0208686fe1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/pull12941.d
@@ -0,0 +1,31 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/pull12941.d(110): Error: `pull12941.foo` called with argument types `(int*)` matches both:
+fail_compilation/pull12941.d(101): `pull12941.foo(ref return scope int* p)`
+and:
+fail_compilation/pull12941.d(102): `pull12941.foo(out return scope int* p)`
+fail_compilation/pull12941.d(111): Error: function `pull12941.bar(return scope int* p)` is not callable using argument types `(int)`
+fail_compilation/pull12941.d(111): cannot pass argument `1` of type `int` to parameter `return scope int* p`
+fail_compilation/pull12941.d(112): Error: function `pull12941.abc(return ref int* p)` is not callable using argument types `(int)`
+fail_compilation/pull12941.d(112): cannot pass rvalue argument `1` of type `int` to parameter `return ref int* p`
+---
+ */
+
+/*********************************/
+// Tests for https://github.com/dlang/dmd/pull/12941
+
+#line 100
+
+int* foo(ref scope return int* p);
+int* foo(out scope return int* p);
+
+int* bar(scope return int* p);
+int* abc(ref return int* p);
+
+void test()
+{
+ int* p;
+ foo(p);
+ bar(1);
+ abc(1);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/reserved_version.d b/gcc/testsuite/gdc.test/fail_compilation/reserved_version.d
index 1fe47282959..034fa541234 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/reserved_version.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/reserved_version.d
@@ -13,95 +13,106 @@ fail_compilation/reserved_version.d(112): Error: version identifier `Win32` is r
fail_compilation/reserved_version.d(113): Error: version identifier `Win64` is reserved and cannot be set
fail_compilation/reserved_version.d(114): Error: version identifier `linux` is reserved and cannot be set
fail_compilation/reserved_version.d(115): Error: version identifier `OSX` is reserved and cannot be set
-fail_compilation/reserved_version.d(116): Error: version identifier `FreeBSD` is reserved and cannot be set
-fail_compilation/reserved_version.d(117): Error: version identifier `OpenBSD` is reserved and cannot be set
-fail_compilation/reserved_version.d(118): Error: version identifier `NetBSD` is reserved and cannot be set
-fail_compilation/reserved_version.d(119): Error: version identifier `DragonFlyBSD` is reserved and cannot be set
-fail_compilation/reserved_version.d(120): Error: version identifier `BSD` is reserved and cannot be set
-fail_compilation/reserved_version.d(121): Error: version identifier `Solaris` is reserved and cannot be set
-fail_compilation/reserved_version.d(122): Error: version identifier `Posix` is reserved and cannot be set
-fail_compilation/reserved_version.d(123): Error: version identifier `AIX` is reserved and cannot be set
-fail_compilation/reserved_version.d(124): Error: version identifier `Haiku` is reserved and cannot be set
-fail_compilation/reserved_version.d(125): Error: version identifier `SkyOS` is reserved and cannot be set
-fail_compilation/reserved_version.d(126): Error: version identifier `SysV3` is reserved and cannot be set
-fail_compilation/reserved_version.d(127): Error: version identifier `SysV4` is reserved and cannot be set
-fail_compilation/reserved_version.d(128): Error: version identifier `Hurd` is reserved and cannot be set
-fail_compilation/reserved_version.d(129): Error: version identifier `Android` is reserved and cannot be set
-fail_compilation/reserved_version.d(130): Error: version identifier `PlayStation` is reserved and cannot be set
-fail_compilation/reserved_version.d(131): Error: version identifier `PlayStation4` is reserved and cannot be set
-fail_compilation/reserved_version.d(132): Error: version identifier `Cygwin` is reserved and cannot be set
-fail_compilation/reserved_version.d(133): Error: version identifier `MinGW` is reserved and cannot be set
-fail_compilation/reserved_version.d(134): Error: version identifier `FreeStanding` is reserved and cannot be set
-fail_compilation/reserved_version.d(135): Error: version identifier `X86` is reserved and cannot be set
-fail_compilation/reserved_version.d(136): Error: version identifier `X86_64` is reserved and cannot be set
-fail_compilation/reserved_version.d(137): Error: version identifier `ARM` is reserved and cannot be set
-fail_compilation/reserved_version.d(138): Error: version identifier `ARM_Thumb` is reserved and cannot be set
-fail_compilation/reserved_version.d(139): Error: version identifier `ARM_SoftFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(140): Error: version identifier `ARM_SoftFP` is reserved and cannot be set
-fail_compilation/reserved_version.d(141): Error: version identifier `ARM_HardFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(142): Error: version identifier `AArch64` is reserved and cannot be set
-fail_compilation/reserved_version.d(143): Error: version identifier `Epiphany` is reserved and cannot be set
-fail_compilation/reserved_version.d(144): Error: version identifier `PPC` is reserved and cannot be set
-fail_compilation/reserved_version.d(145): Error: version identifier `PPC_SoftFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(146): Error: version identifier `PPC_HardFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(147): Error: version identifier `PPC64` is reserved and cannot be set
-fail_compilation/reserved_version.d(148): Error: version identifier `IA64` is reserved and cannot be set
-fail_compilation/reserved_version.d(149): Error: version identifier `MIPS32` is reserved and cannot be set
-fail_compilation/reserved_version.d(150): Error: version identifier `MIPS64` is reserved and cannot be set
-fail_compilation/reserved_version.d(151): Error: version identifier `MIPS_O32` is reserved and cannot be set
-fail_compilation/reserved_version.d(152): Error: version identifier `MIPS_N32` is reserved and cannot be set
-fail_compilation/reserved_version.d(153): Error: version identifier `MIPS_O64` is reserved and cannot be set
-fail_compilation/reserved_version.d(154): Error: version identifier `MIPS_N64` is reserved and cannot be set
-fail_compilation/reserved_version.d(155): Error: version identifier `MIPS_EABI` is reserved and cannot be set
-fail_compilation/reserved_version.d(156): Error: version identifier `MIPS_SoftFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(157): Error: version identifier `MIPS_HardFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(158): Error: version identifier `NVPTX` is reserved and cannot be set
-fail_compilation/reserved_version.d(159): Error: version identifier `NVPTX64` is reserved and cannot be set
-fail_compilation/reserved_version.d(160): Error: version identifier `RISCV32` is reserved and cannot be set
-fail_compilation/reserved_version.d(161): Error: version identifier `RISCV64` is reserved and cannot be set
-fail_compilation/reserved_version.d(162): Error: version identifier `SPARC` is reserved and cannot be set
-fail_compilation/reserved_version.d(163): Error: version identifier `SPARC_V8Plus` is reserved and cannot be set
-fail_compilation/reserved_version.d(164): Error: version identifier `SPARC_SoftFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(165): Error: version identifier `SPARC_HardFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(166): Error: version identifier `SPARC64` is reserved and cannot be set
-fail_compilation/reserved_version.d(167): Error: version identifier `S390` is reserved and cannot be set
-fail_compilation/reserved_version.d(168): Error: version identifier `S390X` is reserved and cannot be set
-fail_compilation/reserved_version.d(169): Error: version identifier `SystemZ` is reserved and cannot be set
-fail_compilation/reserved_version.d(170): Error: version identifier `HPPA` is reserved and cannot be set
-fail_compilation/reserved_version.d(171): Error: version identifier `HPPA64` is reserved and cannot be set
-fail_compilation/reserved_version.d(172): Error: version identifier `SH` is reserved and cannot be set
-fail_compilation/reserved_version.d(173): Error: version identifier `Alpha` is reserved and cannot be set
-fail_compilation/reserved_version.d(174): Error: version identifier `Alpha_SoftFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(175): Error: version identifier `Alpha_HardFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(176): Error: version identifier `LittleEndian` is reserved and cannot be set
-fail_compilation/reserved_version.d(177): Error: version identifier `BigEndian` is reserved and cannot be set
-fail_compilation/reserved_version.d(178): Error: version identifier `ELFv1` is reserved and cannot be set
-fail_compilation/reserved_version.d(179): Error: version identifier `ELFv2` is reserved and cannot be set
-fail_compilation/reserved_version.d(180): Error: version identifier `CRuntime_Bionic` is reserved and cannot be set
-fail_compilation/reserved_version.d(181): Error: version identifier `CRuntime_DigitalMars` is reserved and cannot be set
-fail_compilation/reserved_version.d(182): Error: version identifier `CRuntime_Glibc` is reserved and cannot be set
-fail_compilation/reserved_version.d(183): Error: version identifier `CRuntime_Microsoft` is reserved and cannot be set
-fail_compilation/reserved_version.d(184): Error: version identifier `CRuntime_Musl` is reserved and cannot be set
-fail_compilation/reserved_version.d(185): Error: version identifier `CRuntime_UClibc` is reserved and cannot be set
-fail_compilation/reserved_version.d(186): Error: version identifier `D_Coverage` is reserved and cannot be set
-fail_compilation/reserved_version.d(187): Error: version identifier `D_Ddoc` is reserved and cannot be set
-fail_compilation/reserved_version.d(188): Error: version identifier `D_InlineAsm_X86` is reserved and cannot be set
-fail_compilation/reserved_version.d(189): Error: version identifier `D_InlineAsm_X86_64` is reserved and cannot be set
-fail_compilation/reserved_version.d(190): Error: version identifier `D_LP64` is reserved and cannot be set
-fail_compilation/reserved_version.d(191): Error: version identifier `D_X32` is reserved and cannot be set
-fail_compilation/reserved_version.d(192): Error: version identifier `D_HardFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(193): Error: version identifier `D_SoftFloat` is reserved and cannot be set
-fail_compilation/reserved_version.d(194): Error: version identifier `D_PIC` is reserved and cannot be set
-fail_compilation/reserved_version.d(195): Error: version identifier `D_SIMD` is reserved and cannot be set
-fail_compilation/reserved_version.d(196): Error: version identifier `D_Version2` is reserved and cannot be set
-fail_compilation/reserved_version.d(197): Error: version identifier `D_NoBoundsChecks` is reserved and cannot be set
-fail_compilation/reserved_version.d(200): Error: version identifier `all` is reserved and cannot be set
-fail_compilation/reserved_version.d(201): Error: version identifier `none` is reserved and cannot be set
-fail_compilation/reserved_version.d(202): Error: version identifier `CppRuntime_Clang` is reserved and cannot be set
-fail_compilation/reserved_version.d(203): Error: version identifier `CppRuntime_DigitalMars` is reserved and cannot be set
-fail_compilation/reserved_version.d(204): Error: version identifier `CppRuntime_Gcc` is reserved and cannot be set
-fail_compilation/reserved_version.d(205): Error: version identifier `CppRuntime_Microsoft` is reserved and cannot be set
-fail_compilation/reserved_version.d(206): Error: version identifier `CppRuntime_Sun` is reserved and cannot be set
+fail_compilation/reserved_version.d(116): Error: version identifier `iOS` is reserved and cannot be set
+fail_compilation/reserved_version.d(117): Error: version identifier `TVOS` is reserved and cannot be set
+fail_compilation/reserved_version.d(118): Error: version identifier `WatchOS` is reserved and cannot be set
+fail_compilation/reserved_version.d(119): Error: version identifier `FreeBSD` is reserved and cannot be set
+fail_compilation/reserved_version.d(120): Error: version identifier `OpenBSD` is reserved and cannot be set
+fail_compilation/reserved_version.d(121): Error: version identifier `NetBSD` is reserved and cannot be set
+fail_compilation/reserved_version.d(122): Error: version identifier `DragonFlyBSD` is reserved and cannot be set
+fail_compilation/reserved_version.d(123): Error: version identifier `BSD` is reserved and cannot be set
+fail_compilation/reserved_version.d(124): Error: version identifier `Solaris` is reserved and cannot be set
+fail_compilation/reserved_version.d(125): Error: version identifier `Posix` is reserved and cannot be set
+fail_compilation/reserved_version.d(126): Error: version identifier `AIX` is reserved and cannot be set
+fail_compilation/reserved_version.d(127): Error: version identifier `Haiku` is reserved and cannot be set
+fail_compilation/reserved_version.d(128): Error: version identifier `SkyOS` is reserved and cannot be set
+fail_compilation/reserved_version.d(129): Error: version identifier `SysV3` is reserved and cannot be set
+fail_compilation/reserved_version.d(130): Error: version identifier `SysV4` is reserved and cannot be set
+fail_compilation/reserved_version.d(131): Error: version identifier `Hurd` is reserved and cannot be set
+fail_compilation/reserved_version.d(132): Error: version identifier `Android` is reserved and cannot be set
+fail_compilation/reserved_version.d(133): Error: version identifier `PlayStation` is reserved and cannot be set
+fail_compilation/reserved_version.d(134): Error: version identifier `PlayStation4` is reserved and cannot be set
+fail_compilation/reserved_version.d(135): Error: version identifier `Cygwin` is reserved and cannot be set
+fail_compilation/reserved_version.d(136): Error: version identifier `MinGW` is reserved and cannot be set
+fail_compilation/reserved_version.d(137): Error: version identifier `FreeStanding` is reserved and cannot be set
+fail_compilation/reserved_version.d(138): Error: version identifier `X86` is reserved and cannot be set
+fail_compilation/reserved_version.d(139): Error: version identifier `X86_64` is reserved and cannot be set
+fail_compilation/reserved_version.d(140): Error: version identifier `ARM` is reserved and cannot be set
+fail_compilation/reserved_version.d(141): Error: version identifier `ARM_Thumb` is reserved and cannot be set
+fail_compilation/reserved_version.d(142): Error: version identifier `ARM_SoftFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(143): Error: version identifier `ARM_SoftFP` is reserved and cannot be set
+fail_compilation/reserved_version.d(144): Error: version identifier `ARM_HardFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(145): Error: version identifier `AArch64` is reserved and cannot be set
+fail_compilation/reserved_version.d(146): Error: version identifier `Epiphany` is reserved and cannot be set
+fail_compilation/reserved_version.d(147): Error: version identifier `PPC` is reserved and cannot be set
+fail_compilation/reserved_version.d(148): Error: version identifier `PPC_SoftFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(149): Error: version identifier `PPC_HardFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(150): Error: version identifier `PPC64` is reserved and cannot be set
+fail_compilation/reserved_version.d(151): Error: version identifier `IA64` is reserved and cannot be set
+fail_compilation/reserved_version.d(152): Error: version identifier `MIPS32` is reserved and cannot be set
+fail_compilation/reserved_version.d(153): Error: version identifier `MIPS64` is reserved and cannot be set
+fail_compilation/reserved_version.d(154): Error: version identifier `MIPS_O32` is reserved and cannot be set
+fail_compilation/reserved_version.d(155): Error: version identifier `MIPS_N32` is reserved and cannot be set
+fail_compilation/reserved_version.d(156): Error: version identifier `MIPS_O64` is reserved and cannot be set
+fail_compilation/reserved_version.d(157): Error: version identifier `MIPS_N64` is reserved and cannot be set
+fail_compilation/reserved_version.d(158): Error: version identifier `MIPS_EABI` is reserved and cannot be set
+fail_compilation/reserved_version.d(159): Error: version identifier `MIPS_SoftFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(160): Error: version identifier `MIPS_HardFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(161): Error: version identifier `NVPTX` is reserved and cannot be set
+fail_compilation/reserved_version.d(162): Error: version identifier `NVPTX64` is reserved and cannot be set
+fail_compilation/reserved_version.d(163): Error: version identifier `RISCV32` is reserved and cannot be set
+fail_compilation/reserved_version.d(164): Error: version identifier `RISCV64` is reserved and cannot be set
+fail_compilation/reserved_version.d(165): Error: version identifier `SPARC` is reserved and cannot be set
+fail_compilation/reserved_version.d(166): Error: version identifier `SPARC_V8Plus` is reserved and cannot be set
+fail_compilation/reserved_version.d(167): Error: version identifier `SPARC_SoftFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(168): Error: version identifier `SPARC_HardFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(169): Error: version identifier `SPARC64` is reserved and cannot be set
+fail_compilation/reserved_version.d(170): Error: version identifier `S390` is reserved and cannot be set
+fail_compilation/reserved_version.d(171): Error: version identifier `S390X` is reserved and cannot be set
+fail_compilation/reserved_version.d(172): Error: version identifier `SystemZ` is reserved and cannot be set
+fail_compilation/reserved_version.d(173): Error: version identifier `HPPA` is reserved and cannot be set
+fail_compilation/reserved_version.d(174): Error: version identifier `HPPA64` is reserved and cannot be set
+fail_compilation/reserved_version.d(175): Error: version identifier `SH` is reserved and cannot be set
+fail_compilation/reserved_version.d(176): Error: version identifier `Alpha` is reserved and cannot be set
+fail_compilation/reserved_version.d(177): Error: version identifier `Alpha_SoftFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(178): Error: version identifier `Alpha_HardFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(179): Error: version identifier `LittleEndian` is reserved and cannot be set
+fail_compilation/reserved_version.d(180): Error: version identifier `BigEndian` is reserved and cannot be set
+fail_compilation/reserved_version.d(181): Error: version identifier `ELFv1` is reserved and cannot be set
+fail_compilation/reserved_version.d(182): Error: version identifier `ELFv2` is reserved and cannot be set
+fail_compilation/reserved_version.d(183): Error: version identifier `CRuntime_Bionic` is reserved and cannot be set
+fail_compilation/reserved_version.d(184): Error: version identifier `CRuntime_DigitalMars` is reserved and cannot be set
+fail_compilation/reserved_version.d(185): Error: version identifier `CRuntime_Glibc` is reserved and cannot be set
+fail_compilation/reserved_version.d(186): Error: version identifier `CRuntime_Microsoft` is reserved and cannot be set
+fail_compilation/reserved_version.d(187): Error: version identifier `CRuntime_Musl` is reserved and cannot be set
+fail_compilation/reserved_version.d(188): Error: version identifier `CRuntime_Newlib` is reserved and cannot be set
+fail_compilation/reserved_version.d(189): Error: version identifier `CRuntime_UClibc` is reserved and cannot be set
+fail_compilation/reserved_version.d(190): Error: version identifier `CRuntime_WASI` is reserved and cannot be set
+fail_compilation/reserved_version.d(191): Error: version identifier `D_Coverage` is reserved and cannot be set
+fail_compilation/reserved_version.d(192): Error: version identifier `D_Ddoc` is reserved and cannot be set
+fail_compilation/reserved_version.d(193): Error: version identifier `D_InlineAsm_X86` is reserved and cannot be set
+fail_compilation/reserved_version.d(194): Error: version identifier `D_InlineAsm_X86_64` is reserved and cannot be set
+fail_compilation/reserved_version.d(195): Error: version identifier `D_LP64` is reserved and cannot be set
+fail_compilation/reserved_version.d(196): Error: version identifier `D_X32` is reserved and cannot be set
+fail_compilation/reserved_version.d(197): Error: version identifier `D_HardFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(198): Error: version identifier `D_SoftFloat` is reserved and cannot be set
+fail_compilation/reserved_version.d(199): Error: version identifier `D_PIC` is reserved and cannot be set
+fail_compilation/reserved_version.d(200): Error: version identifier `D_SIMD` is reserved and cannot be set
+fail_compilation/reserved_version.d(201): Error: version identifier `D_Version2` is reserved and cannot be set
+fail_compilation/reserved_version.d(202): Error: version identifier `D_NoBoundsChecks` is reserved and cannot be set
+fail_compilation/reserved_version.d(205): Error: version identifier `all` is reserved and cannot be set
+fail_compilation/reserved_version.d(206): Error: version identifier `none` is reserved and cannot be set
+fail_compilation/reserved_version.d(207): Error: version identifier `AsmJS` is reserved and cannot be set
+fail_compilation/reserved_version.d(208): Error: version identifier `Emscripten` is reserved and cannot be set
+fail_compilation/reserved_version.d(209): Error: version identifier `WebAssembly` is reserved and cannot be set
+fail_compilation/reserved_version.d(210): Error: version identifier `WASI` is reserved and cannot be set
+fail_compilation/reserved_version.d(211): Error: version identifier `CppRuntime_Clang` is reserved and cannot be set
+fail_compilation/reserved_version.d(212): Error: version identifier `CppRuntime_DigitalMars` is reserved and cannot be set
+fail_compilation/reserved_version.d(213): Error: version identifier `CppRuntime_Gcc` is reserved and cannot be set
+fail_compilation/reserved_version.d(214): Error: version identifier `CppRuntime_Microsoft` is reserved and cannot be set
+fail_compilation/reserved_version.d(215): Error: version identifier `CppRuntime_Sun` is reserved and cannot be set
+fail_compilation/reserved_version.d(216): Error: version identifier `D_PIE` is reserved and cannot be set
+fail_compilation/reserved_version.d(217): Error: version identifier `AVR` is reserved and cannot be set
---
*/
@@ -119,6 +130,9 @@ version = Win32;
version = Win64;
version = linux;
version = OSX;
+version = iOS;
+version = TVOS;
+version = WatchOS;
version = FreeBSD;
version = OpenBSD;
version = NetBSD;
@@ -188,7 +202,9 @@ version = CRuntime_DigitalMars;
version = CRuntime_Glibc;
version = CRuntime_Microsoft;
version = CRuntime_Musl;
+version = CRuntime_Newlib;
version = CRuntime_UClibc;
+version = CRuntime_WASI;
version = D_Coverage;
version = D_Ddoc;
version = D_InlineAsm_X86;
@@ -205,11 +221,17 @@ version = D_NoBoundsChecks;
//version = assert;
version = all;
version = none;
+version = AsmJS;
+version = Emscripten;
+version = WebAssembly;
+version = WASI;
version = CppRuntime_Clang;
version = CppRuntime_DigitalMars;
version = CppRuntime_Gcc;
version = CppRuntime_Microsoft;
version = CppRuntime_Sun;
+version = D_PIE;
+version = AVR;
// This should work though
debug = DigitalMars;
@@ -276,6 +298,8 @@ debug = SystemZ;
debug = HPPA;
debug = HPPA64;
debug = SH;
+debug = WebAssembly;
+debug = WASI;
debug = Alpha;
debug = Alpha_SoftFloat;
debug = Alpha_HardFloat;
@@ -288,7 +312,9 @@ debug = CRuntime_DigitalMars;
debug = CRuntime_Glibc;
debug = CRuntime_Microsoft;
debug = CRuntime_Musl;
+debug = CRuntime_Newlib;
debug = CRuntime_UClibc;
+debug = CRuntime_WASI;
debug = CppRuntime_Clang;
debug = CppRuntime_DigitalMars;
debug = CppRuntime_Gcc;
@@ -303,6 +329,7 @@ debug = D_X32;
debug = D_HardFloat;
debug = D_SoftFloat;
debug = D_PIC;
+debug = D_PIE;
debug = D_SIMD;
debug = D_Version2;
debug = D_NoBoundsChecks;
@@ -312,3 +339,4 @@ debug = all;
debug = none;
debug = D_P16;
debug = MSP430;
+debug = AVR;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d b/gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d
index 0259273f89e..b8b6fa4d8e8 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d
@@ -34,6 +34,7 @@
// REQUIRED_ARGS: -version=ARM_SoftFP
// REQUIRED_ARGS: -version=ARM_HardFloat
// REQUIRED_ARGS: -version=AArch64
+// REQUIRED_ARGS: -version=AVR
// REQUIRED_ARGS: -version=Epiphany
// REQUIRED_ARGS: -version=PPC
// REQUIRED_ARGS: -version=PPC_SoftFloat
@@ -64,6 +65,8 @@
// REQUIRED_ARGS: -version=HPPA
// REQUIRED_ARGS: -version=HPPA64
// REQUIRED_ARGS: -version=SH
+// REQUIRED_ARGS: -version=WebAssembly
+// REQUIRED_ARGS: -version=WASI
// REQUIRED_ARGS: -version=Alpha
// REQUIRED_ARGS: -version=Alpha_SoftFloat
// REQUIRED_ARGS: -version=Alpha_HardFloat
@@ -76,7 +79,9 @@
// REQUIRED_ARGS: -version=CRuntime_Glibc
// REQUIRED_ARGS: -version=CRuntime_Microsoft
// REQUIRED_ARGS: -version=CRuntime_Musl
+// REQUIRED_ARGS: -version=CRuntime_Newlib
// REQUIRED_ARGS: -version=CRuntime_UClibc
+// REQUIRED_ARGS: -version=CRuntime_WASI
// REQUIRED_ARGS: -version=CppRuntime_Clang
// REQUIRED_ARGS: -version=CppRuntime_DigitalMars
// REQUIRED_ARGS: -version=CppRuntime_Gcc
@@ -160,6 +165,8 @@
// REQUIRED_ARGS: -debug=HPPA
// REQUIRED_ARGS: -debug=HPPA64
// REQUIRED_ARGS: -debug=SH
+// REQUIRED_ARGS: -debug=WebAssembly
+// REQUIRED_ARGS: -debug=WASI
// REQUIRED_ARGS: -debug=Alpha
// REQUIRED_ARGS: -debug=Alpha_SoftFloat
// REQUIRED_ARGS: -debug=Alpha_HardFloat
@@ -172,7 +179,9 @@
// REQUIRED_ARGS: -debug=CRuntime_Glibc
// REQUIRED_ARGS: -debug=CRuntime_Microsoft
// REQUIRED_ARGS: -debug=CRuntime_Musl
+// REQUIRED_ARGS: -debug=CRuntime_Newlib
// REQUIRED_ARGS: -debug=CRuntime_UClibc
+// REQUIRED_ARGS: -debug=CRuntime_WASI
// REQUIRED_ARGS: -debug=CppRuntime_Clang
// REQUIRED_ARGS: -debug=CppRuntime_DigitalMars
// REQUIRED_ARGS: -debug=CppRuntime_Gcc
@@ -231,6 +240,7 @@ Error: version identifier `ARM_SoftFloat` is reserved and cannot be set
Error: version identifier `ARM_SoftFP` is reserved and cannot be set
Error: version identifier `ARM_HardFloat` is reserved and cannot be set
Error: version identifier `AArch64` is reserved and cannot be set
+Error: version identifier `AVR` is reserved and cannot be set
Error: version identifier `Epiphany` is reserved and cannot be set
Error: version identifier `PPC` is reserved and cannot be set
Error: version identifier `PPC_SoftFloat` is reserved and cannot be set
@@ -261,6 +271,8 @@ Error: version identifier `SystemZ` is reserved and cannot be set
Error: version identifier `HPPA` is reserved and cannot be set
Error: version identifier `HPPA64` is reserved and cannot be set
Error: version identifier `SH` is reserved and cannot be set
+Error: version identifier `WebAssembly` is reserved and cannot be set
+Error: version identifier `WASI` is reserved and cannot be set
Error: version identifier `Alpha` is reserved and cannot be set
Error: version identifier `Alpha_SoftFloat` is reserved and cannot be set
Error: version identifier `Alpha_HardFloat` is reserved and cannot be set
@@ -273,7 +285,9 @@ Error: version identifier `CRuntime_DigitalMars` is reserved and cannot be set
Error: version identifier `CRuntime_Glibc` is reserved and cannot be set
Error: version identifier `CRuntime_Microsoft` is reserved and cannot be set
Error: version identifier `CRuntime_Musl` is reserved and cannot be set
+Error: version identifier `CRuntime_Newlib` is reserved and cannot be set
Error: version identifier `CRuntime_UClibc` is reserved and cannot be set
+Error: version identifier `CRuntime_WASI` is reserved and cannot be set
Error: version identifier `CppRuntime_Clang` is reserved and cannot be set
Error: version identifier `CppRuntime_DigitalMars` is reserved and cannot be set
Error: version identifier `CppRuntime_Gcc` is reserved and cannot be set
diff --git a/gcc/testsuite/gdc.test/fail_compilation/retref2.d b/gcc/testsuite/gdc.test/fail_compilation/retref2.d
index 037c258539b..aaa5b41e306 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/retref2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/retref2.d
@@ -1,11 +1,8 @@
-
-// REQUIRED_ARGS: -dip25
-
/*
TEST_OUTPUT:
---
-fail_compilation/retref2.d(21): Error: function retref2.D.foo does not override any function, did you mean to override 'retref2.C.foo'?
-fail_compilation/retref2.d(22): Error: function retref2.D.bar does not override any function, did you mean to override 'retref2.C.bar'?
+fail_compilation/retref2.d(18): Error: function `ref int retref2.D.foo(return ref int)` does not override any function, did you mean to override `ref int retref2.C.foo(ref int)`?
+fail_compilation/retref2.d(19): Error: function `ref scope int retref2.D.bar() return` does not override any function, did you mean to override `ref int retref2.C.bar()`?
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope.d b/gcc/testsuite/gdc.test/fail_compilation/retscope.d
index 35efda38ac3..58f0430f5ad 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/retscope.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/retscope.d
@@ -1,14 +1,13 @@
/*
-REQUIRED_ARGS: -dip1000
-PERMUTE_ARGS:
+REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/retscope.d(23): Error: scope variable `p` may not be returned
-fail_compilation/retscope.d(33): Error: returning `b ? nested1(& i) : nested2(& j)` escapes a reference to local variable `j`
-fail_compilation/retscope.d(46): Error: scope variable `p` assigned to non-scope `q`
-fail_compilation/retscope.d(48): Error: address of variable `i` assigned to `q` with longer lifetime
-fail_compilation/retscope.d(49): Error: variadic variable `a` assigned to non-scope `b`
-fail_compilation/retscope.d(50): Error: reference to stack allocated value returned by `(*fp2)()` assigned to non-scope `q`
+fail_compilation/retscope.d(22): Error: scope variable `p` may not be returned
+fail_compilation/retscope.d(32): Error: returning `b ? nested1(& i) : nested2(& j)` escapes a reference to local variable `j`
+fail_compilation/retscope.d(45): Error: scope variable `p` assigned to non-scope `q`
+fail_compilation/retscope.d(47): Error: address of variable `i` assigned to `q` with longer lifetime
+fail_compilation/retscope.d(48): Error: scope variable `a` assigned to non-scope `b`
+fail_compilation/retscope.d(49): Error: address of struct temporary returned by `(*fp2)()` assigned to longer lived variable `q`
---
*/
@@ -55,8 +54,8 @@ void test2(scope int* p, int[] a ...) @safe
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(76): Error: function retscope.HTTP.Impl.onReceive is @nogc yet allocates closures with the GC
-fail_compilation/retscope.d(78): retscope.HTTP.Impl.onReceive.__lambda1 closes over variable this at fail_compilation/retscope.d(76)
+fail_compilation/retscope.d(75): Error: function `retscope.HTTP.Impl.onReceive` is `@nogc` yet allocates closures with the GC
+fail_compilation/retscope.d(77): retscope.HTTP.Impl.onReceive.__lambda1 closes over variable this at fail_compilation/retscope.d(75)
---
*/
@@ -86,7 +85,7 @@ struct HTTP
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(97): Error: reference to local variable `sa` assigned to non-scope parameter `a` calling retscope.bar8
+fail_compilation/retscope.d(96): Error: reference to local variable `sa` assigned to non-scope parameter `a` calling retscope.bar8
---
*/
// https://issues.dlang.org/show_bug.cgi?id=8838
@@ -108,7 +107,7 @@ int[] bar8(int[] a) @safe
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(124): Error: returning `foo9(cast(char[])tmp)` escapes a reference to local variable `tmp`
+fail_compilation/retscope.d(123): Error: returning `foo9(cast(char[])tmp)` escapes a reference to local variable `tmp`
---
*/
@@ -129,7 +128,7 @@ char[] bar9() @safe
/*
//
//
-//fail_compilation/retscope.d(143): To enforce @safe compiler allocates a closure unless the opApply() uses 'scope'
+//fail_compilation/retscope.d(143): To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`
//
*/
@@ -150,7 +149,7 @@ S10* test10()
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(159): Error: scope variable `this` may not be returned
+fail_compilation/retscope.d(158): Error: scope variable `this` may not be returned
---
*/
@@ -165,7 +164,7 @@ class C11
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(178): Error: address of variable `i` assigned to `p` with longer lifetime
+fail_compilation/retscope.d(177): Error: address of variable `i` assigned to `p` with longer lifetime
---
*/
@@ -182,7 +181,7 @@ void foo11() @safe
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(198): Error: scope variable `e` may not be returned
+fail_compilation/retscope.d(197): Error: scope variable `e` may not be returned
---
*/
@@ -202,7 +201,7 @@ void* escapeDg1(scope void* d) @safe
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(213): Error: scope variable `p` assigned to non-scope `e`
+fail_compilation/retscope.d(212): Error: scope variable `p` assigned to non-scope `e.e`
---
*/
struct Escaper3 { void* e; }
@@ -219,7 +218,7 @@ void* escape3 (scope void* p) @safe {
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(230): Error: scope variable `ptr` may not be returned
+fail_compilation/retscope.d(229): Error: scope variable `ptr` may not be returned
---
*/
@@ -235,10 +234,10 @@ void* funretscope(scope dg_t ptr) @safe
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda1` of type `void* delegate() pure nothrow @nogc return @safe` to `void* delegate() @safe`
-fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda1` of type `void* delegate() pure nothrow @nogc return @safe` to `void* delegate() @safe`
-fail_compilation/retscope.d(250): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc return @safe` to `void* delegate() @safe`
-fail_compilation/retscope.d(250): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc return @safe` to `void* delegate() @safe`
+fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() @safe`
+fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() @safe`
+fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda4` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() @safe`
+fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda4` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() @safe`
---
*/
@@ -255,7 +254,7 @@ void escape4() @safe
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(267): Error: cannot take address of scope local p in @safe function escape5
+fail_compilation/retscope.d(266): Error: cannot take address of `scope` local `p` in `@safe` function `escape5`
---
*/
@@ -272,7 +271,7 @@ void escape5() @safe
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(287): Error: returning `foo6(& b)` escapes a reference to local variable `b`
+fail_compilation/retscope.d(286): Error: returning `foo6(& b)` escapes a reference to local variable `b`
---
*/
@@ -308,7 +307,7 @@ char*[3] escape9(scope char*[] p) @safe { return p[0 .. 3]; }
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(320): Error: reference to local variable `i` assigned to non-scope `f`
+fail_compilation/retscope.d(319): Error: reference to local variable `i` assigned to non-scope `f`
---
*/
@@ -332,7 +331,7 @@ int* bar10( scope int** ptr ) @safe
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(343): Error: cannot take address of scope local aa in @safe function escape11
+fail_compilation/retscope.d(342): Error: cannot take address of `scope` local `aa` in `@safe` function `escape11`
---
*/
@@ -455,7 +454,7 @@ fail_compilation/retscope.d(1311): Error: scope variable `u2` assigned to `ek` w
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(1405): Error: reference to local variable `buf` assigned to non-scope parameter `unnamed` calling retscope.myprintf
+fail_compilation/retscope.d(1405): Error: reference to local variable `buf` assigned to non-scope parameter `__anonymous_param` calling retscope.myprintf
---
*/
@@ -473,7 +472,7 @@ fail_compilation/retscope.d(1405): Error: reference to local variable `buf` assi
/*
TEST_OUTPUT:
---
-fail_compilation/retscope.d(1509): Error: reference to stack allocated value returned by `(*fp15)()` assigned to non-scope parameter `unnamed`
+fail_compilation/retscope.d(1509): Error: reference to stack allocated value returned by `(*fp15)()` assigned to non-scope parameter `__anonymous_param`
---
*/
@@ -506,9 +505,9 @@ void foo16() @nogc nothrow
TEST_OUTPUT:
---
fail_compilation/retscope.d(1701): Error: cannot implicitly convert expression `& func` of type `int* function(int* p)` to `int* function(scope int* p)`
-fail_compilation/retscope.d(1702): Error: cannot implicitly convert expression `& func` of type `int* function(int* p)` to `int* function(return scope int* p)`
+fail_compilation/retscope.d(1702): Error: cannot implicitly convert expression `& func` of type `int* function(int* p)` to `int* function(return int* p)`
fail_compilation/retscope.d(1703): Error: cannot implicitly convert expression `& func` of type `int* function(int* p)` to `int* function(return scope int* p)`
-fail_compilation/retscope.d(1711): Error: cannot implicitly convert expression `& funcr` of type `int* function(return scope int* p)` to `int* function(scope int* p)`
+fail_compilation/retscope.d(1711): Error: cannot implicitly convert expression `& funcr` of type `int* function(return int* p)` to `int* function(scope int* p)`
fail_compilation/retscope.d(1716): Error: cannot implicitly convert expression `& funcrs` of type `int* function(return scope int* p)` to `int* function(scope int* p)`
---
*/
@@ -660,3 +659,31 @@ int test21()
foo22(s);
}
+/*********************************************
+TEST_OUTPUT:
+---
+fail_compilation/retscope.d(1907): Error: scope variable `x` assigned to `this` with longer lifetime
+fail_compilation/retscope.d(1913): Error: scope variable `x` may not be returned
+---
+*/
+#line 1900
+struct Constant
+{
+ int* member;
+
+ int* foo(scope Repeat!(int*) grid) @safe
+ {
+ foreach(ref x; grid)
+ member = x;
+
+ foreach(ref x; grid)
+ x = member;
+
+ foreach(ref x; grid)
+ return x;
+
+ return null;
+ }
+
+ alias Repeat(T...) = T;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope2.d b/gcc/testsuite/gdc.test/fail_compilation/retscope2.d
index a565008a792..8e6e0a46c5f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/retscope2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/retscope2.d
@@ -1,6 +1,5 @@
/*
-REQUIRED_ARGS: -dip1000
-PERMUTE_ARGS:
+REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
fail_compilation/retscope2.d(102): Error: scope variable `s` assigned to `p` with longer lifetime
@@ -87,9 +86,9 @@ fail_compilation/retscope2.d(504): Error: scope variable `c` may not be returned
/*
TEST_OUTPUT:
---
-fail_compilation/retscope2.d(604): Error: scope variable `_param_0` assigned to non-scope parameter `unnamed` calling retscope2.foo600
-fail_compilation/retscope2.d(604): Error: scope variable `_param_1` assigned to non-scope parameter `unnamed` calling retscope2.foo600
-fail_compilation/retscope2.d(614): Error: template instance retscope2.test600!(int*, int*) error instantiating
+fail_compilation/retscope2.d(604): Error: scope variable `_param_0` assigned to non-scope parameter `__anonymous_param` calling retscope2.foo600
+fail_compilation/retscope2.d(604): Error: scope variable `_param_1` assigned to non-scope parameter `__anonymous_param` calling retscope2.foo600
+fail_compilation/retscope2.d(614): Error: template instance `retscope2.test600!(int*, int*)` error instantiating
---
*/
@@ -124,7 +123,7 @@ fail_compilation/retscope2.d(721): Error: returning `s.get1()` escapes a referen
#line 700
// https://issues.dlang.org/show_bug.cgi?id=17049
-@safe S700* get2(return ref scope S700 _this)
+@safe S700* get2(return ref S700 _this)
{
return &_this;
}
@@ -164,12 +163,12 @@ void foo800()
}
/*************************************************/
-/+
-/*
-XEST_OUTPUT:
+/*
+TEST_OUTPUT:
+---
fail_compilation/retscope2.d(907): Error: address of variable `this` assigned to `p17568` with longer lifetime
-
+---
*/
#line 900
@@ -183,14 +182,15 @@ struct T17568
p17568 = &a;
}
}
-+/
+
/*************************************************/
/*
TEST_OUTPUT:
---
-fail_compilation/retscope2.d(1005): Error: scope variable `p` assigned to `this` with longer lifetime
-fail_compilation/retscope2.d(1024): Error: scope variable `p` assigned to `d` with longer lifetime
+fail_compilation/retscope2.d(1005): Error: scope variable `p` assigned to non-scope `this._p`
+fail_compilation/retscope2.d(1021): Error: scope variable `p` assigned to non-scope `c._p`
+fail_compilation/retscope2.d(1024): Error: scope variable `p` assigned to non-scope `d._p`
---
*/
@@ -216,7 +216,7 @@ void test17428() @safe
int x;
int* p = &x;
scope C17428b c;
- c._p = p; // ok
+ c._p = p; // bad
C17428b d;
d._p = p; // bad
@@ -291,5 +291,28 @@ struct T17388
return t.foo();
}
+/****************************************************/
+/*
+TEST_OUTPUT:
+---
+fail_compilation/retscope2.d(1306): Error: copying `& i` into allocated memory escapes a reference to local variable `i`
+---
+*/
+
+#line 1300
+
+// https://issues.dlang.org/show_bug.cgi?id=17370
+
+void test1300() @safe
+{
+ int i;
+ auto p = new S1300(&i).oops;
+}
+
+struct S1300
+{
+ int* oops;
+// this(int* p) @safe { oops = p; }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope3.d b/gcc/testsuite/gdc.test/fail_compilation/retscope3.d
new file mode 100644
index 00000000000..130d49b494c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/retscope3.d
@@ -0,0 +1,130 @@
+/*
+REQUIRED_ARGS: -preview=dip1000
+*/
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/retscope3.d(2008): Error: copying `& i` into allocated memory escapes a reference to local variable `i`
+fail_compilation/retscope3.d(2017): Error: copying `S2000(& i)` into allocated memory escapes a reference to local variable `i`
+---
+*/
+
+#line 2000
+
+// https://issues.dlang.org/show_bug.cgi?id=17790
+
+@safe:
+
+int* bar1()
+{
+ int i;
+ int*[] arr = [ &i ];
+ return arr[0];
+}
+
+struct S2000 { int* p; }
+
+S2000 bar2()
+{
+ int i;
+ S2000[] arr = [ S2000(&i) ];
+ return arr[0];
+}
+
+void bar3(string[] u...) @safe pure nothrow @nogc
+{
+ foreach (str; u)
+ {
+ }
+}
+
+void bar4()
+{
+ static struct S { int* p; }
+ S[2][10] pairs;
+ foreach (ref pair; pairs)
+ {
+ }
+}
+
+/**********************************************/
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/retscope3.d(3027): Error: scope variable `l` assigned to `elem` with longer lifetime
+---
+*/
+
+#line 3000
+
+struct List
+{
+ Elem front() @safe return scope;
+
+ ~this() @trusted scope;
+
+ @disable this(this);
+
+ void* data;
+}
+
+struct Elem
+{
+ void* data;
+}
+
+List list() @trusted
+{
+ return List();
+}
+
+void test3000() @safe
+{
+ Elem elem;
+ {
+ auto l = list(); // inferred as scope
+ elem = l.front; // escapes, b/c l isn't scoped
+ }
+}
+
+/**********************************************/
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/retscope3.d(4003): Error: copying `u[]` into allocated memory escapes a reference to variadic parameter `u`
+fail_compilation/retscope3.d(4016): Error: storing reference to outer local variable `i` into allocated memory causes it to escape
+fail_compilation/retscope3.d(4025): Error: storing reference to stack allocated value returned by `makeSA()` into allocated memory causes it to escape
+---
+*/
+
+#line 4000
+
+void bar4000(int[1] u...) @safe
+{
+ int[][] n = [u[]];
+}
+
+void bar4001() @safe
+{
+ static int i;
+ int*[] n = [&i];
+}
+
+ref int bar4002(return ref int i) @safe
+{
+ void nested()
+ {
+ int*[] n = [&i];
+ }
+ return i;
+}
+
+int[3] makeSA() @safe;
+
+void bar4003() @safe
+{
+ int[][] a = [makeSA()[]];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope4.d b/gcc/testsuite/gdc.test/fail_compilation/retscope4.d
new file mode 100644
index 00000000000..e5fc278f327
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/retscope4.d
@@ -0,0 +1,21 @@
+/*
+REQUIRED_ARGS: -de
+*/
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/retscope4.d(3007): Deprecation: slice of static array temporary returned by `func()` assigned to longer lived variable `a`
+---
+*/
+
+#line 3000
+
+// https://issues.dlang.org/show_bug.cgi?id=12625
+
+int[16] func();
+
+void foo()
+{
+ int[] a = func();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope5.d b/gcc/testsuite/gdc.test/fail_compilation/retscope5.d
new file mode 100644
index 00000000000..1f9906eb0b1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/retscope5.d
@@ -0,0 +1,26 @@
+/*
+REQUIRED_ARGS: -preview=dip1000
+*/
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/retscope5.d(5010): Error: reference `t` assigned to `p` with longer lifetime
+---
+*/
+
+#line 5000
+
+// https://issues.dlang.org/show_bug.cgi?id=17725
+
+void test() @safe
+{
+ int* p;
+ struct T {
+ int a;
+ }
+ void escape(ref T t) @safe {
+ p = &t.a; // should not compile
+ }
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope6.d b/gcc/testsuite/gdc.test/fail_compilation/retscope6.d
new file mode 100644
index 00000000000..47f216f63a6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/retscope6.d
@@ -0,0 +1,233 @@
+/*
+REQUIRED_ARGS: -preview=dip1000
+*/
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/retscope6.d(6007): Error: copying `& i` into allocated memory escapes a reference to local variable `i`
+---
+*/
+
+#line 6000
+
+// https://issues.dlang.org/show_bug.cgi?id=17795
+
+int* test() @safe
+{
+ int i;
+ int*[][] arr = new int*[][](1);
+ arr[0] ~= &i;
+ return arr[0][0];
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/retscope6.d(7034): Error: address of variable `i` assigned to `s` with longer lifetime
+fail_compilation/retscope6.d(7035): Error: address of variable `i` assigned to `s` with longer lifetime
+fail_compilation/retscope6.d(7025): Error: scope variable `_param_2` assigned to `t` with longer lifetime
+fail_compilation/retscope6.d(7037): Error: template instance `retscope6.S.emplace4!(int*)` error instantiating
+fail_compilation/retscope6.d(7037): Error: address of variable `i` assigned to `s` with longer lifetime
+---
+*/
+
+#line 7000
+
+alias T = int*;
+
+struct S
+{
+ T payload;
+
+ static void emplace(Args...)(ref S s, Args args) @safe
+ {
+ s.payload = args[0];
+ }
+
+ void emplace2(Args...)(Args args) @safe
+ {
+ payload = args[0];
+ }
+
+ static void emplace3(Args...)(S s, Args args) @safe
+ {
+ s.payload = args[0];
+ }
+
+ static void emplace4(Args...)(scope ref S s, scope out S t, scope Args args) @safe
+ {
+ s.payload = args[0];
+ t.payload = args[0];
+ }
+
+}
+
+void foo() @safe
+{
+ S s;
+ int i;
+ s.emplace(s, &i);
+ s.emplace2(&i);
+ s.emplace3(s, &i);
+ s.emplace4(s, s, &i);
+}
+
+
+/* TEST_OUTPUT:
+---
+fail_compilation/retscope6.d(8016): Error: address of variable `i` assigned to `p` with longer lifetime
+fail_compilation/retscope6.d(8031): Error: reference to local variable `i` assigned to non-scope parameter `p` calling retscope6.betty!().betty
+fail_compilation/retscope6.d(8031): Error: reference to local variable `j` assigned to non-scope parameter `q` calling retscope6.betty!().betty
+fail_compilation/retscope6.d(8048): Error: reference to local variable `j` assigned to non-scope parameter `q` calling retscope6.archie!().archie
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=19035
+
+#line 8000
+@safe
+{
+
+void escape(int*);
+
+/**********************/
+
+void frank()(ref scope int* p, int* s)
+{
+ p = s; // should error here
+}
+
+void testfrankly()
+{
+ int* p;
+ int i;
+ frank(p, &i);
+}
+
+/**********************/
+
+void betty()(int* p, int* q)
+{
+ p = q;
+ escape(p);
+}
+
+void testbetty()
+{
+ int i;
+ int j;
+ betty(&i, &j); // should error on i and j
+}
+
+/**********************/
+
+void archie()(int* p, int* q, int* r)
+{
+ p = q;
+ r = p;
+ escape(q);
+}
+
+void testarchie()
+{
+ int i;
+ int j;
+ int k;
+ archie(&i, &j, &k); // should error on j
+}
+
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/retscope6.d(9022): Error: returning `fred(& i)` escapes a reference to local variable `i`
+---
+*/
+
+#line 9000
+
+@safe:
+
+alias T9 = S9!(); struct S9()
+{
+ this(int* q)
+ {
+ this.p = q;
+ }
+
+ int* p;
+}
+
+auto fred(int* r)
+{
+ return T9(r);
+}
+
+T9 testfred()
+{
+ int i;
+ auto j = fred(&i); // ok
+ return fred(&i); // error
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/retscope6.d(10003): Error: scope variable `values` assigned to non-scope parameter `values` calling retscope6.escape
+---
+*/
+
+#line 10000
+
+void variadicCaller(int[] values...)
+{
+ escape(values);
+}
+
+void escape(int[] values) {}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/retscope6.d(11004): Error: address of variable `buffer` assigned to `secret` with longer lifetime
+---
+*/
+
+#line 11000
+
+void hmac(scope ubyte[] secret)
+{
+ ubyte[10] buffer;
+ secret = buffer[];
+}
+
+/* TEST_OUTPUT:
+---
+fail_compilation/retscope6.d(12011): Error: reference to local variable `x` assigned to non-scope parameter `r` calling retscope6.escape_m_20150
+fail_compilation/retscope6.d(12022): Error: reference to local variable `x` assigned to non-scope parameter `r` calling retscope6.escape_c_20150
+---
+*/
+
+#line 12000
+
+// https://issues.dlang.org/show_bug.cgi?id=20150
+
+int* escape_m_20150(int* r) @safe pure
+{
+ return r;
+}
+
+int* f_m_20150() @safe
+{
+ int x = 42;
+ return escape_m_20150(&x);
+}
+
+const(int)* escape_c_20150(const int* r) @safe pure
+{
+ return r;
+}
+
+const(int)* f_c_20150() @safe
+{
+ int x = 42;
+ return escape_c_20150(&x);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/scope_class.d b/gcc/testsuite/gdc.test/fail_compilation/scope_class.d
new file mode 100644
index 00000000000..b70e3eb8c5a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/scope_class.d
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/scope_class.d(11): Error: functions cannot return `scope scope_class.C`
+---
+*/
+
+
+scope class C { int i; } // Notice the use of `scope` here
+
+C increment(C c)
+{
+ c.i++;
+ return c;
+}
+
+void main()
+{
+ scope C c = new C();
+ c.increment();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/scope_type.d b/gcc/testsuite/gdc.test/fail_compilation/scope_type.d
new file mode 100644
index 00000000000..b087411ae22
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/scope_type.d
@@ -0,0 +1,12 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/scope_type.d(11): Deprecation: `scope` as a type constraint is deprecated. Use `scope` at the usage site.
+---
+*/
+
+
+scope class C { }
+scope interface I { }
+//scope struct S { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/shared.d b/gcc/testsuite/gdc.test/fail_compilation/shared.d
new file mode 100644
index 00000000000..6011d17c5ad
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/shared.d
@@ -0,0 +1,227 @@
+/* REQUIRED_ARGS: -preview=nosharedaccess
+ * TEST_OUTPUT:
+---
+fail_compilation/shared.d(1010): Error: direct access to shared `j` is not allowed, see `core.atomic`
+fail_compilation/shared.d(1011): Error: direct access to shared `j` is not allowed, see `core.atomic`
+fail_compilation/shared.d(1012): Error: direct access to shared `*p` is not allowed, see `core.atomic`
+fail_compilation/shared.d(1013): Error: direct access to shared `a[0]` is not allowed, see `core.atomic`
+fail_compilation/shared.d(1014): Error: direct access to shared `s.si` is not allowed, see `core.atomic`
+fail_compilation/shared.d(1015): Error: direct access to shared `t.i` is not allowed, see `core.atomic`
+---
+*/
+
+#line 1000
+
+struct S
+{
+ shared(int) si;
+ int i;
+}
+
+int test1(shared int j, shared(int)* p, shared(int)[] a, ref S s, ref shared S t)
+{
+ int i;
+ j = i;
+ i = j;
+ i = *p;
+ i = a[0];
+ i = s.si;
+ return t.i;
+}
+
+/**************************************/
+
+void byref(ref shared int);
+void byptr(shared(int)*);
+
+shared int x;
+
+void test2()
+{
+ byref(x); // ok
+ byptr(&x); // ok
+}
+
+/**************************************/
+
+/*
+ * TEST_OUTPUT:
+---
+fail_compilation/shared.d(2008): Error: direct access to shared `i` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2009): Error: direct access to shared `j` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2010): Error: direct access to shared `k` is not allowed, see `core.atomic`
+---
+ */
+
+#line 2000
+
+void func(int);
+
+shared int i;
+
+void test3(shared int k)
+{
+ shared int j = void;
+ func(i);
+ func(j);
+ func(k);
+}
+
+/**************************************/
+
+void test4() // no errors for initialization
+{
+ shared int x;
+ shared int y = 3;
+}
+
+/*
+ * TEST_OUTPUT:
+---
+fail_compilation/shared.d(2105): Error: direct access to shared `*pi` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2112): Error: direct access to shared `**pi` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2136): Error: direct access to shared `*c` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2142): Error: direct access to shared `*c` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2148): Error: direct access to shared `*c` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2154): Error: direct access to shared `*c.c1` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2160): Error: direct access to shared `*c.c1.c1` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2181): Error: direct access to shared `k` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2187): Error: direct access to shared `k.k2.k1` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2194): Error: direct access to shared `(new shared(K2)).k1` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2202): Error: direct access to shared `c` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2206): Error: function `shared.test_inference_2` function returns `shared` but cannot be inferred `ref`
+fail_compilation/shared.d(2208): Error: returning `c` escapes a reference to parameter `c`
+fail_compilation/shared.d(2208): perhaps annotate the parameter with `return`
+fail_compilation/shared.d(2214): Error: function `shared.test_inference_3` function returns `shared` but cannot be inferred `ref`
+fail_compilation/shared.d(2216): return value `getSharedObject()` is not an lvalue
+fail_compilation/shared.d(2222): Error: direct access to shared `a` is not allowed, see `core.atomic`
+fail_compilation/shared.d(2220): Error: function `shared.test_inference_4` function returns `shared` but cannot be inferred `ref`
+fail_compilation/shared.d(2222): cannot implicitly convert `a` of type `shared(const(Object))` to `object.Object`
+fail_compilation/shared.d(2222): Error: cannot implicitly convert expression `a` of type `shared(const(Object))` to `object.Object`
+---
+ */
+#line 2100
+// Derived from https://issues.dlang.org/show_bug.cgi?id=20908
+ref shared(int) test20908()
+{
+ shared int* pi;
+ // Single indirection, but the pointer is `shared`
+ return *pi;
+}
+
+ref shared(int) test20908_2()
+{
+ shared(int*)* pi;
+ // Double indirection, external pointer is not `shared`
+ return **pi;
+}
+
+// DotVarExp tests: See matching tests in `compilable/shared.d`
+
+struct C1
+{
+ int value;
+}
+
+struct C2
+{
+ C1* c1;
+}
+
+struct C3
+{
+ C2 c1;
+ C2* c2;
+}
+
+// Reading a shared pointer: not okay
+ref shared(int) test_dotvarexp_1(return shared C1* c)
+{
+ return c.value;
+}
+
+// Ditto, but explicitly dereferenced
+ref shared(int) test_dotvarexp_2(return shared C1* c)
+{
+ return (*c).value;
+}
+
+// Even taking the address (which offset the pointers) requires a load
+shared(int)* test_dotvarexp_3(return shared C1* c)
+{
+ return &c.value;
+}
+
+// First level DotVarExp dereferencing
+ref shared(int) test_dotvarexp_4(return shared ref C2 c)
+{
+ return c.c1.value;
+}
+
+// Second level DotVarExp dereferencing
+ref shared(int) test_dotvarexp_5(return shared ref C3 c)
+{
+ return c.c1.c1.value;
+}
+
+class K1
+{
+ int value;
+}
+
+class K2
+{
+ shared K1 k1;
+}
+
+class K3
+{
+ K2 k2;
+}
+
+// A class is a pointer under the hood, and `shared` applies to the pointer
+ref shared(int) test_dotvarexp_6(return shared K1 k)
+{
+ return k.value;
+}
+
+// Using `k.ke.k1` would be okay, but not `value`
+ref shared(int) test_dotvarexp_7(return ref K3 k)
+{
+ return k.k2.k1.value;
+}
+
+// The returned value is `shared` so we shouldn't be able to access it
+// The pointer could already be shared, e.g. by the ctor
+ref shared(K1) test_newexp_1()
+{
+ return new shared(K2)().k1;
+}
+
+// Inference tests
+
+// Fails because no `ref`
+auto test_inference_1(return shared ref C3 c)
+{
+ return c;
+}
+
+// Fails because no `return` => Escapes
+auto ref test_inference_2(shared C3 c)
+{
+ return c;
+}
+
+shared(Object) getSharedObject() { assert(0); }
+
+// Fails because rvalue
+auto ref test_inference_3()
+{
+ return getSharedObject();
+}
+
+// Fails because `const` conversion
+auto ref Object test_inference_4(const return shared ref Object a)
+{
+ return a;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/skip.d b/gcc/testsuite/gdc.test/fail_compilation/skip.d
index c870e7e945a..0f3a9ec7e56 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/skip.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/skip.d
@@ -2,8 +2,8 @@
* REQUIRED_ARGS: -de
* TEST_OUTPUT:
---
-fail_compilation/skip.d(21): Deprecation: 'switch' skips declaration of 'with' temporary at fail_compilation/skip.d(26)
-fail_compilation/skip.d(43): Deprecation: 'switch' skips declaration of variable skip.test14532.n at fail_compilation/skip.d(45)
+fail_compilation/skip.d(21): Error: `switch` skips declaration of `with` temporary at fail_compilation/skip.d(26)
+fail_compilation/skip.d(43): Error: `switch` skips declaration of variable `skip.test14532.n` at fail_compilation/skip.d(45)
---
*/
// https://issues.dlang.org/show_bug.cgi?id=10524
diff --git a/gcc/testsuite/gdc.test/fail_compilation/spell9644.d b/gcc/testsuite/gdc.test/fail_compilation/spell9644.d
index 383ec677586..0c6d67ba283 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/spell9644.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/spell9644.d
@@ -1,17 +1,16 @@
// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/spell9644a.d imports/spell9644b.d
/*
TEST_OUTPUT:
---
-fail_compilation/spell9644.d(27): Error: undefined identifier `b`
-fail_compilation/spell9644.d(28): Error: undefined identifier `xx`
-fail_compilation/spell9644.d(29): Error: undefined identifier `cb`, did you mean variable `ab`?
-fail_compilation/spell9644.d(30): Error: undefined identifier `bc`, did you mean variable `abc`?
-fail_compilation/spell9644.d(31): Error: undefined identifier `ccc`
-fail_compilation/spell9644.d(33): Error: undefined identifier `cor2`, did you mean variable `cor1`?
-fail_compilation/spell9644.d(34): Error: undefined identifier `pua`, did you mean variable `pub`?
-fail_compilation/spell9644.d(35): Error: undefined identifier `priw`
+fail_compilation/spell9644.d(26): Error: undefined identifier `b`
+fail_compilation/spell9644.d(27): Error: undefined identifier `xx`
+fail_compilation/spell9644.d(28): Error: undefined identifier `cb`, did you mean variable `ab`?
+fail_compilation/spell9644.d(29): Error: undefined identifier `bc`, did you mean variable `abc`?
+fail_compilation/spell9644.d(30): Error: undefined identifier `ccc`
+fail_compilation/spell9644.d(32): Error: undefined identifier `cor2`, did you mean variable `cor1`?
+fail_compilation/spell9644.d(33): Error: undefined identifier `pua`, did you mean variable `pub`?
+fail_compilation/spell9644.d(34): Error: undefined identifier `priw`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/staticarrayoverflow.d b/gcc/testsuite/gdc.test/fail_compilation/staticarrayoverflow.d
index 6a5a16d1e68..1305bc55351 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/staticarrayoverflow.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/staticarrayoverflow.d
@@ -2,14 +2,14 @@
REQUIRED_ARGS: -m64
TEST_OUTPUT:
---
-fail_compilation/staticarrayoverflow.d(24): Error: static array `S[1879048192]` size overflowed to 7516192768000
-fail_compilation/staticarrayoverflow.d(24): Error: variable `staticarrayoverflow.y` size overflow
-fail_compilation/staticarrayoverflow.d(26): Error: static array `S[8070450532247928832]` size overflowed to 8070450532247928832
-fail_compilation/staticarrayoverflow.d(26): Error: variable `staticarrayoverflow.a` size overflow
-fail_compilation/staticarrayoverflow.d(27): Error: static array `S[0][18446744073709551615LU]` size overflowed to 18446744073709551615
-fail_compilation/staticarrayoverflow.d(27): Error: variable `staticarrayoverflow.b` size overflow
-fail_compilation/staticarrayoverflow.d(28): Error: static array `S[0][4294967295]` size overflowed to 4294967295
-fail_compilation/staticarrayoverflow.d(28): Error: variable `staticarrayoverflow.c` size overflow
+fail_compilation/staticarrayoverflow.d(23): Error: static array `S[1879048192]` size overflowed to 7516192768000
+fail_compilation/staticarrayoverflow.d(23): Error: variable `staticarrayoverflow.y` size overflow
+fail_compilation/staticarrayoverflow.d(25): Error: static array `S[8070450532247928832]` size overflowed to 8070450532247928832
+fail_compilation/staticarrayoverflow.d(25): Error: variable `staticarrayoverflow.a` size overflow
+fail_compilation/staticarrayoverflow.d(26): Error: static array `S[0][18446744073709551615LU]` size overflowed to 18446744073709551615
+fail_compilation/staticarrayoverflow.d(26): Error: variable `staticarrayoverflow.b` size overflow
+fail_compilation/staticarrayoverflow.d(27): Error: static array `S[0][4294967295]` size overflowed to 4294967295
+fail_compilation/staticarrayoverflow.d(27): Error: variable `staticarrayoverflow.c` size overflow
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/staticforeach4.d b/gcc/testsuite/gdc.test/fail_compilation/staticforeach4.d
new file mode 100644
index 00000000000..93ba6e40e08
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/staticforeach4.d
@@ -0,0 +1,17 @@
+/*
+REQUIRED_ARGS: -verrors=context
+TEST_OUTPUT:
+---
+fail_compilation/staticforeach4.d(16): Error: index type `byte` cannot cover index range 0..257
+static foreach (byte a, int b; data) { }
+ ^
+fail_compilation/staticforeach4.d(17): Error: index type `byte` cannot cover index range 0..257
+static foreach (byte a, int b; fn()) { }
+ ^
+---
+*/
+immutable int[257] data = 1;
+int[257] fn() { return data; }
+
+static foreach (byte a, int b; data) { }
+static foreach (byte a, int b; fn()) { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/switches.d b/gcc/testsuite/gdc.test/fail_compilation/switches.d
index c96ddb27288..da66d0ea986 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/switches.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/switches.d
@@ -1,8 +1,3 @@
-/*
-REQUIRED_ARGS:
-PERMUTE_ARGS:
-*/
-
/************************************************************/
/*
@@ -29,7 +24,7 @@ void test1(int i)
/*
TEST_OUTPUT:
---
-fail_compilation/switches.d(205): Error: no case statement following goto case;
+fail_compilation/switches.d(205): Error: no `case` statement following `goto case;`
---
*/
@@ -50,7 +45,7 @@ void test2(int i)
/*
TEST_OUTPUT:
---
-fail_compilation/switches.d(302): Deprecation: 'switch' skips declaration of variable switches.test3.j at fail_compilation/switches.d(306)
+fail_compilation/switches.d(302): Error: `switch` skips declaration of variable `switches.test3.j` at fail_compilation/switches.d(306)
---
*/
@@ -72,3 +67,29 @@ void test3(int i)
}
+/************************************************************/
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/switches.d(404): Error: `switch` skips declaration of variable `switches.test.z` at fail_compilation/switches.d(406)
+---
+*/
+
+#line 400
+// https://issues.dlang.org/show_bug.cgi?id=18858
+
+int test(int n)
+{
+ final switch(n)
+ {
+ int z = 5;
+ enum e = 6;
+
+ case 1:
+ int y = 2;
+ return y;
+ }
+}
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test10.d b/gcc/testsuite/gdc.test/fail_compilation/test10.d
new file mode 100644
index 00000000000..4c1425772b2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test10.d
@@ -0,0 +1,12 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test10.d(10): Error: found `else` without a corresponding `if`, `version` or `debug` statement
+---
+*/
+
+void test(int i)
+{
+ ++i;
+ else
+ ++i;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test1021.d b/gcc/testsuite/gdc.test/fail_compilation/test1021.d
new file mode 100644
index 00000000000..0792545d211
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test1021.d
@@ -0,0 +1,171 @@
+/* REQUIRED_ARGS: -preview=dip1021
+ */
+
+@safe:
+
+/* TEST_OUTPUT:
+---
+fail_compilation/test1021.d(1009): Error: more than one mutable reference of `p` in arguments to `test1021.fooa()`
+fail_compilation/test1021.d(1010): Error: mutable and const references of `p` in arguments to `test1021.foob()`
+fail_compilation/test1021.d(1011): Error: mutable and const references of `p` in arguments to `test1021.fooc()`
+fail_compilation/test1021.d(1013): Error: more than one mutable reference of `p` in arguments to `test1021.fooe()`
+---
+*/
+
+#line 1000
+
+void fooa(int*, int*);
+void foob(const(int)*, int*);
+void fooc(int*, const(int)*);
+void food(const(int)*, const(int)*);
+void fooe(int*, ...);
+
+void test1(int* p)
+{
+ fooa(p, p); // error
+ foob(p, p); // error
+ fooc(p, p); // error
+ food(p, p); // ok
+ fooe(p, p); // error
+}
+
+/***********************************/
+
+/* TEST_OUTPUT:
+---
+fail_compilation/test1021.d(2010): Error: more than one mutable reference to `i` in arguments to `test1021.fopa()`
+fail_compilation/test1021.d(2011): Error: mutable and const references to `i` in arguments to `test1021.fopb()`
+fail_compilation/test1021.d(2012): Error: mutable and const references to `i` in arguments to `test1021.fopc()`
+fail_compilation/test1021.d(2014): Error: more than one mutable reference to `i` in arguments to `test1021.fope()`
+---
+*/
+
+#line 2000
+
+void fopa(ref int, scope int*);
+void fopb(ref int, scope const int*);
+void fopc(ref const int, scope int*);
+void fopd(ref const int, scope const int*);
+inout(int) fope(ref inout int, scope int*);
+void test2()
+{
+ int i;
+ @trusted int* toPtr(ref int i) { return &i; }
+ fopa(i, toPtr(i)); // error
+ fopb(i, toPtr(i)); // error
+ fopc(i, toPtr(i)); // error
+ fopd(i, toPtr(i)); // ok
+ fope(i, toPtr(i)); // error
+}
+
+/***********************************/
+
+/* TEST_OUTPUT:
+---
+fail_compilation/test1021.d(3015): Error: more than one mutable reference to `s` in arguments to `test1021.S.method()`
+fail_compilation/test1021.d(3019): Error: more than one mutable reference of `c` in arguments to `test1021.C.method()`
+---
+*/
+
+#line 3000
+
+struct S
+{
+ void method(ref S s);
+}
+
+class C
+{
+ void method(C c);
+}
+
+void test3()
+{
+ S s;
+ S* ps;
+ s.method(s); // error
+ ps.method(s); // ok
+
+ C c;
+ c.method(c); // error
+}
+
+/***********************************/
+
+/* TEST_OUTPUT:
+---
+fail_compilation/test1021.d(4008): Error: more than one mutable reference to `i` in arguments to `test1021.test4.nested()`
+---
+*/
+
+#line 4000
+
+void test4()
+{
+ int i, k;
+ int nested(ref int j)
+ {
+ return i + j;
+ }
+ nested(i); // error
+ nested(k); // ok
+}
+
+/***********************************/
+
+/* TEST_OUTPUT:
+---
+fail_compilation/test1021.d(5012): Error: more than one mutable reference of `s` in arguments to `test1021.foo5()`
+---
+*/
+
+#line 5000
+
+struct S5
+{
+ int i;
+ int* p;
+}
+
+void foo5(S5, S5);
+
+void test5()
+{
+ S5 s;
+ foo5(s, s);
+}
+
+alias A5 = void delegate() const;
+
+void foo5(A5, A5);
+
+void test5a()
+{
+ A5 a;
+ foo5(a, a);
+}
+
+alias B5 = void function();
+
+void foo5(B5, B5);
+
+void test5b()
+{
+ B5 b;
+ foo5(b, b);
+}
+
+struct S5c
+{
+ void function() fp;
+}
+
+void foo5(S5c, S5c);
+
+void test5c()
+{
+ S5c s;
+ foo5(s, s);
+}
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test11006.d b/gcc/testsuite/gdc.test/fail_compilation/test11006.d
new file mode 100644
index 00000000000..e7257b7c2e3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test11006.d
@@ -0,0 +1,11 @@
+/* REQUIRED_ARGS: -main -de
+ * TEST_OUTPUT:
+---
+fail_compilation/test11006.d(10): Deprecation: cannot subtract pointers to different types: `void*` and `int*`.
+fail_compilation/test11006.d(10): while evaluating: `static assert(2L == 2L)`
+fail_compilation/test11006.d(11): Deprecation: cannot subtract pointers to different types: `int*` and `void*`.
+fail_compilation/test11006.d(11): while evaluating: `static assert(8L == 8L)`
+---
+ */
+static assert(cast(void*)8 - cast(int*) 0 == 2L);
+static assert(cast(int*) 8 - cast(void*)0 == 8L);
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test11047.d b/gcc/testsuite/gdc.test/fail_compilation/test11047.d
index ef8007d434b..2cbc9c6bcd4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test11047.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test11047.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/test11047.d(11): Error: value of x is not known at compile time
-fail_compilation/test11047.d(11): Error: value of x is not known at compile time
+fail_compilation/test11047.d(11): Error: value of `x` is not known at compile time
+fail_compilation/test11047.d(11): Error: value of `x` is not known at compile time
---
*/
// https://issues.dlang.org/show_bug.cgi?id=11047
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test11176.d b/gcc/testsuite/gdc.test/fail_compilation/test11176.d
index 020d807d8b8..6c94b4010f4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test11176.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test11176.d
@@ -1,9 +1,8 @@
/*
-REQUIRED_ARGS: -de
TEST_OUTPUT:
---
-fail_compilation/test11176.d(12): Deprecation: b.ptr cannot be used in @safe code, use &b[0] instead
-fail_compilation/test11176.d(16): Deprecation: b.ptr cannot be used in @safe code, use &b[0] instead
+fail_compilation/test11176.d(12): Error: `b.ptr` cannot be used in `@safe` code, use `&b[0]` instead
+fail_compilation/test11176.d(16): Error: `b.ptr` cannot be used in `@safe` code, use `&b[0]` instead
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test12228.d b/gcc/testsuite/gdc.test/fail_compilation/test12228.d
new file mode 100644
index 00000000000..522bceba1e3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test12228.d
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test12228.d(13): Error: Using `this` as a type is obsolete. Use `typeof(this)` instead
+fail_compilation/test12228.d(18): Error: no property `x` for type `object.Object`
+fail_compilation/test12228.d(19): Error: Using `super` as a type is obsolete. Use `typeof(super)` instead
+fail_compilation/test12228.d(20): Error: Using `super` as a type is obsolete. Use `typeof(super)` instead
+---
+*/
+
+class C
+{
+ shared(this) x;
+}
+
+class D : C
+{
+ alias x = typeof(super).x;
+ shared(super) a;
+ super b;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test12385.d b/gcc/testsuite/gdc.test/fail_compilation/test12385.d
new file mode 100644
index 00000000000..cbba723fe74
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test12385.d
@@ -0,0 +1,30 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test12385.d(29): Error: cannot modify `immutable` expression `unbundled.x`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=12385
+
+class BundledState
+{
+ string m_State;
+
+ int x = 3;
+
+ this(string state) immutable
+ {
+ m_State = state;
+ }
+}
+
+enum States : immutable(BundledState)
+{
+ unbundled = new immutable BundledState("bla"),
+}
+
+void main()
+{
+ States.unbundled.x = 6; // Modifies x.
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test12558.d b/gcc/testsuite/gdc.test/fail_compilation/test12558.d
new file mode 100644
index 00000000000..5ff7c97804a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test12558.d
@@ -0,0 +1,57 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test12558.d(32): Deprecation: `catch` statement without an exception specification is deprecated
+fail_compilation/test12558.d(32): use `catch(Throwable)` for old behavior
+fail_compilation/test12558.d(36): Deprecation: `catch` statement without an exception specification is deprecated
+fail_compilation/test12558.d(36): use `catch(Throwable)` for old behavior
+fail_compilation/test12558.d(43): Deprecation: `catch` statement without an exception specification is deprecated
+fail_compilation/test12558.d(43): use `catch(Throwable)` for old behavior
+fail_compilation/test12558.d(47): Deprecation: `catch` statement without an exception specification is deprecated
+fail_compilation/test12558.d(47): use `catch(Throwable)` for old behavior
+fail_compilation/test12558.d(56): Deprecation: `catch` statement without an exception specification is deprecated
+fail_compilation/test12558.d(56): use `catch(Throwable)` for old behavior
+fail_compilation/test12558.d(31): Error: `catch` statement without an exception specification is deprecated
+fail_compilation/test12558.d(31): use `catch(Throwable)` for old behavior
+fail_compilation/test12558.d(36): Error: `catch` statement without an exception specification is deprecated
+fail_compilation/test12558.d(36): use `catch(Throwable)` for old behavior
+fail_compilation/test12558.d(42): Error: `catch` statement without an exception specification is deprecated
+fail_compilation/test12558.d(42): use `catch(Throwable)` for old behavior
+fail_compilation/test12558.d(47): Error: `catch` statement without an exception specification is deprecated
+fail_compilation/test12558.d(47): use `catch(Throwable)` for old behavior
+---
+*/
+
+void main()
+{
+ auto handler = () { };
+
+ try {
+ assert(0);
+ } catch
+ handler();
+
+ try {
+ assert(0);
+ } catch {
+ handler();
+ }
+
+ try {
+ assert(0);
+ } catch
+ handler();
+
+ try {
+ assert(0);
+ } catch {
+ handler();
+ }
+}
+
+void foo()()
+{
+ try {}
+ catch
+ assert(false);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test12822.d b/gcc/testsuite/gdc.test/fail_compilation/test12822.d
index fe710981fe3..510d9e2a9f1 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test12822.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test12822.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/test12822.d(13): Error: cannot modify delegate pointer in @safe code dg.ptr
-fail_compilation/test12822.d(14): Error: dg.funcptr cannot be used in @safe code
+fail_compilation/test12822.d(13): Error: cannot modify delegate pointer in `@safe` code `dg.ptr`
+fail_compilation/test12822.d(14): Error: `dg.funcptr` cannot be used in `@safe` code
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test13152.d b/gcc/testsuite/gdc.test/fail_compilation/test13152.d
index 814dcb7d3f0..8033e281768 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test13152.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test13152.d
@@ -1,7 +1,8 @@
/*
+EXTRA_FILES: imports/test13152a.d imports/test13152b.d imports/test13152c.d imports/test13152d.d imports/test13152e.d imports/test13152f.d imports/test13152g.d imports/test13152h.d imports/test13152i.d imports/test13152j.d imports/test13152k.d imports/test13152l.d imports/test13152m.d imports/test13152n.d imports/test13152o.d imports/test13152p.d imports/test13152q.d imports/test13152r.d imports/test13152s.d imports/test13152t.d imports/test13152u.d imports/test13152v.d imports/test13152w.d imports/test13152x.d imports/test13152y.d imports/test13152z.d
TEST_OUTPUT:
---
-fail_compilation/test13152.d(11): Error: undefined identifier `x`
+fail_compilation/test13152.d(12): Error: undefined identifier `x`
---
*/
import imports.test13152a;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test13536.d b/gcc/testsuite/gdc.test/fail_compilation/test13536.d
index 800757dcf6c..e29861f5b97 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test13536.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test13536.d
@@ -1,10 +1,9 @@
/*
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/test13536.d(24): Error: field U.sysDg cannot access pointers in @safe code that overlap other fields
-fail_compilation/test13536.d(24): Error: address of variable `s` assigned to `u` with longer lifetime
-fail_compilation/test13536.d(25): Error: field U.safeDg cannot access pointers in @safe code that overlap other fields
+fail_compilation/test13536.d(23): Error: field `U.sysDg` cannot access pointers in `@safe` code that overlap other fields
+fail_compilation/test13536.d(23): Error: address of variable `s` assigned to `u` with longer lifetime
+fail_compilation/test13536.d(24): Error: field `U.safeDg` cannot access pointers in `@safe` code that overlap other fields
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test13537.d b/gcc/testsuite/gdc.test/fail_compilation/test13537.d
index 0e087dd5640..a92a7c1aa21 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test13537.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test13537.d
@@ -1,11 +1,10 @@
/*
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/test13537.d(32): Error: field U.y cannot modify fields in @safe code that overlap fields with other storage classes
-fail_compilation/test13537.d(33): Error: field U.y cannot modify fields in @safe code that overlap fields with other storage classes
-fail_compilation/test13537.d(34): Error: field U.z cannot access pointers in @safe code that overlap other fields
-fail_compilation/test13537.d(35): Error: field U.y cannot modify fields in @safe code that overlap fields with other storage classes
+fail_compilation/test13537.d(31): Error: field `U.y` cannot modify fields in `@safe` code that overlap fields with other storage classes
+fail_compilation/test13537.d(32): Error: field `U.y` cannot modify fields in `@safe` code that overlap fields with other storage classes
+fail_compilation/test13537.d(33): Error: field `U.z` cannot access pointers in `@safe` code that overlap other fields
+fail_compilation/test13537.d(34): Error: field `U.y` cannot modify fields in `@safe` code that overlap fields with other storage classes
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test13667.d b/gcc/testsuite/gdc.test/fail_compilation/test13667.d
new file mode 100644
index 00000000000..e63e21e6f94
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test13667.d
@@ -0,0 +1,101 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test13667.d(112): Error: cannot cast expression `this` of type `const(Array1!int)` to `Array1!int*`
+fail_compilation/test13667.d(116): Error: template instance `test13667.Array1!int` error instantiating
+fail_compilation/test13667.d(121): Error: cannot cast expression `this` of type `const(Array2!int)` to `B*`
+fail_compilation/test13667.d(125): Error: template instance `test13667.Array2!int` error instantiating
+fail_compilation/test13667.d(136): Error: cannot cast expression `this` of type `const(Array3!int)` to `C*`
+fail_compilation/test13667.d(140): Error: template instance `test13667.Array3!int` error instantiating
+fail_compilation/test13667.d(151): Error: cannot cast expression `this` of type `const(Array4!int)` to `D*`
+fail_compilation/test13667.d(155): Error: template instance `test13667.Array4!int` error instantiating
+fail_compilation/test13667.d(172): Error: cannot cast expression `this` of type `const(Array5!int)` to `F*`
+fail_compilation/test13667.d(176): Error: template instance `test13667.Array5!int` error instantiating
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=13667
+
+#line 100
+// 0, no error but also crashed before
+struct Array0(T)
+{
+ Array0!(T) impConv() const { return cast(typeof(return))this; }
+ alias impConv this;
+}
+
+alias AI0 = Array0!int;
+
+// 1
+struct Array1(T)
+{
+ Array1!(T) impConv() const { return *cast(typeof(return)*)this; }
+ alias impConv this;
+}
+
+alias AI1 = Array1!int;
+
+// 2
+struct Array2(T)
+{
+ B impConv() const { return cast(B*)this; }
+ alias impConv this;
+}
+
+alias AI2 = Array2!int;
+
+struct B
+{
+ AI2 get() { return AI2(); }
+ alias get this;
+}
+
+// 3
+struct Array3(T)
+{
+ C impConv() const { return cast(C*)this; }
+ alias impConv this;
+}
+
+alias AI3 = Array3!int;
+
+struct C
+{
+ C get() { return C(); }
+ alias get this;
+}
+
+// 4
+struct Array4(T)
+{
+ D impConv() const { return cast(D*)this; }
+ alias impConv this;
+}
+
+alias AI4 = Array4!int;
+
+struct D
+{
+ E get() { return E(); }
+ alias get this;
+}
+
+struct E
+{
+ AI4 ai;
+ alias ai this;
+}
+
+// 5: test enum based on struct, needed to use base type (toBasetype())
+struct Array5(T)
+{
+ F impConv() const { return cast(F*)this; }
+ alias impConv this;
+}
+
+alias AI5 = Array5!int;
+
+enum F : AI5
+{
+ f = AI5()
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test13786.d b/gcc/testsuite/gdc.test/fail_compilation/test13786.d
index 3b05dcc40dc..3524875e3ca 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test13786.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test13786.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/test13786.d(14): Error: debug 123 level declaration must be at module level
-fail_compilation/test13786.d(15): Error: debug abc declaration must be at module level
-fail_compilation/test13786.d(16): Error: version 123 level declaration must be at module level
-fail_compilation/test13786.d(17): Error: version abc declaration must be at module level
-fail_compilation/test13786.d(20): Error: template instance test13786.T!() error instantiating
+fail_compilation/test13786.d(14): Error: debug `123` level declaration must be at module level
+fail_compilation/test13786.d(15): Error: debug `abc` declaration must be at module level
+fail_compilation/test13786.d(16): Error: version `123` level declaration must be at module level
+fail_compilation/test13786.d(17): Error: version `abc` declaration must be at module level
+fail_compilation/test13786.d(20): Error: template instance `test13786.T!()` error instantiating
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test13867.d b/gcc/testsuite/gdc.test/fail_compilation/test13867.d
index aebab2badf9..36543f92ebe 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test13867.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test13867.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/test13867.d(12): Error: function test13867.X.blah does not override any function, did you mean to override 'extern (C++) test13867.Base.blah'?
-fail_compilation/test13867.d(19): Error: function test13867.Z.blah does not override any function, did you mean to override 'extern (C++) test13867.Base.blah'?
+fail_compilation/test13867.d(12): Error: function `void test13867.X.blah()` does not override any function, did you mean to override `extern (C++) void test13867.Base.blah()`?
+fail_compilation/test13867.d(19): Error: function `void test13867.Z.blah()` does not override any function, did you mean to override `extern (C++) void test13867.Base.blah()`?
---
*/
extern (C++) class Base {
@@ -15,14 +15,14 @@ extern (C++) class Y : Base {
override void blah(){}
}
class Z : Base {
- alias blah = super.blah;
+ alias blah = typeof(super).blah;
override void blah(){}//Error
}
class O : Base {
extern (C++) override void blah(){}
}
extern (C++) class OK : Base {
- alias blah = super.blah;
+ alias blah = typeof(super).blah;
override void blah(){}
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test14064.d b/gcc/testsuite/gdc.test/fail_compilation/test14064.d
new file mode 100644
index 00000000000..9513e47fdb0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test14064.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test14064.d(11): Error: `private` is a keyword, not an `@` attribute
+fail_compilation/test14064.d(12): Error: `deprecated` is a keyword, not an `@` attribute
+fail_compilation/test14064.d(13): Error: `pure` is a keyword, not an `@` attribute
+fail_compilation/test14064.d(14): Error: `nothrow` is a keyword, not an `@` attribute
+fail_compilation/test14064.d(15): Error: `in` is a keyword, not an `@` attribute
+---
+*/
+@private int v;
+@deprecated void foo();
+int goo() @pure;
+@nothrow unittest {};
+void zoom(@in int x);
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test14238.d b/gcc/testsuite/gdc.test/fail_compilation/test14238.d
index 2d5f58630b2..e0d0b35c4d6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test14238.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test14238.d
@@ -1,9 +1,8 @@
-/* REQUIRED_ARGS: -dip1000
- PERMUTE_ARGS:
+/* REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test14238.d(21): Error: scope variable `fn` may not be returned
-fail_compilation/test14238.d(29): Error: escaping reference to stack allocated value returned by `&baz`
+fail_compilation/test14238.d(20): Error: scope variable `fn` may not be returned
+fail_compilation/test14238.d(28): Error: escaping reference to stack allocated value returned by `&baz`
---
*/
// https://issues.dlang.org/show_bug.cgi?id=14238
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test143.d b/gcc/testsuite/gdc.test/fail_compilation/test143.d
index c95ecc111fa..1a1ed70698b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test143.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test143.d
@@ -4,7 +4,7 @@
/*
TEST_OUTPUT:
---
-fail_compilation/test143.d(19): Error: undefined identifier `x`
+fail_compilation/test143.d(20): Error: undefined identifier `x`
---
*/
module test143;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test14496.d b/gcc/testsuite/gdc.test/fail_compilation/test14496.d
index 8575e7276ef..2835907210e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test14496.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test14496.d
@@ -1,11 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/test14496.d(21): Error: variable test14496.foo.f void initializers for pointers not allowed in safe functions
-fail_compilation/test14496.d(24): Error: variable test14496.foo.Bar.foo void initializers for pointers not allowed in safe functions
-fail_compilation/test14496.d(28): Error: variable test14496.foo.Baz.x void initializers for pointers not allowed in safe functions
-fail_compilation/test14496.d(48): Error: variable test14496.sinister.bar void initializers for pointers not allowed in safe functions
-fail_compilation/test14496.d(49): Error: variable test14496.sinister.baz void initializers for pointers not allowed in safe functions
+fail_compilation/test14496.d(21): Error: variable `test14496.foo.f` `void` initializers for pointers not allowed in safe functions
+fail_compilation/test14496.d(24): Error: variable `test14496.foo.Bar.foo` `void` initializers for pointers not allowed in safe functions
+fail_compilation/test14496.d(28): Error: variable `test14496.foo.Baz.x` `void` initializers for pointers not allowed in safe functions
+fail_compilation/test14496.d(48): Error: variable `test14496.sinister.bar` `void` initializers for pointers not allowed in safe functions
+fail_compilation/test14496.d(49): Error: variable `test14496.sinister.baz` `void` initializers for pointers not allowed in safe functions
---
*/
// https://issues.dlang.org/show_bug.cgi?id=14496
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test14538.d b/gcc/testsuite/gdc.test/fail_compilation/test14538.d
index b0b76a7f505..1ad2126fd43 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test14538.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test14538.d
@@ -1,9 +1,8 @@
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/test14538.d(19): Error: cannot implicitly convert expression `x ? cast(uint)this.fCells[x].code : 32u` of type `uint` to `Cell`
+fail_compilation/test14538.d(18): Error: cannot implicitly convert expression `x ? cast(uint)this.fCells[x].code : 32u` of type `uint` to `Cell`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15117.d b/gcc/testsuite/gdc.test/fail_compilation/test15117.d
deleted file mode 100644
index e6ce3b5bf11..00000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/test15117.d
+++ /dev/null
@@ -1,30 +0,0 @@
-// REQUIRED_ARGS: -o-
-// PERMUTE_ARGS:
-// COMPILED_IMPORTS: imports/test15117a.d
-/*
-TEST_OUTPUT:
----
-fail_compilation/test15177.d-mixin-20(20): Error: `imports.test15117a.object` is not visible from module `test15177`
-fail_compilation/test15177.d(29): Error: template instance `test15177.RunApiTest!()` error instantiating
----
-*/
-
-import users = imports.test15117a;
-
-void RunApiTest(T...)()
-{
- foreach (name; __traits(allMembers, users))
- {
- // 3. list the name of TyepInfoStructDeclaration,
- // but it's just internal symbol and invisible.
- mixin("alias func = users . " ~ name ~ ";");
- }
-}
-
-void main()
-{
- // 1. run semantic3 of users.test_usr_1
- users.test_usr_1();
-
- RunApiTest!();
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15177.d b/gcc/testsuite/gdc.test/fail_compilation/test15177.d
new file mode 100644
index 00000000000..b0ae7ae53d4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15177.d
@@ -0,0 +1,29 @@
+// REQUIRED_ARGS: -o-
+// COMPILED_IMPORTS: imports/test15117a.d
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test15177.d-mixin-19(19): Error: `imports.test15117a.object` is not visible from module `test15177`
+fail_compilation/test15177.d(28): Error: template instance `test15177.RunApiTest!()` error instantiating
+---
+*/
+
+import users = imports.test15117a;
+
+void RunApiTest(T...)()
+{
+ foreach (name; __traits(allMembers, users))
+ {
+ // 3. list the name of TyepInfoStructDeclaration,
+ // but it's just internal symbol and invisible.
+ mixin("alias func = users . " ~ name ~ ";");
+ }
+}
+
+void main()
+{
+ // 1. run semantic3 of users.test_usr_1
+ users.test_usr_1();
+
+ RunApiTest!();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15191.d b/gcc/testsuite/gdc.test/fail_compilation/test15191.d
index 55b09b799f2..173473a6675 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15191.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15191.d
@@ -1,6 +1,6 @@
/* TEST_OUTPUT:
---
-fail_compilation/test15191.d(17): Error: cannot take address of ref return of foo() in @safe function bar
+fail_compilation/test15191.d(17): Error: cannot take address of `ref return` of `foo()` in `@safe` function `bar`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15306.d b/gcc/testsuite/gdc.test/fail_compilation/test15306.d
index ad51371b2bd..ff532aea220 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15306.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15306.d
@@ -1,10 +1,8 @@
/*
-REQUIRED_ARGS:
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/test15306.d(17): Error: immutable delegate 'test15306.main.__dgliteral1' cannot access mutable data 'i'
-fail_compilation/test15306.d(21): Error: shared delegate 'test15306.main.__dgliteral2' cannot access non-shared data 'p'
+fail_compilation/test15306.d(15): Error: `immutable` delegate `test15306.main.__dgliteral2` cannot access mutable data `i`
+fail_compilation/test15306.d(19): Error: `shared` delegate `test15306.main.__dgliteral5` cannot access non-shared data `p`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15373.d b/gcc/testsuite/gdc.test/fail_compilation/test15373.d
new file mode 100644
index 00000000000..1b3cecd2d02
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15373.d
@@ -0,0 +1,22 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test15373.d(21): Error: Runtime type information is not supported for `extern(C++)` classes
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=15373
+
+// Using `typeid` on an `extern(C++) class` type is ok as it is evaluated at compile-time
+// See test/runnable/test15373.d
+
+// Using `typeid` on an `extern(C++) class` instance is not ok because `extern(C++) class`
+// instances are not rooted in `Object`
+
+extern(C++) class C { }
+
+void foo()
+{
+ C c = new C();
+ auto ti = typeid(c);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15399.d b/gcc/testsuite/gdc.test/fail_compilation/test15399.d
index bad3ed29554..00ec33a43bf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15399.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15399.d
@@ -1,14 +1,14 @@
/* https://issues.dlang.org/show_bug.cgi?id=15399
TEST_OUTPUT:
---
-fail_compilation/test15399.d(31): Error: writing to misaligned pointer in field S1.ptr is not @safe
-fail_compilation/test15399.d(32): Error: writing to misaligned pointer in field S2.ptr is not @safe
-fail_compilation/test15399.d(33): Error: taking address of misaligned pointer in field S1.ptr is not @safe
-fail_compilation/test15399.d(34): Error: taking address of misaligned pointer in field S2.ptr is not @safe
-fail_compilation/test15399.d(35): Error: 'ref' of misaligned pointer in field S1.ptr is not @safe
-fail_compilation/test15399.d(36): Error: 'ref' of misaligned pointer in field S2.ptr is not @safe
-fail_compilation/test15399.d(37): Error: 'out' of misaligned pointer in field S1.ptr is not @safe
-fail_compilation/test15399.d(38): Error: 'out' of misaligned pointer in field S2.ptr is not @safe
+fail_compilation/test15399.d(32): Error: field `S1.ptr` cannot modify misaligned pointers in `@safe` code
+fail_compilation/test15399.d(33): Error: field `S2.ptr` cannot modify misaligned pointers in `@safe` code
+fail_compilation/test15399.d(34): Error: field `S1.ptr` cannot modify misaligned pointers in `@safe` code
+fail_compilation/test15399.d(35): Error: field `S2.ptr` cannot modify misaligned pointers in `@safe` code
+fail_compilation/test15399.d(36): Error: field `S1.ptr` cannot modify misaligned pointers in `@safe` code
+fail_compilation/test15399.d(37): Error: field `S2.ptr` cannot modify misaligned pointers in `@safe` code
+fail_compilation/test15399.d(38): Error: field `S1.ptr` cannot modify misaligned pointers in `@safe` code
+fail_compilation/test15399.d(39): Error: field `S2.ptr` cannot modify misaligned pointers in `@safe` code
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15544.d b/gcc/testsuite/gdc.test/fail_compilation/test15544.d
index 9757dbf3dd7..eeb4492b34d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15544.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15544.d
@@ -1,10 +1,9 @@
/*
-REQUIRED_ARGS: -dip1000
-PERMUTE_ARGS:
+REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test15544.d(21): Error: reference to local `this` assigned to non-scope `_del` in @safe code
-fail_compilation/test15544.d(23): Error: reference to local `this` assigned to non-scope `_del` in @safe code
+fail_compilation/test15544.d(20): Error: reference to local `this` assigned to non-scope `_del` in @safe code
+fail_compilation/test15544.d(22): Error: reference to local `this` assigned to non-scope `_del` in @safe code
---
*/
@@ -27,7 +26,7 @@ struct S {
/*
TEST_OUTPUT:
---
-fail_compilation/test15544.d(47): Error: reference to local `y` assigned to non-scope `dg` in @safe code
+fail_compilation/test15544.d(46): Error: reference to local `y` assigned to non-scope `dg` in @safe code
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15660.d b/gcc/testsuite/gdc.test/fail_compilation/test15660.d
new file mode 100644
index 00000000000..c979bb5c6a9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15660.d
@@ -0,0 +1,22 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test15660.d(20): Error: cannot implicitly convert expression `f(v)` of type `int[]` to `immutable(int[])`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=15660
+
+int[] f(ref void[] m) pure
+{
+ auto result = new int[5];
+ m = result;
+ return result;
+}
+
+void main()
+{
+ void[] v;
+ immutable x = f(v);
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15672.d b/gcc/testsuite/gdc.test/fail_compilation/test15672.d
index 19b5bf1c2d1..859521de004 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15672.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15672.d
@@ -1,8 +1,8 @@
/*
* TEST_OUTPUT:
---
-fail_compilation/test15672.d(15): Error: cast from void[] to byte[] not allowed in safe code
-fail_compilation/test15672.d(25): Error: cast from void* to byte* not allowed in safe code
+fail_compilation/test15672.d(15): Error: cast from `void[]` to `byte[]` not allowed in safe code
+fail_compilation/test15672.d(25): Error: cast from `void*` to `byte*` not allowed in safe code
---
*/
// https://issues.dlang.org/show_bug.cgi?id=15672
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15703.d b/gcc/testsuite/gdc.test/fail_compilation/test15703.d
index cf24c3b4481..1665e85e534 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15703.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15703.d
@@ -1,11 +1,10 @@
/*
REQUIRED_ARGS: -m32
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/test15703.d(17): Error: cast from Object[] to uint[] not allowed in safe code
-fail_compilation/test15703.d(19): Error: cast from object.Object to const(uint)* not allowed in safe code
-fail_compilation/test15703.d(22): Error: cast from uint[] to Object[] not allowed in safe code
+fail_compilation/test15703.d(16): Error: cast from `Object[]` to `uint[]` not allowed in safe code
+fail_compilation/test15703.d(18): Error: cast from `object.Object` to `const(uint)*` not allowed in safe code
+fail_compilation/test15703.d(21): Error: cast from `uint[]` to `Object[]` not allowed in safe code
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15704.d b/gcc/testsuite/gdc.test/fail_compilation/test15704.d
index 6b4190611c7..d818033de10 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15704.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15704.d
@@ -1,7 +1,7 @@
/*
* TEST_OUTPUT:
---
-fail_compilation/test15704.d(15): Error: cannot copy void[] to void[] in @safe code
+fail_compilation/test15704.d(15): Error: cannot copy `void[]` to `void[]` in `@safe` code
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15785.d b/gcc/testsuite/gdc.test/fail_compilation/test15785.d
index 474076fd5e4..23a3660dc9a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15785.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15785.d
@@ -1,8 +1,8 @@
-// PERMUTE_ARGS:
+// EXTRA_FILES: imports/test15785.d
/*
TEST_OUTPUT:
---
-fail_compilation/test15785.d(16): Error: no property `foo` for type `imports.test15785.Base`, did you mean `imports.test15785.Base.foo`?
+fail_compilation/test15785.d(16): Error: no property `foo` for type `imports.test15785.Base`
fail_compilation/test15785.d(17): Error: undefined identifier `bar`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15785b.d b/gcc/testsuite/gdc.test/fail_compilation/test15785b.d
index e09b4bfbc7c..910db18ab2d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15785b.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15785b.d
@@ -1,18 +1,17 @@
-// REQUIRED_ARGS:
-// PERMUTE_ARGS:
+// EXTRA_FILES: imports/test15785.d
/*
TEST_OUTPUT:
---
+fail_compilation/test15785b.d(14): Error: `imports.test15785.Base.T` is not visible from module `test15785b`
fail_compilation/test15785b.d(15): Error: `imports.test15785.Base.T` is not visible from module `test15785b`
-fail_compilation/test15785b.d(16): Error: `imports.test15785.Base.T` is not visible from module `test15785b`
-fail_compilation/test15785b.d(17): Error: `imports.test15785.IBase2.T` is not visible from module `test15785b`
+fail_compilation/test15785b.d(16): Error: `imports.test15785.IBase2.T` is not visible from module `test15785b`
---
*/
import imports.test15785;
class Derived : Base, IBase2
{
- super.T t;
+ typeof(super).T t;
Base.T t2;
IBase2.T t3;
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15897.d b/gcc/testsuite/gdc.test/fail_compilation/test15897.d
index aa22c10b76f..e4ade7de2a4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15897.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15897.d
@@ -1,8 +1,9 @@
// REQUIRED_ARGS: -de
+// EXTRA_FILES: imports/test15897.d
/*
TEST_OUTPUT:
---
-fail_compilation/test15897.d(18): Error: no property `create` for type `imports.test15897.Cat`
+fail_compilation/test15897.d(19): Error: no property `create` for type `imports.test15897.Cat`
---
*/
module test15897;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15925.d b/gcc/testsuite/gdc.test/fail_compilation/test15925.d
index 9359859e6fe..88102ac8ff2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15925.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15925.d
@@ -1,5 +1,5 @@
-/* REQUIRED_ARGS:
-PERMUTE_ARGS:
+/*
+EXTRA_FILES: imports/imp15925.d
TEST_OUTPUT:
---
fail_compilation/test15925.d(18): Error: undefined identifier `X`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test15989.d b/gcc/testsuite/gdc.test/fail_compilation/test15989.d
index 29eab9c38cb..a78dec450d0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test15989.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test15989.d
@@ -1,10 +1,9 @@
/*
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/test15989.d(40): Error: variable test15989.main.ctRegex : Unable to initialize enum with class or pointer to struct. Use static const variable instead.
-fail_compilation/test15989.d(49): Error: variable test15989.test.c : Unable to initialize enum with class or pointer to struct. Use static const variable instead.
-fail_compilation/test15989.d(50): Error: cannot use non-constant CTFE pointer in an initializer '&[3][0]'
+fail_compilation/test15989.d(39): Error: variable `test15989.main.ctRegex` : Unable to initialize enum with class or pointer to struct. Use static const variable instead.
+fail_compilation/test15989.d(48): Error: variable `test15989.test.c` : Unable to initialize enum with class or pointer to struct. Use static const variable instead.
+fail_compilation/test15989.d(49): Error: cannot use non-constant CTFE pointer in an initializer `new int(3)`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16095.d b/gcc/testsuite/gdc.test/fail_compilation/test16095.d
index cb164f5edae..38a3a7dfbc9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test16095.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16095.d
@@ -1,11 +1,9 @@
/*
-REQUIRED_ARGS:
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/test16095.d(20): Error: shared method test16095.C.ping is not callable using a non-shared a
-fail_compilation/test16095.d(30): Error: shared method test16095.S.ping is not callable using a non-shared *a
-fail_compilation/test16095.d(43): Error: mutable method test16095.Foo.flip is not callable using a immutable foo
+fail_compilation/test16095.d(18): Error: `shared` method `test16095.C.ping` is not callable using a non-shared `a`
+fail_compilation/test16095.d(28): Error: `shared` method `test16095.S.ping` is not callable using a non-shared `*a`
+fail_compilation/test16095.d(41): Error: mutable method `test16095.Foo.flip` is not callable using a `immutable` `foo`
---
*/
// https://issues.dlang.org/show_bug.cgi?id=16095
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16116.d b/gcc/testsuite/gdc.test/fail_compilation/test16116.d
index be6e7868960..d407aa315b9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test16116.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16116.d
@@ -1,9 +1,8 @@
/*
REQUIRED_ARGS: -m64
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/test16116.d(15): Error: incompatible types for ((v) * (i)): '__vector(short[8])' and 'int'
+fail_compilation/test16116.d(14): Error: incompatible types for `(v) * (i)`: `__vector(short[8])` and `int`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16193.d b/gcc/testsuite/gdc.test/fail_compilation/test16193.d
index de65e8a871c..4124c43173a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test16193.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16193.d
@@ -1,15 +1,14 @@
/*
-REQUIRED_ARGS: -dip1000
-PERMUTE_ARGS:
+REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test16193.d(39): Error: function test16193.abc is @nogc yet allocates closures with the GC
-fail_compilation/test16193.d(41): test16193.abc.__foreachbody1 closes over variable x at fail_compilation/test16193.d(40)
+fail_compilation/test16193.d(38): Error: function `test16193.abc` is `@nogc` yet allocates closures with the GC
+fail_compilation/test16193.d(40): test16193.abc.__foreachbody2 closes over variable x at fail_compilation/test16193.d(39)
---
*/
-//fail_compilation/test16193.d(22): To enforce @safe compiler allocates a closure unless the opApply() uses 'scope'
-//fail_compilation/test16193.d(34): To enforce @safe compiler allocates a closure unless the opApply() uses 'scope'
-//fail_compilation/test16193.d(41): To enforce @safe compiler allocates a closure unless the opApply() uses 'scope'
+//fail_compilation/test16193.d(22): To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`
+//fail_compilation/test16193.d(34): To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`
+//fail_compilation/test16193.d(41): To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`
// https://issues.dlang.org/show_bug.cgi?id=16193
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16195.d b/gcc/testsuite/gdc.test/fail_compilation/test16195.d
index 704974ca30a..a315f33b90c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test16195.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16195.d
@@ -1,7 +1,8 @@
/*
* TEST_OUTPUT:
---
-fail_compilation/test16195.d(13): Error: delete p is not @safe but is used in @safe function test
+fail_compilation/test16195.d(14): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+fail_compilation/test16195.d(14): Error: `delete p` is not `@safe` but is used in `@safe` function `test`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16228.d b/gcc/testsuite/gdc.test/fail_compilation/test16228.d
index 63951e67727..b8622844f69 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test16228.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16228.d
@@ -1,4 +1,4 @@
-/* REQUIRED_ARGS: -dip25
+/* REQUIRED_ARGS:
TEST_OUTPUT:
---
fail_compilation/test16228.d(23): Error: function `test16228.S.bar` `static` member has no `this` to which `return` can apply
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16284.d b/gcc/testsuite/gdc.test/fail_compilation/test16284.d
new file mode 100644
index 00000000000..77b73b0db94
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16284.d
@@ -0,0 +1,27 @@
+/* REQUIRED_ARGS: -preview=fieldwise
+TEST_OUTPUT:
+---
+fail_compilation/test16284.d(24): Error: reinterpretation through overlapped field `s` is not allowed in CTFE
+fail_compilation/test16284.d(27): called from here: `test()`
+fail_compilation/test16284.d(27): while evaluating: `static assert(test())`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=16284
+
+struct S {}
+
+struct T
+{
+ union {int i; S s;}
+ this(uint dummy) { s = S.init; }
+}
+
+bool test()
+{
+ auto t1 = T(0);
+ auto t2 = T(0);
+ return t1 == t2;
+}
+
+static assert(test());
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16365.d b/gcc/testsuite/gdc.test/fail_compilation/test16365.d
index 067361b6428..a11807f645a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test16365.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16365.d
@@ -1,11 +1,10 @@
-/* REQUIRED_ARGS:
- PERMUTE_ARGS:
- TEST_OUTPUT:
+/*
+TEST_OUTPUT:
---
-fail_compilation/test16365.d(22): Error: 'this' reference necessary to take address of member f1 in @safe function main
-fail_compilation/test16365.d(24): Error: cannot implicitly convert expression `&f2` of type `void delegate() pure nothrow @nogc @safe` to `void function() @safe`
-fail_compilation/test16365.d(28): Error: address of variable `s` assigned to `dg` with longer lifetime
-fail_compilation/test16365.d(29): Error: dg.funcptr cannot be used in @safe code
+fail_compilation/test16365.d(21): Error: `this` reference necessary to take address of member `f1` in `@safe` function `main`
+fail_compilation/test16365.d(23): Error: cannot implicitly convert expression `&f2` of type `void delegate() pure nothrow @nogc @safe` to `void function() @safe`
+fail_compilation/test16365.d(27): Error: address of variable `s` assigned to `dg` with longer lifetime
+fail_compilation/test16365.d(28): Error: `dg.funcptr` cannot be used in `@safe` code
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16381.d b/gcc/testsuite/gdc.test/fail_compilation/test16381.d
index ed591bd31dc..b854c27cb12 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test16381.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16381.d
@@ -1,9 +1,8 @@
/*
REQUIRED_ARGS: -m64
-PERMUTE_ARGS:
TEST_OUTPUT:
---
-fail_compilation/test16381.d(16): Error: foo() is not an lvalue
+fail_compilation/test16381.d(15): Error: `foo()` is not an lvalue and cannot be modified
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16523.d b/gcc/testsuite/gdc.test/fail_compilation/test16523.d
index e5de7c19cb1..78563c92525 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test16523.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16523.d
@@ -1,8 +1,7 @@
-// REQUIRED_ARGS: -de
/*
TEST_OUTPUT:
---
-fail_compilation/test16523.d(13): Deprecation: case variables have to be const or immutable
+fail_compilation/test16523.d(12): Error: `case` variables have to be `const` or `immutable`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16589.d b/gcc/testsuite/gdc.test/fail_compilation/test16589.d
index 205347b7603..7b1f14f526f 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test16589.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16589.d
@@ -1,20 +1,20 @@
-/* PERMUTE_ARGS:
-REQUIRED_ARGS: -dip1000
+/*
+REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test16589.d(26): Error: returning `&this.data` escapes a reference to parameter `this`, perhaps annotate with `return`
-fail_compilation/test16589.d(31): Error: returning `&this` escapes a reference to parameter `this`, perhaps annotate with `return`
-fail_compilation/test16589.d(37): Error: returning `&s.data` escapes a reference to parameter `s`, perhaps annotate with `return`
-fail_compilation/test16589.d(42): Error: returning `&s` escapes a reference to parameter `s`, perhaps annotate with `return`
-fail_compilation/test16589.d(47): Error: returning `&s.data` escapes a reference to parameter `s`, perhaps annotate with `return`
-fail_compilation/test16589.d(52): Error: returning `& s` escapes a reference to parameter `s`, perhaps annotate with `return`
+fail_compilation/test16589.d(26): Error: returning `&this.data` escapes a reference to parameter `this`
+fail_compilation/test16589.d(26): perhaps annotate the parameter with `return`
+fail_compilation/test16589.d(31): Error: returning `&this` escapes a reference to parameter `this`
+fail_compilation/test16589.d(31): perhaps annotate the parameter with `return`
+fail_compilation/test16589.d(37): Error: returning `&s.data` escapes a reference to parameter `s`
+fail_compilation/test16589.d(37): perhaps annotate the parameter with `return`
+fail_compilation/test16589.d(42): Error: returning `&s` escapes a reference to parameter `s`
+fail_compilation/test16589.d(42): perhaps annotate the parameter with `return`
+fail_compilation/test16589.d(47): Error: returning `&s.data` escapes a reference to local variable `s`
+fail_compilation/test16589.d(52): Error: returning `& s` escapes a reference to local variable `s`
---
*/
-
-
-
-
// https://issues.dlang.org/show_bug.cgi?id=16589
struct S
@@ -61,5 +61,3 @@ class C
return &data;
}
}
-
-
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16694.d b/gcc/testsuite/gdc.test/fail_compilation/test16694.d
new file mode 100644
index 00000000000..1b15aa435fb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16694.d
@@ -0,0 +1,8 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test16694.d(8): Error: cannot take address of imported symbol `bar` at compile time
+---
+*/
+export void bar();
+auto barptr = &bar;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17284.d b/gcc/testsuite/gdc.test/fail_compilation/test17284.d
new file mode 100644
index 00000000000..a0ea05b0a3a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17284.d
@@ -0,0 +1,20 @@
+TEST_OUTPUT:
+---
+fail_compilation/test17284.d(1): Error: no identifier for declarator `TEST_OUTPUT`
+fail_compilation/test17284.d(1): Error: declaration expected, not `:`
+fail_compilation/test17284.d(12): Error: unrecognized declaration
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=17284
+
+class C { }
+union U { C c; int i; }
+
+@safe void func(T)(T t)
+{
+ t.c = new C;
+}
+
+pragma(msg, typeof(func!U));
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17380spec.d b/gcc/testsuite/gdc.test/fail_compilation/test17380spec.d
new file mode 100644
index 00000000000..2456a590c57
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17380spec.d
@@ -0,0 +1,23 @@
+/* REQUIRED_ARGS: -verrors=spec
+TEST_OUTPUT:
+---
+(spec:1) fail_compilation/test17380spec.d(14): Error: cannot resolve identifier `ThisTypeDoesNotExistAndCrashesTheCompiler`
+(spec:1) fail_compilation/test17380spec.d(14): Error: no property `ThisTypeDoesNotExistAndCrashesTheCompiler` for type `test17380spec.Uint128`
+fail_compilation/test17380spec.d(14): Error: undefined identifier `ThisTypeDoesNotExistAndCrashesTheCompiler`
+---
+ */
+
+struct Int128
+{
+ Uint128 opCast()
+ {
+ return ThisTypeDoesNotExistAndCrashesTheCompiler;
+ }
+ alias opCast this;
+}
+
+struct Uint128
+{
+ Int128 opCast() { return Int128.init; }
+ alias opCast this;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17422.d b/gcc/testsuite/gdc.test/fail_compilation/test17422.d
index 4d203cbdb5b..7bb1315894e 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test17422.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17422.d
@@ -1,5 +1,5 @@
/*
-REQUIRED_ARGS: -dip1000
+REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
fail_compilation/test17422.d(23): Error: scope variable `p` may not be returned
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17423.d b/gcc/testsuite/gdc.test/fail_compilation/test17423.d
new file mode 100644
index 00000000000..66a81c35613
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17423.d
@@ -0,0 +1,29 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test17423.d(26): Error: reference to local `this` assigned to non-scope parameter `dlg` calling test17423.Bar.opApply
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=17423
+
+struct Bar
+{
+ int delegate(int) @safe myDlg;
+
+ auto opApply(int delegate(int) @safe dlg) @safe {
+ myDlg = dlg;
+ return 0;
+ }
+}
+
+struct Foo
+{
+ Bar o;
+ int i = 3;
+
+ this(int x) @safe {
+ foreach(_; o) { i = 0; }
+ i = x;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17450.d b/gcc/testsuite/gdc.test/fail_compilation/test17450.d
index 58a5c7dafc5..6d0e25a3950 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test17450.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17450.d
@@ -1,9 +1,11 @@
/*
-REQUIRED_ARGS: -dip1000 -dip25
+REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
-fail_compilation/test17450.d(15): Error: returning `&s.bar` escapes a reference to parameter `s`, perhaps annotate with `return`
-fail_compilation/test17450.d(18): Error: returning `&this.bar` escapes a reference to parameter `this`, perhaps annotate with `return`
+fail_compilation/test17450.d(17): Error: returning `&s.bar` escapes a reference to parameter `s`
+fail_compilation/test17450.d(17): perhaps annotate the parameter with `return`
+fail_compilation/test17450.d(20): Error: returning `&this.bar` escapes a reference to parameter `this`
+fail_compilation/test17450.d(20): perhaps annotate the parameter with `return`
---
*/
// https://issues.dlang.org/show_bug.cgi?id=17450
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17451.d b/gcc/testsuite/gdc.test/fail_compilation/test17451.d
index fc2f2d22c76..a7ef88a0dba 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test17451.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17451.d
@@ -1,9 +1,9 @@
/* TEST_OUTPUT:
---
fail_compilation/test17451.d(22): Error: undefined identifier `allocator`
-fail_compilation/test17451.d(23): Error: `long` has no effect in expression `false`
-fail_compilation/test17451.d(30): Error: variable test17451.HashMap!(ThreadSlot).HashMap.__lambda2.v size of type ThreadSlot is invalid
-fail_compilation/test17451.d(44): Error: template instance test17451.HashMap!(ThreadSlot) error instantiating
+fail_compilation/test17451.d(23): Error: `false` has no effect
+fail_compilation/test17451.d(30): Error: variable `test17451.HashMap!(ThreadSlot).HashMap.__lambda2.v` size of type `ThreadSlot` is invalid
+fail_compilation/test17451.d(44): Error: template instance `test17451.HashMap!(ThreadSlot)` error instantiating
---
*/
@@ -20,7 +20,7 @@ struct ArraySet(Key)
~this()
{
try allocator;
- catch false; // should never happen
+ catch (Exception e) false; // should never happen
}
}
@@ -40,6 +40,6 @@ struct ThreadSlot {
ArraySet!Task tasks;
}
-class Libevent2ManualEvent {
+class Libevent2ManualEvent : ManualEvent {
HashMap!ThreadSlot m_waiters;
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17586.d b/gcc/testsuite/gdc.test/fail_compilation/test17586.d
new file mode 100644
index 00000000000..fcfb7b7141a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17586.d
@@ -0,0 +1,14 @@
+/* REQUIRED_ARGS: -o- -de
+TEST_OUTPUT:
+---
+fail_compilation/test17586.d(13): Deprecation: `test17586.D.foo` is overriding the deprecated method `test17586.C.foo`
+---
+*/
+
+class C{
+ deprecated void foo(){}
+}
+
+class D : C{
+ override void foo(){}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17868.d b/gcc/testsuite/gdc.test/fail_compilation/test17868.d
new file mode 100644
index 00000000000..4e55c2a85b0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17868.d
@@ -0,0 +1,24 @@
+/*
+TEST_OUTPUT:
+----
+fail_compilation/test17868.d(10): Error: pragma `crt_constructor` takes no argument
+fail_compilation/test17868.d(11): Error: pragma `crt_constructor` takes no argument
+fail_compilation/test17868.d(12): Error: pragma `crt_constructor` takes no argument
+fail_compilation/test17868.d(13): Error: pragma `crt_constructor` takes no argument
+----
+ */
+pragma(crt_constructor, ctfe())
+pragma(crt_constructor, 1.5f)
+pragma(crt_constructor, "foobar")
+pragma(crt_constructor, S())
+void foo()
+{
+}
+
+int ctfe()
+{
+ __gshared int val;
+ return val;
+}
+
+struct S {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17868b.d b/gcc/testsuite/gdc.test/fail_compilation/test17868b.d
new file mode 100644
index 00000000000..5bfff5cf50a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17868b.d
@@ -0,0 +1,16 @@
+/*
+TEST_OUTPUT:
+----
+fail_compilation/test17868b.d(10): Error: function `test17868b.foo` must be `extern(C)` for `pragma(crt_constructor)`
+fail_compilation/test17868b.d(14): Error: function `test17868b.bar` must be `extern(C)` for `pragma(crt_constructor)`
+fail_compilation/test17868b.d(9): Error: pragma `crt_constructor` can only apply to a single declaration
+----
+ */
+pragma(crt_constructor):
+void foo()
+{
+}
+
+void bar()
+{
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17892.d b/gcc/testsuite/gdc.test/fail_compilation/test17892.d
new file mode 100644
index 00000000000..aaa7ad3c1b4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17892.d
@@ -0,0 +1,32 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test17892.d(27): Error: returning `s.pointer1()` escapes a reference to local variable `s`
+fail_compilation/test17892.d(29): Error: returning `s.pointer2()` escapes a reference to local variable `s`
+fail_compilation/test17892.d(31): Error: returning `s.pointer3()` escapes a reference to local variable `s`
+---
+*/
+
+
+// https://issues.dlang.org/show_bug.cgi?id=17892
+
+struct S
+{
+ @safe:
+ int x;
+ int[1] y;
+ auto pointer1() return { return &x; }
+ auto pointer2() return { return &y[0]; }
+ auto pointer3() return { return &y[0..1][0]; }
+}
+
+@safe int* testPointer(int b)
+{
+ S s;
+ if (b == 1)
+ return s.pointer1();
+ else if (b == 2)
+ return s.pointer2();
+ else
+ return s.pointer3();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17959.d b/gcc/testsuite/gdc.test/fail_compilation/test17959.d
new file mode 100644
index 00000000000..cd2216ff045
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17959.d
@@ -0,0 +1,21 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test17959.d(18): Error: scope variable `this` assigned to non-scope `this.escape`
+fail_compilation/test17959.d(19): Error: scope variable `this` assigned to non-scope `this.f`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=17959
+
+class Foo
+{
+ void delegate () @safe escape;
+ Foo f;
+
+ void escfoo() @safe scope
+ {
+ this.escape = &this.escfoo;
+ f = this;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18130.d b/gcc/testsuite/gdc.test/fail_compilation/test18130.d
new file mode 100644
index 00000000000..4309a6b6867
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18130.d
@@ -0,0 +1,10 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test18130.d(8): Error: variable `test18130.foo.v` Zero-length `out` parameters are not allowed.
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=18130
+void foo(out byte[0] v)
+{
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18282.d b/gcc/testsuite/gdc.test/fail_compilation/test18282.d
new file mode 100644
index 00000000000..b85380163e5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18282.d
@@ -0,0 +1,89 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ TEST_OUTPUT:
+---
+fail_compilation/test18282.d(25): Error: scope variable `aa` may not be returned
+fail_compilation/test18282.d(34): Error: copying `& i` into allocated memory escapes a reference to local variable `i`
+fail_compilation/test18282.d(35): Error: copying `& i` into allocated memory escapes a reference to local variable `i`
+fail_compilation/test18282.d(36): Error: scope variable `staa` may not be returned
+fail_compilation/test18282.d(44): Error: copying `S2000(& i)` into allocated memory escapes a reference to local variable `i`
+fail_compilation/test18282.d(53): Error: copying `& i` into allocated memory escapes a reference to local variable `i`
+fail_compilation/test18282.d(53): Error: copying `& c` into allocated memory escapes a reference to local variable `c`
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=18282
+
+string* f() @safe
+{
+ scope string*[] ls;
+ return ls[0];
+}
+
+int* g() @safe
+{
+ scope int*[3] aa;
+ return aa[0];
+}
+
+@safe:
+
+auto bar1()
+{
+ int i = void;
+ int*[1] staa = [ &i ];
+ auto dyna = [ &i ];
+ int*[ ] dynb = [ &i ];
+ return staa[0];
+}
+
+struct S2000 { int* p; }
+
+S2000 bar2()
+{
+ int i;
+ S2000[] arr = [ S2000(&i) ];
+ return arr[0];
+}
+
+
+void bar3()
+{
+ int i;
+ char c;
+ char*[int*] aa = [ &i : &c ];
+}
+
+
+/******************************
+TEST_OUTPUT:
+---
+fail_compilation/test18282.d(1007): Error: copying `& foo` into allocated memory escapes a reference to local variable `foo`
+fail_compilation/test18282.d(1008): Error: copying `& foo` into allocated memory escapes a reference to local variable `foo`
+fail_compilation/test18282.d(1009): Error: copying `& foo` into allocated memory escapes a reference to local variable `foo`
+fail_compilation/test18282.d(1016): Error: copying `&this` into allocated memory escapes a reference to parameter variable `this`
+---
+*/
+
+#line 1000
+
+// https://issues.dlang.org/show_bug.cgi?id=18282
+
+void test18282() @safe
+{
+ string foo = "foo";
+ scope string*[] ls;
+ ls = ls ~ &foo;
+ ls = &foo ~ ls;
+ ls ~= &foo;
+}
+
+struct S
+{
+ auto fun()
+ {
+ arr ~= &this;
+ }
+
+ S*[] arr;
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18385.d b/gcc/testsuite/gdc.test/fail_compilation/test18385.d
new file mode 100644
index 00000000000..3f87885cb91
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18385.d
@@ -0,0 +1,31 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/test18385.d(13): Deprecation: function `test18385.foo` cannot overload `extern(C)` function at fail_compilation/test18385.d(12)
+---
+*/
+
+
+extern (C):
+
+void foo(int) { }
+void foo(double) { }
+
+struct S
+{
+ static void foo(int) {}
+ static void foo(double) {}
+}
+
+void foo2(int) { }
+extern(D) void foo2(double) { } // OK as it has a different mangling
+
+void foo3(int) { }
+void foo3(double); // duplicate declarations are allowed
+
+void foo4();
+void foo4() { }
+
+extern(D) void foo5();
+extern(D) void foo5() { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18385b.d b/gcc/testsuite/gdc.test/fail_compilation/test18385b.d
new file mode 100644
index 00000000000..f0b9d014f84
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18385b.d
@@ -0,0 +1,47 @@
+/*
+Previous implementation raised errors for overloads using alias declarations
+because they ignored the actual function name
+
+TEST_OUTPUT:
+---
+fail_compilation/test18385b.d(13): Error: `test18385b.S.foo` called with argument types `(int)` matches both:
+fail_compilation/test18385b.d(8): `test18385b.S.foo(int s)`
+and:
+fail_compilation/test18385b.d(3): `test18385b.foo(int s)`
+fail_compilation/test18385b.d(102): Error: `test18385b.bar` called with argument types `(int)` matches both:
+fail_compilation/test18385b.d(2): `test18385b.bar(int s)`
+and:
+fail_compilation/test18385b.d(3): `test18385b.foo(int s)`
+---
+*/
+#line 1
+
+void bar(int s) {}
+void foo(int s) {}
+alias bar = foo;
+
+struct S
+{
+ void foo(int s) {}
+ alias foo = bar;
+
+ void useEm()
+ {
+ foo(1);
+ }
+}
+
+// False positive in mutex.d when building druntime
+class Mutex
+{
+ this() {}
+ this() shared {}
+ this(Object obj) {}
+}
+
+#line 100
+void main()
+{
+ bar(0);
+ new Mutex();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18480.d b/gcc/testsuite/gdc.test/fail_compilation/test18480.d
new file mode 100644
index 00000000000..49f306ba988
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18480.d
@@ -0,0 +1,10 @@
+// REQUIRED_ARGS: -i
+/*
+TEST_OUTPUT:
+---
+fail_compilation/imports/test18480a.d(2): Error: `alias TestTemplate = TestTemplate;` cannot alias itself, use a qualified name to create an overload set
+---
+https://issues.dlang.org/show_bug.cgi?id=18480
+*/
+
+import imports.test18480a : TestTemplate;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18484.d b/gcc/testsuite/gdc.test/fail_compilation/test18484.d
new file mode 100644
index 00000000000..678609b770d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18484.d
@@ -0,0 +1,26 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test18484.d(19): Error: returning `x.bar()` escapes a reference to local variable `x`
+fail_compilation/test18484.d(24): Error: escaping reference to stack allocated value returned by `S(0)`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18484
+
+struct S
+{
+ int* bar() return;
+ int i;
+}
+
+int* test1()
+{
+ auto x = S(); return x.bar(); // error
+}
+
+int* test2()
+{
+ return S().bar(); // error
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18554.d b/gcc/testsuite/gdc.test/fail_compilation/test18554.d
new file mode 100644
index 00000000000..acbda50c8c3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18554.d
@@ -0,0 +1,24 @@
+/* REQUIRED_ARGS: -preview=dip1000
+EXTRA_FILES: imports/imp18554.d
+TEST_OUTPUT:
+---
+fail_compilation/test18554.d(16): Error: struct `imp18554.S` variable `i` is not accessible from `@safe` code
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18554
+
+import imports.imp18554;
+
+void test1() @safe
+{
+ S s;
+ s.tupleof[0] = 1;
+}
+
+void test2()
+{
+ S s;
+ s.tupleof[0] = 1;
+}
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18597.d b/gcc/testsuite/gdc.test/fail_compilation/test18597.d
new file mode 100644
index 00000000000..66cde58e047
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18597.d
@@ -0,0 +1,27 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test18597.d(24): Error: field `Unaligned.p` cannot modify misaligned pointers in `@safe` code
+fail_compilation/test18597.d(25): Error: field `Unaligned.p` cannot assign to misaligned pointers in `@safe` code
+fail_compilation/test18597.d(26): Error: field `Unaligned.p` cannot assign to misaligned pointers in `@safe` code
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18597
+
+@safe:
+
+align(1)
+struct Unaligned
+{
+align(1):
+ ubyte filler;
+ int* p;
+}
+
+void test()
+{
+ Unaligned u;
+ u.p = new int;
+ Unaligned v = Unaligned(0, new int);
+ Unaligned w = { p : new int };
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18607.d b/gcc/testsuite/gdc.test/fail_compilation/test18607.d
new file mode 100644
index 00000000000..b3af393bdac
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18607.d
@@ -0,0 +1,18 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test18607.d(10): Error: function `test18607.test!int.test` no `return exp;` or `assert(0);` at end of function
+& test
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18607
+
+int* test(T...)() pure @safe {
+ L:foreach(_; T) {
+ continue L;
+ return null;
+ }
+}
+
+
+pragma(msg, &test!(int));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18644.d b/gcc/testsuite/gdc.test/fail_compilation/test18644.d
new file mode 100644
index 00000000000..20743ea2f4c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18644.d
@@ -0,0 +1,25 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test18644.d(15): Error: storing reference to stack allocated value returned by `foo()` into allocated memory causes it to escape
+fail_compilation/test18644.d(16): Error: escaping reference to stack allocated value returned by `foo()`
+fail_compilation/test18644.d(22): Error: escaping reference to stack allocated value returned by `foo()`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18644
+
+@safe int* test1() {
+ int i;
+ int* foo() { return &i; }
+ int*[] b = [foo()];
+ return foo();
+}
+
+@safe ref int test2() {
+ int i;
+ ref int foo() { return i; }
+ return foo();
+}
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18708.d b/gcc/testsuite/gdc.test/fail_compilation/test18708.d
new file mode 100644
index 00000000000..5c6f16282c2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18708.d
@@ -0,0 +1,64 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test18708.d(24): Error: one path skips field `s`
+fail_compilation/test18708.d(29): Error: one path skips field `s`
+fail_compilation/test18708.d(34): Error: one path skips field `s`
+fail_compilation/test18708.d(39): Error: one path skips field `s`
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=18708
+
+struct S { int y; @disable this(); }
+
+class C
+{
+ S s;
+
+ this(S t)
+ {
+ if (bar(s = t)) foo(); // OK
+ }
+
+ this(S t, int i)
+ {
+ i || bar(s = t);
+ }
+
+ this(S t, int i, int j)
+ {
+ i && bar(s = t);
+ }
+
+ this(S t, int i, long j)
+ {
+ i ? bar(s = t) : i;
+ }
+
+ this(S t, int i, byte j)
+ {
+ i ? i : bar(s = t);
+ }
+}
+
+int bar(S s);
+int foo();
+
+/***********************************/
+
+class E : Exception
+{
+ this(string msg, int line = 0, int pos = 0) pure nothrow @safe
+ {
+ if (line)
+ super("hello");
+ else
+ super(msg);
+ }
+
+ this(string msg, string file, size_t line) pure nothrow @safe
+ {
+ super(msg, file, line);
+ }
+}
+
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test18736.d b/gcc/testsuite/gdc.test/fail_compilation/test18736.d
new file mode 100644
index 00000000000..36c5fd89a1e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test18736.d
@@ -0,0 +1,23 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test18736.d(21): Error: constructor calls not allowed in loops or after labels
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=18736
+
+class A
+{
+ this(char c) { }
+
+ this(int i)
+ {
+ switch (i)
+ {
+ case 1: break;
+ case 2: .. case 4: break;
+ default: break;
+ }
+ this('c');
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19097.d b/gcc/testsuite/gdc.test/fail_compilation/test19097.d
new file mode 100644
index 00000000000..b5560f0b27b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19097.d
@@ -0,0 +1,56 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ * TEST_OUTPUT:
+---
+fail_compilation/test19097.d(35): Error: scope variable `s` may not be returned
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=19097
+
+@safe:
+
+void betty(ref scope int* r, return scope int* p)
+{
+ r = p;
+}
+
+void freddy(out scope int* r, return scope int* p)
+{
+ r = p;
+}
+
+struct S
+{
+ int* a;
+ this(return scope int* b) scope { a = b; }
+
+ int* c;
+ void mem(return scope int* d) scope { c = d; }
+}
+
+S thorin()
+{
+ int i;
+ S s = S(&i); // should infer scope for s
+ return s; // so this should error
+}
+
+/************************/
+
+struct S2(T)
+{
+ int* p;
+
+ void silent(lazy void dg);
+
+ void foo()
+ {
+ char[] name;
+ silent(name = parseType());
+ }
+
+ char[] parseType(char[] name = null);
+}
+
+S2!int s2;
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19107.d b/gcc/testsuite/gdc.test/fail_compilation/test19107.d
new file mode 100644
index 00000000000..98858b437cd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19107.d
@@ -0,0 +1,25 @@
+/*
+EXTRA_FILES: imports/imp19661.d imports/test19107a.d imports/test19107b.d
+TEST_OUTPUT:
+---
+fail_compilation/test19107.d(24): Error: template `test19107.all` cannot deduce function from argument types `!((c) => c)(string[])`
+fail_compilation/test19107.d(18): Candidate is: `all(alias pred, T)(T t)`
+ with `pred = __lambda2,
+ T = string[]`
+ must satisfy the following constraint:
+` is(typeof(I!pred(t)))`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=19107
+
+import imports.test19107b;
+
+void all(alias pred, T)(T t)
+ if (is(typeof(I!pred(t))))
+{ }
+
+void main(string[] args)
+{
+ args.all!(c => c);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19112.d b/gcc/testsuite/gdc.test/fail_compilation/test19112.d
new file mode 100644
index 00000000000..f5bd4035eeb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19112.d
@@ -0,0 +1,16 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test19112.d(13): Error: cannot implicitly convert expression `[123, 456]` of type `int[]` to `int[1]`
+fail_compilation/test19112.d(15): Error: cannot implicitly convert expression `a` of type `int[]` to `int[1]`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=19112
+
+void main()
+{
+ int[int[1]] aa;
+ int* p = [123, 456] in aa;
+ int[] a;
+ p = a in aa;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19176.d b/gcc/testsuite/gdc.test/fail_compilation/test19176.d
new file mode 100644
index 00000000000..ceceb74c9cb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19176.d
@@ -0,0 +1,26 @@
+/*
+REQUIRED_ARGS: -unittest
+TEST_OUTPUT:
+---
+fail_compilation/test19176.d(13): Error: argument `foo()` to __traits(getUnitTests) must be a module or aggregate, not a template
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=19176
+
+void main()
+{
+ __traits(getUnitTests, foo);
+}
+
+template foo()
+{
+ static if(true)
+ {
+ enum bar;
+ }
+ else
+ {
+ enum bar;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19193.d b/gcc/testsuite/gdc.test/fail_compilation/test19193.d
new file mode 100644
index 00000000000..90227761e7f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19193.d
@@ -0,0 +1,22 @@
+/*
+REQUIRED_ARGS: -de
+TEST_OUTPUT
+---
+fail_compilation/test19193.d(13): Deprecation: enum member `test19193.T19193!int.A.b` is deprecated
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=19193
+
+void main ()
+{
+ cast(void)T19193!int.A.b;
+}
+
+template T19193(T)
+{
+ enum A
+ {
+ deprecated b
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19473.d b/gcc/testsuite/gdc.test/fail_compilation/test19473.d
new file mode 100644
index 00000000000..ba6024b000b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19473.d
@@ -0,0 +1,31 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test19473.d(14): Error: union `test19473.P` no size because of forward reference
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=19473
+
+struct A {
+ P p;
+
+ struct UTpl() {
+ union {
+ P p;
+ }
+ }
+
+ alias U = UTpl!();
+}
+
+alias B = A.U;
+
+struct C {
+ union D {
+ B b;
+ }
+}
+
+union P {
+ C.D p;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19646.d b/gcc/testsuite/gdc.test/fail_compilation/test19646.d
new file mode 100644
index 00000000000..ee0b042f7a7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19646.d
@@ -0,0 +1,11 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test19646.d(11): Error: cast from `const(int)*` to `int*` not allowed in safe code
+---
+https://issues.dlang.org/show_bug.cgi?id=19646
+ */
+
+@safe:
+
+const x = 42;
+int* y = cast(int*)&x;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19661.d b/gcc/testsuite/gdc.test/fail_compilation/test19661.d
new file mode 100644
index 00000000000..369c0a86249
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19661.d
@@ -0,0 +1,18 @@
+/*
+EXTRA_FILES: imports/imp19661.d
+TEST_OUTPUT:
+---
+fail_compilation/test19661.d(11): Error: variables cannot be initialized with an expression of type `void`. Use `void` initialization instead.
+---
+*/
+
+module ice19661;
+
+immutable bool testModule = testFunctionMembers!();
+
+void testFunctionMembers()() {
+ import imports.imp19661 : isFunction;
+ foreach(member; __traits(allMembers, ice19661)) {
+ bool b = isFunction!(__traits(getMember, ice19661, member));
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19971.d b/gcc/testsuite/gdc.test/fail_compilation/test19971.d
new file mode 100644
index 00000000000..f854362459b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19971.d
@@ -0,0 +1,17 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test19971.d(15): Error: function `test19971.f(int x)` is not callable using argument types `(string)`
+fail_compilation/test19971.d(15): cannot pass argument `"%s"` of type `string` to parameter `int x`
+fail_compilation/test19971.d(16): Error: function literal `__lambda1(int x)` is not callable using argument types `(string)`
+fail_compilation/test19971.d(16): cannot pass argument `"%s"` of type `string` to parameter `int x`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=19971
+
+void f(int x) {}
+void main()
+{
+ f("%s");
+ (int x) {} ("%s");
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20096.d b/gcc/testsuite/gdc.test/fail_compilation/test20096.d
new file mode 100644
index 00000000000..159efc0ac79
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20096.d
@@ -0,0 +1,28 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test20096.d(15): Error: cannot `goto` into `try` block
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=20096
+
+void test()
+{
+ int x;
+
+ try {
+ L2:
+ goto L1;
+
+ try
+ {
+ L1:
+ ++x;
+ goto L2;
+ }
+ finally
+ {
+ ++x;
+ }
+ } finally {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20149.d b/gcc/testsuite/gdc.test/fail_compilation/test20149.d
new file mode 100644
index 00000000000..af99a59d7e0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20149.d
@@ -0,0 +1,34 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ * TEST_OUTPUT:
+---
+fail_compilation/test20149.d(28): Error: escaping reference to stack allocated value returned by `S('\xff').this(1)`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=20149#c10
+
+@safe:
+
+struct S
+{
+ this(int){ }
+
+ char[] opSlice() return
+ {
+ return buf[];
+ }
+
+ char[4] buf;
+}
+
+S bar();
+
+char[] fun()
+{
+ return S(1)[];
+}
+
+void main()
+{
+ auto x = fun();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20245.d b/gcc/testsuite/gdc.test/fail_compilation/test20245.d
new file mode 100644
index 00000000000..24a6f99ad32
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20245.d
@@ -0,0 +1,43 @@
+/*
+REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test20245.d(14): Error: scope variable `a` may not be returned
+fail_compilation/test20245.d(18): Error: cannot take address of `scope` parameter `x` in `@safe` function `foo`
+fail_compilation/test20245.d(33): Error: reference to local variable `price` assigned to non-scope `this.minPrice`
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=20245
+@safe int* foo(ref int x) {
+ int* a = &x;
+ return a;
+}
+
+@safe int** foo(ref scope int* x) {
+ int** a = &x;
+ return a;
+}
+
+@safe int* foo(return ref int x) {
+ int* a = &x;
+ return a;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21212
+class MinPointerRecorder
+{
+ int* minPrice;
+ void update(ref int price) @safe
+ {
+ minPrice = &price; // Should not compile.
+ }
+}
+
+void main() @safe
+{
+ auto r = new MinPointerRecorder;
+ () { int mp = 42; r.update(mp); } ();
+ () { ulong[1000] stomp = 13; } ();
+ auto x = *r.minPrice; // "13"
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20267.d b/gcc/testsuite/gdc.test/fail_compilation/test20267.d
new file mode 100644
index 00000000000..996980a94d2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20267.d
@@ -0,0 +1,31 @@
+/*
+EXTRA_FILES: imports/test20267.d
+TEST_OUTPUT:
+---
+fail_compilation/test20267.d(20): Error: variable `string` is used as a type
+fail_compilation/test20267.d(19): variable `string` is declared here
+fail_compilation/test20267.d(23): Error: variable `boolean` is used as a type
+fail_compilation/test20267.d(22): variable `boolean` is declared here
+fail_compilation/test20267.d(30): Error: variable `array` is used as a type
+fail_compilation/test20267.d(28): variable `array` is imported here from: `imports.test20267`
+fail_compilation/imports/test20267.d(3): variable `array` is declared here
+---
+*/
+
+alias boolean = bool;
+
+void foo(string[] args)
+{
+ immutable string = "bar";
+ string[] args2 = args;
+
+ bool boolean = true;
+ boolean b = false;
+}
+
+void bar()
+{
+ import imports.test20267 : array;
+
+ array foo;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20324.d b/gcc/testsuite/gdc.test/fail_compilation/test20324.d
new file mode 100644
index 00000000000..c38e348acee
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20324.d
@@ -0,0 +1,16 @@
+/*
+REQUIRED_ARGS: -unittest
+TEST_OUTPUT:
+---
+fail_compilation/test20324.d(16): Error: argument `Test()` to __traits(getUnitTests) must be a module or aggregate, not a template
+fail_compilation/test20324.d(16): while evaluating `pragma(msg, __traits(getUnitTests, Test))`
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=20324
+
+template Test() {
+ unittest {
+ }
+}
+
+pragma(msg, __traits(getUnitTests, Test));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20383.d b/gcc/testsuite/gdc.test/fail_compilation/test20383.d
new file mode 100644
index 00000000000..88c56ee75d1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20383.d
@@ -0,0 +1,13 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test20383.d(11): Error: invalid array operation `cast(int[])data[] & [42]` (possible missing [])
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=20383
+
+ubyte[1] ice(ubyte[1] data)
+{
+ ubyte[1] result = data[] & [42];
+ return result;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20515.d b/gcc/testsuite/gdc.test/fail_compilation/test20515.d
new file mode 100644
index 00000000000..cf4bbde58d9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20515.d
@@ -0,0 +1,18 @@
+// REQUIRED_ARGS: -verror-style=gnu
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test20515.d:16: Deprecation: function `test20515.foo` is deprecated
+fail_compilation/test20515.d:17: Error: undefined identifier `bar`
+---
+*/
+
+module test20515;
+
+deprecated void foo() { }
+
+void test()
+{
+ foo;
+ bar;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20549.d b/gcc/testsuite/gdc.test/fail_compilation/test20549.d
new file mode 100644
index 00000000000..2cafc1be9e2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20549.d
@@ -0,0 +1,12 @@
+/*
+TEST_OUTPUT:
+----
+fail_compilation/test20549.d(12): Error: variable `test.__a_field_0` variables cannot be of type `void`
+----
+*/
+
+module test;
+
+alias AliasSeq(T...) = T;
+
+enum a = AliasSeq!test;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20565.d b/gcc/testsuite/gdc.test/fail_compilation/test20565.d
new file mode 100644
index 00000000000..5f216755f52
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20565.d
@@ -0,0 +1,19 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test20565.d(107): Error: function `test20565.test.box` the same declaration cannot be in multiple scopes with non-D linkage
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=20565
+
+#line 100
+
+void test()
+{
+ {
+ extern (C++) int box() { return 3; }
+ }
+ {
+ extern (C++) int box() { return 4; }
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20569.d b/gcc/testsuite/gdc.test/fail_compilation/test20569.d
new file mode 100644
index 00000000000..a5ac98b9136
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20569.d
@@ -0,0 +1,24 @@
+/* REQUIRED_ARGS: -preview=dip1000
+ TEST_OUTPUT:
+---
+fail_compilation/test20569.d(19): Error: cannot take address of `scope` local `s1` in `@safe` function `main`
+fail_compilation/test20569.d(23): Error: cannot take address of `scope` local `s2` in `@safe` function `main`
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=20569
+
+struct S
+{
+ int* pointer;
+}
+
+void main() @safe
+{
+ scope S s1;
+ scope int** p1 = &s1.pointer;
+
+ int x;
+ S s2 = S(&x);
+ auto p2 = &s2.pointer;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20610.d b/gcc/testsuite/gdc.test/fail_compilation/test20610.d
new file mode 100644
index 00000000000..9bdeb20cf00
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20610.d
@@ -0,0 +1,22 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test20610.d(20): Error: cannot modify `const` expression `field`
+---
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=20610
+
+struct S
+{
+ int what;
+}
+
+void main()
+{
+ S record;
+
+ foreach (const ref field; record.tupleof)
+ {
+ field = 10;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20626.d b/gcc/testsuite/gdc.test/fail_compilation/test20626.d
new file mode 100644
index 00000000000..bd0bed8a85a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20626.d
@@ -0,0 +1,22 @@
+/*
+REQUIRED_ARGS: -check=invariant=off
+TEST_OUTPUT:
+----
+fail_compilation/test20626.d(2): Error: expression `__unittest_L1_C1` has no type
+_error_
+const void()
+----
+
+https://issues.dlang.org/show_bug.cgi?id=20626
+*/
+
+#line 1
+unittest {}
+pragma(msg, typeof(__unittest_L1_C1));
+
+struct S
+{
+ invariant {}
+}
+
+pragma(msg, typeof(S.init.__invariant1));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20696.d b/gcc/testsuite/gdc.test/fail_compilation/test20696.d
new file mode 100644
index 00000000000..7cb96e5b37e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20696.d
@@ -0,0 +1,21 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test20696.d(106): Error: function `test20696.S!().S.test` cannot retrieve its `.mangleof` while inferring attributes
+fail_compilation/test20696.d(106): while evaluating `pragma(msg, test.mangleof)`
+fail_compilation/test20696.d(111): Error: template instance `test20696.S!()` error instantiating
+---
+*/
+
+#line 100
+
+// https://issues.dlang.org/show_bug.cgi?id=20696
+
+struct S()
+{
+ int test() {
+ pragma(msg, test.mangleof);
+ return 3;
+ }
+}
+
+S!() s;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20719.d b/gcc/testsuite/gdc.test/fail_compilation/test20719.d
new file mode 100644
index 00000000000..44d3d5a36a3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20719.d
@@ -0,0 +1,32 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test20719.d(13): Error: struct `test20719.SumType` no size because of forward reference
+fail_compilation/test20719.d(32): Error: variable `test20719.isCopyable!(SumType).__lambda2.foo` size of type `SumType` is invalid
+fail_compilation/test20719.d(18): Error: template instance `test20719.isCopyable!(SumType)` error instantiating
+---
+*/
+struct SumType
+{
+ alias Types = AliasSeq!(typeof(this));
+ union Storage
+ {
+ Types[0] t;
+ }
+
+ Storage storage;
+
+ static if (isCopyable!(Types[0])) {}
+ static if (isAssignable!(Types[0])) {}
+}
+
+alias AliasSeq(TList...) = TList;
+
+enum isAssignable(Rhs) = __traits(compiles, lvalueOf = rvalueOf!Rhs);
+
+struct __InoutWorkaroundStruct {}
+
+T rvalueOf(T)();
+
+T lvalueOf()(__InoutWorkaroundStruct);
+
+enum isCopyable(S) = { S foo; };
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21096.d b/gcc/testsuite/gdc.test/fail_compilation/test21096.d
new file mode 100644
index 00000000000..e32ad9c9d74
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21096.d
@@ -0,0 +1,13 @@
+// https://issues.dlang.org/show_bug.cgi?id=21096
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21096.d(13): Error: identifier or new keyword expected following `(...)`.
+fail_compilation/test21096.d(13): Error: found `.` when expecting `]`
+fail_compilation/test21096.d(13): Error: no identifier for declarator `char`
+fail_compilation/test21096.d(13): Error: declaration expected, not `]`
+---
+*/
+
+char[(void*).];
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21198.d b/gcc/testsuite/gdc.test/fail_compilation/test21198.d
new file mode 100644
index 00000000000..cab6fc84e89
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21198.d
@@ -0,0 +1,24 @@
+// https://issues.dlang.org/show_bug.cgi?id=21198
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21198.d(23): Error: Generating an `inout` copy constructor for `struct test21198.U` failed, therefore instances of it are uncopyable
+---
+*/
+
+struct S
+{
+ this(ref inout(S) other) inout {}
+}
+
+union U
+{
+ S s;
+}
+
+void fun()
+{
+ U original;
+ U copy = original;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21204.d b/gcc/testsuite/gdc.test/fail_compilation/test21204.d
new file mode 100644
index 00000000000..8732cc09c23
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21204.d
@@ -0,0 +1,23 @@
+// https://issues.dlang.org/show_bug.cgi?id=21204
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21204.d(22): Error: Generating an `inout` copy constructor for `struct test21204.B` failed, therefore instances of it are uncopyable
+---
+*/
+
+struct A
+{
+ this(ref A other) {}
+}
+
+struct B
+{
+ A a;
+}
+
+void example()
+{
+ B b1;
+ B b2 = b1;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21246.d b/gcc/testsuite/gdc.test/fail_compilation/test21246.d
new file mode 100644
index 00000000000..02c0b350171
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21246.d
@@ -0,0 +1,19 @@
+// https://issues.dlang.org/show_bug.cgi?id=21246
+// EXTRA_FILES: imports/test21246.d
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21246.d(16): Error: function `void test21246.C.set(Clock clock)` does not override any function, did you mean to override `void imports.test21246.B.set(imports.test21246.Clock clock)`?
+---
+*/
+module test21246;
+
+import imports.test21246;
+
+class Clock { }
+class C : B
+{
+ override void set (Clock clock) { }
+}
+
+void main () { }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21259.d b/gcc/testsuite/gdc.test/fail_compilation/test21259.d
new file mode 100644
index 00000000000..31dba3d52ca
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21259.d
@@ -0,0 +1,52 @@
+// https://issues.dlang.org/show_bug.cgi?id=21259
+// REQUIRED_ARGS: -de
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21259.d(39): Deprecation: alias `test21259.Foo.width` is deprecated
+fail_compilation/test21259.d(40): Deprecation: alias `test21259.Foo2.width` is deprecated
+fail_compilation/test21259.d(41): Deprecation: variable `test21259.Foo3.bar` is deprecated
+fail_compilation/test21259.d(42): Deprecation: alias `test21259.Foo4.width` is deprecated
+---
+*/
+
+struct Foo
+{
+ int bar;
+ deprecated alias width = bar;
+}
+
+struct Foo2
+{
+ deprecated int bar;
+ deprecated alias width = bar;
+}
+
+struct Foo3
+{
+ deprecated int bar;
+}
+
+struct Foo4
+{
+ int bar;
+deprecated:
+ alias width = bar;
+}
+
+void main()
+{
+ Foo a = { width : 100};
+ Foo2 b = { width : 100};
+ Foo3 c = { bar : 100};
+ Foo4 d = { width : 100};
+}
+
+// deprecations inside a deprecated scope shouldn't be triggered
+deprecated void test()
+{
+ Foo a = { width : 100};
+ Foo2 b = { width : 100};
+ Foo3 c = { bar : 100};
+ Foo4 d = { width : 100};
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21319.d b/gcc/testsuite/gdc.test/fail_compilation/test21319.d
new file mode 100644
index 00000000000..7298de2df14
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21319.d
@@ -0,0 +1,12 @@
+// https://issues.dlang.org/show_bug.cgi?id=21319
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21319.d(11): Error: circular reference to `test21319.C.c`
+---
+*/
+
+class C
+{
+ immutable C c = new C();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21518.d b/gcc/testsuite/gdc.test/fail_compilation/test21518.d
new file mode 100644
index 00000000000..59a9c3cb882
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21518.d
@@ -0,0 +1,38 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=21518
+TEST_OUTPUT:
+---
+fail_compilation/test21518.d(19): Error: cannot implicitly convert expression `[dg]` of type `const(void delegate() pure nothrow @nogc @system)[]` to `void delegate() @safe[]`
+fail_compilation/test21518.d(23): Error: cannot implicitly convert expression `[dg]` of type `const(void delegate() pure nothrow @nogc @system)[]` to `const(void delegate() @safe)[]`
+fail_compilation/test21518.d(28): Error: cannot implicitly convert expression `sysA` of type `const(void delegate() @system)[]` to `const(void delegate() @safe)[]`
+fail_compilation/test21518.d(31): Error: cannot implicitly convert expression `sysA` of type `const(void delegate() @system)[]` to `const(void delegate() @safe)`
+fail_compilation/test21518.d(32): Error: cannot implicitly convert expression `dg` of type `const(void delegate() pure nothrow @nogc @system)` to `const(void delegate() @safe)`
+---
+*/
+
+void delegates()
+{
+ const dg = delegate() @system { int* p; int x; p = &x; };
+ // pragma(msg, typeof(dg)); // const(void delegate() pure nothrow @nogc @system)
+
+ // Correctly fails
+ void delegate() @safe[] arg2 = [ dg ];
+ void delegate() @system[] arg3 = [ dg ]; // But doesnt break this
+
+ // Previously ignored
+ const(void delegate() @safe)[] arg = [ dg ];
+ // pragma(msg, typeof(arg)); // const(void delegate() @safe)[]
+
+ // Also for variables, not only array literals
+ const(void delegate() @system)[] sysA = [ dg ];
+ const(void delegate() @safe)[] safeA = sysA;
+
+ // Original bug report:
+ func(sysA);
+ func(dg);
+}
+
+void func(const void delegate() @safe [] paramDGs...) @safe
+{
+ if (paramDGs.length > 0) paramDGs[0]();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21665.d b/gcc/testsuite/gdc.test/fail_compilation/test21665.d
new file mode 100644
index 00000000000..a3a348dd033
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21665.d
@@ -0,0 +1,31 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test21665.d(18): Error: variable `test21665.test1.s` `void` initializers for structs with invariants are not allowed in safe functions
+fail_compilation/test21665.d(30): Error: field `U.s` cannot access structs with invariants in `@safe` code that overlap other fields
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=21665
+
+struct ShortString {
+ private ubyte length;
+ private char[15] data;
+
+ invariant { assert(length <= data.length); }
+}
+
+@safe void test1() {
+ ShortString s = void;
+}
+
+union U
+{
+ int n;
+ ShortString s;
+}
+
+@safe void test2()
+{
+ U u;
+ u.s.length = 3;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21807.d b/gcc/testsuite/gdc.test/fail_compilation/test21807.d
new file mode 100644
index 00000000000..9f56bf73a90
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21807.d
@@ -0,0 +1,54 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=21807
+
+REQUIRED_ARGS: -de
+TEST_OUTPUT:
+---
+fail_compilation/test21807.d(11): Deprecation: slice of static array temporary returned by `getSArray()` assigned to longer lived variable `this.str`
+fail_compilation/test21807.d(12): Deprecation: slice of static array temporary returned by `getSArray()` assigned to longer lived variable `this.ca`
+---
+*/
+#line 1
+
+char[12] getSArray() pure;
+
+class Foo
+{
+ string str;
+ char[] ca;
+
+ this()
+ {
+ str = getSArray(); // Should probably be a type error
+ ca = getSArray();
+ }
+}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21807.d(117): Error: function `test21807.addr(return ref int b)` is not callable using argument types `(int)`
+fail_compilation/test21807.d(117): cannot pass rvalue argument `S(0).i` of type `int` to parameter `return ref int b`
+---
+*/
+#line 100
+
+struct S
+{
+ int i;
+}
+
+int* addr(return ref int b)
+{
+ return &b;
+}
+
+class Foo2
+{
+ int* ptr;
+
+ this()
+ {
+ ptr = addr(S().i); // struct temporary
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21912.d b/gcc/testsuite/gdc.test/fail_compilation/test21912.d
new file mode 100644
index 00000000000..8dde98a62b4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21912.d
@@ -0,0 +1,54 @@
+/*
+PERMUTE_ARGS: -preview=dip1000
+TEST_OUTPUT
+---
+fail_compilation/test21912.d(24): Error: function `test21912.escapeParam` is `@nogc` yet allocates closures with the GC
+fail_compilation/test21912.d(26): test21912.escapeParam.__lambda2 closes over variable i at fail_compilation/test21912.d(24)
+fail_compilation/test21912.d(29): Error: function `test21912.escapeAssign` is `@nogc` yet allocates closures with the GC
+fail_compilation/test21912.d(31): test21912.escapeAssign.__lambda3 closes over variable i at fail_compilation/test21912.d(29)
+fail_compilation/test21912.d(40): Error: function `test21912.escapeAssignRef` is `@nogc` yet allocates closures with the GC
+fail_compilation/test21912.d(42): test21912.escapeAssignRef.__lambda3 closes over variable i at fail_compilation/test21912.d(40)
+fail_compilation/test21912.d(51): Error: function `test21912.escapeParamInferred` is `@nogc` yet allocates closures with the GC
+fail_compilation/test21912.d(53): test21912.escapeParamInferred.__lambda2 closes over variable i at fail_compilation/test21912.d(51)
+---
+*/
+@nogc:
+
+alias Dg = @nogc int delegate();
+
+Dg identity(return scope Dg dg)
+{
+ return dg;
+}
+
+Dg escapeParam(int i)
+{
+ return identity(() => i);
+}
+
+Dg escapeAssign(int i, return scope Dg dg)
+{
+ dg = () => i;
+ return dg;
+}
+
+ref Dg identityR(ref return scope Dg dg)
+{
+ return dg;
+}
+
+ref Dg escapeAssignRef(int i, ref return scope Dg dg)
+{
+ dg = () => i;
+ return dg;
+}
+
+auto identityInferred(Dg dg)
+{
+ return dg;
+}
+
+Dg escapeParamInferred(int i)
+{
+ return identityInferred(() => i);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test22048.d b/gcc/testsuite/gdc.test/fail_compilation/test22048.d
new file mode 100644
index 00000000000..e0560689f8d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test22048.d
@@ -0,0 +1,10 @@
+// https://issues.dlang.org/show_bug.cgi?id=22048
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test22048.d(10): Error: unexpected identifier `p` in declarator
+---
+*/
+
+alias a = int p;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test22227.d b/gcc/testsuite/gdc.test/fail_compilation/test22227.d
new file mode 100644
index 00000000000..ecffb692bef
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test22227.d
@@ -0,0 +1,16 @@
+/* REQUIRED_ARGS: -preview=dip1000
+TEST_OUTPUT:
+---
+fail_compilation/test22227.d(12): Error: scope variable `foo` may not be returned
+fail_compilation/test22227.d(14): Error: scope variable `foo` may not be returned
+---
+*/
+
+int[] foo() @safe
+{
+ if (scope foo = [1])
+ return foo;
+ while (scope foo = [1])
+ return foo;
+ return [];
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test314.d b/gcc/testsuite/gdc.test/fail_compilation/test314.d
index eb3f07d378a..26de7373145 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test314.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test314.d
@@ -1,5 +1,5 @@
/*
-REQUIRED_ARGS:
+EXTRA_FILES: imports/a314.d imports/b314.d imports/c314.d
TEST_OUTPUT:
---
fail_compilation/test314.d(19): Error: undefined identifier `renamed`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test4838.d b/gcc/testsuite/gdc.test/fail_compilation/test4838.d
index 0bf075737e5..a135c79f6a0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test4838.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test4838.d
@@ -1,12 +1,12 @@
/*
TEST_OUTPUT:
---
-fail_compilation/test4838.d(13): Error: const/immutable/shared/inout/return attributes are only valid for non-static member functions
-fail_compilation/test4838.d(14): Error: const/immutable/shared/inout/return attributes are only valid for non-static member functions
-fail_compilation/test4838.d(15): Error: const/immutable/shared/inout/return attributes are only valid for non-static member functions
-fail_compilation/test4838.d(16): Error: const/immutable/shared/inout/return attributes are only valid for non-static member functions
-fail_compilation/test4838.d(17): Error: const/immutable/shared/inout/return attributes are only valid for non-static member functions
-fail_compilation/test4838.d(18): Error: const/immutable/shared/inout/return attributes are only valid for non-static member functions
+fail_compilation/test4838.d(13): Error: `const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions
+fail_compilation/test4838.d(14): Error: `const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions
+fail_compilation/test4838.d(15): Error: `const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions
+fail_compilation/test4838.d(16): Error: `const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions
+fail_compilation/test4838.d(17): Error: `const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions
+fail_compilation/test4838.d(18): Error: `const`/`immutable`/`shared`/`inout`/`return` attributes are only valid for non-static member functions
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test4946.d b/gcc/testsuite/gdc.test/fail_compilation/test4946.d
new file mode 100644
index 00000000000..e19932b0371
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test4946.d
@@ -0,0 +1,19 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/test4946.d(13): Error: 'pure' cannot be placed after a template constraint
+fail_compilation/test4946.d(14): Error: 'const' cannot be placed after a template constraint
+fail_compilation/test4946.d(15): Error: 'immutable' cannot be placed after a template constraint
+fail_compilation/test4946.d(16): Error: 'inout' cannot be placed after a template constraint
+fail_compilation/test4946.d(17): Error: 'shared' cannot be placed after a template constraint
+fail_compilation/test4946.d(18): Error: 'nothrow' cannot be placed after a template constraint
+fail_compilation/test4946.d(19): Error: attributes cannot be placed after a template constraint
+---
+*/
+
+void bar1(int x)() if (x > 0) pure { int a;}
+void bar2(int x)() if (x > 0) const { int a;}
+void bar3(int x)() if (x > 0) immutable { int a;}
+void bar4(int x)() if (x > 0) inout { int a;}
+void bar5(int x)() if (x > 0) shared { int a;}
+void bar6(int x)() if (x > 0) nothrow { int a;}
+void bar7(int x)() if (x > 0) @safe { int a;}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test64.d b/gcc/testsuite/gdc.test/fail_compilation/test64.d
index bfc63f29f12..d10f669dfcf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test64.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test64.d
@@ -1,11 +1,11 @@
/*
+EXTRA_FILES: imports/test64a.d
TEST_OUTPUT:
---
-fail_compilation/imports/test64a.d(1): Error: module imports from file fail_compilation/imports/test64a.d conflicts with package name imports
+fail_compilation/imports/test64a.d(1): Error: module `imports` from file fail_compilation/imports/test64a.d conflicts with package name imports
---
*/
-// PERMUTE_ARGS:
//import std.stdio;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test8556.d b/gcc/testsuite/gdc.test/fail_compilation/test8556.d
index 34900412e5b..75c5c593eea 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test8556.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test8556.d
@@ -1,8 +1,11 @@
/*
TEST_OUTPUT:
---
-fail_compilation/test8556.d(21): Error: template instance test8556.Grab!(Circle!(uint[])) does not match template declaration Grab(Range) if (!isSliceable!Range)
-fail_compilation/test8556.d(52): Error: template instance test8556.grab!(Circle!(uint[])) error instantiating
+fail_compilation/test8556.d(24): Error: template instance `test8556.Grab!(Circle!(uint[]))` does not match template declaration `Grab(Range)`
+ with `Range = Circle!(uint[])`
+ must satisfy the following constraint:
+` !isSliceable!Range`
+fail_compilation/test8556.d(55): Error: template instance `test8556.grab!(Circle!(uint[]))` error instantiating
---
*/
@@ -34,7 +37,7 @@ struct Circle(Range)
{
//pragma(msg, typeof(opSlice)); // prints "fwdref err" with B, but doesn't with A
- printf("%d %d\n", i, j);
+ printf("%d %d\n", cast(int)i, cast(int)j);
assert(j >= i);
// 1. grabExactly curcular refers this opSlice.
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test9150.d b/gcc/testsuite/gdc.test/fail_compilation/test9150.d
index 650bcd48bfc..e65afece6ff 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test9150.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test9150.d
@@ -1,5 +1,5 @@
-// Issue 9150 - Mismatching static array length should be detected in foreach
-
+// https://issues.dlang.org/show_bug.cgi?id=9150
+// Mismatching static array length should be detected in foreach
/*
TEST_OUTPUT:
---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test9176.d b/gcc/testsuite/gdc.test/fail_compilation/test9176.d
index 357d54f610c..62ae5c048d5 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test9176.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test9176.d
@@ -1,8 +1,8 @@
/*
TEST_OUTPUT:
---
-fail_compilation/test9176.d(14): Error: forward reference to inferred return type of function call 'get()'
-fail_compilation/test9176.d(10): while evaluating: `static assert(!false)`
+fail_compilation/test9176.d(14): Error: forward reference to inferred return type of function call `get()`
+fail_compilation/test9176.d(10): while evaluating: `static assert(!is(typeof(foo(S()))))`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/testCols.d b/gcc/testsuite/gdc.test/fail_compilation/testCols.d
index d072b3c8d02..e5bff1809b4 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/testCols.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/testCols.d
@@ -1,10 +1,9 @@
// REQUIRED_ARGS: -vcolumns
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/testCols.d(13,5): Error: undefined identifier `nonexistent`
+fail_compilation/testCols.d(12,5): Error: undefined identifier `nonexistent`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/testInference.d b/gcc/testsuite/gdc.test/fail_compilation/testInference.d
index 3a49c58a029..c0d5a05d05c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/testInference.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/testInference.d
@@ -137,7 +137,7 @@ immutable(void)* g10063(inout int* p) pure
/*
TEST_OUTPUT:
---
-fail_compilation/testInference.d(154): Error: pure function 'testInference.bar14049' cannot call impure function 'testInference.foo14049!int.foo14049'
+fail_compilation/testInference.d(154): Error: `pure` function `testInference.bar14049` cannot call impure function `testInference.foo14049!int.foo14049`
---
*/
auto impure14049() { static int i = 1; return i; }
@@ -157,7 +157,7 @@ void bar14049() pure
/*
TEST_OUTPUT:
---
-fail_compilation/testInference.d(166): Error: pure function 'testInference.f14160' cannot access mutable static data 'g14160'
+fail_compilation/testInference.d(166): Error: `pure` function `testInference.f14160` cannot access mutable static data `g14160`
---
*/
int g14160;
@@ -169,7 +169,7 @@ int* f14160() pure
/*
TEST_OUTPUT:
---
-fail_compilation/testInference.d(180): Error: pure function 'testInference.test12422' cannot call impure function 'testInference.test12422.bar12422!().bar12422'
+fail_compilation/testInference.d(180): Error: `pure` function `testInference.test12422` cannot call impure function `testInference.test12422.bar12422!().bar12422`
---
*/
int g12422;
@@ -183,8 +183,8 @@ void test12422() pure
/*
TEST_OUTPUT:
---
-fail_compilation/testInference.d(196): Error: pure function 'testInference.test13729a' cannot access mutable static data 'g13729'
-fail_compilation/testInference.d(206): Error: pure function 'testInference.test13729b' cannot call impure function 'testInference.test13729b.foo!().foo'
+fail_compilation/testInference.d(198): Error: `pure` function `testInference.test13729a` cannot call impure function `testInference.test13729a.foo`
+fail_compilation/testInference.d(206): Error: `pure` function `testInference.test13729b` cannot call impure function `testInference.test13729b.foo!().foo`
---
*/
int g13729;
@@ -193,9 +193,9 @@ void test13729a() pure
{
static void foo() // typed as impure
{
- g13729++; // disallowed
+ g13729++;
}
- foo();
+ foo(); // cannot call impure function
}
void test13729b() pure
{
@@ -209,10 +209,10 @@ void test13729b() pure
/*
TEST_OUTPUT:
---
-fail_compilation/testInference.d(225): Error: testInference.test17086 called with argument types (bool) matches both:
-fail_compilation/testInference.d(219): testInference.test17086!(bool, false).test17086(bool x)
+fail_compilation/testInference.d(225): Error: `testInference.test17086` called with argument types `(bool)` matches both:
+fail_compilation/testInference.d(219): `testInference.test17086!(bool, false).test17086(bool x)`
and:
-fail_compilation/testInference.d(220): testInference.test17086!(bool, false).test17086(bool y)
+fail_compilation/testInference.d(220): `testInference.test17086!(bool, false).test17086(bool y)`
---
*/
@@ -224,3 +224,16 @@ void test17086_call ()
bool f;
test17086(f);
}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/testInference.d(238): Error: `pure` function `testInference.test20047_pure_function` cannot call impure function `testInference.test20047_pure_function.bug`
+---
+*/
+void test20047_impure_function() {}
+void test20047_pure_function() pure
+{
+ static void bug() { return test20047_impure_function(); }
+ bug();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/testpull1810.d b/gcc/testsuite/gdc.test/fail_compilation/testpull1810.d
index 141c9d2ff95..73ac8302a94 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/testpull1810.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/testpull1810.d
@@ -2,7 +2,9 @@
/*
TEST_OUTPUT:
---
-fail_compilation/testpull1810.d(19): Warning: statement is not reachable
+fail_compilation/testpull1810.d(21): Warning: statement is not reachable
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/testscopestatic.d b/gcc/testsuite/gdc.test/fail_compilation/testscopestatic.d
index 310e9fef472..45e73c068ef 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/testscopestatic.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/testscopestatic.d
@@ -1,10 +1,10 @@
/* TEST_OUTPUT:
---
-fail_compilation/testscopestatic.d(15): Error: variable testscopestatic.foo.p cannot be 'scope' and 'static'
-fail_compilation/testscopestatic.d(16): Error: variable testscopestatic.foo.b cannot be 'scope' and 'extern'
-fail_compilation/testscopestatic.d(17): Error: variable testscopestatic.foo.c cannot be 'scope' and '__gshared'
-fail_compilation/testscopestatic.d(21): Error: variable testscopestatic.foo.S.x field cannot be 'scope'
+fail_compilation/testscopestatic.d(15): Error: variable `testscopestatic.foo.p` cannot be `scope` and `static`
+fail_compilation/testscopestatic.d(16): Error: variable `testscopestatic.foo.b` cannot be `scope` and `extern`
+fail_compilation/testscopestatic.d(17): Error: variable `testscopestatic.foo.c` cannot be `scope` and `__gshared`
+fail_compilation/testscopestatic.d(21): Error: variable `testscopestatic.foo.S.x` field cannot be `scope`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/trait_loc_err.d b/gcc/testsuite/gdc.test/fail_compilation/trait_loc_err.d
index 8d5d480174e..c5d0579f4a0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/trait_loc_err.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/trait_loc_err.d
@@ -2,14 +2,14 @@
TEST_OUTPUT:
---
fail_compilation/trait_loc_err.d(13): Error: can only get the location of a symbol, not `trait_loc_err`
-fail_compilation/trait_loc_err.d(14): Error: can only get the location of a symbol, not `std`
+fail_compilation/trait_loc_err.d(14): Error: can only get the location of a symbol, not `stdc`
---
*/
module trait_loc_err;
-import std.stdio;
+import core.stdc.stdio;
void main()
{
__traits(getLocation, __traits(parent, main));
- __traits(getLocation, __traits(parent, std.stdio));
+ __traits(getLocation, __traits(parent, core.stdc.stdio));
}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/traits.d b/gcc/testsuite/gdc.test/fail_compilation/traits.d
index bee29ed1d76..5b9daaabf91 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/traits.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/traits.d
@@ -11,6 +11,16 @@ fail_compilation/traits.d(200): Error: undefined identifier `imports.nonexistent
fail_compilation/traits.d(201): Error: undefined identifier `imports.nonexistent`
fail_compilation/traits.d(202): Error: expected 1 arguments for `isPackage` but had 0
fail_compilation/traits.d(203): Error: expected 1 arguments for `isModule` but had 0
+fail_compilation/traits.d(300): Error: In expression `__traits(allMembers, float)` `float` can't have members
+fail_compilation/traits.d(300): `float` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation
+fail_compilation/traits.d(306): Error: In expression `__traits(allMembers, TemplatedStruct)` struct `TemplatedStruct(T)` has no members
+fail_compilation/traits.d(306): `TemplatedStruct(T)` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation
+fail_compilation/traits.d(309): Error: In expression `__traits(derivedMembers, float)` `float` can't have members
+fail_compilation/traits.d(309): `float` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation
+fail_compilation/traits.d(316): Error: In expression `__traits(derivedMembers, TemplatedStruct)` struct `TemplatedStruct(T)` has no members
+fail_compilation/traits.d(316): `TemplatedStruct(T)` must evaluate to either a module, a struct, an union, a class, an interface or a template instantiation
+fail_compilation/traits.d(404): Error: function `traits.func1` circular reference in `__traits(GetCppNamespaces,...)`
+fail_compilation/traits.d(413): Error: function `traits.foo1.func1` circular reference in `__traits(GetCppNamespaces,...)`
---
*/
@@ -25,3 +35,84 @@ enum A2 = __traits(isPackage, imports.nonexistent);
enum B2 = __traits(isModule, imports.nonexistent);
enum C2 = __traits(isPackage);
enum D2 = __traits(isModule);
+
+interface Interface {}
+struct TemplatedStruct(T) {}
+struct Struct {}
+union Union {}
+class Class {}
+
+#line 300
+enum AM0 = __traits(allMembers, float); // compile error
+enum AM1 = __traits(allMembers, Struct); // no error
+enum AM2 = __traits(allMembers, Union); // no error
+enum AM3 = __traits(allMembers, Class); // no error
+enum AM4 = __traits(allMembers, Interface); // no error
+enum AM5 = __traits(allMembers, TemplatedStruct!float); // no error
+enum AM6 = __traits(allMembers, TemplatedStruct); // compile error
+enum AM7 = __traits(allMembers, mixin(__MODULE__)); // no error
+
+enum DM0 = __traits(derivedMembers, float); // compile error
+enum DM1 = __traits(derivedMembers, Struct); // no error
+enum DM2 = __traits(derivedMembers, Struct); // no error
+enum DM3 = __traits(derivedMembers, Union); // no error
+enum DM4 = __traits(derivedMembers, Class); // no error
+enum DM5 = __traits(derivedMembers, Interface); // no error
+enum DM6 = __traits(derivedMembers, TemplatedStruct!float); // no error
+enum DM7 = __traits(derivedMembers, TemplatedStruct); // compile error
+enum DM8 = __traits(derivedMembers, mixin(__MODULE__)); // no error
+
+#line 400
+extern(C++, "bar")
+extern(C++, __traits(getCppNamespaces, func1)) void func () {}
+
+extern(C++, "foo")
+extern(C++, __traits(getCppNamespaces, func2)) void func1 () {}
+
+extern(C++, "foobar")
+extern(C++, __traits(getCppNamespaces, func)) void func2 () {}
+
+extern(C++, bar1)
+extern(C++, __traits(getCppNamespaces, foo1.func1)) void func () {}
+
+extern(C++, foo1)
+extern(C++, __traits(getCppNamespaces, foobar1.func2)) void func1 () {}
+
+extern(C++, foobar1)
+extern(C++, __traits(getCppNamespaces, bar1.func)) void func2 () {}
+
+/********************************************
+https://issues.dlang.org/show_bug.cgi?id=21918
+
+TEST_OUTPUT:
+---
+fail_compilation/traits.d(501): Error: undefined identifier `T`
+fail_compilation/traits.d(502): while evaluating `pragma(msg, __traits(getParameterStorageClasses, yip, 0))`
+---
+*/
+#line 500
+
+auto yip(int f) {return T[];}
+pragma(msg, __traits(getParameterStorageClasses, yip, 0));
+
+
+/********************************************
+TEST_OUTPUT:
+---
+fail_compilation/traits.d(602): Error: expected 1 arguments for `hasCopyConstructor` but had 0
+fail_compilation/traits.d(602): while evaluating `pragma(msg, __traits(hasCopyConstructor))`
+fail_compilation/traits.d(603): Error: type expected as second argument of __traits `hasCopyConstructor` instead of `S()`
+fail_compilation/traits.d(603): while evaluating `pragma(msg, __traits(hasCopyConstructor, S()))`
+fail_compilation/traits.d(604): Error: expected 1 arguments for `hasPostblit` but had 0
+fail_compilation/traits.d(604): while evaluating `pragma(msg, __traits(hasPostblit))`
+fail_compilation/traits.d(605): Error: type expected as second argument of __traits `hasPostblit` instead of `S()`
+fail_compilation/traits.d(605): while evaluating `pragma(msg, __traits(hasPostblit, S()))`
+---
+*/
+#line 600
+
+struct S { this (ref S rhs) {} }
+pragma(msg, __traits(hasCopyConstructor));
+pragma(msg, __traits(hasCopyConstructor, S()));
+pragma(msg, __traits(hasPostblit));
+pragma(msg, __traits(hasPostblit, S()));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/traits_child.d b/gcc/testsuite/gdc.test/fail_compilation/traits_child.d
index 7a0b75e0c9c..aeaf213389d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/traits_child.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/traits_child.d
@@ -4,10 +4,8 @@
TEST_OUTPUT:
---
fail_compilation/traits_child.d(100): Error: expected 2 arguments for `child` but had 1
-fail_compilation/traits_child.d(101): Error: symbol or expression expected as first argument of __traits
-child` instead of `long`
-fail_compilation/traits_child.d(102): Error: symbol expected as second argument of __traits `child` inste
-d of `3`
+fail_compilation/traits_child.d(101): Error: symbol or expression expected as first argument of __traits `child` instead of `long`
+fail_compilation/traits_child.d(102): Error: symbol expected as second argument of __traits `child` instead of `3`
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/typeerrors.d b/gcc/testsuite/gdc.test/fail_compilation/typeerrors.d
index ba80fdc1bdf..a93a49fdc18 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/typeerrors.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/typeerrors.d
@@ -1,22 +1,22 @@
/*
-PERMUTE_ARGS:
TEST_OUTPUT:
---
fail_compilation/typeerrors.d(36): Error: tuple index 4 exceeds 4
-fail_compilation/typeerrors.d(38): Error: variable x cannot be read at compile time
-fail_compilation/typeerrors.d(39): Error: can't have array of void()
-fail_compilation/typeerrors.d(40): Error: cannot have array of scope typeerrors.C
-fail_compilation/typeerrors.d(41): Error: cannot have array of scope typeerrors.C
-fail_compilation/typeerrors.d(44): Error: int[5] is not an expression
-fail_compilation/typeerrors.d(46): Error: x is used as a type
-fail_compilation/typeerrors.d(47): Error: can't have associative array key of void()
-fail_compilation/typeerrors.d(48): Error: can't have associative array key of void
-fail_compilation/typeerrors.d(49): Error: cannot have array of scope typeerrors.C
-fail_compilation/typeerrors.d(50): Error: can't have associative array of void
-fail_compilation/typeerrors.d(51): Error: can't have associative array of void()
-fail_compilation/typeerrors.d(53): Error: cannot have parameter of type void
-fail_compilation/typeerrors.d(55): Error: slice [1..5] is out of range of [0..4]
-fail_compilation/typeerrors.d(56): Error: slice [2..1] is out of range of [0..4]
+fail_compilation/typeerrors.d(38): Error: variable `x` cannot be read at compile time
+fail_compilation/typeerrors.d(39): Error: cannot have array of `void()`
+fail_compilation/typeerrors.d(40): Error: cannot have array of scope `typeerrors.C`
+fail_compilation/typeerrors.d(41): Error: cannot have array of scope `typeerrors.C`
+fail_compilation/typeerrors.d(44): Error: `int[5]` is not an expression
+fail_compilation/typeerrors.d(46): Error: variable `x` is used as a type
+fail_compilation/typeerrors.d(37): variable `x` is declared here
+fail_compilation/typeerrors.d(47): Error: cannot have associative array key of `void()`
+fail_compilation/typeerrors.d(48): Error: cannot have associative array key of `void`
+fail_compilation/typeerrors.d(49): Error: cannot have array of scope `typeerrors.C`
+fail_compilation/typeerrors.d(50): Error: cannot have associative array of `void`
+fail_compilation/typeerrors.d(51): Error: cannot have associative array of `void()`
+fail_compilation/typeerrors.d(53): Error: cannot have parameter of type `void`
+fail_compilation/typeerrors.d(55): Error: slice `[1..5]` is out of range of [0..4]
+fail_compilation/typeerrors.d(56): Error: slice `[2..1]` is out of range of [0..4]
---
*/
@@ -55,4 +55,3 @@ void foo()
alias T2 = T[1 .. 5];
alias T3 = T[2 .. 1];
}
-
diff --git a/gcc/testsuite/gdc.test/fail_compilation/union_initialization.d b/gcc/testsuite/gdc.test/fail_compilation/union_initialization.d
new file mode 100644
index 00000000000..36fc63a6f34
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/union_initialization.d
@@ -0,0 +1,88 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=20068
+
+TEST_OUTPUT:
+---
+fail_compilation/union_initialization.d(19): Error: field `B.p` cannot access pointers in `@safe` code that overlap other fields
+fail_compilation/union_initialization.d(25): Error: field `B.p` cannot access pointers in `@safe` code that overlap other fields
+---
+*/
+
+union B
+{
+ int i;
+ int* p;
+
+ @safe this(int* p)
+ {
+ this.p = p;
+ int* x = this.p;
+ }
+
+ @safe this(int** i)
+ {
+ this.p = null;
+ this.p = *i;
+ }
+}
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/union_initialization.d(109): Error: immutable field `p` initialized multiple times
+fail_compilation/union_initialization.d(108): Previous initialization is here.
+---
+*/
+#line 100
+
+union C
+{
+ int i;
+ immutable int* p;
+
+ @safe this(immutable int* p)
+ {
+ this.p = p;
+ this.p = null;
+ }
+}
+
+/*
+https://issues.dlang.org/show_bug.cgi?id=21229
+
+TEST_OUTPUT:
+---
+fail_compilation/union_initialization.d(223): Error: field `union_` must be initialized in constructor
+fail_compilation/union_initialization.d(223): Error: field `proxy` must be initialized in constructor
+---
+*/
+#line 200
+
+struct NeedsInit
+{
+ int var;
+ long lo;
+ @disable this();
+}
+
+union Union
+{
+ NeedsInit ni;
+}
+
+union Proxy
+{
+ Union union_;
+}
+
+struct S
+{
+ Union union_;
+ Proxy proxy;
+
+ this(int arg)
+ {
+ union_.ni.var = arg;
+ proxy.union_.ni.var = arg;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/vararg2.d b/gcc/testsuite/gdc.test/fail_compilation/vararg2.d
new file mode 100644
index 00000000000..eb235583efa
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/vararg2.d
@@ -0,0 +1,23 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/vararg2.d(106): Error: function `vararg2.foo(int x, const return ...)` is not callable using argument types `(double)`
+fail_compilation/vararg2.d(106): cannot pass argument `1.0` of type `double` to parameter `int x`
+fail_compilation/vararg2.d(111): Error: function `vararg2.bar(int x, scope shared ...)` is not callable using argument types `(double)`
+fail_compilation/vararg2.d(111): cannot pass argument `1.0` of type `double` to parameter `int x`
+---
+*/
+
+#line 100
+
+int* foo(int x, return const ...);
+int* bar(int x, scope shared ...);
+
+void test1()
+{
+ foo(1.0);
+}
+
+void test2()
+{
+ bar(1.0);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/varargsstc.d b/gcc/testsuite/gdc.test/fail_compilation/varargsstc.d
new file mode 100644
index 00000000000..674455b477d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/varargsstc.d
@@ -0,0 +1,11 @@
+/* TEST_OUTPUT:
+---
+fail_compilation/varargsstc.d(102): Error: variadic parameter cannot have attributes `out ref`
+---
+ */
+
+#line 100
+
+int printf(const(char)*, const scope shared return ...);
+int printf(const(char)*, ref out scope immutable shared return ...);
+
diff --git a/gcc/testsuite/gdc.test/fail_compilation/verifyhookexist.d b/gcc/testsuite/gdc.test/fail_compilation/verifyhookexist.d
new file mode 100644
index 00000000000..d7b8f6646c3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/verifyhookexist.d
@@ -0,0 +1,45 @@
+/*
+REQUIRED_ARGS: -checkaction=context
+EXTRA_SOURCES: extra-files/minimal/object.d
+*/
+
+/************************************************************/
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/verifyhookexist.d(22): Error: `object.__ArrayCast` not found. The current runtime does not support casting array of structs, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(28): Error: `object.__equals` not found. The current runtime does not support equal checks on arrays, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(29): Error: `object.__cmp` not found. The current runtime does not support comparing arrays, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(33): Error: `object._d_assert_fail` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(36): Error: `object.__switch` not found. The current runtime does not support switch cases on strings, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(41): Error: `object.__switch_error` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
+---
+*/
+
+struct MyStruct { int a, b; }
+MyStruct[] castToMyStruct(int[] arr) {
+ return cast(MyStruct[])arr;
+}
+
+void test() {
+ int[] arrA, arrB;
+
+ bool a = arrA[] == arrB[];
+ bool b = arrA < arrB;
+
+ {
+ int x = 1; int y = 1;
+ assert(x == y);
+ }
+
+ switch ("") {
+ default:
+ break;
+ }
+
+ final switch (0) {
+ case 1:
+ break;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/warn13679.d b/gcc/testsuite/gdc.test/fail_compilation/warn13679.d
index 0a92b766788..74d45643c8d 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/warn13679.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/warn13679.d
@@ -1,10 +1,11 @@
// REQUIRED_ARGS: -w
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/warn13679.d(14): Warning: cannot use foreach_reverse with an associative array
+fail_compilation/warn13679.d(15): Warning: cannot use `foreach_reverse` with an associative array
+Error: warnings are treated as errors
+ Use -wi if you wish to treat warnings only as informational.
---
*/
diff --git a/gcc/testsuite/gdc.test/fail_compilation/warn7444.d b/gcc/testsuite/gdc.test/fail_compilation/warn7444.d
index ec7d49c150b..025039adcda 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/warn7444.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/warn7444.d
@@ -1,10 +1,9 @@
// REQUIRED_ARGS: -w
-// PERMUTE_ARGS:
/*
TEST_OUTPUT:
---
-fail_compilation/warn7444.d(23): Error: cannot implicitly convert expression `e` of type `int` to `int[]`
+fail_compilation/warn7444.d(22): Error: cannot implicitly convert expression `e` of type `int` to `int[]`
---
*/
diff --git a/gcc/testsuite/gdc.test/runnable/A16.d b/gcc/testsuite/gdc.test/runnable/A16.d
index f65335393b4..e89e71493fe 100644
--- a/gcc/testsuite/gdc.test/runnable/A16.d
+++ b/gcc/testsuite/gdc.test/runnable/A16.d
@@ -1,6 +1,13 @@
-// EXTRA_SOURCES: imports/A16a.d
+/*
+EXTRA_SOURCES: imports/A16a.d
+RUN_OUTPUT:
+---
+class AA16
+class B16
+---
+*/
-import std.stdio;
+import core.stdc.stdio;
class AA16
{
diff --git a/gcc/testsuite/gdc.test/runnable/Same.d b/gcc/testsuite/gdc.test/runnable/Same.d
index 85025e77c4e..a4c6f0f8317 100644
--- a/gcc/testsuite/gdc.test/runnable/Same.d
+++ b/gcc/testsuite/gdc.test/runnable/Same.d
@@ -1,5 +1,12 @@
-// EXTRA_SOURCES: imports/Other.d
-// PERMUTE_ARGS:
+/*
+EXTRA_SOURCES: imports/Other.d
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+Same
+other
+---
+*/
module Same; // makes no difference if removed
import core.stdc.stdio;
diff --git a/gcc/testsuite/gdc.test/runnable/a17.d b/gcc/testsuite/gdc.test/runnable/a17.d
index 79eeecfeb14..21f4f38e46d 100644
--- a/gcc/testsuite/gdc.test/runnable/a17.d
+++ b/gcc/testsuite/gdc.test/runnable/a17.d
@@ -1,8 +1,14 @@
-// EXTRA_SOURCES: imports/a17a.d
+/*
+EXTRA_SOURCES: imports/a17a.d
+RUN_OUTPUT:
+---
+barx
+---
+*/
module a17;
-import std.stdio;
+import core.stdc.stdio;
private import imports.a17a;
diff --git a/gcc/testsuite/gdc.test/runnable/a18.d b/gcc/testsuite/gdc.test/runnable/a18.d
index 4d8b000a138..f568982acee 100644
--- a/gcc/testsuite/gdc.test/runnable/a18.d
+++ b/gcc/testsuite/gdc.test/runnable/a18.d
@@ -1,6 +1,12 @@
-// COMPILE_SEPARATELY
-// EXTRA_SOURCES: imports/a18a.d
-// PERMUTE_ARGS:
+/*
+COMPILE_SEPARATELY
+EXTRA_SOURCES: imports/a18a.d
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+Test enumerator
+---
+*/
import imports.a18a;
diff --git a/gcc/testsuite/gdc.test/runnable/a21.d b/gcc/testsuite/gdc.test/runnable/a21.d
index 6b3be9d814c..65806c4ee63 100644
--- a/gcc/testsuite/gdc.test/runnable/a21.d
+++ b/gcc/testsuite/gdc.test/runnable/a21.d
@@ -1,7 +1,14 @@
-// EXTRA_SOURCES: imports/a21a.d
-// PERMUTE_ARGS:
+/*
+EXTRA_SOURCES: imports/a21a.d
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+goodFunc
+badFunc
+---
+*/
-import std.stdio;
+import core.stdc.stdio;
import imports.a21a;
@@ -26,4 +33,3 @@ int main()
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/aliasassign.d b/gcc/testsuite/gdc.test/runnable/aliasassign.d
new file mode 100644
index 00000000000..986cccc4db8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/aliasassign.d
@@ -0,0 +1,31 @@
+
+
+template AliasSeq(T...) { alias AliasSeq = T; }
+
+template staticMap(alias F, T...)
+{
+ alias A = AliasSeq!();
+ static foreach (t; T)
+ A = AliasSeq!(A, F!t);
+ alias staticMap = A;
+}
+
+template Qual(alias T)
+{
+ alias Qual = T;
+}
+
+void test()
+{
+ int x = 3;
+ int y = 4;
+
+ alias XY = staticMap!(Qual, x, y);
+ assert(XY[0] == 3);
+ assert(XY[1] == 4);
+}
+
+void main()
+{
+ test();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/aliasthis.d b/gcc/testsuite/gdc.test/runnable/aliasthis.d
index ee8e6a13ce0..cc12f55935d 100644
--- a/gcc/testsuite/gdc.test/runnable/aliasthis.d
+++ b/gcc/testsuite/gdc.test/runnable/aliasthis.d
@@ -132,7 +132,7 @@ struct Iter
bool empty() { return true; }
void popFront() { }
ref Tup!(int, int) front() { return *new Tup!(int, int); }
- ref Iter opSlice() { return this; }
+ ref Iter opSlice() return { return this; }
}
void test4()
@@ -153,7 +153,7 @@ void test5()
}
/**********************************************/
-// 4617
+// https://issues.dlang.org/show_bug.cgi?id=4617
struct S4617
{
@@ -215,7 +215,7 @@ void test4617b()
}
/**********************************************/
-// 4773
+// https://issues.dlang.org/show_bug.cgi?id=4773
void test4773()
{
@@ -233,7 +233,7 @@ void test4773()
}
/**********************************************/
-// 5188
+// https://issues.dlang.org/show_bug.cgi?id=5188
void test5188()
{
@@ -434,7 +434,7 @@ void test7()
static assert(!__traits(compiles, { switch (c1) { default: } }));
static assert(!__traits(compiles, { switch (c3) { default: } }));
- // Bugzilla 12537: function arguments with IFTI
+ // https://issues.dlang.org/show_bug.cgi?id=12537: function arguments with IFTI
void eq12537()(Object lhs) {}
const C0 cc0;
const C1 cc1;
@@ -445,7 +445,8 @@ void test7()
}
/***************************************************/
-// 11875 - endless recursion in Type::deduceType
+// https://issues.dlang.org/show_bug.cgi?id=11875
+// endless recursion in Type::deduceType
struct T11875x(C)
{
@@ -467,7 +468,7 @@ class D11875c { T11875y!D11875b c; alias c this; }
static assert(is(D11875c : T11875y!D, D) && is(D == D11875b));
/***************************************************/
-// 11930
+// https://issues.dlang.org/show_bug.cgi?id=11930
class BarObj11930 {}
@@ -492,7 +493,7 @@ void test11930()
}
/***************************************************/
-// 2781
+// https://issues.dlang.org/show_bug.cgi?id=2781
struct Tuple2781a(T...) {
T data;
@@ -594,7 +595,7 @@ void test2781()
}
/**********************************************/
-// 6546
+// https://issues.dlang.org/show_bug.cgi?id=6546
void test6546()
{
@@ -643,7 +644,7 @@ void test6546()
}
/**********************************************/
-// 6736
+// https://issues.dlang.org/show_bug.cgi?id=6736
void test6736()
{
@@ -661,7 +662,7 @@ void test6736()
}
/**********************************************/
-// 2777
+// https://issues.dlang.org/show_bug.cgi?id=2777
struct ArrayWrapper(T) {
T[] array;
@@ -698,7 +699,7 @@ void test2777b()
}
/****************************************/
-// 2787
+// https://issues.dlang.org/show_bug.cgi?id=2787
struct Base2787
{
@@ -715,7 +716,7 @@ struct Derived2787
}
/***********************************/
-// 5679
+// https://issues.dlang.org/show_bug.cgi?id=5679
void test5679()
{
@@ -736,7 +737,7 @@ void test5679()
}
/***********************************/
-// 6508
+// https://issues.dlang.org/show_bug.cgi?id=6508
void test6508()
{
@@ -791,7 +792,7 @@ void test6508x()
}
/***********************************/
-// 6369
+// https://issues.dlang.org/show_bug.cgi?id=6369
void test6369a()
{
@@ -869,7 +870,7 @@ void test6369d()
}
/**********************************************/
-// 6434
+// https://issues.dlang.org/show_bug.cgi?id=6434
struct Variant6434{}
@@ -890,7 +891,7 @@ void test6434()
}
/**************************************/
-// 6366
+// https://issues.dlang.org/show_bug.cgi?id=6366
void test6366()
{
@@ -965,7 +966,7 @@ void test6366()
}
/***************************************************/
-// 6711
+// https://issues.dlang.org/show_bug.cgi?id=6711
void test6711()
{
@@ -989,7 +990,7 @@ void test6711()
}
/**********************************************/
-// 12161
+// https://issues.dlang.org/show_bug.cgi?id=12161
class A12161
{
@@ -1011,7 +1012,7 @@ void test12161()
}
/**********************************************/
-// 6759
+// https://issues.dlang.org/show_bug.cgi?id=6759
struct Range
{
@@ -1037,7 +1038,7 @@ void test6759()
}
/**********************************************/
-// 6479
+// https://issues.dlang.org/show_bug.cgi?id=6479
struct Memory6479
{
@@ -1053,7 +1054,7 @@ mixin template Wrapper6479()
}
/**********************************************/
-// 6832
+// https://issues.dlang.org/show_bug.cgi?id=6832
void test6832()
{
@@ -1071,7 +1072,7 @@ void test6832()
}
/**********************************************/
-// 6928
+// https://issues.dlang.org/show_bug.cgi?id=6928
void test6928()
{
@@ -1094,7 +1095,7 @@ void test6928()
}
/**********************************************/
-// 6929
+// https://issues.dlang.org/show_bug.cgi?id=6929
struct S6929
{
@@ -1114,7 +1115,7 @@ void test6929()
}
/***************************************************/
-// 7136
+// https://issues.dlang.org/show_bug.cgi?id=7136
void test7136()
{
@@ -1140,7 +1141,7 @@ void test7136()
}
/***************************************************/
-// 7731
+// https://issues.dlang.org/show_bug.cgi?id=7731
struct A7731
{
@@ -1183,7 +1184,7 @@ void test7731()
}
/***************************************************/
-// 7808
+// https://issues.dlang.org/show_bug.cgi?id=7808
struct Nullable7808(T)
{
@@ -1213,7 +1214,7 @@ void test7808()
}
/***************************************************/
-// 7945
+// https://issues.dlang.org/show_bug.cgi?id=7945
struct S7945
{
@@ -1232,7 +1233,8 @@ void test7945()
}
/***************************************************/
-// 15674 - alias this on out parameter, consistent with 7945 case
+// https://issues.dlang.org/show_bug.cgi?id=15674
+// alias this on out parameter, consistent with 7945 case
struct S15674
{
@@ -1251,7 +1253,7 @@ void test15674()
}
/***************************************************/
-// 7979
+// https://issues.dlang.org/show_bug.cgi?id=7979
void test7979()
{
@@ -1291,7 +1293,7 @@ void test7979()
}
/***************************************************/
-// 7992
+// https://issues.dlang.org/show_bug.cgi?id=7992
struct S7992
{
@@ -1314,7 +1316,7 @@ void test7992()
}
/***************************************************/
-// 8169
+// https://issues.dlang.org/show_bug.cgi?id=8169
void test8169()
{
@@ -1339,7 +1341,7 @@ void test8169()
}
/***************************************************/
-// 8735
+// https://issues.dlang.org/show_bug.cgi?id=8735
struct S8735(alias Arg)
{
@@ -1360,11 +1362,11 @@ void test8735()
int n = s;
assert(n == 1);
- // 11502 case
+ // https://issues.dlang.org/show_bug.cgi?id=11502
static void f(int i);
S8735!f sf;
- // 9709 case
+ // https://issues.dlang.org/show_bug.cgi?id=9709
alias A = Tuple9709!(1,int,"foo");
A a;
//static assert(A[0] == 1);
@@ -1376,7 +1378,7 @@ void test8735()
}
/***************************************************/
-// 9174
+// https://issues.dlang.org/show_bug.cgi?id=9174
void test9174()
{
@@ -1390,7 +1392,7 @@ void test9174()
}
/***************************************************/
-// 9177
+// https://issues.dlang.org/show_bug.cgi?id=9177
struct S9177
{
@@ -1400,7 +1402,7 @@ struct S9177
pragma(msg, is(S9177 : int));
/***************************************************/
-// 9858
+// https://issues.dlang.org/show_bug.cgi?id=9858
struct S9858()
{
@@ -1418,7 +1420,7 @@ void test9858()
}
/***************************************************/
-// 9873
+// https://issues.dlang.org/show_bug.cgi?id=9873
void test9873()
{
@@ -1453,7 +1455,7 @@ void test9873()
}
/***************************************************/
-// 10178
+// https://issues.dlang.org/show_bug.cgi?id=10178
void test10178()
{
@@ -1473,7 +1475,7 @@ void test10178()
}
/***************************************************/
-// 10179
+// https://issues.dlang.org/show_bug.cgi?id=10179
void test10179()
{
@@ -1493,7 +1495,7 @@ void test10179()
}
/***************************************************/
-// 9890
+// https://issues.dlang.org/show_bug.cgi?id=9890
void test9890()
{
@@ -1519,7 +1521,7 @@ void test9890()
}
/***************************************************/
-// 10004
+// https://issues.dlang.org/show_bug.cgi?id=10004
void test10004()
{
@@ -1541,7 +1543,7 @@ void test10004()
}
/***************************************************/
-// 10180
+// https://issues.dlang.org/show_bug.cgi?id=10180
template TypeTuple10180(TL...) { alias TypeTuple10180 = TL; }
@@ -1580,7 +1582,7 @@ void test10180()
}
/***************************************************/
-// 10456
+// https://issues.dlang.org/show_bug.cgi?id=10456
void test10456()
{
@@ -1596,7 +1598,7 @@ struct S10456
}
/***************************************************/
-// 11261
+// https://issues.dlang.org/show_bug.cgi?id=11261
template Tuple11261(Specs...)
{
@@ -1686,7 +1688,7 @@ void test11261()
}
/***************************************************/
-// 11333
+// https://issues.dlang.org/show_bug.cgi?id=11333
alias id11333(a...) = a;
@@ -1712,7 +1714,22 @@ void test11333()
}
/***************************************************/
-// 11800
+// https://issues.dlang.org/show_bug.cgi?id=11538
+
+struct NullableRef11538(T)
+{
+ T* _value;
+ inout(T) get() inout { return *_value; }
+ alias get this;
+}
+
+struct S11538
+{
+ NullableRef11538!S11538 parent;
+}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11800
struct A11800
{
@@ -1739,7 +1756,7 @@ void test11800()
}
/***************************************************/
-// 12008
+// https://issues.dlang.org/show_bug.cgi?id=12008
struct RefCounted12008(T)
{
@@ -1792,7 +1809,7 @@ struct Group12008
}
/***************************************************/
-// 12038
+// https://issues.dlang.org/show_bug.cgi?id=12038
bool f12038(void* p) { return true; }
@@ -1803,7 +1820,7 @@ struct S12038
}
/***************************************************/
-// 13490
+// https://issues.dlang.org/show_bug.cgi?id=13490
struct S13490
{
@@ -1830,7 +1847,7 @@ void test13490()
}
/***************************************************/
-// 11355
+// https://issues.dlang.org/show_bug.cgi?id=11355
struct A11355
{
@@ -1855,7 +1872,7 @@ void test11355()
}
/***************************************************/
-// 13009
+// https://issues.dlang.org/show_bug.cgi?id=13009
struct T13009
{
@@ -1909,7 +1926,7 @@ void test13009()
}
/***************************************************/
-// 14806
+// https://issues.dlang.org/show_bug.cgi?id=14806
struct Nullable14806
{
@@ -1926,18 +1943,18 @@ struct Foo14806(T)
void test14806()
{
Foo14806!int a, b;
- assert(a != b);
+ assert(a == b);
// ==> a.tupleof != b.tupleof
// ==> a.bar != b.bar || a.baz.get() != b.baz.get()
Foo14806!string c, d;
- assert(c != d);
+ assert(c == d);
// ==> c.tupleof != d.tupleof
// ==> c.bar != d.bar || c.baz.get() != d.baz.get()
}
/***************************************************/
-// 14948
+// https://issues.dlang.org/show_bug.cgi?id=14948
struct RefCounted14948(T)
{
@@ -1967,7 +1984,7 @@ void test14948()
}
/***************************************************/
-// 15292
+// https://issues.dlang.org/show_bug.cgi?id=15292
struct NullableRef15292(T)
{
@@ -1998,6 +2015,93 @@ struct S15292
/***************************************************/
+struct S19284a { int x; }
+struct S19284b
+{
+ S19284a s;
+ alias s this;
+ int t;
+ void f()
+ {
+ void wrapped()
+ {
+ x = 1;
+ t = 1;
+ }
+ wrapped(); // <-- 'x' not found, whereas 's.x' works fine
+ }
+
+ void f1()
+ {
+ x = 2;
+ }
+
+ void f2()
+ {
+ int x;
+ void wrapped()
+ {
+ x = 7;
+ }
+ wrapped();
+ assert(x == 7);
+ }
+
+ void f3()
+ {
+ void wrapped()
+ {
+ void wrapped2()
+ {
+ x = 5;
+ }
+ wrapped2();
+ }
+ wrapped();
+ }
+}
+
+void test19284()
+{
+ S19284b t;
+
+ // nested function modifies alias this
+ t.f();
+ assert(t.x == 1);
+ assert(t.t == 1);
+
+ // member function modifies alias this
+ t.f1();
+ assert(t.x == 2);
+
+ // nested function does not modify alias this when it is shadowd by a local variable
+ t.f2();
+ assert(t.x == 2);
+
+ // multiple levels of nesting
+ t.f3();
+ assert(t.x == 5);
+}
+
+// 16633
+
+class Item
+{
+ alias children this;
+ Item[] children;
+ void populate()
+ {
+ children ~= new Item(); // Item is seen as []
+ assert(children.length == 1);
+ }
+}
+
+void test16633()
+{
+ Item root = new Item();
+ root.populate;
+}
+
int main()
{
test1();
@@ -2054,6 +2158,8 @@ int main()
test13490();
test11355();
test14806();
+ test19284();
+ test16633();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/arrayop.d b/gcc/testsuite/gdc.test/runnable/arrayop.d
index 8c6b083fd30..4c0836a96dc 100644
--- a/gcc/testsuite/gdc.test/runnable/arrayop.d
+++ b/gcc/testsuite/gdc.test/runnable/arrayop.d
@@ -48,28 +48,28 @@ template Floating(T)
abc = null;
A()[] = B()[] + C()[];
- assert(abc == "BCA");
+ assert(abc == "ABC");
assert(a[0] == 5);
assert(a[1] == 7);
assert(a[2] == 9);
abc = null;
A()[] = B()[] + 4;
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 5);
assert(a[1] == 6);
assert(a[2] == 7);
abc = null;
A()[] = 4 + B()[];
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 5);
assert(a[1] == 6);
assert(a[2] == 7);
abc = null;
A()[] = D() + B()[];
- assert(abc == "DBA");
+ assert(abc == "ADB");
assert(a[0] == 5);
assert(a[1] == 6);
assert(a[2] == 7);
@@ -77,7 +77,7 @@ template Floating(T)
a = [11, 22, 33];
abc = null;
A()[] += B()[];
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 12);
assert(a[1] == 24);
assert(a[2] == 36);
@@ -115,14 +115,14 @@ template Floating(T)
a = [11, 22, 33];
abc = null;
A()[] += 4 + B()[];
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 16);
assert(a[1] == 28);
assert(a[2] == 40);
abc = null;
A()[] = B()[] - C()[];
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%Lg, %Lg, %Lg\n", cast(real)a[0], cast(real)a[1], cast(real)a[2]);
assert(a[0] == -3);
assert(a[1] == -3);
@@ -130,7 +130,7 @@ template Floating(T)
abc = null;
A()[] = -B()[] - C()[];
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%Lg, %Lg, %Lg\n", cast(real)a[0], cast(real)a[1], cast(real)a[2]);
assert(a[0] == -5);
assert(a[1] == -7);
@@ -138,7 +138,7 @@ template Floating(T)
abc = null;
A()[] = B()[] + C()[] * 4;
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%Lg, %Lg, %Lg\n", cast(real)a[0], cast(real)a[1], cast(real)a[2]);
assert(a[0] == 17);
assert(a[1] == 22);
@@ -146,7 +146,7 @@ template Floating(T)
abc = null;
A()[] = B()[] + C()[] * B()[];
- assert(abc == "BCBA");
+ assert(abc == "ABCB");
printf("%Lg, %Lg, %Lg\n", cast(real)a[0], cast(real)a[1], cast(real)a[2]);
assert(a[0] == 5);
assert(a[1] == 12);
@@ -154,7 +154,7 @@ template Floating(T)
abc = null;
A()[] = B()[] + C()[] / 2;
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%Lg, %Lg, %Lg\n", cast(real)a[0], cast(real)a[1], cast(real)a[2]);
assert(a[0] == 3);
assert(a[1] == 4.5);
@@ -162,7 +162,7 @@ template Floating(T)
abc = null;
A()[] = B()[] + C()[] % 2;
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%Lg, %Lg, %Lg\n", cast(real)a[0], cast(real)a[1], cast(real)a[2]);
assert(a[0] == 1);
assert(a[1] == 3);
@@ -226,28 +226,28 @@ template Integral(T)
abc = null;
A()[] = B()[] + C()[];
- assert(abc == "BCA");
+ assert(abc == "ABC");
assert(a[0] == 5);
assert(a[1] == 7);
assert(a[2] == 9);
abc = null;
A()[] = B()[] + 4;
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 5);
assert(a[1] == 6);
assert(a[2] == 7);
abc = null;
A()[] = 4 + B()[];
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 5);
assert(a[1] == 6);
assert(a[2] == 7);
abc = null;
A()[] = D() + B()[];
- assert(abc == "DBA");
+ assert(abc == "ADB");
assert(a[0] == 5);
assert(a[1] == 6);
assert(a[2] == 7);
@@ -255,7 +255,7 @@ template Integral(T)
a = [11, 22, 33];
abc = null;
A()[] += B()[];
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 12);
assert(a[1] == 24);
assert(a[2] == 36);
@@ -311,14 +311,14 @@ template Integral(T)
a = [11, 22, 33];
abc = null;
A()[] += 4 + B()[];
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 16);
assert(a[1] == 28);
assert(a[2] == 40);
abc = null;
A()[] = B()[] - C()[];
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%lld, %lld, %lld\n", cast(long)a[0], cast(long)a[1], cast(long)a[2]);
assert(a[0] == -3);
assert(a[1] == -3);
@@ -326,7 +326,7 @@ template Integral(T)
abc = null;
A()[] = -B()[] - C()[];
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%lld, %lld, %lld\n", cast(long)a[0], cast(long)a[1], cast(long)a[2]);
assert(a[0] == -5);
assert(a[1] == -7);
@@ -334,7 +334,7 @@ template Integral(T)
abc = null;
A()[] = B()[] + C()[] * 4;
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%lld, %lld, %lld\n", cast(long)a[0], cast(long)a[1], cast(long)a[2]);
assert(a[0] == 17);
assert(a[1] == 22);
@@ -342,7 +342,7 @@ template Integral(T)
abc = null;
A()[] = B()[] + C()[] * B()[];
- assert(abc == "BCBA");
+ assert(abc == "ABCB");
printf("%lld, %lld, %lld\n", cast(long)a[0], cast(long)a[1], cast(long)a[2]);
assert(a[0] == 5);
assert(a[1] == 12);
@@ -350,7 +350,7 @@ template Integral(T)
abc = null;
A()[] = B()[] + C()[] / 2;
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%lld, %lld, %lld\n", cast(long)a[0], cast(long)a[1], cast(long)a[2]);
assert(a[0] == 3);
assert(a[1] == 4);
@@ -358,7 +358,7 @@ template Integral(T)
abc = null;
A()[] = B()[] + C()[] % 2;
- assert(abc == "BCA");
+ assert(abc == "ABC");
printf("%lld, %lld, %lld\n", cast(long)a[0], cast(long)a[1], cast(long)a[2]);
assert(a[0] == 1);
assert(a[1] == 3);
@@ -366,28 +366,28 @@ template Integral(T)
abc = null;
A()[] = ~B()[];
- assert(abc == "BA");
- assert(a[0] == ~cast(T)1);
- assert(a[1] == ~cast(T)2);
- assert(a[2] == ~cast(T)3);
+ assert(abc == "AB");
+ assert(a[0] == cast(T) ~1);
+ assert(a[1] == cast(T) ~2);
+ assert(a[2] == cast(T) ~3);
abc = null;
A()[] = B()[] & 2;
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 0);
assert(a[1] == 2);
assert(a[2] == 2);
abc = null;
A()[] = B()[] | 2;
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 3);
assert(a[1] == 2);
assert(a[2] == 3);
abc = null;
A()[] = B()[] ^ 2;
- assert(abc == "BA");
+ assert(abc == "AB");
assert(a[0] == 3);
assert(a[1] == 0);
assert(a[2] == 1);
@@ -439,7 +439,7 @@ void test4662()
}
/***************************************************/
-// 5284
+// https://issues.dlang.org/show_bug.cgi?id=5284
void bug5284_1()
{
@@ -550,7 +550,7 @@ void test8390() {
}
/************************************************************************/
-// 8651
+// https://issues.dlang.org/show_bug.cgi?id=8651
void test8651()
{
@@ -605,7 +605,7 @@ void test8651()
}
/************************************************************************/
-// 9656
+// https://issues.dlang.org/show_bug.cgi?id=9656
void test9656()
{
@@ -628,14 +628,14 @@ void test9656()
{
int[] ma = [1,2,3];
- immutable ia = ma.dup;
+ immutable ia = ma.idup;
}
{
static struct V { int val; }
V[] ma = [V(1), V(2)];
- immutable ia = ma.dup;
+ immutable ia = ma.idup;
}
{
@@ -651,7 +651,7 @@ void test9656()
}
/************************************************************************/
-// 10282
+// https://issues.dlang.org/show_bug.cgi?id=10282
void test10282()
{
@@ -669,7 +669,7 @@ void test10282()
}
/************************************************************************/
-// 10433
+// https://issues.dlang.org/show_bug.cgi?id=10433
void test10433()
{
@@ -684,7 +684,7 @@ void test10433()
}
/************************************************************************/
-// 10684
+// https://issues.dlang.org/show_bug.cgi?id=10684
void test10684a()
{
@@ -716,7 +716,7 @@ void test10684b()
}
/************************************************************************/
-// 11376
+// https://issues.dlang.org/show_bug.cgi?id=11376
template TL11376(T...)
{
@@ -732,7 +732,7 @@ auto sumArrs11376(T0, T1)(T0[] a, T1[] b)
static assert(!__traits(compiles, sumArrs11376(TL11376!(string[], string).init)));
/************************************************************************/
-// 11525
+// https://issues.dlang.org/show_bug.cgi?id=11525
void test11525()
{
@@ -756,7 +756,7 @@ void test11525()
}
/************************************************************************/
-// 12250
+// https://issues.dlang.org/show_bug.cgi?id=12250
void f12250(inout int[] p, inout int[] q, int[] r)
{
@@ -774,7 +774,7 @@ void test12250()
}
/************************************************************************/
-// 12179
+// https://issues.dlang.org/show_bug.cgi?id=12179
void test12179()
{
@@ -792,7 +792,7 @@ void test12179()
foo(a[] |= a[]);
foo(a[] ^^= a[]);
- // from issue 11992
+ // from https://issues.dlang.org/show_bug.cgi?id=11992
int[] arr1;
int[][] arr2;
arr1 ~= (a[] = [1] + a[]); // OK
@@ -800,7 +800,7 @@ void test12179()
}
/************************************************************************/
-// 12780
+// https://issues.dlang.org/show_bug.cgi?id=12780
void test12780()
{
@@ -854,7 +854,7 @@ void test12780()
}
/************************************************************************/
-// 13497
+// https://issues.dlang.org/show_bug.cgi?id=13497
void test13497()
{
@@ -866,7 +866,7 @@ void test13497()
}
/************************************************************************/
-// 14649
+// https://issues.dlang.org/show_bug.cgi?id=14649
void test14649()
{
@@ -891,7 +891,7 @@ void test14649()
}
/************************************************************************/
-// 14851
+// https://issues.dlang.org/show_bug.cgi?id=14851
void test14851()
{
diff --git a/gcc/testsuite/gdc.test/runnable/auto1.d b/gcc/testsuite/gdc.test/runnable/auto1.d
index e68b06380ef..ea023836bf2 100644
--- a/gcc/testsuite/gdc.test/runnable/auto1.d
+++ b/gcc/testsuite/gdc.test/runnable/auto1.d
@@ -1,3 +1,16 @@
+/*
+RUN_OUTPUT:
+---
+Foo.~this()
+Foo.~this()
+Foo.~this()
+Foo.~this()
+A2.this()
+Hello world.
+A2.~this()
+Success
+---
+*/
import core.stdc.stdio;
diff --git a/gcc/testsuite/gdc.test/runnable/b10562.d b/gcc/testsuite/gdc.test/runnable/b10562.d
new file mode 100644
index 00000000000..cf79d164461
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/b10562.d
@@ -0,0 +1,93 @@
+void main()
+{
+ {
+ int[3] ok = 3;
+ assert(ok == [ 3, 3, 3]);
+
+ ok = 1;
+ assert(ok == [ 1, 1, 1]);
+
+ int[][] da2;
+ assert(da2 == []);
+ }
+ {
+ int[3][2] a;
+ assert(a == [ [ 0, 0, 0 ], [ 0, 0, 0 ] ]);
+
+ int[3][2] b = 4;
+ assert(b == [ [ 4, 4, 4 ], [ 4, 4, 4 ] ]);
+
+ // b = 9;
+ // assert(b == [ [ 9, 9, 9 ], [ 9, 9, 9 ] ]);
+
+ int[3][2] c = [ 1, 2, 3 ];
+ assert(c == [ [ 1, 2, 3 ], [ 1, 2, 3 ] ]);
+
+ c = [ 5, 6, 7 ];
+ assert(c == [ [ 5, 6, 7 ], [ 5, 6, 7 ] ]);
+
+ int[3][2] d = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ];
+ assert(d == [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]);
+ }
+ {
+ int[3][2][4] a;
+ assert(a == [ [ [ 0, 0, 0 ], [ 0, 0, 0 ] ],
+ [ [ 0, 0, 0 ], [ 0, 0, 0 ] ],
+ [ [ 0, 0, 0 ], [ 0, 0, 0 ] ],
+ [ [ 0, 0, 0 ], [ 0, 0, 0 ] ] ]);
+
+ // a = 1;
+ // assert(a == [ [ [ 1, 1, 1 ], [ 1, 1, 1 ] ],
+ // [ [ 1, 1, 1 ], [ 1, 1, 1 ] ],
+ // [ [ 1, 1, 1 ], [ 1, 1, 1 ] ],
+ // [ [ 1, 1, 1 ], [ 1, 1, 1 ] ] ]);
+
+ int[3][2][4] b = [ 1, 2, 3 ];
+ assert(b == [ [ [ 1, 2, 3 ], [ 1, 2, 3 ] ],
+ [ [ 1, 2, 3 ], [ 1, 2, 3 ] ],
+ [ [ 1, 2, 3 ], [ 1, 2, 3 ] ],
+ [ [ 1, 2, 3 ], [ 1, 2, 3 ] ] ]);
+
+ // b = [ 4, 5, 6];
+ // assert(b == [ [ [ 4, 5, 6 ], [ 4, 5, 6 ] ],
+ // [ [ 4, 5, 6 ], [ 4, 5, 6 ] ],
+ // [ [ 4, 5, 6 ], [ 4, 5, 6 ] ],
+ // [ [ 4, 5, 6 ], [ 4, 5, 6 ] ] ]);
+
+ int[3][2][4] c = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ];
+ assert(c == [ [ [ 1, 2, 3 ], [ 4, 5, 6 ] ],
+ [ [ 1, 2, 3 ], [ 4, 5, 6 ] ],
+ [ [ 1, 2, 3 ], [ 4, 5, 6 ] ],
+ [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] ]);
+
+ c = [ [ 4, 5, 6 ], [ 7, 8, 9 ] ];
+ assert(c == [ [ [ 4, 5, 6 ], [ 7, 8, 9 ] ],
+ [ [ 4, 5, 6 ], [ 7, 8, 9 ] ],
+ [ [ 4, 5, 6 ], [ 7, 8, 9 ] ],
+ [ [ 4, 5, 6 ], [ 7, 8, 9 ] ] ]);
+ }
+ {
+ int[3] val = [4, 5, 6];
+ int[3][2][4] a = val[];
+
+ assert(a == [ [ [ 4, 5, 6 ], [ 4, 5, 6 ] ],
+ [ [ 4, 5, 6 ], [ 4, 5, 6 ] ],
+ [ [ 4, 5, 6 ], [ 4, 5, 6 ] ],
+ [ [ 4, 5, 6 ], [ 4, 5, 6 ] ] ]);
+ }
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=10562
+ int[3] value = [ 1, 2, 3 ];
+ int[3][2] a = value; // <-- COMPILATION ERROR
+ assert(a == [ [ 1, 2, 3 ], [ 1, 2, 3 ] ]);
+ }
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20465
+ int[][3][2] arr;
+ assert(arr == [[ null, null, null ], [ null, null, null ]]);
+
+ // int[] slice = [ 1 ];
+ // arr = slice;
+ // assert(arr == [[ slice, slice, slice ], [ slice, slice, slice ]]);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/b16360.d b/gcc/testsuite/gdc.test/runnable/b16360.d
new file mode 100644
index 00000000000..50c28e6d7c9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/b16360.d
@@ -0,0 +1,50 @@
+// REQUIRED_ARGS: -inline
+
+pragma(inline, true)
+auto foo()
+{
+ static struct U
+ {
+ int a = 42;
+ float b;
+ }
+ U u;
+ return u.a;
+}
+
+pragma(inline, true)
+T bitCast(T, S)(auto ref S s)
+{
+ union BitCaster
+ {
+ S ss;
+ T tt;
+ }
+ BitCaster bt;
+ bt.ss = s;
+ return bt.tt;
+}
+
+pragma(inline, true)
+int withFuncCalls()
+{
+ static struct WithFuncCalls
+ {
+ int v;
+ pragma(inline, true)
+ int call(){return v;}
+ pragma(inline, true)
+ void otherCall(){v++;}
+ }
+ auto bt = WithFuncCalls(50);
+ bt.v += -9;
+ bt.otherCall();
+ return bt.call();
+}
+
+void main()
+{
+ assert(foo == 42);
+ assert(bitCast!int(1.0f) == 0x3f800000);
+ assert(withFuncCalls() == 42);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/b18034.d b/gcc/testsuite/gdc.test/runnable/b18034.d
new file mode 100644
index 00000000000..3981aafdbc4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/b18034.d
@@ -0,0 +1,28 @@
+// REQUIRED_ARGS: -O
+import core.simd;
+
+static if (__traits(compiles, { void16 a; ushort8 b; }))
+{
+ void check(void16 a)
+ {
+ foreach (x; (cast(ushort8)a).array)
+ {
+ assert(x == 1);
+ }
+ }
+
+ void make(ushort x)
+ {
+ ushort8 v = ushort8(x);
+ check(v);
+ }
+
+ void main()
+ {
+ make(1);
+ }
+}
+else
+{
+ void main() { }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/b19584.d b/gcc/testsuite/gdc.test/runnable/b19584.d
new file mode 100644
index 00000000000..8eb790e0370
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/b19584.d
@@ -0,0 +1,13 @@
+/*
+REQUIRED_ARGS: -O
+*/
+void main()
+{
+ int a = 711;
+ assert(182215 == (a | (a << 8)));
+ assert(182727 == (a * (1 + (1 << 8))));
+
+ int b = 31;
+ assert(511 == (b | (b << 4)));
+ assert(527 == (b * (1 + (1 << 4))));
+}
diff --git a/gcc/testsuite/gdc.test/runnable/b20470.d b/gcc/testsuite/gdc.test/runnable/b20470.d
new file mode 100644
index 00000000000..73d8d10ff04
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/b20470.d
@@ -0,0 +1,97 @@
+// https://issues.dlang.org/show_bug.cgi?id=20470
+
+alias AliasSeq(Args...) = Args;
+
+int g, h;
+
+void test0()
+{
+ static struct S
+ {
+ int a, b;
+ float c = 0, d = 0;
+ alias fields = AliasSeq!(a, b, c, d);
+ alias ints = AliasSeq!(a, b);
+ alias floats = AliasSeq!(c, d);
+ alias reversed = AliasSeq!(d, c, b, a);
+ alias globals = AliasSeq!(g, h);
+
+ alias properties = AliasSeq!(e, f);
+ @property int e() { return a; }
+ @property void e(int i) { a = i; }
+ @property float f() { return c; }
+ @property void f(float j) { c = j; }
+ }
+
+ S s;
+ assert(s.fields == AliasSeq!(0, 0, 0, 0));
+ s.ints = AliasSeq!(1, 2);
+ assert(s.fields == AliasSeq!(1, 2, 0, 0));
+ s.floats = AliasSeq!(3, 4);
+ assert(s.fields == AliasSeq!(1, 2, 3, 4));
+
+ int a, b;
+ float c, d;
+ AliasSeq!(d, c, b, a) = s.reversed;
+ assert(AliasSeq!(a, b, c, d) == AliasSeq!(1, 2, 3, 4));
+
+ s.globals = AliasSeq!(30, 40);
+ assert(g == 30 && h == 40);
+
+ // Propagating `this` to functions and properties within tuples will be a breaking change.
+ // See `test2()` below for an example of existing code that would need to be fixed.
+
+ //s.properties = AliasSeq!(11, 12);
+ //assert(s.e == 11 && s.f == 12);
+}
+
+class Nested(Vars...)
+{
+ int a, b;
+ alias outervars = Vars;
+ alias fields = AliasSeq!(a, b);
+ alias all = AliasSeq!(a, b, Vars);
+}
+
+auto makeNested()
+{
+ static class C
+ {
+ bool b;
+ double d;
+ auto nested() { return new Nested!(b, d)(); }
+ }
+ return new C().nested();
+}
+
+void test1()
+{
+ auto n = makeNested();
+ n.fields = AliasSeq!(1, 2);
+ n.outervars = AliasSeq!(true, 1.3);
+ assert(n.all == AliasSeq!(1, 2, true, 1.3));
+}
+
+void test2()
+{
+ // backwards compatibility test for functions within tuples
+
+ static struct S
+ {
+ void f();
+ void g();
+ alias funcs = AliasSeq!(f, g);
+ }
+
+ S s;
+ alias voidTf = void();
+ foreach (f; s.funcs)
+ static assert(is(typeof(f) == voidTf));
+}
+
+void main()
+{
+ test0();
+ test1();
+ test2();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/b20890.d b/gcc/testsuite/gdc.test/runnable/b20890.d
new file mode 100644
index 00000000000..9b208da3f77
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/b20890.d
@@ -0,0 +1,48 @@
+module b20890;
+// https://issues.dlang.org/show_bug.cgi?id=20890
+
+struct S0 { }
+void format0(string spec, S0[1] s)
+{
+ assert (spec == "lengthy");
+}
+struct S1 { ubyte m = 42; }
+void format1(string spec, S1[1] s)
+{
+ assert (spec == "lengthy");
+ assert (s[0].m == 42);
+}
+struct S2 { ushort m = 42; }
+void format2(string spec, S2[1] s)
+{
+ assert (spec == "lengthy");
+ assert (s[0].m == 42);
+}
+struct S4 { uint m = 42; }
+void format4(string spec, S4[1] s)
+{
+ assert (spec == "lengthy");
+ assert (s[0].m == 42);
+}
+struct S8 { ulong m = 42; }
+void format8(string spec, S8[1] s)
+{
+ assert (spec == "lengthy");
+ assert (s[0].m == 42);
+}
+struct S42 { ubyte[42] m = [42]; }
+void format42(string spec, S42[1] s)
+{
+ assert (spec == "lengthy");
+ assert (s[0].m[0] == 42);
+}
+
+void main()
+{
+ { S0[1] s; format0("lengthy", s); }
+ { S1[1] s; format1("lengthy", s); }
+ { S2[1] s; format2("lengthy", s); }
+ { S4[1] s; format4("lengthy", s); }
+ { S8[1] s; format8("lengthy", s); }
+ {S42[1] s;format42("lengthy", s); }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/b26.d b/gcc/testsuite/gdc.test/runnable/b26.d
index e32533b2b48..54e28daaa26 100644
--- a/gcc/testsuite/gdc.test/runnable/b26.d
+++ b/gcc/testsuite/gdc.test/runnable/b26.d
@@ -2,7 +2,7 @@
// EXTRA_SOURCES: imports/b26a.d
// PERMUTE_ARGS:
-// 382
+// https://issues.dlang.org/show_bug.cgi?id=382
struct List(T) {
interface A {}
diff --git a/gcc/testsuite/gdc.test/runnable/b6400.d b/gcc/testsuite/gdc.test/runnable/b6400.d
new file mode 100644
index 00000000000..8b1018400f2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/b6400.d
@@ -0,0 +1,69 @@
+/* TEST_OUTPUT:
+---
+Foo
+Bar
+Foo
+Bar
+Bar
+Foo
+Bar
+---
+*/
+
+// https://issues.dlang.org/show_bug.cgi?id=6400
+
+enum int base(string name) = 10 * (name[$-1] - '0');
+struct Foo { int opDispatch(string name)() { pragma(msg, "Foo"); return base!name + 1; } }
+struct Bar { int opDispatch(string name)() { pragma(msg, "Bar"); return base!name + 2; } }
+struct Baz { }
+
+void main()
+{
+ assert(test());
+ static assert(test());
+}
+
+bool test()
+{
+ auto foo = new Foo;
+ auto bar = new Bar;
+ auto baz = new Baz;
+
+ with (foo)
+ {
+ assert(f1() == 11);
+ with (baz) assert(f1() == 11);
+ with (bar)
+ {
+ assert(f2() == 22);
+ with (baz) assert(f2() == 22);
+ with (foo)
+ {
+ assert(f3() == 31);
+ with (baz) assert(f3() == 31);
+ with (bar)
+ {
+ assert(f4() == 42);
+ with (baz) assert(f4() == 42);
+ with (baz)
+ {
+ assert(f5() == 52);
+ with (baz) assert(f5() == 52);
+ }
+ with (foo)
+ {
+ assert(f6() == 61);
+ with (baz) assert(f6() == 61);
+ }
+ with (bar)
+ {
+ assert(f7() == 72);
+ with (baz) assert(f7() == 72);
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/bench1.d b/gcc/testsuite/gdc.test/runnable/bench1.d
index 74b24a4c6c1..d19ba9a9262 100644
--- a/gcc/testsuite/gdc.test/runnable/bench1.d
+++ b/gcc/testsuite/gdc.test/runnable/bench1.d
@@ -1,5 +1,12 @@
-// REQUIRED_ARGS:
-// EXECUTE_ARGS: 10000
+/*
+REQUIRED_ARGS:
+EXECUTE_ARGS: 10000
+RUN_OUTPUT:
+---
+count = 10000
+70000
+---
+*/
extern(C) int printf(const char *, ...);
extern(C) int atoi(const char *);
@@ -18,7 +25,7 @@ extern(C) int atoi(const char *);
s ~= "hello\n";
for (loop = 0; loop < count; loop ++)
s ~= "h";
- printf ("%d\n", s.length);
+ printf ("%llu\n", cast(ulong) s.length);
//printf("%.*s\n", s[0..100]);
assert(s.length == count * (6 + 1));
s.length = 3;
@@ -27,4 +34,3 @@ extern(C) int atoi(const char *);
s.length = 1000;
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/betterc.d b/gcc/testsuite/gdc.test/runnable/betterc.d
new file mode 100644
index 00000000000..0da798bf119
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/betterc.d
@@ -0,0 +1,202 @@
+/* REQUIRED_ARGS: -betterC
+ PERMUTE_ARGS:
+ */
+
+
+void test(int ij)
+{
+ assert(ij);
+#line 100 "anotherfile"
+ assert(ij,"it is not zero");
+}
+
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18010
+
+void test1()
+{
+ int[10] a1 = void;
+ int[10] a2 = void;
+ a1[] = a2[];
+}
+
+void test2(int[] a1, int[] a2)
+{
+ a1[] = a2[];
+}
+
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=17843
+
+struct S
+{
+ double d = 0.0;
+ int[] x;
+}
+
+/*******************************************/
+
+extern (C) void main()
+{
+ test(1);
+ test18472();
+ testRuntimeLowerings();
+ test18457();
+}
+
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=17605
+
+extern (C) void test17605()
+{
+ int a;
+ enum bool works = __traits(compiles, { a = 1; });
+ a = 1;
+}
+
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18472
+
+void test18472()
+{
+ version(D_LP64)
+ {
+ enum b = typeid(size_t) is typeid(ulong);
+ }
+ else
+ {
+ enum b = typeid(size_t) is typeid(uint);
+ }
+
+ assert(b);
+}
+
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18493
+
+struct S18493
+{
+ this(this) nothrow { } // Since this is attributed with `nothrow` there should be no error about using
+ // try-catch with -betterC
+ ~this() { }
+}
+
+struct S18493_2
+{
+ S18493 s1;
+ S18493 s2;
+}
+
+/******************************************************
+ * tests to ensure there is sufficient runtime support
+ * in imported object.d
+ */
+mixin template initArray()
+{
+ static if (is(T == bool))
+ {
+ T[6] a1 = [true, false, true, true, false, true];
+ }
+ else static if (is(T == Sint))
+ {
+ T[6] a1 = [Sint(1), Sint(2), Sint(3), Sint(1), Sint(2), Sint(3)];
+ }
+ else
+ {
+ T[6] a1 = [1,2,3,1,2,3];
+ }
+}
+
+struct Sint
+{
+ int x;
+ this(int v) { x = v;}
+}
+
+void testRuntimeLowerings()
+{
+ // test call to `object.__equals`
+ void test__equals(T)()
+ {
+ mixin initArray;
+
+ assert(a1[0..3] == a1[3..$]);
+ }
+
+ test__equals!int;
+ test__equals!uint;
+ test__equals!long;
+ test__equals!ulong;
+ test__equals!short;
+ test__equals!ushort;
+ test__equals!byte;
+ test__equals!dchar;
+ test__equals!wchar;
+ test__equals!ubyte;
+ test__equals!char;
+ test__equals!(const char);
+ test__equals!bool;
+ test__equals!Sint;
+
+ // test call to `object.__cmp`
+ void test__cmp(T)()
+ {
+ mixin initArray;
+
+ assert(a1[0..3] >= a1[3..$]);
+ assert(a1[0..3] <= a1[3..$]);
+ }
+
+ test__cmp!int;
+ test__cmp!uint;
+ test__cmp!long;
+ test__cmp!ulong;
+ test__cmp!short;
+ test__cmp!ushort;
+ test__cmp!byte;
+ test__cmp!dchar;
+ test__cmp!wchar;
+ test__cmp!ubyte;
+ test__cmp!char;
+ test__cmp!(const char);
+ test__cmp!bool;
+ test__cmp!Sint;
+
+ // test call to `object.__switch``
+ auto s = "abc";
+ switch(s)
+ {
+ case "abc":
+ break;
+ default:
+ break;
+ }
+}
+
+/**********************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18457
+
+__gshared int dtor;
+
+struct S18457
+{
+ int a = 3;
+ ~this() { a = 0; ++dtor; }
+}
+
+S18457 myFunction()
+{
+ S18457 s = S18457();
+ return s;
+}
+
+void test18457()
+{
+ {
+ S18457 s = myFunction();
+ assert(s.a == 3);
+ assert(dtor == 0);
+ }
+ assert(dtor == 1);
+}
+
diff --git a/gcc/testsuite/gdc.test/runnable/bettercUnittest.d b/gcc/testsuite/gdc.test/runnable/bettercUnittest.d
new file mode 100644
index 00000000000..ace2c1422db
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/bettercUnittest.d
@@ -0,0 +1,38 @@
+/*
+REQUIRED_ARGS: -betterC -unittest
+PERMUTE_ARGS:
+EXTRA_SOURCES: extra-files/moreBettercUnittests.d
+*/
+
+import moreBettercUnittests;
+
+unittest
+{
+ sum |= 0x1;
+}
+
+unittest
+{
+ sum |= 0x10;
+}
+
+extern (C) int main()
+{
+ uint count;
+
+ static foreach (alias unit; __traits(getUnitTests, bettercUnittest))
+ {
+ unit();
+ count++;
+ }
+
+ static foreach (alias unit; __traits(getUnitTests, moreBettercUnittests))
+ {
+ unit();
+ count++;
+ }
+
+ assert(count == 4);
+ assert(sum == 0x1111);
+ return sum == 0x1111 ? 0 : 1;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/bitops.d b/gcc/testsuite/gdc.test/runnable/bitops.d
index eb51f0f475f..28a18c4a767 100644
--- a/gcc/testsuite/gdc.test/runnable/bitops.d
+++ b/gcc/testsuite/gdc.test/runnable/bitops.d
@@ -7,7 +7,7 @@ import core.bitop;
void test1()
{
- size_t array[2];
+ size_t[2] array;
uint x;
version (D_LP64)
size_t bitToUse = 67;
@@ -16,35 +16,35 @@ else
array[0] = 2;
array[1] = 0x100;
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
x = btc(array.ptr, bitToUse);
- printf("btc(array, %d) = %d\n", bitToUse, x);
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("btc(array, %zd) = %d\n", bitToUse, x);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
assert(x == 0);
assert(array[0] == 0x2 && array[1] == 0x108);
x = btc(array.ptr, bitToUse);
- printf("btc(array, %d) = %d\n", bitToUse, x);
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("btc(array, %zd) = %d\n", bitToUse, x);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
assert(x != 0);
assert(array[0] == 2 && array[1] == 0x100);
x = bts(array.ptr, bitToUse);
- printf("bts(array, %d) = %d\n", bitToUse, x);
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("bts(array, %zd) = %d\n", bitToUse, x);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
assert(x == 0);
assert(array[0] == 2 && array[1] == 0x108);
x = btr(array.ptr, bitToUse);
- printf("btr(array, %d) = %d\n", bitToUse, x);
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("btr(array, %zd) = %d\n", bitToUse, x);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
assert(x != 0);
assert(array[0] == 2 && array[1] == 0x100);
x = bt(array.ptr, 1);
printf("bt(array, 1) = %d\n", x);
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
assert(x != 0);
assert(array[0] == 2 && array[1] == 0x100);
}
@@ -69,17 +69,19 @@ void test2()
/*****************************************************/
version (DigitalMars)
-void test3()
-{ uint v;
- int b;
-
- b = inp(b);
- b = inpw(b);
- b = inpl(b);
-
- b = outp(v, cast(ubyte)b);
- b = outpw(v, cast(ushort)b);
- b = outpl(v, b);
+{
+ void test3()
+ { uint v;
+ int b;
+
+ b = inp(b);
+ b = inpw(b);
+ b = inpl(b);
+
+ b = outp(v, cast(ubyte)b);
+ b = outpw(v, cast(ushort)b);
+ b = outpl(v, b);
+ }
}
/*****************************************************/
@@ -95,25 +97,25 @@ void test4()
void test5()
{
- size_t array[2];
+ size_t[2] array;
array[0] = 2;
array[1] = 0x100;
printf("btc(array, 35) = %d\n", btc(array.ptr, 35));
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
printf("btc(array, 35) = %d\n", btc(array.ptr, 35));
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
printf("bts(array, 35) = %d\n", bts(array.ptr, 35));
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
printf("btr(array, 35) = %d\n", btr(array.ptr, 35));
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
printf("bt(array, 1) = %d\n", bt(array.ptr, 1));
- printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]);
+ printf("array = [0]:x%zx, [1]:x%zx\n", array[0], array[1]);
}
diff --git a/gcc/testsuite/gdc.test/runnable/bug11155.d b/gcc/testsuite/gdc.test/runnable/bug11155.d
index afe4208ebaf..b346ce2ca48 100644
--- a/gcc/testsuite/gdc.test/runnable/bug11155.d
+++ b/gcc/testsuite/gdc.test/runnable/bug11155.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
-version(D_SIMD)
+static if (__traits(compiles, __vector(float[4])))
{
alias float4 = __vector(float[4]);
diff --git a/gcc/testsuite/gdc.test/runnable/bug19652.d b/gcc/testsuite/gdc.test/runnable/bug19652.d
new file mode 100644
index 00000000000..e922b5926a4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/bug19652.d
@@ -0,0 +1,22 @@
+
+struct Base {
+ int i;
+}
+
+struct A {
+ Base base;
+ alias base this;
+}
+
+struct B {
+ A a;
+ alias a this;
+}
+
+auto otherTest(inout ref Base block) @nogc { assert(0); }
+auto otherTest(inout ref A block) @nogc {}
+
+void main() {
+ B* thingie;
+ otherTest(*thingie);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/bug7068.d b/gcc/testsuite/gdc.test/runnable/bug7068.d
index 70e351e8154..75ea5bf2b8d 100644
--- a/gcc/testsuite/gdc.test/runnable/bug7068.d
+++ b/gcc/testsuite/gdc.test/runnable/bug7068.d
@@ -1,4 +1,5 @@
-// PERMUTE_ARGS: -inline -g -O -d
+// REQUIRED_ARGS: -d
+// PERMUTE_ARGS: -inline -g -O
void main()
{
auto darray1 = new int*[](10);
diff --git a/gcc/testsuite/gdc.test/runnable/builtin.d b/gcc/testsuite/gdc.test/runnable/builtin.d
index 44817b16ee0..a7f09a378d6 100644
--- a/gcc/testsuite/gdc.test/runnable/builtin.d
+++ b/gcc/testsuite/gdc.test/runnable/builtin.d
@@ -1,7 +1,5 @@
// RUNNABLE_PHOBOS_TEST
-
-import std.stdio;
-import std.math;
+import core.math;
import core.bitop;
version (DigitalMars)
@@ -12,30 +10,31 @@ version (DigitalMars)
version = AnyX86;
}
+bool isClose(real lhs, real rhs, real maxRelDiff = 1e-09L, real maxAbsDiff = 0.0)
+{
+ if (lhs == rhs)
+ return true;
+ if (lhs == lhs.infinity || rhs == rhs.infinity ||
+ lhs == -lhs.infinity || rhs == -rhs.infinity)
+ return false;
+
+ auto diff = fabs(lhs - rhs);
+ return diff <= maxRelDiff*fabs(lhs)
+ || diff <= maxRelDiff*fabs(rhs)
+ || diff <= maxAbsDiff;
+}
+
/*******************************************/
void test1()
{
- writefln("%a", sin(6.8L));
auto f = 6.8L;
- writefln("%a", sin(f));
assert(sin(f) == sin(6.8L));
- static assert(approxEqual(sin(6.8L), 0x1.f9f8d9aea10fdf1cp-2));
+ static assert(isClose(sin(6.8L), 0x1.f9f8d9aea10fdf1cp-2));
- writefln("%a", cos(6.8L));
f = 6.8L;
- writefln("%a", cos(f));
assert(cos(f) == cos(6.8L));
- static assert(approxEqual(cos(6.8L), 0x1.bd21aaf88dcfa13ap-1));
-
- writefln("%a", tan(6.8L));
- f = 6.8L;
- writefln("%a", tan(f));
- version (Win64)
- { }
- else
- assert(tan(f) == tan(6.8L));
- static assert(approxEqual(tan(6.8L), 0x1.22fd752af75cd08cp-1));
+ static assert(isClose(cos(6.8L), 0x1.bd21aaf88dcfa13ap-1));
}
/*******************************************/
@@ -55,7 +54,7 @@ void test2()
assert(i == 2);
}
-/**** Bug 5703 *****************************/
+/**** https://issues.dlang.org/show_bug.cgi?id=5703 ****/
static assert({
int a = 0x80;
@@ -113,7 +112,5 @@ int main()
test1();
test2();
test3();
-
- printf("Success\n");
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/cassert.d b/gcc/testsuite/gdc.test/runnable/cassert.d
deleted file mode 100644
index 8c767508495..00000000000
--- a/gcc/testsuite/gdc.test/runnable/cassert.d
+++ /dev/null
@@ -1,17 +0,0 @@
-/* REQUIRED_ARGS: -betterC
- PERMUTE_ARGS:
- */
-
-
-void test(int ij)
-{
- assert(ij);
-#line 100 "anotherfile"
- assert(ij,"it is not zero");
-}
-
-extern (C) int main()
-{
- test(1);
- return 0;
-}
diff --git a/gcc/testsuite/gdc.test/runnable/casting.d b/gcc/testsuite/gdc.test/runnable/casting.d
index 233cc94f634..1eb262c5c68 100644
--- a/gcc/testsuite/gdc.test/runnable/casting.d
+++ b/gcc/testsuite/gdc.test/runnable/casting.d
@@ -1,9 +1,15 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern(C) int printf(const char*, ...);
template Seq(T...) { alias T Seq; }
/***************************************************/
-// 3133
+// https://issues.dlang.org/show_bug.cgi?id=3133
void test3133()
{
@@ -12,7 +18,7 @@ void test3133()
}
/***************************************************/
-// 7504
+// https://issues.dlang.org/show_bug.cgi?id=7504
void test7504() pure nothrow @safe
{
@@ -56,7 +62,7 @@ C7504 create7504(T...)(T input)
}
/***************************************************/
-// 8119
+// https://issues.dlang.org/show_bug.cgi?id=8119
struct S8119;
@@ -73,7 +79,7 @@ void test8119()
}
/***************************************************/
-// 8645
+// https://issues.dlang.org/show_bug.cgi?id=8645
template TypeTuple8645(TL...)
{
@@ -88,7 +94,7 @@ void test8645()
}
/***************************************************/
-// 10497
+// https://issues.dlang.org/show_bug.cgi?id=10497
struct S10497;
@@ -99,7 +105,7 @@ void test10497(S10497** s)
}
/***************************************************/
-// 10793
+// https://issues.dlang.org/show_bug.cgi?id=10793
struct RealFoo10793
{
@@ -116,7 +122,7 @@ void test10793()
}
/***************************************************/
-// 10834
+// https://issues.dlang.org/show_bug.cgi?id=10834
void test10834()
{
@@ -137,7 +143,7 @@ void test10834()
}
/***************************************************/
-// 10842
+// https://issues.dlang.org/show_bug.cgi?id=10842
template Test10842(F, T)
{
@@ -162,30 +168,12 @@ template Test10842(F, T)
void test10842()
{
- foreach (From; Seq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real))
- {
- foreach (To; Seq!(ifloat, idouble, ireal))
- {
- if (!Test10842!(From, To).test())
- assert(0);
- }
- }
-
- foreach (From; Seq!(ifloat, idouble, ireal))
- {
- foreach (To; Seq!(/*bool*, */byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real))
- {
- if (!Test10842!(From, To).test())
- assert(0);
- }
- }
-
if (!Test10842!(typeof(null), string).test()) // 10842
assert(0);
}
/***************************************************/
-// 11722
+// https://issues.dlang.org/show_bug.cgi?id=11722
class C11722
{
@@ -199,7 +187,7 @@ void test11722()
}
/***************************************************/
-// 14218
+// https://issues.dlang.org/show_bug.cgi?id=14218
void test14218()
{
@@ -214,18 +202,11 @@ void test14218()
version (DigitalMars)
{
// Questionable but currently accepted by DMD (but not GDC).
- foreach (To; Seq!( float, double, real,
- ifloat, idouble, ireal))
+ foreach (To; Seq!( float, double, real))
{
auto x = cast(To)null;
assert(x == 0); // 0i
}
-
- // Internal error: backend/el.c in el_long()
- //foreach (To; Seq!(cfloat, cdouble, creal))
- //{
- // static assert(!__traits(compiles, { auto x = cast(To)null; }));
- //}
}
}
diff --git a/gcc/testsuite/gdc.test/runnable/circular.d b/gcc/testsuite/gdc.test/runnable/circular.d
deleted file mode 100644
index 8fdc8aa7d49..00000000000
--- a/gcc/testsuite/gdc.test/runnable/circular.d
+++ /dev/null
@@ -1,25 +0,0 @@
-// REQUIRED_ARGS: -d
-// PERMUTE_ARGS: -dw
-// EXTRA_SOURCES: imports/circularA.d
-// This bug is typedef-specific.
-
-// Bugzilla 4543
-
-import core.stdc.stdio;
-import imports.circularA;
-
-class bclass {};
-alias bclass Tclass;
-
-struct bstruct {}
-alias bstruct Tstruct;
-
-
-/************************************/
-
-int main()
-{
- printf("Success\n");
- return 0;
-}
-
diff --git a/gcc/testsuite/gdc.test/runnable/closure.d b/gcc/testsuite/gdc.test/runnable/closure.d
index 1ed10e2c031..af304c1f38a 100644
--- a/gcc/testsuite/gdc.test/runnable/closure.d
+++ b/gcc/testsuite/gdc.test/runnable/closure.d
@@ -689,7 +689,7 @@ void test22()
}
/************************************/
-// 1759
+// https://issues.dlang.org/show_bug.cgi?id=1759
void test1759()
{
@@ -724,7 +724,7 @@ void test1759()
}
/************************************/
-// 1841
+// https://issues.dlang.org/show_bug.cgi?id=1841
int delegate() foo1841()
{
@@ -768,7 +768,7 @@ void test1841()
}
/************************************/
-// 5911
+// https://issues.dlang.org/show_bug.cgi?id=5911
void writeln5911(const(char)[] str) {}
@@ -791,7 +791,7 @@ void test5911()
}
/************************************/
-// 9685
+// https://issues.dlang.org/show_bug.cgi?id=9685
auto get9685a(alias fun)()
{
@@ -844,7 +844,7 @@ void test9685b()
}
/************************************/
-// 12406
+// https://issues.dlang.org/show_bug.cgi?id=12406
auto createDg12406()
{
@@ -898,7 +898,7 @@ void test12406()
}
/************************************/
-// 14730
+// https://issues.dlang.org/show_bug.cgi?id=14730
void test14730()
{
diff --git a/gcc/testsuite/gdc.test/runnable/complex.d b/gcc/testsuite/gdc.test/runnable/complex.d
index 78fe574b310..50e793e21e6 100644
--- a/gcc/testsuite/gdc.test/runnable/complex.d
+++ b/gcc/testsuite/gdc.test/runnable/complex.d
@@ -1,27 +1,43 @@
-// RUNNABLE_PHOBOS_TEST
-// PERMUTE_ARGS:
-
-import std.stdio;
-import std.math;
+/*
+REQUIRED_ARGS: -d
+TEST_OUTPUT:
+---
+---
+*/
+
+import core.stdc.math : isnan, signbit;
import core.stdc.stdio;
-/***************************************/
+template AliasSeq(T...) { alias T AliasSeq; }
-void test1()
-{
- creal c = 3.0 + 4.0i;
- c = sqrt(c);
- printf("re = %Lg, im = %Lg\n", c.re, c.im);
- assert(c.re == 2.0);
- assert(c.im == 1.0);
+/************************************/
- float f = sqrt(25.0f);
- assert(f == 5.0);
- double d = sqrt(4.0);
- assert(d == 2.0);
- real r = sqrt(9.0L);
- assert(r == 3.0);
-}
+static assert(-(6i) == -6i);
+static assert(-(1 + 6i) == -1 - 6i);
+
+static assert(!3.7i == 0);
+static assert(!0.0i == 1);
+static assert(!(2+3.7i) == 0);
+static assert(!(0+3.7i) == 0);
+static assert(!(2+0.0i) == 0);
+static assert(!(0+0.0i) == 1);
+
+static assert(-6i + 2i == -4i);
+static assert(6i - 1i == 5i);
+
+static assert((3.6 + 7.2i) / (1 + 0i) == 3.6 + 7.2i);
+static assert((3.6 + 7.2i) / (0.0 + 1i) == 7.2 - 3.6i);
+
+static assert((7.2i < 6.2i) == 0);
+
+static assert((7.2i == 6.2i) == 0);
+static assert((7.2i != 6.2i) == 1);
+
+static assert((7.2i == 7.2i) == 1);
+static assert((7.2i != 7.2i) == 0);
+
+static assert((5.1 is 5.1i) == 0);
+static assert((5.1 !is 5.1i) == 1);
/***************************************/
@@ -227,11 +243,11 @@ void test12()
{
real x = 3;
creal a = (2 + 4i) % 3;
- writeln(a);
+ printf("%Lg %Lgi\n", a.re, a.im);
assert(a == 2 + 1i);
creal b = (2 + 4i) % x;
- writeln(b);
+ printf("%Lg %Lgi\n", b.re, b.im);
assert(b == a);
}
@@ -241,7 +257,7 @@ void test13()
{
ireal a = 5i;
ireal b = a % 2;
- writeln(b);
+ printf("%Lg %Lgi\n", b.re, b.im);
assert(b == 1i);
}
@@ -405,6 +421,974 @@ void test15()
assert(bar15r(1.0L, 2.0Li) == 1.0L + 2.0Li);
}
+/************************************/
+
+void test16()
+{
+ real n = -0.0;
+ const real m = -0.0;
+
+ creal c = -0.0 + 3i;
+ creal d = n + 3i;
+ creal e = m + 3i;
+
+ assert(signbit(c.re) != 0);
+ assert(signbit(d.re) != 0);
+ assert(signbit(e.re) != 0);
+}
+
+/************************************/
+
+void test17()
+{
+ void test(cdouble v)
+ {
+ auto x2 = cdouble(v);
+ assert(x2 == v);
+ }
+ test(1.2+3.4i);
+}
+
+/************************************/
+
+template factorial18(float n, cdouble c, string sss, string ttt)
+{
+ static if (n == 1)
+ const float factorial18 = 1;
+ else
+ const float factorial18 = n * 2;
+}
+
+template bar18(wstring abc, dstring def)
+{
+ const int x = 3;
+}
+
+void test18()
+{
+ float f = factorial18!(4.25, 6.8+3i, "hello", null);
+ printf("%g\n", f);
+ assert(f == 8.5);
+ int i = bar18!("abc"w, "def"d).x;
+ printf("%d\n", i);
+ assert(i == 3);
+}
+
+/*****************************************/
+
+void test19()
+{
+ float f;
+ double d;
+ real r;
+
+ if (f > ifloat.max)
+ goto Loverflow;
+ if (d > ifloat.max)
+ goto Loverflow;
+ if (r > ifloat.max)
+ goto Loverflow;
+
+ if (ifloat.max < f)
+ goto Loverflow;
+ if (ifloat.max < d)
+ goto Loverflow;
+ if (ifloat.max < r)
+ goto Loverflow;
+
+ return;
+
+ Loverflow:
+ return;
+}
+
+/*****************************************/
+
+void test20()
+{
+ double d = 1;
+ cdouble cd = 1+0i;
+ assert(cd == 1.0 + 0i);
+}
+
+/*****************************************/
+
+void test21()
+{
+ cdouble[] a;
+ cdouble[] b;
+ foreach(ref cdouble d; b)
+ {
+ d = -a[0];
+ for(;;){}
+ }
+}
+
+/*************************************/
+
+void test22()
+{
+ static creal[] params = [1+0i, 3+0i, 5+0i];
+
+ printf("params[0] = %Lf + %Lfi\n", params[0].re, params[0].im);
+ printf("params[1] = %Lf + %Lfi\n", params[1].re, params[1].im);
+ printf("params[2] = %Lf + %Lfi\n", params[2].re, params[2].im);
+
+ creal[] sums = new creal[3];
+ sums[] = 0+0i;
+
+ foreach(creal d; params)
+ {
+ creal prod = d;
+
+ printf("prod = %Lf + %Lfi\n", prod.re, prod.im);
+ for(int i; i<2; i++)
+ {
+ sums[i] += prod;
+ prod *= d;
+ }
+ sums[2] += prod;
+ }
+
+ printf("sums[0] = %Lf + %Lfi", sums[0].re, sums[0].im);
+ assert(sums[0].re==9);
+ assert(sums[0].im==0);
+ assert(sums[1].re==35);
+ assert(sums[1].im==0);
+ assert(sums[2].re==153);
+ assert(sums[2].im==0);
+}
+
+/*******************************************/
+
+cdouble y23;
+
+cdouble f23(cdouble x)
+{
+ return (y23 = x);
+}
+
+void test23()
+{
+ f23(1.0+2.0i);
+ assert(y23 == 1.0+2.0i);
+}
+
+/*************************************/
+
+ifloat func_24_1(ifloat f, double d)
+{
+// f /= cast(cdouble)d;
+ return f;
+}
+
+ifloat func_24_2(ifloat f, double d)
+{
+ f = cast(ifloat)(f / cast(cdouble)d);
+ return f;
+}
+
+float func_24_3(float f, double d)
+{
+// f /= cast(cdouble)d;
+ return f;
+}
+
+float func_24_4(float f, double d)
+{
+ f = cast(float)(f / cast(cdouble)d);
+ return f;
+}
+
+void test24()
+{
+ ifloat f = func_24_1(10i, 8);
+ printf("%fi\n", f);
+// assert(f == 1.25i);
+
+ f = func_24_2(10i, 8);
+ printf("%fi\n", f);
+ assert(f == 1.25i);
+
+ float g = func_24_3(10, 8);
+ printf("%f\n", g);
+// assert(g == 1.25);
+
+ g = func_24_4(10, 8);
+ printf("%f\n", g);
+ assert(g == 1.25);
+}
+
+/*******************************************/
+
+void test25()
+{
+ ireal x = 4.0Li;
+ ireal y = 4.0Li;
+ ireal z = 4Li;
+ creal c = 4L + 0Li;
+}
+
+/*************************************/
+
+string toString26(cdouble z)
+{
+ char[ulong.sizeof*8] buf;
+
+ auto len = snprintf(buf.ptr, buf.sizeof, "%f+%fi", z.re, z.im);
+ return buf[0 .. len].idup;
+}
+
+void test26()
+{
+ static cdouble[] A = [1+0i, 0+1i, 1+1i];
+ string s;
+
+ foreach( cdouble z; A )
+ {
+ s = toString26(z);
+ printf("%.*s ", cast(int)s.length, s.ptr);
+ }
+ printf("\n");
+
+ for(int ii=0; ii<A.length; ii++ )
+ A[ii] += -1i*A[ii];
+
+ assert(A[0] == 1 - 1i);
+ assert(A[1] == 1 + 1i);
+ assert(A[2] == 2);
+
+ foreach( cdouble z; A )
+ {
+ s = toString26(z);
+ printf("%.*s ", cast(int)s.length, s.ptr);
+ }
+ printf("\n");
+}
+
+/*************************************/
+
+void test27()
+{
+ creal z = 1. + 2.0i;
+
+ real r = z.re;
+ assert(r == 1.0);
+
+ real i = z.im;
+ assert(i == 2.0);
+
+ assert(r.im == 0.0);
+ assert(r.re == 1.0);
+
+ assert(i.re == 2.0);
+ assert(i.im == 0.0i);
+}
+
+/*************************************/
+
+void test28()
+{
+ alias cdouble X;
+ X four = cast(X) (4.0i + 0.4);
+}
+
+/*************************************/
+
+void test29()
+{
+ ireal a = 6.5i % 3i;
+ printf("%Lfi %Lfi\n", a, a - .5i);
+ assert(a == .5i);
+
+ a = 6.5i % 3;
+ printf("%Lfi %Lfi\n", a, a - .5i);
+ assert(a == .5i);
+
+ real b = 6.5 % 3i;
+ printf("%Lf %Lf\n", b, b - .5);
+ assert(b == .5);
+
+ b = 6.5 % 3;
+ printf("%Lf %Lf\n", b, b - .5);
+ assert(b == .5);
+}
+
+/*************************************/
+
+void test30()
+{
+ cfloat f = 1+0i;
+ f %= 2fi;
+ printf("%f + %fi\n", f.re, f.im);
+ assert(f == 1 + 0i);
+
+ cdouble d = 1+0i;
+ d %= 2i;
+ printf("%f + %fi\n", d.re, d.im);
+ assert(d == 1 + 0i);
+
+ creal r = 1+0i;
+ r %= 2i;
+ printf("%Lf + %Lfi\n", r.re, r.im);
+ assert(r == 1 + 0i);
+}
+
+/*************************************/
+
+void test31()
+{
+ cfloat f = 1+0i;
+ f %= 2i;
+ printf("%f + %fi\n", f.re, f.im);
+ assert(f == 1);
+
+ cdouble d = 1+0i;
+ d = d % 2i;
+ printf("%f + %fi\n", d.re, d.im);
+ assert(d == 1);
+
+ creal r = 1+0i;
+ r = r % 2i;
+ printf("%Lf + %Lfi\n", r.re, r.im);
+ assert(r == 1);
+}
+
+/*************************************/
+
+void assertEqual(real* a, real* b, string file = __FILE__, size_t line = __LINE__)
+{
+ auto x = cast(ubyte*)a;
+ auto y = cast(ubyte*)b;
+
+ // Only compare the 10 value bytes, the padding bytes are of undefined
+ // value.
+ version (X86) enum count = 10;
+ else version (X86_64) enum count = 10;
+ else enum count = real.sizeof;
+ for (size_t i = 0; i < count; i++)
+ {
+ if (x[i] != y[i])
+ {
+ printf("%02zd: %02x %02x\n", i, x[i], y[i]);
+ import core.exception;
+ throw new AssertError(file, line);
+ }
+ }
+}
+
+void assertEqual(creal* a, creal* b, string file = __FILE__, size_t line = __LINE__)
+{
+ assertEqual(cast(real*)a, cast(real*)b, file, line);
+ assertEqual(cast(real*)a + 1, cast(real*)b + 1, file, line);
+}
+
+void test32()
+{
+ creal a = creal.nan;
+ creal b = real.nan + ireal.nan;
+ assertEqual(&a, &b);
+
+ real c= real.nan;
+ real d=a.re;
+ assertEqual(&c, &d);
+
+ d=a.im;
+ assertEqual(&c, &d);
+}
+
+/*************************************/
+
+void test33()
+{
+ creal a = creal.infinity;
+ creal b = real.infinity + ireal.infinity;
+ assertEqual(&a, &b);
+
+ real c = real.infinity;
+ real d=a.re;
+ assertEqual(&c, &d);
+
+ d=a.im;
+ assertEqual(&c, &d);
+}
+
+/*************************************/
+
+void test34()
+{
+ creal a = creal.nan;
+ creal b = creal.nan;
+ b = real.nan + ireal.nan;
+ assertEqual(&a, &b);
+
+ real c = real.nan;
+ real d=a.re;
+ assertEqual(&c, &d);
+
+ d=a.im;
+ assertEqual(&c, &d);
+}
+
+/*************************************/
+
+ireal x35;
+
+void foo35()
+{
+ x35 = -x35;
+}
+
+void bar35()
+{
+ return foo35();
+}
+
+void test35()
+{
+ x35=2i;
+ bar35();
+ assert(x35==-2i);
+}
+
+/*************************************/
+
+void test36()
+{
+ ireal imag = 2.5i;
+ printf ("test of imag*imag = %Lf\n",imag*imag);
+ assert(imag * imag == -6.25);
+}
+
+/*************************************/
+
+void test37()
+{
+ creal z1 = 1. - 2.0i;
+ ireal imag_part = z1.im/1i;
+}
+
+/***********************************/
+
+void test38()
+{
+ ireal imag = 2.5i;
+ //printf ("test of imag*imag = %Lf\n",imag*imag);
+ real f = imag * imag;
+ assert(f == -6.25);
+}
+
+/***********************************/
+
+void test39()
+{
+ creal z = 1 + 2.5i;
+ real e = z.im;
+
+ printf ("e = %Lf\n", e);
+ assert(e == 2.5);
+}
+
+/***********************************/
+
+void test40()
+{
+ ifloat b = cast(ifloat)1i;
+ assert(b == 1.0i);
+
+ ifloat c = 2fi;
+ assert(c == 2.0i);
+
+ c = 0fi;
+ assert(c == 0i);
+}
+
+/***************************************************/
+
+void test41()
+{
+ creal a=1.3L+9.7Li;
+ assert(a.re == 1.3L);
+ assert(a.im == 9.7L);
+}
+
+/***************************************************/
+
+void test42()
+{
+ creal c = 2.7L + 0i;
+ assert(c.re==2.7L);
+ assert(c.im==0.0L);
+}
+
+/***********************************/
+
+void test43()
+{
+ creal C,Cj;
+ real y1,x1;
+
+ C = x1 + y1*1i + Cj;
+ C = 1i*y1 + x1 + Cj;
+ C = Cj + 1i*y1 + x1;
+ C = y1*1i + Cj + x1;
+ C = 1i*y1 + Cj;
+ C = Cj + 1i*y1;
+}
+
+/***************************************************/
+
+void test44()
+{
+ ifloat f = 1.0fi;
+// f *= 2.0fi; // illegal but compiles
+ printf("%g\n", f);
+// assert(f == 0i);
+}
+
+/******************************************************/
+
+void test45()
+{
+ TypeInfo ti;
+
+ ti = typeid(ifloat[]);
+ assert(!(ti is null));
+ ti = typeid(idouble[]);
+ assert(!(ti is null));
+ ti = typeid(ireal[]);
+ assert(!(ti is null));
+
+ ti = typeid(cfloat[]);
+ assert(!(ti is null));
+ ti = typeid(cdouble[]);
+ assert(!(ti is null));
+ ti = typeid(creal[]);
+ assert(!(ti is null));
+}
+
+/******************************************************/
+
+void test46()
+{
+ TypeInfo ti = typeid(ifloat*);
+ assert(!(ti is null));
+ assert(ti.tsize==(ifloat*).sizeof);
+ assert(ti.toString()=="ifloat*");
+}
+
+/******************************************************/
+
+void test47()
+{
+ TypeInfo ti = typeid(cfloat*);
+ assert(!(ti is null));
+ assert(ti.tsize==(cfloat*).sizeof);
+ assert(ti.toString()=="cfloat*");
+}
+
+/******************************************************/
+
+void test48()
+{
+ TypeInfo ti = typeid(idouble*);
+ assert(!(ti is null));
+ assert(ti.tsize==(idouble*).sizeof);
+ assert(ti.toString()=="idouble*");
+}
+
+/******************************************************/
+
+void test49()
+{
+ TypeInfo ti = typeid(cdouble*);
+ assert(!(ti is null));
+ assert(ti.tsize==(cdouble*).sizeof);
+ assert(ti.toString()=="cdouble*");
+}
+
+/***********************************/
+
+void foo51(creal a)
+{
+ assert(a == -8i);
+}
+
+void test51()
+{
+ assert((2-2i)*(2-2i) == -8i);
+
+ cdouble a = (2-2i)*(2-2i);
+ assert(a == -8i);
+
+ foo51((2-2i)*(2-2i));
+}
+
+/******************************************************/
+
+void test52()
+{
+ TypeInfo ti = typeid(ireal*);
+ assert(!(ti is null));
+ assert(ti.tsize==(ireal*).sizeof);
+ assert(ti.toString()=="ireal*");
+}
+
+/******************************************************/
+
+void test53()
+{
+ TypeInfo ti = typeid(creal*);
+ assert(!(ti is null));
+ assert(ti.tsize==(creal*).sizeof);
+ assert(ti.toString()=="creal*");
+}
+
+/*******************************************/
+
+auto init(T)(T val) { return 1; }
+
+void test54()
+{
+ // See built-in 'init' property
+ assert(10i .init is idouble.nan);
+
+ // x.init() has parens, so it runs UFCS call
+ assert(10i .init() == 1);
+
+ // x.init!YYY matches templatized UFCS call.
+ assert(10i .init!idouble() == 1);
+}
+
+/*******************************************/
+
+creal x55;
+
+void foo55()
+{
+ x55 = -x55;
+}
+
+void bar55()
+{
+ return foo55();
+}
+
+void test55()
+{
+ x55 = 2.0L + 0.0Li;
+ bar55();
+ assert(x55 == -2.0L + 0.0Li);
+}
+
+/***************************************************/
+
+template Q(s...) { alias s q; }
+
+void test56()
+{
+ enum complex80 = Q!( 1+1.0i ).q.stringof;
+}
+
+/********************************************************/
+
+void test57()
+{
+ assert(__traits(isArithmetic, ifloat) == true);
+ assert(__traits(isArithmetic, idouble) == true);
+ assert(__traits(isArithmetic, ireal) == true);
+ assert(__traits(isArithmetic, cfloat) == true);
+ assert(__traits(isArithmetic, cdouble) == true);
+ assert(__traits(isArithmetic, creal) == true);
+
+ assert(__traits(isScalar, ifloat) == true);
+ assert(__traits(isScalar, idouble) == true);
+ assert(__traits(isScalar, ireal) == true);
+ assert(__traits(isScalar, cfloat) == true);
+ assert(__traits(isScalar, cdouble) == true);
+ assert(__traits(isScalar, creal) == true);
+
+ assert(__traits(isFloating, ifloat) == true);
+ assert(__traits(isFloating, idouble) == true);
+ assert(__traits(isFloating, ireal) == true);
+ assert(__traits(isFloating, cfloat) == true);
+ assert(__traits(isFloating, cdouble) == true);
+ assert(__traits(isFloating, creal) == true);
+
+ assert(__traits(isIntegral, ifloat) == false);
+ assert(__traits(isIntegral, idouble) == false);
+ assert(__traits(isIntegral, ireal) == false);
+ assert(__traits(isIntegral, cfloat) == false);
+ assert(__traits(isIntegral, cdouble) == false);
+ assert(__traits(isIntegral, creal) == false);
+
+ assert(__traits(isUnsigned, ifloat) == false);
+ assert(__traits(isUnsigned, idouble) == false);
+ assert(__traits(isUnsigned, ireal) == false);
+ assert(__traits(isUnsigned, cfloat) == false);
+ assert(__traits(isUnsigned, cdouble) == false);
+ assert(__traits(isUnsigned, creal) == false);
+
+ assert(__traits(isAssociativeArray, ifloat) == false);
+ assert(__traits(isAssociativeArray, idouble) == false);
+ assert(__traits(isAssociativeArray, ireal) == false);
+ assert(__traits(isAssociativeArray, cfloat) == false);
+ assert(__traits(isAssociativeArray, cdouble) == false);
+ assert(__traits(isAssociativeArray, creal) == false);
+
+ assert(__traits(isStaticArray, ifloat) == false);
+ assert(__traits(isStaticArray, idouble) == false);
+ assert(__traits(isStaticArray, ireal) == false);
+ assert(__traits(isStaticArray, cfloat) == false);
+ assert(__traits(isStaticArray, cdouble) == false);
+ assert(__traits(isStaticArray, creal) == false);
+
+ assert(__traits(isAbstractClass, ifloat) == false);
+ assert(__traits(isAbstractClass, idouble) == false);
+ assert(__traits(isAbstractClass, ireal) == false);
+ assert(__traits(isAbstractClass, cfloat) == false);
+ assert(__traits(isAbstractClass, cdouble) == false);
+ assert(__traits(isAbstractClass, creal) == false);
+}
+
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=3382
+
+real toreal(ireal x){ return x.im; }
+
+void test3382()
+{
+ assert(1.4i.toreal() == 1.4);
+}
+
+/***************************************************/
+
+alias ireal BUG3919;
+alias typeof(BUG3919.init*BUG3919.init) ICE3919;
+alias typeof(BUG3919.init/BUG3919.init) ICE3920;
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=8454
+
+double sqrt8454(double d) { return d/2; }
+void foo8454(cdouble m) {}
+
+void test8454()
+{
+ foo8454(0 - sqrt8454(1.0) * 1i);
+}
+
+/************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9046
+
+void test9046()
+{
+ foreach (T; AliasSeq!(ifloat, idouble, ireal, cfloat, cdouble, creal))
+ foreach (U; AliasSeq!(T, const T, immutable T, shared T, shared const T, inout T, shared inout T))
+ {
+ static assert(is(typeof(U.init) == U));
+ }
+}
+
+/********************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9112
+
+void test9112a() // T() and T(v)
+{
+ void test(T)(T v)
+ {
+ foreach (string qual; AliasSeq!("", "const ", "immutable "))
+ {
+ mixin("alias U = "~qual~T.stringof~";");
+ //pragma(msg, U);
+
+ mixin("auto x1 = "~qual~T.stringof~"();"); // U() default construction syntax
+ mixin("auto x2 = "~qual~T.stringof~"(v);"); // U(v)
+ static assert(!__traits(compiles, mixin(qual~T.stringof~"(v, v)"))); // U(v, v)
+ static assert(is(typeof(x1) == U));
+ static assert(is(typeof(x2) == U));
+ static if ( is(typeof(U.nan) : real)) assert( isnan(x1.re) && !isnan(x1.im), U.stringof);
+ static if ( is(typeof(U.nan) : ireal)) assert(!isnan(x1.re) && isnan(x1.im), U.stringof);
+ static if ( is(typeof(U.nan) : creal)) assert( isnan(x1.re) && isnan(x1.im), U.stringof);
+ static if (!is(typeof(U.nan))) assert( x1 == U.init, U.stringof);
+ assert(x2 == v, U.stringof);
+ }
+ }
+ test!(ifloat )(1.4142i);
+ test!(idouble)(1.4142i);
+ test!(ireal )(1.4142i);
+ test!(cfloat )(1.2+3.4i);
+ test!(cdouble)(1.2+3.4i);
+ test!(creal )(1.2+3.4i);
+
+ static assert(!__traits(compiles, double(3.14i)));
+}
+
+void test9112b() // new T(v)
+{
+ void test(T)(T v)
+ {
+ foreach (string qual; AliasSeq!("", "const ", "immutable "))
+ {
+ mixin("alias U = "~qual~T.stringof~";");
+ //pragma(msg, U);
+
+ mixin("auto p1 = new "~qual~T.stringof~"();"); // U() default construction syntax
+ mixin("auto p2 = new "~qual~T.stringof~"(v);"); // U(v)
+ static assert(!__traits(compiles, mixin("new "~qual~T.stringof~"(v, v)"))); // U(v, v)
+ static assert(is(typeof(p1) == U*));
+ static assert(is(typeof(p2) == U*));
+ assert( p1 !is null);
+ assert( p2 !is null);
+ auto x1 = *p1;
+ auto x2 = *p2;
+ static if ( is(typeof(U.nan) : real)) assert( isnan(x1.re) && !isnan(x1.im), U.stringof);
+ static if ( is(typeof(U.nan) : ireal)) assert(!isnan(x1.re) && isnan(x1.im), U.stringof);
+ static if ( is(typeof(U.nan) : creal)) assert( isnan(x1.re) && isnan(x1.im), U.stringof);
+ static if (!is(typeof(U.nan))) assert( x1 == U.init, U.stringof);
+ assert(x2 == v, U.stringof);
+ }
+ }
+
+ test!(ifloat )(1.4142i);
+ test!(idouble)(1.4142i);
+ test!(ireal )(1.4142i);
+ test!(cfloat )(1.2+3.4i);
+ test!(cdouble)(1.2+3.4i);
+ test!(creal )(1.2+3.4i);
+
+ static assert(!__traits(compiles, new double(3.14i)));
+}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10639
+
+struct S1
+{
+ cdouble val;
+}
+
+void formatTest(S1 s, double re, double im)
+{
+ assert(s.val.re == re);
+ assert(s.val.im == im);
+}
+
+void test10639()
+{
+ S1 s = S1(3+2.25i);
+ formatTest(s, 3, 2.25);
+}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=10842
+
+template Test10842(F, T)
+{
+ bool res;
+ F from()
+ {
+ res = true;
+ return F.init;
+ }
+ T to()
+ {
+ // The cast operand had incorrectly been eliminated
+ return cast(T)from();
+ }
+ bool test()
+ {
+ res = false;
+ to();
+ return res;
+ }
+}
+
+void test10842()
+{
+ foreach (From; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real))
+ {
+ foreach (To; AliasSeq!(ifloat, idouble, ireal))
+ {
+ if (!Test10842!(From, To).test())
+ assert(0);
+ }
+ }
+
+ foreach (From; AliasSeq!(ifloat, idouble, ireal))
+ {
+ foreach (To; AliasSeq!(/*bool*, */byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real))
+ {
+ if (!Test10842!(From, To).test())
+ assert(0);
+ }
+ }
+}
+
+/***************************************************/
+
+void test10927()
+{
+ static assert( (1+2i) ^^ 3 == -11 - 2i );
+ auto a = (1+2i) ^^ 3;
+}
+
+/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13252
+
+alias TypeTuple13252(T...) = T;
+
+static assert(is(typeof(TypeTuple13252!(cast(cfloat )(1 + 2i))[0]) == cfloat ));
+static assert(is(typeof(TypeTuple13252!(cast(cdouble)(1 + 2i))[0]) == cdouble));
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=14218
+
+void test14218()
+{
+ version (DigitalMars)
+ {
+ // Questionable but currently accepted by DMD (but not GDC).
+ foreach (To; AliasSeq!(ifloat, idouble, ireal))
+ {
+ auto x = cast(To)null;
+ assert(x == 0); // 0i
+ }
+
+ // Internal error: backend/el.c in el_long()
+ //foreach (To; AliasSeq!(cfloat, cdouble, creal))
+ //{
+ // static assert(!__traits(compiles, { auto x = cast(To)null; }));
+ //}
+ }
+}
+
+/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15653
+
+alias TypeTuple15653(T...) = T;
+
+void test15653()
+{
+ void foo(U, T)(const T x) { static assert(is(T == U)); }
+ void bar(U, T)(immutable T x) { static assert(is(T == U)); }
+
+ struct X { int a; long[2] b; }
+ struct Y { int* a; long[] b; }
+
+ foreach (U; TypeTuple15653!(
+ ifloat, idouble, ireal,
+ cfloat, cdouble, creal))
+ {
+ foo!U(U.init); // OK
+ bar!U(U.init); // Was error, now OK
+
+ U u;
+ foo!U(u); // OK
+ bar!U(u); // Was error, now OK
+ }
+}
+
/***************************************/
// https://issues.dlang.org/show_bug.cgi?id=17087
@@ -427,11 +1411,108 @@ void test17677()
}
/***************************************/
+// https://issues.dlang.org/show_bug.cgi?id=17677
+
+float getreal_rcx(cfloat z)
+{
+ return z.re;
+}
+float getimag_rcx(cfloat z)
+{
+ return z.im;
+}
+
+float getreal_rdx(cfloat z, int)
+{
+ return z.re;
+}
+float getimag_rdx(cfloat z, int)
+{
+ return z.im;
+}
+
+float getreal_r8(cfloat z, int, int)
+{
+ return z.re;
+}
+float getimag_r8(cfloat z, int, int)
+{
+ return z.im;
+}
+
+float getreal_r9(cfloat z, int, int, int)
+{
+ return z.re;
+}
+float getimag_r9(cfloat z, int, int, int)
+{
+ return z.im;
+}
+
+float getreal_stack(cfloat z, int, int, int, int)
+{
+ return z.re;
+}
+float getimag_stack(cfloat z, int, int, int, int)
+{
+ return z.im;
+}
+
+void test18772a()
+{
+ cfloat[1] A;
+ float[1] B;
+ int i = 0;
+ A[0] = 2.0f + 4i;
+ B[0] = 3.0f;
+ assert(6.0 == getreal_rcx(A[i] * B[i]));
+ assert(12.0 == getimag_rcx(A[i] * B[i]));
+
+ assert(6.0 == getreal_rdx(A[i] * B[i], 1));
+ assert(12.0 == getimag_rdx(A[i] * B[i], 1));
+
+ assert(6.0 == getreal_r8(A[i] * B[i], 1, 2));
+ assert(12.0 == getimag_r8(A[i] * B[i], 1, 2));
+
+ assert(6.0 == getreal_r9(A[i] * B[i], 1, 2, 3));
+ assert(12.0 == getimag_r9(A[i] * B[i], 1, 2, 3));
+
+ assert(6.0 == getreal_stack(A[i] * B[i], 1, 2, 3, 4));
+ assert(12.0 == getimag_stack(A[i] * B[i], 1, 2, 3, 4));
+}
+
+void test18772b(T)()
+{
+ static auto getre0(T z)
+ {
+ return z.re;
+ }
+ static auto getim0(T z)
+ {
+ return z.im;
+ }
+
+ T z = 3 + 4i;
+ auto d = z.re;
+
+ assert(getre0(d * z) == d * 3);
+ assert(getim0(d * z) == d * 4);
+}
+
+void test18772()
+{
+ test18772a();
+
+ test18772b!cfloat();
+ test18772b!cdouble();
+ test18772b!creal();
+}
+
+/***************************************/
int main(char[][] args)
{
- test1();
test2();
test3();
test4();
@@ -454,10 +1535,59 @@ int main(char[][] args)
test7806();
test7976();
test15();
+ test16();
+ test17();
+ test18();
+ test19();
+ test20();
+ test21();
+ test22();
+ test23();
+ test24();
+ test25();
+ test26();
+ test27();
+ test28();
+ test29();
+ test30();
+ test31();
+ test32();
+ test33();
+ test34();
+ test35();
+ test36();
+ test37();
+ test38();
+ test39();
+ test40();
+ test41();
+ test42();
+ test43();
+ test44();
+ test45();
+ test46();
+ test47();
+ test48();
+ test49();
+ test51();
+ test52();
+ test53();
+ test54();
+ test55();
+ test56();
+ test57();
+ test8454();
+ test9046();
+ test9112a();
+ test9112b();
+ test10639();
+ test10842();
+ test14218();
+ test15653();
test17087();
test17677();
+ test18772();
printf("Success!\n");
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/constfold.d b/gcc/testsuite/gdc.test/runnable/constfold.d
index 1d259f610cf..7857eaf544a 100644
--- a/gcc/testsuite/gdc.test/runnable/constfold.d
+++ b/gcc/testsuite/gdc.test/runnable/constfold.d
@@ -2,32 +2,17 @@
// RUNNABLE_PHOBOS_TEST
static assert(__LINE__ == 3); // fails as __LINE__ is 2
-import std.stdio;
-import std.math : signbit, sqrt;
+import core.stdc.math : signbit;
/************************************/
static assert(-(1) == -1);
-static assert(-(6i) == -6i);
-static assert(-(1 + 6i) == -1 - 6i);
static assert(!27 == 0);
static assert(!0 == 1);
static assert(!6.2 == 0);
static assert(!0.0 == 1);
-static assert(!3.7i == 0);
-static assert(!0.0i == 1);
-static assert(!(2+3.7i) == 0);
-static assert(!(0+3.7i) == 0);
-static assert(!(2+0.0i) == 0);
-static assert(!(0+0.0i) == 1);
-
-static assert(-6i + 2i == -4i);
-static assert(6i - 1i == 5i);
-
-static assert((3.6 + 7.2i) / (1 + 0i) == 3.6 + 7.2i);
-static assert((3.6 + 7.2i) / (0.0 + 1i) == 7.2 - 3.6i);
static assert((6 % 4) == 2);
static assert((6u % 4u) == 2u);
@@ -157,16 +142,9 @@ static assert((7.2 <= 6.2) == 0);
static assert((7.2 > 6.2) == 1);
static assert((7.2 >= 6.2) == 1);
-static assert((7.2i < 6.2i) == 0);
-
-
-static assert((7.2i == 6.2i) == 0);
-static assert((7.2i != 6.2i) == 1);
static assert((7.2 == 6.2) == 0);
static assert((7.2 != 6.2) == 1);
-static assert((7.2i == 7.2i) == 1);
-static assert((7.2i != 7.2i) == 0);
static assert((7.2 == 7.2) == 1);
static assert((7.2 != 7.2) == 0);
@@ -187,9 +165,6 @@ static assert((5.1 is 4.1) == 0);
static assert((5.1 !is 5.1) == 0);
static assert((5.1 !is 4.1) == 1);
-static assert((5.1 is 5.1i) == 0);
-static assert((5.1 !is 5.1i) == 1);
-
static assert((5 ? 2 : 3) == 2);
static assert((0 ? 2 : 3) == 3);
static assert((5.0 ? 2 : 3) == 2);
@@ -208,25 +183,6 @@ static assert(['a','b','c','d'] == "abcd");
static assert("efgh" == ['e','f','g','h']);
static assert("efgi" != ['e','f','g','h']);
-static assert((2 ^^ 8) == 256);
-static assert((3 ^^ 8.0) == 6561);
-static assert((4.0 ^^ 8) == 65536);
-static assert((5.0 ^^ 8.0) == 390625);
-
-static assert((0.5 ^^ 3) == 0.125);
-static assert((1.5 ^^ 3.0) == 3.375);
-static assert((2.5 ^^ 3) == 15.625);
-static assert((3.5 ^^ 3.0) == 42.875);
-
-static assert(((-2) ^^ -5.0) == -0.031250);
-static assert(((-2.0) ^^ -6) == 0.015625);
-static assert(((-2.0) ^^ -7.0) == -0.0078125);
-
-static assert((144 ^^ 0.5) == 12);
-static assert((1089 ^^ 0.5) == 33);
-static assert((1764 ^^ 0.5) == 42);
-static assert((650.25 ^^ 0.5) == 25.5);
-
void test1()
{
@@ -256,8 +212,6 @@ void test2()
{
float f = float.infinity;
int i = cast(int) f;
- writeln(i);
- writeln(cast(int)float.max);
assert(i == cast(int)float.max);
assert(i == 0x80000000);
}
@@ -270,19 +224,8 @@ void test3()
real n = -0.0;
const real m = -0.0;
- creal c = -0.0 + 3i;
- creal d = n + 3i;
- creal e = m + 3i;
-
- // should print "11111"
- writeln(signbit(n), signbit(m),
- signbit(c.re), signbit(d.re), signbit(e.re));
-
- assert(signbit(n) == 1);
- assert(signbit(m) == 1);
- assert(signbit(c.re) == 1);
- assert(signbit(d.re) == 1);
- assert(signbit(e.re) == 1);
+ assert(signbit(n) != 0);
+ assert(signbit(m) != 0);
}
/************************************/
@@ -354,7 +297,7 @@ int foo9() {
static assert(foo9()==2);
/************************************/
-// Bugzilla 6077
+// https://issues.dlang.org/show_bug.cgi?id=6077
void test6077() {
static string scat(string s1, string s2)
@@ -424,7 +367,7 @@ int test4()
static assert(test4() == 24666);
/************************************/
-// 8400
+// https://issues.dlang.org/show_bug.cgi?id=8400
void test8400()
{
@@ -434,7 +377,7 @@ void test8400()
}
/************************************/
-// 8939
+// https://issues.dlang.org/show_bug.cgi?id=8939
void foo8939(T)(ref T) { } // same for `auto ref`
void bar8939(ref const int) { }
@@ -486,7 +429,7 @@ class C8939regression
}
/************************************/
-// 9058
+// https://issues.dlang.org/show_bug.cgi?id=9058
template TypeTuple9058(TL...) { alias TypeTuple9058 = TL; }
template EnumMembers9058(T)
@@ -504,22 +447,7 @@ void test9058()
}
/************************************/
-// 11159
-
-void test11159()
-{
- import std.math : pow;
- enum ulong
- e_2_pow_64 = 2uL^^64,
- e_10_pow_19 = 10uL^^19,
- e_10_pow_20 = 10uL^^20;
- assert(e_2_pow_64 == pow(2uL, 64));
- assert(e_10_pow_19 == pow(10uL, 19));
- assert(e_10_pow_20 == pow(10uL, 20));
-}
-
-/************************************/
-// 12306
+// https://issues.dlang.org/show_bug.cgi?id=12306
void test12306()
{
@@ -540,7 +468,7 @@ void test12306()
}
/************************************/
-// 13977
+// https://issues.dlang.org/show_bug.cgi?id=13977
void test13977()
{
@@ -570,7 +498,7 @@ void test13977()
}
/************************************/
-// 13978
+// https://issues.dlang.org/show_bug.cgi?id=13978
void test13978()
{
@@ -616,7 +544,7 @@ void test3697or()
}
/************************************/
-// 14459
+// https://issues.dlang.org/show_bug.cgi?id=14459
void test14459()
{
@@ -665,11 +593,9 @@ int main()
test8400();
test8939();
test9058();
- test11159();
test13977();
test13978();
test14459();
- printf("Success\n");
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/cppdtor.d b/gcc/testsuite/gdc.test/runnable/cppdtor.d
new file mode 100644
index 00000000000..0592d448b1f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/cppdtor.d
@@ -0,0 +1,143 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=21693
+
+RUN_OUTPUT:
+---
+CppA:
+1: CppA.~this
+CppB:
+2: CppB.~this
+2: CppA.~this
+CppC:
+3: CppC.~this
+3: CppB.~this
+3: CppA.~this
+CppC:
+4: CppC.~this
+4: CppB.~this
+4: CppA.~this
+CppNoDestruct:
+DA:
+1: DA.~this
+DB:
+2: DB.~this
+2: DA.~this
+DC:
+3: DC.~this
+3: DB.~this
+3: DA.~this
+DC:
+4: DC.~this
+4: DB.~this
+4: DA.~this
+---
+*/
+
+extern (C) int printf(scope const char*, ...);
+
+extern (C++) class CppA
+{
+ int num;
+ this(int num)
+ {
+ this.num = num;
+ }
+
+ ~this()
+ {
+ printf("%d: CppA.~this\n", num);
+ }
+}
+
+extern (C++) class CppB : CppA
+{
+ this(int num)
+ {
+ super(num);
+ }
+
+ ~this()
+ {
+ printf("%d: CppB.~this\n", num);
+ }
+}
+
+extern (C++) class CppC : CppB
+{
+ this(int num)
+ {
+ super(num);
+ }
+
+ ~this()
+ {
+ printf("%d: CppC.~this\n", num);
+ }
+}
+
+extern (D) class DA
+{
+ int num;
+ this(int num)
+ {
+ this.num = num;
+ }
+
+ ~this()
+ {
+ printf("%d: DA.~this\n", num);
+ }
+}
+
+extern (D) class DB : DA
+{
+ this(int num)
+ {
+ super(num);
+ }
+
+ ~this()
+ {
+ printf("%d: DB.~this\n", num);
+ }
+}
+
+extern (D) class DC : DB
+{
+ this(int num)
+ {
+ super(num);
+ }
+
+ ~this()
+ {
+ printf("%d: DC.~this\n", num);
+ }
+}
+
+extern (C++) class CppNoDestruct
+{
+ int num;
+ this(int num)
+ {
+ this.num = num;
+ }
+}
+
+void main()
+{
+ printf("CppA:\n"); { scope a = new CppA(1); }
+ printf("CppB:\n"); { scope CppA b = new CppB(2); }
+ printf("CppC:\n"); { scope CppA c = new CppC(3); }
+ printf("CppC:\n"); { scope CppB c2 = new CppC(4); }
+
+ printf("CppNoDestruct:\n");
+ {
+ scope const nd = new CppNoDestruct(1);
+ }
+
+ printf("DA:\n"); { scope a = new DA(1); }
+ printf("DB:\n"); { scope DA b = new DB(2); }
+ printf("DC:\n"); { scope DA c = new DC(3); }
+ printf("DC:\n"); { scope DB c2 = new DC(4); }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/ctorpowtests.d b/gcc/testsuite/gdc.test/runnable/ctorpowtests.d
index 89f846a96e5..f2cf5d8fe0c 100644
--- a/gcc/testsuite/gdc.test/runnable/ctorpowtests.d
+++ b/gcc/testsuite/gdc.test/runnable/ctorpowtests.d
@@ -3,23 +3,32 @@
int magicVariable()
{
- if (__ctfe)
- return 3;
+ if (__ctfe)
+ return 3;
- shared int var = 2;
- return var;
+ shared int var = 2;
+ return var;
}
static assert(magicVariable()==3);
-void main()
+/************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11159
+
+void test11159()
{
- assert(!__ctfe);
- assert(magicVariable()==2);
+ import std.math : pow;
+ enum ulong
+ e_2_pow_64 = 2uL^^64,
+ e_10_pow_19 = 10uL^^19,
+ e_10_pow_20 = 10uL^^20;
+ assert(e_2_pow_64 == pow(2uL, 64));
+ assert(e_10_pow_19 == pow(10uL, 19));
+ assert(e_10_pow_20 == pow(10uL, 20));
}
-// bug 991 -- invalid.
-// bug 3500 -- is this related to 2127?
+// https://issues.dlang.org/show_bug.cgi?id=991 -- invalid.
+// https://issues.dlang.org/show_bug.cgi?id=3500 -- is this related to 2127?
// Tests for ^^
// TODO: These tests should not require import std.math.
@@ -32,6 +41,25 @@ static assert( 2.0 ^^ 3 == 8.0);
static assert( 2.0 ^^ 4 == 16.0);
static assert( 2 ^^ 4 == 16);
+static assert((2 ^^ 8) == 256);
+static assert((3 ^^ 8.0) == 6561);
+static assert((4.0 ^^ 8) == 65536);
+static assert((5.0 ^^ 8.0) == 390625);
+
+static assert((0.5 ^^ 3) == 0.125);
+static assert((1.5 ^^ 3.0) == 3.375);
+static assert((2.5 ^^ 3) == 15.625);
+static assert((3.5 ^^ 3.0) == 42.875);
+
+static assert(((-2) ^^ -5.0) == -0.031250);
+static assert(((-2.0) ^^ -6) == 0.015625);
+static assert(((-2.0) ^^ -7.0) == -0.0078125);
+
+static assert((144 ^^ 0.5) == 12);
+static assert((1089 ^^ 0.5) == 33);
+static assert((1764 ^^ 0.5) == 42);
+static assert((650.25 ^^ 0.5) == 25.5);
+
// Check the typing rules.
static assert( is (typeof(2.0^^7) == double));
static assert( is (typeof(7^^3) == int));
@@ -78,13 +106,16 @@ static assert( 2 ^^ 3 ^^ 2 == 2 ^^ 9);
static assert( 2.0 ^^ -3 ^^ 2 == 2.0 ^^ -9);
// 1 ^^ n is always 1, even if n is negative
-static assert( 1 ^^ -5 == 1);
+static assert( 1 ^^ -5.0 == 1);
-// -1 ^^ n gets transformed into n & 1 ? -1 : 1
-// even if n is negative
-static assert( (-1) ^^ -5 == -1);
-static assert( (-1) ^^ -4 == 1);
-static assert( (-1) ^^ 0 == 1);
+// -1.0 ^^ n is either 1 or -1 if n is integral.
+static assert( (-1.0) ^^ -5 == -1);
+static assert( (-1.0) ^^ -4 == 1);
+static assert( (-1.0) ^^ 0 == 1);
+// -1.0 ^^ n is otherwise always NaN.
+static assert( (-1.0) ^^ -5.5 is double.nan);
+static assert( (-1.0) ^^ -4.4 is double.nan);
+static assert( (-1.0) ^^ -0.1 is double.nan);
// n ^^ 0 is always 1
static assert( (-5) ^^ 0 == 1);
@@ -100,7 +131,7 @@ static assert( 9 ^^ -1.0 == 1.0 / 9);
static assert( !is(typeof(2 ^^ -5)));
static assert( !is(typeof((-2) ^^ -4)));
-// Bug 3535
+// https://issues.dlang.org/show_bug.cgi?id=3535
struct StructWithCtor
{
this(int _n) {
@@ -232,3 +263,12 @@ int anotherPowTest()
double x = 5.0;
return x^^4 > 2.0 ? 3: 2;
}
+
+/************************************/
+
+void main()
+{
+ assert(!__ctfe);
+ assert(magicVariable()==2);
+ test11159();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/declaration.d b/gcc/testsuite/gdc.test/runnable/declaration.d
index 0dbd2876d4e..5be76fac6d7 100644
--- a/gcc/testsuite/gdc.test/runnable/declaration.d
+++ b/gcc/testsuite/gdc.test/runnable/declaration.d
@@ -15,7 +15,7 @@ Success
extern(C) int printf(const char*, ...);
/***************************************************/
-// 6475
+// https://issues.dlang.org/show_bug.cgi?id=6475
class Foo6475(Value)
{
@@ -34,7 +34,7 @@ void test6475()
}
/***************************************************/
-// 6905
+// https://issues.dlang.org/show_bug.cgi?id=6905
void test6905()
{
@@ -62,7 +62,7 @@ void test6905()
}
/***************************************************/
-// 7019
+// https://issues.dlang.org/show_bug.cgi?id=7019
struct S7019
{
@@ -106,7 +106,7 @@ void test7019()
}
/***************************************************/
-// 7239
+// https://issues.dlang.org/show_bug.cgi?id=7239
struct vec7239
{
@@ -145,7 +145,7 @@ void test10635()
}
/***************************************************/
-// 8123
+// https://issues.dlang.org/show_bug.cgi?id=8123
void test8123()
{
@@ -169,7 +169,7 @@ void test8123()
}
/***************************************************/
-// 8147
+// https://issues.dlang.org/show_bug.cgi?id=8147
enum A8147 { a, b, c }
@@ -195,7 +195,7 @@ void test8147()
}
/***************************************************/
-// 8410
+// https://issues.dlang.org/show_bug.cgi?id=8410
void test8410()
{
@@ -207,7 +207,7 @@ void test8410()
}
/***************************************************/
-// 8942
+// https://issues.dlang.org/show_bug.cgi?id=8942
alias const int A8942_0;
static assert(is(A8942_0 == const int)); // passes
@@ -230,7 +230,7 @@ void test8942()
}
/***************************************************/
-// 10144
+// https://issues.dlang.org/show_bug.cgi?id=10144
final class TNFA10144(char_t)
{
@@ -258,7 +258,7 @@ class C10144
/***************************************************/
-// 10142
+// https://issues.dlang.org/show_bug.cgi?id=10142
class File10142
{
@@ -291,7 +291,7 @@ void test10142()
}
/***************************************************/
-// 11421
+// https://issues.dlang.org/show_bug.cgi?id=11421
void test11421()
{
@@ -308,7 +308,7 @@ void test11421()
}
/***************************************************/
-// 13776
+// https://issues.dlang.org/show_bug.cgi?id=13776
enum a13776(T) = __traits(compiles, { T; });
@@ -364,7 +364,7 @@ void test13776()
}
/***************************************************/
-// 14090
+// https://issues.dlang.org/show_bug.cgi?id=14090
template Packed14090(Args...)
{
@@ -389,7 +389,7 @@ template RoundRobin14090()
alias roundRobin14090 = RoundRobin14090!();
/***************************************************/
-// 13950
+// https://issues.dlang.org/show_bug.cgi?id=13950
template Tuple13950(T...) { alias T Tuple13950; }
diff --git a/gcc/testsuite/gdc.test/runnable/delegate.d b/gcc/testsuite/gdc.test/runnable/delegate.d
index a371e1cee3d..1eed53abb40 100644
--- a/gcc/testsuite/gdc.test/runnable/delegate.d
+++ b/gcc/testsuite/gdc.test/runnable/delegate.d
@@ -1,4 +1,19 @@
-// REQUIRED_ARGS:
+/*
+REQUIRED_ARGS:
+RUN_OUTPUT:
+---
+47 47
+47 47
+48 48
+48 48
+i = 1
+6
+here 3
+here 3
+here 3
+Success
+---
+*/
import core.stdc.stdio;
@@ -187,7 +202,7 @@ void test9()
}
/********************************************************/
-// 8257
+// https://issues.dlang.org/show_bug.cgi?id=8257
struct S8257 {
static int g() {
@@ -227,8 +242,8 @@ void test10()
class A12
{
public:
- int delegate(int, int) dgs[4];
- int function(int, int) fps[4];
+ int delegate(int, int)[4] dgs;
+ int function(int, int)[4] fps;
int delegate(int, int) dg;
int function(int, int) fp;
int f(int x, int y) {
@@ -263,7 +278,7 @@ void test12()
}
/********************************************************/
-// 1570
+// https://issues.dlang.org/show_bug.cgi?id=1570
class A13
{
@@ -291,7 +306,24 @@ void test13()
}
/********************************************************/
-// 2472
+
+enum dg14 = delegate { ++a14; b14 += 2; };
+
+int a14, b14;
+
+void test14()
+{
+ a14 = b14 = 10;
+
+ auto var = dg14;
+ var();
+
+ assert(a14 == 11);
+ assert(b14 == 12);
+}
+
+/********************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=2472
class A2472
{
@@ -348,6 +380,7 @@ int main()
test10();
test12();
test13();
+ test14();
test2472();
test8257();
testAssign();
diff --git a/gcc/testsuite/gdc.test/runnable/dhry.d b/gcc/testsuite/gdc.test/runnable/dhry.d
new file mode 100644
index 00000000000..f772d6160ad
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/dhry.d
@@ -0,0 +1,931 @@
+/*
+PERMUTE_ARGS:
+REQUIRED_ARGS: -release -O -g -inline
+DISABLED: freebsd dragonflybsd
+
+Deprecation caused by https://issues.dlang.org/show_bug.cgi?id=20645
+*/
+
+/*
+ *************************************************************************
+ *
+ * "DHRYSTONE" Benchmark Program
+ * -----------------------------
+ *
+ * Version: C, Version 2.1
+ *
+ * File: dhry.h (part 1 of 3)
+ *
+ * Date: May 25, 1988
+ *
+ * Author: Reinhold P. Weicker
+ * Siemens Nixdorf Inf. Syst.
+ * STM OS 32
+ * Otto-Hahn-Ring 6
+ * W-8000 Muenchen 83
+ * Germany
+ * Phone: [+49]-89-636-42436
+ * (8-17 Central European Time)
+ * UUCP: weicker@ztivax.uucp@unido.uucp
+ * Internet: weicker@ztivax.siemens.com
+ *
+ * Original Version (in Ada) published in
+ * "Communications of the ACM" vol. 27., no. 10 (Oct. 1984),
+ * pp. 1013 - 1030, together with the statistics
+ * on which the distribution of statements etc. is based.
+ *
+ * In this C version, the following C library functions are
+ * used:
+ * - strcpy, strcmp (inside the measurement loop)
+ * - printf, scanf (outside the measurement loop)
+ *
+ * Collection of Results:
+ * Reinhold Weicker (address see above) and
+ *
+ * Rick Richardson
+ * PC Research. Inc.
+ * 94 Apple Orchard Drive
+ * Tinton Falls, NJ 07724
+ * Phone: (201) 834-1378 (9-17 EST)
+ * UUCP: ...!uunet!pcrat!rick
+ *
+ * Please send results to Rick Richardson and/or Reinhold Weicker.
+ * Complete information should be given on hardware and software
+ * used. Hardware information includes: Machine type, CPU, type and
+ * size of caches; for microprocessors: clock frequency, memory speed
+ * (number of wait states). Software information includes: Compiler
+ * (and runtime library) manufacturer and version, compilation
+ * switches, OS version. The Operating System version may give an
+ * indication about the compiler; Dhrystone itself performs no OS
+ * calls in the measurement loop.
+ *
+ * The complete output generated by the program should be mailed
+ * such that at least some checks for correctness can be made.
+ *
+ *************************************************************************
+ *
+ * History: This version C/2.1 has been made for two reasons:
+ *
+ * 1) There is an obvious need for a common C version of
+ * Dhrystone, since C is at present the most popular system
+ * programming language for the class of processors
+ * (microcomputers, minicomputers) where Dhrystone is used
+ * most. There should be, as far as possible, only one C
+ * version of Dhrystone such that results can be compared
+ * without restrictions. In the past, the C versions
+ * distributed by Rick Richardson (Version 1.1) and by
+ * Reinhold Weicker had small (though not significant)
+ * differences.
+ *
+ * 2) As far as it is possible without changes to the
+ * Dhrystone statistics, optimizing compilers should be
+ * prevented from removing significant statements.
+ *
+ * This C version has been developed in cooperation with
+ * Rick Richardson (Tinton Falls, NJ), it incorporates many
+ * ideas from the "Version 1.1" distributed previously by
+ * him over the UNIX network Usenet.
+ * I also thank Chaim Benedelac (National Semiconductor),
+ * David Ditzel (SUN), Earl Killian and John Mashey (MIPS),
+ * Alan Smith and Rafael Saavedra-Barrera (UC at Berkeley)
+ * for their help with comments on earlier versions of the
+ * benchmark.
+ *
+ * Changes: In the initialization part, this version follows mostly
+ * Rick Richardson's version distributed via Usenet, not the
+ * version distributed earlier via floppy disk by Reinhold
+ * Weicker. As a concession to older compilers, names have
+ * been made unique within the first 8 characters. Inside the
+ * measurement loop, this version follows the version
+ * previously distributed by Reinhold Weicker.
+ *
+ * At several places in the benchmark, code has been added,
+ * but within the measurement loop only in branches that
+ * are not executed. The intention is that optimizing
+ * compilers should be prevented from moving code out of the
+ * measurement loop, or from removing code altogether. Since
+ * the statements that are executed within the measurement
+ * loop have NOT been changed, the numbers defining the
+ * "Dhrystone distribution" (distribution of statements,
+ * operand types and locality) still hold. Except for
+ * sophisticated optimizing compilers, execution times for
+ * this version should be the same as for previous versions.
+ *
+ * Since it has proven difficult to subtract the time for the
+ * measurement loop overhead in a correct way, the loop check
+ * has been made a part of the benchmark. This does have
+ * an impact - though a very minor one - on the distribution
+ * statistics which have been updated for this version.
+ *
+ * All changes within the measurement loop are described
+ * and discussed in the companion paper "Rationale for
+ * Dhrystone version 2".
+ *
+ * Because of the self-imposed limitation that the order and
+ * distribution of the executed statements should not be
+ * changed, there are still cases where optimizing compilers
+ * may not generate code for some statements. To a certain
+ * degree, this is unavoidable for small synthetic
+ * benchmarks. Users of the benchmark are advised to check
+ * code listings whether code is generated for all statements
+ * of Dhrystone.
+ *
+ * Version 2.1 is identical to version 2.0 distributed via
+ * the UNIX network Usenet in March 1988 except that it
+ * corrects some minor deficiencies that were found by users
+ * of version 2.0. The only change within the measurement
+ * loop is that a non-executed "else" part was added to the
+ * "if" statement in Func_3, and a non-executed "else" part
+ * removed from Proc_3.
+ *
+ *************************************************************************
+ *
+ * Defines: The following "Defines" are possible:
+ * -DROPT (default: Not defined)
+ * As an approximation to what an average C
+ * programmer might do, the "register" storage class
+ * is applied (if enabled by -DROPT)
+ * - for local variables, if they are used
+ * (dynamically) five or more times
+ * - for parameters if they are used (dynamically)
+ * six or more times
+ * Note that an optimal "register" strategy is
+ * compiler-dependent, and that "register"
+ * declarations do not necessarily lead to faster
+ * execution.
+ * -DNOSTRUCTASSIGN (default: Not defined)
+ * Define if the C compiler does not support
+ * assignment of structures.
+ * -DNOENUMS (default: Not defined)
+ * Define if the C compiler does not support
+ * enumeration types.
+ *
+ *************************************************************************
+ *
+ * Compilation model and measurement (IMPORTANT):
+ *
+ * This C version of Dhrystone consists of three files:
+ * - dhry.h (this file, containing global definitions and comments)
+ * - dhry_1.c (containing the code corresponding to Ada package Pack_1)
+ * - dhry_2.c (containing the code corresponding to Ada package Pack_2)
+ *
+ * The following "ground rules" apply for measurements:
+ * - Separate compilation
+ * - No procedure merging
+ * - Otherwise, compiler optimizations are allowed but should be
+ * indicated
+ * - Default results are those without register declarations
+ * See the companion paper "Rationale for Dhrystone Version 2" for a more
+ * detailed discussion of these ground rules.
+ *
+ * For 16-Bit processors (e.g. 80186, 80286), times for all compilation
+ * models ("small", "medium", "large" etc.) should be given if possible,
+ * together with a definition of these models for the compiler system
+ * used.
+ *
+ *************************************************************************
+ *
+ * Dhrystone (C version) statistics:
+ *
+ * [Comment from the first distribution, updated for version 2.
+ * Note that because of language differences, the numbers are slightly
+ * different from the Ada version.]
+ *
+ * The following program contains statements of a high level programming
+ * language (here: C) in a distribution considered representative:
+ *
+ * assignments 52 (51.0 %)
+ * control statements 33 (32.4 %)
+ * procedure, function calls 17 (16.7 %)
+ *
+ * 103 statements are dynamically executed. The program is balanced with
+ * respect to the three aspects:
+ *
+ * - statement type
+ * - operand type
+ * - operand locality
+ * operand global, local, parameter, or constant.
+ *
+ * The combination of these three aspects is balanced only approximately.
+ *
+ * 1. Statement Type:
+ * ----------------- number
+ *
+ * V1 = V2 9
+ * (incl. V1 = F(..)
+ * V = Constant 12
+ * Assignment, 7
+ * with array element
+ * Assignment, 6
+ * with record component
+ * --
+ * 34 34
+ *
+ * X = Y +|-|"&&"|"|" Z 5
+ * X = Y +|-|"==" Constant 6
+ * X = X +|- 1 3
+ * X = Y *|/ Z 2
+ * X = Expression, 1
+ * two operators
+ * X = Expression, 1
+ * three operators
+ * --
+ * 18 18
+ *
+ * if .... 14
+ * with "else" 7
+ * without "else" 7
+ * executed 3
+ * not executed 4
+ * for ... 7 | counted every time
+ * while ... 4 | the loop condition
+ * do ... while 1 | is evaluated
+ * switch ... 1
+ * break 1
+ * declaration with 1
+ * initialization
+ * --
+ * 34 34
+ *
+ * P (...) procedure call 11
+ * user procedure 10
+ * library procedure 1
+ * X = F (...)
+ * function call 6
+ * user function 5
+ * library function 1
+ * --
+ * 17 17
+ * ---
+ * 103
+ *
+ * The average number of parameters in procedure or function calls
+ * is 1.82 (not counting the function values as implicit parameters).
+ *
+ *
+ * 2. Operators
+ * ------------
+ * number approximate
+ * percentage
+ *
+ * Arithmetic 32 50.8
+ *
+ * + 21 33.3
+ * - 7 11.1
+ * * 3 4.8
+ * / (int div) 1 1.6
+ *
+ * Comparison 27 42.8
+ *
+ * == 9 14.3
+ * /= 4 6.3
+ * > 1 1.6
+ * < 3 4.8
+ * >= 1 1.6
+ * <= 9 14.3
+ *
+ * Logic 4 6.3
+ *
+ * && (AND-THEN) 1 1.6
+ * | (OR) 1 1.6
+ * ! (NOT) 2 3.2
+ *
+ * -- -----
+ * 63 100.1
+ *
+ *
+ * 3. Operand Type (counted once per operand reference):
+ * ---------------
+ * number approximate
+ * percentage
+ *
+ * Integer 175 72.3 %
+ * Character 45 18.6 %
+ * Pointer 12 5.0 %
+ * String30 6 2.5 %
+ * Array 2 0.8 %
+ * Record 2 0.8 %
+ * --- -------
+ * 242 100.0 %
+ *
+ * When there is an access path leading to the final operand (e.g. a
+ * record component), only the final data type on the access path is
+ * counted.
+ *
+ *
+ * 4. Operand Locality:
+ * -------------------
+ * number approximate
+ * percentage
+ *
+ * local variable 114 47.1 %
+ * global variable 22 9.1 %
+ * parameter 45 18.6 %
+ * value 23 9.5 %
+ * reference 22 9.1 %
+ * function result 6 2.5 %
+ * constant 55 22.7 %
+ * --- -------
+ * 242 100.0 %
+ *
+ *
+ * The program does not compute anything meaningful, but it is
+ * syntactically and semantically correct. All variables have a value
+ * assigned to them before they are used as a source operand.
+ *
+ * There has been no explicit effort to account for the effects of a
+ * cache, or to balance the use of long or short displacements for code
+ * or data.
+ *
+ *************************************************************************
+ */
+
+import core.stdc.stdio;
+import core.stdc.string;
+import core.stdc.stdlib;
+import std.string;
+
+
+/* Compiler and system dependent definitions: */
+
+const double Mic_secs_Per_Second = 1000000.0;
+ /* Berkeley UNIX C returns process times in seconds/HZ */
+
+enum {Ident_1, Ident_2, Ident_3, Ident_4, Ident_5}
+alias int Enumeration;
+ /* for boolean and enumeration types in Ada, Pascal */
+
+/* General definitions: */
+
+const int StrLen = 30;
+
+alias int One_Thirty;
+alias int One_Fifty;
+alias char Capital_Letter;
+alias bool Boolean;
+alias char[StrLen] Str_30;
+alias int[50] Arr_1_Dim;
+alias int[50][50] Arr_2_Dim;
+
+struct record
+{
+ record *Ptr_Comp;
+ Enumeration Discr;
+ union V {
+ struct V1 {
+ Enumeration Enum_Comp;
+ int Int_Comp;
+ char[StrLen] Str_Comp;
+ }
+ V1 var_1;
+ struct V2 {
+ Enumeration E_Comp_2;
+ char [StrLen] Str_2_Comp;
+ }
+ V2 var_2;
+ struct V3 {
+ char Ch_1_Comp;
+ char Ch_2_Comp;
+ }
+ V3 var_3;
+ }
+ V variant;
+}
+
+alias record Rec_Type;
+alias record *Rec_Pointer;
+
+
+/* Global Variables: */
+
+Rec_Pointer Ptr_Glob,
+ Next_Ptr_Glob;
+int Int_Glob;
+Boolean Bool_Glob;
+char Ch_1_Glob,
+ Ch_2_Glob;
+int[50] Arr_1_Glob;
+int[50][50] Arr_2_Glob;
+
+char[StrLen] Reg_Define = "Register option selected.";
+
+/* variables for time measurement: */
+
+const int Too_Small_Time = 2;
+ /* Measurements should last at least 2 seconds */
+
+double Begin_Time,
+ End_Time,
+ User_Time;
+
+double Microseconds,
+ Dhrystones_Per_Second,
+ Vax_Mips;
+
+/* end of variables for time measurement */
+
+
+void main ()
+/*****/
+
+ /* main program, corresponds to procedures */
+ /* Main and Proc_0 in the Ada version */
+{
+ One_Fifty Int_1_Loc;
+ One_Fifty Int_2_Loc;
+ One_Fifty Int_3_Loc;
+ char Ch_Index;
+ Enumeration Enum_Loc;
+ Str_30 Str_1_Loc;
+ Str_30 Str_2_Loc;
+ int Run_Index;
+ int Number_Of_Runs;
+
+/+
+ FILE *Ap;
+
+ /* Initializations */
+
+ if ((Ap = fopen("dhry.res","a+")) == null)
+ {
+ printf("Can not open dhry.res\n\n");
+ exit(1);
+ }
++/
+
+ Next_Ptr_Glob = cast(Rec_Pointer) malloc (Rec_Type.sizeof);
+ Ptr_Glob = cast(Rec_Pointer) malloc (Rec_Type.sizeof);
+
+ Ptr_Glob.Ptr_Comp = Next_Ptr_Glob;
+ Ptr_Glob.Discr = Ident_1;
+ Ptr_Glob.variant.var_1.Enum_Comp = Ident_3;
+ Ptr_Glob.variant.var_1.Int_Comp = 40;
+// strcpy (Ptr_Glob.variant.var_1.Str_Comp,
+// "DHRYSTONE PROGRAM, SOME STRING");
+// strcpy (Str_1_Loc, "DHRYSTONE PROGRAM, 1'ST STRING");
+ Ptr_Glob.variant.var_1.Str_Comp[] = "DHRYSTONE PROGRAM, SOME STRING";
+ Str_1_Loc[] = "DHRYSTONE PROGRAM, 1'ST STRING";
+
+ Arr_2_Glob [8][7] = 10;
+ /* Was missing in published program. Without this statement, */
+ /* Arr_2_Glob [8][7] would have an undefined value. */
+ /* Warning: With 16-Bit processors and Number_Of_Runs > 32000, */
+ /* overflow may occur for this array element. */
+
+ printf ("\n");
+ printf ("Dhrystone Benchmark, Version 2.1 (Language: D)\n");
+ printf ("\n");
+ printf ("Please give the number of runs through the benchmark: ");
+ {
+ int n;
+ //scanf ("%d", &n);
+ n = 10000000;
+ Number_Of_Runs = n;
+ }
+ printf ("\n");
+
+ printf ("Execution starts, %d runs through Dhrystone\n",Number_Of_Runs);
+
+ /***************/
+ /* Start timer */
+ /***************/
+
+ Begin_Time = dtime();
+
+ for (Run_Index = 1; Run_Index <= Number_Of_Runs; ++Run_Index)
+ {
+
+ Proc_5();
+ Proc_4();
+ /* Ch_1_Glob == 'A', Ch_2_Glob == 'B', Bool_Glob == true */
+ Int_1_Loc = 2;
+ Int_2_Loc = 3;
+ //strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 2'ND STRING");
+ Str_2_Loc[] = "DHRYSTONE PROGRAM, 2'ND STRING";
+ Enum_Loc = Ident_2;
+ Bool_Glob = ! Func_2 (Str_1_Loc, Str_2_Loc);
+ /* Bool_Glob == 1 */
+ while (Int_1_Loc < Int_2_Loc) /* loop body executed once */
+ {
+ Int_3_Loc = 5 * Int_1_Loc - Int_2_Loc;
+ /* Int_3_Loc == 7 */
+ Proc_7 (Int_1_Loc, Int_2_Loc, &Int_3_Loc);
+ /* Int_3_Loc == 7 */
+ Int_1_Loc += 1;
+ } /* while */
+ /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */
+ Proc_8 (Arr_1_Glob, Arr_2_Glob, Int_1_Loc, Int_3_Loc);
+ /* Int_Glob == 5 */
+ Proc_1 (Ptr_Glob);
+ for (Ch_Index = 'A'; Ch_Index <= Ch_2_Glob; ++Ch_Index)
+ /* loop body executed twice */
+ {
+ if (Enum_Loc == Func_1 (Ch_Index, 'C'))
+ /* then, not executed */
+ {
+ Proc_6 (Ident_1, &Enum_Loc);
+ //strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 3'RD STRING");
+ Str_2_Loc[] = "DHRYSTONE PROGRAM, 3'RD STRING";
+ Int_2_Loc = Run_Index;
+ Int_Glob = Run_Index;
+ }
+ }
+ /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */
+ Int_2_Loc = Int_2_Loc * Int_1_Loc;
+ Int_1_Loc = Int_2_Loc / Int_3_Loc;
+ Int_2_Loc = 7 * (Int_2_Loc - Int_3_Loc) - Int_1_Loc;
+ /* Int_1_Loc == 1, Int_2_Loc == 13, Int_3_Loc == 7 */
+ Proc_2 (&Int_1_Loc);
+ /* Int_1_Loc == 5 */
+
+ } /* loop "for Run_Index" */
+
+ /**************/
+ /* Stop timer */
+ /**************/
+
+ End_Time = dtime();
+
+ printf ("Execution ends\n");
+ printf ("\n");
+ printf ("Final values of the variables used in the benchmark:\n");
+ printf ("\n");
+ printf ("Int_Glob: %d\n", Int_Glob);
+ printf (" should be: %d\n", 5);
+ printf ("Bool_Glob: %d\n", Bool_Glob);
+ printf (" should be: %d\n", 1);
+ printf ("Ch_1_Glob: %c\n", Ch_1_Glob);
+ printf (" should be: %c\n", 'A');
+ printf ("Ch_2_Glob: %c\n", Ch_2_Glob);
+ printf (" should be: %c\n", 'B');
+ printf ("Arr_1_Glob[8]: %d\n", Arr_1_Glob[8]);
+ printf (" should be: %d\n", 7);
+ printf ("Arr_2_Glob[8][7]: %d\n", Arr_2_Glob[8][7]);
+ printf (" should be: Number_Of_Runs + 10\n");
+ printf ("Ptr_Glob.\n");
+ printf (" Ptr_Comp: %d\n", cast(int) Ptr_Glob.Ptr_Comp);
+ printf (" should be: (implementation-dependent)\n");
+ printf (" Discr: %d\n", Ptr_Glob.Discr);
+ printf (" should be: %d\n", 0);
+ printf (" Enum_Comp: %d\n", Ptr_Glob.variant.var_1.Enum_Comp);
+ printf (" should be: %d\n", 2);
+ printf (" Int_Comp: %d\n", Ptr_Glob.variant.var_1.Int_Comp);
+ printf (" should be: %d\n", 17);
+ printf (" Str_Comp: %.*s\n", cast(int)Ptr_Glob.variant.var_1.Str_Comp.length, Ptr_Glob.variant.var_1.Str_Comp.ptr);
+ printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n");
+ printf ("Next_Ptr_Glob.\n");
+ printf (" Ptr_Comp: %d\n", cast(int) Next_Ptr_Glob.Ptr_Comp);
+ printf (" should be: (implementation-dependent), same as above\n");
+ printf (" Discr: %d\n", Next_Ptr_Glob.Discr);
+ printf (" should be: %d\n", 0);
+ printf (" Enum_Comp: %d\n", Next_Ptr_Glob.variant.var_1.Enum_Comp);
+ printf (" should be: %d\n", 1);
+ printf (" Int_Comp: %d\n", Next_Ptr_Glob.variant.var_1.Int_Comp);
+ printf (" should be: %d\n", 18);
+ printf (" Str_Comp: %.*s\n", cast(int)Next_Ptr_Glob.variant.var_1.Str_Comp.length, Next_Ptr_Glob.variant.var_1.Str_Comp.ptr);
+ printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n");
+ printf ("Int_1_Loc: %d\n", Int_1_Loc);
+ printf (" should be: %d\n", 5);
+ printf ("Int_2_Loc: %d\n", Int_2_Loc);
+ printf (" should be: %d\n", 13);
+ printf ("Int_3_Loc: %d\n", Int_3_Loc);
+ printf (" should be: %d\n", 7);
+ printf ("Enum_Loc: %d\n", Enum_Loc);
+ printf (" should be: %d\n", 1);
+ printf ("Str_1_Loc: %.*s\n", cast(int)Str_1_Loc.length, Str_1_Loc.ptr);
+ printf (" should be: DHRYSTONE PROGRAM, 1'ST STRING\n");
+ printf ("Str_2_Loc: %.*s\n", cast(int)Str_2_Loc.length, Str_2_Loc.ptr);
+ printf (" should be: DHRYSTONE PROGRAM, 2'ND STRING\n");
+ printf ("\n");
+
+ User_Time = End_Time - Begin_Time;
+
+ if (User_Time < Too_Small_Time)
+ {
+ printf ("Measured time too small to obtain meaningful results\n");
+ printf ("Please increase number of runs\n");
+ printf ("\n");
+ }
+ else
+ {
+ Microseconds = User_Time * Mic_secs_Per_Second
+ / cast(double) Number_Of_Runs;
+ Dhrystones_Per_Second = cast(double) Number_Of_Runs / User_Time;
+ Vax_Mips = Dhrystones_Per_Second / 1757.0;
+
+ printf ("Register option selected? NO\n");
+ strcpy(Reg_Define.ptr, "Register option not selected.");
+ printf ("Microseconds for one run through Dhrystone: ");
+ printf ("%7.1f \n", Microseconds);
+ printf ("Dhrystones per Second: ");
+ printf ("%10.1f \n", Dhrystones_Per_Second);
+ printf ("VAX MIPS rating = %10.3f \n",Vax_Mips);
+ printf ("\n");
+
+ /+
+ fprintf(Ap,"\n");
+ fprintf(Ap,"Dhrystone Benchmark, Version 2.1 (Language: D)\n");
+ fprintf(Ap,"%.*s\n",Reg_Define.length, Reg_Define.ptr);
+ fprintf(Ap,"Microseconds for one loop: %7.1f\n",Microseconds);
+ fprintf(Ap,"Dhrystones per second: %10.1f\n",Dhrystones_Per_Second);
+ fprintf(Ap,"VAX MIPS rating: %10.3f\n",Vax_Mips);
+ fclose(Ap);
+ +/
+
+ }
+
+}
+
+void Proc_1 (Rec_Pointer Ptr_Val_Par)
+/******************/
+
+ /* executed once */
+{
+ Rec_Pointer Next_Record = Ptr_Val_Par.Ptr_Comp;
+ /* == Ptr_Glob_Next */
+ /* Local variable, initialized with Ptr_Val_Par.Ptr_Comp, */
+ /* corresponds to "rename" in Ada, "with" in Pascal */
+
+ *Ptr_Val_Par.Ptr_Comp = *Ptr_Glob;
+ Ptr_Val_Par.variant.var_1.Int_Comp = 5;
+ Next_Record.variant.var_1.Int_Comp
+ = Ptr_Val_Par.variant.var_1.Int_Comp;
+ Next_Record.Ptr_Comp = Ptr_Val_Par.Ptr_Comp;
+ Proc_3 (&Next_Record.Ptr_Comp);
+ /* Ptr_Val_Par.Ptr_Comp.Ptr_Comp
+ == Ptr_Glob.Ptr_Comp */
+ if (Next_Record.Discr == Ident_1)
+ /* then, executed */
+ {
+ Next_Record.variant.var_1.Int_Comp = 6;
+ Proc_6 (Ptr_Val_Par.variant.var_1.Enum_Comp,
+ &Next_Record.variant.var_1.Enum_Comp);
+ Next_Record.Ptr_Comp = Ptr_Glob.Ptr_Comp;
+ Proc_7 (Next_Record.variant.var_1.Int_Comp, 10,
+ &Next_Record.variant.var_1.Int_Comp);
+ }
+ else /* not executed */
+ *Ptr_Val_Par = *Ptr_Val_Par.Ptr_Comp;
+} /* Proc_1 */
+
+void Proc_2 (One_Fifty *Int_Par_Ref)
+/******************/
+ /* executed once */
+ /* *Int_Par_Ref == 1, becomes 4 */
+{
+ One_Fifty Int_Loc;
+ Enumeration Enum_Loc;
+
+ Int_Loc = *Int_Par_Ref + 10;
+ do /* executed once */
+ if (Ch_1_Glob == 'A')
+ /* then, executed */
+ {
+ Int_Loc -= 1;
+ *Int_Par_Ref = Int_Loc - Int_Glob;
+ Enum_Loc = Ident_1;
+ } /* if */
+ while (Enum_Loc != Ident_1); /* true */
+} /* Proc_2 */
+
+
+void Proc_3 (Rec_Pointer *Ptr_Ref_Par)
+/******************/
+ /* executed once */
+ /* Ptr_Ref_Par becomes Ptr_Glob */
+
+{
+ if (Ptr_Glob != null)
+ /* then, executed */
+ *Ptr_Ref_Par = Ptr_Glob.Ptr_Comp;
+ Proc_7 (10, Int_Glob, &Ptr_Glob.variant.var_1.Int_Comp);
+} /* Proc_3 */
+
+void Proc_4 () /* without parameters */
+/*******/
+ /* executed once */
+{
+ Boolean Bool_Loc;
+
+ Bool_Loc = Ch_1_Glob == 'A';
+ Bool_Glob = Bool_Loc | Bool_Glob;
+ Ch_2_Glob = 'B';
+} /* Proc_4 */
+
+
+void Proc_5 () /* without parameters */
+/*******/
+ /* executed once */
+{
+ Ch_1_Glob = 'A';
+ Bool_Glob = false;
+} /* Proc_5 */
+
+
+void Proc_6 (Enumeration Enum_Val_Par, Enumeration *Enum_Ref_Par)
+/*********************************/
+ /* executed once */
+ /* Enum_Val_Par == Ident_3, Enum_Ref_Par becomes Ident_2 */
+{
+ *Enum_Ref_Par = Enum_Val_Par;
+ if (! Func_3 (Enum_Val_Par))
+ /* then, not executed */
+ *Enum_Ref_Par = Ident_4;
+ final switch (Enum_Val_Par)
+ {
+ case Ident_1:
+ *Enum_Ref_Par = Ident_1;
+ break;
+ case Ident_2:
+ if (Int_Glob > 100)
+ /* then */
+ *Enum_Ref_Par = Ident_1;
+ else *Enum_Ref_Par = Ident_4;
+ break;
+ case Ident_3: /* executed */
+ *Enum_Ref_Par = Ident_2;
+ break;
+ case Ident_4: break;
+ case Ident_5:
+ *Enum_Ref_Par = Ident_3;
+ break;
+ } /* switch */
+} /* Proc_6 */
+
+
+void Proc_7 (One_Fifty Int_1_Par_Val, One_Fifty Int_2_Par_Val, One_Fifty *Int_Par_Ref)
+/**********************************************/
+ /* executed three times */
+ /* first call: Int_1_Par_Val == 2, Int_2_Par_Val == 3, */
+ /* Int_Par_Ref becomes 7 */
+ /* second call: Int_1_Par_Val == 10, Int_2_Par_Val == 5, */
+ /* Int_Par_Ref becomes 17 */
+ /* third call: Int_1_Par_Val == 6, Int_2_Par_Val == 10, */
+ /* Int_Par_Ref becomes 18 */
+{
+ One_Fifty Int_Loc;
+
+ Int_Loc = Int_1_Par_Val + 2;
+ *Int_Par_Ref = Int_2_Par_Val + Int_Loc;
+} /* Proc_7 */
+
+
+void Proc_8 (ref Arr_1_Dim Arr_1_Par_Ref, ref Arr_2_Dim Arr_2_Par_Ref, int Int_1_Par_Val, int Int_2_Par_Val)
+/*********************************************************************/
+ /* executed once */
+ /* Int_Par_Val_1 == 3 */
+ /* Int_Par_Val_2 == 7 */
+{
+ One_Fifty Int_Index;
+ One_Fifty Int_Loc;
+
+ Int_Loc = Int_1_Par_Val + 5;
+ Arr_1_Par_Ref [Int_Loc] = Int_2_Par_Val;
+ Arr_1_Par_Ref [Int_Loc+1] = Arr_1_Par_Ref [Int_Loc];
+ Arr_1_Par_Ref [Int_Loc+30] = Int_Loc;
+ for (Int_Index = Int_Loc; Int_Index <= Int_Loc+1; ++Int_Index)
+ Arr_2_Par_Ref [Int_Loc] [Int_Index] = Int_Loc;
+ Arr_2_Par_Ref [Int_Loc] [Int_Loc-1] += 1;
+ Arr_2_Par_Ref [Int_Loc+20] [Int_Loc] = Arr_1_Par_Ref [Int_Loc];
+ Int_Glob = 5;
+} /* Proc_8 */
+
+
+Enumeration Func_1 (Capital_Letter Ch_1_Par_Val, Capital_Letter Ch_2_Par_Val)
+/*************************************************/
+ /* executed three times */
+ /* first call: Ch_1_Par_Val == 'H', Ch_2_Par_Val == 'R' */
+ /* second call: Ch_1_Par_Val == 'A', Ch_2_Par_Val == 'C' */
+ /* third call: Ch_1_Par_Val == 'B', Ch_2_Par_Val == 'C' */
+{
+ Capital_Letter Ch_1_Loc;
+ Capital_Letter Ch_2_Loc;
+
+ Ch_1_Loc = Ch_1_Par_Val;
+ Ch_2_Loc = Ch_1_Loc;
+ if (Ch_2_Loc != Ch_2_Par_Val)
+ /* then, executed */
+ return (Ident_1);
+ else /* not executed */
+ {
+ Ch_1_Glob = Ch_1_Loc;
+ return (Ident_2);
+ }
+} /* Func_1 */
+
+
+Boolean Func_2 (Str_30 Str_1_Par_Ref, Str_30 Str_2_Par_Ref)
+/*************************************************/
+ /* executed once */
+ /* Str_1_Par_Ref == "DHRYSTONE PROGRAM, 1'ST STRING" */
+ /* Str_2_Par_Ref == "DHRYSTONE PROGRAM, 2'ND STRING" */
+{
+ One_Thirty Int_Loc;
+ Capital_Letter Ch_Loc;
+
+ Int_Loc = 2;
+ while (Int_Loc <= 2) /* loop body executed once */
+ if (Func_1 (Str_1_Par_Ref[Int_Loc],
+ Str_2_Par_Ref[Int_Loc+1]) == Ident_1)
+ /* then, executed */
+ {
+ Ch_Loc = 'A';
+ Int_Loc += 1;
+ } /* if, while */
+ if (Ch_Loc >= 'W' && Ch_Loc < 'Z')
+ /* then, not executed */
+ Int_Loc = 7;
+ if (Ch_Loc == 'R')
+ /* then, not executed */
+ return (true);
+ else /* executed */
+ {
+ //if (strcmp (Str_1_Par_Ref, Str_2_Par_Ref) > 0)
+ //if (memcmp (Str_1_Par_Ref, Str_2_Par_Ref, 30) > 0)
+ if (Str_1_Par_Ref > Str_2_Par_Ref)
+ /* then, not executed */
+ {
+ Int_Loc += 7;
+ Int_Glob = Int_Loc;
+ return (true);
+ }
+ else /* executed */
+ return (false);
+ } /* if Ch_Loc */
+} /* Func_2 */
+
+
+Boolean Func_3 (Enumeration Enum_Par_Val)
+/***************************/
+ /* executed once */
+ /* Enum_Par_Val == Ident_3 */
+{
+ Enumeration Enum_Loc;
+
+ Enum_Loc = Enum_Par_Val;
+ if (Enum_Loc == Ident_3)
+ /* then, executed */
+ return (true);
+ else /* not executed */
+ return (false);
+} /* Func_3 */
+
+version (Windows)
+{
+ import core.sys.windows.winbase;
+
+ double dtime()
+ {
+ double q;
+
+ q = cast(double)GetTickCount() * 1.0e-03;
+
+ return q;
+ }
+}
+
+version (linux)
+{
+ import core.stdc.time;
+
+ double dtime()
+ {
+ double q;
+
+ q = cast(double)time(null);
+
+ return q;
+ }
+}
+
+version (OSX) // supplied by Anders F Bjorklund
+{
+ import core.sys.posix.sys.time;
+
+ double dtime()
+ {
+ double q;
+ timeval tv;
+
+ gettimeofday(&tv,null);
+ q = cast(double)tv.tv_sec + cast(double)tv.tv_usec * 1.0e-6;
+
+ return q;
+ }
+}
+
+version (NetBSD)
+{
+ import core.sys.posix.sys.time;
+
+ double dtime()
+ {
+ double q;
+ timeval tv;
+
+ gettimeofday(&tv,null);
+ q = cast(double)tv.tv_sec + cast(double)tv.tv_usec * 1.0e-6;
+
+ return q;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/eh.d b/gcc/testsuite/gdc.test/runnable/eh.d
index b3077a2e96f..7e7ec19eb30 100644
--- a/gcc/testsuite/gdc.test/runnable/eh.d
+++ b/gcc/testsuite/gdc.test/runnable/eh.d
@@ -104,7 +104,7 @@ printf("catch, i = %d\n", i);
}
}
- printf("iterations %d totals: %ld, %ld\n", cIterations, total_x, total_nox);
+ printf("iterations %d totals: %lld, %lld\n", cIterations, total_x, total_nox);
}
int fn2_nox()
@@ -170,7 +170,7 @@ void test4()
catch(Exception e)
{
auto es = e.toString();
- printf("%.*s\n", es.length, es.ptr);
+ printf("%.*s\n", cast(int)es.length, es.ptr);
b++;
}
finally
@@ -213,7 +213,7 @@ void test4()
{
d++;
string es = e.toString;
- printf("%.*s\n", es.length, es.ptr);
+ printf("%.*s\n", cast(int)es.length, es.ptr);
}
assert(a == 2);
@@ -255,7 +255,7 @@ void test4()
{
q3++;
string es = e.toString;
- printf("%.*s\n", es.length, es.ptr);
+ printf("%.*s\n", cast(int)es.length, es.ptr);
}
assert(q0 == 1);
@@ -287,7 +287,7 @@ void test5()
result ~= cast(char)('a' + i);
}
}
- printf("--- %.*s", result.length, result.ptr);
+ printf("--- %.*s", cast(int)result.length, result.ptr);
if (result != "tctbta")
assert(0);
}
@@ -352,6 +352,38 @@ void test7()
/****************************************************
* Exception chaining tests. See also test4.d
+ * Don writes about the complexity:
+
+I can explain this, since I did the original implementation.
+When I originally implemented this, I discovered that the idea of
+"chained exceptions" was hopeless naive. The idea was that while
+processing one exception, if you encounter a second one, and you
+chain them together. Then you get a third, fourth, etc.
+
+The problem is that it's much more complicated than that. Each of
+the exceptions can be a chain of exceptions themselves. This means
+that you don't end up with a chain of exceptions, but rather a tree
+of exceptions. That's why there are those really nasty test cases
+in the test suite.
+
+The examples in the test suite are very difficult to understand if
+you expect it to be a simple chain!
+
+On the one hand, I was very proud that I was able to work out the
+barely-documented behaviour of Windows SEH, and it was really
+thorough. In the initial implementation, all the complexity
+was covered. It wasn't the bugfix-driven-development which dmd
+usually operates under <g>.
+
+But on the other hand, once you can see all of the complexity,
+exception chaining becomes much less convincing as a concept. Sure,
+the full exception tree is available in the final exception which
+you catch. But, is it of any use? I doubt it very much.
+It's pretty clearly a nett loss to the language, it increases
+complexity with negligible benefit. Fortunately in this case, the
+cost isn't really high.
+
+https://digitalmars.com/d/archives/digitalmars/D/Dicebot_on_leaving_D_It_is_anarchy_driven_development_in_all_its_317950.html#N318305
****************************************************/
int result1513;
@@ -379,6 +411,15 @@ void bug1513b()
assert(e.msg == "d");
assert(e.next.msg == "f");
assert(!e.next.next);
+ int i;
+ foreach (u; e)
+ {
+ if (i)
+ assert(u.msg == "f");
+ else
+ assert(u.msg == "d");
+ ++i;
+ }
}
}
@@ -658,7 +699,7 @@ void test9()
}
/****************************************************/
-// 10964
+// https://issues.dlang.org/show_bug.cgi?id=10964
void test10964()
{
@@ -836,6 +877,137 @@ void test17481()
/****************************************************/
+// a nothrow function, even though it is not marked as nothrow
+void test12()
+{
+ int i = 3;
+ try
+ {
+ try
+ {
+ ++i;
+ goto L10;
+ }
+ finally
+ {
+ i *= 2;
+ printf("f1\n");
+ }
+ }
+ finally
+ {
+ i += 5;
+ printf("f2\n");
+ }
+
+L10:
+ printf("3\n");
+ assert(i == (3 + 1) * 2 + 5);
+}
+
+/****************************************************/
+
+void foo13() { }
+
+void test13()
+{
+ int i = 3;
+ try
+ {
+ try
+ {
+ foo13(); // compiler assumes it throws
+ ++i;
+ goto L10;
+ }
+ finally
+ {
+ i *= 2;
+ printf("f1\n");
+ }
+ }
+ finally
+ {
+ i += 5;
+ printf("f2\n");
+ }
+
+L10:
+ printf("3\n");
+ assert(i == (3 + 1) * 2 + 5);
+}
+
+/****************************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=10966
+
+void bug10966a(void* p)
+{
+ void* pstart = p;
+
+ try
+ {
+ p = null;
+ throw new Exception("dummy");
+ }
+ catch (Throwable o)
+ {
+ assert(p != pstart);
+ }
+}
+
+void bug10966b()
+{
+ int x = 0;
+ int i = 0;
+ try
+ {
+ i = 1;
+ throw new Exception("dummy");
+ }
+ catch (Throwable o)
+ {
+ x = i;
+ }
+ assert(x == 1);
+}
+
+void test10966()
+{
+ int s;
+ bug10966a(&s);
+ bug10966b();
+}
+
+/****************************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=11049
+
+void test11049()
+{
+ int[] arr = [1,2,3];
+
+#line 4100 "foo"
+ try { auto n = arr[3]; }
+ catch (Error e)
+ {
+ //printf("e.file = %s\n", e.file.ptr);
+ assert(e.file == "foo"); // fails
+ assert(e.line == 4100);
+ }
+
+#line 4200 "bar"
+ try { auto a = arr[3..9]; }
+ catch (Error e)
+ {
+ //printf("e.file = %s\n", e.file.ptr);
+ assert(e.file == "bar"); // fails
+ assert(e.line == 4200);
+ }
+}
+
+/****************************************************/
+
int main()
{
printf("start\n");
@@ -860,6 +1032,10 @@ int main()
test10();
test11();
test17481();
+ test12();
+ test13();
+ test10966();
+ test11049();
printf("finish\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/entity1.d b/gcc/testsuite/gdc.test/runnable/entity1.d
index 39211934110..feeb1ee1536 100644
--- a/gcc/testsuite/gdc.test/runnable/entity1.d
+++ b/gcc/testsuite/gdc.test/runnable/entity1.d
@@ -136,7 +136,7 @@ int main(){
return 0;
}
-// Bug 5221
+// https://issues.dlang.org/show_bug.cgi?id=5221
static assert('\&check;'==10003);
static assert('\&lsim;'==8818);
static assert('\&numero;'==8470);
diff --git a/gcc/testsuite/gdc.test/runnable/evalorder.d b/gcc/testsuite/gdc.test/runnable/evalorder.d
index 8b56cc7af59..6805ee2e649 100644
--- a/gcc/testsuite/gdc.test/runnable/evalorder.d
+++ b/gcc/testsuite/gdc.test/runnable/evalorder.d
@@ -1,3 +1,9 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern(C) int printf(const char*, ...);
void test14040()
diff --git a/gcc/testsuite/gdc.test/runnable/extra-files/lib13742a.d b/gcc/testsuite/gdc.test/runnable/extra-files/lib13742a.d
new file mode 100644
index 00000000000..762559bc12c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/extra-files/lib13742a.d
@@ -0,0 +1,6 @@
+module lib13742a;
+
+void performLocked(alias PROC)()
+{
+ PROC();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/extra-files/lib13742b.d b/gcc/testsuite/gdc.test/runnable/extra-files/lib13742b.d
new file mode 100644
index 00000000000..03163b5504e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/extra-files/lib13742b.d
@@ -0,0 +1,8 @@
+module lib13742b;
+import lib13742a;
+
+void clear()
+{
+ void foo() {} // nested function
+ performLocked!foo; // template from other module (preceding on command line)
+}
diff --git a/gcc/testsuite/gdc.test/runnable/extra-files/minimal/object.d b/gcc/testsuite/gdc.test/runnable/extra-files/minimal/object.d
new file mode 100644
index 00000000000..bc8b3729739
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/extra-files/minimal/object.d
@@ -0,0 +1,8 @@
+module object;
+
+extern(C) void _Dmain();
+
+extern(C) void main()
+{
+ _Dmain();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/extra-files/moreBettercUnittests.d b/gcc/testsuite/gdc.test/runnable/extra-files/moreBettercUnittests.d
new file mode 100644
index 00000000000..1597a16d8db
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/extra-files/moreBettercUnittests.d
@@ -0,0 +1,11 @@
+__gshared uint sum;
+
+unittest
+{
+ sum |= 0x100;
+}
+
+unittest
+{
+ sum |= 0x1000;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/extra-files/test13742.d b/gcc/testsuite/gdc.test/runnable/extra-files/test13742.d
new file mode 100644
index 00000000000..3666bb24998
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/extra-files/test13742.d
@@ -0,0 +1,6 @@
+import lib13742b;
+
+void main()
+{
+ clear();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/extra-files/teststdio.txt b/gcc/testsuite/gdc.test/runnable/extra-files/teststdio.txt
deleted file mode 100644
index 9a5f6b1290d..00000000000
--- a/gcc/testsuite/gdc.test/runnable/extra-files/teststdio.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-asdfasdf
-a
-sdf
-asdf
-
-
diff --git a/gcc/testsuite/gdc.test/runnable/fix20466.d b/gcc/testsuite/gdc.test/runnable/fix20466.d
new file mode 100644
index 00000000000..afc8349d22d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/fix20466.d
@@ -0,0 +1,40 @@
+/* REQUIRED_ARGS: -O -fPIC
+ * DISABLED: win32 win64
+ */
+// https://issues.dlang.org/show_bug.cgi?id=20466
+
+extern (C++) final class Parameter
+{
+ ulong storageClass;
+ void* type;
+}
+
+extern (C++) final class IfStatement
+{
+ Parameter prm;
+}
+
+extern (C++) final class Visitor
+{
+ void visit(IfStatement s)
+ {
+ if (Parameter p = s.prm)
+ {
+ ulong stc = p.storageClass;
+ if (!p.type && !stc)
+ stc = 1L << 8;
+ assert(!(stc & (1L << 34)));
+ }
+ }
+}
+
+int main()
+{
+ auto p = new Parameter;
+ p.storageClass = 1L << 2;
+ auto s = new IfStatement;
+ s.prm = p;
+ auto v = new Visitor;
+ v.visit(s);
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/fix22115.d b/gcc/testsuite/gdc.test/runnable/fix22115.d
new file mode 100644
index 00000000000..2344bcc1a0c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/fix22115.d
@@ -0,0 +1,70 @@
+/* PERMUTE_ARGS: -O -inline
+ */
+// https://issues.dlang.org/show_bug.cgi?id=22115
+
+
+int sx;
+void sss() { ++sx; }
+
+static if (1)
+{
+ struct S { int a; }
+
+ void test1(S* s)
+ {
+ if (s.a == 3 ? s : null)
+ sss();
+ }
+}
+
+static if (1)
+{
+ extern (C++) class Exp
+ {
+ int a;
+
+ void func() { }
+ final inout(AddExp) isAddExp() inout { return a == 3 ? cast(typeof(return))this : null; }
+ }
+
+ extern (C++) class AddExp : Exp
+ {
+ }
+
+ void test2(Exp e)
+ {
+ if (e.isAddExp())
+ sss();
+ }
+}
+
+
+int main()
+{
+ static if (1)
+ {
+ S s;
+ s.a = 3;
+ test1(&s);
+ assert(sx == 1);
+ s.a = 2;
+ test1(&s);
+ assert(sx == 1);
+ }
+ sx = 1;
+
+ static if (1)
+ {
+ auto c = new AddExp();
+ c.a = 3;
+ test2(c);
+ assert(sx == 2);
+ auto ae = c.isAddExp();
+ assert(ae && ae.a == 3);
+ c.a = 2;
+ test2(c);
+ assert(sx == 2);
+ }
+
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/foreach.d b/gcc/testsuite/gdc.test/runnable/foreach.d
index 331c927504a..f0d1ea9ba9c 100644
--- a/gcc/testsuite/gdc.test/runnable/foreach.d
+++ b/gcc/testsuite/gdc.test/runnable/foreach.d
@@ -1,3 +1,22 @@
+/*
+RUN_OUTPUT:
+---
+u = 17
+u = 1
+u = 1
+u = 1
+u = 1
+a[0] = 21
+a[1] = 22
+a[2] = 23
+a[] = 21
+a[] = 22
+a[] = 23
+a = 63, b = 47, c = 83
+a = 63, b = 48, c = 83
+Success
+---
+*/
import core.stdc.stdio;
@@ -209,16 +228,26 @@ void test7()
a["foo"] = 3;
a["bar"] = 4;
+ bool sawBar, sawFoo;
foreach (string s, uint v; a)
{
- printf("a[%.*s] = %d\n", s.length, s.ptr, v);
if (s == "bar")
+ {
assert(v == 4);
+ assert(!sawBar);
+ sawBar = true;
+ }
else if (s == "foo")
+ {
assert(v == 3);
+ assert(!sawFoo);
+ sawFoo = true;
+ }
else
assert(0);
}
+ assert(sawBar);
+ assert(sawFoo);
}
diff --git a/gcc/testsuite/gdc.test/runnable/foreach2.d b/gcc/testsuite/gdc.test/runnable/foreach2.d
index 34edb41edaf..b30d1519704 100644
--- a/gcc/testsuite/gdc.test/runnable/foreach2.d
+++ b/gcc/testsuite/gdc.test/runnable/foreach2.d
@@ -1,3 +1,9 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
import core.stdc.stdio;
diff --git a/gcc/testsuite/gdc.test/runnable/foreach3.d b/gcc/testsuite/gdc.test/runnable/foreach3.d
index e167217e7a3..3c9c288b9e4 100644
--- a/gcc/testsuite/gdc.test/runnable/foreach3.d
+++ b/gcc/testsuite/gdc.test/runnable/foreach3.d
@@ -1,9 +1,15 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
import core.stdc.stdio;
struct Foo
{
- uint array[2];
+ uint[2] array;
int opApply(int delegate(ref uint) dg)
{
diff --git a/gcc/testsuite/gdc.test/runnable/foreach4.d b/gcc/testsuite/gdc.test/runnable/foreach4.d
index 8c9d4218d6e..f2e19439647 100644
--- a/gcc/testsuite/gdc.test/runnable/foreach4.d
+++ b/gcc/testsuite/gdc.test/runnable/foreach4.d
@@ -1,6 +1,4 @@
-// RUNNABLE_PHOBOS_TEST
import core.stdc.stdio;
-import std.stdio;
alias bool bit;
@@ -8,7 +6,7 @@ alias bool bit;
class Foo
{
- uint array[2];
+ uint[2] array;
int opApply(int delegate(ref uint) dg)
{
@@ -363,7 +361,7 @@ void test12()
j = 0;
foreach (size_t i, dchar d; "hello")
{
- printf("i = %d, d = x%x\n", i, d);
+ printf("i = %d, d = x%x\n", cast(int)i, d);
if (j == 0) assert(d == 'h');
if (j == 1) assert(d == 'e');
if (j == 2) assert(d == 'l');
@@ -397,7 +395,7 @@ void test13()
j = 0;
foreach (size_t i, wchar d; "hello")
{
- printf("i = %d, d = x%x\n", i, d);
+ printf("i = %d, d = x%x\n", cast(int)i, d);
if (j == 0) assert(d == 'h');
if (j == 1) assert(d == 'e');
if (j == 2) assert(d == 'l');
@@ -431,7 +429,7 @@ void test14()
j = 0;
foreach (size_t i, char d; cast(wstring)"hello")
{
- printf("i = %d, d = x%x\n", i, d);
+ printf("i = %d, d = x%x\n", cast(int)i, d);
if (j == 0) assert(d == 'h');
if (j == 1) assert(d == 'e');
if (j == 2) assert(d == 'l');
@@ -465,7 +463,7 @@ void test15()
j = 0;
foreach (size_t i, dchar d; cast(wstring)"hello")
{
- printf("i = %d, d = x%x\n", i, d);
+ printf("i = %d, d = x%x\n", cast(int)i, d);
if (j == 0) assert(d == 'h');
if (j == 1) assert(d == 'e');
if (j == 2) assert(d == 'l');
@@ -499,7 +497,7 @@ void test16()
j = 0;
foreach (size_t i, char d; cast(dstring)"hello")
{
- printf("i = %d, d = x%x\n", i, d);
+ printf("i = %d, d = x%x\n", cast(int)i, d);
if (j == 0) assert(d == 'h');
if (j == 1) assert(d == 'e');
if (j == 2) assert(d == 'l');
@@ -533,7 +531,7 @@ void test17()
j = 0;
foreach (size_t i, wchar d; cast(dstring)"hello")
{
- printf("i = %d, d = x%x\n", i, d);
+ printf("i = %d, d = x%x\n", cast(int)i, d);
if (j == 0) assert(d == 'h');
if (j == 1) assert(d == 'e');
if (j == 2) assert(d == 'l');
@@ -577,7 +575,7 @@ void test18()
void test19()
{
- string string = x"F0 9D 83 93";
+ string string = "\xF0\x9D\x83\x93";
int count=0;
dchar tmp;
@@ -615,18 +613,18 @@ void test20()
void foo21(string[] args)
{
- printf("args.length = %d\n", args.length);
+ printf("args.length = %d\n", cast(int)args.length);
assert(args.length == 3);
foreach (i, arg; args)
{
assert(typeid(typeof(i)) == typeid(size_t));
assert(typeid(typeof(arg)) == typeid(string));
- writefln("args[%d] = '%s'", i, arg);
+ printf("args[%d] = '%.*s'\n", cast(int)i, cast(int)arg.length, arg.ptr);
}
foreach (arg; args)
{
assert(typeid(typeof(arg)) == typeid(string));
- writefln("args[] = '%s'", arg);
+ printf("args[] = '%.*s'\n", cast(int)arg.length, arg.ptr);
}
}
@@ -654,24 +652,24 @@ void test22()
{
assert(typeid(typeof(key)) == typeid(string));
assert(typeid(typeof(value)) == typeid(int));
- writefln("map[%s] = %s", key, value);
+ printf("map[%.*s] = %d\n", cast(int)key.length, key.ptr, value);
}
foreach (key, int value; map)
{
assert(typeid(typeof(key)) == typeid(string));
assert(typeid(typeof(value)) == typeid(int));
- writefln("map[%s] = %s", key, value);
+ printf("map[%.*s] = %d\n", cast(int)key.length, key.ptr, value);
}
foreach (string key, value; map)
{
assert(typeid(typeof(key)) == typeid(string));
assert(typeid(typeof(value)) == typeid(int));
- writefln("map[%s] = %s", key, value);
+ printf("map[%.*s] = %d\n", cast(int)key.length, key.ptr, value);
}
foreach (value; map)
{
assert(typeid(typeof(value)) == typeid(int));
- writefln("map[] = %s", value);
+ printf("map[] = %d\n", value);
}
}
@@ -679,7 +677,7 @@ void test22()
class Foo23
{
- int array[2];
+ int[2] array;
int opApply(int delegate(ref int) dg)
{
@@ -719,7 +717,7 @@ void test23()
assert(typeid(typeof(u)) == typeid(int));
i++;
u++;
- //writefln("u = %d", u);
+ //printf("u = %d\n", u);
assert((i == 1) ? u == 74 : u == 83);
}
assert(i == 2);
@@ -732,7 +730,7 @@ void test23()
assert(typeid(typeof(u)) == typeid(int));
i++;
u++;
- writefln("u = %d", u);
+ //printf("u = %d\n", u);
assert((i == 3) ? u == 74 : u == 83);
assert(j == i - 3);
}
@@ -777,7 +775,6 @@ void test25()
{
foreach (string s; aarray)
{
- writeln(s);
assert(s == "b");
}
};
@@ -790,7 +787,7 @@ void test25()
struct Foo26
{
- uint array[2];
+ uint[2] array;
int forward(int delegate(ref uint) dg)
{
@@ -833,7 +830,7 @@ void test26()
foreach (u; &a.forward)
{
- writeln(u);
+ printf("%d\n", u);
i++;
u++;
}
@@ -843,7 +840,7 @@ void test26()
foreach (uint u; &a.reverse)
{
- writeln(u);
+ printf("%d\n", u);
}
}
diff --git a/gcc/testsuite/gdc.test/runnable/foreach5.d b/gcc/testsuite/gdc.test/runnable/foreach5.d
index 003a34a8e6d..59b88ec88cb 100644
--- a/gcc/testsuite/gdc.test/runnable/foreach5.d
+++ b/gcc/testsuite/gdc.test/runnable/foreach5.d
@@ -1,4 +1,5 @@
/*
+EXTRA_FILES: imports/test15777a.d imports/test15777b.d
TEST_OUTPUT:
---
int
@@ -14,6 +15,8 @@ test7406()
extern(C) int printf(const char* fmt, ...);
+alias AliasSeq(X...) = X;
+
/***************************************/
void test1()
@@ -43,7 +46,7 @@ void test1()
}
/***************************************/
-// 2411
+// https://issues.dlang.org/show_bug.cgi?id=2411
struct S2411
{
@@ -68,7 +71,7 @@ void test2411()
}
/***************************************/
-// 2442
+// https://issues.dlang.org/show_bug.cgi?id=2442
template canForeach(T, E)
{
@@ -148,7 +151,7 @@ void test2442()
}
/***************************************/
-// 2443
+// https://issues.dlang.org/show_bug.cgi?id=2443
struct S2443
{
@@ -175,7 +178,7 @@ void test2443()
}
/***************************************/
-// 3187
+// https://issues.dlang.org/show_bug.cgi?id=3187
class Collection
{
@@ -200,7 +203,7 @@ void test3187()
}
/***************************************/
-// 4090
+// https://issues.dlang.org/show_bug.cgi?id=4090
void test4090a()
{
@@ -244,7 +247,7 @@ void test4090b()
}
/***************************************/
-// 5605
+// https://issues.dlang.org/show_bug.cgi?id=5605
struct MyRange
{
@@ -255,7 +258,7 @@ struct MyRange
return true;
}
- @property ref int front()
+ @property ref int front() return
{
return theOnlyOne;
}
@@ -282,7 +285,7 @@ void test5605()
}
/***************************************/
-// 7004
+// https://issues.dlang.org/show_bug.cgi?id=7004
void func7004(A...)(A args)
{
@@ -296,7 +299,7 @@ void test7004()
}
/***************************************/
-// 7406
+// https://issues.dlang.org/show_bug.cgi?id=7406
template TypeTuple7406(T...)
{
@@ -327,7 +330,7 @@ void test7406()
}
/***************************************/
-// 6659
+// https://issues.dlang.org/show_bug.cgi?id=6659
void test6659()
{
@@ -417,7 +420,7 @@ void test6659c()
/***************************************/
-// 10221
+// https://issues.dlang.org/show_bug.cgi?id=10221
void test10221()
{
@@ -446,7 +449,7 @@ void test10221()
}
/***************************************/
-// 7814
+// https://issues.dlang.org/show_bug.cgi?id=7814
struct File7814
{
@@ -475,7 +478,7 @@ void test7814()
}
/***************************************/
-// 10049
+// https://issues.dlang.org/show_bug.cgi?id=10049
struct ByLine10049
{
@@ -515,7 +518,7 @@ void test11955()
}
/******************************************/
-// 6652
+// https://issues.dlang.org/show_bug.cgi?id=6652
void test6652()
{
@@ -616,7 +619,7 @@ void test6652()
}
/***************************************/
-// 8595
+// https://issues.dlang.org/show_bug.cgi?id=8595
struct OpApply8595
{
@@ -636,7 +639,7 @@ string test8595()
}
/***************************************/
-// 9068
+// https://issues.dlang.org/show_bug.cgi?id=9068
struct Foo9068
{
@@ -723,7 +726,7 @@ loop_with_dtors:
}
/***************************************/
-// 11885
+// https://issues.dlang.org/show_bug.cgi?id=11885
struct Foo11885
{
@@ -810,7 +813,7 @@ loop_with_dtors:
}
/***************************************/
-// 10475
+// https://issues.dlang.org/show_bug.cgi?id=10475
void test10475a()
{
@@ -888,7 +891,7 @@ void test10475b()
}
/***************************************/
-// 11291
+// https://issues.dlang.org/show_bug.cgi?id=11291
void test11291()
{
@@ -917,7 +920,7 @@ void test11291()
}
/***************************************/
-// 12103
+// https://issues.dlang.org/show_bug.cgi?id=12103
alias TypeTuple12103(TL...) = TL;
@@ -945,7 +948,7 @@ void test12103()
}
/***************************************/
-// 12739
+// https://issues.dlang.org/show_bug.cgi?id=12739
struct S12739
{
@@ -963,7 +966,7 @@ void test12739() nothrow
}
/***************************************/
-// 12932
+// https://issues.dlang.org/show_bug.cgi?id=12932
void test12932() @nogc
{
@@ -976,7 +979,7 @@ void test12932() @nogc
}
/***************************************/
-// 13756
+// https://issues.dlang.org/show_bug.cgi?id=13756
void test13756()
{
@@ -1046,7 +1049,7 @@ void test13756()
}
/***************************************/
-// 14653
+// https://issues.dlang.org/show_bug.cgi?id=14653
static string result14653;
@@ -1098,6 +1101,81 @@ void test14653()
}
/***************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15777
+
+template funA15777()
+{
+ import imports.test15777a;
+ alias funA15777 = fun;
+}
+
+template funB15777()
+{
+ import imports.test15777b;
+ alias funB15777 = fun;
+}
+
+template funAB15777()
+{
+ import imports.test15777a;
+ import imports.test15777b;
+ alias funAB15777 = fun;
+}
+
+void foo15777(alias tpl)()
+{
+ alias seq = AliasSeq!(tpl!());
+ // Make alias of 'overload set' in tuple elements
+ static assert(seq.length == 1);
+ foreach (i, n; seq)
+ {
+ static assert(__traits(identifier, seq[i]) == "fun");
+ }
+}
+
+void test15777()
+{
+ foo15777!funA15777;
+ foo15777!funB15777;
+ foo15777!funAB15777;
+}
+
+/***************************************/
+// https://issues.dlang.org/show_bug.cgi?id=17041
+
+auto ref int[2] foo17041(A...)(auto ref A args)
+{
+ foreach(a; args)
+ {
+ a = [12, 22];
+ }
+ foreach(ref a; args)
+ {
+ a = [31, 41];
+ return args[0];
+ }
+}
+
+void test17041()
+{
+ int[2] x = [10, 20];
+ foreach(a; AliasSeq!(x))
+ {
+ a = [11, 21];
+ }
+ assert(x == [10, 20]); // test by value
+ foreach(ref a; AliasSeq!(x))
+ {
+ a = [30, 40];
+ }
+ assert(x == [30, 40]); // test by ref value
+
+ assert(foo17041(x) == [31, 41]); // test lvalue
+ assert(x == [31, 41]);
+ assert(foo17041(cast(int[2]) [10, 20]) == [31, 41]); // test rvalue
+}
+
+/***************************************/
int main()
{
@@ -1125,10 +1203,10 @@ int main()
test11291();
test12103();
test12739();
- printf("test12932()\n");
test12932();
test13756();
test14653();
+ test17041();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/funclit.d b/gcc/testsuite/gdc.test/runnable/funclit.d
index c299badec15..25b4e6e512c 100644
--- a/gcc/testsuite/gdc.test/runnable/funclit.d
+++ b/gcc/testsuite/gdc.test/runnable/funclit.d
@@ -176,7 +176,7 @@ void test4()
// function/delegate inference + overload resolution
assert(nbaz4({ }) == 1);
assert(nbaz4({ v = 1; }) == 2);
- assert(nbaz4({ sfoo(); }) == 1); // Bugzilla 8836
+ assert(nbaz4({ sfoo(); }) == 1); // https://issues.dlang.org/show_bug.cgi?id=8836
assert(nbaz4({ nfoo(); }) == 2);
assert(tbaz4({ }) == 1);
@@ -354,7 +354,7 @@ void test11()
}
/***************************************************/
-// 3235
+// https://issues.dlang.org/show_bug.cgi?id=3235
void test3235()
{
@@ -365,7 +365,7 @@ void test3235()
}
/***************************************************/
-// 6714
+// https://issues.dlang.org/show_bug.cgi?id=6714
void foo6714x(int function (int, int) a){}
void bar6714x(int delegate (int, int) a){}
@@ -384,7 +384,7 @@ void test6714()
}
/***************************************************/
-// 7193
+// https://issues.dlang.org/show_bug.cgi?id=7193
void test7193()
{
@@ -394,7 +394,8 @@ void test7193()
}
/***************************************************/
-// 7207 : on CastExp
+// https://issues.dlang.org/show_bug.cgi?id=7207
+// on CastExp
void test7202()
{
@@ -403,7 +404,7 @@ void test7202()
}
/***************************************************/
-// 7288
+// https://issues.dlang.org/show_bug.cgi?id=7288
void test7288()
{
@@ -411,7 +412,7 @@ void test7288()
auto foo()
{
int x;
- return () => { return x; };
+ return () { return () => x; };
}
pragma(msg, typeof(&foo));
alias int delegate() pure nothrow @nogc @safe delegate() pure nothrow @nogc @safe delegate() pure nothrow @safe Dg;
@@ -420,7 +421,7 @@ void test7288()
}
/***************************************************/
-// 7499
+// https://issues.dlang.org/show_bug.cgi?id=7499
void test7499()
{
@@ -431,7 +432,7 @@ void test7499()
}
/***************************************************/
-// 7500
+// https://issues.dlang.org/show_bug.cgi?id=7500
void test7500()
{
@@ -440,7 +441,7 @@ void test7500()
}
/***************************************************/
-// 7525
+// https://issues.dlang.org/show_bug.cgi?id=7525
void test7525()
{
@@ -462,7 +463,7 @@ void test7525()
}
/***************************************************/
-// 7582
+// https://issues.dlang.org/show_bug.cgi?id=7582
void test7582()
{
@@ -474,7 +475,7 @@ void test7582()
}
/***************************************************/
-// 7649
+// https://issues.dlang.org/show_bug.cgi?id=7649
void test7649()
{
@@ -486,7 +487,7 @@ void test7649()
}
/***************************************************/
-// 7650
+// https://issues.dlang.org/show_bug.cgi?id=7650
void test7650()
{
@@ -513,7 +514,7 @@ void test7650()
}
/***************************************************/
-// 7705
+// https://issues.dlang.org/show_bug.cgi?id=7705
void test7705()
{
@@ -529,7 +530,7 @@ void test7705()
}
/***************************************************/
-// 7713
+// https://issues.dlang.org/show_bug.cgi?id=7713
void foo7713(T)(T delegate(in Object) dlg)
{}
@@ -539,7 +540,7 @@ void test7713()
}
/***************************************************/
-// 7743
+// https://issues.dlang.org/show_bug.cgi?id=7743
auto foo7743a()
{
@@ -564,7 +565,7 @@ void test7743()
}
/***************************************************/
-// 7761
+// https://issues.dlang.org/show_bug.cgi?id=7761
enum dg7761 = (int a) pure => 2 * a;
@@ -575,7 +576,7 @@ void test7761()
}
/***************************************************/
-// 7941
+// https://issues.dlang.org/show_bug.cgi?id=7941
void test7941()
{
@@ -583,7 +584,7 @@ void test7941()
}
/***************************************************/
-// 8005
+// https://issues.dlang.org/show_bug.cgi?id=8005
void test8005()
{
@@ -615,7 +616,7 @@ void test8198()
}
/***************************************************/
-// 8226
+// https://issues.dlang.org/show_bug.cgi?id=8226
immutable f8226 = (int x) => x * 2;
@@ -625,7 +626,7 @@ void test8226()
}
/***************************************************/
-// 8241
+// https://issues.dlang.org/show_bug.cgi?id=8241
auto exec8241a(alias a = function(x) => x, T...)(T as)
{
@@ -644,7 +645,7 @@ void test8241()
}
/***************************************************/
-// 8242
+// https://issues.dlang.org/show_bug.cgi?id=8242
template exec8242(alias a, T...)
{
@@ -664,7 +665,7 @@ void test8242()
}
/***************************************************/
-// 8315
+// https://issues.dlang.org/show_bug.cgi?id=8315
void test8315()
{
@@ -677,7 +678,7 @@ if (is(typeof(pred(1)) == bool))
{}
/***************************************************/
-// 8397
+// https://issues.dlang.org/show_bug.cgi?id=8397
void test8397()
{
@@ -688,7 +689,7 @@ void test8397()
}
/***************************************************/
-// 8496
+// https://issues.dlang.org/show_bug.cgi?id=8496
void test8496()
{
@@ -700,7 +701,7 @@ void test8496()
}
/***************************************************/
-// 8575
+// https://issues.dlang.org/show_bug.cgi?id=8575
template tfunc8575(func...)
{
@@ -720,7 +721,7 @@ void test8575()
}
/***************************************************/
-// 9153
+// https://issues.dlang.org/show_bug.cgi?id=9153
void writeln9153(string s){}
@@ -737,7 +738,7 @@ void test9153()
}
/***************************************************/
-// 9393
+// https://issues.dlang.org/show_bug.cgi?id=9393
template ifThrown9393a(E)
{
@@ -761,7 +762,7 @@ void test9393()
}
/***************************************************/
-// 9415
+// https://issues.dlang.org/show_bug.cgi?id=9415
void test9415()
{
@@ -771,7 +772,7 @@ void test9415()
}
/***************************************************/
-// 9628
+// https://issues.dlang.org/show_bug.cgi?id=9628
template TypeTuple9628(TL...) { alias TypeTuple9628 = TL; }
void map9628(alias func)() { func(0); }
@@ -805,7 +806,7 @@ void test9628()
}
/***************************************************/
-// 9928
+// https://issues.dlang.org/show_bug.cgi?id=9928
void test9928()
{
@@ -813,7 +814,7 @@ void test9928()
}
/***************************************************/
-// 10133
+// https://issues.dlang.org/show_bug.cgi?id=10133
ptrdiff_t countUntil10133(alias pred, R)(R haystack)
{
@@ -855,7 +856,7 @@ void test10133()
}
/***************************************************/
-// 10219
+// https://issues.dlang.org/show_bug.cgi?id=10219
void test10219()
{
@@ -904,7 +905,7 @@ void test10219()
}
/***************************************************/
-// 10288
+// https://issues.dlang.org/show_bug.cgi?id=10288
T foo10288(T)(T x)
{
@@ -934,7 +935,7 @@ void test10288() @safe pure nothrow
}
/***************************************************/
-// 10666
+// https://issues.dlang.org/show_bug.cgi?id=10666
struct S10666
{
@@ -954,7 +955,7 @@ void foo10666(S10666 s1)
}
/***************************************************/
-// 11081
+// https://issues.dlang.org/show_bug.cgi?id=11081
T ifThrown11081(E : Throwable, T)(T delegate(E) errorHandler)
{
@@ -972,7 +973,7 @@ void test11081()
}
/***************************************************/
-// 11220
+// https://issues.dlang.org/show_bug.cgi?id=11220
int parsePrimaryExp11220(int x)
{
@@ -986,7 +987,7 @@ typeof(handler(1)) parseAmbig11220(alias handler)()
}
/***************************************************/
-// 11230
+// https://issues.dlang.org/show_bug.cgi?id=11230
template map11230(fun...)
{
@@ -1022,7 +1023,7 @@ C11230 visit11230()
}
/***************************************************/
-// 10336
+// https://issues.dlang.org/show_bug.cgi?id=10336
struct S10336
{
@@ -1041,7 +1042,7 @@ void test10336()
}
/***************************************************/
-// 10928
+// https://issues.dlang.org/show_bug.cgi?id=10928
struct D10928
{
@@ -1065,7 +1066,7 @@ void test10928()
}
/***************************************************/
-// 11661
+// https://issues.dlang.org/show_bug.cgi?id=11661
void test11661()
{
@@ -1074,7 +1075,7 @@ void test11661()
}
/***************************************************/
-// 11774
+// https://issues.dlang.org/show_bug.cgi?id=11774
void f11774(T, R)(R delegate(T[]) dg)
{
@@ -1091,7 +1092,7 @@ void test11774()
}
/***************************************************/
-// 12421
+// https://issues.dlang.org/show_bug.cgi?id=12421
void test12421()
{
@@ -1145,7 +1146,7 @@ void test12421()
}
/***************************************************/
-// 12508
+// https://issues.dlang.org/show_bug.cgi?id=12508
interface A12508(T)
{
@@ -1182,7 +1183,7 @@ void test12508()
}
/***************************************************/
-// 13879
+// https://issues.dlang.org/show_bug.cgi?id=13879
void test13879()
{
@@ -1194,7 +1195,7 @@ void test13879()
}
/***************************************************/
-// 14745
+// https://issues.dlang.org/show_bug.cgi?id=14745
void test14745()
{
@@ -1216,7 +1217,7 @@ void test14745()
}
/***************************************************/
-// 15794
+// https://issues.dlang.org/show_bug.cgi?id=15794
struct Foo15794
{
diff --git a/gcc/testsuite/gdc.test/runnable/functype.d b/gcc/testsuite/gdc.test/runnable/functype.d
index 7aa7efdb507..28e2709ffc1 100644
--- a/gcc/testsuite/gdc.test/runnable/functype.d
+++ b/gcc/testsuite/gdc.test/runnable/functype.d
@@ -183,7 +183,7 @@ void testxx()
}
/***************************************************/
-// 3646
+// https://issues.dlang.org/show_bug.cgi?id=3646
int bar3646(int x = 10) { printf("bar %d\n", x); return x; }
int bam3646(int y) { printf("bam %d\n", y); return y; }
@@ -215,7 +215,7 @@ void test3646()
}
/***************************************************/
-// 3866
+// https://issues.dlang.org/show_bug.cgi?id=3866
void test3866()
{
@@ -230,7 +230,7 @@ void test3866()
}
/***************************************************/
-// 8579
+// https://issues.dlang.org/show_bug.cgi?id=8579
void test8579()
{
@@ -254,7 +254,7 @@ void test8579()
}
/***************************************************/
-// 14210
+// https://issues.dlang.org/show_bug.cgi?id=14210
string foo14210a(DT)(string name, DT dg)
{
@@ -274,7 +274,7 @@ void test14210()
}
/***************************************************/
-// 10734
+// https://issues.dlang.org/show_bug.cgi?id=10734
// There's no platform independent export symbol, so
// test just only in Win32.
@@ -297,7 +297,7 @@ void test10734()
}
/***************************************************/
-// 14656
+// https://issues.dlang.org/show_bug.cgi?id=14656
void test14656()
{
diff --git a/gcc/testsuite/gdc.test/runnable/hello.d b/gcc/testsuite/gdc.test/runnable/hello.d
index 88adaa4234b..b46f59dcd13 100644
--- a/gcc/testsuite/gdc.test/runnable/hello.d
+++ b/gcc/testsuite/gdc.test/runnable/hello.d
@@ -5,8 +5,8 @@ extern(C) int printf(const char*, ...);
int main(char[][] args)
{
printf("hello world\n");
- printf("args.length = %d\n", args.length);
+ printf("args.length = %zd\n", args.length);
for (int i = 0; i < args.length; i++)
- printf("args[%d] = '%.*s'\n", i, args[i].length, args[i].ptr);
+ printf("args[%d] = '%.*s'\n", i, cast(int)args[i].length, args[i].ptr);
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/helloUTF8.d b/gcc/testsuite/gdc.test/runnable/helloUTF8.d
index 78f3bcdb3dd..aed66134f09 100644
--- a/gcc/testsuite/gdc.test/runnable/helloUTF8.d
+++ b/gcc/testsuite/gdc.test/runnable/helloUTF8.d
@@ -1,4 +1,10 @@
-// PERMUTE_ARGS:
+/*
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+hello world
+---
+*/
extern(C) int printf(const char *, ...);
diff --git a/gcc/testsuite/gdc.test/runnable/ice15030.d b/gcc/testsuite/gdc.test/runnable/ice15030.d
index 5925e745ecc..5c9587ca9f6 100644
--- a/gcc/testsuite/gdc.test/runnable/ice15030.d
+++ b/gcc/testsuite/gdc.test/runnable/ice15030.d
@@ -1,6 +1,7 @@
// REQUIRED_ARGS: -unittest -boundscheck=off
// PERMUTE_ARGS:
// EXTRA_SOURCES: imports/a15030.d imports/b15030.d
+// EXTRA_FILES: imports/std15030algo.d
void main() {}
diff --git a/gcc/testsuite/gdc.test/runnable/ice21696.d b/gcc/testsuite/gdc.test/runnable/ice21696.d
new file mode 100644
index 00000000000..8b7a81ebd5c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/ice21696.d
@@ -0,0 +1,23 @@
+module ice21696;
+
+// https://issues.dlang.org/show_bug.cgi?id=21696
+
+double[1][1] func(double[1][1] stuff = [[1.0]])
+{
+ bool myFunc()
+ {
+ if (stuff[])
+ return true;
+ return false;
+ }
+
+ if (!myFunc())
+ assert(false);
+
+ return stuff;
+}
+
+int main()
+{
+ return func() != [[1.0]];
+}
diff --git a/gcc/testsuite/gdc.test/runnable/ice21727.d b/gcc/testsuite/gdc.test/runnable/ice21727.d
new file mode 100644
index 00000000000..5b5745f9df0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/ice21727.d
@@ -0,0 +1,46 @@
+// REQUIRED_ARGS: -m64 -O -inline
+// DISABLED: win32 linux32 freebsd32 osx32 netbsd32 dragonflybsd32
+// https://issues.dlang.org/show_bug.cgi?id=21727
+
+import core.simd;
+
+@nogc nothrow pure @safe:
+
+struct Float4
+{
+ float4 mVector;
+
+ pragma(inline, false) ref typeof(this) doubleInPlace() return
+ @nogc nothrow pure @safe
+ {
+ mVector = mVector + mVector;
+ return this;
+ }
+}
+
+pragma(inline, false) Float4 identity(Float4 a)
+{
+ return a;
+}
+
+pragma(inline, true) Float4 twoTimes(const ref Float4 a)
+{
+ version (D_SIMD)
+ return Float4(cast(float4) __simd(XMM.ADDPS, a.mVector, a.mVector));
+ else // Allow non-DMD compilers to compile this test.
+ return Float4(a.mVector + a.mVector);
+}
+
+pragma(inline, false) Float4 fourTimes(const Float4 a)
+{
+ auto x = identity(a);
+ auto y = x.doubleInPlace(); // This crashed in dmd.backend.cgxmm.xmmload.
+ auto z = twoTimes(y);
+ return z;
+}
+
+void main()
+{
+ const c = fourTimes(Float4([5,7,11,13]));
+ assert(c.mVector.array == [20, 28, 44, 52]);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/ifti.d b/gcc/testsuite/gdc.test/runnable/ifti.d
index 6d669753123..0c94946c8b3 100644
--- a/gcc/testsuite/gdc.test/runnable/ifti.d
+++ b/gcc/testsuite/gdc.test/runnable/ifti.d
@@ -1,27 +1,26 @@
-// RUNNABLE_PHOBOS_TEST
-import std.stdio;
+extern (C) int printf(const scope char*, ...);
struct S {
int x = 3;
- void fun(T)(T x) { writefln("S.fun(%s)(%s)",typeid(T),x); this.x += x; }
+ void fun(T)(T x) { printf("S.fun(%s)(%d)\n", T.stringof.ptr, x); this.x += x; }
}
class Tst(TST, int v = 2) {
int x = 3;
int z = 4;
- final private void proc(int x) { writefln("proc(%s) -> %s",x,this.x); }
- void fun(T)(T x) { writefln("fun(%s)(%s) -> %s",typeid(T),x,this.x);}
- void fun()() { writefln("fun()() -> %s",this.x); }
- void fun2()() { writefln("fun2"); }
+ final private void proc(int x) { printf("proc(%d) -> %d\n", x, this.x); }
+ void fun(T)(T x) { printf("fun(%s)(%d) -> %d\n", T.stringof.ptr, x, this.x);}
+ void fun()() { printf("fun()() -> %d\n", this.x); }
+ void fun2()() { printf("fun2\n"); }
class Inner {
int y = 99;
Tst outer;
void f3() { z = 55; }
// Make sure the correct this-ptr is used
- void f1() { writefln("Inner.f1"); proc(-11); outer.proc(-11); }
- void f2() { writefln("Inner.f2"); fun(-17); outer.fun(-17); }
+ void f1() { printf("Inner.f1\n"); proc(-11); outer.proc(-11); }
+ void f2() { printf("Inner.f2\n"); fun(-17); outer.fun(-17); }
}
Inner inner;
@@ -30,38 +29,36 @@ class Tst(TST, int v = 2) {
inner.outer = this;
}
- void callInnerf1() { writefln("callInnerf1"); inner.f1(); }
- void callInnerf2() { writefln("callInnerf2"); inner.f2(); }
+ void callInnerf1() { printf("callInnerf1\n"); inner.f1(); }
+ void callInnerf2() { printf("callInnerf2\n"); inner.f2(); }
//
- void opAdd(T)(T x) { this.x += x; writefln("opAdd(%s)",x); }
- void opPos()() { writefln("opPos()"); }
- //void opPos() { writefln("xxx"); }
- void opIndex(T)(T x) { writefln("opIndex[%s]",x); }
+ void opBinary(string op : "+", T)(T x) { this.x += x; printf("opAdd(%d)\n", x); }
+ void opUnary(string op : "+")() { printf("opPos()\n"); }
+ //void opPos() { printf("xxx"); }
+ void opIndex(T)(T x) { printf("opIndex[%d]\n",x); }
void opIndex(A,B,C)(A a, B b, C c) {
- writefln("opIndex[(%s)%s,(%s)%s,(%s)%s]",typeid(A),a,
- typeid(B),b,typeid(C),c);
+ printf("opIndex[(%s) %d, (%s) %d, (%s) %d]\n", A.stringof.ptr, a,
+ B.stringof.ptr,b,C.stringof.ptr,c);
}
-
static if (v > 1) {
- void opCall(A = int, B = float)(A a = 1, B b = 8.2) { writefln("opCall(%s,%s)",a,b); this.x++; }
-
+ void opCall(A = int, B = float)(A a = 1, B b = 8.2) { printf("opCall(%d, %d)\n",a,b); this.x++; }
}
- void opSlice(A,B)(A a, B b) { writefln("opSlice(%s,%s)",a,b); }
- void opSlice()() { writefln("opSlice()"); }
+ void opSlice(A,B)(A a, B b) { printf("opSlice(%d, %d)\n",a,b); }
+ void opSlice()() { printf("opSlice()\n"); }
void opIndexAssign(A,B)(A a, B b) {
- writefln("opIndexAssign((%s)%s,(%s)%s)",typeid(A),a,typeid(B),b);
+ printf("opIndexAssign((%s) %d, (%s) %d)\n", A.stringof.ptr, a, B.stringof.ptr, b);
}
void opSliceAssign(A,B,C)(A a, B b, C c) {
- writefln("opSliceAssign(%s,%s,%s)",a,b,c);
+ printf("opSliceAssign(%.s, %d, %d)\n", a.length, a.ptr, b, c);
}
- bool opEquals(A)(A x) { writefln("opEquals((%s))",typeid(A));return true; }
+ bool opEquals(A)(A x) { printf("opEquals((%s))\n", A.stringof.ptr); return true; }
int opApply(T)(int delegate(ref T)dg) {
for (int i = 0; i < 5; i++) {
@@ -103,8 +100,6 @@ void main() {
t[];
t[1..2];
u[1..2.5];
- t[1i] = 5;
- t[-4.5..7i] = "hello";
t == t;
auto b = t != t; // without assignment -> "! has no effect in expression"
t == u;
@@ -112,10 +107,10 @@ void main() {
u == u;
b = u != u;
foreach(int i;t) {
- writefln("%s",i);
+ printf("%d\n", i);
}
foreach(double i;t) {
- writefln("%s",i);
+ printf("%g\n", i);
}
}
diff --git a/gcc/testsuite/gdc.test/runnable/implicit.d b/gcc/testsuite/gdc.test/runnable/implicit.d
index 9170b04adb0..75b992c5c1c 100644
--- a/gcc/testsuite/gdc.test/runnable/implicit.d
+++ b/gcc/testsuite/gdc.test/runnable/implicit.d
@@ -1,5 +1,17 @@
-// RUNNABLE_PHOBOS_TEST
-import std.stdio;
+/*
+TEST_OUTPUT:
+---
+runnable/implicit.d(162): Deprecation: slice of static array temporary returned by `pureMaker3c()` assigned to longer lived variable `z1`
+runnable/implicit.d(163): Deprecation: slice of static array temporary returned by `pureMaker3c()` assigned to longer lived variable `z2`
+---
+
+RUN_OUTPUT:
+---
+Success
+---
+*/
+
+import core.stdc.stdio;
/***********************************/
@@ -150,7 +162,8 @@ void testDIP29_3()
immutable z1 = pureMaker3c()[];
immutable z2 = pureMaker3c()[0..2];
- // Issue 12467 - conversion from lvalue of mutable static array to immutable slice
+ // https://issues.dlang.org/show_bug.cgi?id=12467
+ // conversion from lvalue of mutable static array to immutable slice
char[3] arr = "foo";
static assert(!__traits(compiles, { string str = arr[]; }));
}
@@ -182,11 +195,11 @@ void testDIP29_4()
}
/***********************************/
-// 14155
+// https://issues.dlang.org/show_bug.cgi?id=14155
immutable int g14155;
-static this() { g14155 = 1; }
+shared static this() { g14155 = 1; }
int* make14155m ( int* p) pure { return null; }
const(int*) make14155c ( const(int*) p) pure { return &g14155; }
@@ -232,7 +245,7 @@ void test14155_for_testDIP29_4()
}
/***********************************/
-// 14141
+// https://issues.dlang.org/show_bug.cgi?id=14141
struct S14141
{
@@ -265,7 +278,7 @@ int*[] pureFoo() pure { return null; }
void testDIP29_5() pure
{
- { char[] s; immutable x = s.dup; }
+ { char[] s; immutable x = s.idup; }
{ immutable x = (cast(int*[])null).dup; }
{ immutable x = pureFoo(); }
{ immutable x = pureFoo().dup; }
@@ -396,7 +409,7 @@ void testDIP29_6()
}));
}
-// 14155
+// https://issues.dlang.org/show_bug.cgi?id=14155
void test14155_for_testDIP29_6()
{
@@ -417,7 +430,7 @@ void test14155_for_testDIP29_6()
}
/***********************************/
-// 13640
+// https://issues.dlang.org/show_bug.cgi?id=13640
struct S13640
{
@@ -437,7 +450,7 @@ struct S13640
}
/***********************************/
-// 15778
+// https://issues.dlang.org/show_bug.cgi?id=15778
void test15778()
{
@@ -477,5 +490,5 @@ void main()
testDIP29_6();
test15778();
- writefln("Success");
+ printf("Success\n");
}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/A16a.d b/gcc/testsuite/gdc.test/runnable/imports/A16a.d
index 9f385cc960e..e60acdc07aa 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/A16a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/A16a.d
@@ -1,6 +1,6 @@
import A16;
-import std.stdio;
+import core.stdc.stdio;
class B16 : AA16
{
diff --git a/gcc/testsuite/gdc.test/runnable/imports/Other.d b/gcc/testsuite/gdc.test/runnable/imports/Other.d
index cd523c02a61..81d846f31c9 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/Other.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/Other.d
@@ -2,13 +2,12 @@ module imports.Other; // makes no difference if removed
import Same;
import core.stdc.stdio;
-class Other : Same // segfault
-// class Other : Same.Same //***UGLY ALERT*** but doesn't segfault
+class Other : Same.Same
{
-this()
-{
-printf("other\n");
-}
+ this()
+ {
+ printf("other\n");
+ }
}
int main()
diff --git a/gcc/testsuite/gdc.test/runnable/imports/a12037.d b/gcc/testsuite/gdc.test/runnable/imports/a12037.d
index 7c25d874c42..3d6e456cc29 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/a12037.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/a12037.d
@@ -19,29 +19,28 @@ private template CustomFloatParams(uint precision, uint exponentWidth)
) CustomFloatParams;
}
-struct CustomFloat(uint precision, uint exponentWidth)
-if ((1 + precision + exponentWidth) % 8 == 0 && precision + exponentWidth > 0)
+private union ToBinary(F)
+if (is(typeof(CustomFloatParams!(F.sizeof*8))) || is(F == real))
{
-private:
- union ToBinary(F)
- if (is(typeof(CustomFloatParams!(F.sizeof*8))) || is(F == real))
- {
- F set;
+ F set;
- // If on Linux or Mac, where 80-bit reals are padded, ignore the
- // padding.
- CustomFloat!(CustomFloatParams!(min(F.sizeof*8, 80))) get;
+ // If on Linux or Mac, where 80-bit reals are padded, ignore the
+ // padding.
+ CustomFloat!(CustomFloatParams!(min(F.sizeof*8, 80))) get;
- // Convert F to the correct binary type.
- static typeof(get) opCall(F value)
- {
- ToBinary r;
- r.set = value;
- return r.get;
- }
- alias get this;
+ // Convert F to the correct binary type.
+ static typeof(get) opCall(F value)
+ {
+ ToBinary r;
+ r.set = value;
+ return r.get;
}
+ alias get this;
+}
+struct CustomFloat(uint precision, uint exponentWidth)
+if ((1 + precision + exponentWidth) % 8 == 0 && precision + exponentWidth > 0)
+{
public:
@property bool sign() { return 1; }
@property void sign(bool) {}
@@ -65,7 +64,7 @@ public:
@property F get(F)()
if (is(F == float) || is(F == double) || is(F == real))
{
- ToBinary!F result;
+ ToBinary result;
return F.init;
}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/a21a.d b/gcc/testsuite/gdc.test/runnable/imports/a21a.d
index 8ba14620aed..ded47dad1e5 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/a21a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/a21a.d
@@ -1,6 +1,6 @@
module imports.a21a;
-import std.stdio;
+import core.stdc.stdio;
import a21;
template GoodMixin()
diff --git a/gcc/testsuite/gdc.test/runnable/imports/another_module_with_tests.d b/gcc/testsuite/gdc.test/runnable/imports/another_module_with_tests.d
new file mode 100644
index 00000000000..53290049e17
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/another_module_with_tests.d
@@ -0,0 +1,4 @@
+module imports.another_module_with_tests;
+unittest {}
+unittest {}
+unittest { assert(false); }
diff --git a/gcc/testsuite/gdc.test/runnable/imports/depsprot_default.d b/gcc/testsuite/gdc.test/runnable/imports/depsprot_default.d
deleted file mode 100644
index 2d5de351b2b..00000000000
--- a/gcc/testsuite/gdc.test/runnable/imports/depsprot_default.d
+++ /dev/null
@@ -1 +0,0 @@
-void pack() {}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/depsprot_private.d b/gcc/testsuite/gdc.test/runnable/imports/depsprot_private.d
deleted file mode 100644
index be063dd66aa..00000000000
--- a/gcc/testsuite/gdc.test/runnable/imports/depsprot_private.d
+++ /dev/null
@@ -1 +0,0 @@
-void priv() {}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/depsprot_public.d b/gcc/testsuite/gdc.test/runnable/imports/depsprot_public.d
deleted file mode 100644
index ad18eaf8df3..00000000000
--- a/gcc/testsuite/gdc.test/runnable/imports/depsprot_public.d
+++ /dev/null
@@ -1 +0,0 @@
-void pub() {}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/module_with_tests.d b/gcc/testsuite/gdc.test/runnable/imports/module_with_tests.d
new file mode 100644
index 00000000000..c0372abc9ab
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/module_with_tests.d
@@ -0,0 +1,2 @@
+module imports.module_with_tests;
+unittest {} unittest { assert(false); }
diff --git a/gcc/testsuite/gdc.test/runnable/imports/pubprivtmpla.d b/gcc/testsuite/gdc.test/runnable/imports/pubprivtmpla.d
new file mode 100644
index 00000000000..f68bdd719bf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/pubprivtmpla.d
@@ -0,0 +1,8 @@
+module pubprivtmpla;
+
+struct S
+{
+ private int m = 42;
+ private int _get()() { return m; }
+ public alias get = _get!();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/std11file.d b/gcc/testsuite/gdc.test/runnable/imports/std11file.d
new file mode 100644
index 00000000000..76d43be8c6c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/std11file.d
@@ -0,0 +1,6 @@
+module imports.std11file;
+
+string getcwd() @trusted
+{
+ return "/dlang/test/runnable";
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/std15017variant.d b/gcc/testsuite/gdc.test/runnable/imports/std15017variant.d
index f6f7f5ea404..cf5dbf14110 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/std15017variant.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/std15017variant.d
@@ -1,6 +1,6 @@
module imports.std15017variant;
-struct VariantN(size_t maxDataSize)
+struct VariantN(uint maxDataSize)
{
VariantN opAssign() { return this; }
diff --git a/gcc/testsuite/gdc.test/runnable/imports/template_ovs1.d b/gcc/testsuite/gdc.test/runnable/imports/template_ovs1.d
index a4b2ecf2a60..465e74c6674 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/template_ovs1.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/template_ovs1.d
@@ -1,7 +1,8 @@
module imports.template_ovs1;
/***************************************************/
-// 1900 - template overload set
+// https://issues.dlang.org/show_bug.cgi?id=1900
+// template overload set
auto foo1900a(int num) { return 1; }
auto foo1900b(T)(T arg) if (is(T : const(char)[])) { return 2; }
@@ -34,7 +35,7 @@ auto merge1900(T)(int)
}
/***************************************************/
-// 1900
+// https://issues.dlang.org/show_bug.cgi?id=1900
class AClass1900 {}
template Traits1900(T : AClass1900) { enum name = "AClass"; }
@@ -47,12 +48,12 @@ template Value1900b(T) if (is(T == double)) { enum Value1900b = 1; }
void Value1900b() {}
/***************************************************/
-// 8352
+// https://issues.dlang.org/show_bug.cgi?id=8352
Range remove8352a(alias pred, Range)(Range range) { return range; }
void remove8352b(in char[] name) {}
/***************************************************/
-// 10658
+// https://issues.dlang.org/show_bug.cgi?id=10658
template Val10658(int n) { enum Val10658 = 1; }
diff --git a/gcc/testsuite/gdc.test/runnable/imports/template_ovs2.d b/gcc/testsuite/gdc.test/runnable/imports/template_ovs2.d
index 5b925689e55..1c9500c8216 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/template_ovs2.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/template_ovs2.d
@@ -1,7 +1,8 @@
module imports.template_ovs2;
/***************************************************/
-// 1900 - template overload set
+// https://issues.dlang.org/show_bug.cgi?id=1900
+// template overload set
auto foo1900a(T)(T arg) if (is(T : const(char)[])) { return 2; }
auto foo1900b(int num) { return 1; }
@@ -34,7 +35,7 @@ auto merge1900(T)(string)
}
/***************************************************/
-// 1900
+// https://issues.dlang.org/show_bug.cgi?id=1900
class BClass1900 {}
template Traits1900(T : BClass1900) { enum name = "BClass"; }
@@ -47,12 +48,12 @@ template Value1900b(T) if (is(T == string)) { enum Value1900b = 2; }
void Value1900b() {}
/***************************************************/
-// 8352
+// https://issues.dlang.org/show_bug.cgi?id=8352
void remove8352a(in char[] name) {}
Range remove8352b(alias pred, Range)(Range range) { return range; }
/***************************************************/
-// 10658
+// https://issues.dlang.org/show_bug.cgi?id=10658
template Val10658(long n) { enum Val10658 = 2; }
diff --git a/gcc/testsuite/gdc.test/runnable/imports/template_ovs3.d b/gcc/testsuite/gdc.test/runnable/imports/template_ovs3.d
index ca76914e76b..75beeb9cb19 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/template_ovs3.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/template_ovs3.d
@@ -1,7 +1,8 @@
module imports.template_ovs3;
/***************************************************/
-// 1900 - template overload set
+// https://issues.dlang.org/show_bug.cgi?id=1900
+// template overload set
import imports.template_ovs1;
import imports.template_ovs2;
@@ -31,6 +32,6 @@ struct S1900
}
/***************************************************/
-// 1900
+// https://issues.dlang.org/show_bug.cgi?id=1900
struct Traits1900(T) if (!is(T == class)) { enum name = "any"; }
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test11931a.d b/gcc/testsuite/gdc.test/runnable/imports/test11931a.d
index 152b38a67cd..f43ac4f1919 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test11931a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test11931a.d
@@ -1,7 +1,5 @@
module imports.test11931a;
-import std.stdio;
-
import imports.test11931d;
import imports.test11931b;
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test11931d.d b/gcc/testsuite/gdc.test/runnable/imports/test11931d.d
index 7a3ed39ed68..dc9bbb1b064 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test11931d.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test11931d.d
@@ -1,7 +1,20 @@
module imports.test11931d;
-import std.array;
-import std.algorithm;
+template filter(alias pred)
+{
+ auto filter(Range)(Range r)
+ {
+ struct FilterResult
+ {
+ Range array()
+ {
+ return data;
+ }
+ Range data;
+ }
+ return FilterResult(r);
+ }
+}
struct ConnectionPoint
{
@@ -26,5 +39,5 @@ struct Signal(T, A...)
private:
alias D = T delegate(A);
- D _arr[];
+ D[] _arr;
}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test13a.d b/gcc/testsuite/gdc.test/runnable/imports/test13a.d
index 4b00277fba9..4512e416dcf 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test13a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test13a.d
@@ -17,7 +17,7 @@ template Ordinal(T) {
public T clamp(T item, T lower, T upper)
in {
assert(lower <= upper);
- } body {
+ } do {
return max(min(item, upper), lower);
}
}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test15777a.d b/gcc/testsuite/gdc.test/runnable/imports/test15777a.d
new file mode 100644
index 00000000000..55ae716c67d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test15777a.d
@@ -0,0 +1 @@
+void fun() {}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test15777b.d b/gcc/testsuite/gdc.test/runnable/imports/test15777b.d
new file mode 100644
index 00000000000..55ae716c67d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test15777b.d
@@ -0,0 +1 @@
+void fun() {}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test17181a.d b/gcc/testsuite/gdc.test/runnable/imports/test17181a.d
new file mode 100644
index 00000000000..c080e9640c6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test17181a.d
@@ -0,0 +1,10 @@
+module imports.test17181a;
+
+int a = 0;
+static this() { a = 1; }
+
+T abc(T)(T i)
+{
+ import imports.test17181b;
+ return i;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test17181b.d b/gcc/testsuite/gdc.test/runnable/imports/test17181b.d
new file mode 100644
index 00000000000..25e60880c76
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test17181b.d
@@ -0,0 +1,3 @@
+module imports.test17181b;
+import imports.test17181a;
+static this() { a = 2; }
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test17181c.d b/gcc/testsuite/gdc.test/runnable/imports/test17181c.d
new file mode 100644
index 00000000000..1722711c6aa
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test17181c.d
@@ -0,0 +1,7 @@
+module imports.test17181c;
+
+int getA()()
+{
+ import imports.test17181a;
+ return a;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test18868_a.d b/gcc/testsuite/gdc.test/runnable/imports/test18868_a.d
new file mode 100644
index 00000000000..275cf22de67
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test18868_a.d
@@ -0,0 +1,3 @@
+shared static this() { }
+import imports.test18868_fls;
+alias floop = FLS!(int);
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test18868_fls.d b/gcc/testsuite/gdc.test/runnable/imports/test18868_fls.d
new file mode 100644
index 00000000000..47142052588
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test18868_fls.d
@@ -0,0 +1,33 @@
+module imports.test18868_fls;
+
+template FLS(T)
+{
+ int ctorcount = 0;
+ int dtorcount = 0;
+ int sharedctorcount = 0;
+ int shareddtorcount = 0;
+
+ static this()
+ {
+ assert(ctorcount == 0);
+ ctorcount += 1;
+ }
+
+ static ~this()
+ {
+ assert(dtorcount == 0);
+ dtorcount += 1;
+ }
+
+ shared static this()
+ {
+ assert(sharedctorcount == 0);
+ sharedctorcount += 1;
+ }
+
+ shared static ~this()
+ {
+ assert(shareddtorcount == 0);
+ shareddtorcount += 1;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test19655b.d b/gcc/testsuite/gdc.test/runnable/imports/test19655b.d
new file mode 100644
index 00000000000..83d3c9d4e74
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test19655b.d
@@ -0,0 +1,8 @@
+import test19655c;
+import test19655d;
+class Garply: Grault
+{ }
+void main()
+{
+ (new Garply).func;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test19655c.d b/gcc/testsuite/gdc.test/runnable/imports/test19655c.d
new file mode 100644
index 00000000000..f69b7d96f87
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test19655c.d
@@ -0,0 +1,8 @@
+import test19655f;
+import test19655e;
+import test19655a: Corge;
+class Foo
+{
+ int[Foo] map;
+ void fun0(Corge) { }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test19655d.d b/gcc/testsuite/gdc.test/runnable/imports/test19655d.d
new file mode 100644
index 00000000000..992864467bd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test19655d.d
@@ -0,0 +1,14 @@
+import test19655f;
+import test19655g;
+class Grault: Bar
+{
+ void func()
+ {
+ func2;
+ }
+ void func1()
+ {
+ assert(false, "func1 was never called");
+ }
+ void func2() { }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test19655e.d b/gcc/testsuite/gdc.test/runnable/imports/test19655e.d
new file mode 100644
index 00000000000..d497fffeddb
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test19655e.d
@@ -0,0 +1,2 @@
+import test19655c;
+int[Foo] map;
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test19655f.d b/gcc/testsuite/gdc.test/runnable/imports/test19655f.d
new file mode 100644
index 00000000000..32a58f88b65
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test19655f.d
@@ -0,0 +1,2 @@
+import test19655c;
+import test19655g;
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test19655g.d b/gcc/testsuite/gdc.test/runnable/imports/test19655g.d
new file mode 100644
index 00000000000..d1cacfec120
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test19655g.d
@@ -0,0 +1,3 @@
+import test19655c;
+class Bar: Foo
+{ }
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test24a.d b/gcc/testsuite/gdc.test/runnable/imports/test24a.d
index e606f1c517c..d7ca719a566 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test24a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test24a.d
@@ -1,3 +1,3 @@
module imports.test24a;
-public import std.string;
+public import imports.test24c;
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test24b.d b/gcc/testsuite/gdc.test/runnable/imports/test24b.d
index dcb380f0323..507a848c7c2 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test24b.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test24b.d
@@ -1,3 +1,3 @@
module imports.test24b;
-public import std.string;
+public import imports.test24c;
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test24c.d b/gcc/testsuite/gdc.test/runnable/imports/test24c.d
new file mode 100644
index 00000000000..a6ffee05c08
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test24c.d
@@ -0,0 +1,6 @@
+module imports.test24c;
+
+immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args)
+{
+ return "3";
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test27a.d b/gcc/testsuite/gdc.test/runnable/imports/test27a.d
index 75c945d0798..8a68a165ef5 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test27a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test27a.d
@@ -1,10 +1,17 @@
module imports.test27a;
-import std.variant;
+struct Variant
+{
+ this(T)(T)
+ {
+ }
+}
-class myClass(T) {
+class myClass(T)
+{
public:
- void func(T v) {
+ void func(T v)
+ {
Variant b = Variant(v);
}
}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test39a.d b/gcc/testsuite/gdc.test/runnable/imports/test39a.d
index 84a1530c214..4150eaf0e75 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test39a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test39a.d
@@ -6,7 +6,7 @@ class Test (T)
{
final void show (in T[] msg)
{
- printf ("%.*s\n", msg.length, msg.ptr);
+ printf ("%.*s\n", cast(int)msg.length, msg.ptr);
}
}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test40a.d b/gcc/testsuite/gdc.test/runnable/imports/test40a.d
index b688b9f3161..d894f45b69b 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test40a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test40a.d
@@ -1,6 +1,6 @@
module imports.test40a;
-import std.stdio;
+import core.stdc.stdio;
template Mix()
{
@@ -8,7 +8,7 @@ template Mix()
{
auto context = new Context;
auto ts = context.toString;
- printf("context: %.*s %p\n", ts.length, ts.ptr, context);
+ printf("context: %.*s %p\n", cast(int)ts.length, ts.ptr, context);
context.func!(typeof(this))();
printf("returning from opCall\n");
}
@@ -23,8 +23,8 @@ class Bar
void someFunc(string z)
{
- printf("str length: %d\n", z.length);
- printf("str: '%.*s'\n", z.length, z.ptr);
+ printf("str length: %zd\n", z.length);
+ printf("str: '%.*s'\n", cast(int)z.length, z.ptr);
}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test45a.d b/gcc/testsuite/gdc.test/runnable/imports/test45a.d
index df3b8525597..d466cbe27e0 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test45a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test45a.d
@@ -1,6 +1,6 @@
module imports.test45a;
-import std.stdio;
+import core.stdc.stdio;
int foo()
{
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test45b.d b/gcc/testsuite/gdc.test/runnable/imports/test45b.d
index 371a162b114..d1b0c34ba8e 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test45b.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test45b.d
@@ -1,6 +1,6 @@
module imports.test45b;
-import std.stdio;
+import core.stdc.stdio;
int foo(int i)
{
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test49a.d b/gcc/testsuite/gdc.test/runnable/imports/test49a.d
index 57baf4be00a..cb9c9520e01 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/test49a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/test49a.d
@@ -1,6 +1,6 @@
module imports.test49a;
-import std.stdio;
+import core.stdc.stdio;
int x;
diff --git a/gcc/testsuite/gdc.test/runnable/imports/testmod2a.d b/gcc/testsuite/gdc.test/runnable/imports/testmod2a.d
index a59d12cf16d..a71581103b1 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/testmod2a.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/testmod2a.d
@@ -1,7 +1,7 @@
module imports.testmod2a;
/**********************************/
-// bug 1904
+// https://issues.dlang.org/show_bug.cgi?id=1904
// testmod.d
private void bar(alias a)() {}
diff --git a/gcc/testsuite/gdc.test/runnable/inclusive_incontracts.d b/gcc/testsuite/gdc.test/runnable/inclusive_incontracts.d
new file mode 100644
index 00000000000..7670b24df8b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/inclusive_incontracts.d
@@ -0,0 +1,80 @@
+// REQUIRED_ARGS: -preview=inclusiveincontracts
+// PERMUTE_ARGS: -O -inline
+
+import core.exception : AssertError;
+
+void main()
+{
+ basic_test;
+ multiple_incontracts_test;
+}
+
+void basic_test()
+{
+ class A
+ {
+ void foo() @nogc
+ in (true, "foo")
+ {
+ }
+ }
+
+ class B : A
+ {
+ override void foo() @nogc
+ in (false, "nope not foo")
+ {
+ }
+ }
+
+ auto b = new B;
+
+ try
+ {
+ b.foo;
+ throw new Exception("Assert expected.");
+ }
+ catch (AssertError assertError)
+ {
+ assert(assertError.line == 25); // line of invalid in-contract
+ assert(assertError.msg == "Logic error: in-contract was tighter than parent in-contract");
+ }
+}
+
+void multiple_incontracts_test()
+{
+ class A
+ {
+ void foo(int a, int b)
+ in (a > 0, "A::a")
+ in (b > 0, "B::b")
+ {
+ }
+ }
+
+ class B : A
+ {
+ override void foo(int a, int b)
+ in (a >= 0, "B::a")
+ in (b > 0, "B::b")
+ {
+ }
+ }
+
+ auto b = new B;
+
+ b.foo(0, 2);
+ try
+ {
+ b.foo(0, 0);
+ throw new Exception("Assert expected.");
+ }
+ catch (AssertError assertError) {
+ /**
+ * Having found that the looser contract in B is not fulfilled, we try
+ * the stricter contract in A. As that one is also violated, we error.
+ */
+ assert(assertError.line == 49);
+ assert(assertError.msg == "A::a");
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/inline.d b/gcc/testsuite/gdc.test/runnable/inline.d
index d3bb0a9b5e7..19be4792f3d 100644
--- a/gcc/testsuite/gdc.test/runnable/inline.d
+++ b/gcc/testsuite/gdc.test/runnable/inline.d
@@ -1,3 +1,14 @@
+/*
+RUN_OUTPUT:
+---
+7
+83
+4
+4
+1.000000 2.000000 3.000000
+Success
+---
+*/
import core.stdc.stdio;
@@ -145,7 +156,7 @@ void test7()
/************************************/
-// 10833
+// https://issues.dlang.org/show_bug.cgi?id=10833
string fun10833(T...)()
{
foreach (v ; T)
@@ -159,7 +170,7 @@ void test10833()
}
/************************************/
-// Bugzilla 4825
+// https://issues.dlang.org/show_bug.cgi?id=4825
int a8() {
int r;
@@ -178,7 +189,7 @@ void test8() {
}
/************************************/
-// 4841
+// https://issues.dlang.org/show_bug.cgi?id=4841
auto fun4841a()
{
@@ -264,7 +275,7 @@ void test4841()
}
/************************************/
-// 7261
+// https://issues.dlang.org/show_bug.cgi?id=7261
struct AbstractTask
{
@@ -287,7 +298,7 @@ struct Task
}
/************************************/
-// 9356
+// https://issues.dlang.org/show_bug.cgi?id=9356
void test9356()
{
@@ -302,7 +313,7 @@ void test9356()
}
/************************************/
-// 12079
+// https://issues.dlang.org/show_bug.cgi?id=12079
void test12079()
{
@@ -312,7 +323,7 @@ void test12079()
}
/************************************/
-// 12243
+// https://issues.dlang.org/show_bug.cgi?id=12243
char f12243() { return 'a'; }
@@ -323,7 +334,7 @@ void test12243()
}
/************************************/
-// 11201
+// https://issues.dlang.org/show_bug.cgi?id=11201
struct Foo11201
{
@@ -343,7 +354,7 @@ void test11201()
}
/************************************/
-// 11223
+// https://issues.dlang.org/show_bug.cgi?id=11223
struct Tuple11223(T...)
{
@@ -382,7 +393,7 @@ void test3918()
}
/************************************/
-// 11314
+// https://issues.dlang.org/show_bug.cgi?id=11314
struct Tuple11314(T...)
{
@@ -406,7 +417,7 @@ void test11314()
}
/************************************/
-// 11224
+// https://issues.dlang.org/show_bug.cgi?id=11224
S11224* ptr11224;
@@ -438,7 +449,7 @@ void test11224()
}
/************************************/
-// 11322
+// https://issues.dlang.org/show_bug.cgi?id=11322
bool b11322;
uint n11322;
@@ -460,7 +471,7 @@ void test11322()
}
/************************************/
-// 11394
+// https://issues.dlang.org/show_bug.cgi?id=11394
debug(NRVO) static void* p11394a, p11394b, p11394c;
@@ -497,7 +508,7 @@ void test11394()
}
/**********************************/
-// 12080
+// https://issues.dlang.org/show_bug.cgi?id=12080
class TZ12080 {}
@@ -521,7 +532,7 @@ class Foo12080
public ST12080 sysTime()
out {}
- body
+ do
{
if (quux)
return ST12080();
@@ -533,7 +544,7 @@ class Foo12080
}
/**********************************/
-// 13503
+// https://issues.dlang.org/show_bug.cgi?id=13503
void f13503a(string[] s...)
{
@@ -559,7 +570,7 @@ void test13503()
}
/**********************************/
-// 14267
+// https://issues.dlang.org/show_bug.cgi?id=14267
// EXTRA_SOURCES: imports/a14267.d
import imports.a14267;
@@ -584,7 +595,7 @@ void test14267()
}
/**********************************/
-// 13244
+// https://issues.dlang.org/show_bug.cgi?id=13244
struct MapResult13244(alias fun)
{
@@ -610,7 +621,7 @@ void test13244()
}
/**********************************/
-// 14306
+// https://issues.dlang.org/show_bug.cgi?id=14306
struct MapResult(alias fun)
{
@@ -656,7 +667,7 @@ void test14306()
}
/**********************************/
-// 14754
+// https://issues.dlang.org/show_bug.cgi?id=14754
auto aafunc14754(string k)
{
@@ -694,7 +705,7 @@ void test14754()
}
/**********************************/
-// 14606
+// https://issues.dlang.org/show_bug.cgi?id=14606
struct S14606
{
@@ -729,7 +740,7 @@ void test14606()
}
/**********************************/
-// 14753
+// https://issues.dlang.org/show_bug.cgi?id=14753
pragma(inline)
void test14753(string) { }
@@ -754,7 +765,7 @@ void test14975()
}
/**********************************/
-// 15210
+// https://issues.dlang.org/show_bug.cgi?id=15210
struct BigInt15210 {}
@@ -809,7 +820,7 @@ void test7625()
}
/**********************************/
-// 9785 partial fix
+// https://issues.dlang.org/show_bug.cgi?id=9785 partial fix
void test9785()
{
@@ -829,7 +840,7 @@ void test9785()
/**********************************/
-// 9785 partial fix
+// https://issues.dlang.org/show_bug.cgi?id=9785 partial fix
void test9785_2() {
int j = 3;
@@ -849,7 +860,7 @@ void test9785_2() {
}
/**********************************/
-// 9785 partial fix
+// https://issues.dlang.org/show_bug.cgi?id=9785 partial fix
void test9785_3() @nogc
{
@@ -879,7 +890,7 @@ void test9785_3() @nogc
}
/**********************************/
-// 15207
+// https://issues.dlang.org/show_bug.cgi?id=15207
struct Vec15207
{
@@ -923,7 +934,7 @@ void test15207()
}
/**********************************/
-// 15253
+// https://issues.dlang.org/show_bug.cgi?id=15253
struct MessageType15253
{
@@ -946,7 +957,7 @@ struct ProtoPackage15253
}
/**********************************/
-// 15296
+// https://issues.dlang.org/show_bug.cgi?id=15296
static int x15296;
@@ -1132,7 +1143,7 @@ int main()
test15296b();
test15296c();
test17676();
-
+
printf("Success\n");
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/inner.d b/gcc/testsuite/gdc.test/runnable/inner.d
index e1be7b9cbad..7bf59d24a64 100644
--- a/gcc/testsuite/gdc.test/runnable/inner.d
+++ b/gcc/testsuite/gdc.test/runnable/inner.d
@@ -1,5 +1,5 @@
-// RUNNABLE_PHOBOS_TEST
-import std.stdio;
+
+import core.stdc.stdio;
/*******************************************************/
@@ -591,7 +591,7 @@ class Foo18
{
void doSayHello()
{
- writefln("Betty");
+ printf("Betty\n");
sayHello();
}
}
@@ -599,7 +599,7 @@ class Foo18
void sayHello()
{
- writefln("Hello");
+ printf("Hello\n");
}
}
@@ -614,7 +614,7 @@ class Foo182 : Foo18
void test18()
{
Foo182 foo = new Foo182();
- writefln("This should print Hello:");
+ printf("This should print Hello:\n");
foo.bar.doSayHello();
}
@@ -627,7 +627,7 @@ class Foo19
{
void doSayHello()
{
- writefln("Betty");
+ printf("Betty\n");
sayHello();
}
}
@@ -635,7 +635,7 @@ class Foo19
void sayHello()
{
- writefln("Hello");
+ printf("Hello\n");
}
this()
@@ -651,7 +651,7 @@ class Foo192 : Foo19
void test19()
{
Foo192 foo = new Foo192();
- writefln("This should print Hello:");
+ printf("This should print Hello:\n");
foo.bar.doSayHello();
}
@@ -796,7 +796,7 @@ struct S7426
}
/*******************************************************/
-// 14046
+// https://issues.dlang.org/show_bug.cgi?id=14046
class A14046
{
@@ -828,7 +828,7 @@ void test14046()
}
/*******************************************************/
-// 15839
+// https://issues.dlang.org/show_bug.cgi?id=15839
class AnimatedProgress15839(bool makeClosure)
{
diff --git a/gcc/testsuite/gdc.test/runnable/interface.d b/gcc/testsuite/gdc.test/runnable/interface.d
index dab7beb9c28..78a712e7a23 100644
--- a/gcc/testsuite/gdc.test/runnable/interface.d
+++ b/gcc/testsuite/gdc.test/runnable/interface.d
@@ -1,3 +1,10 @@
+/*
+TEST_OUTPUT:
+---
+runnable/interface.d(41): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+runnable/interface.d(55): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+*/
import core.stdc.stdio;
@@ -79,7 +86,7 @@ void test3()
j.f();
K3 k = a;
k.f();
- assert(&j.f == &k.f); // Bugzilla 3706
+ assert(&j.f == &k.f); // https://issues.dlang.org/show_bug.cgi?id=3706
}
/*******************************************/
diff --git a/gcc/testsuite/gdc.test/runnable/interface2.d b/gcc/testsuite/gdc.test/runnable/interface2.d
index 85adf65b481..328f839896d 100644
--- a/gcc/testsuite/gdc.test/runnable/interface2.d
+++ b/gcc/testsuite/gdc.test/runnable/interface2.d
@@ -1,4 +1,11 @@
// PERMUTE_ARGS:
+/*
+TEST_OUTPUT:
+---
+runnable/interface2.d(47): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+runnable/interface2.d(98): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+*/
extern(C) int printf(const char*, ...);
@@ -27,7 +34,7 @@ void test1()
printf("f = %p\n", f);
assert(cast(void*)b !is cast(void*)f);
- printf("f.class = '%.*s'\n", f.classinfo.name.length, f.classinfo.name.ptr);
+ printf("f.class = '%.*s'\n", cast(int)f.classinfo.name.length, f.classinfo.name.ptr);
assert(f.classinfo.name == "interface2.Foo");
f.bar();
@@ -341,7 +348,7 @@ class B12 : A12
{
printf("B12.clone()\n");
}
- body
+ do
{
return ia;
}
@@ -395,7 +402,7 @@ class B13 : A13
{
printf("B13.clone()\n");
}
- body { return ia; }
+ do { return ia; }
}
void test13()
@@ -662,11 +669,11 @@ void test19()
assert(cast(void*)c + (3*(void*).sizeof) == cast(void*)ifoo);
string s = ifoo.classinfo.name;
- printf("%.*s\n", s.length, s.ptr);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
assert(s == "interface2.IFoo19");
s = (cast(Object)ifoo).toString;
- printf("%.*s\n", s.length, s.ptr);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
assert(s == "interface2.Child19");
}
@@ -923,7 +930,8 @@ void test27()
}
/*******************************************************/
-// 1747 & 2013
+// https://issues.dlang.org/show_bug.cgi?id=1747
+// https://issues.dlang.org/show_bug.cgi?id=2013
void test1747()
{
@@ -1009,14 +1017,14 @@ bool foo4088(Test4088 x, Test4088 y)
}
/*******************************************************/
-// 7950
+// https://issues.dlang.org/show_bug.cgi?id=7950
template TypeTuple7950(T...){alias T TypeTuple7950;}
interface I7950a {} // ok
interface I7950b : I7950a, TypeTuple7950!() {} // fail
/*******************************************************/
-// 10007
+// https://issues.dlang.org/show_bug.cgi?id=10007
struct A10007 {}
@@ -1032,7 +1040,7 @@ class Foo10007 : IFoo10007
}
/*******************************************************/
-// 10744
+// https://issues.dlang.org/show_bug.cgi?id=10744
interface A10744
{
@@ -1065,7 +1073,7 @@ interface Baz10744 { }
interface Qux10744 : Baz10744 { }
/*******************************************************/
-// 11034
+// https://issues.dlang.org/show_bug.cgi?id=11034
class A11034(T)
{
diff --git a/gcc/testsuite/gdc.test/runnable/interface3.d b/gcc/testsuite/gdc.test/runnable/interface3.d
index f748f72b08d..f0de22c95d0 100644
--- a/gcc/testsuite/gdc.test/runnable/interface3.d
+++ b/gcc/testsuite/gdc.test/runnable/interface3.d
@@ -1,4 +1,10 @@
-// PERMUTE_ARGS:
+/*
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+OK
+---
+*/
extern(C) int printf(const char*, ...);
@@ -71,5 +77,3 @@ int main()
assert(Newline.OKset == 1);
return 0;
}
-
-
diff --git a/gcc/testsuite/gdc.test/runnable/interpret.d b/gcc/testsuite/gdc.test/runnable/interpret.d
index fe44744fcfc..a626749ee46 100644
--- a/gcc/testsuite/gdc.test/runnable/interpret.d
+++ b/gcc/testsuite/gdc.test/runnable/interpret.d
@@ -1,4 +1,4 @@
-/* RUNNABLE_PHOBOS_TEST
+/*
TEST_OUTPUT:
---
true
@@ -17,7 +17,8 @@ tfoo
Crash!
---
*/
-import std.stdio;
+
+import core.stdc.stdio;
template Tuple(A...)
{
@@ -813,7 +814,7 @@ void test36()
string someCompileTimeFunction()
{
- return "writefln(\"Wowza!\");";
+ return "printf(\"Wowza!\n\");";
}
void test37()
@@ -871,7 +872,6 @@ string UpToSpace(string x)
void test40()
{
const y = UpToSpace("first space was after first");
- writeln(y);
assert(y == "first");
}
@@ -893,7 +893,6 @@ int foo41(int i)
void test41()
{
const y = foo41(3);
- writeln(y);
assert(y == 6);
}
@@ -915,7 +914,6 @@ int foo42(int i)
void test42()
{
const y = foo42(3);
- writeln(y);
assert(y == 6);
}
@@ -937,7 +935,6 @@ int bar(string a)
void test43()
{
const int foo = bar("a b c d");
- writeln(foo);
assert(foo == 28);
}
@@ -963,11 +960,11 @@ void test45()
/************************************************/
-const int foo46[5] = [0,1,2,3,4];
+const int[5] foo46 = [0,1,2,3,4];
void test46()
{
- writeln(eval!(foo46[3]));
+ printf("%d\n", eval!(foo46[3]));
}
/************************************************/
@@ -1017,7 +1014,6 @@ dstring testd49(dstring input)
void test49()
{
static x = testd49("hello");
- writeln(x);
assert(x == "el");
}
@@ -1126,26 +1122,29 @@ bool equals54(string a, string b)
/************************************************/
-const string foo55[2] = ["a", "b"];
+const string[2] foo55 = ["a", "b"];
string retsth55(int i) { return foo55[i]; }
void test55()
{
- writeln(eval!(foo55[0]));
- writeln(eval!(retsth55(0)));
+ enum res1 = eval!(foo55[0]);
+ printf("%.*s\n", cast(int)res1.length, res1.ptr);
+ enum res2 = eval!(retsth55(0));
+ printf("%.*s\n", cast(int)res2.length, res2.ptr);
}
/************************************************/
string retsth56(int i)
{
- static const string foo[2] = ["a", "b"];
+ static const string[2] foo = ["a", "b"];
return foo[i];
}
void test56()
{
- writeln(eval!(retsth56(0)));
+ enum result = eval!(retsth56(0));
+ printf("%.*s\n", cast(int)result.length, result.ptr);
}
/************************************************/
@@ -1178,7 +1177,6 @@ void test58()
assert(b.length == 2);
assert(b[0] == 2);
assert(b[1] == 3);
- writeln(b);
}
/************************************************/
@@ -1622,7 +1620,6 @@ const string s83 = mixItemList83();
void test83()
{
- writeln(s83);
assert(s83 == "item");
}
@@ -1813,7 +1810,7 @@ string foo90(string a, string b)
void test90()
{
static const string xxx = foo90("A", "xxx");
- printf("%.*s\n", xxx.length, xxx.ptr);
+ printf("%.*s\n", cast(int)xxx.length, xxx.ptr);
assert(xxx == "A");
}
@@ -2199,12 +2196,12 @@ struct Q
{
int x;
char y;
- int opAddAssign(int w)
+ int opOpAssign(string op)(int w) if (op == "+")
{
x += w;
return x + w;
}
- Q opSubAssign(int w)
+ Q opOpAssign(string op)(int w) if (op == "-")
{
x -= w;
version(D_Version2) { mixin("return this;"); } else { mixin("return *this;"); }
@@ -2300,7 +2297,7 @@ static assert(memtest8() == 6 + 17);
// --------- CTFE REF PASSING TESTS --------
-// Bugzilla 1950 - CTFE doesn't work correctly for structs passed by ref
+// https://issues.dlang.org/show_bug.cgi?id=1950 - CTFE doesn't work correctly for structs passed by ref
struct S1950
{
int x;
@@ -2412,7 +2409,8 @@ int nested2(int x)
static assert(nested2(7) == 17 + 8 + 10);
-// 1605 D1 & D2. break in switch with goto breaks in ctfe
+// https://issues.dlang.org/show_bug.cgi?id=1605
+// D1 & D2. break in switch with goto breaks in ctfe
int bug1605()
{
int i = 0;
@@ -2430,7 +2428,8 @@ int bug1605()
static assert(bug1605() == 27);
-// 2564. D2 only. CTFE: the index in a tuple foreach is uninitialized (bogus error)
+// https://issues.dlang.org/show_bug.cgi?id=2564
+// D2 only. CTFE: the index in a tuple foreach is uninitialized (bogus error)
// NOTE: Beware of optimizer bug 3264.
int bug2564()
@@ -2444,7 +2443,8 @@ int bug2564()
static int bug2564b = bug2564();
-// 1461 D1 + D2. Local variable as template alias parameter breaks CTFE
+// https://issues.dlang.org/show_bug.cgi?id=1461
+// D1 + D2. Local variable as template alias parameter breaks CTFE
void bug1461()
{
int x;
@@ -2620,7 +2620,7 @@ int delegtest6()
{
DelegStruct s;
s.a = 5;
- FoolishStruct k[3];
+ FoolishStruct[3] k;
DelegType u = &s.bar;
k[1].z = u;
return k[1].z(3);
@@ -2655,8 +2655,9 @@ static assert(lazyTest2(17) == 18);
version(D_Version2)
{
-// Bug 4020 and 4027 are D2 only
-
+// https://issues.dlang.org/show_bug.cgi?id=4020
+// https://issues.dlang.org/show_bug.cgi?id=4027
+// D2 only
struct PostblitCrash
{
int x;
@@ -2814,7 +2815,7 @@ void bug4257b()
}
/************************************************/
-// 5117
+// https://issues.dlang.org/show_bug.cgi?id=5117
static int dummy5117 = test5117();
@@ -2853,7 +2854,7 @@ int test5117b()
assert(s.value == 1); // fails, value == 0
return 0;
}
-ref S5117b getRef5117b(ref S5117b s) { return s; }
+ref S5117b getRef5117b(return ref S5117b s) { return s; }
struct S5117b
{
@@ -2862,7 +2863,7 @@ struct S5117b
}
/************************************************/
-// 6439
+// https://issues.dlang.org/show_bug.cgi?id=6439
struct A6439
{
@@ -2902,7 +2903,7 @@ static assert(!is(typeof(Compileable!(
}(3)
))));
-// 6504 regression
+// https://issues.dlang.org/show_bug.cgi?id=6504 regression
void test6504()
{
for (int i = 0; i < 3; ++i)
@@ -2913,7 +2914,7 @@ void test6504()
}
}
-// 8818 regression
+// https://issues.dlang.org/show_bug.cgi?id=8818 regression
void test8818()
{
static bool test()
@@ -3148,7 +3149,7 @@ void test108()
}
*/
-/***** Bug 5678 *********************************/
+/***** https://issues.dlang.org/show_bug.cgi?id=5678 *****/
/*
struct Bug5678
@@ -3185,7 +3186,7 @@ struct Test110s { this(int, int, int){} }
auto test110 = [Test110f(1, Test110s(1, 2, 3))];
/************************************************/
-// 6907
+// https://issues.dlang.org/show_bug.cgi?id=6907
int test6907()
{
@@ -3219,7 +3220,7 @@ int test6907()
static assert(test6907());
/************************************************/
-// 9023
+// https://issues.dlang.org/show_bug.cgi?id=9023
bool test9023()
{
@@ -3244,7 +3245,7 @@ bool test9023()
static assert(test9023());
/************************************************/
-// 15817
+// https://issues.dlang.org/show_bug.cgi?id=15817
S[] split15817(S)(S s)
{
@@ -3292,11 +3293,11 @@ void test9954()
}
/************************************************/
-// 10483
+// https://issues.dlang.org/show_bug.cgi?id=10483
struct Bug10483
{
- int val[3][4];
+ int[3][4] val;
}
struct Outer10483
@@ -3340,7 +3341,7 @@ void test112()
}
/************************************************/
-// 10687
+// https://issues.dlang.org/show_bug.cgi?id=10687
enum Foo10687 : uint { A, B, C, D, E }
immutable uint[5][] m10687 = [[0, 1, 2, 3, 4]];
@@ -3363,7 +3364,6 @@ void test113()
static void compare(real a, real b)
{
- writefln("compare(%30.30f, %30.30f);", a, b);
assert(fabs(a - b) < 128 * real.epsilon);
}
@@ -3399,7 +3399,7 @@ void test113()
}
/************************************************/
-// 14140
+// https://issues.dlang.org/show_bug.cgi?id=14140
struct S14140
{
@@ -3442,7 +3442,7 @@ void test14140()
}
/************************************************/
-// 14862
+// https://issues.dlang.org/show_bug.cgi?id=14862
struct S14862
{
@@ -3470,7 +3470,7 @@ void test14862()
}
/************************************************/
-// 15681
+// https://issues.dlang.org/show_bug.cgi?id=15681
void test15681()
{
@@ -3544,6 +3544,68 @@ void testToPrec()
/************************************************/
+auto test20366()
+{
+ const(char)[] s = ['h', 'e', 'l', '\xef', '\xbd', '\x8c', 'o'];
+
+ foreach_reverse (dchar c; s)
+ {
+ }
+
+ return true;
+}
+static assert(test20366());
+
+/************************************************/
+
+bool test20400()
+{
+ char[] s = cast(char[])"1234";
+ char[] ret = s[2 .. $];
+ ret.length += 1;
+ ret[$-1] = '5';
+ assert(ret == "345");
+
+ return true;
+}
+static assert(test20400());
+
+/************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=21878
+
+struct A21878
+{
+ int i;
+ ref inout(int) opIndex(size_t idx) inout return { return i; }
+}
+
+struct B21878
+{
+ A21878[1] a;
+ ref inout(int) opIndex(size_t idx) inout return { return a[0][idx]; }
+}
+
+bool ctfeFunc21878()
+{
+ A21878 a;
+ a[0] = 42;
+ assert(a[0] == 42); // OK
+
+ B21878 b;
+ b[0] = 42;
+ assert(b[0] == 42); // OK <- fails
+
+ return true;
+}
+
+void test21878()
+{
+ enum eval = ctfeFunc21878();
+ ctfeFunc21878(); // succeeds at runtime
+}
+
+/************************************************/
+
int main()
{
test1();
@@ -3667,6 +3729,9 @@ int main()
test14140();
test14862();
test15681();
+ test20366();
+ test20400();
+ test21878();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/interpret2.d b/gcc/testsuite/gdc.test/runnable/interpret2.d
index 38242a7f3ef..77bc25cd3a3 100644
--- a/gcc/testsuite/gdc.test/runnable/interpret2.d
+++ b/gcc/testsuite/gdc.test/runnable/interpret2.d
@@ -1,5 +1,11 @@
-
-//import std.stdio;
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
+
+//import core.stdc.stdio;
extern(C) int printf(const char*, ...);
template Tuple(A...)
@@ -52,7 +58,7 @@ void test1()
assert(z1 == 8194);
}
-/***** Bug 2850 *********************************/
+/***** https://issues.dlang.org/show_bug.cgi?id=2850 *****/
/* These tests are not passing, and shouldn't pass. A non-first field in a union
being initialized cannot be converted to an expression, at least not until there are
@@ -122,11 +128,11 @@ void test2()
}
}
-/***** Bug 3779 *********************************/
+/***** https://issues.dlang.org/show_bug.cgi?id=3779 *****/
static const bug3779 = ["123"][0][$-1];
-/***** Bug 1880 *********************************/
+/***** https://issues.dlang.org/show_bug.cgi?id=1880 *****/
enum Property1880 {First=1,Second=2}
diff --git a/gcc/testsuite/gdc.test/runnable/issue16995.d b/gcc/testsuite/gdc.test/runnable/issue16995.d
new file mode 100644
index 00000000000..3b028d42bf6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/issue16995.d
@@ -0,0 +1,42 @@
+// REQUIRED_ARGS: -unittest
+// COMPILE_SEPARATELY
+// EXTRA_SOURCES: imports/module_with_tests.d imports/another_module_with_tests.d
+
+import imports.module_with_tests;
+import imports.another_module_with_tests;
+import core.exception: AssertError;
+
+shared static this()
+{
+ import core.runtime: Runtime, UnitTestResult;
+ Runtime.extendedModuleUnitTester = () => UnitTestResult.pass;
+}
+
+void main()
+{
+ foreach(i, ut; __traits(getUnitTests, imports.module_with_tests))
+ {
+ try
+ {
+ ut();
+ assert(i == 0, "2nd unittest should fail");
+ }
+ catch(AssertError e)
+ {
+ assert(i == 1, "Only 2nd unittest should fail");
+ }
+ }
+
+ foreach(i, ut; __traits(getUnitTests, imports.another_module_with_tests))
+ {
+ try
+ {
+ ut();
+ assert(i == 0 || i == 1, "3rd unittest should fail");
+ }
+ catch(AssertError e)
+ {
+ assert(i == 2, "Only 3rd unittest should fail");
+ }
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/issue8671.d b/gcc/testsuite/gdc.test/runnable/issue8671.d
deleted file mode 100644
index c28e63b275b..00000000000
--- a/gcc/testsuite/gdc.test/runnable/issue8671.d
+++ /dev/null
@@ -1,6 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-import std.random;
-void main()
-{
- double t = 1.0 - uniform(0.0, 1.0);
-}
diff --git a/gcc/testsuite/gdc.test/runnable/lazy.d b/gcc/testsuite/gdc.test/runnable/lazy.d
index c1846190aed..0bd8f96633f 100644
--- a/gcc/testsuite/gdc.test/runnable/lazy.d
+++ b/gcc/testsuite/gdc.test/runnable/lazy.d
@@ -1,6 +1,5 @@
-// RUNNABLE_PHOBOS_TEST
import core.vararg;
-import std.stdio;
+import core.stdc.stdio;
/*********************************************************/
@@ -105,9 +104,14 @@ void bar3(...)
assert(va_arg!int(_argptr) == 14);
}
+void arr3(...)
+{
+ assert(_arguments.length == 1);
+ assert(va_arg!(int[])(_argptr) == [1,0,0,0,0,0,0,0,0,9]);
+}
+
void abc3(int* p)
{
- writeln(*p);
assert(*p == 3);
}
@@ -115,15 +119,13 @@ void test3()
{
int x = 3;
dotimes3(10, abc3(&x));
- dotimes3(10, write(++x));
- writeln();
+ dotimes3(10, ++x);
dotimes3(1, bar3(++x));
int[10] a = new int[10];
a[0] = 1;
a[$ - 1] = 9;
- dotimes3(3, write(a[0..$]));
- writeln();
+ dotimes3(3, arr3(a[0..$]));
}
/*********************************************************/
@@ -133,7 +135,6 @@ int p4;
void foo4(void* delegate()[] dgs...)
{
assert(dgs.length == 4);
- writefln("%s %s", dgs[0](), cast(void*)&p4);
assert(dgs[0]() == cast(void*)&p4);
assert(dgs[1]() == cast(void*)&p4);
assert(dgs[2]() == null);
@@ -144,7 +145,6 @@ void test4()
{
void *abc()
{
- writeln(cast(void*)&p4);
return cast(void*)&p4;
}
@@ -254,7 +254,7 @@ void test6682()
}
/*********************************************************/
-// 9109
+// https://issues.dlang.org/show_bug.cgi?id=9109
void test9109()
{
@@ -269,7 +269,7 @@ void test9109()
}
/*********************************************************/
-// 15835
+// https://issues.dlang.org/show_bug.cgi?id=15835
class C15835 {}
diff --git a/gcc/testsuite/gdc.test/runnable/lexer.d b/gcc/testsuite/gdc.test/runnable/lexer.d
index c16d7b3e240..ee2fef8f23f 100644
--- a/gcc/testsuite/gdc.test/runnable/lexer.d
+++ b/gcc/testsuite/gdc.test/runnable/lexer.d
@@ -53,7 +53,7 @@ void test7()
}
/*********************************************************/
-// 4633
+// https://issues.dlang.org/show_bug.cgi?id=4633
template Types(alias v)
{
@@ -71,7 +71,7 @@ void test8()
/*********************************************************/
-// bug 6584
+// https://issues.dlang.org/show_bug.cgi?id=6584
version(9223372036854775807){}
debug(9223372036854775807){}
diff --git a/gcc/testsuite/gdc.test/runnable/link11069a.d b/gcc/testsuite/gdc.test/runnable/link11069a.d
index 01b923e7497..e31cfa0bcf6 100644
--- a/gcc/testsuite/gdc.test/runnable/link11069a.d
+++ b/gcc/testsuite/gdc.test/runnable/link11069a.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/std11069array.d imports/std11069container.d imports/std11069range.d imports/std11069typecons.d
// REQUIRED_ARGS: -noboundscheck
// <-- To remove necessity of _D7imports13std11069array7__arrayZ
diff --git a/gcc/testsuite/gdc.test/runnable/link11127.d b/gcc/testsuite/gdc.test/runnable/link11127.d
index 0ce963d2859..0e5118f15c9 100644
--- a/gcc/testsuite/gdc.test/runnable/link11127.d
+++ b/gcc/testsuite/gdc.test/runnable/link11127.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/link11127a.d
import imports.link11127a;
void main()
diff --git a/gcc/testsuite/gdc.test/runnable/link12037.d b/gcc/testsuite/gdc.test/runnable/link12037.d
index ce5b17ff5e3..ca3e3c7c620 100644
--- a/gcc/testsuite/gdc.test/runnable/link12037.d
+++ b/gcc/testsuite/gdc.test/runnable/link12037.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/a12037.d
import imports.a12037;
alias CustomFloat!(10, 5) Float16;
diff --git a/gcc/testsuite/gdc.test/runnable/link12144.d b/gcc/testsuite/gdc.test/runnable/link12144.d
index 90925563e15..e7bcf865183 100644
--- a/gcc/testsuite/gdc.test/runnable/link12144.d
+++ b/gcc/testsuite/gdc.test/runnable/link12144.d
@@ -1,5 +1,11 @@
// COMPILE_SEPARATELY: -g
// EXTRA_SOURCES: imports/link12144a.d
+/*
+TEST_OUTPUT:
+---
+runnable/imports/link12144a.d(31): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+*/
import imports.link12144a;
diff --git a/gcc/testsuite/gdc.test/runnable/link13043.d b/gcc/testsuite/gdc.test/runnable/link13043.d
index 296907442ab..561651fac6e 100644
--- a/gcc/testsuite/gdc.test/runnable/link13043.d
+++ b/gcc/testsuite/gdc.test/runnable/link13043.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS: -g -inline -version=bug -release -O
-
+// EXTRA_FILES: imports/link13043a.d
import imports.link13043a;
void main() {}
diff --git a/gcc/testsuite/gdc.test/runnable/link13350.d b/gcc/testsuite/gdc.test/runnable/link13350.d
index bceb4b8586b..2a2b40bb1e6 100644
--- a/gcc/testsuite/gdc.test/runnable/link13350.d
+++ b/gcc/testsuite/gdc.test/runnable/link13350.d
@@ -1,3 +1,9 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern (C) int printf(const(char*) fmt, ...);
static int foo();
diff --git a/gcc/testsuite/gdc.test/runnable/link13415.d b/gcc/testsuite/gdc.test/runnable/link13415.d
index 00b39d1e8d6..782bb6bbd27 100644
--- a/gcc/testsuite/gdc.test/runnable/link13415.d
+++ b/gcc/testsuite/gdc.test/runnable/link13415.d
@@ -1,7 +1,13 @@
-// EXTRA_SOURCES: imports/link13415a.d
-// REQUIRED_ARGS: -inline
-// PERMUTE_ARGS: -allinst -unittest -debug
-// COMPILE_SEPARATELY
+/*
+EXTRA_SOURCES: imports/link13415a.d
+REQUIRED_ARGS: -inline
+PERMUTE_ARGS: -allinst -unittest -debug
+COMPILE_SEPARATELY
+RUN_OUTPUT:
+---
+i = 77;
+---
+*/
import imports.link13415a;
diff --git a/gcc/testsuite/gdc.test/runnable/link14074a.d b/gcc/testsuite/gdc.test/runnable/link14074a.d
index eaf1c89ef97..200e8101e4b 100644
--- a/gcc/testsuite/gdc.test/runnable/link14074a.d
+++ b/gcc/testsuite/gdc.test/runnable/link14074a.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/link14074x.d
// EXTRA_SOURCES: imports/link14074y.d
import imports.link14074x;
import imports.link14074y;
diff --git a/gcc/testsuite/gdc.test/runnable/link14074b.d b/gcc/testsuite/gdc.test/runnable/link14074b.d
index fea23697fb7..a05ecdc50cf 100644
--- a/gcc/testsuite/gdc.test/runnable/link14074b.d
+++ b/gcc/testsuite/gdc.test/runnable/link14074b.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/link14074z.d
import imports.link14074z;
void main()
diff --git a/gcc/testsuite/gdc.test/runnable/link14541.d b/gcc/testsuite/gdc.test/runnable/link14541.d
index 433b9d73950..7877a34448b 100644
--- a/gcc/testsuite/gdc.test/runnable/link14541.d
+++ b/gcc/testsuite/gdc.test/runnable/link14541.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/link14541traits.d
import imports.link14541traits;
void main()
diff --git a/gcc/testsuite/gdc.test/runnable/link14992.d b/gcc/testsuite/gdc.test/runnable/link14992.d
index 2da97b9afa7..d024b5f7fdf 100644
--- a/gcc/testsuite/gdc.test/runnable/link14992.d
+++ b/gcc/testsuite/gdc.test/runnable/link14992.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/a14992.d
import imports.a14992; // do not link
int test()
diff --git a/gcc/testsuite/gdc.test/runnable/link15017.d b/gcc/testsuite/gdc.test/runnable/link15017.d
index ec3304ec793..16187a8b404 100644
--- a/gcc/testsuite/gdc.test/runnable/link15017.d
+++ b/gcc/testsuite/gdc.test/runnable/link15017.d
@@ -1,5 +1,11 @@
// COMPILE_SEPARATELY
// EXTRA_SOURCES: imports/std15017variant.d
+/*
+TEST_OUTPUT:
+---
+runnable/link15017.d(48): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+*/
import imports.std15017variant;
@@ -40,6 +46,7 @@ void test()
// OK <- in DeleteExp::semantic
Variant10* p10;
delete p10;
+ static assert(Variant10.__dtor.mangleof == "_D7imports15std15017variant__T8VariantNVki10ZQp6__dtorMFNaNbNiNfZv");
}
void main() {}
diff --git a/gcc/testsuite/gdc.test/runnable/link6574.d b/gcc/testsuite/gdc.test/runnable/link6574.d
index 53e95f21ca9..0f633f3852f 100644
--- a/gcc/testsuite/gdc.test/runnable/link6574.d
+++ b/gcc/testsuite/gdc.test/runnable/link6574.d
@@ -15,14 +15,18 @@ import imports.testmangle;
enum Method { A, B, }
+@safe @nogc pure nothrow:
+
+enum FZi = "FNaNbNiNfZi"; // pure nothrow @nogc @safe
+
int foo(Method method = Method.A)()
{
- static assert(foo.mangleof == "_D8link6574"~tl!"28"~"__T3fooVE"~id!("8link6574","Qs")~"6Methodi0Z"~id!("3foo","Qs")~"FZi");
+ static assert(foo.mangleof == "_D8link6574"~tl!"28"~"__T3fooVE"~id!("8link6574","Qs")~"6Methodi0Z"~id!("3foo","Qs")~FZi);
return 10 * foo!method();
}
int foo(Method method : Method.A)()
{
- static assert(foo.mangleof == "_D8link6574"~tl!"29"~"__T3fooHVE"~id!("8link6574","Qt")~"6Methodi0Z"~id!("3foo","Qt")~"FZi");
+ static assert(foo.mangleof == "_D8link6574"~tl!"29"~"__T3fooHVE"~id!("8link6574","Qt")~"6Methodi0Z"~id!("3foo","Qt")~FZi);
return 2;
}
int foo(Method method : Method.B)()
@@ -33,7 +37,7 @@ int foo(Method method : Method.B)()
int bar(Method method = Method.B)()
{
- static assert(bar.mangleof == "_D8link6574"~tl!"28"~"__T3barVE"~id!("8link6574","Qs")~"6Methodi1Z"~id!("3bar","Qs")~"FZi");
+ static assert(bar.mangleof == "_D8link6574"~tl!"28"~"__T3barVE"~id!("8link6574","Qs")~"6Methodi1Z"~id!("3bar","Qs")~FZi);
return 10 * bar!method();
}
int bar(Method method : Method.A)()
@@ -43,7 +47,7 @@ int bar(Method method : Method.A)()
}
int bar(Method method : Method.B)()
{
- static assert(bar.mangleof == "_D8link6574"~tl!"29"~"__T3barHVE"~id!("8link6574","Qt")~"6Methodi1Z"~id!("3bar","Qt")~"FZi");
+ static assert(bar.mangleof == "_D8link6574"~tl!"29"~"__T3barHVE"~id!("8link6574","Qt")~"6Methodi1Z"~id!("3bar","Qt")~FZi);
return 3;
}
diff --git a/gcc/testsuite/gdc.test/runnable/link7745.d b/gcc/testsuite/gdc.test/runnable/link7745.d
index fb36a9aae9e..9a0eccffef3 100644
--- a/gcc/testsuite/gdc.test/runnable/link7745.d
+++ b/gcc/testsuite/gdc.test/runnable/link7745.d
@@ -15,6 +15,6 @@ static assert(forceSemantic7745());
void f(C c) { auto x = &c.asdfg; }
void main() {
- // extra test for bug 4820
+ // https://issues.dlang.org/show_bug.cgi?id=4820
nextis!(int)();
}
diff --git a/gcc/testsuite/gdc.test/runnable/literal.d b/gcc/testsuite/gdc.test/runnable/literal.d
index 60bac918ec8..99b177759b4 100644
--- a/gcc/testsuite/gdc.test/runnable/literal.d
+++ b/gcc/testsuite/gdc.test/runnable/literal.d
@@ -1,3 +1,9 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern(C) int printf(const char*, ...);
@@ -165,7 +171,7 @@ void test2()
}
/***************************************************/
-// 13907
+// https://issues.dlang.org/show_bug.cgi?id=13907
void f13907_1(wchar[1] a) {}
void f13907_2(wchar[2] a) {}
@@ -185,8 +191,8 @@ void test13907()
static assert(!__traits(compiles, { f13907_1("\U00010000" ); }));
f13907_2("\U00010000"w);
f13907_2("\U00010000");
- f13907_3("\U00010000"w); // Re-enable implicit length extension, from issue 13999
- f13907_3("\U00010000" ); // Re-enable implicit length extension, from issue 13999
+ f13907_3("\U00010000"w); // Re-enable implicit length extension, from https://issues.dlang.org/show_bug.cgi?id=13999
+ f13907_3("\U00010000" ); // Re-enable implicit length extension, from https://issues.dlang.org/show_bug.cgi?id=13999
assert(f13907_12("a") == 1);
assert(f13907_12("ab") == 2);
@@ -210,13 +216,13 @@ void test13907()
static wchar[20] wsa = "hello world"; // ok
static dchar[20] dsa = "hello world"; // ok
- // Bugzilla 13966
+ // https://issues.dlang.org/show_bug.cgi?id=13966
string[1][] arr;
arr ~= ["class"];
enum immutable(char[5]) sarrstr = "class";
arr ~= [sarrstr];
- // Bugzilla 13999
+ // https://issues.dlang.org/show_bug.cgi?id=13999
string[dchar[2]] aa13999 = ["ã‚": "bar"];
assert(aa13999["ã‚"] == "bar");
dchar[2] key13999 = "ã‚";
@@ -225,6 +231,16 @@ void test13907()
assert(aa13999[key13999] == "bar");
}
+ulong op12950(ulong v){return v + 12950;}
+
+void test12950()
+{
+ assert(0x00_00_00_01.op12950() == 12951);
+ assert(0x00_00_00_01UL.op12950() == 12951);
+ assert(0b00_00_00_01.op12950() == 12951);
+ assert(0b00_00_00_01UL.op12950() == 12951);
+}
+
/***************************************************/
int main()
@@ -232,6 +248,7 @@ int main()
test1();
test2();
test13907();
+ test12950();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/loopunroll.d b/gcc/testsuite/gdc.test/runnable/loopunroll.d
index 19e2db2a03a..b614d1bdf7f 100644
--- a/gcc/testsuite/gdc.test/runnable/loopunroll.d
+++ b/gcc/testsuite/gdc.test/runnable/loopunroll.d
@@ -1,5 +1,22 @@
-
-/* PERMUTE_ARGS: -O
+/*
+PERMUTE_ARGS: -O
+RUN_OUTPUT:
+---
+0
+45
+45
+45
+45
+45
+45
+45
+10
+45
+0
+5
+45
+45
+---
*/
import core.stdc.stdio;
diff --git a/gcc/testsuite/gdc.test/runnable/m1.d b/gcc/testsuite/gdc.test/runnable/m1.d
index 49a3d15bc3e..5689ffdb367 100644
--- a/gcc/testsuite/gdc.test/runnable/m1.d
+++ b/gcc/testsuite/gdc.test/runnable/m1.d
@@ -1,9 +1,15 @@
-// EXTRA_SOURCES: imports/m1a.d
-// PERMUTE_ARGS:
+/*
+EXTRA_SOURCES: imports/m1a.d
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+Success
+---
+*/
module m1;
-import std.stdio;
+import core.stdc.stdio;
import imports.m1a;
diff --git a/gcc/testsuite/gdc.test/runnable/manboy.d b/gcc/testsuite/gdc.test/runnable/manboy.d
index 038622668f2..8e533074efb 100644
--- a/gcc/testsuite/gdc.test/runnable/manboy.d
+++ b/gcc/testsuite/gdc.test/runnable/manboy.d
@@ -1,6 +1,6 @@
// PERMUTE_ARGS:
-import std.stdio;
+import core.stdc.stdio;
int a(int k, lazy int x1, lazy int x2, lazy int x3, lazy int x4, lazy int x5)
{
diff --git a/gcc/testsuite/gdc.test/runnable/mangle.d b/gcc/testsuite/gdc.test/runnable/mangle.d
index 9a8420aa20b..7599e0e03c3 100644
--- a/gcc/testsuite/gdc.test/runnable/mangle.d
+++ b/gcc/testsuite/gdc.test/runnable/mangle.d
@@ -14,7 +14,8 @@ false
import imports.testmangle;
/***************************************************/
-// 10077 - pragma(mangle)
+// https://issues.dlang.org/show_bug.cgi?id=10077
+// pragma(mangle)
pragma(mangle, "_test10077a_") int test10077a;
static assert(test10077a.mangleof == "_test10077a_");
@@ -54,7 +55,7 @@ void test10077i()
}
/***************************************************/
-// 13050
+// https://issues.dlang.org/show_bug.cgi?id=13050
void func13050(int);
template decl13050(Arg)
@@ -79,7 +80,7 @@ static assert(is(typeof(&problem13050!int) == void function(int)));
static assert(is(typeof(&workaround13050!int) == void function(int)));
/***************************************************/
-// 2774
+// https://issues.dlang.org/show_bug.cgi?id=2774
int foo2774(int n) { return 0; }
static assert(foo2774.mangleof == "_D6mangle7foo2774FiZi");
@@ -100,7 +101,7 @@ void test2774()
}
/*******************************************/
-// 8847
+// https://issues.dlang.org/show_bug.cgi?id=8847
auto S8847()
{
@@ -294,7 +295,7 @@ static assert(typeof(f8847a()).mangleof == "S6mangle6f8847aFNaZ1S");
static assert(typeof(f8847b()).mangleof == "S6mangle6f8847bFNaZ1S");
/*******************************************/
-// 12352
+// https://issues.dlang.org/show_bug.cgi?id=12352
auto bar12352()
{
@@ -325,7 +326,7 @@ static assert(typeof(baz12352()) .mangleof == "C6mangle8baz12352FZ1C");
static assert(typeof(baz12352()).func.mangleof == "_D6mangle8baz12352FZ1C4funcMFZv");
/*******************************************/
-// 9525
+// https://issues.dlang.org/show_bug.cgi?id=9525
void f9525(T)(in T*) { }
@@ -334,27 +335,29 @@ void test9525()
enum result1 = "S6mangle8test9525FZ"~tl!"26"~"__T5test1S"~tl!"13"~id!("6mangle","QBc")~"5f9525Z"~id!("5test1","Qr")~"MFZ1S";
enum result2 = "S6mangle8test9525FZ"~tl!"26"~"__T5test2S"~tl!"13"~id!("6mangle","QBc")~"5f9525Z"~id!("5test2","Qr")~"MFNaNbZ1S";
- void test1(alias a)()
+ bool test1(alias a)()
{
static struct S {}
static assert(S.mangleof == result1);
S s;
a(&s); // Error: Cannot convert &S to const(S*) at compile time
+ return true;
}
- static assert((test1!f9525(), true));
+ enum evalTest1 = test1!f9525();
- void test2(alias a)() pure nothrow
+ bool test2(alias a)() pure nothrow
{
static struct S {}
static assert(S.mangleof == result2);
S s;
a(&s); // Error: Cannot convert &S to const(S*) at compile time
+ return true;
}
- static assert((test2!f9525(), true));
+ enum evalTest2 = test2!f9525();
}
/******************************************/
-// 10249
+// https://issues.dlang.org/show_bug.cgi?id=10249
template Seq10249(T...) { alias Seq10249 = T; }
@@ -390,7 +393,7 @@ static: // necessary to make overloaded symbols accessible via __traits(getOverl
}
/*******************************************/
-// 11718
+// https://issues.dlang.org/show_bug.cgi?id=11718
struct Ty11718(alias sym) {}
@@ -431,7 +434,7 @@ void test11718()
}
/*******************************************/
-// 11776
+// https://issues.dlang.org/show_bug.cgi?id=11776
struct S11776(alias fun) { }
@@ -454,7 +457,7 @@ void test11776()
}
/***************************************************/
-// 12044
+// https://issues.dlang.org/show_bug.cgi?id=12044
struct S12044(T)
{
@@ -481,7 +484,7 @@ void test12044()
}
/*******************************************/
-// 12217
+// https://issues.dlang.org/show_bug.cgi?id=12217
void test12217(int)
{
@@ -499,7 +502,7 @@ void test12217(int)
void test12217() {}
/***************************************************/
-// 12231
+// https://issues.dlang.org/show_bug.cgi?id=12231
void func12231a()()
if (is(typeof({
@@ -599,6 +602,13 @@ void fooB(void delegate (void delegate()) scope dg)
//pragma(msg, fooB.mangleof);
static assert(typeof(fooA).mangleof != typeof(fooB).mangleof);
+
+/***************************************************/
+
+@live int testLive() { return 42; }
+
+static assert(testLive.mangleof == "_D6mangle8testLiveFNmZi");
+
/***************************************************/
alias noreturn = typeof(*null);
@@ -608,6 +618,12 @@ static assert(funcd.mangleof == "_D6mangle5funcdFPFZNnZi");
/***************************************************/
+struct S21753 { void function() f1; }
+void fun21753(S21753 v)() {}
+alias fl21753 = (){};
+static assert((fun21753!(S21753(fl21753))).mangleof == "_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10" ~ fl21753.stringof ~ "MFNaNbNiNfZvZQCbQp");
+
+/***************************************************/
void main()
{
test10077h();
diff --git a/gcc/testsuite/gdc.test/runnable/mars1.d b/gcc/testsuite/gdc.test/runnable/mars1.d
index 4d17d33aebf..4f38f4d3322 100644
--- a/gcc/testsuite/gdc.test/runnable/mars1.d
+++ b/gcc/testsuite/gdc.test/runnable/mars1.d
@@ -1,259 +1,36 @@
/*
-RUNNABLE_PHOBOS_TEST
-REQUIRED_ARGS: -mcpu=native
-PERMUTE_ARGS: -O -inline
+REQUIRED_ARGS: -mcpu=native -preview=intpromote
+PERMUTE_ARGS: -O -inline -release
*/
import core.stdc.stdio;
-void testgoto()
-{
- int i;
-
- i = 3;
- goto L4;
-L3: i++;
- goto L5;
-L4: goto L3;
-L5: assert(i == 4);
-}
-
-int testswitch()
-{
- int i;
-
- i = 3;
- switch (i)
- {
- case 0:
- case 1:
- default:
- assert(0);
- case 3:
- break;
- }
- return 0;
-}
-
-void testdo()
-{
- int x = 0;
-
- do
- {
- x++;
- } while (x < 10);
- printf("x == %d\n", x);
- assert(x == 10);
-}
-
-
-void testbreak()
-{ int i, j;
-
- Louter:
- for (i = 0; i < 10; i++)
- {
- for (j = 0; j < 10; j++)
- {
- if (j == 3)
- break Louter;
- }
- }
-
- printf("i = %d, j = %d\n", i, j);
- assert(i == 0);
- assert(j == 3);
-}
+template tuple(A...) { alias tuple = A; }
///////////////////////
-int foo(string s)
-{
- int i;
-
- i = 0;
- switch (s)
- {
- case "hello":
- i = 1;
- break;
- case "goodbye":
- i = 2;
- break;
- case "goodb":
- i = 3;
- break;
- default:
- i = 10;
- break;
- }
- return i;
-}
-
-
-void teststringswitch()
-{ int i;
-
- i = foo("hello");
- printf("i = %d\n", i);
- assert(i == 1);
-
- i = foo("goodbye");
- printf("i = %d\n", i);
- assert(i == 2);
-
- i = foo("goodb");
- printf("i = %d\n", i);
- assert(i == 3);
-
- i = foo("huzzah");
- printf("i = %d\n", i);
- assert(i == 10);
-}
-
-
-///////////////////////
+// https://github.com/dlang/dmd/pull/11441
-struct Foo
+long sdiv1(long l)
{
- int a;
- char b;
- long c;
+ return l / 2;
}
-Foo test(Foo f)
+int sdiv2(int i)
{
- f.a += 1;
- f.b += 3;
- f.c += 4;
- return f;
+ return i / 2;
}
-
-void teststrarg()
+void testsdiv2()
{
- Foo g;
- g.a = 1;
- g.b = 2;
- g.c = 3;
-
- Foo q;
- q = test(g);
- assert(q.a == 2);
- assert(q.b == 5);
- assert(q.c == 7);
+ assert(sdiv1(10) == 5);
+ assert(sdiv1(-10) == -5);
+ assert(sdiv2(10) == 5);
+ assert(sdiv2(-10) == -5);
}
///////////////////////
-align (1) struct Foo1
-{
- align (1):
- int a;
- char b;
- long c;
-}
-
-struct Foo2
-{
- int a;
- char b;
- long c;
-}
-
-struct Foo3
-{
- int a;
- align (1) char b;
- long c;
-}
-
-struct Foo4
-{
- int a;
- struct { char b; }
- long c;
-}
-
-void testsizes()
-{
- printf("%d\n", Foo1.sizeof);
- assert(Foo1.a.offsetof == 0);
- assert(Foo1.b.offsetof == 4);
- assert(Foo1.c.offsetof == 5);
- assert(Foo1.sizeof == 13);
-
- assert(Foo2.a.offsetof == 0);
- assert(Foo2.b.offsetof == 4);
- assert(Foo2.c.offsetof == 8);
- assert(Foo2.sizeof == 16);
-
- assert(Foo3.a.offsetof == 0);
- assert(Foo3.b.offsetof == 4);
- assert(Foo3.c.offsetof == 8);
- assert(Foo3.b.sizeof == 1);
- assert(Foo3.sizeof == 16);
-
- assert(Foo4.sizeof == 16);
-}
-
-///////////////////////
-
-size_t cond11565(size_t val)
-{
- return val ? size_t.max : 0;
-}
-
-void test11565()
-{
- assert(cond11565(true) == size_t.max);
-}
-
-///////////////////////
-
-int[3] array1 = [1:1,2,0:3];
-
-void testarrayinit()
-{
- assert(array1[0] == 3);
- assert(array1[1] == 1);
- assert(array1[2] == 2);
-}
-
-///////////////////////
-
-void test13023(ulong n)
-{
- static void func(bool b) {}
-
- ulong k = 0;
-
- func(k >= n / 2);
-
- if (k >= n / 2)
- assert(0);
-}
-
-///////////////////////
-
-struct U { int a; union { char c; int d; } long b; }
-
-U f = { b:3, d:0x22222222, a:1 };
-
-void testU()
-{
- assert(f.b == 3);
- assert(f.d == 0x22222222);
- assert(f.c == 0x22);
- assert(f.a == 1);
- assert(f.sizeof == 16);
- assert(U.sizeof == 16);
-}
-
-
-///////////////////////
-
void testulldiv()
{
__gshared ulong[4][] vectors =
@@ -280,19 +57,18 @@ void testulldiv()
{
ulong q = vectors[i][0] / vectors[i][1];
if (q != vectors[i][2])
- printf("[%d] %lld / %lld = %lld, should be %lld\n",
- vectors[i][0], vectors[i][1], q, vectors[i][2]);
+ printf("[%zd] %lld / %lld = %lld, should be %lld\n",
+ i, vectors[i][0], vectors[i][1], q, vectors[i][2]);
ulong r = vectors[i][0] % vectors[i][1];
if (r != vectors[i][3])
- printf("[%d] %lld %% %lld = %lld, should be %lld\n",
+ printf("[%zd] %lld %% %lld = %lld, should be %lld\n",
i, vectors[i][0], vectors[i][1], r, vectors[i][3]);
}
}
////////////////////////////////////////////////////////////////////////
-
uint udiv10(uint x)
{
return x / 10;
@@ -427,307 +203,498 @@ void testfastudiv()
////////////////////////////////////////////////////////////////////////
-void vfunc() {}
-
-void test12095(int k)
-{
- int e = 0;
- e ? k || assert(0) : !e || vfunc();
- e ? k || assert(0) : e && vfunc();
- !e ? !e || vfunc() : k || assert(0);
+// https://issues.dlang.org/show_bug.cgi?id=14936
+
+long sldiv1 (long x) { return x / (1L << 1); }
+long sldiv2 (long x) { return x / (1L << 2); }
+long sldiv3 (long x) { return x / (1L << 3); }
+long sldiv7 (long x) { return x / (1L << 7); }
+long sldiv8 (long x) { return x / (1L << 8); }
+long sldiv9 (long x) { return x / (1L << 9); }
+long sldiv30(long x) { return x / (1L << 30); }
+long sldiv31(long x) { return x / (1L << 31); }
+long sldiv32(long x) { return x / (1L << 32); }
+long sldiv33(long x) { return x / (1L << 33); }
+long sldiv34(long x) { return x / (1L << 34); }
+long sldiv62(long x) { return x / (1L << 62); }
+long sldiv63(long x) { return x / (1L << 63); }
+
+void testsldiv()
+{
+ /* Test special div code for signed long divide
+ * by power of 2 for 32 bit targets.
+ */
+
+ // printf("63 = %llx\n", sldiv63(-0x7FFF_F8FF_FF3F_2FFFL));
+
+ static foreach (C; tuple!(
+ 1,2,3,10,300,1000,
+ 4_1001_2030_0030,
+ 0x7FFF_F8FF_FF3F_2FFFL))
+ {
+ /* Check if runtime computation matches compile time
+ */
+ assert(sldiv1 ( C) == C / (1L << 1));
+ assert(sldiv1 (-C) == -C / (1L << 1));
+ assert(sldiv2 ( C) == C / (1L << 2));
+ assert(sldiv2 (-C) == -C / (1L << 2));
+ assert(sldiv3 ( C) == C / (1L << 3));
+ assert(sldiv3 (-C) == -C / (1L << 3));
+ assert(sldiv7 ( C) == C / (1L << 7));
+ assert(sldiv7 (-C) == -C / (1L << 7));
+ assert(sldiv8 ( C) == C / (1L << 8));
+ assert(sldiv8 (-C) == -C / (1L << 8));
+ assert(sldiv9 ( C) == C / (1L << 9));
+ assert(sldiv9 (-C) == -C / (1L << 9));
+
+ assert(sldiv30( C) == C / (1L << 30));
+ assert(sldiv30(-C) == -C / (1L << 30));
+ assert(sldiv31( C) == C / (1L << 31));
+ assert(sldiv31(-C) == -C / (1L << 31));
+ assert(sldiv32( C) == C / (1L << 32));
+ assert(sldiv32(-C) == -C / (1L << 32));
+ assert(sldiv33( C) == C / (1L << 33));
+ assert(sldiv33(-C) == -C / (1L << 33));
+ assert(sldiv34( C) == C / (1L << 34));
+ assert(sldiv34(-C) == -C / (1L << 34));
+ assert(sldiv62( C) == C / (1L << 62));
+ assert(sldiv62(-C) == -C / (1L << 62));
+ assert(sldiv63( C) == C / (1L << 63));
+ assert(sldiv63(-C) == -C / (1L << 63));
+ }
}
+////////////////////////////////////////////////////////////////////////
+
+// https://issues.dlang.org/show_bug.cgi?id=14936
+
+long slmod1 (long x) { return x % (1L << 1); }
+long slmod2 (long x) { return x % (1L << 2); }
+long slmod3 (long x) { return x % (1L << 3); }
+long slmod7 (long x) { return x % (1L << 7); }
+long slmod8 (long x) { return x % (1L << 8); }
+long slmod9 (long x) { return x % (1L << 9); }
+long slmod30(long x) { return x % (1L << 30); }
+long slmod31(long x) { return x % (1L << 31); }
+long slmod32(long x) { return x % (1L << 32); }
+long slmod33(long x) { return x % (1L << 33); }
+long slmod34(long x) { return x % (1L << 34); }
+long slmod62(long x) { return x % (1L << 62); }
+long slmod63(long x) { return x % (1L << 63); }
+
+void testslmod()
+{
+ static foreach (C; tuple!(
+ 1,2,3,10,300,1000,
+ 4_1001_2030_0030,
+ 0x7FFF_F8FF_FF3F_2FFFL))
+ {
+ /* Check if runtime computation matches compile time
+ */
+ assert(slmod1 ( C) == C % (1L << 1));
+ assert(slmod1 (-C) == -C % (1L << 1));
+ assert(slmod2 ( C) == C % (1L << 2));
+ assert(slmod2 (-C) == -C % (1L << 2));
+ assert(slmod3 ( C) == C % (1L << 3));
+ assert(slmod3 (-C) == -C % (1L << 3));
+ assert(slmod7 ( C) == C % (1L << 7));
+ assert(slmod7 (-C) == -C % (1L << 7));
+ assert(slmod8 ( C) == C % (1L << 8));
+ assert(slmod8 (-C) == -C % (1L << 8));
+ assert(slmod9 ( C) == C % (1L << 9));
+ assert(slmod9 (-C) == -C % (1L << 9));
+
+ assert(slmod30( C) == C % (1L << 30));
+ assert(slmod30(-C) == -C % (1L << 30));
+ assert(slmod31( C) == C % (1L << 31));
+ assert(slmod31(-C) == -C % (1L << 31));
+ assert(slmod32( C) == C % (1L << 32));
+ assert(slmod32(-C) == -C % (1L << 32));
+ assert(slmod33( C) == C % (1L << 33));
+ assert(slmod33(-C) == -C % (1L << 33));
+ assert(slmod34( C) == C % (1L << 34));
+ assert(slmod34(-C) == -C % (1L << 34));
+ assert(slmod62( C) == C % (1L << 62));
+ assert(slmod62(-C) == -C % (1L << 62));
+ assert(slmod63( C) == C % (1L << 63));
+ assert(slmod63(-C) == -C % (1L << 63));
+ }
+}
////////////////////////////////////////////////////////////////////////
+T divC(int C, T)(T x)
+{
+ T y = x;
+ y /= C;
+ assert(y == x / C);
+ y = x;
+ y /= -C;
+ assert(y == x / -C);
+ return x / C;
+}
-bool test3918a( float t, real u )
+T modC(int C, T)(T x)
{
- printf("%f\n", u );
- return t && u;
+ T y = x;
+ y %= C;
+ assert(y == x % C);
+ y = x;
+ y %= -C;
+ assert(y == x % -C);
+ return x % C;
}
-bool test3918b( real t, float u )
+T remquoC(int C, T)(T x)
{
- printf("%f\n", t );
- return t && u;
+ return (x / C) | (x % C);
}
-void test3918()
+void testfastdiv()
{
- assert(test3918a(float.nan, real.nan));
- assert(test3918b(real.nan, float.nan));
+ static int z = 0; // prevent constant folding by optimizer
+
+ static foreach (T; tuple!(int, long, uint, ulong))
+ {{
+ T u = 10000;
+ T r;
+ static foreach (C; tuple!(10, 14, 14007, -10, -14, -14007))
+ {
+ r = divC!C(u); assert(r == u / (z + C));
+ r = modC!C(u); assert(r == u % (z + C));
+ r = remquoC!C(u); assert(r == ((u / (z + C) | (u % (z + C)))));
+ }
+ }}
}
////////////////////////////////////////////////////////////////////////
-int div10(int x)
-{
- return x / 10;
-}
+/* Test the pattern:
+ * replace ((i / C1) / C2) with (i / (C1 * C2))
+ * when e1 is 0 or 1 and (i2-i1) is a power of 2.
+ */
-int div14(int x)
+void divdiv(T, T C1, T C2)(T i)
{
- return x / 14;
+ auto a = (i / C1) / C2;
+ auto b = i / (C1 * C2);
+ if (a != b) assert(0);
}
-int div14007(int x)
+void testdivdiv()
{
- return x / 14007;
+ divdiv!(int,10,20)(30);
+ divdiv!(uint,10,20)(30);
+ divdiv!(long,10,20)(30);
+ divdiv!(ulong,10,20)(30);
+
+ divdiv!(int,-10,20)(30);
+ divdiv!(long,-10,20)(30);
+
+ divdiv!(int,-10,-20)(-30);
+ divdiv!(long,-10,-20)(-30);
}
-int mod10(int x)
+////////////////////////////////////////////////////////////////////////
+
+void testdivcmp()
{
- return x % 10;
+ // https://github.com/dlang/dmd/pull/7128
+ static bool foo(uint a, uint b)
+ {
+ return cast(bool)(a / b); // convert / to >=
+ }
+
+ assert(!foo(3, 4));
+ assert(foo(4, 4));
+ assert(foo(5, 4));
}
-int mod14(int x)
+/////////////////////////////////////////////////////
+
+void testgoto()
{
- return x % 14;
+ int i;
+
+ i = 3;
+ goto L4;
+L3: i++;
+ goto L5;
+L4: goto L3;
+L5: assert(i == 4);
}
-int mod14007(int x)
+int testswitch()
{
- return x % 14007;
+ int i;
+
+ i = 3;
+ switch (i)
+ {
+ case 0:
+ case 1:
+ default:
+ assert(0);
+ case 3:
+ break;
+ }
+ return 0;
}
-int remquo10(int x)
+void testdo()
{
- return (x / 10) | (x % 10);
+ int x = 0;
+
+ do
+ {
+ x++;
+ } while (x < 10);
+ printf("x == %d\n", x);
+ assert(x == 10);
}
-int remquo14(int x)
-{
- return (x / 14) | (x % 14);
+
+void testbreak()
+{ int i, j;
+
+ Louter:
+ for (i = 0; i < 10; i++)
+ {
+ for (j = 0; j < 10; j++)
+ {
+ if (j == 3)
+ break Louter;
+ }
+ }
+
+ printf("i = %d, j = %d\n", i, j);
+ assert(i == 0);
+ assert(j == 3);
}
-int remquo14007(int x)
+///////////////////////
+
+int foo(string s)
{
- return (x / 14007) | (x % 14007);
+ int i;
+
+ i = 0;
+ switch (s)
+ {
+ case "hello":
+ i = 1;
+ break;
+ case "goodbye":
+ i = 2;
+ break;
+ case "goodb":
+ i = 3;
+ break;
+ default:
+ i = 10;
+ break;
+ }
+ return i;
}
-////////////////////
-int mdiv10(int x)
-{
- return x / -10;
+void teststringswitch()
+{ int i;
+
+ i = foo("hello");
+ printf("i = %d\n", i);
+ assert(i == 1);
+
+ i = foo("goodbye");
+ printf("i = %d\n", i);
+ assert(i == 2);
+
+ i = foo("goodb");
+ printf("i = %d\n", i);
+ assert(i == 3);
+
+ i = foo("huzzah");
+ printf("i = %d\n", i);
+ assert(i == 10);
}
-int mdiv14(int x)
+
+///////////////////////
+
+struct Foo
{
- return x / -14;
+ int a;
+ char b;
+ long c;
}
-int mdiv14007(int x)
+Foo test(Foo f)
{
- return x / -14007;
+ f.a += 1;
+ f.b += 3;
+ f.c += 4;
+ return f;
}
-int mmod10(int x)
+
+void teststrarg()
{
- return x % -10;
+ Foo g;
+ g.a = 1;
+ g.b = 2;
+ g.c = 3;
+
+ Foo q;
+ q = test(g);
+ assert(q.a == 2);
+ assert(q.b == 5);
+ assert(q.c == 7);
}
-int mmod14(int x)
+///////////////////////
+
+align (1) struct Foo1
{
- return x % -14;
+ align (1):
+ int a;
+ char b;
+ long c;
}
-int mmod14007(int x)
+struct Foo2
{
- return x % -14007;
+ int a;
+ char b;
+ long c;
}
-int mremquo10(int x)
+struct Foo3
{
- return (x / -10) | (x % -10);
+ int a;
+ align (1) char b;
+ long c;
}
-int mremquo14(int x)
+struct Foo4
{
- return (x / -14) | (x % -14);
+ int a;
+ struct { char b; }
+ long c;
}
-int mremquo14007(int x)
+void testsizes()
{
- return (x / -14007) | (x % -14007);
-}
+ printf("%zd\n", Foo1.sizeof);
+ assert(Foo1.a.offsetof == 0);
+ assert(Foo1.b.offsetof == 4);
+ assert(Foo1.c.offsetof == 5);
+ assert(Foo1.sizeof == 13);
-////////////////////
+ assert(Foo2.a.offsetof == 0);
+ assert(Foo2.b.offsetof == 4);
+ assert(Foo2.c.offsetof == 8);
+ assert(Foo2.sizeof == 16);
+ assert(Foo3.a.offsetof == 0);
+ assert(Foo3.b.offsetof == 4);
+ assert(Foo3.c.offsetof == 8);
+ assert(Foo3.b.sizeof == 1);
+ assert(Foo3.sizeof == 16);
-long ldiv10(long x)
-{
- return x / 10;
+ assert(Foo4.sizeof == 16);
}
-long ldiv14(long x)
-{
- return x / 14;
-}
+///////////////////////
-long ldiv14007(long x)
+size_t cond11565(size_t val)
{
- return x / 14007;
+ return val ? size_t.max : 0;
}
-long lmod10(long x)
+void test11565()
{
- return x % 10;
+ assert(cond11565(true) == size_t.max);
}
-long lmod14(long x)
-{
- return x % 14;
-}
+///////////////////////
-long lmod14007(long x)
-{
- return x % 14007;
-}
+int[3] array1 = [1:1,2,0:3];
-long lremquo10(long x)
+void testarrayinit()
{
- return (x / 10) | (x % 10);
+ assert(array1[0] == 3);
+ assert(array1[1] == 1);
+ assert(array1[2] == 2);
}
-long lremquo14(long x)
-{
- return (x / 14) | (x % 14);
-}
+///////////////////////
-long lremquo14007(long x)
+void test13023(ulong n)
{
- return (x / 14007) | (x % 14007);
+ static void func(bool b) {}
+
+ ulong k = 0;
+
+ func(k >= n / 2);
+
+ if (k >= n / 2)
+ assert(0);
}
+///////////////////////
-////////////////////
+struct U { int a; union { char c; int d; } long b; }
+U f = { b:3, d:0x22222222, a:1 };
-long mldiv10(long x)
+void testU()
{
- return x / -10;
+ assert(f.b == 3);
+ assert(f.d == 0x22222222);
+ assert(f.c == 0x22);
+ assert(f.a == 1);
+ assert(f.sizeof == 16);
+ assert(U.sizeof == 16);
}
-long mldiv14(long x)
-{
- return x / -14;
-}
-long mldiv14007(long x)
-{
- return x / -14007;
-}
+////////////////////////////////////////////////////////////////////////
-long mlmod10(long x)
-{
- return x % -10;
-}
+void vfunc() {}
-long mlmod14(long x)
+void test12095(int k)
{
- return x % -14;
+ int e = 0;
+ e ? k || assert(0) : !e || vfunc();
+ e ? k || assert(0) : e && vfunc();
+ !e ? !e || vfunc() : k || assert(0);
}
-long mlmod14007(long x)
-{
- return x % -14007;
-}
-long mlremquo10(long x)
-{
- return (x / -10) | (x % -10);
-}
+////////////////////////////////////////////////////////////////////////
+
-long mlremquo14(long x)
+bool test3918a( float t, real u )
{
- return (x / -14) | (x % -14);
+ printf("%Lf\n", u );
+ return t && u;
}
-long mlremquo14007(long x)
+bool test3918b( real t, float u )
{
- return (x / -14007) | (x % -14007);
+ printf("%Lf\n", t );
+ return t && u;
}
-
-
-void testfastdiv()
+void test3918()
{
- {
- static int x10 = 10;
- static int x14 = 14;
- static int x14007 = 14007;
-
- int u = 10000;
- int r;
- r = div10(u); assert(r == u/x10);
- r = div14(u); assert(r == u/x14);
- r = div14007(u); assert(r == u/x14007);
- r = mod10(u); assert(r == u%x10);
- r = mod14(u); assert(r == u%x14);
- r = mod14007(u); assert(r == u%x14007);
- r = remquo10(u); assert(r == ((u/x10)|(u%x10)));
- r = remquo14(u); assert(r == ((u/x14)|(u%x14)));
- r = remquo14007(u); assert(r == ((u/x14007)|(u%x14007)));
- }
- {
- static int t10 = -10;
- static int t14 = -14;
- static int t14007 = -14007;
-
- int u = 10000;
- int r;
- r = mdiv10(u); assert(r == u/t10);
- r = mdiv14(u); assert(r == u/t14);
- r = mdiv14007(u); assert(r == u/t14007);
- r = mmod10(u); assert(r == u%t10);
- r = mmod14(u); assert(r == u%t14);
- r = mmod14007(u); assert(r == u%t14007);
- r = mremquo10(u); assert(r == ((u/t10)|(u%t10)));
- r = mremquo14(u); assert(r == ((u/t14)|(u%t14)));
- r = mremquo14007(u); assert(r == ((u/t14007)|(u%t14007)));
- }
- {
- static long y10 = 10;
- static long y14 = 14;
- static long y14007 = 14007;
-
- long u = 10000;
- long r;
- r = ldiv10(u); assert(r == u/y10);
- r = ldiv14(u); assert(r == u/y14);
- r = ldiv14007(u); assert(r == u/y14007);
- r = lmod10(u); assert(r == u%y10);
- r = lmod14(u); assert(r == u%y14);
- r = lmod14007(u); assert(r == u%y14007);
- r = lremquo10(u); assert(r == ((u/y10)|(u%y10)));
- r = lremquo14(u); assert(r == ((u/y14)|(u%y14)));
- r = lremquo14007(u); assert(r == ((u/y14007)|(u%y14007)));
- }
- {
- static long z10 = -10;
- static long z14 = -14;
- static long z14007 = -14007;
-
- long u = 10000;
- long r;
- r = mldiv10(u); assert(r == u/z10);
- r = mldiv14(u); assert(r == u/z14);
- r = mldiv14007(u); assert(r == u/z14007);
- r = mlmod10(u); assert(r == u%z10);
- r = mlmod14(u); assert(r == u%z14);
- r = mlmod14007(u); assert(r == u%z14007);
- r = mlremquo10(u); assert(r == ((u/z10)|(u%z10)));
- r = mlremquo14(u); assert(r == ((u/z14)|(u%z14)));
- r = mlremquo14007(u); assert(r == ((u/z14007)|(u%z14007)));
- }
+ assert(test3918a(float.nan, real.nan));
+ assert(test3918b(real.nan, float.nan));
}
////////////////////////////////////////////////////////////////////////
-
T docond1(T)(T l, ubyte thresh, ubyte val) {
l += (thresh < val);
return l;
@@ -1041,25 +1008,6 @@ void testshrshl()
////////////////////////////////////////////////////////////////////////
-struct S1
-{
- cdouble val;
-}
-
-void formatTest(S1 s, double re, double im)
-{
- assert(s.val.re == re);
- assert(s.val.im == im);
-}
-
-void test10639()
-{
- S1 s = S1(3+2.25i);
- formatTest(s, 3, 2.25);
-}
-
-////////////////////////////////////////////////////////////////////////
-
bool bt10715(in uint[] ary, size_t bitnum)
{
return !!(ary[bitnum >> 5] & 1 << (bitnum & 31)); // uses bt
@@ -1245,6 +1193,19 @@ void test9449()
if (arr[0].g != 4.0) assert(0);
}
+struct Point9449x
+{
+ float f = 0.0;
+ double g = 0.0;
+}
+
+void test9449x()
+{
+ Point9449x[1] arr;
+ if (arr[0].f != 0.0) assert(0);
+ if (arr[0].g != 0.0) assert(0);
+}
+
////////////////////////////////////////////////////////////////////////
// https://issues.dlang.org/show_bug.cgi?id=12057
@@ -1351,18 +1312,6 @@ void test14829()
////////////////////////////////////////////////////////////////////////
-void test2()
-{
- void test(cdouble v)
- {
- auto x2 = cdouble(v);
- assert(x2 == v);
- }
- test(1.2+3.4i);
-}
-
-////////////////////////////////////////////////////////////////////////
-
void test3()
{
int[6] a;
@@ -1374,7 +1323,7 @@ void test3()
}
////////////////////////////////////////////////////////////////////////
-// 14782
+// https://issues.dlang.org/show_bug.cgi?id=14782
void test14782()
@@ -1477,7 +1426,7 @@ void test4()
double sumKBN(double s = 0.0)
{
- import std.math : fabs;
+ import core.math : fabs;
double c = 0.0;
foreach(double x; [1, 1e100, 1, -1e100])
{
@@ -1547,35 +1496,6 @@ void test16102()
////////////////////////////////////////////////////////////////////////
-
-/* Test the pattern:
- * replace ((i / C1) / C2) with (i / (C1 * C2))
- * when e1 is 0 or 1 and (i2-i1) is a power of 2.
- */
-
-void divdiv(T, T C1, T C2)(T i)
-{
- auto a = (i / C1) / C2;
- auto b = i / (C1 * C2);
- if (a != b) assert(0);
-}
-
-void testdivdiv()
-{
- divdiv!(int,10,20)(30);
- divdiv!(uint,10,20)(30);
- divdiv!(long,10,20)(30);
- divdiv!(ulong,10,20)(30);
-
- divdiv!(int,-10,20)(30);
- divdiv!(long,-10,20)(30);
-
- divdiv!(int,-10,-20)(-30);
- divdiv!(long,-10,-20)(-30);
-}
-
-////////////////////////////////////////////////////////////////////////
-
void test5a(ulong x, ulong y)
{
int a;
@@ -1663,8 +1583,904 @@ void testeqeqranges()
////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=16189
+
+void test16189()
+{
+ ubyte[9][1] data;
+ uint a = 0;
+ loop:
+ data[0] = data[a];
+ a--;
+ bool b = false;
+ if (b) goto loop;
+ assert(a == -1); // was failing with -O
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+// https://issues.dlang.org/show_bug.cgi?id=16997
+
+void test16997()
+{
+ /* Exhaustively test all signed and unsigned byte promotions for
+ * - + and ~
+ */
+ for (int i = 0; i < 256; ++i)
+ {
+ ubyte c = cast(ubyte)i;
+
+ int i1 = cast(int)(~c);
+ int i2 = cast(int)(~cast(int)c);
+
+ //printf("%d, %d\n", i1, i2);
+ assert(i1 == i2);
+
+ i1 = cast(int)(+c);
+ i2 = cast(int)(+cast(int)c);
+ assert(i1 == i2);
+
+ i1 = cast(int)(-c);
+ i2 = cast(int)(-cast(int)c);
+ assert(i1 == i2);
+ }
+
+ for (int i = 0; i < 256; ++i)
+ {
+ byte c = cast(byte)i;
+
+ int i1 = cast(int)(~c);
+ int i2 = cast(int)(~cast(int)c);
+
+ //printf("%d, %d\n", i1, i2);
+ assert(i1 == i2);
+
+ i1 = cast(int)(+c);
+ i2 = cast(int)(+cast(int)c);
+ assert(i1 == i2);
+
+ i1 = cast(int)(-c);
+ i2 = cast(int)(-cast(int)c);
+ assert(i1 == i2);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+void test18315() // https://issues.dlang.org/show_bug.cgi?id=18315
+{
+ int i = int.min;
+ bool b = i > 0;
+ assert(!b);
+ b = 0 < i;
+ assert(!b);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// https://issues.dlang.org/show_bug.cgi?id=18461
+
+void test18461()
+{
+ import core.bitop;
+
+ size_t test_val = 0b0001_0000;
+
+ if (bt(&test_val, 4) == 0)
+ assert(false);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+void test18730() // https://issues.dlang.org/show_bug.cgi?id=18730
+{
+ static if (size_t.sizeof == 8)
+ {
+ static int bt18730_64_64(in ulong* p, ulong bitnum) pure @system
+ {
+ return ((p[bitnum >> 6] & (1L << (bitnum & 63)))) != 0;
+ }
+ static int bt18730_64_32(in ulong* p, uint bitnum) pure @system
+ {
+ return ((p[bitnum >> 6] & (1L << (bitnum & 63)))) != 0;
+ }
+ static int bt18730_32_64(in uint* p, ulong bitnum) pure @system
+ {
+ return ((p[bitnum >> 5] & (1 << (bitnum & 31)))) != 0;
+ }
+
+ // Check that bt_64_64 uses a 64-bit register for the offset.
+ {
+ enum bitIndex = int.max + 1L;
+ auto a = new ulong[](bitIndex / 64 + 1);
+ a[bitIndex / 64] = 1;
+ assert(bt18730_64_64(a.ptr, bitIndex));
+ assert(!bt18730_64_64(a.ptr, bitIndex + 1));
+ assert(!bt18730_64_64(a.ptr, bitIndex - 1));
+ }
+ // Check that bt_64_32 uses a 32-bit register for the offset.
+ {
+ static int f(ulong* p, ulong bitnum)
+ {
+ return bt18730_64_32(p, cast(uint) bitnum);
+ }
+ enum bitIndex = uint.max + 1L;
+ assert(cast(uint) bitIndex == 0);
+ ulong s = 1;
+ assert(f(&s, bitIndex));
+ }
+ /* Check that bt_32_64 does not become a 64-bit bt instruction. Would lead
+ to a segfault when trying to load 8 bytes while only 4 are accessible. */
+ version (Posix)
+ {{
+ import core.sys.posix.sys.mman;
+ import core.sys.posix.unistd;
+ // Allocate two pages.
+ immutable sz = 2 * sysconf(_SC_PAGESIZE);
+ auto m = mmap(null, sz, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
+ // Discard the higher page. It becomes unreadable.
+ munmap(m + sz / 2, sz / 2);
+ // Try looking at the last 4 bytes of the readable page.
+ uint* p = cast(uint*) (m + sz / 2 - uint.sizeof);
+ bt18730_32_64(p, 0);
+ munmap(m, sz / 2); // Free the readable page.
+ }}
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+void test19497() // https://issues.dlang.org/show_bug.cgi?id=19497
+{
+ {
+ ubyte[1024] data;
+ ushort* ushortPtr = cast(ushort*) data.ptr;
+ *ushortPtr++ = 0xfe00;
+ printf("ushortPtr(%p)\n", ushortPtr);
+ fflush(stdout);
+ }
+
+ alias Seq(stuff ...) = stuff;
+ static foreach (T; Seq!(ubyte, ushort, uint, ulong, byte, short, int, long))
+ {{
+ T[2] data = 0x2A;
+ T* q = &data[0];
+ *q++ = cast(T) 0x1122334455667788;
+ if (*q != 0x2A) assert(false);
+ }}
+
+ {
+ static int toStringz(string s) { return s.length > 0 ? s[0] : 0; }
+ static void toAStringz(in string[] a, int* az)
+ {
+ foreach (string s; a)
+ {
+ *az++ = toStringz(s);
+ }
+ }
+ string[1] sa = ["abc"];
+ int[2] tgt = 0x2a;
+ toAStringz(sa[], tgt.ptr);
+ if (tgt[0] != 'a') assert(false);
+ if (tgt[1] != 0x2a) assert(false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// https://issues.dlang.org/show_bug.cgi?id=18794
+
+bool method18794(size_t* p)
+{
+ int bitIdx = 0;
+ func18794();
+ return (*p & (1UL << bitIdx)) != 0;
+}
+
+void func18794() {}
+
+void prep18794()
+{
+ asm {}
+ ulong[2] x = -1;
+}
+
+void test18794()
+{
+ prep18794();
+ size_t s;
+ method18794(&s);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+/* Test the optimization
+ * (e1+c)-e2 => (e1-e2)+c
+ */
+
+void testelmin()
+{
+ static void foo(int i)
+ {
+ static ubyte[4] bar()
+ {
+ ubyte[4] array;
+ foreach (i, ref a; array)
+ a = cast(ubyte)(i + 1);
+ return array;
+ }
+
+ static void test(int i, ubyte* p)
+ {
+ foreach (j; 0 .. 4)
+ assert(p[i * 4 + j] == j + 1);
+ }
+
+ ubyte[32] data;
+ data[i*4..(i+1)*4] = bar(); // optimize to single MOV
+
+ test(i, data.ptr);
+ }
+
+ foo(4);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+const(char)* fastpar(string s)
+{
+ return s.ptr + s.length;
+}
+
+void testfastpar()
+{
+ string s = "abcde";
+ auto p = fastpar(s);
+ assert(*p == 0);
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=20363
+
+ulong foo20363(double d)
+{
+ ulong u = * cast(ulong*) &d;
+ return (u >> 1) & 1;
+}
+
+void test20363()
+{
+ ulong u = 0b10;
+ if (foo20363(*cast(double*) &u) == 0)
+ assert(false);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+
+T testfooa(T)(T value)
+{
+ return 10 - (value * 57); // gets rewritten into (value*-57)+10
+}
+
+T testfoob(T)(T value)
+{
+ return (value * -57) + 10;
+}
+
+void testNegConst()
+{
+ assert(testfooa(1) == -47);
+ assert(testfoob(1) == -47);
+ assert(testfooa(1.0) == -47);
+ assert(testfoob(1.0) == -47);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// https://issues.dlang.org/show_bug.cgi?id=16317
+
+int add8ret3(ref int s)
+{
+ s += 8;
+ return 3;
+}
+
+int binAdd(int val)
+{
+ val = val + add8ret3(val);
+ return val;
+}
+
+void test16317()
+{
+ assert(binAdd(1) == (1 + 3));
+ static assert(binAdd(1) == (1 + 3));
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// https://issues.dlang.org/show_bug.cgi?id=20050
+
+int test20050_g = 0;
+void test20050_impure_function_1() { ++test20050_g; }
+void function() test20050_get_impure_function() pure
+{
+ static void impure_function_2()
+ {
+ ++test20050_g;
+ test20050_impure_function_1();
+ }
+ return &impure_function_2;
+}
+void test20050()
+{
+ auto f = test20050_get_impure_function();
+ f();
+ assert(test20050_g == 2);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// http://github.com/dlang/dmd/pull/11238
+
+int testCpStatic1(int y)
+{
+ __gshared int yyy = 7;
+ auto x = yyy; // no copy-propagation
+ if (y)
+ return x;
+ return x + 3;
+}
+
+void testCpStatic()
+{
+ assert(testCpStatic1(1) == 7);
+ assert(testCpStatic1(0) == 10);
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=20991
+
+int x7;
+
+void bar7(int i)
+{
+ assert(i == x7);
+ ++x7;
+}
+
+void test7()
+{
+ for (int i = 0; i <= 1; ++i)
+ bar7(i);
+ assert(x7 == 2);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// http://github.com/dlang/dmd/pull/11388
+
+ushort byteswap(ushort x) pure
+{
+ // Should be detected and XCHG instruction generated
+ return cast(ushort) (((x >> 8) & 0xFF) | ((x << 8) & 0xFF00u));
+}
+
+void testbyteswap()
+{
+ assert(byteswap(0xF234) == 0x34F2);
+ static ushort xx = 0xF234;
+ assert(byteswap(xx) == 0x34F2);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// These should all be recognized by the compiler and generate ROL or ROR
+// instructions.
+
+uint rol32(uint x, uint n)
+{
+ return (x << n) | (x >> (32 - n));
+}
+
+uint ror32(uint x, uint n)
+{
+ return (x >> n) | (x << (32 - n));
+}
+
+ulong rol64(ulong x, uint n)
+{
+ return (x << n) | (x >> (64 - n));
+}
+
+ulong ror64(ulong x, uint n)
+{
+ return (x >> n) | (x << (64 - n));
+}
+
+void testrolror()
+{
+ assert(ror32(0x0123_4567u, 4) == 0x7012_3456);
+ assert(rol32(0x7012_3456u, 4) == 0x0123_4567);
+
+ assert(ror64(0x0123_4567_89AB_CDEFuL, 4) == 0xF012_3456_789A_BCDE);
+ assert(rol64(0xF012_3456_789A_BCDEuL, 4) == 0x0123_4567_89AB_CDEF);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// https://issues.dlang.org/show_bug.cgi?id=20162
+
+void test20162()
+{
+ static long f(long a)
+ {
+ assert(a == -1L);
+ return a;
+ }
+
+ foreach (i; 1 .. 2)
+ {
+ foreach (j; 0 .. 2)
+ {
+ printf("%d %d %llx\n", i,
+ ((i != 0) ? -1 : +1),
+ f((i != 0) ? -1 : +1));
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=3713
+
+int star1(int i)
+{
+ return i ? star1(i - 1) : 0;
+}
+
+int star2(int i)
+{
+ return i == 0 ? 0 : star2(i - 1);
+}
+
+int star3(int i)
+{
+ if (i == 0)
+ return 0;
+ return i == 2 ? star3(i - 2) : star3(i - 1);
+}
+
+int star4(int i)
+{
+ return (i == 0) ? 0
+ : i != 2 ? star4(i - 1)
+ : star4(i - 2);
+}
+
+void test3713()
+{
+ assert(star1(10) == 0);
+ assert(star2(10) == 0);
+ assert(star3(10) == 0);
+ assert(star4(10) == 0);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+void testsbbrex()
+{
+ // special code is generated for these two cases
+ static long foolt(dchar c)
+ {
+ return c < 0x10000 ? 1 : 2;
+ }
+
+ static long fooge(uint c)
+ {
+ return c >= 0x10000 ? 1L : 2L;
+ }
+
+ assert(foolt(0) == 1);
+ assert(foolt(0x10000) == 2);
+ assert(fooge(0) == 2);
+ assert(fooge(0x10000) == 1);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+// https://issues.dlang.org/show_bug.cgi?id=19846
+
+alias Void = byte[0];
+static immutable Void VOID; // = [];
+
+__gshared int x19846;
+
+Void print19846()
+{
+ //printf("This should print\n");
+ x19846 = 3;
+ return VOID;
+}
+
+Void identity19846(Void value, out int i)
+{
+ i = 7;
+ return value;
+}
+
+void test19846()
+{
+ int i;
+ identity19846(print19846(), i);
+ //printf("i = %d\n", i);
+ assert(x19846 == 3);
+ assert(i == 7);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+// Some tests for OPmemcpy
+
+enum N = 128;
+
+ubyte[N] def()
+{
+ ubyte[N] array;
+ foreach (i, ref a; array)
+ a = cast(ubyte)(i + 1);
+ return array;
+}
+
+
+void ghi(ubyte* p)
+{
+ foreach (i; 0 .. N)
+ assert(p[i] == i + 1);
+}
+
+void testmemcpy()
+{
+ ubyte[N] bits;
+ ubyte[N] bits2;
+ bits2[0..N] = bits[0..N] = def();
+ ghi(bits.ptr);
+ ghi(bits2.ptr);
+
+ __gshared size_t n = N;
+ ubyte[N] bits3;
+ ubyte[N] bits4;
+ bits4[0..n] = bits3[0..n] = def();
+ ghi(bits3.ptr);
+ ghi(bits4.ptr);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+
+/* Test all the cases of uses of LEA for multiplication by a constant
+ */
+
+T testlea(uint C, T)(T x, T y)
+{
+ y = y * C; // cdmul()
+ x *= C; // cdmulass()
+ return x + y;
+}
+
+void testleax(uint C)(uint X, uint Y)
+{
+ assert(testlea!C(X,Y) == C * (X + Y));
+ assert(testlea!C(cast(long)X,cast(long)Y) == cast(long)C*X + cast(long)C*Y);
+}
+
+void testMulLea()
+{
+ testleax!3(10,11);
+ testleax!5(10,11);
+ testleax!6(10,11);
+ testleax!9(10,11);
+
+ testleax!10(10,11);
+ testleax!12(10,11);
+ testleax!18(10,11);
+ testleax!20(10,11);
+ testleax!24(10,11);
+ testleax!36(10,11);
+ testleax!40(10,11);
+ testleax!72(10,11);
+
+ testleax!37(10,11);
+ testleax!74(10,11);
+ testleax!13(10,11);
+ testleax!26(10,11);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+/* Test *= of register pair
+ */
+
+void testMulAssPair()
+{
+ static ulong pow(ulong x, int m)
+ {
+ ulong v = x;
+ ulong p = 1;
+ while (1)
+ {
+ if (m & 1)
+ p *= v;
+ m >>= 1;
+ if (!m)
+ break;
+ v *= v;
+ }
+ return p;
+ }
+
+ enum ulong e_10_pow_19 = 10uL^^19;
+ assert(e_10_pow_19 == pow(10uL, 19));
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=21038
+
+const(wchar)* x21038 = "xz";
+const(dchar)* name21038 = "abcd";
+
+void test21038()
+{
+ assert((cast(size_t) x21038) % wchar.sizeof == 0);
+ assert((cast(size_t) name21038) % dchar.sizeof == 0);
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=21325
+
+real f21325(const real x) pure @safe nothrow @nogc
+{
+ return (x != 0.0L) ? x : real.nan;
+}
+
+void test21325() @safe
+{
+ ulong x = 0uL;
+ while(true)
+ {
+ const y = f21325(x); // should set y to real.nan
+
+ assert(y != y);
+
+ if (++x)
+ return; // good
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=16274
+
+extern(C) int pair(short a, ushort b, byte c, ubyte d);
+
+struct S
+{
+ // provide alternate implementation of .pair()
+ pragma(mangle, "pair")
+ extern(C) static void pair(int a, int b, int c, int d)
+ {
+ //printf("%d %d %d %d\n", a, b, c, d);
+ assert(a == -1);
+ assert(b == 2);
+ assert(c == -3);
+ assert(d == 4);
+ }
+}
+
+void test16274()
+{
+ version (X86_64)
+ pair(-1, 2, -3, 4);
+ version (X86)
+ pair(-1, 2, -3, 4);
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=16268
+
+void test16268()
+{
+ static void f(byte x)
+ {
+ for (byte i = 0; i <= x && i >= 0; ++i)
+ {
+ assert(i >= 0);
+ assert(i != -1);
+ //printf("%d\n", i);
+ }
+ }
+
+ f(byte.max);
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=11435
+
+void test11435a()
+{
+ alias T = byte;
+
+ static void fun(T c, T b, int v)
+ {
+ }
+
+ static void abc(T[] b)
+ {
+ fun(b[0], b[1], 0);
+ }
+
+ version(Windows)
+ {
+ import core.sys.windows.windows;
+ auto p = VirtualAlloc(null, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ }
+ else
+ {
+ import core.sys.posix.sys.mman;
+ auto p = mmap(null, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0L);
+ }
+ assert(p);
+ auto px = (cast(T*)(p + 4096 - 2 * T.sizeof));
+ abc(px[0..2]);
+}
+
+void test11435b()
+{
+ import core.sys.windows.windows;
+ alias T = short;
+
+ static void fun(T c, T b, int v)
+ {
+ }
+
+ static void abc(T[] b)
+ {
+ fun(b[0], b[1], 0);
+ }
+
+ version(Windows)
+ {
+ import core.sys.windows.windows;
+ auto p = VirtualAlloc(null, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ }
+ else
+ {
+ import core.sys.posix.sys.mman;
+ auto p = mmap(null, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0L);
+ }
+ assert(p);
+ auto px = (cast(T*)(p + 4096 - 2 * T.sizeof));
+ abc(px[0..2]);
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=21513
+
+struct Stuff
+{
+ size_t c; // declare after items and not crash !
+ ubyte[1] items;
+}
+
+void grow(ref Stuff stuff)
+{
+ with (stuff)
+ {
+ const oldCapacity = c;
+ items.ptr[0..oldCapacity] = items.ptr[0..0]; // use literal 0 instead of
+ items.ptr[0] = 0; // oldcapacity and no crash !
+ }
+}
+
+void test21513()
+{
+ Stuff stuff;
+ grow(stuff);
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=21526
+
+double f21256(double a, double b) {
+ double c = a + b;
+ return c;
+}
+
+void test21256()
+{
+ union DX
+ {
+ double d;
+ ulong l;
+ }
+
+ DX a, b;
+ a.l = 0x4341c37937e08000;
+ b.l = 0x4007ffcb923a29c7;
+
+ DX r;
+ r.d = f21256(a.d, b.d);
+ //if (r.d != 0x1.1c37937e08001p+53)
+ //printf("r = %A should be 0x1.1c37937e08001p+53 %A\n", r.d, 0x1.1c37937e08001p+53);
+ //assert(r == 0x1.1c37937e08001p+53);
+
+ // cannot seem to get the two to produce the same value
+ assert(r.l == 0x4341c37937e08001 || // value using XMM
+ r.l == 0x4341c37937e08002); // value using x87
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=21816
+
+bool test21816a(float t)
+{
+ return cast(bool)t;
+}
+
+void test21816()
+{
+ assert(test21816a(float.nan));
+}
+
+////////////////////////////////////////////////////////////////////////
+// https://issues.dlang.org/show_bug.cgi?id=21835
+
+struct Point21835
+{
+ float f = 3.0;
+ double d = 4.0;
+ real r = 5.0;
+}
+
+void test21835y()
+{
+ Point21835[1] arr;
+ if (arr[0].f != 3.0) assert(0);
+ if (arr[0].d != 4.0) assert(0);
+ if (arr[0].r != 5.0) assert(0);
+}
+
+struct Point21835x
+{
+ float f = 0.0;
+ double d = 0.0;
+ real r = 0.0;
+}
+
+void test21835()
+{
+ test21835y();
+ Point21835x[1] arr;
+ if (arr[0].f != 0.0) assert(0);
+ if (arr[0].d != 0.0) assert(0);
+ if (arr[0].r != 0.0) assert(0);
+}
+
+////////////////////////////////////////////////////////////////////////
+
int main()
{
+ // All the various integer divide tests
+ testsdiv2();
+ testulldiv();
+ testfastudiv();
+ testsldiv();
+ testslmod();
+ testfastdiv();
+ testdivdiv();
+ testdivcmp();
+
testgoto();
testswitch();
testdo();
@@ -1675,11 +2491,8 @@ int main()
testsizes();
testarrayinit();
testU();
- testulldiv();
testbittest();
test8658();
- testfastudiv();
- testfastdiv();
test3918();
test12051();
testdocond();
@@ -1695,18 +2508,17 @@ int main()
test13190();
test13485();
test14436();
- test10639();
test10715();
test10678();
test7565();
test13023(0x10_0000_0000);
test12833();
test9449();
+ test9449x();
test12057();
test13784();
test14220();
test14829();
- test2();
test3();
test14782();
test14987();
@@ -1717,10 +2529,44 @@ int main()
test13474();
test16699();
test16102();
- testdivdiv();
test5();
test6();
testeqeqranges();
+ test16189();
+ test16997();
+ test18315();
+ test18461();
+ test18730();
+ test19497();
+ test18794();
+ testelmin();
+ testfastpar();
+ test20363();
+ testNegConst();
+ test16317();
+ test20050();
+ testCpStatic();
+ test7();
+ testbyteswap();
+ testrolror();
+ test20162();
+ test3713();
+ testsbbrex();
+ test19846();
+ testmemcpy();
+ testMulLea();
+ testMulAssPair();
+ test21038();
+ test21325();
+ test16274();
+ test16268();
+ test11435a();
+ test11435b();
+ test21513();
+ test21256();
+ test21816();
+ test21835();
+
printf("Success\n");
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/minimal.d b/gcc/testsuite/gdc.test/runnable/minimal.d
new file mode 100644
index 00000000000..2f2df71f894
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/minimal.d
@@ -0,0 +1,8 @@
+// DFLAGS:
+// PERMUTE_ARGS:
+// REQUIRED_ARGS: -defaultlib=
+// EXTRA_SOURCES: extra-files/minimal/object.d
+
+// This test ensures an empty main can be built and executed with a minimal runtime
+
+void main() { }
diff --git a/gcc/testsuite/gdc.test/runnable/minimal2.d b/gcc/testsuite/gdc.test/runnable/minimal2.d
new file mode 100644
index 00000000000..7319463e920
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/minimal2.d
@@ -0,0 +1,46 @@
+// DFLAGS:
+// REQUIRED_ARGS: -defaultlib=
+// EXTRA_SOURCES: extra-files/minimal/object.d
+
+// This test ensures that interfaces and classes can be used in a minimal
+// runtime as long as they only contain shared static members. Non-shared
+// static members would require a thread-local storage (TLS) implementation.
+
+interface I
+{
+ shared static int i;
+}
+
+class A : I
+{
+ shared static int a;
+}
+
+class B : A
+{
+ shared static int b;
+
+ static int sumAll()
+ {
+ return b + a + i;
+ }
+}
+
+void poorMansAssert(bool condition)
+{
+ if (!condition)
+ {
+ static char* hlt;
+ *hlt = 0;
+ }
+}
+
+void main()
+{
+ B.i = 32;
+ B.a = 42;
+ B.b = 52;
+
+ poorMansAssert(B.i == 32 || B.a == 42 || B.b == 52);
+ poorMansAssert(B.sumAll() == (32 + 42 + 52));
+}
diff --git a/gcc/testsuite/gdc.test/runnable/mixin1.d b/gcc/testsuite/gdc.test/runnable/mixin1.d
index 9d88fbd2841..d8b5516e586 100644
--- a/gcc/testsuite/gdc.test/runnable/mixin1.d
+++ b/gcc/testsuite/gdc.test/runnable/mixin1.d
@@ -1,7 +1,61 @@
-// RUNNABLE_PHOBOS_TEST
+/*
+TEST_OUTPUT:
+---
+runnable/mixin1.d(948): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+
+RUN_OUTPUT:
+---
+Foo3.func()
+Code3.func()
+Foo4.func()
+Foo5.func()
+b.x = 5
+x = 5
+duff_for(1, 11)
+fid = 1, 2
+foo12
+foo12
+foo13 j = 1
+foo13 j = 1
+x14 = 6
+x15 = 6
+bar15() = 5
+x16 = 6
+bar() = 5
+x17 = 5
+b.x17 = 5
+x17 = 3
+x17 = 5
+x17 = 4
+x17 = 3
+x17 = 5
+in C20.f()
+B22.foo()
+5
+5
+a = 0
+int
+int
+int
+int
+foo 1
+foo 2
+0 0
+two
+one
+one
+Class39 dtor
+Mixed-in dtor
+Mixed-in dtor
+Base39 dtor
+Success
+---
+*/
+
module mixin1;
-import std.stdio;
+import core.stdc.stdio;
alias TypeTuple(T...) = T;
@@ -680,7 +734,11 @@ class A30
{
this(Type[] arr)
{
- foreach(Type v; arr) writeln(typeid(typeof(v)));
+ foreach(Type v; arr)
+ {
+ const str = typeid(typeof(v)).toString();
+ printf("%.*s\n", cast(int)str.length, str.ptr);
+ }
}
}
@@ -744,11 +802,6 @@ template T33( int i )
printf("foo %d\n", i );
return i;
}
- int opCall()
- {
- printf("opCall %d\n", i );
- return i;
- }
}
@@ -766,10 +819,6 @@ void test33()
assert(i == 1);
i = c1.t2.foo();
assert(i == 2);
- i = c1.t1();
- assert(i == 1);
- i = c1.t2();
- assert(i == 2);
}
@@ -825,7 +874,6 @@ struct Foo36
void test36()
{
Foo36 f;
- printf("f.sizeof = %d\n", f.sizeof);
assert(f.sizeof == 12);
f.a = 1;
@@ -932,7 +980,7 @@ class Class39 : Base39
void test39()
{
- auto test = new Class39;
+ scope test = new Class39;
}
@@ -974,7 +1022,7 @@ void test41()
}
/*******************************************/
-// 2245
+// https://issues.dlang.org/show_bug.cgi?id=2245
template TCALL2245a(ARGS...)
{
@@ -1044,7 +1092,7 @@ void test2245()
}
/*******************************************/
-// 2481
+// https://issues.dlang.org/show_bug.cgi?id=2481
template M2481() { int i; }
class Z2481a { struct { mixin M2481!(); } }
@@ -1058,7 +1106,7 @@ void test2481()
}
/*******************************************/
-// 2740
+// https://issues.dlang.org/show_bug.cgi?id=2740
interface IFooable2740
{
@@ -1144,7 +1192,7 @@ void test42()
}
/*******************************************/
-// 7744
+// https://issues.dlang.org/show_bug.cgi?id=7744
class ZeroOrMore7744(Expr)
{
@@ -1165,7 +1213,7 @@ mixin(q{
});
/*******************************************/
-// 8032
+// https://issues.dlang.org/show_bug.cgi?id=8032
mixin template T8032()
{
@@ -1193,7 +1241,7 @@ class B8032b : A8032b
}
/*********************************************/
-// 9417
+// https://issues.dlang.org/show_bug.cgi?id=9417
mixin template Foo9417()
{
@@ -1209,7 +1257,7 @@ void test9417()
}
/*******************************************/
-// 11487
+// https://issues.dlang.org/show_bug.cgi?id=11487
template X11487()
{
@@ -1236,7 +1284,7 @@ class C11487
}
/*******************************************/
-// 11767
+// https://issues.dlang.org/show_bug.cgi?id=11767
mixin template M11767()
{
@@ -1262,7 +1310,7 @@ void test11767()
}
/*******************************************/
-// 12023
+// https://issues.dlang.org/show_bug.cgi?id=12023
void Delete12023(Object obj) {}
@@ -1303,7 +1351,7 @@ void test12023()
}
/*******************************************/
-// 14243
+// https://issues.dlang.org/show_bug.cgi?id=14243
mixin template Mix14243a(int n)
{
@@ -1397,7 +1445,7 @@ int test14243()
static assert(test14243()); // changed to be workable
/*******************************************/
-// 10492
+// https://issues.dlang.org/show_bug.cgi?id=10492
class TestClass10492 {}
diff --git a/gcc/testsuite/gdc.test/runnable/mixin2.d b/gcc/testsuite/gdc.test/runnable/mixin2.d
index 26a235210c9..7679bbe5b34 100644
--- a/gcc/testsuite/gdc.test/runnable/mixin2.d
+++ b/gcc/testsuite/gdc.test/runnable/mixin2.d
@@ -1,4 +1,4 @@
-/* RUNNABLE_PHOBOS_TEST
+/*
TEST_OUTPUT:
---
hello
@@ -31,7 +31,7 @@ hey
Success
---
*/
-import std.stdio;
+import core.stdc.stdio;
/*********************************************/
@@ -53,7 +53,7 @@ void test2()
mixin("
int x = 3;
for (int i = 0; i < 10; i++)
- writeln(x + i, ++j);
+ printf(\"%d%d\n\", x + i, ++j);
");
assert(j == 10);
}
@@ -64,7 +64,7 @@ mixin("int abc3 = 5;");
void test3()
{
- writeln(abc3);
+ printf("%d\n", abc3);
assert(abc3 == 5);
}
@@ -73,24 +73,24 @@ void test3()
mixin("
void test4()
{
- writeln(\"test4\");
+ printf(\"test4\n\");
" ~ "}");
/*********************************************/
int x5;
-scope class Foo5
+class Foo5
{
this ()
{
- writeln ("Constructor");
+ printf ("Constructor\n");
assert(x5 == 0);
x5++;
}
~this ()
{
- writeln ("Destructor");
+ printf ("Destructor\n");
assert(x5 == 2);
x5++;
}
@@ -100,7 +100,7 @@ void test5()
{
{
mixin ("scope Foo5 f = new Foo5;\n");
- writeln (" Inside Scope");
+ printf (" Inside Scope\n");
assert(x5 == 1);
x5++;
}
@@ -194,7 +194,7 @@ void test10()
}
/*********************************************/
-// 7560
+// https://issues.dlang.org/show_bug.cgi?id=7560
class Base7560
{
@@ -213,7 +213,7 @@ class Derived7560 : Base7560
}
/*********************************************/
-// 10577
+// https://issues.dlang.org/show_bug.cgi?id=10577
enum sync10577;
@@ -267,7 +267,7 @@ class derived10577 : base10577
}
/*********************************************/
-// 10583
+// https://issues.dlang.org/show_bug.cgi?id=10583
enum sync10583;
@@ -329,7 +329,7 @@ void test7156()
}
/*********************************************/
-// 7553
+// https://issues.dlang.org/show_bug.cgi?id=7553
template Foo7553()
{
@@ -358,7 +358,7 @@ void test7553()
}
/*********************************************/
-// 13479
+// https://issues.dlang.org/show_bug.cgi?id=13479
mixin template F13479()
{
@@ -392,5 +392,5 @@ void main()
test7156();
test13479();
- writeln("Success");
+ printf("Success\n");
}
diff --git a/gcc/testsuite/gdc.test/runnable/mod1.d b/gcc/testsuite/gdc.test/runnable/mod1.d
index 3efb0cf9308..46b14ed0ec3 100644
--- a/gcc/testsuite/gdc.test/runnable/mod1.d
+++ b/gcc/testsuite/gdc.test/runnable/mod1.d
@@ -1,5 +1,11 @@
-// PERMUTE_ARGS:
-// EXTRA_SOURCES: imports/mod2.d
+/*
+PERMUTE_ARGS:
+EXTRA_SOURCES: imports/mod2.d
+RUN_OUTPUT:
+---
+EvilOne
+---
+*/
// mod1.d
diff --git a/gcc/testsuite/gdc.test/runnable/nan.d b/gcc/testsuite/gdc.test/runnable/nan.d
index b229fd571f3..d4e4ae4ebdc 100644
--- a/gcc/testsuite/gdc.test/runnable/nan.d
+++ b/gcc/testsuite/gdc.test/runnable/nan.d
@@ -20,6 +20,43 @@ static assert(!(ed1 <= ed2));
bool b;
+
+T byCTFE(T)()
+{
+ T x;
+ return x;
+}
+
+bool bittst(const ubyte[] ba, uint pos)
+{
+ uint mask = 1 << (pos % 8);
+ version(LittleEndian)
+ return (ba[pos / 8] & mask) != 0;
+ else
+ return (ba[$ - 1 - pos / 8] & mask) != 0;
+}
+
+void test2(T)()
+{
+ T a = T.init, b = T.nan;
+ assert(a is b);
+
+ enum c = byCTFE!T();
+ assert(a is c);
+
+ static if (T.mant_dig == 64 && T.max_exp == 16384)
+ enum size = 10; // x87, exclude padding
+ else
+ enum size = T.sizeof;
+ const pa = (cast(ubyte*) &a)[0 .. size];
+
+ // the highest 2 bits of the mantissa should be set, everything else zero
+ assert(bittst(pa, T.mant_dig - 1));
+ assert(bittst(pa, T.mant_dig - 2));
+ foreach(p; 0..T.mant_dig - 2)
+ assert(!bittst(pa, p));
+}
+
bool test()
{
real r1 = real.nan;
@@ -53,5 +90,8 @@ bool test()
void main()
{
- assert(test());
+ assert(test());
+ test2!float();
+ test2!double();
+ test2!real();
}
diff --git a/gcc/testsuite/gdc.test/runnable/nested.d b/gcc/testsuite/gdc.test/runnable/nested.d
index 6b2fcb06ab2..4c99c59e8a9 100644
--- a/gcc/testsuite/gdc.test/runnable/nested.d
+++ b/gcc/testsuite/gdc.test/runnable/nested.d
@@ -863,8 +863,8 @@ class Foo35
//writefln("y = %s", y);
assert(x == 42);
assert(y == 43);
- //static assert(is(typeof(this.outer) == void*)); // Bugzilla 14442
- static assert(is(typeof(this.outer) == Foo35)); // Bugzilla 15839
+ //static assert(is(typeof(this.outer) == void*)); // https://issues.dlang.org/show_bug.cgi?id=14442
+ static assert(is(typeof(this.outer) == Foo35)); // https://issues.dlang.org/show_bug.cgi?id=15839
}
};
}
@@ -1485,7 +1485,20 @@ void test55()
}
/*******************************************/
-// 4401
+
+enum dg56 = delegate { return 5; };
+
+void test56()
+{
+ auto inner() {
+ return dg56();
+ }
+
+ assert(inner() == 5);
+}
+
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=4401
void test4401()
{
@@ -1542,7 +1555,7 @@ void test7428(){
}
/*******************************************/
-// 4612
+// https://issues.dlang.org/show_bug.cgi?id=4612
struct S4612a(alias x)
{
@@ -1604,7 +1617,7 @@ void test7199()
}
/*******************************************/
-// 7965
+// https://issues.dlang.org/show_bug.cgi?id=7965
void test7965()
{
@@ -1662,7 +1675,7 @@ void test7965a()
}
/*******************************************/
-// 8188
+// https://issues.dlang.org/show_bug.cgi?id=8188
mixin template Print8188(b...)
{
@@ -1687,7 +1700,7 @@ void test8188()
}
/*******************************************/
-// 5082
+// https://issues.dlang.org/show_bug.cgi?id=5082
struct S5082 { float x; }
@@ -1714,7 +1727,7 @@ void test5082()
/*******************************************/
-// 8194
+// https://issues.dlang.org/show_bug.cgi?id=8194
void test8194()
{
@@ -1727,7 +1740,7 @@ void test8194()
}
/*******************************************/
-// 8339
+// https://issues.dlang.org/show_bug.cgi?id=8339
template map8339a(fun...)
{
@@ -1822,7 +1835,7 @@ void test8339c()
}
/*******************************************/
-// 8704
+// https://issues.dlang.org/show_bug.cgi?id=8704
void check8704(T, int num)()
{
@@ -1849,7 +1862,7 @@ void test8704()
}
/*******************************************/
-// 8923
+// https://issues.dlang.org/show_bug.cgi?id=8923
void test8923a()
{
@@ -2029,7 +2042,7 @@ void test8923c()
}
/*******************************************/
-// 9003
+// https://issues.dlang.org/show_bug.cgi?id=9003
void test9003()
{
@@ -2072,7 +2085,7 @@ void test9003()
}
/*******************************************/
-// 9006
+// https://issues.dlang.org/show_bug.cgi?id=9006
void test9006()
{
@@ -2101,7 +2114,7 @@ void test9006()
}
/*******************************************/
-// 9035
+// https://issues.dlang.org/show_bug.cgi?id=9035
void test9035()
{
@@ -2144,7 +2157,7 @@ void test9035a()
}
/*******************************************/
-// 9036
+// https://issues.dlang.org/show_bug.cgi?id=9036
void test9036()
{
@@ -2201,7 +2214,7 @@ void test8863()
+/
/*******************************************/
-// 8774
+// https://issues.dlang.org/show_bug.cgi?id=8774
void popFront8774()
{
@@ -2274,7 +2287,7 @@ void test8832()
}
/*******************************************/
-// 9315
+// https://issues.dlang.org/show_bug.cgi?id=9315
auto test9315()
{
@@ -2287,7 +2300,7 @@ auto test9315()
}
/*******************************************/
-// 9244
+// https://issues.dlang.org/show_bug.cgi?id=9244
void test9244()
{
@@ -2298,7 +2311,7 @@ void test9244()
}
/*******************************************/
-// 10495
+// https://issues.dlang.org/show_bug.cgi?id=10495
struct X10495
{
@@ -2321,7 +2334,7 @@ class C10495
}
/*******************************************/
-// 11385
+// https://issues.dlang.org/show_bug.cgi?id=11385
auto map11385(alias fun, R)(R range)
{
@@ -2354,6 +2367,12 @@ enum foo11297 = function (int x)
xmap!(y => x)(7);
};
+enum goo11297 = delegate (int x)
+ {
+ //int bar(int y) { return x; } xmap!bar(7);
+ xmap!(y => x)(7);
+ };
+
void xreduce(alias f)()
{
f(4);
@@ -2362,10 +2381,11 @@ void xreduce(alias f)()
void test11297()
{
xreduce!foo11297();
+ xreduce!goo11297();
}
/*******************************************/
-// 11886
+// https://issues.dlang.org/show_bug.cgi?id=11886
struct Lambda11886(alias fun)
{
@@ -2388,7 +2408,7 @@ void test11886()
}
/*******************************************/
-// 12234
+// https://issues.dlang.org/show_bug.cgi?id=12234
void test12234()
{
@@ -2407,7 +2427,7 @@ void test12234()
}
/*******************************************/
-// 12981
+// https://issues.dlang.org/show_bug.cgi?id=12981
template Mix12981(T)
{
@@ -2425,7 +2445,7 @@ class B12981
}
/*******************************************/
-// 13861
+// https://issues.dlang.org/show_bug.cgi?id=13861
struct Foo13861(alias f)
{
@@ -2444,7 +2464,7 @@ void test13861()
}
/*******************************************/
-// 14398
+// https://issues.dlang.org/show_bug.cgi?id=14398
void test14398()
{
@@ -2479,7 +2499,7 @@ void test14398()
}
/*******************************************/
-// 14846
+// https://issues.dlang.org/show_bug.cgi?id=14846
void foo14846(Dg)(scope Dg code)
{
@@ -2509,14 +2529,14 @@ void test14846()
}
/*******************************************/
-// 15422
+// https://issues.dlang.org/show_bug.cgi?id=15422
class App15422(T)
{
this() {}
auto test1(T val)
- in {} body // necessary to reproduce the crash
+ in {} do // necessary to reproduce the crash
{
struct Foo
{
@@ -2544,7 +2564,7 @@ class App15422(T)
}
auto test2(T val)
- //in {} body
+ //in {} do
{
int closVar;
struct Foo
@@ -2662,7 +2682,7 @@ void test15422b()
}
/***************************************************/
-// 15757
+// https://issues.dlang.org/show_bug.cgi?id=15757
template map15757(fun...)
{
@@ -2703,6 +2723,45 @@ void test15757() @safe
/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19384
+
+struct Vec
+{
+ uint item;
+
+ ref uint august() return
+ {
+ return item;
+ // commenting next line removes bug
+ foreach(ref val; range()) return val;
+ assert(false);
+ }
+
+ uint* august2() return
+ {
+ return &item;
+ foreach(ref val; range()) return &val;
+ assert(false);
+ }
+}
+
+struct range
+{
+ int opApply(scope int delegate(ref uint) dg) { return 0; }
+}
+
+void test19384()
+{
+ Vec preds = Vec(0xDEAD);
+ void* ptr2 = &preds.august();
+ void* ptr3 = preds.august2();
+ assert(&preds == ptr2);
+ assert(&preds == ptr3);
+}
+
+
+/***************************************************/
+
int main()
{
test1();
@@ -2760,6 +2819,7 @@ int main()
test53();
test54();
test55();
+ test56();
test4401();
test7428();
test4612();
@@ -2797,6 +2857,7 @@ int main()
test15422a();
test15422b();
test15757();
+ test19384();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/newdel.d b/gcc/testsuite/gdc.test/runnable/newdel.d
index 21f7809c684..3729b2eb188 100644
--- a/gcc/testsuite/gdc.test/runnable/newdel.d
+++ b/gcc/testsuite/gdc.test/runnable/newdel.d
@@ -1,4 +1,10 @@
// PERMUTE_ARGS:
+/*
+TEST_OUTPUT:
+---
+runnable/newdel.d(46): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+*/
import core.stdc.stdio;
import core.stdc.stdlib;
@@ -9,18 +15,6 @@ class Foo
{
static uint flags;
- new(size_t sz, int x)
- { void* p;
-
- printf("Foo.new(sz = %d, x = %d)\n", sz, x);
- assert(sz == Foo.classinfo.initializer.length);
- assert(x == 5);
-
- p = core.stdc.stdlib.malloc(sz);
- flags |= 4;
- return p;
- }
-
this()
{
printf("this() %p\n", this);
@@ -34,13 +28,6 @@ class Foo
flags |= 1;
}
- delete(void* p)
- {
- printf("delete %p\n", p);
- free(p);
- flags |= 2;
- }
-
int a = 3;
int b = 4;
int d = 56;
@@ -50,61 +37,22 @@ void test1()
{
Foo f;
- f = new(5) Foo;
+ f = new Foo();
assert(f.a == 36);
assert(f.b == 4);
assert(f.d == 56);
- assert(Foo.flags == 4);
-
- delete f;
- assert(Foo.flags == 7);
-}
-
-
-/*********************************************/
-
-struct Foo2
-{
- static uint flags;
-
- new(size_t sz, int x)
- { void* p;
-
- printf("Foo2.new(sz = %d, x = %d)\n", sz, x);
- assert(sz == Foo2.sizeof);
- assert(x == 5);
-
- p = core.stdc.stdlib.malloc(sz);
- flags |= 4;
- return p;
- }
-
- delete(void *p)
- {
- printf("p = %p\n", p);
- flags |= 2;
- core.stdc.stdlib.free(p);
- }
-}
+ assert(Foo.flags == 0);
-void test2()
-{
- Foo2 *f = new(5) Foo2();
-
- printf("f = %p\n", f);
delete f;
- assert(Foo2.flags == 6);
+ assert(Foo.flags == 1);
}
-
/*********************************************/
int main()
{
test1();
- test2();
printf("Success\n");
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/nogc.d b/gcc/testsuite/gdc.test/runnable/nogc.d
index 8214e96e32a..c9a58b7e744 100644
--- a/gcc/testsuite/gdc.test/runnable/nogc.d
+++ b/gcc/testsuite/gdc.test/runnable/nogc.d
@@ -1,3 +1,9 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern(C) int printf(const char*, ...);
@@ -9,7 +15,7 @@ extern(C) int printf(const char*, ...);
}
/***********************/
-// 3032
+// https://issues.dlang.org/show_bug.cgi?id=3032
void test3032() @nogc
{
@@ -23,7 +29,7 @@ void test3032() @nogc
}
/***********************/
-// 12642
+// https://issues.dlang.org/show_bug.cgi?id=12642
__gshared int[1] data12642;
@@ -44,7 +50,7 @@ void test12642() @nogc
}
/***********************/
-// 12936
+// https://issues.dlang.org/show_bug.cgi?id=12936
void test12936() @nogc
{
diff --git a/gcc/testsuite/gdc.test/runnable/nulltype.d b/gcc/testsuite/gdc.test/runnable/nulltype.d
index 7a1f1c333b4..87d79400505 100644
--- a/gcc/testsuite/gdc.test/runnable/nulltype.d
+++ b/gcc/testsuite/gdc.test/runnable/nulltype.d
@@ -58,7 +58,7 @@ void test2()
}
/**********************************************/
-// 5899
+// https://issues.dlang.org/show_bug.cgi?id=5899
auto f5899(bool b)
{
@@ -91,7 +91,7 @@ static assert(is(typeof(h5899) R == return) && is(R == int[]));
pragma(msg, typeof(h5899));
/**********************************************/
-// 7278
+// https://issues.dlang.org/show_bug.cgi?id=7278
struct Foo7278(string s)
{
@@ -109,7 +109,7 @@ void test7278()
}
/**********************************************/
-// 8221
+// https://issues.dlang.org/show_bug.cgi?id=8221
class A8221
{
@@ -130,7 +130,7 @@ void test8221()
}
/***************************************************/
-// 8589
+// https://issues.dlang.org/show_bug.cgi?id=8589
void test8589()
{
@@ -157,7 +157,7 @@ void test8589()
}
/**********************************************/
-// 9385
+// https://issues.dlang.org/show_bug.cgi?id=9385
void test9385()
{
@@ -167,7 +167,7 @@ void test9385()
}
/**********************************************/
-// 12203
+// https://issues.dlang.org/show_bug.cgi?id=12203
void test12203()
{
diff --git a/gcc/testsuite/gdc.test/runnable/opdisp.d b/gcc/testsuite/gdc.test/runnable/opdisp.d
index f84a0ec794c..6aca559d7c4 100644
--- a/gcc/testsuite/gdc.test/runnable/opdisp.d
+++ b/gcc/testsuite/gdc.test/runnable/opdisp.d
@@ -1,3 +1,9 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern (C) int printf(const char* fmt, ...);
int pass(int n){ return n; }
@@ -265,7 +271,7 @@ void test6()
}
/**********************************************/
-// 7578
+// https://issues.dlang.org/show_bug.cgi?id=7578
struct Foo7578
{
diff --git a/gcc/testsuite/gdc.test/runnable/opover.d b/gcc/testsuite/gdc.test/runnable/opover.d
index 66270f4f0bd..633981e4cbb 100644
--- a/gcc/testsuite/gdc.test/runnable/opover.d
+++ b/gcc/testsuite/gdc.test/runnable/opover.d
@@ -1,5 +1,29 @@
+/*
+RUN_OUTPUT:
+---
+i = 1
+Writer.opShl(char[])
+BinaryWriter.opShl(int)
+a + 1 = 2
+1 + a = 2
+a + b = 3
+b + a = 3
+i = 64
+12
+534
+A::opShl(int 4)
+4A::opShl(char[])
+ A::opShl(int 12)
+12A::opShl(char[])
+
+B::opShl_r(A)
+Success
+---
+*/
// Test operator overloading
+// Ignore deprecation warnings for D1 style operator overloading
+// TRANSFORM_OUTPUT: remove_lines("Deprecation: `op")
import core.stdc.stdio;
@@ -776,7 +800,7 @@ class A13
A13 opShl(string x)
{
printf("A::opShl(char[])\n");
- printf("%.*s", x.length, x.ptr);
+ printf("%.*s", cast(int)x.length, x.ptr);
return this;
}
}
@@ -821,7 +845,7 @@ void test14()
/**************************************/
-// 3983
+// https://issues.dlang.org/show_bug.cgi?id=3983
struct Fug
{
@@ -849,7 +873,7 @@ void test15()
}
/**************************************/
-// 4953
+// https://issues.dlang.org/show_bug.cgi?id=4953
struct S4953a
{
@@ -907,7 +931,7 @@ void test4953d()
}
/**************************************/
-// 4993
+// https://issues.dlang.org/show_bug.cgi?id=4993
// reduced from the bug report
struct Bar4993
@@ -924,7 +948,7 @@ void test4993()
}
/**************************************/
-// 8133
+// https://issues.dlang.org/show_bug.cgi?id=8133
void test8133()
{
@@ -942,7 +966,7 @@ void test8133()
}
/**************************************/
-// 8522
+// https://issues.dlang.org/show_bug.cgi?id=8522
struct Point8522
{
@@ -961,7 +985,7 @@ void test8522()
}
/**************************************/
-// 12778
+// https://issues.dlang.org/show_bug.cgi?id=12778
struct Vec12778X
{
@@ -1009,7 +1033,7 @@ void test12778()
}
/**************************************/
-// 14343
+// https://issues.dlang.org/show_bug.cgi?id=14343
struct S14343a
{
@@ -1049,7 +1073,7 @@ void test14343()
}
/**************************************/
-// 14344
+// https://issues.dlang.org/show_bug.cgi?id=14344
struct S14344
{
@@ -1080,7 +1104,41 @@ class C14344
}
/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=1547
+struct A
+{
+ int b;
+ static A opCall(int k)
+ {
+ assert(0);
+ }
+ this(int) {}
+}
+
+void fun(A k = 2) {}
+
+void test1547()
+{
+ fun();
+}
+/**************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20475
+struct S20475
+{
+ string[2] x;
+}
+
+void test20475()
+{
+ auto s = S20475(["abc", "bcd"]);
+ auto t = S20475(["abc", ""]);
+ string u = "abcd";
+ t.x[1] = u[1..$];
+ assert(s == t);
+}
+
+/**************************************/
int main()
{
test1();
@@ -1098,6 +1156,7 @@ int main()
test13();
test14();
test15();
+ test1547();
test4953a();
test4953b();
test4953c();
@@ -1105,6 +1164,7 @@ int main()
test4993();
test8133();
test8522();
+ test20475();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/opover2.d b/gcc/testsuite/gdc.test/runnable/opover2.d
index 78fa04c4b23..dfc03528b2b 100644
--- a/gcc/testsuite/gdc.test/runnable/opover2.d
+++ b/gcc/testsuite/gdc.test/runnable/opover2.d
@@ -1,5 +1,5 @@
-// PERMUTE_ARGS: -inline -O -property
-// REQUIRED_ARGS: -dip25
+// PERMUTE_ARGS: -inline -O
+// REQUIRED_ARGS: -preview=fieldwise
// Test operator overloading
@@ -21,7 +21,7 @@ class A
{
string opUnary(string s)()
{
- printf("A.opUnary!(%.*s)\n", s.length, s.ptr);
+ printf("A.opUnary!(%.*s)\n", cast(int)s.length, s.ptr);
return s;
}
}
@@ -50,7 +50,7 @@ class A2
T opCast(T)()
{
auto s = T.stringof;
- printf("A.opCast!(%.*s)\n", s.length, s.ptr);
+ printf("A.opCast!(%.*s)\n", cast(int)s.length, s.ptr);
return T.init;
}
}
@@ -73,20 +73,20 @@ struct A3
{
int opBinary(string s)(int i)
{
- printf("A.opBinary!(%.*s)\n", s.length, s.ptr);
+ printf("A.opBinary!(%.*s)\n", cast(int)s.length, s.ptr);
return 0;
}
int opBinaryRight(string s)(int i) if (s == "/" || s == "*")
{
- printf("A.opBinaryRight!(%.*s)\n", s.length, s.ptr);
+ printf("A.opBinaryRight!(%.*s)\n", cast(int)s.length, s.ptr);
return 0;
}
T opCast(T)()
{
auto s = T.stringof;
- printf("A.opCast!(%.*s)\n", s.length, s.ptr);
+ printf("A.opCast!(%.*s)\n", cast(int)s.length, s.ptr);
return T.init;
}
}
@@ -108,14 +108,14 @@ struct A4
{
int opUnary(string s)()
{
- printf("A.opUnary!(%.*s)\n", s.length, s.ptr);
+ printf("A.opUnary!(%.*s)\n", cast(int)s.length, s.ptr);
return 0;
}
T opCast(T)()
{
auto s = T.stringof;
- printf("A.opCast!(%.*s)\n", s.length, s.ptr);
+ printf("A.opCast!(%.*s)\n", cast(int)s.length, s.ptr);
return T.init;
}
}
@@ -145,14 +145,14 @@ class A5
int opUnary(string s)()
{
- printf("A.opUnary!(%.*s)\n", s.length, s.ptr);
+ printf("A.opUnary!(%.*s)\n", cast(int)s.length, s.ptr);
return 0;
}
T opCast(T)()
{
auto s = T.stringof;
- printf("A.opCast!(%.*s)\n", s.length, s.ptr);
+ printf("A.opCast!(%.*s)\n", cast(int)s.length, s.ptr);
return T.init;
}
}
@@ -296,31 +296,31 @@ struct A8
{
int opUnary(string s)()
{
- printf("A.opUnary!(%.*s)\n", s.length, s.ptr);
+ printf("A.opUnary!(%.*s)\n", cast(int)s.length, s.ptr);
return 0;
}
int opIndexUnary(string s, T)(T i)
{
- printf("A.opIndexUnary!(%.*s)(%d)\n", s.length, s.ptr, i);
+ printf("A.opIndexUnary!(%.*s)(%d)\n", cast(int)s.length, s.ptr, i);
return 0;
}
int opIndexUnary(string s, T)(T i, T j)
{
- printf("A.opIndexUnary!(%.*s)(%d, %d)\n", s.length, s.ptr, i, j);
+ printf("A.opIndexUnary!(%.*s)(%d, %d)\n", cast(int)s.length, s.ptr, i, j);
return 0;
}
int opSliceUnary(string s)()
{
- printf("A.opSliceUnary!(%.*s)()\n", s.length, s.ptr);
+ printf("A.opSliceUnary!(%.*s)()\n", cast(int)s.length, s.ptr);
return 0;
}
int opSliceUnary(string s, T)(T i, T j)
{
- printf("A.opSliceUnary!(%.*s)(%d, %d)\n", s.length, s.ptr, i, j);
+ printf("A.opSliceUnary!(%.*s)(%d, %d)\n", cast(int)s.length, s.ptr, i, j);
return 0;
}
}
@@ -344,31 +344,31 @@ struct A9
{
int opOpAssign(string s)(int i)
{
- printf("A.opOpAssign!(%.*s)\n", s.length, s.ptr);
+ printf("A.opOpAssign!(%.*s)\n", cast(int)s.length, s.ptr);
return 0;
}
int opIndexOpAssign(string s, T)(int v, T i)
{
- printf("A.opIndexOpAssign!(%.*s)(%d, %d)\n", s.length, s.ptr, v, i);
+ printf("A.opIndexOpAssign!(%.*s)(%d, %d)\n", cast(int)s.length, s.ptr, v, i);
return 0;
}
int opIndexOpAssign(string s, T)(int v, T i, T j)
{
- printf("A.opIndexOpAssign!(%.*s)(%d, %d, %d)\n", s.length, s.ptr, v, i, j);
+ printf("A.opIndexOpAssign!(%.*s)(%d, %d, %d)\n", cast(int)s.length, s.ptr, v, i, j);
return 0;
}
int opSliceOpAssign(string s)(int v)
{
- printf("A.opSliceOpAssign!(%.*s)(%d)\n", s.length, s.ptr, v);
+ printf("A.opSliceOpAssign!(%.*s)(%d)\n", cast(int)s.length, s.ptr, v);
return 0;
}
int opSliceOpAssign(string s, T)(int v, T i, T j)
{
- printf("A.opSliceOpAssign!(%.*s)(%d, %d, %d)\n", s.length, s.ptr, v, i, j);
+ printf("A.opSliceOpAssign!(%.*s)(%d, %d, %d)\n", cast(int)s.length, s.ptr, v, i, j);
return 0;
}
}
@@ -468,7 +468,7 @@ int bug4913()
static assert(bug4913() == 83);
/**************************************/
-// 5551
+// https://issues.dlang.org/show_bug.cgi?id=5551
struct Foo11 {
Foo11 opUnary(string op:"++")() {
@@ -486,7 +486,7 @@ void test11()
}
/**************************************/
-// 4099
+// https://issues.dlang.org/show_bug.cgi?id=4099
struct X4099
{
@@ -672,7 +672,7 @@ void test17()
}
/**************************************/
-// 3789
+// https://issues.dlang.org/show_bug.cgi?id=3789
bool test3789()
{
@@ -728,7 +728,7 @@ bool test3789()
auto ua2 = UnionA([1,2,3]);
assert(ua1.u.x is ua2.u.x);
assert(ua1.u.x != ua2.u.x);
- assert(ua1 == ua2);
+ assert(ua1 != ua2);
ua1.u.x = 1.0;
ua2.u.x = 1.0;
assert(ua1.u.x is ua2.u.x);
@@ -755,7 +755,7 @@ bool test3789()
ub2.u.a = [1,2,3].dup;
assert(ub1.u.a !is ub2.u.a);
assert(ub1.u.a == ub2.u.a);
- assert(ub1 != ub2);
+ assert(ub1 == ub2);
ub2.u.a = ub1.u.a;
assert(ub1.u.a is ub2.u.a);
assert(ub1.u.a == ub2.u.a);
@@ -789,44 +789,27 @@ bool test3789()
}
static assert(test3789());
-/**************************************/
-// 10037
-
-struct S10037
-{
- bool opEquals(ref const S10037) { assert(0); }
-}
-
-struct T10037
+struct S
{
- S10037 s;
- // Compiler should not generate 'opEquals' here implicitly:
+ bool opEquals(ref const S) { return false; }
}
-struct Sub10037(TL...)
+struct T
{
- TL data;
+ S s;
int value;
alias value this;
}
-void test10037()
+void test11161()
{
- S10037 s;
- T10037 t;
- static assert( __traits(hasMember, S10037, "opEquals"));
- static assert(!__traits(hasMember, T10037, "opEquals"));
- assert(thrown!Error(s == s));
- assert(thrown!Error(t == t));
-
- Sub10037!(S10037) lhs;
- Sub10037!(S10037) rhs;
- static assert(!__traits(hasMember, Sub10037!(S10037), "opEquals"));
- assert(lhs == rhs); // lowered to: lhs.value == rhs.value
+ T t1, t2;
+ assert(t1.tupleof != t2.tupleof);
+ assert(t1 != t2); // fails
}
/**************************************/
-// 5810
+// https://issues.dlang.org/show_bug.cgi?id=5810
struct Bug5810
{
@@ -840,7 +823,7 @@ struct Foo5810
}
/**************************************/
-// 6798
+// https://issues.dlang.org/show_bug.cgi?id=6798
struct Tuple6798(T...)
{
@@ -1013,7 +996,7 @@ void test6798()
}
/**************************************/
-// 12382
+// https://issues.dlang.org/show_bug.cgi?id=12382
struct S12382
{
@@ -1041,7 +1024,7 @@ void test12382()
}
/**************************************/
-// 12904
+// https://issues.dlang.org/show_bug.cgi?id=12904
struct S12904
{
@@ -1080,7 +1063,7 @@ void test12904()
}
/**************************************/
-// 7641
+// https://issues.dlang.org/show_bug.cgi?id=7641
mixin template Proxy7641(alias a)
{
@@ -1110,7 +1093,7 @@ void test7641()
}
/**************************************/
-// 8434
+// https://issues.dlang.org/show_bug.cgi?id=8434
void test8434()
{
@@ -1338,7 +1321,7 @@ void test19()
}
/**************************************/
-// 9453
+// https://issues.dlang.org/show_bug.cgi?id=9453
struct Foo9453
{
@@ -1360,7 +1343,7 @@ void test9453()
}
/**************************************/
-// 9496
+// https://issues.dlang.org/show_bug.cgi?id=9496
struct S9496
{
@@ -1392,7 +1375,7 @@ void test9496()
}
/**************************************/
-// 9689
+// https://issues.dlang.org/show_bug.cgi?id=9689
struct B9689(T)
{
@@ -1418,7 +1401,7 @@ void test9689()
}
/**************************************/
-// 9694
+// https://issues.dlang.org/show_bug.cgi?id=9694
struct S9694
{
@@ -1438,7 +1421,7 @@ void test9694()
}
/**************************************/
-// 10064
+// https://issues.dlang.org/show_bug.cgi?id=10064
void test10064()
{
@@ -1465,7 +1448,7 @@ void test10064()
}
/**************************************/
-// 12585
+// https://issues.dlang.org/show_bug.cgi?id=12585
void test12585()
{
@@ -1504,7 +1487,7 @@ void test12585()
}
/**************************************/
-// 10394
+// https://issues.dlang.org/show_bug.cgi?id=10394
void test10394()
{
@@ -1533,7 +1516,7 @@ void test10394()
}
/**************************************/
-// 10597
+// https://issues.dlang.org/show_bug.cgi?id=10597
struct R10597
{
@@ -1577,7 +1560,7 @@ void test10597()
}
/**************************************/
-// 10567
+// https://issues.dlang.org/show_bug.cgi?id=10567
// doesn't require thunk
struct S10567x1n { int value; int opCmp(ref const S10567x1n rhs) const { return 0; } }
@@ -1662,13 +1645,6 @@ void test10567()
S sy = S(2);
assert(!(sx < sy) && !(sx > sy));
assert(sx.opCmp(sy) == 0);
-
- try
- {
- auto x = typeid(S).compare(&sx, &sy);
- assert(0);
- }
- catch (Error e) { assert(e.msg[$-15 .. $] == "not implemented"); }
}
/+
foreach (S; Seq!(S10567d1, S10567d2))
@@ -1690,7 +1666,7 @@ void test10567()
}
/**************************************/
-// 11062
+// https://issues.dlang.org/show_bug.cgi?id=11062
struct S11062ia
{
@@ -1734,7 +1710,7 @@ void test11062()
}
/**************************************/
-// 11311
+// https://issues.dlang.org/show_bug.cgi?id=11311
void test11311()
{
@@ -1791,7 +1767,7 @@ void test11311()
}
/**************************************/
-// 12193
+// https://issues.dlang.org/show_bug.cgi?id=12193
void test12193()
{
@@ -1810,7 +1786,7 @@ void test12193()
}
/**************************************/
-// 14057
+// https://issues.dlang.org/show_bug.cgi?id=14057
struct W14057
{
@@ -1936,7 +1912,7 @@ void test20d()
}
/**************************************/
-// 14624
+// https://issues.dlang.org/show_bug.cgi?id=14624
void test14624()
{
@@ -1981,7 +1957,7 @@ void test14624()
}
/**************************************/
-// 14625
+// https://issues.dlang.org/show_bug.cgi?id=14625
void test14625()
{
@@ -2033,7 +2009,7 @@ int main()
test16();
test17();
test3789();
- test10037();
+ test11161();
test6798();
test12904();
test7641();
@@ -2061,4 +2037,3 @@ int main()
printf("Success\n");
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/opover3.d b/gcc/testsuite/gdc.test/runnable/opover3.d
index c6de5d757e2..71468262553 100644
--- a/gcc/testsuite/gdc.test/runnable/opover3.d
+++ b/gcc/testsuite/gdc.test/runnable/opover3.d
@@ -125,7 +125,7 @@ void test4()
}
/**************************************/
-// 12070
+// https://issues.dlang.org/show_bug.cgi?id=12070
void test12070()
{
@@ -160,7 +160,7 @@ void test12070()
}
/**************************************/
-// 12124
+// https://issues.dlang.org/show_bug.cgi?id=12124
struct S12124
{
diff --git a/gcc/testsuite/gdc.test/runnable/overload.d b/gcc/testsuite/gdc.test/runnable/overload.d
index f8fa7d5cdda..c413ade029a 100644
--- a/gcc/testsuite/gdc.test/runnable/overload.d
+++ b/gcc/testsuite/gdc.test/runnable/overload.d
@@ -1,5 +1,13 @@
-// EXTRA_SOURCES: imports/ovs1528a.d imports/ovs1528b.d
-// EXTRA_SOURCES: imports/template_ovs1.d imports/template_ovs2.d imports/template_ovs3.d
+/*
+REQUIRED_ARGS: -preview=rvaluerefparam
+EXTRA_SOURCES: imports/ovs1528a.d imports/ovs1528b.d
+EXTRA_SOURCES: imports/template_ovs1.d imports/template_ovs2.d imports/template_ovs3.d
+EXTRA_FILES: imports/m8668a.d imports/m8668b.d imports/m8668c.d
+RUN_OUTPUT:
+---
+Success
+---
+*/
import imports.template_ovs1;
import imports.template_ovs2;
@@ -12,7 +20,7 @@ template Id( T){ alias T Id; }
template Id(alias A){ alias A Id; }
/***************************************************/
-// 1528
+// https://issues.dlang.org/show_bug.cgi?id=1528
int foo1528(long){ return 1; }
int foo1528(int[]){ return 2; }
@@ -313,7 +321,7 @@ void test1528d()
}
/***************************************************/
-// 1680
+// https://issues.dlang.org/show_bug.cgi?id=1680
struct S1680
{
@@ -360,7 +368,7 @@ void test1680()
}
/***************************************************/
-// 7418
+// https://issues.dlang.org/show_bug.cgi?id=7418
int foo7418(uint a) { return 1; }
int foo7418(char[] a) { return 2; }
@@ -378,7 +386,7 @@ void test7418()
}
/***************************************************/
-// 7552
+// https://issues.dlang.org/show_bug.cgi?id=7552
struct S7552
{
@@ -434,7 +442,7 @@ void test7552()
}
/***************************************************/
-// 8668
+// https://issues.dlang.org/show_bug.cgi?id=8668
import imports.m8668a;
import imports.m8668c; //replace with m8668b to make it work
@@ -446,7 +454,7 @@ void test8668()
}
/***************************************************/
-// 8943
+// https://issues.dlang.org/show_bug.cgi?id=8943
void test8943()
{
@@ -461,7 +469,7 @@ void test8943()
}
/***************************************************/
-// 9410
+// https://issues.dlang.org/show_bug.cgi?id=9410
struct S {}
int foo(float f, ref S s) { return 1; }
@@ -470,11 +478,17 @@ void test9410()
{
S s;
assert(foo(1, s ) == 1); // works fine. Print: ref
- assert(foo(1, S()) == 2); // Fails with: Error: S() is not an lvalue
+
+ /* With the rvalue to ref param change, this calls the 'ref' version
+ * because both are the same match level, but the 'ref' version is
+ * considered "more specialized", as the non-ref version undergoes
+ * a "conversion" to call the ref version.
+ */
+ assert(foo(1, S()) == 1);
}
/***************************************************/
-// 10171
+// https://issues.dlang.org/show_bug.cgi?id=10171
struct B10171(T) { static int x; }
@@ -484,7 +498,8 @@ void test10171()
}
/***************************************************/
-// 1900 - template overload set
+// https://issues.dlang.org/show_bug.cgi?id=1900
+// template overload set
void test1900a()
{
@@ -623,7 +638,7 @@ void test1900e()
}
/***************************************************/
-// 1900
+// https://issues.dlang.org/show_bug.cgi?id=1900
void test1900()
{
@@ -669,7 +684,7 @@ mixin Foo1900!(char) B1900;
alias Bar1900!(int) bar1900; // error
/***************************************************/
-// 7780
+// https://issues.dlang.org/show_bug.cgi?id=7780
mixin template A7780()
{
@@ -710,7 +725,7 @@ void test7849()
}
/***************************************************/
-// 8352
+// https://issues.dlang.org/show_bug.cgi?id=8352
void test8352()
{
@@ -721,7 +736,7 @@ void test8352()
}
/***************************************************/
-// 8441
+// https://issues.dlang.org/show_bug.cgi?id=8441
mixin template T8441a(string i)
{
@@ -819,7 +834,7 @@ void test8441c()
}
/***************************************************/
-// 9235
+// https://issues.dlang.org/show_bug.cgi?id=9235
template FlowEvaluator9235()
{
@@ -893,7 +908,7 @@ void test9235b()
}
/***************************************************/
-// 10658
+// https://issues.dlang.org/show_bug.cgi?id=10658
alias Val10658 = imports.template_ovs1.Val10658;
alias Val10658 = imports.template_ovs2.Val10658;
@@ -1009,7 +1024,7 @@ void test11785()
}
/***************************************************/
-// 11915
+// https://issues.dlang.org/show_bug.cgi?id=11915
int f11915( int) { return 1; }
int f11915(ref int) { return 2; }
@@ -1025,7 +1040,7 @@ void test11915()
}
/***************************************************/
-// 11916
+// https://issues.dlang.org/show_bug.cgi?id=11916
auto f11916(T)( T) { return 1; }
auto f11916(T)(out T) if (false) { return 2; }
@@ -1054,12 +1069,12 @@ void test11916()
}
/***************************************************/
-// 13783
+// https://issues.dlang.org/show_bug.cgi?id=13783
enum E13783 { a = 5 }
inout(int) f( inout(int) t) { return t * 2; }
-ref inout(int) f(ref inout(int) t) { return t; }
+ref inout(int) f(return ref inout(int) t) { return t; }
void test13783()
{
@@ -1068,7 +1083,7 @@ void test13783()
}
/***************************************************/
-// 14858
+// https://issues.dlang.org/show_bug.cgi?id=14858
int foo14858()() { return 1; }
int bar14858(int) { return 2; }
@@ -1083,7 +1098,7 @@ void test14858()
}
/***************************************************/
-// 14989
+// https://issues.dlang.org/show_bug.cgi?id=14989
template Foo14989(T) if (is(T == int)) { enum Foo14989 = 1; }
template Bar14989(T) if (is(T == double)) { enum Bar14989 = 2; }
@@ -1136,7 +1151,7 @@ void test14989()
}
/***************************************************/
-// 14965
+// https://issues.dlang.org/show_bug.cgi?id=14965
auto f14965a1() { return f14965a1(123); }
int f14965a1(int x) { return x; }
@@ -1212,6 +1227,40 @@ void test14965()
}
/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=21481
+
+struct S21481
+{
+ void funB2(char a) {}
+ alias funB = funB2;
+ // template as first symbol in overload set and overloading an alias
+ void funB()(float t) {}
+ void funB(int b) {} // function was lost -> OK now
+}
+
+void test21481()
+{
+ static assert(__traits(getOverloads, S21481, "funB", true).length == 3);
+}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=21522
+
+struct S21522
+{
+ // alias to template as first symbol in overload set
+ void funA2()(int a) {}
+ void funA2(char a) {} // function was lost -> OK now
+ alias funA = funA2;
+ void funA(float b) {}
+}
+
+void test21522()
+{
+ static assert(__traits(getOverloads, S21522, "funA", true).length == 3);
+}
+
+/***************************************************/
int main()
{
@@ -1247,6 +1296,8 @@ int main()
test13783();
test14858();
test14965();
+ test21481();
+ test21522();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/previewin.d b/gcc/testsuite/gdc.test/runnable/previewin.d
new file mode 100644
index 00000000000..12a0551f7e5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/previewin.d
@@ -0,0 +1,189 @@
+// REQUIRED_ARGS: -preview=dip1000 -preview=in
+
+void main ()
+{
+ testWithAllAttributes();
+ testForeach();
+}
+
+void testWithAllAttributes() @safe pure nothrow @nogc
+{
+ // Used to test dtors
+ bool isTestOver = false;
+
+ // rvalues
+ testin1(42);
+ testin2((ulong[64]).init);
+ testin3(ValueT(42));
+ testin3(RefT(42));
+ testin4((ValueT[64]).init);
+ testin4([RefT(42), RefT(84), RefT(126), RefT(4)]);
+ testin5(NonCopyable(true));
+ testin6(WithPostblit(true));
+ testin7(WithCopyCtor(true));
+ isTestOver = false;
+ testin8(WithDtor(&isTestOver), &isTestOver);
+ isTestOver = false;
+
+ // lvalues
+ uint a1;
+ ulong[64] a2;
+ ValueT a3;
+ ValueT[64] a4;
+ RefT a5;
+ RefT[4] a6;
+ NonCopyable a7 = NonCopyable(true);
+ WithPostblit a8;
+ WithCopyCtor a9;
+ WithDtor a10 = WithDtor(&isTestOver);
+
+ testin1(a1);
+ testin2(a2);
+ testin3(a3);
+ testin3(a5);
+ testin4(a4);
+ testin4(a6);
+ testin5(a7);
+ testin6(a8);
+ testin7(a9);
+ isTestOver = false;
+ testin8(a10, null);
+
+ // Arguments are all values, no `ref` needed
+ testin9(int.init);
+ testin9(char.init, ubyte.init, short.init, int.init, size_t.init);
+ // Arguments are all refs
+ testin9(a2, a4, a5, a6, a7, a8, a9, a10);
+ testin9(NonCopyable(true), WithPostblit(true), WithCopyCtor(true));
+ // Mixed values and ref
+ testin9(char.init, ubyte.init, a2, a4, a5, a6, a7, a8, a9, a10, size_t.init);
+
+ // With dtor
+ isTestOver = false;
+ testin10(&isTestOver, NonCopyable(true), WithPostblit(true),
+ WithCopyCtor(true), WithDtor(&isTestOver));
+ isTestOver = true;
+}
+
+void testForeach() @safe pure
+{
+ int testCallNC (in NonCopyable k, in NonCopyable v)
+ {
+ assert(k == v);
+ return k.value - v.value;
+ }
+
+ NonCopyable[NonCopyable] nc;
+ nc[NonCopyable(0)] = NonCopyable(0);
+ nc[NonCopyable(42)] = NonCopyable(42);
+ nc[NonCopyable(int.min)] = NonCopyable(int.min);
+ nc[NonCopyable(int.max)] = NonCopyable(int.max);
+ foreach (ref k, const ref v; nc)
+ {
+ assert(k.value == v.value);
+ assert(testCallNC(k, v) == 0);
+ }
+ assert(nc == nc);
+ assert(nc.length == 4);
+
+ RefT[RefT] rt;
+ rt[RefT(42)] = RefT(42);
+ rt[RefT(4)] = RefT(4);
+ rt[RefT(242)] = RefT(242);
+ rt[RefT(24)] = RefT(24);
+ foreach (k, v; rt)
+ assert(k.value == v.value);
+ assert(rt == rt);
+
+ static struct Msg
+ {
+ ubyte[3] value;
+ const(char)[] msg;
+ }
+
+ static void testMsg (in Msg k_func, in Msg v_func)
+ {
+ assert(k_func.value == v_func.value);
+ assert(k_func.msg == v_func.msg);
+ assert(k_func == v_func);
+ }
+
+ Msg[Msg] msg;
+ msg[Msg([1, 2, 3], "123")] = Msg([1, 2, 3], "123");
+ msg[Msg([42, 4, 2], "4242")] = Msg([42, 4, 2], "4242");
+ msg[Msg([242, 4, 0], "2424")] = Msg([242, 4, 0], "2424");
+ foreach (ref k_loop, ref v_loop; msg)
+ testMsg(k_loop, v_loop);
+}
+
+struct ValueT { int value; }
+struct RefT { ulong[64] value; }
+
+struct NonCopyable
+{
+ @safe pure nothrow @nogc:
+
+ int value;
+ this(int b) { this.value = b; }
+
+ @disable this(this);
+ @disable this(ref NonCopyable);
+}
+
+struct WithPostblit
+{
+ int value;
+ this(this) @safe pure nothrow @nogc { assert(0); }
+}
+
+struct WithCopyCtor
+{
+ @safe pure nothrow @nogc:
+
+ int value;
+ this(int b) { this.value = b; }
+ this(ref WithCopyCtor) { assert(0); }
+}
+
+struct WithDtor
+{
+ bool* value;
+ ~this() scope @safe pure nothrow @nogc { assert(*value); }
+}
+
+@safe pure nothrow @nogc:
+
+// By value
+void testin1(in uint p) { static assert(!__traits(isRef, p)); }
+// By ref because of size
+void testin2(in ulong[64] p) { static assert(__traits(isRef, p)); }
+// By value or ref depending on size
+void testin3(in ValueT p) { static assert(!__traits(isRef, p)); }
+void testin3(in RefT p) { static assert(__traits(isRef, p)); }
+// By ref because of size
+void testin4(in ValueT[64] p) { static assert(__traits(isRef, p)); }
+void testin4(in RefT[4] p) { static assert(__traits(isRef, p)); }
+
+// By ref because of non-copyability
+void testin5(in NonCopyable noncopy) { static assert(__traits(isRef, noncopy)); }
+static assert(testin5.mangleof == "_D9previewin7testin5FNaNbNiNfIKSQBe11NonCopyableZv"); // incl. `ref`
+// By ref because of postblit
+void testin6(in WithPostblit withpostblit) { static assert(__traits(isRef, withpostblit)); }
+// By ref because of copy ctor
+void testin7(in WithCopyCtor withcopy) { static assert(__traits(isRef, withcopy)); }
+// By ref because of dtor
+void testin8(in WithDtor withdtor, scope bool* isTestOver)
+{
+ static assert(__traits(isRef, withdtor));
+ if (isTestOver)
+ *isTestOver = true;
+}
+
+// Allow to test various tuples (e.g. `(int, int)` and `(int, WithDtor)`)
+// `ref` is only applied to the members which need it
+void testin9(T...)(in T args) {}
+void testin10(T...)(scope bool* isTestOver, in T args)
+{
+ if (isTestOver)
+ *isTestOver = true;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/printargs.d b/gcc/testsuite/gdc.test/runnable/printargs.d
index f4fc703163c..7d4d916f13d 100644
--- a/gcc/testsuite/gdc.test/runnable/printargs.d
+++ b/gcc/testsuite/gdc.test/runnable/printargs.d
@@ -3,12 +3,12 @@
extern(C) int printf(const char*, ...);
-int main(char args[][])
+int main(char[][] args)
{
int i;
for (i = 0; i < args.length; i++)
- printf("args[%d] = '%.*s'\n", i, args[i].length, args[i].ptr);
+ printf("args[%d] = '%.*s'\n", i, cast(int)args[i].length, args[i].ptr);
assert(args[1] == "A");
assert(args[2] == "B");
diff --git a/gcc/testsuite/gdc.test/runnable/property.d b/gcc/testsuite/gdc.test/runnable/property.d
index 4fd3af4347b..661bd68565e 100644
--- a/gcc/testsuite/gdc.test/runnable/property.d
+++ b/gcc/testsuite/gdc.test/runnable/property.d
@@ -26,12 +26,12 @@ int test1()
}
/******************************************/
-// 6259
+// https://issues.dlang.org/show_bug.cgi?id=6259
struct S6259
{
private int m_prop;
- ref const(int) prop() { return m_prop; }
+ ref const(int) prop() return { return m_prop; }
void prop(int v) { m_prop = v; }
}
diff --git a/gcc/testsuite/gdc.test/runnable/property2.d b/gcc/testsuite/gdc.test/runnable/property2.d
index 1c5cf30aff0..275729f2a3e 100644
--- a/gcc/testsuite/gdc.test/runnable/property2.d
+++ b/gcc/testsuite/gdc.test/runnable/property2.d
@@ -1,4 +1,3 @@
-// PERMUTE_ARGS: -property
/*
TEST_OUTPUT:
---
@@ -19,23 +18,13 @@ RUN_OUTPUT:
Success
---
*/
-
extern (C) int printf(const char* fmt, ...);
-// Is -property option specified?
-enum enforceProperty = !__traits(compiles, {
- int prop(){ return 1; }
- int n = prop;
-});
-
/*******************************************/
template select(alias v1, alias v2)
{
- static if (enforceProperty)
- enum select = v1;
- else
- enum select = v2;
+ enum select = v2;
}
struct Test(int N)
@@ -48,32 +37,28 @@ struct Test(int N)
ref foo(){ getset = 1; return value; }
enum result = select!(0, 1);
- // -property test.d(xx): Error: not a property foo
- // (no option) prints "getter"
+ // prints "getter"
}
static if (N == 1)
{
ref foo(int x){ getset = 2; value = x; return value; }
enum result = select!(0, 2);
- // -property test.d(xx): Error: not a property foo
- // (no option) prints "setter"
+ // prints "setter"
}
static if (N == 2)
{
@property ref foo(){ getset = 1; return value; }
enum result = select!(1, 1);
- // -property prints "getter"
- // (no option) prints "getter"
+ // prints "getter"
}
static if (N == 3)
{
@property ref foo(int x){ getset = 2; value = x; return value; }
enum result = select!(2, 2);
- // -property prints "setter"
- // (no option) prints "setter"
+ // prints "setter"
}
@@ -83,8 +68,7 @@ struct Test(int N)
ref foo(int x){ getset = 2; value = x; return value; }
enum result = select!(0, 2);
- // -property test.d(xx): Error: not a property foo
- // (no option) prints "setter"
+ // prints "setter"
}
static if (N == 5)
{
@@ -92,8 +76,7 @@ struct Test(int N)
ref foo(int x){ getset = 2; value = x; return value; }
enum result = select!(0, 0);
- // -property test.d(xx): Error: cannot overload both property and non-property functions
- // (no option) test.d(xx): Error: cannot overload both property and non-property functions
+ // test.d(xx): Error: cannot overload both property and non-property functions
}
static if (N == 6)
{
@@ -101,8 +84,7 @@ struct Test(int N)
@property ref foo(int x){ getset = 2; value = x; return value; }
enum result = select!(0, 0);
- // -property test.d(xx): Error: cannot overload both property and non-property functions
- // (no option) test.d(xx): Error: cannot overload both property and non-property functions
+ // test.d(xx): Error: cannot overload both property and non-property functions
}
static if (N == 7)
{
@@ -110,8 +92,7 @@ struct Test(int N)
@property ref foo(int x){ getset = 2; value = x; return value; }
enum result = select!(2, 2);
- // -property prints "setter"
- // (no option) prints "setter"
+ // prints "setter"
}
}
@@ -153,7 +134,7 @@ void test1()
}
/*******************************************/
-// 7722
+// https://issues.dlang.org/show_bug.cgi?id=7722
class Foo7722 {}
void spam7722(Foo7722 f) {}
@@ -161,10 +142,7 @@ void spam7722(Foo7722 f) {}
void test7722()
{
auto f = new Foo7722;
- static if (enforceProperty)
- static assert(!__traits(compiles, f.spam7722));
- else
- f.spam7722;
+ f.spam7722;
}
/*******************************************/
@@ -179,10 +157,7 @@ void test7722()
assert(dg(0) == v);
}
- static if (enforceProperty)
- checkImpl!(v1)();
- else
- checkImpl!(v2)();
+ checkImpl!(v2)();
}
struct S {}
@@ -292,7 +267,7 @@ void test7722b()
}
/*******************************************/
-// 7174
+// https://issues.dlang.org/show_bug.cgi?id=7174
void test7174()
{
@@ -301,7 +276,7 @@ void test7174()
}
/***************************************************/
-// 7274
+// https://issues.dlang.org/show_bug.cgi?id=7274
@property foo7274(){ return "test"; }
@property bar7274(){ return "kernel32.lib"; }
@@ -316,7 +291,7 @@ void test7274()
}
/***************************************************/
-// 7275
+// https://issues.dlang.org/show_bug.cgi?id=7275
void test7275()
{
@@ -333,7 +308,7 @@ void test7275()
}
/*****************************************/
-// 7538
+// https://issues.dlang.org/show_bug.cgi?id=7538
void test7538()
{
@@ -478,7 +453,7 @@ void test7538()
}
/*****************************************/
-// 8251
+// https://issues.dlang.org/show_bug.cgi?id=8251
struct S8251
{
@@ -515,13 +490,13 @@ void test8251()
}
/*****************************************/
-// 9063
+// https://issues.dlang.org/show_bug.cgi?id=9063
@property bool foo9063(){ return true; }
static assert(foo9063);
/*****************************************/
-// 9234
+// https://issues.dlang.org/show_bug.cgi?id=9234
class Fizz9234
{
@@ -533,7 +508,7 @@ struct Foo9234(alias F) {}
struct Foo9234(string thunk) {}
/*****************************************/
-// 10103
+// https://issues.dlang.org/show_bug.cgi?id=10103
mixin template Getter10103()
{
@@ -577,7 +552,7 @@ void test10103()
}
/*****************************************/
-// 10197
+// https://issues.dlang.org/show_bug.cgi?id=10197
template OriginalType10197(T)
{
diff --git a/gcc/testsuite/gdc.test/runnable/pubprivtmpl.d b/gcc/testsuite/gdc.test/runnable/pubprivtmpl.d
new file mode 100644
index 00000000000..992f04fb74d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/pubprivtmpl.d
@@ -0,0 +1,20 @@
+// EXTRA_SOURCES: imports/pubprivtmpla.d
+
+module pubprivtmpl;
+
+// Idiom: public alias to private template
+// This idiom was discovered while refactoring access.d. The idiom was not being used in any DLang repository
+// but was being used by a few projects in the D ecosystem. It is unkown at this time if this idiom is permitted
+// by design or by accident. This test was added to DMD to prevent regressions in those projects that utilize this
+// idiom. See also:
+// https://issues.dlang.org/show_bug.cgi?id=4533
+// https://issues.dlang.org/show_bug.cgi?id=11173
+
+import pubprivtmpla;
+
+void main()
+{
+ auto s = S();
+ auto v = s.get();
+ assert(v == 42);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/s2ir.d b/gcc/testsuite/gdc.test/runnable/s2ir.d
index 29cfc9669bb..840e6de9432 100644
--- a/gcc/testsuite/gdc.test/runnable/s2ir.d
+++ b/gcc/testsuite/gdc.test/runnable/s2ir.d
@@ -1,5 +1,14 @@
-// RUNNABLE_PHOBOS_TEST
-import std.stdio;
+/*
+RUN_OUTPUT:
+---
+hello
+world
+foo
+Success
+---
+*/
+
+import core.stdc.stdio;
/***********************************/
@@ -17,6 +26,8 @@ void test1()
}
version(D_PIC)
{}
+ else version (D_PIE)
+ {}
else
{
asm
@@ -43,7 +54,7 @@ int main()
a[2] = "foo";
foreach (string s; a)
- writefln(s);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
switch (1)
{
@@ -92,6 +103,6 @@ int main()
default: assert(0);
}
- writefln("Success\n");
+ printf("Success\n");
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/sctor.d b/gcc/testsuite/gdc.test/runnable/sctor.d
index 7d640c09332..b587e6efe80 100644
--- a/gcc/testsuite/gdc.test/runnable/sctor.d
+++ b/gcc/testsuite/gdc.test/runnable/sctor.d
@@ -1,5 +1,11 @@
-// REQUIRED_ARGS:
-// PERMUTE_ARGS: -w -d -de -dw
+/*
+REQUIRED_ARGS: -w -de
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern(C) int printf(const char*, ...);
@@ -117,7 +123,7 @@ struct S4
}
/***************************************************/
-// 8117
+// https://issues.dlang.org/show_bug.cgi?id=8117
struct S8117
{
@@ -136,7 +142,7 @@ void test8117()
}
/***************************************************/
-// 9665
+// https://issues.dlang.org/show_bug.cgi?id=9665
struct X9665
{
@@ -182,7 +188,7 @@ void test9665()
}
/***************************************************/
-// 11246
+// https://issues.dlang.org/show_bug.cgi?id=11246
struct Foo11246
{
@@ -223,7 +229,7 @@ void test11246()
}
/***************************************************/
-// 13515
+// https://issues.dlang.org/show_bug.cgi?id=13515
Object[string][100] aa13515;
@@ -253,7 +259,7 @@ void test13515()
}
/***************************************************/
-// 14409
+// https://issues.dlang.org/show_bug.cgi?id=14409
class B14409 { this(int) {} }
class C14409 : B14409
@@ -268,7 +274,7 @@ class C14409 : B14409
}
/***************************************************/
-// 14376
+// https://issues.dlang.org/show_bug.cgi?id=14376
auto map14376()
{
@@ -294,7 +300,7 @@ struct S14376
}
/***************************************************/
-// 14351
+// https://issues.dlang.org/show_bug.cgi?id=14351
class B14351
{
@@ -317,7 +323,7 @@ class D14351c : B14351
}
/***************************************************/
-// 14450
+// https://issues.dlang.org/show_bug.cgi?id=14450
struct S14450a // non-template struct + non template ctors - OK
{
@@ -366,7 +372,7 @@ void test14450()
}
/***************************************************/
-// 14944
+// https://issues.dlang.org/show_bug.cgi?id=14944
static int[2] tbl14944;
@@ -386,7 +392,8 @@ void test14944()
}
/***************************************************/
-// 15258 - a field initialization affects other overlapped fields
+// https://issues.dlang.org/show_bug.cgi?id=15258
+// a field initialization affects other overlapped fields
class C15258
{
@@ -403,18 +410,62 @@ class C15258
}
/***************************************************/
-// 15665
+// https://issues.dlang.org/show_bug.cgi?id=15869
-scope class C15665 (V)
+struct Set {
+ @disable this(this);
+ int value = 0;
+}
+
+Set clobber(ref Set a) {
+ Set ret; // <- This overwrites *a, i.e. &ret is the same as a
+ ret.value = a.value; // <- Now a.value is 0
+ return ret;
+}
+
+struct XX {
+ Set a = Set(1);
+ this(int n) {
+ a = clobber(a); // fix is to make this an assignment, not a construction
+ }
+}
+void test15869()
{
- this () {}
+ Set a = Set(1);
+ a = clobber(a);
+ assert(a.value == 1);
+
+ XX xx = XX(0);
+ assert(xx.a.value == 1);
}
-void test15665()
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19389
+
+struct Foo19389 {
+ int x;
+
+ this(int dummy) { x = dummy; }
+}
+
+struct Bar19389 {
+ Foo19389 a;
+ Foo19389 b;
+
+ this(int dummy) {
+ a = (b = Foo19389(dummy));
+ }
+}
+
+
+void test19389()
{
- scope foo = new C15665!int;
+ Bar19389 bar = Bar19389(7);
+ assert(bar.a.x == 7);
+ assert(bar.b.x == 7); // fails
}
+
/***************************************************/
int main()
@@ -425,7 +476,8 @@ int main()
test13515();
test14450();
test14944();
- test15665();
+ test15869();
+ test19389();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/sctor2.d b/gcc/testsuite/gdc.test/runnable/sctor2.d
new file mode 100644
index 00000000000..a2367cf7999
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/sctor2.d
@@ -0,0 +1,20 @@
+// REQUIRED_ARGS: -w -de
+// PERMUTE_ARGS:
+
+/***************************************************/
+// 15665
+
+scope class C15665 (V)
+{
+ this () {}
+}
+
+void test15665()
+{
+ scope foo = new C15665!int;
+}
+
+void main()
+{
+ test15665();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/sdtor.d b/gcc/testsuite/gdc.test/runnable/sdtor.d
index 3edf8cf85aa..56c5125cdaf 100644
--- a/gcc/testsuite/gdc.test/runnable/sdtor.d
+++ b/gcc/testsuite/gdc.test/runnable/sdtor.d
@@ -1,4 +1,5 @@
// PERMUTE_ARGS: -unittest -O -release -inline -fPIC -g
+// REQUIRED_ARGS: -preview=dtorfields
/*
TEST_OUTPUT:
---
@@ -16,7 +17,7 @@ S7353
import core.vararg;
-extern (C) int printf(const(char*) fmt, ...);
+extern (C) int printf(const(char*) fmt, ...) nothrow;
template TypeTuple(T...) { alias TypeTuple = T; }
@@ -38,23 +39,6 @@ void test1()
/**********************************/
-int sdtor2;
-
-struct S2
-{
- ~this() { printf("~S2()\n"); sdtor2++; }
- delete(void* p) { assert(sdtor2 == 1); printf("S2.delete()\n"); sdtor2++; }
-}
-
-void test2()
-{
- S2* s = new S2();
- delete s;
- assert(sdtor2 == 2);
-}
-
-/**********************************/
-
int sdtor3;
struct S3
@@ -1185,7 +1169,7 @@ void test45()
}
/**********************************/
-// 3986
+// https://issues.dlang.org/show_bug.cgi?id=3986
struct SiberianHamster
{
@@ -1200,7 +1184,7 @@ void test46()
}
/**********************************/
-// 8741
+// https://issues.dlang.org/show_bug.cgi?id=8741
struct Vec8741
{
@@ -1351,12 +1335,12 @@ void test52()
A52 b = a.copy();
printf("a: %p, b: %p\n", &a, &b);
}
- printf("s = '%.*s'\n", s52.length, s52.ptr);
+ printf("s = '%.*s'\n", cast(int)s52.length, s52.ptr);
assert(s52 == "cabb");
}
/**********************************/
-// 4339
+// https://issues.dlang.org/show_bug.cgi?id=4339
struct A53 {
invariant() { }
@@ -1520,7 +1504,7 @@ void test56()
}
/**********************************/
-// 5859
+// https://issues.dlang.org/show_bug.cgi?id=5859
int dtor_cnt = 0;
struct S57
@@ -1669,7 +1653,7 @@ void test57()
}
/**********************************/
-// 5574
+// https://issues.dlang.org/show_bug.cgi?id=5574
struct foo5574a
{
@@ -1690,7 +1674,7 @@ struct bar5574b
}
/**********************************/
-// 5777
+// https://issues.dlang.org/show_bug.cgi?id=5777
int sdtor58 = 0;
S58* ps58;
@@ -1716,7 +1700,7 @@ void test58()
}
/**********************************/
-// 6308
+// https://issues.dlang.org/show_bug.cgi?id=6308
struct C59
{
@@ -1749,7 +1733,7 @@ void test59()
}
/**********************************/
-// 5737
+// https://issues.dlang.org/show_bug.cgi?id=5737
void test5737()
{
@@ -1785,7 +1769,7 @@ void test5737()
}
/**********************************/
-// 6119
+// https://issues.dlang.org/show_bug.cgi?id=6119
void test6119()
{
@@ -1817,7 +1801,7 @@ void test6119()
}
/**********************************/
-// 6364
+// https://issues.dlang.org/show_bug.cgi?id=6364
struct Foo6364
{
@@ -1843,7 +1827,7 @@ void test6364()
}
/**********************************/
-// 6499
+// https://issues.dlang.org/show_bug.cgi?id=6499
struct S6499
{
@@ -1852,18 +1836,18 @@ struct S6499
this(string s)
{
m = s;
- printf("Constructor - %.*s\n", m.length, m.ptr);
+ printf("Constructor - %.*s\n", cast(int)m.length, m.ptr);
if (m == "foo") { ++sdtor; assert(sdtor == 1); }
if (m == "bar") { ++sdtor; assert(sdtor == 2); }
}
this(this)
{
- printf("Postblit - %.*s\n", m.length, m.ptr);
+ printf("Postblit - %.*s\n", cast(int)m.length, m.ptr);
assert(0);
}
~this()
{
- printf("Destructor - %.*s\n", m.length, m.ptr);
+ printf("Destructor - %.*s\n", cast(int)m.length, m.ptr);
if (m == "bar") { assert(sdtor == 2); --sdtor; }
if (m == "foo") { assert(sdtor == 1); --sdtor; }
}
@@ -1957,7 +1941,7 @@ void test60()
}
/**********************************/
-// 4316
+// https://issues.dlang.org/show_bug.cgi?id=4316
struct A4316
{
@@ -1990,7 +1974,7 @@ void test6177()
/**********************************/
-// 6470
+// https://issues.dlang.org/show_bug.cgi?id=6470
struct S6470
{
@@ -2021,7 +2005,7 @@ void test6470()
}
/**********************************/
-// 6636
+// https://issues.dlang.org/show_bug.cgi?id=6636
struct S6636
{
@@ -2043,7 +2027,7 @@ void test6636()
}
/**********************************/
-// 6637
+// https://issues.dlang.org/show_bug.cgi?id=6637
struct S6637
{
@@ -2062,7 +2046,7 @@ void test6637()
}
/**********************************/
-// 7353
+// https://issues.dlang.org/show_bug.cgi?id=7353
struct S7353
{
@@ -2101,7 +2085,7 @@ void test7353()
}
/**********************************/
-// 8036
+// https://issues.dlang.org/show_bug.cgi?id=8036
struct S8036a
{
@@ -2142,7 +2126,7 @@ void test61()
}
/**********************************/
-// 7506
+// https://issues.dlang.org/show_bug.cgi?id=7506
void test7506()
{
@@ -2172,7 +2156,7 @@ void test7506()
}
/**********************************/
-// 7516
+// https://issues.dlang.org/show_bug.cgi?id=7516
struct S7516
{
@@ -2295,7 +2279,7 @@ void test7516e()
}
/**********************************/
-// 7530
+// https://issues.dlang.org/show_bug.cgi?id=7530
void test7530()
{
@@ -2352,7 +2336,7 @@ void test62()
}
/**********************************/
-// 7579
+// https://issues.dlang.org/show_bug.cgi?id=7579
void test7579a()
{
@@ -2425,7 +2409,7 @@ void test7579b()
}
/**********************************/
-// 8335
+// https://issues.dlang.org/show_bug.cgi?id=8335
struct S8335
{
@@ -2471,7 +2455,7 @@ void test8335()
}
/**********************************/
-// 8356
+// https://issues.dlang.org/show_bug.cgi?id=8356
void test8356()
{
@@ -2493,7 +2477,7 @@ void test8356()
}
/**********************************/
-// 8475
+// https://issues.dlang.org/show_bug.cgi?id=8475
T func8475(T)(T x) @safe pure
{
@@ -2535,7 +2519,7 @@ Foo9320 test9320(Foo9320 a, Foo9320 b, Foo9320 c) {
}
/**********************************/
-// 9386
+// https://issues.dlang.org/show_bug.cgi?id=9386
struct Test9386
{
@@ -2548,21 +2532,21 @@ struct Test9386
this(string name)
{
this.name = name;
- printf("Created %.*s...\n", name.length, name.ptr);
+ printf("Created %.*s...\n", cast(int)name.length, name.ptr);
assert(i + 1 < op.length);
op[i++] = 'a';
}
this(this)
{
- printf("Copied %.*s...\n", name.length, name.ptr);
+ printf("Copied %.*s...\n", cast(int)name.length, name.ptr);
assert(i + 1 < op.length);
op[i++] = 'b';
}
~this()
{
- printf("Deleted %.*s\n", name.length, name.ptr);
+ printf("Deleted %.*s\n", cast(int)name.length, name.ptr);
assert(i + 1 < op.length);
op[i++] = 'c';
}
@@ -2592,7 +2576,7 @@ void test9386()
printf("----\n");
foreach (Test9386 test; tests)
{
- printf("\tForeach %.*s\n", test.name.length, test.name.ptr);
+ printf("\tForeach %.*s\n", cast(int)test.name.length, test.name.ptr);
Test9386.op[Test9386.i++] = 'x';
}
@@ -2603,7 +2587,7 @@ void test9386()
printf("----\n");
foreach (ref Test9386 test; tests)
{
- printf("\tForeach %.*s\n", test.name.length, test.name.ptr);
+ printf("\tForeach %.*s\n", cast(int)test.name.length, test.name.ptr);
Test9386.op[Test9386.i++] = 'x';
}
assert(Test9386.sop == "xxxx");
@@ -2625,8 +2609,8 @@ void test9386()
printf("----\n");
foreach (Test9386 k, Test9386 v; tests)
{
- printf("\tForeach %.*s : %.*s\n", k.name.length, k.name.ptr,
- v.name.length, v.name.ptr);
+ printf("\tForeach %.*s : %.*s\n", cast(int)k.name.length, k.name.ptr,
+ cast(int)v.name.length, v.name.ptr);
Test9386.op[Test9386.i++] = 'x';
}
@@ -2637,8 +2621,8 @@ void test9386()
printf("----\n");
foreach (Test9386 k, ref Test9386 v; tests)
{
- printf("\tForeach %.*s : %.*s\n", k.name.length, k.name.ptr,
- v.name.length, v.name.ptr);
+ printf("\tForeach %.*s : %.*s\n", cast(int)k.name.length, k.name.ptr,
+ cast(int)v.name.length, v.name.ptr);
Test9386.op[Test9386.i++] = 'x';
}
assert(Test9386.sop == "bxcbxcbxcbxc");
@@ -2648,7 +2632,7 @@ void test9386()
}
/**********************************/
-// 9441
+// https://issues.dlang.org/show_bug.cgi?id=9441
auto x9441 = X9441(0.123);
@@ -2697,7 +2681,7 @@ struct Data
~this()
{
- printf("%d\n", _store._count);
+ printf("%zd\n", _store._count);
--_store._count;
}
}
@@ -2711,7 +2695,7 @@ void test9720()
}
/**********************************/
-// 9899
+// https://issues.dlang.org/show_bug.cgi?id=9899
struct S9899
{
@@ -2729,7 +2713,7 @@ void test9899() @safe pure nothrow
}
/**********************************/
-// 9907
+// https://issues.dlang.org/show_bug.cgi?id=9907
void test9907()
{
@@ -2746,13 +2730,13 @@ void test9907()
void opAssign(SX rhs)
{
- printf("%08X(%d) from Rvalue %08X(%d)\n", &this.i, this.i, &rhs.i, rhs.i);
+ printf("%08zX(%d) from Rvalue %08zX(%d)\n", cast(size_t)&this.i, this.i, cast(size_t)&rhs.i, rhs.i);
++assign;
}
void opAssign(ref SX rhs)
{
- printf("%08X(%d) from Lvalue %08X(%d)\n", &this.i, this.i, &rhs.i, rhs.i);
+ printf("%08zX(%d) from Lvalue %08zX(%d)\n", cast(size_t)&this.i, this.i, cast(size_t)&rhs.i, rhs.i);
assert(0);
}
@@ -2760,7 +2744,7 @@ void test9907()
{
~this()
{
- printf("destroying %08X(%d)\n", &this.i, this.i);
+ printf("destroying %08zX(%d)\n", cast(size_t)&this.i, this.i);
++dtor;
}
}
@@ -2793,12 +2777,12 @@ void test9907()
}
/**********************************/
-// 9985
+// https://issues.dlang.org/show_bug.cgi?id=9985
struct S9985
{
ubyte* b;
- ubyte buf[128];
+ ubyte[128] buf;
this(this) { assert(0); }
static void* ptr;
@@ -2864,7 +2848,7 @@ void test17457()
}
/**********************************/
-// 9994
+// https://issues.dlang.org/show_bug.cgi?id=9994
void test9994()
{
@@ -2884,7 +2868,7 @@ void test9994()
}
/**********************************/
-// 10053
+// https://issues.dlang.org/show_bug.cgi?id=10053
struct S10053A
{
@@ -2898,7 +2882,7 @@ struct S10053B
}
/**********************************/
-// 10055
+// https://issues.dlang.org/show_bug.cgi?id=10055
void test10055a()
{
@@ -2999,7 +2983,7 @@ void test10055b()
}
/**********************************/
-// 10160
+// https://issues.dlang.org/show_bug.cgi?id=10160
struct S10160 { this(this) {} }
@@ -3013,7 +2997,7 @@ void test10160()
}
/**********************************/
-// 10094
+// https://issues.dlang.org/show_bug.cgi?id=10094
void test10094()
{
@@ -3035,7 +3019,7 @@ void test10094()
}
/**********************************/
-// 10079
+// https://issues.dlang.org/show_bug.cgi?id=10079
// dtor || postblit
struct S10079a
@@ -3082,7 +3066,7 @@ static assert(__traits(compiles, &check10079!S10079e));
static assert(__traits(compiles, &check10079!S10079f));
/**********************************/
-// 10244
+// https://issues.dlang.org/show_bug.cgi?id=10244
void test10244()
{
@@ -3110,7 +3094,7 @@ void test10244()
}
/**********************************/
-// 10694
+// https://issues.dlang.org/show_bug.cgi?id=10694
struct Foo10694 { ~this() { } }
@@ -3126,7 +3110,7 @@ void test10694() pure
}
/**********************************/
-// 10787
+// https://issues.dlang.org/show_bug.cgi?id=10787
int global10787;
@@ -3145,7 +3129,7 @@ shared static ~this() nothrow pure @safe
}
/**********************************/
-// 10789
+// https://issues.dlang.org/show_bug.cgi?id=10789
struct S10789
{
@@ -3212,7 +3196,7 @@ void test10789()
}
/**********************************/
-// 10972
+// https://issues.dlang.org/show_bug.cgi?id=10972
int test10972()
{
@@ -3276,7 +3260,7 @@ int test10972()
static assert(test10972()); // CTFE
/**********************************/
-// 11134
+// https://issues.dlang.org/show_bug.cgi?id=11134
void test11134()
{
@@ -3312,7 +3296,7 @@ void test11134()
}
/**********************************/
-// 11197
+// https://issues.dlang.org/show_bug.cgi?id=11197
struct S11197a
{
@@ -3346,7 +3330,7 @@ void fun7474(T...)() { T x; }
void test7474() { fun7474!S7474(); }
/**********************************/
-// 11286
+// https://issues.dlang.org/show_bug.cgi?id=11286
struct A11286
{
@@ -3364,7 +3348,7 @@ void test11286()
}
/**********************************/
-// 11505
+// https://issues.dlang.org/show_bug.cgi?id=11505
struct Foo11505
{
@@ -3384,7 +3368,7 @@ void test11505()
}
/**********************************/
-// 12045
+// https://issues.dlang.org/show_bug.cgi?id=12045
bool test12045()
{
@@ -3435,7 +3419,7 @@ bool test12045()
static assert(test12045());
/**********************************/
-// 12591
+// https://issues.dlang.org/show_bug.cgi?id=12591
struct S12591(T)
{
@@ -3458,7 +3442,7 @@ void test12591()
}
/**********************************/
-// 12660
+// https://issues.dlang.org/show_bug.cgi?id=12660
struct X12660
{
@@ -3499,7 +3483,7 @@ void test12660() @nogc
}
/**********************************/
-// 12686
+// https://issues.dlang.org/show_bug.cgi?id=12686
struct Foo12686
{
@@ -3527,7 +3511,7 @@ void test12686()
}
/**********************************/
-// 13089
+// https://issues.dlang.org/show_bug.cgi?id=13089
struct S13089
{
@@ -3687,7 +3671,7 @@ void test13586()
}
/**********************************/
-// 14443
+// https://issues.dlang.org/show_bug.cgi?id=14443
T enforce14443(E : Throwable = Exception, T)(T value)
{
@@ -3831,7 +3815,10 @@ void test14443()
}
/**********************************/
-// 13661, 14022, 14023 - postblit/dtor call on static array assignment
+// postblit/dtor call on static array assignment
+// https://issues.dlang.org/show_bug.cgi?id=13661
+// https://issues.dlang.org/show_bug.cgi?id=14022
+// https://issues.dlang.org/show_bug.cgi?id=14023
bool test13661()
{
@@ -4039,7 +4026,7 @@ bool test14023()
static assert(test14023());
/************************************************/
-// 13669 - dtor call on static array variable
+// https://issues.dlang.org/show_bug.cgi?id=13669 - dtor call on static array variable
bool test13669()
{
@@ -4084,7 +4071,7 @@ void test13095()
}
/**********************************/
-// 14264
+// https://issues.dlang.org/show_bug.cgi?id=14264
void test14264()
{
@@ -4116,7 +4103,7 @@ void test14264()
}
/**********************************/
-// 14686
+// https://issues.dlang.org/show_bug.cgi?id=14686
int test14686()
{
@@ -4160,7 +4147,7 @@ int test14686()
static assert(test14686());
/**********************************/
-// 14815
+// https://issues.dlang.org/show_bug.cgi?id=14815
int test14815()
{
@@ -4222,7 +4209,7 @@ void test16197() {
}
/**********************************/
-// 14860
+// https://issues.dlang.org/show_bug.cgi?id=14860
int test14860()
{
@@ -4245,7 +4232,39 @@ int test14860()
static assert(test14860());
/**********************************/
-// 14696
+// https://issues.dlang.org/show_bug.cgi?id=14246
+
+struct A14246 {
+ int a = 3;
+ static string s;
+ this( int var ) { printf("A()\n"); a += var; s ~= "a"; }
+
+ ~this() { printf("~A()\n"); s ~= "b"; }
+}
+
+struct B14246 {
+ int i;
+ A14246 a;
+
+ this( int var ) {
+ A14246.s ~= "c";
+ a = A14246(var+1);
+ throw new Exception("An exception");
+ }
+}
+
+void test14246() {
+ try {
+ auto b = B14246(2);
+ } catch( Exception ex ) {
+ printf("Caught ex\n");
+ A14246.s ~= "d";
+ }
+ assert(A14246.s == "cabd");
+}
+
+/**********************************/
+// https://issues.dlang.org/show_bug.cgi?id=14696
void test14696(int len = 2)
{
@@ -4351,10 +4370,12 @@ void test14696(int len = 2)
check({ foo(len == 2 ? makeS(1).get(len != 2 ? makeS(2).get() : null) : null); }, "makeS(1).get(1).foo.dtor(1).");
check({ foo(len != 2 ? makeS(1).get(len == 2 ? makeS(2).get() : null) : null); }, "foo.");
check({ foo(len != 2 ? makeS(1).get(len != 2 ? makeS(2).get() : null) : null); }, "foo.");
+ check({ foo(len == 2 ? makeS(makeS(2).n - 1).get() : null); }, "makeS(2).makeS(1).get(1).foo.dtor(1).dtor(2).");
+ check({ foo(len != 2 ? makeS(makeS(2).n - 1).get() : null); }, "foo.");
}
/**********************************/
-// 14838
+// https://issues.dlang.org/show_bug.cgi?id=14838
int test14838() pure nothrow @safe
{
@@ -4408,6 +4429,21 @@ static assert(test14838());
/**********************************/
+// https://issues.dlang.org/show_bug.cgi?id=14639
+
+struct Biggy {
+ ulong[50000] a;
+ @disable this(this);
+}
+
+__gshared Biggy biggy;
+
+void test14639() {
+ biggy = Biggy.init;
+}
+
+/**********************************/
+
struct S63
{
private long p = 87;
@@ -4501,7 +4537,7 @@ void test65()
}
/**********************************/
-// 15661
+// https://issues.dlang.org/show_bug.cgi?id=15661
struct X15661
{
@@ -4542,10 +4578,253 @@ void test15661()
/**********************************/
-int main()
+// https://issues.dlang.org/show_bug.cgi?id=18045
+
+struct A18045
+{
+ nothrow:
+ __gshared int r;
+ int state;
+ this(this) { printf("postblit: A(%d)\n", state); r += 1; }
+ ~this() { printf("dtor: A(%d)\n", state); r *= 3; }
+}
+
+A18045 fun18045() nothrow
+{
+ __gshared a = A18045(42);
+ return a;
+}
+
+void test18045() nothrow
+{
+ alias A = A18045;
+
+ __gshared a = A(-42);
+ if (fun18045() == a)
+ assert(0);
+ else
+ assert(A.r == 3);
+
+ A.r = 0;
+ if (a == fun18045())
+ assert(0);
+ else
+ assert(A.r == 3);
+}
+
+/**********************************/
+
+struct S66
+{
+ ~this() { }
+}
+
+nothrow void notthrow() { }
+
+class C66
{
+ S66 s;
+
+ this() nothrow { notthrow(); }
+}
+
+/**********************************/
+// https://issues.dlang.org/show_bug.cgi?id=16652
+
+struct Vector
+{
+ this(ubyte a)
+ {
+ pragma(inline, false);
+ buf = a;
+ }
+
+ ~this()
+ {
+ pragma(inline, false);
+ buf = 0;
+ }
+
+ ubyte buf;
+}
+
+int bar16652(ubyte* v)
+{
+ pragma(inline, true);
+ assert(*v == 1);
+ return 0;
+}
+
+void test16652()
+{
+ bar16652(&Vector(1).buf);
+}
+
+
+/**********************************/
+// https://issues.dlang.org/show_bug.cgi?id=19676
+
+void test19676()
+{
+ static struct S
+ {
+ __gshared int count;
+ ~this() { ++count; }
+ }
+
+ static S foo() { return S(); }
+
+ static void test1()
+ {
+ cast(void)foo();
+ }
+
+ static void test2()
+ {
+ foo();
+ }
+
test1();
+ assert(S.count == 1);
test2();
+ assert(S.count == 2);
+}
+
+/**********************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=14708
+
+__gshared bool dtor14078 = false;
+
+struct S14078
+{
+ int n;
+
+ void* get(void* p = null)
+ {
+ return null;
+ }
+
+ ~this()
+ {
+ //printf("dtor\n");
+ dtor14078 = true;
+ }
+}
+
+S14078 makeS14078(int n)
+{
+ return S14078(n);
+}
+
+void foo14078(void* x)
+{
+ throw new Exception("fail!");
+}
+
+void test(int len = 2)
+{
+ foo14078(makeS14078(1).get());
+ // A temporary is allocated on stack for the
+ // return value from makeS14078(1).
+ // When foo14078 throws exception, it's dtor should be called
+ // during unwinding stack, but it does not happen in Win64.
+}
+
+void test14078()
+{
+ try
+ {
+ test();
+ } catch (Exception e) {}
+ assert(dtor14078); // fails!
+}
+
+/**********************************/
+
+void test67()
+{
+ char[] deleted;
+
+ struct S
+ {
+ char* p;
+
+ ~this() { deleted ~= *p; }
+
+ void opAssign(S rhs)
+ {
+ // swap
+ char* tmp = p;
+ this.p = rhs.p;
+ rhs.p = tmp;
+ }
+ }
+
+ char a = 'a', b = 'b';
+ {
+ S s = S(&a);
+ s = S(&b);
+ }
+ assert(deleted == "ab", deleted);
+}
+
+/**********************************/
+
+void test68()
+{
+ static struct S
+ {
+ int i;
+ bool opEquals(S) { return false; }
+ ~this() {}
+ }
+
+ assert(S(0) != S(1));
+}
+
+/**********************************/
+
+// https://github.com/dlang/dmd/pull/12012
+
+extern (C++)
+{
+struct S12012
+{
+ int* ctr;
+ ~this() { }
+}
+
+void bar12012(int value, S12012 s)
+{
+}
+
+S12012 abc12012(ref S12012 s)
+{
+ s.ctr = null;
+ return s;
+}
+
+int def12012(ref S12012 s)
+{
+ return *s.ctr; // seg fault is here
+}
+
+void testPR12012()
+{
+ int i;
+ S12012 s = S12012(&i);
+ // def must be executed before abc else seg fault
+ bar12012(def12012(s), abc12012(s));
+}
+}
+
+/**********************************/
+
+int main()
+{
+ test1();
+
test3();
test4();
test5();
@@ -4666,12 +4945,21 @@ int main()
test14815();
test16197();
test14860();
+ test14246();
test14696();
test14838();
+ test14639();
test63();
test64();
test65();
test15661();
+ test18045();
+ test16652();
+ test19676();
+ test14078();
+ test67();
+ test68();
+ testPR12012();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/statictor.d b/gcc/testsuite/gdc.test/runnable/statictor.d
index 7e34bd123c2..b00277b789e 100644
--- a/gcc/testsuite/gdc.test/runnable/statictor.d
+++ b/gcc/testsuite/gdc.test/runnable/statictor.d
@@ -1,7 +1,19 @@
-// PERMUTE_ARGS:
-// POST_SCRIPT: runnable/extra-files/statictor-postscript.sh
+/*
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+shared static this()
+Foo static ctor
+static ctor
+Bar static ctor
+Bar static dtor
+static dtor
+Foo static dtor
+shared static this()
+---
+*/
-private import std.stdio;
+private import core.stdc.stdio;
class Foo
{
@@ -29,7 +41,7 @@ class Bar
}
/***********************************************/
-// 6677
+// https://issues.dlang.org/show_bug.cgi?id=6677
int global6677;
@@ -50,7 +62,7 @@ shared static this() nothrow pure @safe
}
/***********************************************/
-// 7533
+// https://issues.dlang.org/show_bug.cgi?id=7533
struct Foo7533(int n)
{
pure static this() { }
@@ -64,4 +76,3 @@ int main()
{
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/stress.d b/gcc/testsuite/gdc.test/runnable/stress.d
index a8c6c91008c..66373c4270d 100644
--- a/gcc/testsuite/gdc.test/runnable/stress.d
+++ b/gcc/testsuite/gdc.test/runnable/stress.d
@@ -83,10 +83,10 @@ void MDCHAR()
str[idx] = str[idx] ~ "TEST LINE\n";
}
- if(str.length != ITERS) printf("Length Error: %d\n",str.length);
- if(str[0].length != 10) printf("Length Error: %d\n",str[0].length);
- if(str[ITERS-1].sizeof != (typ[]).sizeof) printf("Size Error: %d\n",str[ITERS-1].sizeof);
- if(str[ITERS-1][0].sizeof != (typ).sizeof) printf("Size Error: %d\n",str[ITERS-1][0].sizeof);
+ if(str.length != ITERS) printf("Length Error: %zd\n",str.length);
+ if(str[0].length != 10) printf("Length Error: %zd\n",str[0].length);
+ if(str[ITERS-1].sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",str[ITERS-1].sizeof);
+ if(str[ITERS-1][0].sizeof != (typ).sizeof) printf("Size Error: %zd\n",str[ITERS-1][0].sizeof);
foreach(s; str) {
size_t lstart;
@@ -131,8 +131,8 @@ void CHAR()
str = str ~ "TEST LINE\n";
}
- if(str.length != (ITERS * 10)) printf("Length Error: %d\n",str.length);
- if(str.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",str.sizeof);
+ if(str.length != (ITERS * 10)) printf("Length Error: %zd\n",str.length);
+ if(str.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",str.sizeof);
size_t lstart;
foreach(size_t idx, char c; str) {
@@ -156,8 +156,8 @@ void WCHAR()
str = str ~ toUTF16(cast(char[])"TEST LINE\n");
}
- if(str.length != (ITERS * 10)) printf("Length Error: %d\n",str.length);
- if(str.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",str.sizeof);
+ if(str.length != (ITERS * 10)) printf("Length Error: %zd\n",str.length);
+ if(str.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",str.sizeof);
size_t lstart;
foreach(size_t idx, char c; str) {
@@ -181,8 +181,8 @@ void DCHAR()
str = str ~ toUTF32(cast(char[])"TEST LINE\n");
}
- if(str.length != (ITERS * 10)) printf("Length Error: %d\n",str.length);
- if(str.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",str.sizeof);
+ if(str.length != (ITERS * 10)) printf("Length Error: %zd\n",str.length);
+ if(str.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",str.sizeof);
size_t lstart;
foreach(size_t idx, char c; str) {
@@ -206,8 +206,8 @@ void BYTE()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
printf("a Data Error: %d\n",a[idx]);
@@ -218,8 +218,8 @@ void BYTE()
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
printf("b Data Error: %d\n",b[idx]);
@@ -230,8 +230,8 @@ void BYTE()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
printf("c Data Error: %d\n",c[idx]);
@@ -250,8 +250,8 @@ void UBYTE()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
printf("a Data Error: %d\n",a[idx]);
@@ -262,8 +262,8 @@ void UBYTE()
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
printf("b Data Error: %d\n",b[idx]);
@@ -275,8 +275,8 @@ void UBYTE()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
printf("c Data Error: %d\n",c[idx]);
@@ -295,8 +295,8 @@ void SHORT()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
printf("a Data Error: %d\n",a[idx]);
@@ -306,8 +306,8 @@ void SHORT()
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
printf("b Data Error: %d\n",b[idx]);
@@ -319,8 +319,8 @@ void SHORT()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
printf("c Data Error: %d\n",c[idx]);
@@ -339,8 +339,8 @@ void USHORT()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
printf("a Data Error: %d\n",a[idx]);
@@ -350,8 +350,8 @@ void USHORT()
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
printf("b Data Error: %d\n",b[idx]);
@@ -363,8 +363,8 @@ void USHORT()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
printf("c Data Error: %d\n",c[idx]);
@@ -383,8 +383,8 @@ void INT()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
printf("a Data Error: %d\n",a[idx]);
@@ -395,8 +395,8 @@ void INT()
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
printf("b Data Error: %d\n",b[idx]);
@@ -408,8 +408,8 @@ void INT()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
printf("c Data Error: %d\n",c[idx]);
@@ -428,8 +428,8 @@ void UINT()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
printf("a Data Error: %d\n",a[idx]);
@@ -440,8 +440,8 @@ void UINT()
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
printf("b Data Error: %d\n",b[idx]);
@@ -453,8 +453,8 @@ void UINT()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
printf("c Data Error: %d\n",c[idx]);
@@ -473,22 +473,22 @@ void LONG()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
- printf("a Data Error: %d\n",a[idx]);
+ printf("a Data Error: %lld\n",a[idx]);
break;
}
}
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
- printf("b Data Error: %d\n",b[idx]);
+ printf("b Data Error: %lld\n",b[idx]);
break;
}
}
@@ -497,11 +497,11 @@ void LONG()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
- printf("c Data Error: %d\n",c[idx]);
+ printf("c Data Error: %lld\n",c[idx]);
break;
}
}
@@ -517,22 +517,22 @@ void ULONG()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
- printf("a Data Error: %d\n",a[idx]);
+ printf("a Data Error: %lld\n",a[idx]);
break;
}
}
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
- printf("b Data Error: %d\n",b[idx]);
+ printf("b Data Error: %lld\n",b[idx]);
break;
}
}
@@ -541,11 +541,11 @@ void ULONG()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
- printf("c Data Error: %d\n",c[idx]);
+ printf("c Data Error: %lld\n",c[idx]);
break;
}
}
@@ -561,22 +561,22 @@ void FLOAT()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
- printf("a Data Error: %d\n",a[idx]);
+ printf("a Data Error: %g\n",a[idx]);
break;
}
}
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
- printf("b Data Error: %d\n",b[idx]);
+ printf("b Data Error: %g\n",b[idx]);
break;
}
}
@@ -585,11 +585,11 @@ void FLOAT()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
- printf("c Data Error: %d\n",c[idx]);
+ printf("c Data Error: %g\n",c[idx]);
break;
}
}
@@ -605,22 +605,22 @@ void DOUBLE()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
- printf("a Data Error: %d\n",a[idx]);
+ printf("a Data Error: %g\n",a[idx]);
break;
}
}
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
- printf("b Data Error: %d\n",b[idx]);
+ printf("b Data Error: %g\n",b[idx]);
break;
}
}
@@ -629,11 +629,11 @@ void DOUBLE()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
- printf("c Data Error: %d\n",c[idx]);
+ printf("c Data Error: %g\n",c[idx]);
break;
}
}
@@ -649,22 +649,22 @@ void REAL()
a ~= idx;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx] != idx) {
- printf("a Data Error: %d\n",a[idx]);
+ printf("a Data Error: %Lg\n",a[idx]);
break;
}
}
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx] != idx) {
- printf("b Data Error: %d\n",b[idx]);
+ printf("b Data Error: %Lg\n",b[idx]);
break;
}
}
@@ -673,11 +673,11 @@ void REAL()
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx] != idx) {
- printf("c Data Error: %d\n",c[idx]);
+ printf("c Data Error: %Lg\n",c[idx]);
break;
}
}
@@ -694,8 +694,8 @@ void CLASS()
a ~= tc;
}
- if(a.length != ITERS) printf("Length Error: %d\n",a.length);
- if(a.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",a.sizeof);
+ if(a.length != ITERS) printf("Length Error: %zd\n",a.length);
+ if(a.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",a.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(a[idx].i != idx) {
printf("a Data Error: %d\n",a[idx].i);
@@ -705,8 +705,8 @@ void CLASS()
typ[] b = a[];
- if(b.length != ITERS) printf("Length Error: %d\n",b.length);
- if(b.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",b.sizeof);
+ if(b.length != ITERS) printf("Length Error: %zd\n",b.length);
+ if(b.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",b.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(b[idx].i != idx) {
printf("b Data Error: %d\n",b[idx].i);
@@ -717,8 +717,8 @@ void CLASS()
typ[] c;
c = a[0..ITERS/2] ~ b[ITERS/2..$];
- if(c.length != ITERS) printf("Length Error: %d\n",c.length);
- if(c.sizeof != (typ[]).sizeof) printf("Size Error: %d\n",c.sizeof);
+ if(c.length != ITERS) printf("Length Error: %zd\n",c.length);
+ if(c.sizeof != (typ[]).sizeof) printf("Size Error: %zd\n",c.sizeof);
for(int idx = 0; idx < ITERS; idx++) {
if(c[idx].i != idx) {
printf("c Data Error: %d\n",c[idx].i);
diff --git a/gcc/testsuite/gdc.test/runnable/structlit.d b/gcc/testsuite/gdc.test/runnable/structlit.d
index 38af612e351..d4a6bd6061b 100644
--- a/gcc/testsuite/gdc.test/runnable/structlit.d
+++ b/gcc/testsuite/gdc.test/runnable/structlit.d
@@ -1,4 +1,12 @@
-import std.stdio;
+/*
+REQUIRED_ARGS: -preview=rvaluerefparam
+RUN_OUTPUT:
+---
+Success
+---
+*/
+
+import core.stdc.stdio;
struct S
{
@@ -318,13 +326,13 @@ int waz14(S)(ref S s) { return 2; }
void test14()
{
- // can not bind rvalue-sl with ref
- static assert(!__traits(compiles, foo14(S14a(0))));
- static assert(!__traits(compiles, foo14(S14b(0))));
- static assert(!__traits(compiles, hoo14(S14a(0))));
- static assert(!__traits(compiles, hoo14(S14b(0))));
- static assert(!__traits(compiles, poo14(S14a(0))));
- static assert(!__traits(compiles, poo14(S14b(0))));
+ // can bind rvalue-sl with ref
+ foo14(S14a(0));
+ foo14(S14b(0));
+ hoo14(S14a(0));
+ hoo14(S14b(0));
+ poo14(S14a(0));
+ poo14(S14b(0));
// still can bind rvalue-sl with non-ref
bar14(S14a(0));
@@ -491,7 +499,7 @@ void test15c()
auto c2 = new immutable C(1);
}
-void test15d() // Bugzilla 9974
+void test15d() // https://issues.dlang.org/show_bug.cgi?id=9974
{
class CM { this() {} }
auto cm = new CM();
@@ -525,7 +533,7 @@ void test15d() // Bugzilla 9974
shared const ssc = new shared const SSC(1);
}
-void test15e() // Bugzilla 10005
+void test15e() // https://issues.dlang.org/show_bug.cgi?id=10005
{
// struct literal
static struct S
@@ -642,7 +650,7 @@ void test9993b()
}
/********************************************/
-// 1914
+// https://issues.dlang.org/show_bug.cgi?id=1914
struct Bug1914a
{
@@ -703,7 +711,7 @@ void test3198and1914()
}
/********************************************/
-// 14996
+// https://issues.dlang.org/show_bug.cgi?id=14996
enum E14996a : string { confirm = "confirm" }
enum E14996b : long[] { confirm = [1,2,3,4] }
@@ -715,7 +723,7 @@ struct S14996
}
/********************************************/
-// 2427
+// https://issues.dlang.org/show_bug.cgi?id=2427
void test2427()
{
@@ -755,7 +763,7 @@ void test5885()
}
/********************************************/
-// 5889
+// https://issues.dlang.org/show_bug.cgi?id=5889
struct S5889a { int n; }
struct S5889b { this(int n){} }
@@ -812,7 +820,7 @@ void test5889()
}
/********************************************/
-// 4147
+// https://issues.dlang.org/show_bug.cgi?id=4147
struct S4247
{
@@ -829,7 +837,7 @@ void test4247()
}
/********************************************/
-// 6937
+// https://issues.dlang.org/show_bug.cgi?id=6937
void test6937()
{
@@ -885,7 +893,7 @@ void test6937()
}
/********************************************/
-// 12681
+// https://issues.dlang.org/show_bug.cgi?id=12681
struct HasUnion12774
{
@@ -930,7 +938,7 @@ bool test12681()
static assert(test12681());
/********************************************/
-// 3991
+// https://issues.dlang.org/show_bug.cgi?id=3991
union X3991
{
@@ -940,8 +948,8 @@ union X3991
union Y3991
{
- int a = void;
dchar b = 'a';
+ int a = void;
}
union Z3991
@@ -962,7 +970,7 @@ void test3991()
}
/********************************************/
-// 7727
+// https://issues.dlang.org/show_bug.cgi?id=7727
union U7727A1 { int i; double d; }
union U7727A2 { int i = 123; double d; }
@@ -990,7 +998,7 @@ void test7727()
{ U7727A2 u = { i: 1024, d: 1.225 }; }
));
-// Blocked by issue 1432
+// Blocked by https://issues.dlang.org/show_bug.cgi?id=1432
// { U7727A3 u; assert(u.d == 2.5); }
// { U7727A3 u = { i: 1024 }; assert(u.i == 1024); }
// { U7727A3 u = { d: 1.225 }; assert(u.d == 1.225); }
@@ -1012,7 +1020,7 @@ void test7727()
{ U7727B2 u = { i: 1024, d: 1.225 }; }
));
-// Blocked by issue 1432
+// Blocked by https://issues.dlang.org/show_bug.cgi?id=1432
// { U7727B3 u; assert(u.i == 123); }
// { U7727B3 u = { i: 1024 }; assert(u.i == 1024); }
// { U7727B3 u = { d: 1.225 }; assert(u.d == 1.225); }
@@ -1058,7 +1066,7 @@ void test7727b()
}
/********************************************/
-// 7929
+// https://issues.dlang.org/show_bug.cgi?id=7929
void test7929()
{
@@ -1076,7 +1084,7 @@ void test7929()
}
/********************************************/
-// 7021
+// https://issues.dlang.org/show_bug.cgi?id=7021
struct S7021
{
@@ -1091,7 +1099,7 @@ void test7021()
}
/********************************************/
-// 8738
+// https://issues.dlang.org/show_bug.cgi?id=8738
void test8738()
{
@@ -1108,7 +1116,7 @@ void test8738()
}
/********************************************/
-// 8763
+// https://issues.dlang.org/show_bug.cgi?id=8763
void test8763()
{
@@ -1128,7 +1136,7 @@ void test8763()
}
/********************************************/
-// 8902
+// https://issues.dlang.org/show_bug.cgi?id=8902
union U8902 { int a, b; }
@@ -1146,7 +1154,7 @@ void test8902()
}
/********************************************/
-// 9116
+// https://issues.dlang.org/show_bug.cgi?id=9116
void test9116()
{
@@ -1167,7 +1175,7 @@ void test9116()
}
/********************************************/
-// 9293
+// https://issues.dlang.org/show_bug.cgi?id=9293
void test9293()
{
@@ -1192,7 +1200,7 @@ void test9293()
}
/********************************************/
-// 9566
+// https://issues.dlang.org/show_bug.cgi?id=9566
void test9566()
{
@@ -1205,7 +1213,7 @@ void test9566()
}
/********************************************/
-// 9775
+// https://issues.dlang.org/show_bug.cgi?id=9775
enum Month9775 : ubyte { jan = 1, }
struct Date9775
@@ -1227,7 +1235,7 @@ enum Date9775 date9775e1 = Date9775(2012, 12, 21);
enum date9775e2 = Date9775(2012, 12, 21);
/********************************************/
-// 11105
+// https://issues.dlang.org/show_bug.cgi?id=11105
struct S11105
{
@@ -1240,7 +1248,7 @@ void test11105()
}
/********************************************/
-// 11147
+// https://issues.dlang.org/show_bug.cgi?id=11147
struct V11147
{
@@ -1273,7 +1281,7 @@ void test11147()
}
/********************************************/
-// 11256
+// https://issues.dlang.org/show_bug.cgi?id=11256
struct S11256 { @disable this(); }
@@ -1311,7 +1319,7 @@ void test11256()
}
/********************************************/
-// 11269
+// https://issues.dlang.org/show_bug.cgi?id=11269
struct Atom
{
@@ -1336,7 +1344,7 @@ void test11269()
}
/********************************************/
-// 11427
+// https://issues.dlang.org/show_bug.cgi?id=11427
struct S11427
{
@@ -1356,7 +1364,7 @@ int foo11427() @safe
}
/********************************************/
-// 12011
+// https://issues.dlang.org/show_bug.cgi?id=12011
struct S12011a
{
@@ -1379,7 +1387,7 @@ void test12011()
}
/********************************************/
-// 13021
+// https://issues.dlang.org/show_bug.cgi?id=13021
void test13021()
{
@@ -1417,7 +1425,7 @@ void test13021()
}
/********************************************/
-// 14556
+// https://issues.dlang.org/show_bug.cgi?id=14556
enum E14556 { a = 1 }
diff --git a/gcc/testsuite/gdc.test/runnable/template1.d b/gcc/testsuite/gdc.test/runnable/template1.d
index 51f96bf2acf..dde9213deb5 100644
--- a/gcc/testsuite/gdc.test/runnable/template1.d
+++ b/gcc/testsuite/gdc.test/runnable/template1.d
@@ -715,7 +715,7 @@ public template TRange29(T) {
recursing = false;
}
}
- } body {
+ } do {
return contains(other.lower()) || contains(other.upper()) || other.includes(this);
}
public bool includes(Range other)
@@ -723,7 +723,7 @@ public template TRange29(T) {
assert(other !is null);
} out (result) {
assert(result == (contains(other.lower()) && contains(other.upper())));
- } body {
+ } do {
return contains(other.lower()) && contains(other.upper());
}
}
@@ -771,22 +771,22 @@ void test30()
assert(i == 3);
copystr.copy(s, "Here it comes");
- printf("%.*s\n", s.length, s.ptr);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
assert(s == "Here it comes");
}
/******************************************/
-import std.string;
+import core.demangle;
template Foo31(alias X)
{
- alias X.toStringz y;
+ alias X.demangle y;
}
void test31()
{
- alias Foo31!(std.string) bar;
+ alias Foo31!(core.demangle) bar;
}
@@ -1367,7 +1367,7 @@ void test56()
{
alias CT56!(int) Ct;
Ct.C c= new Ct.C();
- printf("%.*s\n", c.arrArr[0].length, c.arrArr[0].ptr);
+ printf("%.*s\n", cast(int)c.arrArr[0].length, c.arrArr[0].ptr);
assert(c.arrArr[0] == "foo");
}
@@ -1449,15 +1449,15 @@ void test60()
B60!(int, long).Thing thing1;
B60!(int).Thing thing2;
- printf("thing1.sizeof: %u\n", thing1.sizeof);
- printf("thing2.sizeof: %u\n", thing2.sizeof);
+ printf("thing1.sizeof: %zu\n", thing1.sizeof);
+ printf("thing2.sizeof: %zu\n", thing2.sizeof);
assert(thing1.sizeof == long.alignof + long.sizeof);
assert(thing2.sizeof == 8);
C60!(int /*,A60*/ ) container1;
- printf("container1.sizeof: %u\n", container1.sizeof);
+ printf("container1.sizeof: %zu\n", container1.sizeof);
assert(container1.sizeof == (void*).sizeof);
}
@@ -1604,7 +1604,7 @@ template Foo67(alias T)
{
void Foo67()
{
- printf("T = '%.*s'\n", T.length, T.ptr);
+ printf("T = '%.*s'\n", cast(int)T.length, T.ptr);
assert(T == "hello");
}
}
@@ -1620,7 +1620,7 @@ void test67()
/******************************************/
template T68(int a) {
- int vec[a];
+ int[a] vec;
}
void test68()
@@ -1637,7 +1637,7 @@ void test68()
size_t printx(string s)
{
- printf("s = '%.*s'\n", s.length, s.ptr);
+ printf("s = '%.*s'\n", cast(int)s.length, s.ptr);
return s.length;
}
@@ -1735,8 +1735,8 @@ void test72()
static ulong[5] a = [0,1,2,3,4];
static uint[5] b = [0,1,2,3,4];
char[] r;
- r = foo72!(ulong[5])(a); printf("%.*s\n", r.length, r.ptr);
- r = foo72!(uint[5])(b); printf("%.*s\n", r.length, r.ptr);
+ r = foo72!(ulong[5])(a); printf("%.*s\n", cast(int)r.length, r.ptr);
+ r = foo72!(uint[5])(b); printf("%.*s\n", cast(int)r.length, r.ptr);
}
diff --git a/gcc/testsuite/gdc.test/runnable/template13478.d b/gcc/testsuite/gdc.test/runnable/template13478.d
index 7fa7a18aa27..b7b9709a32b 100644
--- a/gcc/testsuite/gdc.test/runnable/template13478.d
+++ b/gcc/testsuite/gdc.test/runnable/template13478.d
@@ -1,3 +1,5 @@
+// EXTRA_FILES: imports/template13478a.d imports/template13478b.d
+
/// Tests emission of templates also referenced in speculative contexts.
/// Failure triggered with -inline.
module template13478;
diff --git a/gcc/testsuite/gdc.test/runnable/template2.d b/gcc/testsuite/gdc.test/runnable/template2.d
index 3adaeae61ae..fe08a6c0738 100644
--- a/gcc/testsuite/gdc.test/runnable/template2.d
+++ b/gcc/testsuite/gdc.test/runnable/template2.d
@@ -1,17 +1,16 @@
-// RUNNABLE_PHOBOS_TEST
// original post to the D newsgroup:
// http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=D&artnum=10554&header
// Test to manipulate 3D vectors, in D!
// by Sean L Palmer (seanpalmer@directvinternet.com)
// This code is released without any warranty. Use at your own risk.
import core.stdc.stdio;
-import std.math : sqrt;
+import core.math : sqrt;
template VecTemplate(tfloat, int dim:3)
{
struct Vector
{
- tfloat d[dim];
+ tfloat[dim] d;
version(none)
{
@@ -21,24 +20,24 @@ template VecTemplate(tfloat, int dim:3)
bool opEquals(Vector b) { for (int i=0; i<dim; ++i) if (d[i] != b.d[i]) return
false; return true; }
// negate (-a)
- Vector opNeg() { Vector r; for (int i=0; i<dim; ++i) r.d[i] = -d[i]; return
+ Vector opUnary(string op : "-")() { Vector r; for (int i=0; i<dim; ++i) r.d[i] = -d[i]; return
r; }
// complement (~a)
- Vector opCom() { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[(i+1)%dim];
+ Vector opUnary(string op : "~")() { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[(i+1)%dim];
d[0] = -d[0]; return r; }
// add (a + b)
- Vector opAdd(Vector b) { Vector r; r.d[] = d[] + b.d[]; return r; }
+ Vector opBinary(string op : "+")(Vector b) { Vector r; r.d[] = d[] + b.d[]; return r; }
// addto (a += b)
- Vector opAddAssign(Vector b) { d[] += b.d[]; return r; }
+ Vector opOpAssign(string op : "+")(Vector b) { d[] += b.d[]; return r; }
// subtract (a - b)
- Vector opSub(Vector b) { Vector r; r.d[] = d[] - b.d[]; return r; }
+ Vector opBinary(string op : "-")(Vector b) { Vector r; r.d[] = d[] - b.d[]; return r; }
// multiply by scalar (a * 2.0)
- Vector opMul(tfloat b) { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[i]
+ Vector opBinary(string op : "*")(tfloat b) { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[i]
* b; return r; }
// divide by scalar (a / b)
- Vector opDiv(tfloat b) { return *this * (1/b); }
+ Vector opBinary(string op : "/")(tfloat b) { return *this * (1/b); }
// dot product (a * b)
- tfloat opMul(Vector b) { tfloat r=0; for (int i=0; i<dim; ++i) r += d[i];
+ tfloat opBinary(string op : "*")(Vector b) { tfloat r=0; for (int i=0; i<dim; ++i) r += d[i];
return r; }
// outer product (a ^ b)
//Vector opXor(Vector b) { Vector r; for (int i=0; i<dim; ++i) r += d[i]; return r; }
@@ -49,27 +48,27 @@ return r; }
const bool opEquals(ref const Vector b) { for (int i=0; i<dim; ++i) if (d[i] != b.d[i]) return
false; return true; }
// negate (-a)
- Vector opNeg() { Vector r; for (int i=0; i<dim; ++i) r.d[i] = -d[i]; return
+ Vector opUnary(string op : "-")() { Vector r; for (int i=0; i<dim; ++i) r.d[i] = -d[i]; return
r; }
// complement (~a)
- Vector opCom() { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[(i+1)%dim];
+ Vector opUnary(string op : "~")() { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[(i+1)%dim];
d[0] = -d[0]; return r; }
// add (a + b)
- Vector opAdd(Vector b) { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[i] +
+ Vector opBinary(string op : "+")(Vector b) { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[i] +
b.d[i]; return r; }
// addto (a += b)
- Vector opAddAssign(Vector b) { for (int i=0; i<dim; ++i) d[i] += b.d[i]; return
+ Vector opOpAssign(string op : "+")(Vector b) { for (int i=0; i<dim; ++i) d[i] += b.d[i]; return
this; }
// subtract (a - b)
- Vector opSub(Vector b) { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[i] -
+ Vector opBinary(string op : "-")(Vector b) { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[i] -
b.d[i]; return r; }
// multiply by scalar (a * 2.0)
- Vector opMul(tfloat b) { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[i] *
+ Vector opBinary(string op : "*")(tfloat b) { Vector r; for (int i=0; i<dim; ++i) r.d[i] = d[i] *
b; return r; }
// divide by scalar (a / b)
- Vector opDiv(tfloat b) { return this * (1/b); }
+ Vector opBinary(string op : "/")(tfloat b) { return this * (1/b); }
// dot product (a * b)
- tfloat opMul(Vector b) { tfloat r=0; for (int i=0; i<dim; ++i) r += d[i];
+ tfloat opBinary(string op : "*")(Vector b) { tfloat r=0; for (int i=0; i<dim; ++i) r += d[i];
return r; }
// outer product (a ^ b)
//Vector opXor(Vector b) { Vector r; for (int i=0; i<dim; ++i) r += d[i]; return r; }
@@ -110,6 +109,3 @@ int main(char[][] args)
printf("closing\n");
return 0;
}
-
-
-
diff --git a/gcc/testsuite/gdc.test/runnable/template4.d b/gcc/testsuite/gdc.test/runnable/template4.d
index 0381d890ceb..ae02c182814 100644
--- a/gcc/testsuite/gdc.test/runnable/template4.d
+++ b/gcc/testsuite/gdc.test/runnable/template4.d
@@ -1,4 +1,4 @@
-/* RUNNABLE_PHOBOS_TEST
+/*
TEST_OUTPUT:
---
This actually gets evaluated!
@@ -10,7 +10,6 @@ Alias Test instantiated
Alias Test instantiated
---
*/
-import std.stdio;
import core.stdc.stdio;
/*********************************************************/
@@ -246,31 +245,6 @@ void test6()
/*********************************************************/
-template factorial7(float n, cdouble c, string sss, string ttt)
-{
- static if (n == 1)
- const float factorial7 = 1;
- else
- const float factorial7 = n * 2;
-}
-
-template bar7(wstring abc, dstring def)
-{
- const int x = 3;
-}
-
-void test7()
-{
- float f = factorial7!(4.25, 6.8+3i, "hello", null);
- printf("%g\n", f);
- assert(f == 8.5);
- int i = bar7!("abc"w, "def"d).x;
- printf("%d\n", i);
- assert(i == 3);
-}
-
-/*********************************************************/
-
template whale(string walrus)
{
const char [] whale = walrus;
@@ -449,20 +423,15 @@ template horse(string w)
void test14()
{
bool lion = zebra!("a");
- writeln(lion);
assert(!lion);
lion = zebra!("aqb");
- writeln(lion);
assert(lion);
lion = horse!("a");
- writeln(lion);
assert(lion);
lion = horse!("aqb");
- writeln(lion);
assert(lion);
lion = horse!("ab");
- writeln(lion);
assert(!lion);
}
@@ -529,7 +498,7 @@ void test16()
{
for (int i=0; i<smallfactorials.length; ++i)
{
- writefln("%d %d", i, smallfactorials[i]);
+ printf("%d %d\n", i, smallfactorials[i]);
assert(smallfactorials[i] == testtable[i]);
}
}
@@ -621,7 +590,7 @@ template sqrt(real x, real root = x/2, int ntries = 0)
void test20()
{
real x = sqrt!(2);
- writefln("%.20g", x); // 1.4142135623730950487
+ printf("%.20Lg\n", x); // 1.4142135623730950487
}
/*********************************************************/
@@ -642,7 +611,7 @@ uint foo21()
void test21()
{
auto i = foo21();
- writeln(i);
+ printf("%d\n", i);
assert(i == 1871483972);
}
@@ -826,10 +795,14 @@ void test31()
i2s[1] = "Hello";
i2s[5] = "There";
- writeln( i2s.get31(1, "yeh") );
- writeln( i2s.get31(2, "default") );
- writeln( i2s.get31(1) );
- writeln( i2s.get31(2) );
+ auto result = i2s.get31(1, "yeh");
+ printf("%.*s\n", cast(int)result.length, result.ptr);
+ result = i2s.get31(2, "default");
+ printf("%.*s\n", cast(int)result.length, result.ptr);
+ result = i2s.get31(1);
+ printf("%.*s\n", cast(int)result.length, result.ptr);
+ result = i2s.get31(2);
+ printf("%.*s\n", cast(int)result.length, result.ptr);
}
/*********************************************************/
@@ -860,7 +833,7 @@ struct Composer(T) {
}
return result;
}
- public void opAddAssign(Fun f) {
+ public void opOpAssign(string op)(Fun f) if (op == "+") {
funs ~= f;
}
}
@@ -904,14 +877,14 @@ void test33() {
comp += delegate double (double x) { return x/3.0;};
comp += delegate double (double x) { return x*x;};
comp += (double x) => x + 1.0;
- writefln("%f", comp(2.0));
+ printf("%f\n", comp(2.0));
// Try function objects
Composer!(double) comp2;
comp2 += tofp!(div3!(double))();
comp2 += tofp!(square!(double))();
comp2 += tofp!(plus1!(double))();
- writefln("%f", comp2( 2.0));
+ printf("%f\n", comp2( 2.0));
}
/*********************************************************/
@@ -1035,7 +1008,7 @@ void instantiate4652()
}
/*********************************************************/
-// 7589
+// https://issues.dlang.org/show_bug.cgi?id=7589
struct T7589(T)
{
@@ -1062,7 +1035,7 @@ void test39()
}
/*********************************************************/
-// 6701
+// https://issues.dlang.org/show_bug.cgi?id=6701
uint foo_6701(uint v:0)() { return 1; }
uint foo_6701(uint v)() { return 0; }
@@ -1076,7 +1049,7 @@ void test6701()
}
/******************************************/
-// 7469
+// https://issues.dlang.org/show_bug.cgi?id=7469
struct Foo7469a(int x) { }
struct Foo7469b(int x) { }
@@ -1142,7 +1115,6 @@ int main()
test4();
test5();
test6();
- test7();
test8();
test9();
test10();
diff --git a/gcc/testsuite/gdc.test/runnable/template9.d b/gcc/testsuite/gdc.test/runnable/template9.d
index 486eb67ab09..63620dd9254 100644
--- a/gcc/testsuite/gdc.test/runnable/template9.d
+++ b/gcc/testsuite/gdc.test/runnable/template9.d
@@ -1,4 +1,5 @@
-/* RUNNABLE_PHOBOS_TEST
+/*
+REQUIRED_ARGS: -preview=rvaluerefparam
PERMUTE_ARGS:
EXTRA_FILES: imports/testmangle.d
TEST_OUTPUT:
@@ -123,19 +124,23 @@ void test4()
/**********************************/
-import std.stdio:writefln;
-
template foo5(T,S)
{
void foo5(T t, S s) {
- writefln("typeof(T)=",typeid(T)," typeof(S)=",typeid(S));
+ const tstr = typeid(T).toString();
+ const sstr = typeid(S).toString();
+ printf("typeof(T)=%.*s typeof(S)=%.*s\n",
+ cast(int)tstr.length, tstr.ptr, cast(int)sstr.length, sstr.ptr);
}
}
template bar5(T,S)
{
void bar5(S s) {
- writefln("typeof(T)=",typeid(T),"typeof(S)=",typeid(S));
+ const tstr = typeid(T).toString();
+ const sstr = typeid(S).toString();
+ printf("typeof(T)=%.*s typeof(S)=%.*s\n",
+ cast(int)tstr.length, tstr.ptr, cast(int)sstr.length, sstr.ptr);
}
}
@@ -200,7 +205,7 @@ void test7()
}
/**********************************/
-// 5946
+// https://issues.dlang.org/show_bug.cgi?id=5946
template TTest8()
{
@@ -217,7 +222,7 @@ void test8()
}
/**********************************/
-// 693
+// https://issues.dlang.org/show_bug.cgi?id=693
template TTest9(alias sym)
{
@@ -236,7 +241,7 @@ void test9()
}
/**********************************/
-// 1780
+// https://issues.dlang.org/show_bug.cgi?id=1780
template Tuple1780(Ts ...) { alias Ts Tuple1780; }
@@ -253,7 +258,7 @@ void test1780()
}
/**********************************/
-// 1659
+// https://issues.dlang.org/show_bug.cgi?id=1659
class Foo1659 { }
class Bar1659 : Foo1659 { }
@@ -267,7 +272,7 @@ void test1659()
}
/**********************************/
-// 2025
+// https://issues.dlang.org/show_bug.cgi?id=2025
struct S2025 {}
void f2025() {}
@@ -294,7 +299,7 @@ static assert(Baz2025!S2025 == 1); // 2 -> 1
static assert(Baz2025!f2025 == 2);
/**********************************/
-// 3608
+// https://issues.dlang.org/show_bug.cgi?id=3608
template foo3608(T, U){}
@@ -318,7 +323,7 @@ void test3608()
}
/**********************************/
-// 5015
+// https://issues.dlang.org/show_bug.cgi?id=5015
import breaker;
@@ -333,7 +338,7 @@ template _ElemType(T) {
}
/**********************************/
-// 5185
+// https://issues.dlang.org/show_bug.cgi?id=5185
class C5185(V)
{
@@ -349,7 +354,7 @@ void test5185()
}
/**********************************/
-// 5893
+// https://issues.dlang.org/show_bug.cgi?id=5893
class C5893
{
@@ -372,7 +377,7 @@ void test5893()
}
/**********************************/
-// 5988
+// https://issues.dlang.org/show_bug.cgi?id=5988
template Templ5988(alias T)
{
@@ -388,7 +393,7 @@ Templ5988!C5988b foo5988b; // Uncomment version
void test5988b() { C5988b!int a; } // Works
/**********************************/
-// 6404
+// https://issues.dlang.org/show_bug.cgi?id=6404
// receive only rvalue
void rvalue(T)(auto ref T x) if (!__traits(isRef, x)) {}
@@ -416,7 +421,7 @@ void test6404()
}
/**********************************/
-// 2246
+// https://issues.dlang.org/show_bug.cgi?id=2246
class A2246(T,d){
T p;
@@ -452,7 +457,7 @@ void test2246(){
}
/**********************************/
-// 2296
+// https://issues.dlang.org/show_bug.cgi?id=2296
void foo2296(size_t D)(int[D] i...){}
void test2296()
@@ -461,7 +466,7 @@ void test2296()
}
/**********************************/
-// 1684
+// https://issues.dlang.org/show_bug.cgi?id=1684
template Test1684( uint memberOffset ){}
@@ -494,7 +499,7 @@ void bug4984() {
}
/***************************************/
-// 2579
+// https://issues.dlang.org/show_bug.cgi?id=2579
void foo2579(T)(T delegate(in Object) dlg)
{
@@ -506,7 +511,7 @@ void test2579()
}
/**********************************/
-// 2803
+// https://issues.dlang.org/show_bug.cgi?id=2803
auto foo2803(T)(T t = 0) { return t; }
@@ -576,7 +581,7 @@ void test2803()
}
/**********************************/
-// 6613
+// https://issues.dlang.org/show_bug.cgi?id=6613
alias Tuple6613(T...) = T;
@@ -592,13 +597,14 @@ void test6613()
}
/**********************************/
-// 4953
+// https://issues.dlang.org/show_bug.cgi?id=4953
void bug4953(T = void)(short x) {}
static assert(is(typeof(bug4953(3))));
/**********************************/
-// 5886 & 5393
+// https://issues.dlang.org/show_bug.cgi?id=5886
+// https://issues.dlang.org/show_bug.cgi?id=5393
mixin template Foo5886(T)
{
@@ -666,7 +672,7 @@ void test5393()
}
/**********************************/
-// 5896
+// https://issues.dlang.org/show_bug.cgi?id=5896
struct X5896
{
@@ -691,7 +697,7 @@ void test5896()
}
/**********************************/
-// 6312
+// https://issues.dlang.org/show_bug.cgi?id=6312
void h6312() {}
@@ -715,7 +721,7 @@ void test6312()
}
/**********************************/
-// 6825
+// https://issues.dlang.org/show_bug.cgi?id=6825
void test6825()
{
@@ -731,7 +737,7 @@ void test6825()
}
/**********************************/
-// 6789
+// https://issues.dlang.org/show_bug.cgi?id=6789
template isStaticArray6789(T)
{
@@ -751,7 +757,7 @@ void test6789()
}
/**********************************/
-// 2778
+// https://issues.dlang.org/show_bug.cgi?id=2778
struct ArrayWrapper2778(T)
{
@@ -806,7 +812,7 @@ void test2778get()
static struct S
{
ubyte[] val = [1,2,3];
- @property ref ubyte[] get(){ return val; }
+ @property ref ubyte[] get() return { return val; }
alias get this;
}
S s;
@@ -814,7 +820,7 @@ void test2778get()
}
/**********************************/
-// 6208
+// https://issues.dlang.org/show_bug.cgi?id=6208
int getRefNonref(T)(ref T s){ return 1; }
int getRefNonref(T)( T s){ return 2; }
@@ -929,7 +935,7 @@ void test6208c()
}
/**********************************/
-// 6805
+// https://issues.dlang.org/show_bug.cgi?id=6805
struct T6805
{
@@ -941,7 +947,7 @@ struct T6805
static assert(is(T6805.xxx.Type == int));
/**********************************/
-// 6738
+// https://issues.dlang.org/show_bug.cgi?id=6738
struct Foo6738
{
@@ -960,7 +966,7 @@ void test6738()
}
/**********************************/
-// 7498
+// https://issues.dlang.org/show_bug.cgi?id=7498
template IndexMixin(){
void insert(T)(T value){ }
@@ -976,7 +982,7 @@ class MultiIndexContainer{
}
/**********************************/
-// 6780
+// https://issues.dlang.org/show_bug.cgi?id=6780
@property int foo6780()(){ return 10; }
@@ -993,7 +999,7 @@ void test6780()
}
/**********************************/
-// 6810
+// https://issues.dlang.org/show_bug.cgi?id=6810
int f6810(int n)(int) { return 1;}
int f6810(U...)(U) { assert(0); }
@@ -1007,7 +1013,7 @@ void test6810()
}
/**********************************/
-// 6891
+// https://issues.dlang.org/show_bug.cgi?id=6891
struct S6891(int N, T)
{
@@ -1021,7 +1027,7 @@ void test6891()
}
/**********************************/
-// 6994
+// https://issues.dlang.org/show_bug.cgi?id=6994
struct Foo6994
{
@@ -1044,7 +1050,7 @@ void test6994()
}
/**********************************/
-// 6764
+// https://issues.dlang.org/show_bug.cgi?id=6764
enum N6764 = 1; //use const for D1
@@ -1064,7 +1070,8 @@ void test6764()
}
/**********************************/
-// 3467 & 6806
+// https://issues.dlang.org/show_bug.cgi?id=3467
+// https://issues.dlang.org/show_bug.cgi?id=6806
struct Foo3467( uint n )
{
@@ -1087,11 +1094,11 @@ void test3467()
a1 ~ a2; // line 7, Error
}
-struct TS6806(size_t n) { pragma(msg, typeof(n)); }
+struct TS6806(uint n) { pragma(msg, typeof(n)); }
static assert(is(TS6806!(1u) == TS6806!(1)));
/**********************************/
-// 4413
+// https://issues.dlang.org/show_bug.cgi?id=4413
struct Foo4413
{
@@ -1112,7 +1119,7 @@ void test4413()
}
/**********************************/
-// 4675
+// https://issues.dlang.org/show_bug.cgi?id=4675
template isNumeric(T)
{
@@ -1130,7 +1137,7 @@ void test4675()
}
/**********************************/
-// 5525
+// https://issues.dlang.org/show_bug.cgi?id=5525
template foo5525(T)
{
@@ -1146,7 +1153,7 @@ void test5525()
}
/**********************************/
-// 5801
+// https://issues.dlang.org/show_bug.cgi?id=5801
int a5801;
void bar5801(T = double)(typeof(a5801) i) {}
@@ -1158,7 +1165,7 @@ void test5801()
}
/**********************************/
-// 5832
+// https://issues.dlang.org/show_bug.cgi?id=5832
struct Bar5832(alias v) {}
@@ -1188,7 +1195,7 @@ static assert( isBar5832b!(Bar5832!1234));
static assert(!isBar5832c!(Bar5832!1234));
/**********************************/
-// 2550
+// https://issues.dlang.org/show_bug.cgi?id=2550
template pow10_2550(long n)
{
@@ -1246,7 +1253,7 @@ void test10()
}
/**********************************/
-// 3092
+// https://issues.dlang.org/show_bug.cgi?id=3092
template Foo3092(A...)
{
@@ -1255,7 +1262,7 @@ template Foo3092(A...)
static assert(is(Foo3092!(int, "foo") == int));
/**********************************/
-// 7037
+// https://issues.dlang.org/show_bug.cgi?id=7037
struct Foo7037 {}
struct Bar7037 { Foo7037 f; alias f this; }
@@ -1270,7 +1277,7 @@ void test7037()
}
/**********************************/
-// 7110
+// https://issues.dlang.org/show_bug.cgi?id=7110
struct S7110
{
@@ -1304,7 +1311,7 @@ alias T7110!( (e7110[0]) ) D1; // passes
alias T7110!( e7110[0] ) D2; // fails: e7110 must be an array or pointer type, not S7110
/**********************************/
-// 7124
+// https://issues.dlang.org/show_bug.cgi?id=7124
template StaticArrayOf(T : E[dim], E, size_t dim)
{
@@ -1336,7 +1343,7 @@ void test7124()
}
/**********************************/
-// 7359
+// https://issues.dlang.org/show_bug.cgi?id=7359
bool foo7359(T)(T[] a ...)
{
@@ -1350,7 +1357,7 @@ void test7359()
}
/**********************************/
-// 7363
+// https://issues.dlang.org/show_bug.cgi?id=7363
template t7363()
{
@@ -1382,7 +1389,7 @@ alias S4371!("hi!") t;
static if (is(t U == S4371!(U))) { }
/**********************************/
-// 7416
+// https://issues.dlang.org/show_bug.cgi?id=7416
void t7416(alias a)() if(is(typeof(a())))
{}
@@ -1393,7 +1400,7 @@ void test7416() {
}
/**********************************/
-// 7563
+// https://issues.dlang.org/show_bug.cgi?id=7563
class Test7563
{
@@ -1411,7 +1418,7 @@ void test7563()
}
/**********************************/
-// 7572
+// https://issues.dlang.org/show_bug.cgi?id=7572
class F7572
{
@@ -1430,7 +1437,7 @@ void test7572()
}
/**********************************/
-// 7580
+// https://issues.dlang.org/show_bug.cgi?id=7580
struct S7580(T)
{
@@ -1462,7 +1469,7 @@ void test7580()
}
/**********************************/
-// 7585
+// https://issues.dlang.org/show_bug.cgi?id=7585
extern(C) alias void function() Callback;
@@ -1499,7 +1506,7 @@ void test7585()
}
/**********************************/
-// 7643
+// https://issues.dlang.org/show_bug.cgi?id=7643
template T7643(A...){ alias A T7643; }
@@ -1508,7 +1515,7 @@ alias T7643!(long, "x", string, "y") Specs7643;
alias T7643!( Specs7643[] ) U7643; // Error: tuple A is used as a type
/**********************************/
-// 7671
+// https://issues.dlang.org/show_bug.cgi?id=7671
inout(int)[3] id7671n1 ( inout(int)[3] );
inout( U )[n] id7671x1(U, size_t n)( inout( U )[n] );
@@ -1526,7 +1533,7 @@ void test7671()
}
/************************************/
-// 7672
+// https://issues.dlang.org/show_bug.cgi?id=7672
T foo7672(T)(T a){ return a; }
@@ -1540,7 +1547,7 @@ void test7672(inout(int[]) a = null, inout(int*) p = null)
}
/**********************************/
-// 7684
+// https://issues.dlang.org/show_bug.cgi?id=7684
U[] id7684(U)( U[] );
shared(U[]) id7684(U)( shared(U[]) );
@@ -1552,7 +1559,7 @@ void test7684()
}
/**********************************/
-// 7694
+// https://issues.dlang.org/show_bug.cgi?id=7694
void match7694(alias m)()
{
@@ -1573,7 +1580,7 @@ struct T7694
}
/**********************************/
-// 7755
+// https://issues.dlang.org/show_bug.cgi?id=7755
template to7755(T)
{
@@ -1628,7 +1635,7 @@ void test11b()
}
/**********************************/
-// 7769
+// https://issues.dlang.org/show_bug.cgi?id=7769
void f7769(K)(inout(K) value){}
void test7769()
@@ -1637,7 +1644,7 @@ void test7769()
}
/**********************************/
-// 7812
+// https://issues.dlang.org/show_bug.cgi?id=7812
template A7812(T...) {}
@@ -1651,7 +1658,7 @@ template D7812()
static assert(!__traits(compiles, D7812!()));
/**********************************/
-// 7873
+// https://issues.dlang.org/show_bug.cgi?id=7873
inout(T)* foo(T)(inout(T)* t)
{
@@ -1671,7 +1678,7 @@ void test7873()
}
/**********************************/
-// 7933
+// https://issues.dlang.org/show_bug.cgi?id=7933
struct Boo7933(size_t dim){int a;}
struct Baa7933(size_t dim)
@@ -1690,7 +1697,7 @@ void test7933()
}
/**********************************/
-// 8094
+// https://issues.dlang.org/show_bug.cgi?id=8094
struct Tuple8094(T...) {}
@@ -1728,14 +1735,14 @@ void test12()
}
/**********************************/
-// 14290
+// https://issues.dlang.org/show_bug.cgi?id=14290
struct Foo14290(int i) {}
alias Foo14290a = Foo14290!1;
static assert(!is(Foo14290!2 == Foo14290a!T, T...));
/**********************************/
-// 8125
+// https://issues.dlang.org/show_bug.cgi?id=8125
void foo8125(){}
@@ -1823,7 +1830,7 @@ void test15()
}
/**********************************/
-// 8129
+// https://issues.dlang.org/show_bug.cgi?id=8129
class X8129 {}
class A8129 {}
@@ -1857,7 +1864,7 @@ void test8129()
}
/**********************************/
-// 8238
+// https://issues.dlang.org/show_bug.cgi?id=8238
void test8238()
{
@@ -1873,7 +1880,7 @@ void test8238()
}
/**********************************/
-// 8669
+// https://issues.dlang.org/show_bug.cgi?id=8669
struct X8669
{
@@ -1939,7 +1946,7 @@ void test8669()
}
/**********************************/
-// 8833
+// https://issues.dlang.org/show_bug.cgi?id=8833
template TypeTuple8833(T...) { alias TypeTuple = T; }
@@ -1956,7 +1963,7 @@ void test8833()
}
/**********************************/
-// 8976
+// https://issues.dlang.org/show_bug.cgi?id=8976
void f8976(ref int) { }
@@ -1971,17 +1978,17 @@ void h8976()()
g8976!()();
}
-static assert(! __traits(compiles, h8976!()() ) ); // causes error
-static assert(!is(typeof( h8976!()() )));
+static assert( __traits(compiles, h8976!()() ) ); // causes error
+static assert(is(typeof( h8976!()() )));
void test8976()
{
- static assert(! __traits(compiles, h8976!()() ) );
- static assert(!is(typeof( h8976!()() )));
+ static assert( __traits(compiles, h8976!()() ) );
+ static assert(is(typeof( h8976!()() )));
}
/****************************************/
-// 8940
+// https://issues.dlang.org/show_bug.cgi?id=8940
const int n8940; // or `immutable`
static this() { n8940 = 3; }
@@ -2002,7 +2009,8 @@ void test8940()
}
/**********************************/
-// 6969 + 8990
+// https://issues.dlang.org/show_bug.cgi?id=6969
+// https://issues.dlang.org/show_bug.cgi?id=8990
class A6969() { alias C6969!() C1; }
class B6969 { alias A6969!() A1; }
@@ -2013,7 +2021,7 @@ struct B8990(T) { A8990!T* a; }
struct C8990 { B8990!C8990* b; }
/**********************************/
-// 9018
+// https://issues.dlang.org/show_bug.cgi?id=9018
template Inst9018(alias Template, T)
{
@@ -2029,7 +2037,7 @@ static assert(!__traits(compiles, Inst9018!(Template9018, int))); // Assert pass
static assert(!__traits(compiles, Inst9018!(Template9018, int))); // Assert fails
/**********************************/
-// 9022
+// https://issues.dlang.org/show_bug.cgi?id=9022
class C9022
{
@@ -2074,7 +2082,7 @@ void test9022()
}
/**********************************/
-// 9026
+// https://issues.dlang.org/show_bug.cgi?id=9026
mixin template node9026()
{
@@ -2107,7 +2115,7 @@ void test9026()
}
/**********************************/
-// 9038
+// https://issues.dlang.org/show_bug.cgi?id=9038
mixin template Foo9038()
{
@@ -2142,7 +2150,7 @@ void test9038()
}
/**********************************/
-// 9050
+// https://issues.dlang.org/show_bug.cgi?id=9050
struct A9050(T) {}
@@ -2159,7 +2167,7 @@ auto foo9050()(A9050!int base) pure
auto s9050 = foo9050(A9050!int());
/**********************************/
-// 10936 (dup of 9050)
+// https://issues.dlang.org/show_bug.cgi?id=10936 (dup of 9050)
struct Vec10936(string s)
{
@@ -2178,7 +2186,7 @@ struct Vec10936(string s)
Vec10936!"" v;
/**********************************/
-// 9076
+// https://issues.dlang.org/show_bug.cgi?id=9076
template forward9076(args...)
{
@@ -2193,7 +2201,7 @@ void test9076()
}
/**********************************/
-// 9083
+// https://issues.dlang.org/show_bug.cgi?id=9083
template isFunction9083(X...) if (X.length == 1)
{
@@ -2231,7 +2239,7 @@ class C9083
}
/**********************************/
-// 9100
+// https://issues.dlang.org/show_bug.cgi?id=9100
template Id(alias A) { alias Id = A; }
template ErrId(alias A) { static assert(0); }
@@ -2287,7 +2295,7 @@ void test9100()
}
/**********************************/
-// 9101
+// https://issues.dlang.org/show_bug.cgi?id=9101
class Node9101
{
@@ -2300,7 +2308,7 @@ class Node9101
enum x9101 = __traits(compiles, Node9101.ForwardCtorNoId!());
/**********************************/
-// 9124
+// https://issues.dlang.org/show_bug.cgi?id=9124
struct Foo9124a(N...)
{
@@ -2353,7 +2361,7 @@ void test9124b()
}
/**********************************/
-// 9143
+// https://issues.dlang.org/show_bug.cgi?id=9143
struct Foo9143a(bool S, bool L)
{
@@ -2392,7 +2400,7 @@ void test9143()
}
/**********************************/
-// 9266
+// https://issues.dlang.org/show_bug.cgi?id=9266
template Foo9266(T...)
{
@@ -2408,7 +2416,7 @@ void test9266()
}
/**********************************/
-// 9361
+// https://issues.dlang.org/show_bug.cgi?id=9361
struct Unit9361(A)
{
@@ -2429,7 +2437,7 @@ void test9361()
}
/**********************************/
-// 9536
+// https://issues.dlang.org/show_bug.cgi?id=9536
struct S9536
{
@@ -2450,7 +2458,7 @@ void test9536()
}
/**********************************/
-// 9578
+// https://issues.dlang.org/show_bug.cgi?id=9578
template t9578(alias f) { void tf()() { f(); } }
@@ -2467,7 +2475,7 @@ void test9578()
}
/**********************************/
-// 9596
+// https://issues.dlang.org/show_bug.cgi?id=9596
int foo9596a(K, V)(inout( V [K])) { return 1; }
int foo9596a(K, V)(inout(shared(V) [K])) { return 2; }
@@ -2499,7 +2507,7 @@ void test9596()
}
/******************************************/
-// 9806
+// https://issues.dlang.org/show_bug.cgi?id=9806
struct S9806a(alias x)
{
@@ -2535,7 +2543,7 @@ alias S9806c!1 One9806c;
alias S9806c!0.Next!() OneAgain9806c;
/******************************************/
-// 9837
+// https://issues.dlang.org/show_bug.cgi?id=9837
void test9837()
{
@@ -2576,7 +2584,7 @@ void test9837()
}
/******************************************/
-// 9874
+// https://issues.dlang.org/show_bug.cgi?id=9874
bool foo9874() { return true; }
void bar9874(T)(T) if (foo9874()) {} // OK
@@ -2667,7 +2675,7 @@ void test9885()
}
/******************************************/
-// 9971
+// https://issues.dlang.org/show_bug.cgi?id=9971
void goo9971()()
{
@@ -2691,7 +2699,7 @@ void test9971()
}
/******************************************/
-// 9977
+// https://issues.dlang.org/show_bug.cgi?id=9977
void test9977()
{
@@ -2730,7 +2738,7 @@ static assert(T8848b!([1:2,3:4]) == [1:2,3:4]);
static assert(T8848c!(null) == null);
/******************************************/
-// 9990
+// https://issues.dlang.org/show_bug.cgi?id=9990
auto initS9990() { return "hi"; }
@@ -2739,7 +2747,7 @@ class C9990(alias init) {}
alias SC9990 = C9990!(initS9990);
/******************************************/
-// 10067
+// https://issues.dlang.org/show_bug.cgi?id=10067
struct assumeSize10067(alias F) {}
@@ -2753,7 +2761,7 @@ template useItemAt10067(size_t idx, T)
useItemAt10067!(0, char) mapS10067;
/******************************************/
-// 4072
+// https://issues.dlang.org/show_bug.cgi?id=4072
void bug4072(T)(T x)
if (is(typeof(bug4072(x))))
@@ -2762,7 +2770,7 @@ void bug4072(T)(T x)
static assert(!is(typeof(bug4072(7))));
/******************************************/
-// 10074
+// https://issues.dlang.org/show_bug.cgi?id=10074
template foo10074(F)
{
@@ -2777,7 +2785,7 @@ bool foo10074(F)(F f)
static assert(!is(typeof(foo10074(1))));
/******************************************/
-// 10083
+// https://issues.dlang.org/show_bug.cgi?id=10083
// [a-c] IFTI can find syntactic eponymous member
template foo10083a(T)
@@ -2867,7 +2875,7 @@ void test10083()
}
/******************************************/
-// 10134
+// https://issues.dlang.org/show_bug.cgi?id=10134
template ReturnType10134(alias func)
{
@@ -2914,7 +2922,7 @@ template b10134()
pragma(msg, getResultType10134!(a10134!()));
/******************************************/
-// 10313
+// https://issues.dlang.org/show_bug.cgi?id=10313
void test10313()
{
@@ -2933,7 +2941,7 @@ void test10313()
}
/******************************************/
-// 10498
+// https://issues.dlang.org/show_bug.cgi?id=10498
template triggerIssue10498a()
{
@@ -2985,7 +2993,7 @@ void test10498b()
}
/******************************************/
-// 10537
+// https://issues.dlang.org/show_bug.cgi?id=10537
struct Iota10537
{
@@ -3010,7 +3018,7 @@ dstring rewriteCode10537(dstring code)
}
/******************************************/
-// 10558
+// https://issues.dlang.org/show_bug.cgi?id=10558
template Template10558() {}
@@ -3029,7 +3037,7 @@ template foo10558(alias T)
}
/******************************************/
-// 10592
+// https://issues.dlang.org/show_bug.cgi?id=10592
void test10592()
{
@@ -3058,7 +3066,7 @@ void test10592()
}
/******************************************/
-// 11242
+// https://issues.dlang.org/show_bug.cgi?id=11242
inout(T[]) fromString11242(T)(inout(char[]) s, T[] dst)
{
@@ -3072,7 +3080,7 @@ void test11242()
}
/******************************************/
-// 10811
+// https://issues.dlang.org/show_bug.cgi?id=10811
void foo10811a(R1, R2)(R1, R2) {}
template foo10811a(alias pred) { void foo10811a(R1, R2)(R1, R2) {} }
@@ -3090,7 +3098,7 @@ void test10811()
}
/******************************************/
-// 10969
+// https://issues.dlang.org/show_bug.cgi?id=10969
template A10969(T, U...) { alias A10969 = T; }
void foo10969(T, U...)(A10969!(T, U) a) {}
@@ -3105,7 +3113,7 @@ void test10969()
}
/******************************************/
-// 11271
+// https://issues.dlang.org/show_bug.cgi?id=11271
struct SmartPtr11271(T)
{
@@ -3120,7 +3128,7 @@ void test11271()
}
/******************************************/
-// 11533
+// https://issues.dlang.org/show_bug.cgi?id=11533
version (none)
{
@@ -3171,7 +3179,7 @@ void test11533()
}
/******************************************/
-// 11553
+// https://issues.dlang.org/show_bug.cgi?id=11553
struct Pack11553(T ...)
{
@@ -3226,7 +3234,7 @@ static if ( hl11553!(Pack11553!(5))) { pragma(msg, "All good 2"); }
static if (!hl11553!(Pack11553!( ))) { pragma(msg, "All good 3"); }
/******************************************/
-// 11818
+// https://issues.dlang.org/show_bug.cgi?id=11818
enum E11818 { e0, e1 }
@@ -3244,13 +3252,13 @@ void test11818()
}
/******************************************/
-// 11843
+// https://issues.dlang.org/show_bug.cgi?id=11843
void test11843()
{
struct Foo
{
- int x[string];
+ int[string] x;
}
struct Bar(alias foo) {}
@@ -3268,7 +3276,7 @@ void test11843()
}
/******************************************/
-// 11872
+// https://issues.dlang.org/show_bug.cgi?id=11872
class Foo11872
{
@@ -3299,7 +3307,7 @@ void test11872()
}
/******************************************/
-// 12042
+// https://issues.dlang.org/show_bug.cgi?id=12042
struct S12042
{
@@ -3324,7 +3332,7 @@ int test12042()
static assert(test12042());
/******************************************/
-// 12077
+// https://issues.dlang.org/show_bug.cgi?id=12077
struct S12077(A) {}
@@ -3336,7 +3344,7 @@ alias U12077( T : Base!Args, alias Base, Args...) = Base;
static assert(__traits(isSame, U12077!(S12077!int), S12077));
/******************************************/
-// 12262
+// https://issues.dlang.org/show_bug.cgi?id=12262
template Inst12262(T) { int x; }
@@ -3347,7 +3355,7 @@ static assert(fqnSym12262!(Inst12262!(Object)) == 2);
static assert(fqnSym12262!(Inst12262!(Object).x) == 1);
/******************************************/
-// 12264
+// https://issues.dlang.org/show_bug.cgi?id=12264
struct S12264(A) {}
@@ -3372,7 +3380,7 @@ static assert(TY12264!(S12264!int) == 2);
static assert(TZ12264!(S12264!int) == 2);
/******************************************/
-// 12122
+// https://issues.dlang.org/show_bug.cgi?id=12122
enum N12122 = 1;
@@ -3385,7 +3393,7 @@ void test12122()
}
/******************************************/
-// 12186
+// https://issues.dlang.org/show_bug.cgi?id=12186
template map_front12186(fun...)
{
@@ -3406,7 +3414,7 @@ void test12186()
}
/******************************************/
-// 12207
+// https://issues.dlang.org/show_bug.cgi?id=12207
void test12207()
{
@@ -3421,7 +3429,7 @@ void test12207()
}
/******************************************/
-// 12263
+// https://issues.dlang.org/show_bug.cgi?id=12263
template A12263(alias a) { int x; }
template B12263(alias a) { int x; }
@@ -3435,7 +3443,7 @@ static assert(fqnSym12263!(A12263!(Object)));
static assert(fqnSym12263!(B12263!(Object)));
/******************************************/
-// 12290
+// https://issues.dlang.org/show_bug.cgi?id=12290
void test12290()
{
@@ -3520,7 +3528,7 @@ void test12290()
}
/******************************************/
-// 12292
+// https://issues.dlang.org/show_bug.cgi?id=12292
void test12292()
{
@@ -3532,7 +3540,7 @@ void test12292()
}
/******************************************/
-// 12376
+// https://issues.dlang.org/show_bug.cgi?id=12376
static auto encode12376(size_t sz)(dchar ch) if (sz > 1)
{
@@ -3545,7 +3553,7 @@ void test12376()
}
/******************************************/
-// 12447
+// https://issues.dlang.org/show_bug.cgi?id=12447
enum test12447(string str) = str; // [1]
string test12447(T...)(T args) if (T.length) { return args[0]; } // [2]
@@ -3560,7 +3568,7 @@ static assert(test12447("foo") == "foo");
static assert(test12447!("bar") == "bar");
/******************************************/
-// 12651
+// https://issues.dlang.org/show_bug.cgi?id=12651
alias TemplateArgsOf12651(alias T : Base!Args, alias Base, Args...) = Args;
@@ -3569,7 +3577,7 @@ struct S12651(T) { }
static assert(!__traits(compiles, TemplateArgsOf12651!(S12651!int, S, float)));
/******************************************/
-// 12719
+// https://issues.dlang.org/show_bug.cgi?id=12719
struct A12719
{
@@ -3598,7 +3606,7 @@ struct W12719(R)
W12719!int a12719;
/******************************************/
-// 12746
+// https://issues.dlang.org/show_bug.cgi?id=12746
template foo12746()
{
@@ -3620,7 +3628,7 @@ void test12746()
}
/******************************************/
-// 12748
+// https://issues.dlang.org/show_bug.cgi?id=12748
void foo12748(S, C : typeof(S.init[0]))(S s, C c)
{
@@ -3632,7 +3640,7 @@ void test12748()
}
/******************************************/
-// 9708
+// https://issues.dlang.org/show_bug.cgi?id=9708
struct S9708
{
@@ -3646,13 +3654,13 @@ void test9708()
}
/******************************************/
-// 12880
+// https://issues.dlang.org/show_bug.cgi?id=12880
void f12880(T)(in T value) { static assert(is(T == string)); }
void test12880() { f12880(string.init); }
/******************************************/
-// 13087
+// https://issues.dlang.org/show_bug.cgi?id=13087
struct Vec13087
{
@@ -3674,7 +3682,7 @@ template component13087(alias vec, char c)
}
/******************************************/
-// 13127
+// https://issues.dlang.org/show_bug.cgi?id=13127
/+void test13127(inout int = 0)
{
@@ -3895,7 +3903,7 @@ void test13127a()
}
/******************************************/
-// 13159
+// https://issues.dlang.org/show_bug.cgi?id=13159
template maxSize13159(T...)
{
@@ -3924,7 +3932,7 @@ struct Node13159
}
/******************************************/
-// 13180
+// https://issues.dlang.org/show_bug.cgi?id=13180
void test13180()
{
@@ -3962,7 +3970,7 @@ void test13180()
}
/******************************************/
-// 13204
+// https://issues.dlang.org/show_bug.cgi?id=13204
struct A13204(uint v)
{
@@ -3994,7 +4002,7 @@ void test13204()
}
/******************************************/
-// 8462 (dup of 13204)
+// https://issues.dlang.org/show_bug.cgi?id=8462 (dup of 13204)
alias FP8462 = void function(C8462.Type arg);
@@ -4005,7 +4013,7 @@ class C8462
}
/******************************************/
-// 13218
+// https://issues.dlang.org/show_bug.cgi?id=13218
template isCallable13218(T...)
if (T.length == 1)
@@ -4039,7 +4047,7 @@ struct R13218
}
/******************************************/
-// 13219
+// https://issues.dlang.org/show_bug.cgi?id=13219
struct Map13219(V) {}
@@ -4059,7 +4067,7 @@ void test13219()
}
/******************************************/
-// 13223
+// https://issues.dlang.org/show_bug.cgi?id=13223
void test13223()
{
@@ -4125,7 +4133,7 @@ void test13223a()
}
/******************************************/
-// 13235
+// https://issues.dlang.org/show_bug.cgi?id=13235
struct Tuple13235(T...)
{
@@ -4171,7 +4179,7 @@ void test13235()
}
/******************************************/
-// 13252
+// https://issues.dlang.org/show_bug.cgi?id=13252
alias TypeTuple13252(T...) = T;
@@ -4181,9 +4189,6 @@ static assert(is(typeof(TypeTuple13252!(cast(long)1)[0]) == long));
static assert(is(typeof(TypeTuple13252!(cast(float )3.14)[0]) == float ));
static assert(is(typeof(TypeTuple13252!(cast(double)3.14)[0]) == double));
-static assert(is(typeof(TypeTuple13252!(cast(cfloat )(1 + 2i))[0]) == cfloat ));
-static assert(is(typeof(TypeTuple13252!(cast(cdouble)(1 + 2i))[0]) == cdouble));
-
static assert(is(typeof(TypeTuple13252!(cast(string )null)[0]) == string ));
static assert(is(typeof(TypeTuple13252!(cast(string[])null)[0]) == string[])); // OK <- NG
@@ -4198,7 +4203,7 @@ static assert(is(typeof(TypeTuple13252!(const S13252())[0]) == const(S13
static assert(is(typeof(TypeTuple13252!(immutable S13252())[0]) == immutable(S13252))); // OK <- NG
/******************************************/
-// 13294
+// https://issues.dlang.org/show_bug.cgi?id=13294
void test13294()
{
@@ -4218,7 +4223,7 @@ void test13294()
f(src, dst);
}
- // 13351
+ // https://issues.dlang.org/show_bug.cgi?id=13351
T add(T)(in T x, in T y)
{
T z;
@@ -4232,7 +4237,7 @@ void test13294()
}
/******************************************/
-// 13299
+// https://issues.dlang.org/show_bug.cgi?id=13299
struct Foo13299
{
@@ -4263,7 +4268,7 @@ void test13299()
}
/******************************************/
-// 13333
+// https://issues.dlang.org/show_bug.cgi?id=13333
template AliasThisTypeOf13333(T)
{
@@ -4315,7 +4320,7 @@ void test13333()
}
/******************************************/
-// 13374
+// https://issues.dlang.org/show_bug.cgi?id=13374
int f13374(alias a)() { return 1; }
int f13374(string s)() { return 2; }
@@ -4328,7 +4333,7 @@ void test13374()
}
/******************************************/
-// 14109
+// https://issues.dlang.org/show_bug.cgi?id=14109
string f14109() { return "a"; }
string g14109()() { return "a"; }
@@ -4340,7 +4345,7 @@ alias Y14109 = S14109!(g14109!());
static assert(is(X14109 == Y14109));
/******************************************/
-// 13378
+// https://issues.dlang.org/show_bug.cgi?id=13378
struct Vec13378(size_t n, T, string as)
{
@@ -4356,7 +4361,7 @@ void test13378()
}
/******************************************/
-// 13379
+// https://issues.dlang.org/show_bug.cgi?id=13379
void test13379()
{
@@ -4395,7 +4400,7 @@ MinType13379!T min13379(T...)(T args) // #4 MinType!uint (speculative && thist
}
/******************************************/
-// 13417
+// https://issues.dlang.org/show_bug.cgi?id=13417
struct V13417(size_t N, E, alias string AS)
{
@@ -4412,7 +4417,7 @@ void test13417()
}
/******************************************/
-// 13484
+// https://issues.dlang.org/show_bug.cgi?id=13484
int foo13484()(void delegate() hi) { return 1; }
int foo13484(T)(void delegate(T) hi) { return 2; }
@@ -4424,7 +4429,7 @@ void test13484()
}
/******************************************/
-// 13675
+// https://issues.dlang.org/show_bug.cgi?id=13675
enum E13675;
@@ -4440,7 +4445,7 @@ void test13675()
}
/******************************************/
-// 13694
+// https://issues.dlang.org/show_bug.cgi?id=13694
auto foo13694(T)(string A, T[] G ...) { return 1; }
auto foo13694(T)(string A, long E, T[] G ...) { return 2; }
@@ -4456,7 +4461,7 @@ void test13694()
}
/******************************************/
-// 13760
+// https://issues.dlang.org/show_bug.cgi?id=13760
void test13760()
{
@@ -4468,7 +4473,7 @@ void test13760()
}
/******************************************/
-// 13714
+// https://issues.dlang.org/show_bug.cgi?id=13714
struct JSONValue13714
{
@@ -4497,7 +4502,7 @@ void test13714()
}
/******************************************/
-// 13807
+// https://issues.dlang.org/show_bug.cgi?id=13807
T f13807(T)(inout(T)[] arr)
{
@@ -4512,7 +4517,7 @@ void test13807()
}
/******************************************/
-// 14174
+// https://issues.dlang.org/show_bug.cgi?id=14174
import imports.testmangle;
struct Config14174(a, b) {}
@@ -4521,6 +4526,7 @@ struct N14174 {}
alias defConfig14174 = Config14174!(N14174, N14174);
+@safe @nogc pure nothrow
void accepter14174a(Config : Config14174!(T) = defConfig14174, T...)()
{
static assert(equalDemangle(accepter14174a.mangleof,
@@ -4528,9 +4534,10 @@ void accepter14174a(Config : Config14174!(T) = defConfig14174, T...)()
"accepter14174a"~
"HTS7breaker51__T11Config14174TS7breaker6N14174TS7breaker6N14174Z11Config14174TS7breaker6N14174TS7breaker6N14174Z14"~
"accepter14174a"~
- "FZv"));
+ "FNaNbNiNfZv"));
}
+@safe @nogc pure nothrow
void accepter14174b(Config : Config14174!(T) = defConfig14174, T...)()
{
static assert(equalDemangle(accepter14174b.mangleof,
@@ -4538,17 +4545,18 @@ void accepter14174b(Config : Config14174!(T) = defConfig14174, T...)()
"accepter14174b"~
"HTS7breaker51__T11Config14174TS7breaker6N14174TS7breaker6N14174Z11Config14174TS7breaker6N14174TS7breaker6N14174Z14"~
"accepter14174b"~
- "FZv"));
+ "FNaNbNiNfZv"));
}
void test14174()
{
- accepter14174a!()(); // ok
- accepter14174b(); // error
+ accepter14174a!()();
+
+ accepter14174b!()();
}
/******************************************/
-// 14836
+// https://issues.dlang.org/show_bug.cgi?id=14836
template a14836x(alias B, C...)
{
@@ -4571,7 +4579,7 @@ void test14836()
}
/******************************************/
-// 14357
+// https://issues.dlang.org/show_bug.cgi?id=14357
template Qux14357(T : U*, U : V*, V)
{
@@ -4583,7 +4591,7 @@ template Qux14357(T : U*, U : V*, V)
static assert(!__traits(compiles, Qux14357!(float**, int*)));
/******************************************/
-// 14481
+// https://issues.dlang.org/show_bug.cgi?id=14481
template someT14481(alias e)
{
@@ -4602,7 +4610,7 @@ struct Hoge14481
}
/******************************************/
-// 14520
+// https://issues.dlang.org/show_bug.cgi?id=14520
template M14520(alias a) { enum M14520 = 1; }
template M14520(string s) { enum M14520 = 2; }
@@ -4616,7 +4624,7 @@ static assert(M14520!f14520b == 1);
static assert(M14520!f14520c == 1);
/******************************************/
-// 14568
+// https://issues.dlang.org/show_bug.cgi?id=14568
struct Interval14568()
{
@@ -4659,7 +4667,8 @@ template SubOps14568(Args...)
struct Nat14568 { mixin SubOps14568!(null); }
/******************************************/
-// 14603, 14604
+// https://issues.dlang.org/show_bug.cgi?id=14603
+// https://issues.dlang.org/show_bug.cgi?id=14604
struct S14603
{
@@ -4683,7 +4692,7 @@ alias c14604 = Id14604!(S14604.opDispatch!"go"); // ok
alias d14604 = Id14604!(S14604.go); // issue 14604, 'Error: template instance opDispatch!"go" cannot resolve forward reference'
/******************************************/
-// 14735
+// https://issues.dlang.org/show_bug.cgi?id=14735
enum CS14735 { yes, no }
@@ -4707,7 +4716,7 @@ void test14735()
}
/******************************************/
-// 14743
+// https://issues.dlang.org/show_bug.cgi?id=14743
class A14743
{
@@ -4716,7 +4725,7 @@ class A14743
}
/******************************************/
-// 14802
+// https://issues.dlang.org/show_bug.cgi?id=14802
void test14802()
{
@@ -4756,7 +4765,7 @@ void test14802()
}
/******************************************/
-// 14886
+// https://issues.dlang.org/show_bug.cgi?id=14886
void test14886()
{
@@ -4778,9 +4787,9 @@ void test14886()
}
/******************************************/
-// 15156
+// https://issues.dlang.org/show_bug.cgi?id=15156
-// 15156
+// https://issues.dlang.org/show_bug.cgi?id=15156
auto f15116a(T)(string s, string arg2) { return 1; }
auto f15116b(T)(int i, string arg2) { return 2; }
@@ -4798,7 +4807,7 @@ void test15116()
}
/******************************************/
-// 15152
+// https://issues.dlang.org/show_bug.cgi?id=15152
void test15152()
{
@@ -4814,7 +4823,7 @@ void test15152()
}
/******************************************/
-// 15352
+// https://issues.dlang.org/show_bug.cgi?id=15352
struct S15352(T, T delegate(uint idx) supplier)
{
@@ -4844,7 +4853,7 @@ void test15352()
}
/******************************************/
-// 15623
+// https://issues.dlang.org/show_bug.cgi?id=15623
struct WithFoo15623a { void foo() {} }
struct WithFoo15623b { void foo() {} }
@@ -4875,7 +4884,7 @@ static assert( __traits(compiles, { alias Baz = CallsFoo15623!WithFoo15623d; ret
static assert(!__traits(compiles, { alias Baz = CallsFoo15623!WithoutFoo15623d; return Baz.init; }()));
/******************************************/
-// 15781
+// https://issues.dlang.org/show_bug.cgi?id=15781
void test15781()
{
@@ -4898,6 +4907,133 @@ void test15781()
}
/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=16042
+
+struct Foo16042 {}
+
+auto map16042(alias func, T)(T t)
+{
+ return func(t);
+}
+
+auto toChars16042(R)(R r) if (is(R == int[]))
+{
+ Foo16042 f;
+ assert(toChars16042(f) == 1); // OK
+ assert(map16042!(toChars16042)(f) == 1); // OK <- NG
+ assert(map16042!((toChars16042))(f) == 1); // OK
+}
+
+auto toChars16042(Foo16042 f)
+{
+ return 1;
+}
+
+void test16042()
+{
+ [1].toChars16042();
+}
+
+// ---
+
+auto fn16042(R)(R r) if (is(R == int[])) {}
+auto fn16042(Foo16042 f) { return 1; }
+
+struct Namespace16042
+{
+ alias fn = fn16042!(int[]);
+}
+
+void test16042b()
+{
+ Foo16042 f;
+
+ with (Namespace16042)
+ {
+ static assert(!__traits(compiles, fn(f))); // NG
+ static assert(!__traits(compiles, map16042!(fn)(f))); // should be NG -> actually NG
+ static assert(!__traits(compiles, map16042!((fn))(f))); // NG
+ }
+}
+
+/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15243
+
+struct S15243(Types...)
+{
+ void apply1(U)(U delegate(Types[0]) f0) {}
+
+ void apply2(U)(U delegate(Types) f0) {}
+
+ void apply3(U)(U delegate(Types[1..$]) f0) {}
+}
+
+void test15243()
+{
+ int f1(int) { return 0; }
+ int f2(int, long) { return 0; }
+ int f3(long, string) { return 0; }
+
+ S15243!(int) s1;
+ s1.apply1(&f1);
+ s1.apply2(&f1);
+
+ S15243!(int, long) s2;
+ s2.apply2(&f2);
+
+ S15243!(int, long, string) s3;
+ s3.apply3(&f3);
+}
+
+/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15653
+
+alias TypeTuple15653(T...) = T;
+
+void test15653()
+{
+ void foo(U, T)(const T x) { static assert(is(T == U)); }
+ void bar(U, T)(immutable T x) { static assert(is(T == U)); }
+
+ struct X { int a; long[2] b; }
+ struct Y { int* a; long[] b; }
+
+ foreach (U; TypeTuple15653!( byte, short, int, long,
+ ubyte, ushort, uint, ulong,
+ float, double, real,
+ void delegate(),
+ int[2], X, X[2]))
+ {
+ foo!U(U.init); // OK
+ bar!U(U.init); // Was error, now OK
+
+ U u;
+ foo!U(u); // OK
+ bar!U(u); // Was error, now OK
+ }
+
+ foreach (U; TypeTuple15653!(void*, int**, long[], double*[2]))
+ {
+ foo!U(U.init); // OK
+ bar!U(U.init); // Was error, now OK
+
+ U u;
+ foo!U(u);
+ static assert(!__traits(compiles, bar!U(u)), U.stringof);
+ }
+
+ foreach (U; TypeTuple15653!(Object, Y, Y[2], int[int]))
+ {
+ foo!U(U.init); // OK
+ static assert(!__traits(compiles, bar!U(U.init)), U.stringof);
+
+ U u;
+ foo!U(u); // OK
+ static assert(!__traits(compiles, bar!U(u)), U.stringof);
+ }
+}
+
+/******************************************/
int main()
{
@@ -5011,6 +5147,10 @@ int main()
test14735();
test14802();
test15116();
+ test16042();
+ test16042b();
+ test15243();
+ test15653();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/test10.d b/gcc/testsuite/gdc.test/runnable/test10.d
index 671a61a661e..b9f8885696a 100644
--- a/gcc/testsuite/gdc.test/runnable/test10.d
+++ b/gcc/testsuite/gdc.test/runnable/test10.d
@@ -1,4 +1,10 @@
-// EXTRA_SOURCES: imports/test10a.d
+/*
+EXTRA_SOURCES: imports/test10a.d
+RUN_OUTPUT:
+---
+it is 32
+---
+*/
import imports.test10a;
diff --git a/gcc/testsuite/gdc.test/runnable/test10378.d b/gcc/testsuite/gdc.test/runnable/test10378.d
index f83ece18bbf..8ad457a5b5e 100644
--- a/gcc/testsuite/gdc.test/runnable/test10378.d
+++ b/gcc/testsuite/gdc.test/runnable/test10378.d
@@ -1,4 +1,4 @@
-
+// EXTRA_FILES: imports/bar10378.d
int writeln() { return 3; }
struct S {
diff --git a/gcc/testsuite/gdc.test/runnable/test10619.d b/gcc/testsuite/gdc.test/runnable/test10619.d
new file mode 100644
index 00000000000..4156289fdd7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test10619.d
@@ -0,0 +1,38 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=10619
+
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+1
+2
+3
+4
+---
+*/
+
+void main()
+{
+ {
+ int x = 1;
+ print!x();
+ }
+ {
+ int x = 2;
+ print!x();
+ }
+ {
+ static int y = 3;
+ print!y();
+ }
+ {
+ static int y = 4;
+ print!y();
+ }
+}
+
+void print(alias symbol)()
+{
+ import core.stdc.stdio : printf;
+ printf("%d\n", symbol);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test10736.d b/gcc/testsuite/gdc.test/runnable/test10736.d
index 55b6f539887..0b70606faff 100644
--- a/gcc/testsuite/gdc.test/runnable/test10736.d
+++ b/gcc/testsuite/gdc.test/runnable/test10736.d
@@ -1,5 +1,12 @@
-// EXTRA_SOURCES: imports/test10736a.d
-// EXTRA_SOURCES: imports/test10736b.d
+/*
+EXTRA_SOURCES: imports/test10736a.d
+EXTRA_SOURCES: imports/test10736b.d
+EXTRA_FILES: imports/test10736c.d
+RUN_OUTPUT:
+---
+success
+---
+*/
import imports.test10736a;
import imports.test10736b;
diff --git a/gcc/testsuite/gdc.test/runnable/test10942.d b/gcc/testsuite/gdc.test/runnable/test10942.d
index 0d48946383d..8c45c203dbb 100644
--- a/gcc/testsuite/gdc.test/runnable/test10942.d
+++ b/gcc/testsuite/gdc.test/runnable/test10942.d
@@ -1,16 +1,22 @@
-// RUNNABLE_PHOBOS_TEST
// REQUIRED_ARGS: -g
-import std.string;
-
string getEnum(size_t count)
{
string en;
en ~= "enum KeyCode\n { \n";
- foreach (i; 0 .. count)
+ foreach (i; 1 .. count + 1)
{
- en ~= format(" memb_%s = %s,\n", i+1, i+1);
+ char[4] buffer;
+ int start = buffer.length;
+
+ while (i > 0)
+ {
+ buffer[--start] = cast(char) ('0' + (i % 10));
+ i /= 10;
+ }
+ char[] id = buffer[start .. $];
+ en ~= "memb_" ~ id ~ " = " ~ id ~ ",\n";
}
en ~= "} ";
diff --git a/gcc/testsuite/gdc.test/runnable/test11.d b/gcc/testsuite/gdc.test/runnable/test11.d
index 0d916c1af6c..abaae37a15f 100644
--- a/gcc/testsuite/gdc.test/runnable/test11.d
+++ b/gcc/testsuite/gdc.test/runnable/test11.d
@@ -1,5 +1,5 @@
-// RUNNABLE_PHOBOS_TEST
// REQUIRED_ARGS:
+// EXTRA_SOURCES: imports/std11file.d
extern(C) int printf(const char*, ...);
extern(C) size_t strlen(const char*);
@@ -531,14 +531,14 @@ struct NODE27 {
shared(NODE27) *next;
}
-static shared NODE27 nodetbl[3] =
+static shared NODE27[3] nodetbl =
[
{ 0,cast(shared(NODE27)*)nodetbl + 1},
{ 0,cast(shared(NODE27)*)nodetbl + 2},
{ 0,null}
];
-static shared NODE27 nodetbl2[3] = [
+static shared NODE27[3] nodetbl2 = [
{ 0,&nodetbl2[1]},
{ 0,&nodetbl2[2]},
{ 0,null}
@@ -682,7 +682,7 @@ void test35()
try {
alias Foo35!( Bar35 ) filter;
} catch (Exception e) {
- printf( "Exception %.*s", e.msg.length, e.msg.ptr );
+ printf( "Exception %.*s", cast(int)e.msg.length, e.msg.ptr );
} finally {
printf( "Done0." );
}
@@ -886,7 +886,7 @@ void test45()
char[5] foo;
foo[] = "hello";
- printf("'%.*s'\n", foo.length, foo.ptr);
+ printf("'%.*s'\n", cast(int)foo.length, foo.ptr);
func45(cast(string)foo);
}
@@ -1182,15 +1182,15 @@ void test62()
class A63
{
- private import std.file;
- alias std.file.getcwd getcwd;
+ private import imports.std11file;
+ alias imports.std11file.getcwd getcwd;
}
void test63()
{
A63 f = new A63();
auto s = f.getcwd();
- printf("%.*s\n", s.length, s.ptr);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
}
@@ -1229,7 +1229,7 @@ void test65()
}
/**************************************/
-// 8809
+// https://issues.dlang.org/show_bug.cgi?id=8809
void test8809()
{
@@ -1298,7 +1298,7 @@ void test8809()
}
/**************************************/
-// 9734
+// https://issues.dlang.org/show_bug.cgi?id=9734
void test9734()
{
diff --git a/gcc/testsuite/gdc.test/runnable/test11447c.d b/gcc/testsuite/gdc.test/runnable/test11447c.d
index d09e3d2098f..ecec3e5c2c9 100644
--- a/gcc/testsuite/gdc.test/runnable/test11447c.d
+++ b/gcc/testsuite/gdc.test/runnable/test11447c.d
@@ -1,6 +1,7 @@
// COMPILE_SEPARATELY
// EXTRA_SOURCES: imports/c11447.d
-// PERMUTE_ARGS: -allinst -w -debug -g
+// REQUIRED_ARGS: -w
+// PERMUTE_ARGS: -allinst -debug -g
import imports.c11447;
diff --git a/gcc/testsuite/gdc.test/runnable/test11863.d b/gcc/testsuite/gdc.test/runnable/test11863.d
index c1285b3bef7..f6446aae725 100644
--- a/gcc/testsuite/gdc.test/runnable/test11863.d
+++ b/gcc/testsuite/gdc.test/runnable/test11863.d
@@ -1,6 +1,6 @@
// COMPILE_SEPARATELY
// EXTRA_SOURCES: imports/std11863conv.d
-// EXTRA_FILES: imports/std11863format.d
+// EXTRA_FILES: imports/std11863bitmanip.d imports/std11863format.d
import imports.std11863conv;
diff --git a/gcc/testsuite/gdc.test/runnable/test11934.d b/gcc/testsuite/gdc.test/runnable/test11934.d
new file mode 100644
index 00000000000..4ab65d23eb2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test11934.d
@@ -0,0 +1,22 @@
+void main()
+{
+ struct Struct11934
+ {
+ this(int i) { instances++; }
+ this(this) { instances++; }
+ ~this() { instances--; }
+ static size_t instances = 0;
+ }
+
+ struct Range11934
+ {
+ void popFront() { cnt++; }
+ @property front() { return Struct11934(0); }
+ @property empty() { return cnt >= 10; }
+ size_t cnt;
+ }
+
+ foreach(ref i; Range11934()) { }
+
+ assert(Struct11934.instances == 0);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test12.d b/gcc/testsuite/gdc.test/runnable/test12.d
index eb7e422c28b..dac4ed647b0 100644
--- a/gcc/testsuite/gdc.test/runnable/test12.d
+++ b/gcc/testsuite/gdc.test/runnable/test12.d
@@ -1,4 +1,3 @@
-// RUNNABLE_PHOBOS_TEST
// PERMUTE_ARGS: -unittest -O -release -inline -fPIC -g
extern(C) int printf(const char*, ...);
@@ -156,7 +155,7 @@ int[] fun(int i)
{
assert(result[0] == 2);
}
- body
+ do
{
char result;
int[] res = new int[10];
@@ -312,15 +311,15 @@ void test12()
class A13
{
- int opShl(char* v) { return 1; }
- int opShl(string v) { return 2; }
+ int opBinary(string op : "<<")(char* v) { return 1; }
+ int opBinary(string op : "<<")(string v) { return 2; }
}
void test13()
{
A13 a = new A13();
int i;
- i = a.opShl(cast(string)"");
+ i = a << cast(string) "";
assert(i == 2);
i = a << cast(string)"";
assert(i == 2);
@@ -366,32 +365,32 @@ union U6
void test14()
{
- printf("%d %d %d\n", U1.a.offsetof, U1.b.offsetof, U1.sizeof);
+ printf("%zd %zd %zd\n", U1.a.offsetof, U1.b.offsetof, U1.sizeof);
assert(U1.a.offsetof == 0);
assert(U1.b.offsetof == 0);
assert(U1.sizeof == 4);
- printf("%d %d %d\n", U2.a.offsetof, U2.b.offsetof, U2.sizeof);
+ printf("%zd %zd %zd\n", U2.a.offsetof, U2.b.offsetof, U2.sizeof);
assert(U2.a.offsetof == 0);
assert(U2.b.offsetof == 0);
assert(U2.sizeof == 8);
- printf("%d %d %d\n", U3.a.offsetof, U3.b.offsetof, U3.sizeof);
+ printf("%zd %zd %zd\n", U3.a.offsetof, U3.b.offsetof, U3.sizeof);
assert(U3.a.offsetof == 0);
assert(U3.b.offsetof == 0);
assert(U3.sizeof == 8);
- printf("%d %d %d\n", U4.a.offsetof, U4.b.offsetof, U4.sizeof);
+ printf("%zd %zd %zd\n", U4.a.offsetof, U4.b.offsetof, U4.sizeof);
assert(U4.a.offsetof == 0);
assert(U4.b.offsetof == 0);
assert(U4.sizeof == 4);
- printf("%d %d %d\n", U5.a.offsetof, U5.b.offsetof, U5.sizeof);
+ printf("%zd %zd %zd\n", U5.a.offsetof, U5.b.offsetof, U5.sizeof);
assert(U5.a.offsetof == 0);
assert(U5.b.offsetof == 0);
assert(U5.sizeof == 8);
- printf("%d %d %d\n", U6.a.offsetof, U6.b.offsetof, U6.sizeof);
+ printf("%zd %zd %zd\n", U6.a.offsetof, U6.b.offsetof, U6.sizeof);
assert(U6.a.offsetof == 0);
assert(U6.b.offsetof == 0);
assert(U6.sizeof == 8);
@@ -434,7 +433,7 @@ class Cout17
printf("%d",x);
return this;
}
- alias set opShl;
+ alias opBinary(string op : "<<") = set;
}
void test17()
@@ -671,9 +670,9 @@ void test31()
printf("%s\n", foo.ptr);
auto s = typeid(typeof(foo.ptr)).toString();
- printf("%.*s\n", s.length, s.ptr);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
s = typeid(char*).toString();
- printf("%.*s\n", s.length, s.ptr);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
assert(typeid(typeof(foo.ptr)) == typeid(immutable(char)*));
}
@@ -689,7 +688,7 @@ class Qwert32
void foo()
{
- printf("yuiop = %d, asdfg = %d\n", Qwert32.yuiop.offsetof, Qwert32.asdfg.offsetof);
+ printf("yuiop = %zd, asdfg = %zd\n", Qwert32.yuiop.offsetof, Qwert32.asdfg.offsetof);
version(D_LP64)
{
assert(Qwert32.yuiop.offsetof == 16);
@@ -778,7 +777,7 @@ void test36()
{
A36 a = new A36;
- printf("A36.sizeof = %d\n", a.classinfo.initializer.length);
+ printf("A36.sizeof = %zd\n", a.classinfo.initializer.length);
printf("%d\n", a.s);
printf("%d\n", a.a);
printf("%d\n", a.b);
@@ -819,7 +818,7 @@ class Foo38
{
static void display_name()
{
- printf("%.*s\n", Object.classinfo.name.length, Object.classinfo.name.ptr);
+ printf("%.*s\n", cast(int)Object.classinfo.name.length, Object.classinfo.name.ptr);
assert(Object.classinfo.name == "object.Object");
assert(super.classinfo.name == "object.Object");
assert(this.classinfo.name == "test12.Foo38");
@@ -951,31 +950,28 @@ struct Shell
const int opCmp(ref const Shell s)
{
- import std.algorithm;
- return std.algorithm.cmp(this.str, s.str);
+ // Obviously not Unicode-aware...
+ foreach (const i, const a; this.str)
+ {
+ const b = s.str[i];
+ if (a < b) return -1;
+ if (a > b) return 1;
+ }
+
+ if (this.str.length < s.str.length) return -1;
+ if (this.str.length > s.str.length) return 1;
+ return 0;
}
}
void test45()
{
- import std.algorithm;
-
- Shell[3] a;
-
- a[0].str = "hello";
- a[1].str = "betty";
- a[2].str = "fred";
-
- a[].sort;
-
- foreach (Shell s; a)
- {
- printf("%.*s\n", s.str.length, s.str.ptr);
- }
+ Shell a = Shell("hello");
+ Shell b = Shell("betty");
+ Shell c = Shell("fred");
- assert(a[0].str == "betty");
- assert(a[1].str == "fred");
- assert(a[2].str == "hello");
+ assert(a > b);
+ assert(b < c);
}
/**************************************/
@@ -1153,7 +1149,7 @@ void test55()
void writefln(string s)
{
- printf("%.*s\n", s.length, s.ptr);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
}
void test58()
diff --git a/gcc/testsuite/gdc.test/runnable/test12486.d b/gcc/testsuite/gdc.test/runnable/test12486.d
new file mode 100644
index 00000000000..a0a6e2c4152
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test12486.d
@@ -0,0 +1,18 @@
+module test12486;
+
+struct S { enum a = 1; } // or `const` but not for all types
+
+S f(ref int i)
+{
+ ++i;
+ return S();
+}
+
+void main()
+{
+ int i = 2;
+ assert(f(i).a == 1);
+ // ensure that f(i) was actually called, even though
+ // a is a statically known property of the returned type
+ assert(i == 3);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test14874.d b/gcc/testsuite/gdc.test/runnable/test14874.d
index 470a93c60ff..fa69211e18f 100644
--- a/gcc/testsuite/gdc.test/runnable/test14874.d
+++ b/gcc/testsuite/gdc.test/runnable/test14874.d
@@ -1,38 +1,38 @@
-// REQUIRED_ARGS: -dip25
-
-template indexOfReturn(T...)
-{
- static if (T.length == 0)
- {
- enum indexOfReturn = -1;
- }
- else static if (T[$ - 1] == "return")
- {
- enum indexOfReturn = T.length - 1;
- }
- else
- {
- enum indexOfReturn = indexOfReturn!(T[0..$-1]);
- }
-}
-
-struct Test
-{
- int n;
-
- ref int getN() return
- {
- return n;
- }
-
- int getNNonReturn()
- {
- return n;
- }
-}
-
-void main()
-{
- assert(indexOfReturn!(__traits(getFunctionAttributes, Test.getN)) != -1);
- assert(indexOfReturn!(__traits(getFunctionAttributes, Test.getNNonReturn)) == -1);
-}
+// REQUIRED_ARGS:
+
+template indexOfReturn(T...)
+{
+ static if (T.length == 0)
+ {
+ enum indexOfReturn = -1;
+ }
+ else static if (T[$ - 1] == "return")
+ {
+ enum indexOfReturn = T.length - 1;
+ }
+ else
+ {
+ enum indexOfReturn = indexOfReturn!(T[0..$-1]);
+ }
+}
+
+struct Test
+{
+ int n;
+
+ ref int getN() return
+ {
+ return n;
+ }
+
+ int getNNonReturn()
+ {
+ return n;
+ }
+}
+
+void main()
+{
+ assert(indexOfReturn!(__traits(getFunctionAttributes, Test.getN)) != -1);
+ assert(indexOfReturn!(__traits(getFunctionAttributes, Test.getNNonReturn)) == -1);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test15.d b/gcc/testsuite/gdc.test/runnable/test15.d
index 405171e44ae..222742c4080 100644
--- a/gcc/testsuite/gdc.test/runnable/test15.d
+++ b/gcc/testsuite/gdc.test/runnable/test15.d
@@ -1,13 +1,10 @@
-// RUNNABLE_PHOBOS_TEST
-// REQUIRED_ARGS:
-// EXTRA_FILES: extra-files/test15.txt
+/*
+REQUIRED_ARGS: -Jrunnable/extra-files
+EXTRA_FILES: extra-files/test15.txt
+*/
-import std.array;
-import core.stdc.math : cos, fabs, sin, sqrt;
+import core.math;
import core.vararg;
-import std.math: rndtol, rint;
-import std.string;
-import std.stdio : File;
extern (C)
{
@@ -46,9 +43,9 @@ void test6()
void test7()
{
string s = `hello"there'you`;
- printf("s = '%.*s'\n", s.length, s.ptr);
+ printf("s = '%.*s'\n", cast(int)s.length, s.ptr);
assert(s == "hello\"there'you");
- ubyte[] b = cast(ubyte[])x"8B 7D f4 0d";
+ ubyte[] b = cast(ubyte[])"\x8B\x7D\xf4\x0d";
for (int i = 0; i < b.length; i++)
printf("b[%d] = x%02x\n", i, b[i]);
assert(b.length == 4);
@@ -95,7 +92,7 @@ struct Pair
return this;
}
- Pair opDiv(Pair other)
+ Pair opBinary(string op)(Pair other) if (op == "/")
{
Pair result;
@@ -206,8 +203,8 @@ class A15
List2.rehash;
}
private:
- int delegate(in int arg1) List1[char[]];
- int List2[char []];
+ int delegate(in int arg1)[char[]] List1;
+ int[char []] List2;
}
void test15()
@@ -223,7 +220,7 @@ void test16()
uint c = 200000;
while (c--)
a ~= 'x';
- //printf("a = '%.*s'\n", a.length, a.ptr);
+ //printf("a = '%.*s'\n", cast(int)a.length, a.ptr);
}
@@ -266,7 +263,7 @@ void test19()
int foo20(string s,char d) { return 1; }
int foo20(string s,double d) { return 2; }
-int foo20(string s,cdouble d) { return 3; }
+int foo20(string s,long d) { return 3; }
void test20()
{
@@ -280,10 +277,47 @@ void test20()
void test21()
{
+ // Minimalistic byLine implementation
+ static struct Lines
+ {
+ private string text, line;
+ this(string text)
+ {
+ this.text = text;
+ popFront();
+ }
+
+ bool empty() const { return text == ""; }
+
+ string front() const
+ {
+ assert(!empty);
+ return line;
+ }
+
+ void popFront()
+ {
+ assert(!empty);
+ foreach (const idx; 0 .. text.length)
+ {
+ if (text[idx] == '\n')
+ {
+ line = text[0..idx];
+ text = text[idx + 1..$];
+ return;
+ }
+ }
+
+ line = text;
+ text = null;
+ }
+ }
+
+ static immutable string file = import(`test15.txt`);
+
int[string] esdom;
- auto f = File("runnable/extra-files/test15.txt", "r");
- foreach(it; f.byLine())
+ foreach(it; Lines(file))
esdom[it.idup] = 0;
esdom.rehash;
@@ -362,16 +396,16 @@ void test25()
void test26()
{
- string[] instructions = std.array.split("a;b;c", ";");
+ string[] instructions =[ "a", "b", "c" ];
foreach(ref string instr; instructions)
{
- std.string.strip(instr);
+ instr = instr[];
}
foreach(string instr; instructions)
{
- printf("%.*s\n", instr.length, instr.ptr);
+ printf("%.*s\n", cast(int)instr.length, instr.ptr);
}
}
@@ -402,7 +436,7 @@ void test27()
void foo28(ClassInfo ci)
{
- printf("%.*s\n", ci.name.length, ci.name.ptr);
+ printf("%.*s\n", cast(int)ci.name.length, ci.name.ptr);
static int i;
switch (i++)
@@ -786,7 +820,7 @@ class C44
void test44()
{
C44 c= new C44();
- printf("%.*s\n", c.arrArr[0].length, c.arrArr[0].ptr);
+ printf("%.*s\n", cast(int)c.arrArr[0].length, c.arrArr[0].ptr);
assert(c.arrArr[0] == "foo");
}
@@ -815,11 +849,11 @@ union A46
void test46()
{
A46 a;
- printf("%d\n", cast(byte*)&a.c - cast(byte*)&a);
- printf("%d\n", cast(byte*)&a.s - cast(byte*)&a);
- printf("%d\n", cast(byte*)&a.l - cast(byte*)&a);
- printf("%d\n", cast(byte*)&a.a - cast(byte*)&a);
- printf("%d\n", cast(byte*)&a.f - cast(byte*)&a);
+ printf("%td\n", cast(byte*)&a.c - cast(byte*)&a);
+ printf("%td\n", cast(byte*)&a.s - cast(byte*)&a);
+ printf("%td\n", cast(byte*)&a.l - cast(byte*)&a);
+ printf("%td\n", cast(byte*)&a.a - cast(byte*)&a);
+ printf("%td\n", cast(byte*)&a.f - cast(byte*)&a);
assert(cast(byte*)&a.c == cast(byte*)&a);
assert(cast(byte*)&a.s == cast(byte*)&a);
@@ -1044,9 +1078,9 @@ void test56()
/************************************/
-void det(float mat[][])
+void det(float[][] mat)
{
- float newmat[][];
+ float[][] newmat;
size_t i = newmat[0 .. (mat.length - 1)].length;
}
@@ -1099,7 +1133,6 @@ void test59()
class Foo60
{
int x;
-static:
this() { x = 3; }
~this() { }
}
@@ -1117,16 +1150,13 @@ void test60()
class StdString
{
- alias std.string.format toString;
+ alias nearest = core.math.rint;
}
void test61()
{
- int i = 123;
StdString g = new StdString();
- string s = g.toString("%s", i);
- printf("%.*s\n", s.length, s.ptr);
- assert(s == "123");
+ assert(g.nearest(123.1) == 123);
}
@@ -1366,6 +1396,38 @@ void test72()
assert(foos.length == 1);
}
+/************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19758
+
+void test19758()
+{
+ byte[1] a = [1];
+ int b = 0;
+
+ // If delete this 4 lines, the result is correct.
+ if (a[b] == 0)
+ {
+ a[b] = 0;
+ if (1 << b) { }
+ }
+
+ if ((a[b] & 0xFF) == 0)
+ {
+ assert((a[b] & 0xFF) == 0);
+ }
+}
+
+/************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19968
+
+@safe void test19968()
+{
+ int[2] array = [16, 678];
+ union U { int i; bool b; }
+ U u;
+ u.i = 0xDEADBEEF;
+ assert(array[u.b] == 678);
+}
/************************************/
@@ -1436,6 +1498,8 @@ int main()
test70();
test71();
test72();
+ test19758();
+ test19968();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/test15079.d b/gcc/testsuite/gdc.test/runnable/test15079.d
index 5316d73e2ca..e0fea972ea4 100644
--- a/gcc/testsuite/gdc.test/runnable/test15079.d
+++ b/gcc/testsuite/gdc.test/runnable/test15079.d
@@ -1,3 +1,4 @@
+// EXTRA_FILES: imports/a15079.d
module test15079;
import imports.a15079;
diff --git a/gcc/testsuite/gdc.test/runnable/test15373.d b/gcc/testsuite/gdc.test/runnable/test15373.d
new file mode 100644
index 00000000000..13144b75e45
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test15373.d
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=15373
+
+// Using `typeid` on an `extern(C++) class` type is fine as it is evaluated at compile-time
+
+// Using `typeid` on an `extern(C++) class` instance is not ok because `extern(C++) class`
+// instances are not rooted in `Object`. See test/fail_compilation/test15373.d
+
+extern(C++) class C
+{ }
+
+void main()
+{
+ auto Cti = typeid(C);
+ assert(Cti.name == "test15373.C");
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test15568.d b/gcc/testsuite/gdc.test/runnable/test15568.d
new file mode 100644
index 00000000000..d21ede26d09
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test15568.d
@@ -0,0 +1,58 @@
+// REQUIRED_ARGS: -unittest -main -O
+// https://issues.dlang.org/show_bug.cgi?id=15568
+
+auto filter(alias pred)(D[])
+{
+ struct FilterResult
+ {
+ void popFront()
+ {
+ pred(null);
+ }
+
+ D[] array()
+ {
+ return null;
+ }
+ }
+ return FilterResult();
+}
+
+class A
+{
+ B foo(C c, D[] ds, bool f)
+ in
+ {
+ assert(c !is null);
+ }
+ do
+ {
+ D[] ds2 = ds.filter!(a => c).array;
+
+ return new B(ds2, f);
+ }
+}
+
+class B
+{
+ this(D[], bool)
+ {
+ }
+}
+
+class C
+{
+}
+
+struct D
+{
+}
+
+unittest
+{
+ auto a = new A;
+ C c = new C;
+
+ a.foo(c, null, false);
+}
+
diff --git a/gcc/testsuite/gdc.test/runnable/test15624.d b/gcc/testsuite/gdc.test/runnable/test15624.d
new file mode 100644
index 00000000000..792757932e4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test15624.d
@@ -0,0 +1,51 @@
+/* PERMUTE_ARGS:
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=15624
+
+struct Foo {
+ int x;
+ int opApply(int delegate(int, string, string) @safe dg) @safe {
+ x = 1;
+ return 0;
+ }
+ int opApply(int delegate(int, string, string) @system dg) @system {
+ x = 2;
+ return 0;
+ }
+}
+
+void testSafe() @safe {
+ Foo foo;
+ foreach (i, k, v; foo) {
+ }
+ assert(foo.x == 1);
+}
+
+void testSystem() @system {
+ Foo foo;
+ foreach (i, k, v; foo) {
+ }
+ assert(foo.x == 2);
+}
+
+void test() @system
+{
+ Foo f;
+
+ int dgsafe (int x, string s, string t) @safe { return 1; }
+ int dgsystem(int x, string s, string t) @system { return 1; }
+
+ f.opApply(&dgsafe);
+ assert(f.x == 1);
+ f.opApply(&dgsystem);
+ assert(f.x == 2);
+}
+
+int main()
+{
+ testSafe();
+ testSystem();
+ test();
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test16047.d b/gcc/testsuite/gdc.test/runnable/test16047.d
new file mode 100644
index 00000000000..eff2db3c7c4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test16047.d
@@ -0,0 +1,17 @@
+// https://issues.dlang.org/show_bug.cgi?id=16047
+module test16047;
+
+void main()
+{
+ Reassignable[int][int] aa;
+
+ aa[0][0] = Reassignable.init;
+ aa[s()][0] = Reassignable.init; // range violation
+}
+
+struct Reassignable
+{
+ void opAssign(Reassignable) {}
+}
+
+int s() { return 1; }
diff --git a/gcc/testsuite/gdc.test/runnable/test16115.d b/gcc/testsuite/gdc.test/runnable/test16115.d
index c8ad4649f2c..8cd9c6ad482 100644
--- a/gcc/testsuite/gdc.test/runnable/test16115.d
+++ b/gcc/testsuite/gdc.test/runnable/test16115.d
@@ -25,7 +25,7 @@ auto call()
else // assert error
{
//return n = tagx, null;
- return n = Test.tag, null;
+ return n = Test.tag;
//return n = Test.tag;
}
}
diff --git a/gcc/testsuite/gdc.test/runnable/test16140.d b/gcc/testsuite/gdc.test/runnable/test16140.d
new file mode 100644
index 00000000000..3004945c070
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test16140.d
@@ -0,0 +1,32 @@
+// https://issues.dlang.org/show_bug.cgi?id=16140
+
+int fun()
+{
+ static int count = 0;
+ if (count == 3)
+ {
+ count = 0;
+ return 0;
+ }
+ ++count;
+ return count;
+}
+
+void main()
+{
+ uint[] res;
+ while(auto value = fun())
+ res ~= value;
+ assert(res == [1, 2, 3]);
+
+ res.length = 0;
+ while(uint value = fun())
+ res ~= value;
+ assert(res == [1, 2, 3]);
+
+ res.length = 0;
+ while(const value = fun())
+ res ~= value;
+ assert(res == [1, 2, 3]);
+}
+
diff --git a/gcc/testsuite/gdc.test/runnable/test16555.d b/gcc/testsuite/gdc.test/runnable/test16555.d
new file mode 100644
index 00000000000..78db1f74e6b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test16555.d
@@ -0,0 +1,20 @@
+// https://issues.dlang.org/show_bug.cgi?id=16555
+
+void outer(
+ double x,
+ double a, double b, double c, double d,
+ double e, double f, double g, double h)
+{
+ assert(x == 999.0 && a == 1 && b == 2 && c == 3 && d == 4
+ && e == 5 && f == 6 && g == 7 && h == 8);
+}
+
+void main()
+{
+ void inner(double x)
+ {
+ outer(x, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);
+ }
+
+ inner(999.0);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test17.d b/gcc/testsuite/gdc.test/runnable/test17.d
index 58873a095e6..4b17ede464b 100644
--- a/gcc/testsuite/gdc.test/runnable/test17.d
+++ b/gcc/testsuite/gdc.test/runnable/test17.d
@@ -5,15 +5,15 @@ extern(C) int printf(const char*, ...);
void ulog(string s)
{
- printf("%.*s\n",s.length, s.ptr);
+ printf("%.*s\n",cast(int)s.length, s.ptr);
fflush(stdout);
}
int open()
{
char *s;
- char abs[2000];
- char qu[100];
+ char[2000] abs;
+ char[100] qu;
int a;
ulog("reaches this only 9 times of 10!\n");
return 0;
@@ -22,7 +22,7 @@ int open()
int yhenda()
{
- char MEM[2200];
+ char[2200] MEM;
int a;
ulog("point(2.1) \n");
open();
diff --git a/gcc/testsuite/gdc.test/runnable/test17072.d b/gcc/testsuite/gdc.test/runnable/test17072.d
index 0ad04109f0a..2c5977fe998 100644
--- a/gcc/testsuite/gdc.test/runnable/test17072.d
+++ b/gcc/testsuite/gdc.test/runnable/test17072.d
@@ -1,6 +1,6 @@
/*
REQUIRED_ARGS: -inline
-PERMUTE_ARGS: -release -O -dip25
+PERMUTE_ARGS: -release -O
*/
// https://issues.dlang.org/show_bug.cgi?id=17072
diff --git a/gcc/testsuite/gdc.test/runnable/test17073.d b/gcc/testsuite/gdc.test/runnable/test17073.d
deleted file mode 100644
index 82df219254d..00000000000
--- a/gcc/testsuite/gdc.test/runnable/test17073.d
+++ /dev/null
@@ -1,13 +0,0 @@
-struct S0
-{
- int x = void;
-}
-struct S1
-{
- S0 x = S0(42);
-}
-void main()
-{
- S1 x;
- assert(x.x.x == 42);
-}
diff --git a/gcc/testsuite/gdc.test/runnable/test17181.d b/gcc/testsuite/gdc.test/runnable/test17181.d
new file mode 100644
index 00000000000..59ee8957dc0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17181.d
@@ -0,0 +1,12 @@
+// EXTRA_SOURCES: imports/test17181a.d imports/test17181b.d
+
+module test17181;
+import imports.test17181a;
+
+int foo()
+{
+ return imports.test17181a.abc(1);
+}
+
+static this() { assert(a == 2); }
+void main() {}
diff --git a/gcc/testsuite/gdc.test/runnable/test17181b.d b/gcc/testsuite/gdc.test/runnable/test17181b.d
new file mode 100644
index 00000000000..5e4a4be98ae
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17181b.d
@@ -0,0 +1,16 @@
+// EXTRA_SOURCES: imports/test17181a.d
+// EXTRA_FILES: imports/test17181c.d
+module test17181b;
+
+import imports.test17181c; // only imported, not compiled
+ // => must not be in ModuleInfo.importedModules
+
+static this()
+{
+ // By instantiating the getA template, its local imports.test17181a
+ // import is added to this module (not to imports.test17181c), and its
+ // module ctor must have run already.
+ assert(imports.test17181c.getA!() == 1);
+}
+
+void main() {}
diff --git a/gcc/testsuite/gdc.test/runnable/test17246.d b/gcc/testsuite/gdc.test/runnable/test17246.d
new file mode 100644
index 00000000000..2d517205896
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17246.d
@@ -0,0 +1,50 @@
+/* REQUIRED_ARGS:
+ * OPTIONAL_ARGS:
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=17246
+
+struct Foo
+{
+ int* rc;
+ this(int val)
+ {
+ rc = new int;
+ (*rc) = 1;
+ }
+ this(this)
+ {
+ (*rc)++;
+ }
+ ~this()
+ {
+ if (rc)
+ {
+ assert(*rc > 0);
+ (*rc)--;
+ }
+ }
+}
+
+struct Bar
+{
+ Foo foo;
+ this(Foo foo, bool)
+ {
+ this.foo = foo;
+ }
+}
+
+bool fun(bool val) { return !val; }
+
+auto genBar(bool flag)
+{
+ return flag ? Bar() : Bar(Foo(10), fun(!flag));
+}
+
+int main(string[] args)
+{
+ auto bar = genBar(args.length == 0);
+ return 0;
+}
+
diff --git a/gcc/testsuite/gdc.test/runnable/test17258.d b/gcc/testsuite/gdc.test/runnable/test17258.d
new file mode 100644
index 00000000000..1e86c95c4f0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17258.d
@@ -0,0 +1,33 @@
+class ByNameC(alias Var)
+{
+ alias var = Var;
+}
+
+struct ByNameS(alias Var)
+{
+ alias var = Var;
+ ubyte value = 1;
+}
+
+void main()
+{
+ ulong x;
+ ByNameS!x v;
+ ubyte w = 2;
+
+ v.var = 0xAA_BB; /* stomps over v.value and w */
+
+ assert(w == 2);
+ assert(v.value == 1);
+ //printf("%x\n", w); /* prints "aa", should be 2 */
+ //printf("%x\n", v.value); /* prints "bb", should be 1 */
+
+ auto c = new ByNameC!x;
+ c.var = 0xAA_BB; /* stomps over c.__vptr */
+
+ assert(*cast(ulong*)c != 0xAA_BB);
+ //printf("%x\n", *cast(ulong*)c); /* prints "aabb", should be pointer value */
+
+ assert(x == 0xAA_BB);
+ //printf("%lx\n", x); /* prints 0, should be "aabb" */
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test17337.d b/gcc/testsuite/gdc.test/runnable/test17337.d
new file mode 100644
index 00000000000..f817eb7d2e8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17337.d
@@ -0,0 +1,23 @@
+// REQUIRED_ARGS: -mcpu=native
+
+static if (__traits(compiles, __vector(ubyte[16])))
+{
+ alias ubyte16 = __vector(ubyte[16]);
+
+ ubyte16 bug(ubyte val)
+ {
+ immutable ubyte16 a = 0, b = val;
+ return b;
+ }
+
+ void main()
+ {
+ bug(0);
+ }
+}
+else
+{
+ void main()
+ {
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test17338.d b/gcc/testsuite/gdc.test/runnable/test17338.d
index 5c5012fecdd..1c937be77e6 100644
--- a/gcc/testsuite/gdc.test/runnable/test17338.d
+++ b/gcc/testsuite/gdc.test/runnable/test17338.d
@@ -1,25 +1,23 @@
// PERMUTE_ARGS:
+
+// COMDAT folding increases runtime by > 80x
+// REQUIRED_ARGS(windows): -L/OPT:NOICF
+
+// Apparently omf or optlink does not support more than 32767 symbols.
+// DISABLED: win32
+
// Generate \sum_{i=0}^{14} 2^i = 32767 template instantiations
// (each with 3 sections) to use more than 64Ki sections in total.
-version (Win32)
+
+size_t foo(size_t i, size_t mask)()
{
- // Apparently omf or optlink does not support more than 32767 symbols.
- void main()
- {
- }
+ static if (i == 14)
+ return mask;
+ else
+ return foo!(i + 1, mask) + foo!(i + 1, mask | (1UL << i));
}
-else
-{
- size_t foo(size_t i, size_t mask)()
- {
- static if (i == 14)
- return mask;
- else
- return foo!(i + 1, mask) + foo!(i + 1, mask | (1UL << i));
- }
- void main()
- {
- assert(foo!(0, 0) != 0);
- }
+void main()
+{
+ assert(foo!(0, 0) != 0);
}
diff --git a/gcc/testsuite/gdc.test/runnable/test17373.d b/gcc/testsuite/gdc.test/runnable/test17373.d
deleted file mode 100644
index 93753baf27b..00000000000
--- a/gcc/testsuite/gdc.test/runnable/test17373.d
+++ /dev/null
@@ -1,20 +0,0 @@
-interface Foo { void visit (int); }
-interface Bar { void visit(double); }
-interface FooBar : Foo, Bar {}
-static assert(__traits(getOverloads, FooBar, "visit").length == 2);
-
-interface Fbar { void visit(char); void visit(double); }
-interface Triple : Foo, Bar, Fbar {}
-static assert(__traits(getOverloads, Triple, "visit").length == 3);
-
-interface InheritanceMadness : FooBar, Triple {}
-static assert(__traits(getOverloads, Triple, "visit").length == 3);
-
-interface Simple
-{
- int square(int);
- real square(real);
-}
-static assert(__traits(getOverloads, Simple, "square").length == 2);
-
-void main() {}
diff --git a/gcc/testsuite/gdc.test/runnable/test17559.d b/gcc/testsuite/gdc.test/runnable/test17559.d
new file mode 100644
index 00000000000..a759f1564c6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17559.d
@@ -0,0 +1,84 @@
+// REQUIRED_ARGS: -g
+// REQUIRED_ARGS(linux freebsd dragonflybsd): -L-export-dynamic
+// PERMUTE_ARGS:
+// DISABLED: osx
+
+import core.stdc.stdio;
+
+void main()
+{
+ fun(1);
+ fun(2);
+ fun(3);
+#line 30
+ fun(4);
+
+ foo(1, 10);
+ foo(2, 10);
+ foo(3, 10);
+#line 40
+ foo(4, 10);
+}
+
+void fun(int n, int defParam = 10)
+{
+ try
+ {
+ if (n == 4)
+ throw new Exception("fun");
+ }
+ catch(Exception e)
+ {
+ string s = e.toString();
+ printf("%.*s\n", cast(int)s.length, s.ptr);
+ int line = lineInMain(e.toString());
+ assert(line >= 30 && line <= 32); // return address might be next statement
+ }
+}
+
+void foo(int n, int m)
+{
+ try
+ {
+ if (n == 4)
+ throw new Exception("foo");
+ }
+ catch(Exception e)
+ {
+ string s = e.toString();
+ printf("%.*s\n", cast(int)s.length, s.ptr);
+ int line = lineInMain(e.toString());
+ assert(line >= 40 && line <= 41); // return address might be next statement
+ }
+}
+
+int lineInMain(string msg)
+{
+ // find line number of _Dmain in stack trace
+ // on linux: file.d:line _Dmain [addr]
+ // on windows: addr in _Dmain at file.d(line)
+ int line = 0;
+ bool mainFound = false;
+ for (size_t pos = 0; pos + 6 < msg.length; pos++)
+ {
+ if (msg[pos] == '\n')
+ {
+ line = 0;
+ mainFound = false;
+ }
+ else if ((msg[pos] == ':' || msg[pos] == '(') && line == 0)
+ {
+ for (pos++; pos < msg.length && msg[pos] >= '0' && msg[pos] <= '9'; pos++)
+ line = line * 10 + msg[pos] - '0';
+ if (line > 0 && mainFound)
+ return line;
+ }
+ else if (msg[pos .. pos + 6] == "_Dmain" || msg[pos .. pos + 6] == "D main")
+ {
+ mainFound = true;
+ if (line > 0 && mainFound)
+ return line;
+ }
+ }
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test17684.d b/gcc/testsuite/gdc.test/runnable/test17684.d
index 4734d962810..d42baac0e16 100644
--- a/gcc/testsuite/gdc.test/runnable/test17684.d
+++ b/gcc/testsuite/gdc.test/runnable/test17684.d
@@ -74,6 +74,20 @@ bool boolTest(T)()
assert(t == boolValue);
assert(boolValue == t);
+ t = true; // tests inferType
+ auto inferredValue = t;
+ assert(inferredValue == true);
+
+ t = true; // tests function argument
+ bool functionCall(bool test)
+ {
+ return test;
+ }
+ assert(t == functionCall(t));
+
+ t = true; // tests CastExp
+ assert(t == cast(bool)t);
+
t = true;
return t; // tests ReturnStatement
}
@@ -97,6 +111,9 @@ int intTest(T)()
assert(t <= 42);
assert(42 >= t);
+ t = 42; // tests CastExp
+ assert(42 == cast(int)t);
+
// These currently don't work for properties due to https://issues.dlang.org/show_bug.cgi?id=8006
static if (!(typeid(T) is typeid(StructProperty!int)) && !(typeid(T) is typeid(ClassProperty!int)))
{
diff --git a/gcc/testsuite/gdc.test/runnable/test17868.d b/gcc/testsuite/gdc.test/runnable/test17868.d
new file mode 100644
index 00000000000..4609440c702
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17868.d
@@ -0,0 +1,45 @@
+/*
+REQUIRED_ARGS: -betterC
+RUN_OUTPUT:
+---
+init
+init
+main
+fini
+fini
+---
+*/
+
+import core.stdc.stdio;
+
+extern(C):
+
+pragma(crt_constructor)
+void init()
+{
+ puts("init");
+}
+
+pragma(crt_destructor)
+void fini2()
+{
+ puts("fini");
+}
+
+pragma(crt_constructor)
+void foo()
+{
+ puts("init");
+}
+
+pragma(crt_destructor)
+void bar()
+{
+ puts("fini");
+}
+
+int main()
+{
+ puts("main");
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test17868b.d b/gcc/testsuite/gdc.test/runnable/test17868b.d
new file mode 100644
index 00000000000..d28cae29edc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17868b.d
@@ -0,0 +1,52 @@
+/*
+REQUIRED_ARGS: -betterC
+RUN_OUTPUT:
+---
+init
+init
+main
+fini
+fini
+---
+*/
+
+import core.stdc.stdio;
+
+extern(C):
+
+pragma(crt_constructor)
+pragma(crt_destructor)
+void ctor_dtor_1()
+{
+ __gshared bool initialized;
+ puts(initialized ? "fini" : "init");
+ initialized = true;
+}
+
+pragma(crt_constructor)
+__gshared int var; // ignored for anything but functions
+
+pragma(crt_constructor)
+{
+ version (all) void init()
+ {
+ puts("init");
+ }
+}
+
+template fini()
+{
+ pragma(crt_destructor)
+ void fini()
+ {
+ puts("fini");
+ }
+}
+
+alias instantiate = fini!();
+
+int main()
+{
+ puts("main");
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test17885.d b/gcc/testsuite/gdc.test/runnable/test17885.d
new file mode 100644
index 00000000000..a6e521b03a3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17885.d
@@ -0,0 +1,11 @@
+module test17885;
+
+struct T { ulong a, b; }
+T f() { return T(); }
+
+void main()
+{
+ int[T] set = [f(): 0];
+ set.remove(f());
+ assert(f() !in set);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test17940.d b/gcc/testsuite/gdc.test/runnable/test17940.d
new file mode 100644
index 00000000000..1ea43dbfacc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17940.d
@@ -0,0 +1,46 @@
+// PERMUTE_ARGS: -O
+
+// https://issues.dlang.org/show_bug.cgi?id=17940
+
+struct Array
+{
+ long length;
+ long ptr;
+}
+
+struct Struct
+{
+ bool b = true;
+}
+
+void fun1(int)
+{
+}
+
+void fun2(Array arr, int, int)
+{
+ assert(!arr.length);
+}
+
+void fn(Struct* str)
+{
+ Array arr;
+ if (!str)
+ {
+ return;
+ }
+ if (str)
+ {
+ fun1(str.b);
+ }
+ if (str.b)
+ {
+ fun2(arr, str.b, 0);
+ }
+}
+
+void main()
+{
+ Struct s;
+ fn(&s);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test17943.d b/gcc/testsuite/gdc.test/runnable/test17943.d
new file mode 100644
index 00000000000..7ec2665845d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17943.d
@@ -0,0 +1,9 @@
+// REQUIRED_ARGS: -O
+
+void main()
+{
+ int[32] data;
+ auto p1 = data.ptr + 0;
+ auto p2 = data.ptr + 3;
+ assert(p2 - p1 == 3);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test17965.d b/gcc/testsuite/gdc.test/runnable/test17965.d
new file mode 100644
index 00000000000..2937d6d8844
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test17965.d
@@ -0,0 +1,29 @@
+/***************************************/
+// https://issues.dlang.org/show_bug.cgi?id=17965
+
+import core.stdc.math;
+
+struct Point{double x,y;}
+
+Point foo10()
+{
+ Point result = Point(1.0, 2.0);
+ return result;
+}
+
+Point foo20()
+{
+ Point result;
+ return result;
+}
+
+void main()
+{
+ auto p = foo10();
+ assert(p.x == 1.0);
+ assert(p.y == 2.0);
+
+ auto q = foo20();
+ assert(isnan(q.x));
+ assert(isnan(q.y));
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test18296.d b/gcc/testsuite/gdc.test/runnable/test18296.d
new file mode 100644
index 00000000000..0f7ac7e0db1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test18296.d
@@ -0,0 +1,24 @@
+// REQUIRED_ARGS: -cov
+// PERMUTE_ARGS: -fPIC
+alias AliasSeq(Args...) = Args;
+
+struct Duration
+{
+ this(long hnsecs)
+ {
+ _hnsecs = hnsecs;
+ }
+
+
+ long _hnsecs;
+}
+
+void main()
+{
+ foreach(U; AliasSeq!(Duration, const Duration))
+ {
+ const Duration t = 42;
+ U u = t;
+ assert(t._hnsecs == u._hnsecs);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test18534.d b/gcc/testsuite/gdc.test/runnable/test18534.d
new file mode 100644
index 00000000000..8d4653dec9d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test18534.d
@@ -0,0 +1,18 @@
+/* REQUIRED_ARGS: -O
+ * PERMUTE_ARGS:
+ */
+
+// https://issues.dlang.org/show_bug.cgi?id=18534
+
+auto blah(char ch) { return ch; }
+
+auto foo(int i)
+{
+ return blah(i ? 'A' : 'A');
+}
+
+void main()
+{
+ auto c = foo(0);
+ assert(c == 'A');
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test18545.d b/gcc/testsuite/gdc.test/runnable/test18545.d
new file mode 100644
index 00000000000..fc3d24acc99
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test18545.d
@@ -0,0 +1,63 @@
+module test18545;
+
+enum Constness
+{
+ Mutable,
+ Const,
+ Immutable,
+}
+
+enum Cases = [Constness.Mutable, Constness.Const, Constness.Immutable];
+
+void main()
+{
+ static foreach (from; Cases)
+ {
+ static foreach (to; Cases)
+ {
+ test!(from, to)();
+ }
+ }
+}
+
+void test(Constness from, Constness to)()
+{
+ struct S {
+ int i;
+
+ @property int get() const { return 0; }
+
+ alias get this;
+ }
+
+ static if (from == Constness.Mutable)
+ {
+ alias ConstS = S;
+ }
+ else static if (from == Constness.Const)
+ {
+ alias ConstS = const(S);
+ }
+ else
+ {
+ alias ConstS = immutable(S);
+ }
+
+ ConstS s1 = S(42);
+
+ // this should reinterpret-cast, NOT call the implicit constructor with .get!
+ static if (to == Constness.Mutable)
+ {
+ auto s2 = cast() s1;
+ }
+ else static if (to == Constness.Const)
+ {
+ const s2 = cast(const) s1;
+ }
+ else static if (to == Constness.Immutable)
+ {
+ immutable s2 = cast(immutable) s1;
+ }
+
+ assert(s2.i == s1.i, "Bug 18545 occurred casting from "~from.stringof~" to "~to.stringof);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test18746.d b/gcc/testsuite/gdc.test/runnable/test18746.d
new file mode 100644
index 00000000000..a2cf38b20df
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test18746.d
@@ -0,0 +1,16 @@
+// https://issues.dlang.org/show_bug.cgi?id=18746
+
+struct S {}
+
+S f(ref int i)
+{
+ ++i;
+ return S();
+}
+
+void main()
+{
+ int i = 2;
+ assert(f(i) == S());
+ assert(i == 3); /* failed before patch, i = 2; should pass */
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test18868.d b/gcc/testsuite/gdc.test/runnable/test18868.d
new file mode 100644
index 00000000000..b0085c030e0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test18868.d
@@ -0,0 +1,9 @@
+/*
+COMPILE_SEPARATELY
+EXTRA_SOURCES: imports/test18868_a.d imports/test18868_fls.d
+PERMUTE_ARGS:
+*/
+
+import imports.test18868_fls;
+alias floop = FLS!(int);
+void main() {}
diff --git a/gcc/testsuite/gdc.test/runnable/test18868_2.d b/gcc/testsuite/gdc.test/runnable/test18868_2.d
new file mode 100644
index 00000000000..a90ec1f3318
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test18868_2.d
@@ -0,0 +1,13 @@
+mixin(genCtor("666")); mixin(genCtor("777"));
+
+int i;
+
+string genCtor(string a)
+{
+ return "static this() { i += " ~ a ~ "; }";
+}
+
+void main()
+{
+ assert(i == 0 + 666 + 777);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test18868_3.d b/gcc/testsuite/gdc.test/runnable/test18868_3.d
new file mode 100644
index 00000000000..ed6f1724322
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test18868_3.d
@@ -0,0 +1,16 @@
+static foreach(s; ["666", "777", "888"])
+{
+ mixin(genCtor(s));
+}
+
+int i;
+
+string genCtor(string a)
+{
+ return "static this() { i += " ~ a ~ "; }";
+}
+
+void main()
+{
+ assert(i == 0 + 666 + 777 + 888);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test18880.d b/gcc/testsuite/gdc.test/runnable/test18880.d
new file mode 100644
index 00000000000..c275ef22827
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test18880.d
@@ -0,0 +1,20 @@
+/* REQUIRED_ARGS: -unittest
+ PERMUTE_ARGS:
+ */
+
+static foreach(s; ["666", "777", "888"])
+{
+ mixin(genTest(s));
+}
+
+int i;
+
+string genTest(string a)
+{
+ return "unittest { i += " ~ a ~ "; }";
+}
+
+void main()
+{
+ assert(i == 0 + 666 + 777 + 888);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test18916.d b/gcc/testsuite/gdc.test/runnable/test18916.d
new file mode 100644
index 00000000000..0e844ada15a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test18916.d
@@ -0,0 +1,22 @@
+struct Line
+{
+ int line;
+ alias line this;
+
+ this(int line)
+ {
+ this.line = line;
+ }
+}
+
+void foo(Line line1 = __LINE__, int line2 = __LINE__, int line3 = int(__LINE__))
+{
+ assert(line1 == 12);
+ assert(line2 == 21);
+ assert(line3 == 12);
+}
+
+void main()
+{
+ foo();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19.d b/gcc/testsuite/gdc.test/runnable/test19.d
index b9360eeeabb..372d32f71e3 100644
--- a/gcc/testsuite/gdc.test/runnable/test19.d
+++ b/gcc/testsuite/gdc.test/runnable/test19.d
@@ -128,8 +128,8 @@ int[] test6_1(int[] a)
void test6()
{
printf("test6()\n");
- int b[3];
- int a[];
+ int[3] b;
+ int[] a;
b[0] = 0;
b[1] = 1;
@@ -144,7 +144,7 @@ void test6()
class OutBuffer7
{
- char data[];
+ char[] data;
uint offset;
void write(const(char) *p, uint nbytes)
@@ -161,7 +161,7 @@ void test7()
OutBuffer7 ob = new OutBuffer7;
ob.data = new char[10];
- printf("ob.data.length = %d\n", ob.data.length);
+ printf("ob.data.length = %zd\n", ob.data.length);
assert(ob.data.length == 10);
for (i = 0; i < 10; i++)
assert(ob.data[i] == char.init);
@@ -169,14 +169,14 @@ void test7()
printf("test7.1()\n");
ob.data[] = '-';
printf("test7.2()\n");
- printf("ob.data[] = '%.*s'\n", ob.data.length, ob.data.ptr);
+ printf("ob.data[] = '%.*s'\n", cast(int)ob.data.length, ob.data.ptr);
for (i = 0; i < 10; i++)
assert(ob.data[i] == '-');
ob.offset = 3;
ob.write("foo", 3);
- printf("ob.data.length = %d\n", ob.data.length);
- printf("ob.data[] = '%.*s'\n", ob.data.length, ob.data.ptr);
+ printf("ob.data.length = %zd\n", ob.data.length);
+ printf("ob.data[] = '%.*s'\n", cast(int)ob.data.length, ob.data.ptr);
for (i = 0; i < 10; i++)
{
if (i < 3 || i >= 6)
@@ -404,7 +404,7 @@ void test20()
}
/* ================================ */
-// 7848
+// https://issues.dlang.org/show_bug.cgi?id=7848
@safe pure nothrow void func7848() {}
@@ -414,7 +414,7 @@ void test20()
}
/* ================================ */
-// 8128
+// https://issues.dlang.org/show_bug.cgi?id=8128
int flag8128 = 0;
diff --git a/gcc/testsuite/gdc.test/runnable/test19086.d b/gcc/testsuite/gdc.test/runnable/test19086.d
new file mode 100644
index 00000000000..026aee436c8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19086.d
@@ -0,0 +1,64 @@
+// REQUIRED_ARGS: -g
+// REQUIRED_ARGS(linux freebsd dragonflybsd): -L-export-dynamic
+// PERMUTE_ARGS:
+// DISABLED: osx
+
+void run19086()
+{
+ long x = 1;
+ int y = 0;
+#line 20
+ throw newException();
+}
+
+// moved here to keep run19086 short
+Exception newException() { return new Exception("hi"); }
+
+void test19086()
+{
+ try
+ {
+ run19086();
+ }
+ catch(Exception e)
+ {
+ int line = findLineStackTrace(e.toString(), "run19086");
+ assert(line >= 20 && line <= 21);
+ }
+}
+
+int findLineStackTrace(string msg, string func)
+{
+ // find line number of _Dmain in stack trace
+ // on linux: file.d:line _Dmain [addr]
+ // on windows: addr in _Dmain at file.d(line)
+ int line = 0;
+ bool found = false;
+ for (size_t pos = 0; pos + func.length < msg.length; pos++)
+ {
+ if (msg[pos] == '\n')
+ {
+ line = 0;
+ found = false;
+ }
+ else if ((msg[pos] == ':' || msg[pos] == '(') && line == 0)
+ {
+ for (pos++; pos < msg.length && msg[pos] >= '0' && msg[pos] <= '9'; pos++)
+ line = line * 10 + msg[pos] - '0';
+ if (line > 0 && found)
+ return line;
+ }
+ else if (msg[pos .. pos + func.length] == func)
+ {
+ found = true;
+ if (line > 0 && found)
+ return line;
+ }
+ }
+ return 0;
+}
+
+void main()
+{
+ test19086();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19122.d b/gcc/testsuite/gdc.test/runnable/test19122.d
new file mode 100644
index 00000000000..369f5dd5cfc
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19122.d
@@ -0,0 +1,46 @@
+// https://issues.dlang.org/show_bug.cgi?id=19122
+struct HasDestructor
+{
+ ~this()
+ {
+ assert(0);
+ }
+ this(this)
+ {
+ assert(0);
+ }
+}
+
+struct S
+{
+ union
+ {
+ int i;
+ HasDestructor h;
+ }
+}
+
+struct S2
+{
+ union
+ {
+ align(1)
+ {
+ int i;
+ HasDestructor h;
+ }
+ }
+}
+
+void main()
+{
+ {
+ S s;
+ s = s;
+ }
+
+ {
+ S2 s2;
+ s2 = s2;
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19185.d b/gcc/testsuite/gdc.test/runnable/test19185.d
new file mode 100644
index 00000000000..d5a1e5ec7c5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19185.d
@@ -0,0 +1,22 @@
+// https://issues.dlang.org/show_bug.cgi?id=19185
+
+int fun()
+{
+ int x = 2;
+ struct A
+ {
+ int a;
+ this(int a)
+ {
+ this.a = a + x; // segault here
+ }
+ }
+
+ A a = 5;
+ return a.a;
+}
+
+void main()
+{
+ assert(fun() == 7);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19192.d b/gcc/testsuite/gdc.test/runnable/test19192.d
new file mode 100644
index 00000000000..24f245089e1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19192.d
@@ -0,0 +1,18 @@
+// https://issues.dlang.org/show_bug.cgi?id=19192
+interface Foo
+{
+ Foo covariant();
+}
+
+abstract class Frop : Foo {}
+
+class Bar : Frop
+{
+ Bar covariant() { return this; }
+}
+
+void main()
+{
+ Foo foo = new Bar;
+ assert(foo is foo.covariant());
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19223.d b/gcc/testsuite/gdc.test/runnable/test19223.d
new file mode 100644
index 00000000000..6faa59bbedf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19223.d
@@ -0,0 +1,38 @@
+
+static if (__traits(compiles, __vector(int[4])))
+{
+ alias int4 = __vector(int[4]);
+
+ int fn(const int[4] x)
+ {
+ int sum = 0;
+ foreach (i; x) sum += i;
+ return sum;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19223
+ void test19223()
+ {
+ int4 v1 = int4.init;
+ assert(fn(v1.array) == 0);
+ assert(fn(int4.init.array) == 0);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19607
+ void test19607()
+ {
+ int4 v1 = 1;
+ assert(fn(v1.array) == 4);
+ assert(fn(int4(2).array) == 8);
+ }
+
+ void main ()
+ {
+ test19223();
+ test19607();
+ }
+}
+else
+{
+ void main() { }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19251.d b/gcc/testsuite/gdc.test/runnable/test19251.d
new file mode 100644
index 00000000000..9e0b9d4f083
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19251.d
@@ -0,0 +1,20 @@
+string result;
+
+struct A
+{
+ int[] a;
+ immutable(A) fun()
+ {
+ result ~= "Yo";
+ return immutable A([7]);
+ }
+
+ alias fun this;
+}
+
+void main()
+{
+ A a;
+ immutable A b = a; // error: cannot implicitly convert expression a of type A to immutable(A)
+ assert(result == "Yo");
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19317.d b/gcc/testsuite/gdc.test/runnable/test19317.d
new file mode 100644
index 00000000000..a5633b8c13b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19317.d
@@ -0,0 +1,32 @@
+// REQUIRED_ARGS: -preview=dip1008
+// https://issues.dlang.org/show_bug.cgi?id=19317
+
+class MyException: Exception {
+ static int numInstances;
+ this(string msg) {
+ super(msg);
+ ++numInstances;
+ }
+
+ ~this() {
+ --numInstances;
+ }
+}
+
+void main() {
+ assert(MyException.numInstances == 0);
+
+ try
+ throw new MyException("oops");
+ catch(MyException _)
+ assert(MyException.numInstances == 1);
+
+ assert(MyException.numInstances == 0);
+
+ try
+ throw new MyException("oops I did it again");
+ catch(MyException)
+ assert(MyException.numInstances == 1);
+
+ assert(MyException.numInstances == 0);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19386.d b/gcc/testsuite/gdc.test/runnable/test19386.d
new file mode 100644
index 00000000000..3e3157a8a2b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19386.d
@@ -0,0 +1,36 @@
+struct Thing
+{
+ this(int* i)
+ {
+ ptr = i;
+ (*ptr)++;
+ }
+
+ ~this()
+ {
+ (*ptr)--;
+ }
+
+ T opCast(T : bool)()
+ {
+ return false;
+ }
+
+ int* ptr;
+}
+
+Thing makeThing(int* p)
+{
+ return Thing(p);
+}
+
+void main()
+{
+ int i;
+ {
+ if (auto t = makeThing(&i)) // destructor not called
+ {
+ }
+ }
+ assert(i == 0);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19393.d b/gcc/testsuite/gdc.test/runnable/test19393.d
new file mode 100644
index 00000000000..4226bbd402c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19393.d
@@ -0,0 +1,37 @@
+string result;
+
+struct S
+{
+ this(this)
+ {
+ result ~= "A";
+ }
+
+ ~this()
+ {
+ result ~= "B";
+ }
+}
+
+void foo(const(S)[] ar...)
+{
+ /* postblit gets called on this initialization,
+ * then when the function returns, the destructor
+ * gets called => result = "AB";
+ */
+ auto d = ar[0];
+}
+
+void bar()
+{
+ /* S(null) needs to be destroyed after the function call,
+ * that means that another `B` is appended => result = "ABB"
+ */
+ foo(S());
+}
+
+void main()
+{
+ bar();
+ assert(result == "ABB");
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19441.d b/gcc/testsuite/gdc.test/runnable/test19441.d
new file mode 100644
index 00000000000..5dcb573600a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19441.d
@@ -0,0 +1,24 @@
+// https://issues.dlang.org/show_bug.cgi?id=19441
+struct S1
+{
+ int a;
+ long b;
+ alias a this;
+}
+
+struct S2
+{
+ S1 v;
+ alias v this;
+}
+
+void main()
+{
+ auto x = S2(S1(1, 12345678));
+ assert(x.a == 1 && x.b == 12345678); // prints: 1 12345678
+ S1 y;
+ y = x;
+ assert(y.a == 1 && y.b == 12345678);
+ y = x.v;
+ assert(y.a == 1 && y.b == 12345678);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19476.d b/gcc/testsuite/gdc.test/runnable/test19476.d
new file mode 100644
index 00000000000..3143c20817f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19476.d
@@ -0,0 +1,18 @@
+// https://issues.dlang.org/show_bug.cgi?id=19476
+
+mixin template operators() {
+ int foo(int op = 1, T)(T rhs) {
+ return 1;
+ }
+}
+
+struct S {
+ mixin operators ops;
+ int foo(int op = 1, T)(T a) {
+ return ops.foo!1(a);
+ }
+}
+
+void main() {
+ S.init.foo(S.init);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19639.d b/gcc/testsuite/gdc.test/runnable/test19639.d
new file mode 100644
index 00000000000..36b763046e5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19639.d
@@ -0,0 +1,22 @@
+enum EMPTY_STRING = ""[0..0];
+enum SMALL_STRING = "a"[0..1];
+
+void main()
+{
+ {
+ char[64] buf = EMPTY_STRING;
+ foreach (c; buf) assert(c == 0);
+ buf[$-1] = 'e';
+ buf = EMPTY_STRING;
+ assert(buf[$-1] == 0);
+ }
+
+ {
+ char[64] buf = SMALL_STRING;
+ assert(buf[0] == 'a');
+ foreach (c; buf[1..$]) assert(c == 0);
+ buf[$-1] = 'e';
+ buf = SMALL_STRING;
+ assert(buf[$-1] == 0);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19655a.d b/gcc/testsuite/gdc.test/runnable/test19655a.d
new file mode 100644
index 00000000000..fc636396b3f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19655a.d
@@ -0,0 +1,10 @@
+/*
+REQUIRED_ARGS: -Irunnable/imports
+PERMUTE_ARGS:
+COMPILE_SEPARATELY:
+EXTRA_SOURCES: imports/test19655b.d imports/test19655c.d imports/test19655d.d imports/test19655e.d imports/test19655f.d imports/test19655g.d
+*/
+
+import test19655g;
+class Corge
+{ }
diff --git a/gcc/testsuite/gdc.test/runnable/test19672.d b/gcc/testsuite/gdc.test/runnable/test19672.d
new file mode 100644
index 00000000000..8f5f50bdf97
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19672.d
@@ -0,0 +1,21 @@
+// https://issues.dlang.org/show_bug.cgi?id=19672
+
+struct S
+{
+ ulong c;
+ bool b; // removing this prevents bug
+}
+
+// increase the struct size at least to 17 bytes also prevents the bug.
+
+void main()
+{
+ S[1] a = [S(42)];
+ assert(a[0].c == 42); /* Passes. */
+ f(a);
+}
+
+void f(S[1] a)
+{
+ assert(a[0].c == 42); /* Fails. */
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19679.d b/gcc/testsuite/gdc.test/runnable/test19679.d
new file mode 100644
index 00000000000..18b050c91fd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19679.d
@@ -0,0 +1,21 @@
+void delegate() foo()
+{
+ size_t value = 0;
+
+ void check()
+ {
+ assert(value == 0);
+ }
+
+ void nest1()
+ {
+ void nest2() { check(); }
+ nest2();
+ }
+ return &nest1;
+}
+
+void main()
+{
+ foo()();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19688.d b/gcc/testsuite/gdc.test/runnable/test19688.d
new file mode 100644
index 00000000000..9cc4dd7298d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19688.d
@@ -0,0 +1,13 @@
+/* TEST_OUTUT:
+---
+---
+*/
+void test(string s = __FUNCTION__ ~ __MODULE__ ~ __FUNCTION__)
+{
+ assert(s == "test19688.maintest19688test19688.main");
+}
+
+void main()
+{
+ test();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19729.d b/gcc/testsuite/gdc.test/runnable/test19729.d
new file mode 100644
index 00000000000..b959c71c62e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19729.d
@@ -0,0 +1,61 @@
+//https://issues.dlang.org/show_bug.cgi?id=19729
+// PERMUTE_ARGS:
+module test19729;
+
+mixin template Templ(T)
+{
+ this(T t)
+ {
+ }
+}
+
+class C // original TC
+{
+ mixin Templ!int;
+ mixin Templ!string;
+}
+
+class D // named
+{
+ mixin Templ!int ti;
+ mixin Templ!string ts;
+}
+
+class E // top level ctor wins
+{
+ bool topLevelWins;
+ mixin Templ!int;
+ this(int){topLevelWins = true;}
+}
+
+class F // top level ctor wins even if not exact match
+{
+ bool topLevelWins;
+ mixin Templ!ubyte;
+ this(int){topLevelWins = true;}
+}
+
+class G // same as F but change lexicographical order
+{
+ bool topLevelWins;
+ this(int){topLevelWins = true;}
+ mixin Templ!ubyte;
+}
+
+void main()
+{
+ auto c0 = new C("should work");
+ auto c1 = new C(42);
+
+ auto d0 = new D("should work");
+ auto d1 = new D(42);
+
+ auto e = new E(0);
+ assert(e.topLevelWins);
+
+ auto f = new F(ubyte(0));
+ assert(f.topLevelWins);
+
+ auto g = new G(ubyte(0));
+ assert(g.topLevelWins);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19734.d b/gcc/testsuite/gdc.test/runnable/test19734.d
index efa7da3b019..73429202b2a 100644
--- a/gcc/testsuite/gdc.test/runnable/test19734.d
+++ b/gcc/testsuite/gdc.test/runnable/test19734.d
@@ -9,15 +9,13 @@ class C19734
{
// Invalid 'this' parameter because of applied 'extern' storage class.
void testin(typeof(this) p)
- in { assert(this is p); }
- body
+ in(this is p)
{
}
// Undefined reference to __result.
int testout()
- out { assert(__result == 2); }
- body
+ out(; __result == 2)
{
return 2;
}
diff --git a/gcc/testsuite/gdc.test/runnable/test19774.d b/gcc/testsuite/gdc.test/runnable/test19774.d
new file mode 100644
index 00000000000..940a07cc039
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19774.d
@@ -0,0 +1,43 @@
+// https://issues.dlang.org/show_bug.cgi?id=19774
+C bar()
+{
+ return C(42);
+}
+
+C foo()
+{
+ return bar()[1];
+}
+
+C gun()
+{
+ return bar()[$];
+}
+
+struct C
+{
+ int x;
+
+ ~this()
+ {
+ x = 0;
+ }
+
+ int opDollar()
+ {
+ return 1;
+ }
+
+ C opIndex(int a)
+ {
+ return this;
+ }
+}
+
+void main()
+{
+ auto c = foo();
+ assert(c.x == 42); /* fails; should pass */
+ auto d = gun();
+ assert(d.x == 42);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19782.d b/gcc/testsuite/gdc.test/runnable/test19782.d
new file mode 100644
index 00000000000..a24d84173ec
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19782.d
@@ -0,0 +1,23 @@
+// https://issues.dlang.org/show_bug.cgi?id=19782
+class Inner
+{
+ int a;
+}
+
+class Outer
+{
+ Inner inner; alias inner this;
+ this(Inner i) { inner = i; }
+}
+
+void main()
+{
+ Inner[] inners = [];
+ inners ~= new Inner;
+ inners ~= new Outer(new Inner); // Appends null
+
+ foreach(inner; inners)
+ {
+ assert(inner.a == 0);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19822.d b/gcc/testsuite/gdc.test/runnable/test19822.d
new file mode 100644
index 00000000000..8f0e3f14faf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19822.d
@@ -0,0 +1,29 @@
+// https://issues.dlang.org/show_bug.cgi?id=19822
+struct Quat
+{
+ static struct Vec { int x; }
+
+ union
+ {
+ Vec v;
+ struct { float x; }
+ }
+
+ static Quat identity()
+ {
+ Quat q;
+ q.x = 1.0f;
+ return q;
+ }
+}
+
+struct QuatContainerWithIncompatibleInit
+{
+ Quat q = Quat.identity;
+}
+
+void main()
+{
+ QuatContainerWithIncompatibleInit c;
+ assert(c.q.x == 1.0f); // fails
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19825.d b/gcc/testsuite/gdc.test/runnable/test19825.d
new file mode 100644
index 00000000000..a4f85ca0150
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19825.d
@@ -0,0 +1,42 @@
+struct JSONValue
+{
+ TaggedUnion payload;
+}
+
+struct TaggedUnion
+{
+ size_t[2] m_data;
+ int m_kind;
+
+ JSONValue opIndex(size_t i)
+ {
+ return JSONValue();
+ }
+}
+
+void yap(lazy JSONValue arg)
+{
+ arg();
+}
+
+struct Foo
+{
+ int a;
+ string name;
+}
+
+Foo makeFoo()
+{
+ JSONValue root;
+ yap(root.payload[0]
+ .payload[0]
+ .payload[0]);
+
+ Foo foo;
+ return foo;
+}
+
+void main()
+{
+ auto foo = makeFoo();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test19891.d b/gcc/testsuite/gdc.test/runnable/test19891.d
new file mode 100644
index 00000000000..08e51bc0f8d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test19891.d
@@ -0,0 +1,13 @@
+int g;
+
+void fun(R)(auto ref int a, auto ref R r = g, auto ref int b = 1)
+{
+ ++r;
+}
+
+void main()
+{
+ fun(10, 2);
+ fun(10);
+ assert(g == 1);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test20.d b/gcc/testsuite/gdc.test/runnable/test20.d
index 0e21e30920e..5a1985a1fa2 100644
--- a/gcc/testsuite/gdc.test/runnable/test20.d
+++ b/gcc/testsuite/gdc.test/runnable/test20.d
@@ -1,3 +1,9 @@
+/*
+TEST_OUTPUT:
+---
+runnable/test20.d(448): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+*/
import core.vararg;
extern(C) int printf(const char*, ...);
@@ -72,7 +78,7 @@ struct A4
void test4()
{
- printf("A4.sizeof = %d\n", A4.sizeof);
+ printf("A4.sizeof = %zd\n", A4.sizeof);
assert(A4.sizeof == 1 * int.sizeof);
A4 q;
@@ -213,7 +219,7 @@ void test10()
/*****************************************/
-scope class T11
+class T11
{
this(){}
~this(){}
@@ -270,7 +276,7 @@ void test13()
void write14(bool[] c)
{
- printf("[%2d]: ", c.length);
+ printf("[%2zd]: ", c.length);
foreach (bool x; c)
printf("%d,", x);
printf("\n");
@@ -334,12 +340,6 @@ int y16;
class C16
{
- new(size_t size, byte blah){
- void* v = (new byte[C16.classinfo.initializer.length]).ptr;
- y16 = 1;
- assert(blah == 3);
- return v;
- }
int x;
this()
{
@@ -349,8 +349,7 @@ class C16
void test16()
{
- C16 c = new(3) C16;
- assert(y16 == 1);
+ C16 c = new C16();
assert(c.x == 4);
}
@@ -491,34 +490,6 @@ void test22()
/*****************************************/
-void test23()
-{
- float f;
- double d;
- real r;
-
- if (f > ifloat.max)
- goto Loverflow;
- if (d > ifloat.max)
- goto Loverflow;
- if (r > ifloat.max)
- goto Loverflow;
-
- if (ifloat.max < f)
- goto Loverflow;
- if (ifloat.max < d)
- goto Loverflow;
- if (ifloat.max < r)
- goto Loverflow;
-
- return;
-
- Loverflow:
- return;
-}
-
-/*****************************************/
-
interface I24 { }
void test24()
@@ -583,15 +554,6 @@ void test29()
/*****************************************/
-void test30()
-{
- double d = 1;
- cdouble cd = 1+0i;
- assert(cd == 1.0 + 0i);
-}
-
-/*****************************************/
-
void foo31(...)
{
byte b = va_arg!byte(_argptr);
@@ -743,7 +705,7 @@ void test42()
string string1 = "ワロスw";
string string2 = r"ワロスw";
string string3 = `ワロスw`;
- string string4 = x"E3 83 AF E3 83 AD E3 82 B9 EF BD 97";
+ string string4 = "\xE3\x83\xAF\xE3\x83\xAD\xE3\x82\xB9\xEF\xBD\x97";
assert(string1.length==master.length);
@@ -806,7 +768,7 @@ struct S45
{
double x = 0, y = 0;
static S45 opCall(int i) { S45 r; r.x = i; return r; }
- S45 opMul(S45 s)
+ S45 opBinary(string op)(S45 s) if (op == "*")
{
S45 r;
r.x = x * s.x;
@@ -842,11 +804,11 @@ void test45()
{
S45 s = S45(10);
S45 val = pow!(S45)(s,2);
- printf("x = %2.2lf, y = %2.2lf\n", val.x, val.y);
+ printf("x = %2.2f, y = %2.2f\n", val.x, val.y);
assert(val.x == 100);
assert(val.y == 0);
double d = pow!(double)(10,3);
- printf("%2.2lf\n", d);
+ printf("%2.2f\n", d);
assert(d == 1000);
}
@@ -873,19 +835,6 @@ void test46()
/*****************************************/
-void test47()
-{
- cdouble[] a;
- cdouble[] b;
- foreach(ref cdouble d; b)
- {
- d = -a[0];
- for(;;){}
- }
-}
-
-/*****************************************/
-
string foo48(string s)
{
return s;
@@ -915,7 +864,7 @@ void test49()
{
int i = void;
//printf("i = %d\n", i);
- int[10] a = void;
+ int[10] a;
foreach (int x; a)
{
printf("\tx = %d\n", x);
@@ -938,7 +887,7 @@ void test51()
{
bool[9][3] qwert;
- printf("qwert.sizeof = %d\n", qwert.sizeof);
+ printf("qwert.sizeof = %zd\n", qwert.sizeof);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 9; j++)
@@ -987,13 +936,13 @@ const char[3][13] month = [
void test53()
{
- printf("%.*s\n", month[1].length, month[1].ptr);
- printf("%.*s\n", month[2].length, month[2].ptr);
- printf("%.*s\n", month[3].length, month[3].ptr);
- printf("%.*s\n", month[4].length, month[4].ptr);
- printf("%.*s\n", month[5].length, month[5].ptr);
- printf("%.*s\n", month[6].length, month[6].ptr);
- printf("%.*s\n", month[8].length, month[8].ptr);
+ printf("%.*s\n", cast(int)month[1].length, month[1].ptr);
+ printf("%.*s\n", cast(int)month[2].length, month[2].ptr);
+ printf("%.*s\n", cast(int)month[3].length, month[3].ptr);
+ printf("%.*s\n", cast(int)month[4].length, month[4].ptr);
+ printf("%.*s\n", cast(int)month[5].length, month[5].ptr);
+ printf("%.*s\n", cast(int)month[6].length, month[6].ptr);
+ printf("%.*s\n", cast(int)month[8].length, month[8].ptr);
assert(month[1] == "Jan");
assert(month[2] == "Feb");
@@ -1032,7 +981,7 @@ struct S54
return S54.foo() * S54.foo();
}
- S54 opMul(S54 s)
+ S54 opBinary(string op)(S54 s) if (op == "*")
{
return s;
}
@@ -1052,12 +1001,12 @@ void test55()
str = str ~ c;
uvw = c ~ uvw;
- printf("%.*s\n", str.length, str.ptr);
+ printf("%.*s\n", cast(int)str.length, str.ptr);
assert(str == "a");
assert(uvw == "a");
c = 'b';
- printf("%.*s\n", str.length, str.ptr);
+ printf("%.*s\n", cast(int)str.length, str.ptr);
assert(str == "a");
assert(uvw == "a");
}
@@ -1199,7 +1148,7 @@ void foo61(real[] arr)
for (size_t j = i; j >= i; j -= i)
{
// interesting results follow from this:
- printf("%d ", i);
+ printf("%zd ", i);
// it prints a _lot_ of ones
arr[j] = arr[j - i];
@@ -1253,14 +1202,12 @@ int main()
test20();
test21();
test22();
- test23();
test24();
test25();
test26();
test27();
test28();
test29();
- test30();
test31();
test33();
test34();
@@ -1274,7 +1221,6 @@ int main()
test44();
test45();
test46();
- test47();
test48();
test49();
test50();
@@ -1294,4 +1240,3 @@ int main()
printf("Success\n");
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/test20025.d b/gcc/testsuite/gdc.test/runnable/test20025.d
new file mode 100644
index 00000000000..9abee457cb0
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test20025.d
@@ -0,0 +1,21 @@
+// https://issues.dlang.org/show_bug.cgi?id=20025
+struct B
+{
+ static int value = 77;
+ alias value this;
+
+ this(ref return scope inout B rhs) inout { }
+}
+
+void test(int x)
+{
+ assert(x == 77);
+}
+
+int main()
+{
+ B b;
+ test(b);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test20036.d b/gcc/testsuite/gdc.test/runnable/test20036.d
new file mode 100644
index 00000000000..640de192480
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test20036.d
@@ -0,0 +1,10 @@
+
+__gshared int x = 7;
+__gshared int*[70000] px = &x;
+
+void main()
+{
+ foreach(p; px)
+ assert(p && *p == 7);
+}
+
diff --git a/gcc/testsuite/gdc.test/runnable/test20130.d b/gcc/testsuite/gdc.test/runnable/test20130.d
new file mode 100644
index 00000000000..09eaee36dd4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test20130.d
@@ -0,0 +1,44 @@
+// https://issues.dlang.org/show_bug.cgi?id=20130
+
+void main() {
+ auto a1 = cast(char[]) "12345678";
+ auto a2 = cast(wchar[]) "12345678"; // Or cast(string|wstring|dstring).
+ auto a3 = cast(dchar[]) "12345678"; // Encoding conversion.
+ assert(a1.length == 8);
+ assert(a2.length == 8);
+ assert(a3.length == 8);
+
+ auto b1 = cast(char[]) "12345678"c;
+ auto b2 = cast(wchar[]) "12345678"c;
+ auto b3 = cast(dchar[]) "12345678"c;
+ assert(b1.length == 8);
+ assert(b2.length == 4);
+ assert(b3.length == 2);
+
+ auto c1 = cast(char[]) "12345678"w;
+ auto c2 = cast(wchar[]) "12345678"w;
+ auto c3 = cast(dchar[]) "12345678"w;
+ assert(c1.length == 16);
+ assert(c2.length == 8);
+ assert(c3.length == 4);
+
+ auto d1 = cast(char[]) "12345678"d;
+ auto d2 = cast(wchar[]) "12345678"d;
+ auto d3 = cast(dchar[]) "12345678"d;
+ assert(d1.length == 32);
+ assert(d2.length == 16);
+ assert(d3.length == 8);
+
+ auto a = cast(uint[]) "12345678";
+ auto b = cast(uint[]) "12345678"d;
+ auto c = cast(uint[]) "12345678"w;
+ auto d = cast(const char[5][]) "12345";
+ auto e = cast(const wchar[2][]) "12345678";
+ immutable f = cast(immutable(uint)[]) "123456789012";
+ assert(a.length == 2);
+ assert(b.length == 8);
+ assert(c.length == 4);
+ assert(d.length == 1);
+ assert(e.length == 2);
+ assert(f.length == 3);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test20401.d b/gcc/testsuite/gdc.test/runnable/test20401.d
new file mode 100644
index 00000000000..c3b60721469
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test20401.d
@@ -0,0 +1,20 @@
+// https://issues.dlang.org/show_bug.cgi?id=20401
+
+void main()
+{
+ int i;
+ assert(&passthrough(i) == &i);
+}
+
+ref int passthrough(return ref int i)
+{
+ return get().flag ? i : i;
+}
+
+S get() { return S(); }
+
+struct S
+{
+ bool flag;
+ ~this(){}
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test20565.d b/gcc/testsuite/gdc.test/runnable/test20565.d
new file mode 100644
index 00000000000..47a8db34342
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test20565.d
@@ -0,0 +1,18 @@
+// https://issues.dlang.org/show_bug.cgi?id=20565
+
+void main()
+{
+ {
+ int temp(T)() { return 3; }
+ assert(temp!int() == 3);
+ }
+ {
+ int temp(T)() { return 4; }
+ assert(temp!int() == 4);
+ }
+ {
+ int temp(T)() { return 5; }
+ assert(temp!int() == 5);
+ }
+}
+
diff --git a/gcc/testsuite/gdc.test/runnable/test20649.d b/gcc/testsuite/gdc.test/runnable/test20649.d
new file mode 100644
index 00000000000..6c645e696e7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test20649.d
@@ -0,0 +1,15 @@
+struct S { int i; }
+
+auto f()
+{
+ S[] ss;
+ ss.length = 1;
+ return 0;
+}
+
+enum a = f();
+
+void main()
+{
+ f();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test20855.d b/gcc/testsuite/gdc.test/runnable/test20855.d
new file mode 100644
index 00000000000..7e43ec801a7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test20855.d
@@ -0,0 +1,26 @@
+// https://issues.dlang.org/show_bug.cgi?id=20855
+
+string exp()
+{
+ string s = "a = b + c * d + a;";
+ foreach (i; 0 .. 9)
+ s = s ~ s;
+ return s;
+}
+
+int test()
+{
+ auto a=1, b=2, c=3, d=4;
+ mixin(exp());
+ return a;
+}
+
+import core.stdc.stdio;
+
+int main()
+{
+ int a = test();
+ printf("a = %d\n", a);
+ assert(test() == 7169);
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test20893.d b/gcc/testsuite/gdc.test/runnable/test20893.d
new file mode 100644
index 00000000000..18a83aa44f1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test20893.d
@@ -0,0 +1,21 @@
+// REQUIRED_ARGS: -O -inline
+//
+// https://issues.dlang.org/show_bug.cgi?id=20893
+// caused by https://github.com/dlang/dmd/pull/9722
+
+int f(int n)
+{
+ foreach (i; 0..n) {}
+ return 10;
+}
+
+int c(int a, int b)
+{
+ return (f(a) * 1L * f(b)) % 1000;
+}
+
+void main()
+{
+ int[] a = [1];
+ assert(c(2 - 1, 2) == 100);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21040.d b/gcc/testsuite/gdc.test/runnable/test21040.d
new file mode 100644
index 00000000000..8e5ee56fa1e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21040.d
@@ -0,0 +1,61 @@
+// REQUIRED_ARGS:
+// PERMUTE_ARGS: -mcpu=native
+// https://issues.dlang.org/show_bug.cgi?id=21040
+
+import core.simd;
+
+alias AliasSeq(A ...) = A;
+
+void main()
+{
+ static foreach (T; AliasSeq!(
+ float[8], float[4], double[4], double[2],
+ byte[32], ubyte[32], byte[16], ubyte[16],
+ short[16], ushort[16], short[8], ushort[8],
+ int[8], uint[8], int[4], uint[4],
+ long[4], ulong[4], long[2], ulong[2],
+ void[32], void[16]))
+ {
+ static if (__traits(compiles, __vector(T)))
+ {{
+ __vector(T) v;
+
+ static if (__traits(compiles, { __vector(T) x = 2; }))
+ v = 2;
+ static if (__traits(compiles, { __vector(T) x; x = +x; }))
+ v = +v;
+ static if (__traits(compiles, { __vector(T) x; x = -x; }))
+ v = -v;
+ static if (__traits(compiles, { __vector(T) x; x = x + x; }))
+ v = v + v;
+ static if (__traits(compiles, { __vector(T) x; x += 2; }))
+ v += 2;
+ static if (__traits(compiles, { __vector(T) x; x = x - x; }))
+ v = v - v;
+ static if (__traits(compiles, { __vector(T) x; x -= 2; }))
+ v -= 2;
+ static if (__traits(compiles, { __vector(T) x; x = x * x; }))
+ v = v * v;
+ static if (__traits(compiles, { __vector(T) x; x *= 2; }))
+ v *= 2;
+ static if (__traits(compiles, { __vector(T) x; x = x / x; }))
+ v = v / v;
+ static if (__traits(compiles, { __vector(T) x; x /= 2; }))
+ v /= 2;
+ static if (__traits(compiles, { __vector(T) x; x = x & x; }))
+ v = v & v;
+ static if (__traits(compiles, { __vector(T) x; x &= 2; }))
+ v &= 2;
+ static if (__traits(compiles, { __vector(T) x; x = x | x; }))
+ v = v | v;
+ static if (__traits(compiles, { __vector(T) x; x |= 2; }))
+ v |= 2;
+ static if (__traits(compiles, { __vector(T) x; x = x ^ x; }))
+ v = v ^ v;
+ static if (__traits(compiles, { __vector(T) x; x ^= 2; }))
+ v ^= 2;
+ static if (__traits(compiles, { __vector(T) x; x = ~x; }))
+ v = ~v;
+ }}
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21120.d b/gcc/testsuite/gdc.test/runnable/test21120.d
new file mode 100644
index 00000000000..2714bfc6da8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21120.d
@@ -0,0 +1,27 @@
+// https://issues.dlang.org/show_bug.cgi?id=21120
+
+module one.two.three;
+
+struct S {}
+
+struct StructTemplate(T)
+{
+ int a = 123; // non-zero initialized
+
+ ref const(StructTemplate) getInitSymbol()
+ {
+ return initSymbol!StructTemplate;
+ }
+}
+
+template initSymbol(T)
+{
+ pragma(mangle, "_D" ~ T.mangleof[1..$] ~ "6__initZ")
+ extern immutable T initSymbol;
+}
+
+void main()
+{
+ StructTemplate!S inst;
+ assert(inst.getInitSymbol() == inst);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21357.d b/gcc/testsuite/gdc.test/runnable/test21357.d
new file mode 100644
index 00000000000..de219e99480
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21357.d
@@ -0,0 +1,35 @@
+// https://issues.dlang.org/show_bug.cgi?id=21357
+// PERMUTE_ARGS:
+struct BatchState
+{
+ int[10] arr;
+
+ BatchState copy()
+ {
+ auto ret = BatchState(arr);
+ arr[0] += 1;
+ return ret;
+ }
+}
+
+struct GrayArea
+{
+ BatchState low;
+
+ this(this)
+ {
+ low = low.copy;
+ }
+}
+
+void main()
+{
+ GrayArea a;
+ a.low.arr[0] = 1;
+ GrayArea b;
+ b.low.arr[0] = 4;
+ b = a; // calls the postblit
+
+ assert(a.low.arr[0] == 1);
+ assert(b.low.arr[0] == 1);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21403.d b/gcc/testsuite/gdc.test/runnable/test21403.d
new file mode 100644
index 00000000000..15314b02fe3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21403.d
@@ -0,0 +1,72 @@
+// https://issues.dlang.org/show_bug.cgi?id=21403
+
+/***********************************************/
+
+int[] cat11ret3(ref int[] s)
+{
+ s ~= 11;
+ return [3];
+}
+
+int[] doit1(int[] val)
+{
+ (val ~= cat11ret3(val)) ~= 7;
+ return val;
+}
+
+void test1()
+{
+ static assert(doit1([2]) == [2, 11, 3, 7]);
+ assert(doit1([2]) == [2, 11, 3, 7]);
+}
+
+/***********************************************/
+
+char catbretc(ref char[] s)
+{
+ s ~= 'b';
+ return 'c';
+}
+
+char[] doit2(char[] val)
+{
+ (val ~= catbretc(val)) ~= 'd';
+ return val;
+}
+
+void test2()
+{
+ static assert(doit2(['a']) == ['a', 'b', 'c', 'd']);
+ assert(doit2(['a']) == ['a', 'b', 'c', 'd']);
+}
+
+/***********************************************/
+
+int cat2ret3(ref int[] s)
+{
+ s ~= 2;
+ return 3;
+}
+
+int[] doit2(int[] val)
+{
+ (val ~= cat2ret3(val)) ~= 4;
+ return val;
+}
+
+void test3()
+{
+ static assert(doit2([1]) == [1, 2, 3, 4]);
+ assert(doit2([1]) == [1, 2, 3, 4]);
+}
+
+/***********************************************/
+
+int main()
+{
+ test1();
+ test2();
+ test3();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21424.d b/gcc/testsuite/gdc.test/runnable/test21424.d
new file mode 100644
index 00000000000..72abd3f33cf
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21424.d
@@ -0,0 +1,12 @@
+// https://issues.dlang.org/show_bug.cgi?id=21424
+
+void main()
+{
+ ubyte[10] buf;
+ size_t pos = 0;
+ size_t num = 5;
+ buf[pos++] += num;
+ assert(pos == 1);
+ assert(buf[0] == 5);
+ assert(buf[1] == 0);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21479.d b/gcc/testsuite/gdc.test/runnable/test21479.d
new file mode 100644
index 00000000000..84612ab49ea
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21479.d
@@ -0,0 +1,28 @@
+// https://issues.dlang.org/show_bug.cgi?id=21479
+enum Side
+{
+ left,
+ right
+}
+
+struct Both(T)
+{
+ T left;
+ T right;
+
+ ref T get(Side side)
+ {
+ return side == Side.left ? left : right;
+ }
+}
+
+void main()
+{
+ Both!(int[]) t;
+ t.get(Side.left) ~= 1;
+ assert (t.left.length == 1);
+
+ t.get(Side.right) ~= 1;
+ t.get(Side.right) ~= 2;
+ assert (t.right.length == 2);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21515.d b/gcc/testsuite/gdc.test/runnable/test21515.d
new file mode 100644
index 00000000000..09aea83ac11
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21515.d
@@ -0,0 +1,81 @@
+// https://issues.dlang.org/show_bug.cgi?id=21515
+// DISABLED: win32 win64
+
+// ABI layout of native complex
+struct _Complex(T) { T re; T im; }
+
+// Special enum definitions.
+version (Posix)
+{
+ align(float.alignof) enum __c_complex_float : _Complex!float;
+ align(double.alignof) enum __c_complex_double : _Complex!double;
+ align(real.alignof) enum __c_complex_real : _Complex!real;
+}
+else
+{
+ align(float.sizeof * 2) enum __c_complex_float : _Complex!float;
+ align(double.sizeof * 2) enum __c_complex_double : _Complex!double;
+ align(real.alignof) enum __c_complex_real : _Complex!real;
+}
+alias complex_float = __c_complex_float;
+alias complex_double = __c_complex_double;
+alias complex_real = __c_complex_real;
+
+extern(D) complex_float dcomplexf() { return typeof(return)(2, 1); }
+extern(D) complex_double dcomplex() { return typeof(return)(2, 1); }
+extern(D) complex_real dcomplexl() { return typeof(return)(2, 1); }
+
+extern(D) void dcomplexf(complex_float c) { assert(c.re == 2 && c.im == 1); }
+extern(D) void dcomplex(complex_double c) { assert(c.re == 2 && c.im == 1); }
+extern(D) void dcomplexl(complex_real c) { assert(c.re == 2 && c.im == 1); }
+
+extern(C) complex_float ccomplexf() { return typeof(return)(2, 1); }
+extern(C) complex_double ccomplex() { return typeof(return)(2, 1); }
+extern(C) complex_real ccomplexl() { return typeof(return)(2, 1); }
+
+extern(C) void ccomplexf2(complex_float c) { assert(c.re == 2 && c.im == 1); }
+extern(C) void ccomplex2(complex_double c) { assert(c.re == 2 && c.im == 1); }
+extern(C) void ccomplexl2(complex_real c) { assert(c.re == 2 && c.im == 1); }
+
+extern(C++) complex_float cpcomplexf() { return typeof(return)(2, 1); }
+extern(C++) complex_double cpcomplex() { return typeof(return)(2, 1); }
+extern(C++) complex_real cpcomplexl() { return typeof(return)(2, 1); }
+
+extern(C++) void cpcomplexf(complex_float c) { assert(c.re == 2 && c.im == 1); }
+extern(C++) void cpcomplex(complex_double c) { assert(c.re == 2 && c.im == 1); }
+extern(C++) void cpcomplexl(complex_real c) { assert(c.re == 2 && c.im == 1); }
+
+int main()
+{
+ auto a1 = dcomplexf();
+ auto b1 = dcomplex();
+ auto c1 = dcomplexl();
+ assert(a1.re == 2 && a1.im == 1);
+ assert(b1.re == 2 && b1.im == 1);
+ assert(c1.re == 2 && c1.im == 1);
+ dcomplexf(a1);
+ dcomplex(b1);
+ dcomplexl(c1);
+
+ auto a2 = ccomplexf();
+ auto b2 = ccomplex();
+ auto c2 = ccomplexl();
+ assert(a2.re == 2 && a2.im == 1);
+ assert(b2.re == 2 && b2.im == 1);
+ assert(c2.re == 2 && c2.im == 1);
+ ccomplexf2(a2);
+ ccomplex2(b2);
+ ccomplexl2(c2);
+
+ auto a3 = cpcomplexf();
+ auto b3 = cpcomplex();
+ auto c3 = cpcomplexl();
+ assert(a3.re == 2 && a3.im == 1);
+ assert(b3.re == 2 && b3.im == 1);
+ assert(c3.re == 2 && c3.im == 1);
+ cpcomplexf(a3);
+ cpcomplex(b3);
+ cpcomplexl(c3);
+
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21586.d b/gcc/testsuite/gdc.test/runnable/test21586.d
new file mode 100644
index 00000000000..6af695c759d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21586.d
@@ -0,0 +1,31 @@
+/*
+RUN_OUTPUT:
+---
+this
+~this 1
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=21586
+
+import core.stdc.stdio : printf;
+
+struct S
+{
+ this(int arg)
+ {
+ a = arg;
+ printf("this\n");
+ }
+
+ ~this()
+ {
+ printf("~this %d\n", a);
+ }
+
+ int a;
+}
+
+void main()
+{
+ auto s = true ? S(1) : S(0);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21822.d b/gcc/testsuite/gdc.test/runnable/test21822.d
new file mode 100644
index 00000000000..f0a020ade82
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21822.d
@@ -0,0 +1,18 @@
+// https://issues.dlang.org/show_bug.cgi?id=21822
+
+bool testAliasedString()
+{
+ auto a = soundexer();
+ auto b = soundexer();
+ return a == b;
+}
+
+char[4] soundexer()
+{
+ return "M365";
+}
+
+void main()
+{
+ assert(testAliasedString());
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21833.d b/gcc/testsuite/gdc.test/runnable/test21833.d
new file mode 100644
index 00000000000..dd59a8e56b6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test21833.d
@@ -0,0 +1,30 @@
+// REQUIRED_ARGS: -O -inline
+// https://issues.dlang.org/show_bug.cgi?id=20855
+
+void testit()
+{
+ pragma(inline, false);
+ short[4] arr = [-1, 6, 0, 4];
+ long1 A = *cast(long1*)(arr.ptr);
+ assert(_mm_extract_pi16(A, 0) == 65535);
+}
+
+struct short4
+{
+ short[4] array;
+}
+
+struct long1
+{
+ long[1] array;
+}
+
+int _mm_extract_pi16 (long1 a, int imm8)
+{
+ return cast(ushort)((cast(short4)a).array[imm8]);
+}
+
+void main()
+{
+ testit();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test22.d b/gcc/testsuite/gdc.test/runnable/test22.d
index 2aabb4d95d1..1d806d641ab 100644
--- a/gcc/testsuite/gdc.test/runnable/test22.d
+++ b/gcc/testsuite/gdc.test/runnable/test22.d
@@ -1,7 +1,5 @@
-// RUNNABLE_PHOBOS_TEST
// REQUIRED_ARGS:
-import std.math: poly;
import core.stdc.stdarg;
extern(C)
@@ -119,67 +117,6 @@ void test5()
/*************************************/
-void test6()
-{
- ireal a = 6.5i % 3i;
- printf("%Lfi %Lfi\n", a, a - .5i);
- assert(a == .5i);
-
- a = 6.5i % 3;
- printf("%Lfi %Lfi\n", a, a - .5i);
- assert(a == .5i);
-
- real b = 6.5 % 3i;
- printf("%Lf %Lf\n", b, b - .5);
- assert(b == .5);
-
- b = 6.5 % 3;
- printf("%Lf %Lf\n", b, b - .5);
- assert(b == .5);
-}
-
-/*************************************/
-
-void test7()
-{
- cfloat f = 1+0i;
- f %= 2fi;
- printf("%f + %fi\n", f.re, f.im);
- assert(f == 1 + 0i);
-
- cdouble d = 1+0i;
- d %= 2i;
- printf("%f + %fi\n", d.re, d.im);
- assert(d == 1 + 0i);
-
- creal r = 1+0i;
- r %= 2i;
- printf("%Lf + %Lfi\n", r.re, r.im);
- assert(r == 1 + 0i);
-}
-
-/*************************************/
-
-void test8()
-{
- cfloat f = 1+0i;
- f %= 2i;
- printf("%f + %fi\n", f.re, f.im);
- assert(f == 1);
-
- cdouble d = 1+0i;
- d = d % 2i;
- printf("%f + %fi\n", d.re, d.im);
- assert(d == 1);
-
- creal r = 1+0i;
- r = r % 2i;
- printf("%Lf + %Lfi\n", r.re, r.im);
- assert(r == 1);
-}
-
-/*************************************/
-
class A9
{
this(int[] params ...)
@@ -219,7 +156,7 @@ void test10()
{
auto i = 5u;
auto s = typeid(typeof(i)).toString;
- printf("%.*s\n", s.length, s.ptr);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
assert(typeid(typeof(i)) == typeid(uint));
}
@@ -233,103 +170,6 @@ void test11()
/*************************************/
-void assertEqual(real* a, real* b, string file = __FILE__, size_t line = __LINE__)
-{
- auto x = cast(ubyte*)a;
- auto y = cast(ubyte*)b;
-
- // Only compare the 10 value bytes, the padding bytes are of undefined
- // value.
- version (X86) enum count = 10;
- else version (X86_64) enum count = 10;
- else enum count = real.sizeof;
- for (size_t i = 0; i < count; i++)
- {
- if (x[i] != y[i])
- {
- printf("%02d: %02x %02x\n", i, x[i], y[i]);
- import core.exception;
- throw new AssertError(file, line);
- }
- }
-}
-
-void assertEqual(creal* a, creal* b, string file = __FILE__, size_t line = __LINE__)
-{
- assertEqual(cast(real*)a, cast(real*)b, file, line);
- assertEqual(cast(real*)a + 1, cast(real*)b + 1, file, line);
-}
-
-void test12()
-{
- creal a = creal.nan;
- creal b = real.nan + ireal.nan;
- assertEqual(&a, &b);
-
- real c= real.nan;
- real d=a.re;
- assertEqual(&c, &d);
-
- d=a.im;
- assertEqual(&c, &d);
-}
-
-/*************************************/
-
-void test13()
-{
- creal a = creal.infinity;
- creal b = real.infinity + ireal.infinity;
- assertEqual(&a, &b);
-
- real c = real.infinity;
- real d=a.re;
- assertEqual(&c, &d);
-
- d=a.im;
- assertEqual(&c, &d);
-}
-
-/*************************************/
-
-void test14()
-{
- creal a = creal.nan;
- creal b = creal.nan;
- b = real.nan + ireal.nan;
- assertEqual(&a, &b);
-
- real c = real.nan;
- real d=a.re;
- assertEqual(&c, &d);
-
- d=a.im;
- assertEqual(&c, &d);
-}
-
-/*************************************/
-
-ireal x15;
-
-void foo15()
-{
- x15 = -x15;
-}
-
-void bar15()
-{
- return foo15();
-}
-
-void test15()
-{
- x15=2i;
- bar15();
- assert(x15==-2i);
-}
-
-/*************************************/
-
real x16;
void foo16()
@@ -358,7 +198,7 @@ class Bar17
class Foo17
{
- void opAdd (Bar17 b) {}
+ void opBinary(string op : "+") (Bar17 b) {}
}
void test17()
@@ -427,41 +267,6 @@ void test21()
/*************************************/
-void test22()
-{
- static creal[] params = [1+0i, 3+0i, 5+0i];
-
- printf("params[0] = %Lf + %Lfi\n", params[0].re, params[0].im);
- printf("params[1] = %Lf + %Lfi\n", params[1].re, params[1].im);
- printf("params[2] = %Lf + %Lfi\n", params[2].re, params[2].im);
-
- creal[] sums = new creal[3];
- sums[] = 0+0i;
-
- foreach(creal d; params)
- {
- creal prod = d;
-
- printf("prod = %Lf + %Lfi\n", prod.re, prod.im);
- for(int i; i<2; i++)
- {
- sums[i] += prod;
- prod *= d;
- }
- sums[2] += prod;
- }
-
- printf("sums[0] = %Lf + %Lfi", sums[0].re, sums[0].im);
- assert(sums[0].re==9);
- assert(sums[0].im==0);
- assert(sums[1].re==35);
- assert(sums[1].im==0);
- assert(sums[2].re==153);
- assert(sums[2].im==0);
-}
-
-/*************************************/
-
const int c23 = b23 * b23;
const int a23 = 1;
const int b23 = a23 * 3;
@@ -480,51 +285,6 @@ void test23()
/*************************************/
-ifloat func_24_1(ifloat f, double d)
-{
-// f /= cast(cdouble)d;
- return f;
-}
-
-ifloat func_24_2(ifloat f, double d)
-{
- f = cast(ifloat)(f / cast(cdouble)d);
- return f;
-}
-
-float func_24_3(float f, double d)
-{
-// f /= cast(cdouble)d;
- return f;
-}
-
-float func_24_4(float f, double d)
-{
- f = cast(float)(f / cast(cdouble)d);
- return f;
-}
-
-void test24()
-{
- ifloat f = func_24_1(10i, 8);
- printf("%fi\n", f);
-// assert(f == 1.25i);
-
- f = func_24_2(10i, 8);
- printf("%fi\n", f);
- assert(f == 1.25i);
-
- float g = func_24_3(10, 8);
- printf("%f\n", g);
-// assert(g == 1.25);
-
- g = func_24_4(10, 8);
- printf("%f\n", g);
- assert(g == 1.25);
-}
-
-/*************************************/
-
template cat(int n)
{
const int dog = n;
@@ -541,48 +301,11 @@ void test25()
/*************************************/
-string toString26(cdouble z)
-{
- char[ulong.sizeof*8] buf;
-
- auto len = snprintf(buf.ptr, buf.sizeof, "%f+%fi", z.re, z.im);
- return buf[0 .. len].idup;
-}
-
-void test26()
-{
- static cdouble[] A = [1+0i, 0+1i, 1+1i];
- string s;
-
- foreach( cdouble z; A )
- {
- s = toString26(z);
- printf("%.*s ", s.length, s.ptr);
- }
- printf("\n");
-
- for(int ii=0; ii<A.length; ii++ )
- A[ii] += -1i*A[ii];
-
- assert(A[0] == 1 - 1i);
- assert(A[1] == 1 + 1i);
- assert(A[2] == 2);
-
- foreach( cdouble z; A )
- {
- s = toString26(z);
- printf("%.*s ", s.length, s.ptr);
- }
- printf("\n");
-}
-
-/*************************************/
-
void test27()
{ int x;
string s = (int*function(int ...)[]).mangleof;
- printf("%.*s\n", s.length, s.ptr);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
assert((int*function(int ...)[]).mangleof == "APFiXPi");
assert(typeof(x).mangleof == "i");
assert(x.mangleof == "_D6test226test27FZ1xi");
@@ -590,20 +313,12 @@ void test27()
/*************************************/
-void test28()
-{
- alias cdouble X;
- X four = cast(X) (4.0i + 0.4);
-}
-
-/*************************************/
-
void test29()
{
ulong a = 10_000_000_000_000_000,
b = 1_000_000_000_000_000;
- printf("test29\n%lx\n%lx\n%lx\n", a, b, a / b);
+ printf("test29\n%llx\n%llx\n%llx\n", a, b, a / b);
assert((a / b) == 10);
}
@@ -666,7 +381,7 @@ struct particle
float yg; /* Y Gravity */
}
-particle particles[10000];
+particle[10000] particles;
void test32()
{
@@ -923,7 +638,7 @@ in
{
assert(A.length > 0);
}
-body
+do
{
version (D_InlineAsm_X86)
{
@@ -1086,7 +801,7 @@ in
{
assert(A.length > 0);
}
-body
+do
{
ptrdiff_t i = A.length - 1;
real r = A[i];
@@ -1101,19 +816,17 @@ body
void test47()
{
real x = 3.1;
- static real pp[] = [56.1, 32.7, 6];
+ static real[] pp = [56.1, 32.7, 6];
real r;
printf("The result should be %Lf\n",(56.1L + (32.7L + 6L * x) * x));
printf("The C version outputs %Lf\n", poly_c(x, pp));
printf("The asm version outputs %Lf\n", poly_asm(x, pp));
- printf("The std.math version outputs %Lf\n", poly(x, pp));
r = (56.1L + (32.7L + 6L * x) * x);
assert(r == poly_c(x, pp));
version (D_InlineAsm_X86)
assert(r == poly_asm(x, pp));
- assert(r == poly(x, pp));
}
/*************************************/
@@ -1211,28 +924,36 @@ void test52()
}
/*************************************/
-import std.stdio;
-import core.stdc.stdarg;
void myfunc(int a1, ...) {
va_list argument_list;
TypeInfo argument_type;
string sa; int ia; double da;
- writefln("%d variable arguments", _arguments.length);
- writefln("argument types %s", _arguments);
+ assert(_arguments.length == 9);
+
va_start(argument_list, a1);
for (int i = 0; i < _arguments.length; ) {
if ((argument_type=_arguments[i++]) == typeid(string)) {
va_arg(argument_list, sa);
- writefln("%d) string arg = '%s', length %d", i+1, sa.length<=20? sa : "?", sa.length);
+ switch (i)
+ {
+ case 1: assert(sa == "2"); break;
+ case 7: assert(sa == "8"); break;
+ case 8: assert(sa == "9"); break;
+ case 9: assert(sa == "10"); break;
+ default:
+ printf("i = %d\n", i);
+ assert(false);
+ }
} else if (argument_type == typeid(int)) {
va_arg(argument_list, ia);
- writefln("%d) int arg = %d", i+1, ia);
+ assert(ia == i+1);
} else if (argument_type == typeid(double)) {
va_arg(argument_list, da);
- writefln("%d) double arg = %f", i+1, da);
+ const e = i+1;
+ assert((e - 0.0001) < da && da < (e + 0.0001));
} else {
- throw new Exception("invalid argument type");
+ assert(false, argument_type.toString());
}
}
va_end(argument_list);
@@ -1248,6 +969,24 @@ void test6758() {
/*************************************/
+real f18573() { return 1; }
+
+void test18573()
+{
+ cast(void) f18573();
+ cast(void) f18573();
+ cast(void) f18573();
+ cast(void) f18573();
+ cast(void) f18573();
+ cast(void) f18573();
+ cast(void) f18573();
+
+ real b = 2;
+ assert(b == 2); /* fails; should pass */
+}
+
+/*************************************/
+
int main()
{
test1();
@@ -1255,29 +994,18 @@ int main()
test3();
test4();
test5();
- test6();
- test7();
- test8();
test9();
test10();
test11();
- test12();
- test13();
- test14();
- test15();
test16();
test17();
test18();
test19();
test20();
test21();
- test22();
test23();
- test24();
test25();
- test26();
test27();
- test28();
test29();
test30();
test31();
@@ -1305,6 +1033,7 @@ int main()
test51();
test52();
test6758();
+ test18573();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/test22209.d b/gcc/testsuite/gdc.test/runnable/test22209.d
new file mode 100644
index 00000000000..f2a4bd54a40
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test22209.d
@@ -0,0 +1,21 @@
+// https://issues.dlang.org/show_bug.cgi?id=22209
+
+struct U { size_t[4] a; }
+
+struct S
+{
+ int x;
+ U u;
+ alias u this;
+}
+
+U foo()
+{
+ S s = S(42, U([1, 2, 3, 4]));
+ return s;
+}
+
+void main()
+{
+ assert(foo().a[0] == 1);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test23.d b/gcc/testsuite/gdc.test/runnable/test23.d
index abf8e37f83d..f1fd4a6c2bc 100644
--- a/gcc/testsuite/gdc.test/runnable/test23.d
+++ b/gcc/testsuite/gdc.test/runnable/test23.d
@@ -1,14 +1,8 @@
-// RUNNABLE_PHOBOS_TEST
-// REQUIRED_ARGS:
-
module test;
import core.vararg;
import core.stdc.stdlib;
-import std.stdio;
-import std.string;
-import core.stdc.stdlib;
-
+import core.stdc.stdio;
/*******************************************/
@@ -69,13 +63,6 @@ void test3()
/*******************************************/
-void test4()
-{
- writeln("",true);
-}
-
-/*******************************************/
-
void test5()
{
int[] qwert = new int[6];
@@ -228,9 +215,9 @@ void test11()
{
Foo11 fooIt = new Foo11();
if (fooIt !is null)
- writefln("fooIt should be valid");
+ printf("fooIt should be valid\n");
fooIt.f.foo();
- writefln("it worked");
+ printf("it worked\n");
}
/*******************************************/
@@ -251,7 +238,7 @@ struct B12 {
void test12()
{
A12 a;
- printf("%d\n", A12.sizeof);
+ printf("%zd\n", A12.sizeof);
assert(A12.sizeof == 12);
}
@@ -272,7 +259,7 @@ interface Father {}
class Mother {
Father test() {
- writefln("Called Mother.test!");
+ printf("Called Mother.test!\n");
return new Child(42);
}
}
@@ -283,7 +270,7 @@ class Child : Mother, Father {
this(int d) { data = d; }
override Child test() {
- writefln("Called Child.test!");
+ printf("Called Child.test!\n");
return new Child(69);
}
}
@@ -294,7 +281,7 @@ void test14()
Mother childsMum = aChild;
Child childsChild = aChild.test();
Child mumsChild = cast(Child) childsMum.test();
- writefln("Success2");
+ printf("Success2\n");
}
@@ -335,27 +322,6 @@ void test15()
/*******************************************/
-creal x16;
-
-void foo16()
-{
- x16 = -x16;
-}
-
-void bar16()
-{
- return foo16();
-}
-
-void test16()
-{
- x16 = 2.0L + 0.0Li;
- bar16();
- assert(x16 == -2.0L + 0.0Li);
-}
-
-/*******************************************/
-
void test17()
{
version(D_InlineAsm_X86_64)
@@ -557,15 +523,12 @@ void test25()
char[6] cstr = "123456"c;
auto str1 = cast(wchar[3])(cstr);
- writefln("str1: ", (cast(char[])str1).length , " : ", (cast(char[])str1));
assert(cast(char[])str1 == "123456"c);
auto str2 = cast(wchar[3])("789abc"c);
- writefln("str2: ", (cast(char[])str2).length , " : ", (cast(char[])str2));
assert(cast(char[])str2 == "789abc"c);
auto str3 = cast(wchar[3])("defghi");
- writefln("str3: ", (cast(char[])str3).length , " : ", (cast(char[])str3));
version (LittleEndian)
assert(cast(char[])str3 == "d\000e\000f\000"c);
version (BigEndian)
@@ -631,26 +594,20 @@ uint intRes()
void test28()
{
- auto s = std.string.format("%s", "abc123"[intRes() % $] );
- writefln( "%s", s );
- assert(s == "2");
+ auto s = "abc123"[intRes() % $];
+ assert(s == '2');
static const char[] foo = "abc123";
- s = std.string.format("%s", foo[intRes() % $] );
- assert(s == "2");
-
+ assert(foo[intRes() % $] == '2');
static string bar = "abc123";
- s = std.string.format("%s", bar[intRes() % $] );
- assert(s == "2");
+ assert(bar[intRes() % $] == '2');
const char[] abc = "abc123";
- s = std.string.format("%s", abc[intRes() % $] );
- assert(s == "2");
+ assert(abc[intRes() % $] == '2');
string def = "abc123";
- s = std.string.format("%s", def[intRes() % $] );
- assert(s == "2");
+ assert(def[intRes() % $] == '2');
}
/*******************************************/
@@ -703,72 +660,6 @@ void test31()
/*******************************************/
-class Foo32
-{
- static void* ps;
-
- new (size_t sz)
- {
- void* p = core.stdc.stdlib.malloc(sz);
- printf("new(sz = %d) = %p\n", sz, p);
- ps = p;
- return p;
- }
-
- delete(void* p)
- {
- printf("delete(p = %p)\n", p);
- assert(p == ps);
- if (p) core.stdc.stdlib.free(p);
- }
-}
-
-void test32()
-{
- Foo32 f = new Foo32;
- delete f;
-}
-
-/*******************************************/
-
-class Foo33
-{
-// this() { printf("this()\n"); }
-// ~this() { printf("~this()\n"); }
-
- static void* ps;
- static int del;
-
- new (size_t sz, int i)
- {
- void* p = core.stdc.stdlib.malloc(sz);
- printf("new(sz = %d) = %p\n", sz, p);
- ps = p;
- return p;
- }
-
- delete(void* p)
- {
- printf("delete(p = %p)\n", p);
- assert(p == ps);
- if (p) core.stdc.stdlib.free(p);
- del += 1;
- }
-}
-
-void foo33()
-{
- scope Foo33 f = new(3) Foo33;
-}
-
-void test33()
-{
- foo33();
- assert(Foo33.del == 1);
-}
-
-/*******************************************/
-
struct o_O { int a; }
union O_O { int a; }
class O_o { int a; }
@@ -806,12 +697,6 @@ void test37()
{
Foo37 f = new Foo37();
- writefln("Foo.array[0] = %s", f.array[0] );
- writefln("Foo.array[1] = %s", f.array[1] );
- writefln("Foo.array[2] = %s", f.array[2] );
- writefln("Foo.array[3] = %s", f.array[3] );
- writefln("Foo.count = %s", f.count );
-
assert(f.array[0] == 1.0);
assert(f.array[1] == 1.0);
assert(f.array[2] == 1.0);
@@ -831,7 +716,7 @@ in
checkParameters();
}
-body
+do
{
}
@@ -848,7 +733,7 @@ void delegate() foo39()
void dg()
{
- writefln("delegate!");
+ printf("delegate!\n");
assert(a == 3);
}
}).dg;
@@ -1019,7 +904,8 @@ void test45()
buffer[i] -= cast(char)'a' - 'A'; // segfault here
}
}
- writeln(buffer);
+
+ assert(buffer == "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
/*******************************************/
@@ -1189,10 +1075,10 @@ bool[void[]] reg57;
void addToReg57(const(void)[] a, int b, bool v)
{
if (!v)
- writefln("X");
+ printf("X\n");
auto key = a~(cast(void*)&b)[0..4];
reg57[cast(immutable(void)[])key] = v;
- writefln("OK");
+ printf("OK\n");
}
void test57()
@@ -1232,10 +1118,9 @@ struct S59
void test59()
{ S59 s;
- writefln("s = %s", s);
string p;
- p = std.string.format("s = %s", s);
+ p = "s = " ~ s.toString();
assert(p == "s = foo");
}
@@ -1263,11 +1148,13 @@ void test60()
void test61()
{
int[][] f = [[1,2],[3,4]];
+ assert(f.length == 2);
+ assert(f[0].length == 2);
+ assert(f[1].length == 2);
assert(f[0][0] == 1);
assert(f[0][1] == 2);
assert(f[1][0] == 3);
assert(f[1][1] == 4);
- writeln(f);
}
/*******************************************/
@@ -1304,7 +1191,7 @@ void test63()
arr = [1] ~ 2;
// runtime crash, the length == 1
- printf("%d\n", arr.length);
+ printf("%zd\n", arr.length);
assert (arr.length == 2);
assert(arr[0] == 1);
assert(arr[1] == 2);
@@ -1333,7 +1220,6 @@ void test64()
foreach_reverse(v; x)
{
- writeln(v);
assert(j == v);
j--;
}
@@ -1342,7 +1228,6 @@ void test64()
j = 4;
foreach_reverse(i, v; x)
{
- writefln("[%s] = %s", i, v);
assert(i + 1 == j);
assert(j == v);
j--;
@@ -1411,11 +1296,11 @@ void test69()
auto n = new NoBug;
auto n2 = new NoBug2;
- writefln("bug %d", b.t);
+ printf("bug %d\n", b.t);
assert(b.t == 1);
- writefln("nobug %d", n.t);
+ printf("nobug %d\n", n.t);
assert(n.t == 2);
- writefln("nobug2 %d", n2.t);
+ printf("nobug2 %d\n", n2.t);
assert(n2.t == 3);
}
@@ -1428,8 +1313,6 @@ void test70()
}
static const char[0] altsep;
- string s = std.string.format("test%spath", altsep);
- assert(s == "testpath");
foo(altsep);
}
@@ -1502,7 +1385,6 @@ void main()
test1();
test2();
test3();
- test4();
test5();
test6();
test7();
@@ -1514,7 +1396,6 @@ void main()
test13();
test14();
test15();
- test16();
test17();
test18();
test19();
@@ -1530,8 +1411,8 @@ void main()
test29();
test30();
test31();
- test32();
- test33();
+
+
test34();
test37();
test38();
diff --git a/gcc/testsuite/gdc.test/runnable/test24.d b/gcc/testsuite/gdc.test/runnable/test24.d
index 2f31d757921..d41d0602cb6 100644
--- a/gcc/testsuite/gdc.test/runnable/test24.d
+++ b/gcc/testsuite/gdc.test/runnable/test24.d
@@ -1,5 +1,5 @@
-// RUNNABLE_PHOBOS_TEST
// EXTRA_SOURCES: imports/test24a.d imports/test24b.d
+// EXTRA_FILES: imports/test24c.d
// PERMUTE_ARGS:
// REQUIRED_ARGS:
@@ -7,5 +7,5 @@ import imports.test24a, imports.test24b;
void main()
{
- string hi = std.string.format("%s", 3);
+ string hi = imports.test24c.format("%s", 3);
}
diff --git a/gcc/testsuite/gdc.test/runnable/test27.d b/gcc/testsuite/gdc.test/runnable/test27.d
index a3e76ea8d62..b45c132620e 100644
--- a/gcc/testsuite/gdc.test/runnable/test27.d
+++ b/gcc/testsuite/gdc.test/runnable/test27.d
@@ -1,4 +1,3 @@
-// RUNNABLE_PHOBOS_TEST
// COMPILE_SEPARATELY
// EXTRA_SOURCES: imports/test27a.d
// PERMUTE_ARGS:
diff --git a/gcc/testsuite/gdc.test/runnable/test28.d b/gcc/testsuite/gdc.test/runnable/test28.d
index 5355c2cb23a..8fb60b9cc39 100644
--- a/gcc/testsuite/gdc.test/runnable/test28.d
+++ b/gcc/testsuite/gdc.test/runnable/test28.d
@@ -1,11 +1,7 @@
-// RUNNABLE_PHOBOS_TEST
module test;
+import core.stdc.stdio;
import core.vararg;
-import std.stdio;
-import std.string;
-
-extern(C) int printf(const char*, ...);
/*******************************************/
@@ -117,41 +113,24 @@ void test8()
{
int[] test;
test.length = 10;
- // Show address of array start and its length (10)
- writefln("%s %s", cast(uint)test.ptr, test.length);
+ assert(test.length == 10);
+ assert(test.ptr != null);
test.length = 1;
- // Show address of array start and its length (1)
- writefln("%s %s", cast(uint)test.ptr, test.length);
+ assert(test.length == 1);
+ assert(test.ptr != null);
test.length = 8;
- // Show address of array start and its length (8)
- writefln("%s %s", cast(uint)test.ptr, test.length);
+ assert(test.length == 8);
+ assert(test.ptr != null);
test.length = 0;
- // Shows 0 and 0!
- writefln("%s %s", cast(uint)test.ptr, test.length);
assert(test.length == 0);
assert(test.ptr != null);
}
/*******************************************/
-cdouble y9;
-
-cdouble f9(cdouble x)
-{
- return (y9 = x);
-}
-
-void test9()
-{
- f9(1.0+2.0i);
- assert(y9 == 1.0+2.0i);
-}
-
-/*******************************************/
-
class CBase10
{
this() { }
@@ -227,10 +206,10 @@ void test14()
void func15(...)
in {
- writefln("Arguments len = %d\n", _arguments.length);
+ printf("Arguments len = %d\n", cast(int)_arguments.length);
assert(_arguments.length == 2);
}
-body {
+do {
}
@@ -402,11 +381,9 @@ void test22()
void test23()
{
auto t=['a','b','c','d'];
- writeln(typeid(typeof(t)));
assert(is(typeof(t) == char[]));
const t2=['a','b','c','d','e'];
- writeln(typeid(typeof(t2)));
assert(is(typeof(t2) == const(const(char)[])));
}
@@ -424,16 +401,6 @@ void test24()
/*******************************************/
-void test25()
-{
- ireal x = 4.0Li;
- ireal y = 4.0Li;
- ireal z = 4Li;
- creal c = 4L + 0Li;
-}
-
-/*******************************************/
-
struct Foo26
{
int a;
@@ -688,7 +655,6 @@ template a34(string name, T...)
string localchar;
foreach (a34; T)
{
- writefln(`hello`);
localchar ~= a34.mangleof;
}
return localchar;
@@ -697,7 +663,7 @@ template a34(string name, T...)
void test34()
{
- writeln(a34!("Adf"[], typeof("adf"),uint)("Adf"[],"adf",1234));
+ assert(a34!("Adf"[], typeof("adf"),uint)("Adf"[],"adf",1234) == "Ayak");
}
/*******************************************/
@@ -722,7 +688,6 @@ template a36(AnotherT,string name,T...){
AnotherT localchar;
foreach(a;T)
{
- writefln(`hello`);
localchar~=a.mangleof;
}
return cast(AnotherT)localchar;
@@ -734,7 +699,7 @@ void test36()
string b="adf";
uint i=123;
char[3] c="Adf";
- writeln(a36!(typeof(b),"Adf")());
+ assert(a36!(typeof(b),"Adf")() == "");
}
/*******************************************/
@@ -780,8 +745,7 @@ void test39()
{
void print(string[] strs)
{
- writeln(strs);
- assert(format("%s", strs) == `["Matt", "Andrew"]`);
+ assert(strs == ["Matt", "Andrew"]);
}
print(["Matt", "Andrew"]);
@@ -803,7 +767,7 @@ void test40()
with (c.propName)
{
- writeln(toString());
+ assert(toString() == "test.test40.C");
}
auto foo = c.propName;
@@ -829,7 +793,7 @@ void test41()
assert(x3 == 1);
//test [0 .. 2] = 'b'; // this line should assert
- writef ("%s\n", test.ptr [0 .. 2]);
+ assert(test.ptr[0 .. 2] == "ba");
}
/*******************************************/
@@ -846,6 +810,12 @@ void test42()
/*******************************************/
+void vararg43(string fmt, ...)
+{
+ assert(_arguments[0] is typeid(const string));
+ assert(va_arg!(const string)(_argptr) == "hello");
+}
+
struct A43
{
static const MY_CONST_STRING = "hello";
@@ -854,7 +824,7 @@ struct A43
{
// This will either print garbage or throw a UTF exception.
// But if never_called() is commented out, then it will work.
- writefln("%s", MY_CONST_STRING);
+ vararg43("%s", MY_CONST_STRING);
}
}
@@ -862,7 +832,7 @@ void never_called43()
{
// This can be anything; there just needs to be a reference to
// A43.MY_CONST_STRING somewhere.
- writefln("%s", A43.MY_CONST_STRING);
+ vararg43("%s", A43.MY_CONST_STRING);
}
void test43()
@@ -873,6 +843,12 @@ void test43()
/*******************************************/
+void vararg44(string fmt, ...)
+{
+ assert(_arguments[0] is typeid(const string));
+ assert(va_arg!(const string)(_argptr) == "hello");
+}
+
class A44
{
static const MY_CONST_STRING = "hello";
@@ -881,7 +857,7 @@ class A44
{
// This will either print garbage or throw a UTF exception.
// But if never_called() is commented out, then it will work.
- writefln("%s", MY_CONST_STRING);
+ vararg44("%s", MY_CONST_STRING);
}
}
@@ -889,7 +865,7 @@ void never_called44()
{
// This can be anything; there just needs to be a reference to
// A44.MY_CONST_STRING somewhere.
- writefln("%s", A44.MY_CONST_STRING);
+ vararg44("%s", A44.MY_CONST_STRING);
}
void test44()
@@ -941,9 +917,9 @@ void test47()
void test48()
{
Object o = new Object();
- printf("%.*s\n", typeof(o).classinfo.name.length, typeof(o).classinfo.name.ptr);
- printf("%.*s\n", (typeof(o)).classinfo.name.length, (typeof(o)).classinfo.name.ptr);
- printf("%.*s\n", (Object).classinfo.name.length, (Object).classinfo.name.ptr);
+ printf("%.*s\n", cast(int)typeof(o).classinfo.name.length, typeof(o).classinfo.name.ptr);
+ printf("%.*s\n", cast(int)(typeof(o)).classinfo.name.length, (typeof(o)).classinfo.name.ptr);
+ printf("%.*s\n", cast(int)(Object).classinfo.name.length, (Object).classinfo.name.ptr);
}
/*******************************************/
@@ -1076,13 +1052,11 @@ void test58()
static S a = {i: 1};
static S b;
- writefln("a.bar: %s, %s", a.bar, a.abc);
assert(a.i == 1);
assert(a.bar[0] == 4);
assert(a.bar[1] == 4);
assert(a.bar[2] == 4);
assert(a.bar[3] == 4);
- writefln("b.bar: %s, %s", b.bar, b.abc);
assert(b.i == 0);
assert(b.bar[0] == 4);
assert(b.bar[1] == 4);
@@ -1093,10 +1067,18 @@ void test58()
/*******************************************/
+void vararg59(...)
+{
+ if (_arguments[0] is typeid(size_t))
+ assert(va_arg!size_t(_argptr) == 454);
+ else
+ assert(va_arg!string(_argptr) == "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234");
+}
+
void bug59(string s)()
{
- writeln(s);
- writeln(s.length);
+ vararg59(s);
+ vararg59(s.length);
}
void test59()
@@ -1139,7 +1121,6 @@ void test62()
Vector62 a;
a.set(1,2,24);
a = a * 2;
- writeln(a.x, a.y, a.z);
assert(a.x == 2);
assert(a.y == 4);
assert(a.z == 48);
@@ -1158,7 +1139,7 @@ struct Vector62
z = _z;
}
- Vector62 opMul(float s)
+ Vector62 opBinary(string op : "*")(float s)
{
Vector62 ret;
ret.x = x*s;
@@ -1191,8 +1172,6 @@ void test63()
{
Data63 d; d.x = 1; d.y = 2;
d = frob(d);
- writeln(d.x);
- writeln(d.y);
assert(d.x == 3 && d.y == -1);
}
@@ -1200,8 +1179,8 @@ void test63()
class Foo64
{
- this() { writefln("Foo64 created"); }
- ~this() { writefln("Foo64 destroyed"); }
+ this() { }
+ ~this() { }
}
template Mix64()
@@ -1249,6 +1228,49 @@ void test65()
/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18576
+
+void delegate() callback;
+
+struct S66 {
+ int x;
+ @disable this(this);
+
+ this(int x) {
+ this.x = x;
+ callback = &inc;
+ }
+ void inc() {
+ x++;
+ }
+}
+
+auto f66()
+{
+ return g66(); // RVO should be done
+}
+
+auto g66()
+{
+ return h66(); // RVO should be done
+}
+
+auto h66()
+{
+ return S66(100);
+}
+
+void test18576()
+{
+ auto s = f66();
+ printf("%p vs %p\n", &s, callback.ptr);
+ callback();
+ printf("s.x = %d\n", s.x);
+ assert(s.x == 101);
+ assert(&s == callback.ptr);
+}
+
+/*******************************************/
void main()
{
@@ -1261,7 +1283,6 @@ void main()
test6();
test7();
test8();
- test9();
test10();
test11();
test12();
@@ -1276,7 +1297,6 @@ void main()
test22();
test23();
test24();
- test25();
test26();
test27();
test28();
@@ -1315,6 +1335,7 @@ void main()
test63();
test64();
test65();
+ test18576();
printf("Success\n");
}
diff --git a/gcc/testsuite/gdc.test/runnable/test29.d b/gcc/testsuite/gdc.test/runnable/test29.d
index 9156c91923b..03c81c65877 100644
--- a/gcc/testsuite/gdc.test/runnable/test29.d
+++ b/gcc/testsuite/gdc.test/runnable/test29.d
@@ -1,6 +1,12 @@
-// COMPILE_SEPARATELY
-// EXTRA_SOURCES: imports/test29a.d imports/test29b.d
-// PERMUTE_ARGS:
+/*
+COMPILE_SEPARATELY
+EXTRA_SOURCES: imports/test29a.d imports/test29b.d
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+42
+---
+*/
import imports.test29a;
import imports.test29b;
@@ -10,4 +16,3 @@ extern(C) int printf(const char*, ...);
void main() {
printf("%d\n", qwert);
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/test3.d b/gcc/testsuite/gdc.test/runnable/test3.d
index 11f66c68e6f..4c6b9deaa47 100644
--- a/gcc/testsuite/gdc.test/runnable/test3.d
+++ b/gcc/testsuite/gdc.test/runnable/test3.d
@@ -34,7 +34,7 @@ int main(string[] args)
a.bar = "hello";
a.bar = baz ~ "betty";
- printf("a.bar = '%.*s'\n", a.bar.length, a.bar.ptr);
+ printf("a.bar = '%.*s'\n", cast(int)a.bar.length, a.bar.ptr);
assert(a.bar == "lolobetty");
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/test30.d b/gcc/testsuite/gdc.test/runnable/test30.d
index 085311a585f..384ff4cb92c 100644
--- a/gcc/testsuite/gdc.test/runnable/test30.d
+++ b/gcc/testsuite/gdc.test/runnable/test30.d
@@ -1,4 +1,4 @@
-// 444
+// https://issues.dlang.org/show_bug.cgi?id=444
int main()
{
diff --git a/gcc/testsuite/gdc.test/runnable/test34.d b/gcc/testsuite/gdc.test/runnable/test34.d
index e38ab1c858d..dee6609e3d7 100644
--- a/gcc/testsuite/gdc.test/runnable/test34.d
+++ b/gcc/testsuite/gdc.test/runnable/test34.d
@@ -1,18 +1,8 @@
-// RUNNABLE_PHOBOS_TEST
-/*
-TEST_OUTPUT:
----
-Object
----
-*/
-
module test34;
-import std.stdio;
-import std.string;
-import std.format;
import core.exception;
-
+import core.stdc.stdio;
+import core.vararg;
/************************************************/
@@ -26,16 +16,12 @@ void test1()
auto hfoo = ti_foo.toHash();
auto hbar = ti_bar.toHash();
- writefln("typeid(Foo).toHash: ", hfoo);
- writefln("typeid(Bar).toHash: ", hbar);
assert(hfoo != hbar);
auto e = (ti_foo == ti_bar);
- writefln("opEquals: ", e ? "equal" : "not equal");
assert(!e);
auto c = (ti_foo.opCmp(ti_bar) == 0);
- writefln("opCmp: ", c ? "equal" : "not equal");
assert(!c);
}
@@ -63,7 +49,6 @@ Struct[1] table;
Struct getfirst()
{
foreach(v; table) {
- writeln(v.langID);
assert(v.langID == 1);
return v;
}
@@ -73,7 +58,6 @@ Struct getfirst()
Struct getsecond()
{
foreach(ref v; table) {
- writeln(v.langID);
assert(v.langID == 1);
return v;
}
@@ -85,11 +69,9 @@ void test3()
table[0].langID = 1;
auto v = getfirst();
- writeln(v.langID);
assert(v.langID == 1);
v = getsecond();
- writeln(v.langID);
assert(v.langID == 1);
}
@@ -198,11 +180,6 @@ template parseInteger(string s)
void test7()
{
- writeln(parseUinteger!("1234abc").value);
- writeln(parseUinteger!("1234abc").rest);
- writeln(parseInteger!("-1234abc").value);
- writeln(parseInteger!("-1234abc").rest);
-
assert(parseUinteger!("1234abc").value == "1234");
assert(parseUinteger!("1234abc").rest == "abc");
assert(parseInteger!("-1234abc").value == "-1234");
@@ -284,10 +261,8 @@ class B34 : A34 { }
void test11()
{
A34 test=new B34;
- writefln("Test is ", test.toString);
assert(test.toString == "test34.B34");
A34 test_2=cast(A34)(new B34);
- writefln("Test 2 is ", test_2.toString);
assert(test_2.toString == "test34.B34");
}
@@ -357,7 +332,6 @@ struct Iterator16(T : Basic16!(T, U), U)
{
static void Foo()
{
- writeln(typeid(T), typeid(U));
assert(is(T == int));
assert(is(U == float));
}
@@ -529,11 +503,8 @@ template Mang(alias F)
template moo(alias A)
{
- pragma(msg," ");
const string a = Mang!(A).mangledname;
- pragma(msg," ");
static assert(Mang!(A).mangledname == a); // FAILS !!!
- pragma(msg," ");
}
void test27()
@@ -658,10 +629,39 @@ struct Vector34
public string toString()
{
- return std.string.format("<%f, %f, %f>", x, y, z);
+ return formatImpl("<%f, %f, %f>", x, y, z);
+ }
+
+ private static string formatImpl(string fmt, ...)
+ {
+ string ret = "<";
+ bool comma;
+ foreach (arg; _arguments)
+ {
+ assert(arg is typeid(float));
+ if (comma)
+ ret ~= ", ";
+ auto f = va_arg!float(_argptr);
+ if (f == 1)
+ ret ~= "1.000000";
+ else if (f == 0)
+ ret ~= "0.000000";
+ else
+ assert(0);
+ comma = true;
+ }
+ ret ~= ">";
+ return ret;
}
}
+string format34(string fmt, ...)
+{
+ assert(_arguments[0] is typeid(Vector34));
+ auto arg = va_arg!Vector34(_argptr);
+ return arg.toString();
+}
+
class Foo34
{
private Vector34 v;
@@ -679,17 +679,12 @@ class Foo34
private void bar()
{
auto s = foobar();
- writef("Returned: %s\n", s);
- assert(std.string.format("%s", s) == "<1.000000, 0.000000, 0.000000>");
+ assert(format34("%s", s) == "<1.000000, 0.000000, 0.000000>");
}
public Vector34 foobar()
{
- writef("Returning %s\n", v);
-
return v;
- Vector34 b = Vector34();
- return b;
}
}
@@ -704,18 +699,18 @@ void test34()
void foo35()
{
- uint a;
- uint b;
- uint c;
- extern (Windows) int function(int i, int j, int k) xxx;
+ uint a;
+ uint b;
+ uint c;
+ extern (Windows) int function(int i, int j, int k) xxx;
- a = 1;
- b = 2;
- c = 3;
+ a = 1;
+ b = 2;
+ c = 3;
- xxx = cast(typeof(xxx))(a + b);
- throw new Exception("xxx");
- xxx( 4, 5, 6 );
+ xxx = cast(typeof(xxx))(a + b);
+ throw new Exception("xxx");
+ xxx( 4, 5, 6 );
}
void test35()
@@ -737,7 +732,7 @@ void test37()
{
synchronized
{
- writefln("Hello world!");
+ printf("Hello world!\n");
}
}
}
@@ -768,7 +763,7 @@ static Rect sizeTest(bool empty) {
}
void print38(Rect r) {
- writefln("(%d, %d)-(%d, %d)", r.left, r.top, r.right, r.bottom);
+ printf("(%d, %d)-(%d, %d)\n", r.left, r.top, r.right, r.bottom);
assert(r.left == 0);
assert(r.right == 0);
assert(r.top == 0);
@@ -781,37 +776,66 @@ Rect defaultRect() {
/************************************************/
+void varargs39(...)
+{
+ if (_arguments[0] is typeid(double[]))
+ {
+ auto arg = va_arg!(double[])(_argptr);
+ assert(arg.length == 1 && arg[0] == 1 || arg[0] == 2);
+ }
+ else if (_arguments[0] is typeid(double[][]))
+ {
+ auto arg = va_arg!(double[][])(_argptr);
+ assert(arg == [[1],[2]]);
+ }
+ else if (_arguments[0] is typeid(double[1][]))
+ {
+ auto arg = va_arg!(double[1][])(_argptr);
+ assert(arg == [[1], [2]]);
+ }
+ else
+ assert(0);
+}
+
void test39()
{
double[][] foo = [[1.0],[2.0]];
- writeln(foo[0]); // --> [1] , ok
- writeln(foo[1]); // --> [2] , ok
-
- writeln(foo); // --> [[1],4.63919e-306] ack!
- writefln("%s", foo); // --> ditto
- auto f = std.string.format("%s", foo);
- assert(f == "[[1], [2]]");
+ varargs39(foo[0]); // --> [1] , ok
+ varargs39(foo[1]); // --> [2] , ok
+ varargs39(foo); // --> [[1],4.63919e-306] ack!
double[1][2] bar;
bar[0][0] = 1.0;
bar[1][0] = 2.0;
- writeln(bar); // Error: Access violation
- auto r = std.string.format("%s", bar);
- assert(r == "[[1], [2]]");
+ varargs39(bar); // Error: Access violation
}
/************************************************/
+void varargs40(...)
+{
+ if (_arguments[0] is typeid(int[char]))
+ {
+ auto x = va_arg!(int[char])(_argptr);
+ assert(x == ['b':123]);
+ }
+ else if (_arguments[0] is typeid(int))
+ {
+ auto x = va_arg!int(_argptr);
+ assert(x == 123);
+ }
+ else
+ assert(0);
+}
+
void test40()
{
int[char] x;
x['b'] = 123;
- writeln(x);
- auto r = std.string.format("%s", x);
- assert(r == "['b':123]");
- writeln(x['b']);
+ varargs40(x);
+ varargs40(x['b']);
}
/************************************************/
@@ -855,12 +879,28 @@ void test44()
/************************************************/
+void varargs45(...)
+{
+ if (_arguments[0] is typeid(const(char[3])[]))
+ {
+ auto a = va_arg!(const(char[3])[])(_argptr);
+ assert(a == ["abc", "def"]);
+ }
+ else if (_arguments[0] is typeid(const(char)[][]))
+ {
+ auto b = va_arg!(const(char)[][])(_argptr);
+ assert(b == ["abc", "def"]);
+ }
+ else
+ assert(0);
+}
+
void test45()
{
- //char[3][] a = ["abc", "def"];
- //writefln(a);
- //char[][2] b = ["abc", "def"];
- //writefln(b);
+ const(char)[3][] a = ["abc", "def"];
+ varargs45(a);
+ const(char)[][2] b = ["abc", "def"];
+ varargs45(b);
}
/************************************************/
@@ -981,26 +1021,22 @@ void test49()
version (all)
{
- writefln("Before test 1: ", a.v);
- if (a == a.init) { writeln(a.v,"(a==a.init)"); assert(0); }
- else { writeln(a.v,"(a!=a.init)"); assert(a.v == 10); }
+ if (a == a.init) { assert(0); }
+ else { assert(a.v == 10); }
}
else
{
- writefln("Before test 1: ", a.v);
- if (a == a.init) { writeln(a.v,"(a==a.init)"); assert(a.v == 10); }
- else { writeln(a.v,"(a!=a.init)"); assert(0); }
+ if (a == a.init) { assert(a.v == 10); }
+ else { assert(0); }
}
a.v = 100;
- writefln("Before test 2: ", a.v);
- if (a == a.init) { writeln(a.v,"(a==a.init)"); assert(0); }
- else { writeln(a.v,"(a!=a.init)"); assert(a.v == 100); }
+ if (a == a.init) { assert(0); }
+ else { assert(a.v == 100); }
a = A(1000);
- writefln("Before test 3: ", a.v);
- if (a == a.init) { writeln(a.v,"(a==a.init)"); assert(0); }
- else { writeln(a.v,"(a!=a.init)"); assert(a.v == 1000); }
+ if (a == a.init) { assert(0); }
+ else { assert(a.v == 1000); }
version (all)
assert(a.init.v == 0);
@@ -1044,14 +1080,6 @@ struct TestStruct
void func53(TestStruct[2] testarg)
{
- writeln(testarg[0].dummy0);
- writeln(testarg[0].dummy1);
- writeln(testarg[0].dummy2);
-
- writeln(testarg[1].dummy0);
- writeln(testarg[1].dummy1);
- writeln(testarg[1].dummy2);
-
assert(testarg[0].dummy0 == 0);
assert(testarg[0].dummy1 == 1);
assert(testarg[0].dummy2 == 2);
@@ -1061,11 +1089,10 @@ void func53(TestStruct[2] testarg)
assert(testarg[1].dummy2 == 2);
}
-TestStruct m53[2];
+TestStruct[2] m53;
void test53()
{
- writeln(&m53);
func53(m53);
}
@@ -1076,13 +1103,11 @@ void test54()
double a = 0;
double b = 1;
// Internal error: ..\ztc\cg87.c 3233
-// a += (1? b: 1+1i)*1i;
- writeln(a);
-// assert(a == 0);
+ a += ((1? b: 1+1i)*1i).re;
+ assert(a == 0);
// Internal error: ..\ztc\cod2.c 1680
-// a += (b?1:b-1i)*1i;
- writeln(a);
-// assert(a == 0);
+ a += ((b?1:b-1i)*1i).re;
+ assert(a == 0);
}
/************************************************/
@@ -1101,7 +1126,7 @@ void test55()
/************************************************/
template t56() { alias Object t56; }
-pragma(msg, t56!().stringof);
+static assert(t56!().stringof == "Object");
void test56()
{
@@ -1115,9 +1140,6 @@ void test57()
static if (is(AA T : T[U], U : const char[]))
{
- writeln(typeid(T));
- writeln(typeid(U));
-
assert(is(T == long));
assert(is(U == const(char)[]));
}
@@ -1129,9 +1151,7 @@ void test57()
static if (is(int[10] W : W[V], int V))
{
- writeln(typeid(W));
assert(is(W == int));
- writeln(V);
assert(V == 10);
}
@@ -1295,7 +1315,7 @@ void main()
test61();
test62();
- writefln("Success");
+ printf("Success\n");
}
diff --git a/gcc/testsuite/gdc.test/runnable/test3449.d b/gcc/testsuite/gdc.test/runnable/test3449.d
index d330cb95f20..8d389188463 100644
--- a/gcc/testsuite/gdc.test/runnable/test3449.d
+++ b/gcc/testsuite/gdc.test/runnable/test3449.d
@@ -1,6 +1,6 @@
/******************************************/
-// 3449
+// https://issues.dlang.org/show_bug.cgi?id=3449
template TypeTuple(T...) { alias TypeTuple = T; }
@@ -13,6 +13,10 @@ static this()
{
mg1 = 10;
cg1 = 10;
+}
+
+shared static this()
+{
ig1 = 10;
}
static assert(!__traits(compiles, { static assert(mg1 == 0); }));
@@ -89,7 +93,7 @@ void test3449()
}
/******************************************/
-// 10643
+// https://issues.dlang.org/show_bug.cgi?id=10643
struct S10643
{
diff --git a/gcc/testsuite/gdc.test/runnable/test3574a.d b/gcc/testsuite/gdc.test/runnable/test3574a.d
index c2283f704b8..c6122e757a2 100644
--- a/gcc/testsuite/gdc.test/runnable/test3574a.d
+++ b/gcc/testsuite/gdc.test/runnable/test3574a.d
@@ -6,7 +6,7 @@ out
{
g = 100;
}
-body
+do
{
return;
// expected return code == 0
diff --git a/gcc/testsuite/gdc.test/runnable/test3574b.d b/gcc/testsuite/gdc.test/runnable/test3574b.d
index d239ab68469..b256c64fe8b 100644
--- a/gcc/testsuite/gdc.test/runnable/test3574b.d
+++ b/gcc/testsuite/gdc.test/runnable/test3574b.d
@@ -6,7 +6,7 @@ out
{
g = 100;
}
-body
+do
{
//return;
// expected return code == 0
diff --git a/gcc/testsuite/gdc.test/runnable/test3574c.d b/gcc/testsuite/gdc.test/runnable/test3574c.d
index 2974da53383..f93e22a81e8 100644
--- a/gcc/testsuite/gdc.test/runnable/test3574c.d
+++ b/gcc/testsuite/gdc.test/runnable/test3574c.d
@@ -6,7 +6,7 @@ void main()
//{
// g = 100;
//}
-//body
+//do
{
return;
// expected return code == 0
diff --git a/gcc/testsuite/gdc.test/runnable/test3574d.d b/gcc/testsuite/gdc.test/runnable/test3574d.d
index 0eaf2dd8e79..7976b345f2a 100644
--- a/gcc/testsuite/gdc.test/runnable/test3574d.d
+++ b/gcc/testsuite/gdc.test/runnable/test3574d.d
@@ -6,7 +6,7 @@ void main()
//{
// g = 100;
//}
-//body
+//do
{
//return;
// expected return code == 0
diff --git a/gcc/testsuite/gdc.test/runnable/test37.d b/gcc/testsuite/gdc.test/runnable/test37.d
index f4a454747b4..08305cbff5b 100644
--- a/gcc/testsuite/gdc.test/runnable/test37.d
+++ b/gcc/testsuite/gdc.test/runnable/test37.d
@@ -1,14 +1,15 @@
-// RUNNABLE_PHOBOS_TEST
// PERMUTE_ARGS:
// REQUIRED_ARGS: -Jrunnable/extra-files
// EXTRA_FILES: extra-files/foo37.txt extra-files/std14198/uni.d
-import std.stdio;
+import core.stdc.stdio;
void main()
{
- writefln(import("foo37.txt"));
+ auto s = import("foo37.txt");
+ printf("%.*s\n", cast(int)s.length, s.ptr);
// also want to ensure that we can access
// imports in a subdirectory of the -J path
- writefln(import("std14198/uni.d"));
+ s = import("std14198/uni.d");
+ printf("%.*s\n", cast(int)s.length, s.ptr);
}
diff --git a/gcc/testsuite/gdc.test/runnable/test38.d b/gcc/testsuite/gdc.test/runnable/test38.d
index 4fc728d4310..4c76e9f9f28 100644
--- a/gcc/testsuite/gdc.test/runnable/test38.d
+++ b/gcc/testsuite/gdc.test/runnable/test38.d
@@ -1,8 +1,14 @@
-// COMPILE_SEPARATELY
-// EXTRA_SOURCES: imports/test38a.d
-// PERMUTE_ARGS:
+/*
+COMPILE_SEPARATELY
+EXTRA_SOURCES: imports/test38a.d
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+b = 49, 49
+---
+*/
-import std.stdio;
+import core.stdc.stdio;
import imports.test38a;
void main()
diff --git a/gcc/testsuite/gdc.test/runnable/test4.d b/gcc/testsuite/gdc.test/runnable/test4.d
index 1d035726088..9811254cf60 100644
--- a/gcc/testsuite/gdc.test/runnable/test4.d
+++ b/gcc/testsuite/gdc.test/runnable/test4.d
@@ -1,5 +1,11 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS:
+/*
+TEST_OUTPUT:
+---
+runnable/test4.d(717): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+*/
import core.exception;
import core.stdc.math;
@@ -48,17 +54,17 @@ void test1()
void test2()
{
- byte foo1[5];
- ubyte foo2[6];
- short foo3[7];
- ushort foo4[8];
- int foo5[9];
- uint foo6[10];
- long foo7[11];
- ulong foo8[12];
- float foo9[13];
- double foo10[14];
- real foo11[15];
+ byte[5] foo1;
+ ubyte[6] foo2;
+ short[7] foo3;
+ ushort[8] foo4;
+ int[9] foo5;
+ uint[10] foo6;
+ long[11] foo7;
+ ulong[12] foo8;
+ float[13] foo9;
+ double[14] foo10;
+ real[15] foo11;
int i;
@@ -96,17 +102,17 @@ void test2()
void test3()
{
- byte foo1[5] = 20;
- ubyte foo2[6] = 21;
- short foo3[7] = 22;
- ushort foo4[8] = 23;
- int foo5[9] = 24;
- uint foo6[10] = 25;
- long foo7[11] = 26;
- ulong foo8[12] = 27;
- float foo9[13] = 28;
- double foo10[14] = 29;
- real foo11[15] = 30;
+ byte[5] foo1 = 20;
+ ubyte[6] foo2 = 21;
+ short[7] foo3 = 22;
+ ushort[8] foo4 = 23;
+ int[9] foo5 = 24;
+ uint[10] foo6 = 25;
+ long[11] foo7 = 26;
+ ulong[12] foo8 = 27;
+ float[13] foo9 = 28;
+ double[14] foo10 = 29;
+ real[15] foo11 = 30;
int i;
@@ -257,13 +263,13 @@ struct TestVectors
string replace;
};
-TestVectors tva[] =
+TestVectors[] tva =
[
{ pattern:"(a)\\1", input:"abaab", result:"y", format:"&", replace:"aa" },
{ pattern:"abc", input:"abc", result:"y", format:"&", replace:"abc" },
];
-TestVectors tvs[2] =
+TestVectors[2] tvs =
[
{ pattern:"(a)\\1", input:"abaab", result:"y", format:"&", replace:"aa" },
{ pattern:"abc", input:"abc", result:"y", format:"&", replace:"abc" },
@@ -360,8 +366,8 @@ const uint MAX_PATH1 = 260;
enum { MAX_PATH2 = 261 }
struct WIN32_FIND_DATA {
- char cFileName1[MAX_PATH1];
- char cFileName2[MAX_PATH2];
+ char[MAX_PATH1] cFileName1;
+ char[MAX_PATH2] cFileName2;
}
void test11()
@@ -502,10 +508,10 @@ void test15()
struct GUID { // size is 16
align(1):
- uint Data1;
- ushort Data2;
- ushort Data3;
- ubyte Data4[8];
+ uint Data1;
+ ushort Data2;
+ ushort Data3;
+ ubyte[8] Data4;
}
@@ -543,26 +549,7 @@ void test16()
/* ================================ */
-void test17()
-{
- creal z = 1. + 2.0i;
-
- real r = z.re;
- assert(r == 1.0);
-
- real i = z.im;
- assert(i == 2.0);
-
- assert(r.im == 0.0);
- assert(r.re == 1.0);
-
- assert(i.re == 2.0);
- assert(i.im == 0.0i);
-}
-
-/* ================================ */
-
-private const uint crc_table[256] = [
+private const uint[256] crc_table = [
0x00000000u, 0x77073096u, 0xee0e612cu, 0x990951bau, 0x076dc419u,
0x2d02ef8du
];
@@ -640,24 +627,6 @@ void test20()
/* ================================ */
-void test21()
-{
- ireal imag = 2.5i;
- printf ("test of imag*imag = %Lf\n",imag*imag);
- assert(imag * imag == -6.25);
-}
-
-/* ================================ */
-
-void test22()
-{
- creal z1 = 1. - 2.0i;
- ireal imag_part = z1.im/1i;
-}
-
-
-/* ================================ */
-
int def23(int x, int y)
{
return x * y;
@@ -978,11 +947,11 @@ void test43()
{
string s;
- s = __FILE__; printf("file = '%.*s'\n", s.length, s.ptr);
+ s = __FILE__; printf("file = '%.*s'\n", cast(int)s.length, s.ptr);
printf("line = %d\n", __LINE__);
- s = __DATE__; printf("date = '%.*s'\n", s.length, s.ptr);
- s = __TIME__; printf("time = '%.*s'\n", s.length, s.ptr);
- s = __TIMESTAMP__; printf("timestamp = '%.*s'\n", s.length, s.ptr);
+ s = __DATE__; printf("date = '%.*s'\n", cast(int)s.length, s.ptr);
+ s = __TIME__; printf("time = '%.*s'\n", cast(int)s.length, s.ptr);
+ s = __TIMESTAMP__; printf("timestamp = '%.*s'\n", cast(int)s.length, s.ptr);
}
/* ================================ */
@@ -1143,7 +1112,10 @@ class Cout{
Cout set(int x){
return this;
}
- alias set opShl;
+ Cout opBinary(string op)(int x) if (op == "<<")
+ {
+ return set(x);
+ }
}
void test49()
@@ -1250,7 +1222,7 @@ void test54()
}
catch(Exception e)
{
- printf("catch %.*s\n", e.msg.length, e.msg.ptr);
+ printf("catch %.*s\n", cast(int)e.msg.length, e.msg.ptr);
assert(e.msg == "first");
assert(e.next.msg == "second");
}
@@ -1270,7 +1242,7 @@ void foo55()
catch (Exception e)
{
printf("inner catch %p\n", e);
- printf("e.msg == %.*s\n", e.msg.length, e.msg.ptr);
+ printf("e.msg == %.*s\n", cast(int)e.msg.length, e.msg.ptr);
assert(e.msg == "second");
//assert(e.msg == "first");
//assert(e.next.msg == "second");
@@ -1404,6 +1376,46 @@ void test59()
/* ================================ */
+// https://issues.dlang.org/show_bug.cgi?id=10664
+
+Exception collectExceptionE(int delegate () expression, ref int result)
+{
+ try
+ {
+ result = expression();
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ return null;
+}
+
+RangeError collectExceptionR(int delegate () expression, ref int result)
+{
+ try
+ {
+ result = expression();
+ }
+ catch (RangeError e)
+ {
+ return e;
+ }
+ return null;
+}
+
+void test10664()
+{
+ int b;
+ int foo() { throw new Exception("blah"); }
+ assert(collectExceptionE(&foo, b));
+
+ int[] a = new int[3];
+ int goo() { return a[4]; }
+ collectExceptionR(&goo, b);
+}
+
+/* ================================ */
int main()
@@ -1424,12 +1436,9 @@ int main()
test14();
//test15();
test16();
- test17();
test18();
test19();
test20();
- test21();
- test22();
test23();
test24();
// test26();
@@ -1465,6 +1474,7 @@ int main()
test57();
test58();
test59();
+ test10664();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/test40.d b/gcc/testsuite/gdc.test/runnable/test40.d
index 528162b78c1..2c1069d52a4 100644
--- a/gcc/testsuite/gdc.test/runnable/test40.d
+++ b/gcc/testsuite/gdc.test/runnable/test40.d
@@ -2,7 +2,7 @@
// PERMUTE_ARGS:
// REQUIRED_ARGS:
-import std.stdio;
+import core.stdc.stdio;
import imports.test40a;
class Foo {
diff --git a/gcc/testsuite/gdc.test/runnable/test42.d b/gcc/testsuite/gdc.test/runnable/test42.d
index e887faea2eb..57045ba00e7 100644
--- a/gcc/testsuite/gdc.test/runnable/test42.d
+++ b/gcc/testsuite/gdc.test/runnable/test42.d
@@ -1,4 +1,4 @@
-/* RUNNABLE_PHOBOS_TEST
+/*
REQUIRED_ARGS:
TEST_OUTPUT:
---
@@ -13,10 +13,10 @@ C6test427test219FZ8__mixin11C
*/
module test42;
-import std.stdio;
+
import core.stdc.stdio;
-import std.string;
import core.memory;
+import core.vararg;
/***************************************************/
@@ -61,7 +61,7 @@ void test2()
void test3()
{
auto i = mixin("__LINE__");
- writefln("%d", i);
+ printf("%d\n", i);
assert(i == 63);
}
@@ -94,7 +94,8 @@ void test5()
}
/***************************************************/
-// Bug 1200. One case moved to deprecate1.d
+// https://issues.dlang.org/show_bug.cgi?id=1200
+// One case moved to deprecate1.d
void foo6a() {
do
@@ -162,8 +163,8 @@ void test8()
void test9()
{
- writeln(long.max.stringof);
- writeln(ulong.max.stringof);
+ //writeln(long.max.stringof);
+ //writeln(ulong.max.stringof);
assert(long.max.stringof == "9223372036854775807L");
assert(ulong.max.stringof == "18446744073709551615LU");
}
@@ -205,7 +206,7 @@ void test12()
assert((foo ~ cast(char[])"foo").length == foo.length + 1);
assert((cast(char[])"foo" ~ foo).length == foo.length + 1);
- printf("%d\n", (foo ~ cast(char[])"foo")[0].length);
+ printf("%zd\n", (foo ~ cast(char[])"foo")[0].length);
assert((foo ~ [cast(char[])"foo"]).length == foo.length + 1);
@@ -421,7 +422,7 @@ void test25()
int[10] arrayA = [0,1,2,3,4,5,6,7,8,9];
foreach(int i; arrayA)
{
- writeln(i);
+ printf("%d\n", i);
}
}
@@ -532,11 +533,11 @@ struct S35
void test35()
{ S35 s;
auto t = typeid(S35);
- writefln("s = %s", s);
- writefln("s = %s", t);
+ //writefln("s = %s", s);
+ //writefln("s = %s", t);
auto tis = cast(TypeInfo_Struct)t;
- writefln("s = %s", tis);
- writefln("s = %s", tis.xtoString);
+ //writefln("s = %s", tis);
+ //writefln("s = %s", tis.xtoString);
assert(tis.xtoString != null);
}
@@ -660,16 +661,6 @@ void test43()
/***************************************************/
-void test44()
-{
- ifloat f = 1.0fi;
-// f *= 2.0fi; // illegal but compiles
- writefln("%s", f);
-// assert(f == 0i);
-}
-
-/***************************************************/
-
int foo45(int i)
{
if(i==0){
@@ -685,7 +676,7 @@ void test45()
assert(foo45(0)==2);
try{
foo45(1);
- }catch{
+ }catch(Throwable){
return cast(void)0;
}
assert(0);
@@ -819,7 +810,7 @@ void test54()
string[] k=["adf","AsdfadSF","dfdsfassdf"];
foreach(d;k)
{
- printf("%.*s\n", d.length, d.ptr);
+ printf("%.*s\n", cast(int)d.length, d.ptr);
string foo() {assert(d!="");return d;}
func54(&foo);
func54(delegate string() {assert(d!="");return d;});
@@ -827,7 +818,7 @@ void test54()
}
/***************************************************/
-// bug 1767
+// https://issues.dlang.org/show_bug.cgi?id=1767
class DebugInfo
{
@@ -862,7 +853,7 @@ void test56()
void writecrossing(bool goal)
{
- writeln(goal?"escape":"return");
+ goal ? printf("escape\n") : printf("return\n");
}
void test57()
@@ -1313,15 +1304,15 @@ class C79
void test79()
{
C79 c = new C79();
- writeln(c.__vptr);
- writeln(c.__vptr[0]);
- writeln(cast(void*)c.classinfo);
+// writeln(c.__vptr);
+// writeln(c.__vptr[0]);
+// writeln(cast(void*)c.classinfo);
assert(c.__vptr[0] == cast(void*)c.classinfo);
- writeln(c.__monitor);
+// writeln(c.__monitor);
assert(c.__monitor == null);
synchronized (c)
{
- writeln(c.__monitor);
+// writeln(c.__monitor);
assert(c.__monitor !is null);
}
}
@@ -1396,7 +1387,7 @@ void test83()
void test84()
{
int[0][10] arr;
- printf("%u\n", &arr[9] - &arr[0]);
+ printf("%tu\n", &arr[9] - &arr[0]);
auto i = &arr[9] - &arr[0];
assert(i == 0);
}
@@ -1615,11 +1606,11 @@ void test96()
void test97()
{
const short[] ct = cast(short[]) [cast(byte)1, 1];
- writeln(ct);
+// writeln(ct);
assert(ct.length == 2 && ct[0] == 1 && ct[1] == 1);
short[] rt = cast(short[]) [cast(byte)1, cast(byte)1].dup;
- writeln(rt);
+// writeln(rt);
assert(rt.length == 1 && rt[0] == 257);
}
@@ -1751,7 +1742,7 @@ void test105()
{
Templ!(int).Type x;
auto s = Templ!(int).XXX;
- writeln(s);
+ printf("%.*s\n", cast(int)s.length, s.ptr);
assert(s == "i");
}
@@ -1793,7 +1784,7 @@ void test108()
void test109()
{
- double x[] = new double[1];
+ double[] x = new double[1];
assert(x[0] != 0);
}
@@ -1860,7 +1851,7 @@ struct VariantN
void bar(int value, int i)
{
- int args[2] = [ VariantN(value), VariantN(i) ];
+ int[2] args = [ VariantN(value), VariantN(i) ];
}
}
@@ -2100,7 +2091,7 @@ void test129()
}
/***************************************************/
-// 12725
+// https://issues.dlang.org/show_bug.cgi?id=12725
struct R12725(R : E[], E)
{
@@ -2118,7 +2109,7 @@ void test12725()
}
/***************************************************/
-// 12728
+// https://issues.dlang.org/show_bug.cgi?id=12728
struct Matrix12728(T, uint m, uint n = m, ubyte f = 0)
{
@@ -2226,7 +2217,7 @@ void test135()
void test136()
{
- struct S { int i[3]; }
+ struct S { int[3] i; }
enum S s = S(8);
const int i = s.i[2];
assert(i == 8);
@@ -2286,7 +2277,7 @@ void test140()
class Foo141 {
Foo141 next;
void start()
- in { assert (!next); } body
+ in { assert (!next); } do
{
void* p = cast(void*)this;
}
@@ -2426,7 +2417,7 @@ bool foo150()
}
/***************************************************/
-// 3521
+// https://issues.dlang.org/show_bug.cgi?id=3521
void crash(int x)
{
@@ -2724,7 +2715,7 @@ enum FwdEnum : int
}
/***************************************************/
-// 3740
+// https://issues.dlang.org/show_bug.cgi?id=3740
abstract class Address {
abstract int nameLen();
@@ -2848,12 +2839,6 @@ double[100_000] arr = 0.0;
/***************************************************/
-alias ireal BUG3919;
-alias typeof(BUG3919.init*BUG3919.init) ICE3919;
-alias typeof(BUG3919.init/BUG3919.init) ICE3920;
-
-/***************************************************/
-
struct S179 {
char a, b, c, d;
}
@@ -2935,7 +2920,7 @@ void test181()
}
/***************************************************/
-// 4042
+// https://issues.dlang.org/show_bug.cgi?id=4042
template isQObjectType(T)
{
@@ -3193,7 +3178,7 @@ void test197()
/***************************************************/
-void test198() // Bugzilla 4506
+void test198() // https://issues.dlang.org/show_bug.cgi?id=4506
{
int c = 1;
for (int k = 0; k < 2; k++) {
@@ -3204,7 +3189,7 @@ void test198() // Bugzilla 4506
/***************************************************/
-// Bugzilla 4514
+// https://issues.dlang.org/show_bug.cgi?id=4514
void g199(void delegate(void*, void*) d) { }
struct X199 {
@@ -3216,7 +3201,7 @@ struct X199 {
}
/***************************************************/
-// Bugzilla 4443
+// https://issues.dlang.org/show_bug.cgi?id=4443
struct Struct4443
{
@@ -3241,10 +3226,10 @@ void test200()
/***************************************************/
-// Bugzilla 2931
+// https://issues.dlang.org/show_bug.cgi?id=2931
struct Bug2931 {
- int val[3][4];
+ int[4][3] val;
}
struct Outer2931 {
@@ -3290,10 +3275,9 @@ void test201() {
/***************************************************/
// This was the original varargs example in std.vararg
-import core.vararg;
void foo202(int x, ...) {
- printf("%d arguments\n", _arguments.length);
+ printf("%zd arguments\n", _arguments.length);
for (int i = 0; i < _arguments.length; i++) {
int j = va_arg!(int)(_argptr);
printf("\t%d\n", j);
@@ -3302,7 +3286,7 @@ void foo202(int x, ...) {
}
void fooRef202(ref int x, ...) {
- printf("%d arguments\n", _arguments.length);
+ printf("%zd arguments\n", _arguments.length);
for (int i = 0; i < _arguments.length; i++) {
int j = va_arg!(int)(_argptr);
printf("\t%d\n", j);
@@ -3321,7 +3305,7 @@ void test202()
}
/***************************************************/
-// Bugzilla 1418
+// https://issues.dlang.org/show_bug.cgi?id=1418
class A203
{
@@ -3343,22 +3327,22 @@ void test203()
auto b = a.new B203;
auto c = new C203;
- writeln(a.tupleof); // prints: A
- writeln(b.tupleof); // prints: B main.A
- writeln(c.tupleof); // prints: C 0000
+// writeln(a.tupleof); // prints: A
+// writeln(b.tupleof); // prints: B main.A
+// writeln(c.tupleof); // prints: C 0000
assert(a.tupleof.length == 1 && a.tupleof[0] == 'A');
assert(b.tupleof.length == 1 && b.tupleof[0] == 'B');
assert(c.tupleof.length == 1 && c.tupleof[0] == 'C');
}
/***************************************************/
-// Bugzilla 4516
+// https://issues.dlang.org/show_bug.cgi?id=4516
struct A204 { B204 b; }
enum B204 { Z }
/***************************************************/
-// Bugzilla 4503
+// https://issues.dlang.org/show_bug.cgi?id=4503
class Collection205(T) { }
ICollection c;
@@ -3451,13 +3435,13 @@ template Bug6602B(U) {
enum bug6602Compiles = __traits(compiles, Bug6602A!short);
/***************************************************/
-// Bugzilla 3493
+// https://issues.dlang.org/show_bug.cgi?id=3493
const bar209 = foo209;
const int * foo209 = null;
/***************************************************/
-// 3418
+// https://issues.dlang.org/show_bug.cgi?id=3418
void test210()
{
@@ -3482,7 +3466,7 @@ void test212()
}
/***************************************************/
-// 4768
+// https://issues.dlang.org/show_bug.cgi?id=4768
struct A213 { B213 b; }
enum B213 { Z213 = 2 }
@@ -3515,7 +3499,6 @@ void test215()
{
class C {}
enum assocarrayliteral = Q!( [1:2] ).q.stringof;
- enum complex80 = Q!( 1+1.0i ).q.stringof;
//enum dottype = Q!( C.Object.toString ).q.stringof;
enum halt = 0.stringof; // ICE w/ -release
//enum remove = Q!( [1:2].remove(1) ).q.stringof;
@@ -3523,7 +3506,7 @@ void test215()
}
/***************************************************/
-// 4941
+// https://issues.dlang.org/show_bug.cgi?id=4941
template T216(_...) { alias _ T216; }
size_t mid216(size_t n) { return n/2; }
@@ -3558,7 +3541,7 @@ void test217()
}
/***************************************************/
-// 2954
+// https://issues.dlang.org/show_bug.cgi?id=2954
void test218()
{
@@ -3574,7 +3557,7 @@ void test218()
}
/***************************************************/
-// 2206
+// https://issues.dlang.org/show_bug.cgi?id=2206
template T219(U) {
class C {}
@@ -3593,7 +3576,7 @@ void test219()
/***************************************************/
-// 2206
+// https://issues.dlang.org/show_bug.cgi?id=2206
class D220 {}
@@ -3606,9 +3589,9 @@ void test220()
mixin T220!(int);
// all print 8
- writeln(T220!(int).C.classinfo.initializer.length);
- writeln(C.classinfo.initializer.length);
- writeln(D220.classinfo.initializer.length);
+// writeln(T220!(int).C.classinfo.initializer.length);
+// writeln(C.classinfo.initializer.length);
+// writeln(D220.classinfo.initializer.length);
auto c = new C; // segfault in _d_newclass
}
@@ -3677,7 +3660,7 @@ synchronized shared class C5105
}
/***************************************************/
-// 5145
+// https://issues.dlang.org/show_bug.cgi?id=5145
interface I221{
void bla();
@@ -3692,7 +3675,7 @@ class A221 : B221
{
final override I221 sync()
in { assert( valid ); }
- body
+ do
{
return null;
}
@@ -3702,7 +3685,7 @@ class B221 : J221
{
override I221 sync()
in { assert( valid ); }
- body
+ do
{
return null;
}
@@ -3729,7 +3712,7 @@ template Bug3276_b(alias W) {
alias Bug3276_b!(Bug3276) Bug3276_c;
/***************************************************/
-// 5294
+// https://issues.dlang.org/show_bug.cgi?id=5294
void foo222(int) {}
@@ -3757,7 +3740,7 @@ void test223()
}
/***************************************************/
-// 4379
+// https://issues.dlang.org/show_bug.cgi?id=4379
template BigTuple(U...) {
alias U BigTuple;
@@ -3777,7 +3760,7 @@ void test224()
}
/***************************************************/
-// 3681
+// https://issues.dlang.org/show_bug.cgi?id=3681
public final class A3681 {
private this() {
@@ -3876,7 +3859,7 @@ static assert(mixin(ice4390()) == ``);
static assert(mixin(ice4390()) == ``);
/***************************************************/
-// 190
+// https://issues.dlang.org/show_bug.cgi?id=190
alias int avocado;
void eat(avocado x225 = .x225);
@@ -3887,7 +3870,7 @@ void test225()
}
/***************************************************/
-// 5534
+// https://issues.dlang.org/show_bug.cgi?id=5534
void doStuff(byte start, byte end, uint increment = 1U) {
auto output = new byte[3];
@@ -3903,7 +3886,7 @@ void test226() {
}
/***************************************************/
-// 5536
+// https://issues.dlang.org/show_bug.cgi?id=5536
void test227()
{
@@ -3914,7 +3897,7 @@ void test227()
}
/***************************************************/
-// 4017
+// https://issues.dlang.org/show_bug.cgi?id=4017
struct _A
{
@@ -3926,7 +3909,7 @@ const A_SIZE = (A4017.sizeof);
alias _A A4017;
/***************************************************/
-// 5455
+// https://issues.dlang.org/show_bug.cgi?id=5455
void thrw(Data *s) {
throw new Exception("xxx");
@@ -3959,7 +3942,7 @@ struct Resp {
}
/**************************************/
-// 5571
+// https://issues.dlang.org/show_bug.cgi?id=5571
void test228() {
auto b = new bool;
@@ -3968,7 +3951,7 @@ void test228() {
}
/***************************************************/
-// 5572
+// https://issues.dlang.org/show_bug.cgi?id=5572
void doSynchronized() {
printf("In doSynchronized() 1: %p\n", cast(void*) global229);
@@ -3994,10 +3977,10 @@ void test229() {
/***************************************************/
-static immutable real negtab[14] =
+static immutable real[14] negtab =
[ 1e-4096L,1e-2048L,1e-1024L,1e-512L,1e-256L,1e-128L,1e-64L,1e-32L,
1e-16L,1e-8L,1e-4L,1e-2L,1e-1L,1.0L ];
-static immutable real postab[13] =
+static immutable real[13] postab =
[ 1e+4096L,1e+2048L,1e+1024L,1e+512L,1e+256L,1e+128L,1e+64L,1e+32L,
1e+16L,1e+8L,1e+4L,1e+2L,1e+1L ];
@@ -4095,7 +4078,7 @@ void bug5717()
}
/***************************************************/
-// 3086
+// https://issues.dlang.org/show_bug.cgi?id=3086
class X231 {
void a() {}
@@ -4115,7 +4098,7 @@ void test231() {
}
/***************************************************/
-// 4140
+// https://issues.dlang.org/show_bug.cgi?id=4140
const A232 = [1,2,3];
const B232 = A232[1..A232.length];
@@ -4133,7 +4116,7 @@ void test232()
}
/***************************************************/
-// 1389
+// https://issues.dlang.org/show_bug.cgi?id=1389
void test233()
{
@@ -4142,7 +4125,7 @@ void test233()
}
/***************************************************/
-// 5735
+// https://issues.dlang.org/show_bug.cgi?id=5735
struct A234 {}
@@ -4204,7 +4187,7 @@ void bug6184()
{
bool cmp(ref int[3] a, ref int[3] b)
{
- return a is b;
+ return a[] is b[];
}
static struct Ary
@@ -4224,7 +4207,7 @@ void bug6184()
}
/***************************************************/
-// 6229
+// https://issues.dlang.org/show_bug.cgi?id=6229
int test6229()
{
@@ -4288,13 +4271,13 @@ void test236()
/***************************************************/
-// 4460
+// https://issues.dlang.org/show_bug.cgi?id=4460
void test237()
{
foreach (s, i; [ "a":1, "b":2 ])
{
- writeln(s, i);
+ //writeln(s, i);
}
}
@@ -4327,7 +4310,7 @@ void test238()
}
/***************************************************/
-// 5239
+// https://issues.dlang.org/show_bug.cgi?id=5239
struct S239 { int x; }
@@ -4353,22 +4336,25 @@ void test6506() {
}
/***************************************************/
-// 6505
+// https://issues.dlang.org/show_bug.cgi?id=6505
double foo240() {
return 1.0;
}
void test240() {
- double a = foo240();
- double b = foo240();
- double x = a*a + a*a + a*a + a*a + a*a + a*a + a*a +
+ double a = void;
+ double b = void;
+ double x = void;
+ a = foo240();
+ b = foo240();
+ x = a*a + a*a + a*a + a*a + a*a + a*a + a*a +
a*b + a*b;
assert(x > 0);
}
/***************************************************/
-// 6563
+// https://issues.dlang.org/show_bug.cgi?id=6563
int foo6563(float a, float b, float c, float d, float e, float f, float g, float h)
{
@@ -4480,7 +4466,7 @@ void test6189() {
}
/***************************************************/
-// 6997
+// https://issues.dlang.org/show_bug.cgi?id=6997
long fun6997(long a,long b,long c)
{
@@ -4517,6 +4503,7 @@ void test6997()
auto x = S6997().foo();
}
+
/***************************************************/
ubyte foo7026(uint n) {
@@ -4584,7 +4571,7 @@ struct Point6881
}
/***************************************************/
-// 7212
+// https://issues.dlang.org/show_bug.cgi?id=7212
void foo7212(scope int delegate(int a) dg)
{
}
@@ -4612,7 +4599,7 @@ void test242()
}
/***************************************************/
-// 7290
+// https://issues.dlang.org/show_bug.cgi?id=7290
void foo7290a(alias dg)()
{
@@ -4634,11 +4621,13 @@ void test7290()
int add = 2;
scope dg = (int a) => a + add;
+ // This will break with -preview=dip1000 because a closure will no longer be allocated
assert(GC.addrOf(dg.ptr) == null);
foo7290a!dg();
foo7290b(dg);
- foo7290c(dg);
+ foo7290c(dg); // this will fail with -preview=dip1000 and @safe because a scope delegate gets
+ // assigned to @system delegate, but no closure was allocated
}
/***************************************************/
@@ -4651,7 +4640,7 @@ void test7367()
}
/***************************************************/
-// 7375
+// https://issues.dlang.org/show_bug.cgi?id=7375
class A7375 {}
class B7375(int i) : A7375 {}
@@ -4833,7 +4822,7 @@ void test243() {
}
/***************************************************/
-// 7742
+// https://issues.dlang.org/show_bug.cgi?id=7742
struct Foo7742 {
static immutable f = Foo7742(1, 2);
@@ -4855,7 +4844,7 @@ void test7742()
}
/***************************************************/
-// 7807
+// https://issues.dlang.org/show_bug.cgi?id=7807
interface Interface7807
{
@@ -4890,7 +4879,7 @@ void test7807()
}
/***************************************************/
-// 7815
+// https://issues.dlang.org/show_bug.cgi?id=7815
enum Closure {
Matrix
@@ -4985,7 +4974,7 @@ struct Foo7974
}
/***************************************************/
-// 4155
+// https://issues.dlang.org/show_bug.cgi?id=4155
float getnanf() { return float.nan; }
@@ -5000,7 +4989,7 @@ void test4155()
}
/***************************************************/
-// 7911
+// https://issues.dlang.org/show_bug.cgi?id=7911
struct Klass7911
{
@@ -5020,7 +5009,7 @@ void test7911()
}
/***************************************************/
-// 8429
+// https://issues.dlang.org/show_bug.cgi?id=8429
static if(true)
version = Foo8429;
@@ -5028,7 +5017,7 @@ static if(true)
version(Foo8429) {}
/***************************************************/
-// 8069
+// https://issues.dlang.org/show_bug.cgi?id=8069
interface I8069
{
@@ -5044,7 +5033,7 @@ struct A8069
}
/***************************************************/
-// 8095
+// https://issues.dlang.org/show_bug.cgi?id=8095
void bug8095(int p0, int *p1, int z, int edx, int *p4, int p5)
{
@@ -5068,7 +5057,7 @@ void test8095() {
}
/***************************************************/
-// 8091
+// https://issues.dlang.org/show_bug.cgi?id=8091
int solve1(int n) {
int a;
@@ -5114,7 +5103,7 @@ void test6189_2()
}
/***************************************************/
-// 8199
+// https://issues.dlang.org/show_bug.cgi?id=8199
version (D_InlineAsm_X86_64)
{
@@ -5126,6 +5115,8 @@ else version (D_InlineAsm_X86)
version = Check;
version (OSX)
enum Align = 0xC;
+// version (linux)
+// enum Align = 0xC;
}
void onFailure()
@@ -5167,6 +5158,21 @@ void test8199()
}
/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=13285
+void test13285()
+{
+ static struct S
+ {
+ ~this()
+ {
+ checkAlign();
+ }
+ }
+ S s; // correct alignment of RSP when calling ~this()
+ S(); // incorrect alignment
+}
+
+/***************************************************/
void test246()
{
@@ -5178,15 +5184,7 @@ void test246()
}
/***************************************************/
-
-double sqrt8454(double d) { return d/2; }
-void foo8454(cdouble m) {}
-void test8454() {
- foo8454(0 - sqrt8454(1.0) * 1i);
-}
-
-/***************************************************/
-// 8423
+// https://issues.dlang.org/show_bug.cgi?id=8423
struct S8423
{
@@ -5217,8 +5215,7 @@ public:
ubyte size = value < (0x7fU << 0 ) ? 1 :
value < (0x7fU << 14) ? 2 :
3;
- import std.stdio;
- writeln(size);
+ printf("%u\n", size);
assert(size == 2);
}
}
@@ -5336,7 +5333,7 @@ void test247()
}
/***************************************************/
-// 8340
+// https://issues.dlang.org/show_bug.cgi?id=8340
void test8340(){
byte[] ba = [1,2,3,4,5];
@@ -5356,7 +5353,7 @@ void test8340(){
}
/***************************************************/
-// 8376
+// https://issues.dlang.org/show_bug.cgi?id=8376
void test8376() {
int i = 0;
@@ -5392,7 +5389,7 @@ void test8987(){
}
/***************************************************/
-// 8796
+// https://issues.dlang.org/show_bug.cgi?id=8796
int* wrong8796(int* p)
{
@@ -5409,7 +5406,7 @@ void test8796()
}
/***************************************************/
-// 9171
+// https://issues.dlang.org/show_bug.cgi?id=9171
ulong bitcomb9171(ulong v)
{
@@ -5447,7 +5444,7 @@ void test9171()
}
/***************************************************/
-// 9248
+// https://issues.dlang.org/show_bug.cgi?id=9248
void test9248()
{
@@ -5458,7 +5455,7 @@ void test9248()
}
/***************************************************/
-// 14682
+// https://issues.dlang.org/show_bug.cgi?id=14682
void test14682a()
{
@@ -5562,8 +5559,9 @@ void test14682b()
{ auto x = [[[[]]]] ~ a3; static assert(is(typeof(x) == typeof(a3)[])); assert(x == r4b); } // fix
}
+
/***************************************************/
-// 9739
+// https://issues.dlang.org/show_bug.cgi?id=9739
class Foo9739
{
@@ -5580,7 +5578,7 @@ void test9739()
}
/***************************************************/
-// 6057
+// https://issues.dlang.org/show_bug.cgi?id=6057
void test6057()
{
enum Foo { A=1, B=2 }
@@ -5665,7 +5663,7 @@ void testreal_to_ulong()
static assert(false, "Test not implemented for this architecture");
auto v = r2ulong(adjust * 1.1);
- writefln("%s %s %s", adjust, v, u + u/10);
+ //writefln("%s %s %s", adjust, v, u + u/10);
//assert(v == 10145709240540253389UL);
}
@@ -5808,7 +5806,7 @@ void test251()
}
/***************************************************/
-// 9387
+// https://issues.dlang.org/show_bug.cgi?id=9387
void bug9387a(double x) { }
@@ -5866,10 +5864,15 @@ void test4414() {
assert(x == 7);
}
{
- auto x = bytes4414()[0..4];
+ auto u = bytes4414();
+ auto x = u[0..4];
if (x[0] != 7 || x[1] != 8 || x[2] != 9 || x[3] != 10)
assert(0);
}
+ assert(bytes4414()[0] == 7);
+ assert(bytes4414()[1] == 8);
+ assert(bytes4414()[2] == 9);
+ assert(bytes4414()[3] == 10);
}
/***************************************************/
@@ -5882,7 +5885,7 @@ void test9844() {
}
/***************************************************/
-// 10628
+// https://issues.dlang.org/show_bug.cgi?id=10628
abstract class B10628
{
@@ -5905,7 +5908,7 @@ void test10628()
}
/***************************************************/
-// 11265
+// https://issues.dlang.org/show_bug.cgi?id=11265
struct S11265
{
@@ -5956,8 +5959,6 @@ void test10633()
/***************************************************/
-import std.stdio;
-
void _assertEq (ubyte lhs, short rhs, string msg, string file, size_t line)
{
immutable result = lhs == rhs;
@@ -5966,9 +5967,9 @@ void _assertEq (ubyte lhs, short rhs, string msg, string file, size_t line)
{
string op = "==";
if(msg.length > 0)
- writefln(`_assertEq failed: [%s] is not [%s].`, lhs, rhs);
+ printf("_assertEq failed: [%u] is not [%d].\n", lhs, rhs);
else
- writefln(`_assertEq failed: [%s] is not [%s]: %s`, lhs, rhs, msg);
+ printf("_assertEq failed: [%u] is not [%d]: %.*s\n", lhs, rhs, cast(int)msg.length, msg.ptr);
}
assert(result);
@@ -5998,7 +5999,7 @@ void test10642()
}
/***************************************************/
-// 11581
+// https://issues.dlang.org/show_bug.cgi?id=11581
alias TT11581(T...) = T;
@@ -6036,7 +6037,7 @@ void test11581()
}
/***************************************************/
-// 7436
+// https://issues.dlang.org/show_bug.cgi?id=7436
void test7436()
{
@@ -6047,7 +6048,7 @@ void test7436()
}
/***************************************************/
-// 12138
+// https://issues.dlang.org/show_bug.cgi?id=12138
struct S12138
{
@@ -6064,7 +6065,7 @@ label:
}
/***************************************************/
-// 14430
+// https://issues.dlang.org/show_bug.cgi?id=14430
void setCookie(long x = 1L << 32L, string y = null){
assert(y.ptr is null);
@@ -6075,7 +6076,7 @@ void test14430(){
}
/***************************************************/
-// 14510
+// https://issues.dlang.org/show_bug.cgi?id=14510
alias Vector14510 = ulong[3];
@@ -6118,7 +6119,7 @@ double entropy2(double[] probs)
__gshared int x;
++x;
if (!p) continue;
- import std.math : log2;
+ import core.stdc.math : log2;
result -= p * log2(p);
}
return result;
@@ -6126,7 +6127,6 @@ double entropy2(double[] probs)
void test16530()
{
- import std.stdio;
if (entropy2([1.0, 0, 0]) != 0.0)
assert(0);
}
@@ -6144,9 +6144,42 @@ void test252()
}
/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7997
+
+void test7997()
+{
+ __gshared int[0] foos;
+ foreach (f; foos) {}
+}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=5332
+
+int[0] arr5332;
+
+void test5332()
+{
+ auto a = arr5332;
+}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=11742
+
+const int x11472 = void;
+
+static this() { x11472 = 10; }
+
+void test11472()
+{
+ assert(x11472 == 10);
+}
+
+
+/***************************************************/
int main()
{
+ checkAlign();
test1();
test2();
test3();
@@ -6188,7 +6221,6 @@ int main()
test41();
test42();
test43();
- test44();
test45();
test46();
test47();
@@ -6404,7 +6436,6 @@ int main()
test6189_2();
test8199();
test246();
- test8454();
test8423();
test8496();
test8840();
@@ -6443,8 +6474,11 @@ int main()
test16027();
test16530();
test252();
+ test7997();
+ test5332();
+ test11472();
+ test13285();
- writefln("Success");
+ printf("Success\n");
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/test42a.d b/gcc/testsuite/gdc.test/runnable/test42a.d
index 0bb98efcff0..10c09b3e665 100644
--- a/gcc/testsuite/gdc.test/runnable/test42a.d
+++ b/gcc/testsuite/gdc.test/runnable/test42a.d
@@ -1,4 +1,10 @@
-// PERMUTE_ARGS:
+/*
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+Success
+---
+*/
module test42;
@@ -312,4 +318,3 @@ int main()
printf("Success\n");
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/test435.d b/gcc/testsuite/gdc.test/runnable/test435.d
index 4dcabd1fcff..700e9fd0eba 100644
--- a/gcc/testsuite/gdc.test/runnable/test435.d
+++ b/gcc/testsuite/gdc.test/runnable/test435.d
@@ -1,3 +1,9 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
import core.stdc.stdio;
class A
@@ -65,7 +71,7 @@ void test1()
//------------------------------------------------------------------------------
-// bug 435.
+// https://issues.dlang.org/show_bug.cgi?id=435.
class B
{
int i;
@@ -90,7 +96,7 @@ void test2()
//------------------------------------------------------------------------------
-// bug 4905
+// https://issues.dlang.org/show_bug.cgi?id=4905
class C2
{
string x;
@@ -114,7 +120,7 @@ void test3()
//------------------------------------------------------------------------------
-// bug 4531 test case 2
+// https://issues.dlang.org/show_bug.cgi?id=4531 test case 2
class MyError : Exception
{
this(T...)(T msg)
@@ -139,5 +145,3 @@ void main()
test4();
printf("Success\n");
}
-
-
diff --git a/gcc/testsuite/gdc.test/runnable/test45.d b/gcc/testsuite/gdc.test/runnable/test45.d
index 49fa548351a..d36b378e250 100644
--- a/gcc/testsuite/gdc.test/runnable/test45.d
+++ b/gcc/testsuite/gdc.test/runnable/test45.d
@@ -1,5 +1,17 @@
-// EXTRA_SOURCES: imports/test45a.d imports/test45b.d
-// PERMUTE_ARGS:
+/*
+EXTRA_SOURCES: imports/test45a.d imports/test45b.d
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+foo()
+foo(int)
+foo()
+foo()
+foo(int)
+bar(t)
+bar(t,i)
+---
+*/
import imports.test45a;
import imports.test45b;
diff --git a/gcc/testsuite/gdc.test/runnable/test48.d b/gcc/testsuite/gdc.test/runnable/test48.d
index 827d681259b..36160e5637d 100644
--- a/gcc/testsuite/gdc.test/runnable/test48.d
+++ b/gcc/testsuite/gdc.test/runnable/test48.d
@@ -1,7 +1,7 @@
// EXTRA_SOURCES: imports/test48a.d
// PERMUTE_ARGS:
-import std.stdio;
+import core.stdc.stdio;
import imports.test48a;
void main()
@@ -16,28 +16,28 @@ void main()
printf("i = %d\n", i);
assert(i == 6);
- printf("a = %d %d %d\n", S.tupleof.offsetof);
+ printf("a = %zd %zd %zd\n", S.tupleof.offsetof);
auto o = S.tupleof.offsetof;
assert(o[0] == 0);
assert(o[1] == 4);
assert(o[2] == 8);
- printf("a = %d %d %d\n", S.tupleof[0].offsetof, S.tupleof[1].offsetof, S.tupleof[2].offsetof);
+ printf("a = %zd %zd %zd\n", S.tupleof[0].offsetof, S.tupleof[1].offsetof, S.tupleof[2].offsetof);
assert(S.tupleof[0].offsetof == 0);
assert(S.tupleof[1].offsetof == 4);
assert(S.tupleof[2].offsetof == 8);
auto offset0 = cast(void*)&s.tupleof[0] - cast(void*)&s;
- printf("offset0 = %d\n", offset0);
+ printf("offset0 = %td\n", offset0);
assert(offset0 == 0);
auto offset1 = cast(void*)&s.tupleof[1] - cast(void*)&s;
- printf("offset1 = %d\n", offset1);
+ printf("offset1 = %td\n", offset1);
assert(offset1 == 4);
auto offset2 = cast(void*)&s.tupleof[2] - cast(void*)&s;
- printf("offset2 = %d\n", offset2);
+ printf("offset2 = %td\n", offset2);
assert(offset2 == 8);
- int t1[S.tupleof.offsetof[1]];
+ int[S.tupleof.offsetof[1]] t1;
assert(t1.length == 4);
}
diff --git a/gcc/testsuite/gdc.test/runnable/test49.d b/gcc/testsuite/gdc.test/runnable/test49.d
index fc80d61fd99..641d196b59b 100644
--- a/gcc/testsuite/gdc.test/runnable/test49.d
+++ b/gcc/testsuite/gdc.test/runnable/test49.d
@@ -1,6 +1,13 @@
-// COMPILE_SEPARATELY
-// EXTRA_SOURCES: imports/test49a.d
-// PERMUTE_ARGS:
+/*
+COMPILE_SEPARATELY
+EXTRA_SOURCES: imports/test49a.d
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+static this()
+static ~this()
+---
+*/
import imports.test49a;
@@ -9,4 +16,3 @@ alias Foo!(int) foo;
void main()
{
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/test5.d b/gcc/testsuite/gdc.test/runnable/test5.d
index 7d9769dc03d..a14b4b13271 100644
--- a/gcc/testsuite/gdc.test/runnable/test5.d
+++ b/gcc/testsuite/gdc.test/runnable/test5.d
@@ -40,7 +40,7 @@ class bar : foo
int def(foo f)
{
- printf("def(%p), %p\n", f, (cast(int*)f)[0]);
+ printf("def(%p), %d\n", f, (cast(int*)f)[0]);
assert(f.testc(3) == 50);
assert(f.testd(7) == 54);
assert(f.testd(10) == 57);
@@ -49,7 +49,7 @@ int def(foo f)
void abc(bar b)
{
- printf("abc(%p), %p\n", b, (cast(int*)b)[3]);
+ printf("abc(%p), %d\n", b, (cast(int*)b)[3]);
def(b);
}
@@ -57,8 +57,8 @@ int main()
{
bar b = new bar();
- printf("b.size = x%x\n", b.classinfo.initializer.length);
- printf("bar.size = x%x\n", bar.classinfo.initializer.length);
+ printf("b.size = x%zx\n", b.classinfo.initializer.length);
+ printf("bar.size = x%zx\n", bar.classinfo.initializer.length);
assert(b.classinfo.initializer.length == bar.classinfo.initializer.length);
abc(b);
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/test52.d b/gcc/testsuite/gdc.test/runnable/test52.d
index 2a906d0f454..85b8d6725d7 100644
--- a/gcc/testsuite/gdc.test/runnable/test52.d
+++ b/gcc/testsuite/gdc.test/runnable/test52.d
@@ -1,29 +1,36 @@
-// PERMUTE_ARGS:
+/*
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+count = 3
+---
+*/
-// 2311
+// https://issues.dlang.org/show_bug.cgi?id=2311
-extern(C)
+extern(C) int printf(const char*, ...);
+
+__gshared ulong count;
+
+shared static ~this()
{
- void exit(int);
- int printf(const char*, ...);
+ printf("count = %llu\n", count);
+ assert(count == 3);
}
-struct X()
+template X(uint idx)
{
- static this()
- {
- printf("this()\n");
- }
- static ~this()
- {
- printf("~this()\n");
- exit(0);
- }
+ static ~this()
+ {
+ assert(count == idx);
+ ++count;
+ }
}
-static ~this()
+void main()
{
- printf("g: ~this()\n");
+ // Instantiate module destructors in reverse order
+ alias x = X!(2);
+ alias y = X!(1);
+ alias z = X!(0);
}
-
-int main() { alias X!() x; return 1; }
diff --git a/gcc/testsuite/gdc.test/runnable/test5305.d b/gcc/testsuite/gdc.test/runnable/test5305.d
deleted file mode 100644
index ff52936a8dc..00000000000
--- a/gcc/testsuite/gdc.test/runnable/test5305.d
+++ /dev/null
@@ -1,8 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-// https://issues.dlang.org/show_bug.cgi?id=5305
-
-import std.math;
-void map(real function(real) f) { }
-int main() { map(&sqrt); return 0; }
-
-
diff --git a/gcc/testsuite/gdc.test/runnable/test60.d b/gcc/testsuite/gdc.test/runnable/test60.d
deleted file mode 100644
index cfd92b225ca..00000000000
--- a/gcc/testsuite/gdc.test/runnable/test60.d
+++ /dev/null
@@ -1,23 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-import std.stdio;
-import std.algorithm;
-
-void test1()
-{
- int[] a = [1,2,3,4,5];
- writeln( remove!("a < 3")(a) );
-}
-
-void test2()
-{
- auto arr = [1,2,3,4,5];
- auto m = map!"a + 1"(filter!"a < 4"(arr));
-}
-
-void main()
-{
- test1();
- test2();
-
- writeln("Success");
-}
diff --git a/gcc/testsuite/gdc.test/runnable/test61.d b/gcc/testsuite/gdc.test/runnable/test61.d
index 6e097699127..016e9a0d237 100644
--- a/gcc/testsuite/gdc.test/runnable/test61.d
+++ b/gcc/testsuite/gdc.test/runnable/test61.d
@@ -1,7 +1,7 @@
// PERMUTE_ARGS:
// EXTRA_SOURCES: imports/test61a.d
-// Bugzilla 6556
+// https://issues.dlang.org/show_bug.cgi?id=6556
debug=BUG;
diff --git a/gcc/testsuite/gdc.test/runnable/test6795.d b/gcc/testsuite/gdc.test/runnable/test6795.d
new file mode 100644
index 00000000000..7e44fd4ad7a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test6795.d
@@ -0,0 +1,26 @@
+// https://issues.dlang.org/show_bug.cgi?id=6795
+void check6795()
+{
+ enum int[] array = [0];
+ // PostExp
+ assert(array[0]++ == 0);
+ assert(array[0]-- == 0);
+ // PreExp
+ assert(++array[0] == 1);
+ assert(--array[0] == -1);
+ // BinAssignExp
+ assert((array[0] += 3) == 3);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21312
+void check21312()
+{
+ auto p = &[123][0];
+ assert(*p == 123);
+}
+
+void main()
+{
+ check6795();
+ check21312();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test711.d b/gcc/testsuite/gdc.test/runnable/test711.d
new file mode 100644
index 00000000000..b9fae4e749e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test711.d
@@ -0,0 +1,51 @@
+// https://issues.dlang.org/show_bug.cgi?id=711
+string result;
+
+template Mixer()
+{
+ override void test()
+ {
+ result ~= "A";
+ }
+}
+
+class Foo
+{
+ void test()
+ {
+ result ~= "B";
+ }
+}
+
+class Bar : Foo
+{
+ mixin Mixer!() mixer;
+ override void test()
+ {
+ result ~= "C";
+ mixer.test();
+ }
+}
+
+class Bar2 : Foo
+{
+ override void test()
+ {
+ result ~= "C";
+ mixer.test();
+ }
+ mixin Mixer!() mixer;
+}
+
+void main()
+{
+ Bar f = new Bar();
+ f.test();
+ assert(result == "CA");
+
+ result = "";
+
+ Bar2 f2 = new Bar2();
+ f2.test();
+ assert(result == "CA");
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test7452.d b/gcc/testsuite/gdc.test/runnable/test7452.d
index cc0452907d6..de591f39e76 100644
--- a/gcc/testsuite/gdc.test/runnable/test7452.d
+++ b/gcc/testsuite/gdc.test/runnable/test7452.d
@@ -1,3 +1,9 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
import core.stdc.stdio : printf;
//------------------------------------------------------------------------------
@@ -71,5 +77,3 @@ int main()
printf("Success\n");
return 0;
}
-
-
diff --git a/gcc/testsuite/gdc.test/runnable/test7511.d b/gcc/testsuite/gdc.test/runnable/test7511.d
index 0d19f5466dd..bb879957d4c 100644
--- a/gcc/testsuite/gdc.test/runnable/test7511.d
+++ b/gcc/testsuite/gdc.test/runnable/test7511.d
@@ -1,7 +1,13 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern(C) int printf(const char*, ...);
/**********************************/
-// 7511
+// https://issues.dlang.org/show_bug.cgi?id=7511
struct S7511(T)
{
@@ -186,7 +192,7 @@ void test7511d()
}
/**********************************/
-// 9952
+// https://issues.dlang.org/show_bug.cgi?id=9952
@system void writeln9952(int) {} // impure throwable
@@ -214,7 +220,7 @@ void test9952()
}
/**********************************/
-// 10373
+// https://issues.dlang.org/show_bug.cgi?id=10373
template isMutable10373(T)
{
@@ -294,7 +300,7 @@ void test10373()
}
/**********************************/
-// 10329
+// https://issues.dlang.org/show_bug.cgi?id=10329
auto foo10329(T)(T arg)
{
@@ -326,7 +332,7 @@ void test10329() pure nothrow @safe
}
/**********************************/
-// 11896
+// https://issues.dlang.org/show_bug.cgi?id=11896
class Foo11896a(T = int)
{
@@ -393,7 +399,7 @@ void test11896c()
}
/**********************************/
-// 12392
+// https://issues.dlang.org/show_bug.cgi?id=12392
void f12392(T)() {}
alias fa12392 = f12392;
diff --git a/gcc/testsuite/gdc.test/runnable/test7932.d b/gcc/testsuite/gdc.test/runnable/test7932.d
index 245cf178648..22aa279ce5b 100644
--- a/gcc/testsuite/gdc.test/runnable/test7932.d
+++ b/gcc/testsuite/gdc.test/runnable/test7932.d
@@ -1,6 +1,6 @@
-// 7932
+// https://issues.dlang.org/show_bug.cgi?id=7932
-import std.stdio;
+import core.stdc.stdio;
size_t N;
@@ -13,7 +13,7 @@ class C
cast(void*) this, &n, n);
assert (N == n);
}
- body
+ do
{
int dummy;
//printf("\n");
diff --git a/gcc/testsuite/gdc.test/runnable/test8.d b/gcc/testsuite/gdc.test/runnable/test8.d
index 4927bbc97ed..7d66eb6c623 100644
--- a/gcc/testsuite/gdc.test/runnable/test8.d
+++ b/gcc/testsuite/gdc.test/runnable/test8.d
@@ -1,3 +1,9 @@
+/*
+TEST_OUTPUT:
+---
+runnable/test8.d(261): Deprecation: identity comparison of static arrays implicitly coerces them to slices, which are compared by reference
+---
+*/
module testxxx8;
@@ -167,28 +173,6 @@ void test8()
/***********************************/
-void test9()
-{
- ireal imag = 2.5i;
- //printf ("test of imag*imag = %Lf\n",imag*imag);
- real f = imag * imag;
- assert(f == -6.25);
-}
-
-/***********************************/
-
-void test10()
-{
- creal z = 1 + 2.5i;
- real e = z.im;
-
- printf ("e = %Lf\n", e);
- assert(e == 2.5);
-}
-
-
-/***********************************/
-
class Foo11
{
public:
@@ -316,11 +300,11 @@ void test17()
string s;
s = passString();
- printf("passString() = %.*s\n", s.length, s.ptr);
+ printf("passString() = %.*s\n", cast(int)s.length, s.ptr);
assert(s == "First stringConcatenated with second");
s = butThisWorks();
- printf("butThisWorks() = %.*s\n", s.length, s.ptr);
+ printf("butThisWorks() = %.*s\n", cast(int)s.length, s.ptr);
assert(s == "Third stringConcatenated with fourth");
}
@@ -453,7 +437,7 @@ in
out (result)
{
}
-body
+do
{ int i = 5;
while (i)
@@ -607,21 +591,6 @@ void test34()
assert(b[i][j] == 16);
}
-
-/***********************************/
-
-void test35()
-{
- ifloat b = cast(ifloat)1i;
- assert(b == 1.0i);
-
- ifloat c = 2fi;
- assert(c == 2.0i);
-
- c = 0fi;
- assert(c == 0i);
-}
-
/***********************************/
string itoa(int i)
@@ -646,7 +615,7 @@ void test36()
{
string s = testa36(26, 47, "a", "b", "c");
- printf("s = '%.*s'\n", s.length, s.ptr);
+ printf("s = '%.*s'\n", cast(int)s.length, s.ptr);
assert(s == "string 0;26string 1;47string 2;26string 3;");
}
@@ -668,7 +637,7 @@ void test37()
void test38()
{
int n = atoi("1");
- static char flags[8192 + 1];
+ static char[8192 + 1] flags;
long i, k;
int count = 0;
@@ -698,7 +667,7 @@ void test38()
}
catch(Throwable)
{
- printf("Exception: %d\n", k);
+ printf("Exception: %lld\n", k);
assert(0);
}
}
@@ -776,21 +745,6 @@ void test42()
/***********************************/
-void test43()
-{
- creal C,Cj;
- real y1,x1;
-
- C = x1 + y1*1i + Cj;
- C = 1i*y1 + x1 + Cj;
- C = Cj + 1i*y1 + x1;
- C = y1*1i + Cj + x1;
- C = 1i*y1 + Cj;
- C = Cj + 1i*y1;
-}
-
-/***********************************/
-
int x44;
class A44 {
@@ -883,46 +837,6 @@ void test50()
/***********************************/
-/+
-void foo51(creal a)
-{
- writeln(a);
- assert(a == -8i);
-}
-
-void test51()
-{
- cdouble a = (2-2i)*(2-2i);
-
- // This fails
- writeln(a);
- assert(a == -8i);
-
- // This works
- writeln((2-2i)*(2-2i));
-
- // This fails
- foo51((2-2i)*(2-2i));
-}
-+/
-
-void foo51(creal a)
-{
- assert(a == -8i);
-}
-
-void test51()
-{
- assert((2-2i)*(2-2i) == -8i);
-
- cdouble a = (2-2i)*(2-2i);
- assert(a == -8i);
-
- foo51((2-2i)*(2-2i));
-}
-
-/***********************************/
-
int main()
{
test1();
@@ -933,8 +847,6 @@ int main()
test6();
test7();
test8();
- test9();
- test10();
test11();
test12();
test13();
@@ -956,21 +868,18 @@ int main()
test32();
test33();
test34();
- test35();
test36();
test37();
test38();
test39();
test40();
test42();
- test43();
test44();
test45();
test46();
test48();
test49();
test50();
- test51();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/test809.d b/gcc/testsuite/gdc.test/runnable/test809.d
new file mode 100644
index 00000000000..efe4a3704c5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test809.d
@@ -0,0 +1,13 @@
+// https://issues.dlang.org/show_bug.cgi?id=809
+void test(lazy int dg)
+{
+ int delegate() dg_ = &dg;
+ assert(dg_() == 7);
+ assert(dg == dg_());
+}
+
+void main()
+{
+ int a = 7;
+ test(a);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test8544.d b/gcc/testsuite/gdc.test/runnable/test8544.d
index 7c47e867db6..0b8b86bd99f 100644
--- a/gcc/testsuite/gdc.test/runnable/test8544.d
+++ b/gcc/testsuite/gdc.test/runnable/test8544.d
@@ -1,6 +1,6 @@
// EXECUTE_ARGS: foo bar doo
// PERMUTE_ARGS:
-import std.stdio;
+import core.stdc.stdio;
import std.conv;
import core.runtime;
diff --git a/gcc/testsuite/gdc.test/runnable/test9259.d b/gcc/testsuite/gdc.test/runnable/test9259.d
index f870f63a492..abad02b01d4 100644
--- a/gcc/testsuite/gdc.test/runnable/test9259.d
+++ b/gcc/testsuite/gdc.test/runnable/test9259.d
@@ -1,4 +1,5 @@
-// PERMUTE_ARGS: -inline -release -g -O -d -dw -de
+// REQUIRED_ARGS: -de
+// PERMUTE_ARGS: -inline -release -g -O
void test(int*[] arr...)
{
diff --git a/gcc/testsuite/gdc.test/runnable/test9271.d b/gcc/testsuite/gdc.test/runnable/test9271.d
index 3ce7fc8f189..b10b575ac4e 100644
--- a/gcc/testsuite/gdc.test/runnable/test9271.d
+++ b/gcc/testsuite/gdc.test/runnable/test9271.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-
+// EXTRA_FILES: imports/test9271a.d
import algorithm = imports.test9271a;
bool any(alias predicate, Range)(Range range)
diff --git a/gcc/testsuite/gdc.test/runnable/testCopyCtor.d b/gcc/testsuite/gdc.test/runnable/testCopyCtor.d
new file mode 100644
index 00000000000..9d86bdcd3e2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/testCopyCtor.d
@@ -0,0 +1,159 @@
+string result;
+
+struct A
+{
+ this(ref A rhs)
+ {
+ result ~= "A";
+ }
+ this(ref immutable A rhs)
+ {
+ result ~= "B";
+ }
+ this(ref const A rhs)
+ {
+ result ~= "C";
+ }
+ this(ref A rhs) immutable
+ {
+ result ~= "D";
+ }
+ this(ref const A rhs) shared
+ {
+ result ~= "E";
+ }
+ this(ref A rhs) shared
+ {
+ result ~= "F";
+ }
+ this(ref shared A rhs) immutable
+ {
+ result ~= "G";
+ }
+ this(ref shared A rhs) shared
+ {
+ result ~= "H";
+ }
+}
+
+// copy constructor correctly uses function declaration overload resolution
+void test1()
+{
+ result = "";
+ A a;
+ A a1 = a;
+ immutable A a2 = a;
+ const A a3 = a2;
+ shared A a4 = a3;
+ A a5 = a3;
+ assert(result == "ADBEC");
+}
+
+// copy constructor has priority over alias this
+struct B
+{
+ B fun(immutable B)
+ {
+ return B();
+ }
+
+ this(ref immutable B rhs)
+ {
+ result ~= "A";
+ }
+ alias fun this;
+}
+
+void test2()
+{
+ result = "";
+ immutable B a;
+ B a1 = a;
+ assert(result == "A");
+}
+
+// arguments and return values correctly call the copy constructor
+shared(A) fun(A x)
+{
+ return x;
+}
+
+immutable(A) fun2(shared A x)
+{
+ return x;
+}
+
+void test3()
+{
+ result = "";
+ A a1;
+ shared A a2 = fun(a1);
+ immutable A a3 = fun2(a2);
+ assert(result == "AFHG");
+}
+
+// nested structs
+int fun()
+{
+ int x = 1;
+ struct A
+ {
+ struct B
+ {
+ int x2 = 2;
+ }
+
+ B b;
+ int x1;
+
+ this(int x)
+ {
+ this.x1 = x;
+ b = B(3);
+ }
+ this(ref A rhs)
+ {
+ this.x1 = rhs.x1 + rhs.b.x2 + x;
+ }
+ }
+
+ A a = A(2);
+ A b = a;
+ return b.x1;
+}
+
+void test4()
+{
+ assert(fun() == 6);
+}
+
+// generated copy constructor
+struct X
+{
+ this(ref inout(X) rhs) inout
+ {
+ result ~= "A";
+ }
+}
+
+struct Y
+{
+ X a;
+}
+
+void test5()
+{
+ result = "";
+ Y b1;
+ Y b2 = b1;
+ assert(result == "A");
+}
+
+void main()
+{
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test_dip1006.d b/gcc/testsuite/gdc.test/runnable/test_dip1006.d
new file mode 100644
index 00000000000..c785e2152b5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test_dip1006.d
@@ -0,0 +1,38 @@
+// REQUIRED_ARGS: -check=in=off -check=out=off -check=invariant=off
+// PERMUTE_ARGS:
+class C
+{
+ int foo(int a)
+ in { assert(a != 0); } // skipped
+ out(res) { assert(res != 0); } // skipped
+ do
+ {
+ return a;
+ }
+
+ invariant // skipped
+ {
+ assert(false);
+ }
+
+ void bar(int a)
+ {
+ assert(a != 0); // triggered
+ }
+}
+
+void main()
+{
+ import core.exception : AssertError;
+
+ auto c = new C;
+ c.foo(0);
+
+ bool catched;
+ try
+ c.bar(0);
+ catch (AssertError e)
+ catched = true;
+ if (!catched)
+ assert(0);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test_dip1006b.d b/gcc/testsuite/gdc.test/runnable/test_dip1006b.d
new file mode 100644
index 00000000000..e1677685a11
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test_dip1006b.d
@@ -0,0 +1,35 @@
+// REQUIRED_ARGS: -check=in=off -check=invariant=off
+// PERMUTE_ARGS:
+class C
+{
+ int foo(int a)
+ in { assert(a != 0); } // skipped
+ out(res) { assert(res != 0, "out"); } // triggered
+ do
+ {
+ return a;
+ }
+
+ invariant // skipped
+ {
+ assert(false);
+ }
+}
+
+void main()
+{
+ import core.exception : AssertError;
+
+ auto c = new C;
+ bool catched;
+ try
+ c.foo(0);
+ catch (AssertError e)
+ {
+ assert(e.msg == "out");
+ catched = e.msg == "out";
+ }
+
+ if (!catched)
+ assert(0);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/testaa.d b/gcc/testsuite/gdc.test/runnable/testaa.d
index 34cc705c08d..75d541438cb 100644
--- a/gcc/testsuite/gdc.test/runnable/testaa.d
+++ b/gcc/testsuite/gdc.test/runnable/testaa.d
@@ -1,4 +1,3 @@
-// RUNNABLE_PHOBOS_TEST
// PERMUTE_ARGS: -fPIC
/* Test associative arrays */
@@ -7,11 +6,10 @@ extern(C) int printf(const char*, ...);
extern(C) int memcmp(const void *s1, const void *s2, size_t n);
import core.memory; // for GC.collect
-import std.random; // for uniform random numbers
/************************************************/
-int nametable[char[]];
+int[char[]] nametable;
void insert(string name, int value)
{
@@ -272,9 +270,9 @@ struct S12
void test12()
{
S12[] x;
- printf("size %d\n",S12.sizeof);
- printf("align %d\n",S12.alignof);
- printf("offset %d\n",S12.description.offsetof);
+ printf("size %zd\n",S12.sizeof);
+ printf("align %zd\n",S12.alignof);
+ printf("offset %zd\n",S12.description.offsetof);
for (int i=0;i<3;i++) {
S12 s;
@@ -294,8 +292,8 @@ void test12()
x ~= s;
*/
GC.collect();
- printf("%.*s\n",x[0].font_face.length,x[0].font_face.ptr);
- printf("%.*s\n",x[1].font_face.length,x[1].font_face.ptr);
+ printf("%.*s\n", cast(int)x[0].font_face.length, x[0].font_face.ptr);
+ printf("%.*s\n", cast(int)x[1].font_face.length, x[1].font_face.ptr);
}
@@ -316,7 +314,7 @@ void test13()
string[] key = array.keys;
assert(key.length==3);
- bool have[3];
+ bool[3] have;
assert(!have[0]);
assert(!have[1]);
@@ -424,13 +422,9 @@ void test16()
{
int[int] aa;
- Random gen;
for (int i = 0; i < 50000; i++)
{
- int key = uniform(0, int.max, gen);
- int value = uniform(0, int.max, gen);
-
- aa[key] = value;
+ aa[i] = i;
}
int[] keys = aa.keys;
@@ -442,7 +436,7 @@ void test16()
assert(k in aa);
j += aa[k];
}
- printf("test16 = %d\n", j);
+ assert(j == 1249975000);
int m;
foreach (k, v; aa)
@@ -471,10 +465,7 @@ void test16()
for (int i = 0; i < 1000; i++)
{
- int key2 = uniform(0, int.max, gen);
- int value2 = uniform(0, int.max, gen);
-
- aa[key2] = value2;
+ aa[i] = i;
}
foreach(k; aa)
{
@@ -494,7 +485,7 @@ void dummy17()
{
}
-int bb17[string];
+int[string] bb17;
int foo17()
{
@@ -558,14 +549,14 @@ void test19()
printf("%d\n", keys[1]);
auto vs = aa.values;
- printf("%.*s\n", vs[0].length, vs[0].ptr);
- printf("%.*s\n", vs[1].length, vs[1].ptr);
+ printf("%.*s\n", cast(int)vs[0].length, vs[0].ptr);
+ printf("%.*s\n", cast(int)vs[1].length, vs[1].ptr);
string aavalue_typeid = typeid(typeof(aa.values)).toString();
- printf("%.*s\n", aavalue_typeid.length, aavalue_typeid.ptr);
+ printf("%.*s\n", cast(int)aavalue_typeid.length, aavalue_typeid.ptr);
- printf("%.*s\n", aa[3].length, aa[3].ptr);
- printf("%.*s\n", aa[4].length, aa[4].ptr);
+ printf("%.*s\n", cast(int)aa[3].length, aa[3].ptr);
+ printf("%.*s\n", cast(int)aa[4].length, aa[4].ptr);
}
/************************************************/
@@ -582,14 +573,14 @@ void test20()
printf("%d\n", keys[1]);
auto values = aa.values;
- printf("%.*s\n", values[0].length, values[0].ptr);
- printf("%.*s\n", values[1].length, values[1].ptr);
+ printf("%.*s\n", cast(int)values[0].length, values[0].ptr);
+ printf("%.*s\n", cast(int)values[1].length, values[1].ptr);
string aavalue_typeid = typeid(typeof(aa.values)).toString();
- printf("%.*s\n", aavalue_typeid.length, aavalue_typeid.ptr);
+ printf("%.*s\n", cast(int)aavalue_typeid.length, aavalue_typeid.ptr);
- printf("%.*s\n", aa[3].length, aa[3].ptr);
- printf("%.*s\n", aa[4].length, aa[4].ptr);
+ printf("%.*s\n", cast(int)aa[3].length, aa[3].ptr);
+ printf("%.*s\n", cast(int)aa[4].length, aa[4].ptr);
}
/************************************************/
@@ -783,7 +774,7 @@ void test4826c()
}
/************************************************/
-// 5131
+// https://issues.dlang.org/show_bug.cgi?id=5131
struct ICE5131
{
@@ -799,7 +790,7 @@ void test5131()
}
/************************************************/
-// 6178
+// https://issues.dlang.org/show_bug.cgi?id=6178
bool test6178a()
{
@@ -1036,7 +1027,7 @@ void test6178x()
}
/************************************************/
-// 10595
+// https://issues.dlang.org/show_bug.cgi?id=10595
struct S10595
{
@@ -1073,14 +1064,13 @@ void test10595()
Wrap10595[int] wrap;
wrap[0] = Wrap10595();
- wrap[0] = 0; // note: using 'alias this' to assign
assert(wrap[0].s.test()); // failure
}
}
/************************************************/
-// 10970
+// https://issues.dlang.org/show_bug.cgi?id=10970
struct RefCounted10970(T) //if (!is(T == class))
{
@@ -1119,7 +1109,7 @@ void test10970()
}
/************************************************/
-// 6433
+// https://issues.dlang.org/show_bug.cgi?id=6433
void test6433()
{
@@ -1138,7 +1128,7 @@ void test6433()
}
/************************************************/
-// 6612
+// https://issues.dlang.org/show_bug.cgi?id=6612
void test6612()
{
@@ -1150,7 +1140,7 @@ void test6612()
}
/************************************************/
-// 7365
+// https://issues.dlang.org/show_bug.cgi?id=7365
struct TickDuration
{
@@ -1197,7 +1187,7 @@ void test6799()
}
/************************************************/
-// 11359
+// https://issues.dlang.org/show_bug.cgi?id=11359
void test11359()
{
@@ -1213,7 +1203,7 @@ void test11359()
}
/************************************************/
-// 11730
+// https://issues.dlang.org/show_bug.cgi?id=11730
struct SysTime11730
{
@@ -1246,7 +1236,7 @@ void test11730()
}
/************************************************/
-// 14089
+// https://issues.dlang.org/show_bug.cgi?id=14089
struct S14089
{
@@ -1263,7 +1253,7 @@ void test14089()
}
/************************************************/
-// 14144
+// https://issues.dlang.org/show_bug.cgi?id=14144
struct JSON14144
{
@@ -1291,7 +1281,7 @@ void test14144()
}
/************************************************/
-// 14321
+// https://issues.dlang.org/show_bug.cgi?id=14321
void test14321()
{
@@ -1330,6 +1320,20 @@ void test14321()
}
/************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19112
+
+void test19112()
+{
+ int[int[1]] aa;
+ aa[[2]] = 1;
+ assert([2] in aa);
+
+ int[int[]] aa2 = [[1, 2, 3]: 4];
+ int[3] k = [1, 2, 3];
+ assert(*(k in aa2) == 4);
+}
+
+/************************************************/
int main()
{
@@ -1380,6 +1384,7 @@ int main()
test11730();
test14089();
test14321();
+ test19112();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/testaa2.d b/gcc/testsuite/gdc.test/runnable/testaa2.d
index 17de1679d8b..ba8ceecc55c 100644
--- a/gcc/testsuite/gdc.test/runnable/testaa2.d
+++ b/gcc/testsuite/gdc.test/runnable/testaa2.d
@@ -1,16 +1,26 @@
-// RUNNABLE_PHOBOS_TEST
-// PERMUTE_ARGS:
+/*
+RUNNABLE_PHOBOS_TEST
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+foo()
+foo() 2
+foo() 3
+foo() 4
+Success
+---
+*/
extern(C) int printf(const char*, ...);
/************************************************/
-int a[string];
+int[string] a;
size_t foo(immutable char [3] s)
{
printf("foo()\n");
- int b[string];
+ int[string] b;
string[] key;
int[] value;
printf("foo() 2\n");
@@ -23,7 +33,7 @@ size_t foo(immutable char [3] s)
void foo2()
{
- int c[string];
+ int[string] c;
string[] key;
int[] value;
int i;
@@ -51,10 +61,9 @@ void foo2()
value = c.values;
assert(value.length == 2);
- for (i = 0; i < key.length; i++)
- {
- printf("c[\"%.*s\"] = %d\n", key[i].length, key[i].ptr, value[i]);
- }
+ const fooIndex = key[1] == "foo";
+ assert(key[fooIndex] == "foo" && value[fooIndex] == 3);
+ assert(key[1 - fooIndex] == "bar" && value[1 - fooIndex] == 4);
assert("foo" in c);
c.remove("foo");
@@ -70,7 +79,6 @@ void foo2()
void testaa()
{
size_t i = foo("abc");
- printf("i = %d\n", i);
assert(i == 0);
foo2();
@@ -107,7 +115,7 @@ void test4523()
}
/************************************************/
-// 3825
+// https://issues.dlang.org/show_bug.cgi?id=3825
import std.math; // necessary for ^^=
void test3825()
@@ -257,7 +265,7 @@ void test3825x()
}
/************************************************/
-// 10106
+// https://issues.dlang.org/show_bug.cgi?id=10106
struct GcPolicy10106 {}
diff --git a/gcc/testsuite/gdc.test/runnable/testaa3.d b/gcc/testsuite/gdc.test/runnable/testaa3.d
index 3e3ae96ba02..4aac1fba1a4 100644
--- a/gcc/testsuite/gdc.test/runnable/testaa3.d
+++ b/gcc/testsuite/gdc.test/runnable/testaa3.d
@@ -315,7 +315,7 @@ int[int] testRetx()
void aafunc(int[int] aa) {}
/***************************************************/
-// 12214
+// https://issues.dlang.org/show_bug.cgi?id=12214
void test12214() pure nothrow
{
@@ -324,7 +324,8 @@ void test12214() pure nothrow
}
/***************************************************/
-// 12220 & 12221
+// https://issues.dlang.org/show_bug.cgi?id=12220
+// https://issues.dlang.org/show_bug.cgi?id=12221
void test12220()
{
@@ -338,7 +339,7 @@ void test12220()
}
/***************************************************/
-// 12403
+// https://issues.dlang.org/show_bug.cgi?id=12403
void test12403()
{
diff --git a/gcc/testsuite/gdc.test/runnable/testabi.d b/gcc/testsuite/gdc.test/runnable/testabi.d
deleted file mode 100644
index c50dfbd0ba6..00000000000
--- a/gcc/testsuite/gdc.test/runnable/testabi.d
+++ /dev/null
@@ -1,926 +0,0 @@
-// PERMUTE_ARGS: -release -g
-
-version(Windows) {}
-else version(X86_64)
-{
- /* uncomment to enable tests! */
- //version = Run_X86_64_Tests;
-}
-
-extern (C) int printf(const char*, ...);
-
-template tuple(A...) { alias A tuple; }
-
-alias byte B;
-alias short S;
-alias int I;
-alias long L;
-alias float F;
-alias double D;
-alias real R;
-
-// Single Type
-
-struct b { B a; }
-struct bb { B a,b; }
-struct bbb { B a,b,c; }
-struct bbbb { B a,b,c,d; }
-struct bbbbb { B a,b,c,d, e; }
-struct b6 { B a,b,c,d, e,f; }
-struct b7 { B a,b,c,d, e,f,g; }
-struct b8 { B a,b,c,d, e,f,g,h; }
-struct b9 { B a,b,c,d, e,f,g,h, i; }
-struct b10 { B a,b,c,d, e,f,g,h, i,j; }
-struct b11 { B a,b,c,d, e,f,g,h, i,j,k; }
-struct b12 { B a,b,c,d, e,f,g,h, i,j,k,l; }
-struct b13 { B a,b,c,d, e,f,g,h, i,j,k,l, m; }
-struct b14 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n; }
-struct b15 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o; }
-struct b16 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p; }
-struct b17 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p, q; }
-struct b18 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p, q,r; }
-struct b19 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p, q,r,s; }
-struct b20 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p, q,r,s,t;}
-
-struct s { S a; }
-struct ss { S a,b; }
-struct sss { S a,b,c; }
-struct ssss { S a,b,c,d; }
-struct sssss { S a,b,c,d, e; }
-struct s6 { S a,b,c,d, e,f; }
-struct s7 { S a,b,c,d, e,f,g; }
-struct s8 { S a,b,c,d, e,f,g,h; }
-struct s9 { S a,b,c,d, e,f,g,h, i; }
-struct s10 { S a,b,c,d, e,f,g,h, i,j;}
-
-struct i { I a; } struct l { L a; }
-struct ii { I a,b; } struct ll { L a,b; }
-struct iii { I a,b,c; } struct lll { L a,b,c; }
-struct iiii { I a,b,c,d; } struct llll { L a,b,c,d; }
-struct iiiii { I a,b,c,d,e; } struct lllll { L a,b,c,d,e; }
-
-struct f { F a; } struct d { D a; }
-struct ff { F a,b; } struct dd { D a,b; }
-struct fff { F a,b,c; } struct ddd { D a,b,c; }
-struct ffff { F a,b,c,d; } struct dddd { D a,b,c,d; }
-struct fffff { F a,b,c,d,e; } struct ddddd { D a,b,c,d,e; }
-
-// Mixed Size
-
-struct js { I a; S b; }
-struct iss { I a; S b,c; }
-struct si { S a; I b; }
-struct ssi { S a,b; I c; }
-struct sis { S a; I b; S c; }
-
-struct ls { L a; S b; }
-struct lss { L a; S b,c; }
-struct sl { S a; L b; }
-struct ssl { S a,b; L c; }
-struct sls { S a; L b; S c; }
-
-struct li { L a; I b; }
-struct lii { L a; I b,c; }
-struct il { I a; L b; }
-struct iil { I a,b; L c; }
-struct ili { I a; L b; I c; }
-
-struct df { D a; F b; }
-struct dff { D a; F b,c; }
-struct fd { F a; D b; }
-struct ffd { F a,b; D c; }
-struct fdf { F a; D b; F c; }
-
-// Mixed Types
-
-struct fi { F a; I b; }
-struct fii { F a; I b,c; }
-struct jf { I a; F b; }
-struct iif { I a,b; F c; }
-struct ifi { I a; F b; I c; }
-
-struct ffi { F a,b; I c; }
-struct ffii { F a,b; I c,d; }
-struct iff { I a; F b,c; }
-struct iiff { I a,b; F c,d; }
-struct ifif { I a; F b; I c; F d;}
-
-struct di { D a; I b; }
-struct dii { D a; I b,c; }
-struct id { I a; D b; }
-struct iid { I a,b; D c; }
-struct idi { I a; D b; I c; }
-
-// Real ( long double )
-
-struct r { R a; }
-struct rr { R a,b; }
-struct rb { R a; B b; }
-struct rf { R a; F b; }
-struct fr { F a; R b; }
-
- // Int Registers only
-alias tuple!( b,bb,bbb,bbbb,bbbbb,
- b6, b7, b8, b9, b10,
- b11,b12,b13,b14,b15,
- b16,b17,b18,b19,b20,
- s,ss,sss,ssss,sssss,
- s6, s7, s8, s9, s10,
- i,ii,iii,iiii,iiiii,
- l,ll,lll,llll,lllll,
- //
- js,iss,si,ssi, sis,
- ls,lss,sl,ssl, sls,
- li,lii,il,iil, ili,
- fi,fii,jf,iif, ifi,
- ffi,ffii,iff,iiff,ifif, // INT_END
-
- // SSE registers only
- f,ff,fff,ffff,fffff,
- d,dd,ddd,dddd,ddddd,
- //
- df,dff,fd,ffd, fdf, // SSE_END
-
- // Int and SSE
- di, dii,id, iid, idi, // MIX_END
- // ---
- ) ALL_T;
-
-enum INT_END = 65;
-enum SSE_END = 80;
-enum MIX_END = ALL_T.length;
-
- // x87
-alias tuple!( r,rr,rb,rf,fr,
- // ---
- ) R_T;
-//"r","rr","rb","rf","fr",
-
-
-string[] ALL_S=[
- "b","bb","bbb","bbbb","bbbbb",
- "b6", "b7", "b8", "b9", "b10",
- "b11","b12","b13","b14","b15",
- "b16","b17","b18","b19","b20",
- "s","ss","sss","ssss","sssss",
- "s6","s7","s8","s9" , "s10",
- "i","ii","iii","iiii","iiiii",
- "l","ll","lll","llll","lllll",
- // ---
- "js","iss","si","ssi", "sis",
- "ls","lss","sl","ssl", "sls",
- "li","lii","il","iil", "ili",
- "fi","fii","jf","iif", "ifi",
- "ffi","ffii","iff","iiff","ifif",
- // ---
- "f","ff","fff","ffff","fffff",
- "d","dd","ddd","dddd","ddddd",
- "df","dff","fd","ffd", "dfd",
- // ---
- "di","dii","id","iid","idi",
- ];
-
-/* ***********************************************************************
- All
- ************************************************************************/
-// test1 Struct passing and return
-
-int[MIX_END] results_1;
-
-T test1_out(T)( )
-{
- T t;
- foreach( i, ref e; t.tupleof ) e = i+1;
- return t;
-}
-T test1_inout(T)( T t)
-{
- foreach( i, ref e; t.tupleof ) e += 10;
- return t;
-}
-
-void test1_call_out(T)( int n )
-{
- T t1;
- foreach( i, ref e; t1.tupleof ) e = i+1;
- T t2 = test1_out!(T)();
-
- if( t1 == t2 ) results_1[n] |= 1;
-}
-void test1_call_inout(T)( int n )
-{
- T t1;
- foreach( i, ref e; t1.tupleof ) e = i+1;
- T t2 = test1_inout!(T)( t1 );
- foreach( i, ref e; t1.tupleof ) e += 10;
-
- if( t1 == t2 ) results_1[n] |= 2;
-}
-
-void D_test1( )
-{
- // Run Tests
- foreach( n, T; ALL_T )
- {
- test1_call_out!(T)(n);
- test1_call_inout!(T)(n);
- }
-
- bool pass = true;
- foreach( i, r; results_1 )
- {
- if( ~r & 1 )
- {
- pass = false;
- printf( "Test1 out %s \tFail\n", ALL_S[i].ptr );
- }
- if( ~r & 2 )
- {
- pass = false;
- printf( "Test1 inout %s \tFail\n", ALL_S[i].ptr );
- }
- }
- assert( pass );
-
- results_1[0..5] = 0;
- foreach( n, T; R_T )
- {
- test1_call_out!(T)(n);
- test1_call_inout!(T)(n);
- }
-}
-
-/************************************************************************/
-// based on runnable/test23.d : test44()
-// Return Struct into an Array
-
-struct S1
-{ int i,j;
-
- static S1 foo(int x)
- { S1 s;
- s.i = x;
- return s;
-} }
-struct S2
-{ int i,j,k;
-
- static S2 foo(int x)
- { S2 s;
- s.i = x;
- return s;
-} }
-struct S3
-{ float i,j;
-
- static S3 foo(int x)
- { S3 s;
- s.i = x;
- return s;
-} }
-struct S4
-{ float i,j,k;
-
- static S4 foo(int x)
- { S4 s;
- s.i = x;
- return s;
-} }
-struct S5
-{ float i,j;
- int k;
-
- static S5 foo(float x)
- { S5 s;
- s.i = x;
- return s;
-} }
-
-void D_test2()
-{
- S1[] s1;
- S2[] s2;
- S3[] s3;
- S4[] s4;
- S5[] s5;
-
- s1 = s1 ~ S1.foo(6); s1 = s1 ~ S1.foo(1);
- s2 = s2 ~ S2.foo(6); s2 = s2 ~ S2.foo(1);
- s3 = s3 ~ S3.foo(6); s3 = s3 ~ S3.foo(1);
- s4 = s4 ~ S4.foo(6); s4 = s4 ~ S4.foo(1);
- s5 = s5 ~ S5.foo(6); s5 = s5 ~ S5.foo(1);
-
- assert( s1.length == 2 );
- assert( s1[0].i == 6 );
- assert( s1[1].i == 1 );
-
- assert( s2.length == 2 );
- assert( s2[0].i == 6 );
- assert( s2[1].i == 1 );
-
-/+ // These Fail on Mainline DMD64 ( Should pass! )
- assert( s3.length == 2 );
- assert( s3[0].i == 6 );
- assert( s3[1].i == 1 );
-
- assert( s4.length == 2 );
- assert( s4[0].i == 6 );
- assert( s4[1].i == 1 );
-+/
- assert( s5.length == 2 );
- assert( s5[0].i == 6 );
- assert( s5[1].i == 1 );
-
-}
-
-/* ***********************************************************************
- X86_64
- ************************************************************************/
-
-version(Run_X86_64_Tests)
-{
-
-
-struct TEST
-{
- immutable int num;
- immutable string desc;
- bool[MIX_END] result;
-}
-
-/**
- * 0 = Should Fail
- * 1 = Should Pass
- */
-immutable int[MIX_END] expected =
- [
- 1,1,1,1,1, // b
- 1,1,1,1,1, // b6
- 1,1,1,1,1, // b11
- 1,0,0,0,0, // b16
-
- 1,1,1,1,1, // s
- 1,1,1,0,0, // s6
- 1,1,1,1,0, // i
- 1,1,0,0,0, // l
- 1,1,1,1,1, // si mix
- 1,1,1,1,0, // sl
- 1,1,1,1,0, // il
- 1,1,1,1,1, // int and float
- 1,1,1,1,1, // int and float
-
- // SSE regs only
- 1,1,1,1,0, // f
- 1,1,0,0,0, // d
- 1,1,1,1,0, // float and double
-
- // SSE + INT regs
- 1,1,1,1,0, // int and double
- ];
-
-
-/**
- * Describes value expected in registers
- *
- * null means do not test
- * ( because value is passed on the stack ).
- */
-
-immutable long[][] RegValue =
- [
-/* 0 b */ [ 0x0000000000000001, ],
-/* 1 bb */ [ 0x0000000000000201, ],
-/* 2 bbb */ [ 0x0000000000030201, ],
-/* 3 bbbb */ [ 0x0000000004030201, ],
-/* 4 bbbbb */ [ 0x0000000504030201, ],
-/* 5 b6 */ [ 0x0000060504030201, ],
-/* 6 b7 */ [ 0x0007060504030201, ],
-/* 7 b8 */ [ 0x0807060504030201, ],
-/* 8 b9 */ [ 0x0807060504030201, 0x0000000000000009 ],
-/* 9 b10 */ [ 0x0807060504030201, 0x0000000000000a09 ],
-/* 10 b11 */ [ 0x0807060504030201, 0x00000000000b0a09 ],
-/* 11 b12 */ [ 0x0807060504030201, 0x000000000c0b0a09 ],
-/* 12 b13 */ [ 0x0807060504030201, 0x0000000d0c0b0a09 ],
-/* 13 b14 */ [ 0x0807060504030201, 0x00000e0d0c0b0a09 ],
-/* 14 b15 */ [ 0x0807060504030201, 0x000f0e0d0c0b0a09 ],
-/* 15 b16 */ [ 0x0807060504030201, 0x100f0e0d0c0b0a09 ],
-/* 16 b17 */ null,
-/* 17 b18 */ null,
-/* 18 b19 */ null,
-/* 19 b20 */ null,
-/* 20 s */ [ 0x0000000000000001, ],
-/* 21 ss */ [ 0x0000000000020001, ],
-/* 22 sss */ [ 0x0000000300020001, ],
-/* 23 ssss */ [ 0x0004000300020001, ],
-/* 24 sssss */ [ 0x0004000300020001, 0x0000000000000005 ],
-/* 25 s6 */ [ 0x0004000300020001, 0x0000000000060005 ],
-/* 26 s7 */ [ 0x0004000300020001, 0x0000000700060005 ],
-/* 27 s8 */ [ 0x0004000300020001, 0x0008000700060005 ],
-/* 28 s9 */ null,
-/* 29 s10 */ null,
-/* 30 i */ [ 0x0000000000000001, ],
-/* 31 ii */ [ 0x0000000200000001, ],
-/* 32 iii */ [ 0x0000000200000001, 0x0000000000000003 ],
-/* 33 iiii */ [ 0x0000000200000001, 0x0000000400000003 ],
-/* 34 iiiii */ null,
-/* 35 l */ [ 0x0000000000000001, ],
-/* 36 ll */ [ 0x0000000000000001, 0x0000000000000002 ],
-/* 37 lll */ null,
-/* 38 llll */ null,
-/* 39 lllll */ null,
-
-/* 40 js */ [ 0x0000000200000001, ],
-/* 41 iss */ [ 0x0003000200000001, ],
-/* 42 si */ [ 0x0000000200000001, ],
-/* 43 ssi */ [ 0x0000000300020001, ],
-/* 44 sis */ [ 0x0000000200000001, 0x0000000000000003 ],
-/* 45 ls */ [ 0x0000000000000001, 0x0000000000000002 ],
-/* 46 lss */ [ 0x0000000000000001, 0x0000000000030002 ],
-/* 47 sl */ [ 0x0000000000000001, 0x0000000000000002 ],
-/* 48 ssl */ [ 0x0000000000020001, 0x0000000000000003 ],
-/* 49 sls */ null,
-/* 50 li */ [ 0x0000000000000001, 0x0000000000000002 ],
-/* 51 lii */ [ 0x0000000000000001, 0x0000000300000002 ],
-/* 52 il */ [ 0x0000000000000001, 0x0000000000000002 ],
-/* 53 iil */ [ 0x0000000200000001, 0x0000000000000003 ],
-/* 54 ili */ null,
-
-/* 55 fi */ [ 0x000000023f800000, ],
-/* 56 fii */ [ 0x000000023f800000, 0x0000000000000003 ],
-/* 57 jf */ [ 0x4000000000000001, ],
-/* 58 iif */ [ 0x0000000200000001, 0x0000000040400000 ],
-/* 59 ifi */ [ 0x4000000000000001, 0x0000000000000003 ],
-
-/* 60 ffi */ [ 0x0000000000000003, 0x400000003f800000 ],
-/* 61 ffii */ [ 0x0000000400000003, 0x400000003f800000 ],
-/* 62 iff */ [ 0x4000000000000001, 0x0000000040400000 ],
-/* 63 iiff */ [ 0x0000000200000001, 0x4080000040400000 ],
-/* 64 ifif */ [ 0x4000000000000001, 0x4080000000000003 ],
-
-/* 65 f */ [ 0x000000003f800000, ],
-/* 66 ff */ [ 0x400000003f800000, ],
-/* 67 fff */ [ 0x400000003f800000, 0x0000000040400000 ],
-/* 68 ffff */ [ 0x400000003f800000, 0x4080000040400000 ],
-/* 69 fffff */ null,
-/* 70 d */ [ 0x3ff0000000000000, ],
-/* 71 dd */ [ 0x3ff0000000000000, 0x4000000000000000 ],
-/* 72 ddd */ null,
-/* 73 dddd */ null,
-/* 74 ddddd */ null,
-
-/* 75 df */ [ 0x3ff0000000000000, 0x0000000040000000 ],
-/* 76 dff */ [ 0x3ff0000000000000, 0x4040000040000000 ],
-/* 77 fd */ [ 0x000000003f800000, 0x4000000000000000 ],
-/* 78 ffd */ [ 0x400000003f800000, 0x4008000000000000 ],
-/* 79 fdf */ null,
-
-/* 80 di */ [ 0x3ff0000000000000, 0x0000000000000002 ],
-/* 81 dii */ [ 0x3ff0000000000000, 0x0000000300000002 ],
-/* 82 id */ [ 0x4000000000000000, 0x0000000000000001 ],
-/* 83 iid */ [ 0x4008000000000000, 0x0000000200000001 ],
-/* 84 idi */ null,
- ];
-
-/* Have to do it this way for OSX: Issue 7354 */
-__gshared long[2] dump;
-
-/**
- * Generate Register capture
- */
-string gen_reg_capture( int n, string registers )( )
-{
- if( RegValue[n] == null ) return "return;";
-
- string[] REG = mixin(registers); // ["RDI","RSI"];
-
- // Which type of compare
- static if(n < INT_END)
- enum MODE = 1; // Int
- else static if(n < SSE_END)
- enum MODE = 2; // Float
- else enum MODE = 3; // Mix
-
- /* Begin */
-
- string code = "asm {\n";
-
- final switch( MODE )
- {
- case 1: code ~= "mov [dump], "~REG[0]~";\n";
- REG = REG[1..$];
- break;
- case 2:
- case 3: code ~= "movq [dump], XMM0;\n";
- }
-
- if( RegValue[n].length == 2 )
- final switch( MODE )
- {
- case 1:
- case 3: code ~= "mov [dump+8], "~REG[0]~";\n";
- break;
- case 2: code ~= "movq [dump+8], XMM1;\n";
- } else {
- code ~= "xor R8, R8;\n";
- code ~= "mov [dump+8], R8;\n";
- }
-
- return code ~ "}\n";
-}
-
-/**
- * Check the results
- */
-bool check( TEST data )
-{
- bool pass = true;
- foreach( i, e; expected )
- {
- if( data.result[i] != (e & 1) )
- {
- printf( "Test%d %s \tFail\n", data.num, ALL_S[i].ptr);
- pass = false;
- }
- }
- return pass;
-}
-
-/************************************************************************/
-
-// test1 Return Struct in Registers
-// ( if RDI == 12 we have no hidden pointer )
-
-TEST data1 = { 1, "RDI hidden pointer" };
-
-T test1_asm( T, int n )( int i )
-{
- asm {
-
- cmp EDI, 12;
- je L1;
-
- leave; ret;
- }
-L1:
- data1.result[n] = true;
-}
-
-void test1()
-{
- printf("\nRunning iasm Test 1 ( %s )\n", data1.desc.ptr);
-
- foreach( int n, T; ALL_T )
- test1_asm!(T,n)(12);
-
- check( data1 );
-}
-
-/************************************************************************/
-// test2 Pass Struct in Registers
-// ( if RDI == 0 we have no stack pointer )
-
-TEST data2 = { 2, "RDI struct pointer" };
-
-T test2_asm( T, int n )( T t )
-{
- asm {
-
- mov [dump], RDI;
- mov [dump+8], RSP;
- cmp EDI, 0; // TODO test RDI is a ptr to stack ? ?
- je L1;
-
- leave; ret;
- }
-L1:
- data2.result[n] = true;
-}
-T test2f_asm( T, int n )( T t, int x )
-{
- asm {
-
- cmp EDI, 12;
- je L1;
-
- leave; ret;
- }
-L1:
- data2.result[n] = true;
-}
-
-void test2()
-{
- printf("\nRunning iasm Test 2 ( %s )\n", data2.desc.ptr);
-
- // Integer
- foreach( int n, T; ALL_T ) {
- T t = { 0 };
- test2_asm!(T,n)( t );
- }
-
- // float alternative test
- foreach( int n, T; ALL_T[INT_END..SSE_END] )
- {
- enum n2 = n + INT_END;
- data2.result[n2] = false;
- test2f_asm!(T,n2)( T.init, 12 );
- }
-
- check( data2 );
-}
-
-/************************************************************************/
-// test3
-
-TEST data3 = { 3, "Check Return Register value" };
-
-void test3_run( T, int n )( )
-{
- test3_ret!T();
- mixin( gen_reg_capture!(n,`["RAX","RDX"]`)() );
-
- //dbg!(T,n)( );
-
- enum len = RegValue[n].length;
- if( dump[0..len] == RegValue[n] )
- data3.result[n] = true;
-}
-
-T test3_ret( T )( )
-{
- T t;
- foreach( i, ref e; t.tupleof ) e = i+1;
- return t;
-}
-
-void test3()
-{
- printf("\nRunning iasm Test 3 ( %s )\n", data3.desc.ptr);
-
- foreach( int n, T; ALL_T )
- test3_run!(T,n)( );
-
- check( data3 );
-}
-
-/************************************************************************/
-// test4
-
-TEST data4 = { 4, "Check Input Register value" };
-
-void test4_run( T, int n )( T t )
-{
- mixin( gen_reg_capture!(n,`["RDI","RSI"]`)() );
-
- //dbg!(T,n)( );
-
- enum len = RegValue[n].length;
- if( dump[0..len] == RegValue[n] )
- data4.result[n] = true;
-}
-
-void dbg( T, int n )( )
-{
- import std.stdio;
- writefln( "D %s\t[ %16x, %16x ]", T.stringof, dump[0], dump[1], );
- writef( "C %s\t[ %16x", T.stringof, RegValue[n][0] );
- if( RegValue[n].length == 2 )
- writef( ", %16x", RegValue[n][1] );
- writefln( " ]" );
-}
-void test4()
-{
- printf("\nRunning iasm Test 4 ( %s )\n", data4.desc.ptr);
-
- foreach( int n, T; ALL_T )
- {
- T t;
- foreach( i, ref e; t.tupleof ) e = i+1;
- test4_run!(T,n)( t );
- }
- check( data4 );
-}
-
-
-} // end version(Run_X86_64_Tests)
-
-/************************************************************************/
-
-
-void main()
-{
- D_test1();
- D_test2();
-
- version(Run_X86_64_Tests)
- {
- test1();
- test2();
- test3();
- test4();
- }
-}
-
-/+
-/**
- * C code to generate the table RegValue
- */
-string c_generate_returns()
-{
- string value = " 1, 2, 3, 4, 5, 6, 7, 8, 9,10,"
- "11,12,13,14,15,16,17,18,19,20,";
-
- string code = "#include \"cgen.h\"\n";
-
- // Generate return functions
- foreach( int n, T; ALL_T )
- {
- auto Ts = T.stringof;
- auto len = T.tupleof.length;
-
- code ~= "struct "~Ts~" func_ret_"~Ts~"(void) { \n";
- code ~= "struct "~Ts~" x = { ";
- code ~= value[0..len*3] ~ " };\n";
- code ~= "return x;\n}\n";
- }
- return code;
-}
-string c_generate_pass()
-{
- string value = " 1, 2, 3, 4, 5, 6, 7, 8, 9,10,"
- "11,12,13,14,15,16,17,18,19,20,";
-
- string code;
-
- // Generate return functions
- foreach( int n, T; ALL_T )
- {
- auto Ts = T.stringof;
- auto len = T.tupleof.length;
-
- code ~= "void func_pass_"~Ts~"( struct "~Ts~" x ) {\n";
- ////////////////
- // Which type of compare
- static if(n < INT_END)
- enum MODE = 1; // Int
- else static if(n < SSE_END)
- enum MODE = 2; // Float
- else enum MODE = 3; // Mix
-
- auto nn = n.stringof;
-
- /* Begin */
-
- code ~= "asm(\n";
- final switch( MODE )
- {
- case 1:
- code ~= `"movq %rdi, reg\n" "movq %rsi, reg+8\n"`;
- break;
- case 2:
- code ~= `"movq %xmm0, reg\n" "movq %xmm1, reg+8\n"`;
- break;
- case 3:
- code ~= `"movq %xmm0, reg\n" "movq %rdi, reg+8\n"`;
- }
- code ~= "\n);\n";
- code ~= "}\n";
-
- ////////////////
- code ~= "void func_call_"~Ts~"( void ) {\n";
- code ~= "struct "~Ts~" x = { ";
- code ~= value[0..len*3] ~ " };\n";
- code ~= "func_pass_"~Ts~"( x );\n}\n";
- }
- return code;
-}
-string c_generate_main()
-{
- string code = "void main() {\n";
-
- foreach( int n, T; ALL_T )
- {
- // Which type of compare
- static if(n < INT_END)
- enum MODE = 1; // Int
- else static if(n < SSE_END)
- enum MODE = 2; // Float
- else enum MODE = 3; // Mix
-
- auto nn = n.stringof;
- auto Ts = T.stringof;
-
- /* Begin */
-
- code ~= `printf("/* %3d `~Ts~`\t*/ ", `~nn~`);`"\n";
- if( !(expected[n] & 1) )
- {
- code ~= `printf("null,\n");`"\n";
- continue;
- }
- code ~= "asm(\n";
- code ~= `"call func_ret_`~Ts~`\n"`"\n";
- final switch( MODE )
- {
- case 1:
- code ~= `"movq %rax, reg\n" "movq %rdx, reg+8\n"`;
- break;
- case 2:
- code ~= `"movq %xmm0, reg\n" "movq %xmm1, reg+8\n"`;
- break;
- case 3:
- code ~= `"movq %xmm0, reg\n" "movq %rax, reg+8\n"`;
- }
- code ~= "\n);\n";
-
- code ~= `printf("[ 0x%016lx", reg.r1 );`"\n";
-
- if( T.sizeof > 8 || MODE == 3 )
- code ~= `printf(", 0x%016lx ],\n", reg.r2 );`"\n";
- else code ~= `printf(", %015c ],\n", ' ' );`"\n";
- }
-
- foreach( int n, T; ALL_T )
- {
- // Which type of compare
- static if(n < INT_END)
- enum MODE = 1; // Int
- else static if(n < SSE_END)
- enum MODE = 2; // Float
- else enum MODE = 3; // Mix
-
- auto nn = n.stringof;
- auto Ts = T.stringof;
-
- /* Begin */
-
- code ~= `printf("/* %3d `~Ts~`\t*/ ", `~nn~`);`"\n";
- if( !(expected[n] & 1) )
- {
- code ~= `printf("null,\n");`"\n";
- continue;
- }
- code ~= "func_call_"~Ts~"();\n";
-
- code ~= `printf("[ 0x%016lx", reg.r1 );`"\n";
-
- if( T.sizeof > 8 || MODE == 3 )
- code ~= `printf(", 0x%016lx ],\n", reg.r2 );`"\n";
- else code ~= `printf(", %015c ],\n", ' ' );`"\n";
- }
-
-
- return code ~ "}";
-}
-pragma(msg, c_generate_returns() );
-pragma(msg, c_generate_pass() );
-pragma(msg, c_generate_main() );
-// +/
-
-/+
-/**
- * Generate Functions that pass/return each Struct type
- *
- * ( Easier to look at objdump this way )
- */
-string d_generate_functions( )
-{
- string code = "extern(C) {";
-
- // pass
- foreach( s; ALL_T )
- {
- string ss = s.stringof;
-
- code ~= "void func_in_"~ss~"( "~ss~" t ) { t.a = 12; }\n";
- }
- // return
- foreach( s; ALL_T[0..10] )
- {
- string ss = s.stringof;
-
- code ~= `
- auto func_out_`~ss~`()
- {
- `~ss~` t;
- foreach( i, ref e; t.tupleof ) e = i+1;
- return t;
- }`;
- }
- // pass & return
- foreach( s; ALL_T[0..10] )
- {
- string ss = s.stringof;
-
- code ~= `
- auto func_inout_`~ss~`( `~ss~` t )
- {
- foreach( i, ref e; t.tupleof ) e += 10;
- return t;
- }`;
- }
- return code ~ "\n} // extern(C)\n";
-}
-//pragma( msg, d_generate_functions() );
-mixin( d_generate_functions() );
-// +/
diff --git a/gcc/testsuite/gdc.test/runnable/testaliascast.d b/gcc/testsuite/gdc.test/runnable/testaliascast.d
new file mode 100644
index 00000000000..c55f8203499
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/testaliascast.d
@@ -0,0 +1,63 @@
+// https://issues.dlang.org/show_bug.cgi?id=11294
+
+string result;
+
+extern(C) void rt_finalize(void *ptr, bool det=true);
+void clear(T)(T obj) if (is(T == class))
+{
+ rt_finalize(cast(void*)obj);
+}
+
+class A
+{
+ ~this() { result ~= "A"; }
+ string dummy = "0";
+}
+
+class B
+{
+ A a;
+ string dummy = "0";
+ alias a this;
+ ~this() { result ~= "B"; }
+}
+
+void test11294()
+{
+ auto a = new A;
+ auto b = new B;
+ b.a = a;
+ result ~= b.dummy;
+ clear(b);
+ result ~= a.dummy;
+ result ~= "END";
+ clear(a);
+
+ assert(result == "0B0ENDA");
+}
+
+
+// https://issues.dlang.org/show_bug.cgi?id=13392
+void foo(T)(T t)
+{
+ void* p = cast(void*) t; //Callas alias this
+}
+
+class X {}
+
+class Y
+{
+ alias a this;
+ @property X a(){assert(0);} //Here
+}
+
+void test13392()
+{
+ foo(B.init);
+}
+
+void main()
+{
+ test11294();
+ test13392();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/testappend.d b/gcc/testsuite/gdc.test/runnable/testappend.d
index cbdae36b96c..f8bff3e16e8 100644
--- a/gcc/testsuite/gdc.test/runnable/testappend.d
+++ b/gcc/testsuite/gdc.test/runnable/testappend.d
@@ -1,4 +1,18 @@
-// PERMUTE_ARGS:
+/*
+PERMUTE_ARGS:
+TEST_OUTPUT:
+---
+runnable/testappend.d(54): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+runnable/testappend.d(55): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+runnable/testappend.d(76): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+runnable/testappend.d(77): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+
+RUN_OUTPUT:
+---
+Success
+---
+*/
import core.stdc.stdio;
import core.stdc.math : isnan;
diff --git a/gcc/testsuite/gdc.test/runnable/testassert.d b/gcc/testsuite/gdc.test/runnable/testassert.d
new file mode 100644
index 00000000000..30fe8d941b5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/testassert.d
@@ -0,0 +1,380 @@
+/*
+REQUIRED_ARGS: -checkaction=context -preview=dip1000
+PERMUTE_ARGS: -O -g -inline
+*/
+
+void test8765()
+{
+ string msg;
+ try
+ {
+ int a = 0;
+ assert(a);
+ }
+ catch (Throwable e)
+ {
+ // no-message -> assert expression
+ msg = e.msg;
+ }
+ assert(msg == "0 != true");
+}
+
+ void test9255()
+{
+ string file;
+ try
+ {
+ int x = 0;
+ assert(x);
+ }
+ catch (Throwable e)
+ {
+ file = e.file;
+ }
+
+ version(Windows)
+ assert(file && file == r"runnable\testassert.d");
+ else
+ assert(file && file == "runnable/testassert.d");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20114
+void test20114()
+{
+ // Function call returning simple type
+ static int fun() {
+ static int i = 0;
+ assert(i++ == 0);
+ return 3;
+ }
+
+ const a = getMessage(assert(fun() == 4));
+ assert(a == "3 != 4");
+
+ // Function call returning complex type with opEquals
+ static struct S
+ {
+ bool opEquals(const int x) const
+ {
+ return false;
+ }
+ }
+
+ static S bar()
+ {
+ static int i = 0;
+ assert(i++ == 0);
+ return S.init;
+ }
+
+ const b = getMessage(assert(bar() == 4));
+ assert(b == "S() != 4");
+
+ // Non-call expression with side effects
+ int i = 0;
+ const c = getMessage(assert(++i == 0));
+ assert(c == "1 != 0");
+}
+
+version (DigitalMars) version (Win64) version = DMD_Win64;
+
+void test20375() @safe
+{
+ static struct RefCounted
+ {
+ // Force temporary through "impure" generator function
+ static RefCounted create() @trusted
+ {
+ __gshared int counter = 0;
+ return RefCounted(++counter > 0);
+ }
+
+ static int instances;
+ static int postblits;
+
+ this(bool) @safe
+ {
+ instances++;
+ }
+
+ this(this) @safe
+ {
+ instances++;
+ postblits++;
+ }
+
+ ~this() @safe
+ {
+ // make the dtor non-nothrow (we are tracking clean-ups during AssertError unwinding)
+ if (postblits > 100)
+ throw new Exception("");
+ assert(instances > 0);
+ instances--;
+ }
+
+ bool opEquals(RefCounted) @safe
+ {
+ return true;
+ }
+ }
+
+ {
+ auto a = RefCounted.create();
+ RefCounted.instances++; // we're about to construct an instance below, increment manually
+ assert(a == RefCounted()); // both operands are pure expressions => no temporaries
+ }
+
+ assert(RefCounted.instances == 0);
+ assert(RefCounted.postblits == 0);
+
+ {
+ auto a = RefCounted.create();
+ assert(a == RefCounted.create()); // impure rhs is promoted to a temporary lvalue => copy for a.opEquals(rhs)
+ }
+
+ assert(RefCounted.instances == 0);
+ assert(RefCounted.postblits == 1);
+ RefCounted.postblits = 0;
+
+ {
+ const msg = getMessage(assert(RefCounted.create() != RefCounted.create())); // both operands promoted to temporary lvalues
+ assert(msg == "RefCounted() == RefCounted()");
+ }
+
+ version (DMD_Win64) // FIXME: temporaries apparently not destructed when unwinding via AssertError
+ {
+ assert(RefCounted.instances >= 0 && RefCounted.instances <= 2);
+ RefCounted.instances = 0;
+ }
+ else
+ assert(RefCounted.instances == 0);
+ assert(RefCounted.postblits == 1);
+ RefCounted.postblits = 0;
+
+ static int numGetLvalImpureCalls = 0;
+ ref RefCounted getLvalImpure() @trusted
+ {
+ numGetLvalImpureCalls++;
+ __gshared lval = RefCounted(); // not incrementing RefCounted.instances
+ return lval;
+ }
+
+ {
+ const msg = getMessage(assert(getLvalImpure() != getLvalImpure())); // both operands promoted to ref temporaries
+ assert(msg == "RefCounted() == RefCounted()");
+ }
+
+ assert(numGetLvalImpureCalls == 2);
+ assert(RefCounted.instances == 0);
+ assert(RefCounted.postblits == 1);
+ RefCounted.postblits = 0;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21471
+void test21471()
+{
+ {
+ static struct S
+ {
+ S get()() const { return this; }
+ }
+
+ static auto f(S s) { return s; }
+
+ auto s = S();
+ assert(f(s.get) == s);
+ }
+ {
+ pragma(inline, true)
+ real exp(real x) pure nothrow
+ {
+ return x;
+ }
+
+ bool isClose(int lhs, real rhs) pure nothrow
+ {
+ return false;
+ }
+ auto c = 0;
+ assert(!isClose(c, exp(1)));
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20581
+void test20581() @safe
+{
+ static auto retro(return scope int[] s) @safe
+ {
+ static struct R
+ {
+ int[] source;
+ }
+ return R(s);
+ }
+
+ int[5] a = [ 1, 2, 3, 4, 5 ];
+ // Creates ref temporary __assertTmpXXXX for source
+ // Error: address of variable a assigned to __assertOp27 with longer lifetime
+ assert(retro(a[]).source is a[]);
+}
+
+string getMessage(T)(lazy T expr) @trusted
+{
+ try
+ {
+ expr();
+ return null;
+ }
+ catch (Throwable t)
+ {
+ return t.msg;
+ }
+}
+
+void testMixinExpression() @safe
+{
+ static struct S
+ {
+ bool opEquals(S) @safe { return true; }
+ }
+
+ const msg = getMessage(assert(mixin("S() != S()")));
+ assert(msg == "S() == S()");
+}
+
+void testUnaryFormat()
+{
+ int zero = 0, one = 1;
+ assert(getMessage(assert(zero)) == "0 != true");
+ assert(getMessage(assert(!one)) == "1 == true");
+
+ assert(getMessage(assert(!cast(int) 1.5)) == "1 == true");
+
+ assert(getMessage(assert(!!__ctfe)) == "assert(__ctfe) failed!");
+
+ static struct S
+ {
+ int i;
+ bool opCast() const
+ {
+ return i < 2;
+ }
+ }
+
+ assert(getMessage(assert(S(4))) == "S(4) != true");
+
+ S s = S(4);
+ assert(getMessage(assert(*&s)) == "S(4) != true");
+
+ assert(getMessage(assert(--(++zero))) == "0 != true");
+}
+
+void testAssignments()
+{
+ int a = 1;
+ int b = 2;
+ assert(getMessage(assert(a -= --b)) == "0 != true");
+
+ static ref int c()
+ {
+ static int counter;
+ counter++;
+ return counter;
+ }
+
+ assert(getMessage(assert(--c())) == "0 != true");
+}
+
+/// https://issues.dlang.org/show_bug.cgi?id=21472
+void testTupleFormat()
+{
+ alias AliasSeq(T...) = T;
+
+ // Default usage
+ {
+ alias a = AliasSeq!(1, "ab");
+ alias b = AliasSeq!(false, "xyz");
+ assert(getMessage(assert(a == b)) == `(1, "ab") != (false, "xyz")`);
+ }
+
+ // Single elements work but are not formatted as tuples
+ // Is this acceptable? (a workaround would probably require a
+ // different name for the tuple formatting hook)
+ {
+ alias a = AliasSeq!(1, "ab", []);
+ alias b = AliasSeq!(false, "xyz", [1]);
+ assert(getMessage(assert(a == b)) == `(1, "ab", []) != (false, "xyz", [1])`);
+ }
+
+ // Also works with tupleof (as taken from the bug report)
+ {
+ static struct Var { int a, b; }
+ const a = Var(1, 2);
+ const b = Var(3, 4);
+ const msg = getMessage(assert(a.tupleof == b.tupleof));
+ assert(msg == `(1, 2) != (3, 4)`);
+ }
+
+ // Also works when creating temporaries for the TupleExp
+ {
+ static struct S
+ {
+ int a, b;
+ }
+
+ static S get()
+ {
+ static int i;
+ return S(i++, i++);
+ }
+
+ auto a = get().tupleof;
+ auto b = get().tupleof;
+
+ string msg = getMessage(assert(a == b));
+ assert(msg == `(0, 1) != (2, 3)`);
+
+ msg = getMessage(assert(get().tupleof == AliasSeq!(2, 3)));
+ assert(msg == `(4, 5) != (2, 3)`);
+
+ msg = getMessage(assert(get().tupleof == get().tupleof));
+ assert(msg == `(6, 7) != (8, 9)`);
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21682
+void testStaticOperators()
+{
+ static class environment {
+ static bool opCmp(scope const(char)[] name)
+ {
+ return false;
+ }
+
+ static bool opBinaryRight(string op : "in")(scope const(char)[] name)
+ {
+ return false;
+ }
+ }
+
+ string msg = getMessage(assert(environment < "Hello"));
+ assert(msg == `"environment" >= "Hello"`);
+
+ msg = getMessage(assert("Hello" in environment));
+ assert(msg == `"Hello" !in "environment"`);
+}
+
+void main()
+{
+ test8765();
+ test9255();
+ test20114();
+ test20375();
+ test21471();
+ test20581();
+ testMixinExpression();
+ testUnaryFormat();
+ testAssignments();
+ testTupleFormat();
+ testStaticOperators();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/testassert_debug.d b/gcc/testsuite/gdc.test/runnable/testassert_debug.d
new file mode 100644
index 00000000000..8817a96c547
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/testassert_debug.d
@@ -0,0 +1,26 @@
+/*
+https://issues.dlang.org/show_bug.cgi?id=21598
+
+REQUIRED_ARGS: -checkaction=context -debug
+PERMUTE_ARGS:
+*/
+
+void main()
+{
+ bool caught;
+ try
+ assert(foo(1));
+ catch (Throwable)
+ caught = true;
+
+ assert(caught);
+ assert(counter == 1);
+}
+
+__gshared int counter;
+
+int foo(int i) pure nothrow
+{
+ debug counter++;
+ return i - 1;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/testassign.d b/gcc/testsuite/gdc.test/runnable/testassign.d
index b0009671b19..f47d2b2bd97 100644
--- a/gcc/testsuite/gdc.test/runnable/testassign.d
+++ b/gcc/testsuite/gdc.test/runnable/testassign.d
@@ -1,4 +1,5 @@
/*
+REQUIRED_ARGS: -preview=rvaluerefparam
TEST_OUTPUT:
---
\ S1 S2a S2b S3a S3b S4a S4b
@@ -23,7 +24,7 @@ import core.stdc.stdio;
template TypeTuple(T...){ alias T TypeTuple; }
/***************************************************/
-// 2625
+// https://issues.dlang.org/show_bug.cgi?id=2625
struct Pair {
immutable uint g1;
@@ -36,7 +37,7 @@ void test1() {
}
/***************************************************/
-// 5327
+// https://issues.dlang.org/show_bug.cgi?id=5327
struct ID
{
@@ -154,12 +155,12 @@ void test3()
}
/***************************************************/
-// 3511
+// https://issues.dlang.org/show_bug.cgi?id=3511
struct S4
{
private int _prop = 42;
- ref int property() { return _prop; }
+ ref int property() return { return _prop; }
}
void test4()
@@ -177,11 +178,11 @@ struct S5
int mX;
string mY;
- ref int x()
+ ref int x() return
{
return mX;
}
- ref string y()
+ ref string y() return
{
return mY;
}
@@ -230,7 +231,7 @@ void test5()
}
/***************************************************/
-// 4424
+// https://issues.dlang.org/show_bug.cgi?id=4424
void test4424()
{
@@ -242,7 +243,7 @@ void test4424()
}
/***************************************************/
-// 6174
+// https://issues.dlang.org/show_bug.cgi?id=6174
struct CtorTest6174(Data)
{
@@ -464,12 +465,12 @@ void test6174c()
static assert(!is(typeof({
int func1a(int n)
in{ n = 10; }
- body { return n; }
+ do { return n; }
})));
static assert(!is(typeof({
int func1b(int n)
out(r){ r = 20; }
- body{ return n; }
+ do{ return n; }
})));
struct DataX
@@ -479,18 +480,18 @@ void test6174c()
static assert(!is(typeof({
DataX func2a(DataX n)
in{ n.x = 10; }
- body { return n; }
+ do { return n; }
})));
static assert(!is(typeof({
DataX func2b(DataX n)
in{}
out(r){ r.x = 20; }
- body{ return n; }
+ do{ return n; }
})));
}
/***************************************************/
-// 6216
+// https://issues.dlang.org/show_bug.cgi?id=6216
void test6216a()
{
@@ -658,7 +659,7 @@ void test6216e()
}
/***************************************************/
-// 6286
+// https://issues.dlang.org/show_bug.cgi?id=6286
void test6286()
{
@@ -672,7 +673,7 @@ void test6286()
}
/***************************************************/
-// 6336
+// https://issues.dlang.org/show_bug.cgi?id=6336
void test6336()
{
@@ -701,7 +702,7 @@ void test6336()
}
/***************************************************/
-// 8783
+// https://issues.dlang.org/show_bug.cgi?id=8783
struct Foo8783
{
@@ -719,7 +720,7 @@ static this()
}
/***************************************************/
-// 9077
+// https://issues.dlang.org/show_bug.cgi?id=9077
struct S9077a
{
@@ -735,17 +736,17 @@ struct S9077b
}
/***************************************************/
-// 9140
+// https://issues.dlang.org/show_bug.cgi?id=9140
immutable(int)[] bar9140()
out(result) {
foreach (ref r; result) {}
-} body {
+} do {
return null;
}
/***************************************************/
-// 9154
+// https://issues.dlang.org/show_bug.cgi?id=9154
struct S9154a
{
@@ -770,7 +771,7 @@ void test9154()
}
/***************************************************/
-// 9258
+// https://issues.dlang.org/show_bug.cgi?id=9258
class A9258 {}
class B9258 : A9258 // Error: class test.B9258 identity assignment operator overload is illegal
@@ -797,7 +798,7 @@ class E9258 : A9258
}
/***************************************************/
-// 9416
+// https://issues.dlang.org/show_bug.cgi?id=9416
struct S9416
{
@@ -818,7 +819,7 @@ void test9416()
}
/***************************************************/
-// 9658
+// https://issues.dlang.org/show_bug.cgi?id=9658
struct S9658
{
@@ -830,7 +831,7 @@ struct S9658
}
/***************************************************/
-// 11187
+// https://issues.dlang.org/show_bug.cgi?id=11187
void test11187()
{
@@ -850,7 +851,7 @@ void test11187()
}
/***************************************************/
-// 12131
+// https://issues.dlang.org/show_bug.cgi?id=12131
struct X12131
{
@@ -872,7 +873,7 @@ void test12131() pure
}
/***************************************************/
-// 12211
+// https://issues.dlang.org/show_bug.cgi?id=12211
void test12211()
{
@@ -891,12 +892,12 @@ void test12211()
// array ops should make rvalue
int[3] sa, sb;
void bar(ref int[]) {}
- static assert(!__traits(compiles, bar(sa[] = sb[])));
- static assert(!__traits(compiles, bar(sa[] += sb[])));
+ static assert(__traits(compiles, bar(sa[] = sb[])));
+ static assert(__traits(compiles, bar(sa[] += sb[])));
}
/***************************************************/
-// 4791 (dup of 12212)
+// https://issues.dlang.org/show_bug.cgi?id=4791 (dup of 12212)
void test4791()
{
@@ -925,7 +926,7 @@ void test4791()
}
/***************************************************/
-// 12212
+// https://issues.dlang.org/show_bug.cgi?id=12212
void test12212()
{
@@ -1013,7 +1014,7 @@ void test12212()
}
/***************************************************/
-// 12650
+// https://issues.dlang.org/show_bug.cgi?id=12650
void test12650()
{
@@ -1083,7 +1084,7 @@ void test12650()
}
/***************************************************/
-// 13044
+// https://issues.dlang.org/show_bug.cgi?id=13044
void test13044()
{
@@ -1106,7 +1107,7 @@ void test13044()
}
/***************************************************/
-// 12500
+// https://issues.dlang.org/show_bug.cgi?id=12500
void test12500()
{
@@ -1115,7 +1116,7 @@ void test12500()
}
/***************************************************/
-// 14672
+// https://issues.dlang.org/show_bug.cgi?id=14672
void test14672()
{
@@ -1150,7 +1151,7 @@ void test14672()
}
/***************************************************/
-// 15044
+// https://issues.dlang.org/show_bug.cgi?id=15044
void destroy15044(T)(ref T obj)
{
@@ -1166,7 +1167,7 @@ struct V15044
{
}
- RC15044!V15044 dup()
+ RC15044!V15044 dup() return
{
return RC15044!V15044(&this);
}
diff --git a/gcc/testsuite/gdc.test/runnable/testbitarray.d b/gcc/testsuite/gdc.test/runnable/testbitarray.d
deleted file mode 100644
index 8a34f886b05..00000000000
--- a/gcc/testsuite/gdc.test/runnable/testbitarray.d
+++ /dev/null
@@ -1,17 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-// PERMUTE_ARGS:
-
-import std.bitmanip;
-
-void main() {
- BitArray a;
- a.length = 5;
- foreach (ref bool b; a) {
- assert (b == 0);
- b = 1;
- }
- foreach (bool b; a)
- assert (b == 1); // FAILS, they're all 0
-}
-
-
diff --git a/gcc/testsuite/gdc.test/runnable/testbounds.d b/gcc/testsuite/gdc.test/runnable/testbounds.d
index b23c88f29b6..bb9e54d75c2 100644
--- a/gcc/testsuite/gdc.test/runnable/testbounds.d
+++ b/gcc/testsuite/gdc.test/runnable/testbounds.d
@@ -1,3 +1,9 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
// REQUIRED_ARGS:
// Test array bounds checking
@@ -179,44 +185,63 @@ void test1()
}
/******************************************/
-// 13976
+// https://issues.dlang.org/show_bug.cgi?id=13976
void test13976()
{
int[] da = new int[](10);
int[10] sa;
- size_t l = 0; // upperInRange
- size_t u = 9; // | lowerLessThan
- // | | check code
- { auto s = da[l .. u]; } // 0 0 (u <= 10 && l <= u )
- { auto s = da[1 .. u]; } // 0 0 (u <= 10 && l <= u )
- { auto s = da[l .. 10]; } // 0 0 (u <= 10 && l <= u )
- { auto s = da[1 .. u%5]; } // 0 0 (u <= 10 && l <= u%5)
-
- { auto s = da[l .. u]; } // 0 0 (u <= 10 && l <= u)
- { auto s = da[0 .. u]; } // 0 1 (u <= 10 )
- { auto s = da[l .. 10]; } // 0 0 (u <= 10 && l <= u)
- { auto s = da[0 .. u%5]; } // 0 1 (u%5 <= 10 )
-
- { auto s = sa[l .. u]; } // 0 0 (u <= 10 && l <= u )
- { auto s = sa[1 .. u]; } // 0 0 (u <= 10 && l <= u )
- { auto s = sa[l .. 10]; } // 1 0 ( l <= u )
- { auto s = sa[1 .. u%5]; } // 1 0 ( l <= u%5)
-
- { auto s = sa[l .. u]; } // 0 0 (u <= 10 && l <= u )
- { auto s = sa[0 .. u]; } // 0 1 (u <= 10 )
- { auto s = sa[l .. 10]; } // 1 0 ( l <= 10)
- { auto s = sa[0 .. u%5]; } // 1 1 NULL
+ enum size_t two = 2;
+ enum size_t five = 5;
+ size_t lb = 0; // upperInRange
+ size_t ub = 9; // | lowerLessThan
+ // | | check code
+ { auto s = da[lb .. ub]; } // 0 0 (ub <= $ && lb <= ub )
+ { auto s = da[1 .. ub]; } // 0 0 (ub <= $ && 1 <= ub )
+ { auto s = da[lb .. 10]; } // 0 0 (10 <= $ && lb <= 10 )
+ { auto s = da[1 .. ub%5]; } // 0 0 (ub%5 <= $ && 1 <= ub%5)
+
+ { auto s = da[lb .. ub]; } // 0 0 (ub <= $ && lb <= ub )
+ { auto s = da[0 .. ub]; } // 0 1 (ub <= $ )
+ { auto s = da[lb .. 10]; } // 0 0 (10 <= $ && lb <= 10 )
+ { auto s = da[0 .. ub%5]; } // 0 1 (ub%5 <= $ )
+
+ { auto s = da[0 .. 0]; } // 1 1 NULL
+ { auto s = da[0 .. $]; } // 1 1 NULL
+ { auto s = da[1 .. $]; } // 1 0 ( 1 <= $ )
+ { auto s = da[$ .. $]; } // 1 0 ( $ <= $ )
+ { auto s = da[0 .. $/two]; } // 0 1 ($/2 <= $ )
+ { auto s = da[$/two .. $]; } // 1 0 ( $/2 <= $ )
+ { auto s = da[$/five .. $/two]; } // 0 0 ($/2 <= $ && $/5 <= $/2)
+
+ { auto s = sa[lb .. ub]; } // 0 0 (ub <= 10 && lb <= ub )
+ { auto s = sa[1 .. ub]; } // 0 0 (ub <= 10 && 1 <= ub )
+ { auto s = sa[lb .. 10]; } // 1 0 ( lb <= 10 )
+ { auto s = sa[1 .. ub%5]; } // 1 0 ( 1 <= ub%5)
+
+ { auto s = sa[lb .. ub]; } // 0 0 (ub <= 10 && lb <= ub )
+ { auto s = sa[0 .. ub]; } // 0 1 (ub <= 10 )
+ { auto s = sa[lb .. 10]; } // 1 0 ( lb <= 10 )
+ { auto s = sa[0 .. ub%5]; } // 1 1 NULL
+
+ { auto s = sa[0 .. 0]; } // 1 1 NULL
+ { auto s = sa[0 .. $]; } // 1 1 NULL
+ { auto s = sa[1 .. $]; } // 1 1 NULL
+ { auto s = sa[$ .. $]; } // 1 1 NULL
+ { auto s = sa[0 .. $/two]; } // 1 1 NULL
+ { auto s = sa[$/two .. $]; } // 1 1 NULL
+ { auto s = sa[$/five .. $/two]; } // 1 1 NULL
int* p = new int[](10).ptr;
- { auto s = p[0 .. u]; } // 1 1 NULL
- { auto s = p[l .. u]; } // 1 0 (l <= u)
- { auto s = p[0 .. u%5]; } // 1 1 NULL
- { auto s = p[1 .. u%5]; } // 1 0 (l <= u%5)
+ { auto s = p[0 .. ub]; } // 1 1 NULL
+ { auto s = p[lb .. ub]; } // 1 0 (lb <= ub )
+ { auto s = p[0 .. ub%5]; } // 1 1 NULL
+ { auto s = p[1 .. ub%5]; } // 1 0 (1 <= ub%5)
+ { auto s = p[0 .. 0]; } // 1 1 NULL
}
/******************************************/
-// 3652
+// https://issues.dlang.org/show_bug.cgi?id=3652
void test3652()
{
@@ -340,7 +365,7 @@ void test3652b() @safe
}
/**********************************/
-// 9654
+// https://issues.dlang.org/show_bug.cgi?id=9654
auto foo9654a(ref char[8] str) { return str; }
auto foo9654b(ref const char[8] str) { return str; }
@@ -364,7 +389,7 @@ static assert( is(typeof(baz9654b("testinfo")) == const char[8]));
static assert( is(typeof(baz9654c("testinfo")) == immutable char[8]));
/******************************************/
-// 9712
+// https://issues.dlang.org/show_bug.cgi?id=9712
auto func9712(T)(T[2] arg) { return arg; }
static assert(is(typeof(func9712([1,2])) == int[2]));
@@ -373,7 +398,7 @@ auto deduceLength9712(T,size_t n)(T[n] a) { return a; }
static assert(is(typeof(deduceLength9712([1,2,3])) == int[3]));
/******************************************/
-// 9743
+// https://issues.dlang.org/show_bug.cgi?id=9743
void test9743()
{
@@ -436,7 +461,7 @@ void test9743()
}
/******************************************/
-// 9747
+// https://issues.dlang.org/show_bug.cgi?id=9747
void foo9747A(T)(T[4]) {}
void foo9747C(size_t dim)(char[dim]) {}
@@ -454,7 +479,7 @@ void test9747()
}
/******************************************/
-// 12876
+// https://issues.dlang.org/show_bug.cgi?id=12876
void test12876()
{
@@ -467,24 +492,24 @@ void test12876()
}
/******************************************/
-// 13775
+// https://issues.dlang.org/show_bug.cgi?id=13775
void test13775()
{
ubyte[4] ubytes = [1,2,3,4];
- // CT-known slicing (issue 3652)
+ // CT-known slicing (https://issues.dlang.org/show_bug.cgi?id=3652)
auto ok1 = cast(ubyte[2]) ubytes[0 .. 2];
assert(ok1 == [1, 2]);
- // CT-known slicing with implicit conversion of SliceExp::e1 (issue 13154)
+ // CT-known slicing with implicit conversion of SliceExp::e1 (https://issues.dlang.org/show_bug.cgi?id=13154)
enum double[] arr = [1.0, 2.0, 3.0];
auto ok2 = cast(float[2]) [1.0, 2.0, 3.0][0..2];
auto ok3 = cast(float[2]) arr[1..3]; // currently this is accepted
assert(ok2 == [1f, 2f]);
assert(ok3 == [2f, 3f]);
- // CT-known slicing with type coercing (issue 13775)
+ // CT-known slicing with type coercing (https://issues.dlang.org/show_bug.cgi?id=13775)
auto ok4 = cast( byte[2]) ubytes[0 .. 2]; // CT-known slicing + type coercing
auto ok5 = cast(short[1]) ubytes[0 .. 2]; // CT-known slicing + type coercing
assert(ok4 == [1, 2]);
@@ -493,6 +518,29 @@ void test13775()
}
/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15889
+// Array bounds check should report index and length
+void test15889()
+{
+ int[] a = new int[2];
+
+ try {
+ a[3] = 40;
+ } catch (ArrayIndexError e) {
+ assert(e.index == 3);
+ assert(e.length == 2);
+ }
+
+ try {
+ a[1 .. 4] = 50;
+ } catch (ArraySliceError e) {
+ assert(e.lower == 1);
+ assert(e.upper == 4);
+ assert(e.length == 2);
+ }
+}
+
+/******************************************/
int main()
{
@@ -504,6 +552,7 @@ int main()
test9743();
test9747();
test13775();
+ test15889();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/testbtst.d b/gcc/testsuite/gdc.test/runnable/testbtst.d
new file mode 100644
index 00000000000..a6967f6c17d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/testbtst.d
@@ -0,0 +1,156 @@
+/* PERMUTE_ARGS: -O
+ * https://issues.dlang.org/show_bug.cgi?id=19813
+ */
+
+struct BitArray
+{
+ import core.bitop : btc, bts, btr, bsf, bt;
+
+ size_t _len;
+ size_t* _ptr;
+ enum bitsPerSizeT = size_t.sizeof * 8;
+
+ static size_t lenToDim(size_t len) @nogc pure nothrow @safe
+ {
+ return (len + (bitsPerSizeT-1)) / bitsPerSizeT;
+ }
+
+ this(in bool[] ba) nothrow pure
+ {
+ length = ba.length;
+ foreach (i, b; ba)
+ {
+ if (b)
+ bts(_ptr, i);
+ else
+ btr(_ptr, i);
+ }
+ }
+
+ @property size_t length(size_t newlen) pure nothrow @system
+ {
+ if (newlen != _len)
+ {
+ size_t olddim = lenToDim(_len);
+ immutable newdim = lenToDim(newlen);
+
+ if (newdim != olddim)
+ {
+ // Create a fake array so we can use D's realloc machinery
+ auto b = _ptr[0 .. olddim];
+ b.length = newdim; // realloc
+ _ptr = b.ptr;
+ }
+
+ _len = newlen;
+ }
+ return _len;
+ }
+
+ int opCmp(ref BitArray a2) const @nogc pure nothrow
+ {
+ const lesser = this._len < a2._len ? &this : &a2;
+ immutable fullWords = lesser._len / lesser.bitsPerSizeT;
+ immutable endBits = lesser._len % lesser.bitsPerSizeT;
+ auto p1 = this._ptr;
+ auto p2 = a2._ptr;
+
+ foreach (i; 0 .. fullWords)
+ {
+ if (p1[i] != p2[i])
+ {
+ return p1[i] & (size_t(1) << bsf(p1[i] ^ p2[i])) ? 1 : -1;
+ }
+ }
+
+ if (endBits)
+ {
+ immutable i = fullWords;
+ immutable diff = p1[i] ^ p2[i];
+ if (diff)
+ {
+ immutable index = bsf(diff);
+ if (index < endBits)
+ {
+ // This gets optimized into OPbtst, and was doing it incorrectly
+ return p1[i] & (size_t(1) << index) ? 1 : -1;
+ }
+ }
+ }
+
+ return -1;
+ }
+}
+
+void test1()
+{
+ bool[] ba = [1,0,1,0,1];
+ bool[] bd = [1,0,1,1,1];
+
+ auto a = BitArray(ba);
+ auto d = BitArray(bd);
+
+ assert(a < d);
+}
+
+/***************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=18748
+
+int bt_32_imm(in uint* p)
+{
+ enum bitnum = 1;
+ return ((p[bitnum >> 5] & (1 << (bitnum & 31)))) != 0;
+}
+
+void test18748()
+{
+ version (linux)
+ {
+ import core.sys.posix.sys.mman;
+ import core.sys.posix.unistd;
+ // Allocate two pages.
+ immutable sz = 2 * sysconf(_SC_PAGESIZE);
+ auto m = mmap(null, sz, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
+ // Discard the higher page. It becomes unreadable.
+ munmap(m + sz / 2, sz / 2);
+ // Try looking at the last 4 bytes of the readable page.
+ uint* p = cast(uint*) (m + sz / 2 - uint.sizeof);
+ bt_32_imm(p);
+ munmap(m, sz / 2); // Free the readable page.
+ }
+}
+
+/***************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=18749
+
+ulong f(ulong* p, uint shift)
+{
+ return (*p >> shift) & 1;
+}
+
+ulong g(ulong* p, ulong shift)
+{
+ return f(p, cast(uint) shift);
+}
+
+void test18749()
+{
+ enum shift = uint.max + 1L;
+ assert(cast(uint) shift == 0);
+ ulong s = 1;
+ assert(g(&s, shift));
+}
+
+
+/***************************************/
+
+int main()
+{
+ test1();
+ test18748();
+ test18749();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/testcgelem.d b/gcc/testsuite/gdc.test/runnable/testcgelem.d
new file mode 100644
index 00000000000..b5c7f7d1855
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/testcgelem.d
@@ -0,0 +1,47 @@
+/*
+REQUIRED_ARGS: -mcpu=native -preview=intpromote
+PERMUTE_ARGS: -O -inline -release
+*/
+
+/***
+ * Do coverage testing of cgelem.d
+ * Check coverage here:
+ * https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/cgelem.d
+ */
+
+import core.stdc.stdio;
+
+template tuple(A...) { alias tuple = A; }
+
+/*************************************************/
+
+void test_eladdr()
+{
+ // & (*p1 = e) => ((*(t = p1) = e), t)
+ int i = 4;
+ int* p1 = &i;
+ int e = 5;
+ auto x = &(*p1 = e);
+ assert(i == 5);
+ assert(x == p1);
+}
+
+/*************************************************/
+
+void test_elneg()
+{
+ static int i = 3;
+ int j = - -i;
+ assert(i == j);
+}
+
+/*************************************************/
+
+int main()
+{
+ test_eladdr();
+ test_elneg();
+
+ printf("Success\n");
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/testclass.d b/gcc/testsuite/gdc.test/runnable/testclass.d
index c5480da0cc5..cd8dd84c2cf 100644
--- a/gcc/testsuite/gdc.test/runnable/testclass.d
+++ b/gcc/testsuite/gdc.test/runnable/testclass.d
@@ -1,7 +1,13 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern(C) int printf(const char*, ...);
/******************************************/
-// 12078
+// https://issues.dlang.org/show_bug.cgi?id=12078
class B12078(T)
{
@@ -26,7 +32,7 @@ void test12078()
}
/******************************************/
-// 12143
+// https://issues.dlang.org/show_bug.cgi?id=12143
class Node12143
{
@@ -39,7 +45,7 @@ class Type12143 : Node12143 {}
class Class12143 : Type12143 {}
/***************************************************/
-// 13353
+// https://issues.dlang.org/show_bug.cgi?id=13353
interface Base13353(T)
{
@@ -57,7 +63,7 @@ class Concrete13353 : Derived13353
}
/***************************************************/
-// 15733
+// https://issues.dlang.org/show_bug.cgi?id=15733
class CStmt15733 : CNode15733 {}
class CDecl15733 : CStmt15733 {}
@@ -71,8 +77,48 @@ template IMix(T){ mixin("static " ~ T.stringof ~ " x;"); }
/***************************************************/
+// https://issues.dlang.org/show_bug.zip?id=20716
+
+extern(C++):
+
+struct S20716
+{
+ void* s;
+ ~this() {}
+ // or this(this) {}
+}
+
+interface I20716
+{
+ S20716 x();
+}
+
+final class C20716 : I20716
+{
+ int l = 3;
+
+ S20716 x()
+ {
+ //printf("this = %p, %p\n", this, &this.l);
+ assert(l == 3); //fails
+ return S20716.init;
+ }
+}
+
+extern(D):
+
+void test20716()
+{
+ auto s = new C20716().x;
+ auto t = new C20716().I20716.x;
+}
+
+/***************************************************/
+
int main()
{
+ test20716();
+
printf("Success\n");
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/testconst.d b/gcc/testsuite/gdc.test/runnable/testconst.d
index 7ec42452ec5..5c0e75de60f 100644
--- a/gcc/testsuite/gdc.test/runnable/testconst.d
+++ b/gcc/testsuite/gdc.test/runnable/testconst.d
@@ -53,7 +53,7 @@ template TypeTuple(T...) { alias T TypeTuple; }
void showf(string f)
{
- printf("%.*s\n", f.length, f.ptr);
+ printf("%.*s\n", cast(int)f.length, f.ptr);
}
/************************************/
@@ -626,7 +626,7 @@ class C42
void test42()
{
- printf("%d\n", C42.classinfo.initializer.length);
+ printf("%zd\n", C42.classinfo.initializer.length);
assert(C42.classinfo.initializer.length == 12 + (void*).sizeof +
(void*).sizeof);
C42 c = new C42;
@@ -963,7 +963,7 @@ void test56()
{
S56 s;
S56 t;
- printf("S56.sizeof = %d\n", S56.sizeof);
+ printf("S56.sizeof = %zd\n", S56.sizeof);
//t = s;
}
@@ -1574,7 +1574,7 @@ void test87()
}
/************************************/
-// 2751
+// https://issues.dlang.org/show_bug.cgi?id=2751
void test88(immutable(int[3]) a)
@@ -1603,7 +1603,7 @@ void test88(immutable(int[3]) a)
}
/************************************/
-// 3748
+// https://issues.dlang.org/show_bug.cgi?id=3748
// version = error8;
// version = error11;
@@ -1878,7 +1878,7 @@ void test3748c(inout int = 1)
}
/************************************/
-// 4968
+// https://issues.dlang.org/show_bug.cgi?id=4968
void test4968()
{
@@ -1924,7 +1924,7 @@ void test4968()
}
/************************************/
-// 1961
+// https://issues.dlang.org/show_bug.cgi?id=1961
inout(char)[] strstr(inout(char)[] source, const(char)[] pattern)
{
@@ -2042,7 +2042,7 @@ void test88()
}
/************************************/
-// 4251
+// https://issues.dlang.org/show_bug.cgi?id=4251
void test4251a()
{
@@ -2185,7 +2185,7 @@ void test4251b()
}
/************************************/
-// 5473
+// https://issues.dlang.org/show_bug.cgi?id=5473
void test5473()
{
@@ -2227,7 +2227,7 @@ void test5473()
}
/************************************/
-// 5493
+// https://issues.dlang.org/show_bug.cgi?id=5493
void test5493()
{
@@ -2282,7 +2282,7 @@ void test5493()
}
/************************************/
-// 5493 + inout
+// https://issues.dlang.org/show_bug.cgi?id=5493 + inout
void test5493inout()
{
@@ -2330,7 +2330,7 @@ void test5493inout()
}
/************************************/
-// 6782
+// https://issues.dlang.org/show_bug.cgi?id=6782
struct Tuple6782(T...)
{
@@ -2368,7 +2368,7 @@ void test6782()
}
/************************************/
-// 6864
+// https://issues.dlang.org/show_bug.cgi?id=6864
int fn6864( const int n) { return 1; }
int fn6864(shared int n) { return 2; }
@@ -2387,7 +2387,7 @@ void test6864()
}
/************************************/
-// 6865
+// https://issues.dlang.org/show_bug.cgi?id=6865
shared(inout(int)) foo6865(shared(inout(int)) n){ return n; }
void test6865()
@@ -2397,7 +2397,7 @@ void test6865()
}
/************************************/
-// 6866
+// https://issues.dlang.org/show_bug.cgi?id=6866
struct S6866
{
@@ -2414,7 +2414,7 @@ void test6866()
}
/************************************/
-// 6867
+// https://issues.dlang.org/show_bug.cgi?id=6867
inout(char)[] test6867(inout(char)[] a)
{
@@ -2428,7 +2428,7 @@ inout(char)[] test6867(inout(char)[] a)
}
/************************************/
-// 6870
+// https://issues.dlang.org/show_bug.cgi?id=6870
void test6870()
{
@@ -2440,7 +2440,8 @@ void test6870()
}
/************************************/
-// 6338, 6922
+// https://issues.dlang.org/show_bug.cgi?id=6338
+// https://issues.dlang.org/show_bug.cgi?id=6922
alias int T;
@@ -2468,7 +2469,7 @@ static assert(is( inout(shared(immutable(T))) == immutable(T) ));
static assert(is( inout(immutable(shared(T))) == immutable(T) ));
/************************************/
-// 6912
+// https://issues.dlang.org/show_bug.cgi?id=6912
void test6912()
{
@@ -2585,7 +2586,7 @@ void test6912()
}
/************************************/
-// 6930
+// https://issues.dlang.org/show_bug.cgi?id=6930
void test6930a()
{
@@ -2657,7 +2658,7 @@ void test6930b(inout int = 0)
}
/************************************/
-// 11868
+// https://issues.dlang.org/show_bug.cgi?id=11868
void f11868(A...)(A) { }
@@ -2673,7 +2674,7 @@ void test11868()
}
/************************************/
-// 11924
+// https://issues.dlang.org/show_bug.cgi?id=11924
inout(StringType) localize11924(StringType)(inout StringType str, string locale)
{
@@ -2695,7 +2696,7 @@ struct S11924
}
/************************************/
-// 11966
+// https://issues.dlang.org/show_bug.cgi?id=11966
inout(char)[] stripped11966 (inout(char)[] path)
{
@@ -2721,7 +2722,7 @@ void test11966()
}
/************************************/
-// 14788
+// https://issues.dlang.org/show_bug.cgi?id=14788
auto make14788(K, V)(inout V[K] aa)
{
@@ -2740,7 +2741,7 @@ void test14788()
}
/************************************/
-// 12089
+// https://issues.dlang.org/show_bug.cgi?id=12089
void foo12089(inout(char[]) a)
{
@@ -2754,7 +2755,7 @@ void decodeImpl12089(S)(auto ref S str)
{}
/************************************/
-// 12524
+// https://issues.dlang.org/show_bug.cgi?id=12524
inout(int) dup12524(inout(const(int)) val)
{
@@ -2771,7 +2772,7 @@ void test12524(inout(int))
}
/************************************/
-// 6941
+// https://issues.dlang.org/show_bug.cgi?id=6941
static assert((const(shared(int[])[])).stringof == "const(shared(int[])[])"); // fail
static assert((const(shared(int[])[])).stringof != "const(shared(const(int[]))[])"); // fail
@@ -2780,7 +2781,7 @@ static assert((inout(shared(int[])[])).stringof == "inout(shared(int[])[])");
static assert((inout(shared(int[])[])).stringof != "inout(shared(inout(int[]))[])"); // fail
/************************************/
-// 6872
+// https://issues.dlang.org/show_bug.cgi?id=6872
static assert((shared(inout(int)[])).stringof == "shared(inout(int)[])");
static assert((shared(inout(const(int)[]))).stringof == "shared(inout(const(int)[]))");
@@ -2788,7 +2789,7 @@ static assert((shared(inout(const(int)[])[])).stringof == "shared(inout(const(in
static assert((shared(inout(const(immutable(int)[])[])[])).stringof == "shared(inout(const(immutable(int)[])[])[])");
/************************************/
-// 6939
+// https://issues.dlang.org/show_bug.cgi?id=6939
void test6939()
{
@@ -2808,7 +2809,7 @@ void test6939()
}
/************************************/
-// 6940
+// https://issues.dlang.org/show_bug.cgi?id=6940
void test6940()
{
@@ -2830,7 +2831,7 @@ void test6940()
}
/************************************/
-// 6982
+// https://issues.dlang.org/show_bug.cgi?id=6982
void test6982()
{
@@ -2847,7 +2848,7 @@ void test6982()
}
/************************************/
-// 7038
+// https://issues.dlang.org/show_bug.cgi?id=7038
static assert(is(S7038 == const));
const struct S7038{ int x; }
@@ -2875,7 +2876,7 @@ void test7038()
}
/************************************/
-// 7105
+// https://issues.dlang.org/show_bug.cgi?id=7105
void copy(inout(int)** tgt, inout(int)* src){ *tgt = src; }
@@ -2909,11 +2910,11 @@ void test7105()
}
/************************************/
-// 7202
+// https://issues.dlang.org/show_bug.cgi?id=7202
void test7202()
{
- void writeln(string s) @system { printf("%.*s\n", s.length, s.ptr); }
+ void writeln(string s) @system { printf("%.*s\n", cast(int)s.length, s.ptr); }
void delegate() @system x = { writeln("I am @system"); };
void delegate() @safe y = { };
auto px = &x;
@@ -2924,7 +2925,7 @@ void test7202()
}
/************************************/
-// 7554
+// https://issues.dlang.org/show_bug.cgi?id=7554
T outer7554(T)(immutable T function(T) pure foo) pure {
pure int inner() {
@@ -2957,7 +2958,7 @@ void test7518() {
}
/************************************/
-// 7669
+// https://issues.dlang.org/show_bug.cgi?id=7669
shared(inout U)[n] id7669(U, size_t n)( shared(inout U)[n] );
void test7669()
@@ -2966,7 +2967,7 @@ void test7669()
}
/************************************/
-// 7757
+// https://issues.dlang.org/show_bug.cgi?id=7757
inout(int) foo7757a(int x, lazy inout(int) def) { return def; }
inout(int)[] foo7757b(int x, lazy inout(int)[] def) { return def; }
@@ -2999,7 +3000,7 @@ void test7757()
}
/************************************/
-// 8098
+// https://issues.dlang.org/show_bug.cgi?id=8098
class Outer8098
{
@@ -3033,7 +3034,7 @@ void test8098()
}
/************************************/
-// 8099
+// https://issues.dlang.org/show_bug.cgi?id=8099
void test8099()
{
@@ -3068,7 +3069,7 @@ void test8099()
}
/************************************/
-// 8201
+// https://issues.dlang.org/show_bug.cgi?id=8201
void test8201()
{
@@ -3080,7 +3081,7 @@ void test8201()
}
/************************************/
-// 8212
+// https://issues.dlang.org/show_bug.cgi?id=8212
struct S8212 { int x; }
@@ -3095,7 +3096,7 @@ void test8212()
}
/************************************/
-// 8366
+// https://issues.dlang.org/show_bug.cgi?id=8366
class B8366
{
@@ -3115,7 +3116,7 @@ class C8366a : B8366
class C8366b : B8366
{
bool foo(in Object o) { return false; }
- alias super.foo foo;
+ alias typeof(super).foo foo;
bool foo(in Object o) immutable { return false; }
bool foo(in Object o) shared { return false; }
bool foo(in Object o) shared const { return false; }
@@ -3146,7 +3147,7 @@ void test8366()
}
/************************************/
-// 8408
+// https://issues.dlang.org/show_bug.cgi?id=8408
template hasMutableIndirection8408(T)
{
@@ -3211,18 +3212,20 @@ void test8408()
struct T2 { S2 s; }
struct T3 { S1 s1; S2 s2; }
- test!(int , true )();
- test!(int[3], true )();
+/*
+ test!(int , false)();
+ test!(int[3], false)();
test!(C , false)();
- test!(S1 , true )();
+ test!(S1 , false)();
test!(S2 , false)();
- test!(T1 , true )();
+ test!(T1 , false)();
test!(T2 , false)();
test!(T3 , false)();
+*/
}
/************************************/
-// 8688
+// https://issues.dlang.org/show_bug.cgi?id=8688
void test8688()
{
@@ -3236,7 +3239,7 @@ void test8688()
}
/************************************/
-// 10946 (regression by fixing bug 8688, from 2.061)
+// https://issues.dlang.org/show_bug.cgi?id=10946 (regression by fixing bug 8688, from 2.061)
enum xlen10946 = 4;
alias immutable(char)[xlen10946] e3;
@@ -3246,12 +3249,12 @@ alias immutable(char)[vlen10946] i3;
alias immutable(char[vlen10946]) i4; // NG -> OK
/************************************/
-// 9046
+// https://issues.dlang.org/show_bug.cgi?id=9046
void test9046()
{
foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, char, wchar, dchar,
- float, double, real, ifloat, idouble, ireal, cfloat, cdouble, creal))
+ float, double, real))
foreach (U; TypeTuple!(T, const T, immutable T, shared T, shared const T, inout T, shared inout T))
{
static assert(is(typeof(U.init) == U));
@@ -3285,7 +3288,7 @@ void test9046()
}
/************************************/
-// 9090
+// https://issues.dlang.org/show_bug.cgi?id=9090
void test9090()
{
@@ -3296,7 +3299,7 @@ void test9090()
}
/************************************/
-// 9461
+// https://issues.dlang.org/show_bug.cgi?id=9461
void test9461()
{
@@ -3328,15 +3331,15 @@ void test9209() {
}
/************************************/
-// 10758
+// https://issues.dlang.org/show_bug.cgi?id=10758
struct X10758
{
static:
inout(int) screwUpVal(ref inout(int) wx) { return wx; }
- ref inout(int) screwUpRef(ref inout(int) wx) { return wx; }
- inout(int)* screwUpPtr(ref inout(int) wx) { return &wx; }
- inout(int)[] screwUpArr(ref inout(int) wx) { return (&wx)[0 .. 1]; }
+ ref inout(int) screwUpRef(return ref inout(int) wx) { return wx; }
+ inout(int)* screwUpPtr(return ref inout(int) wx) { return &wx; }
+ inout(int)[] screwUpArr(return ref inout(int) wx) { return (&wx)[0 .. 1]; }
}
struct S10758
@@ -3471,7 +3474,7 @@ void test10758(ref inout(int) wx, inout(int)* wp, inout(int)[] wa, inout(S10758)
}
/************************************/
-// 10761
+// https://issues.dlang.org/show_bug.cgi?id=10761
inout(int)* function(inout(int)*) fptr10761(inout(int)*)
{
@@ -3545,7 +3548,7 @@ void test10761()
}
/************************************/
-// 11226
+// https://issues.dlang.org/show_bug.cgi?id=11226
void test11226()
{
@@ -3573,7 +3576,7 @@ void test11226()
}
/************************************/
-// 11257
+// https://issues.dlang.org/show_bug.cgi?id=11257
struct R11257
{
@@ -3590,7 +3593,7 @@ void test11257()
}
/************************************/
-// 11215
+// https://issues.dlang.org/show_bug.cgi?id=11215
shared(inout(void)**) f11215(inout int);
@@ -3598,7 +3601,7 @@ static assert(is(typeof(f11215(0)) == shared(void**)));
static assert(is(typeof(f11215((const int).init)) == shared(const(void)**)));
/************************************/
-// 11489
+// https://issues.dlang.org/show_bug.cgi?id=11489
void test11489(inout int = 0)
{
@@ -3671,7 +3674,7 @@ void test11489(inout int = 0)
}
/************************************/
-// 11768
+// https://issues.dlang.org/show_bug.cgi?id=11768
void test11768(inout int = 0)
{
@@ -3683,7 +3686,7 @@ void test11768(inout int = 0)
}
/************************************/
-// 12403
+// https://issues.dlang.org/show_bug.cgi?id=12403
void test12403()
{
@@ -3698,7 +3701,7 @@ void test12403()
}
/************************************/
-// 13011
+// https://issues.dlang.org/show_bug.cgi?id=13011
void test13011()
{
@@ -3712,7 +3715,7 @@ void test13011()
}
/************************************/
-// 13030
+// https://issues.dlang.org/show_bug.cgi?id=13030
void va13030(Args...)(const Args args) {}
@@ -3722,7 +3725,8 @@ void func13030(int delegate(int n) a)
}
/************************************/
-// 13802 & 13803
+// https://issues.dlang.org/show_bug.cgi?id=13802
+// https://issues.dlang.org/show_bug.cgi?id=13803
static assert(( string ).stringof == "string" );
static assert(( string[] ).stringof == "string[]" );
diff --git a/gcc/testsuite/gdc.test/runnable/testcontracts.d b/gcc/testsuite/gdc.test/runnable/testcontracts.d
index f99a10afff2..786969104f5 100644
--- a/gcc/testsuite/gdc.test/runnable/testcontracts.d
+++ b/gcc/testsuite/gdc.test/runnable/testcontracts.d
@@ -1,5 +1,21 @@
-// PERMUTE_ARGS: -inline -g -O
-
+/* PERMUTE_ARGS: -inline -g -O
+TEST_OUTPUT:
+---
+runnable/testcontracts.d(323): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(324): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(325): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(326): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(328): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(329): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(330): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(331): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(502): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(503): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(504): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(505): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+runnable/testcontracts.d(505): Deprecation: Usage of the `body` keyword is deprecated. Use `do` instead.
+---
+*/
extern(C) int printf(const char*, ...);
/*******************************************/
@@ -192,7 +208,7 @@ void test5()
}
/*******************************************/
-// 3273
+// https://issues.dlang.org/show_bug.cgi?id=3273
// original case
struct Bug3273
@@ -205,7 +221,7 @@ struct Bug3273
ref int func3273()
out(r)
{
- // Regression check of issue 3390
+ // Regression check of https://issues.dlang.org/show_bug.cgi?id=3390
static assert(!__traits(compiles, r = 1));
}
do
@@ -318,13 +334,13 @@ void test9()
/*******************************************/
-auto test10() body { return 3; }
-auto test11()() body { return 3; }
+auto test10() do { return 3; }
+auto test11()() do { return 3; }
auto test12()
{
- auto test10() body { return 3; }
- auto test11()() body { return 3; }
+ auto test10() do { return 3; }
+ auto test11()() do { return 3; }
return 3;
}
@@ -335,7 +351,7 @@ void test13()
}
/*******************************************/
-// 4785
+// https://issues.dlang.org/show_bug.cgi?id=4785
int cnt;
@@ -360,7 +376,7 @@ void test4785()
}
/*******************************************/
-// 5039
+// https://issues.dlang.org/show_bug.cgi?id=5039
class C5039 {
int x;
@@ -375,7 +391,7 @@ class C5039 {
}
/*******************************************/
-// 5204
+// https://issues.dlang.org/show_bug.cgi?id=5204
interface IFoo5204
{
@@ -388,7 +404,7 @@ class Foo5204 : IFoo5204
}
/*******************************************/
-// 6417
+// https://issues.dlang.org/show_bug.cgi?id=6417
class Bug6417
{
@@ -422,7 +438,7 @@ void test6417()
}
/*******************************************/
-// 6549
+// https://issues.dlang.org/show_bug.cgi?id=6549
class C6549
{
@@ -472,7 +488,7 @@ void test6549()
}
/*******************************************/
-// 7218
+// https://issues.dlang.org/show_bug.cgi?id=7218
void test7218()
{
@@ -491,7 +507,55 @@ void test7218()
}
/*******************************************/
-// 7517
+// https://issues.dlang.org/show_bug.cgi?id=7335
+
+class A7335
+{
+ int mValue = 10;
+
+ void setValue(int newValue)
+ in { }
+ out { assert(mValue == 3); }
+ do
+ {
+ mValue = newValue;
+ }
+}
+
+class B7335 : A7335
+{
+ override void setValue(int newValue)
+ in { assert(false); }
+ out { assert(mValue == 3); }
+ do
+ {
+ mValue = newValue;
+ }
+}
+
+class C7335 : A7335
+{
+ override void setValue(int newValue)
+ in { int a = newValue; }
+ out { assert(mValue == 3); }
+ do
+ {
+ mValue = newValue;
+ }
+}
+
+void test7335()
+{
+ A7335 aObject = new B7335();
+ aObject.setValue(3);
+
+ A7335 bObject = new C7335();
+ bObject.setValue(3); // <<<<< will crash because undefined mValue in the
+ // A7335.setValue().out-block.
+}
+
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7517
void test7517()
{
@@ -575,7 +639,7 @@ void test7517()
}
/*******************************************/
-// 7699
+// https://issues.dlang.org/show_bug.cgi?id=7699
class P7699
{
@@ -589,7 +653,7 @@ class D7699 : P7699
}
/*******************************************/
-// 7883
+// https://issues.dlang.org/show_bug.cgi?id=7883
// Segmentation fault
class AA7883
@@ -665,7 +729,7 @@ class DC7883 : CC7883
}
/*******************************************/
-// 7892
+// https://issues.dlang.org/show_bug.cgi?id=7892
struct S7892
{
@@ -695,7 +759,7 @@ class C7892
}
/*******************************************/
-// 8066
+// https://issues.dlang.org/show_bug.cgi?id=8066
struct CLCommandQueue
{
@@ -709,7 +773,7 @@ struct CLCommandQueue
}
/*******************************************/
-// 8073
+// https://issues.dlang.org/show_bug.cgi?id=8073
struct Container8073
{
@@ -748,7 +812,7 @@ void test8073()
}
/*******************************************/
-// 8093
+// https://issues.dlang.org/show_bug.cgi?id=8093
void test8093()
{
@@ -785,7 +849,7 @@ void test8093()
}
/*******************************************/
-// 9383
+// https://issues.dlang.org/show_bug.cgi?id=9383
class A9383
{
@@ -871,7 +935,8 @@ void test9383()
}
/*******************************************/
-// 15524 - Different from issue 9383 cases, closed variable size is bigger than REGSIZE.
+// https://issues.dlang.org/show_bug.cgi?id=15524
+// Different from issue 9383 cases, closed variable size is bigger than REGSIZE.
class A15524
{
@@ -1004,7 +1069,7 @@ class Test15524b
}
/*******************************************/
-// 10479
+// https://issues.dlang.org/show_bug.cgi?id=10479
class B10479
{
@@ -1018,7 +1083,7 @@ class D10479 : B10479
}
/*******************************************/
-// 10596
+// https://issues.dlang.org/show_bug.cgi?id=10596
class Foo10596
{
@@ -1028,7 +1093,7 @@ class Foo10596
}
/*******************************************/
-// 10721
+// https://issues.dlang.org/show_bug.cgi?id=10721
class Foo10721
{
@@ -1049,7 +1114,7 @@ struct Bar10721
}
/*******************************************/
-// 10981
+// https://issues.dlang.org/show_bug.cgi?id=10981
class C10981
{
@@ -1060,7 +1125,7 @@ class C10981
}
/*******************************************/
-// 14779
+// https://issues.dlang.org/show_bug.cgi?id=14779
class C14779
{
@@ -1079,6 +1144,79 @@ void test14779()
}
/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15984
+
+I15984 i15984;
+C15984 c15984;
+
+void check15984(T)(const char* s, T this_, int i)
+{
+ printf("%s this = %p, i = %d\n", s, this_, i);
+ static if (is(T == I15984)) assert(this_ is i15984);
+ else static if (is(T == C15984)) assert(this_ is c15984);
+ else static assert(0);
+ assert(i == 5);
+}
+
+interface I15984
+{
+ void f1(int i)
+ in { check15984("I.f1.i", this, i); assert(0); }
+ out { check15984("I.f1.o", this, i); }
+
+ int[3] f2(int i)
+ in { check15984("I.f2.i", this, i); assert(0); }
+ out { check15984("I.f2.o", this, i); }
+
+ void f3(int i)
+ in { void nested() { check15984("I.f3.i", this, i); } nested(); assert(0); }
+ out { void nested() { check15984("I.f3.o", this, i); } nested(); }
+
+ void f4(out int i, lazy int j)
+ in { }
+ out { }
+}
+
+class C15984 : I15984
+{
+ void f1(int i)
+ in { check15984("C.f1.i", this, i); }
+ out { check15984("C.f1.o", this, i); }
+ do { check15984("C.f1 ", this, i); }
+
+ int[3] f2(int i)
+ in { check15984("C.f2.i", this, i); }
+ out { check15984("C.f2.o", this, i); }
+ do { check15984("C.f2 ", this, i); return [0,0,0]; }
+
+ void f3(int i)
+ in { void nested() { check15984("C.f3.i", this, i); } nested(); }
+ out { void nested() { check15984("C.f3.o", this, i); } nested(); }
+ do { check15984("C.f3 ", this, i); }
+
+ void f4(out int i, lazy int j)
+ in { assert(0); }
+ do { i = 10; }
+}
+
+void test15984()
+{
+ c15984 = new C15984;
+ i15984 = c15984;
+ printf("i = %p\n", i15984);
+ printf("c = %p\n", c15984);
+ printf("====\n");
+ i15984.f1(5);
+ printf("====\n");
+ i15984.f2(5);
+ printf("====\n");
+ i15984.f3(5);
+ int i;
+ i15984.f4(i, 1);
+ assert(i == 10);
+}
+
+/*******************************************/
//******************************************/
// DIP 1009
@@ -1170,6 +1308,7 @@ int main()
test6417();
test6549();
test7218();
+ test7335();
test7517();
test8073();
test8093();
@@ -1177,6 +1316,7 @@ int main()
test15524();
test15524a();
test14779();
+ test15984();
dip1009_1(1);
dip1009_2(1);
dip1009_3(1);
diff --git a/gcc/testsuite/gdc.test/runnable/testdefault_after_variadic.d b/gcc/testsuite/gdc.test/runnable/testdefault_after_variadic.d
new file mode 100644
index 00000000000..f0bade264e1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/testdefault_after_variadic.d
@@ -0,0 +1,98 @@
+/*
+PERMUTE_ARGS:
+*/
+
+version (with_phobos) import std.conv : text;
+
+struct Tuple(T...)
+{
+ T expand;
+ alias expand this;
+}
+
+auto tuple(T...)(T t)
+{
+ return Tuple!T(t);
+}
+
+void fun0(U, T...)(U gold, int b_gold, T a, int b)
+{
+ assert(tuple(a) == gold);
+ assert(b == b_gold);
+}
+
+void fun(U, T...)(U gold, T a, int b = 1)
+{
+ assert(tuple(a) == gold);
+ assert(b == 1);
+}
+
+void fun2(U, V, T...)(U gold, V gold2, T a, string file = __FILE__, int line = __LINE__)
+{
+ assert(tuple(a) == gold);
+ assert(tuple(file, line) == gold2);
+}
+
+void fun3(int[] gold, int[] a...)
+{
+ assert(gold == a);
+}
+
+void fun4(T...)(size_t length_gold, int b_gold, T a, int b = 1)
+{
+ assert(T.length == length_gold);
+ assert(b == b_gold);
+}
+
+// Example in changelog
+string log(T...)(T a, string file = __FILE__, int line = __LINE__)
+{
+ return text(file, ":", line, " ", a);
+}
+
+void fun_constraint(T...)(T a, string b = "bar") if (T.length == 1)
+{
+}
+
+/+
+NOTE: this is disallowed by the parser:
+void fun5(int[] gold, int[] a ..., int b = 1)
+{
+ assert(gold==a);
+ assert(b==1);
+}
++/
+
+void main()
+{
+ fun0(tuple(10), 7, 10, 7);
+
+ fun(tuple());
+ fun(tuple(10), 10);
+ fun(tuple(10, 11), 10, 11);
+
+ fun2(tuple(10), tuple(__FILE__, __LINE__), 10);
+
+ fun3([1, 2, 3], 1, 2, 3);
+
+ fun_constraint(1);
+ assert(!__traits(compiles, fun_constraint(1, "baz")));
+
+ version (with_phobos)
+ assert(log(10, "abc") == text(__FILE__, ":", __LINE__, " 10abc"));
+
+ // IFTI: `b` is always default-set
+ fun4(0, 1);
+ fun4(1, 1, 10);
+ fun4(2, 1, 10, 11);
+
+ // with explicit instantiation, and default-set `b`
+ fun4!int(1, 1, 10);
+ fun4!(int, int)(2, 1, 10, 11);
+
+ // with explicit instantiation, and over-ridden `b`
+ fun4!int(1, 100, 10, 100);
+ fun4!(int, int)(2, 100, 10, 11, 100);
+
+ // fun5([1,2,3], 1,2,3);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/testdstress.d b/gcc/testsuite/gdc.test/runnable/testdstress.d
index a416b8a8c85..6ca5500bfb3 100644
--- a/gcc/testsuite/gdc.test/runnable/testdstress.d
+++ b/gcc/testsuite/gdc.test/runnable/testdstress.d
@@ -1,5 +1,10 @@
-// RUNNABLE_PHOBOS_TEST
// PERMUTE_ARGS:
+/*
+TEST_OUTPUT:
+---
+runnable/testdstress.d(666): Deprecation: The `delete` keyword has been deprecated. Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.
+---
+*/
module run.module_01;
@@ -8,6 +13,7 @@ import core.exception;
import core.vararg;
extern(C) void* malloc(size_t size);
+extern(C) int printf(const char*, ...);
/* ================================ */
@@ -23,7 +29,7 @@ void test1()
assert(1);
}out (result){
assert(result.i==1);
- }body{
+ }do{
MyStruct s;
s.i = 1;
return s;
@@ -37,7 +43,7 @@ void foo2()
in{
assert(0);
}
-body{
+do{
}
void test2()
@@ -452,7 +458,7 @@ void test21()
/* ================================ */
-scope class AutoClass{
+class AutoClass{
}
void test22()
@@ -467,7 +473,7 @@ void test22()
int status23;
-scope class C23{
+class C23{
~this(){
assert(status23==0);
status23--;
@@ -493,7 +499,7 @@ void test23()
int status24;
-scope class C24{
+class C24{
this(){
assert(status24==0);
status24+=2;
@@ -525,7 +531,7 @@ void test24()
/* ================================ */
struct S25{
- S25 opSub(int i) { S25 s; return s; }
+ S25 opBinary(string op)(int i) if (op == "-") { S25 s; return s; }
}
struct GeomObject{
@@ -539,7 +545,7 @@ void extractTriangles(GeomObject g)
void foobar()
{
g.mesh - g.xlate;
- //g.mesh.opSub(g.xlate);
+ //g.mesh.opBinary!("-")(g.xlate);
}
foobar();
@@ -672,7 +678,7 @@ void test30()
void test31()
{
- string str = x"F0 9D 83 93"; // utf-8 for U+1D0D3
+ string str = "\xF0\x9D\x83\x93"; // utf-8 for U+1D0D3
int count=0;
dchar tmp;
@@ -686,8 +692,6 @@ void test31()
/* ================================ */
-import std.stdio;
-
union MyUnion32
{
int i;
@@ -698,31 +702,12 @@ void test32()
{
TypeInfo ti = typeid(MyUnion32*);
assert(!(ti is null));
- writefln("%s %d %d", ti.toString(), ti.tsize, (MyUnion32*).sizeof);
assert(ti.tsize==(MyUnion32*).sizeof);
assert(ti.toString()=="run.module_01.MyUnion32*");
}
/* ================================ */
-void test33()
-{
- creal a=1.3L+9.7Li;
- assert(a.re == 1.3L);
- assert(a.im == 9.7L);
-}
-
-/* ================================ */
-
-void test34()
-{
- creal c = 2.7L + 0i;
- assert(c.re==2.7L);
- assert(c.im==0.0L);
-}
-
-/* ================================ */
-
void test35()
{
try{
@@ -845,15 +830,8 @@ int counter41;
class C41{
this(){
printf("this: counter41 = %d\n", counter41);
- assert(counter41==1);
- counter41+=2;
- }
-
- new(size_t size){
- printf("new: size = %d\n", size);
assert(counter41==0);
- counter41++;
- return malloc(size);
+ counter41+=2;
}
}
@@ -862,7 +840,7 @@ void test41()
C41 c;
assert(counter41==0);
c = new C41();
- assert(counter41==3);
+ assert(counter41==2);
}
/* ================================ */
@@ -917,8 +895,6 @@ int main()
test30();
test31();
test32();
- test33();
- test34();
test35();
test36();
test37();
diff --git a/gcc/testsuite/gdc.test/runnable/testdt.d b/gcc/testsuite/gdc.test/runnable/testdt.d
index 8224abc1356..4b5338fec5a 100644
--- a/gcc/testsuite/gdc.test/runnable/testdt.d
+++ b/gcc/testsuite/gdc.test/runnable/testdt.d
@@ -2,7 +2,7 @@
/******************************************/
-static int bigarray[100][100];
+static int[100][100] bigarray;
void test1()
{
@@ -17,7 +17,7 @@ void test1()
}
/******************************************/
-// 10629
+// https://issues.dlang.org/show_bug.cgi?id=10629
class Foo10629 {}
@@ -27,7 +27,7 @@ struct Bar10629
}
/******************************************/
-// 11233
+// https://issues.dlang.org/show_bug.cgi?id=11233
struct S11233
{
@@ -35,7 +35,7 @@ struct S11233
}
/******************************************/
-// 11672
+// https://issues.dlang.org/show_bug.cgi?id=11672
void test11672()
{
@@ -50,7 +50,7 @@ void test11672()
}
/******************************************/
-// 12509
+// https://issues.dlang.org/show_bug.cgi?id=12509
struct A12509
{
@@ -62,7 +62,7 @@ struct B12509
}
/******************************************/
-// 13505
+// https://issues.dlang.org/show_bug.cgi?id=13505
class C13505 { void[10] x; }
struct S13505 { void[10] x; }
@@ -74,7 +74,7 @@ void test13505()
}
/******************************************/
-// 14699
+// https://issues.dlang.org/show_bug.cgi?id=14699
struct S14699a { ubyte[0][10] values; }
struct S14699b { S14699a tbl; }
@@ -88,7 +88,7 @@ void test14699()
}
/******************************************/
-// 14805
+// https://issues.dlang.org/show_bug.cgi?id=14805
struct S14805
{
@@ -97,7 +97,7 @@ struct S14805
auto a14805 = new S14805[513*513];
/******************************************/
-// 15664
+// https://issues.dlang.org/show_bug.cgi?id=15664
struct Data15664A
{
diff --git a/gcc/testsuite/gdc.test/runnable/testenum.d b/gcc/testsuite/gdc.test/runnable/testenum.d
index 05327449209..a7529510e9b 100644
--- a/gcc/testsuite/gdc.test/runnable/testenum.d
+++ b/gcc/testsuite/gdc.test/runnable/testenum.d
@@ -109,7 +109,7 @@ void test6()
}
/**********************************************/
-// 2407
+// https://issues.dlang.org/show_bug.cgi?id=2407
int i2407;
@@ -223,7 +223,7 @@ void test2407()
}
/**********************************************/
-// 3096
+// https://issues.dlang.org/show_bug.cgi?id=3096
void test3096()
{
@@ -252,30 +252,30 @@ void test3096()
}
/**********************************************/
-// 7719
+// https://issues.dlang.org/show_bug.cgi?id=7719
enum foo7719 = bar7719;
enum { bar7719 = 1 }
/**********************************************/
-// 9845
+// https://issues.dlang.org/show_bug.cgi?id=9845
enum { A9845 = B9845 }
enum { B9845 = 1 }
/**********************************************/
-// 9846
+// https://issues.dlang.org/show_bug.cgi?id=9846
const int A9846 = B9846;
enum { B9846 = 1 }
/**********************************************/
-// 10105
+// https://issues.dlang.org/show_bug.cgi?id=10105
enum E10105 : char[1] { a = "a" }
/**********************************************/
-// 10113
+// https://issues.dlang.org/show_bug.cgi?id=10113
enum E10113 : string
{
@@ -299,7 +299,7 @@ void test10113()
}
/**********************************************/
-// 10503
+// https://issues.dlang.org/show_bug.cgi?id=10503
@property int octal10503(string num)()
{
@@ -313,7 +313,7 @@ enum
}
/**********************************************/
-// 10505
+// https://issues.dlang.org/show_bug.cgi?id=10505
enum
{
@@ -331,7 +331,7 @@ static assert(is(typeof(d10505) == int));
static assert(is(typeof(e10505) == typeof(null)));
/**********************************************/
-// 10561
+// https://issues.dlang.org/show_bug.cgi?id=10561
void test10561()
{
@@ -367,7 +367,7 @@ void test10561()
}
/**********************************************/
-// 10612
+// https://issues.dlang.org/show_bug.cgi?id=10612
int[E10612] ie10612;
E10612[int] ei10612;
@@ -376,7 +376,7 @@ E10612[E10612] ee10612;
enum E10612 { a }
/**********************************************/
-// 10788
+// https://issues.dlang.org/show_bug.cgi?id=10788
enum v10788 = e10788;
enum : int { e10788 }
@@ -417,7 +417,7 @@ void test8()
}
/**********************************************/
-// 13220
+// https://issues.dlang.org/show_bug.cgi?id=13220
enum E13220a;
@(1) enum E13220b;
diff --git a/gcc/testsuite/gdc.test/runnable/testfile.d b/gcc/testsuite/gdc.test/runnable/testfile.d
deleted file mode 100644
index 6f568044ea4..00000000000
--- a/gcc/testsuite/gdc.test/runnable/testfile.d
+++ /dev/null
@@ -1,25 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-// PERMUTE_ARGS:
-
-import std.file;
-import std.stdio;
-
-/***********************************************/
-
-void test1()
-{
- auto p = std.file.getcwd();
-
- writefln("%s '%s'\n", p.length, p);
- assert(p[$ - 1] != 0);
-}
-
-/***********************************************/
-
-int main()
-{
- test1();
-
- printf("Success\n");
- return 0;
-}
diff --git a/gcc/testsuite/gdc.test/runnable/testfloat.d b/gcc/testsuite/gdc.test/runnable/testfloat.d
new file mode 100644
index 00000000000..e9fe3970550
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/testfloat.d
@@ -0,0 +1,239 @@
+/* PERMUTE_ARGS: -O
+ * Test floating point code generation
+ */
+
+import core.stdc.stdio;
+import core.stdc.stdlib;
+
+double value_1() {
+ return 1;
+}
+
+double value_2() {
+ return 2;
+}
+
+/***************************************/
+
+void testcse1(T)() // common subexpressions
+{
+ T a = value_1();
+ T b = value_2();
+ T x = a*a + a*a + a*a + a*a + a*a + a*a + a*a +
+ a*b + a*b;
+ printf("%g\n", cast(double)x); // destroy scratch reg contents
+ T y = a*a + a*a + a*a + a*a + a*a + a*a + a*a +
+ a*b + a*b;
+ assert(x == 11);
+ assert(x == y);
+}
+
+void test240()
+{
+ testcse1!float();
+ testcse1!double();
+ testcse1!real();
+}
+
+/***************************************/
+
+void testcse2(T)() // common subexpressions
+{
+ T a = value_1();
+ T b = value_2();
+ T x = a*a + a*a + a*a + a*a + a*a + a*a + a*a +
+ a*b + a*b + 1;
+ printf("%g\n", cast(double)x); // destroy scratch reg contents
+ int i = (a*a + a*a + a*a + a*a + a*a + a*a + a*a + a*b + a*b) != 0;
+ assert(i);
+ assert(x == 12);
+}
+
+void test241()
+{
+ testcse2!float();
+ testcse2!double();
+ testcse2!real();
+}
+
+/***************************************/
+
+void test1(float f)
+{
+ real r = f;
+ double d = f;
+}
+
+void test2(long l)
+{
+ real r = l;
+ double d = l;
+}
+
+void test3(float f)
+{
+ real r = f * f;
+ double d = f * f;
+}
+
+void test3(long l)
+{
+ real r = l * l;
+ double d = l * l;
+}
+
+/***************************************/
+
+double foo4(int i, double d)
+{
+ return ((i << 1) - d) + ((i << 1) - d);
+}
+
+void test4()
+{
+ double d = foo4(3, 4);
+ assert(d == 4);
+}
+
+/***************************************/
+
+import core.math; // trigger use of sqrt intrinsic
+
+void test5x(double p)
+{
+ bool b = p >= 0;
+ double mean = (1 - p) / p;
+ double skew = sqrt(1 - p);
+}
+
+void test5()
+{
+ test5x(2);
+}
+
+/***************************************/
+
+import core.math; // trigger use of sqrt intrinsic
+
+void dstatsEnforce(bool, string) { }
+
+ulong invNegBinomCDF(double pVal, ulong n, double p)
+{
+ dstatsEnforce(p >= 0 && p <= 1,
+ "p must be between 0, 1 for negative binomial distribution.");
+ dstatsEnforce(pVal >= 0 && pVal <= 1,
+ "P-values must be between 0, 1.");
+
+ // Normal or gamma approx, then adjust.
+ double mean = n * (1 - p) / p;
+ double var = n * (1 - p) / (p * p);
+ double skew = (2 - p) / sqrt(n * (1 - p));
+ double kk = 4.0L / (skew * skew);
+ double theta = sqrt(var / kk);
+ double offset = (kk * theta) - mean + 0.5L;
+ ulong guess;
+ return 0;
+}
+
+void test6()
+{
+ invNegBinomCDF(2.0, 3, 4.0);
+}
+
+/***************************************/
+
+float expDigamma(F)(in F x)
+{
+ return x;
+}
+
+float nextDown(float f) { return f; }
+
+void test7()
+{
+ foreach (i; 1 .. 2)
+ {
+ assert(expDigamma(float(i)) >= expDigamma(float(i).nextDown));
+ }
+}
+
+/***************************************/
+
+void foo8_1(double x)
+{
+ printf("x = %g\n", x);
+ assert(x == 0);
+}
+
+void foo8_2(double x)
+{
+ printf("x = %g\n", x);
+ assert(x != 0);
+}
+
+void test8()
+{
+ foo8_1(0.0);
+ foo8_2(1.0);
+}
+
+/***************************************/
+
+void test9()
+{
+ double a = 9;
+ double b = 3;
+ double c = a * b + 1;
+ double d = a + b + 1;
+ printf("%g %g\n", c, d); // clobber XMM registers
+ assert(c == 28 && d == 13);
+ double e = a * b - 1;
+ double f = a + b - 1; // reload 2 CSEs
+ printf("%g %g\n", e, f);
+ assert(e == 26 && f == 11);
+}
+
+/***************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20349
+
+double f20349(double a, int b)
+{
+ import core.math;
+ return core.math.sqrt(-a / b) / b;
+}
+
+void test20349()
+{
+ assert(f20349(-9, 1) == 3);
+}
+
+/****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20963
+
+void test20963()
+{
+ ulong v = 0xE3251BACB112CB8B;
+ double d = cast(double)v;
+ printf("%a\n", d); //0x1.c64a37596225ap+63
+ assert(d == 0x1.c64a375962259p+63);
+}
+
+/***************************************/
+
+
+int main()
+{
+ test240();
+ test241();
+ test4();
+ test5();
+ test6();
+ test7();
+ test8();
+ test9();
+ test20349();
+ test20963();
+
+ printf("Success\n");
+ return EXIT_SUCCESS;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/testformat.d b/gcc/testsuite/gdc.test/runnable/testformat.d
deleted file mode 100644
index 74f4095c009..00000000000
--- a/gcc/testsuite/gdc.test/runnable/testformat.d
+++ /dev/null
@@ -1,126 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-// PERMUTE_ARGS:
-
-import std.stdio;
-import std.string;
-
-/*************************************************************/
-
-void test1()
-{
- double x = 1e6;
- float f;
- double d;
- real r;
-
- while (x > 1e-5)
- { double y = x / 101.0;
-
- std.stdio.writef("x = %g\t%f\t%e\n",x/101.0,x/101.0,x/101.0);
- x /= 10.0;
- }
-
- double a = 123456789.0;
- for (int i = 20; i--; a /= 10)
- std.stdio.writef("%20.6g|%20.6e|%20.6f\n",a,a,a);
-
- std.stdio.writef("%e %e %e %e\n",float.nan,double.nan,real.nan,double.infinity);
- std.stdio.writef("%e %e %e\n",-float.nan,-double.nan,-double.infinity);
- std.stdio.writef("%-5E %5E %E\n",float.nan,double.nan,double.infinity);
-
- std.stdio.writef("%f %f %f\n",float.nan,double.nan,double.infinity);
- std.stdio.writef("%f %f %f\n",-float.nan,-double.nan,-double.infinity);
- std.stdio.writef("%+F %+ F %F\n",float.nan,double.nan,double.infinity);
-
- std.stdio.writef("%g %g %g\n",float.nan,double.nan,double.infinity);
- std.stdio.writef("%g %g %g\n",-float.nan,-double.nan,-double.infinity);
- std.stdio.writef("% G %G %G\n",float.nan,double.nan,double.infinity);
-
- r = 0x1.AAp+3L;
- std.stdio.writef(" r = %g\n", r);
- std.stdio.writef(" r = %a\n", r);
- std.stdio.writef(" r = %A\n", r);
-
- d = 0x1.AAp+3;
- std.stdio.writef(" d = %.5a\n", d);
- std.stdio.writef(" d = %A\n", d);
-
- f = 0x1.AAp+3f;
- std.stdio.writef(" f = %a\n", f);
- std.stdio.writef(" f = %A\n", f);
-
- f = 0x1.FFp+3f;
- std.stdio.writef(" f = %.1a\n", f);
- std.stdio.writef(" f = %A\n", f);
-
- r = 0;
- std.stdio.writef(" r = %a\n", r);
- std.stdio.writef(" r = %A\n", r);
-
- std.stdio.writef("%e\n", 1e+300);
- std.stdio.writef("%.0f\n%.307f\n", 1e+300, 1e-300);
- std.stdio.writef("%.0A\n%.307A\n", 1e+300, 1e-300);
-}
-
-/*************************************************************/
-
-void test2()
-{
- writefln("%o", 9);
- assert(std.string.format("%o", 9) == "11");
- assert(std.string.format("%o", 10) == "12");
- assert(std.string.format("%b", 9) == "1001");
- assert(std.string.format("%b", 10) == "1010");
-}
-
-
-/*************************************************************/
-
-void test3()
-{
- Object e = new Exception("hello");
- writeln(e.toString());
- //assert(e.toString() == "object.Exception: hello");
- //assert(format(e) == "object.Exception: hello");
-
- alias char[] xstring;
- assert(format(cast(xstring)"world") == "world");
-}
-
-/*************************************************************/
-
-void test4()
-{
- const char[][] x = ["%s","123"];
- writeln(x);
- assert(std.string.format("%s", x) == `["%s", "123"]`);
-}
-
-/*************************************************************/
-
-void test5()
-{
- int[int] foo;
-
- foo[1] = 2;
- foo[7] = 28;
-
- writefln("%s", foo);
-
- void[0] v;
- assert(format("%s", v) == "[]");
-}
-
-/*************************************************************/
-
-int main()
-{
- test1();
- test2();
- test3();
- test4();
- test5();
-
- std.stdio.writefln("Success");
- return 0;
-}
diff --git a/gcc/testsuite/gdc.test/runnable/testgc2.d b/gcc/testsuite/gdc.test/runnable/testgc2.d
index 0d7ab8c4e3e..f107f1cc4c1 100644
--- a/gcc/testsuite/gdc.test/runnable/testgc2.d
+++ b/gcc/testsuite/gdc.test/runnable/testgc2.d
@@ -1,4 +1,10 @@
-// PERMUTE_ARGS:
+/*
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+Success
+---
+*/
module testgc2;
@@ -7,30 +13,34 @@ import core.exception : OutOfMemoryError;
/*******************************************/
+__gshared ulong counter;
+
void test1()
{
- printf("This should not take a while\n");
try
{
long[] l = new long[ptrdiff_t.max];
- printf("%lu\n", cast(ulong)l.capacity); // Make sure l is not optimized out.
+ counter += l.capacity; // Make sure l is not optimized out.
assert(0);
}
catch (OutOfMemoryError o)
{
}
- printf("This may take a while\n");
+ assert(counter == 0);
+
try
{
byte[] b = new byte[size_t.max / 3];
- printf("%lu\n", cast(ulong)b.capacity); // Make sure b is not optimized out.
+ counter += b.capacity; // Make sure b is not optimized out.
version (Windows)
assert(0);
}
catch (OutOfMemoryError o)
{
}
+
+ assert(counter >= 0);
}
/*******************************************/
@@ -41,5 +51,3 @@ void main()
printf("Success\n");
}
-
-
diff --git a/gcc/testsuite/gdc.test/runnable/testgc3.d b/gcc/testsuite/gdc.test/runnable/testgc3.d
index 88232fefe36..300888fc5cd 100644
--- a/gcc/testsuite/gdc.test/runnable/testgc3.d
+++ b/gcc/testsuite/gdc.test/runnable/testgc3.d
@@ -1,5 +1,11 @@
-// PERMUTE_ARGS:
-// REQUIRED_ARGS:
+/*
+PERMUTE_ARGS:
+REQUIRED_ARGS:
+RUN_OUTPUT:
+---
+finished
+---
+*/
import core.stdc.stdio;
@@ -21,4 +27,3 @@ void main()
printf("finished\n");
aa[] = null;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/testinvariant.d b/gcc/testsuite/gdc.test/runnable/testinvariant.d
index 93d66efd21a..d3b3b6ffa16 100644
--- a/gcc/testsuite/gdc.test/runnable/testinvariant.d
+++ b/gcc/testsuite/gdc.test/runnable/testinvariant.d
@@ -17,7 +17,7 @@ int testinvariant()
printf("hello\n");
Foo f = new Foo();
printf("f = %p\n", f);
- printf("f.sizeof = x%x\n", Foo.sizeof);
+ printf("f.sizeof = x%zx\n", Foo.sizeof);
printf("f.classinfo = %p\n", f.classinfo);
printf("f.classinfo._invariant = %p\n", f.classinfo.base);
f.test();
@@ -26,7 +26,7 @@ int testinvariant()
}
/***************************************************/
-// 6453
+// https://issues.dlang.org/show_bug.cgi?id=6453
void test6453()
{
@@ -107,7 +107,7 @@ void test6453()
}
/***************************************************/
-// 13113
+// https://issues.dlang.org/show_bug.cgi?id=13113
struct S13113
{
@@ -140,7 +140,7 @@ void test13113()
}
/***************************************************/
-// 13147
+// https://issues.dlang.org/show_bug.cgi?id=13147
version (D_InlineAsm_X86)
enum x86iasm = true;
diff --git a/gcc/testsuite/gdc.test/runnable/testkeyword.d b/gcc/testsuite/gdc.test/runnable/testkeyword.d
index 79f2c29b24b..5fb4d442038 100644
--- a/gcc/testsuite/gdc.test/runnable/testkeyword.d
+++ b/gcc/testsuite/gdc.test/runnable/testkeyword.d
@@ -97,8 +97,8 @@ void main(string[] args) nothrow
auto funcLiteral = (int x, int y)
{
- enum thisFunc = "testkeyword.main.__lambda3";
- enum thisFunc2 = "testkeyword.main.__lambda3(int x, int y)";
+ enum thisFunc = "testkeyword.main.__lambda5";
+ enum thisFunc2 = "testkeyword.main.__lambda5(int x, int y)";
static assert(getFuncArgFile() == thisFile);
static assert(getFuncArgLine() == 104);
diff --git a/gcc/testsuite/gdc.test/runnable/testline.d b/gcc/testsuite/gdc.test/runnable/testline.d
index 5b84204b785..cbf536ce62f 100644
--- a/gcc/testsuite/gdc.test/runnable/testline.d
+++ b/gcc/testsuite/gdc.test/runnable/testline.d
@@ -1,4 +1,3 @@
-// RUNNABLE_PHOBOS_TEST
// PERMUTE_ARGS:
// $HeadURL$
@@ -7,7 +6,6 @@
module dstress.run.line_token_03;
-import std.stdio;
import core.exception;
int main(){
@@ -22,8 +20,6 @@ int main(){
assert(0);
}
-import std.stdio;
-
/*
* @WARNING@: this code depends on the phobos implementation.
* char[]s returned by wrong assertions have to look like:
@@ -39,6 +35,5 @@ void checkFileSpec(Object o){
}
}
-writeln(str);
assert(str[start .. start+3]=="(1)");
}
diff --git a/gcc/testsuite/gdc.test/runnable/testmain.d b/gcc/testsuite/gdc.test/runnable/testmain.d
index 4619bde2901..d615e1026f7 100644
--- a/gcc/testsuite/gdc.test/runnable/testmain.d
+++ b/gcc/testsuite/gdc.test/runnable/testmain.d
@@ -1,5 +1,6 @@
// REQUIRED_ARGS: -main
+// PERMUTE_ARGS: -betterC
void foo() { }
diff --git a/gcc/testsuite/gdc.test/runnable/testminit.d b/gcc/testsuite/gdc.test/runnable/testminit.d
index 52c99bbf28f..92c2d3a74a7 100644
--- a/gcc/testsuite/gdc.test/runnable/testminit.d
+++ b/gcc/testsuite/gdc.test/runnable/testminit.d
@@ -1,5 +1,14 @@
-// EXTRA_SOURCES: imports/testminitAA.d imports/testminitBB.d
-// PERMUTE_ARGS:
+/*
+EXTRA_SOURCES: imports/testminitAA.d imports/testminitBB.d
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+AA
+BB
+hello
+Success
+---
+*/
import core.stdc.stdio;
diff --git a/gcc/testsuite/gdc.test/runnable/testmmfile.d b/gcc/testsuite/gdc.test/runnable/testmmfile.d
deleted file mode 100644
index 6a9d6e9094a..00000000000
--- a/gcc/testsuite/gdc.test/runnable/testmmfile.d
+++ /dev/null
@@ -1,120 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-// PERMUTE_ARGS:
-// REQUIRED_ARGS:
-
-import std.file;
-import std.mmfile;
-
-int main()
-{
- static string name = "test.tmp";
- static string s = "abcd";
-
- write(name, s);
-
- { scope MmFile mmf = new MmFile(name);
- string p;
-
- assert(mmf[0] == 'a');
- p = cast(string)mmf[];
- //printf("p.length = %d\n", p.length);
- assert(p[1] == 'b');
- p = cast(string)mmf[0 .. 4];
- assert(p[2] == 'c');
- }
-
- { scope MmFile mmf = new MmFile(name, MmFile.Mode.read, 0, null);
- string p;
-
- assert(mmf[0] == 'a');
- p = cast(string)mmf[];
- //printf("p.length = %d\n", p.length);
- assert(mmf.length == 4);
- assert(p[1] == 'b');
- p = cast(string)mmf[0 .. 4];
- assert(p[2] == 'c');
- }
-
- remove(name);
-
- { scope MmFile mmf = new MmFile(name, MmFile.Mode.readWriteNew, 4, null);
- char[] p;
-
- p = cast(char[])mmf[];
- p[] = "1234";
- mmf[3] = '5';
- assert(mmf[2] == '3');
- assert(mmf[3] == '5');
- }
-
- { string p = cast(string)read(name);
-
- assert(p[] == "1235");
- }
-
- { scope MmFile mmf = new MmFile(name, MmFile.Mode.readWriteNew, 4, null);
- char[] p;
-
- p = cast(char[])mmf[];
- p[] = "5678";
- mmf[3] = '5';
- assert(mmf[2] == '7');
- assert(mmf[3] == '5');
- assert(cast(string)mmf[] == "5675");
- }
-
- { string p = cast(string)read(name);
-
- assert(p[] == "5675");
- }
-
- { scope MmFile mmf = new MmFile(name, MmFile.Mode.readWrite, 4, null);
- char[] p;
-
- p = cast(char[])mmf[];
- assert(cast(char[])mmf[] == "5675");
- p[] = "9102";
- mmf[2] = '5';
- assert(cast(string)mmf[] == "9152");
- }
-
- { string p = cast(string)read(name);
-
- assert(p[] == "9152");
- }
-
- remove(name);
-
- { scope MmFile mmf = new MmFile(name, MmFile.Mode.readWrite, 4, null);
- char[] p;
-
- p = cast(char[])mmf[];
- p[] = "abcd";
- mmf[2] = '5';
- assert(cast(string)mmf[] == "ab5d");
- }
-
- { string p = cast(string)read(name);
-
- assert(p[] == "ab5d");
- }
-
- { scope MmFile mmf = new MmFile(name, MmFile.Mode.readCopyOnWrite, 4, null);
- char[] p;
-
- p = cast(char[])mmf[];
- assert(cast(string)mmf[] == "ab5d");
- p[] = "9102";
- mmf[2] = '5';
- assert(cast(string)mmf[] == "9152");
- }
-
- { string p = cast(string)read(name);
-
- assert(p[] == "ab5d");
- }
-
- remove(name);
-
- return 0;
-}
diff --git a/gcc/testsuite/gdc.test/runnable/testmod2.d b/gcc/testsuite/gdc.test/runnable/testmod2.d
index c03a8c790c5..111696aa623 100644
--- a/gcc/testsuite/gdc.test/runnable/testmod2.d
+++ b/gcc/testsuite/gdc.test/runnable/testmod2.d
@@ -1,7 +1,7 @@
// EXTRA_SOURCES: imports/testmod2a.d
/**********************************/
-// bug 1904
+// https://issues.dlang.org/show_bug.cgi?id=1904
import imports.testmod2a;
void main()
diff --git a/gcc/testsuite/gdc.test/runnable/testpic.d b/gcc/testsuite/gdc.test/runnable/testpic.d
index 60f67756a86..ef3ab8667fc 100644
--- a/gcc/testsuite/gdc.test/runnable/testpic.d
+++ b/gcc/testsuite/gdc.test/runnable/testpic.d
@@ -1,4 +1,5 @@
-// PERMUTE_ARGS: -fPIC -O
+// PERMUTE_ARGS: -fPIC -O -fPIE
+// DISABLED: win32 win64
extern (C) int printf(const char*, ...);
@@ -44,10 +45,26 @@ void test17034()
/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20441
+
+const(char)* moo(const (char) *s)
+{
+ return s;
+}
+
+void test20441()
+{
+ const(char) *x = "abc".ptr;
+ assert( moo(x) - x == 0 );
+}
+
+/***************************************************/
+
int main()
{
test11310();
test17034();
+ test20441();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/testptrref.d b/gcc/testsuite/gdc.test/runnable/testptrref.d
index 5bcf444c739..c616d57888e 100644
--- a/gcc/testsuite/gdc.test/runnable/testptrref.d
+++ b/gcc/testsuite/gdc.test/runnable/testptrref.d
@@ -7,15 +7,11 @@ version(CRuntime_Microsoft)
extern __gshared uint _DP_end;
extern __gshared uint _TP_beg;
extern __gshared uint _TP_end;
- extern int _tls_start;
- extern int _tls_end;
}
alias _DPbegin = _DP_beg;
alias _DPend = _DP_end;
alias _TPbegin = _TP_beg;
alias _TPend = _TP_end;
- alias _tlsstart = _tls_start;
- alias _tlsend = _tls_end;
__gshared void[] dataSection;
shared static this()
@@ -26,6 +22,15 @@ version(CRuntime_Microsoft)
dataSection = findImageSection(".data");
}
+ void[] tlsRange;
+ static this()
+ {
+ import core.internal.traits : externDFunc;
+ alias initTLSRanges = externDFunc!("rt.sections_win64.initTLSRanges",
+ void[] function() nothrow @nogc);
+ tlsRange = initTLSRanges();
+ }
+
version = ptrref_supported;
}
else version(Win32)
@@ -39,6 +44,13 @@ else version(Win32)
extern int _tlsstart;
extern int _tlsend;
}
+
+ void[] tlsRange;
+ static this()
+ {
+ tlsRange = (cast(void*)&_tlsstart)[0.. cast(void*)&_tlsend - cast(void*)&_tlsstart];
+ }
+
version = ptrref_supported;
}
@@ -91,13 +103,13 @@ void main()
version(ptrref_supported):
bool findTlsPtr(const(void)* ptr)
-{
+{
debug(PRINT) printf("findTlsPtr %p\n", ptr);
for (uint* p = &_TPbegin; p < &_TPend; p++)
{
- void* addr = cast(void*) &_tlsstart + *p;
+ void* addr = tlsRange.ptr + *p;
debug(PRINT) printf(" try %p\n", addr);
- assert(addr < &_tlsend);
+ assert(*p < tlsRange.length);
if (addr == ptr)
return true;
}
@@ -126,6 +138,9 @@ void testRefPtr()
debug(PRINT) printf("&_DPbegin %p\n", &_DPbegin);
debug(PRINT) printf("&_DPend %p\n", &_DPend);
+ debug(PRINT) printf("&_TPbegin %p\n", &_TPbegin);
+ debug(PRINT) printf("&_TPend %p\n", &_TPend);
+
assert(!findDataPtr(cast(void*)&sharedInt));
assert(!findTlsPtr(&tlsInt));
diff --git a/gcc/testsuite/gdc.test/runnable/testptrref_gc.d b/gcc/testsuite/gdc.test/runnable/testptrref_gc.d
new file mode 100644
index 00000000000..3bd6bf5412b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/testptrref_gc.d
@@ -0,0 +1,3 @@
+// REQUIRED_ARGS: -lowmem -Jrunnable
+// EXTRA_FILES: testptrref.d
+mixin(import("testptrref.d"));
diff --git a/gcc/testsuite/gdc.test/runnable/testreturn.d b/gcc/testsuite/gdc.test/runnable/testreturn.d
index fcebf79f769..42b93ba1ca8 100644
--- a/gcc/testsuite/gdc.test/runnable/testreturn.d
+++ b/gcc/testsuite/gdc.test/runnable/testreturn.d
@@ -1,9 +1,15 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern(C) int printf(const char*, ...);
alias TypeTuple(T...) = T;
/***************************************************/
-// 13336
+// https://issues.dlang.org/show_bug.cgi?id=13336
struct S13336
{
@@ -96,7 +102,7 @@ out(r)
assert(r == (f ? sx : sy));
result13336 = r;
}
-body
+do
{
mixin(fbody13336);
}
@@ -109,7 +115,7 @@ out(r)
assert(r == (f ? sx : sy));
result13336 = r;
}
-body
+do
{
mixin(fbody13336);
}
@@ -150,7 +156,7 @@ void test13336()
}
/***************************************************/
-// 15018
+// https://issues.dlang.org/show_bug.cgi?id=15018
struct S15018(int n)
{
diff --git a/gcc/testsuite/gdc.test/runnable/testrightthis.d b/gcc/testsuite/gdc.test/runnable/testrightthis.d
index 32879ce3e4c..e5d7ba3aec3 100644
--- a/gcc/testsuite/gdc.test/runnable/testrightthis.d
+++ b/gcc/testsuite/gdc.test/runnable/testrightthis.d
@@ -474,7 +474,7 @@ void test7()
}
/********************************************************/
-// 4350
+// https://issues.dlang.org/show_bug.cgi?id=4350
template Mix4350() { int b; }
@@ -509,7 +509,7 @@ void test4350()
}
/********************************************************/
-// 6430
+// https://issues.dlang.org/show_bug.cgi?id=6430
auto bug6430(int a)
{
@@ -528,7 +528,7 @@ auto bug6430(int a, int b)
}
/********************************************************/
-// 9619
+// https://issues.dlang.org/show_bug.cgi?id=9619
struct Foo9619 { int x; }
void test9619()
@@ -540,7 +540,7 @@ void test9619()
}
/********************************************************/
-// 9633
+// https://issues.dlang.org/show_bug.cgi?id=9633
class Foo9633
{
@@ -573,7 +573,7 @@ void test9633()
}
/********************************************************/
-// 11245
+// https://issues.dlang.org/show_bug.cgi?id=11245
struct Vec11245
{
@@ -589,7 +589,7 @@ class Bar11245
}
/********************************************************/
-// 11614
+// https://issues.dlang.org/show_bug.cgi?id=11614
struct Tuple11614(T...)
{
@@ -613,7 +613,7 @@ struct Foo11614
}
/********************************************************/
-// 11993
+// https://issues.dlang.org/show_bug.cgi?id=11993
struct S11993
{
@@ -634,7 +634,7 @@ void test11993()
}
/********************************************************/
-// 15934
+// https://issues.dlang.org/show_bug.cgi?id=15934
class B15934
{
diff --git a/gcc/testsuite/gdc.test/runnable/testsafe.d b/gcc/testsuite/gdc.test/runnable/testsafe.d
index cec4c0f7fa4..40496f5b24e 100644
--- a/gcc/testsuite/gdc.test/runnable/testsafe.d
+++ b/gcc/testsuite/gdc.test/runnable/testsafe.d
@@ -137,7 +137,7 @@ struct uD
}
@safe
-void safeunions() // improved for issue 11510
+void safeunions() // improved for https://issues.dlang.org/show_bug.cgi?id=11510
{
SafeUnion1 su1;
SafeUnion2 su2;
@@ -197,11 +197,6 @@ void safeexception()
try {}
catch(Throwable e) {}
}));
-
- static assert(!__traits(compiles, () @safe {
- try {}
- catch {}
- }));
}
@safe
@@ -415,7 +410,7 @@ void classcast()
}
/***************************************************/
-// 6278
+// https://issues.dlang.org/show_bug.cgi?id=6278
@safe
{
@@ -423,18 +418,18 @@ void classcast()
class A6278 {
int test()
in { assert(0); }
- body { return 1; }
+ do { return 1; }
}
class B6278 : A6278 {
override int test()
in { assert(0); }
- body { return 1; }
+ do { return 1; }
}
}
/***************************************************/
-// 7803
+// https://issues.dlang.org/show_bug.cgi?id=7803
@safe int f7803() {
scope(success) {/* ... */}
@@ -447,13 +442,13 @@ nothrow int g7803() {
}
/***************************************************/
-// 6405
+// https://issues.dlang.org/show_bug.cgi?id=6405
void foo6405(int[][] args...) @trusted { }
void test6405() @safe { foo6405([1,2,3], [1,2,3]); }
/***************************************************/
-// 12502
+// https://issues.dlang.org/show_bug.cgi?id=12502
void test12502() @safe
{
@@ -495,4 +490,3 @@ void main()
{
test14162();
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/testscope.d b/gcc/testsuite/gdc.test/runnable/testscope.d
index ca5c4c92280..32d66b58c76 100644
--- a/gcc/testsuite/gdc.test/runnable/testscope.d
+++ b/gcc/testsuite/gdc.test/runnable/testscope.d
@@ -1,5 +1,5 @@
// PERMUTE_ARGS:
-// REQUIRED_ARGS: -d -dip1000
+// REQUIRED_ARGS: -d -preview=dip1000
extern(C) int printf(const char*, ...);
@@ -293,8 +293,6 @@ void test11()
static int* p;
static int i;
bar11(p, &i);
-
- bar11((i,p), &i); // comma expressions are deprecated, but need to test them
}
/********************************************/
@@ -302,7 +300,7 @@ void test11()
int test17432(scope int delegate() dg)
{
- return dg();
+ return dg();
}
// stripped down version of std.traits.Parameters
@@ -342,6 +340,53 @@ template test14(T)
test14!(char[] function(return char[])) x14;
/********************************************/
+// https://issues.dlang.org/show_bug.cgi?id=17935
+
+struct ByChunk(IO)
+{
+@safe:
+ ~this() scope
+ {}
+
+ ubyte[] buf;
+ IO io;
+}
+
+struct IO
+{
+ ~this() @safe @nogc scope
+ {}
+}
+
+@safe @nogc void test17395()
+{
+ ubyte[256] buf;
+ auto chunks = ByChunk!IO(buf[], IO());
+ chunks.__xdtor(); // auto-generated inclusive (fields and struct) dtor
+}
+
+/********************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20569
+
+void test20569() @safe
+{
+ static struct S
+ {
+ int value;
+ int* pointer;
+ }
+
+ /* explicit `scope`: */
+ scope S s1;
+ scope int* p1 = &s1.value;
+
+ /* inferred `scope`: */
+ int x;
+ S s2 = S(0, &x);
+ int* p2 = &s2.value;
+}
+
+/********************************************/
void main()
{
@@ -359,6 +404,8 @@ void main()
test7049();
test16747();
test11();
+ test17395();
+ test20569();
printf("Success\n");
}
diff --git a/gcc/testsuite/gdc.test/runnable/testscope2.d b/gcc/testsuite/gdc.test/runnable/testscope2.d
index 85282c2aae8..1b8cf296d3b 100644
--- a/gcc/testsuite/gdc.test/runnable/testscope2.d
+++ b/gcc/testsuite/gdc.test/runnable/testscope2.d
@@ -1,5 +1,4 @@
-// RUNNABLE_PHOBOS_TEST
-// REQUIRED_ARGS: -dip25
+// REQUIRED_ARGS:
/*
TEST_OUTPUT:
---
@@ -30,7 +29,7 @@ pragma(msg, "foo4 ", typeof(&SS.foo4));
void test3()
{
- version (all)
+ version (none)
{
import std.stdio;
writeln(SS.foo1.mangleof);
@@ -52,7 +51,7 @@ void test3()
assert(SS.foo4.mangleof == "_D10testscope22SS4foo4MFNcNkKNgPiZi");
// Test scope pretty-printing
- assert(typeof(SS.foo1).stringof == "ref return ulong(return ref int* delegate() return p)");
+ assert(typeof(SS.foo1).stringof == "ref ulong(return ref int* delegate() return p) return");
assert(typeof(SS.foo2).stringof == "ref int(return ref int delegate() p)");
assert(typeof(SS.foo3).stringof == "ref int(return ref inout(int*) p)");
assert(typeof(SS.foo4).stringof == "ref int(return ref inout(int*) p)");
@@ -255,4 +254,3 @@ void main()
test11();
printf("Success\n");
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/testsignals.d b/gcc/testsuite/gdc.test/runnable/testsignals.d
deleted file mode 100644
index c2fbcee45cb..00000000000
--- a/gcc/testsuite/gdc.test/runnable/testsignals.d
+++ /dev/null
@@ -1,114 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-import std.stdio;
-import std.signals;
-
-class Observer
-{ // our slot
- void watch(string msg, int i)
- {
- writefln("Observed msg '%s' and value %s", msg, i);
- }
-
- void watch2(int i, int j)
- {
- writefln("Observed msg %s,%s", i, j);
- }
-}
-
-class Foo
-{
- int value() { return _value; }
-
- int value(int v)
- {
- if (v != _value)
- { _value = v;
- // call all the connected slots with the two parameters
- emit("setting new value", v);
- }
- return v;
- }
-
- // Mix in all the code we need to make Foo into a signal
- mixin Signal!(string, int);
-
- private :
- int _value;
-}
-
-void test1()
-{
- Foo a = new Foo;
- Observer o = new Observer;
-
- a.value = 3; // should not call o.watch()
- a.connect(&o.watch); // o.watch is the slot
- a.value = 4; // should call o.watch()
- a.disconnect(&o.watch); // o.watch is no longer a slot
- a.value = 5; // so should not call o.watch()
- a.connect(&o.watch); // connect again
- a.value = 6; // should call o.watch()
- delete o; // destroying o should automatically disconnect it
- a.value = 7; // should not call o.watch()
-}
-
-/******************************************/
-
-class Input
-{
- mixin Signal!(int, int) click;
- mixin Signal!(char) keyDown;
-}
-
-void test2()
-{
- Observer o = new Observer();
- Input a = new Input();
- a.click.connect(&o.watch2);
- a.click.emit(5,6);
-}
-
-/******************************************/
-
-class Args3
-{
- int foo;
-}
-
-class Base3
-{
- ~this()
- {
- writefln("Base3 dtor!");
- }
-}
-
-class Test3 : Base3
-{
- mixin Signal!(Args3) A;
- mixin Signal!(Args3) B;
-
- ~this()
- {
- writefln("Test3 dtor");
- }
-}
-
-
-void test3()
-{
- auto test = new Test3;
-}
-
-
-/******************************************/
-
-int main()
-{
- test1();
- test2();
- test3();
-
- printf("Success\n");
- return 0;
-}
diff --git a/gcc/testsuite/gdc.test/runnable/testsocket.d b/gcc/testsuite/gdc.test/runnable/testsocket.d
deleted file mode 100644
index d0619299ece..00000000000
--- a/gcc/testsuite/gdc.test/runnable/testsocket.d
+++ /dev/null
@@ -1,51 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-// PERMUTE_ARGS:
-
-import std.stdio;
-import std.socket;
-
-class Connection
-{
- private
- {
- Socket sock;
- }
-
- void connect (string host, ushort port)
- {
- sock.connect (new InternetAddress (host, port));
- }
-
- void poll ()
- {
- SocketSet rset = new SocketSet (1); /** XXX: here is the bug */
-
- rset.reset ();
- rset.add (sock);
- }
-
- this ()
- {
-
- sock = new TcpSocket;
- sock.blocking = false;
- }
-}
-
-int main ()
-{
- try
- {
- Connection ns;
- ns = new Connection ();
- ns.connect ("localhost", 80);
- ns.poll ();
- delete ns;
- }
- catch(SocketException e)
- {
- }
- return 0;
-}
-
-
diff --git a/gcc/testsuite/gdc.test/runnable/teststdio.d b/gcc/testsuite/gdc.test/runnable/teststdio.d
deleted file mode 100644
index d340f21892f..00000000000
--- a/gcc/testsuite/gdc.test/runnable/teststdio.d
+++ /dev/null
@@ -1,34 +0,0 @@
-// RUNNABLE_PHOBOS_TEST
-// PERMUTE_ARGS:
-// EXTRA_FILES: extra-files/teststdio.txt
-
-import std.stdio;
-import core.stdc.stdio;
-
-void main()
-{
- auto f = std.stdio.File("runnable/extra-files/teststdio.txt", "r");
- FILE* fp = f.getFP();
- string buf;
- int i;
- do
- {
- buf = f.readln('\n');
- foreach (c; buf)
- printf("%x\n", c);
- printf("\n");
- switch (i)
- {
- case 0: assert(buf == "asdfasdf\n"); break;
- case 1: assert(buf == "a\n"); break;
- case 2: assert(buf == "sdf\n"); break;
- case 3: assert(buf == "asdf\n"); break;
- case 4: assert(buf == "\n"); break;
- case 5: assert(buf == "\n"); break;
- case 6: assert(buf == null); break;
- default: assert(0);
- }
- i++;
- } while (!feof(fp));
- //fclose(fp);
-}
diff --git a/gcc/testsuite/gdc.test/runnable/testswitch.d b/gcc/testsuite/gdc.test/runnable/testswitch.d
index 02633a84525..c7b9378f842 100644
--- a/gcc/testsuite/gdc.test/runnable/testswitch.d
+++ b/gcc/testsuite/gdc.test/runnable/testswitch.d
@@ -308,7 +308,7 @@ void bar14(A...)(int i)
{
goto case;
case A[j]:
- printf("a = %d, A[%d] = %d\n", a, j, A[j]);
+ printf("a = %d, A[%zd] = %d\n", a, j, A[j]);
}
break;
default:
@@ -356,10 +356,14 @@ int foo15(int i)
static this()
{
X15 = 4;
- Y15 = 4;
Z15 = 5;
}
+shared static this()
+{
+ Y15 = 4;
+}
+
void test15()
{
auto i = foo15(3);
@@ -465,7 +469,7 @@ int wrongcode3139(int x)
static assert(wrongcode3139(-5)==3);
-// bug 3139
+// https://issues.dlang.org/show_bug.cgi?id=3139
static assert(!is(typeof(
(long x) { switch(x) { case long.max: .. case -long.max:
default:} return 4; }(3)
@@ -499,7 +503,7 @@ void test7358()
}
/*****************************************/
-// 9263
+// https://issues.dlang.org/show_bug.cgi?id=9263
void test9263()
{
@@ -622,7 +626,7 @@ void test23()
}
/*****************************************/
-// 14352
+// https://issues.dlang.org/show_bug.cgi?id=14352
int transmogrify14352(int input)
{
@@ -659,7 +663,8 @@ void test14352()
}
/*****************************************/
-// Issue 14587 - DMD 2.067.1 generating crashing binary on OSX
+// https://issues.dlang.org/show_bug.cgi?id=14587
+// DMD 2.067.1 generating crashing binary on OSX
struct Card {
int value;
@@ -680,7 +685,8 @@ void test14587() {
}
/*****************************************/
-// Issue 15396 - static immutable not recognized as constant within switch statement
+// https://issues.dlang.org/show_bug.cgi?id=15396
+// static immutable not recognized as constant within switch statement
void test15396()
{
@@ -702,6 +708,33 @@ void test15396()
}
/*****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15538
+
+struct S15538
+{
+ int a = 0;
+ int b = 1;
+}
+
+int f15538(S15538 s)
+{
+ switch (s.a)
+ {
+ case 0: return 10;
+ case 1: return 20;
+ case 2: return 30;
+ case 3: return 40;
+ default: return 99;
+ }
+}
+
+void test15538()
+{
+ S15538 s;
+ assert(f15538(s) == 10); /* fails */
+}
+
+/*****************************************/
int main()
{
@@ -732,6 +765,7 @@ int main()
test14352();
test14587();
test15396();
+ test15538();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/testthread.d b/gcc/testsuite/gdc.test/runnable/testthread.d
index 4ae43950863..139a891072c 100644
--- a/gcc/testsuite/gdc.test/runnable/testthread.d
+++ b/gcc/testsuite/gdc.test/runnable/testthread.d
@@ -49,7 +49,7 @@ int main()
assert(f.x == 3);
int i;
- Thread tx[5];
+ Thread[5] tx;
for (i = 0; i < tx.length; i++)
tx[i] = new Thread(&f.bar);
for (i = 0; i < tx.length; i++)
diff --git a/gcc/testsuite/gdc.test/runnable/testthread2.d b/gcc/testsuite/gdc.test/runnable/testthread2.d
index 93aace6c650..53f8a3b50f3 100644
--- a/gcc/testsuite/gdc.test/runnable/testthread2.d
+++ b/gcc/testsuite/gdc.test/runnable/testthread2.d
@@ -1,11 +1,5 @@
-// RUNNABLE_PHOBOS_TEST
// PERMUTE_ARGS:
-import std.algorithm : map;
-import std.random : Random, uniform, unpredictableSeed;
-import std.range : repeat;
-import std.stdio : writeln;
-
// Quick, dirty and inefficient AA using linear search, useful for testing.
struct LinearAA(K, V) {
K[] keys;
@@ -33,7 +27,7 @@ struct LinearAA(K, V) {
return val;
}
- V* opIn_r(K key) {
+ V* opBinaryRight(string op : "in")(K key) {
foreach(i, k; keys) {
if(key == k) {
return values.ptr + i;
@@ -67,24 +61,35 @@ struct LinearAA(K, V) {
}
}
-void main() {
- Random gen;
- uint[] seed;
- gen.seed(map!((a) {
- seed ~= unpredictableSeed;
- return seed[$-1]; })(repeat(0)));
- writeln(seed);
+extern (C) int rand();
+
+uint random(const uint max = uint.max)
+{
+ version (Windows)
+ {
+ // RAND_MAX is quite low for windows, extend the range by
+ // abusing multiple random values and rounding errors.
+ const a = rand(), b = rand();
+ const c = a < b ? double(a) / b : double(b) / a;
+ return (cast(int) (c * max)) % max;
+ }
+ else
+ return rand() % max;
+}
+
+void main()
+{
foreach(iter; 0..10) { // Bug only happens after a few iterations.
- writeln(iter);
+
uint[size_t] builtin;
LinearAA!(size_t, uint) linAA;
uint[] nums = new uint[100_000];
foreach(ref num; nums) {
- num = uniform(0U, uint.max, gen);
+ num = random();
}
foreach(i; 0..10_000) {
- auto index = uniform(0, nums.length, gen);
+ auto index = random(cast(uint) nums.length);
if(index in builtin) {
assert(index in linAA);
assert(builtin[index] == nums[index]);
diff --git a/gcc/testsuite/gdc.test/runnable/testtypeid.d b/gcc/testsuite/gdc.test/runnable/testtypeid.d
index ebdda30c197..d3f4503a187 100644
--- a/gcc/testsuite/gdc.test/runnable/testtypeid.d
+++ b/gcc/testsuite/gdc.test/runnable/testtypeid.d
@@ -1,6 +1,6 @@
-// RUNNABLE_PHOBOS_TEST
+
import core.vararg;
-import std.stdio;
+import core.stdc.stdio;
/******************************************************/
@@ -18,10 +18,10 @@ FOO3 foox3;
void foo3(int x, ...)
{
- printf("%d arguments\n", _arguments.length);
+ printf("%zd arguments\n", _arguments.length);
for (int i = 0; i < _arguments.length; i++)
{
- writeln(_arguments[i].toString());
+ //writeln(_arguments[i].toString());
if (_arguments[i] is typeid(int))
{
@@ -76,20 +76,6 @@ void test4()
ti = typeid(real[]);
assert(!(ti is null));
- ti = typeid(ifloat[]);
- assert(!(ti is null));
- ti = typeid(idouble[]);
- assert(!(ti is null));
- ti = typeid(ireal[]);
- assert(!(ti is null));
-
- ti = typeid(cfloat[]);
- assert(!(ti is null));
- ti = typeid(cdouble[]);
- assert(!(ti is null));
- ti = typeid(creal[]);
- assert(!(ti is null));
-
ti = typeid(void);
assert(!(ti is null));
ti = typeid(void[]);
@@ -210,26 +196,6 @@ void test16()
/******************************************************/
-void test17()
-{
- TypeInfo ti = typeid(ifloat*);
- assert(!(ti is null));
- assert(ti.tsize==(ifloat*).sizeof);
- assert(ti.toString()=="ifloat*");
-}
-
-/******************************************************/
-
-void test18()
-{
- TypeInfo ti = typeid(cfloat*);
- assert(!(ti is null));
- assert(ti.tsize==(cfloat*).sizeof);
- assert(ti.toString()=="cfloat*");
-}
-
-/******************************************************/
-
void test19()
{
TypeInfo ti = typeid(double*);
@@ -240,26 +206,6 @@ void test19()
/******************************************************/
-void test20()
-{
- TypeInfo ti = typeid(idouble*);
- assert(!(ti is null));
- assert(ti.tsize==(idouble*).sizeof);
- assert(ti.toString()=="idouble*");
-}
-
-/******************************************************/
-
-void test21()
-{
- TypeInfo ti = typeid(cdouble*);
- assert(!(ti is null));
- assert(ti.tsize==(cdouble*).sizeof);
- assert(ti.toString()=="cdouble*");
-}
-
-/******************************************************/
-
void test22()
{
TypeInfo ti = typeid(real*);
@@ -270,26 +216,6 @@ void test22()
/******************************************************/
-void test23()
-{
- TypeInfo ti = typeid(ireal*);
- assert(!(ti is null));
- assert(ti.tsize==(ireal*).sizeof);
- assert(ti.toString()=="ireal*");
-}
-
-/******************************************************/
-
-void test24()
-{
- TypeInfo ti = typeid(creal*);
- assert(!(ti is null));
- assert(ti.tsize==(creal*).sizeof);
- assert(ti.toString()=="creal*");
-}
-
-/******************************************************/
-
void test25()
{
TypeInfo ti = typeid(char*);
@@ -367,10 +293,10 @@ class Bar32 { long y = 4; }
void printargs(int x, ...)
{
- printf("%d arguments\n", _arguments.length);
+ printf("%zd arguments\n", _arguments.length);
for (int i = 0; i < _arguments.length; i++)
{
- writeln(_arguments[i].toString());
+ //writeln(_arguments[i].toString());
if (_arguments[i] == typeid(int))
{
@@ -506,7 +432,7 @@ void test40()
}
/******************************************************/
-// 9442
+// https://issues.dlang.org/show_bug.cgi?id=9442
class C
{
@@ -526,7 +452,7 @@ void test9442()
}
/******************************************************/
-// 10451
+// https://issues.dlang.org/show_bug.cgi?id=10451
struct Foo10451;
@@ -543,7 +469,7 @@ void test10451()
}
/******************************************************/
-// 11010
+// https://issues.dlang.org/show_bug.cgi?id=11010
struct S11010 { S11010* p; }
@@ -567,7 +493,7 @@ void test11010()
}
/******************************************************/
-// 13045
+// https://issues.dlang.org/show_bug.cgi?id=13045
void test13045a()
{
@@ -623,7 +549,7 @@ void test13045b()
}
/******************************************************/
-// 15680
+// https://issues.dlang.org/show_bug.cgi?id=15680
void test15680()
{
@@ -651,14 +577,8 @@ int main()
test14();
test15();
test16();
- test17();
- test18();
test19();
- test20();
- test21();
test22();
- test23();
- test24();
test25();
test26();
test27();
diff --git a/gcc/testsuite/gdc.test/runnable/traits.d b/gcc/testsuite/gdc.test/runnable/traits.d
index 9d14ecdcc09..ddd505919d2 100644
--- a/gcc/testsuite/gdc.test/runnable/traits.d
+++ b/gcc/testsuite/gdc.test/runnable/traits.d
@@ -1,4 +1,3 @@
-// RUNNABLE_PHOBOS_TEST
/*
PERMUTE_ARGS:
EXTRA_FILES: imports/a9546.d
@@ -17,7 +16,7 @@ __lambda1
module traits;
-import std.stdio;
+import core.stdc.stdio;
alias int myint;
struct S { void bar() { } int x = 4; static int z = 5; }
@@ -34,7 +33,6 @@ struct D1 { @disable void true_(); void false_(){} }
void test1()
{
auto t = __traits(isArithmetic, int);
- writeln(t);
assert(t == true);
assert(__traits(isArithmetic) == false);
@@ -61,12 +59,6 @@ void test1()
assert(__traits(isArithmetic, float) == true);
assert(__traits(isArithmetic, double) == true);
assert(__traits(isArithmetic, real) == true);
- assert(__traits(isArithmetic, ifloat) == true);
- assert(__traits(isArithmetic, idouble) == true);
- assert(__traits(isArithmetic, ireal) == true);
- assert(__traits(isArithmetic, cfloat) == true);
- assert(__traits(isArithmetic, cdouble) == true);
- assert(__traits(isArithmetic, creal) == true);
assert(__traits(isArithmetic, char) == true);
assert(__traits(isArithmetic, wchar) == true);
assert(__traits(isArithmetic, dchar) == true);
@@ -81,7 +73,6 @@ void test1()
void test2()
{
auto t = __traits(isScalar, int);
- writeln(t);
assert(t == true);
assert(__traits(isScalar) == false);
@@ -108,12 +99,6 @@ void test2()
assert(__traits(isScalar, float) == true);
assert(__traits(isScalar, double) == true);
assert(__traits(isScalar, real) == true);
- assert(__traits(isScalar, ifloat) == true);
- assert(__traits(isScalar, idouble) == true);
- assert(__traits(isScalar, ireal) == true);
- assert(__traits(isScalar, cfloat) == true);
- assert(__traits(isScalar, cdouble) == true);
- assert(__traits(isScalar, creal) == true);
assert(__traits(isScalar, char) == true);
assert(__traits(isScalar, wchar) == true);
assert(__traits(isScalar, dchar) == true);
@@ -147,12 +132,6 @@ void test3()
assert(__traits(isIntegral, float) == false);
assert(__traits(isIntegral, double) == false);
assert(__traits(isIntegral, real) == false);
- assert(__traits(isIntegral, ifloat) == false);
- assert(__traits(isIntegral, idouble) == false);
- assert(__traits(isIntegral, ireal) == false);
- assert(__traits(isIntegral, cfloat) == false);
- assert(__traits(isIntegral, cdouble) == false);
- assert(__traits(isIntegral, creal) == false);
assert(__traits(isIntegral, char) == true);
assert(__traits(isIntegral, wchar) == true);
assert(__traits(isIntegral, dchar) == true);
@@ -185,12 +164,6 @@ void test4()
assert(__traits(isFloating, float) == true);
assert(__traits(isFloating, double) == true);
assert(__traits(isFloating, real) == true);
- assert(__traits(isFloating, ifloat) == true);
- assert(__traits(isFloating, idouble) == true);
- assert(__traits(isFloating, ireal) == true);
- assert(__traits(isFloating, cfloat) == true);
- assert(__traits(isFloating, cdouble) == true);
- assert(__traits(isFloating, creal) == true);
assert(__traits(isFloating, char) == false);
assert(__traits(isFloating, wchar) == false);
assert(__traits(isFloating, dchar) == false);
@@ -223,12 +196,6 @@ void test5()
assert(__traits(isUnsigned, float) == false);
assert(__traits(isUnsigned, double) == false);
assert(__traits(isUnsigned, real) == false);
- assert(__traits(isUnsigned, ifloat) == false);
- assert(__traits(isUnsigned, idouble) == false);
- assert(__traits(isUnsigned, ireal) == false);
- assert(__traits(isUnsigned, cfloat) == false);
- assert(__traits(isUnsigned, cdouble) == false);
- assert(__traits(isUnsigned, creal) == false);
assert(__traits(isUnsigned, char) == true);
assert(__traits(isUnsigned, wchar) == true);
assert(__traits(isUnsigned, dchar) == true);
@@ -261,12 +228,6 @@ void test6()
assert(__traits(isAssociativeArray, float) == false);
assert(__traits(isAssociativeArray, double) == false);
assert(__traits(isAssociativeArray, real) == false);
- assert(__traits(isAssociativeArray, ifloat) == false);
- assert(__traits(isAssociativeArray, idouble) == false);
- assert(__traits(isAssociativeArray, ireal) == false);
- assert(__traits(isAssociativeArray, cfloat) == false);
- assert(__traits(isAssociativeArray, cdouble) == false);
- assert(__traits(isAssociativeArray, creal) == false);
assert(__traits(isAssociativeArray, char) == false);
assert(__traits(isAssociativeArray, wchar) == false);
assert(__traits(isAssociativeArray, dchar) == false);
@@ -299,12 +260,6 @@ void test7()
assert(__traits(isStaticArray, float) == false);
assert(__traits(isStaticArray, double) == false);
assert(__traits(isStaticArray, real) == false);
- assert(__traits(isStaticArray, ifloat) == false);
- assert(__traits(isStaticArray, idouble) == false);
- assert(__traits(isStaticArray, ireal) == false);
- assert(__traits(isStaticArray, cfloat) == false);
- assert(__traits(isStaticArray, cdouble) == false);
- assert(__traits(isStaticArray, creal) == false);
assert(__traits(isStaticArray, char) == false);
assert(__traits(isStaticArray, wchar) == false);
assert(__traits(isStaticArray, dchar) == false);
@@ -338,12 +293,6 @@ void test8()
assert(__traits(isAbstractClass, float) == false);
assert(__traits(isAbstractClass, double) == false);
assert(__traits(isAbstractClass, real) == false);
- assert(__traits(isAbstractClass, ifloat) == false);
- assert(__traits(isAbstractClass, idouble) == false);
- assert(__traits(isAbstractClass, ireal) == false);
- assert(__traits(isAbstractClass, cfloat) == false);
- assert(__traits(isAbstractClass, cdouble) == false);
- assert(__traits(isAbstractClass, creal) == false);
assert(__traits(isAbstractClass, char) == false);
assert(__traits(isAbstractClass, wchar) == false);
assert(__traits(isAbstractClass, dchar) == false);
@@ -400,18 +349,16 @@ void test13()
auto j = __traits(getMember, S, "z");
assert(j == 5);
- writeln(__traits(hasMember, s, "x"));
assert(__traits(hasMember, s, "x") == true);
assert(__traits(hasMember, S, "z") == true);
assert(__traits(hasMember, S, "aaa") == false);
auto k = __traits(classInstanceSize, C);
- writeln(k);
assert(k == C.classinfo.initializer.length);
}
/********************************************************/
-// 7123
+// https://issues.dlang.org/show_bug.cgi?id=7123
private struct DelegateFaker7123(F)
{
@@ -443,7 +390,6 @@ class D14
void test14()
{
auto a = [__traits(derivedMembers, D14)];
- writeln(a);
assert(a == ["__ctor","__dtor","foo", "__xdtor"]);
}
@@ -461,12 +407,15 @@ void test15()
{
D15 d = new D15();
- foreach (t; __traits(getVirtualFunctions, D15, "foo"))
- writeln(typeid(typeof(t)));
+ assert(__traits(getVirtualFunctions, D15, "foo").length == 2);
+ assert(typeid(typeof(__traits(getVirtualFunctions, D15, "foo")[0])).toString()
+ == "void function()");
+ assert(typeid(typeof(__traits(getVirtualFunctions, D15, "foo")[1])).toString()
+ == "int function(int)");
alias typeof(__traits(getVirtualFunctions, D15, "foo")) b;
- foreach (t; b)
- writeln(typeid(t));
+ assert(typeid(b[0]).toString() == "void function()");
+ assert(typeid(b[1]).toString() == "int function(int)");
auto i = __traits(getVirtualFunctions, d, "foo")[1](1);
assert(i == 2);
@@ -485,8 +434,8 @@ void test16()
assert(__traits(isSame, foo16, bar16) == false);
assert(__traits(isSame, foo16, S16) == false);
assert(__traits(isSame, S16, S16) == true);
- assert(__traits(isSame, std, S16) == false);
- assert(__traits(isSame, std, std) == true);
+ assert(__traits(isSame, core, S16) == false);
+ assert(__traits(isSame, core, core) == true);
}
/********************************************************/
@@ -508,7 +457,7 @@ void test17()
assert(__traits(compiles, typeof(1)) == true);
assert(__traits(compiles, S17.s1) == true);
assert(__traits(compiles, S17.s3) == false);
- assert(__traits(compiles, 1,2,3,int,long,std) == true);
+ assert(__traits(compiles, 1,2,3,int,long,core) == true);
assert(__traits(compiles, 3[1]) == false);
assert(__traits(compiles, 1,2,3,int,long,3[1]) == false);
}
@@ -525,7 +474,6 @@ interface D18
void test18()
{
auto a = __traits(allMembers, D18);
- writeln(a);
assert(a.length == 1);
}
@@ -546,11 +494,10 @@ class C19
void test19()
{
auto a = __traits(allMembers, C19);
- writeln(a);
assert(a.length == 9);
foreach( m; __traits(allMembers, C19) )
- writeln(m);
+ printf("%.*s\n", cast(int)m.length, m.ptr);
}
@@ -603,12 +550,14 @@ void test22()
{
D22 d = new D22();
- foreach (t; __traits(getOverloads, D22, "foo"))
- writeln(typeid(typeof(t)));
+ assert(typeid(typeof(__traits(getOverloads, D22, "foo")[0])).toString()
+ == "void function()");
+ assert(typeid(typeof(__traits(getOverloads, D22, "foo")[1])).toString()
+ == "int function(int)");
alias typeof(__traits(getOverloads, D22, "foo")) b;
- foreach (t; b)
- writeln(typeid(t));
+ assert(typeid(b[0]).toString() == "void function()");
+ assert(typeid(b[1]).toString() == "int function(int)");
auto i = __traits(getOverloads, d, "foo")[1](1);
assert(i == 2);
@@ -642,7 +591,7 @@ struct Test24
static assert(__traits(getVisibility, __traits(getOverloads, Test24, "test24")[1]) == "private");
/********************************************************/
-// 1369
+// https://issues.dlang.org/show_bug.cgi?id=1369
void test1369()
{
@@ -698,7 +647,7 @@ static assert([__traits(allMembers, S2234b)] == ["x"]);
static assert([__traits(allMembers, S2234c)] == ["foo"]);
/********************************************************/
-// 5878
+// https://issues.dlang.org/show_bug.cgi?id=5878
template J5878(A)
{
@@ -735,7 +684,7 @@ static assert([__traits(allMembers,Test6674)] == [
"toString","toHash","opCmp","opEquals","Monitor","factory"]);
/********************************************************/
-// 6073
+// https://issues.dlang.org/show_bug.cgi?id=6073
struct S6073 {}
@@ -747,13 +696,13 @@ alias T6073!(__traits(parent, S6073)) U6073; // error
static assert(__traits(isSame, V6073, U6073)); // same instantiation == same arguemnts
/********************************************************/
-// 7027
+// https://issues.dlang.org/show_bug.cgi?id=7027
struct Foo7027 { int a; }
static assert(!__traits(compiles, { return Foo7027.a; }));
/********************************************************/
-// 9213
+// https://issues.dlang.org/show_bug.cgi?id=9213
class Foo9213 { int a; }
static assert(!__traits(compiles, { return Foo9213.a; }));
@@ -795,7 +744,7 @@ static assert(__traits(isVirtualMethod, FF.YYY));
static assert(__traits(getVirtualMethods, FF, "YYY").length == 1);
/********************************************************/
-// 7608
+// https://issues.dlang.org/show_bug.cgi?id=7608
struct S7608a(bool T)
{
@@ -820,7 +769,7 @@ void test7608()
}
/********************************************************/
-// 7858
+// https://issues.dlang.org/show_bug.cgi?id=7858
void test7858()
{
@@ -857,7 +806,7 @@ void test7858()
}
/********************************************************/
-// 8971
+// https://issues.dlang.org/show_bug.cgi?id=8971
template Tuple8971(TL...){ alias TL Tuple8971; }
@@ -873,7 +822,7 @@ class A8971
}
/********************************************************/
-// 8972
+// https://issues.dlang.org/show_bug.cgi?id=8972
struct A8972
{
@@ -956,7 +905,7 @@ void getVisibility()
}
/********************************************************/
-// 9546
+// https://issues.dlang.org/show_bug.cgi?id=9546
void test9546()
{
@@ -1007,7 +956,7 @@ void test9546()
}
/********************************************************/
-// 9091
+// https://issues.dlang.org/show_bug.cgi?id=9091
template isVariable9091(X...) if (X.length == 1)
{
@@ -1153,7 +1102,7 @@ void test9237()
}
/*************************************************************/
-// 5978
+// https://issues.dlang.org/show_bug.cgi?id=5978
void test5978()
{
@@ -1178,7 +1127,7 @@ void test7408()
}
/*************************************************************/
-// 9552
+// https://issues.dlang.org/show_bug.cgi?id=9552
class C9552
{
@@ -1225,7 +1174,7 @@ void test9136()
}
/********************************************************/
-// 9939
+// https://issues.dlang.org/show_bug.cgi?id=9939
struct Test9939
{
@@ -1245,7 +1194,7 @@ struct Test9939
static assert([__traits(allMembers, Test9939)] == ["f", "A", "B", "NamedEnum"]);
/********************************************************/
-// 10043
+// https://issues.dlang.org/show_bug.cgi?id=10043
void test10043()
{
@@ -1255,7 +1204,7 @@ void test10043()
}
/********************************************************/
-// 10096
+// https://issues.dlang.org/show_bug.cgi?id=10096
struct S10096X
{
@@ -1270,7 +1219,7 @@ struct S10096X
this(this) {}
~this() {}
- string getStr() in { assert(str); } out(r) { assert(r == str); } body { return str; }
+ string getStr() in(str) out(r; r == str) { return str; }
}
static assert(
[__traits(allMembers, S10096X)] ==
@@ -1288,7 +1237,7 @@ class C10096X
this(int) {}
~this() {}
- string getStr() in { assert(str); } out(r) { assert(r == str); } body { return str; }
+ string getStr() in(str) out(r; r == str) { return str; }
}
static assert(
[__traits(allMembers, C10096X)] ==
@@ -1341,117 +1290,6 @@ void test_getUnitTests ()
/********************************************************/
-void test_getFunctionAttributes()
-{
- alias tuple(T...) = T;
-
- struct S
- {
- int noF() { return 0; }
- int constF() const { return 0; }
- int immutableF() immutable { return 0; }
- int inoutF() inout { return 0; }
- int sharedF() shared { return 0; }
-
- int x;
- ref int refF() { return x; }
- int propertyF() @property { return 0; }
- int nothrowF() nothrow { return 0; }
- int nogcF() @nogc { return 0; }
-
- int systemF() @system { return 0; }
- int trustedF() @trusted { return 0; }
- int safeF() @safe { return 0; }
-
- int pureF() pure { return 0; }
- }
-
- static assert(__traits(getFunctionAttributes, S.noF) == tuple!("@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.noF)) == tuple!("@system"));
-
- static assert(__traits(getFunctionAttributes, S.constF) == tuple!("const", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.constF)) == tuple!("const", "@system"));
-
- static assert(__traits(getFunctionAttributes, S.immutableF) == tuple!("immutable", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.immutableF)) == tuple!("immutable", "@system"));
-
- static assert(__traits(getFunctionAttributes, S.inoutF) == tuple!("inout", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.inoutF)) == tuple!("inout", "@system"));
-
- static assert(__traits(getFunctionAttributes, S.sharedF) == tuple!("shared", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.sharedF)) == tuple!("shared", "@system"));
-
- static assert(__traits(getFunctionAttributes, S.refF) == tuple!("ref", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.refF)) == tuple!("ref", "@system"));
-
- static assert(__traits(getFunctionAttributes, S.propertyF) == tuple!("@property", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(&S.propertyF)) == tuple!("@property", "@system"));
-
- static assert(__traits(getFunctionAttributes, S.nothrowF) == tuple!("nothrow", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.nothrowF)) == tuple!("nothrow", "@system"));
-
- static assert(__traits(getFunctionAttributes, S.nogcF) == tuple!("@nogc", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.nogcF)) == tuple!("@nogc", "@system"));
-
- static assert(__traits(getFunctionAttributes, S.systemF) == tuple!("@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.systemF)) == tuple!("@system"));
-
- static assert(__traits(getFunctionAttributes, S.trustedF) == tuple!("@trusted"));
- static assert(__traits(getFunctionAttributes, typeof(S.trustedF)) == tuple!("@trusted"));
-
- static assert(__traits(getFunctionAttributes, S.safeF) == tuple!("@safe"));
- static assert(__traits(getFunctionAttributes, typeof(S.safeF)) == tuple!("@safe"));
-
- static assert(__traits(getFunctionAttributes, S.pureF) == tuple!("pure", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S.pureF)) == tuple!("pure", "@system"));
-
- int pure_nothrow() nothrow pure { return 0; }
- static ref int static_ref_property() @property { return *(new int); }
- ref int ref_property() @property { return *(new int); }
- void safe_nothrow() @safe nothrow { }
-
- static assert(__traits(getFunctionAttributes, pure_nothrow) == tuple!("pure", "nothrow", "@nogc", "@safe"));
- static assert(__traits(getFunctionAttributes, typeof(pure_nothrow)) == tuple!("pure", "nothrow", "@nogc", "@safe"));
-
- static assert(__traits(getFunctionAttributes, static_ref_property) == tuple!("pure", "nothrow", "@property", "ref", "@safe"));
- static assert(__traits(getFunctionAttributes, typeof(&static_ref_property)) == tuple!("pure", "nothrow", "@property", "ref", "@safe"));
-
- static assert(__traits(getFunctionAttributes, ref_property) == tuple!("pure", "nothrow", "@property", "ref", "@safe"));
- static assert(__traits(getFunctionAttributes, typeof(&ref_property)) == tuple!("pure", "nothrow", "@property", "ref", "@safe"));
-
- static assert(__traits(getFunctionAttributes, safe_nothrow) == tuple!("pure", "nothrow", "@nogc", "@safe"));
- static assert(__traits(getFunctionAttributes, typeof(safe_nothrow)) == tuple!("pure", "nothrow", "@nogc", "@safe"));
-
- struct S2
- {
- int pure_const() const pure { return 0; }
- int pure_sharedconst() const shared pure { return 0; }
- }
-
- static assert(__traits(getFunctionAttributes, S2.pure_const) == tuple!("const", "pure", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S2.pure_const)) == tuple!("const", "pure", "@system"));
-
- static assert(__traits(getFunctionAttributes, S2.pure_sharedconst) == tuple!("const", "shared", "pure", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(S2.pure_sharedconst)) == tuple!("const", "shared", "pure", "@system"));
-
- static assert(__traits(getFunctionAttributes, (int a) { }) == tuple!("pure", "nothrow", "@nogc", "@safe"));
- static assert(__traits(getFunctionAttributes, typeof((int a) { })) == tuple!("pure", "nothrow", "@nogc", "@safe"));
-
- auto safeDel = delegate() @safe { };
- static assert(__traits(getFunctionAttributes, safeDel) == tuple!("pure", "nothrow", "@nogc", "@safe"));
- static assert(__traits(getFunctionAttributes, typeof(safeDel)) == tuple!("pure", "nothrow", "@nogc", "@safe"));
-
- auto trustedDel = delegate() @trusted { };
- static assert(__traits(getFunctionAttributes, trustedDel) == tuple!("pure", "nothrow", "@nogc", "@trusted"));
- static assert(__traits(getFunctionAttributes, typeof(trustedDel)) == tuple!("pure", "nothrow", "@nogc", "@trusted"));
-
- auto systemDel = delegate() @system { };
- static assert(__traits(getFunctionAttributes, systemDel) == tuple!("pure", "nothrow", "@nogc", "@system"));
- static assert(__traits(getFunctionAttributes, typeof(systemDel)) == tuple!("pure", "nothrow", "@nogc", "@system"));
-}
-
-/********************************************************/
-
class TestIsOverrideFunctionBase
{
void bar () {}
@@ -1469,7 +1307,8 @@ void test_isOverrideFunction ()
}
/********************************************************/
-// 11711 - Add __traits(getAliasThis)
+// https://issues.dlang.org/show_bug.cgi?id=11711
+// Add __traits(getAliasThis)
alias TypeTuple(T...) = T;
@@ -1492,11 +1331,15 @@ void test11711()
static assert(__traits(getAliasThis, S2) == TypeTuple!("var"));
static assert(is(typeof(__traits(getMember, S2.init, __traits(getAliasThis, S2)[0]))
== TypeTuple!(int, string)));
+
+ // https://issues.dlang.org/show_bug.cgi?id=19439
+ // Return empty tuple for non-aggregate types.
+ static assert(__traits(getAliasThis, int).length == 0);
}
/********************************************************/
-// Issue 12278
+// https://issues.dlang.org/show_bug.cgi?id=12278
class Foo12278
{
@@ -1511,7 +1354,7 @@ struct InPlace12278(T)
}
/********************************************************/
-// 12571
+// https://issues.dlang.org/show_bug.cgi?id=12571
mixin template getScopeName12571()
{
@@ -1525,7 +1368,7 @@ void test12571()
}
/********************************************************/
-// 12237
+// https://issues.dlang.org/show_bug.cgi?id=12237
auto f12237(T)(T a)
{
@@ -1564,7 +1407,7 @@ void async(ARGS...)(ARGS)
alias test17495 = async!(int, int);
/********************************************************/
-// 15094
+// https://issues.dlang.org/show_bug.cgi?id=15094
void test15094()
{
@@ -1638,11 +1481,10 @@ int main()
test9136();
test10096();
test_getUnitTests();
- test_getFunctionAttributes();
test_isOverrideFunction();
test12237();
test15094();
- writeln("Success");
+ printf("Success\n");
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d b/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d
index 512bcea79af..3c5a4bd9786 100644
--- a/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d
+++ b/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d
@@ -1,8 +1,7 @@
module traits_getPointerBitmap;
-import std.stdio;
-static import std.traits;
+import core.stdc.stdio;
// version = RTInfo;
// debug = LOG;
@@ -190,7 +189,7 @@ class N
union U
{
- size_t data[4];
+ size_t[4] data;
Large*[] arr; // { length, ptr }
struct
@@ -210,8 +209,6 @@ void testRTInfo()
testType!(int) ([ 0b0 ]);
testType!(long) ([ 0b00 ]);
testType!(double) ([ 0b00 ]);
- testType!(ifloat) ([ 0b0 ]);
- testType!(cdouble) ([ 0b0000 ]);
testType!(dg) ([ 0b01 ]);
testType!(fn) ([ 0b0 ]);
testType!(S!fn) ([ 0b100 ]);
diff --git a/gcc/testsuite/gdc.test/runnable/traits_getUnitTests.d b/gcc/testsuite/gdc.test/runnable/traits_getUnitTests.d
index 38295ae446a..2786917ba08 100644
--- a/gcc/testsuite/gdc.test/runnable/traits_getUnitTests.d
+++ b/gcc/testsuite/gdc.test/runnable/traits_getUnitTests.d
@@ -62,7 +62,7 @@ void test_getUnitTestsFromImport ()
static assert(__traits(getUnitTests, mixin("imports.traits_getUnitTests_import")).length == 1);
}
-// 11358
+// https://issues.dlang.org/show_bug.cgi?id=11358
debug { }
enum len11358 = __traits(getUnitTests, mixin(__MODULE__)).length;
diff --git a/gcc/testsuite/gdc.test/runnable/tuple_default_parameters.d b/gcc/testsuite/gdc.test/runnable/tuple_default_parameters.d
new file mode 100644
index 00000000000..a725d31a4d1
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/tuple_default_parameters.d
@@ -0,0 +1,64 @@
+void main()
+{
+ assert(func(1,0) == 1);
+ assert(func(1) == 1);
+ assert(func2() == 0);
+
+ assert(func3() == 15);
+ assert(func3(2) == 10);
+ assert(func3(2, 1) == 2);
+
+ assert(func4() == 15);
+
+ assert(func5() == 86);
+ assert(func5(1) == 84);
+ assert(func5(1, 2) == 81);
+ assert(func5(1, 2, 3) == 80);
+ assert(func5(1, 2, 3, 4) == 74);
+ assert(func5(1, 2, 3, 4, 0) == 69);
+ assert(func5(1, 2, 3, 4, 0, 5) == 68);
+ assert(func5(1, 2, 3, 4, 0, 5, 6) == 32);
+ assert(func5(1, 2, 3, 4, 0, 5, 6, 7) == 37);
+ assert(func5(1, 2, 3, 4, 0, 5, 6, 7, 8) == 42);
+ assert(func5(1, 2, 3, 4, 0, 5, 6, 7, 8, 9) == 46);
+}
+
+template AliasSeq(TList...)
+{
+ alias AliasSeq = TList;
+}
+
+T func(T)(T value, AliasSeq!(int) params = AliasSeq!(0))
+{
+ return value;
+}
+
+int func2(AliasSeq!(int) params = AliasSeq!(0))
+{
+ return 0;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21258
+int func3(AliasSeq!(int, int) args = AliasSeq!(3, 5))
+{
+ return args[0] * args[1];
+}
+
+size_t func4(AliasSeq!(int, string) args = AliasSeq!(3, "hello"))
+{
+ return args[0] * args[1].length;
+}
+
+int func5(AliasSeq!(int, int, int) a1 = AliasSeq!(3, 5, 4),
+ AliasSeq!(int, int, int) a2 = AliasSeq!(10, 5, 6),
+ int a3 = 42,
+ AliasSeq!(int, int, int) a4 = AliasSeq!(2, 3, 5),
+ int a5 = 1,
+ )
+{
+ return a1[0] + a1[1] + a1[2] +
+ a2[0] + a2[1] + a2[2] +
+ a3 +
+ a4[0] + a4[1] + a4[2] +
+ a5;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/uda.d b/gcc/testsuite/gdc.test/runnable/uda.d
index a01a5ca6039..cdb9aa660cb 100644
--- a/gcc/testsuite/gdc.test/runnable/uda.d
+++ b/gcc/testsuite/gdc.test/runnable/uda.d
@@ -286,7 +286,7 @@ void test12()
}
/************************************************/
-// 9178
+// https://issues.dlang.org/show_bug.cgi?id=9178
void test9178()
{
@@ -297,7 +297,7 @@ void test9178()
}
/************************************************/
-// 9652
+// https://issues.dlang.org/show_bug.cgi?id=9652
struct Bug9652
{
@@ -353,7 +353,7 @@ struct Bug9652
}
/************************************************/
-// 9741
+// https://issues.dlang.org/show_bug.cgi?id=9741
import imports.a9741;
@@ -364,7 +364,7 @@ alias X9741 = ShowAttributes!(B9741);
@A9741 struct B9741 {}
/************************************************/
-// 12160
+// https://issues.dlang.org/show_bug.cgi?id=12160
auto before12160(alias Hook)(string) { return 0; }
@@ -384,7 +384,7 @@ void test12160()
}
/************************************************/
-// 10208
+// https://issues.dlang.org/show_bug.cgi?id=10208
@( 10) enum int x10208_01 = 100;
@( 20) int x10208_02;
@@ -416,7 +416,8 @@ static assert(__traits(getAttributes, x10208_13)[0] == 130); // Error -> OK
static assert(__traits(getAttributes, x10208_14)[0] == 140); // Error -> OK
/************************************************/
-// 11677, 11678
+// https://issues.dlang.org/show_bug.cgi?id=11677
+// https://issues.dlang.org/show_bug.cgi?id=11678
bool test_uda(alias func)() @safe
{
@@ -445,7 +446,7 @@ static assert(test_uda!func11678b());
static assert(test_uda!func11678c());
/************************************************/
-// 11678
+// https://issues.dlang.org/show_bug.cgi?id=11678
class C11678
{
@@ -460,7 +461,7 @@ static ~this() @(10) @(20) {}
shared static ~this() @(10) @(20) {}
/************************************************/
-// 11679
+// https://issues.dlang.org/show_bug.cgi?id=11679
void test11679()
{
@@ -469,7 +470,7 @@ void test11679()
}
/************************************************/
-// 11680
+// https://issues.dlang.org/show_bug.cgi?id=11680
@(10) gvar11680 = 1; // OK <- NG
@(10) gfun11680() {} // OK <- NG
@@ -481,7 +482,7 @@ void test11680()
}
/************************************************/
-// 11844
+// https://issues.dlang.org/show_bug.cgi?id=11844
auto make_encode11844(T, string name)()
{
@@ -687,7 +688,14 @@ void test20()
}
/************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20831
+void foo20831(int f, float, @("test") string s = "test") {}
+
+static if(is(typeof(foo20831) Params20831 == __parameters))
+ static assert([__traits(getAttributes, Params20831[1..2])] == []);
+
+/************************************************/
int main()
{
diff --git a/gcc/testsuite/gdc.test/runnable/ufcs.d b/gcc/testsuite/gdc.test/runnable/ufcs.d
index e09c531f38a..2d9bf155983 100644
--- a/gcc/testsuite/gdc.test/runnable/ufcs.d
+++ b/gcc/testsuite/gdc.test/runnable/ufcs.d
@@ -129,7 +129,6 @@ void test3()
assert([1] .init == null);
assert([1:1].init == null);
assert(1.0 .init is double.nan);
- assert(10i .init is idouble.nan);
assert('c' .init == 0xFF);
assert("s" .init == null);
@@ -138,7 +137,6 @@ void test3()
assert([1] .init() == 1);
assert([1:1].init() == 1);
assert(1.0 .init() == 1);
- assert(10i .init() == 1);
assert('c' .init() == 1);
assert("s" .init() == 1);
@@ -147,7 +145,6 @@ void test3()
assert([1] .init!(int[])() == 1);
assert([1:1].init!(int[int])() == 1);
assert(1.0 .init!double() == 1);
- assert(10i .init!idouble() == 1);
assert('c' .init!char() == 1);
assert("s" .init!string() == 1);
@@ -422,9 +419,7 @@ class C5 : B5
}
/*******************************************/
-// 662
-
-import std.stdio,std.string, std.conv;
+// https://issues.dlang.org/show_bug.cgi?id=662
enum Etest
{
@@ -450,6 +445,10 @@ int test(Etest test)
//{
// return cast(int)i;
//}
+string to(T)(int i) {
+ assert(i == 22);
+ return "22";
+}
void test682()
{
@@ -465,16 +464,56 @@ void test682()
}
/*******************************************/
-// 3382
-
-import std.range, std.algorithm;
+// https://issues.dlang.org/show_bug.cgi?id=3382
@property T twice(T)(T x){ return x * x; }
-real toreal(ireal x){ return x.im; }
char toupper(char c){ return ('a'<=c && c<='z') ? cast(char)(c - 'a' + 'A') : c; }
@property ref T setter(T)(ref T x, T v){ x = v; return x; }
+auto iota(T)(T min, T max)
+{
+ static struct Result
+ {
+ T cur, end;
+
+ T front() { return cur; }
+ bool empty() { return front == end; }
+ void popFront() { cur++; }
+ }
+ return Result(min, max);
+}
+
+auto map(string s, R)(R range)
+{
+ static struct Result
+ {
+ R source;
+ auto front() { auto a = source.front; return mixin(s); }
+ alias source this;
+ }
+ return Result(range);
+}
+
+auto filter(string s, R)(R range)
+{
+ static struct Result
+ {
+ R source;
+ alias source this;
+ void popFront()
+ {
+ while (true)
+ {
+ auto a = source.front;
+ if (mixin(s)) break;
+ source.popFront();
+ }
+ }
+ }
+ return Result(range);
+}
+
void test3382()
{
auto r = iota(0, 10).map!"a*3"().filter!"a%2 != 0"();
@@ -483,17 +522,29 @@ void test3382()
assert(10.twice == 100);
assert(0.5.twice == 0.25);
- assert(1.4i.toreal() == 1.4);
assert('c'.toupper() == 'C');
}
/*******************************************/
-// 6185
+// https://issues.dlang.org/show_bug.cgi?id=6185
-void test6185()
+ref T front(T)(T[] array) { return array[0]; }
+bool empty(T)(T[] array) { return array.length == 0; }
+void popFront(T)(ref T[] array) { array = array[1..$]; }
+
+bool equal(T, U)(T t, U u)
{
- import std.algorithm;
+ while (true)
+ {
+ if (t.empty) return u.empty;
+ if (u.empty || t.front != u.front) return false;
+ t.popFront();
+ u.popFront();
+ }
+}
+void test6185()
+{
auto r1 = [1,2,3].map!"a*2";
assert(equal(r1, [2,4,6]));
@@ -502,7 +553,7 @@ void test6185()
}
/*******************************************/
-// 6070
+// https://issues.dlang.org/show_bug.cgi?id=6070
enum test6070a = ["test"].foo6070();
enum test6070b = foo6070(["test"]);
@@ -510,13 +561,13 @@ enum test6070b = foo6070(["test"]);
string foo6070(string[] s) { return ""; }
/*******************************************/
-// 7670
+// https://issues.dlang.org/show_bug.cgi?id=7670
struct A7670
{
double x;
}
-@property ref double y7670(ref A7670 a)
+@property ref double y7670(return ref A7670 a)
{
return a.x;
}
@@ -528,7 +579,7 @@ void test7670()
}
/*******************************************/
-// 7703
+// https://issues.dlang.org/show_bug.cgi?id=7703
void f7703(T)(T a) { }
void test7703()
@@ -541,9 +592,9 @@ void test7703()
}
/*******************************************/
-// 7773
+// https://issues.dlang.org/show_bug.cgi?id=7773
-//import std.stdio;
+//import core.stdc.stdio;
void writeln7773(int n){}
void test7773()
{
@@ -552,7 +603,7 @@ void test7773()
}
/*******************************************/
-// 7943
+// https://issues.dlang.org/show_bug.cgi?id=7943
struct Foo7943
{
@@ -570,7 +621,7 @@ void test7943()
}
/*******************************************/
-// 8180
+// https://issues.dlang.org/show_bug.cgi?id=8180
int writeln8180(T...)(T args) { return 1; }
@@ -587,7 +638,7 @@ void test8180()
}
/*******************************************/
-// 8245
+// https://issues.dlang.org/show_bug.cgi?id=8245
string toStr8245(immutable(char)* p) { return null; }
@property string asStr8245(immutable(char)* p) { return null; }
@@ -600,7 +651,7 @@ void test8245()
}
/*******************************************/
-// 8252
+// https://issues.dlang.org/show_bug.cgi?id=8252
bool f(int x) { return !x; }
@@ -611,7 +662,7 @@ void test8252()
}
/*******************************************/
-// 8453
+// https://issues.dlang.org/show_bug.cgi?id=8453
T[] sort8453(T)(T[] a) { return a; }
@@ -623,7 +674,7 @@ void test8453()
}
/*******************************************/
-// 8503
+// https://issues.dlang.org/show_bug.cgi?id=8503
void α8503(int i) {}
@@ -634,7 +685,7 @@ void test8503()
}
/*******************************************/
-// 9014
+// https://issues.dlang.org/show_bug.cgi?id=9014
@property ref int foo9014(int[] a)
{
@@ -649,7 +700,7 @@ void test9014()
}
/*******************************************/
-// 9590
+// https://issues.dlang.org/show_bug.cgi?id=9590
auto func9590(E)(lazy E expr) { }
@@ -666,7 +717,7 @@ void test9590()
}
/*******************************************/
-// 9946
+// https://issues.dlang.org/show_bug.cgi?id=9946
size_t count9946(alias x)(int[] haystack)
{
@@ -682,7 +733,7 @@ void test9946()
}
/*******************************************/
-// 10618
+// https://issues.dlang.org/show_bug.cgi?id=10618
template Temp10618(T)
{
@@ -695,7 +746,7 @@ void test10618()
}
/*******************************************/
-// 10003
+// https://issues.dlang.org/show_bug.cgi?id=10003
void foo10003(void *p) {}
void test10003()
@@ -705,7 +756,7 @@ void test10003()
}
/*******************************************/
-// 10041
+// https://issues.dlang.org/show_bug.cgi?id=10041
auto writeln10041(T...)(T args) { return typeof(args[0]).stringof; }
@@ -717,7 +768,7 @@ void test10041()
}
/*******************************************/
-// 10047
+// https://issues.dlang.org/show_bug.cgi?id=10047
struct Typedef10047(T)
{
@@ -737,7 +788,7 @@ void test10047()
}
/*******************************************/
-// 10166
+// https://issues.dlang.org/show_bug.cgi?id=10166
auto foo10166()
{
@@ -752,7 +803,7 @@ void bar10166(alias handler, T)(T t, int i)
void buzz10166() {}
/*******************************************/
-// 10526
+// https://issues.dlang.org/show_bug.cgi?id=10526
struct S10526
{
@@ -781,7 +832,7 @@ void test10526()
}
/********************************************************/
-// 10609
+// https://issues.dlang.org/show_bug.cgi?id=10609
int foo10609(int x) { return x; }
@@ -794,7 +845,7 @@ void test10609()
}
/*******************************************/
-// 11312
+// https://issues.dlang.org/show_bug.cgi?id=11312
struct S11312;
@@ -809,7 +860,7 @@ void test11312()
}
/*******************************************/
-// 15123
+// https://issues.dlang.org/show_bug.cgi?id=15123
auto keys15123(K, V)(V[K] aa) { return [1]; }
auto values15123(K, V)(V[K] aa) { return [2]; }
diff --git a/gcc/testsuite/gdc.test/runnable/uniformctor.d b/gcc/testsuite/gdc.test/runnable/uniformctor.d
index d56c782bf9e..6cd45827820 100644
--- a/gcc/testsuite/gdc.test/runnable/uniformctor.d
+++ b/gcc/testsuite/gdc.test/runnable/uniformctor.d
@@ -1,10 +1,16 @@
+/*
+RUN_OUTPUT:
+---
+Success
+---
+*/
extern(C) int printf(const char*, ...);
template TypeTuple(TL...) { alias TypeTuple = TL; }
import core.stdc.math : isnan;
/********************************************/
-// 9112
+// https://issues.dlang.org/show_bug.cgi?id=9112
void test9112a() // T() and T(v)
{
@@ -40,19 +46,12 @@ void test9112a() // T() and T(v)
test!( float )(3.14);
test!( double)(3.14);
test!( real )(3.14);
- test!(ifloat )(1.4142i);
- test!(idouble)(1.4142i);
- test!(ireal )(1.4142i);
- test!(cfloat )(1.2+3.4i);
- test!(cdouble)(1.2+3.4i);
- test!(creal )(1.2+3.4i);
test!( char )('A');
test!(wchar )('A');
test!(dchar )('A');
test!(bool )(true);
static assert(!__traits(compiles, int(1.42))); // in curre,t this is disallowed
- static assert(!__traits(compiles, double(3.14i)));
{
int x;
@@ -107,12 +106,6 @@ void test9112b() // new T(v)
test!( float )(3.14);
test!( double)(3.14);
test!( real )(3.14);
- test!(ifloat )(1.4142i);
- test!(idouble)(1.4142i);
- test!(ireal )(1.4142i);
- test!(cfloat )(1.2+3.4i);
- test!(cdouble)(1.2+3.4i);
- test!(creal )(1.2+3.4i);
test!( char )('A');
test!(wchar )('A');
test!(dchar )('A');
@@ -138,7 +131,6 @@ void test9112b() // new T(v)
static assert(!__traits(compiles, new const int(1, 2)));
static assert(!__traits(compiles, new int(1.42))); // in curre,t this is disallowed
- static assert(!__traits(compiles, new double(3.14i)));
// int(1) in directly on statement scope should be parsed as an expression, but
// would fail to compile because of "has no effect" error.
diff --git a/gcc/testsuite/gdc.test/runnable/unique_typeinfo_names.d b/gcc/testsuite/gdc.test/runnable/unique_typeinfo_names.d
new file mode 100644
index 00000000000..120f8ffedca
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/unique_typeinfo_names.d
@@ -0,0 +1,93 @@
+module unique_typeinfo_names;
+
+// https://issues.dlang.org/show_bug.cgi?id=22149
+void structs()
+{
+ static struct Foo(T) {}
+
+ auto foo()
+ {
+ struct S {}
+ return Foo!S();
+ }
+
+ auto bar()
+ {
+ struct S {}
+ return Foo!S();
+ }
+
+ auto f = foo();
+ auto b = bar();
+
+ assert(typeid(f) != typeid(b));
+ assert(typeid(f).name != typeid(b).name);
+
+ assert(typeid(f).mangledName == typeof(f).mangleof);
+ assert(typeid(b).mangledName == typeof(b).mangleof);
+ assert(typeid(f).name == "unique_typeinfo_names.structs().Foo!(unique_typeinfo_names.structs().foo().S).Foo");
+ assert(typeid(b).name == "unique_typeinfo_names.structs().Foo!(unique_typeinfo_names.structs().bar().S).Foo");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=22150
+void classes()
+{
+ static class Foo(T) {}
+
+ static auto foo()
+ {
+ struct S {}
+ return new Foo!S();
+ }
+
+ static auto bar()
+ {
+ struct S {}
+ return new Foo!S();
+ }
+
+ auto f = foo();
+ auto b = bar();
+
+ assert(typeid(f) != typeid(b));
+ assert(typeid(f).name != typeid(b).name);
+
+ assert(typeid(f).name == "unique_typeinfo_names.classes.Foo!(unique_typeinfo_names.classes.foo.S).Foo");
+ assert(typeid(b).name == "unique_typeinfo_names.classes.Foo!(unique_typeinfo_names.classes.bar.S).Foo");
+}
+
+void interfaces()
+{
+ static interface IFoo(T) {}
+ static class Foo(T) : IFoo!T {}
+
+ static auto foo()
+ {
+ struct S {}
+ IFoo!S r = new Foo!S();
+ return r;
+ }
+
+ static auto bar()
+ {
+ struct S {}
+ IFoo!S r = new Foo!S();
+ return r;
+ }
+
+ auto f = foo();
+ auto b = bar();
+
+ assert(typeid(f) != typeid(b));
+ assert(typeid(f).name != typeid(b).name);
+
+ assert(typeid(f).name == "unique_typeinfo_names.interfaces.IFoo!(unique_typeinfo_names.interfaces.foo.S).IFoo");
+ assert(typeid(b).name == "unique_typeinfo_names.interfaces.IFoo!(unique_typeinfo_names.interfaces.bar.S).IFoo");
+}
+
+void main()
+{
+ structs();
+ classes();
+ interfaces();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/variadic.d b/gcc/testsuite/gdc.test/runnable/variadic.d
index 984d896e29a..2d0736ded66 100644
--- a/gcc/testsuite/gdc.test/runnable/variadic.d
+++ b/gcc/testsuite/gdc.test/runnable/variadic.d
@@ -672,7 +672,7 @@ void test61()
template Tuple63(T...){
alias T Tuple63;
}
-// Bugzilla 3336
+// https://issues.dlang.org/show_bug.cgi?id=3336
static assert(!is(int[ Tuple63!(int, int) ]));
void test63()
@@ -691,7 +691,7 @@ void test1411()
}
/***************************************/
-// Bugzilla 4444
+// https://issues.dlang.org/show_bug.cgi?id=4444
void test4444()
{
@@ -701,7 +701,7 @@ void test4444()
}
/***************************************/
-// 13864
+// https://issues.dlang.org/show_bug.cgi?id=13864
struct Tuple13864(T...)
{
@@ -721,7 +721,7 @@ void test13864()
}
/***************************************/
-// 4884
+// https://issues.dlang.org/show_bug.cgi?id=4884
struct A4884(T...)
{
@@ -736,7 +736,7 @@ void test4884()
}
/***************************************/
-// 4920
+// https://issues.dlang.org/show_bug.cgi?id=4920
struct Test4920(parameters_...)
{
@@ -751,7 +751,7 @@ void test4920()
}
/***************************************/
-// 4940
+// https://issues.dlang.org/show_bug.cgi?id=4940
template Tuple4940(T...)
{
@@ -777,7 +777,7 @@ struct S4940add
long x;
}
-ref S4940add get4940add(ref S4940add s){ return s; }
+ref S4940add get4940add(return ref S4940add s){ return s; }
void test4940add()
{
@@ -787,7 +787,7 @@ void test4940add()
}
/***************************************/
-// 6530
+// https://issues.dlang.org/show_bug.cgi?id=6530
struct S6530
{
@@ -876,7 +876,7 @@ void testCopy()
}
/***************************************/
-// 6700
+// https://issues.dlang.org/show_bug.cgi?id=6700
template bug6700(TList ...) {
const int bug6700 = 2;
@@ -886,7 +886,7 @@ TypeTuple!(int, long) TT6700;
static assert(bug6700!( (TT6700[1..$]) )==2);
/***************************************/
-// 6966
+// https://issues.dlang.org/show_bug.cgi?id=6966
template X6966(T...)
{
@@ -896,7 +896,7 @@ static assert(is(X6966!(int) == const(int)));
static assert(is(X6966!(int, 0) == const(int)));
/***************************************/
-// 7233
+// https://issues.dlang.org/show_bug.cgi?id=7233
struct Foo7233 { int x, y; }
Foo7233[] front7233(Foo7233[][] a)
@@ -920,7 +920,7 @@ void test7233()
}
/***************************************/
-// 7263
+// https://issues.dlang.org/show_bug.cgi?id=7263
template TypeTuple7263(T...){ alias T TypeTuple7263; }
@@ -940,13 +940,13 @@ void test7263()
}
/***************************************/
-// 8244
+// https://issues.dlang.org/show_bug.cgi?id=8244
TypeTuple!(int,int)[] x8244;
static assert(is(typeof(x8244) == TypeTuple!(int, int)));
/***************************************/
-// 9017
+// https://issues.dlang.org/show_bug.cgi?id=9017
template X9017(Args...)
{
@@ -974,13 +974,13 @@ void test9017()
}
/***************************************/
-// 10279
+// https://issues.dlang.org/show_bug.cgi?id=10279
void foo10279(int[][] strs...) @trusted { }
void bar10279() @safe { foo10279(); }
/***************************************/
-// 13508
+// https://issues.dlang.org/show_bug.cgi?id=13508
struct S13508
{
@@ -1001,7 +1001,7 @@ void test13508() @safe @nogc
}
/***************************************/
-// 14395
+// https://issues.dlang.org/show_bug.cgi?id=14395
int v2u14395(uint[1] ar...)
{
@@ -1019,7 +1019,7 @@ void test14395()
}
/***************************************/
-// 10414
+// https://issues.dlang.org/show_bug.cgi?id=10414
void foo10414(void delegate()[] ...) { }
@@ -1057,7 +1057,7 @@ void test14179()
}
/***************************************/
-// 10722
+// https://issues.dlang.org/show_bug.cgi?id=10722
struct S10722
{
diff --git a/gcc/testsuite/gdc.test/runnable/version.d b/gcc/testsuite/gdc.test/runnable/version.d
index 67da1885882..1186e4c6a36 100644
--- a/gcc/testsuite/gdc.test/runnable/version.d
+++ b/gcc/testsuite/gdc.test/runnable/version.d
@@ -1,5 +1,12 @@
-// PERMUTE_ARGS:
-// REQUIRED_ARGS: -version=3 -version=foo
+/*
+PERMUTE_ARGS:
+REQUIRED_ARGS: -version=3 -version=foo
+RUN_OUTPUT:
+---
+i = 2
+i = 2
+---
+*/
extern(C) int printf(const char*, ...);
@@ -64,4 +71,3 @@ int main()
test2();
return 0;
}
-
diff --git a/gcc/testsuite/gdc.test/runnable/warning1.d b/gcc/testsuite/gdc.test/runnable/warning1.d
index 9dcbea9f471..537088e97cc 100644
--- a/gcc/testsuite/gdc.test/runnable/warning1.d
+++ b/gcc/testsuite/gdc.test/runnable/warning1.d
@@ -120,7 +120,7 @@ nothrow int foo6()
}
/******************************************/
-// 6518
+// https://issues.dlang.org/show_bug.cgi?id=6518
template TypeTuple(T...) { alias T TypeTuple; }
void test6518()
@@ -134,7 +134,7 @@ void test6518()
}
/******************************************/
-// 7232
+// https://issues.dlang.org/show_bug.cgi?id=7232
bool test7232()
{
@@ -154,7 +154,7 @@ struct S9332
}
/******************************************/
-// 13201
+// https://issues.dlang.org/show_bug.cgi?id=13201
class C13201
{
diff --git a/gcc/testsuite/gdc.test/runnable/wc.d b/gcc/testsuite/gdc.test/runnable/wc.d
index 8f847c5938f..69d3cf85d1d 100644
--- a/gcc/testsuite/gdc.test/runnable/wc.d
+++ b/gcc/testsuite/gdc.test/runnable/wc.d
@@ -37,14 +37,14 @@ int main (string[] args)
inword = 0;
++c_cnt;
}
- printf ("%8lu%8lu%8lu %.*s\n", l_cnt, w_cnt, c_cnt, arg.length, arg.ptr);
+ printf ("%8u%8u%8u %.*s\n", l_cnt, w_cnt, c_cnt, cast(int)arg.length, arg.ptr);
l_total += l_cnt;
w_total += w_cnt;
c_total += c_cnt;
}
if (args.length > 2)
{
- printf ("--------------------------------------\n%8lu%8lu%8lu total",
+ printf ("--------------------------------------\n%8u%8u%8u total",
l_total, w_total, c_total);
}
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/wc2.d b/gcc/testsuite/gdc.test/runnable/wc2.d
index a97c6fa8620..97fcdeef621 100644
--- a/gcc/testsuite/gdc.test/runnable/wc2.d
+++ b/gcc/testsuite/gdc.test/runnable/wc2.d
@@ -54,7 +54,7 @@ int main (string[] args)
{ string w = input[wstart .. input.length];
dictionary[w]++;
}
- printf("%8lu%8lu%8lu %.*s\n", l_cnt, w_cnt, c_cnt, arg.length, arg.ptr);
+ printf("%8u%8u%8u %.*s\n", l_cnt, w_cnt, c_cnt, cast(int)arg.length, arg.ptr);
l_total += l_cnt;
w_total += w_cnt;
c_total += c_cnt;
@@ -62,14 +62,14 @@ int main (string[] args)
if (args.length > 2)
{
- printf("--------------------------------------\n%8lu%8lu%8lu total",
+ printf("--------------------------------------\n%8u%8u%8u total",
l_total, w_total, c_total);
}
printf("--------------------------------------\n");
foreach (string word1; dictionary.keys)
{
- printf("%3d %.*s\n", dictionary[word1], word1.length, word1.ptr);
+ printf("%3d %.*s\n", dictionary[word1], cast(int)word1.length, word1.ptr);
}
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/whetstone.d b/gcc/testsuite/gdc.test/runnable/whetstone.d
new file mode 100644
index 00000000000..9145c74bcfd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/whetstone.d
@@ -0,0 +1,231 @@
+/*
+PERMUTE_ARGS: -O
+*/
+
+/*
+ WHETSTONE BENCHMARK PROGRAM
+
+ This program uses a carefully chosen mix of instructions typical of
+ scientific (floating point) calculations.
+
+ See H.J. Curnow and B.A. Wichmann,
+ "A Synthetic Benchmark", Computer J., V19 #1, Feb. 1976, pp. 43-49.
+
+ Table of times for various computers in <info-ibmpc>whetst.answers
+ compiled by Richard Gillmann (GILLMANN@ISIB)
+
+Whetstone Fortran Benchmark
+(I=10, optimization off, CPU seconds)
+
+DEC 1.1 sec DECsystem 2060 (TOPS-20 v4, F66)
+PR1ME 1.4 sec PR1ME 750 (PRIMOS v18.1, F66)
+PR1ME 1.5 sec PR1ME 750 (PRIMOS v18.1, F77)
+DEC 2.1 sec VAX 11/780 (Unix, F77)
+Apollo 6.2 sec 10 MHz MC68000 w/hardware float. point (AEGIS v4.0, F77)
+Apollo 13.1 sec 10 MHz MC68000 w/software float. point (AEGIS v4.0, F77)
+Intel 16.0 sec 8086/8087 (286WD Micro Development System,Intel FORTRAN)
+IBM 16.0 sec 4.77 MHz 8088 PC w/8087 (DOS 2, Microsoft F77/3.10)
+Z80 124.0 sec 4 MHz Z80 with Microsoft Fortran, CP/M
+IBM 268.9 sec 4.77 MHz 8088 PC ($NODEBUG) (DOS 1, Microsoft F77/1.0)
+Intel 390.0 sec 8086 alone (286WD Micro Development System,Intel FORTRAN)
+
+Table compiled by Richard Gillmann (Gillmann@ISIB).
+*/
+
+import core.stdc.stdio;
+import core.stdc.time;
+import core.stdc.math;
+
+double t, t1, t2;
+double[5] e1;
+int j, k, l;
+
+int main()
+{
+ clock_t start, stop;
+ double x1, x2, x3, x4, x, y, z;
+ int i, isave, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12;
+
+ start = clock();
+
+/* I=10 CORRESPONDS TO ONE MILLION WHETSTONE INSTRUCTIONS */
+
+ i = 10;
+ t1 = 0.50025000;
+ t = 0.499975000;
+ t2 = 2.0000;
+ isave = i;
+ n1 = 0;
+ n2 = 12 * i;
+ n3 = 14 * i;
+ n4 = 348 * i;
+ n5 = 0;
+ n6 = 210 * i;
+ n7 = 32 * i;
+ n8 = 899 * i;
+ n9 = 516 * i;
+ n10 = 0;
+ n11 = 93 * i;
+ n12 = 0;
+ x1 = 1.0;
+ x2 = -1.0;
+ x3 = -1.0;
+ x4 = -1.0;
+ for (i = 1; i <= n1; i++)
+ {
+ x1 = (x1+x2+x3-x4)*t;
+ x2 = (x1+x2-x3+x4)*t;
+ x3 = (x1-x2+x3+x4)*t;
+ x4 = (-x1+x2+x3+x4)*t;
+ }
+ auto s = pout(n1,n1,n1,x1,x2,x3,x4);
+ assert(s[] == " 0 0 0 1.0000e+00 -1.0000e+00 -1.0000e+00 -1.0000e+00");
+
+ e1[1] = 1.0;
+ e1[2] = -1.0;
+ e1[3] = -1.0;
+ e1[4] = -1.0;
+
+ for (i = 1; i <= n2; i++)
+ {
+ e1[1] = (e1[1]+e1[2]+e1[3]-e1[4])*t;
+ e1[2] = (e1[1]+e1[2]-e1[3]+e1[4])*t;
+ e1[3] = (e1[1]-e1[2]+e1[3]+e1[4])*t;
+ e1[4] = (-e1[1]+e1[2]+e1[3]+e1[4])*t;
+ }
+ pout(n2,n3,n2,e1[1],e1[2],e1[3],e1[4]);
+ assert(s == " 120 140 120 -6.8342e-02 -4.6264e-01 -7.2972e-01 -1.1240e+00");
+
+ for (i = 1; i <= n3; i++)
+ {
+ pa(e1);
+ }
+ pout(n3,n2,n2,e1[1],e1[2],e1[3],e1[4]);
+ assert(s == " 140 120 120 -5.5336e-02 -4.4744e-01 -7.1097e-01 -1.1031e+00");
+
+ j = 1;
+ for (i = 1; i <= n4; i++)
+ {
+ j = (j-1) ? 3 : 2;
+ j = (j-2 < 0) ? 0 : 1;
+ j = (j-1 < 0) ? 1 : 0;
+ }
+ pout(n4, j, j, x1, x2, x3, x4);
+ assert(s == " 3480 0 0 1.0000e+00 -1.0000e+00 -1.0000e+00 -1.0000e+00");
+
+ j = 1;
+ k = 2;
+ l = 3;
+ for (i = 1; i <= n6; i++)
+ {
+ j = j*(k-j)*(l-k);
+ k = l*k-(l-j)*k;
+ l = (l-k)*(k+j);
+ e1[l-1] = j+k+l;
+ e1[k-1] = j*k*l;
+ }
+ pout(n6,j,k,e1[1],e1[2],e1[3],e1[4]);
+ assert(s == " 2100 1 2 6.0000e+00 6.0000e+00 -7.1097e-01 -1.1031e+00");
+
+ x = 0.5;
+ y = 0.5;
+ for (i = 1; i <= n7; i++)
+ {
+ x = t * atan(t2* sin(x)* cos(x) /
+ ( cos(x+y)+ cos(x-y)-1.0 ));
+ y = t * atan(t2* sin(y)* cos(y) /
+ ( cos(x+y)+ cos(x-y)-1.0 ));
+ }
+ pout(n7, j, k, x, x, y, y);
+ assert(s == " 320 1 2 4.9041e-01 4.9041e-01 4.9039e-01 4.9039e-01");
+
+ x = 1.0;
+ y = 1.0;
+ z = 1.0;
+
+ for (i = 1; i <= n8; i++)
+ {
+ z = p3(x, y);
+ }
+ pout(n8, j, k, x, y, z, z);
+ assert(s == " 8990 1 2 1.0000e+00 1.0000e+00 9.9994e-01 9.9994e-01");
+
+ j = 1;
+ k = 2;
+ l = 3;
+ e1[1] = 1.0;
+ e1[2] = 2.0;
+ e1[3] = 3.0;
+
+ for (i = 1; i <= n9; i++)
+ {
+ p0();
+ }
+ pout(n9, j, k, e1[1], e1[2], e1[3], e1[4]);
+ assert(s == " 5160 1 2 3.0000e+00 2.0000e+00 3.0000e+00 -1.1031e+00");
+
+ j = 2;
+ k = 3;
+
+ for (i = 1; i <= n10; i++)
+ {
+ j = j + k;
+ k = j + k;
+ j = j - k;
+ k = k - j - j;
+ }
+ pout(n10, j, k, x1, x2, x3, x4);
+ assert(s == " 0 2 3 1.0000e+00 -1.0000e+00 -1.0000e+00 -1.0000e+00");
+
+ x = 0.75;
+
+ for (i = 1; i <= n11; i++)
+ {
+ x = sqrt( exp( log(x) / t1));
+ }
+
+ pout(n11,j,k,x,x,x,x);
+ assert(s == " 930 2 3 8.3467e-01 8.3467e-01 8.3467e-01 8.3467e-01");
+
+ stop = clock();
+
+ version (none)
+ printf("Elapsed time = %d.%02d seconds\n",
+ cast(int)(stop - start)/CLOCKS_PER_SEC,
+ cast(int)(stop - start)%CLOCKS_PER_SEC);
+ return 0;
+}
+
+void pa(double[] e)
+{
+ for (j = 0; j < 6; j++)
+ {
+ e[1] = (e[1] + e[2] + e[3] - e[4]) * t;
+ e[2] = (e[1] + e[2] - e[3] + e[4]) * t;
+ e[3] = (e[1] - e[2] + e[3] + e[4]) * t;
+ e[4] = (-e[1] + e[2] + e[3] + e[4]) / t2;
+ }
+}
+
+void p0()
+{
+ e1[j] = e1[k];
+ e1[k] = e1[l];
+ e1[l] = e1[j];
+}
+
+double p3(double x, double y)
+{
+ x = t * (x + y);
+ y = t * (x + y);
+ return (x + y) / t2;
+}
+
+char[] pout(int n, int j, int k, double x1, double x2, double x3, double x4)
+{
+ __gshared char[80] result;
+ const len = sprintf(result.ptr, " %7d %7d %7d %12.4e %12.4e %12.4e %12.4e",
+ n, j, k, x1, x2, x3, x4);
+ printf("%s\n", result.ptr);
+ return result[0 .. len];
+}
diff --git a/gcc/testsuite/gdc.test/runnable/xdtor.d b/gcc/testsuite/gdc.test/runnable/xdtor.d
index df3a4d18c44..d4b6e2ba8ff 100644
--- a/gcc/testsuite/gdc.test/runnable/xdtor.d
+++ b/gcc/testsuite/gdc.test/runnable/xdtor.d
@@ -79,8 +79,34 @@ void test2() @safe @nogc nothrow
assert(Counter.cnt == 1);
}
+struct Bar17257(E)
+{
+ ~this() @safe @nogc nothrow
+ {
+ assert(__traits(hasMember, E, "__xdtor"));
+ }
+}
+
+struct Foo17257A
+{
+ Bar17257!Foo17257A foo;
+ ~this() @safe @nogc nothrow {}
+}
+
+struct Foo17257B
+{
+ Bar17257!Foo17257B foo;
+}
+
+void test3() @safe @nogc nothrow
+{
+ Foo17257A foo17257A;
+ Foo17257B foo17257B;
+}
+
void main()
{
test1();
test2();
+ test3();
}
diff --git a/gcc/testsuite/gdc.test/runnable/xpostblit.d b/gcc/testsuite/gdc.test/runnable/xpostblit.d
index b364c4cff63..8f8a59d093f 100644
--- a/gcc/testsuite/gdc.test/runnable/xpostblit.d
+++ b/gcc/testsuite/gdc.test/runnable/xpostblit.d
@@ -70,8 +70,67 @@ void test2() @safe @nogc nothrow
assert(Counter.cnt == 1);
}
+/****************************************************************
+ This test is intended to verify the exception safety of field
+ postblits
+*/
+string trace = "";
+
+struct FieldThrow
+{
+ string name;
+ this(string n)
+ {
+ name = n;
+ }
+
+ bool throwExcept;
+ this(this)
+ {
+ if (throwExcept)
+ {
+ throw new Exception("");
+ }
+ }
+
+ ~this() { trace ~= name ~ ".dtor"; }
+}
+
+struct S
+{
+ auto f1 = FieldThrow("f1");
+ FieldThrow[2] f2f3= [FieldThrow("f2"), FieldThrow("f3")];
+ auto f4 = FieldThrow("f4");
+}
+
+void test3()
+{
+ trace = "";
+
+ S s1;
+
+ // Cause `s1.f4`'s postblit to throw
+ s1.f4.throwExcept = true;
+
+ try
+ {
+ // `s`'s postblit will be a combination of `f1`, `f2f3`, and `f4`'s
+ // postblit in that order. However, `f4`'s postblit will throw,
+ // causing `s1.f2f3` and `s1.f1`'s destructors to execute in that
+ // order
+ S s2 = s1;
+ }
+ catch(Exception ex){ }
+
+ // Confirm the field destructors were called and were called in the
+ // corrrect order
+ assert(trace == "f3.dtor" ~ "f2.dtor" ~ "f1.dtor");
+}
+/****************************************************************************/
+
void main()
{
test1();
test2();
+ test3();
}
diff --git a/gcc/testsuite/gdc.test/runnable/xtest46.d b/gcc/testsuite/gdc.test/runnable/xtest46.d
index bf19f57c68b..20cc225759a 100644
--- a/gcc/testsuite/gdc.test/runnable/xtest46.d
+++ b/gcc/testsuite/gdc.test/runnable/xtest46.d
@@ -1,5 +1,5 @@
-// RUNNABLE_PHOBOS_TEST
-// PERMUTE_ARGS: -unittest -O -release -inline -fPIC -g
+// REQUIRED_ARGS: -preview=rvaluerefparam
+//
/* TEST_OUTPUT:
---
Boo!double
@@ -33,14 +33,14 @@ TFunction1: extern (C) void function()
---
*/
-import std.stdio;
+//import std.stdio;
import core.stdc.stdio;
/******************************************/
struct S
{
- int opStar() { return 7; }
+ int opUnary(string op)() if (op == "*") { return 7; }
}
void test1()
@@ -76,29 +76,6 @@ void bar2(D)(const(void)* arg)
D obj = *cast(D*) arg;
}
-/***************************************************/
-
-void test3()
-{
- version (unittest)
- {
- printf("unittest!\n");
- }
- else
- {
- printf("no unittest!\n");
- }
-
- version (assert)
- {
- printf("assert!\n");
- }
- else
- {
- printf("no assert!\n");
- }
-}
-
/***************************************************/
@@ -160,7 +137,7 @@ struct T6
S6 s;
int b = 7;
- S6* opDot()
+ S6* opDot() return
{
return &s;
}
@@ -217,7 +194,7 @@ void test7()
void foo8(int n1 = __LINE__ + 0, int n2 = __LINE__, string s = __FILE__)
{
assert(n1 < n2);
- printf("n1 = %d, n2 = %d, s = %.*s\n", n1, n2, s.length, s.ptr);
+ printf("n1 = %d, n2 = %d, s = %.*s\n", n1, n2, cast(int)s.length, s.ptr);
}
void test8()
@@ -230,7 +207,7 @@ void test8()
void foo9(int n1 = __LINE__ + 0, int n2 = __LINE__, string s = __FILE__)()
{
assert(n1 < n2);
- printf("n1 = %d, n2 = %d, s = %.*s\n", n1, n2, s.length, s.ptr);
+ printf("n1 = %d, n2 = %d, s = %.*s\n", n1, n2, cast(int)s.length, s.ptr);
}
void test9()
@@ -564,7 +541,7 @@ void test27()
/***************************************************/
-ref int foo28(ref int x) { return x; }
+ref int foo28(return ref int x) { return x; }
void test28()
{
@@ -687,13 +664,13 @@ void test34()
/***************************************************/
-ref int foo35(bool condition, ref int lhs, ref int rhs)
+ref int foo35(bool condition, return ref int lhs, return ref int rhs)
{
if ( condition ) return lhs;
return rhs;
}
-ref int bar35()(bool condition, ref int lhs, ref int rhs)
+ref int bar35()(bool condition, return ref int lhs, return ref int rhs)
{
if ( condition ) return lhs;
return rhs;
@@ -874,7 +851,7 @@ void test44()
}
/***************************************************/
-// 2006
+// https://issues.dlang.org/show_bug.cgi?id=2006
void test2006()
{
@@ -887,7 +864,7 @@ void test2006()
}
/***************************************************/
-// 8442
+// https://issues.dlang.org/show_bug.cgi?id=8442
void test8442()
{
@@ -988,7 +965,7 @@ void test48()
}
/***************************************************/
-// 6408
+// https://issues.dlang.org/show_bug.cgi?id=6408
static assert(!is(typeof(string[0..1].init)));
static assert(is(typeof(string[].init) == string[]));
@@ -1009,7 +986,7 @@ static assert(is(typeof(TT6408!(int, int)[0..$].init) == TT6408!(int, int)));
static assert(is(typeof(TT6408!(int, int)[$-1].init) == int));
/***************************************************/
-// 9409
+// https://issues.dlang.org/show_bug.cgi?id=9409
template TT9409(T...) { alias T TT9409; }
@@ -1028,7 +1005,7 @@ struct S49
this( string name )
{
- printf( "(ctor) &%.*s.x = %p\n", name.length, name.ptr, &x );
+ printf( "(ctor) &%.*s.x = %p\n", cast(int)name.length, name.ptr, &x );
p = cast(void*)&x;
}
@@ -1198,7 +1175,8 @@ pure immutable(T)[] fooPT(T)(immutable(T)[] x, immutable(T)[] y){
void test61()
{
- writeln(fooPT("p", "c"));
+ auto s = fooPT("p", "c");
+ printf("%.*s\n", cast(int)s.length, s.ptr);
}
/***************************************************/
@@ -1347,7 +1325,7 @@ void test67()
void test68()
{
- digestToString(cast(ubyte[16])x"c3fcd3d76192e4007dfb496cca67e13b");
+ digestToString(cast(ubyte[16])"\xc3\xfc\xd3\xd7\x61\x92\xe4\x00\x7d\xfb\x49\x6c\xca\x67\xe1\x3b");
}
void digestToString(const ubyte[16] digest)
@@ -1360,7 +1338,7 @@ void digestToString(const ubyte[16] digest)
void test69()
{
- digestToString69(cast(ubyte[16])x"c3fcd3d76192e4007dfb496cca67e13b");
+ digestToString69(cast(ubyte[16])"\xc3\xfc\xd3\xd7\x61\x92\xe4\x00\x7d\xfb\x49\x6c\xca\x67\xe1\x3b");
}
void digestToString69(ref const ubyte[16] digest)
@@ -1776,7 +1754,7 @@ void test86()
/***************************************************/
-// Bugzilla 3379
+// https://issues.dlang.org/show_bug.cgi?id=3379
T1[] find(T1, T2)(T1[] longer, T2[] shorter)
if (is(typeof(longer[0 .. 1] == shorter) : bool))
@@ -1832,7 +1810,7 @@ struct S88
{
void opDispatch(string s, T)(T i)
{
- printf("S.opDispatch('%.*s', %d)\n", s.length, s.ptr, i);
+ printf("S.opDispatch('%.*s', %d)\n", cast(int)s.length, s.ptr, i);
}
}
@@ -1840,7 +1818,7 @@ class C88
{
void opDispatch(string s)(int i)
{
- printf("C.opDispatch('%.*s', %d)\n", s.length, s.ptr, i);
+ printf("C.opDispatch('%.*s', %d)\n", cast(int)s.length, s.ptr, i);
}
}
@@ -1874,7 +1852,7 @@ void test89() {
int bar() { return x; }
}
X s;
- printf("%d\n", s.sizeof);
+ printf("%zd\n", s.sizeof);
assert(s.sizeof == 4);
}
@@ -2053,8 +2031,8 @@ void test96()
{
S96!([12, 3]) s1;
S96!([1, 23]) s2;
- writeln(s1.content);
- writeln(s2.content);
+ //writeln(s1.content);
+ //writeln(s2.content);
assert(!is(typeof(s1) == typeof(s2)));
}
@@ -2105,7 +2083,7 @@ void test99()
}
/***************************************************/
-// 5081
+// https://issues.dlang.org/show_bug.cgi?id=5081
void test5081()
{
@@ -2222,7 +2200,7 @@ void test104()
/***************************************************/
-ref int bump105(ref int x) { return ++x; }
+ref int bump105(return ref int x) { return ++x; }
void test105()
{
@@ -2246,8 +2224,8 @@ pure int genFactorials(int n) {
void test107()
{
int[6] a;
- writeln(a);
- writeln(a.init);
+ //writeln(a);
+ //writeln(a.init);
assert(a.init == [0,0,0,0,0,0]);
}
@@ -2289,7 +2267,7 @@ int test11247()
/***************************************************/
-// 3716
+// https://issues.dlang.org/show_bug.cgi?id=3716
void test111()
{
auto k1 = true ? [1,2] : []; // OK
@@ -2302,7 +2280,7 @@ void test111()
}
/***************************************************/
-// 658
+// https://issues.dlang.org/show_bug.cgi?id=658
void test658()
{
@@ -2333,7 +2311,7 @@ void test3069()
}
/***************************************************/
-// 4303
+// https://issues.dlang.org/show_bug.cgi?id=4303
template foo112() if (__traits(compiles,undefined))
{
@@ -2405,7 +2383,7 @@ template foo114(fun...)
pragma(msg, typeof(foo114!"a + b"([1,2,3])));
/***************************************************/
-// Bugzilla 3935
+// https://issues.dlang.org/show_bug.cgi?id=3935
struct Foo115 {
void opBinary(string op)(Foo other) {
@@ -2421,7 +2399,7 @@ void test115()
}
/***************************************************/
-// Bugzilla 2477
+// https://issues.dlang.org/show_bug.cgi?id=2477
void foo116(T,)(T t) { T x; }
@@ -2458,7 +2436,7 @@ void test1891()
}
/***************************************************/
-// Bugzilla 4291
+// https://issues.dlang.org/show_bug.cgi?id=4291
void test117() pure
{
@@ -2474,7 +2452,7 @@ template declareFunction()
}
/***************************************************/
-// Bugzilla 4177
+// https://issues.dlang.org/show_bug.cgi?id=4177
pure real log118(real x) {
if (__ctfe)
@@ -2510,7 +2488,7 @@ pure void test120()
}
/***************************************************/
-// 4866
+// https://issues.dlang.org/show_bug.cgi?id=4866
immutable int[3] statik = [ 1, 2, 3 ];
enum immutable(int)[] dynamic = statik;
@@ -2523,7 +2501,7 @@ static if (! is(typeof(dynamic) == immutable(int)[]))
pragma(msg, "!! ", typeof(dynamic));
/***************************************************/
-// 2943
+// https://issues.dlang.org/show_bug.cgi?id=2943
struct Foo2943
{
@@ -2545,7 +2523,7 @@ void test122()
}
/***************************************************/
-// 4641
+// https://issues.dlang.org/show_bug.cgi?id=4641
struct S123 {
int i;
@@ -2558,7 +2536,7 @@ void test123() {
}
/***************************************************/
-// 2451
+// https://issues.dlang.org/show_bug.cgi?id=2451
struct Foo124 {
int z = 3;
@@ -2585,21 +2563,6 @@ void test124() {
/***************************************************/
-void test3022()
-{
- static class Foo3022
- {
- new(size_t)
- {
- assert(0);
- }
- }
-
- scope x = new Foo3022;
-}
-
-/***************************************************/
-
void doNothing() {}
void bug5071(short d, ref short c) {
@@ -2681,7 +2644,7 @@ alias const MyInt4434[3] IceConstInt4434;
alias immutable string[] Bug4830;
/***************************************************/
-// 4254
+// https://issues.dlang.org/show_bug.cgi?id=4254
void bub(const inout int other) {}
@@ -2703,12 +2666,12 @@ void bug4915c()
}
/***************************************************/
-// 5164
+// https://issues.dlang.org/show_bug.cgi?id=5164
static if (is(int Q == int, Z...)) { }
/***************************************************/
-// 5195
+// https://issues.dlang.org/show_bug.cgi?id=5195
alias typeof(foo5195) food5195;
const int * foo5195 = null;
@@ -2727,7 +2690,7 @@ void test5332() { auto x = var5332; }
}
/***************************************************/
-// 5191
+// https://issues.dlang.org/show_bug.cgi?id=5191
struct Foo129
{
@@ -2750,15 +2713,15 @@ void test129()
assert(foo.value == 5);
foo.add(2);
- writeln(foo.value);
+ printf("%d\n", foo.value);
assert(foo.value == 7);
foo.add(3);
- writeln(foo.value);
+ printf("%d\n", foo.value);
assert(foo.value == 10);
foo.add(3);
- writeln(foo.value);
+ printf("%d\n", foo.value);
assert(foo.value == 13);
void delegate (int) nothrow dg = &foo.add!(int);
@@ -2767,7 +2730,7 @@ void test129()
}
/***************************************************/
-// 6169
+// https://issues.dlang.org/show_bug.cgi?id=6169
auto ctfefunc6169() { return "{}"; }
enum ctfefptr6169 = &ctfefunc6169;
@@ -2813,7 +2776,7 @@ void test6169() pure @safe
}
/***************************************************/
-// 10506
+// https://issues.dlang.org/show_bug.cgi?id=10506
void impureFunc10506() {}
string join10506(RoR)(RoR ror)
@@ -2847,7 +2810,7 @@ immutable struct S3598
}
/***************************************************/
-// 4211
+// https://issues.dlang.org/show_bug.cgi?id=4211
@safe struct X130
{
@@ -2885,7 +2848,7 @@ alias Return!( __traits(getOverloads, I4217, "square")[1] ) S4217;
static assert(! is(R4217 == S4217));
/***************************************************/
-// 5094
+// https://issues.dlang.org/show_bug.cgi?id=5094
void test131()
{
@@ -2913,7 +2876,7 @@ void test7545()
}
/***************************************************/
-// 5020
+// https://issues.dlang.org/show_bug.cgi?id=5020
void test132()
{
@@ -2928,7 +2891,7 @@ struct S132
}
/***************************************************/
-// 5343
+// https://issues.dlang.org/show_bug.cgi?id=5343
struct Tuple5343(Specs...)
{
@@ -2944,7 +2907,7 @@ alias Tuple5343!(A5343) TA5343;
alias S5343!(A5343) SA5343;
/***************************************************/
-// 5365
+// https://issues.dlang.org/show_bug.cgi?id=5365
interface IFactory
{
@@ -2987,7 +2950,7 @@ void test133()
}
/***************************************************/
-// 5365
+// https://issues.dlang.org/show_bug.cgi?id=5365
class B134
{
@@ -3020,7 +2983,7 @@ void test134()
}
/***************************************************/
-// 5025
+// https://issues.dlang.org/show_bug.cgi?id=5025
struct S135 {
void delegate() d;
@@ -3034,7 +2997,7 @@ void test135()
}
/***************************************************/
-// 5545
+// https://issues.dlang.org/show_bug.cgi?id=5545
bool enforce136(bool value, lazy const(char)[] msg = null) {
if(!value) {
@@ -3052,7 +3015,7 @@ struct Perm {
foreach(elem; input) {
enforce136(i < 3);
perm[i++] = elem;
- std.stdio.stderr.writeln(i); // Never gets incremented. Stays at 0.
+ printf("%d\n", i); // Never gets incremented. Stays at 0.
}
}
}
@@ -3060,12 +3023,12 @@ struct Perm {
void test136() {
byte[] stuff = [0, 1, 2];
auto perm2 = Perm(stuff);
- writeln(perm2.perm); // Prints [2, 0, 0]
+ //writeln(perm2.perm); // Prints [2, 0, 0]
assert(perm2.perm[] == [0, 1, 2]);
}
/***************************************************/
-// 4097
+// https://issues.dlang.org/show_bug.cgi?id=4097
void foo4097() { }
alias typeof(&foo4097) T4097;
@@ -3074,7 +3037,7 @@ static assert(is(T4097 X : X*) && is(X == function));
static assert(!is(X));
/***************************************************/
-// 5798
+// https://issues.dlang.org/show_bug.cgi?id=5798
void assign9(ref int lhs) pure {
lhs = 9;
@@ -3099,7 +3062,7 @@ int test137(){
/***************************************************/
-// 9366
+// https://issues.dlang.org/show_bug.cgi?id=9366
static assert(!is(typeof((void[]).init ~ cast(void)0)));
static assert(!is(typeof(cast(void)0 ~ (void[]).init)));
@@ -3146,7 +3109,8 @@ void test3822()
/***************************************************/
-// 5939, 5940
+// https://issues.dlang.org/show_bug.cgi?id=5939
+// https://issues.dlang.org/show_bug.cgi?id=5940
template map(fun...)
{
@@ -3176,7 +3140,7 @@ void test139()
/***************************************************/
-// 5966
+// https://issues.dlang.org/show_bug.cgi?id=5966
string[] foo5966(string[] a)
{
@@ -3187,7 +3151,7 @@ string[] foo5966(string[] a)
enum var5966 = foo5966([""]);
/***************************************************/
-// 5975
+// https://issues.dlang.org/show_bug.cgi?id=5975
int foo5975(wstring replace)
{
@@ -3199,7 +3163,7 @@ int foo5975(wstring replace)
enum X5975 = foo5975("X"w);
/***************************************************/
-// 5965
+// https://issues.dlang.org/show_bug.cgi?id=5965
template mapx(fun...) if (fun.length >= 1)
{
@@ -3232,7 +3196,7 @@ void bug5976()
}
/***************************************************/
-// 5771
+// https://issues.dlang.org/show_bug.cgi?id=5771
struct S141
{
@@ -3253,7 +3217,7 @@ class test5498_C : test5498_A {}
static assert(is(typeof([test5498_B.init, test5498_C.init]) == test5498_A[]));
/***************************************************/
-// 3688
+// https://issues.dlang.org/show_bug.cgi?id=3688
struct S142
{
@@ -3290,7 +3254,7 @@ void test142()
}
/***************************************************/
-// 6072
+// https://issues.dlang.org/show_bug.cgi?id=6072
static assert({
if (int x = 5) {}
@@ -3298,7 +3262,7 @@ static assert({
}());
/***************************************************/
-// 5959
+// https://issues.dlang.org/show_bug.cgi?id=5959
int n;
@@ -3318,7 +3282,7 @@ void test143()
}
/***************************************************/
-// 6119
+// https://issues.dlang.org/show_bug.cgi?id=6119
void startsWith(alias pred) () if (is(typeof(pred('c', 'd')) : bool))
{
@@ -3349,18 +3313,18 @@ void test146()
}
/***************************************************/
-// 5856
+// https://issues.dlang.org/show_bug.cgi?id=5856
struct X147
{
- void f() { writeln("X.f mutable"); }
- void f() const { writeln("X.f const"); }
+ void f() { printf("X.f mutable\n"); }
+ void f() const { printf("X.f const\n"); }
- void g()() { writeln("X.g mutable"); }
- void g()() const { writeln("X.g const"); }
+ void g()() { printf("X.g mutable\n"); }
+ void g()() const { printf("X.g const\n"); }
- void opOpAssign(string op)(int n) { writeln("X+= mutable"); }
- void opOpAssign(string op)(int n) const { writeln("X+= const"); }
+ void opOpAssign(string op)(int n) { printf("X+= mutable\n"); }
+ void opOpAssign(string op)(int n) const { printf("X+= const\n"); }
}
void test147()
@@ -3465,7 +3429,7 @@ void test13182()
}
/***************************************************/
-// 5897
+// https://issues.dlang.org/show_bug.cgi?id=5897
struct A148{ int n; }
struct B148{
@@ -3493,7 +3457,7 @@ void test148()
}
/***************************************************/
-// 4969
+// https://issues.dlang.org/show_bug.cgi?id=4969
class MyException : Exception
{
@@ -3519,7 +3483,7 @@ void cantthrow() nothrow
}
/***************************************************/
-// 2356
+// https://issues.dlang.org/show_bug.cgi?id=2356
void test2356()
{
@@ -3565,7 +3529,7 @@ void test2356()
}
/***************************************************/
-// 13652
+// https://issues.dlang.org/show_bug.cgi?id=13652
void test13652()
{
@@ -3633,7 +3597,7 @@ void test13652()
}
/***************************************************/
-// 11238
+// https://issues.dlang.org/show_bug.cgi?id=11238
void test11238()
{
@@ -3676,18 +3640,18 @@ class A2540
class B2540 : A2540
{
int b;
- override super.X foo() { return 1; }
+ override typeof(super).X foo() { return 1; }
- alias this athis;
- alias this.b thisb;
- alias super.a supera;
- alias super.foo superfoo;
- alias this.foo thisfoo;
+ alias typeof(this) athis;
+ alias typeof(this).b thisb;
+ alias typeof(super).a supera;
+ alias typeof(super).foo superfoo;
+ alias typeof(this).foo thisfoo;
}
struct X2540
{
- alias this athis;
+ alias typeof(this) athis;
}
void test2540()
@@ -3726,12 +3690,12 @@ B14348 test14348()
}
/***************************************************/
-// 7295
+// https://issues.dlang.org/show_bug.cgi?id=7295
struct S7295
{
int member;
- @property ref int refCountedPayload() { return member; }
+ @property ref int refCountedPayload() return { return member; }
alias refCountedPayload this;
}
@@ -3745,24 +3709,20 @@ void bar7295() pure
}
/***************************************************/
-// 5659
+// https://issues.dlang.org/show_bug.cgi?id=5659
void test149()
{
- import std.traits;
-
char a;
immutable(char) b;
static assert(is(typeof(true ? a : b) == const(char)));
static assert(is(typeof([a, b][0]) == const(char)));
-
- static assert(is(CommonType!(typeof(a), typeof(b)) == const(char)));
}
/***************************************************/
-// 1373
+// https://issues.dlang.org/show_bug.cgi?id=1373
void func1373a(){}
@@ -3815,7 +3775,7 @@ nothrow void test151()
@property int eoo() { return 1; }
@property auto ref hoo(int i) { return i; }
-// 3359
+// https://issues.dlang.org/show_bug.cgi?id=3359
int goo(int i) pure { return i; }
auto ioo(int i) pure { return i; }
@@ -3829,7 +3789,7 @@ class A152 {
auto eoo(int i) shared { return i; }
}
-// 4706
+// https://issues.dlang.org/show_bug.cgi?id=4706
struct Foo152(T) {
@property auto ref front() {
@@ -3846,7 +3806,7 @@ void test152() {
}
/***************************************************/
-// 6733
+// https://issues.dlang.org/show_bug.cgi?id=6733
void bug6733(int a, int b) pure nothrow { }
void test6733() {
@@ -3856,7 +3816,7 @@ void test6733() {
}
/***************************************************/
-// 3799
+// https://issues.dlang.org/show_bug.cgi?id=3799
void test153()
{
@@ -3868,7 +3828,7 @@ void test153()
}
/***************************************************/
-// 3632
+// https://issues.dlang.org/show_bug.cgi?id=3632
void test154() {
@@ -3905,7 +3865,7 @@ void test6545()
}
/***************************************************/
-// 3147
+// https://issues.dlang.org/show_bug.cgi?id=3147
void test155()
@@ -3924,7 +3884,7 @@ void test155()
}
/***************************************************/
-// 2486
+// https://issues.dlang.org/show_bug.cgi?id=2486
void test2486()
{
@@ -3932,7 +3892,7 @@ void test2486()
int[] arr = [1,2,3];
foo(arr); //OK
- static assert(!__traits(compiles, foo(arr[1..2]))); // should be NG
+ static assert(__traits(compiles, foo(arr[1..2])));
struct S
{
@@ -3943,7 +3903,7 @@ void test2486()
s[];
// opSlice should return rvalue
static assert(is(typeof(&S.opSlice) == int[] function() pure nothrow @nogc @safe));
- static assert(!__traits(compiles, foo(s[]))); // should be NG
+ static assert(__traits(compiles, foo(s[])));
}
/***************************************************/
@@ -3963,7 +3923,7 @@ void test15080()
}
/***************************************************/
-// 2521
+// https://issues.dlang.org/show_bug.cgi?id=2521
immutable int val = 23;
const int val2 = 23;
@@ -4011,7 +3971,7 @@ void test5554()
}
/***************************************************/
-// 5962
+// https://issues.dlang.org/show_bug.cgi?id=5962
struct S156
{
@@ -4044,7 +4004,7 @@ void test6708(const ref int y)
}
/***************************************************/
-// 4258
+// https://issues.dlang.org/show_bug.cgi?id=4258
struct Vec4258 {
Vec4258 opOpAssign(string Op)(auto ref Vec4258 other) if (Op == "+") {
@@ -4097,7 +4057,7 @@ static assert(!is(typeof(Bar4258.init += 1)));
static assert(!is(typeof(1 + Baz4258.init)));
/***************************************************/
-// 4539
+// https://issues.dlang.org/show_bug.cgi?id=4539
void test4539()
{
@@ -4118,20 +4078,20 @@ void test4539()
assert(s[4] == 0x61);
}
- static assert(!__traits(compiles, foo1("hello")));
+ static assert(__traits(compiles, foo1("hello")));
static assert(!__traits(compiles, foo2("hello")));
static assert(!__traits(compiles, foo3("hello")));
// same as test68, 69, 70
foo4("hello");
- foo5(cast(ubyte[5])x"c3fcd3d761");
+ foo5(cast(ubyte[5])"\xc3\xfc\xd3\xd7\x61");
//import std.conv;
//static assert(!__traits(compiles, parse!int("10") == 10));
}
/***************************************************/
-// 1471
+// https://issues.dlang.org/show_bug.cgi?id=1471
void test1471()
{
@@ -4147,14 +4107,6 @@ static assert(!is(typeof(bug6389 = bug6389)));
/***************************************************/
-void test10927()
-{
- static assert( (1+2i) ^^ 3 == -11 - 2i );
- auto a = (1+2i) ^^ 3;
-}
-
-/***************************************************/
-
void test4963()
{
struct Value {
@@ -4178,7 +4130,7 @@ pure int test4031()
}
/***************************************************/
-// 5437
+// https://issues.dlang.org/show_bug.cgi?id=5437
template EnumMembers5437(E)
{
@@ -4203,7 +4155,7 @@ void test5437()
}
/***************************************************/
-// 1962
+// https://issues.dlang.org/show_bug.cgi?id=1962
void test1962()
@@ -4213,12 +4165,12 @@ void test1962()
}
/***************************************************/
-// 6228
-
+// https://issues.dlang.org/show_bug.cgi?id=6228
void test6228()
{
- const(int)* ptr;
+ int val;
+ const(int)* ptr = &val;
const(int) temp;
auto x = (*ptr) ^^ temp;
}
@@ -4308,7 +4260,7 @@ void test6264()
}
/***************************************************/
-// 5046
+// https://issues.dlang.org/show_bug.cgi?id=5046
void test5046()
{
@@ -4328,7 +4280,7 @@ S5046!(p, T) makeS5046(alias p, T)()
}
/***************************************************/
-// 6335
+// https://issues.dlang.org/show_bug.cgi?id=6335
struct S6335
{
@@ -4428,7 +4380,7 @@ void test6293() {
}
/***************************************************/
-// 3733
+// https://issues.dlang.org/show_bug.cgi?id=3733
class C3733
{
@@ -4444,7 +4396,7 @@ void test3733()
}
/***************************************************/
-// 4392
+// https://issues.dlang.org/show_bug.cgi?id=4392
class C4392
{
@@ -4460,7 +4412,7 @@ void test4392()
}
/***************************************************/
-// 6220
+// https://issues.dlang.org/show_bug.cgi?id=6220
void test6220() {
struct Foobar { real x; real y; real z;}
@@ -4473,7 +4425,7 @@ void test6220() {
}
/***************************************************/
-// 5799
+// https://issues.dlang.org/show_bug.cgi?id=5799
void test5799()
{
@@ -4483,7 +4435,7 @@ void test5799()
}
/***************************************************/
-// 6529
+// https://issues.dlang.org/show_bug.cgi?id=6529
enum Foo6529 : char { A='a' }
ref const(Foo6529) func6529(const(Foo6529)[] arr){ return arr[0]; }
@@ -4536,7 +4488,7 @@ void test157()
}
/***************************************************/
-// 6473
+// https://issues.dlang.org/show_bug.cgi?id=6473
struct Eins6473
{
@@ -4634,7 +4586,7 @@ void test6578()
}
/***************************************************/
-// 6630
+// https://issues.dlang.org/show_bug.cgi?id=6630
void test6630()
{
@@ -4674,7 +4626,7 @@ void test199()
}
/***************************************************/
-// 6690
+// https://issues.dlang.org/show_bug.cgi?id=6690
T useLazy6690(T)(lazy T val)
{
@@ -4694,7 +4646,7 @@ template Hoge6691()
immutable static int[int] dict;
immutable static int value;
- static this()
+ shared static this()
{
dict = [1:1, 2:2];
value = 10;
@@ -4716,13 +4668,14 @@ void test10626()
double[2] a = v[] * ++z;
double[2] b = v[] * --z;
double[2] c = v[] * y.u;
- double[2] d = v[] * (x[] = 3, x[0]);
+ x[] = 3;
+ double[2] d = v[] * x[0];
double[2] e = v[] * (v[] ~ z)[0];
}
/***************************************************/
-// 2953
+// https://issues.dlang.org/show_bug.cgi?id=2953
template Tuple2953(T...)
{
@@ -4743,7 +4696,7 @@ void test2953()
}
/***************************************************/
-// 2997
+// https://issues.dlang.org/show_bug.cgi?id=2997
abstract class B2997 { void foo(); }
interface I2997 { void bar(); }
@@ -4758,7 +4711,7 @@ void test2997()
}
/***************************************************/
-// 6596
+// https://issues.dlang.org/show_bug.cgi?id=6596
extern (C) int function() pfunc6596;
extern (C) int cfunc6596(){ return 0; }
@@ -4767,7 +4720,7 @@ static assert(typeof(cfunc6596).stringof == "extern (C) int()");
/***************************************************/
-// 4423
+// https://issues.dlang.org/show_bug.cgi?id=4423
struct S4423
{
@@ -4813,7 +4766,7 @@ void test4423()
}
/***************************************************/
-// 4647
+// https://issues.dlang.org/show_bug.cgi?id=4647
interface Timer
{
@@ -4903,7 +4856,7 @@ void test1064()
}
/***************************************************/
-// 5696
+// https://issues.dlang.org/show_bug.cgi?id=5696
template Seq5696(T...){ alias T Seq5696; }
template Pred5696(T) { alias T Pred5696; } // TOKtemplate
@@ -4931,7 +4884,7 @@ void test5696()
}
/***************************************************/
-// 5933
+// https://issues.dlang.org/show_bug.cgi?id=5933
int dummyfunc5933();
alias typeof(dummyfunc5933) FuncType5933;
@@ -4962,7 +4915,7 @@ class C5933d { auto x() { return 0; } }
static assert(is(typeof(C5933d.x) == FuncType5933));
/***************************************************/
-// 6084
+// https://issues.dlang.org/show_bug.cgi?id=6084
template TypeTuple6084(T...){ alias T TypeTuple6084; }
void test6084()
@@ -4973,7 +4926,7 @@ void test6084()
}
/***************************************************/
-// 6763
+// https://issues.dlang.org/show_bug.cgi?id=6763
template TypeTuple6763(TList...)
{
@@ -4994,20 +4947,20 @@ void test6763()
f6763(0); //With D2: Error: function main.f ((ref const const(int) _param_0)) is not callable using argument types (int)
c6763(0);
- r6763(n); static assert(!__traits(compiles, r6763(0)));
+ r6763(n); static assert(__traits(compiles, r6763(0)));
i6763(0);
o6763(n); static assert(!__traits(compiles, o6763(0)));
- // 6755
+ // https://issues.dlang.org/show_bug.cgi?id=6755
static assert(typeof(f6763).stringof == "void(int _param_0)");
static assert(typeof(c6763).stringof == "void(const(int) _param_0)");
static assert(typeof(r6763).stringof == "void(ref int _param_0)");
- static assert(typeof(i6763).stringof == "void(const(int) _param_0)");
+ static assert(typeof(i6763).stringof == "void(in int _param_0)");
static assert(typeof(o6763).stringof == "void(out int _param_0)");
}
/***************************************************/
-// 6695
+// https://issues.dlang.org/show_bug.cgi?id=6695
struct X6695
{
@@ -5044,7 +4997,7 @@ struct X6695
}
/***************************************************/
-// 6087
+// https://issues.dlang.org/show_bug.cgi?id=6087
template True6087(T)
{
@@ -5061,7 +5014,7 @@ struct Bar6087
}
/***************************************************/
-// 6848
+// https://issues.dlang.org/show_bug.cgi?id=6848
class Foo6848 {}
@@ -5094,7 +5047,7 @@ else
static assert(!__traits(compiles, { cent x; }));
/***************************************************/
-// 6847
+// https://issues.dlang.org/show_bug.cgi?id=6847
template True6847(T)
{
@@ -5144,7 +5097,7 @@ void test6488()
}
/***************************************************/
-// 6565
+// https://issues.dlang.org/show_bug.cgi?id=6565
void foo6565(out int[2][2] m) {}
@@ -5156,7 +5109,7 @@ void test6565()
}
/***************************************************/
-// 6836
+// https://issues.dlang.org/show_bug.cgi?id=6836
template map6836(fun...) if (fun.length >= 1)
{
@@ -5194,7 +5147,7 @@ void test5448()
}
/***************************************************/
-// 6837
+// https://issues.dlang.org/show_bug.cgi?id=6837
struct Ref6837a(T)
{
@@ -5232,7 +5185,7 @@ void test6837()
}
/***************************************************/
-// 6927
+// https://issues.dlang.org/show_bug.cgi?id=6927
@property int[] foo6927()
{
@@ -5286,7 +5239,7 @@ struct Interval6753{ int a,b; }
}
/***************************************************/
-// 6859
+// https://issues.dlang.org/show_bug.cgi?id=6859
class Parent6859
{
@@ -5299,7 +5252,7 @@ public:
{
assert(isHage);
}
- body { }
+ do { }
}
class Child6859 : Parent6859
@@ -5322,7 +5275,7 @@ void test6859()
}
/***************************************************/
-// 6910
+// https://issues.dlang.org/show_bug.cgi?id=6910
template Test6910(alias i, B)
{
@@ -5371,7 +5324,7 @@ void fun12503()
b = null;
return;
}
- catch
+ catch(Throwable)
{
}
}
@@ -5387,7 +5340,7 @@ void test12503()
}
/***************************************************/
-// 6902
+// https://issues.dlang.org/show_bug.cgi?id=6902
void test6902()
{
@@ -5407,7 +5360,7 @@ void test6902()
}
/***************************************************/
-// 6330
+// https://issues.dlang.org/show_bug.cgi?id=6330
struct S6330
{
@@ -5444,7 +5397,7 @@ void test8269()
}
/***************************************************/
-// 5311
+// https://issues.dlang.org/show_bug.cgi?id=5311
class C5311
{
@@ -5487,7 +5440,7 @@ static void breaksPure5311b(S5311 x) pure
}
/***************************************************/
-// 6868
+// https://issues.dlang.org/show_bug.cgi?id=6868
@property bool empty6868(T)(in T[] a) @safe pure nothrow
{
@@ -5504,7 +5457,7 @@ void test6868()
}
/***************************************************/
-// 2856
+// https://issues.dlang.org/show_bug.cgi?id=2856
struct foo2856 { static void opIndex(int i) { printf("foo\n"); } }
struct bar2856(T) { static void opIndex(int i) { printf("bar\n"); } }
@@ -5539,7 +5492,7 @@ void test13947()
}
/***************************************************/
-// 3091
+// https://issues.dlang.org/show_bug.cgi?id=3091
void test3091(inout int = 0)
{
@@ -5563,7 +5516,7 @@ void test3091(inout int = 0)
}
/***************************************************/
-// 6837
+// https://issues.dlang.org/show_bug.cgi?id=6837
template Id6837(T)
{
@@ -5573,7 +5526,7 @@ static assert(is(Id6837!(shared const int) == shared const int));
static assert(is(Id6837!(shared inout int) == shared inout int));
/***************************************************/
-// 6056 fixup
+// https://issues.dlang.org/show_bug.cgi?id=6056 fixup
template ParameterTypeTuple6056(func)
{
@@ -5599,7 +5552,7 @@ void test6056()
}
/***************************************************/
-// 6356
+// https://issues.dlang.org/show_bug.cgi?id=6356
int f6356()(int a)
{
@@ -5619,13 +5572,13 @@ void test6356()
}
/***************************************************/
-// 7108
+// https://issues.dlang.org/show_bug.cgi?id=7108
static assert(!__traits(hasMember, int, "x"));
static assert( __traits(hasMember, int, "init"));
/***************************************************/
-// 7073
+// https://issues.dlang.org/show_bug.cgi?id=7073
void test7073()
{
@@ -5636,7 +5589,7 @@ void test7073()
}
/***************************************************/
-// 7104
+// https://issues.dlang.org/show_bug.cgi?id=7104
void test7104()
{
@@ -5645,7 +5598,7 @@ void test7104()
}
/***************************************************/
-// 7150
+// https://issues.dlang.org/show_bug.cgi?id=7150
struct A7150
{
@@ -5669,7 +5622,7 @@ void test7150()
}
/***************************************************/
-// 7159
+// https://issues.dlang.org/show_bug.cgi?id=7159
alias void delegate() Void7159;
@@ -5683,7 +5636,7 @@ class HomeController7159 {
}
/***************************************************/
-// 7160
+// https://issues.dlang.org/show_bug.cgi?id=7160
class HomeController {
static if (false) {
@@ -5699,7 +5652,7 @@ void test7160()
{}
/***************************************************/
-// 7168
+// https://issues.dlang.org/show_bug.cgi?id=7168
void test7168()
{
@@ -5720,7 +5673,7 @@ void test7168()
}
/***************************************************/
-// 7170
+// https://issues.dlang.org/show_bug.cgi?id=7170
T to7170(T)(string x) { return 1; }
void test7170()
@@ -5730,7 +5683,7 @@ void test7170()
}
/***************************************************/
-// 7196
+// https://issues.dlang.org/show_bug.cgi?id=7196
auto foo7196(int x){return x;}
auto foo7196(double x){return x;}
@@ -5742,7 +5695,7 @@ void test7196()
}
/***************************************************/
-// 7285
+// https://issues.dlang.org/show_bug.cgi?id=7285
int[2] spam7285()
{
@@ -5759,7 +5712,7 @@ void test7285()
}
/***************************************************/
-// 14737
+// https://issues.dlang.org/show_bug.cgi?id=14737
void test14737()
{
@@ -5783,7 +5736,7 @@ void test14737()
}
/***************************************************/
-// 7321
+// https://issues.dlang.org/show_bug.cgi?id=7321
void test7321()
{
@@ -5810,13 +5763,13 @@ class B158 : A158
}
/***************************************************/
-// 9231
+// https://issues.dlang.org/show_bug.cgi?id=9231
class B9231 { void foo() inout pure {} }
class D9231 : B9231 { override void foo() inout {} }
/***************************************************/
-// 3282
+// https://issues.dlang.org/show_bug.cgi?id=3282
class Base3282
{
@@ -5848,7 +5801,7 @@ void test3282()
}
/***************************************************/
-// 7534
+// https://issues.dlang.org/show_bug.cgi?id=7534
class C7534
{
@@ -5877,7 +5830,8 @@ void test7534()
}
/***************************************************/
-// 7534 + return type covariance
+// https://issues.dlang.org/show_bug.cgi?id=7534
+// return type covariance
class X7534 {}
class Y7534 : X7534
@@ -5913,7 +5867,7 @@ void test7534cov()
}
/***************************************************/
-// 7562
+// https://issues.dlang.org/show_bug.cgi?id=7562
static struct MyInt
{
@@ -5946,7 +5900,7 @@ void test13427(void* buffer = alloca(100))
}
/***************************************************/
-// 7583
+// https://issues.dlang.org/show_bug.cgi?id=7583
template Tup7583(E...) { alias E Tup7583; }
@@ -5966,7 +5920,7 @@ int bug7583() {
static assert (bug7583());
/***************************************************/
-// 7618
+// https://issues.dlang.org/show_bug.cgi?id=7618
void test7618(const int x = 1)
{
@@ -5984,7 +5938,7 @@ void test7618(const int x = 1)
}
/***************************************************/
-// 7621
+// https://issues.dlang.org/show_bug.cgi?id=7621
void test7621()
{
@@ -5996,7 +5950,7 @@ void test7621()
}
/***************************************************/
-// 7682
+// https://issues.dlang.org/show_bug.cgi?id=7682
template ConstOf7682(T)
{
@@ -6017,7 +5971,7 @@ void test7682()
}
/***************************************************/
-// 7735
+// https://issues.dlang.org/show_bug.cgi?id=7735
void a7735(void[][] data...)
{
@@ -6055,7 +6009,7 @@ struct A7823 {
void test7823(A7823 a = A7823.b) { }
/***************************************************/
-// 7871
+// https://issues.dlang.org/show_bug.cgi?id=7871
struct Tuple7871
{
@@ -6076,7 +6030,7 @@ void test7871()
}
/***************************************************/
-// 7906
+// https://issues.dlang.org/show_bug.cgi?id=7906
void test7906()
{
@@ -6084,7 +6038,7 @@ void test7906()
}
/***************************************************/
-// 7907
+// https://issues.dlang.org/show_bug.cgi?id=7907
template Id7907(E)
{
@@ -6101,7 +6055,7 @@ void test7907()
}
/***************************************************/
-// 1175
+// https://issues.dlang.org/show_bug.cgi?id=1175
class A1175
{
@@ -6116,28 +6070,7 @@ class B1175 : A1175
}
/***************************************************/
-// 7983
-
-class A7983 {
- void f() {
- g7983(this);
- }
- unittest {
- }
-}
-
-void g7983(T)(T a)
-{
- foreach (name; __traits(allMembers, T)) {
- pragma(msg, name);
- static if (__traits(compiles, &__traits(getMember, a, name)))
- {
- }
- }
-}
-
-/***************************************************/
-// 8004
+// https://issues.dlang.org/show_bug.cgi?id=8004
void test8004()
{
@@ -6146,7 +6079,7 @@ void test8004()
}
/***************************************************/
-// 8064
+// https://issues.dlang.org/show_bug.cgi?id=8064
void test8064()
{
@@ -6159,7 +6092,7 @@ void test8064()
}
/***************************************************/
-// 8220
+// https://issues.dlang.org/show_bug.cgi?id=8220
void foo8220(int){}
static assert(!__traits(compiles, foo8220(typeof(0)))); // fail
@@ -6200,7 +6133,7 @@ void test159()
}
/***************************************************/
-// 8283
+// https://issues.dlang.org/show_bug.cgi?id=8283
struct Foo8283 {
this(long) { }
@@ -6222,7 +6155,7 @@ void test8283() {
/***************************************************/
-// 8395
+// https://issues.dlang.org/show_bug.cgi?id=8395
struct S8395
{
@@ -6238,7 +6171,7 @@ void test8395()
}
/***************************************************/
-// 5749
+// https://issues.dlang.org/show_bug.cgi?id=5749
void test5749()
{
@@ -6286,7 +6219,7 @@ void test5749()
}
/***************************************************/
-// 8396
+// https://issues.dlang.org/show_bug.cgi?id=8396
void test8396()
{
@@ -6380,7 +6313,7 @@ void test160()
}
/***************************************************/
-// 8437
+// https://issues.dlang.org/show_bug.cgi?id=8437
class Cgi8437
{
@@ -6394,7 +6327,7 @@ class Cgi8437
}
/***************************************************/
-// 8665
+// https://issues.dlang.org/show_bug.cgi?id=8665
auto foo8665a(bool val)
{
@@ -6441,7 +6374,7 @@ void test8108()
}
/***************************************************/
-// 8360
+// https://issues.dlang.org/show_bug.cgi?id=8360
struct Foo8360
{
@@ -6482,7 +6415,7 @@ void test8360()
}
/***************************************************/
-// 8361
+// https://issues.dlang.org/show_bug.cgi?id=8361
struct Foo8361
{
@@ -6496,7 +6429,8 @@ void test8361()
}
/***************************************************/
-// 6141 + 8526
+// https://issues.dlang.org/show_bug.cgi?id=6141
+// https://issues.dlang.org/show_bug.cgi?id=8526
void test6141()
{
@@ -6569,7 +6503,7 @@ void test161()
}
/***************************************************/
-// 7175
+// https://issues.dlang.org/show_bug.cgi?id=7175
void test7175()
{
@@ -6580,7 +6514,7 @@ void test7175()
}
/***************************************************/
-// 8819
+// https://issues.dlang.org/show_bug.cgi?id=8819
void test8819()
{
@@ -6603,7 +6537,7 @@ void test8819()
}
/***************************************************/
-// 8897
+// https://issues.dlang.org/show_bug.cgi?id=8897
class C8897
{
@@ -6614,7 +6548,7 @@ class C8897
template M8897 ( E ) { }
/***************************************************/
-// 8917
+// https://issues.dlang.org/show_bug.cgi?id=8917
void test8917()
{
@@ -6624,7 +6558,7 @@ void test8917()
}
/***************************************************/
-// 8945
+// https://issues.dlang.org/show_bug.cgi?id=8945
struct S8945 // or `class`, or `union`
{
@@ -6746,7 +6680,7 @@ struct X164()
/***************************************************/
-// 9428
+// https://issues.dlang.org/show_bug.cgi?id=9428
void test9428()
{
@@ -6783,7 +6717,7 @@ void test9428()
}
/***************************************************/
-// 9477
+// https://issues.dlang.org/show_bug.cgi?id=9477
template Tuple9477(T...) { alias T Tuple9477; }
template Select9477(bool b, T, U) { static if (b) alias T Select9477; else alias U Select9477; }
@@ -6808,7 +6742,7 @@ void test9477()
foreach (b1; Tuple9477!(false, true))
foreach (b2; Tuple9477!(false, true))
{
- version (D_PIC) {} else // Work around http://d.puremagic.com/issues/show_bug.cgi?id=9754
+ version (D_PIC) {} else version (D_PIE) {} else // Work around http://d.puremagic.com/issues/show_bug.cgi?id=9754
{
assert( isEq (cast(Select9477!(b1, string, char[0]))"" , cast(Select9477!(b2, string, char[0]))"" ));
assert(!isNeq(cast(Select9477!(b1, string, char[0]))"" , cast(Select9477!(b2, string, char[0]))"" ));
@@ -6853,7 +6787,7 @@ void test9477()
}
/***************************************************/
-// 9504
+// https://issues.dlang.org/show_bug.cgi?id=9504
struct Bar9504
{
@@ -6884,7 +6818,7 @@ Bar9504 test9504()
}
/***************************************************/
-// 9538
+// https://issues.dlang.org/show_bug.cgi?id=9538
void test9538()
{
@@ -6893,7 +6827,7 @@ void test9538()
}
/***************************************************/
-// 9539
+// https://issues.dlang.org/show_bug.cgi?id=9539
void test9539()
{
@@ -6911,7 +6845,7 @@ void test9539()
}
/***************************************************/
-// 9700
+// https://issues.dlang.org/show_bug.cgi?id=9700
mixin template Proxy9700(alias a)
{
@@ -6931,7 +6865,7 @@ void test9700()
}
/***************************************************/
-// 9834
+// https://issues.dlang.org/show_bug.cgi?id=9834
struct Event9834
{
@@ -6955,7 +6889,7 @@ void test9834()
}
/***************************************************/
-// 9859
+// https://issues.dlang.org/show_bug.cgi?id=9859
void test9859(inout int[] arr)
{
@@ -6999,7 +6933,7 @@ void test9859(inout int[] arr)
}
/***************************************************/
-// 9912
+// https://issues.dlang.org/show_bug.cgi?id=9912
template TypeTuple9912(Stuff...)
{
@@ -7021,7 +6955,7 @@ struct S9912
}
/***************************************************/
-// 9883
+// https://issues.dlang.org/show_bug.cgi?id=9883
struct S9883
{
@@ -7041,7 +6975,7 @@ void test9883()
/***************************************************/
-// 10091
+// https://issues.dlang.org/show_bug.cgi?id=10091
struct S10091
{
@@ -7064,7 +6998,7 @@ label:
}
/***************************************************/
-// 9130
+// https://issues.dlang.org/show_bug.cgi?id=9130
class S9130 { void bar() { } }
@@ -7078,7 +7012,7 @@ struct Function
@property void meta(alias m)()
{
static Function md;
- printf("length = %d\n", md.ai.length);
+ printf("length = %zd\n", md.ai.length);
printf("ptr = %p\n", md.ai.ptr);
md.ai[0] = 0;
}
@@ -7090,14 +7024,14 @@ void test9130()
}
/***************************************************/
-// 10390
+// https://issues.dlang.org/show_bug.cgi?id=10390
class C10390 { this() { this.c = this; } C10390 c; }
const c10390 = new C10390();
pragma(msg, c10390);
/***************************************************/
-// 10542
+// https://issues.dlang.org/show_bug.cgi?id=10542
class B10542
{
@@ -7114,7 +7048,7 @@ void test10542() nothrow pure @safe
}
/***************************************************/
-// 10539
+// https://issues.dlang.org/show_bug.cgi?id=10539
void test10539()
{
@@ -7193,7 +7127,7 @@ void test11075()
}
/***************************************************/
-// 11181
+// https://issues.dlang.org/show_bug.cgi?id=11181
void test11181()
{
@@ -7207,7 +7141,7 @@ void test11181()
}
/***************************************************/
-// 11317
+// https://issues.dlang.org/show_bug.cgi?id=11317
void test11317()
{
@@ -7217,13 +7151,13 @@ void test11317()
}
void test(ref uint x) {}
- static assert(!__traits(compiles, test(fun())));
+ static assert(__traits(compiles, test(fun())));
assert(fun() == 0);
}
/***************************************************/
-// 11888
+// https://issues.dlang.org/show_bug.cgi?id=11888
void test11888()
{
@@ -7248,7 +7182,7 @@ void test11888()
}
/***************************************************/
-// 12036
+// https://issues.dlang.org/show_bug.cgi?id=12036
template T12036(alias a)
{
@@ -7268,7 +7202,7 @@ void test12036()
}
/***************************************************/
-// 12153
+// https://issues.dlang.org/show_bug.cgi?id=12153
void test12153()
{
@@ -7284,7 +7218,7 @@ void test12153()
}
/***************************************************/
-// 12498
+// https://issues.dlang.org/show_bug.cgi?id=12498
string a12498()
{
@@ -7301,7 +7235,7 @@ void test12498()
}
/***************************************************/
-// 12900
+// https://issues.dlang.org/show_bug.cgi?id=12900
struct A12900
{
@@ -7316,7 +7250,22 @@ void test12900()
}
/***************************************************/
-// 12937
+// https://issues.dlang.org/show_bug.cgi?id=12929
+
+struct Foo12929
+{
+ union { }
+ int var;
+}
+
+struct Bar12929
+{
+ struct { }
+ int var;
+}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=12937
void test12937()
{
@@ -7325,7 +7274,7 @@ void test12937()
}
/***************************************************/
-// 13154
+// https://issues.dlang.org/show_bug.cgi?id=13154
void test13154()
{
@@ -7341,7 +7290,7 @@ void test13154()
}
/***************************************************/
-// 13437
+// https://issues.dlang.org/show_bug.cgi?id=13437
ubyte[4] foo13437() { return [1,2,3,4]; }
@@ -7353,7 +7302,7 @@ void test13437()
}
/***************************************************/
-// 13472
+// https://issues.dlang.org/show_bug.cgi?id=13472
class A13472
{
@@ -7369,7 +7318,7 @@ void test13472()
}
/***************************************************/
-// 13476
+// https://issues.dlang.org/show_bug.cgi?id=13476
template ParameterTypeTuple13476(func...)
{
@@ -7416,16 +7365,16 @@ void test13476()
}
/***************************************************/
-// 14038
+// https://issues.dlang.org/show_bug.cgi?id=14038
static immutable ubyte[string] wordsAA14038;
-static this()
+shared static this()
{
wordsAA14038["zero"] = 0;
}
/***************************************************/
-// 14192
+// https://issues.dlang.org/show_bug.cgi?id=14192
void test14192()
{
@@ -7434,7 +7383,7 @@ void test14192()
}
/***************************************************/
-// 13720
+// https://issues.dlang.org/show_bug.cgi?id=13720
struct FracSec13720
{
@@ -7461,7 +7410,7 @@ void test13720()
}
/***************************************************/
-// 13952
+// https://issues.dlang.org/show_bug.cgi?id=13952
struct Reg13952
{
@@ -7507,7 +7456,7 @@ void test13952()
}
/***************************************************/
-// 14165
+// https://issues.dlang.org/show_bug.cgi?id=14165
class Foo14165
{
@@ -7516,7 +7465,7 @@ class Foo14165
}
/***************************************************/
-// 13985
+// https://issues.dlang.org/show_bug.cgi?id=13985
interface I13985
{
@@ -7552,7 +7501,7 @@ void test13985()
}
/***************************************************/
-// 14211
+// https://issues.dlang.org/show_bug.cgi?id=14211
extern(C++) // all derived classes won't have invariants
class B14211
@@ -7574,7 +7523,7 @@ void test14211()
}
/***************************************************/
-// 14552
+// https://issues.dlang.org/show_bug.cgi?id=14552
template map14552(fun...)
{
@@ -7608,7 +7557,7 @@ class Outer14552
}
/***************************************************/
-// 14853
+// https://issues.dlang.org/show_bug.cgi?id=14853
struct Queue14853(T)
{
@@ -7641,7 +7590,7 @@ void test14853()
}
/********************************************************/
-// 15045
+// https://issues.dlang.org/show_bug.cgi?id=15045
void test15045()
{
@@ -7700,7 +7649,7 @@ void test15045()
}
/***************************************************/
-// 15116
+// https://issues.dlang.org/show_bug.cgi?id=15116
alias TypeTuple15116(T...) = T;
@@ -7722,7 +7671,7 @@ void test15116()
}
/***************************************************/
-// 15117
+// https://issues.dlang.org/show_bug.cgi?id=15117
template Mix15117()
{
@@ -7737,7 +7686,7 @@ struct S15117
}
/***************************************************/
-// 15126
+// https://issues.dlang.org/show_bug.cgi?id=15126
struct Json15126
{
@@ -7754,7 +7703,7 @@ template isCustomSerializable15126(T)
alias bug15126 = isCustomSerializable15126!Json15126;
/***************************************************/
-// 15141
+// https://issues.dlang.org/show_bug.cgi?id=15141
class A15141
{
@@ -7772,7 +7721,7 @@ void test15141()
}
/***************************************************/
-// 15366
+// https://issues.dlang.org/show_bug.cgi?id=15366
enum E15366 : bool { A, B };
@@ -7791,7 +7740,7 @@ struct S15366
}
/***************************************************/
-// 15369
+// https://issues.dlang.org/show_bug.cgi?id=15369
struct MsgTable15369
{
@@ -7856,7 +7805,7 @@ void test15638()
}
/***************************************************/
-// 15961
+// https://issues.dlang.org/show_bug.cgi?id=15961
struct SliceOverIndexed15961(T)
{
@@ -7877,13 +7826,26 @@ struct Grapheme15961
}
/***************************************************/
-// 16022
+// https://issues.dlang.org/show_bug.cgi?id=16022
bool test16022()
{
enum Type { Colon, Comma }
Type type;
- return type == Type.Colon, type == Type.Comma;
+ return type == Type.Comma;
+}
+
+bool test16022_structs()
+{
+ struct A
+ {
+ int i;
+ string s;
+ }
+
+ enum Type { Colon = A(0, "zero"), Comma = A(1, "one") }
+ Type type;
+ return type == Type.Comma;
}
/***************************************************/
@@ -7918,7 +7880,7 @@ void test16466()
real r;
}
real r;
- printf("S.alignof: %x, r.alignof: %x\n", S.alignof, r.alignof);
+ printf("S.alignof: %zx, r.alignof: %zx\n", S.alignof, r.alignof);
assert(S.alignof == r.alignof);
}
@@ -7991,13 +7953,30 @@ struct S17915(T)
T owner;
}
+void test18232()
+{
+ static struct Canary
+ {
+ int x = 0x900D_900D;
+ }
+ union U
+ {
+ Canary method()
+ {
+ Canary c;
+ return c;
+ }
+ }
+ U u;
+ assert(u.method() == Canary.init);
+}
+
/***************************************************/
int main()
{
test1();
test2();
- test3();
test4();
test5();
test6();
@@ -8214,7 +8193,7 @@ int main()
test6733();
test6813();
test6859();
- test3022();
+
test6910();
test6902();
test6330();
@@ -8312,6 +8291,7 @@ int main()
test16408();
test17349();
test17915();
+ test18232();
printf("Success\n");
return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/xtest46_gc.d b/gcc/testsuite/gdc.test/runnable/xtest46_gc.d
new file mode 100644
index 00000000000..14b82a00cd3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/xtest46_gc.d
@@ -0,0 +1,37 @@
+/*
+REQUIRED_ARGS: -lowmem -Jrunnable -preview=rvaluerefparam
+EXTRA_FILES: xtest46.d
+TEST_OUTPUT:
+---
+Boo!double
+Boo!int
+true
+int
+!! immutable(int)[]
+int(int i, long j = 7L)
+long
+C10390(C10390(<recursion>))
+tuple(height)
+tuple(get, get)
+tuple(clear)
+tuple(draw, draw)
+runnable/xtest46_gc.d-mixin-$n$(185): Deprecation: `opDot` is deprecated. Use `alias this`
+runnable/xtest46_gc.d-mixin-$n$(187): Deprecation: `opDot` is deprecated. Use `alias this`
+runnable/xtest46_gc.d-mixin-$n$(188): Deprecation: `opDot` is deprecated. Use `alias this`
+runnable/xtest46_gc.d-mixin-$n$(190): Deprecation: `opDot` is deprecated. Use `alias this`
+runnable/xtest46_gc.d-mixin-$n$(217): Deprecation: `opDot` is deprecated. Use `alias this`
+runnable/xtest46_gc.d-mixin-$n$(219): Deprecation: `opDot` is deprecated. Use `alias this`
+runnable/xtest46_gc.d-mixin-$n$(220): Deprecation: `opDot` is deprecated. Use `alias this`
+runnable/xtest46_gc.d-mixin-$n$(222): Deprecation: `opDot` is deprecated. Use `alias this`
+const(int)
+string[]
+double[]
+double[]
+{}
+tuple("m")
+true
+TFunction1: extern (C) void function()
+---
+*/
+
+mixin(import("xtest46.d"));
diff --git a/gcc/testsuite/gdc.test/runnable/xtest47.d b/gcc/testsuite/gdc.test/runnable/xtest47.d
new file mode 100644
index 00000000000..42cabaf9197
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/xtest47.d
@@ -0,0 +1,67 @@
+// PERMUTE_ARGS: -unittest
+/* TEST_OUTPUT:
+---
+f
+toString
+toHash
+opCmp
+opEquals
+Monitor
+factory
+---
+*/
+
+import core.stdc.stdio;
+
+/***************************************************/
+
+void test3()
+{
+ version (unittest)
+ {
+ printf("unittest!\n");
+ }
+ else
+ {
+ printf("no unittest!\n");
+ }
+
+ version (assert)
+ {
+ printf("assert!\n");
+ }
+ else
+ {
+ printf("no assert!\n");
+ }
+}
+
+/***************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=7983
+
+class A7983 {
+ void f() {
+ g7983(this);
+ }
+ unittest {
+ }
+}
+
+void g7983(T)(T a)
+{
+ foreach (name; __traits(allMembers, T)) {
+ pragma(msg, name);
+ static if (__traits(compiles, &__traits(getMember, a, name)))
+ {
+ }
+ }
+}
+
+/***************************************************/
+
+int main()
+{
+ test3();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/xtest55.d b/gcc/testsuite/gdc.test/runnable/xtest55.d
index 4b295d8c686..f976520cfa3 100644
--- a/gcc/testsuite/gdc.test/runnable/xtest55.d
+++ b/gcc/testsuite/gdc.test/runnable/xtest55.d
@@ -1,7 +1,6 @@
-// RUNNABLE_PHOBOS_TEST
// PERMUTE_ARGS:
-import core.memory, std.stdio;
+import core.memory, core.stdc.stdio;
Stuff* stuff1;
@@ -16,9 +15,9 @@ int main()
auto bar = new byte[1024 * 1024];
auto stuff2 = new Stuff;
stuff2.num = 2;
- writeln(stuff1, "\t", stuff2); // Same address.
+ printf("%p\t%p\n", stuff1, stuff2); // Same address.
assert(stuff1 != stuff2);
- writeln(stuff1.num, "\t", stuff2.num); // Both 2.
+ printf("%d\t%d\n", stuff1.num, stuff2.num); // Both 2.
assert(stuff1.num == 1);
return 0;
}
diff --git a/gcc/testsuite/gdc.test/runnable/xtestenum.d b/gcc/testsuite/gdc.test/runnable/xtestenum.d
index fa812a121f8..ce77782b88c 100644
--- a/gcc/testsuite/gdc.test/runnable/xtestenum.d
+++ b/gcc/testsuite/gdc.test/runnable/xtestenum.d
@@ -1,4 +1,11 @@
-// PERMUTE_ARGS:
+/*
+PERMUTE_ARGS:
+RUN_OUTPUT:
+---
+foo
+Success
+---
+*/
extern(C) int printf(const char*, ...);
@@ -45,7 +52,7 @@ enum E3 : string
void test3()
{
- printf("%.*s\n", E3.E3a.length, E3.E3a.ptr);
+ printf("%.*s\n", cast(int)E3.E3a.length, E3.E3a.ptr);
assert(E3.E3a == "foo");
assert(E3.E3b == "bar");
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/abi_tags.d b/gcc/testsuite/gdc.test/runnable_cxx/abi_tags.d
new file mode 100644
index 00000000000..80eead2dbe8
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/abi_tags.d
@@ -0,0 +1,139 @@
+/*
+ * Test C++ abi-tag name mangling.
+ * They are a C++11 feature required to bind `std::string`,
+ * introduced in G++ 5.1 / clang++ 3.9
+ * https://issues.dlang.org/show_bug.cgi?id=19949
+ *
+ * DISABLED: win32 win64
+ * REQUIRED_ARGS: -extern-std=c++11
+ * EXTRA_CPP_SOURCES: abi_tags.cpp
+ * CXXFLAGS: -std=c++11
+ */
+
+#line 100
+import core.attribute;
+
+alias Tuple(A...) = A;
+enum foo_bar = gnuAbiTag("foo", "bar");
+
+extern(C++)
+{
+ @gnuAbiTag("tag1") struct Tagged1 {}
+ @gnuAbiTag("tag2") struct Tagged2 {}
+ @gnuAbiTag("tag1") struct Tagged1Too {}
+
+ // Note: Outer tags do not propagate, unlike in C++
+ @gnuAbiTag("tag1", "tag2") struct Tagged1_2
+ {
+ @gnuAbiTag("tag1", "tag2", "tag3") struct Tagged3
+ {
+ // _ZN9Tagged1_2B4tag1B4tag27Tagged3B4tag37Tagged4B4tag4Ev
+ @gnuAbiTag("tag1", "tag2", "tag3", "tag4") int Tagged4 ();
+
+ int value;
+ }
+ }
+
+ extern __gshared Tagged1_2 inst1;
+ extern __gshared Tagged1_2.Tagged3 inst2;
+
+ Tagged1_2 func0(int a);
+ Tagged1_2 func1(Tagged1_2 a);
+ Tagged1_2 func2(Tagged1 a);
+ Tagged1_2 func3(Tagged2 a);
+ Tagged1_2 func4(Tagged2 a, Tagged1 b);
+ Tagged1_2.Tagged3 func5(Tagged2 a, Tagged1 b);
+ void func6(Tagged2 a, Tagged2 b, Tagged1 c, Tagged1_2 d);
+ T func7(T)(T a, int);
+ void func8 (Tagged1, Tagged1Too);
+
+ @foo_bar struct S
+ {
+ int i;
+ }
+
+ @foo_bar extern __gshared int a;
+
+ extern __gshared S b;
+
+ @foo_bar int f();
+
+ S gs(int);
+ S gss(S, int);
+
+ @foo_bar S fss(S, int);
+
+ T gt(T)(int);
+ T gtt(T)(T, int);
+
+ @foo_bar T ft(T)(int);
+
+ @foo_bar T ftt(T)(T, int);
+
+ @("abc") extern(C++, "N")
+ {
+ @gnuAbiTag("AAA", "foo")
+ template K(int i)
+ {
+ @gnuAbiTag("bar", "AAA", "foo")
+ struct K
+ {
+ int i;
+ this(int);
+ }
+ }
+ }
+
+ //K!i fk(int i)(int);
+ K!1 fk1(int);
+
+ extern __gshared K!10 k10;
+
+ @gnuAbiTag("ENN") enum E0 { a = 0xa, }
+ E0 fe();
+ E0 fei(int i)();
+
+ void initVars();
+}
+
+void main()
+{
+ inst1 = func0(42);
+ assert(inst2.value == 42);
+ inst2 = func5(Tagged2.init, Tagged1.init);
+ assert(inst2.value == 420);
+ func1(inst1);
+ func2(Tagged1.init);
+ func3(Tagged2.init);
+ func4(Tagged2.init, Tagged1.init);
+
+ func6(Tagged2.init, Tagged2.init, Tagged1.init, Tagged1_2.init);
+ func7(Tagged1_2.init, 42);
+ func8(Tagged1.init, Tagged1Too.init);
+
+ initVars();
+ assert(a == 10);
+ assert(b.i == 20);
+ assert(k10.i == 30);
+
+ assert(f() == 0xf);
+ assert(gs(1).i == 1+0xe0);
+ assert(gss(S(1), 1).i == 2+0xe0);
+ assert(fss(S(1), 1).i == 2+0xf);
+ assert(gt!S(1).i == 1+0xe0);
+ assert(gtt!S(S(1), 1).i == 2+0xe0);
+
+ // Bug: Template parameter tags get double mangled
+ version(none)
+ {
+ assert(ft!S(1).i == 1+0xf); // GCC inconsistent
+ assert(ftt!S(S(1), 1).i == 2+0xf); // GCC inconsistent
+ }
+ //assert(fk!0(1).i == 1+0xf);
+ assert(fk1(1).i == 2+0xf);
+ version(gcc6)
+ {
+ assert(fei!0() == E0.a); // GCC only
+ assert(fe() == E0.a); // GCC only
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/cpp11.d b/gcc/testsuite/gdc.test/runnable_cxx/cpp11.d
new file mode 100644
index 00000000000..6951dec8f70
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/cpp11.d
@@ -0,0 +1,70 @@
+// DISABLED: win32
+// REQUIRED_ARGS: -extern-std=c++11
+// EXTRA_CPP_SOURCES: cpp11.cpp
+// CXXFLAGS(osx linux freebsd openbsd netbsd dragonflybsd solaris): -std=c++11
+
+// Disabled on win32 because the compiler is too old
+
+/****************************************/
+alias nullptr_t = typeof(null);
+
+// Only run on OSX/Win64 because the compilers are too old
+// and nullptr_t gets substituted
+version (FreeBSD)
+ version = IgnoreNullptrTest;
+version (linux)
+ version = IgnoreNullptrTest;
+
+version (IgnoreNullptrTest) { void test17() {} }
+else
+{
+ extern (C++) void testnull(nullptr_t);
+ extern (C++) void testnullnull(nullptr_t, nullptr_t);
+
+ void test17()
+ {
+ testnull(null);
+ testnullnull(null, null);
+ }
+}
+
+/****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19658
+
+enum i8_19658 : byte { a }
+enum u8_19658 : ubyte { a }
+enum i16_19658 : short { a }
+enum u16_19658 : ushort { a }
+enum i32_19658 : int { a }
+enum u32_19658 : uint { a }
+enum i64_19658 : long { a }
+enum u64_19658 : ulong { a }
+
+extern(C++) void test19658_i8(i8_19658);
+extern(C++) void test19658_u8(u8_19658);
+extern(C++) void test19658_i16(i16_19658);
+extern(C++) void test19658_u16(u16_19658);
+extern(C++) void test19658_i32(i32_19658);
+extern(C++) void test19658_u32(u32_19658);
+extern(C++) void test19658_i64(i64_19658);
+extern(C++) void test19658_u64(u64_19658);
+
+void test19658()
+{
+ test19658_i8(i8_19658.a);
+ test19658_u8(u8_19658.a);
+ test19658_i16(i16_19658.a);
+ test19658_u16(u16_19658.a);
+ test19658_i32(i32_19658.a);
+ test19658_u32(u32_19658.a);
+ test19658_i64(i64_19658.a);
+ test19658_u64(u64_19658.a);
+}
+
+/****************************************/
+
+void main()
+{
+ test17();
+ test19658();
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/cpp_abi_tests.d b/gcc/testsuite/gdc.test/runnable_cxx/cpp_abi_tests.d
index 83e1cff10d9..c677d6e0be8 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/cpp_abi_tests.d
+++ b/gcc/testsuite/gdc.test/runnable_cxx/cpp_abi_tests.d
@@ -1,4 +1,14 @@
// EXTRA_CPP_SOURCES: cpp_abi_tests.cpp
+// CXXFLAGS(linux freebsd osx netbsd dragonflybsd): -std=c++11
+
+// N.B MSVC doesn't have a C++11 switch, but it defaults to the latest fully-supported standard
+// N.B MSVC 2013 doesn't support char16_t/char32_t
+
+version(Posix)
+ enum __c_wchar_t : dchar;
+else version(Windows)
+ enum __c_wchar_t : wchar;
+alias wchar_t = __c_wchar_t;
extern(C++) {
@@ -7,11 +17,43 @@ struct S
float a = 1;
}
+struct S18784
+{
+ int i;
+ this(int);
+}
+
+extern(C++, std)
+{
+ struct test19248_ {int a = 42;}
+}
+extern(C++, `std`)
+{
+ struct test19248 {int a = 34;}
+}
+
+struct Sdtor
+{
+ extern __gshared int counter;
+ ~this();
+}
+void consume(Sdtor);
+void consume2(Sdtor value){}
+void doConsume2(ref Sdtor);
+
+struct SPack(Args...)
+{
+ int i;
+}
+alias SInt = SPack!int;
+
bool passthrough(bool value);
byte passthrough(byte value);
ubyte passthrough(ubyte value);
char passthrough(char value);
+wchar passthrough(wchar value);
dchar passthrough(dchar value);
+wchar_t passthrough(wchar_t value);
short passthrough(short value);
ushort passthrough(ushort value);
int passthrough(int value);
@@ -21,12 +63,17 @@ ulong passthrough(ulong value);
float passthrough(float value);
double passthrough(double value);
S passthrough(S value);
+test19248 passthrough(const(test19248) value);
+std.test19248_ passthrough(const(std.test19248_) value);
+SInt passthrough(SInt value);
bool passthrough_ptr(bool *value);
byte passthrough_ptr(byte *value);
ubyte passthrough_ptr(ubyte *value);
char passthrough_ptr(char *value);
+wchar passthrough_ptr(wchar *value);
dchar passthrough_ptr(dchar *value);
+wchar_t passthrough_ptr(wchar_t *value);
short passthrough_ptr(short *value);
ushort passthrough_ptr(ushort *value);
int passthrough_ptr(int *value);
@@ -36,12 +83,17 @@ ulong passthrough_ptr(ulong *value);
float passthrough_ptr(float *value);
double passthrough_ptr(double *value);
S passthrough_ptr(S *value);
+test19248 passthrough_ptr(const(test19248)* value);
+std.test19248_ passthrough_ptr(const(std.test19248_)* value);
+SInt passthrough_ptr(SInt *value);
bool passthrough_ref(ref bool value);
byte passthrough_ref(ref byte value);
ubyte passthrough_ref(ref ubyte value);
char passthrough_ref(ref char value);
+wchar passthrough_ref(ref wchar value);
dchar passthrough_ref(ref dchar value);
+wchar_t passthrough_ref(ref wchar_t value);
short passthrough_ref(ref short value);
ushort passthrough_ref(ref ushort value);
int passthrough_ref(ref int value);
@@ -51,6 +103,9 @@ ulong passthrough_ref(ref ulong value);
float passthrough_ref(ref float value);
double passthrough_ref(ref double value);
S passthrough_ref(ref S value);
+test19248 passthrough_ref(ref const(test19248) value);
+std.test19248_ passthrough_ref(ref const(std.test19248_) value);
+SInt passthrough_ref(ref SInt value);
}
template IsSigned(T)
@@ -86,7 +141,7 @@ template IsBoolean(T)
template IsSomeChar(T)
{
- enum IsSomeChar = is(T==char) || is(T==dchar);
+ enum IsSomeChar = is(T==char) || is(T==wchar) || is(T==dchar) || is(T==wchar_t);
}
void check(T)(T actual, T expected)
@@ -138,13 +193,50 @@ T[] values(T)()
return values;
}
+extern(C++, `ns1`)
+ {
+ // C++: `const char*, const char**`
+ int constFunction1(const(char)*, const(char)**);
+ // C++: `const char*, const char* const*`
+ int constFunction2(const(char)*, const(char*)*);
+ // C++: `const char* const, const char* const* const*`
+ int constFunction3(const(char*), const(char**)*);
+ // C++: `const char* const, const char* const* const* const`
+ int constFunction4(const(char*), const(char***));
+}
+
+extern(C++)
+{
+ struct SmallStruct
+ {
+ int i;
+ this(int i) { this.i = i; }
+ this(ref const SmallStruct); // implemented in C++
+ }
+ void smallStructTest(SmallStruct p);
+ void smallStructCallBack(SmallStruct p)
+ {
+ assert(p.i == 62);
+ }
+}
+
void main()
{
foreach(bool val; values!bool()) check(val);
foreach(byte val; values!byte()) check(val);
foreach(ubyte val; values!ubyte()) check(val);
foreach(char val; values!char()) check(val);
+version(CppRuntime_DigitalMars){} else
+version(CppRuntime_Microsoft)
+{
+// TODO: figure out how to detect VS2013 which doesn't support char16_t/char32_t
+}
+else
+{
+ foreach(wchar val; values!wchar()) check(val);
foreach(dchar val; values!dchar()) check(val);
+}
+ foreach(wchar_t val; values!wchar_t()) check(val);
foreach(short val; values!short()) check(val);
foreach(ushort val; values!ushort()) check(val);
foreach(int val; values!int()) check(val);
@@ -154,4 +246,26 @@ void main()
foreach(float val; values!float()) check(val);
foreach(double val; values!double()) check(val);
check(S());
+ check(test19248());
+ check(std.test19248_());
+ check(SInt());
+
+ assert(constFunction1(null, null) == 1);
+ assert(constFunction2(null, null) == 2);
+ assert(constFunction3(null, null) == 3);
+ assert(constFunction4(null, null) == 42);
+
+ auto ss = SmallStruct(42);
+ smallStructTest(ss);
+ assert(ss.i == 42);
+ assert(S18784(1).i == 1);
+
+ {
+ Sdtor sd;
+ assert(Sdtor.counter == 0);
+ consume(sd);
+ assert(Sdtor.counter == 1);
+ doConsume2(sd);
+ assert(Sdtor.counter == 2);
+ }
}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/cpp_stdlib.d b/gcc/testsuite/gdc.test/runnable_cxx/cpp_stdlib.d
new file mode 100644
index 00000000000..dd7bb11fc64
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/cpp_stdlib.d
@@ -0,0 +1,58 @@
+// DISABLED: win32 win64
+// EXTRA_CPP_SOURCES: cpp_stdlib.cpp
+// CXXFLAGS(osx linux freebsd openbsd netbsd dragonflybsd solaris): -std=c++11
+import core.stdc.stdio;
+
+// Disabled on windows because it needs bindings
+
+version (CppRuntime_Clang)
+{
+ extern(C++, `std`, `__1`)
+ {
+ struct allocator(T);
+ struct vector (T, A = allocator!T);
+ struct array (T, size_t N);
+ }
+}
+else
+{
+ extern(C++, `std`)
+ {
+ struct allocator(T);
+ struct vector (T, A = allocator!T);
+ struct array (T, size_t N);
+ }
+}
+
+extern(C++):
+
+ref T identity (T) (ref T v);
+T** identityPP (T) (T** v);
+vector!T* getVector (T) (size_t length, const T* ptr);
+array!(T, N)* getArray(T, size_t N) (const T* ptr);
+
+void main ()
+{
+ int i = 42;
+ float f = 21.0f;
+
+ int* pi = &i;
+ float* pf = &f;
+
+ assert(42 == identity(i));
+ assert(21.0f == identity(f));
+ assert(&pi == identityPP(&pi));
+ assert(&pf == identityPP(&pf));
+
+ auto vi = getVector(1, &i);
+ auto vf = getVector(3, [f, f, f].ptr);
+ assert(vi !is null);
+ assert(vf !is null);
+
+ auto ai = getArray!(int, 4)([2012, 10, 11, 42].ptr);
+ auto af = getArray!(float, 4)([42.0f, 21.0f, 14.0f, 1957.0f].ptr);
+ assert(ai !is null);
+ assert(af !is null);
+
+ printf("Success\n");
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/cppa.d b/gcc/testsuite/gdc.test/runnable_cxx/cppa.d
index cb576ba59a8..e6db3c4da43 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/cppa.d
+++ b/gcc/testsuite/gdc.test/runnable_cxx/cppa.d
@@ -1,9 +1,18 @@
// PERMUTE_ARGS: -g
// EXTRA_CPP_SOURCES: cppb.cpp
+// EXTRA_FILES: extra-files/cppb.h
+// CXXFLAGS(linux freebsd osx netbsd dragonflybsd): -std=c++11
+// druntime isn't linked, this prevents missing symbols '_d_arraybounds_slicep':
+// REQUIRED_ARGS: -checkaction=C
+// Filter a spurious warning on Semaphore:
+// TRANSFORM_OUTPUT: remove_lines("warning: relocation refers to discarded section")
+
+// N.B MSVC doesn't have a C++11 switch, but it defaults to the latest fully-supported standard
import core.stdc.stdio;
import core.stdc.stdarg;
import core.stdc.config;
+import core.stdc.stdint;
extern (C++)
int foob(int i, int j, int k);
@@ -152,7 +161,7 @@ extern (C) int foosize6();
void test6()
{
S6 f = foo6();
- printf("%d %d\n", foosize6(), S6.sizeof);
+ printf("%d %zd\n", foosize6(), S6.sizeof);
assert(foosize6() == S6.sizeof);
version (X86)
{
@@ -176,7 +185,7 @@ struct S
void test7()
{
- printf("%d %d\n", foo7(), S.sizeof);
+ printf("%d %zd\n", foo7(), S.sizeof);
assert(foo7() == S.sizeof);
}
@@ -191,7 +200,7 @@ void test8()
}
/****************************************/
-// 4059
+// https://issues.dlang.org/show_bug.cgi?id=4059
struct elem9 { }
@@ -249,14 +258,7 @@ extern(C++) void check13956(S13956 arg0, int arg1, int arg2, int arg3, int arg4,
assert(arg3 == 3);
assert(arg4 == 4);
assert(arg5 == 5);
- version (OSX)
- {
- version (D_LP64)
- assert(arg6 == 6);
- // fails on OSX 32-bit
- }
- else
- assert(arg6 == 6);
+ assert(arg6 == 6);
}
void test13956()
@@ -265,7 +267,7 @@ void test13956()
}
/****************************************/
-// 5148
+// https://issues.dlang.org/show_bug.cgi?id=5148
extern (C++)
{
@@ -342,7 +344,7 @@ void testvalist()
}
/****************************************/
-// 12825
+// https://issues.dlang.org/show_bug.cgi?id=12825
extern(C++) class C12825
{
@@ -455,7 +457,7 @@ version (linux)
extern (C++, std)
{
- struct allocator(T)
+ extern (C++, class) struct allocator(T)
{
version (linux)
{
@@ -465,50 +467,47 @@ extern (C++, std)
}
}
- version (linux)
+ class vector(T, A = allocator!T)
{
- class vector(T, A = allocator!T)
- {
- final void push_back(ref const T);
- }
+ final void push_back(ref const T);
+ }
- struct char_traits(T)
- {
- }
+ struct char_traits(T)
+ {
+ }
- // https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html
- version (none)
- {
- extern (C++, __cxx11)
- {
- struct basic_string(T, C = char_traits!T, A = allocator!T)
- {
- }
- }
- }
- else
+ // https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html
+ version (none)
+ {
+ extern (C++, __cxx11)
{
struct basic_string(T, C = char_traits!T, A = allocator!T)
{
}
}
-
- struct basic_istream(T, C = char_traits!T)
+ }
+ else
+ {
+ extern (C++, class) struct basic_string(T, C = char_traits!T, A = allocator!T)
{
}
+ }
- struct basic_ostream(T, C = char_traits!T)
- {
- }
+ struct basic_istream(T, C = char_traits!T)
+ {
+ }
- struct basic_iostream(T, C = char_traits!T)
- {
- }
+ struct basic_ostream(T, C = char_traits!T)
+ {
+ }
+
+ struct basic_iostream(T, C = char_traits!T)
+ {
}
class exception { }
- // 14956
+ // https://issues.dlang.org/show_bug.cgi?id=14956
extern(C++, N14956)
{
struct S14956 { }
@@ -584,8 +583,10 @@ extern(C++)
{
bool f13289_cpp_test();
+
wchar_t f13289_cpp_wchar_t(wchar_t);
+
wchar f13289_d_wchar(wchar ch)
{
if (ch <= 'z' && ch >= 'a')
@@ -681,6 +682,7 @@ void test16()
{
mylong ld = 5;
ld = testl(ld);
+ printf("ld = %lld, mylong.sizeof = %lld\n", cast(long)ld, cast(long)mylong.sizeof);
assert(ld == 5 + mylong.sizeof);
}
{
@@ -694,13 +696,16 @@ void test16()
static assert(__c_long.max == long.max);
static assert(__c_long.min == long.min);
static assert(__c_long.init == long.init);
+
static assert(__c_ulong.max == ulong.max);
static assert(__c_ulong.min == ulong.min);
static assert(__c_ulong.init == ulong.init);
+
__c_long cl = 0;
cl = cl + 1;
long l = cl;
cl = l;
+
__c_ulong cul = 0;
cul = cul + 1;
ulong ul = cul;
@@ -711,13 +716,16 @@ void test16()
static assert(__c_long.max == int.max);
static assert(__c_long.min == int.min);
static assert(__c_long.init == int.init);
+
static assert(__c_ulong.max == uint.max);
static assert(__c_ulong.min == uint.min);
static assert(__c_ulong.init == uint.init);
+
__c_long cl = 0;
cl = cl + 1;
int i = cl;
cl = i;
+
__c_ulong cul = 0;
cul = cul + 1;
uint u = cul;
@@ -767,7 +775,7 @@ extern(C++, N13337.M13337)
}
/****************************************/
-// 14195
+// https://issues.dlang.org/show_bug.cgi?id=14195
struct Delegate1(T) {}
struct Delegate2(T1, T2) {}
@@ -793,7 +801,7 @@ void test14195()
/****************************************/
-// 14200
+// https://issues.dlang.org/show_bug.cgi?id=14200
template Tuple14200(T...)
{
@@ -810,7 +818,7 @@ void test14200()
}
/****************************************/
-// 14956
+// https://issues.dlang.org/show_bug.cgi?id=14956
extern(C++) void test14956(S14956 s);
@@ -869,13 +877,13 @@ void testVtable()
/****************************************/
/* problems detected by fuzzer */
-extern(C++) void fuzz1_cppvararg(long arg10, long arg11, bool arg12);
-extern(C++) void fuzz1_dvararg(long arg10, long arg11, bool arg12)
+extern(C++) void fuzz1_cppvararg(int64_t arg10, int64_t arg11, bool arg12);
+extern(C++) void fuzz1_dvararg(int64_t arg10, int64_t arg11, bool arg12)
{
fuzz1_checkValues(arg10, arg11, arg12);
}
-extern(C++) void fuzz1_checkValues(long arg10, long arg11, bool arg12)
+extern(C++) void fuzz1_checkValues(int64_t arg10, int64_t arg11, bool arg12)
{
assert(arg10 == 103);
assert(arg11 == 104);
@@ -892,13 +900,13 @@ void fuzz1()
}
////////
-extern(C++) void fuzz2_cppvararg(ulong arg10, ulong arg11, bool arg12);
-extern(C++) void fuzz2_dvararg(ulong arg10, ulong arg11, bool arg12)
+extern(C++) void fuzz2_cppvararg(uint64_t arg10, uint64_t arg11, bool arg12);
+extern(C++) void fuzz2_dvararg(uint64_t arg10, uint64_t arg11, bool arg12)
{
fuzz2_checkValues(arg10, arg11, arg12);
}
-extern(C++) void fuzz2_checkValues(ulong arg10, ulong arg11, bool arg12)
+extern(C++) void fuzz2_checkValues(uint64_t arg10, uint64_t arg11, bool arg12)
{
assert(arg10 == 103);
assert(arg11 == 104);
@@ -915,13 +923,22 @@ void fuzz2()
}
////////
-extern(C++) void fuzz3_cppvararg(wchar arg10, wchar arg11, bool arg12);
-extern(C++) void fuzz3_dvararg(wchar arg10, wchar arg11, bool arg12)
+version(CppRuntime_DigitalMars)
+ enum UNICODE = false;
+else version(CppRuntime_Microsoft)
+ enum UNICODE = false; //VS2013 doesn't support them
+else
+ enum UNICODE = true;
+
+static if (UNICODE)
+{
+extern(C++) void fuzz3_cppvararg(wchar arg10, dchar arg11, bool arg12);
+extern(C++) void fuzz3_dvararg(wchar arg10, dchar arg11, bool arg12)
{
fuzz2_checkValues(arg10, arg11, arg12);
}
-extern(C++) void fuzz3_checkValues(wchar arg10, wchar arg11, bool arg12)
+extern(C++) void fuzz3_checkValues(wchar arg10, dchar arg11, bool arg12)
{
assert(arg10 == 103);
assert(arg11 == 104);
@@ -931,17 +948,18 @@ extern(C++) void fuzz3_checkValues(wchar arg10, wchar arg11, bool arg12)
void fuzz3()
{
wchar arg10 = 103;
- wchar arg11 = 104;
+ dchar arg11 = 104;
bool arg12 = false;
fuzz3_dvararg(arg10, arg11, arg12);
fuzz3_cppvararg(arg10, arg11, arg12);
}
+}
void fuzz()
{
fuzz1();
fuzz2();
- fuzz3();
+ static if (UNICODE) fuzz3();
}
/****************************************/
@@ -1038,7 +1056,7 @@ void testeh3()
}
/****************************************/
-// 15576
+// https://issues.dlang.org/show_bug.cgi?id=15576
extern (C++, ns15576)
{
@@ -1056,7 +1074,7 @@ void test15576()
}
/****************************************/
-// 15579
+// https://issues.dlang.org/show_bug.cgi?id=15579
extern (C++)
{
@@ -1138,7 +1156,7 @@ void test15579()
}
/****************************************/
-// 15610
+// https://issues.dlang.org/show_bug.cgi?id=15610
extern(C++) class Base2
{
@@ -1164,7 +1182,7 @@ void test15610()
}
/******************************************/
-// 15455
+// https://issues.dlang.org/show_bug.cgi?id=15455
struct X6
{
@@ -1209,9 +1227,9 @@ void test15455()
}
/****************************************/
-// 15372
+// https://issues.dlang.org/show_bug.cgi?id=15372
-extern(C++) int foo15372(T)(T v);
+extern(C++) int foo15372(T)(int v);
void test15372()
{
@@ -1221,7 +1239,7 @@ void test15372()
}
/****************************************/
-// 15802
+// https://issues.dlang.org/show_bug.cgi?id=15802
extern(C++) {
template Foo15802(T) {
@@ -1237,9 +1255,10 @@ void test15802()
}
/****************************************/
-// 16536 - mangling mismatch on OSX
+// https://issues.dlang.org/show_bug.cgi?id=16536
+// mangling mismatch on OSX
-version(OSX) extern(C++) ulong pass16536(ulong);
+version(OSX) extern(C++) uint64_t pass16536(uint64_t);
void test16536()
{
@@ -1247,6 +1266,347 @@ void test16536()
}
/****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15589
+// extern(C++) virtual destructors are not put in vtbl[]
+
+extern(C++)
+{
+ class A15589
+ {
+ extern(D) static int[] dtorSeq;
+ struct S
+ {
+ this(int x) { this.x = x; }
+ ~this() { dtorSeq ~= x; }
+ int x;
+ }
+ int foo() { return 100; } // shift dtor to slot 1
+ ~this() { dtorSeq ~= 10; }
+ S s1 = S(1);
+ S s2 = S(2);
+ }
+ class B15589 : A15589
+ {
+ int bar() { return 200;} // add an additional function AFTER the dtor at slot 2
+ ~this() { dtorSeq ~= 20; }
+ S s3 = S(3);
+ }
+
+ void test15589b(A15589 p);
+}
+
+void test15589()
+{
+ A15589 c = new B15589;
+ assert(A15589.dtorSeq == null);
+ assert(c.foo() == 100);
+ assert((cast(B15589)c).bar() == 200);
+ c.__xdtor(); // virtual dtor call
+ assert(A15589.dtorSeq[] == [ 20, 3, 10, 2, 1 ]); // destroyed full hierarchy!
+
+ A15589.dtorSeq = null;
+ test15589b(c);
+ assert(A15589.dtorSeq[] == [ 20, 3, 10, 2, 1 ]); // destroyed full hierarchy!
+}
+
+extern(C++)
+{
+ class Cpp15589Base
+ {
+ public:
+ final ~this();
+
+ void nonVirtual();
+ int a;
+ }
+
+ class Cpp15589Derived : Cpp15589Base
+ {
+ public:
+ this();
+ final ~this();
+ int b;
+ }
+
+ class Cpp15589BaseVirtual
+ {
+ public:
+ void beforeDtor();
+
+ this();
+ ~this();
+
+ void afterDtor();
+ int c = 1;
+ }
+
+ class Cpp15589DerivedVirtual : Cpp15589BaseVirtual
+ {
+ public:
+ this();
+ ~this();
+
+ override void afterDtor();
+
+ int d;
+ }
+
+ class Cpp15589IntroducingVirtual : Cpp15589Base
+ {
+ public:
+ this();
+ void beforeIntroducedVirtual();
+ ~this();
+ void afterIntroducedVirtual(int);
+
+ int e;
+ }
+
+ struct Cpp15589Struct
+ {
+ ~this();
+ int s;
+ }
+
+ void trace15589(int ch)
+ {
+ traceBuf[traceBufPos++] = cast(char) ch;
+ }
+}
+
+__gshared char[32] traceBuf;
+__gshared size_t traceBufPos;
+
+// workaround for https://issues.dlang.org/show_bug.cgi?id=18986
+version(OSX)
+ enum cppCtorReturnsThis = false;
+else version(FreeBSD)
+ enum cppCtorReturnsThis = false;
+else
+ enum cppCtorReturnsThis = true;
+
+mixin template scopeAllocCpp(C)
+{
+ static if (cppCtorReturnsThis)
+ scope C ptr = new C;
+ else
+ {
+ ubyte[__traits(classInstanceSize, C)] data;
+ C ptr = (){ auto p = cast(C) data.ptr; p.__ctor(); return p; }();
+ }
+}
+
+void test15589b()
+{
+ traceBufPos = 0;
+ {
+ Cpp15589Struct struc = Cpp15589Struct();
+ mixin scopeAllocCpp!Cpp15589Derived derived;
+ mixin scopeAllocCpp!Cpp15589DerivedVirtual derivedVirtual;
+ mixin scopeAllocCpp!Cpp15589IntroducingVirtual introducingVirtual;
+
+ // `scope` instances are destroyed automatically
+ static if (!cppCtorReturnsThis)
+ {
+ introducingVirtual.ptr.destroy();
+ derivedVirtual.ptr.destroy();
+ derived.ptr.destroy();
+ }
+ }
+ printf("traceBuf15589 %.*s\n", cast(int)traceBufPos, traceBuf.ptr);
+ assert(traceBuf[0..traceBufPos] == "IbVvBbs");
+}
+
+/****************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=18928
+// Win64: extern(C++) bad codegen, wrong calling convention
+
+extern(C++) struct Small18928
+{
+ int x;
+}
+
+extern(C++) class CC18928
+{
+ Small18928 getVirtual(); // { return S(3); }
+ final Small18928 getFinal(); // { return S(4); }
+ static Small18928 getStatic(); // { return S(5); }
+}
+
+extern(C++) CC18928 newCC18928();
+
+void test18928()
+{
+ auto cc = newCC18928();
+ Small18928 v = cc.getVirtual();
+ assert(v.x == 3);
+ Small18928 f = cc.getFinal();
+ assert(f.x == 4);
+ Small18928 s = cc.getStatic();
+ assert(s.x == 5);
+}
+
+/****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18953
+// Win32: extern(C++) struct destructor not called correctly through runtime
+
+extern(C++)
+struct S18953
+{
+ char x;
+ ~this() nothrow @nogc { traceBuf[traceBufPos++] = x; }
+}
+
+void test18953()
+{
+ traceBufPos = 0;
+ S18953[] arr = new S18953[3];
+ arr[1].x = '1';
+ arr[2].x = '2';
+ arr.length = 1;
+ assumeSafeAppend(arr); // destroys arr[1] and arr[2]
+ printf("traceBuf18953 %.*s\n", cast(int)traceBufPos, traceBuf.ptr);
+ assert(traceBuf[0..traceBufPos] == "21");
+}
+
+/****************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=18966
+
+extern(C++):
+class Base18966
+{
+ this() @safe nothrow;
+ ~this() @safe;
+ void vf();
+ int x;
+}
+
+class Derived18966 : Base18966
+{
+ override void vf() { x = 200; }
+}
+
+class Explicit18966 : Base18966
+{
+ this() @safe { super(); }
+ override void vf() { x = 250; }
+}
+
+class Implicit18966 : Base18966
+{
+ this() nothrow {}
+ override void vf() { x = 300; }
+}
+
+// test vptr in full ctor chain of mixed D/C++ class hierarchies
+
+// TODO: Make this a D class and let C++ derive from it. This works on Windows,
+// but results in linker errors on Posix due to extra base ctor (`C2`
+// mangling) being called by the B ctor.
+class A18966 // in C++
+{
+ char[8] calledOverloads = 0;
+ int i;
+ this();
+ void foo();
+}
+
+class B18966 : A18966 // in C++
+{
+ this();
+ override void foo();
+}
+
+class C18966 : B18966
+{
+ this() { foo(); }
+ override void foo() { calledOverloads[i++] = 'C'; }
+}
+
+class D18966 : C18966
+{
+ this() { foo(); }
+ override void foo() { calledOverloads[i++] = 'D'; }
+}
+
+void test18966()
+{
+ Derived18966 d = new Derived18966;
+ assert(d.x == 10);
+ d.vf();
+ assert(d.x == 200);
+
+ Explicit18966 e = new Explicit18966;
+ assert(e.x == 10);
+ e.vf();
+ assert(e.x == 250);
+
+ Implicit18966 i = new Implicit18966;
+ assert(i.x == 10);
+ i.vf();
+ assert(i.x == 300);
+
+ // TODO: Allocating + constructing a C++ class with the D GC is not
+ // supported on Posix. The returned pointer (probably from C++ ctor)
+ // seems to be an offset and not the actual object address.
+ version (Windows)
+ {
+ auto a = new A18966;
+ assert(a.calledOverloads[0..2] == "A\0");
+
+ auto b = new B18966;
+ assert(b.calledOverloads[0..3] == "AB\0");
+ }
+
+ auto c = new C18966;
+ assert(c.calledOverloads[0..4] == "ABC\0");
+
+ auto d2 = new D18966;
+ // note: the vptr semantics in ctors of extern(C++) classes may be revised (to "ABCD")
+ assert(d2.calledOverloads[0..5] == "ABDD\0");
+}
+
+/****************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=19134
+
+class Base19134
+{
+ int a = 123;
+ this() { a += 42; }
+ int foo() const { return a; }
+}
+
+class Derived19134 : Base19134
+{
+ int b = 666;
+ this()
+ {
+ a *= 2;
+ b -= 6;
+ }
+ override int foo() const { return b; }
+}
+
+void test19134()
+{
+ static const d = new Derived19134;
+ assert(d.a == (123 + 42) * 2);
+ assert(d.b == 666 - 6);
+ assert(d.foo() == 660);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18955
+alias std_string = std.basic_string!(char);
+
+extern(C++) void callback18955(ref const(std_string) str)
+{
+}
+extern(C++) void test18955();
+
+/****************************************/
void main()
{
@@ -1290,6 +1650,13 @@ void main()
test15372();
test15802();
test16536();
+ test15589();
+ test15589b();
+ test18928();
+ test18953();
+ test18966();
+ test19134();
+ test18955();
printf("Success\n");
}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/externmangle.d b/gcc/testsuite/gdc.test/runnable_cxx/externmangle.d
index 9099f94f2d0..ef132dcca72 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/externmangle.d
+++ b/gcc/testsuite/gdc.test/runnable_cxx/externmangle.d
@@ -1,4 +1,8 @@
// EXTRA_CPP_SOURCES: externmangle.cpp
+// REQUIRED_ARGS: -extern-std=c++11
+
+import core.stdc.config;
+import core.stdc.stdint;
extern(C++):
@@ -122,7 +126,16 @@ interface Module
public static int dim(Array!Module*);
};
-ulong testlongmangle(int a, uint b, long c, ulong d);
+uint64_t testlongmangle(int a, uint b, int64_t c, uint64_t d);
+cpp_ulong testCppLongMangle(cpp_long a, cpp_ulong b);
+cpp_ulonglong testCppLongLongMangle(cpp_longlong a, cpp_ulonglong b);
+
+// direct size_t/ptrdiff_t interop is fine except on 32-bit OS X
+version (OSX) { version (D_LP64) {} else version = OSX_32; }
+version (OSX_32)
+ cpp_size_t testCppSizeTMangle(cpp_ptrdiff_t a, cpp_size_t b);
+else
+ size_t testCppSizeTMangle(ptrdiff_t a, size_t b);
__gshared extern int[2][2][2] test31;
__gshared extern int* test32;
@@ -224,6 +237,40 @@ void test39()
assert(result == 0);
}
+extern(C++, "foo", "bar", "baz") int doStuff(int);
+
+version(CppRuntime_DigitalMars) // DMC doesn't support c++11
+{
+ void test40() {}
+ void test41() {}
+}
+else
+{
+ void test40();
+
+ void foovargs(T...)(T args)
+ {
+ static if (is(T[0] == char*))
+ {
+ assert(*args[0] == 'a');
+ }
+ else
+ {
+ float ret = args[0] + args[1];
+ assert(ret == 3.0f);
+ }
+ }
+
+ alias FooVargs = foovargs!(int, float);
+ alias FooVargs2 = foovargs!(char*);
+
+ void test41();
+ void make_shared_poc(T, Args...)(ref Args args)
+ {
+ assert(args[0] + args[1] == 3);
+ }
+ alias Make_Shared_Poc = make_shared_poc!(int, int, int);
+}
void main()
{
@@ -280,6 +327,9 @@ void main()
assert(Module.dim(&arr2) == 20);
assert(testlongmangle(1, 2, 3, 4) == 10);
+ assert(testCppLongMangle(1, 2) == 3);
+ assert(testCppLongLongMangle(3, 4) == 7);
+ assert(testCppSizeTMangle(3, 4) == 7);
assert(test31 == [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]);
assert(test32 == null);
@@ -311,4 +361,9 @@ void main()
assert(t38.test(1, 2, 3) == 1);
Test38.dispose(t38);
test39();
+
+ assert(doStuff(2) == 4);
+
+ test40();
+ test41();
}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/externmangle2.d b/gcc/testsuite/gdc.test/runnable_cxx/externmangle2.d
index 74beb2581f0..e003c959f4e 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/externmangle2.d
+++ b/gcc/testsuite/gdc.test/runnable_cxx/externmangle2.d
@@ -1,164 +1,156 @@
// EXTRA_CPP_SOURCES: externmangle2.cpp
+// DISABLED: win
-version(Windows)
+extern(C++):
+
+struct Test32NS1
{
- void main()
+ struct Foo(X)
{
+ X *v;
}
-}
-else
-{
- extern(C++):
-
- struct Test32NS1
- {
- struct Foo(X)
- {
- X *v;
- }
- struct Bar(X)
- {
- X *v;
- }
+ struct Bar(X)
+ {
+ X *v;
+ }
- };
+};
- struct Test32NS2
+struct Test32NS2
+{
+ struct Foo(X)
{
- struct Foo(X)
- {
- X *v;
- }
- };
+ X *v;
+ }
+};
- struct Test32(alias Y, alias Z)
- {
- Y!(int)* field;
- };
+struct Test32(alias Y, alias Z)
+{
+ Y!(int)* field;
+};
- void test32a(Test32!(Test32NS1.Foo, Test32NS1.Foo) arg);
- void test32b(Test32!(Test32NS1.Foo, Test32NS1.Bar) arg);
- void test32c(Test32!(Test32NS1.Foo, Test32NS2.Foo) arg);
- void test32d(Test32!(Test32NS1.Foo, Test32NS2.Foo) arg1, Test32!(Test32NS2.Foo, Test32NS1.Foo) arg2);
+void test32a(Test32!(Test32NS1.Foo, Test32NS1.Foo) arg);
+void test32b(Test32!(Test32NS1.Foo, Test32NS1.Bar) arg);
+void test32c(Test32!(Test32NS1.Foo, Test32NS2.Foo) arg);
+void test32d(Test32!(Test32NS1.Foo, Test32NS2.Foo) arg1, Test32!(Test32NS2.Foo, Test32NS1.Foo) arg2);
- interface XXX
- {
- }
+interface XXX
+{
+}
- void test33a(XXX, XXX*);
+void test33a(XXX, XXX*);
- struct Test33(alias A, alias B)
- {
- }
+struct Test33(alias A, alias B)
+{
+}
- /*
- void test33(XXX, Test33!(test33a, test33a) arg, XXX);
+/*
+void test33(XXX, Test33!(test33a, test33a) arg, XXX);
- struct Test34(alias A)
- {
- };
+struct Test34(alias A)
+{
+};
- struct Test34A
- {
- static void foo(int);
- };
+struct Test34A
+{
+ static void foo(int);
+};
- void test34(Test34!(Test34A.foo) arg);
- */
+void test34(Test34!(Test34A.foo) arg);
+*/
- __gshared extern int test36;
+__gshared extern int test36;
- /*
- struct Test37(alias A)
- {
- };
+/*
+struct Test37(alias A)
+{
+};
- struct Test37A
- {
- __gshared extern int t38;
- };
+struct Test37A
+{
+ __gshared extern int t38;
+};
- void test37(Test37!(test36) arg);
- void test38(Test37!(Test37A.t38) arg);
- */
+void test37(Test37!(test36) arg);
+void test38(Test37!(Test37A.t38) arg);
+*/
- struct Test39
+struct Test39
+{
+ struct T39A(X)
{
- struct T39A(X)
- {
- }
}
+}
- struct T39A
- {
- }
+struct T39A
+{
+}
- void test39(Test39.T39A!(.T39A));
+void test39(Test39.T39A!(.T39A));
- version(none)
+version(none)
+{
+ version(Posix) //Only for g++ with -std=c++0x and Visual Studio 2013+
{
- version(Posix) //Only for g++ with -std=c++0x and Visual Studio 2013+
- {
-
- struct Test40(T, V...)
- {
- }
+ struct Test40(T, V...)
+ {
- void test40(Test40!(int, double, void))
- {
- }
}
- else version(Win64) //Only for g++ with -std=c++0x and Visual Studio 2013+
+
+ void test40(Test40!(int, double, void))
{
+ }
+ }
+ else version(Win64) //Only for g++ with -std=c++0x and Visual Studio 2013+
+ {
- struct Test40(T, V...)
- {
+ struct Test40(T, V...)
+ {
- }
+ }
- void test40(Test40!(int, double, void))
- {
- }
+ void test40(Test40!(int, double, void))
+ {
}
}
+}
- __gshared extern const XXX test41;
- struct Test42
- {
- __gshared extern const XXX test42;
- }
- __gshared extern int[4] test43;
- const(XXX) test44();
+__gshared extern const XXX test41;
+struct Test42
+{
+ __gshared extern const XXX test42;
+}
+__gshared extern int[4] test43;
+const(XXX) test44();
- void main()
- {
- test32a(Test32!(Test32NS1.Foo, Test32NS1.Foo)());
- test32b(Test32!(Test32NS1.Foo, Test32NS1.Bar)());
- test32c(Test32!(Test32NS1.Foo, Test32NS2.Foo)());
- test32d(Test32!(Test32NS1.Foo, Test32NS2.Foo)(), Test32!(Test32NS2.Foo, Test32NS1.Foo)());
+void main()
+{
+ test32a(Test32!(Test32NS1.Foo, Test32NS1.Foo)());
+ test32b(Test32!(Test32NS1.Foo, Test32NS1.Bar)());
+ test32c(Test32!(Test32NS1.Foo, Test32NS2.Foo)());
+ test32d(Test32!(Test32NS1.Foo, Test32NS2.Foo)(), Test32!(Test32NS2.Foo, Test32NS1.Foo)());
- //test33a(null, null);
- //test33(null, Test33!(test33a, test33a)(), null);
+ //test33a(null, null);
+ //test33(null, Test33!(test33a, test33a)(), null);
- //test34(Test34!(Test34A.foo)());
+ //test34(Test34!(Test34A.foo)());
- assert(test36 == 36);
+ assert(test36 == 36);
- //test37(Test37!(test36)());
- //test38(Test37!(Test37A.t38)());
- test39(Test39.T39A!(.T39A)());
+ //test37(Test37!(test36)());
+ //test38(Test37!(Test37A.t38)());
+ test39(Test39.T39A!(.T39A)());
- assert(test41 is null);
- assert(Test42.test42 is null);
- assert(test43 == [1, 2, 3, 4]);
- auto ptr = &test44;
- }
+ assert(test41 is null);
+ assert(Test42.test42 is null);
+ assert(test43 == [1, 2, 3, 4]);
+ auto ptr = &test44;
}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/abi_tags.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/abi_tags.cpp
new file mode 100644
index 00000000000..a161f05e72f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/abi_tags.cpp
@@ -0,0 +1,146 @@
+/*
+ * Test C++ abi-tag name mangling.
+ * https://issues.dlang.org/show_bug.cgi?id=19949
+ *
+ * ABI tags are only supported on Linux & OSX,
+ * however OSX doesn't use it in its standard library.
+ *
+ * Requires at minimum Clang 3.9.0 or GCC 5.1
+ */
+
+struct [[gnu::abi_tag("tag1")]] Tagged1 {};
+struct [[gnu::abi_tag("tag2")]] Tagged2 {};
+
+struct [[gnu::abi_tag("tag1", "tag2")]] Tagged1_2
+{
+ struct [[gnu::abi_tag("tag3")]] Tagged3
+ {
+ Tagged3(int value_) : value(value_) {}
+
+ // _ZN9Tagged1_2B4tag1B4tag27Tagged3B4tag37Tagged4B4tag4Ev
+ [[gnu::abi_tag("tag4")]]
+ int Tagged4 () { return this->value; }
+
+ int value;
+ };
+};
+struct [[gnu::abi_tag("tag1")]] Tagged1Too {};
+
+// _Z5inst1B4tag1B4tag2
+Tagged1_2 inst1;
+// _Z5inst2B4tag1B4tag2B4tag3
+Tagged1_2::Tagged3 inst2(42);
+
+// _Z5func0B4tag1B4tag2i
+Tagged1_2 func0(int a) { return Tagged1_2(); }
+// _Z5func19Tagged1_2B4tag1B4tag2
+Tagged1_2 func1(Tagged1_2 a) { return a; }
+// _Z5func2B4tag27Tagged1B4tag1
+Tagged1_2 func2(Tagged1 a) { return Tagged1_2(); }
+// _Z5func3B4tag17Tagged2B4tag2
+Tagged1_2 func3(Tagged2 a) { return Tagged1_2(); }
+// _Z5func47Tagged2B4tag27Tagged1B4tag1
+Tagged1_2 func4(Tagged2 a, Tagged1 b) { return Tagged1_2(); }
+// _Z5func5B4tag37Tagged2B4tag27Tagged1B4tag1
+Tagged1_2::Tagged3 func5(Tagged2 a, Tagged1 b) { return Tagged1_2::Tagged3(420); }
+// _Z5func67Tagged2B4tag2S_7Tagged1B4tag19Tagged1_2B4tag1B4tag2
+void func6(Tagged2 a, Tagged2 b, Tagged1 c, Tagged1_2 d) {}
+// With T=Tagged1_2: _Z5func7I9Tagged1_2B4tag1B4tag2ET_S1_i
+template<typename T>
+T func7(T a, int) { return a; }
+// _Z5func87Tagged1B4tag110Tagged1TooB4tag1
+void func8 (Tagged1, Tagged1Too) {}
+
+// Explicitly instantiate the above templates.
+template Tagged1_2 func7<Tagged1_2>(Tagged1_2, int);
+
+struct [[gnu::abi_tag("foo", "bar")]] S
+{
+public:
+ int i;
+ S(int);
+};
+
+S::S(int i) : i(i) {}
+
+[[gnu::abi_tag("foo", "bar")]]
+int a;
+
+S b(0);
+
+[[gnu::abi_tag("foo", "bar")]]
+int f() { return 0xf; }
+
+S gs(int i) { return S(i + 0xe0); }
+S gss(S s, int i) { return S(i + s.i + 0xe0); }
+
+[[gnu::abi_tag("foo", "bar")]]
+S fss(S s, int i) { return S(i + s.i + 0xf); }
+
+template <class T>
+T gt(int i) { return T(i + 0xe0); }
+
+template <class T>
+T gtt(T t, int i) { return T(i + t.i + 0xe0); }
+
+
+template <class T>
+[[gnu::abi_tag("foo", "bar")]] /* GCC is inconsistent here, <= 6 matches clang but >= 7 is different */
+T ft(int i) { return T(i + 0xf); }
+
+template <class T>
+[[gnu::abi_tag("foo", "bar")]] /* GCC is inconsistent here, <= 6 matches clang but >= 7 is different */
+T ftt(T t, int i) { return T(i + t.i + 0xf); }
+
+#ifdef __clang__
+inline namespace [[gnu::abi_tag("AAA")]] N
+#else
+inline namespace N [[gnu::abi_tag("AAA")]]
+#endif
+{
+ template <int>
+ struct [[gnu::abi_tag("foo", "bar")]] K
+ {
+ public:
+ int i;
+ K(int i);
+ };
+}
+
+template <int j>
+K<j>::K(int i) : i(i) {}
+
+// Note: This does not include the 'AAA' in the mangling
+// template <int j>
+// K<j> fk(int i) { return K<j>(i + j + 0xf); }
+
+K<1> fk1(int i) { return K<1>(i + 1 + 0xf); }
+
+K<10> k10(0);
+
+void initVars()
+{
+ a = 10;
+ b = S(20);
+ k10 = K<10>(30);
+}
+
+// doesn't compile on GCC < 6, and not at all on Clang (tested with 9.0.1)
+#if __GNUC__ >= 6
+enum [[gnu::abi_tag("ENN")]] E0
+{ E0a = 0xa };
+
+E0 fe() { return E0a; }
+
+template<int>
+E0 fei() { return E0a; }
+#endif
+
+// Explicitly instantiate the above templates.
+template S gt<S>(int);
+template S gtt<S>(S, int);
+template S ft<S>(int);
+template S ftt<S>(S, int);
+#if __GNUC__ >= 6
+template E0 fei<0>();
+#endif
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/c14203.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/c14203.cpp
new file mode 100644
index 00000000000..059d0eeff5b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/c14203.cpp
@@ -0,0 +1,2 @@
+
+float func1() { return 73; }
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp11.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp11.cpp
new file mode 100644
index 00000000000..88aa719084d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp11.cpp
@@ -0,0 +1,35 @@
+#include <assert.h>
+#include <cstddef>
+#include <cstdint>
+
+void testnull(std::nullptr_t n)
+{
+ assert(n == nullptr);
+}
+
+void testnullnull(std::nullptr_t n1, std::nullptr_t n2)
+{
+ assert(n1 == nullptr);
+ assert(n2 == nullptr);
+}
+
+/****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19658
+
+enum class i8_19658 : std::int8_t;
+enum class u8_19658 : std::uint8_t;
+enum class i16_19658 : std::int16_t;
+enum class u16_19658 : std::uint16_t;
+enum class i32_19658 : std::int32_t;
+enum class u32_19658 : std::uint32_t;
+enum class i64_19658 : std::int64_t;
+enum class u64_19658 : std::uint64_t;
+
+void test19658_i8(i8_19658) {}
+void test19658_u8(u8_19658) {}
+void test19658_i16(i16_19658) {}
+void test19658_u16(u16_19658) {}
+void test19658_i32(i32_19658) {}
+void test19658_u32(u32_19658) {}
+void test19658_i64(i64_19658) {}
+void test19658_u64(u64_19658) {}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp19179.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp19179.cpp
new file mode 100644
index 00000000000..3ca23c1d368
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp19179.cpp
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=19179
+
+struct SmallStruct { int x, y; };
+
+SmallStruct test_small(SmallStruct);
+void test_small_noret(SmallStruct);
+
+void cppmain()
+{
+ SmallStruct s;
+ s.x = 10;
+ s.y = 20;
+ test_small(s);
+ test_small_noret(s);
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp_abi_tests.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp_abi_tests.cpp
index 63f74a280c1..8ba613920a4 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp_abi_tests.cpp
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp_abi_tests.cpp
@@ -1,11 +1,51 @@
+#include <assert.h>
+
struct S{
float a;
};
+namespace std
+{
+ struct test19248_ {int a;}; // Remove when `extern(C++, ns)` is gone
+ struct test19248 {int a;};
+};
+
+#ifdef __DMC__
+// DMC doesn't support c++11
+#elif defined (_MSC_VER) && _MSC_VER <= 1800
+// MSVC2013 doesn't support char16_t/char32_t
+#else
+#define TEST_UNICODE
+#endif
+
+struct S18784
+{
+ int i;
+ S18784(int n);
+};
+
+S18784::S18784(int n) : i(n) {}
+
+#ifdef __DMC__ // DMC doesn't support c++11
+template <class>
+#else
+template <class...>
+#endif
+struct SPack
+{
+ int i;
+};
+
+typedef SPack<int> SInt;
+
bool passthrough(bool value) { return value; }
signed char passthrough(signed char value) { return value; }
unsigned char passthrough(unsigned char value) { return value; }
char passthrough(char value) { return value; }
+#ifdef TEST_UNICODE
+char16_t passthrough(char16_t value) { return value; }
+char32_t passthrough(char32_t value) { return value; }
+#endif
wchar_t passthrough(wchar_t value) { return value; }
short passthrough(short value) { return value; }
unsigned short passthrough(unsigned short value) { return value; }
@@ -18,11 +58,18 @@ unsigned long long passthrough(unsigned long long value) { return value; }
float passthrough(float value) { return value; }
double passthrough(double value) { return value; }
S passthrough(S value) { return value; }
+std::test19248 passthrough(const std::test19248 value) { return value; }
+std::test19248_ passthrough(const std::test19248_ value) { return value; }
+SInt passthrough(SInt value) { return value; }
bool passthrough_ptr(bool *value) { return *value; }
signed char passthrough_ptr(signed char *value) { return *value; }
unsigned char passthrough_ptr(unsigned char *value) { return *value; }
char passthrough_ptr(char *value) { return *value; }
+#ifdef TEST_UNICODE
+char16_t passthrough_ptr(char16_t *value) { return *value; }
+char32_t passthrough_ptr(char32_t *value) { return *value; }
+#endif
wchar_t passthrough_ptr(wchar_t *value) { return *value; }
short passthrough_ptr(short *value) { return *value; }
unsigned short passthrough_ptr(unsigned short *value) { return *value; }
@@ -35,11 +82,18 @@ unsigned long long passthrough_ptr(unsigned long long *value) { return *value; }
float passthrough_ptr(float *value) { return *value; }
double passthrough_ptr(double *value) { return *value; }
S passthrough_ptr(S *value) { return *value; }
+std::test19248 passthrough_ptr(const std::test19248 *value) { return *value; }
+std::test19248_ passthrough_ptr(const std::test19248_ *value) { return *value; }
+SInt passthrough_ptr(SInt *value) { return *value; }
bool passthrough_ref(bool &value) { return value; }
signed char passthrough_ref(signed char &value) { return value; }
unsigned char passthrough_ref(unsigned char &value) { return value; }
char passthrough_ref(char &value) { return value; }
+#ifdef TEST_UNICODE
+char16_t passthrough_ref(char16_t &value) { return value; }
+char32_t passthrough_ref(char32_t &value) { return value; }
+#endif
wchar_t passthrough_ref(wchar_t &value) { return value; }
short passthrough_ref(short &value) { return value; }
unsigned short passthrough_ref(unsigned short &value) { return value; }
@@ -52,6 +106,55 @@ unsigned long long passthrough_ref(unsigned long long &value) { return value; }
float passthrough_ref(float &value) { return value; }
double passthrough_ref(double &value) { return value; }
S passthrough_ref(S &value) { return value; }
+std::test19248 passthrough_ref(const std::test19248 &value) { return value; }
+std::test19248_ passthrough_ref(const std::test19248_ &value) { return value; }
+SInt passthrough_ref(SInt &value) { return value; }
+
+namespace ns1
+{
+ // D: `char*, const(char)**`
+ int constFunction1(const char*, const char**) { return 1; }
+ // D: `const(char)*, const(char*)*`
+ int constFunction2(const char*, const char* const*) { return 2; }
+ // D: `const(char*), const(char**)*`
+ int constFunction3(const char* const, const char* const* const*) { return 3; }
+ // D: `const(char*), const(char***)`
+ int constFunction4(const char* const, const char* const* const* const) { return 42; }
+};
+
+struct SmallStruct
+{
+ int i;
+ SmallStruct(int); // implemented in D
+ SmallStruct(const SmallStruct &);
+};
+SmallStruct::SmallStruct(const SmallStruct &rhs)
+ : i(rhs.i + 10) {}
+void smallStructCallBack(SmallStruct p);
+void smallStructTest(SmallStruct p)
+{
+ assert(p.i == 52);
+
+ smallStructCallBack(p);
+ assert(p.i == 52);
+}
+
+struct Sdtor
+{
+ static int counter;
+ ~Sdtor();
+};
+
+Sdtor::~Sdtor() { ++counter; }
+int Sdtor::counter = 0;
+
+void consume(Sdtor value) {}
+
+void consume2(Sdtor value);
+void doConsume2(Sdtor& value)
+{
+ consume2(value);
+}
// Uncomment when mangling is fixed
// typedef void(*fn0)();
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp_stdlib.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp_stdlib.cpp
new file mode 100644
index 00000000000..deb027ace5e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp_stdlib.cpp
@@ -0,0 +1,47 @@
+#include <array>
+#include <string>
+#include <vector>
+
+template<typename T>
+T& identity (T& v) { return v; }
+template<typename T>
+T** identityPP (T** v) { return v; }
+
+template<typename T>
+std::vector<T>* getVector(size_t len, const T* ptr)
+{
+ std::vector<T>* ret = new std::vector<T>(len);
+ for (size_t i = 0; i < len; ++i)
+ (*ret)[i] = ptr[i];
+ return ret;
+}
+
+std::string* getString(int len, const char* ptr)
+{
+ return new std::string(ptr, len);
+}
+
+template<typename T, size_t N>
+std::array<T, N>* getArray(const T* ptr)
+{
+ std::array<T, N>* ret = new std::array<T, N>();
+ for (size_t x = 0; x < N; ++x)
+ (*ret)[x] = ptr[x];
+ return ret;
+}
+
+// Explicit instantiations, so they are callable outside this compilation unit.
+template int** identityPP<int>(int**);
+template float** identityPP<float>(float**);
+template int& identity<int>(int&);
+template float& identity<float>(float&);
+
+template std::vector<int>* getVector<int>(size_t, const int*);
+template std::vector<float>* getVector<float>(size_t, const float*);
+//typedef std::vector<float> svf_t;
+//template std::vector<svf_t>* getVector<svf_t>(size_t, const svf_t*);
+
+template std::array<int, 4>* getArray<int, 4>(const int*);
+template std::array<float, 4>* getArray<float, 4>(const float*);
+//typedef Foo<int, 42> fi42_t;
+//template std::array<fi42_t, 4> getArray<fi42_t, 4>(const fi42_t*);
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cppb.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cppb.cpp
index 823c6603878..ab1ea0a5a55 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cppb.cpp
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cppb.cpp
@@ -32,10 +32,13 @@ headers.
#define _GLIBCXX_USE_CXX11_ABI 0
#include <stdio.h>
+#include <stdint.h>
#include <assert.h>
#include <exception>
#include <cstdarg>
+#include "cppb.h"
+
/**************************************/
int foo(int i, int j, int k);
@@ -268,13 +271,13 @@ void foo8(const char *p)
}
/**************************************/
-// 4059
+// https://issues.dlang.org/show_bug.cgi?id=4059
struct elem9 { };
void foobar9(elem9*, elem9*) { }
/**************************************/
-// 5148
+// https://issues.dlang.org/show_bug.cgi?id=5148
void foo10(const char*, const char*) { }
void foo10(const int, const int) { }
@@ -347,7 +350,7 @@ size_t getoffset13161a()
/****************************************************/
-#if __linux__ || __APPLE__ || __FreeBSD__
+#if __linux__ || __APPLE__ || __FreeBSD__ || __DragonFly__
#include <memory>
#include <vector>
#include <string>
@@ -403,36 +406,30 @@ wchar_t f13289_cpp_wchar_t(wchar_t ch)
return ch;
}
}
-
-#if __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun || __NetBSD__
-unsigned short f13289_d_wchar(unsigned short ch);
-wchar_t f13289_d_dchar(wchar_t ch);
-#elif _WIN32
-wchar_t f13289_d_wchar(wchar_t ch);
-unsigned int f13289_d_dchar(unsigned int ch);
+#ifdef __DMC__
+// DMC doesn't support c++11
+#elif defined (_MSC_VER) //&& _MSC_VER <= 1800
+// MSVC2013 doesn't support char16_t/char32_t
+#else
+#define TEST_UNICODE
+#endif
+#ifdef TEST_UNICODE
+char16_t f13289_d_wchar(char16_t ch);
+char32_t f13289_d_dchar(char32_t ch);
#endif
-
wchar_t f13289_d_wchar_t(wchar_t ch);
bool f13289_cpp_test()
{
if (!(f13289_d_wchar_t(L'e') == L'E')) return false;
if (!(f13289_d_wchar_t(L'F') == L'F')) return false;
-#if __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun || __NetBSD__
- if (!(f13289_d_wchar((unsigned short)'c') == (unsigned short)'C')) return false;
- if (!(f13289_d_wchar((unsigned short)'D') == (unsigned short)'D')) return false;
- if (!(f13289_d_dchar(L'e') == L'E')) return false;
- if (!(f13289_d_dchar(L'F') == L'F')) return false;
- return true;
-#elif _WIN32
- if (!(f13289_d_wchar(L'c') == L'C')) return false;
- if (!(f13289_d_wchar(L'D') == L'D')) return false;
- if (!(f13289_d_dchar((unsigned int)'e') == (unsigned int)'E')) return false;
- if (!(f13289_d_dchar((unsigned int)'F') == (unsigned int)'F')) return false;
- return true;
-#else
- return false;
+#ifdef TEST_UNICODE
+ if (!(f13289_d_wchar(u'c') == u'C')) return false;
+ if (!(f13289_d_wchar(u'D') == u'D')) return false;
+ if (!(f13289_d_dchar(U'e') == U'E')) return false;
+ if (!(f13289_d_dchar(U'F') == U'F')) return false;
#endif
+ return true;
}
/******************************************/
@@ -499,7 +496,7 @@ namespace N13337 {
}
/****************************************/
-// 14195
+// https://issues.dlang.org/show_bug.cgi?id=14195
template <typename T>
struct Delegate1 {};
@@ -518,17 +515,17 @@ void test14195a(Delegate1<void()> func) {}
void test14195b(Delegate2<int(float, double), int(float, double)> func) {}
/******************************************/
-// 14200
+// https://issues.dlang.org/show_bug.cgi?id=14200
void test14200a(int a) {};
void test14200b(float a, int b, double c) {};
/******************************************/
-// 14956
+// https://issues.dlang.org/show_bug.cgi?id=14956
namespace std {
namespace N14956 {
- struct S14956 { };
+ struct S14956 { };
}
}
@@ -577,35 +574,26 @@ Visitor2* getVisitor2()
/******************************************/
// issues detected by fuzzer
-#if _LP64
-#define longlong long
-#else
-#define longlong long long
-#endif
-void fuzz1_checkValues(longlong arg10, longlong arg11, bool arg12);
-void fuzz1_cppvararg(longlong arg10, longlong arg11, bool arg12)
+void fuzz1_checkValues(int64_t arg10, int64_t arg11, bool arg12);
+void fuzz1_cppvararg(int64_t arg10, int64_t arg11, bool arg12)
{
fuzz1_checkValues(arg10, arg11, arg12);
}
-void fuzz2_checkValues(unsigned longlong arg10, unsigned longlong arg11, bool arg12);
-void fuzz2_cppvararg(unsigned longlong arg10, unsigned longlong arg11, bool arg12)
+void fuzz2_checkValues(uint64_t arg10, uint64_t arg11, bool arg12);
+void fuzz2_cppvararg(uint64_t arg10, uint64_t arg11, bool arg12)
{
fuzz2_checkValues(arg10, arg11, arg12);
}
-#if __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun || __NetBSD__
-#define wchar unsigned short
-#elif _WIN32
-#define wchar wchar_t
-#endif
-
-void fuzz3_checkValues(wchar arg10, wchar arg11, bool arg12);
-void fuzz3_cppvararg(wchar arg10, wchar arg11, bool arg12)
+#ifdef TEST_UNICODE
+void fuzz3_checkValues(char16_t arg10, char32_t arg11, bool arg12);
+void fuzz3_cppvararg(char16_t arg10, char32_t arg11, bool arg12)
{
fuzz3_checkValues(arg10, arg11, arg12);
}
+#endif
/******************************************/
@@ -620,7 +608,7 @@ void throwit()
/******************************************/
-#if linux
+#if __linux__
#include <stdexcept>
void throwle()
@@ -632,10 +620,10 @@ void throwle()
#endif
/******************************************/
-// 15579
+// https://issues.dlang.org/show_bug.cgi?id=15579
/******************************************/
-// 15579
+// https://issues.dlang.org/show_bug.cgi?id=15579
class Base
{
@@ -710,7 +698,7 @@ Interface *cppfooi(Interface *i)
}
/******************************************/
-// 15610
+// https://issues.dlang.org/show_bug.cgi?id=15610
class Base2
{
@@ -738,7 +726,7 @@ void Derived2::f()
}
/******************************************/
-// 15455
+// https://issues.dlang.org/show_bug.cgi?id=15455
struct X6
{
@@ -766,7 +754,7 @@ void test15455b(X8 s)
}
/******************************************/
-// 15372
+// https://issues.dlang.org/show_bug.cgi?id=15372
template <typename T>
int foo15372(int value)
@@ -776,11 +764,11 @@ int foo15372(int value)
void test15372b()
{
- int t = foo15372<int>(1);
+ int t = foo15372<int>(1);
}
/****************************************/
-// 15576
+// https://issues.dlang.org/show_bug.cgi?id=15576
namespace ns15576
{
@@ -793,7 +781,7 @@ namespace ns15576
}
/****************************************/
-// 15802
+// https://issues.dlang.org/show_bug.cgi?id=15802
template <typename T>
class Foo15802
@@ -807,16 +795,174 @@ public:
void test15802b()
{
- int t = Foo15802<int>::boo(1);
+ int t = Foo15802<int>::boo(1);
}
/****************************************/
-// 16536 - mangling mismatch on OSX
+// https://issues.dlang.org/show_bug.cgi?id=16536
+// mangling mismatch on OSX
#if defined(__APPLE__)
-__UINTMAX_TYPE__ pass16536(__UINTMAX_TYPE__ a)
+uint64_t pass16536(uint64_t a)
{
return a;
}
#endif
+
+/****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=15589
+// extern(C++) virtual destructors are not put in vtbl[]
+
+class A15589
+{
+public:
+ struct S
+ {
+ public:
+ int x;
+ };
+ virtual int foo();
+ virtual ~A15589();
+ S s1;
+ S s2;
+};
+class B15589 : public A15589
+{
+public:
+ virtual int bar();
+ virtual ~B15589();
+ S s3;
+};
+
+void test15589b(A15589 *p)
+{
+ assert(p->foo() == 100);
+ assert(((B15589*)p)->bar() == 200);
+ p->~A15589();
+}
+
+
+/////////////////
+void trace15589(int ch);
+
+Cpp15589Base::~Cpp15589Base()
+{
+ trace15589('b');
+}
+
+Cpp15589Derived::Cpp15589Derived()
+{
+ b = 1;
+}
+
+Cpp15589Derived::~Cpp15589Derived()
+{
+ trace15589('B');
+}
+
+Cpp15589BaseVirtual::Cpp15589BaseVirtual()
+{
+ c = 2;
+}
+
+Cpp15589BaseVirtual::~Cpp15589BaseVirtual()
+{
+ trace15589('v');
+}
+
+Cpp15589DerivedVirtual::Cpp15589DerivedVirtual()
+{
+ d = 3;
+}
+
+Cpp15589DerivedVirtual::~Cpp15589DerivedVirtual()
+{
+ trace15589('V');
+}
+
+Cpp15589IntroducingVirtual::Cpp15589IntroducingVirtual()
+{
+ e = 4;
+}
+
+Cpp15589IntroducingVirtual::~Cpp15589IntroducingVirtual()
+{
+ trace15589('I');
+}
+
+Cpp15589Struct::~Cpp15589Struct()
+{
+ trace15589('s');
+}
+
+/****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18928
+
+struct Small18928
+{
+ int x;
+};
+
+class CC18928
+{
+public:
+ virtual Small18928 getVirtual();
+ Small18928 getFinal();
+ static Small18928 getStatic();
+};
+
+Small18928 CC18928::getVirtual() { Small18928 s = {3}; return s; }
+Small18928 CC18928::getFinal() { Small18928 s = {4}; return s; }
+Small18928 CC18928::getStatic() { Small18928 s = {5}; return s; }
+
+CC18928* newCC18928()
+{
+ return new CC18928();
+}
+
+/****************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18966
+Base18966::Base18966() { x = 10; }
+Base18966::~Base18966() {}
+void Base18966::vf()
+{
+ x = 100;
+}
+
+A18966::A18966() : calledOverloads(/*zero-init*/), i(0) { foo(); }
+void A18966::foo() { calledOverloads[i++] = 'A'; }
+
+B18966::B18966() { foo(); }
+void B18966::foo() { calledOverloads[i++] = 'B'; }
+
+#if _WIN32 // otherwise defined in C header files!
+// https://issues.dlang.org/show_bug.cgi?id=18955
+namespace std
+{
+ template<typename Char>
+ struct char_traits
+ {
+ };
+ template<typename Char>
+ class allocator
+ {
+ };
+ template<typename Char, typename Traits, typename Alloc>
+ class basic_string
+ {
+ };
+ typedef basic_string<char, char_traits<char>, allocator<char> > string;
+}
+#endif // _WIN32
+
+void callback18955(const std::string& s);
+
+void test18955()
+{
+ std::string s;
+// TODO: on OSX and FreeBSD, std is mangled as std::__1
+#if !__APPLE__ && !__FreeBSD__
+ callback18955(s);
+#endif
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cppb.h b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cppb.h
new file mode 100644
index 00000000000..2ace6329dce
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cppb.h
@@ -0,0 +1,83 @@
+// avoid declaration in cpp file so the C/C++ compiler does no assume
+// these are inaccessible from elsewhere
+class Cpp15589Base
+{
+public:
+ ~Cpp15589Base();
+
+ virtual void nonVirtual() {}
+ int a;
+};
+
+class Cpp15589Derived : public Cpp15589Base
+{
+public:
+ Cpp15589Derived();
+ ~Cpp15589Derived();
+ int b;
+};
+
+class Cpp15589BaseVirtual
+{
+public:
+ virtual void beforeDtor() {}
+
+ Cpp15589BaseVirtual();
+ virtual ~Cpp15589BaseVirtual();
+
+ virtual void afterDtor() {}
+ int c;
+};
+
+class Cpp15589DerivedVirtual : public Cpp15589BaseVirtual
+{
+public:
+ Cpp15589DerivedVirtual(); // explicit C++ ctor needed, see https://issues.dlang.org/show_bug.cgi?id=18966
+ virtual ~Cpp15589DerivedVirtual();
+
+ virtual void afterDtor() {}
+
+ int d;
+};
+
+class Cpp15589IntroducingVirtual : public Cpp15589Base
+{
+public:
+ Cpp15589IntroducingVirtual();
+ virtual void beforeIntroducedVirtual() {}
+ virtual ~Cpp15589IntroducingVirtual();
+ virtual void afterIntroducedVirtual(int) {}
+
+ int e;
+};
+
+struct Cpp15589Struct
+{
+ ~Cpp15589Struct();
+ int s;
+};
+
+class Base18966
+{
+public:
+ Base18966();
+ virtual ~Base18966();
+ virtual void vf();
+ int x;
+};
+
+class A18966
+{
+public:
+ char calledOverloads[8];
+ int i;
+ A18966();
+ virtual void foo();
+};
+
+class B18966 : public A18966
+{
+public:
+ B18966();
+ void foo() /*override*/;
+};
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/externmangle.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/externmangle.cpp
index da3e8449b2e..37c98ea8fdd 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/externmangle.cpp
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/externmangle.cpp
@@ -1,3 +1,4 @@
+#include <stddef.h>
#include <stdint.h>
template<class X>
@@ -194,17 +195,25 @@ int Module::dim(Array<Module*>* arr)
return arr->dim;
}
-#if _LP64
-unsigned long testlongmangle(int32_t a, uint32_t b, long c, unsigned long d)
+uint64_t testlongmangle(int a, unsigned int b, int64_t c, uint64_t d)
{
return a + b + c + d;
}
-#else
-unsigned long long testlongmangle(int a, unsigned int b, long long c, unsigned long long d)
+
+unsigned long testCppLongMangle(long a, unsigned long b)
{
- return a + b + c + d;
+ return a + b;
+}
+
+unsigned long long testCppLongLongMangle(long long a, unsigned long long b)
+{
+ return a + b;
+}
+
+size_t testCppSizeTMangle(ptrdiff_t a, size_t b)
+{
+ return a + b;
}
-#endif
int test31[2][2][2] = {1, 1, 1, 1, 1, 1, 1, 1};
int *test32 = 0;
@@ -401,4 +410,40 @@ int test39cpp(C2<char>* c2, S2<int>* s2)
if (s2->value() != otherS2->value())
return 2;
return 0;
-} \ No newline at end of file
+}
+
+namespace foo
+{
+ namespace bar
+ {
+ namespace baz
+ {
+ int doStuff(int i)
+ {
+ return i * 2;
+ }
+ }
+ }
+}
+
+#ifndef __DMC__ // DMC doesn't support c++11
+template<typename ...T> void foovargs(T... args);
+
+void test40()
+{
+ foovargs<int, float>(1, 2.0f);
+ char c = 'a';
+ foovargs<char*>(&c);
+}
+
+template<typename T, typename ...Args>
+void make_shared_poc(Args&... args);
+
+void test41()
+{
+ int a = 1;
+ int b = 2;
+ make_shared_poc<int, int, int>(a, b);
+
+}
+#endif
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/stdint.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/stdint.cpp
new file mode 100644
index 00000000000..1f5d605cd4b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/stdint.cpp
@@ -0,0 +1,8 @@
+#include <stdint.h>
+
+int testCppI8Mangle (int8_t, uint8_t, int_least8_t, uint_least8_t, int_fast8_t, uint_fast8_t) { return 1; }
+int testCppI16Mangle(int16_t, uint16_t, int_least16_t, uint_least16_t, int_fast16_t, uint_fast16_t) { return 2; }
+int testCppI32Mangle(int32_t, uint32_t, int_least32_t, uint_least32_t, int_fast32_t, uint_fast32_t) { return 3; }
+int testCppI64Mangle(int64_t, uint64_t, int_least64_t, uint_least64_t, int_fast64_t, uint_fast64_t) { return 4; }
+int testCppIntPtrMangle(intptr_t, uintptr_t) { return 5; }
+int testCppIntMaxMangle(intmax_t, uintmax_t) { return 6; }
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test20652.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test20652.cpp
new file mode 100644
index 00000000000..91ab66aaf19
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test20652.cpp
@@ -0,0 +1,34 @@
+#if defined(__DMC__) // DMC doesn't support immintrin.h
+#else
+
+#include <assert.h>
+
+// Inline the typedef of __m128 instead of including immintrin.h.
+#if defined(__GNUC__) || defined(__clang__)
+typedef float __m128 __attribute__((__vector_size__(16), __may_alias__));
+
+#elif defined(_MSC_VER)
+typedef union __declspec(intrin_type) __declspec(align(16)) __m128 {
+ float m128_f32[4];
+} __m128;
+
+#else
+#error "Unknown vendor"
+#endif
+
+void test20652(const __m128& a)
+{
+ union
+ {
+ __m128 value;
+ float array[4];
+ } b;
+ b.value = a;
+
+ assert(b.array[0] == 1);
+ assert(b.array[1] == 1);
+ assert(b.array[2] == 1);
+ assert(b.array[3] == 1);
+}
+
+#endif
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test21515.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test21515.cpp
index 076777a268b..71c624e8577 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test21515.cpp
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test21515.cpp
@@ -7,17 +7,17 @@ union cdouble_t { _Complex double z; struct { double re; double im; }; };
union creal_t { _Complex long double z; struct { long double re; long double im; }; };
// extern(C) tests
-extern "C" _Complex float ccomplexf() { return 2.0f+I; }
-extern "C" _Complex double ccomplex() { return 2.0+I; }
-extern "C" _Complex long double ccomplexl() { return 2.0L+I; }
+extern "C" _Complex float ccomplexf() { return {2.0f, 1.0f}; }
+extern "C" _Complex double ccomplex() { return {2.0, 1.0}; }
+extern "C" _Complex long double ccomplexl() { return {2.0L, 1.0L}; }
extern "C" void ccomplexf2(_Complex float c) { cfloat_t z = {c}; assert(z.re == 2 && z.im == 1); }
extern "C" void ccomplex2(_Complex double c) { cdouble_t z = {c}; assert(z.re == 2 && z.im == 1); }
extern "C" void ccomplexl2(_Complex long double c) { creal_t z = {c}; assert(z.re == 2 && z.im == 1); }
// extern(C++) tests
-_Complex float cpcomplexf() { return 2.0f+I; }
-_Complex double cpcomplex() { return 2.0+I; }
-_Complex long double cpcomplexl() { return 2.0L+I; }
+_Complex float cpcomplexf() { return {2.0f, 1.0f}; }
+_Complex double cpcomplex() { return {2.0, 1.0}; }
+_Complex long double cpcomplexl() { return {2.0L, 1.0L}; }
void cpcomplexf(_Complex float c) { cfloat_t z = {c}; assert(z.re == 2 && z.im == 1); }
void cpcomplex(_Complex double c) { cdouble_t z = {c}; assert(z.re == 2 && z.im == 1); }
void cpcomplexl(_Complex long double c) { creal_t z = {c}; assert(z.re == 2 && z.im == 1); }
@@ -30,21 +30,21 @@ struct wrap_complexl { _Complex long double c; };
wrap_complexf wcomplexf()
{
wrap_complexf s;
- s.c = 2.0f+I;
+ s.c = {2.0f, 1.0f};
return s;
}
wrap_complex wcomplex()
{
wrap_complex s;
- s.c = 2.0+I;
+ s.c = {2.0, 1.0};
return s;
}
wrap_complexl wcomplexl()
{
wrap_complexl s;
- s.c = 2.0L+I;
+ s.c = {2.0L, 1.0L};
return s;
}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test6716.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test6716.cpp
new file mode 100644
index 00000000000..2528b1899d7
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/test6716.cpp
@@ -0,0 +1,13 @@
+
+int test6716(int magic);
+
+extern "C" int rt_init();
+extern "C" int rt_term();
+
+int main(int argc, char*argv[])
+{
+ rt_init();
+ int rc = test6716(12345);
+ rt_term();
+ return rc;
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/stdint.d b/gcc/testsuite/gdc.test/runnable_cxx/stdint.d
new file mode 100644
index 00000000000..e599425d20c
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/stdint.d
@@ -0,0 +1,24 @@
+// EXTRA_CPP_SOURCES: stdint.cpp
+
+module stdint_test;
+
+import core.stdc.stdint;
+
+extern(C++):
+
+int testCppI8Mangle (int8_t, uint8_t, int_least8_t, uint_least8_t, int_fast8_t, uint_fast8_t);
+int testCppI16Mangle(int16_t, uint16_t, int_least16_t, uint_least16_t, int_fast16_t, uint_fast16_t);
+int testCppI32Mangle(int32_t, uint32_t, int_least32_t, uint_least32_t, int_fast32_t, uint_fast32_t);
+int testCppI64Mangle(int64_t, uint64_t, int_least64_t, uint_least64_t, int_fast64_t, uint_fast64_t);
+int testCppIntPtrMangle(intptr_t, uintptr_t);
+int testCppIntMaxMangle(intmax_t, uintmax_t);
+
+void main()
+{
+ assert(testCppI8Mangle (1, 2, 3, 4, 5, 6) == 1);
+ assert(testCppI16Mangle(1, 2, 3, 4, 5, 6) == 2);
+ assert(testCppI32Mangle(1, 2, 3, 4, 5, 6) == 3);
+ assert(testCppI64Mangle(1, 2, 3, 4, 5, 6) == 4);
+ assert(testCppIntPtrMangle(1, 2) == 5);
+ assert(testCppIntMaxMangle(1, 2) == 6);
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/test14203.d b/gcc/testsuite/gdc.test/runnable_cxx/test14203.d
new file mode 100644
index 00000000000..3b29c662039
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/test14203.d
@@ -0,0 +1,22 @@
+// EXTRA_CPP_SOURCES: c14203.cpp
+
+
+/************************************************/
+
+// https://issues.dlang.org/show_bug.cgi?id=14203
+
+extern (C++) float func1();
+
+void test14203()
+{
+ assert(func1() == 73);
+}
+
+/************************************************/
+
+int main()
+{
+ test14203();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/test19179.d b/gcc/testsuite/gdc.test/runnable_cxx/test19179.d
new file mode 100644
index 00000000000..6bdccfd18aa
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/test19179.d
@@ -0,0 +1,32 @@
+// EXTRA_CPP_SOURCES: cpp19179.cpp
+
+// https://issues.dlang.org/show_bug.cgi?id=19179
+
+import core.stdc.stdio;
+
+extern(C++) struct SmallStruct { int x = 10, y = 20; }
+
+extern (C++)
+SmallStruct test_small(SmallStruct s)
+{
+ printf("%d %d\n", s.x, s.y); // prints: invalid memory
+ assert(s.x == 10);
+ assert(s.y == 20);
+ return s;
+}
+
+extern (C++)
+void test_small_noret(SmallStruct s)
+{
+ printf("%d %d\n", s.x, s.y); // prints: 10 20
+ assert(s.x == 10);
+ assert(s.y == 20);
+}
+
+extern (C++) void cppmain();
+
+int main()
+{
+ cppmain();
+ return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/test20652.d b/gcc/testsuite/gdc.test/runnable_cxx/test20652.d
new file mode 100644
index 00000000000..68bb0a711e4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/test20652.d
@@ -0,0 +1,23 @@
+// https://issues.dlang.org/show_bug.cgi?id=20652
+// EXTRA_CPP_SOURCES: test20652.cpp
+
+import core.simd;
+
+version (CRuntime_DigitalMars) // DMC doesn't support immintrin.h
+{
+ void main() {}
+}
+else static if (!__traits(compiles, float4)) // No __vector support
+{
+ void main() {}
+}
+else
+{
+ extern(C++) void test20652(ref const float4);
+
+ void main()
+ {
+ float4 f4 = 1;
+ test20652(f4);
+ }
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/test21515.d b/gcc/testsuite/gdc.test/runnable_cxx/test21515.d
index cc4420734a6..e5484561945 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/test21515.d
+++ b/gcc/testsuite/gdc.test/runnable_cxx/test21515.d
@@ -1,5 +1,6 @@
// https://issues.dlang.org/show_bug.cgi?id=21515
// EXTRA_CPP_SOURCES: test21515.cpp
+// CXXFLAGS: -std=c++11
// DISABLED: win32 win64
// ABI layout of native complex
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/test6716.d b/gcc/testsuite/gdc.test/runnable_cxx/test6716.d
new file mode 100644
index 00000000000..edda4235fac
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable_cxx/test6716.d
@@ -0,0 +1,20 @@
+// EXTRA_CPP_SOURCES: test6716.cpp
+
+version(Windows)
+{
+ // without main, there is no implicit reference to the runtime library
+ // other platforms pass the runtime library on the linker command line
+ version(CRuntime_Microsoft)
+ version(Win64)
+ pragma(lib, "phobos64");
+ else
+ pragma(lib, "phobos32mscoff");
+ else
+ pragma(lib, "phobos");
+}
+
+extern(C++) int test6716(int magic)
+{
+ assert(magic == 12345);
+ return 0;
+} \ No newline at end of file
diff --git a/gcc/testsuite/lib/gdc-utils.exp b/gcc/testsuite/lib/gdc-utils.exp
index 33c01450eb4..b3da7889bc0 100644
--- a/gcc/testsuite/lib/gdc-utils.exp
+++ b/gcc/testsuite/lib/gdc-utils.exp
@@ -35,6 +35,15 @@ proc gdc-convert-args { args } {
set compilable_output_file_ext "di"
lappend out "-H"
+ } elseif [string match "-HC" $arg] {
+ upvar 1 compilable_output_file_ext compilable_output_file_ext
+ upvar 1 name name
+ set compilable_output_file_ext "h"
+ lappend out "-fdump-c++-spec=[file rootname $name].h"
+
+ } elseif [string match "-HC=verbose" $arg] {
+ lappend out "-fdump-c++-spec-verbose"
+
} elseif { [regexp -- {^-I([\w+/-]+)} $arg pattern path] } {
lappend out "-I$path"
@@ -54,11 +63,11 @@ proc gdc-convert-args { args } {
} elseif { [string match "-boundscheck" $arg]
|| [string match "-boundscheck=on" $arg] } {
- lappend out "-fbounds-check"
+ lappend out "-fbounds-check=on"
} elseif { [string match "-boundscheck=off" $arg]
|| [string match "-noboundscheck" $arg] } {
- lappend out "-fno-bounds-check"
+ lappend out "-fbounds-check=off"
} elseif [string match "-boundscheck=safeonly" $arg] {
lappend out "-fbounds-check=safeonly"
@@ -66,6 +75,12 @@ proc gdc-convert-args { args } {
} elseif [string match "-c" $arg] {
lappend out "-c"
+ } elseif [regexp -- {^-check=(\w+)=off} $arg pattern value] {
+ lappend out "-fno-check=$value"
+
+ } elseif [string match "-checkaction=context" $arg] {
+ lappend out "-fcheckaction=context"
+
} elseif [string match "-d" $arg] {
lappend out "-Wno-deprecated"
@@ -79,23 +94,35 @@ proc gdc-convert-args { args } {
} elseif [regexp -- {^-debug=(\w+)} $arg pattern value] {
lappend out "-fdebug=$value"
+ } elseif [string match "-dip25" $arg] {
+ lappend out "-fpreview=dip25"
+
} elseif [string match "-dip1000" $arg] {
- lappend out "-ftransition=dip1000"
+ lappend out "-fpreview=dip1000"
- } elseif [string match "-dip25" $arg] {
- lappend out "-ftransition=dip25"
+ } elseif [string match "-dip1008" $arg] {
+ lappend out "-fpreview=dip1008"
} elseif [string match "-dw" $arg] {
lappend out "-Wdeprecated"
lappend out "-Wno-error"
+ } elseif [regexp -- {^-extern-std=([\w+]+)} $arg pattern value] {
+ lappend out "-fextern-std=$value"
+
} elseif [string match "-fPIC" $arg] {
lappend out "-fPIC"
+ } elseif [string match "-fPIE" $arg] {
+ lappend out "-fPIE"
+
} elseif { [string match "-g" $arg]
|| [string match "-gc" $arg] } {
lappend out "-g"
+ } elseif [string match "-ignore" $arg] {
+ lappend out "-fignore-unknown-pragmas"
+
} elseif [string match "-inline" $arg] {
lappend out "-finline-functions"
@@ -108,11 +135,17 @@ proc gdc-convert-args { args } {
} elseif [string match "-O" $arg] {
lappend out "-O2"
+ } elseif [regexp -- {^-preview=(\w+)} $arg pattern value] {
+ lappend out "-fpreview=[string tolower $value]"
+
} elseif [string match "-release" $arg] {
lappend out "-frelease"
+ } elseif [regexp -- {^-revert=(\w+)} $arg pattern value] {
+ lappend out "-frevert=[string tolower $value]"
+
} elseif [regexp -- {^-transition=(\w+)} $arg pattern value] {
- lappend out "-ftransition=$value"
+ lappend out "-ftransition=[string tolower $value]"
} elseif [string match "-unittest" $arg] {
lappend out "-funittest"
@@ -126,6 +159,16 @@ proc gdc-convert-args { args } {
} elseif [regexp -- {^-version=(\w+)} $arg pattern value] {
lappend out "-fversion=$value"
+ } elseif [string match "-o-" $arg] {
+ upvar 2 compilable_do_what compilable_do_what
+ set compilable_do_what "compile"
+
+ } elseif [string match "-vgc" $arg] {
+ lappend out "-ftransition=nogc"
+
+ } elseif [string match "-vtemplates" $arg] {
+ lappend out "-ftransition=templates"
+
} elseif [string match "-vtls" $arg] {
lappend out "-ftransition=tls"
@@ -184,10 +227,15 @@ proc gdc-copy-file { srcdir filename } {
# the test.
# EXTRA_CPP_SOURCES: List of extra C++ files to build and link along with
# the test.
+# CXXFLAGS: list of extra arguments passed when compiling C++
+# sources defined in EXTRA_CPP_SOURCES.
+# EXTRA_OBJC_SOURCES: List of extra Objective-C file to build and link along
+# with the test. Currently not handled.
# EXTRA_FILES: List of extra files to copy for the test runs.
# PERMUTE_ARGS: The set of arguments to permute in multiple compiler
# invocations. An empty set means only one permutation
# with no arguments.
+# ARG_SETS: Not handled.
# LINK: Enables linking.
# TEST_OUTPUT: The output expected from the compilation.
# POST_SCRIPT: Not handled.
@@ -241,6 +289,10 @@ proc gdc-convert-test { base test } {
# LINK sets dg-do-what-default "link"
set compilable_do_what "link"
+ } elseif [regexp -- {ARG_SETS} $copy_line] {
+ # ARG_SETS is not handled.
+ regsub -- {ARG_SETS.*$} $copy_line "" out_line
+
} elseif [regexp -- {POST_SCRIPT} $copy_line] {
# POST_SCRIPT is not handled
@@ -279,6 +331,16 @@ proc gdc-convert-test { base test } {
lappend extra_sources "extra-files/$srcfile"
}
+ } elseif [regexp -- {CXXFLAGS\s*:\s*(.*)} $copy_line match args] {
+ # Both C++ and D sources are compiled together, so include each
+ # other's command line flags too.
+ set new_option "{ dg-additional-options \"$args\" }"
+ regsub -- {CXXFLAGS.*$} $copy_line $new_option out_line
+
+ } elseif [regexp -- {EXTRA_OBJC_SOURCES\s*:\s*(.*)} $copy_line match sources] {
+ # EXTRA_OBJC_SOURCES is not handled.
+ regsub -- {EXTRA_OBJC_SOURCES.*$} $copy_line "" out_line
+
} elseif [regexp -- {EXTRA_FILES\s*:\s*(.*)} $copy_line match files] {
# EXTRA_FILES are appended to extra_files list
foreach file $files {
@@ -365,6 +427,11 @@ proc gdc-convert-test { base test } {
}
fail_compilation {
+ # Fail compilation tests only check for language errors from the
+ # front-end. No need to run all permutations of the default DFLAGS.
+ if { $PERMUTE_ARGS == $DEFAULT_DFLAGS } {
+ set PERMUTE_ARGS ""
+ }
puts $fdout "// { dg-final { output-exists-not } }"
}
}
@@ -434,7 +501,7 @@ proc gdc-do-test { testcases } {
regexp -- "(.*)/(.+)/(.+)\.(.+)$" $test match base type name ext
# Convert to DG test.
- set imports [format "-I%s/%s" $base $type]
+ set imports [format "-I%s" $type]
set cleanup_extra_files ""
set compilable_do_what "compile"
set filename "[gdc-convert-test $base $type/$name.$ext]"
diff --git a/libphobos/libdruntime/LICENSE b/libphobos/libdruntime/LICENSE
deleted file mode 100644
index c83ac898df4..00000000000
--- a/libphobos/libdruntime/LICENSE
+++ /dev/null
@@ -1,26 +0,0 @@
-DRuntime: Runtime Library for the D Programming Language
-========================================================
-
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/libphobos/libdruntime/LICENSE.txt b/libphobos/libdruntime/LICENSE.txt
new file mode 100644
index 00000000000..36b7cd93cdf
--- /dev/null
+++ b/libphobos/libdruntime/LICENSE.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index 0d554e07098..11bef0f3388 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@
-98c6ff0cf1241a0cfac196bf8a0523b1d4ecd3ac
+e6caaab9d359198b760c698dcb6d253afb3f81f6
The first line of this file holds the git revision number of the last
merge done from the dlang/druntime repository.
diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am
index a2e2bff9e44..80c7567079a 100644
--- a/libphobos/libdruntime/Makefile.am
+++ b/libphobos/libdruntime/Makefile.am
@@ -19,7 +19,8 @@
include $(top_srcdir)/d_rules.am
# Make sure GDC can find libdruntime include files
-D_EXTRA_DFLAGS=-nostdinc -I $(srcdir) -I .
+D_EXTRA_DFLAGS=-fpreview=dip1000 -fpreview=fieldwise -fpreview=dtorfields \
+ -nostdinc -I $(srcdir) -I .
# D flags for compilation
AM_DFLAGS= \
@@ -119,6 +120,7 @@ endif
DRUNTIME_DSOURCES_GENERATED = gcc/config.d gcc/libbacktrace.d
ALL_DRUNTIME_SOURCES = $(DRUNTIME_DSOURCES) $(DRUNTIME_CSOURCES) \
+ $(DRUNTIME_DSOURCES_STDCXX) \
$(DRUNTIME_SOURCES_CONFIGURED) $(DRUNTIME_DSOURCES_GENERATED)
# Need this library to both be part of libgphobos.a, and installed separately.
@@ -166,12 +168,30 @@ install-data-local:
DRUNTIME_CSOURCES = core/stdc/errno_.c
DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
- core/checkedint.d core/cpuid.d core/demangle.d core/exception.d \
- core/internal/abort.d core/internal/arrayop.d \
- core/internal/attributes.d core/internal/convert.d \
- core/internal/hash.d core/internal/spinlock.d core/internal/string.d \
- core/internal/traits.d core/math.d core/memory.d core/runtime.d \
- core/simd.d core/stdc/assert_.d core/stdc/complex.d core/stdc/config.d \
+ core/builtins.d core/checkedint.d core/cpuid.d core/demangle.d \
+ core/exception.d core/gc/config.d core/gc/gcinterface.d \
+ core/gc/registry.d core/internal/abort.d \
+ core/internal/array/appending.d core/internal/array/capacity.d \
+ core/internal/array/casting.d core/internal/array/comparison.d \
+ core/internal/array/concatenation.d core/internal/array/construction.d \
+ core/internal/array/equality.d core/internal/array/operations.d \
+ core/internal/array/utils.d core/internal/atomic.d \
+ core/internal/attributes.d core/internal/container/array.d \
+ core/internal/container/common.d core/internal/container/hashtab.d \
+ core/internal/container/treap.d core/internal/convert.d \
+ core/internal/dassert.d core/internal/destruction.d \
+ core/internal/entrypoint.d core/internal/gc/bits.d \
+ core/internal/gc/impl/conservative/gc.d \
+ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \
+ core/internal/gc/os.d core/internal/gc/pooltable.d \
+ core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \
+ core/internal/moving.d core/internal/parseoptions.d \
+ core/internal/postblit.d core/internal/qsort.d \
+ core/internal/spinlock.d core/internal/string.d \
+ core/internal/switch_.d core/internal/traits.d core/internal/utf.d \
+ core/internal/util/array.d core/internal/util/math.d core/lifetime.d \
+ core/math.d core/memory.d core/runtime.d core/simd.d \
+ core/stdc/assert_.d core/stdc/complex.d core/stdc/config.d \
core/stdc/ctype.d core/stdc/errno.d core/stdc/fenv.d \
core/stdc/float_.d core/stdc/inttypes.d core/stdc/limits.d \
core/stdc/locale.d core/stdc/math.d core/stdc/signal.d \
@@ -179,28 +199,28 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
core/stdc/stdio.d core/stdc/stdlib.d core/stdc/string.d \
core/stdc/tgmath.d core/stdc/time.d core/stdc/wchar_.d \
core/stdc/wctype.d core/sync/barrier.d core/sync/condition.d \
- core/sync/config.d core/sync/exception.d core/sync/mutex.d \
- core/sync/rwmutex.d core/sync/semaphore.d core/thread/context.d \
- core/thread/fiber.d core/thread/osthread.d core/thread/package.d \
- core/thread/threadbase.d core/thread/threadgroup.d core/thread/types.d \
- core/time.d core/vararg.d core/volatile.d gc/bits.d gc/config.d \
- gc/gcinterface.d gc/impl/conservative/gc.d gc/impl/manual/gc.d gc/os.d \
- gc/pooltable.d gc/proxy.d gcc/attribute.d gcc/attributes.d \
+ core/sync/config.d core/sync/event.d core/sync/exception.d \
+ core/sync/mutex.d core/sync/rwmutex.d core/sync/semaphore.d \
+ core/thread/context.d core/thread/fiber.d core/thread/osthread.d \
+ core/thread/package.d core/thread/threadbase.d \
+ core/thread/threadgroup.d core/thread/types.d core/time.d \
+ core/vararg.d core/volatile.d gcc/attribute.d gcc/attributes.d \
gcc/backtrace.d gcc/builtins.d gcc/deh.d gcc/emutls.d gcc/gthread.d \
gcc/sections/common.d gcc/sections/elf.d gcc/sections/macho.d \
gcc/sections/package.d gcc/sections/pecoff.d gcc/unwind/arm.d \
gcc/unwind/arm_common.d gcc/unwind/c6x.d gcc/unwind/generic.d \
gcc/unwind/package.d gcc/unwind/pe.d object.d rt/aApply.d rt/aApplyR.d \
- rt/aaA.d rt/adi.d rt/arrayassign.d rt/arraycast.d rt/arraycat.d \
- rt/cast_.d rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d \
+ rt/aaA.d rt/adi.d rt/arrayassign.d rt/arraycat.d rt/cast_.d \
+ rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
- rt/obj.d rt/qsort.d rt/sections.d rt/switch_.d rt/tlsgc.d \
- rt/util/array.d rt/util/container/array.d rt/util/container/common.d \
- rt/util/container/hashtab.d rt/util/container/treap.d rt/util/random.d \
- rt/util/typeinfo.d rt/util/utf.d
+ rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
+ rt/util/utility.d
-DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \
- core/stdcpp/typeinfo.d
+DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
+ core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
+ core/stdcpp/string.d core/stdcpp/string_view.d \
+ core/stdcpp/type_traits.d core/stdcpp/typeinfo.d core/stdcpp/utility.d \
+ core/stdcpp/vector.d core/stdcpp/xutility.d
DRUNTIME_DSOURCES_BIONIC = core/sys/bionic/err.d \
core/sys/bionic/fcntl.d core/sys/bionic/stdlib.d \
@@ -249,17 +269,19 @@ DRUNTIME_DSOURCES_FREEBSD = core/sys/freebsd/config.d \
DRUNTIME_DSOURCES_LINUX = core/sys/linux/config.d \
core/sys/linux/dlfcn.d core/sys/linux/elf.d core/sys/linux/epoll.d \
core/sys/linux/err.d core/sys/linux/errno.d core/sys/linux/execinfo.d \
- core/sys/linux/fcntl.d core/sys/linux/ifaddrs.d core/sys/linux/link.d \
+ core/sys/linux/fcntl.d core/sys/linux/fs.d core/sys/linux/ifaddrs.d \
+ core/sys/linux/io_uring.d core/sys/linux/link.d \
core/sys/linux/netinet/in_.d core/sys/linux/netinet/tcp.d \
- core/sys/linux/sched.d core/sys/linux/stdio.d core/sys/linux/string.d \
+ core/sys/linux/perf_event.d core/sys/linux/sched.d \
+ core/sys/linux/stdio.d core/sys/linux/string.d \
core/sys/linux/sys/auxv.d core/sys/linux/sys/eventfd.d \
core/sys/linux/sys/file.d core/sys/linux/sys/inotify.d \
core/sys/linux/sys/mman.d core/sys/linux/sys/prctl.d \
- core/sys/linux/sys/signalfd.d core/sys/linux/sys/socket.d \
- core/sys/linux/sys/sysinfo.d core/sys/linux/sys/time.d \
- core/sys/linux/sys/xattr.d core/sys/linux/termios.d \
- core/sys/linux/time.d core/sys/linux/timerfd.d core/sys/linux/tipc.d \
- core/sys/linux/unistd.d
+ core/sys/linux/sys/procfs.d core/sys/linux/sys/signalfd.d \
+ core/sys/linux/sys/socket.d core/sys/linux/sys/sysinfo.d \
+ core/sys/linux/sys/time.d core/sys/linux/sys/xattr.d \
+ core/sys/linux/termios.d core/sys/linux/time.d \
+ core/sys/linux/timerfd.d core/sys/linux/tipc.d core/sys/linux/unistd.d
DRUNTIME_DSOURCES_NETBSD = core/sys/netbsd/dlfcn.d \
core/sys/netbsd/err.d core/sys/netbsd/execinfo.d \
@@ -271,13 +293,13 @@ DRUNTIME_DSOURCES_NETBSD = core/sys/netbsd/dlfcn.d \
core/sys/netbsd/sys/sysctl.d core/sys/netbsd/time.d
DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \
- core/sys/openbsd/err.d core/sys/openbsd/stdlib.d \
- core/sys/openbsd/string.d core/sys/openbsd/sys/cdefs.d \
- core/sys/openbsd/sys/elf.d core/sys/openbsd/sys/elf32.d \
- core/sys/openbsd/sys/elf64.d core/sys/openbsd/sys/elf_common.d \
- core/sys/openbsd/sys/link_elf.d core/sys/openbsd/sys/mman.d \
- core/sys/openbsd/sys/sysctl.d core/sys/openbsd/time.d \
- core/sys/openbsd/unistd.d
+ core/sys/openbsd/err.d core/sys/openbsd/execinfo.d \
+ core/sys/openbsd/stdlib.d core/sys/openbsd/string.d \
+ core/sys/openbsd/sys/cdefs.d core/sys/openbsd/sys/elf.d \
+ core/sys/openbsd/sys/elf32.d core/sys/openbsd/sys/elf64.d \
+ core/sys/openbsd/sys/elf_common.d core/sys/openbsd/sys/link_elf.d \
+ core/sys/openbsd/sys/mman.d core/sys/openbsd/sys/sysctl.d \
+ core/sys/openbsd/time.d core/sys/openbsd/unistd.d
DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \
core/sys/posix/arpa/inet.d core/sys/posix/config.d \
@@ -402,4 +424,4 @@ DRUNTIME_DSOURCES_WINDOWS = core/sys/windows/accctrl.d \
core/sys/windows/winuser.d core/sys/windows/winver.d \
core/sys/windows/wtsapi32.d core/sys/windows/wtypes.d
-DRUNTIME_DISOURCES = __entrypoint.di __main.di
+DRUNTIME_DISOURCES = __main.di
diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in
index cb2e372bca0..b5f29da8540 100644
--- a/libphobos/libdruntime/Makefile.in
+++ b/libphobos/libdruntime/Makefile.in
@@ -188,47 +188,70 @@ LTLIBRARIES = $(noinst_LTLIBRARIES) $(toolexeclib_LTLIBRARIES)
am__DEPENDENCIES_1 =
am__dirstamp = $(am__leading_dot)dirstamp
am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \
- core/checkedint.lo core/cpuid.lo core/demangle.lo \
- core/exception.lo core/internal/abort.lo \
- core/internal/arrayop.lo core/internal/attributes.lo \
- core/internal/convert.lo core/internal/hash.lo \
+ core/builtins.lo core/checkedint.lo core/cpuid.lo \
+ core/demangle.lo core/exception.lo core/gc/config.lo \
+ core/gc/gcinterface.lo core/gc/registry.lo \
+ core/internal/abort.lo core/internal/array/appending.lo \
+ core/internal/array/capacity.lo core/internal/array/casting.lo \
+ core/internal/array/comparison.lo \
+ core/internal/array/concatenation.lo \
+ core/internal/array/construction.lo \
+ core/internal/array/equality.lo \
+ core/internal/array/operations.lo core/internal/array/utils.lo \
+ core/internal/atomic.lo core/internal/attributes.lo \
+ core/internal/container/array.lo \
+ core/internal/container/common.lo \
+ core/internal/container/hashtab.lo \
+ core/internal/container/treap.lo core/internal/convert.lo \
+ core/internal/dassert.lo core/internal/destruction.lo \
+ core/internal/entrypoint.lo core/internal/gc/bits.lo \
+ core/internal/gc/impl/conservative/gc.lo \
+ core/internal/gc/impl/manual/gc.lo \
+ core/internal/gc/impl/proto/gc.lo core/internal/gc/os.lo \
+ core/internal/gc/pooltable.lo core/internal/gc/proxy.lo \
+ core/internal/hash.lo core/internal/lifetime.lo \
+ core/internal/moving.lo core/internal/parseoptions.lo \
+ core/internal/postblit.lo core/internal/qsort.lo \
core/internal/spinlock.lo core/internal/string.lo \
- core/internal/traits.lo core/math.lo core/memory.lo \
- core/runtime.lo core/simd.lo core/stdc/assert_.lo \
- core/stdc/complex.lo core/stdc/config.lo core/stdc/ctype.lo \
- core/stdc/errno.lo core/stdc/fenv.lo core/stdc/float_.lo \
- core/stdc/inttypes.lo core/stdc/limits.lo core/stdc/locale.lo \
- core/stdc/math.lo core/stdc/signal.lo core/stdc/stdarg.lo \
- core/stdc/stddef.lo core/stdc/stdint.lo core/stdc/stdio.lo \
- core/stdc/stdlib.lo core/stdc/string.lo core/stdc/tgmath.lo \
- core/stdc/time.lo core/stdc/wchar_.lo core/stdc/wctype.lo \
- core/sync/barrier.lo core/sync/condition.lo \
- core/sync/config.lo core/sync/exception.lo core/sync/mutex.lo \
- core/sync/rwmutex.lo core/sync/semaphore.lo \
- core/thread/context.lo core/thread/fiber.lo \
- core/thread/osthread.lo core/thread/package.lo \
- core/thread/threadbase.lo core/thread/threadgroup.lo \
- core/thread/types.lo core/time.lo core/vararg.lo \
- core/volatile.lo gc/bits.lo gc/config.lo gc/gcinterface.lo \
- gc/impl/conservative/gc.lo gc/impl/manual/gc.lo gc/os.lo \
- gc/pooltable.lo gc/proxy.lo gcc/attribute.lo gcc/attributes.lo \
- gcc/backtrace.lo gcc/builtins.lo gcc/deh.lo gcc/emutls.lo \
- gcc/gthread.lo gcc/sections/common.lo gcc/sections/elf.lo \
- gcc/sections/macho.lo gcc/sections/package.lo \
- gcc/sections/pecoff.lo gcc/unwind/arm.lo \
- gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \
+ core/internal/switch_.lo core/internal/traits.lo \
+ core/internal/utf.lo core/internal/util/array.lo \
+ core/internal/util/math.lo core/lifetime.lo core/math.lo \
+ core/memory.lo core/runtime.lo core/simd.lo \
+ core/stdc/assert_.lo core/stdc/complex.lo core/stdc/config.lo \
+ core/stdc/ctype.lo core/stdc/errno.lo core/stdc/fenv.lo \
+ core/stdc/float_.lo core/stdc/inttypes.lo core/stdc/limits.lo \
+ core/stdc/locale.lo core/stdc/math.lo core/stdc/signal.lo \
+ core/stdc/stdarg.lo core/stdc/stddef.lo core/stdc/stdint.lo \
+ core/stdc/stdio.lo core/stdc/stdlib.lo core/stdc/string.lo \
+ core/stdc/tgmath.lo core/stdc/time.lo core/stdc/wchar_.lo \
+ core/stdc/wctype.lo core/sync/barrier.lo \
+ core/sync/condition.lo core/sync/config.lo core/sync/event.lo \
+ core/sync/exception.lo core/sync/mutex.lo core/sync/rwmutex.lo \
+ core/sync/semaphore.lo core/thread/context.lo \
+ core/thread/fiber.lo core/thread/osthread.lo \
+ core/thread/package.lo core/thread/threadbase.lo \
+ core/thread/threadgroup.lo core/thread/types.lo core/time.lo \
+ core/vararg.lo core/volatile.lo gcc/attribute.lo \
+ gcc/attributes.lo gcc/backtrace.lo gcc/builtins.lo gcc/deh.lo \
+ gcc/emutls.lo gcc/gthread.lo gcc/sections/common.lo \
+ gcc/sections/elf.lo gcc/sections/macho.lo \
+ gcc/sections/package.lo gcc/sections/pecoff.lo \
+ gcc/unwind/arm.lo gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \
gcc/unwind/generic.lo gcc/unwind/package.lo gcc/unwind/pe.lo \
object.lo rt/aApply.lo rt/aApplyR.lo rt/aaA.lo rt/adi.lo \
- rt/arrayassign.lo rt/arraycast.lo rt/arraycat.lo rt/cast_.lo \
- rt/config.lo rt/critical_.lo rt/deh.lo rt/dmain2.lo \
+ rt/arrayassign.lo rt/arraycat.lo rt/cast_.lo rt/config.lo \
+ rt/critical_.lo rt/deh.lo rt/dmain2.lo rt/ehalloc.lo \
rt/invariant.lo rt/lifetime.lo rt/memory.lo rt/minfo.lo \
- rt/monitor_.lo rt/obj.lo rt/qsort.lo rt/sections.lo \
- rt/switch_.lo rt/tlsgc.lo rt/util/array.lo \
- rt/util/container/array.lo rt/util/container/common.lo \
- rt/util/container/hashtab.lo rt/util/container/treap.lo \
- rt/util/random.lo rt/util/typeinfo.lo rt/util/utf.lo
+ rt/monitor_.lo rt/profilegc.lo rt/sections.lo rt/tlsgc.lo \
+ rt/util/typeinfo.lo rt/util/utility.lo
am__objects_2 = core/stdc/libgdruntime_la-errno_.lo
-am__objects_3 = core/sys/posix/aio.lo core/sys/posix/arpa/inet.lo \
+am__objects_3 = core/stdcpp/allocator.lo core/stdcpp/array.lo \
+ core/stdcpp/exception.lo core/stdcpp/memory.lo \
+ core/stdcpp/new_.lo core/stdcpp/string.lo \
+ core/stdcpp/string_view.lo core/stdcpp/type_traits.lo \
+ core/stdcpp/typeinfo.lo core/stdcpp/utility.lo \
+ core/stdcpp/vector.lo core/stdcpp/xutility.lo
+am__objects_4 = core/sys/posix/aio.lo core/sys/posix/arpa/inet.lo \
core/sys/posix/config.lo core/sys/posix/dirent.lo \
core/sys/posix/dlfcn.lo core/sys/posix/fcntl.lo \
core/sys/posix/grp.lo core/sys/posix/iconv.lo \
@@ -255,8 +278,8 @@ am__objects_3 = core/sys/posix/aio.lo core/sys/posix/arpa/inet.lo \
core/sys/posix/syslog.lo core/sys/posix/termios.lo \
core/sys/posix/time.lo core/sys/posix/ucontext.lo \
core/sys/posix/unistd.lo core/sys/posix/utime.lo
-@DRUNTIME_OS_POSIX_TRUE@am__objects_4 = $(am__objects_3)
-am__objects_5 = core/sys/darwin/config.lo \
+@DRUNTIME_OS_POSIX_TRUE@am__objects_5 = $(am__objects_4)
+am__objects_6 = core/sys/darwin/config.lo \
core/sys/darwin/crt_externs.lo core/sys/darwin/dlfcn.lo \
core/sys/darwin/err.lo core/sys/darwin/execinfo.lo \
core/sys/darwin/fcntl.lo core/sys/darwin/ifaddrs.lo \
@@ -271,8 +294,8 @@ am__objects_5 = core/sys/darwin/config.lo \
core/sys/darwin/sys/attr.lo core/sys/darwin/sys/cdefs.lo \
core/sys/darwin/sys/event.lo core/sys/darwin/sys/mman.lo \
core/sys/darwin/sys/sysctl.lo
-@DRUNTIME_OS_DARWIN_TRUE@am__objects_6 = $(am__objects_5)
-am__objects_7 = core/sys/dragonflybsd/dlfcn.lo \
+@DRUNTIME_OS_DARWIN_TRUE@am__objects_7 = $(am__objects_6)
+am__objects_8 = core/sys/dragonflybsd/dlfcn.lo \
core/sys/dragonflybsd/err.lo core/sys/dragonflybsd/execinfo.lo \
core/sys/dragonflybsd/netinet/in_.lo \
core/sys/dragonflybsd/pthread_np.lo \
@@ -291,12 +314,12 @@ am__objects_7 = core/sys/dragonflybsd/dlfcn.lo \
core/sys/dragonflybsd/sys/socket.lo \
core/sys/dragonflybsd/sys/sysctl.lo \
core/sys/dragonflybsd/time.lo
-@DRUNTIME_OS_DRAGONFLYBSD_TRUE@am__objects_8 = $(am__objects_7)
-am__objects_9 = core/sys/bionic/err.lo core/sys/bionic/fcntl.lo \
+@DRUNTIME_OS_DRAGONFLYBSD_TRUE@am__objects_9 = $(am__objects_8)
+am__objects_10 = core/sys/bionic/err.lo core/sys/bionic/fcntl.lo \
core/sys/bionic/stdlib.lo core/sys/bionic/string.lo \
core/sys/bionic/unistd.lo
-@DRUNTIME_OS_ANDROID_TRUE@am__objects_10 = $(am__objects_9)
-am__objects_11 = core/sys/freebsd/config.lo core/sys/freebsd/dlfcn.lo \
+@DRUNTIME_OS_ANDROID_TRUE@am__objects_11 = $(am__objects_10)
+am__objects_12 = core/sys/freebsd/config.lo core/sys/freebsd/dlfcn.lo \
core/sys/freebsd/err.lo core/sys/freebsd/execinfo.lo \
core/sys/freebsd/netinet/in_.lo core/sys/freebsd/pthread_np.lo \
core/sys/freebsd/stdlib.lo core/sys/freebsd/string.lo \
@@ -309,8 +332,8 @@ am__objects_11 = core/sys/freebsd/config.lo core/sys/freebsd/dlfcn.lo \
core/sys/freebsd/sys/mman.lo core/sys/freebsd/sys/mount.lo \
core/sys/freebsd/sys/sysctl.lo core/sys/freebsd/time.lo \
core/sys/freebsd/unistd.lo
-@DRUNTIME_OS_FREEBSD_TRUE@am__objects_12 = $(am__objects_11)
-am__objects_13 = core/sys/netbsd/dlfcn.lo core/sys/netbsd/err.lo \
+@DRUNTIME_OS_FREEBSD_TRUE@am__objects_13 = $(am__objects_12)
+am__objects_14 = core/sys/netbsd/dlfcn.lo core/sys/netbsd/err.lo \
core/sys/netbsd/execinfo.lo core/sys/netbsd/stdlib.lo \
core/sys/netbsd/string.lo core/sys/netbsd/sys/elf.lo \
core/sys/netbsd/sys/elf32.lo core/sys/netbsd/sys/elf64.lo \
@@ -318,34 +341,37 @@ am__objects_13 = core/sys/netbsd/dlfcn.lo core/sys/netbsd/err.lo \
core/sys/netbsd/sys/featuretest.lo \
core/sys/netbsd/sys/link_elf.lo core/sys/netbsd/sys/mman.lo \
core/sys/netbsd/sys/sysctl.lo core/sys/netbsd/time.lo
-@DRUNTIME_OS_NETBSD_TRUE@am__objects_14 = $(am__objects_13)
-am__objects_15 = core/sys/openbsd/dlfcn.lo core/sys/openbsd/err.lo \
- core/sys/openbsd/stdlib.lo core/sys/openbsd/string.lo \
- core/sys/openbsd/sys/cdefs.lo core/sys/openbsd/sys/elf.lo \
- core/sys/openbsd/sys/elf32.lo core/sys/openbsd/sys/elf64.lo \
+@DRUNTIME_OS_NETBSD_TRUE@am__objects_15 = $(am__objects_14)
+am__objects_16 = core/sys/openbsd/dlfcn.lo core/sys/openbsd/err.lo \
+ core/sys/openbsd/execinfo.lo core/sys/openbsd/stdlib.lo \
+ core/sys/openbsd/string.lo core/sys/openbsd/sys/cdefs.lo \
+ core/sys/openbsd/sys/elf.lo core/sys/openbsd/sys/elf32.lo \
+ core/sys/openbsd/sys/elf64.lo \
core/sys/openbsd/sys/elf_common.lo \
core/sys/openbsd/sys/link_elf.lo core/sys/openbsd/sys/mman.lo \
core/sys/openbsd/sys/sysctl.lo core/sys/openbsd/time.lo \
core/sys/openbsd/unistd.lo
-@DRUNTIME_OS_OPENBSD_TRUE@am__objects_16 = $(am__objects_15)
-am__objects_17 = core/sys/linux/config.lo core/sys/linux/dlfcn.lo \
+@DRUNTIME_OS_OPENBSD_TRUE@am__objects_17 = $(am__objects_16)
+am__objects_18 = core/sys/linux/config.lo core/sys/linux/dlfcn.lo \
core/sys/linux/elf.lo core/sys/linux/epoll.lo \
core/sys/linux/err.lo core/sys/linux/errno.lo \
core/sys/linux/execinfo.lo core/sys/linux/fcntl.lo \
- core/sys/linux/ifaddrs.lo core/sys/linux/link.lo \
+ core/sys/linux/fs.lo core/sys/linux/ifaddrs.lo \
+ core/sys/linux/io_uring.lo core/sys/linux/link.lo \
core/sys/linux/netinet/in_.lo core/sys/linux/netinet/tcp.lo \
- core/sys/linux/sched.lo core/sys/linux/stdio.lo \
- core/sys/linux/string.lo core/sys/linux/sys/auxv.lo \
- core/sys/linux/sys/eventfd.lo core/sys/linux/sys/file.lo \
- core/sys/linux/sys/inotify.lo core/sys/linux/sys/mman.lo \
- core/sys/linux/sys/prctl.lo core/sys/linux/sys/signalfd.lo \
+ core/sys/linux/perf_event.lo core/sys/linux/sched.lo \
+ core/sys/linux/stdio.lo core/sys/linux/string.lo \
+ core/sys/linux/sys/auxv.lo core/sys/linux/sys/eventfd.lo \
+ core/sys/linux/sys/file.lo core/sys/linux/sys/inotify.lo \
+ core/sys/linux/sys/mman.lo core/sys/linux/sys/prctl.lo \
+ core/sys/linux/sys/procfs.lo core/sys/linux/sys/signalfd.lo \
core/sys/linux/sys/socket.lo core/sys/linux/sys/sysinfo.lo \
core/sys/linux/sys/time.lo core/sys/linux/sys/xattr.lo \
core/sys/linux/termios.lo core/sys/linux/time.lo \
core/sys/linux/timerfd.lo core/sys/linux/tipc.lo \
core/sys/linux/unistd.lo
-@DRUNTIME_OS_LINUX_TRUE@am__objects_18 = $(am__objects_17)
-am__objects_19 = core/sys/windows/accctrl.lo \
+@DRUNTIME_OS_LINUX_TRUE@am__objects_19 = $(am__objects_18)
+am__objects_20 = core/sys/windows/accctrl.lo \
core/sys/windows/aclapi.lo core/sys/windows/aclui.lo \
core/sys/windows/basetsd.lo core/sys/windows/basetyps.lo \
core/sys/windows/cderr.lo core/sys/windows/cguid.lo \
@@ -430,9 +456,9 @@ am__objects_19 = core/sys/windows/accctrl.lo \
core/sys/windows/winsvc.lo core/sys/windows/winuser.lo \
core/sys/windows/winver.lo core/sys/windows/wtsapi32.lo \
core/sys/windows/wtypes.lo
-@DRUNTIME_OS_MINGW_TRUE@am__objects_20 = $(am__objects_19) \
+@DRUNTIME_OS_MINGW_TRUE@am__objects_21 = $(am__objects_20) \
@DRUNTIME_OS_MINGW_TRUE@ config/mingw/libgdruntime_la-msvc.lo
-am__objects_21 = core/sys/solaris/dlfcn.lo core/sys/solaris/elf.lo \
+am__objects_22 = core/sys/solaris/dlfcn.lo core/sys/solaris/elf.lo \
core/sys/solaris/err.lo core/sys/solaris/execinfo.lo \
core/sys/solaris/libelf.lo core/sys/solaris/link.lo \
core/sys/solaris/stdlib.lo core/sys/solaris/sys/elf.lo \
@@ -444,48 +470,48 @@ am__objects_21 = core/sys/solaris/dlfcn.lo core/sys/solaris/elf.lo \
core/sys/solaris/sys/priocntl.lo \
core/sys/solaris/sys/procset.lo core/sys/solaris/sys/types.lo \
core/sys/solaris/time.lo
-@DRUNTIME_OS_SOLARIS_TRUE@am__objects_22 = $(am__objects_21)
-@DRUNTIME_CPU_AARCH64_TRUE@am__objects_23 = config/aarch64/libgdruntime_la-switchcontext.lo
-@DRUNTIME_CPU_ARM_TRUE@am__objects_24 = config/arm/libgdruntime_la-switchcontext.lo
-@DRUNTIME_CPU_MIPS_TRUE@am__objects_25 = config/mips/libgdruntime_la-switchcontext.lo
-@DRUNTIME_CPU_POWERPC_TRUE@am__objects_26 = config/powerpc/libgdruntime_la-switchcontext.lo
-@DRUNTIME_CPU_X86_TRUE@@DRUNTIME_OS_MINGW_TRUE@am__objects_27 = config/mingw/libgdruntime_la-switchcontext.lo
-@DRUNTIME_CPU_X86_TRUE@@DRUNTIME_OS_MINGW_FALSE@am__objects_28 = config/x86/libgdruntime_la-switchcontext.lo
-@DRUNTIME_CPU_SYSTEMZ_TRUE@am__objects_29 = config/systemz/libgdruntime_la-get_tls_offset.lo
-@DRUNTIME_CPU_S390_TRUE@am__objects_30 = config/s390/libgdruntime_la-get_tls_offset.lo
-am__objects_31 = $(am__objects_4) $(am__objects_6) $(am__objects_8) \
- $(am__objects_10) $(am__objects_12) $(am__objects_14) \
- $(am__objects_16) $(am__objects_18) $(am__objects_20) \
- $(am__objects_22) $(am__objects_23) $(am__objects_24) \
- $(am__objects_25) $(am__objects_26) $(am__objects_27) \
- $(am__objects_28) $(am__objects_29) $(am__objects_30)
-am__objects_32 = gcc/config.lo gcc/libbacktrace.lo
-am__objects_33 = $(am__objects_1) $(am__objects_2) $(am__objects_31) \
- $(am__objects_32)
-am_libgdruntime_la_OBJECTS = $(am__objects_33)
+@DRUNTIME_OS_SOLARIS_TRUE@am__objects_23 = $(am__objects_22)
+@DRUNTIME_CPU_AARCH64_TRUE@am__objects_24 = config/aarch64/libgdruntime_la-switchcontext.lo
+@DRUNTIME_CPU_ARM_TRUE@am__objects_25 = config/arm/libgdruntime_la-switchcontext.lo
+@DRUNTIME_CPU_MIPS_TRUE@am__objects_26 = config/mips/libgdruntime_la-switchcontext.lo
+@DRUNTIME_CPU_POWERPC_TRUE@am__objects_27 = config/powerpc/libgdruntime_la-switchcontext.lo
+@DRUNTIME_CPU_X86_TRUE@@DRUNTIME_OS_MINGW_TRUE@am__objects_28 = config/mingw/libgdruntime_la-switchcontext.lo
+@DRUNTIME_CPU_X86_TRUE@@DRUNTIME_OS_MINGW_FALSE@am__objects_29 = config/x86/libgdruntime_la-switchcontext.lo
+@DRUNTIME_CPU_SYSTEMZ_TRUE@am__objects_30 = config/systemz/libgdruntime_la-get_tls_offset.lo
+@DRUNTIME_CPU_S390_TRUE@am__objects_31 = config/s390/libgdruntime_la-get_tls_offset.lo
+am__objects_32 = $(am__objects_5) $(am__objects_7) $(am__objects_9) \
+ $(am__objects_11) $(am__objects_13) $(am__objects_15) \
+ $(am__objects_17) $(am__objects_19) $(am__objects_21) \
+ $(am__objects_23) $(am__objects_24) $(am__objects_25) \
+ $(am__objects_26) $(am__objects_27) $(am__objects_28) \
+ $(am__objects_29) $(am__objects_30) $(am__objects_31)
+am__objects_33 = gcc/config.lo gcc/libbacktrace.lo
+am__objects_34 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \
+ $(am__objects_32) $(am__objects_33)
+am_libgdruntime_la_OBJECTS = $(am__objects_34)
libgdruntime_la_OBJECTS = $(am_libgdruntime_la_OBJECTS)
am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
-am__objects_34 = core/stdc/libgdruntime_convenience_la-errno_.lo
-@DRUNTIME_OS_MINGW_TRUE@am__objects_35 = $(am__objects_19) \
+am__objects_35 = core/stdc/libgdruntime_convenience_la-errno_.lo
+@DRUNTIME_OS_MINGW_TRUE@am__objects_36 = $(am__objects_20) \
@DRUNTIME_OS_MINGW_TRUE@ config/mingw/libgdruntime_convenience_la-msvc.lo
-@DRUNTIME_CPU_AARCH64_TRUE@am__objects_36 = config/aarch64/libgdruntime_convenience_la-switchcontext.lo
-@DRUNTIME_CPU_ARM_TRUE@am__objects_37 = config/arm/libgdruntime_convenience_la-switchcontext.lo
-@DRUNTIME_CPU_MIPS_TRUE@am__objects_38 = config/mips/libgdruntime_convenience_la-switchcontext.lo
-@DRUNTIME_CPU_POWERPC_TRUE@am__objects_39 = config/powerpc/libgdruntime_convenience_la-switchcontext.lo
-@DRUNTIME_CPU_X86_TRUE@@DRUNTIME_OS_MINGW_TRUE@am__objects_40 = config/mingw/libgdruntime_convenience_la-switchcontext.lo
-@DRUNTIME_CPU_X86_TRUE@@DRUNTIME_OS_MINGW_FALSE@am__objects_41 = config/x86/libgdruntime_convenience_la-switchcontext.lo
-@DRUNTIME_CPU_SYSTEMZ_TRUE@am__objects_42 = config/systemz/libgdruntime_convenience_la-get_tls_offset.lo
-@DRUNTIME_CPU_S390_TRUE@am__objects_43 = config/s390/libgdruntime_convenience_la-get_tls_offset.lo
-am__objects_44 = $(am__objects_4) $(am__objects_6) $(am__objects_8) \
- $(am__objects_10) $(am__objects_12) $(am__objects_14) \
- $(am__objects_16) $(am__objects_18) $(am__objects_35) \
- $(am__objects_22) $(am__objects_36) $(am__objects_37) \
- $(am__objects_38) $(am__objects_39) $(am__objects_40) \
- $(am__objects_41) $(am__objects_42) $(am__objects_43)
-am__objects_45 = $(am__objects_1) $(am__objects_34) $(am__objects_44) \
- $(am__objects_32)
-am__objects_46 = $(am__objects_45)
-am_libgdruntime_convenience_la_OBJECTS = $(am__objects_46)
+@DRUNTIME_CPU_AARCH64_TRUE@am__objects_37 = config/aarch64/libgdruntime_convenience_la-switchcontext.lo
+@DRUNTIME_CPU_ARM_TRUE@am__objects_38 = config/arm/libgdruntime_convenience_la-switchcontext.lo
+@DRUNTIME_CPU_MIPS_TRUE@am__objects_39 = config/mips/libgdruntime_convenience_la-switchcontext.lo
+@DRUNTIME_CPU_POWERPC_TRUE@am__objects_40 = config/powerpc/libgdruntime_convenience_la-switchcontext.lo
+@DRUNTIME_CPU_X86_TRUE@@DRUNTIME_OS_MINGW_TRUE@am__objects_41 = config/mingw/libgdruntime_convenience_la-switchcontext.lo
+@DRUNTIME_CPU_X86_TRUE@@DRUNTIME_OS_MINGW_FALSE@am__objects_42 = config/x86/libgdruntime_convenience_la-switchcontext.lo
+@DRUNTIME_CPU_SYSTEMZ_TRUE@am__objects_43 = config/systemz/libgdruntime_convenience_la-get_tls_offset.lo
+@DRUNTIME_CPU_S390_TRUE@am__objects_44 = config/s390/libgdruntime_convenience_la-get_tls_offset.lo
+am__objects_45 = $(am__objects_5) $(am__objects_7) $(am__objects_9) \
+ $(am__objects_11) $(am__objects_13) $(am__objects_15) \
+ $(am__objects_17) $(am__objects_19) $(am__objects_36) \
+ $(am__objects_23) $(am__objects_37) $(am__objects_38) \
+ $(am__objects_39) $(am__objects_40) $(am__objects_41) \
+ $(am__objects_42) $(am__objects_43) $(am__objects_44)
+am__objects_46 = $(am__objects_1) $(am__objects_35) $(am__objects_3) \
+ $(am__objects_45) $(am__objects_33)
+am__objects_47 = $(am__objects_46)
+am_libgdruntime_convenience_la_OBJECTS = $(am__objects_47)
libgdruntime_convenience_la_OBJECTS = \
$(am_libgdruntime_convenience_la_OBJECTS)
AM_V_P = $(am__v_P_@AM_V@)
@@ -728,7 +754,9 @@ LTDCOMPILE = $(LIBTOOL) --tag=D $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
# Include D build rules
# Make sure GDC can find libdruntime include files
-D_EXTRA_DFLAGS = -nostdinc -I $(srcdir) -I .
+D_EXTRA_DFLAGS = -fpreview=dip1000 -fpreview=fieldwise -fpreview=dtorfields \
+ -nostdinc -I $(srcdir) -I .
+
# D flags for compilation
AM_DFLAGS = \
@@ -767,6 +795,7 @@ DRUNTIME_SOURCES_CONFIGURED = $(am__append_1) $(am__append_2) \
# Generated by configure
DRUNTIME_DSOURCES_GENERATED = gcc/config.d gcc/libbacktrace.d
ALL_DRUNTIME_SOURCES = $(DRUNTIME_DSOURCES) $(DRUNTIME_CSOURCES) \
+ $(DRUNTIME_DSOURCES_STDCXX) \
$(DRUNTIME_SOURCES_CONFIGURED) $(DRUNTIME_DSOURCES_GENERATED)
@@ -803,12 +832,30 @@ libgdruntime_convenience_la_LINK = $(libgdruntime_la_LINK)
# https://www.gnu.org/software/automake/manual/html_node/Wildcards.html
DRUNTIME_CSOURCES = core/stdc/errno_.c
DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
- core/checkedint.d core/cpuid.d core/demangle.d core/exception.d \
- core/internal/abort.d core/internal/arrayop.d \
- core/internal/attributes.d core/internal/convert.d \
- core/internal/hash.d core/internal/spinlock.d core/internal/string.d \
- core/internal/traits.d core/math.d core/memory.d core/runtime.d \
- core/simd.d core/stdc/assert_.d core/stdc/complex.d core/stdc/config.d \
+ core/builtins.d core/checkedint.d core/cpuid.d core/demangle.d \
+ core/exception.d core/gc/config.d core/gc/gcinterface.d \
+ core/gc/registry.d core/internal/abort.d \
+ core/internal/array/appending.d core/internal/array/capacity.d \
+ core/internal/array/casting.d core/internal/array/comparison.d \
+ core/internal/array/concatenation.d core/internal/array/construction.d \
+ core/internal/array/equality.d core/internal/array/operations.d \
+ core/internal/array/utils.d core/internal/atomic.d \
+ core/internal/attributes.d core/internal/container/array.d \
+ core/internal/container/common.d core/internal/container/hashtab.d \
+ core/internal/container/treap.d core/internal/convert.d \
+ core/internal/dassert.d core/internal/destruction.d \
+ core/internal/entrypoint.d core/internal/gc/bits.d \
+ core/internal/gc/impl/conservative/gc.d \
+ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \
+ core/internal/gc/os.d core/internal/gc/pooltable.d \
+ core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \
+ core/internal/moving.d core/internal/parseoptions.d \
+ core/internal/postblit.d core/internal/qsort.d \
+ core/internal/spinlock.d core/internal/string.d \
+ core/internal/switch_.d core/internal/traits.d core/internal/utf.d \
+ core/internal/util/array.d core/internal/util/math.d core/lifetime.d \
+ core/math.d core/memory.d core/runtime.d core/simd.d \
+ core/stdc/assert_.d core/stdc/complex.d core/stdc/config.d \
core/stdc/ctype.d core/stdc/errno.d core/stdc/fenv.d \
core/stdc/float_.d core/stdc/inttypes.d core/stdc/limits.d \
core/stdc/locale.d core/stdc/math.d core/stdc/signal.d \
@@ -816,28 +863,28 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
core/stdc/stdio.d core/stdc/stdlib.d core/stdc/string.d \
core/stdc/tgmath.d core/stdc/time.d core/stdc/wchar_.d \
core/stdc/wctype.d core/sync/barrier.d core/sync/condition.d \
- core/sync/config.d core/sync/exception.d core/sync/mutex.d \
- core/sync/rwmutex.d core/sync/semaphore.d core/thread/context.d \
- core/thread/fiber.d core/thread/osthread.d core/thread/package.d \
- core/thread/threadbase.d core/thread/threadgroup.d core/thread/types.d \
- core/time.d core/vararg.d core/volatile.d gc/bits.d gc/config.d \
- gc/gcinterface.d gc/impl/conservative/gc.d gc/impl/manual/gc.d gc/os.d \
- gc/pooltable.d gc/proxy.d gcc/attribute.d gcc/attributes.d \
+ core/sync/config.d core/sync/event.d core/sync/exception.d \
+ core/sync/mutex.d core/sync/rwmutex.d core/sync/semaphore.d \
+ core/thread/context.d core/thread/fiber.d core/thread/osthread.d \
+ core/thread/package.d core/thread/threadbase.d \
+ core/thread/threadgroup.d core/thread/types.d core/time.d \
+ core/vararg.d core/volatile.d gcc/attribute.d gcc/attributes.d \
gcc/backtrace.d gcc/builtins.d gcc/deh.d gcc/emutls.d gcc/gthread.d \
gcc/sections/common.d gcc/sections/elf.d gcc/sections/macho.d \
gcc/sections/package.d gcc/sections/pecoff.d gcc/unwind/arm.d \
gcc/unwind/arm_common.d gcc/unwind/c6x.d gcc/unwind/generic.d \
gcc/unwind/package.d gcc/unwind/pe.d object.d rt/aApply.d rt/aApplyR.d \
- rt/aaA.d rt/adi.d rt/arrayassign.d rt/arraycast.d rt/arraycat.d \
- rt/cast_.d rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d \
+ rt/aaA.d rt/adi.d rt/arrayassign.d rt/arraycat.d rt/cast_.d \
+ rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
rt/invariant.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
- rt/obj.d rt/qsort.d rt/sections.d rt/switch_.d rt/tlsgc.d \
- rt/util/array.d rt/util/container/array.d rt/util/container/common.d \
- rt/util/container/hashtab.d rt/util/container/treap.d rt/util/random.d \
- rt/util/typeinfo.d rt/util/utf.d
+ rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
+ rt/util/utility.d
-DRUNTIME_DSOURCES_STDCXX = core/stdcpp/exception.d \
- core/stdcpp/typeinfo.d
+DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
+ core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
+ core/stdcpp/string.d core/stdcpp/string_view.d \
+ core/stdcpp/type_traits.d core/stdcpp/typeinfo.d core/stdcpp/utility.d \
+ core/stdcpp/vector.d core/stdcpp/xutility.d
DRUNTIME_DSOURCES_BIONIC = core/sys/bionic/err.d \
core/sys/bionic/fcntl.d core/sys/bionic/stdlib.d \
@@ -886,17 +933,19 @@ DRUNTIME_DSOURCES_FREEBSD = core/sys/freebsd/config.d \
DRUNTIME_DSOURCES_LINUX = core/sys/linux/config.d \
core/sys/linux/dlfcn.d core/sys/linux/elf.d core/sys/linux/epoll.d \
core/sys/linux/err.d core/sys/linux/errno.d core/sys/linux/execinfo.d \
- core/sys/linux/fcntl.d core/sys/linux/ifaddrs.d core/sys/linux/link.d \
+ core/sys/linux/fcntl.d core/sys/linux/fs.d core/sys/linux/ifaddrs.d \
+ core/sys/linux/io_uring.d core/sys/linux/link.d \
core/sys/linux/netinet/in_.d core/sys/linux/netinet/tcp.d \
- core/sys/linux/sched.d core/sys/linux/stdio.d core/sys/linux/string.d \
+ core/sys/linux/perf_event.d core/sys/linux/sched.d \
+ core/sys/linux/stdio.d core/sys/linux/string.d \
core/sys/linux/sys/auxv.d core/sys/linux/sys/eventfd.d \
core/sys/linux/sys/file.d core/sys/linux/sys/inotify.d \
core/sys/linux/sys/mman.d core/sys/linux/sys/prctl.d \
- core/sys/linux/sys/signalfd.d core/sys/linux/sys/socket.d \
- core/sys/linux/sys/sysinfo.d core/sys/linux/sys/time.d \
- core/sys/linux/sys/xattr.d core/sys/linux/termios.d \
- core/sys/linux/time.d core/sys/linux/timerfd.d core/sys/linux/tipc.d \
- core/sys/linux/unistd.d
+ core/sys/linux/sys/procfs.d core/sys/linux/sys/signalfd.d \
+ core/sys/linux/sys/socket.d core/sys/linux/sys/sysinfo.d \
+ core/sys/linux/sys/time.d core/sys/linux/sys/xattr.d \
+ core/sys/linux/termios.d core/sys/linux/time.d \
+ core/sys/linux/timerfd.d core/sys/linux/tipc.d core/sys/linux/unistd.d
DRUNTIME_DSOURCES_NETBSD = core/sys/netbsd/dlfcn.d \
core/sys/netbsd/err.d core/sys/netbsd/execinfo.d \
@@ -908,13 +957,13 @@ DRUNTIME_DSOURCES_NETBSD = core/sys/netbsd/dlfcn.d \
core/sys/netbsd/sys/sysctl.d core/sys/netbsd/time.d
DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \
- core/sys/openbsd/err.d core/sys/openbsd/stdlib.d \
- core/sys/openbsd/string.d core/sys/openbsd/sys/cdefs.d \
- core/sys/openbsd/sys/elf.d core/sys/openbsd/sys/elf32.d \
- core/sys/openbsd/sys/elf64.d core/sys/openbsd/sys/elf_common.d \
- core/sys/openbsd/sys/link_elf.d core/sys/openbsd/sys/mman.d \
- core/sys/openbsd/sys/sysctl.d core/sys/openbsd/time.d \
- core/sys/openbsd/unistd.d
+ core/sys/openbsd/err.d core/sys/openbsd/execinfo.d \
+ core/sys/openbsd/stdlib.d core/sys/openbsd/string.d \
+ core/sys/openbsd/sys/cdefs.d core/sys/openbsd/sys/elf.d \
+ core/sys/openbsd/sys/elf32.d core/sys/openbsd/sys/elf64.d \
+ core/sys/openbsd/sys/elf_common.d core/sys/openbsd/sys/link_elf.d \
+ core/sys/openbsd/sys/mman.d core/sys/openbsd/sys/sysctl.d \
+ core/sys/openbsd/time.d core/sys/openbsd/unistd.d
DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \
core/sys/posix/arpa/inet.d core/sys/posix/config.d \
@@ -1039,7 +1088,7 @@ DRUNTIME_DSOURCES_WINDOWS = core/sys/windows/accctrl.d \
core/sys/windows/winuser.d core/sys/windows/winver.d \
core/sys/windows/wtsapi32.d core/sys/windows/wtypes.d
-DRUNTIME_DISOURCES = __entrypoint.di __main.di
+DRUNTIME_DISOURCES = __main.di
all: all-am
.SUFFIXES:
@@ -1126,21 +1175,93 @@ core/$(am__dirstamp):
core/atomic.lo: core/$(am__dirstamp)
core/attribute.lo: core/$(am__dirstamp)
core/bitop.lo: core/$(am__dirstamp)
+core/builtins.lo: core/$(am__dirstamp)
core/checkedint.lo: core/$(am__dirstamp)
core/cpuid.lo: core/$(am__dirstamp)
core/demangle.lo: core/$(am__dirstamp)
core/exception.lo: core/$(am__dirstamp)
+core/gc/$(am__dirstamp):
+ @$(MKDIR_P) core/gc
+ @: > core/gc/$(am__dirstamp)
+core/gc/config.lo: core/gc/$(am__dirstamp)
+core/gc/gcinterface.lo: core/gc/$(am__dirstamp)
+core/gc/registry.lo: core/gc/$(am__dirstamp)
core/internal/$(am__dirstamp):
@$(MKDIR_P) core/internal
@: > core/internal/$(am__dirstamp)
core/internal/abort.lo: core/internal/$(am__dirstamp)
-core/internal/arrayop.lo: core/internal/$(am__dirstamp)
+core/internal/array/$(am__dirstamp):
+ @$(MKDIR_P) core/internal/array
+ @: > core/internal/array/$(am__dirstamp)
+core/internal/array/appending.lo: core/internal/array/$(am__dirstamp)
+core/internal/array/capacity.lo: core/internal/array/$(am__dirstamp)
+core/internal/array/casting.lo: core/internal/array/$(am__dirstamp)
+core/internal/array/comparison.lo: \
+ core/internal/array/$(am__dirstamp)
+core/internal/array/concatenation.lo: \
+ core/internal/array/$(am__dirstamp)
+core/internal/array/construction.lo: \
+ core/internal/array/$(am__dirstamp)
+core/internal/array/equality.lo: core/internal/array/$(am__dirstamp)
+core/internal/array/operations.lo: \
+ core/internal/array/$(am__dirstamp)
+core/internal/array/utils.lo: core/internal/array/$(am__dirstamp)
+core/internal/atomic.lo: core/internal/$(am__dirstamp)
core/internal/attributes.lo: core/internal/$(am__dirstamp)
+core/internal/container/$(am__dirstamp):
+ @$(MKDIR_P) core/internal/container
+ @: > core/internal/container/$(am__dirstamp)
+core/internal/container/array.lo: \
+ core/internal/container/$(am__dirstamp)
+core/internal/container/common.lo: \
+ core/internal/container/$(am__dirstamp)
+core/internal/container/hashtab.lo: \
+ core/internal/container/$(am__dirstamp)
+core/internal/container/treap.lo: \
+ core/internal/container/$(am__dirstamp)
core/internal/convert.lo: core/internal/$(am__dirstamp)
+core/internal/dassert.lo: core/internal/$(am__dirstamp)
+core/internal/destruction.lo: core/internal/$(am__dirstamp)
+core/internal/entrypoint.lo: core/internal/$(am__dirstamp)
+core/internal/gc/$(am__dirstamp):
+ @$(MKDIR_P) core/internal/gc
+ @: > core/internal/gc/$(am__dirstamp)
+core/internal/gc/bits.lo: core/internal/gc/$(am__dirstamp)
+core/internal/gc/impl/conservative/$(am__dirstamp):
+ @$(MKDIR_P) core/internal/gc/impl/conservative
+ @: > core/internal/gc/impl/conservative/$(am__dirstamp)
+core/internal/gc/impl/conservative/gc.lo: \
+ core/internal/gc/impl/conservative/$(am__dirstamp)
+core/internal/gc/impl/manual/$(am__dirstamp):
+ @$(MKDIR_P) core/internal/gc/impl/manual
+ @: > core/internal/gc/impl/manual/$(am__dirstamp)
+core/internal/gc/impl/manual/gc.lo: \
+ core/internal/gc/impl/manual/$(am__dirstamp)
+core/internal/gc/impl/proto/$(am__dirstamp):
+ @$(MKDIR_P) core/internal/gc/impl/proto
+ @: > core/internal/gc/impl/proto/$(am__dirstamp)
+core/internal/gc/impl/proto/gc.lo: \
+ core/internal/gc/impl/proto/$(am__dirstamp)
+core/internal/gc/os.lo: core/internal/gc/$(am__dirstamp)
+core/internal/gc/pooltable.lo: core/internal/gc/$(am__dirstamp)
+core/internal/gc/proxy.lo: core/internal/gc/$(am__dirstamp)
core/internal/hash.lo: core/internal/$(am__dirstamp)
+core/internal/lifetime.lo: core/internal/$(am__dirstamp)
+core/internal/moving.lo: core/internal/$(am__dirstamp)
+core/internal/parseoptions.lo: core/internal/$(am__dirstamp)
+core/internal/postblit.lo: core/internal/$(am__dirstamp)
+core/internal/qsort.lo: core/internal/$(am__dirstamp)
core/internal/spinlock.lo: core/internal/$(am__dirstamp)
core/internal/string.lo: core/internal/$(am__dirstamp)
+core/internal/switch_.lo: core/internal/$(am__dirstamp)
core/internal/traits.lo: core/internal/$(am__dirstamp)
+core/internal/utf.lo: core/internal/$(am__dirstamp)
+core/internal/util/$(am__dirstamp):
+ @$(MKDIR_P) core/internal/util
+ @: > core/internal/util/$(am__dirstamp)
+core/internal/util/array.lo: core/internal/util/$(am__dirstamp)
+core/internal/util/math.lo: core/internal/util/$(am__dirstamp)
+core/lifetime.lo: core/$(am__dirstamp)
core/math.lo: core/$(am__dirstamp)
core/memory.lo: core/$(am__dirstamp)
core/runtime.lo: core/$(am__dirstamp)
@@ -1176,6 +1297,7 @@ core/sync/$(am__dirstamp):
core/sync/barrier.lo: core/sync/$(am__dirstamp)
core/sync/condition.lo: core/sync/$(am__dirstamp)
core/sync/config.lo: core/sync/$(am__dirstamp)
+core/sync/event.lo: core/sync/$(am__dirstamp)
core/sync/exception.lo: core/sync/$(am__dirstamp)
core/sync/mutex.lo: core/sync/$(am__dirstamp)
core/sync/rwmutex.lo: core/sync/$(am__dirstamp)
@@ -1193,23 +1315,6 @@ core/thread/types.lo: core/thread/$(am__dirstamp)
core/time.lo: core/$(am__dirstamp)
core/vararg.lo: core/$(am__dirstamp)
core/volatile.lo: core/$(am__dirstamp)
-gc/$(am__dirstamp):
- @$(MKDIR_P) gc
- @: > gc/$(am__dirstamp)
-gc/bits.lo: gc/$(am__dirstamp)
-gc/config.lo: gc/$(am__dirstamp)
-gc/gcinterface.lo: gc/$(am__dirstamp)
-gc/impl/conservative/$(am__dirstamp):
- @$(MKDIR_P) gc/impl/conservative
- @: > gc/impl/conservative/$(am__dirstamp)
-gc/impl/conservative/gc.lo: gc/impl/conservative/$(am__dirstamp)
-gc/impl/manual/$(am__dirstamp):
- @$(MKDIR_P) gc/impl/manual
- @: > gc/impl/manual/$(am__dirstamp)
-gc/impl/manual/gc.lo: gc/impl/manual/$(am__dirstamp)
-gc/os.lo: gc/$(am__dirstamp)
-gc/pooltable.lo: gc/$(am__dirstamp)
-gc/proxy.lo: gc/$(am__dirstamp)
gcc/$(am__dirstamp):
@$(MKDIR_P) gcc
@: > gcc/$(am__dirstamp)
@@ -1245,38 +1350,42 @@ rt/aApplyR.lo: rt/$(am__dirstamp)
rt/aaA.lo: rt/$(am__dirstamp)
rt/adi.lo: rt/$(am__dirstamp)
rt/arrayassign.lo: rt/$(am__dirstamp)
-rt/arraycast.lo: rt/$(am__dirstamp)
rt/arraycat.lo: rt/$(am__dirstamp)
rt/cast_.lo: rt/$(am__dirstamp)
rt/config.lo: rt/$(am__dirstamp)
rt/critical_.lo: rt/$(am__dirstamp)
rt/deh.lo: rt/$(am__dirstamp)
rt/dmain2.lo: rt/$(am__dirstamp)
+rt/ehalloc.lo: rt/$(am__dirstamp)
rt/invariant.lo: rt/$(am__dirstamp)
rt/lifetime.lo: rt/$(am__dirstamp)
rt/memory.lo: rt/$(am__dirstamp)
rt/minfo.lo: rt/$(am__dirstamp)
rt/monitor_.lo: rt/$(am__dirstamp)
-rt/obj.lo: rt/$(am__dirstamp)
-rt/qsort.lo: rt/$(am__dirstamp)
+rt/profilegc.lo: rt/$(am__dirstamp)
rt/sections.lo: rt/$(am__dirstamp)
-rt/switch_.lo: rt/$(am__dirstamp)
rt/tlsgc.lo: rt/$(am__dirstamp)
rt/util/$(am__dirstamp):
@$(MKDIR_P) rt/util
@: > rt/util/$(am__dirstamp)
-rt/util/array.lo: rt/util/$(am__dirstamp)
-rt/util/container/$(am__dirstamp):
- @$(MKDIR_P) rt/util/container
- @: > rt/util/container/$(am__dirstamp)
-rt/util/container/array.lo: rt/util/container/$(am__dirstamp)
-rt/util/container/common.lo: rt/util/container/$(am__dirstamp)
-rt/util/container/hashtab.lo: rt/util/container/$(am__dirstamp)
-rt/util/container/treap.lo: rt/util/container/$(am__dirstamp)
-rt/util/random.lo: rt/util/$(am__dirstamp)
rt/util/typeinfo.lo: rt/util/$(am__dirstamp)
-rt/util/utf.lo: rt/util/$(am__dirstamp)
+rt/util/utility.lo: rt/util/$(am__dirstamp)
core/stdc/libgdruntime_la-errno_.lo: core/stdc/$(am__dirstamp)
+core/stdcpp/$(am__dirstamp):
+ @$(MKDIR_P) core/stdcpp
+ @: > core/stdcpp/$(am__dirstamp)
+core/stdcpp/allocator.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/array.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/exception.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/memory.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/new_.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/string.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/string_view.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/type_traits.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/typeinfo.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/utility.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/vector.lo: core/stdcpp/$(am__dirstamp)
+core/stdcpp/xutility.lo: core/stdcpp/$(am__dirstamp)
core/sys/posix/$(am__dirstamp):
@$(MKDIR_P) core/sys/posix
@: > core/sys/posix/$(am__dirstamp)
@@ -1506,6 +1615,7 @@ core/sys/openbsd/$(am__dirstamp):
@: > core/sys/openbsd/$(am__dirstamp)
core/sys/openbsd/dlfcn.lo: core/sys/openbsd/$(am__dirstamp)
core/sys/openbsd/err.lo: core/sys/openbsd/$(am__dirstamp)
+core/sys/openbsd/execinfo.lo: core/sys/openbsd/$(am__dirstamp)
core/sys/openbsd/stdlib.lo: core/sys/openbsd/$(am__dirstamp)
core/sys/openbsd/string.lo: core/sys/openbsd/$(am__dirstamp)
core/sys/openbsd/sys/$(am__dirstamp):
@@ -1534,13 +1644,16 @@ core/sys/linux/err.lo: core/sys/linux/$(am__dirstamp)
core/sys/linux/errno.lo: core/sys/linux/$(am__dirstamp)
core/sys/linux/execinfo.lo: core/sys/linux/$(am__dirstamp)
core/sys/linux/fcntl.lo: core/sys/linux/$(am__dirstamp)
+core/sys/linux/fs.lo: core/sys/linux/$(am__dirstamp)
core/sys/linux/ifaddrs.lo: core/sys/linux/$(am__dirstamp)
+core/sys/linux/io_uring.lo: core/sys/linux/$(am__dirstamp)
core/sys/linux/link.lo: core/sys/linux/$(am__dirstamp)
core/sys/linux/netinet/$(am__dirstamp):
@$(MKDIR_P) core/sys/linux/netinet
@: > core/sys/linux/netinet/$(am__dirstamp)
core/sys/linux/netinet/in_.lo: core/sys/linux/netinet/$(am__dirstamp)
core/sys/linux/netinet/tcp.lo: core/sys/linux/netinet/$(am__dirstamp)
+core/sys/linux/perf_event.lo: core/sys/linux/$(am__dirstamp)
core/sys/linux/sched.lo: core/sys/linux/$(am__dirstamp)
core/sys/linux/stdio.lo: core/sys/linux/$(am__dirstamp)
core/sys/linux/string.lo: core/sys/linux/$(am__dirstamp)
@@ -1553,6 +1666,7 @@ core/sys/linux/sys/file.lo: core/sys/linux/sys/$(am__dirstamp)
core/sys/linux/sys/inotify.lo: core/sys/linux/sys/$(am__dirstamp)
core/sys/linux/sys/mman.lo: core/sys/linux/sys/$(am__dirstamp)
core/sys/linux/sys/prctl.lo: core/sys/linux/sys/$(am__dirstamp)
+core/sys/linux/sys/procfs.lo: core/sys/linux/sys/$(am__dirstamp)
core/sys/linux/sys/signalfd.lo: core/sys/linux/sys/$(am__dirstamp)
core/sys/linux/sys/socket.lo: core/sys/linux/sys/$(am__dirstamp)
core/sys/linux/sys/sysinfo.lo: core/sys/linux/sys/$(am__dirstamp)
@@ -1857,10 +1971,28 @@ mostlyclean-compile:
-rm -f config/x86/*.lo
-rm -f core/*.$(OBJEXT)
-rm -f core/*.lo
+ -rm -f core/gc/*.$(OBJEXT)
+ -rm -f core/gc/*.lo
-rm -f core/internal/*.$(OBJEXT)
-rm -f core/internal/*.lo
+ -rm -f core/internal/array/*.$(OBJEXT)
+ -rm -f core/internal/array/*.lo
+ -rm -f core/internal/container/*.$(OBJEXT)
+ -rm -f core/internal/container/*.lo
+ -rm -f core/internal/gc/*.$(OBJEXT)
+ -rm -f core/internal/gc/*.lo
+ -rm -f core/internal/gc/impl/conservative/*.$(OBJEXT)
+ -rm -f core/internal/gc/impl/conservative/*.lo
+ -rm -f core/internal/gc/impl/manual/*.$(OBJEXT)
+ -rm -f core/internal/gc/impl/manual/*.lo
+ -rm -f core/internal/gc/impl/proto/*.$(OBJEXT)
+ -rm -f core/internal/gc/impl/proto/*.lo
+ -rm -f core/internal/util/*.$(OBJEXT)
+ -rm -f core/internal/util/*.lo
-rm -f core/stdc/*.$(OBJEXT)
-rm -f core/stdc/*.lo
+ -rm -f core/stdcpp/*.$(OBJEXT)
+ -rm -f core/stdcpp/*.lo
-rm -f core/sync/*.$(OBJEXT)
-rm -f core/sync/*.lo
-rm -f core/sys/bionic/*.$(OBJEXT)
@@ -1921,12 +2053,6 @@ mostlyclean-compile:
-rm -f core/sys/windows/stdc/*.lo
-rm -f core/thread/*.$(OBJEXT)
-rm -f core/thread/*.lo
- -rm -f gc/*.$(OBJEXT)
- -rm -f gc/*.lo
- -rm -f gc/impl/conservative/*.$(OBJEXT)
- -rm -f gc/impl/conservative/*.lo
- -rm -f gc/impl/manual/*.$(OBJEXT)
- -rm -f gc/impl/manual/*.lo
-rm -f gcc/*.$(OBJEXT)
-rm -f gcc/*.lo
-rm -f gcc/sections/*.$(OBJEXT)
@@ -1937,8 +2063,6 @@ mostlyclean-compile:
-rm -f rt/*.lo
-rm -f rt/util/*.$(OBJEXT)
-rm -f rt/util/*.lo
- -rm -f rt/util/container/*.$(OBJEXT)
- -rm -f rt/util/container/*.lo
distclean-compile:
-rm -f *.tab.c
@@ -2035,8 +2159,17 @@ clean-libtool:
-rm -rf config/systemz/.libs config/systemz/_libs
-rm -rf config/x86/.libs config/x86/_libs
-rm -rf core/.libs core/_libs
+ -rm -rf core/gc/.libs core/gc/_libs
-rm -rf core/internal/.libs core/internal/_libs
+ -rm -rf core/internal/array/.libs core/internal/array/_libs
+ -rm -rf core/internal/container/.libs core/internal/container/_libs
+ -rm -rf core/internal/gc/.libs core/internal/gc/_libs
+ -rm -rf core/internal/gc/impl/conservative/.libs core/internal/gc/impl/conservative/_libs
+ -rm -rf core/internal/gc/impl/manual/.libs core/internal/gc/impl/manual/_libs
+ -rm -rf core/internal/gc/impl/proto/.libs core/internal/gc/impl/proto/_libs
+ -rm -rf core/internal/util/.libs core/internal/util/_libs
-rm -rf core/stdc/.libs core/stdc/_libs
+ -rm -rf core/stdcpp/.libs core/stdcpp/_libs
-rm -rf core/sync/.libs core/sync/_libs
-rm -rf core/sys/bionic/.libs core/sys/bionic/_libs
-rm -rf core/sys/darwin/.libs core/sys/darwin/_libs
@@ -2067,15 +2200,11 @@ clean-libtool:
-rm -rf core/sys/windows/.libs core/sys/windows/_libs
-rm -rf core/sys/windows/stdc/.libs core/sys/windows/stdc/_libs
-rm -rf core/thread/.libs core/thread/_libs
- -rm -rf gc/.libs gc/_libs
- -rm -rf gc/impl/conservative/.libs gc/impl/conservative/_libs
- -rm -rf gc/impl/manual/.libs gc/impl/manual/_libs
-rm -rf gcc/.libs gcc/_libs
-rm -rf gcc/sections/.libs gcc/sections/_libs
-rm -rf gcc/unwind/.libs gcc/unwind/_libs
-rm -rf rt/.libs rt/_libs
-rm -rf rt/util/.libs rt/util/_libs
- -rm -rf rt/util/container/.libs rt/util/container/_libs
install-toolexeclibDATA: $(toolexeclib_DATA)
@$(NORMAL_INSTALL)
@list='$(toolexeclib_DATA)'; test -n "$(toolexeclibdir)" || list=; \
@@ -2191,8 +2320,17 @@ distclean-generic:
-rm -f config/systemz/$(am__dirstamp)
-rm -f config/x86/$(am__dirstamp)
-rm -f core/$(am__dirstamp)
+ -rm -f core/gc/$(am__dirstamp)
-rm -f core/internal/$(am__dirstamp)
+ -rm -f core/internal/array/$(am__dirstamp)
+ -rm -f core/internal/container/$(am__dirstamp)
+ -rm -f core/internal/gc/$(am__dirstamp)
+ -rm -f core/internal/gc/impl/conservative/$(am__dirstamp)
+ -rm -f core/internal/gc/impl/manual/$(am__dirstamp)
+ -rm -f core/internal/gc/impl/proto/$(am__dirstamp)
+ -rm -f core/internal/util/$(am__dirstamp)
-rm -f core/stdc/$(am__dirstamp)
+ -rm -f core/stdcpp/$(am__dirstamp)
-rm -f core/sync/$(am__dirstamp)
-rm -f core/sys/bionic/$(am__dirstamp)
-rm -f core/sys/darwin/$(am__dirstamp)
@@ -2223,15 +2361,11 @@ distclean-generic:
-rm -f core/sys/windows/$(am__dirstamp)
-rm -f core/sys/windows/stdc/$(am__dirstamp)
-rm -f core/thread/$(am__dirstamp)
- -rm -f gc/$(am__dirstamp)
- -rm -f gc/impl/conservative/$(am__dirstamp)
- -rm -f gc/impl/manual/$(am__dirstamp)
-rm -f gcc/$(am__dirstamp)
-rm -f gcc/sections/$(am__dirstamp)
-rm -f gcc/unwind/$(am__dirstamp)
-rm -f rt/$(am__dirstamp)
-rm -f rt/util/$(am__dirstamp)
- -rm -f rt/util/container/$(am__dirstamp)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
diff --git a/libphobos/libdruntime/__entrypoint.di b/libphobos/libdruntime/__entrypoint.di
deleted file mode 100644
index fba2ae28b77..00000000000
--- a/libphobos/libdruntime/__entrypoint.di
+++ /dev/null
@@ -1,56 +0,0 @@
-/* GDC -- D front-end for GCC
- Copyright (C) 2013-2021 Free Software Foundation, Inc.
-
- GCC is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 3, or (at your option) any later
- version.
-
- GCC is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
-
- You should have received a copy of the GNU General Public License
- along with GCC; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>.
-*/
-
-/* This module provides the C main() function supplied by the user's program. */
-
-module __entrypoint;
-
-extern(C):
-
-/* The D main() function supplied by the user's program
-
- It always has `_Dmain` symbol name and uses C calling convention.
- But D frontend returns its type as `extern(D)` because of Issue 9028.
- As we need to deal with actual calling convention we have to mark it
- as `extern(C)` and use its symbol name.
-*/
-
-int _Dmain(char[][] args);
-int _d_run_main(int argc, char **argv, void* mainFunc);
-
-/* Substitutes for the C main() function. Just calls into d_run_main with
- the default main function. Applications are free to implement their own
- main function and call the _d_run_main function themselves with any main
- function.
-*/
-
-int main(int argc, char **argv)
-{
- return _d_run_main(argc, argv, &_Dmain);
-}
-
-/* This is apparently needed on Solaris because the C tool chain seems to
- expect the main function to be called _main. It needs both not just one!
-*/
-
-version (Solaris)
-int _main(int argc, char** argv)
-{
- return main(argc, argv);
-}
-
diff --git a/libphobos/libdruntime/core/atomic.d b/libphobos/libdruntime/core/atomic.d
index 1d0a2ea8b48..e6a82e58f85 100644
--- a/libphobos/libdruntime/core/atomic.d
+++ b/libphobos/libdruntime/core/atomic.d
@@ -4,1691 +4,915 @@
*
* Copyright: Copyright Sean Kelly 2005 - 2016.
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
- * Authors: Sean Kelly, Alex Rønne Petersen
+ * Authors: Sean Kelly, Alex Rønne Petersen, Manu Evans
* Source: $(DRUNTIMESRC core/_atomic.d)
*/
-
-/* NOTE: This file has been patched from the original DMD distribution to
- * work with the GDC compiler.
- */
module core.atomic;
-version (D_InlineAsm_X86)
+import core.internal.atomic;
+import core.internal.attributes : betterC;
+import core.internal.traits : hasUnsharedIndirections;
+
+/**
+ * Specifies the memory ordering semantics of an atomic operation.
+ *
+ * See_Also:
+ * $(HTTP en.cppreference.com/w/cpp/atomic/memory_order)
+ */
+enum MemoryOrder
{
- version = AsmX86;
- version = AsmX86_32;
- enum has64BitCAS = true;
- enum has128BitCAS = false;
+ /**
+ * Not sequenced.
+ * Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#monotonic, LLVM AtomicOrdering.Monotonic)
+ * and C++11/C11 `memory_order_relaxed`.
+ */
+ raw = 0,
+ /**
+ * Hoist-load + hoist-store barrier.
+ * Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#acquire, LLVM AtomicOrdering.Acquire)
+ * and C++11/C11 `memory_order_acquire`.
+ */
+ acq = 2,
+ /**
+ * Sink-load + sink-store barrier.
+ * Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#release, LLVM AtomicOrdering.Release)
+ * and C++11/C11 `memory_order_release`.
+ */
+ rel = 3,
+ /**
+ * Acquire + release barrier.
+ * Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#acquirerelease, LLVM AtomicOrdering.AcquireRelease)
+ * and C++11/C11 `memory_order_acq_rel`.
+ */
+ acq_rel = 4,
+ /**
+ * Fully sequenced (acquire + release). Corresponds to
+ * $(LINK2 https://llvm.org/docs/Atomics.html#sequentiallyconsistent, LLVM AtomicOrdering.SequentiallyConsistent)
+ * and C++11/C11 `memory_order_seq_cst`.
+ */
+ seq = 5,
}
-else version (D_InlineAsm_X86_64)
+
+/**
+ * Loads 'val' from memory and returns it. The memory barrier specified
+ * by 'ms' is applied to the operation, which is fully sequenced by
+ * default. Valid memory orders are MemoryOrder.raw, MemoryOrder.acq,
+ * and MemoryOrder.seq.
+ *
+ * Params:
+ * val = The target variable.
+ *
+ * Returns:
+ * The value of 'val'.
+ */
+T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope const T val) pure nothrow @nogc @trusted
+ if (!is(T == shared U, U) && !is(T == shared inout U, U) && !is(T == shared const U, U))
{
- version = AsmX86;
- version = AsmX86_64;
- enum has64BitCAS = true;
- enum has128BitCAS = true;
+ static if (__traits(isFloating, T))
+ {
+ alias IntTy = IntForFloat!T;
+ IntTy r = core.internal.atomic.atomicLoad!ms(cast(IntTy*)&val);
+ return *cast(T*)&r;
+ }
+ else
+ return core.internal.atomic.atomicLoad!ms(cast(T*)&val);
}
-else version (GNU)
+
+/// Ditto
+T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope shared const T val) pure nothrow @nogc @trusted
+ if (!hasUnsharedIndirections!T)
{
- import gcc.config;
- enum has64BitCAS = GNU_Have_64Bit_Atomics;
- enum has128BitCAS = GNU_Have_LibAtomic;
+ import core.internal.traits : hasUnsharedIndirections;
+ static assert(!hasUnsharedIndirections!T, "Copying `" ~ shared(const(T)).stringof ~ "` would violate shared.");
+
+ return atomicLoad!ms(*cast(T*)&val);
}
-else
+
+/// Ditto
+TailShared!T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref shared const T val) pure nothrow @nogc @trusted
+ if (hasUnsharedIndirections!T)
{
- enum has64BitCAS = false;
- enum has128BitCAS = false;
+ // HACK: DEPRECATE THIS FUNCTION, IT IS INVALID TO DO ATOMIC LOAD OF SHARED CLASS
+ // this is here because code exists in the wild that does this...
+
+ return core.internal.atomic.atomicLoad!ms(cast(TailShared!T*)&val);
}
-private
+/**
+ * Writes 'newval' into 'val'. The memory barrier specified by 'ms' is
+ * applied to the operation, which is fully sequenced by default.
+ * Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and
+ * MemoryOrder.seq.
+ *
+ * Params:
+ * val = The target variable.
+ * newval = The value to store.
+ */
+void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) pure nothrow @nogc @trusted
+ if (!is(T == shared) && !is(V == shared))
{
- template HeadUnshared(T)
- {
- static if ( is( T U : shared(U*) ) )
- alias shared(U)* HeadUnshared;
- else
- alias T HeadUnshared;
- }
-}
+ import core.internal.traits : hasElaborateCopyConstructor;
+ static assert (!hasElaborateCopyConstructor!T, "`T` may not have an elaborate copy: atomic operations override regular copying semantics.");
+ // resolve implicit conversions
+ T arg = newval;
-version (AsmX86)
-{
- // NOTE: Strictly speaking, the x86 supports atomic operations on
- // unaligned values. However, this is far slower than the
- // common case, so such behavior should be prohibited.
- private bool atomicValueIsProperlyAligned(T)( ref T val ) pure nothrow @nogc @trusted
+ static if (__traits(isFloating, T))
{
- return atomicPtrIsProperlyAligned(&val);
+ alias IntTy = IntForFloat!T;
+ core.internal.atomic.atomicStore!ms(cast(IntTy*)&val, *cast(IntTy*)&arg);
}
+ else
+ core.internal.atomic.atomicStore!ms(&val, arg);
+}
- private bool atomicPtrIsProperlyAligned(T)( T* ptr ) pure nothrow @nogc @safe
+/// Ditto
+void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref shared T val, V newval) pure nothrow @nogc @trusted
+ if (!is(T == class))
+{
+ static if (is (V == shared U, U))
+ alias Thunk = U;
+ else
{
- // NOTE: 32 bit x86 systems support 8 byte CAS, which only requires
- // 4 byte alignment, so use size_t as the align type here.
- static if ( T.sizeof > size_t.sizeof )
- return cast(size_t)ptr % size_t.sizeof == 0;
- else
- return cast(size_t)ptr % T.sizeof == 0;
+ import core.internal.traits : hasUnsharedIndirections;
+ static assert(!hasUnsharedIndirections!V, "Copying argument `" ~ V.stringof ~ " newval` to `" ~ shared(T).stringof ~ " here` would violate shared.");
+ alias Thunk = V;
}
+ atomicStore!ms(*cast(T*)&val, *cast(Thunk*)&newval);
}
-
-version (CoreDdoc)
+/// Ditto
+void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref shared T val, shared V newval) pure nothrow @nogc @trusted
+ if (is(T == class))
{
- /**
- * Performs the binary operation 'op' on val using 'mod' as the modifier.
- *
- * Params:
- * val = The target variable.
- * mod = The modifier to apply.
- *
- * Returns:
- * The result of the operation.
- */
- HeadUnshared!(T) atomicOp(string op, T, V1)( ref shared T val, V1 mod ) pure nothrow @nogc @safe
- if ( __traits( compiles, mixin( "*cast(T*)&val" ~ op ~ "mod" ) ) )
- {
- return HeadUnshared!(T).init;
- }
+ static assert (is (V : T), "Can't assign `newval` of type `shared " ~ V.stringof ~ "` to `shared " ~ T.stringof ~ "`.");
+ core.internal.atomic.atomicStore!ms(cast(T*)&val, cast(V)newval);
+}
- /**
- * Stores 'writeThis' to the memory referenced by 'here' if the value
- * referenced by 'here' is equal to 'ifThis'. This operation is both
- * lock-free and atomic.
- *
- * Params:
- * here = The address of the destination variable.
- * writeThis = The value to store.
- * ifThis = The comparison value.
- *
- * Returns:
- * true if the store occurred, false if not.
- */
- bool cas(T,V1,V2)( shared(T)* here, const V1 ifThis, V2 writeThis ) pure nothrow @nogc @safe
- if ( !is(T == class) && !is(T U : U*) && __traits( compiles, { *here = writeThis; } ) );
+/**
+ * Atomically adds `mod` to the value referenced by `val` and returns the value `val` held previously.
+ * This operation is both lock-free and atomic.
+ *
+ * Params:
+ * val = Reference to the value to modify.
+ * mod = The value to add.
+ *
+ * Returns:
+ * The value held previously by `val`.
+ */
+T atomicFetchAdd(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope T val, size_t mod) pure nothrow @nogc @trusted
+ if ((__traits(isIntegral, T) || is(T == U*, U)) && !is(T == shared))
+in (atomicValueIsProperlyAligned(val))
+{
+ static if (is(T == U*, U))
+ return cast(T)core.internal.atomic.atomicFetchAdd!ms(cast(size_t*)&val, mod * U.sizeof);
+ else
+ return core.internal.atomic.atomicFetchAdd!ms(&val, cast(T)mod);
+}
- /// Ditto
- bool cas(T,V1,V2)( shared(T)* here, const shared(V1) ifThis, shared(V2) writeThis ) pure nothrow @nogc @safe
- if ( is(T == class) && __traits( compiles, { *here = writeThis; } ) );
+/// Ditto
+T atomicFetchAdd(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope shared T val, size_t mod) pure nothrow @nogc @trusted
+ if (__traits(isIntegral, T) || is(T == U*, U))
+in (atomicValueIsProperlyAligned(val))
+{
+ return atomicFetchAdd!ms(*cast(T*)&val, mod);
+}
- /// Ditto
- bool cas(T,V1,V2)( shared(T)* here, const shared(V1)* ifThis, shared(V2)* writeThis ) pure nothrow @nogc @safe
- if ( is(T U : U*) && __traits( compiles, { *here = writeThis; } ) );
+/**
+ * Atomically subtracts `mod` from the value referenced by `val` and returns the value `val` held previously.
+ * This operation is both lock-free and atomic.
+ *
+ * Params:
+ * val = Reference to the value to modify.
+ * mod = The value to subtract.
+ *
+ * Returns:
+ * The value held previously by `val`.
+ */
+T atomicFetchSub(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope T val, size_t mod) pure nothrow @nogc @trusted
+ if ((__traits(isIntegral, T) || is(T == U*, U)) && !is(T == shared))
+in (atomicValueIsProperlyAligned(val))
+{
+ static if (is(T == U*, U))
+ return cast(T)core.internal.atomic.atomicFetchSub!ms(cast(size_t*)&val, mod * U.sizeof);
+ else
+ return core.internal.atomic.atomicFetchSub!ms(&val, cast(T)mod);
+}
- /**
- * Loads 'val' from memory and returns it. The memory barrier specified
- * by 'ms' is applied to the operation, which is fully sequenced by
- * default. Valid memory orders are MemoryOrder.raw, MemoryOrder.acq,
- * and MemoryOrder.seq.
- *
- * Params:
- * val = The target variable.
- *
- * Returns:
- * The value of 'val'.
- */
- HeadUnshared!(T) atomicLoad(MemoryOrder ms = MemoryOrder.seq,T)( ref const shared T val ) pure nothrow @nogc @safe
- {
- return HeadUnshared!(T).init;
- }
+/// Ditto
+T atomicFetchSub(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope shared T val, size_t mod) pure nothrow @nogc @trusted
+ if (__traits(isIntegral, T) || is(T == U*, U))
+in (atomicValueIsProperlyAligned(val))
+{
+ return atomicFetchSub!ms(*cast(T*)&val, mod);
+}
+/**
+ * Exchange `exchangeWith` with the memory referenced by `here`.
+ * This operation is both lock-free and atomic.
+ *
+ * Params:
+ * here = The address of the destination variable.
+ * exchangeWith = The value to exchange.
+ *
+ * Returns:
+ * The value held previously by `here`.
+ */
+T atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)(T* here, V exchangeWith) pure nothrow @nogc @trusted
+ if (!is(T == shared) && !is(V == shared))
+in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
+{
+ // resolve implicit conversions
+ T arg = exchangeWith;
- /**
- * Writes 'newval' into 'val'. The memory barrier specified by 'ms' is
- * applied to the operation, which is fully sequenced by default.
- * Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and
- * MemoryOrder.seq.
- *
- * Params:
- * val = The target variable.
- * newval = The value to store.
- */
- void atomicStore(MemoryOrder ms = MemoryOrder.seq,T,V1)( ref shared T val, V1 newval ) pure nothrow @nogc @safe
- if ( __traits( compiles, { val = newval; } ) )
+ static if (__traits(isFloating, T))
{
-
+ alias IntTy = IntForFloat!T;
+ IntTy r = core.internal.atomic.atomicExchange!ms(cast(IntTy*)here, *cast(IntTy*)&arg);
+ return *cast(shared(T)*)&r;
}
+ else
+ return core.internal.atomic.atomicExchange!ms(here, arg);
+}
-
- /**
- * Specifies the memory ordering semantics of an atomic operation.
- */
- enum MemoryOrder
+/// Ditto
+TailShared!T atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)(shared(T)* here, V exchangeWith) pure nothrow @nogc @trusted
+ if (!is(T == class) && !is(T == interface))
+in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
+{
+ static if (is (V == shared U, U))
+ alias Thunk = U;
+ else
{
- raw, /// Not sequenced.
- acq, /// Hoist-load + hoist-store barrier.
- rel, /// Sink-load + sink-store barrier.
- seq, /// Fully sequenced (acquire + release).
+ import core.internal.traits : hasUnsharedIndirections;
+ static assert(!hasUnsharedIndirections!V, "Copying `exchangeWith` of type `" ~ V.stringof ~ "` to `" ~ shared(T).stringof ~ "` would violate shared.");
+ alias Thunk = V;
}
+ return atomicExchange!ms(cast(T*)here, *cast(Thunk*)&exchangeWith);
+}
- deprecated("Please use MemoryOrder instead.")
- alias MemoryOrder msync;
+/// Ditto
+shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)(shared(T)* here, shared(V) exchangeWith) pure nothrow @nogc @trusted
+ if (is(T == class) || is(T == interface))
+in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
+{
+ static assert (is (V : T), "Can't assign `exchangeWith` of type `" ~ shared(V).stringof ~ "` to `" ~ shared(T).stringof ~ "`.");
- /**
- * Inserts a full load/store memory fence (on platforms that need it). This ensures
- * that all loads and stores before a call to this function are executed before any
- * loads and stores after the call.
- */
- void atomicFence() nothrow @nogc;
+ return cast(shared)core.internal.atomic.atomicExchange!ms(cast(T*)here, cast(V)exchangeWith);
}
-else version (AsmX86_32)
+
+/**
+ * Performs either compare-and-set or compare-and-swap (or exchange).
+ *
+ * There are two categories of overloads in this template:
+ * The first category does a simple compare-and-set.
+ * The comparison value (`ifThis`) is treated as an rvalue.
+ *
+ * The second category does a compare-and-swap (a.k.a. compare-and-exchange),
+ * and expects `ifThis` to be a pointer type, where the previous value
+ * of `here` will be written.
+ *
+ * This operation is both lock-free and atomic.
+ *
+ * Params:
+ * here = The address of the destination variable.
+ * writeThis = The value to store.
+ * ifThis = The comparison value.
+ *
+ * Returns:
+ * true if the store occurred, false if not.
+ */
+template cas(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq)
{
- // Uses specialized asm for fast fetch and add operations
- private HeadUnshared!(T) atomicFetchAdd(T)( ref shared T val, size_t mod ) pure nothrow @nogc @safe
- if ( T.sizeof <= 4 )
+ /// Compare-and-set for non-shared values
+ bool cas(T, V1, V2)(T* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted
+ if (!is(T == shared) && is(T : V1))
+ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
{
- size_t tmp = mod;
- asm pure nothrow @nogc @trusted
- {
- mov EAX, tmp;
- mov EDX, val;
- }
- static if (T.sizeof == 1) asm pure nothrow @nogc @trusted { lock; xadd[EDX], AL; }
- else static if (T.sizeof == 2) asm pure nothrow @nogc @trusted { lock; xadd[EDX], AX; }
- else static if (T.sizeof == 4) asm pure nothrow @nogc @trusted { lock; xadd[EDX], EAX; }
+ // resolve implicit conversions
+ T arg1 = ifThis;
+ T arg2 = writeThis;
- asm pure nothrow @nogc @trusted
+ static if (__traits(isFloating, T))
{
- mov tmp, EAX;
+ alias IntTy = IntForFloat!T;
+ return atomicCompareExchangeStrongNoResult!(succ, fail)(
+ cast(IntTy*)here, *cast(IntTy*)&arg1, *cast(IntTy*)&arg2);
}
-
- return cast(T)tmp;
- }
-
- private HeadUnshared!(T) atomicFetchSub(T)( ref shared T val, size_t mod ) pure nothrow @nogc @safe
- if ( T.sizeof <= 4)
- {
- return atomicFetchAdd(val, -mod);
+ else
+ return atomicCompareExchangeStrongNoResult!(succ, fail)(here, arg1, arg2);
}
- HeadUnshared!(T) atomicOp(string op, T, V1)( ref shared T val, V1 mod ) pure nothrow @nogc
- if ( __traits( compiles, mixin( "*cast(T*)&val" ~ op ~ "mod" ) ) )
- in
- {
- assert(atomicValueIsProperlyAligned(val));
- }
- body
+ /// Compare-and-set for shared value type
+ bool cas(T, V1, V2)(shared(T)* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted
+ if (!is(T == class) && (is(T : V1) || is(shared T : V1)))
+ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
{
- // binary operators
- //
- // + - * / % ^^ &
- // | ^ << >> >>> ~ in
- // == != < <= > >=
- static if ( op == "+" || op == "-" || op == "*" || op == "/" ||
- op == "%" || op == "^^" || op == "&" || op == "|" ||
- op == "^" || op == "<<" || op == ">>" || op == ">>>" ||
- op == "~" || // skip "in"
- op == "==" || op == "!=" || op == "<" || op == "<=" ||
- op == ">" || op == ">=" )
- {
- HeadUnshared!(T) get = atomicLoad!(MemoryOrder.raw)( val );
- mixin( "return get " ~ op ~ " mod;" );
- }
+ static if (is (V1 == shared U1, U1))
+ alias Thunk1 = U1;
else
- // assignment operators
- //
- // += -= *= /= %= ^^= &=
- // |= ^= <<= >>= >>>= ~=
- static if ( op == "+=" && __traits(isIntegral, T) && T.sizeof <= 4 && V1.sizeof <= 4)
- {
- return cast(T)(atomicFetchAdd!(T)(val, mod) + mod);
- }
- else static if ( op == "-=" && __traits(isIntegral, T) && T.sizeof <= 4 && V1.sizeof <= 4)
- {
- return cast(T)(atomicFetchSub!(T)(val, mod) - mod);
- }
- else static if ( op == "+=" || op == "-=" || op == "*=" || op == "/=" ||
- op == "%=" || op == "^^=" || op == "&=" || op == "|=" ||
- op == "^=" || op == "<<=" || op == ">>=" || op == ">>>=" ) // skip "~="
- {
- HeadUnshared!(T) get, set;
-
- do
- {
- get = set = atomicLoad!(MemoryOrder.raw)( val );
- mixin( "set " ~ op ~ " mod;" );
- } while ( !casByRef( val, get, set ) );
- return set;
- }
+ alias Thunk1 = V1;
+ static if (is (V2 == shared U2, U2))
+ alias Thunk2 = U2;
else
{
- static assert( false, "Operation not supported." );
+ import core.internal.traits : hasUnsharedIndirections;
+ static assert(!hasUnsharedIndirections!V2,
+ "Copying `" ~ V2.stringof ~ "* writeThis` to `" ~
+ shared(T).stringof ~ "* here` would violate shared.");
+ alias Thunk2 = V2;
}
+ return cas(cast(T*)here, *cast(Thunk1*)&ifThis, *cast(Thunk2*)&writeThis);
}
- bool casByRef(T,V1,V2)( ref T value, V1 ifThis, V2 writeThis ) pure nothrow @nogc @trusted
+ /// Compare-and-set for `shared` reference type (`class`)
+ bool cas(T, V1, V2)(shared(T)* here, shared(V1) ifThis, shared(V2) writeThis)
+ pure nothrow @nogc @trusted
+ if (is(T == class))
+ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
{
- return cas(&value, ifThis, writeThis);
+ return atomicCompareExchangeStrongNoResult!(succ, fail)(
+ cast(T*)here, cast(V1)ifThis, cast(V2)writeThis);
}
- bool cas(T,V1,V2)( shared(T)* here, const V1 ifThis, V2 writeThis ) pure nothrow @nogc @safe
- if ( !is(T == class) && !is(T U : U*) && __traits( compiles, { *here = writeThis; } ) )
+ /// Compare-and-exchange for non-`shared` types
+ bool cas(T, V)(T* here, T* ifThis, V writeThis) pure nothrow @nogc @trusted
+ if (!is(T == shared) && !is(V == shared))
+ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
{
- return casImpl(here, ifThis, writeThis);
- }
-
- bool cas(T,V1,V2)( shared(T)* here, const shared(V1) ifThis, shared(V2) writeThis ) pure nothrow @nogc @safe
- if ( is(T == class) && __traits( compiles, { *here = writeThis; } ) )
- {
- return casImpl(here, ifThis, writeThis);
- }
-
- bool cas(T,V1,V2)( shared(T)* here, const shared(V1)* ifThis, shared(V2)* writeThis ) pure nothrow @nogc @safe
- if ( is(T U : U*) && __traits( compiles, { *here = writeThis; } ) )
- {
- return casImpl(here, ifThis, writeThis);
- }
+ // resolve implicit conversions
+ T arg1 = writeThis;
- private bool casImpl(T,V1,V2)( shared(T)* here, V1 ifThis, V2 writeThis ) pure nothrow @nogc @safe
- in
- {
- assert( atomicPtrIsProperlyAligned( here ) );
- }
- body
- {
- static if ( T.sizeof == byte.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 1 Byte CAS
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- mov DL, writeThis;
- mov AL, ifThis;
- mov ECX, here;
- lock; // lock always needed to make this op atomic
- cmpxchg [ECX], DL;
- setz AL;
- }
- }
- else static if ( T.sizeof == short.sizeof )
+ static if (__traits(isFloating, T))
{
- //////////////////////////////////////////////////////////////////
- // 2 Byte CAS
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- mov DX, writeThis;
- mov AX, ifThis;
- mov ECX, here;
- lock; // lock always needed to make this op atomic
- cmpxchg [ECX], DX;
- setz AL;
- }
- }
- else static if ( T.sizeof == int.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 4 Byte CAS
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- mov EDX, writeThis;
- mov EAX, ifThis;
- mov ECX, here;
- lock; // lock always needed to make this op atomic
- cmpxchg [ECX], EDX;
- setz AL;
- }
- }
- else static if ( T.sizeof == long.sizeof && has64BitCAS )
- {
-
- //////////////////////////////////////////////////////////////////
- // 8 Byte CAS on a 32-Bit Processor
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- push EDI;
- push EBX;
- lea EDI, writeThis;
- mov EBX, [EDI];
- mov ECX, 4[EDI];
- lea EDI, ifThis;
- mov EAX, [EDI];
- mov EDX, 4[EDI];
- mov EDI, here;
- lock; // lock always needed to make this op atomic
- cmpxchg8b [EDI];
- setz AL;
- pop EBX;
- pop EDI;
-
- }
-
+ alias IntTy = IntForFloat!T;
+ return atomicCompareExchangeStrong!(succ, fail)(
+ cast(IntTy*)here, cast(IntTy*)ifThis, *cast(IntTy*)&writeThis);
}
else
- {
- static assert( false, "Invalid template type specified." );
- }
- }
-
-
- enum MemoryOrder
- {
- raw,
- acq,
- rel,
- seq,
+ return atomicCompareExchangeStrong!(succ, fail)(here, ifThis, writeThis);
}
- deprecated("Please use MemoryOrder instead.")
- alias MemoryOrder msync;
-
-
- private
+ /// Compare and exchange for mixed-`shared`ness types
+ bool cas(T, V1, V2)(shared(T)* here, V1* ifThis, V2 writeThis) pure nothrow @nogc @trusted
+ if (!is(T == class) && (is(T : V1) || is(shared T : V1)))
+ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
{
- // NOTE: x86 loads implicitly have acquire semantics so a memory
- // barrier is only necessary on releases.
- template needsLoadBarrier( MemoryOrder ms )
- {
- enum bool needsLoadBarrier = ms == MemoryOrder.seq;
- }
-
-
- // NOTE: x86 stores implicitly have release semantics so a memory
- // barrier is only necessary on acquires.
- template needsStoreBarrier( MemoryOrder ms )
- {
- enum bool needsStoreBarrier = ms == MemoryOrder.seq;
- }
- }
-
-
- HeadUnshared!(T) atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( ref const shared T val ) pure nothrow @nogc @safe
- if (!__traits(isFloating, T))
- {
- static assert( ms != MemoryOrder.rel, "invalid MemoryOrder for atomicLoad()" );
- static assert( __traits(isPOD, T), "argument to atomicLoad() must be POD" );
-
- static if ( T.sizeof == byte.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 1 Byte Load
- //////////////////////////////////////////////////////////////////
-
- static if ( needsLoadBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov DL, 0;
- mov AL, 0;
- mov ECX, val;
- lock; // lock always needed to make this op atomic
- cmpxchg [ECX], DL;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov EAX, val;
- mov AL, [EAX];
- }
- }
- }
- else static if ( T.sizeof == short.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 2 Byte Load
- //////////////////////////////////////////////////////////////////
-
- static if ( needsLoadBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov DX, 0;
- mov AX, 0;
- mov ECX, val;
- lock; // lock always needed to make this op atomic
- cmpxchg [ECX], DX;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov EAX, val;
- mov AX, [EAX];
- }
- }
- }
- else static if ( T.sizeof == int.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 4 Byte Load
- //////////////////////////////////////////////////////////////////
-
- static if ( needsLoadBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov EDX, 0;
- mov EAX, 0;
- mov ECX, val;
- lock; // lock always needed to make this op atomic
- cmpxchg [ECX], EDX;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov EAX, val;
- mov EAX, [EAX];
- }
- }
- }
- else static if ( T.sizeof == long.sizeof && has64BitCAS )
+ static if (is (V1 == shared U1, U1))
+ alias Thunk1 = U1;
+ else
{
- //////////////////////////////////////////////////////////////////
- // 8 Byte Load on a 32-Bit Processor
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- push EDI;
- push EBX;
- mov EBX, 0;
- mov ECX, 0;
- mov EAX, 0;
- mov EDX, 0;
- mov EDI, val;
- lock; // lock always needed to make this op atomic
- cmpxchg8b [EDI];
- pop EBX;
- pop EDI;
- }
+ import core.internal.traits : hasUnsharedIndirections;
+ static assert(!hasUnsharedIndirections!V1,
+ "Copying `" ~ shared(T).stringof ~ "* here` to `" ~
+ V1.stringof ~ "* ifThis` would violate shared.");
+ alias Thunk1 = V1;
}
+ static if (is (V2 == shared U2, U2))
+ alias Thunk2 = U2;
else
{
- static assert( false, "Invalid template type specified." );
+ import core.internal.traits : hasUnsharedIndirections;
+ static assert(!hasUnsharedIndirections!V2,
+ "Copying `" ~ V2.stringof ~ "* writeThis` to `" ~
+ shared(T).stringof ~ "* here` would violate shared.");
+ alias Thunk2 = V2;
}
+ static assert (is(T : Thunk1),
+ "Mismatching types for `here` and `ifThis`: `" ~
+ shared(T).stringof ~ "` and `" ~ V1.stringof ~ "`.");
+ return cas(cast(T*)here, cast(Thunk1*)ifThis, *cast(Thunk2*)&writeThis);
}
- void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V1)( ref shared T val, V1 newval ) pure nothrow @nogc @safe
- if ( __traits( compiles, { val = newval; } ) )
+ /// Compare-and-exchange for `class`
+ bool cas(T, V)(shared(T)* here, shared(T)* ifThis, shared(V) writeThis)
+ pure nothrow @nogc @trusted
+ if (is(T == class))
+ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
{
- static assert( ms != MemoryOrder.acq, "invalid MemoryOrder for atomicStore()" );
- static assert( __traits(isPOD, T), "argument to atomicStore() must be POD" );
-
- static if ( T.sizeof == byte.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 1 Byte Store
- //////////////////////////////////////////////////////////////////
-
- static if ( needsStoreBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov EAX, val;
- mov DL, newval;
- lock;
- xchg [EAX], DL;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov EAX, val;
- mov DL, newval;
- mov [EAX], DL;
- }
- }
- }
- else static if ( T.sizeof == short.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 2 Byte Store
- //////////////////////////////////////////////////////////////////
-
- static if ( needsStoreBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov EAX, val;
- mov DX, newval;
- lock;
- xchg [EAX], DX;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov EAX, val;
- mov DX, newval;
- mov [EAX], DX;
- }
- }
- }
- else static if ( T.sizeof == int.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 4 Byte Store
- //////////////////////////////////////////////////////////////////
-
- static if ( needsStoreBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov EAX, val;
- mov EDX, newval;
- lock;
- xchg [EAX], EDX;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov EAX, val;
- mov EDX, newval;
- mov [EAX], EDX;
- }
- }
- }
- else static if ( T.sizeof == long.sizeof && has64BitCAS )
- {
- //////////////////////////////////////////////////////////////////
- // 8 Byte Store on a 32-Bit Processor
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- push EDI;
- push EBX;
- lea EDI, newval;
- mov EBX, [EDI];
- mov ECX, 4[EDI];
- mov EDI, val;
- mov EAX, [EDI];
- mov EDX, 4[EDI];
- L1: lock; // lock always needed to make this op atomic
- cmpxchg8b [EDI];
- jne L1;
- pop EBX;
- pop EDI;
- }
- }
- else
- {
- static assert( false, "Invalid template type specified." );
- }
+ return atomicCompareExchangeStrong!(succ, fail)(
+ cast(T*)here, cast(T*)ifThis, cast(V)writeThis);
}
+}
+/**
+* Stores 'writeThis' to the memory referenced by 'here' if the value
+* referenced by 'here' is equal to 'ifThis'.
+* The 'weak' version of cas may spuriously fail. It is recommended to
+* use `casWeak` only when `cas` would be used in a loop.
+* This operation is both
+* lock-free and atomic.
+*
+* Params:
+* here = The address of the destination variable.
+* writeThis = The value to store.
+* ifThis = The comparison value.
+*
+* Returns:
+* true if the store occurred, false if not.
+*/
+bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(T* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted
+ if (!is(T == shared) && is(T : V1))
+in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
+{
+ // resolve implicit conversions
+ T arg1 = ifThis;
+ T arg2 = writeThis;
- void atomicFence() nothrow @nogc @safe
+ static if (__traits(isFloating, T))
{
- import core.cpuid;
-
- asm pure nothrow @nogc @trusted
- {
- naked;
-
- call sse2;
- test AL, AL;
- jne Lcpuid;
-
- // Fast path: We have SSE2, so just use mfence.
- mfence;
- jmp Lend;
-
- Lcpuid:
-
- // Slow path: We use cpuid to serialize. This is
- // significantly slower than mfence, but is the
- // only serialization facility we have available
- // on older non-SSE2 chips.
- push EBX;
-
- mov EAX, 0;
- cpuid;
-
- pop EBX;
-
- Lend:
-
- ret;
- }
+ alias IntTy = IntForFloat!T;
+ return atomicCompareExchangeWeakNoResult!(succ, fail)(cast(IntTy*)here, *cast(IntTy*)&arg1, *cast(IntTy*)&arg2);
}
+ else
+ return atomicCompareExchangeWeakNoResult!(succ, fail)(here, arg1, arg2);
}
-else version (AsmX86_64)
+
+/// Ditto
+bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(shared(T)* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted
+ if (!is(T == class) && (is(T : V1) || is(shared T : V1)))
+in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
{
- // Uses specialized asm for fast fetch and add operations
- private HeadUnshared!(T) atomicFetchAdd(T)( ref shared T val, size_t mod ) pure nothrow @nogc @trusted
- if ( __traits(isIntegral, T) )
- in
+ static if (is (V1 == shared U1, U1))
+ alias Thunk1 = U1;
+ else
+ alias Thunk1 = V1;
+ static if (is (V2 == shared U2, U2))
+ alias Thunk2 = U2;
+ else
{
- assert( atomicValueIsProperlyAligned(val));
+ import core.internal.traits : hasUnsharedIndirections;
+ static assert(!hasUnsharedIndirections!V2, "Copying `" ~ V2.stringof ~ "* writeThis` to `" ~ shared(T).stringof ~ "* here` would violate shared.");
+ alias Thunk2 = V2;
}
- body
- {
- size_t tmp = mod;
- asm pure nothrow @nogc @trusted
- {
- mov RAX, tmp;
- mov RDX, val;
- }
- static if (T.sizeof == 1) asm pure nothrow @nogc @trusted { lock; xadd[RDX], AL; }
- else static if (T.sizeof == 2) asm pure nothrow @nogc @trusted { lock; xadd[RDX], AX; }
- else static if (T.sizeof == 4) asm pure nothrow @nogc @trusted { lock; xadd[RDX], EAX; }
- else static if (T.sizeof == 8) asm pure nothrow @nogc @trusted { lock; xadd[RDX], RAX; }
+ return casWeak!(succ, fail)(cast(T*)here, *cast(Thunk1*)&ifThis, *cast(Thunk2*)&writeThis);
+}
- asm pure nothrow @nogc @trusted
- {
- mov tmp, RAX;
- }
+/// Ditto
+bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(shared(T)* here, shared(V1) ifThis, shared(V2) writeThis) pure nothrow @nogc @trusted
+ if (is(T == class))
+in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
+{
+ return atomicCompareExchangeWeakNoResult!(succ, fail)(cast(T*)here, cast(V1)ifThis, cast(V2)writeThis);
+}
- return cast(T)tmp;
- }
+/**
+* Stores 'writeThis' to the memory referenced by 'here' if the value
+* referenced by 'here' is equal to the value referenced by 'ifThis'.
+* The prior value referenced by 'here' is written to `ifThis` and
+* returned to the user.
+* The 'weak' version of cas may spuriously fail. It is recommended to
+* use `casWeak` only when `cas` would be used in a loop.
+* This operation is both lock-free and atomic.
+*
+* Params:
+* here = The address of the destination variable.
+* writeThis = The value to store.
+* ifThis = The address of the value to compare, and receives the prior value of `here` as output.
+*
+* Returns:
+* true if the store occurred, false if not.
+*/
+bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V)(T* here, T* ifThis, V writeThis) pure nothrow @nogc @trusted
+ if (!is(T == shared S, S) && !is(V == shared U, U))
+in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
+{
+ // resolve implicit conversions
+ T arg1 = writeThis;
- private HeadUnshared!(T) atomicFetchSub(T)( ref shared T val, size_t mod ) pure nothrow @nogc @safe
- if ( __traits(isIntegral, T) )
+ static if (__traits(isFloating, T))
{
- return atomicFetchAdd(val, -mod);
+ alias IntTy = IntForFloat!T;
+ return atomicCompareExchangeWeak!(succ, fail)(cast(IntTy*)here, cast(IntTy*)ifThis, *cast(IntTy*)&writeThis);
}
+ else
+ return atomicCompareExchangeWeak!(succ, fail)(here, ifThis, writeThis);
+}
- HeadUnshared!(T) atomicOp(string op, T, V1)( ref shared T val, V1 mod ) pure nothrow @nogc
- if ( __traits( compiles, mixin( "*cast(T*)&val" ~ op ~ "mod" ) ) )
- in
+/// Ditto
+bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(shared(T)* here, V1* ifThis, V2 writeThis) pure nothrow @nogc @trusted
+ if (!is(T == class) && (is(T : V1) || is(shared T : V1)))
+in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
+{
+ static if (is (V1 == shared U1, U1))
+ alias Thunk1 = U1;
+ else
{
- assert( atomicValueIsProperlyAligned(val));
+ import core.internal.traits : hasUnsharedIndirections;
+ static assert(!hasUnsharedIndirections!V1, "Copying `" ~ shared(T).stringof ~ "* here` to `" ~ V1.stringof ~ "* ifThis` would violate shared.");
+ alias Thunk1 = V1;
}
- body
+ static if (is (V2 == shared U2, U2))
+ alias Thunk2 = U2;
+ else
{
- // binary operators
- //
- // + - * / % ^^ &
- // | ^ << >> >>> ~ in
- // == != < <= > >=
- static if ( op == "+" || op == "-" || op == "*" || op == "/" ||
- op == "%" || op == "^^" || op == "&" || op == "|" ||
- op == "^" || op == "<<" || op == ">>" || op == ">>>" ||
- op == "~" || // skip "in"
- op == "==" || op == "!=" || op == "<" || op == "<=" ||
- op == ">" || op == ">=" )
- {
- HeadUnshared!(T) get = atomicLoad!(MemoryOrder.raw)( val );
- mixin( "return get " ~ op ~ " mod;" );
- }
- else
- // assignment operators
- //
- // += -= *= /= %= ^^= &=
- // |= ^= <<= >>= >>>= ~=
- static if ( op == "+=" && __traits(isIntegral, T) && __traits(isIntegral, V1))
- {
- return cast(T)(atomicFetchAdd!(T)(val, mod) + mod);
- }
- else static if ( op == "-=" && __traits(isIntegral, T) && __traits(isIntegral, V1))
- {
- return cast(T)(atomicFetchSub!(T)(val, mod) - mod);
- }
- else static if ( op == "+=" || op == "-=" || op == "*=" || op == "/=" ||
- op == "%=" || op == "^^=" || op == "&=" || op == "|=" ||
- op == "^=" || op == "<<=" || op == ">>=" || op == ">>>=" ) // skip "~="
- {
- HeadUnshared!(T) get, set;
-
- do
- {
- get = set = atomicLoad!(MemoryOrder.raw)( val );
- mixin( "set " ~ op ~ " mod;" );
- } while ( !casByRef( val, get, set ) );
- return set;
- }
- else
- {
- static assert( false, "Operation not supported." );
- }
+ import core.internal.traits : hasUnsharedIndirections;
+ static assert(!hasUnsharedIndirections!V2, "Copying `" ~ V2.stringof ~ "* writeThis` to `" ~ shared(T).stringof ~ "* here` would violate shared.");
+ alias Thunk2 = V2;
}
+ static assert (is(T : Thunk1), "Mismatching types for `here` and `ifThis`: `" ~ shared(T).stringof ~ "` and `" ~ V1.stringof ~ "`.");
+ return casWeak!(succ, fail)(cast(T*)here, cast(Thunk1*)ifThis, *cast(Thunk2*)&writeThis);
+}
+/// Ditto
+bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V)(shared(T)* here, shared(T)* ifThis, shared(V) writeThis) pure nothrow @nogc @trusted
+ if (is(T == class))
+in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
+{
+ return atomicCompareExchangeWeak!(succ, fail)(cast(T*)here, cast(T*)ifThis, cast(V)writeThis);
+}
- bool casByRef(T,V1,V2)( ref T value, V1 ifThis, V2 writeThis ) pure nothrow @nogc @trusted
- {
- return cas(&value, ifThis, writeThis);
- }
+/**
+ * Inserts a full load/store memory fence (on platforms that need it). This ensures
+ * that all loads and stores before a call to this function are executed before any
+ * loads and stores after the call.
+ */
+void atomicFence(MemoryOrder order = MemoryOrder.seq)() pure nothrow @nogc @safe
+{
+ core.internal.atomic.atomicFence!order();
+}
- bool cas(T,V1,V2)( shared(T)* here, const V1 ifThis, V2 writeThis ) pure nothrow @nogc @safe
- if ( !is(T == class) && !is(T U : U*) && __traits( compiles, { *here = writeThis; } ) )
- {
- return casImpl(here, ifThis, writeThis);
- }
+/**
+ * Gives a hint to the processor that the calling thread is in a 'spin-wait' loop,
+ * allowing to more efficiently allocate resources.
+ */
+void pause() pure nothrow @nogc @safe
+{
+ core.internal.atomic.pause();
+}
- bool cas(T,V1,V2)( shared(T)* here, const shared(V1) ifThis, shared(V2) writeThis ) pure nothrow @nogc @safe
- if ( is(T == class) && __traits( compiles, { *here = writeThis; } ) )
+/**
+ * Performs the binary operation 'op' on val using 'mod' as the modifier.
+ *
+ * Params:
+ * val = The target variable.
+ * mod = The modifier to apply.
+ *
+ * Returns:
+ * The result of the operation.
+ */
+TailShared!T atomicOp(string op, T, V1)(ref shared T val, V1 mod) pure nothrow @nogc @safe
+ if (__traits(compiles, mixin("*cast(T*)&val" ~ op ~ "mod")))
+in (atomicValueIsProperlyAligned(val))
+{
+ // binary operators
+ //
+ // + - * / % ^^ &
+ // | ^ << >> >>> ~ in
+ // == != < <= > >=
+ static if (op == "+" || op == "-" || op == "*" || op == "/" ||
+ op == "%" || op == "^^" || op == "&" || op == "|" ||
+ op == "^" || op == "<<" || op == ">>" || op == ">>>" ||
+ op == "~" || // skip "in"
+ op == "==" || op == "!=" || op == "<" || op == "<=" ||
+ op == ">" || op == ">=")
{
- return casImpl(here, ifThis, writeThis);
+ T get = atomicLoad!(MemoryOrder.raw, T)(val);
+ mixin("return get " ~ op ~ " mod;");
}
-
- bool cas(T,V1,V2)( shared(T)* here, const shared(V1)* ifThis, shared(V2)* writeThis ) pure nothrow @nogc @safe
- if ( is(T U : U*) && __traits( compiles, { *here = writeThis; } ) )
+ else
+ // assignment operators
+ //
+ // += -= *= /= %= ^^= &=
+ // |= ^= <<= >>= >>>= ~=
+ static if (op == "+=" && __traits(isIntegral, T) && __traits(isIntegral, V1) && T.sizeof <= size_t.sizeof && V1.sizeof <= size_t.sizeof)
{
- return casImpl(here, ifThis, writeThis);
+ return cast(T)(atomicFetchAdd(val, mod) + mod);
}
-
- private bool casImpl(T,V1,V2)( shared(T)* here, V1 ifThis, V2 writeThis ) pure nothrow @nogc @safe
- in
+ else static if (op == "-=" && __traits(isIntegral, T) && __traits(isIntegral, V1) && T.sizeof <= size_t.sizeof && V1.sizeof <= size_t.sizeof)
{
- assert( atomicPtrIsProperlyAligned( here ) );
+ return cast(T)(atomicFetchSub(val, mod) - mod);
}
- body
+ else static if (op == "+=" || op == "-=" || op == "*=" || op == "/=" ||
+ op == "%=" || op == "^^=" || op == "&=" || op == "|=" ||
+ op == "^=" || op == "<<=" || op == ">>=" || op == ">>>=") // skip "~="
{
- static if ( T.sizeof == byte.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 1 Byte CAS
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- mov DL, writeThis;
- mov AL, ifThis;
- mov RCX, here;
- lock; // lock always needed to make this op atomic
- cmpxchg [RCX], DL;
- setz AL;
- }
- }
- else static if ( T.sizeof == short.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 2 Byte CAS
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- mov DX, writeThis;
- mov AX, ifThis;
- mov RCX, here;
- lock; // lock always needed to make this op atomic
- cmpxchg [RCX], DX;
- setz AL;
- }
- }
- else static if ( T.sizeof == int.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 4 Byte CAS
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- mov EDX, writeThis;
- mov EAX, ifThis;
- mov RCX, here;
- lock; // lock always needed to make this op atomic
- cmpxchg [RCX], EDX;
- setz AL;
- }
- }
- else static if ( T.sizeof == long.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 8 Byte CAS on a 64-Bit Processor
- //////////////////////////////////////////////////////////////////
-
- asm pure nothrow @nogc @trusted
- {
- mov RDX, writeThis;
- mov RAX, ifThis;
- mov RCX, here;
- lock; // lock always needed to make this op atomic
- cmpxchg [RCX], RDX;
- setz AL;
- }
- }
- else static if ( T.sizeof == long.sizeof*2 && has128BitCAS)
- {
- //////////////////////////////////////////////////////////////////
- // 16 Byte CAS on a 64-Bit Processor
- //////////////////////////////////////////////////////////////////
- version (Win64){
- //Windows 64 calling convention uses different registers.
- //DMD appears to reverse the register order.
- asm pure nothrow @nogc @trusted
- {
- push RDI;
- push RBX;
- mov R9, writeThis;
- mov R10, ifThis;
- mov R11, here;
-
- mov RDI, R9;
- mov RBX, [RDI];
- mov RCX, 8[RDI];
-
- mov RDI, R10;
- mov RAX, [RDI];
- mov RDX, 8[RDI];
-
- mov RDI, R11;
- lock;
- cmpxchg16b [RDI];
- setz AL;
- pop RBX;
- pop RDI;
- }
-
- }else{
-
- asm pure nothrow @nogc @trusted
- {
- push RDI;
- push RBX;
- lea RDI, writeThis;
- mov RBX, [RDI];
- mov RCX, 8[RDI];
- lea RDI, ifThis;
- mov RAX, [RDI];
- mov RDX, 8[RDI];
- mov RDI, here;
- lock; // lock always needed to make this op atomic
- cmpxchg16b [RDI];
- setz AL;
- pop RBX;
- pop RDI;
- }
- }
- }
- else
+ T set, get = atomicLoad!(MemoryOrder.raw, T)(val);
+ do
{
- static assert( false, "Invalid template type specified." );
- }
+ set = get;
+ mixin("set " ~ op ~ " mod;");
+ } while (!casWeakByRef(val, get, set));
+ return set;
}
-
-
- enum MemoryOrder
+ else
{
- raw,
- acq,
- rel,
- seq,
+ static assert(false, "Operation not supported.");
}
+}
- deprecated("Please use MemoryOrder instead.")
- alias MemoryOrder msync;
+version (D_InlineAsm_X86)
+{
+ enum has64BitXCHG = false;
+ enum has64BitCAS = true;
+ enum has128BitCAS = false;
+}
+else version (D_InlineAsm_X86_64)
+{
+ enum has64BitXCHG = true;
+ enum has64BitCAS = true;
+ enum has128BitCAS = true;
+}
+else version (GNU)
+{
+ import gcc.config;
+ enum has64BitCAS = GNU_Have_64Bit_Atomics;
+ enum has64BitXCHG = GNU_Have_64Bit_Atomics;
+ enum has128BitCAS = GNU_Have_LibAtomic;
+}
+else
+{
+ enum has64BitXCHG = false;
+ enum has64BitCAS = false;
+ enum has128BitCAS = false;
+}
- private
+private
+{
+ bool atomicValueIsProperlyAligned(T)(ref T val) pure nothrow @nogc @trusted
{
- // NOTE: x86 loads implicitly have acquire semantics so a memory
- // barrier is only necessary on releases.
- template needsLoadBarrier( MemoryOrder ms )
- {
- enum bool needsLoadBarrier = ms == MemoryOrder.seq;
- }
-
-
- // NOTE: x86 stores implicitly have release semantics so a memory
- // barrier is only necessary on acquires.
- template needsStoreBarrier( MemoryOrder ms )
- {
- enum bool needsStoreBarrier = ms == MemoryOrder.seq;
- }
+ return atomicPtrIsProperlyAligned(&val);
}
-
- HeadUnshared!(T) atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( ref const shared T val ) pure nothrow @nogc @safe
- if (!__traits(isFloating, T))
+ bool atomicPtrIsProperlyAligned(T)(T* ptr) pure nothrow @nogc @safe
{
- static assert( ms != MemoryOrder.rel, "invalid MemoryOrder for atomicLoad()" );
- static assert( __traits(isPOD, T), "argument to atomicLoad() must be POD" );
-
- static if ( T.sizeof == byte.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 1 Byte Load
- //////////////////////////////////////////////////////////////////
-
- static if ( needsLoadBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov DL, 0;
- mov AL, 0;
- mov RCX, val;
- lock; // lock always needed to make this op atomic
- cmpxchg [RCX], DL;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov AL, [RAX];
- }
- }
- }
- else static if ( T.sizeof == short.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 2 Byte Load
- //////////////////////////////////////////////////////////////////
-
- static if ( needsLoadBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov DX, 0;
- mov AX, 0;
- mov RCX, val;
- lock; // lock always needed to make this op atomic
- cmpxchg [RCX], DX;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov AX, [RAX];
- }
- }
- }
- else static if ( T.sizeof == int.sizeof )
+ // NOTE: Strictly speaking, the x86 supports atomic operations on
+ // unaligned values. However, this is far slower than the
+ // common case, so such behavior should be prohibited.
+ static if (T.sizeof > size_t.sizeof)
{
- //////////////////////////////////////////////////////////////////
- // 4 Byte Load
- //////////////////////////////////////////////////////////////////
-
- static if ( needsLoadBarrier!(ms) )
+ version (X86)
{
- asm pure nothrow @nogc @trusted
- {
- mov EDX, 0;
- mov EAX, 0;
- mov RCX, val;
- lock; // lock always needed to make this op atomic
- cmpxchg [RCX], EDX;
- }
+ // cmpxchg8b only requires 4-bytes alignment
+ return cast(size_t)ptr % size_t.sizeof == 0;
}
else
{
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov EAX, [RAX];
- }
- }
- }
- else static if ( T.sizeof == long.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 8 Byte Load
- //////////////////////////////////////////////////////////////////
-
- static if ( needsLoadBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov RDX, 0;
- mov RAX, 0;
- mov RCX, val;
- lock; // lock always needed to make this op atomic
- cmpxchg [RCX], RDX;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov RAX, [RAX];
- }
- }
- }
- else static if ( T.sizeof == long.sizeof*2 && has128BitCAS )
- {
- //////////////////////////////////////////////////////////////////
- // 16 Byte Load on a 64-Bit Processor
- //////////////////////////////////////////////////////////////////
- version (Win64){
- size_t[2] retVal;
- asm pure nothrow @nogc @trusted
- {
- push RDI;
- push RBX;
- mov RDI, val;
- mov RBX, 0;
- mov RCX, 0;
- mov RAX, 0;
- mov RDX, 0;
- lock; // lock always needed to make this op atomic
- cmpxchg16b [RDI];
- lea RDI, retVal;
- mov [RDI], RAX;
- mov 8[RDI], RDX;
- pop RBX;
- pop RDI;
- }
-
- static if (is(T:U[], U))
- {
- pragma(inline, true)
- static typeof(return) toTrusted(size_t[2] retVal) @trusted
- {
- return *(cast(typeof(return)*) retVal.ptr);
- }
-
- return toTrusted(retVal);
- }
- else
- {
- return cast(typeof(return)) retVal;
- }
- }else{
- asm pure nothrow @nogc @trusted
- {
- push RDI;
- push RBX;
- mov RBX, 0;
- mov RCX, 0;
- mov RAX, 0;
- mov RDX, 0;
- mov RDI, val;
- lock; // lock always needed to make this op atomic
- cmpxchg16b [RDI];
- pop RBX;
- pop RDI;
- }
+ // e.g., x86_64 cmpxchg16b requires 16-bytes alignment
+ return cast(size_t)ptr % T.sizeof == 0;
}
}
else
{
- static assert( false, "Invalid template type specified." );
+ return cast(size_t)ptr % T.sizeof == 0;
}
}
-
- void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V1)( ref shared T val, V1 newval ) pure nothrow @nogc @safe
- if ( __traits( compiles, { val = newval; } ) )
+ template IntForFloat(F)
+ if (__traits(isFloating, F))
{
- static assert( ms != MemoryOrder.acq, "invalid MemoryOrder for atomicStore()" );
- static assert( __traits(isPOD, T), "argument to atomicStore() must be POD" );
-
- static if ( T.sizeof == byte.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 1 Byte Store
- //////////////////////////////////////////////////////////////////
-
- static if ( needsStoreBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov DL, newval;
- lock;
- xchg [RAX], DL;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov DL, newval;
- mov [RAX], DL;
- }
- }
- }
- else static if ( T.sizeof == short.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 2 Byte Store
- //////////////////////////////////////////////////////////////////
-
- static if ( needsStoreBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov DX, newval;
- lock;
- xchg [RAX], DX;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov DX, newval;
- mov [RAX], DX;
- }
- }
- }
- else static if ( T.sizeof == int.sizeof )
- {
- //////////////////////////////////////////////////////////////////
- // 4 Byte Store
- //////////////////////////////////////////////////////////////////
-
- static if ( needsStoreBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov EDX, newval;
- lock;
- xchg [RAX], EDX;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov EDX, newval;
- mov [RAX], EDX;
- }
- }
- }
- else static if ( T.sizeof == long.sizeof && has64BitCAS )
- {
- //////////////////////////////////////////////////////////////////
- // 8 Byte Store on a 64-Bit Processor
- //////////////////////////////////////////////////////////////////
-
- static if ( needsStoreBarrier!(ms) )
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov RDX, newval;
- lock;
- xchg [RAX], RDX;
- }
- }
- else
- {
- asm pure nothrow @nogc @trusted
- {
- mov RAX, val;
- mov RDX, newval;
- mov [RAX], RDX;
- }
- }
- }
- else static if ( T.sizeof == long.sizeof*2 && has128BitCAS )
- {
- //////////////////////////////////////////////////////////////////
- // 16 Byte Store on a 64-Bit Processor
- //////////////////////////////////////////////////////////////////
- version (Win64){
- asm pure nothrow @nogc @trusted
- {
- push RDI;
- push RBX;
- mov R9, val;
- mov R10, newval;
-
- mov RDI, R10;
- mov RBX, [RDI];
- mov RCX, 8[RDI];
-
- mov RDI, R9;
- mov RAX, [RDI];
- mov RDX, 8[RDI];
-
- L1: lock; // lock always needed to make this op atomic
- cmpxchg16b [RDI];
- jne L1;
- pop RBX;
- pop RDI;
- }
- }else{
- asm pure nothrow @nogc @trusted
- {
- push RDI;
- push RBX;
- lea RDI, newval;
- mov RBX, [RDI];
- mov RCX, 8[RDI];
- mov RDI, val;
- mov RAX, [RDI];
- mov RDX, 8[RDI];
- L1: lock; // lock always needed to make this op atomic
- cmpxchg16b [RDI];
- jne L1;
- pop RBX;
- pop RDI;
- }
- }
- }
+ static if (F.sizeof == 4)
+ alias IntForFloat = uint;
+ else static if (F.sizeof == 8)
+ alias IntForFloat = ulong;
else
- {
- static assert( false, "Invalid template type specified." );
- }
+ static assert (false, "Invalid floating point type: " ~ F.stringof ~ ", only support `float` and `double`.");
}
-
- void atomicFence() nothrow @nogc @safe
+ template IntForStruct(S)
+ if (is(S == struct))
{
- // SSE2 is always present in 64-bit x86 chips.
- asm nothrow @nogc @trusted
- {
- naked;
-
- mfence;
- ret;
- }
+ static if (S.sizeof == 1)
+ alias IntForFloat = ubyte;
+ else static if (F.sizeof == 2)
+ alias IntForFloat = ushort;
+ else static if (F.sizeof == 4)
+ alias IntForFloat = uint;
+ else static if (F.sizeof == 8)
+ alias IntForFloat = ulong;
+ else static if (F.sizeof == 16)
+ alias IntForFloat = ulong[2]; // TODO: what's the best type here? slice/delegates pass in registers...
+ else
+ static assert (ValidateStruct!S);
}
-}
-else version (GNU)
-{
- import gcc.builtins;
- HeadUnshared!(T) atomicOp(string op, T, V1)( ref shared T val, V1 mod ) pure nothrow @nogc @trusted
- if ( __traits( compiles, mixin( "*cast(T*)&val" ~ op ~ "mod" ) ) )
+ template ValidateStruct(S)
+ if (is(S == struct))
{
- // binary operators
- //
- // + - * / % ^^ &
- // | ^ << >> >>> ~ in
- // == != < <= > >=
- static if ( op == "+" || op == "-" || op == "*" || op == "/" ||
- op == "%" || op == "^^" || op == "&" || op == "|" ||
- op == "^" || op == "<<" || op == ">>" || op == ">>>" ||
- op == "~" || // skip "in"
- op == "==" || op == "!=" || op == "<" || op == "<=" ||
- op == ">" || op == ">=" )
- {
- HeadUnshared!(T) get = atomicLoad!(MemoryOrder.raw)( val );
- mixin( "return get " ~ op ~ " mod;" );
- }
- else
- // assignment operators
- //
- // += -= *= /= %= ^^= &=
- // |= ^= <<= >>= >>>= ~=
- static if ( op == "+=" || op == "-=" || op == "*=" || op == "/=" ||
- op == "%=" || op == "^^=" || op == "&=" || op == "|=" ||
- op == "^=" || op == "<<=" || op == ">>=" || op == ">>>=" ) // skip "~="
- {
- HeadUnshared!(T) get, set;
-
- do
- {
- get = set = atomicLoad!(MemoryOrder.raw)( val );
- mixin( "set " ~ op ~ " mod;" );
- } while ( !cas( &val, get, set ) );
- return set;
- }
- else
- {
- static assert( false, "Operation not supported." );
- }
- }
+ import core.internal.traits : hasElaborateAssign;
+ // `(x & (x-1)) == 0` checks that x is a power of 2.
+ static assert (S.sizeof <= size_t.sizeof * 2
+ && (S.sizeof & (S.sizeof - 1)) == 0,
+ S.stringof ~ " has invalid size for atomic operations.");
+ static assert (!hasElaborateAssign!S, S.stringof ~ " may not have an elaborate assignment when used with atomic operations.");
- bool cas(T,V1,V2)( shared(T)* here, const V1 ifThis, V2 writeThis ) pure nothrow @nogc @safe
- if ( !is(T == class) && !is(T U : U*) && __traits( compiles, { *here = writeThis; } ) )
- {
- return casImpl(here, ifThis, writeThis);
+ enum ValidateStruct = true;
}
- bool cas(T,V1,V2)( shared(T)* here, const shared(V1) ifThis, shared(V2) writeThis ) pure nothrow @nogc @safe
- if ( is(T == class) && __traits( compiles, { *here = writeThis; } ) )
+ // TODO: it'd be nice if we had @trusted scopes; we could remove this...
+ bool casWeakByRef(T,V1,V2)(ref T value, ref V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted
{
- return casImpl(here, ifThis, writeThis);
+ return casWeak(&value, &ifThis, writeThis);
}
- bool cas(T,V1,V2)( shared(T)* here, const shared(V1)* ifThis, shared(V2)* writeThis ) pure nothrow @nogc @safe
- if ( is(T U : U*) && __traits( compiles, { *here = writeThis; } ) )
+ /* Construct a type with a shared tail, and if possible with an unshared
+ head. */
+ template TailShared(U) if (!is(U == shared))
{
- return casImpl(here, ifThis, writeThis);
+ alias TailShared = .TailShared!(shared U);
}
-
- private bool casImpl(T,V1,V2)( shared(T)* here, V1 ifThis, V2 writeThis ) pure nothrow @nogc @trusted
+ template TailShared(S) if (is(S == shared))
{
- bool res = void;
-
- static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
- {
- static if (T.sizeof == byte.sizeof)
- {
- res = __atomic_compare_exchange_1(here, cast(void*) &ifThis, *cast(ubyte*) &writeThis,
- false, MemoryOrder.seq, MemoryOrder.seq);
- }
- else static if (T.sizeof == short.sizeof)
- {
- res = __atomic_compare_exchange_2(here, cast(void*) &ifThis, *cast(ushort*) &writeThis,
- false, MemoryOrder.seq, MemoryOrder.seq);
- }
- else static if (T.sizeof == int.sizeof)
- {
- res = __atomic_compare_exchange_4(here, cast(void*) &ifThis, *cast(uint*) &writeThis,
- false, MemoryOrder.seq, MemoryOrder.seq);
- }
- else static if (T.sizeof == long.sizeof && GNU_Have_64Bit_Atomics)
- {
- res = __atomic_compare_exchange_8(here, cast(void*) &ifThis, *cast(ulong*) &writeThis,
- false, MemoryOrder.seq, MemoryOrder.seq);
- }
- else static if (GNU_Have_LibAtomic)
+ // Get the unshared variant of S.
+ static if (is(S U == shared U)) {}
+ else static assert(false, "Should never be triggered. The `static " ~
+ "if` declares `U` as the unshared version of the shared type " ~
+ "`S`. `S` is explicitly declared as shared, so getting `U` " ~
+ "should always work.");
+
+ static if (is(S : U))
+ alias TailShared = U;
+ else static if (is(S == struct))
+ {
+ enum implName = () {
+ /* Start with "_impl". If S has a field with that name, append
+ underscores until the clash is resolved. */
+ string name = "_impl";
+ string[] fieldNames;
+ static foreach (alias field; S.tupleof)
+ {
+ fieldNames ~= __traits(identifier, field);
+ }
+ static bool canFind(string[] haystack, string needle)
+ {
+ foreach (candidate; haystack)
+ {
+ if (candidate == needle) return true;
+ }
+ return false;
+ }
+ while (canFind(fieldNames, name)) name ~= "_";
+ return name;
+ } ();
+ struct TailShared
{
- res = __atomic_compare_exchange(T.sizeof, here, cast(void*) &ifThis, cast(void*) &writeThis,
- MemoryOrder.seq, MemoryOrder.seq);
+ static foreach (i, alias field; S.tupleof)
+ {
+ /* On @trusted: This is casting the field from shared(Foo)
+ to TailShared!Foo. The cast is safe because the field has
+ been loaded and is not shared anymore. */
+ mixin("
+ @trusted @property
+ ref " ~ __traits(identifier, field) ~ "()
+ {
+ alias R = TailShared!(typeof(field));
+ return * cast(R*) &" ~ implName ~ ".tupleof[i];
+ }
+ ");
+ }
+ mixin("
+ S " ~ implName ~ ";
+ alias " ~ implName ~ " this;
+ ");
}
- else
- static assert(0, "Invalid template type specified.");
}
else
- {
- static if (T.sizeof == byte.sizeof)
- alias U = byte;
- else static if (T.sizeof == short.sizeof)
- alias U = short;
- else static if (T.sizeof == int.sizeof)
- alias U = int;
- else static if (T.sizeof == long.sizeof)
- alias U = long;
- else
- static assert(0, "Invalid template type specified.");
-
- getAtomicMutex.lock();
- scope(exit) getAtomicMutex.unlock();
-
- if (*cast(U*)here == *cast(U*)&ifThis)
- {
- *here = writeThis;
- res = true;
- }
- else
- res = false;
- }
-
- return res;
+ alias TailShared = S;
}
-
-
- // Memory model types for the __atomic* builtins.
- enum MemoryOrder
+ @safe unittest
{
- raw = 0,
- acq = 2,
- rel = 3,
- seq = 5,
- }
+ // No tail (no indirections) -> fully unshared.
- deprecated("Please use MemoryOrder instead.")
- alias MemoryOrder msync;
+ static assert(is(TailShared!int == int));
+ static assert(is(TailShared!(shared int) == int));
+ static struct NoIndir { int i; }
+ static assert(is(TailShared!NoIndir == NoIndir));
+ static assert(is(TailShared!(shared NoIndir) == NoIndir));
- HeadUnshared!(T) atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( ref const shared T val ) pure nothrow @nogc @trusted
- if (!__traits(isFloating, T))
- {
- static assert(ms != MemoryOrder.rel, "Invalid MemoryOrder for atomicLoad");
- static assert(__traits(isPOD, T), "argument to atomicLoad() must be POD");
-
- static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
- {
- static if (T.sizeof == ubyte.sizeof)
- {
- ubyte value = __atomic_load_1(&val, ms);
- return *cast(HeadUnshared!T*) &value;
- }
- else static if (T.sizeof == ushort.sizeof)
- {
- ushort value = __atomic_load_2(&val, ms);
- return *cast(HeadUnshared!T*) &value;
- }
- else static if (T.sizeof == uint.sizeof)
- {
- uint value = __atomic_load_4(&val, ms);
- return *cast(HeadUnshared!T*) &value;
- }
- else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics)
- {
- ulong value = __atomic_load_8(&val, ms);
- return *cast(HeadUnshared!T*) &value;
- }
- else static if (GNU_Have_LibAtomic)
- {
- T value;
- __atomic_load(T.sizeof, &val, cast(void*)&value, ms);
- return *cast(HeadUnshared!T*) &value;
- }
- else
- static assert(0, "Invalid template type specified.");
- }
- else
- {
- getAtomicMutex.lock();
- scope(exit) getAtomicMutex.unlock();
- return *cast(HeadUnshared!T*)&val;
- }
- }
+ // Tail can be independently shared or is already -> tail-shared.
+ static assert(is(TailShared!(int*) == shared(int)*));
+ static assert(is(TailShared!(shared int*) == shared(int)*));
+ static assert(is(TailShared!(shared(int)*) == shared(int)*));
- void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V1)( ref shared T val, V1 newval ) pure nothrow @nogc @trusted
- if ( __traits( compiles, { val = newval; } ) )
- {
- static assert(ms != MemoryOrder.acq, "Invalid MemoryOrder for atomicStore");
- static assert(__traits(isPOD, T), "argument to atomicLoad() must be POD");
-
- static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
- {
- static if (T.sizeof == ubyte.sizeof)
- {
- __atomic_store_1(&val, *cast(ubyte*) &newval, ms);
- }
- else static if (T.sizeof == ushort.sizeof)
- {
- __atomic_store_2(&val, *cast(ushort*) &newval, ms);
- }
- else static if (T.sizeof == uint.sizeof)
- {
- __atomic_store_4(&val, *cast(uint*) &newval, ms);
- }
- else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics)
- {
- __atomic_store_8(&val, *cast(ulong*) &newval, ms);
- }
- else static if (GNU_Have_LibAtomic)
- {
- __atomic_store(T.sizeof, &val, cast(void*)&newval, ms);
- }
- else
- static assert(0, "Invalid template type specified.");
- }
- else
- {
- getAtomicMutex.lock();
- val = newval;
- getAtomicMutex.unlock();
- }
- }
+ static assert(is(TailShared!(int[]) == shared(int)[]));
+ static assert(is(TailShared!(shared int[]) == shared(int)[]));
+ static assert(is(TailShared!(shared(int)[]) == shared(int)[]));
+ static struct S1 { shared int* p; }
+ static assert(is(TailShared!S1 == S1));
+ static assert(is(TailShared!(shared S1) == S1));
- void atomicFence() nothrow @nogc
- {
- static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
- __atomic_thread_fence(MemoryOrder.seq);
- else
- {
- getAtomicMutex.lock();
- getAtomicMutex.unlock();
- }
- }
+ static struct S2 { shared(int)* p; }
+ static assert(is(TailShared!S2 == S2));
+ static assert(is(TailShared!(shared S2) == S2));
- static if (!GNU_Have_Atomics && !GNU_Have_LibAtomic)
- {
- // Use system mutex for atomics, faking the purity of the functions so
- // that they can be used in pure/nothrow/@safe code.
- extern (C) private pure @trusted @nogc nothrow
- {
- static if (GNU_Thread_Model == ThreadModel.Posix)
- {
- import core.sys.posix.pthread;
- alias atomicMutexHandle = pthread_mutex_t;
+ // Tail follows shared-ness of head -> fully shared.
- pragma(mangle, "pthread_mutex_init") int fakePureMutexInit(pthread_mutex_t*, pthread_mutexattr_t*);
- pragma(mangle, "pthread_mutex_lock") int fakePureMutexLock(pthread_mutex_t*);
- pragma(mangle, "pthread_mutex_unlock") int fakePureMutexUnlock(pthread_mutex_t*);
- }
- else static if (GNU_Thread_Model == ThreadModel.Win32)
- {
- import core.sys.windows.winbase;
- alias atomicMutexHandle = CRITICAL_SECTION;
+ static class C { int i; }
+ static assert(is(TailShared!C == shared C));
+ static assert(is(TailShared!(shared C) == shared C));
- pragma(mangle, "InitializeCriticalSection") int fakePureMutexInit(CRITICAL_SECTION*);
- pragma(mangle, "EnterCriticalSection") void fakePureMutexLock(CRITICAL_SECTION*);
- pragma(mangle, "LeaveCriticalSection") int fakePureMutexUnlock(CRITICAL_SECTION*);
- }
- else
- {
- alias atomicMutexHandle = int;
- }
- }
+ /* However, structs get a wrapper that has getters which cast to
+ TailShared. */
- // Implements lock/unlock operations.
- private struct AtomicMutex
- {
- int lock() pure @trusted @nogc nothrow
- {
- static if (GNU_Thread_Model == ThreadModel.Posix)
- {
- if (!_inited)
- {
- fakePureMutexInit(&_handle, null);
- _inited = true;
- }
- return fakePureMutexLock(&_handle);
- }
- else
- {
- static if (GNU_Thread_Model == ThreadModel.Win32)
- {
- if (!_inited)
- {
- fakePureMutexInit(&_handle);
- _inited = true;
- }
- fakePureMutexLock(&_handle);
- }
- return 0;
- }
- }
+ static struct S3 { int* p; int _impl; int _impl_; int _impl__; }
+ static assert(!is(TailShared!S3 : S3));
+ static assert(is(TailShared!S3 : shared S3));
+ static assert(is(TailShared!(shared S3) == TailShared!S3));
- int unlock() pure @trusted @nogc nothrow
- {
- static if (GNU_Thread_Model == ThreadModel.Posix)
- return fakePureMutexUnlock(&_handle);
- else
- {
- static if (GNU_Thread_Model == ThreadModel.Win32)
- fakePureMutexUnlock(&_handle);
- return 0;
- }
- }
+ static struct S4 { shared(int)** p; }
+ static assert(!is(TailShared!S4 : S4));
+ static assert(is(TailShared!S4 : shared S4));
+ static assert(is(TailShared!(shared S4) == TailShared!S4));
+ }
+}
- private:
- atomicMutexHandle _handle;
- bool _inited;
- }
- // Internal static mutex reference.
- private AtomicMutex* _getAtomicMutex() @trusted @nogc nothrow
- {
- __gshared static AtomicMutex mutex;
- return &mutex;
- }
+////////////////////////////////////////////////////////////////////////////////
+// Unit Tests
+////////////////////////////////////////////////////////////////////////////////
- // Pure alias for _getAtomicMutex.
- pragma(mangle, _getAtomicMutex.mangleof)
- private AtomicMutex* getAtomicMutex() pure @trusted @nogc nothrow @property;
- }
-}
-// This is an ABI adapter that works on all architectures. It type puns
-// floats and doubles to ints and longs, atomically loads them, then puns
-// them back. This is necessary so that they get returned in floating
-// point instead of integer registers.
-HeadUnshared!(T) atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( ref const shared T val ) pure nothrow @nogc @trusted
-if (__traits(isFloating, T))
+version (CoreUnittest)
{
- static if (T.sizeof == int.sizeof)
+ version (D_LP64)
{
- static assert(is(T : float));
- auto ptr = cast(const shared int*) &val;
- auto asInt = atomicLoad!(ms)(*ptr);
- return *(cast(typeof(return)*) &asInt);
+ enum hasDWCAS = has128BitCAS;
}
- else static if (T.sizeof == long.sizeof)
+ else
{
- static assert(is(T : double));
- auto ptr = cast(const shared long*) &val;
- auto asLong = atomicLoad!(ms)(*ptr);
- return *(cast(typeof(return)*) &asLong);
+ enum hasDWCAS = has64BitCAS;
}
- else
+
+ void testXCHG(T)(T val) pure nothrow @nogc @trusted
+ in
{
- static assert(0, "Cannot atomically load 80-bit reals.");
+ assert(val !is T.init);
}
-}
+ do
+ {
+ T base = cast(T)null;
+ shared(T) atom = cast(shared(T))null;
-////////////////////////////////////////////////////////////////////////////////
-// Unit Tests
-////////////////////////////////////////////////////////////////////////////////
+ assert(base !is val, T.stringof);
+ assert(atom is base, T.stringof);
+ assert(atomicExchange(&atom, val) is base, T.stringof);
+ assert(atom is val, T.stringof);
+ }
-version (unittest)
-{
- void testCAS(T)( T val ) pure nothrow @nogc @trusted
+ void testCAS(T)(T val) pure nothrow @nogc @trusted
in
{
assert(val !is T.init);
}
- body
+ do
{
T base = cast(T)null;
shared(T) atom = cast(shared(T))null;
- assert( base !is val, T.stringof );
- assert( atom is base, T.stringof );
+ assert(base !is val, T.stringof);
+ assert(atom is base, T.stringof);
+
+ assert(cas(&atom, base, val), T.stringof);
+ assert(atom is val, T.stringof);
+ assert(!cas(&atom, base, base), T.stringof);
+ assert(atom is val, T.stringof);
- assert( cas( &atom, base, val ), T.stringof );
- assert( atom is val, T.stringof );
- assert( !cas( &atom, base, base ), T.stringof );
- assert( atom is val, T.stringof );
+ atom = cast(shared(T))null;
+
+ shared(T) arg = base;
+ assert(cas(&atom, &arg, val), T.stringof);
+ assert(arg is base, T.stringof);
+ assert(atom is val, T.stringof);
+
+ arg = base;
+ assert(!cas(&atom, &arg, base), T.stringof);
+ assert(arg is val, T.stringof);
+ assert(atom is val, T.stringof);
}
- void testLoadStore(MemoryOrder ms = MemoryOrder.seq, T)( T val = T.init + 1 ) pure nothrow @nogc @trusted
+ void testLoadStore(MemoryOrder ms = MemoryOrder.seq, T)(T val = T.init + 1) pure nothrow @nogc @trusted
{
T base = cast(T) 0;
shared(T) atom = cast(T) 0;
- assert( base !is val );
- assert( atom is base );
- atomicStore!(ms)( atom, val );
- base = atomicLoad!(ms)( atom );
+ assert(base !is val);
+ assert(atom is base);
+ atomicStore!(ms)(atom, val);
+ base = atomicLoad!(ms)(atom);
- assert( base is val, T.stringof );
- assert( atom is val );
+ assert(base is val, T.stringof);
+ assert(atom is val);
}
- void testType(T)( T val = T.init + 1 ) pure nothrow @nogc @safe
+ void testType(T)(T val = T.init + 1) pure nothrow @nogc @safe
{
- testCAS!(T)( val );
- testLoadStore!(MemoryOrder.seq, T)( val );
- testLoadStore!(MemoryOrder.raw, T)( val );
+ static if (T.sizeof < 8 || has64BitXCHG)
+ testXCHG!(T)(val);
+ testCAS!(T)(val);
+ testLoadStore!(MemoryOrder.seq, T)(val);
+ testLoadStore!(MemoryOrder.raw, T)(val);
}
- @safe pure nothrow unittest
+ @betterC @safe pure nothrow unittest
{
testType!(bool)();
@@ -1700,42 +924,81 @@ version (unittest)
testType!(int)();
testType!(uint)();
+ }
+
+ @safe pure nothrow unittest
+ {
testType!(shared int*)();
+ static interface Inter {}
+ static class KlassImpl : Inter {}
+ testXCHG!(shared Inter)(new shared(KlassImpl));
+ testCAS!(shared Inter)(new shared(KlassImpl));
+
static class Klass {}
- testCAS!(shared Klass)( new shared(Klass) );
+ testXCHG!(shared Klass)(new shared(Klass));
+ testCAS!(shared Klass)(new shared(Klass));
+
+ testXCHG!(shared int)(42);
- testType!(float)(1.0f);
+ testType!(float)(0.1f);
- static if ( has64BitCAS )
+ static if (has64BitCAS)
{
- testType!(double)(1.0);
+ testType!(double)(0.1);
testType!(long)();
testType!(ulong)();
}
+ static if (has128BitCAS)
+ {
+ () @trusted
+ {
+ align(16) struct Big { long a, b; }
+
+ shared(Big) atom;
+ shared(Big) base;
+ shared(Big) arg;
+ shared(Big) val = Big(1, 2);
+
+ assert(cas(&atom, arg, val), Big.stringof);
+ assert(atom is val, Big.stringof);
+ assert(!cas(&atom, arg, val), Big.stringof);
+ assert(atom is val, Big.stringof);
+
+ atom = Big();
+ assert(cas(&atom, &arg, val), Big.stringof);
+ assert(arg is base, Big.stringof);
+ assert(atom is val, Big.stringof);
+
+ arg = Big();
+ assert(!cas(&atom, &arg, base), Big.stringof);
+ assert(arg is val, Big.stringof);
+ assert(atom is val, Big.stringof);
+ }();
+ }
shared(size_t) i;
- atomicOp!"+="( i, cast(size_t) 1 );
- assert( i == 1 );
+ atomicOp!"+="(i, cast(size_t) 1);
+ assert(i == 1);
- atomicOp!"-="( i, cast(size_t) 1 );
- assert( i == 0 );
+ atomicOp!"-="(i, cast(size_t) 1);
+ assert(i == 0);
- shared float f = 0;
- atomicOp!"+="( f, 1 );
- assert( f == 1 );
+ shared float f = 0.1f;
+ atomicOp!"+="(f, 0.1f);
+ assert(f > 0.1999f && f < 0.2001f);
- static if ( has64BitCAS )
+ static if (has64BitCAS)
{
- shared double d = 0;
- atomicOp!"+="( d, 1 );
- assert( d == 1 );
+ shared double d = 0.1;
+ atomicOp!"+="(d, 0.1);
+ assert(d > 0.1999 && d < 0.2001);
}
}
- pure nothrow unittest
+ @betterC pure nothrow unittest
{
static if (has128BitCAS)
{
@@ -1756,15 +1019,6 @@ version (unittest)
assert(b.value1 == 3 && b.value2 ==4);
}
- version (D_LP64)
- {
- enum hasDWCAS = has128BitCAS;
- }
- else
- {
- enum hasDWCAS = has64BitCAS;
- }
-
static if (hasDWCAS)
{
static struct List { size_t gen; List* next; }
@@ -1773,9 +1027,48 @@ version (unittest)
assert(head.gen == 1);
assert(cast(size_t)head.next == 1);
}
+
+ // https://issues.dlang.org/show_bug.cgi?id=20629
+ static struct Struct
+ {
+ uint a, b;
+ }
+ shared Struct s1 = Struct(1, 2);
+ atomicStore(s1, Struct(3, 4));
+ assert(cast(uint) s1.a == 3);
+ assert(cast(uint) s1.b == 4);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=20844
+ static if (hasDWCAS)
+ {
+ debug: // tests CAS in-contract
+
+ pure nothrow unittest
+ {
+ import core.exception : AssertError;
+
+ align(16) shared ubyte[2 * size_t.sizeof + 1] data;
+ auto misalignedPointer = cast(size_t[2]*) &data[1];
+ size_t[2] x;
+
+ try
+ cas(misalignedPointer, x, x);
+ catch (AssertError)
+ return;
+
+ assert(0, "should have failed");
+ }
}
- pure nothrow unittest
+ @betterC pure nothrow @nogc @safe unittest
+ {
+ int a;
+ if (casWeak!(MemoryOrder.acq_rel, MemoryOrder.raw)(&a, 0, 4))
+ assert(a == 4);
+ }
+
+ @betterC pure nothrow unittest
{
static struct S { int val; }
auto s = shared(S)(1);
@@ -1794,11 +1087,6 @@ version (unittest)
shared(S*) writeThis2 = null;
assert(cas(&ptr, ifThis2, writeThis2));
assert(ptr is null);
-
- // head unshared target doesn't want atomic CAS
- shared(S)* ptr2;
- static assert(!__traits(compiles, cas(&ptr2, ifThis, writeThis)));
- static assert(!__traits(compiles, cas(&ptr2, ifThis2, writeThis2)));
}
unittest
@@ -1838,7 +1126,7 @@ version (unittest)
}
// === atomicFetchAdd and atomicFetchSub operations ====
- pure nothrow @nogc @safe unittest
+ @betterC pure nothrow @nogc @safe unittest
{
shared ubyte u8 = 1;
shared ushort u16 = 2;
@@ -1853,7 +1141,7 @@ version (unittest)
assert(atomicOp!"+="(i8, 8) == 13);
assert(atomicOp!"+="(i16, 8) == 14);
assert(atomicOp!"+="(i32, 8) == 15);
- version (AsmX86_64)
+ version (D_LP64)
{
shared ulong u64 = 4;
shared long i64 = 8;
@@ -1862,7 +1150,30 @@ version (unittest)
}
}
- pure nothrow @nogc @safe unittest
+ @betterC pure nothrow @nogc unittest
+ {
+ byte[10] byteArray = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19];
+ ulong[10] ulongArray = [2, 4, 6, 8, 10, 12, 14, 16, 19, 20];
+
+ {
+ auto array = byteArray;
+ byte* ptr = &array[0];
+ byte* prevPtr = atomicFetchAdd(ptr, 3);
+ assert(prevPtr == &array[0]);
+ assert(*prevPtr == 1);
+ assert(*ptr == 7);
+ }
+ {
+ auto array = ulongArray;
+ ulong* ptr = &array[0];
+ ulong* prevPtr = atomicFetchAdd(ptr, 3);
+ assert(prevPtr == &array[0]);
+ assert(*prevPtr == 2);
+ assert(*ptr == 8);
+ }
+ }
+
+ @betterC pure nothrow @nogc @safe unittest
{
shared ubyte u8 = 1;
shared ushort u16 = 2;
@@ -1877,7 +1188,7 @@ version (unittest)
assert(atomicOp!"-="(i8, 1) == 4);
assert(atomicOp!"-="(i16, 1) == 5);
assert(atomicOp!"-="(i32, 1) == 6);
- version (AsmX86_64)
+ version (D_LP64)
{
shared ulong u64 = 4;
shared long i64 = 8;
@@ -1886,16 +1197,83 @@ version (unittest)
}
}
- pure nothrow @nogc @safe unittest // issue 16651
+ @betterC pure nothrow @nogc unittest
+ {
+ byte[10] byteArray = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19];
+ ulong[10] ulongArray = [2, 4, 6, 8, 10, 12, 14, 16, 19, 20];
+
+ {
+ auto array = byteArray;
+ byte* ptr = &array[5];
+ byte* prevPtr = atomicFetchSub(ptr, 4);
+ assert(prevPtr == &array[5]);
+ assert(*prevPtr == 11);
+ assert(*ptr == 3); // https://issues.dlang.org/show_bug.cgi?id=21578
+ }
+ {
+ auto array = ulongArray;
+ ulong* ptr = &array[5];
+ ulong* prevPtr = atomicFetchSub(ptr, 4);
+ assert(prevPtr == &array[5]);
+ assert(*prevPtr == 12);
+ assert(*ptr == 4); // https://issues.dlang.org/show_bug.cgi?id=21578
+ }
+ }
+
+ @betterC pure nothrow @nogc @safe unittest // issue 16651
{
shared ulong a = 2;
uint b = 1;
- atomicOp!"-="( a, b );
+ atomicOp!"-="(a, b);
assert(a == 1);
shared uint c = 2;
ubyte d = 1;
- atomicOp!"-="( c, d );
+ atomicOp!"-="(c, d);
assert(c == 1);
}
+
+ pure nothrow @safe unittest // issue 16230
+ {
+ shared int i;
+ static assert(is(typeof(atomicLoad(i)) == int));
+
+ shared int* p;
+ static assert(is(typeof(atomicLoad(p)) == shared(int)*));
+
+ shared int[] a;
+ static if (__traits(compiles, atomicLoad(a)))
+ {
+ static assert(is(typeof(atomicLoad(a)) == shared(int)[]));
+ }
+
+ static struct S { int* _impl; }
+ shared S s;
+ static assert(is(typeof(atomicLoad(s)) : shared S));
+ static assert(is(typeof(atomicLoad(s)._impl) == shared(int)*));
+ auto u = atomicLoad(s);
+ assert(u._impl is null);
+ u._impl = new shared int(42);
+ assert(atomicLoad(*u._impl) == 42);
+
+ static struct S2 { S s; }
+ shared S2 s2;
+ static assert(is(typeof(atomicLoad(s2).s) == TailShared!S));
+
+ static struct S3 { size_t head; int* tail; }
+ shared S3 s3;
+ static if (__traits(compiles, atomicLoad(s3)))
+ {
+ static assert(is(typeof(atomicLoad(s3).head) == size_t));
+ static assert(is(typeof(atomicLoad(s3).tail) == shared(int)*));
+ }
+
+ static class C { int i; }
+ shared C c;
+ static assert(is(typeof(atomicLoad(c)) == shared C));
+
+ static struct NoIndirections { int i; }
+ shared NoIndirections n;
+ static assert(is(typeof(atomicLoad(n)) == NoIndirections));
+ }
}
diff --git a/libphobos/libdruntime/core/attribute.d b/libphobos/libdruntime/core/attribute.d
index 9d350d8e100..b0b973fbfa6 100644
--- a/libphobos/libdruntime/core/attribute.d
+++ b/libphobos/libdruntime/core/attribute.d
@@ -15,6 +15,56 @@
*/
module core.attribute;
+version (GNU)
+ public import gcc.attributes;
+
+version (LDC)
+ public import ldc.attributes;
+
+version (D_ObjectiveC)
+{
+ version = UdaOptional;
+ version = UdaSelector;
+}
+
+version (Posix)
+ version = UdaGNUAbiTag;
+
+version (CoreDdoc)
+{
+ version = UdaGNUAbiTag;
+ version = UdaOptional;
+ version = UdaSelector;
+}
+
+/**
+ * Use this attribute to specify that a global symbol should be emitted with
+ * weak linkage. This is primarily useful in defining library functions that
+ * can be overridden by user code, though it can also be used with shared and
+ * static variables too.
+ *
+ * The overriding symbol must have the same type as the weak symbol. In
+ * addition, if it designates a variable it must also have the same size and
+ * alignment as the weak symbol.
+ *
+ * Quote from the LLVM manual: "Note that weak linkage does not actually allow
+ * the optimizer to inline the body of this function into callers because it
+ * doesn’t know if this definition of the function is the definitive definition
+ * within the program or whether it will be overridden by a stronger
+ * definition."
+ *
+ * This attribute is only meaningful to the GNU and LLVM D compilers. The
+ * Digital Mars D compiler emits all symbols with weak linkage by default.
+ */
+version (DigitalMars)
+{
+ enum weak;
+}
+else
+{
+ // GDC and LDC declare this attribute in their own modules.
+}
+
/**
* Use this attribute to attach an Objective-C selector to a method.
*
@@ -51,7 +101,143 @@ module core.attribute;
* }
* ---
*/
-version (D_ObjectiveC) struct selector
+version (UdaSelector) struct selector
{
string selector;
}
+
+/**
+ * Use this attribute to make an Objective-C interface method optional.
+ *
+ * An optional method is a method that does **not** have to be implemented in
+ * the class that implements the interface. To safely call an optional method,
+ * a runtime check should be performed to make sure the receiver implements the
+ * method.
+ *
+ * This is a special compiler recognized attribute, it has several
+ * requirements, which all will be enforced by the compiler:
+ *
+ * * The attribute can only be attached to methods which have Objective-C
+ * linkage. That is, a method inside an interface declared as `extern (Objective-C)`
+ *
+ * * It can only be used for methods that are declared inside an interface
+ * * It can only be used once in a method declaration
+ * * It cannot be attached to a method that is a template
+ *
+ * Examples:
+ * ---
+ * import core.attribute : optional, selector;
+ *
+ * extern (Objective-C):
+ *
+ * struct objc_selector;
+ * alias SEL = objc_selector*;
+ *
+ * SEL sel_registerName(in char* str);
+ *
+ * extern class NSObject
+ * {
+ * bool respondsToSelector(SEL sel) @selector("respondsToSelector:");
+ * }
+ *
+ * interface Foo
+ * {
+ * @optional void foo() @selector("foo");
+ * @optional void bar() @selector("bar");
+ * }
+ *
+ * class Bar : NSObject
+ * {
+ * static Bar alloc() @selector("alloc");
+ * Bar init() @selector("init");
+ *
+ * void bar() @selector("bar")
+ * {
+ * }
+ * }
+ *
+ * extern (D) void main()
+ * {
+ * auto bar = Bar.alloc.init;
+ *
+ * if (bar.respondsToSelector(sel_registerName("bar")))
+ * bar.bar();
+ * }
+ * ---
+ */
+version (UdaOptional)
+ enum optional;
+
+/**
+ * Use this attribute to declare an ABI tag on a C++ symbol.
+ *
+ * ABI tag is an attribute introduced by the GNU C++ compiler.
+ * It modifies the mangled name of the symbol to incorporate the tag name,
+ * in order to distinguish from an earlier version with a different ABI.
+ *
+ * This is a special compiler recognized attribute, it has a few
+ * requirements, which all will be enforced by the compiler:
+ *
+ * $(UL
+ * $(LI
+ * There can only be one such attribute per symbol.
+ * ),
+ * $(LI
+ * The attribute can only be attached to an `extern(C++)` symbol
+ * (`struct`, `class`, `enum`, function, and their templated counterparts).
+ * ),
+ * $(LI
+ * The attribute cannot be applied to C++ namespaces.
+ * This is to prevent confusion with the C++ semantic, which allows it to
+ * be applied to namespaces.
+ * ),
+ * $(LI
+ * The string arguments must only contain valid characters
+ * for C++ name mangling which currently include alphanumerics
+ * and the underscore character.
+ * ),
+ * )
+ *
+ * This UDA is not transitive, and inner scope do not inherit outer scopes'
+ * ABI tag. See examples below for how to translate a C++ declaration to D.
+ * Also note that entries in this UDA will be automatically sorted
+ * alphabetically, hence `gnuAbiTag("c", "b", "a")` will appear as
+ * `@gnuAbiTag("a", "b", "c")`.
+ *
+ * See_Also:
+ * $(LINK2 https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.abi-tag, Itanium ABI spec)
+ * $(LINK2 https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html, GCC attributes documentation).
+ *
+ * Examples:
+ * ---
+ * // ---- foo.cpp
+ * struct [[gnu::abi_tag ("tag1", "tag2")]] Tagged1_2
+ * {
+ * struct [[gnu::abi_tag ("tag3")]] Tagged3
+ * {
+ * [[gnu::abi_tag ("tag4")]]
+ * int Tagged4 () { return 42; }
+ * }
+ * }
+ * Tagged1_2 inst1;
+ * // ---- foo.d
+ * @gnuAbiTag("tag1", "tag2") struct Tagged1_2
+ * {
+ * // Notice the repetition
+ * @gnuAbiTag("tag1", "tag2", "tag3") struct Tagged3
+ * {
+ * @gnuAbiTag("tag1", "tag2", "tag3", "tag4") int Tagged4 ();
+ * }
+ * }
+ * extern __gshared Tagged1_2 inst1;
+ * ---
+ */
+version (UdaGNUAbiTag) struct gnuAbiTag
+{
+ string[] tags;
+
+ this(string[] tags...)
+ {
+ this.tags = tags;
+ }
+}
diff --git a/libphobos/libdruntime/core/bitop.d b/libphobos/libdruntime/core/bitop.d
index 25b5cd515b2..40f224214e3 100644
--- a/libphobos/libdruntime/core/bitop.d
+++ b/libphobos/libdruntime/core/bitop.d
@@ -758,11 +758,13 @@ version (DigitalMars) version (AnyX86)
}
+// @@@DEPRECATED_2.099@@@
deprecated("volatileLoad has been moved to core.volatile. Use core.volatile.volatileLoad instead.")
{
public import core.volatile : volatileLoad;
}
+// @@@DEPRECATED_2.099@@@
deprecated("volatileStore has been moved to core.volatile. Use core.volatile.volatileStore instead.")
{
public import core.volatile : volatileStore;
@@ -951,6 +953,9 @@ pure T rol(T)(const T value, const uint count)
if (__traits(isIntegral, T) && __traits(isUnsigned, T))
{
assert(count < 8 * T.sizeof);
+ if (count == 0)
+ return cast(T) value;
+
return cast(T) ((value << count) | (value >> (T.sizeof * 8 - count)));
}
/// ditto
@@ -958,6 +963,9 @@ pure T ror(T)(const T value, const uint count)
if (__traits(isIntegral, T) && __traits(isUnsigned, T))
{
assert(count < 8 * T.sizeof);
+ if (count == 0)
+ return cast(T) value;
+
return cast(T) ((value >> count) | (value << (T.sizeof * 8 - count)));
}
/// ditto
@@ -965,6 +973,9 @@ pure T rol(uint count, T)(const T value)
if (__traits(isIntegral, T) && __traits(isUnsigned, T))
{
static assert(count < 8 * T.sizeof);
+ static if (count == 0)
+ return cast(T) value;
+
return cast(T) ((value << count) | (value >> (T.sizeof * 8 - count)));
}
/// ditto
@@ -972,6 +983,9 @@ pure T ror(uint count, T)(const T value)
if (__traits(isIntegral, T) && __traits(isUnsigned, T))
{
static assert(count < 8 * T.sizeof);
+ static if (count == 0)
+ return cast(T) value;
+
return cast(T) ((value >> count) | (value << (T.sizeof * 8 - count)));
}
@@ -994,4 +1008,9 @@ unittest
assert(rol!3(a) == 0b10000111);
assert(ror!3(a) == 0b00011110);
+
+ enum c = rol(uint(1), 0);
+ enum d = ror(uint(1), 0);
+ assert(c == uint(1));
+ assert(d == uint(1));
}
diff --git a/libphobos/libdruntime/core/builtins.d b/libphobos/libdruntime/core/builtins.d
new file mode 100644
index 00000000000..f2ca5038c59
--- /dev/null
+++ b/libphobos/libdruntime/core/builtins.d
@@ -0,0 +1,19 @@
+/**********************************************
+ * This module implements common builtins for the D frontend.
+ *
+ * Copyright: Copyright © 2019, The D Language Foundation
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Authors: Walter Bright
+ * Source: $(DRUNTIMESRC core/builtins.d)
+ */
+
+module core.builtins;
+
+version (GNU)
+ public import gcc.builtins;
+
+version (LDC)
+ public import ldc.intrinsics;
+
+/// Writes `s` to `stderr` during CTFE (does nothing at runtime).
+void __ctfeWrite(scope const(char)[] s) @nogc @safe pure nothrow {}
diff --git a/libphobos/libdruntime/core/checkedint.d b/libphobos/libdruntime/core/checkedint.d
index 57209adcbeb..49a5c11d137 100644
--- a/libphobos/libdruntime/core/checkedint.d
+++ b/libphobos/libdruntime/core/checkedint.d
@@ -19,6 +19,9 @@
* relative to the cost of the operation itself, compiler implementations are free
* to recognize them and generate equivalent and faster code.
*
+ * The functions here are templates so they can be used with -betterC,
+ * as betterC does not link with this library.
+ *
* References: $(LINK2 http://blog.regehr.org/archives/1139, Fast Integer Overflow Checks)
* Copyright: Copyright (c) Walter Bright 2014.
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
@@ -28,6 +31,8 @@
module core.checkedint;
+import core.internal.attributes : betterC;
+
nothrow:
@safe:
@nogc:
@@ -55,20 +60,27 @@ int adds()(int x, int y, ref bool overflow)
return cast(int)r;
}
+///
+@betterC
unittest
{
bool overflow;
assert(adds(2, 3, overflow) == 5);
assert(!overflow);
+
assert(adds(1, int.max - 1, overflow) == int.max);
assert(!overflow);
+
assert(adds(int.min + 1, -1, overflow) == int.min);
assert(!overflow);
+
assert(adds(int.max, 1, overflow) == int.min);
assert(overflow);
+
overflow = false;
assert(adds(int.min, -1, overflow) == int.max);
assert(overflow);
+
assert(adds(0, 0, overflow) == 0);
assert(overflow); // sticky
}
@@ -84,20 +96,27 @@ long adds()(long x, long y, ref bool overflow)
return r;
}
+///
+@betterC
unittest
{
bool overflow;
assert(adds(2L, 3L, overflow) == 5);
assert(!overflow);
+
assert(adds(1L, long.max - 1, overflow) == long.max);
assert(!overflow);
+
assert(adds(long.min + 1, -1, overflow) == long.min);
assert(!overflow);
+
assert(adds(long.max, 1, overflow) == long.min);
assert(overflow);
+
overflow = false;
assert(adds(long.min, -1, overflow) == long.max);
assert(overflow);
+
assert(adds(0L, 0L, overflow) == 0);
assert(overflow); // sticky
}
@@ -152,25 +171,42 @@ pragma(inline, true)
uint addu()(uint x, uint y, ref bool overflow)
{
immutable uint r = x + y;
- if (r < x || r < y)
+ immutable bool o = r < x;
+ assert(o == (r < y));
+ if (o)
overflow = true;
return r;
}
+///
+@betterC
unittest
{
+ for (uint i = 0; i < 10; ++i)
+ {
+ bool overflow;
+ immutable uint r = addu (uint.max - i, uint.max - i, overflow);
+ assert (r == 2 * (uint.max - i));
+ assert (overflow);
+ }
+
bool overflow;
assert(addu(2, 3, overflow) == 5);
assert(!overflow);
+
assert(addu(1, uint.max - 1, overflow) == uint.max);
assert(!overflow);
+
assert(addu(uint.min, -1, overflow) == uint.max);
assert(!overflow);
+
assert(addu(uint.max, 1, overflow) == uint.min);
assert(overflow);
+
overflow = false;
assert(addu(uint.min + 1, -1, overflow) == uint.min);
assert(overflow);
+
assert(addu(0, 0, overflow) == 0);
assert(overflow); // sticky
}
@@ -180,25 +216,34 @@ pragma(inline, true)
ulong addu()(ulong x, ulong y, ref bool overflow)
{
immutable ulong r = x + y;
- if (r < x || r < y)
+ immutable bool o = r < x;
+ assert(o == (r < y));
+ if (o)
overflow = true;
return r;
}
+///
+@betterC
unittest
{
bool overflow;
assert(addu(2L, 3L, overflow) == 5);
assert(!overflow);
+
assert(addu(1, ulong.max - 1, overflow) == ulong.max);
assert(!overflow);
+
assert(addu(ulong.min, -1L, overflow) == ulong.max);
assert(!overflow);
+
assert(addu(ulong.max, 1, overflow) == ulong.min);
assert(overflow);
+
overflow = false;
assert(addu(ulong.min + 1, -1L, overflow) == ulong.min);
assert(overflow);
+
assert(addu(0L, 0L, overflow) == 0);
assert(overflow); // sticky
}
@@ -210,7 +255,9 @@ pragma(inline, true)
ucent addu()(ucent x, ucent y, ref bool overflow)
{
immutable ucent r = x + y;
- if (r < x || r < y)
+ immutable bool o = r < x;
+ assert(o == (r < y));
+ if (o)
overflow = true;
return r;
}
@@ -257,20 +304,27 @@ int subs()(int x, int y, ref bool overflow)
return cast(int)r;
}
+///
+@betterC
unittest
{
bool overflow;
assert(subs(2, -3, overflow) == 5);
assert(!overflow);
+
assert(subs(1, -int.max + 1, overflow) == int.max);
assert(!overflow);
+
assert(subs(int.min + 1, 1, overflow) == int.min);
assert(!overflow);
+
assert(subs(int.max, -1, overflow) == int.min);
assert(overflow);
+
overflow = false;
assert(subs(int.min, 1, overflow) == int.max);
assert(overflow);
+
assert(subs(0, 0, overflow) == 0);
assert(overflow); // sticky
}
@@ -286,22 +340,30 @@ long subs()(long x, long y, ref bool overflow)
return r;
}
+///
+@betterC
unittest
{
bool overflow;
assert(subs(2L, -3L, overflow) == 5);
assert(!overflow);
+
assert(subs(1L, -long.max + 1, overflow) == long.max);
assert(!overflow);
+
assert(subs(long.min + 1, 1, overflow) == long.min);
assert(!overflow);
+
assert(subs(-1L, long.min, overflow) == long.max);
assert(!overflow);
+
assert(subs(long.max, -1, overflow) == long.min);
assert(overflow);
+
overflow = false;
assert(subs(long.min, 1, overflow) == long.max);
assert(overflow);
+
assert(subs(0L, 0L, overflow) == 0);
assert(overflow); // sticky
}
@@ -362,20 +424,27 @@ uint subu()(uint x, uint y, ref bool overflow)
return x - y;
}
+///
+@betterC
unittest
{
bool overflow;
assert(subu(3, 2, overflow) == 1);
assert(!overflow);
+
assert(subu(uint.max, 1, overflow) == uint.max - 1);
assert(!overflow);
+
assert(subu(1, 1, overflow) == uint.min);
assert(!overflow);
+
assert(subu(0, 1, overflow) == uint.max);
assert(overflow);
+
overflow = false;
assert(subu(uint.max - 1, uint.max, overflow) == uint.max);
assert(overflow);
+
assert(subu(0, 0, overflow) == 0);
assert(overflow); // sticky
}
@@ -390,20 +459,27 @@ ulong subu()(ulong x, ulong y, ref bool overflow)
return x - y;
}
+///
+@betterC
unittest
{
bool overflow;
assert(subu(3UL, 2UL, overflow) == 1);
assert(!overflow);
+
assert(subu(ulong.max, 1, overflow) == ulong.max - 1);
assert(!overflow);
+
assert(subu(1UL, 1UL, overflow) == ulong.min);
assert(!overflow);
+
assert(subu(0UL, 1UL, overflow) == ulong.max);
assert(overflow);
+
overflow = false;
assert(subu(ulong.max - 1, ulong.max, overflow) == ulong.max);
assert(overflow);
+
assert(subu(0UL, 0UL, overflow) == 0);
assert(overflow); // sticky
}
@@ -457,17 +533,23 @@ int negs()(int x, ref bool overflow)
return -x;
}
+///
+@betterC
unittest
{
bool overflow;
assert(negs(0, overflow) == -0);
assert(!overflow);
+
assert(negs(1234, overflow) == -1234);
assert(!overflow);
+
assert(negs(-5678, overflow) == 5678);
assert(!overflow);
+
assert(negs(int.min, overflow) == -int.min);
assert(overflow);
+
assert(negs(0, overflow) == -0);
assert(overflow); // sticky
}
@@ -481,17 +563,23 @@ long negs()(long x, ref bool overflow)
return -x;
}
+///
+@betterC
unittest
{
bool overflow;
assert(negs(0L, overflow) == -0);
assert(!overflow);
+
assert(negs(1234L, overflow) == -1234);
assert(!overflow);
+
assert(negs(-5678L, overflow) == 5678);
assert(!overflow);
+
assert(negs(long.min, overflow) == -long.min);
assert(overflow);
+
assert(negs(0L, overflow) == -0);
assert(overflow); // sticky
}
@@ -546,22 +634,30 @@ int muls()(int x, int y, ref bool overflow)
return cast(int)r;
}
+///
+@betterC
unittest
{
bool overflow;
assert(muls(2, 3, overflow) == 6);
assert(!overflow);
+
assert(muls(-200, 300, overflow) == -60_000);
assert(!overflow);
+
assert(muls(1, int.max, overflow) == int.max);
assert(!overflow);
+
assert(muls(int.min, 1, overflow) == int.min);
assert(!overflow);
+
assert(muls(int.max, 2, overflow) == (int.max * 2));
assert(overflow);
+
overflow = false;
assert(muls(int.min, -1, overflow) == int.min);
assert(overflow);
+
assert(muls(0, 0, overflow) == 0);
assert(overflow); // sticky
}
@@ -579,25 +675,34 @@ long muls()(long x, long y, ref bool overflow)
return r;
}
+///
+@betterC
unittest
{
bool overflow;
assert(muls(2L, 3L, overflow) == 6);
assert(!overflow);
+
assert(muls(-200L, 300L, overflow) == -60_000);
assert(!overflow);
+
assert(muls(1, long.max, overflow) == long.max);
assert(!overflow);
+
assert(muls(long.min, 1L, overflow) == long.min);
assert(!overflow);
+
assert(muls(long.max, 2L, overflow) == (long.max * 2));
assert(overflow);
overflow = false;
+
assert(muls(-1L, long.min, overflow) == long.min);
assert(overflow);
+
overflow = false;
assert(muls(long.min, -1L, overflow) == long.min);
assert(overflow);
+
assert(muls(0L, 0L, overflow) == 0);
assert(overflow); // sticky
}
@@ -652,7 +757,6 @@ unittest
* Returns:
* the product
*/
-
pragma(inline, true)
uint mulu()(uint x, uint y, ref bool overflow)
{
@@ -662,6 +766,7 @@ uint mulu()(uint x, uint y, ref bool overflow)
return cast(uint) r;
}
+@betterC
unittest
{
void test(uint x, uint y, uint r, bool overflow) @nogc nothrow
@@ -705,6 +810,7 @@ ulong mulu()(ulong x, ulong y, ref bool overflow)
return r;
}
+@betterC
unittest
{
void test(T, U)(T x, U y, ulong r, bool overflow) @nogc nothrow
diff --git a/libphobos/libdruntime/core/demangle.d b/libphobos/libdruntime/core/demangle.d
index 4458b70122b..ad9b44a1ee5 100644
--- a/libphobos/libdruntime/core/demangle.d
+++ b/libphobos/libdruntime/core/demangle.d
@@ -54,13 +54,13 @@ pure @safe:
enum AddType { no, yes }
- this( const(char)[] buf_, char[] dst_ = null )
+ this( return const(char)[] buf_, return char[] dst_ = null )
{
this( buf_, AddType.yes, dst_ );
}
- this( const(char)[] buf_, AddType addType_, char[] dst_ = null )
+ this( return const(char)[] buf_, AddType addType_, return char[] dst_ = null )
{
buf = buf_;
addType = addType_;
@@ -208,15 +208,15 @@ pure @safe:
{
assert( contains( dst[0 .. len], val ) );
debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr );
-
size_t v = &val[0] - &dst[0];
+ assert( len >= val.length && len <= dst.length );
+ len -= val.length;
for (size_t p = v; p < len; p++)
dst[p] = dst[p + val.length];
- len -= val.length;
}
}
- char[] append( const(char)[] val )
+ char[] append( const(char)[] val ) return scope
{
pragma(inline, false); // tame dmd inliner
@@ -227,8 +227,7 @@ pure @safe:
assert( !contains( dst[0 .. len], val ) );
debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr );
- if ( &dst[len] == &val[0] &&
- dst.length - len >= val.length )
+ if ( dst.length - len >= val.length && &dst[len] == &val[0] )
{
// data is already in place
auto t = dst[len .. len + val.length];
@@ -254,13 +253,13 @@ pure @safe:
put(", ");
}
- char[] put(char c)
+ char[] put(char c) return scope
{
char[1] val = c;
return put(val[]);
}
- char[] put( const(char)[] val )
+ char[] put( scope const(char)[] val ) return scope
{
pragma(inline, false); // tame dmd inliner
@@ -278,9 +277,9 @@ pure @safe:
{
import core.internal.string;
- UnsignedStringBuf buf;
+ UnsignedStringBuf buf = void;
- auto s = unsignedToTempString(val, buf, 16);
+ auto s = unsignedToTempString!16(val, buf);
int slen = cast(int)s.length;
if (slen < width)
{
@@ -301,7 +300,7 @@ pure @safe:
}
- void silent( lazy void dg )
+ void silent( void delegate() pure @safe dg )
{
debug(trace) printf( "silent+\n" );
debug(trace) scope(success) printf( "silent-\n" );
@@ -431,7 +430,7 @@ pure @safe:
Digit
Digit Number
*/
- const(char)[] sliceNumber()
+ const(char)[] sliceNumber() return scope
{
debug(trace) printf( "sliceNumber+\n" );
debug(trace) scope(success) printf( "sliceNumber-\n" );
@@ -449,7 +448,7 @@ pure @safe:
}
- size_t decodeNumber()
+ size_t decodeNumber() scope
{
debug(trace) printf( "decodeNumber+\n" );
debug(trace) scope(success) printf( "decodeNumber-\n" );
@@ -458,7 +457,7 @@ pure @safe:
}
- size_t decodeNumber( const(char)[] num )
+ size_t decodeNumber( scope const(char)[] num ) scope
{
debug(trace) printf( "decodeNumber+\n" );
debug(trace) scope(success) printf( "decodeNumber-\n" );
@@ -479,7 +478,7 @@ pure @safe:
}
- void parseReal()
+ void parseReal() scope
{
debug(trace) printf( "parseReal+\n" );
debug(trace) scope(success) printf( "parseReal-\n" );
@@ -570,7 +569,7 @@ pure @safe:
Namechar
Namechar Namechars
*/
- void parseLName()
+ void parseLName() scope
{
debug(trace) printf( "parseLName+\n" );
debug(trace) scope(success) printf( "parseLName-\n" );
@@ -788,7 +787,7 @@ pure @safe:
TypeTuple:
B Number Arguments
*/
- char[] parseType( char[] name = null )
+ char[] parseType( char[] name = null ) return scope
{
static immutable string[23] primitives = [
"char", // a
@@ -924,7 +923,6 @@ pure @safe:
return dst[beg .. len];
case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction
return parseTypeFunction( name );
- case 'I': // TypeIdent (I LName)
case 'C': // TypeClass (C LName)
case 'S': // TypeStruct (S LName)
case 'E': // TypeEnum (E LName)
@@ -1186,13 +1184,17 @@ pure @safe:
popFront();
put( "scope " );
continue;
+ case 'm': // FuncAttrLive
+ popFront();
+ put( "@live " );
+ continue;
default:
error();
}
}
}
- void parseFuncArguments()
+ void parseFuncArguments() scope
{
// Arguments
for ( size_t n = 0; true; n++ )
@@ -1233,9 +1235,11 @@ pure @safe:
}
switch ( front )
{
- case 'J': // out (J Type)
+ case 'I': // in (I Type)
popFront();
- put( "out " );
+ put("in ");
+ if (front == 'K')
+ goto case;
parseType();
continue;
case 'K': // ref (K Type)
@@ -1243,6 +1247,11 @@ pure @safe:
put( "ref " );
parseType();
continue;
+ case 'J': // out (J Type)
+ popFront();
+ put( "out " );
+ parseType();
+ continue;
case 'L': // lazy (L Type)
popFront();
put( "lazy " );
@@ -1260,7 +1269,7 @@ pure @safe:
TypeFunction:
CallConvention FuncAttrs Arguments ArgClose Type
*/
- char[] parseTypeFunction( char[] name = null, IsDelegate isdg = IsDelegate.no )
+ char[] parseTypeFunction( char[] name = null, IsDelegate isdg = IsDelegate.no ) return
{
debug(trace) printf( "parseTypeFunction+\n" );
debug(trace) scope(success) printf( "parseTypeFunction-\n" );
@@ -1349,7 +1358,7 @@ pure @safe:
E
F
*/
- void parseValue( char[] name = null, char type = '\0' )
+ void parseValue(scope char[] name = null, char type = '\0' ) scope
{
debug(trace) printf( "parseValue+\n" );
debug(trace) scope(success) printf( "parseValue-\n" );
@@ -1464,13 +1473,19 @@ pure @safe:
}
put( ')' );
return;
+ case 'f':
+ // f MangledName
+ // A function literal symbol
+ popFront();
+ parseMangledName(false, 1);
+ return;
default:
error();
}
}
- void parseIntegerValue( char[] name = null, char type = '\0' )
+ void parseIntegerValue( scope char[] name = null, char type = '\0' ) scope
{
debug(trace) printf( "parseIntegerValue+\n" );
debug(trace) scope(success) printf( "parseIntegerValue-\n" );
@@ -1580,7 +1595,7 @@ pure @safe:
S Number_opt QualifiedName
X ExternallyMangledName
*/
- void parseTemplateArgs()
+ void parseTemplateArgs() scope
{
debug(trace) printf( "parseTemplateArgs+\n" );
debug(trace) scope(success) printf( "parseTemplateArgs-\n" );
@@ -1608,7 +1623,7 @@ pure @safe:
char t = front; // peek at type for parseValue
if ( t == 'Q' )
t = peekBackref();
- char[] name; silent( name = parseType() );
+ char[] name; silent( delegate void() { name = parseType(); } );
parseValue( name, t );
continue;
case 'S':
@@ -1714,7 +1729,7 @@ pure @safe:
TemplateInstanceName:
Number __T LName TemplateArgs Z
*/
- void parseTemplateInstanceName(bool hasNumber)
+ void parseTemplateInstanceName(bool hasNumber) scope
{
debug(trace) printf( "parseTemplateInstanceName+\n" );
debug(trace) scope(success) printf( "parseTemplateInstanceName-\n" );
@@ -1739,7 +1754,7 @@ pure @safe:
}
- bool mayBeTemplateInstanceName()
+ bool mayBeTemplateInstanceName() scope
{
debug(trace) printf( "mayBeTemplateInstanceName+\n" );
debug(trace) scope(success) printf( "mayBeTemplateInstanceName-\n" );
@@ -1759,7 +1774,7 @@ pure @safe:
LName
TemplateInstanceName
*/
- void parseSymbolName()
+ void parseSymbolName() scope
{
debug(trace) printf( "parseSymbolName+\n" );
debug(trace) scope(success) printf( "parseSymbolName-\n" );
@@ -1801,7 +1816,7 @@ pure @safe:
// parse optional function arguments as part of a symbol name, i.e without return type
// if keepAttr, the calling convention and function attributes are not discarded, but returned
- char[] parseFunctionTypeNoReturn( bool keepAttr = false )
+ char[] parseFunctionTypeNoReturn( bool keepAttr = false ) return scope
{
// try to demangle a function, in case we are pointing to some function local
auto prevpos = pos;
@@ -1852,7 +1867,7 @@ pure @safe:
SymbolName
SymbolName QualifiedName
*/
- char[] parseQualifiedName()
+ char[] parseQualifiedName() return scope
{
debug(trace) printf( "parseQualifiedName+\n" );
debug(trace) scope(success) printf( "parseQualifiedName-\n" );
@@ -1876,7 +1891,7 @@ pure @safe:
_D QualifiedName Type
_D QualifiedName M Type
*/
- void parseMangledName( bool displayType, size_t n = 0 )
+ void parseMangledName( bool displayType, size_t n = 0 ) scope
{
debug(trace) printf( "parseMangledName+\n" );
debug(trace) scope(success) printf( "parseMangledName-\n" );
@@ -1951,7 +1966,16 @@ pure @safe:
parseMangledName( AddType.yes == addType );
}
- char[] doDemangle(alias FUNC)()
+ char[] copyInput() return scope
+ {
+ if (dst.length < buf.length)
+ dst.length = buf.length;
+ char[] r = dst[0 .. buf.length];
+ r[] = buf[];
+ return r;
+ }
+
+ char[] doDemangle(alias FUNC)() return scope
{
while ( true )
{
@@ -1979,10 +2003,7 @@ pure @safe:
auto msg = e.toString();
printf( "error: %.*s\n", cast(int) msg.length, msg.ptr );
}
- if ( dst.length < buf.length )
- dst.length = buf.length;
- dst[0 .. buf.length] = buf[];
- return dst[0 .. buf.length];
+ return copyInput();
}
catch ( Exception e )
{
@@ -2015,10 +2036,13 @@ pure @safe:
* The demangled name or the original string if the name is not a mangled D
* name.
*/
-char[] demangle( const(char)[] buf, char[] dst = null ) nothrow pure @safe
+char[] demangle(return scope const(char)[] buf, return scope char[] dst = null ) nothrow pure @safe
{
- //return Demangle(buf, dst)();
auto d = Demangle!()(buf, dst);
+ // fast path (avoiding throwing & catching exception) for obvious
+ // non-D mangled names
+ if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D"))
+ return d.copyInput();
return d.demangleName();
}
@@ -2051,7 +2075,7 @@ char[] demangleType( const(char)[] buf, char[] dst = null ) nothrow pure @safe
* Returns:
* The mangled name with deduplicated identifiers
*/
-char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
+char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe
{
static struct PrependHooks
{
@@ -2067,7 +2091,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
Replacement [] replacements;
pure @safe:
- size_t positionInResult(size_t pos)
+ size_t positionInResult(size_t pos) scope
{
foreach_reverse (r; replacements)
if (pos >= r.pos)
@@ -2077,7 +2101,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
alias Remangle = Demangle!(PrependHooks);
- void flushPosition(ref Remangle d)
+ void flushPosition(ref Remangle d) scope
{
if (lastpos < d.pos)
{
@@ -2096,7 +2120,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
}
}
- bool parseLName(ref Remangle d)
+ bool parseLName(scope ref Remangle d) scope
{
flushPosition(d);
@@ -2127,7 +2151,8 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
npos = positionInResult(*pid);
}
encodeBackref(reslen - npos);
- replacements ~= Replacement(d.pos, result.length);
+ const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
+ replacements ~= Replacement(pos, result.length);
}
else
{
@@ -2141,7 +2166,8 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
size_t npos = positionInResult(*pid);
result.length = reslen;
encodeBackref(reslen - npos);
- replacements ~= Replacement(d.pos, result.length);
+ const pos = d.pos; // work around issues.dlang.org/show_bug.cgi?id=20675
+ replacements ~= Replacement(pos, result.length);
}
else
{
@@ -2153,7 +2179,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
return true;
}
- char[] parseType( ref Remangle d, char[] name = null )
+ char[] parseType( ref Remangle d, char[] name = null ) return scope
{
if (d.front != 'Q')
return null;
@@ -2174,7 +2200,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
return result[reslen .. $]; // anything but null
}
- void encodeBackref(size_t relpos)
+ void encodeBackref(size_t relpos) scope
{
result ~= 'Q';
enum base = 26;
@@ -2221,7 +2247,7 @@ char[] reencodeMangled(const(char)[] mangled) nothrow pure @safe
* The mangled name for a symbols of type T and the given fully
* qualified name.
*/
-char[] mangle(T)(const(char)[] fqn, char[] dst = null) @safe pure nothrow
+char[] mangle(T)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow
{
import core.internal.string : numDigits, unsignedToTempString;
@@ -2232,19 +2258,19 @@ char[] mangle(T)(const(char)[] fqn, char[] dst = null) @safe pure nothrow
@property bool empty() const { return !s.length; }
- @property const(char)[] front() const
+ @property const(char)[] front() const return
{
immutable i = indexOfDot();
return i == -1 ? s[0 .. $] : s[0 .. i];
}
- void popFront()
+ void popFront() scope
{
immutable i = indexOfDot();
s = i == -1 ? s[$ .. $] : s[i+1 .. $];
}
- private ptrdiff_t indexOfDot() const
+ private ptrdiff_t indexOfDot() const scope
{
foreach (i, c; s) if (c == '.') return i;
return -1;
@@ -2311,7 +2337,7 @@ char[] mangle(T)(const(char)[] fqn, char[] dst = null) @safe pure nothrow
* The mangled name for a function with function pointer type T and
* the given fully qualified name.
*/
-char[] mangleFunc(T:FT*, FT)(const(char)[] fqn, char[] dst = null) @safe pure nothrow if (is(FT == function))
+char[] mangleFunc(T:FT*, FT)(return scope const(char)[] fqn, return scope char[] dst = null) @safe pure nothrow if (is(FT == function))
{
static if (isExternD!FT)
{
@@ -2335,7 +2361,6 @@ char[] mangleFunc(T:FT*, FT)(const(char)[] fqn, char[] dst = null) @safe pure no
private enum hasTypeBackRef = (int function(void**,void**)).mangleof[$-4 .. $] == "QdZi";
-///
@safe pure nothrow unittest
{
assert(mangleFunc!(int function(int))("a.b") == "_D1a1bFiZi");
@@ -2412,13 +2437,15 @@ else version (Darwin)
else
enum string cPrefix = "";
-version (unittest)
+@safe pure nothrow unittest
{
immutable string[2][] table =
[
["printf", "printf"],
["_foo", "_foo"],
["_D88", "_D88"],
+ ["_D3fooQeFIAyaZv", "void foo.foo(in immutable(char)[])" ],
+ ["_D3barQeFIKAyaZv", "void bar.bar(in ref immutable(char)[])" ],
["_D4test3fooAa", "char[] test.foo"],
["_D8demangle8demangleFAaZAa", "char[] demangle.demangle(char[])"],
["_D6object6Object8opEqualsFC6ObjectZi", "int object.Object.opEquals(Object)"],
@@ -2470,7 +2497,7 @@ version (unittest)
["_D3foo7__arrayZ", "foo.__array"],
["_D8link657428__T3fooVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
["_D8link657429__T3fooHVE8link65746Methodi0Z3fooFZi", "int link6574.foo!(0).foo()"],
- ["_D4test22__T4funcVAyaa3_610a62Z4funcFNaNbNiNfZAya", `pure nothrow @nogc @safe immutable(char)[] test.func!("a\x0ab").func()`],
+ ["_D4test22__T4funcVAyaa3_610a62Z4funcFNaNbNiNmNfZAya", `pure nothrow @nogc @live @safe immutable(char)[] test.func!("a\x0ab").func()`],
["_D3foo3barFzkZzi", "cent foo.bar(ucent)"],
["_D5bug145Class3fooMFNlZPv", "scope void* bug14.Class.foo()"],
["_D5bug145Class3barMFNjZPv", "return void* bug14.Class.bar()"],
@@ -2482,6 +2509,8 @@ version (unittest)
"pure @safe int std.format.getNth!(\"integer width\", std.traits.isIntegral, int, uint, uint).getNth(uint, uint, uint)"],
["_D3std11parallelism42__T16RoundRobinBufferTDFKAaZvTDxFNaNdNeZbZ16RoundRobinBuffer5primeMFZv",
"void std.parallelism.RoundRobinBuffer!(void delegate(ref char[]), bool delegate() pure @property @trusted const).RoundRobinBuffer.prime()"],
+ ["_D6mangle__T8fun21753VSQv6S21753S1f_DQBj10__lambda71MFNaNbNiNfZvZQCbQp",
+ "void function() pure nothrow @nogc @safe mangle.fun21753!(mangle.S21753(mangle.__lambda71())).fun21753"],
// Lname '0'
["_D3std9algorithm9iteration__T9MapResultSQBmQBlQBe005stripTAAyaZQBi7opSliceMFNaNbNiNfmmZSQDiQDhQDa__TQCtSQDyQDxQDq00QCmTQCjZQDq",
"pure nothrow @nogc @safe std.algorithm.iteration.MapResult!(std.algorithm.iteration.__anonymous.strip, "
@@ -2541,23 +2570,56 @@ version (unittest)
else
alias staticIota = Seq!(staticIota!(x - 1), x - 1);
}
-}
-@safe pure nothrow unittest
-{
foreach ( i, name; table )
{
auto r = demangle( name[0] );
assert( r == name[1],
- "demangled \"" ~ name[0] ~ "\" as \"" ~ r ~ "\" but expected \"" ~ name[1] ~ "\"");
+ "demangled `" ~ name[0] ~ "` as `" ~ r ~ "` but expected `" ~ name[1] ~ "`");
}
foreach ( i; staticIota!(table.length) )
{
enum r = demangle( table[i][0] );
static assert( r == table[i][1],
- "demangled \"" ~ table[i][0] ~ "\" as \"" ~ r ~ "\" but expected \"" ~ table[i][1] ~ "\"");
+ "demangled `" ~ table[i][0] ~ "` as `" ~ r ~ "` but expected `" ~ table[i][1] ~ "`");
+ }
+
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=18531
+ auto symbol = `_D3std3uni__T6toCaseS_DQvQt12toLowerIndexFNaNbNiNewZtVii1043S_DQCjQCi10toLowerTabFNaNbNiNemZwSQDo5ascii7toLowerTAyaZQDzFNaNeQmZ14__foreachbody2MFNaNeKmKwZ14__foreachbody3MFNaNeKwZi`;
+ auto demangled = `pure @trusted int std.uni.toCase!(std.uni.toLowerIndex(dchar), 1043, std.uni.toLowerTab(ulong), std.ascii.toLower, immutable(char)[]).toCase(immutable(char)[]).__foreachbody2(ref ulong, ref dchar).__foreachbody3(ref dchar)`;
+ auto dst = new char[200];
+ auto ret = demangle( symbol, dst);
+ assert( ret == demangled );
}
}
+unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=18300
+ string s = demangle.mangleof;
+ foreach (i; 1..77)
+ {
+ char[] buf = new char[i];
+ auto ds = demangle(s, buf);
+ assert(ds == "pure nothrow @safe char[] core.demangle.demangle(scope return const(char)[], scope return char[])" ||
+ ds == "pure nothrow @safe char[] core.demangle.demangle(return scope const(char)[], return scope char[])");
+ }
+}
+
+unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=18300
+ string s = "_D1";
+ string expected = "int ";
+ foreach (_; 0..10_000)
+ {
+ s ~= "a1";
+ expected ~= "a.";
+ }
+ s ~= "FiZi";
+ expected ~= "F";
+ assert(s.demangle == expected);
+}
/*
*
diff --git a/libphobos/libdruntime/core/exception.d b/libphobos/libdruntime/core/exception.d
index f21c43eb7a4..fe298d4a09f 100644
--- a/libphobos/libdruntime/core/exception.d
+++ b/libphobos/libdruntime/core/exception.d
@@ -1,29 +1,61 @@
/**
- * The exception module defines all system-level exceptions and provides a
- * mechanism to alter system-level error handling.
- *
- * Copyright: Copyright Sean Kelly 2005 - 2013.
- * License: Distributed under the
- * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- * (See accompanying file LICENSE)
- * Authors: Sean Kelly and Jonathan M Davis
- * Source: $(DRUNTIMESRC core/_exception.d)
- */
+ The exception module defines all system-level exceptions and provides a
+ mechanism to alter system-level error handling.
-/* NOTE: This file has been patched from the original DMD distribution to
- * work with the GDC compiler.
+ Copyright: Copyright Sean Kelly 2005 - 2013.
+ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ Authors: Sean Kelly and $(HTTP jmdavisprog.com, Jonathan M Davis)
+ Source: $(DRUNTIMESRC core/_exception.d)
*/
module core.exception;
+// Compiler lowers final switch default case to this (which is a runtime error)
+void __switch_errorT()(string file = __FILE__, size_t line = __LINE__) @trusted
+{
+ // Consider making this a compile time check.
+ version (D_Exceptions)
+ throw staticError!SwitchError(file, line, null);
+ else
+ assert(0, "No appropriate switch clause found");
+}
+
+version (D_BetterC)
+{
+ // When compiling with -betterC we use template functions so if they are
+ // used the bodies are copied into the user's program so there is no need
+ // for the D runtime during linking.
+
+ // In the future we might want to convert all functions in this module to
+ // templates even for ordinary builds instead of providing them as an
+ // extern(C) library.
+
+ void onOutOfMemoryError()(void* pretend_sideffect = null) @nogc nothrow pure @trusted
+ {
+ assert(0, "Memory allocation failed");
+ }
+ alias onOutOfMemoryErrorNoGC = onOutOfMemoryError;
+
+ void onInvalidMemoryOperationError()(void* pretend_sideffect = null) @nogc nothrow pure @trusted
+ {
+ assert(0, "Invalid memory operation");
+ }
+}
+else:
+
/**
* Thrown on a range error.
*/
class RangeError : Error
{
- @safe pure nothrow this( string file = __FILE__, size_t line = __LINE__, Throwable next = null )
+ this( string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @nogc nothrow pure @safe
{
super( "Range violation", file, line, next );
}
+
+ protected this( string msg, string file, size_t line, Throwable next = null ) @nogc nothrow pure @safe
+ {
+ super( msg, file, line, next );
+ }
}
unittest
@@ -45,6 +77,142 @@ unittest
}
}
+/**
+ * Thrown when an out of bounds array index is accessed.
+ */
+class ArrayIndexError : RangeError
+{
+ /// Index into array
+ const size_t index;
+ /// Length of indexed array
+ const size_t length;
+
+ // Buffer to avoid GC allocations
+ private immutable char[100] msgBuf = '\0';
+
+ this(size_t index, size_t length, string file = __FILE__,
+ size_t line = __LINE__, Throwable next = null) @nogc nothrow pure @safe
+ {
+ this.index = index;
+ this.length = length;
+
+ // Constructing the message is a bit clumsy:
+ // It's essentially `printf("index [%zu] exceeds array of length [%zu]", index, length)`,
+ // but even `snprintf` isn't `pure`.
+ // Also string concatenation isn't `@nogc`, and casting to/from immutable isn't `@safe`
+ import core.internal.string : unsignedToTempString;
+ char[msgBuf.length] buf = void;
+ char[20] tmpBuf = void;
+ char[] sink = buf[];
+ sink.rangeMsgPut("index [");
+ sink.rangeMsgPut(unsignedToTempString!10(index, tmpBuf));
+ sink.rangeMsgPut("]");
+ sink.rangeMsgPut(" exceeds array of length ");
+ sink.rangeMsgPut(unsignedToTempString!10(length, tmpBuf));
+ this.msgBuf = buf;
+ super(msgBuf[0..$-sink.length], file, line, next);
+ }
+}
+
+@safe pure unittest
+{
+ assert(new ArrayIndexError(900, 700).msg == "index [900] exceeds array of length 700");
+ // Ensure msg buffer doesn't overflow on large numbers
+ assert(new ArrayIndexError(size_t.max, size_t.max-1).msg);
+}
+
+unittest
+{
+ try
+ {
+ _d_arraybounds_indexp("test", 400, 9, 3);
+ assert(0, "no ArrayIndexError thrown");
+ }
+ catch (ArrayIndexError re)
+ {
+ assert(re.file == "test");
+ assert(re.line == 400);
+ assert(re.index == 9);
+ assert(re.length == 3);
+ }
+}
+
+/**
+ * Thrown when an out of bounds array slice is created
+ */
+class ArraySliceError : RangeError
+{
+ /// Lower/upper bound passed to slice: `array[lower .. upper]`
+ const size_t lower, upper;
+ /// Length of sliced array
+ const size_t length;
+
+ private immutable char[120] msgBuf = '\0';
+
+ this(size_t lower, size_t upper, size_t length, string file = __FILE__,
+ size_t line = __LINE__, Throwable next = null) @nogc nothrow pure @safe
+ {
+ this.lower = lower;
+ this.upper = upper;
+ this.length = length;
+
+ // Constructing the message is a bit clumsy for the same reasons as ArrayIndexError
+ import core.internal.string : unsignedToTempString;
+ char[msgBuf.length] buf = void;
+ char[20] tmpBuf = void;
+ char[] sink = buf;
+ sink.rangeMsgPut("slice [");
+ sink.rangeMsgPut(unsignedToTempString!10(lower, tmpBuf));
+ sink.rangeMsgPut(" .. ");
+ sink.rangeMsgPut(unsignedToTempString!10(upper, tmpBuf));
+ sink.rangeMsgPut("] ");
+ if (lower > upper)
+ {
+ sink.rangeMsgPut("has a larger lower index than upper index");
+ }
+ else
+ {
+ sink.rangeMsgPut("extends past source array of length ");
+ sink.rangeMsgPut(unsignedToTempString!10(length, tmpBuf));
+ }
+
+ this.msgBuf = buf;
+ super(msgBuf[0..$-sink.length], file, line, next);
+ }
+}
+
+@safe pure unittest
+{
+ assert(new ArraySliceError(40, 80, 20).msg == "slice [40 .. 80] extends past source array of length 20");
+ assert(new ArraySliceError(90, 70, 20).msg == "slice [90 .. 70] has a larger lower index than upper index");
+ // Ensure msg buffer doesn't overflow on large numbers
+ assert(new ArraySliceError(size_t.max, size_t.max, size_t.max-1).msg);
+}
+
+unittest
+{
+ try
+ {
+ _d_arraybounds_slicep("test", 400, 1, 7, 3);
+ assert(0, "no ArraySliceError thrown");
+ }
+ catch (ArraySliceError re)
+ {
+ assert(re.file == "test");
+ assert(re.line == 400);
+ assert(re.lower == 1);
+ assert(re.upper == 7);
+ assert(re.length == 3);
+ }
+}
+
+/// Mini `std.range.primitives: put` for constructor of ArraySliceError / ArrayIndexError
+private void rangeMsgPut(ref char[] r, scope const(char)[] e) @nogc nothrow pure @safe
+{
+ assert(r.length >= e.length); // don't throw ArraySliceError inside ArrayIndexError ctor
+ r[0 .. e.length] = e[];
+ r = r[e.length .. $];
+}
/**
* Thrown on an assert error.
@@ -187,32 +355,6 @@ unittest
}
/**
- * Thrown on hidden function error.
- * $(RED Deprecated.
- * This feature is not longer part of the language.)
- */
-deprecated class HiddenFuncError : Error
-{
- @safe pure nothrow this( ClassInfo ci )
- {
- super( "Hidden method called for " ~ ci.name );
- }
-}
-
-deprecated unittest
-{
- ClassInfo info = new ClassInfo;
- info.name = "testInfo";
-
- {
- auto hfe = new HiddenFuncError(info);
- assert(hfe.next is null);
- assert(hfe.msg == "Hidden method called for testInfo");
- }
-}
-
-
-/**
* Thrown on an out of memory error.
*/
class OutOfMemoryError : Error
@@ -312,11 +454,23 @@ unittest
/**
+* Thrown on a configuration error.
+*/
+class ForkError : Error
+{
+ this( string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @nogc nothrow pure @safe
+ {
+ super( "fork() failed", file, line, next );
+ }
+}
+
+
+/**
* Thrown on a switch error.
*/
class SwitchError : Error
{
- @safe pure nothrow this( string file = __FILE__, size_t line = __LINE__, Throwable next = null )
+ @safe pure nothrow @nogc this( string file = __FILE__, size_t line = __LINE__, Throwable next = null )
{
super( "No appropriate switch clause found", file, line, next );
}
@@ -349,7 +503,7 @@ class UnicodeException : Exception
{
size_t idx;
- this( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure nothrow
+ this( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__, Throwable next = null ) @safe pure nothrow @nogc
{
super( msg, file, line, next );
this.idx = idx;
@@ -407,19 +561,6 @@ alias AssertHandler = void function(string file, size_t line, string msg) nothro
_assertHandler = handler;
}
-/**
- * Overrides the default assert hander with a user-supplied version.
- * $(RED Deprecated.
- * Please use $(LREF assertHandler) instead.)
- *
- * Params:
- * h = The new assert handler. Set to null to use the default handler.
- */
-deprecated void setAssertHandler( AssertHandler h ) @trusted nothrow @nogc
-{
- assertHandler = h;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Overridable Callbacks
@@ -438,7 +579,7 @@ deprecated void setAssertHandler( AssertHandler h ) @trusted nothrow @nogc
extern (C) void onAssertError( string file = __FILE__, size_t line = __LINE__ ) nothrow
{
if ( _assertHandler is null )
- throw new AssertError( file, line );
+ throw staticError!AssertError(file, line);
_assertHandler( file, line, null);
}
@@ -456,7 +597,7 @@ extern (C) void onAssertError( string file = __FILE__, size_t line = __LINE__ )
extern (C) void onAssertErrorMsg( string file, size_t line, string msg ) nothrow
{
if ( _assertHandler is null )
- throw new AssertError( msg, file, line );
+ throw staticError!AssertError(msg, file, line);
_assertHandler( file, line, msg );
}
@@ -482,7 +623,7 @@ extern (C) void onUnittestErrorMsg( string file, size_t line, string msg ) nothr
///////////////////////////////////////////////////////////////////////////////
/**
- * A callback for array bounds errors in D. A $(LREF RangeError) will be thrown.
+ * A callback for general array bounds errors in D. A $(LREF RangeError) will be thrown.
*
* Params:
* file = The name of the file that signaled this error.
@@ -491,11 +632,47 @@ extern (C) void onUnittestErrorMsg( string file, size_t line, string msg ) nothr
* Throws:
* $(LREF RangeError).
*/
-extern (C) void onRangeError( string file = __FILE__, size_t line = __LINE__ ) @safe pure nothrow
+extern (C) void onRangeError( string file = __FILE__, size_t line = __LINE__ ) @trusted pure nothrow @nogc
+{
+ throw staticError!RangeError(file, line, null);
+}
+
+/**
+ * A callback for array slice out of bounds errors in D.
+ *
+ * Params:
+ * lower = the lower bound of the index passed of a slice
+ * upper = the upper bound of the index passed of a slice or the index if not a slice
+ * length = length of the array
+ * file = The name of the file that signaled this error.
+ * line = The line number on which this error occurred.
+ *
+ * Throws:
+ * $(LREF ArraySliceError).
+ */
+extern (C) void onArraySliceError( size_t lower = 0, size_t upper = 0, size_t length = 0,
+ string file = __FILE__, size_t line = __LINE__ ) @trusted pure nothrow @nogc
{
- throw new RangeError( file, line, null );
+ throw staticError!ArraySliceError(lower, upper, length, file, line, null);
}
+/**
+ * A callback for array index out of bounds errors in D.
+ *
+ * Params:
+ * index = index in the array
+ * length = length of the array
+ * file = The name of the file that signaled this error.
+ * line = The line number on which this error occurred.
+ *
+ * Throws:
+ * $(LREF ArrayIndexError).
+ */
+extern (C) void onArrayIndexError( size_t index = 0, size_t length = 0,
+ string file = __FILE__, size_t line = __LINE__ ) @trusted pure nothrow @nogc
+{
+ throw staticError!ArrayIndexError(index, length, file, line, null);
+}
/**
* A callback for finalize errors in D. A $(LREF FinalizeError) will be thrown.
@@ -516,22 +693,6 @@ extern (C) void onFinalizeError( TypeInfo info, Throwable e, string file = __FIL
throw staticError!FinalizeError(info, e, file, line);
}
-
-/**
- * A callback for hidden function errors in D. A $(LREF HiddenFuncError) will be
- * thrown.
- * $(RED Deprecated.
- * This feature is not longer part of the language.)
- *
- * Throws:
- * $(LREF HiddenFuncError).
- */
-deprecated extern (C) void onHiddenFuncError( Object o ) @safe pure nothrow
-{
- throw new HiddenFuncError( typeid(o) );
-}
-
-
/**
* A callback for out of memory errors in D. An $(LREF OutOfMemoryError) will be
* thrown.
@@ -569,21 +730,20 @@ extern (C) void onInvalidMemoryOperationError(void* pretend_sideffect = null) @t
/**
- * A callback for switch errors in D. A $(LREF SwitchError) will be thrown.
+ * A callback for errors in the case of a failed fork in D. A $(LREF ForkError) will be thrown.
*
* Params:
* file = The name of the file that signaled this error.
* line = The line number on which this error occurred.
*
* Throws:
- * $(LREF SwitchError).
+ * $(LREF ConfigurationError).
*/
-extern (C) void onSwitchError( string file = __FILE__, size_t line = __LINE__ ) @safe pure nothrow
+extern (C) void onForkError( string file = __FILE__, size_t line = __LINE__ ) @trusted pure nothrow @nogc
{
- throw new SwitchError( file, line, null );
+ throw staticError!ForkError( file, line, null );
}
-
/**
* A callback for unicode errors in D. A $(LREF UnicodeException) will be thrown.
*
@@ -611,7 +771,6 @@ extern (C) void onAssertErrorMsg(string file, size_t line, string msg);
extern (C) void onUnittestErrorMsg(string file, size_t line, string msg);
extern (C) void onRangeError(string file, size_t line);
extern (C) void onHiddenFuncError(Object o);
-extern (C) void onSwitchError(string file, size_t line);
+/
/***********************************
@@ -621,8 +780,6 @@ extern (C) void onSwitchError(string file, size_t line);
extern (C)
{
- // Use ModuleInfo to get file name for "m" versions
-
/* One of these three is called upon an assert() fail.
*/
void _d_assertp(immutable(char)* file, uint line)
@@ -659,34 +816,36 @@ extern (C)
_d_unittest_msg("unittest failure", file, line);
}
- /* Called when an array index is out of bounds
- */
+ /// Called when an invalid array index/slice or associative array key is accessed
void _d_arrayboundsp(immutable(char*) file, uint line)
{
import core.stdc.string : strlen;
onRangeError(file[0 .. strlen(file)], line);
}
+ /// ditto
void _d_arraybounds(string file, uint line)
{
onRangeError(file, line);
}
- /* Called when a switch statement has no DefaultStatement, yet none of the cases match
- */
- void _d_switch_errorm(immutable(ModuleInfo)* m, uint line)
+ /// Called when an out of range slice of an array is created
+ void _d_arraybounds_slicep(immutable(char*) file, uint line, size_t lower, size_t upper, size_t length)
{
- onSwitchError(m.name, line);
+ import core.stdc.string : strlen;
+ onArraySliceError(lower, upper, length, file[0 .. strlen(file)], line);
}
- void _d_switch_error(string file, uint line)
+ /// Called when an out of range array index is accessed
+ void _d_arraybounds_indexp(immutable(char*) file, uint line, size_t index, size_t length)
{
- onSwitchError(file, line);
+ import core.stdc.string : strlen;
+ onArrayIndexError(index, length, file[0 .. strlen(file)], line);
}
}
// TLS storage shared for all errors, chaining might create circular reference
-private void[128] _store;
+private align(2 * size_t.sizeof) void[256] _store;
// only Errors for now as those are rarely chained
private T staticError(T, Args...)(auto ref Args args)
@@ -698,11 +857,11 @@ private T staticError(T, Args...)(auto ref Args args)
static assert(__traits(classInstanceSize, T) <= _store.length,
T.stringof ~ " is too large for staticError()");
- _store[0 .. __traits(classInstanceSize, T)] = typeid(T).initializer[];
return cast(T) _store.ptr;
}
auto res = (cast(T function() @trusted pure nothrow @nogc) &get)();
- res.__ctor(args);
+ import core.lifetime : emplace;
+ emplace(res, args);
return res;
}
diff --git a/libphobos/libdruntime/core/gc/config.d b/libphobos/libdruntime/core/gc/config.d
new file mode 100644
index 00000000000..258183fd505
--- /dev/null
+++ b/libphobos/libdruntime/core/gc/config.d
@@ -0,0 +1,129 @@
+/**
+* Contains the garbage collector configuration.
+*
+* Copyright: Copyright Digital Mars 2016
+* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+*/
+
+module core.gc.config;
+
+import core.stdc.stdio;
+import core.internal.parseoptions;
+
+__gshared Config config;
+
+struct Config
+{
+ bool disable; // start disabled
+ bool fork = false; // optional concurrent behaviour
+ ubyte profile; // enable profiling with summary when terminating program
+ string gc = "conservative"; // select gc implementation conservative|precise|manual
+
+ @MemVal size_t initReserve; // initial reserve (bytes)
+ @MemVal size_t minPoolSize = 1 << 20; // initial and minimum pool size (bytes)
+ @MemVal size_t maxPoolSize = 64 << 20; // maximum pool size (bytes)
+ @MemVal size_t incPoolSize = 3 << 20; // pool size increment (bytes)
+ uint parallel = 99; // number of additional threads for marking (limited by cpuid.threadsPerCPU-1)
+ float heapSizeFactor = 2.0; // heap size to used memory ratio
+ string cleanup = "collect"; // select gc cleanup method none|collect|finalize
+
+@nogc nothrow:
+
+ bool initialize()
+ {
+ return initConfigOptions(this, "gcopt");
+ }
+
+ void help() @nogc nothrow
+ {
+ import core.gc.registry : registeredGCFactories;
+
+ printf("GC options are specified as white space separated assignments:
+ disable:0|1 - start disabled (%d)
+ fork:0|1 - set fork behaviour (%d)
+ profile:0|1|2 - enable profiling with summary when terminating program (%d)
+ gc:".ptr, disable, fork, profile);
+ foreach (i, entry; registeredGCFactories)
+ {
+ if (i) printf("|");
+ printf("%.*s", cast(int) entry.name.length, entry.name.ptr);
+ }
+ auto _initReserve = initReserve.bytes2prettyStruct;
+ auto _minPoolSize = minPoolSize.bytes2prettyStruct;
+ auto _maxPoolSize = maxPoolSize.bytes2prettyStruct;
+ auto _incPoolSize = incPoolSize.bytes2prettyStruct;
+ printf(" - select gc implementation (default = conservative)
+
+ initReserve:N - initial memory to reserve in MB (%lld%c)
+ minPoolSize:N - initial and minimum pool size in MB (%lld%c)
+ maxPoolSize:N - maximum pool size in MB (%lld%c)
+ incPoolSize:N - pool size increment MB (%lld%c)
+ parallel:N - number of additional threads for marking (%lld)
+ heapSizeFactor:N - targeted heap size to used memory ratio (%g)
+ cleanup:none|collect|finalize - how to treat live objects when terminating (collect)
+
+ Memory-related values can use B, K, M or G suffixes.
+".ptr,
+ _initReserve.v, _initReserve.u,
+ _minPoolSize.v, _minPoolSize.u,
+ _maxPoolSize.v, _maxPoolSize.u,
+ _incPoolSize.v, _incPoolSize.u,
+ cast(long)parallel, heapSizeFactor);
+ }
+
+ string errorName() @nogc nothrow { return "GC"; }
+}
+
+private struct PrettyBytes
+{
+ long v;
+ char u; /// unit
+}
+
+pure @nogc nothrow:
+
+private PrettyBytes bytes2prettyStruct(size_t val)
+{
+ char c = prettyBytes(val);
+
+ return PrettyBytes(val, c);
+}
+
+private static char prettyBytes(ref size_t val)
+{
+ char sym = 'B';
+
+ if (val == 0)
+ return sym;
+
+ char[3] units = ['K', 'M', 'G'];
+
+ foreach (u; units)
+ if (val % (1 << 10) == 0)
+ {
+ val /= (1 << 10);
+ sym = u;
+ }
+ else if (sym != 'B')
+ break;
+
+ return sym;
+}
+unittest
+{
+ size_t v = 1024;
+ assert(prettyBytes(v) == 'K');
+ assert(v == 1);
+
+ v = 1025;
+ assert(prettyBytes(v) == 'B');
+ assert(v == 1025);
+
+ v = 1024UL * 1024 * 1024 * 3;
+ assert(prettyBytes(v) == 'G');
+ assert(v == 3);
+
+ v = 1024 * 1024 + 1;
+ assert(prettyBytes(v) == 'B');
+ assert(v == 1024 * 1024 + 1);
+}
diff --git a/libphobos/libdruntime/core/gc/gcinterface.d b/libphobos/libdruntime/core/gc/gcinterface.d
new file mode 100644
index 00000000000..e8cdf1109ad
--- /dev/null
+++ b/libphobos/libdruntime/core/gc/gcinterface.d
@@ -0,0 +1,198 @@
+/**
+ * Contains the internal GC interface.
+ *
+ * Copyright: Copyright Digital Mars 2016.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright, Sean Kelly, Jeremy DeHaan
+ */
+
+ /* Copyright Digital Mars 2016.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+module core.gc.gcinterface;
+
+static import core.memory;
+alias BlkAttr = core.memory.GC.BlkAttr;
+alias BlkInfo = core.memory.GC.BlkInfo;
+
+alias RootIterator = int delegate(scope int delegate(ref Root) nothrow dg);
+alias RangeIterator = int delegate(scope int delegate(ref Range) nothrow dg);
+
+
+struct Root
+{
+ void* proot;
+ alias proot this;
+}
+
+struct Range
+{
+ void* pbot;
+ void* ptop;
+ TypeInfo ti; // should be tail const, but doesn't exist for references
+ alias pbot this; // only consider pbot for relative ordering (opCmp)
+ bool opEquals(const scope Range rhs) nothrow const { return pbot == rhs.pbot; }
+}
+
+interface GC
+{
+ /**
+ *
+ */
+ void enable();
+
+ /**
+ *
+ */
+ void disable();
+
+ /**
+ *
+ */
+ void collect() nothrow;
+
+ /**
+ *
+ */
+ void collectNoStack() nothrow;
+
+ /**
+ * minimize free space usage
+ */
+ void minimize() nothrow;
+
+ /**
+ *
+ */
+ uint getAttr(void* p) nothrow;
+
+ /**
+ *
+ */
+ uint setAttr(void* p, uint mask) nothrow;
+
+ /**
+ *
+ */
+ uint clrAttr(void* p, uint mask) nothrow;
+
+ /**
+ *
+ */
+ void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow;
+
+ /*
+ *
+ */
+ BlkInfo qalloc(size_t size, uint bits, const scope TypeInfo ti) nothrow;
+
+ /*
+ *
+ */
+ void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow;
+
+ /*
+ *
+ */
+ void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow;
+
+ /**
+ * Attempt to in-place enlarge the memory block pointed to by p by at least
+ * minsize bytes, up to a maximum of maxsize additional bytes.
+ * This does not attempt to move the memory block (like realloc() does).
+ *
+ * Returns:
+ * 0 if could not extend p,
+ * total size of entire memory block if successful.
+ */
+ size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow;
+
+ /**
+ *
+ */
+ size_t reserve(size_t size) nothrow;
+
+ /**
+ *
+ */
+ void free(void* p) nothrow @nogc;
+
+ /**
+ * Determine the base address of the block containing p. If p is not a gc
+ * allocated pointer, return null.
+ */
+ void* addrOf(void* p) nothrow @nogc;
+
+ /**
+ * Determine the allocated size of pointer p. If p is an interior pointer
+ * or not a gc allocated pointer, return 0.
+ */
+ size_t sizeOf(void* p) nothrow @nogc;
+
+ /**
+ * Determine the base address of the block containing p. If p is not a gc
+ * allocated pointer, return null.
+ */
+ BlkInfo query(void* p) nothrow;
+
+ /**
+ * Retrieve statistics about garbage collection.
+ * Useful for debugging and tuning.
+ */
+ core.memory.GC.Stats stats() nothrow;
+
+ /**
+ * Retrieve profile statistics about garbage collection.
+ * Useful for debugging and tuning.
+ */
+ core.memory.GC.ProfileStats profileStats() nothrow @safe;
+
+ /**
+ * add p to list of roots
+ */
+ void addRoot(void* p) nothrow @nogc;
+
+ /**
+ * remove p from list of roots
+ */
+ void removeRoot(void* p) nothrow @nogc;
+
+ /**
+ *
+ */
+ @property RootIterator rootIter() @nogc;
+
+ /**
+ * add range to scan for roots
+ */
+ void addRange(void* p, size_t sz, const TypeInfo ti) nothrow @nogc;
+
+ /**
+ * remove range
+ */
+ void removeRange(void* p) nothrow @nogc;
+
+ /**
+ *
+ */
+ @property RangeIterator rangeIter() @nogc;
+
+ /**
+ * run finalizers
+ */
+ void runFinalizers(const scope void[] segment) nothrow;
+
+ /*
+ *
+ */
+ bool inFinalizer() nothrow @nogc @safe;
+
+ /**
+ * Returns the number of bytes allocated for the current thread
+ * since program start. It is the same as
+ * GC.stats().allocatedInCurrentThread, but faster.
+ */
+ ulong allocatedInCurrentThread() nothrow;
+}
diff --git a/libphobos/libdruntime/core/gc/registry.d b/libphobos/libdruntime/core/gc/registry.d
new file mode 100644
index 00000000000..da2dcff1e7d
--- /dev/null
+++ b/libphobos/libdruntime/core/gc/registry.d
@@ -0,0 +1,87 @@
+/**
+ * Contains a registry for GC factories.
+ *
+ * Copyright: Copyright Digital Mars 2016.
+ * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Martin Nowak
+ */
+module core.gc.registry;
+
+import core.gc.gcinterface : GC;
+
+/*@nogc nothrow:*/
+
+/**
+ * A factory function that instantiates an implementation of the GC interface.
+ * In case the instance was allocated on the C heap, it is supposed to
+ * free itself upon calling it's destructor.
+ *
+ * The factory should print an error and abort the program if it
+ * cannot successfully initialize the GC instance.
+ */
+alias GCFactory = GC function();
+
+/**
+ * Register a GC factory under the given `name`. This function must be called
+ * from a C constructor before druntime is initialized.
+ *
+ * To use the registered GC, it's name must be specified gcopt runtime option,
+ * e.g. by passing $(TT, --DRT-gcopt=gc:my_gc_name) as application argument.
+ *
+ * Params:
+ * name = name of the GC implementation; should be unique
+ * factory = function to instantiate the implementation
+ * Note: The registry does not perform synchronization, as registration is
+ * assumed to be executed serially, as is the case for C constructors.
+ * See_Also:
+ * $(LINK2 https://dlang.org/spec/garbage.html#gc_config, Configuring the Garbage Collector)
+ */
+void registerGCFactory(string name, GCFactory factory) nothrow @nogc
+{
+ import core.stdc.stdlib : realloc;
+
+ auto ptr = cast(Entry*)realloc(entries.ptr, (entries.length + 1) * Entry.sizeof);
+ entries = ptr[0 .. entries.length + 1];
+ entries[$ - 1] = Entry(name, factory);
+}
+
+/**
+ * Called during runtime initialization to initialize a GC instance of given `name`.
+ *
+ * Params:
+ * name = name of the GC to instantiate
+ * Returns:
+ * The created GC instance or `null` if no factory for that name was registered
+ */
+GC createGCInstance(string name)
+{
+ import core.stdc.stdlib : free;
+
+ foreach (entry; entries)
+ {
+ if (entry.name != name)
+ continue;
+ auto instance = entry.factory();
+ // only one GC at a time for now, so free the registry to not leak
+ free(entries.ptr);
+ entries = null;
+ return instance;
+ }
+ return null;
+}
+
+// list of all registerd GCs
+const(Entry[]) registeredGCFactories(scope int dummy=0) nothrow @nogc
+{
+ return entries;
+}
+
+private:
+
+struct Entry
+{
+ string name;
+ GCFactory factory;
+}
+
+__gshared Entry[] entries;
diff --git a/libphobos/libdruntime/core/internal/abort.d b/libphobos/libdruntime/core/internal/abort.d
index 8ee1684d146..6942f7e37d2 100644
--- a/libphobos/libdruntime/core/internal/abort.d
+++ b/libphobos/libdruntime/core/internal/abort.d
@@ -11,7 +11,7 @@ void abort(scope string msg, scope string filename = __FILE__, size_t line = __L
version (Posix)
{
import core.sys.posix.unistd: write;
- static void writeStr(const(char)[][] m...) @nogc nothrow @trusted
+ static void writeStr(scope const(char)[][] m...) @nogc nothrow @trusted
{
foreach (s; m)
write(2, s.ptr, s.length);
@@ -19,12 +19,20 @@ void abort(scope string msg, scope string filename = __FILE__, size_t line = __L
}
else version (Windows)
{
- import core.sys.windows.windows: GetStdHandle, STD_ERROR_HANDLE, WriteFile, INVALID_HANDLE_VALUE;
+ import core.sys.windows.winbase : GetStdHandle, STD_ERROR_HANDLE, WriteFile, INVALID_HANDLE_VALUE;
auto h = (() @trusted => GetStdHandle(STD_ERROR_HANDLE))();
if (h == INVALID_HANDLE_VALUE)
+ {
// attempt best we can to print the message
- assert(0, msg);
- void writeStr(const(char)[][] m...) @nogc nothrow @trusted
+
+ /* Note that msg is scope.
+ * assert() calls _d_assert_msg() calls onAssertErrorMsg() calls _assertHandler() but
+ * msg parameter isn't scope and can escape.
+ * Give up and use our own immutable message instead.
+ */
+ assert(0, "Cannot get stderr handle for message");
+ }
+ void writeStr(scope const(char)[][] m...) @nogc nothrow @trusted
{
foreach (s; m)
{
@@ -37,9 +45,9 @@ void abort(scope string msg, scope string filename = __FILE__, size_t line = __L
static assert(0, "Unsupported OS");
import core.internal.string;
- UnsignedStringBuf strbuff;
+ UnsignedStringBuf strbuff = void;
// write an appropriate message, then abort the program
- writeStr("Aborting from ", filename, "(", line.unsignedToTempString(strbuff, 10), ") ", msg);
+ writeStr("Aborting from ", filename, "(", line.unsignedToTempString(strbuff), ") ", msg);
c_abort();
}
diff --git a/libphobos/libdruntime/core/internal/array/appending.d b/libphobos/libdruntime/core/internal/array/appending.d
new file mode 100644
index 00000000000..1e58ddc9880
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/appending.d
@@ -0,0 +1,222 @@
+/**
+ This module contains support for controlling dynamic arrays' appending
+
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/_internal/_array/_appending.d)
+*/
+module core.internal.array.appending;
+
+/// See $(REF _d_arrayappendcTX, rt,lifetime,_d_arrayappendcTX)
+private extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref return scope byte[] px, size_t n) @trusted pure nothrow;
+
+private enum isCopyingNothrow(T) = __traits(compiles, (ref T rhs) nothrow { T lhs = rhs; });
+
+/// Implementation of `_d_arrayappendcTX` and `_d_arrayappendcTXTrace`
+template _d_arrayappendcTXImpl(Tarr : T[], T)
+{
+ import core.internal.array.utils : _d_HookTraceImpl;
+
+ private enum errorMessage = "Cannot append to array if compiling without support for runtime type information!";
+
+ /**
+ * Extend an array `px` by `n` elements.
+ * Caller must initialize those elements.
+ * Params:
+ * px = the array that will be extended, taken as a reference
+ * n = how many new elements to extend it with
+ * Returns:
+ * The new value of `px`
+ * Bugs:
+ * This function template was ported from a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
+ */
+ static if (isCopyingNothrow!T) // `nothrow` deduction doesn't work, so this is needed
+ ref Tarr _d_arrayappendcTX(return scope ref Tarr px, size_t n) @trusted pure nothrow
+ {
+ pragma(inline, false);
+
+ mixin(_d_arrayappendcTXBody);
+ }
+ else
+ ref Tarr _d_arrayappendcTX(return scope ref Tarr px, size_t n) @trusted pure nothrow
+ {
+ pragma(inline, false);
+
+ mixin(_d_arrayappendcTXBody);
+ }
+
+ private enum _d_arrayappendcTXBody = q{
+ version (D_TypeInfo)
+ {
+ auto ti = typeid(Tarr);
+
+ // _d_arrayappendcTX takes the `px` as a ref byte[], but its length
+ // should still be the original length
+ auto pxx = (cast(byte*)px.ptr)[0 .. px.length];
+ ._d_arrayappendcTX(ti, pxx, n);
+ px = (cast(T*)pxx.ptr)[0 .. pxx.length];
+
+ return px;
+ }
+ else
+ assert(0, "Cannot append arrays if compiling without support for runtime type information!");
+ };
+
+ /**
+ * TraceGC wrapper around $(REF _d_arrayappendcTX, rt,array,appending,_d_arrayappendcTXImpl).
+ * Bugs:
+ * This function template was ported from a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
+ */
+ alias _d_arrayappendcTXTrace = _d_HookTraceImpl!(Tarr, _d_arrayappendcTX, errorMessage);
+}
+
+/// Implementation of `_d_arrayappendT` and `_d_arrayappendTTrace`
+template _d_arrayappendTImpl(Tarr : T[], T)
+{
+ import core.internal.array.utils : _d_HookTraceImpl;
+
+ private enum errorMessage = "Cannot append to array if compiling without support for runtime type information!";
+
+ /**
+ * Append array `y` to array `x`.
+ * Params:
+ * x = what array to append to, taken as a reference
+ * y = what should be appended
+ * Returns:
+ * The new value of `x`
+ * Bugs:
+ * This function template was ported from a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
+ */
+ static if (isCopyingNothrow!T)
+ ref Tarr _d_arrayappendT(return scope ref Tarr x, scope Tarr y) @trusted pure nothrow
+ {
+ pragma(inline, false);
+
+ mixin(_d_arrayappendTBody);
+ }
+ else
+ ref Tarr _d_arrayappendT(return scope ref Tarr x, scope Tarr y) @trusted pure
+ {
+ pragma(inline, false);
+
+ mixin(_d_arrayappendTBody);
+ }
+
+ private enum _d_arrayappendTBody = q{
+ import core.stdc.string : memcpy;
+ import core.internal.traits : hasElaborateCopyConstructor, Unqual;
+ import core.lifetime : copyEmplace;
+
+ auto length = x.length;
+
+ _d_arrayappendcTXImpl!Tarr._d_arrayappendcTX(x, y.length);
+
+ static if (hasElaborateCopyConstructor!T)
+ {
+ foreach (i; 0 .. y.length)
+ copyEmplace(y[i], x[length + i]);
+ }
+ else
+ {
+ // blit all elements at once
+ if (y.length)
+ memcpy(cast(Unqual!T *)&x[length], cast(Unqual!T *)&y[0], y.length * T.sizeof);
+ }
+
+ return x;
+ };
+
+ /**
+ * TraceGC wrapper around $(REF _d_arrayappendT, rt,array,appending,_d_arrayappendTImpl).
+ * Bugs:
+ * This function template was ported from a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
+ */
+ alias _d_arrayappendTTrace = _d_HookTraceImpl!(Tarr, _d_arrayappendT, errorMessage);
+}
+
+@safe unittest
+{
+ double[] arr1;
+ foreach (i; 0 .. 4)
+ _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, [cast(double)i]);
+ assert(arr1 == [0.0, 1.0, 2.0, 3.0]);
+}
+
+@safe unittest
+{
+ int blitted;
+ struct Item
+ {
+ this(this)
+ {
+ blitted++;
+ }
+ }
+
+ Item[] arr1 = [Item(), Item()];
+ Item[] arr2 = [Item(), Item()];
+ Item[] arr1_org = [Item(), Item()];
+ arr1_org ~= arr2;
+ _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2);
+
+ // postblit should have triggered on at least the items in arr2
+ assert(blitted >= arr2.length);
+}
+
+@safe nothrow unittest
+{
+ int blitted;
+ struct Item
+ {
+ this(this) nothrow
+ {
+ blitted++;
+ }
+ }
+
+ Item[][] arr1 = [[Item()]];
+ Item[][] arr2 = [[Item()]];
+
+ _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2);
+
+ // no postblit should have happened because arr{1,2} contain dynamic arrays
+ assert(blitted == 0);
+}
+
+@safe nothrow unittest
+{
+ int copied;
+ struct Item
+ {
+ this(const scope ref Item) nothrow
+ {
+ copied++;
+ }
+ }
+
+ Item[1][] arr1 = [[Item()]];
+ Item[1][] arr2 = [[Item()]];
+
+ _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2);
+ // copy constructor should have been invoked because arr{1,2} contain static arrays
+ assert(copied >= arr2.length);
+}
+
+@safe nothrow unittest
+{
+ string str;
+ _d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "a");
+ _d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "b");
+ _d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "c");
+ assert(str == "abc");
+}
diff --git a/libphobos/libdruntime/core/internal/array/capacity.d b/libphobos/libdruntime/core/internal/array/capacity.d
new file mode 100644
index 00000000000..9440428ebd7
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/capacity.d
@@ -0,0 +1,85 @@
+/**
+ This module contains support for controlling dynamic arrays' capacity and length
+
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/internal/_array/_capacity.d)
+*/
+module core.internal.array.capacity;
+
+// HACK: `nothrow` and `pure` is faked.
+private extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) nothrow pure;
+private extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) nothrow pure;
+
+/*
+ * This template is needed because there need to be a `_d_arraysetlengthTTrace!Tarr` instance for every
+ * `_d_arraysetlengthT!Tarr`. By wrapping both of these functions inside of this template we force the
+ * compiler to create a instance of both function for every type that is used.
+ */
+
+/// Implementation of `_d_arraysetlengthT` and `_d_arraysetlengthTTrace`
+template _d_arraysetlengthTImpl(Tarr : T[], T)
+{
+ import core.internal.array.utils : _d_HookTraceImpl;
+
+ private enum errorMessage = "Cannot resize arrays if compiling without support for runtime type information!";
+
+ /**
+ * Resize dynamic array
+ * Params:
+ * arr = the array that will be resized, taken as a reference
+ * newlength = new length of array
+ * Returns:
+ * The new length of the array
+ * Bugs:
+ * The safety level of this function is faked. It shows itself as `@trusted pure nothrow` to not break existing code.
+ */
+ size_t _d_arraysetlengthT(return scope ref Tarr arr, size_t newlength) @trusted pure nothrow
+ {
+ pragma(inline, false);
+ version (D_TypeInfo)
+ {
+ auto ti = typeid(Tarr);
+
+ static if (__traits(isZeroInit, T))
+ ._d_arraysetlengthT(ti, newlength, cast(void[]*)&arr);
+ else
+ ._d_arraysetlengthiT(ti, newlength, cast(void[]*)&arr);
+
+ return arr.length;
+ }
+ else
+ assert(0, errorMessage);
+ }
+
+ /**
+ * TraceGC wrapper around $(REF _d_arraysetlengthT, core,internal,array,core.internal.array.capacity).
+ * Bugs:
+ * This function template was ported from a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations.
+ */
+ alias _d_arraysetlengthTTrace = _d_HookTraceImpl!(Tarr, _d_arraysetlengthT, errorMessage);
+}
+
+@safe unittest
+{
+ struct S
+ {
+ float f = 1.0;
+ }
+
+ int[] arr;
+ _d_arraysetlengthTImpl!(typeof(arr))._d_arraysetlengthT(arr, 16);
+ assert(arr.length == 16);
+ foreach (int i; arr)
+ assert(i == int.init);
+
+ shared S[] arr2;
+ _d_arraysetlengthTImpl!(typeof(arr2))._d_arraysetlengthT(arr2, 16);
+ assert(arr2.length == 16);
+ foreach (s; arr2)
+ assert(s == S.init);
+}
diff --git a/libphobos/libdruntime/core/internal/array/casting.d b/libphobos/libdruntime/core/internal/array/casting.d
new file mode 100644
index 00000000000..e862f8eb96a
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/casting.d
@@ -0,0 +1,115 @@
+/**
+ This module contains compiler support for casting dynamic arrays
+
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/internal/_array/_casting.d)
+*/
+module core.internal.array.casting;
+
+/**
+Used by `__ArrayCast` to emit a descriptive error message.
+
+It is a template so it can be used by `__ArrayCast` in -betterC
+builds. It is separate from `__ArrayCast` to minimize code
+bloat.
+
+Params:
+ fromType = name of the type being cast from
+ fromSize = total size in bytes of the array being cast from
+ toType = name of the type being cast o
+ toSize = total size in bytes of the array being cast to
+ */
+private void onArrayCastError()(string fromType, size_t fromSize, string toType, size_t toSize) @trusted
+{
+ import core.internal.string : unsignedToTempString;
+ import core.memory : pureMalloc;
+
+ // convert discontiguous `msgComponents` to contiguous string on the C heap
+ enum msgLength = 2048;
+ // note: never freed!
+ char* msg = cast(char *)pureMalloc(msgLength);
+
+ size_t index = 0;
+ void add(const(char)[] m)
+ {
+ import core.stdc.string : memcpy;
+
+ auto N = msgLength - 1 - index;
+ if (N > m.length)
+ N = m.length;
+ // prevent superfluous and betterC-unfriendly checks via direct memcpy
+ memcpy(msg + index, m.ptr, N);
+ index += N;
+ }
+
+ add("An array of size ");
+ auto s = unsignedToTempString(fromSize);
+ add(s[]);
+ add(" does not align on an array of size ");
+ s = unsignedToTempString(toSize);
+ add(s[]);
+ add(", so `");
+ add(fromType);
+ add("` cannot be cast to `");
+ add(toType);
+ add("`");
+ msg[index] = '\0'; // null-termination
+
+ // first argument must evaluate to `false` at compile-time to maintain memory safety in release builds
+ assert(false, msg[0 .. index]);
+}
+
+/**
+The compiler lowers expressions of `cast(TTo[])TFrom[]` to
+this implementation.
+
+Params:
+ from = the array to reinterpret-cast
+
+Returns:
+ `from` reinterpreted as `TTo[]`
+ */
+TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) @nogc pure @trusted
+{
+ const fromSize = from.length * TFrom.sizeof;
+ const toLength = fromSize / TTo.sizeof;
+
+ if ((fromSize % TTo.sizeof) != 0)
+ {
+ onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof);
+ }
+
+ struct Array
+ {
+ size_t length;
+ void* ptr;
+ }
+ auto a = cast(Array*)&from;
+ a.length = toLength; // jam new length
+ return *cast(TTo[]*)a;
+}
+
+@safe @nogc pure nothrow unittest
+{
+ byte[int.sizeof * 3] b = cast(byte) 0xab;
+ int[] i;
+ short[] s;
+
+ i = __ArrayCast!(byte, int)(b);
+ assert(i.length == 3);
+ foreach (v; i)
+ assert(v == cast(int) 0xabab_abab);
+
+ s = __ArrayCast!(byte, short)(b);
+ assert(s.length == 6);
+ foreach (v; s)
+ assert(v == cast(short) 0xabab);
+
+ s = __ArrayCast!(int, short)(i);
+ assert(s.length == 6);
+ foreach (v; s)
+ assert(v == cast(short) 0xabab);
+}
diff --git a/libphobos/libdruntime/core/internal/array/comparison.d b/libphobos/libdruntime/core/internal/array/comparison.d
new file mode 100644
index 00000000000..1a68b9b9e71
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/comparison.d
@@ -0,0 +1,242 @@
+/**
+ * This module contains compiler support for comparing dynamic arrays
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2019.
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Source: $(DRUNTIMESRC core/internal/_array/_comparison.d)
+ */
+
+module core.internal.array.comparison;
+
+int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted
+ if (__traits(isScalar, T))
+{
+ // Compute U as the implementation type for T
+ static if (is(T == ubyte) || is(T == void) || is(T == bool))
+ alias U = char;
+ else static if (is(T == wchar))
+ alias U = ushort;
+ else static if (is(T == dchar))
+ alias U = uint;
+ else static if (is(T == ifloat))
+ alias U = float;
+ else static if (is(T == idouble))
+ alias U = double;
+ else static if (is(T == ireal))
+ alias U = real;
+ else
+ alias U = T;
+
+ static if (is(U == char))
+ {
+ import core.internal.string : dstrcmp;
+ return dstrcmp(cast(char[]) lhs, cast(char[]) rhs);
+ }
+ else static if (!is(U == T))
+ {
+ // Reuse another implementation
+ return __cmp(cast(U[]) lhs, cast(U[]) rhs);
+ }
+ else
+ {
+ version (BigEndian)
+ static if (__traits(isUnsigned, T) ? !is(T == __vector) : is(T : P*, P))
+ {
+ if (!__ctfe)
+ {
+ import core.stdc.string : memcmp;
+ int c = memcmp(lhs.ptr, rhs.ptr, (lhs.length <= rhs.length ? lhs.length : rhs.length) * T.sizeof);
+ if (c)
+ return c;
+ static if (size_t.sizeof <= uint.sizeof && T.sizeof >= 2)
+ return cast(int) lhs.length - cast(int) rhs.length;
+ else
+ return int(lhs.length > rhs.length) - int(lhs.length < rhs.length);
+ }
+ }
+
+ immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length;
+ foreach (const u; 0 .. len)
+ {
+ static if (__traits(isFloating, T))
+ {
+ immutable a = lhs.ptr[u], b = rhs.ptr[u];
+ static if (is(T == cfloat) || is(T == cdouble)
+ || is(T == creal))
+ {
+ // Use rt.cmath2._Ccmp instead ?
+ auto r = (a.re > b.re) - (a.re < b.re);
+ if (!r) r = (a.im > b.im) - (a.im < b.im);
+ }
+ else
+ {
+ const r = (a > b) - (a < b);
+ }
+ if (r) return r;
+ }
+ else if (lhs.ptr[u] != rhs.ptr[u])
+ return lhs.ptr[u] < rhs.ptr[u] ? -1 : 1;
+ }
+ return (lhs.length > rhs.length) - (lhs.length < rhs.length);
+ }
+}
+
+// This function is called by the compiler when dealing with array
+// comparisons in the semantic analysis phase of CmpExp. The ordering
+// comparison is lowered to a call to this template.
+int __cmp(T1, T2)(T1[] s1, T2[] s2)
+if (!__traits(isScalar, T1) && !__traits(isScalar, T2))
+{
+ import core.internal.traits : Unqual;
+ alias U1 = Unqual!T1;
+ alias U2 = Unqual!T2;
+
+ static if (is(U1 == void) && is(U2 == void))
+ static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; }
+ else
+ static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; }
+
+ // All unsigned byte-wide types = > dstrcmp
+ immutable len = s1.length <= s2.length ? s1.length : s2.length;
+
+ foreach (const u; 0 .. len)
+ {
+ static if (__traits(compiles, __cmp(at(s1, u), at(s2, u))))
+ {
+ auto c = __cmp(at(s1, u), at(s2, u));
+ if (c != 0)
+ return c;
+ }
+ else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u))))
+ {
+ auto c = at(s1, u).opCmp(at(s2, u));
+ if (c != 0)
+ return c;
+ }
+ else static if (__traits(compiles, at(s1, u) < at(s2, u)))
+ {
+ if (at(s1, u) != at(s2, u))
+ return at(s1, u) < at(s2, u) ? -1 : 1;
+ }
+ else
+ {
+ // TODO: fix this legacy bad behavior, see
+ // https://issues.dlang.org/show_bug.cgi?id=17244
+ static assert(is(U1 == U2), "Internal error.");
+ import core.stdc.string : memcmp;
+ auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))();
+ if (c != 0)
+ return c;
+ }
+ }
+ return (s1.length > s2.length) - (s1.length < s2.length);
+}
+
+// integral types
+@safe unittest
+{
+ void compareMinMax(T)()
+ {
+ T[2] a = [T.max, T.max];
+ T[2] b = [T.min, T.min];
+
+ assert(__cmp(a, b) > 0);
+ assert(__cmp(b, a) < 0);
+ }
+
+ compareMinMax!int;
+ compareMinMax!uint;
+ compareMinMax!long;
+ compareMinMax!ulong;
+ compareMinMax!short;
+ compareMinMax!ushort;
+ compareMinMax!byte;
+ compareMinMax!dchar;
+ compareMinMax!wchar;
+}
+
+// char types (dstrcmp)
+@safe unittest
+{
+ void compareMinMax(T)()
+ {
+ T[2] a = [T.max, T.max];
+ T[2] b = [T.min, T.min];
+
+ assert(__cmp(a, b) > 0);
+ assert(__cmp(b, a) < 0);
+ }
+
+ compareMinMax!ubyte;
+ compareMinMax!bool;
+ compareMinMax!char;
+ compareMinMax!(const char);
+
+ string s1 = "aaaa";
+ string s2 = "bbbb";
+ assert(__cmp(s2, s1) > 0);
+ assert(__cmp(s1, s2) < 0);
+}
+
+// fp types
+@safe unittest
+{
+ void compareMinMax(T)()
+ {
+ T[2] a = [T.max, T.max];
+ T[2] b = [T.min_normal, T.min_normal];
+ T[2] c = [T.max, T.min_normal];
+ T[1] d = [T.max];
+
+ assert(__cmp(a, b) > 0);
+ assert(__cmp(b, a) < 0);
+ assert(__cmp(a, c) > 0);
+ assert(__cmp(a, d) > 0);
+ assert(__cmp(d, c) < 0);
+ assert(__cmp(c, c) == 0);
+ }
+
+ compareMinMax!real;
+ compareMinMax!float;
+ compareMinMax!double;
+
+ // qualifiers
+ compareMinMax!(const real);
+ compareMinMax!(immutable real);
+}
+
+// void[]
+@safe unittest
+{
+ void[] a;
+ const(void)[] b;
+
+ (() @trusted
+ {
+ a = cast(void[]) "bb";
+ b = cast(const(void)[]) "aa";
+ })();
+
+ assert(__cmp(a, b) > 0);
+ assert(__cmp(b, a) < 0);
+}
+
+// arrays of arrays with mixed modifiers
+@safe unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=17876
+ bool less1(immutable size_t[][] a, size_t[][] b) { return a < b; }
+ bool less2(const void[][] a, void[][] b) { return a < b; }
+ bool less3(inout size_t[][] a, size_t[][] b) { return a < b; }
+
+ immutable size_t[][] a = [[1, 2], [3, 4]];
+ size_t[][] b = [[1, 2], [3, 5]];
+ assert(less1(a, b));
+ assert(less3(a, b));
+
+ auto va = [cast(immutable void[])a[0], a[1]];
+ auto vb = [cast(void[])b[0], b[1]];
+ assert(less2(va, vb));
+}
diff --git a/libphobos/libdruntime/core/internal/array/concatenation.d b/libphobos/libdruntime/core/internal/array/concatenation.d
new file mode 100644
index 00000000000..955e3814769
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/concatenation.d
@@ -0,0 +1,75 @@
+/**
+ This module contains support for controlling dynamic arrays' concatenation
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/internal/_array/_concatenation.d)
+*/
+module core.internal.array.concatenation;
+
+/// See $(REF _d_arraycatnTX, rt,lifetime)
+private extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) pure nothrow;
+
+/// Implementation of `_d_arraycatnTX` and `_d_arraycatnTXTrace`
+template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T)
+{
+ import core.internal.array.utils : _d_HookTraceImpl;
+
+ private enum errorMessage = "Cannot concatenate arrays if compiling without support for runtime type information!";
+
+ /**
+ * Concatenating the arrays inside of `arrs`.
+ * `_d_arraycatnTX([a, b, c])` means `a ~ b ~ c`.
+ * Params:
+ * arrs = Array containing arrays that will be concatenated.
+ * Returns:
+ * A newly allocated array that contains all the elements from all the arrays in `arrs`.
+ * Bugs:
+ * This function template was ported from a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations.
+ */
+ ResultArrT _d_arraycatnTX(scope const Tarr arrs) @trusted pure nothrow
+ {
+ pragma(inline, false);
+ version (D_TypeInfo)
+ {
+ auto ti = typeid(ResultArrT);
+
+ byte[][] arrs2 = (cast(byte[]*)arrs.ptr)[0 .. arrs.length];
+ void[] result = ._d_arraycatnTX(ti, arrs2);
+ return (cast(T*)result.ptr)[0 .. result.length];
+ }
+ else
+ assert(0, errorMessage);
+ }
+
+ /**
+ * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concat).
+ * Bugs:
+ * This function template was ported from a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations.
+ */
+ alias _d_arraycatnTXTrace = _d_HookTraceImpl!(ResultArrT, _d_arraycatnTX, errorMessage);
+}
+
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ }
+ }
+
+ S[][] arr = [[S(0), S(1), S(2), S(3)], [S(4), S(5), S(6), S(7)]];
+ S[] result = _d_arraycatnTXImpl!(typeof(arr))._d_arraycatnTX(arr);
+
+ assert(counter == 8);
+ assert(result == [S(0), S(1), S(2), S(3), S(4), S(5), S(6), S(7)]);
+}
diff --git a/libphobos/libdruntime/core/internal/array/construction.d b/libphobos/libdruntime/core/internal/array/construction.d
new file mode 100644
index 00000000000..b58ed51557f
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/construction.d
@@ -0,0 +1,307 @@
+/**
+ This module contains compiler support for constructing dynamic arrays
+
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/internal/_array/_construction.d)
+*/
+module core.internal.array.construction;
+
+/**
+ * Does array initialization (not assignment) from another array of the same element type.
+ * Params:
+ * to = what array to initialize
+ * from = what data the array should be initialized with
+ * Returns:
+ * The constructed `to`
+ * Bugs:
+ * This function template was ported from a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations.
+ */
+Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted
+{
+ pragma(inline, false);
+ import core.internal.traits : hasElaborateCopyConstructor, Unqual;
+ import core.lifetime : copyEmplace;
+ import core.stdc.string : memcpy;
+ debug(PRINTF) import core.stdc.stdio;
+
+ // Force `enforceRawArraysConformable` to be `pure`
+ void enforceRawArraysConformable(const char[] action, const size_t elementSize, const void[] a1, const void[] a2, in bool allowOverlap = false) @trusted
+ {
+ import core.internal.util.array : enforceRawArraysConformable;
+
+ alias Type = void function(const char[] action, const size_t elementSize, const void[] a1, const void[] a2, in bool allowOverlap = false) pure nothrow;
+ (cast(Type)&enforceRawArraysConformable)(action, elementSize, a1, a2, allowOverlap);
+ }
+
+ debug(PRINTF) printf("_d_arrayctor(to = %p,%d, from = %p,%d) size = %d\n", from.ptr, from.length, to.ptr, to.length, T.tsize);
+
+ auto element_size = T.sizeof;
+
+ void[] vFrom = (cast(void*)from.ptr)[0..from.length];
+ void[] vTo = (cast(void*)to.ptr)[0..to.length];
+ enforceRawArraysConformable("initialization", element_size, vFrom, vTo, false);
+
+ static if (hasElaborateCopyConstructor!T)
+ {
+ size_t i;
+ try
+ {
+ for (i = 0; i < to.length; i++)
+ copyEmplace(from[i], to[i]);
+ }
+ catch (Exception o)
+ {
+ /* Destroy, in reverse order, what we've constructed so far
+ */
+ while (i--)
+ {
+ auto elem = cast(Unqual!T*)&to[i];
+ destroy(*elem);
+ }
+
+ throw o;
+ }
+ }
+ else
+ {
+ // blit all elements at once
+ memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof);
+ }
+
+ return to;
+}
+
+// postblit
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(this) { counter++; }
+ }
+
+ S[4] arr1;
+ S[4] arr2 = [S(0), S(1), S(2), S(3)];
+ _d_arrayctor(arr1[], arr2[]);
+
+ assert(counter == 4);
+ assert(arr1 == arr2);
+}
+
+// copy constructor
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(int val) { this.val = val; }
+ this(const scope ref S rhs)
+ {
+ val = rhs.val;
+ counter++;
+ }
+ }
+
+ S[4] arr1;
+ S[4] arr2 = [S(0), S(1), S(2), S(3)];
+ _d_arrayctor(arr1[], arr2[]);
+
+ assert(counter == 4);
+ assert(arr1 == arr2);
+}
+
+@safe nothrow unittest
+{
+ // Test that throwing works
+ int counter;
+ bool didThrow;
+
+ struct Throw
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ if (counter == 2)
+ throw new Exception("");
+ }
+ }
+ try
+ {
+ Throw[4] a;
+ Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)];
+ _d_arrayctor(a[], b[]);
+ }
+ catch (Exception)
+ {
+ didThrow = true;
+ }
+ assert(didThrow);
+ assert(counter == 2);
+
+
+ // Test that `nothrow` works
+ didThrow = false;
+ counter = 0;
+ struct NoThrow
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ }
+ }
+ try
+ {
+ NoThrow[4] a;
+ NoThrow[4] b = [NoThrow(1), NoThrow(2), NoThrow(3), NoThrow(4)];
+ _d_arrayctor(a[], b[]);
+ }
+ catch (Exception)
+ {
+ didThrow = false;
+ }
+ assert(!didThrow);
+ assert(counter == 4);
+}
+
+/**
+ * Do construction of an array.
+ * ti[count] p = value;
+ * Params:
+ * p = what array to initialize
+ * value = what data to construct the array with
+ * Bugs:
+ * This function template was ported from a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations.
+ */
+void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted
+{
+ pragma(inline, false);
+ import core.internal.traits : Unqual;
+ import core.lifetime : copyEmplace;
+
+ size_t i;
+ try
+ {
+ for (i = 0; i < p.length; i++)
+ copyEmplace(value, p[i]);
+ }
+ catch (Exception o)
+ {
+ // Destroy, in reverse order, what we've constructed so far
+ while (i--)
+ {
+ auto elem = cast(Unqual!T*)&p[i];
+ destroy(*elem);
+ }
+
+ throw o;
+ }
+}
+
+// postblit
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ }
+ }
+
+ S[4] arr;
+ S s = S(1234);
+ _d_arraysetctor(arr[], s);
+ assert(counter == arr.length);
+ assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
+}
+
+// copy constructor
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(int val) { this.val = val; }
+ this(const scope ref S rhs)
+ {
+ val = rhs.val;
+ counter++;
+ }
+ }
+
+ S[4] arr;
+ S s = S(1234);
+ _d_arraysetctor(arr[], s);
+ assert(counter == arr.length);
+ assert(arr == [S(1234), S(1234), S(1234), S(1234)]);
+}
+
+@safe nothrow unittest
+{
+ // Test that throwing works
+ int counter;
+ bool didThrow;
+ struct Throw
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ if (counter == 2)
+ throw new Exception("Oh no.");
+ }
+ }
+ try
+ {
+ Throw[4] a;
+ Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)];
+ _d_arrayctor(a[], b[]);
+ }
+ catch (Exception)
+ {
+ didThrow = true;
+ }
+ assert(didThrow);
+ assert(counter == 2);
+
+
+ // Test that `nothrow` works
+ didThrow = false;
+ counter = 0;
+ struct NoThrow
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ }
+ }
+ try
+ {
+ NoThrow[4] a;
+ NoThrow b = NoThrow(1);
+ _d_arraysetctor(a[], b);
+ foreach (ref e; a)
+ assert(e == NoThrow(1));
+ }
+ catch (Exception)
+ {
+ didThrow = false;
+ }
+ assert(!didThrow);
+ assert(counter == 4);
+}
diff --git a/libphobos/libdruntime/core/internal/array/equality.d b/libphobos/libdruntime/core/internal/array/equality.d
new file mode 100644
index 00000000000..b12e2f24ccf
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/equality.d
@@ -0,0 +1,237 @@
+/**
+ * This module contains compiler support determining equality of arrays.
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2020.
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Source: $(DRUNTIMESRC core/internal/_array/_equality.d)
+ */
+
+module core.internal.array.equality;
+
+// The compiler lowers `lhs == rhs` to `__equals(lhs, rhs)` for
+// * dynamic arrays,
+// * (most) arrays of different (unqualified) element types, and
+// * arrays of structs with custom opEquals.
+
+ // The scalar-only overload takes advantage of known properties of scalars to
+ // reduce template instantiation. This is expected to be the most common case.
+bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs)
+@nogc nothrow pure @trusted
+if (__traits(isScalar, T1) && __traits(isScalar, T2))
+{
+ if (lhs.length != rhs.length)
+ return false;
+
+ static if (T1.sizeof == T2.sizeof
+ // Signedness needs to match for types that promote to int.
+ // (Actually it would be okay to memcmp bool[] and byte[] but that is
+ // probably too uncommon to be worth checking for.)
+ && (T1.sizeof >= 4 || __traits(isUnsigned, T1) == __traits(isUnsigned, T2))
+ && !__traits(isFloating, T1) && !__traits(isFloating, T2))
+ {
+ if (!__ctfe)
+ {
+ // This would improperly allow equality of integers and pointers
+ // but the CTFE branch will stop this function from compiling then.
+ import core.stdc.string : memcmp;
+ return lhs.length == 0 ||
+ 0 == memcmp(cast(const void*) lhs.ptr, cast(const void*) rhs.ptr, lhs.length * T1.sizeof);
+ }
+ }
+
+ foreach (const i; 0 .. lhs.length)
+ if (lhs.ptr[i] != rhs.ptr[i])
+ return false;
+ return true;
+}
+
+bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs)
+if (!__traits(isScalar, T1) || !__traits(isScalar, T2))
+{
+ if (lhs.length != rhs.length)
+ return false;
+
+ if (lhs.length == 0)
+ return true;
+
+ static if (useMemcmp!(T1, T2))
+ {
+ if (!__ctfe)
+ {
+ static bool trustedMemcmp(scope T1[] lhs, scope T2[] rhs) @trusted @nogc nothrow pure
+ {
+ pragma(inline, true);
+ import core.stdc.string : memcmp;
+ return memcmp(cast(void*) lhs.ptr, cast(void*) rhs.ptr, lhs.length * T1.sizeof) == 0;
+ }
+ return trustedMemcmp(lhs, rhs);
+ }
+ else
+ {
+ foreach (const i; 0 .. lhs.length)
+ {
+ if (at(lhs, i) != at(rhs, i))
+ return false;
+ }
+ return true;
+ }
+ }
+ else
+ {
+ foreach (const i; 0 .. lhs.length)
+ {
+ if (at(lhs, i) != at(rhs, i))
+ return false;
+ }
+ return true;
+ }
+}
+
+@safe unittest
+{
+ assert(__equals([], []));
+ assert(!__equals([1, 2], [1, 2, 3]));
+}
+
+@safe unittest
+{
+ auto a = "hello"c;
+
+ assert(a != "hel");
+ assert(a != "helloo");
+ assert(a != "betty");
+ assert(a == "hello");
+ assert(a != "hxxxx");
+
+ float[] fa = [float.nan];
+ assert(fa != fa);
+}
+
+@safe unittest
+{
+ struct A
+ {
+ int a;
+ }
+
+ auto arr1 = [A(0), A(2)];
+ auto arr2 = [A(0), A(1)];
+ auto arr3 = [A(0), A(1)];
+
+ assert(arr1 != arr2);
+ assert(arr2 == arr3);
+}
+
+@safe unittest
+{
+ struct A
+ {
+ int a;
+ int b;
+
+ bool opEquals(const A other)
+ {
+ return this.a == other.b && this.b == other.a;
+ }
+ }
+
+ auto arr1 = [A(1, 0), A(0, 1)];
+ auto arr2 = [A(1, 0), A(0, 1)];
+ auto arr3 = [A(0, 1), A(1, 0)];
+
+ assert(arr1 != arr2);
+ assert(arr2 == arr3);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18252
+@safe unittest
+{
+ string[int][] a1, a2;
+ assert(__equals(a1, a2));
+ assert(a1 == a2);
+ a1 ~= [0: "zero"];
+ a2 ~= [0: "zero"];
+ assert(__equals(a1, a2));
+ assert(a1 == a2);
+ a2[0][1] = "one";
+ assert(!__equals(a1, a2));
+ assert(a1 != a2);
+}
+
+
+private:
+
+// - Recursively folds static array types to their element type,
+// - maps void to ubyte, and
+// - pointers to size_t.
+template BaseType(T)
+{
+ static if (__traits(isStaticArray, T))
+ alias BaseType = BaseType!(typeof(T.init[0]));
+ else static if (is(immutable T == immutable void))
+ alias BaseType = ubyte;
+ else static if (is(T == E*, E))
+ alias BaseType = size_t;
+ else
+ alias BaseType = T;
+}
+
+// Use memcmp if the element sizes match and both base element types are integral.
+// Due to int promotion, disallow small integers of diverging signed-ness though.
+template useMemcmp(T1, T2)
+{
+ static if (T1.sizeof != T2.sizeof)
+ enum useMemcmp = false;
+ else
+ {
+ alias B1 = BaseType!T1;
+ alias B2 = BaseType!T2;
+ enum useMemcmp = __traits(isIntegral, B1) && __traits(isIntegral, B2)
+ && !( (B1.sizeof < 4 || B2.sizeof < 4) && __traits(isUnsigned, B1) != __traits(isUnsigned, B2) );
+ }
+}
+
+unittest
+{
+ enum E { foo, bar }
+
+ static assert(useMemcmp!(byte, byte));
+ static assert(useMemcmp!(ubyte, ubyte));
+ static assert(useMemcmp!(void, const void));
+ static assert(useMemcmp!(void, immutable bool));
+ static assert(useMemcmp!(void, inout char));
+ static assert(useMemcmp!(void, shared ubyte));
+ static assert(!useMemcmp!(void, byte)); // differing signed-ness
+ static assert(!useMemcmp!(char[8], byte[8])); // ditto
+
+ static assert(useMemcmp!(short, short));
+ static assert(useMemcmp!(wchar, ushort));
+ static assert(!useMemcmp!(wchar, short)); // differing signed-ness
+
+ static assert(useMemcmp!(int, uint)); // no promotion, ignoring signed-ness
+ static assert(useMemcmp!(dchar, E));
+
+ static assert(useMemcmp!(immutable void*, size_t));
+ static assert(useMemcmp!(double*, ptrdiff_t));
+ static assert(useMemcmp!(long[2][3], const(ulong)[2][3]));
+
+ static assert(!useMemcmp!(float, float));
+ static assert(!useMemcmp!(double[2], double[2]));
+ static assert(!useMemcmp!(Object, Object));
+ static assert(!useMemcmp!(int[], int[]));
+}
+
+// Returns a reference to an array element, eliding bounds check and
+// casting void to ubyte.
+pragma(inline, true)
+ref at(T)(T[] r, size_t i) @trusted
+ // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959
+ if (!(is(T == struct) && !is(typeof(T.sizeof))))
+{
+ static if (is(immutable T == immutable void))
+ return (cast(ubyte*) r.ptr)[i];
+ else
+ return r.ptr[i];
+}
diff --git a/libphobos/libdruntime/core/internal/array/operations.d b/libphobos/libdruntime/core/internal/array/operations.d
new file mode 100644
index 00000000000..3e2331484b3
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/operations.d
@@ -0,0 +1,670 @@
+/**
+ This module contains support array (vector) operations
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/_internal/_array/_operations.d)
+*/
+module core.internal.array.operations;
+import core.internal.traits : Filter, staticMap, Unqual;
+
+version (GNU) version = GNU_OR_LDC;
+version (LDC) version = GNU_OR_LDC;
+
+/**
+ * Perform array (vector) operations and store the result in `res`. Operand
+ * types and operations are passed as template arguments in Reverse Polish
+ * Notation (RPN).
+
+ * Operands can be slices or scalar types. The element types of all
+ * slices and all scalar types must be implicitly convertible to `T`.
+ *
+ * Operations are encoded as strings, e.g. `"+"`, `"%"`, `"*="`. Unary
+ * operations are prefixed with "u", e.g. `"u-"`, `"u~"`. Only the last
+ * operation can and must be an assignment (`"="`) or op-assignment (`"op="`).
+ *
+ * All slice operands must have the same length as the result slice.
+ *
+ * Params: T[] = type of result slice
+ * Args = operand types and operations in RPN
+ * res = the slice in which to store the results
+ * args = operand values
+ *
+ * Returns: the slice containing the result
+ */
+T[] arrayOp(T : T[], Args...)(T[] res, Filter!(isType, Args) args) @trusted @nogc pure nothrow
+{
+ alias scalarizedExp = staticMap!(toElementType, Args);
+ alias check = typeCheck!(true, T, scalarizedExp); // must support all scalar ops
+
+ foreach (argsIdx, arg; typeof(args))
+ {
+ static if (is(arg == U[], U))
+ {
+ assert(res.length == args[argsIdx].length, "Mismatched array lengths for vector operation");
+ }
+ }
+
+ size_t pos;
+ static if (vectorizeable!(T[], Args))
+ {
+ alias vec = .vec!T;
+ alias load = .load!(T, vec.length);
+ alias store = .store!(T, vec.length);
+
+ // Given that there are at most as many scalars broadcast as there are
+ // operations in any `ary[] = ary[] op const op const`, it should always be
+ // worthwhile to choose vector operations.
+ if (!__ctfe && res.length >= vec.length)
+ {
+ mixin(initScalarVecs!Args);
+
+ auto n = res.length / vec.length;
+ do
+ {
+ mixin(vectorExp!Args ~ ";");
+ pos += vec.length;
+ }
+ while (--n);
+ }
+ }
+ for (; pos < res.length; ++pos)
+ mixin(scalarExp!Args ~ ";");
+
+ return res;
+}
+
+private:
+
+// SIMD helpers
+
+version (DigitalMars)
+{
+ import core.simd;
+
+ template vec(T)
+ {
+ enum regsz = 16; // SSE2
+ enum N = regsz / T.sizeof;
+ alias vec = __vector(T[N]);
+ }
+
+ void store(T, size_t N)(T* p, const scope __vector(T[N]) val)
+ {
+ pragma(inline, true);
+ alias vec = __vector(T[N]);
+
+ static if (is(T == float))
+ cast(void) __simd_sto(XMM.STOUPS, *cast(vec*) p, val);
+ else static if (is(T == double))
+ cast(void) __simd_sto(XMM.STOUPD, *cast(vec*) p, val);
+ else
+ cast(void) __simd_sto(XMM.STODQU, *cast(vec*) p, val);
+ }
+
+ const(__vector(T[N])) load(T, size_t N)(const scope T* p)
+ {
+ import core.simd;
+
+ pragma(inline, true);
+ alias vec = __vector(T[N]);
+
+ static if (is(T == float))
+ return cast(typeof(return)) __simd(XMM.LODUPS, *cast(const vec*) p);
+ else static if (is(T == double))
+ return cast(typeof(return)) __simd(XMM.LODUPD, *cast(const vec*) p);
+ else
+ return cast(typeof(return)) __simd(XMM.LODDQU, *cast(const vec*) p);
+ }
+
+ __vector(T[N]) binop(string op, T, size_t N)(const scope __vector(T[N]) a, const scope __vector(T[N]) b)
+ {
+ pragma(inline, true);
+ return mixin("a " ~ op ~ " b");
+ }
+
+ __vector(T[N]) unaop(string op, T, size_t N)(const scope __vector(T[N]) a)
+ if (op[0] == 'u')
+ {
+ pragma(inline, true);
+ return mixin(op[1 .. $] ~ "a");
+ }
+}
+
+// mixin gen
+
+/**
+Check whether operations on operand types are supported. This
+template recursively reduces the expression tree and determines
+intermediate types.
+Type checking is done here rather than in the compiler to provide more
+detailed error messages.
+
+Params:
+ fail = whether to fail (static assert) with a human-friendly error message
+ T = type of result
+ Args = operand types and operations in RPN
+Returns:
+ The resulting type of the expression
+See_Also:
+ $(LREF arrayOp)
+*/
+template typeCheck(bool fail, T, Args...)
+{
+ enum idx = staticIndexOf!(not!isType, Args);
+ static if (isUnaryOp(Args[idx]))
+ {
+ alias UT = Args[idx - 1];
+ enum op = Args[idx][1 .. $];
+ static if (is(typeof((UT a) => mixin(op ~ "cast(int) a")) RT == return))
+ alias typeCheck = typeCheck!(fail, T, Args[0 .. idx - 1], RT, Args[idx + 1 .. $]);
+ else static if (fail)
+ static assert(0, "Unary `" ~ op ~ "` not supported for type `" ~ UT.stringof ~ "`.");
+ }
+ else static if (isBinaryOp(Args[idx]))
+ {
+ alias LHT = Args[idx - 2];
+ alias RHT = Args[idx - 1];
+ enum op = Args[idx];
+ static if (is(typeof((LHT a, RHT b) => mixin("a " ~ op ~ " b")) RT == return))
+ alias typeCheck = typeCheck!(fail, T, Args[0 .. idx - 2], RT, Args[idx + 1 .. $]);
+ else static if (fail)
+ static assert(0,
+ "Binary `" ~ op ~ "` not supported for types `"
+ ~ LHT.stringof ~ "` and `" ~ RHT.stringof ~ "`.");
+ }
+ else static if (Args[idx] == "=" || isBinaryAssignOp(Args[idx]))
+ {
+ alias RHT = Args[idx - 1];
+ enum op = Args[idx];
+ static if (is(T == __vector(ET[N]), ET, size_t N))
+ {
+ // no `cast(T)` before assignment for vectors
+ static if (is(typeof((T res, RHT b) => mixin("res " ~ op ~ " b")) RT == return)
+ && // workaround https://issues.dlang.org/show_bug.cgi?id=17758
+ (op != "=" || is(Unqual!T == Unqual!RHT)))
+ alias typeCheck = typeCheck!(fail, T, Args[0 .. idx - 1], RT, Args[idx + 1 .. $]);
+ else static if (fail)
+ static assert(0,
+ "Binary op `" ~ op ~ "` not supported for types `"
+ ~ T.stringof ~ "` and `" ~ RHT.stringof ~ "`.");
+ }
+ else
+ {
+ static if (is(typeof((RHT b) => mixin("cast(T) b"))))
+ {
+ static if (is(typeof((T res, T b) => mixin("res " ~ op ~ " b")) RT == return))
+ alias typeCheck = typeCheck!(fail, T, Args[0 .. idx - 1], RT, Args[idx + 1 .. $]);
+ else static if (fail)
+ static assert(0,
+ "Binary op `" ~ op ~ "` not supported for types `"
+ ~ T.stringof ~ "` and `" ~ T.stringof ~ "`.");
+ }
+ else static if (fail)
+ static assert(0,
+ "`cast(" ~ T.stringof ~ ")` not supported for type `" ~ RHT.stringof ~ "`.");
+ }
+ }
+ else
+ static assert(0);
+}
+/// ditto
+template typeCheck(bool fail, T, ResultType)
+{
+ alias typeCheck = ResultType;
+}
+
+version (GNU_OR_LDC)
+{
+ // leave it to the auto-vectorizer
+ enum vectorizeable(E : E[], Args...) = false;
+}
+else
+{
+ // check whether arrayOp is vectorizable
+ template vectorizeable(E : E[], Args...)
+ {
+ static if (is(vec!E))
+ {
+ // type check with vector types
+ enum vectorizeable = is(typeCheck!(false, vec!E, staticMap!(toVecType, Args)));
+ }
+ else
+ enum vectorizeable = false;
+ }
+
+ version (X86_64) unittest
+ {
+ static assert(vectorizeable!(double[], const(double)[], double[], "+", "="));
+ static assert(!vectorizeable!(double[], const(ulong)[], double[], "+", "="));
+ // Vector type are (atm.) not implicitly convertible and would require
+ // lots of SIMD intrinsics. Therefor leave mixed type array ops to
+ // GDC/LDC's auto-vectorizers.
+ static assert(!vectorizeable!(double[], const(uint)[], uint, "+", "="));
+ }
+}
+
+bool isUnaryOp(scope string op) pure nothrow @safe @nogc
+{
+ return op[0] == 'u';
+}
+
+bool isBinaryOp(scope string op) pure nothrow @safe @nogc
+{
+ if (op == "^^")
+ return true;
+ if (op.length != 1)
+ return false;
+ switch (op[0])
+ {
+ case '+', '-', '*', '/', '%', '|', '&', '^':
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isBinaryAssignOp(string op)
+{
+ return op.length >= 2 && op[$ - 1] == '=' && isBinaryOp(op[0 .. $ - 1]);
+}
+
+// Generate mixin expression to perform scalar arrayOp loop expression, assumes
+// `pos` to be the current slice index, `args` to contain operand values, and
+// `res` the target slice.
+enum scalarExp(Args...) =
+(){
+ string[] stack;
+ size_t argsIdx;
+
+ static if (is(Args[0] == U[], U))
+ alias Type = U;
+ else
+ alias Type = Args[0];
+
+ foreach (i, arg; Args)
+ {
+ static if (is(arg == T[], T))
+ stack ~= "args[" ~ argsIdx++.toString ~ "][pos]";
+ else static if (is(arg))
+ stack ~= "args[" ~ argsIdx++.toString ~ "]";
+ else static if (isUnaryOp(arg))
+ {
+ auto op = arg[0] == 'u' ? arg[1 .. $] : arg;
+ // Explicitly use the old integral promotion rules
+ // See also: https://dlang.org/changelog/2.078.0.html#fix16997
+ static if (is(Type : int))
+ stack[$ - 1] = "cast(typeof(" ~ stack[$ -1] ~ "))" ~ op ~ "cast(int)("~ stack[$ - 1] ~ ")";
+ else
+ stack[$ - 1] = op ~ stack[$ - 1];
+ }
+ else static if (arg == "=")
+ {
+ stack[$ - 1] = "res[pos] = cast(T)(" ~ stack[$ - 1] ~ ")";
+ }
+ else static if (isBinaryAssignOp(arg))
+ {
+ stack[$ - 1] = "res[pos] " ~ arg ~ " cast(T)(" ~ stack[$ - 1] ~ ")";
+ }
+ else static if (isBinaryOp(arg))
+ {
+ stack[$ - 2] = "(" ~ stack[$ - 2] ~ " " ~ arg ~ " " ~ stack[$ - 1] ~ ")";
+ stack.length -= 1;
+ }
+ else
+ assert(0, "Unexpected op " ~ arg);
+ }
+ assert(stack.length == 1);
+ return stack[0];
+}();
+
+// Generate mixin statement to perform vector loop initialization, assumes
+// `args` to contain operand values.
+enum initScalarVecs(Args...) =
+() {
+ size_t scalarsIdx, argsIdx;
+ string res;
+ foreach (arg; Args)
+ {
+ static if (is(arg == T[], T))
+ {
+ ++argsIdx;
+ }
+ else static if (is(arg))
+ res ~= "immutable vec scalar" ~ scalarsIdx++.toString ~ " = args["
+ ~ argsIdx++.toString ~ "];\n";
+ }
+ return res;
+}();
+
+// Generate mixin expression to perform vector arrayOp loop expression, assumes
+// `pos` to be the current slice index, `args` to contain operand values, and
+// `res` the target slice.
+enum vectorExp(Args...) =
+() {
+ size_t scalarsIdx, argsIdx;
+ string[] stack;
+ foreach (arg; Args)
+ {
+ static if (is(arg == T[], T))
+ stack ~= "load(&args[" ~ argsIdx++.toString ~ "][pos])";
+ else static if (is(arg))
+ {
+ ++argsIdx;
+ stack ~= "scalar" ~ scalarsIdx++.toString;
+ }
+ else static if (isUnaryOp(arg))
+ {
+ auto op = arg[0] == 'u' ? arg[1 .. $] : arg;
+ stack[$ - 1] = "unaop!\"" ~ arg ~ "\"(" ~ stack[$ - 1] ~ ")";
+ }
+ else static if (arg == "=")
+ {
+ stack[$ - 1] = "store(&res[pos], " ~ stack[$ - 1] ~ ")";
+ }
+ else static if (isBinaryAssignOp(arg))
+ {
+ stack[$ - 1] = "store(&res[pos], binop!\"" ~ arg[0 .. $ - 1]
+ ~ "\"(load(&res[pos]), " ~ stack[$ - 1] ~ "))";
+ }
+ else static if (isBinaryOp(arg))
+ {
+ stack[$ - 2] = "binop!\"" ~ arg ~ "\"(" ~ stack[$ - 2] ~ ", " ~ stack[$ - 1] ~ ")";
+ stack.length -= 1;
+ }
+ else
+ assert(0, "Unexpected op " ~ arg);
+ }
+ assert(stack.length == 1);
+ return stack[0];
+}();
+
+// other helpers
+
+enum isType(T) = true;
+enum isType(alias a) = false;
+template not(alias tmlp)
+{
+ enum not(Args...) = !tmlp!Args;
+}
+/**
+Find element in `haystack` for which `pred` is true.
+
+Params:
+ pred = the template predicate
+ haystack = elements to search
+Returns:
+ The first index for which `pred!haystack[index]` is true or -1.
+ */
+template staticIndexOf(alias pred, haystack...)
+{
+ static if (pred!(haystack[0]))
+ enum staticIndexOf = 0;
+ else
+ {
+ enum next = staticIndexOf!(pred, haystack[1 .. $]);
+ enum staticIndexOf = next == -1 ? -1 : next + 1;
+ }
+}
+/// converts slice types to their element type, preserves anything else
+alias toElementType(E : E[]) = E;
+alias toElementType(S) = S;
+alias toElementType(alias op) = op;
+/// converts slice types to their element type, preserves anything else
+alias toVecType(E : E[]) = vec!E;
+alias toVecType(S) = vec!S;
+alias toVecType(alias op) = op;
+
+string toString(size_t num)
+{
+ import core.internal.string : unsignedToTempString;
+ version (D_BetterC)
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=19268
+ if (__ctfe)
+ {
+ char[20] fixedbuf = void;
+ char[] buf = unsignedToTempString(num, fixedbuf);
+ char[] result = new char[buf.length];
+ result[] = buf[];
+ return (() @trusted => cast(string) result)();
+ }
+ else
+ {
+ // Failing at execution rather than during compilation is
+ // not good, but this is in `core.internal` so it should
+ // not be used by the unwary.
+ assert(0, __FUNCTION__ ~ " not available in -betterC except during CTFE.");
+ }
+ }
+ else
+ {
+ char[20] buf = void;
+ return unsignedToTempString(num, buf).idup;
+ }
+}
+
+bool contains(T)(const scope T[] ary, const scope T[] vals...)
+{
+ foreach (v1; ary)
+ foreach (v2; vals)
+ if (v1 == v2)
+ return true;
+ return false;
+}
+
+// tests
+
+version (CoreUnittest) template TT(T...)
+{
+ alias TT = T;
+}
+
+version (CoreUnittest) template _arrayOp(Args...)
+{
+ alias _arrayOp = arrayOp!Args;
+}
+
+unittest
+{
+ static void check(string op, TA, TB, T, size_t N)(TA a, TB b, const scope ref T[N] exp)
+ {
+ T[N] res;
+ _arrayOp!(T[], TA, TB, op, "=")(res[], a, b);
+ foreach (i; 0 .. N)
+ assert(res[i] == exp[i]);
+ }
+
+ static void check2(string unaOp, string binOp, TA, TB, T, size_t N)(TA a, TB b, const scope ref T[N] exp)
+ {
+ T[N] res;
+ _arrayOp!(T[], TA, TB, unaOp, binOp, "=")(res[], a, b);
+ foreach (i; 0 .. N)
+ assert(res[i] == exp[i]);
+ }
+
+ static void test(T, string op, size_t N = 16)(T a, T b, T exp)
+ {
+ T[N] va = a, vb = b, vexp = exp;
+
+ check!op(va[], vb[], vexp);
+ check!op(va[], b, vexp);
+ check!op(a, vb[], vexp);
+ }
+
+ static void test2(T, string unaOp, string binOp, size_t N = 16)(T a, T b, T exp)
+ {
+ T[N] va = a, vb = b, vexp = exp;
+
+ check2!(unaOp, binOp)(va[], vb[], vexp);
+ check2!(unaOp, binOp)(va[], b, vexp);
+ check2!(unaOp, binOp)(a, vb[], vexp);
+ }
+
+ alias UINTS = TT!(ubyte, ushort, uint, ulong);
+ alias INTS = TT!(byte, short, int, long);
+ alias FLOATS = TT!(float, double);
+
+ foreach (T; TT!(UINTS, INTS, FLOATS))
+ {
+ test!(T, "+")(1, 2, 3);
+ test!(T, "-")(3, 2, 1);
+ static if (__traits(compiles, { import std.math; }))
+ test!(T, "^^")(2, 3, 8);
+
+ test2!(T, "u-", "+")(3, 2, 1);
+ }
+
+ foreach (T; TT!(UINTS, INTS))
+ {
+ test!(T, "|")(1, 2, 3);
+ test!(T, "&")(3, 1, 1);
+ test!(T, "^")(3, 1, 2);
+
+ test2!(T, "u~", "+")(3, cast(T)~2, 5);
+ }
+
+ foreach (T; TT!(INTS, FLOATS))
+ {
+ test!(T, "-")(1, 2, -1);
+ test2!(T, "u-", "+")(-3, -2, -1);
+ test2!(T, "u-", "*")(-3, -2, -6);
+ }
+
+ foreach (T; TT!(UINTS, INTS, FLOATS))
+ {
+ test!(T, "*")(2, 3, 6);
+ test!(T, "/")(8, 4, 2);
+ test!(T, "%")(8, 6, 2);
+ }
+}
+
+// test handling of v op= exp
+unittest
+{
+ uint[32] c;
+ arrayOp!(uint[], uint, "+=")(c[], 2);
+ foreach (v; c)
+ assert(v == 2);
+ static if (__traits(compiles, { import std.math; }))
+ {
+ arrayOp!(uint[], uint, "^^=")(c[], 3);
+ foreach (v; c)
+ assert(v == 8);
+ }
+}
+
+// proper error message for UDT lacking certain ops
+unittest
+{
+ static assert(!is(typeof(&arrayOp!(int[4][], int[4], "+="))));
+ static assert(!is(typeof(&arrayOp!(int[4][], int[4], "u-", "="))));
+
+ static struct S
+ {
+ }
+
+ static assert(!is(typeof(&arrayOp!(S[], S, "+="))));
+ static assert(!is(typeof(&arrayOp!(S[], S[], "*", S, "+="))));
+ static struct S2
+ {
+ S2 opBinary(string op)(in S2) @nogc pure nothrow
+ {
+ return this;
+ }
+
+ ref S2 opOpAssign(string op)(in S2) @nogc pure nothrow
+ {
+ return this;
+ }
+ }
+
+ static assert(is(typeof(&arrayOp!(S2[], S2[], S2[], S2, "*", "+", "="))));
+ static assert(is(typeof(&arrayOp!(S2[], S2[], S2, "*", "+="))));
+}
+
+// test mixed type array op
+unittest
+{
+ uint[32] a = 0xF;
+ float[32] res = 2.0f;
+ arrayOp!(float[], const(uint)[], uint, "&", "*=")(res[], a[], 12);
+ foreach (v; res[])
+ assert(v == 24.0f);
+}
+
+// test mixed type array op
+unittest
+{
+ static struct S
+ {
+ float opBinary(string op)(in S) @nogc const pure nothrow
+ {
+ return 2.0f;
+ }
+ }
+
+ float[32] res = 24.0f;
+ S[32] s;
+ arrayOp!(float[], const(S)[], const(S)[], "+", "/=")(res[], s[], s[]);
+ foreach (v; res[])
+ assert(v == 12.0f);
+}
+
+// test scalar after operation argument
+unittest
+{
+ float[32] res, a = 2, b = 3;
+ float c = 4;
+ arrayOp!(float[], const(float)[], const(float)[], "*", float, "+", "=")(res[], a[], b[], c);
+ foreach (v; res[])
+ assert(v == 2 * 3 + 4);
+}
+
+unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=17964
+ uint bug(){
+ uint[] a = [1, 2, 3, 5, 6, 7];
+ uint[] b = [1, 2, 3, 5, 6, 7];
+ a[] |= ~b[];
+ return a[1];
+ }
+ enum x = bug();
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19796
+unittest
+{
+ double[] data = [0.5];
+ double[] result;
+ result.length = data.length;
+ result[] = -data[];
+ assert(result[0] == -0.5);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21110
+unittest
+{
+ import core.exception;
+
+ static void assertThrown(T : Throwable, E)(lazy E expression, string msg)
+ {
+ try
+ expression;
+ catch (T)
+ return;
+ assert(0, "msg");
+ }
+
+ int[] dst;
+ int[] a;
+ int[] b;
+ a.length = 3;
+ b.length = 3;
+ dst.length = 4;
+
+ void func() { dst[] = a[] + b[]; }
+ assertThrown!AssertError(func(), "Array operations with mismatched lengths must throw an error");
+}
diff --git a/libphobos/libdruntime/core/internal/array/utils.d b/libphobos/libdruntime/core/internal/array/utils.d
new file mode 100644
index 00000000000..7a829a0b3f7
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/utils.d
@@ -0,0 +1,121 @@
+/**
+ This module contains utility functions to help the implementation of the runtime hook
+
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/internal/_array/_utils.d)
+*/
+module core.internal.array.utils;
+
+import core.internal.traits : Parameters;
+
+private auto gcStatsPure() nothrow pure
+{
+ import core.memory : GC;
+
+ auto impureBypass = cast(GC.Stats function() pure nothrow)&GC.stats;
+ return impureBypass();
+}
+
+private ulong accumulatePure(string file, int line, string funcname, string name, ulong size) nothrow pure
+{
+ static ulong impureBypass(string file, int line, string funcname, string name, ulong size) @nogc nothrow
+ {
+ import core.internal.traits : externDFunc;
+
+ alias accumulate = externDFunc!("rt.profilegc.accumulate", void function(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow);
+ accumulate(file, line, funcname, name, size);
+ return size;
+ }
+
+ auto func = cast(ulong function(string file, int line, string funcname, string name, ulong size) @nogc nothrow pure)&impureBypass;
+ return func(file, line, funcname, name, size);
+}
+
+/**
+ * TraceGC wrapper around runtime hook `Hook`.
+ * Params:
+ * T = Type of hook to report to accumulate
+ * Hook = The hook to wrap
+ * errorMessage = The error message incase `version != D_TypeInfo`
+ * file = File that called `_d_HookTraceImpl`
+ * line = Line inside of `file` that called `_d_HookTraceImpl`
+ * funcname = Function that called `_d_HookTraceImpl`
+ * parameters = Parameters that will be used to call `Hook`
+ * Bugs:
+ * This function template needs be between the compiler and a much older runtime hook that bypassed safety,
+ * purity, and throwabilty checks. To prevent breaking existing code, this function template
+ * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
+*/
+auto _d_HookTraceImpl(T, alias Hook, string errorMessage)(string file, int line, string funcname, Parameters!Hook parameters) @trusted pure
+{
+ version (D_TypeInfo)
+ {
+ pragma(inline, false);
+ string name = T.stringof;
+
+ // FIXME: use rt.tracegc.accumulator when it is accessable in the future.
+ version (tracegc)
+ {
+ import core.stdc.stdio;
+
+ printf("%sTrace file = '%.*s' line = %d function = '%.*s' type = %.*s\n",
+ Hook.stringof.ptr,
+ file.length, file.ptr,
+ line,
+ funcname.length, funcname.ptr,
+ name.length, name.ptr
+ );
+ }
+
+ ulong currentlyAllocated = gcStatsPure().allocatedInCurrentThread;
+
+ scope(exit)
+ {
+ ulong size = gcStatsPure().allocatedInCurrentThread - currentlyAllocated;
+ if (size > 0)
+ if (!accumulatePure(file, line, funcname, name, size)) {
+ // This 'if' and 'assert' is needed to force the compiler to not remove the call to
+ // `accumulatePure`. It really want to do that while optimizing as the function is
+ // `pure` and it does not influence the result of this hook.
+
+ // `accumulatePure` returns the value of `size`, which can never be zero due to the
+ // previous 'if'. So this assert will never be triggered.
+ assert(0);
+ }
+ }
+ return Hook(parameters);
+ }
+ else
+ assert(0, errorMessage);
+}
+
+/**
+ * Check if the function `F` is calleable in a `nothrow` scope.
+ * Params:
+ * F = Function that does not take any parameters
+ * Returns:
+ * if the function is callable in a `nothrow` scope.
+ */
+enum isNoThrow(alias F) = is(typeof(() nothrow { F(); }));
+
+/**
+ * Check if the type `T`'s postblit is called in nothrow, if it exist
+ * Params:
+ * T = Type to check
+ * Returns:
+ * if the postblit is callable in a `nothrow` scope, if it exist.
+ * if it does not exist, return true.
+ */
+template isPostblitNoThrow(T) {
+ static if (__traits(isStaticArray, T))
+ enum isPostblitNoThrow = isPostblitNoThrow!(typeof(T.init[0]));
+ else static if (__traits(hasMember, T, "__xpostblit") &&
+ // Bugzilla 14746: Check that it's the exact member of S.
+ __traits(isSame, T, __traits(parent, T.init.__xpostblit)))
+ enum isPostblitNoThrow = isNoThrow!(T.init.__xpostblit);
+ else
+ enum isPostblitNoThrow = true;
+}
diff --git a/libphobos/libdruntime/core/internal/arrayop.d b/libphobos/libdruntime/core/internal/arrayop.d
deleted file mode 100644
index 34531d8a539..00000000000
--- a/libphobos/libdruntime/core/internal/arrayop.d
+++ /dev/null
@@ -1,451 +0,0 @@
-module core.internal.arrayop;
-import core.internal.traits : Filter, Unqual;
-
-version (GNU) version = GNU_OR_LDC;
-version (LDC) version = GNU_OR_LDC;
-
-/**
- * Perform array (vector) operations and store the result in `res`. Operand
- * types and operations are passed as template arguments in Reverse Polish
- * Notation (RPN).
-
- * Operands can be slices or scalar types. The unqualified element types of all
- * slices must be `T`, scalar types must be implicitly convertible to `T`.
- *
- * Operations are encoded as strings, e.g. `"+"`, `"%"`, `"*="`. Unary
- * operations are prefixed with "u", e.g. `"u-"`, `"u~"`. Only the last
- * operation can and must be an assignment (`"="`) or op-assignment (`"op="`).
- *
- * All slice operands must have the same length as the result slice.
- *
- * Params: T[] = type of result slice
- * Args = operand types and operations in RPN
- * res = the slice in which to store the results
- * args = operand values
- *
- * Returns: the slice containing the result
- */
-T[] arrayOp(T : T[], Args...)(T[] res, Filter!(isType, Args) args) @trusted @nogc pure nothrow
-{
- enum check = opsSupported!(true, T, Filter!(not!isType, Args)); // must support all scalar ops
-
- size_t pos;
- static if (vectorizeable!(T[], Args))
- {
- alias vec = .vec!T;
- alias load = .load!(T, vec.length);
- alias store = .store!(T, vec.length);
-
- // Given that there are at most as many scalars broadcast as there are
- // operations in any `ary[] = ary[] op const op const`, it should always be
- // worthwhile to choose vector operations.
- if (res.length >= vec.length)
- {
- mixin(initScalarVecs!Args);
-
- auto n = res.length / vec.length;
- do
- {
- mixin(vectorExp!Args ~ ";");
- pos += vec.length;
- }
- while (--n);
- }
- }
- for (; pos < res.length; ++pos)
- mixin(scalarExp!Args ~ ";");
-
- return res;
-}
-
-private:
-
-// SIMD helpers
-
-version (DigitalMars)
-{
- import core.simd;
-
- template vec(T)
- {
- enum regsz = 16; // SSE2
- enum N = regsz / T.sizeof;
- alias vec = __vector(T[N]);
- }
-
- void store(T, size_t N)(T* p, in __vector(T[N]) val)
- {
- pragma(inline, true);
- alias vec = __vector(T[N]);
-
- static if (is(T == float))
- cast(void) __simd_sto(XMM.STOUPS, *cast(vec*) p, val);
- else static if (is(T == double))
- cast(void) __simd_sto(XMM.STOUPD, *cast(vec*) p, val);
- else
- cast(void) __simd_sto(XMM.STODQU, *cast(vec*) p, val);
- }
-
- const(__vector(T[N])) load(T, size_t N)(in T* p)
- {
- import core.simd;
-
- pragma(inline, true);
- alias vec = __vector(T[N]);
-
- static if (is(T == float))
- return __simd(XMM.LODUPS, *cast(const vec*) p);
- else static if (is(T == double))
- return __simd(XMM.LODUPD, *cast(const vec*) p);
- else
- return __simd(XMM.LODDQU, *cast(const vec*) p);
- }
-
- __vector(T[N]) binop(string op, T, size_t N)(in __vector(T[N]) a, in __vector(T[N]) b)
- {
- pragma(inline, true);
- return mixin("a " ~ op ~ " b");
- }
-
- __vector(T[N]) unaop(string op, T, size_t N)(in __vector(T[N]) a)
- if (op[0] == 'u')
- {
- pragma(inline, true);
- return mixin(op[1 .. $] ~ "a");
- }
-}
-
-// mixin gen
-
-// Check whether operations `ops` are supported for type `T`. Fails with a human-friendly static assert message, if `fail` is true.
-template opsSupported(bool fail, T, ops...) if (ops.length > 1)
-{
- enum opsSupported = opsSupported!(fail, T, ops[0 .. $ / 2])
- && opsSupported!(fail, T, ops[$ / 2 .. $]);
-}
-
-template opsSupported(bool fail, T, string op)
-{
- static if (isUnaryOp(op))
- {
- enum opsSupported = is(typeof((T a) => mixin(op[1 .. $] ~ " a")));
- static assert(!fail || opsSupported,
- "Unary op `" ~ op[1 .. $] ~ "` not supported for element type " ~ T.stringof ~ ".");
- }
- else
- {
- enum opsSupported = is(typeof((T a, T b) => mixin("a " ~ op ~ " b")));
- static assert(!fail || opsSupported,
- "Binary op `" ~ op ~ "` not supported for element type " ~ T.stringof ~ ".");
- }
-}
-
-// check whether slices have the unqualified element type `E` and scalars are implicitly convertible to `E`
-// i.e. filter out things like float[] = float[] / size_t[]
-enum compatibleVecTypes(E, T : T[]) = is(Unqual!T == Unqual!E); // array elem types must be same (maybe add cvtpi2ps)
-enum compatibleVecTypes(E, T) = is(T : E); // scalar must be convertible to target elem type
-enum compatibleVecTypes(E, Types...) = compatibleVecTypes!(E, Types[0 .. $ / 2])
- && compatibleVecTypes!(E, Types[$ / 2 .. $]);
-
-version (GNU_OR_LDC)
-{
- // leave it to the auto-vectorizer
- enum vectorizeable(E : E[], Args...) = false;
-}
-else
-{
- // check whether arrayOp is vectorizable
- template vectorizeable(E : E[], Args...)
- {
- static if (is(vec!E))
- enum vectorizeable = opsSupported!(false, vec!E, Filter!(not!isType, Args))
- && compatibleVecTypes!(E, Filter!(isType, Args));
- else
- enum vectorizeable = false;
- }
-
- version (X86_64) unittest
- {
- static assert(vectorizeable!(double[], const(double)[], double[], "+", "="));
- static assert(!vectorizeable!(double[], const(ulong)[], double[], "+", "="));
- }
-}
-
-bool isUnaryOp(string op)
-{
- return op[0] == 'u';
-}
-
-bool isBinaryOp(string op)
-{
- if (op == "^^")
- return true;
- if (op.length != 1)
- return false;
- switch (op[0])
- {
- case '+', '-', '*', '/', '%', '|', '&', '^':
- return true;
- default:
- return false;
- }
-}
-
-bool isBinaryAssignOp(string op)
-{
- return op.length >= 2 && op[$ - 1] == '=' && isBinaryOp(op[0 .. $ - 1]);
-}
-
-// Generate mixin expression to perform scalar arrayOp loop expression, assumes
-// `pos` to be the current slice index, `args` to contain operand values, and
-// `res` the target slice.
-string scalarExp(Args...)()
-{
- string[] stack;
- size_t argsIdx;
- foreach (i, arg; Args)
- {
- static if (is(arg == T[], T))
- stack ~= "args[" ~ argsIdx++.toString ~ "][pos]";
- else static if (is(arg))
- stack ~= "args[" ~ argsIdx++.toString ~ "]";
- else static if (isUnaryOp(arg))
- {
- auto op = arg[0] == 'u' ? arg[1 .. $] : arg;
- stack[$ - 1] = op ~ stack[$ - 1];
- }
- else static if (arg == "=")
- {
- stack[$ - 1] = "res[pos] = cast(T)(" ~ stack[$ - 1] ~ ")";
- }
- else static if (isBinaryAssignOp(arg))
- {
- stack[$ - 1] = "res[pos] " ~ arg ~ " cast(T)(" ~ stack[$ - 1] ~ ")";
- }
- else static if (isBinaryOp(arg))
- {
- stack[$ - 2] = "(cast(T)(" ~ stack[$ - 2] ~ " " ~ arg ~ " " ~ stack[$ - 1] ~ "))";
- stack.length -= 1;
- }
- else
- assert(0, "Unexpected op " ~ arg);
- }
- assert(stack.length == 1);
- return stack[0];
-}
-
-// Generate mixin statement to perform vector loop initialization, assumes
-// `args` to contain operand values.
-string initScalarVecs(Args...)()
-{
- size_t scalarsIdx;
- string res;
- foreach (aidx, arg; Args)
- {
- static if (is(arg == T[], T))
- {
- }
- else static if (is(arg))
- res ~= "immutable vec scalar" ~ scalarsIdx++.toString ~ " = args["
- ~ aidx.toString ~ "];\n";
- }
- return res;
-}
-
-// Generate mixin expression to perform vector arrayOp loop expression, assumes
-// `pos` to be the current slice index, `args` to contain operand values, and
-// `res` the target slice.
-string vectorExp(Args...)()
-{
- size_t scalarsIdx, argsIdx;
- string[] stack;
- foreach (i, arg; Args)
- {
- static if (is(arg == T[], T))
- stack ~= "load(&args[" ~ argsIdx++.toString ~ "][pos])";
- else static if (is(arg))
- {
- ++argsIdx;
- stack ~= "scalar" ~ scalarsIdx++.toString;
- }
- else static if (isUnaryOp(arg))
- {
- auto op = arg[0] == 'u' ? arg[1 .. $] : arg;
- stack[$ - 1] = "unaop!\"" ~ arg ~ "\"(" ~ stack[$ - 1] ~ ")";
- }
- else static if (arg == "=")
- {
- stack[$ - 1] = "store(&res[pos], " ~ stack[$ - 1] ~ ")";
- }
- else static if (isBinaryAssignOp(arg))
- {
- stack[$ - 1] = "store(&res[pos], binop!\"" ~ arg[0 .. $ - 1]
- ~ "\"(load(&res[pos]), " ~ stack[$ - 1] ~ "))";
- }
- else static if (isBinaryOp(arg))
- {
- stack[$ - 2] = "binop!\"" ~ arg ~ "\"(" ~ stack[$ - 2] ~ ", " ~ stack[$ - 1] ~ ")";
- stack.length -= 1;
- }
- else
- assert(0, "Unexpected op " ~ arg);
- }
- assert(stack.length == 1);
- return stack[0];
-}
-
-// other helpers
-
-enum isType(T) = true;
-enum isType(alias a) = false;
-template not(alias tmlp)
-{
- enum not(Args...) = !tmlp!Args;
-}
-
-string toString(size_t num)
-{
- import core.internal.string : unsignedToTempString;
-
- char[20] buf = void;
- return unsignedToTempString(num, buf).idup;
-}
-
-bool contains(T)(in T[] ary, in T[] vals...)
-{
- foreach (v1; ary)
- foreach (v2; vals)
- if (v1 == v2)
- return true;
- return false;
-}
-
-// tests
-
-version (unittest) template TT(T...)
-{
- alias TT = T;
-}
-
-version (unittest) template _arrayOp(Args...)
-{
- alias _arrayOp = arrayOp!Args;
-}
-
-unittest
-{
- static void check(string op, TA, TB, T, size_t N)(TA a, TB b, in ref T[N] exp)
- {
- T[N] res;
- _arrayOp!(T[], TA, TB, op, "=")(res[], a, b);
- foreach (i; 0 .. N)
- assert(res[i] == exp[i]);
- }
-
- static void check2(string unaOp, string binOp, TA, TB, T, size_t N)(TA a, TB b, in ref T[N] exp)
- {
- T[N] res;
- _arrayOp!(T[], TA, TB, unaOp, binOp, "=")(res[], a, b);
- foreach (i; 0 .. N)
- assert(res[i] == exp[i]);
- }
-
- static void test(T, string op, size_t N = 16)(T a, T b, T exp)
- {
- T[N] va = a, vb = b, vexp = exp;
-
- check!op(va[], vb[], vexp);
- check!op(va[], b, vexp);
- check!op(a, vb[], vexp);
- }
-
- static void test2(T, string unaOp, string binOp, size_t N = 16)(T a, T b, T exp)
- {
- T[N] va = a, vb = b, vexp = exp;
-
- check2!(unaOp, binOp)(va[], vb[], vexp);
- check2!(unaOp, binOp)(va[], b, vexp);
- check2!(unaOp, binOp)(a, vb[], vexp);
- }
-
- alias UINTS = TT!(ubyte, ushort, uint, ulong);
- alias INTS = TT!(byte, short, int, long);
- alias FLOATS = TT!(float, double);
-
- foreach (T; TT!(UINTS, INTS, FLOATS))
- {
- test!(T, "+")(1, 2, 3);
- test!(T, "-")(3, 2, 1);
- static if (__traits(compiles, { import std.math; }))
- test!(T, "^^")(2, 3, 8);
-
- test2!(T, "u-", "+")(3, 2, 1);
- }
-
- foreach (T; TT!(UINTS, INTS))
- {
- test!(T, "|")(1, 2, 3);
- test!(T, "&")(3, 1, 1);
- test!(T, "^")(3, 1, 2);
-
- test2!(T, "u~", "+")(3, cast(T)~2, 5);
- }
-
- foreach (T; TT!(INTS, FLOATS))
- {
- test!(T, "-")(1, 2, -1);
- test2!(T, "u-", "+")(-3, -2, -1);
- test2!(T, "u-", "*")(-3, -2, -6);
- }
-
- foreach (T; TT!(UINTS, INTS, FLOATS))
- {
- test!(T, "*")(2, 3, 6);
- test!(T, "/")(8, 4, 2);
- test!(T, "%")(8, 6, 2);
- }
-}
-
-// test handling of v op= exp
-unittest
-{
- uint[32] c;
- arrayOp!(uint[], uint, "+=")(c[], 2);
- foreach (v; c)
- assert(v == 2);
- static if (__traits(compiles, { import std.math; }))
- {
- arrayOp!(uint[], uint, "^^=")(c[], 3);
- foreach (v; c)
- assert(v == 8);
- }
-}
-
-// proper error message for UDT lacking certain ops
-unittest
-{
- static assert(!is(typeof(&arrayOp!(int[4][], int[4], "+="))));
- static assert(!is(typeof(&arrayOp!(int[4][], int[4], "u-", "="))));
-
- static struct S
- {
- }
-
- static assert(!is(typeof(&arrayOp!(S[], S, "+="))));
- static assert(!is(typeof(&arrayOp!(S[], S[], "*", S, "+="))));
- static struct S2
- {
- S2 opBinary(string op)(in S2) @nogc pure nothrow
- {
- return this;
- }
-
- ref S2 opOpAssign(string op)(in S2) @nogc pure nothrow
- {
- return this;
- }
- }
-
- static assert(is(typeof(&arrayOp!(S2[], S2[], S2[], S2, "*", "+", "="))));
- static assert(is(typeof(&arrayOp!(S2[], S2[], S2, "*", "+="))));
-}
diff --git a/libphobos/libdruntime/core/internal/atomic.d b/libphobos/libdruntime/core/internal/atomic.d
new file mode 100644
index 00000000000..3036ea72d15
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/atomic.d
@@ -0,0 +1,1141 @@
+/**
+* The core.internal.atomic module comtains the low-level atomic features available in hardware.
+* This module may be a routing layer for compiler intrinsics.
+*
+* Copyright: Copyright Manu Evans 2019.
+* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+* Authors: Sean Kelly, Alex Rønne Petersen, Manu Evans
+* Source: $(DRUNTIMESRC core/internal/_atomic.d)
+*/
+
+module core.internal.atomic;
+
+import core.atomic : MemoryOrder, has128BitCAS;
+
+version (DigitalMars)
+{
+ private
+ {
+ enum : int
+ {
+ AX, BX, CX, DX, DI, SI, R8, R9
+ }
+
+ immutable string[4][8] registerNames = [
+ [ "AL", "AX", "EAX", "RAX" ],
+ [ "BL", "BX", "EBX", "RBX" ],
+ [ "CL", "CX", "ECX", "RCX" ],
+ [ "DL", "DX", "EDX", "RDX" ],
+ [ "DIL", "DI", "EDI", "RDI" ],
+ [ "SIL", "SI", "ESI", "RSI" ],
+ [ "R8B", "R8W", "R8D", "R8" ],
+ [ "R9B", "R9W", "R9D", "R9" ],
+ ];
+
+ template RegIndex(T)
+ {
+ static if (T.sizeof == 1)
+ enum RegIndex = 0;
+ else static if (T.sizeof == 2)
+ enum RegIndex = 1;
+ else static if (T.sizeof == 4)
+ enum RegIndex = 2;
+ else static if (T.sizeof == 8)
+ enum RegIndex = 3;
+ else
+ static assert(false, "Invalid type");
+ }
+
+ enum SizedReg(int reg, T = size_t) = registerNames[reg][RegIndex!T];
+ }
+
+ inout(T) atomicLoad(MemoryOrder order = MemoryOrder.seq, T)(inout(T)* src) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ static assert(order != MemoryOrder.rel, "invalid MemoryOrder for atomicLoad()");
+
+ static if (T.sizeof == size_t.sizeof * 2)
+ {
+ version (D_InlineAsm_X86)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ push EDI;
+ push EBX;
+ mov EBX, 0;
+ mov ECX, 0;
+ mov EAX, 0;
+ mov EDX, 0;
+ mov EDI, src;
+ lock; cmpxchg8b [EDI];
+ pop EBX;
+ pop EDI;
+ }
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ version (Windows)
+ {
+ static if (RegisterReturn!T)
+ {
+ enum SrcPtr = SizedReg!CX;
+ enum RetPtr = null;
+ }
+ else
+ {
+ enum SrcPtr = SizedReg!DX;
+ enum RetPtr = SizedReg!CX;
+ }
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ push RBX;
+ mov R8, %0;
+ ?1 mov R9, %1;
+ mov RBX, 0;
+ mov RCX, 0;
+ mov RAX, 0;
+ mov RDX, 0;
+ lock; cmpxchg16b [R8];
+ ?1 mov [R9], RAX;
+ ?1 mov 8[R9], RDX;
+ pop RBX;
+ ret;
+ }
+ }, SrcPtr, RetPtr));
+ }
+ else
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ push RBX;
+ mov RBX, 0;
+ mov RCX, 0;
+ mov RAX, 0;
+ mov RDX, 0;
+ lock; cmpxchg16b [RDI];
+ pop RBX;
+ ret;
+ }
+ }
+ }
+ }
+ else static if (needsLoadBarrier!order)
+ {
+ version (D_InlineAsm_X86)
+ {
+ enum SrcReg = SizedReg!CX;
+ enum ZeroReg = SizedReg!(DX, T);
+ enum ResReg = SizedReg!(AX, T);
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ mov %1, 0;
+ mov %2, 0;
+ mov %0, src;
+ lock; cmpxchg [%0], %1;
+ }
+ }, SrcReg, ZeroReg, ResReg));
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ version (Windows)
+ enum SrcReg = SizedReg!CX;
+ else
+ enum SrcReg = SizedReg!DI;
+ enum ZeroReg = SizedReg!(DX, T);
+ enum ResReg = SizedReg!(AX, T);
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ mov %1, 0;
+ mov %2, 0;
+ lock; cmpxchg [%0], %1;
+ ret;
+ }
+ }, SrcReg, ZeroReg, ResReg));
+ }
+ }
+ else
+ return *src;
+ }
+
+ void atomicStore(MemoryOrder order = MemoryOrder.seq, T)(T* dest, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ static assert(order != MemoryOrder.acq, "Invalid MemoryOrder for atomicStore()");
+
+ static if (T.sizeof == size_t.sizeof * 2)
+ {
+ version (D_InlineAsm_X86)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ push EDI;
+ push EBX;
+ lea EDI, value;
+ mov EBX, [EDI];
+ mov ECX, 4[EDI];
+ mov EDI, dest;
+ mov EAX, [EDI];
+ mov EDX, 4[EDI];
+ L1: lock; cmpxchg8b [EDI];
+ jne L1;
+ pop EBX;
+ pop EDI;
+ }
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ version (Windows)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ push RBX;
+ mov R8, RDX;
+ mov RAX, [RDX];
+ mov RDX, 8[RDX];
+ mov RBX, [RCX];
+ mov RCX, 8[RCX];
+ L1: lock; cmpxchg16b [R8];
+ jne L1;
+ pop RBX;
+ ret;
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ push RBX;
+ mov RBX, RDI;
+ mov RCX, RSI;
+ mov RDI, RDX;
+ mov RAX, [RDX];
+ mov RDX, 8[RDX];
+ L1: lock; cmpxchg16b [RDI];
+ jne L1;
+ pop RBX;
+ ret;
+ }
+ }
+ }
+ }
+ else static if (needsStoreBarrier!order)
+ atomicExchange!(order, false)(dest, value);
+ else
+ *dest = value;
+ }
+
+ T atomicFetchAdd(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) pure nothrow @nogc @trusted
+ if (is(T : ulong))
+ {
+ version (D_InlineAsm_X86)
+ {
+ static assert(T.sizeof <= 4, "64bit atomicFetchAdd not supported on 32bit target." );
+
+ enum DestReg = SizedReg!DX;
+ enum ValReg = SizedReg!(AX, T);
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ mov %1, value;
+ mov %0, dest;
+ lock; xadd[%0], %1;
+ }
+ }, DestReg, ValReg));
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ version (Windows)
+ {
+ enum DestReg = SizedReg!DX;
+ enum ValReg = SizedReg!(CX, T);
+ }
+ else
+ {
+ enum DestReg = SizedReg!SI;
+ enum ValReg = SizedReg!(DI, T);
+ }
+ enum ResReg = result ? SizedReg!(AX, T) : null;
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ lock; xadd[%0], %1;
+ ?2 mov %2, %1;
+ ret;
+ }
+ }, DestReg, ValReg, ResReg));
+ }
+ else
+ static assert (false, "Unsupported architecture.");
+ }
+
+ T atomicFetchSub(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) pure nothrow @nogc @trusted
+ if (is(T : ulong))
+ {
+ return atomicFetchAdd(dest, cast(T)-cast(IntOrLong!T)value);
+ }
+
+ T atomicExchange(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ version (D_InlineAsm_X86)
+ {
+ static assert(T.sizeof <= 4, "64bit atomicExchange not supported on 32bit target." );
+
+ enum DestReg = SizedReg!CX;
+ enum ValReg = SizedReg!(AX, T);
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ mov %1, value;
+ mov %0, dest;
+ xchg [%0], %1;
+ }
+ }, DestReg, ValReg));
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ version (Windows)
+ {
+ enum DestReg = SizedReg!DX;
+ enum ValReg = SizedReg!(CX, T);
+ }
+ else
+ {
+ enum DestReg = SizedReg!SI;
+ enum ValReg = SizedReg!(DI, T);
+ }
+ enum ResReg = result ? SizedReg!(AX, T) : null;
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ xchg [%0], %1;
+ ?2 mov %2, %1;
+ ret;
+ }
+ }, DestReg, ValReg, ResReg));
+ }
+ else
+ static assert (false, "Unsupported architecture.");
+ }
+
+ alias atomicCompareExchangeWeak = atomicCompareExchangeStrong;
+
+ bool atomicCompareExchangeStrong(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T)(T* dest, T* compare, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ version (D_InlineAsm_X86)
+ {
+ static if (T.sizeof <= 4)
+ {
+ enum DestAddr = SizedReg!CX;
+ enum CmpAddr = SizedReg!DI;
+ enum Val = SizedReg!(DX, T);
+ enum Cmp = SizedReg!(AX, T);
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ push %1;
+ mov %2, value;
+ mov %1, compare;
+ mov %3, [%1];
+ mov %0, dest;
+ lock; cmpxchg [%0], %2;
+ mov [%1], %3;
+ setz AL;
+ pop %1;
+ }
+ }, DestAddr, CmpAddr, Val, Cmp));
+ }
+ else static if (T.sizeof == 8)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ push EDI;
+ push EBX;
+ lea EDI, value;
+ mov EBX, [EDI];
+ mov ECX, 4[EDI];
+ mov EDI, compare;
+ mov EAX, [EDI];
+ mov EDX, 4[EDI];
+ mov EDI, dest;
+ lock; cmpxchg8b [EDI];
+ mov EDI, compare;
+ mov [EDI], EAX;
+ mov 4[EDI], EDX;
+ setz AL;
+ pop EBX;
+ pop EDI;
+ }
+ }
+ else
+ static assert(T.sizeof <= 8, "128bit atomicCompareExchangeStrong not supported on 32bit target." );
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ static if (T.sizeof <= 8)
+ {
+ version (Windows)
+ {
+ enum DestAddr = SizedReg!R8;
+ enum CmpAddr = SizedReg!DX;
+ enum Val = SizedReg!(CX, T);
+ }
+ else
+ {
+ enum DestAddr = SizedReg!DX;
+ enum CmpAddr = SizedReg!SI;
+ enum Val = SizedReg!(DI, T);
+ }
+ enum Res = SizedReg!(AX, T);
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ mov %3, [%1];
+ lock; cmpxchg [%0], %2;
+ jne compare_fail;
+ mov AL, 1;
+ ret;
+ compare_fail:
+ mov [%1], %3;
+ xor AL, AL;
+ ret;
+ }
+ }, DestAddr, CmpAddr, Val, Res));
+ }
+ else
+ {
+ version (Windows)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ push RBX;
+ mov R9, RDX;
+ mov RAX, [RDX];
+ mov RDX, 8[RDX];
+ mov RBX, [RCX];
+ mov RCX, 8[RCX];
+ lock; cmpxchg16b [R8];
+ pop RBX;
+ jne compare_fail;
+ mov AL, 1;
+ ret;
+ compare_fail:
+ mov [R9], RAX;
+ mov 8[R9], RDX;
+ xor AL, AL;
+ ret;
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ push RBX;
+ mov R8, RCX;
+ mov R9, RDX;
+ mov RAX, [RDX];
+ mov RDX, 8[RDX];
+ mov RBX, RDI;
+ mov RCX, RSI;
+ lock; cmpxchg16b [R8];
+ pop RBX;
+ jne compare_fail;
+ mov AL, 1;
+ ret;
+ compare_fail:
+ mov [R9], RAX;
+ mov 8[R9], RDX;
+ xor AL, AL;
+ ret;
+ }
+ }
+ }
+ }
+ else
+ static assert (false, "Unsupported architecture.");
+ }
+
+ alias atomicCompareExchangeWeakNoResult = atomicCompareExchangeStrongNoResult;
+
+ bool atomicCompareExchangeStrongNoResult(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T)(T* dest, const T compare, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ version (D_InlineAsm_X86)
+ {
+ static if (T.sizeof <= 4)
+ {
+ enum DestAddr = SizedReg!CX;
+ enum Cmp = SizedReg!(AX, T);
+ enum Val = SizedReg!(DX, T);
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ mov %2, value;
+ mov %1, compare;
+ mov %0, dest;
+ lock; cmpxchg [%0], %2;
+ setz AL;
+ }
+ }, DestAddr, Cmp, Val));
+ }
+ else static if (T.sizeof == 8)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ push EDI;
+ push EBX;
+ lea EDI, value;
+ mov EBX, [EDI];
+ mov ECX, 4[EDI];
+ lea EDI, compare;
+ mov EAX, [EDI];
+ mov EDX, 4[EDI];
+ mov EDI, dest;
+ lock; cmpxchg8b [EDI];
+ setz AL;
+ pop EBX;
+ pop EDI;
+ }
+ }
+ else
+ static assert(T.sizeof <= 8, "128bit atomicCompareExchangeStrong not supported on 32bit target." );
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ static if (T.sizeof <= 8)
+ {
+ version (Windows)
+ {
+ enum DestAddr = SizedReg!R8;
+ enum Cmp = SizedReg!(DX, T);
+ enum Val = SizedReg!(CX, T);
+ }
+ else
+ {
+ enum DestAddr = SizedReg!DX;
+ enum Cmp = SizedReg!(SI, T);
+ enum Val = SizedReg!(DI, T);
+ }
+ enum AXReg = SizedReg!(AX, T);
+
+ mixin (simpleFormat(q{
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ mov %3, %1;
+ lock; cmpxchg [%0], %2;
+ setz AL;
+ ret;
+ }
+ }, DestAddr, Cmp, Val, AXReg));
+ }
+ else
+ {
+ version (Windows)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ push RBX;
+ mov RAX, [RDX];
+ mov RDX, 8[RDX];
+ mov RBX, [RCX];
+ mov RCX, 8[RCX];
+ lock; cmpxchg16b [R8];
+ setz AL;
+ pop RBX;
+ ret;
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ push RBX;
+ mov RAX, RDX;
+ mov RDX, RCX;
+ mov RBX, RDI;
+ mov RCX, RSI;
+ lock; cmpxchg16b [R8];
+ setz AL;
+ pop RBX;
+ ret;
+ }
+ }
+ }
+ }
+ else
+ static assert (false, "Unsupported architecture.");
+ }
+
+ void atomicFence(MemoryOrder order = MemoryOrder.seq)() pure nothrow @nogc @trusted
+ {
+ // TODO: `mfence` should only be required for seq_cst operations, but this depends on
+ // the compiler's backend knowledge to not reorder code inappropriately,
+ // so we'll apply it conservatively.
+ static if (order != MemoryOrder.raw)
+ {
+ version (D_InlineAsm_X86)
+ {
+ import core.cpuid;
+
+ // TODO: review this implementation; it seems way overly complicated
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+
+ call sse2;
+ test AL, AL;
+ jne Lcpuid;
+
+ // Fast path: We have SSE2, so just use mfence.
+ mfence;
+ jmp Lend;
+
+ Lcpuid:
+
+ // Slow path: We use cpuid to serialize. This is
+ // significantly slower than mfence, but is the
+ // only serialization facility we have available
+ // on older non-SSE2 chips.
+ push EBX;
+
+ mov EAX, 0;
+ cpuid;
+
+ pop EBX;
+
+ Lend:
+
+ ret;
+ }
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ mfence;
+ ret;
+ }
+ }
+ else
+ static assert (false, "Unsupported architecture.");
+ }
+ }
+
+ void pause() pure nothrow @nogc @trusted
+ {
+ version (D_InlineAsm_X86)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ rep; nop;
+ ret;
+ }
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ asm pure nothrow @nogc @trusted
+ {
+ naked;
+ // pause; // TODO: DMD should add this opcode to its inline asm
+ rep; nop;
+ ret;
+ }
+ }
+ else
+ {
+ // ARM should `yield`
+ // other architectures? otherwise some sort of nop...
+ }
+ }
+}
+else version (GNU)
+{
+ import gcc.builtins;
+ import gcc.config;
+
+ inout(T) atomicLoad(MemoryOrder order = MemoryOrder.seq, T)(inout(T)* src) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ static assert(order != MemoryOrder.rel, "invalid MemoryOrder for atomicLoad()");
+
+ static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
+ {
+ static if (T.sizeof == ubyte.sizeof)
+ {
+ ubyte value = __atomic_load_1(cast(shared)src, order);
+ return *cast(typeof(return)*)&value;
+ }
+ else static if (T.sizeof == ushort.sizeof)
+ {
+ ushort value = __atomic_load_2(cast(shared)src, order);
+ return *cast(typeof(return)*)&value;
+ }
+ else static if (T.sizeof == uint.sizeof)
+ {
+ uint value = __atomic_load_4(cast(shared)src, order);
+ return *cast(typeof(return)*)&value;
+ }
+ else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics)
+ {
+ ulong value = __atomic_load_8(cast(shared)src, order);
+ return *cast(typeof(return)*)&value;
+ }
+ else static if (GNU_Have_LibAtomic)
+ {
+ T value;
+ __atomic_load(T.sizeof, cast(shared)src, &value, order);
+ return *cast(typeof(return)*)&value;
+ }
+ else
+ static assert(0, "Invalid template type specified.");
+ }
+ else
+ {
+ getAtomicMutex.lock();
+ scope(exit) getAtomicMutex.unlock();
+ return *cast(typeof(return)*)&src;
+ }
+ }
+
+ void atomicStore(MemoryOrder order = MemoryOrder.seq, T)(T* dest, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ static assert(order != MemoryOrder.acq, "Invalid MemoryOrder for atomicStore()");
+
+ static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
+ {
+ static if (T.sizeof == ubyte.sizeof)
+ __atomic_store_1(cast(shared)dest, *cast(ubyte*)&value, order);
+ else static if (T.sizeof == ushort.sizeof)
+ __atomic_store_2(cast(shared)dest, *cast(ushort*)&value, order);
+ else static if (T.sizeof == uint.sizeof)
+ __atomic_store_4(cast(shared)dest, *cast(uint*)&value, order);
+ else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics)
+ __atomic_store_8(cast(shared)dest, *cast(ulong*)&value, order);
+ else static if (GNU_Have_LibAtomic)
+ __atomic_store(T.sizeof, cast(shared)dest, cast(void*)&value, order);
+ else
+ static assert(0, "Invalid template type specified.");
+ }
+ else
+ {
+ getAtomicMutex.lock();
+ *dest = value;
+ getAtomicMutex.unlock();
+ }
+ }
+
+ T atomicFetchAdd(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) pure nothrow @nogc @trusted
+ if (is(T : ulong))
+ {
+ static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
+ {
+ static if (T.sizeof == ubyte.sizeof)
+ return __atomic_fetch_add_1(cast(shared)dest, value, order);
+ else static if (T.sizeof == ushort.sizeof)
+ return __atomic_fetch_add_2(cast(shared)dest, value, order);
+ else static if (T.sizeof == uint.sizeof)
+ return __atomic_fetch_add_4(cast(shared)dest, value, order);
+ else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics)
+ return __atomic_fetch_add_8(cast(shared)dest, value, order);
+ else static if (GNU_Have_LibAtomic)
+ return __atomic_fetch_add(T.sizeof, cast(shared)dest, cast(void*)&value, order);
+ else
+ static assert(0, "Invalid template type specified.");
+ }
+ else
+ {
+ getAtomicMutex.lock();
+ scope(exit) getAtomicMutex.unlock();
+ T tmp = *dest;
+ *dest += value;
+ return tmp;
+ }
+ }
+
+ T atomicFetchSub(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) pure nothrow @nogc @trusted
+ if (is(T : ulong))
+ {
+ static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
+ {
+ static if (T.sizeof == ubyte.sizeof)
+ return __atomic_fetch_sub_1(cast(shared)dest, value, order);
+ else static if (T.sizeof == ushort.sizeof)
+ return __atomic_fetch_sub_2(cast(shared)dest, value, order);
+ else static if (T.sizeof == uint.sizeof)
+ return __atomic_fetch_sub_4(cast(shared)dest, value, order);
+ else static if (T.sizeof == ulong.sizeof && GNU_Have_64Bit_Atomics)
+ return __atomic_fetch_sub_8(cast(shared)dest, value, order);
+ else static if (GNU_Have_LibAtomic)
+ return __atomic_fetch_sub(T.sizeof, cast(shared)dest, cast(void*)&value, order);
+ else
+ static assert(0, "Invalid template type specified.");
+ }
+ else
+ {
+ getAtomicMutex.lock();
+ scope(exit) getAtomicMutex.unlock();
+ T tmp = *dest;
+ *dest -= value;
+ return tmp;
+ }
+ }
+
+ T atomicExchange(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) pure nothrow @nogc @trusted
+ if (is(T : ulong) || is(T == class) || is(T == interface) || is(T U : U*))
+ {
+ static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
+ {
+ static if (T.sizeof == byte.sizeof)
+ {
+ ubyte res = __atomic_exchange_1(cast(shared)dest, *cast(ubyte*)&value, order);
+ return *cast(typeof(return)*)&res;
+ }
+ else static if (T.sizeof == short.sizeof)
+ {
+ ushort res = __atomic_exchange_2(cast(shared)dest, *cast(ushort*)&value, order);
+ return *cast(typeof(return)*)&res;
+ }
+ else static if (T.sizeof == int.sizeof)
+ {
+ uint res = __atomic_exchange_4(cast(shared)dest, *cast(uint*)&value, order);
+ return *cast(typeof(return)*)&res;
+ }
+ else static if (T.sizeof == long.sizeof && GNU_Have_64Bit_Atomics)
+ {
+ ulong res = __atomic_exchange_8(cast(shared)dest, *cast(ulong*)&value, order);
+ return *cast(typeof(return)*)&res;
+ }
+ else static if (GNU_Have_LibAtomic)
+ {
+ T res = void;
+ __atomic_exchange(T.sizeof, cast(shared)dest, cast(void*)&value, &res, order);
+ return res;
+ }
+ else
+ static assert(0, "Invalid template type specified.");
+ }
+ else
+ {
+ getAtomicMutex.lock();
+ scope(exit) getAtomicMutex.unlock();
+
+ T res = *dest;
+ *dest = value;
+ return res;
+ }
+ }
+
+ bool atomicCompareExchangeWeak(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T)(T* dest, T* compare, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ return atomicCompareExchangeImpl!(succ, fail, true)(dest, compare, value);
+ }
+
+ bool atomicCompareExchangeStrong(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T)(T* dest, T* compare, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ return atomicCompareExchangeImpl!(succ, fail, false)(dest, compare, value);
+ }
+
+ bool atomicCompareExchangeStrongNoResult(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T)(T* dest, const T compare, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ return atomicCompareExchangeImpl!(succ, fail, false)(dest, cast(T*)&compare, value);
+ }
+
+ bool atomicCompareExchangeWeakNoResult(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, T)(T* dest, const T compare, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ return atomicCompareExchangeImpl!(succ, fail, true)(dest, cast(T*)&compare, value);
+ }
+
+ private bool atomicCompareExchangeImpl(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq, bool weak, T)(T* dest, T* compare, T value) pure nothrow @nogc @trusted
+ if (CanCAS!T)
+ {
+ bool res = void;
+
+ static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
+ {
+ static if (T.sizeof == byte.sizeof)
+ res = __atomic_compare_exchange_1(cast(shared)dest, compare, *cast(ubyte*)&value,
+ weak, succ, fail);
+ else static if (T.sizeof == short.sizeof)
+ res = __atomic_compare_exchange_2(cast(shared)dest, compare, *cast(ushort*)&value,
+ weak, succ, fail);
+ else static if (T.sizeof == int.sizeof)
+ res = __atomic_compare_exchange_4(cast(shared)dest, compare, *cast(uint*)&value,
+ weak, succ, fail);
+ else static if (T.sizeof == long.sizeof && GNU_Have_64Bit_Atomics)
+ res = __atomic_compare_exchange_8(cast(shared)dest, compare, *cast(ulong*)&value,
+ weak, succ, fail);
+ else static if (GNU_Have_LibAtomic)
+ res = __atomic_compare_exchange(T.sizeof, cast(shared)dest, compare, cast(void*)&value,
+ succ, fail);
+ else
+ static assert(0, "Invalid template type specified.");
+ }
+ else
+ {
+ static if (T.sizeof == byte.sizeof)
+ alias U = byte;
+ else static if (T.sizeof == short.sizeof)
+ alias U = short;
+ else static if (T.sizeof == int.sizeof)
+ alias U = int;
+ else static if (T.sizeof == long.sizeof)
+ alias U = long;
+ else
+ static assert(0, "Invalid template type specified.");
+
+ getAtomicMutex.lock();
+ scope(exit) getAtomicMutex.unlock();
+
+ if (*cast(U*)dest == *cast(U*)&compare)
+ {
+ *dest = value;
+ res = true;
+ }
+ else
+ {
+ *compare = *dest;
+ res = false;
+ }
+ }
+
+ return res;
+ }
+
+ void atomicFence(MemoryOrder order = MemoryOrder.seq)() pure nothrow @nogc @trusted
+ {
+ static if (GNU_Have_Atomics || GNU_Have_LibAtomic)
+ __atomic_thread_fence(order);
+ else
+ {
+ getAtomicMutex.lock();
+ getAtomicMutex.unlock();
+ }
+ }
+
+ void pause() pure nothrow @nogc @trusted
+ {
+ version (X86)
+ {
+ __builtin_ia32_pause();
+ }
+ else version (X86_64)
+ {
+ __builtin_ia32_pause();
+ }
+ else
+ {
+ // Other architectures? Some sort of nop or barrier.
+ }
+ }
+
+ static if (!GNU_Have_Atomics && !GNU_Have_LibAtomic)
+ {
+ // Use system mutex for atomics, faking the purity of the functions so
+ // that they can be used in pure/nothrow/@safe code.
+ extern (C) private pure @trusted @nogc nothrow
+ {
+ static if (GNU_Thread_Model == ThreadModel.Posix)
+ {
+ import core.sys.posix.pthread;
+ alias atomicMutexHandle = pthread_mutex_t;
+
+ pragma(mangle, "pthread_mutex_init") int fakePureMutexInit(pthread_mutex_t*, pthread_mutexattr_t*);
+ pragma(mangle, "pthread_mutex_lock") int fakePureMutexLock(pthread_mutex_t*);
+ pragma(mangle, "pthread_mutex_unlock") int fakePureMutexUnlock(pthread_mutex_t*);
+ }
+ else static if (GNU_Thread_Model == ThreadModel.Win32)
+ {
+ import core.sys.windows.winbase;
+ alias atomicMutexHandle = CRITICAL_SECTION;
+
+ pragma(mangle, "InitializeCriticalSection") int fakePureMutexInit(CRITICAL_SECTION*);
+ pragma(mangle, "EnterCriticalSection") void fakePureMutexLock(CRITICAL_SECTION*);
+ pragma(mangle, "LeaveCriticalSection") int fakePureMutexUnlock(CRITICAL_SECTION*);
+ }
+ else
+ {
+ alias atomicMutexHandle = int;
+ }
+ }
+
+ // Implements lock/unlock operations.
+ private struct AtomicMutex
+ {
+ int lock() pure @trusted @nogc nothrow
+ {
+ static if (GNU_Thread_Model == ThreadModel.Posix)
+ {
+ if (!_inited)
+ {
+ fakePureMutexInit(&_handle, null);
+ _inited = true;
+ }
+ return fakePureMutexLock(&_handle);
+ }
+ else
+ {
+ static if (GNU_Thread_Model == ThreadModel.Win32)
+ {
+ if (!_inited)
+ {
+ fakePureMutexInit(&_handle);
+ _inited = true;
+ }
+ fakePureMutexLock(&_handle);
+ }
+ return 0;
+ }
+ }
+
+ int unlock() pure @trusted @nogc nothrow
+ {
+ static if (GNU_Thread_Model == ThreadModel.Posix)
+ return fakePureMutexUnlock(&_handle);
+ else
+ {
+ static if (GNU_Thread_Model == ThreadModel.Win32)
+ fakePureMutexUnlock(&_handle);
+ return 0;
+ }
+ }
+
+ private:
+ atomicMutexHandle _handle;
+ bool _inited;
+ }
+
+ // Internal static mutex reference.
+ private AtomicMutex* _getAtomicMutex() @trusted @nogc nothrow
+ {
+ __gshared static AtomicMutex mutex;
+ return &mutex;
+ }
+
+ // Pure alias for _getAtomicMutex.
+ pragma(mangle, _getAtomicMutex.mangleof)
+ private AtomicMutex* getAtomicMutex() pure @trusted @nogc nothrow @property;
+ }
+}
+
+private:
+
+version (Windows)
+{
+ enum RegisterReturn(T) = is(T : U[], U) || is(T : R delegate(A), R, A...);
+}
+
+enum CanCAS(T) = is(T : ulong) ||
+ is(T == class) ||
+ is(T == interface) ||
+ is(T : U*, U) ||
+ is(T : U[], U) ||
+ is(T : R delegate(A), R, A...) ||
+ (is(T == struct) && __traits(isPOD, T) &&
+ (T.sizeof <= size_t.sizeof*2 || // no more than 2 words
+ (T.sizeof == 16 && has128BitCAS)) && // or supports 128-bit CAS
+ (T.sizeof & (T.sizeof - 1)) == 0 // is power of 2
+ );
+
+template IntOrLong(T)
+{
+ static if (T.sizeof > 4)
+ alias IntOrLong = long;
+ else
+ alias IntOrLong = int;
+}
+
+// NOTE: x86 loads implicitly have acquire semantics so a memory
+// barrier is only necessary on releases.
+template needsLoadBarrier( MemoryOrder ms )
+{
+ enum bool needsLoadBarrier = ms == MemoryOrder.seq;
+}
+
+
+// NOTE: x86 stores implicitly have release semantics so a memory
+// barrier is only necessary on acquires.
+template needsStoreBarrier( MemoryOrder ms )
+{
+ enum bool needsStoreBarrier = ms == MemoryOrder.seq;
+}
+
+// this is a helper to build asm blocks
+string simpleFormat(string format, string[] args...)
+{
+ string result;
+ outer: while (format.length)
+ {
+ foreach (i; 0 .. format.length)
+ {
+ if (format[i] == '%' || format[i] == '?')
+ {
+ bool isQ = format[i] == '?';
+ result ~= format[0 .. i++];
+ assert (i < format.length, "Invalid format string");
+ if (format[i] == '%' || format[i] == '?')
+ {
+ assert(!isQ, "Invalid format string");
+ result ~= format[i++];
+ }
+ else
+ {
+ int index = 0;
+ assert (format[i] >= '0' && format[i] <= '9', "Invalid format string");
+ while (i < format.length && format[i] >= '0' && format[i] <= '9')
+ index = index * 10 + (ubyte(format[i++]) - ubyte('0'));
+ if (!isQ)
+ result ~= args[index];
+ else if (!args[index])
+ {
+ size_t j = i;
+ for (; j < format.length;)
+ {
+ if (format[j++] == '\n')
+ break;
+ }
+ i = j;
+ }
+ }
+ format = format[i .. $];
+ continue outer;
+ }
+ }
+ result ~= format;
+ break;
+ }
+ return result;
+}
diff --git a/libphobos/libdruntime/core/internal/container/array.d b/libphobos/libdruntime/core/internal/container/array.d
new file mode 100644
index 00000000000..27292cdb1c3
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/container/array.d
@@ -0,0 +1,232 @@
+/**
+ * Array container for internal usage.
+ *
+ * Copyright: Copyright Martin Nowak 2013.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Martin Nowak
+ */
+module core.internal.container.array;
+
+static import common = core.internal.container.common;
+
+import core.exception : onOutOfMemoryErrorNoGC;
+
+struct Array(T)
+{
+nothrow:
+ @disable this(this);
+
+ ~this()
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ length = 0;
+ }
+
+ @property size_t length() const
+ {
+ return _length;
+ }
+
+ @property void length(size_t nlength)
+ {
+ import core.checkedint : mulu;
+
+ bool overflow = false;
+ size_t reqsize = mulu(T.sizeof, nlength, overflow);
+ if (!overflow)
+ {
+ if (nlength < _length)
+ foreach (ref val; _ptr[nlength .. _length]) common.destroy(val);
+ _ptr = cast(T*)common.xrealloc(_ptr, reqsize);
+ if (nlength > _length)
+ foreach (ref val; _ptr[_length .. nlength]) common.initialize(val);
+ _length = nlength;
+ }
+ else
+ onOutOfMemoryErrorNoGC();
+
+ }
+
+ @property bool empty() const
+ {
+ return !length;
+ }
+
+ @property ref inout(T) front() inout
+ in { assert(!empty); }
+ do
+ {
+ return _ptr[0];
+ }
+
+ @property ref inout(T) back() inout
+ in { assert(!empty); }
+ do
+ {
+ return _ptr[_length - 1];
+ }
+
+ ref inout(T) opIndex(size_t idx) inout
+ in { assert(idx < length); }
+ do
+ {
+ return _ptr[idx];
+ }
+
+ inout(T)[] opSlice() inout
+ {
+ return _ptr[0 .. _length];
+ }
+
+ inout(T)[] opSlice(size_t a, size_t b) inout
+ in { assert(a < b && b <= length); }
+ do
+ {
+ return _ptr[a .. b];
+ }
+
+ alias length opDollar;
+
+ void insertBack()(auto ref T val)
+ {
+ import core.checkedint : addu;
+
+ bool overflow = false;
+ size_t newlength = addu(length, 1, overflow);
+ if (!overflow)
+ {
+ length = newlength;
+ back = val;
+ }
+ else
+ onOutOfMemoryErrorNoGC();
+ }
+
+ void popBack()
+ {
+ length = length - 1;
+ }
+
+ void remove(size_t idx)
+ in { assert(idx < length); }
+ do
+ {
+ foreach (i; idx .. length - 1)
+ _ptr[i] = _ptr[i+1];
+ popBack();
+ }
+
+ void swap(ref Array other)
+ {
+ auto ptr = _ptr;
+ _ptr = other._ptr;
+ other._ptr = ptr;
+ immutable len = _length;
+ _length = other._length;
+ other._length = len;
+ }
+
+ invariant
+ {
+ assert(!_ptr == !_length);
+ }
+
+private:
+ T* _ptr;
+ size_t _length;
+}
+
+unittest
+{
+ Array!size_t ary;
+
+ assert(ary[] == []);
+ ary.insertBack(5);
+ assert(ary[] == [5]);
+ assert(ary[$-1] == 5);
+ ary.popBack();
+ assert(ary[] == []);
+ ary.insertBack(0);
+ ary.insertBack(1);
+ assert(ary[] == [0, 1]);
+ assert(ary[0 .. 1] == [0]);
+ assert(ary[1 .. 2] == [1]);
+ assert(ary[$ - 2 .. $] == [0, 1]);
+ size_t idx;
+ foreach (val; ary) assert(idx++ == val);
+ foreach_reverse (val; ary) assert(--idx == val);
+ foreach (i, val; ary) assert(i == val);
+ foreach_reverse (i, val; ary) assert(i == val);
+
+ ary.insertBack(2);
+ ary.remove(1);
+ assert(ary[] == [0, 2]);
+
+ assert(!ary.empty);
+ ary.reset();
+ assert(ary.empty);
+ ary.insertBack(0);
+ assert(!ary.empty);
+ destroy(ary);
+ assert(ary.empty);
+
+ // not copyable
+ static assert(!__traits(compiles, { Array!size_t ary2 = ary; }));
+ Array!size_t ary2;
+ static assert(!__traits(compiles, ary = ary2));
+ static void foo(Array!size_t copy) {}
+ static assert(!__traits(compiles, foo(ary)));
+
+ ary2.insertBack(0);
+ assert(ary.empty);
+ assert(ary2[] == [0]);
+ ary.swap(ary2);
+ assert(ary[] == [0]);
+ assert(ary2.empty);
+}
+
+unittest
+{
+ alias RC = common.RC!();
+ Array!RC ary;
+
+ size_t cnt;
+ assert(cnt == 0);
+ ary.insertBack(RC(&cnt));
+ assert(cnt == 1);
+ ary.insertBack(RC(&cnt));
+ assert(cnt == 2);
+ ary.back = ary.front;
+ assert(cnt == 2);
+ ary.popBack();
+ assert(cnt == 1);
+ ary.popBack();
+ assert(cnt == 0);
+}
+
+unittest
+{
+ import core.exception;
+ try
+ {
+ // Overflow ary.length.
+ auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -1);
+ ary.insertBack(0);
+ }
+ catch (OutOfMemoryError)
+ {
+ }
+ try
+ {
+ // Overflow requested memory size for common.xrealloc().
+ auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -2);
+ ary.insertBack(0);
+ }
+ catch (OutOfMemoryError)
+ {
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/container/common.d b/libphobos/libdruntime/core/internal/container/common.d
new file mode 100644
index 00000000000..582d63ba2a2
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/container/common.d
@@ -0,0 +1,63 @@
+/**
+ * Common code for writing containers.
+ *
+ * Copyright: Copyright Martin Nowak 2013.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Martin Nowak
+ */
+module core.internal.container.common;
+
+import core.stdc.stdlib : malloc, realloc;
+public import core.stdc.stdlib : free;
+import core.internal.traits : dtorIsNothrow;
+nothrow:
+
+void* xrealloc(void* ptr, size_t sz) nothrow @nogc
+{
+ import core.exception;
+
+ if (!sz) { .free(ptr); return null; }
+ if (auto nptr = .realloc(ptr, sz)) return nptr;
+ .free(ptr); onOutOfMemoryErrorNoGC();
+ assert(0);
+}
+
+void* xmalloc(size_t sz) nothrow @nogc
+{
+ import core.exception;
+ if (auto nptr = .malloc(sz))
+ return nptr;
+ onOutOfMemoryErrorNoGC();
+ assert(0);
+}
+
+void destroy(T)(ref T t) if (is(T == struct) && dtorIsNothrow!T)
+{
+ scope (failure) assert(0); // nothrow hack
+ object.destroy(t);
+}
+
+void destroy(T)(ref T t) if (!is(T == struct))
+{
+ t = T.init;
+}
+
+void initialize(T)(ref T t) if (is(T == struct))
+{
+ import core.internal.lifetime : emplaceInitializer;
+ emplaceInitializer(t);
+}
+
+void initialize(T)(ref T t) if (!is(T == struct))
+{
+ t = T.init;
+}
+
+version (CoreUnittest) struct RC()
+{
+nothrow:
+ this(size_t* cnt) { ++*(_cnt = cnt); }
+ ~this() { if (_cnt) --*_cnt; }
+ this(this) { if (_cnt) ++*_cnt; }
+ size_t* _cnt;
+}
diff --git a/libphobos/libdruntime/core/internal/container/hashtab.d b/libphobos/libdruntime/core/internal/container/hashtab.d
new file mode 100644
index 00000000000..5e91193db23
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/container/hashtab.d
@@ -0,0 +1,330 @@
+/**
+ * HashTab container for internal usage.
+ *
+ * Copyright: Copyright Martin Nowak 2013.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Martin Nowak
+ */
+module core.internal.container.hashtab;
+
+import core.internal.container.array;
+static import common = core.internal.container.common;
+
+struct HashTab(Key, Value)
+{
+ static struct Node
+ {
+ Key _key;
+ Value _value;
+ Node* _next;
+ }
+
+ @disable this(this);
+
+ ~this()
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ foreach (p; _buckets)
+ {
+ while (p !is null)
+ {
+ auto pn = p._next;
+ common.destroy(*p);
+ common.free(p);
+ p = pn;
+ }
+ }
+ _buckets.reset();
+ _length = 0;
+ }
+
+ @property size_t length() const
+ {
+ return _length;
+ }
+
+ @property bool empty() const
+ {
+ return !_length;
+ }
+
+ void remove(in Key key)
+ in { assert(key in this); }
+ do
+ {
+ ensureNotInOpApply();
+
+ immutable hash = hashOf(key) & mask;
+ auto pp = &_buckets[hash];
+ while (*pp)
+ {
+ auto p = *pp;
+ if (p._key == key)
+ {
+ *pp = p._next;
+ common.destroy(*p);
+ common.free(p);
+ if (--_length < _buckets.length && _length >= 4)
+ shrink();
+ return;
+ }
+ else
+ {
+ pp = &p._next;
+ }
+ }
+ assert(0);
+ }
+
+ ref inout(Value) opIndex(Key key) inout
+ {
+ return *opBinaryRight!("in")(key);
+ }
+
+ void opIndexAssign(Value value, Key key)
+ {
+ *get(key) = value;
+ }
+
+ inout(Value)* opBinaryRight(string op)(const scope Key key) inout
+ if (op == "in")
+ {
+ if (_buckets.length)
+ {
+ immutable hash = hashOf(key) & mask;
+ for (inout(Node)* p = _buckets[hash]; p !is null; p = p._next)
+ {
+ if (p._key == key)
+ return &p._value;
+ }
+ }
+ return null;
+ }
+
+ int opApply(scope int delegate(ref Key, ref Value) dg)
+ {
+ immutable save = _inOpApply;
+ _inOpApply = true;
+ scope (exit) _inOpApply = save;
+ foreach (p; _buckets)
+ {
+ while (p !is null)
+ {
+ if (auto res = dg(p._key, p._value))
+ return res;
+ p = p._next;
+ }
+ }
+ return 0;
+ }
+
+private:
+
+ Value* get(Key key)
+ {
+ if (auto p = opBinaryRight!("in")(key))
+ return p;
+
+ ensureNotInOpApply();
+
+ if (!_buckets.length)
+ _buckets.length = 4;
+
+ immutable hash = hashOf(key) & mask;
+ auto p = cast(Node*)common.xmalloc(Node.sizeof);
+ common.initialize(*p);
+ p._key = key;
+ p._next = _buckets[hash];
+ _buckets[hash] = p;
+ if (++_length >= 2 * _buckets.length)
+ grow();
+ return &p._value;
+ }
+
+ static hash_t hashOf(const scope ref Key key) @trusted
+ {
+ static if (is(Key U : U[]))
+ return .hashOf(key, 0);
+ else
+ return .hashOf((&key)[0 .. 1], 0);
+ }
+
+ @property hash_t mask() const
+ {
+ return _buckets.length - 1;
+ }
+
+ void grow()
+ in
+ {
+ assert(_buckets.length);
+ }
+ do
+ {
+ immutable ocnt = _buckets.length;
+ immutable nmask = 2 * ocnt - 1;
+ _buckets.length = 2 * ocnt;
+ for (size_t i = 0; i < ocnt; ++i)
+ {
+ auto pp = &_buckets[i];
+ while (*pp)
+ {
+ auto p = *pp;
+
+ immutable nidx = hashOf(p._key) & nmask;
+ if (nidx != i)
+ {
+ *pp = p._next;
+ p._next = _buckets[nidx];
+ _buckets[nidx] = p;
+ }
+ else
+ {
+ pp = &p._next;
+ }
+ }
+ }
+ }
+
+ void shrink()
+ in
+ {
+ assert(_buckets.length >= 2);
+ }
+ do
+ {
+ immutable ocnt = _buckets.length;
+ immutable ncnt = ocnt >> 1;
+ immutable nmask = ncnt - 1;
+
+ for (size_t i = ncnt; i < ocnt; ++i)
+ {
+ if (auto tail = _buckets[i])
+ {
+ immutable nidx = i & nmask;
+ auto pp = &_buckets[nidx];
+ while (*pp)
+ pp = &(*pp)._next;
+ *pp = tail;
+ _buckets[i] = null;
+ }
+ }
+ _buckets.length = ncnt;
+ }
+
+ void ensureNotInOpApply()
+ {
+ if (_inOpApply)
+ assert(0, "Invalid HashTab manipulation during opApply iteration.");
+ }
+
+ Array!(Node*) _buckets;
+ size_t _length;
+ bool _inOpApply;
+}
+
+unittest
+{
+ HashTab!(int, int) tab;
+
+ foreach (i; 0 .. 100)
+ tab[i] = 100 - i;
+
+ foreach (i; 0 .. 100)
+ assert(tab[i] == 100 - i);
+
+ foreach (k, v; tab)
+ assert(v == 100 - k);
+
+ foreach (i; 0 .. 50)
+ tab.remove(2 * i);
+
+ assert(tab.length == 50);
+
+ foreach (i; 0 .. 50)
+ assert(tab[2 * i + 1] == 100 - 2 * i - 1);
+
+ assert(tab.length == 50);
+
+ tab.reset();
+ assert(tab.empty);
+ tab[0] = 0;
+ assert(!tab.empty);
+ destroy(tab);
+ assert(tab.empty);
+
+ // not copyable
+ static assert(!__traits(compiles, { HashTab!(int, int) tab2 = tab; }));
+ HashTab!(int, int) tab2;
+ static assert(!__traits(compiles, tab = tab2));
+ static void foo(HashTab!(int, int) copy) {}
+ static assert(!__traits(compiles, foo(tab)));
+}
+
+unittest
+{
+ HashTab!(string, size_t) tab;
+
+ tab["foo"] = 0;
+ assert(tab["foo"] == 0);
+ ++tab["foo"];
+ assert(tab["foo"] == 1);
+ tab["foo"]++;
+ assert(tab["foo"] == 2);
+
+ auto s = "fo";
+ s ~= "o";
+ assert(tab[s] == 2);
+ assert(tab.length == 1);
+ tab[s] -= 2;
+ assert(tab[s] == 0);
+ tab["foo"] = 12;
+ assert(tab[s] == 12);
+
+ tab.remove("foo");
+ assert(tab.empty);
+}
+
+unittest
+{
+ alias RC = common.RC!();
+ HashTab!(size_t, RC) tab;
+
+ size_t cnt;
+ assert(cnt == 0);
+ tab[0] = RC(&cnt);
+ assert(cnt == 1);
+ tab[1] = tab[0];
+ assert(cnt == 2);
+ tab.remove(0);
+ assert(cnt == 1);
+ tab.remove(1);
+ assert(cnt == 0);
+}
+
+unittest
+{
+ import core.exception;
+
+ HashTab!(uint, uint) tab;
+ foreach (i; 0 .. 5)
+ tab[i] = i;
+ bool thrown;
+ foreach (k, v; tab)
+ {
+ try
+ {
+ if (k == 3) tab.remove(k);
+ }
+ catch (AssertError e)
+ {
+ thrown = true;
+ }
+ }
+ assert(thrown);
+ assert(tab[3] == 3);
+}
diff --git a/libphobos/libdruntime/core/internal/container/treap.d b/libphobos/libdruntime/core/internal/container/treap.d
new file mode 100644
index 00000000000..1202b85a521
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/container/treap.d
@@ -0,0 +1,368 @@
+/**
+ * Treap container for internal usage.
+ *
+ * Copyright: Copyright Digital Mars 2014 - 2014.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ */
+module core.internal.container.treap;
+
+static import common = core.internal.container.common;
+import core.internal.qsort;
+
+struct Treap(E)
+{
+nothrow:
+ static struct Node
+ {
+ Node* left, right;
+ E element;
+ uint priority;
+ }
+
+ @disable this(this);
+
+ ~this()
+ {
+ removeAll();
+ }
+
+ void initialize(ulong randSeed)
+ {
+ Rand _rand = { randSeed };
+ rand = _rand;
+ }
+
+ void insert(E element) @nogc
+ {
+ root = insert(root, element);
+ }
+
+ void remove(E element)
+ {
+ remove(&root, element);
+ }
+
+ int opApply(scope int delegate(ref E) nothrow dg)
+ {
+ return (cast(const)&this).opApply((ref const E e) => dg(*cast(E*)&e));
+ }
+
+ int opApply(scope int delegate(ref const E) nothrow dg) const
+ {
+ return opApplyHelper(root, dg);
+ }
+
+ version (CoreUnittest)
+ bool opEquals(E[] elements)
+ {
+ size_t i;
+ foreach (e; this)
+ {
+ if (i >= elements.length)
+ return false;
+ if (e != elements[i++])
+ return false;
+ }
+ return i == elements.length;
+ }
+
+ void removeAll()
+ {
+ removeAll(root);
+ root = null;
+ }
+
+ version (CoreUnittest)
+ bool valid()
+ {
+ return valid(root);
+ }
+
+
+ version (none)
+ uint height()
+ {
+ static uint height(Node* node)
+ {
+ if (!node)
+ return 0;
+ auto left = height(node.left);
+ auto right = height(node.right);
+ return 1 + (left > right ? left : right);
+ }
+ return height(root);
+ }
+
+ version (none)
+ size_t count()
+ {
+ static size_t count(Node* node)
+ {
+ if (!node)
+ return 0;
+ return count(node.left) + count(node.right) + 1;
+ }
+ return count(root);
+ }
+
+
+private:
+ Node* root;
+ Rand rand;
+
+ Node* allocNode(E element) @nogc
+ {
+ Node* node = cast(Node*)common.xmalloc(Node.sizeof);
+ node.left = node.right = null;
+ node.priority = rand();
+ node.element = element;
+ return node;
+ }
+
+ Node* insert(Node* node, E element) @nogc
+ {
+ if (!node)
+ return allocNode(element);
+ else if (element < node.element)
+ {
+ node.left = insert(node.left, element);
+ if (node.left.priority < node.priority)
+ node = rotateR(node);
+ }
+ else if (element > node.element)
+ {
+ node.right = insert(node.right, element);
+ if (node.right.priority < node.priority)
+ node = rotateL(node);
+ }
+ else
+ {} // ignore duplicate
+
+ return node;
+ }
+
+static:
+
+ void freeNode(Node* node)
+ {
+ common.free(node);
+ }
+
+ Node* rotateL(Node* root)
+ {
+ auto pivot = root.right;
+ root.right = pivot.left;
+ pivot.left = root;
+ return pivot;
+ }
+
+ Node* rotateR(Node* root)
+ {
+ auto pivot = root.left;
+ root.left = pivot.right;
+ pivot.right = root;
+ return pivot;
+ }
+
+ void remove(Node** ppnode, E element)
+ {
+ Node* node = *ppnode;
+ if (!node)
+ return; // element not in treap
+
+ if (element < node.element)
+ {
+ remove(&node.left, element);
+ }
+ else if (element > node.element)
+ {
+ remove(&node.right, element);
+ }
+ else
+ {
+ while (node.left && node.right)
+ {
+ if (node.left.priority < node.right.priority)
+ {
+ *ppnode = rotateR(node);
+ ppnode = &(*ppnode).right;
+ }
+ else
+ {
+ *ppnode = rotateL(node);
+ ppnode = &(*ppnode).left;
+ }
+ }
+ if (!node.left)
+ *ppnode = node.right;
+ else
+ *ppnode = node.left;
+ freeNode(node);
+ }
+ }
+
+ void removeAll(Node* node)
+ {
+ if (!node)
+ return;
+ removeAll(node.left);
+ removeAll(node.right);
+ freeNode(node);
+ }
+
+ int opApplyHelper(const Node* node, scope int delegate(ref const E) nothrow dg)
+ {
+ if (!node)
+ return 0;
+
+ int result = opApplyHelper(node.left, dg);
+ if (result)
+ return result;
+ result = dg(node.element);
+ if (result)
+ return result;
+ return opApplyHelper(node.right, dg);
+ }
+
+ version (CoreUnittest)
+ bool valid(Node* node)
+ {
+ if (!node)
+ return true;
+
+ if (node.left)
+ {
+ if (node.left.priority < node.priority)
+ return false;
+ if (node.left.element > node.element)
+ return false;
+ }
+ if (node.right)
+ {
+ if (node.right.priority < node.priority)
+ return false;
+ if (node.right.element < node.element)
+ return false;
+ }
+ return valid(node.left) && valid(node.right);
+ }
+}
+
+unittest
+{
+ // randomized unittest for randomized data structure
+ import /*cstdlib = */core.stdc.stdlib : rand, srand;
+ import /*ctime = */core.stdc.time : time;
+
+ enum OP { add, remove }
+ enum initialSize = 1000;
+ enum randOps = 1000;
+
+ Treap!uint treap;
+ OP[] ops;
+ uint[] opdata;
+
+ srand(cast(uint)time(null));
+ treap.initialize(rand());
+
+ uint[] data;
+initialLoop:
+ foreach (i; 0 .. initialSize)
+ {
+ data ~= rand();
+ treap.insert(data[$-1]);
+ foreach (e; data[0..$-1])
+ if (e == data[$-1])
+ {
+ data = data[0..$-1];
+ continue initialLoop;
+ }
+ }
+ _adSort(*cast(void[]*)&data, typeid(data[0]));
+ assert(treap == data);
+ assert(treap.valid());
+
+ for (int i = randOps; i > 0; --i)
+ {
+ ops ~= cast(OP)(rand() < uint.max / 2 ? OP.add: OP.remove);
+ opdata ~= rand();
+ }
+
+ foreach (op; ops)
+ {
+ if (op == OP.add)
+ {
+ treap.insert(opdata[0]);
+
+ size_t i;
+ for (i = 0; i < data.length; ++i)
+ if (data[i] >= opdata[0])
+ break;
+
+ if (i == data.length || data[i] != opdata[0])
+ { // not a duplicate
+ data.length++;
+ uint tmp = opdata[0];
+ for (; i < data.length; ++i)
+ {
+ uint tmp2 = data[i];
+ data[i] = tmp;
+ tmp = tmp2;
+ }
+ }
+ }
+ else if (!data.length) // nothing to remove
+ {
+ opdata = opdata[1..$];
+ continue;
+ }
+ else
+ {
+ uint tmp = data[opdata[0]%data.length];
+ treap.remove(tmp);
+ size_t i;
+ for (i = 0; data[i] < tmp; ++i)
+ {}
+ for (; i < data.length-1; ++i)
+ data[i] = data[i+1];
+ data.length--;
+ }
+ assert(treap.valid());
+ assert(treap == data);
+ opdata = opdata[1..$];
+ }
+
+ treap.removeAll();
+ data.length = 0;
+ assert(treap == data);
+}
+
+/// Random number generators for internal usage.
+private struct Rand
+{
+ private ulong rng_state;
+
+@safe @nogc nothrow:
+pure:
+
+ auto opCall()
+ {
+ auto result = front;
+ popFront();
+ return result;
+ }
+
+ @property uint front()
+ {
+ return cast(uint)(rng_state >> 32);
+ }
+
+ void popFront()
+ {
+ immutable ulong a = 2862933555777941757;
+ immutable ulong c = 1;
+ rng_state = a * rng_state + c;
+ }
+
+ enum empty = false;
+}
diff --git a/libphobos/libdruntime/core/internal/convert.d b/libphobos/libdruntime/core/internal/convert.d
index d92204902fa..2789d2913a7 100644
--- a/libphobos/libdruntime/core/internal/convert.d
+++ b/libphobos/libdruntime/core/internal/convert.d
@@ -3,18 +3,17 @@
* This module provides functions to converting different values to const(ubyte)[]
*
* Copyright: Copyright Igor Stepanov 2013-2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Igor Stepanov
* Source: $(DRUNTIMESRC core/internal/_convert.d)
*/
module core.internal.convert;
-import core.internal.traits : Unqual;
/+
A @nogc function can allocate memory during CTFE.
+/
@nogc nothrow pure @trusted
-private ubyte[] ctfe_alloc()(size_t n)
+private ubyte[] ctfe_alloc(size_t n)
{
if (!__ctfe)
{
@@ -34,8 +33,7 @@ private ubyte[] ctfe_alloc()(size_t n)
}
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real) ||
- is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal))
+const(ubyte)[] toUbyte(T)(const scope ref T val) if (__traits(isFloating, T) && (is(T : real) || is(T : ireal)))
{
if (__ctfe)
{
@@ -84,7 +82,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqua
ubyte[] buff = ctfe_alloc(T.sizeof);
enum msbSize = double.sizeof;
- static if (is(Unqual!T == ireal))
+ static if (is(T : ireal))
double hi = toPrec!double(val.im);
else
double hi = toPrec!double(val);
@@ -101,7 +99,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqua
}
else
{
- static if (is(Unqual!T == ireal))
+ static if (is(T : ireal))
double low = toPrec!double(val.im - hi);
else
double low = toPrec!double(val - hi);
@@ -183,7 +181,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqua
}
@safe pure nothrow @nogc
-private Float parse(bool is_denormalized = false, T)(T x) if (is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal))
+private Float parse(bool is_denormalized = false, T:ireal)(T x)
{
return parse(x.im);
}
@@ -191,6 +189,7 @@ private Float parse(bool is_denormalized = false, T)(T x) if (is(Unqual!T == ifl
@safe pure nothrow @nogc
private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!T != FloatFormat.Real80)
{
+ import core.internal.traits : Unqual;
Unqual!T x = x_;
static assert(floatFormat!T != FloatFormat.DoubleDouble,
"doubledouble float format not supported in CTFE");
@@ -249,6 +248,7 @@ private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!
@safe pure nothrow @nogc
private Float parse(bool _ = false, T:real)(T x_) if (floatFormat!T == FloatFormat.Real80)
{
+ import core.internal.traits : Unqual;
Unqual!T x = x_;
//HACK @@@3632@@@
@@ -472,14 +472,14 @@ private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == Float
return Float(fl.mantissa2 & 0x00FFFFFFFFFFFFFFUL , 0, sign, 1);
}
-version (unittest)
+@system unittest
{
- private const(ubyte)[] toUbyte2(T)(T val)
+ static const(ubyte)[] toUbyte2(T)(T val)
{
return toUbyte(val).dup;
}
- private void testNumberConvert(string v)()
+ static void testNumberConvert(string v)()
{
enum ctval = mixin(v);
@@ -495,7 +495,7 @@ version (unittest)
assert(rtbytes[0..testsize] == ctbytes[0..testsize]);
}
- private void testConvert()
+ static void testConvert()
{
/**Test special values*/
testNumberConvert!("-float.infinity");
@@ -572,11 +572,6 @@ version (unittest)
testNumberConvert!("real.min_normal/19");
testNumberConvert!("real.min_normal/17");
- /**Test imaginary values: convert algorithm is same with real values*/
- testNumberConvert!("0.0Fi");
- testNumberConvert!("0.0i");
- testNumberConvert!("0.0Li");
-
/**True random values*/
testNumberConvert!("-0x9.0f7ee55df77618fp-13829L");
testNumberConvert!("0x7.36e6e2640120d28p+8797L");
@@ -605,11 +600,7 @@ version (unittest)
testNumberConvert!("cast(float)0x9.54bb0d88806f714p-7088L");
}
-
- unittest
- {
- testConvert();
- }
+ testConvert();
}
@@ -654,13 +645,13 @@ package template floatSize(T) if (is(T:real) || is(T:ireal))
// all toUbyte functions must be evaluable at compile time
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof == 1)
+const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof == 1)
{
return cast(const(ubyte)[])arr;
}
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof > 1)
+const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof > 1)
{
if (__ctfe)
{
@@ -692,7 +683,7 @@ const(ubyte)[] toUbyte(T)(const T[] arr) if (T.sizeof > 1)
}
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
+const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector))
{
static if (T.sizeof == 1)
{
@@ -709,6 +700,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (__traits(isIntegral, T) && !is(T
}
else if (__ctfe)
{
+ import core.internal.traits : Unqual;
ubyte[] tmp = ctfe_alloc(T.sizeof);
Unqual!T val_ = val;
for (size_t i = 0; i < T.sizeof; ++i)
@@ -728,7 +720,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (__traits(isIntegral, T) && !is(T
}
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == __vector))
+const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
{
if (!__ctfe)
return (cast(const ubyte*) &val)[0 .. T.sizeof];
@@ -749,8 +741,10 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == __vector))
}
}
+// @@@DEPRECATED_2022-02@@@
+deprecated
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == cfloat) || is(Unqual!T == cdouble) ||is(Unqual!T == creal))
+const(ubyte)[] toUbyte(T)(const ref return scope T val) if (__traits(isFloating, T) && is(T : creal))
{
if (__ctfe)
{
@@ -770,12 +764,12 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == cfloat) || is(Unqu
}
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == enum))
+const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == enum))
{
if (__ctfe)
{
static if (is(T V == enum)){}
- return toUbyte(cast(const V) val);
+ return toUbyte(*cast(const V*) &val);
}
else
{
@@ -789,7 +783,7 @@ nothrow pure @safe unittest
enum Month : uint { jan = 1}
Month m = Month.jan;
const bytes = toUbyte(m);
- enum ctfe_works = (() => { Month x = Month.jan; return toUbyte(x).length > 0; })();
+ enum ctfe_works = (() { Month x = Month.jan; return toUbyte(x).length > 0; })();
}
@trusted pure nothrow @nogc
@@ -807,7 +801,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V
}
@trusted pure nothrow @nogc
-const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == struct) || is(T == union))
+const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == struct) || is(T == union))
{
if (__ctfe)
{
diff --git a/libphobos/libdruntime/core/internal/dassert.d b/libphobos/libdruntime/core/internal/dassert.d
new file mode 100644
index 00000000000..ac7600f8a03
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/dassert.d
@@ -0,0 +1,590 @@
+/*
+ * Support for rich error messages generation with `assert`
+ *
+ * This module provides the `_d_assert_fail` hooks which are instantiated
+ * by the compiler whenever `-checkaction=context` is used.
+ * There are two hooks, one for unary expressions, and one for binary.
+ * When used, the compiler will rewrite `assert(a >= b)` as
+ * `assert(a >= b, _d_assert_fail!(typeof(a))(">=", a, b))`.
+ * Temporaries will be created to avoid side effects if deemed necessary
+ * by the compiler.
+ *
+ * For more information, refer to the implementation in DMD frontend
+ * for `AssertExpression`'s semantic analysis.
+ *
+ * Copyright: D Language Foundation 2018 - 2020
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/druntime/blob/master/src/core/internal/dassert.d, _dassert.d)
+ * Documentation: https://dlang.org/phobos/core_internal_dassert.html
+ */
+module core.internal.dassert;
+
+/**
+ * Generates rich assert error messages for unary expressions
+ *
+ * The unary expression `assert(!una)` will be turned into
+ * `assert(!una, _d_assert_fail("!", una))`.
+ * This routine simply acts as if the user wrote `assert(una == false)`.
+ *
+ * Params:
+ * op = Operator that was used in the expression, currently only "!"
+ * is supported.
+ * a = Result of the expression that was used in `assert` before
+ * its implicit conversion to `bool`.
+ *
+ * Returns:
+ * A string such as "$a != true" or "$a == true".
+ */
+string _d_assert_fail(A)(const scope string op, auto ref const scope A a)
+{
+ // Prevent InvalidMemoryOperationError when triggered from a finalizer
+ if (inFinalizer())
+ return "Assertion failed (rich formatting is disabled in finalizers)";
+
+ string[2] vals = [ miniFormatFakeAttributes(a), "true" ];
+ immutable token = op == "!" ? "==" : "!=";
+ return combine(vals[0 .. 1], token, vals[1 .. $]);
+}
+
+/**
+ * Generates rich assert error messages for binary expressions
+ *
+ * The binary expression `assert(x == y)` will be turned into
+ * `assert(x == y, _d_assert_fail!(typeof(x))("==", x, y))`.
+ *
+ * Params:
+ * comp = Comparison operator that was used in the expression.
+ * a = Left hand side operand (can be a tuple).
+ * b = Right hand side operand (can be a tuple).
+ *
+ * Returns:
+ * A string such as "$a $comp $b".
+ */
+template _d_assert_fail(A...)
+{
+ string _d_assert_fail(B...)(
+ const scope string comp, auto ref const scope A a, auto ref const scope B b)
+ if (B.length != 0 || A.length != 1) // Resolve ambiguity with unary overload
+ {
+ // Prevent InvalidMemoryOperationError when triggered from a finalizer
+ if (inFinalizer())
+ return "Assertion failed (rich formatting is disabled in finalizers)";
+
+ string[A.length + B.length] vals;
+ static foreach (idx; 0 .. A.length)
+ vals[idx] = miniFormatFakeAttributes(a[idx]);
+ static foreach (idx; 0 .. B.length)
+ vals[A.length + idx] = miniFormatFakeAttributes(b[idx]);
+ immutable token = invertCompToken(comp);
+ return combine(vals[0 .. A.length], token, vals[A.length .. $]);
+ }
+}
+
+/// Combines the supplied arguments into one string `"valA token valB"`
+private string combine(const scope string[] valA, const scope string token,
+ const scope string[] valB) pure nothrow @nogc @safe
+{
+ // Each separator is 2 chars (", "), plus the two spaces around the token.
+ size_t totalLen = (valA.length - 1) * 2 +
+ (valB.length - 1) * 2 + 2 + token.length;
+
+ // Empty arrays are printed as ()
+ if (valA.length == 0) totalLen += 2;
+ if (valB.length == 0) totalLen += 2;
+
+ foreach (v; valA) totalLen += v.length;
+ foreach (v; valB) totalLen += v.length;
+
+ // Include braces when printing tuples
+ const printBraces = (valA.length + valB.length) != 2;
+ if (printBraces) totalLen += 4; // '(', ')' for both tuples
+
+ char[] buffer = cast(char[]) pureAlloc(totalLen)[0 .. totalLen];
+ // @nogc-concat of "<valA> <comp> <valB>"
+ static void formatTuple (scope char[] buffer, ref size_t n, in string[] vals, in bool printBraces)
+ {
+ if (printBraces) buffer[n++] = '(';
+ foreach (idx, v; vals)
+ {
+ if (idx)
+ {
+ buffer[n++] = ',';
+ buffer[n++] = ' ';
+ }
+ buffer[n .. n + v.length] = v;
+ n += v.length;
+ }
+ if (printBraces) buffer[n++] = ')';
+ }
+
+ size_t n;
+ formatTuple(buffer, n, valA, printBraces);
+ buffer[n++] = ' ';
+ buffer[n .. n + token.length] = token;
+ n += token.length;
+ buffer[n++] = ' ';
+ formatTuple(buffer, n, valB, printBraces);
+ return (() @trusted => cast(string) buffer)();
+}
+
+/// Yields the appropriate `printf` format token for a type `T`
+private template getPrintfFormat(T)
+{
+ static if (is(T == long))
+ {
+ enum getPrintfFormat = "%lld";
+ }
+ else static if (is(T == ulong))
+ {
+ enum getPrintfFormat = "%llu";
+ }
+ else static if (__traits(isIntegral, T))
+ {
+ static if (__traits(isUnsigned, T))
+ {
+ enum getPrintfFormat = "%u";
+ }
+ else
+ {
+ enum getPrintfFormat = "%d";
+ }
+ }
+ else
+ {
+ static assert(0, "Unknown format");
+ }
+}
+
+/**
+ * Generates a textual representation of `v` without relying on Phobos.
+ * The value is formatted as follows:
+ *
+ * - primitive types and arrays yield their respective literals
+ * - pointers are printed as hexadecimal numbers
+ * - enum members are represented by their name
+ * - user-defined types are formatted by either calling `toString`
+ * if defined or printing all members, e.g. `S(1, 2)`
+ *
+ * Note that unions are rejected because this method cannot determine which
+ * member is valid when calling this method.
+ *
+ * Params:
+ * v = the value to print
+ *
+ * Returns: a string respresenting `v` or `V.stringof` if `V` is not supported
+ */
+private string miniFormat(V)(const scope ref V v)
+{
+ import core.internal.traits: isAggregateType;
+
+ /// `shared` values are formatted as their base type
+ static if (is(V == shared T, T))
+ {
+ // Use atomics to avoid race conditions whenever possible
+ static if (__traits(compiles, atomicLoad(v)))
+ {
+ if (!__ctfe)
+ {
+ T tmp = cast(T) atomicLoad(v);
+ return miniFormat(tmp);
+ }
+ }
+
+ // Fall back to a simple cast - we're violating the type system anyways
+ return miniFormat(__ctfe ? cast(const T) v : *cast(const T*) &v);
+ }
+ // Format enum members using their name
+ else static if (is(V BaseType == enum))
+ {
+ // Always generate repeated if's instead of switch to skip the detection
+ // of non-integral enums. This method doesn't need to be fast.
+ static foreach (mem; __traits(allMembers, V))
+ {
+ if (v == __traits(getMember, V, mem))
+ return mem;
+ }
+
+ // Format invalid enum values as their base type
+ enum cast_ = "cast(" ~ V.stringof ~ ")";
+ const val = miniFormat(__ctfe ? cast(const BaseType) v : *cast(const BaseType*) &v);
+ return combine([ cast_ ], "", [ val ]);
+ }
+ else static if (is(V == bool))
+ {
+ return v ? "true" : "false";
+ }
+ // Detect vectors which match isIntegral / isFloating
+ else static if (is(V == __vector(ET[N]), ET, size_t N))
+ {
+ string msg = "[";
+ foreach (i; 0 .. N)
+ {
+ if (i > 0)
+ msg ~= ", ";
+
+ msg ~= miniFormat(v[i]);
+ }
+ msg ~= "]";
+ return msg;
+ }
+ else static if (__traits(isIntegral, V))
+ {
+ static if (is(V == char))
+ {
+ // Avoid invalid code points
+ if (v < 0x7F)
+ return ['\'', v, '\''];
+
+ uint tmp = v;
+ return "cast(char) " ~ miniFormat(tmp);
+ }
+ else static if (is(V == wchar) || is(V == dchar))
+ {
+ import core.internal.utf: isValidDchar, toUTF8;
+
+ // Avoid invalid code points
+ if (isValidDchar(v))
+ return toUTF8(['\'', v, '\'']);
+
+ uint tmp = v;
+ return "cast(" ~ V.stringof ~ ") " ~ miniFormat(tmp);
+ }
+ else
+ {
+ import core.internal.string;
+ static if (__traits(isUnsigned, V))
+ const val = unsignedToTempString(v);
+ else
+ const val = signedToTempString(v);
+ return val.get().idup();
+ }
+ }
+ else static if (__traits(isFloating, V))
+ {
+ import core.stdc.stdio : sprintf;
+ import core.stdc.config : LD = c_long_double;
+
+ // No suitable replacement for sprintf in druntime ATM
+ if (__ctfe)
+ return '<' ~ V.stringof ~ " not supported>";
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=20759
+ static if (is(LD == real))
+ enum realFmt = "%Lg";
+ else
+ enum realFmt = "%g";
+
+ char[60] val;
+ int len;
+ static if (is(V == float) || is(V == double))
+ len = sprintf(&val[0], "%g", v);
+ else static if (is(V == real))
+ len = sprintf(&val[0], realFmt, cast(LD) v);
+ else static if (is(V == cfloat) || is(V == cdouble))
+ len = sprintf(&val[0], "%g + %gi", v.re, v.im);
+ else static if (is(V == creal))
+ len = sprintf(&val[0], realFmt ~ " + " ~ realFmt ~ 'i', cast(LD) v.re, cast(LD) v.im);
+ else static if (is(V == ifloat) || is(V == idouble))
+ len = sprintf(&val[0], "%gi", v);
+ else // ireal
+ {
+ static assert(is(V == ireal));
+ static if (is(LD == real))
+ alias R = ireal;
+ else
+ alias R = idouble;
+ len = sprintf(&val[0], realFmt ~ 'i', cast(R) v);
+ }
+ return val.idup[0 .. len];
+ }
+ // special-handling for void-arrays
+ else static if (is(V == typeof(null)))
+ {
+ return "`null`";
+ }
+ else static if (is(V == U*, U))
+ {
+ // Format as ulong and prepend a 0x for pointers
+ import core.internal.string;
+ return cast(immutable) ("0x" ~ unsignedToTempString!16(cast(ulong) v));
+ }
+ // toString() isn't always const, e.g. classes inheriting from Object
+ else static if (__traits(compiles, { string s = V.init.toString(); }))
+ {
+ // Object references / struct pointers may be null
+ static if (is(V == class) || is(V == interface))
+ {
+ if (v is null)
+ return "`null`";
+ }
+
+ try
+ {
+ // Prefer const overload of toString
+ static if (__traits(compiles, { string s = v.toString(); }))
+ return v.toString();
+ else
+ return (cast() v).toString();
+ }
+ catch (Exception e)
+ {
+ return `<toString() failed: "` ~ e.msg ~ `", called on ` ~ formatMembers(v) ~`>`;
+ }
+ }
+ // Static arrays or slices (but not aggregates with `alias this`)
+ else static if (is(V : U[], U) && !isAggregateType!V)
+ {
+ import core.internal.traits: Unqual;
+ alias E = Unqual!U;
+
+ // special-handling for void-arrays
+ static if (is(E == void))
+ {
+ if (__ctfe)
+ return "<void[] not supported>";
+
+ const bytes = cast(byte[]) v;
+ return miniFormat(bytes);
+ }
+ // anything string-like
+ else static if (is(E == char) || is(E == dchar) || is(E == wchar))
+ {
+ const s = `"` ~ v ~ `"`;
+
+ // v could be a char[], dchar[] or wchar[]
+ static if (is(typeof(s) : const char[]))
+ return cast(immutable) s;
+ else
+ {
+ import core.internal.utf: toUTF8;
+ return toUTF8(s);
+ }
+ }
+ else
+ {
+ string msg = "[";
+ foreach (i, ref el; v)
+ {
+ if (i > 0)
+ msg ~= ", ";
+
+ // don't fully print big arrays
+ if (i >= 30)
+ {
+ msg ~= "...";
+ break;
+ }
+ msg ~= miniFormat(el);
+ }
+ msg ~= "]";
+ return msg;
+ }
+ }
+ else static if (is(V : Val[K], K, Val))
+ {
+ size_t i;
+ string msg = "[";
+ foreach (ref k, ref val; v)
+ {
+ if (i > 0)
+ msg ~= ", ";
+ // don't fully print big AAs
+ if (i++ >= 30)
+ {
+ msg ~= "...";
+ break;
+ }
+ msg ~= miniFormat(k) ~ ": " ~ miniFormat(val);
+ }
+ msg ~= "]";
+ return msg;
+ }
+ else static if (is(V == struct))
+ {
+ return formatMembers(v);
+ }
+ // Extern C++ classes don't have a toString by default
+ else static if (is(V == class) || is(V == interface))
+ {
+ if (v is null)
+ return "null";
+
+ // Extern classes might be opaque
+ static if (is(typeof(v.tupleof)))
+ return formatMembers(v);
+ else
+ return '<' ~ V.stringof ~ '>';
+ }
+ else
+ {
+ return V.stringof;
+ }
+}
+
+/// Formats `v`'s members as `V(<member 1>, <member 2>, ...)`
+private string formatMembers(V)(const scope ref V v)
+{
+ enum ctxPtr = __traits(isNested, V);
+ enum isOverlapped = calcFieldOverlap([ v.tupleof.offsetof ]);
+
+ string msg = V.stringof ~ "(";
+ foreach (i, ref field; v.tupleof)
+ {
+ if (i > 0)
+ msg ~= ", ";
+
+ static if (isOverlapped[i])
+ {
+ msg ~= "<overlapped field>";
+ }
+ else
+ {
+ // Mark context pointer
+ static if (ctxPtr && i == v.tupleof.length - 1)
+ msg ~= "<context>: ";
+
+ msg ~= miniFormat(field);
+ }
+ }
+ msg ~= ")";
+ return msg;
+}
+
+/**
+ * Calculates whether fields are overlapped based on the passed offsets.
+ *
+ * Params:
+ * offsets = offsets of all fields matching the order of `.tupleof`
+ *
+ * Returns: an array such that arr[n] = true indicates that the n'th field
+ * overlaps with an adjacent field
+ **/
+private bool[] calcFieldOverlap(const scope size_t[] offsets)
+{
+ bool[] overlaps = new bool[](offsets.length);
+
+ foreach (const idx; 1 .. overlaps.length)
+ {
+ if (offsets[idx - 1] == offsets[idx])
+ overlaps[idx - 1] = overlaps[idx] = true;
+ }
+
+ return overlaps;
+}
+
+// This should be a local import in miniFormat but fails with a cyclic dependency error
+// core.thread.osthread -> core.time -> object -> core.internal.array.capacity
+// -> core.atomic -> core.thread -> core.thread.osthread
+import core.atomic : atomicLoad;
+
+/// Negates a comparison token, e.g. `==` is mapped to `!=`
+private string invertCompToken(scope string comp) pure nothrow @nogc @safe
+{
+ switch (comp)
+ {
+ case "==":
+ return "!=";
+ case "!=":
+ return "==";
+ case "<":
+ return ">=";
+ case "<=":
+ return ">";
+ case ">":
+ return "<=";
+ case ">=":
+ return "<";
+ case "is":
+ return "!is";
+ case "!is":
+ return "is";
+ case "in":
+ return "!in";
+ case "!in":
+ return "in";
+ default:
+ assert(0, combine(["Invalid comparison operator '"], comp, ["'"]));
+ }
+}
+
+/// Casts the function pointer to include `@safe`, `@nogc`, ...
+private auto assumeFakeAttributes(T)(T t) @trusted
+{
+ import core.internal.traits : Parameters, ReturnType;
+ alias RT = ReturnType!T;
+ alias P = Parameters!T;
+ alias type = RT function(P) nothrow @nogc @safe pure;
+ return cast(type) t;
+}
+
+/// Wrapper for `miniFormat` which assumes that the implementation is `@safe`, `@nogc`, ...
+/// s.t. it does not violate the constraints of the the function containing the `assert`.
+private string miniFormatFakeAttributes(T)(const scope ref T t)
+{
+ alias miniT = miniFormat!T;
+ return assumeFakeAttributes(&miniT)(t);
+}
+
+/// Allocates an array of `t` bytes while pretending to be `@safe`, `@nogc`, ...
+private auto pureAlloc(size_t t)
+{
+ static auto alloc(size_t len)
+ {
+ return new ubyte[len];
+ }
+ return assumeFakeAttributes(&alloc)(t);
+}
+
+/// Wrapper for GC.inFinalizer that fakes purity
+private bool inFinalizer()() pure nothrow @nogc @safe
+{
+ // CTFE doesn't trigger InvalidMemoryErrors
+ import core.memory : GC;
+ return !__ctfe && assumeFakeAttributes(&GC.inFinalizer)();
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21544
+unittest
+{
+ // Normal enum values
+ enum E { A, BCDE }
+ E e = E.A;
+ assert(miniFormat(e) == "A");
+ e = E.BCDE;
+ assert(miniFormat(e) == "BCDE");
+
+ // Invalid enum value is printed as their implicit base type (int)
+ e = cast(E) 3;
+ assert(miniFormat(e) == "cast(E) 3");
+
+ // Non-integral enums work as well
+ static struct S
+ {
+ int a;
+ string str;
+ }
+
+ enum E2 : S { a2 = S(1, "Hello") }
+ E2 es = E2.a2;
+ assert(miniFormat(es) == `a2`);
+
+ // Even invalid values
+ es = cast(E2) S(2, "World");
+ assert(miniFormat(es) == `cast(E2) S(2, "World")`);
+}
+
+// vectors
+unittest
+{
+ static if (is(__vector(float[4])))
+ {
+ __vector(float[4]) f = [-1.5f, 0.5f, 1.0f, 0.125f];
+ assert(miniFormat(f) == "[-1.5, 0.5, 1, 0.125]");
+ }
+
+ static if (is(__vector(int[4])))
+ {
+ __vector(int[4]) i = [-1, 0, 1, 3];
+ assert(miniFormat(i) == "[-1, 0, 1, 3]");
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/destruction.d b/libphobos/libdruntime/core/internal/destruction.d
new file mode 100644
index 00000000000..5c5932d99c6
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/destruction.d
@@ -0,0 +1,47 @@
+/**
+ This module contains implementations for destroying instances of types
+
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/_internal/_destruction.d)
+*/
+module core.internal.destruction;
+
+// compiler frontend lowers dynamic array deconstruction to this
+void __ArrayDtor(T)(scope T[] a)
+{
+ foreach_reverse (ref T e; a)
+ e.__xdtor();
+}
+
+public void destructRecurse(E, size_t n)(ref E[n] arr)
+{
+ import core.internal.traits : hasElaborateDestructor;
+
+ static if (hasElaborateDestructor!E)
+ {
+ foreach_reverse (ref elem; arr)
+ destructRecurse(elem);
+ }
+}
+
+public void destructRecurse(S)(ref S s)
+ if (is(S == struct))
+{
+ static if (__traits(hasMember, S, "__xdtor") &&
+ // Bugzilla 14746: Check that it's the exact member of S.
+ __traits(isSame, S, __traits(parent, s.__xdtor)))
+ s.__xdtor();
+}
+
+// Test static struct
+nothrow @safe @nogc unittest
+{
+ static int i = 0;
+ static struct S { ~this() nothrow @safe @nogc { i = 42; } }
+ S s;
+ destructRecurse(s);
+ assert(i == 42);
+}
diff --git a/libphobos/libdruntime/core/internal/entrypoint.d b/libphobos/libdruntime/core/internal/entrypoint.d
new file mode 100644
index 00000000000..839c120bfcf
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/entrypoint.d
@@ -0,0 +1,41 @@
+/**
+ This module contains the code for C main and any call(s) to initialize the
+ D runtime and call D main.
+
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/_internal/_entrypoint.d)
+*/
+module core.internal.entrypoint;
+
+/**
+A template containing C main and any call(s) to initialize druntime and
+call D main. Any module containing a D main function declaration will
+cause the compiler to generate a `mixin _d_cmain();` statement to inject
+this code into the module.
+*/
+template _d_cmain()
+{
+ extern(C)
+ {
+ int _d_run_main(int argc, char **argv, void* mainFunc);
+
+ int _Dmain(char[][] args);
+
+ int main(int argc, char **argv)
+ {
+ return _d_run_main(argc, argv, &_Dmain);
+ }
+
+ // Solaris, for unknown reasons, requires both a main() and an _main()
+ version (Solaris)
+ {
+ int _main(int argc, char** argv)
+ {
+ return main(argc, argv);
+ }
+ }
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/gc/bits.d b/libphobos/libdruntime/core/internal/gc/bits.d
new file mode 100644
index 00000000000..d50c38f0d21
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/gc/bits.d
@@ -0,0 +1,493 @@
+/**
+ * Contains a bitfield used by the GC.
+ *
+ * Copyright: D Language Foundation 2005 - 2021.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright, David Friedman, Sean Kelly
+ */
+module core.internal.gc.bits;
+
+import core.internal.gc.os : os_mem_map, os_mem_unmap, HaveFork;
+
+import core.bitop;
+import core.stdc.string;
+import core.stdc.stdlib;
+import core.exception : onOutOfMemoryError;
+
+// use version gcbitsSingleBitOperation to disable optimizations that use
+// word operands on bulk operation copyRange, setRange, clrRange, etc.
+// version = gcbitsSingleBitOperation;
+
+struct GCBits
+{
+@nogc:
+ alias size_t wordtype;
+
+ enum BITS_PER_WORD = (wordtype.sizeof * 8);
+ enum BITS_SHIFT = (wordtype.sizeof == 8 ? 6 : 5);
+ enum BITS_MASK = (BITS_PER_WORD - 1);
+ enum BITS_0 = cast(wordtype)0;
+ enum BITS_1 = cast(wordtype)1;
+ enum BITS_2 = cast(wordtype)2;
+
+ wordtype* data;
+ size_t nbits;
+
+ void Dtor(bool share = false) nothrow
+ {
+ if (data)
+ {
+ static if (!HaveFork)
+ free(data);
+ else if (share)
+ os_mem_unmap(data, nwords * data[0].sizeof);
+ else
+ free(data);
+ data = null;
+ }
+ }
+
+ void alloc(size_t nbits, bool share = false) nothrow
+ {
+ this.nbits = nbits;
+ static if (!HaveFork)
+ data = cast(typeof(data[0])*)calloc(nwords, data[0].sizeof);
+ else if (share)
+ data = cast(typeof(data[0])*)os_mem_map(nwords * data[0].sizeof, true); // Allocate as MAP_SHARED
+ else
+ data = cast(typeof(data[0])*)calloc(nwords, data[0].sizeof);
+ if (!data)
+ onOutOfMemoryError();
+ }
+
+ wordtype test(size_t i) const nothrow
+ in
+ {
+ assert(i < nbits);
+ }
+ do
+ {
+ return core.bitop.bt(data, i);
+ }
+
+ int set(size_t i) nothrow
+ in
+ {
+ assert(i < nbits);
+ }
+ do
+ {
+ return core.bitop.bts(data, i);
+ }
+
+ int clear(size_t i) nothrow
+ in
+ {
+ assert(i <= nbits);
+ }
+ do
+ {
+ return core.bitop.btr(data, i);
+ }
+
+ // return non-zero if bit already set
+ size_t setLocked(size_t i) nothrow
+ {
+ version (GNU)
+ {
+ import gcc.builtins;
+ const pos = i >> BITS_SHIFT;
+ const mask = BITS_1 << (i & BITS_MASK);
+ mixin("auto val = __atomic_fetch_or_" ~ size_t.sizeof.stringof[0]
+ ~ "(cast(shared)(data + pos), mask, 3);");
+ return (val & mask) != 0;
+ }
+ else version (LDC)
+ {
+ import ldc.intrinsics;
+ const pos = i >> BITS_SHIFT;
+ const mask = BITS_1 << (i & BITS_MASK);
+ auto val = llvm_atomic_rmw_or(cast(shared)(data + pos), mask);
+ return (val & mask) != 0;
+ }
+ else version (D_InlineAsm_X86)
+ {
+ asm @nogc nothrow {
+ mov EAX, this;
+ mov ECX, data[EAX];
+ mov EDX, i;
+ lock;
+ bts dword ptr[ECX], EDX;
+ sbb EAX,EAX;
+ }
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ asm @nogc nothrow {
+ mov RAX, this;
+ mov RAX, data[RAX];
+ mov RDX, i;
+ lock;
+ bts qword ptr[RAX], RDX;
+ sbb RAX,RAX;
+ }
+ }
+ else
+ {
+ auto pos = i >> BITS_SHIFT;
+ auto pdata = cast(shared)(data + pos);
+ auto mask = BITS_1 << (i & BITS_MASK);
+ auto state = *pdata;
+ if (state & mask)
+ return state;
+
+ import core.atomic;
+ auto newstate = state | mask;
+ while (!cas(pdata, state, newstate))
+ {
+ state = *pdata;
+ if (state & mask)
+ return state;
+ newstate = state | mask;
+ }
+ return 0;
+ }
+ }
+
+ template testAndSet(bool locked)
+ {
+ static if (locked)
+ alias testAndSet = setLocked;
+ else
+ alias testAndSet = set;
+ }
+
+
+ mixin template RangeVars()
+ {
+ size_t firstWord = (target >> BITS_SHIFT);
+ size_t firstOff = target & BITS_MASK;
+ size_t last = target + len - 1;
+ size_t lastWord = (last >> BITS_SHIFT);
+ size_t lastOff = last & BITS_MASK;
+ }
+
+ // extract loops to allow inlining the rest
+ void clearWords(size_t firstWord, size_t lastWord) nothrow
+ {
+ for (size_t w = firstWord; w < lastWord; w++)
+ data[w] = 0;
+ }
+
+ void setWords(size_t firstWord, size_t lastWord) nothrow
+ {
+ for (size_t w = firstWord; w < lastWord; w++)
+ data[w] = ~0;
+ }
+
+ void copyWords(size_t firstWord, size_t lastWord, const(wordtype)* source) nothrow
+ {
+ for (size_t w = firstWord; w < lastWord; w++)
+ data[w] = source[w - firstWord];
+ }
+
+ void copyWordsShifted(size_t firstWord, size_t cntWords, size_t firstOff, const(wordtype)* source) nothrow
+ {
+ wordtype mask = ~BITS_0 << firstOff;
+ data[firstWord] = (data[firstWord] & ~mask) | (source[0] << firstOff);
+ for (size_t w = 1; w < cntWords; w++)
+ data[firstWord + w] = (source[w - 1] >> (BITS_PER_WORD - firstOff)) | (source[w] << firstOff);
+ }
+
+ // target = the biti to start the copy to
+ // destlen = the number of bits to copy from source
+ void copyRange(size_t target, size_t len, const(wordtype)* source) nothrow
+ {
+ version (gcbitsSingleBitOperation)
+ {
+ for (size_t i = 0; i < len; i++)
+ if (source[(i >> BITS_SHIFT)] & (BITS_1 << (i & BITS_MASK)))
+ set(target+i);
+ else
+ clear(target+i);
+ }
+ else
+ {
+ if (len > 0)
+ copyRangeZ(target, len, source);
+ }
+ }
+
+ void copyRangeZ(size_t target, size_t len, const(wordtype)* source) nothrow
+ {
+ mixin RangeVars!();
+
+ if (firstWord == lastWord)
+ {
+ wordtype mask = ((BITS_2 << (lastOff - firstOff)) - 1) << firstOff;
+ data[firstWord] = (data[firstWord] & ~mask) | ((source[0] << firstOff) & mask);
+ }
+ else if (firstOff == 0)
+ {
+ copyWords(firstWord, lastWord, source);
+
+ wordtype mask = (BITS_2 << lastOff) - 1;
+ data[lastWord] = (data[lastWord] & ~mask) | (source[lastWord - firstWord] & mask);
+ }
+ else
+ {
+ size_t cntWords = lastWord - firstWord;
+ copyWordsShifted(firstWord, cntWords, firstOff, source);
+
+ wordtype src = (source[cntWords - 1] >> (BITS_PER_WORD - firstOff)) | (source[cntWords] << firstOff);
+ wordtype mask = (BITS_2 << lastOff) - 1;
+ data[lastWord] = (data[lastWord] & ~mask) | (src & mask);
+ }
+ }
+
+ void copyRangeRepeating(size_t target, size_t destlen, const(wordtype)* source, size_t sourcelen) nothrow
+ {
+ version (gcbitsSingleBitOperation)
+ {
+ for (size_t i=0; i < destlen; i++)
+ {
+ bool b;
+ size_t j = i % sourcelen;
+ b = (source[j >> BITS_SHIFT] & (BITS_1 << (j & BITS_MASK))) != 0;
+ if (b) set(target+i);
+ else clear(target+i);
+ }
+ }
+ else
+ {
+ while (destlen > sourcelen)
+ {
+ copyRange(target, sourcelen, source);
+ target += sourcelen;
+ destlen -= sourcelen;
+ }
+ copyRange(target, destlen, source);
+ }
+ }
+
+ unittest
+ {
+ // simulate broken array append test case in vibe.d
+ GCBits bits;
+ bits.alloc(10000);
+ auto data = bits.data;
+
+ GCBits src;
+ src.alloc(67);
+ src.data[0] = 0x4;
+
+ bits.copyRangeRepeating(2, 10000, src.data, 67);
+
+ foreach (i; 0 .. 10000)
+ if ((i - 2) % 67 == 2)
+ assert(bits.test(i));
+ else
+ assert(!bits.test(i));
+ }
+
+ void setRange(size_t target, size_t len) nothrow
+ {
+ version (gcbitsSingleBitOperation)
+ {
+ for (size_t i = 0; i < len; i++)
+ set(target+i);
+ }
+ else
+ {
+ if (len > 0)
+ setRangeZ(target, len);
+ }
+ }
+
+ void setRangeZ(size_t target, size_t len) nothrow
+ {
+ mixin RangeVars!();
+
+ if (firstWord == lastWord)
+ {
+ wordtype mask = ((BITS_2 << (lastOff - firstOff)) - 1) << firstOff;
+ data[firstWord] |= mask;
+ }
+ else
+ {
+ data[firstWord] |= ~BITS_0 << firstOff;
+ setWords(firstWord + 1, lastWord);
+ wordtype mask = (BITS_2 << lastOff) - 1;
+ data[lastWord] |= mask;
+ }
+ }
+
+ void clrRange(size_t target, size_t len) nothrow
+ {
+ version (gcbitsSingleBitOperation)
+ {
+ for (size_t i = 0; i < len; i++)
+ clear(target+i);
+ }
+ else
+ {
+ if (len > 0)
+ clrRangeZ(target, len);
+ }
+ }
+
+ void clrRangeZ(size_t target, size_t len) nothrow
+ {
+ mixin RangeVars!();
+ if (firstWord == lastWord)
+ {
+ wordtype mask = ((BITS_2 << (lastOff - firstOff)) - 1) << firstOff;
+ data[firstWord] &= ~mask;
+ }
+ else
+ {
+ data[firstWord] &= ~(~BITS_0 << firstOff);
+ clearWords(firstWord + 1, lastWord);
+ wordtype mask = (BITS_2 << lastOff) - 1;
+ data[lastWord] &= ~mask;
+ }
+ }
+
+ unittest
+ {
+ GCBits bits;
+ bits.alloc(1000);
+ auto data = bits.data;
+
+ bits.setRange(0,1);
+ assert(data[0] == 1);
+
+ bits.clrRange(0,1);
+ assert(data[0] == 0);
+
+ bits.setRange(BITS_PER_WORD-1,1);
+ assert(data[0] == BITS_1 << (BITS_PER_WORD-1));
+
+ bits.clrRange(BITS_PER_WORD-1,1);
+ assert(data[0] == 0);
+
+ bits.setRange(12,7);
+ assert(data[0] == 0b0111_1111_0000_0000_0000);
+
+ bits.clrRange(14,4);
+ assert(data[0] == 0b0100_0011_0000_0000_0000);
+
+ bits.clrRange(0,BITS_PER_WORD);
+ assert(data[0] == 0);
+
+ bits.setRange(0,BITS_PER_WORD);
+ assert(data[0] == ~0);
+ assert(data[1] == 0);
+
+ bits.setRange(BITS_PER_WORD,BITS_PER_WORD);
+ assert(data[0] == ~0);
+ assert(data[1] == ~0);
+ assert(data[2] == 0);
+ bits.clrRange(BITS_PER_WORD/2,BITS_PER_WORD);
+ assert(data[0] == (BITS_1 << (BITS_PER_WORD/2)) - 1);
+ assert(data[1] == ~data[0]);
+ assert(data[2] == 0);
+
+ bits.setRange(8*BITS_PER_WORD+1,4*BITS_PER_WORD-2);
+ assert(data[8] == ~0 << 1);
+ assert(data[9] == ~0);
+ assert(data[10] == ~0);
+ assert(data[11] == cast(wordtype)~0 >> 1);
+
+ bits.clrRange(9*BITS_PER_WORD+1,2*BITS_PER_WORD);
+ assert(data[8] == ~0 << 1);
+ assert(data[9] == 1);
+ assert(data[10] == 0);
+ assert(data[11] == ((cast(wordtype)~0 >> 1) & ~1));
+
+ wordtype[4] src = [ 0xa, 0x5, 0xaa, 0x55 ];
+
+ void testCopyRange(size_t start, size_t len, int repeat = 1)
+ {
+ bits.setRange(0, bits.nbits);
+ if (repeat > 1)
+ bits.copyRangeRepeating(start, repeat * len, src.ptr, len);
+ else
+ bits.copyRange(start, len, src.ptr);
+ foreach (i; 0 .. start)
+ assert(bits.test(i));
+ foreach (r; 0 .. repeat)
+ foreach (i; 0 .. len)
+ assert(!bits.test(start + r*len + i) == !core.bitop.bt(src.ptr, i));
+ foreach (i; start + repeat*len .. 10*BITS_PER_WORD)
+ assert(bits.test(i));
+ }
+
+ testCopyRange(20, 10); // short copy range within same word
+ testCopyRange(50, 20); // short copy range spanning two words
+ testCopyRange(64, 3 * BITS_PER_WORD + 3); // aligned copy range
+ testCopyRange(77, 2 * BITS_PER_WORD + 15); // unaligned copy range
+ testCopyRange(64, 127); // copy range within critical end alignment
+
+ testCopyRange(10, 4, 5); // repeating small range within same word
+ testCopyRange(20, 5, 10); // repeating small range spanning two words
+ testCopyRange(40, 21, 7); // repeating medium range
+ testCopyRange(73, 2 * BITS_PER_WORD + 15, 5); // repeating multi-word range
+
+ testCopyRange(2, 3, 166); // failed with assert
+ }
+
+ void zero() nothrow
+ {
+ memset(data, 0, nwords * wordtype.sizeof);
+ }
+
+ void setAll() nothrow
+ {
+ memset(data, 0xFF, nwords * wordtype.sizeof);
+ }
+
+ void copy(GCBits *f) nothrow
+ in
+ {
+ assert(nwords == f.nwords);
+ }
+ do
+ {
+ memcpy(data, f.data, nwords * wordtype.sizeof);
+ }
+
+ @property size_t nwords() const pure nothrow
+ {
+ return (nbits + (BITS_PER_WORD - 1)) >> BITS_SHIFT;
+ }
+}
+
+unittest
+{
+ GCBits b;
+
+ b.alloc(786);
+ assert(!b.test(123));
+ assert(!b.clear(123));
+ assert(!b.set(123));
+ assert(b.test(123));
+ assert(b.clear(123));
+ assert(!b.test(123));
+
+ b.set(785);
+ b.set(0);
+ assert(b.test(785));
+ assert(b.test(0));
+ b.zero();
+ assert(!b.test(785));
+ assert(!b.test(0));
+
+ GCBits b2;
+ b2.alloc(786);
+ b2.set(38);
+ b.copy(&b2);
+ assert(b.test(38));
+ b2.Dtor();
+ b.Dtor();
+}
diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
new file mode 100644
index 00000000000..0c49955c669
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
@@ -0,0 +1,4836 @@
+/**
+ * Contains the garbage collector implementation.
+ *
+ * Copyright: D Language Foundation 2001 - 2021.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright, David Friedman, Sean Kelly
+ */
+module core.internal.gc.impl.conservative.gc;
+
+// D Programming Language Garbage Collector implementation
+
+/************** Debugging ***************************/
+
+//debug = PRINTF; // turn on printf's
+//debug = PARALLEL_PRINTF; // turn on printf's
+//debug = COLLECT_PRINTF; // turn on printf's
+//debug = MARK_PRINTF; // turn on printf's
+//debug = PRINTF_TO_FILE; // redirect printf's ouptut to file "gcx.log"
+//debug = LOGGING; // log allocations / frees
+//debug = MEMSTOMP; // stomp on memory
+//debug = SENTINEL; // add underrun/overrrun protection
+ // NOTE: this needs to be enabled globally in the makefiles
+ // (-debug=SENTINEL) to pass druntime's unittests.
+//debug = PTRCHECK; // more pointer checking
+//debug = PTRCHECK2; // thorough but slow pointer checking
+//debug = INVARIANT; // enable invariants
+//debug = PROFILE_API; // profile API calls for config.profile > 1
+//debug = GC_RECURSIVE_LOCK; // check for recursive locking on the same thread
+
+/***************************************************/
+version = COLLECT_PARALLEL; // parallel scanning
+version (Posix)
+ version = COLLECT_FORK;
+
+import core.internal.gc.bits;
+import core.internal.gc.os;
+import core.gc.config;
+import core.gc.gcinterface;
+
+import core.internal.container.treap;
+
+import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
+import core.stdc.string : memcpy, memset, memmove;
+import core.bitop;
+import core.thread;
+static import core.memory;
+
+version (GNU) import gcc.builtins;
+
+debug (PRINTF_TO_FILE) import core.stdc.stdio : sprintf, fprintf, fopen, fflush, FILE;
+else import core.stdc.stdio : sprintf, printf; // needed to output profiling results
+
+import core.time;
+alias currTime = MonoTime.currTime;
+
+// Track total time spent preparing for GC,
+// marking, sweeping and recovering pages.
+__gshared Duration prepTime;
+__gshared Duration markTime;
+__gshared Duration sweepTime;
+__gshared Duration pauseTime;
+__gshared Duration maxPauseTime;
+__gshared Duration maxCollectionTime;
+__gshared size_t numCollections;
+__gshared size_t maxPoolMemory;
+
+__gshared long numMallocs;
+__gshared long numFrees;
+__gshared long numReallocs;
+__gshared long numExtends;
+__gshared long numOthers;
+__gshared long mallocTime; // using ticks instead of MonoTime for better performance
+__gshared long freeTime;
+__gshared long reallocTime;
+__gshared long extendTime;
+__gshared long otherTime;
+__gshared long lockTime;
+
+ulong bytesAllocated; // thread local counter
+
+private
+{
+ extern (C)
+ {
+ // to allow compilation of this module without access to the rt package,
+ // make these functions available from rt.lifetime
+ void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow;
+ int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, const scope void[] segment) nothrow;
+
+ // Declared as an extern instead of importing core.exception
+ // to avoid inlining - see issue 13725.
+ void onInvalidMemoryOperationError(void* pretend_sideffect = null) @trusted pure nothrow @nogc;
+ void onOutOfMemoryErrorNoGC() @trusted nothrow @nogc;
+
+ version (COLLECT_FORK)
+ version (OSX)
+ pid_t __fork() nothrow;
+ }
+
+ enum
+ {
+ OPFAIL = ~cast(size_t)0
+ }
+}
+
+alias GC gc_t;
+
+/* ============================ GC =============================== */
+
+// register GC in C constructor (_STI_)
+extern(C) pragma(crt_constructor) void _d_register_conservative_gc()
+{
+ import core.gc.registry;
+ registerGCFactory("conservative", &initialize);
+}
+
+extern(C) pragma(crt_constructor) void _d_register_precise_gc()
+{
+ import core.gc.registry;
+ registerGCFactory("precise", &initialize_precise);
+}
+
+private GC initialize()
+{
+ import core.lifetime : emplace;
+
+ auto gc = cast(ConservativeGC) cstdlib.malloc(__traits(classInstanceSize, ConservativeGC));
+ if (!gc)
+ onOutOfMemoryErrorNoGC();
+
+ return emplace(gc);
+}
+
+private GC initialize_precise()
+{
+ ConservativeGC.isPrecise = true;
+ return initialize();
+}
+
+class ConservativeGC : GC
+{
+ // For passing to debug code (not thread safe)
+ __gshared size_t line;
+ __gshared char* file;
+
+ Gcx *gcx; // implementation
+
+ import core.internal.spinlock;
+ static gcLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy);
+ static bool _inFinalizer;
+ __gshared bool isPrecise = false;
+
+ // lock GC, throw InvalidMemoryOperationError on recursive locking during finalization
+ static void lockNR() @nogc nothrow
+ {
+ if (_inFinalizer)
+ onInvalidMemoryOperationError();
+ gcLock.lock();
+ }
+
+ this()
+ {
+ //config is assumed to have already been initialized
+
+ gcx = cast(Gcx*)cstdlib.calloc(1, Gcx.sizeof);
+ if (!gcx)
+ onOutOfMemoryErrorNoGC();
+ gcx.initialize();
+
+ if (config.initReserve)
+ gcx.reserve(config.initReserve);
+ if (config.disable)
+ gcx.disabled++;
+ }
+
+
+ ~this()
+ {
+ version (linux)
+ {
+ //debug(PRINTF) printf("Thread %x ", pthread_self());
+ //debug(PRINTF) printf("GC.Dtor()\n");
+ }
+
+ if (gcx)
+ {
+ gcx.Dtor();
+ cstdlib.free(gcx);
+ gcx = null;
+ }
+ // TODO: cannot free as memory is overwritten and
+ // the monitor is still read in rt_finalize (called by destroy)
+ // cstdlib.free(cast(void*) this);
+ }
+
+
+ void enable()
+ {
+ static void go(Gcx* gcx) nothrow
+ {
+ assert(gcx.disabled > 0);
+ gcx.disabled--;
+ }
+ runLocked!(go, otherTime, numOthers)(gcx);
+ }
+
+
+ void disable()
+ {
+ static void go(Gcx* gcx) nothrow
+ {
+ gcx.disabled++;
+ }
+ runLocked!(go, otherTime, numOthers)(gcx);
+ }
+
+ debug (GC_RECURSIVE_LOCK) static bool lockedOnThisThread;
+
+ auto runLocked(alias func, Args...)(auto ref Args args)
+ {
+ debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0);
+ debug(GC_RECURSIVE_LOCK)
+ {
+ if (lockedOnThisThread)
+ onInvalidMemoryOperationError();
+ lockedOnThisThread = true;
+ }
+ lockNR();
+ scope (failure) gcLock.unlock();
+ debug(PROFILE_API) immutable tm2 = (config.profile > 1 ? currTime.ticks : 0);
+
+ static if (is(typeof(func(args)) == void))
+ func(args);
+ else
+ auto res = func(args);
+
+ debug(PROFILE_API) if (config.profile > 1)
+ lockTime += tm2 - tm;
+ gcLock.unlock();
+ debug(GC_RECURSIVE_LOCK)
+ {
+ if (!lockedOnThisThread)
+ onInvalidMemoryOperationError();
+ lockedOnThisThread = false;
+ }
+
+ static if (!is(typeof(func(args)) == void))
+ return res;
+ }
+
+
+ auto runLocked(alias func, alias time, alias count, Args...)(auto ref Args args)
+ {
+ debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0);
+ debug(GC_RECURSIVE_LOCK)
+ {
+ if (lockedOnThisThread)
+ onInvalidMemoryOperationError();
+ lockedOnThisThread = true;
+ }
+ lockNR();
+ scope (failure) gcLock.unlock();
+ debug(PROFILE_API) immutable tm2 = (config.profile > 1 ? currTime.ticks : 0);
+
+ static if (is(typeof(func(args)) == void))
+ func(args);
+ else
+ auto res = func(args);
+
+ debug(PROFILE_API) if (config.profile > 1)
+ {
+ count++;
+ immutable now = currTime.ticks;
+ lockTime += tm2 - tm;
+ time += now - tm2;
+ }
+ gcLock.unlock();
+ debug(GC_RECURSIVE_LOCK)
+ {
+ if (!lockedOnThisThread)
+ onInvalidMemoryOperationError();
+ lockedOnThisThread = false;
+ }
+
+ static if (!is(typeof(func(args)) == void))
+ return res;
+ }
+
+
+ uint getAttr(void* p) nothrow
+ {
+ if (!p)
+ {
+ return 0;
+ }
+
+ static uint go(Gcx* gcx, void* p) nothrow
+ {
+ Pool* pool = gcx.findPool(p);
+ uint oldb = 0;
+
+ if (pool)
+ {
+ p = sentinel_sub(p);
+ if (p != pool.findBase(p))
+ return 0;
+ auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
+
+ oldb = pool.getBits(biti);
+ }
+ return oldb;
+ }
+
+ return runLocked!(go, otherTime, numOthers)(gcx, p);
+ }
+
+
+ uint setAttr(void* p, uint mask) nothrow
+ {
+ if (!p)
+ {
+ return 0;
+ }
+
+ static uint go(Gcx* gcx, void* p, uint mask) nothrow
+ {
+ Pool* pool = gcx.findPool(p);
+ uint oldb = 0;
+
+ if (pool)
+ {
+ p = sentinel_sub(p);
+ if (p != pool.findBase(p))
+ return 0;
+ auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
+
+ oldb = pool.getBits(biti);
+ pool.setBits(biti, mask);
+ }
+ return oldb;
+ }
+
+ return runLocked!(go, otherTime, numOthers)(gcx, p, mask);
+ }
+
+
+ uint clrAttr(void* p, uint mask) nothrow
+ {
+ if (!p)
+ {
+ return 0;
+ }
+
+ static uint go(Gcx* gcx, void* p, uint mask) nothrow
+ {
+ Pool* pool = gcx.findPool(p);
+ uint oldb = 0;
+
+ if (pool)
+ {
+ p = sentinel_sub(p);
+ if (p != pool.findBase(p))
+ return 0;
+ auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
+
+ oldb = pool.getBits(biti);
+ pool.clrBits(biti, mask);
+ }
+ return oldb;
+ }
+
+ return runLocked!(go, otherTime, numOthers)(gcx, p, mask);
+ }
+
+
+ void *malloc(size_t size, uint bits, const TypeInfo ti) nothrow
+ {
+ if (!size)
+ {
+ return null;
+ }
+
+ size_t localAllocSize = void;
+
+ auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti);
+
+ if (!(bits & BlkAttr.NO_SCAN))
+ {
+ memset(p + size, 0, localAllocSize - size);
+ }
+
+ return p;
+ }
+
+
+ //
+ //
+ //
+ private void *mallocNoSync(size_t size, uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow
+ {
+ assert(size != 0);
+
+ debug(PRINTF)
+ printf("GC::malloc(gcx = %p, size = %d bits = %x, ti = %s)\n", gcx, size, bits, debugTypeName(ti).ptr);
+
+ assert(gcx);
+ //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self());
+
+ auto p = gcx.alloc(size + SENTINEL_EXTRA, alloc_size, bits, ti);
+ if (!p)
+ onOutOfMemoryErrorNoGC();
+
+ debug (SENTINEL)
+ {
+ p = sentinel_add(p);
+ sentinel_init(p, size);
+ alloc_size = size;
+ }
+ gcx.leakDetector.log_malloc(p, size);
+ bytesAllocated += alloc_size;
+
+ debug(PRINTF) printf(" => p = %p\n", p);
+ return p;
+ }
+
+
+ BlkInfo qalloc( size_t size, uint bits, const scope TypeInfo ti) nothrow
+ {
+
+ if (!size)
+ {
+ return BlkInfo.init;
+ }
+
+ BlkInfo retval;
+
+ retval.base = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, retval.size, ti);
+
+ if (!(bits & BlkAttr.NO_SCAN))
+ {
+ memset(retval.base + size, 0, retval.size - size);
+ }
+
+ retval.attr = bits;
+ return retval;
+ }
+
+
+ void *calloc(size_t size, uint bits, const TypeInfo ti) nothrow
+ {
+ if (!size)
+ {
+ return null;
+ }
+
+ size_t localAllocSize = void;
+
+ auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti);
+
+ memset(p, 0, size);
+ if (!(bits & BlkAttr.NO_SCAN))
+ {
+ memset(p + size, 0, localAllocSize - size);
+ }
+
+ return p;
+ }
+
+
+ void *realloc(void *p, size_t size, uint bits, const TypeInfo ti) nothrow
+ {
+ size_t localAllocSize = void;
+ auto oldp = p;
+
+ p = runLocked!(reallocNoSync, mallocTime, numMallocs)(p, size, bits, localAllocSize, ti);
+
+ if (p && !(bits & BlkAttr.NO_SCAN))
+ {
+ memset(p + size, 0, localAllocSize - size);
+ }
+
+ return p;
+ }
+
+
+ //
+ // bits will be set to the resulting bits of the new block
+ //
+ private void *reallocNoSync(void *p, size_t size, ref uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow
+ {
+ if (!size)
+ {
+ if (p)
+ freeNoSync(p);
+ alloc_size = 0;
+ return null;
+ }
+ if (!p)
+ return mallocNoSync(size, bits, alloc_size, ti);
+
+ debug(PRINTF) printf("GC::realloc(p = %p, size = %llu)\n", p, cast(ulong)size);
+
+ Pool *pool = gcx.findPool(p);
+ if (!pool)
+ return null;
+
+ size_t psize;
+ size_t biti;
+
+ debug(SENTINEL)
+ {
+ void* q = p;
+ p = sentinel_sub(p);
+ bool alwaysMalloc = true;
+ }
+ else
+ {
+ alias q = p;
+ enum alwaysMalloc = false;
+ }
+
+ void* doMalloc()
+ {
+ if (!bits)
+ bits = pool.getBits(biti);
+
+ void* p2 = mallocNoSync(size, bits, alloc_size, ti);
+ debug (SENTINEL)
+ psize = sentinel_size(q, psize);
+ if (psize < size)
+ size = psize;
+ //debug(PRINTF) printf("\tcopying %d bytes\n",size);
+ memcpy(p2, q, size);
+ freeNoSync(q);
+ return p2;
+ }
+
+ if (pool.isLargeObject)
+ {
+ auto lpool = cast(LargeObjectPool*) pool;
+ auto psz = lpool.getPages(p); // get allocated size
+ if (psz == 0)
+ return null; // interior pointer
+ psize = psz * PAGESIZE;
+
+ alias pagenum = biti; // happens to be the same, but rename for clarity
+ pagenum = lpool.pagenumOf(p);
+
+ if (size <= PAGESIZE / 2 || alwaysMalloc)
+ return doMalloc(); // switching from large object pool to small object pool
+
+ auto newsz = lpool.numPages(size);
+ if (newsz == psz)
+ {
+ // nothing to do
+ }
+ else if (newsz < psz)
+ {
+ // Shrink in place
+ debug (MEMSTOMP) memset(p + size, 0xF2, psize - size);
+ lpool.freePages(pagenum + newsz, psz - newsz);
+ lpool.mergeFreePageOffsets!(false, true)(pagenum + newsz, psz - newsz);
+ lpool.bPageOffsets[pagenum] = cast(uint) newsz;
+ }
+ else if (pagenum + newsz <= pool.npages)
+ {
+ // Attempt to expand in place (TODO: merge with extend)
+ if (lpool.pagetable[pagenum + psz] != B_FREE)
+ return doMalloc();
+
+ auto newPages = newsz - psz;
+ auto freesz = lpool.bPageOffsets[pagenum + psz];
+ if (freesz < newPages)
+ return doMalloc(); // free range too small
+
+ debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize);
+ debug (PRINTF) printFreeInfo(pool);
+ memset(&lpool.pagetable[pagenum + psz], B_PAGEPLUS, newPages);
+ lpool.bPageOffsets[pagenum] = cast(uint) newsz;
+ for (auto offset = psz; offset < newsz; offset++)
+ lpool.bPageOffsets[pagenum + offset] = cast(uint) offset;
+ if (freesz > newPages)
+ lpool.setFreePageOffsets(pagenum + newsz, freesz - newPages);
+ gcx.usedLargePages += newPages;
+ lpool.freepages -= newPages;
+ debug (PRINTF) printFreeInfo(pool);
+ }
+ else
+ return doMalloc(); // does not fit into current pool
+
+ alloc_size = newsz * PAGESIZE;
+ }
+ else
+ {
+ psize = (cast(SmallObjectPool*) pool).getSize(p); // get allocated bin size
+ if (psize == 0)
+ return null; // interior pointer
+ biti = cast(size_t)(p - pool.baseAddr) >> Pool.ShiftBy.Small;
+ if (pool.freebits.test (biti))
+ return null;
+
+ // allocate if new size is bigger or less than half
+ if (psize < size || psize > size * 2 || alwaysMalloc)
+ return doMalloc();
+
+ alloc_size = psize;
+ if (isPrecise)
+ pool.setPointerBitmapSmall(p, size, psize, bits, ti);
+ }
+
+ if (bits)
+ {
+ pool.clrBits(biti, ~BlkAttr.NONE);
+ pool.setBits(biti, bits);
+ }
+ return p;
+ }
+
+
+ size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow
+ {
+ return runLocked!(extendNoSync, extendTime, numExtends)(p, minsize, maxsize, ti);
+ }
+
+
+ //
+ //
+ //
+ private size_t extendNoSync(void* p, size_t minsize, size_t maxsize, const TypeInfo ti = null) nothrow
+ in
+ {
+ assert(minsize <= maxsize);
+ }
+ do
+ {
+ debug(PRINTF) printf("GC::extend(p = %p, minsize = %zu, maxsize = %zu)\n", p, minsize, maxsize);
+ debug (SENTINEL)
+ {
+ return 0;
+ }
+ else
+ {
+ auto pool = gcx.findPool(p);
+ if (!pool || !pool.isLargeObject)
+ return 0;
+
+ auto lpool = cast(LargeObjectPool*) pool;
+ size_t pagenum = lpool.pagenumOf(p);
+ if (lpool.pagetable[pagenum] != B_PAGE)
+ return 0;
+
+ size_t psz = lpool.bPageOffsets[pagenum];
+ assert(psz > 0);
+
+ auto minsz = lpool.numPages(minsize);
+ auto maxsz = lpool.numPages(maxsize);
+
+ if (pagenum + psz >= lpool.npages)
+ return 0;
+ if (lpool.pagetable[pagenum + psz] != B_FREE)
+ return 0;
+
+ size_t freesz = lpool.bPageOffsets[pagenum + psz];
+ if (freesz < minsz)
+ return 0;
+ size_t sz = freesz > maxsz ? maxsz : freesz;
+ debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE);
+ memset(lpool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
+ lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz);
+ for (auto offset = psz; offset < psz + sz; offset++)
+ lpool.bPageOffsets[pagenum + offset] = cast(uint) offset;
+ if (freesz > sz)
+ lpool.setFreePageOffsets(pagenum + psz + sz, freesz - sz);
+ lpool.freepages -= sz;
+ gcx.usedLargePages += sz;
+ return (psz + sz) * PAGESIZE;
+ }
+ }
+
+
+ size_t reserve(size_t size) nothrow
+ {
+ if (!size)
+ {
+ return 0;
+ }
+
+ return runLocked!(reserveNoSync, otherTime, numOthers)(size);
+ }
+
+
+ //
+ //
+ //
+ private size_t reserveNoSync(size_t size) nothrow
+ {
+ assert(size != 0);
+ assert(gcx);
+
+ return gcx.reserve(size);
+ }
+
+
+ void free(void *p) nothrow @nogc
+ {
+ if (!p || _inFinalizer)
+ {
+ return;
+ }
+
+ return runLocked!(freeNoSync, freeTime, numFrees)(p);
+ }
+
+
+ //
+ //
+ //
+ private void freeNoSync(void *p) nothrow @nogc
+ {
+ debug(PRINTF) printf("Freeing %p\n", cast(size_t) p);
+ assert (p);
+
+ Pool* pool;
+ size_t pagenum;
+ Bins bin;
+ size_t biti;
+
+ // Find which page it is in
+ pool = gcx.findPool(p);
+ if (!pool) // if not one of ours
+ return; // ignore
+
+ pagenum = pool.pagenumOf(p);
+
+ debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]);
+ debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]);
+
+ bin = cast(Bins)pool.pagetable[pagenum];
+
+ // Verify that the pointer is at the beginning of a block,
+ // no action should be taken if p is an interior pointer
+ if (bin > B_PAGE) // B_PAGEPLUS or B_FREE
+ return;
+ size_t off = (sentinel_sub(p) - pool.baseAddr);
+ size_t base = baseOffset(off, bin);
+ if (off != base)
+ return;
+
+ sentinel_Invariant(p);
+ auto q = p;
+ p = sentinel_sub(p);
+ size_t ssize;
+
+ if (pool.isLargeObject) // if large alloc
+ {
+ biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Large;
+ assert(bin == B_PAGE);
+ auto lpool = cast(LargeObjectPool*) pool;
+
+ // Free pages
+ size_t npages = lpool.bPageOffsets[pagenum];
+ auto size = npages * PAGESIZE;
+ ssize = sentinel_size(q, size);
+ debug (MEMSTOMP) memset(p, 0xF2, size);
+ lpool.freePages(pagenum, npages);
+ lpool.mergeFreePageOffsets!(true, true)(pagenum, npages);
+ }
+ else
+ {
+ biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Small;
+ if (pool.freebits.test (biti))
+ return;
+ // Add to free list
+ List *list = cast(List*)p;
+
+ auto size = binsize[bin];
+ ssize = sentinel_size(q, size);
+ debug (MEMSTOMP) memset(p, 0xF2, size);
+
+ // in case the page hasn't been recovered yet, don't add the object to the free list
+ if (!gcx.recoverPool[bin] || pool.binPageChain[pagenum] == Pool.PageRecovered)
+ {
+ list.next = gcx.bucket[bin];
+ list.pool = pool;
+ gcx.bucket[bin] = list;
+ }
+ pool.freebits.set(biti);
+ }
+ pool.clrBits(biti, ~BlkAttr.NONE);
+
+ gcx.leakDetector.log_free(sentinel_add(p), ssize);
+ }
+
+
+ void* addrOf(void *p) nothrow @nogc
+ {
+ if (!p)
+ {
+ return null;
+ }
+
+ return runLocked!(addrOfNoSync, otherTime, numOthers)(p);
+ }
+
+
+ //
+ //
+ //
+ void* addrOfNoSync(void *p) nothrow @nogc
+ {
+ if (!p)
+ {
+ return null;
+ }
+
+ auto q = gcx.findBase(p);
+ if (q)
+ q = sentinel_add(q);
+ return q;
+ }
+
+
+ size_t sizeOf(void *p) nothrow @nogc
+ {
+ if (!p)
+ {
+ return 0;
+ }
+
+ return runLocked!(sizeOfNoSync, otherTime, numOthers)(p);
+ }
+
+
+ //
+ //
+ //
+ private size_t sizeOfNoSync(void *p) nothrow @nogc
+ {
+ assert (p);
+
+ debug (SENTINEL)
+ {
+ p = sentinel_sub(p);
+ size_t size = gcx.findSize(p);
+ return size ? size - SENTINEL_EXTRA : 0;
+ }
+ else
+ {
+ size_t size = gcx.findSize(p);
+ return size;
+ }
+ }
+
+
+ BlkInfo query(void *p) nothrow
+ {
+ if (!p)
+ {
+ BlkInfo i;
+ return i;
+ }
+
+ return runLocked!(queryNoSync, otherTime, numOthers)(p);
+ }
+
+ //
+ //
+ //
+ BlkInfo queryNoSync(void *p) nothrow
+ {
+ assert(p);
+
+ BlkInfo info = gcx.getInfo(p);
+ debug(SENTINEL)
+ {
+ if (info.base)
+ {
+ info.base = sentinel_add(info.base);
+ info.size = *sentinel_psize(info.base);
+ }
+ }
+ return info;
+ }
+
+
+ /**
+ * Verify that pointer p:
+ * 1) belongs to this memory pool
+ * 2) points to the start of an allocated piece of memory
+ * 3) is not on a free list
+ */
+ void check(void *p) nothrow
+ {
+ if (!p)
+ {
+ return;
+ }
+
+ return runLocked!(checkNoSync, otherTime, numOthers)(p);
+ }
+
+
+ //
+ //
+ //
+ private void checkNoSync(void *p) nothrow
+ {
+ assert(p);
+
+ sentinel_Invariant(p);
+ debug (PTRCHECK)
+ {
+ Pool* pool;
+ size_t pagenum;
+ Bins bin;
+
+ p = sentinel_sub(p);
+ pool = gcx.findPool(p);
+ assert(pool);
+ pagenum = pool.pagenumOf(p);
+ bin = cast(Bins)pool.pagetable[pagenum];
+ assert(bin <= B_PAGE);
+ assert(p == cast(void*)baseOffset(cast(size_t)p, bin));
+
+ debug (PTRCHECK2)
+ {
+ if (bin < B_PAGE)
+ {
+ // Check that p is not on a free list
+ List *list;
+
+ for (list = gcx.bucket[bin]; list; list = list.next)
+ {
+ assert(cast(void*)list != p);
+ }
+ }
+ }
+ }
+ }
+
+
+ void addRoot(void *p) nothrow @nogc
+ {
+ if (!p)
+ {
+ return;
+ }
+
+ gcx.addRoot(p);
+ }
+
+
+ void removeRoot(void *p) nothrow @nogc
+ {
+ if (!p)
+ {
+ return;
+ }
+
+ gcx.removeRoot(p);
+ }
+
+
+ @property RootIterator rootIter() @nogc
+ {
+ return &gcx.rootsApply;
+ }
+
+
+ void addRange(void *p, size_t sz, const TypeInfo ti = null) nothrow @nogc
+ {
+ if (!p || !sz)
+ {
+ return;
+ }
+
+ gcx.addRange(p, p + sz, ti);
+ }
+
+
+ void removeRange(void *p) nothrow @nogc
+ {
+ if (!p)
+ {
+ return;
+ }
+
+ gcx.removeRange(p);
+ }
+
+
+ @property RangeIterator rangeIter() @nogc
+ {
+ return &gcx.rangesApply;
+ }
+
+
+ void runFinalizers(const scope void[] segment) nothrow
+ {
+ static void go(Gcx* gcx, const scope void[] segment) nothrow
+ {
+ gcx.runFinalizers(segment);
+ }
+ return runLocked!(go, otherTime, numOthers)(gcx, segment);
+ }
+
+
+ bool inFinalizer() nothrow @nogc
+ {
+ return _inFinalizer;
+ }
+
+
+ void collect() nothrow
+ {
+ fullCollect();
+ }
+
+
+ void collectNoStack() nothrow
+ {
+ fullCollectNoStack();
+ }
+
+
+ /**
+ * Do full garbage collection.
+ * Return number of pages free'd.
+ */
+ size_t fullCollect() nothrow
+ {
+ debug(PRINTF) printf("GC.fullCollect()\n");
+
+ // Since a finalizer could launch a new thread, we always need to lock
+ // when collecting.
+ static size_t go(Gcx* gcx) nothrow
+ {
+ return gcx.fullcollect(false, true); // standard stop the world
+ }
+ immutable result = runLocked!go(gcx);
+
+ version (none)
+ {
+ GCStats stats;
+
+ getStats(stats);
+ debug(PRINTF) printf("heapSize = %zx, freeSize = %zx\n",
+ stats.heapSize, stats.freeSize);
+ }
+
+ gcx.leakDetector.log_collect();
+ return result;
+ }
+
+
+ /**
+ * do full garbage collection ignoring roots
+ */
+ void fullCollectNoStack() nothrow
+ {
+ // Since a finalizer could launch a new thread, we always need to lock
+ // when collecting.
+ static size_t go(Gcx* gcx) nothrow
+ {
+ return gcx.fullcollect(true, true, true); // standard stop the world
+ }
+ runLocked!go(gcx);
+ }
+
+
+ void minimize() nothrow
+ {
+ static void go(Gcx* gcx) nothrow
+ {
+ gcx.minimize();
+ }
+ runLocked!(go, otherTime, numOthers)(gcx);
+ }
+
+
+ core.memory.GC.Stats stats() nothrow
+ {
+ typeof(return) ret;
+
+ runLocked!(getStatsNoSync, otherTime, numOthers)(ret);
+
+ return ret;
+ }
+
+
+ core.memory.GC.ProfileStats profileStats() nothrow @trusted
+ {
+ typeof(return) ret;
+
+ ret.numCollections = numCollections;
+ ret.totalCollectionTime = prepTime + markTime + sweepTime;
+ ret.totalPauseTime = pauseTime;
+ ret.maxCollectionTime = maxCollectionTime;
+ ret.maxPauseTime = maxPauseTime;
+
+ return ret;
+ }
+
+
+ ulong allocatedInCurrentThread() nothrow
+ {
+ return bytesAllocated;
+ }
+
+
+ //
+ //
+ //
+ private void getStatsNoSync(out core.memory.GC.Stats stats) nothrow
+ {
+ foreach (pool; gcx.pooltable[0 .. gcx.npools])
+ {
+ foreach (bin; pool.pagetable[0 .. pool.npages])
+ {
+ if (bin == B_FREE)
+ stats.freeSize += PAGESIZE;
+ else
+ stats.usedSize += PAGESIZE;
+ }
+ }
+
+ size_t freeListSize;
+ foreach (n; 0 .. B_PAGE)
+ {
+ immutable sz = binsize[n];
+ for (List *list = gcx.bucket[n]; list; list = list.next)
+ freeListSize += sz;
+
+ foreach (pool; gcx.pooltable[0 .. gcx.npools])
+ {
+ if (pool.isLargeObject)
+ continue;
+ for (uint pn = pool.recoverPageFirst[n]; pn < pool.npages; pn = pool.binPageChain[pn])
+ {
+ const bitbase = pn * PAGESIZE / 16;
+ const top = PAGESIZE - sz + 1; // ensure <size> bytes available even if unaligned
+ for (size_t u = 0; u < top; u += sz)
+ if (pool.freebits.test(bitbase + u / 16))
+ freeListSize += sz;
+ }
+ }
+ }
+
+ stats.usedSize -= freeListSize;
+ stats.freeSize += freeListSize;
+ stats.allocatedInCurrentThread = bytesAllocated;
+ }
+}
+
+
+/* ============================ Gcx =============================== */
+
+enum
+{ PAGESIZE = 4096,
+}
+
+
+enum
+{
+ B_16,
+ B_32,
+ B_48,
+ B_64,
+ B_96,
+ B_128,
+ B_176,
+ B_256,
+ B_368,
+ B_512,
+ B_816,
+ B_1024,
+ B_1360,
+ B_2048,
+ B_NUMSMALL,
+
+ B_PAGE = B_NUMSMALL,// start of large alloc
+ B_PAGEPLUS, // continuation of large alloc
+ B_FREE, // free page
+ B_MAX,
+}
+
+
+alias ubyte Bins;
+
+
+struct List
+{
+ List *next;
+ Pool *pool;
+}
+
+// non power of two sizes optimized for small remainder within page (<= 64 bytes)
+immutable short[B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ];
+immutable short[PAGESIZE / 16][B_NUMSMALL + 1] binbase = calcBinBase();
+
+short[PAGESIZE / 16][B_NUMSMALL + 1] calcBinBase()
+{
+ short[PAGESIZE / 16][B_NUMSMALL + 1] bin;
+
+ foreach (i, size; binsize)
+ {
+ short end = (PAGESIZE / size) * size;
+ short bsz = size / 16;
+ foreach (off; 0..PAGESIZE/16)
+ {
+ // add the remainder to the last bin, so no check during scanning
+ // is needed if a false pointer targets that area
+ const base = (off - off % bsz) * 16;
+ bin[i][off] = cast(short)(base < end ? base : end - size);
+ }
+ }
+ return bin;
+}
+
+size_t baseOffset(size_t offset, Bins bin) @nogc nothrow
+{
+ assert(bin <= B_PAGE);
+ return (offset & ~(PAGESIZE - 1)) + binbase[bin][(offset & (PAGESIZE - 1)) >> 4];
+}
+
+alias PageBits = GCBits.wordtype[PAGESIZE / 16 / GCBits.BITS_PER_WORD];
+static assert(PAGESIZE % (GCBits.BITS_PER_WORD * 16) == 0);
+
+// bitmask with bits set at base offsets of objects
+immutable PageBits[B_NUMSMALL] baseOffsetBits = (){
+ PageBits[B_NUMSMALL] bits;
+ foreach (bin; 0..B_NUMSMALL)
+ {
+ size_t size = binsize[bin];
+ const top = PAGESIZE - size + 1; // ensure <size> bytes available even if unaligned
+ for (size_t u = 0; u < top; u += size)
+ {
+ size_t biti = u / 16;
+ size_t off = biti / GCBits.BITS_PER_WORD;
+ size_t mod = biti % GCBits.BITS_PER_WORD;
+ bits[bin][off] |= GCBits.BITS_1 << mod;
+ }
+ }
+ return bits;
+}();
+
+private void set(ref PageBits bits, size_t i) @nogc pure nothrow
+{
+ assert(i < PageBits.sizeof * 8);
+ bts(bits.ptr, i);
+}
+
+/* ============================ Gcx =============================== */
+
+struct Gcx
+{
+ import core.internal.spinlock;
+ auto rootsLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
+ auto rangesLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
+ Treap!Root roots;
+ Treap!Range ranges;
+ bool minimizeAfterNextCollection = false;
+ version (COLLECT_FORK)
+ {
+ private pid_t markProcPid = 0;
+ bool shouldFork;
+ }
+
+ debug(INVARIANT) bool initialized;
+ debug(INVARIANT) bool inCollection;
+ uint disabled; // turn off collections if >0
+
+ import core.internal.gc.pooltable;
+ private @property size_t npools() pure const nothrow { return pooltable.length; }
+ PoolTable!Pool pooltable;
+
+ List*[B_NUMSMALL] bucket; // free list for each small size
+
+ // run a collection when reaching those thresholds (number of used pages)
+ float smallCollectThreshold, largeCollectThreshold;
+ uint usedSmallPages, usedLargePages;
+ // total number of mapped pages
+ uint mappedPages;
+
+ debug (LOGGING)
+ LeakDetector leakDetector;
+ else
+ alias leakDetector = LeakDetector;
+
+ SmallObjectPool*[B_NUMSMALL] recoverPool;
+ version (Posix) __gshared Gcx* instance;
+
+ void initialize()
+ {
+ (cast(byte*)&this)[0 .. Gcx.sizeof] = 0;
+ leakDetector.initialize(&this);
+ roots.initialize(0x243F6A8885A308D3UL);
+ ranges.initialize(0x13198A2E03707344UL);
+ smallCollectThreshold = largeCollectThreshold = 0.0f;
+ usedSmallPages = usedLargePages = 0;
+ mappedPages = 0;
+ //printf("gcx = %p, self = %x\n", &this, self);
+ version (Posix)
+ {
+ import core.sys.posix.pthread : pthread_atfork;
+ instance = &this;
+ __gshared atforkHandlersInstalled = false;
+ if (!atforkHandlersInstalled)
+ {
+ pthread_atfork(
+ &_d_gcx_atfork_prepare,
+ &_d_gcx_atfork_parent,
+ &_d_gcx_atfork_child);
+ atforkHandlersInstalled = true;
+ }
+ }
+ debug(INVARIANT) initialized = true;
+ version (COLLECT_FORK)
+ shouldFork = config.fork;
+
+ }
+
+ void Dtor()
+ {
+ if (config.profile)
+ {
+ printf("\tNumber of collections: %llu\n", cast(ulong)numCollections);
+ printf("\tTotal GC prep time: %lld milliseconds\n",
+ prepTime.total!("msecs"));
+ printf("\tTotal mark time: %lld milliseconds\n",
+ markTime.total!("msecs"));
+ printf("\tTotal sweep time: %lld milliseconds\n",
+ sweepTime.total!("msecs"));
+ long maxPause = maxPauseTime.total!("msecs");
+ printf("\tMax Pause Time: %lld milliseconds\n", maxPause);
+ long gcTime = (sweepTime + markTime + prepTime).total!("msecs");
+ printf("\tGrand total GC time: %lld milliseconds\n", gcTime);
+ long pauseTime = (markTime + prepTime).total!("msecs");
+
+ char[30] apitxt = void;
+ apitxt[0] = 0;
+ debug(PROFILE_API) if (config.profile > 1)
+ {
+ static Duration toDuration(long dur)
+ {
+ return MonoTime(dur) - MonoTime(0);
+ }
+
+ printf("\n");
+ printf("\tmalloc: %llu calls, %lld ms\n", cast(ulong)numMallocs, toDuration(mallocTime).total!"msecs");
+ printf("\trealloc: %llu calls, %lld ms\n", cast(ulong)numReallocs, toDuration(reallocTime).total!"msecs");
+ printf("\tfree: %llu calls, %lld ms\n", cast(ulong)numFrees, toDuration(freeTime).total!"msecs");
+ printf("\textend: %llu calls, %lld ms\n", cast(ulong)numExtends, toDuration(extendTime).total!"msecs");
+ printf("\tother: %llu calls, %lld ms\n", cast(ulong)numOthers, toDuration(otherTime).total!"msecs");
+ printf("\tlock time: %lld ms\n", toDuration(lockTime).total!"msecs");
+
+ long apiTime = mallocTime + reallocTime + freeTime + extendTime + otherTime + lockTime;
+ printf("\tGC API: %lld ms\n", toDuration(apiTime).total!"msecs");
+ sprintf(apitxt.ptr, " API%5ld ms", toDuration(apiTime).total!"msecs");
+ }
+
+ printf("GC summary:%5lld MB,%5lld GC%5lld ms, Pauses%5lld ms <%5lld ms%s\n",
+ cast(long) maxPoolMemory >> 20, cast(ulong)numCollections, gcTime,
+ pauseTime, maxPause, apitxt.ptr);
+ }
+
+ version (Posix)
+ instance = null;
+ version (COLLECT_PARALLEL)
+ stopScanThreads();
+
+ debug(INVARIANT) initialized = false;
+
+ for (size_t i = 0; i < npools; i++)
+ {
+ Pool *pool = pooltable[i];
+ mappedPages -= pool.npages;
+ pool.Dtor();
+ cstdlib.free(pool);
+ }
+ assert(!mappedPages);
+ pooltable.Dtor();
+
+ roots.removeAll();
+ ranges.removeAll();
+ toscanConservative.reset();
+ toscanPrecise.reset();
+ }
+
+
+ void Invariant() const { }
+
+ debug(INVARIANT)
+ invariant()
+ {
+ if (initialized)
+ {
+ //printf("Gcx.invariant(): this = %p\n", &this);
+ pooltable.Invariant();
+ for (size_t p = 0; p < pooltable.length; p++)
+ if (pooltable.pools[p].isLargeObject)
+ (cast(LargeObjectPool*)(pooltable.pools[p])).Invariant();
+ else
+ (cast(SmallObjectPool*)(pooltable.pools[p])).Invariant();
+
+ if (!inCollection)
+ (cast()rangesLock).lock();
+ foreach (range; ranges)
+ {
+ assert(range.pbot);
+ assert(range.ptop);
+ assert(range.pbot <= range.ptop);
+ }
+ if (!inCollection)
+ (cast()rangesLock).unlock();
+
+ for (size_t i = 0; i < B_NUMSMALL; i++)
+ {
+ size_t j = 0;
+ List* prev, pprev, ppprev; // keep a short history to inspect in the debugger
+ for (auto list = cast(List*)bucket[i]; list; list = list.next)
+ {
+ auto pool = list.pool;
+ auto biti = cast(size_t)(cast(void*)list - pool.baseAddr) >> Pool.ShiftBy.Small;
+ assert(pool.freebits.test(biti));
+ ppprev = pprev;
+ pprev = prev;
+ prev = list;
+ }
+ }
+ }
+ }
+
+ @property bool collectInProgress() const nothrow
+ {
+ version (COLLECT_FORK)
+ return markProcPid != 0;
+ else
+ return false;
+ }
+
+
+ /**
+ *
+ */
+ void addRoot(void *p) nothrow @nogc
+ {
+ rootsLock.lock();
+ scope (failure) rootsLock.unlock();
+ roots.insert(Root(p));
+ rootsLock.unlock();
+ }
+
+
+ /**
+ *
+ */
+ void removeRoot(void *p) nothrow @nogc
+ {
+ rootsLock.lock();
+ scope (failure) rootsLock.unlock();
+ roots.remove(Root(p));
+ rootsLock.unlock();
+ }
+
+
+ /**
+ *
+ */
+ int rootsApply(scope int delegate(ref Root) nothrow dg) nothrow
+ {
+ rootsLock.lock();
+ scope (failure) rootsLock.unlock();
+ auto ret = roots.opApply(dg);
+ rootsLock.unlock();
+ return ret;
+ }
+
+
+ /**
+ *
+ */
+ void addRange(void *pbot, void *ptop, const TypeInfo ti) nothrow @nogc
+ {
+ //debug(PRINTF) printf("Thread %x ", pthread_self());
+ debug(PRINTF) printf("%p.Gcx::addRange(%p, %p)\n", &this, pbot, ptop);
+ rangesLock.lock();
+ scope (failure) rangesLock.unlock();
+ ranges.insert(Range(pbot, ptop));
+ rangesLock.unlock();
+ }
+
+
+ /**
+ *
+ */
+ void removeRange(void *pbot) nothrow @nogc
+ {
+ //debug(PRINTF) printf("Thread %x ", pthread_self());
+ debug(PRINTF) printf("Gcx.removeRange(%p)\n", pbot);
+ rangesLock.lock();
+ scope (failure) rangesLock.unlock();
+ ranges.remove(Range(pbot, pbot)); // only pbot is used, see Range.opCmp
+ rangesLock.unlock();
+
+ // debug(PRINTF) printf("Wrong thread\n");
+ // This is a fatal error, but ignore it.
+ // The problem is that we can get a Close() call on a thread
+ // other than the one the range was allocated on.
+ //assert(zero);
+ }
+
+ /**
+ *
+ */
+ int rangesApply(scope int delegate(ref Range) nothrow dg) nothrow
+ {
+ rangesLock.lock();
+ scope (failure) rangesLock.unlock();
+ auto ret = ranges.opApply(dg);
+ rangesLock.unlock();
+ return ret;
+ }
+
+
+ /**
+ *
+ */
+ void runFinalizers(const scope void[] segment) nothrow
+ {
+ ConservativeGC._inFinalizer = true;
+ scope (failure) ConservativeGC._inFinalizer = false;
+
+ foreach (pool; pooltable[0 .. npools])
+ {
+ if (!pool.finals.nbits) continue;
+
+ if (pool.isLargeObject)
+ {
+ auto lpool = cast(LargeObjectPool*) pool;
+ lpool.runFinalizers(segment);
+ }
+ else
+ {
+ auto spool = cast(SmallObjectPool*) pool;
+ spool.runFinalizers(segment);
+ }
+ }
+ ConservativeGC._inFinalizer = false;
+ }
+
+ Pool* findPool(void* p) pure nothrow @nogc
+ {
+ return pooltable.findPool(p);
+ }
+
+ /**
+ * Find base address of block containing pointer p.
+ * Returns null if not a gc'd pointer
+ */
+ void* findBase(void *p) nothrow @nogc
+ {
+ Pool *pool;
+
+ pool = findPool(p);
+ if (pool)
+ return pool.findBase(p);
+ return null;
+ }
+
+
+ /**
+ * Find size of pointer p.
+ * Returns 0 if not a gc'd pointer
+ */
+ size_t findSize(void *p) nothrow @nogc
+ {
+ Pool* pool = findPool(p);
+ if (pool)
+ return pool.slGetSize(p);
+ return 0;
+ }
+
+ /**
+ *
+ */
+ BlkInfo getInfo(void* p) nothrow
+ {
+ Pool* pool = findPool(p);
+ if (pool)
+ return pool.slGetInfo(p);
+ return BlkInfo();
+ }
+
+ /**
+ * Computes the bin table using CTFE.
+ */
+ static byte[2049] ctfeBins() nothrow
+ {
+ byte[2049] ret;
+ size_t p = 0;
+ for (Bins b = B_16; b <= B_2048; b++)
+ for ( ; p <= binsize[b]; p++)
+ ret[p] = b;
+
+ return ret;
+ }
+
+ static const byte[2049] binTable = ctfeBins();
+
+ /**
+ * Allocate a new pool of at least size bytes.
+ * Sort it into pooltable[].
+ * Mark all memory in the pool as B_FREE.
+ * Return the actual number of bytes reserved or 0 on error.
+ */
+ size_t reserve(size_t size) nothrow
+ {
+ size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
+
+ // Assume reserve() is for small objects.
+ Pool* pool = newPool(npages, false);
+
+ if (!pool)
+ return 0;
+ return pool.npages * PAGESIZE;
+ }
+
+ /**
+ * Update the thresholds for when to collect the next time
+ */
+ void updateCollectThresholds() nothrow
+ {
+ static float max(float a, float b) nothrow
+ {
+ return a >= b ? a : b;
+ }
+
+ // instantly increases, slowly decreases
+ static float smoothDecay(float oldVal, float newVal) nothrow
+ {
+ // decay to 63.2% of newVal over 5 collections
+ // http://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
+ enum alpha = 1.0 / (5 + 1);
+ immutable decay = (newVal - oldVal) * alpha + oldVal;
+ return max(newVal, decay);
+ }
+
+ immutable smTarget = usedSmallPages * config.heapSizeFactor;
+ smallCollectThreshold = smoothDecay(smallCollectThreshold, smTarget);
+ immutable lgTarget = usedLargePages * config.heapSizeFactor;
+ largeCollectThreshold = smoothDecay(largeCollectThreshold, lgTarget);
+ }
+
+ /**
+ * Minimizes physical memory usage by returning free pools to the OS.
+ */
+ void minimize() nothrow
+ {
+ debug(PRINTF) printf("Minimizing.\n");
+
+ foreach (pool; pooltable.minimize())
+ {
+ debug(PRINTF) printFreeInfo(pool);
+ mappedPages -= pool.npages;
+ pool.Dtor();
+ cstdlib.free(pool);
+ }
+
+ debug(PRINTF) printf("Done minimizing.\n");
+ }
+
+ private @property bool lowMem() const nothrow
+ {
+ return isLowOnMem(cast(size_t)mappedPages * PAGESIZE);
+ }
+
+ void* alloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti) nothrow
+ {
+ return size <= PAGESIZE/2 ? smallAlloc(size, alloc_size, bits, ti)
+ : bigAlloc(size, alloc_size, bits, ti);
+ }
+
+ void* smallAlloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti) nothrow
+ {
+ immutable bin = binTable[size];
+ alloc_size = binsize[bin];
+
+ void* p = bucket[bin];
+ if (p)
+ goto L_hasBin;
+
+ if (recoverPool[bin])
+ recoverNextPage(bin);
+
+ bool tryAlloc() nothrow
+ {
+ if (!bucket[bin])
+ {
+ bucket[bin] = allocPage(bin);
+ if (!bucket[bin])
+ return false;
+ }
+ p = bucket[bin];
+ return true;
+ }
+
+ if (!tryAlloc())
+ {
+ if (!lowMem && (disabled || usedSmallPages < smallCollectThreshold))
+ {
+ // disabled or threshold not reached => allocate a new pool instead of collecting
+ if (!newPool(1, false))
+ {
+ // out of memory => try to free some memory
+ fullcollect(false, true); // stop the world
+ if (lowMem)
+ minimize();
+ recoverNextPage(bin);
+ }
+ }
+ else if (usedSmallPages > 0)
+ {
+ fullcollect();
+ if (lowMem)
+ minimize();
+ recoverNextPage(bin);
+ }
+ // tryAlloc will succeed if a new pool was allocated above, if it fails allocate a new pool now
+ if (!tryAlloc() && (!newPool(1, false) || !tryAlloc()))
+ // out of luck or memory
+ onOutOfMemoryErrorNoGC();
+ }
+ assert(p !is null);
+ L_hasBin:
+ // Return next item from free list
+ bucket[bin] = (cast(List*)p).next;
+ auto pool = (cast(List*)p).pool;
+
+ auto biti = (p - pool.baseAddr) >> pool.shiftBy;
+ assert(pool.freebits.test(biti));
+ if (collectInProgress)
+ pool.mark.setLocked(biti); // be sure that the child is aware of the page being used
+ pool.freebits.clear(biti);
+ if (bits)
+ pool.setBits(biti, bits);
+ //debug(PRINTF) printf("\tmalloc => %p\n", p);
+ debug (MEMSTOMP) memset(p, 0xF0, alloc_size);
+
+ if (ConservativeGC.isPrecise)
+ {
+ debug(SENTINEL)
+ pool.setPointerBitmapSmall(sentinel_add(p), size - SENTINEL_EXTRA, size - SENTINEL_EXTRA, bits, ti);
+ else
+ pool.setPointerBitmapSmall(p, size, alloc_size, bits, ti);
+ }
+ return p;
+ }
+
+ /**
+ * Allocate a chunk of memory that is larger than a page.
+ * Return null if out of memory.
+ */
+ void* bigAlloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti = null) nothrow
+ {
+ debug(PRINTF) printf("In bigAlloc. Size: %d\n", size);
+
+ LargeObjectPool* pool;
+ size_t pn;
+ immutable npages = LargeObjectPool.numPages(size);
+ if (npages == size_t.max)
+ onOutOfMemoryErrorNoGC(); // size just below size_t.max requested
+
+ bool tryAlloc() nothrow
+ {
+ foreach (p; pooltable[0 .. npools])
+ {
+ if (!p.isLargeObject || p.freepages < npages)
+ continue;
+ auto lpool = cast(LargeObjectPool*) p;
+ if ((pn = lpool.allocPages(npages)) == OPFAIL)
+ continue;
+ pool = lpool;
+ return true;
+ }
+ return false;
+ }
+
+ bool tryAllocNewPool() nothrow
+ {
+ pool = cast(LargeObjectPool*) newPool(npages, true);
+ if (!pool) return false;
+ pn = pool.allocPages(npages);
+ assert(pn != OPFAIL);
+ return true;
+ }
+
+ if (!tryAlloc())
+ {
+ if (!lowMem && (disabled || usedLargePages < largeCollectThreshold))
+ {
+ // disabled or threshold not reached => allocate a new pool instead of collecting
+ if (!tryAllocNewPool())
+ {
+ // disabled but out of memory => try to free some memory
+ minimizeAfterNextCollection = true;
+ fullcollect(false, true);
+ }
+ }
+ else if (usedLargePages > 0)
+ {
+ minimizeAfterNextCollection = true;
+ fullcollect();
+ }
+ // If alloc didn't yet succeed retry now that we collected/minimized
+ if (!pool && !tryAlloc() && !tryAllocNewPool())
+ // out of luck or memory
+ return null;
+ }
+ assert(pool);
+
+ debug(PRINTF) printFreeInfo(&pool.base);
+ if (collectInProgress)
+ pool.mark.setLocked(pn);
+ usedLargePages += npages;
+
+ debug(PRINTF) printFreeInfo(&pool.base);
+
+ auto p = pool.baseAddr + pn * PAGESIZE;
+ debug(PRINTF) printf("Got large alloc: %p, pt = %d, np = %d\n", p, pool.pagetable[pn], npages);
+ debug (MEMSTOMP) memset(p, 0xF1, size);
+ alloc_size = npages * PAGESIZE;
+ //debug(PRINTF) printf("\tp = %p\n", p);
+
+ if (bits)
+ pool.setBits(pn, bits);
+
+ if (ConservativeGC.isPrecise)
+ {
+ // an array of classes is in fact an array of pointers
+ immutable(void)* rtinfo;
+ if (!ti)
+ rtinfo = rtinfoHasPointers;
+ else if ((bits & BlkAttr.APPENDABLE) && (typeid(ti) is typeid(TypeInfo_Class)))
+ rtinfo = rtinfoHasPointers;
+ else
+ rtinfo = ti.rtInfo();
+ pool.rtinfo[pn] = cast(immutable(size_t)*)rtinfo;
+ }
+
+ return p;
+ }
+
+
+ /**
+ * Allocate a new pool with at least npages in it.
+ * Sort it into pooltable[].
+ * Return null if failed.
+ */
+ Pool *newPool(size_t npages, bool isLargeObject) nothrow
+ {
+ //debug(PRINTF) printf("************Gcx::newPool(npages = %d)****************\n", npages);
+
+ // Minimum of POOLSIZE
+ size_t minPages = config.minPoolSize / PAGESIZE;
+ if (npages < minPages)
+ npages = minPages;
+ else if (npages > minPages)
+ { // Give us 150% of requested size, so there's room to extend
+ auto n = npages + (npages >> 1);
+ if (n < size_t.max/PAGESIZE)
+ npages = n;
+ }
+
+ // Allocate successively larger pools up to 8 megs
+ if (npools)
+ { size_t n;
+
+ n = config.minPoolSize + config.incPoolSize * npools;
+ if (n > config.maxPoolSize)
+ n = config.maxPoolSize; // cap pool size
+ n /= PAGESIZE; // convert bytes to pages
+ if (npages < n)
+ npages = n;
+ }
+
+ //printf("npages = %d\n", npages);
+
+ auto pool = cast(Pool *)cstdlib.calloc(1, isLargeObject ? LargeObjectPool.sizeof : SmallObjectPool.sizeof);
+ if (pool)
+ {
+ pool.initialize(npages, isLargeObject);
+ if (collectInProgress)
+ pool.mark.setAll();
+ if (!pool.baseAddr || !pooltable.insert(pool))
+ {
+ pool.Dtor();
+ cstdlib.free(pool);
+ return null;
+ }
+ }
+
+ mappedPages += npages;
+
+ if (config.profile)
+ {
+ if (cast(size_t)mappedPages * PAGESIZE > maxPoolMemory)
+ maxPoolMemory = cast(size_t)mappedPages * PAGESIZE;
+ }
+ return pool;
+ }
+
+ /**
+ * Allocate a page of bin's.
+ * Returns:
+ * head of a single linked list of new entries
+ */
+ List* allocPage(Bins bin) nothrow
+ {
+ //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin);
+ for (size_t n = 0; n < npools; n++)
+ {
+ Pool* pool = pooltable[n];
+ if (pool.isLargeObject)
+ continue;
+ if (List* p = (cast(SmallObjectPool*)pool).allocPage(bin))
+ {
+ ++usedSmallPages;
+ return p;
+ }
+ }
+ return null;
+ }
+
+ static struct ScanRange(bool precise)
+ {
+ void* pbot;
+ void* ptop;
+ static if (precise)
+ {
+ void** pbase; // start of memory described by ptrbitmap
+ size_t* ptrbmp; // bits from is_pointer or rtinfo
+ size_t bmplength; // number of valid bits
+ }
+ }
+
+ static struct ToScanStack(RANGE)
+ {
+ nothrow:
+ @disable this(this);
+ auto stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
+
+ void reset()
+ {
+ _length = 0;
+ if (_p)
+ {
+ os_mem_unmap(_p, _cap * RANGE.sizeof);
+ _p = null;
+ }
+ _cap = 0;
+ }
+ void clear()
+ {
+ _length = 0;
+ }
+
+ void push(RANGE rng)
+ {
+ if (_length == _cap) grow();
+ _p[_length++] = rng;
+ }
+
+ RANGE pop()
+ in { assert(!empty); }
+ do
+ {
+ return _p[--_length];
+ }
+
+ bool popLocked(ref RANGE rng)
+ {
+ if (_length == 0)
+ return false;
+
+ stackLock.lock();
+ scope(exit) stackLock.unlock();
+ if (_length == 0)
+ return false;
+ rng = _p[--_length];
+ return true;
+ }
+
+ ref inout(RANGE) opIndex(size_t idx) inout
+ in { assert(idx < _length); }
+ do
+ {
+ return _p[idx];
+ }
+
+ @property size_t length() const { return _length; }
+ @property bool empty() const { return !length; }
+
+ private:
+ void grow()
+ {
+ pragma(inline, false);
+
+ enum initSize = 64 * 1024; // Windows VirtualAlloc granularity
+ immutable ncap = _cap ? 2 * _cap : initSize / RANGE.sizeof;
+ auto p = cast(RANGE*)os_mem_map(ncap * RANGE.sizeof);
+ if (p is null) onOutOfMemoryErrorNoGC();
+ if (_p !is null)
+ {
+ p[0 .. _length] = _p[0 .. _length];
+ os_mem_unmap(_p, _cap * RANGE.sizeof);
+ }
+ _p = p;
+ _cap = ncap;
+ }
+
+ size_t _length;
+ RANGE* _p;
+ size_t _cap;
+ }
+
+ ToScanStack!(ScanRange!false) toscanConservative;
+ ToScanStack!(ScanRange!true) toscanPrecise;
+
+ template scanStack(bool precise)
+ {
+ static if (precise)
+ alias scanStack = toscanPrecise;
+ else
+ alias scanStack = toscanConservative;
+ }
+
+ /**
+ * Search a range of memory values and mark any pointers into the GC pool.
+ */
+ private void mark(bool precise, bool parallel, bool shared_mem)(ScanRange!precise rng) scope nothrow
+ {
+ alias toscan = scanStack!precise;
+
+ debug(MARK_PRINTF)
+ printf("marking range: [%p..%p] (%#llx)\n", pbot, ptop, cast(long)(ptop - pbot));
+
+ // limit the amount of ranges added to the toscan stack
+ enum FANOUT_LIMIT = 32;
+ size_t stackPos;
+ ScanRange!precise[FANOUT_LIMIT] stack = void;
+
+ size_t pcache = 0;
+
+ // let dmd allocate a register for this.pools
+ auto pools = pooltable.pools;
+ const highpool = pooltable.npools - 1;
+ const minAddr = pooltable.minAddr;
+ size_t memSize = pooltable.maxAddr - minAddr;
+ Pool* pool = null;
+
+ // properties of allocation pointed to
+ ScanRange!precise tgt = void;
+
+ for (;;)
+ {
+ auto p = *cast(void**)(rng.pbot);
+
+ debug(MARK_PRINTF) printf("\tmark %p: %p\n", rng.pbot, p);
+
+ if (cast(size_t)(p - minAddr) < memSize &&
+ (cast(size_t)p & ~cast(size_t)(PAGESIZE-1)) != pcache)
+ {
+ static if (precise) if (rng.pbase)
+ {
+ size_t bitpos = cast(void**)rng.pbot - rng.pbase;
+ while (bitpos >= rng.bmplength)
+ {
+ bitpos -= rng.bmplength;
+ rng.pbase += rng.bmplength;
+ }
+ import core.bitop;
+ if (!core.bitop.bt(rng.ptrbmp, bitpos))
+ {
+ debug(MARK_PRINTF) printf("\t\tskipping non-pointer\n");
+ goto LnextPtr;
+ }
+ }
+
+ if (!pool || p < pool.baseAddr || p >= pool.topAddr)
+ {
+ size_t low = 0;
+ size_t high = highpool;
+ while (true)
+ {
+ size_t mid = (low + high) >> 1;
+ pool = pools[mid];
+ if (p < pool.baseAddr)
+ high = mid - 1;
+ else if (p >= pool.topAddr)
+ low = mid + 1;
+ else break;
+
+ if (low > high)
+ goto LnextPtr;
+ }
+ }
+ size_t offset = cast(size_t)(p - pool.baseAddr);
+ size_t biti = void;
+ size_t pn = offset / PAGESIZE;
+ size_t bin = pool.pagetable[pn]; // not Bins to avoid multiple size extension instructions
+
+ debug(MARK_PRINTF)
+ printf("\t\tfound pool %p, base=%p, pn = %lld, bin = %d\n", pool, pool.baseAddr, cast(long)pn, bin);
+
+ // Adjust bit to be at start of allocated memory block
+ if (bin < B_PAGE)
+ {
+ // We don't care abou setting pointsToBase correctly
+ // because it's ignored for small object pools anyhow.
+ auto offsetBase = baseOffset(offset, cast(Bins)bin);
+ biti = offsetBase >> Pool.ShiftBy.Small;
+ //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
+
+ if (!pool.mark.testAndSet!shared_mem(biti) && !pool.noscan.test(biti))
+ {
+ tgt.pbot = pool.baseAddr + offsetBase;
+ tgt.ptop = tgt.pbot + binsize[bin];
+ static if (precise)
+ {
+ tgt.pbase = cast(void**)pool.baseAddr;
+ tgt.ptrbmp = pool.is_pointer.data;
+ tgt.bmplength = size_t.max; // no repetition
+ }
+ goto LaddRange;
+ }
+ }
+ else if (bin == B_PAGE)
+ {
+ biti = offset >> Pool.ShiftBy.Large;
+ //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
+
+ pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
+ tgt.pbot = cast(void*)pcache;
+
+ // For the NO_INTERIOR attribute. This tracks whether
+ // the pointer is an interior pointer or points to the
+ // base address of a block.
+ if (tgt.pbot != sentinel_sub(p) && pool.nointerior.nbits && pool.nointerior.test(biti))
+ goto LnextPtr;
+
+ if (!pool.mark.testAndSet!shared_mem(biti) && !pool.noscan.test(biti))
+ {
+ tgt.ptop = tgt.pbot + (cast(LargeObjectPool*)pool).getSize(pn);
+ goto LaddLargeRange;
+ }
+ }
+ else if (bin == B_PAGEPLUS)
+ {
+ pn -= pool.bPageOffsets[pn];
+ biti = pn * (PAGESIZE >> Pool.ShiftBy.Large);
+
+ pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
+ if (pool.nointerior.nbits && pool.nointerior.test(biti))
+ goto LnextPtr;
+
+ if (!pool.mark.testAndSet!shared_mem(biti) && !pool.noscan.test(biti))
+ {
+ tgt.pbot = pool.baseAddr + (pn * PAGESIZE);
+ tgt.ptop = tgt.pbot + (cast(LargeObjectPool*)pool).getSize(pn);
+ LaddLargeRange:
+ static if (precise)
+ {
+ auto rtinfo = pool.rtinfo[biti];
+ if (rtinfo is rtinfoNoPointers)
+ goto LnextPtr; // only if inconsistent with noscan
+ if (rtinfo is rtinfoHasPointers)
+ {
+ tgt.pbase = null; // conservative
+ }
+ else
+ {
+ tgt.ptrbmp = cast(size_t*)rtinfo;
+ size_t element_size = *tgt.ptrbmp++;
+ tgt.bmplength = (element_size + (void*).sizeof - 1) / (void*).sizeof;
+ assert(tgt.bmplength);
+
+ debug(SENTINEL)
+ tgt.pbot = sentinel_add(tgt.pbot);
+ if (pool.appendable.test(biti))
+ {
+ // take advantage of knowing array layout in rt.lifetime
+ void* arrtop = tgt.pbot + 16 + *cast(size_t*)tgt.pbot;
+ assert (arrtop > tgt.pbot && arrtop <= tgt.ptop);
+ tgt.pbot += 16;
+ tgt.ptop = arrtop;
+ }
+ else
+ {
+ tgt.ptop = tgt.pbot + element_size;
+ }
+ tgt.pbase = cast(void**)tgt.pbot;
+ }
+ }
+ goto LaddRange;
+ }
+ }
+ else
+ {
+ // Don't mark bits in B_FREE pages
+ assert(bin == B_FREE);
+ }
+ }
+ LnextPtr:
+ rng.pbot += (void*).sizeof;
+ if (rng.pbot < rng.ptop)
+ continue;
+
+ LnextRange:
+ if (stackPos)
+ {
+ // pop range from local stack and recurse
+ rng = stack[--stackPos];
+ }
+ else
+ {
+ static if (parallel)
+ {
+ if (!toscan.popLocked(rng))
+ break; // nothing more to do
+ }
+ else
+ {
+ if (toscan.empty)
+ break; // nothing more to do
+
+ // pop range from global stack and recurse
+ rng = toscan.pop();
+ }
+ }
+ // printf(" pop [%p..%p] (%#zx)\n", p1, p2, cast(size_t)p2 - cast(size_t)p1);
+ goto LcontRange;
+
+ LaddRange:
+ rng.pbot += (void*).sizeof;
+ if (rng.pbot < rng.ptop)
+ {
+ if (stackPos < stack.length)
+ {
+ stack[stackPos] = tgt;
+ stackPos++;
+ continue;
+ }
+ static if (parallel)
+ {
+ toscan.stackLock.lock();
+ scope(exit) toscan.stackLock.unlock();
+ }
+ toscan.push(rng);
+ // reverse order for depth-first-order traversal
+ foreach_reverse (ref range; stack)
+ toscan.push(range);
+ stackPos = 0;
+ }
+ LendOfRange:
+ // continue with last found range
+ rng = tgt;
+
+ LcontRange:
+ pcache = 0;
+ }
+ }
+
+ void markConservative(bool shared_mem)(void *pbot, void *ptop) scope nothrow
+ {
+ if (pbot < ptop)
+ mark!(false, false, shared_mem)(ScanRange!false(pbot, ptop));
+ }
+
+ void markPrecise(bool shared_mem)(void *pbot, void *ptop) scope nothrow
+ {
+ if (pbot < ptop)
+ mark!(true, false, shared_mem)(ScanRange!true(pbot, ptop, null));
+ }
+
+ version (COLLECT_PARALLEL)
+ ToScanStack!(void*) toscanRoots;
+
+ version (COLLECT_PARALLEL)
+ void collectRoots(void *pbot, void *ptop) scope nothrow
+ {
+ const minAddr = pooltable.minAddr;
+ size_t memSize = pooltable.maxAddr - minAddr;
+
+ for (auto p = cast(void**)pbot; cast(void*)p < ptop; p++)
+ {
+ auto ptr = *p;
+ if (cast(size_t)(ptr - minAddr) < memSize)
+ toscanRoots.push(ptr);
+ }
+ }
+
+ // collection step 1: prepare freebits and mark bits
+ void prepare() nothrow
+ {
+ debug(COLLECT_PRINTF) printf("preparing mark.\n");
+
+ for (size_t n = 0; n < npools; n++)
+ {
+ Pool* pool = pooltable[n];
+ if (pool.isLargeObject)
+ pool.mark.zero();
+ else
+ pool.mark.copy(&pool.freebits);
+ }
+ }
+
+ // collection step 2: mark roots and heap
+ void markAll(alias markFn)(bool nostack) nothrow
+ {
+ if (!nostack)
+ {
+ debug(COLLECT_PRINTF) printf("\tscan stacks.\n");
+ // Scan stacks and registers for each paused thread
+ thread_scanAll(&markFn);
+ }
+
+ // Scan roots[]
+ debug(COLLECT_PRINTF) printf("\tscan roots[]\n");
+ foreach (root; roots)
+ {
+ markFn(cast(void*)&root.proot, cast(void*)(&root.proot + 1));
+ }
+
+ // Scan ranges[]
+ debug(COLLECT_PRINTF) printf("\tscan ranges[]\n");
+ //log++;
+ foreach (range; ranges)
+ {
+ debug(COLLECT_PRINTF) printf("\t\t%p .. %p\n", range.pbot, range.ptop);
+ markFn(range.pbot, range.ptop);
+ }
+ //log--;
+ }
+
+ version (COLLECT_PARALLEL)
+ void collectAllRoots(bool nostack) nothrow
+ {
+ if (!nostack)
+ {
+ debug(COLLECT_PRINTF) printf("\tcollect stacks.\n");
+ // Scan stacks and registers for each paused thread
+ thread_scanAll(&collectRoots);
+ }
+
+ // Scan roots[]
+ debug(COLLECT_PRINTF) printf("\tcollect roots[]\n");
+ foreach (root; roots)
+ {
+ toscanRoots.push(root);
+ }
+
+ // Scan ranges[]
+ debug(COLLECT_PRINTF) printf("\tcollect ranges[]\n");
+ foreach (range; ranges)
+ {
+ debug(COLLECT_PRINTF) printf("\t\t%p .. %p\n", range.pbot, range.ptop);
+ collectRoots(range.pbot, range.ptop);
+ }
+ }
+
+ // collection step 3: finalize unreferenced objects, recover full pages with no live objects
+ size_t sweep() nothrow
+ {
+ // Free up everything not marked
+ debug(COLLECT_PRINTF) printf("\tfree'ing\n");
+ size_t freedLargePages;
+ size_t freedSmallPages;
+ size_t freed;
+ for (size_t n = 0; n < npools; n++)
+ {
+ size_t pn;
+ Pool* pool = pooltable[n];
+
+ if (pool.isLargeObject)
+ {
+ auto lpool = cast(LargeObjectPool*)pool;
+ size_t numFree = 0;
+ size_t npages;
+ for (pn = 0; pn < pool.npages; pn += npages)
+ {
+ npages = pool.bPageOffsets[pn];
+ Bins bin = cast(Bins)pool.pagetable[pn];
+ if (bin == B_FREE)
+ {
+ numFree += npages;
+ continue;
+ }
+ assert(bin == B_PAGE);
+ size_t biti = pn;
+
+ if (!pool.mark.test(biti))
+ {
+ void *p = pool.baseAddr + pn * PAGESIZE;
+ void* q = sentinel_add(p);
+ sentinel_Invariant(q);
+
+ if (pool.finals.nbits && pool.finals.clear(biti))
+ {
+ size_t size = npages * PAGESIZE - SENTINEL_EXTRA;
+ uint attr = pool.getBits(biti);
+ rt_finalizeFromGC(q, sentinel_size(q, size), attr);
+ }
+
+ pool.clrBits(biti, ~BlkAttr.NONE ^ BlkAttr.FINALIZE);
+
+ debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
+ leakDetector.log_free(q, sentinel_size(q, npages * PAGESIZE - SENTINEL_EXTRA));
+ pool.pagetable[pn..pn+npages] = B_FREE;
+ if (pn < pool.searchStart) pool.searchStart = pn;
+ freedLargePages += npages;
+ pool.freepages += npages;
+ numFree += npages;
+
+ debug (MEMSTOMP) memset(p, 0xF3, npages * PAGESIZE);
+ // Don't need to update searchStart here because
+ // pn is guaranteed to be greater than last time
+ // we updated it.
+
+ pool.largestFree = pool.freepages; // invalidate
+ }
+ else
+ {
+ if (numFree > 0)
+ {
+ lpool.setFreePageOffsets(pn - numFree, numFree);
+ numFree = 0;
+ }
+ }
+ }
+ if (numFree > 0)
+ lpool.setFreePageOffsets(pn - numFree, numFree);
+ }
+ else
+ {
+ // reinit chain of pages to rebuild free list
+ pool.recoverPageFirst[] = cast(uint)pool.npages;
+
+ for (pn = 0; pn < pool.npages; pn++)
+ {
+ Bins bin = cast(Bins)pool.pagetable[pn];
+
+ if (bin < B_PAGE)
+ {
+ auto freebitsdata = pool.freebits.data + pn * PageBits.length;
+ auto markdata = pool.mark.data + pn * PageBits.length;
+
+ // the entries to free are allocated objects (freebits == false)
+ // that are not marked (mark == false)
+ PageBits toFree;
+ static foreach (w; 0 .. PageBits.length)
+ toFree[w] = (~freebitsdata[w] & ~markdata[w]);
+
+ // the page is unchanged if there is nothing to free
+ bool unchanged = true;
+ static foreach (w; 0 .. PageBits.length)
+ unchanged = unchanged && (toFree[w] == 0);
+ if (unchanged)
+ {
+ bool hasDead = false;
+ static foreach (w; 0 .. PageBits.length)
+ hasDead = hasDead || (~freebitsdata[w] != baseOffsetBits[bin][w]);
+ if (hasDead)
+ {
+ // add to recover chain
+ pool.binPageChain[pn] = pool.recoverPageFirst[bin];
+ pool.recoverPageFirst[bin] = cast(uint)pn;
+ }
+ else
+ {
+ pool.binPageChain[pn] = Pool.PageRecovered;
+ }
+ continue;
+ }
+
+ // the page can be recovered if all of the allocated objects (freebits == false)
+ // are freed
+ bool recoverPage = true;
+ static foreach (w; 0 .. PageBits.length)
+ recoverPage = recoverPage && (~freebitsdata[w] == toFree[w]);
+
+ // We need to loop through each object if any have a finalizer,
+ // or, if any of the debug hooks are enabled.
+ bool doLoop = false;
+ debug (SENTINEL)
+ doLoop = true;
+ else version (assert)
+ doLoop = true;
+ else debug (COLLECT_PRINTF) // need output for each object
+ doLoop = true;
+ else debug (LOGGING)
+ doLoop = true;
+ else debug (MEMSTOMP)
+ doLoop = true;
+ else if (pool.finals.data)
+ {
+ // finalizers must be called on objects that are about to be freed
+ auto finalsdata = pool.finals.data + pn * PageBits.length;
+ static foreach (w; 0 .. PageBits.length)
+ doLoop = doLoop || (toFree[w] & finalsdata[w]) != 0;
+ }
+
+ if (doLoop)
+ {
+ immutable size = binsize[bin];
+ void *p = pool.baseAddr + pn * PAGESIZE;
+ immutable base = pn * (PAGESIZE/16);
+ immutable bitstride = size / 16;
+
+ // ensure that there are at least <size> bytes for every address
+ // below ptop even if unaligned
+ void *ptop = p + PAGESIZE - size + 1;
+ for (size_t i; p < ptop; p += size, i += bitstride)
+ {
+ immutable biti = base + i;
+
+ if (!pool.mark.test(biti))
+ {
+ void* q = sentinel_add(p);
+ sentinel_Invariant(q);
+
+ if (pool.finals.nbits && pool.finals.test(biti))
+ rt_finalizeFromGC(q, sentinel_size(q, size), pool.getBits(biti));
+
+ assert(core.bitop.bt(toFree.ptr, i));
+
+ debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p);
+ leakDetector.log_free(q, sentinel_size(q, size));
+
+ debug (MEMSTOMP) memset(p, 0xF3, size);
+ }
+ }
+ }
+
+ if (recoverPage)
+ {
+ pool.freeAllPageBits(pn);
+
+ pool.pagetable[pn] = B_FREE;
+ // add to free chain
+ pool.binPageChain[pn] = cast(uint) pool.searchStart;
+ pool.searchStart = pn;
+ pool.freepages++;
+ freedSmallPages++;
+ }
+ else
+ {
+ pool.freePageBits(pn, toFree);
+
+ // add to recover chain
+ pool.binPageChain[pn] = pool.recoverPageFirst[bin];
+ pool.recoverPageFirst[bin] = cast(uint)pn;
+ }
+ }
+ }
+ }
+ }
+
+ assert(freedLargePages <= usedLargePages);
+ usedLargePages -= freedLargePages;
+ debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedLargePages, npools);
+
+ assert(freedSmallPages <= usedSmallPages);
+ usedSmallPages -= freedSmallPages;
+ debug(COLLECT_PRINTF) printf("\trecovered small pages = %d\n", freedSmallPages);
+
+ return freedLargePages + freedSmallPages;
+ }
+
+ bool recoverPage(SmallObjectPool* pool, size_t pn, Bins bin) nothrow
+ {
+ size_t size = binsize[bin];
+ size_t bitbase = pn * (PAGESIZE / 16);
+
+ auto freebitsdata = pool.freebits.data + pn * PageBits.length;
+
+ // the page had dead objects when collecting, these cannot have been resurrected
+ bool hasDead = false;
+ static foreach (w; 0 .. PageBits.length)
+ hasDead = hasDead || (freebitsdata[w] != 0);
+ assert(hasDead);
+
+ // prepend to buckets, but with forward addresses inside the page
+ assert(bucket[bin] is null);
+ List** bucketTail = &bucket[bin];
+
+ void* p = pool.baseAddr + pn * PAGESIZE;
+ const top = PAGESIZE - size + 1; // ensure <size> bytes available even if unaligned
+ for (size_t u = 0; u < top; u += size)
+ {
+ if (!core.bitop.bt(freebitsdata, u / 16))
+ continue;
+ auto elem = cast(List *)(p + u);
+ elem.pool = &pool.base;
+ *bucketTail = elem;
+ bucketTail = &elem.next;
+ }
+ *bucketTail = null;
+ assert(bucket[bin] !is null);
+ return true;
+ }
+
+ bool recoverNextPage(Bins bin) nothrow
+ {
+ SmallObjectPool* pool = recoverPool[bin];
+ while (pool)
+ {
+ auto pn = pool.recoverPageFirst[bin];
+ while (pn < pool.npages)
+ {
+ auto next = pool.binPageChain[pn];
+ pool.binPageChain[pn] = Pool.PageRecovered;
+ pool.recoverPageFirst[bin] = next;
+ if (recoverPage(pool, pn, bin))
+ return true;
+ pn = next;
+ }
+ pool = setNextRecoverPool(bin, pool.ptIndex + 1);
+ }
+ return false;
+ }
+
+ private SmallObjectPool* setNextRecoverPool(Bins bin, size_t poolIndex) nothrow
+ {
+ Pool* pool;
+ while (poolIndex < npools &&
+ ((pool = pooltable[poolIndex]).isLargeObject ||
+ pool.recoverPageFirst[bin] >= pool.npages))
+ poolIndex++;
+
+ return recoverPool[bin] = poolIndex < npools ? cast(SmallObjectPool*)pool : null;
+ }
+
+ version (COLLECT_FORK)
+ void disableFork() nothrow
+ {
+ markProcPid = 0;
+ shouldFork = false;
+ }
+
+ version (COLLECT_FORK)
+ ChildStatus collectFork(bool block) nothrow
+ {
+ typeof(return) rc = wait_pid(markProcPid, block);
+ final switch (rc)
+ {
+ case ChildStatus.done:
+ debug(COLLECT_PRINTF) printf("\t\tmark proc DONE (block=%d)\n",
+ cast(int) block);
+ markProcPid = 0;
+ // process GC marks then sweep
+ thread_suspendAll();
+ thread_processGCMarks(&isMarked);
+ thread_resumeAll();
+ break;
+ case ChildStatus.running:
+ debug(COLLECT_PRINTF) printf("\t\tmark proc RUNNING\n");
+ if (!block)
+ break;
+ // Something went wrong, if block is true, wait() should never
+ // return RUNNING.
+ goto case ChildStatus.error;
+ case ChildStatus.error:
+ debug(COLLECT_PRINTF) printf("\t\tmark proc ERROR\n");
+ // Try to keep going without forking
+ // and do the marking in this thread
+ break;
+ }
+ return rc;
+ }
+
+ version (COLLECT_FORK)
+ ChildStatus markFork(bool nostack, bool block, bool doParallel) nothrow
+ {
+ // Forking is enabled, so we fork() and start a new concurrent mark phase
+ // in the child. If the collection should not block, the parent process
+ // tells the caller no memory could be recycled immediately (if this collection
+ // was triggered by an allocation, the caller should allocate more memory
+ // to fulfill the request).
+ // If the collection should block, the parent will wait for the mark phase
+ // to finish before returning control to the mutator,
+ // but other threads are restarted and may run in parallel with the mark phase
+ // (unless they allocate or use the GC themselves, in which case
+ // the global GC lock will stop them).
+ // fork now and sweep later
+ int child_mark() scope
+ {
+ if (doParallel)
+ markParallel(nostack);
+ else if (ConservativeGC.isPrecise)
+ markAll!(markPrecise!true)(nostack);
+ else
+ markAll!(markConservative!true)(nostack);
+ return 0;
+ }
+
+ import core.stdc.stdlib : _Exit;
+ debug (PRINTF_TO_FILE)
+ {
+ import core.stdc.stdio : fflush;
+ fflush(null); // avoid duplicated FILE* output
+ }
+ version (OSX)
+ {
+ auto pid = __fork(); // avoids calling handlers (from libc source code)
+ }
+ else version (linux)
+ {
+ // clone() fits better as we don't want to do anything but scanning in the child process.
+ // no fork-handlera are called, so we can avoid deadlocks due to malloc locks. Probably related:
+ // https://sourceware.org/bugzilla/show_bug.cgi?id=4737
+ import core.sys.linux.sched : clone;
+ import core.sys.posix.signal : SIGCHLD;
+ enum CLONE_CHILD_CLEARTID = 0x00200000; /* Register exit futex and memory */
+ const flags = CLONE_CHILD_CLEARTID | SIGCHLD; // child thread id not needed
+ scope int delegate() scope dg = &child_mark;
+ extern(C) static int wrap_delegate(void* arg)
+ {
+ auto dg = cast(int delegate() scope*)arg;
+ return (*dg)();
+ }
+ char[256] stackbuf; // enough stack space for clone() to place some info for the child without stomping the parent stack
+ auto stack = stackbuf.ptr + (isStackGrowingDown ? stackbuf.length : 0);
+ auto pid = clone(&wrap_delegate, stack, flags, &dg);
+ }
+ else
+ {
+ fork_needs_lock = false;
+ auto pid = fork();
+ fork_needs_lock = true;
+ }
+ assert(pid != -1);
+ switch (pid)
+ {
+ case -1: // fork() failed, retry without forking
+ return ChildStatus.error;
+ case 0: // child process (not run with clone)
+ child_mark();
+ _Exit(0);
+ default: // the parent
+ thread_resumeAll();
+ if (!block)
+ {
+ markProcPid = pid;
+ return ChildStatus.running;
+ }
+ ChildStatus r = wait_pid(pid); // block until marking is done
+ if (r == ChildStatus.error)
+ {
+ thread_suspendAll();
+ // there was an error
+ // do the marking in this thread
+ disableFork();
+ if (doParallel)
+ markParallel(nostack);
+ else if (ConservativeGC.isPrecise)
+ markAll!(markPrecise!false)(nostack);
+ else
+ markAll!(markConservative!false)(nostack);
+ } else {
+ assert(r == ChildStatus.done);
+ assert(r != ChildStatus.running);
+ }
+ }
+ return ChildStatus.done; // waited for the child
+ }
+
+ /**
+ * Return number of full pages free'd.
+ * The collection is done concurrently only if block and isFinal are false.
+ */
+ size_t fullcollect(bool nostack = false, bool block = false, bool isFinal = false) nothrow
+ {
+ // It is possible that `fullcollect` will be called from a thread which
+ // is not yet registered in runtime (because allocating `new Thread` is
+ // part of `thread_attachThis` implementation). In that case it is
+ // better not to try actually collecting anything
+
+ if (Thread.getThis() is null)
+ return 0;
+
+ MonoTime start, stop, begin;
+ begin = start = currTime;
+
+ debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
+ version (COLLECT_PARALLEL)
+ {
+ bool doParallel = config.parallel > 0 && !config.fork;
+ if (doParallel && !scanThreadData)
+ {
+ if (isFinal) // avoid starting threads for parallel marking
+ doParallel = false;
+ else
+ startScanThreads();
+ }
+ }
+ else
+ enum doParallel = false;
+
+ //printf("\tpool address range = %p .. %p\n", minAddr, maxAddr);
+
+ version (COLLECT_FORK)
+ bool doFork = shouldFork;
+ else
+ enum doFork = false;
+
+ if (doFork && collectInProgress)
+ {
+ version (COLLECT_FORK)
+ {
+ // If there is a mark process running, check if it already finished.
+ // If that is the case, we move to the sweep phase.
+ // If it's still running, either we block until the mark phase is
+ // done (and then sweep to finish the collection), or in case of error
+ // we redo the mark phase without forking.
+ ChildStatus rc = collectFork(block);
+ final switch (rc)
+ {
+ case ChildStatus.done:
+ break;
+ case ChildStatus.running:
+ return 0;
+ case ChildStatus.error:
+ disableFork();
+ goto Lmark;
+ }
+ }
+ }
+ else
+ {
+Lmark:
+ // lock roots and ranges around suspending threads b/c they're not reentrant safe
+ rangesLock.lock();
+ rootsLock.lock();
+ debug(INVARIANT) inCollection = true;
+ scope (exit)
+ {
+ debug(INVARIANT) inCollection = false;
+ rangesLock.unlock();
+ rootsLock.unlock();
+ }
+ thread_suspendAll();
+
+ prepare();
+
+ stop = currTime;
+ prepTime += (stop - start);
+ start = stop;
+
+ if (doFork && !isFinal && !block) // don't start a new fork during termination
+ {
+ version (COLLECT_FORK)
+ {
+ auto forkResult = markFork(nostack, block, doParallel);
+ final switch (forkResult)
+ {
+ case ChildStatus.error:
+ disableFork();
+ goto Lmark;
+ case ChildStatus.running:
+ // update profiling informations
+ stop = currTime;
+ markTime += (stop - start);
+ Duration pause = stop - begin;
+ if (pause > maxPauseTime)
+ maxPauseTime = pause;
+ pauseTime += pause;
+ return 0;
+ case ChildStatus.done:
+ break;
+ }
+ // if we get here, forking failed and a standard STW collection got issued
+ // threads were suspended again, restart them
+ thread_suspendAll();
+ }
+ }
+ else if (doParallel)
+ {
+ version (COLLECT_PARALLEL)
+ markParallel(nostack);
+ }
+ else
+ {
+ if (ConservativeGC.isPrecise)
+ markAll!(markPrecise!false)(nostack);
+ else
+ markAll!(markConservative!false)(nostack);
+ }
+
+ thread_processGCMarks(&isMarked);
+ thread_resumeAll();
+ isFinal = false;
+ }
+
+ // If we get here with the forking GC, the child process has finished the marking phase
+ // or block == true and we are using standard stop the world collection.
+ // It is time to sweep
+
+ stop = currTime;
+ markTime += (stop - start);
+ Duration pause = stop - begin;
+ if (pause > maxPauseTime)
+ maxPauseTime = pause;
+ pauseTime += pause;
+ start = stop;
+
+ ConservativeGC._inFinalizer = true;
+ size_t freedPages = void;
+ {
+ scope (failure) ConservativeGC._inFinalizer = false;
+ freedPages = sweep();
+ ConservativeGC._inFinalizer = false;
+ }
+
+ // minimize() should be called only after a call to fullcollect
+ // terminates with a sweep
+ if (minimizeAfterNextCollection || lowMem)
+ {
+ minimizeAfterNextCollection = false;
+ minimize();
+ }
+
+ // init bucket lists
+ bucket[] = null;
+ foreach (Bins bin; 0..B_NUMSMALL)
+ setNextRecoverPool(bin, 0);
+
+ stop = currTime;
+ sweepTime += (stop - start);
+
+ Duration collectionTime = stop - begin;
+ if (collectionTime > maxCollectionTime)
+ maxCollectionTime = collectionTime;
+
+ ++numCollections;
+
+ updateCollectThresholds();
+ if (doFork && isFinal)
+ return fullcollect(true, true, false);
+ return freedPages;
+ }
+
+ /**
+ * Returns true if the addr lies within a marked block.
+ *
+ * Warning! This should only be called while the world is stopped inside
+ * the fullcollect function after all live objects have been marked, but before sweeping.
+ */
+ int isMarked(void *addr) scope nothrow
+ {
+ // first, we find the Pool this block is in, then check to see if the
+ // mark bit is clear.
+ auto pool = findPool(addr);
+ if (pool)
+ {
+ auto offset = cast(size_t)(addr - pool.baseAddr);
+ auto pn = offset / PAGESIZE;
+ auto bins = cast(Bins)pool.pagetable[pn];
+ size_t biti = void;
+ if (bins < B_PAGE)
+ {
+ biti = baseOffset(offset, bins) >> pool.ShiftBy.Small;
+ // doesn't need to check freebits because no pointer must exist
+ // to a block that was free before starting the collection
+ }
+ else if (bins == B_PAGE)
+ {
+ biti = pn * (PAGESIZE >> pool.ShiftBy.Large);
+ }
+ else if (bins == B_PAGEPLUS)
+ {
+ pn -= pool.bPageOffsets[pn];
+ biti = pn * (PAGESIZE >> pool.ShiftBy.Large);
+ }
+ else // bins == B_FREE
+ {
+ assert(bins == B_FREE);
+ return IsMarked.no;
+ }
+ return pool.mark.test(biti) ? IsMarked.yes : IsMarked.no;
+ }
+ return IsMarked.unknown;
+ }
+
+ version (Posix)
+ {
+ // A fork might happen while GC code is running in a different thread.
+ // Because that would leave the GC in an inconsistent state,
+ // make sure no GC code is running by acquiring the lock here,
+ // before a fork.
+ // This must not happen if fork is called from the GC with the lock already held
+
+ __gshared bool fork_needs_lock = true; // racing condition with cocurrent calls of fork?
+
+
+ extern(C) static void _d_gcx_atfork_prepare()
+ {
+ if (instance && fork_needs_lock)
+ ConservativeGC.lockNR();
+ }
+
+ extern(C) static void _d_gcx_atfork_parent()
+ {
+ if (instance && fork_needs_lock)
+ ConservativeGC.gcLock.unlock();
+ }
+
+ extern(C) static void _d_gcx_atfork_child()
+ {
+ if (instance && fork_needs_lock)
+ {
+ ConservativeGC.gcLock.unlock();
+
+ // make sure the threads and event handles are reinitialized in a fork
+ version (COLLECT_PARALLEL)
+ {
+ if (Gcx.instance.scanThreadData)
+ {
+ cstdlib.free(Gcx.instance.scanThreadData);
+ Gcx.instance.numScanThreads = 0;
+ Gcx.instance.scanThreadData = null;
+ Gcx.instance.busyThreads = 0;
+
+ memset(&Gcx.instance.evStart, 0, Gcx.instance.evStart.sizeof);
+ memset(&Gcx.instance.evDone, 0, Gcx.instance.evDone.sizeof);
+ }
+ }
+ }
+ }
+ }
+
+ /* ============================ Parallel scanning =============================== */
+ version (COLLECT_PARALLEL):
+ import core.sync.event;
+ import core.atomic;
+ private: // disable invariants for background threads
+
+ static struct ScanThreadData
+ {
+ ThreadID tid;
+ }
+ uint numScanThreads;
+ ScanThreadData* scanThreadData;
+
+ Event evStart;
+ Event evDone;
+
+ shared uint busyThreads;
+ shared uint stoppedThreads;
+ bool stopGC;
+
+ void markParallel(bool nostack) nothrow
+ {
+ toscanRoots.clear();
+ collectAllRoots(nostack);
+ if (toscanRoots.empty)
+ return;
+
+ void** pbot = toscanRoots._p;
+ void** ptop = toscanRoots._p + toscanRoots._length;
+
+ debug(PARALLEL_PRINTF) printf("markParallel\n");
+
+ size_t pointersPerThread = toscanRoots._length / (numScanThreads + 1);
+ if (pointersPerThread > 0)
+ {
+ void pushRanges(bool precise)()
+ {
+ alias toscan = scanStack!precise;
+ toscan.stackLock.lock();
+
+ for (int idx = 0; idx < numScanThreads; idx++)
+ {
+ toscan.push(ScanRange!precise(pbot, pbot + pointersPerThread));
+ pbot += pointersPerThread;
+ }
+ toscan.stackLock.unlock();
+ }
+ if (ConservativeGC.isPrecise)
+ pushRanges!true();
+ else
+ pushRanges!false();
+ }
+ assert(pbot < ptop);
+
+ busyThreads.atomicOp!"+="(1); // main thread is busy
+
+ evStart.set();
+
+ debug(PARALLEL_PRINTF) printf("mark %lld roots\n", cast(ulong)(ptop - pbot));
+
+ if (ConservativeGC.isPrecise)
+ mark!(true, true, true)(ScanRange!true(pbot, ptop, null));
+ else
+ mark!(false, true, true)(ScanRange!false(pbot, ptop));
+
+ busyThreads.atomicOp!"-="(1);
+
+ debug(PARALLEL_PRINTF) printf("waitForScanDone\n");
+ pullFromScanStack();
+ debug(PARALLEL_PRINTF) printf("waitForScanDone done\n");
+ }
+
+ int maxParallelThreads() nothrow
+ {
+ import core.cpuid;
+ auto threads = threadsPerCPU();
+
+ if (threads == 0)
+ {
+ // If the GC is called by module ctors no explicit
+ // import dependency on the GC is generated. So the
+ // GC module is not correctly inserted into the module
+ // initialization chain. As it relies on core.cpuid being
+ // initialized, force this here.
+ try
+ {
+ foreach (m; ModuleInfo)
+ if (m.name == "core.cpuid")
+ if (auto ctor = m.ctor())
+ {
+ ctor();
+ threads = threadsPerCPU();
+ break;
+ }
+ }
+ catch (Exception)
+ {
+ assert(false, "unexpected exception iterating ModuleInfo");
+ }
+ }
+ return threads;
+ }
+
+
+ void startScanThreads() nothrow
+ {
+ auto threads = maxParallelThreads();
+ debug(PARALLEL_PRINTF) printf("startScanThreads: %d threads per CPU\n", threads);
+ if (threads <= 1)
+ return; // either core.cpuid not initialized or single core
+
+ numScanThreads = threads >= config.parallel ? config.parallel : threads - 1;
+
+ scanThreadData = cast(ScanThreadData*) cstdlib.calloc(numScanThreads, ScanThreadData.sizeof);
+ if (!scanThreadData)
+ onOutOfMemoryErrorNoGC();
+
+ evStart.initialize(false, false);
+ evDone.initialize(false, false);
+
+ version (Posix)
+ {
+ import core.sys.posix.signal;
+ // block all signals, scanBackground inherits this mask.
+ // see https://issues.dlang.org/show_bug.cgi?id=20256
+ sigset_t new_mask, old_mask;
+ sigfillset(&new_mask);
+ auto sigmask_rc = pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask);
+ assert(sigmask_rc == 0, "failed to set up GC scan thread sigmask");
+ }
+
+ for (int idx = 0; idx < numScanThreads; idx++)
+ scanThreadData[idx].tid = createLowLevelThread(&scanBackground, 0x4000, &stopScanThreads);
+
+ version (Posix)
+ {
+ sigmask_rc = pthread_sigmask(SIG_SETMASK, &old_mask, null);
+ assert(sigmask_rc == 0, "failed to set up GC scan thread sigmask");
+ }
+ }
+
+ void stopScanThreads() nothrow
+ {
+ if (!numScanThreads)
+ return;
+
+ debug(PARALLEL_PRINTF) printf("stopScanThreads\n");
+ int startedThreads = 0;
+ for (int idx = 0; idx < numScanThreads; idx++)
+ if (scanThreadData[idx].tid != scanThreadData[idx].tid.init)
+ startedThreads++;
+
+ version (Windows)
+ alias allThreadsDead = thread_DLLProcessDetaching;
+ else
+ enum allThreadsDead = false;
+ stopGC = true;
+ while (atomicLoad(stoppedThreads) < startedThreads && !allThreadsDead)
+ {
+ evStart.set();
+ evDone.wait(dur!"msecs"(1));
+ }
+
+ for (int idx = 0; idx < numScanThreads; idx++)
+ {
+ if (scanThreadData[idx].tid != scanThreadData[idx].tid.init)
+ {
+ joinLowLevelThread(scanThreadData[idx].tid);
+ scanThreadData[idx].tid = scanThreadData[idx].tid.init;
+ }
+ }
+
+ evDone.terminate();
+ evStart.terminate();
+
+ cstdlib.free(scanThreadData);
+ // scanThreadData = null; // keep non-null to not start again after shutdown
+ numScanThreads = 0;
+
+ debug(PARALLEL_PRINTF) printf("stopScanThreads done\n");
+ }
+
+ void scanBackground() nothrow
+ {
+ while (!stopGC)
+ {
+ evStart.wait();
+ pullFromScanStack();
+ evDone.set();
+ }
+ stoppedThreads.atomicOp!"+="(1);
+ }
+
+ void pullFromScanStack() nothrow
+ {
+ if (ConservativeGC.isPrecise)
+ pullFromScanStackImpl!true();
+ else
+ pullFromScanStackImpl!false();
+ }
+
+ void pullFromScanStackImpl(bool precise)() nothrow
+ {
+ if (atomicLoad(busyThreads) == 0)
+ return;
+
+ debug(PARALLEL_PRINTF)
+ pthread_t threadId = pthread_self();
+ debug(PARALLEL_PRINTF) printf("scanBackground thread %d start\n", threadId);
+
+ ScanRange!precise rng;
+ alias toscan = scanStack!precise;
+
+ while (atomicLoad(busyThreads) > 0)
+ {
+ if (toscan.empty)
+ {
+ evDone.wait(dur!"msecs"(1));
+ continue;
+ }
+
+ busyThreads.atomicOp!"+="(1);
+ if (toscan.popLocked(rng))
+ {
+ debug(PARALLEL_PRINTF) printf("scanBackground thread %d scanning range [%p,%lld] from stack\n", threadId,
+ rng.pbot, cast(long) (rng.ptop - rng.pbot));
+ mark!(precise, true, true)(rng);
+ }
+ busyThreads.atomicOp!"-="(1);
+ }
+ debug(PARALLEL_PRINTF) printf("scanBackground thread %d done\n", threadId);
+ }
+}
+
+/* ============================ Pool =============================== */
+
+struct Pool
+{
+ void* baseAddr;
+ void* topAddr;
+ size_t ptIndex; // index in pool table
+ GCBits mark; // entries already scanned, or should not be scanned
+ GCBits freebits; // entries that are on the free list (all bits set but for allocated objects at their base offset)
+ GCBits finals; // entries that need finalizer run on them
+ GCBits structFinals;// struct entries that need a finalzier run on them
+ GCBits noscan; // entries that should not be scanned
+ GCBits appendable; // entries that are appendable
+ GCBits nointerior; // interior pointers should be ignored.
+ // Only implemented for large object pools.
+ GCBits is_pointer; // precise GC only: per-word, not per-block like the rest of them (SmallObjectPool only)
+ size_t npages;
+ size_t freepages; // The number of pages not in use.
+ ubyte* pagetable;
+
+ bool isLargeObject;
+
+ enum ShiftBy
+ {
+ Small = 4,
+ Large = 12
+ }
+ ShiftBy shiftBy; // shift count for the divisor used for determining bit indices.
+
+ // This tracks how far back we have to go to find the nearest B_PAGE at
+ // a smaller address than a B_PAGEPLUS. To save space, we use a uint.
+ // This limits individual allocations to 16 terabytes, assuming a 4k
+ // pagesize. (LargeObjectPool only)
+ // For B_PAGE and B_FREE, this specifies the number of pages in this block.
+ // As an optimization, a contiguous range of free pages tracks this information
+ // only for the first and the last page.
+ uint* bPageOffsets;
+
+ // The small object pool uses the same array to keep a chain of
+ // - pages with the same bin size that are still to be recovered
+ // - free pages (searchStart is first free page)
+ // other pages are marked by value PageRecovered
+ alias binPageChain = bPageOffsets;
+
+ enum PageRecovered = uint.max;
+
+ // first of chain of pages to recover (SmallObjectPool only)
+ uint[B_NUMSMALL] recoverPageFirst;
+
+ // precise GC: TypeInfo.rtInfo for allocation (LargeObjectPool only)
+ immutable(size_t)** rtinfo;
+
+ // This variable tracks a conservative estimate of where the first free
+ // page in this pool is, so that if a lot of pages towards the beginning
+ // are occupied, we can bypass them in O(1).
+ size_t searchStart;
+ size_t largestFree; // upper limit for largest free chunk in large object pool
+
+ void initialize(size_t npages, bool isLargeObject) nothrow
+ {
+ assert(npages >= 256);
+
+ this.isLargeObject = isLargeObject;
+ size_t poolsize;
+
+ shiftBy = isLargeObject ? ShiftBy.Large : ShiftBy.Small;
+
+ //debug(PRINTF) printf("Pool::Pool(%u)\n", npages);
+ poolsize = npages * PAGESIZE;
+ baseAddr = cast(byte *)os_mem_map(poolsize);
+
+ // Some of the code depends on page alignment of memory pools
+ assert((cast(size_t)baseAddr & (PAGESIZE - 1)) == 0);
+
+ if (!baseAddr)
+ {
+ //debug(PRINTF) printf("GC fail: poolsize = x%zx, errno = %d\n", poolsize, errno);
+ //debug(PRINTF) printf("message = '%s'\n", sys_errlist[errno]);
+
+ npages = 0;
+ poolsize = 0;
+ }
+ //assert(baseAddr);
+ topAddr = baseAddr + poolsize;
+ auto nbits = cast(size_t)poolsize >> shiftBy;
+
+ version (COLLECT_FORK)
+ mark.alloc(nbits, config.fork);
+ else
+ mark.alloc(nbits);
+ if (ConservativeGC.isPrecise)
+ {
+ if (isLargeObject)
+ {
+ rtinfo = cast(immutable(size_t)**)cstdlib.malloc(npages * (size_t*).sizeof);
+ if (!rtinfo)
+ onOutOfMemoryErrorNoGC();
+ memset(rtinfo, 0, npages * (size_t*).sizeof);
+ }
+ else
+ {
+ is_pointer.alloc(cast(size_t)poolsize/(void*).sizeof);
+ is_pointer.clrRange(0, is_pointer.nbits);
+ }
+ }
+
+ // pagetable already keeps track of what's free for the large object
+ // pool.
+ if (!isLargeObject)
+ {
+ freebits.alloc(nbits);
+ freebits.setRange(0, nbits);
+ }
+
+ noscan.alloc(nbits);
+ appendable.alloc(nbits);
+
+ pagetable = cast(ubyte*)cstdlib.malloc(npages);
+ if (!pagetable)
+ onOutOfMemoryErrorNoGC();
+
+ if (npages > 0)
+ {
+ bPageOffsets = cast(uint*)cstdlib.malloc(npages * uint.sizeof);
+ if (!bPageOffsets)
+ onOutOfMemoryErrorNoGC();
+
+ if (isLargeObject)
+ {
+ bPageOffsets[0] = cast(uint)npages;
+ bPageOffsets[npages-1] = cast(uint)npages;
+ }
+ else
+ {
+ // all pages free
+ foreach (n; 0..npages)
+ binPageChain[n] = cast(uint)(n + 1);
+ recoverPageFirst[] = cast(uint)npages;
+ }
+ }
+
+ memset(pagetable, B_FREE, npages);
+
+ this.npages = npages;
+ this.freepages = npages;
+ this.searchStart = 0;
+ this.largestFree = npages;
+ }
+
+
+ void Dtor() nothrow
+ {
+ if (baseAddr)
+ {
+ int result;
+
+ if (npages)
+ {
+ result = os_mem_unmap(baseAddr, npages * PAGESIZE);
+ assert(result == 0);
+ npages = 0;
+ }
+
+ baseAddr = null;
+ topAddr = null;
+ }
+ if (pagetable)
+ {
+ cstdlib.free(pagetable);
+ pagetable = null;
+ }
+
+ if (bPageOffsets)
+ {
+ cstdlib.free(bPageOffsets);
+ bPageOffsets = null;
+ }
+
+ mark.Dtor(config.fork);
+ if (ConservativeGC.isPrecise)
+ {
+ if (isLargeObject)
+ cstdlib.free(rtinfo);
+ else
+ is_pointer.Dtor();
+ }
+ if (isLargeObject)
+ {
+ nointerior.Dtor();
+ }
+ else
+ {
+ freebits.Dtor();
+ }
+ finals.Dtor();
+ structFinals.Dtor();
+ noscan.Dtor();
+ appendable.Dtor();
+ }
+
+ /**
+ *
+ */
+ uint getBits(size_t biti) nothrow
+ {
+ uint bits;
+
+ if (finals.nbits && finals.test(biti))
+ bits |= BlkAttr.FINALIZE;
+ if (structFinals.nbits && structFinals.test(biti))
+ bits |= BlkAttr.STRUCTFINAL;
+ if (noscan.test(biti))
+ bits |= BlkAttr.NO_SCAN;
+ if (nointerior.nbits && nointerior.test(biti))
+ bits |= BlkAttr.NO_INTERIOR;
+ if (appendable.test(biti))
+ bits |= BlkAttr.APPENDABLE;
+ return bits;
+ }
+
+ /**
+ *
+ */
+ void clrBits(size_t biti, uint mask) nothrow @nogc
+ {
+ immutable dataIndex = biti >> GCBits.BITS_SHIFT;
+ immutable bitOffset = biti & GCBits.BITS_MASK;
+ immutable keep = ~(GCBits.BITS_1 << bitOffset);
+
+ if (mask & BlkAttr.FINALIZE && finals.nbits)
+ finals.data[dataIndex] &= keep;
+
+ if (structFinals.nbits && (mask & BlkAttr.STRUCTFINAL))
+ structFinals.data[dataIndex] &= keep;
+
+ if (mask & BlkAttr.NO_SCAN)
+ noscan.data[dataIndex] &= keep;
+ if (mask & BlkAttr.APPENDABLE)
+ appendable.data[dataIndex] &= keep;
+ if (nointerior.nbits && (mask & BlkAttr.NO_INTERIOR))
+ nointerior.data[dataIndex] &= keep;
+ }
+
+ /**
+ *
+ */
+ void setBits(size_t biti, uint mask) nothrow
+ {
+ // Calculate the mask and bit offset once and then use it to
+ // set all of the bits we need to set.
+ immutable dataIndex = biti >> GCBits.BITS_SHIFT;
+ immutable bitOffset = biti & GCBits.BITS_MASK;
+ immutable orWith = GCBits.BITS_1 << bitOffset;
+
+ if (mask & BlkAttr.STRUCTFINAL)
+ {
+ if (!structFinals.nbits)
+ structFinals.alloc(mark.nbits);
+ structFinals.data[dataIndex] |= orWith;
+ }
+
+ if (mask & BlkAttr.FINALIZE)
+ {
+ if (!finals.nbits)
+ finals.alloc(mark.nbits);
+ finals.data[dataIndex] |= orWith;
+ }
+
+ if (mask & BlkAttr.NO_SCAN)
+ {
+ noscan.data[dataIndex] |= orWith;
+ }
+// if (mask & BlkAttr.NO_MOVE)
+// {
+// if (!nomove.nbits)
+// nomove.alloc(mark.nbits);
+// nomove.data[dataIndex] |= orWith;
+// }
+ if (mask & BlkAttr.APPENDABLE)
+ {
+ appendable.data[dataIndex] |= orWith;
+ }
+
+ if (isLargeObject && (mask & BlkAttr.NO_INTERIOR))
+ {
+ if (!nointerior.nbits)
+ nointerior.alloc(mark.nbits);
+ nointerior.data[dataIndex] |= orWith;
+ }
+ }
+
+ void freePageBits(size_t pagenum, const scope ref PageBits toFree) nothrow
+ {
+ assert(!isLargeObject);
+ assert(!nointerior.nbits); // only for large objects
+
+ import core.internal.traits : staticIota;
+ immutable beg = pagenum * (PAGESIZE / 16 / GCBits.BITS_PER_WORD);
+ foreach (i; staticIota!(0, PageBits.length))
+ {
+ immutable w = toFree[i];
+ if (!w) continue;
+
+ immutable wi = beg + i;
+ freebits.data[wi] |= w;
+ noscan.data[wi] &= ~w;
+ appendable.data[wi] &= ~w;
+ }
+
+ if (finals.nbits)
+ {
+ foreach (i; staticIota!(0, PageBits.length))
+ if (toFree[i])
+ finals.data[beg + i] &= ~toFree[i];
+ }
+
+ if (structFinals.nbits)
+ {
+ foreach (i; staticIota!(0, PageBits.length))
+ if (toFree[i])
+ structFinals.data[beg + i] &= ~toFree[i];
+ }
+ }
+
+ void freeAllPageBits(size_t pagenum) nothrow
+ {
+ assert(!isLargeObject);
+ assert(!nointerior.nbits); // only for large objects
+
+ immutable beg = pagenum * PageBits.length;
+ static foreach (i; 0 .. PageBits.length)
+ {{
+ immutable w = beg + i;
+ freebits.data[w] = ~0;
+ noscan.data[w] = 0;
+ appendable.data[w] = 0;
+ if (finals.data)
+ finals.data[w] = 0;
+ if (structFinals.data)
+ structFinals.data[w] = 0;
+ }}
+ }
+
+ /**
+ * Given a pointer p in the p, return the pagenum.
+ */
+ size_t pagenumOf(void *p) const nothrow @nogc
+ in
+ {
+ assert(p >= baseAddr);
+ assert(p < topAddr);
+ }
+ do
+ {
+ return cast(size_t)(p - baseAddr) / PAGESIZE;
+ }
+
+ public
+ @property bool isFree() const pure nothrow
+ {
+ return npages == freepages;
+ }
+
+ /**
+ * Return number of pages necessary for an allocation of the given size
+ *
+ * returns size_t.max if more than uint.max pages are requested
+ * (return type is still size_t to avoid truncation when being used
+ * in calculations, e.g. npages * PAGESIZE)
+ */
+ static size_t numPages(size_t size) nothrow @nogc
+ {
+ version (D_LP64)
+ {
+ if (size > PAGESIZE * cast(size_t)uint.max)
+ return size_t.max;
+ }
+ else
+ {
+ if (size > size_t.max - PAGESIZE)
+ return size_t.max;
+ }
+ return (size + PAGESIZE - 1) / PAGESIZE;
+ }
+
+ void* findBase(void* p) nothrow @nogc
+ {
+ size_t offset = cast(size_t)(p - baseAddr);
+ size_t pn = offset / PAGESIZE;
+ Bins bin = cast(Bins)pagetable[pn];
+
+ // Adjust bit to be at start of allocated memory block
+ if (bin < B_NUMSMALL)
+ {
+ auto baseOff = baseOffset(offset, bin);
+ const biti = baseOff >> Pool.ShiftBy.Small;
+ if (freebits.test (biti))
+ return null;
+ return baseAddr + baseOff;
+ }
+ if (bin == B_PAGE)
+ {
+ return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
+ }
+ if (bin == B_PAGEPLUS)
+ {
+ size_t pageOffset = bPageOffsets[pn];
+ offset -= pageOffset * PAGESIZE;
+ pn -= pageOffset;
+
+ return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
+ }
+ // we are in a B_FREE page
+ assert(bin == B_FREE);
+ return null;
+ }
+
+ size_t slGetSize(void* p) nothrow @nogc
+ {
+ if (isLargeObject)
+ return (cast(LargeObjectPool*)&this).getPages(p) * PAGESIZE;
+ else
+ return (cast(SmallObjectPool*)&this).getSize(p);
+ }
+
+ BlkInfo slGetInfo(void* p) nothrow
+ {
+ if (isLargeObject)
+ return (cast(LargeObjectPool*)&this).getInfo(p);
+ else
+ return (cast(SmallObjectPool*)&this).getInfo(p);
+ }
+
+
+ void Invariant() const {}
+
+ debug(INVARIANT)
+ invariant()
+ {
+ if (baseAddr)
+ {
+ //if (baseAddr + npages * PAGESIZE != topAddr)
+ //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr);
+ assert(baseAddr + npages * PAGESIZE == topAddr);
+ }
+
+ if (pagetable !is null)
+ {
+ for (size_t i = 0; i < npages; i++)
+ {
+ Bins bin = cast(Bins)pagetable[i];
+ assert(bin < B_MAX);
+ }
+ }
+ }
+
+ void setPointerBitmapSmall(void* p, size_t s, size_t allocSize, uint attr, const TypeInfo ti) nothrow
+ {
+ if (!(attr & BlkAttr.NO_SCAN))
+ setPointerBitmap(p, s, allocSize, ti, attr);
+ }
+
+ pragma(inline,false)
+ void setPointerBitmap(void* p, size_t s, size_t allocSize, const TypeInfo ti, uint attr) nothrow
+ {
+ size_t offset = p - baseAddr;
+ //debug(PRINTF) printGCBits(&pool.is_pointer);
+
+ debug(PRINTF)
+ printf("Setting a pointer bitmap for %s at %p + %llu\n", debugTypeName(ti).ptr, p, cast(ulong)s);
+
+ if (ti)
+ {
+ if (attr & BlkAttr.APPENDABLE)
+ {
+ // an array of classes is in fact an array of pointers
+ if (typeid(ti) is typeid(TypeInfo_Class))
+ goto L_conservative;
+ s = allocSize;
+ }
+
+ auto rtInfo = cast(const(size_t)*)ti.rtInfo();
+
+ if (rtInfo is rtinfoNoPointers)
+ {
+ debug(PRINTF) printf("\tCompiler generated rtInfo: no pointers\n");
+ is_pointer.clrRange(offset/(void*).sizeof, s/(void*).sizeof);
+ }
+ else if (rtInfo is rtinfoHasPointers)
+ {
+ debug(PRINTF) printf("\tCompiler generated rtInfo: has pointers\n");
+ is_pointer.setRange(offset/(void*).sizeof, s/(void*).sizeof);
+ }
+ else
+ {
+ const(size_t)* bitmap = cast (size_t*) rtInfo;
+ //first element of rtInfo is the size of the object the bitmap encodes
+ size_t element_size = * bitmap;
+ bitmap++;
+ size_t tocopy;
+ if (attr & BlkAttr.APPENDABLE)
+ {
+ tocopy = s/(void*).sizeof;
+ is_pointer.copyRangeRepeating(offset/(void*).sizeof, tocopy, bitmap, element_size/(void*).sizeof);
+ }
+ else
+ {
+ tocopy = (s < element_size ? s : element_size)/(void*).sizeof;
+ is_pointer.copyRange(offset/(void*).sizeof, tocopy, bitmap);
+ }
+
+ debug(PRINTF) printf("\tSetting bitmap for new object (%s)\n\t\tat %p\t\tcopying from %p + %llu: ",
+ debugTypeName(ti).ptr, p, bitmap, cast(ulong)element_size);
+ debug(PRINTF)
+ for (size_t i = 0; i < element_size/((void*).sizeof); i++)
+ printf("%d", (bitmap[i/(8*size_t.sizeof)] >> (i%(8*size_t.sizeof))) & 1);
+ debug(PRINTF) printf("\n");
+
+ if (tocopy * (void*).sizeof < s) // better safe than sorry: if allocated more, assume pointers inside
+ {
+ debug(PRINTF) printf(" Appending %d pointer bits\n", s/(void*).sizeof - tocopy);
+ is_pointer.setRange(offset/(void*).sizeof + tocopy, s/(void*).sizeof - tocopy);
+ }
+ }
+
+ if (s < allocSize)
+ {
+ offset = (offset + s + (void*).sizeof - 1) & ~((void*).sizeof - 1);
+ is_pointer.clrRange(offset/(void*).sizeof, (allocSize - s)/(void*).sizeof);
+ }
+ }
+ else
+ {
+ L_conservative:
+ // limit pointers to actual size of allocation? might fail for arrays that append
+ // without notifying the GC
+ s = allocSize;
+
+ debug(PRINTF) printf("Allocating a block without TypeInfo\n");
+ is_pointer.setRange(offset/(void*).sizeof, s/(void*).sizeof);
+ }
+ //debug(PRINTF) printGCBits(&pool.is_pointer);
+ }
+}
+
+struct LargeObjectPool
+{
+ Pool base;
+ alias base this;
+
+ debug(INVARIANT)
+ void Invariant()
+ {
+ //base.Invariant();
+ for (size_t n = 0; n < npages; )
+ {
+ uint np = bPageOffsets[n];
+ assert(np > 0 && np <= npages - n);
+
+ if (pagetable[n] == B_PAGE)
+ {
+ for (uint p = 1; p < np; p++)
+ {
+ assert(pagetable[n + p] == B_PAGEPLUS);
+ assert(bPageOffsets[n + p] == p);
+ }
+ }
+ else if (pagetable[n] == B_FREE)
+ {
+ for (uint p = 1; p < np; p++)
+ {
+ assert(pagetable[n + p] == B_FREE);
+ }
+ assert(bPageOffsets[n + np - 1] == np);
+ }
+ else
+ assert(false);
+ n += np;
+ }
+ }
+
+ /**
+ * Allocate n pages from Pool.
+ * Returns OPFAIL on failure.
+ */
+ size_t allocPages(size_t n) nothrow
+ {
+ if (largestFree < n || searchStart + n > npages)
+ return OPFAIL;
+
+ //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n);
+ size_t largest = 0;
+ if (pagetable[searchStart] == B_PAGEPLUS)
+ {
+ searchStart -= bPageOffsets[searchStart]; // jump to B_PAGE
+ searchStart += bPageOffsets[searchStart];
+ }
+ while (searchStart < npages && pagetable[searchStart] == B_PAGE)
+ searchStart += bPageOffsets[searchStart];
+
+ for (size_t i = searchStart; i < npages; )
+ {
+ assert(pagetable[i] == B_FREE);
+
+ auto p = bPageOffsets[i];
+ if (p > n)
+ {
+ setFreePageOffsets(i + n, p - n);
+ goto L_found;
+ }
+ if (p == n)
+ {
+ L_found:
+ pagetable[i] = B_PAGE;
+ bPageOffsets[i] = cast(uint) n;
+ if (n > 1)
+ {
+ memset(&pagetable[i + 1], B_PAGEPLUS, n - 1);
+ for (auto offset = 1; offset < n; offset++)
+ bPageOffsets[i + offset] = cast(uint) offset;
+ }
+ freepages -= n;
+ return i;
+ }
+ if (p > largest)
+ largest = p;
+
+ i += p;
+ while (i < npages && pagetable[i] == B_PAGE)
+ {
+ // we have the size information, so we skip a whole bunch of pages.
+ i += bPageOffsets[i];
+ }
+ }
+
+ // not enough free pages found, remember largest free chunk
+ largestFree = largest;
+ return OPFAIL;
+ }
+
+ /**
+ * Free npages pages starting with pagenum.
+ */
+ void freePages(size_t pagenum, size_t npages) nothrow @nogc
+ {
+ //memset(&pagetable[pagenum], B_FREE, npages);
+ if (pagenum < searchStart)
+ searchStart = pagenum;
+
+ for (size_t i = pagenum; i < npages + pagenum; i++)
+ {
+ assert(pagetable[i] < B_FREE);
+ pagetable[i] = B_FREE;
+ }
+ freepages += npages;
+ largestFree = freepages; // invalidate
+ }
+
+ /**
+ * Set the first and the last entry of a B_FREE block to the size
+ */
+ void setFreePageOffsets(size_t page, size_t num) nothrow @nogc
+ {
+ assert(pagetable[page] == B_FREE);
+ assert(pagetable[page + num - 1] == B_FREE);
+ bPageOffsets[page] = cast(uint)num;
+ if (num > 1)
+ bPageOffsets[page + num - 1] = cast(uint)num;
+ }
+
+ void mergeFreePageOffsets(bool bwd, bool fwd)(size_t page, size_t num) nothrow @nogc
+ {
+ static if (bwd)
+ {
+ if (page > 0 && pagetable[page - 1] == B_FREE)
+ {
+ auto sz = bPageOffsets[page - 1];
+ page -= sz;
+ num += sz;
+ }
+ }
+ static if (fwd)
+ {
+ if (page + num < npages && pagetable[page + num] == B_FREE)
+ num += bPageOffsets[page + num];
+ }
+ setFreePageOffsets(page, num);
+ }
+
+ /**
+ * Get pages of allocation at pointer p in pool.
+ */
+ size_t getPages(void *p) const nothrow @nogc
+ in
+ {
+ assert(p >= baseAddr);
+ assert(p < topAddr);
+ }
+ do
+ {
+ if (cast(size_t)p & (PAGESIZE - 1)) // check for interior pointer
+ return 0;
+ size_t pagenum = pagenumOf(p);
+ Bins bin = cast(Bins)pagetable[pagenum];
+ if (bin != B_PAGE)
+ return 0;
+ return bPageOffsets[pagenum];
+ }
+
+ /**
+ * Get size of allocation at page pn in pool.
+ */
+ size_t getSize(size_t pn) const nothrow @nogc
+ {
+ assert(pagetable[pn] == B_PAGE);
+ return cast(size_t) bPageOffsets[pn] * PAGESIZE;
+ }
+
+ /**
+ *
+ */
+ BlkInfo getInfo(void* p) nothrow
+ {
+ BlkInfo info;
+
+ size_t offset = cast(size_t)(p - baseAddr);
+ size_t pn = offset / PAGESIZE;
+ Bins bin = cast(Bins)pagetable[pn];
+
+ if (bin == B_PAGEPLUS)
+ pn -= bPageOffsets[pn];
+ else if (bin != B_PAGE)
+ return info; // no info for free pages
+
+ info.base = baseAddr + pn * PAGESIZE;
+ info.size = getSize(pn);
+ info.attr = getBits(pn);
+ return info;
+ }
+
+ void runFinalizers(const scope void[] segment) nothrow
+ {
+ foreach (pn; 0 .. npages)
+ {
+ Bins bin = cast(Bins)pagetable[pn];
+ if (bin > B_PAGE)
+ continue;
+ size_t biti = pn;
+
+ if (!finals.test(biti))
+ continue;
+
+ auto p = sentinel_add(baseAddr + pn * PAGESIZE);
+ size_t size = sentinel_size(p, getSize(pn));
+ uint attr = getBits(biti);
+
+ if (!rt_hasFinalizerInSegment(p, size, attr, segment))
+ continue;
+
+ rt_finalizeFromGC(p, size, attr);
+
+ clrBits(biti, ~BlkAttr.NONE);
+
+ if (pn < searchStart)
+ searchStart = pn;
+
+ debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
+ //log_free(sentinel_add(p));
+
+ size_t n = 1;
+ for (; pn + n < npages; ++n)
+ if (pagetable[pn + n] != B_PAGEPLUS)
+ break;
+ debug (MEMSTOMP) memset(baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE);
+ freePages(pn, n);
+ mergeFreePageOffsets!(true, true)(pn, n);
+ }
+ }
+}
+
+
+struct SmallObjectPool
+{
+ Pool base;
+ alias base this;
+
+ debug(INVARIANT)
+ void Invariant()
+ {
+ //base.Invariant();
+ uint cntRecover = 0;
+ foreach (Bins bin; 0 .. B_NUMSMALL)
+ {
+ for (auto pn = recoverPageFirst[bin]; pn < npages; pn = binPageChain[pn])
+ {
+ assert(pagetable[pn] == bin);
+ cntRecover++;
+ }
+ }
+ uint cntFree = 0;
+ for (auto pn = searchStart; pn < npages; pn = binPageChain[pn])
+ {
+ assert(pagetable[pn] == B_FREE);
+ cntFree++;
+ }
+ assert(cntFree == freepages);
+ assert(cntFree + cntRecover <= npages);
+ }
+
+ /**
+ * Get size of pointer p in pool.
+ */
+ size_t getSize(void *p) const nothrow @nogc
+ in
+ {
+ assert(p >= baseAddr);
+ assert(p < topAddr);
+ }
+ do
+ {
+ size_t pagenum = pagenumOf(p);
+ Bins bin = cast(Bins)pagetable[pagenum];
+ assert(bin < B_PAGE);
+ if (p != cast(void*)baseOffset(cast(size_t)p, bin)) // check for interior pointer
+ return 0;
+ const biti = cast(size_t)(p - baseAddr) >> ShiftBy.Small;
+ if (freebits.test (biti))
+ return 0;
+ return binsize[bin];
+ }
+
+ BlkInfo getInfo(void* p) nothrow
+ {
+ BlkInfo info;
+ size_t offset = cast(size_t)(p - baseAddr);
+ size_t pn = offset / PAGESIZE;
+ Bins bin = cast(Bins)pagetable[pn];
+
+ if (bin >= B_PAGE)
+ return info;
+
+ auto base = cast(void*)baseOffset(cast(size_t)p, bin);
+ const biti = cast(size_t)(base - baseAddr) >> ShiftBy.Small;
+ if (freebits.test (biti))
+ return info;
+
+ info.base = base;
+ info.size = binsize[bin];
+ offset = info.base - baseAddr;
+ info.attr = getBits(biti);
+
+ return info;
+ }
+
+ void runFinalizers(const scope void[] segment) nothrow
+ {
+ foreach (pn; 0 .. npages)
+ {
+ Bins bin = cast(Bins)pagetable[pn];
+ if (bin >= B_PAGE)
+ continue;
+
+ immutable size = binsize[bin];
+ auto p = baseAddr + pn * PAGESIZE;
+ const ptop = p + PAGESIZE - size + 1;
+ immutable base = pn * (PAGESIZE/16);
+ immutable bitstride = size / 16;
+
+ bool freeBits;
+ PageBits toFree;
+
+ for (size_t i; p < ptop; p += size, i += bitstride)
+ {
+ immutable biti = base + i;
+
+ if (!finals.test(biti))
+ continue;
+
+ auto q = sentinel_add(p);
+ uint attr = getBits(biti);
+ const ssize = sentinel_size(q, size);
+ if (!rt_hasFinalizerInSegment(q, ssize, attr, segment))
+ continue;
+
+ rt_finalizeFromGC(q, ssize, attr);
+
+ freeBits = true;
+ toFree.set(i);
+
+ debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p);
+ //log_free(sentinel_add(p));
+
+ debug (MEMSTOMP) memset(p, 0xF3, size);
+ }
+
+ if (freeBits)
+ freePageBits(pn, toFree);
+ }
+ }
+
+ /**
+ * Allocate a page of bin's.
+ * Returns:
+ * head of a single linked list of new entries
+ */
+ List* allocPage(Bins bin) nothrow
+ {
+ if (searchStart >= npages)
+ return null;
+
+ assert(pagetable[searchStart] == B_FREE);
+
+ L1:
+ size_t pn = searchStart;
+ searchStart = binPageChain[searchStart];
+ binPageChain[pn] = Pool.PageRecovered;
+ pagetable[pn] = cast(ubyte)bin;
+ freepages--;
+
+ // Convert page to free list
+ size_t size = binsize[bin];
+ void* p = baseAddr + pn * PAGESIZE;
+ auto first = cast(List*) p;
+
+ // ensure 2 <size> bytes blocks are available below ptop, one
+ // being set in the loop, and one for the tail block
+ void* ptop = p + PAGESIZE - 2 * size + 1;
+ for (; p < ptop; p += size)
+ {
+ (cast(List *)p).next = cast(List *)(p + size);
+ (cast(List *)p).pool = &base;
+ }
+ (cast(List *)p).next = null;
+ (cast(List *)p).pool = &base;
+ return first;
+ }
+}
+
+debug(SENTINEL) {} else // no additional capacity with SENTINEL
+unittest // bugzilla 14467
+{
+ int[] arr = new int[10];
+ assert(arr.capacity);
+ arr = arr[$..$];
+ assert(arr.capacity);
+}
+
+unittest // bugzilla 15353
+{
+ import core.memory : GC;
+
+ static struct Foo
+ {
+ ~this()
+ {
+ GC.free(buf); // ignored in finalizer
+ }
+
+ void* buf;
+ }
+ new Foo(GC.malloc(10));
+ GC.collect();
+}
+
+unittest // bugzilla 15822
+{
+ import core.memory : GC;
+
+ __gshared ubyte[16] buf;
+ static struct Foo
+ {
+ ~this()
+ {
+ GC.removeRange(ptr);
+ GC.removeRoot(ptr);
+ }
+
+ ubyte* ptr;
+ }
+ GC.addRoot(buf.ptr);
+ GC.addRange(buf.ptr, buf.length);
+ new Foo(buf.ptr);
+ GC.collect();
+}
+
+unittest // bugzilla 1180
+{
+ import core.exception;
+ try
+ {
+ size_t x = size_t.max - 100;
+ byte[] big_buf = new byte[x];
+ }
+ catch (OutOfMemoryError)
+ {
+ }
+}
+
+/* ============================ PRINTF =============================== */
+
+debug(PRINTF_TO_FILE)
+{
+ private __gshared MonoTime gcStartTick;
+ private __gshared FILE* gcx_fh;
+ private __gshared bool hadNewline = false;
+ import core.internal.spinlock;
+ static printLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy);
+
+ private int printf(ARGS...)(const char* fmt, ARGS args) nothrow
+ {
+ printLock.lock();
+ scope(exit) printLock.unlock();
+
+ if (!gcx_fh)
+ gcx_fh = fopen("gcx.log", "w");
+ if (!gcx_fh)
+ return 0;
+
+ int len;
+ if (MonoTime.ticksPerSecond == 0)
+ {
+ len = fprintf(gcx_fh, "before init: ");
+ }
+ else if (hadNewline)
+ {
+ if (gcStartTick == MonoTime.init)
+ gcStartTick = MonoTime.currTime;
+ immutable timeElapsed = MonoTime.currTime - gcStartTick;
+ immutable secondsAsDouble = timeElapsed.total!"hnsecs" / cast(double)convert!("seconds", "hnsecs")(1);
+ len = fprintf(gcx_fh, "%10.6f: ", secondsAsDouble);
+ }
+ len += fprintf(gcx_fh, fmt, args);
+ fflush(gcx_fh);
+ import core.stdc.string;
+ hadNewline = fmt && fmt[0] && fmt[strlen(fmt) - 1] == '\n';
+ return len;
+ }
+}
+
+debug(PRINTF) void printFreeInfo(Pool* pool) nothrow
+{
+ uint nReallyFree;
+ foreach (i; 0..pool.npages) {
+ if (pool.pagetable[i] >= B_FREE) nReallyFree++;
+ }
+
+ printf("Pool %p: %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages);
+}
+
+debug(PRINTF)
+void printGCBits(GCBits* bits)
+{
+ for (size_t i = 0; i < bits.nwords; i++)
+ {
+ if (i % 32 == 0) printf("\n\t");
+ printf("%x ", bits.data[i]);
+ }
+ printf("\n");
+}
+
+// we can assume the name is always from a literal, so it is zero terminated
+debug(PRINTF)
+string debugTypeName(const(TypeInfo) ti) nothrow
+{
+ string name;
+ if (ti is null)
+ name = "null";
+ else if (auto ci = cast(TypeInfo_Class)ti)
+ name = ci.name;
+ else if (auto si = cast(TypeInfo_Struct)ti)
+ name = si.mangledName; // .name() might GC-allocate, avoid deadlock
+ else if (auto ci = cast(TypeInfo_Const)ti)
+ static if (__traits(compiles,ci.base)) // different whether compiled with object.di or object.d
+ return debugTypeName(ci.base);
+ else
+ return debugTypeName(ci.next);
+ else
+ name = ti.classinfo.name;
+ return name;
+}
+
+/* ======================= Leak Detector =========================== */
+
+debug (LOGGING)
+{
+ struct Log
+ {
+ void* p;
+ size_t size;
+ size_t line;
+ char* file;
+ void* parent;
+
+ void print() nothrow
+ {
+ printf(" p = %p, size = %lld, parent = %p ", p, cast(ulong)size, parent);
+ if (file)
+ {
+ printf("%s(%u)", file, cast(uint)line);
+ }
+ printf("\n");
+ }
+ }
+
+
+ struct LogArray
+ {
+ size_t dim;
+ size_t allocdim;
+ Log *data;
+
+ void Dtor() nothrow @nogc
+ {
+ if (data)
+ cstdlib.free(data);
+ data = null;
+ }
+
+ void reserve(size_t nentries) nothrow @nogc
+ {
+ assert(dim <= allocdim);
+ if (allocdim - dim < nentries)
+ {
+ allocdim = (dim + nentries) * 2;
+ assert(dim + nentries <= allocdim);
+ if (!data)
+ {
+ data = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
+ if (!data && allocdim)
+ onOutOfMemoryErrorNoGC();
+ }
+ else
+ { Log *newdata;
+
+ newdata = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
+ if (!newdata && allocdim)
+ onOutOfMemoryErrorNoGC();
+ memcpy(newdata, data, dim * Log.sizeof);
+ cstdlib.free(data);
+ data = newdata;
+ }
+ }
+ }
+
+
+ void push(Log log) nothrow @nogc
+ {
+ reserve(1);
+ data[dim++] = log;
+ }
+
+ void remove(size_t i) nothrow @nogc
+ {
+ memmove(data + i, data + i + 1, (dim - i) * Log.sizeof);
+ dim--;
+ }
+
+
+ size_t find(void *p) nothrow @nogc
+ {
+ for (size_t i = 0; i < dim; i++)
+ {
+ if (data[i].p == p)
+ return i;
+ }
+ return OPFAIL; // not found
+ }
+
+
+ void copy(LogArray *from) nothrow @nogc
+ {
+ if (allocdim < from.dim)
+ reserve(from.dim - dim);
+ assert(from.dim <= allocdim);
+ memcpy(data, from.data, from.dim * Log.sizeof);
+ dim = from.dim;
+ }
+ }
+
+ struct LeakDetector
+ {
+ Gcx* gcx;
+ LogArray current;
+ LogArray prev;
+
+ private void initialize(Gcx* gc)
+ {
+ gcx = gc;
+ //debug(PRINTF) printf("+log_init()\n");
+ current.reserve(1000);
+ prev.reserve(1000);
+ //debug(PRINTF) printf("-log_init()\n");
+ }
+
+
+ private void log_malloc(void *p, size_t size) nothrow
+ {
+ //debug(PRINTF) printf("+log_malloc(p = %p, size = %zd)\n", p, size);
+ Log log;
+
+ log.p = p;
+ log.size = size;
+ log.line = ConservativeGC.line;
+ log.file = ConservativeGC.file;
+ log.parent = null;
+
+ ConservativeGC.line = 0;
+ ConservativeGC.file = null;
+
+ current.push(log);
+ //debug(PRINTF) printf("-log_malloc()\n");
+ }
+
+
+ private void log_free(void *p, size_t size) nothrow @nogc
+ {
+ //debug(PRINTF) printf("+log_free(%p)\n", p);
+ auto i = current.find(p);
+ if (i == OPFAIL)
+ {
+ debug(PRINTF) printf("free'ing unallocated memory %p (size %zu)\n", p, size);
+ }
+ else
+ current.remove(i);
+ //debug(PRINTF) printf("-log_free()\n");
+ }
+
+
+ private void log_collect() nothrow
+ {
+ //debug(PRINTF) printf("+log_collect()\n");
+ // Print everything in current that is not in prev
+
+ debug(PRINTF) printf("New pointers this cycle: --------------------------------\n");
+ size_t used = 0;
+ for (size_t i = 0; i < current.dim; i++)
+ {
+ auto j = prev.find(current.data[i].p);
+ if (j == OPFAIL)
+ current.data[i].print();
+ else
+ used++;
+ }
+
+ debug(PRINTF) printf("All roots this cycle: --------------------------------\n");
+ for (size_t i = 0; i < current.dim; i++)
+ {
+ void* p = current.data[i].p;
+ if (!gcx.findPool(current.data[i].parent))
+ {
+ auto j = prev.find(current.data[i].p);
+ debug(PRINTF) printf(j == OPFAIL ? "N" : " ");
+ current.data[i].print();
+ }
+ }
+
+ debug(PRINTF) printf("Used = %d-------------------------------------------------\n", used);
+ prev.copy(&current);
+
+ debug(PRINTF) printf("-log_collect()\n");
+ }
+
+
+ private void log_parent(void *p, void *parent) nothrow
+ {
+ //debug(PRINTF) printf("+log_parent()\n");
+ auto i = current.find(p);
+ if (i == OPFAIL)
+ {
+ debug(PRINTF) printf("parent'ing unallocated memory %p, parent = %p\n", p, parent);
+ Pool *pool;
+ pool = gcx.findPool(p);
+ assert(pool);
+ size_t offset = cast(size_t)(p - pool.baseAddr);
+ size_t biti;
+ size_t pn = offset / PAGESIZE;
+ Bins bin = cast(Bins)pool.pagetable[pn];
+ biti = (offset & (PAGESIZE - 1)) >> pool.shiftBy;
+ debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti);
+ }
+ else
+ {
+ current.data[i].parent = parent;
+ }
+ //debug(PRINTF) printf("-log_parent()\n");
+ }
+ }
+}
+else
+{
+ struct LeakDetector
+ {
+ static void initialize(Gcx* gcx) nothrow { }
+ static void log_malloc(void *p, size_t size) nothrow { }
+ static void log_free(void *p, size_t size) nothrow @nogc {}
+ static void log_collect() nothrow { }
+ static void log_parent(void *p, void *parent) nothrow { }
+ }
+}
+
+/* ============================ SENTINEL =============================== */
+
+debug (SENTINEL)
+{
+ // pre-sentinel must be smaller than 16 bytes so that the same GC bits
+ // are used for the allocated pointer and the user pointer
+ // so use uint for both 32 and 64 bit platforms, limiting usage to < 4GB
+ const uint SENTINEL_PRE = 0xF4F4F4F4;
+ const ubyte SENTINEL_POST = 0xF5; // 8 bits
+ const uint SENTINEL_EXTRA = 2 * uint.sizeof + 1;
+
+
+ inout(uint*) sentinel_psize(inout void *p) nothrow @nogc { return &(cast(inout uint *)p)[-2]; }
+ inout(uint*) sentinel_pre(inout void *p) nothrow @nogc { return &(cast(inout uint *)p)[-1]; }
+ inout(ubyte*) sentinel_post(inout void *p) nothrow @nogc { return &(cast(inout ubyte *)p)[*sentinel_psize(p)]; }
+
+
+ void sentinel_init(void *p, size_t size) nothrow @nogc
+ {
+ assert(size <= uint.max);
+ *sentinel_psize(p) = cast(uint)size;
+ *sentinel_pre(p) = SENTINEL_PRE;
+ *sentinel_post(p) = SENTINEL_POST;
+ }
+
+
+ void sentinel_Invariant(const void *p) nothrow @nogc
+ {
+ debug
+ {
+ assert(*sentinel_pre(p) == SENTINEL_PRE);
+ assert(*sentinel_post(p) == SENTINEL_POST);
+ }
+ else if (*sentinel_pre(p) != SENTINEL_PRE || *sentinel_post(p) != SENTINEL_POST)
+ onInvalidMemoryOperationError(); // also trigger in release build
+ }
+
+ size_t sentinel_size(const void *p, size_t alloc_size) nothrow @nogc
+ {
+ return *sentinel_psize(p);
+ }
+
+ void *sentinel_add(void *p) nothrow @nogc
+ {
+ return p + 2 * uint.sizeof;
+ }
+
+
+ void *sentinel_sub(void *p) nothrow @nogc
+ {
+ return p - 2 * uint.sizeof;
+ }
+}
+else
+{
+ const uint SENTINEL_EXTRA = 0;
+
+
+ void sentinel_init(void *p, size_t size) nothrow @nogc
+ {
+ }
+
+
+ void sentinel_Invariant(const void *p) nothrow @nogc
+ {
+ }
+
+ size_t sentinel_size(const void *p, size_t alloc_size) nothrow @nogc
+ {
+ return alloc_size;
+ }
+
+ void *sentinel_add(void *p) nothrow @nogc
+ {
+ return p;
+ }
+
+
+ void *sentinel_sub(void *p) nothrow @nogc
+ {
+ return p;
+ }
+}
+
+debug (MEMSTOMP)
+unittest
+{
+ import core.memory;
+ auto p = cast(size_t*)GC.malloc(size_t.sizeof*3);
+ assert(*p == cast(size_t)0xF0F0F0F0F0F0F0F0);
+ p[2] = 0; // First two will be used for free list
+ GC.free(p);
+ assert(p[2] == cast(size_t)0xF2F2F2F2F2F2F2F2);
+}
+
+debug (SENTINEL)
+unittest
+{
+ import core.memory;
+ auto p = cast(ubyte*)GC.malloc(1);
+ assert(p[-1] == 0xF4);
+ assert(p[ 1] == 0xF5);
+
+ // See also stand-alone tests in test/gc
+}
+
+unittest
+{
+ import core.memory;
+
+ // https://issues.dlang.org/show_bug.cgi?id=9275
+ GC.removeRoot(null);
+ GC.removeRoot(cast(void*)13);
+}
+
+// improve predictability of coverage of code that is eventually not hit by other tests
+debug (SENTINEL) {} else // cannot extend with SENTINEL
+debug (MARK_PRINTF) {} else // takes forever
+unittest
+{
+ import core.memory;
+ auto p = GC.malloc(260 << 20); // new pool has 390 MB
+ auto q = GC.malloc(65 << 20); // next chunk (larger than 64MB to ensure the same pool is used)
+ auto r = GC.malloc(65 << 20); // another chunk in same pool
+ assert(p + (260 << 20) == q);
+ assert(q + (65 << 20) == r);
+ GC.free(q);
+ // should trigger "assert(bin == B_FREE);" in mark due to dangling pointer q:
+ GC.collect();
+ // should trigger "break;" in extendNoSync:
+ size_t sz = GC.extend(p, 64 << 20, 66 << 20); // trigger size after p large enough (but limited)
+ assert(sz == 325 << 20);
+ GC.free(p);
+ GC.free(r);
+ r = q; // ensure q is not trashed before collection above
+
+ p = GC.malloc(70 << 20); // from the same pool
+ q = GC.malloc(70 << 20);
+ r = GC.malloc(70 << 20);
+ auto s = GC.malloc(70 << 20);
+ auto t = GC.malloc(70 << 20); // 350 MB of 390 MB used
+ assert(p + (70 << 20) == q);
+ assert(q + (70 << 20) == r);
+ assert(r + (70 << 20) == s);
+ assert(s + (70 << 20) == t);
+ GC.free(r); // ensure recalculation of largestFree in nxxt allocPages
+ auto z = GC.malloc(75 << 20); // needs new pool
+
+ GC.free(p);
+ GC.free(q);
+ GC.free(s);
+ GC.free(t);
+ GC.free(z);
+ GC.minimize(); // release huge pool
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19281
+debug (SENTINEL) {} else // cannot allow >= 4 GB with SENTINEL
+debug (MEMSTOMP) {} else // might take too long to actually touch the memory
+version (D_LP64) unittest
+{
+ static if (__traits(compiles, os_physical_mem))
+ {
+ // only run if the system has enough physical memory
+ size_t sz = 2L^^32;
+ //import core.stdc.stdio;
+ //printf("availphys = %lld", os_physical_mem());
+ if (os_physical_mem() > sz)
+ {
+ import core.memory;
+ GC.collect();
+ GC.minimize();
+ auto stats = GC.stats();
+ auto ptr = GC.malloc(sz, BlkAttr.NO_SCAN);
+ auto info = GC.query(ptr);
+ //printf("info.size = %lld", info.size);
+ assert(info.size >= sz);
+ GC.free(ptr);
+ GC.minimize();
+ auto nstats = GC.stats();
+ assert(nstats.usedSize == stats.usedSize);
+ assert(nstats.freeSize == stats.freeSize);
+ assert(nstats.allocatedInCurrentThread - sz == stats.allocatedInCurrentThread);
+ }
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19522
+unittest
+{
+ import core.memory;
+
+ void test(void* p)
+ {
+ assert(GC.getAttr(p) == BlkAttr.NO_SCAN);
+ assert(GC.setAttr(p + 4, BlkAttr.NO_SCAN) == 0); // interior pointer should fail
+ assert(GC.clrAttr(p + 4, BlkAttr.NO_SCAN) == 0); // interior pointer should fail
+ GC.free(p);
+ assert(GC.query(p).base == null);
+ assert(GC.query(p).size == 0);
+ assert(GC.addrOf(p) == null);
+ assert(GC.sizeOf(p) == 0); // fails
+ assert(GC.getAttr(p) == 0);
+ assert(GC.setAttr(p, BlkAttr.NO_SCAN) == 0);
+ assert(GC.clrAttr(p, BlkAttr.NO_SCAN) == 0);
+ }
+ void* large = GC.malloc(10000, BlkAttr.NO_SCAN);
+ test(large);
+
+ void* small = GC.malloc(100, BlkAttr.NO_SCAN);
+ test(small);
+}
+
+unittest
+{
+ import core.memory;
+
+ auto now = currTime;
+ GC.ProfileStats stats1 = GC.profileStats();
+ GC.collect();
+ GC.ProfileStats stats2 = GC.profileStats();
+ auto diff = currTime - now;
+
+ assert(stats2.totalCollectionTime - stats1.totalCollectionTime <= diff);
+ assert(stats2.totalPauseTime - stats1.totalPauseTime <= stats2.totalCollectionTime - stats1.totalCollectionTime);
+
+ assert(stats2.maxPauseTime >= stats1.maxPauseTime);
+ assert(stats2.maxCollectionTime >= stats1.maxCollectionTime);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20214
+unittest
+{
+ import core.memory;
+ import core.stdc.stdio;
+
+ // allocate from large pool
+ auto o = GC.malloc(10);
+ auto p = (cast(void**)GC.malloc(4096 * (void*).sizeof))[0 .. 4096];
+ auto q = (cast(void**)GC.malloc(4096 * (void*).sizeof))[0 .. 4096];
+ if (p.ptr + p.length is q.ptr)
+ {
+ q[] = o; // fill with pointers
+
+ // shrink, unused area cleared?
+ auto nq = (cast(void**)GC.realloc(q.ptr, 4000 * (void*).sizeof))[0 .. 4000];
+ assert(q.ptr is nq.ptr);
+ assert(q.ptr[4095] !is o);
+
+ GC.free(q.ptr);
+ // expected to extend in place
+ auto np = (cast(void**)GC.realloc(p.ptr, 4200 * (void*).sizeof))[0 .. 4200];
+ assert(p.ptr is np.ptr);
+ assert(q.ptr[4200] !is o);
+ }
+ else
+ {
+ // adjacent allocations likely but not guaranteed
+ printf("unexpected pointers %p and %p\n", p.ptr, q.ptr);
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d b/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d
new file mode 100644
index 00000000000..a65c636ef23
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d
@@ -0,0 +1,269 @@
+/**
+ * This module contains a minimal garbage collector implementation according to
+ * published requirements. This library is mostly intended to serve as an
+ * example, but it is usable in applications which do not rely on a garbage
+ * collector to clean up memory (ie. when dynamic array resizing is not used,
+ * and all memory allocated with 'new' is freed deterministically with
+ * 'delete').
+ *
+ * Please note that block attribute data must be tracked, or at a minimum, the
+ * FINALIZE bit must be tracked for any allocated memory block because calling
+ * rt_finalize on a non-object block can result in an access violation. In the
+ * allocator below, this tracking is done via a leading uint bitmask. A real
+ * allocator may do better to store this data separately, similar to the basic
+ * GC.
+ *
+ * Copyright: Copyright Sean Kelly 2005 - 2016.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Sean Kelly
+ */
+module core.internal.gc.impl.manual.gc;
+
+import core.gc.gcinterface;
+
+import core.internal.container.array;
+
+import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
+static import core.memory;
+
+extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc; /* dmd @@@BUG11461@@@ */
+
+// register GC in C constructor (_STI_)
+extern(C) pragma(crt_constructor) void _d_register_manual_gc()
+{
+ import core.gc.registry;
+ registerGCFactory("manual", &initialize);
+}
+
+private GC initialize()
+{
+ import core.lifetime : emplace;
+
+ auto gc = cast(ManualGC) cstdlib.malloc(__traits(classInstanceSize, ManualGC));
+ if (!gc)
+ onOutOfMemoryError();
+
+ return emplace(gc);
+}
+
+class ManualGC : GC
+{
+ Array!Root roots;
+ Array!Range ranges;
+
+ this()
+ {
+ }
+
+ ~this()
+ {
+ // TODO: cannot free as memory is overwritten and
+ // the monitor is still read in rt_finalize (called by destroy)
+ // cstdlib.free(cast(void*) this);
+ }
+
+ void enable()
+ {
+ }
+
+ void disable()
+ {
+ }
+
+ void collect() nothrow
+ {
+ }
+
+ void collectNoStack() nothrow
+ {
+ }
+
+ void minimize() nothrow
+ {
+ }
+
+ uint getAttr(void* p) nothrow
+ {
+ return 0;
+ }
+
+ uint setAttr(void* p, uint mask) nothrow
+ {
+ return 0;
+ }
+
+ uint clrAttr(void* p, uint mask) nothrow
+ {
+ return 0;
+ }
+
+ void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow
+ {
+ void* p = cstdlib.malloc(size);
+
+ if (size && p is null)
+ onOutOfMemoryError();
+ return p;
+ }
+
+ BlkInfo qalloc(size_t size, uint bits, const scope TypeInfo ti) nothrow
+ {
+ BlkInfo retval;
+ retval.base = malloc(size, bits, ti);
+ retval.size = size;
+ retval.attr = bits;
+ return retval;
+ }
+
+ void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow
+ {
+ void* p = cstdlib.calloc(1, size);
+
+ if (size && p is null)
+ onOutOfMemoryError();
+ return p;
+ }
+
+ void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow
+ {
+ p = cstdlib.realloc(p, size);
+
+ if (size && p is null)
+ onOutOfMemoryError();
+ return p;
+ }
+
+ size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow
+ {
+ return 0;
+ }
+
+ size_t reserve(size_t size) nothrow
+ {
+ return 0;
+ }
+
+ void free(void* p) nothrow @nogc
+ {
+ cstdlib.free(p);
+ }
+
+ /**
+ * Determine the base address of the block containing p. If p is not a gc
+ * allocated pointer, return null.
+ */
+ void* addrOf(void* p) nothrow @nogc
+ {
+ return null;
+ }
+
+ /**
+ * Determine the allocated size of pointer p. If p is an interior pointer
+ * or not a gc allocated pointer, return 0.
+ */
+ size_t sizeOf(void* p) nothrow @nogc
+ {
+ return 0;
+ }
+
+ /**
+ * Determine the base address of the block containing p. If p is not a gc
+ * allocated pointer, return null.
+ */
+ BlkInfo query(void* p) nothrow
+ {
+ return BlkInfo.init;
+ }
+
+ core.memory.GC.Stats stats() nothrow
+ {
+ return typeof(return).init;
+ }
+
+ core.memory.GC.ProfileStats profileStats() nothrow
+ {
+ return typeof(return).init;
+ }
+
+ void addRoot(void* p) nothrow @nogc
+ {
+ roots.insertBack(Root(p));
+ }
+
+ void removeRoot(void* p) nothrow @nogc
+ {
+ foreach (ref r; roots)
+ {
+ if (r is p)
+ {
+ r = roots.back;
+ roots.popBack();
+ return;
+ }
+ }
+ assert(false);
+ }
+
+ @property RootIterator rootIter() return @nogc
+ {
+ return &rootsApply;
+ }
+
+ private int rootsApply(scope int delegate(ref Root) nothrow dg)
+ {
+ foreach (ref r; roots)
+ {
+ if (auto result = dg(r))
+ return result;
+ }
+ return 0;
+ }
+
+ void addRange(void* p, size_t sz, const TypeInfo ti = null) nothrow @nogc
+ {
+ ranges.insertBack(Range(p, p + sz, cast() ti));
+ }
+
+ void removeRange(void* p) nothrow @nogc
+ {
+ foreach (ref r; ranges)
+ {
+ if (r.pbot is p)
+ {
+ r = ranges.back;
+ ranges.popBack();
+ return;
+ }
+ }
+ assert(false);
+ }
+
+ @property RangeIterator rangeIter() return @nogc
+ {
+ return &rangesApply;
+ }
+
+ private int rangesApply(scope int delegate(ref Range) nothrow dg)
+ {
+ foreach (ref r; ranges)
+ {
+ if (auto result = dg(r))
+ return result;
+ }
+ return 0;
+ }
+
+ void runFinalizers(const scope void[] segment) nothrow
+ {
+ }
+
+ bool inFinalizer() nothrow
+ {
+ return false;
+ }
+
+ ulong allocatedInCurrentThread() nothrow
+ {
+ return typeof(return).init;
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d b/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d
new file mode 100644
index 00000000000..ff044d9a9b2
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d
@@ -0,0 +1,248 @@
+
+module core.internal.gc.impl.proto.gc;
+
+import core.gc.gcinterface;
+
+import core.internal.container.array;
+
+import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
+static import core.memory;
+
+extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc; /* dmd @@@BUG11461@@@ */
+
+private
+{
+ extern (C) void gc_init_nothrow() nothrow @nogc;
+ extern (C) void gc_term();
+
+ extern (C) void gc_enable() nothrow;
+ extern (C) void gc_disable() nothrow;
+
+ extern (C) void* gc_malloc( size_t sz, uint ba = 0, const scope TypeInfo = null ) pure nothrow;
+ extern (C) void* gc_calloc( size_t sz, uint ba = 0, const scope TypeInfo = null ) pure nothrow;
+ extern (C) BlkInfo gc_qalloc( size_t sz, uint ba = 0, const scope TypeInfo = null ) pure nothrow;
+ extern (C) void* gc_realloc(return scope void* p, size_t sz, uint ba = 0, const scope TypeInfo = null ) pure nothrow;
+ extern (C) size_t gc_reserve( size_t sz ) nothrow;
+
+ extern (C) void gc_addRange(const void* p, size_t sz, const scope TypeInfo ti = null ) nothrow @nogc;
+ extern (C) void gc_addRoot(const void* p ) nothrow @nogc;
+}
+
+class ProtoGC : GC
+{
+ Array!Root roots;
+ Array!Range ranges;
+
+ // Call this function when initializing the real GC
+ // upon ProtoGC term. This function should be called
+ // after the real GC is in place.
+ void transferRangesAndRoots()
+ {
+ // Transfer all ranges
+ foreach (ref r; ranges)
+ {
+ // Range(p, p + sz, cast() ti)
+ gc_addRange(r.pbot, r.ptop - r.pbot, r.ti);
+ }
+
+ // Transfer all roots
+ foreach (ref r; roots)
+ {
+ gc_addRoot(r.proot);
+ }
+ }
+
+ this()
+ {
+ }
+
+ void Dtor()
+ {
+ }
+
+ void enable()
+ {
+ .gc_init_nothrow();
+ .gc_enable();
+ }
+
+ void disable()
+ {
+ .gc_init_nothrow();
+ .gc_disable();
+ }
+
+ void collect() nothrow
+ {
+ }
+
+ void collectNoStack() nothrow
+ {
+ }
+
+ void minimize() nothrow
+ {
+ }
+
+ uint getAttr(void* p) nothrow
+ {
+ return 0;
+ }
+
+ uint setAttr(void* p, uint mask) nothrow
+ {
+ return 0;
+ }
+
+ uint clrAttr(void* p, uint mask) nothrow
+ {
+ return 0;
+ }
+
+ void* malloc(size_t size, uint bits, const scope TypeInfo ti) nothrow
+ {
+ .gc_init_nothrow();
+ return .gc_malloc(size, bits, ti);
+ }
+
+ BlkInfo qalloc(size_t size, uint bits, const scope TypeInfo ti) nothrow
+ {
+ .gc_init_nothrow();
+ return .gc_qalloc(size, bits, ti);
+ }
+
+ void* calloc(size_t size, uint bits, const scope TypeInfo ti) nothrow
+ {
+ .gc_init_nothrow();
+ return .gc_calloc(size, bits, ti);
+ }
+
+ void* realloc(void* p, size_t size, uint bits, const scope TypeInfo ti) nothrow
+ {
+ .gc_init_nothrow();
+ return .gc_realloc(p, size, bits, ti);
+ }
+
+ size_t extend(void* p, size_t minsize, size_t maxsize, const scope TypeInfo ti) nothrow
+ {
+ return 0;
+ }
+
+ size_t reserve(size_t size) nothrow
+ {
+ .gc_init_nothrow();
+ return .gc_reserve(size);
+ }
+
+ void free(void* p) nothrow @nogc
+ {
+ if (p) assert(false, "Invalid memory deallocation");
+ }
+
+ void* addrOf(void* p) nothrow @nogc
+ {
+ return null;
+ }
+
+ size_t sizeOf(void* p) nothrow @nogc
+ {
+ return 0;
+ }
+
+ BlkInfo query(void* p) nothrow
+ {
+ return BlkInfo.init;
+ }
+
+ core.memory.GC.Stats stats() nothrow
+ {
+ return typeof(return).init;
+ }
+
+
+ core.memory.GC.ProfileStats profileStats() nothrow
+ {
+ return typeof(return).init;
+ }
+
+
+ void addRoot(void* p) nothrow @nogc
+ {
+ roots.insertBack(Root(p));
+ }
+
+ void removeRoot(void* p) nothrow @nogc
+ {
+ foreach (ref r; roots)
+ {
+ if (r is p)
+ {
+ r = roots.back;
+ roots.popBack();
+ return;
+ }
+ }
+ }
+
+ @property RootIterator rootIter() return @nogc
+ {
+ return &rootsApply;
+ }
+
+ private int rootsApply(scope int delegate(ref Root) nothrow dg)
+ {
+ foreach (ref r; roots)
+ {
+ if (auto result = dg(r))
+ return result;
+ }
+ return 0;
+ }
+
+ void addRange(void* p, size_t sz, const TypeInfo ti = null) nothrow @nogc
+ {
+ ranges.insertBack(Range(p, p + sz, cast() ti));
+ }
+
+ void removeRange(void* p) nothrow @nogc
+ {
+ foreach (ref r; ranges)
+ {
+ if (r.pbot is p)
+ {
+ r = ranges.back;
+ ranges.popBack();
+ return;
+ }
+ }
+ }
+
+ @property RangeIterator rangeIter() return @nogc
+ {
+ return &rangesApply;
+ }
+
+ private int rangesApply(scope int delegate(ref Range) nothrow dg)
+ {
+ foreach (ref r; ranges)
+ {
+ if (auto result = dg(r))
+ return result;
+ }
+ return 0;
+ }
+
+ void runFinalizers(const scope void[] segment) nothrow
+ {
+ }
+
+ bool inFinalizer() nothrow
+ {
+ return false;
+ }
+
+ ulong allocatedInCurrentThread() nothrow
+ {
+ return stats().allocatedInCurrentThread;
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/gc/os.d b/libphobos/libdruntime/core/internal/gc/os.d
new file mode 100644
index 00000000000..ca4cbe2b1c8
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/gc/os.d
@@ -0,0 +1,308 @@
+/**
+ * Contains OS-level routines needed by the garbage collector.
+ *
+ * Copyright: D Language Foundation 2005 - 2021.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright, David Friedman, Sean Kelly, Leandro Lucarella
+ */
+module core.internal.gc.os;
+
+
+version (Windows)
+{
+ import core.sys.windows.winbase : GetCurrentThreadId, VirtualAlloc, VirtualFree;
+ import core.sys.windows.winnt : MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE;
+
+ alias int pthread_t;
+
+ pthread_t pthread_self() nothrow
+ {
+ return cast(pthread_t) GetCurrentThreadId();
+ }
+
+ //version = GC_Use_Alloc_Win32;
+}
+else version (Posix)
+{
+ version (OSX)
+ version = Darwin;
+ else version (iOS)
+ version = Darwin;
+ else version (TVOS)
+ version = Darwin;
+ else version (WatchOS)
+ version = Darwin;
+
+ import core.sys.posix.sys.mman;
+ import core.stdc.stdlib;
+
+
+ /// Possible results for the wait_pid() function.
+ enum ChildStatus
+ {
+ done, /// The process has finished successfully
+ running, /// The process is still running
+ error /// There was an error waiting for the process
+ }
+
+ /**
+ * Wait for a process with PID pid to finish.
+ *
+ * If block is false, this function will not block, and return ChildStatus.running if
+ * the process is still running. Otherwise it will return always ChildStatus.done
+ * (unless there is an error, in which case ChildStatus.error is returned).
+ */
+ ChildStatus wait_pid(pid_t pid, bool block = true) nothrow @nogc
+ {
+ import core.exception : onForkError;
+
+ int status = void;
+ pid_t waited_pid = void;
+ // In the case where we are blocking, we need to consider signals
+ // arriving while we wait, and resume the waiting if EINTR is returned
+ do {
+ errno = 0;
+ waited_pid = waitpid(pid, &status, block ? 0 : WNOHANG);
+ }
+ while (waited_pid == -1 && errno == EINTR);
+ if (waited_pid == 0)
+ return ChildStatus.running;
+ else if (errno == ECHILD)
+ return ChildStatus.done; // someone called posix.syswait
+ else if (waited_pid != pid || status != 0)
+ {
+ onForkError();
+ return ChildStatus.error;
+ }
+ return ChildStatus.done;
+ }
+
+ public import core.sys.posix.unistd: pid_t, fork;
+ import core.sys.posix.sys.wait: waitpid, WNOHANG;
+ import core.stdc.errno: errno, EINTR, ECHILD;
+
+ //version = GC_Use_Alloc_MMap;
+}
+else
+{
+ import core.stdc.stdlib;
+
+ //version = GC_Use_Alloc_Malloc;
+}
+
+/+
+static if (is(typeof(VirtualAlloc)))
+ version = GC_Use_Alloc_Win32;
+else static if (is(typeof(mmap)))
+ version = GC_Use_Alloc_MMap;
+else static if (is(typeof(valloc)))
+ version = GC_Use_Alloc_Valloc;
+else static if (is(typeof(malloc)))
+ version = GC_Use_Alloc_Malloc;
+else static assert(false, "No supported allocation methods available.");
++/
+
+static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32)
+{
+ /**
+ * Indicates if an implementation supports fork().
+ *
+ * The value shown here is just demostrative, the real value is defined based
+ * on the OS it's being compiled in.
+ * enum HaveFork = true;
+ */
+ enum HaveFork = false;
+
+ /**
+ * Map memory.
+ */
+ void *os_mem_map(size_t nbytes) nothrow @nogc
+ {
+ return VirtualAlloc(null, nbytes, MEM_RESERVE | MEM_COMMIT,
+ PAGE_READWRITE);
+ }
+
+
+ /**
+ * Unmap memory allocated with os_mem_map().
+ * Returns:
+ * 0 success
+ * !=0 failure
+ */
+ int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+ {
+ return cast(int)(VirtualFree(base, 0, MEM_RELEASE) == 0);
+ }
+}
+else static if (is(typeof(mmap))) // else version (GC_Use_Alloc_MMap)
+{
+ enum HaveFork = true;
+
+ void *os_mem_map(size_t nbytes, bool share = false) nothrow @nogc
+ { void *p;
+
+ auto map_f = share ? MAP_SHARED : MAP_PRIVATE;
+ p = mmap(null, nbytes, PROT_READ | PROT_WRITE, map_f | MAP_ANON, -1, 0);
+ return (p == MAP_FAILED) ? null : p;
+ }
+
+
+ int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+ {
+ return munmap(base, nbytes);
+ }
+}
+else static if (is(typeof(valloc))) // else version (GC_Use_Alloc_Valloc)
+{
+ enum HaveFork = false;
+
+ void *os_mem_map(size_t nbytes) nothrow @nogc
+ {
+ return valloc(nbytes);
+ }
+
+
+ int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+ {
+ free(base);
+ return 0;
+ }
+}
+else static if (is(typeof(malloc))) // else version (GC_Use_Alloc_Malloc)
+{
+ // NOTE: This assumes malloc granularity is at least (void*).sizeof. If
+ // (req_size + PAGESIZE) is allocated, and the pointer is rounded up
+ // to PAGESIZE alignment, there will be space for a void* at the end
+ // after PAGESIZE bytes used by the GC.
+
+ enum HaveFork = false;
+
+ import core.internal.gc.impl.conservative.gc;
+
+
+ const size_t PAGE_MASK = PAGESIZE - 1;
+
+
+ void *os_mem_map(size_t nbytes) nothrow @nogc
+ { byte *p, q;
+ p = cast(byte *) malloc(nbytes + PAGESIZE);
+ if (!p)
+ return null;
+ q = p + ((PAGESIZE - ((cast(size_t) p & PAGE_MASK))) & PAGE_MASK);
+ * cast(void**)(q + nbytes) = p;
+ return q;
+ }
+
+
+ int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
+ {
+ free( *cast(void**)( cast(byte*) base + nbytes ) );
+ return 0;
+ }
+}
+else
+{
+ static assert(false, "No supported allocation methods available.");
+}
+
+/**
+ Check for any kind of memory pressure.
+
+ Params:
+ mapped = the amount of memory mapped by the GC in bytes
+ Returns:
+ true if memory is scarce
+*/
+// TOOD: get virtual mem sizes and current usage from OS
+// TODO: compare current RSS and avail. physical memory
+version (Windows)
+{
+ bool isLowOnMem(size_t mapped) nothrow @nogc
+ {
+ version (D_LP64)
+ return false;
+ else
+ {
+ import core.sys.windows.winbase : GlobalMemoryStatus, MEMORYSTATUS;
+ MEMORYSTATUS stat;
+ GlobalMemoryStatus(&stat);
+ // Less than 5 % of virtual address space available
+ return stat.dwAvailVirtual < stat.dwTotalVirtual / 20;
+ }
+ }
+}
+else version (Darwin)
+{
+ bool isLowOnMem(size_t mapped) nothrow @nogc
+ {
+ enum GB = 2 ^^ 30;
+ version (D_LP64)
+ return false;
+ else
+ {
+ // 80 % of available 4GB is used for GC (excluding malloc and mmap)
+ enum size_t limit = 4UL * GB * 8 / 10;
+ return mapped > limit;
+ }
+ }
+}
+else
+{
+ bool isLowOnMem(size_t mapped) nothrow @nogc
+ {
+ enum GB = 2 ^^ 30;
+ version (D_LP64)
+ return false;
+ else
+ {
+ // be conservative and assume 3GB
+ enum size_t limit = 3UL * GB * 8 / 10;
+ return mapped > limit;
+ }
+ }
+}
+
+/**
+ Get the size of available physical memory
+
+ Returns:
+ size of installed physical RAM
+*/
+version (Windows)
+{
+ ulong os_physical_mem() nothrow @nogc
+ {
+ import core.sys.windows.winbase : GlobalMemoryStatus, MEMORYSTATUS;
+ MEMORYSTATUS stat;
+ GlobalMemoryStatus(&stat);
+ return stat.dwTotalPhys; // limited to 4GB for Win32
+ }
+}
+else version (Darwin)
+{
+ extern (C) int sysctl(const int* name, uint namelen, void* oldp, size_t* oldlenp, const void* newp, size_t newlen) @nogc nothrow;
+ ulong os_physical_mem() nothrow @nogc
+ {
+ enum
+ {
+ CTL_HW = 6,
+ HW_MEMSIZE = 24,
+ }
+ int[2] mib = [ CTL_HW, HW_MEMSIZE ];
+ ulong system_memory_bytes;
+ size_t len = system_memory_bytes.sizeof;
+ if (sysctl(mib.ptr, 2, &system_memory_bytes, &len, null, 0) != 0)
+ return 0;
+ return system_memory_bytes;
+ }
+}
+else version (Posix)
+{
+ ulong os_physical_mem() nothrow @nogc
+ {
+ import core.sys.posix.unistd : sysconf, _SC_PAGESIZE, _SC_PHYS_PAGES;
+ const pageSize = sysconf(_SC_PAGESIZE);
+ const pages = sysconf(_SC_PHYS_PAGES);
+ return pageSize * pages;
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/gc/pooltable.d b/libphobos/libdruntime/core/internal/gc/pooltable.d
new file mode 100644
index 00000000000..5924f9c1a55
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/gc/pooltable.d
@@ -0,0 +1,295 @@
+/**
+ * A sorted array to quickly lookup pools.
+ *
+ * Copyright: D Language Foundation 2001 - 2021
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright, David Friedman, Sean Kelly, Martin Nowak
+ */
+module core.internal.gc.pooltable;
+
+static import cstdlib=core.stdc.stdlib;
+
+struct PoolTable(Pool)
+{
+ import core.stdc.string : memmove;
+
+nothrow:
+ void Dtor()
+ {
+ cstdlib.free(pools);
+ pools = null;
+ npools = 0;
+ }
+
+ bool insert(Pool* pool)
+ {
+ auto newpools = cast(Pool **)cstdlib.realloc(pools, (npools + 1) * pools[0].sizeof);
+ if (!newpools)
+ return false;
+
+ pools = newpools;
+
+ // Sort pool into newpooltable[]
+ size_t i;
+ for (; i < npools; ++i)
+ {
+ if (pool.baseAddr < pools[i].baseAddr)
+ break;
+ }
+ if (i != npools)
+ memmove(pools + i + 1, pools + i, (npools - i) * pools[0].sizeof);
+ pools[i] = pool;
+
+ ++npools;
+
+ foreach (idx; i .. npools)
+ pools[idx].ptIndex = idx;
+
+ _minAddr = pools[0].baseAddr;
+ _maxAddr = pools[npools - 1].topAddr;
+
+ return true;
+ }
+
+ @property size_t length() pure const
+ {
+ return npools;
+ }
+
+ ref inout(Pool*) opIndex(size_t idx) inout pure
+ in { assert(idx < length); }
+ do
+ {
+ return pools[idx];
+ }
+
+ inout(Pool*)[] opSlice(size_t a, size_t b) inout pure
+ in { assert(a <= length && b <= length); }
+ do
+ {
+ return pools[a .. b];
+ }
+
+ alias opDollar = length;
+
+ /**
+ * Find Pool that pointer is in.
+ * Return null if not in a Pool.
+ * Assume pooltable[] is sorted.
+ */
+ Pool *findPool(void *p) nothrow
+ {
+ if (p >= minAddr && p < maxAddr)
+ {
+ assert(npools);
+
+ // let dmd allocate a register for this.pools
+ auto pools = this.pools;
+
+ if (npools == 1)
+ return pools[0];
+
+ /* The pooltable[] is sorted by address, so do a binary search
+ */
+ size_t low = 0;
+ size_t high = npools - 1;
+ while (low <= high)
+ {
+ size_t mid = (low + high) >> 1;
+ auto pool = pools[mid];
+ if (p < pool.baseAddr)
+ high = mid - 1;
+ else if (p >= pool.topAddr)
+ low = mid + 1;
+ else
+ return pool;
+ }
+ }
+ return null;
+ }
+
+ // semi-stable partition, returns right half for which pred is false
+ Pool*[] minimize() pure
+ {
+ static void swap(ref Pool* a, ref Pool* b)
+ {
+ auto c = a; a = b; b = c;
+ }
+
+ size_t i;
+ // find first bad entry
+ for (; i < npools; ++i)
+ if (pools[i].isFree) break;
+
+ // move good in front of bad entries
+ size_t j = i + 1;
+ for (; j < npools; ++j)
+ {
+ if (!pools[j].isFree) // keep
+ {
+ swap(pools[i], pools[j]);
+ pools[i].ptIndex = i;
+ ++i;
+ }
+ }
+ // npooltable[0 .. i] => used pools
+ // npooltable[i .. npools] => free pools
+
+ if (i)
+ {
+ _minAddr = pools[0].baseAddr;
+ _maxAddr = pools[i - 1].topAddr;
+ }
+ else
+ {
+ _minAddr = _maxAddr = null;
+ }
+
+ immutable len = npools;
+ npools = i;
+ // return freed pools to the caller
+ return pools[npools .. len];
+ }
+
+ void Invariant() const
+ {
+ if (!npools) return;
+
+ foreach (i; 0 .. npools)
+ assert(pools[i].ptIndex == i);
+
+ foreach (i, pool; pools[0 .. npools - 1])
+ assert(pool.baseAddr < pools[i + 1].baseAddr);
+
+ assert(_minAddr == pools[0].baseAddr);
+ assert(_maxAddr == pools[npools - 1].topAddr);
+ }
+
+ @property const(void)* minAddr() pure const { return _minAddr; }
+ @property const(void)* maxAddr() pure const { return _maxAddr; }
+
+package:
+ Pool** pools;
+ size_t npools;
+ void* _minAddr, _maxAddr;
+}
+
+unittest
+{
+ enum NPOOLS = 6;
+ enum NPAGES = 10;
+ enum PAGESIZE = 4096;
+
+ static struct MockPool
+ {
+ byte* baseAddr, topAddr;
+ size_t freepages, npages, ptIndex;
+ @property bool isFree() const pure nothrow { return freepages == npages; }
+ }
+ PoolTable!MockPool pooltable;
+
+ void reset()
+ {
+ foreach (ref pool; pooltable[0 .. $])
+ pool.freepages = pool.npages;
+ pooltable.minimize();
+ assert(pooltable.length == 0);
+
+ foreach (i; 0 .. NPOOLS)
+ {
+ auto pool = cast(MockPool*)cstdlib.malloc(MockPool.sizeof);
+ *pool = MockPool.init;
+ assert(pooltable.insert(pool));
+ }
+ }
+
+ void usePools()
+ {
+ foreach (pool; pooltable[0 .. $])
+ {
+ pool.npages = NPAGES;
+ pool.freepages = NPAGES / 2;
+ }
+ }
+
+ // all pools are free
+ reset();
+ assert(pooltable.length == NPOOLS);
+ auto freed = pooltable.minimize();
+ assert(freed.length == NPOOLS);
+ assert(pooltable.length == 0);
+
+ // all pools used
+ reset();
+ usePools();
+ assert(pooltable.length == NPOOLS);
+ freed = pooltable.minimize();
+ assert(freed.length == 0);
+ assert(pooltable.length == NPOOLS);
+
+ // preserves order of used pools
+ reset();
+ usePools();
+
+ {
+ MockPool*[NPOOLS] opools = pooltable[0 .. NPOOLS];
+ // make the 2nd pool free
+ pooltable[2].freepages = NPAGES;
+
+ pooltable.minimize();
+ assert(pooltable.length == NPOOLS - 1);
+ assert(pooltable[0] == opools[0]);
+ assert(pooltable[1] == opools[1]);
+ assert(pooltable[2] == opools[3]);
+ }
+
+ // test that PoolTable reduces min/max address span
+ reset();
+ usePools();
+
+ byte* base, top;
+
+ {
+ // fill with fake addresses
+ size_t i;
+ foreach (pool; pooltable[0 .. NPOOLS])
+ {
+ pool.baseAddr = cast(byte*)(i++ * NPAGES * PAGESIZE);
+ pool.topAddr = pool.baseAddr + NPAGES * PAGESIZE;
+ }
+ base = pooltable[0].baseAddr;
+ top = pooltable[NPOOLS - 1].topAddr;
+ }
+
+ freed = pooltable.minimize();
+ assert(freed.length == 0);
+ assert(pooltable.length == NPOOLS);
+ assert(pooltable.minAddr == base);
+ assert(pooltable.maxAddr == top);
+
+ pooltable[NPOOLS - 1].freepages = NPAGES;
+ pooltable[NPOOLS - 2].freepages = NPAGES;
+
+ freed = pooltable.minimize();
+ assert(freed.length == 2);
+ assert(pooltable.length == NPOOLS - 2);
+ assert(pooltable.minAddr == base);
+ assert(pooltable.maxAddr == pooltable[NPOOLS - 3].topAddr);
+
+ pooltable[0].freepages = NPAGES;
+
+ freed = pooltable.minimize();
+ assert(freed.length == 1);
+ assert(pooltable.length == NPOOLS - 3);
+ assert(pooltable.minAddr != base);
+ assert(pooltable.minAddr == pooltable[0].baseAddr);
+ assert(pooltable.maxAddr == pooltable[NPOOLS - 4].topAddr);
+
+ // free all
+ foreach (pool; pooltable[0 .. $])
+ pool.freepages = NPAGES;
+ freed = pooltable.minimize();
+ assert(freed.length == NPOOLS - 3);
+ assert(pooltable.length == 0);
+ pooltable.Dtor();
+}
diff --git a/libphobos/libdruntime/core/internal/gc/proxy.d b/libphobos/libdruntime/core/internal/gc/proxy.d
new file mode 100644
index 00000000000..2c89472a558
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/gc/proxy.d
@@ -0,0 +1,296 @@
+/**
+ * Contains the external GC interface.
+ *
+ * Copyright: D Language Foundation 2005 - 2021.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright, Sean Kelly
+ */
+module core.internal.gc.proxy;
+
+import core.internal.gc.impl.proto.gc;
+import core.gc.config;
+import core.gc.gcinterface;
+import core.gc.registry : createGCInstance;
+
+static import core.memory;
+
+private
+{
+ static import core.memory;
+ alias BlkInfo = core.memory.GC.BlkInfo;
+
+ import core.internal.spinlock;
+ static SpinLock instanceLock;
+
+ __gshared bool isInstanceInit = false;
+ __gshared GC _instance = new ProtoGC();
+ __gshared GC proxiedGC; // used to iterate roots of Windows DLLs
+
+ pragma (inline, true) @trusted @nogc nothrow
+ GC instance() { return _instance; }
+}
+
+extern (C)
+{
+ import core.attribute : weak;
+
+ // do not import GC modules, they might add a dependency to this whole module
+ void _d_register_conservative_gc();
+ void _d_register_manual_gc();
+
+ // if you don't want to include the default GCs, replace during link by another implementation
+ void* register_default_gcs() @weak
+ {
+ pragma(inline, false);
+ // do not call, they register implicitly through pragma(crt_constructor)
+ // avoid being optimized away
+ auto reg1 = &_d_register_conservative_gc;
+ auto reg2 = &_d_register_manual_gc;
+ return reg1 < reg2 ? reg1 : reg2;
+ }
+
+ void gc_init()
+ {
+ instanceLock.lock();
+ if (!isInstanceInit)
+ {
+ register_default_gcs();
+ config.initialize();
+ auto protoInstance = instance;
+ auto newInstance = createGCInstance(config.gc);
+ if (newInstance is null)
+ {
+ import core.stdc.stdio : fprintf, stderr;
+ import core.stdc.stdlib : exit;
+
+ fprintf(stderr, "No GC was initialized, please recheck the name of the selected GC ('%.*s').\n", cast(int)config.gc.length, config.gc.ptr);
+ instanceLock.unlock();
+ exit(1);
+
+ // Shouldn't get here.
+ assert(0);
+ }
+ _instance = newInstance;
+ // Transfer all ranges and roots to the real GC.
+ (cast(ProtoGC) protoInstance).transferRangesAndRoots();
+ isInstanceInit = true;
+ }
+ instanceLock.unlock();
+ }
+
+ void gc_init_nothrow() nothrow
+ {
+ scope(failure)
+ {
+ import core.internal.abort;
+ abort("Cannot initialize the garbage collector.\n");
+ assert(0);
+ }
+ gc_init();
+ }
+
+ void gc_term()
+ {
+ if (isInstanceInit)
+ {
+ switch (config.cleanup)
+ {
+ default:
+ import core.stdc.stdio : fprintf, stderr;
+ fprintf(stderr, "Unknown GC cleanup method, please recheck ('%.*s').\n",
+ cast(int)config.cleanup.length, config.cleanup.ptr);
+ break;
+ case "none":
+ break;
+ case "collect":
+ // NOTE: There may be daemons threads still running when this routine is
+ // called. If so, cleaning memory out from under then is a good
+ // way to make them crash horribly. This probably doesn't matter
+ // much since the app is supposed to be shutting down anyway, but
+ // I'm disabling cleanup for now until I can think about it some
+ // more.
+ //
+ // NOTE: Due to popular demand, this has been re-enabled. It still has
+ // the problems mentioned above though, so I guess we'll see.
+
+ instance.collectNoStack(); // not really a 'collect all' -- still scans
+ // static data area, roots, and ranges.
+ break;
+ case "finalize":
+ instance.runFinalizers((cast(ubyte*)null)[0 .. size_t.max]);
+ break;
+ }
+ destroy(instance);
+ }
+ }
+
+ void gc_enable()
+ {
+ instance.enable();
+ }
+
+ void gc_disable()
+ {
+ instance.disable();
+ }
+
+ void gc_collect() nothrow
+ {
+ instance.collect();
+ }
+
+ void gc_minimize() nothrow
+ {
+ instance.minimize();
+ }
+
+ uint gc_getAttr( void* p ) nothrow
+ {
+ return instance.getAttr(p);
+ }
+
+ uint gc_setAttr( void* p, uint a ) nothrow
+ {
+ return instance.setAttr(p, a);
+ }
+
+ uint gc_clrAttr( void* p, uint a ) nothrow
+ {
+ return instance.clrAttr(p, a);
+ }
+
+ void* gc_malloc( size_t sz, uint ba = 0, const scope TypeInfo ti = null ) nothrow
+ {
+ return instance.malloc(sz, ba, ti);
+ }
+
+ BlkInfo gc_qalloc( size_t sz, uint ba = 0, const scope TypeInfo ti = null ) nothrow
+ {
+ return instance.qalloc( sz, ba, ti );
+ }
+
+ void* gc_calloc( size_t sz, uint ba = 0, const scope TypeInfo ti = null ) nothrow
+ {
+ return instance.calloc( sz, ba, ti );
+ }
+
+ void* gc_realloc( void* p, size_t sz, uint ba = 0, const scope TypeInfo ti = null ) nothrow
+ {
+ return instance.realloc( p, sz, ba, ti );
+ }
+
+ size_t gc_extend( void* p, size_t mx, size_t sz, const scope TypeInfo ti = null ) nothrow
+ {
+ return instance.extend( p, mx, sz,ti );
+ }
+
+ size_t gc_reserve( size_t sz ) nothrow
+ {
+ return instance.reserve( sz );
+ }
+
+ void gc_free( void* p ) nothrow @nogc
+ {
+ return instance.free( p );
+ }
+
+ void* gc_addrOf( void* p ) nothrow @nogc
+ {
+ return instance.addrOf( p );
+ }
+
+ size_t gc_sizeOf( void* p ) nothrow @nogc
+ {
+ return instance.sizeOf( p );
+ }
+
+ BlkInfo gc_query( void* p ) nothrow
+ {
+ return instance.query( p );
+ }
+
+ core.memory.GC.Stats gc_stats() nothrow
+ {
+ return instance.stats();
+ }
+
+ core.memory.GC.ProfileStats gc_profileStats() nothrow @safe
+ {
+ return instance.profileStats();
+ }
+
+ void gc_addRoot( void* p ) nothrow @nogc
+ {
+ return instance.addRoot( p );
+ }
+
+ void gc_addRange( void* p, size_t sz, const TypeInfo ti = null ) nothrow @nogc
+ {
+ return instance.addRange( p, sz, ti );
+ }
+
+ void gc_removeRoot( void* p ) nothrow
+ {
+ return instance.removeRoot( p );
+ }
+
+ void gc_removeRange( void* p ) nothrow
+ {
+ return instance.removeRange( p );
+ }
+
+ void gc_runFinalizers(const scope void[] segment ) nothrow
+ {
+ return instance.runFinalizers( segment );
+ }
+
+ bool gc_inFinalizer() nothrow @nogc @safe
+ {
+ return instance.inFinalizer();
+ }
+
+ ulong gc_allocatedInCurrentThread() nothrow
+ {
+ return instance.allocatedInCurrentThread();
+ }
+
+ GC gc_getProxy() nothrow
+ {
+ return instance;
+ }
+
+ export
+ {
+ void gc_setProxy( GC proxy )
+ {
+ foreach (root; instance.rootIter)
+ {
+ proxy.addRoot(root);
+ }
+
+ foreach (range; instance.rangeIter)
+ {
+ proxy.addRange(range.pbot, range.ptop - range.pbot, range.ti);
+ }
+
+ proxiedGC = instance; // remember initial GC to later remove roots
+ _instance = proxy;
+ }
+
+ void gc_clrProxy()
+ {
+ foreach (root; proxiedGC.rootIter)
+ {
+ instance.removeRoot(root);
+ }
+
+ foreach (range; proxiedGC.rangeIter)
+ {
+ instance.removeRange(range);
+ }
+
+ _instance = proxiedGC;
+ proxiedGC = null;
+ }
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/hash.d b/libphobos/libdruntime/core/internal/hash.d
index 8d0067e6214..e999f0cada9 100644
--- a/libphobos/libdruntime/core/internal/hash.d
+++ b/libphobos/libdruntime/core/internal/hash.d
@@ -3,14 +3,13 @@
* This module provides functions to uniform calculating hash values for different types
*
* Copyright: Copyright Igor Stepanov 2013-2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Igor Stepanov
* Source: $(DRUNTIMESRC core/internal/_hash.d)
*/
module core.internal.hash;
-import core.internal.convert;
-import core.internal.traits : allSatisfy;
+import core.internal.traits : Unconst;
// If true ensure that positive zero and negative zero have the same hash.
// Historically typeid(float).getHash did this but hashOf(float) did not.
@@ -57,6 +56,15 @@ private enum isFinalClassWithAddressBasedHash(T) = __traits(isFinalClass, T)
static assert(!isFinalClassWithAddressBasedHash!C3);
}
+private template isCppClassWithoutHash(T)
+{
+ static if (!is(T == class) && !is(T == interface))
+ enum isCppClassWithoutHash = false;
+ else
+ enum bool isCppClassWithoutHash = __traits(getLinkage, T) == "C++"
+ && !is(immutable T* : immutable Object*) && !hasCallableToHash!T;
+}
+
/+
Is it valid to calculate a hash code for T based on the bits of its
representation? Always false for interfaces, dynamic arrays, and
@@ -80,18 +88,21 @@ private template canBitwiseHash(T)
enum canBitwiseHash = true;
else static if (is(T == class))
{
- enum canBitwiseHash = isFinalClassWithAddressBasedHash!T;
+ enum canBitwiseHash = isFinalClassWithAddressBasedHash!T || isCppClassWithoutHash!T;
}
else static if (is(T == interface))
{
- enum canBitwiseHash = false;
+ enum canBitwiseHash = isCppClassWithoutHash!T;
}
else static if (is(T == struct))
{
static if (hasCallableToHash!T || __traits(isNested, T))
enum canBitwiseHash = false;
else
+ {
+ import core.internal.traits : allSatisfy;
enum canBitwiseHash = allSatisfy!(.canBitwiseHash, typeof(T.tupleof));
+ }
}
else static if (is(T == union))
{
@@ -117,107 +128,19 @@ private template canBitwiseHash(T)
}
}
-// Overly restrictive for simplicity: has false negatives but no false positives.
-private template useScopeConstPassByValue(T)
-{
- static if (__traits(isScalar, T))
- enum useScopeConstPassByValue = true;
- else static if (is(T == class) || is(T == interface))
- // Overly restrictive for simplicity.
- enum useScopeConstPassByValue = isFinalClassWithAddressBasedHash!T;
- else static if (is(T == struct) || is(T == union))
- {
- // Overly restrictive for simplicity.
- enum useScopeConstPassByValue = T.sizeof <= (int[]).sizeof &&
- __traits(isPOD, T) && // "isPOD" just to check there's no dtor or postblit.
- canBitwiseHash!T; // We can't verify toHash doesn't leak.
- }
- else static if (is(T : E[], E))
- {
- static if (!__traits(isStaticArray, T))
- // Overly restrictive for simplicity.
- enum useScopeConstPassByValue = .useScopeConstPassByValue!E;
- else static if (T.length == 0)
- enum useScopeConstPassByValue = true;
- else
- enum useScopeConstPassByValue = T.sizeof <= (uint[]).sizeof
- && .useScopeConstPassByValue!(typeof(T.init[0]));
- }
- else static if (is(T : V[K], K, V))
- {
- // Overly restrictive for simplicity.
- enum useScopeConstPassByValue = .useScopeConstPassByValue!K
- && .useScopeConstPassByValue!V;
- }
- else
- {
- static assert(is(T == delegate) || is(T : void) || is(T : typeof(null)),
- "Internal error: unanticipated type "~T.stringof);
- enum useScopeConstPassByValue = true;
- }
-}
-
-@safe unittest
-{
- static assert(useScopeConstPassByValue!int);
- static assert(useScopeConstPassByValue!string);
-
- static int ctr;
- static struct S1 { ~this() { ctr++; } }
- static struct S2 { this(this) { ctr++; } }
- static assert(!useScopeConstPassByValue!S1,
- "Don't default pass by value a struct with a non-vacuous destructor.");
- static assert(!useScopeConstPassByValue!S2,
- "Don't default pass by value a struct with a non-vacuous postblit.");
-}
-
-//enum hash. CTFE depends on base type
-size_t hashOf(T)(scope const T val)
-if (is(T EType == enum) && useScopeConstPassByValue!EType)
-{
- static if (is(T EType == enum)) //for EType
- {
- return hashOf(cast(const EType) val);
- }
- else
- {
- static assert(0);
- }
-}
-
-//enum hash. CTFE depends on base type
-size_t hashOf(T)(scope const T val, size_t seed)
-if (is(T EType == enum) && useScopeConstPassByValue!EType)
-{
- static if (is(T EType == enum)) //for EType
- {
- return hashOf(cast(const EType) val, seed);
- }
- else
- {
- static assert(0);
- }
-}
-
//enum hash. CTFE depends on base type
size_t hashOf(T)(auto ref T val, size_t seed = 0)
-if (is(T EType == enum) && !useScopeConstPassByValue!EType)
+if (is(T == enum) && !__traits(isScalar, T))
{
- static if (is(T EType == enum)) //for EType
- {
- EType e_val = cast(EType)val;
- return hashOf(e_val, seed);
- }
- else
- {
- static assert(0);
- }
+ static if (is(T EType == enum)) {} //for EType
+ return hashOf(cast(EType) val, seed);
}
//CTFE ready (depends on base type).
size_t hashOf(T)(scope const auto ref T val, size_t seed = 0)
if (!is(T == enum) && __traits(isStaticArray, T) && canBitwiseHash!T)
{
+ import core.internal.convert : toUbyte;
// FIXME:
// We would like to to do this:
//
@@ -268,10 +191,9 @@ if (!is(T == enum) && __traits(isStaticArray, T) && !canBitwiseHash!T)
//dynamic array hash
size_t hashOf(T)(scope const T val, size_t seed = 0)
-if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStaticArray, T)
- && !is(T == struct) && !is(T == class) && !is(T == union)
- && (__traits(isScalar, S) || canBitwiseHash!S))
+if (is(T == S[], S) && (__traits(isScalar, S) || canBitwiseHash!S)) // excludes enum types
{
+ import core.internal.convert : toUbyte;
alias ElementType = typeof(val[0]);
static if (!canBitwiseHash!ElementType)
{
@@ -296,9 +218,7 @@ if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStati
//dynamic array hash
size_t hashOf(T)(T val, size_t seed = 0)
-if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStaticArray, T)
- && !is(T == struct) && !is(T == class) && !is(T == union)
- && !(__traits(isScalar, S) || canBitwiseHash!S))
+if (is(T == S[], S) && !(__traits(isScalar, S) || canBitwiseHash!S)) // excludes enum types
{
size_t hash = seed;
foreach (ref o; val)
@@ -308,117 +228,148 @@ if (!is(T == enum) && !is(T : typeof(null)) && is(T S: S[]) && !__traits(isStati
return hash;
}
-//arithmetic type hash
-@trusted @nogc nothrow pure
-size_t hashOf(T)(scope const T val) if (!is(T == enum) && __traits(isArithmetic, T)
- && __traits(isIntegral, T) && T.sizeof <= size_t.sizeof && !is(T == __vector))
+// Indicates if F is a built-in complex number type.
+private F coalesceFloat(F)(const F val)
+if (__traits(isFloating, val) && !is(F == __vector) && !is(F : creal))
{
+ static if (floatCoalesceZeroes)
+ if (val == cast(F) 0)
+ return cast(F) 0;
+ static if (floatCoalesceNaNs)
+ if (val != val)
+ return F.nan;
return val;
}
-//arithmetic type hash
+//scalar type hash
@trusted @nogc nothrow pure
-size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && __traits(isArithmetic, T)
- && __traits(isIntegral, T) && T.sizeof <= size_t.sizeof && !is(T == __vector))
+size_t hashOf(T)(scope const T val) if (__traits(isScalar, T) && !is(T == __vector))
{
- static if (size_t.sizeof < ulong.sizeof)
+ static if (is(T V : V*))
{
- //MurmurHash3 32-bit single round
- enum uint c1 = 0xcc9e2d51;
- enum uint c2 = 0x1b873593;
- enum uint c3 = 0xe6546b64;
- enum uint r1 = 15;
- enum uint r2 = 13;
+ if (__ctfe)
+ {
+ if (val is null) return 0;
+ assert(0, "Unable to calculate hash of non-null pointer at compile time");
+ }
+ size_t result = cast(size_t) val;
+ return result ^ (result >> 4);
+ }
+ else static if (is(T EType == enum) && is(typeof(val[0])))
+ {
+ // Enum type whose base type is vector.
+ return hashOf(cast(EType) val);
+ }
+ else static if (__traits(isIntegral, T))
+ {
+ static if (T.sizeof <= size_t.sizeof)
+ return val;
+ else
+ return cast(size_t) (val ^ (val >>> (size_t.sizeof * 8)));
+ }
+ else static if (is(T : creal))
+ {
+ return hashOf(coalesceFloat(val.re), hashOf(coalesceFloat(val.im)));
}
else
{
- //Half of MurmurHash3 64-bit single round
- //(omits second interleaved update)
- enum ulong c1 = 0x87c37b91114253d5;
- enum ulong c2 = 0x4cf5ad432745937f;
- enum ulong c3 = 0x52dce729;
- enum uint r1 = 31;
- enum uint r2 = 27;
+ static assert(__traits(isFloating, T));
+ auto data = coalesceFloat(val);
+ static if (T.sizeof == float.sizeof && T.mant_dig == float.mant_dig)
+ return *cast(const uint*) &data;
+ else static if (T.sizeof == double.sizeof && T.mant_dig == double.mant_dig)
+ return hashOf(*cast(const ulong*) &data);
+ else
+ {
+ import core.internal.convert : floatSize, toUbyte;
+ return bytesHashWithExactSizeAndAlignment!T(toUbyte(data)[0 .. floatSize!T], 0);
+ }
}
- size_t h = c1 * val;
- h = (h << r1) | (h >>> (size_t.sizeof * 8 - r1));
- h = (h * c2) ^ seed;
- h = (h << r2) | (h >>> (size_t.sizeof * 8 - r2));
- return h * 5 + c3;
}
-//arithmetic type hash
+//scalar type hash
@trusted @nogc nothrow pure
-size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && __traits(isArithmetic, T)
- && (!__traits(isIntegral, T) || T.sizeof > size_t.sizeof) && !is(T == __vector))
+size_t hashOf(T)(scope const T val, size_t seed) if (__traits(isScalar, T) && !is(T == __vector))
{
- static if (__traits(isFloating, val))
+ static if (is(T V : V*))
{
- import core.internal.convert : floatSize;
-
- static if (floatCoalesceZeroes || floatCoalesceNaNs)
+ if (__ctfe)
{
- import core.internal.traits : Unqual;
- Unqual!T data = val;
- // +0.0 and -0.0 become the same.
- static if (floatCoalesceZeroes && is(typeof(data = 0)))
- if (data == 0) data = 0;
- static if (floatCoalesceZeroes && is(typeof(data = 0.0i)))
- if (data == 0.0i) data = 0.0i;
- static if (floatCoalesceZeroes && is(typeof(data = 0.0 + 0.0i)))
- {
- if (data.re == 0.0) data = 0.0 + (data.im * 1.0i);
- if (data.im == 0.0i) data = data.re + 0.0i;
- }
- static if (floatCoalesceNaNs)
- if (data != data) data = T.nan; // All NaN patterns become the same.
+ if (val is null) return hashOf(size_t(0), seed);
+ assert(0, "Unable to calculate hash of non-null pointer at compile time");
+ }
+ return hashOf(cast(size_t) val, seed);
+ }
+ else static if (is(T EType == enum) && is(typeof(val[0])))
+ {
+ // Enum type whose base type is vector.
+ return hashOf(cast(EType) val, seed);
+ }
+ else static if (__traits(isIntegral, val) && T.sizeof <= size_t.sizeof)
+ {
+ static if (size_t.sizeof < ulong.sizeof)
+ {
+ //MurmurHash3 32-bit single round
+ enum uint c1 = 0xcc9e2d51;
+ enum uint c2 = 0x1b873593;
+ enum uint c3 = 0xe6546b64;
+ enum uint r1 = 15;
+ enum uint r2 = 13;
}
else
{
- alias data = val;
+ //Half of MurmurHash3 64-bit single round
+ //(omits second interleaved update)
+ enum ulong c1 = 0x87c37b91114253d5;
+ enum ulong c2 = 0x4cf5ad432745937f;
+ enum ulong c3 = 0x52dce729;
+ enum uint r1 = 31;
+ enum uint r2 = 27;
}
-
- static if (T.mant_dig == float.mant_dig && T.sizeof == uint.sizeof)
+ size_t h = c1 * val;
+ h = (h << r1) | (h >>> (size_t.sizeof * 8 - r1));
+ h = (h * c2) ^ seed;
+ h = (h << r2) | (h >>> (size_t.sizeof * 8 - r2));
+ return h * 5 + c3;
+ }
+ else static if (__traits(isIntegral, val) && T.sizeof > size_t.sizeof)
+ {
+ static foreach (i; 0 .. T.sizeof / size_t.sizeof)
+ seed = hashOf(cast(size_t) (val >>> (size_t.sizeof * 8 * i)), seed);
+ return seed;
+ }
+ else static if (is(T : creal))
+ {
+ return hashOf(val.re, hashOf(val.im, seed));
+ }
+ else static if (__traits(isFloating, T))
+ {
+ auto data = coalesceFloat(val);
+ static if (T.sizeof == float.sizeof && T.mant_dig == float.mant_dig)
return hashOf(*cast(const uint*) &data, seed);
- else static if (T.mant_dig == double.mant_dig && T.sizeof == ulong.sizeof)
+ else static if (T.sizeof == double.sizeof && T.mant_dig == double.mant_dig)
return hashOf(*cast(const ulong*) &data, seed);
else
{
- static if (is(T : creal) && T.sizeof != 2 * floatSize!(typeof(T.re)))
- {
- auto h1 = hashOf(data.re);
- return hashOf(data.im, h1);
- }
- else static if (is(T : real) || is(T : ireal))
- {
- // Ignore trailing padding
- auto bytes = toUbyte(data)[0 .. floatSize!T];
- return bytesHashWithExactSizeAndAlignment!T(bytes, seed);
- }
- else
- {
- return bytesHashWithExactSizeAndAlignment!T(toUbyte(data), seed);
- }
+ import core.internal.convert : floatSize, toUbyte;
+ return bytesHashWithExactSizeAndAlignment!T(toUbyte(data)[0 .. floatSize!T], seed);
}
}
else
{
- static assert(T.sizeof > size_t.sizeof && __traits(isIntegral, T));
- foreach (i; 0 .. T.sizeof / size_t.sizeof)
- seed = hashOf(cast(size_t) (val >>> (size_t.sizeof * 8 * i)), seed);
- return seed;
+ static assert(0);
}
}
-size_t hashOf(T)(scope const auto ref T val, size_t seed = 0) @safe @nogc nothrow pure
-if (is(T == __vector) && !is(T == enum))
+size_t hashOf(T)(scope const T val, size_t seed = 0) @safe @nogc nothrow pure
+if (is(T == __vector)) // excludes enum types
{
static if (__traits(isFloating, T) && (floatCoalesceZeroes || floatCoalesceNaNs))
{
if (__ctfe)
{
// Workaround for CTFE bug.
- alias E = Unqual!(typeof(val[0]));
+ static if (is(immutable typeof(val[0]) == immutable E, E)) {} // Get E.
E[T.sizeof / E.sizeof] array;
foreach (i; 0 .. T.sizeof / E.sizeof)
array[i] = val[i];
@@ -428,6 +379,7 @@ if (is(T == __vector) && !is(T == enum))
}
else
{
+ import core.internal.convert : toUbyte;
return bytesHashAlignedBy!T(toUbyte(val), seed);
}
}
@@ -446,65 +398,48 @@ size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && is(T : ty
return hashOf(size_t(0), seed);
}
-//Pointers hash. CTFE unsupported if not null
-@trusted @nogc nothrow pure
-size_t hashOf(T)(scope const T val)
-if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null))
- && !is(T == struct) && !is(T == class) && !is(T == union))
-{
- if (__ctfe)
- {
- if (val is null)
- {
- return 0;
- }
- else
- {
- assert(0, "Unable to calculate hash of non-null pointer at compile time");
- }
-
- }
- auto addr = cast(size_t) val;
- return addr ^ (addr >>> 4);
-}
-
-//Pointers hash. CTFE unsupported if not null
-@trusted @nogc nothrow pure
-size_t hashOf(T)(scope const T val, size_t seed)
-if (!is(T == enum) && is(T V : V*) && !is(T : typeof(null))
- && !is(T == struct) && !is(T == class) && !is(T == union))
-{
- if (__ctfe)
- {
- if (val is null)
- {
- return hashOf(cast(size_t)0, seed);
- }
- else
- {
- assert(0, "Unable to calculate hash of non-null pointer at compile time");
- }
-
- }
- return hashOf(cast(size_t)val, seed);
-}
-
private enum _hashOfStruct =
q{
enum bool isChained = is(typeof(seed) : size_t);
static if (!isChained) enum size_t seed = 0;
- static if (hasCallableToHash!T) //CTFE depends on toHash()
+ static if (hasCallableToHash!(typeof(val))) //CTFE depends on toHash()
{
- static if (isChained)
- return hashOf(cast(size_t) val.toHash(), seed);
+ static if (!__traits(isSame, typeof(val), __traits(parent, val.toHash))
+ && is(typeof(val is null)))
+ {
+ static if (isChained)
+ return hashOf(__traits(getMember, val, __traits(getAliasThis, typeof(val))), seed);
+ else
+ return hashOf(__traits(getMember, val, __traits(getAliasThis, typeof(val))));
+ }
else
- return val.toHash();
+ {
+ static if (isChained)
+ return hashOf(cast(size_t) val.toHash(), seed);
+ else
+ return val.toHash();
+ }
}
else
{
+ import core.internal.convert : toUbyte;
static if (__traits(hasMember, T, "toHash") && is(typeof(T.toHash) == function))
{
- pragma(msg, "Warning: struct "~__traits(identifier, T)~" has method toHash, however it cannot be called with "~T.stringof~" this.");
+ // TODO: in the future maybe this should be changed to a static
+ // assert(0), because if there's a `toHash` the programmer probably
+ // expected it to be called and a compilation failure here will
+ // expose a bug in his code.
+ // In the future we also might want to disallow non-const toHash
+ // altogether.
+ pragma(msg, "Warning: struct "~__traits(identifier, T)
+ ~" has method toHash, however it cannot be called with "
+ ~typeof(val).stringof~" this.");
+ static if (__traits(compiles, __traits(getLocation, T.toHash)))
+ {
+ enum file = __traits(getLocation, T.toHash)[0];
+ enum line = __traits(getLocation, T.toHash)[1].stringof;
+ pragma(msg, " ",__traits(identifier, T),".toHash defined here: ",file,"(",line,")");
+ }
}
static if (T.tupleof.length == 0)
@@ -513,13 +448,12 @@ q{
}
else static if ((is(T == struct) && !canBitwiseHash!T) || T.tupleof.length == 1)
{
- size_t h = void;
- static if (isChained) h = seed;
- foreach (i, F; typeof(val.tupleof))
+ static if (isChained) size_t h = seed;
+ static foreach (i, F; typeof(val.tupleof))
{
static if (__traits(isStaticArray, F))
{
- static if (i == 0 && !isChained) h = 0;
+ static if (i == 0 && !isChained) size_t h = 0;
static if (F.sizeof > 0 && canBitwiseHash!F)
// May use smallBytesHash instead of bytesHash.
h = bytesHashWithExactSizeAndAlignment!F(toUbyte(val.tupleof[i]), h);
@@ -533,30 +467,41 @@ q{
{
static if (hasCallableToHash!F)
{
- static if (i == 0 && !isChained)
- h = val.tupleof[i].toHash();
+ static if (!__traits(isSame, F, __traits(parent, val.tupleof[i].toHash))
+ && is(typeof(val.tupleof[i] is null)))
+ {
+ static if (i == 0 && !isChained)
+ size_t h = hashOf(__traits(getMember, val.tupleof[i], __traits(getAliasThis, F)));
+ else
+ h = hashOf(__traits(getMember, val.tupleof[i], __traits(getAliasThis, F)), h);
+ }
else
- h = hashOf(cast(size_t) val.tupleof[i].toHash(), h);
+ {
+ static if (i == 0 && !isChained)
+ size_t h = val.tupleof[i].toHash();
+ else
+ h = hashOf(cast(size_t) val.tupleof[i].toHash(), h);
+ }
}
else static if (F.tupleof.length == 1)
{
// Handle the single member case separately to avoid unnecessarily using bytesHash.
static if (i == 0 && !isChained)
- h = hashOf(val.tupleof[i].tupleof[0]);
+ size_t h = hashOf(val.tupleof[i].tupleof[0]);
else
h = hashOf(val.tupleof[i].tupleof[0], h);
}
else static if (canBitwiseHash!F)
{
// May use smallBytesHash instead of bytesHash.
- static if (i == 0 && !isChained) h = 0;
+ static if (i == 0 && !isChained) size_t h = 0;
h = bytesHashWithExactSizeAndAlignment!F(toUbyte(val.tupleof[i]), h);
}
else
{
// Nothing special happening.
static if (i == 0 && !isChained)
- h = hashOf(val.tupleof[i]);
+ size_t h = hashOf(val.tupleof[i]);
else
h = hashOf(val.tupleof[i], h);
}
@@ -565,7 +510,7 @@ q{
{
// Nothing special happening.
static if (i == 0 && !isChained)
- h = hashOf(val.tupleof[i]);
+ size_t h = hashOf(val.tupleof[i]);
else
h = hashOf(val.tupleof[i], h);
}
@@ -592,6 +537,7 @@ q{
//struct or union hash
size_t hashOf(T)(scope const auto ref T val, size_t seed = 0)
if (!is(T == enum) && (is(T == struct) || is(T == union))
+ && !is(T == const) && !is(T == immutable)
&& canBitwiseHash!T)
{
mixin(_hashOfStruct);
@@ -613,13 +559,25 @@ if (!is(T == enum) && (is(T == struct) || is(T == union))
mixin(_hashOfStruct);
}
-//delegate hash. CTFE unsupported
+//struct or union hash - https://issues.dlang.org/show_bug.cgi?id=19332 (support might be removed in future)
+size_t hashOf(T)(scope auto ref T val, size_t seed = 0)
+if (!is(T == enum) && (is(T == struct) || is(T == union))
+ && (is(T == const) || is(T == immutable))
+ && canBitwiseHash!T && !canBitwiseHash!(Unconst!T))
+{
+ mixin(_hashOfStruct);
+}
+
+//delegate hash. CTFE only if null.
@trusted @nogc nothrow pure
size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate))
{
- assert(!__ctfe, "unable to compute hash of "~T.stringof~" at compile time");
- const(ubyte)[] bytes = (cast(const(ubyte)*)&val)[0 .. T.sizeof];
- return bytesHashWithExactSizeAndAlignment!T(bytes, seed);
+ if (__ctfe)
+ {
+ if (val is null) return hashOf(size_t(0), hashOf(size_t(0), seed));
+ assert(0, "unable to compute hash of "~T.stringof~" at compile time");
+ }
+ return hashOf(val.ptr, hashOf(cast(void*) val.funcptr, seed));
}
//address-based class hash. CTFE only if null.
@@ -648,7 +606,13 @@ if (!is(T == enum) && (is(T == interface) || is(T == class))
&& !canBitwiseHash!T)
{
static if (__traits(compiles, {size_t h = val.toHash();}))
- return val ? val.toHash() : 0;
+ {
+ static if (is(__traits(parent, val.toHash) P) && !is(immutable T* : immutable P*)
+ && is(typeof((ref P p) => p is null)))
+ return val ? hashOf(__traits(getMember, val, __traits(getAliasThis, T))) : 0;
+ else
+ return val ? val.toHash() : 0;
+ }
else
return val ? (cast(Object)val).toHash() : 0;
}
@@ -659,7 +623,14 @@ if (!is(T == enum) && (is(T == interface) || is(T == class))
&& !canBitwiseHash!T)
{
static if (__traits(compiles, {size_t h = val.toHash();}))
- return hashOf(val ? cast(size_t) val.toHash() : size_t(0), seed);
+ {
+ static if (is(__traits(parent, val.toHash) P) && !is(immutable T* : immutable P*)
+ && is(typeof((ref P p) => p is null)))
+ return hashOf(val ? hashOf(__traits(getMember, val, __traits(getAliasThis, T)))
+ : size_t(0), seed);
+ else
+ return hashOf(val ? cast(size_t) val.toHash() : size_t(0), seed);
+ }
else
return hashOf(val ? (cast(Object)val).toHash() : 0, seed);
}
diff --git a/libphobos/libdruntime/core/internal/lifetime.d b/libphobos/libdruntime/core/internal/lifetime.d
new file mode 100644
index 00000000000..7e9b5f2ad48
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/lifetime.d
@@ -0,0 +1,213 @@
+module core.internal.lifetime;
+
+import core.lifetime : forward;
+
+/+
+emplaceRef is a package function for druntime internal use. It works like
+emplace, but takes its argument by ref (as opposed to "by pointer").
+This makes it easier to use, easier to be safe, and faster in a non-inline
+build.
+Furthermore, emplaceRef optionally takes a type parameter, which specifies
+the type we want to build. This helps to build qualified objects on mutable
+buffer, without breaking the type system with unsafe casts.
++/
+void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args)
+{
+ static if (args.length == 0)
+ {
+ static assert(is(typeof({static T i;})),
+ "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~
+ ".this() is annotated with @disable.");
+ static if (is(T == class)) static assert(!__traits(isAbstractClass, T),
+ T.stringof ~ " is abstract and it can't be emplaced");
+ emplaceInitializer(chunk);
+ }
+ else static if (
+ !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */
+ ||
+ Args.length == 1 && is(typeof({T t = forward!(args[0]);})) /* conversions */
+ ||
+ is(typeof(T(forward!args))) /* general constructors */)
+ {
+ static struct S
+ {
+ T payload;
+ this()(auto ref Args args)
+ {
+ static if (__traits(compiles, payload = forward!args))
+ payload = forward!args;
+ else
+ payload = T(forward!args);
+ }
+ }
+ if (__ctfe)
+ {
+ static if (__traits(compiles, chunk = T(forward!args)))
+ chunk = T(forward!args);
+ else static if (args.length == 1 && __traits(compiles, chunk = forward!(args[0])))
+ chunk = forward!(args[0]);
+ else assert(0, "CTFE emplace doesn't support "
+ ~ T.stringof ~ " from " ~ Args.stringof);
+ }
+ else
+ {
+ S* p = () @trusted { return cast(S*) &chunk; }();
+ static if (UT.sizeof > 0)
+ emplaceInitializer(*p);
+ p.__ctor(forward!args);
+ }
+ }
+ else static if (is(typeof(chunk.__ctor(forward!args))))
+ {
+ // This catches the rare case of local types that keep a frame pointer
+ emplaceInitializer(chunk);
+ chunk.__ctor(forward!args);
+ }
+ else
+ {
+ //We can't emplace. Try to diagnose a disabled postblit.
+ static assert(!(Args.length == 1 && is(Args[0] : T)),
+ "Cannot emplace a " ~ T.stringof ~ " because " ~ T.stringof ~
+ ".this(this) is annotated with @disable.");
+
+ //We can't emplace.
+ static assert(false,
+ T.stringof ~ " cannot be emplaced from " ~ Args[].stringof ~ ".");
+ }
+}
+
+// ditto
+static import core.internal.traits;
+void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
+if (is(UT == core.internal.traits.Unqual!UT))
+{
+ emplaceRef!(UT, UT)(chunk, forward!args);
+}
+
+/+
+Emplaces T.init.
+In contrast to `emplaceRef(chunk)`, there are no checks for disabled default
+constructors etc.
++/
+template emplaceInitializer(T)
+if (!is(T == const) && !is(T == immutable) && !is(T == inout))
+{
+ import core.internal.traits : hasElaborateAssign, Unqual;
+
+ // Avoid stack allocation by hacking to get to the struct/union init symbol.
+ static if (is(T == struct) || is(T == union))
+ {
+ pragma(mangle, "_D" ~ Unqual!T.mangleof[1..$] ~ "6__initZ")
+ __gshared extern immutable T initializer;
+ }
+
+ void emplaceInitializer(scope ref T chunk) nothrow pure @trusted
+ {
+ static if (__traits(isZeroInit, T))
+ {
+ import core.stdc.string : memset;
+ memset(cast(void*) &chunk, 0, T.sizeof);
+ }
+ else static if (__traits(isScalar, T) ||
+ T.sizeof <= 16 && !hasElaborateAssign!T && __traits(compiles, (){ T chunk; chunk = T.init; }))
+ {
+ chunk = T.init;
+ }
+ else static if (__traits(isStaticArray, T))
+ {
+ // For static arrays there is no initializer symbol created. Instead, we emplace elements one-by-one.
+ foreach (i; 0 .. T.length)
+ {
+ emplaceInitializer(chunk[i]);
+ }
+ }
+ else
+ {
+ import core.stdc.string : memcpy;
+ memcpy(cast(void*)&chunk, &initializer, T.sizeof);
+ }
+ }
+}
+
+@safe unittest
+{
+ static void testInitializer(T)()
+ {
+ // mutable T
+ {
+ T dst = void;
+ emplaceInitializer(dst);
+ assert(dst is T.init);
+ }
+
+ // shared T
+ {
+ shared T dst = void;
+ emplaceInitializer(dst);
+ assert(dst is shared(T).init);
+ }
+
+ // const T
+ {
+ const T dst = void;
+ static assert(!__traits(compiles, emplaceInitializer(dst)));
+ }
+ }
+
+ static struct ElaborateAndZero
+ {
+ int a;
+ this(this) {}
+ }
+
+ static struct ElaborateAndNonZero
+ {
+ int a = 42;
+ this(this) {}
+ }
+
+ static union LargeNonZeroUnion
+ {
+ byte[128] a = 1;
+ }
+
+ testInitializer!int();
+ testInitializer!double();
+ testInitializer!ElaborateAndZero();
+ testInitializer!ElaborateAndNonZero();
+ testInitializer!LargeNonZeroUnion();
+
+ static if (is(__vector(double[4])))
+ {
+ // DMD 2.096 and GDC 11.1 can't compare vectors with `is` so can't use
+ // testInitializer.
+ enum VE : __vector(double[4])
+ {
+ a = [1.0, 2.0, 3.0, double.nan],
+ b = [4.0, 5.0, 6.0, double.nan],
+ }
+ const VE expected = VE.a;
+ VE dst = VE.b;
+ shared VE sharedDst = VE.b;
+ emplaceInitializer(dst);
+ emplaceInitializer(sharedDst);
+ () @trusted {
+ import core.stdc.string : memcmp;
+ assert(memcmp(&expected, &dst, VE.sizeof) == 0);
+ assert(memcmp(&expected, cast(void*) &sharedDst, VE.sizeof) == 0);
+ }();
+ static assert(!__traits(compiles, emplaceInitializer(expected)));
+ }
+}
+
+/*
+Simple swap function.
+*/
+void swap(T)(ref T lhs, ref T rhs)
+{
+ import core.lifetime : move, moveEmplace;
+
+ T tmp = move(lhs);
+ moveEmplace(rhs, lhs);
+ moveEmplace(tmp, rhs);
+}
diff --git a/libphobos/libdruntime/core/internal/moving.d b/libphobos/libdruntime/core/internal/moving.d
new file mode 100644
index 00000000000..9c97d2966f7
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/moving.d
@@ -0,0 +1,147 @@
+/**
+ This module contains the implementation of move semantics of DIP 1014
+
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/_internal/_moving.d)
+*/
+module core.internal.moving;
+
+/**
+Recursively calls the `opPostMove` callbacks of a struct and its members if
+they're defined.
+
+When moving a struct instance, the compiler emits a call to this function
+after blitting the instance and before releasing the original instance's
+memory.
+
+Params:
+ newLocation = reference to struct instance being moved into
+ oldLocation = reference to the original instance
+
+Note:
+ This function is tentatively defined as `nothrow` to prevent
+ `opPostMove` from being defined without `nothrow`, which would allow
+ for possibly confusing changes in program flow.
+*/
+void __move_post_blt(S)(ref S newLocation, ref S oldLocation) nothrow
+ if (is(S == struct))
+{
+ import core.internal.traits : hasElaborateMove;
+ static foreach (i, M; typeof(S.tupleof))
+ {
+ static if (hasElaborateMove!M)
+ {
+ __move_post_blt(newLocation.tupleof[i], oldLocation.tupleof[i]);
+ }
+ }
+
+ static if (__traits(hasMember, S, "opPostMove"))
+ {
+ import core.internal.traits : lvalueOf, rvalueOf;
+ static assert( is(typeof(S.init.opPostMove(lvalueOf!S))) &&
+ !is(typeof(S.init.opPostMove(rvalueOf!S))),
+ "`" ~ S.stringof ~ ".opPostMove` must take exactly one argument of type `" ~ S.stringof ~ "` by reference");
+
+ newLocation.opPostMove(oldLocation);
+ }
+}
+
+void __move_post_blt(S)(ref S newLocation, ref S oldLocation) nothrow
+ if (__traits(isStaticArray, S))
+{
+ import core.internal.traits : hasElaborateMove;
+ static if (S.length && hasElaborateMove!(typeof(newLocation[0])))
+ {
+ foreach (i; 0 .. S.length)
+ __move_post_blt(newLocation[i], oldLocation[i]);
+ }
+}
+
+@safe nothrow unittest
+{
+ struct A
+ {
+ bool movedInto;
+ void opPostMove(const ref A oldLocation)
+ {
+ movedInto = true;
+ }
+ }
+ A src, dest;
+ __move_post_blt(dest, src);
+ assert(dest.movedInto);
+}
+
+@safe nothrow unittest
+{
+ struct A
+ {
+ bool movedInto;
+ void opPostMove(const ref A oldLocation)
+ {
+ movedInto = true;
+ }
+ }
+ struct B
+ {
+ A a;
+
+ bool movedInto;
+ void opPostMove(const ref B oldLocation)
+ {
+ movedInto = true;
+ }
+ }
+ B src, dest;
+ __move_post_blt(dest, src);
+ assert(dest.movedInto && dest.a.movedInto);
+}
+
+@safe nothrow unittest
+{
+ static struct DoNotMove
+ {
+ bool movedInto;
+ void opPostMove(const ref DoNotMove oldLocation)
+ {
+ movedInto = true;
+ }
+ }
+ static DoNotMove doNotMove;
+
+ struct A
+ {
+ @property ref DoNotMove member()
+ {
+ return doNotMove;
+ }
+ }
+ A src, dest;
+ __move_post_blt(dest, src);
+ assert(!doNotMove.movedInto);
+}
+
+@safe nothrow unittest
+{
+ static struct A
+ {
+ bool movedInto;
+ void opPostMove(const ref A oldLocation)
+ {
+ movedInto = true;
+ }
+ }
+ static struct B
+ {
+ A[2] a;
+ }
+ B src, dest;
+ __move_post_blt(dest, src);
+ foreach (ref a; src.a)
+ assert(!a.movedInto);
+ foreach (ref a; dest.a)
+ assert(a.movedInto);
+}
diff --git a/libphobos/libdruntime/core/internal/parseoptions.d b/libphobos/libdruntime/core/internal/parseoptions.d
new file mode 100644
index 00000000000..4e5105d82da
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/parseoptions.d
@@ -0,0 +1,422 @@
+/**
+* parse configuration options
+*
+* Copyright: Copyright Digital Mars 2017
+* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+*
+* Source: $(DRUNTIMESRC src/core/internal/parseoptions.d)
+*/
+
+module core.internal.parseoptions;
+
+import core.stdc.stdlib;
+import core.stdc.stdio;
+import core.stdc.ctype;
+import core.stdc.string;
+import core.vararg;
+import core.internal.traits : externDFunc, hasUDA;
+
+
+@nogc nothrow:
+extern extern(C) string[] rt_args() @system;
+
+extern extern(C) __gshared bool rt_envvars_enabled;
+extern extern(C) __gshared bool rt_cmdline_enabled;
+extern extern(C) __gshared string[] rt_options;
+
+alias rt_configCallBack = string delegate(string) @nogc nothrow;
+alias fn_configOption = string function(string opt, scope rt_configCallBack dg, bool reverse) @nogc nothrow;
+alias rt_configOption = externDFunc!("rt.config.rt_configOption", fn_configOption);
+
+/// UDA for field treated as memory value
+struct MemVal {}
+
+/**
+* initialize members of struct CFG from rt_config options
+*
+* options will be read from the environment, the command line or embedded
+* into the executable as configured (see rt.config)
+*
+* fields of the struct are populated by parseOptions().
+*/
+bool initConfigOptions(CFG)(ref CFG cfg, string cfgname)
+{
+ string parse(string opt) @nogc nothrow
+ {
+ if (!parseOptions(cfg, opt))
+ return "err";
+ return null; // continue processing
+ }
+ string s = rt_configOption(cfgname, &parse, true);
+ return s is null;
+}
+
+/**
+* initialize members of struct CFG from a string of sub-options.
+*
+* fields of the struct are populated by listing them as space separated
+* sub-options <field-name>:value, e.g. "precise:1 profile:1"
+*
+* supported field value types:
+* - strings (without spaces)
+* - integer types (positive values only)
+* - bool
+* - float
+*
+* If the struct has a member "help" it is called if it is found as a sub-option.
+* If the struct has a member "errorName", is used as the name reported in error
+* messages. Otherwise the struct name is used.
+*/
+bool parseOptions(CFG)(ref CFG cfg, string opt)
+{
+ static if (is(typeof(__traits(getMember, CFG, "errorName"))))
+ string errName = cfg.errorName;
+ else
+ string errName = CFG.stringof;
+ opt = skip!isspace(opt);
+ while (opt.length)
+ {
+ auto tail = find!(c => c == ':' || c == '=' || c == ' ')(opt);
+ auto name = opt[0 .. $ - tail.length];
+ static if (is(typeof(__traits(getMember, CFG, "help"))))
+ if (name == "help")
+ {
+ version (CoreUnittest) {} else
+ cfg.help();
+ opt = skip!isspace(tail);
+ continue;
+ }
+ if (tail.length <= 1 || tail[0] == ' ')
+ return optError("Missing argument for", name, errName);
+ tail = tail[1 .. $];
+
+ NAMES_SWITCH:
+ switch (name)
+ {
+ static foreach (field; __traits(allMembers, CFG))
+ {
+ static if (!is(typeof(__traits(getMember, cfg, field)) == function))
+ {
+ case field:
+ bool r;
+
+ static if (hasUDA!(__traits(getMember, cfg, field), MemVal))
+ r = parse(name, tail, __traits(getMember, cfg, field), errName, true);
+ else
+ r = parse(name, tail, __traits(getMember, cfg, field), errName);
+
+ if (!r)
+ return false;
+
+ break NAMES_SWITCH;
+ }
+ }
+
+ default:
+ return optError("Unknown", name, errName);
+ }
+ opt = skip!isspace(tail);
+ }
+ return true;
+}
+
+/**
+Parses an individual option `optname` value from a provided string `str`.
+The option type is given by the type `T` of the field `res` to which the parsed
+value will be written too.
+In case of an error, `errName` will be used to display an error message and
+the failure of the parsing will be indicated by a falsy return value.
+
+For boolean values, '0/n/N' (false) or '1/y/Y' (true) are accepted.
+
+Params:
+ optname = name of the option to parse
+ str = raw string to parse the option value from
+ res = reference to the resulting data field that the option should be parsed too
+ errName = full-text name of the option which should be displayed in case of errors
+
+Returns: `false` if a parsing error happened.
+*/
+bool rt_parseOption(T)(const(char)[] optname, ref inout(char)[] str, ref T res, const(char)[] errName)
+{
+ return parse(optname, str, res, errName);
+}
+
+private:
+
+bool optError(const scope char[] msg, const scope char[] name, const(char)[] errName)
+{
+ version (CoreUnittest) if (inUnittest) return false;
+
+ fprintf(stderr, "%.*s %.*s option '%.*s'.\n",
+ cast(int)msg.length, msg.ptr,
+ cast(int)errName.length, errName.ptr,
+ cast(int)name.length, name.ptr);
+ return false;
+}
+
+inout(char)[] skip(alias pred)(inout(char)[] str)
+{
+ return find!(c => !pred(c))(str);
+}
+
+inout(char)[] find(alias pred)(inout(char)[] str)
+{
+ foreach (i; 0 .. str.length)
+ if (pred(str[i])) return str[i .. $];
+ return null;
+}
+
+bool parse(T : size_t)(const(char)[] optname, ref inout(char)[] str, ref T res, const(char)[] errName, bool mayHaveSuffix = false)
+in { assert(str.length); }
+do
+{
+ size_t i, v;
+
+ auto tail = find!(c => c == ' ')(str);
+ size_t len = str.length - tail.length;
+
+ import core.checkedint : mulu;
+
+ bool overflowed;
+
+ for (; i < len; i++)
+ {
+ char c = str[i];
+
+ if (isdigit(c))
+ v = 10 * v + c - '0';
+ else // non-digit
+ {
+ if (mayHaveSuffix && i == len-1) // suffix
+ {
+ switch (c)
+ {
+
+ case 'G':
+ v = mulu(v, 1024 * 1024 * 1024, overflowed);
+ break;
+
+ case 'M':
+ v = mulu(v, 1024 * 1024, overflowed);
+ break;
+
+ case 'K':
+ v = mulu(v, 1024, overflowed);
+ break;
+
+ case 'B':
+ break;
+
+ default:
+ return parseError("value with unit type M, K or B", optname, str, "with suffix");
+ }
+
+ if (overflowed)
+ return overflowedError(optname, str);
+
+ i++;
+ break;
+ }
+ else // unexpected non-digit character
+ {
+ i = 0;
+ break;
+ }
+ }
+ }
+
+ if (!i)
+ return parseError("a number", optname, str, errName);
+
+ if (mayHaveSuffix && isdigit(str[len-1]))
+ {
+ // No suffix found, default to megabytes
+
+ v = mulu(v, 1024 * 1024, overflowed);
+
+ if (overflowed)
+ return overflowedError(optname, str);
+ }
+
+ if (v > res.max)
+ return parseError("a number " ~ T.max.stringof ~ " or below", optname, str[0 .. i], errName);
+ str = str[i .. $];
+ res = cast(T) v;
+ return true;
+}
+
+bool parse(const(char)[] optname, ref inout(char)[] str, ref bool res, const(char)[] errName)
+in { assert(str.length); }
+do
+{
+ if (str[0] == '1' || str[0] == 'y' || str[0] == 'Y')
+ res = true;
+ else if (str[0] == '0' || str[0] == 'n' || str[0] == 'N')
+ res = false;
+ else
+ return parseError("'0/n/N' or '1/y/Y'", optname, str, errName);
+ str = str[1 .. $];
+ return true;
+}
+
+bool parse(const(char)[] optname, ref inout(char)[] str, ref float res, const(char)[] errName)
+in { assert(str.length); }
+do
+{
+ // % uint f %n \0
+ char[1 + 10 + 1 + 2 + 1] fmt=void;
+ // specify max-width
+ immutable n = snprintf(fmt.ptr, fmt.length, "%%%uf%%n", cast(uint)str.length);
+ assert(n > 4 && n < fmt.length);
+
+ int nscanned;
+ version (CRuntime_DigitalMars)
+ {
+ /* Older sscanf's in snn.lib can write to its first argument, causing a crash
+ * if the string is in readonly memory. Recent updates to DMD
+ * https://github.com/dlang/dmd/pull/6546
+ * put string literals in readonly memory.
+ * Although sscanf has been fixed,
+ * http://ftp.digitalmars.com/snn.lib
+ * this workaround is here so it still works with the older snn.lib.
+ */
+ // Create mutable copy of str
+ const length = str.length;
+ char* mptr = cast(char*)malloc(length + 1);
+ assert(mptr);
+ memcpy(mptr, str.ptr, length);
+ mptr[length] = 0;
+ const result = sscanf(mptr, fmt.ptr, &res, &nscanned);
+ free(mptr);
+ if (result < 1)
+ return parseError("a float", optname, str, errName);
+ }
+ else
+ {
+ if (sscanf(str.ptr, fmt.ptr, &res, &nscanned) < 1)
+ return parseError("a float", optname, str, errName);
+ }
+ str = str[nscanned .. $];
+ return true;
+}
+
+bool parse(const(char)[] optname, ref inout(char)[] str, ref inout(char)[] res, const(char)[] errName)
+in { assert(str.length); }
+do
+{
+ auto tail = str.find!(c => c == ' ');
+ res = str[0 .. $ - tail.length];
+ if (!res.length)
+ return parseError("an identifier", optname, str, errName);
+ str = tail;
+ return true;
+}
+
+bool parseError(const scope char[] exp, const scope char[] opt, const scope char[] got, const(char)[] errName)
+{
+ version (CoreUnittest) if (inUnittest) return false;
+
+ fprintf(stderr, "Expecting %.*s as argument for %.*s option '%.*s', got '%.*s' instead.\n",
+ cast(int)exp.length, exp.ptr,
+ cast(int)errName.length, errName.ptr,
+ cast(int)opt.length, opt.ptr,
+ cast(int)got.length, got.ptr);
+ return false;
+}
+
+bool overflowedError(const scope char[] opt, const scope char[] got)
+{
+ version (CoreUnittest) if (inUnittest) return false;
+
+ fprintf(stderr, "Argument for %.*s option '%.*s' is too big.\n",
+ cast(int)opt.length, opt.ptr,
+ cast(int)got.length, got.ptr);
+ return false;
+}
+
+size_t min(size_t a, size_t b) { return a <= b ? a : b; }
+
+version (CoreUnittest) __gshared bool inUnittest;
+
+unittest
+{
+ inUnittest = true;
+ scope (exit) inUnittest = false;
+
+ static struct Config
+ {
+ bool disable; // start disabled
+ ubyte profile; // enable profiling with summary when terminating program
+ string gc = "conservative"; // select gc implementation conservative|manual
+
+ @MemVal size_t initReserve; // initial reserve (bytes)
+ @MemVal size_t minPoolSize = 1 << 20; // initial and minimum pool size (bytes)
+ float heapSizeFactor = 2.0; // heap size to used memory ratio
+
+ @nogc nothrow:
+ void help();
+ string errorName() @nogc nothrow { return "GC"; }
+ }
+ Config conf;
+
+ assert(!conf.parseOptions("disable"));
+ assert(!conf.parseOptions("disable:"));
+ assert(!conf.parseOptions("disable:5"));
+ assert(conf.parseOptions("disable:y") && conf.disable);
+ assert(conf.parseOptions("disable:n") && !conf.disable);
+ assert(conf.parseOptions("disable:Y") && conf.disable);
+ assert(conf.parseOptions("disable:N") && !conf.disable);
+ assert(conf.parseOptions("disable:1") && conf.disable);
+ assert(conf.parseOptions("disable:0") && !conf.disable);
+
+ assert(conf.parseOptions("disable=y") && conf.disable);
+ assert(conf.parseOptions("disable=n") && !conf.disable);
+
+ assert(conf.parseOptions("profile=0") && conf.profile == 0);
+ assert(conf.parseOptions("profile=1") && conf.profile == 1);
+ assert(conf.parseOptions("profile=2") && conf.profile == 2);
+ assert(!conf.parseOptions("profile=256"));
+
+ assert(conf.parseOptions("disable:1 minPoolSize:16"));
+ assert(conf.disable);
+ assert(conf.minPoolSize == 1024 * 1024 * 16);
+
+ assert(conf.parseOptions("disable:1 minPoolSize:4096B"));
+ assert(conf.disable);
+ assert(conf.minPoolSize == 4096);
+
+ assert(conf.parseOptions("disable:1 minPoolSize:2K help"));
+ assert(conf.disable);
+ assert(conf.minPoolSize == 2048);
+
+ assert(conf.parseOptions("minPoolSize:3G help"));
+ assert(conf.disable);
+ assert(conf.minPoolSize == 1024UL * 1024 * 1024 * 3);
+
+ assert(!conf.parseOptions("minPoolSize:922337203685477G"), "size_t overflow");
+
+ assert(conf.parseOptions("heapSizeFactor:3.1"));
+ assert(conf.heapSizeFactor == 3.1f);
+ assert(conf.parseOptions("heapSizeFactor:3.1234567890 disable:0"));
+ assert(conf.heapSizeFactor > 3.123f);
+ assert(!conf.disable);
+ assert(!conf.parseOptions("heapSizeFactor:3.0.2.5"));
+ assert(conf.parseOptions("heapSizeFactor:2"));
+ assert(conf.heapSizeFactor == 2.0f);
+
+ assert(!conf.parseOptions("initReserve:foo"));
+ assert(!conf.parseOptions("initReserve:y"));
+ assert(!conf.parseOptions("initReserve:20.5"));
+
+ assert(conf.parseOptions("help"));
+ assert(conf.parseOptions("help profile:1"));
+ assert(conf.parseOptions("help profile:1 help"));
+
+ assert(conf.parseOptions("gc:manual") && conf.gc == "manual");
+ assert(conf.parseOptions("gc:my-gc~modified") && conf.gc == "my-gc~modified");
+ assert(conf.parseOptions("gc:conservative help profile:1") && conf.gc == "conservative" && conf.profile == 1);
+
+ // the config parse doesn't know all available GC names, so should accept unknown ones
+ assert(conf.parseOptions("gc:whatever"));
+}
diff --git a/libphobos/libdruntime/core/internal/postblit.d b/libphobos/libdruntime/core/internal/postblit.d
new file mode 100644
index 00000000000..ed4ec1b7ff4
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/postblit.d
@@ -0,0 +1,274 @@
+/**
+ This module contains support for D's postblit feature
+
+ Copyright: Copyright Digital Mars 2000 - 2019.
+ License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+ Source: $(DRUNTIMESRC core/_internal/_destruction.d)
+*/
+module core.internal.postblit;
+
+// compiler frontend lowers struct array postblitting to this
+void __ArrayPostblit(T)(T[] a)
+{
+ foreach (ref T e; a)
+ e.__xpostblit();
+}
+
+package void postblitRecurse(S)(ref S s)
+ if (is(S == struct))
+{
+ static if (__traits(hasMember, S, "__xpostblit") &&
+ // Bugzilla 14746: Check that it's the exact member of S.
+ __traits(isSame, S, __traits(parent, s.__xpostblit)))
+ s.__xpostblit();
+}
+
+package void postblitRecurse(E, size_t n)(ref E[n] arr)
+{
+ import core.internal.destruction: destructRecurse;
+ import core.internal.traits : hasElaborateCopyConstructor;
+
+ static if (hasElaborateCopyConstructor!E)
+ {
+ size_t i;
+ scope(failure)
+ {
+ for (; i != 0; --i)
+ {
+ destructRecurse(arr[i - 1]); // What to do if this throws?
+ }
+ }
+
+ for (i = 0; i < arr.length; ++i)
+ postblitRecurse(arr[i]);
+ }
+}
+
+// Test destruction/postblit order
+@safe nothrow pure unittest
+{
+ string[] order;
+
+ struct InnerTop
+ {
+ ~this() @safe nothrow pure
+ {
+ order ~= "destroy inner top";
+ }
+
+ this(this) @safe nothrow pure
+ {
+ order ~= "copy inner top";
+ }
+ }
+
+ struct InnerMiddle {}
+
+ version (none) // https://issues.dlang.org/show_bug.cgi?id=14242
+ struct InnerElement
+ {
+ static char counter = '1';
+
+ ~this() @safe nothrow pure
+ {
+ order ~= "destroy inner element #" ~ counter++;
+ }
+
+ this(this) @safe nothrow pure
+ {
+ order ~= "copy inner element #" ~ counter++;
+ }
+ }
+
+ struct InnerBottom
+ {
+ ~this() @safe nothrow pure
+ {
+ order ~= "destroy inner bottom";
+ }
+
+ this(this) @safe nothrow pure
+ {
+ order ~= "copy inner bottom";
+ }
+ }
+
+ struct S
+ {
+ char[] s;
+ InnerTop top;
+ InnerMiddle middle;
+ version (none) InnerElement[3] array; // https://issues.dlang.org/show_bug.cgi?id=14242
+ int a;
+ InnerBottom bottom;
+ ~this() @safe nothrow pure { order ~= "destroy outer"; }
+ this(this) @safe nothrow pure { order ~= "copy outer"; }
+ }
+
+ string[] destructRecurseOrder;
+ {
+ import core.internal.destruction: destructRecurse;
+
+ S s;
+ destructRecurse(s);
+ destructRecurseOrder = order;
+ order = null;
+ }
+
+ assert(order.length);
+ assert(destructRecurseOrder == order);
+ order = null;
+
+ S s;
+ postblitRecurse(s);
+ assert(order.length);
+ auto postblitRecurseOrder = order;
+ order = null;
+ S s2 = s;
+ assert(order.length);
+ assert(postblitRecurseOrder == order);
+}
+
+@safe unittest
+{
+ // Bugzilla 14746
+ static struct HasPostblit
+ {
+ this(this) { assert(0); }
+ }
+ static struct Owner
+ {
+ HasPostblit* ptr;
+ alias ptr this;
+ }
+
+ Owner o;
+ assert(o.ptr is null);
+ postblitRecurse(o); // must not reach in HasPostblit.__postblit()
+}
+
+// Test handling of fixed-length arrays
+// Separate from first test because of https://issues.dlang.org/show_bug.cgi?id=14242
+@safe unittest
+{
+ string[] order;
+
+ struct S
+ {
+ char id;
+
+ this(this)
+ {
+ order ~= "copy #" ~ id;
+ }
+
+ ~this()
+ {
+ order ~= "destroy #" ~ id;
+ }
+ }
+
+ string[] destructRecurseOrder;
+ {
+ import core.internal.destruction: destructRecurse;
+
+ S[3] arr = [S('1'), S('2'), S('3')];
+ destructRecurse(arr);
+ destructRecurseOrder = order;
+ order = null;
+ }
+ assert(order.length);
+ assert(destructRecurseOrder == order);
+ order = null;
+
+ S[3] arr = [S('1'), S('2'), S('3')];
+ postblitRecurse(arr);
+ assert(order.length);
+ auto postblitRecurseOrder = order;
+ order = null;
+
+ auto arrCopy = arr;
+ assert(order.length);
+ assert(postblitRecurseOrder == order);
+}
+
+// Test handling of failed postblit
+// Not nothrow or @safe because of https://issues.dlang.org/show_bug.cgi?id=14242
+/+ nothrow @safe +/ unittest
+{
+ static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } }
+ static string[] order;
+ static struct Inner
+ {
+ char id;
+
+ @safe:
+ this(this)
+ {
+ order ~= "copy inner #" ~ id;
+ if (id == '2')
+ throw new FailedPostblitException();
+ }
+
+ ~this() nothrow
+ {
+ order ~= "destroy inner #" ~ id;
+ }
+ }
+
+ static struct Outer
+ {
+ Inner inner1, inner2, inner3;
+
+ nothrow @safe:
+ this(char first, char second, char third)
+ {
+ inner1 = Inner(first);
+ inner2 = Inner(second);
+ inner3 = Inner(third);
+ }
+
+ this(this)
+ {
+ order ~= "copy outer";
+ }
+
+ ~this()
+ {
+ order ~= "destroy outer";
+ }
+ }
+
+ auto outer = Outer('1', '2', '3');
+
+ try postblitRecurse(outer);
+ catch (FailedPostblitException) {}
+ catch (Exception) assert(false);
+
+ auto postblitRecurseOrder = order;
+ order = null;
+
+ try auto copy = outer;
+ catch (FailedPostblitException) {}
+ catch (Exception) assert(false);
+
+ assert(postblitRecurseOrder == order);
+ order = null;
+
+ Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')];
+
+ try postblitRecurse(arr);
+ catch (FailedPostblitException) {}
+ catch (Exception) assert(false);
+
+ postblitRecurseOrder = order;
+ order = null;
+
+ try auto arrCopy = arr;
+ catch (FailedPostblitException) {}
+ catch (Exception) assert(false);
+
+ assert(postblitRecurseOrder == order);
+}
diff --git a/libphobos/libdruntime/core/internal/qsort.d b/libphobos/libdruntime/core/internal/qsort.d
new file mode 100644
index 00000000000..ad8307a2523
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/qsort.d
@@ -0,0 +1,196 @@
+/**
+ * This is a public domain version of qsort.d. All it does is call C's
+ * qsort().
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2010.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright, Martin Nowak
+ */
+module core.internal.qsort;
+
+//debug=qsort;
+
+import core.stdc.stdlib;
+
+version (OSX)
+ version = Darwin;
+else version (iOS)
+ version = Darwin;
+else version (TVOS)
+ version = Darwin;
+else version (WatchOS)
+ version = Darwin;
+
+// qsort_r was added in glibc in 2.8. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88127
+version (CRuntime_Glibc)
+{
+ version (GNU)
+ {
+ import gcc.config : Have_Qsort_R;
+ enum Glibc_Qsort_R = Have_Qsort_R;
+ }
+ else
+ {
+ enum Glibc_Qsort_R = true;
+ }
+}
+else
+{
+ enum Glibc_Qsort_R = false;
+}
+
+static if (Glibc_Qsort_R)
+{
+ alias extern (C) int function(scope const void *, scope const void *, scope void *) Cmp;
+ extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, Cmp cmp, scope void *arg);
+
+ extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
+ {
+ extern (C) int cmp(scope const void* p1, scope const void* p2, scope void* ti)
+ {
+ return (cast(TypeInfo)ti).compare(p1, p2);
+ }
+ qsort_r(a.ptr, a.length, ti.tsize, &cmp, cast(void*)ti);
+ return a;
+ }
+}
+else version (FreeBSD)
+{
+ alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp;
+ extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp);
+
+ extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
+ {
+ extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2)
+ {
+ return (cast(TypeInfo)ti).compare(p1, p2);
+ }
+ qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp);
+ return a;
+ }
+}
+else version (DragonFlyBSD)
+{
+ alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp;
+ extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp);
+
+ extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
+ {
+ extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2)
+ {
+ return (cast(TypeInfo)ti).compare(p1, p2);
+ }
+ qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp);
+ return a;
+ }
+}
+else version (Darwin)
+{
+ alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp;
+ extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp);
+
+ extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
+ {
+ extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2)
+ {
+ return (cast(TypeInfo)ti).compare(p1, p2);
+ }
+ qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp);
+ return a;
+ }
+}
+else version (CRuntime_UClibc)
+{
+ alias extern (C) int function(scope const void *, scope const void *, scope void *) __compar_d_fn_t;
+ extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, __compar_d_fn_t cmp, scope void *arg);
+
+ extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
+ {
+ extern (C) int cmp(scope const void* p1, scope const void* p2, scope void* ti)
+ {
+ return (cast(TypeInfo)ti).compare(p1, p2);
+ }
+ qsort_r(a.ptr, a.length, ti.tsize, &cmp, cast(void*)ti);
+ return a;
+ }
+}
+else
+{
+ private TypeInfo tiglobal;
+
+ extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
+ {
+ extern (C) int cmp(scope const void* p1, scope const void* p2)
+ {
+ return tiglobal.compare(p1, p2);
+ }
+ tiglobal = ti;
+ qsort(a.ptr, a.length, ti.tsize, &cmp);
+ return a;
+ }
+}
+
+unittest
+{
+ debug(qsort) printf("array.sort.unittest()\n");
+
+ int[] a = new int[10];
+
+ a[0] = 23;
+ a[1] = 1;
+ a[2] = 64;
+ a[3] = 5;
+ a[4] = 6;
+ a[5] = 5;
+ a[6] = 17;
+ a[7] = 3;
+ a[8] = 0;
+ a[9] = -1;
+
+ _adSort(*cast(void[]*)&a, typeid(a[0]));
+
+ for (int i = 0; i < a.length - 1; i++)
+ {
+ //printf("i = %d", i);
+ //printf(" %d %d\n", a[i], a[i + 1]);
+ assert(a[i] <= a[i + 1]);
+ }
+}
+
+unittest
+{
+ debug(qsort) printf("struct.sort.unittest()\n");
+
+ static struct TestStruct
+ {
+ int value;
+
+ int opCmp(const TestStruct rhs) const
+ {
+ return value <= rhs.value ?
+ value < rhs.value ? -1 : 0 : 1;
+ }
+ }
+
+ TestStruct[] a = new TestStruct[10];
+
+ a[0] = TestStruct(23);
+ a[1] = TestStruct(1);
+ a[2] = TestStruct(64);
+ a[3] = TestStruct(5);
+ a[4] = TestStruct(6);
+ a[5] = TestStruct(5);
+ a[6] = TestStruct(17);
+ a[7] = TestStruct(3);
+ a[8] = TestStruct(0);
+ a[9] = TestStruct(-1);
+
+ _adSort(*cast(void[]*)&a, typeid(TestStruct));
+
+ for (int i = 0; i < a.length - 1; i++)
+ {
+ //printf("i = %d", i);
+ //printf(" %d %d\n", a[i], a[i + 1]);
+ assert(a[i] <= a[i + 1]);
+ }
+}
diff --git a/libphobos/libdruntime/core/internal/spinlock.d b/libphobos/libdruntime/core/internal/spinlock.d
index fb689dc561f..36d806ad014 100644
--- a/libphobos/libdruntime/core/internal/spinlock.d
+++ b/libphobos/libdruntime/core/internal/spinlock.d
@@ -2,7 +2,7 @@
* SpinLock for runtime internal usage.
*
* Copyright: Copyright Digital Mars 2015 -.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Martin Nowak
* Source: $(DRUNTIMESRC core/internal/_spinlock.d)
*/
@@ -50,8 +50,9 @@ shared struct SpinLock
/// yield with backoff
void yield(size_t k)
{
+ import core.time;
if (k < pauseThresh)
- return pause();
+ return core.atomic.pause();
else if (k < 32)
return Thread.yield();
Thread.sleep(1.msecs);
@@ -66,25 +67,9 @@ private:
enum X86 = false;
static if (X86)
- {
enum pauseThresh = 16;
- void pause()
- {
- asm @trusted @nogc nothrow
- {
- // pause instruction
- rep;
- nop;
- }
- }
- }
else
- {
enum pauseThresh = 4;
- void pause()
- {
- }
- }
size_t val;
Contention contention;
@@ -93,7 +78,7 @@ private:
// aligned to cacheline to avoid false sharing
shared align(64) struct AlignedSpinLock
{
- this(SpinLock.Contention contention)
+ this(SpinLock.Contention contention) @trusted @nogc nothrow
{
impl = shared(SpinLock)(contention);
}
diff --git a/libphobos/libdruntime/core/internal/string.d b/libphobos/libdruntime/core/internal/string.d
index d2144c75a32..529fee49436 100644
--- a/libphobos/libdruntime/core/internal/string.d
+++ b/libphobos/libdruntime/core/internal/string.d
@@ -2,9 +2,9 @@
* String manipulation and comparison utilities.
*
* Copyright: Copyright Sean Kelly 2005 - 2009.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Sean Kelly, Walter Bright
- * Source: $(DRUNTIMESRC src/rt/util/_string.d)
+ * Source: $(DRUNTIMESRC rt/util/_string.d)
*/
module core.internal.string;
@@ -15,66 +15,107 @@ nothrow:
alias UnsignedStringBuf = char[20];
-char[] unsignedToTempString(ulong value, return char[] buf, uint radix = 10) @safe
+/**
+Converts an unsigned integer value to a string of characters.
+
+This implementation is a template so it can be used when compiling with -betterC.
+
+Params:
+ value = the unsigned integer value to convert
+ buf = the pre-allocated buffer used to store the result
+ radix = the numeric base to use in the conversion (defaults to 10)
+
+Returns:
+ The unsigned integer value as a string of characters
+*/
+char[] unsignedToTempString(uint radix = 10)(ulong value, return scope char[] buf) @safe
+if (radix >= 2 && radix <= 16)
{
size_t i = buf.length;
do
{
- ubyte x = cast(ubyte)(value % radix);
- value = value / radix;
- buf[--i] = cast(char)((x < 10) ? x + '0' : x - 10 + 'a');
+ uint x = void;
+ if (value < radix)
+ {
+ x = cast(uint)value;
+ value = 0;
+ }
+ else
+ {
+ x = cast(uint)(value % radix);
+ value /= radix;
+ }
+ buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + 'a');
} while (value);
return buf[i .. $];
}
-private struct TempStringNoAlloc
+private struct TempStringNoAlloc(ubyte N)
{
- // need to handle 65 bytes for radix of 2 with negative sign.
- private char[65] _buf;
+ private char[N] _buf = void;
private ubyte _len;
- auto get() return
+ inout(char)[] get() inout return
{
return _buf[$-_len..$];
}
alias get this;
}
-auto unsignedToTempString(ulong value, uint radix) @safe
+/**
+Converts an unsigned integer value to a string of characters.
+
+This implementation is a template so it can be used when compiling with -betterC.
+
+Params:
+ value = the unsigned integer value to convert
+ radix = the numeric base to use in the conversion (defaults to 10)
+
+Returns:
+ The unsigned integer value as a string of characters
+*/
+auto unsignedToTempString(uint radix = 10)(ulong value) @safe
{
- TempStringNoAlloc result = void;
- result._len = unsignedToTempString(value, result._buf, radix).length & 0xff;
+ // Need a buffer of 65 bytes for radix of 2 with room for
+ // signedToTempString to possibly add a negative sign.
+ enum bufferSize = radix >= 10 ? 20 : 65;
+ TempStringNoAlloc!bufferSize result = void;
+ result._len = unsignedToTempString!radix(value, result._buf).length & 0xff;
return result;
}
unittest
{
UnsignedStringBuf buf;
- assert(0.unsignedToTempString(buf, 10) == "0");
- assert(1.unsignedToTempString(buf, 10) == "1");
- assert(12.unsignedToTempString(buf, 10) == "12");
- assert(0x12ABCF .unsignedToTempString(buf, 16) == "12abcf");
- assert(long.sizeof.unsignedToTempString(buf, 10) == "8");
- assert(uint.max.unsignedToTempString(buf, 10) == "4294967295");
- assert(ulong.max.unsignedToTempString(buf, 10) == "18446744073709551615");
+ assert(0.unsignedToTempString(buf) == "0");
+ assert(1.unsignedToTempString(buf) == "1");
+ assert(12.unsignedToTempString(buf) == "12");
+ assert(0x12ABCF .unsignedToTempString!16(buf) == "12abcf");
+ assert(long.sizeof.unsignedToTempString(buf) == "8");
+ assert(uint.max.unsignedToTempString(buf) == "4294967295");
+ assert(ulong.max.unsignedToTempString(buf) == "18446744073709551615");
// use stack allocated struct version
- assert(0.unsignedToTempString(10) == "0");
- assert(1.unsignedToTempString(10) == "1");
- assert(12.unsignedToTempString(10) == "12");
- assert(0x12ABCF .unsignedToTempString(16) == "12abcf");
- assert(long.sizeof.unsignedToTempString(10) == "8");
- assert(uint.max.unsignedToTempString(10) == "4294967295");
- assert(ulong.max.unsignedToTempString(10) == "18446744073709551615");
+ assert(0.unsignedToTempString == "0");
+ assert(1.unsignedToTempString == "1");
+ assert(12.unsignedToTempString == "12");
+ assert(0x12ABCF .unsignedToTempString!16 == "12abcf");
+ assert(long.sizeof.unsignedToTempString == "8");
+ assert(uint.max.unsignedToTempString == "4294967295");
+ assert(ulong.max.unsignedToTempString == "18446744073709551615");
+
+ // test bad radices
+ assert(!is(typeof(100.unsignedToTempString!1(buf))));
+ assert(!is(typeof(100.unsignedToTempString!0(buf) == "")));
}
alias SignedStringBuf = char[20];
-char[] signedToTempString(long value, return char[] buf, uint radix) @safe
+char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) @safe
{
bool neg = value < 0;
if (neg)
value = cast(ulong)-value;
- auto r = unsignedToTempString(value, buf, radix);
+ auto r = unsignedToTempString!radix(value, buf);
if (neg)
{
// about to do a slice without a bounds check
@@ -85,12 +126,12 @@ char[] signedToTempString(long value, return char[] buf, uint radix) @safe
return r;
}
-auto signedToTempString(long value, uint radix) @safe
+auto signedToTempString(uint radix = 10)(long value) @safe
{
bool neg = value < 0;
if (neg)
value = cast(ulong)-value;
- auto r = unsignedToTempString(value, radix);
+ auto r = unsignedToTempString!radix(value);
if (neg)
{
r._len++;
@@ -102,34 +143,34 @@ auto signedToTempString(long value, uint radix) @safe
unittest
{
SignedStringBuf buf;
- assert(0.signedToTempString(buf, 10) == "0");
- assert(1.signedToTempString(buf, 10) == "1");
- assert((-1).signedToTempString(buf, 10) == "-1");
- assert(12.signedToTempString(buf, 10) == "12");
- assert((-12).signedToTempString(buf, 10) == "-12");
- assert(0x12ABCF .signedToTempString(buf, 16) == "12abcf");
- assert((-0x12ABCF) .signedToTempString(buf, 16) == "-12abcf");
- assert(long.sizeof.signedToTempString(buf, 10) == "8");
- assert(int.max.signedToTempString(buf, 10) == "2147483647");
- assert(int.min.signedToTempString(buf, 10) == "-2147483648");
- assert(long.max.signedToTempString(buf, 10) == "9223372036854775807");
- assert(long.min.signedToTempString(buf, 10) == "-9223372036854775808");
+ assert(0.signedToTempString(buf) == "0");
+ assert(1.signedToTempString(buf) == "1");
+ assert((-1).signedToTempString(buf) == "-1");
+ assert(12.signedToTempString(buf) == "12");
+ assert((-12).signedToTempString(buf) == "-12");
+ assert(0x12ABCF .signedToTempString!16(buf) == "12abcf");
+ assert((-0x12ABCF) .signedToTempString!16(buf) == "-12abcf");
+ assert(long.sizeof.signedToTempString(buf) == "8");
+ assert(int.max.signedToTempString(buf) == "2147483647");
+ assert(int.min.signedToTempString(buf) == "-2147483648");
+ assert(long.max.signedToTempString(buf) == "9223372036854775807");
+ assert(long.min.signedToTempString(buf) == "-9223372036854775808");
// use stack allocated struct version
- assert(0.signedToTempString(10) == "0");
- assert(1.signedToTempString(10) == "1");
- assert((-1).signedToTempString(10) == "-1");
- assert(12.signedToTempString(10) == "12");
- assert((-12).signedToTempString(10) == "-12");
- assert(0x12ABCF .signedToTempString(16) == "12abcf");
- assert((-0x12ABCF) .signedToTempString(16) == "-12abcf");
- assert(long.sizeof.signedToTempString(10) == "8");
- assert(int.max.signedToTempString(10) == "2147483647");
- assert(int.min.signedToTempString(10) == "-2147483648");
- assert(long.max.signedToTempString(10) == "9223372036854775807");
- assert(long.min.signedToTempString(10) == "-9223372036854775808");
- assert(long.max.signedToTempString(2) == "111111111111111111111111111111111111111111111111111111111111111");
- assert(long.min.signedToTempString(2) == "-1000000000000000000000000000000000000000000000000000000000000000");
+ assert(0.signedToTempString() == "0");
+ assert(1.signedToTempString == "1");
+ assert((-1).signedToTempString == "-1");
+ assert(12.signedToTempString == "12");
+ assert((-12).signedToTempString == "-12");
+ assert(0x12ABCF .signedToTempString!16 == "12abcf");
+ assert((-0x12ABCF) .signedToTempString!16 == "-12abcf");
+ assert(long.sizeof.signedToTempString == "8");
+ assert(int.max.signedToTempString == "2147483647");
+ assert(int.min.signedToTempString == "-2147483648");
+ assert(long.max.signedToTempString == "9223372036854775807");
+ assert(long.min.signedToTempString == "-9223372036854775808");
+ assert(long.max.signedToTempString!2 == "111111111111111111111111111111111111111111111111111111111111111");
+ assert(long.min.signedToTempString!2 == "-1000000000000000000000000000000000000000000000000000000000000000");
}
@@ -142,7 +183,7 @@ unittest
* Returns:
* number of digits
*/
-int numDigits(uint radix = 10)(ulong value) @safe
+int numDigits(uint radix = 10)(ulong value) @safe if (radix >= 2 && radix <= 36)
{
int n = 1;
while (1)
@@ -188,9 +229,14 @@ unittest
assert(1.numDigits!2 == 1);
assert(2.numDigits!2 == 2);
assert(3.numDigits!2 == 2);
+
+ // test bad radices
+ static assert(!__traits(compiles, 100.numDigits!1()));
+ static assert(!__traits(compiles, 100.numDigits!0()));
+ static assert(!__traits(compiles, 100.numDigits!37()));
}
-int dstrcmp( scope const char[] s1, scope const char[] s2 ) @trusted
+int dstrcmp()( scope const char[] s1, scope const char[] s2 ) @trusted
{
immutable len = s1.length <= s2.length ? s1.length : s2.length;
if (__ctfe)
@@ -209,5 +255,5 @@ int dstrcmp( scope const char[] s1, scope const char[] s2 ) @trusted
if ( ret )
return ret;
}
- return s1.length < s2.length ? -1 : (s1.length > s2.length);
+ return (s1.length > s2.length) - (s1.length < s2.length);
}
diff --git a/libphobos/libdruntime/core/internal/switch_.d b/libphobos/libdruntime/core/internal/switch_.d
new file mode 100644
index 00000000000..c052c584301
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/switch_.d
@@ -0,0 +1,190 @@
+/**
+This module contains compiler support for switch...case statements
+
+Copyright: Copyright Digital Mars 2000 - 2019.
+License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+Source: $(DRUNTIMESRC core/internal/_switch_.d)
+*/
+module core.internal.switch_;
+
+/**
+Support for switch statements switching on strings.
+Params:
+ caseLabels = sorted array of strings generated by compiler. Note the
+ strings are sorted by length first, and then lexicographically.
+ condition = string to look up in table
+Returns:
+ index of match in caseLabels, a negative integer if not found
+*/
+int __switch(T, caseLabels...)(/*in*/ const scope T[] condition) pure nothrow @safe @nogc
+{
+ // This closes recursion for other cases.
+ static if (caseLabels.length == 0)
+ {
+ return int.min;
+ }
+ else static if (caseLabels.length == 1)
+ {
+ return __cmp(condition, caseLabels[0]) == 0 ? 0 : int.min;
+ }
+ // To be adjusted after measurements
+ // Compile-time inlined binary search.
+ else static if (caseLabels.length < 7)
+ {
+ int r = void;
+ enum mid = cast(int)caseLabels.length / 2;
+ if (condition.length == caseLabels[mid].length)
+ {
+ r = __cmp(condition, caseLabels[mid]);
+ if (r == 0) return mid;
+ }
+ else
+ {
+ // Equivalent to (but faster than) condition.length > caseLabels[$ / 2].length ? 1 : -1
+ r = ((condition.length > caseLabels[mid].length) << 1) - 1;
+ }
+
+ if (r < 0)
+ {
+ // Search the left side
+ return __switch!(T, caseLabels[0 .. mid])(condition);
+ }
+ else
+ {
+ // Search the right side
+ return __switch!(T, caseLabels[mid + 1 .. $])(condition) + mid + 1;
+ }
+ }
+ else
+ {
+ // Need immutable array to be accessible in pure code, but case labels are
+ // currently coerced to the switch condition type (e.g. const(char)[]).
+ pure @trusted nothrow @nogc asImmutable(scope const(T[])[] items)
+ {
+ assert(__ctfe); // only @safe for CTFE
+ immutable T[][caseLabels.length] result = cast(immutable)(items[]);
+ return result;
+ }
+ static immutable T[][caseLabels.length] cases = asImmutable([caseLabels]);
+
+ // Run-time binary search in a static array of labels.
+ return __switchSearch!T(cases[], condition);
+ }
+}
+
+// binary search in sorted string cases, also see `__switch`.
+private int __switchSearch(T)(/*in*/ const scope T[][] cases, /*in*/ const scope T[] condition) pure nothrow @safe @nogc
+{
+ size_t low = 0;
+ size_t high = cases.length;
+
+ do
+ {
+ auto mid = (low + high) / 2;
+ int r = void;
+ if (condition.length == cases[mid].length)
+ {
+ r = __cmp(condition, cases[mid]);
+ if (r == 0) return cast(int) mid;
+ }
+ else
+ {
+ // Generates better code than "expr ? 1 : -1" on dmd and gdc, same with ldc
+ r = ((condition.length > cases[mid].length) << 1) - 1;
+ }
+
+ if (r > 0) low = mid + 1;
+ else high = mid;
+ }
+ while (low < high);
+
+ // Not found
+ return -1;
+}
+
+@system unittest
+{
+ static void testSwitch(T)()
+ {
+ switch (cast(T[]) "c")
+ {
+ case "coo":
+ default:
+ break;
+ }
+
+ static int bug5381(immutable(T)[] s)
+ {
+ switch (s)
+ {
+ case "unittest": return 1;
+ case "D_Version2": return 2;
+ case "nonenone": return 3;
+ case "none": return 4;
+ case "all": return 5;
+ default: return 6;
+ }
+ }
+
+ int rc = bug5381("unittest");
+ assert(rc == 1);
+
+ rc = bug5381("D_Version2");
+ assert(rc == 2);
+
+ rc = bug5381("nonenone");
+ assert(rc == 3);
+
+ rc = bug5381("none");
+ assert(rc == 4);
+
+ rc = bug5381("all");
+ assert(rc == 5);
+
+ rc = bug5381("nonerandom");
+ assert(rc == 6);
+
+ static int binarySearch(immutable(T)[] s)
+ {
+ switch (s)
+ {
+ static foreach (i; 0 .. 16)
+ case i.stringof: return i;
+ default: return -1;
+ }
+ }
+ static foreach (i; 0 .. 16)
+ assert(binarySearch(i.stringof) == i);
+ assert(binarySearch("") == -1);
+ assert(binarySearch("sth.") == -1);
+ assert(binarySearch(null) == -1);
+
+ static int bug16739(immutable(T)[] s)
+ {
+ switch (s)
+ {
+ case "\u0100": return 1;
+ case "a": return 2;
+ default: return 3;
+ }
+ }
+ assert(bug16739("\u0100") == 1);
+ assert(bug16739("a") == 2);
+ assert(bug16739("foo") == 3);
+ }
+ testSwitch!char;
+ testSwitch!wchar;
+ testSwitch!dchar;
+}
+
+/**
+Compiler lowers final switch default case to this (which is a runtime error)
+Old implementation is in core/exception.d
+*/
+void __switch_error()(string file = __FILE__, size_t line = __LINE__)
+{
+ import core.exception : __switch_errorT;
+ __switch_errorT(file, line);
+}
diff --git a/libphobos/libdruntime/core/internal/traits.d b/libphobos/libdruntime/core/internal/traits.d
index 9f79dd014b8..1856eb8b881 100644
--- a/libphobos/libdruntime/core/internal/traits.d
+++ b/libphobos/libdruntime/core/internal/traits.d
@@ -2,7 +2,7 @@
* Contains traits for runtime internal usage.
*
* Copyright: Copyright Digital Mars 2014 -.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Martin Nowak
* Source: $(DRUNTIMESRC core/internal/_traits.d)
*/
@@ -25,38 +25,59 @@ T trustedCast(T, U)(auto ref U u) @trusted pure nothrow
return cast(T)u;
}
-template Unconst(T)
+alias Unconst(T : const U, U) = U;
+
+/// taken from std.traits.Unqual
+template Unqual(T : const U, U)
{
- static if (is(T U == immutable U)) alias Unconst = U;
- else static if (is(T U == inout const U)) alias Unconst = U;
- else static if (is(T U == inout U)) alias Unconst = U;
- else static if (is(T U == const U)) alias Unconst = U;
- else alias Unconst = T;
+ static if (is(U == shared V, V))
+ alias Unqual = V;
+ else
+ alias Unqual = U;
}
-/// taken from std.traits.Unqual
-template Unqual(T)
+template BaseElemOf(T)
{
- version (none) // Error: recursive alias declaration @@@BUG1308@@@
- {
- static if (is(T U == const U)) alias Unqual = Unqual!U;
- else static if (is(T U == immutable U)) alias Unqual = Unqual!U;
- else static if (is(T U == inout U)) alias Unqual = Unqual!U;
- else static if (is(T U == shared U)) alias Unqual = Unqual!U;
- else alias Unqual = T;
- }
- else // workaround
- {
- static if (is(T U == immutable U)) alias Unqual = U;
- else static if (is(T U == shared inout const U)) alias Unqual = U;
- else static if (is(T U == shared inout U)) alias Unqual = U;
- else static if (is(T U == shared const U)) alias Unqual = U;
- else static if (is(T U == shared U)) alias Unqual = U;
- else static if (is(T U == inout const U)) alias Unqual = U;
- else static if (is(T U == inout U)) alias Unqual = U;
- else static if (is(T U == const U)) alias Unqual = U;
- else alias Unqual = T;
- }
+ static if (is(T == E[N], E, size_t N))
+ alias BaseElemOf = BaseElemOf!E;
+ else
+ alias BaseElemOf = T;
+}
+
+unittest
+{
+ static assert(is(BaseElemOf!(int) == int));
+ static assert(is(BaseElemOf!(int[1]) == int));
+ static assert(is(BaseElemOf!(int[1][2]) == int));
+ static assert(is(BaseElemOf!(int[1][]) == int[1][]));
+ static assert(is(BaseElemOf!(int[][1]) == int[]));
+}
+
+// [For internal use]
+template ModifyTypePreservingTQ(alias Modifier, T)
+{
+ static if (is(T U == immutable U)) alias ModifyTypePreservingTQ = immutable Modifier!U;
+ else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U;
+ else static if (is(T U == shared inout U)) alias ModifyTypePreservingTQ = shared inout Modifier!U;
+ else static if (is(T U == shared const U)) alias ModifyTypePreservingTQ = shared const Modifier!U;
+ else static if (is(T U == shared U)) alias ModifyTypePreservingTQ = shared Modifier!U;
+ else static if (is(T U == inout const U)) alias ModifyTypePreservingTQ = inout const Modifier!U;
+ else static if (is(T U == inout U)) alias ModifyTypePreservingTQ = inout Modifier!U;
+ else static if (is(T U == const U)) alias ModifyTypePreservingTQ = const Modifier!U;
+ else alias ModifyTypePreservingTQ = Modifier!T;
+}
+@safe unittest
+{
+ alias Intify(T) = int;
+ static assert(is(ModifyTypePreservingTQ!(Intify, real) == int));
+ static assert(is(ModifyTypePreservingTQ!(Intify, const real) == const int));
+ static assert(is(ModifyTypePreservingTQ!(Intify, inout real) == inout int));
+ static assert(is(ModifyTypePreservingTQ!(Intify, inout const real) == inout const int));
+ static assert(is(ModifyTypePreservingTQ!(Intify, shared real) == shared int));
+ static assert(is(ModifyTypePreservingTQ!(Intify, shared const real) == shared const int));
+ static assert(is(ModifyTypePreservingTQ!(Intify, shared inout real) == shared inout int));
+ static assert(is(ModifyTypePreservingTQ!(Intify, shared inout const real) == shared inout const int));
+ static assert(is(ModifyTypePreservingTQ!(Intify, immutable real) == immutable int));
}
// Substitute all `inout` qualifiers that appears in T to `const`
@@ -129,116 +150,190 @@ template staticIota(int beg, int end)
}
}
+private struct __InoutWorkaroundStruct {}
+@property T rvalueOf(T)(T val) { return val; }
+@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);
+@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);
+
+// taken from std.traits.isAssignable
+template isAssignable(Lhs, Rhs = Lhs)
+{
+ enum isAssignable = __traits(compiles, lvalueOf!Lhs = rvalueOf!Rhs) && __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs);
+}
+
+// taken from std.traits.isInnerClass
+template isInnerClass(T) if (is(T == class))
+{
+ static if (is(typeof(T.outer)))
+ {
+ template hasOuterMember(T...)
+ {
+ static if (T.length == 0)
+ enum hasOuterMember = false;
+ else
+ enum hasOuterMember = T[0] == "outer" || hasOuterMember!(T[1 .. $]);
+ }
+ enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) && !hasOuterMember!(__traits(allMembers, T));
+ }
+ else
+ enum isInnerClass = false;
+}
+
template dtorIsNothrow(T)
{
enum dtorIsNothrow = is(typeof(function{T t=void;}) : void function() nothrow);
}
-/*
-Tests whether all given items satisfy a template predicate, i.e. evaluates to
-$(D F!(T[0]) && F!(T[1]) && ... && F!(T[$ - 1])).
-*/
-package(core.internal)
-template allSatisfy(alias F, T...)
+// taken from std.meta.allSatisfy
+enum allSatisfy(alias pred, items...) =
{
- static if (T.length == 0)
+ static foreach (item; items)
+ static if (!pred!item)
+ if (__ctfe) return false;
+ return true;
+}();
+
+// taken from std.meta.anySatisfy
+enum anySatisfy(alias pred, items...) =
+{
+ static foreach (item; items)
+ static if (pred!item)
+ if (__ctfe) return true;
+ return false;
+}();
+
+// simplified from std.traits.maxAlignment
+template maxAlignment(Ts...)
+if (Ts.length > 0)
+{
+ enum maxAlignment =
{
- enum allSatisfy = true;
+ size_t result = 0;
+ static foreach (T; Ts)
+ if (T.alignof > result) result = T.alignof;
+ return result;
+ }();
+}
+
+template classInstanceAlignment(T)
+if (is(T == class))
+{
+ alias classInstanceAlignment = maxAlignment!(void*, typeof(T.tupleof));
+}
+
+/// See $(REF hasElaborateMove, std,traits)
+template hasElaborateMove(S)
+{
+ static if (__traits(isStaticArray, S))
+ {
+ enum bool hasElaborateMove = S.sizeof && hasElaborateMove!(BaseElemOf!S);
}
- else static if (T.length == 1)
+ else static if (is(S == struct))
{
- enum allSatisfy = F!(T[0]);
+ enum hasElaborateMove = (is(typeof(S.init.opPostMove(lvalueOf!S))) &&
+ !is(typeof(S.init.opPostMove(rvalueOf!S)))) ||
+ anySatisfy!(.hasElaborateMove, Fields!S);
}
else
{
- static if (allSatisfy!(F, T[0 .. $/2]))
- enum allSatisfy = allSatisfy!(F, T[$/2 .. $]);
- else
- enum allSatisfy = false;
+ enum bool hasElaborateMove = false;
}
}
-template anySatisfy(alias F, T...)
+// std.traits.hasElaborateDestructor
+template hasElaborateDestructor(S)
{
- static if (T.length == 0)
+ static if (__traits(isStaticArray, S))
{
- enum anySatisfy = false;
+ enum bool hasElaborateDestructor = S.sizeof && hasElaborateDestructor!(BaseElemOf!S);
}
- else static if (T.length == 1)
+ else static if (is(S == struct))
{
- enum anySatisfy = F!(T[0]);
+ enum hasElaborateDestructor = __traits(hasMember, S, "__dtor")
+ || anySatisfy!(.hasElaborateDestructor, Fields!S);
}
else
{
- enum anySatisfy =
- anySatisfy!(F, T[ 0 .. $/2]) ||
- anySatisfy!(F, T[$/2 .. $ ]);
+ enum bool hasElaborateDestructor = false;
}
}
-// simplified from std.traits.maxAlignment
-template maxAlignment(U...)
+// std.traits.hasElaborateCopyDestructor
+template hasElaborateCopyConstructor(S)
{
- static if (U.length == 0)
- static assert(0);
- else static if (U.length == 1)
- enum maxAlignment = U[0].alignof;
- else static if (U.length == 2)
- enum maxAlignment = U[0].alignof > U[1].alignof ? U[0].alignof : U[1].alignof;
+ static if (__traits(isStaticArray, S))
+ {
+ enum bool hasElaborateCopyConstructor = S.sizeof && hasElaborateCopyConstructor!(BaseElemOf!S);
+ }
+ else static if (is(S == struct))
+ {
+ enum hasElaborateCopyConstructor = __traits(hasCopyConstructor, S) || __traits(hasPostblit, S);
+ }
else
{
- enum a = maxAlignment!(U[0 .. ($+1)/2]);
- enum b = maxAlignment!(U[($+1)/2 .. $]);
- enum maxAlignment = a > b ? a : b;
+ enum bool hasElaborateCopyConstructor = false;
}
}
-template classInstanceAlignment(T)
-if (is(T == class))
+@safe unittest
{
- alias classInstanceAlignment = maxAlignment!(void*, typeof(T.tupleof));
-}
+ static struct S
+ {
+ int x;
+ this(return scope ref typeof(this) rhs) { }
+ this(int x, int y) {}
+ }
-// Somehow fails for non-static nested structs without support for aliases
-template hasElaborateDestructor(T...)
-{
- static if (is(T[0]))
- alias S = T[0];
- else
- alias S = typeof(T[0]);
+ static assert(hasElaborateCopyConstructor!S);
+ static assert(!hasElaborateCopyConstructor!(S[0][1]));
- static if (is(S : E[n], E, size_t n) && S.length)
+ static struct S2
{
- enum bool hasElaborateDestructor = hasElaborateDestructor!E;
+ int x;
+ this(int x, int y) {}
}
- else static if (is(S == struct))
+
+ static assert(!hasElaborateCopyConstructor!S2);
+
+ static struct S3
{
- enum hasElaborateDestructor = __traits(hasMember, S, "__dtor")
- || anySatisfy!(.hasElaborateDestructor, S.tupleof);
+ int x;
+ this(return scope ref typeof(this) rhs, int x = 42) { }
+ this(int x, int y) {}
}
- else
- enum bool hasElaborateDestructor = false;
+
+ static assert(hasElaborateCopyConstructor!S3);
}
-// Somehow fails for non-static nested structs without support for aliases
-template hasElaborateCopyConstructor(T...)
+template hasElaborateAssign(S)
{
- static if (is(T[0]))
- alias S = T[0];
- else
- alias S = typeof(T[0]);
-
- static if (is(S : E[n], E, size_t n) && S.length)
+ static if (__traits(isStaticArray, S))
{
- enum bool hasElaborateCopyConstructor = hasElaborateCopyConstructor!E;
+ enum bool hasElaborateAssign = S.sizeof && hasElaborateAssign!(BaseElemOf!S);
}
else static if (is(S == struct))
{
- enum hasElaborateCopyConstructor = __traits(hasMember, S, "__postblit")
- || anySatisfy!(.hasElaborateCopyConstructor, S.tupleof);
+ enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) ||
+ is(typeof(S.init.opAssign(lvalueOf!S))) ||
+ anySatisfy!(.hasElaborateAssign, Fields!S);
}
else
- enum bool hasElaborateCopyConstructor = false;
+ {
+ enum bool hasElaborateAssign = false;
+ }
+}
+
+template hasIndirections(T)
+{
+ static if (is(T == struct) || is(T == union))
+ enum hasIndirections = anySatisfy!(.hasIndirections, Fields!T);
+ else static if (is(T == E[N], E, size_t N))
+ enum hasIndirections = T.sizeof && is(E == void) ? true : hasIndirections!(BaseElemOf!E);
+ else static if (isFunctionPointer!T)
+ enum hasIndirections = false;
+ else
+ enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T ||
+ __traits(isAssociativeArray, T) || is (T == class) || is(T == interface);
}
template hasUnsharedIndirections(T)
@@ -389,3 +484,333 @@ template Filter(alias pred, TList...)
Filter!(pred, TList[$/2 .. $ ]));
}
}
+
+// std.meta.staticMap
+template staticMap(alias F, T...)
+{
+ static if (T.length == 0)
+ {
+ alias staticMap = AliasSeq!();
+ }
+ else static if (T.length == 1)
+ {
+ alias staticMap = AliasSeq!(F!(T[0]));
+ }
+ /* Cases 2 to 8 improve compile performance by reducing
+ * the number of recursive instantiations of staticMap
+ */
+ else static if (T.length == 2)
+ {
+ alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]));
+ }
+ else static if (T.length == 3)
+ {
+ alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]));
+ }
+ else static if (T.length == 4)
+ {
+ alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]));
+ }
+ else static if (T.length == 5)
+ {
+ alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]));
+ }
+ else static if (T.length == 6)
+ {
+ alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]), F!(T[5]));
+ }
+ else static if (T.length == 7)
+ {
+ alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]), F!(T[5]), F!(T[6]));
+ }
+ else static if (T.length == 8)
+ {
+ alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), F!(T[3]), F!(T[4]), F!(T[5]), F!(T[6]), F!(T[7]));
+ }
+ else
+ {
+ alias staticMap =
+ AliasSeq!(
+ staticMap!(F, T[ 0 .. $/2]),
+ staticMap!(F, T[$/2 .. $ ]));
+ }
+}
+
+// std.exception.assertCTFEable
+version (CoreUnittest) package(core)
+void assertCTFEable(alias dg)()
+{
+ static assert({ cast(void) dg(); return true; }());
+ cast(void) dg();
+}
+
+// std.traits.FunctionTypeOf
+/*
+Get the function type from a callable object `func`.
+
+Using builtin `typeof` on a property function yields the types of the
+property value, not of the property function itself. Still,
+`FunctionTypeOf` is able to obtain function types of properties.
+
+Note:
+Do not confuse function types with function pointer types; function types are
+usually used for compile-time reflection purposes.
+ */
+template FunctionTypeOf(func...)
+if (func.length == 1 /*&& isCallable!func*/)
+{
+ static if (is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function) || is(typeof(& func[0]) Fsym == delegate))
+ {
+ alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol
+ }
+ else static if (is(typeof(& func[0].opCall) Fobj == delegate))
+ {
+ alias FunctionTypeOf = Fobj; // HIT: callable object
+ }
+ else static if (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function))
+ {
+ alias FunctionTypeOf = Ftyp; // HIT: callable type
+ }
+ else static if (is(func[0] T) || is(typeof(func[0]) T))
+ {
+ static if (is(T == function))
+ alias FunctionTypeOf = T; // HIT: function
+ else static if (is(T Fptr : Fptr*) && is(Fptr == function))
+ alias FunctionTypeOf = Fptr; // HIT: function pointer
+ else static if (is(T Fdlg == delegate))
+ alias FunctionTypeOf = Fdlg; // HIT: delegate
+ else
+ static assert(0);
+ }
+ else
+ static assert(0);
+}
+
+@safe unittest
+{
+ class C
+ {
+ int value() @property { return 0; }
+ }
+ static assert(is( typeof(C.value) == int ));
+ static assert(is( FunctionTypeOf!(C.value) == function ));
+}
+
+@system unittest
+{
+ int test(int a);
+ int propGet() @property;
+ int propSet(int a) @property;
+ int function(int) test_fp;
+ int delegate(int) test_dg;
+ static assert(is( typeof(test) == FunctionTypeOf!(typeof(test)) ));
+ static assert(is( typeof(test) == FunctionTypeOf!test ));
+ static assert(is( typeof(test) == FunctionTypeOf!test_fp ));
+ static assert(is( typeof(test) == FunctionTypeOf!test_dg ));
+ alias int GetterType() @property;
+ alias int SetterType(int) @property;
+ static assert(is( FunctionTypeOf!propGet == GetterType ));
+ static assert(is( FunctionTypeOf!propSet == SetterType ));
+
+ interface Prop { int prop() @property; }
+ Prop prop;
+ static assert(is( FunctionTypeOf!(Prop.prop) == GetterType ));
+ static assert(is( FunctionTypeOf!(prop.prop) == GetterType ));
+
+ class Callable { int opCall(int) { return 0; } }
+ auto call = new Callable;
+ static assert(is( FunctionTypeOf!call == typeof(test) ));
+
+ struct StaticCallable { static int opCall(int) { return 0; } }
+ StaticCallable stcall_val;
+ StaticCallable* stcall_ptr;
+ static assert(is( FunctionTypeOf!stcall_val == typeof(test) ));
+ static assert(is( FunctionTypeOf!stcall_ptr == typeof(test) ));
+
+ interface Overloads
+ {
+ void test(string);
+ real test(real);
+ int test(int);
+ int test() @property;
+ }
+ alias ov = __traits(getVirtualFunctions, Overloads, "test");
+ alias F_ov0 = FunctionTypeOf!(ov[0]);
+ alias F_ov1 = FunctionTypeOf!(ov[1]);
+ alias F_ov2 = FunctionTypeOf!(ov[2]);
+ alias F_ov3 = FunctionTypeOf!(ov[3]);
+ static assert(is(F_ov0* == void function(string)));
+ static assert(is(F_ov1* == real function(real)));
+ static assert(is(F_ov2* == int function(int)));
+ static assert(is(F_ov3* == int function() @property));
+
+ alias F_dglit = FunctionTypeOf!((int a){ return a; });
+ static assert(is(F_dglit* : int function(int)));
+}
+
+// std.traits.ReturnType
+/*
+Get the type of the return value from a function,
+a pointer to function, a delegate, a struct
+with an opCall, a pointer to a struct with an opCall,
+or a class with an `opCall`. Please note that $(D_KEYWORD ref)
+is not part of a type, but the attribute of the function
+(see template $(LREF functionAttributes)).
+*/
+template ReturnType(func...)
+if (func.length == 1 /*&& isCallable!func*/)
+{
+ static if (is(FunctionTypeOf!func R == return))
+ alias ReturnType = R;
+ else
+ static assert(0, "argument has no return type");
+}
+
+//
+@safe unittest
+{
+ int foo();
+ ReturnType!foo x; // x is declared as int
+}
+
+@safe unittest
+{
+ struct G
+ {
+ int opCall (int i) { return 1;}
+ }
+
+ alias ShouldBeInt = ReturnType!G;
+ static assert(is(ShouldBeInt == int));
+
+ G g;
+ static assert(is(ReturnType!g == int));
+
+ G* p;
+ alias pg = ReturnType!p;
+ static assert(is(pg == int));
+
+ class C
+ {
+ int opCall (int i) { return 1;}
+ }
+
+ static assert(is(ReturnType!C == int));
+
+ C c;
+ static assert(is(ReturnType!c == int));
+
+ class Test
+ {
+ int prop() @property { return 0; }
+ }
+ alias R_Test_prop = ReturnType!(Test.prop);
+ static assert(is(R_Test_prop == int));
+
+ alias R_dglit = ReturnType!((int a) { return a; });
+ static assert(is(R_dglit == int));
+}
+
+// std.traits.Parameters
+/*
+Get, as a tuple, the types of the parameters to a function, a pointer
+to function, a delegate, a struct with an `opCall`, a pointer to a
+struct with an `opCall`, or a class with an `opCall`.
+*/
+template Parameters(func...)
+if (func.length == 1 /*&& isCallable!func*/)
+{
+ static if (is(FunctionTypeOf!func P == function))
+ alias Parameters = P;
+ else
+ static assert(0, "argument has no parameters");
+}
+
+//
+@safe unittest
+{
+ int foo(int, long);
+ void bar(Parameters!foo); // declares void bar(int, long);
+ void abc(Parameters!foo[1]); // declares void abc(long);
+}
+
+@safe unittest
+{
+ int foo(int i, bool b) { return 0; }
+ static assert(is(Parameters!foo == AliasSeq!(int, bool)));
+ static assert(is(Parameters!(typeof(&foo)) == AliasSeq!(int, bool)));
+
+ struct S { real opCall(real r, int i) { return 0.0; } }
+ S s;
+ static assert(is(Parameters!S == AliasSeq!(real, int)));
+ static assert(is(Parameters!(S*) == AliasSeq!(real, int)));
+ static assert(is(Parameters!s == AliasSeq!(real, int)));
+
+ class Test
+ {
+ int prop() @property { return 0; }
+ }
+ alias P_Test_prop = Parameters!(Test.prop);
+ static assert(P_Test_prop.length == 0);
+
+ alias P_dglit = Parameters!((int a){});
+ static assert(P_dglit.length == 1);
+ static assert(is(P_dglit[0] == int));
+}
+
+// Return `true` if `Type` has `member` that evaluates to `true` in a static if condition
+enum isTrue(Type, string member) = __traits(compiles, { static if (__traits(getMember, Type, member)) {} else static assert(0); });
+
+unittest
+{
+ static struct T
+ {
+ enum a = true;
+ enum b = false;
+ enum c = 1;
+ enum d = 45;
+ enum e = "true";
+ enum f = "";
+ enum g = null;
+ alias h = bool;
+ }
+
+ static assert( isTrue!(T, "a"));
+ static assert(!isTrue!(T, "b"));
+ static assert( isTrue!(T, "c"));
+ static assert( isTrue!(T, "d"));
+ static assert( isTrue!(T, "e"));
+ static assert( isTrue!(T, "f"));
+ static assert(!isTrue!(T, "g"));
+ static assert(!isTrue!(T, "h"));
+}
+
+template hasUDA(alias symbol, alias attribute)
+{
+ alias attrs = __traits(getAttributes, symbol);
+
+ static foreach (a; attrs)
+ {
+ static if (is(a == attribute))
+ {
+ enum hasUDA = true;
+ }
+ }
+
+ static if (!__traits(compiles, (hasUDA == true)))
+ enum hasUDA = false;
+}
+
+unittest
+{
+ struct SomeUDA{}
+
+ struct Test
+ {
+ int woUDA;
+ @SomeUDA int withUDA;
+ }
+
+ static assert(hasUDA!(Test.withUDA, SomeUDA));
+ static assert(!hasUDA!(Test.woUDA, SomeUDA));
+}
diff --git a/libphobos/libdruntime/core/internal/utf.d b/libphobos/libdruntime/core/internal/utf.d
new file mode 100644
index 00000000000..ca0f7f599a6
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/utf.d
@@ -0,0 +1,938 @@
+/********************************************
+ * Encode and decode UTF-8, UTF-16 and UTF-32 strings.
+ *
+ * For Win32 systems, the C wchar_t type is UTF-16 and corresponds to the D
+ * wchar type.
+ * For Posix systems, the C wchar_t type is UTF-32 and corresponds to
+ * the D utf.dchar type.
+ *
+ * UTF character support is restricted to (\u0000 &lt;= character &lt;= \U0010FFFF).
+ *
+ * See_Also:
+ * $(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)<br>
+ * $(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)<br>
+ * $(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335)
+ *
+ * Copyright: Copyright Digital Mars 2003 - 2016.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright, Sean Kelly
+ * Source: $(DRUNTIMESRC core/internal/_utf.d)
+ */
+
+module core.internal.utf;
+
+extern (C) void onUnicodeError( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__ ) @safe pure;
+
+/*******************************
+ * Test if c is a valid UTF-32 character.
+ *
+ * \uFFFE and \uFFFF are considered valid by this function,
+ * as they are permitted for internal use by an application,
+ * but they are not allowed for interchange by the Unicode standard.
+ *
+ * Returns: true if it is, false if not.
+ */
+
+@safe @nogc pure nothrow
+bool isValidDchar(dchar c)
+{
+ /* Note: FFFE and FFFF are specifically permitted by the
+ * Unicode standard for application internal use, but are not
+ * allowed for interchange.
+ * (thanks to Arcane Jill)
+ */
+
+ return c < 0xD800 ||
+ (c > 0xDFFF && c <= 0x10FFFF /*&& c != 0xFFFE && c != 0xFFFF*/);
+}
+
+unittest
+{
+ debug(utf) printf("utf.isValidDchar.unittest\n");
+ assert(isValidDchar(cast(dchar)'a') == true);
+ assert(isValidDchar(cast(dchar)0x1FFFFF) == false);
+}
+
+
+
+static immutable UTF8stride =
+[
+ cast(ubyte)
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0xFF,0xFF,
+];
+
+/**
+ * stride() returns the length of a UTF-8 sequence starting at index i
+ * in string s.
+ * Returns:
+ * The number of bytes in the UTF-8 sequence or
+ * 0xFF meaning s[i] is not the start of of UTF-8 sequence.
+ */
+@safe @nogc pure nothrow
+uint stride(const scope char[] s, size_t i)
+{
+ return UTF8stride[s[i]];
+}
+
+/**
+ * stride() returns the length of a UTF-16 sequence starting at index i
+ * in string s.
+ */
+@safe @nogc pure nothrow
+uint stride(const scope wchar[] s, size_t i)
+{ uint u = s[i];
+ return 1 + (u >= 0xD800 && u <= 0xDBFF);
+}
+
+/**
+ * stride() returns the length of a UTF-32 sequence starting at index i
+ * in string s.
+ * Returns: The return value will always be 1.
+ */
+@safe @nogc pure nothrow
+uint stride(const scope dchar[] s, size_t i)
+{
+ return 1;
+}
+
+/*******************************************
+ * Given an index i into an array of characters s[],
+ * and assuming that index i is at the start of a UTF character,
+ * determine the number of UCS characters up to that index i.
+ */
+@safe pure
+size_t toUCSindex(const scope char[] s, size_t i)
+{
+ size_t n;
+ size_t j;
+
+ for (j = 0; j < i; )
+ {
+ j += stride(s, j);
+ n++;
+ }
+ if (j > i)
+ {
+ onUnicodeError("invalid UTF-8 sequence", j);
+ }
+ return n;
+}
+
+/** ditto */
+@safe pure
+size_t toUCSindex(const scope wchar[] s, size_t i)
+{
+ size_t n;
+ size_t j;
+
+ for (j = 0; j < i; )
+ {
+ j += stride(s, j);
+ n++;
+ }
+ if (j > i)
+ {
+ onUnicodeError("invalid UTF-16 sequence", j);
+ }
+ return n;
+}
+
+/** ditto */
+@safe @nogc pure nothrow
+size_t toUCSindex(const scope dchar[] s, size_t i)
+{
+ return i;
+}
+
+/******************************************
+ * Given a UCS index n into an array of characters s[], return the UTF index.
+ */
+@safe pure
+size_t toUTFindex(const scope char[] s, size_t n)
+{
+ size_t i;
+
+ while (n--)
+ {
+ uint j = UTF8stride[s[i]];
+ if (j == 0xFF)
+ onUnicodeError("invalid UTF-8 sequence", i);
+ i += j;
+ }
+ return i;
+}
+
+/** ditto */
+@safe @nogc pure nothrow
+size_t toUTFindex(const scope wchar[] s, size_t n)
+{
+ size_t i;
+
+ while (n--)
+ { wchar u = s[i];
+
+ i += 1 + (u >= 0xD800 && u <= 0xDBFF);
+ }
+ return i;
+}
+
+/** ditto */
+@safe @nogc pure nothrow
+size_t toUTFindex(const scope dchar[] s, size_t n)
+{
+ return n;
+}
+
+/* =================== Decode ======================= */
+
+/***************
+ * Decodes and returns character starting at s[idx]. idx is advanced past the
+ * decoded character. If the character is not well formed, a UtfException is
+ * thrown and idx remains unchanged.
+ */
+@safe pure
+dchar decode(const scope char[] s, ref size_t idx)
+ in
+ {
+ assert(idx >= 0 && idx < s.length);
+ }
+ out (result)
+ {
+ assert(isValidDchar(result));
+ }
+ do
+ {
+ size_t len = s.length;
+ dchar V;
+ size_t i = idx;
+ char u = s[i];
+
+ if (u & 0x80)
+ { uint n;
+ char u2;
+
+ /* The following encodings are valid, except for the 5 and 6 byte
+ * combinations:
+ * 0xxxxxxx
+ * 110xxxxx 10xxxxxx
+ * 1110xxxx 10xxxxxx 10xxxxxx
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ */
+ for (n = 1; ; n++)
+ {
+ if (n > 4)
+ goto Lerr; // only do the first 4 of 6 encodings
+ if (((u << n) & 0x80) == 0)
+ {
+ if (n == 1)
+ goto Lerr;
+ break;
+ }
+ }
+
+ // Pick off (7 - n) significant bits of B from first byte of octet
+ V = cast(dchar)(u & ((1 << (7 - n)) - 1));
+
+ if (i + (n - 1) >= len)
+ goto Lerr; // off end of string
+
+ /* The following combinations are overlong, and illegal:
+ * 1100000x (10xxxxxx)
+ * 11100000 100xxxxx (10xxxxxx)
+ * 11110000 1000xxxx (10xxxxxx 10xxxxxx)
+ * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx)
+ * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)
+ */
+ u2 = s[i + 1];
+ if ((u & 0xFE) == 0xC0 ||
+ (u == 0xE0 && (u2 & 0xE0) == 0x80) ||
+ (u == 0xF0 && (u2 & 0xF0) == 0x80) ||
+ (u == 0xF8 && (u2 & 0xF8) == 0x80) ||
+ (u == 0xFC && (u2 & 0xFC) == 0x80))
+ goto Lerr; // overlong combination
+
+ for (uint j = 1; j != n; j++)
+ {
+ u = s[i + j];
+ if ((u & 0xC0) != 0x80)
+ goto Lerr; // trailing bytes are 10xxxxxx
+ V = (V << 6) | (u & 0x3F);
+ }
+ if (!isValidDchar(V))
+ goto Lerr;
+ i += n;
+ }
+ else
+ {
+ V = cast(dchar) u;
+ i++;
+ }
+
+ idx = i;
+ return V;
+
+ Lerr:
+ onUnicodeError("invalid UTF-8 sequence", i);
+ return V; // dummy return
+ }
+
+unittest
+{ size_t i;
+ dchar c;
+
+ debug(utf) printf("utf.decode.unittest\n");
+
+ static s1 = "abcd"c;
+ i = 0;
+ c = decode(s1, i);
+ assert(c == cast(dchar)'a');
+ assert(i == 1);
+ c = decode(s1, i);
+ assert(c == cast(dchar)'b');
+ assert(i == 2);
+
+ static s2 = "\xC2\xA9"c;
+ i = 0;
+ c = decode(s2, i);
+ assert(c == cast(dchar)'\u00A9');
+ assert(i == 2);
+
+ static s3 = "\xE2\x89\xA0"c;
+ i = 0;
+ c = decode(s3, i);
+ assert(c == cast(dchar)'\u2260');
+ assert(i == 3);
+
+ static s4 =
+ [ "\xE2\x89"c[], // too short
+ "\xC0\x8A",
+ "\xE0\x80\x8A",
+ "\xF0\x80\x80\x8A",
+ "\xF8\x80\x80\x80\x8A",
+ "\xFC\x80\x80\x80\x80\x8A",
+ ];
+
+ for (int j = 0; j < s4.length; j++)
+ {
+ try
+ {
+ i = 0;
+ c = decode(s4[j], i);
+ assert(0);
+ }
+ catch (Throwable o)
+ {
+ i = 23;
+ }
+ assert(i == 23);
+ }
+}
+
+/** ditto */
+@safe pure
+dchar decode(const scope wchar[] s, ref size_t idx)
+ in
+ {
+ assert(idx >= 0 && idx < s.length);
+ }
+ out (result)
+ {
+ assert(isValidDchar(result));
+ }
+ do
+ {
+ string msg;
+ dchar V;
+ size_t i = idx;
+ uint u = s[i];
+
+ if (u & ~0x7F)
+ { if (u >= 0xD800 && u <= 0xDBFF)
+ { uint u2;
+
+ if (i + 1 == s.length)
+ { msg = "surrogate UTF-16 high value past end of string";
+ goto Lerr;
+ }
+ u2 = s[i + 1];
+ if (u2 < 0xDC00 || u2 > 0xDFFF)
+ { msg = "surrogate UTF-16 low value out of range";
+ goto Lerr;
+ }
+ u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00);
+ i += 2;
+ }
+ else if (u >= 0xDC00 && u <= 0xDFFF)
+ { msg = "unpaired surrogate UTF-16 value";
+ goto Lerr;
+ }
+ else if (u == 0xFFFE || u == 0xFFFF)
+ { msg = "illegal UTF-16 value";
+ goto Lerr;
+ }
+ else
+ i++;
+ }
+ else
+ {
+ i++;
+ }
+
+ idx = i;
+ return cast(dchar)u;
+
+ Lerr:
+ onUnicodeError(msg, i);
+ return cast(dchar)u; // dummy return
+ }
+
+/** ditto */
+@safe pure
+dchar decode(const scope dchar[] s, ref size_t idx)
+ in
+ {
+ assert(idx >= 0 && idx < s.length);
+ }
+ do
+ {
+ size_t i = idx;
+ dchar c = s[i];
+
+ if (!isValidDchar(c))
+ goto Lerr;
+ idx = i + 1;
+ return c;
+
+ Lerr:
+ onUnicodeError("invalid UTF-32 value", i);
+ return c; // dummy return
+ }
+
+
+/* =================== Encode ======================= */
+
+/*******************************
+ * Encodes character c and appends it to array s[].
+ */
+@safe pure nothrow
+void encode(ref char[] s, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ char[] r = s;
+
+ if (c <= 0x7F)
+ {
+ r ~= cast(char) c;
+ }
+ else
+ {
+ char[4] buf = void;
+ uint L;
+
+ if (c <= 0x7FF)
+ {
+ buf[0] = cast(char)(0xC0 | (c >> 6));
+ buf[1] = cast(char)(0x80 | (c & 0x3F));
+ L = 2;
+ }
+ else if (c <= 0xFFFF)
+ {
+ buf[0] = cast(char)(0xE0 | (c >> 12));
+ buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[2] = cast(char)(0x80 | (c & 0x3F));
+ L = 3;
+ }
+ else if (c <= 0x10FFFF)
+ {
+ buf[0] = cast(char)(0xF0 | (c >> 18));
+ buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
+ buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[3] = cast(char)(0x80 | (c & 0x3F));
+ L = 4;
+ }
+ else
+ {
+ assert(0);
+ }
+ r ~= buf[0 .. L];
+ }
+ s = r;
+ }
+
+unittest
+{
+ debug(utf) printf("utf.encode.unittest\n");
+
+ char[] s = "abcd".dup;
+ encode(s, cast(dchar)'a');
+ assert(s.length == 5);
+ assert(s == "abcda");
+
+ encode(s, cast(dchar)'\u00A9');
+ assert(s.length == 7);
+ assert(s == "abcda\xC2\xA9");
+ //assert(s == "abcda\u00A9"); // BUG: fix compiler
+
+ encode(s, cast(dchar)'\u2260');
+ assert(s.length == 10);
+ assert(s == "abcda\xC2\xA9\xE2\x89\xA0");
+}
+
+/** ditto */
+@safe pure nothrow
+void encode(ref wchar[] s, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ wchar[] r = s;
+
+ if (c <= 0xFFFF)
+ {
+ r ~= cast(wchar) c;
+ }
+ else
+ {
+ wchar[2] buf = void;
+
+ buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
+ r ~= buf;
+ }
+ s = r;
+ }
+
+/** ditto */
+@safe pure nothrow
+void encode(ref dchar[] s, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ s ~= c;
+ }
+
+/**
+Returns the code length of $(D c) in the encoding using $(D C) as a
+code point. The code is returned in character count, not in bytes.
+ */
+@safe pure nothrow @nogc
+ubyte codeLength(C)(dchar c)
+{
+ static if (C.sizeof == 1)
+ {
+ if (c <= 0x7F) return 1;
+ if (c <= 0x7FF) return 2;
+ if (c <= 0xFFFF) return 3;
+ if (c <= 0x10FFFF) return 4;
+ assert(false);
+ }
+ else static if (C.sizeof == 2)
+ {
+ return c <= 0xFFFF ? 1 : 2;
+ }
+ else
+ {
+ static assert(C.sizeof == 4);
+ return 1;
+ }
+}
+
+/* =================== Validation ======================= */
+
+/***********************************
+Checks to see if string is well formed or not. $(D S) can be an array
+ of $(D char), $(D wchar), or $(D dchar). Throws a $(D UtfException)
+ if it is not. Use to check all untrusted input for correctness.
+ */
+@safe pure
+void validate(S)(const scope S s)
+{
+ auto len = s.length;
+ for (size_t i = 0; i < len; )
+ {
+ decode(s, i);
+ }
+}
+
+/* =================== Conversion to UTF8 ======================= */
+
+@safe pure nothrow @nogc
+char[] toUTF8(return char[] buf, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ if (c <= 0x7F)
+ {
+ buf[0] = cast(char) c;
+ return buf[0 .. 1];
+ }
+ else if (c <= 0x7FF)
+ {
+ buf[0] = cast(char)(0xC0 | (c >> 6));
+ buf[1] = cast(char)(0x80 | (c & 0x3F));
+ return buf[0 .. 2];
+ }
+ else if (c <= 0xFFFF)
+ {
+ buf[0] = cast(char)(0xE0 | (c >> 12));
+ buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[2] = cast(char)(0x80 | (c & 0x3F));
+ return buf[0 .. 3];
+ }
+ else if (c <= 0x10FFFF)
+ {
+ buf[0] = cast(char)(0xF0 | (c >> 18));
+ buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
+ buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[3] = cast(char)(0x80 | (c & 0x3F));
+ return buf[0 .. 4];
+ }
+ assert(0);
+ }
+
+/*******************
+ * Encodes string s into UTF-8 and returns the encoded string.
+ */
+@safe pure nothrow
+string toUTF8(return string s)
+ in
+ {
+ validate(s);
+ }
+ do
+ {
+ return s;
+ }
+
+/** ditto */
+@trusted pure
+string toUTF8(const scope wchar[] s)
+{
+ char[] r;
+ size_t i;
+ size_t slen = s.length;
+
+ r.length = slen;
+
+ for (i = 0; i < slen; i++)
+ { wchar c = s[i];
+
+ if (c <= 0x7F)
+ r[i] = cast(char)c; // fast path for ascii
+ else
+ {
+ r.length = i;
+ foreach (dchar ch; s[i .. slen])
+ {
+ encode(r, ch);
+ }
+ break;
+ }
+ }
+ return cast(string)r;
+}
+
+/** ditto */
+@trusted pure
+string toUTF8(const scope dchar[] s)
+{
+ char[] r;
+ size_t i;
+ size_t slen = s.length;
+
+ r.length = slen;
+
+ for (i = 0; i < slen; i++)
+ { dchar c = s[i];
+
+ if (c <= 0x7F)
+ r[i] = cast(char)c; // fast path for ascii
+ else
+ {
+ r.length = i;
+ foreach (dchar d; s[i .. slen])
+ {
+ encode(r, d);
+ }
+ break;
+ }
+ }
+ return cast(string)r;
+}
+
+/* =================== Conversion to UTF16 ======================= */
+
+@safe pure nothrow @nogc
+wchar[] toUTF16(return wchar[] buf, dchar c)
+ in
+ {
+ assert(isValidDchar(c));
+ }
+ do
+ {
+ if (c <= 0xFFFF)
+ {
+ buf[0] = cast(wchar) c;
+ return buf[0 .. 1];
+ }
+ else
+ {
+ buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
+ return buf[0 .. 2];
+ }
+ }
+
+/****************
+ * Encodes string s into UTF-16 and returns the encoded string.
+ * toUTF16z() is suitable for calling the 'W' functions in the Win32 API that take
+ * an LPWSTR or LPCWSTR argument.
+ */
+@trusted pure
+wstring toUTF16(const scope char[] s)
+{
+ wchar[] r;
+ size_t slen = s.length;
+
+ if (!__ctfe)
+ {
+ // Reserve still does a lot if slen is zero.
+ // Return early for that case.
+ if (0 == slen)
+ return ""w;
+ r.reserve(slen);
+ }
+ for (size_t i = 0; i < slen; )
+ {
+ dchar c = s[i];
+ if (c <= 0x7F)
+ {
+ i++;
+ r ~= cast(wchar)c;
+ }
+ else
+ {
+ c = decode(s, i);
+ encode(r, c);
+ }
+ }
+ return cast(wstring)r;
+}
+
+alias const(wchar)* wptr;
+/** ditto */
+@safe pure
+wptr toUTF16z(const scope char[] s)
+{
+ wchar[] r;
+ size_t slen = s.length;
+
+ if (!__ctfe)
+ {
+ // Reserve still does a lot if slen is zero.
+ // Return early for that case.
+ if (0 == slen)
+ return &"\0"w[0];
+ r.reserve(slen + 1);
+ }
+ for (size_t i = 0; i < slen; )
+ {
+ dchar c = s[i];
+ if (c <= 0x7F)
+ {
+ i++;
+ r ~= cast(wchar)c;
+ }
+ else
+ {
+ c = decode(s, i);
+ encode(r, c);
+ }
+ }
+ r ~= '\000';
+ return &r[0];
+}
+
+/** ditto */
+@safe pure nothrow
+wstring toUTF16(return wstring s)
+ in
+ {
+ validate(s);
+ }
+ do
+ {
+ return s;
+ }
+
+/** ditto */
+@trusted pure nothrow
+wstring toUTF16(const scope dchar[] s)
+{
+ wchar[] r;
+ size_t slen = s.length;
+
+ if (!__ctfe)
+ {
+ // Reserve still does a lot if slen is zero.
+ // Return early for that case.
+ if (0 == slen)
+ return ""w;
+ r.reserve(slen);
+ }
+ for (size_t i = 0; i < slen; i++)
+ {
+ encode(r, s[i]);
+ }
+ return cast(wstring)r;
+}
+
+/* =================== Conversion to UTF32 ======================= */
+
+/*****
+ * Encodes string s into UTF-32 and returns the encoded string.
+ */
+@trusted pure
+dstring toUTF32(const scope char[] s)
+{
+ dchar[] r;
+ size_t slen = s.length;
+ size_t j = 0;
+
+ r.length = slen; // r[] will never be longer than s[]
+ for (size_t i = 0; i < slen; )
+ {
+ dchar c = s[i];
+ if (c >= 0x80)
+ c = decode(s, i);
+ else
+ i++; // c is ascii, no need for decode
+ r[j++] = c;
+ }
+ return cast(dstring)r[0 .. j];
+}
+
+/** ditto */
+@trusted pure
+dstring toUTF32(const scope wchar[] s)
+{
+ dchar[] r;
+ size_t slen = s.length;
+ size_t j = 0;
+
+ r.length = slen; // r[] will never be longer than s[]
+ for (size_t i = 0; i < slen; )
+ {
+ dchar c = s[i];
+ if (c >= 0x80)
+ c = decode(s, i);
+ else
+ i++; // c is ascii, no need for decode
+ r[j++] = c;
+ }
+ return cast(dstring)r[0 .. j];
+}
+
+/** ditto */
+@safe pure nothrow
+dstring toUTF32(return dstring s)
+ in
+ {
+ validate(s);
+ }
+ do
+ {
+ return s;
+ }
+
+/* ================================ tests ================================== */
+
+unittest
+{
+ debug(utf) printf("utf.toUTF.unittest\n");
+
+ auto c = "hello"c[];
+ auto w = toUTF16(c);
+ assert(w == "hello");
+ auto d = toUTF32(c);
+ assert(d == "hello");
+
+ c = toUTF8(w);
+ assert(c == "hello");
+ d = toUTF32(w);
+ assert(d == "hello");
+
+ c = toUTF8(d);
+ assert(c == "hello");
+ w = toUTF16(d);
+ assert(w == "hello");
+
+
+ c = "hel\u1234o";
+ w = toUTF16(c);
+ assert(w == "hel\u1234o");
+ d = toUTF32(c);
+ assert(d == "hel\u1234o");
+
+ c = toUTF8(w);
+ assert(c == "hel\u1234o");
+ d = toUTF32(w);
+ assert(d == "hel\u1234o");
+
+ c = toUTF8(d);
+ assert(c == "hel\u1234o");
+ w = toUTF16(d);
+ assert(w == "hel\u1234o");
+
+
+ c = "he\U000BAAAAllo";
+ w = toUTF16(c);
+ //foreach (wchar c; w) printf("c = x%x\n", c);
+ //foreach (wchar c; cast(wstring)"he\U000BAAAAllo") printf("c = x%x\n", c);
+ assert(w == "he\U000BAAAAllo");
+ d = toUTF32(c);
+ assert(d == "he\U000BAAAAllo");
+
+ c = toUTF8(w);
+ assert(c == "he\U000BAAAAllo");
+ d = toUTF32(w);
+ assert(d == "he\U000BAAAAllo");
+
+ c = toUTF8(d);
+ assert(c == "he\U000BAAAAllo");
+ w = toUTF16(d);
+ assert(w == "he\U000BAAAAllo");
+
+ wchar[2] buf;
+ auto ret = toUTF16(buf, '\U000BAAAA');
+ assert(ret == "\U000BAAAA");
+}
diff --git a/libphobos/libdruntime/core/internal/util/array.d b/libphobos/libdruntime/core/internal/util/array.d
new file mode 100644
index 00000000000..bc9b72c1474
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/util/array.d
@@ -0,0 +1,72 @@
+/**
+ * Array utilities.
+ *
+ * Copyright: Denis Shelomovskij 2013
+ * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Denis Shelomovskij
+ * Source: $(DRUNTIMESRC core/internal/util/_array.d)
+ */
+module core.internal.util.array;
+
+
+import core.internal.string;
+import core.stdc.stdint;
+
+
+@safe /* pure dmd @@@BUG11461@@@ */ nothrow:
+
+void enforceTypedArraysConformable(T)(const char[] action,
+ const T[] a1, const T[] a2, const bool allowOverlap = false)
+{
+ _enforceSameLength(action, a1.length, a2.length);
+ if (!allowOverlap)
+ _enforceNoOverlap(action, arrayToPtr(a1), arrayToPtr(a2), T.sizeof * a1.length);
+}
+
+void enforceRawArraysConformable(const char[] action, const size_t elementSize,
+ const void[] a1, const void[] a2, const bool allowOverlap = false)
+{
+ _enforceSameLength(action, a1.length, a2.length);
+ if (!allowOverlap)
+ _enforceNoOverlap(action, arrayToPtr(a1), arrayToPtr(a2), elementSize * a1.length);
+}
+
+private void _enforceSameLength(const char[] action,
+ const size_t length1, const size_t length2)
+{
+ if (length1 == length2)
+ return;
+
+ UnsignedStringBuf tmpBuff = void;
+ string msg = "Array lengths don't match for ";
+ msg ~= action;
+ msg ~= ": ";
+ msg ~= length1.unsignedToTempString(tmpBuff);
+ msg ~= " != ";
+ msg ~= length2.unsignedToTempString(tmpBuff);
+ assert(0, msg);
+}
+
+private void _enforceNoOverlap(const char[] action,
+ uintptr_t ptr1, uintptr_t ptr2, const size_t bytes)
+{
+ const d = ptr1 > ptr2 ? ptr1 - ptr2 : ptr2 - ptr1;
+ if (d >= bytes)
+ return;
+ const overlappedBytes = bytes - d;
+
+ UnsignedStringBuf tmpBuff = void;
+ string msg = "Overlapping arrays in ";
+ msg ~= action;
+ msg ~= ": ";
+ msg ~= overlappedBytes.unsignedToTempString(tmpBuff);
+ msg ~= " byte(s) overlap of ";
+ msg ~= bytes.unsignedToTempString(tmpBuff);
+ assert(0, msg);
+}
+
+private uintptr_t arrayToPtr(const void[] array) @trusted
+{
+ // Ok because the user will never dereference the pointer
+ return cast(uintptr_t)array.ptr;
+}
diff --git a/libphobos/libdruntime/core/internal/util/math.d b/libphobos/libdruntime/core/internal/util/math.d
new file mode 100644
index 00000000000..416e3703deb
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/util/math.d
@@ -0,0 +1,53 @@
+// Written in the D programming language
+
+/**
+ * Internal math utilities.
+ *
+ * Copyright: The D Language Foundation 2021.
+ * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Luís Ferreira
+ * Source: $(DRUNTIMESRC core/internal/util/_math.d)
+ */
+module core.internal.util.math;
+
+/**
+ * Calculates the maximum of the passed arguments
+ * Params:
+ * a = first value to select the maximum from
+ * b = second value to select the maximum from
+ * Returns: The maximum of the passed-in values.
+ */
+T max(T)(T a, T b) pure nothrow @nogc @safe
+{
+ return b > a ? b : a;
+}
+
+/**
+ * Calculates the minimum of the passed arguments
+ * Params:
+ * a = first value to select the minimum from
+ * b = second value to select the minimum from
+ * Returns: The minimum of the passed-in values.
+ */
+T min(T)(T a, T b) pure nothrow @nogc @safe
+{
+ return b < a ? b : a;
+}
+
+///
+@safe pure @nogc nothrow
+unittest
+{
+ assert(max(1,3) == 3);
+ assert(max(3,1) == 3);
+ assert(max(1,1) == 1);
+}
+
+///
+@safe pure @nogc nothrow
+unittest
+{
+ assert(min(1,3) == 1);
+ assert(min(3,1) == 1);
+ assert(min(1,1) == 1);
+}
diff --git a/libphobos/libdruntime/core/lifetime.d b/libphobos/libdruntime/core/lifetime.d
new file mode 100644
index 00000000000..fc47b1d9394
--- /dev/null
+++ b/libphobos/libdruntime/core/lifetime.d
@@ -0,0 +1,2201 @@
+module core.lifetime;
+
+import core.internal.attributes : betterC;
+
+// emplace
+/**
+Given a pointer `chunk` to uninitialized memory (but already typed
+as `T`), constructs an object of non-`class` type `T` at that
+address. If `T` is a class, initializes the class reference to null.
+Returns: A pointer to the newly constructed object (which is the same
+as `chunk`).
+ */
+T* emplace(T)(T* chunk) @safe pure nothrow
+{
+ import core.internal.lifetime : emplaceRef;
+
+ emplaceRef!T(*chunk);
+ return chunk;
+}
+
+///
+@betterC
+@system unittest
+{
+ static struct S
+ {
+ int i = 42;
+ }
+ S[2] s2 = void;
+ emplace(&s2);
+ assert(s2[0].i == 42 && s2[1].i == 42);
+}
+
+///
+@system unittest
+{
+ interface I {}
+ class K : I {}
+
+ K k = void;
+ emplace(&k);
+ assert(k is null);
+
+ I i = void;
+ emplace(&i);
+ assert(i is null);
+}
+
+/**
+Given a pointer `chunk` to uninitialized memory (but already typed
+as a non-class type `T`), constructs an object of type `T` at
+that address from arguments `args`. If `T` is a class, initializes
+the class reference to `args[0]`.
+This function can be `@trusted` if the corresponding constructor of
+`T` is `@safe`.
+Returns: A pointer to the newly constructed object (which is the same
+as `chunk`).
+ */
+T* emplace(T, Args...)(T* chunk, auto ref Args args)
+ if (is(T == struct) || Args.length == 1)
+{
+ import core.internal.lifetime : emplaceRef;
+
+ emplaceRef!T(*chunk, forward!args);
+ return chunk;
+}
+
+///
+@betterC
+@system unittest
+{
+ int a;
+ int b = 42;
+ assert(*emplace!int(&a, b) == 42);
+}
+
+@betterC
+@system unittest
+{
+ shared int i;
+ emplace(&i, 42);
+ assert(i == 42);
+}
+
+/**
+Given a raw memory area `chunk` (but already typed as a class type `T`),
+constructs an object of `class` type `T` at that address. The constructor
+is passed the arguments `Args`.
+If `T` is an inner class whose `outer` field can be used to access an instance
+of the enclosing class, then `Args` must not be empty, and the first member of it
+must be a valid initializer for that `outer` field. Correct initialization of
+this field is essential to access members of the outer class inside `T` methods.
+Note:
+This function is `@safe` if the corresponding constructor of `T` is `@safe`.
+Returns: The newly constructed object.
+ */
+T emplace(T, Args...)(T chunk, auto ref Args args)
+ if (is(T == class))
+{
+ import core.internal.traits : isInnerClass;
+
+ static assert(!__traits(isAbstractClass, T), T.stringof ~
+ " is abstract and it can't be emplaced");
+
+ // Initialize the object in its pre-ctor state
+ enum classSize = __traits(classInstanceSize, T);
+ (() @trusted => (cast(void*) chunk)[0 .. classSize] = typeid(T).initializer[])();
+
+ static if (isInnerClass!T)
+ {
+ static assert(Args.length > 0,
+ "Initializing an inner class requires a pointer to the outer class");
+ static assert(is(Args[0] : typeof(T.outer)),
+ "The first argument must be a pointer to the outer class");
+
+ chunk.outer = args[0];
+ alias args1 = args[1..$];
+ }
+ else alias args1 = args;
+
+ // Call the ctor if any
+ static if (is(typeof(chunk.__ctor(forward!args1))))
+ {
+ // T defines a genuine constructor accepting args
+ // Go the classic route: write .init first, then call ctor
+ chunk.__ctor(forward!args1);
+ }
+ else
+ {
+ static assert(args1.length == 0 && !is(typeof(&T.__ctor)),
+ "Don't know how to initialize an object of type "
+ ~ T.stringof ~ " with arguments " ~ typeof(args1).stringof);
+ }
+ return chunk;
+}
+
+///
+@safe unittest
+{
+ () @safe {
+ class SafeClass
+ {
+ int x;
+ @safe this(int x) { this.x = x; }
+ }
+
+ auto buf = new void[__traits(classInstanceSize, SafeClass)];
+ auto support = (() @trusted => cast(SafeClass)(buf.ptr))();
+ auto safeClass = emplace!SafeClass(support, 5);
+ assert(safeClass.x == 5);
+
+ class UnsafeClass
+ {
+ int x;
+ @system this(int x) { this.x = x; }
+ }
+
+ auto buf2 = new void[__traits(classInstanceSize, UnsafeClass)];
+ auto support2 = (() @trusted => cast(UnsafeClass)(buf2.ptr))();
+ static assert(!__traits(compiles, emplace!UnsafeClass(support2, 5)));
+ static assert(!__traits(compiles, emplace!UnsafeClass(buf2, 5)));
+ }();
+}
+
+@safe unittest
+{
+ class Outer
+ {
+ int i = 3;
+ class Inner
+ {
+ @safe auto getI() { return i; }
+ }
+ }
+ auto outerBuf = new void[__traits(classInstanceSize, Outer)];
+ auto outerSupport = (() @trusted => cast(Outer)(outerBuf.ptr))();
+
+ auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)];
+ auto innerSupport = (() @trusted => cast(Outer.Inner)(innerBuf.ptr))();
+
+ auto inner = innerSupport.emplace!(Outer.Inner)(outerSupport.emplace!Outer);
+ assert(inner.getI == 3);
+}
+
+/**
+Given a raw memory area `chunk`, constructs an object of `class` type `T` at
+that address. The constructor is passed the arguments `Args`.
+If `T` is an inner class whose `outer` field can be used to access an instance
+of the enclosing class, then `Args` must not be empty, and the first member of it
+must be a valid initializer for that `outer` field. Correct initialization of
+this field is essential to access members of the outer class inside `T` methods.
+Preconditions:
+`chunk` must be at least as large as `T` needs and should have an alignment
+multiple of `T`'s alignment. (The size of a `class` instance is obtained by using
+$(D __traits(classInstanceSize, T))).
+Note:
+This function can be `@trusted` if the corresponding constructor of `T` is `@safe`.
+Returns: The newly constructed object.
+ */
+T emplace(T, Args...)(void[] chunk, auto ref Args args)
+ if (is(T == class))
+{
+ import core.internal.traits : maxAlignment;
+
+ enum classSize = __traits(classInstanceSize, T);
+ assert(chunk.length >= classSize, "chunk size too small.");
+
+ enum alignment = maxAlignment!(void*, typeof(T.tupleof));
+ assert((cast(size_t) chunk.ptr) % alignment == 0, "chunk is not aligned.");
+
+ return emplace!T(cast(T)(chunk.ptr), forward!args);
+}
+
+///
+@system unittest
+{
+ static class C
+ {
+ int i;
+ this(int i){this.i = i;}
+ }
+ auto buf = new void[__traits(classInstanceSize, C)];
+ auto c = emplace!C(buf, 5);
+ assert(c.i == 5);
+}
+
+@system unittest
+{
+ class Outer
+ {
+ int i = 3;
+ class Inner
+ {
+ auto getI() { return i; }
+ }
+ }
+ auto outerBuf = new void[__traits(classInstanceSize, Outer)];
+ auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)];
+ auto inner = innerBuf.emplace!(Outer.Inner)(outerBuf.emplace!Outer);
+ assert(inner.getI == 3);
+}
+
+@nogc pure nothrow @safe unittest
+{
+ static class __conv_EmplaceTestClass
+ {
+ @nogc @safe pure nothrow:
+ int i = 3;
+ this(int i)
+ {
+ assert(this.i == 3);
+ this.i = 10 + i;
+ }
+ this(ref int i)
+ {
+ assert(this.i == 3);
+ this.i = 20 + i;
+ }
+ this(int i, ref int j)
+ {
+ assert(this.i == 3 && i == 5 && j == 6);
+ this.i = i;
+ ++j;
+ }
+ }
+
+ int var = 6;
+ align(__conv_EmplaceTestClass.alignof) ubyte[__traits(classInstanceSize, __conv_EmplaceTestClass)] buf;
+ auto support = (() @trusted => cast(__conv_EmplaceTestClass)(buf.ptr))();
+
+ auto fromRval = emplace!__conv_EmplaceTestClass(support, 1);
+ assert(fromRval.i == 11);
+
+ auto fromLval = emplace!__conv_EmplaceTestClass(support, var);
+ assert(fromLval.i == 26);
+
+ auto k = emplace!__conv_EmplaceTestClass(support, 5, var);
+ assert(k.i == 5);
+ assert(var == 7);
+}
+
+/**
+Given a raw memory area `chunk`, constructs an object of non-$(D
+class) type `T` at that address. The constructor is passed the
+arguments `args`, if any.
+Preconditions:
+`chunk` must be at least as large
+as `T` needs and should have an alignment multiple of `T`'s
+alignment.
+Note:
+This function can be `@trusted` if the corresponding constructor of
+`T` is `@safe`.
+Returns: A pointer to the newly constructed object.
+ */
+T* emplace(T, Args...)(void[] chunk, auto ref Args args)
+ if (!is(T == class))
+{
+ import core.internal.traits : Unqual;
+ import core.internal.lifetime : emplaceRef;
+
+ assert(chunk.length >= T.sizeof, "chunk size too small.");
+ assert((cast(size_t) chunk.ptr) % T.alignof == 0, "emplace: Chunk is not aligned.");
+
+ emplaceRef!(T, Unqual!T)(*cast(Unqual!T*) chunk.ptr, forward!args);
+ return cast(T*) chunk.ptr;
+}
+
+///
+@betterC
+@system unittest
+{
+ struct S
+ {
+ int a, b;
+ }
+ void[S.sizeof] buf = void;
+ S s;
+ s.a = 42;
+ s.b = 43;
+ auto s1 = emplace!S(buf, s);
+ assert(s1.a == 42 && s1.b == 43);
+}
+
+// Bulk of emplace unittests starts here
+
+@betterC
+@system unittest /* unions */
+{
+ static union U
+ {
+ string a;
+ int b;
+ struct
+ {
+ long c;
+ int[] d;
+ }
+ }
+ U u1 = void;
+ U u2 = { "hello" };
+ emplace(&u1, u2);
+ assert(u1.a == "hello");
+}
+
+@system unittest // bugzilla 15772
+{
+ abstract class Foo {}
+ class Bar: Foo {}
+ void[] memory;
+ // test in emplaceInitializer
+ static assert(!is(typeof(emplace!Foo(cast(Foo*) memory.ptr))));
+ static assert( is(typeof(emplace!Bar(cast(Bar*) memory.ptr))));
+ // test in the emplace overload that takes void[]
+ static assert(!is(typeof(emplace!Foo(memory))));
+ static assert( is(typeof(emplace!Bar(memory))));
+}
+
+@betterC
+@system unittest
+{
+ struct S { @disable this(); }
+ S s = void;
+ static assert(!__traits(compiles, emplace(&s)));
+ emplace(&s, S.init);
+}
+
+@betterC
+@system unittest
+{
+ struct S1
+ {}
+
+ struct S2
+ {
+ void opAssign(S2);
+ }
+
+ S1 s1 = void;
+ S2 s2 = void;
+ S1[2] as1 = void;
+ S2[2] as2 = void;
+ emplace(&s1);
+ emplace(&s2);
+ emplace(&as1);
+ emplace(&as2);
+}
+
+@system unittest
+{
+ static struct S1
+ {
+ this(this) @disable;
+ }
+ static struct S2
+ {
+ this() @disable;
+ }
+ S1[2] ss1 = void;
+ S2[2] ss2 = void;
+ emplace(&ss1);
+ static assert(!__traits(compiles, emplace(&ss2)));
+ S1 s1 = S1.init;
+ S2 s2 = S2.init;
+ static assert(!__traits(compiles, emplace(&ss1, s1)));
+ emplace(&ss2, s2);
+}
+
+@system unittest
+{
+ struct S
+ {
+ immutable int i;
+ }
+ S s = void;
+ S[2] ss1 = void;
+ S[2] ss2 = void;
+ emplace(&s, 5);
+ assert(s.i == 5);
+ emplace(&ss1, s);
+ assert(ss1[0].i == 5 && ss1[1].i == 5);
+ emplace(&ss2, ss1);
+ assert(ss2 == ss1);
+}
+
+//Start testing emplace-args here
+
+@system unittest
+{
+ interface I {}
+ class K : I {}
+
+ K k = null, k2 = new K;
+ assert(k !is k2);
+ emplace!K(&k, k2);
+ assert(k is k2);
+
+ I i = null;
+ assert(i !is k);
+ emplace!I(&i, k);
+ assert(i is k);
+}
+
+@system unittest
+{
+ static struct S
+ {
+ int i = 5;
+ void opAssign(S){assert(0);}
+ }
+ S[2] sa = void;
+ S[2] sb;
+ emplace(&sa, sb);
+ assert(sa[0].i == 5 && sa[1].i == 5);
+}
+
+//Start testing emplace-struct here
+
+// Test constructor branch
+@betterC
+@system unittest
+{
+ struct S
+ {
+ double x = 5, y = 6;
+ this(int a, int b)
+ {
+ assert(x == 5 && y == 6);
+ x = a;
+ y = b;
+ }
+ }
+
+ void[S.sizeof] s1 = void;
+ auto s2 = S(42, 43);
+ assert(*emplace!S(cast(S*) s1.ptr, s2) == s2);
+ assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45));
+}
+
+@system unittest
+{
+ static struct __conv_EmplaceTest
+ {
+ int i = 3;
+ this(int i)
+ {
+ assert(this.i == 3 && i == 5);
+ this.i = i;
+ }
+ this(int i, ref int j)
+ {
+ assert(i == 5 && j == 6);
+ this.i = i;
+ ++j;
+ }
+
+ @disable:
+ this();
+ this(this);
+ void opAssign();
+ }
+
+ __conv_EmplaceTest k = void;
+ emplace(&k, 5);
+ assert(k.i == 5);
+
+ int var = 6;
+ __conv_EmplaceTest x = void;
+ emplace(&x, 5, var);
+ assert(x.i == 5);
+ assert(var == 7);
+
+ var = 6;
+ auto z = emplace!__conv_EmplaceTest(new void[__conv_EmplaceTest.sizeof], 5, var);
+ assert(z.i == 5);
+ assert(var == 7);
+}
+
+// Test matching fields branch
+@betterC
+@system unittest
+{
+ struct S { uint n; }
+ S s;
+ emplace!S(&s, 2U);
+ assert(s.n == 2);
+}
+
+@betterC
+@safe unittest
+{
+ struct S { int a, b; this(int){} }
+ S s;
+ static assert(!__traits(compiles, emplace!S(&s, 2, 3)));
+}
+
+@betterC
+@system unittest
+{
+ struct S { int a, b = 7; }
+ S s1 = void, s2 = void;
+
+ emplace!S(&s1, 2);
+ assert(s1.a == 2 && s1.b == 7);
+
+ emplace!S(&s2, 2, 3);
+ assert(s2.a == 2 && s2.b == 3);
+}
+
+//opAssign
+@betterC
+@system unittest
+{
+ static struct S
+ {
+ int i = 5;
+ void opAssign(int){assert(0);}
+ void opAssign(S){assert(0);}
+ }
+ S sa1 = void;
+ S sa2 = void;
+ S sb1 = S(1);
+ emplace(&sa1, sb1);
+ emplace(&sa2, 2);
+ assert(sa1.i == 1);
+ assert(sa2.i == 2);
+}
+
+//postblit precedence
+@betterC
+@system unittest
+{
+ //Works, but breaks in "-w -O" because of @@@9332@@@.
+ //Uncomment test when 9332 is fixed.
+ static struct S
+ {
+ int i;
+
+ this(S other){assert(false);}
+ this(int i){this.i = i;}
+ this(this){}
+ }
+ S a = void;
+ assert(is(typeof({S b = a;}))); //Postblit
+ assert(is(typeof({S b = S(a);}))); //Constructor
+ auto b = S(5);
+ emplace(&a, b);
+ assert(a.i == 5);
+
+ static struct S2
+ {
+ int* p;
+ this(const S2){}
+ }
+ static assert(!is(immutable S2 : S2));
+ S2 s2 = void;
+ immutable is2 = (immutable S2).init;
+ emplace(&s2, is2);
+}
+
+//nested structs and postblit
+@system unittest
+{
+ static struct S
+ {
+ int* p;
+ this(int i){p = [i].ptr;}
+ this(this)
+ {
+ if (p)
+ p = [*p].ptr;
+ }
+ }
+ static struct SS
+ {
+ S s;
+ void opAssign(const SS)
+ {
+ assert(0);
+ }
+ }
+ SS ssa = void;
+ SS ssb = SS(S(5));
+ emplace(&ssa, ssb);
+ assert(*ssa.s.p == 5);
+ assert(ssa.s.p != ssb.s.p);
+}
+
+//disabled postblit
+@betterC
+@system unittest
+{
+ static struct S1
+ {
+ int i;
+ @disable this(this);
+ }
+ S1 s1 = void;
+ emplace(&s1, 1);
+ assert(s1.i == 1);
+ static assert(!__traits(compiles, emplace(&s1, s1))); // copy disabled
+ static assert(__traits(compiles, emplace(&s1, move(s1)))); // move not affected
+
+ static struct S2
+ {
+ int i;
+ @disable this(this);
+ this(ref S2){}
+ }
+ S2 s2 = void;
+ //static assert(!__traits(compiles, emplace(&s2, 1)));
+ emplace(&s2, S2.init);
+
+ static struct SS1
+ {
+ S1 s;
+ }
+ SS1 ss1 = void;
+ emplace(&ss1);
+ static assert(!__traits(compiles, emplace(&ss1, ss1))); // copying disabled
+ static assert(__traits(compiles, emplace(&ss1, move(ss1)))); // move unaffected
+
+ static struct SS2
+ {
+ S2 s;
+ }
+ SS2 ss2 = void;
+ emplace(&ss2);
+ static assert(!__traits(compiles, emplace(&ss2, ss2))); // copying disabled
+ static assert(__traits(compiles, emplace(&ss2, SS2.init))); // move is OK
+
+
+ // SS1 sss1 = s1; //This doesn't compile
+ // SS1 sss1 = SS1(s1); //This doesn't compile
+ // So emplace shouldn't compile either
+ static assert(!__traits(compiles, emplace(&sss1, s1)));
+ static assert(!__traits(compiles, emplace(&sss2, s2)));
+}
+
+//Imutability
+@betterC
+@system unittest
+{
+ //Castable immutability
+ {
+ static struct S1
+ {
+ int i;
+ }
+ static assert(is( immutable(S1) : S1));
+ S1 sa = void;
+ auto sb = immutable(S1)(5);
+ emplace(&sa, sb);
+ assert(sa.i == 5);
+ }
+ //Un-castable immutability
+ {
+ static struct S2
+ {
+ int* p;
+ }
+ static assert(!is(immutable(S2) : S2));
+ S2 sa = void;
+ auto sb = immutable(S2)(null);
+ assert(!__traits(compiles, emplace(&sa, sb)));
+ }
+}
+
+@betterC
+@system unittest
+{
+ static struct S
+ {
+ immutable int i;
+ immutable(int)* j;
+ }
+ S s = void;
+ emplace(&s, 1, null);
+ emplace(&s, 2, &s.i);
+ assert(s is S(2, &s.i));
+}
+
+//Context pointer
+@system unittest
+{
+ int i = 0;
+ {
+ struct S1
+ {
+ void foo(){++i;}
+ }
+ S1 sa = void;
+ S1 sb;
+ emplace(&sa, sb);
+ sa.foo();
+ assert(i == 1);
+ }
+ {
+ struct S2
+ {
+ void foo(){++i;}
+ this(this){}
+ }
+ S2 sa = void;
+ S2 sb;
+ emplace(&sa, sb);
+ sa.foo();
+ assert(i == 2);
+ }
+}
+
+//Alias this
+@betterC
+@system unittest
+{
+ static struct S
+ {
+ int i;
+ }
+ //By Ref
+ {
+ static struct SS1
+ {
+ int j;
+ S s;
+ alias s this;
+ }
+ S s = void;
+ SS1 ss = SS1(1, S(2));
+ emplace(&s, ss);
+ assert(s.i == 2);
+ }
+ //By Value
+ {
+ static struct SS2
+ {
+ int j;
+ S s;
+ S foo() @property{return s;}
+ alias foo this;
+ }
+ S s = void;
+ SS2 ss = SS2(1, S(2));
+ emplace(&s, ss);
+ assert(s.i == 2);
+ }
+}
+
+version (CoreUnittest)
+{
+ //Ambiguity
+ private struct __std_conv_S
+ {
+ int i;
+ this(__std_conv_SS ss) {assert(0);}
+ static opCall(__std_conv_SS ss)
+ {
+ __std_conv_S s; s.i = ss.j;
+ return s;
+ }
+ }
+ private struct __std_conv_SS
+ {
+ int j;
+ __std_conv_S s;
+ ref __std_conv_S foo() return @property {s.i = j; return s;}
+ alias foo this;
+ }
+}
+
+@system unittest
+{
+ static assert(is(__std_conv_SS : __std_conv_S));
+ __std_conv_S s = void;
+ __std_conv_SS ss = __std_conv_SS(1);
+
+ __std_conv_S sTest1 = ss; //this calls "SS alias this" (and not "S.this(SS)")
+ emplace(&s, ss); //"alias this" should take precedence in emplace over "opCall"
+ assert(s.i == 1);
+}
+
+//Nested classes
+@system unittest
+{
+ class A{}
+ static struct S
+ {
+ A a;
+ }
+ S s1 = void;
+ S s2 = S(new A);
+ emplace(&s1, s2);
+ assert(s1.a is s2.a);
+}
+
+//safety & nothrow & CTFE
+@betterC
+@system unittest
+{
+ //emplace should be safe for anything with no elaborate opassign
+ static struct S1
+ {
+ int i;
+ }
+ static struct S2
+ {
+ int i;
+ this(int j)@safe nothrow{i = j;}
+ }
+
+ int i;
+ S1 s1 = void;
+ S2 s2 = void;
+
+ auto pi = &i;
+ auto ps1 = &s1;
+ auto ps2 = &s2;
+
+ void foo() @safe nothrow
+ {
+ emplace(pi);
+ emplace(pi, 5);
+ emplace(ps1);
+ emplace(ps1, 5);
+ emplace(ps1, S1.init);
+ emplace(ps2);
+ emplace(ps2, 5);
+ emplace(ps2, S2.init);
+ }
+ foo();
+
+ T bar(T)() @property
+ {
+ T t/+ = void+/; //CTFE void illegal
+ emplace(&t, 5);
+ return t;
+ }
+ // CTFE
+ enum a = bar!int;
+ static assert(a == 5);
+ enum b = bar!S1;
+ static assert(b.i == 5);
+ enum c = bar!S2;
+ static assert(c.i == 5);
+ // runtime
+ auto aa = bar!int;
+ assert(aa == 5);
+ auto bb = bar!S1;
+ assert(bb.i == 5);
+ auto cc = bar!S2;
+ assert(cc.i == 5);
+}
+
+@betterC
+@system unittest
+{
+ struct S
+ {
+ int[2] get(){return [1, 2];}
+ alias get this;
+ }
+ struct SS
+ {
+ int[2] ii;
+ }
+ struct ISS
+ {
+ int[2] ii;
+ }
+ S s;
+ SS ss = void;
+ ISS iss = void;
+ emplace(&ss, s);
+ emplace(&iss, s);
+ assert(ss.ii == [1, 2]);
+ assert(iss.ii == [1, 2]);
+}
+
+//disable opAssign
+@betterC
+@system unittest
+{
+ static struct S
+ {
+ @disable void opAssign(S);
+ }
+ S s;
+ emplace(&s, S.init);
+}
+
+//opCall
+@betterC
+@system unittest
+{
+ int i;
+ //Without constructor
+ {
+ static struct S1
+ {
+ int i;
+ static S1 opCall(int*){assert(0);}
+ }
+ S1 s = void;
+ static assert(!__traits(compiles, emplace(&s, 1)));
+ }
+ //With constructor
+ {
+ static struct S2
+ {
+ int i = 0;
+ static S2 opCall(int*){assert(0);}
+ static S2 opCall(int){assert(0);}
+ this(int i){this.i = i;}
+ }
+ S2 s = void;
+ emplace(&s, 1);
+ assert(s.i == 1);
+ }
+ //With postblit ambiguity
+ {
+ static struct S3
+ {
+ int i = 0;
+ static S3 opCall(ref S3){assert(0);}
+ }
+ S3 s = void;
+ emplace(&s, S3.init);
+ }
+}
+
+//static arrays
+@system unittest
+{
+ static struct S
+ {
+ int[2] ii;
+ }
+ static struct IS
+ {
+ immutable int[2] ii;
+ }
+ int[2] ii;
+ S s = void;
+ IS ims = void;
+ ubyte ub = 2;
+ emplace(&s, ub);
+ emplace(&s, ii);
+ emplace(&ims, ub);
+ emplace(&ims, ii);
+ uint[2] uu;
+ static assert(!__traits(compiles, {S ss = S(uu);}));
+ static assert(!__traits(compiles, emplace(&s, uu)));
+}
+
+@system unittest
+{
+ int[2] sii;
+ int[2] sii2;
+ uint[2] uii;
+ uint[2] uii2;
+ emplace(&sii, 1);
+ emplace(&sii, 1U);
+ emplace(&uii, 1);
+ emplace(&uii, 1U);
+ emplace(&sii, sii2);
+ //emplace(&sii, uii2); //Sorry, this implementation doesn't know how to...
+ //emplace(&uii, sii2); //Sorry, this implementation doesn't know how to...
+ emplace(&uii, uii2);
+ emplace(&sii, sii2[]);
+ //emplace(&sii, uii2[]); //Sorry, this implementation doesn't know how to...
+ //emplace(&uii, sii2[]); //Sorry, this implementation doesn't know how to...
+ emplace(&uii, uii2[]);
+}
+
+@system unittest
+{
+ bool allowDestruction = false;
+ struct S
+ {
+ int i;
+ this(this){}
+ ~this(){assert(allowDestruction);}
+ }
+ S s = S(1);
+ S[2] ss1 = void;
+ S[2] ss2 = void;
+ S[2] ss3 = void;
+ emplace(&ss1, s);
+ emplace(&ss2, ss1);
+ emplace(&ss3, ss2[]);
+ assert(ss1[1] == s);
+ assert(ss2[1] == s);
+ assert(ss3[1] == s);
+ allowDestruction = true;
+}
+
+@system unittest
+{
+ //Checks postblit, construction, and context pointer
+ int count = 0;
+ struct S
+ {
+ this(this)
+ {
+ ++count;
+ }
+ ~this()
+ {
+ --count;
+ }
+ }
+
+ S s;
+ {
+ S[4] ss = void;
+ emplace(&ss, s);
+ assert(count == 4);
+ }
+ assert(count == 0);
+}
+
+@system unittest
+{
+ struct S
+ {
+ int i;
+ }
+ S s;
+ S[2][2][2] sss = void;
+ emplace(&sss, s);
+}
+
+@system unittest //Constness
+{
+ import core.internal.lifetime : emplaceRef;
+
+ int a = void;
+ emplaceRef!(const int)(a, 5);
+
+ immutable i = 5;
+ const(int)* p = void;
+ emplaceRef!(const int*)(p, &i);
+
+ struct S
+ {
+ int* p;
+ }
+ alias IS = immutable(S);
+ S s = void;
+ emplaceRef!IS(s, IS());
+ S[2] ss = void;
+ emplaceRef!(IS[2])(ss, IS());
+
+ IS[2] iss = IS.init;
+ emplaceRef!(IS[2])(ss, iss);
+ emplaceRef!(IS[2])(ss, iss[]);
+}
+
+@betterC
+pure nothrow @safe @nogc unittest
+{
+ import core.internal.lifetime : emplaceRef;
+
+ int i;
+ emplaceRef(i);
+ emplaceRef!int(i);
+ emplaceRef(i, 5);
+ emplaceRef!int(i, 5);
+}
+
+// Test attribute propagation for UDTs
+pure nothrow @safe /* @nogc */ unittest
+{
+ import core.internal.lifetime : emplaceRef;
+
+ static struct Safe
+ {
+ this(this) pure nothrow @safe @nogc {}
+ }
+
+ Safe safe = void;
+ emplaceRef(safe, Safe());
+
+ Safe[1] safeArr = [Safe()];
+ Safe[1] uninitializedSafeArr = void;
+ emplaceRef(uninitializedSafeArr, safe);
+ emplaceRef(uninitializedSafeArr, safeArr);
+
+ static struct Unsafe
+ {
+ this(this) @system {}
+ }
+
+ Unsafe unsafe = void;
+ static assert(!__traits(compiles, emplaceRef(unsafe, unsafe)));
+
+ Unsafe[1] unsafeArr = [Unsafe()];
+ Unsafe[1] uninitializedUnsafeArr = void;
+ static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafe)));
+ static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafeArr)));
+}
+
+@betterC
+@system unittest
+{
+ // Issue 15313
+ static struct Node
+ {
+ int payload;
+ Node* next;
+ uint refs;
+ }
+
+ import core.stdc.stdlib : malloc;
+ void[] buf = malloc(Node.sizeof)[0 .. Node.sizeof];
+
+ const Node* n = emplace!(const Node)(buf, 42, null, 10);
+ assert(n.payload == 42);
+ assert(n.next == null);
+ assert(n.refs == 10);
+}
+
+@system unittest
+{
+ class A
+ {
+ int x = 5;
+ int y = 42;
+ this(int z)
+ {
+ assert(x == 5 && y == 42);
+ x = y = z;
+ }
+ }
+ void[] buf;
+
+ static align(A.alignof) byte[__traits(classInstanceSize, A)] sbuf;
+ buf = sbuf[];
+ auto a = emplace!A(buf, 55);
+ assert(a.x == 55 && a.y == 55);
+
+ // emplace in bigger buffer
+ buf = new byte[](__traits(classInstanceSize, A) + 10);
+ a = emplace!A(buf, 55);
+ assert(a.x == 55 && a.y == 55);
+
+ // need ctor args
+ static assert(!is(typeof(emplace!A(buf))));
+}
+
+//constructor arguments forwarding
+@betterC
+@system unittest
+{
+ static struct S
+ {
+ this()(auto ref long arg)
+ {
+ // assert that arg is an lvalue
+ static assert(__traits(isRef, arg));
+ }
+ this()(auto ref double arg)
+ // assert that arg is an rvalue
+ {
+ static assert(!__traits(isRef, arg));
+ }
+ }
+ S obj = void;
+ long i;
+ emplace(&obj, i); // lvalue
+ emplace(&obj, 0.0); // rvalue
+}
+// Bulk of emplace unittests ends here
+
+/**
+ * Emplaces a copy of the specified source value into uninitialized memory,
+ * i.e., simulates `T target = source` copy-construction for cases where the
+ * target memory is already allocated and to be initialized with a copy.
+ *
+ * Params:
+ * source = value to be copied into target
+ * target = uninitialized value to be initialized with a copy of source
+ */
+void copyEmplace(S, T)(ref S source, ref T target) @system
+ if (is(immutable S == immutable T))
+{
+ import core.internal.traits : BaseElemOf, hasElaborateCopyConstructor, Unconst, Unqual;
+
+ // cannot have the following as simple template constraint due to nested-struct special case...
+ static if (!__traits(compiles, (ref S src) { T tgt = src; }))
+ {
+ alias B = BaseElemOf!T;
+ enum isNestedStruct = is(B == struct) && __traits(isNested, B);
+ static assert(isNestedStruct, "cannot copy-construct " ~ T.stringof ~ " from " ~ S.stringof);
+ }
+
+ void blit()
+ {
+ import core.stdc.string : memcpy;
+ memcpy(cast(Unqual!(T)*) &target, cast(Unqual!(T)*) &source, T.sizeof);
+ }
+
+ static if (is(T == struct))
+ {
+ static if (__traits(hasPostblit, T))
+ {
+ blit();
+ (cast() target).__xpostblit();
+ }
+ else static if (__traits(hasCopyConstructor, T))
+ {
+ emplace(cast(Unqual!(T)*) &target); // blit T.init
+ static if (__traits(isNested, T))
+ {
+ // copy context pointer
+ *(cast(void**) &target.tupleof[$-1]) = cast(void*) source.tupleof[$-1];
+ }
+ target.__ctor(source); // invoke copy ctor
+ }
+ else
+ {
+ blit(); // no opAssign
+ }
+ }
+ else static if (is(T == E[n], E, size_t n))
+ {
+ static if (hasElaborateCopyConstructor!E)
+ {
+ size_t i;
+ try
+ {
+ for (i = 0; i < n; i++)
+ copyEmplace(source[i], target[i]);
+ }
+ catch (Exception e)
+ {
+ // destroy, in reverse order, what we've constructed so far
+ while (i--)
+ destroy(*cast(Unconst!(E)*) &target[i]);
+ throw e;
+ }
+ }
+ else // trivial copy
+ {
+ blit(); // all elements at once
+ }
+ }
+ else
+ {
+ *cast(Unconst!(T)*) &target = *cast(Unconst!(T)*) &source;
+ }
+}
+
+///
+@betterC
+@system pure nothrow @nogc unittest
+{
+ int source = 123;
+ int target = void;
+ copyEmplace(source, target);
+ assert(target == 123);
+}
+
+///
+@betterC
+@system pure nothrow @nogc unittest
+{
+ immutable int[1][1] source = [ [123] ];
+ immutable int[1][1] target = void;
+ copyEmplace(source, target);
+ assert(target[0][0] == 123);
+}
+
+///
+@betterC
+@system pure nothrow @nogc unittest
+{
+ struct S
+ {
+ int x;
+ void opAssign(const scope ref S rhs) @safe pure nothrow @nogc
+ {
+ assert(0);
+ }
+ }
+
+ S source = S(42);
+ S target = void;
+ copyEmplace(source, target);
+ assert(target.x == 42);
+}
+
+// preserve shared-ness
+@system pure nothrow unittest
+{
+ auto s = new Object();
+ auto ss = new shared Object();
+
+ Object t;
+ shared Object st;
+
+ copyEmplace(s, t);
+ assert(t is s);
+
+ copyEmplace(ss, st);
+ assert(st is ss);
+
+ static assert(!__traits(compiles, copyEmplace(s, st)));
+ static assert(!__traits(compiles, copyEmplace(ss, t)));
+}
+
+version (DigitalMars) version (X86) version (Posix) version = DMD_X86_Posix;
+
+// don't violate immutability for reference types
+@system pure nothrow unittest
+{
+ auto s = new Object();
+ auto si = new immutable Object();
+
+ Object t;
+ immutable Object ti;
+
+ copyEmplace(s, t);
+ assert(t is s);
+
+ copyEmplace(si, ti);
+ version (DMD_X86_Posix) { /* wrongly fails without -O */ } else
+ assert(ti is si);
+
+ static assert(!__traits(compiles, copyEmplace(s, ti)));
+ static assert(!__traits(compiles, copyEmplace(si, t)));
+}
+
+version (CoreUnittest)
+{
+ private void testCopyEmplace(S, T)(const scope T* expected = null)
+ {
+ S source;
+ T target = void;
+ copyEmplace(source, target);
+ if (expected)
+ assert(target == *expected);
+ else
+ {
+ T expectedCopy = source;
+ assert(target == expectedCopy);
+ }
+ }
+}
+
+// postblit
+@system pure nothrow @nogc unittest
+{
+ static struct S
+ {
+ @safe pure nothrow @nogc:
+ int x = 42;
+ this(this) { x += 10; }
+ }
+
+ testCopyEmplace!(S, S)();
+ testCopyEmplace!(immutable S, S)();
+ testCopyEmplace!(S, immutable S)();
+ testCopyEmplace!(immutable S, immutable S)();
+
+ testCopyEmplace!(S[1], S[1])();
+ testCopyEmplace!(immutable S[1], S[1])();
+
+ // copying to an immutable static array works, but `T expected = source`
+ // wrongly ignores the postblit: https://issues.dlang.org/show_bug.cgi?id=8950
+ immutable S[1] expectedImmutable = [S(52)];
+ testCopyEmplace!(S[1], immutable S[1])(&expectedImmutable);
+ testCopyEmplace!(immutable S[1], immutable S[1])(&expectedImmutable);
+}
+
+// copy constructors
+@system pure nothrow @nogc unittest
+{
+ static struct S
+ {
+ @safe pure nothrow @nogc:
+ int x = 42;
+ this(int x) { this.x = x; }
+ this(const scope ref S rhs) { x = rhs.x + 10; }
+ this(const scope ref S rhs) immutable { x = rhs.x + 20; }
+ }
+
+ testCopyEmplace!(S, S)();
+ testCopyEmplace!(immutable S, S)();
+ testCopyEmplace!(S, immutable S)();
+ testCopyEmplace!(immutable S, immutable S)();
+
+ // static arrays work, but `T expected = source` wrongly ignores copy ctors
+ // https://issues.dlang.org/show_bug.cgi?id=20365
+ S[1] expectedMutable = [S(52)];
+ immutable S[1] expectedImmutable = [immutable S(62)];
+ testCopyEmplace!(S[1], S[1])(&expectedMutable);
+ testCopyEmplace!(immutable S[1], S[1])(&expectedMutable);
+ testCopyEmplace!(S[1], immutable S[1])(&expectedImmutable);
+ testCopyEmplace!(immutable S[1], immutable S[1])(&expectedImmutable);
+}
+
+// copy constructor in nested struct
+@system pure nothrow unittest
+{
+ int copies;
+ struct S
+ {
+ @safe pure nothrow @nogc:
+ size_t x = 42;
+ this(size_t x) { this.x = x; }
+ this(const scope ref S rhs)
+ {
+ assert(x == 42); // T.init
+ x = rhs.x;
+ ++copies;
+ }
+ }
+
+ {
+ copies = 0;
+ S source = S(123);
+ immutable S target = void;
+ copyEmplace(source, target);
+ assert(target is source);
+ assert(copies == 1);
+ }
+
+ {
+ copies = 0;
+ immutable S[1] source = [immutable S(456)];
+ S[1] target = void;
+ copyEmplace(source, target);
+ assert(target[0] is source[0]);
+ assert(copies == 1);
+ }
+}
+
+// destruction of partially copied static array
+@system unittest
+{
+ static struct S
+ {
+ __gshared int[] deletions;
+ int x;
+ this(this) { if (x == 5) throw new Exception(""); }
+ ~this() { deletions ~= x; }
+ }
+
+ alias T = immutable S[3][2];
+ T source = [ [S(1), S(2), S(3)], [S(4), S(5), S(6)] ];
+ T target = void;
+ try
+ {
+ copyEmplace(source, target);
+ assert(0);
+ }
+ catch (Exception)
+ {
+ static immutable expectedDeletions = [ 4, 3, 2, 1 ];
+ version (DigitalMars)
+ {
+ assert(S.deletions == expectedDeletions ||
+ S.deletions == [ 4 ]); // FIXME: happens with -O
+ }
+ else
+ assert(S.deletions == expectedDeletions);
+ }
+}
+
+/**
+Forwards function arguments while keeping `out`, `ref`, and `lazy` on
+the parameters.
+
+Params:
+ args = a parameter list or an $(REF AliasSeq,std,meta).
+Returns:
+ An `AliasSeq` of `args` with `out`, `ref`, and `lazy` saved.
+*/
+template forward(args...)
+{
+ import core.internal.traits : AliasSeq;
+
+ static if (args.length)
+ {
+ alias arg = args[0];
+ // by ref || lazy || const/immutable
+ static if (__traits(isRef, arg) ||
+ __traits(isOut, arg) ||
+ __traits(isLazy, arg) ||
+ !is(typeof(move(arg))))
+ alias fwd = arg;
+ // (r)value
+ else
+ @property auto fwd(){ return move(arg); }
+
+ static if (args.length == 1)
+ alias forward = fwd;
+ else
+ alias forward = AliasSeq!(fwd, forward!(args[1..$]));
+ }
+ else
+ alias forward = AliasSeq!();
+}
+
+///
+@safe unittest
+{
+ class C
+ {
+ static int foo(int n) { return 1; }
+ static int foo(ref int n) { return 2; }
+ }
+
+ // with forward
+ int bar()(auto ref int x) { return C.foo(forward!x); }
+
+ // without forward
+ int baz()(auto ref int x) { return C.foo(x); }
+
+ int i;
+ assert(bar(1) == 1);
+ assert(bar(i) == 2);
+
+ assert(baz(1) == 2);
+ assert(baz(i) == 2);
+}
+
+///
+@safe unittest
+{
+ void foo(int n, ref string s) { s = null; foreach (i; 0 .. n) s ~= "Hello"; }
+
+ // forwards all arguments which are bound to parameter tuple
+ void bar(Args...)(auto ref Args args) { return foo(forward!args); }
+
+ // forwards all arguments with swapping order
+ void baz(Args...)(auto ref Args args) { return foo(forward!args[$/2..$], forward!args[0..$/2]); }
+
+ string s;
+ bar(1, s);
+ assert(s == "Hello");
+ baz(s, 2);
+ assert(s == "HelloHello");
+}
+
+@safe unittest
+{
+ auto foo(TL...)(auto ref TL args)
+ {
+ string result = "";
+ foreach (i, _; args)
+ {
+ //pragma(msg, "[",i,"] ", __traits(isRef, args[i]) ? "L" : "R");
+ result ~= __traits(isRef, args[i]) ? "L" : "R";
+ }
+ return result;
+ }
+
+ string bar(TL...)(auto ref TL args)
+ {
+ return foo(forward!args);
+ }
+ string baz(TL...)(auto ref TL args)
+ {
+ int x;
+ return foo(forward!args[3], forward!args[2], 1, forward!args[1], forward!args[0], x);
+ }
+
+ struct S {}
+ S makeS(){ return S(); }
+ int n;
+ string s;
+ assert(bar(S(), makeS(), n, s) == "RRLL");
+ assert(baz(S(), makeS(), n, s) == "LLRRRL");
+}
+
+@betterC
+@safe unittest
+{
+ ref int foo(return ref int a) { return a; }
+ ref int bar(Args)(auto ref Args args)
+ {
+ return foo(forward!args);
+ }
+ static assert(!__traits(compiles, { auto x1 = bar(3); })); // case of NG
+ int value = 3;
+ auto x2 = bar(value); // case of OK
+}
+
+///
+@betterC
+@safe unittest
+{
+ struct X {
+ int i;
+ this(this)
+ {
+ ++i;
+ }
+ }
+
+ struct Y
+ {
+ private X x_;
+ this()(auto ref X x)
+ {
+ x_ = forward!x;
+ }
+ }
+
+ struct Z
+ {
+ private const X x_;
+ this()(auto ref X x)
+ {
+ x_ = forward!x;
+ }
+ this()(auto const ref X x)
+ {
+ x_ = forward!x;
+ }
+ }
+
+ X x;
+ const X cx;
+ auto constX = (){ const X x; return x; };
+ static assert(__traits(compiles, { Y y = x; }));
+ static assert(__traits(compiles, { Y y = X(); }));
+ static assert(!__traits(compiles, { Y y = cx; }));
+ static assert(!__traits(compiles, { Y y = constX(); }));
+ static assert(__traits(compiles, { Z z = x; }));
+ static assert(__traits(compiles, { Z z = X(); }));
+ static assert(__traits(compiles, { Z z = cx; }));
+ static assert(__traits(compiles, { Z z = constX(); }));
+
+
+ Y y1 = x;
+ // ref lvalue, copy
+ assert(y1.x_.i == 1);
+ Y y2 = X();
+ // rvalue, move
+ assert(y2.x_.i == 0);
+
+ Z z1 = x;
+ // ref lvalue, copy
+ assert(z1.x_.i == 1);
+ Z z2 = X();
+ // rvalue, move
+ assert(z2.x_.i == 0);
+ Z z3 = cx;
+ // ref const lvalue, copy
+ assert(z3.x_.i == 1);
+ Z z4 = constX();
+ // const rvalue, copy
+ assert(z4.x_.i == 1);
+}
+
+// lazy -> lazy
+@betterC
+@safe unittest
+{
+ int foo1(lazy int i) { return i; }
+ int foo2(A)(auto ref A i) { return foo1(forward!i); }
+ int foo3(lazy int i) { return foo2(i); }
+
+ int numCalls = 0;
+ assert(foo3({ ++numCalls; return 42; }()) == 42);
+ assert(numCalls == 1);
+}
+
+// lazy -> non-lazy
+@betterC
+@safe unittest
+{
+ int foo1(int a, int b) { return a + b; }
+ int foo2(A...)(auto ref A args) { return foo1(forward!args); }
+ int foo3(int a, lazy int b) { return foo2(a, b); }
+
+ int numCalls;
+ assert(foo3(11, { ++numCalls; return 31; }()) == 42);
+ assert(numCalls == 1);
+}
+
+// non-lazy -> lazy
+@betterC
+@safe unittest
+{
+ int foo1(int a, lazy int b) { return a + b; }
+ int foo2(A...)(auto ref A args) { return foo1(forward!args); }
+ int foo3(int a, int b) { return foo2(a, b); }
+
+ assert(foo3(11, 31) == 42);
+}
+
+// out
+@betterC
+@safe unittest
+{
+ void foo1(int a, out int b) { b = a; }
+ void foo2(A...)(auto ref A args) { foo1(forward!args); }
+ void foo3(int a, out int b) { foo2(a, b); }
+
+ int b;
+ foo3(42, b);
+ assert(b == 42);
+}
+
+// move
+/**
+Moves `source` into `target`, via a destructive copy when necessary.
+
+If `T` is a struct with a destructor or postblit defined, source is reset
+to its `.init` value after it is moved into target, otherwise it is
+left unchanged.
+
+Preconditions:
+If source has internal pointers that point to itself and doesn't define
+opPostMove, it cannot be moved, and will trigger an assertion failure.
+
+Params:
+ source = Data to copy.
+ target = Where to copy into. The destructor, if any, is invoked before the
+ copy is performed.
+*/
+void move(T)(ref T source, ref T target)
+{
+ moveImpl(target, source);
+}
+
+/// For non-struct types, `move` just performs `target = source`:
+@safe unittest
+{
+ Object obj1 = new Object;
+ Object obj2 = obj1;
+ Object obj3;
+
+ move(obj2, obj3);
+ assert(obj3 is obj1);
+ // obj2 unchanged
+ assert(obj2 is obj1);
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ // Structs without destructors are simply copied
+ struct S1
+ {
+ int a = 1;
+ int b = 2;
+ }
+ S1 s11 = { 10, 11 };
+ S1 s12;
+
+ move(s11, s12);
+
+ assert(s12 == S1(10, 11));
+ assert(s11 == s12);
+
+ // But structs with destructors or postblits are reset to their .init value
+ // after copying to the target.
+ struct S2
+ {
+ int a = 1;
+ int b = 2;
+
+ ~this() pure nothrow @safe @nogc { }
+ }
+ S2 s21 = { 3, 4 };
+ S2 s22;
+
+ move(s21, s22);
+
+ assert(s21 == S2(1, 2));
+ assert(s22 == S2(3, 4));
+}
+
+@safe unittest
+{
+ import core.internal.traits;
+
+ assertCTFEable!((){
+ Object obj1 = new Object;
+ Object obj2 = obj1;
+ Object obj3;
+ move(obj2, obj3);
+ assert(obj3 is obj1);
+
+ static struct S1 { int a = 1, b = 2; }
+ S1 s11 = { 10, 11 };
+ S1 s12;
+ move(s11, s12);
+ assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11);
+
+ static struct S2 { int a = 1; int * b; }
+ S2 s21 = { 10, null };
+ s21.b = new int;
+ S2 s22;
+ move(s21, s22);
+ assert(s21 == s22);
+ });
+ // Issue 5661 test(1)
+ static struct S3
+ {
+ static struct X { int n = 0; ~this(){n = 0;} }
+ X x;
+ }
+ static assert(hasElaborateDestructor!S3);
+ S3 s31, s32;
+ s31.x.n = 1;
+ move(s31, s32);
+ assert(s31.x.n == 0);
+ assert(s32.x.n == 1);
+
+ // Issue 5661 test(2)
+ static struct S4
+ {
+ static struct X { int n = 0; this(this){n = 0;} }
+ X x;
+ }
+ static assert(hasElaborateCopyConstructor!S4);
+ S4 s41, s42;
+ s41.x.n = 1;
+ move(s41, s42);
+ assert(s41.x.n == 0);
+ assert(s42.x.n == 1);
+
+ // Issue 13990 test
+ class S5;
+
+ S5 s51;
+ S5 s52 = s51;
+ S5 s53;
+ move(s52, s53);
+ assert(s53 is s51);
+}
+
+/// Ditto
+T move(T)(return scope ref T source)
+{
+ return moveImpl(source);
+}
+
+/// Non-copyable structs can still be moved:
+pure nothrow @safe @nogc unittest
+{
+ struct S
+ {
+ int a = 1;
+ @disable this(this);
+ ~this() pure nothrow @safe @nogc {}
+ }
+ S s1;
+ s1.a = 2;
+ S s2 = move(s1);
+ assert(s1.a == 1);
+ assert(s2.a == 2);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20869
+// `move` should propagate the attributes of `opPostMove`
+@system unittest
+{
+ static struct S
+ {
+ void opPostMove(const ref S old) nothrow @system
+ {
+ __gshared int i;
+ new int(i++); // Force @gc impure @system
+ }
+ }
+
+ alias T = void function() @system nothrow;
+ static assert(is(typeof({ S s; move(s); }) == T));
+ static assert(is(typeof({ S s; move(s, s); }) == T));
+}
+
+private void moveImpl(T)(scope ref T target, return scope ref T source)
+{
+ import core.internal.traits : hasElaborateDestructor;
+
+ static if (is(T == struct))
+ {
+ // Unsafe when compiling without -dip1000
+ if ((() @trusted => &source == &target)()) return;
+ // Destroy target before overwriting it
+ static if (hasElaborateDestructor!T) target.__xdtor();
+ }
+ // move and emplace source into target
+ moveEmplaceImpl(target, source);
+}
+
+private T moveImpl(T)(return scope ref T source)
+{
+ // Properly infer safety from moveEmplaceImpl as the implementation below
+ // might void-initialize pointers in result and hence needs to be @trusted
+ if (false) moveEmplaceImpl(source, source);
+
+ return trustedMoveImpl(source);
+}
+
+private T trustedMoveImpl(T)(return scope ref T source) @trusted
+{
+ T result = void;
+ moveEmplaceImpl(result, source);
+ return result;
+}
+
+@safe unittest
+{
+ import core.internal.traits;
+
+ assertCTFEable!((){
+ Object obj1 = new Object;
+ Object obj2 = obj1;
+ Object obj3 = move(obj2);
+ assert(obj3 is obj1);
+
+ static struct S1 { int a = 1, b = 2; }
+ S1 s11 = { 10, 11 };
+ S1 s12 = move(s11);
+ assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11);
+
+ static struct S2 { int a = 1; int * b; }
+ S2 s21 = { 10, null };
+ s21.b = new int;
+ S2 s22 = move(s21);
+ assert(s21 == s22);
+ });
+
+ // Issue 5661 test(1)
+ static struct S3
+ {
+ static struct X { int n = 0; ~this(){n = 0;} }
+ X x;
+ }
+ static assert(hasElaborateDestructor!S3);
+ S3 s31;
+ s31.x.n = 1;
+ S3 s32 = move(s31);
+ assert(s31.x.n == 0);
+ assert(s32.x.n == 1);
+
+ // Issue 5661 test(2)
+ static struct S4
+ {
+ static struct X { int n = 0; this(this){n = 0;} }
+ X x;
+ }
+ static assert(hasElaborateCopyConstructor!S4);
+ S4 s41;
+ s41.x.n = 1;
+ S4 s42 = move(s41);
+ assert(s41.x.n == 0);
+ assert(s42.x.n == 1);
+
+ // Issue 13990 test
+ class S5;
+
+ S5 s51;
+ S5 s52 = s51;
+ S5 s53;
+ s53 = move(s52);
+ assert(s53 is s51);
+}
+
+@betterC
+@system unittest
+{
+ static struct S { int n = 0; ~this() @system { n = 0; } }
+ S a, b;
+ static assert(!__traits(compiles, () @safe { move(a, b); }));
+ static assert(!__traits(compiles, () @safe { move(a); }));
+ a.n = 1;
+ () @trusted { move(a, b); }();
+ assert(a.n == 0);
+ a.n = 1;
+ () @trusted { move(a); }();
+ assert(a.n == 0);
+}
+/+ this can't be tested in druntime, tests are still run in phobos
+@safe unittest//Issue 6217
+{
+ import std.algorithm.iteration : map;
+ auto x = map!"a"([1,2,3]);
+ x = move(x);
+}
++/
+@betterC
+@safe unittest// Issue 8055
+{
+ static struct S
+ {
+ int x;
+ ~this()
+ {
+ assert(x == 0);
+ }
+ }
+ S foo(S s)
+ {
+ return move(s);
+ }
+ S a;
+ a.x = 0;
+ auto b = foo(a);
+ assert(b.x == 0);
+}
+
+@system unittest// Issue 8057
+{
+ int n = 10;
+ struct S
+ {
+ int x;
+ ~this()
+ {
+ // Access to enclosing scope
+ assert(n == 10);
+ }
+ }
+ S foo(S s)
+ {
+ // Move nested struct
+ return move(s);
+ }
+ S a;
+ a.x = 1;
+ auto b = foo(a);
+ assert(b.x == 1);
+
+ // Regression 8171
+ static struct Array(T)
+ {
+ // nested struct has no member
+ struct Payload
+ {
+ ~this() {}
+ }
+ }
+ Array!int.Payload x = void;
+ move(x);
+ move(x, x);
+}
+
+// target must be first-parameter, because in void-functions DMD + dip1000 allows it to take the place of a return-scope
+private void moveEmplaceImpl(T)(scope ref T target, return scope ref T source)
+{
+ import core.stdc.string : memcpy, memset;
+ import core.internal.traits;
+
+ // TODO: this assert pulls in half of phobos. we need to work out an alternative assert strategy.
+// static if (!is(T == class) && hasAliasing!T) if (!__ctfe)
+// {
+// import std.exception : doesPointTo;
+// assert(!doesPointTo(source, source) && !hasElaborateMove!T),
+// "Cannot move object with internal pointer unless `opPostMove` is defined.");
+// }
+
+ static if (is(T == struct))
+ {
+ // Unsafe when compiling without -dip1000
+ assert((() @trusted => &source !is &target)(), "source and target must not be identical");
+
+ static if (hasElaborateAssign!T || !isAssignable!T)
+ () @trusted { memcpy(&target, &source, T.sizeof); }();
+ else
+ target = source;
+
+ static if (hasElaborateMove!T)
+ __move_post_blt(target, source);
+
+ // If the source defines a destructor or a postblit hook, we must obliterate the
+ // object in order to avoid double freeing and undue aliasing
+ static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T)
+ {
+ // If T is nested struct, keep original context pointer
+ static if (__traits(isNested, T))
+ enum sz = T.sizeof - (void*).sizeof;
+ else
+ enum sz = T.sizeof;
+
+ static if (__traits(isZeroInit, T))
+ () @trusted { memset(&source, 0, sz); }();
+ else
+ {
+ auto init = typeid(T).initializer();
+ () @trusted { memcpy(&source, init.ptr, sz); }();
+ }
+ }
+ }
+ else static if (__traits(isStaticArray, T))
+ {
+ for (size_t i = 0; i < source.length; ++i)
+ move(source[i], target[i]);
+ }
+ else
+ {
+ // Primitive data (including pointers and arrays) or class -
+ // assignment works great
+ target = source;
+ }
+}
+
+/**
+ * Similar to $(LREF move) but assumes `target` is uninitialized. This
+ * is more efficient because `source` can be blitted over `target`
+ * without destroying or initializing it first.
+ *
+ * Params:
+ * source = value to be moved into target
+ * target = uninitialized value to be filled by source
+ */
+void moveEmplace(T)(ref T source, ref T target) @system
+{
+ moveEmplaceImpl(target, source);
+}
+
+///
+@betterC
+pure nothrow @nogc @system unittest
+{
+ static struct Foo
+ {
+ pure nothrow @nogc:
+ this(int* ptr) { _ptr = ptr; }
+ ~this() { if (_ptr) ++*_ptr; }
+ int* _ptr;
+ }
+
+ int val;
+ Foo foo1 = void; // uninitialized
+ auto foo2 = Foo(&val); // initialized
+ assert(foo2._ptr is &val);
+
+ // Using `move(foo2, foo1)` would have an undefined effect because it would destroy
+ // the uninitialized foo1.
+ // moveEmplace directly overwrites foo1 without destroying or initializing it first.
+ moveEmplace(foo2, foo1);
+ assert(foo1._ptr is &val);
+ assert(foo2._ptr is null);
+ assert(val == 0);
+}
+
+// issue 18913
+@safe unittest
+{
+ static struct NoCopy
+ {
+ int payload;
+ ~this() { }
+ @disable this(this);
+ }
+
+ static void f(NoCopy[2]) { }
+
+ NoCopy[2] ncarray = [ NoCopy(1), NoCopy(2) ];
+
+ static assert(!__traits(compiles, f(ncarray)));
+ f(move(ncarray));
+}
diff --git a/libphobos/libdruntime/core/memory.d b/libphobos/libdruntime/core/memory.d
index af0fee1a47d..3770c13337c 100644
--- a/libphobos/libdruntime/core/memory.d
+++ b/libphobos/libdruntime/core/memory.d
@@ -104,31 +104,26 @@
module core.memory;
+version (ARM)
+ version = AnyARM;
+else version (AArch64)
+ version = AnyARM;
+
+version (iOS)
+ version = iOSDerived;
+else version (TVOS)
+ version = iOSDerived;
+else version (WatchOS)
+ version = iOSDerived;
private
{
- extern (C) void gc_init();
- extern (C) void gc_term();
-
- extern (C) void gc_enable() nothrow;
- extern (C) void gc_disable() nothrow;
- extern (C) void gc_collect() nothrow;
- extern (C) void gc_minimize() nothrow;
-
extern (C) uint gc_getAttr( void* p ) pure nothrow;
extern (C) uint gc_setAttr( void* p, uint a ) pure nothrow;
extern (C) uint gc_clrAttr( void* p, uint a ) pure nothrow;
- extern (C) void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow;
- extern (C) void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow;
- extern (C) BlkInfo_ gc_qalloc( size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow;
- extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow;
- extern (C) size_t gc_extend( void* p, size_t mx, size_t sz, const TypeInfo = null ) pure nothrow;
- extern (C) size_t gc_reserve( size_t sz ) nothrow;
- extern (C) void gc_free( void* p ) pure nothrow;
-
- extern (C) void* gc_addrOf( void* p ) pure nothrow;
- extern (C) size_t gc_sizeOf( void* p ) pure nothrow;
+ extern (C) void* gc_addrOf( void* p ) pure nothrow @nogc;
+ extern (C) size_t gc_sizeOf( void* p ) pure nothrow @nogc;
struct BlkInfo_
{
@@ -137,19 +132,96 @@ private
uint attr;
}
- extern (C) BlkInfo_ gc_query( void* p ) pure nothrow;
+ extern (C) BlkInfo_ gc_query(return scope void* p) pure nothrow;
extern (C) GC.Stats gc_stats ( ) nothrow @nogc;
+ extern (C) GC.ProfileStats gc_profileStats ( ) nothrow @nogc @safe;
+}
- extern (C) void gc_addRoot( in void* p ) nothrow @nogc;
- extern (C) void gc_addRange( in void* p, size_t sz, const TypeInfo ti = null ) nothrow @nogc;
+version (CoreDoc)
+{
+ /**
+ * The minimum size of a system page in bytes.
+ *
+ * This is a compile time, platform specific value. This value might not
+ * be accurate, since it might be possible to change this value. Whenever
+ * possible, please use $(LREF pageSize) instead, which is initialized
+ * during runtime.
+ *
+ * The minimum size is useful when the context requires a compile time known
+ * value, like the size of a static array: `ubyte[minimumPageSize] buffer`.
+ */
+ enum minimumPageSize : size_t;
+}
+else version (AnyARM)
+{
+ version (iOSDerived)
+ enum size_t minimumPageSize = 16384;
+ else
+ enum size_t minimumPageSize = 4096;
+}
+else
+ enum size_t minimumPageSize = 4096;
- extern (C) void gc_removeRoot( in void* p ) nothrow @nogc;
- extern (C) void gc_removeRange( in void* p ) nothrow @nogc;
- extern (C) void gc_runFinalizers( in void[] segment );
+///
+unittest
+{
+ ubyte[minimumPageSize] buffer;
+}
- package extern (C) bool gc_inFinalizer();
+/**
+ * The size of a system page in bytes.
+ *
+ * This value is set at startup time of the application. It's safe to use
+ * early in the start process, like in shared module constructors and
+ * initialization of the D runtime itself.
+ */
+immutable size_t pageSize;
+
+///
+unittest
+{
+ ubyte[] buffer = new ubyte[pageSize];
}
+// The reason for this elaborated way of declaring a function is:
+//
+// * `pragma(crt_constructor)` is used to declare a constructor that is called by
+// the C runtime, before C main. This allows the `pageSize` value to be used
+// during initialization of the D runtime. This also avoids any issues with
+// static module constructors and circular references.
+//
+// * `pragma(mangle)` is used because `pragma(crt_constructor)` requires a
+// function with C linkage. To avoid any name conflict with other C symbols,
+// standard D mangling is used.
+//
+// * The extra function declaration, without the body, is to be able to get the
+// D mangling of the function without the need to hardcode the value.
+//
+// * The extern function declaration also has the side effect of making it
+// impossible to manually call the function with standard syntax. This is to
+// make it more difficult to call the function again, manually.
+private void initialize();
+pragma(crt_constructor)
+pragma(mangle, `_D` ~ initialize.mangleof)
+private extern (C) void initialize() @system
+{
+ version (Posix)
+ {
+ import core.sys.posix.unistd : sysconf, _SC_PAGESIZE;
+
+ (cast() pageSize) = cast(size_t) sysconf(_SC_PAGESIZE);
+ }
+ else version (Windows)
+ {
+ import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
+
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ (cast() pageSize) = cast(size_t) si.dwPageSize;
+ }
+ else
+ static assert(false, __FUNCTION__ ~ " is not implemented on this platform");
+}
/**
* This struct encapsulates all garbage collection functionality for the D
@@ -168,18 +240,37 @@ struct GC
size_t usedSize;
/// number of free bytes on the GC heap (might only get updated after a collection)
size_t freeSize;
+ /// number of bytes allocated for current thread since program start
+ ulong allocatedInCurrentThread;
}
/**
+ * Aggregation of current profile information
+ */
+ static struct ProfileStats
+ {
+ import core.time : Duration;
+ /// total number of GC cycles
+ size_t numCollections;
+ /// total time spent doing GC
+ Duration totalCollectionTime;
+ /// total time threads were paused doing GC
+ Duration totalPauseTime;
+ /// largest time threads were paused during one GC cycle
+ Duration maxPauseTime;
+ /// largest time spent doing one GC cycle
+ Duration maxCollectionTime;
+ }
+
+extern(C):
+
+ /**
* Enables automatic garbage collection behavior if collections have
* previously been suspended by a call to disable. This function is
* reentrant, and must be called once for every call to disable before
* automatic collections are enabled.
*/
- static void enable() nothrow /* FIXME pure */
- {
- gc_enable();
- }
+ pragma(mangle, "gc_enable") static void enable() nothrow; /* FIXME pure */
/**
@@ -189,10 +280,7 @@ struct GC
* such as during an out of memory condition. This function is reentrant,
* but enable must be called once for each call to disable.
*/
- static void disable() nothrow /* FIXME pure */
- {
- gc_disable();
- }
+ pragma(mangle, "gc_disable") static void disable() nothrow; /* FIXME pure */
/**
@@ -202,21 +290,16 @@ struct GC
* and then to reclaim free space. This action may need to suspend all
* running threads for at least part of the collection process.
*/
- static void collect() nothrow /* FIXME pure */
- {
- gc_collect();
- }
+ pragma(mangle, "gc_collect") static void collect() nothrow; /* FIXME pure */
/**
* Indicates that the managed memory space be minimized by returning free
* physical memory to the operating system. The amount of free memory
* returned depends on the allocator design and on program behavior.
*/
- static void minimize() nothrow /* FIXME pure */
- {
- gc_minimize();
- }
+ pragma(mangle, "gc_minimize") static void minimize() nothrow; /* FIXME pure */
+extern(D):
/**
* Elements for a bit field representing memory block attributes. These
@@ -288,9 +371,9 @@ struct GC
* A bit field containing any bits set for the memory block referenced by
* p or zero on error.
*/
- static uint getAttr( in void* p ) nothrow
+ static uint getAttr( const scope void* p ) nothrow
{
- return getAttr(cast()p);
+ return gc_getAttr(cast(void*) p);
}
@@ -315,9 +398,9 @@ struct GC
* The result of a call to getAttr after the specified bits have been
* set.
*/
- static uint setAttr( in void* p, uint a ) nothrow
+ static uint setAttr( const scope void* p, uint a ) nothrow
{
- return setAttr(cast()p, a);
+ return gc_setAttr(cast(void*) p, a);
}
@@ -342,9 +425,9 @@ struct GC
* The result of a call to getAttr after the specified bits have been
* cleared.
*/
- static uint clrAttr( in void* p, uint a ) nothrow
+ static uint clrAttr( const scope void* p, uint a ) nothrow
{
- return clrAttr(cast()p, a);
+ return gc_clrAttr(cast(void*) p, a);
}
@@ -354,6 +437,7 @@ struct GC
return gc_clrAttr( p, a );
}
+extern(C):
/**
* Requests an aligned block of managed memory from the garbage collector.
@@ -375,10 +459,7 @@ struct GC
* Throws:
* OutOfMemoryError on allocation failure.
*/
- static void* malloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) pure nothrow
- {
- return gc_malloc( sz, ba, ti );
- }
+ pragma(mangle, "gc_malloc") static void* malloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null) pure nothrow;
/**
@@ -401,10 +482,7 @@ struct GC
* Throws:
* OutOfMemoryError on allocation failure.
*/
- static BlkInfo qalloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) pure nothrow
- {
- return gc_qalloc( sz, ba, ti );
- }
+ pragma(mangle, "gc_qalloc") static BlkInfo qalloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null) pure nothrow;
/**
@@ -428,52 +506,55 @@ struct GC
* Throws:
* OutOfMemoryError on allocation failure.
*/
- static void* calloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) pure nothrow
- {
- return gc_calloc( sz, ba, ti );
- }
+ pragma(mangle, "gc_calloc") static void* calloc(size_t sz, uint ba = 0, const TypeInfo ti = null) pure nothrow;
/**
- * If sz is zero, the memory referenced by p will be deallocated as if
- * by a call to free. A new memory block of size sz will then be
- * allocated as if by a call to malloc, or the implementation may instead
- * resize the memory block in place. The contents of the new memory block
- * will be the same as the contents of the old memory block, up to the
- * lesser of the new and old sizes. Note that existing memory will only
- * be freed by realloc if sz is equal to zero. The garbage collector is
- * otherwise expected to later reclaim the memory block if it is unused.
- * If allocation fails, this function will call onOutOfMemory which is
- * expected to throw an OutOfMemoryError. If p references memory not
- * originally allocated by this garbage collector, or if it points to the
- * interior of a memory block, no action will be taken. If ba is zero
- * (the default) and p references the head of a valid, known memory block
- * then any bits set on the current block will be set on the new block if a
- * reallocation is required. If ba is not zero and p references the head
- * of a valid, known memory block then the bits in ba will replace those on
- * the current memory block and will also be set on the new block if a
- * reallocation is required.
+ * Extend, shrink or allocate a new block of memory keeping the contents of
+ * an existing block
+ *
+ * If `sz` is zero, the memory referenced by p will be deallocated as if
+ * by a call to `free`.
+ * If `p` is `null`, new memory will be allocated via `malloc`.
+ * If `p` is pointing to memory not allocated from the GC or to the interior
+ * of an allocated memory block, no operation is performed and null is returned.
+ *
+ * Otherwise, a new memory block of size `sz` will be allocated as if by a
+ * call to `malloc`, or the implementation may instead resize or shrink the memory
+ * block in place.
+ * The contents of the new memory block will be the same as the contents
+ * of the old memory block, up to the lesser of the new and old sizes.
+ *
+ * The caller guarantees that there are no other live pointers to the
+ * passed memory block, still it might not be freed immediately by `realloc`.
+ * The garbage collector can reclaim the memory block in a later
+ * collection if it is unused.
+ * If allocation fails, this function will throw an `OutOfMemoryError`.
+ *
+ * If `ba` is zero (the default) the attributes of the existing memory
+ * will be used for an allocation.
+ * If `ba` is not zero and no new memory is allocated, the bits in ba will
+ * replace those of the current memory block.
*
* Params:
- * p = A pointer to the root of a valid memory block or to null.
+ * p = A pointer to the base of a valid memory block or to `null`.
* sz = The desired allocation size in bytes.
- * ba = A bitmask of the attributes to set on this block.
+ * ba = A bitmask of the BlkAttr attributes to set on this block.
* ti = TypeInfo to describe the memory. The GC might use this information
* to improve scanning for pointers or to call finalizers.
*
* Returns:
- * A reference to the allocated memory on success or null if sz is
- * zero. On failure, the original value of p is returned.
+ * A reference to the allocated memory on success or `null` if `sz` is
+ * zero or the pointer does not point to the base of an GC allocated
+ * memory block.
*
* Throws:
- * OutOfMemoryError on allocation failure.
+ * `OutOfMemoryError` on allocation failure.
*/
- static void* realloc( void* p, size_t sz, uint ba = 0, const TypeInfo ti = null ) pure nothrow
- {
- return gc_realloc( p, sz, ba, ti );
- }
+ pragma(mangle, "gc_realloc") static void* realloc(return void* p, size_t sz, uint ba = 0, const TypeInfo ti = null) pure nothrow;
- /// Issue 13111
+ // https://issues.dlang.org/show_bug.cgi?id=13111
+ ///
unittest
{
enum size1 = 1 << 11 + 1; // page in large object pool
@@ -482,7 +563,7 @@ struct GC
auto data1 = cast(ubyte*)GC.calloc(size1);
auto data2 = cast(ubyte*)GC.realloc(data1, size2);
- BlkInfo info = query(data2);
+ GC.BlkInfo info = GC.query(data2);
assert(info.size >= size2);
}
@@ -512,10 +593,7 @@ struct GC
* as an indicator of success. $(LREF capacity) should be used to
* retrieve actual usable slice capacity.
*/
- static size_t extend( void* p, size_t mx, size_t sz, const TypeInfo ti = null ) pure nothrow
- {
- return gc_extend( p, mx, sz, ti );
- }
+ pragma(mangle, "gc_extend") static size_t extend(void* p, size_t mx, size_t sz, const TypeInfo ti = null) pure nothrow;
/// Standard extending
unittest
{
@@ -557,10 +635,7 @@ struct GC
* Returns:
* The actual number of bytes reserved or zero on error.
*/
- static size_t reserve( size_t sz ) nothrow /* FIXME pure */
- {
- return gc_reserve( sz );
- }
+ pragma(mangle, "gc_reserve") static size_t reserve(size_t sz) nothrow; /* FIXME pure */
/**
@@ -569,16 +644,14 @@ struct GC
* collector, if p points to the interior of a memory block, or if this
* method is called from a finalizer, no action will be taken. The block
* will not be finalized regardless of whether the FINALIZE attribute is
- * set. If finalization is desired, use delete instead.
+ * set. If finalization is desired, call $(REF1 destroy, object) prior to `GC.free`.
*
* Params:
* p = A pointer to the root of a valid memory block or to null.
*/
- static void free( void* p ) pure nothrow
- {
- gc_free( p );
- }
+ pragma(mangle, "gc_free") static void free(void* p) pure nothrow @nogc;
+extern(D):
/**
* Returns the base address of the memory block containing p. This value
@@ -595,19 +668,17 @@ struct GC
* Returns:
* The base address of the memory block referenced by p or null on error.
*/
- static inout(void)* addrOf( inout(void)* p ) nothrow /* FIXME pure */
+ static inout(void)* addrOf( inout(void)* p ) nothrow @nogc pure @trusted
{
return cast(inout(void)*)gc_addrOf(cast(void*)p);
}
-
/// ditto
- static void* addrOf(void* p) pure nothrow
+ static void* addrOf(void* p) pure nothrow @nogc @trusted
{
return gc_addrOf(p);
}
-
/**
* Returns the true size of the memory block referenced by p. This value
* represents the maximum number of bytes for which a call to realloc may
@@ -621,14 +692,14 @@ struct GC
* Returns:
* The size in bytes of the memory block referenced by p or zero on error.
*/
- static size_t sizeOf( in void* p ) nothrow
+ static size_t sizeOf( const scope void* p ) nothrow @nogc /* FIXME pure */
{
return gc_sizeOf(cast(void*)p);
}
/// ditto
- static size_t sizeOf(void* p) pure nothrow
+ static size_t sizeOf(void* p) pure nothrow @nogc
{
return gc_sizeOf( p );
}
@@ -659,14 +730,14 @@ struct GC
* Information regarding the memory block referenced by p or BlkInfo.init
* on error.
*/
- static BlkInfo query( in void* p ) nothrow
+ static BlkInfo query(return scope const void* p) nothrow
{
return gc_query(cast(void*)p);
}
/// ditto
- static BlkInfo query(void* p) pure nothrow
+ static BlkInfo query(return scope void* p) pure nothrow
{
return gc_query( p );
}
@@ -681,6 +752,17 @@ struct GC
}
/**
+ * Returns runtime profile stats for currently active GC implementation
+ * See `core.memory.GC.ProfileStats` for list of available metrics.
+ */
+ static ProfileStats profileStats() nothrow @nogc @safe
+ {
+ return gc_profileStats();
+ }
+
+extern(C):
+
+ /**
* Adds an internal root pointing to the GC memory block referenced by p.
* As a result, the block referenced by p itself and any blocks accessible
* via it will be considered live until the root is removed again.
@@ -725,10 +807,7 @@ struct GC
* }
* ---
*/
- static void addRoot( in void* p ) nothrow @nogc /* FIXME pure */
- {
- gc_addRoot( p );
- }
+ pragma(mangle, "gc_addRoot") static void addRoot(const void* p) nothrow @nogc; /* FIXME pure */
/**
@@ -739,10 +818,7 @@ struct GC
* Params:
* p = A pointer into a GC-managed memory block or null.
*/
- static void removeRoot( in void* p ) nothrow @nogc /* FIXME pure */
- {
- gc_removeRoot( p );
- }
+ pragma(mangle, "gc_removeRoot") static void removeRoot(const void* p) nothrow @nogc; /* FIXME pure */
/**
@@ -773,10 +849,7 @@ struct GC
* // rawMemory will be recognized on collection.
* ---
*/
- static void addRange( in void* p, size_t sz, const TypeInfo ti = null ) @nogc nothrow /* FIXME pure */
- {
- gc_addRange( p, sz, ti );
- }
+ pragma(mangle, "gc_addRange") static void addRange(const void* p, size_t sz, const TypeInfo ti = null) @nogc nothrow; /* FIXME pure */
/**
@@ -788,10 +861,7 @@ struct GC
* Params:
* p = A pointer to a valid memory address or to null.
*/
- static void removeRange( in void* p ) nothrow @nogc /* FIXME pure */
- {
- gc_removeRange( p );
- }
+ pragma(mangle, "gc_removeRange") static void removeRange(const void* p) nothrow @nogc; /* FIXME pure */
/**
@@ -804,9 +874,133 @@ struct GC
* Params:
* segment = address range of a code segment.
*/
- static void runFinalizers( in void[] segment )
+ pragma(mangle, "gc_runFinalizers") static void runFinalizers(const scope void[] segment);
+
+ /**
+ * Queries the GC whether the current thread is running object finalization
+ * as part of a GC collection, or an explicit call to runFinalizers.
+ *
+ * As some GC implementations (such as the current conservative one) don't
+ * support GC memory allocation during object finalization, this function
+ * can be used to guard against such programming errors.
+ *
+ * Returns:
+ * true if the current thread is in a finalizer, a destructor invoked by
+ * the GC.
+ */
+ pragma(mangle, "gc_inFinalizer") static bool inFinalizer() nothrow @nogc @safe;
+
+ ///
+ @safe nothrow @nogc unittest
{
- gc_runFinalizers( segment );
+ // Only code called from a destructor is executed during finalization.
+ assert(!GC.inFinalizer);
+ }
+
+ ///
+ unittest
+ {
+ enum Outcome
+ {
+ notCalled,
+ calledManually,
+ calledFromDruntime
+ }
+
+ static class Resource
+ {
+ static Outcome outcome;
+
+ this()
+ {
+ outcome = Outcome.notCalled;
+ }
+
+ ~this()
+ {
+ if (GC.inFinalizer)
+ {
+ outcome = Outcome.calledFromDruntime;
+
+ import core.exception : InvalidMemoryOperationError;
+ try
+ {
+ /*
+ * Presently, allocating GC memory during finalization
+ * is forbidden and leads to
+ * `InvalidMemoryOperationError` being thrown.
+ *
+ * `GC.inFinalizer` can be used to guard against
+ * programming erros such as these and is also a more
+ * efficient way to verify whether a destructor was
+ * invoked by the GC.
+ */
+ cast(void) GC.malloc(1);
+ assert(false);
+ }
+ catch (InvalidMemoryOperationError e)
+ {
+ return;
+ }
+ assert(false);
+ }
+ else
+ outcome = Outcome.calledManually;
+ }
+ }
+
+ static void createGarbage()
+ {
+ auto r = new Resource;
+ r = null;
+ }
+
+ assert(Resource.outcome == Outcome.notCalled);
+ createGarbage();
+ GC.collect;
+ assert(
+ Resource.outcome == Outcome.notCalled ||
+ Resource.outcome == Outcome.calledFromDruntime);
+
+ auto r = new Resource;
+ GC.runFinalizers((cast(const void*)typeid(Resource).destructor)[0..1]);
+ assert(Resource.outcome == Outcome.calledFromDruntime);
+ Resource.outcome = Outcome.notCalled;
+
+ debug(MEMSTOMP) {} else
+ {
+ // assume Resource data is still available
+ r.destroy;
+ assert(Resource.outcome == Outcome.notCalled);
+ }
+
+ r = new Resource;
+ assert(Resource.outcome == Outcome.notCalled);
+ r.destroy;
+ assert(Resource.outcome == Outcome.calledManually);
+ }
+
+ /**
+ * Returns the number of bytes allocated for the current thread
+ * since program start. It is the same as
+ * GC.stats().allocatedInCurrentThread, but faster.
+ */
+ pragma(mangle, "gc_allocatedInCurrentThread") static ulong allocatedInCurrentThread() nothrow;
+
+ /// Using allocatedInCurrentThread
+ nothrow unittest
+ {
+ ulong currentlyAllocated = GC.allocatedInCurrentThread();
+ struct DataStruct
+ {
+ long l1;
+ long l2;
+ long l3;
+ long l4;
+ }
+ DataStruct* unused = new DataStruct;
+ assert(GC.allocatedInCurrentThread() == currentlyAllocated + 32);
+ assert(GC.stats().allocatedInCurrentThread == currentlyAllocated + 32);
}
}
@@ -814,6 +1008,7 @@ struct GC
* Pure variants of C's memory allocation functions `malloc`, `calloc`, and
* `realloc` and deallocation function `free`.
*
+ * UNIX 98 requires that errno be set to ENOMEM upon failure.
* Purity is achieved by saving and restoring the value of `errno`, thus
* behaving as if it were never changed.
*
@@ -821,44 +1016,44 @@ struct GC
* $(LINK2 https://dlang.org/spec/function.html#pure-functions, D's rules for purity),
* which allow for memory allocation under specific circumstances.
*/
-void* pureMalloc(size_t size) @trusted pure @nogc nothrow
+void* pureMalloc()(size_t size) @trusted pure @nogc nothrow
{
- const errno = fakePureGetErrno();
+ const errnosave = fakePureErrno;
void* ret = fakePureMalloc(size);
- if (!ret || errno != 0)
- {
- cast(void)fakePureSetErrno(errno);
- }
+ fakePureErrno = errnosave;
return ret;
}
/// ditto
-void* pureCalloc(size_t nmemb, size_t size) @trusted pure @nogc nothrow
+void* pureCalloc()(size_t nmemb, size_t size) @trusted pure @nogc nothrow
{
- const errno = fakePureGetErrno();
+ const errnosave = fakePureErrno;
void* ret = fakePureCalloc(nmemb, size);
- if (!ret || errno != 0)
- {
- cast(void)fakePureSetErrno(errno);
- }
+ fakePureErrno = errnosave;
return ret;
}
/// ditto
-void* pureRealloc(void* ptr, size_t size) @system pure @nogc nothrow
+void* pureRealloc()(void* ptr, size_t size) @system pure @nogc nothrow
{
- const errno = fakePureGetErrno();
+ const errnosave = fakePureErrno;
void* ret = fakePureRealloc(ptr, size);
- if (!ret || errno != 0)
- {
- cast(void)fakePureSetErrno(errno);
- }
+ fakePureErrno = errnosave;
return ret;
}
+
/// ditto
-void pureFree(void* ptr) @system pure @nogc nothrow
+void pureFree()(void* ptr) @system pure @nogc nothrow
{
- const errno = fakePureGetErrno();
- fakePureFree(ptr);
- cast(void)fakePureSetErrno(errno);
+ version (Posix)
+ {
+ // POSIX free doesn't set errno
+ fakePureFree(ptr);
+ }
+ else
+ {
+ const errnosave = fakePureErrno;
+ fakePureFree(ptr);
+ fakePureErrno = errnosave;
+ }
}
///
@@ -881,40 +1076,451 @@ void pureFree(void* ptr) @system pure @nogc nothrow
@system pure nothrow @nogc unittest
{
- const int errno = fakePureGetErrno();
+ const int errno = fakePureErrno();
void* x = pureMalloc(10); // normal allocation
- assert(errno == fakePureGetErrno()); // errno shouldn't change
+ assert(errno == fakePureErrno()); // errno shouldn't change
assert(x !is null); // allocation should succeed
x = pureRealloc(x, 10); // normal reallocation
- assert(errno == fakePureGetErrno()); // errno shouldn't change
+ assert(errno == fakePureErrno()); // errno shouldn't change
assert(x !is null); // allocation should succeed
fakePureFree(x);
void* y = pureCalloc(10, 1); // normal zeroed allocation
- assert(errno == fakePureGetErrno()); // errno shouldn't change
+ assert(errno == fakePureErrno()); // errno shouldn't change
assert(y !is null); // allocation should succeed
fakePureFree(y);
- // subtract 2 because snn.lib adds 2 unconditionally before passing
- // the size to the Windows API
- void* z = pureMalloc(size_t.max - 2); // won't affect `errno`
- assert(errno == fakePureGetErrno()); // errno shouldn't change
+ // Workaround bug in glibc 2.26
+ // See also: https://issues.dlang.org/show_bug.cgi?id=17956
+ void* z = pureMalloc(size_t.max & ~255); // won't affect `errno`
+ assert(errno == fakePureErrno()); // errno shouldn't change
assert(z is null);
}
// locally purified for internal use here only
-extern (C) private pure @system @nogc nothrow
+
+static import core.stdc.errno;
+static if (__traits(getOverloads, core.stdc.errno, "errno").length == 1
+ && __traits(getLinkage, core.stdc.errno.errno) == "C")
{
- pragma(mangle, "getErrno") int fakePureGetErrno();
- pragma(mangle, "setErrno") int fakePureSetErrno(int);
+ extern(C) pragma(mangle, __traits(identifier, core.stdc.errno.errno))
+ private ref int fakePureErrno() @nogc nothrow pure @system;
+}
+else
+{
+ extern(C) private @nogc nothrow pure @system
+ {
+ pragma(mangle, __traits(identifier, core.stdc.errno.getErrno))
+ @property int fakePureErrno();
+ pragma(mangle, __traits(identifier, core.stdc.errno.setErrno))
+ @property int fakePureErrno(int);
+ }
+}
+
+version (D_BetterC) {}
+else // TODO: remove this function after Phobos no longer needs it.
+extern (C) private @system @nogc nothrow
+{
+ ref int fakePureErrnoImpl()
+ {
+ import core.stdc.errno;
+ return errno();
+ }
+}
+
+extern (C) private pure @system @nogc nothrow
+{
pragma(mangle, "malloc") void* fakePureMalloc(size_t);
pragma(mangle, "calloc") void* fakePureCalloc(size_t nmemb, size_t size);
pragma(mangle, "realloc") void* fakePureRealloc(void* ptr, size_t size);
pragma(mangle, "free") void fakePureFree(void* ptr);
}
+
+/**
+Destroys and then deallocates an object.
+
+In detail, `__delete(x)` returns with no effect if `x` is `null`. Otherwise, it
+performs the following actions in sequence:
+$(UL
+ $(LI
+ Calls the destructor `~this()` for the object referred to by `x`
+ (if `x` is a class or interface reference) or
+ for the object pointed to by `x` (if `x` is a pointer to a `struct`).
+ Arrays of structs call the destructor, if defined, for each element in the array.
+ If no destructor is defined, this step has no effect.
+ )
+ $(LI
+ Frees the memory allocated for `x`. If `x` is a reference to a class
+ or interface, the memory allocated for the underlying instance is freed. If `x` is
+ a pointer, the memory allocated for the pointed-to object is freed. If `x` is a
+ built-in array, the memory allocated for the array is freed.
+ If `x` does not refer to memory previously allocated with `new` (or the lower-level
+ equivalents in the GC API), the behavior is undefined.
+ )
+ $(LI
+ Lastly, `x` is set to `null`. Any attempt to read or write the freed memory via
+ other references will result in undefined behavior.
+ )
+)
+
+Note: Users should prefer $(REF1 destroy, object) to explicitly finalize objects,
+and only resort to $(REF __delete, core,memory) when $(REF destroy, object)
+wouldn't be a feasible option.
+
+Params:
+ x = aggregate object that should be destroyed
+
+See_Also: $(REF1 destroy, object), $(REF free, core,GC)
+
+History:
+
+The `delete` keyword allowed to free GC-allocated memory.
+As this is inherently not `@safe`, it has been deprecated.
+This function has been added to provide an easy transition from `delete`.
+It performs the same functionality as the former `delete` keyword.
+*/
+void __delete(T)(ref T x) @system
+{
+ static void _destructRecurse(S)(ref S s)
+ if (is(S == struct))
+ {
+ static if (__traits(hasMember, S, "__xdtor") &&
+ // Bugzilla 14746: Check that it's the exact member of S.
+ __traits(isSame, S, __traits(parent, s.__xdtor)))
+ s.__xdtor();
+ }
+
+ // See also: https://github.com/dlang/dmd/blob/v2.078.0/src/dmd/e2ir.d#L3886
+ static if (is(T == interface))
+ {
+ .object.destroy(x);
+ }
+ else static if (is(T == class))
+ {
+ .object.destroy(x);
+ }
+ else static if (is(T == U*, U))
+ {
+ static if (is(U == struct))
+ _destructRecurse(*x);
+ }
+ else static if (is(T : E[], E))
+ {
+ static if (is(E == struct))
+ {
+ foreach_reverse (ref e; x)
+ _destructRecurse(e);
+ }
+ }
+ else
+ {
+ static assert(0, "It is not possible to delete: `" ~ T.stringof ~ "`");
+ }
+
+ static if (is(T == interface) ||
+ is(T == class) ||
+ is(T == U2*, U2))
+ {
+ GC.free(GC.addrOf(cast(void*) x));
+ x = null;
+ }
+ else static if (is(T : E2[], E2))
+ {
+ GC.free(GC.addrOf(cast(void*) x.ptr));
+ x = null;
+ }
+}
+
+/// Deleting classes
+unittest
+{
+ bool dtorCalled;
+ class B
+ {
+ int test;
+ ~this()
+ {
+ dtorCalled = true;
+ }
+ }
+ B b = new B();
+ B a = b;
+ b.test = 10;
+
+ assert(GC.addrOf(cast(void*) b) != null);
+ __delete(b);
+ assert(b is null);
+ assert(dtorCalled);
+ assert(GC.addrOf(cast(void*) b) == null);
+ // but be careful, a still points to it
+ assert(a !is null);
+ assert(GC.addrOf(cast(void*) a) == null); // but not a valid GC pointer
+}
+
+/// Deleting interfaces
+unittest
+{
+ bool dtorCalled;
+ interface A
+ {
+ int quack();
+ }
+ class B : A
+ {
+ int a;
+ int quack()
+ {
+ a++;
+ return a;
+ }
+ ~this()
+ {
+ dtorCalled = true;
+ }
+ }
+ A a = new B();
+ a.quack();
+
+ assert(GC.addrOf(cast(void*) a) != null);
+ __delete(a);
+ assert(a is null);
+ assert(dtorCalled);
+ assert(GC.addrOf(cast(void*) a) == null);
+}
+
+/// Deleting structs
+unittest
+{
+ bool dtorCalled;
+ struct A
+ {
+ string test;
+ ~this()
+ {
+ dtorCalled = true;
+ }
+ }
+ auto a = new A("foo");
+
+ assert(GC.addrOf(cast(void*) a) != null);
+ __delete(a);
+ assert(a is null);
+ assert(dtorCalled);
+ assert(GC.addrOf(cast(void*) a) == null);
+}
+
+/// Deleting arrays
+unittest
+{
+ int[] a = [1, 2, 3];
+ auto b = a;
+
+ assert(GC.addrOf(b.ptr) != null);
+ __delete(b);
+ assert(b is null);
+ assert(GC.addrOf(b.ptr) == null);
+ // but be careful, a still points to it
+ assert(a !is null);
+ assert(GC.addrOf(a.ptr) == null); // but not a valid GC pointer
+}
+
+/// Deleting arrays of structs
+unittest
+{
+ int dtorCalled;
+ struct A
+ {
+ int a;
+ ~this()
+ {
+ assert(dtorCalled == a);
+ dtorCalled++;
+ }
+ }
+ auto arr = [A(1), A(2), A(3)];
+ arr[0].a = 2;
+ arr[1].a = 1;
+ arr[2].a = 0;
+
+ assert(GC.addrOf(arr.ptr) != null);
+ __delete(arr);
+ assert(dtorCalled == 3);
+ assert(GC.addrOf(arr.ptr) == null);
+}
+
+// Deleting raw memory
+unittest
+{
+ import core.memory : GC;
+ auto a = GC.malloc(5);
+ assert(GC.addrOf(cast(void*) a) != null);
+ __delete(a);
+ assert(a is null);
+ assert(GC.addrOf(cast(void*) a) == null);
+}
+
+// __delete returns with no effect if x is null
+unittest
+{
+ Object x = null;
+ __delete(x);
+
+ struct S { ~this() { } }
+ class C { }
+ interface I { }
+
+ int[] a; __delete(a);
+ S[] as; __delete(as);
+ C c; __delete(c);
+ I i; __delete(i);
+ C* pc = &c; __delete(*pc);
+ I* pi = &i; __delete(*pi);
+ int* pint; __delete(pint);
+ S* ps; __delete(ps);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19092
+unittest
+{
+ const(int)[] x = [1, 2, 3];
+ assert(GC.addrOf(x.ptr) != null);
+ __delete(x);
+ assert(x is null);
+ assert(GC.addrOf(x.ptr) == null);
+
+ immutable(int)[] y = [1, 2, 3];
+ assert(GC.addrOf(y.ptr) != null);
+ __delete(y);
+ assert(y is null);
+ assert(GC.addrOf(y.ptr) == null);
+}
+
+// test realloc behaviour
+unittest
+{
+ static void set(int* p, size_t size)
+ {
+ foreach (i; 0 .. size)
+ *p++ = cast(int) i;
+ }
+ static void verify(int* p, size_t size)
+ {
+ foreach (i; 0 .. size)
+ assert(*p++ == i);
+ }
+ static void test(size_t memsize)
+ {
+ int* p = cast(int*) GC.malloc(memsize * int.sizeof);
+ assert(p);
+ set(p, memsize);
+ verify(p, memsize);
+
+ int* q = cast(int*) GC.realloc(p + 4, 2 * memsize * int.sizeof);
+ assert(q == null);
+
+ q = cast(int*) GC.realloc(p + memsize / 2, 2 * memsize * int.sizeof);
+ assert(q == null);
+
+ q = cast(int*) GC.realloc(p + memsize - 1, 2 * memsize * int.sizeof);
+ assert(q == null);
+
+ int* r = cast(int*) GC.realloc(p, 5 * memsize * int.sizeof);
+ verify(r, memsize);
+ set(r, 5 * memsize);
+
+ int* s = cast(int*) GC.realloc(r, 2 * memsize * int.sizeof);
+ verify(s, 2 * memsize);
+
+ assert(GC.realloc(s, 0) == null); // free
+ assert(GC.addrOf(p) == null);
+ }
+
+ test(16);
+ test(200);
+ test(800); // spans large and small pools
+ test(1200);
+ test(8000);
+
+ void* p = GC.malloc(100);
+ assert(GC.realloc(&p, 50) == null); // non-GC pointer
+}
+
+// test GC.profileStats
+unittest
+{
+ auto stats = GC.profileStats();
+ GC.collect();
+ auto nstats = GC.profileStats();
+ assert(nstats.numCollections > stats.numCollections);
+}
+
+// in rt.lifetime:
+private extern (C) void* _d_newitemU(scope const TypeInfo _ti) @system pure nothrow;
+
+/**
+Moves a value to a new GC allocation.
+
+Params:
+ value = Value to be moved. If the argument is an lvalue and a struct with a
+ destructor or postblit, it will be reset to its `.init` value.
+
+Returns:
+ A pointer to the new GC-allocated value.
+*/
+T* moveToGC(T)(auto ref T value)
+{
+ static T* doIt(ref T value) @trusted
+ {
+ import core.lifetime : moveEmplace;
+ auto mem = cast(T*) _d_newitemU(typeid(T)); // allocate but don't initialize
+ moveEmplace(value, *mem);
+ return mem;
+ }
+
+ return doIt(value); // T dtor might be @system
+}
+
+///
+@safe pure nothrow unittest
+{
+ struct S
+ {
+ int x;
+ this(this) @disable;
+ ~this() @safe pure nothrow @nogc {}
+ }
+
+ S* p;
+
+ // rvalue
+ p = moveToGC(S(123));
+ assert(p.x == 123);
+
+ // lvalue
+ auto lval = S(456);
+ p = moveToGC(lval);
+ assert(p.x == 456);
+ assert(lval.x == 0);
+}
+
+// @system dtor
+unittest
+{
+ struct S
+ {
+ int x;
+ ~this() @system {}
+ }
+
+ // lvalue case is @safe, ref param isn't destructed
+ static assert(__traits(compiles, (ref S lval) @safe { moveToGC(lval); }));
+
+ // rvalue case is @system, value param is destructed
+ static assert(!__traits(compiles, () @safe { moveToGC(S(0)); }));
+}
diff --git a/libphobos/libdruntime/core/runtime.d b/libphobos/libdruntime/core/runtime.d
index 5fc99046d23..bfb72e07b05 100644
--- a/libphobos/libdruntime/core/runtime.d
+++ b/libphobos/libdruntime/core/runtime.d
@@ -4,13 +4,8 @@
* Copyright: Copyright Sean Kelly 2005 - 2009.
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Authors: Sean Kelly
- * Source: $(DRUNTIMESRC core/_runtime.d)
- */
-
-/* Copyright Sean Kelly 2005 - 2009.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
+ * Source: $(LINK2 https://github.com/dlang/druntime/blob/master/src/core/runtime.d, _runtime.d)
+ * Documentation: https://dlang.org/phobos/core_runtime.html
*/
/* NOTE: This file has been patched from the original DMD distribution to
@@ -18,8 +13,6 @@
*/
module core.runtime;
-version (Windows) import core.stdc.wchar_ : wchar_t;
-
version (OSX)
version = Darwin;
else version (iOS)
@@ -29,10 +22,30 @@ else version (TVOS)
else version (WatchOS)
version = Darwin;
+version (GNU)
+{
+ import gcc.backtrace;
+ // This shouldn't be necessary but ensure that code doesn't get mixed
+ // It does however prevent the unittest SEGV handler to be installed,
+ // which is desireable as it uses backtrace directly.
+ private enum hasExecinfo = false;
+}
+else version (DRuntime_Use_Libunwind)
+{
+ import core.internal.backtrace.libunwind;
+ // This shouldn't be necessary but ensure that code doesn't get mixed
+ // It does however prevent the unittest SEGV handler to be installed,
+ // which is desireable as it uses backtrace directly.
+ private enum hasExecinfo = false;
+}
+else
+ import core.internal.execinfo;
+
/// C interface for Runtime.loadLibrary
extern (C) void* rt_loadLibrary(const char* name);
/// ditto
-version (Windows) extern (C) void* rt_loadLibraryW(const wchar_t* name);
+version (Windows) extern (C) void* rt_loadLibraryW(const wchar* name);
+
/// C interface for Runtime.unloadLibrary, returns 1/0 instead of bool
extern (C) int rt_unloadLibrary(void* ptr);
@@ -41,29 +54,69 @@ extern(C) int rt_init();
/// C interface for Runtime.terminate, returns 1/0 instead of bool
extern(C) int rt_term();
+/**
+ * This type is returned by the module unit test handler to indicate testing
+ * results.
+ */
+struct UnitTestResult
+{
+ /**
+ * Number of modules which were tested
+ */
+ size_t executed;
+
+ /**
+ * Number of modules passed the unittests
+ */
+ size_t passed;
+
+ /**
+ * Should the main function be run or not? This is ignored if any tests
+ * failed.
+ */
+ bool runMain;
+
+ /**
+ * Should we print a summary of the results?
+ */
+ bool summarize;
+
+ /**
+ * Simple check for whether execution should continue after unit tests
+ * have been run. Works with legacy code that expected a bool return.
+ *
+ * Returns:
+ * true if execution should continue after testing is complete, false if
+ * not.
+ */
+ bool opCast(T : bool)() const
+ {
+ return runMain && (executed == passed);
+ }
+
+ /// Simple return code that says unit tests pass, and main should be run
+ enum UnitTestResult pass = UnitTestResult(0, 0, true, false);
+ /// Simple return code that says unit tests failed.
+ enum UnitTestResult fail = UnitTestResult(1, 0, false, false);
+}
+
+/// Legacy module unit test handler
+alias bool function() ModuleUnitTester;
+/// Module unit test handler
+alias UnitTestResult function() ExtendedModuleUnitTester;
private
{
- alias bool function() ModuleUnitTester;
alias bool function(Object) CollectHandler;
alias Throwable.TraceInfo function( void* ptr ) TraceHandler;
- extern (C) void rt_setCollectHandler( CollectHandler h );
- extern (C) CollectHandler rt_getCollectHandler();
-
- extern (C) void rt_setTraceHandler( TraceHandler h );
- extern (C) TraceHandler rt_getTraceHandler();
-
alias void delegate( Throwable ) ExceptionHandler;
extern (C) void _d_print_throwable(Throwable t);
extern (C) void* thread_stackBottom();
-
- extern (C) string[] rt_args();
- extern (C) CArgs rt_cArgs() @nogc;
}
-static this()
+shared static this()
{
// NOTE: Some module ctors will run before this handler is set, so it's
// still possible the app could exit without a stack trace. If
@@ -108,13 +161,6 @@ struct Runtime
return !!rt_init();
}
- deprecated("Please use the overload of Runtime.initialize that takes no argument.")
- static bool initialize(ExceptionHandler dg = null)
- {
- return !!rt_init();
- }
-
-
/**
* Terminates the runtime. This call is to be used in instances where the
* standard program termination process will not be not executed. This is
@@ -129,23 +175,13 @@ struct Runtime
return !!rt_term();
}
- deprecated("Please use the overload of Runtime.terminate that takes no argument.")
- static bool terminate(ExceptionHandler dg = null)
- {
- return !!rt_term();
- }
-
-
/**
* Returns the arguments supplied when the process was started.
*
* Returns:
* The arguments supplied when this process was started.
*/
- static @property string[] args()
- {
- return rt_args();
- }
+ extern(C) pragma(mangle, "rt_args") static @property string[] args();
/**
* Returns the unprocessed C arguments supplied when the process was started.
@@ -168,10 +204,7 @@ struct Runtime
* }
* ---
*/
- static @property CArgs cArgs() @nogc
- {
- return rt_cArgs();
- }
+ extern(C) pragma(mangle, "rt_cArgs") static @property CArgs cArgs() @nogc;
/**
* Locates a dynamic library with the supplied library name and dynamically
@@ -184,12 +217,13 @@ struct Runtime
* Returns:
* A reference to the library or null on error.
*/
- static void* loadLibrary()(in char[] name)
+ static void* loadLibrary()(const scope char[] name)
{
import core.stdc.stdlib : free, malloc;
version (Windows)
{
- import core.sys.windows.windows;
+ import core.sys.windows.winnls : CP_UTF8, MultiByteToWideChar;
+ import core.sys.windows.winnt : WCHAR;
if (name.length == 0) return null;
// Load a DLL at runtime
@@ -198,7 +232,7 @@ struct Runtime
if (len == 0)
return null;
- auto buf = cast(wchar_t*)malloc((len+1) * wchar_t.sizeof);
+ auto buf = cast(WCHAR*)malloc((len+1) * WCHAR.sizeof);
if (buf is null) return null;
scope (exit) free(buf);
@@ -253,10 +287,7 @@ struct Runtime
* Params:
* h = The new trace handler. Set to null to use the default handler.
*/
- static @property void traceHandler( TraceHandler h )
- {
- rt_setTraceHandler( h );
- }
+ extern(C) pragma(mangle, "rt_setTraceHandler") static @property void traceHandler(TraceHandler h);
/**
* Gets the current trace handler.
@@ -264,10 +295,7 @@ struct Runtime
* Returns:
* The current trace handler or null if none has been set.
*/
- static @property TraceHandler traceHandler()
- {
- return rt_getTraceHandler();
- }
+ extern(C) pragma(mangle, "rt_getTraceHandler") static @property TraceHandler traceHandler();
/**
* Overrides the default collect hander with a user-supplied version. This
@@ -280,10 +308,7 @@ struct Runtime
* Params:
* h = The new collect handler. Set to null to use the default handler.
*/
- static @property void collectHandler( CollectHandler h )
- {
- rt_setCollectHandler( h );
- }
+ extern(C) pragma(mangle, "rt_setCollectHandler") static @property void collectHandler( CollectHandler h );
/**
@@ -292,10 +317,7 @@ struct Runtime
* Returns:
* The current collect handler or null if none has been set.
*/
- static @property CollectHandler collectHandler()
- {
- return rt_getCollectHandler();
- }
+ extern(C) pragma(mangle, "rt_getCollectHandler") static @property CollectHandler collectHandler();
/**
@@ -304,26 +326,39 @@ struct Runtime
* value of this routine indicates to the runtime whether the tests ran
* without error.
*
+ * There are two options for handlers. The `bool` version is deprecated but
+ * will be kept for legacy support. Returning `true` from the handler is
+ * equivalent to returning `UnitTestResult.pass` from the extended version.
+ * Returning `false` from the handler is equivalent to returning
+ * `UnitTestResult.fail` from the extended version.
+ *
+ * See the documentation for `UnitTestResult` to see how you should set up
+ * the return structure.
+ *
+ * See the documentation for `runModuleUnitTests` for how the default
+ * algorithm works, or read the example below.
+ *
* Params:
- * h = The new unit tester. Set to null to use the default unit tester.
+ * h = The new unit tester. Set both to null to use the default unit
+ * tester.
*
* Example:
* ---------
- * version (unittest) shared static this()
+ * shared static this()
* {
* import core.runtime;
*
- * Runtime.moduleUnitTester = &customModuleUnitTester;
+ * Runtime.extendedModuleUnitTester = &customModuleUnitTester;
* }
*
- * bool customModuleUnitTester()
+ * UnitTestResult customModuleUnitTester()
* {
* import std.stdio;
*
* writeln("Using customModuleUnitTester");
*
* // Do the same thing as the default moduleUnitTester:
- * size_t failed = 0;
+ * UnitTestResult result;
* foreach (m; ModuleInfo)
* {
* if (m)
@@ -332,45 +367,82 @@ struct Runtime
*
* if (fp)
* {
+ * ++result.executed;
* try
* {
* fp();
+ * ++result.passed;
* }
* catch (Throwable e)
* {
* writeln(e);
- * failed++;
* }
* }
* }
* }
- * return failed == 0;
+ * if (result.executed != result.passed)
+ * {
+ * result.runMain = false; // don't run main
+ * result.summarize = true; // print failure
+ * }
+ * else
+ * {
+ * result.runMain = true; // all UT passed
+ * result.summarize = false; // be quiet about it.
+ * }
+ * return result;
* }
* ---------
*/
+ static @property void extendedModuleUnitTester( ExtendedModuleUnitTester h )
+ {
+ sm_extModuleUnitTester = h;
+ }
+
+ /// Ditto
static @property void moduleUnitTester( ModuleUnitTester h )
{
sm_moduleUnitTester = h;
}
-
/**
- * Gets the current module unit tester.
+ * Gets the current legacy module unit tester.
+ *
+ * This property should not be used, but is supported for legacy purposes.
+ *
+ * Note that if the extended unit test handler is set, this handler will
+ * be ignored.
*
* Returns:
- * The current module unit tester handler or null if none has been set.
+ * The current legacy module unit tester handler or null if none has been
+ * set.
*/
static @property ModuleUnitTester moduleUnitTester()
{
return sm_moduleUnitTester;
}
+ /**
+ * Gets the current module unit tester.
+ *
+ * This handler overrides any legacy module unit tester set by the
+ * moduleUnitTester property.
+ *
+ * Returns:
+ * The current module unit tester handler or null if none has been
+ * set.
+ */
+ static @property ExtendedModuleUnitTester extendedModuleUnitTester()
+ {
+ return sm_extModuleUnitTester;
+ }
private:
// NOTE: This field will only ever be set in a static ctor and should
// never occur within any but the main thread, so it is safe to
// make it __gshared.
+ __gshared ExtendedModuleUnitTester sm_extModuleUnitTester = null;
__gshared ModuleUnitTester sm_moduleUnitTester = null;
}
@@ -444,40 +516,52 @@ extern (C) void profilegc_setlogfilename(string name);
/**
* This routine is called by the runtime to run module unit tests on startup.
- * The user-supplied unit tester will be called if one has been supplied,
+ * The user-supplied unit tester will be called if one has been set,
* otherwise all unit tests will be run in sequence.
*
+ * If the extended unittest handler is registered, this function returns the
+ * result from that handler directly.
+ *
+ * If a legacy boolean returning custom handler is used, `false` maps to
+ * `UnitTestResult.fail`, and `true` maps to `UnitTestResult.pass`. This was
+ * the original behavior of the unit testing system.
+ *
+ * If no unittest custom handlers are registered, the following algorithm is
+ * executed (the behavior can be affected by the `--DRT-testmode` switch
+ * below):
+ * 1. Execute any unittests present. For each that fails, print the stack
+ * trace and continue.
+ * 2. If no unittests were present, set summarize to false, and runMain to
+ * true.
+ * 3. Otherwise, set summarize to true, and runMain to false.
+ *
+ * See the documentation for `UnitTestResult` for details on how the runtime
+ * treats the return value from this function.
+ *
+ * If the switch `--DRT-testmode` is passed to the executable, it can have
+ * one of 3 values:
+ * 1. "run-main": even if unit tests are run (and all pass), runMain is set
+ to true.
+ * 2. "test-or-main": any unit tests present will cause the program to
+ * summarize the results and exit regardless of the result. This is the
+ * default.
+ * 3. "test-only", runMain is set to false, even with no tests present.
+ *
+ * This command-line parameter does not affect custom unit test handlers.
+ *
* Returns:
- * true if execution should continue after testing is complete and false if
- * not. Default behavior is to return true.
+ * A `UnitTestResult` struct indicating the result of running unit tests.
*/
-extern (C) bool runModuleUnitTests()
+extern (C) UnitTestResult runModuleUnitTests()
{
- // backtrace
- version (GNU)
- import gcc.backtrace;
- version (CRuntime_Glibc)
- import core.sys.linux.execinfo;
- else version (Darwin)
- import core.sys.darwin.execinfo;
- else version (FreeBSD)
- import core.sys.freebsd.execinfo;
- else version (NetBSD)
- import core.sys.netbsd.execinfo;
- else version (DragonFlyBSD)
- import core.sys.dragonflybsd.execinfo;
- else version (Windows)
+ version (Windows)
import core.sys.windows.stacktrace;
- else version (Solaris)
- import core.sys.solaris.execinfo;
- else version (CRuntime_UClibc)
- import core.sys.linux.execinfo;
- static if ( __traits( compiles, new LibBacktrace(0) ) )
+ static if (__traits(compiles, new LibBacktrace(0)))
{
import core.sys.posix.signal; // segv handler
- static extern (C) void unittestSegvHandler( int signum, siginfo_t* info, void* ptr )
+ static extern (C) void unittestSegvHandler(int signum, siginfo_t* info, void* ptr)
{
import core.stdc.stdio;
fprintf(stderr, "Segmentation fault while running unittests:\n");
@@ -496,18 +580,18 @@ extern (C) bool runModuleUnitTests()
sigaction_t oldbus = void;
(cast(byte*) &action)[0 .. action.sizeof] = 0;
- sigfillset( &action.sa_mask ); // block other signals
+ sigfillset(&action.sa_mask); // block other signals
action.sa_flags = SA_SIGINFO | SA_RESETHAND;
action.sa_sigaction = &unittestSegvHandler;
- sigaction( SIGSEGV, &action, &oldseg );
- sigaction( SIGBUS, &action, &oldbus );
- scope( exit )
+ sigaction(SIGSEGV, &action, &oldseg);
+ sigaction(SIGBUS, &action, &oldbus);
+ scope (exit)
{
- sigaction( SIGSEGV, &oldseg, null );
- sigaction( SIGBUS, &oldbus, null );
+ sigaction(SIGSEGV, &oldseg, null);
+ sigaction(SIGBUS, &oldbus, null);
}
}
- else static if ( __traits( compiles, backtrace ) )
+ else static if (hasExecinfo)
{
import core.sys.posix.signal; // segv handler
@@ -537,385 +621,311 @@ extern (C) bool runModuleUnitTests()
}
}
- if ( Runtime.sm_moduleUnitTester is null )
+ if (Runtime.sm_extModuleUnitTester !is null)
+ return Runtime.sm_extModuleUnitTester();
+ else if (Runtime.sm_moduleUnitTester !is null)
+ return Runtime.sm_moduleUnitTester() ? UnitTestResult.pass : UnitTestResult.fail;
+ UnitTestResult results;
+ foreach ( m; ModuleInfo )
{
- size_t failed = 0;
- foreach ( m; ModuleInfo )
+ if ( !m )
+ continue;
+ auto fp = m.unitTest;
+ if ( !fp )
+ continue;
+
+ import core.exception;
+ ++results.executed;
+ try
{
- if ( m )
+ fp();
+ ++results.passed;
+ }
+ catch ( Throwable e )
+ {
+ if ( typeid(e) == typeid(AssertError) )
{
- auto fp = m.unitTest;
-
- if ( fp )
+ // Crude heuristic to figure whether the assertion originates in
+ // the unittested module. TODO: improve.
+ auto moduleName = m.name;
+ if (moduleName.length && e.file.length > moduleName.length
+ && e.file[0 .. moduleName.length] == moduleName)
{
- try
- {
- fp();
- }
- catch ( Throwable e )
- {
- _d_print_throwable(e);
- failed++;
- }
+ import core.stdc.stdio;
+ printf("%.*s(%llu): [unittest] %.*s\n",
+ cast(int) e.file.length, e.file.ptr, cast(ulong) e.line,
+ cast(int) e.message.length, e.message.ptr);
+
+ // Exception originates in the same module, don't print
+ // the stack trace.
+ // TODO: omit stack trace only if assert was thrown
+ // directly by the unittest.
+ continue;
}
}
+ // TODO: perhaps indent all of this stuff.
+ _d_print_throwable(e);
}
- return failed == 0;
}
- return Runtime.sm_moduleUnitTester();
-}
+ import core.internal.parseoptions : rt_configOption;
-///////////////////////////////////////////////////////////////////////////////
-// Default Implementations
-///////////////////////////////////////////////////////////////////////////////
+ if (results.passed != results.executed)
+ {
+ // by default, we always print a summary if there are failures.
+ results.summarize = true;
+ }
+ else switch (rt_configOption("testmode", null, false))
+ {
+ case "run-main":
+ results.runMain = true;
+ break;
+ case "test-only":
+ // Never run main, always summarize
+ results.summarize = true;
+ break;
+ case "":
+ // By default, do not run main if tests are present.
+ case "test-or-main":
+ // only run main if there were no tests. Only summarize if we are not
+ // running main.
+ results.runMain = (results.executed == 0);
+ results.summarize = !results.runMain;
+ break;
+ default:
+ assert(0, "Unknown --DRT-testmode option: " ~ rt_configOption("testmode", null, false));
+ }
+ return results;
+}
/**
+ * Get the default `Throwable.TraceInfo` implementation for the platform
+ *
+ * This functions returns a trace handler, allowing to inspect the
+ * current stack trace.
+ *
+ * Params:
+ * ptr = (Windows only) The context to get the stack trace from.
+ * When `null` (the default), start from the current frame.
*
+ * Returns:
+ * A `Throwable.TraceInfo` implementation suitable to iterate over the stack,
+ * or `null`. If called from a finalizer (destructor), always returns `null`
+ * as trace handlers allocate.
*/
Throwable.TraceInfo defaultTraceHandler( void* ptr = null )
{
- // backtrace
- version (GNU)
- import gcc.backtrace;
- version (CRuntime_Glibc)
- import core.sys.linux.execinfo;
- else version (Darwin)
- import core.sys.darwin.execinfo;
- else version (FreeBSD)
- import core.sys.freebsd.execinfo;
- else version (NetBSD)
- import core.sys.netbsd.execinfo;
- else version (DragonFlyBSD)
- import core.sys.dragonflybsd.execinfo;
- else version (Windows)
- import core.sys.windows.stacktrace;
- else version (Solaris)
- import core.sys.solaris.execinfo;
- else version (CRuntime_UClibc)
- import core.sys.linux.execinfo;
-
// avoid recursive GC calls in finalizer, trace handlers should be made @nogc instead
- import core.memory : gc_inFinalizer;
- if (gc_inFinalizer)
+ import core.memory : GC;
+ if (GC.inFinalizer)
return null;
- //printf("runtime.defaultTraceHandler()\n");
- static if ( __traits( compiles, new LibBacktrace(0) ) )
+ static if (__traits(compiles, new LibBacktrace(0)))
{
version (Posix)
- {
static enum FIRSTFRAME = 4;
- }
else version (Win64)
- {
static enum FIRSTFRAME = 4;
- }
else
- {
static enum FIRSTFRAME = 0;
- }
return new LibBacktrace(FIRSTFRAME);
}
- else static if ( __traits( compiles, new UnwindBacktrace(0) ) )
+ else static if (__traits(compiles, new UnwindBacktrace(0)))
{
version (Posix)
- {
static enum FIRSTFRAME = 5;
- }
else version (Win64)
- {
static enum FIRSTFRAME = 4;
- }
else
- {
static enum FIRSTFRAME = 0;
- }
return new UnwindBacktrace(FIRSTFRAME);
}
- else static if ( __traits( compiles, backtrace ) )
+ else version (Windows)
{
- import core.demangle;
- import core.stdc.stdlib : free;
- import core.stdc.string : strlen, memchr, memmove;
-
- class DefaultTraceInfo : Throwable.TraceInfo
+ import core.sys.windows.stacktrace;
+ static if (__traits(compiles, new StackTrace(0, null)))
{
- this()
- {
- numframes = 0; //backtrace( callstack, MAXFRAMES );
- if (numframes < 2) // backtrace() failed, do it ourselves
- {
- static void** getBasePtr()
- {
- version (D_InlineAsm_X86)
- asm { naked; mov EAX, EBP; ret; }
- else
- version (D_InlineAsm_X86_64)
- asm { naked; mov RAX, RBP; ret; }
- else
- return null;
- }
-
- auto stackTop = getBasePtr();
- auto stackBottom = cast(void**) thread_stackBottom();
- void* dummy;
-
- if ( stackTop && &dummy < stackTop && stackTop < stackBottom )
- {
- auto stackPtr = stackTop;
-
- for ( numframes = 0; stackTop <= stackPtr &&
- stackPtr < stackBottom &&
- numframes < MAXFRAMES; )
- {
- enum CALL_INSTRUCTION_SIZE = 1; // it may not be 1 but it is good enough to get
- // in CALL instruction address range for backtrace
- callstack[numframes++] = *(stackPtr + 1) - CALL_INSTRUCTION_SIZE;
- stackPtr = cast(void**) *stackPtr;
- }
- }
- }
- }
-
- override int opApply( scope int delegate(ref const(char[])) dg ) const
- {
- return opApply( (ref size_t, ref const(char[]) buf)
- {
- return dg( buf );
- } );
- }
+ import core.sys.windows.winnt : CONTEXT;
+ version (Win64)
+ enum FIRSTFRAME = 4;
+ else version (Win32)
+ enum FIRSTFRAME = 0;
+ return new StackTrace(FIRSTFRAME, cast(CONTEXT*)ptr);
+ }
+ else
+ return null;
+ }
+ else static if (__traits(compiles, new DefaultTraceInfo()))
+ return new DefaultTraceInfo();
+ else
+ return null;
+}
- override int opApply( scope int delegate(ref size_t, ref const(char[])) dg ) const
- {
- version (Posix)
- {
- // NOTE: The first 4 frames with the current implementation are
- // inside core.runtime and the object code, so eliminate
- // these for readability. The alternative would be to
- // exclude the first N frames that are in a list of
- // mangled function names.
- enum FIRSTFRAME = 4;
- }
- else version (Windows)
- {
- // NOTE: On Windows, the number of frames to exclude is based on
- // whether the exception is user or system-generated, so
- // it may be necessary to exclude a list of function names
- // instead.
- enum FIRSTFRAME = 0;
- }
+/// Example of a simple program printing its stack trace
+unittest
+{
+ import core.runtime;
+ import core.stdc.stdio;
- version (linux) enum enableDwarf = true;
- else version (FreeBSD) enum enableDwarf = true;
- else version (DragonFlyBSD) enum enableDwarf = true;
- else version (Darwin) enum enableDwarf = true;
- else enum enableDwarf = false;
+ void main()
+ {
+ auto trace = defaultTraceHandler(null);
+ foreach (line; trace)
+ {
+ printf("%.*s\n", cast(int)line.length, line.ptr);
+ }
+ }
+}
- static if (enableDwarf)
- {
- import core.internal.traits : externDFunc;
-
- alias traceHandlerOpApplyImpl = externDFunc!(
- "rt.backtrace.dwarf.traceHandlerOpApplyImpl",
- int function(const void*[], scope int delegate(ref size_t, ref const(char[])))
- );
-
- if (numframes >= FIRSTFRAME)
- {
- return traceHandlerOpApplyImpl(
- callstack[FIRSTFRAME .. numframes],
- dg
- );
- }
- else
- {
- return 0;
- }
- }
- else
- {
- const framelist = backtrace_symbols( callstack.ptr, numframes );
- scope(exit) free(cast(void*) framelist);
-
- int ret = 0;
- for ( int i = FIRSTFRAME; i < numframes; ++i )
- {
- char[4096] fixbuf;
- auto buf = framelist[i][0 .. strlen(framelist[i])];
- auto pos = cast(size_t)(i - FIRSTFRAME);
- buf = fixline( buf, fixbuf );
- ret = dg( pos, buf );
- if ( ret )
- break;
- }
- return ret;
- }
+version (DRuntime_Use_Libunwind)
+{
+ import core.internal.backtrace.handler;
- }
+ alias DefaultTraceInfo = LibunwindHandler;
+}
+/// Default implementation for most POSIX systems
+else static if (hasExecinfo) private class DefaultTraceInfo : Throwable.TraceInfo
+{
+ import core.demangle;
+ import core.stdc.stdlib : free;
+ import core.stdc.string : strlen, memchr, memmove;
- override string toString() const
+ this()
+ {
+ // it may not be 1 but it is good enough to get
+ // in CALL instruction address range for backtrace
+ enum CALL_INSTRUCTION_SIZE = 1;
+
+ static if (__traits(compiles, backtrace((void**).init, int.init)))
+ numframes = backtrace(this.callstack.ptr, MAXFRAMES);
+ // Backtrace succeeded, adjust the frame to point to the caller
+ if (numframes >= 2)
+ foreach (ref elem; this.callstack)
+ elem -= CALL_INSTRUCTION_SIZE;
+ else // backtrace() failed, do it ourselves
+ {
+ static void** getBasePtr()
{
- string buf;
- foreach ( i, line; this )
- buf ~= i ? "\n" ~ line : line;
- return buf;
+ version (D_InlineAsm_X86)
+ asm { naked; mov EAX, EBP; ret; }
+ else
+ version (D_InlineAsm_X86_64)
+ asm { naked; mov RAX, RBP; ret; }
+ else
+ return null;
}
- private:
- int numframes;
- static enum MAXFRAMES = 128;
- void*[MAXFRAMES] callstack = void;
+ auto stackTop = getBasePtr();
+ auto stackBottom = cast(void**) thread_stackBottom();
+ void* dummy;
- private:
- const(char)[] fixline( const(char)[] buf, return ref char[4096] fixbuf ) const
+ if ( stackTop && &dummy < stackTop && stackTop < stackBottom )
{
- size_t symBeg, symEnd;
- version (Darwin)
- {
- // format is:
- // 1 module 0x00000000 D6module4funcAFZv + 0
- for ( size_t i = 0, n = 0; i < buf.length; i++ )
- {
- if ( ' ' == buf[i] )
- {
- n++;
- while ( i < buf.length && ' ' == buf[i] )
- i++;
- if ( 3 > n )
- continue;
- symBeg = i;
- while ( i < buf.length && ' ' != buf[i] )
- i++;
- symEnd = i;
- break;
- }
- }
- }
- else version (CRuntime_Glibc)
- {
- // format is: module(_D6module4funcAFZv) [0x00000000]
- // or: module(_D6module4funcAFZv+0x78) [0x00000000]
- auto bptr = cast(char*) memchr( buf.ptr, '(', buf.length );
- auto eptr = cast(char*) memchr( buf.ptr, ')', buf.length );
- auto pptr = cast(char*) memchr( buf.ptr, '+', buf.length );
-
- if (pptr && pptr < eptr)
- eptr = pptr;
-
- if ( bptr++ && eptr )
- {
- symBeg = bptr - buf.ptr;
- symEnd = eptr - buf.ptr;
- }
- }
- else version (FreeBSD)
- {
- // format is: 0x00000000 <_D6module4funcAFZv+0x78> at module
- auto bptr = cast(char*) memchr( buf.ptr, '<', buf.length );
- auto eptr = cast(char*) memchr( buf.ptr, '+', buf.length );
-
- if ( bptr++ && eptr )
- {
- symBeg = bptr - buf.ptr;
- symEnd = eptr - buf.ptr;
- }
- }
- else version (NetBSD)
- {
- // format is: 0x00000000 <_D6module4funcAFZv+0x78> at module
- auto bptr = cast(char*) memchr( buf.ptr, '<', buf.length );
- auto eptr = cast(char*) memchr( buf.ptr, '+', buf.length );
-
- if ( bptr++ && eptr )
- {
- symBeg = bptr - buf.ptr;
- symEnd = eptr - buf.ptr;
- }
- }
- else version (DragonFlyBSD)
- {
- // format is: 0x00000000 <_D6module4funcAFZv+0x78> at module
- auto bptr = cast(char*) memchr( buf.ptr, '<', buf.length );
- auto eptr = cast(char*) memchr( buf.ptr, '+', buf.length );
-
- if ( bptr++ && eptr )
- {
- symBeg = bptr - buf.ptr;
- symEnd = eptr - buf.ptr;
- }
- }
- else version (Solaris)
- {
- // format is object'symbol+offset [pc]
- auto bptr = cast(char*) memchr( buf.ptr, '\'', buf.length );
- auto eptr = cast(char*) memchr( buf.ptr, '+', buf.length );
-
- if ( bptr++ && eptr )
- {
- symBeg = bptr - buf.ptr;
- symEnd = eptr - buf.ptr;
- }
- }
- else
- {
- // fallthrough
- }
+ auto stackPtr = stackTop;
- assert(symBeg < buf.length && symEnd < buf.length);
- assert(symBeg <= symEnd);
-
- enum min = (size_t a, size_t b) => a <= b ? a : b;
- if (symBeg == symEnd || symBeg >= fixbuf.length)
+ for ( numframes = 0; stackTop <= stackPtr &&
+ stackPtr < stackBottom &&
+ numframes < MAXFRAMES; )
{
- immutable len = min(buf.length, fixbuf.length);
- fixbuf[0 .. len] = buf[0 .. len];
- return fixbuf[0 .. len];
- }
- else
- {
- fixbuf[0 .. symBeg] = buf[0 .. symBeg];
-
- auto sym = demangle(buf[symBeg .. symEnd], fixbuf[symBeg .. $]);
-
- if (sym.ptr !is fixbuf.ptr + symBeg)
- {
- // demangle reallocated the buffer, copy the symbol to fixbuf
- immutable len = min(fixbuf.length - symBeg, sym.length);
- memmove(fixbuf.ptr + symBeg, sym.ptr, len);
- if (symBeg + len == fixbuf.length)
- return fixbuf[];
- }
-
- immutable pos = symBeg + sym.length;
- assert(pos < fixbuf.length);
- immutable tail = buf.length - symEnd;
- immutable len = min(fixbuf.length - pos, tail);
- fixbuf[pos .. pos + len] = buf[symEnd .. symEnd + len];
- return fixbuf[0 .. pos + len];
+ callstack[numframes++] = *(stackPtr + 1) - CALL_INSTRUCTION_SIZE;
+ stackPtr = cast(void**) *stackPtr;
}
}
}
+ }
- return new DefaultTraceInfo;
+ override int opApply( scope int delegate(ref const(char[])) dg ) const
+ {
+ return opApply( (ref size_t, ref const(char[]) buf)
+ {
+ return dg( buf );
+ } );
}
- else static if ( __traits( compiles, new StackTrace(0, null) ) )
+
+ override int opApply( scope int delegate(ref size_t, ref const(char[])) dg ) const
{
- version (Win64)
+ version (linux) enum enableDwarf = true;
+ else version (FreeBSD) enum enableDwarf = true;
+ else version (DragonFlyBSD) enum enableDwarf = true;
+ else version (Darwin) enum enableDwarf = true;
+ else enum enableDwarf = false;
+
+ const framelist = backtrace_symbols( callstack.ptr, numframes );
+ scope(exit) free(cast(void*) framelist);
+
+ static if (enableDwarf)
{
- static enum FIRSTFRAME = 4;
+ import core.internal.backtrace.dwarf;
+ return traceHandlerOpApplyImpl(numframes,
+ i => callstack[i],
+ (i) { auto str = framelist[i][0 .. strlen(framelist[i])]; return getMangledSymbolName(str); },
+ dg);
}
- else version (Win32)
+ else
{
- static enum FIRSTFRAME = 0;
+ int ret = 0;
+ for (size_t pos = 0; pos < numframes; ++pos)
+ {
+ char[4096] fixbuf = void;
+ auto buf = framelist[pos][0 .. strlen(framelist[pos])];
+ buf = fixline( buf, fixbuf );
+ ret = dg( pos, buf );
+ if ( ret )
+ break;
+ }
+ return ret;
}
- import core.sys.windows.winnt : CONTEXT;
- auto s = new StackTrace(FIRSTFRAME, cast(CONTEXT*)ptr);
- return s;
}
- else
+
+ override string toString() const
{
- return null;
+ string buf;
+ foreach ( i, line; this )
+ buf ~= i ? "\n" ~ line : line;
+ return buf;
+ }
+
+private:
+ int numframes;
+ static enum MAXFRAMES = 128;
+ void*[MAXFRAMES] callstack = void;
+
+private:
+ const(char)[] fixline( const(char)[] buf, return ref char[4096] fixbuf ) const
+ {
+ size_t symBeg, symEnd;
+
+ getMangledSymbolName(buf, symBeg, symEnd);
+
+ enum min = (size_t a, size_t b) => a <= b ? a : b;
+ if (symBeg == symEnd || symBeg >= fixbuf.length)
+ {
+ immutable len = min(buf.length, fixbuf.length);
+ fixbuf[0 .. len] = buf[0 .. len];
+ return fixbuf[0 .. len];
+ }
+ else
+ {
+ fixbuf[0 .. symBeg] = buf[0 .. symBeg];
+
+ auto sym = demangle(buf[symBeg .. symEnd], fixbuf[symBeg .. $]);
+
+ if (sym.ptr !is fixbuf.ptr + symBeg)
+ {
+ // demangle reallocated the buffer, copy the symbol to fixbuf
+ immutable len = min(fixbuf.length - symBeg, sym.length);
+ memmove(fixbuf.ptr + symBeg, sym.ptr, len);
+ if (symBeg + len == fixbuf.length)
+ return fixbuf[];
+ }
+
+ immutable pos = symBeg + sym.length;
+ assert(pos < fixbuf.length);
+ immutable tail = buf.length - symEnd;
+ immutable len = min(fixbuf.length - pos, tail);
+ fixbuf[pos .. pos + len] = buf[symEnd .. symEnd + len];
+ return fixbuf[0 .. pos + len];
+ }
}
}
diff --git a/libphobos/libdruntime/core/stdc/math.d b/libphobos/libdruntime/core/stdc/math.d
index 2de6e579575..de029c41af8 100644
--- a/libphobos/libdruntime/core/stdc/math.d
+++ b/libphobos/libdruntime/core/stdc/math.d
@@ -315,17 +315,17 @@ version (CRuntime_DigitalMars)
pure uint __fpclassify_d(double x);
pure uint __fpclassify_ld(real x);
- extern (D)
- {
//int fpclassify(real-floating x);
///
- extern(C) pragma(mangle, "__fpclassify_f") pure int fpclassify(float x);
+ pragma(mangle, "__fpclassify_f") pure int fpclassify(float x);
///
- extern(C) pragma(mangle, "__fpclassify_d") pure int fpclassify(double x);
+ pragma(mangle, "__fpclassify_d") pure int fpclassify(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify_d" : "__fpclassify_ld")
+ pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify_d" : "__fpclassify_ld")
pure int fpclassify(real x);
+ extern (D)
+ {
//int isfinite(real-floating x);
///
pure int isfinite(float x) { return fpclassify(x) >= FP_NORMAL; }
@@ -452,17 +452,17 @@ else version (CRuntime_Microsoft) // fully supported since MSVCRT 12 (VS 2013) o
pure int __signbit(double x);
pure int __signbitl(real x);
+ //int fpclassify(real-floating x);
+ ///
+ pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
+ ///
+ pragma(mangle, "__fpclassify") pure int fpclassify(double x);
+ ///
+ pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify" : "__fpclassifyl")
+ pure int fpclassify(real x);
+
extern (D)
{
- //int fpclassify(real-floating x);
- ///
- extern(C) pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
- ///
- extern(C) pragma(mangle, "__fpclassify") pure int fpclassify(double x);
- ///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify" : "__fpclassifyl")
- pure int fpclassify(real x);
-
//int isfinite(real-floating x);
///
pure int isfinite(float x) { return (fpclassify(x) & FP_NORMAL) == 0; }
@@ -478,16 +478,19 @@ else version (CRuntime_Microsoft) // fully supported since MSVCRT 12 (VS 2013) o
pure int isinf(double x) { return fpclassify(x) == FP_INFINITE; }
///
pure int isinf(real x) { return fpclassify(x) == FP_INFINITE; }
+ }
- //int isnan(real-floating x);
- ///
- extern(C) pragma(mangle, "__isnanf") pure int isnan(float x);
- ///
- extern(C) pragma(mangle, "__isnan") pure int isnan(double x);
- ///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__isnan" : "__isnanl")
- pure int isnan(real x);
+ //int isnan(real-floating x);
+ ///
+ pragma(mangle, "__isnanf") pure int isnan(float x);
+ ///
+ pragma(mangle, "__isnan") pure int isnan(double x);
+ ///
+ pragma(mangle, real.sizeof == double.sizeof ? "__isnan" : "__isnanl")
+ pure int isnan(real x);
+ extern (D)
+ {
//int isnormal(real-floating x);
///
int isnormal(float x) { return fpclassify(x) == FP_NORMAL; }
@@ -495,16 +498,16 @@ else version (CRuntime_Microsoft) // fully supported since MSVCRT 12 (VS 2013) o
int isnormal(double x) { return fpclassify(x) == FP_NORMAL; }
///
int isnormal(real x) { return fpclassify(x) == FP_NORMAL; }
-
- //int signbit(real-floating x);
- ///
- extern(C) pragma(mangle, "__signbitf") pure int signbit(float x);
- ///
- extern(C) pragma(mangle, "__signbit") pure int signbit(double x);
- ///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__signbit" : "__signbitl")
- int signbit(real x);
}
+
+ //int signbit(real-floating x);
+ ///
+ pragma(mangle, "__signbitf") pure int signbit(float x);
+ ///
+ pragma(mangle, "__signbit") pure int signbit(double x);
+ ///
+ pragma(mangle, real.sizeof == double.sizeof ? "__signbit" : "__signbitl")
+ int signbit(real x);
}
else
{
@@ -644,61 +647,58 @@ else version (CRuntime_Glibc)
pure int __signbit(double x);
pure int __signbitl(real x);
- extern (D)
- {
//int fpclassify(real-floating x);
///
- extern(C) pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
+ pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
///
- extern(C) pragma(mangle, "__fpclassify") pure int fpclassify(double x);
+ pragma(mangle, "__fpclassify") pure int fpclassify(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify" : "__fpclassifyl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify" : "__fpclassifyl")
pure int fpclassify(real x);
//int isfinite(real-floating x);
///
- extern(C) pragma(mangle, "__finitef") pure int isfinite(float x);
+ pragma(mangle, "__finitef") pure int isfinite(float x);
///
- extern(C) pragma(mangle, "__finite") pure int isfinite(double x);
+ pragma(mangle, "__finite") pure int isfinite(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__finite" : "__finitel")
+ pragma(mangle, real.sizeof == double.sizeof ? "__finite" : "__finitel")
pure int isfinite(real x);
//int isinf(real-floating x);
///
- extern(C) pragma(mangle, "__isinff") pure int isinf(float x);
+ pragma(mangle, "__isinff") pure int isinf(float x);
///
- extern(C) pragma(mangle, "__isinf") pure int isinf(double x);
+ pragma(mangle, "__isinf") pure int isinf(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__isinf" : "__isinfl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__isinf" : "__isinfl")
pure int isinf(real x);
//int isnan(real-floating x);
///
- extern(C) pragma(mangle, "__isnanf") pure int isnan(float x);
+ pragma(mangle, "__isnanf") pure int isnan(float x);
///
- extern(C) pragma(mangle, "__isnan") pure int isnan(double x);
+ pragma(mangle, "__isnan") pure int isnan(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__isnan" : "__isnanl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__isnan" : "__isnanl")
pure int isnan(real x);
//int isnormal(real-floating x);
///
- pure int isnormal(float x) { return fpclassify(x) == FP_NORMAL; }
+ extern (D) pure int isnormal(float x) { return fpclassify(x) == FP_NORMAL; }
///
- pure int isnormal(double x) { return fpclassify(x) == FP_NORMAL; }
+ extern (D) pure int isnormal(double x) { return fpclassify(x) == FP_NORMAL; }
///
- pure int isnormal(real x) { return fpclassify(x) == FP_NORMAL; }
+ extern (D) pure int isnormal(real x) { return fpclassify(x) == FP_NORMAL; }
//int signbit(real-floating x);
///
- extern(C) pragma(mangle, "__signbitf") pure int signbit(float x);
+ pragma(mangle, "__signbitf") pure int signbit(float x);
///
- extern(C) pragma(mangle, "__signbit") pure int signbit(double x);
+ pragma(mangle, "__signbit") pure int signbit(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__signbit" : "__signbitl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__signbit" : "__signbitl")
pure int signbit(real x);
- }
}
else version (CRuntime_Musl)
{
@@ -736,16 +736,16 @@ else version (CRuntime_Musl)
int __signbitl(real x);
}
- extern (D) pure
- {
//int fpclassify(real-floating x);
///
- extern(C) pragma(mangle, "__fpclassifyf") int fpclassify(float x);
+ pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
///
- extern(C) pragma(mangle, "__fpclassify") int fpclassify(double x);
+ pragma(mangle, "__fpclassify") pure int fpclassify(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify" : "__fpclassifyl")
- int fpclassify(real x);
+ pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify" : "__fpclassifyl")
+ pure int fpclassify(real x);
+ extern (D) pure
+ {
private uint __FLOAT_BITS(float __f)
{
union __u_t {
@@ -813,16 +813,16 @@ else version (CRuntime_Musl)
int isnormal(double x) { return fpclassify(x) == FP_NORMAL; }
///
int isnormal(real x) { return fpclassify(x) == FP_NORMAL; }
+ }
//int signbit(real-floating x);
///
- extern(C) pragma(mangle, "__signbitf") int signbit(float x);
+ pragma(mangle, "__signbitf") pure int signbit(float x);
///
- extern(C) pragma(mangle, "__signbit") int signbit(double x);
+ pragma(mangle, "__signbit") pure int signbit(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__signbit" : "__signbitl")
- int signbit(real x);
- }
+ pragma(mangle, real.sizeof == double.sizeof ? "__signbit" : "__signbitl")
+ pure int signbit(real x);
}
else version (CRuntime_UClibc)
{
@@ -870,55 +870,55 @@ else version (CRuntime_UClibc)
int __signbit(double x);
int __signbitl(real x);
- extern (D)
- {
///
- extern(C) pragma(mangle, "__fpclassifyf") int fpclassify(float x);
+ pragma(mangle, "__fpclassifyf") int fpclassify(float x);
///
- extern(C) pragma(mangle, "__fpclassify") int fpclassify(double x);
+ pragma(mangle, "__fpclassify") int fpclassify(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify" : "__fpclassifyl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__fpclassify" : "__fpclassifyl")
int fpclassify(real x);
///
- extern(C) pragma(mangle, "__finitef") int isfinite(float x);
+ pragma(mangle, "__finitef") int isfinite(float x);
///
- extern(C) pragma(mangle, "__finite") int isfinite(double x);
+ pragma(mangle, "__finite") int isfinite(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__finite" : "__finitel")
+ pragma(mangle, real.sizeof == double.sizeof ? "__finite" : "__finitel")
int isfinite(real x);
///
- extern(C) pragma(mangle, "__isinff") int isinf(float x);
+ pragma(mangle, "__isinff") int isinf(float x);
///
- extern(C) pragma(mangle, "__isinf") int isinf(double x);
+ pragma(mangle, "__isinf") int isinf(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__isinf" : "__isinfl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__isinf" : "__isinfl")
int isinf(real x);
///
- extern(C) pragma(mangle, "__isnanf") int isnan(float x);
+ pragma(mangle, "__isnanf") int isnan(float x);
///
- extern(C) pragma(mangle, "__isnan") int isnan(double x);
+ pragma(mangle, "__isnan") int isnan(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__isnan" : "__isnanl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__isnan" : "__isnanl")
int isnan(real x);
+ extern (D)
+ {
///
int isnormal(float x) { return fpclassify(x) == FP_NORMAL; }
///
int isnormal(double x) { return fpclassify(x) == FP_NORMAL; }
///
int isnormal(real x) { return fpclassify(x) == FP_NORMAL; }
+ }
///
- extern(C) pragma(mangle, "__signbitf") int signbit(float x);
+ pragma(mangle, "__signbitf") int signbit(float x);
///
- extern(C) pragma(mangle, "__signbit") int signbit(double x);
+ pragma(mangle, "__signbit") int signbit(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__signbit" : "__signbitl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__signbit" : "__signbitl")
int signbit(real x);
- }
}
else version (Darwin)
{
@@ -999,40 +999,40 @@ else version (Darwin)
pure int __isnanl(real x);
}
- extern (D)
- {
//int fpclassify(real-floating x);
///
- extern(C) pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
+ pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
///
- extern(C) pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
+ pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
///
- extern(C) pragma(mangle, __fpclassifyl.mangleof) pure int fpclassify(real x);
+ pragma(mangle, __fpclassifyl.mangleof) pure int fpclassify(real x);
//int isfinite(real-floating x);
///
- extern(C) pragma(mangle, "__isfinitef") pure int isfinite(float x);
+ pragma(mangle, "__isfinitef") pure int isfinite(float x);
///
- extern(C) pragma(mangle, "__isfinited") pure int isfinite(double x);
+ pragma(mangle, "__isfinited") pure int isfinite(double x);
///
- extern(C) pragma(mangle, __isfinitel.mangleof) pure int isfinite(real x);
+ pragma(mangle, __isfinitel.mangleof) pure int isfinite(real x);
//int isinf(real-floating x);
///
- extern(C) pragma(mangle, "__isinff") pure int isinf(float x);
+ pragma(mangle, "__isinff") pure int isinf(float x);
///
- extern(C) pragma(mangle, "__isinfd") pure int isinf(double x);
+ pragma(mangle, "__isinfd") pure int isinf(double x);
///
- extern(C) pragma(mangle, __isinfl.mangleof) pure int isinf(real x);
+ pragma(mangle, __isinfl.mangleof) pure int isinf(real x);
//int isnan(real-floating x);
///
- extern(C) pragma(mangle, "__isnanf") pure int isnan(float x);
+ pragma(mangle, "__isnanf") pure int isnan(float x);
///
- extern(C) pragma(mangle, "__isnand") pure int isnan(double x);
+ pragma(mangle, "__isnand") pure int isnan(double x);
///
- extern(C) pragma(mangle, __isnanl.mangleof) pure int isnan(real x);
+ pragma(mangle, __isnanl.mangleof) pure int isnan(real x);
+ extern (D)
+ {
//int isnormal(real-floating x);
///
pure int isnormal(float x) { return fpclassify(x) == FP_NORMAL; }
@@ -1040,15 +1040,15 @@ else version (Darwin)
pure int isnormal(double x) { return fpclassify(x) == FP_NORMAL; }
///
pure int isnormal(real x) { return fpclassify(x) == FP_NORMAL; }
+ }
//int signbit(real-floating x);
///
- extern(C) pragma(mangle, "__signbitf") pure int signbit(float x);
+ pragma(mangle, "__signbitf") pure int signbit(float x);
///
- extern(C) pragma(mangle, "__signbitd") pure int signbit(double x);
+ pragma(mangle, "__signbitd") pure int signbit(double x);
///
- extern(C) pragma(mangle, "__signbitl") pure int signbit(real x);
- }
+ pragma(mangle, "__signbitl") pure int signbit(real x);
}
else version (FreeBSD)
{
@@ -1092,56 +1092,53 @@ else version (FreeBSD)
pure int __signbitf(float);
pure int __signbitl(real);
- extern (D)
- {
//int fpclassify(real-floating x);
///
- extern(C) pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
+ pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
///
- extern(C) pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
+ pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
///
- extern(C) pragma(mangle, "__fpclassifyl") pure int fpclassify(real x);
+ pragma(mangle, "__fpclassifyl") pure int fpclassify(real x);
//int isfinite(real-floating x);
///
- extern(C) pragma(mangle, "__isfinitef") pure int isfinite(float x);
+ pragma(mangle, "__isfinitef") pure int isfinite(float x);
///
- extern(C) pragma(mangle, "__isfinite") pure int isfinite(double x);
+ pragma(mangle, "__isfinite") pure int isfinite(double x);
///
- extern(C) pragma(mangle, "__isfinitel") pure int isfinite(real x);
+ pragma(mangle, "__isfinitel") pure int isfinite(real x);
//int isinf(real-floating x);
///
- extern(C) pragma(mangle, "__isinff") pure int isinf(float x);
+ pragma(mangle, "__isinff") pure int isinf(float x);
///
- pure int isinf(double x) { return __isinfl(x); }
+ extern (D) pure int isinf(double x) { return __isinfl(x); }
///
- extern(C) pragma(mangle, "__isinfl") pure int isinf(real x);
+ pragma(mangle, "__isinfl") pure int isinf(real x);
//int isnan(real-floating x);
///
- pure int isnan(float x) { return __isnanl(x); }
+ extern (D) pure int isnan(float x) { return __isnanl(x); }
///
- pure int isnan(double x) { return __isnanl(x); }
+ extern (D) pure int isnan(double x) { return __isnanl(x); }
///
- extern(C) pragma(mangle, "__isnanl") pure int isnan(real x);
+ pragma(mangle, "__isnanl") pure int isnan(real x);
//int isnormal(real-floating x);
///
- extern(C) pragma(mangle, "__isnormalf") pure int isnormal(float x);
+ pragma(mangle, "__isnormalf") pure int isnormal(float x);
///
- extern(C) pragma(mangle, "__isnormal") pure int isnormal(double x);
+ pragma(mangle, "__isnormal") pure int isnormal(double x);
///
- extern(C) pragma(mangle, "__isnormall") pure int isnormal(real x);
+ pragma(mangle, "__isnormall") pure int isnormal(real x);
//int signbit(real-floating x);
///
- extern(C) pragma(mangle, "__signbitf") pure int signbit(float x);
+ pragma(mangle, "__signbitf") pure int signbit(float x);
///
- extern(C) pragma(mangle, "__signbit") pure int signbit(double x);
+ pragma(mangle, "__signbit") pure int signbit(double x);
///
- pure int signbit(real x) { return __signbit(x); }
- }
+ extern (D) pure int signbit(real x) { return __signbit(x); }
}
else version (OpenBSD)
{
@@ -1185,56 +1182,53 @@ else version (OpenBSD)
pure int __signbitf(float);
pure int __signbitl(real);
- extern (D)
- {
//int fpclassify(real-floating x);
///
- extern(C) pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
+ pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
///
- extern(C) pragma(mangle, "__fpclassify") pure int fpclassify(double x);
+ pragma(mangle, "__fpclassify") pure int fpclassify(double x);
///
- extern(C) pragma(mangle, "__fpclassifyl") pure int fpclassify(real x);
+ pragma(mangle, "__fpclassifyl") pure int fpclassify(real x);
//int isfinite(real-floating x);
///
- extern(C) pragma(mangle, "__isfinitef") pure int isfinite(float x);
+ pragma(mangle, "__isfinitef") pure int isfinite(float x);
///
- extern(C) pragma(mangle, "__isfinite") pure int isfinite(double x);
+ pragma(mangle, "__isfinite") pure int isfinite(double x);
///
- extern(C) pragma(mangle, "__isfinitel") pure int isfinite(real x);
+ pragma(mangle, "__isfinitel") pure int isfinite(real x);
//int isinf(real-floating x);
///
- extern(C) pragma(mangle, "__isinff") pure int isinf(float x);
+ pragma(mangle, "__isinff") pure int isinf(float x);
///
- pure int isinf(double x) { return __isinfl(x); }
+ extern (D) pure int isinf(double x) { return __isinfl(x); }
///
- extern(C) pragma(mangle, "__isinfl") pure int isinf(real x);
+ pragma(mangle, "__isinfl") pure int isinf(real x);
//int isnan(real-floating x);
///
- pure int isnan(float x) { return __isnanl(x); }
+ extern (D) pure int isnan(float x) { return __isnanl(x); }
///
- pure int isnan(double x) { return __isnanl(x); }
+ extern (D) pure int isnan(double x) { return __isnanl(x); }
///
- extern(C) pragma(mangle, "__isnanl") pure int isnan(real x);
+ pragma(mangle, "__isnanl") pure int isnan(real x);
//int isnormal(real-floating x);
///
- extern(C) pragma(mangle, "__isnormalf") pure int isnormal(float x);
+ pragma(mangle, "__isnormalf") pure int isnormal(float x);
///
- extern(C) pragma(mangle, "__isnormal") pure int isnormal(double x);
+ pragma(mangle, "__isnormal") pure int isnormal(double x);
///
- extern(C) pragma(mangle, "__isnormall") pure int isnormal(real x);
+ pragma(mangle, "__isnormall") pure int isnormal(real x);
//int signbit(real-floating x);
///
- extern(C) pragma(mangle, "__signbitf") pure int signbit(float x);
+ pragma(mangle, "__signbitf") pure int signbit(float x);
///
- extern(C) pragma(mangle, "__signbit") pure int signbit(double x);
+ pragma(mangle, "__signbit") pure int signbit(double x);
///
- pure int signbit(real x) { return __signbit(x); }
- }
+ extern (D) pure int signbit(real x) { return __signbit(x); }
}
else version (NetBSD)
{
@@ -1266,17 +1260,17 @@ else version (NetBSD)
pure uint __fpclassifyd(double x);
pure uint __fpclassifyl(real x);
- extern (D)
- {
//int fpclassify(real-floating x);
///
- extern(C) pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
+ pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
///
- extern(C) pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
+ pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__fpclassifyd" : "__fpclassifyl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__fpclassifyd" : "__fpclassifyl")
pure int fpclassify(real x);
+ extern (D)
+ {
//int isfinite(real-floating x);
///
pure int isfinite(float x) { return fpclassify(x) >= FP_NORMAL; }
@@ -1360,32 +1354,29 @@ else version (DragonFlyBSD)
pure int __signbitf(float);
pure int __signbitl(real);
- extern (D)
- {
- extern(C) pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
- extern(C) pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
- extern(C) pragma(mangle, "__fpclassifyl") pure int fpclassify(real x);
+ pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
+ pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
+ pragma(mangle, "__fpclassifyl") pure int fpclassify(real x);
- extern(C) pragma(mangle, "__isfinitef") pure int isfinite(float x);
- extern(C) pragma(mangle, "__isfinite") pure int isfinite(double x);
- extern(C) pragma(mangle, "__isfinitel") pure int isfinite(real x);
+ pragma(mangle, "__isfinitef") pure int isfinite(float x);
+ pragma(mangle, "__isfinite") pure int isfinite(double x);
+ pragma(mangle, "__isfinitel") pure int isfinite(real x);
- extern(C) pragma(mangle, "__isinff") pure int isinf(float x);
- extern(C) pragma(mangle, "__isinf") pure int isinf(double x);
- extern(C) pragma(mangle, "__isinfl") pure int isinf(real x);
+ pragma(mangle, "__isinff") pure int isinf(float x);
+ pragma(mangle, "__isinf") pure int isinf(double x);
+ pragma(mangle, "__isinfl") pure int isinf(real x);
- extern(C) pragma(mangle, "__isnanf") pure int isnan(float x);
- extern(C) pragma(mangle, "__isnan") pure int isnan(double x);
- extern(C) pragma(mangle, "__isnanl") pure int isnan(real x);
+ pragma(mangle, "__isnanf") pure int isnan(float x);
+ pragma(mangle, "__isnan") pure int isnan(double x);
+ pragma(mangle, "__isnanl") pure int isnan(real x);
- extern(C) pragma(mangle, "__isnormalf") pure int isnormal(float x);
- extern(C) pragma(mangle, "__isnormal") pure int isnormal(double x);
- extern(C) pragma(mangle, "__isnormall") pure int isnormal(real x);
+ pragma(mangle, "__isnormalf") pure int isnormal(float x);
+ pragma(mangle, "__isnormal") pure int isnormal(double x);
+ pragma(mangle, "__isnormall") pure int isnormal(real x);
- extern(C) pragma(mangle, "__signbitf") pure int signbit(float x);
- extern(C) pragma(mangle, "__signbit") pure int signbit(double x);
- extern(C) pragma(mangle, "__signbitl") pure int signbit(real x);
- }
+ pragma(mangle, "__signbitf") pure int signbit(float x);
+ pragma(mangle, "__signbit") pure int signbit(double x);
+ pragma(mangle, "__signbitl") pure int signbit(real x);
}
else version (Solaris)
{
@@ -1393,17 +1384,14 @@ else version (Solaris)
pure int __isnan(double x);
pure int __isnanl(real x);
- extern (D)
- {
//int isnan(real-floating x);
///
- extern(C) pragma(mangle, "__isnanf") pure int isnan(float x);
+ pragma(mangle, "__isnanf") pure int isnan(float x);
///
- extern(C) pragma(mangle, "__isnan") pure int isnan(double x);
+ pragma(mangle, "__isnan") pure int isnan(double x);
///
- extern(C) pragma(mangle, real.sizeof == double.sizeof ? "__isnan" : "__isnanl")
+ pragma(mangle, real.sizeof == double.sizeof ? "__isnan" : "__isnanl")
pure int isnan(real x);
- }
}
else version (CRuntime_Bionic)
{
@@ -1448,89 +1436,86 @@ else version (CRuntime_Bionic)
pure int __signbitf(float);
pure int __signbitl(real);
- extern (D)
- {
//int fpclassify(real-floating x);
///
- extern(C) pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
+ pragma(mangle, "__fpclassifyf") pure int fpclassify(float x);
///
- extern(C) pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
+ pragma(mangle, "__fpclassifyd") pure int fpclassify(double x);
///
- extern(C) pragma(mangle, "__fpclassifyl") pure int fpclassify(real x);
+ pragma(mangle, "__fpclassifyl") pure int fpclassify(real x);
//int isfinite(real-floating x);
///
- extern(C) pragma(mangle, "__isfinitef") pure int isfinite(float x);
+ pragma(mangle, "__isfinitef") pure int isfinite(float x);
///
- extern(C) pragma(mangle, "__isfinite") pure int isfinite(double x);
+ pragma(mangle, "__isfinite") pure int isfinite(double x);
///
- extern(C) pragma(mangle, "__isfinitel") pure int isfinite(real x);
+ pragma(mangle, "__isfinitel") pure int isfinite(real x);
//int isinf(real-floating x);
///
- extern(C) pragma(mangle, "__isinff") pure int isinf(float x);
+ pragma(mangle, "__isinff") pure int isinf(float x);
///
- extern(C) pragma(mangle, "__isinf") pure int isinf(double x);
+ pragma(mangle, "__isinf") pure int isinf(double x);
///
- extern(C) pragma(mangle, "__isinfl") pure int isinf(real x);
+ pragma(mangle, "__isinfl") pure int isinf(real x);
//int isnan(real-floating x);
///
- extern(C) pragma(mangle, "isnanf") pure int isnan(float x);
+ pragma(mangle, "isnanf") pure int isnan(float x);
///
- extern(C) pragma(mangle, "__isnanl") pure int isnan(real x);
+ pragma(mangle, "__isnanl") pure int isnan(real x);
//int isnormal(real-floating x);
///
- extern(C) pragma(mangle, "__isnormalf") pure int isnormal(float x);
+ pragma(mangle, "__isnormalf") pure int isnormal(float x);
///
- extern(C) pragma(mangle, "__isnormal") pure int isnormal(double x);
+ pragma(mangle, "__isnormal") pure int isnormal(double x);
///
- extern(C) pragma(mangle, "__isnormall") pure int isnormal(real x);
+ pragma(mangle, "__isnormall") pure int isnormal(real x);
//int signbit(real-floating x);
///
- extern(C) pragma(mangle, "__signbitf") pure int signbit(float x);
+ pragma(mangle, "__signbitf") pure int signbit(float x);
///
- extern(C) pragma(mangle, "__signbit") pure int signbit(double x);
+ pragma(mangle, "__signbit") pure int signbit(double x);
///
- extern(C) pragma(mangle, "__signbitl") pure int signbit(real x);
- }
+ pragma(mangle, "__signbitl") pure int signbit(real x);
}
extern (D)
{
//int isgreater(real-floating x, real-floating y);
///
- pure int isgreater(float x, float y) { return x > y && !isunordered(x, y); }
+ pure int isgreater(float x, float y) { return x > y; }
///
- pure int isgreater(double x, double y) { return x > y && !isunordered(x, y); }
+ pure int isgreater(double x, double y) { return x > y; }
///
- pure int isgreater(real x, real y) { return x > y && !isunordered(x, y); }
+ pure int isgreater(real x, real y) { return x > y; }
//int isgreaterequal(real-floating x, real-floating y);
///
- pure int isgreaterequal(float x, float y) { return x >= y && !isunordered(x, y); }
+ pure int isgreaterequal(float x, float y) { return x >= y; }
///
- pure int isgreaterequal(double x, double y) { return x >= y && !isunordered(x, y); }
+ pure int isgreaterequal(double x, double y) { return x >= y; }
///
- pure int isgreaterequal(real x, real y) { return x >= y && !isunordered(x, y); }
+ pure int isgreaterequal(real x, real y) { return x >= y; }
//int isless(real-floating x, real-floating y);
///
- pure int isless(float x, float y) { return x < y && !isunordered(x, y); }
+ pure int isless(float x, float y) { return x < y; }
///
- pure int isless(double x, double y) { return x < y && !isunordered(x, y); }
+ pure int isless(double x, double y) { return x < y; }
///
- pure int isless(real x, real y) { return x < y && !isunordered(x, y); }
+ pure int isless(real x, real y) { return x < y; }
//int islessequal(real-floating x, real-floating y);
///
- pure int islessequal(float x, float y) { return x <= y && !isunordered(x, y); }
+ pure int islessequal(float x, float y) { return x <= y; }
///
- pure int islessequal(double x, double y) { return x <= y && !isunordered(x, y); }
+ pure int islessequal(double x, double y) { return x <= y; }
///
- pure int islessequal(real x, real y) { return x <= y && !isunordered(x, y); }
+ pure int islessequal(real x, real y) { return x <= y; }
//int islessgreater(real-floating x, real-floating y);
///
diff --git a/libphobos/libdruntime/core/stdc/stdint.d b/libphobos/libdruntime/core/stdc/stdint.d
index ac71b1d09d5..9db2fdaa691 100644
--- a/libphobos/libdruntime/core/stdc/stdint.d
+++ b/libphobos/libdruntime/core/stdc/stdint.d
@@ -36,7 +36,20 @@ extern (C):
nothrow:
@nogc:
-
+// These are defined the same way as D basic types, so the definition is
+// platform-independant
+alias int8_t = byte; ///
+alias int16_t = short; ///
+alias uint8_t = ubyte; ///
+alias uint16_t = ushort; ///
+
+// 32 bit types and need to be defined on-platform basis, because
+// they might have C++ binary mangling of `int` or `long`.
+// 64-bit types respectively might be mangled as `long` or `long long`
+
+// It would seem correct to define intmax_t and uintmax_t here, but C and C++
+// compilers don't in practice always set them to the maximum supported value.
+// See https://quuxplusone.github.io/blog/2019/02/28/is-int128-integral/
static if (is(ucent))
{
alias int128_t = cent; ///
@@ -45,10 +58,6 @@ static if (is(ucent))
version (Windows)
{
- alias int8_t = byte; ///
- alias int16_t = short; ///
- alias uint8_t = ubyte; ///
- alias uint16_t = ushort; ///
version (CRuntime_DigitalMars)
{
alias int32_t = cpp_long; ///
@@ -62,23 +71,23 @@ version (Windows)
alias int64_t = long; ///
alias uint64_t = ulong; ///
- alias int_least8_t = byte; ///
- alias uint_least8_t = ubyte; ///
- alias int_least16_t = short; ///
- alias uint_least16_t = ushort; ///
- alias int_least32_t = int32_t; ///
+ alias int_least8_t = byte; ///
+ alias uint_least8_t = ubyte; ///
+ alias int_least16_t = short; ///
+ alias uint_least16_t = ushort; ///
+ alias int_least32_t = int32_t; ///
alias uint_least32_t = uint32_t; ///
- alias int_least64_t = long; ///
- alias uint_least64_t = ulong; ///
-
- alias int_fast8_t = byte; ///
- alias uint_fast8_t = ubyte; ///
- alias int_fast16_t = int; ///
- alias uint_fast16_t = uint; ///
- alias int_fast32_t = int32_t; ///
+ alias int_least64_t = long; ///
+ alias uint_least64_t = ulong; ///
+
+ alias int_fast8_t = byte; ///
+ alias uint_fast8_t = ubyte; ///
+ alias int_fast16_t = int; ///
+ alias uint_fast16_t = uint; ///
+ alias int_fast32_t = int32_t; ///
alias uint_fast32_t = uint32_t; ///
- alias int_fast64_t = long; ///
- alias uint_fast64_t = ulong; ///
+ alias int_fast64_t = long; ///
+ alias uint_fast64_t = ulong; ///
alias intptr_t = ptrdiff_t; ///
alias uintptr_t = size_t; ///
@@ -87,10 +96,6 @@ version (Windows)
}
else version (Darwin)
{
- alias int8_t = byte; ///
- alias int16_t = short; ///
- alias uint8_t = ubyte; ///
- alias uint16_t = ushort; ///
alias int32_t = int; ///
alias uint32_t = uint; ///
alias int64_t = cpp_longlong; ///
@@ -121,23 +126,19 @@ else version (Darwin)
}
else version (Posix)
{
- alias int8_t = byte; ///
- alias int16_t = short; ///
- alias uint8_t = ubyte; ///
- alias uint16_t = ushort; ///
- alias int32_t = int; ///
- alias uint32_t = uint; ///
- alias int64_t = long; ///
- alias uint64_t = ulong; ///
-
- alias int_least8_t = byte; ///
- alias uint_least8_t = ubyte; ///
- alias int_least16_t = short; ///
+ alias int32_t = int; ///
+ alias uint32_t = uint; ///
+ alias int64_t = long; ///
+ alias uint64_t = ulong; ///
+
+ alias int_least8_t = byte; ///
+ alias uint_least8_t = ubyte; ///
+ alias int_least16_t = short; ///
alias uint_least16_t = ushort; ///
- alias int_least32_t = int; ///
- alias uint_least32_t = uint; ///
- alias int_least64_t = long; ///
- alias uint_least64_t = ulong; ///
+ alias int_least32_t = int; ///
+ alias uint_least32_t = uint; ///
+ alias int_least64_t = long; ///
+ alias uint_least64_t = ulong;///
version (FreeBSD)
{
@@ -166,13 +167,13 @@ else version (Posix)
alias int_fast32_t = ptrdiff_t; ///
alias uint_fast32_t = size_t; ///
}
- alias int_fast64_t = long; ///
- alias uint_fast64_t = ulong; ///
+ alias int_fast64_t = long; ///
+ alias uint_fast64_t = ulong; ///
alias intptr_t = ptrdiff_t; ///
- alias uintptr_t = size_t; ///
- alias intmax_t = long; ///
- alias uintmax_t = ulong; ///
+ alias uintptr_t = size_t; ///
+ alias intmax_t = long; ///
+ alias uintmax_t = ulong; ///
}
else
{
diff --git a/libphobos/libdruntime/core/stdcpp/allocator.d b/libphobos/libdruntime/core/stdcpp/allocator.d
new file mode 100644
index 00000000000..abf97c48b0b
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/allocator.d
@@ -0,0 +1,373 @@
+/**
+ * D binding to C++ std::allocator.
+ *
+ * Copyright: Copyright (c) 2019 D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Manu Evans
+ * Source: $(DRUNTIMESRC core/stdcpp/allocator.d)
+ */
+
+module core.stdcpp.allocator;
+
+import core.stdcpp.new_;
+import core.stdcpp.xutility : StdNamespace, __cpp_sized_deallocation, __cpp_aligned_new;
+
+extern(C++, (StdNamespace)):
+
+/**
+ * Allocators are classes that define memory models to be used by some parts of
+ * the C++ Standard Library, and most specifically, by STL containers.
+ */
+extern(C++, class)
+struct allocator(T)
+{
+ static assert(!is(T == const), "The C++ Standard forbids containers of const elements because allocator!(const T) is ill-formed.");
+ static assert(!is(T == immutable), "immutable is not representable in C++");
+ static assert(!is(T == class), "Instantiation with `class` is not supported; D can't mangle the base (non-pointer) type of a class. Use `extern (C++, class) struct T { ... }` instead.");
+extern(D):
+
+ ///
+ this(U)(ref allocator!U) {}
+
+ ///
+ alias size_type = size_t;
+ ///
+ alias difference_type = ptrdiff_t;
+ ///
+ alias pointer = T*;
+ ///
+ alias value_type = T;
+
+ ///
+ enum propagate_on_container_move_assignment = true;
+ ///
+ enum is_always_equal = true;
+
+ ///
+ alias rebind(U) = allocator!U;
+
+ version (CppRuntime_Microsoft)
+ {
+ import core.stdcpp.xutility : _MSC_VER;
+
+ ///
+ T* allocate(size_t count) @nogc
+ {
+ static if (_MSC_VER <= 1800)
+ {
+ import core.stdcpp.xutility : _Xbad_alloc;
+ if (count == 0)
+ return null;
+ void* mem;
+ if ((size_t.max / T.sizeof < count) || (mem = __cpp_new(count * T.sizeof)) is null)
+ _Xbad_alloc();
+ return cast(T*)mem;
+ }
+ else
+ {
+ enum _Align = _New_alignof!T;
+
+ static size_t _Get_size_of_n(T)(const size_t _Count)
+ {
+ static if (T.sizeof == 1)
+ return _Count;
+ else
+ {
+ enum size_t _Max_possible = size_t.max / T.sizeof;
+ return _Max_possible < _Count ? size_t.max : _Count * T.sizeof;
+ }
+ }
+
+ const size_t _Bytes = _Get_size_of_n!T(count);
+ if (_Bytes == 0)
+ return null;
+
+ static if (!__cpp_aligned_new || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ {
+ version (INTEL_ARCH)
+ {
+ if (_Bytes >= _Big_allocation_threshold)
+ return cast(T*)_Allocate_manually_vector_aligned(_Bytes);
+ }
+ return cast(T*)__cpp_new(_Bytes);
+ }
+ else
+ {
+ size_t _Passed_align = _Align;
+ version (INTEL_ARCH)
+ {
+ if (_Bytes >= _Big_allocation_threshold)
+ _Passed_align = _Align < _Big_allocation_alignment ? _Big_allocation_alignment : _Align;
+ }
+ return cast(T*)__cpp_new_aligned(_Bytes, cast(align_val_t)_Passed_align);
+ }
+ }
+ }
+ ///
+ void deallocate(T* ptr, size_t count) @nogc
+ {
+ static if (_MSC_VER <= 1800)
+ {
+ __cpp_delete(ptr);
+ }
+ else
+ {
+ // this is observed from VS2017
+ void* _Ptr = ptr;
+ size_t _Bytes = T.sizeof * count;
+
+ enum _Align = _New_alignof!T;
+ static if (!__cpp_aligned_new || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ {
+ version (INTEL_ARCH)
+ {
+ if (_Bytes >= _Big_allocation_threshold)
+ _Adjust_manually_vector_aligned(_Ptr, _Bytes);
+ }
+ static if (_MSC_VER <= 1900)
+ __cpp_delete(ptr);
+ else
+ __cpp_delete_size(_Ptr, _Bytes);
+ }
+ else
+ {
+ size_t _Passed_align = _Align;
+ version (INTEL_ARCH)
+ {
+ if (_Bytes >= _Big_allocation_threshold)
+ _Passed_align = _Align < _Big_allocation_alignment ? _Big_allocation_alignment : _Align;
+ }
+ __cpp_delete_size_aligned(_Ptr, _Bytes, cast(align_val_t)_Passed_align);
+ }
+ }
+ }
+
+ ///
+ enum size_t max_size = size_t.max / T.sizeof;
+ }
+ else version (CppRuntime_Gcc)
+ {
+ ///
+ T* allocate(size_t count, const(void)* = null) @nogc
+ {
+// if (count > max_size)
+// std::__throw_bad_alloc();
+
+ static if (__cpp_aligned_new && T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ return cast(T*)__cpp_new_aligned(count * T.sizeof, cast(align_val_t)T.alignof);
+ else
+ return cast(T*)__cpp_new(count * T.sizeof);
+ }
+ ///
+ void deallocate(T* ptr, size_t count) @nogc
+ {
+ // NOTE: GCC doesn't seem to use the sized delete when it's available...
+
+ static if (__cpp_aligned_new && T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ __cpp_delete_aligned(cast(void*)ptr, cast(align_val_t)T.alignof);
+ else
+ __cpp_delete(cast(void*)ptr);
+ }
+
+ ///
+ enum size_t max_size = (ptrdiff_t.max < size_t.max ? cast(size_t)ptrdiff_t.max : size_t.max) / T.sizeof;
+ }
+ else version (CppRuntime_Clang)
+ {
+ ///
+ T* allocate(size_t count, const(void)* = null) @nogc
+ {
+// if (count > max_size)
+// __throw_length_error("allocator!T.allocate(size_t n) 'n' exceeds maximum supported size");
+
+ static if (__cpp_aligned_new && T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ return cast(T*)__cpp_new_aligned(count * T.sizeof, cast(align_val_t)T.alignof);
+ else
+ return cast(T*)__cpp_new(count * T.sizeof);
+ }
+ ///
+ void deallocate(T* ptr, size_t count) @nogc
+ {
+ static if (__cpp_aligned_new && T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ {
+ static if (__cpp_sized_deallocation)
+ return __cpp_delete_size_aligned(cast(void*)ptr, count * T.sizeof, cast(align_val_t)T.alignof);
+ else
+ return __cpp_delete_aligned(cast(void*)ptr, cast(align_val_t)T.alignof);
+ }
+ else static if (__cpp_sized_deallocation)
+ return __cpp_delete_size(cast(void*)ptr, count * T.sizeof);
+ else
+ return __cpp_delete(cast(void*)ptr);
+ }
+
+ ///
+ enum size_t max_size = size_t.max / T.sizeof;
+ }
+ else
+ {
+ static assert(false, "C++ runtime not supported");
+ }
+}
+
+///
+extern(C++, (StdNamespace))
+struct allocator_traits(Alloc)
+{
+ import core.internal.traits : isTrue;
+
+ ///
+ alias allocator_type = Alloc;
+ ///
+ alias value_type = allocator_type.value_type;
+ ///
+ alias size_type = allocator_type.size_type;
+ ///
+ alias difference_type = allocator_type.difference_type;
+ ///
+ alias pointer = allocator_type.pointer;
+
+ ///
+ enum propagate_on_container_copy_assignment = isTrue!(allocator_type, "propagate_on_container_copy_assignment");
+ ///
+ enum propagate_on_container_move_assignment = isTrue!(allocator_type, "propagate_on_container_move_assignment");
+ ///
+ enum propagate_on_container_swap = isTrue!(allocator_type, "propagate_on_container_swap");
+ ///
+ enum is_always_equal = isTrue!(allocator_type, "is_always_equal");
+
+ ///
+ template rebind_alloc(U)
+ {
+ static if (__traits(hasMember, allocator_type, "rebind"))
+ alias rebind_alloc = allocator_type.rebind!U;
+ else
+ alias rebind_alloc = allocator_type!U;
+ }
+ ///
+ alias rebind_traits(U) = allocator_traits!(rebind_alloc!U);
+
+ ///
+ static size_type max_size()(auto ref allocator_type a)
+ {
+ static if (__traits(hasMember, allocator_type, "max_size"))
+ return a.max_size();
+ else
+ return size_type.max / value_type.sizeof;
+ }
+
+ ///
+ static allocator_type select_on_container_copy_construction()(auto ref allocator_type a)
+ {
+ static if (__traits(hasMember, allocator_type, "select_on_container_copy_construction"))
+ return a.select_on_container_copy_construction();
+ else
+ return a;
+ }
+}
+
+private:
+
+// MSVC has some bonus complexity!
+version (CppRuntime_Microsoft)
+{
+ // some versions of VS require a `* const` pointer mangling hack
+ // we need a way to supply the target VS version to the compile
+ version = NeedsMangleHack;
+
+ version (X86)
+ version = INTEL_ARCH;
+ version (X86_64)
+ version = INTEL_ARCH;
+
+ // HACK: should we guess _DEBUG for `debug` builds?
+ version (_DEBUG)
+ enum _DEBUG = true;
+ else version (NDEBUG)
+ enum _DEBUG = false;
+ else
+ {
+ import core.stdcpp.xutility : __CXXLIB__;
+ enum _DEBUG = __CXXLIB__.length && 'd' == __CXXLIB__[$-1]; // libcmtd, msvcrtd
+ }
+
+ enum _New_alignof(T) = T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__ ? T.alignof : __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+
+ version (INTEL_ARCH)
+ {
+ enum size_t _Big_allocation_threshold = 4096;
+ enum size_t _Big_allocation_alignment = 32;
+
+ static assert(2 * (void*).sizeof <= _Big_allocation_alignment, "Big allocation alignment should at least match vector register alignment");
+ static assert((v => v != 0 && (v & (v - 1)) == 0)(_Big_allocation_alignment), "Big allocation alignment must be a power of two");
+ static assert(size_t.sizeof == (void*).sizeof, "uintptr_t is not the same size as size_t");
+
+ // NOTE: this must track `_DEBUG` macro used in C++...
+ static if (_DEBUG)
+ enum size_t _Non_user_size = 2 * (void*).sizeof + _Big_allocation_alignment - 1;
+ else
+ enum size_t _Non_user_size = (void*).sizeof + _Big_allocation_alignment - 1;
+
+ version (Win64)
+ enum size_t _Big_allocation_sentinel = 0xFAFAFAFAFAFAFAFA;
+ else
+ enum size_t _Big_allocation_sentinel = 0xFAFAFAFA;
+
+ extern(D) // Template so it gets compiled according to _DEBUG.
+ void* _Allocate_manually_vector_aligned()(const size_t _Bytes) @nogc
+ {
+ size_t _Block_size = _Non_user_size + _Bytes;
+ if (_Block_size <= _Bytes)
+ _Block_size = size_t.max;
+
+ const size_t _Ptr_container = cast(size_t)__cpp_new(_Block_size);
+ if (!(_Ptr_container != 0))
+ assert(false, "invalid argument");
+ void* _Ptr = cast(void*)((_Ptr_container + _Non_user_size) & ~(_Big_allocation_alignment - 1));
+ (cast(size_t*)_Ptr)[-1] = _Ptr_container;
+
+ static if (_DEBUG)
+ (cast(size_t*)_Ptr)[-2] = _Big_allocation_sentinel;
+ return (_Ptr);
+ }
+
+ extern(D) // Template so it gets compiled according to _DEBUG.
+ void _Adjust_manually_vector_aligned()(ref void* _Ptr, ref size_t _Bytes) pure nothrow @nogc
+ {
+ _Bytes += _Non_user_size;
+
+ const size_t* _Ptr_user = cast(size_t*)_Ptr;
+ const size_t _Ptr_container = _Ptr_user[-1];
+
+ // If the following asserts, it likely means that we are performing
+ // an aligned delete on memory coming from an unaligned allocation.
+ static if (_DEBUG)
+ assert(_Ptr_user[-2] == _Big_allocation_sentinel, "invalid argument");
+
+ // Extra paranoia on aligned allocation/deallocation; ensure _Ptr_container is
+ // in range [_Min_back_shift, _Non_user_size]
+ static if (_DEBUG)
+ enum size_t _Min_back_shift = 2 * (void*).sizeof;
+ else
+ enum size_t _Min_back_shift = (void*).sizeof;
+
+ const size_t _Back_shift = cast(size_t)_Ptr - _Ptr_container;
+ if (!(_Back_shift >= _Min_back_shift && _Back_shift <= _Non_user_size))
+ assert(false, "invalid argument");
+ _Ptr = cast(void*)_Ptr_container;
+ }
+ }
+}
+version (CppRuntime_Clang)
+{
+ // Helper for container swap
+ package(core.stdcpp) void __swap_allocator(Alloc)(ref Alloc __a1, ref Alloc __a2)
+ {
+ import core.internal.lifetime : swap;
+
+ static if (allocator_traits!Alloc.propagate_on_container_swap)
+ swap(__a1, __a2);
+ }
+}
diff --git a/libphobos/libdruntime/core/stdcpp/array.d b/libphobos/libdruntime/core/stdcpp/array.d
new file mode 100644
index 00000000000..eb63d4ccaab
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/array.d
@@ -0,0 +1,133 @@
+/**
+ * D header file for interaction with C++ std::array.
+ *
+ * Copyright: Copyright (c) 2018 D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Manu Evans
+ * Source: $(DRUNTIMESRC core/stdcpp/array.d)
+ */
+
+module core.stdcpp.array;
+
+import core.stdcpp.xutility : StdNamespace;
+
+// hacks to support DMD on Win32
+version (CppRuntime_Microsoft)
+{
+ version = CppRuntime_Windows; // use the MS runtime ABI for win32
+}
+else version (CppRuntime_DigitalMars)
+{
+ version = CppRuntime_Windows; // use the MS runtime ABI for win32
+ pragma(msg, "std::array not supported by DMC");
+}
+
+extern(C++, (StdNamespace)):
+
+/**
+ * D language counterpart to C++ std::array.
+ *
+ * C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/container/array)
+ */
+extern(C++, class) struct array(T, size_t N)
+{
+extern(D):
+pragma(inline, true):
+
+ ///
+ alias size_type = size_t;
+ ///
+ alias difference_type = ptrdiff_t;
+ ///
+ alias value_type = T;
+ ///
+ alias pointer = T*;
+ ///
+ alias const_pointer = const(T)*;
+
+ ///
+ alias as_array this;
+
+ /// Variadic constructor
+ this(T[N] args ...) { this[] = args[]; }
+
+ ///
+ void fill()(auto ref const(T) value) { this[] = value; }
+
+pure nothrow @nogc:
+ ///
+ size_type size() const @safe { return N; }
+ ///
+ alias length = size;
+ ///
+ alias opDollar = length;
+ ///
+ size_type max_size() const @safe { return N; }
+ ///
+ bool empty() const @safe { return N == 0; }
+
+ ///
+ ref inout(T) front() inout @safe { static if (N > 0) { return this[0]; } else { return as_array()[][0]; /* HACK: force OOB */ } }
+ ///
+ ref inout(T) back() inout @safe { static if (N > 0) { return this[N-1]; } else { return as_array()[][0]; /* HACK: force OOB */ } }
+
+ version (CppRuntime_Windows)
+ {
+ ///
+ inout(T)* data() inout @safe { return &_Elems[0]; }
+ ///
+ ref inout(T)[N] as_array() inout @safe { return _Elems[0 .. N]; }
+ ///
+ ref inout(T) at(size_type i) inout @safe { return _Elems[0 .. N][i]; }
+
+ private:
+ T[N ? N : 1] _Elems;
+ }
+ else version (CppRuntime_Gcc)
+ {
+ ///
+ inout(T)* data() inout @safe { static if (N > 0) { return &_M_elems[0]; } else { return null; } }
+ ///
+ ref inout(T)[N] as_array() inout @trusted { return data()[0 .. N]; }
+ ///
+ ref inout(T) at(size_type i) inout @trusted { return data()[0 .. N][i]; }
+
+ private:
+ static if (N > 0)
+ {
+ T[N] _M_elems;
+ }
+ else
+ {
+ struct _Placeholder {}
+ _Placeholder _M_placeholder;
+ }
+ }
+ else version (CppRuntime_Clang)
+ {
+ ///
+ inout(T)* data() inout @trusted { static if (N > 0) { return &__elems_[0]; } else { return cast(inout(T)*)__elems_.ptr; } }
+ ///
+ ref inout(T)[N] as_array() inout @trusted { return data()[0 .. N]; }
+ ///
+ ref inout(T) at(size_type i) inout @trusted { return data()[0 .. N][i]; }
+
+ private:
+ static if (N > 0)
+ {
+ T[N] __elems_;
+ }
+ else
+ {
+ struct _ArrayInStructT { T[1] __data_; }
+ align(_ArrayInStructT.alignof)
+ byte[_ArrayInStructT.sizeof] __elems_ = void;
+ }
+ }
+ else
+ {
+ static assert(false, "C++ runtime not supported");
+ }
+}
diff --git a/libphobos/libdruntime/core/stdcpp/exception.d b/libphobos/libdruntime/core/stdcpp/exception.d
index 14203b09918..f920057cd06 100644
--- a/libphobos/libdruntime/core/stdcpp/exception.d
+++ b/libphobos/libdruntime/core/stdcpp/exception.d
@@ -6,95 +6,134 @@
* Copyright: Copyright (c) 2016 D Language Foundation
* License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright)
+ * Manu Evans
* Source: $(DRUNTIMESRC core/stdcpp/_exception.d)
*/
module core.stdcpp.exception;
-extern (C++, "std"):
+import core.stdcpp.xutility : __cplusplus, CppStdRevision;
+import core.attribute : weak;
-version (CppRuntime_DigitalMars)
-{
- import core.stdcpp.typeinfo;
+version (CppRuntime_Gcc)
+ version = GenericBaseException;
+version (CppRuntime_Clang)
+ version = GenericBaseException;
- alias void function() unexpected_handler;
- unexpected_handler set_unexpected(unexpected_handler f) nothrow;
- void unexpected();
+extern (C++, "std"):
+@nogc:
- alias void function() terminate_handler;
- terminate_handler set_terminate(terminate_handler f) nothrow;
- void terminate();
+///
+alias terminate_handler = void function() nothrow;
+///
+terminate_handler set_terminate(terminate_handler f) nothrow;
+///
+terminate_handler get_terminate() nothrow;
+///
+void terminate() nothrow;
- bool uncaught_exception();
+static if (__cplusplus < CppStdRevision.cpp17)
+{
+ ///
+ alias unexpected_handler = void function();
+ ///
+ deprecated unexpected_handler set_unexpected(unexpected_handler f) nothrow;
+ ///
+ deprecated unexpected_handler get_unexpected() nothrow;
+ ///
+ deprecated void unexpected();
+}
+static if (__cplusplus < CppStdRevision.cpp17)
+{
+ ///
+ bool uncaught_exception() nothrow;
+}
+else static if (__cplusplus == CppStdRevision.cpp17)
+{
+ ///
+ deprecated bool uncaught_exception() nothrow;
+}
+static if (__cplusplus >= CppStdRevision.cpp17)
+{
+ ///
+ int uncaught_exceptions() nothrow;
+}
+
+version (GenericBaseException)
+{
+ ///
class exception
{
- this() nothrow { }
- this(const exception) nothrow { }
- //exception operator=(const exception) nothrow { return this; }
- //virtual ~this() nothrow;
- void dtor() { }
- const(char)* what() const nothrow;
- }
+ @nogc:
+ ///
+ this() nothrow {}
+ ///
+ @weak ~this() nothrow {} // HACK: this should extern, but then we have link errors!
- class bad_exception : exception
- {
- this() nothrow { }
- this(const bad_exception) nothrow { }
- //bad_exception operator=(const bad_exception) nothrow { return this; }
- //virtual ~this() nothrow;
- override const(char)* what() const nothrow;
+ ///
+ @weak const(char)* what() const nothrow { return "unknown"; } // HACK: this should extern, but then we have link errors!
+
+ protected:
+ this(const(char)*, int = 1) nothrow { this(); } // compat with MS derived classes
}
}
-else version (CppRuntime_Gcc)
+else version (CppRuntime_DigitalMars)
{
- alias void function() unexpected_handler;
- unexpected_handler set_unexpected(unexpected_handler f) nothrow;
- void unexpected();
-
- alias void function() terminate_handler;
- terminate_handler set_terminate(terminate_handler f) nothrow;
- void terminate();
-
- pure bool uncaught_exception();
-
+ ///
class exception
{
- this();
+ @nogc:
+ ///
+ this() nothrow {}
//virtual ~this();
- void dtor1();
- void dtor2();
- const(char)* what() const;
- }
+ void dtor() { } // reserve slot in vtbl[]
- class bad_exception : exception
- {
- this();
- //virtual ~this();
- override const(char)* what() const;
+ ///
+ const(char)* what() const nothrow;
+
+ protected:
+ this(const(char)*, int = 1) nothrow { this(); } // compat with MS derived classes
}
}
else version (CppRuntime_Microsoft)
{
+ ///
class exception
{
- this();
- this(const exception);
- //exception operator=(const exception) { return this; }
- //virtual ~this();
- void dtor() { }
- const(char)* what() const;
-
- private:
- const(char)* mywhat;
- bool dofree;
- }
+ @nogc:
+ ///
+ this(const(char)* message = "unknown", int = 1) nothrow { msg = message; }
+ ///
+ @weak ~this() nothrow {}
- class bad_exception : exception
- {
- this(const(char)* msg = "bad exception");
- //virtual ~this();
+ ///
+ @weak const(char)* what() const nothrow { return msg != null ? msg : "unknown exception"; }
+
+ // TODO: do we want this? exceptions are classes... ref types.
+// final ref exception opAssign(ref const(exception) e) nothrow { msg = e.msg; return this; }
+
+ protected:
+ @weak void _Doraise() const { assert(0); }
+
+ protected:
+ const(char)* msg;
}
+
}
else
static assert(0, "Missing std::exception binding for this platform");
+
+///
+class bad_exception : exception
+{
+@nogc:
+ ///
+ this(const(char)* message = "bad exception") { super(message); }
+
+ version (GenericBaseException)
+ {
+ ///
+ @weak override const(char)* what() const nothrow { return "bad exception"; }
+ }
+}
diff --git a/libphobos/libdruntime/core/stdcpp/memory.d b/libphobos/libdruntime/core/stdcpp/memory.d
new file mode 100644
index 00000000000..bd7976cfbc5
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/memory.d
@@ -0,0 +1,163 @@
+/**
+* D binding to C++ <memory>.
+*
+* Copyright: Copyright (c) 2019 D Language Foundation
+* License: Distributed under the
+* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+* (See accompanying file LICENSE)
+* Authors: Manu Evans
+* Source: $(DRUNTIMESRC core/stdcpp/memory.d)
+*/
+
+module core.stdcpp.memory;
+
+public import core.stdcpp.allocator;
+
+import core.stdcpp.xutility : StdNamespace;
+
+extern(C++, (StdNamespace)):
+
+///
+unique_ptr!T make_unique(T, Args...)(auto ref Args args)
+{
+ import core.lifetime : forward;
+ import core.stdcpp.new_ : cpp_new;
+
+ return unique_ptr!T(cpp_new!T(forward!args));
+}
+
+///
+struct default_delete(T)
+{
+ ///
+ alias pointer = ClassOrPtr!T;
+
+ ///
+ void opCall()(pointer ptr) const
+ {
+ import core.stdcpp.new_ : cpp_delete;
+
+ cpp_delete(ptr);
+ }
+}
+
+///
+extern(C++, class)
+struct unique_ptr(T, Deleter = default_delete!T)
+{
+extern(D):
+ ///
+ this(this) @disable;
+
+ ///
+ ~this()
+ {
+ reset();
+ }
+
+ ///
+ ref unique_ptr opAssign(typeof(null))
+ {
+ reset();
+ return this;
+ }
+
+ ///
+ void reset(pointer p = null)
+ {
+ pointer t = __ptr();
+ __ptr() = p;
+ if (t)
+ get_deleter()(t);
+ }
+
+nothrow pure @safe @nogc:
+ ///
+ alias pointer = ClassOrPtr!T;
+ ///
+ alias element_type = T;
+ ///
+ alias deleter_type = Deleter;
+
+ ///
+ this(pointer ptr)
+ {
+ __ptr() = ptr;
+ }
+
+ ///
+ inout(pointer) get() inout nothrow
+ {
+ return __ptr();
+ }
+
+ ///
+ bool opCast(T : bool)() const nothrow
+ {
+ return __ptr() != null;
+ }
+
+ ///
+ pointer release() nothrow
+ {
+ pointer t = __ptr();
+ __ptr() = null;
+ return t;
+ }
+
+// void swap(ref unique_ptr u) nothrow
+// {
+// __ptr_.swap(__u.__ptr_);
+// }
+
+ version (CppRuntime_Microsoft)
+ {
+ ///
+ ref inout(deleter_type) get_deleter() inout nothrow { return _Mypair._Myval1; }
+
+ private:
+ import core.stdcpp.xutility : _Compressed_pair;
+
+ ref pointer __ptr() nothrow { return _Mypair._Myval2; }
+ inout(pointer) __ptr() inout nothrow { return _Mypair._Myval2; }
+
+ _Compressed_pair!(Deleter, pointer) _Mypair;
+ }
+ else version (CppRuntime_Gcc)
+ {
+ ///
+ ref inout(deleter_type) get_deleter() inout nothrow { return _M_t.get!1; }
+
+ private:
+ import core.stdcpp.tuple : tuple, get;
+
+ ref pointer __ptr() nothrow { return _M_t.get!0; }
+ inout(pointer) __ptr() inout nothrow { return _M_t.get!0; }
+
+ tuple!(pointer, Deleter) _M_t;
+ }
+ else version (CppRuntime_Clang)
+ {
+ ///
+ ref inout(deleter_type) get_deleter() inout nothrow { return __ptr_.second; }
+
+ private:
+ import core.stdcpp.xutility : __compressed_pair;
+
+ ref pointer __ptr() nothrow { return __ptr_.first; }
+ inout(pointer) __ptr() inout nothrow { return __ptr_.first; }
+
+ __compressed_pair!(pointer, deleter_type) __ptr_;
+ }
+}
+
+
+private:
+
+template ClassOrPtr(T)
+{
+ static if (is(T == class))
+ alias ClassOrPtr = T;
+ else
+ alias ClassOrPtr = T*;
+}
diff --git a/libphobos/libdruntime/core/stdcpp/new_.d b/libphobos/libdruntime/core/stdcpp/new_.d
new file mode 100644
index 00000000000..77c179ce962
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/new_.d
@@ -0,0 +1,186 @@
+/**
+ * D binding to C++ <new>
+ *
+ * Copyright: Copyright (c) 2019 D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Manu Evans
+ * Source: $(DRUNTIMESRC core/stdcpp/new_.d)
+ */
+
+module core.stdcpp.new_;
+
+import core.stdcpp.xutility : __cpp_sized_deallocation, __cpp_aligned_new;
+import core.stdcpp.exception : exception;
+
+// TODO: this really should come from __traits(getTargetInfo, "defaultNewAlignment")
+version (D_LP64)
+ enum size_t __STDCPP_DEFAULT_NEW_ALIGNMENT__ = 16;
+else
+ enum size_t __STDCPP_DEFAULT_NEW_ALIGNMENT__ = 8;
+
+extern (C++, "std")
+{
+ ///
+ struct nothrow_t {}
+
+ ///
+ enum align_val_t : size_t { defaultAlignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__ };
+
+ ///
+ class bad_alloc : exception
+ {
+ @nogc:
+ ///
+ this() { super("bad allocation", 1); }
+ }
+}
+
+
+///
+T* cpp_new(T, Args...)(auto ref Args args) if (!is(T == class))
+{
+ import core.lifetime : emplace, forward;
+
+ T* mem = cast(T*)__cpp_new(T.sizeof);
+ return mem.emplace(forward!args);
+}
+
+///
+T cpp_new(T, Args...)(auto ref Args args) if (is(T == class))
+{
+ import core.lifetime : emplace, forward;
+
+ T mem = cast(T)__cpp_new(__traits(classInstanceSize, T));
+ return mem.emplace(forward!args);
+}
+
+///
+void cpp_delete(T)(T* ptr) if (!is(T == class))
+{
+ destroy!false(*ptr);
+ __cpp_delete(ptr);
+}
+
+///
+void cpp_delete(T)(T instance) if (is(T == class))
+{
+ destroy!false(instance);
+ __cpp_delete(cast(void*) instance);
+}
+
+
+// raw C++ functions
+extern(C++):
+@nogc:
+
+/// Binding for ::operator new(std::size_t count)
+pragma(mangle, __new_mangle)
+void* __cpp_new(size_t count);
+
+/// Binding for ::operator new(std::size_t count, const std::nothrow_t&)
+pragma(mangle, __new_nothrow_mangle)
+void* __cpp_new_nothrow(size_t count, ref const(nothrow_t) = std_nothrow) nothrow;
+
+/// Binding for ::operator delete(void* ptr)
+pragma(mangle, __delete_mangle)
+void __cpp_delete(void* ptr);
+
+/// Binding for ::operator delete(void* ptr, const std::nothrow_t& tag)
+pragma(mangle, __delete_nothrow_mangle)
+void __cpp_delete_nothrow(void* ptr, ref const(nothrow_t) = std_nothrow) nothrow;
+
+static if (__cpp_sized_deallocation)
+{
+ /// Binding for ::operator delete(void* ptr, size_t size)
+ pragma(mangle, __delete_size_mangle)
+ void __cpp_delete_size(void* ptr, size_t size);
+}
+static if (__cpp_aligned_new)
+{
+ /// Binding for ::operator new(std::size_t count, std::align_val_t al)
+ pragma(mangle, __new_align_mangle)
+ void* __cpp_new_aligned(size_t count, align_val_t alignment);
+
+ /// Binding for ::operator new(std::size_t count, std::align_val_t al, const std::nothrow_t&)
+ pragma(mangle, __new_aligned_nothrow_mangle)
+ void* __cpp_new_aligned_nothrow(size_t count, align_val_t alignment, ref const(nothrow_t) = std_nothrow) nothrow;
+
+ /// Binding for ::operator delete(void* ptr, std::align_val_t al)
+ pragma(mangle, __delete_align_mangle)
+ void __cpp_delete_aligned(void* ptr, align_val_t alignment);
+
+ /// Binding for ::operator delete(void* ptr, std::align_val_t al, const std::nothrow_t& tag)
+ pragma(mangle, __delete_align_nothrow_mangle)
+ void __cpp_delete_align_nothrow(void* ptr, align_val_t alignment, ref const(nothrow_t) = std_nothrow) nothrow;
+
+ /// Binding for ::operator delete(void* ptr, size_t size, std::align_val_t al)
+ pragma(mangle, __delete_size_align_mangle)
+ void __cpp_delete_size_aligned(void* ptr, size_t size, align_val_t alignment);
+}
+
+private:
+extern (D):
+
+__gshared immutable nothrow_t std_nothrow;
+
+// we have to hard-code the mangling for the global new/delete operators
+version (CppRuntime_Microsoft)
+{
+ version (D_LP64)
+ {
+ enum __new_mangle = "??2@YAPEAX_K@Z";
+ enum __new_nothrow_mangle = "??2@YAPEAX_KAEBUnothrow_t@std@@@Z";
+ enum __delete_mangle = "??3@YAXPEAX@Z";
+ enum __delete_nothrow_mangle = "??3@YAXPEAXAEBUnothrow_t@std@@@Z";
+ enum __delete_size_mangle = "??3@YAXPEAX_K@Z";
+ enum __new_align_mangle = "??2@YAPEAX_KW4align_val_t@std@@@Z";
+ enum __new_aligned_nothrow_mangle = "??2@YAPEAX_KW4align_val_t@std@@AEBUnothrow_t@1@@Z";
+ enum __delete_align_mangle = "??3@YAXPEAXW4align_val_t@std@@@Z";
+ enum __delete_align_nothrow_mangle = "??3@YAXPEAXW4align_val_t@std@@AEBUnothrow_t@1@@Z";
+ enum __delete_size_align_mangle = "??3@YAXPEAX_KW4align_val_t@std@@@Z";
+ }
+ else
+ {
+ enum __new_mangle = "??2@YAPAXI@Z";
+ enum __new_nothrow_mangle = "??2@YAPAXIABUnothrow_t@std@@@Z";
+ enum __delete_mangle = "??3@YAXPAX@Z";
+ enum __delete_nothrow_mangle = "??3@YAXPAXABUnothrow_t@std@@@Z";
+ enum __delete_size_mangle = "??3@YAXPAXI@Z";
+ enum __new_align_mangle = "??2@YAPAXIW4align_val_t@std@@@Z";
+ enum __new_aligned_nothrow_mangle = "??2@YAPAXIW4align_val_t@std@@ABUnothrow_t@1@@Z";
+ enum __delete_align_mangle = "??3@YAXPAXW4align_val_t@std@@@Z";
+ enum __delete_align_nothrow_mangle = "??3@YAXPAXW4align_val_t@std@@ABUnothrow_t@1@@Z";
+ enum __delete_size_align_mangle = "??3@YAXPAXIW4align_val_t@std@@@Z";
+ }
+}
+else
+{
+ version (D_LP64)
+ {
+ enum __new_mangle = "_Znwm";
+ enum __new_nothrow_mangle = "_ZnwmRKSt9nothrow_t";
+ enum __delete_mangle = "_ZdlPv";
+ enum __delete_nothrow_mangle = "_ZdlPvRKSt9nothrow_t";
+ enum __delete_size_mangle = "_ZdlPvm";
+ enum __new_align_mangle = "_ZnwmSt11align_val_t";
+ enum __new_aligned_nothrow_mangle = "_ZnwmSt11align_val_tRKSt9nothrow_t";
+ enum __delete_align_mangle = "_ZdlPvSt11align_val_t";
+ enum __delete_align_nothrow_mangle = "_ZdlPvSt11align_val_tRKSt9nothrow_t";
+ enum __delete_size_align_mangle = "_ZdlPvmSt11align_val_t";
+ }
+ else
+ {
+ enum __new_mangle = "_Znwj";
+ enum __new_nothrow_mangle = "_ZnwjRKSt9nothrow_t";
+ enum __delete_mangle = "_ZdlPv";
+ enum __delete_nothrow_mangle = "_ZdlPvRKSt9nothrow_t";
+ enum __delete_size_mangle = "_ZdlPvj";
+ enum __new_align_mangle = "_ZnwjSt11align_val_t";
+ enum __new_aligned_nothrow_mangle = "_ZnwjSt11align_val_tRKSt9nothrow_t";
+ enum __delete_align_mangle = "_ZdlPvSt11align_val_t";
+ enum __delete_align_nothrow_mangle = "_ZdlPvSt11align_val_tRKSt9nothrow_t";
+ enum __delete_size_align_mangle = "_ZdlPvjSt11align_val_t";
+ }
+}
diff --git a/libphobos/libdruntime/core/stdcpp/string.d b/libphobos/libdruntime/core/stdcpp/string.d
new file mode 100644
index 00000000000..1cdb0f40952
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/string.d
@@ -0,0 +1,2593 @@
+/**
+ * D header file for interaction with C++ std::string.
+ *
+ * Copyright: Copyright (c) 2019 D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Guillaume Chatelet
+ * Manu Evans
+ * Source: $(DRUNTIMESRC core/stdcpp/string.d)
+ */
+
+module core.stdcpp.string;
+
+import core.stdcpp.allocator;
+import core.stdcpp.xutility : StdNamespace;
+import core.stdc.stddef : wchar_t;
+
+version (OSX)
+ version = Darwin;
+else version (iOS)
+ version = Darwin;
+else version (TVOS)
+ version = Darwin;
+else version (WatchOS)
+ version = Darwin;
+
+version (Darwin)
+{
+ // Apple decided to rock a different ABI... good for them!
+ version = _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT;
+}
+
+version (CppRuntime_Gcc)
+{
+ version (_GLIBCXX_USE_CXX98_ABI)
+ {
+ private enum StringNamespace = "std";
+ version = __GTHREADS;
+ }
+ else
+ {
+ import core.internal.traits : AliasSeq;
+ private enum StringNamespace = AliasSeq!("std", "__cxx11");
+ }
+}
+else
+ alias StringNamespace = StdNamespace;
+
+enum DefaultConstruct { value }
+
+/// Constructor argument for default construction
+enum Default = DefaultConstruct();
+
+@nogc:
+
+/**
+ * Character traits classes specify character properties and provide specific
+ * semantics for certain operations on characters and sequences of characters.
+ */
+extern(C++, (StdNamespace)) struct char_traits(CharT)
+{
+ alias char_type = CharT;
+
+ static size_t length(const(char_type)* s) @trusted pure nothrow @nogc
+ {
+ static if (is(char_type == char) || is(char_type == ubyte))
+ {
+ import core.stdc.string : strlen;
+ return strlen(s);
+ }
+ else
+ {
+ size_t len = 0;
+ for (; *s != char_type(0); ++s)
+ ++len;
+ return len;
+ }
+ }
+
+ static char_type* move(char_type* s1, const char_type* s2, size_t n) @trusted pure nothrow @nogc
+ {
+ import core.stdc.string : memmove;
+ import core.stdc.wchar_ : wmemmove;
+ import core.stdc.stddef : wchar_t;
+
+ if (n == 0)
+ return s1;
+
+ version (CRuntime_Microsoft)
+ {
+ enum crt = __traits(getTargetInfo, "cppRuntimeLibrary");
+ static if (crt.length >= 6 && crt[0 .. 6] == "msvcrt")
+ enum use_wmemmove = false; // https://issues.dlang.org/show_bug.cgi?id=20456
+ else
+ enum use_wmemmove = true;
+ }
+ else
+ enum use_wmemmove = true;
+
+ static if (use_wmemmove
+ && (is(char_type == wchar_t)
+ || is(char_type == ushort) && wchar_t.sizeof == ushort.sizeof // Windows
+ || is(char_type == uint) && wchar_t.sizeof == uint.sizeof)) // POSIX
+ return cast(char_type*) wmemmove(s1, s2, n);
+ else
+ return cast(char_type*) memmove(s1, s2, n * char_type.sizeof);
+ }
+}
+
+// I don't think we can have these here, otherwise symbols are emit to druntime, and we don't want that...
+//alias std_string = basic_string!char;
+//alias std_u16string = basic_string!wchar; // TODO: can't mangle these yet either...
+//alias std_u32string = basic_string!dchar;
+//alias std_wstring = basic_string!wchar_t; // TODO: we can't mangle wchar_t properly (yet?)
+
+/**
+ * D language counterpart to C++ std::basic_string.
+ *
+ * C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/string/basic_string)
+ */
+extern(C++, class)
+extern(C++, (StringNamespace))
+struct basic_string(T, Traits = char_traits!T, Alloc = allocator!T)
+{
+extern(D):
+@nogc:
+
+ ///
+ enum size_type npos = size_type.max;
+
+ ///
+ alias size_type = size_t;
+ ///
+ alias difference_type = ptrdiff_t;
+ ///
+ alias value_type = T;
+ ///
+ alias traits_type = Traits;
+ ///
+ alias allocator_type = Alloc;
+ ///
+ alias pointer = value_type*;
+ ///
+ alias const_pointer = const(value_type)*;
+
+ ///
+ alias toString = as_array;
+
+ /// MSVC allocates on default initialisation in debug, which can't be modelled by D `struct`
+ @disable this();
+
+ ///
+ alias length = size;
+ ///
+ alias opDollar = length;
+ ///
+ bool empty() const nothrow @safe { return size() == 0; }
+
+ ///
+ size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const pure nothrow @safe @nogc { return [start, end]; }
+
+ ///
+ ref inout(T) opIndex(size_t index) inout pure nothrow @safe @nogc { return as_array[index]; }
+ ///
+ inout(T)[] opIndex(size_t[2] slice) inout pure nothrow @safe @nogc { return as_array[slice[0] .. slice[1]]; }
+ ///
+ inout(T)[] opIndex() inout pure nothrow @safe @nogc { return as_array(); }
+
+ /// Two `basic_string`s are equal if they represent the same sequence of code units.
+ bool opEquals(scope const ref basic_string s) const pure nothrow @safe { return as_array == s.as_array; }
+ /// ditto
+ bool opEquals(scope const T[] s) const pure nothrow @safe { return as_array == s; }
+
+ /// Performs lexicographical comparison.
+ int opCmp(scope const ref basic_string rhs) const pure nothrow @safe { return __cmp(as_array, rhs.as_array); }
+ /// ditto
+ int opCmp(scope const T[] rhs) const pure nothrow @safe { return __cmp(as_array, rhs); }
+
+ /// Hash to allow `basic_string`s to be used as keys for built-in associative arrays.
+ /// **The result will generally not be the same as C++ `std::hash<std::basic_string<T>>`.**
+ size_t toHash() const @nogc nothrow pure @safe { return .hashOf(as_array); }
+
+ ///
+ void clear() { eos(0); } // TODO: bounds-check
+ ///
+ void resize(size_type n, T c = T(0)) @trusted
+ {
+ if (n <= size())
+ eos(n);
+ else
+ append(n - size(), c);
+ }
+
+ ///
+ ref inout(T) front() inout nothrow @safe { return this[0]; }
+ ///
+ ref inout(T) back() inout nothrow @safe { return this[$-1]; }
+
+ ///
+ const(T)* c_str() const nothrow @safe { return data(); }
+
+ // Modifiers
+ ///
+ ref basic_string opAssign()(auto ref basic_string str) { return assign(str); }
+// ref basic_string assign(size_type n, T c);
+ ///
+ ref basic_string opAssign(const(T)[] str) { return assign(str); }
+ ///
+ ref basic_string opAssign(T c) { return assign((&c)[0 .. 1]); }
+
+ ///
+ ref basic_string opIndexAssign(T c, size_t index) { as_array[index] = c; return this; }
+ ///
+ ref basic_string opIndexAssign(T c, size_t[2] slice) { as_array[slice[0] .. slice[1]] = c; return this; }
+ ///
+ ref basic_string opIndexAssign(const(T)[] str, size_t[2] slice) { as_array[slice[0] .. slice[1]] = str[]; return this; }
+ ///
+ ref basic_string opIndexAssign(T c) { as_array[] = c; return this; }
+ ///
+ ref basic_string opIndexAssign(const(T)[] str) { as_array[] = str[]; return this; }
+
+ ///
+ ref basic_string opIndexOpAssign(string op)(T c, size_t index) { mixin("as_array[index] " ~ op ~ "= c;"); return this; }
+ ///
+ ref basic_string opIndexOpAssign(string op)(T c, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= c;"); return this; }
+ ///
+ ref basic_string opIndexOpAssign(string op)(const(T)[] str, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= str[];"); return this; }
+ ///
+ ref basic_string opIndexOpAssign(string op)(T c) { mixin("as_array[] " ~ op ~ "= c;"); return this; }
+ ///
+ ref basic_string opIndexOpAssign(string op)(const(T)[] str) { mixin("as_array[] " ~ op ~ "= str[];"); return this; }
+ ///
+ ref basic_string append(T c) { return append((&c)[0 .. 1]); }
+ ///
+ ref basic_string opOpAssign(string op : "~")(const(T)[] str) { return append(str); }
+ ///
+ ref basic_string opOpAssign(string op : "~")(T c) { return append((&c)[0 .. 1]); }
+
+ ///
+ ref basic_string insert(size_type pos, ref const(basic_string) str) { return insert(pos, str.data(), str.size()); }
+ ///
+ ref basic_string insert(size_type pos, ref const(basic_string) str, size_type subpos, size_type sublen) @trusted
+ {
+ const _strsz = str.size();
+ assert(subpos <= _strsz);
+// if (subpos > _strsz)
+// throw new RangeError("subpos exceeds length of str");
+ return insert(pos, str.data() + subpos, min(sublen, _strsz - subpos));
+ }
+ ///
+ ref basic_string insert(S : size_type)(S pos, const(T)* s)
+ {
+ // This overload is declared as a template to give precedence to the slice overload const(T)[] in case of conflict.
+ assert(s);
+ return insert(pos, s, traits_type.length(s));
+ }
+ ///
+ ref basic_string insert(size_type pos, const(T)[] s) { insert(pos, &s[0], s.length); return this; }
+
+ ///
+ ref basic_string erase(size_type pos = 0) // TODO: bounds-check
+ {
+// _My_data._Check_offset(pos);
+ eos(pos);
+ return this;
+ }
+ ///
+ ref basic_string erase(size_type pos, size_type len) // TODO: bounds-check
+ {
+// _My_data._Check_offset(pos);
+ T[] str = as_array();
+ size_type new_len = str.length - len;
+ this[pos .. new_len] = this[pos + len .. str.length]; // TODO: should be memmove!
+ eos(new_len);
+ return this;
+ }
+
+ ///
+ ref basic_string replace()(size_type pos, size_type len, auto ref basic_string str) { return replace(pos, len, str.data(), str.size()); }
+ ///
+ ref basic_string replace()(size_type pos, size_type len, auto ref basic_string str,
+ size_type subpos, size_type sublen=npos)
+ {
+ size_type strsz = str.size();
+ assert(subpos <= strsz);
+// if (subpos > strsz)
+// throw new RangeError("subpos exceeds size of str");
+ return replace(pos, len, str.data() + subpos, min(sublen, strsz - subpos));
+ }
+ ///
+ ref basic_string replace(size_type pos, size_type len, const(value_type)[] s) { return replace(pos, len, s.ptr, s.length); }
+ ///
+ ref basic_string replace(S : size_type)(S pos, size_type len, const(value_type)* s)
+ {
+ // This overload is declared as a template to give precedence to the slice overload const(T)[] in case of conflict.
+ assert(s !is null, "string::replace received null");
+ return replace(pos, len, s, traits_type.length(s));
+ }
+
+ ///
+ void push_back(T c) @trusted { append((&c)[0 .. 1]); }
+ ///
+ void pop_back() { erase(size() - 1); }
+
+ version (CppRuntime_Microsoft)
+ {
+ //----------------------------------------------------------------------------------
+ // Microsoft runtime
+ //----------------------------------------------------------------------------------
+
+ ///
+ this(DefaultConstruct) { _Alloc_proxy(); _Tidy_init(); }
+ ///
+ this(const(T)[] str) { _Alloc_proxy(); _Tidy_init(); assign(str); }
+ ///
+ this(const(T)[] str, ref const(allocator_type) al) { _Alloc_proxy(); _AssignAllocator(al); _Tidy_init(); assign(str); }
+ ///
+ this(this)
+ {
+ _Alloc_proxy();
+ if (_Get_data()._IsAllocated())
+ {
+ T[] _Str = _Get_data()._Mystr;
+ _Tidy_init();
+ assign(_Str);
+ }
+ }
+
+ ///
+ ~this() { _Tidy_deallocate(); }
+
+ ///
+ ref inout(Alloc) get_allocator() inout { return _Getal(); }
+
+ ///
+ size_type max_size() const nothrow @safe { return ((size_t.max / T.sizeof) - 1) / 2; } // HACK: clone the windows version precisely?
+
+ ///
+ size_type size() const nothrow @safe { return _Get_data()._Mysize; }
+ ///
+ size_type capacity() const nothrow @safe { return _Get_data()._Myres; }
+ ///
+ inout(T)* data() inout @safe { return _Get_data()._Myptr; }
+ ///
+ inout(T)[] as_array() inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
+ ///
+ ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; }
+
+ ///
+ ref basic_string assign(const(T)[] str)
+ {
+ size_type _Count = str.length;
+ auto _My_data = &_Get_data();
+ if (_Count <= _My_data._Myres)
+ {
+ T* _Old_ptr = _My_data._Myptr;
+ _My_data._Mysize = _Count;
+ _Old_ptr[0 .. _Count] = str[]; // TODO: this needs to be a memmove(), does that work here?
+ _Old_ptr[_Count] = T(0);
+ return this;
+ }
+ return _Reallocate_for(_Count, (T* _New_ptr, size_type _Count, const(T)* _Ptr) nothrow {
+ _New_ptr[0 .. _Count] = _Ptr[0 .. _Count];
+ _New_ptr[_Count] = T(0);
+ }, str.ptr);
+ }
+
+ ///
+ ref basic_string assign(const ref basic_string str)
+ {
+ if (&this != &str)
+ assign(str.as_array);
+ return this;
+ }
+
+ ///
+ ref basic_string append(const(T)[] str)
+ {
+ size_type _Count = str.length;
+ auto _My_data = &_Get_data();
+ size_type _Old_size = _My_data._Mysize;
+ if (_Count <= _My_data._Myres - _Old_size)
+ {
+ pointer _Old_ptr = _My_data._Myptr;
+ _My_data._Mysize = _Old_size + _Count;
+ _Old_ptr[_Old_size .. _Old_size + _Count] = str[]; // TODO: this needs to be a memmove(), does that work here?
+ _Old_ptr[_Old_size + _Count] = T(0);
+ return this;
+ }
+ return _Reallocate_grow_by(_Count, (T* _New_ptr, const(T)[] _Old_str, const(T)[] _Str) {
+ _New_ptr[0 .. _Old_str.length] = _Old_str[];
+ _New_ptr[_Old_str.length .. _Old_str.length + _Str.length] = _Str[];
+ _New_ptr[_Old_str.length + _Str.length] = T(0);
+ }, str);
+ }
+
+ ///
+ ref basic_string append(size_type n, T c)
+ {
+ alias _Count = n;
+ alias _Ch = c;
+ auto _My_data = &_Get_data();
+ const size_type _Old_size = _My_data._Mysize;
+ if (_Count <= _My_data._Myres - _Old_size)
+ {
+ _My_data._Mysize = _Old_size + _Count;
+ pointer _Old_ptr = _My_data._Myptr();
+ _Old_ptr[_Old_size .. _Old_size + _Count] = _Ch;
+ _Old_ptr[_Old_size + _Count] = T(0);
+ return this;
+ }
+
+ return _Reallocate_grow_by(_Count, (T* _New_ptr, const(T)[] _Old_str, size_type _Count, T _Ch) {
+ _New_ptr[0 .. _Old_str.length] = _Old_str[];
+ _New_ptr[_Old_str.length .. _Old_str.length + _Count] = _Ch;
+ _New_ptr[_Old_str.length + _Count] = T(0);
+ }, _Count, _Ch);
+ }
+
+ ///
+ void reserve(size_type _Newcap = 0)
+ {
+ // determine new minimum length of allocated storage
+
+ auto _My_data = &_Get_data();
+
+ if (_My_data._Mysize > _Newcap)
+ {
+ // requested capacity is not large enough for current size, ignore
+ return; // nothing to do
+ }
+
+ if (_My_data._Myres == _Newcap)
+ {
+ // we're already at the requested capacity
+ return; // nothing to do
+ }
+
+ if (_My_data._Myres < _Newcap)
+ {
+ // reallocate to grow
+ const size_type _Old_size = _My_data._Mysize;
+ _Reallocate_grow_by(
+ _Newcap - _Old_size, (T* _New_ptr, const(T)[] _Old_str) {
+ _New_ptr[0 .. _Old_str.length] = _Old_str[];
+ _New_ptr[_Old_str.length] = _Old_str.ptr[_Old_str.length];
+ });
+
+ _My_data._Mysize = _Old_size;
+ return;
+ }
+
+ if (_My_data._BUF_SIZE > _Newcap && _My_data._Large_string_engaged())
+ {
+ // deallocate everything; switch back to "small" mode
+ _Become_small();
+ return;
+ }
+
+ // ignore requests to reserve to [_BUF_SIZE, _Myres)
+ }
+
+ ///
+ void shrink_to_fit()
+ {
+ // reduce capacity
+
+ auto _My_data = &_Get_data();
+ if (!_My_data._Large_string_engaged())
+ {
+ // can't shrink from small mode
+ return;
+ }
+
+ if (_My_data._Mysize < _My_data._BUF_SIZE)
+ {
+ _Become_small();
+ return;
+ }
+
+ const size_type _Target_capacity = min(_My_data._Mysize | _My_data._ALLOC_MASK, max_size());
+ if (_Target_capacity < _My_data._Myres)
+ {
+ // worth shrinking, do it
+ auto _Al = &_Getal();
+ pointer _New_ptr = _Al.allocate(_Target_capacity + 1); // throws
+ _Base._Orphan_all();
+ _New_ptr[0 .. _My_data._Mysize + 1] = _My_data._Bx._Ptr[0 .. _My_data._Mysize + 1];
+ _Al.deallocate(_My_data._Bx._Ptr, _My_data._Myres + 1);
+ _My_data._Bx._Ptr = _New_ptr;
+ _My_data._Myres = _Target_capacity;
+ }
+ }
+
+ ///
+ ref basic_string insert(size_type pos, const(T)* s, size_type n)
+ {
+ // insert [_Ptr, _Ptr + _Count) at _Off
+ alias _Off = pos;
+ alias _Ptr = s;
+ alias _Count = n;
+ auto _My_data = &_Get_data();
+// _My_data._Check_offset(_Off);
+ const size_type _Old_size = _My_data._Mysize;
+ if (_Count <= _My_data._Myres - _Old_size)
+ {
+ _My_data._Mysize = _Old_size + _Count;
+ T* _Old_ptr = _My_data._Myptr();
+ T* _Insert_at = _Old_ptr + _Off;
+ // the range [_Ptr, _Ptr + _Ptr_shifted_after) is left alone by moving the suffix out,
+ // while the range [_Ptr + _Ptr_shifted_after, _Ptr + _Count) shifts down by _Count
+ size_type _Ptr_shifted_after;
+ if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size)
+ {
+ // inserted content is before the shifted region, or does not alias
+ _Ptr_shifted_after = _Count; // none of _Ptr's data shifts
+ }
+ else if (_Insert_at <= _Ptr)
+ {
+ // all of [_Ptr, _Ptr + _Count) shifts
+ _Ptr_shifted_after = 0;
+ }
+ else
+ {
+ // [_Ptr, _Ptr + _Count) contains _Insert_at, so only the part after _Insert_at shifts
+ _Ptr_shifted_after = cast(size_type)(_Insert_at - _Ptr);
+ }
+
+ _Traits.move(_Insert_at + _Count, _Insert_at, _Old_size - _Off + 1); // move suffix + null down
+ _Insert_at[0 .. _Ptr_shifted_after] = _Ptr[0 .. _Ptr_shifted_after];
+ (_Insert_at + _Ptr_shifted_after)[0 .. _Count - _Ptr_shifted_after] = (_Ptr + _Count + _Ptr_shifted_after)[0 .. _Count - _Ptr_shifted_after];
+ return this;
+ }
+
+ return _Reallocate_grow_by(
+ _Count,
+ (T* _New_ptr, const(T)[] _Old_str, size_type _Off, const(T)* _Ptr, size_type _Count) {
+ _New_ptr[0 .. _Off] = _Old_str[0 .. _Off];
+ _New_ptr[_Off .. _Off + _Count] = _Ptr[0 .. _Count];
+ _New_ptr[_Off + _Count .. _Old_str.length + _Count + 1] = _Old_str.ptr[_Off .. _Old_str.length + 1];
+ },
+ _Off, _Ptr, _Count);
+ }
+
+ ///
+ ref basic_string insert(size_type pos, size_type n, T c)
+ {
+ // insert _Count * _Ch at _Off
+ alias _Off = pos;
+ alias _Count = n;
+ alias _Ch = c;
+ auto _My_data = &_Get_data();
+// _My_data._Check_offset(_Off);
+ const size_type _Old_size = _My_data._Mysize;
+ if (_Count <= _My_data._Myres - _Old_size)
+ {
+ _My_data._Mysize = _Old_size + _Count;
+ T* _Old_ptr = _My_data._Myptr();
+ T* _Insert_at = _Old_ptr + _Off;
+ _Traits.move(_Insert_at + _Count, _Insert_at, _Old_size - _Off + 1); // move suffix + null down
+ _Insert_at[0 .. _Count] = _Ch; // fill hole
+ return this;
+ }
+
+ return _Reallocate_grow_by(
+ _Count,
+ (T* _New_ptr, const(T)[] _Old_str, size_type _Off, size_type _Count, T _Ch)
+ {
+ _New_ptr[0 .. _Off] = _Old_str[0 .. _Off];
+ _New_ptr[_Off .. _Off + _Count] = _Ch;
+ _New_ptr[_Off + _Count .. _Old_str.length + 1] = _Old_str.ptr[_Off .. _Old_str.length + 1];
+ },
+ _Off, _Count, _Ch);
+ }
+
+ ///
+ ref basic_string replace(size_type pos, size_type len, const(T)* s, size_type slen)
+ {
+ // replace [_Off, _Off + _N0) with [_Ptr, _Ptr + _Count)
+ alias _Off = pos;
+ alias _N0 = len;
+ alias _Ptr = s;
+ alias _Count = slen;
+ auto _My_data = &_Get_data();
+// _Mypair._Myval2._Check_offset(_Off);
+ _N0 = _My_data._Clamp_suffix_size(_Off, _N0);
+ if (_N0 == _Count)
+ {
+ // size doesn't change, so a single move does the trick
+ _Traits.move(_My_data._Myptr() + _Off, _Ptr, _Count);
+ return this;
+ }
+
+ const size_type _Old_size = _My_data._Mysize;
+ const size_type _Suffix_size = _Old_size - _N0 - _Off + 1;
+ if (_Count < _N0)
+ {
+ // suffix shifts backwards; we don't have to move anything out of the way
+ _My_data._Mysize = _Old_size - (_N0 - _Count);
+ T* _Old_ptr = _My_data._Myptr();
+ T* _Insert_at = _Old_ptr + _Off;
+ _Traits.move(_Insert_at, _Ptr, _Count);
+ _Traits.move(_Insert_at + _Count, _Insert_at + _N0, _Suffix_size);
+ return this;
+ }
+
+ const size_type _Growth = cast(size_type)(_Count - _N0);
+ if (_Growth <= _My_data._Myres - _Old_size)
+ {
+ // growth fits
+ _My_data._Mysize = _Old_size + _Growth;
+ T* _Old_ptr = _My_data._Myptr();
+ T* _Insert_at = _Old_ptr + _Off;
+ T* _Suffix_at = _Insert_at + _N0;
+
+ size_type _Ptr_shifted_after; // see rationale in insert
+ if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size)
+ _Ptr_shifted_after = _Count;
+ else if (_Suffix_at <= _Ptr)
+ _Ptr_shifted_after = 0;
+ else
+ _Ptr_shifted_after = cast(size_type)(_Suffix_at - _Ptr);
+
+ _Traits.move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size);
+ // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole;
+ // this case doesn't occur in insert because the new content must come from outside the removed
+ // content there (because in insert there is no removed content)
+ _Traits.move(_Insert_at, _Ptr, _Ptr_shifted_after);
+ // the next case can be copy, because it comes from the chunk moved out of the way in the
+ // first move, and the hole we're filling can't alias the chunk we moved out of the way
+ _Insert_at[_Ptr_shifted_after .. _Count] = _Ptr[_Growth + _Ptr_shifted_after .. _Growth + _Count];
+ return this;
+ }
+
+ return _Reallocate_grow_by(
+ _Growth,
+ (T* _New_ptr, const(T)[] _Old_str, size_type _Off, size_type _N0, const(T)* _Ptr, size_type _Count) {
+ _New_ptr[0 .. _Off] = _Old_str[0 .. _Off];
+ _New_ptr[_Off .. _Count] = _Ptr[0 .. _Count];
+ const __n = _Old_str.length - _N0 - _Off + 1;
+ (_New_ptr + _Off + _Count)[0 .. __n] = (_Old_str.ptr + _Off + _N0)[0 .. __n];
+ },
+ _Off, _N0, _Ptr, _Count);
+ }
+
+ ///
+ ref basic_string replace(size_type _Off, size_type _N0, size_type _Count, T _Ch)
+ {
+ // replace [_Off, _Off + _N0) with _Count * _Ch
+ auto _My_data = &_Get_data();
+// _My_data._Check_offset(_Off);
+ _N0 = _My_data._Clamp_suffix_size(_Off, _N0);
+ if (_Count == _N0)
+ {
+ _My_data._Myptr()[_Off .. _Off + _Count] = _Ch;
+ return this;
+ }
+
+ const size_type _Old_size = _My_data._Mysize;
+ if (_Count < _N0 || _Count - _N0 <= _My_data._Myres - _Old_size)
+ {
+ // either we are shrinking, or the growth fits
+ _My_data._Mysize = _Old_size + _Count - _N0; // may temporarily overflow;
+ // OK because size_type must be unsigned
+ T* _Old_ptr = _My_data._Myptr();
+ T* _Insert_at = _Old_ptr + _Off;
+ _Traits.move(_Insert_at + _Count, _Insert_at + _N0, _Old_size - _N0 - _Off + 1);
+ _Insert_at[0 .. _Count] = _Ch;
+ return this;
+ }
+
+ return _Reallocate_grow_by(
+ _Count - _N0,
+ (T* _New_ptr, const(T)[] _Old_str, size_type _Off, size_type _N0, size_type _Count, T _Ch) {
+ _New_ptr[0 .. _Off] = _Old_str[0 .. _Off];
+ _New_ptr[_Off .. _Off + _Count] = _Ch;
+ const __n = _Old_str.length - _N0 - _Off + 1;
+ (_New_ptr + _Off + _Count)[0 .. __n] = (_Old_str.ptr + _Off + _N0)[0 .. __n];
+ },
+ _Off, _N0, _Count, _Ch);
+ }
+
+ ///
+ void swap(ref basic_string _Right)
+ {
+ import core.internal.lifetime : swap;
+ import core.stdcpp.type_traits : is_empty;
+
+ if (&this != &_Right)
+ {
+ static if (!is_empty!allocator_type.value
+ && allocator_traits!allocator_type.propagate_on_container_swap)
+ {
+ swap(_Getal(), _Right._Getal());
+ }
+
+ static if (_ITERATOR_DEBUG_LEVEL != 0)
+ {
+ auto _My_data = &_Get_data();
+ const bool _My_large = _My_data._Large_string_engaged();
+ const bool _Right_large = _Right._Get_data()._Large_string_engaged();
+ if (!_My_large)
+ _Base._Orphan_all();
+
+ if (!_Right_large)
+ _Right._Base._Orphan_all();
+
+ if (_My_large || _Right_large)
+ _My_data._Base._Swap_proxy_and_iterators(_Right._Get_data()._Base);
+ } // _ITERATOR_DEBUG_LEVEL != 0
+ }
+
+ _Swap_data!_Can_memcpy_val(_Right);
+ }
+
+ private:
+ import core.stdcpp.xutility : MSVCLinkDirectives;
+ import core.stdcpp.xutility : _Container_base;
+
+ alias _Traits = traits_type;
+ alias _Scary_val = _String_val!T;
+
+ enum bool _Can_memcpy_val = is(_Traits == char_traits!E, E) && is(pointer == U*, U);
+ // This offset skips over the _Container_base members, if any
+ enum size_t _Memcpy_val_offset = _Size_after_ebco_v!_Container_base;
+ enum size_t _Memcpy_val_size = _Scary_val.sizeof - _Memcpy_val_offset;
+
+ // Make sure the object files wont link against mismatching objects
+ mixin MSVCLinkDirectives!true;
+
+ pragma (inline, true)
+ {
+ void eos(size_type offset) nothrow { _Get_data()._Myptr[_Get_data()._Mysize = offset] = T(0); }
+
+ ref inout(_Base.Alloc) _Getal() inout nothrow @safe { return _Base._Mypair._Myval1; }
+ ref inout(_Base.ValTy) _Get_data() inout nothrow @safe { return _Base._Mypair._Myval2; }
+ }
+
+ void _Alloc_proxy() nothrow
+ {
+ static if (_ITERATOR_DEBUG_LEVEL > 0)
+ _Base._Alloc_proxy();
+ }
+
+ void _AssignAllocator(ref const(allocator_type) al) nothrow
+ {
+ static if (_Base._Mypair._HasFirst)
+ _Getal() = al;
+ }
+
+ void _Become_small()
+ {
+ // release any held storage and return to small string mode
+ // pre: *this is in large string mode
+ // pre: this is small enough to return to small string mode
+ auto _My_data = &_Get_data();
+ _Base._Orphan_all();
+ pointer _Ptr = _My_data._Bx._Ptr;
+ auto _Al = &_Getal();
+ _My_data._Bx._Buf[0 .. _My_data._Mysize + 1] = _Ptr[0 .. _My_data._Mysize + 1];
+ _Al.deallocate(_Ptr, _My_data._Myres + 1);
+ _My_data._Myres = _My_data._BUF_SIZE - 1;
+ }
+
+ void _Tidy_init() nothrow
+ {
+ auto _My_data = &_Get_data();
+ _My_data._Mysize = 0;
+ _My_data._Myres = _My_data._BUF_SIZE - 1;
+ _My_data._Bx._Buf[0] = T(0);
+ }
+
+ size_type _Calculate_growth(size_type _Requested) const nothrow
+ {
+ auto _My_data = &_Get_data();
+ size_type _Masked = _Requested | _My_data._ALLOC_MASK;
+ size_type _Old = _My_data._Myres;
+ size_type _Expanded = _Old + _Old / 2;
+ return _Masked > _Expanded ? _Masked : _Expanded;
+ }
+
+ ref basic_string _Reallocate_for(_ArgTys...)(size_type _New_size, void function(pointer, size_type, _ArgTys) nothrow @nogc _Fn, _ArgTys _Args)
+ {
+ auto _My_data = &_Get_data();
+ size_type _Old_capacity = _My_data._Myres;
+ size_type _New_capacity = _Calculate_growth(_New_size);
+ auto _Al = &_Getal();
+ pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws
+ _Base._Orphan_all();
+ _My_data._Mysize = _New_size;
+ _My_data._Myres = _New_capacity;
+ _Fn(_New_ptr, _New_size, _Args);
+ if (_My_data._BUF_SIZE <= _Old_capacity)
+ _Al.deallocate(_My_data._Bx._Ptr, _Old_capacity + 1);
+ _My_data._Bx._Ptr = _New_ptr;
+ return this;
+ }
+
+ ref basic_string _Reallocate_grow_by(_ArgTys...)(size_type _Size_increase, void function(pointer, const(T)[], _ArgTys) nothrow @nogc _Fn, _ArgTys _Args)
+ {
+ auto _My_data = &_Get_data();
+ size_type _Old_size = _My_data._Mysize;
+ size_type _New_size = _Old_size + _Size_increase;
+ size_type _Old_capacity = _My_data._Myres;
+ size_type _New_capacity = _Calculate_growth(_New_size);
+ auto _Al = &_Getal();
+ pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws
+ _Base._Orphan_all();
+ _My_data._Mysize = _New_size;
+ _My_data._Myres = _New_capacity;
+ if (_My_data._BUF_SIZE <= _Old_capacity)
+ {
+ pointer _Old_ptr = _My_data._Bx._Ptr;
+ _Fn(_New_ptr, _Old_ptr[0 .. _Old_size], _Args);
+ _Al.deallocate(_Old_ptr, _Old_capacity + 1);
+ }
+ else
+ _Fn(_New_ptr, _My_data._Bx._Buf[0 .. _Old_size], _Args);
+ _My_data._Bx._Ptr = _New_ptr;
+ return this;
+ }
+
+ void _Tidy_deallocate()
+ {
+ _Base._Orphan_all();
+ auto _My_data = &_Get_data();
+ if (_My_data._BUF_SIZE <= _My_data._Myres)
+ {
+ pointer _Ptr = _My_data._Bx._Ptr;
+ auto _Al = &_Getal();
+ _Al.deallocate(_Ptr, _My_data._Myres + 1);
+ }
+ _My_data._Mysize = 0;
+ _My_data._Myres = _My_data._BUF_SIZE - 1;
+ _My_data._Bx._Buf[0] = T(0);
+ }
+
+ void _Swap_data(bool _memcpy : true)(ref basic_string _Right)
+ {
+ import core.stdc.string : memcpy;
+
+ // exchange _String_val instances with _Right, memcpy optimization
+ auto _My_data = &_Get_data();
+ auto _My_data_mem = cast(ubyte*)_My_data + _Memcpy_val_offset;
+ auto _Right_data_mem = cast(ubyte*)(&_Right._Get_data()) + _Memcpy_val_offset;
+ ubyte[_Memcpy_val_size] _Temp_mem;
+ memcpy(_Temp_mem.ptr, _My_data_mem, _Memcpy_val_size);
+ memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size);
+ memcpy(_Right_data_mem, _Temp_mem.ptr, _Memcpy_val_size);
+ }
+
+ void _Swap_data(bool _memcpy : false)(ref basic_string _Right)
+ {
+ import core.lifetime : swap;
+
+ // exchange _String_val instances with _Right, general case
+ auto _My_data = &_Get_data();
+ auto _Right_data = &_Right._Get_data();
+ const bool _My_large = _My_data._Large_string_engaged();
+ const bool _Right_large = _Right_data._Large_string_engaged();
+ if (_My_large)
+ {
+ if (_Right_large) // swap buffers, iterators preserved
+ swap(_My_data._Bx._Ptr, _Right_data._Bx._Ptr);
+ else // swap large with small
+ _Swap_bx_large_with_small(*_My_data, *_Right_data);
+ }
+ else
+ {
+ if (_Right_large) // swap small with large
+ _Swap_bx_large_with_small(*_Right_data, *_My_data);
+ else
+ {
+ enum _BUF_SIZE = _My_data._BUF_SIZE;
+ T[_BUF_SIZE] _Temp_buf;
+ _Temp_buf[0 .. _BUF_SIZE] = _My_data._Bx._Buf[0 .. _BUF_SIZE];
+ _My_data._Bx._Buf[0 .. _BUF_SIZE] = _Right_data._Bx._Buf[0 .. _BUF_SIZE];
+ _Right_data._Bx._Buf[0 .. _BUF_SIZE] = _Temp_buf[0 .. _BUF_SIZE];
+ }
+ }
+
+ swap(_My_data._Mysize, _Right_data._Mysize);
+ swap(_My_data._Myres, _Right_data._Myres);
+ }
+
+ void _Swap_bx_large_with_small(ref _Scary_val _Starts_large, ref _Scary_val _Starts_small)
+ {
+ // exchange a string in large mode with one in small mode
+ pointer _Ptr = _Starts_large._Bx._Ptr;
+ _Starts_large._Bx._Buf[] = _Starts_small._Bx._Buf[];
+ _Starts_small._Bx._Ptr = _Ptr;
+ }
+
+ _String_alloc!(_String_base_types!(T, Alloc)) _Base;
+ }
+ else version (CppRuntime_Gcc)
+ {
+ version (_GLIBCXX_USE_CXX98_ABI)
+ {
+ //----------------------------------------------------------------------------------
+ // Old GCC/libstdc++ ref-counted implementation
+ //----------------------------------------------------------------------------------
+
+ ///
+ this(DefaultConstruct)
+ {
+ version (_GLIBCXX_FULLY_DYNAMIC_STRING)
+ static_assert(false, "DO WE NEED THIS?");
+ else
+ _M_data = _S_empty_rep()._M_refdata();
+ }
+ ///
+ this(const(T)[] str, ref const(allocator_type) al) { _M_assign_allocator(al); this(str); }
+ ///
+ this(const(T)[] str)
+ {
+ _M_data = _S_construct(str.ptr, str.ptr + str.length, _M_get_allocator);
+ }
+ ///
+ this(const ref basic_string str)
+ {
+ import core.stdcpp.type_traits : is_empty;
+
+ static if (!is_empty!allocator_type.value)
+ _M_Alloc = str.get_allocator();
+ _M_data = str._M_rep()._M_grab(get_allocator(), str.get_allocator());
+ }
+
+ ///
+ ~this() { _M_rep()._M_dispose(get_allocator()); }
+
+ ///
+ ref inout(Alloc) get_allocator() inout { return _M_get_allocator(); }
+
+ ///
+ size_type max_size() const nothrow @safe { return _Rep._S_max_size; }
+
+ ///
+ size_type size() const nothrow @safe { return _M_rep()._M_length; }
+ ///
+ size_type capacity() const nothrow { return _M_rep()._M_capacity; }
+ ///
+ inout(T)* data() inout @safe { return _M_data; }
+ ///
+ inout(T)[] as_array() inout nothrow @trusted { return _M_data[0 .. _M_rep()._M_length]; }
+ ///
+ ref inout(T) at(size_type i) inout nothrow { return _M_data[0 .. _M_rep()._M_length][i]; }
+
+ ///
+ ref basic_string assign(const(T)[] str)
+ {
+ const(T)* __s = str.ptr;
+ size_t __n = str.length;
+// __glibcxx_requires_string_len(__s, __n);
+ _M_check_length(size(), __n, "basic_string::assign");
+ if (_M_disjunct(__s) || _M_rep()._M_is_shared())
+ return _M_replace_safe(size_type(0), this.size(), __s, __n);
+ else
+ {
+ const size_type __pos = __s - _M_data;
+ if (__pos >= __n)
+ _S_copy(_M_data, __s, __n);
+ else if (__pos)
+ _S_move(_M_data, __s, __n);
+ _M_rep()._M_set_length_and_sharable(__n);
+ return this;
+ }
+ }
+
+ ///
+ ref basic_string assign(const ref basic_string str)
+ {
+ if (_M_rep() != str._M_rep())
+ {
+ // XXX MT
+ allocator_type __a = this.get_allocator();
+ T* __tmp = str._M_rep()._M_grab(__a, str.get_allocator());
+ _M_rep()._M_dispose(__a);
+ _M_data = __tmp;
+ }
+ return this;
+ }
+
+ ///
+ ref basic_string append(const(T)[] str)
+ {
+ const(T)* __s = str.ptr;
+ size_t __n = str.length;
+// __glibcxx_requires_string_len(__s, __n);
+ if (__n)
+ {
+ _M_check_length(size_type(0), __n, "basic_string::append");
+ const size_type __len = __n + size();
+ if (__len > capacity() || _M_rep()._M_is_shared())
+ {
+ if (_M_disjunct(__s))
+ reserve(__len);
+ else
+ {
+ const size_type __off = __s - _M_data;
+ reserve(__len);
+ __s = _M_data + __off;
+ }
+ }
+ _S_copy(_M_data + size(), __s, __n);
+ _M_rep()._M_set_length_and_sharable(__len);
+ }
+ return this;
+ }
+
+ ///
+ ref basic_string append(size_type __n, T __c)
+ {
+ if (__n)
+ {
+ _M_check_length(size_type(0), __n, "basic_string::append");
+ const size_type __len = __n + size();
+ if (__len > capacity() || _M_rep()._M_is_shared())
+ reserve(__len);
+ const __sz = size();
+ _M_data[__sz .. __sz + __n] = __c;
+ _M_rep()._M_set_length_and_sharable(__len);
+ }
+ return this;
+ }
+
+ ///
+ void reserve(size_type __res = 0)
+ {
+ if (__res != capacity() || _M_rep()._M_is_shared())
+ {
+ // Make sure we don't shrink below the current size
+ if (__res < size())
+ __res = size();
+ allocator_type __a = get_allocator();
+ T* __tmp = _M_rep()._M_clone(__a, __res - size());
+ _M_rep()._M_dispose(__a);
+ _M_data = __tmp;
+ }
+ }
+
+ ///
+ void shrink_to_fit() nothrow
+ {
+ if (capacity() > size())
+ {
+ try reserve(0);
+ catch (Throwable) {}
+ }
+ }
+
+ ///
+ ref basic_string insert(size_type __pos, const(T)* __s, size_type __n)
+ {
+// __glibcxx_requires_string_len(__s, __n);
+ cast(void) _M_check(__pos, "basic_string::insert");
+ _M_check_length(size_type(0), __n, "basic_string::insert");
+ if (_M_disjunct(__s) || _M_rep()._M_is_shared())
+ return _M_replace_safe(__pos, size_type(0), __s, __n);
+ else
+ {
+ // Work in-place.
+ const size_type __off = __s - _M_data;
+ _M_mutate(__pos, 0, __n);
+ __s = _M_data + __off;
+ T* __p = _M_data + __pos;
+ if (__s + __n <= __p)
+ __p[0 .. __n] = __s[0 .. __n];
+ else if (__s >= __p)
+ __p[0 .. __n] = (__s + __n)[0 .. __n];
+ else
+ {
+ const size_type __nleft = __p - __s;
+ __p[0 .. __nleft] = __s[0.. __nleft];
+ (__p + __nleft)[0 .. __n - __nleft] = (__p + __n)[0 .. __n - __nleft];
+ }
+ return this;
+ }
+ }
+
+ ///
+ ref basic_string insert(size_type pos, size_type n, T c)
+ {
+ return _M_replace_aux(_M_check(pos, "basic_string::insert"), size_type(0), n, c);
+ }
+
+ ///
+ ref basic_string replace(size_type __pos, size_type __n1, const(T)* __s, size_type __n2)
+ {
+// __glibcxx_requires_string_len(__s, __n2);
+ cast(void) _M_check(__pos, "basic_string::replace");
+ __n1 = _M_limit(__pos, __n1);
+ _M_check_length(__n1, __n2, "basic_string::replace");
+ bool __left;
+ if (_M_disjunct(__s) || _M_rep()._M_is_shared())
+ return _M_replace_safe(__pos, __n1, __s, __n2);
+ else if ((__left = __s + __n2 <= _M_data + __pos) == true || _M_data + __pos + __n1 <= __s)
+ {
+ // Work in-place: non-overlapping case.
+ size_type __off = __s - _M_data;
+ __left ? __off : (__off += __n2 - __n1);
+ _M_mutate(__pos, __n1, __n2);
+ (_M_data + __pos)[0 .. __n2] = (_M_data + __off)[0 .. __n2];
+ return this;
+ }
+ else
+ {
+ // Todo: overlapping case.
+ auto __tmp = basic_string(__s[0 .. __n2]);
+ return _M_replace_safe(__pos, __n1, __tmp._M_data, __n2);
+ }
+ }
+
+ ///
+ ref basic_string replace(size_type pos, size_type n1, size_type n2, T c)
+ {
+ return _M_replace_aux(_M_check(pos, "basic_string::replace"), _M_limit(pos, n1), n2, c);
+ }
+
+ ///
+ void swap(ref basic_string __s)
+ {
+ if (_M_rep()._M_is_leaked())
+ _M_rep()._M_set_sharable();
+ if (__s._M_rep()._M_is_leaked())
+ __s._M_rep()._M_set_sharable();
+ if (this.get_allocator() == __s.get_allocator())
+ {
+ T* __tmp = _M_data;
+ _M_data = __s._M_data;
+ __s._M_data = __tmp;
+ }
+ // The code below can usually be optimized away.
+ else
+ {
+ import core.lifetime : move;
+
+ auto __tmp1 = basic_string(this[], __s.get_allocator());
+ auto __tmp2 = basic_string(__s[], this.get_allocator());
+ this = move(__tmp2);
+ __s = move(__tmp1);
+ }
+ }
+
+ private:
+ import core.stdcpp.type_traits : is_empty;
+
+ version (__GTHREADS)
+ {
+ import core.atomic;
+ alias _Atomic_word = int; // should we use atomic!int?
+ }
+ else
+ alias _Atomic_word = int;
+
+ struct _Rep_base
+ {
+ size_type _M_length;
+ size_type _M_capacity;
+ _Atomic_word _M_refcount;
+ }
+
+ struct _Rep
+ {
+ _Rep_base base;
+ alias base this;
+
+ alias _Raw_bytes_alloc = Alloc.rebind!char;
+
+ enum size_type _S_max_size = (((npos - _Rep_base.sizeof) / T.sizeof) - 1) / 4;
+ enum T _S_terminal = T(0);
+
+ __gshared size_type[(_Rep_base.sizeof + T.sizeof + size_type.sizeof - 1) / size_type.sizeof] _S_empty_rep_storage;
+
+ static ref _Rep _S_empty_rep() nothrow @trusted { return *cast(_Rep*)_S_empty_rep_storage.ptr; }
+
+ void _M_set_sharable() nothrow
+ {
+ _M_refcount = 0;
+ }
+
+ void _M_set_length_and_sharable(size_type __n) nothrow
+ {
+ if (&this != &_S_empty_rep())
+ {
+ _M_set_sharable();
+ _M_length = __n;
+ _M_refdata()[__n] = _S_terminal;
+ }
+ }
+
+ bool _M_is_leaked() const nothrow
+ {
+ import core.atomic : atomicLoad;
+
+ version (__GTHREADS)
+ return atomicLoad!(MemoryOrder.raw)(this._M_refcount) < 0;
+ else
+ return _M_refcount < 0;
+ }
+//
+ bool _M_is_shared() const nothrow
+ {
+ import core.atomic : atomicLoad;
+
+ version (__GTHREADS)
+ return atomicLoad!(MemoryOrder.acq)(this._M_refcount) > 0;
+ else
+ return _M_refcount > 0;
+ }
+
+ T* _M_refdata() nothrow @trusted { return cast(T*)(&this + 1); }
+
+ T* _M_grab(ref allocator_type __alloc1, const ref allocator_type __alloc2)
+ {
+ return (!_M_is_leaked() && __alloc1 == __alloc2)
+ ? _M_refcopy() : _M_clone(__alloc1);
+ }
+
+ static _Rep* _S_create(size_type __capacity, size_type __old_capacity, ref Alloc __alloc)
+ {
+ assert(__capacity <= _S_max_size);
+// if (__capacity > _S_max_size)
+// __throw_length_error(__N("basic_string::_S_create"));
+
+ enum __pagesize = 4096;
+ enum __malloc_header_size = 4 * pointer.sizeof;
+
+ if (__capacity > __old_capacity && __capacity < 2 * __old_capacity)
+ __capacity = 2 * __old_capacity;
+
+ size_type __size = (__capacity + 1) * T.sizeof + _Rep.sizeof;
+
+ const size_type __adj_size = __size + __malloc_header_size;
+ if (__adj_size > __pagesize && __capacity > __old_capacity)
+ {
+ const size_type __extra = __pagesize - __adj_size % __pagesize;
+ __capacity += __extra / T.sizeof;
+ if (__capacity > _S_max_size)
+ __capacity = _S_max_size;
+ __size = (__capacity + 1) * T.sizeof + _Rep.sizeof;
+ }
+
+ _Rep* __p = cast(_Rep*)_Raw_bytes_alloc(__alloc).allocate(__size);
+ *__p = _Rep.init;
+ __p._M_capacity = __capacity;
+ __p._M_set_sharable();
+ return __p;
+ }
+
+ void _M_dispose(ref Alloc __a)
+ {
+ import core.stdcpp.xutility : __exchange_and_add_dispatch;
+
+ if (&this != &_S_empty_rep())
+ {
+ // Be race-detector-friendly. For more info see bits/c++config.
+// _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&this._M_refcount);
+ // Decrement of _M_refcount is acq_rel, because:
+ // - all but last decrements need to release to synchronize with
+ // the last decrement that will delete the object.
+ // - the last decrement needs to acquire to synchronize with
+ // all the previous decrements.
+ // - last but one decrement needs to release to synchronize with
+ // the acquire load in _M_is_shared that will conclude that
+ // the object is not shared anymore.
+ if (__exchange_and_add_dispatch(&this._M_refcount, -1) <= 0)
+ {
+// _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&this._M_refcount);
+ _M_destroy(__a);
+ }
+ }
+ }
+
+ void _M_destroy(ref Alloc __a)
+ {
+ const size_type __size = _Rep_base.sizeof + (_M_capacity + 1) * T.sizeof;
+ _Raw_bytes_alloc(__a).deallocate(cast(char*)&this, __size);
+ }
+
+ T* _M_refcopy() nothrow @trusted
+ {
+ import core.stdcpp.xutility : __atomic_add_dispatch;
+
+ if (&this != &_S_empty_rep())
+ __atomic_add_dispatch(&this._M_refcount, 1);
+ return _M_refdata();
+ // XXX MT
+ }
+
+ T* _M_clone(ref Alloc __alloc, size_type __res = 0)
+ {
+ const size_type __requested_cap = _M_length + __res;
+ _Rep* __r = _S_create(__requested_cap, _M_capacity, __alloc);
+ if (_M_length)
+ _S_copy(__r._M_refdata(), _M_refdata(), _M_length);
+
+ __r._M_set_length_and_sharable(_M_length);
+ return __r._M_refdata();
+ }
+ }
+
+ static if (!is_empty!allocator_type.value)
+ allocator_type _M_Alloc;
+ T* _M_p; // The actual data.
+
+ alias _M_data = _M_p;
+
+ pragma (inline, true)
+ {
+ void eos(size_type offset)
+ {
+ _M_mutate(offset, size() - offset, size_type(0));
+ }
+
+ ref inout(allocator_type) _M_get_allocator() inout
+ {
+ static if (!is_empty!allocator_type.value)
+ return _M_Alloc;
+ else
+ return *cast(inout(allocator_type)*)&this;
+ }
+
+ _Rep* _M_rep() const nothrow @trusted { return &(cast(_Rep*)_M_data)[-1]; }
+
+ size_type _M_limit(size_type __pos, size_type __off) const @safe nothrow @nogc pure
+ {
+ const bool __testoff = __off < size() - __pos;
+ return __testoff ? __off : size() - __pos;
+ }
+ }
+
+ size_type _M_check(size_type __pos, const char* __s) const
+ {
+ assert(__pos <= size());
+// if (__pos > size())
+// __throw_out_of_range_fmt(__N("%s: __pos (which is %zu) > "
+// "this->size() (which is %zu)"),
+// __s, __pos, this->size());
+ return __pos;
+ }
+
+ static ref _Rep _S_empty_rep() nothrow
+ {
+ return _Rep._S_empty_rep();
+ }
+
+ static T* _S_construct(const(T)* __beg, const(T)* __end, ref Alloc __a)
+ {
+ version (_GLIBCXX_FULLY_DYNAMIC_STRING) {} else
+ {
+ if (__beg == __end && __a == Alloc())
+ return _S_empty_rep()._M_refdata();
+ }
+
+ const size_type __dnew = __end - __beg;
+
+ _Rep* __r = _Rep._S_create(__dnew, size_type(0), __a);
+ _S_copy(__r._M_refdata(), __beg, __end - __beg);
+ __r._M_set_length_and_sharable(__dnew);
+ return __r._M_refdata();
+ }
+
+ ref basic_string _M_replace_safe(size_type __pos1, size_type __n1, const(T)* __s, size_type __n2)
+ {
+ _M_mutate(__pos1, __n1, __n2);
+ if (__n2)
+ _S_copy(_M_data + __pos1, __s, __n2);
+ return this;
+ }
+
+ ref basic_string _M_replace_aux(size_type __pos1, size_type __n1, size_type __n2, T __c)
+ {
+ _M_check_length(__n1, __n2, "basic_string::_M_replace_aux");
+ _M_mutate(__pos1, __n1, __n2);
+ if (__n2)
+ _M_data[__pos1 .. __pos1 + __n2] = __c;
+ return this;
+ }
+
+ void _M_mutate(size_type __pos, size_type __len1, size_type __len2)
+ {
+ const size_type __old_size = size();
+ const size_type __new_size = __old_size + __len2 - __len1;
+ const size_type __how_much = __old_size - __pos - __len1;
+
+ if (__new_size > capacity() || _M_rep()._M_is_shared())
+ {
+ allocator_type __a = get_allocator();
+ _Rep* __r = _Rep._S_create(__new_size, capacity(), __a);
+
+ if (__pos)
+ _S_copy(__r._M_refdata(), _M_data, __pos);
+ if (__how_much)
+ _S_copy(__r._M_refdata() + __pos + __len2, _M_data + __pos + __len1, __how_much);
+
+ allocator_type* __al = cast() &__a;
+ _M_rep()._M_dispose(*__al);
+ _M_data = __r._M_refdata();
+ }
+ else if (__how_much && __len1 != __len2)
+ _S_move(_M_data + __pos + __len2, _M_data + __pos + __len1, __how_much);
+ _M_rep()._M_set_length_and_sharable(__new_size);
+ }
+ }
+ else
+ {
+ pragma(msg, "libstdc++ std::__cxx11::basic_string is not yet supported; the struct contains an interior pointer which breaks D move semantics!");
+
+ //----------------------------------------------------------------------------------
+ // GCC/libstdc++ modern implementation
+ //----------------------------------------------------------------------------------
+
+ ///
+ this(DefaultConstruct) { _M_p = _M_local_data(); _M_set_length(0); }
+ ///
+ this(const(T)[] str, ref const(allocator_type) al) { _M_assign_allocator(al); this(str); }
+ ///
+ this(const(T)[] str)
+ {
+ _M_p = _M_local_data();
+ _M_construct(str.ptr, str.length);
+ }
+ ///
+ this(this)
+ {
+ assert(false);
+ // TODO: how do I know if it was local before?!
+ }
+
+ ///
+ ~this() { _M_dispose(); }
+
+ ///
+ ref inout(Alloc) get_allocator() inout { return _M_get_allocator(); }
+
+ ///
+ size_type max_size() const nothrow @safe { return ((size_t.max / T.sizeof) - 1) / 2; }
+
+ ///
+ size_type size() const nothrow @safe { return _M_string_length; }
+ ///
+ size_type capacity() const nothrow { return _M_is_local ? _S_local_capacity : _M_allocated_capacity; }
+ ///
+ inout(T)* data() inout @safe { return _M_data; }
+ ///
+ inout(T)[] as_array() inout nothrow @trusted { return _M_data[0 .. _M_string_length]; }
+ ///
+ ref inout(T) at(size_type i) inout nothrow { return _M_data[0 .. _M_string_length][i]; }
+
+ ///
+ ref basic_string assign(const(T)[] str)
+ {
+// __glibcxx_requires_string_len(str.ptr, str.length);
+ return _M_replace(size_type(0), size(), str.ptr, str.length);
+ }
+
+ ///
+ ref basic_string assign(const ref basic_string str)
+ {
+ if (&this != &str)
+ assign(str.as_array);
+ return this;
+ }
+
+ ///
+ ref basic_string append(const(T)[] str)
+ {
+// __glibcxx_requires_string_len(str.ptr, str.length);
+ _M_check_length(size_type(0), str.length, "basic_string::append");
+ return _M_append(str.ptr, str.length);
+ }
+
+ ///
+ ref basic_string append(size_type n, T c)
+ {
+ return _M_replace_aux(size(), size_type(0), n, c);
+ }
+
+ ///
+ void reserve(size_type __res = 0)
+ {
+ // Make sure we don't shrink below the current size.
+ if (__res < length())
+ __res = length();
+
+ const size_type __capacity = capacity();
+ if (__res != __capacity)
+ {
+ if (__res > __capacity || __res > size_type(_S_local_capacity))
+ {
+ pointer __tmp = _M_create(__res, __capacity);
+ _S_copy(__tmp, _M_data, length() + 1);
+ _M_dispose();
+ _M_data = __tmp;
+ _M_capacity = __res;
+ }
+ else if (!_M_is_local())
+ {
+ _S_copy(_M_local_data(), _M_data, length() + 1);
+ _M_destroy(__capacity);
+ _M_data = _M_local_data();
+ }
+ }
+ }
+
+ ///
+ void shrink_to_fit() nothrow
+ {
+ if (capacity() > size())
+ {
+ try reserve(0);
+ catch (Throwable) {}
+ }
+ }
+
+ ///
+ ref basic_string insert(size_type pos, const(T)* s, size_type n)
+ {
+ return replace(pos, size_type(0), s, n);
+ }
+
+ ///
+ ref basic_string insert(size_type pos, size_type n, T c)
+ {
+ return _M_replace_aux(_M_check(pos, "basic_string::insert"), size_type(0), n, c);
+ }
+
+ ///
+ ref basic_string replace(size_type pos, size_type n1, const(T)* s, size_type n2)
+ {
+// __glibcxx_requires_string_len(s, n2);
+ return _M_replace(_M_check(pos, "basic_string::replace"), _M_limit(pos, n1), s, n2);
+ }
+
+ ///
+ ref basic_string replace(size_type pos, size_type n1, size_type n2, T c)
+ {
+ return _M_replace_aux(_M_check(pos, "basic_string::replace"), _M_limit(pos, n1), n2, c);
+ }
+
+ ///
+ void swap(ref basic_string __s)
+ {
+ if (&this == &__s)
+ return;
+
+ __alloc_on_swap(__s._M_get_allocator());
+
+ if (_M_is_local())
+ {
+ if (__s._M_is_local())
+ {
+ if (length() && __s.length())
+ {
+ T[_S_local_capacity + 1] __tmp_data;
+ __tmp_data[] = __s._M_local_buf[];
+ __s._M_local_buf[] = _M_local_buf[];
+ _M_local_buf[] = __tmp_data[];
+ }
+ else if (__s.length())
+ {
+ _M_local_buf[] = __s._M_local_buf[];
+ _M_length = __s.length();
+ __s._M_set_length(0);
+ return;
+ }
+ else if (length())
+ {
+ __s._M_local_buf[] = _M_local_buf[];
+ __s._M_length = length();
+ _M_set_length(0);
+ return;
+ }
+ }
+ else
+ {
+ const size_type __tmp_capacity = __s._M_allocated_capacity;
+ __s._M_local_buf[] = _M_local_buf[];
+ _M_data = __s._M_data;
+ __s._M_data = __s._M_local_buf.ptr;
+ _M_capacity = __tmp_capacity;
+ }
+ }
+ else
+ {
+ const size_type __tmp_capacity = _M_allocated_capacity;
+ if (__s._M_is_local())
+ {
+ _M_local_buf[] = __s._M_local_buf[];
+ __s._M_data = _M_data;
+ _M_data = _M_local_buf.ptr;
+ }
+ else
+ {
+ pointer __tmp_ptr = _M_data;
+ _M_data = __s._M_data;
+ __s._M_data = __tmp_ptr;
+ _M_capacity = __s._M_allocated_capacity;
+ }
+ __s._M_capacity = __tmp_capacity;
+ }
+
+ const size_type __tmp_length = length();
+ _M_length = __s.length();
+ __s._M_length = __tmp_length;
+ }
+
+ private:
+// import core.exception : RangeError;
+ import core.stdcpp.type_traits : is_empty;
+
+ static if (!is_empty!allocator_type.value)
+ allocator_type _M_Alloc;
+ pointer _M_p; // The actual data.
+ size_type _M_string_length;
+
+ enum size_type _S_local_capacity = 15 / T.sizeof;
+ union
+ {
+ T[_S_local_capacity + 1] _M_local_buf;
+ size_type _M_allocated_capacity;
+ }
+
+ alias _M_length = _M_string_length;
+ alias _M_capacity = _M_allocated_capacity;
+ alias _M_data = _M_p;
+
+ pragma (inline, true)
+ {
+ void eos(size_type offset) nothrow { _M_set_length(offset); }
+
+ inout(pointer) _M_local_data() inout { return _M_local_buf.ptr; }
+ bool _M_is_local() const { return _M_data == _M_local_data; }
+
+ ref inout(allocator_type) _M_get_allocator() inout
+ {
+ static if (!is_empty!allocator_type.value)
+ return _M_Alloc;
+ else
+ return *cast(inout(allocator_type)*)&this;
+ }
+
+ void _M_set_length(size_type __n)
+ {
+ _M_length = __n;
+ _M_data[__n] = T(0);
+ }
+
+ size_type _M_check(size_type __pos, const char* __s) const
+ {
+ assert(__pos <= size());
+// if (__pos > size())
+// __throw_out_of_range_fmt(__N("%s: __pos (which is %zu) > "
+// "this->size() (which is %zu)"),
+// __s, __pos, this->size());
+ return __pos;
+ }
+
+ // NB: _M_limit doesn't check for a bad __pos value.
+ size_type _M_limit(size_type __pos, size_type __off) const nothrow pure @nogc @safe
+ {
+ const bool __testoff = __off < size() - __pos;
+ return __testoff ? __off : size() - __pos;
+ }
+
+ void __alloc_on_swap()(ref allocator_type __a)
+ if (!is_empty!allocator_type.value)
+ {
+ import core.internal.lifetime : swap;
+
+ static if (allocator_traits!allocator_type.propagate_on_container_swap)
+ swap(_M_get_allocator(), __a);
+ }
+
+ void __alloc_on_swap()(ref allocator_type __a)
+ if (is_empty!allocator_type.value)
+ {
+ import core.internal.lifetime : swap;
+ import core.lifetime : move;
+
+ static if (allocator_traits!allocator_type.propagate_on_container_swap)
+ {
+ static if (is(typeof(_M_get_allocator().opAssign(move(__a)))))
+ swap(_M_get_allocator(), __a);
+ }
+ }
+ }
+
+ void _M_construct(const(T)* __beg, size_type __dnew)
+ {
+ if (__dnew > _S_local_capacity)
+ {
+ _M_data = _M_create(__dnew, size_type(0));
+ _M_capacity = __dnew;
+ }
+ _M_data[0 .. __dnew] = __beg[0 .. __dnew];
+ _M_set_length(__dnew);
+ }
+
+ pointer _M_create(ref size_type __capacity, size_type __old_capacity)
+ {
+ assert(__capacity <= max_size());
+// if (__capacity > max_size())
+// throw new RangeError("Length exceeds `max_size()`"); // std::__throw_length_error(__N("basic_string::_M_create"));
+ if (__capacity > __old_capacity && __capacity < 2 * __old_capacity)
+ {
+ __capacity = 2 * __old_capacity;
+ if (__capacity > max_size())
+ __capacity = max_size();
+ }
+ return _M_get_allocator().allocate(__capacity + 1);
+ }
+
+ ref basic_string _M_replace(size_type __pos, size_type __len1, const(T)* __s, const size_type __len2)
+ {
+ _M_check_length(__len1, __len2, "basic_string::_M_replace");
+
+ const size_type __old_size = size();
+ const size_type __new_size = __old_size + __len2 - __len1;
+
+ if (__new_size <= capacity())
+ {
+ pointer __p = _M_data + __pos;
+
+ const size_type __how_much = __old_size - __pos - __len1;
+ if (_M_disjunct(__s))
+ {
+ if (__how_much && __len1 != __len2)
+ _S_move(__p + __len2, __p + __len1, __how_much);
+ if (__len2)
+ _S_copy(__p, __s, __len2);
+ }
+ else
+ {
+ // Work in-place.
+ if (__len2 && __len2 <= __len1)
+ _S_move(__p, __s, __len2);
+ if (__how_much && __len1 != __len2)
+ _S_move(__p + __len2, __p + __len1, __how_much);
+ if (__len2 > __len1)
+ {
+ if (__s + __len2 <= __p + __len1)
+ _S_move(__p, __s, __len2);
+ else if (__s >= __p + __len1)
+ _S_copy(__p, __s + __len2 - __len1, __len2);
+ else
+ {
+ const size_type __nleft = (__p + __len1) - __s;
+ _S_move(__p, __s, __nleft);
+ _S_copy(__p + __nleft, __p + __len2,
+ __len2 - __nleft);
+ }
+ }
+ }
+ }
+ else
+ _M_mutate(__pos, __len1, __s, __len2);
+
+ _M_set_length(__new_size);
+ return this;
+ }
+
+ ref basic_string _M_replace_aux(size_type __pos1, size_type __n1, size_type __n2, T __c)
+ {
+ _M_check_length(__n1, __n2, "basic_string::_M_replace_aux");
+
+ const size_type __old_size = size();
+ const size_type __new_size = __old_size + __n2 - __n1;
+
+ if (__new_size <= capacity())
+ {
+ pointer __p = _M_data + __pos1;
+
+ const size_type __how_much = __old_size - __pos1 - __n1;
+ if (__how_much && __n1 != __n2)
+ _S_move(__p + __n2, __p + __n1, __how_much);
+ }
+ else
+ _M_mutate(__pos1, __n1, null, __n2);
+
+ if (__n2)
+ _M_data[__pos1 .. __pos1 + __n2] = __c;
+
+ _M_set_length(__new_size);
+ return this;
+ }
+
+ ref basic_string _M_append(const(T)* __s, size_type __n)
+ {
+ const size_type __len = __n + size();
+ if (__len <= capacity())
+ {
+ if (__n)
+ _S_copy(_M_data + size(), __s, __n);
+ }
+ else
+ _M_mutate(size(), size_type(0), __s, __n);
+ _M_set_length(__len);
+ return this;
+ }
+
+ void _M_mutate(size_type __pos, size_type __len1, const(T)* __s, size_type __len2)
+ {
+ const size_type __how_much = length() - __pos - __len1;
+
+ size_type __new_capacity = length() + __len2 - __len1;
+ pointer __r = _M_create(__new_capacity, capacity());
+
+ if (__pos)
+ _S_copy(__r, _M_data, __pos);
+ if (__s && __len2)
+ _S_copy(__r + __pos, __s, __len2);
+ if (__how_much)
+ _S_copy(__r + __pos + __len2,
+ _M_data + __pos + __len1, __how_much);
+
+ _M_dispose();
+ _M_data = __r;
+ _M_capacity = __new_capacity;
+ }
+
+ void _M_dispose()
+ {
+ if (!_M_is_local)
+ _M_destroy(_M_allocated_capacity);
+ }
+
+ void _M_destroy(size_type __size)
+ {
+ _M_get_allocator().deallocate(_M_data, __size + 1);
+ }
+ }
+
+ // common GCC/stdlibc++ code
+
+ void _M_check_length(size_type __n1, size_type __n2, const char* __s) const
+ {
+ assert (!(max_size() - (size() - __n1) < __n2));
+// if (max_size() - (size() - __n1) < __n2)
+// __throw_length_error(__N(__s));
+ }
+
+ void _M_assign_allocator(ref const(allocator_type) al) nothrow
+ {
+ static if (!is_empty!allocator_type.value)
+ _M_Alloc = al;
+ }
+
+ bool _M_disjunct(const(T)* __s) const nothrow
+ {
+ return __s < _M_data || _M_data + size() < __s;
+ }
+
+ static void _S_move(T* __d, const(T)* __s, size_type __n)
+ {
+ if (__d == __s)
+ return;
+ if (__d < __s)
+ {
+ for (size_t i = 0; i < __n; ++i)
+ __d[i] = __s[i];
+ }
+ else
+ {
+ for (ptrdiff_t i = __n - 1; i >= 0; --i)
+ __d[i] = __s[i];
+ }
+ }
+ static void _S_copy(T* __d, const(T)* __s, size_type __n)
+ {
+ __d[0 .. __n] = __s[0 .. __n];
+ }
+ }
+ else version (CppRuntime_Clang)
+ {
+ //----------------------------------------------------------------------------------
+ // Clang/libc++ implementation
+ //----------------------------------------------------------------------------------
+
+ ///
+ this(DefaultConstruct) { __zero(); }
+ ///
+ this(const(T)[] str, ref const(allocator_type) al) { __assign_allocator(al); this(str); }
+ ///
+ this(const(T)[] str) { __init(str.ptr, str.length); }
+ ///
+ this(this)
+ {
+ if (__is_long())
+ __init(__get_long_pointer(), __get_long_size());
+ }
+
+ ///
+ ~this()
+ {
+// __get_db()->__erase_c(this); // TODO: support `_LIBCPP_DEBUG_LEVEL >= 2` ??
+ if (__is_long())
+ __alloc().deallocate(__get_long_pointer(), __get_long_cap());
+ }
+
+ ///
+ ref inout(Alloc) get_allocator() inout { return __alloc(); }
+
+ ///
+ size_type max_size() const nothrow @safe
+ {
+ size_type __m = size_t.max; // TODO: __alloc_traits::max_size(__alloc());
+ version (BigEndian)
+ return (__m <= ~__long_mask ? __m : __m/2) - __alignment;
+ else
+ return __m - __alignment;
+ }
+
+ ///
+ size_type size() const nothrow { return __is_long() ? __get_long_size() : __get_short_size(); }
+ ///
+ size_type capacity() const nothrow { return (__is_long() ? __get_long_cap() : __min_cap) - 1; }
+ ///
+ inout(T)* data() inout @safe { return __get_pointer(); }
+ ///
+ inout(T)[] as_array() inout nothrow @trusted { return __get_pointer()[0 .. size()]; }
+ ///
+ ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; }
+
+ ///
+ ref basic_string assign(const(T)[] str)
+ {
+ const(value_type)* __s = str.ptr;
+ size_type __n = str.length;
+ size_type __cap = capacity();
+ if (__cap >= __n)
+ {
+ value_type* __p = __get_pointer();
+ __p[0 .. __n] = __s[0 .. __n]; // TODO: is memmove?
+ __p[__n] = value_type(0);
+ __set_size(__n);
+// __invalidate_iterators_past(__n); // TODO: support `_LIBCPP_DEBUG_LEVEL >= 2` ??
+ }
+ else
+ {
+ size_type __sz = size();
+ __grow_by_and_replace(__cap, __n - __cap, __sz, 0, __sz, __n, __s);
+ }
+ return this;
+ }
+
+ ///
+ ref basic_string assign(const ref basic_string str)
+ {
+ if (&this != &str)
+ assign(str.as_array);
+ return this;
+ }
+
+ ///
+ ref basic_string append(const(T)[] str)
+ {
+ const(value_type)* __s = str.ptr;
+ size_type __n = str.length;
+ size_type __cap = capacity();
+ size_type __sz = size();
+ if (__cap - __sz >= __n)
+ {
+ if (__n)
+ {
+ value_type* __p = __get_pointer();
+ (__p + __sz)[0 .. __n] = __s[0 .. __n];
+ __sz += __n;
+ __set_size(__sz);
+ __p[__sz] = value_type(0);
+ }
+ }
+ else
+ __grow_by_and_replace(__cap, __sz + __n - __cap, __sz, __sz, 0, __n, __s);
+ return this;
+ }
+
+ ///
+ ref basic_string append(size_type __n, value_type __c)
+ {
+ if (__n)
+ {
+ size_type __cap = capacity();
+ size_type __sz = size();
+ if (__cap - __sz < __n)
+ __grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0);
+ pointer __p = __get_pointer();
+ __p[__sz .. __sz + __n] = __c;
+ __sz += __n;
+ __set_size(__sz);
+ __p[__sz] = value_type(0);
+ }
+ return this;
+ }
+
+ ///
+ void reserve(size_type __res_arg = 0)
+ {
+ assert(__res_arg <= max_size());
+// if (__res_arg > max_size())
+// __throw_length_error();
+ size_type __cap = capacity();
+ size_type __sz = size();
+ __res_arg = max(__res_arg, __sz);
+ __res_arg = __recommend(__res_arg);
+ if (__res_arg != __cap)
+ {
+ pointer __new_data, __p;
+ bool __was_long, __now_long;
+ if (__res_arg == __min_cap - 1)
+ {
+ __was_long = true;
+ __now_long = false;
+ __new_data = __get_short_pointer();
+ __p = __get_long_pointer();
+ }
+ else
+ {
+ if (__res_arg > __cap)
+ __new_data = __alloc().allocate(__res_arg+1);
+ else
+ {
+ try
+ __new_data = __alloc().allocate(__res_arg+1);
+ catch (Throwable)
+ return;
+ }
+ __now_long = true;
+ __was_long = __is_long();
+ __p = __get_pointer();
+ }
+ __new_data[0 .. size()+1] = __p[0 .. size()+1];
+ if (__was_long)
+ __alloc().deallocate(__p, __cap+1);
+ if (__now_long)
+ {
+ __set_long_cap(__res_arg+1);
+ __set_long_size(__sz);
+ __set_long_pointer(__new_data);
+ }
+ else
+ __set_short_size(__sz);
+// __invalidate_all_iterators(); // TODO:
+ }
+ }
+
+ ///
+ void shrink_to_fit()
+ {
+ reserve();
+ }
+
+ ///
+ ref basic_string insert(size_type __pos, const(value_type)* __s, size_type __n)
+ {
+ assert(__n == 0 || __s != null, "string::insert received null");
+ size_type __sz = size();
+ assert(__pos <= __sz);
+// if (__pos > __sz)
+// this->__throw_out_of_range();
+ size_type __cap = capacity();
+ if (__cap - __sz >= __n)
+ {
+ if (__n)
+ {
+ value_type* __p = __get_pointer();
+ size_type __n_move = __sz - __pos;
+ if (__n_move != 0)
+ {
+ if (__p + __pos <= __s && __s < __p + __sz)
+ __s += __n;
+ traits_type.move(__p + __pos + __n, __p + __pos, __n_move);
+ }
+ traits_type.move(__p + __pos, __s, __n);
+ __sz += __n;
+ __set_size(__sz);
+ __p[__sz] = value_type(0);
+ }
+ }
+ else
+ __grow_by_and_replace(__cap, __sz + __n - __cap, __sz, __pos, 0, __n, __s);
+ return this;
+ }
+
+ ///
+ ref basic_string insert(size_type pos, size_type n, value_type c)
+ {
+ alias __pos = pos;
+ alias __n = n;
+ alias __c = c;
+ size_type __sz = size();
+ assert(__pos <= __sz);
+// if (__pos > __sz)
+// __throw_out_of_range();
+ if (__n)
+ {
+ size_type __cap = capacity();
+ value_type* __p;
+ if (__cap - __sz >= __n)
+ {
+ __p = __get_pointer();
+ size_type __n_move = __sz - __pos;
+ if (__n_move != 0)
+ traits_type.move(__p + __pos + __n, __p + __pos, __n_move);
+ }
+ else
+ {
+ __grow_by(__cap, __sz + __n - __cap, __sz, __pos, 0, __n);
+ __p = __get_long_pointer();
+ }
+ __p[__pos .. __pos + __n] = __c;
+ __sz += __n;
+ __set_size(__sz);
+ __p[__sz] = value_type(0);
+ }
+ return this;
+ }
+
+ ///
+ ref basic_string replace(size_type __pos, size_type __n1, const(T)* __s, size_type __n2)
+ {
+ assert(__n2 == 0 || __s != null, "string::replace received null");
+ size_type __sz = size();
+ assert(__pos <= __sz);
+// if (__pos > __sz)
+// __throw_out_of_range();
+ __n1 = min(__n1, __sz - __pos);
+ size_type __cap = capacity();
+ if (__cap - __sz + __n1 >= __n2)
+ {
+ value_type* __p = __get_pointer();
+ if (__n1 != __n2)
+ {
+ size_type __n_move = __sz - __pos - __n1;
+ if (__n_move != 0)
+ {
+ if (__n1 > __n2)
+ {
+ traits_type.move(__p + __pos, __s, __n2);
+ traits_type.move(__p + __pos + __n2, __p + __pos + __n1, __n_move);
+ goto __finish;
+ }
+ if (__p + __pos < __s && __s < __p + __sz)
+ {
+ if (__p + __pos + __n1 <= __s)
+ __s += __n2 - __n1;
+ else // __p + __pos < __s < __p + __pos + __n1
+ {
+ traits_type.move(__p + __pos, __s, __n1);
+ __pos += __n1;
+ __s += __n2;
+ __n2 -= __n1;
+ __n1 = 0;
+ }
+ }
+ traits_type.move(__p + __pos + __n2, __p + __pos + __n1, __n_move);
+ }
+ }
+ traits_type.move(__p + __pos, __s, __n2);
+ __finish:
+ // __sz += __n2 - __n1; in this and the below function below can cause unsigned integer overflow,
+ // but this is a safe operation, so we disable the check.
+ __sz += __n2 - __n1;
+ __set_size(__sz);
+// __invalidate_iterators_past(__sz); // TODO
+ __p[__sz] = value_type(0);
+ }
+ else
+ __grow_by_and_replace(__cap, __sz - __n1 + __n2 - __cap, __sz, __pos, __n1, __n2, __s);
+ return this;
+ }
+
+ ///
+ ref basic_string replace(size_type __pos, size_type __n1, size_type __n2, value_type __c)
+ {
+ size_type __sz = size();
+ assert(__pos <= __sz);
+// if (__pos > __sz)
+// __throw_out_of_range();
+ __n1 = min(__n1, __sz - __pos);
+ size_type __cap = capacity();
+ value_type* __p;
+ if (__cap - __sz + __n1 >= __n2)
+ {
+ __p = __get_pointer();
+ if (__n1 != __n2)
+ {
+ size_type __n_move = __sz - __pos - __n1;
+ if (__n_move != 0)
+ traits_type.move(__p + __pos + __n2, __p + __pos + __n1, __n_move);
+ }
+ }
+ else
+ {
+ __grow_by(__cap, __sz - __n1 + __n2 - __cap, __sz, __pos, __n1, __n2);
+ __p = __get_long_pointer();
+ }
+ __p[__pos .. __pos + __n2] = __c;
+ __sz += __n2 - __n1;
+ __set_size(__sz);
+// __invalidate_iterators_past(__sz); // TODO
+ __p[__sz] = value_type(0);
+ return this;
+ }
+
+ ///
+ void swap(ref basic_string __str)
+ {
+ import core.internal.lifetime : swap;
+// static if (_LIBCPP_DEBUG_LEVEL >= 2)
+// {
+// if (!__is_long())
+// __get_db().__invalidate_all(&this);
+// if (!__str.__is_long())
+// __get_db().__invalidate_all(&__str);
+// __get_db().swap(&this, &__str);
+// }
+ assert(
+ __alloc_traits.propagate_on_container_swap ||
+ __alloc_traits.is_always_equal ||
+ __alloc() == __str.__alloc(), "swapping non-equal allocators");
+ swap(__r_.first(), __str.__r_.first());
+ __swap_allocator(__alloc(), __str.__alloc());
+ }
+
+ private:
+// import core.exception : RangeError;
+ import core.stdcpp.xutility : __compressed_pair;
+
+ alias __alloc_traits = allocator_traits!allocator_type;
+
+ enum __alignment = 16;
+
+ version (_LIBCPP_ABI_ALTERNATE_STRING_LAYOUT)
+ {
+ struct __long
+ {
+ pointer __data_;
+ size_type __size_;
+ size_type __cap_;
+ }
+
+ version (BigEndian)
+ {
+ enum size_type __short_mask = 0x01;
+ enum size_type __long_mask = 0x1;
+ }
+ else
+ {
+ enum size_type __short_mask = 0x80;
+ enum size_type __long_mask = ~(size_type(~0) >> 1);
+ }
+
+ enum size_type __min_cap = (__long.sizeof - 1)/value_type.sizeof > 2 ? (__long.sizeof - 1)/value_type.sizeof : 2;
+
+ struct __short
+ {
+ value_type[__min_cap] __data_;
+ struct
+ {
+ static if (value_type.sizeof > 1)
+ ubyte[value_type.sizeof-1] __xx; // __padding<value_type>
+ ubyte __size_;
+ }
+ }
+ }
+ else
+ {
+ struct __long
+ {
+ size_type __cap_;
+ size_type __size_;
+ pointer __data_;
+ }
+
+ version (BigEndian)
+ {
+ enum size_type __short_mask = 0x80;
+ enum size_type __long_mask = ~(size_type(~0) >> 1);
+ }
+ else
+ {
+ enum size_type __short_mask = 0x01;
+ enum size_type __long_mask = 0x1;
+ }
+
+ enum size_type __min_cap = (__long.sizeof - 1)/value_type.sizeof > 2 ? (__long.sizeof - 1)/value_type.sizeof : 2;
+
+ struct __short
+ {
+ union
+ {
+ ubyte __size_;
+ value_type __lx;
+ }
+ value_type[__min_cap] __data_;
+ }
+ }
+
+ union __ulx { __long __lx; __short __lxx; }
+ enum __n_words = __ulx.sizeof / size_type.sizeof;
+
+ struct __raw
+ {
+ size_type[__n_words] __words;
+ }
+
+ struct __rep
+ {
+ union
+ {
+ __long __l;
+ __short __s;
+ __raw __r;
+ }
+ }
+
+ __compressed_pair!(__rep, allocator_type) __r_;
+
+ pragma (inline, true)
+ {
+ void eos(size_type offset) nothrow
+ {
+ __set_size(offset);
+// __invalidate_iterators_past(__sz); // TODO: support `_LIBCPP_DEBUG_LEVEL >= 2` ??
+ __get_pointer()[offset] = value_type(0);
+ }
+
+ version (_LIBCPP_ABI_ALTERNATE_STRING_LAYOUT)
+ {
+ version (BigEndian)
+ {
+ void __set_short_size(size_type __s) nothrow @safe { __r_.first().__s.__size_ = cast(ubyte)(__s << 1); }
+ size_type __get_short_size() const nothrow @safe { return __r_.first().__s.__size_ >> 1; }
+ }
+ else
+ {
+ void __set_short_size(size_type __s) nothrow @safe { __r_.first().__s.__size_ = cast(ubyte)(__s);}
+ size_type __get_short_size() const nothrow @safe { return __r_.first().__s.__size_;}
+ }
+ }
+ else
+ {
+ version (BigEndian)
+ {
+ void __set_short_size(size_type __s) nothrow @safe { __r_.first().__s.__size_ = cast(ubyte)(__s); }
+ size_type __get_short_size() const nothrow @safe { return __r_.first().__s.__size_; }
+ }
+ else
+ {
+ void __set_short_size(size_type __s) nothrow @safe { __r_.first().__s.__size_ = cast(ubyte)(__s << 1); }
+ size_type __get_short_size() const nothrow @safe { return __r_.first().__s.__size_ >> 1; }
+ }
+ }
+ void __set_long_size(size_type __s) nothrow { __r_.first().__l.__size_ = __s; }
+ size_type __get_long_size() const nothrow { return __r_.first().__l.__size_; }
+ void __set_size(size_type __s) nothrow { if (__is_long()) __set_long_size(__s); else __set_short_size(__s); }
+
+ void __set_long_cap(size_type __s) nothrow { __r_.first().__l.__cap_ = __long_mask | __s; }
+ size_type __get_long_cap() const nothrow { return __r_.first().__l.__cap_ & size_type(~__long_mask); }
+
+ void __set_long_pointer(pointer __p) nothrow { __r_.first().__l.__data_ = __p; }
+ inout(T)* __get_long_pointer() inout nothrow { return __r_.first().__l.__data_; }
+ inout(T)* __get_short_pointer() inout nothrow @safe { return &__r_.first().__s.__data_[0]; }
+ inout(T)* __get_pointer() inout nothrow { return __is_long() ? __get_long_pointer() : __get_short_pointer(); }
+
+ bool __is_long() const nothrow @safe { return (__r_.first().__s.__size_ & __short_mask) != 0; }
+
+ void __zero() nothrow @safe { __r_.first().__r.__words[] = 0; }
+
+ ref inout(allocator_type) __alloc() inout nothrow @safe { return __r_.second(); }
+
+ void __init(const(value_type)* __s, size_type __sz) { return __init(__s, __sz, __sz); }
+ }
+
+ void __assign_allocator(ref const(allocator_type) al) nothrow
+ {
+ static if (!__r_.Ty2Empty)
+ __alloc() = al;
+ }
+
+ void __init(const(value_type)* __s, size_type __sz, size_type __reserve)
+ {
+ assert(__reserve <= max_size());
+// if (__reserve > max_size())
+// throw new RangeError("Length exceeds `max_size()`"); // this->__throw_length_error();
+ pointer __p;
+ if (__reserve < __min_cap)
+ {
+ __set_short_size(__sz);
+ __p = __get_short_pointer();
+ }
+ else
+ {
+ size_type __cap = __recommend(__reserve);
+ __p = __alloc().allocate(__cap+1, null);
+ __set_long_pointer(__p);
+ __set_long_cap(__cap+1);
+ __set_long_size(__sz);
+ }
+ __p[0 .. __sz] = __s[0 .. __sz];
+ __p[__sz] = value_type(0);
+ }
+
+ static size_type __recommend(size_type __s) nothrow @safe
+ {
+ static size_type __align_it(size_type __a)(size_type __s) nothrow @safe { return (__s + (__a-1)) & ~(__a-1); }
+ if (__s < __min_cap) return __min_cap - 1;
+ size_type __guess = __align_it!(value_type.sizeof < __alignment ? __alignment/value_type.sizeof : 1)(__s+1) - 1;
+ if (__guess == __min_cap) ++__guess;
+ return __guess;
+ }
+
+ void __grow_by_and_replace(size_type __old_cap, size_type __delta_cap, size_type __old_sz, size_type __n_copy,
+ size_type __n_del, size_type __n_add, const(value_type)* __p_new_stuff)
+ {
+ size_type __ms = max_size();
+ assert(__delta_cap <= __ms - __old_cap - 1);
+// if (__delta_cap > __ms - __old_cap - 1)
+// throw new RangeError("Length exceeds `max_size()`"); // this->__throw_length_error();
+ pointer __old_p = __get_pointer();
+ size_type __cap = __old_cap < __ms / 2 - __alignment ?
+ __recommend(max(__old_cap + __delta_cap, 2 * __old_cap)) :
+ __ms - 1;
+ pointer __p = __alloc().allocate(__cap+1);
+// __invalidate_all_iterators(); // TODO: support `_LIBCPP_DEBUG_LEVEL >= 2` ??
+ if (__n_copy != 0)
+ __p[0 .. __n_copy] = __old_p[0 .. __n_copy];
+ if (__n_add != 0)
+ (__p + __n_copy)[0 .. __n_add] = __p_new_stuff[0 .. __n_add];
+ size_type __sec_cp_sz = __old_sz - __n_del - __n_copy;
+ if (__sec_cp_sz != 0)
+ (__p + __n_copy + __n_add)[0 .. __sec_cp_sz] = (__old_p + __n_copy + __n_del)[0 .. __sec_cp_sz];
+ if (__old_cap+1 != __min_cap)
+ __alloc().deallocate(__old_p, __old_cap+1);
+ __set_long_pointer(__p);
+ __set_long_cap(__cap+1);
+ __old_sz = __n_copy + __n_add + __sec_cp_sz;
+ __set_long_size(__old_sz);
+ __p[__old_sz] = value_type(0);
+ }
+
+ void __grow_by(size_type __old_cap, size_type __delta_cap, size_type __old_sz,
+ size_type __n_copy, size_type __n_del, size_type __n_add = 0)
+ {
+ size_type __ms = max_size();
+ assert(__delta_cap <= __ms - __old_cap);
+// if (__delta_cap > __ms - __old_cap)
+// __throw_length_error();
+ pointer __old_p = __get_pointer();
+ size_type __cap = __old_cap < __ms / 2 - __alignment ?
+ __recommend(max(__old_cap + __delta_cap, 2 * __old_cap)) :
+ __ms - 1;
+ pointer __p = __alloc().allocate(__cap+1);
+// __invalidate_all_iterators(); // TODO:
+ if (__n_copy != 0)
+ __p[0 .. __n_copy] = __old_p[0 .. __n_copy];
+ size_type __sec_cp_sz = __old_sz - __n_del - __n_copy;
+ if (__sec_cp_sz != 0)
+ (__p + __n_copy + __n_add)[0 .. __sec_cp_sz] = (__old_p + __n_copy + __n_del)[0 .. __sec_cp_sz];
+ if (__old_cap+1 != __min_cap)
+ __alloc().deallocate(__old_p, __old_cap+1);
+ __set_long_pointer(__p);
+ __set_long_cap(__cap+1);
+ }
+ }
+ else
+ {
+ static assert(false, "C++ runtime not supported");
+ }
+}
+
+
+// platform detail
+private:
+version (CppRuntime_Microsoft)
+{
+ import core.stdcpp.xutility : _ITERATOR_DEBUG_LEVEL;
+
+extern(C++, (StdNamespace)):
+ extern (C++) struct _String_base_types(_Elem, _Alloc)
+ {
+ alias Ty = _Elem;
+ alias Alloc = _Alloc;
+ }
+
+ extern (C++, class) struct _String_alloc(_Alloc_types)
+ {
+ import core.stdcpp.xutility : _Compressed_pair;
+
+ alias Ty = _Alloc_types.Ty;
+ alias Alloc = _Alloc_types.Alloc;
+ alias ValTy = _String_val!Ty;
+
+ extern(D) @safe @nogc:
+ pragma(inline, true)
+ {
+ ref inout(Alloc) _Getal() inout pure nothrow { return _Mypair._Myval1; }
+ ref inout(ValTy) _Get_data() inout pure nothrow { return _Mypair._Myval2; }
+ }
+
+ void _Orphan_all() nothrow { _Get_data._Base._Orphan_all(); }
+
+ static if (_ITERATOR_DEBUG_LEVEL > 0)
+ {
+ import core.stdcpp.xutility : _Container_proxy;
+
+ ~this()
+ {
+ _Free_proxy();
+ }
+
+ pragma(inline, true)
+ ref inout(_Container_proxy*) _Myproxy() inout pure nothrow { return _Get_data._Base._Myproxy; }
+
+ void _Alloc_proxy() nothrow @trusted
+ {
+ import core.lifetime : emplace;
+
+ alias _Alproxy = Alloc.rebind!_Container_proxy;
+ try // TODO: or should we make allocator<T>::allocate() `nothrow`?
+ _Myproxy() = _Alproxy(_Getal()).allocate(1);
+ catch (Throwable)
+ assert(false, "Failed to allocate iterator debug container proxy");
+ emplace!_Container_proxy(_Myproxy());
+ _Myproxy()._Mycont = &_Get_data()._Base;
+ }
+ void _Free_proxy() nothrow @trusted
+ {
+ alias _Alproxy = Alloc.rebind!_Container_proxy;
+ _Orphan_all();
+ destroy!false(*_Myproxy());
+ try // TODO: or should we make allocator<T>::deallocate() `nothrow`?
+ _Alproxy(_Getal()).deallocate(_Myproxy(), 1);
+ catch (Throwable)
+ assert(false, "Failed to deallocate iterator debug container proxy");
+ _Myproxy() = null;
+ }
+ }
+
+ _Compressed_pair!(Alloc, ValTy) _Mypair;
+ }
+
+ extern (C++, class) struct _String_val(T)
+ {
+ import core.stdcpp.xutility : _Container_base;
+ import core.stdcpp.type_traits : is_empty;
+
+ enum _BUF_SIZE = 16 / T.sizeof < 1 ? 1 : 16 / T.sizeof;
+ enum _ALLOC_MASK = T.sizeof <= 1 ? 15 : T.sizeof <= 2 ? 7 : T.sizeof <= 4 ? 3 : T.sizeof <= 8 ? 1 : 0;
+
+ static if (!is_empty!_Container_base.value)
+ _Container_base _Base;
+ else
+ ref inout(_Container_base) _Base() inout { return *cast(inout(_Container_base)*)&this; }
+
+ union _Bxty
+ {
+ T[_BUF_SIZE] _Buf;
+ T* _Ptr;
+ }
+
+ _Bxty _Bx;
+ size_t _Mysize = 0; // current length of string
+ size_t _Myres = _BUF_SIZE - 1; // current storage reserved for string
+
+ pragma (inline, true):
+ extern (D):
+ pure nothrow @nogc:
+ bool _IsAllocated() const @safe { return _BUF_SIZE <= _Myres; }
+ alias _Large_string_engaged = _IsAllocated;
+ @property inout(T)* _Myptr() inout @trusted { return _BUF_SIZE <= _Myres ? _Bx._Ptr : _Bx._Buf.ptr; }
+ @property inout(T)[] _Mystr() inout @trusted { return _BUF_SIZE <= _Myres ? _Bx._Ptr[0 .. _Mysize] : _Bx._Buf[0 .. _Mysize]; }
+
+ auto _Clamp_suffix_size(T)(const T _Off, const T _Size) const
+ {
+ // trims _Size to the longest it can be assuming a string at/after _Off
+ return min(_Size, _Mysize - _Off);
+ }
+ }
+
+ template _Size_after_ebco_v(_Ty)
+ {
+ import core.stdcpp.type_traits : is_empty;
+
+ enum size_t _Size_after_ebco_v = is_empty!_Ty.value ? 0 : _Ty.sizeof; // get _Ty's size after being EBCO'd
+ }
+}
+
+auto ref T max(T)(auto ref T a, auto ref T b) { return b > a ? b : a; }
+auto ref T min(T)(auto ref T a, auto ref T b) { return b < a ? b : a; }
diff --git a/libphobos/libdruntime/core/stdcpp/string_view.d b/libphobos/libdruntime/core/stdcpp/string_view.d
new file mode 100644
index 00000000000..172c170444b
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/string_view.d
@@ -0,0 +1,130 @@
+/**
+ * D header file for interaction with C++ std::string_view.
+ *
+ * Copyright: Copyright (c) 2018 D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Manu Evans
+ * Source: $(DRUNTIMESRC core/stdcpp/string_view.d)
+ */
+
+module core.stdcpp.string_view;
+
+import core.stdc.stddef : wchar_t;
+import core.stdcpp.xutility : StdNamespace;
+
+// hacks to support DMD on Win32
+version (CppRuntime_Microsoft)
+{
+ version = CppRuntime_Windows; // use the MS runtime ABI for win32
+}
+else version (CppRuntime_DigitalMars)
+{
+ version = CppRuntime_Windows; // use the MS runtime ABI for win32
+ pragma(msg, "std::basic_string_view not supported by DMC");
+}
+
+extern(C++, (StdNamespace)):
+@nogc:
+
+///
+alias string_view = basic_string_view!char;
+///
+alias u16string_view = basic_string_view!wchar;
+///
+alias u32string_view = basic_string_view!dchar;
+///
+alias wstring_view = basic_string_view!wchar_t;
+
+
+/**
+ * Character traits classes specify character properties and provide specific
+ * semantics for certain operations on characters and sequences of characters.
+ */
+extern(C++, struct) struct char_traits(CharT) {}
+
+
+/**
+* D language counterpart to C++ std::basic_string_view.
+*
+* C++ reference: $(LINK2 hhttps://en.cppreference.com/w/cpp/string/basic_string_view)
+*/
+extern(C++, class) struct basic_string_view(T, Traits = char_traits!T)
+{
+extern(D):
+pragma(inline, true):
+pure nothrow @nogc:
+
+ ///
+ enum size_type npos = size_type.max;
+
+ ///
+ alias size_type = size_t;
+ ///
+ alias difference_type = ptrdiff_t;
+ ///
+ alias value_type = T;
+ ///
+ alias pointer = T*;
+ ///
+ alias const_pointer = const(T)*;
+
+ ///
+ alias as_array this;
+ ///
+ alias toString = as_array;
+
+ ///
+ this(const(T)[] str) @trusted { __data = str.ptr; __size = str.length; }
+
+ ///
+ alias length = size;
+ ///
+ alias opDollar = length;
+ ///
+ size_type size() const @safe { return __size; }
+ ///
+ bool empty() const @safe { return __size == 0; }
+
+ ///
+ const(T)* data() const @safe { return __data; }
+ ///
+ const(T)[] as_array() const @trusted { return __data[0 .. __size]; }
+
+ ///
+ ref const(T) at(size_type i) const @trusted { return __data[0 .. __size][i]; }
+
+ ///
+ ref const(T) front() const @safe { return this[0]; }
+ ///
+ ref const(T) back() const @safe { return this[$-1]; }
+
+private:
+ // use the proper field names from C++ so debugging doesn't get weird
+ version (CppRuntime_Windows)
+ {
+ const_pointer _Mydata;
+ size_type _Mysize;
+
+ alias __data = _Mydata;
+ alias __size = _Mysize;
+ }
+ else version (CppRuntime_Gcc)
+ {
+ size_t _M_len;
+ const(T)* _M_str;
+
+ alias __data = _M_str;
+ alias __size = _M_len;
+ }
+ else version (CppRuntime_Clang)
+ {
+ const value_type* __data;
+ size_type __size;
+ }
+ else
+ {
+ static assert(false, "C++ runtime not supported");
+ }
+}
diff --git a/libphobos/libdruntime/core/stdcpp/type_traits.d b/libphobos/libdruntime/core/stdcpp/type_traits.d
new file mode 100644
index 00000000000..e4bf41c1430
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/type_traits.d
@@ -0,0 +1,50 @@
+/**
+ * D header file for interaction with C++ std::type_traits.
+ *
+ * Copyright: Copyright Digital Mars 2018.
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Manu Evans
+ * Source: $(DRUNTIMESRC core/stdcpp/type_traits.d)
+ */
+
+module core.stdcpp.type_traits;
+
+extern(C++, "std"):
+
+///
+struct integral_constant(T, T Val)
+{
+ ///
+ enum T value = Val;
+ ///
+ alias value_type = T;
+ ///
+ alias type = typeof(this);
+}
+
+///
+alias bool_constant(bool b) = integral_constant!(bool, b);
+
+// Useful for dealing with enable_if constraints.
+///
+alias true_type = bool_constant!true;
+///
+alias false_type = bool_constant!false;
+
+///
+struct is_empty(T)
+{
+ static if (is(T == struct))
+ private enum __is_empty = T.tupleof.length == 0;
+ else
+ private enum __is_empty = false;
+
+ ///
+ enum bool value = __is_empty;
+ ///
+ alias value_type = bool;
+ ///
+ alias type = integral_constant!(bool, value);
+}
diff --git a/libphobos/libdruntime/core/stdcpp/typeinfo.d b/libphobos/libdruntime/core/stdcpp/typeinfo.d
index 693b8cdbe65..53b25c5e9a5 100644
--- a/libphobos/libdruntime/core/stdcpp/typeinfo.d
+++ b/libphobos/libdruntime/core/stdcpp/typeinfo.d
@@ -11,11 +11,14 @@
module core.stdcpp.typeinfo;
+import core.attribute : weak;
+
version (CppRuntime_DigitalMars)
{
import core.stdcpp.exception;
extern (C++, "std"):
+ @nogc:
class type_info
{
@@ -27,8 +30,8 @@ version (CppRuntime_DigitalMars)
//bool operator==(const type_info rhs) const;
//bool operator!=(const type_info rhs) const;
- final bool before(const type_info rhs) const;
- final const(char)* name() const;
+ final bool before(const type_info rhs) const nothrow;
+ final const(char)* name() const nothrow;
protected:
//type_info();
private:
@@ -59,6 +62,7 @@ else version (CppRuntime_Microsoft)
import core.stdcpp.exception;
extern (C++, "std"):
+ @nogc:
struct __type_info_node
{
@@ -70,12 +74,11 @@ else version (CppRuntime_Microsoft)
class type_info
{
- //virtual ~this();
- void dtor() { } // reserve slot in vtbl[]
+ @weak ~this() nothrow {}
//bool operator==(const type_info rhs) const;
//bool operator!=(const type_info rhs) const;
- final bool before(const type_info rhs) const;
- final const(char)* name(__type_info_node* p = &__type_info_root_node) const;
+ final bool before(const type_info rhs) const nothrow;
+ final const(char)* name(__type_info_node* p = &__type_info_root_node) const nothrow;
private:
void* pdata;
@@ -85,13 +88,13 @@ else version (CppRuntime_Microsoft)
class bad_cast : exception
{
- this(const(char)* msg = "bad cast");
+ this(const(char)* msg = "bad cast") @nogc nothrow { super(msg); }
//virtual ~this();
}
class bad_typeid : exception
{
- this(const(char)* msg = "bad typeid");
+ this(const(char)* msg = "bad typeid") @nogc nothrow { super(msg); }
//virtual ~this();
}
}
@@ -101,19 +104,21 @@ else version (CppRuntime_Gcc)
extern (C++, "__cxxabiv1")
{
- class __class_type_info;
+ extern(C++, class) struct __class_type_info;
}
extern (C++, "std"):
+ @nogc:
- class type_info
+ abstract class type_info
{
- void dtor1(); // consume destructor slot in vtbl[]
- void dtor2(); // consume destructor slot in vtbl[]
- final const(char)* name()() const nothrow {
+ @weak ~this() {}
+ @weak final const(char)* name() const nothrow
+ {
return _name[0] == '*' ? _name + 1 : _name;
}
- final bool before()(const type_info _arg) const {
+ @weak final bool before(const type_info _arg) const nothrow
+ {
import core.stdc.string : strcmp;
return (_name[0] == '*' && _arg._name[0] == '*')
? _name < _arg._name
@@ -123,24 +128,66 @@ else version (CppRuntime_Gcc)
bool __is_pointer_p() const;
bool __is_function_p() const;
bool __do_catch(const type_info, void**, uint) const;
- bool __do_upcast(const __class_type_info, void**) const;
+ bool __do_upcast(const __class_type_info*, void**) const;
+ protected:
const(char)* _name;
- this(const(char)*);
+
+ this(const(char)* name) { _name = name; }
+ }
+
+ class bad_cast : exception
+ {
+ this() nothrow {}
+ //~this();
+ @weak override const(char)* what() const nothrow { return "bad cast"; }
+ }
+
+ class bad_typeid : exception
+ {
+ this() nothrow {}
+ //~this();
+ @weak override const(char)* what() const nothrow { return "bad typeid"; }
+ }
+}
+else version (CppRuntime_Clang)
+{
+ import core.stdcpp.exception;
+
+ extern (C++, "std"):
+ @nogc:
+
+ abstract class type_info
+ {
+ @weak ~this() {}
+ @weak final const(char)* name() const nothrow
+ {
+ return __type_name;
+ }
+ @weak final bool before(const type_info __arg) const nothrow
+ {
+ return __type_name < __arg.__type_name;
+ }
+ //bool operator==(const type_info) const;
+
+ protected:
+ const(char)* __type_name;
+
+ this(const(char)* __n) { __type_name = __n; }
}
class bad_cast : exception
{
- this();
+ this() nothrow {}
//~this();
- override const(char)* what() const;
+ @weak override const(char)* what() const nothrow { return "bad cast"; }
}
class bad_typeid : exception
{
- this();
+ this() nothrow {}
//~this();
- override const(char)* what() const;
+ @weak override const(char)* what() const nothrow { return "bad typeid"; }
}
}
else
diff --git a/libphobos/libdruntime/core/stdcpp/utility.d b/libphobos/libdruntime/core/stdcpp/utility.d
new file mode 100644
index 00000000000..a34e3562dcd
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/utility.d
@@ -0,0 +1,50 @@
+/**
+ * D header file for interaction with Microsoft C++ <utility>
+ *
+ * Copyright: Copyright (c) 2018 D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Manu Evans
+ * Source: $(DRUNTIMESRC core/stdcpp/utility.d)
+ */
+
+module core.stdcpp.utility;
+
+import core.stdcpp.xutility : StdNamespace;
+
+extern(C++, (StdNamespace)):
+@nogc:
+
+/**
+* D language counterpart to C++ std::pair.
+*
+* C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/utility/pair)
+*/
+struct pair(T1, T2)
+{
+ ///
+ alias first_type = T1;
+ ///
+ alias second_type = T2;
+
+ ///
+ T1 first;
+ ///
+ T2 second;
+
+ // FreeBSD has pair as non-POD so add a contructor
+ version (FreeBSD)
+ {
+ this(T1 t1, T2 t2) inout
+ {
+ first = t1;
+ second = t2;
+ }
+ this(ref return scope inout pair!(T1, T2) src) inout
+ {
+ first = src.first;
+ second = src.second;
+ }
+ }
+}
diff --git a/libphobos/libdruntime/core/stdcpp/vector.d b/libphobos/libdruntime/core/stdcpp/vector.d
new file mode 100644
index 00000000000..c64dbf68abf
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/vector.d
@@ -0,0 +1,850 @@
+/**
+ * D header file for interaction with C++ std::vector.
+ *
+ * Copyright: Copyright (c) 2018 D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Guillaume Chatelet
+ * Manu Evans
+ * Source: $(DRUNTIMESRC core/stdcpp/vector.d)
+ */
+
+module core.stdcpp.vector;
+
+///////////////////////////////////////////////////////////////////////////////
+// std::vector declaration.
+//
+// Current caveats :
+// - missing noexcept
+// - nothrow @trusted @nogc for most functions depend on knowledge
+// of T's construction/destruction/assignment semantics
+///////////////////////////////////////////////////////////////////////////////
+
+import core.stdcpp.allocator;
+
+enum DefaultConstruct { value }
+
+/// Constructor argument for default construction
+enum Default = DefaultConstruct();
+
+extern(C++, "std"):
+
+alias vector(T) = vector!(T, allocator!T);
+extern(C++, class) struct vector(T, Alloc)
+{
+ import core.lifetime : forward, move, core_emplace = emplace;
+
+ static assert(!is(T == bool), "vector!bool not supported!");
+extern(D):
+
+ ///
+ alias size_type = size_t;
+ ///
+ alias difference_type = ptrdiff_t;
+ ///
+ alias value_type = T;
+ ///
+ alias allocator_type = Alloc;
+ ///
+ alias pointer = T*;
+ ///
+ alias const_pointer = const(T)*;
+
+ /// MSVC allocates on default initialisation in debug, which can't be modelled by D `struct`
+ @disable this();
+
+ ///
+ alias length = size;
+ ///
+ alias opDollar = length;
+
+ ///
+ size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const pure nothrow @safe @nogc { return [start, end]; }
+
+ ///
+ ref inout(T) opIndex(size_t index) inout pure nothrow @safe @nogc { return as_array[index]; }
+ ///
+ inout(T)[] opIndex(size_t[2] slice) inout pure nothrow @safe @nogc { return as_array[slice[0] .. slice[1]]; }
+ ///
+ inout(T)[] opIndex() inout pure nothrow @safe @nogc { return as_array(); }
+
+ ///
+ ref vector opAssign(U)(auto ref vector!(U, Alloc) s) { opAssign(s.as_array); return this; }
+ ///
+ ref vector opAssign(T[] array)
+ {
+ clear();
+ reserve(array.length);
+ insert(0, array);
+ return this;
+ }
+
+ ///
+ void opIndexAssign()(auto ref T val, size_t index) { as_array[index] = val; }
+ ///
+ void opIndexAssign()(auto ref T val, size_t[2] slice) { as_array[slice[0] .. slice[1]] = val; }
+ ///
+ void opIndexAssign(T[] val, size_t[2] slice) { as_array[slice[0] .. slice[1]] = val[]; }
+ ///
+ void opIndexAssign()(auto ref T val) { as_array[] = val; }
+ ///
+ void opIndexAssign(T[] val) { as_array[] = val[]; }
+
+ ///
+ void opIndexOpAssign(string op)(auto ref T val, size_t index) { mixin("as_array[index] " ~ op ~ "= val;"); }
+ ///
+ void opIndexOpAssign(string op)(auto ref T val, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= val;"); }
+ ///
+ void opIndexOpAssign(string op)(T[] val, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= val[];"); }
+ ///
+ void opIndexOpAssign(string op)(auto ref T val) { mixin("as_array[] " ~ op ~ "= val;"); }
+ ///
+ void opIndexOpAssign(string op)(T[] val) { mixin("as_array[] " ~ op ~ "= val[];"); }
+
+ ///
+ ref inout(T) front() inout pure nothrow @safe @nogc { return as_array[0]; }
+ ///
+ ref inout(T) back() inout pure nothrow @safe @nogc { return as_array[$-1]; }
+
+ ///
+ ref vector opOpAssign(string op : "~")(auto ref T item) { push_back(forward!item); return this; }
+ ///
+ ref vector opOpAssign(string op : "~")(T[] array) { insert(length, array); return this; }
+
+ ///
+ void append(T[] array) { insert(length, array); }
+
+ /// Performs elementwise equality check.
+ bool opEquals(this This, That)(auto ref That rhs)
+ if (is(immutable That == immutable vector)) { return as_array == rhs.as_array; }
+
+ /// Performs lexicographical comparison.
+ static if (is(typeof((ref T a, ref T b) => a < b)))
+ int opCmp(this This, That)(auto ref That rhs)
+ if (is(immutable That == immutable vector)) { return __cmp(as_array, rhs.as_array); }
+
+ /// Hash to allow `vector`s to be used as keys for built-in associative arrays.
+ /// **The result will generally not be the same as C++ `std::hash<std::vector<T>>`.**
+ size_t toHash() const { return .hashOf(as_array); }
+
+ // Modifiers
+ ///
+ void push_back(U)(auto ref U element)
+ {
+ emplace_back(forward!element);
+ }
+
+ version (CppRuntime_Microsoft)
+ {
+ //----------------------------------------------------------------------------------
+ // Microsoft runtime
+ //----------------------------------------------------------------------------------
+
+ ///
+ this(DefaultConstruct) @nogc { _Alloc_proxy(); }
+ ///
+ this()(size_t count)
+ {
+ _Alloc_proxy();
+ _Buy(count);
+ scope(failure) _Tidy();
+ _Get_data()._Mylast = _Udefault(_Get_data()._Myfirst, count);
+ }
+ ///
+ this()(size_t count, auto ref T val)
+ {
+ _Alloc_proxy();
+ _Buy(count);
+ scope(failure) _Tidy();
+ _Get_data()._Mylast = _Ufill(_Get_data()._Myfirst, count, val);
+ }
+ ///
+ this()(T[] array)
+ {
+ _Alloc_proxy();
+ _Buy(array.length);
+ scope(failure) _Tidy();
+ _Get_data()._Mylast = _Utransfer!false(array.ptr, array.ptr + array.length, _Get_data()._Myfirst);
+ }
+ ///
+ this(this)
+ {
+ _Alloc_proxy();
+ pointer _First = _Get_data()._Myfirst;
+ pointer _Last = _Get_data()._Mylast;
+ _Buy(_Last - _First);
+ scope(failure) _Tidy();
+ _Get_data()._Mylast = _Utransfer!false(_First, _Last, _Get_data()._Myfirst);
+ }
+
+ ///
+ ~this() { _Tidy(); }
+
+ ///
+ ref inout(Alloc) get_allocator() inout pure nothrow @safe @nogc { return _Getal(); }
+
+ ///
+ size_type max_size() const pure nothrow @safe @nogc { return ((size_t.max / T.sizeof) - 1) / 2; } // HACK: clone the windows version precisely?
+
+ ///
+ size_type size() const pure nothrow @safe @nogc { return _Get_data()._Mylast - _Get_data()._Myfirst; }
+ ///
+ size_type capacity() const pure nothrow @safe @nogc { return _Get_data()._Myend - _Get_data()._Myfirst; }
+ ///
+ bool empty() const pure nothrow @safe @nogc { return _Get_data()._Myfirst == _Get_data()._Mylast; }
+ ///
+ inout(T)* data() inout pure nothrow @safe @nogc { return _Get_data()._Myfirst; }
+ ///
+ inout(T)[] as_array() inout pure nothrow @trusted @nogc { return _Get_data()._Myfirst[0 .. size()]; }
+ ///
+ ref inout(T) at(size_type i) inout pure nothrow @trusted @nogc { return _Get_data()._Myfirst[0 .. size()][i]; }
+
+ ///
+ ref T emplace_back(Args...)(auto ref Args args)
+ {
+ if (_Has_unused_capacity())
+ return _Emplace_back_with_unused_capacity(forward!args);
+ return *_Emplace_reallocate(_Get_data()._Mylast, forward!args);
+ }
+
+ ///
+ void reserve(const size_type newCapacity)
+ {
+ if (newCapacity > capacity())
+ {
+// if (newCapacity > max_size())
+// _Xlength();
+ _Reallocate_exactly(newCapacity);
+ }
+ }
+
+ ///
+ void shrink_to_fit()
+ {
+ if (_Has_unused_capacity())
+ {
+ if (empty())
+ _Tidy();
+ else
+ _Reallocate_exactly(size());
+ }
+ }
+
+ ///
+ void pop_back()
+ {
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ {
+ assert(!empty(), "vector empty before pop");
+ _Orphan_range(_Get_data()._Mylast - 1, _Get_data()._Mylast);
+ }
+ destroy!false(_Get_data()._Mylast[-1]);
+ --_Get_data()._Mylast;
+ }
+
+ ///
+ void clear()
+ {
+ _Base._Orphan_all();
+ _Destroy(_Get_data()._Myfirst, _Get_data()._Mylast);
+ _Get_data()._Mylast = _Get_data()._Myfirst;
+ }
+
+ ///
+ void resize()(const size_type newsize)
+ {
+ static assert(is(typeof({static T i;})), T.stringof ~ ".this() is annotated with @disable.");
+ _Resize(newsize, (pointer _Dest, size_type _Count) => _Udefault(_Dest, _Count));
+ }
+
+ ///
+ void resize()(const size_type newsize, auto ref T val)
+ {
+ _Resize(newsize, (pointer _Dest, size_type _Count) => _Ufill(_Dest, _Count, forward!val));
+ }
+
+ void emplace(Args...)(size_t offset, auto ref Args args)
+ {
+ pointer _Whereptr = _Get_data()._Myfirst + offset;
+ pointer _Oldlast = _Get_data()._Mylast;
+ if (_Has_unused_capacity())
+ {
+ if (_Whereptr == _Oldlast)
+ _Emplace_back_with_unused_capacity(forward!args);
+ else
+ {
+ T _Obj = T(forward!args);
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ _Orphan_range(_Whereptr, _Oldlast);
+ move(_Oldlast[-1], *_Oldlast);
+ ++_Get_data()._Mylast;
+ _Move_backward_unchecked(_Whereptr, _Oldlast - 1, _Oldlast);
+ move(_Obj, *_Whereptr);
+ }
+ return;
+ }
+ _Emplace_reallocate(_Whereptr, forward!args);
+ }
+
+ ///
+ void insert(size_t offset, T[] array)
+ {
+ pointer _Where = _Get_data()._Myfirst + offset;
+ pointer _First = array.ptr;
+ pointer _Last = _First + array.length;
+
+ const size_type _Count = array.length;
+ const size_type _Whereoff = offset;
+ const bool _One_at_back = _Count == 1 && _Get_data()._Myfirst + _Whereoff == _Get_data()._Mylast;
+
+ if (_Count == 0)
+ {
+ // nothing to do, avoid invalidating iterators
+ }
+ else if (_Count > _Unused_capacity())
+ { // reallocate
+ const size_type _Oldsize = size();
+
+// if (_Count > max_size() - _Oldsize)
+// _Xlength();
+
+ const size_type _Newsize = _Oldsize + _Count;
+ const size_type _Newcapacity = _Calculate_growth(_Newsize);
+
+ pointer _Newvec = _Getal().allocate(_Newcapacity);
+ pointer _Constructed_last = _Newvec + _Whereoff + _Count;
+ pointer _Constructed_first = _Constructed_last;
+
+ try
+ {
+ _Utransfer!false(_First, _Last, _Newvec + _Whereoff);
+ _Constructed_first = _Newvec + _Whereoff;
+
+ if (_One_at_back)
+ {
+ _Utransfer!(true, true)(_Get_data()._Myfirst, _Get_data()._Mylast, _Newvec);
+ }
+ else
+ {
+ _Utransfer!true(_Get_data()._Myfirst, _Where, _Newvec);
+ _Constructed_first = _Newvec;
+ _Utransfer!true(_Where, _Get_data()._Mylast, _Newvec + _Whereoff + _Count);
+ }
+ }
+ catch (Throwable e)
+ {
+ _Destroy(_Constructed_first, _Constructed_last);
+ _Getal().deallocate(_Newvec, _Newcapacity);
+ throw e;
+ }
+
+ _Change_array(_Newvec, _Newsize, _Newcapacity);
+ }
+ else
+ { // Attempt to provide the strong guarantee for EmplaceConstructible failure.
+ // If we encounter copy/move construction/assignment failure, provide the basic guarantee.
+ // (For one-at-back, this provides the strong guarantee.)
+
+ pointer _Oldlast = _Get_data()._Mylast;
+ const size_type _Affected_elements = cast(size_type)(_Oldlast - _Where);
+
+ if (_Count < _Affected_elements)
+ { // some affected elements must be assigned
+ _Get_data()._Mylast = _Utransfer!true(_Oldlast - _Count, _Oldlast, _Oldlast);
+ _Move_backward_unchecked(_Where, _Oldlast - _Count, _Oldlast);
+ _Destroy(_Where, _Where + _Count);
+
+ try
+ {
+ _Utransfer!false(_First, _Last, _Where);
+ }
+ catch (Throwable e)
+ {
+ // glue the broken pieces back together
+ try
+ {
+ _Utransfer!true(_Where + _Count, _Where + 2 * _Count, _Where);
+ }
+ catch (Throwable e)
+ {
+ // vaporize the detached piece
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ _Orphan_range(_Where, _Oldlast);
+ _Destroy(_Where + _Count, _Get_data()._Mylast);
+ _Get_data()._Mylast = _Where;
+ throw e;
+ }
+
+ _Move_unchecked(_Where + 2 * _Count, _Get_data()._Mylast, _Where + _Count);
+ _Destroy(_Oldlast, _Get_data()._Mylast);
+ _Get_data()._Mylast = _Oldlast;
+ throw e;
+ }
+ }
+ else
+ { // affected elements don't overlap before/after
+ pointer _Relocated = _Where + _Count;
+ _Get_data()._Mylast = _Utransfer!true(_Where, _Oldlast, _Relocated);
+ _Destroy(_Where, _Oldlast);
+
+ try
+ {
+ _Utransfer!false(_First, _Last, _Where);
+ }
+ catch (Throwable e)
+ {
+ // glue the broken pieces back together
+ try
+ {
+ _Utransfer!true(_Relocated, _Get_data()._Mylast, _Where);
+ }
+ catch (Throwable e)
+ {
+ // vaporize the detached piece
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ _Orphan_range(_Where, _Oldlast);
+ _Destroy(_Relocated, _Get_data()._Mylast);
+ _Get_data()._Mylast = _Where;
+ throw e;
+ }
+
+ _Destroy(_Relocated, _Get_data()._Mylast);
+ _Get_data()._Mylast = _Oldlast;
+ throw e;
+ }
+ }
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ _Orphan_range(_Where, _Oldlast);
+ }
+ }
+
+ private:
+ import core.stdcpp.xutility : MSVCLinkDirectives;
+
+ // Make sure the object files wont link against mismatching objects
+ mixin MSVCLinkDirectives!true;
+
+ pragma(inline, true)
+ {
+ ref inout(_Base.Alloc) _Getal() inout pure nothrow @safe @nogc { return _Base._Mypair._Myval1; }
+ ref inout(_Base.ValTy) _Get_data() inout pure nothrow @safe @nogc { return _Base._Mypair._Myval2; }
+ }
+
+ void _Alloc_proxy() @nogc
+ {
+ static if (_ITERATOR_DEBUG_LEVEL > 0)
+ _Base._Alloc_proxy();
+ }
+
+ void _AssignAllocator(ref const(allocator_type) al) nothrow @nogc
+ {
+ static if (_Base._Mypair._HasFirst)
+ _Getal() = al;
+ }
+
+ bool _Buy(size_type _Newcapacity) @trusted @nogc
+ {
+ _Get_data()._Myfirst = null;
+ _Get_data()._Mylast = null;
+ _Get_data()._Myend = null;
+
+ if (_Newcapacity == 0)
+ return false;
+
+ // TODO: how to handle this in D? kinda like a range exception...
+// if (_Newcapacity > max_size())
+// _Xlength();
+
+ _Get_data()._Myfirst = _Getal().allocate(_Newcapacity);
+ _Get_data()._Mylast = _Get_data()._Myfirst;
+ _Get_data()._Myend = _Get_data()._Myfirst + _Newcapacity;
+
+ return true;
+ }
+
+ static void _Destroy(pointer _First, pointer _Last)
+ {
+ for (; _First != _Last; ++_First)
+ destroy!false(*_First);
+ }
+
+ void _Tidy()
+ {
+ _Base._Orphan_all();
+ if (_Get_data()._Myfirst)
+ {
+ _Destroy(_Get_data()._Myfirst, _Get_data()._Mylast);
+ _Getal().deallocate(_Get_data()._Myfirst, capacity());
+ _Get_data()._Myfirst = null;
+ _Get_data()._Mylast = null;
+ _Get_data()._Myend = null;
+ }
+ }
+
+ size_type _Unused_capacity() const pure nothrow @safe @nogc
+ {
+ return _Get_data()._Myend - _Get_data()._Mylast;
+ }
+
+ bool _Has_unused_capacity() const pure nothrow @safe @nogc
+ {
+ return _Get_data()._Myend != _Get_data()._Mylast;
+ }
+
+ ref T _Emplace_back_with_unused_capacity(Args...)(auto ref Args args)
+ {
+ core_emplace(_Get_data()._Mylast, forward!args);
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ _Orphan_range(_Get_data()._Mylast, _Get_data()._Mylast);
+ return *_Get_data()._Mylast++;
+ }
+
+ pointer _Emplace_reallocate(_Valty...)(pointer _Whereptr, auto ref _Valty _Val)
+ {
+ const size_type _Whereoff = _Whereptr - _Get_data()._Myfirst;
+ const size_type _Oldsize = size();
+
+ // TODO: what should we do in D? kinda like a range overflow?
+// if (_Oldsize == max_size())
+// _Xlength();
+
+ const size_type _Newsize = _Oldsize + 1;
+ const size_type _Newcapacity = _Calculate_growth(_Newsize);
+
+ pointer _Newvec = _Getal().allocate(_Newcapacity);
+ pointer _Constructed_last = _Newvec + _Whereoff + 1;
+ pointer _Constructed_first = _Constructed_last;
+
+ try
+ {
+ core_emplace(_Newvec + _Whereoff, forward!_Val);
+ _Constructed_first = _Newvec + _Whereoff;
+ if (_Whereptr == _Get_data()._Mylast)
+ _Utransfer!(true, true)(_Get_data()._Myfirst, _Get_data()._Mylast, _Newvec);
+ else
+ {
+ _Utransfer!true(_Get_data()._Myfirst, _Whereptr, _Newvec);
+ _Constructed_first = _Newvec;
+ _Utransfer!true(_Whereptr, _Get_data()._Mylast, _Newvec + _Whereoff + 1);
+ }
+ }
+ catch (Throwable e)
+ {
+ _Destroy(_Constructed_first, _Constructed_last);
+ _Getal().deallocate(_Newvec, _Newcapacity);
+ throw e;
+ }
+
+ _Change_array(_Newvec, _Newsize, _Newcapacity);
+ return _Get_data()._Myfirst + _Whereoff;
+ }
+
+ void _Resize(_Lambda)(const size_type _Newsize, _Lambda _Udefault_or_fill)
+ {
+ const size_type _Oldsize = size();
+ const size_type _Oldcapacity = capacity();
+
+ if (_Newsize > _Oldcapacity)
+ {
+// if (_Newsize > max_size())
+// _Xlength();
+
+ const size_type _Newcapacity = _Calculate_growth(_Newsize);
+
+ pointer _Newvec = _Getal().allocate(_Newcapacity);
+ pointer _Appended_first = _Newvec + _Oldsize;
+ pointer _Appended_last = _Appended_first;
+
+ try
+ {
+ _Appended_last = _Udefault_or_fill(_Appended_first, _Newsize - _Oldsize);
+ _Utransfer!(true, true)(_Get_data()._Myfirst, _Get_data()._Mylast, _Newvec);
+ }
+ catch (Throwable e)
+ {
+ _Destroy(_Appended_first, _Appended_last);
+ _Getal().deallocate(_Newvec, _Newcapacity);
+ throw e;
+ }
+ _Change_array(_Newvec, _Newsize, _Newcapacity);
+ }
+ else if (_Newsize > _Oldsize)
+ {
+ pointer _Oldlast = _Get_data()._Mylast;
+ _Get_data()._Mylast = _Udefault_or_fill(_Oldlast, _Newsize - _Oldsize);
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ _Orphan_range(_Oldlast, _Oldlast);
+ }
+ else if (_Newsize == _Oldsize)
+ {
+ // nothing to do, avoid invalidating iterators
+ }
+ else
+ {
+ pointer _Newlast = _Get_data()._Myfirst + _Newsize;
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ _Orphan_range(_Newlast, _Get_data()._Mylast);
+ _Destroy(_Newlast, _Get_data()._Mylast);
+ _Get_data()._Mylast = _Newlast;
+ }
+ }
+
+ void _Reallocate_exactly(const size_type _Newcapacity)
+ {
+ import core.lifetime : moveEmplace;
+
+ const size_type _Size = size();
+ pointer _Newvec = _Getal().allocate(_Newcapacity);
+
+ try
+ {
+ for (size_t i = _Size; i > 0; )
+ {
+ --i;
+ moveEmplace(_Get_data()._Myfirst[i], _Newvec[i]);
+ }
+ }
+ catch (Throwable e)
+ {
+ _Getal().deallocate(_Newvec, _Newcapacity);
+ throw e;
+ }
+
+ _Change_array(_Newvec, _Size, _Newcapacity);
+ }
+
+ void _Change_array(pointer _Newvec, const size_type _Newsize, const size_type _Newcapacity)
+ {
+ _Base._Orphan_all();
+
+ if (_Get_data()._Myfirst != null)
+ {
+ _Destroy(_Get_data()._Myfirst, _Get_data()._Mylast);
+ _Getal().deallocate(_Get_data()._Myfirst, capacity());
+ }
+
+ _Get_data()._Myfirst = _Newvec;
+ _Get_data()._Mylast = _Newvec + _Newsize;
+ _Get_data()._Myend = _Newvec + _Newcapacity;
+ }
+
+ size_type _Calculate_growth(const size_type _Newsize) const pure nothrow @nogc @safe
+ {
+ const size_type _Oldcapacity = capacity();
+ if (_Oldcapacity > max_size() - _Oldcapacity/2)
+ return _Newsize;
+ const size_type _Geometric = _Oldcapacity + _Oldcapacity/2;
+ if (_Geometric < _Newsize)
+ return _Newsize;
+ return _Geometric;
+ }
+
+ struct _Uninitialized_backout
+ {
+ this() @disable;
+ this(pointer _Dest)
+ {
+ _First = _Dest;
+ _Last = _Dest;
+ }
+ ~this()
+ {
+ _Destroy(_First, _Last);
+ }
+ void _Emplace_back(Args...)(auto ref Args args)
+ {
+ core_emplace(_Last, forward!args);
+ ++_Last;
+ }
+ pointer _Release()
+ {
+ _First = _Last;
+ return _Last;
+ }
+ private:
+ pointer _First;
+ pointer _Last;
+ }
+ pointer _Utransfer(bool _move, bool _ifNothrow = false)(pointer _First, pointer _Last, pointer _Dest)
+ {
+ // TODO: if copy/move are trivial, then we can memcpy/memmove
+ auto _Backout = _Uninitialized_backout(_Dest);
+ for (; _First != _Last; ++_First)
+ {
+ static if (_move && (!_ifNothrow || true)) // isNothrow!T (move in D is always nothrow! ...until opPostMove)
+ _Backout._Emplace_back(move(*_First));
+ else
+ _Backout._Emplace_back(*_First);
+ }
+ return _Backout._Release();
+ }
+ pointer _Ufill()(pointer _Dest, size_t _Count, auto ref T val)
+ {
+ // TODO: if T.sizeof == 1 and no elaborate constructor, fast-path to memset
+ // TODO: if copy ctor/postblit are nothrow, just range assign
+ auto _Backout = _Uninitialized_backout(_Dest);
+ for (; 0 < _Count; --_Count)
+ _Backout._Emplace_back(val);
+ return _Backout._Release();
+ }
+ pointer _Udefault()(pointer _Dest, size_t _Count)
+ {
+ // TODO: if zero init, then fast-path to zeromem
+ auto _Backout = _Uninitialized_backout(_Dest);
+ for (; 0 < _Count; --_Count)
+ _Backout._Emplace_back();
+ return _Backout._Release();
+ }
+ pointer _Move_unchecked(pointer _First, pointer _Last, pointer _Dest)
+ {
+ // TODO: can `memmove` if conditions are right...
+ for (; _First != _Last; ++_Dest, ++_First)
+ move(*_First, *_Dest);
+ return _Dest;
+ }
+ pointer _Move_backward_unchecked(pointer _First, pointer _Last, pointer _Dest)
+ {
+ while (_First != _Last)
+ move(*--_Last, *--_Dest);
+ return _Dest;
+ }
+
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ {
+ void _Orphan_range(pointer _First, pointer _Last) const @nogc
+ {
+ import core.stdcpp.xutility : _Lockit, _LOCK_DEBUG;
+
+ alias const_iterator = _Base.const_iterator;
+ auto _Lock = _Lockit(_LOCK_DEBUG);
+
+ const_iterator** _Pnext = cast(const_iterator**)_Get_data()._Base._Getpfirst();
+ if (!_Pnext)
+ return;
+
+ while (*_Pnext)
+ {
+ if ((*_Pnext)._Ptr < _First || _Last < (*_Pnext)._Ptr)
+ {
+ _Pnext = cast(const_iterator**)(*_Pnext)._Base._Getpnext();
+ }
+ else
+ {
+ (*_Pnext)._Base._Clrcont();
+ *_Pnext = *cast(const_iterator**)(*_Pnext)._Base._Getpnext();
+ }
+ }
+ }
+ }
+
+ _Vector_alloc!(_Vec_base_types!(T, Alloc)) _Base;
+ }
+ else version (None)
+ {
+ size_type size() const pure nothrow @safe @nogc { return 0; }
+ size_type capacity() const pure nothrow @safe @nogc { return 0; }
+ bool empty() const pure nothrow @safe @nogc { return true; }
+
+ inout(T)* data() inout pure nothrow @safe @nogc { return null; }
+ inout(T)[] as_array() inout pure nothrow @trusted @nogc { return null; }
+ ref inout(T) at(size_type i) inout pure nothrow @trusted @nogc { data()[0]; }
+ }
+ else
+ {
+ static assert(false, "C++ runtime not supported");
+ }
+}
+
+
+// platform detail
+private:
+version (CppRuntime_Microsoft)
+{
+ import core.stdcpp.xutility : _ITERATOR_DEBUG_LEVEL;
+
+ extern (C++, struct) struct _Vec_base_types(_Ty, _Alloc0)
+ {
+ alias Ty = _Ty;
+ alias Alloc = _Alloc0;
+ }
+
+ extern (C++, class) struct _Vector_alloc(_Alloc_types)
+ {
+ import core.stdcpp.xutility : _Compressed_pair;
+ extern(D):
+ @nogc:
+
+ alias Ty = _Alloc_types.Ty;
+ alias Alloc = _Alloc_types.Alloc;
+ alias ValTy = _Vector_val!Ty;
+
+ void _Orphan_all() nothrow @safe
+ {
+ static if (is(typeof(ValTy._Base)))
+ _Mypair._Myval2._Base._Orphan_all();
+ }
+
+ static if (_ITERATOR_DEBUG_LEVEL != 0)
+ {
+ import core.stdcpp.xutility : _Container_proxy;
+
+ alias const_iterator = _Vector_const_iterator!(ValTy);
+
+ ~this()
+ {
+ _Free_proxy();
+ }
+
+ void _Alloc_proxy() @trusted
+ {
+ import core.lifetime : emplace;
+
+ alias _Alproxy = Alloc.rebind!_Container_proxy;
+ _Alproxy _Proxy_allocator = _Alproxy(_Mypair._Myval1);
+ _Mypair._Myval2._Base._Myproxy = _Proxy_allocator.allocate(1);
+ emplace(_Mypair._Myval2._Base._Myproxy);
+ _Mypair._Myval2._Base._Myproxy._Mycont = &_Mypair._Myval2._Base;
+ }
+ void _Free_proxy()
+ {
+ alias _Alproxy = Alloc.rebind!_Container_proxy;
+ _Alproxy _Proxy_allocator = _Alproxy(_Mypair._Myval1);
+ _Orphan_all();
+ destroy!false(_Mypair._Myval2._Base._Myproxy);
+ _Proxy_allocator.deallocate(_Mypair._Myval2._Base._Myproxy, 1);
+ _Mypair._Myval2._Base._Myproxy = null;
+ }
+ }
+
+ _Compressed_pair!(Alloc, ValTy) _Mypair;
+ }
+
+ extern (C++, class) struct _Vector_val(T)
+ {
+ import core.stdcpp.xutility : _Container_base;
+ import core.stdcpp.type_traits : is_empty;
+
+ alias pointer = T*;
+
+ static if (!is_empty!_Container_base.value)
+ _Container_base _Base;
+
+ pointer _Myfirst; // pointer to beginning of array
+ pointer _Mylast; // pointer to current end of sequence
+ pointer _Myend; // pointer to end of array
+ }
+
+ static if (_ITERATOR_DEBUG_LEVEL > 0)
+ {
+ extern (C++, class) struct _Vector_const_iterator(_Myvec)
+ {
+ import core.stdcpp.xutility : _Iterator_base;
+ import core.stdcpp.type_traits : is_empty;
+
+ static if (!is_empty!_Iterator_base.value)
+ _Iterator_base _Base;
+ _Myvec.pointer _Ptr;
+ }
+ }
+}
diff --git a/libphobos/libdruntime/core/stdcpp/xutility.d b/libphobos/libdruntime/core/stdcpp/xutility.d
new file mode 100644
index 00000000000..fa61701e1cf
--- /dev/null
+++ b/libphobos/libdruntime/core/stdcpp/xutility.d
@@ -0,0 +1,427 @@
+/**
+ * D header file for interaction with Microsoft C++ <xutility>
+ *
+ * Copyright: Copyright (c) 2018 D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Manu Evans
+ * Source: $(DRUNTIMESRC core/stdcpp/xutility.d)
+ */
+
+module core.stdcpp.xutility;
+
+@nogc:
+
+version (CppRuntime_Clang)
+{
+ import core.internal.traits : AliasSeq;
+ enum StdNamespace = AliasSeq!("std", "__1");
+}
+else
+{
+ enum StdNamespace = "std";
+}
+
+enum CppStdRevision : uint
+{
+ cpp98 = 199711,
+ cpp11 = 201103,
+ cpp14 = 201402,
+ cpp17 = 201703
+}
+
+enum __cplusplus = __traits(getTargetInfo, "cppStd");
+
+// wrangle C++ features
+enum __cpp_sized_deallocation = __cplusplus >= CppStdRevision.cpp14 || is(typeof(_MSC_VER)) ? 201309 : 0;
+enum __cpp_aligned_new = __cplusplus >= CppStdRevision.cpp17 ? 201606 : 0;
+
+
+version (CppRuntime_Microsoft)
+{
+ import core.stdcpp.type_traits : is_empty;
+
+ version (_MSC_VER_1200)
+ enum _MSC_VER = 1200;
+ else version (_MSC_VER_1300)
+ enum _MSC_VER = 1300;
+ else version (_MSC_VER_1310)
+ enum _MSC_VER = 1310;
+ else version (_MSC_VER_1400)
+ enum _MSC_VER = 1400;
+ else version (_MSC_VER_1500)
+ enum _MSC_VER = 1500;
+ else version (_MSC_VER_1600)
+ enum _MSC_VER = 1600;
+ else version (_MSC_VER_1700)
+ enum _MSC_VER = 1700;
+ else version (_MSC_VER_1800)
+ enum _MSC_VER = 1800;
+ else version (_MSC_VER_1900)
+ enum _MSC_VER = 1900;
+ else version (_MSC_VER_1910)
+ enum _MSC_VER = 1910;
+ else version (_MSC_VER_1911)
+ enum _MSC_VER = 1911;
+ else version (_MSC_VER_1912)
+ enum _MSC_VER = 1912;
+ else version (_MSC_VER_1913)
+ enum _MSC_VER = 1913;
+ else version (_MSC_VER_1914)
+ enum _MSC_VER = 1914;
+ else version (_MSC_VER_1915)
+ enum _MSC_VER = 1915;
+ else version (_MSC_VER_1916)
+ enum _MSC_VER = 1916;
+ else version (_MSC_VER_1920)
+ enum _MSC_VER = 1920;
+ else version (_MSC_VER_1921)
+ enum _MSC_VER = 1921;
+ else version (_MSC_VER_1922)
+ enum _MSC_VER = 1922;
+ else version (_MSC_VER_1923)
+ enum _MSC_VER = 1923;
+ else
+ enum _MSC_VER = 1923; // assume most recent compiler version
+
+ // Client code can mixin the set of MSVC linker directives
+ mixin template MSVCLinkDirectives(bool failMismatch = false)
+ {
+ import core.stdcpp.xutility : __CXXLIB__, _ITERATOR_DEBUG_LEVEL;
+
+ static if (__CXXLIB__ == "libcmtd")
+ {
+ pragma(lib, "libcpmtd");
+ static if (failMismatch)
+ pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MTd_StaticDebug");
+ }
+ else static if (__CXXLIB__ == "msvcrtd")
+ {
+ pragma(lib, "msvcprtd");
+ static if (failMismatch)
+ pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MDd_DynamicDebug");
+ }
+ else static if (__CXXLIB__ == "libcmt")
+ {
+ pragma(lib, "libcpmt");
+ static if (failMismatch)
+ pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MT_StaticRelease");
+ }
+ else static if (__CXXLIB__ == "msvcrt")
+ {
+ pragma(lib, "msvcprt");
+ static if (failMismatch)
+ pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MD_DynamicRelease");
+ }
+ static if (failMismatch)
+ pragma(linkerDirective, "/FAILIFMISMATCH:_ITERATOR_DEBUG_LEVEL=" ~ ('0' + _ITERATOR_DEBUG_LEVEL));
+ }
+
+ // HACK: should we guess _DEBUG for `debug` builds?
+ version (NDEBUG) {}
+ else debug version = _DEBUG;
+
+ // By specific user request
+ version (_ITERATOR_DEBUG_LEVEL_0)
+ enum _ITERATOR_DEBUG_LEVEL = 0;
+ else version (_ITERATOR_DEBUG_LEVEL_1)
+ enum _ITERATOR_DEBUG_LEVEL = 1;
+ else version (_ITERATOR_DEBUG_LEVEL_2)
+ enum _ITERATOR_DEBUG_LEVEL = 2;
+ else
+ {
+ // Match the C Runtime
+ static if (__CXXLIB__ == "libcmtd" || __CXXLIB__ == "msvcrtd")
+ enum _ITERATOR_DEBUG_LEVEL = 2;
+ else static if (__CXXLIB__ == "libcmt" || __CXXLIB__ == "msvcrt" ||
+ __CXXLIB__ == "msvcrt100" || __CXXLIB__ == "msvcrt110" || __CXXLIB__ == "msvcrt120")
+ enum _ITERATOR_DEBUG_LEVEL = 0;
+ else
+ {
+ static if (__CXXLIB__.length > 0)
+ pragma(msg, "Unrecognised C++ runtime library '" ~ __CXXLIB__ ~ "'");
+
+ // No runtime specified; as a best-guess, -release will produce code that matches the MSVC release CRT
+ version (_DEBUG)
+ enum _ITERATOR_DEBUG_LEVEL = 2;
+ else
+ enum _ITERATOR_DEBUG_LEVEL = 0;
+ }
+ }
+
+ // convenient alias for the C++ std library name
+ enum __CXXLIB__ = __traits(getTargetInfo, "cppRuntimeLibrary");
+
+extern(C++, "std"):
+package:
+ enum _LOCK_DEBUG = 3;
+
+ extern(C++, class) struct _Lockit
+ {
+ this(int) nothrow @nogc @safe;
+ ~this() nothrow @nogc @safe;
+
+ private:
+ int _Locktype;
+ }
+ void dummyDtor() { assert(false); }
+ pragma(linkerDirective, "/ALTERNATENAME:" ~ _Lockit.__dtor.mangleof ~ "=" ~ dummyDtor.mangleof);
+
+ struct _Container_base0
+ {
+ extern(D):
+ void _Orphan_all()() nothrow @nogc @safe {}
+ void _Swap_all()(ref _Container_base0) nothrow @nogc @safe {}
+ void _Swap_proxy_and_iterators()(ref _Container_base0) nothrow {}
+ }
+ struct _Iterator_base0
+ {
+ extern(D):
+ void _Adopt()(const(void)*) nothrow @nogc @safe {}
+ const(_Container_base0)* _Getcont()() const nothrow @nogc @safe { return null; }
+
+ enum bool _Unwrap_when_unverified = true;
+ }
+
+ struct _Container_proxy
+ {
+ const(_Container_base12)* _Mycont;
+ _Iterator_base12* _Myfirstiter;
+ }
+
+ struct _Container_base12
+ {
+ extern(D):
+ inout(_Iterator_base12*)*_Getpfirst()() inout nothrow @nogc @safe
+ {
+ return _Myproxy == null ? null : &_Myproxy._Myfirstiter;
+ }
+ void _Orphan_all()() nothrow @nogc @safe
+ {
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ {
+ if (_Myproxy != null)
+ {
+ auto _Lock = _Lockit(_LOCK_DEBUG);
+ for (_Iterator_base12 **_Pnext = &_Myproxy._Myfirstiter; *_Pnext != null; *_Pnext = (*_Pnext)._Mynextiter)
+ (*_Pnext)._Myproxy = null;
+ _Myproxy._Myfirstiter = null;
+ }
+ }
+ }
+// void _Swap_all()(ref _Container_base12) nothrow @nogc;
+
+ void _Swap_proxy_and_iterators()(ref _Container_base12 _Right) nothrow
+ {
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ auto _Lock = _Lockit(_LOCK_DEBUG);
+
+ _Container_proxy* _Temp = _Myproxy;
+ _Myproxy = _Right._Myproxy;
+ _Right._Myproxy = _Temp;
+
+ if (_Myproxy)
+ _Myproxy._Mycont = &this;
+
+ if (_Right._Myproxy)
+ _Right._Myproxy._Mycont = &_Right;
+ }
+
+ _Container_proxy* _Myproxy;
+ }
+
+ struct _Iterator_base12
+ {
+ extern(D):
+ void _Adopt()(_Container_base12 *_Parent) nothrow @nogc @safe
+ {
+ if (_Parent == null)
+ {
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ {
+ auto _Lock = _Lockit(_LOCK_DEBUG);
+ _Orphan_me();
+ }
+ }
+ else
+ {
+ _Container_proxy *_Parent_proxy = _Parent._Myproxy;
+
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ {
+ if (_Myproxy != _Parent_proxy)
+ {
+ auto _Lock = _Lockit(_LOCK_DEBUG);
+ _Orphan_me();
+ _Mynextiter = _Parent_proxy._Myfirstiter;
+ _Parent_proxy._Myfirstiter = &this;
+ _Myproxy = _Parent_proxy;
+ }
+ }
+ else
+ _Myproxy = _Parent_proxy;
+ }
+ }
+ void _Clrcont()() nothrow @nogc @safe
+ {
+ _Myproxy = null;
+ }
+ const(_Container_base12)* _Getcont()() const nothrow @nogc @safe
+ {
+ return _Myproxy == null ? null : _Myproxy._Mycont;
+ }
+ inout(_Iterator_base12*)*_Getpnext()() inout nothrow @nogc @safe
+ {
+ return &_Mynextiter;
+ }
+ void _Orphan_me()() nothrow @nogc @safe
+ {
+ static if (_ITERATOR_DEBUG_LEVEL == 2)
+ {
+ if (_Myproxy != null)
+ {
+ _Iterator_base12 **_Pnext = &_Myproxy._Myfirstiter;
+ while (*_Pnext != null && *_Pnext != &this)
+ _Pnext = &(*_Pnext)._Mynextiter;
+ assert(*_Pnext, "ITERATOR LIST CORRUPTED!");
+ *_Pnext = _Mynextiter;
+ _Myproxy = null;
+ }
+ }
+ }
+
+ enum bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0;
+
+ _Container_proxy *_Myproxy;
+ _Iterator_base12 *_Mynextiter;
+ }
+
+ static if (_ITERATOR_DEBUG_LEVEL == 0)
+ {
+ alias _Container_base = _Container_base0;
+ alias _Iterator_base = _Iterator_base0;
+ }
+ else
+ {
+ alias _Container_base = _Container_base12;
+ alias _Iterator_base = _Iterator_base12;
+ }
+
+ extern (C++, class) struct _Compressed_pair(_Ty1, _Ty2, bool Ty1Empty = is_empty!_Ty1.value)
+ {
+ pragma (inline, true):
+ extern(D):
+ pure nothrow @nogc:
+ enum _HasFirst = !Ty1Empty;
+
+ ref inout(_Ty1) first() inout @safe { return _Myval1; }
+ ref inout(_Ty2) second() inout @safe { return _Myval2; }
+
+ static if (!Ty1Empty)
+ _Ty1 _Myval1;
+ else
+ {
+ @property ref inout(_Ty1) _Myval1() inout @trusted { return *_GetBase(); }
+ private inout(_Ty1)* _GetBase() inout @trusted { return cast(inout(_Ty1)*)&this; }
+ }
+ _Ty2 _Myval2;
+ }
+
+ // these are all [[noreturn]]
+ void _Xbad_alloc() nothrow;
+ void _Xinvalid_argument(const(char)* message) nothrow;
+ void _Xlength_error(const(char)* message) nothrow;
+ void _Xout_of_range(const(char)* message) nothrow;
+ void _Xoverflow_error(const(char)* message) nothrow;
+ void _Xruntime_error(const(char)* message) nothrow;
+}
+else version (CppRuntime_Clang)
+{
+ import core.stdcpp.type_traits : is_empty;
+
+extern(C++, "std"):
+
+ extern (C++, class) struct __compressed_pair(_T1, _T2)
+ {
+ pragma (inline, true):
+ extern(D):
+ enum Ty1Empty = is_empty!_T1.value;
+ enum Ty2Empty = is_empty!_T2.value;
+
+ ref inout(_T1) first() inout nothrow @safe @nogc { return __value1_; }
+ ref inout(_T2) second() inout nothrow @safe @nogc { return __value2_; }
+
+ private:
+ private inout(_T1)* __get_base1() inout { return cast(inout(_T1)*)&this; }
+ private inout(_T2)* __get_base2() inout { return cast(inout(_T2)*)&__get_base1()[Ty1Empty ? 0 : 1]; }
+
+ static if (!Ty1Empty)
+ _T1 __value1_;
+ else
+ @property ref inout(_T1) __value1_() inout nothrow @trusted @nogc { return *__get_base1(); }
+ static if (!Ty2Empty)
+ _T2 __value2_;
+ else
+ @property ref inout(_T2) __value2_() inout nothrow @trusted @nogc { return *__get_base2(); }
+ }
+}
+version (CppRuntime_Gcc)
+{
+ import core.atomic;
+
+ alias _Atomic_word = int;
+
+ void __atomic_add_dispatch()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
+ {
+ version (__GTHREADS)
+ {
+ // TODO: check __gthread_active_p()
+// if (__gthread_active_p())
+ __atomic_add(__mem, __val);
+// }
+// else
+// __atomic_add_single(__mem, __val);
+ }
+ else
+ __atomic_add_single(__mem, __val);
+ }
+
+ void __atomic_add()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
+ {
+ atomicFetchAdd!(MemoryOrder.acq_rel)(*__mem, __val);
+ }
+
+ void __atomic_add_single()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
+ {
+ *__mem += __val;
+ }
+
+ _Atomic_word __exchange_and_add_dispatch()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
+ {
+ version (__GTHREADS)
+ {
+ // TODO: check __gthread_active_p()
+ return __exchange_and_add(__mem, __val);
+
+// if (__gthread_active_p())
+// return __exchange_and_add(__mem, __val);
+// else
+// return __exchange_and_add_single(__mem, __val);
+ }
+ else
+ return __exchange_and_add_single(__mem, __val);
+ }
+
+ _Atomic_word __exchange_and_add()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
+ {
+ return atomicFetchAdd!(MemoryOrder.acq_rel)(*__mem, __val);
+ }
+
+ _Atomic_word __exchange_and_add_single()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
+ {
+ _Atomic_word __result = *__mem;
+ *__mem += __val;
+ return __result;
+ }
+}
diff --git a/libphobos/libdruntime/core/sync/barrier.d b/libphobos/libdruntime/core/sync/barrier.d
index dd54d5c7543..1d0442136b3 100644
--- a/libphobos/libdruntime/core/sync/barrier.d
+++ b/libphobos/libdruntime/core/sync/barrier.d
@@ -17,14 +17,8 @@ module core.sync.barrier;
public import core.sync.exception;
-private import core.sync.condition;
-private import core.sync.mutex;
-
-version (Posix)
-{
- private import core.stdc.errno;
- private import core.sys.posix.pthread;
-}
+import core.sync.condition;
+import core.sync.mutex;
////////////////////////////////////////////////////////////////////////////////
@@ -60,7 +54,7 @@ class Barrier
{
assert( limit > 0 );
}
- body
+ do
{
m_lock = new Mutex;
m_cond = new Condition( m_lock );
@@ -112,40 +106,35 @@ private:
// Unit Tests
////////////////////////////////////////////////////////////////////////////////
-
-version (unittest)
+unittest
{
- private import core.thread;
+ import core.thread;
+ int numThreads = 10;
+ auto barrier = new Barrier( numThreads );
+ auto synInfo = new Object;
+ int numReady = 0;
+ int numPassed = 0;
- unittest
+ void threadFn()
{
- int numThreads = 10;
- auto barrier = new Barrier( numThreads );
- auto synInfo = new Object;
- int numReady = 0;
- int numPassed = 0;
-
- void threadFn()
+ synchronized( synInfo )
{
- synchronized( synInfo )
- {
- ++numReady;
- }
- barrier.wait();
- synchronized( synInfo )
- {
- ++numPassed;
- }
+ ++numReady;
}
-
- auto group = new ThreadGroup;
-
- for ( int i = 0; i < numThreads; ++i )
+ barrier.wait();
+ synchronized( synInfo )
{
- group.create( &threadFn );
+ ++numPassed;
}
- group.joinAll();
- assert( numReady == numThreads && numPassed == numThreads );
}
+
+ auto group = new ThreadGroup;
+
+ for ( int i = 0; i < numThreads; ++i )
+ {
+ group.create( &threadFn );
+ }
+ group.joinAll();
+ assert( numReady == numThreads && numPassed == numThreads );
}
diff --git a/libphobos/libdruntime/core/sync/condition.d b/libphobos/libdruntime/core/sync/condition.d
index 8afa8f7cc38..674d78d60bb 100644
--- a/libphobos/libdruntime/core/sync/condition.d
+++ b/libphobos/libdruntime/core/sync/condition.d
@@ -22,20 +22,20 @@ public import core.time;
version (Windows)
{
- private import core.sync.semaphore;
- private import core.sys.windows.basetsd /+: HANDLE+/;
- private import core.sys.windows.winbase /+: CloseHandle, CreateSemaphoreA, CRITICAL_SECTION,
+ import core.sync.semaphore;
+ import core.sys.windows.basetsd /+: HANDLE+/;
+ import core.sys.windows.winbase /+: CloseHandle, CreateSemaphoreA, CRITICAL_SECTION,
DeleteCriticalSection, EnterCriticalSection, INFINITE, InitializeCriticalSection,
LeaveCriticalSection, ReleaseSemaphore, WAIT_OBJECT_0, WaitForSingleObject+/;
- private import core.sys.windows.windef /+: BOOL, DWORD+/;
- private import core.sys.windows.winerror /+: WAIT_TIMEOUT+/;
+ import core.sys.windows.windef /+: BOOL, DWORD+/;
+ import core.sys.windows.winerror /+: WAIT_TIMEOUT+/;
}
else version (Posix)
{
- private import core.sync.config;
- private import core.stdc.errno;
- private import core.sys.posix.pthread;
- private import core.sys.posix.time;
+ import core.sync.config;
+ import core.stdc.errno;
+ import core.sys.posix.pthread;
+ import core.sys.posix.time;
}
else
{
@@ -76,27 +76,71 @@ class Condition
*/
this( Mutex m ) nothrow @safe
{
+ this(m, true);
+ }
+
+ /// ditto
+ this( shared Mutex m ) shared nothrow @safe
+ {
+ this(m, true);
+ }
+
+ //
+ private this(this Q, M)( M m, bool _unused_ ) nothrow @trusted
+ if ((is(Q == Condition) && is(M == Mutex)) ||
+ (is(Q == shared Condition) && is(M == shared Mutex)))
+ {
version (Windows)
{
- m_blockLock = CreateSemaphoreA( null, 1, 1, null );
+ static if (is(Q == Condition))
+ {
+ alias HANDLE_TYPE = void*;
+ }
+ else
+ {
+ alias HANDLE_TYPE = shared(void*);
+ }
+ m_blockLock = cast(HANDLE_TYPE) CreateSemaphoreA( null, 1, 1, null );
if ( m_blockLock == m_blockLock.init )
throw new SyncError( "Unable to initialize condition" );
- scope(failure) CloseHandle( m_blockLock );
+ scope(failure) CloseHandle( cast(void*) m_blockLock );
- m_blockQueue = CreateSemaphoreA( null, 0, int.max, null );
+ m_blockQueue = cast(HANDLE_TYPE) CreateSemaphoreA( null, 0, int.max, null );
if ( m_blockQueue == m_blockQueue.init )
throw new SyncError( "Unable to initialize condition" );
- scope(failure) CloseHandle( m_blockQueue );
+ scope(failure) CloseHandle( cast(void*) m_blockQueue );
- InitializeCriticalSection( &m_unblockLock );
+ InitializeCriticalSection( cast(RTL_CRITICAL_SECTION*) &m_unblockLock );
m_assocMutex = m;
}
else version (Posix)
{
m_assocMutex = m;
- int rc = pthread_cond_init( &m_hndl, null );
- if ( rc )
- throw new SyncError( "Unable to initialize condition" );
+ static if ( is( typeof( pthread_condattr_setclock ) ) )
+ {
+ () @trusted
+ {
+ pthread_condattr_t attr = void;
+ int rc = pthread_condattr_init( &attr );
+ if ( rc )
+ throw new SyncError( "Unable to initialize condition" );
+ rc = pthread_condattr_setclock( &attr, CLOCK_MONOTONIC );
+ if ( rc )
+ throw new SyncError( "Unable to initialize condition" );
+ rc = pthread_cond_init( cast(pthread_cond_t*) &m_hndl, &attr );
+ if ( rc )
+ throw new SyncError( "Unable to initialize condition" );
+ rc = pthread_condattr_destroy( &attr );
+ if ( rc )
+ throw new SyncError( "Unable to initialize condition" );
+ } ();
+ }
+ else
+ {
+ int rc = pthread_cond_init( cast(pthread_cond_t*) &m_hndl, null );
+ if ( rc )
+ throw new SyncError( "Unable to initialize condition" );
+ }
}
}
@@ -135,12 +179,23 @@ class Condition
return m_assocMutex;
}
+ /// ditto
+ @property shared(Mutex) mutex() shared
+ {
+ return m_assocMutex;
+ }
+
// undocumented function for internal use
final @property Mutex mutex_nothrow() pure nothrow @safe @nogc
{
return m_assocMutex;
}
+ // ditto
+ final @property shared(Mutex) mutex_nothrow() shared pure nothrow @safe @nogc
+ {
+ return m_assocMutex;
+ }
////////////////////////////////////////////////////////////////////////////
// General Actions
@@ -155,19 +210,31 @@ class Condition
*/
void wait()
{
+ wait!(typeof(this))(true);
+ }
+
+ /// ditto
+ void wait() shared
+ {
+ wait!(typeof(this))(true);
+ }
+
+ /// ditto
+ void wait(this Q)( bool _unused_ )
+ if (is(Q == Condition) || is(Q == shared Condition))
+ {
version (Windows)
{
timedWait( INFINITE );
}
else version (Posix)
{
- int rc = pthread_cond_wait( &m_hndl, m_assocMutex.handleAddr() );
+ int rc = pthread_cond_wait( cast(pthread_cond_t*) &m_hndl, (cast(Mutex) m_assocMutex).handleAddr() );
if ( rc )
throw new SyncError( "Unable to wait for condition" );
}
}
-
/**
* Suspends the calling thread until a notification occurs or until the
* supplied time period has elapsed.
@@ -185,11 +252,24 @@ class Condition
* true if notified before the timeout and false if not.
*/
bool wait( Duration val )
+ {
+ return wait!(typeof(this))(val, true);
+ }
+
+ /// ditto
+ bool wait( Duration val ) shared
+ {
+ return wait!(typeof(this))(val, true);
+ }
+
+ /// ditto
+ bool wait(this Q)( Duration val, bool _unused_ )
+ if (is(Q == Condition) || is(Q == shared Condition))
in
{
assert( !val.isNegative );
}
- body
+ do
{
version (Windows)
{
@@ -209,8 +289,8 @@ class Condition
timespec t = void;
mktspec( t, val );
- int rc = pthread_cond_timedwait( &m_hndl,
- m_assocMutex.handleAddr(),
+ int rc = pthread_cond_timedwait( cast(pthread_cond_t*) &m_hndl,
+ (cast(Mutex) m_assocMutex).handleAddr(),
&t );
if ( !rc )
return true;
@@ -220,7 +300,6 @@ class Condition
}
}
-
/**
* Notifies one waiter.
*
@@ -229,19 +308,46 @@ class Condition
*/
void notify()
{
+ notify!(typeof(this))(true);
+ }
+
+ /// ditto
+ void notify() shared
+ {
+ notify!(typeof(this))(true);
+ }
+
+ /// ditto
+ void notify(this Q)( bool _unused_ )
+ if (is(Q == Condition) || is(Q == shared Condition))
+ {
version (Windows)
{
- notify( false );
+ notify_( false );
}
else version (Posix)
{
- int rc = pthread_cond_signal( &m_hndl );
+ // Since OS X 10.7 (Lion), pthread_cond_signal returns EAGAIN after retrying 8192 times,
+ // so need to retrying while it returns EAGAIN.
+ //
+ // 10.7.0 (Lion): http://www.opensource.apple.com/source/Libc/Libc-763.11/pthreads/pthread_cond.c
+ // 10.8.0 (Mountain Lion): http://www.opensource.apple.com/source/Libc/Libc-825.24/pthreads/pthread_cond.c
+ // 10.10.0 (Yosemite): http://www.opensource.apple.com/source/libpthread/libpthread-105.1.4/src/pthread_cond.c
+ // 10.11.0 (El Capitan): http://www.opensource.apple.com/source/libpthread/libpthread-137.1.1/src/pthread_cond.c
+ // 10.12.0 (Sierra): http://www.opensource.apple.com/source/libpthread/libpthread-218.1.3/src/pthread_cond.c
+ // 10.13.0 (High Sierra): http://www.opensource.apple.com/source/libpthread/libpthread-301.1.6/src/pthread_cond.c
+ // 10.14.0 (Mojave): http://www.opensource.apple.com/source/libpthread/libpthread-330.201.1/src/pthread_cond.c
+ // 10.14.1 (Mojave): http://www.opensource.apple.com/source/libpthread/libpthread-330.220.2/src/pthread_cond.c
+
+ int rc;
+ do {
+ rc = pthread_cond_signal( cast(pthread_cond_t*) &m_hndl );
+ } while ( rc == EAGAIN );
if ( rc )
throw new SyncError( "Unable to notify condition" );
}
}
-
/**
* Notifies all waiters.
*
@@ -250,40 +356,84 @@ class Condition
*/
void notifyAll()
{
+ notifyAll!(typeof(this))(true);
+ }
+
+ /// ditto
+ void notifyAll() shared
+ {
+ notifyAll!(typeof(this))(true);
+ }
+
+ /// ditto
+ void notifyAll(this Q)( bool _unused_ )
+ if (is(Q == Condition) || is(Q == shared Condition))
+ {
version (Windows)
{
- notify( true );
+ notify_( true );
}
else version (Posix)
{
- int rc = pthread_cond_broadcast( &m_hndl );
+ // Since OS X 10.7 (Lion), pthread_cond_broadcast returns EAGAIN after retrying 8192 times,
+ // so need to retrying while it returns EAGAIN.
+ //
+ // 10.7.0 (Lion): http://www.opensource.apple.com/source/Libc/Libc-763.11/pthreads/pthread_cond.c
+ // 10.8.0 (Mountain Lion): http://www.opensource.apple.com/source/Libc/Libc-825.24/pthreads/pthread_cond.c
+ // 10.10.0 (Yosemite): http://www.opensource.apple.com/source/libpthread/libpthread-105.1.4/src/pthread_cond.c
+ // 10.11.0 (El Capitan): http://www.opensource.apple.com/source/libpthread/libpthread-137.1.1/src/pthread_cond.c
+ // 10.12.0 (Sierra): http://www.opensource.apple.com/source/libpthread/libpthread-218.1.3/src/pthread_cond.c
+ // 10.13.0 (High Sierra): http://www.opensource.apple.com/source/libpthread/libpthread-301.1.6/src/pthread_cond.c
+ // 10.14.0 (Mojave): http://www.opensource.apple.com/source/libpthread/libpthread-330.201.1/src/pthread_cond.c
+ // 10.14.1 (Mojave): http://www.opensource.apple.com/source/libpthread/libpthread-330.220.2/src/pthread_cond.c
+
+ int rc;
+ do {
+ rc = pthread_cond_broadcast( cast(pthread_cond_t*) &m_hndl );
+ } while ( rc == EAGAIN );
if ( rc )
throw new SyncError( "Unable to notify condition" );
}
}
-
private:
version (Windows)
{
- bool timedWait( DWORD timeout )
+ bool timedWait(this Q)( DWORD timeout )
+ if (is(Q == Condition) || is(Q == shared Condition))
{
+ static if (is(Q == Condition))
+ {
+ auto op(string o, T, V1)(ref T val, V1 mod)
+ {
+ return mixin("val " ~ o ~ "mod");
+ }
+ }
+ else
+ {
+ auto op(string o, T, V1)(ref shared T val, V1 mod)
+ {
+ import core.atomic: atomicOp;
+ return atomicOp!o(val, mod);
+ }
+ }
+
int numSignalsLeft;
int numWaitersGone;
DWORD rc;
- rc = WaitForSingleObject( m_blockLock, INFINITE );
+ rc = WaitForSingleObject( cast(HANDLE) m_blockLock, INFINITE );
assert( rc == WAIT_OBJECT_0 );
- m_numWaitersBlocked++;
+ op!"+="(m_numWaitersBlocked, 1);
- rc = ReleaseSemaphore( m_blockLock, 1, null );
+ rc = ReleaseSemaphore( cast(HANDLE) m_blockLock, 1, null );
assert( rc );
m_assocMutex.unlock();
scope(failure) m_assocMutex.lock();
- rc = WaitForSingleObject( m_blockQueue, timeout );
+ rc = WaitForSingleObject( cast(HANDLE) m_blockQueue, timeout );
assert( rc == WAIT_OBJECT_0 || rc == WAIT_TIMEOUT );
bool timedOut = (rc == WAIT_TIMEOUT);
@@ -297,7 +447,7 @@ private:
// timeout (or canceled)
if ( m_numWaitersBlocked != 0 )
{
- m_numWaitersBlocked--;
+ op!"-="(m_numWaitersBlocked, 1);
// do not unblock next waiter below (already unblocked)
numSignalsLeft = 0;
}
@@ -307,12 +457,12 @@ private:
m_numWaitersGone = 1;
}
}
- if ( --m_numWaitersToUnblock == 0 )
+ if ( op!"-="(m_numWaitersToUnblock, 1) == 0 )
{
if ( m_numWaitersBlocked != 0 )
{
// open the gate
- rc = ReleaseSemaphore( m_blockLock, 1, null );
+ rc = ReleaseSemaphore( cast(HANDLE) m_blockLock, 1, null );
assert( rc );
// do not open the gate below again
numSignalsLeft = 0;
@@ -323,14 +473,14 @@ private:
}
}
}
- else if ( ++m_numWaitersGone == int.max / 2 )
+ else if ( op!"+="(m_numWaitersGone, 1) == int.max / 2 )
{
// timeout/canceled or spurious event :-)
- rc = WaitForSingleObject( m_blockLock, INFINITE );
+ rc = WaitForSingleObject( cast(HANDLE) m_blockLock, INFINITE );
assert( rc == WAIT_OBJECT_0 );
// something is going on here - test of timeouts?
- m_numWaitersBlocked -= m_numWaitersGone;
- rc = ReleaseSemaphore( m_blockLock, 1, null );
+ op!"-="(m_numWaitersBlocked, m_numWaitersGone);
+ rc = ReleaseSemaphore( cast(HANDLE) m_blockLock, 1, null );
assert( rc == WAIT_OBJECT_0 );
m_numWaitersGone = 0;
}
@@ -342,17 +492,17 @@ private:
// better now than spurious later (same as ResetEvent)
for ( ; numWaitersGone > 0; --numWaitersGone )
{
- rc = WaitForSingleObject( m_blockQueue, INFINITE );
+ rc = WaitForSingleObject( cast(HANDLE) m_blockQueue, INFINITE );
assert( rc == WAIT_OBJECT_0 );
}
// open the gate
- rc = ReleaseSemaphore( m_blockLock, 1, null );
+ rc = ReleaseSemaphore( cast(HANDLE) m_blockLock, 1, null );
assert( rc );
}
else if ( numSignalsLeft != 0 )
{
// unblock next waiter
- rc = ReleaseSemaphore( m_blockQueue, 1, null );
+ rc = ReleaseSemaphore( cast(HANDLE) m_blockQueue, 1, null );
assert( rc );
}
m_assocMutex.lock();
@@ -360,8 +510,25 @@ private:
}
- void notify( bool all )
+ void notify_(this Q)( bool all )
+ if (is(Q == Condition) || is(Q == shared Condition))
{
+ static if (is(Q == Condition))
+ {
+ auto op(string o, T, V1)(ref T val, V1 mod)
+ {
+ return mixin("val " ~ o ~ "mod");
+ }
+ }
+ else
+ {
+ auto op(string o, T, V1)(ref shared T val, V1 mod)
+ {
+ import core.atomic: atomicOp;
+ return atomicOp!o(val, mod);
+ }
+ }
+
DWORD rc;
EnterCriticalSection( &m_unblockLock );
@@ -376,23 +543,23 @@ private:
}
if ( all )
{
- m_numWaitersToUnblock += m_numWaitersBlocked;
+ op!"+="(m_numWaitersToUnblock, m_numWaitersBlocked);
m_numWaitersBlocked = 0;
}
else
{
- m_numWaitersToUnblock++;
- m_numWaitersBlocked--;
+ op!"+="(m_numWaitersToUnblock, 1);
+ op!"-="(m_numWaitersBlocked, 1);
}
LeaveCriticalSection( &m_unblockLock );
}
else if ( m_numWaitersBlocked > m_numWaitersGone )
{
- rc = WaitForSingleObject( m_blockLock, INFINITE );
+ rc = WaitForSingleObject( cast(HANDLE) m_blockLock, INFINITE );
assert( rc == WAIT_OBJECT_0 );
if ( 0 != m_numWaitersGone )
{
- m_numWaitersBlocked -= m_numWaitersGone;
+ op!"-="(m_numWaitersBlocked, m_numWaitersGone);
m_numWaitersGone = 0;
}
if ( all )
@@ -403,10 +570,10 @@ private:
else
{
m_numWaitersToUnblock = 1;
- m_numWaitersBlocked--;
+ op!"-="(m_numWaitersBlocked, 1);
}
LeaveCriticalSection( &m_unblockLock );
- rc = ReleaseSemaphore( m_blockQueue, 1, null );
+ rc = ReleaseSemaphore( cast(HANDLE) m_blockQueue, 1, null );
assert( rc );
}
else
@@ -439,12 +606,11 @@ private:
// Unit Tests
////////////////////////////////////////////////////////////////////////////////
-
-version (unittest)
+unittest
{
- private import core.thread;
- private import core.sync.mutex;
- private import core.sync.semaphore;
+ import core.thread;
+ import core.sync.mutex;
+ import core.sync.semaphore;
void testNotify()
@@ -601,11 +767,173 @@ version (unittest)
assert( !alertedTwo );
}
+ testNotify();
+ testNotifyAll();
+ testWaitTimeout();
+}
+
+unittest
+{
+ import core.thread;
+ import core.sync.mutex;
+ import core.sync.semaphore;
+
- unittest
+ void testNotify()
{
- testNotify();
- testNotifyAll();
- testWaitTimeout();
+ auto mutex = new shared Mutex;
+ auto condReady = new shared Condition( mutex );
+ auto semDone = new Semaphore;
+ auto synLoop = new Object;
+ int numWaiters = 10;
+ int numTries = 10;
+ int numReady = 0;
+ int numTotal = 0;
+ int numDone = 0;
+ int numPost = 0;
+
+ void waiter()
+ {
+ for ( int i = 0; i < numTries; ++i )
+ {
+ synchronized( mutex )
+ {
+ while ( numReady < 1 )
+ {
+ condReady.wait();
+ }
+ --numReady;
+ ++numTotal;
+ }
+
+ synchronized( synLoop )
+ {
+ ++numDone;
+ }
+ semDone.wait();
+ }
+ }
+
+ auto group = new ThreadGroup;
+
+ for ( int i = 0; i < numWaiters; ++i )
+ group.create( &waiter );
+
+ for ( int i = 0; i < numTries; ++i )
+ {
+ for ( int j = 0; j < numWaiters; ++j )
+ {
+ synchronized( mutex )
+ {
+ ++numReady;
+ condReady.notify();
+ }
+ }
+ while ( true )
+ {
+ synchronized( synLoop )
+ {
+ if ( numDone >= numWaiters )
+ break;
+ }
+ Thread.yield();
+ }
+ for ( int j = 0; j < numWaiters; ++j )
+ {
+ semDone.notify();
+ }
+ }
+
+ group.joinAll();
+ assert( numTotal == numWaiters * numTries );
+ }
+
+
+ void testNotifyAll()
+ {
+ auto mutex = new shared Mutex;
+ auto condReady = new shared Condition( mutex );
+ int numWaiters = 10;
+ int numReady = 0;
+ int numDone = 0;
+ bool alert = false;
+
+ void waiter()
+ {
+ synchronized( mutex )
+ {
+ ++numReady;
+ while ( !alert )
+ condReady.wait();
+ ++numDone;
+ }
+ }
+
+ auto group = new ThreadGroup;
+
+ for ( int i = 0; i < numWaiters; ++i )
+ group.create( &waiter );
+
+ while ( true )
+ {
+ synchronized( mutex )
+ {
+ if ( numReady >= numWaiters )
+ {
+ alert = true;
+ condReady.notifyAll();
+ break;
+ }
+ }
+ Thread.yield();
+ }
+ group.joinAll();
+ assert( numReady == numWaiters && numDone == numWaiters );
+ }
+
+
+ void testWaitTimeout()
+ {
+ auto mutex = new shared Mutex;
+ auto condReady = new shared Condition( mutex );
+ bool waiting = false;
+ bool alertedOne = true;
+ bool alertedTwo = true;
+
+ void waiter()
+ {
+ synchronized( mutex )
+ {
+ waiting = true;
+ // we never want to miss the notification (30s)
+ alertedOne = condReady.wait( dur!"seconds"(30) );
+ // but we don't want to wait long for the timeout (10ms)
+ alertedTwo = condReady.wait( dur!"msecs"(10) );
+ }
+ }
+
+ auto thread = new Thread( &waiter );
+ thread.start();
+
+ while ( true )
+ {
+ synchronized( mutex )
+ {
+ if ( waiting )
+ {
+ condReady.notify();
+ break;
+ }
+ }
+ Thread.yield();
+ }
+ thread.join();
+ assert( waiting );
+ assert( alertedOne );
+ assert( !alertedTwo );
}
+
+ testNotify();
+ testNotifyAll();
+ testWaitTimeout();
}
diff --git a/libphobos/libdruntime/core/sync/config.d b/libphobos/libdruntime/core/sync/config.d
index b2de225bdb4..39f7a8cd769 100644
--- a/libphobos/libdruntime/core/sync/config.d
+++ b/libphobos/libdruntime/core/sync/config.d
@@ -3,7 +3,7 @@
* specific to this package.
*
* Copyright: Copyright Sean Kelly 2005 - 2009.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Sean Kelly
* Source: $(DRUNTIMESRC core/sync/_config.d)
*/
@@ -18,16 +18,17 @@ module core.sync.config;
version (Posix)
{
- private import core.sys.posix.time;
- private import core.sys.posix.sys.time;
- private import core.time;
+ import core.sys.posix.pthread;
+ import core.sys.posix.time;
+ import core.sys.posix.sys.time;
+ import core.time;
- void mktspec( ref timespec t ) nothrow
+ void mktspec( ref timespec t ) nothrow @nogc
{
- static if ( false && is( typeof( clock_gettime ) ) )
+ static if ( is (typeof ( pthread_condattr_setclock ) ) )
{
- clock_gettime( CLOCK_REALTIME, &t );
+ clock_gettime( CLOCK_MONOTONIC, &t );
}
else
{
@@ -41,14 +42,14 @@ version (Posix)
}
- void mktspec( ref timespec t, Duration delta ) nothrow
+ void mktspec( ref timespec t, Duration delta ) nothrow @nogc
{
mktspec( t );
mvtspec( t, delta );
}
- void mvtspec( ref timespec t, Duration delta ) nothrow
+ void mvtspec( ref timespec t, Duration delta ) nothrow @nogc
{
auto val = delta;
val += dur!"seconds"( t.tv_sec );
diff --git a/libphobos/libdruntime/core/sync/event.d b/libphobos/libdruntime/core/sync/event.d
new file mode 100644
index 00000000000..37951061d93
--- /dev/null
+++ b/libphobos/libdruntime/core/sync/event.d
@@ -0,0 +1,345 @@
+/**
+ * The event module provides a primitive for lightweight signaling of other threads
+ * (emulating Windows events on Posix)
+ *
+ * Copyright: Copyright (c) 2019 D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Rainer Schuetze
+ * Source: $(DRUNTIMESRC core/sync/event.d)
+ */
+module core.sync.event;
+
+version (Windows)
+{
+ import core.sys.windows.basetsd /+: HANDLE +/;
+ import core.sys.windows.winerror /+: WAIT_TIMEOUT +/;
+ import core.sys.windows.winbase /+: CreateEvent, CloseHandle, SetEvent, ResetEvent,
+ WaitForSingleObject, INFINITE, WAIT_OBJECT_0+/;
+}
+else version (Posix)
+{
+ import core.sys.posix.pthread;
+ import core.sys.posix.sys.types;
+ import core.sys.posix.time;
+}
+else
+{
+ static assert(false, "Platform not supported");
+}
+
+import core.time;
+import core.internal.abort : abort;
+
+/**
+ * represents an event. Clients of an event are suspended while waiting
+ * for the event to be "signaled".
+ *
+ * Implemented using `pthread_mutex` and `pthread_condition` on Posix and
+ * `CreateEvent` and `SetEvent` on Windows.
+---
+import core.sync.event, core.thread, std.file;
+
+struct ProcessFile
+{
+ ThreadGroup group;
+ Event event;
+ void[] buffer;
+
+ void doProcess()
+ {
+ event.wait();
+ // process buffer
+ }
+
+ void process(string filename)
+ {
+ event.initialize(true, false);
+ group = new ThreadGroup;
+ for (int i = 0; i < 10; ++i)
+ group.create(&doProcess);
+
+ buffer = std.file.read(filename);
+ event.set();
+ group.joinAll();
+ event.terminate();
+ }
+}
+---
+ */
+struct Event
+{
+nothrow @nogc:
+ /**
+ * Creates an event object.
+ *
+ * Params:
+ * manualReset = the state of the event is not reset automatically after resuming waiting clients
+ * initialState = initial state of the signal
+ */
+ this(bool manualReset, bool initialState)
+ {
+ initialize(manualReset, initialState);
+ }
+
+ /**
+ * Initializes an event object. Does nothing if the event is already initialized.
+ *
+ * Params:
+ * manualReset = the state of the event is not reset automatically after resuming waiting clients
+ * initialState = initial state of the signal
+ */
+ void initialize(bool manualReset, bool initialState)
+ {
+ version (Windows)
+ {
+ if (m_event)
+ return;
+ m_event = CreateEvent(null, manualReset, initialState, null);
+ m_event || abort("Error: CreateEvent failed.");
+ }
+ else version (Posix)
+ {
+ if (m_initalized)
+ return;
+ pthread_mutex_init(cast(pthread_mutex_t*) &m_mutex, null) == 0 ||
+ abort("Error: pthread_mutex_init failed.");
+ static if ( is( typeof( pthread_condattr_setclock ) ) )
+ {
+ pthread_condattr_t attr = void;
+ pthread_condattr_init(&attr) == 0 ||
+ abort("Error: pthread_condattr_init failed.");
+ pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0 ||
+ abort("Error: pthread_condattr_setclock failed.");
+ pthread_cond_init(&m_cond, &attr) == 0 ||
+ abort("Error: pthread_cond_init failed.");
+ pthread_condattr_destroy(&attr) == 0 ||
+ abort("Error: pthread_condattr_destroy failed.");
+ }
+ else
+ {
+ pthread_cond_init(&m_cond, null) == 0 ||
+ abort("Error: pthread_cond_init failed.");
+ }
+ m_state = initialState;
+ m_manualReset = manualReset;
+ m_initalized = true;
+ }
+ }
+
+ // copying not allowed, can produce resource leaks
+ @disable this(this);
+ @disable void opAssign(Event);
+
+ ~this()
+ {
+ terminate();
+ }
+
+ /**
+ * deinitialize event. Does nothing if the event is not initialized. There must not be
+ * threads currently waiting for the event to be signaled.
+ */
+ void terminate()
+ {
+ version (Windows)
+ {
+ if (m_event)
+ CloseHandle(m_event);
+ m_event = null;
+ }
+ else version (Posix)
+ {
+ if (m_initalized)
+ {
+ pthread_mutex_destroy(&m_mutex) == 0 ||
+ abort("Error: pthread_mutex_destroy failed.");
+ pthread_cond_destroy(&m_cond) == 0 ||
+ abort("Error: pthread_cond_destroy failed.");
+ m_initalized = false;
+ }
+ }
+ }
+
+
+ /// Set the event to "signaled", so that waiting clients are resumed
+ void set()
+ {
+ version (Windows)
+ {
+ if (m_event)
+ SetEvent(m_event);
+ }
+ else version (Posix)
+ {
+ if (m_initalized)
+ {
+ pthread_mutex_lock(&m_mutex);
+ m_state = true;
+ pthread_cond_broadcast(&m_cond);
+ pthread_mutex_unlock(&m_mutex);
+ }
+ }
+ }
+
+ /// Reset the event manually
+ void reset()
+ {
+ version (Windows)
+ {
+ if (m_event)
+ ResetEvent(m_event);
+ }
+ else version (Posix)
+ {
+ if (m_initalized)
+ {
+ pthread_mutex_lock(&m_mutex);
+ m_state = false;
+ pthread_mutex_unlock(&m_mutex);
+ }
+ }
+ }
+
+ /**
+ * Wait for the event to be signaled without timeout.
+ *
+ * Returns:
+ * `true` if the event is in signaled state, `false` if the event is uninitialized or another error occured
+ */
+ bool wait()
+ {
+ version (Windows)
+ {
+ return m_event && WaitForSingleObject(m_event, INFINITE) == WAIT_OBJECT_0;
+ }
+ else version (Posix)
+ {
+ return wait(Duration.max);
+ }
+ }
+
+ /**
+ * Wait for the event to be signaled with timeout.
+ *
+ * Params:
+ * tmout = the maximum time to wait
+ * Returns:
+ * `true` if the event is in signaled state, `false` if the event was nonsignaled for the given time or
+ * the event is uninitialized or another error occured
+ */
+ bool wait(Duration tmout)
+ {
+ version (Windows)
+ {
+ if (!m_event)
+ return false;
+
+ auto maxWaitMillis = dur!("msecs")(uint.max - 1);
+
+ while (tmout > maxWaitMillis)
+ {
+ auto res = WaitForSingleObject(m_event, uint.max - 1);
+ if (res != WAIT_TIMEOUT)
+ return res == WAIT_OBJECT_0;
+ tmout -= maxWaitMillis;
+ }
+ auto ms = cast(uint)(tmout.total!"msecs");
+ return WaitForSingleObject(m_event, ms) == WAIT_OBJECT_0;
+ }
+ else version (Posix)
+ {
+ if (!m_initalized)
+ return false;
+
+ pthread_mutex_lock(&m_mutex);
+
+ int result = 0;
+ if (!m_state)
+ {
+ if (tmout == Duration.max)
+ {
+ result = pthread_cond_wait(&m_cond, &m_mutex);
+ }
+ else
+ {
+ import core.sync.config;
+
+ timespec t = void;
+ mktspec(t, tmout);
+
+ result = pthread_cond_timedwait(&m_cond, &m_mutex, &t);
+ }
+ }
+ if (result == 0 && !m_manualReset)
+ m_state = false;
+
+ pthread_mutex_unlock(&m_mutex);
+
+ return result == 0;
+ }
+ }
+
+private:
+ version (Windows)
+ {
+ HANDLE m_event;
+ }
+ else version (Posix)
+ {
+ pthread_mutex_t m_mutex;
+ pthread_cond_t m_cond;
+ bool m_initalized;
+ bool m_state;
+ bool m_manualReset;
+ }
+}
+
+// Test single-thread (non-shared) use.
+@nogc nothrow unittest
+{
+ // auto-reset, initial state false
+ Event ev1 = Event(false, false);
+ assert(!ev1.wait(1.dur!"msecs"));
+ ev1.set();
+ assert(ev1.wait());
+ assert(!ev1.wait(1.dur!"msecs"));
+
+ // manual-reset, initial state true
+ Event ev2 = Event(true, true);
+ assert(ev2.wait());
+ assert(ev2.wait());
+ ev2.reset();
+ assert(!ev2.wait(1.dur!"msecs"));
+}
+
+unittest
+{
+ import core.thread, core.atomic;
+
+ scope event = new Event(true, false);
+ int numThreads = 10;
+ shared int numRunning = 0;
+
+ void testFn()
+ {
+ event.wait(8.dur!"seconds"); // timeout below limit for druntime test_runner
+ numRunning.atomicOp!"+="(1);
+ }
+
+ auto group = new ThreadGroup;
+
+ for (int i = 0; i < numThreads; ++i)
+ group.create(&testFn);
+
+ auto start = MonoTime.currTime;
+ assert(numRunning == 0);
+
+ event.set();
+ group.joinAll();
+
+ assert(numRunning == numThreads);
+
+ assert(MonoTime.currTime - start < 5.dur!"seconds");
+}
diff --git a/libphobos/libdruntime/core/sync/mutex.d b/libphobos/libdruntime/core/sync/mutex.d
index 024009f48aa..b153ab9aef0 100644
--- a/libphobos/libdruntime/core/sync/mutex.d
+++ b/libphobos/libdruntime/core/sync/mutex.d
@@ -20,13 +20,13 @@ public import core.sync.exception;
version (Windows)
{
- private import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection,
+ import core.sys.windows.winbase /+: CRITICAL_SECTION, DeleteCriticalSection,
EnterCriticalSection, InitializeCriticalSection, LeaveCriticalSection,
TryEnterCriticalSection+/;
}
else version (Posix)
{
- private import core.sys.posix.pthread;
+ import core.sys.posix.pthread;
}
else
{
@@ -129,7 +129,7 @@ class Mutex :
assert(obj.__monitor is null,
"The provided object has a monitor already set!");
}
- body
+ do
{
this();
obj.__monitor = cast(void*) &m_proxy;
@@ -345,14 +345,10 @@ unittest
@system @nogc nothrow unittest
{
import core.stdc.stdlib : malloc, free;
+ import core.lifetime : emplace;
- void* p = malloc(__traits(classInstanceSize, Mutex));
-
- auto ti = typeid(Mutex);
- p[0 .. ti.initializer.length] = ti.initializer[];
-
- shared Mutex mtx = cast(shared(Mutex)) p;
- mtx.__ctor();
+ auto mtx = cast(shared Mutex) malloc(__traits(classInstanceSize, Mutex));
+ emplace(mtx);
mtx.lock_nothrow();
diff --git a/libphobos/libdruntime/core/sync/rwmutex.d b/libphobos/libdruntime/core/sync/rwmutex.d
index ba94a9ee9a9..89ef6671e2f 100644
--- a/libphobos/libdruntime/core/sync/rwmutex.d
+++ b/libphobos/libdruntime/core/sync/rwmutex.d
@@ -17,13 +17,13 @@ module core.sync.rwmutex;
public import core.sync.exception;
-private import core.sync.condition;
-private import core.sync.mutex;
-private import core.memory;
+import core.sync.condition;
+import core.sync.mutex;
+import core.memory;
version (Posix)
{
- private import core.sys.posix.pthread;
+ import core.sys.posix.pthread;
}
@@ -225,6 +225,51 @@ class ReadWriteMutex
}
}
+ /**
+ * Attempts to acquire a read lock on the enclosing mutex. If one can
+ * be obtained without blocking, the lock is acquired and true is
+ * returned. If not, the function blocks until either the lock can be
+ * obtained or the time elapsed exceeds $(D_PARAM timeout), returning
+ * true if the lock was acquired and false if the function timed out.
+ *
+ * Params:
+ * timeout = maximum amount of time to wait for the lock
+ * Returns:
+ * true if the lock was acquired and false if not.
+ */
+ bool tryLock(Duration timeout)
+ {
+ synchronized( m_commonMutex )
+ {
+ if (!shouldQueueReader)
+ {
+ ++m_numActiveReaders;
+ return true;
+ }
+
+ enum zero = Duration.zero();
+ if (timeout <= zero)
+ return false;
+
+ ++m_numQueuedReaders;
+ scope(exit) --m_numQueuedReaders;
+
+ enum maxWaitPerCall = dur!"hours"(24 * 365); // Avoid problems calling wait with huge Duration.
+ const initialTime = MonoTime.currTime;
+ m_readerQueue.wait(timeout < maxWaitPerCall ? timeout : maxWaitPerCall);
+ while (shouldQueueReader)
+ {
+ const timeElapsed = MonoTime.currTime - initialTime;
+ if (timeElapsed >= timeout)
+ return false;
+ auto nextWait = timeout - timeElapsed;
+ m_readerQueue.wait(nextWait < maxWaitPerCall ? nextWait : maxWaitPerCall);
+ }
+ ++m_numActiveReaders;
+ return true;
+ }
+ }
+
private:
@property bool shouldQueueReader()
@@ -341,6 +386,50 @@ class ReadWriteMutex
}
}
+ /**
+ * Attempts to acquire a write lock on the enclosing mutex. If one can
+ * be obtained without blocking, the lock is acquired and true is
+ * returned. If not, the function blocks until either the lock can be
+ * obtained or the time elapsed exceeds $(D_PARAM timeout), returning
+ * true if the lock was acquired and false if the function timed out.
+ *
+ * Params:
+ * timeout = maximum amount of time to wait for the lock
+ * Returns:
+ * true if the lock was acquired and false if not.
+ */
+ bool tryLock(Duration timeout)
+ {
+ synchronized( m_commonMutex )
+ {
+ if (!shouldQueueWriter)
+ {
+ ++m_numActiveWriters;
+ return true;
+ }
+
+ enum zero = Duration.zero();
+ if (timeout <= zero)
+ return false;
+
+ ++m_numQueuedWriters;
+ scope(exit) --m_numQueuedWriters;
+
+ enum maxWaitPerCall = dur!"hours"(24 * 365); // Avoid problems calling wait with huge Duration.
+ const initialTime = MonoTime.currTime;
+ m_writerQueue.wait(timeout < maxWaitPerCall ? timeout : maxWaitPerCall);
+ while (shouldQueueWriter)
+ {
+ const timeElapsed = MonoTime.currTime - initialTime;
+ if (timeElapsed >= timeout)
+ return false;
+ auto nextWait = timeout - timeElapsed;
+ m_writerQueue.wait(nextWait < maxWaitPerCall ? nextWait : maxWaitPerCall);
+ }
+ ++m_numActiveWriters;
+ return true;
+ }
+ }
private:
@property bool shouldQueueWriter()
@@ -526,3 +615,79 @@ unittest
runTest(ReadWriteMutex.Policy.PREFER_READERS);
runTest(ReadWriteMutex.Policy.PREFER_WRITERS);
}
+
+unittest
+{
+ import core.atomic, core.thread;
+ __gshared ReadWriteMutex rwmutex;
+ shared static bool threadTriedOnceToGetLock;
+ shared static bool threadFinallyGotLock;
+
+ rwmutex = new ReadWriteMutex();
+ atomicFence;
+ const maxTimeAllowedForTest = dur!"seconds"(20);
+ // Test ReadWriteMutex.Reader.tryLock(Duration).
+ {
+ static void testReaderTryLock()
+ {
+ assert(!rwmutex.reader.tryLock(Duration.min));
+ threadTriedOnceToGetLock.atomicStore(true);
+ assert(rwmutex.reader.tryLock(Duration.max));
+ threadFinallyGotLock.atomicStore(true);
+ rwmutex.reader.unlock;
+ }
+ assert(rwmutex.writer.tryLock(Duration.zero), "should have been able to obtain lock without blocking");
+ auto otherThread = new Thread(&testReaderTryLock).start;
+ const failIfThisTimeisReached = MonoTime.currTime + maxTimeAllowedForTest;
+ Thread.yield;
+ // We started otherThread with the writer lock held so otherThread's
+ // first rwlock.reader.tryLock with timeout Duration.min should fail.
+ while (!threadTriedOnceToGetLock.atomicLoad)
+ {
+ assert(MonoTime.currTime < failIfThisTimeisReached, "timed out");
+ Thread.yield;
+ }
+ rwmutex.writer.unlock;
+ // Soon after we release the writer lock otherThread's second
+ // rwlock.reader.tryLock with timeout Duration.max should succeed.
+ while (!threadFinallyGotLock.atomicLoad)
+ {
+ assert(MonoTime.currTime < failIfThisTimeisReached, "timed out");
+ Thread.yield;
+ }
+ otherThread.join;
+ }
+ threadTriedOnceToGetLock.atomicStore(false); // Reset.
+ threadFinallyGotLock.atomicStore(false); // Reset.
+ // Test ReadWriteMutex.Writer.tryLock(Duration).
+ {
+ static void testWriterTryLock()
+ {
+ assert(!rwmutex.writer.tryLock(Duration.min));
+ threadTriedOnceToGetLock.atomicStore(true);
+ assert(rwmutex.writer.tryLock(Duration.max));
+ threadFinallyGotLock.atomicStore(true);
+ rwmutex.writer.unlock;
+ }
+ assert(rwmutex.reader.tryLock(Duration.zero), "should have been able to obtain lock without blocking");
+ auto otherThread = new Thread(&testWriterTryLock).start;
+ const failIfThisTimeisReached = MonoTime.currTime + maxTimeAllowedForTest;
+ Thread.yield;
+ // We started otherThread with the reader lock held so otherThread's
+ // first rwlock.writer.tryLock with timeout Duration.min should fail.
+ while (!threadTriedOnceToGetLock.atomicLoad)
+ {
+ assert(MonoTime.currTime < failIfThisTimeisReached, "timed out");
+ Thread.yield;
+ }
+ rwmutex.reader.unlock;
+ // Soon after we release the reader lock otherThread's second
+ // rwlock.writer.tryLock with timeout Duration.max should succeed.
+ while (!threadFinallyGotLock.atomicLoad)
+ {
+ assert(MonoTime.currTime < failIfThisTimeisReached, "timed out");
+ Thread.yield;
+ }
+ otherThread.join;
+ }
+}
diff --git a/libphobos/libdruntime/core/sync/semaphore.d b/libphobos/libdruntime/core/sync/semaphore.d
index 56ac7dc3663..cf2bddbf106 100644
--- a/libphobos/libdruntime/core/sync/semaphore.d
+++ b/libphobos/libdruntime/core/sync/semaphore.d
@@ -29,25 +29,25 @@ else version (WatchOS)
version (Windows)
{
- private import core.sys.windows.basetsd /+: HANDLE+/;
- private import core.sys.windows.winbase /+: CloseHandle, CreateSemaphoreA, INFINITE,
+ import core.sys.windows.basetsd /+: HANDLE+/;
+ import core.sys.windows.winbase /+: CloseHandle, CreateSemaphoreA, INFINITE,
ReleaseSemaphore, WAIT_OBJECT_0, WaitForSingleObject+/;
- private import core.sys.windows.windef /+: BOOL, DWORD+/;
- private import core.sys.windows.winerror /+: WAIT_TIMEOUT+/;
+ import core.sys.windows.windef /+: BOOL, DWORD+/;
+ import core.sys.windows.winerror /+: WAIT_TIMEOUT+/;
}
else version (Darwin)
{
- private import core.sync.config;
- private import core.stdc.errno;
- private import core.sys.posix.time;
- private import core.sys.darwin.mach.semaphore;
+ import core.sync.config;
+ import core.stdc.errno;
+ import core.sys.posix.time;
+ import core.sys.darwin.mach.semaphore;
}
else version (Posix)
{
- private import core.sync.config;
- private import core.stdc.errno;
- private import core.sys.posix.pthread;
- private import core.sys.posix.semaphore;
+ import core.sync.config;
+ import core.stdc.errno;
+ import core.sys.posix.pthread;
+ import core.sys.posix.semaphore;
}
else
{
@@ -197,7 +197,7 @@ class Semaphore
{
assert( !period.isNegative );
}
- body
+ do
{
version (Windows)
{
@@ -253,8 +253,11 @@ class Semaphore
}
else version (Posix)
{
+ import core.sys.posix.time : clock_gettime, CLOCK_REALTIME;
+
timespec t = void;
- mktspec( t, period );
+ clock_gettime( CLOCK_REALTIME, &t );
+ mvtspec( t, period );
while ( true )
{
@@ -359,8 +362,7 @@ protected:
// Unit Tests
////////////////////////////////////////////////////////////////////////////////
-
-version (unittest)
+unittest
{
import core.thread, core.atomic;
@@ -447,10 +449,6 @@ version (unittest)
assert(alertedOne && !alertedTwo);
}
-
- unittest
- {
- testWait();
- testWaitTimeout();
- }
+ testWait();
+ testWaitTimeout();
}
diff --git a/libphobos/libdruntime/core/sys/darwin/dlfcn.d b/libphobos/libdruntime/core/sys/darwin/dlfcn.d
index a38d9009e9d..406d588abf3 100644
--- a/libphobos/libdruntime/core/sys/darwin/dlfcn.d
+++ b/libphobos/libdruntime/core/sys/darwin/dlfcn.d
@@ -38,3 +38,8 @@ int dladdr(const scope void* addr, Dl_info* info);
enum RTLD_NOLOAD = 0x10;
enum RTLD_NODELETE = 0x80;
enum RTLD_FIRST = 0x100;
+
+enum RTLD_NEXT = cast(void*) -1;
+enum RTLD_DEFAULT = cast(void*) -2;
+enum RTLD_SELF = cast(void*) -3;
+enum RTLD_MAIN_ONLY = cast(void*) -5;
diff --git a/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf32.d b/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf32.d
index 2c35d0b59a3..035bba5eb30 100644
--- a/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf32.d
+++ b/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf32.d
@@ -112,7 +112,7 @@ extern (D) pure
{
auto ELF32_M_SYM(I)(I info) @safe { return info >> 8; }
auto ELF32_M_SIZE(I)(I info) { return cast(ubyte)info; }
- auto ELF32_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubye)size; }
+ auto ELF32_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubyte)size; }
}
struct Elf32_Cap
diff --git a/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf64.d b/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf64.d
index 94b7e42b5cd..f7d9247e0e3 100644
--- a/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf64.d
+++ b/libphobos/libdruntime/core/sys/dragonflybsd/sys/elf64.d
@@ -118,7 +118,7 @@ extern (D) pure
{
auto ELF64_M_SYM(I)(I info) @safe { return info >> 8; }
auto ELF64_M_SIZE(I)(I info) { return cast(ubyte)info; }
- auto ELF64_M_INFO(S, SZ)(S sym, SZ size) @safe { return (sym << 8) + cast(ubye)size; }
+ auto ELF64_M_INFO(S, SZ)(S sym, SZ size) @safe { return (sym << 8) + cast(ubyte)size; }
}
struct Elf64_Cap
diff --git a/libphobos/libdruntime/core/sys/freebsd/sys/elf32.d b/libphobos/libdruntime/core/sys/freebsd/sys/elf32.d
index 61455223b71..63cc4f9328a 100644
--- a/libphobos/libdruntime/core/sys/freebsd/sys/elf32.d
+++ b/libphobos/libdruntime/core/sys/freebsd/sys/elf32.d
@@ -112,7 +112,7 @@ extern (D)
{
auto ELF32_M_SYM(I)(I info) { return info >> 8; }
auto ELF32_M_SIZE(I)(I info) { return cast(ubyte)info; }
- auto ELF32_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubye)size; }
+ auto ELF32_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubyte)size; }
}
struct Elf32_Cap
diff --git a/libphobos/libdruntime/core/sys/freebsd/sys/elf64.d b/libphobos/libdruntime/core/sys/freebsd/sys/elf64.d
index f208b017758..8c63e04973d 100644
--- a/libphobos/libdruntime/core/sys/freebsd/sys/elf64.d
+++ b/libphobos/libdruntime/core/sys/freebsd/sys/elf64.d
@@ -127,7 +127,7 @@ extern (D)
{
auto ELF64_M_SYM(I)(I info) { return info >> 8; }
auto ELF64_M_SIZE(I)(I info) { return cast(ubyte)info; }
- auto ELF64_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubye)size; }
+ auto ELF64_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubyte)size; }
}
struct Elf64_Cap
diff --git a/libphobos/libdruntime/core/sys/linux/fs.d b/libphobos/libdruntime/core/sys/linux/fs.d
new file mode 100644
index 00000000000..5faa7564d1b
--- /dev/null
+++ b/libphobos/libdruntime/core/sys/linux/fs.d
@@ -0,0 +1,265 @@
+/**
+ * D header file for the linux/fs.h interface.
+ *
+ * This file has definitions for some important file table structures
+ * and constants and structures used by various generic file system
+ * ioctl's.
+ *
+ * Copyright: The D Language Foundation 2021.
+ * License : $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Authors : Luís Ferreira
+ */
+module core.sys.linux.fs;
+
+version (linux):
+
+public import core.sys.posix.sys.ioctl;
+
+import core.stdc.config : c_ulong, c_long;
+
+extern (C):
+@system:
+@nogc:
+nothrow:
+
+enum INR_OPEN_CUR = 1024; /// Initial setting for nfile rlimits
+enum INR_OPEN_MAX = 4096; /// Hard limit for nfile rlimits
+
+enum BLOCK_SIZE_BITS = 10; ///
+enum BLOCK_SIZE = 1 << BLOCK_SIZE_BITS; ///
+
+enum
+{
+ SEEK_SET = 0, /// seek relative to beginning of file
+ SEEK_CUR = 1, /// seek relative to current file position
+ SEEK_END = 2, /// seek relative to end of file
+ SEEK_DATA = 3, /// seek to the next data
+ SEEK_HOLE = 4, /// seek to the next hole
+ SEEK_MAX = SEEK_HOLE, ///
+}
+
+enum
+{
+ RENAME_NOREPLACE = 1 << 0, /// Don't overwrite target
+ RENAME_EXCHANGE = 1 << 1, /// Exchange source and dest
+ RENAME_WHITEOUT = 1 << 2, /// Whiteout source
+}
+
+struct file_clone_range
+{
+ long src_fd;
+ ulong src_offset;
+ ulong src_length;
+ ulong dest_offset;
+}
+
+struct fstrim_range
+{
+ ulong start;
+ ulong len;
+ ulong minlen;
+}
+
+/**
+ * extent-same (dedupe) ioctls; these MUST match the btrfs ioctl definitions
+ */
+enum
+{
+ FILE_DEDUPE_RANGE_SAME = 0,
+ FILE_DEDUPE_RANGE_DIFFERS = 1,
+}
+
+/**
+ * from struct btrfs_ioctl_file_extent_same_info
+ */
+struct file_dedupe_range_info
+{
+ long dest_fd; /// in - destination file
+ ulong dest_offset; /// in - start of extent in destination
+ ulong bytes_deduped; /// out - total # of bytes we were able to dedupe from this file.
+ /** status of this dedupe operation:
+ * < 0 for error
+ * == FILE_DEDUPE_RANGE_SAME if dedupe succeeds
+ * == FILE_DEDUPE_RANGE_DIFFERS if data differs
+ */
+ int status;
+ uint reserved; /// must be zero
+}
+
+/**
+ * from struct btrfs_ioctl_file_extent_same_args
+ */
+struct file_dedupe_range
+{
+ ulong src_offset; /// in - start of extent in source
+ ulong src_length; /// in - length of extent
+ ushort dest_count; /// in - total elements in info array
+ ushort reserved1; /// must be zero
+ uint reserved2; /// must be zero
+ file_dedupe_range_info[0] info;
+}
+
+/**
+ * And dynamically-tunable limits and defaults:
+ */
+struct files_stat_struct
+{
+ c_ulong nr_files; /// read only
+ c_ulong nr_free_files; /// read only
+ c_ulong max_files; /// tunable
+}
+
+struct inodes_stat_t
+{
+ c_long nr_inodes;
+ c_long nr_unused;
+ c_long[5] dummy; /// padding for sysctl ABI compatibility
+}
+
+enum NR_FILE = 8192;
+
+/**
+ * Structure for FS_IOC_FSGETXATTR[A] and FS_IOC_FSSETXATTR.
+ */
+struct fsxattr
+{
+ uint fsx_xflags;
+ uint fsx_extsize;
+ uint fsx_nextents;
+ uint fsx_projid; /// project identifier
+ uint fsx_cowextsize; /// CoW extsize
+ ubyte[8] fsx_pad;
+}
+
+/*
+ * Flags for the fsx_xflags field
+ */
+enum {
+ S_XFLAG_REALTIME = 0x00000001, /// data in realtime volume
+ S_XFLAG_PREALLOC = 0x00000002, /// preallocated file extents
+ S_XFLAG_IMMUTABLE = 0x00000008, /// file cannot be modified
+ S_XFLAG_APPEND = 0x00000010, /// all writes append
+ S_XFLAG_SYNC = 0x00000020, /// all writes synchronous
+ S_XFLAG_NOATIME = 0x00000040, /// do not update access time
+ S_XFLAG_NODUMP = 0x00000080, /// do not include in backups
+ S_XFLAG_RTINHERIT = 0x00000100, /// create with rt bit set
+ S_XFLAG_PROJINHERIT = 0x00000200, /// create with parents projid
+ S_XFLAG_NOSYMLINKS = 0x00000400, /// disallow symlink creation
+ S_XFLAG_EXTSIZE = 0x00000800, /// extent size allocator hint
+ S_XFLAG_EXTSZINHERIT = 0x00001000, /// inherit inode extent size
+ S_XFLAG_NODEFRAG = 0x00002000, /// do not defragment
+ S_XFLAG_FILESTREAM = 0x00004000, /// use filestream allocator
+ S_XFLAG_DAX = 0x00008000, /// use DAX for IO
+ S_XFLAG_COWEXTSIZE = 0x00010000, /// CoW extent size allocator hint
+ S_XFLAG_HASATTR = 0x80000000, /// no DIFLAG for this
+}
+
+enum BLKROSET = _IO(0x12, 93); /// set device read-only
+enum BLKROGET = _IO(0x12, 94); /// get read-only status
+enum BLKRRPART = _IO(0x12, 95); /// re-read partition table
+enum BLKGETSIZE = _IO(0x12, 96); /// return device size
+enum BLKFLSBUF = _IO(0x12, 97); /// flush buffer cache
+enum BLKRASET = _IO(0x12, 98); /// set read ahead for block device
+enum BLKRAGET = _IO(0x12, 99); /// get current read ahead setting
+enum BLKFRASET = _IO(0x12, 100); /// set filesystem
+enum BLKFRAGET = _IO(0x12, 101); /// get filesystem
+enum BLKSECTSET = _IO(0x12, 102); /// set max sectors per request
+enum BLKSECTGET = _IO(0x12, 103); /// get max sectors per request
+enum BLKSSZGET = _IO(0x12, 104); /// get block device sector size
+
+
+enum BLKBSZGET = _IOR!size_t(0x12, 112);
+enum BLKBSZSET = _IOW!size_t(0x12, 113);
+enum BLKGETSIZE64 = _IOR!size_t(0x12, 114);
+enum BLKTRACESTART = _IO(0x12, 116);
+enum BLKTRACESTOP = _IO(0x12, 117);
+enum BLKTRACETEARDOWN = _IO(0x12, 118);
+enum BLKDISCARD = _IO(0x12, 119);
+enum BLKIOMIN = _IO(0x12, 120);
+enum BLKIOOPT = _IO(0x12, 121);
+enum BLKALIGNOFF = _IO(0x12, 122);
+enum BLKPBSZGET = _IO(0x12, 123);
+enum BLKDISCARDZEROES = _IO(0x12, 124);
+enum BLKSECDISCARD = _IO(0x12, 125);
+enum BLKROTATIONAL = _IO(0x12, 126);
+enum BLKZEROOUT = _IO(0x12, 127);
+
+enum BMAP_IOCTL = 1; /// obsolete - kept for compatibility
+enum FIBMAP = _IO(0x00, 1); /// bmap access
+enum FIGETBSZ = _IO(0x00, 2); /// get the block size used for bmap
+
+enum FSLABEL_MAX = 256; /// Max chars for the interface; each fs may differ
+
+/**
+ * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
+ *
+ * Note: for historical reasons, these flags were originally used and
+ * defined for use by ext2/ext3, and then other file systems started
+ * using these flags so they wouldn't need to write their own version
+ * of chattr/lsattr (which was shipped as part of e2fsprogs). You
+ * should think twice before trying to use these flags in new
+ * contexts, or trying to assign these flags, since they are used both
+ * as the UAPI and the on-disk encoding for ext2/3/4. Also, we are
+ * almost out of 32-bit flags. :-)
+ *
+ * We have recently hoisted FS_IOC_FSGETXATTR / FS_IOC_FSSETXATTR from
+ * XFS to the generic FS level interface. This uses a structure that
+ * has padding and hence has more room to grow, so it may be more
+ * appropriate for many new use cases.
+ */
+enum {
+ FS_SECRM_FL = 0x00000001, /// Secure deletion
+ FS_UNRM_FL = 0x00000002, /// Undelete
+ FS_COMPR_FL = 0x00000004, /// Compress file
+ FS_SYNC_FL = 0x00000008, /// Synchronous updates
+ FS_IMMUTABLE_FL = 0x00000010, /// Immutable file
+ FS_APPEND_FL = 0x00000020, /// writes to file may only append
+ FS_NODUMP_FL = 0x00000040, /// do not dump file
+ FS_NOATIME_FL = 0x00000080, /// do not update atime
+ FS_DIRTY_FL = 0x00000100, /// Reserved for compression usage
+ FS_COMPRBLK_FL = 0x00000200, /// One or more compressed clusters
+ FS_NOCOMP_FL = 0x00000400, /// Don't compress
+ FS_ENCRYPT_FL = 0x00000800, /// Encrypted file
+ FS_BTREE_FL = 0x00001000, /// btree format dir
+ FS_INDEX_FL = 0x00001000, /// hash-indexed directory
+ FS_IMAGIC_FL = 0x00002000, /// AFS directory
+ FS_JOURNAL_DATA_FL = 0x00004000, /// Reserved for ext3
+ FS_NOTAIL_FL = 0x00008000, /// file tail should not be merged
+ FS_DIRSYNC_FL = 0x00010000, /// dirsync behaviour (directories only)
+ FS_TOPDIR_FL = 0x00020000, /// Top of directory hierarchie
+ FS_HUGE_FILE_FL = 0x00040000, /// Reserved for ext4
+ FS_EXTENT_FL = 0x00080000, /// Extents
+ FS_VERITY_FL = 0x00100000, /// Verity protected inode
+ FS_EA_INODE_FL = 0x00200000, /// Inode used for large EA
+ FS_EOFBLOCKS_FL = 0x00400000, /// Reserved for ext4
+ FS_NOCOW_FL = 0x00800000, /// Do not cow file
+ FS_DAX_FL = 0x02000000, /// Inode is DAX
+ FS_INLINE_DATA_FL = 0x10000000, /// Reserved for ext4
+ FS_PROJINHERIT_FL = 0x20000000, /// Create with parents projid
+ FS_CASEFOLD_FL = 0x40000000, /// Folder is case insensitive
+ FS_RESERVED_FL = 0x80000000, /// reserved for ext2 lib
+}
+
+enum FS_FL_USER_VISIBLE = 0x0003DFFF; /// User visible flags
+enum FS_FL_USER_MODIFIABLE = 0x000380FF; /// User modifiable flags
+
+enum SYNC_FILE_RANGE_WAIT_BEFORE = 1;
+enum SYNC_FILE_RANGE_WRITE = 2;
+enum SYNC_FILE_RANGE_WAIT_AFTER = 4;
+enum SYNC_FILE_RANGE_WRITE_AND_WAIT = SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WAIT_AFTER;
+
+alias __kernel_rwf_t = int;
+
+/**
+ * Flags for preadv2/pwritev2:
+ */
+enum : __kernel_rwf_t {
+ RWF_HIPRI = 0x00000001, /// high priority request, poll if possible
+ RWF_DSYNC = 0x00000002, /// per-IO O_DSYNC
+ RWF_SYNC = 0x00000004, /// per-IO O_SYNC
+ RWF_NOWAIT = 0x00000008, /// per-IO, return -EAGAIN if operation would block
+ RWF_APPEND = 0x00000010, /// per-IO O_APPEND
+}
+
+/// mask of flags supported by the kernel
+enum RWF_SUPPORTED = RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT | RWF_APPEND;
diff --git a/libphobos/libdruntime/core/sys/linux/io_uring.d b/libphobos/libdruntime/core/sys/linux/io_uring.d
new file mode 100644
index 00000000000..5e1a20c24b3
--- /dev/null
+++ b/libphobos/libdruntime/core/sys/linux/io_uring.d
@@ -0,0 +1,414 @@
+/**
+ * D header file for the io_uring interface.
+ * Available since Linux 5.1
+ *
+ * Copyright: Copyright Jens Axboe 2019,
+ * Copyright Christoph Hellwig 2019.
+ * License : $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Authors : Luís Ferreira
+ */
+module core.sys.linux.io_uring;
+
+version (linux):
+
+import core.sys.linux.fs : __kernel_rwf_t;
+
+extern (C):
+@system:
+@nogc:
+nothrow:
+@system:
+
+/**
+ * IO submission data structure (Submission Queue Entry)
+ */
+struct io_uring_sqe
+{
+ /// type of operation for this sqe
+ ubyte opcode;
+ /// IOSQE_* flags
+ ubyte flags;
+ /// ioprio for the request
+ ushort ioprio;
+ /// file descriptor to do IO on
+ int fd;
+ union
+ {
+ /// offset into file
+ ulong off;
+ ulong addr2;
+ }
+
+ union
+ {
+ /// pointer to buffer or iovecs
+ ulong addr;
+ ulong splice_off_in;
+ }
+
+ /// buffer size or number of iovecs
+ uint len;
+ union
+ {
+ __kernel_rwf_t rw_flags;
+ uint fsync_flags;
+
+ /// compatibility
+ ushort poll_events;
+ /// word-reversed for BE
+ uint poll32_events;
+
+ uint sync_range_flags;
+ uint msg_flags;
+ uint timeout_flags;
+ uint accept_flags;
+ uint cancel_flags;
+ uint open_flags;
+ uint statx_flags;
+ uint fadvise_advice;
+ uint splice_flags;
+ uint rename_flags;
+ uint unlink_flags;
+ }
+
+ /// data to be passed back at completion time
+ ulong user_data;
+ union
+ {
+ struct
+ {
+ /**
+ * pack this to avoid bogus arm OABI complaints
+ */
+ union
+ {
+ align (1):
+
+ /// index into fixed buffers, if used
+ ushort buf_index;
+ /// for grouped buffer selection
+ ushort buf_group;
+ }
+
+ /// personality to use, if used
+ ushort personality;
+ int splice_fd_in;
+ }
+
+ ulong[3] __pad2;
+ }
+}
+
+enum
+{
+ IOSQE_FIXED_FILE_BIT = 0,
+ IOSQE_IO_DRAIN_BIT = 1,
+ IOSQE_IO_LINK_BIT = 2,
+ IOSQE_IO_HARDLINK_BIT = 3,
+ IOSQE_ASYNC_BIT = 4,
+ IOSQE_BUFFER_SELECT_BIT = 5
+}
+
+enum
+{
+ /// use fixed fileset
+ IOSQE_FIXED_FILE = 1U << IOSQE_FIXED_FILE_BIT,
+ /// issue after inflight IO
+ IOSQE_IO_DRAIN = 1U << IOSQE_IO_DRAIN_BIT,
+ /// links next sqe
+ IOSQE_IO_LINK = 1U << IOSQE_IO_LINK_BIT,
+ /// like LINK, but stronger
+ IOSQE_IO_HARDLINK = 1U << IOSQE_IO_HARDLINK_BIT,
+ /// always go async
+ IOSQE_ASYNC = 1U << IOSQE_ASYNC_BIT,
+ /// select buffer from sqe.buf_group
+ IOSQE_BUFFER_SELECT = 1U << IOSQE_BUFFER_SELECT_BIT,
+}
+
+/**
+ * io_uring_setup() flags
+ */
+enum
+{
+ /// io_context is polled
+ IORING_SETUP_IOPOLL = 1U << 0,
+ /// SQ poll thread
+ IORING_SETUP_SQPOLL = 1U << 1,
+ /// sq_thread_cpu is valid
+ IORING_SETUP_SQ_AFF = 1U << 2,
+ /// app defines CQ size
+ IORING_SETUP_CQSIZE = 1U << 3,
+ /// clamp SQ/CQ ring sizes
+ IORING_SETUP_CLAMP = 1U << 4,
+ /// attach to existing wq
+ IORING_SETUP_ATTACH_WQ = 1U << 5,
+ /// start with ring disabled
+ IORING_SETUP_R_DISABLED = 1U << 6,
+}
+
+enum
+{
+ IORING_OP_NOP = 0,
+ IORING_OP_READV = 1,
+ IORING_OP_WRITEV = 2,
+ IORING_OP_FSYNC = 3,
+ IORING_OP_READ_FIXED = 4,
+ IORING_OP_WRITE_FIXED = 5,
+ IORING_OP_POLL_ADD = 6,
+ IORING_OP_POLL_REMOVE = 7,
+ IORING_OP_SYNC_FILE_RANGE = 8,
+ IORING_OP_SENDMSG = 9,
+ IORING_OP_RECVMSG = 10,
+ IORING_OP_TIMEOUT = 11,
+ IORING_OP_TIMEOUT_REMOVE = 12,
+ IORING_OP_ACCEPT = 13,
+ IORING_OP_ASYNC_CANCEL = 14,
+ IORING_OP_LINK_TIMEOUT = 15,
+ IORING_OP_CONNECT = 16,
+ IORING_OP_FALLOCATE = 17,
+ IORING_OP_OPENAT = 18,
+ IORING_OP_CLOSE = 19,
+ IORING_OP_FILES_UPDATE = 20,
+ IORING_OP_STATX = 21,
+ IORING_OP_READ = 22,
+ IORING_OP_WRITE = 23,
+ IORING_OP_FADVISE = 24,
+ IORING_OP_MADVISE = 25,
+ IORING_OP_SEND = 26,
+ IORING_OP_RECV = 27,
+ IORING_OP_OPENAT2 = 28,
+ IORING_OP_EPOLL_CTL = 29,
+ IORING_OP_SPLICE = 30,
+ IORING_OP_PROVIDE_BUFFERS = 31,
+ IORING_OP_REMOVE_BUFFERS = 32,
+ IORING_OP_TEE = 33,
+ IORING_OP_SHUTDOWN = 34,
+ IORING_OP_RENAMEAT = 35,
+ IORING_OP_UNLINKAT = 36,
+
+ IORING_OP_LAST = 37
+}
+
+enum
+{
+ IORING_FSYNC_DATASYNC = 1U << 0,
+}
+
+enum
+{
+ IORING_TIMEOUT_ABS = 1U << 0,
+ IORING_TIMEOUT_UPDATE = 1U << 1,
+}
+
+enum SPLICE_F_FD_IN_FIXED = 1U << 31;
+
+/**
+ * IO completion data structure (Completion Queue Entry)
+ */
+struct io_uring_cqe
+{
+ /// submission passed back
+ ulong user_data;
+ /// result code for this event
+ int res;
+
+ uint flags;
+}
+
+/**
+ * If set, the upper 16 bits are the buffer ID
+ */
+enum IORING_CQE_F_BUFFER = 1U << 0;
+
+enum
+{
+ IORING_CQE_BUFFER_SHIFT = 16,
+}
+
+/**
+ * Magic offsets for the application to mmap the data it needs
+ */
+enum
+{
+ IORING_OFF_SQ_RING = 0UL,
+ IORING_OFF_CQ_RING = 0x8000000UL,
+ IORING_OFF_SQES = 0x10000000UL,
+}
+
+/**
+ * Filled with the offset for mmap(2)
+ */
+struct io_sqring_offsets
+{
+ uint head;
+ uint tail;
+ uint ring_mask;
+ uint ring_entries;
+ uint flags;
+ uint dropped;
+ uint array;
+ uint resv1;
+ ulong resv2;
+}
+
+enum
+{
+ /// needs io_uring_enter wakeup
+ IORING_SQ_NEED_WAKEUP = 1U << 0,
+ /// CQ ring is overflown
+ IORING_SQ_CQ_OVERFLOW = 1U << 1,
+}
+
+struct io_cqring_offsets
+{
+ uint head;
+ uint tail;
+ uint ring_mask;
+ uint ring_entries;
+ uint overflow;
+ uint cqes;
+ uint flags;
+ uint resv1;
+ ulong resv2;
+}
+
+enum
+{
+ /// disable eventfd notifications
+ IORING_CQ_EVENTFD_DISABLED = 1U << 0,
+}
+
+/**
+ * io_uring_enter(2) flags
+ */
+enum
+{
+ IORING_ENTER_GETEVENTS = 1U << 0,
+ IORING_ENTER_SQ_WAKEUP = 1U << 1,
+ IORING_ENTER_SQ_WAIT = 1U << 2,
+ IORING_ENTER_EXT_ARG = 1U << 3,
+}
+
+/**
+ * Passed in for io_uring_setup(2)
+ */
+struct io_uring_params
+{
+ uint sq_entries;
+ uint cq_entries;
+ uint flags;
+ uint sq_thread_cpu;
+ uint sq_thread_idle;
+ uint features;
+ uint wq_fd;
+ uint[3] resv;
+ io_sqring_offsets sq_off;
+ io_cqring_offsets cq_off;
+}
+
+enum
+{
+ IORING_FEAT_SINGLE_MMAP = 1U << 0,
+ IORING_FEAT_NODROP = 1U << 1,
+ IORING_FEAT_SUBMIT_STABLE = 1U << 2,
+ IORING_FEAT_RW_CUR_POS = 1U << 3,
+ IORING_FEAT_CUR_PERSONALITY = 1U << 4,
+ IORING_FEAT_FAST_POLL = 1U << 5,
+ IORING_FEAT_POLL_32BITS = 1U << 6,
+ IORING_FEAT_SQPOLL_NONFIXED = 1U << 7,
+ IORING_FEAT_EXT_ARG = 1U << 8,
+}
+
+/**
+ * io_uring_register(2) opcodes and arguments
+ */
+enum
+{
+ IORING_REGISTER_BUFFERS = 0,
+ IORING_UNREGISTER_BUFFERS = 1,
+ IORING_REGISTER_FILES = 2,
+ IORING_UNREGISTER_FILES = 3,
+ IORING_REGISTER_EVENTFD = 4,
+ IORING_UNREGISTER_EVENTFD = 5,
+ IORING_REGISTER_FILES_UPDATE = 6,
+ IORING_REGISTER_EVENTFD_ASYNC = 7,
+ IORING_REGISTER_PROBE = 8,
+ IORING_REGISTER_PERSONALITY = 9,
+ IORING_UNREGISTER_PERSONALITY = 10,
+ IORING_REGISTER_RESTRICTIONS = 11,
+ IORING_REGISTER_ENABLE_RINGS = 12,
+
+ IORING_REGISTER_LAST = 13
+}
+
+struct io_uring_files_update
+{
+ uint offset;
+ uint resv;
+ ulong fds;
+}
+
+enum IO_URING_OP_SUPPORTED = 1U << 0;
+
+struct io_uring_probe_op
+{
+ ubyte op;
+ ubyte resv;
+
+ /// IO_URING_OP_* flags
+ ushort flags;
+ uint resv2;
+}
+
+struct io_uring_probe
+{
+ /// last opcode supported
+ ubyte last_op;
+
+ /// length of ops[] array below
+ ubyte ops_len;
+
+ ushort resv;
+ uint[3] resv2;
+ io_uring_probe_op[0] ops;
+}
+
+struct io_uring_restriction
+{
+ ushort opcode;
+
+ union
+ {
+ ubyte register_op;
+ ubyte sqe_op;
+ ubyte sqe_flags;
+ }
+
+ ubyte resv;
+ uint[3] resv2;
+}
+
+enum
+{
+ /// Allow an io_uring_register(2) opcode
+ IORING_RESTRICTION_REGISTER_OP = 0,
+
+ /// Allow an sqe opcode
+ IORING_RESTRICTION_SQE_OP = 1,
+
+ /// Allow sqe flags
+ IORING_RESTRICTION_SQE_FLAGS_ALLOWED = 2,
+
+ /// Require sqe flags (these flags must be set on each submission)
+ IORING_RESTRICTION_SQE_FLAGS_REQUIRED = 3,
+
+ IORING_RESTRICTION_LAST = 4
+}
+
+struct io_uring_getevents_arg
+{
+ ulong sigmask;
+ uint sigmask_sz;
+ uint pad;
+ ulong ts;
+}
diff --git a/libphobos/libdruntime/core/sys/linux/perf_event.d b/libphobos/libdruntime/core/sys/linux/perf_event.d
new file mode 100644
index 00000000000..805b47e6e33
--- /dev/null
+++ b/libphobos/libdruntime/core/sys/linux/perf_event.d
@@ -0,0 +1,2515 @@
+/**
+ * D header file for perf_event_open system call.
+ *
+ * Converted from linux userspace header, comments included.
+ *
+ * Authors: Max Haughton
+ */
+module core.sys.linux.perf_event;
+version (linux) : extern (C):
+@nogc:
+nothrow:
+@system:
+
+import core.sys.posix.sys.ioctl;
+import core.sys.posix.unistd;
+
+version (HPPA) version = HPPA_Any;
+version (HPPA64) version = HPPA_Any;
+version (PPC) version = PPC_Any;
+version (PPC64) version = PPC_Any;
+version (RISCV32) version = RISCV_Any;
+version (RISCV64) version = RISCV_Any;
+version (S390) version = IBMZ_Any;
+version (SPARC) version = SPARC_Any;
+version (SPARC64) version = SPARC_Any;
+version (SystemZ) version = IBMZ_Any;
+
+version (X86_64)
+{
+ version (D_X32)
+ enum __NR_perf_event_open = 0x40000000 + 298;
+ else
+ enum __NR_perf_event_open = 298;
+}
+else version (X86)
+{
+ enum __NR_perf_event_open = 336;
+}
+else version (ARM)
+{
+ enum __NR_perf_event_open = 364;
+}
+else version (AArch64)
+{
+ enum __NR_perf_event_open = 241;
+}
+else version (HPPA_Any)
+{
+ enum __NR_perf_event_open = 318;
+}
+else version (IBMZ_Any)
+{
+ enum __NR_perf_event_open = 331;
+}
+else version (MIPS32)
+{
+ enum __NR_perf_event_open = 4333;
+}
+else version (MIPS64)
+{
+ version (MIPS_N32)
+ enum __NR_perf_event_open = 6296;
+ else version (MIPS_N64)
+ enum __NR_perf_event_open = 5292;
+ else
+ static assert(0, "Architecture not supported");
+}
+else version (PPC_Any)
+{
+ enum __NR_perf_event_open = 319;
+}
+else version (RISCV_Any)
+{
+ enum __NR_perf_event_open = 241;
+}
+else version (SPARC_Any)
+{
+ enum __NR_perf_event_open = 327;
+}
+else
+{
+ static assert(0, "Architecture not supported");
+}
+extern (C) extern long syscall(long __sysno, ...);
+static long perf_event_open(perf_event_attr* hw_event, pid_t pid, int cpu, int group_fd, ulong flags)
+{
+ return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
+}
+/*
+ * User-space ABI bits:
+ */
+
+/**
+ * attr.type
+ */
+enum perf_type_id
+{
+ PERF_TYPE_HARDWARE = 0,
+ PERF_TYPE_SOFTWARE = 1,
+ PERF_TYPE_TRACEPOINT = 2,
+ PERF_TYPE_HW_CACHE = 3,
+ PERF_TYPE_RAW = 4,
+ PERF_TYPE_BREAKPOINT = 5,
+
+ PERF_TYPE_MAX = 6 /* non-ABI */
+}
+/**
+ * Generalized performance event event_id types, used by the
+ * attr.event_id parameter of the sys_perf_event_open()
+ * syscall:
+ */
+enum perf_hw_id
+{
+ ///
+ PERF_COUNT_HW_CPU_CYCLES = 0,
+ ///
+ PERF_COUNT_HW_INSTRUCTIONS = 1,
+ ///
+ PERF_COUNT_HW_CACHE_REFERENCES = 2,
+ ///
+ PERF_COUNT_HW_CACHE_MISSES = 3,
+ ///
+ PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
+ ///
+ PERF_COUNT_HW_BRANCH_MISSES = 5,
+ ///
+ PERF_COUNT_HW_BUS_CYCLES = 6,
+ ///
+ PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7,
+ ///
+ PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8,
+ ///
+ PERF_COUNT_HW_REF_CPU_CYCLES = 9,
+ ///
+ PERF_COUNT_HW_MAX = 10 /* non-ABI */
+}
+
+/**
+ * Generalized hardware cache events:
+ *
+ * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x
+ * { read, write, prefetch } x
+ * { accesses, misses }
+ */
+enum perf_hw_cache_id
+{
+ ///
+ PERF_COUNT_HW_CACHE_L1D = 0,
+ ///
+ PERF_COUNT_HW_CACHE_L1I = 1,
+ ///
+ PERF_COUNT_HW_CACHE_LL = 2,
+ ///
+ PERF_COUNT_HW_CACHE_DTLB = 3,
+ ///
+ PERF_COUNT_HW_CACHE_ITLB = 4,
+ ///
+ PERF_COUNT_HW_CACHE_BPU = 5,
+ ///
+ PERF_COUNT_HW_CACHE_NODE = 6,
+ ///
+ PERF_COUNT_HW_CACHE_MAX = 7 /* non-ABI */
+}
+///
+enum perf_hw_cache_op_id
+{
+ ///
+ PERF_COUNT_HW_CACHE_OP_READ = 0,
+ ///
+ PERF_COUNT_HW_CACHE_OP_WRITE = 1,
+ ///
+ PERF_COUNT_HW_CACHE_OP_PREFETCH = 2,
+ ///
+ PERF_COUNT_HW_CACHE_OP_MAX = 3 /* non-ABI */
+}
+///
+enum perf_hw_cache_op_result_id
+{
+ ///
+ PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0,
+ ///
+ PERF_COUNT_HW_CACHE_RESULT_MISS = 1,
+ ///
+ PERF_COUNT_HW_CACHE_RESULT_MAX = 2 /* non-ABI */
+}
+
+/**
+ * Special "software" events provided by the kernel, even if the hardware
+ * does not support performance events. These events measure various
+ * physical and sw events of the kernel (and allow the profiling of them as
+ * well):
+ */
+enum perf_sw_ids
+{
+ ///
+ PERF_COUNT_SW_CPU_CLOCK = 0,
+ ///
+ PERF_COUNT_SW_TASK_CLOCK = 1,
+ ///
+ PERF_COUNT_SW_PAGE_FAULTS = 2,
+ ///
+ PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
+ ///
+ PERF_COUNT_SW_CPU_MIGRATIONS = 4,
+ ///
+ PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
+ ///
+ PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
+ ///
+ PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
+ ///
+ PERF_COUNT_SW_EMULATION_FAULTS = 8,
+ ///
+ PERF_COUNT_SW_DUMMY = 9,
+ ///
+ PERF_COUNT_SW_BPF_OUTPUT = 10,
+ ///
+ PERF_COUNT_SW_MAX = 11 /* non-ABI */
+}
+
+/**
+ * Bits that can be set in attr.sample_type to request information
+ * in the overflow packets.
+ */
+enum perf_event_sample_format
+{
+ ///
+ PERF_SAMPLE_IP = 1U << 0,
+ ///
+ PERF_SAMPLE_TID = 1U << 1,
+ ///
+ PERF_SAMPLE_TIME = 1U << 2,
+ ///
+ PERF_SAMPLE_ADDR = 1U << 3,
+ ///
+ PERF_SAMPLE_READ = 1U << 4,
+ ///
+ PERF_SAMPLE_CALLCHAIN = 1U << 5,
+ ///
+ PERF_SAMPLE_ID = 1U << 6,
+ ///
+ PERF_SAMPLE_CPU = 1U << 7,
+ ///
+ PERF_SAMPLE_PERIOD = 1U << 8,
+ ///
+ PERF_SAMPLE_STREAM_ID = 1U << 9,
+ ///
+ PERF_SAMPLE_RAW = 1U << 10,
+ ///
+ PERF_SAMPLE_BRANCH_STACK = 1U << 11,
+ ///
+ PERF_SAMPLE_REGS_USER = 1U << 12,
+ ///
+ PERF_SAMPLE_STACK_USER = 1U << 13,
+ ///
+ PERF_SAMPLE_WEIGHT = 1U << 14,
+ ///
+ PERF_SAMPLE_DATA_SRC = 1U << 15,
+ ///
+ PERF_SAMPLE_IDENTIFIER = 1U << 16,
+ ///
+ PERF_SAMPLE_TRANSACTION = 1U << 17,
+ ///
+ PERF_SAMPLE_REGS_INTR = 1U << 18,
+ ///
+ PERF_SAMPLE_PHYS_ADDR = 1U << 19,
+ ///
+ PERF_SAMPLE_MAX = 1U << 20 /* non-ABI */
+}
+
+/**
+ * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set
+ *
+ * If the user does not pass priv level information via branch_sample_type,
+ * the kernel uses the event's priv level. Branch and event priv levels do
+ * not have to match. Branch priv level is checked for permissions.
+ *
+ * The branch types can be combined, however BRANCH_ANY covers all types
+ * of branches and therefore it supersedes all the other types.
+ */
+enum perf_branch_sample_type_shift
+{
+ PERF_SAMPLE_BRANCH_USER_SHIFT = 0, /** user branches */
+ PERF_SAMPLE_BRANCH_KERNEL_SHIFT = 1, /** kernel branches */
+ PERF_SAMPLE_BRANCH_HV_SHIFT = 2, /** hypervisor branches */
+
+ PERF_SAMPLE_BRANCH_ANY_SHIFT = 3, /** any branch types */
+ PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT = 4, /** any call branch */
+ PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT = 5, /** any return branch */
+ PERF_SAMPLE_BRANCH_IND_CALL_SHIFT = 6, /** indirect calls */
+ PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT = 7, /** transaction aborts */
+ PERF_SAMPLE_BRANCH_IN_TX_SHIFT = 8, /** in transaction */
+ PERF_SAMPLE_BRANCH_NO_TX_SHIFT = 9, /** not in transaction */
+ PERF_SAMPLE_BRANCH_COND_SHIFT = 10, /** conditional branches */
+
+ PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT = 11, /** call/ret stack */
+ PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT = 12, /** indirect jumps */
+ PERF_SAMPLE_BRANCH_CALL_SHIFT = 13, /** direct call */
+
+ PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT = 14, /** no flags */
+ PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT = 15, /** no cycles */
+
+ PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT = 16, /** save branch type */
+
+ PERF_SAMPLE_BRANCH_MAX_SHIFT = 17 /** non-ABI */
+}
+///
+enum perf_branch_sample_type
+{
+ PERF_SAMPLE_BRANCH_USER = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_USER_SHIFT,
+ PERF_SAMPLE_BRANCH_KERNEL = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_KERNEL_SHIFT,
+ PERF_SAMPLE_BRANCH_HV = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_HV_SHIFT,
+ PERF_SAMPLE_BRANCH_ANY = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_ANY_SHIFT,
+ PERF_SAMPLE_BRANCH_ANY_CALL = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT,
+ PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT,
+ PERF_SAMPLE_BRANCH_IND_CALL = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_IND_CALL_SHIFT,
+ PERF_SAMPLE_BRANCH_ABORT_TX = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT,
+ PERF_SAMPLE_BRANCH_IN_TX = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_IN_TX_SHIFT,
+ PERF_SAMPLE_BRANCH_NO_TX = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_NO_TX_SHIFT,
+ PERF_SAMPLE_BRANCH_COND = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_COND_SHIFT,
+ PERF_SAMPLE_BRANCH_CALL_STACK = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT,
+ PERF_SAMPLE_BRANCH_IND_JUMP = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT,
+ PERF_SAMPLE_BRANCH_CALL = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_CALL_SHIFT,
+ PERF_SAMPLE_BRANCH_NO_FLAGS = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT,
+ PERF_SAMPLE_BRANCH_NO_CYCLES = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT,
+ PERF_SAMPLE_BRANCH_TYPE_SAVE = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT,
+ PERF_SAMPLE_BRANCH_MAX = 1U << perf_branch_sample_type_shift.PERF_SAMPLE_BRANCH_MAX_SHIFT
+}
+
+/**
+ * Common flow change classification
+ */
+enum
+{
+ PERF_BR_UNKNOWN = 0, /** unknown */
+ PERF_BR_COND = 1, /** conditional */
+ PERF_BR_UNCOND = 2, /** unconditional */
+ PERF_BR_IND = 3, /** indirect */
+ PERF_BR_CALL = 4, /** function call */
+ PERF_BR_IND_CALL = 5, /** indirect function call */
+ PERF_BR_RET = 6, /** function return */
+ PERF_BR_SYSCALL = 7, /** syscall */
+ PERF_BR_SYSRET = 8, /** syscall return */
+ PERF_BR_COND_CALL = 9, /** conditional function call */
+ PERF_BR_COND_RET = 10, /** conditional function return */
+ PERF_BR_MAX = 11
+}
+
+///
+enum PERF_SAMPLE_BRANCH_PLM_ALL = perf_branch_sample_type.PERF_SAMPLE_BRANCH_USER
+ | perf_branch_sample_type.PERF_SAMPLE_BRANCH_KERNEL
+ | perf_branch_sample_type.PERF_SAMPLE_BRANCH_HV;
+
+/**
+ * Values to determine ABI of the registers dump.
+ */
+enum perf_sample_regs_abi
+{
+ ///
+ PERF_SAMPLE_REGS_ABI_NONE = 0,
+ ///
+ PERF_SAMPLE_REGS_ABI_32 = 1,
+ ///
+ PERF_SAMPLE_REGS_ABI_64 = 2
+}
+
+/**
+ * Values for the memory transaction event qualifier, mostly for
+ * abort events. Multiple bits can be set.
+ */
+enum
+{
+ PERF_TXN_ELISION = 1 << 0, /** From elision */
+ PERF_TXN_TRANSACTION = 1 << 1, /** From transaction */
+ PERF_TXN_SYNC = 1 << 2, /** Instruction is related */
+ PERF_TXN_ASYNC = 1 << 3, /** Instruction not related */
+ PERF_TXN_RETRY = 1 << 4, /** Retry possible */
+ PERF_TXN_CONFLICT = 1 << 5, /** Conflict abort */
+ PERF_TXN_CAPACITY_WRITE = 1 << 6, /** Capacity write abort */
+ PERF_TXN_CAPACITY_READ = 1 << 7, /** Capacity read abort */
+
+ PERF_TXN_MAX = 1 << 8, /** non-ABI */
+
+ /** bits 32..63 are reserved for the abort code */
+
+ ///PERF_TXN_ABORT_MASK = 0xffffffff << 32,
+ PERF_TXN_ABORT_SHIFT = 32
+}
+
+/**
+ * The format of the data returned by read() on a perf event fd,
+ * as specified by attr.read_format:
+ * ---
+ * struct read_format {
+ * { u64 value;
+ * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
+ * { u64 id; } && PERF_FORMAT_ID
+ * } && !PERF_FORMAT_GROUP
+ *
+ * { u64 nr;
+ * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
+ * { u64 value;
+ * { u64 id; } && PERF_FORMAT_ID
+ * } cntr[nr];
+ * } && PERF_FORMAT_GROUP
+ * };
+ * ---
+ */
+enum perf_event_read_format
+{
+ ///
+ PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
+ ///
+ PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
+ ///
+ PERF_FORMAT_ID = 1U << 2,
+ ///
+ PERF_FORMAT_GROUP = 1U << 3,
+ PERF_FORMAT_MAX = 1U << 4 /** non-ABI */
+}
+
+enum PERF_ATTR_SIZE_VER0 = 64; /** sizeof first published struct */
+enum PERF_ATTR_SIZE_VER1 = 72; /** add: config2 */
+enum PERF_ATTR_SIZE_VER2 = 80; /** add: branch_sample_type */
+enum PERF_ATTR_SIZE_VER3 = 96; /** add: sample_regs_user */
+/* add: sample_stack_user */
+enum PERF_ATTR_SIZE_VER4 = 104; /** add: sample_regs_intr */
+enum PERF_ATTR_SIZE_VER5 = 112; /** add: aux_watermark */
+
+/**
+ * Hardware event_id to monitor via a performance monitoring event:
+ *
+ * @sample_max_stack: Max number of frame pointers in a callchain,
+ * should be < /proc/sys/kernel/perf_event_max_stack
+ */
+struct perf_event_attr
+{
+ /**
+ *Major type: hardware/software/tracepoint/etc.
+ */
+ uint type;
+
+ /**
+ * Size of the attr structure, for fwd/bwd compat.
+ */
+ uint size;
+
+ /**
+ * Type specific configuration information.
+ */
+ ulong config;
+ ///
+ union
+ {
+ ///
+ ulong sample_period;
+ ///
+ ulong sample_freq;
+ }
+ ///
+ ulong sample_type;
+ ///
+ ulong read_format;
+
+ // mixin(bitfields!(
+ // ulong, "disabled", 1,
+ // ulong, "inherit", 1,
+ // ulong, "pinned", 1,
+ // ulong, "exclusive", 1,
+ // ulong, "exclude_user", 1,
+ // ulong, "exclude_kernel", 1,
+ // ulong, "exclude_hv", 1,
+ // ulong, "exclude_idle", 1,
+ // ulong, "mmap", 1,
+ // ulong, "comm", 1,
+ // ulong, "freq", 1,
+ // ulong, "inherit_stat", 1,
+ // ulong, "enable_on_exec", 1,
+ // ulong, "task", 1,
+ // ulong, "watermark", 1,
+ // ulong, "precise_ip", 2,
+ // ulong, "mmap_data", 1,
+ // ulong, "sample_id_all", 1,
+ // ulong, "exclude_host", 1,
+ // ulong, "exclude_guest", 1,
+ // ulong, "exclude_callchain_kernel", 1,
+ // ulong, "exclude_callchain_user", 1,
+ // ulong, "mmap2", 1,
+ // ulong, "comm_exec", 1,
+ // ulong, "use_clockid", 1,
+ // ulong, "context_switch", 1,
+ // ulong, "write_backward", 1,
+ // ulong, "namespaces", 1,
+ // ulong, "__reserved_1", 35));
+ private ulong perf_event_attr_bitmanip;
+ ///
+ @property ulong disabled() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 1U) >> 0U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void disabled(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= disabled_min,
+ "Value is smaller than the minimum value of bitfield 'disabled'");
+ assert(v <= disabled_max,
+ "Value is greater than the maximum value of bitfield 'disabled'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 1U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 0U) & 1U));
+ }
+
+ enum ulong disabled_min = cast(ulong) 0U;
+ enum ulong disabled_max = cast(ulong) 1U;
+ ///
+ @property ulong inherit() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 2U) >> 1U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void inherit(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= inherit_min,
+ "Value is smaller than the minimum value of bitfield 'inherit'");
+ assert(v <= inherit_max,
+ "Value is greater than the maximum value of bitfield 'inherit'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 2U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 1U) & 2U));
+ }
+
+ enum ulong inherit_min = cast(ulong) 0U;
+ enum ulong inherit_max = cast(ulong) 1U;
+ ///
+ @property ulong pinned() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 4U) >> 2U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void pinned(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= pinned_min,
+ "Value is smaller than the minimum value of bitfield 'pinned'");
+ assert(v <= pinned_max,
+ "Value is greater than the maximum value of bitfield 'pinned'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 4U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 2U) & 4U));
+ }
+
+ enum ulong pinned_min = cast(ulong) 0U;
+ enum ulong pinned_max = cast(ulong) 1U;
+ ///
+ @property ulong exclusive() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 8U) >> 3U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void exclusive(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= exclusive_min,
+ "Value is smaller than the minimum value of bitfield 'exclusive'");
+ assert(v <= exclusive_max,
+ "Value is greater than the maximum value of bitfield 'exclusive'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 8U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 3U) & 8U));
+ }
+
+ enum ulong exclusive_min = cast(ulong) 0U;
+ enum ulong exclusive_max = cast(ulong) 1U;
+ ///
+ @property ulong exclude_user() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 16U) >> 4U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void exclude_user(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= exclude_user_min,
+ "Value is smaller than the minimum value of bitfield 'exclude_user'");
+ assert(v <= exclude_user_max,
+ "Value is greater than the maximum value of bitfield 'exclude_user'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 16U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 4U) & 16U));
+ }
+
+ enum ulong exclude_user_min = cast(ulong) 0U;
+ enum ulong exclude_user_max = cast(ulong) 1U;
+ ///
+ @property ulong exclude_kernel() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 32U) >> 5U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void exclude_kernel(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= exclude_kernel_min,
+ "Value is smaller than the minimum value of bitfield 'exclude_kernel'");
+ assert(v <= exclude_kernel_max,
+ "Value is greater than the maximum value of bitfield 'exclude_kernel'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 32U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 5U) & 32U));
+ }
+
+ enum ulong exclude_kernel_min = cast(ulong) 0U;
+ enum ulong exclude_kernel_max = cast(ulong) 1U;
+ ///
+ @property ulong exclude_hv() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 64U) >> 6U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void exclude_hv(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= exclude_hv_min,
+ "Value is smaller than the minimum value of bitfield 'exclude_hv'");
+ assert(v <= exclude_hv_max,
+ "Value is greater than the maximum value of bitfield 'exclude_hv'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 64U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 6U) & 64U));
+ }
+
+ enum ulong exclude_hv_min = cast(ulong) 0U;
+ enum ulong exclude_hv_max = cast(ulong) 1U;
+ ///
+ @property ulong exclude_idle() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 128U) >> 7U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void exclude_idle(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= exclude_idle_min,
+ "Value is smaller than the minimum value of bitfield 'exclude_idle'");
+ assert(v <= exclude_idle_max,
+ "Value is greater than the maximum value of bitfield 'exclude_idle'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 128U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 7U) & 128U));
+ }
+
+ enum ulong exclude_idle_min = cast(ulong) 0U;
+ enum ulong exclude_idle_max = cast(ulong) 1U;
+ ///
+ @property ulong mmap() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 256U) >> 8U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mmap(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mmap_min, "Value is smaller than the minimum value of bitfield 'mmap'");
+ assert(v <= mmap_max, "Value is greater than the maximum value of bitfield 'mmap'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 256U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 8U) & 256U));
+ }
+
+ enum ulong mmap_min = cast(ulong) 0U;
+ enum ulong mmap_max = cast(ulong) 1U;
+ ///
+ @property ulong comm() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 512U) >> 9U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void comm(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= comm_min, "Value is smaller than the minimum value of bitfield 'comm'");
+ assert(v <= comm_max, "Value is greater than the maximum value of bitfield 'comm'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 512U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 9U) & 512U));
+ }
+
+ enum ulong comm_min = cast(ulong) 0U;
+ enum ulong comm_max = cast(ulong) 1U;
+ ///
+ @property ulong freq() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 1024U) >> 10U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void freq(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= freq_min, "Value is smaller than the minimum value of bitfield 'freq'");
+ assert(v <= freq_max, "Value is greater than the maximum value of bitfield 'freq'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 1024U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 10U) & 1024U));
+ }
+
+ enum ulong freq_min = cast(ulong) 0U;
+ enum ulong freq_max = cast(ulong) 1U;
+ ///
+ @property ulong inherit_stat() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 2048U) >> 11U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void inherit_stat(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= inherit_stat_min,
+ "Value is smaller than the minimum value of bitfield 'inherit_stat'");
+ assert(v <= inherit_stat_max,
+ "Value is greater than the maximum value of bitfield 'inherit_stat'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 2048U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 11U) & 2048U));
+ }
+
+ enum ulong inherit_stat_min = cast(ulong) 0U;
+ enum ulong inherit_stat_max = cast(ulong) 1U;
+ ///
+ @property ulong enable_on_exec() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 4096U) >> 12U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void enable_on_exec(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= enable_on_exec_min,
+ "Value is smaller than the minimum value of bitfield 'enable_on_exec'");
+ assert(v <= enable_on_exec_max,
+ "Value is greater than the maximum value of bitfield 'enable_on_exec'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 4096U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 12U) & 4096U));
+ }
+
+ enum ulong enable_on_exec_min = cast(ulong) 0U;
+ enum ulong enable_on_exec_max = cast(ulong) 1U;
+ ///
+ @property ulong task() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 8192U) >> 13U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void task(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= task_min, "Value is smaller than the minimum value of bitfield 'task'");
+ assert(v <= task_max, "Value is greater than the maximum value of bitfield 'task'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 8192U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 13U) & 8192U));
+ }
+
+ enum ulong task_min = cast(ulong) 0U;
+ enum ulong task_max = cast(ulong) 1U;
+ ///
+ @property ulong watermark() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 16384U) >> 14U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void watermark(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= watermark_min,
+ "Value is smaller than the minimum value of bitfield 'watermark'");
+ assert(v <= watermark_max,
+ "Value is greater than the maximum value of bitfield 'watermark'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 16384U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 14U) & 16384U));
+ }
+
+ enum ulong watermark_min = cast(ulong) 0U;
+ enum ulong watermark_max = cast(ulong) 1U;
+ ///
+ @property ulong precise_ip() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 98304U) >> 15U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void precise_ip(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= precise_ip_min,
+ "Value is smaller than the minimum value of bitfield 'precise_ip'");
+ assert(v <= precise_ip_max,
+ "Value is greater than the maximum value of bitfield 'precise_ip'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 98304U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 15U) & 98304U));
+ }
+
+ enum ulong precise_ip_min = cast(ulong) 0U;
+ enum ulong precise_ip_max = cast(ulong) 3U;
+ ///
+ @property ulong mmap_data() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 131072U) >> 17U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mmap_data(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mmap_data_min,
+ "Value is smaller than the minimum value of bitfield 'mmap_data'");
+ assert(v <= mmap_data_max,
+ "Value is greater than the maximum value of bitfield 'mmap_data'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 131072U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 17U) & 131072U));
+ }
+
+ enum ulong mmap_data_min = cast(ulong) 0U;
+ enum ulong mmap_data_max = cast(ulong) 1U;
+ ///
+ @property ulong sample_id_all() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 262144U) >> 18U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void sample_id_all(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= sample_id_all_min,
+ "Value is smaller than the minimum value of bitfield 'sample_id_all'");
+ assert(v <= sample_id_all_max,
+ "Value is greater than the maximum value of bitfield 'sample_id_all'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 262144U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 18U) & 262144U));
+ }
+
+ enum ulong sample_id_all_min = cast(ulong) 0U;
+ enum ulong sample_id_all_max = cast(ulong) 1U;
+ ///
+ @property ulong exclude_host() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 524288U) >> 19U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void exclude_host(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= exclude_host_min,
+ "Value is smaller than the minimum value of bitfield 'exclude_host'");
+ assert(v <= exclude_host_max,
+ "Value is greater than the maximum value of bitfield 'exclude_host'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 524288U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 19U) & 524288U));
+ }
+
+ enum ulong exclude_host_min = cast(ulong) 0U;
+ enum ulong exclude_host_max = cast(ulong) 1U;
+ ///
+ @property ulong exclude_guest() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 1048576U) >> 20U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void exclude_guest(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= exclude_guest_min,
+ "Value is smaller than the minimum value of bitfield 'exclude_guest'");
+ assert(v <= exclude_guest_max,
+ "Value is greater than the maximum value of bitfield 'exclude_guest'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 1048576U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 20U) & 1048576U));
+ }
+
+ enum ulong exclude_guest_min = cast(ulong) 0U;
+ enum ulong exclude_guest_max = cast(ulong) 1U;
+ ///
+ @property ulong exclude_callchain_kernel() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 2097152U) >> 21U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void exclude_callchain_kernel(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= exclude_callchain_kernel_min,
+ "Value is smaller than the minimum value of bitfield 'exclude_callchain_kernel'");
+ assert(v <= exclude_callchain_kernel_max,
+ "Value is greater than the maximum value of bitfield 'exclude_callchain_kernel'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 2097152U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 21U) & 2097152U));
+ }
+
+ enum ulong exclude_callchain_kernel_min = cast(ulong) 0U;
+ enum ulong exclude_callchain_kernel_max = cast(ulong) 1U;
+ ///
+ @property ulong exclude_callchain_user() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 4194304U) >> 22U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void exclude_callchain_user(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= exclude_callchain_user_min,
+ "Value is smaller than the minimum value of bitfield 'exclude_callchain_user'");
+ assert(v <= exclude_callchain_user_max,
+ "Value is greater than the maximum value of bitfield 'exclude_callchain_user'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 4194304U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 22U) & 4194304U));
+ }
+
+ enum ulong exclude_callchain_user_min = cast(ulong) 0U;
+ enum ulong exclude_callchain_user_max = cast(ulong) 1U;
+ ///
+ @property ulong mmap2() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 8388608U) >> 23U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mmap2(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mmap2_min,
+ "Value is smaller than the minimum value of bitfield 'mmap2'");
+ assert(v <= mmap2_max,
+ "Value is greater than the maximum value of bitfield 'mmap2'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 8388608U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 23U) & 8388608U));
+ }
+
+ enum ulong mmap2_min = cast(ulong) 0U;
+ enum ulong mmap2_max = cast(ulong) 1U;
+ ///
+ @property ulong comm_exec() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 16777216U) >> 24U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void comm_exec(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= comm_exec_min,
+ "Value is smaller than the minimum value of bitfield 'comm_exec'");
+ assert(v <= comm_exec_max,
+ "Value is greater than the maximum value of bitfield 'comm_exec'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 16777216U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 24U) & 16777216U));
+ }
+
+ enum ulong comm_exec_min = cast(ulong) 0U;
+ enum ulong comm_exec_max = cast(ulong) 1U;
+ ///
+ @property ulong use_clockid() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 33554432U) >> 25U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void use_clockid(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= use_clockid_min,
+ "Value is smaller than the minimum value of bitfield 'use_clockid'");
+ assert(v <= use_clockid_max,
+ "Value is greater than the maximum value of bitfield 'use_clockid'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 33554432U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 25U) & 33554432U));
+ }
+
+ enum ulong use_clockid_min = cast(ulong) 0U;
+ enum ulong use_clockid_max = cast(ulong) 1U;
+ ///
+ @property ulong context_switch() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 67108864U) >> 26U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void context_switch(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= context_switch_min,
+ "Value is smaller than the minimum value of bitfield 'context_switch'");
+ assert(v <= context_switch_max,
+ "Value is greater than the maximum value of bitfield 'context_switch'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 67108864U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 26U) & 67108864U));
+ }
+
+ enum ulong context_switch_min = cast(ulong) 0U;
+ enum ulong context_switch_max = cast(ulong) 1U;
+ ///
+ @property ulong write_backward() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 134217728U) >> 27U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void write_backward(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= write_backward_min,
+ "Value is smaller than the minimum value of bitfield 'write_backward'");
+ assert(v <= write_backward_max,
+ "Value is greater than the maximum value of bitfield 'write_backward'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 134217728U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 27U) & 134217728U));
+ }
+
+ enum ulong write_backward_min = cast(ulong) 0U;
+ enum ulong write_backward_max = cast(ulong) 1U;
+ ///
+ @property ulong namespaces() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 268435456U) >> 28U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void namespaces(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= namespaces_min,
+ "Value is smaller than the minimum value of bitfield 'namespaces'");
+ assert(v <= namespaces_max,
+ "Value is greater than the maximum value of bitfield 'namespaces'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(typeof(perf_event_attr_bitmanip)) 268435456U)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 28U) & 268435456U));
+ }
+
+ enum ulong namespaces_min = cast(ulong) 0U;
+ enum ulong namespaces_max = cast(ulong) 1U;
+ ///
+ @property ulong __reserved_1() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_event_attr_bitmanip & 18446744073172680704UL) >> 29U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void __reserved_1(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= __reserved_1_min,
+ "Value is smaller than the minimum value of bitfield '__reserved_1'");
+ assert(v <= __reserved_1_max,
+ "Value is greater than the maximum value of bitfield '__reserved_1'");
+ perf_event_attr_bitmanip = cast(typeof(perf_event_attr_bitmanip))(
+ (perf_event_attr_bitmanip & (-1 - cast(
+ typeof(perf_event_attr_bitmanip)) 18446744073172680704UL)) | (
+ (cast(typeof(perf_event_attr_bitmanip)) v << 29U) & 18446744073172680704UL));
+ }
+
+ enum ulong __reserved_1_min = cast(ulong) 0U;
+ enum ulong __reserved_1_max = cast(ulong) 34359738367UL;
+ ///
+ union
+ {
+ uint wakeup_events; /** wakeup every n events */
+ uint wakeup_watermark; /** bytes before wakeup */
+ }
+ ///
+ uint bp_type;
+
+ union
+ {
+ ///
+ ulong bp_addr;
+ ulong config1; /** extension of config */
+ }
+
+ union
+ {
+ ///
+ ulong bp_len;
+ ulong config2; /** extension of config1 */
+ }
+
+ ulong branch_sample_type; /** enum perf_branch_sample_type */
+
+ /**
+ * Defines set of user regs to dump on samples.
+ * See asm/perf_regs.h for details.
+ */
+ ulong sample_regs_user;
+
+ /**
+ * Defines size of the user stack to dump on samples.
+ */
+ uint sample_stack_user;
+ ///
+ int clockid;
+
+ /**
+ * Defines set of regs to dump for each sample
+ * state captured on:
+ * - precise = 0: PMU interrupt
+ * - precise > 0: sampled instruction
+ *
+ * See asm/perf_regs.h for details.
+ */
+ ulong sample_regs_intr;
+
+ /**
+ * Wakeup watermark for AUX area
+ */
+ uint aux_watermark;
+ ///
+ ushort sample_max_stack;
+ /** align to __u64 */
+ ushort __reserved_2;
+}
+///
+extern (D) auto perf_flags(T)(auto ref T attr)
+{
+ return *(&attr.read_format + 1);
+}
+
+/**
+ * Ioctls that can be done on a perf event fd:
+ */
+enum PERF_EVENT_IOC_ENABLE = _IO('$', 0);
+///
+enum PERF_EVENT_IOC_DISABLE = _IO('$', 1);
+///
+enum PERF_EVENT_IOC_REFRESH = _IO('$', 2);
+///
+enum PERF_EVENT_IOC_RESET = _IO('$', 3);
+///
+enum PERF_EVENT_IOC_PERIOD = _IOW!ulong('$', 4);
+///
+enum PERF_EVENT_IOC_SET_OUTPUT = _IO('$', 5);
+///
+enum PERF_EVENT_IOC_SET_FILTER = _IOW!(char*)('$', 6);
+///
+enum PERF_EVENT_IOC_ID = _IOR!(ulong*)('$', 7);
+///
+enum PERF_EVENT_IOC_SET_BPF = _IOW!uint('$', 8);
+///
+enum PERF_EVENT_IOC_PAUSE_OUTPUT = _IOW!uint('$', 9);
+
+///
+enum perf_event_ioc_flags
+{
+ PERF_IOC_FLAG_GROUP = 1U << 0
+}
+
+/**
+ * Structure of the page that can be mapped via mmap
+ */
+struct perf_event_mmap_page
+{
+ uint version_; /** version number of this structure */
+ uint compat_version; /** lowest version this is compat with */
+
+ /**
+ * Bits needed to read the hw events in user-space.
+ * ---
+ * u32 seq, time_mult, time_shift, index, width;
+ * u64 count, enabled, running;
+ * u64 cyc, time_offset;
+ * s64 pmc = 0;
+ *
+ * do {
+ * seq = pc->lock;
+ * barrier()
+ *
+ * enabled = pc->time_enabled;
+ * running = pc->time_running;
+ *
+ * if (pc->cap_usr_time && enabled != running) {
+ * cyc = rdtsc();
+ * time_offset = pc->time_offset;
+ * time_mult = pc->time_mult;
+ * time_shift = pc->time_shift;
+ * }
+ *
+ * index = pc->index;
+ * count = pc->offset;
+ * if (pc->cap_user_rdpmc && index) {
+ * width = pc->pmc_width;
+ * pmc = rdpmc(index - 1);
+ * }
+ *
+ * barrier();
+ * } while (pc->lock != seq);
+ * ---
+ * NOTE: for obvious reason this only works on self-monitoring
+ * processes.
+ */
+ uint lock; /** seqlock for synchronization */
+ uint index; /** hardware event identifier */
+ long offset; /** add to hardware event value */
+ ulong time_enabled; /** time event active */
+ ulong time_running; /** time event on cpu */
+ ///
+ union
+ {
+ ///
+ ulong capabilities;
+
+ struct
+ {
+ /* mixin(bitfields!(ulong, "cap_bit0", 1, ulong, "cap_bit0_is_deprecated", 1, ulong,
+ "cap_user_rdpmc", 1, ulong, "cap_user_time", 1, ulong,
+ "cap_user_time_zero", 1, ulong, "cap_____res", 59)); */
+
+ private ulong mmap_page_bitmanip;
+ ///
+ @property ulong cap_bit0() @safe pure nothrow @nogc const
+ {
+ auto result = (mmap_page_bitmanip & 1U) >> 0U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void cap_bit0(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= cap_bit0_min,
+ "Value is smaller than the minimum value of bitfield 'cap_bit0'");
+ assert(v <= cap_bit0_max,
+ "Value is greater than the maximum value of bitfield 'cap_bit0'");
+ mmap_page_bitmanip = cast(typeof(mmap_page_bitmanip))(
+ (mmap_page_bitmanip & (-1 - cast(typeof(mmap_page_bitmanip)) 1U)) | (
+ (cast(typeof(mmap_page_bitmanip)) v << 0U) & 1U));
+ }
+
+ enum ulong cap_bit0_min = cast(ulong) 0U;
+ enum ulong cap_bit0_max = cast(ulong) 1U;
+ ///
+ @property ulong cap_bit0_is_deprecated() @safe pure nothrow @nogc const
+ {
+ auto result = (mmap_page_bitmanip & 2U) >> 1U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void cap_bit0_is_deprecated(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= cap_bit0_is_deprecated_min,
+ "Value is smaller than the minimum value of bitfield 'cap_bit0_is_deprecated'");
+ assert(v <= cap_bit0_is_deprecated_max,
+ "Value is greater than the maximum value of bitfield 'cap_bit0_is_deprecated'");
+ mmap_page_bitmanip = cast(typeof(mmap_page_bitmanip))(
+ (mmap_page_bitmanip & (-1 - cast(typeof(mmap_page_bitmanip)) 2U)) | (
+ (cast(typeof(mmap_page_bitmanip)) v << 1U) & 2U));
+ }
+
+ enum ulong cap_bit0_is_deprecated_min = cast(ulong) 0U;
+ enum ulong cap_bit0_is_deprecated_max = cast(ulong) 1U;
+ ///
+ @property ulong cap_user_rdpmc() @safe pure nothrow @nogc const
+ {
+ auto result = (mmap_page_bitmanip & 4U) >> 2U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void cap_user_rdpmc(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= cap_user_rdpmc_min,
+ "Value is smaller than the minimum value of bitfield 'cap_user_rdpmc'");
+ assert(v <= cap_user_rdpmc_max,
+ "Value is greater than the maximum value of bitfield 'cap_user_rdpmc'");
+ mmap_page_bitmanip = cast(typeof(mmap_page_bitmanip))(
+ (mmap_page_bitmanip & (-1 - cast(typeof(mmap_page_bitmanip)) 4U)) | (
+ (cast(typeof(mmap_page_bitmanip)) v << 2U) & 4U));
+ }
+
+ enum ulong cap_user_rdpmc_min = cast(ulong) 0U;
+ enum ulong cap_user_rdpmc_max = cast(ulong) 1U;
+ ///
+ @property ulong cap_user_time() @safe pure nothrow @nogc const
+ {
+ auto result = (mmap_page_bitmanip & 8U) >> 3U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void cap_user_time(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= cap_user_time_min,
+ "Value is smaller than the minimum value of bitfield 'cap_user_time'");
+ assert(v <= cap_user_time_max,
+ "Value is greater than the maximum value of bitfield 'cap_user_time'");
+ mmap_page_bitmanip = cast(typeof(mmap_page_bitmanip))(
+ (mmap_page_bitmanip & (-1 - cast(typeof(mmap_page_bitmanip)) 8U)) | (
+ (cast(typeof(mmap_page_bitmanip)) v << 3U) & 8U));
+ }
+
+ enum ulong cap_user_time_min = cast(ulong) 0U;
+ enum ulong cap_user_time_max = cast(ulong) 1U;
+ ///
+ @property ulong cap_user_time_zero() @safe pure nothrow @nogc const
+ {
+ auto result = (mmap_page_bitmanip & 16U) >> 4U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void cap_user_time_zero(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= cap_user_time_zero_min,
+ "Value is smaller than the minimum value of bitfield 'cap_user_time_zero'");
+ assert(v <= cap_user_time_zero_max,
+ "Value is greater than the maximum value of bitfield 'cap_user_time_zero'");
+ mmap_page_bitmanip = cast(typeof(mmap_page_bitmanip))(
+ (mmap_page_bitmanip & (-1 - cast(typeof(mmap_page_bitmanip)) 16U)) | (
+ (cast(typeof(mmap_page_bitmanip)) v << 4U) & 16U));
+ }
+
+ enum ulong cap_user_time_zero_min = cast(ulong) 0U;
+ enum ulong cap_user_time_zero_max = cast(ulong) 1U;
+ ///
+ @property ulong cap_____res() @safe pure nothrow @nogc const
+ {
+ auto result = (mmap_page_bitmanip & 18446744073709551584UL) >> 5U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void cap_____res(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= cap_____res_min,
+ "Value is smaller than the minimum value of bitfield 'cap_____res'");
+ assert(v <= cap_____res_max,
+ "Value is greater than the maximum value of bitfield 'cap_____res'");
+ mmap_page_bitmanip = cast(typeof(mmap_page_bitmanip))((mmap_page_bitmanip & (
+ -1 - cast(typeof(mmap_page_bitmanip)) 18446744073709551584UL)) | (
+ (cast(typeof(mmap_page_bitmanip)) v << 5U) & 18446744073709551584UL));
+ }
+
+ enum ulong cap_____res_min = cast(ulong) 0U;
+ enum ulong cap_____res_max = cast(ulong) 576460752303423487UL;
+ }
+ }
+
+ /**
+ * If cap_user_rdpmc this field provides the bit-width of the value
+ * read using the rdpmc() or equivalent instruction. This can be used
+ * to sign extend the result like:
+ *
+ * pmc <<= 64 - width;
+ * pmc >>= 64 - width; // signed shift right
+ * count += pmc;
+ */
+ ushort pmc_width;
+
+ /**
+ * If cap_usr_time the below fields can be used to compute the time
+ * delta since time_enabled (in ns) using rdtsc or similar.
+ *
+ * u64 quot, rem;
+ * u64 delta;
+ *
+ * quot = (cyc >> time_shift);
+ * rem = cyc & (((u64)1 << time_shift) - 1);
+ * delta = time_offset + quot * time_mult +
+ * ((rem * time_mult) >> time_shift);
+ *
+ * Where time_offset,time_mult,time_shift and cyc are read in the
+ * seqcount loop described above. This delta can then be added to
+ * enabled and possible running (if index), improving the scaling:
+ *
+ * enabled += delta;
+ * if (index)
+ * running += delta;
+ *
+ * quot = count / running;
+ * rem = count % running;
+ * count = quot * enabled + (rem * enabled) / running;
+ */
+ ushort time_shift;
+ ///
+ uint time_mult;
+ ///
+ ulong time_offset;
+ /**
+ * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated
+ * from sample timestamps.
+ *
+ * time = timestamp - time_zero;
+ * quot = time / time_mult;
+ * rem = time % time_mult;
+ * cyc = (quot << time_shift) + (rem << time_shift) / time_mult;
+ *
+ * And vice versa:
+ *
+ * quot = cyc >> time_shift;
+ * rem = cyc & (((u64)1 << time_shift) - 1);
+ * timestamp = time_zero + quot * time_mult +
+ * ((rem * time_mult) >> time_shift);
+ */
+ ulong time_zero;
+ uint size; /** Header size up to __reserved[] fields. */
+
+ /**
+ * Hole for extension of the self monitor capabilities
+ */
+
+ ubyte[948] __reserved; /** align to 1k. */
+
+ /**
+ * Control data for the mmap() data buffer.
+ *
+ * User-space reading the @data_head value should issue an smp_rmb(),
+ * after reading this value.
+ *
+ * When the mapping is PROT_WRITE the @data_tail value should be
+ * written by userspace to reflect the last read data, after issueing
+ * an smp_mb() to separate the data read from the ->data_tail store.
+ * In this case the kernel will not over-write unread data.
+ *
+ * See perf_output_put_handle() for the data ordering.
+ *
+ * data_{offset,size} indicate the location and size of the perf record
+ * buffer within the mmapped area.
+ */
+ ulong data_head; /** head in the data section */
+ ulong data_tail; /** user-space written tail */
+ ulong data_offset; /** where the buffer starts */
+ ulong data_size; /** data buffer size */
+
+ /**
+ * AUX area is defined by aux_{offset,size} fields that should be set
+ * by the userspace, so that
+ * ---
+ * aux_offset >= data_offset + data_size
+ * ---
+ * prior to mmap()ing it. Size of the mmap()ed area should be aux_size.
+ *
+ * Ring buffer pointers aux_{head,tail} have the same semantics as
+ * data_{head,tail} and same ordering rules apply.
+ */
+ ulong aux_head;
+ ///
+ ulong aux_tail;
+ ///
+ ulong aux_offset;
+ ///
+ ulong aux_size;
+}
+///
+enum PERF_RECORD_MISC_CPUMODE_MASK = 7 << 0;
+///
+enum PERF_RECORD_MISC_CPUMODE_UNKNOWN = 0 << 0;
+///
+enum PERF_RECORD_MISC_KERNEL = 1 << 0;
+///
+enum PERF_RECORD_MISC_USER = 2 << 0;
+///
+enum PERF_RECORD_MISC_HYPERVISOR = 3 << 0;
+///
+enum PERF_RECORD_MISC_GUEST_KERNEL = 4 << 0;
+///
+enum PERF_RECORD_MISC_GUEST_USER = 5 << 0;
+
+/**
+ * Indicates that /proc/PID/maps parsing are truncated by time out.
+ */
+enum PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT = 1 << 12;
+/**
+ * PERF_RECORD_MISC_MMAP_DATA and PERF_RECORD_MISC_COMM_EXEC are used on
+ * different events so can reuse the same bit position.
+ * Ditto PERF_RECORD_MISC_SWITCH_OUT.
+ */
+enum PERF_RECORD_MISC_MMAP_DATA = 1 << 13;
+///
+enum PERF_RECORD_MISC_COMM_EXEC = 1 << 13;
+///
+enum PERF_RECORD_MISC_SWITCH_OUT = 1 << 13;
+/**
+ * Indicates that the content of PERF_SAMPLE_IP points to
+ * the actual instruction that triggered the event. See also
+ * perf_event_attr::precise_ip.
+ */
+enum PERF_RECORD_MISC_EXACT_IP = 1 << 14;
+/**
+ * Reserve the last bit to indicate some extended misc field
+ */
+enum PERF_RECORD_MISC_EXT_RESERVED = 1 << 15;
+///
+struct perf_event_header
+{
+ ///
+ uint type;
+ ///
+ ushort misc;
+ ///
+ ushort size;
+}
+///
+struct perf_ns_link_info
+{
+ ///
+ ulong dev;
+ ///
+ ulong ino;
+}
+
+enum
+{
+ ///
+ NET_NS_INDEX = 0,
+ ///
+ UTS_NS_INDEX = 1,
+ ///
+ IPC_NS_INDEX = 2,
+ ///
+ PID_NS_INDEX = 3,
+ ///
+ USER_NS_INDEX = 4,
+ ///
+ MNT_NS_INDEX = 5,
+ ///
+ CGROUP_NS_INDEX = 6,
+ NR_NAMESPACES = 7 /** number of available namespaces */
+}
+///
+enum perf_event_type
+{
+ /**
+ * If perf_event_attr.sample_id_all is set then all event types will
+ * have the sample_type selected fields related to where/when
+ * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
+ * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
+ * just after the perf_event_header and the fields already present for
+ * the existing fields, i.e. at the end of the payload. That way a newer
+ * perf.data file will be supported by older perf tools, with these new
+ * optional fields being ignored.
+ * ---
+ * struct sample_id {
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * } && perf_event_attr::sample_id_all
+ * ---
+ * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The
+ * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
+ * relative to header.size.
+ */
+
+ /*
+ * The MMAP events record the PROT_EXEC mappings so that we can
+ * correlate userspace IPs to code. They have the following structure:
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_MMAP = 1,
+
+ /**
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ * u64 id;
+ * u64 lost;
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_LOST = 2,
+
+ /**
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * char comm[];
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_COMM = 3,
+
+ /**
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_EXIT = 4,
+
+ /**
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ * u64 time;
+ * u64 id;
+ * u64 stream_id;
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_THROTTLE = 5,
+ PERF_RECORD_UNTHROTTLE = 6,
+ /**
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_FORK = 7,
+ /**
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, tid;
+ *
+ * struct read_format values;
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_READ = 8,
+ /**
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ *
+ * #
+ * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
+ * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
+ * # is fixed relative to header.
+ * #
+ *
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * { u64 ip; } && PERF_SAMPLE_IP
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 addr; } && PERF_SAMPLE_ADDR
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 period; } && PERF_SAMPLE_PERIOD
+ *
+ * { struct read_format values; } && PERF_SAMPLE_READ
+ *
+ * { u64 nr,
+ * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ *
+ * #
+ * # The RAW record below is opaque data wrt the ABI
+ * #
+ * # That is, the ABI doesn't make any promises wrt to
+ * # the stability of its content, it may vary depending
+ * # on event, hardware, kernel version and phase of
+ * # the moon.
+ * #
+ * # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+ * #
+ *
+ * { u32 size;
+ * char data[size];}&& PERF_SAMPLE_RAW
+ *
+ * { u64 nr;
+ * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+ *
+ * { u64 abi; # enum perf_sample_regs_abi
+ * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+ *
+ * { u64 size;
+ * char data[size];
+ * u64 dyn_size; } && PERF_SAMPLE_STACK_USER
+ *
+ * { u64 weight; } && PERF_SAMPLE_WEIGHT
+ * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
+ * { u64 transaction; } && PERF_SAMPLE_TRANSACTION
+ * { u64 abi; # enum perf_sample_regs_abi
+ * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
+ * { u64 phys_addr;} && PERF_SAMPLE_PHYS_ADDR
+ * };
+ * ---
+ */
+ PERF_RECORD_SAMPLE = 9,
+
+ /**
+ * ---
+ * The MMAP2 records are an augmented version of MMAP, they add
+ * maj, min, ino numbers to be used to uniquely identify each mapping
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * u32 maj;
+ * u32 min;
+ * u64 ino;
+ * u64 ino_generation;
+ * u32 prot, flags;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_MMAP2 = 10,
+
+ /**
+ * Records that new data landed in the AUX buffer part.
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u64 aux_offset;
+ * u64 aux_size;
+ * u64 flags;
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_AUX = 11,
+
+ /**
+ * ---
+ * Indicates that instruction trace has started
+ *
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid;
+ * u32 tid;
+ * };
+ * ---
+ */
+ PERF_RECORD_ITRACE_START = 12,
+
+ /**
+ * Records the dropped/lost sample number.
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u64 lost;
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_LOST_SAMPLES = 13,
+
+ /**
+ *
+ * Records a context switch in or out (flagged by
+ * PERF_RECORD_MISC_SWITCH_OUT). See also
+ * PERF_RECORD_SWITCH_CPU_WIDE.
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_SWITCH = 14,
+
+ /**
+ * CPU-wide version of PERF_RECORD_SWITCH with next_prev_pid and
+ * next_prev_tid that are the next (switching out) or previous
+ * (switching in) pid/tid.
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ * u32 next_prev_pid;
+ * u32 next_prev_tid;
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_SWITCH_CPU_WIDE = 15,
+
+ /**
+ * ---
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid;
+ * u32 tid;
+ * u64 nr_namespaces;
+ * { u64 dev, inode; } [nr_namespaces];
+ * struct sample_id sample_id;
+ * };
+ * ---
+ */
+ PERF_RECORD_NAMESPACES = 16,
+
+ PERF_RECORD_MAX = 17 /* non-ABI */
+}
+///
+enum PERF_MAX_STACK_DEPTH = 127;
+///
+enum PERF_MAX_CONTEXTS_PER_STACK = 8;
+///
+enum perf_callchain_context
+{
+ ///
+ PERF_CONTEXT_HV = cast(ulong)-32,
+ ///
+ PERF_CONTEXT_KERNEL = cast(ulong)-128,
+ ///
+ PERF_CONTEXT_USER = cast(ulong)-512,
+ ///
+ PERF_CONTEXT_GUEST = cast(ulong)-2048,
+ ///
+ PERF_CONTEXT_GUEST_KERNEL = cast(ulong)-2176,
+ ///
+ PERF_CONTEXT_GUEST_USER = cast(ulong)-2560,
+ ///
+ PERF_CONTEXT_MAX = cast(ulong)-4095
+}
+
+/**
+ * PERF_RECORD_AUX::flags bits
+ */
+enum PERF_AUX_FLAG_TRUNCATED = 0x01; /** record was truncated to fit */
+enum PERF_AUX_FLAG_OVERWRITE = 0x02; /** snapshot from overwrite mode */
+enum PERF_AUX_FLAG_PARTIAL = 0x04; /** record contains gaps */
+enum PERF_AUX_FLAG_COLLISION = 0x08; /** sample collided with another */
+///
+enum PERF_FLAG_FD_NO_GROUP = 1UL << 0;
+///
+enum PERF_FLAG_FD_OUTPUT = 1UL << 1;
+enum PERF_FLAG_PID_CGROUP = 1UL << 2; /** pid=cgroup id, per-cpu mode only */
+enum PERF_FLAG_FD_CLOEXEC = 1UL << 3; /** O_CLOEXEC */
+///perm_mem_data_src is endian specific.
+version (LittleEndian)
+{
+ ///
+ union perf_mem_data_src
+ {
+ ///
+ ulong val;
+
+ struct
+ {
+ /* mixin(bitfields!(ulong, "mem_op", 5, ulong, "mem_lvl", 14, ulong,
+ "mem_snoop", 5, ulong, "mem_lock", 2, ulong, "mem_dtlb", 7, ulong,
+ "mem_lvl_num", 4, ulong, "mem_remote", 1, ulong,
+ "mem_snoopx", 2, ulong, "mem_rsvd", 24)); */
+
+ private ulong perf_mem_data_src_bitmanip;
+ ///
+ @property ulong mem_op() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src_bitmanip & 31U) >> 0U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_op(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_op_min,
+ "Value is smaller than the minimum value of bitfield 'mem_op'");
+ assert(v <= mem_op_max,
+ "Value is greater than the maximum value of bitfield 'mem_op'");
+ perf_mem_data_src_bitmanip = cast(
+ typeof(perf_mem_data_src_bitmanip))((perf_mem_data_src_bitmanip & (
+ -1 - cast(typeof(perf_mem_data_src_bitmanip)) 31U)) | (
+ (cast(typeof(perf_mem_data_src_bitmanip)) v << 0U) & 31U));
+ }
+
+ enum ulong mem_op_min = cast(ulong) 0U;
+ enum ulong mem_op_max = cast(ulong) 31U;
+ ///
+ @property ulong mem_lvl() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src_bitmanip & 524256U) >> 5U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_lvl(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_lvl_min,
+ "Value is smaller than the minimum value of bitfield 'mem_lvl'");
+ assert(v <= mem_lvl_max,
+ "Value is greater than the maximum value of bitfield 'mem_lvl'");
+ perf_mem_data_src_bitmanip = cast(
+ typeof(perf_mem_data_src_bitmanip))((perf_mem_data_src_bitmanip & (
+ -1 - cast(typeof(perf_mem_data_src_bitmanip)) 524256U)) | (
+ (cast(typeof(perf_mem_data_src_bitmanip)) v << 5U) & 524256U));
+ }
+
+ enum ulong mem_lvl_min = cast(ulong) 0U;
+ enum ulong mem_lvl_max = cast(ulong) 16383U;
+ ///
+ @property ulong mem_snoop() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src_bitmanip & 16252928U) >> 19U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_snoop(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_snoop_min,
+ "Value is smaller than the minimum value of bitfield 'mem_snoop'");
+ assert(v <= mem_snoop_max,
+ "Value is greater than the maximum value of bitfield 'mem_snoop'");
+ perf_mem_data_src_bitmanip = cast(
+ typeof(perf_mem_data_src_bitmanip))((perf_mem_data_src_bitmanip & (
+ -1 - cast(typeof(perf_mem_data_src_bitmanip)) 16252928U)) | (
+ (cast(typeof(perf_mem_data_src_bitmanip)) v << 19U) & 16252928U));
+ }
+
+ enum ulong mem_snoop_min = cast(ulong) 0U;
+ enum ulong mem_snoop_max = cast(ulong) 31U;
+ ///
+ @property ulong mem_lock() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src_bitmanip & 50331648U) >> 24U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_lock(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_lock_min,
+ "Value is smaller than the minimum value of bitfield 'mem_lock'");
+ assert(v <= mem_lock_max,
+ "Value is greater than the maximum value of bitfield 'mem_lock'");
+ perf_mem_data_src_bitmanip = cast(
+ typeof(perf_mem_data_src_bitmanip))((perf_mem_data_src_bitmanip & (
+ -1 - cast(typeof(perf_mem_data_src_bitmanip)) 50331648U)) | (
+ (cast(typeof(perf_mem_data_src_bitmanip)) v << 24U) & 50331648U));
+ }
+
+ enum ulong mem_lock_min = cast(ulong) 0U;
+ enum ulong mem_lock_max = cast(ulong) 3U;
+ ///
+ @property ulong mem_dtlb() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src_bitmanip & 8522825728UL) >> 26U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_dtlb(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_dtlb_min,
+ "Value is smaller than the minimum value of bitfield 'mem_dtlb'");
+ assert(v <= mem_dtlb_max,
+ "Value is greater than the maximum value of bitfield 'mem_dtlb'");
+ perf_mem_data_src_bitmanip = cast(
+ typeof(perf_mem_data_src_bitmanip))((perf_mem_data_src_bitmanip & (
+ -1 - cast(typeof(perf_mem_data_src_bitmanip)) 8522825728UL)) | (
+ (cast(typeof(perf_mem_data_src_bitmanip)) v << 26U) & 8522825728UL));
+ }
+
+ enum ulong mem_dtlb_min = cast(ulong) 0U;
+ enum ulong mem_dtlb_max = cast(ulong) 127U;
+ ///
+ @property ulong mem_lvl_num() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src_bitmanip & 128849018880UL) >> 33U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_lvl_num(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_lvl_num_min,
+ "Value is smaller than the minimum value of bitfield 'mem_lvl_num'");
+ assert(v <= mem_lvl_num_max,
+ "Value is greater than the maximum value of bitfield 'mem_lvl_num'");
+ perf_mem_data_src_bitmanip = cast(
+ typeof(perf_mem_data_src_bitmanip))((perf_mem_data_src_bitmanip & (
+ -1 - cast(typeof(perf_mem_data_src_bitmanip)) 128849018880UL)) | (
+ (cast(typeof(perf_mem_data_src_bitmanip)) v << 33U) & 128849018880UL));
+ }
+
+ enum ulong mem_lvl_num_min = cast(ulong) 0U;
+ enum ulong mem_lvl_num_max = cast(ulong) 15U;
+ ///
+ @property ulong mem_remote() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src_bitmanip & 137438953472UL) >> 37U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_remote(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_remote_min,
+ "Value is smaller than the minimum value of bitfield 'mem_remote'");
+ assert(v <= mem_remote_max,
+ "Value is greater than the maximum value of bitfield 'mem_remote'");
+ perf_mem_data_src_bitmanip = cast(
+ typeof(perf_mem_data_src_bitmanip))((perf_mem_data_src_bitmanip & (
+ -1 - cast(typeof(perf_mem_data_src_bitmanip)) 137438953472UL)) | (
+ (cast(typeof(perf_mem_data_src_bitmanip)) v << 37U) & 137438953472UL));
+ }
+
+ enum ulong mem_remote_min = cast(ulong) 0U;
+ enum ulong mem_remote_max = cast(ulong) 1U;
+ ///
+ @property ulong mem_snoopx() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src_bitmanip & 824633720832UL) >> 38U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_snoopx(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_snoopx_min,
+ "Value is smaller than the minimum value of bitfield 'mem_snoopx'");
+ assert(v <= mem_snoopx_max,
+ "Value is greater than the maximum value of bitfield 'mem_snoopx'");
+ perf_mem_data_src_bitmanip = cast(
+ typeof(perf_mem_data_src_bitmanip))((perf_mem_data_src_bitmanip & (
+ -1 - cast(typeof(perf_mem_data_src_bitmanip)) 824633720832UL)) | (
+ (cast(typeof(perf_mem_data_src_bitmanip)) v << 38U) & 824633720832UL));
+ }
+
+ enum ulong mem_snoopx_min = cast(ulong) 0U;
+ enum ulong mem_snoopx_max = cast(ulong) 3U;
+ ///
+ @property ulong mem_rsvd() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src_bitmanip & 18446742974197923840UL) >> 40U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_rsvd(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_rsvd_min,
+ "Value is smaller than the minimum value of bitfield 'mem_rsvd'");
+ assert(v <= mem_rsvd_max,
+ "Value is greater than the maximum value of bitfield 'mem_rsvd'");
+ perf_mem_data_src_bitmanip = cast(
+ typeof(perf_mem_data_src_bitmanip))(
+ (perf_mem_data_src_bitmanip & (-1 - cast(
+ typeof(perf_mem_data_src_bitmanip)) 18446742974197923840UL)) | (
+ (cast(typeof(perf_mem_data_src_bitmanip)) v << 40U) & 18446742974197923840UL));
+ }
+
+ enum ulong mem_rsvd_min = cast(ulong) 0U;
+ enum ulong mem_rsvd_max = cast(ulong) 16777215U;
+
+ }
+ }
+}
+else
+{
+ ///
+ union perf_mem_data_src
+ {
+ ///
+ ulong val;
+
+ struct
+ {
+ import std.bitmanip : bitfields;
+
+ /* mixin(bitfields!(ulong, "mem_rsvd", 24, ulong, "mem_snoopx", 2, ulong,
+ "mem_remote", 1, ulong, "mem_lvl_num", 4, ulong, "mem_dtlb", 7, ulong,
+ "mem_lock", 2, ulong, "mem_snoop", 5, ulong, "mem_lvl",
+ 14, ulong, "mem_op", 5)); */
+ private ulong perf_mem_data_src;
+ ///
+ @property ulong mem_rsvd() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src & 16777215U) >> 0U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_rsvd(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_rsvd_min,
+ "Value is smaller than the minimum value of bitfield 'mem_rsvd'");
+ assert(v <= mem_rsvd_max,
+ "Value is greater than the maximum value of bitfield 'mem_rsvd'");
+ perf_mem_data_src = cast(typeof(perf_mem_data_src))(
+ (perf_mem_data_src & (-1 - cast(typeof(perf_mem_data_src)) 16777215U)) | (
+ (cast(typeof(perf_mem_data_src)) v << 0U) & 16777215U));
+ }
+
+ enum ulong mem_rsvd_min = cast(ulong) 0U;
+ enum ulong mem_rsvd_max = cast(ulong) 16777215U;
+ ///
+ @property ulong mem_snoopx() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src & 50331648U) >> 24U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_snoopx(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_snoopx_min,
+ "Value is smaller than the minimum value of bitfield 'mem_snoopx'");
+ assert(v <= mem_snoopx_max,
+ "Value is greater than the maximum value of bitfield 'mem_snoopx'");
+ perf_mem_data_src = cast(typeof(perf_mem_data_src))(
+ (perf_mem_data_src & (-1 - cast(typeof(perf_mem_data_src)) 50331648U)) | (
+ (cast(typeof(perf_mem_data_src)) v << 24U) & 50331648U));
+ }
+
+ enum ulong mem_snoopx_min = cast(ulong) 0U;
+ enum ulong mem_snoopx_max = cast(ulong) 3U;
+ ///
+ @property ulong mem_remote() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src & 67108864U) >> 26U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_remote(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_remote_min,
+ "Value is smaller than the minimum value of bitfield 'mem_remote'");
+ assert(v <= mem_remote_max,
+ "Value is greater than the maximum value of bitfield 'mem_remote'");
+ perf_mem_data_src = cast(typeof(perf_mem_data_src))(
+ (perf_mem_data_src & (-1 - cast(typeof(perf_mem_data_src)) 67108864U)) | (
+ (cast(typeof(perf_mem_data_src)) v << 26U) & 67108864U));
+ }
+
+ enum ulong mem_remote_min = cast(ulong) 0U;
+ enum ulong mem_remote_max = cast(ulong) 1U;
+ ///
+ @property ulong mem_lvl_num() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src & 2013265920U) >> 27U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_lvl_num(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_lvl_num_min,
+ "Value is smaller than the minimum value of bitfield 'mem_lvl_num'");
+ assert(v <= mem_lvl_num_max,
+ "Value is greater than the maximum value of bitfield 'mem_lvl_num'");
+ perf_mem_data_src = cast(typeof(perf_mem_data_src))(
+ (perf_mem_data_src & (-1 - cast(typeof(perf_mem_data_src)) 2013265920U)) | (
+ (cast(typeof(perf_mem_data_src)) v << 27U) & 2013265920U));
+ }
+
+ enum ulong mem_lvl_num_min = cast(ulong) 0U;
+ enum ulong mem_lvl_num_max = cast(ulong) 15U;
+ ///
+ @property ulong mem_dtlb() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src & 272730423296UL) >> 31U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_dtlb(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_dtlb_min,
+ "Value is smaller than the minimum value of bitfield 'mem_dtlb'");
+ assert(v <= mem_dtlb_max,
+ "Value is greater than the maximum value of bitfield 'mem_dtlb'");
+ perf_mem_data_src = cast(typeof(perf_mem_data_src))(
+ (perf_mem_data_src & (-1 - cast(typeof(perf_mem_data_src)) 272730423296UL)) | (
+ (cast(typeof(perf_mem_data_src)) v << 31U) & 272730423296UL));
+ }
+
+ enum ulong mem_dtlb_min = cast(ulong) 0U;
+ enum ulong mem_dtlb_max = cast(ulong) 127U;
+ ///
+ @property ulong mem_lock() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src & 824633720832UL) >> 38U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_lock(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_lock_min,
+ "Value is smaller than the minimum value of bitfield 'mem_lock'");
+ assert(v <= mem_lock_max,
+ "Value is greater than the maximum value of bitfield 'mem_lock'");
+ perf_mem_data_src = cast(typeof(perf_mem_data_src))(
+ (perf_mem_data_src & (-1 - cast(typeof(perf_mem_data_src)) 824633720832UL)) | (
+ (cast(typeof(perf_mem_data_src)) v << 38U) & 824633720832UL));
+ }
+
+ enum ulong mem_lock_min = cast(ulong) 0U;
+ enum ulong mem_lock_max = cast(ulong) 3U;
+ ///
+ @property ulong mem_snoop() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src & 34084860461056UL) >> 40U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_snoop(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_snoop_min,
+ "Value is smaller than the minimum value of bitfield 'mem_snoop'");
+ assert(v <= mem_snoop_max,
+ "Value is greater than the maximum value of bitfield 'mem_snoop'");
+ perf_mem_data_src = cast(typeof(perf_mem_data_src))(
+ (perf_mem_data_src & (-1 - cast(typeof(perf_mem_data_src)) 34084860461056UL)) | (
+ (cast(typeof(perf_mem_data_src)) v << 40U) & 34084860461056UL));
+ }
+
+ enum ulong mem_snoop_min = cast(ulong) 0U;
+ enum ulong mem_snoop_max = cast(ulong) 31U;
+ ///
+ @property ulong mem_lvl() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src & 576425567931334656UL) >> 45U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_lvl(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_lvl_min,
+ "Value is smaller than the minimum value of bitfield 'mem_lvl'");
+ assert(v <= mem_lvl_max,
+ "Value is greater than the maximum value of bitfield 'mem_lvl'");
+ perf_mem_data_src = cast(typeof(perf_mem_data_src))((perf_mem_data_src & (
+ -1 - cast(typeof(perf_mem_data_src)) 576425567931334656UL)) | (
+ (cast(typeof(perf_mem_data_src)) v << 45U) & 576425567931334656UL));
+ }
+
+ enum ulong mem_lvl_min = cast(ulong) 0U;
+ enum ulong mem_lvl_max = cast(ulong) 16383U;
+ ///
+ @property ulong mem_op() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_mem_data_src & 17870283321406128128UL) >> 59U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mem_op(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mem_op_min,
+ "Value is smaller than the minimum value of bitfield 'mem_op'");
+ assert(v <= mem_op_max,
+ "Value is greater than the maximum value of bitfield 'mem_op'");
+ perf_mem_data_src = cast(typeof(perf_mem_data_src))((perf_mem_data_src & (
+ -1 - cast(typeof(perf_mem_data_src)) 17870283321406128128UL)) | (
+ (cast(typeof(perf_mem_data_src)) v << 59U) & 17870283321406128128UL));
+ }
+
+ enum ulong mem_op_min = cast(ulong) 0U;
+ enum ulong mem_op_max = cast(ulong) 31U;
+ }
+ }
+}
+
+/* snoop mode, ext */
+/* remote */
+/* memory hierarchy level number */
+/* tlb access */
+/* lock instr */
+/* snoop mode */
+/* memory hierarchy level */
+/* type of opcode */
+
+/** type of opcode (load/store/prefetch,code) */
+enum PERF_MEM_OP_NA = 0x01; /** not available */
+enum PERF_MEM_OP_LOAD = 0x02; /** load instruction */
+enum PERF_MEM_OP_STORE = 0x04; /** store instruction */
+enum PERF_MEM_OP_PFETCH = 0x08; /** prefetch */
+enum PERF_MEM_OP_EXEC = 0x10; /** code (execution) */
+enum PERF_MEM_OP_SHIFT = 0;
+
+/* memory hierarchy (memory level, hit or miss) */
+enum PERF_MEM_LVL_NA = 0x01; /** not available */
+enum PERF_MEM_LVL_HIT = 0x02; /** hit level */
+enum PERF_MEM_LVL_MISS = 0x04; /** miss level */
+enum PERF_MEM_LVL_L1 = 0x08; /** L1 */
+enum PERF_MEM_LVL_LFB = 0x10; /** Line Fill Buffer */
+enum PERF_MEM_LVL_L2 = 0x20; /** L2 */
+enum PERF_MEM_LVL_L3 = 0x40; /** L3 */
+enum PERF_MEM_LVL_LOC_RAM = 0x80; /** Local DRAM */
+enum PERF_MEM_LVL_REM_RAM1 = 0x100; /** Remote DRAM (1 hop) */
+enum PERF_MEM_LVL_REM_RAM2 = 0x200; /** Remote DRAM (2 hops) */
+enum PERF_MEM_LVL_REM_CCE1 = 0x400; /** Remote Cache (1 hop) */
+enum PERF_MEM_LVL_REM_CCE2 = 0x800; /** Remote Cache (2 hops) */
+enum PERF_MEM_LVL_IO = 0x1000; /** I/O memory */
+enum PERF_MEM_LVL_UNC = 0x2000; /** Uncached memory */
+///
+enum PERF_MEM_LVL_SHIFT = 5;
+
+enum PERF_MEM_REMOTE_REMOTE = 0x01; /** Remote */
+///
+enum PERF_MEM_REMOTE_SHIFT = 37;
+
+enum PERF_MEM_LVLNUM_L1 = 0x01; /** L1 */
+enum PERF_MEM_LVLNUM_L2 = 0x02; /** L2 */
+enum PERF_MEM_LVLNUM_L3 = 0x03; /** L3 */
+enum PERF_MEM_LVLNUM_L4 = 0x04; /** L4 */
+/* 5-0xa available */
+enum PERF_MEM_LVLNUM_ANY_CACHE = 0x0b; /** Any cache */
+enum PERF_MEM_LVLNUM_LFB = 0x0c; /** LFB */
+enum PERF_MEM_LVLNUM_RAM = 0x0d; /** RAM */
+enum PERF_MEM_LVLNUM_PMEM = 0x0e; /** PMEM */
+enum PERF_MEM_LVLNUM_NA = 0x0f; /** N/A */
+///
+enum PERF_MEM_LVLNUM_SHIFT = 33;
+
+/* snoop mode */
+enum PERF_MEM_SNOOP_NA = 0x01; /** not available */
+enum PERF_MEM_SNOOP_NONE = 0x02; /** no snoop */
+enum PERF_MEM_SNOOP_HIT = 0x04; /** snoop hit */
+enum PERF_MEM_SNOOP_MISS = 0x08; /** snoop miss */
+enum PERF_MEM_SNOOP_HITM = 0x10; /** snoop hit modified */
+///
+enum PERF_MEM_SNOOP_SHIFT = 19;
+
+enum PERF_MEM_SNOOPX_FWD = 0x01; /** forward */
+/** 1 free */
+enum PERF_MEM_SNOOPX_SHIFT = 37;
+
+/** locked instruction */
+enum PERF_MEM_LOCK_NA = 0x01; /** not available */
+enum PERF_MEM_LOCK_LOCKED = 0x02; /** locked transaction */
+///
+enum PERF_MEM_LOCK_SHIFT = 24;
+
+/* TLB access */
+enum PERF_MEM_TLB_NA = 0x01; /** not available */
+enum PERF_MEM_TLB_HIT = 0x02; /** hit level */
+enum PERF_MEM_TLB_MISS = 0x04; /** miss level */
+enum PERF_MEM_TLB_L1 = 0x08; /** L1 */
+enum PERF_MEM_TLB_L2 = 0x10; /** L2 */
+enum PERF_MEM_TLB_WK = 0x20; /** Hardware Walker*/
+enum PERF_MEM_TLB_OS = 0x40; /** OS fault handler */
+///
+enum PERF_MEM_TLB_SHIFT = 26;
+
+/**
+ * single taken branch record layout:
+ *
+ * from: source instruction (may not always be a branch insn)
+ * to: branch target
+ * mispred: branch target was mispredicted
+ * predicted: branch target was predicted
+ *
+ * support for mispred, predicted is optional. In case it
+ * is not supported mispred = predicted = 0.
+ *
+ * in_tx: running in a hardware transaction
+ * abort: aborting a hardware transaction
+ * cycles: cycles from last branch (or 0 if not supported)
+ * type: branch type
+ */
+struct perf_branch_entry
+{
+ ///
+ ulong from;
+ ///
+ ulong to;
+
+ /* mixin(bitfields!(ulong, "mispred", 1, ulong, "predicted", 1, ulong,
+ "in_tx", 1, ulong, "abort", 1, ulong, "cycles", 16, ulong, "type",
+ 4, ulong, "reserved", 40)); */
+ private ulong perf_branch_entry_bitmanip;
+ ///
+ @property ulong mispred() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_branch_entry_bitmanip & 1U) >> 0U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void mispred(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= mispred_min,
+ "Value is smaller than the minimum value of bitfield 'mispred'");
+ assert(v <= mispred_max,
+ "Value is greater than the maximum value of bitfield 'mispred'");
+ perf_branch_entry_bitmanip = cast(typeof(perf_branch_entry_bitmanip))(
+ (perf_branch_entry_bitmanip & (-1 - cast(typeof(perf_branch_entry_bitmanip)) 1U)) | (
+ (cast(typeof(perf_branch_entry_bitmanip)) v << 0U) & 1U));
+ }
+
+ enum ulong mispred_min = cast(ulong) 0U;
+ enum ulong mispred_max = cast(ulong) 1U;
+ ///
+ @property ulong predicted() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_branch_entry_bitmanip & 2U) >> 1U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void predicted(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= predicted_min,
+ "Value is smaller than the minimum value of bitfield 'predicted'");
+ assert(v <= predicted_max,
+ "Value is greater than the maximum value of bitfield 'predicted'");
+ perf_branch_entry_bitmanip = cast(typeof(perf_branch_entry_bitmanip))(
+ (perf_branch_entry_bitmanip & (-1 - cast(typeof(perf_branch_entry_bitmanip)) 2U)) | (
+ (cast(typeof(perf_branch_entry_bitmanip)) v << 1U) & 2U));
+ }
+
+ enum ulong predicted_min = cast(ulong) 0U;
+ enum ulong predicted_max = cast(ulong) 1U;
+ ///
+ @property ulong in_tx() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_branch_entry_bitmanip & 4U) >> 2U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void in_tx(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= in_tx_min,
+ "Value is smaller than the minimum value of bitfield 'in_tx'");
+ assert(v <= in_tx_max,
+ "Value is greater than the maximum value of bitfield 'in_tx'");
+ perf_branch_entry_bitmanip = cast(typeof(perf_branch_entry_bitmanip))(
+ (perf_branch_entry_bitmanip & (-1 - cast(typeof(perf_branch_entry_bitmanip)) 4U)) | (
+ (cast(typeof(perf_branch_entry_bitmanip)) v << 2U) & 4U));
+ }
+
+ enum ulong in_tx_min = cast(ulong) 0U;
+ enum ulong in_tx_max = cast(ulong) 1U;
+ ///
+ @property ulong abort() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_branch_entry_bitmanip & 8U) >> 3U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void abort(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= abort_min,
+ "Value is smaller than the minimum value of bitfield 'abort'");
+ assert(v <= abort_max,
+ "Value is greater than the maximum value of bitfield 'abort'");
+ perf_branch_entry_bitmanip = cast(typeof(perf_branch_entry_bitmanip))(
+ (perf_branch_entry_bitmanip & (-1 - cast(typeof(perf_branch_entry_bitmanip)) 8U)) | (
+ (cast(typeof(perf_branch_entry_bitmanip)) v << 3U) & 8U));
+ }
+
+ enum ulong abort_min = cast(ulong) 0U;
+ enum ulong abort_max = cast(ulong) 1U;
+ ///
+ @property ulong cycles() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_branch_entry_bitmanip & 1048560U) >> 4U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void cycles(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= cycles_min,
+ "Value is smaller than the minimum value of bitfield 'cycles'");
+ assert(v <= cycles_max,
+ "Value is greater than the maximum value of bitfield 'cycles'");
+ perf_branch_entry_bitmanip = cast(typeof(perf_branch_entry_bitmanip))(
+ (perf_branch_entry_bitmanip & (-1 - cast(typeof(perf_branch_entry_bitmanip)) 1048560U)) | (
+ (cast(typeof(perf_branch_entry_bitmanip)) v << 4U) & 1048560U));
+ }
+
+ enum ulong cycles_min = cast(ulong) 0U;
+ enum ulong cycles_max = cast(ulong) 65535U;
+ ///
+ @property ulong type() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_branch_entry_bitmanip & 15728640U) >> 20U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void type(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= type_min, "Value is smaller than the minimum value of bitfield 'type'");
+ assert(v <= type_max, "Value is greater than the maximum value of bitfield 'type'");
+ perf_branch_entry_bitmanip = cast(typeof(perf_branch_entry_bitmanip))(
+ (perf_branch_entry_bitmanip & (-1 - cast(typeof(perf_branch_entry_bitmanip)) 15728640U)) | (
+ (cast(typeof(perf_branch_entry_bitmanip)) v << 20U) & 15728640U));
+ }
+
+ enum ulong type_min = cast(ulong) 0U;
+ enum ulong type_max = cast(ulong) 15U;
+ ///
+ @property ulong reserved() @safe pure nothrow @nogc const
+ {
+ auto result = (perf_branch_entry_bitmanip & 18446744073692774400UL) >> 24U;
+ return cast(ulong) result;
+ }
+ ///
+ @property void reserved(ulong v) @safe pure nothrow @nogc
+ {
+ assert(v >= reserved_min,
+ "Value is smaller than the minimum value of bitfield 'reserved'");
+ assert(v <= reserved_max,
+ "Value is greater than the maximum value of bitfield 'reserved'");
+ perf_branch_entry_bitmanip = cast(typeof(perf_branch_entry_bitmanip))(
+ (perf_branch_entry_bitmanip & (-1 - cast(
+ typeof(perf_branch_entry_bitmanip)) 18446744073692774400UL)) | (
+ (cast(typeof(perf_branch_entry_bitmanip)) v << 24U) & 18446744073692774400UL));
+ }
+
+ enum ulong reserved_min = cast(ulong) 0U;
+ enum ulong reserved_max = cast(ulong) 1099511627775UL;
+}
diff --git a/libphobos/libdruntime/core/sys/linux/sys/procfs.d b/libphobos/libdruntime/core/sys/linux/sys/procfs.d
new file mode 100644
index 00000000000..6a113e172dc
--- /dev/null
+++ b/libphobos/libdruntime/core/sys/linux/sys/procfs.d
@@ -0,0 +1,15 @@
+/**
+ * D header file for GNU/Linux.
+ *
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Teodor Dutu
+ */
+
+module core.sys.linux.sys.procfs;
+
+import core.sys.posix.sys.types : pid_t;
+
+version (linux)
+{
+ alias lwpid_t = pid_t;
+}
diff --git a/libphobos/libdruntime/core/sys/netbsd/sys/elf32.d b/libphobos/libdruntime/core/sys/netbsd/sys/elf32.d
index b22b97f9f3c..ac623d6955d 100644
--- a/libphobos/libdruntime/core/sys/netbsd/sys/elf32.d
+++ b/libphobos/libdruntime/core/sys/netbsd/sys/elf32.d
@@ -112,7 +112,7 @@ extern (D)
{
auto ELF32_M_SYM(I)(I info) { return info >> 8; }
auto ELF32_M_SIZE(I)(I info) { return cast(ubyte)info; }
- auto ELF32_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubye)size; }
+ auto ELF32_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubyte)size; }
}
struct Elf32_Cap
diff --git a/libphobos/libdruntime/core/sys/netbsd/sys/elf64.d b/libphobos/libdruntime/core/sys/netbsd/sys/elf64.d
index f78d066ef69..659ac4054fb 100644
--- a/libphobos/libdruntime/core/sys/netbsd/sys/elf64.d
+++ b/libphobos/libdruntime/core/sys/netbsd/sys/elf64.d
@@ -118,7 +118,7 @@ extern (D)
{
auto ELF64_M_SYM(I)(I info) { return info >> 8; }
auto ELF64_M_SIZE(I)(I info) { return cast(ubyte)info; }
- auto ELF64_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubye)size; }
+ auto ELF64_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubyte)size; }
}
struct Elf64_Cap
diff --git a/libphobos/libdruntime/core/sys/openbsd/execinfo.d b/libphobos/libdruntime/core/sys/openbsd/execinfo.d
new file mode 100644
index 00000000000..f5b317f2bfc
--- /dev/null
+++ b/libphobos/libdruntime/core/sys/openbsd/execinfo.d
@@ -0,0 +1,147 @@
+/**
+ * OpenBSD implementation of glibc's $(LINK2 http://www.gnu.org/software/libc/manual/html_node/Backtraces.html backtrace) facility.
+ *
+ * Copyright: Copyright Martin Nowak 2012.
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Authors: Martin Nowak
+ * Source: $(DRUNTIMESRC core/sys/openbsd/_execinfo.d)
+ */
+module core.sys.openbsd.execinfo;
+
+version (OpenBSD):
+extern (C):
+nothrow:
+
+version (GNU)
+ version = BacktraceExternal;
+
+version (BacktraceExternal)
+{
+ size_t backtrace(void**, size_t);
+ char** backtrace_symbols(const(void*)*, size_t);
+ void backtrace_symbols_fd(const(void*)*, size_t, int);
+ char** backtrace_symbols_fmt(const(void*)*, size_t, const char*);
+ int backtrace_symbols_fd_fmt(const(void*)*, size_t, int, const char*);
+}
+else
+{
+ import core.sys.openbsd.dlfcn;
+
+ // Use extern (D) so that these functions don't collide with libexecinfo.
+
+ extern (D) int backtrace(void** buffer, int size)
+ {
+ import core.thread : thread_stackBottom;
+
+ void** p, pend=cast(void**)thread_stackBottom();
+ version (D_InlineAsm_X86)
+ asm nothrow @trusted { mov p[EBP], EBP; }
+ else version (D_InlineAsm_X86_64)
+ asm nothrow @trusted { mov p[RBP], RBP; }
+ else
+ static assert(false, "Architecture not supported.");
+
+ int i;
+ for (; i < size && p < pend; ++i)
+ {
+ buffer[i] = *(p + 1);
+ auto pnext = cast(void**)*p;
+ if (pnext <= p) break;
+ p = pnext;
+ }
+ return i;
+ }
+
+
+ extern (D) char** backtrace_symbols(const(void*)* buffer, int size)
+ {
+ static void* realloc(void* p, size_t len) nothrow
+ {
+ static import cstdlib=core.stdc.stdlib;
+ auto res = cstdlib.realloc(p, len);
+ if (res is null) cstdlib.free(p);
+ return res;
+ }
+
+ if (size <= 0) return null;
+
+ size_t pos = size * (char*).sizeof;
+ char** p = cast(char**)realloc(null, pos);
+ if (p is null) return null;
+
+ Dl_info info;
+ foreach (i, addr; buffer[0 .. size])
+ {
+ if (dladdr(addr, &info) == 0)
+ (cast(ubyte*)&info)[0 .. info.sizeof] = 0;
+ fixupDLInfo(addr, info);
+
+ immutable len = formatStackFrame(null, 0, addr, info);
+ assert(len > 0);
+
+ p = cast(char**)realloc(p, pos + len);
+ if (p is null) return null;
+
+ formatStackFrame(cast(char*)p + pos, len, addr, info) == len || assert(0);
+
+ p[i] = cast(char*)pos;
+ pos += len;
+ }
+ foreach (i; 0 .. size)
+ {
+ pos = cast(size_t)p[i];
+ p[i] = cast(char*)p + pos;
+ }
+ return p;
+ }
+
+
+ extern (D) void backtrace_symbols_fd(const(void*)* buffer, int size, int fd)
+ {
+ import core.sys.posix.unistd : write;
+ import core.stdc.stdlib : alloca;
+
+ if (size <= 0) return;
+
+ Dl_info info;
+ foreach (i, addr; buffer[0 .. size])
+ {
+ if (dladdr(addr, &info) == 0)
+ (cast(ubyte*)&info)[0 .. info.sizeof] = 0;
+ fixupDLInfo(addr, info);
+
+ enum maxAlloca = 1024;
+ enum min = (size_t a, size_t b) => a <= b ? a : b;
+ immutable len = min(formatStackFrame(null, 0, addr, info), maxAlloca);
+ assert(len > 0);
+
+ auto p = cast(char*)alloca(len);
+ if (p is null) return;
+
+ formatStackFrame(p, len, addr, info) >= len || assert(0);
+ p[len - 1] = '\n';
+ write(fd, p, len);
+ }
+ }
+
+
+ private void fixupDLInfo(const(void)* addr, ref Dl_info info)
+ {
+ if (info.dli_fname is null) info.dli_fname = "???";
+ if (info.dli_fbase is null) info.dli_fbase = null;
+ if (info.dli_sname is null) info.dli_sname = "???";
+ if (info.dli_saddr is null) info.dli_saddr = cast(void*)addr;
+ }
+
+
+ private size_t formatStackFrame(char* p, size_t plen, const(void)* addr, const ref Dl_info info)
+ {
+ import core.stdc.stdio : snprintf;
+
+ immutable off = addr - info.dli_saddr;
+ immutable len = snprintf(p, plen, "%p <%s+%zd> at %s",
+ addr, info.dli_sname, off, info.dli_fname);
+ assert(len > 0);
+ return cast(size_t)len + 1; // + '\0'
+ }
+}
diff --git a/libphobos/libdruntime/core/sys/openbsd/sys/elf32.d b/libphobos/libdruntime/core/sys/openbsd/sys/elf32.d
index cefee38f116..dae977a632e 100644
--- a/libphobos/libdruntime/core/sys/openbsd/sys/elf32.d
+++ b/libphobos/libdruntime/core/sys/openbsd/sys/elf32.d
@@ -112,7 +112,7 @@ extern (D) pure
{
auto ELF32_M_SYM(I)(I info) @safe { return info >> 8; }
auto ELF32_M_SIZE(I)(I info) { return cast(ubyte)info; }
- auto ELF32_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubye)size; }
+ auto ELF32_M_INFO(S, SZ)(S sym, SZ size) { return (sym << 8) + cast(ubyte)size; }
}
struct Elf32_Cap
diff --git a/libphobos/libdruntime/core/sys/openbsd/sys/elf64.d b/libphobos/libdruntime/core/sys/openbsd/sys/elf64.d
index d5e15fc2520..e26a5fc6e3c 100644
--- a/libphobos/libdruntime/core/sys/openbsd/sys/elf64.d
+++ b/libphobos/libdruntime/core/sys/openbsd/sys/elf64.d
@@ -118,7 +118,7 @@ extern (D) pure
{
auto ELF64_M_SYM(I)(I info) @safe { return info >> 8; }
auto ELF64_M_SIZE(I)(I info) { return cast(ubyte)info; }
- auto ELF64_M_INFO(S, SZ)(S sym, SZ size) @safe { return (sym << 8) + cast(ubye)size; }
+ auto ELF64_M_INFO(S, SZ)(S sym, SZ size) @safe { return (sym << 8) + cast(ubyte)size; }
}
struct Elf64_Cap
diff --git a/libphobos/libdruntime/core/sys/posix/arpa/inet.d b/libphobos/libdruntime/core/sys/posix/arpa/inet.d
index 6881142a0fb..c602e17d322 100644
--- a/libphobos/libdruntime/core/sys/posix/arpa/inet.d
+++ b/libphobos/libdruntime/core/sys/posix/arpa/inet.d
@@ -68,8 +68,6 @@ version (CRuntime_Glibc)
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
-
@trusted pure
{
uint32_t htonl(uint32_t);
@@ -93,8 +91,6 @@ else version (Darwin)
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
-
@trusted pure
{
uint32_t htonl(uint32_t);
@@ -118,8 +114,6 @@ else version (FreeBSD)
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
-
@trusted pure
{
uint32_t htonl(uint32_t);
@@ -143,8 +137,6 @@ else version (NetBSD)
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
-
@trusted pure
{
uint32_t htonl(uint32_t);
@@ -168,30 +160,22 @@ else version (OpenBSD)
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
-
@safe pure extern (D)
{
- private
+ version (BigEndian)
{
- uint32_t __swap32( uint32_t x )
- {
- uint32_t byte32_swap = (x & 0xff) << 24 | (x &0xff00) << 8 |
- (x & 0xff0000) >> 8 | (x & 0xff000000) >> 24;
- return byte32_swap;
- }
-
- uint16_t __swap16( uint16_t x )
- {
- uint16_t byte16_swap = (x & 0xff) << 8 | (x & 0xff00) >> 8;
- return byte16_swap;
- }
+ uint32_t htonl(uint32_t x) { return x; }
+ uint16_t htons(uint16_t x) { return x; }
}
+ else
+ {
+ import core.bitop : bswap, byteswap;
- uint32_t htonl(uint32_t x) { return __swap32(x); }
- uint16_t htons(uint16_t x) { return __swap16(x); }
- uint32_t ntohl(uint32_t x) { return __swap32(x); }
- uint16_t ntohs(uint16_t x) { return __swap16(x); }
+ uint32_t htonl(uint32_t x) { return bswap(x); }
+ uint16_t htons(uint16_t x) { return byteswap(x); }
+ }
+ alias ntohl = htonl;
+ alias ntohs = htons;
}
in_addr_t inet_addr(const scope char*);
@@ -209,8 +193,6 @@ else version (DragonFlyBSD)
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
-
@trusted pure
{
uint32_t htonl(uint32_t);
@@ -233,7 +215,6 @@ else version (Solaris)
{
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
@trusted pure
{
@@ -257,30 +238,22 @@ else version (CRuntime_Bionic)
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
-
@safe pure extern (D)
{
- private
+ version (BigEndian)
{
- uint32_t __swap32( uint32_t x )
- {
- uint32_t byte32_swap = (x & 0xff) << 24 | (x &0xff00) << 8 |
- (x & 0xff0000) >> 8 | (x & 0xff000000) >> 24;
- return byte32_swap;
- }
-
- uint16_t __swap16( uint16_t x )
- {
- uint16_t byte16_swap = (x & 0xff) << 8 | (x & 0xff00) >> 8;
- return byte16_swap;
- }
+ uint32_t htonl(uint32_t x) { return x; }
+ uint16_t htons(uint16_t x) { return x; }
}
+ else
+ {
+ import core.bitop : bswap, byteswap;
- uint32_t htonl(uint32_t x) { return __swap32(x); }
- uint16_t htons(uint16_t x) { return __swap16(x); }
- uint32_t ntohl(uint32_t x) { return __swap32(x); }
- uint16_t ntohs(uint16_t x) { return __swap16(x); }
+ uint32_t htonl(uint32_t x) { return bswap(x); }
+ uint16_t htons(uint16_t x) { return byteswap(x); }
+ }
+ alias ntohl = htonl;
+ alias ntohs = htons;
}
in_addr_t inet_addr(const scope char*);
@@ -298,8 +271,6 @@ else version (CRuntime_Musl)
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
-
@trusted pure
{
uint32_t htonl(uint32_t);
@@ -323,8 +294,6 @@ else version (CRuntime_UClibc)
in_addr_t s_addr;
}
- enum INET_ADDRSTRLEN = 16;
-
@trusted pure
{
uint32_t htonl(uint32_t);
@@ -339,9 +308,6 @@ else version (CRuntime_UClibc)
int inet_pton(int, const scope char*, void*);
}
-//
-// IPV6 (IP6)
-//
/*
NOTE: The following must must be defined in core.sys.posix.arpa.inet to break
a circular import: INET6_ADDRSTRLEN.
@@ -349,39 +315,5 @@ NOTE: The following must must be defined in core.sys.posix.arpa.inet to break
INET6_ADDRSTRLEN // from core.sys.posix.netinet.in_
*/
-version (CRuntime_Glibc)
-{
- enum INET6_ADDRSTRLEN = 46;
-}
-else version (Darwin)
-{
- enum INET6_ADDRSTRLEN = 46;
-}
-else version (FreeBSD)
-{
- enum INET6_ADDRSTRLEN = 46;
-}
-else version (NetBSD)
-{
- enum INET6_ADDRSTRLEN = 46;
-}
-else version (OpenBSD)
-{
- enum INET6_ADDRSTRLEN = 46;
-}
-else version (DragonFlyBSD)
-{
- enum INET6_ADDRSTRLEN = 46;
-}
-else version (Solaris)
-{
- enum INET6_ADDRSTRLEN = 46;
-}
-else version (CRuntime_Bionic)
-{
- enum INET6_ADDRSTRLEN = 46;
-}
-else version (CRuntime_UClibc)
-{
- enum INET6_ADDRSTRLEN = 46;
-}
+enum INET_ADDRSTRLEN = 16;
+enum INET6_ADDRSTRLEN = 46;
diff --git a/libphobos/libdruntime/core/sys/posix/fcntl.d b/libphobos/libdruntime/core/sys/posix/fcntl.d
index 59df921ba41..6833f3badf0 100644
--- a/libphobos/libdruntime/core/sys/posix/fcntl.d
+++ b/libphobos/libdruntime/core/sys/posix/fcntl.d
@@ -159,6 +159,7 @@ version (CRuntime_Glibc)
enum O_APPEND = 0x400; // octal 02000
enum O_NONBLOCK = 0x800; // octal 04000
+ enum O_CLOEXEC = 0x80000; // octal 02000000
enum O_SYNC = 0x101000; // octal 04010000
enum O_DSYNC = 0x1000; // octal 010000
enum O_RSYNC = O_SYNC;
@@ -172,6 +173,7 @@ version (CRuntime_Glibc)
enum O_APPEND = 0x00008; // octal 010
enum O_NONBLOCK = 0x10004; // octal 0200004
+ enum O_CLOEXEC = 0x200000; // octal 02000000
enum O_SYNC = 0x48000; // octal 01100000
enum O_DSYNC = 0x40000; // octal 01000000
enum O_RSYNC = 0x80000; // octal 02000000
@@ -186,6 +188,7 @@ version (CRuntime_Glibc)
enum O_APPEND = 0x0008;
enum O_DSYNC = 0x0010;
enum O_NONBLOCK = 0x0080;
+ enum O_CLOEXEC = 0x80000;
enum O_RSYNC = O_SYNC;
enum O_SYNC = 0x4010;
}
@@ -198,6 +201,7 @@ version (CRuntime_Glibc)
enum O_APPEND = 0x400; // octal 02000
enum O_NONBLOCK = 0x800; // octal 04000
+ enum O_CLOEXEC = 0x80000; // octal 02000000
enum O_SYNC = 0x101000; // octal 04010000
enum O_DSYNC = 0x1000; // octal 010000
enum O_RSYNC = O_SYNC;
@@ -211,6 +215,7 @@ version (CRuntime_Glibc)
enum O_APPEND = 0x400; // octal 02000
enum O_NONBLOCK = 0x800; // octal 04000
+ enum O_CLOEXEC = 0x80000; // octal 02000000
enum O_SYNC = 0x101000; // octal 04010000
enum O_DSYNC = 0x1000; // octal 010000
enum O_RSYNC = O_SYNC;
@@ -224,6 +229,7 @@ version (CRuntime_Glibc)
enum O_APPEND = 0x400; // octal 02000
enum O_NONBLOCK = 0x800; // octal 04000
+ enum O_CLOEXEC = 0x80000; // octal 02000000
enum O_SYNC = 0x101000; // octal 04010000
enum O_DSYNC = 0x1000; // octal 010000
enum O_RSYNC = O_SYNC;
@@ -237,6 +243,7 @@ version (CRuntime_Glibc)
enum O_APPEND = 0x8;
enum O_NONBLOCK = 0x4000;
+ enum O_CLOEXEC = 0x400000;
enum O_SYNC = 0x802000;
enum O_DSYNC = 0x2000;
enum O_RSYNC = O_SYNC;
@@ -250,6 +257,7 @@ version (CRuntime_Glibc)
enum O_APPEND = 0x400; // octal 02000
enum O_NONBLOCK = 0x800; // octal 04000
+ enum O_CLOEXEC = 0x80000; // octal 02000000
enum O_SYNC = 0x101000; // octal 04010000
enum O_DSYNC = 0x1000; // octal 010000
enum O_RSYNC = O_SYNC;
@@ -895,10 +903,10 @@ else version (CRuntime_Musl)
O_SEARCH = O_PATH,
O_EXEC = O_PATH,
- O_ACCMODE = (03|O_SEARCH),
- O_RDONLY = 00,
- O_WRONLY = 01,
- O_RDWR = 02,
+ O_ACCMODE = (3|O_SEARCH),
+ O_RDONLY = 0,
+ O_WRONLY = 1,
+ O_RDWR = 2,
}
enum
{
diff --git a/libphobos/libdruntime/core/sys/posix/net/if_.d b/libphobos/libdruntime/core/sys/posix/net/if_.d
index 3713673e4bc..e63af4f7c32 100644
--- a/libphobos/libdruntime/core/sys/posix/net/if_.d
+++ b/libphobos/libdruntime/core/sys/posix/net/if_.d
@@ -157,4 +157,4 @@ else version (CRuntime_UClibc)
char* if_indextoname(uint, char*);
if_nameindex_t* if_nameindex();
void if_freenameindex(if_nameindex_t*);
-}
+} \ No newline at end of file
diff --git a/libphobos/libdruntime/core/sys/posix/semaphore.d b/libphobos/libdruntime/core/sys/posix/semaphore.d
index 4f6f63988ff..a163e592bda 100644
--- a/libphobos/libdruntime/core/sys/posix/semaphore.d
+++ b/libphobos/libdruntime/core/sys/posix/semaphore.d
@@ -99,7 +99,7 @@ else version (NetBSD)
}
else version (OpenBSD)
{
- struct __sem { }
+ struct __sem;
alias sem_t = __sem*;
enum SEM_FAILED = cast(sem_t*) null;
diff --git a/libphobos/libdruntime/core/sys/posix/setjmp.d b/libphobos/libdruntime/core/sys/posix/setjmp.d
index b98d321a883..91e3a19d081 100644
--- a/libphobos/libdruntime/core/sys/posix/setjmp.d
+++ b/libphobos/libdruntime/core/sys/posix/setjmp.d
@@ -261,6 +261,10 @@ else version (OpenBSD)
{
enum _JBLEN = 64;
}
+ else version (AArch64)
+ {
+ enum _JBLEN = 64;
+ }
else version (PPC)
{
enum _JBLEN = 100;
diff --git a/libphobos/libdruntime/core/sys/posix/stdio.d b/libphobos/libdruntime/core/sys/posix/stdio.d
index 41b52da7c64..c8f92ec301b 100644
--- a/libphobos/libdruntime/core/sys/posix/stdio.d
+++ b/libphobos/libdruntime/core/sys/posix/stdio.d
@@ -526,6 +526,16 @@ else version (CRuntime_Musl)
int putc_unlocked(int, FILE*);
int putchar_unlocked(int);
}
+else version (CRuntime_Bionic)
+{
+ void flockfile(FILE*);
+ int ftrylockfile(FILE*);
+ void funlockfile(FILE*);
+ int getc_unlocked(FILE*);
+ int getchar_unlocked();
+ int putc_unlocked(int, FILE*);
+ int putchar_unlocked(int);
+}
else version (Darwin)
{
void flockfile(FILE*);
diff --git a/libphobos/libdruntime/core/sys/posix/string.d b/libphobos/libdruntime/core/sys/posix/string.d
index e17dfc66d33..b9e1c1c88c2 100644
--- a/libphobos/libdruntime/core/sys/posix/string.d
+++ b/libphobos/libdruntime/core/sys/posix/string.d
@@ -31,11 +31,11 @@ public import core.sys.posix.locale : locale_t;
public import core.stdc.string;
/// Copy string until character found
-void* memccpy(return void* dst, scope const void* src, int c, size_t n);
+void* memccpy(return void* dst, scope const void* src, int c, size_t n) pure;
/// Copy string (including terminating '\0')
-char* stpcpy(return char* dst, scope const char* src);
+char* stpcpy(return char* dst, scope const char* src) pure;
/// Ditto
-char* stpncpy(return char* dst, const char* src, size_t len);
+char* stpncpy(return char* dst, const char* src, size_t len) pure;
/// Compare strings according to current collation
int strcoll_l(scope const char* s1, scope const char* s2, locale_t locale);
///
@@ -43,7 +43,7 @@ char* strerror_l(int, locale_t);
/// Save a copy of a string
char* strndup(scope const char* str, size_t len);
/// Find length of string up to `maxlen`
-size_t strnlen(scope const char* str, size_t maxlen);
+size_t strnlen(scope const char* str, size_t maxlen) pure;
/// System signal messages
const(char)* strsignal(int);
/// Isolate sequential tokens in a null-terminated string
diff --git a/libphobos/libdruntime/core/sys/windows/basetsd.d b/libphobos/libdruntime/core/sys/windows/basetsd.d
index 97983c6d74a..3bcac1208ca 100644
--- a/libphobos/libdruntime/core/sys/windows/basetsd.d
+++ b/libphobos/libdruntime/core/sys/windows/basetsd.d
@@ -59,7 +59,7 @@ package mixin template AlignedStr(int alignVal, string name, string memberlist,
mixin( _alignSpec ~ " struct " ~ name ~" { " ~ _alignSpec ~":"~ memberlist~" }" );
}
-version (unittest) {
+version (CoreUnittest) {
private mixin AlignedStr!(16, "_Test_Aligned_Str", q{char a; char b;});
private mixin AlignedStr!(0, "_Test_NoAligned_Str", q{char a; char b;});
}
diff --git a/libphobos/libdruntime/core/sys/windows/dll.d b/libphobos/libdruntime/core/sys/windows/dll.d
index cc2422bcaa0..8e9d7a07fc0 100644
--- a/libphobos/libdruntime/core/sys/windows/dll.d
+++ b/libphobos/libdruntime/core/sys/windows/dll.d
@@ -425,7 +425,6 @@ int dll_getRefCount( HINSTANCE hInstance ) nothrow @nogc
mov peb, RAX;
}
}
-
}
else version (Win32)
{
diff --git a/libphobos/libdruntime/core/sys/windows/sqlext.d b/libphobos/libdruntime/core/sys/windows/sqlext.d
index 3acfc5aa70c..1f891056a82 100644
--- a/libphobos/libdruntime/core/sys/windows/sqlext.d
+++ b/libphobos/libdruntime/core/sys/windows/sqlext.d
@@ -535,7 +535,7 @@ enum SQL_U_UNION_ALL = 2;
enum SQL_UB_OFF = 0UL;
enum SQL_UB_DEFAULT = SQL_UB_OFF;
-enum SQL_UB_ON = 01UL;
+enum SQL_UB_ON = 1UL;
enum SQL_UNION = 96;
enum SQL_UNSEARCHABLE = 0;
diff --git a/libphobos/libdruntime/core/thread/fiber.d b/libphobos/libdruntime/core/thread/fiber.d
index 2f90f179edb..56f6d67d581 100644
--- a/libphobos/libdruntime/core/thread/fiber.d
+++ b/libphobos/libdruntime/core/thread/fiber.d
@@ -1710,7 +1710,7 @@ unittest {
assert( composed.state == Fiber.State.TERM );
}
-version (unittest)
+version (CoreUnittest)
{
class TestFiber : Fiber
{
diff --git a/libphobos/libdruntime/core/thread/osthread.d b/libphobos/libdruntime/core/thread/osthread.d
index 9fcd30e50fb..653adb91a7d 100644
--- a/libphobos/libdruntime/core/thread/osthread.d
+++ b/libphobos/libdruntime/core/thread/osthread.d
@@ -1049,7 +1049,7 @@ class Thread : ThreadBase
}
}
-private Thread toThread(ThreadBase t) @trusted nothrow @nogc pure
+private Thread toThread(return scope ThreadBase t) @trusted nothrow @nogc pure
{
return cast(Thread) cast(void*) t;
}
@@ -1209,6 +1209,18 @@ unittest
thr.join();
}
+// https://issues.dlang.org/show_bug.cgi?id=22124
+unittest
+{
+ Thread thread = new Thread({});
+ auto fun(Thread t, int x)
+ {
+ t.__ctor({x = 3;});
+ return t;
+ }
+ static assert(!__traits(compiles, () @nogc => fun(thread, 3) ));
+}
+
unittest
{
import core.sync.semaphore;
@@ -2212,15 +2224,7 @@ version (Windows)
void append( Throwable t )
{
- if ( obj.m_unhandled is null )
- obj.m_unhandled = t;
- else
- {
- Throwable last = obj.m_unhandled;
- while ( last.next !is null )
- last = last.next;
- last.next = t;
- }
+ obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
}
version (D_InlineAsm_X86)
@@ -2367,15 +2371,7 @@ else version (Posix)
void append( Throwable t )
{
- if ( obj.m_unhandled is null )
- obj.m_unhandled = t;
- else
- {
- Throwable last = obj.m_unhandled;
- while ( last.next !is null )
- last = last.next;
- last.next = t;
- }
+ obj.m_unhandled = Throwable.chainTogether(obj.m_unhandled, t);
}
try
{
diff --git a/libphobos/libdruntime/core/thread/threadbase.d b/libphobos/libdruntime/core/thread/threadbase.d
index 0a8de10e17b..4592bf1d975 100644
--- a/libphobos/libdruntime/core/thread/threadbase.d
+++ b/libphobos/libdruntime/core/thread/threadbase.d
@@ -573,11 +573,9 @@ package(core.thread):
static void initLocks() @nogc
{
- _slock[] = typeid(Mutex).initializer[];
- (cast(Mutex)_slock.ptr).__ctor();
-
- _criticalRegionLock[] = typeid(Mutex).initializer[];
- (cast(Mutex)_criticalRegionLock.ptr).__ctor();
+ import core.lifetime : emplace;
+ emplace!Mutex(_slock[]);
+ emplace!Mutex(_criticalRegionLock[]);
}
static void termLocks() @nogc
@@ -1338,8 +1336,8 @@ package
void initLowlevelThreads() @nogc
{
- ll_lock[] = typeid(Mutex).initializer[];
- lowlevelLock.__ctor();
+ import core.lifetime : emplace;
+ emplace(lowlevelLock());
}
void termLowlevelThreads() @nogc
diff --git a/libphobos/libdruntime/core/time.d b/libphobos/libdruntime/core/time.d
index e7744c85fba..26e515bc8b4 100644
--- a/libphobos/libdruntime/core/time.d
+++ b/libphobos/libdruntime/core/time.d
@@ -22,8 +22,6 @@
system clock ticks, using the highest precision that the system provides.))
$(TR $(TDNW $(LREF MonoTime)) $(TD Represents a monotonic timestamp in
system clock ticks, using the highest precision that the system provides.))
- $(TR $(TDNW $(LREF FracSec)) $(TD Represents fractional seconds
- (portions of time smaller than a second).))
$(LEADINGROW Functions)
$(TR $(TDNW $(LREF convert)) $(TD Generic way of converting between two time
units.))
@@ -40,37 +38,27 @@
$(TR $(TH )
$(TH From $(LREF Duration))
$(TH From $(LREF TickDuration))
- $(TH From $(LREF FracSec))
$(TH From units)
)
$(TR $(TD $(B To $(LREF Duration)))
$(TD -)
$(TD $(D tickDuration.)$(REF_SHORT to, std,conv)$(D !Duration()))
- $(TD -)
$(TD $(D dur!"msecs"(5)) or $(D 5.msecs()))
)
$(TR $(TD $(B To $(LREF TickDuration)))
$(TD $(D duration.)$(REF_SHORT to, std,conv)$(D !TickDuration()))
$(TD -)
- $(TD -)
$(TD $(D TickDuration.from!"msecs"(msecs)))
)
- $(TR $(TD $(B To $(LREF FracSec)))
- $(TD $(D duration.fracSec))
- $(TD -)
- $(TD -)
- $(TD $(D FracSec.from!"msecs"(msecs)))
- )
$(TR $(TD $(B To units))
$(TD $(D duration.total!"days"))
$(TD $(D tickDuration.msecs))
- $(TD $(D fracSec.msecs))
$(TD $(D convert!("days", "msecs")(msecs)))
))
Copyright: Copyright 2010 - 2012
- License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: Jonathan M Davis and Kato Shoichi
+ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) and Kato Shoichi
Source: $(DRUNTIMESRC core/_time.d)
Macros:
NBSP=&nbsp;
@@ -80,7 +68,6 @@ module core.time;
import core.exception;
import core.stdc.time;
import core.stdc.stdio;
-import core.internal.traits : _Unqual = Unqual;
import core.internal.string;
version (Windows)
@@ -128,13 +115,6 @@ ulong mach_absolute_time();
}
-//To verify that an lvalue isn't required.
-version (unittest) private T copy(T)(T t)
-{
- return t;
-}
-
-
/++
What type of clock to use with $(LREF MonoTime) / $(LREF MonoTimeImpl) or
$(D std.datetime.Clock.currTime). They default to $(D ClockType.normal),
@@ -181,7 +161,7 @@ version (CoreDdoc) enum ClockType
On systems which do not support a coarser clock,
$(D MonoTimeImpl!(ClockType.coarse)) will internally use the same clock
- as $(D Monotime) does, and $(D Clock.currTime!(ClockType.coarse)) will
+ as $(D MonoTime) does, and $(D Clock.currTime!(ClockType.coarse)) will
use the same clock as $(D Clock.currTime). This is because the coarse
clock is doing the same thing as the normal clock (just at lower
precision), whereas some of the other clock types
@@ -536,7 +516,7 @@ public:
+/
static @property nothrow @nogc Duration min() { return Duration(long.min); }
- unittest
+ version (CoreUnittest) unittest
{
assert(zero == dur!"seconds"(0));
assert(Duration.max == Duration(long.max));
@@ -561,30 +541,31 @@ public:
+/
int opCmp(Duration rhs) const nothrow @nogc
{
- if (_hnsecs < rhs._hnsecs)
- return -1;
- if (_hnsecs > rhs._hnsecs)
- return 1;
- return 0;
+ return (_hnsecs > rhs._hnsecs) - (_hnsecs < rhs._hnsecs);
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (T; _TypeTuple!(Duration, const Duration, immutable Duration))
+ import core.internal.traits : rvalueOf;
+ foreach (T; AliasSeq!(Duration, const Duration, immutable Duration))
{
- foreach (U; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (U; AliasSeq!(Duration, const Duration, immutable Duration))
{
T t = 42;
- U u = t;
+ // workaround https://issues.dlang.org/show_bug.cgi?id=18296
+ version (D_Coverage)
+ U u = T(t._hnsecs);
+ else
+ U u = t;
assert(t == u);
- assert(copy(t) == u);
- assert(t == copy(u));
+ assert(rvalueOf(t) == u);
+ assert(t == rvalueOf(u));
}
}
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
- foreach (E; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (E; AliasSeq!(Duration, const Duration, immutable Duration))
{
assert((cast(D)Duration(12)).opCmp(cast(E)Duration(12)) == 0);
assert((cast(D)Duration(-12)).opCmp(cast(E)Duration(-12)) == 0);
@@ -595,23 +576,23 @@ public:
assert((cast(D)Duration(12)).opCmp(cast(E)Duration(10)) > 0);
assert((cast(D)Duration(12)).opCmp(cast(E)Duration(-12)) > 0);
- assert(copy(cast(D)Duration(12)).opCmp(cast(E)Duration(12)) == 0);
- assert(copy(cast(D)Duration(-12)).opCmp(cast(E)Duration(-12)) == 0);
+ assert(rvalueOf(cast(D)Duration(12)).opCmp(cast(E)Duration(12)) == 0);
+ assert(rvalueOf(cast(D)Duration(-12)).opCmp(cast(E)Duration(-12)) == 0);
- assert(copy(cast(D)Duration(10)).opCmp(cast(E)Duration(12)) < 0);
- assert(copy(cast(D)Duration(-12)).opCmp(cast(E)Duration(12)) < 0);
+ assert(rvalueOf(cast(D)Duration(10)).opCmp(cast(E)Duration(12)) < 0);
+ assert(rvalueOf(cast(D)Duration(-12)).opCmp(cast(E)Duration(12)) < 0);
- assert(copy(cast(D)Duration(12)).opCmp(cast(E)Duration(10)) > 0);
- assert(copy(cast(D)Duration(12)).opCmp(cast(E)Duration(-12)) > 0);
+ assert(rvalueOf(cast(D)Duration(12)).opCmp(cast(E)Duration(10)) > 0);
+ assert(rvalueOf(cast(D)Duration(12)).opCmp(cast(E)Duration(-12)) > 0);
- assert((cast(D)Duration(12)).opCmp(copy(cast(E)Duration(12))) == 0);
- assert((cast(D)Duration(-12)).opCmp(copy(cast(E)Duration(-12))) == 0);
+ assert((cast(D)Duration(12)).opCmp(rvalueOf(cast(E)Duration(12))) == 0);
+ assert((cast(D)Duration(-12)).opCmp(rvalueOf(cast(E)Duration(-12))) == 0);
- assert((cast(D)Duration(10)).opCmp(copy(cast(E)Duration(12))) < 0);
- assert((cast(D)Duration(-12)).opCmp(copy(cast(E)Duration(12))) < 0);
+ assert((cast(D)Duration(10)).opCmp(rvalueOf(cast(E)Duration(12))) < 0);
+ assert((cast(D)Duration(-12)).opCmp(rvalueOf(cast(E)Duration(12))) < 0);
- assert((cast(D)Duration(12)).opCmp(copy(cast(E)Duration(10))) > 0);
- assert((cast(D)Duration(12)).opCmp(copy(cast(E)Duration(-12))) > 0);
+ assert((cast(D)Duration(12)).opCmp(rvalueOf(cast(E)Duration(10))) > 0);
+ assert((cast(D)Duration(12)).opCmp(rvalueOf(cast(E)Duration(-12))) > 0);
}
}
}
@@ -634,20 +615,20 @@ public:
rhs = The duration to add to or subtract from this $(D Duration).
+/
Duration opBinary(string op, D)(D rhs) const nothrow @nogc
- if (((op == "+" || op == "-" || op == "%") && is(_Unqual!D == Duration)) ||
- ((op == "+" || op == "-") && is(_Unqual!D == TickDuration)))
+ if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) ||
+ ((op == "+" || op == "-") && is(immutable D == immutable TickDuration)))
{
- static if (is(_Unqual!D == Duration))
+ static if (is(immutable D == immutable Duration))
return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs"));
- else if (is(_Unqual!D == TickDuration))
+ else
return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs"));
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
- foreach (E; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (E; AliasSeq!(Duration, const Duration, immutable Duration))
{
assert((cast(D)Duration(5)) + (cast(E)Duration(7)) == Duration(12));
assert((cast(D)Duration(5)) - (cast(E)Duration(7)) == Duration(-2));
@@ -678,7 +659,7 @@ public:
assert((cast(D)Duration(-7)) % (cast(E)Duration(5)) == Duration(-2));
}
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
assertApprox((cast(D)Duration(5)) + cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80));
assertApprox((cast(D)Duration(5)) - cast(T)TickDuration.from!"usecs"(7), Duration(-70), Duration(-60));
@@ -720,16 +701,16 @@ public:
+/
Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc
if ((op == "+" || op == "-") &&
- is(_Unqual!D == TickDuration))
+ is(immutable D == immutable TickDuration))
{
return Duration(mixin("lhs.hnsecs " ~ op ~ " _hnsecs"));
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
assertApprox((cast(T)TickDuration.from!"usecs"(7)) + cast(D)Duration(5), Duration(70), Duration(80));
assertApprox((cast(T)TickDuration.from!"usecs"(7)) - cast(D)Duration(5), Duration(60), Duration(70));
@@ -772,18 +753,18 @@ public:
Params:
rhs = The duration to add to or subtract from this $(D Duration).
+/
- ref Duration opOpAssign(string op, D)(in D rhs) nothrow @nogc
- if (((op == "+" || op == "-" || op == "%") && is(_Unqual!D == Duration)) ||
- ((op == "+" || op == "-") && is(_Unqual!D == TickDuration)))
+ ref Duration opOpAssign(string op, D)(const scope D rhs) nothrow @nogc
+ if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) ||
+ ((op == "+" || op == "-") && is(immutable D == immutable TickDuration)))
{
- static if (is(_Unqual!D == Duration))
+ static if (is(immutable D == immutable Duration))
mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;");
- else if (is(_Unqual!D == TickDuration))
+ else
mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;");
return this;
}
- unittest
+ version (CoreUnittest) unittest
{
static void test1(string op, E)(Duration actual, in E rhs, Duration expected, size_t line = __LINE__)
{
@@ -801,7 +782,7 @@ public:
assertApprox(actual, lower, upper, "op assign failed", line);
}
- foreach (E; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (E; AliasSeq!(Duration, const Duration, immutable Duration))
{
test1!"+="(Duration(5), (cast(E)Duration(7)), Duration(12));
test1!"-="(Duration(5), (cast(E)Duration(7)), Duration(-2));
@@ -832,7 +813,7 @@ public:
test1!"%="(Duration(-7), (cast(E)Duration(-5)), Duration(-2));
}
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
test2!"+="(Duration(5), cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80));
test2!"-="(Duration(5), cast(T)TickDuration.from!"usecs"(7), Duration(-70), Duration(-60));
@@ -855,9 +836,9 @@ public:
test2!"-="(Duration(-7), cast(T)TickDuration.from!"usecs"(-5), Duration(38), Duration(48));
}
- foreach (D; _TypeTuple!(const Duration, immutable Duration))
+ foreach (D; AliasSeq!(const Duration, immutable Duration))
{
- foreach (E; _TypeTuple!(Duration, const Duration, immutable Duration,
+ foreach (E; AliasSeq!(Duration, const Duration, immutable Duration,
TickDuration, const TickDuration, immutable TickDuration))
{
D lhs = D(120);
@@ -888,9 +869,9 @@ public:
mixin("return Duration(_hnsecs " ~ op ~ " value);");
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
assert((cast(D)Duration(5)) * 7 == Duration(35));
assert((cast(D)Duration(7)) * 5 == Duration(35));
@@ -909,9 +890,9 @@ public:
}
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
assert((cast(D)Duration(5)) / 7 == Duration(0));
assert((cast(D)Duration(7)) / 5 == Duration(1));
@@ -950,7 +931,7 @@ public:
return this;
}
- unittest
+ version (CoreUnittest) unittest
{
static void test(D)(D actual, long value, Duration expected, size_t line = __LINE__)
{
@@ -982,7 +963,7 @@ public:
static assert(!__traits(compiles, idur *= 12));
}
- unittest
+ version (CoreUnittest) unittest
{
static void test(Duration actual, long value, Duration expected, size_t line = __LINE__)
{
@@ -1030,7 +1011,7 @@ public:
return _hnsecs / rhs._hnsecs;
}
- unittest
+ version (CoreUnittest) unittest
{
assert(Duration(5) / Duration(7) == 0);
assert(Duration(7) / Duration(5) == 1);
@@ -1069,9 +1050,9 @@ public:
return opBinary!op(value);
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
assert(5 * cast(D)Duration(7) == Duration(35));
assert(7 * cast(D)Duration(5) == Duration(35));
@@ -1100,9 +1081,9 @@ public:
return Duration(-_hnsecs);
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
assert(-(cast(D)Duration(7)) == Duration(-7));
assert(-(cast(D)Duration(5)) == Duration(-5));
@@ -1121,22 +1102,22 @@ public:
$(D duration.to!TickDuration())
+/
TickDuration opCast(T)() const nothrow @nogc
- if (is(_Unqual!T == TickDuration))
+ if (is(immutable T == immutable TickDuration))
{
return TickDuration.from!"hnsecs"(_hnsecs);
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
- foreach (units; _TypeTuple!("seconds", "msecs", "usecs", "hnsecs"))
+ foreach (units; AliasSeq!("seconds", "msecs", "usecs", "hnsecs"))
{
enum unitsPerSec = convert!("seconds", units)(1);
if (TickDuration.ticksPerSec >= unitsPerSec)
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
auto t = TickDuration.from!units(1);
assertApprox(cast(T)cast(D)dur!units(1), t - TickDuration(1), t + TickDuration(1), units);
@@ -1165,7 +1146,7 @@ public:
return _hnsecs != 0;
}
- unittest
+ version (CoreUnittest) unittest
{
auto d = 10.minutes;
assert(d);
@@ -1175,7 +1156,7 @@ public:
//Temporary hack until bug http://d.puremagic.com/issues/show_bug.cgi?id=5747 is fixed.
Duration opCast(T)() const nothrow @nogc
- if (is(_Unqual!T == Duration))
+ if (is(immutable T == immutable Duration))
{
return this;
}
@@ -1299,13 +1280,13 @@ public:
enum allAreMutableIntegralTypes = allAreMutableIntegralTypes!(Args[1 .. $]);
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (T; _TypeTuple!(long, int, short, byte, ulong, uint, ushort, ubyte))
+ foreach (T; AliasSeq!(long, int, short, byte, ulong, uint, ushort, ubyte))
static assert(allAreMutableIntegralTypes!T);
- foreach (T; _TypeTuple!(long, int, short, byte, ulong, uint, ushort, ubyte))
+ foreach (T; AliasSeq!(long, int, short, byte, ulong, uint, ushort, ubyte))
static assert(!allAreMutableIntegralTypes!(const T));
- foreach (T; _TypeTuple!(char, wchar, dchar, float, double, real, string))
+ foreach (T; AliasSeq!(char, wchar, dchar, float, double, real, string))
static assert(!allAreMutableIntegralTypes!T);
static assert(allAreMutableIntegralTypes!(long, int, short, byte));
static assert(!allAreMutableIntegralTypes!(long, int, short, char, byte));
@@ -1366,9 +1347,9 @@ public:
}
}
- pure nothrow unittest
+ version (CoreUnittest) pure nothrow unittest
{
- foreach (D; _TypeTuple!(const Duration, immutable Duration))
+ foreach (D; AliasSeq!(const Duration, immutable Duration))
{
D d = dur!"weeks"(3) + dur!"days"(5) + dur!"hours"(19) + dur!"minutes"(7) +
dur!"seconds"(2) + dur!"hnsecs"(1234567);
@@ -1474,7 +1455,7 @@ public:
static assert(!is(typeof(d.split("hnsecs", "seconds", "msecs")())));
static assert(!is(typeof(d.split("seconds", "hnecs", "msecs")())));
static assert(!is(typeof(d.split("seconds", "msecs", "msecs")())));
- alias _TypeTuple!("nsecs", "hnsecs", "usecs", "msecs", "seconds",
+ alias AliasSeq!("nsecs", "hnsecs", "usecs", "msecs", "seconds",
"minutes", "hours", "days", "weeks") timeStrs;
foreach (i, str; timeStrs[1 .. $])
static assert(!is(typeof(d.split!(timeStrs[i - 1], str)())));
@@ -1521,10 +1502,7 @@ public:
units == "hnsecs" ||
units == "nsecs")
{
- static if (units == "nsecs")
- return convert!("hnsecs", "nsecs")(_hnsecs);
- else
- return getUnitsFromHNSecs!units(_hnsecs);
+ return convert!("hnsecs", units)(_hnsecs);
}
///
@@ -1543,9 +1521,9 @@ public:
assert(dur!"nsecs"(2007).total!"nsecs" == 2000);
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(const Duration, immutable Duration))
+ foreach (D; AliasSeq!(const Duration, immutable Duration))
{
assert((cast(D)dur!"weeks"(12)).total!"weeks" == 12);
assert((cast(D)dur!"weeks"(12)).total!"days" == 84);
@@ -1598,7 +1576,7 @@ public:
unit = "μs";
else
unit = plural ? units : units[0 .. $-1];
- res ~= signedToTempString(val, 10);
+ res ~= signedToTempString(val);
res ~= " ";
res ~= unit;
}
@@ -1649,9 +1627,9 @@ public:
assert(usecs(-5239492).toString() == "-5 secs, -239 ms, and -492 μs");
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
assert((cast(D)Duration(0)).toString() == "0 hnsecs");
assert((cast(D)Duration(1)).toString() == "1 hnsec");
@@ -1707,9 +1685,9 @@ public:
return _hnsecs < 0;
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
assert(!(cast(D)Duration(100)).isNegative);
assert(!(cast(D)Duration(1)).isNegative);
@@ -1766,7 +1744,7 @@ unittest
td = The TickDuration to convert
+/
T to(string units, T, D)(D td) @safe pure nothrow @nogc
- if (is(_Unqual!D == TickDuration) &&
+ if (is(immutable D == immutable TickDuration) &&
(units == "seconds" ||
units == "msecs" ||
units == "usecs" ||
@@ -1803,8 +1781,9 @@ unittest
long tl = to!("seconds",long)(t);
assert(tl == 1000);
+ import core.stdc.math : fabs;
double td = to!("seconds",double)(t);
- assert(_abs(td - 1000) < 0.001);
+ assert(fabs(td - 1000) < 0.001);
}
unittest
@@ -1819,12 +1798,12 @@ unittest
auto _str(F)(F val)
{
static if (is(F == int) || is(F == long))
- return signedToTempString(val, 10);
+ return signedToTempString(val);
else
- return unsignedToTempString(val, 10);
+ return unsignedToTempString(val);
}
- foreach (F; _TypeTuple!(int,uint,long,ulong,float,double,real))
+ foreach (F; AliasSeq!(int,uint,long,ulong,float,double,real))
{
F t1f = to!(U,F)(t1);
F t2f = to!(U,F)(t2);
@@ -1950,7 +1929,7 @@ unittest
unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
assert(dur!"weeks"(7).total!"weeks" == 7);
assert(dur!"days"(7).total!"days" == 7);
@@ -2093,7 +2072,7 @@ struct MonoTimeImpl(ClockType clockType)
static assert(0, "Unsupported platform");
// POD value, test mutable/const/immutable conversion
- unittest
+ version (CoreUnittest) unittest
{
MonoTimeImpl m;
const MonoTimeImpl cm = m;
@@ -2130,26 +2109,26 @@ struct MonoTimeImpl(ClockType clockType)
version (Windows)
{
- long ticks;
- if (QueryPerformanceCounter(&ticks) == 0)
- {
- // This probably cannot happen on Windows 95 or later
- import core.internal.abort : abort;
- abort("Call to QueryPerformanceCounter failed.");
- }
+ long ticks = void;
+ QueryPerformanceCounter(&ticks);
return MonoTimeImpl(ticks);
}
else version (Darwin)
return MonoTimeImpl(mach_absolute_time());
else version (Posix)
{
- timespec ts;
- if (clock_gettime(clockArg, &ts) != 0)
+ timespec ts = void;
+ immutable error = clock_gettime(clockArg, &ts);
+ // clockArg is supported and if tv_sec is long or larger
+ // overflow won't happen before 292 billion years A.D.
+ static if (ts.tv_sec.max < long.max)
{
- import core.internal.abort : abort;
- abort("Call to clock_gettime failed.");
+ if (error)
+ {
+ import core.internal.abort : abort;
+ abort("Call to clock_gettime failed.");
+ }
}
-
return MonoTimeImpl(convClockFreq(ts.tv_sec * 1_000_000_000L + ts.tv_nsec,
1_000_000_000L,
ticksPerSecond));
@@ -2176,7 +2155,7 @@ struct MonoTimeImpl(ClockType clockType)
MonoTimeImpl min() { return MonoTimeImpl(long.min); }
}
- unittest
+ version (CoreUnittest) unittest
{
assert(MonoTimeImpl.zero == MonoTimeImpl(0));
assert(MonoTimeImpl.max == MonoTimeImpl(long.max));
@@ -2199,28 +2178,28 @@ struct MonoTimeImpl(ClockType clockType)
+/
int opCmp(MonoTimeImpl rhs) const pure nothrow @nogc
{
- if (_ticks < rhs._ticks)
- return -1;
- return _ticks > rhs._ticks ? 1 : 0;
+ return (_ticks > rhs._ticks) - (_ticks < rhs._ticks);
}
- unittest
+ version (CoreUnittest) unittest
{
+ import core.internal.traits : rvalueOf;
const t = MonoTimeImpl.currTime;
- assert(t == copy(t));
+ assert(t == rvalueOf(t));
}
- unittest
+ version (CoreUnittest) unittest
{
+ import core.internal.traits : rvalueOf;
const before = MonoTimeImpl.currTime;
auto after = MonoTimeImpl(before._ticks + 42);
assert(before < after);
- assert(copy(before) <= before);
- assert(copy(after) > before);
- assert(after >= copy(after));
+ assert(rvalueOf(before) <= before);
+ assert(rvalueOf(after) > before);
+ assert(after >= rvalueOf(after));
}
- unittest
+ version (CoreUnittest) unittest
{
const currTime = MonoTimeImpl.currTime;
assert(MonoTimeImpl(long.max) > MonoTimeImpl(0));
@@ -2274,16 +2253,17 @@ assert(before + timeElapsed == after);
return Duration(convClockFreq(diff , ticksPerSecond, hnsecsPer!"seconds"));
}
- unittest
+ version (CoreUnittest) unittest
{
+ import core.internal.traits : rvalueOf;
const t = MonoTimeImpl.currTime;
- assert(t - copy(t) == Duration.zero);
+ assert(t - rvalueOf(t) == Duration.zero);
static assert(!__traits(compiles, t + t));
}
- unittest
+ version (CoreUnittest) unittest
{
- static void test(in MonoTimeImpl before, in MonoTimeImpl after, in Duration min)
+ static void test(const scope MonoTimeImpl before, const scope MonoTimeImpl after, const scope Duration min)
{
immutable diff = after - before;
assert(diff >= min);
@@ -2312,14 +2292,14 @@ assert(before + timeElapsed == after);
mixin("return MonoTimeImpl(_ticks " ~ op ~ " rhsConverted);");
}
- unittest
+ version (CoreUnittest) unittest
{
const t = MonoTimeImpl.currTime;
assert(t + Duration(0) == t);
assert(t - Duration(0) == t);
}
- unittest
+ version (CoreUnittest) unittest
{
const t = MonoTimeImpl.currTime;
@@ -2345,7 +2325,7 @@ assert(before + timeElapsed == after);
return this;
}
- unittest
+ version (CoreUnittest) unittest
{
auto mt = MonoTimeImpl.currTime;
const initial = mt;
@@ -2386,7 +2366,7 @@ assert(before + timeElapsed == after);
return _ticks;
}
- unittest
+ version (CoreUnittest) unittest
{
const mt = MonoTimeImpl.currTime;
assert(mt.ticks == mt._ticks);
@@ -2405,7 +2385,7 @@ assert(before + timeElapsed == after);
return _ticksPerSecond[_clockIdx];
}
- unittest
+ version (CoreUnittest) unittest
{
assert(MonoTimeImpl.ticksPerSecond == _ticksPerSecond[_clockIdx]);
}
@@ -2415,15 +2395,15 @@ assert(before + timeElapsed == after);
string toString() const pure nothrow
{
static if (clockType == ClockType.normal)
- return "MonoTime(" ~ signedToTempString(_ticks, 10) ~ " ticks, " ~ signedToTempString(ticksPerSecond, 10) ~ " ticks per second)";
+ return "MonoTime(" ~ signedToTempString(_ticks) ~ " ticks, " ~ signedToTempString(ticksPerSecond) ~ " ticks per second)";
else
- return "MonoTimeImpl!(ClockType." ~ _clockName ~ ")(" ~ signedToTempString(_ticks, 10) ~ " ticks, " ~
- signedToTempString(ticksPerSecond, 10) ~ " ticks per second)";
+ return "MonoTimeImpl!(ClockType." ~ _clockName ~ ")(" ~ signedToTempString(_ticks) ~ " ticks, " ~
+ signedToTempString(ticksPerSecond) ~ " ticks per second)";
}
- unittest
+ version (CoreUnittest) unittest
{
- static min(T)(T a, T b) { return a < b ? a : b; }
+ import core.internal.util.math : min;
static void eat(ref string s, const(char)[] exp)
{
@@ -2438,9 +2418,9 @@ assert(before + timeElapsed == after);
else
eat(str, "MonoTimeImpl!(ClockType."~_clockName~")(");
- eat(str, signedToTempString(mt._ticks, 10));
+ eat(str, signedToTempString(mt._ticks));
eat(str, " ticks, ");
- eat(str, signedToTempString(ticksPerSecond, 10));
+ eat(str, signedToTempString(ticksPerSecond));
eat(str, " ticks per second)");
}
@@ -2448,7 +2428,7 @@ private:
// static immutable long _ticksPerSecond;
- unittest
+ version (CoreUnittest) unittest
{
assert(_ticksPerSecond[_clockIdx]);
}
@@ -2784,8 +2764,7 @@ struct TickDuration
{
/++
It's the same as $(D TickDuration(0)), but it's provided to be
- consistent with $(D Duration) and $(D FracSec), which provide $(D zero)
- properties.
+ consistent with $(D Duration), which provides a $(D zero) property.
+/
TickDuration zero() { return TickDuration(0); }
@@ -2800,7 +2779,7 @@ struct TickDuration
TickDuration min() { return TickDuration(long.min); }
}
- unittest
+ version (CoreUnittest) unittest
{
assert(zero == TickDuration(0));
assert(TickDuration.max == TickDuration(long.max));
@@ -2851,7 +2830,7 @@ struct TickDuration
appOrigin = TickDuration.currSystemTick;
}
- unittest
+ version (CoreUnittest) unittest
{
assert(ticksPerSec);
}
@@ -2874,9 +2853,9 @@ struct TickDuration
return this.to!("seconds", long)();
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
assert((cast(T)TickDuration(ticksPerSec)).seconds == 1);
assert((cast(T)TickDuration(ticksPerSec - 1)).seconds == 0);
@@ -2945,11 +2924,11 @@ struct TickDuration
return TickDuration(cast(long)(length * (ticksPerSec / cast(real)unitsPerSec)));
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (units; _TypeTuple!("seconds", "msecs", "usecs", "nsecs"))
+ foreach (units; AliasSeq!("seconds", "msecs", "usecs", "nsecs"))
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
assertApprox((cast(T)TickDuration.from!units(1000)).to!(units, long)(),
500, 1500, units);
@@ -2970,21 +2949,21 @@ struct TickDuration
$(D tickDuration.to!Duration())
+/
Duration opCast(T)() @safe const pure nothrow @nogc
- if (is(_Unqual!T == Duration))
+ if (is(immutable T == immutable Duration))
{
return Duration(hnsecs);
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (D; _TypeTuple!(Duration, const Duration, immutable Duration))
+ foreach (D; AliasSeq!(Duration, const Duration, immutable Duration))
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
auto expected = dur!"seconds"(1);
assert(cast(D)cast(T)TickDuration.from!"seconds"(1) == expected);
- foreach (units; _TypeTuple!("msecs", "usecs", "hnsecs"))
+ foreach (units; AliasSeq!("msecs", "usecs", "hnsecs"))
{
D actual = cast(D)cast(T)TickDuration.from!units(1_000_000);
assertApprox(actual, dur!units(900_000), dur!units(1_100_000));
@@ -2996,7 +2975,7 @@ struct TickDuration
//Temporary hack until bug http://d.puremagic.com/issues/show_bug.cgi?id=5747 is fixed.
TickDuration opCast(T)() @safe const pure nothrow @nogc
- if (is(_Unqual!T == TickDuration))
+ if (is(immutable T == immutable TickDuration))
{
return this;
}
@@ -3025,9 +3004,9 @@ struct TickDuration
return this;
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
auto a = TickDuration.currSystemTick;
auto result = a += cast(T)TickDuration.currSystemTick;
@@ -3039,7 +3018,7 @@ struct TickDuration
assert(b == result);
assert(b.to!("seconds", real)() <= 0);
- foreach (U; _TypeTuple!(const TickDuration, immutable TickDuration))
+ foreach (U; AliasSeq!(const TickDuration, immutable TickDuration))
{
U u = TickDuration(12);
static assert(!__traits(compiles, u += cast(T)TickDuration.currSystemTick));
@@ -3070,13 +3049,13 @@ struct TickDuration
return TickDuration(mixin("length " ~ op ~ " rhs.length"));
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
T a = TickDuration.currSystemTick;
T b = TickDuration.currSystemTick;
- assert((a + b).seconds > 0);
+ assert((a + b).usecs > 0);
assert((a - b).seconds <= 0);
}
}
@@ -3091,9 +3070,9 @@ struct TickDuration
return TickDuration(-length);
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
assert(-(cast(T)TickDuration(7)) == TickDuration(-7));
assert(-(cast(T)TickDuration(5)) == TickDuration(-5));
@@ -3109,26 +3088,27 @@ struct TickDuration
+/
int opCmp(TickDuration rhs) @safe const pure nothrow @nogc
{
- return length < rhs.length ? -1 : (length == rhs.length ? 0 : 1);
+ return (length > rhs.length) - (length < rhs.length);
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ import core.internal.traits : rvalueOf;
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
- foreach (U; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (U; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
T t = TickDuration.currSystemTick;
U u = t;
assert(t == u);
- assert(copy(t) == u);
- assert(t == copy(u));
+ assert(rvalueOf(t) == u);
+ assert(t == rvalueOf(u));
}
}
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
- foreach (U; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (U; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
T t = TickDuration.currSystemTick;
U u = t + t;
@@ -3137,15 +3117,15 @@ struct TickDuration
assert(u > t);
assert(u >= u);
- assert(copy(t) < u);
- assert(copy(t) <= t);
- assert(copy(u) > t);
- assert(copy(u) >= u);
+ assert(rvalueOf(t) < u);
+ assert(rvalueOf(t) <= t);
+ assert(rvalueOf(u) > t);
+ assert(rvalueOf(u) >= u);
- assert(t < copy(u));
- assert(t <= copy(t));
- assert(u > copy(t));
- assert(u >= copy(u));
+ assert(t < rvalueOf(u));
+ assert(t <= rvalueOf(t));
+ assert(u > rvalueOf(t));
+ assert(u >= rvalueOf(u));
}
}
}
@@ -3170,7 +3150,7 @@ struct TickDuration
length = cast(long)(length * value);
}
- unittest
+ version (CoreUnittest) unittest
{
immutable curr = TickDuration.currSystemTick;
TickDuration t1 = curr;
@@ -3187,7 +3167,7 @@ struct TickDuration
t1 *= 2.1;
assert(t1 > t2);
- foreach (T; _TypeTuple!(const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(const TickDuration, immutable TickDuration))
{
T t = TickDuration.currSystemTick;
assert(!__traits(compiles, t *= 12));
@@ -3221,7 +3201,7 @@ struct TickDuration
length = cast(long)(length / value);
}
- unittest
+ version (CoreUnittest) unittest
{
immutable curr = TickDuration.currSystemTick;
immutable t1 = curr;
@@ -3240,7 +3220,7 @@ struct TickDuration
_assertThrown!TimeException(t2 /= 0);
- foreach (T; _TypeTuple!(const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(const TickDuration, immutable TickDuration))
{
T t = TickDuration.currSystemTick;
assert(!__traits(compiles, t /= 12));
@@ -3268,9 +3248,9 @@ struct TickDuration
return TickDuration(cast(long)(length * value));
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
T t1 = TickDuration.currSystemTick;
T t2 = t1 + t1;
@@ -3307,9 +3287,9 @@ struct TickDuration
return TickDuration(cast(long)(length / value));
}
- unittest
+ version (CoreUnittest) unittest
{
- foreach (T; _TypeTuple!(TickDuration, const TickDuration, immutable TickDuration))
+ foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration))
{
T t1 = TickDuration.currSystemTick;
T t2 = t1 + t1;
@@ -3332,7 +3312,7 @@ struct TickDuration
this.length = ticks;
}
- unittest
+ version (CoreUnittest) unittest
{
foreach (i; [-42, 0, 42])
assert(TickDuration(i).length == i);
@@ -3369,10 +3349,8 @@ struct TickDuration
import core.internal.abort : abort;
version (Windows)
{
- ulong ticks;
- if (QueryPerformanceCounter(cast(long*)&ticks) == 0)
- abort("Failed in QueryPerformanceCounter().");
-
+ ulong ticks = void;
+ QueryPerformanceCounter(cast(long*)&ticks);
return TickDuration(ticks);
}
else version (Darwin)
@@ -3381,10 +3359,8 @@ struct TickDuration
return TickDuration(cast(long)mach_absolute_time());
else
{
- timeval tv;
- if (gettimeofday(&tv, null) != 0)
- abort("Failed in gettimeofday().");
-
+ timeval tv = void;
+ gettimeofday(&tv, null);
return TickDuration(tv.tv_sec * TickDuration.ticksPerSec +
tv.tv_usec * TickDuration.ticksPerSec / 1000 / 1000);
}
@@ -3393,26 +3369,32 @@ struct TickDuration
{
static if (is(typeof(clock_gettime)))
{
- timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
- abort("Failed in clock_gettime().");
-
+ timespec ts = void;
+ immutable error = clock_gettime(CLOCK_MONOTONIC, &ts);
+ // CLOCK_MONOTONIC is supported and if tv_sec is long or larger
+ // overflow won't happen before 292 billion years A.D.
+ static if (ts.tv_sec.max < long.max)
+ {
+ if (error)
+ {
+ import core.internal.abort : abort;
+ abort("Call to clock_gettime failed.");
+ }
+ }
return TickDuration(ts.tv_sec * TickDuration.ticksPerSec +
ts.tv_nsec * TickDuration.ticksPerSec / 1000 / 1000 / 1000);
}
else
{
- timeval tv;
- if (gettimeofday(&tv, null) != 0)
- abort("Failed in gettimeofday().");
-
+ timeval tv = void;
+ gettimeofday(&tv, null);
return TickDuration(tv.tv_sec * TickDuration.ticksPerSec +
tv.tv_usec * TickDuration.ticksPerSec / 1000 / 1000);
}
}
}
- @safe nothrow unittest
+ version (CoreUnittest) @safe nothrow unittest
{
assert(TickDuration.currSystemTick.length > 0);
}
@@ -3500,13 +3482,13 @@ unittest
unittest
{
- foreach (units; _TypeTuple!("weeks", "days", "hours", "seconds", "msecs", "usecs", "hnsecs", "nsecs"))
+ foreach (units; AliasSeq!("weeks", "days", "hours", "seconds", "msecs", "usecs", "hnsecs", "nsecs"))
{
static assert(!__traits(compiles, convert!("years", units)(12)), units);
static assert(!__traits(compiles, convert!(units, "years")(12)), units);
}
- foreach (units; _TypeTuple!("years", "months", "weeks", "days",
+ foreach (units; AliasSeq!("years", "months", "weeks", "days",
"hours", "seconds", "msecs", "usecs", "hnsecs", "nsecs"))
{
assert(convert!(units, units)(12) == 12);
@@ -3568,612 +3550,6 @@ unittest
assert(convert!("nsecs", "hnsecs")(100) == 1);
}
-
-/++
- Represents fractional seconds.
-
- This is the portion of the time which is smaller than a second and it cannot
- hold values which would be greater than or equal to a second (or less than
- or equal to a negative second).
-
- It holds hnsecs internally, but you can create it using either milliseconds,
- microseconds, or hnsecs. What it does is allow for a simple way to set or
- adjust the fractional seconds portion of a $(D Duration) or a
- $(REF SysTime, std,datetime) without having to worry about whether you're
- dealing with milliseconds, microseconds, or hnsecs.
-
- $(D FracSec)'s functions which take time unit strings do accept
- $(D "nsecs"), but because the resolution of $(D Duration) and
- $(REF SysTime, std,datetime) is hnsecs, you don't actually get precision higher
- than hnsecs. $(D "nsecs") is accepted merely for convenience. Any values
- given as nsecs will be converted to hnsecs using $(D convert) (which uses
- truncating division when converting to smaller units).
- +/
-struct FracSec
-{
-@safe pure:
-
-public:
-
- /++
- A $(D FracSec) of $(D 0). It's shorter than doing something like
- $(D FracSec.from!"msecs"(0)) and more explicit than $(D FracSec.init).
- +/
- static @property nothrow @nogc FracSec zero() { return FracSec(0); }
-
- unittest
- {
- assert(zero == FracSec.from!"msecs"(0));
- }
-
-
- /++
- Create a $(D FracSec) from the given units ($(D "msecs"), $(D "usecs"),
- or $(D "hnsecs")).
-
- Params:
- units = The units to create a FracSec from.
- value = The number of the given units passed the second.
-
- Throws:
- $(D TimeException) if the given value would result in a $(D FracSec)
- greater than or equal to $(D 1) second or less than or equal to
- $(D -1) seconds.
- +/
- static FracSec from(string units)(long value)
- if (units == "msecs" ||
- units == "usecs" ||
- units == "hnsecs" ||
- units == "nsecs")
- {
- immutable hnsecs = cast(int)convert!(units, "hnsecs")(value);
- _enforceValid(hnsecs);
- return FracSec(hnsecs);
- }
-
- unittest
- {
- assert(FracSec.from!"msecs"(0) == FracSec(0));
- assert(FracSec.from!"usecs"(0) == FracSec(0));
- assert(FracSec.from!"hnsecs"(0) == FracSec(0));
-
- foreach (sign; [1, -1])
- {
- _assertThrown!TimeException(from!"msecs"(1000 * sign));
-
- assert(FracSec.from!"msecs"(1 * sign) == FracSec(10_000 * sign));
- assert(FracSec.from!"msecs"(999 * sign) == FracSec(9_990_000 * sign));
-
- _assertThrown!TimeException(from!"usecs"(1_000_000 * sign));
-
- assert(FracSec.from!"usecs"(1 * sign) == FracSec(10 * sign));
- assert(FracSec.from!"usecs"(999 * sign) == FracSec(9990 * sign));
- assert(FracSec.from!"usecs"(999_999 * sign) == FracSec(9999_990 * sign));
-
- _assertThrown!TimeException(from!"hnsecs"(10_000_000 * sign));
-
- assert(FracSec.from!"hnsecs"(1 * sign) == FracSec(1 * sign));
- assert(FracSec.from!"hnsecs"(999 * sign) == FracSec(999 * sign));
- assert(FracSec.from!"hnsecs"(999_999 * sign) == FracSec(999_999 * sign));
- assert(FracSec.from!"hnsecs"(9_999_999 * sign) == FracSec(9_999_999 * sign));
-
- assert(FracSec.from!"nsecs"(1 * sign) == FracSec(0));
- assert(FracSec.from!"nsecs"(10 * sign) == FracSec(0));
- assert(FracSec.from!"nsecs"(99 * sign) == FracSec(0));
- assert(FracSec.from!"nsecs"(100 * sign) == FracSec(1 * sign));
- assert(FracSec.from!"nsecs"(99_999 * sign) == FracSec(999 * sign));
- assert(FracSec.from!"nsecs"(99_999_999 * sign) == FracSec(999_999 * sign));
- assert(FracSec.from!"nsecs"(999_999_999 * sign) == FracSec(9_999_999 * sign));
- }
- }
-
-
- /++
- Returns the negation of this $(D FracSec).
- +/
- FracSec opUnary(string op)() const nothrow @nogc
- if (op == "-")
- {
- return FracSec(-_hnsecs);
- }
-
- unittest
- {
- foreach (val; [-7, -5, 0, 5, 7])
- {
- foreach (F; _TypeTuple!(FracSec, const FracSec, immutable FracSec))
- {
- F fs = FracSec(val);
- assert(-fs == FracSec(-val));
- }
- }
- }
-
-
- /++
- The value of this $(D FracSec) as milliseconds.
- +/
- @property int msecs() const nothrow @nogc
- {
- return cast(int)convert!("hnsecs", "msecs")(_hnsecs);
- }
-
- unittest
- {
- foreach (F; _TypeTuple!(FracSec, const FracSec, immutable FracSec))
- {
- assert(FracSec(0).msecs == 0);
-
- foreach (sign; [1, -1])
- {
- assert((cast(F)FracSec(1 * sign)).msecs == 0);
- assert((cast(F)FracSec(999 * sign)).msecs == 0);
- assert((cast(F)FracSec(999_999 * sign)).msecs == 99 * sign);
- assert((cast(F)FracSec(9_999_999 * sign)).msecs == 999 * sign);
- }
- }
- }
-
-
- /++
- The value of this $(D FracSec) as milliseconds.
-
- Params:
- milliseconds = The number of milliseconds passed the second.
-
- Throws:
- $(D TimeException) if the given value is not less than $(D 1) second
- and greater than a $(D -1) seconds.
- +/
- @property void msecs(int milliseconds)
- {
- immutable hnsecs = cast(int)convert!("msecs", "hnsecs")(milliseconds);
- _enforceValid(hnsecs);
- _hnsecs = hnsecs;
- }
-
- unittest
- {
- static void test(int msecs, FracSec expected = FracSec.init, size_t line = __LINE__)
- {
- FracSec fs;
- fs.msecs = msecs;
-
- if (fs != expected)
- throw new AssertError("unittest failure", __FILE__, line);
- }
-
- _assertThrown!TimeException(test(-1000));
- _assertThrown!TimeException(test(1000));
-
- test(0, FracSec(0));
-
- foreach (sign; [1, -1])
- {
- test(1 * sign, FracSec(10_000 * sign));
- test(999 * sign, FracSec(9_990_000 * sign));
- }
-
- foreach (F; _TypeTuple!(const FracSec, immutable FracSec))
- {
- F fs = FracSec(1234567);
- static assert(!__traits(compiles, fs.msecs = 12), F.stringof);
- }
- }
-
-
- /++
- The value of this $(D FracSec) as microseconds.
- +/
- @property int usecs() const nothrow @nogc
- {
- return cast(int)convert!("hnsecs", "usecs")(_hnsecs);
- }
-
- unittest
- {
- foreach (F; _TypeTuple!(FracSec, const FracSec, immutable FracSec))
- {
- assert(FracSec(0).usecs == 0);
-
- foreach (sign; [1, -1])
- {
- assert((cast(F)FracSec(1 * sign)).usecs == 0);
- assert((cast(F)FracSec(999 * sign)).usecs == 99 * sign);
- assert((cast(F)FracSec(999_999 * sign)).usecs == 99_999 * sign);
- assert((cast(F)FracSec(9_999_999 * sign)).usecs == 999_999 * sign);
- }
- }
- }
-
-
- /++
- The value of this $(D FracSec) as microseconds.
-
- Params:
- microseconds = The number of microseconds passed the second.
-
- Throws:
- $(D TimeException) if the given value is not less than $(D 1) second
- and greater than a $(D -1) seconds.
- +/
- @property void usecs(int microseconds)
- {
- immutable hnsecs = cast(int)convert!("usecs", "hnsecs")(microseconds);
- _enforceValid(hnsecs);
- _hnsecs = hnsecs;
- }
-
- unittest
- {
- static void test(int usecs, FracSec expected = FracSec.init, size_t line = __LINE__)
- {
- FracSec fs;
- fs.usecs = usecs;
-
- if (fs != expected)
- throw new AssertError("unittest failure", __FILE__, line);
- }
-
- _assertThrown!TimeException(test(-1_000_000));
- _assertThrown!TimeException(test(1_000_000));
-
- test(0, FracSec(0));
-
- foreach (sign; [1, -1])
- {
- test(1 * sign, FracSec(10 * sign));
- test(999 * sign, FracSec(9990 * sign));
- test(999_999 * sign, FracSec(9_999_990 * sign));
- }
-
- foreach (F; _TypeTuple!(const FracSec, immutable FracSec))
- {
- F fs = FracSec(1234567);
- static assert(!__traits(compiles, fs.usecs = 12), F.stringof);
- }
- }
-
-
- /++
- The value of this $(D FracSec) as hnsecs.
- +/
- @property int hnsecs() const nothrow @nogc
- {
- return _hnsecs;
- }
-
- unittest
- {
- foreach (F; _TypeTuple!(FracSec, const FracSec, immutable FracSec))
- {
- assert(FracSec(0).hnsecs == 0);
-
- foreach (sign; [1, -1])
- {
- assert((cast(F)FracSec(1 * sign)).hnsecs == 1 * sign);
- assert((cast(F)FracSec(999 * sign)).hnsecs == 999 * sign);
- assert((cast(F)FracSec(999_999 * sign)).hnsecs == 999_999 * sign);
- assert((cast(F)FracSec(9_999_999 * sign)).hnsecs == 9_999_999 * sign);
- }
- }
- }
-
-
- /++
- The value of this $(D FracSec) as hnsecs.
-
- Params:
- hnsecs = The number of hnsecs passed the second.
-
- Throws:
- $(D TimeException) if the given value is not less than $(D 1) second
- and greater than a $(D -1) seconds.
- +/
- @property void hnsecs(int hnsecs)
- {
- _enforceValid(hnsecs);
- _hnsecs = hnsecs;
- }
-
- unittest
- {
- static void test(int hnsecs, FracSec expected = FracSec.init, size_t line = __LINE__)
- {
- FracSec fs;
- fs.hnsecs = hnsecs;
-
- if (fs != expected)
- throw new AssertError("unittest failure", __FILE__, line);
- }
-
- _assertThrown!TimeException(test(-10_000_000));
- _assertThrown!TimeException(test(10_000_000));
-
- test(0, FracSec(0));
-
- foreach (sign; [1, -1])
- {
- test(1 * sign, FracSec(1 * sign));
- test(999 * sign, FracSec(999 * sign));
- test(999_999 * sign, FracSec(999_999 * sign));
- test(9_999_999 * sign, FracSec(9_999_999 * sign));
- }
-
- foreach (F; _TypeTuple!(const FracSec, immutable FracSec))
- {
- F fs = FracSec(1234567);
- static assert(!__traits(compiles, fs.hnsecs = 12), F.stringof);
- }
- }
-
-
- /++
- The value of this $(D FracSec) as nsecs.
-
- Note that this does not give you any greater precision
- than getting the value of this $(D FracSec) as hnsecs.
- +/
- @property int nsecs() const nothrow @nogc
- {
- return cast(int)convert!("hnsecs", "nsecs")(_hnsecs);
- }
-
- unittest
- {
- foreach (F; _TypeTuple!(FracSec, const FracSec, immutable FracSec))
- {
- assert(FracSec(0).nsecs == 0);
-
- foreach (sign; [1, -1])
- {
- assert((cast(F)FracSec(1 * sign)).nsecs == 100 * sign);
- assert((cast(F)FracSec(999 * sign)).nsecs == 99_900 * sign);
- assert((cast(F)FracSec(999_999 * sign)).nsecs == 99_999_900 * sign);
- assert((cast(F)FracSec(9_999_999 * sign)).nsecs == 999_999_900 * sign);
- }
- }
- }
-
-
- /++
- The value of this $(D FracSec) as nsecs.
-
- Note that this does not give you any greater precision
- than setting the value of this $(D FracSec) as hnsecs.
-
- Params:
- nsecs = The number of nsecs passed the second.
-
- Throws:
- $(D TimeException) if the given value is not less than $(D 1) second
- and greater than a $(D -1) seconds.
- +/
- @property void nsecs(long nsecs)
- {
- immutable hnsecs = cast(int)convert!("nsecs", "hnsecs")(nsecs);
- _enforceValid(hnsecs);
- _hnsecs = hnsecs;
- }
-
- unittest
- {
- static void test(int nsecs, FracSec expected = FracSec.init, size_t line = __LINE__)
- {
- FracSec fs;
- fs.nsecs = nsecs;
-
- if (fs != expected)
- throw new AssertError("unittest failure", __FILE__, line);
- }
-
- _assertThrown!TimeException(test(-1_000_000_000));
- _assertThrown!TimeException(test(1_000_000_000));
-
- test(0, FracSec(0));
-
- foreach (sign; [1, -1])
- {
- test(1 * sign, FracSec(0));
- test(10 * sign, FracSec(0));
- test(100 * sign, FracSec(1 * sign));
- test(999 * sign, FracSec(9 * sign));
- test(999_999 * sign, FracSec(9999 * sign));
- test(9_999_999 * sign, FracSec(99_999 * sign));
- }
-
- foreach (F; _TypeTuple!(const FracSec, immutable FracSec))
- {
- F fs = FracSec(1234567);
- static assert(!__traits(compiles, fs.nsecs = 12), F.stringof);
- }
- }
-
-
- /+
- Converts this $(D TickDuration) to a string.
- +/
- //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't
- //have versions of toString() with extra modifiers, so we define one version
- //with modifiers and one without.
- string toString()
- {
- return _toStringImpl();
- }
-
-
- /++
- Converts this $(D TickDuration) to a string.
- +/
- //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't
- //have versions of toString() with extra modifiers, so we define one version
- //with modifiers and one without.
- string toString() const nothrow
- {
- return _toStringImpl();
- }
-
- unittest
- {
- auto fs = FracSec(12);
- const cfs = FracSec(12);
- immutable ifs = FracSec(12);
- assert(fs.toString() == "12 hnsecs");
- assert(cfs.toString() == "12 hnsecs");
- assert(ifs.toString() == "12 hnsecs");
- }
-
-
-private:
-
- /+
- Since we have two versions of $(D toString), we have $(D _toStringImpl)
- so that they can share implementations.
- +/
- string _toStringImpl() const nothrow
- {
- long hnsecs = _hnsecs;
-
- immutable milliseconds = splitUnitsFromHNSecs!"msecs"(hnsecs);
- immutable microseconds = splitUnitsFromHNSecs!"usecs"(hnsecs);
-
- if (hnsecs == 0)
- {
- if (microseconds == 0)
- {
- if (milliseconds == 0)
- return "0 hnsecs";
- else
- {
- if (milliseconds == 1)
- return "1 ms";
- else
- {
- auto r = signedToTempString(milliseconds, 10).idup;
- r ~= " ms";
- return r;
- }
- }
- }
- else
- {
- immutable fullMicroseconds = getUnitsFromHNSecs!"usecs"(_hnsecs);
-
- if (fullMicroseconds == 1)
- return "1 μs";
- else
- {
- auto r = signedToTempString(fullMicroseconds, 10).idup;
- r ~= " μs";
- return r;
- }
- }
- }
- else
- {
- if (_hnsecs == 1)
- return "1 hnsec";
- else
- {
- auto r = signedToTempString(_hnsecs, 10).idup;
- r ~= " hnsecs";
- return r;
- }
- }
- }
-
- unittest
- {
- foreach (sign; [1 , -1])
- {
- immutable signStr = sign == 1 ? "" : "-";
-
- assert(FracSec.from!"msecs"(0 * sign).toString() == "0 hnsecs");
- assert(FracSec.from!"msecs"(1 * sign).toString() == signStr ~ "1 ms");
- assert(FracSec.from!"msecs"(2 * sign).toString() == signStr ~ "2 ms");
- assert(FracSec.from!"msecs"(100 * sign).toString() == signStr ~ "100 ms");
- assert(FracSec.from!"msecs"(999 * sign).toString() == signStr ~ "999 ms");
-
- assert(FracSec.from!"usecs"(0* sign).toString() == "0 hnsecs");
- assert(FracSec.from!"usecs"(1* sign).toString() == signStr ~ "1 μs");
- assert(FracSec.from!"usecs"(2* sign).toString() == signStr ~ "2 μs");
- assert(FracSec.from!"usecs"(100* sign).toString() == signStr ~ "100 μs");
- assert(FracSec.from!"usecs"(999* sign).toString() == signStr ~ "999 μs");
- assert(FracSec.from!"usecs"(1000* sign).toString() == signStr ~ "1 ms");
- assert(FracSec.from!"usecs"(2000* sign).toString() == signStr ~ "2 ms");
- assert(FracSec.from!"usecs"(9999* sign).toString() == signStr ~ "9999 μs");
- assert(FracSec.from!"usecs"(10_000* sign).toString() == signStr ~ "10 ms");
- assert(FracSec.from!"usecs"(20_000* sign).toString() == signStr ~ "20 ms");
- assert(FracSec.from!"usecs"(100_000* sign).toString() == signStr ~ "100 ms");
- assert(FracSec.from!"usecs"(100_001* sign).toString() == signStr ~ "100001 μs");
- assert(FracSec.from!"usecs"(999_999* sign).toString() == signStr ~ "999999 μs");
-
- assert(FracSec.from!"hnsecs"(0* sign).toString() == "0 hnsecs");
- assert(FracSec.from!"hnsecs"(1* sign).toString() == (sign == 1 ? "1 hnsec" : "-1 hnsecs"));
- assert(FracSec.from!"hnsecs"(2* sign).toString() == signStr ~ "2 hnsecs");
- assert(FracSec.from!"hnsecs"(100* sign).toString() == signStr ~ "10 μs");
- assert(FracSec.from!"hnsecs"(999* sign).toString() == signStr ~ "999 hnsecs");
- assert(FracSec.from!"hnsecs"(1000* sign).toString() == signStr ~ "100 μs");
- assert(FracSec.from!"hnsecs"(2000* sign).toString() == signStr ~ "200 μs");
- assert(FracSec.from!"hnsecs"(9999* sign).toString() == signStr ~ "9999 hnsecs");
- assert(FracSec.from!"hnsecs"(10_000* sign).toString() == signStr ~ "1 ms");
- assert(FracSec.from!"hnsecs"(20_000* sign).toString() == signStr ~ "2 ms");
- assert(FracSec.from!"hnsecs"(100_000* sign).toString() == signStr ~ "10 ms");
- assert(FracSec.from!"hnsecs"(100_001* sign).toString() == signStr ~ "100001 hnsecs");
- assert(FracSec.from!"hnsecs"(200_000* sign).toString() == signStr ~ "20 ms");
- assert(FracSec.from!"hnsecs"(999_999* sign).toString() == signStr ~ "999999 hnsecs");
- assert(FracSec.from!"hnsecs"(1_000_001* sign).toString() == signStr ~ "1000001 hnsecs");
- assert(FracSec.from!"hnsecs"(9_999_999* sign).toString() == signStr ~ "9999999 hnsecs");
- }
- }
-
-
- /+
- Returns whether the given number of hnsecs fits within the range of
- $(D FracSec).
-
- Params:
- hnsecs = The number of hnsecs.
- +/
- static bool _valid(int hnsecs) nothrow @nogc
- {
- immutable second = convert!("seconds", "hnsecs")(1);
- return hnsecs > -second && hnsecs < second;
- }
-
-
- /+
- Throws:
- $(D TimeException) if $(D valid(hnsecs)) is $(D false).
- +/
- static void _enforceValid(int hnsecs)
- {
- if (!_valid(hnsecs))
- throw new TimeException("FracSec must be greater than equal to 0 and less than 1 second.");
- }
-
-
- /+
- Params:
- hnsecs = The number of hnsecs passed the second.
- +/
- this(int hnsecs) nothrow @nogc
- {
- _hnsecs = hnsecs;
- }
-
-
- invariant()
- {
- if (!_valid(_hnsecs))
- throw new AssertError("Invariant Failure: hnsecs [" ~ signedToTempString(_hnsecs, 10).idup ~ "]", __FILE__, __LINE__);
- }
-
-
- int _hnsecs;
-}
-
-
/++
Exception type used by core.time.
+/
@@ -4318,7 +3694,6 @@ long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nog
return value;
}
-///
unittest
{
auto hnsecs = 2595000000007L;
@@ -4331,83 +3706,6 @@ unittest
assert(hnsecs == 7);
}
-
-/+
- This function is used to split out the units without getting the remaining
- hnsecs.
-
- See_Also:
- $(LREF splitUnitsFromHNSecs)
-
- Params:
- units = The units to split out.
- hnsecs = The current total hnsecs.
-
- Returns:
- The split out value.
- +/
-long getUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow @nogc
- if (units == "weeks" ||
- units == "days" ||
- units == "hours" ||
- units == "minutes" ||
- units == "seconds" ||
- units == "msecs" ||
- units == "usecs" ||
- units == "hnsecs")
-{
- return convert!("hnsecs", units)(hnsecs);
-}
-
-///
-unittest
-{
- auto hnsecs = 2595000000007L;
- immutable days = getUnitsFromHNSecs!"days"(hnsecs);
- assert(days == 3);
- assert(hnsecs == 2595000000007L);
-}
-
-
-/+
- This function is used to split out the units without getting the units but
- just the remaining hnsecs.
-
- See_Also:
- $(LREF splitUnitsFromHNSecs)
-
- Params:
- units = The units to split out.
- hnsecs = The current total hnsecs.
-
- Returns:
- The remaining hnsecs.
- +/
-long removeUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow @nogc
- if (units == "weeks" ||
- units == "days" ||
- units == "hours" ||
- units == "minutes" ||
- units == "seconds" ||
- units == "msecs" ||
- units == "usecs" ||
- units == "hnsecs")
-{
- immutable value = convert!("hnsecs", units)(hnsecs);
-
- return hnsecs - convert!(units, "hnsecs")(value);
-}
-
-///
-unittest
-{
- auto hnsecs = 2595000000007L;
- auto returned = removeUnitsFromHNSecs!"days"(hnsecs);
- assert(returned == 3000000007);
- assert(hnsecs == 2595000000007L);
-}
-
-
/+
Whether all of the given strings are among the accepted strings.
+/
@@ -4494,63 +3792,6 @@ unittest
assert(!unitsAreInDescendingOrder("days", "hours", "days"));
}
-
-/+
- The time units which are one step larger than the given units.
- +/
-template nextLargerTimeUnits(string units)
- if (units == "days" ||
- units == "hours" ||
- units == "minutes" ||
- units == "seconds" ||
- units == "msecs" ||
- units == "usecs" ||
- units == "hnsecs" ||
- units == "nsecs")
-{
- static if (units == "days")
- enum nextLargerTimeUnits = "weeks";
- else static if (units == "hours")
- enum nextLargerTimeUnits = "days";
- else static if (units == "minutes")
- enum nextLargerTimeUnits = "hours";
- else static if (units == "seconds")
- enum nextLargerTimeUnits = "minutes";
- else static if (units == "msecs")
- enum nextLargerTimeUnits = "seconds";
- else static if (units == "usecs")
- enum nextLargerTimeUnits = "msecs";
- else static if (units == "hnsecs")
- enum nextLargerTimeUnits = "usecs";
- else static if (units == "nsecs")
- enum nextLargerTimeUnits = "hnsecs";
- else
- static assert(0, "Broken template constraint");
-}
-
-///
-unittest
-{
- assert(nextLargerTimeUnits!"minutes" == "hours");
- assert(nextLargerTimeUnits!"hnsecs" == "usecs");
-}
-
-unittest
-{
- assert(nextLargerTimeUnits!"nsecs" == "hnsecs");
- assert(nextLargerTimeUnits!"hnsecs" == "usecs");
- assert(nextLargerTimeUnits!"usecs" == "msecs");
- assert(nextLargerTimeUnits!"msecs" == "seconds");
- assert(nextLargerTimeUnits!"seconds" == "minutes");
- assert(nextLargerTimeUnits!"minutes" == "hours");
- assert(nextLargerTimeUnits!"hours" == "days");
- assert(nextLargerTimeUnits!"days" == "weeks");
-
- static assert(!__traits(compiles, nextLargerTimeUnits!"weeks"));
- static assert(!__traits(compiles, nextLargerTimeUnits!"months"));
- static assert(!__traits(compiles, nextLargerTimeUnits!"years"));
-}
-
version (Darwin)
long machTicksPerSecond()
{
@@ -4581,16 +3822,16 @@ double _abs(double val) @safe pure nothrow @nogc
}
-version (unittest)
+version (CoreUnittest)
string doubleToString(double value) @safe pure nothrow
{
string result;
if (value < 0 && cast(long)value == 0)
result = "-0";
else
- result = signedToTempString(cast(long)value, 10).idup;
+ result = signedToTempString(cast(long)value).idup;
result ~= '.';
- result ~= unsignedToTempString(cast(ulong)(_abs((value - cast(long)value) * 1_000_000) + .5), 10);
+ result ~= unsignedToTempString(cast(ulong)(_abs((value - cast(long)value) * 1_000_000) + .5));
while (result[$-1] == '0')
result = result[0 .. $-1];
@@ -4612,21 +3853,17 @@ unittest
assert(aStr == "-0.337", aStr);
}
-version (unittest) const(char)* numToStringz()(long value) @trusted pure nothrow
+version (CoreUnittest) const(char)* numToStringz()(long value) @trusted pure nothrow
{
- return (signedToTempString(value, 10) ~ "\0").ptr;
+ return (signedToTempString(value) ~ "\0").ptr;
}
-/+ A copy of std.typecons.TypeTuple. +/
-template _TypeTuple(TList...)
-{
- alias TList _TypeTuple;
-}
+import core.internal.traits : AliasSeq;
/+ An adjusted copy of std.exception.assertThrown. +/
-version (unittest) void _assertThrown(T : Throwable = Exception, E)
+version (CoreUnittest) void _assertThrown(T : Throwable = Exception, E)
(lazy E expression,
string msg = null,
string file = __FILE__,
@@ -4721,7 +3958,7 @@ unittest
}
-version (unittest) void assertApprox(D, E)(D actual,
+version (CoreUnittest) void assertApprox(D, E)(D actual,
E lower,
E upper,
string msg = "unittest failure",
@@ -4734,7 +3971,7 @@ version (unittest) void assertApprox(D, E)(D actual,
throw new AssertError(msg ~ ": upper: " ~ actual.toString(), __FILE__, line);
}
-version (unittest) void assertApprox(D, E)(D actual,
+version (CoreUnittest) void assertApprox(D, E)(D actual,
E lower,
E upper,
string msg = "unittest failure",
@@ -4743,14 +3980,14 @@ version (unittest) void assertApprox(D, E)(D actual,
{
if (actual.length < lower.length || actual.length > upper.length)
{
- throw new AssertError(msg ~ (": [" ~ signedToTempString(lower.length, 10) ~ "] [" ~
- signedToTempString(actual.length, 10) ~ "] [" ~
- signedToTempString(upper.length, 10) ~ "]").idup,
+ throw new AssertError(msg ~ (": [" ~ signedToTempString(lower.length) ~ "] [" ~
+ signedToTempString(actual.length) ~ "] [" ~
+ signedToTempString(upper.length) ~ "]").idup,
__FILE__, line);
}
}
-version (unittest) void assertApprox(MT)(MT actual,
+version (CoreUnittest) void assertApprox(MT)(MT actual,
MT lower,
MT upper,
string msg = "unittest failure",
@@ -4760,14 +3997,14 @@ version (unittest) void assertApprox(MT)(MT actual,
assertApprox(actual._ticks, lower._ticks, upper._ticks, msg, line);
}
-version (unittest) void assertApprox()(long actual,
+version (CoreUnittest) void assertApprox()(long actual,
long lower,
long upper,
string msg = "unittest failure",
size_t line = __LINE__)
{
if (actual < lower)
- throw new AssertError(msg ~ ": lower: " ~ signedToTempString(actual, 10).idup, __FILE__, line);
+ throw new AssertError(msg ~ ": lower: " ~ signedToTempString(actual).idup, __FILE__, line);
if (actual > upper)
- throw new AssertError(msg ~ ": upper: " ~ signedToTempString(actual, 10).idup, __FILE__, line);
+ throw new AssertError(msg ~ ": upper: " ~ signedToTempString(actual).idup, __FILE__, line);
}
diff --git a/libphobos/libdruntime/gc/bits.d b/libphobos/libdruntime/gc/bits.d
deleted file mode 100644
index 60ba4f4eefd..00000000000
--- a/libphobos/libdruntime/gc/bits.d
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Contains a bitfield used by the GC.
- *
- * Copyright: Copyright Digital Mars 2005 - 2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, David Friedman, Sean Kelly
- */
-
-/* Copyright Digital Mars 2005 - 2013.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module gc.bits;
-
-
-import core.bitop;
-import core.stdc.string;
-import core.stdc.stdlib;
-import core.exception : onOutOfMemoryError;
-
-struct GCBits
-{
- alias size_t wordtype;
-
- enum BITS_PER_WORD = (wordtype.sizeof * 8);
- enum BITS_SHIFT = (wordtype.sizeof == 8 ? 6 : 5);
- enum BITS_MASK = (BITS_PER_WORD - 1);
- enum BITS_1 = cast(wordtype)1;
-
- wordtype* data;
- size_t nbits;
-
- void Dtor() nothrow
- {
- if (data)
- {
- free(data);
- data = null;
- }
- }
-
- void alloc(size_t nbits) nothrow
- {
- this.nbits = nbits;
- data = cast(typeof(data[0])*)calloc(nwords, data[0].sizeof);
- if (!data)
- onOutOfMemoryError();
- }
-
- wordtype test(size_t i) const nothrow
- in
- {
- assert(i < nbits);
- }
- body
- {
- return core.bitop.bt(data, i);
- }
-
- int set(size_t i) nothrow
- in
- {
- assert(i < nbits);
- }
- body
- {
- return core.bitop.bts(data, i);
- }
-
- int clear(size_t i) nothrow
- in
- {
- assert(i <= nbits);
- }
- body
- {
- return core.bitop.btr(data, i);
- }
-
- void zero() nothrow
- {
- memset(data, 0, nwords * wordtype.sizeof);
- }
-
- void copy(GCBits *f) nothrow
- in
- {
- assert(nwords == f.nwords);
- }
- body
- {
- memcpy(data, f.data, nwords * wordtype.sizeof);
- }
-
- @property size_t nwords() const pure nothrow
- {
- return (nbits + (BITS_PER_WORD - 1)) >> BITS_SHIFT;
- }
-}
-
-unittest
-{
- GCBits b;
-
- b.alloc(786);
- assert(!b.test(123));
- assert(!b.clear(123));
- assert(!b.set(123));
- assert(b.test(123));
- assert(b.clear(123));
- assert(!b.test(123));
-
- b.set(785);
- b.set(0);
- assert(b.test(785));
- assert(b.test(0));
- b.zero();
- assert(!b.test(785));
- assert(!b.test(0));
-
- GCBits b2;
- b2.alloc(786);
- b2.set(38);
- b.copy(&b2);
- assert(b.test(38));
- b2.Dtor();
- b.Dtor();
-}
diff --git a/libphobos/libdruntime/gc/config.d b/libphobos/libdruntime/gc/config.d
deleted file mode 100644
index b0789cd2b2f..00000000000
--- a/libphobos/libdruntime/gc/config.d
+++ /dev/null
@@ -1,291 +0,0 @@
-/**
-* Contains the garbage collector configuration.
-*
-* Copyright: Copyright Digital Mars 2016
-* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
-*/
-
-module gc.config;
-
-import core.stdc.stdlib;
-import core.stdc.stdio;
-import core.stdc.ctype;
-import core.stdc.string;
-import core.vararg;
-
-nothrow @nogc:
-extern extern(C) string[] rt_args();
-
-extern extern(C) __gshared bool rt_envvars_enabled;
-extern extern(C) __gshared bool rt_cmdline_enabled;
-extern extern(C) __gshared string[] rt_options;
-
-__gshared Config config;
-
-struct Config
-{
- bool disable; // start disabled
- ubyte profile; // enable profiling with summary when terminating program
- string gc = "conservative"; // select gc implementation conservative|manual
-
- size_t initReserve; // initial reserve (MB)
- size_t minPoolSize = 1; // initial and minimum pool size (MB)
- size_t maxPoolSize = 64; // maximum pool size (MB)
- size_t incPoolSize = 3; // pool size increment (MB)
- float heapSizeFactor = 2.0; // heap size to used memory ratio
-
-@nogc nothrow:
-
- bool initialize()
- {
- import core.internal.traits : externDFunc;
-
- alias rt_configCallBack = string delegate(string) @nogc nothrow;
- alias fn_configOption = string function(string opt, scope rt_configCallBack dg, bool reverse) @nogc nothrow;
-
- alias rt_configOption = externDFunc!("rt.config.rt_configOption", fn_configOption);
-
- string parse(string opt) @nogc nothrow
- {
- if (!parseOptions(opt))
- return "err";
- return null; // continue processing
- }
- string s = rt_configOption("gcopt", &parse, true);
- return s is null;
- }
-
- void help()
- {
- version (unittest) if (inUnittest) return;
-
- string s = "GC options are specified as white space separated assignments:
- disable:0|1 - start disabled (%d)
- profile:0|1|2 - enable profiling with summary when terminating program (%d)
- gc:conservative|manual - select gc implementation (default = conservative)
-
- initReserve:N - initial memory to reserve in MB (%lld)
- minPoolSize:N - initial and minimum pool size in MB (%lld)
- maxPoolSize:N - maximum pool size in MB (%lld)
- incPoolSize:N - pool size increment MB (%lld)
- heapSizeFactor:N - targeted heap size to used memory ratio (%g)
-";
- printf(s.ptr, disable, profile, cast(long)initReserve, cast(long)minPoolSize,
- cast(long)maxPoolSize, cast(long)incPoolSize, heapSizeFactor);
- }
-
- bool parseOptions(string opt)
- {
- opt = skip!isspace(opt);
- while (opt.length)
- {
- auto tail = find!(c => c == ':' || c == '=' || c == ' ')(opt);
- auto name = opt[0 .. $ - tail.length];
- if (name == "help")
- {
- help();
- opt = skip!isspace(tail);
- continue;
- }
- if (tail.length <= 1 || tail[0] == ' ')
- return optError("Missing argument for", name);
- tail = tail[1 .. $];
-
- switch (name)
- {
- foreach (field; __traits(allMembers, Config))
- {
- static if (!is(typeof(__traits(getMember, this, field)) == function))
- {
- case field:
- if (!parse(name, tail, __traits(getMember, this, field)))
- return false;
- break;
- }
- }
- break;
-
- default:
- return optError("Unknown", name);
- }
- opt = skip!isspace(tail);
- }
- return true;
- }
-}
-
-private:
-
-bool optError(in char[] msg, in char[] name)
-{
- version (unittest) if (inUnittest) return false;
-
- fprintf(stderr, "%.*s GC option '%.*s'.\n",
- cast(int)msg.length, msg.ptr,
- cast(int)name.length, name.ptr);
- return false;
-}
-
-inout(char)[] skip(alias pred)(inout(char)[] str)
-{
- return find!(c => !pred(c))(str);
-}
-
-inout(char)[] find(alias pred)(inout(char)[] str)
-{
- foreach (i; 0 .. str.length)
- if (pred(str[i])) return str[i .. $];
- return null;
-}
-
-bool parse(T:size_t)(const(char)[] optname, ref inout(char)[] str, ref T res)
-in { assert(str.length); }
-body
-{
- size_t i, v;
- for (; i < str.length && isdigit(str[i]); ++i)
- v = 10 * v + str[i] - '0';
-
- if (!i)
- return parseError("a number", optname, str);
- if (v > res.max)
- return parseError("a number " ~ T.max.stringof ~ " or below", optname, str[0 .. i]);
- str = str[i .. $];
- res = cast(T) v;
- return true;
-}
-
-bool parse(const(char)[] optname, ref inout(char)[] str, ref bool res)
-in { assert(str.length); }
-body
-{
- if (str[0] == '1' || str[0] == 'y' || str[0] == 'Y')
- res = true;
- else if (str[0] == '0' || str[0] == 'n' || str[0] == 'N')
- res = false;
- else
- return parseError("'0/n/N' or '1/y/Y'", optname, str);
- str = str[1 .. $];
- return true;
-}
-
-bool parse(const(char)[] optname, ref inout(char)[] str, ref float res)
-in { assert(str.length); }
-body
-{
- // % uint f %n \0
- char[1 + 10 + 1 + 2 + 1] fmt=void;
- // specify max-width
- immutable n = snprintf(fmt.ptr, fmt.length, "%%%uf%%n", cast(uint)str.length);
- assert(n > 4 && n < fmt.length);
-
- int nscanned;
- version (CRuntime_DigitalMars)
- {
- /* Older sscanf's in snn.lib can write to its first argument, causing a crash
- * if the string is in readonly memory. Recent updates to DMD
- * https://github.com/dlang/dmd/pull/6546
- * put string literals in readonly memory.
- * Although sscanf has been fixed,
- * http://ftp.digitalmars.com/snn.lib
- * this workaround is here so it still works with the older snn.lib.
- */
- // Create mutable copy of str
- const length = str.length;
- char* mptr = cast(char*)malloc(length + 1);
- assert(mptr);
- memcpy(mptr, str.ptr, length);
- mptr[length] = 0;
- const result = sscanf(mptr, fmt.ptr, &res, &nscanned);
- free(mptr);
- if (result < 1)
- return parseError("a float", optname, str);
- }
- else
- {
- if (sscanf(str.ptr, fmt.ptr, &res, &nscanned) < 1)
- return parseError("a float", optname, str);
- }
- str = str[nscanned .. $];
- return true;
-}
-
-bool parse(const(char)[] optname, ref inout(char)[] str, ref inout(char)[] res)
-in { assert(str.length); }
-body
-{
- auto tail = str.find!(c => c == ':' || c == '=' || c == ' ');
- res = str[0 .. $ - tail.length];
- if (!res.length)
- return parseError("an identifier", optname, str);
- str = tail;
- return true;
-}
-
-bool parseError(in char[] exp, in char[] opt, in char[] got)
-{
- version (unittest) if (inUnittest) return false;
-
- fprintf(stderr, "Expecting %.*s as argument for GC option '%.*s', got '%.*s' instead.\n",
- cast(int)exp.length, exp.ptr,
- cast(int)opt.length, opt.ptr,
- cast(int)got.length, got.ptr);
- return false;
-}
-
-size_t min(size_t a, size_t b) { return a <= b ? a : b; }
-
-version (unittest) __gshared bool inUnittest;
-
-unittest
-{
- inUnittest = true;
- scope (exit) inUnittest = false;
-
- Config conf;
- assert(!conf.parseOptions("disable"));
- assert(!conf.parseOptions("disable:"));
- assert(!conf.parseOptions("disable:5"));
- assert(conf.parseOptions("disable:y") && conf.disable);
- assert(conf.parseOptions("disable:n") && !conf.disable);
- assert(conf.parseOptions("disable:Y") && conf.disable);
- assert(conf.parseOptions("disable:N") && !conf.disable);
- assert(conf.parseOptions("disable:1") && conf.disable);
- assert(conf.parseOptions("disable:0") && !conf.disable);
-
- assert(conf.parseOptions("disable=y") && conf.disable);
- assert(conf.parseOptions("disable=n") && !conf.disable);
-
- assert(conf.parseOptions("profile=0") && conf.profile == 0);
- assert(conf.parseOptions("profile=1") && conf.profile == 1);
- assert(conf.parseOptions("profile=2") && conf.profile == 2);
- assert(!conf.parseOptions("profile=256"));
-
- assert(conf.parseOptions("disable:1 minPoolSize:16"));
- assert(conf.disable);
- assert(conf.minPoolSize == 16);
-
- assert(conf.parseOptions("heapSizeFactor:3.1"));
- assert(conf.heapSizeFactor == 3.1f);
- assert(conf.parseOptions("heapSizeFactor:3.1234567890 disable:0"));
- assert(conf.heapSizeFactor > 3.123f);
- assert(!conf.disable);
- assert(!conf.parseOptions("heapSizeFactor:3.0.2.5"));
- assert(conf.parseOptions("heapSizeFactor:2"));
- assert(conf.heapSizeFactor == 2.0f);
-
- assert(!conf.parseOptions("initReserve:foo"));
- assert(!conf.parseOptions("initReserve:y"));
- assert(!conf.parseOptions("initReserve:20.5"));
-
- assert(conf.parseOptions("help"));
- assert(conf.parseOptions("help profile:1"));
- assert(conf.parseOptions("help profile:1 help"));
-
- assert(conf.parseOptions("gc:manual") && conf.gc == "manual");
- assert(conf.parseOptions("gc:my-gc~modified") && conf.gc == "my-gc~modified");
- assert(conf.parseOptions("gc:conservative help profile:1") && conf.gc == "conservative" && conf.profile == 1);
-
- // the config parse doesn't know all available GC names, so should accept unknown ones
- assert(conf.parseOptions("gc:whatever"));
-}
diff --git a/libphobos/libdruntime/gc/gcinterface.d b/libphobos/libdruntime/gc/gcinterface.d
deleted file mode 100644
index abe88f1c829..00000000000
--- a/libphobos/libdruntime/gc/gcinterface.d
+++ /dev/null
@@ -1,190 +0,0 @@
-/**
- * Contains the internal GC interface.
- *
- * Copyright: Copyright Digital Mars 2016.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, Sean Kelly, Jeremy DeHaan
- */
-
- /* Copyright Digital Mars 2016.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module gc.gcinterface;
-
-static import core.memory;
-alias BlkAttr = core.memory.GC.BlkAttr;
-alias BlkInfo = core.memory.GC.BlkInfo;
-
-alias RootIterator = int delegate(scope int delegate(ref Root) nothrow dg);
-alias RangeIterator = int delegate(scope int delegate(ref Range) nothrow dg);
-
-
-struct Root
-{
- void* proot;
- alias proot this;
-}
-
-struct Range
-{
- void* pbot;
- void* ptop;
- TypeInfo ti; // should be tail const, but doesn't exist for references
- alias pbot this; // only consider pbot for relative ordering (opCmp)
-}
-
-interface GC
-{
-
- /*
- *
- */
- void Dtor();
-
- /**
- *
- */
- void enable();
-
- /**
- *
- */
- void disable();
-
- /**
- *
- */
- void collect() nothrow;
-
- /**
- *
- */
- void collectNoStack() nothrow;
-
- /**
- * minimize free space usage
- */
- void minimize() nothrow;
-
- /**
- *
- */
- uint getAttr(void* p) nothrow;
-
- /**
- *
- */
- uint setAttr(void* p, uint mask) nothrow;
-
- /**
- *
- */
- uint clrAttr(void* p, uint mask) nothrow;
-
- /**
- *
- */
- void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow;
-
- /*
- *
- */
- BlkInfo qalloc(size_t size, uint bits, const TypeInfo ti) nothrow;
-
- /*
- *
- */
- void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow;
-
- /*
- *
- */
- void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow;
-
- /**
- * Attempt to in-place enlarge the memory block pointed to by p by at least
- * minsize bytes, up to a maximum of maxsize additional bytes.
- * This does not attempt to move the memory block (like realloc() does).
- *
- * Returns:
- * 0 if could not extend p,
- * total size of entire memory block if successful.
- */
- size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow;
-
- /**
- *
- */
- size_t reserve(size_t size) nothrow;
-
- /**
- *
- */
- void free(void* p) nothrow;
-
- /**
- * Determine the base address of the block containing p. If p is not a gc
- * allocated pointer, return null.
- */
- void* addrOf(void* p) nothrow;
-
- /**
- * Determine the allocated size of pointer p. If p is an interior pointer
- * or not a gc allocated pointer, return 0.
- */
- size_t sizeOf(void* p) nothrow;
-
- /**
- * Determine the base address of the block containing p. If p is not a gc
- * allocated pointer, return null.
- */
- BlkInfo query(void* p) nothrow;
-
- /**
- * Retrieve statistics about garbage collection.
- * Useful for debugging and tuning.
- */
- core.memory.GC.Stats stats() nothrow;
-
- /**
- * add p to list of roots
- */
- void addRoot(void* p) nothrow @nogc;
-
- /**
- * remove p from list of roots
- */
- void removeRoot(void* p) nothrow @nogc;
-
- /**
- *
- */
- @property RootIterator rootIter() @nogc;
-
- /**
- * add range to scan for roots
- */
- void addRange(void* p, size_t sz, const TypeInfo ti) nothrow @nogc;
-
- /**
- * remove range
- */
- void removeRange(void* p) nothrow @nogc;
-
- /**
- *
- */
- @property RangeIterator rangeIter() @nogc;
-
- /**
- * run finalizers
- */
- void runFinalizers(in void[] segment) nothrow;
-
- /*
- *
- */
- bool inFinalizer() nothrow;
-}
diff --git a/libphobos/libdruntime/gc/impl/conservative/gc.d b/libphobos/libdruntime/gc/impl/conservative/gc.d
deleted file mode 100644
index 300a32ad2b0..00000000000
--- a/libphobos/libdruntime/gc/impl/conservative/gc.d
+++ /dev/null
@@ -1,3413 +0,0 @@
-/**
- * Contains the garbage collector implementation.
- *
- * Copyright: Copyright Digital Mars 2001 - 2016.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, David Friedman, Sean Kelly
- */
-
-/* Copyright Digital Mars 2005 - 2016.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module gc.impl.conservative.gc;
-
-// D Programming Language Garbage Collector implementation
-
-/************** Debugging ***************************/
-
-//debug = PRINTF; // turn on printf's
-//debug = COLLECT_PRINTF; // turn on printf's
-//debug = PRINTF_TO_FILE; // redirect printf's ouptut to file "gcx.log"
-//debug = LOGGING; // log allocations / frees
-//debug = MEMSTOMP; // stomp on memory
-//debug = SENTINEL; // add underrun/overrrun protection
- // NOTE: this needs to be enabled globally in the makefiles
- // (-debug=SENTINEL) to pass druntime's unittests.
-//debug = PTRCHECK; // more pointer checking
-//debug = PTRCHECK2; // thorough but slow pointer checking
-//debug = INVARIANT; // enable invariants
-//debug = PROFILE_API; // profile API calls for config.profile > 1
-
-/***************************************************/
-
-import gc.bits;
-import gc.os;
-import gc.config;
-import gc.gcinterface;
-
-import rt.util.container.treap;
-
-import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
-import core.stdc.string : memcpy, memset, memmove;
-import core.bitop;
-import core.thread;
-static import core.memory;
-
-version (GNU) import gcc.builtins;
-
-debug (PRINTF_TO_FILE) import core.stdc.stdio : sprintf, fprintf, fopen, fflush, FILE;
-else import core.stdc.stdio : sprintf, printf; // needed to output profiling results
-
-import core.time;
-alias currTime = MonoTime.currTime;
-
-debug(PRINTF_TO_FILE)
-{
- private __gshared MonoTime gcStartTick;
- private __gshared FILE* gcx_fh;
-
- private int printf(ARGS...)(const char* fmt, ARGS args) nothrow
- {
- if (!gcx_fh)
- gcx_fh = fopen("gcx.log", "w");
- if (!gcx_fh)
- return 0;
-
- int len;
- if (MonoTime.ticksPerSecond == 0)
- {
- len = fprintf(gcx_fh, "before init: ");
- }
- else
- {
- if (gcStartTick == MonoTime.init)
- gcStartTick = MonoTime.currTime;
- immutable timeElapsed = MonoTime.currTime - gcStartTick;
- immutable secondsAsDouble = timeElapsed.total!"hnsecs" / cast(double)convert!("seconds", "hnsecs")(1);
- len = fprintf(gcx_fh, "%10.6f: ", secondsAsDouble);
- }
- len += fprintf(gcx_fh, fmt, args);
- fflush(gcx_fh);
- return len;
- }
-}
-
-debug(PRINTF) void printFreeInfo(Pool* pool) nothrow
-{
- uint nReallyFree;
- foreach (i; 0..pool.npages) {
- if (pool.pagetable[i] >= B_FREE) nReallyFree++;
- }
-
- printf("Pool %p: %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages);
-}
-
-// Track total time spent preparing for GC,
-// marking, sweeping and recovering pages.
-__gshared Duration prepTime;
-__gshared Duration markTime;
-__gshared Duration sweepTime;
-__gshared Duration recoverTime;
-__gshared Duration maxPauseTime;
-__gshared size_t numCollections;
-__gshared size_t maxPoolMemory;
-
-__gshared long numMallocs;
-__gshared long numFrees;
-__gshared long numReallocs;
-__gshared long numExtends;
-__gshared long numOthers;
-__gshared long mallocTime; // using ticks instead of MonoTime for better performance
-__gshared long freeTime;
-__gshared long reallocTime;
-__gshared long extendTime;
-__gshared long otherTime;
-__gshared long lockTime;
-
-private
-{
- extern (C)
- {
- // to allow compilation of this module without access to the rt package,
- // make these functions available from rt.lifetime
- void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow;
- int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, in void[] segment) nothrow;
-
- // Declared as an extern instead of importing core.exception
- // to avoid inlining - see issue 13725.
- void onInvalidMemoryOperationError() @nogc nothrow;
- void onOutOfMemoryErrorNoGC() @nogc nothrow;
- }
-
- enum
- {
- OPFAIL = ~cast(size_t)0
- }
-}
-
-
-alias GC gc_t;
-
-
-/* ======================= Leak Detector =========================== */
-
-
-debug (LOGGING)
-{
- struct Log
- {
- void* p;
- size_t size;
- size_t line;
- char* file;
- void* parent;
-
- void print() nothrow
- {
- printf(" p = %p, size = %zd, parent = %p ", p, size, parent);
- if (file)
- {
- printf("%s(%u)", file, cast(uint)line);
- }
- printf("\n");
- }
- }
-
-
- struct LogArray
- {
- size_t dim;
- size_t allocdim;
- Log *data;
-
- void Dtor() nothrow
- {
- if (data)
- cstdlib.free(data);
- data = null;
- }
-
- void reserve(size_t nentries) nothrow
- {
- assert(dim <= allocdim);
- if (allocdim - dim < nentries)
- {
- allocdim = (dim + nentries) * 2;
- assert(dim + nentries <= allocdim);
- if (!data)
- {
- data = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
- if (!data && allocdim)
- onOutOfMemoryErrorNoGC();
- }
- else
- { Log *newdata;
-
- newdata = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
- if (!newdata && allocdim)
- onOutOfMemoryErrorNoGC();
- memcpy(newdata, data, dim * Log.sizeof);
- cstdlib.free(data);
- data = newdata;
- }
- }
- }
-
-
- void push(Log log) nothrow
- {
- reserve(1);
- data[dim++] = log;
- }
-
- void remove(size_t i) nothrow
- {
- memmove(data + i, data + i + 1, (dim - i) * Log.sizeof);
- dim--;
- }
-
-
- size_t find(void *p) nothrow
- {
- for (size_t i = 0; i < dim; i++)
- {
- if (data[i].p == p)
- return i;
- }
- return OPFAIL; // not found
- }
-
-
- void copy(LogArray *from) nothrow
- {
- reserve(from.dim - dim);
- assert(from.dim <= allocdim);
- memcpy(data, from.data, from.dim * Log.sizeof);
- dim = from.dim;
- }
- }
-}
-
-
-/* ============================ GC =============================== */
-
-class ConservativeGC : GC
-{
- // For passing to debug code (not thread safe)
- __gshared size_t line;
- __gshared char* file;
-
- Gcx *gcx; // implementation
-
- import core.internal.spinlock;
- static gcLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy);
- static bool _inFinalizer;
-
- // lock GC, throw InvalidMemoryOperationError on recursive locking during finalization
- static void lockNR() @nogc nothrow
- {
- if (_inFinalizer)
- onInvalidMemoryOperationError();
- gcLock.lock();
- }
-
-
- static void initialize(ref GC gc)
- {
- import core.stdc.string: memcpy;
-
- if (config.gc != "conservative")
- return;
-
- auto p = cstdlib.malloc(__traits(classInstanceSize,ConservativeGC));
-
- if (!p)
- onOutOfMemoryErrorNoGC();
-
- auto init = typeid(ConservativeGC).initializer();
- assert(init.length == __traits(classInstanceSize, ConservativeGC));
- auto instance = cast(ConservativeGC) memcpy(p, init.ptr, init.length);
- instance.__ctor();
-
- gc = instance;
- }
-
-
- static void finalize(ref GC gc)
- {
- if (config.gc != "conservative")
- return;
-
- auto instance = cast(ConservativeGC) gc;
- instance.Dtor();
- cstdlib.free(cast(void*)instance);
- }
-
-
- this()
- {
- //config is assumed to have already been initialized
-
- gcx = cast(Gcx*)cstdlib.calloc(1, Gcx.sizeof);
- if (!gcx)
- onOutOfMemoryErrorNoGC();
- gcx.initialize();
-
- if (config.initReserve)
- gcx.reserve(config.initReserve << 20);
- if (config.disable)
- gcx.disabled++;
- }
-
-
- void Dtor()
- {
- version (linux)
- {
- //debug(PRINTF) printf("Thread %x ", pthread_self());
- //debug(PRINTF) printf("GC.Dtor()\n");
- }
-
- if (gcx)
- {
- gcx.Dtor();
- cstdlib.free(gcx);
- gcx = null;
- }
- }
-
-
- void enable()
- {
- static void go(Gcx* gcx) nothrow
- {
- assert(gcx.disabled > 0);
- gcx.disabled--;
- }
- runLocked!(go, otherTime, numOthers)(gcx);
- }
-
-
- void disable()
- {
- static void go(Gcx* gcx) nothrow
- {
- gcx.disabled++;
- }
- runLocked!(go, otherTime, numOthers)(gcx);
- }
-
-
- auto runLocked(alias func, Args...)(auto ref Args args)
- {
- debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0);
- lockNR();
- scope (failure) gcLock.unlock();
- debug(PROFILE_API) immutable tm2 = (config.profile > 1 ? currTime.ticks : 0);
-
- static if (is(typeof(func(args)) == void))
- func(args);
- else
- auto res = func(args);
-
- debug(PROFILE_API) if (config.profile > 1)
- lockTime += tm2 - tm;
- gcLock.unlock();
-
- static if (!is(typeof(func(args)) == void))
- return res;
- }
-
-
- auto runLocked(alias func, alias time, alias count, Args...)(auto ref Args args)
- {
- debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0);
- lockNR();
- scope (failure) gcLock.unlock();
- debug(PROFILE_API) immutable tm2 = (config.profile > 1 ? currTime.ticks : 0);
-
- static if (is(typeof(func(args)) == void))
- func(args);
- else
- auto res = func(args);
-
- debug(PROFILE_API) if (config.profile > 1)
- {
- count++;
- immutable now = currTime.ticks;
- lockTime += tm2 - tm;
- time += now - tm2;
- }
- gcLock.unlock();
-
- static if (!is(typeof(func(args)) == void))
- return res;
- }
-
-
- uint getAttr(void* p) nothrow
- {
- if (!p)
- {
- return 0;
- }
-
- static uint go(Gcx* gcx, void* p) nothrow
- {
- Pool* pool = gcx.findPool(p);
- uint oldb = 0;
-
- if (pool)
- {
- p = sentinel_sub(p);
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- oldb = pool.getBits(biti);
- }
- return oldb;
- }
-
- return runLocked!(go, otherTime, numOthers)(gcx, p);
- }
-
-
- uint setAttr(void* p, uint mask) nothrow
- {
- if (!p)
- {
- return 0;
- }
-
- static uint go(Gcx* gcx, void* p, uint mask) nothrow
- {
- Pool* pool = gcx.findPool(p);
- uint oldb = 0;
-
- if (pool)
- {
- p = sentinel_sub(p);
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- oldb = pool.getBits(biti);
- pool.setBits(biti, mask);
- }
- return oldb;
- }
-
- return runLocked!(go, otherTime, numOthers)(gcx, p, mask);
- }
-
-
- uint clrAttr(void* p, uint mask) nothrow
- {
- if (!p)
- {
- return 0;
- }
-
- static uint go(Gcx* gcx, void* p, uint mask) nothrow
- {
- Pool* pool = gcx.findPool(p);
- uint oldb = 0;
-
- if (pool)
- {
- p = sentinel_sub(p);
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- oldb = pool.getBits(biti);
- pool.clrBits(biti, mask);
- }
- return oldb;
- }
-
- return runLocked!(go, otherTime, numOthers)(gcx, p, mask);
- }
-
-
- void *malloc(size_t size, uint bits, const TypeInfo ti) nothrow
- {
- if (!size)
- {
- return null;
- }
-
- size_t localAllocSize = void;
-
- auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti);
-
- if (!(bits & BlkAttr.NO_SCAN))
- {
- memset(p + size, 0, localAllocSize - size);
- }
-
- return p;
- }
-
-
- //
- //
- //
- private void *mallocNoSync(size_t size, uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow
- {
- assert(size != 0);
-
- //debug(PRINTF) printf("GC::malloc(size = %d, gcx = %p)\n", size, gcx);
- assert(gcx);
- //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self());
-
- auto p = gcx.alloc(size + SENTINEL_EXTRA, alloc_size, bits);
- if (!p)
- onOutOfMemoryErrorNoGC();
-
- debug (SENTINEL)
- {
- p = sentinel_add(p);
- sentinel_init(p, size);
- alloc_size = size;
- }
- gcx.log_malloc(p, size);
-
- return p;
- }
-
-
- BlkInfo qalloc( size_t size, uint bits, const TypeInfo ti) nothrow
- {
-
- if (!size)
- {
- return BlkInfo.init;
- }
-
- BlkInfo retval;
-
- retval.base = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, retval.size, ti);
-
- if (!(bits & BlkAttr.NO_SCAN))
- {
- memset(retval.base + size, 0, retval.size - size);
- }
-
- retval.attr = bits;
- return retval;
- }
-
-
- void *calloc(size_t size, uint bits, const TypeInfo ti) nothrow
- {
- if (!size)
- {
- return null;
- }
-
- size_t localAllocSize = void;
-
- auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti);
-
- memset(p, 0, size);
- if (!(bits & BlkAttr.NO_SCAN))
- {
- memset(p + size, 0, localAllocSize - size);
- }
-
- return p;
- }
-
-
- void *realloc(void *p, size_t size, uint bits, const TypeInfo ti) nothrow
- {
- size_t localAllocSize = void;
- auto oldp = p;
-
- p = runLocked!(reallocNoSync, mallocTime, numMallocs)(p, size, bits, localAllocSize, ti);
-
- if (p !is oldp && !(bits & BlkAttr.NO_SCAN))
- {
- memset(p + size, 0, localAllocSize - size);
- }
-
- return p;
- }
-
-
- //
- // bits will be set to the resulting bits of the new block
- //
- private void *reallocNoSync(void *p, size_t size, ref uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow
- {
- if (!size)
- { if (p)
- { freeNoSync(p);
- p = null;
- }
- alloc_size = 0;
- }
- else if (!p)
- {
- p = mallocNoSync(size, bits, alloc_size, ti);
- }
- else
- { void *p2;
- size_t psize;
-
- //debug(PRINTF) printf("GC::realloc(p = %p, size = %zu)\n", p, size);
- debug (SENTINEL)
- {
- sentinel_Invariant(p);
- psize = *sentinel_size(p);
- if (psize != size)
- {
- if (psize)
- {
- Pool *pool = gcx.findPool(p);
-
- if (pool)
- {
- auto biti = cast(size_t)(sentinel_sub(p) - pool.baseAddr) >> pool.shiftBy;
-
- if (bits)
- {
- pool.clrBits(biti, ~BlkAttr.NONE);
- pool.setBits(biti, bits);
- }
- else
- {
- bits = pool.getBits(biti);
- }
- }
- }
- p2 = mallocNoSync(size, bits, alloc_size, ti);
- if (psize < size)
- size = psize;
- //debug(PRINTF) printf("\tcopying %d bytes\n",size);
- memcpy(p2, p, size);
- p = p2;
- }
- }
- else
- {
- auto pool = gcx.findPool(p);
- if (pool.isLargeObject)
- {
- auto lpool = cast(LargeObjectPool*) pool;
- psize = lpool.getSize(p); // get allocated size
-
- if (size <= PAGESIZE / 2)
- goto Lmalloc; // switching from large object pool to small object pool
-
- auto psz = psize / PAGESIZE;
- auto newsz = (size + PAGESIZE - 1) / PAGESIZE;
- if (newsz == psz)
- {
- alloc_size = psize;
- return p;
- }
-
- auto pagenum = lpool.pagenumOf(p);
-
- if (newsz < psz)
- { // Shrink in place
- debug (MEMSTOMP) memset(p + size, 0xF2, psize - size);
- lpool.freePages(pagenum + newsz, psz - newsz);
- }
- else if (pagenum + newsz <= pool.npages)
- { // Attempt to expand in place
- foreach (binsz; lpool.pagetable[pagenum + psz .. pagenum + newsz])
- if (binsz != B_FREE)
- goto Lmalloc;
-
- debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize);
- debug(PRINTF) printFreeInfo(pool);
- memset(&lpool.pagetable[pagenum + psz], B_PAGEPLUS, newsz - psz);
- gcx.usedLargePages += newsz - psz;
- lpool.freepages -= (newsz - psz);
- debug(PRINTF) printFreeInfo(pool);
- }
- else
- goto Lmalloc; // does not fit into current pool
-
- lpool.updateOffsets(pagenum);
- if (bits)
- {
- immutable biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
- pool.clrBits(biti, ~BlkAttr.NONE);
- pool.setBits(biti, bits);
- }
- alloc_size = newsz * PAGESIZE;
- return p;
- }
-
- psize = (cast(SmallObjectPool*) pool).getSize(p); // get allocated size
- if (psize < size || // if new size is bigger
- psize > size * 2) // or less than half
- {
- Lmalloc:
- if (psize && pool)
- {
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- if (bits)
- {
- pool.clrBits(biti, ~BlkAttr.NONE);
- pool.setBits(biti, bits);
- }
- else
- {
- bits = pool.getBits(biti);
- }
- }
- p2 = mallocNoSync(size, bits, alloc_size, ti);
- if (psize < size)
- size = psize;
- //debug(PRINTF) printf("\tcopying %d bytes\n",size);
- memcpy(p2, p, size);
- p = p2;
- }
- else
- alloc_size = psize;
- }
- }
- return p;
- }
-
-
- size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow
- {
- return runLocked!(extendNoSync, extendTime, numExtends)(p, minsize, maxsize, ti);
- }
-
-
- //
- //
- //
- private size_t extendNoSync(void* p, size_t minsize, size_t maxsize, const TypeInfo ti = null) nothrow
- in
- {
- assert(minsize <= maxsize);
- }
- body
- {
- //debug(PRINTF) printf("GC::extend(p = %p, minsize = %zu, maxsize = %zu)\n", p, minsize, maxsize);
- debug (SENTINEL)
- {
- return 0;
- }
- else
- {
- auto pool = gcx.findPool(p);
- if (!pool || !pool.isLargeObject)
- return 0;
-
- auto lpool = cast(LargeObjectPool*) pool;
- auto psize = lpool.getSize(p); // get allocated size
- if (psize < PAGESIZE)
- return 0; // cannot extend buckets
-
- auto psz = psize / PAGESIZE;
- auto minsz = (minsize + PAGESIZE - 1) / PAGESIZE;
- auto maxsz = (maxsize + PAGESIZE - 1) / PAGESIZE;
-
- auto pagenum = lpool.pagenumOf(p);
-
- size_t sz;
- for (sz = 0; sz < maxsz; sz++)
- {
- auto i = pagenum + psz + sz;
- if (i == lpool.npages)
- break;
- if (lpool.pagetable[i] != B_FREE)
- { if (sz < minsz)
- return 0;
- break;
- }
- }
- if (sz < minsz)
- return 0;
- debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE);
- memset(lpool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
- lpool.updateOffsets(pagenum);
- lpool.freepages -= sz;
- gcx.usedLargePages += sz;
- return (psz + sz) * PAGESIZE;
- }
- }
-
-
- size_t reserve(size_t size) nothrow
- {
- if (!size)
- {
- return 0;
- }
-
- return runLocked!(reserveNoSync, otherTime, numOthers)(size);
- }
-
-
- //
- //
- //
- private size_t reserveNoSync(size_t size) nothrow
- {
- assert(size != 0);
- assert(gcx);
-
- return gcx.reserve(size);
- }
-
-
- void free(void *p) nothrow
- {
- if (!p || _inFinalizer)
- {
- return;
- }
-
- return runLocked!(freeNoSync, freeTime, numFrees)(p);
- }
-
-
- //
- //
- //
- private void freeNoSync(void *p) nothrow
- {
- debug(PRINTF) printf("Freeing %p\n", cast(size_t) p);
- assert (p);
-
- Pool* pool;
- size_t pagenum;
- Bins bin;
- size_t biti;
-
- // Find which page it is in
- pool = gcx.findPool(p);
- if (!pool) // if not one of ours
- return; // ignore
-
- pagenum = pool.pagenumOf(p);
-
- debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]);
- debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]);
-
- bin = cast(Bins)pool.pagetable[pagenum];
-
- // Verify that the pointer is at the beginning of a block,
- // no action should be taken if p is an interior pointer
- if (bin > B_PAGE) // B_PAGEPLUS or B_FREE
- return;
- if ((sentinel_sub(p) - pool.baseAddr) & (binsize[bin] - 1))
- return;
-
- sentinel_Invariant(p);
- p = sentinel_sub(p);
- biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- pool.clrBits(biti, ~BlkAttr.NONE);
-
- if (pool.isLargeObject) // if large alloc
- {
- assert(bin == B_PAGE);
- auto lpool = cast(LargeObjectPool*) pool;
-
- // Free pages
- size_t npages = lpool.bPageOffsets[pagenum];
- debug (MEMSTOMP) memset(p, 0xF2, npages * PAGESIZE);
- lpool.freePages(pagenum, npages);
- }
- else
- { // Add to free list
- List *list = cast(List*)p;
-
- debug (MEMSTOMP) memset(p, 0xF2, binsize[bin]);
-
- list.next = gcx.bucket[bin];
- list.pool = pool;
- gcx.bucket[bin] = list;
- }
-
- gcx.log_free(sentinel_add(p));
- }
-
-
- void* addrOf(void *p) nothrow
- {
- if (!p)
- {
- return null;
- }
-
- return runLocked!(addrOfNoSync, otherTime, numOthers)(p);
- }
-
-
- //
- //
- //
- void* addrOfNoSync(void *p) nothrow
- {
- if (!p)
- {
- return null;
- }
-
- auto q = gcx.findBase(p);
- if (q)
- q = sentinel_add(q);
- return q;
- }
-
-
- size_t sizeOf(void *p) nothrow
- {
- if (!p)
- {
- return 0;
- }
-
- return runLocked!(sizeOfNoSync, otherTime, numOthers)(p);
- }
-
-
- //
- //
- //
- private size_t sizeOfNoSync(void *p) nothrow
- {
- assert (p);
-
- debug (SENTINEL)
- {
- p = sentinel_sub(p);
- size_t size = gcx.findSize(p);
-
- // Check for interior pointer
- // This depends on:
- // 1) size is a power of 2 for less than PAGESIZE values
- // 2) base of memory pool is aligned on PAGESIZE boundary
- if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
- size = 0;
- return size ? size - SENTINEL_EXTRA : 0;
- }
- else
- {
- size_t size = gcx.findSize(p);
-
- // Check for interior pointer
- // This depends on:
- // 1) size is a power of 2 for less than PAGESIZE values
- // 2) base of memory pool is aligned on PAGESIZE boundary
- if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
- return 0;
- return size;
- }
- }
-
-
- BlkInfo query(void *p) nothrow
- {
- if (!p)
- {
- BlkInfo i;
- return i;
- }
-
- return runLocked!(queryNoSync, otherTime, numOthers)(p);
- }
-
- //
- //
- //
- BlkInfo queryNoSync(void *p) nothrow
- {
- assert(p);
-
- BlkInfo info = gcx.getInfo(p);
- debug(SENTINEL)
- {
- if (info.base)
- {
- info.base = sentinel_add(info.base);
- info.size = *sentinel_size(info.base);
- }
- }
- return info;
- }
-
-
- /**
- * Verify that pointer p:
- * 1) belongs to this memory pool
- * 2) points to the start of an allocated piece of memory
- * 3) is not on a free list
- */
- void check(void *p) nothrow
- {
- if (!p)
- {
- return;
- }
-
- return runLocked!(checkNoSync, otherTime, numOthers)(p);
- }
-
-
- //
- //
- //
- private void checkNoSync(void *p) nothrow
- {
- assert(p);
-
- sentinel_Invariant(p);
- debug (PTRCHECK)
- {
- Pool* pool;
- size_t pagenum;
- Bins bin;
- size_t size;
-
- p = sentinel_sub(p);
- pool = gcx.findPool(p);
- assert(pool);
- pagenum = pool.pagenumOf(p);
- bin = cast(Bins)pool.pagetable[pagenum];
- assert(bin <= B_PAGE);
- size = binsize[bin];
- assert((cast(size_t)p & (size - 1)) == 0);
-
- debug (PTRCHECK2)
- {
- if (bin < B_PAGE)
- {
- // Check that p is not on a free list
- List *list;
-
- for (list = gcx.bucket[bin]; list; list = list.next)
- {
- assert(cast(void*)list != p);
- }
- }
- }
- }
- }
-
-
- void addRoot(void *p) nothrow @nogc
- {
- if (!p)
- {
- return;
- }
-
- gcx.addRoot(p);
- }
-
-
- void removeRoot(void *p) nothrow @nogc
- {
- if (!p)
- {
- return;
- }
-
- gcx.removeRoot(p);
- }
-
-
- @property RootIterator rootIter() @nogc
- {
- return &gcx.rootsApply;
- }
-
-
- void addRange(void *p, size_t sz, const TypeInfo ti = null) nothrow @nogc
- {
- if (!p || !sz)
- {
- return;
- }
-
- gcx.addRange(p, p + sz, ti);
- }
-
-
- void removeRange(void *p) nothrow @nogc
- {
- if (!p)
- {
- return;
- }
-
- gcx.removeRange(p);
- }
-
-
- @property RangeIterator rangeIter() @nogc
- {
- return &gcx.rangesApply;
- }
-
-
- void runFinalizers(in void[] segment) nothrow
- {
- static void go(Gcx* gcx, in void[] segment) nothrow
- {
- gcx.runFinalizers(segment);
- }
- return runLocked!(go, otherTime, numOthers)(gcx, segment);
- }
-
-
- bool inFinalizer() nothrow
- {
- return _inFinalizer;
- }
-
-
- void collect() nothrow
- {
- fullCollect();
- }
-
-
- void collectNoStack() nothrow
- {
- fullCollectNoStack();
- }
-
-
- /**
- * Do full garbage collection.
- * Return number of pages free'd.
- */
- size_t fullCollect() nothrow
- {
- debug(PRINTF) printf("GC.fullCollect()\n");
-
- // Since a finalizer could launch a new thread, we always need to lock
- // when collecting.
- static size_t go(Gcx* gcx) nothrow
- {
- return gcx.fullcollect();
- }
- immutable result = runLocked!go(gcx);
-
- version (none)
- {
- GCStats stats;
-
- getStats(stats);
- debug(PRINTF) printf("heapSize = %zx, freeSize = %zx\n",
- stats.heapSize, stats.freeSize);
- }
-
- gcx.log_collect();
- return result;
- }
-
-
- /**
- * do full garbage collection ignoring roots
- */
- void fullCollectNoStack() nothrow
- {
- // Since a finalizer could launch a new thread, we always need to lock
- // when collecting.
- static size_t go(Gcx* gcx) nothrow
- {
- return gcx.fullcollect(true);
- }
- runLocked!go(gcx);
- }
-
-
- void minimize() nothrow
- {
- static void go(Gcx* gcx) nothrow
- {
- gcx.minimize();
- }
- runLocked!(go, otherTime, numOthers)(gcx);
- }
-
-
- core.memory.GC.Stats stats() nothrow
- {
- typeof(return) ret;
-
- runLocked!(getStatsNoSync, otherTime, numOthers)(ret);
-
- return ret;
- }
-
-
- //
- //
- //
- private void getStatsNoSync(out core.memory.GC.Stats stats) nothrow
- {
- foreach (pool; gcx.pooltable[0 .. gcx.npools])
- {
- foreach (bin; pool.pagetable[0 .. pool.npages])
- {
- if (bin == B_FREE)
- stats.freeSize += PAGESIZE;
- else
- stats.usedSize += PAGESIZE;
- }
- }
-
- size_t freeListSize;
- foreach (n; 0 .. B_PAGE)
- {
- immutable sz = binsize[n];
- for (List *list = gcx.bucket[n]; list; list = list.next)
- freeListSize += sz;
- }
-
- stats.usedSize -= freeListSize;
- stats.freeSize += freeListSize;
- }
-}
-
-
-/* ============================ Gcx =============================== */
-
-enum
-{ PAGESIZE = 4096,
- POOLSIZE = (4096*256),
-}
-
-
-enum
-{
- B_16,
- B_32,
- B_64,
- B_128,
- B_256,
- B_512,
- B_1024,
- B_2048,
- B_PAGE, // start of large alloc
- B_PAGEPLUS, // continuation of large alloc
- B_FREE, // free page
- B_MAX
-}
-
-
-alias ubyte Bins;
-
-
-struct List
-{
- List *next;
- Pool *pool;
-}
-
-
-immutable uint[B_MAX] binsize = [ 16,32,64,128,256,512,1024,2048,4096 ];
-immutable size_t[B_MAX] notbinsize = [ ~(16-1),~(32-1),~(64-1),~(128-1),~(256-1),
- ~(512-1),~(1024-1),~(2048-1),~(4096-1) ];
-
-alias PageBits = GCBits.wordtype[PAGESIZE / 16 / GCBits.BITS_PER_WORD];
-static assert(PAGESIZE % (GCBits.BITS_PER_WORD * 16) == 0);
-
-private void set(ref PageBits bits, size_t i) @nogc pure nothrow
-{
- assert(i < PageBits.sizeof * 8);
- bts(bits.ptr, i);
-}
-
-/* ============================ Gcx =============================== */
-
-struct Gcx
-{
- import core.internal.spinlock;
- auto rootsLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
- auto rangesLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
- Treap!Root roots;
- Treap!Range ranges;
-
- bool log; // turn on logging
- debug(INVARIANT) bool initialized;
- uint disabled; // turn off collections if >0
-
- import gc.pooltable;
- @property size_t npools() pure const nothrow { return pooltable.length; }
- PoolTable!Pool pooltable;
-
- List*[B_PAGE] bucket; // free list for each small size
-
- // run a collection when reaching those thresholds (number of used pages)
- float smallCollectThreshold, largeCollectThreshold;
- uint usedSmallPages, usedLargePages;
- // total number of mapped pages
- uint mappedPages;
-
- void initialize()
- {
- (cast(byte*)&this)[0 .. Gcx.sizeof] = 0;
- log_init();
- roots.initialize();
- ranges.initialize();
- smallCollectThreshold = largeCollectThreshold = 0.0f;
- usedSmallPages = usedLargePages = 0;
- mappedPages = 0;
- //printf("gcx = %p, self = %x\n", &this, self);
- debug(INVARIANT) initialized = true;
- }
-
-
- void Dtor()
- {
- if (config.profile)
- {
- printf("\tNumber of collections: %llu\n", cast(ulong)numCollections);
- printf("\tTotal GC prep time: %lld milliseconds\n",
- prepTime.total!("msecs"));
- printf("\tTotal mark time: %lld milliseconds\n",
- markTime.total!("msecs"));
- printf("\tTotal sweep time: %lld milliseconds\n",
- sweepTime.total!("msecs"));
- printf("\tTotal page recovery time: %lld milliseconds\n",
- recoverTime.total!("msecs"));
- long maxPause = maxPauseTime.total!("msecs");
- printf("\tMax Pause Time: %lld milliseconds\n", maxPause);
- long gcTime = (recoverTime + sweepTime + markTime + prepTime).total!("msecs");
- printf("\tGrand total GC time: %lld milliseconds\n", gcTime);
- long pauseTime = (markTime + prepTime).total!("msecs");
-
- char[30] apitxt;
- apitxt[0] = 0;
- debug(PROFILE_API) if (config.profile > 1)
- {
- static Duration toDuration(long dur)
- {
- return MonoTime(dur) - MonoTime(0);
- }
-
- printf("\n");
- printf("\tmalloc: %llu calls, %lld ms\n", cast(ulong)numMallocs, toDuration(mallocTime).total!"msecs");
- printf("\trealloc: %llu calls, %lld ms\n", cast(ulong)numReallocs, toDuration(reallocTime).total!"msecs");
- printf("\tfree: %llu calls, %lld ms\n", cast(ulong)numFrees, toDuration(freeTime).total!"msecs");
- printf("\textend: %llu calls, %lld ms\n", cast(ulong)numExtends, toDuration(extendTime).total!"msecs");
- printf("\tother: %llu calls, %lld ms\n", cast(ulong)numOthers, toDuration(otherTime).total!"msecs");
- printf("\tlock time: %lld ms\n", toDuration(lockTime).total!"msecs");
-
- long apiTime = mallocTime + reallocTime + freeTime + extendTime + otherTime + lockTime;
- printf("\tGC API: %lld ms\n", toDuration(apiTime).total!"msecs");
- sprintf(apitxt.ptr, " API%5ld ms", toDuration(apiTime).total!"msecs");
- }
-
- printf("GC summary:%5lld MB,%5lld GC%5lld ms, Pauses%5lld ms <%5lld ms%s\n",
- cast(long) maxPoolMemory >> 20, cast(ulong)numCollections, gcTime,
- pauseTime, maxPause, apitxt.ptr);
- }
-
- debug(INVARIANT) initialized = false;
-
- for (size_t i = 0; i < npools; i++)
- {
- Pool *pool = pooltable[i];
- mappedPages -= pool.npages;
- pool.Dtor();
- cstdlib.free(pool);
- }
- assert(!mappedPages);
- pooltable.Dtor();
-
- roots.removeAll();
- ranges.removeAll();
- toscan.reset();
- }
-
-
- void Invariant() const { }
-
- debug(INVARIANT)
- invariant()
- {
- if (initialized)
- {
- //printf("Gcx.invariant(): this = %p\n", &this);
- pooltable.Invariant();
-
- rangesLock.lock();
- foreach (range; ranges)
- {
- assert(range.pbot);
- assert(range.ptop);
- assert(range.pbot <= range.ptop);
- }
- rangesLock.unlock();
-
- for (size_t i = 0; i < B_PAGE; i++)
- {
- for (auto list = cast(List*)bucket[i]; list; list = list.next)
- {
- }
- }
- }
- }
-
-
- /**
- *
- */
- void addRoot(void *p) nothrow @nogc
- {
- rootsLock.lock();
- scope (failure) rootsLock.unlock();
- roots.insert(Root(p));
- rootsLock.unlock();
- }
-
-
- /**
- *
- */
- void removeRoot(void *p) nothrow @nogc
- {
- rootsLock.lock();
- scope (failure) rootsLock.unlock();
- roots.remove(Root(p));
- rootsLock.unlock();
- }
-
-
- /**
- *
- */
- int rootsApply(scope int delegate(ref Root) nothrow dg) nothrow
- {
- rootsLock.lock();
- scope (failure) rootsLock.unlock();
- auto ret = roots.opApply(dg);
- rootsLock.unlock();
- return ret;
- }
-
-
- /**
- *
- */
- void addRange(void *pbot, void *ptop, const TypeInfo ti) nothrow @nogc
- {
- //debug(PRINTF) printf("Thread %x ", pthread_self());
- debug(PRINTF) printf("%p.Gcx::addRange(%p, %p)\n", &this, pbot, ptop);
- rangesLock.lock();
- scope (failure) rangesLock.unlock();
- ranges.insert(Range(pbot, ptop));
- rangesLock.unlock();
- }
-
-
- /**
- *
- */
- void removeRange(void *pbot) nothrow @nogc
- {
- //debug(PRINTF) printf("Thread %x ", pthread_self());
- debug(PRINTF) printf("Gcx.removeRange(%p)\n", pbot);
- rangesLock.lock();
- scope (failure) rangesLock.unlock();
- ranges.remove(Range(pbot, pbot)); // only pbot is used, see Range.opCmp
- rangesLock.unlock();
-
- // debug(PRINTF) printf("Wrong thread\n");
- // This is a fatal error, but ignore it.
- // The problem is that we can get a Close() call on a thread
- // other than the one the range was allocated on.
- //assert(zero);
- }
-
- /**
- *
- */
- int rangesApply(scope int delegate(ref Range) nothrow dg) nothrow
- {
- rangesLock.lock();
- scope (failure) rangesLock.unlock();
- auto ret = ranges.opApply(dg);
- rangesLock.unlock();
- return ret;
- }
-
-
- /**
- *
- */
- void runFinalizers(in void[] segment) nothrow
- {
- ConservativeGC._inFinalizer = true;
- scope (failure) ConservativeGC._inFinalizer = false;
-
- foreach (pool; pooltable[0 .. npools])
- {
- if (!pool.finals.nbits) continue;
-
- if (pool.isLargeObject)
- {
- auto lpool = cast(LargeObjectPool*) pool;
- lpool.runFinalizers(segment);
- }
- else
- {
- auto spool = cast(SmallObjectPool*) pool;
- spool.runFinalizers(segment);
- }
- }
- ConservativeGC._inFinalizer = false;
- }
-
- Pool* findPool(void* p) pure nothrow
- {
- return pooltable.findPool(p);
- }
-
- /**
- * Find base address of block containing pointer p.
- * Returns null if not a gc'd pointer
- */
- void* findBase(void *p) nothrow
- {
- Pool *pool;
-
- pool = findPool(p);
- if (pool)
- {
- size_t offset = cast(size_t)(p - pool.baseAddr);
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pool.pagetable[pn];
-
- // Adjust bit to be at start of allocated memory block
- if (bin <= B_PAGE)
- {
- return pool.baseAddr + (offset & notbinsize[bin]);
- }
- else if (bin == B_PAGEPLUS)
- {
- auto pageOffset = pool.bPageOffsets[pn];
- offset -= pageOffset * PAGESIZE;
- pn -= pageOffset;
-
- return pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
- }
- else
- {
- // we are in a B_FREE page
- assert(bin == B_FREE);
- return null;
- }
- }
- return null;
- }
-
-
- /**
- * Find size of pointer p.
- * Returns 0 if not a gc'd pointer
- */
- size_t findSize(void *p) nothrow
- {
- Pool* pool = findPool(p);
- if (pool)
- return pool.slGetSize(p);
- return 0;
- }
-
- /**
- *
- */
- BlkInfo getInfo(void* p) nothrow
- {
- Pool* pool = findPool(p);
- if (pool)
- return pool.slGetInfo(p);
- return BlkInfo();
- }
-
- /**
- * Computes the bin table using CTFE.
- */
- static byte[2049] ctfeBins() nothrow
- {
- byte[2049] ret;
- size_t p = 0;
- for (Bins b = B_16; b <= B_2048; b++)
- for ( ; p <= binsize[b]; p++)
- ret[p] = b;
-
- return ret;
- }
-
- static const byte[2049] binTable = ctfeBins();
-
- /**
- * Allocate a new pool of at least size bytes.
- * Sort it into pooltable[].
- * Mark all memory in the pool as B_FREE.
- * Return the actual number of bytes reserved or 0 on error.
- */
- size_t reserve(size_t size) nothrow
- {
- size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
-
- // Assume reserve() is for small objects.
- Pool* pool = newPool(npages, false);
-
- if (!pool)
- return 0;
- return pool.npages * PAGESIZE;
- }
-
- /**
- * Update the thresholds for when to collect the next time
- */
- void updateCollectThresholds() nothrow
- {
- static float max(float a, float b) nothrow
- {
- return a >= b ? a : b;
- }
-
- // instantly increases, slowly decreases
- static float smoothDecay(float oldVal, float newVal) nothrow
- {
- // decay to 63.2% of newVal over 5 collections
- // http://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
- enum alpha = 1.0 / (5 + 1);
- immutable decay = (newVal - oldVal) * alpha + oldVal;
- return max(newVal, decay);
- }
-
- immutable smTarget = usedSmallPages * config.heapSizeFactor;
- smallCollectThreshold = smoothDecay(smallCollectThreshold, smTarget);
- immutable lgTarget = usedLargePages * config.heapSizeFactor;
- largeCollectThreshold = smoothDecay(largeCollectThreshold, lgTarget);
- }
-
- /**
- * Minimizes physical memory usage by returning free pools to the OS.
- */
- void minimize() nothrow
- {
- debug(PRINTF) printf("Minimizing.\n");
-
- foreach (pool; pooltable.minimize())
- {
- debug(PRINTF) printFreeInfo(pool);
- mappedPages -= pool.npages;
- pool.Dtor();
- cstdlib.free(pool);
- }
-
- debug(PRINTF) printf("Done minimizing.\n");
- }
-
- private @property bool lowMem() const nothrow
- {
- return isLowOnMem(mappedPages * PAGESIZE);
- }
-
- void* alloc(size_t size, ref size_t alloc_size, uint bits) nothrow
- {
- return size <= 2048 ? smallAlloc(binTable[size], alloc_size, bits)
- : bigAlloc(size, alloc_size, bits);
- }
-
- void* smallAlloc(Bins bin, ref size_t alloc_size, uint bits) nothrow
- {
- alloc_size = binsize[bin];
-
- void* p;
- bool tryAlloc() nothrow
- {
- if (!bucket[bin])
- {
- bucket[bin] = allocPage(bin);
- if (!bucket[bin])
- return false;
- }
- p = bucket[bin];
- return true;
- }
-
- if (!tryAlloc())
- {
- if (!lowMem && (disabled || usedSmallPages < smallCollectThreshold))
- {
- // disabled or threshold not reached => allocate a new pool instead of collecting
- if (!newPool(1, false))
- {
- // out of memory => try to free some memory
- fullcollect();
- if (lowMem) minimize();
- }
- }
- else
- {
- fullcollect();
- if (lowMem) minimize();
- }
- // tryAlloc will succeed if a new pool was allocated above, if it fails allocate a new pool now
- if (!tryAlloc() && (!newPool(1, false) || !tryAlloc()))
- // out of luck or memory
- onOutOfMemoryErrorNoGC();
- }
- assert(p !is null);
-
- // Return next item from free list
- bucket[bin] = (cast(List*)p).next;
- auto pool = (cast(List*)p).pool;
- if (bits)
- pool.setBits((p - pool.baseAddr) >> pool.shiftBy, bits);
- //debug(PRINTF) printf("\tmalloc => %p\n", p);
- debug (MEMSTOMP) memset(p, 0xF0, alloc_size);
- return p;
- }
-
- /**
- * Allocate a chunk of memory that is larger than a page.
- * Return null if out of memory.
- */
- void* bigAlloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti = null) nothrow
- {
- debug(PRINTF) printf("In bigAlloc. Size: %d\n", size);
-
- LargeObjectPool* pool;
- size_t pn;
- immutable npages = (size + PAGESIZE - 1) / PAGESIZE;
- if (npages == 0)
- onOutOfMemoryErrorNoGC(); // size just below size_t.max requested
-
- bool tryAlloc() nothrow
- {
- foreach (p; pooltable[0 .. npools])
- {
- if (!p.isLargeObject || p.freepages < npages)
- continue;
- auto lpool = cast(LargeObjectPool*) p;
- if ((pn = lpool.allocPages(npages)) == OPFAIL)
- continue;
- pool = lpool;
- return true;
- }
- return false;
- }
-
- bool tryAllocNewPool() nothrow
- {
- pool = cast(LargeObjectPool*) newPool(npages, true);
- if (!pool) return false;
- pn = pool.allocPages(npages);
- assert(pn != OPFAIL);
- return true;
- }
-
- if (!tryAlloc())
- {
- if (!lowMem && (disabled || usedLargePages < largeCollectThreshold))
- {
- // disabled or threshold not reached => allocate a new pool instead of collecting
- if (!tryAllocNewPool())
- {
- // disabled but out of memory => try to free some memory
- fullcollect();
- minimize();
- }
- }
- else
- {
- fullcollect();
- minimize();
- }
- // If alloc didn't yet succeed retry now that we collected/minimized
- if (!pool && !tryAlloc() && !tryAllocNewPool())
- // out of luck or memory
- return null;
- }
- assert(pool);
-
- debug(PRINTF) printFreeInfo(&pool.base);
- pool.pagetable[pn] = B_PAGE;
- if (npages > 1)
- memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1);
- pool.updateOffsets(pn);
- usedLargePages += npages;
- pool.freepages -= npages;
-
- debug(PRINTF) printFreeInfo(&pool.base);
-
- auto p = pool.baseAddr + pn * PAGESIZE;
- debug(PRINTF) printf("Got large alloc: %p, pt = %d, np = %d\n", p, pool.pagetable[pn], npages);
- debug (MEMSTOMP) memset(p, 0xF1, size);
- alloc_size = npages * PAGESIZE;
- //debug(PRINTF) printf("\tp = %p\n", p);
-
- if (bits)
- pool.setBits(pn, bits);
- return p;
- }
-
-
- /**
- * Allocate a new pool with at least npages in it.
- * Sort it into pooltable[].
- * Return null if failed.
- */
- Pool *newPool(size_t npages, bool isLargeObject) nothrow
- {
- //debug(PRINTF) printf("************Gcx::newPool(npages = %d)****************\n", npages);
-
- // Minimum of POOLSIZE
- size_t minPages = (config.minPoolSize << 20) / PAGESIZE;
- if (npages < minPages)
- npages = minPages;
- else if (npages > minPages)
- { // Give us 150% of requested size, so there's room to extend
- auto n = npages + (npages >> 1);
- if (n < size_t.max/PAGESIZE)
- npages = n;
- }
-
- // Allocate successively larger pools up to 8 megs
- if (npools)
- { size_t n;
-
- n = config.minPoolSize + config.incPoolSize * npools;
- if (n > config.maxPoolSize)
- n = config.maxPoolSize; // cap pool size
- n *= (1 << 20) / PAGESIZE; // convert MB to pages
- if (npages < n)
- npages = n;
- }
-
- //printf("npages = %d\n", npages);
-
- auto pool = cast(Pool *)cstdlib.calloc(1, isLargeObject ? LargeObjectPool.sizeof : SmallObjectPool.sizeof);
- if (pool)
- {
- pool.initialize(npages, isLargeObject);
- if (!pool.baseAddr || !pooltable.insert(pool))
- {
- pool.Dtor();
- cstdlib.free(pool);
- return null;
- }
- }
-
- mappedPages += npages;
-
- if (config.profile)
- {
- if (mappedPages * PAGESIZE > maxPoolMemory)
- maxPoolMemory = mappedPages * PAGESIZE;
- }
- return pool;
- }
-
- /**
- * Allocate a page of bin's.
- * Returns:
- * head of a single linked list of new entries
- */
- List* allocPage(Bins bin) nothrow
- {
- //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin);
- for (size_t n = 0; n < npools; n++)
- {
- Pool* pool = pooltable[n];
- if (pool.isLargeObject)
- continue;
- if (List* p = (cast(SmallObjectPool*)pool).allocPage(bin))
- {
- ++usedSmallPages;
- return p;
- }
- }
- return null;
- }
-
- static struct ToScanStack
- {
- nothrow:
- @disable this(this);
-
- void reset()
- {
- _length = 0;
- os_mem_unmap(_p, _cap * Range.sizeof);
- _p = null;
- _cap = 0;
- }
-
- void push(Range rng)
- {
- if (_length == _cap) grow();
- _p[_length++] = rng;
- }
-
- Range pop()
- in { assert(!empty); }
- body
- {
- return _p[--_length];
- }
-
- ref inout(Range) opIndex(size_t idx) inout
- in { assert(idx < _length); }
- body
- {
- return _p[idx];
- }
-
- @property size_t length() const { return _length; }
- @property bool empty() const { return !length; }
-
- private:
- void grow()
- {
- enum initSize = 64 * 1024; // Windows VirtualAlloc granularity
- immutable ncap = _cap ? 2 * _cap : initSize / Range.sizeof;
- auto p = cast(Range*)os_mem_map(ncap * Range.sizeof);
- if (p is null) onOutOfMemoryErrorNoGC();
- if (_p !is null)
- {
- p[0 .. _length] = _p[0 .. _length];
- os_mem_unmap(_p, _cap * Range.sizeof);
- }
- _p = p;
- _cap = ncap;
- }
-
- size_t _length;
- Range* _p;
- size_t _cap;
- }
-
- ToScanStack toscan;
-
- /**
- * Search a range of memory values and mark any pointers into the GC pool.
- */
- void mark(void *pbot, void *ptop) scope nothrow
- {
- void **p1 = cast(void **)pbot;
- void **p2 = cast(void **)ptop;
-
- // limit the amount of ranges added to the toscan stack
- enum FANOUT_LIMIT = 32;
- size_t stackPos;
- Range[FANOUT_LIMIT] stack = void;
-
- Lagain:
- size_t pcache = 0;
-
- // let dmd allocate a register for this.pools
- auto pools = pooltable.pools;
- const highpool = pooltable.npools - 1;
- const minAddr = pooltable.minAddr;
- const maxAddr = pooltable.maxAddr;
-
- //printf("marking range: [%p..%p] (%#zx)\n", p1, p2, cast(size_t)p2 - cast(size_t)p1);
- Lnext: for (; p1 < p2; p1++)
- {
- auto p = *p1;
-
- //if (log) debug(PRINTF) printf("\tmark %p\n", p);
- if (p >= minAddr && p < maxAddr)
- {
- if ((cast(size_t)p & ~cast(size_t)(PAGESIZE-1)) == pcache)
- continue;
-
- Pool* pool = void;
- size_t low = 0;
- size_t high = highpool;
- while (true)
- {
- size_t mid = (low + high) >> 1;
- pool = pools[mid];
- if (p < pool.baseAddr)
- high = mid - 1;
- else if (p >= pool.topAddr)
- low = mid + 1;
- else break;
-
- if (low > high)
- continue Lnext;
- }
- size_t offset = cast(size_t)(p - pool.baseAddr);
- size_t biti = void;
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pool.pagetable[pn];
- void* base = void;
-
- //debug(PRINTF) printf("\t\tfound pool %p, base=%p, pn = %zd, bin = %d, biti = x%x\n", pool, pool.baseAddr, pn, bin, biti);
-
- // Adjust bit to be at start of allocated memory block
- if (bin < B_PAGE)
- {
- // We don't care abou setting pointsToBase correctly
- // because it's ignored for small object pools anyhow.
- auto offsetBase = offset & notbinsize[bin];
- biti = offsetBase >> pool.shiftBy;
- base = pool.baseAddr + offsetBase;
- //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
-
- if (!pool.mark.set(biti) && !pool.noscan.test(biti)) {
- stack[stackPos++] = Range(base, base + binsize[bin]);
- if (stackPos == stack.length)
- break;
- }
- }
- else if (bin == B_PAGE)
- {
- auto offsetBase = offset & notbinsize[bin];
- base = pool.baseAddr + offsetBase;
- biti = offsetBase >> pool.shiftBy;
- //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
-
- pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
-
- // For the NO_INTERIOR attribute. This tracks whether
- // the pointer is an interior pointer or points to the
- // base address of a block.
- bool pointsToBase = (base == sentinel_sub(p));
- if (!pointsToBase && pool.nointerior.nbits && pool.nointerior.test(biti))
- continue;
-
- if (!pool.mark.set(biti) && !pool.noscan.test(biti)) {
- stack[stackPos++] = Range(base, base + pool.bPageOffsets[pn] * PAGESIZE);
- if (stackPos == stack.length)
- break;
- }
- }
- else if (bin == B_PAGEPLUS)
- {
- pn -= pool.bPageOffsets[pn];
- base = pool.baseAddr + (pn * PAGESIZE);
- biti = pn * (PAGESIZE >> pool.shiftBy);
-
- pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
- if (pool.nointerior.nbits && pool.nointerior.test(biti))
- continue;
-
- if (!pool.mark.set(biti) && !pool.noscan.test(biti)) {
- stack[stackPos++] = Range(base, base + pool.bPageOffsets[pn] * PAGESIZE);
- if (stackPos == stack.length)
- break;
- }
- }
- else
- {
- // Don't mark bits in B_FREE pages
- assert(bin == B_FREE);
- continue;
- }
- }
- }
-
- Range next=void;
- if (p1 < p2)
- {
- // local stack is full, push it to the global stack
- assert(stackPos == stack.length);
- toscan.push(Range(p1, p2));
- // reverse order for depth-first-order traversal
- foreach_reverse (ref rng; stack[0 .. $ - 1])
- toscan.push(rng);
- stackPos = 0;
- next = stack[$-1];
- }
- else if (stackPos)
- {
- // pop range from local stack and recurse
- next = stack[--stackPos];
- }
- else if (!toscan.empty)
- {
- // pop range from global stack and recurse
- next = toscan.pop();
- }
- else
- {
- // nothing more to do
- return;
- }
- p1 = cast(void**)next.pbot;
- p2 = cast(void**)next.ptop;
- // printf(" pop [%p..%p] (%#zx)\n", p1, p2, cast(size_t)p2 - cast(size_t)p1);
- goto Lagain;
- }
-
- // collection step 1: prepare freebits and mark bits
- void prepare() nothrow
- {
- size_t n;
- Pool* pool;
-
- for (n = 0; n < npools; n++)
- {
- pool = pooltable[n];
- pool.mark.zero();
- if (!pool.isLargeObject) pool.freebits.zero();
- }
-
- debug(COLLECT_PRINTF) printf("Set bits\n");
-
- // Mark each free entry, so it doesn't get scanned
- for (n = 0; n < B_PAGE; n++)
- {
- for (List *list = bucket[n]; list; list = list.next)
- {
- pool = list.pool;
- assert(pool);
- pool.freebits.set(cast(size_t)(cast(void*)list - pool.baseAddr) / 16);
- }
- }
-
- debug(COLLECT_PRINTF) printf("Marked free entries.\n");
-
- for (n = 0; n < npools; n++)
- {
- pool = pooltable[n];
- if (!pool.isLargeObject)
- {
- pool.mark.copy(&pool.freebits);
- }
- }
- }
-
- // collection step 2: mark roots and heap
- void markAll(bool nostack) nothrow
- {
- if (!nostack)
- {
- debug(COLLECT_PRINTF) printf("\tscan stacks.\n");
- // Scan stacks and registers for each paused thread
- thread_scanAll(&mark);
- }
-
- // Scan roots[]
- debug(COLLECT_PRINTF) printf("\tscan roots[]\n");
- foreach (root; roots)
- {
- mark(cast(void*)&root.proot, cast(void*)(&root.proot + 1));
- }
-
- // Scan ranges[]
- debug(COLLECT_PRINTF) printf("\tscan ranges[]\n");
- //log++;
- foreach (range; ranges)
- {
- debug(COLLECT_PRINTF) printf("\t\t%p .. %p\n", range.pbot, range.ptop);
- mark(range.pbot, range.ptop);
- }
- //log--;
- }
-
- // collection step 3: free all unreferenced objects
- size_t sweep() nothrow
- {
- // Free up everything not marked
- debug(COLLECT_PRINTF) printf("\tfree'ing\n");
- size_t freedLargePages;
- size_t freed;
- for (size_t n = 0; n < npools; n++)
- {
- size_t pn;
- Pool* pool = pooltable[n];
-
- if (pool.isLargeObject)
- {
- for (pn = 0; pn < pool.npages; pn++)
- {
- Bins bin = cast(Bins)pool.pagetable[pn];
- if (bin > B_PAGE) continue;
- size_t biti = pn;
-
- if (!pool.mark.test(biti))
- {
- void *p = pool.baseAddr + pn * PAGESIZE;
- void* q = sentinel_add(p);
- sentinel_Invariant(q);
-
- if (pool.finals.nbits && pool.finals.clear(biti))
- {
- size_t size = pool.bPageOffsets[pn] * PAGESIZE - SENTINEL_EXTRA;
- uint attr = pool.getBits(biti);
- rt_finalizeFromGC(q, size, attr);
- }
-
- pool.clrBits(biti, ~BlkAttr.NONE ^ BlkAttr.FINALIZE);
-
- debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
- log_free(q);
- pool.pagetable[pn] = B_FREE;
- if (pn < pool.searchStart) pool.searchStart = pn;
- freedLargePages++;
- pool.freepages++;
-
- debug (MEMSTOMP) memset(p, 0xF3, PAGESIZE);
- while (pn + 1 < pool.npages && pool.pagetable[pn + 1] == B_PAGEPLUS)
- {
- pn++;
- pool.pagetable[pn] = B_FREE;
-
- // Don't need to update searchStart here because
- // pn is guaranteed to be greater than last time
- // we updated it.
-
- pool.freepages++;
- freedLargePages++;
-
- debug (MEMSTOMP)
- { p += PAGESIZE;
- memset(p, 0xF3, PAGESIZE);
- }
- }
- pool.largestFree = pool.freepages; // invalidate
- }
- }
- }
- else
- {
-
- for (pn = 0; pn < pool.npages; pn++)
- {
- Bins bin = cast(Bins)pool.pagetable[pn];
-
- if (bin < B_PAGE)
- {
- immutable size = binsize[bin];
- void *p = pool.baseAddr + pn * PAGESIZE;
- void *ptop = p + PAGESIZE;
- immutable base = pn * (PAGESIZE/16);
- immutable bitstride = size / 16;
-
- bool freeBits;
- PageBits toFree;
-
- for (size_t i; p < ptop; p += size, i += bitstride)
- {
- immutable biti = base + i;
-
- if (!pool.mark.test(biti))
- {
- void* q = sentinel_add(p);
- sentinel_Invariant(q);
-
- if (pool.finals.nbits && pool.finals.test(biti))
- rt_finalizeFromGC(q, size - SENTINEL_EXTRA, pool.getBits(biti));
-
- freeBits = true;
- toFree.set(i);
-
- debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p);
- log_free(sentinel_add(p));
-
- debug (MEMSTOMP) memset(p, 0xF3, size);
-
- freed += size;
- }
- }
-
- if (freeBits)
- pool.freePageBits(pn, toFree);
- }
- }
- }
- }
-
- assert(freedLargePages <= usedLargePages);
- usedLargePages -= freedLargePages;
- debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedLargePages, npools);
- return freedLargePages;
- }
-
- // collection step 4: recover pages with no live objects, rebuild free lists
- size_t recover() nothrow
- {
- // init tail list
- List**[B_PAGE] tail = void;
- foreach (i, ref next; tail)
- next = &bucket[i];
-
- // Free complete pages, rebuild free list
- debug(COLLECT_PRINTF) printf("\tfree complete pages\n");
- size_t freedSmallPages;
- for (size_t n = 0; n < npools; n++)
- {
- size_t pn;
- Pool* pool = pooltable[n];
-
- if (pool.isLargeObject)
- continue;
-
- for (pn = 0; pn < pool.npages; pn++)
- {
- Bins bin = cast(Bins)pool.pagetable[pn];
- size_t biti;
- size_t u;
-
- if (bin < B_PAGE)
- {
- size_t size = binsize[bin];
- size_t bitstride = size / 16;
- size_t bitbase = pn * (PAGESIZE / 16);
- size_t bittop = bitbase + (PAGESIZE / 16);
- void* p;
-
- biti = bitbase;
- for (biti = bitbase; biti < bittop; biti += bitstride)
- {
- if (!pool.freebits.test(biti))
- goto Lnotfree;
- }
- pool.pagetable[pn] = B_FREE;
- if (pn < pool.searchStart) pool.searchStart = pn;
- pool.freepages++;
- freedSmallPages++;
- continue;
-
- Lnotfree:
- p = pool.baseAddr + pn * PAGESIZE;
- for (u = 0; u < PAGESIZE; u += size)
- {
- biti = bitbase + u / 16;
- if (!pool.freebits.test(biti))
- continue;
- auto elem = cast(List *)(p + u);
- elem.pool = pool;
- *tail[bin] = elem;
- tail[bin] = &elem.next;
- }
- }
- }
- }
- // terminate tail list
- foreach (ref next; tail)
- *next = null;
-
- assert(freedSmallPages <= usedSmallPages);
- usedSmallPages -= freedSmallPages;
- debug(COLLECT_PRINTF) printf("\trecovered pages = %d\n", freedSmallPages);
- return freedSmallPages;
- }
-
- /**
- * Return number of full pages free'd.
- */
- size_t fullcollect(bool nostack = false) nothrow
- {
- MonoTime start, stop, begin;
-
- if (config.profile)
- {
- begin = start = currTime;
- }
-
- debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
- //printf("\tpool address range = %p .. %p\n", minAddr, maxAddr);
-
- {
- // lock roots and ranges around suspending threads b/c they're not reentrant safe
- rangesLock.lock();
- rootsLock.lock();
- scope (exit)
- {
- rangesLock.unlock();
- rootsLock.unlock();
- }
- thread_suspendAll();
-
- prepare();
-
- if (config.profile)
- {
- stop = currTime;
- prepTime += (stop - start);
- start = stop;
- }
-
- markAll(nostack);
-
- thread_processGCMarks(&isMarked);
- thread_resumeAll();
- }
-
- if (config.profile)
- {
- stop = currTime;
- markTime += (stop - start);
- Duration pause = stop - begin;
- if (pause > maxPauseTime)
- maxPauseTime = pause;
- start = stop;
- }
-
- ConservativeGC._inFinalizer = true;
- size_t freedLargePages=void;
- {
- scope (failure) ConservativeGC._inFinalizer = false;
- freedLargePages = sweep();
- ConservativeGC._inFinalizer = false;
- }
-
- if (config.profile)
- {
- stop = currTime;
- sweepTime += (stop - start);
- start = stop;
- }
-
- immutable freedSmallPages = recover();
-
- if (config.profile)
- {
- stop = currTime;
- recoverTime += (stop - start);
- ++numCollections;
- }
-
- updateCollectThresholds();
-
- return freedLargePages + freedSmallPages;
- }
-
- /**
- * Returns true if the addr lies within a marked block.
- *
- * Warning! This should only be called while the world is stopped inside
- * the fullcollect function.
- */
- int isMarked(void *addr) scope nothrow
- {
- // first, we find the Pool this block is in, then check to see if the
- // mark bit is clear.
- auto pool = findPool(addr);
- if (pool)
- {
- auto offset = cast(size_t)(addr - pool.baseAddr);
- auto pn = offset / PAGESIZE;
- auto bins = cast(Bins)pool.pagetable[pn];
- size_t biti = void;
- if (bins <= B_PAGE)
- {
- biti = (offset & notbinsize[bins]) >> pool.shiftBy;
- }
- else if (bins == B_PAGEPLUS)
- {
- pn -= pool.bPageOffsets[pn];
- biti = pn * (PAGESIZE >> pool.shiftBy);
- }
- else // bins == B_FREE
- {
- assert(bins == B_FREE);
- return IsMarked.no;
- }
- return pool.mark.test(biti) ? IsMarked.yes : IsMarked.no;
- }
- return IsMarked.unknown;
- }
-
-
- /***** Leak Detector ******/
-
-
- debug (LOGGING)
- {
- LogArray current;
- LogArray prev;
-
-
- void log_init()
- {
- //debug(PRINTF) printf("+log_init()\n");
- current.reserve(1000);
- prev.reserve(1000);
- //debug(PRINTF) printf("-log_init()\n");
- }
-
-
- void log_malloc(void *p, size_t size) nothrow
- {
- //debug(PRINTF) printf("+log_malloc(p = %p, size = %zd)\n", p, size);
- Log log;
-
- log.p = p;
- log.size = size;
- log.line = GC.line;
- log.file = GC.file;
- log.parent = null;
-
- GC.line = 0;
- GC.file = null;
-
- current.push(log);
- //debug(PRINTF) printf("-log_malloc()\n");
- }
-
-
- void log_free(void *p) nothrow
- {
- //debug(PRINTF) printf("+log_free(%p)\n", p);
- auto i = current.find(p);
- if (i == OPFAIL)
- {
- debug(PRINTF) printf("free'ing unallocated memory %p\n", p);
- }
- else
- current.remove(i);
- //debug(PRINTF) printf("-log_free()\n");
- }
-
-
- void log_collect() nothrow
- {
- //debug(PRINTF) printf("+log_collect()\n");
- // Print everything in current that is not in prev
-
- debug(PRINTF) printf("New pointers this cycle: --------------------------------\n");
- size_t used = 0;
- for (size_t i = 0; i < current.dim; i++)
- {
- auto j = prev.find(current.data[i].p);
- if (j == OPFAIL)
- current.data[i].print();
- else
- used++;
- }
-
- debug(PRINTF) printf("All roots this cycle: --------------------------------\n");
- for (size_t i = 0; i < current.dim; i++)
- {
- void* p = current.data[i].p;
- if (!findPool(current.data[i].parent))
- {
- auto j = prev.find(current.data[i].p);
- debug(PRINTF) printf(j == OPFAIL ? "N" : " ");
- current.data[i].print();
- }
- }
-
- debug(PRINTF) printf("Used = %d-------------------------------------------------\n", used);
- prev.copy(&current);
-
- debug(PRINTF) printf("-log_collect()\n");
- }
-
-
- void log_parent(void *p, void *parent) nothrow
- {
- //debug(PRINTF) printf("+log_parent()\n");
- auto i = current.find(p);
- if (i == OPFAIL)
- {
- debug(PRINTF) printf("parent'ing unallocated memory %p, parent = %p\n", p, parent);
- Pool *pool;
- pool = findPool(p);
- assert(pool);
- size_t offset = cast(size_t)(p - pool.baseAddr);
- size_t biti;
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pool.pagetable[pn];
- biti = (offset & notbinsize[bin]);
- debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti);
- }
- else
- {
- current.data[i].parent = parent;
- }
- //debug(PRINTF) printf("-log_parent()\n");
- }
-
- }
- else
- {
- void log_init() nothrow { }
- void log_malloc(void *p, size_t size) nothrow { }
- void log_free(void *p) nothrow { }
- void log_collect() nothrow { }
- void log_parent(void *p, void *parent) nothrow { }
- }
-}
-
-/* ============================ Pool =============================== */
-
-struct Pool
-{
- void* baseAddr;
- void* topAddr;
- GCBits mark; // entries already scanned, or should not be scanned
- GCBits freebits; // entries that are on the free list
- GCBits finals; // entries that need finalizer run on them
- GCBits structFinals;// struct entries that need a finalzier run on them
- GCBits noscan; // entries that should not be scanned
- GCBits appendable; // entries that are appendable
- GCBits nointerior; // interior pointers should be ignored.
- // Only implemented for large object pools.
- size_t npages;
- size_t freepages; // The number of pages not in use.
- ubyte* pagetable;
-
- bool isLargeObject;
-
- uint shiftBy; // shift count for the divisor used for determining bit indices.
-
- // This tracks how far back we have to go to find the nearest B_PAGE at
- // a smaller address than a B_PAGEPLUS. To save space, we use a uint.
- // This limits individual allocations to 16 terabytes, assuming a 4k
- // pagesize.
- uint* bPageOffsets;
-
- // This variable tracks a conservative estimate of where the first free
- // page in this pool is, so that if a lot of pages towards the beginning
- // are occupied, we can bypass them in O(1).
- size_t searchStart;
- size_t largestFree; // upper limit for largest free chunk in large object pool
-
- void initialize(size_t npages, bool isLargeObject) nothrow
- {
- this.isLargeObject = isLargeObject;
- size_t poolsize;
-
- shiftBy = isLargeObject ? 12 : 4;
-
- //debug(PRINTF) printf("Pool::Pool(%u)\n", npages);
- poolsize = npages * PAGESIZE;
- assert(poolsize >= POOLSIZE);
- baseAddr = cast(byte *)os_mem_map(poolsize);
-
- // Some of the code depends on page alignment of memory pools
- assert((cast(size_t)baseAddr & (PAGESIZE - 1)) == 0);
-
- if (!baseAddr)
- {
- //debug(PRINTF) printf("GC fail: poolsize = x%zx, errno = %d\n", poolsize, errno);
- //debug(PRINTF) printf("message = '%s'\n", sys_errlist[errno]);
-
- npages = 0;
- poolsize = 0;
- }
- //assert(baseAddr);
- topAddr = baseAddr + poolsize;
- auto nbits = cast(size_t)poolsize >> shiftBy;
-
- mark.alloc(nbits);
-
- // pagetable already keeps track of what's free for the large object
- // pool.
- if (!isLargeObject)
- {
- freebits.alloc(nbits);
- }
-
- noscan.alloc(nbits);
- appendable.alloc(nbits);
-
- pagetable = cast(ubyte*)cstdlib.malloc(npages);
- if (!pagetable)
- onOutOfMemoryErrorNoGC();
-
- if (isLargeObject)
- {
- bPageOffsets = cast(uint*)cstdlib.malloc(npages * uint.sizeof);
- if (!bPageOffsets)
- onOutOfMemoryErrorNoGC();
- }
-
- memset(pagetable, B_FREE, npages);
-
- this.npages = npages;
- this.freepages = npages;
- this.searchStart = 0;
- this.largestFree = npages;
- }
-
-
- void Dtor() nothrow
- {
- if (baseAddr)
- {
- int result;
-
- if (npages)
- {
- result = os_mem_unmap(baseAddr, npages * PAGESIZE);
- assert(result == 0);
- npages = 0;
- }
-
- baseAddr = null;
- topAddr = null;
- }
- if (pagetable)
- {
- cstdlib.free(pagetable);
- pagetable = null;
- }
-
- if (bPageOffsets)
- cstdlib.free(bPageOffsets);
-
- mark.Dtor();
- if (isLargeObject)
- {
- nointerior.Dtor();
- }
- else
- {
- freebits.Dtor();
- }
- finals.Dtor();
- structFinals.Dtor();
- noscan.Dtor();
- appendable.Dtor();
- }
-
- /**
- *
- */
- uint getBits(size_t biti) nothrow
- {
- uint bits;
-
- if (finals.nbits && finals.test(biti))
- bits |= BlkAttr.FINALIZE;
- if (structFinals.nbits && structFinals.test(biti))
- bits |= BlkAttr.STRUCTFINAL;
- if (noscan.test(biti))
- bits |= BlkAttr.NO_SCAN;
- if (nointerior.nbits && nointerior.test(biti))
- bits |= BlkAttr.NO_INTERIOR;
- if (appendable.test(biti))
- bits |= BlkAttr.APPENDABLE;
- return bits;
- }
-
- /**
- *
- */
- void clrBits(size_t biti, uint mask) nothrow
- {
- immutable dataIndex = biti >> GCBits.BITS_SHIFT;
- immutable bitOffset = biti & GCBits.BITS_MASK;
- immutable keep = ~(GCBits.BITS_1 << bitOffset);
-
- if (mask & BlkAttr.FINALIZE && finals.nbits)
- finals.data[dataIndex] &= keep;
-
- if (structFinals.nbits && (mask & BlkAttr.STRUCTFINAL))
- structFinals.data[dataIndex] &= keep;
-
- if (mask & BlkAttr.NO_SCAN)
- noscan.data[dataIndex] &= keep;
- if (mask & BlkAttr.APPENDABLE)
- appendable.data[dataIndex] &= keep;
- if (nointerior.nbits && (mask & BlkAttr.NO_INTERIOR))
- nointerior.data[dataIndex] &= keep;
- }
-
- /**
- *
- */
- void setBits(size_t biti, uint mask) nothrow
- {
- // Calculate the mask and bit offset once and then use it to
- // set all of the bits we need to set.
- immutable dataIndex = biti >> GCBits.BITS_SHIFT;
- immutable bitOffset = biti & GCBits.BITS_MASK;
- immutable orWith = GCBits.BITS_1 << bitOffset;
-
- if (mask & BlkAttr.STRUCTFINAL)
- {
- if (!structFinals.nbits)
- structFinals.alloc(mark.nbits);
- structFinals.data[dataIndex] |= orWith;
- }
-
- if (mask & BlkAttr.FINALIZE)
- {
- if (!finals.nbits)
- finals.alloc(mark.nbits);
- finals.data[dataIndex] |= orWith;
- }
-
- if (mask & BlkAttr.NO_SCAN)
- {
- noscan.data[dataIndex] |= orWith;
- }
-// if (mask & BlkAttr.NO_MOVE)
-// {
-// if (!nomove.nbits)
-// nomove.alloc(mark.nbits);
-// nomove.data[dataIndex] |= orWith;
-// }
- if (mask & BlkAttr.APPENDABLE)
- {
- appendable.data[dataIndex] |= orWith;
- }
-
- if (isLargeObject && (mask & BlkAttr.NO_INTERIOR))
- {
- if (!nointerior.nbits)
- nointerior.alloc(mark.nbits);
- nointerior.data[dataIndex] |= orWith;
- }
- }
-
- void freePageBits(size_t pagenum, in ref PageBits toFree) nothrow
- {
- assert(!isLargeObject);
- assert(!nointerior.nbits); // only for large objects
-
- import core.internal.traits : staticIota;
- immutable beg = pagenum * (PAGESIZE / 16 / GCBits.BITS_PER_WORD);
- foreach (i; staticIota!(0, PageBits.length))
- {
- immutable w = toFree[i];
- if (!w) continue;
-
- immutable wi = beg + i;
- freebits.data[wi] |= w;
- noscan.data[wi] &= ~w;
- appendable.data[wi] &= ~w;
- }
-
- if (finals.nbits)
- {
- foreach (i; staticIota!(0, PageBits.length))
- if (toFree[i])
- finals.data[beg + i] &= ~toFree[i];
- }
-
- if (structFinals.nbits)
- {
- foreach (i; staticIota!(0, PageBits.length))
- if (toFree[i])
- structFinals.data[beg + i] &= ~toFree[i];
- }
- }
-
- /**
- * Given a pointer p in the p, return the pagenum.
- */
- size_t pagenumOf(void *p) const nothrow
- in
- {
- assert(p >= baseAddr);
- assert(p < topAddr);
- }
- body
- {
- return cast(size_t)(p - baseAddr) / PAGESIZE;
- }
-
- @property bool isFree() const pure nothrow
- {
- return npages == freepages;
- }
-
- size_t slGetSize(void* p) nothrow
- {
- if (isLargeObject)
- return (cast(LargeObjectPool*)&this).getSize(p);
- else
- return (cast(SmallObjectPool*)&this).getSize(p);
- }
-
- BlkInfo slGetInfo(void* p) nothrow
- {
- if (isLargeObject)
- return (cast(LargeObjectPool*)&this).getInfo(p);
- else
- return (cast(SmallObjectPool*)&this).getInfo(p);
- }
-
-
- void Invariant() const {}
-
- debug(INVARIANT)
- invariant()
- {
- //mark.Invariant();
- //scan.Invariant();
- //freebits.Invariant();
- //finals.Invariant();
- //structFinals.Invariant();
- //noscan.Invariant();
- //appendable.Invariant();
- //nointerior.Invariant();
-
- if (baseAddr)
- {
- //if (baseAddr + npages * PAGESIZE != topAddr)
- //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr);
- assert(baseAddr + npages * PAGESIZE == topAddr);
- }
-
- if (pagetable !is null)
- {
- for (size_t i = 0; i < npages; i++)
- {
- Bins bin = cast(Bins)pagetable[i];
- assert(bin < B_MAX);
- }
- }
- }
-}
-
-struct LargeObjectPool
-{
- Pool base;
- alias base this;
-
- void updateOffsets(size_t fromWhere) nothrow
- {
- assert(pagetable[fromWhere] == B_PAGE);
- size_t pn = fromWhere + 1;
- for (uint offset = 1; pn < npages; pn++, offset++)
- {
- if (pagetable[pn] != B_PAGEPLUS) break;
- bPageOffsets[pn] = offset;
- }
-
- // Store the size of the block in bPageOffsets[fromWhere].
- bPageOffsets[fromWhere] = cast(uint) (pn - fromWhere);
- }
-
- /**
- * Allocate n pages from Pool.
- * Returns OPFAIL on failure.
- */
- size_t allocPages(size_t n) nothrow
- {
- if (largestFree < n || searchStart + n > npages)
- return OPFAIL;
-
- //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n);
- size_t largest = 0;
- if (pagetable[searchStart] == B_PAGEPLUS)
- {
- searchStart -= bPageOffsets[searchStart]; // jump to B_PAGE
- searchStart += bPageOffsets[searchStart];
- }
- while (searchStart < npages && pagetable[searchStart] == B_PAGE)
- searchStart += bPageOffsets[searchStart];
-
- for (size_t i = searchStart; i < npages; )
- {
- assert(pagetable[i] == B_FREE);
- size_t p = 1;
- while (p < n && i + p < npages && pagetable[i + p] == B_FREE)
- p++;
-
- if (p == n)
- return i;
-
- if (p > largest)
- largest = p;
-
- i += p;
- while (i < npages && pagetable[i] == B_PAGE)
- {
- // we have the size information, so we skip a whole bunch of pages.
- i += bPageOffsets[i];
- }
- }
-
- // not enough free pages found, remember largest free chunk
- largestFree = largest;
- return OPFAIL;
- }
-
- /**
- * Free npages pages starting with pagenum.
- */
- void freePages(size_t pagenum, size_t npages) nothrow
- {
- //memset(&pagetable[pagenum], B_FREE, npages);
- if (pagenum < searchStart)
- searchStart = pagenum;
-
- for (size_t i = pagenum; i < npages + pagenum; i++)
- {
- if (pagetable[i] < B_FREE)
- {
- freepages++;
- }
-
- pagetable[i] = B_FREE;
- }
- largestFree = freepages; // invalidate
- }
-
- /**
- * Get size of pointer p in pool.
- */
- size_t getSize(void *p) const nothrow
- in
- {
- assert(p >= baseAddr);
- assert(p < topAddr);
- }
- body
- {
- size_t pagenum = pagenumOf(p);
- Bins bin = cast(Bins)pagetable[pagenum];
- assert(bin == B_PAGE);
- return bPageOffsets[pagenum] * PAGESIZE;
- }
-
- /**
- *
- */
- BlkInfo getInfo(void* p) nothrow
- {
- BlkInfo info;
-
- size_t offset = cast(size_t)(p - baseAddr);
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pagetable[pn];
-
- if (bin == B_PAGEPLUS)
- pn -= bPageOffsets[pn];
- else if (bin != B_PAGE)
- return info; // no info for free pages
-
- info.base = baseAddr + pn * PAGESIZE;
- info.size = bPageOffsets[pn] * PAGESIZE;
-
- info.attr = getBits(pn);
- return info;
- }
-
- void runFinalizers(in void[] segment) nothrow
- {
- foreach (pn; 0 .. npages)
- {
- Bins bin = cast(Bins)pagetable[pn];
- if (bin > B_PAGE)
- continue;
- size_t biti = pn;
-
- if (!finals.test(biti))
- continue;
-
- auto p = sentinel_add(baseAddr + pn * PAGESIZE);
- size_t size = bPageOffsets[pn] * PAGESIZE - SENTINEL_EXTRA;
- uint attr = getBits(biti);
-
- if (!rt_hasFinalizerInSegment(p, size, attr, segment))
- continue;
-
- rt_finalizeFromGC(p, size, attr);
-
- clrBits(biti, ~BlkAttr.NONE);
-
- if (pn < searchStart)
- searchStart = pn;
-
- debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
- //log_free(sentinel_add(p));
-
- size_t n = 1;
- for (; pn + n < npages; ++n)
- if (pagetable[pn + n] != B_PAGEPLUS)
- break;
- debug (MEMSTOMP) memset(baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE);
- freePages(pn, n);
- }
- }
-}
-
-
-struct SmallObjectPool
-{
- Pool base;
- alias base this;
-
- /**
- * Get size of pointer p in pool.
- */
- size_t getSize(void *p) const nothrow
- in
- {
- assert(p >= baseAddr);
- assert(p < topAddr);
- }
- body
- {
- size_t pagenum = pagenumOf(p);
- Bins bin = cast(Bins)pagetable[pagenum];
- assert(bin < B_PAGE);
- return binsize[bin];
- }
-
- BlkInfo getInfo(void* p) nothrow
- {
- BlkInfo info;
- size_t offset = cast(size_t)(p - baseAddr);
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pagetable[pn];
-
- if (bin >= B_PAGE)
- return info;
-
- info.base = cast(void*)((cast(size_t)p) & notbinsize[bin]);
- info.size = binsize[bin];
- offset = info.base - baseAddr;
- info.attr = getBits(cast(size_t)(offset >> shiftBy));
-
- return info;
- }
-
- void runFinalizers(in void[] segment) nothrow
- {
- foreach (pn; 0 .. npages)
- {
- Bins bin = cast(Bins)pagetable[pn];
- if (bin >= B_PAGE)
- continue;
-
- immutable size = binsize[bin];
- auto p = baseAddr + pn * PAGESIZE;
- const ptop = p + PAGESIZE;
- immutable base = pn * (PAGESIZE/16);
- immutable bitstride = size / 16;
-
- bool freeBits;
- PageBits toFree;
-
- for (size_t i; p < ptop; p += size, i += bitstride)
- {
- immutable biti = base + i;
-
- if (!finals.test(biti))
- continue;
-
- auto q = sentinel_add(p);
- uint attr = getBits(biti);
-
- if (!rt_hasFinalizerInSegment(q, size, attr, segment))
- continue;
-
- rt_finalizeFromGC(q, size, attr);
-
- freeBits = true;
- toFree.set(i);
-
- debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p);
- //log_free(sentinel_add(p));
-
- debug (MEMSTOMP) memset(p, 0xF3, size);
- }
-
- if (freeBits)
- freePageBits(pn, toFree);
- }
- }
-
- /**
- * Allocate a page of bin's.
- * Returns:
- * head of a single linked list of new entries
- */
- List* allocPage(Bins bin) nothrow
- {
- size_t pn;
- for (pn = searchStart; pn < npages; pn++)
- if (pagetable[pn] == B_FREE)
- goto L1;
-
- return null;
-
- L1:
- searchStart = pn + 1;
- pagetable[pn] = cast(ubyte)bin;
- freepages--;
-
- // Convert page to free list
- size_t size = binsize[bin];
- void* p = baseAddr + pn * PAGESIZE;
- void* ptop = p + PAGESIZE - size;
- auto first = cast(List*) p;
-
- for (; p < ptop; p += size)
- {
- (cast(List *)p).next = cast(List *)(p + size);
- (cast(List *)p).pool = &base;
- }
- (cast(List *)p).next = null;
- (cast(List *)p).pool = &base;
- return first;
- }
-}
-
-unittest // bugzilla 14467
-{
- int[] arr = new int[10];
- assert(arr.capacity);
- arr = arr[$..$];
- assert(arr.capacity);
-}
-
-unittest // bugzilla 15353
-{
- import core.memory : GC;
-
- static struct Foo
- {
- ~this()
- {
- GC.free(buf); // ignored in finalizer
- }
-
- void* buf;
- }
- new Foo(GC.malloc(10));
- GC.collect();
-}
-
-unittest // bugzilla 15822
-{
- import core.memory : GC;
-
- ubyte[16] buf;
- static struct Foo
- {
- ~this()
- {
- GC.removeRange(ptr);
- GC.removeRoot(ptr);
- }
-
- ubyte* ptr;
- }
- GC.addRoot(buf.ptr);
- GC.addRange(buf.ptr, buf.length);
- new Foo(buf.ptr);
- GC.collect();
-}
-
-unittest // bugzilla 1180
-{
- import core.exception;
- try
- {
- size_t x = size_t.max - 100;
- byte[] big_buf = new byte[x];
- }
- catch (OutOfMemoryError)
- {
- }
-}
-
-/* ============================ SENTINEL =============================== */
-
-
-debug (SENTINEL)
-{
- const size_t SENTINEL_PRE = cast(size_t) 0xF4F4F4F4F4F4F4F4UL; // 32 or 64 bits
- const ubyte SENTINEL_POST = 0xF5; // 8 bits
- const uint SENTINEL_EXTRA = 2 * size_t.sizeof + 1;
-
-
- inout(size_t*) sentinel_size(inout void *p) nothrow { return &(cast(inout size_t *)p)[-2]; }
- inout(size_t*) sentinel_pre(inout void *p) nothrow { return &(cast(inout size_t *)p)[-1]; }
- inout(ubyte*) sentinel_post(inout void *p) nothrow { return &(cast(inout ubyte *)p)[*sentinel_size(p)]; }
-
-
- void sentinel_init(void *p, size_t size) nothrow
- {
- *sentinel_size(p) = size;
- *sentinel_pre(p) = SENTINEL_PRE;
- *sentinel_post(p) = SENTINEL_POST;
- }
-
-
- void sentinel_Invariant(const void *p) nothrow
- {
- debug
- {
- assert(*sentinel_pre(p) == SENTINEL_PRE);
- assert(*sentinel_post(p) == SENTINEL_POST);
- }
- else if (*sentinel_pre(p) != SENTINEL_PRE || *sentinel_post(p) != SENTINEL_POST)
- onInvalidMemoryOperationError(); // also trigger in release build
- }
-
-
- void *sentinel_add(void *p) nothrow
- {
- return p + 2 * size_t.sizeof;
- }
-
-
- void *sentinel_sub(void *p) nothrow
- {
- return p - 2 * size_t.sizeof;
- }
-}
-else
-{
- const uint SENTINEL_EXTRA = 0;
-
-
- void sentinel_init(void *p, size_t size) nothrow
- {
- }
-
-
- void sentinel_Invariant(const void *p) nothrow
- {
- }
-
-
- void *sentinel_add(void *p) nothrow
- {
- return p;
- }
-
-
- void *sentinel_sub(void *p) nothrow
- {
- return p;
- }
-}
-
-debug (MEMSTOMP)
-unittest
-{
- import core.memory;
- auto p = cast(uint*)GC.malloc(uint.sizeof*3);
- assert(*p == 0xF0F0F0F0);
- p[2] = 0; // First two will be used for free list
- GC.free(p);
- assert(p[2] == 0xF2F2F2F2);
-}
-
-debug (SENTINEL)
-unittest
-{
- import core.memory;
- auto p = cast(ubyte*)GC.malloc(1);
- assert(p[-1] == 0xF4);
- assert(p[ 1] == 0xF5);
-/*
- p[1] = 0;
- bool thrown;
- try
- GC.free(p);
- catch (Error e)
- thrown = true;
- p[1] = 0xF5;
- assert(thrown);
-*/
-}
-
-unittest
-{
- import core.memory;
-
- // https://issues.dlang.org/show_bug.cgi?id=9275
- GC.removeRoot(null);
- GC.removeRoot(cast(void*)13);
-}
-
-// improve predictability of coverage of code that is eventually not hit by other tests
-unittest
-{
- import core.memory;
- auto p = GC.malloc(260 << 20); // new pool has 390 MB
- auto q = GC.malloc(65 << 20); // next chunk (larger than 64MB to ensure the same pool is used)
- auto r = GC.malloc(65 << 20); // another chunk in same pool
- assert(p + (260 << 20) == q);
- assert(q + (65 << 20) == r);
- GC.free(q);
- // should trigger "assert(bin == B_FREE);" in mark due to dangling pointer q:
- GC.collect();
- // should trigger "break;" in extendNoSync:
- size_t sz = GC.extend(p, 64 << 20, 66 << 20); // trigger size after p large enough (but limited)
- assert(sz == 325 << 20);
- GC.free(p);
- GC.free(r);
- r = q; // ensure q is not trashed before collection above
-
- p = GC.malloc(70 << 20); // from the same pool
- q = GC.malloc(70 << 20);
- r = GC.malloc(70 << 20);
- auto s = GC.malloc(70 << 20);
- auto t = GC.malloc(70 << 20); // 350 MB of 390 MB used
- assert(p + (70 << 20) == q);
- assert(q + (70 << 20) == r);
- assert(r + (70 << 20) == s);
- assert(s + (70 << 20) == t);
- GC.free(r); // ensure recalculation of largestFree in nxxt allocPages
- auto z = GC.malloc(75 << 20); // needs new pool
-
- GC.free(p);
- GC.free(q);
- GC.free(s);
- GC.free(t);
- GC.free(z);
- GC.minimize(); // release huge pool
-}
-
diff --git a/libphobos/libdruntime/gc/impl/manual/gc.d b/libphobos/libdruntime/gc/impl/manual/gc.d
deleted file mode 100644
index f5c6b770f6f..00000000000
--- a/libphobos/libdruntime/gc/impl/manual/gc.d
+++ /dev/null
@@ -1,274 +0,0 @@
-/**
- * This module contains a minimal garbage collector implementation according to
- * published requirements. This library is mostly intended to serve as an
- * example, but it is usable in applications which do not rely on a garbage
- * collector to clean up memory (ie. when dynamic array resizing is not used,
- * and all memory allocated with 'new' is freed deterministically with
- * 'delete').
- *
- * Please note that block attribute data must be tracked, or at a minimum, the
- * FINALIZE bit must be tracked for any allocated memory block because calling
- * rt_finalize on a non-object block can result in an access violation. In the
- * allocator below, this tracking is done via a leading uint bitmask. A real
- * allocator may do better to store this data separately, similar to the basic
- * GC.
- *
- * Copyright: Copyright Sean Kelly 2005 - 2016.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Sean Kelly
- */
-
-/* Copyright Sean Kelly 2005 - 2016.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module gc.impl.manual.gc;
-
-import gc.config;
-import gc.gcinterface;
-
-import rt.util.container.array;
-
-import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
-static import core.memory;
-
-extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc; /* dmd @@@BUG11461@@@ */
-
-class ManualGC : GC
-{
- __gshared Array!Root roots;
- __gshared Array!Range ranges;
-
- static void initialize(ref GC gc)
- {
- import core.stdc.string;
-
- if (config.gc != "manual")
- return;
-
- auto p = cstdlib.malloc(__traits(classInstanceSize, ManualGC));
- if (!p)
- onOutOfMemoryError();
-
- auto init = typeid(ManualGC).initializer();
- assert(init.length == __traits(classInstanceSize, ManualGC));
- auto instance = cast(ManualGC) memcpy(p, init.ptr, init.length);
- instance.__ctor();
-
- gc = instance;
- }
-
- static void finalize(ref GC gc)
- {
- if (config.gc != "manual")
- return;
-
- auto instance = cast(ManualGC) gc;
- instance.Dtor();
- cstdlib.free(cast(void*) instance);
- }
-
- this()
- {
- }
-
- void Dtor()
- {
- }
-
- void enable()
- {
- }
-
- void disable()
- {
- }
-
- void collect() nothrow
- {
- }
-
- void collectNoStack() nothrow
- {
- }
-
- void minimize() nothrow
- {
- }
-
- uint getAttr(void* p) nothrow
- {
- return 0;
- }
-
- uint setAttr(void* p, uint mask) nothrow
- {
- return 0;
- }
-
- uint clrAttr(void* p, uint mask) nothrow
- {
- return 0;
- }
-
- void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow
- {
- void* p = cstdlib.malloc(size);
-
- if (size && p is null)
- onOutOfMemoryError();
- return p;
- }
-
- BlkInfo qalloc(size_t size, uint bits, const TypeInfo ti) nothrow
- {
- BlkInfo retval;
- retval.base = malloc(size, bits, ti);
- retval.size = size;
- retval.attr = bits;
- return retval;
- }
-
- void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow
- {
- void* p = cstdlib.calloc(1, size);
-
- if (size && p is null)
- onOutOfMemoryError();
- return p;
- }
-
- void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow
- {
- p = cstdlib.realloc(p, size);
-
- if (size && p is null)
- onOutOfMemoryError();
- return p;
- }
-
- size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow
- {
- return 0;
- }
-
- size_t reserve(size_t size) nothrow
- {
- return 0;
- }
-
- void free(void* p) nothrow
- {
- cstdlib.free(p);
- }
-
- /**
- * Determine the base address of the block containing p. If p is not a gc
- * allocated pointer, return null.
- */
- void* addrOf(void* p) nothrow
- {
- return null;
- }
-
- /**
- * Determine the allocated size of pointer p. If p is an interior pointer
- * or not a gc allocated pointer, return 0.
- */
- size_t sizeOf(void* p) nothrow
- {
- return 0;
- }
-
- /**
- * Determine the base address of the block containing p. If p is not a gc
- * allocated pointer, return null.
- */
- BlkInfo query(void* p) nothrow
- {
- return BlkInfo.init;
- }
-
- core.memory.GC.Stats stats() nothrow
- {
- return typeof(return).init;
- }
-
- void addRoot(void* p) nothrow @nogc
- {
- roots.insertBack(Root(p));
- }
-
- void removeRoot(void* p) nothrow @nogc
- {
- foreach (ref r; roots)
- {
- if (r is p)
- {
- r = roots.back;
- roots.popBack();
- return;
- }
- }
- assert(false);
- }
-
- @property RootIterator rootIter() return @nogc
- {
- return &rootsApply;
- }
-
- private int rootsApply(scope int delegate(ref Root) nothrow dg)
- {
- foreach (ref r; roots)
- {
- if (auto result = dg(r))
- return result;
- }
- return 0;
- }
-
- void addRange(void* p, size_t sz, const TypeInfo ti = null) nothrow @nogc
- {
- ranges.insertBack(Range(p, p + sz, cast() ti));
- }
-
- void removeRange(void* p) nothrow @nogc
- {
- foreach (ref r; ranges)
- {
- if (r.pbot is p)
- {
- r = ranges.back;
- ranges.popBack();
- return;
- }
- }
- assert(false);
- }
-
- @property RangeIterator rangeIter() return @nogc
- {
- return &rangesApply;
- }
-
- private int rangesApply(scope int delegate(ref Range) nothrow dg)
- {
- foreach (ref r; ranges)
- {
- if (auto result = dg(r))
- return result;
- }
- return 0;
- }
-
- void runFinalizers(in void[] segment) nothrow
- {
- }
-
- bool inFinalizer() nothrow
- {
- return false;
- }
-}
diff --git a/libphobos/libdruntime/gc/os.d b/libphobos/libdruntime/gc/os.d
deleted file mode 100644
index 337f134e6bb..00000000000
--- a/libphobos/libdruntime/gc/os.d
+++ /dev/null
@@ -1,214 +0,0 @@
-/**
- * Contains OS-level routines needed by the garbage collector.
- *
- * Copyright: Copyright Digital Mars 2005 - 2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, David Friedman, Sean Kelly, Leandro Lucarella
- */
-
-/* Copyright Digital Mars 2005 - 2013.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module gc.os;
-
-
-version (Windows)
-{
- import core.sys.windows.winbase : GetCurrentThreadId, VirtualAlloc, VirtualFree;
- import core.sys.windows.winnt : MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE;
-
- alias int pthread_t;
-
- pthread_t pthread_self() nothrow
- {
- return cast(pthread_t) GetCurrentThreadId();
- }
-
- //version = GC_Use_Alloc_Win32;
-}
-else version (Posix)
-{
- version (OSX)
- version = Darwin;
- else version (iOS)
- version = Darwin;
- else version (TVOS)
- version = Darwin;
- else version (WatchOS)
- version = Darwin;
-
- import core.sys.posix.sys.mman;
- version (FreeBSD) import core.sys.freebsd.sys.mman : MAP_ANON;
- version (DragonFlyBSD) import core.sys.dragonflybsd.sys.mman : MAP_ANON;
- version (NetBSD) import core.sys.netbsd.sys.mman : MAP_ANON;
- version (OpenBSD) import core.sys.openbsd.sys.mman : MAP_ANON;
- version (CRuntime_Glibc) import core.sys.linux.sys.mman : MAP_ANON;
- version (Darwin) import core.sys.darwin.sys.mman : MAP_ANON;
- version (CRuntime_UClibc) import core.sys.linux.sys.mman : MAP_ANON;
- import core.stdc.stdlib;
-
- //version = GC_Use_Alloc_MMap;
-}
-else
-{
- import core.stdc.stdlib;
-
- //version = GC_Use_Alloc_Malloc;
-}
-
-/+
-static if (is(typeof(VirtualAlloc)))
- version = GC_Use_Alloc_Win32;
-else static if (is(typeof(mmap)))
- version = GC_Use_Alloc_MMap;
-else static if (is(typeof(valloc)))
- version = GC_Use_Alloc_Valloc;
-else static if (is(typeof(malloc)))
- version = GC_Use_Alloc_Malloc;
-else static assert(false, "No supported allocation methods available.");
-+/
-
-static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32)
-{
- /**
- * Map memory.
- */
- void *os_mem_map(size_t nbytes) nothrow
- {
- return VirtualAlloc(null, nbytes, MEM_RESERVE | MEM_COMMIT,
- PAGE_READWRITE);
- }
-
-
- /**
- * Unmap memory allocated with os_mem_map().
- * Returns:
- * 0 success
- * !=0 failure
- */
- int os_mem_unmap(void *base, size_t nbytes) nothrow
- {
- return cast(int)(VirtualFree(base, 0, MEM_RELEASE) == 0);
- }
-}
-else static if (is(typeof(mmap))) // else version (GC_Use_Alloc_MMap)
-{
- void *os_mem_map(size_t nbytes) nothrow
- { void *p;
-
- p = mmap(null, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
- return (p == MAP_FAILED) ? null : p;
- }
-
-
- int os_mem_unmap(void *base, size_t nbytes) nothrow
- {
- return munmap(base, nbytes);
- }
-}
-else static if (is(typeof(valloc))) // else version (GC_Use_Alloc_Valloc)
-{
- void *os_mem_map(size_t nbytes) nothrow
- {
- return valloc(nbytes);
- }
-
-
- int os_mem_unmap(void *base, size_t nbytes) nothrow
- {
- free(base);
- return 0;
- }
-}
-else static if (is(typeof(malloc))) // else version (GC_Use_Alloc_Malloc)
-{
- // NOTE: This assumes malloc granularity is at least (void*).sizeof. If
- // (req_size + PAGESIZE) is allocated, and the pointer is rounded up
- // to PAGESIZE alignment, there will be space for a void* at the end
- // after PAGESIZE bytes used by the GC.
-
-
- import gc.gc;
-
-
- const size_t PAGE_MASK = PAGESIZE - 1;
-
-
- void *os_mem_map(size_t nbytes) nothrow
- { byte *p, q;
- p = cast(byte *) malloc(nbytes + PAGESIZE);
- q = p + ((PAGESIZE - ((cast(size_t) p & PAGE_MASK))) & PAGE_MASK);
- * cast(void**)(q + nbytes) = p;
- return q;
- }
-
-
- int os_mem_unmap(void *base, size_t nbytes) nothrow
- {
- free( *cast(void**)( cast(byte*) base + nbytes ) );
- return 0;
- }
-}
-else
-{
- static assert(false, "No supported allocation methods available.");
-}
-
-/**
- Check for any kind of memory pressure.
-
- Params:
- mapped = the amount of memory mapped by the GC in bytes
- Returns:
- true if memory is scarce
-*/
-// TOOD: get virtual mem sizes and current usage from OS
-// TODO: compare current RSS and avail. physical memory
-version (Windows)
-{
- bool isLowOnMem(size_t mapped) nothrow @nogc
- {
- version (D_LP64)
- return false;
- else
- {
- import core.sys.windows.winbase : GlobalMemoryStatus, MEMORYSTATUS;
- MEMORYSTATUS stat;
- GlobalMemoryStatus(&stat);
- // Less than 5 % of virtual address space available
- return stat.dwAvailVirtual < stat.dwTotalVirtual / 20;
- }
- }
-}
-else version (Darwin)
-{
- bool isLowOnMem(size_t mapped) nothrow @nogc
- {
- enum GB = 2 ^^ 30;
- version (D_LP64)
- return false;
- else
- {
- // 80 % of available 4GB is used for GC (excluding malloc and mmap)
- enum size_t limit = 4UL * GB * 8 / 10;
- return mapped > limit;
- }
- }
-}
-else
-{
- bool isLowOnMem(size_t mapped) nothrow @nogc
- {
- enum GB = 2 ^^ 30;
- version (D_LP64)
- return false;
- else
- {
- // be conservative and assume 3GB
- enum size_t limit = 3UL * GB * 8 / 10;
- return mapped > limit;
- }
- }
-}
diff --git a/libphobos/libdruntime/gc/pooltable.d b/libphobos/libdruntime/gc/pooltable.d
deleted file mode 100644
index cfbe4652972..00000000000
--- a/libphobos/libdruntime/gc/pooltable.d
+++ /dev/null
@@ -1,285 +0,0 @@
-/**
- * A sorted array to quickly lookup pools.
- *
- * Copyright: Copyright Digital Mars 2001 -.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, David Friedman, Sean Kelly, Martin Nowak
- */
-module gc.pooltable;
-
-static import cstdlib=core.stdc.stdlib;
-
-struct PoolTable(Pool)
-{
- import core.stdc.string : memmove;
-
-nothrow:
- void Dtor()
- {
- cstdlib.free(pools);
- pools = null;
- npools = 0;
- }
-
- bool insert(Pool* pool)
- {
- auto newpools = cast(Pool **)cstdlib.realloc(pools, (npools + 1) * pools[0].sizeof);
- if (!newpools)
- return false;
-
- pools = newpools;
-
- // Sort pool into newpooltable[]
- size_t i;
- for (; i < npools; ++i)
- {
- if (pool.baseAddr < pools[i].baseAddr)
- break;
- }
- if (i != npools)
- memmove(pools + i + 1, pools + i, (npools - i) * pools[0].sizeof);
- pools[i] = pool;
-
- ++npools;
-
- _minAddr = pools[0].baseAddr;
- _maxAddr = pools[npools - 1].topAddr;
-
- return true;
- }
-
- @property size_t length() pure const
- {
- return npools;
- }
-
- ref inout(Pool*) opIndex(size_t idx) inout pure
- in { assert(idx < length); }
- body
- {
- return pools[idx];
- }
-
- inout(Pool*)[] opSlice(size_t a, size_t b) inout pure
- in { assert(a <= length && b <= length); }
- body
- {
- return pools[a .. b];
- }
-
- alias opDollar = length;
-
- /**
- * Find Pool that pointer is in.
- * Return null if not in a Pool.
- * Assume pooltable[] is sorted.
- */
- Pool *findPool(void *p) nothrow
- {
- if (p >= minAddr && p < maxAddr)
- {
- assert(npools);
-
- // let dmd allocate a register for this.pools
- auto pools = this.pools;
-
- if (npools == 1)
- return pools[0];
-
- /* The pooltable[] is sorted by address, so do a binary search
- */
- size_t low = 0;
- size_t high = npools - 1;
- while (low <= high)
- {
- size_t mid = (low + high) >> 1;
- auto pool = pools[mid];
- if (p < pool.baseAddr)
- high = mid - 1;
- else if (p >= pool.topAddr)
- low = mid + 1;
- else
- return pool;
- }
- }
- return null;
- }
-
- // semi-stable partition, returns right half for which pred is false
- Pool*[] minimize() pure
- {
- static void swap(ref Pool* a, ref Pool* b)
- {
- auto c = a; a = b; b = c;
- }
-
- size_t i;
- // find first bad entry
- for (; i < npools; ++i)
- if (pools[i].isFree) break;
-
- // move good in front of bad entries
- size_t j = i + 1;
- for (; j < npools; ++j)
- {
- if (!pools[j].isFree) // keep
- swap(pools[i++], pools[j]);
- }
- // npooltable[0 .. i] => used pools
- // npooltable[i .. npools] => free pools
-
- if (i)
- {
- _minAddr = pools[0].baseAddr;
- _maxAddr = pools[i - 1].topAddr;
- }
- else
- {
- _minAddr = _maxAddr = null;
- }
-
- immutable len = npools;
- npools = i;
- // return freed pools to the caller
- return pools[npools .. len];
- }
-
- void Invariant() const
- {
- if (!npools) return;
-
- foreach (i, pool; pools[0 .. npools - 1])
- assert(pool.baseAddr < pools[i + 1].baseAddr);
-
- assert(_minAddr == pools[0].baseAddr);
- assert(_maxAddr == pools[npools - 1].topAddr);
- }
-
- @property const(void)* minAddr() pure const { return _minAddr; }
- @property const(void)* maxAddr() pure const { return _maxAddr; }
-
-package:
- Pool** pools;
- size_t npools;
- void* _minAddr, _maxAddr;
-}
-
-unittest
-{
- enum NPOOLS = 6;
- enum NPAGES = 10;
- enum PAGESIZE = 4096;
-
- static struct MockPool
- {
- byte* baseAddr, topAddr;
- size_t freepages, npages;
- @property bool isFree() const pure nothrow { return freepages == npages; }
- }
- PoolTable!MockPool pooltable;
-
- void reset()
- {
- foreach (ref pool; pooltable[0 .. $])
- pool.freepages = pool.npages;
- pooltable.minimize();
- assert(pooltable.length == 0);
-
- foreach (i; 0 .. NPOOLS)
- {
- auto pool = cast(MockPool*)cstdlib.malloc(MockPool.sizeof);
- *pool = MockPool.init;
- assert(pooltable.insert(pool));
- }
- }
-
- void usePools()
- {
- foreach (pool; pooltable[0 .. $])
- {
- pool.npages = NPAGES;
- pool.freepages = NPAGES / 2;
- }
- }
-
- // all pools are free
- reset();
- assert(pooltable.length == NPOOLS);
- auto freed = pooltable.minimize();
- assert(freed.length == NPOOLS);
- assert(pooltable.length == 0);
-
- // all pools used
- reset();
- usePools();
- assert(pooltable.length == NPOOLS);
- freed = pooltable.minimize();
- assert(freed.length == 0);
- assert(pooltable.length == NPOOLS);
-
- // preserves order of used pools
- reset();
- usePools();
-
- {
- MockPool*[NPOOLS] opools = pooltable[0 .. NPOOLS];
- // make the 2nd pool free
- pooltable[2].freepages = NPAGES;
-
- pooltable.minimize();
- assert(pooltable.length == NPOOLS - 1);
- assert(pooltable[0] == opools[0]);
- assert(pooltable[1] == opools[1]);
- assert(pooltable[2] == opools[3]);
- }
-
- // test that PoolTable reduces min/max address span
- reset();
- usePools();
-
- byte* base, top;
-
- {
- // fill with fake addresses
- size_t i;
- foreach (pool; pooltable[0 .. NPOOLS])
- {
- pool.baseAddr = cast(byte*)(i++ * NPAGES * PAGESIZE);
- pool.topAddr = pool.baseAddr + NPAGES * PAGESIZE;
- }
- base = pooltable[0].baseAddr;
- top = pooltable[NPOOLS - 1].topAddr;
- }
-
- freed = pooltable.minimize();
- assert(freed.length == 0);
- assert(pooltable.length == NPOOLS);
- assert(pooltable.minAddr == base);
- assert(pooltable.maxAddr == top);
-
- pooltable[NPOOLS - 1].freepages = NPAGES;
- pooltable[NPOOLS - 2].freepages = NPAGES;
-
- freed = pooltable.minimize();
- assert(freed.length == 2);
- assert(pooltable.length == NPOOLS - 2);
- assert(pooltable.minAddr == base);
- assert(pooltable.maxAddr == pooltable[NPOOLS - 3].topAddr);
-
- pooltable[0].freepages = NPAGES;
-
- freed = pooltable.minimize();
- assert(freed.length == 1);
- assert(pooltable.length == NPOOLS - 3);
- assert(pooltable.minAddr != base);
- assert(pooltable.minAddr == pooltable[0].baseAddr);
- assert(pooltable.maxAddr == pooltable[NPOOLS - 4].topAddr);
-
- // free all
- foreach (pool; pooltable[0 .. $])
- pool.freepages = NPAGES;
- freed = pooltable.minimize();
- assert(freed.length == NPOOLS - 3);
- assert(pooltable.length == 0);
- pooltable.Dtor();
-}
diff --git a/libphobos/libdruntime/gc/proxy.d b/libphobos/libdruntime/gc/proxy.d
deleted file mode 100644
index 794da90a4d5..00000000000
--- a/libphobos/libdruntime/gc/proxy.d
+++ /dev/null
@@ -1,239 +0,0 @@
-/**
- * Contains the external GC interface.
- *
- * Copyright: Copyright Digital Mars 2005 - 2016.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, Sean Kelly
- */
-
-/* Copyright Digital Mars 2005 - 2016.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module gc.proxy;
-
-import gc.impl.conservative.gc;
-import gc.impl.manual.gc;
-import gc.config;
-import gc.gcinterface;
-
-static import core.memory;
-
-private
-{
- static import core.memory;
- alias BlkInfo = core.memory.GC.BlkInfo;
-
- extern (C) void thread_init();
- extern (C) void thread_term();
-
- __gshared GC instance;
- __gshared GC proxiedGC; // used to iterate roots of Windows DLLs
-
-}
-
-
-extern (C)
-{
-
- void gc_init()
- {
- config.initialize();
- ManualGC.initialize(instance);
- ConservativeGC.initialize(instance);
- if (instance is null)
- {
- import core.stdc.stdio : fprintf, stderr;
- import core.stdc.stdlib : exit;
-
- fprintf(stderr, "No GC was initialized, please recheck the name of the selected GC ('%.*s').\n", cast(int)config.gc.length, config.gc.ptr);
- exit(1);
- }
-
- // NOTE: The GC must initialize the thread library
- // before its first collection.
- thread_init();
- }
-
- void gc_term()
- {
- // NOTE: There may be daemons threads still running when this routine is
- // called. If so, cleaning memory out from under then is a good
- // way to make them crash horribly. This probably doesn't matter
- // much since the app is supposed to be shutting down anyway, but
- // I'm disabling cleanup for now until I can think about it some
- // more.
- //
- // NOTE: Due to popular demand, this has been re-enabled. It still has
- // the problems mentioned above though, so I guess we'll see.
-
- instance.collectNoStack(); // not really a 'collect all' -- still scans
- // static data area, roots, and ranges.
-
- thread_term();
-
- ManualGC.finalize(instance);
- ConservativeGC.finalize(instance);
- }
-
- void gc_enable()
- {
- instance.enable();
- }
-
- void gc_disable()
- {
- instance.disable();
- }
-
- void gc_collect() nothrow
- {
- instance.collect();
- }
-
- void gc_minimize() nothrow
- {
- instance.minimize();
- }
-
- uint gc_getAttr( void* p ) nothrow
- {
- return instance.getAttr(p);
- }
-
- uint gc_setAttr( void* p, uint a ) nothrow
- {
- return instance.setAttr(p, a);
- }
-
- uint gc_clrAttr( void* p, uint a ) nothrow
- {
- return instance.clrAttr(p, a);
- }
-
- void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow
- {
- return instance.malloc(sz, ba, ti);
- }
-
- BlkInfo gc_qalloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow
- {
- return instance.qalloc( sz, ba, ti );
- }
-
- void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow
- {
- return instance.calloc( sz, ba, ti );
- }
-
- void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow
- {
- return instance.realloc( p, sz, ba, ti );
- }
-
- size_t gc_extend( void* p, size_t mx, size_t sz, const TypeInfo ti = null ) nothrow
- {
- return instance.extend( p, mx, sz,ti );
- }
-
- size_t gc_reserve( size_t sz ) nothrow
- {
- return instance.reserve( sz );
- }
-
- void gc_free( void* p ) nothrow
- {
- return instance.free( p );
- }
-
- void* gc_addrOf( void* p ) nothrow
- {
- return instance.addrOf( p );
- }
-
- size_t gc_sizeOf( void* p ) nothrow
- {
- return instance.sizeOf( p );
- }
-
- BlkInfo gc_query( void* p ) nothrow
- {
- return instance.query( p );
- }
-
- core.memory.GC.Stats gc_stats() nothrow
- {
- return instance.stats();
- }
-
- void gc_addRoot( void* p ) nothrow
- {
- return instance.addRoot( p );
- }
-
- void gc_addRange( void* p, size_t sz, const TypeInfo ti = null ) nothrow
- {
- return instance.addRange( p, sz, ti );
- }
-
- void gc_removeRoot( void* p ) nothrow
- {
- return instance.removeRoot( p );
- }
-
- void gc_removeRange( void* p ) nothrow
- {
- return instance.removeRange( p );
- }
-
- void gc_runFinalizers( in void[] segment ) nothrow
- {
- return instance.runFinalizers( segment );
- }
-
- bool gc_inFinalizer() nothrow
- {
- return instance.inFinalizer();
- }
-
- GC gc_getProxy() nothrow
- {
- return instance;
- }
-
- export
- {
- void gc_setProxy( GC proxy )
- {
- foreach (root; instance.rootIter)
- {
- proxy.addRoot(root);
- }
-
- foreach (range; instance.rangeIter)
- {
- proxy.addRange(range.pbot, range.ptop - range.pbot, range.ti);
- }
-
- proxiedGC = instance; // remember initial GC to later remove roots
- instance = proxy;
- }
-
- void gc_clrProxy()
- {
- foreach (root; proxiedGC.rootIter)
- {
- instance.removeRoot(root);
- }
-
- foreach (range; proxiedGC.rangeIter)
- {
- instance.removeRange(range);
- }
-
- instance = proxiedGC;
- proxiedGC = null;
- }
- }
-}
diff --git a/libphobos/libdruntime/gcc/deh.d b/libphobos/libdruntime/gcc/deh.d
index bbc351c7805..85322db75eb 100644
--- a/libphobos/libdruntime/gcc/deh.d
+++ b/libphobos/libdruntime/gcc/deh.d
@@ -32,8 +32,8 @@ import gcc.attributes;
extern(C)
{
- int _d_isbaseof(ClassInfo, ClassInfo);
- void _d_createTrace(Object, void*);
+ int _d_isbaseof(ClassInfo, ClassInfo) @nogc nothrow pure @safe;
+ void _d_createTrace(Throwable, void*);
void _d_print_throwable(Throwable t);
}
@@ -432,6 +432,9 @@ extern(C) void* __gdc_begin_catch(_Unwind_Exception* unwindHeader)
ExceptionHeader* header = ExceptionHeader.toExceptionHeader(unwindHeader);
void* objectp = cast(void*)header.object;
+ // Remove our reference to the exception. We should not decrease its refcount,
+ // because we pass the object on to the caller.
+ header.object = null;
// Something went wrong when stacking up chained headers...
if (header != ExceptionHeader.pop())
@@ -455,6 +458,11 @@ extern(C) void _d_throw(Throwable object)
// Add to thrown exception stack.
eh.push();
+ // Increment reference count if object is a refcounted Throwable.
+ auto refcount = object.refcount();
+ if (refcount)
+ object.refcount() = refcount + 1;
+
// Called by unwinder when exception object needs destruction by other than our code.
extern(C) void exception_cleanup(_Unwind_Reason_Code code, _Unwind_Exception* exc)
{
@@ -976,14 +984,10 @@ private _Unwind_Reason_Code __gdc_personality(_Unwind_Action actions,
if (currentLsd != nextLsd)
break;
- // Add our object onto the end of the existing chain.
- Throwable n = ehn.object;
- while (n.next)
- n = n.next;
- n.next = eh.object;
+ // Add our object onto the end of the existing chain and replace
+ // our exception object with in-flight one.
+ eh.object = Throwable.chainTogether(ehn.object, eh.object);
- // Replace our exception object with in-flight one
- eh.object = ehn.object;
if (nextHandler != handler && !bypassed)
{
handler = nextHandler;
diff --git a/libphobos/libdruntime/gcc/emutls.d b/libphobos/libdruntime/gcc/emutls.d
index 462230508ab..e0eab8c6ee7 100644
--- a/libphobos/libdruntime/gcc/emutls.d
+++ b/libphobos/libdruntime/gcc/emutls.d
@@ -25,7 +25,8 @@
module gcc.emutls;
import core.atomic, core.stdc.stdlib, core.stdc.string, core.sync.mutex;
-import rt.util.container.array, rt.util.container.hashtab;
+import core.internal.container.array;
+import core.internal.container.hashtab;
import core.internal.traits : classInstanceAlignment;
import gcc.builtins, gcc.gthread;
diff --git a/libphobos/libdruntime/gcc/sections/elf.d b/libphobos/libdruntime/gcc/sections/elf.d
index 3480fb9474c..9662cdd8ba9 100644
--- a/libphobos/libdruntime/gcc/sections/elf.d
+++ b/libphobos/libdruntime/gcc/sections/elf.d
@@ -90,8 +90,8 @@ import core.sys.posix.pthread;
import rt.deh;
import rt.dmain2;
import rt.minfo;
-import rt.util.container.array;
-import rt.util.container.hashtab;
+import core.internal.container.array;
+import core.internal.container.hashtab;
import gcc.builtins;
import gcc.config;
import gcc.sections.common;
@@ -124,7 +124,7 @@ struct DSO
return _moduleGroup.modules;
}
- @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc
{
return _moduleGroup;
}
diff --git a/libphobos/libdruntime/gcc/sections/macho.d b/libphobos/libdruntime/gcc/sections/macho.d
index 3ce58a533c3..e6e79661a3e 100644
--- a/libphobos/libdruntime/gcc/sections/macho.d
+++ b/libphobos/libdruntime/gcc/sections/macho.d
@@ -31,8 +31,8 @@ import core.sys.darwin.mach.dyld;
import core.sys.darwin.mach.getsect;
import core.sys.posix.pthread;
import rt.minfo;
-import rt.util.container.array;
-import rt.util.container.hashtab;
+import core.internal.container.array;
+import core.internal.container.hashtab;
import gcc.sections.common;
version (GNU_EMUTLS)
@@ -66,7 +66,7 @@ struct DSO
return _moduleGroup.modules;
}
- @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc
{
return _moduleGroup;
}
diff --git a/libphobos/libdruntime/gcc/sections/pecoff.d b/libphobos/libdruntime/gcc/sections/pecoff.d
index ed0340e0311..736134913c1 100644
--- a/libphobos/libdruntime/gcc/sections/pecoff.d
+++ b/libphobos/libdruntime/gcc/sections/pecoff.d
@@ -30,8 +30,8 @@ import core.sys.windows.winbase;
import core.sys.windows.windef;
import core.sys.windows.winnt;
import rt.minfo;
-import rt.util.container.array;
-import rt.util.container.hashtab;
+import core.internal.container.array;
+import core.internal.container.hashtab;
import gcc.sections.common;
version (GNU_EMUTLS)
@@ -65,7 +65,7 @@ struct DSO
return _moduleGroup.modules;
}
- @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
+ @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc
{
return _moduleGroup;
}
diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d
index e96d1c48563..151755feed0 100644
--- a/libphobos/libdruntime/object.d
+++ b/libphobos/libdruntime/object.d
@@ -1,21 +1,62 @@
/**
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ * $(DIVC quickindex,
+ * $(BOOKTABLE,
+ * $(TR $(TH Category) $(TH Symbols))
+ * $(TR $(TD Arrays) $(TD
+ * $(MYREF assumeSafeAppend)
+ * $(MYREF capacity)
+ * $(MYREF idup)
+ * $(MYREF reserve)
+ * ))
+ * $(TR $(TD Associative arrays) $(TD
+ * $(MYREF byKey)
+ * $(MYREF byKeyValue)
+ * $(MYREF byValue)
+ * $(MYREF clear)
+ * $(MYREF get)
+ * $(MYREF keys)
+ * $(MYREF rehash)
+ * $(MYREF require)
+ * $(MYREF update)
+ * $(MYREF values)
+ * ))
+ * $(TR $(TD General) $(TD
+ * $(MYREF destroy)
+ * $(MYREF dup)
+ * $(MYREF hashOf)
+ * $(MYREF opEquals)
+ * ))
+ * $(TR $(TD Types) $(TD
+ * $(MYREF Error)
+ * $(MYREF Exception)
+ * $(MYREF noreturn)
+ * $(MYREF Object)
+ * $(MYREF Throwable)
+ * ))
+ * $(TR $(TD Type info) $(TD
+ * $(MYREF Interface)
+ * $(MYREF ModuleInfo)
+ * $(MYREF OffsetTypeInfo)
+ * $(MYREF RTInfoImpl)
+ * $(MYREF rtinfoNoPointers)
+ * $(MYREF TypeInfo)
+ * $(MYREF TypeInfo_Class)
+ * ))
+ * ))
+ *
* Forms the symbols available to all D programs. Includes Object, which is
* the root of the class object hierarchy. This module is implicitly
* imported.
*
* Copyright: Copyright Digital Mars 2000 - 2011.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright, Sean Kelly
+ * Source: $(DRUNTIMESRC object.d)
*/
module object;
-private
-{
- extern (C) Object _d_newclass(const TypeInfo_Class ci);
- extern (C) void rt_finalize(void *data, bool det=true);
-}
-
alias size_t = typeof(int.sizeof);
alias ptrdiff_t = typeof(cast(void*)0 - cast(void*)0);
@@ -29,7 +70,12 @@ alias string = immutable(char)[];
alias wstring = immutable(wchar)[];
alias dstring = immutable(dchar)[];
-version (D_ObjectiveC) public import core.attribute : selector;
+version (D_ObjectiveC)
+{
+ deprecated("explicitly import `selector` instead using: `import core.attribute : selector;`")
+ public import core.attribute : selector;
+}
+version (Posix) public import core.attribute : gnuAbiTag;
// Some ABIs use a complex varargs implementation requiring TypeInfo.argTypes().
version (GNU)
@@ -65,6 +111,20 @@ class Object
return typeid(this).name;
}
+ @system unittest
+ {
+ enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
+ enum fqn_unittest = "object.Object." ~ unittest_sym_name; // object.__unittest_LX_CY
+
+ class C {}
+
+ Object obj = new Object;
+ C c = new C;
+
+ assert(obj.toString() == "object.Object");
+ assert(c.toString() == fqn_unittest ~ ".C");
+ }
+
/**
* Compute hash function for Object.
*/
@@ -100,6 +160,23 @@ class Object
//return this !is o;
}
+ @system unittest
+ {
+ Object obj = new Object;
+
+ bool gotCaught;
+ try
+ {
+ obj.opCmp(new Object);
+ }
+ catch (Exception e)
+ {
+ gotCaught = true;
+ assert(e.msg == "need opCmp for class object.Object");
+ }
+ assert(gotCaught);
+ }
+
/**
* Test whether $(D this) is equal to $(D o).
* The default implementation only compares by identity (using the $(D is) operator).
@@ -149,9 +226,18 @@ class Object
}
return null;
}
+
+ @system unittest
+ {
+ Object valid_obj = Object.factory("object.Object");
+ Object invalid_obj = Object.factory("object.__this_class_doesnt_exist__");
+
+ assert(valid_obj !is null);
+ assert(invalid_obj is null);
+ }
}
-auto opEquals(Object lhs, Object rhs)
+bool opEquals(Object lhs, Object rhs)
{
// If aliased to the same object or both null => equal
if (lhs is rhs) return true;
@@ -159,6 +245,8 @@ auto opEquals(Object lhs, Object rhs)
// If either is null => non-equal
if (lhs is null || rhs is null) return false;
+ if (!lhs.opEquals(rhs)) return false;
+
// If same exact type => one call to method opEquals
if (typeid(lhs) is typeid(rhs) ||
!__ctfe && typeid(lhs).opEquals(typeid(rhs)))
@@ -166,22 +254,116 @@ auto opEquals(Object lhs, Object rhs)
(issue 7147). But CTFE also guarantees that equal TypeInfos are
always identical. So, no opEquals needed during CTFE. */
{
- return lhs.opEquals(rhs);
+ return true;
}
// General case => symmetric calls to method opEquals
- return lhs.opEquals(rhs) && rhs.opEquals(lhs);
+ return rhs.opEquals(lhs);
}
/************************
* Returns true if lhs and rhs are equal.
*/
-auto opEquals(const Object lhs, const Object rhs)
+bool opEquals(const Object lhs, const Object rhs)
{
// A hack for the moment.
return opEquals(cast()lhs, cast()rhs);
}
+/// If aliased to the same object or both null => equal
+@system unittest
+{
+ class F { int flag; this(int flag) { this.flag = flag; } }
+
+ F f;
+ assert(f == f); // both null
+ f = new F(1);
+ assert(f == f); // both aliased to the same object
+}
+
+/// If either is null => non-equal
+@system unittest
+{
+ class F { int flag; this(int flag) { this.flag = flag; } }
+ F f;
+ assert(!(new F(0) == f));
+ assert(!(f == new F(0)));
+}
+
+/// If same exact type => one call to method opEquals
+@system unittest
+{
+ class F
+ {
+ int flag;
+
+ this(int flag)
+ {
+ this.flag = flag;
+ }
+
+ override bool opEquals(const Object o)
+ {
+ return flag == (cast(F) o).flag;
+ }
+ }
+
+ F f;
+ assert(new F(0) == new F(0));
+ assert(!(new F(0) == new F(1)));
+}
+
+/// General case => symmetric calls to method opEquals
+@system unittest
+{
+ int fEquals, gEquals;
+
+ class Base
+ {
+ int flag;
+ this(int flag)
+ {
+ this.flag = flag;
+ }
+ }
+
+ class F : Base
+ {
+ this(int flag) { super(flag); }
+
+ override bool opEquals(const Object o)
+ {
+ fEquals++;
+ return flag == (cast(Base) o).flag;
+ }
+ }
+
+ class G : Base
+ {
+ this(int flag) { super(flag); }
+
+ override bool opEquals(const Object o)
+ {
+ gEquals++;
+ return flag == (cast(Base) o).flag;
+ }
+ }
+
+ assert(new F(1) == new G(1));
+ assert(fEquals == 1);
+ assert(gEquals == 1);
+}
+
+// To cover const Object opEquals
+@system unittest
+{
+ const Object obj1 = new Object;
+ const Object obj2 = new Object;
+
+ assert(obj1 == obj1);
+ assert(obj1 != obj2);
+}
+
private extern(C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow;
void setSameMutex(shared Object ownee, shared Object owner)
@@ -189,6 +371,24 @@ void setSameMutex(shared Object ownee, shared Object owner)
_d_setSameMutex(ownee, owner);
}
+@system unittest
+{
+ shared Object obj1 = new Object;
+ synchronized class C
+ {
+ void bar() {}
+ }
+ shared C obj2 = new shared(C);
+ obj2.bar();
+
+ assert(obj1.__monitor != obj2.__monitor);
+ assert(obj1.__monitor is null);
+
+ setSameMutex(obj1, obj2);
+ assert(obj1.__monitor == obj2.__monitor);
+ assert(obj1.__monitor !is null);
+}
+
/**
* Information about an interface.
* When an object is accessed via an interface, an Interface* appears as the
@@ -218,7 +418,7 @@ struct OffsetTypeInfo
*/
class TypeInfo
{
- override string toString() const pure @safe nothrow
+ override string toString() const @safe nothrow
{
return typeid(this).name;
}
@@ -228,18 +428,21 @@ class TypeInfo
return hashOf(this.toString());
}
- override int opCmp(Object o)
+ override int opCmp(Object rhs)
{
- import core.internal.traits : externDFunc;
- alias dstrcmp = externDFunc!("core.internal.string.dstrcmp",
- int function(scope const char[] s1, scope const char[] s2) @trusted pure nothrow @nogc);
-
- if (this is o)
+ if (this is rhs)
return 0;
- TypeInfo ti = cast(TypeInfo)o;
+ auto ti = cast(TypeInfo) rhs;
if (ti is null)
return 1;
- return dstrcmp(this.toString(), ti.toString());
+ return __cmp(this.toString(), ti.toString());
+ }
+
+ @system unittest
+ {
+ assert(typeid(void) <= typeid(void));
+ assert(typeid(void).opCmp(null));
+ assert(!typeid(void).opCmp(typeid(void)));
}
override bool opEquals(Object o)
@@ -254,6 +457,14 @@ class TypeInfo
return ti && this.toString() == ti.toString();
}
+ @system unittest
+ {
+ auto anotherObj = new Object();
+
+ assert(typeid(void).opEquals(typeid(void)));
+ assert(!typeid(void).opEquals(anotherObj));
+ }
+
/**
* Computes a hash of the instance of a type.
* Params:
@@ -280,8 +491,22 @@ class TypeInfo
/// Swaps two instances of the type.
void swap(void* p1, void* p2) const
{
- immutable size_t n = tsize;
- for (size_t i = 0; i < n; i++)
+ size_t remaining = tsize;
+ // If the type might contain pointers perform the swap in pointer-sized
+ // chunks in case a garbage collection pass interrupts this function.
+ if ((cast(size_t) p1 | cast(size_t) p2) % (void*).alignof == 0)
+ {
+ while (remaining >= (void*).sizeof)
+ {
+ void* tmp = *cast(void**) p1;
+ *cast(void**) p1 = *cast(void**) p2;
+ *cast(void**) p2 = tmp;
+ p1 += (void*).sizeof;
+ p2 += (void*).sizeof;
+ remaining -= (void*).sizeof;
+ }
+ }
+ for (size_t i = 0; i < remaining; i++)
{
byte t = (cast(byte *)p1)[i];
(cast(byte*)p1)[i] = (cast(byte*)p2)[i];
@@ -289,6 +514,36 @@ class TypeInfo
}
}
+ @system unittest
+ {
+ class _TypeInfo_Dummy : TypeInfo
+ {
+ override const(void)[] initializer() const { return []; }
+ @property override size_t tsize() nothrow pure const @safe @nogc { return tsize_val; }
+
+ size_t tsize_val;
+ }
+ auto dummy = new _TypeInfo_Dummy();
+ cast(void)dummy.initializer(); // For coverage completeness
+
+ int a = 2, b = -2;
+ dummy.swap(&a, &b);
+ // does nothing because tsize is 0
+ assert(a == 2);
+ assert(b == -2);
+
+ dummy.tsize_val = int.sizeof;
+ dummy.swap(&a, &b);
+ assert(a == -2);
+ assert(b == 2);
+
+ void* ptr_a = null, ptr_b = cast(void*)1;
+ dummy.tsize_val = (void*).sizeof;
+ dummy.swap(&ptr_a, &ptr_b);
+ assert(ptr_a is cast(void*)1);
+ assert(ptr_b is null);
+ }
+
/** Get TypeInfo for 'next' type, as defined by what kind of type this is,
null if none. */
@property inout(TypeInfo) next() nothrow pure inout @nogc { return null; }
@@ -302,7 +557,7 @@ class TypeInfo
abstract const(void)[] initializer() nothrow pure const @safe @nogc;
/** Get flags for type: 1 means GC should scan for pointers,
- 2 means arg of this type is passed in XMM register */
+ 2 means arg of this type is passed in SIMD register(s) if available */
@property uint flags() nothrow pure const @safe @nogc { return 0; }
/// Get type information on the contents of the type; null if not available
@@ -330,9 +585,87 @@ class TypeInfo
@property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return rtinfoHasPointers; } // better safe than sorry
}
+@system unittest
+{
+ class _TypeInfo_Dummy : TypeInfo
+ {
+ override const(void)[] initializer() const { return []; }
+ }
+ auto dummy = new _TypeInfo_Dummy();
+ cast(void)dummy.initializer(); // For coverage completeness
+
+ assert(dummy.rtInfo() is rtinfoHasPointers);
+ assert(typeid(void).rtInfo() is rtinfoNoPointers);
+
+ assert(dummy.tsize() == 0);
+
+ bool gotCaught;
+ try
+ {
+ dummy.compare(null, null);
+ } catch (Error e)
+ {
+ gotCaught = true;
+ assert(e.msg == "TypeInfo.compare is not implemented");
+ }
+ assert(gotCaught);
+
+ assert(dummy.equals(null, null));
+ assert(!dummy.equals(cast(void*)1, null));
+}
+
+@system unittest
+{
+ assert(typeid(void).next() is null);
+ assert(typeid(void).offTi() is null);
+ assert(typeid(void).tsize() == 1);
+
+ version (WithArgTypes)
+ {
+ TypeInfo ti1;
+ TypeInfo ti2;
+ assert(typeid(void).argTypes(ti1, ti2) == 0);
+ assert(typeid(void) is ti1);
+
+ assert(ti1 !is null);
+ assert(ti2 is null);
+ }
+}
+
+@system unittest
+{
+ class _ZypeInfo_Dummy : TypeInfo
+ {
+ override const(void)[] initializer() const { return []; }
+ }
+ auto dummy2 = new _ZypeInfo_Dummy();
+ cast(void)dummy2.initializer(); // For coverage completeness
+
+ assert(typeid(void) > dummy2);
+ assert(dummy2 < typeid(void));
+}
+
+@safe unittest
+{
+ enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
+ enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY
+
+ class _TypeInfo_Dummy : TypeInfo
+ {
+ override const(void)[] initializer() const { return []; }
+ }
+
+ auto dummy = new _TypeInfo_Dummy();
+ cast(void)dummy.initializer(); // For coverage completeness
+
+ assert(dummy.toString() == fqn_unittest ~ "._TypeInfo_Dummy");
+ assert(dummy.toHash() == hashOf(dummy.toString()));
+ assert(dummy.getHash(null) == 0);
+}
+
class TypeInfo_Enum : TypeInfo
{
- override string toString() const { return name; }
+ override string toString() const pure { return name; }
override bool opEquals(Object o)
{
@@ -343,15 +676,117 @@ class TypeInfo_Enum : TypeInfo
this.base == c.base;
}
+ @system unittest
+ {
+ enum E { A, B, C }
+ enum EE { A, B, C }
+
+ assert(typeid(E).opEquals(typeid(E)));
+ assert(!typeid(E).opEquals(typeid(EE)));
+ }
+
override size_t getHash(scope const void* p) const { return base.getHash(p); }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+ E e1 = E.A;
+ E e2 = E.B;
+
+ assert(typeid(E).getHash(&e1) == hashOf(E.A));
+ assert(typeid(E).getHash(&e2) == hashOf(E.B));
+
+ enum ES : string { A = "foo", B = "bar" }
+ ES es1 = ES.A;
+ ES es2 = ES.B;
+
+ assert(typeid(ES).getHash(&es1) == hashOf("foo"));
+ assert(typeid(ES).getHash(&es2) == hashOf("bar"));
+ }
+
override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ E e1 = E.A;
+ E e2 = E.B;
+
+ assert(typeid(E).equals(&e1, &e1));
+ assert(!typeid(E).equals(&e1, &e2));
+ }
+
override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ E e1 = E.A;
+ E e2 = E.B;
+
+ assert(typeid(E).compare(&e1, &e1) == 0);
+ assert(typeid(E).compare(&e1, &e2) < 0);
+ assert(typeid(E).compare(&e2, &e1) > 0);
+ }
+
override @property size_t tsize() nothrow pure const { return base.tsize; }
+
+ @safe unittest
+ {
+ enum E { A, B, C }
+ enum ES : string { A = "a", B = "b", C = "c"}
+
+ assert(typeid(E).tsize == E.sizeof);
+ assert(typeid(ES).tsize == ES.sizeof);
+ assert(typeid(E).tsize != ES.sizeof);
+ }
+
override void swap(void* p1, void* p2) const { return base.swap(p1, p2); }
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ E e1 = E.A;
+ E e2 = E.B;
+
+ typeid(E).swap(&e1, &e2);
+ assert(e1 == E.B);
+ assert(e2 == E.A);
+ }
+
override @property inout(TypeInfo) next() nothrow pure inout { return base.next; }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ assert(typeid(E).next is null);
+ }
+
override @property uint flags() nothrow pure const { return base.flags; }
+ @safe unittest
+ {
+ enum E { A, B, C }
+
+ assert(typeid(E).flags == 0);
+ }
+
+ override const(OffsetTypeInfo)[] offTi() const { return base.offTi; }
+
+ @system unittest
+ {
+ enum E { A, B, C }
+
+ assert(typeid(E).offTi is null);
+ }
+
+ override void destroy(void* p) const { return base.destroy(p); }
+ override void postblit(void* p) const { return base.postblit(p); }
+
override const(void)[] initializer() const
{
return m_init.length ? m_init : base.initializer();
@@ -371,7 +806,19 @@ class TypeInfo_Enum : TypeInfo
void[] m_init;
}
-unittest // issue 12233
+@safe unittest
+{
+ enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
+ enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY
+
+ enum E { A, B, C }
+ enum EE { A, B, C }
+
+ assert(typeid(E).toString() == fqn_unittest ~ ".E");
+}
+
+
+@safe unittest // issue 12233
{
static assert(is(typeof(TypeInfo.init) == TypeInfo));
assert(TypeInfo.init is null);
@@ -404,12 +851,8 @@ class TypeInfo_Pointer : TypeInfo
override int compare(in void* p1, in void* p2) const
{
- if (*cast(void**)p1 < *cast(void**)p2)
- return -1;
- else if (*cast(void**)p1 > *cast(void**)p2)
- return 1;
- else
- return 0;
+ const v1 = *cast(void**) p1, v2 = *cast(void**) p2;
+ return (v1 > v2) - (v1 < v2);
}
override @property size_t tsize() nothrow pure const
@@ -483,7 +926,7 @@ class TypeInfo_Array : TypeInfo
if (result)
return result;
}
- return cast(int)a1.length - cast(int)a2.length;
+ return (a1.length > a2.length) - (a1.length < a2.length);
}
override @property size_t tsize() nothrow pure const
@@ -531,12 +974,12 @@ class TypeInfo_StaticArray : TypeInfo
{
override string toString() const
{
- import core.internal.traits : externDFunc;
- alias sizeToTempString = externDFunc!("core.internal.string.unsignedToTempString",
- char[] function(ulong, return char[], uint) @safe pure nothrow @nogc);
+ import core.internal.string : unsignedToTempString;
char[20] tmpBuff = void;
- return value.toString() ~ "[" ~ sizeToTempString(len, tmpBuff, 10) ~ "]";
+ const lenString = unsignedToTempString(len, tmpBuff);
+
+ return (() @trusted => cast(string) (value.toString() ~ "[" ~ lenString ~ "]"))();
}
override bool opEquals(Object o)
@@ -585,28 +1028,22 @@ class TypeInfo_StaticArray : TypeInfo
override void swap(void* p1, void* p2) const
{
- import core.memory;
import core.stdc.string : memcpy;
- void* tmp;
- size_t sz = value.tsize;
- ubyte[16] buffer;
- void* pbuffer;
-
- if (sz < buffer.sizeof)
- tmp = buffer.ptr;
- else
- tmp = pbuffer = (new void[sz]).ptr;
-
- for (size_t u = 0; u < len; u += sz)
+ size_t remaining = value.tsize * len;
+ void[size_t.sizeof * 4] buffer = void;
+ while (remaining > buffer.length)
{
- size_t o = u * sz;
- memcpy(tmp, p1 + o, sz);
- memcpy(p1 + o, p2 + o, sz);
- memcpy(p2 + o, tmp, sz);
+ memcpy(buffer.ptr, p1, buffer.length);
+ memcpy(p1, p2, buffer.length);
+ memcpy(p2, buffer.ptr, buffer.length);
+ p1 += buffer.length;
+ p2 += buffer.length;
+ remaining -= buffer.length;
}
- if (pbuffer)
- GC.free(pbuffer);
+ memcpy(buffer.ptr, p1, remaining);
+ memcpy(p1, p2, remaining);
+ memcpy(p2, buffer.ptr, remaining);
}
override const(void)[] initializer() nothrow pure const
@@ -656,6 +1093,23 @@ class TypeInfo_StaticArray : TypeInfo
override @property immutable(void)* rtInfo() nothrow pure const @safe { return value.rtInfo(); }
}
+// https://issues.dlang.org/show_bug.cgi?id=21315
+@system unittest
+{
+ int[16] a, b;
+ foreach (int i; 0 .. 16)
+ {
+ a[i] = i;
+ b[i] = ~i;
+ }
+ typeid(int[16]).swap(&a, &b);
+ foreach (int i; 0 .. 16)
+ {
+ assert(a[i] == ~i);
+ assert(b[i] == i);
+ }
+}
+
class TypeInfo_AssociativeArray : TypeInfo
{
override string toString() const
@@ -731,7 +1185,7 @@ class TypeInfo_Vector : TypeInfo
override void swap(void* p1, void* p2) const { return base.swap(p1, p2); }
override @property inout(TypeInfo) next() nothrow pure inout { return base.next; }
- override @property uint flags() nothrow pure const { return base.flags; }
+ override @property uint flags() nothrow pure const { return 2; /* passed in SIMD register */ }
override const(void)[] initializer() nothrow pure const
{
@@ -750,14 +1204,14 @@ class TypeInfo_Vector : TypeInfo
class TypeInfo_Function : TypeInfo
{
- override string toString() const
+ override string toString() const pure @trusted
{
import core.demangle : demangleType;
alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure;
- SafeDemangleFunctionType demangle = ( () @trusted => cast(SafeDemangleFunctionType)(&demangleType) ) ();
+ SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType;
- return (() @trusted => cast(string)(demangle(deco))) ();
+ return cast(string) demangle(deco);
}
override bool opEquals(Object o)
@@ -790,7 +1244,7 @@ class TypeInfo_Function : TypeInfo
string deco;
}
-unittest
+@safe unittest
{
abstract class C
{
@@ -805,11 +1259,57 @@ unittest
assert(typeid(functionTypes[2]).toString() == "int function(int, int)");
}
+@system unittest
+{
+ abstract class C
+ {
+ void func();
+ void func(int a);
+ }
+
+ alias functionTypes = typeof(__traits(getVirtualFunctions, C, "func"));
+
+ Object obj = typeid(functionTypes[0]);
+ assert(obj.opEquals(typeid(functionTypes[0])));
+ assert(typeid(functionTypes[0]) == typeid(functionTypes[0]));
+ assert(typeid(functionTypes[0]) != typeid(functionTypes[1]));
+
+ assert(typeid(functionTypes[0]).tsize() == 0);
+ assert(typeid(functionTypes[0]).initializer() is null);
+ assert(typeid(functionTypes[0]).rtInfo() is null);
+}
+
class TypeInfo_Delegate : TypeInfo
{
- override string toString() const
+ override string toString() const pure @trusted
+ {
+ import core.demangle : demangleType;
+
+ alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure;
+ SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType;
+
+ return cast(string) demangle(deco);
+ }
+
+ @safe unittest
{
- return cast(string)(next.toString() ~ " delegate()");
+ double sqr(double x) { return x * x; }
+ sqr(double.init); // for coverage completeness
+
+ auto delegate_str = "double delegate(double) pure nothrow @nogc @safe";
+
+ assert(typeid(typeof(&sqr)).toString() == delegate_str);
+ assert(delegate_str.hashOf() == typeid(typeof(&sqr)).hashOf());
+ assert(typeid(typeof(&sqr)).toHash() == typeid(typeof(&sqr)).hashOf());
+
+ int g;
+
+ alias delegate_type = typeof((int a, int b) => a + b + g);
+ delegate_str = "int delegate(int, int) pure nothrow @nogc @safe";
+
+ assert(typeid(delegate_type).toString() == delegate_str);
+ assert(delegate_str.hashOf() == typeid(delegate_type).hashOf());
+ assert(typeid(delegate_type).toHash() == typeid(delegate_type).hashOf());
}
override bool opEquals(Object o)
@@ -820,6 +1320,19 @@ class TypeInfo_Delegate : TypeInfo
return c && this.deco == c.deco;
}
+ @system unittest
+ {
+ double sqr(double x) { return x * x; }
+ int dbl(int x) { return x + x; }
+ sqr(double.init); // for coverage completeness
+ dbl(int.init); // for coverage completeness
+
+ Object obj = typeid(typeof(&sqr));
+ assert(obj.opEquals(typeid(typeof(&sqr))));
+ assert(typeid(typeof(&sqr)) == typeid(typeof(&sqr)));
+ assert(typeid(typeof(&dbl)) != typeid(typeof(&sqr)));
+ }
+
override size_t getHash(scope const void* p) @trusted const
{
return hashOf(*cast(void delegate()*)p);
@@ -877,6 +1390,10 @@ class TypeInfo_Delegate : TypeInfo
override @property immutable(void)* rtInfo() nothrow pure const @safe { return RTInfo!(int delegate()); }
}
+private extern (C) Object _d_newclass(const TypeInfo_Class ci);
+private extern (C) int _d_isbaseof(scope TypeInfo_Class child,
+ scope const TypeInfo_Class parent) @nogc nothrow pure @safe; // rt.cast_
+
/**
* Runtime type information about a class.
* Can be retrieved from an object instance by using the
@@ -884,14 +1401,14 @@ class TypeInfo_Delegate : TypeInfo
*/
class TypeInfo_Class : TypeInfo
{
- override string toString() const { return info.name; }
+ override string toString() const pure { return name; }
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_Class)o;
- return c && this.info.name == c.info.name;
+ return c && this.name == c.name;
}
override size_t getHash(scope const void* p) @trusted const
@@ -947,8 +1464,8 @@ class TypeInfo_Class : TypeInfo
return m_offTi;
}
- @property auto info() @safe nothrow pure const { return this; }
- @property auto typeinfo() @safe nothrow pure const { return this; }
+ final @property auto info() @safe @nogc nothrow pure const return { return this; }
+ final @property auto typeinfo() @safe @nogc nothrow pure const return { return this; }
byte[] m_init; /** class static initializer
* (init.length gives size in bytes of class)
@@ -983,7 +1500,7 @@ class TypeInfo_Class : TypeInfo
* Search all modules for TypeInfo_Class corresponding to classname.
* Returns: null if not found
*/
- static const(TypeInfo_Class) find(in char[] classname)
+ static const(TypeInfo_Class) find(const scope char[] classname)
{
foreach (m; ModuleInfo)
{
@@ -1019,11 +1536,41 @@ class TypeInfo_Class : TypeInfo
}
return o;
}
+
+ /**
+ * Returns true if the class described by `child` derives from or is
+ * the class described by this `TypeInfo_Class`. Always returns false
+ * if the argument is null.
+ *
+ * Params:
+ * child = TypeInfo for some class
+ * Returns:
+ * true if the class described by `child` derives from or is the
+ * class described by this `TypeInfo_Class`.
+ */
+ final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
+ {
+ if (m_init.length)
+ {
+ // If this TypeInfo_Class represents an actual class we only need
+ // to check the child and its direct ancestors.
+ for (auto ti = cast() child; ti !is null; ti = ti.base)
+ if (ti is this)
+ return true;
+ return false;
+ }
+ else
+ {
+ // If this TypeInfo_Class is the .info field of a TypeInfo_Interface
+ // we also need to recursively check the child's interfaces.
+ return child !is null && _d_isbaseof(cast() child, this);
+ }
+ }
}
alias ClassInfo = TypeInfo_Class;
-unittest
+@safe unittest
{
// Bugzilla 14401
static class X
@@ -1039,7 +1586,7 @@ unittest
class TypeInfo_Interface : TypeInfo
{
- override string toString() const { return info.name; }
+ override string toString() const pure { return info.name; }
override bool opEquals(Object o)
{
@@ -1108,19 +1655,67 @@ class TypeInfo_Interface : TypeInfo
override @property uint flags() nothrow pure const { return 1; }
TypeInfo_Class info;
+
+ /**
+ * Returns true if the class described by `child` derives from the
+ * interface described by this `TypeInfo_Interface`. Always returns
+ * false if the argument is null.
+ *
+ * Params:
+ * child = TypeInfo for some class
+ * Returns:
+ * true if the class described by `child` derives from the
+ * interface described by this `TypeInfo_Interface`.
+ */
+ final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
+ {
+ return child !is null && _d_isbaseof(cast() child, this.info);
+ }
+
+ /**
+ * Returns true if the interface described by `child` derives from
+ * or is the interface described by this `TypeInfo_Interface`.
+ * Always returns false if the argument is null.
+ *
+ * Params:
+ * child = TypeInfo for some interface
+ * Returns:
+ * true if the interface described by `child` derives from or is
+ * the interface described by this `TypeInfo_Interface`.
+ */
+ final bool isBaseOf(scope const TypeInfo_Interface child) const @nogc nothrow pure @trusted
+ {
+ return child !is null && _d_isbaseof(cast() child.info, this.info);
+ }
+}
+
+@safe unittest
+{
+ enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
+ enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY
+
+ interface I {}
+
+ assert(fqn_unittest ~ ".I" == typeid(I).info.name);
+ assert((fqn_unittest ~ ".I").hashOf() == typeid(I).hashOf());
+ assert(typeid(I).toHash() == typeid(I).hashOf());
}
class TypeInfo_Struct : TypeInfo
{
override string toString() const { return name; }
+ override size_t toHash() const
+ {
+ return hashOf(this.mangledName);
+ }
+
override bool opEquals(Object o)
{
if (this is o)
return true;
auto s = cast(const TypeInfo_Struct)o;
- return s && this.name == s.name &&
- this.initializer().length == s.initializer().length;
+ return s && this.mangledName == s.mangledName;
}
override size_t getHash(scope const void* p) @trusted pure nothrow const
@@ -1219,23 +1814,45 @@ class TypeInfo_Struct : TypeInfo
(*xpostblit)(p);
}
- string name;
- void[] m_init; // initializer; m_init.ptr == null if 0 initialize
+ string mangledName;
+
+ final @property string name() nothrow const @trusted
+ {
+ import core.demangle : demangleType;
+
+ if (mangledName is null) // e.g., opaque structs
+ return null;
- @safe pure nothrow
- {
- size_t function(in void*) xtoHash;
- bool function(in void*, in void*) xopEquals;
- int function(in void*, in void*) xopCmp;
- string function(in void*) xtoString;
+ const key = cast(const void*) this; // faster lookup than TypeInfo_Struct, at the cost of potential duplicates per binary
+ static string[typeof(key)] demangledNamesCache; // per thread
+
+ // not nothrow:
+ //return demangledNamesCache.require(key, cast(string) demangleType(mangledName));
+
+ if (auto pDemangled = key in demangledNamesCache)
+ return *pDemangled;
+
+ const demangled = cast(string) demangleType(mangledName);
+ demangledNamesCache[key] = demangled;
+ return demangled;
+ }
+
+ void[] m_init; // initializer; m_init.ptr == null if 0 initialize
- enum StructFlags : uint
+ @safe pure nothrow
{
- hasPointers = 0x1,
- isDynamicType = 0x2, // built at runtime, needs type info in xdtor
+ size_t function(in void*) xtoHash;
+ bool function(in void*, in void*) xopEquals;
+ int function(in void*, in void*) xopCmp;
+ string function(in void*) xtoString;
+
+ enum StructFlags : uint
+ {
+ hasPointers = 0x1,
+ isDynamicType = 0x2, // built at runtime, needs type info in xdtor
+ }
+ StructFlags m_flags;
}
- StructFlags m_flags;
- }
union
{
void function(void*) xdtor;
@@ -1261,7 +1878,7 @@ class TypeInfo_Struct : TypeInfo
immutable(void)* m_RTInfo; // data for precise GC
}
-unittest
+@system unittest
{
struct S
{
@@ -1428,12 +2045,7 @@ class TypeInfo_Inout : TypeInfo_Const
}
}
-
-///////////////////////////////////////////////////////////////////////////////
-// ModuleInfo
-///////////////////////////////////////////////////////////////////////////////
-
-
+// Contents of Moduleinfo._flags
enum
{
MIctorstart = 0x1, // we've started constructing it
@@ -1452,31 +2064,35 @@ enum
MIname = 0x1000,
}
-
+/*****************************************
+ * An instance of ModuleInfo is generated into the object file for each compiled module.
+ *
+ * It provides access to various aspects of the module.
+ * It is not generated for betterC.
+ */
struct ModuleInfo
{
- uint _flags;
+ uint _flags; // MIxxxx
uint _index; // index into _moduleinfo_array[]
version (all)
{
deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.")
- void opAssign(in ModuleInfo m) { _flags = m._flags; _index = m._index; }
+ void opAssign(const scope ModuleInfo m) { _flags = m._flags; _index = m._index; }
}
else
{
@disable this();
- @disable this(this) const;
}
const:
- private void* addrOf(int flag) nothrow pure @nogc
+ private void* addrOf(int flag) return nothrow pure @nogc
in
{
assert(flag >= MItlsctor && flag <= MIname);
assert(!(flag & (flag - 1)) && !(flag & ~(flag - 1) << 1));
}
- body
+ do
{
import core.stdc.string : strlen;
@@ -1539,42 +2155,74 @@ const:
@property uint flags() nothrow pure @nogc { return _flags; }
+ /************************
+ * Returns:
+ * module constructor for thread locals, `null` if there isn't one
+ */
@property void function() tlsctor() nothrow pure @nogc
{
return flags & MItlsctor ? *cast(typeof(return)*)addrOf(MItlsctor) : null;
}
+ /************************
+ * Returns:
+ * module destructor for thread locals, `null` if there isn't one
+ */
@property void function() tlsdtor() nothrow pure @nogc
{
return flags & MItlsdtor ? *cast(typeof(return)*)addrOf(MItlsdtor) : null;
}
+ /*****************************
+ * Returns:
+ * address of a module's `const(MemberInfo)[] getMembers(string)` function, `null` if there isn't one
+ */
@property void* xgetMembers() nothrow pure @nogc
{
return flags & MIxgetMembers ? *cast(typeof(return)*)addrOf(MIxgetMembers) : null;
}
+ /************************
+ * Returns:
+ * module constructor, `null` if there isn't one
+ */
@property void function() ctor() nothrow pure @nogc
{
return flags & MIctor ? *cast(typeof(return)*)addrOf(MIctor) : null;
}
+ /************************
+ * Returns:
+ * module destructor, `null` if there isn't one
+ */
@property void function() dtor() nothrow pure @nogc
{
return flags & MIdtor ? *cast(typeof(return)*)addrOf(MIdtor) : null;
}
+ /************************
+ * Returns:
+ * module order independent constructor, `null` if there isn't one
+ */
@property void function() ictor() nothrow pure @nogc
{
return flags & MIictor ? *cast(typeof(return)*)addrOf(MIictor) : null;
}
+ /*************
+ * Returns:
+ * address of function that runs the module's unittests, `null` if there isn't one
+ */
@property void function() unitTest() nothrow pure @nogc
{
return flags & MIunitTest ? *cast(typeof(return)*)addrOf(MIunitTest) : null;
}
- @property immutable(ModuleInfo*)[] importedModules() nothrow pure @nogc
+ /****************
+ * Returns:
+ * array of pointers to the ModuleInfo's of modules imported by this one
+ */
+ @property immutable(ModuleInfo*)[] importedModules() return nothrow pure @nogc
{
if (flags & MIimportedModules)
{
@@ -1584,7 +2232,11 @@ const:
return null;
}
- @property TypeInfo_Class[] localClasses() nothrow pure @nogc
+ /****************
+ * Returns:
+ * array of TypeInfo_Class references for classes defined in this module
+ */
+ @property TypeInfo_Class[] localClasses() return nothrow pure @nogc
{
if (flags & MIlocalClasses)
{
@@ -1594,16 +2246,16 @@ const:
return null;
}
- @property string name() nothrow pure @nogc
+ /********************
+ * Returns:
+ * name of module, `null` if no name
+ */
+ @property string name() return nothrow pure @nogc
{
- if (true || flags & MIname) // always available for now
- {
- import core.stdc.string : strlen;
+ import core.stdc.string : strlen;
- auto p = cast(immutable char*)addrOf(MIname);
- return p[0 .. strlen(p)];
- }
- // return null;
+ auto p = cast(immutable char*) addrOf(MIname);
+ return p[0 .. strlen(p)];
}
static int opApply(scope int delegate(ModuleInfo*) dg)
@@ -1617,7 +2269,7 @@ const:
}
}
-unittest
+@system unittest
{
ModuleInfo* m1;
foreach (m; ModuleInfo)
@@ -1677,23 +2329,115 @@ class Throwable : Object
* caught $(D Exception) will be chained to the new $(D Throwable) via this
* field.
*/
- Throwable next;
+ private Throwable nextInChain;
- @nogc @safe pure nothrow this(string msg, Throwable next = null)
+ private uint _refcount; // 0 : allocated by GC
+ // 1 : allocated by _d_newThrowable()
+ // 2.. : reference count + 1
+
+ /**
+ * Returns:
+ * A reference to the _next error in the list. This is used when a new
+ * $(D Throwable) is thrown from inside a $(D catch) block. The originally
+ * caught $(D Exception) will be chained to the new $(D Throwable) via this
+ * field.
+ */
+ @property inout(Throwable) next() @safe inout return scope pure nothrow @nogc { return nextInChain; }
+
+ /**
+ * Replace next in chain with `tail`.
+ * Use `chainTogether` instead if at all possible.
+ */
+ @property void next(Throwable tail) @safe scope pure nothrow @nogc
+ {
+ if (tail && tail._refcount)
+ ++tail._refcount; // increment the replacement *first*
+
+ auto n = nextInChain;
+ nextInChain = null; // sever the tail before deleting it
+
+ if (n && n._refcount)
+ _d_delThrowable(n); // now delete the old tail
+
+ nextInChain = tail; // and set the new tail
+ }
+
+ /**
+ * Returns:
+ * mutable reference to the reference count, which is
+ * 0 - allocated by the GC, 1 - allocated by _d_newThrowable(),
+ * and >=2 which is the reference count + 1
+ * Note:
+ * Marked as `@system` to discourage casual use of it.
+ */
+ @system @nogc final pure nothrow ref uint refcount() return { return _refcount; }
+
+ /**
+ * Loop over the chain of Throwables.
+ */
+ int opApply(scope int delegate(Throwable) dg)
+ {
+ int result = 0;
+ for (Throwable t = this; t; t = t.nextInChain)
+ {
+ result = dg(t);
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Append `e2` to chain of exceptions that starts with `e1`.
+ * Params:
+ * e1 = start of chain (can be null)
+ * e2 = second part of chain (can be null)
+ * Returns:
+ * Throwable that is at the start of the chain; null if both `e1` and `e2` are null
+ */
+ static @__future @system @nogc pure nothrow Throwable chainTogether(return scope Throwable e1, return scope Throwable e2)
+ {
+ if (!e1)
+ return e2;
+ if (!e2)
+ return e1;
+ if (e2.refcount())
+ ++e2.refcount();
+
+ for (auto e = e1; 1; e = e.nextInChain)
+ {
+ if (!e.nextInChain)
+ {
+ e.nextInChain = e2;
+ break;
+ }
+ }
+ return e1;
+ }
+
+ @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
{
this.msg = msg;
- this.next = next;
+ this.nextInChain = nextInChain;
+ if (nextInChain && nextInChain._refcount)
+ ++nextInChain._refcount;
//this.info = _d_traceContext();
}
- @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable next = null)
+ @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null)
{
- this(msg, next);
+ this(msg, nextInChain);
this.file = file;
this.line = line;
//this.info = _d_traceContext();
}
+ @trusted nothrow ~this()
+ {
+ if (nextInChain && nextInChain._refcount)
+ _d_delThrowable(nextInChain);
+ }
+
/**
* Overrides $(D Object.toString) and returns the error message.
* Internally this forwards to the $(D toString) overload that
@@ -1714,15 +2458,13 @@ class Throwable : Object
*/
void toString(scope void delegate(in char[]) sink) const
{
- import core.internal.traits : externDFunc;
- alias sizeToTempString = externDFunc!("core.internal.string.unsignedToTempString",
- char[] function(ulong, return char[], uint) @safe pure nothrow @nogc);
+ import core.internal.string : unsignedToTempString;
char[20] tmpBuff = void;
sink(typeid(this).name);
sink("@"); sink(file);
- sink("("); sink(sizeToTempString(line, tmpBuff, 10)); sink(")");
+ sink("("); sink(unsignedToTempString(line, tmpBuff)); sink(")");
if (msg.length)
{
@@ -1744,6 +2486,19 @@ class Throwable : Object
}
}
}
+
+ /**
+ * Get the message describing the error.
+ * Base behavior is to return the `Throwable.msg` field.
+ * Override to return some other error message.
+ *
+ * Returns:
+ * Error message
+ */
+ @__future const(char)[] message() const
+ {
+ return this.msg;
+ }
}
@@ -1759,29 +2514,45 @@ class Exception : Throwable
{
/**
- * Creates a new instance of Exception. The next parameter is used
+ * Creates a new instance of Exception. The nextInChain parameter is used
* internally and should always be $(D null) when passed by user code.
* This constructor does not automatically throw the newly-created
* Exception; the $(D throw) statement should be used for that purpose.
*/
- @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
+ @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
{
- super(msg, file, line, next);
+ super(msg, file, line, nextInChain);
}
- @nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
+ @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
{
- super(msg, file, line, next);
+ super(msg, file, line, nextInChain);
}
}
-unittest
+///
+@safe unittest
+{
+ bool gotCaught;
+ try
+ {
+ throw new Exception("msg");
+ }
+ catch (Exception e)
+ {
+ gotCaught = true;
+ assert(e.msg == "msg");
+ }
+ assert(gotCaught);
+}
+
+@system unittest
{
{
auto e = new Exception("msg");
assert(e.file == __FILE__);
assert(e.line == __LINE__ - 2);
- assert(e.next is null);
+ assert(e.nextInChain is null);
assert(e.msg == "msg");
}
@@ -1789,7 +2560,7 @@ unittest
auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42);
assert(e.file == "hello");
assert(e.line == 42);
- assert(e.next !is null);
+ assert(e.nextInChain !is null);
assert(e.msg == "msg");
}
@@ -1797,9 +2568,14 @@ unittest
auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!"));
assert(e.file == "hello");
assert(e.line == 42);
- assert(e.next !is null);
+ assert(e.nextInChain !is null);
assert(e.msg == "msg");
}
+
+ {
+ auto e = new Exception("message");
+ assert(e.message == "message");
+ }
}
@@ -1815,20 +2591,20 @@ unittest
class Error : Throwable
{
/**
- * Creates a new instance of Error. The next parameter is used
+ * Creates a new instance of Error. The nextInChain parameter is used
* internally and should always be $(D null) when passed by user code.
* This constructor does not automatically throw the newly-created
* Error; the $(D throw) statement should be used for that purpose.
*/
- @nogc @safe pure nothrow this(string msg, Throwable next = null)
+ @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
{
- super(msg, next);
+ super(msg, nextInChain);
bypassedException = null;
}
- @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable next = null)
+ @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null)
{
- super(msg, file, line, next);
+ super(msg, file, line, nextInChain);
bypassedException = null;
}
@@ -1837,13 +2613,29 @@ class Error : Throwable
Throwable bypassedException;
}
-unittest
+///
+@system unittest
+{
+ bool gotCaught;
+ try
+ {
+ throw new Error("msg");
+ }
+ catch (Error e)
+ {
+ gotCaught = true;
+ assert(e.msg == "msg");
+ }
+ assert(gotCaught);
+}
+
+@safe unittest
{
{
auto e = new Error("msg");
assert(e.file is null);
assert(e.line == 0);
- assert(e.next is null);
+ assert(e.nextInChain is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
@@ -1852,7 +2644,7 @@ unittest
auto e = new Error("msg", new Exception("It's an Exception!"));
assert(e.file is null);
assert(e.line == 0);
- assert(e.next !is null);
+ assert(e.nextInChain !is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
@@ -1861,32 +2653,24 @@ unittest
auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!"));
assert(e.file == "hello");
assert(e.line == 42);
- assert(e.next !is null);
+ assert(e.nextInChain !is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
}
-/* Used in Exception Handling LSDA tables to 'wrap' C++ type info
- * so it can be distinguished from D TypeInfo
- */
-class __cpp_type_info_ptr
-{
- void* ptr; // opaque pointer to C++ RTTI type info
-}
-
extern (C)
{
// from druntime/src/rt/aaA.d
private struct AA { void* impl; }
// size_t _aaLen(in AA aa) pure nothrow @nogc;
- private void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti, in size_t valsz, in void* pkey) pure nothrow;
- private void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti, in size_t valsz, in void* pkey, out bool found) pure nothrow;
+ private void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey) pure nothrow;
+ private void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey, out bool found) pure nothrow;
// inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey);
- inout(void[]) _aaValues(inout AA aa, in size_t keysz, in size_t valsz, const TypeInfo tiValueArray) pure nothrow;
- inout(void[]) _aaKeys(inout AA aa, in size_t keysz, const TypeInfo tiKeyArray) pure nothrow;
- void* _aaRehash(AA* paa, in TypeInfo keyti) pure nothrow;
+ inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) pure nothrow;
+ inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow;
+ void* _aaRehash(AA* paa, const scope TypeInfo keyti) pure nothrow;
void _aaClear(AA aa) pure nothrow;
// alias _dg_t = extern(D) int delegate(void*);
@@ -1902,8 +2686,8 @@ extern (C)
void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe;
void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe;
- int _aaEqual(in TypeInfo tiRaw, in AA aa1, in AA aa2);
- hash_t _aaGetHash(in AA* aa, in TypeInfo tiRaw) nothrow;
+ int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2);
+ hash_t _aaGetHash(scope const AA* aa, scope const TypeInfo tiRaw) nothrow;
/*
_d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code.
@@ -1926,17 +2710,44 @@ alias AssociativeArray(Key, Value) = Value[Key];
* Params:
* aa = The associative array.
*/
-void clear(T : Value[Key], Value, Key)(T aa)
+void clear(Value, Key)(Value[Key] aa)
{
_aaClear(*cast(AA *) &aa);
}
/* ditto */
-void clear(T : Value[Key], Value, Key)(T* aa)
+void clear(Value, Key)(Value[Key]* aa)
{
_aaClear(*cast(AA *) aa);
}
+///
+@system unittest
+{
+ auto aa = ["k1": 2];
+ aa.clear;
+ assert("k1" !in aa);
+}
+
+// Issue 20559
+@system unittest
+{
+ static class Foo
+ {
+ int[string] aa;
+ alias aa this;
+ }
+
+ auto v = new Foo();
+ v["Hello World"] = 42;
+ v.clear;
+ assert("Hello World" !in v);
+
+ // Test for T*
+ static assert(!__traits(compiles, (&v).clear));
+ static assert( __traits(compiles, (*(&v)).clear));
+}
+
/***********************************
* Reorganizes the associative array in place so that lookups are more
* efficient.
@@ -2000,15 +2811,16 @@ V[K] dup(T : V[K], K, V)(T aa)
return *cast(V*)pv;
}
- if (auto postblit = _getPostblit!V())
- {
- foreach (k, ref v; aa)
- postblit(duplicateElem(k, v));
- }
- else
+ foreach (k, ref v; aa)
{
- foreach (k, ref v; aa)
+ static if (!__traits(hasPostblit, V))
duplicateElem(k, v);
+ else static if (__traits(isStaticArray, V))
+ _doPostblit(duplicateElem(k, v)[]);
+ else static if (!is(typeof(v.__xpostblit())) && is(immutable V == immutable UV, UV))
+ (() @trusted => *cast(UV*) &duplicateElem(k, v))().__xpostblit();
+ else
+ duplicateElem(k, v).__xpostblit();
}
return result;
@@ -2020,6 +2832,15 @@ V[K] dup(T : V[K], K, V)(T* aa)
return (*aa).dup;
}
+///
+@safe unittest
+{
+ auto aa = ["k1": 2];
+ auto a2 = aa.dup;
+ aa["k2"] = 3;
+ assert("k2" !in a2);
+}
+
// this should never be made public.
private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
{
@@ -2048,10 +2869,9 @@ auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
- @property ref front()
+ @property ref front() @trusted
{
- auto p = (() @trusted => cast(substInout!K*) _aaRangeFrontKey(r)) ();
- return *p;
+ return *cast(substInout!K*) _aaRangeFrontKey(r);
}
void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
@@ -2066,6 +2886,17 @@ auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc
return (*aa).byKey();
}
+///
+@safe unittest
+{
+ auto dict = [1: 0, 2: 0];
+ int sum;
+ foreach (v; dict.byKey)
+ sum += v;
+
+ assert(sum == 3);
+}
+
/***********************************
* Returns a forward range over the values of the associative array.
* Params:
@@ -2083,10 +2914,9 @@ auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
- @property ref front()
+ @property ref front() @trusted
{
- auto p = (() @trusted => cast(substInout!V*) _aaRangeFrontValue(r)) ();
- return *p;
+ return *cast(substInout!V*) _aaRangeFrontValue(r);
}
void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
@@ -2101,6 +2931,17 @@ auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
return (*aa).byValue();
}
+///
+@safe unittest
+{
+ auto dict = ["k1": 1, "k2": 2];
+ int sum;
+ foreach (v; dict.byValue)
+ sum += v;
+
+ assert(sum == 3);
+}
+
/***********************************
* Returns a forward range over the key value pairs of the associative array.
* Params:
@@ -2127,15 +2968,13 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
private void* keyp;
private void* valp;
- @property ref key() inout
+ @property ref key() inout @trusted
{
- auto p = (() @trusted => cast(substInout!K*) keyp) ();
- return *p;
+ return *cast(substInout!K*) keyp;
}
- @property ref value() inout
+ @property ref value() inout @trusted
{
- auto p = (() @trusted => cast(substInout!V*) valp) ();
- return *p;
+ return *cast(substInout!V*) valp;
}
}
return Pair(_aaRangeFrontKey(r),
@@ -2154,6 +2993,17 @@ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
return (*aa).byKeyValue();
}
+///
+@safe unittest
+{
+ auto dict = ["k1": 1, "k2": 2];
+ int sum;
+ foreach (e; dict.byKeyValue)
+ sum += e.value;
+
+ assert(sum == 3);
+}
+
/***********************************
* Returns a dynamic array, the elements of which are the keys in the
* associative array.
@@ -2169,9 +3019,12 @@ Key[] keys(T : Value[Key], Value, Key)(T aa) @property
alias realAA = aa;
else
const(Value[Key]) realAA = aa;
- auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
- auto res = *cast(Key[]*)&a;
- _doPostblit(res);
+ auto res = () @trusted {
+ auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
+ return *cast(Key[]*)&a;
+ }();
+ static if (__traits(hasPostblit, Key))
+ _doPostblit(res);
return res;
}
@@ -2181,7 +3034,18 @@ Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
return (*aa).keys;
}
-@system unittest
+///
+@safe unittest
+{
+ auto aa = [1: "v1", 2: "v2"];
+ int sum;
+ foreach (k; aa.keys)
+ sum += k;
+
+ assert(sum == 3);
+}
+
+@safe unittest
{
static struct S
{
@@ -2194,6 +3058,36 @@ Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
assert(s.keys.length == 0);
}
+@safe unittest
+{
+ @safe static struct Key
+ {
+ string str;
+ this(this) @safe {}
+ }
+ string[Key] aa;
+ static assert(__traits(compiles, {
+ void test() @safe {
+ const _ = aa.keys;
+ }
+ }));
+}
+
+@safe unittest
+{
+ static struct Key
+ {
+ string str;
+ this(this) @system {}
+ }
+ string[Key] aa;
+ static assert(!__traits(compiles, {
+ void test() @safe {
+ const _ = aa.keys;
+ }
+ }));
+}
+
/***********************************
* Returns a dynamic array, the elements of which are the values in the
* associative array.
@@ -2209,9 +3103,12 @@ Value[] values(T : Value[Key], Value, Key)(T aa) @property
alias realAA = aa;
else
const(Value[Key]) realAA = aa;
- auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
- auto res = *cast(Value[]*)&a;
- _doPostblit(res);
+ auto res = () @trusted {
+ auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
+ return *cast(Value[]*)&a;
+ }();
+ static if (__traits(hasPostblit, Value))
+ _doPostblit(res);
return res;
}
@@ -2221,7 +3118,18 @@ Value[] values(T : Value[Key], Value, Key)(T *aa) @property
return (*aa).values;
}
-@system unittest
+///
+@safe unittest
+{
+ auto aa = ["k1": 1, "k2": 2];
+ int sum;
+ foreach (e; aa.values)
+ sum += e;
+
+ assert(sum == 3);
+}
+
+@safe unittest
{
static struct S
{
@@ -2234,6 +3142,36 @@ Value[] values(T : Value[Key], Value, Key)(T *aa) @property
assert(s.values.length == 0);
}
+@safe unittest
+{
+ @safe static struct Value
+ {
+ string str;
+ this(this) @safe {}
+ }
+ Value[string] aa;
+ static assert(__traits(compiles, {
+ void test() @safe {
+ const _ = aa.values;
+ }
+ }));
+}
+
+@safe unittest
+{
+ static struct Value
+ {
+ string str;
+ this(this) @system {}
+ }
+ Value[string] aa;
+ static assert(!__traits(compiles, {
+ void test() @safe {
+ const _ = aa.values;
+ }
+ }));
+}
+
/***********************************
* Looks up key; if it exists returns corresponding value else evaluates and
* returns defaultValue.
@@ -2256,6 +3194,13 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
return (*aa).get(key, defaultValue);
}
+@safe unittest
+{
+ auto aa = ["k1": 1];
+ assert(aa.get("k1", 0) == 1);
+ assert(aa.get("k2", 0) == 0);
+}
+
/***********************************
* Looks up key; if it exists returns corresponding value else evaluates
* value, adds it to the associative array and returns it.
@@ -2281,48 +3226,38 @@ ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
{
auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
}
- return found ? *p : (*p = value);
-}
-
-// Constraints for aa update. Delegates, Functions or Functors (classes that
-// provide opCall) are allowed. See unittest for an example.
-private
-{
- template isCreateOperation(C, V)
+ if (found)
+ return *p;
+ else
{
- static if (is(C : V delegate()) || is(C : V function()))
- enum bool isCreateOperation = true;
- else static if (isCreateOperation!(typeof(&C.opCall), V))
- enum bool isCreateOperation = true;
- else
- enum bool isCreateOperation = false;
+ *p = value; // Not `return (*p = value)` since if `=` is overloaded
+ return *p; // this might not return a ref to the left-hand side.
}
+}
- template isUpdateOperation(U, V)
- {
- static if (is(U : V delegate(ref V)) || is(U : V function(ref V)))
- enum bool isUpdateOperation = true;
- else static if (isUpdateOperation!(typeof(&U.opCall), V))
- enum bool isUpdateOperation = true;
- else
- enum bool isUpdateOperation = false;
- }
+///
+@safe unittest
+{
+ auto aa = ["k1": 1];
+ assert(aa.require("k1", 0) == 1);
+ assert(aa.require("k2", 0) == 0);
+ assert(aa["k2"] == 0);
}
// Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test.
private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); }));
/***********************************
- * Looks up key; if it exists applies the update delegate else evaluates the
- * create delegate and adds it to the associative array
+ * Looks up key; if it exists applies the update callable else evaluates the
+ * create callable and adds it to the associative array
* Params:
* aa = The associative array.
* key = The key.
- * create = The delegate to apply on create.
- * update = The delegate to apply on update.
+ * create = The callable to apply on create.
+ * update = The callable to apply on update.
*/
void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
-if (isCreateOperation!(C, V) && isUpdateOperation!(U, V))
+if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void)))
{
bool found;
// if key is @safe-ly copyable, `update` may infer @safe
@@ -2340,10 +3275,35 @@ if (isCreateOperation!(C, V) && isUpdateOperation!(U, V))
if (!found)
*p = create();
else
- *p = update(*p);
+ {
+ static if (is(typeof(update(*p)) == void))
+ update(*p);
+ else
+ *p = update(*p);
+ }
+}
+
+///
+@system unittest
+{
+ auto aa = ["k1": 1];
+
+ aa.update("k1", {
+ return -1; // create (won't be executed)
+ }, (ref int v) {
+ v += 1; // update
+ });
+ assert(aa["k1"] == 2);
+
+ aa.update("k2", {
+ return 0; // create
+ }, (ref int v) {
+ v = -1; // update (won't be executed)
+ });
+ assert(aa["k2"] == 0);
}
-unittest
+@safe unittest
{
static struct S
{
@@ -2373,557 +3333,348 @@ unittest
static assert(!is(typeof(() @safe { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); })));
}
-private void _destructRecurse(S)(ref S s)
- if (is(S == struct))
-{
- static if (__traits(hasMember, S, "__xdtor") &&
- // Bugzilla 14746: Check that it's the exact member of S.
- __traits(isSame, S, __traits(parent, s.__xdtor)))
- s.__xdtor();
-}
-
-private void _destructRecurse(E, size_t n)(ref E[n] arr)
-{
- import core.internal.traits : hasElaborateDestructor;
-
- static if (hasElaborateDestructor!E)
- {
- foreach_reverse (ref elem; arr)
- _destructRecurse(elem);
- }
-}
-
-// Public and explicitly undocumented
-void _postblitRecurse(S)(ref S s)
- if (is(S == struct))
-{
- static if (__traits(hasMember, S, "__xpostblit") &&
- // Bugzilla 14746: Check that it's the exact member of S.
- __traits(isSame, S, __traits(parent, s.__xpostblit)))
- s.__xpostblit();
-}
-
-// Ditto
-void _postblitRecurse(E, size_t n)(ref E[n] arr)
+@safe unittest
{
- import core.internal.traits : hasElaborateCopyConstructor;
-
- static if (hasElaborateCopyConstructor!E)
+ struct S0
{
- size_t i;
- scope(failure)
+ int opCall(ref int v)
{
- for (; i != 0; --i)
- {
- _destructRecurse(arr[i - 1]); // What to do if this throws?
- }
+ return v + 1;
}
-
- for (i = 0; i < arr.length; ++i)
- _postblitRecurse(arr[i]);
}
-}
-
-// Test destruction/postblit order
-@safe nothrow pure unittest
-{
- string[] order;
- struct InnerTop
+ struct S1
{
- ~this() @safe nothrow pure
+ int opCall()()
{
- order ~= "destroy inner top";
+ return -2;
}
- this(this) @safe nothrow pure
+ T opCall(T)(ref T v)
{
- order ~= "copy inner top";
+ return v + 1;
}
}
- struct InnerMiddle {}
+ int[string] a = ["2" : 1];
+ a.update("2", () => -1, S0.init);
+ assert(a["2"] == 2);
+ a.update("0", () => -1, S0.init);
+ assert(a["0"] == -1);
+ a.update("2", S1.init, S1.init);
+ assert(a["2"] == 3);
+ a.update("1", S1.init, S1.init);
+ assert(a["1"] == -2);
+}
- version (none) // https://issues.dlang.org/show_bug.cgi?id=14242
- struct InnerElement
- {
- static char counter = '1';
+@system unittest
+{
+ int[string] aa;
- ~this() @safe nothrow pure
- {
- order ~= "destroy inner element #" ~ counter++;
- }
+ foreach (n; 0 .. 2)
+ aa.update("k1", {
+ return 7;
+ }, (ref int v) {
+ return v + 3;
+ });
+ assert(aa["k1"] == 10);
+}
- this(this) @safe nothrow pure
- {
- order ~= "copy inner element #" ~ counter++;
- }
- }
+version (CoreDdoc)
+{
+ // This lets DDoc produce better documentation.
- struct InnerBottom
- {
- ~this() @safe nothrow pure
- {
- order ~= "destroy inner bottom";
- }
+ /**
+ Calculates the hash value of `arg` with an optional `seed` initial value.
+ The result might not be equal to `typeid(T).getHash(&arg)`.
- this(this) @safe nothrow pure
- {
- order ~= "copy inner bottom";
- }
- }
+ Params:
+ arg = argument to calculate the hash value of
+ seed = optional `seed` value (may be used for hash chaining)
- struct S
+ Return: calculated hash value of `arg`
+ */
+ size_t hashOf(T)(auto ref T arg, size_t seed)
{
- char[] s;
- InnerTop top;
- InnerMiddle middle;
- version (none) InnerElement[3] array; // https://issues.dlang.org/show_bug.cgi?id=14242
- int a;
- InnerBottom bottom;
- ~this() @safe nothrow pure { order ~= "destroy outer"; }
- this(this) @safe nothrow pure { order ~= "copy outer"; }
+ static import core.internal.hash;
+ return core.internal.hash.hashOf(arg, seed);
}
-
- string[] destructRecurseOrder;
+ /// ditto
+ size_t hashOf(T)(auto ref T arg)
{
- S s;
- _destructRecurse(s);
- destructRecurseOrder = order;
- order = null;
+ static import core.internal.hash;
+ return core.internal.hash.hashOf(arg);
}
- assert(order.length);
- assert(destructRecurseOrder == order);
- order = null;
-
- S s;
- _postblitRecurse(s);
- assert(order.length);
- auto postblitRecurseOrder = order;
- order = null;
- S s2 = s;
- assert(order.length);
- assert(postblitRecurseOrder == order);
+ @safe unittest
+ {
+ auto h1 = "my.string".hashOf;
+ assert(h1 == "my.string".hashOf);
+ }
}
-
-// Test static struct
-nothrow @safe @nogc unittest
+else
{
- static int i = 0;
- static struct S { ~this() nothrow @safe @nogc { i = 42; } }
- S s;
- _destructRecurse(s);
- assert(i == 42);
+ public import core.internal.hash : hashOf;
}
-unittest
+///
+@system unittest
{
- // Bugzilla 14746
- static struct HasDtor
+ class MyObject
{
- ~this() { assert(0); }
+ size_t myMegaHash() const @safe pure nothrow
+ {
+ return 42;
+ }
}
- static struct Owner
+ struct Test
{
- HasDtor* ptr;
- alias ptr this;
+ int a;
+ string b;
+ MyObject c;
+ size_t toHash() const pure nothrow
+ {
+ size_t hash = a.hashOf();
+ hash = b.hashOf(hash);
+ size_t h1 = c.myMegaHash();
+ hash = h1.hashOf(hash); //Mix two hash values
+ return hash;
+ }
}
+}
- Owner o;
- assert(o.ptr is null);
- destroy(o); // must not reach in HasDtor.__dtor()
+bool _xopEquals(in void*, in void*)
+{
+ throw new Error("TypeInfo.equals is not implemented");
}
-unittest
+bool _xopCmp(in void*, in void*)
{
- // Bugzilla 14746
- static struct HasPostblit
- {
- this(this) { assert(0); }
- }
- static struct Owner
- {
- HasPostblit* ptr;
- alias ptr this;
- }
+ throw new Error("TypeInfo.compare is not implemented");
+}
- Owner o;
- assert(o.ptr is null);
- _postblitRecurse(o); // must not reach in HasPostblit.__postblit()
+/******************************************
+ * Create RTInfo for type T
+ */
+
+template RTInfoImpl(size_t[] pointerBitmap)
+{
+ immutable size_t[pointerBitmap.length] RTInfoImpl = pointerBitmap[];
}
-// Test handling of fixed-length arrays
-// Separate from first test because of https://issues.dlang.org/show_bug.cgi?id=14242
-unittest
+template NoPointersBitmapPayload(size_t N)
{
- string[] order;
+ enum size_t[N] NoPointersBitmapPayload = 0;
+}
- struct S
- {
- char id;
+template RTInfo(T)
+{
+ enum pointerBitmap = __traits(getPointerBitmap, T);
+ static if (pointerBitmap[1 .. $] == NoPointersBitmapPayload!(pointerBitmap.length - 1))
+ enum RTInfo = rtinfoNoPointers;
+ else
+ enum RTInfo = RTInfoImpl!(pointerBitmap).ptr;
+}
- this(this)
- {
- order ~= "copy #" ~ id;
- }
+/**
+* shortcuts for the precise GC, also generated by the compiler
+* used instead of the actual pointer bitmap
+*/
+enum immutable(void)* rtinfoNoPointers = null;
+enum immutable(void)* rtinfoHasPointers = cast(void*)1;
- ~this()
- {
- order ~= "destroy #" ~ id;
- }
- }
+// Helper functions
- string[] destructRecurseOrder;
+private inout(TypeInfo) getElement(return inout TypeInfo value) @trusted pure nothrow
+{
+ TypeInfo element = cast() value;
+ for (;;)
{
- S[3] arr = [S('1'), S('2'), S('3')];
- _destructRecurse(arr);
- destructRecurseOrder = order;
- order = null;
+ if (auto qualified = cast(TypeInfo_Const) element)
+ element = qualified.base;
+ else if (auto redefined = cast(TypeInfo_Enum) element)
+ element = redefined.base;
+ else if (auto staticArray = cast(TypeInfo_StaticArray) element)
+ element = staticArray.value;
+ else if (auto vector = cast(TypeInfo_Vector) element)
+ element = vector.base;
+ else
+ break;
}
- assert(order.length);
- assert(destructRecurseOrder == order);
- order = null;
-
- S[3] arr = [S('1'), S('2'), S('3')];
- _postblitRecurse(arr);
- assert(order.length);
- auto postblitRecurseOrder = order;
- order = null;
-
- auto arrCopy = arr;
- assert(order.length);
- assert(postblitRecurseOrder == order);
+ return cast(inout) element;
}
-// Test handling of failed postblit
-// Not nothrow or @safe because of https://issues.dlang.org/show_bug.cgi?id=14242
-/+ nothrow @safe +/ unittest
+private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, const size_t count) @trusted nothrow
{
- static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } }
- static string[] order;
- static struct Inner
- {
- char id;
-
- @safe:
- this(this)
- {
- order ~= "copy inner #" ~ id;
- if (id == '2')
- throw new FailedPostblitException();
- }
+ if (!count)
+ return 0;
- ~this() nothrow
- {
- order ~= "destroy inner #" ~ id;
- }
- }
+ const size_t elementSize = element.tsize;
+ if (!elementSize)
+ return 0;
- static struct Outer
+ static bool hasCustomToHash(const scope TypeInfo value) @trusted pure nothrow
{
- Inner inner1, inner2, inner3;
-
- nothrow @safe:
- this(char first, char second, char third)
- {
- inner1 = Inner(first);
- inner2 = Inner(second);
- inner3 = Inner(third);
- }
+ const element = getElement(value);
- this(this)
- {
- order ~= "copy outer";
- }
+ if (const struct_ = cast(const TypeInfo_Struct) element)
+ return !!struct_.xtoHash;
- ~this()
- {
- order ~= "destroy outer";
- }
+ return cast(const TypeInfo_Array) element
+ || cast(const TypeInfo_AssociativeArray) element
+ || cast(const ClassInfo) element
+ || cast(const TypeInfo_Interface) element;
}
- auto outer = Outer('1', '2', '3');
-
- try _postblitRecurse(outer);
- catch (FailedPostblitException) {}
- catch (Exception) assert(false);
-
- auto postblitRecurseOrder = order;
- order = null;
-
- try auto copy = outer;
- catch (FailedPostblitException) {}
- catch (Exception) assert(false);
-
- assert(postblitRecurseOrder == order);
- order = null;
-
- Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')];
-
- try _postblitRecurse(arr);
- catch (FailedPostblitException) {}
- catch (Exception) assert(false);
+ if (!hasCustomToHash(element))
+ return hashOf(ptr[0 .. elementSize * count]);
- postblitRecurseOrder = order;
- order = null;
+ size_t hash = 0;
+ foreach (size_t i; 0 .. count)
+ hash = hashOf(element.getHash(ptr + i * elementSize), hash);
+ return hash;
+}
- try auto arrCopy = arr;
- catch (FailedPostblitException) {}
- catch (Exception) assert(false);
+/// Provide the .dup array property.
+@property auto dup(T)(T[] a)
+ if (!is(const(T) : T))
+{
+ import core.internal.traits : Unconst;
+ static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~
+ " to "~Unconst!T.stringof~" in dup.");
- assert(postblitRecurseOrder == order);
+ return _dup!(T, Unconst!T)(a);
}
-/++
- Destroys the given object and puts it in an invalid state. It's used to
- _destroy an object so that any cleanup which its destructor or finalizer
- does is done and so that it no longer references any other objects. It does
- $(I not) initiate a GC cycle or free any GC memory.
- +/
-void destroy(T)(T obj) if (is(T == class))
+///
+@safe unittest
{
- rt_finalize(cast(void*)obj);
+ auto arr = [1, 2];
+ auto arr2 = arr.dup;
+ arr[0] = 0;
+ assert(arr == [0, 2]);
+ assert(arr2 == [1, 2]);
}
/// ditto
-void destroy(T)(T obj) if (is(T == interface))
-{
- destroy(cast(Object)obj);
-}
-
-version (unittest) unittest
-{
- interface I { }
- {
- class A: I { string s = "A"; this() {} }
- auto a = new A, b = new A;
- a.s = b.s = "asd";
- destroy(a);
- assert(a.s == "A");
-
- I i = b;
- destroy(i);
- assert(b.s == "A");
- }
- {
- static bool destroyed = false;
- class B: I
- {
- string s = "B";
- this() {}
- ~this()
- {
- destroyed = true;
- }
- }
- auto a = new B, b = new B;
- a.s = b.s = "asd";
- destroy(a);
- assert(destroyed);
- assert(a.s == "B");
-
- destroyed = false;
- I i = b;
- destroy(i);
- assert(destroyed);
- assert(b.s == "B");
- }
- // this test is invalid now that the default ctor is not run after clearing
- version (none)
- {
- class C
- {
- string s;
- this()
- {
- s = "C";
- }
- }
- auto a = new C;
- a.s = "asd";
- destroy(a);
- assert(a.s == "C");
- }
+// const overload to support implicit conversion to immutable (unique result, see DIP29)
+@property T[] dup(T)(const(T)[] a)
+ if (is(const(T) : T))
+{
+ return _dup!(const(T), T)(a);
}
-/// ditto
-void destroy(T)(ref T obj) if (is(T == struct))
-{
- _destructRecurse(obj);
- () @trusted {
- auto buf = (cast(ubyte*) &obj)[0 .. T.sizeof];
- auto init = cast(ubyte[])typeid(T).initializer();
- if (init.ptr is null) // null ptr means initialize to 0s
- buf[] = 0;
- else
- buf[] = init[];
- } ();
-}
-version (unittest) nothrow @safe @nogc unittest
-{
- {
- struct A { string s = "A"; }
- A a;
- a.s = "asd";
- destroy(a);
- assert(a.s == "A");
- }
- {
- static int destroyed = 0;
- struct C
- {
- string s = "C";
- ~this() nothrow @safe @nogc
- {
- destroyed ++;
- }
- }
-
- struct B
- {
- C c;
- string s = "B";
- ~this() nothrow @safe @nogc
- {
- destroyed ++;
- }
- }
- B a;
- a.s = "asd";
- a.c.s = "jkl";
- destroy(a);
- assert(destroyed == 2);
- assert(a.s == "B");
- assert(a.c.s == "C" );
- }
+/// Provide the .idup array property.
+@property immutable(T)[] idup(T)(T[] a)
+{
+ static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~
+ " to immutable in idup.");
+ return _dup!(T, immutable(T))(a);
}
/// ditto
-void destroy(T : U[n], U, size_t n)(ref T obj) if (!is(T == struct))
+@property immutable(T)[] idup(T:void)(const(T)[] a)
{
- foreach_reverse (ref e; obj[])
- destroy(e);
+ return a.dup;
}
-version (unittest) unittest
+///
+@safe unittest
{
- int[2] a;
- a[0] = 1;
- a[1] = 2;
- destroy(a);
- assert(a == [ 0, 0 ]);
+ char[] arr = ['a', 'b', 'c'];
+ string s = arr.idup;
+ arr[0] = '.';
+ assert(s == "abc");
}
-unittest
+private U[] _dup(T, U)(scope T[] a) pure nothrow @trusted if (__traits(isPOD, T))
{
- static struct vec2f {
- float[2] values;
- alias values this;
- }
+ if (__ctfe)
+ return _dupCtfe!(T, U)(a);
- vec2f v;
- destroy!vec2f(v);
+ import core.stdc.string : memcpy;
+ auto arr = _d_newarrayU(typeid(T[]), a.length);
+ memcpy(arr.ptr, cast(const(void)*) a.ptr, T.sizeof * a.length);
+ return *cast(U[]*) &arr;
}
-unittest
+private U[] _dupCtfe(T, U)(scope T[] a)
{
- // Bugzilla 15009
- static string op;
- static struct S
- {
- int x;
- this(int x) { op ~= "C" ~ cast(char)('0'+x); this.x = x; }
- this(this) { op ~= "P" ~ cast(char)('0'+x); }
- ~this() { op ~= "D" ~ cast(char)('0'+x); }
- }
-
- {
- S[2] a1 = [S(1), S(2)];
- op = "";
- }
- assert(op == "D2D1"); // built-in scope destruction
- {
- S[2] a1 = [S(1), S(2)];
- op = "";
- destroy(a1);
- assert(op == "D2D1"); // consistent with built-in behavior
- }
-
+ static if (is(T : void))
+ assert(0, "Cannot dup a void[] array at compile time.");
+ else
{
- S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]];
- op = "";
- }
- assert(op == "D4D3D2D1");
- {
- S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]];
- op = "";
- destroy(a2);
- assert(op == "D4D3D2D1", op);
+ U[] res;
+ foreach (ref e; a)
+ res ~= e;
+ return res;
}
}
-/// ditto
-void destroy(T)(ref T obj)
- if (!is(T == struct) && !is(T == interface) && !is(T == class) && !_isStaticArray!T)
+private U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T))
{
- obj = T.init;
-}
+ // note: copyEmplace is `@system` inside a `@trusted` block, so the __ctfe branch
+ // has the extra duty to infer _dup `@system` when the copy-constructor is `@system`.
+ if (__ctfe)
+ return _dupCtfe!(T, U)(a);
-template _isStaticArray(T : U[N], U, size_t N)
-{
- enum bool _isStaticArray = true;
-}
+ import core.lifetime: copyEmplace;
+ U[] res = () @trusted {
+ auto arr = cast(U*) _d_newarrayU(typeid(T[]), a.length);
+ size_t i;
+ scope (failure)
+ {
+ import core.internal.lifetime: emplaceInitializer;
+ // Initialize all remaining elements to not destruct garbage
+ foreach (j; i .. a.length)
+ emplaceInitializer(cast() arr[j]);
+ }
+ for (; i < a.length; i++)
+ {
+ copyEmplace(a.ptr[i], arr[i]);
+ }
+ return cast(U[])(arr[0..a.length]);
+ } ();
-template _isStaticArray(T)
-{
- enum bool _isStaticArray = false;
+ return res;
}
-version (unittest) unittest
+// https://issues.dlang.org/show_bug.cgi?id=22107
+@safe unittest
{
- {
- int a = 42;
- destroy(a);
- assert(a == 0);
- }
- {
- float a = 42;
- destroy(a);
- assert(isnan(a));
- }
-}
+ static int i;
+ @safe struct S
+ {
+ this(this) { i++; }
+ }
-version (unittest)
-{
- private bool isnan(float x)
+ void fun(scope S[] values...) @safe
{
- return x != x;
+ values.dup;
}
}
-private
-{
- extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow;
- extern (C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void *arrptr) pure nothrow;
-}
+// HACK: This is a lie. `_d_arraysetcapacity` is neither `nothrow` nor `pure`, but this lie is
+// necessary for now to prevent breaking code.
+private extern (C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* arrptr) pure nothrow;
/**
- * (Property) Gets the current _capacity of a slice. The _capacity is the size
- * that the slice can grow to before the underlying array must be
- * reallocated or extended.
- *
- * If an append must reallocate a slice with no possibility of extension, then
- * `0` is returned. This happens when the slice references a static array, or
- * if another slice references elements past the end of the current slice.
- *
- * Note: The _capacity of a slice may be impacted by operations on other slices.
- */
+(Property) Gets the current _capacity of a slice. The _capacity is the size
+that the slice can grow to before the underlying array must be
+reallocated or extended.
+
+If an append must reallocate a slice with no possibility of extension, then
+`0` is returned. This happens when the slice references a static array, or
+if another slice references elements past the end of the current slice.
+
+Note: The _capacity of a slice may be impacted by operations on other slices.
+*/
@property size_t capacity(T)(T[] arr) pure nothrow @trusted
{
- return _d_arraysetcapacity(typeid(T[]), 0, cast(void *)&arr);
+ return _d_arraysetcapacity(typeid(T[]), 0, cast(void[]*)&arr);
}
+
///
@safe unittest
{
@@ -2948,38 +3699,54 @@ private
}
/**
- * Reserves capacity for a slice. The capacity is the size
- * that the slice can grow to before the underlying array must be
- * reallocated or extended.
- *
- * Returns: The new capacity of the array (which may be larger than
- * the requested capacity).
- */
+Reserves capacity for a slice. The capacity is the size
+that the slice can grow to before the underlying array must be
+reallocated or extended.
+
+Returns: The new capacity of the array (which may be larger than
+the requested capacity).
+*/
size_t reserve(T)(ref T[] arr, size_t newcapacity) pure nothrow @trusted
{
- return _d_arraysetcapacity(typeid(T[]), newcapacity, cast(void *)&arr);
+ if (__ctfe)
+ return newcapacity;
+ else
+ return _d_arraysetcapacity(typeid(T[]), newcapacity, cast(void[]*)&arr);
}
+
///
-unittest
+@safe unittest
{
//Static array slice: no capacity. Reserve relocates.
int[4] sarray = [1, 2, 3, 4];
int[] slice = sarray[];
auto u = slice.reserve(8);
assert(u >= 8);
- assert(sarray.ptr !is slice.ptr);
+ assert(&sarray[0] !is &slice[0]);
assert(slice.capacity == u);
//Dynamic array slices
int[] a = [1, 2, 3, 4];
a.reserve(8); //prepare a for appending 4 more items
- auto p = a.ptr;
+ auto p = &a[0];
u = a.capacity;
a ~= [5, 6, 7, 8];
- assert(p == a.ptr); //a should not have been reallocated
+ assert(p == &a[0]); //a should not have been reallocated
assert(u == a.capacity); //a should not have been extended
}
+// https://issues.dlang.org/show_bug.cgi?id=12330, reserve() at CTFE time
+@safe unittest
+{
+ int[] foo() {
+ int[] result;
+ auto a = result.reserve = 5;
+ assert(a == 5);
+ return result;
+ }
+ enum r = foo();
+}
+
// Issue 6646: should be possible to use array.reserve from SafeD.
@safe unittest
{
@@ -2987,28 +3754,33 @@ unittest
a.reserve(10);
}
+// HACK: This is a lie. `_d_arrayshrinkfit` is not `nothrow`, but this lie is necessary
+// for now to prevent breaking code.
+private extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow;
+
/**
- * Assume that it is safe to append to this array. Appends made to this array
- * after calling this function may append in place, even if the array was a
- * slice of a larger array to begin with.
- *
- * Use this only when it is certain there are no elements in use beyond the
- * array in the memory block. If there are, those elements will be
- * overwritten by appending to this array.
- *
- * Warning: Calling this function, and then using references to data located after the
- * given array results in undefined behavior.
- *
- * Returns:
- * The input is returned.
- */
-auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow
+Assume that it is safe to append to this array. Appends made to this array
+after calling this function may append in place, even if the array was a
+slice of a larger array to begin with.
+
+Use this only when it is certain there are no elements in use beyond the
+array in the memory block. If there are, those elements will be
+overwritten by appending to this array.
+
+Warning: Calling this function, and then using references to data located after the
+given array results in undefined behavior.
+
+Returns:
+ The input is returned.
+*/
+auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow @system
{
_d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr));
return arr;
}
+
///
-unittest
+@system unittest
{
int[] a = [1, 2, 3, 4];
@@ -3026,7 +3798,7 @@ unittest
}
}
-unittest
+@system unittest
{
int[] arr;
auto newcap = arr.reserve(2000);
@@ -3042,7 +3814,7 @@ unittest
assert(ptr == arr.ptr);
}
-unittest
+@system unittest
{
int[] arr = [1, 2, 3];
void foo(ref int[] i)
@@ -3056,7 +3828,7 @@ unittest
}
// https://issues.dlang.org/show_bug.cgi?id=10574
-unittest
+@system unittest
{
int[] a;
immutable(int[]) b;
@@ -3070,914 +3842,913 @@ unittest
assert(is(typeof(b3) == immutable(int[])));
}
-version (none)
-{
- // enforce() copied from Phobos std.contracts for destroy(), left out until
- // we decide whether to use it.
-
+private extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow;
- T _enforce(T, string file = __FILE__, int line = __LINE__)
- (T value, lazy const(char)[] msg = null)
+private void _doPostblit(T)(T[] arr)
+{
+ // infer static postblit type, run postblit if any
+ static if (__traits(hasPostblit, T))
{
- if (!value) bailOut(file, line, msg);
- return value;
+ static if (__traits(isStaticArray, T) && is(T : E[], E))
+ _doPostblit(cast(E[]) arr);
+ else static if (!is(typeof(arr[0].__xpostblit())) && is(immutable T == immutable U, U))
+ foreach (ref elem; (() @trusted => cast(U[]) arr)())
+ elem.__xpostblit();
+ else
+ foreach (ref elem; arr)
+ elem.__xpostblit();
}
+}
- T _enforce(T, string file = __FILE__, int line = __LINE__)
- (T value, scope void delegate() dg)
- {
- if (!value) dg();
- return value;
- }
+@safe unittest
+{
+ static struct S1 { int* p; }
+ static struct S2 { @disable this(); }
+ static struct S3 { @disable this(this); }
- T _enforce(T)(T value, lazy Exception ex)
+ int dg1() pure nothrow @safe
{
- if (!value) throw ex();
- return value;
+ {
+ char[] m;
+ string i;
+ m = m.dup;
+ i = i.idup;
+ m = i.dup;
+ i = m.idup;
+ }
+ {
+ S1[] m;
+ immutable(S1)[] i;
+ m = m.dup;
+ i = i.idup;
+ static assert(!is(typeof(m.idup)));
+ static assert(!is(typeof(i.dup)));
+ }
+ {
+ S3[] m;
+ immutable(S3)[] i;
+ static assert(!is(typeof(m.dup)));
+ static assert(!is(typeof(i.idup)));
+ }
+ {
+ shared(S1)[] m;
+ m = m.dup;
+ static assert(!is(typeof(m.idup)));
+ }
+ {
+ int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0);
+ }
+ return 1;
}
- private void _bailOut(string file, int line, in char[] msg)
+ int dg2() pure nothrow @safe
{
- char[21] buf;
- throw new Exception(cast(string)(file ~ "(" ~ ulongToString(buf[], line) ~ "): " ~ (msg ? msg : "Enforcement failed")));
+ {
+ S2[] m = [S2.init, S2.init];
+ immutable(S2)[] i = [S2.init, S2.init];
+ m = m.dup;
+ m = i.dup;
+ i = m.idup;
+ i = i.idup;
+ }
+ return 2;
}
-}
-
-/***************************************
- * Helper function used to see if two containers of different
- * types have the same contents in the same sequence.
- */
+ enum a = dg1();
+ enum b = dg2();
+ assert(dg1() == a);
+ assert(dg2() == b);
+}
-bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
+@system unittest
{
- if (a1.length != a2.length)
- return false;
+ static struct Sunpure { this(this) @safe nothrow {} }
+ static struct Sthrow { this(this) @safe pure {} }
+ static struct Sunsafe { this(this) @system pure nothrow {} }
+ static struct Snocopy { @disable this(this); }
- // This is function is used as a compiler intrinsic and explicitly written
- // in a lowered flavor to use as few CTFE instructions as possible.
- size_t idx = 0;
- immutable length = a1.length;
+ [].dup!Sunpure;
+ [].dup!Sthrow;
+ cast(void) [].dup!Sunsafe;
+ static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
+ static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
+ static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
+ static assert(!__traits(compiles, () { [].dup!Snocopy; }));
- for (;idx < length;++idx)
- {
- if (a1[idx] != a2[idx])
- return false;
- }
- return true;
+ [].idup!Sunpure;
+ [].idup!Sthrow;
+ [].idup!Sunsafe;
+ static assert(!__traits(compiles, () pure { [].idup!Sunpure; }));
+ static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; }));
+ static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; }));
+ static assert(!__traits(compiles, () { [].idup!Snocopy; }));
}
-version (D_Ddoc)
+@safe unittest
{
- // This lets DDoc produce better documentation.
+ // test that the copy-constructor is called with .dup
+ static struct ArrElem
+ {
+ int a;
+ this(int a)
+ {
+ this.a = a;
+ }
+ this(ref const ArrElem)
+ {
+ a = 2;
+ }
+ this(ref ArrElem) immutable
+ {
+ a = 3;
+ }
+ }
- /**
- Calculates the hash value of `arg` with an optional `seed` initial value.
- The result might not be equal to `typeid(T).getHash(&arg)`.
+ auto arr = [ArrElem(1), ArrElem(1)];
- Params:
- arg = argument to calculate the hash value of
- seed = optional `seed` value (may be used for hash chaining)
+ ArrElem[] b = arr.dup;
+ assert(b[0].a == 2 && b[1].a == 2);
- Return: calculated hash value of `arg`
- */
- size_t hashOf(T)(auto ref T arg, size_t seed)
- {
- static import core.internal.hash;
- return core.internal.hash.hashOf(arg, seed);
- }
- /// ditto
- size_t hashOf(T)(auto ref T arg)
- {
- static import core.internal.hash;
- return core.internal.hash.hashOf(arg);
- }
-}
-else
-{
- public import core.internal.hash : hashOf;
+ immutable ArrElem[] c = arr.idup;
+ assert(c[0].a == 3 && c[1].a == 3);
}
-unittest
+@system unittest
{
- // Issue # 16654 / 16764
- auto a = [1];
- auto b = a.dup;
- assert(hashOf(a) == hashOf(b));
+ static struct Sunpure { this(ref const typeof(this)) @safe nothrow {} }
+ static struct Sthrow { this(ref const typeof(this)) @safe pure {} }
+ static struct Sunsafe { this(ref const typeof(this)) @system pure nothrow {} }
+ [].dup!Sunpure;
+ [].dup!Sthrow;
+ cast(void) [].dup!Sunsafe;
+ static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
+ static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
+ static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
+
+ // for idup to work on structs that have copy constructors, it is necessary
+ // that the struct defines a copy constructor that creates immutable objects
+ static struct ISunpure { this(ref const typeof(this)) immutable @safe nothrow {} }
+ static struct ISthrow { this(ref const typeof(this)) immutable @safe pure {} }
+ static struct ISunsafe { this(ref const typeof(this)) immutable @system pure nothrow {} }
+ [].idup!ISunpure;
+ [].idup!ISthrow;
+ [].idup!ISunsafe;
+ static assert(!__traits(compiles, () pure { [].idup!ISunpure; }));
+ static assert(!__traits(compiles, () nothrow { [].idup!ISthrow; }));
+ static assert(!__traits(compiles, () @safe { [].idup!ISunsafe; }));
}
-bool _xopEquals(in void*, in void*)
+@safe unittest
{
- throw new Error("TypeInfo.equals is not implemented");
+ static int*[] pureFoo() pure { return null; }
+ { char[] s; immutable x = s.dup; }
+ { immutable x = (cast(int*[])null).dup; }
+ { immutable x = pureFoo(); }
+ { immutable x = pureFoo().dup; }
}
-bool _xopCmp(in void*, in void*)
+@safe unittest
{
- throw new Error("TypeInfo.compare is not implemented");
+ auto a = [1, 2, 3];
+ auto b = a.dup;
+ debug(SENTINEL) {} else
+ assert(b.capacity >= 3);
}
-void __ctfeWrite(const string s) @nogc @safe pure nothrow {}
+@system unittest
+{
+ // Bugzilla 12580
+ void[] m = [0];
+ shared(void)[] s = [cast(shared)1];
+ immutable(void)[] i = [cast(immutable)2];
-/******************************************
- * Create RTInfo for type T
- */
+ s = s.dup;
+ static assert(is(typeof(s.dup) == shared(void)[]));
-template RTInfoImpl(size_t[] pointerBitmap)
-{
- immutable size_t[pointerBitmap.length] RTInfoImpl = pointerBitmap[];
+ m = i.dup;
+ i = m.dup;
+ i = i.idup;
+ i = m.idup;
+ i = s.idup;
+ i = s.dup;
+ static assert(!__traits(compiles, m = s.dup));
}
-template NoPointersBitmapPayload(size_t N)
+@safe unittest
{
- enum size_t[N] NoPointersBitmapPayload = 0;
-}
+ // Bugzilla 13809
+ static struct S
+ {
+ this(this) {}
+ ~this() {}
+ }
-template RTInfo(T)
-{
- enum pointerBitmap = __traits(getPointerBitmap, T);
- static if (pointerBitmap[1 .. $] == NoPointersBitmapPayload!(pointerBitmap.length - 1))
- enum RTInfo = rtinfoNoPointers;
- else
- enum RTInfo = RTInfoImpl!(pointerBitmap).ptr;
+ S[] arr;
+ auto a = arr.dup;
}
-/**
-* shortcuts for the precise GC, also generated by the compiler
-* used instead of the actual pointer bitmap
-*/
-enum immutable(void)* rtinfoNoPointers = null;
-enum immutable(void)* rtinfoHasPointers = cast(void*)1;
-
-// lhs == rhs lowers to __equals(lhs, rhs) for dynamic arrays
-bool __equals(T1, T2)(T1[] lhs, T2[] rhs)
+@system unittest
{
- import core.internal.traits : Unqual;
- alias U1 = Unqual!T1;
- alias U2 = Unqual!T2;
-
- static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; }
- static @trusted R trustedCast(R, S)(S[] r) { return cast(R) r; }
-
- if (lhs.length != rhs.length)
- return false;
-
- if (lhs.length == 0 && rhs.length == 0)
- return true;
-
- static if (is(U1 == void) && is(U2 == void))
- {
- return __equals(trustedCast!(ubyte[])(lhs), trustedCast!(ubyte[])(rhs));
- }
- else static if (is(U1 == void))
- {
- return __equals(trustedCast!(ubyte[])(lhs), rhs);
- }
- else static if (is(U2 == void))
- {
- return __equals(lhs, trustedCast!(ubyte[])(rhs));
- }
- else static if (!is(U1 == U2))
+ // Bugzilla 16504
+ static struct S
{
- // This should replace src/object.d _ArrayEq which
- // compares arrays of different types such as long & int,
- // char & wchar.
- // Compiler lowers to __ArrayEq in dmd/src/opover.d
- foreach (const u; 0 .. lhs.length)
- {
- if (at(lhs, u) != at(rhs, u))
- return false;
- }
- return true;
+ __gshared int* gp;
+ int* p;
+ // postblit and hence .dup could escape
+ this(this) { gp = p; }
}
- else static if (__traits(isIntegral, U1))
+
+ int p;
+ scope S[1] arr = [S(&p)];
+ auto a = arr.dup; // dup does escape
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21983
+// dup/idup destroys partially constructed arrays on failure
+@safe unittest
+{
+ static struct SImpl(bool postblit)
{
+ int num;
+ long l = 0xDEADBEEF;
- if (!__ctfe)
+ static if (postblit)
{
- import core.stdc.string : memcmp;
- return () @trusted { return memcmp(cast(void*)lhs.ptr, cast(void*)rhs.ptr, lhs.length * U1.sizeof) == 0; }();
+ this(this)
+ {
+ if (this.num == 3)
+ throw new Exception("");
+ }
}
else
{
- foreach (const u; 0 .. lhs.length)
+ this(scope ref const SImpl other)
{
- if (at(lhs, u) != at(rhs, u))
- return false;
+ if (other.num == 3)
+ throw new Exception("");
+
+ this.num = other.num;
+ this.l = other.l;
}
- return true;
}
- }
- else
- {
- foreach (const u; 0 .. lhs.length)
+
+ ~this() @trusted
{
- static if (__traits(compiles, __equals(at(lhs, u), at(rhs, u))))
- {
- if (!__equals(at(lhs, u), at(rhs, u)))
- return false;
- }
- else static if (__traits(isFloating, U1))
- {
- if (at(lhs, u) != at(rhs, u))
- return false;
- }
- else static if (is(U1 : Object) && is(U2 : Object))
+ if (l != 0xDEADBEEF)
{
- if (!(cast(Object)at(lhs, u) is cast(Object)at(rhs, u)
- || at(lhs, u) && (cast(Object)at(lhs, u)).opEquals(cast(Object)at(rhs, u))))
- return false;
- }
- else static if (__traits(hasMember, U1, "opEquals"))
- {
- if (!at(lhs, u).opEquals(at(rhs, u)))
- return false;
- }
- else static if (is(U1 == delegate))
- {
- if (at(lhs, u) != at(rhs, u))
- return false;
- }
- else static if (is(U1 == U11*, U11))
- {
- if (at(lhs, u) != at(rhs, u))
- return false;
- }
- else
- {
- if (at(lhs, u).tupleof != at(rhs, u).tupleof)
- return false;
+ import core.stdc.stdio;
+ printf("Unexpected value: %lld\n", l);
+ fflush(stdout);
+ assert(false);
}
}
-
- return true;
}
-}
-unittest {
- assert(__equals([], []));
- assert(!__equals([1, 2], [1, 2, 3]));
-}
+ alias Postblit = SImpl!true;
+ alias Copy = SImpl!false;
-unittest
-{
- struct A
+ static int test(S)()
{
- int a;
+ S[4] arr = [ S(1), S(2), S(3), S(4) ];
+ try
+ {
+ arr.dup();
+ assert(false);
+ }
+ catch (Exception)
+ {
+ return 1;
+ }
}
- auto arr1 = [A(0), A(2)];
- auto arr2 = [A(0), A(1)];
- auto arr3 = [A(0), A(1)];
+ static assert(test!Postblit());
+ assert(test!Postblit());
- assert(arr1 != arr2);
- assert(arr2 == arr3);
+ static assert(test!Copy());
+ assert(test!Copy());
}
-unittest
+/**
+Destroys the given object and optionally resets to initial state. It's used to
+_destroy an object, calling its destructor or finalizer so it no longer
+references any other objects. It does $(I not) initiate a GC cycle or free
+any GC memory.
+If `initialize` is supplied `false`, the object is considered invalid after
+destruction, and should not be referenced.
+*/
+void destroy(bool initialize = true, T)(ref T obj) if (is(T == struct))
{
- struct A
- {
- int a;
- int b;
-
- bool opEquals(const A other)
- {
- return this.a == other.b && this.b == other.a;
- }
- }
+ import core.internal.destruction : destructRecurse;
- auto arr1 = [A(1, 0), A(0, 1)];
- auto arr2 = [A(1, 0), A(0, 1)];
- auto arr3 = [A(0, 1), A(1, 0)];
+ destructRecurse(obj);
- assert(arr1 != arr2);
- assert(arr2 == arr3);
+ static if (initialize)
+ {
+ import core.internal.lifetime : emplaceInitializer;
+ emplaceInitializer(obj); // emplace T.init
+ }
}
-// Compare class and interface objects for ordering.
-private int __cmp(Obj)(Obj lhs, Obj rhs)
-if (is(Obj : Object))
+@safe unittest
{
- if (lhs is rhs)
- return 0;
- // Regard null references as always being "less than"
- if (!lhs)
- return -1;
- if (!rhs)
- return 1;
- return lhs.opCmp(rhs);
+ struct A { string s = "A"; }
+ A a = {s: "B"};
+ assert(a.s == "B");
+ a.destroy;
+ assert(a.s == "A");
}
-int __cmp(T)(const T[] lhs, const T[] rhs) @trusted
-if (__traits(isScalar, T))
-{
- // Compute U as the implementation type for T
- static if (is(T == ubyte) || is(T == void) || is(T == bool))
- alias U = char;
- else static if (is(T == wchar))
- alias U = ushort;
- else static if (is(T == dchar))
- alias U = uint;
- else static if (is(T == ifloat))
- alias U = float;
- else static if (is(T == idouble))
- alias U = double;
- else static if (is(T == ireal))
- alias U = real;
- else
- alias U = T;
-
- static if (is(U == char))
- {
- import core.internal.string : dstrcmp;
- return dstrcmp(cast(char[]) lhs, cast(char[]) rhs);
- }
- else static if (!is(U == T))
+nothrow @safe @nogc unittest
+{
{
- // Reuse another implementation
- return __cmp(cast(U[]) lhs, cast(U[]) rhs);
+ struct A { string s = "A"; }
+ A a;
+ a.s = "asd";
+ destroy!false(a);
+ assert(a.s == "asd");
+ destroy(a);
+ assert(a.s == "A");
}
- else
{
- immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length;
- foreach (const u; 0 .. len)
+ static int destroyed = 0;
+ struct C
{
- static if (__traits(isFloating, T))
+ string s = "C";
+ ~this() nothrow @safe @nogc
{
- immutable a = lhs.ptr[u], b = rhs.ptr[u];
- static if (is(T == cfloat) || is(T == cdouble)
- || is(T == creal))
- {
- // Use rt.cmath2._Ccmp instead ?
- auto r = (a.re > b.re) - (a.re < b.re);
- if (!r) r = (a.im > b.im) - (a.im < b.im);
- }
- else
- {
- const r = (a > b) - (a < b);
- }
- if (r) return r;
+ destroyed ++;
}
- else if (lhs.ptr[u] != rhs.ptr[u])
- return lhs.ptr[u] < rhs.ptr[u] ? -1 : 1;
}
- return lhs.length < rhs.length ? -1 : (lhs.length > rhs.length);
- }
-}
-
-// This function is called by the compiler when dealing with array
-// comparisons in the semantic analysis phase of CmpExp. The ordering
-// comparison is lowered to a call to this template.
-int __cmp(T1, T2)(T1[] s1, T2[] s2)
-if (!__traits(isScalar, T1) && !__traits(isScalar, T2))
-{
- import core.internal.traits : Unqual;
- alias U1 = Unqual!T1;
- alias U2 = Unqual!T2;
-
- static if (is(U1 == void) && is(U2 == void))
- static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; }
- else
- static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; }
-
- // All unsigned byte-wide types = > dstrcmp
- immutable len = s1.length <= s2.length ? s1.length : s2.length;
- foreach (const u; 0 .. len)
- {
- static if (__traits(compiles, __cmp(at(s1, u), at(s2, u))))
- {
- auto c = __cmp(at(s1, u), at(s2, u));
- if (c != 0)
- return c;
- }
- else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u))))
- {
- auto c = at(s1, u).opCmp(at(s2, u));
- if (c != 0)
- return c;
- }
- else static if (__traits(compiles, at(s1, u) < at(s2, u)))
- {
- if (at(s1, u) != at(s2, u))
- return at(s1, u) < at(s2, u) ? -1 : 1;
- }
- else
+ struct B
{
- // TODO: fix this legacy bad behavior, see
- // https://issues.dlang.org/show_bug.cgi?id=17244
- static assert(is(U1 == U2), "Internal error.");
- import core.stdc.string : memcmp;
- auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))();
- if (c != 0)
- return c;
+ C c;
+ string s = "B";
+ ~this() nothrow @safe @nogc
+ {
+ destroyed ++;
+ }
}
+ B a;
+ a.s = "asd";
+ a.c.s = "jkl";
+ destroy!false(a);
+ assert(destroyed == 2);
+ assert(a.s == "asd");
+ assert(a.c.s == "jkl" );
+ destroy(a);
+ assert(destroyed == 4);
+ assert(a.s == "B");
+ assert(a.c.s == "C" );
}
- return s1.length < s2.length ? -1 : (s1.length > s2.length);
}
-// integral types
-@safe unittest
+private extern (C) void rt_finalize(void *data, bool det=true) nothrow;
+
+/// ditto
+void destroy(bool initialize = true, T)(T obj) if (is(T == class))
{
- void compareMinMax(T)()
+ static if (__traits(getLinkage, T) == "C++")
{
- T[2] a = [T.max, T.max];
- T[2] b = [T.min, T.min];
+ static if (__traits(hasMember, T, "__xdtor"))
+ obj.__xdtor();
- assert(__cmp(a, b) > 0);
- assert(__cmp(b, a) < 0);
+ static if (initialize)
+ {
+ enum classSize = __traits(classInstanceSize, T);
+ (cast(void*)obj)[0 .. classSize] = typeid(T).initializer[];
+ }
}
-
- compareMinMax!int;
- compareMinMax!uint;
- compareMinMax!long;
- compareMinMax!ulong;
- compareMinMax!short;
- compareMinMax!ushort;
- compareMinMax!byte;
- compareMinMax!dchar;
- compareMinMax!wchar;
+ else
+ rt_finalize(cast(void*)obj);
}
-// char types (dstrcmp)
-@safe unittest
+/// ditto
+void destroy(bool initialize = true, T)(T obj) if (is(T == interface))
{
- void compareMinMax(T)()
- {
- T[2] a = [T.max, T.max];
- T[2] b = [T.min, T.min];
-
- assert(__cmp(a, b) > 0);
- assert(__cmp(b, a) < 0);
- }
-
- compareMinMax!ubyte;
- compareMinMax!bool;
- compareMinMax!char;
- compareMinMax!(const char);
+ static assert(__traits(getLinkage, T) == "D", "Invalid call to destroy() on extern(" ~ __traits(getLinkage, T) ~ ") interface");
- string s1 = "aaaa";
- string s2 = "bbbb";
- assert(__cmp(s2, s1) > 0);
- assert(__cmp(s1, s2) < 0);
+ destroy!initialize(cast(Object)obj);
}
-// fp types
-@safe unittest
+/// Reference type demonstration
+@system unittest
{
- void compareMinMax(T)()
+ class C
{
- T[2] a = [T.max, T.max];
- T[2] b = [T.min_normal, T.min_normal];
- T[2] c = [T.max, T.min_normal];
- T[1] d = [T.max];
+ struct Agg
+ {
+ static int dtorCount;
- assert(__cmp(a, b) > 0);
- assert(__cmp(b, a) < 0);
- assert(__cmp(a, c) > 0);
- assert(__cmp(a, d) > 0);
- assert(__cmp(d, c) < 0);
- assert(__cmp(c, c) == 0);
- }
+ int x = 10;
+ ~this() { dtorCount++; }
+ }
- compareMinMax!real;
- compareMinMax!float;
- compareMinMax!double;
- compareMinMax!ireal;
- compareMinMax!ifloat;
- compareMinMax!idouble;
- compareMinMax!creal;
- //compareMinMax!cfloat;
- compareMinMax!cdouble;
+ static int dtorCount;
- // qualifiers
- compareMinMax!(const real);
- compareMinMax!(immutable real);
-}
+ string s = "S";
+ Agg a;
+ ~this() { dtorCount++; }
+ }
-// void[]
-@safe unittest
-{
- void[] a;
- const(void)[] b;
+ C c = new C();
+ assert(c.dtorCount == 0); // destructor not yet called
+ assert(c.s == "S"); // initial state `c.s` is `"S"`
+ assert(c.a.dtorCount == 0); // destructor not yet called
+ assert(c.a.x == 10); // initial state `c.a.x` is `10`
+ c.s = "T";
+ c.a.x = 30;
+ assert(c.s == "T"); // `c.s` is `"T"`
+ destroy(c);
+ assert(c.dtorCount == 1); // `c`'s destructor was called
+ assert(c.s == "S"); // `c.s` is back to its inital state, `"S"`
+ assert(c.a.dtorCount == 1); // `c.a`'s destructor was called
+ assert(c.a.x == 10); // `c.a.x` is back to its inital state, `10`
- (() @trusted
+ // check C++ classes work too!
+ extern (C++) class CPP
{
- a = cast(void[]) "bb";
- b = cast(const(void)[]) "aa";
- })();
+ struct Agg
+ {
+ __gshared int dtorCount;
- assert(__cmp(a, b) > 0);
- assert(__cmp(b, a) < 0);
-}
+ int x = 10;
+ ~this() { dtorCount++; }
+ }
-// arrays of arrays with mixed modifiers
+ __gshared int dtorCount;
+
+ string s = "S";
+ Agg a;
+ ~this() { dtorCount++; }
+ }
+
+ CPP cpp = new CPP();
+ assert(cpp.dtorCount == 0); // destructor not yet called
+ assert(cpp.s == "S"); // initial state `cpp.s` is `"S"`
+ assert(cpp.a.dtorCount == 0); // destructor not yet called
+ assert(cpp.a.x == 10); // initial state `cpp.a.x` is `10`
+ cpp.s = "T";
+ cpp.a.x = 30;
+ assert(cpp.s == "T"); // `cpp.s` is `"T"`
+ destroy!false(cpp); // destroy without initialization
+ assert(cpp.dtorCount == 1); // `cpp`'s destructor was called
+ assert(cpp.s == "T"); // `cpp.s` is not initialized
+ assert(cpp.a.dtorCount == 1); // `cpp.a`'s destructor was called
+ assert(cpp.a.x == 30); // `cpp.a.x` is not initialized
+ destroy(cpp);
+ assert(cpp.dtorCount == 2); // `cpp`'s destructor was called again
+ assert(cpp.s == "S"); // `cpp.s` is back to its inital state, `"S"`
+ assert(cpp.a.dtorCount == 2); // `cpp.a`'s destructor was called again
+ assert(cpp.a.x == 10); // `cpp.a.x` is back to its inital state, `10`
+}
+
+/// Value type demonstration
@safe unittest
{
- // https://issues.dlang.org/show_bug.cgi?id=17876
- bool less1(immutable size_t[][] a, size_t[][] b) { return a < b; }
- bool less2(const void[][] a, void[][] b) { return a < b; }
- bool less3(inout size_t[][] a, size_t[][] b) { return a < b; }
-
- immutable size_t[][] a = [[1, 2], [3, 4]];
- size_t[][] b = [[1, 2], [3, 5]];
- assert(less1(a, b));
- assert(less3(a, b));
-
- auto va = [cast(immutable void[])a[0], a[1]];
- auto vb = [cast(void[])b[0], b[1]];
- assert(less2(va, vb));
+ int i;
+ assert(i == 0); // `i`'s initial state is `0`
+ i = 1;
+ assert(i == 1); // `i` changed to `1`
+ destroy!false(i);
+ assert(i == 1); // `i` was not initialized
+ destroy(i);
+ assert(i == 0); // `i` is back to its initial state `0`
}
-// objects
-@safe unittest
+@system unittest
{
- class C
+ extern(C++)
+ static class C
{
- int i;
- this(int i) { this.i = i; }
-
- override int opCmp(Object c) const @safe
- {
- return i - (cast(C)c).i;
- }
+ void* ptr;
+ this() {}
}
- auto c1 = new C(1);
- auto c2 = new C(2);
- assert(__cmp(c1, null) > 0);
- assert(__cmp(null, c1) < 0);
- assert(__cmp(c1, c1) == 0);
- assert(__cmp(c1, c2) < 0);
- assert(__cmp(c2, c1) > 0);
-
- assert(__cmp([c1, c1][], [c2, c2][]) < 0);
- assert(__cmp([c2, c2], [c1, c1]) > 0);
+ destroy!false(new C());
+ destroy!true(new C());
}
-// structs
-@safe unittest
+@system unittest
{
- struct C
+ // class with an `alias this`
+ class A
{
- ubyte i;
- this(ubyte i) { this.i = i; }
+ static int dtorCount;
+ ~this()
+ {
+ dtorCount++;
+ }
}
- auto c1 = C(1);
- auto c2 = C(2);
+ class B
+ {
+ A a;
+ alias a this;
+ this()
+ {
+ a = new A;
+ }
+ static int dtorCount;
+ ~this()
+ {
+ dtorCount++;
+ }
+ }
+ auto b = new B;
+ assert(A.dtorCount == 0);
+ assert(B.dtorCount == 0);
+ destroy(b);
+ assert(A.dtorCount == 0);
+ assert(B.dtorCount == 1);
- assert(__cmp([c1, c1][], [c2, c2][]) < 0);
- assert(__cmp([c2, c2], [c1, c1]) > 0);
- assert(__cmp([c2, c2], [c2, c1]) > 0);
+ auto a = new A;
+ destroy(a);
+ assert(A.dtorCount == 1);
}
-// Compiler hook into the runtime implementation of array (vector) operations.
-template _arrayOp(Args...)
+@system unittest
{
- import core.internal.arrayop;
- alias _arrayOp = arrayOp!Args;
-}
-
-// Helper functions
+ interface I { }
+ {
+ class A: I { string s = "A"; this() {} }
+ auto a = new A, b = new A;
+ a.s = b.s = "asd";
+ destroy(a);
+ assert(a.s == "A");
-private inout(TypeInfo) getElement(inout TypeInfo value) @trusted pure nothrow
-{
- TypeInfo element = cast() value;
- for (;;)
+ I i = b;
+ destroy(i);
+ assert(b.s == "A");
+ }
{
- if (auto qualified = cast(TypeInfo_Const) element)
- element = qualified.base;
- else if (auto redefined = cast(TypeInfo_Enum) element)
- element = redefined.base;
- else if (auto staticArray = cast(TypeInfo_StaticArray) element)
- element = staticArray.value;
- else if (auto vector = cast(TypeInfo_Vector) element)
- element = vector.base;
- else
- break;
+ static bool destroyed = false;
+ class B: I
+ {
+ string s = "B";
+ this() {}
+ ~this()
+ {
+ destroyed = true;
+ }
+ }
+ auto a = new B, b = new B;
+ a.s = b.s = "asd";
+ destroy(a);
+ assert(destroyed);
+ assert(a.s == "B");
+
+ destroyed = false;
+ I i = b;
+ destroy(i);
+ assert(destroyed);
+ assert(b.s == "B");
+ }
+ // this test is invalid now that the default ctor is not run after clearing
+ version (none)
+ {
+ class C
+ {
+ string s;
+ this()
+ {
+ s = "C";
+ }
+ }
+ auto a = new C;
+ a.s = "asd";
+ destroy(a);
+ assert(a.s == "C");
}
- return cast(inout) element;
}
-private size_t getArrayHash(in TypeInfo element, in void* ptr, in size_t count) @trusted nothrow
+nothrow @safe @nogc unittest
{
- if (!count)
- return 0;
-
- const size_t elementSize = element.tsize;
- if (!elementSize)
- return 0;
-
- static bool hasCustomToHash(in TypeInfo value) @trusted pure nothrow
{
- const element = getElement(value);
-
- if (const struct_ = cast(const TypeInfo_Struct) element)
- return !!struct_.xtoHash;
-
- return cast(const TypeInfo_Array) element
- || cast(const TypeInfo_AssociativeArray) element
- || cast(const ClassInfo) element
- || cast(const TypeInfo_Interface) element;
+ struct A { string s = "A"; }
+ A a;
+ a.s = "asd";
+ destroy!false(a);
+ assert(a.s == "asd");
+ destroy(a);
+ assert(a.s == "A");
}
+ {
+ static int destroyed = 0;
+ struct C
+ {
+ string s = "C";
+ ~this() nothrow @safe @nogc
+ {
+ destroyed ++;
+ }
+ }
- import core.internal.traits : externDFunc;
- if (!hasCustomToHash(element))
- return hashOf(ptr[0 .. elementSize * count]);
-
- size_t hash = 0;
- foreach (size_t i; 0 .. count)
- hash = hashOf(element.getHash(ptr + i * elementSize), hash);
- return hash;
+ struct B
+ {
+ C c;
+ string s = "B";
+ ~this() nothrow @safe @nogc
+ {
+ destroyed ++;
+ }
+ }
+ B a;
+ a.s = "asd";
+ a.c.s = "jkl";
+ destroy!false(a);
+ assert(destroyed == 2);
+ assert(a.s == "asd");
+ assert(a.c.s == "jkl" );
+ destroy(a);
+ assert(destroyed == 4);
+ assert(a.s == "B");
+ assert(a.c.s == "C" );
+ }
}
-/// Provide the .dup array property.
-@property auto dup(T)(T[] a)
- if (!is(const(T) : T))
+nothrow unittest
{
- import core.internal.traits : Unconst;
- static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~
- " to "~Unconst!T.stringof~" in dup.");
+ // Bugzilla 20049: Test to ensure proper behavior of `nothrow` destructors
+ class C
+ {
+ static int dtorCount = 0;
+ this() nothrow {}
+ ~this() nothrow { dtorCount++; }
+ }
- // wrap unsafe _dup in @trusted to preserve @safe postblit
- static if (__traits(compiles, (T b) @safe { T a = b; }))
- return _trustedDup!(T, Unconst!T)(a);
- else
- return _dup!(T, Unconst!T)(a);
+ auto c = new C;
+ destroy(c);
+ assert(C.dtorCount == 1);
}
/// ditto
-// const overload to support implicit conversion to immutable (unique result, see DIP29)
-@property T[] dup(T)(const(T)[] a)
- if (is(const(T) : T))
+void destroy(bool initialize = true, T)(ref T obj)
+if (__traits(isStaticArray, T))
{
- // wrap unsafe _dup in @trusted to preserve @safe postblit
- static if (__traits(compiles, (T b) @safe { T a = b; }))
- return _trustedDup!(const(T), T)(a);
- else
- return _dup!(const(T), T)(a);
+ foreach_reverse (ref e; obj[])
+ destroy!initialize(e);
}
-
-/// Provide the .idup array property.
-@property immutable(T)[] idup(T)(T[] a)
+@safe unittest
{
- static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~
- " to immutable in idup.");
-
- // wrap unsafe _dup in @trusted to preserve @safe postblit
- static if (__traits(compiles, (T b) @safe { T a = b; }))
- return _trustedDup!(T, immutable(T))(a);
- else
- return _dup!(T, immutable(T))(a);
+ int[2] a;
+ a[0] = 1;
+ a[1] = 2;
+ destroy!false(a);
+ assert(a == [ 1, 2 ]);
+ destroy(a);
+ assert(a == [ 0, 0 ]);
}
-/// ditto
-@property immutable(T)[] idup(T:void)(const(T)[] a)
+@safe unittest
{
- return a.dup;
-}
+ static struct vec2f {
+ float[2] values;
+ alias values this;
+ }
-private U[] _trustedDup(T, U)(T[] a) @trusted
-{
- return _dup!(T, U)(a);
+ vec2f v;
+ destroy!(true, vec2f)(v);
}
-private U[] _dup(T, U)(T[] a) // pure nothrow depends on postblit
+@system unittest
{
- if (__ctfe)
+ // Bugzilla 15009
+ static string op;
+ static struct S
{
- static if (is(T : void))
- assert(0, "Cannot dup a void[] array at compile time.");
- else
- {
- U[] res;
- foreach (ref e; a)
- res ~= e;
- return res;
- }
+ int x;
+ this(int x) { op ~= "C" ~ cast(char)('0'+x); this.x = x; }
+ this(this) { op ~= "P" ~ cast(char)('0'+x); }
+ ~this() { op ~= "D" ~ cast(char)('0'+x); }
}
- import core.stdc.string : memcpy;
-
- void[] arr = _d_newarrayU(typeid(T[]), a.length);
- memcpy(arr.ptr, cast(const(void)*)a.ptr, T.sizeof * a.length);
- auto res = *cast(U[]*)&arr;
-
- static if (!is(T : void))
- _doPostblit(res);
- return res;
-}
-
-private extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow;
-
-
-/**************
- * Get the postblit for type T.
- * Returns:
- * null if no postblit is necessary
- * function pointer for struct postblits
- * delegate for class postblits
- */
-private auto _getPostblit(T)() @trusted pure nothrow @nogc
-{
- // infer static postblit type, run postblit if any
- static if (is(T == struct))
{
- import core.internal.traits : Unqual;
- // use typeid(Unqual!T) here to skip TypeInfo_Const/Shared/...
- alias _PostBlitType = typeof(function (ref T t){ T a = t; });
- return cast(_PostBlitType)typeid(Unqual!T).xpostblit;
+ S[2] a1 = [S(1), S(2)];
+ op = "";
}
- else if ((&typeid(T).postblit).funcptr !is &TypeInfo.postblit)
+ assert(op == "D2D1"); // built-in scope destruction
{
- alias _PostBlitType = typeof(delegate (ref T t){ T a = t; });
- return cast(_PostBlitType)&typeid(T).postblit;
+ S[2] a1 = [S(1), S(2)];
+ op = "";
+ destroy(a1);
+ assert(op == "D2D1"); // consistent with built-in behavior
}
- else
- return null;
-}
-private void _doPostblit(T)(T[] arr)
-{
- // infer static postblit type, run postblit if any
- if (auto postblit = _getPostblit!T())
{
- foreach (ref elem; arr)
- postblit(elem);
+ S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]];
+ op = "";
+ }
+ assert(op == "D4D3D2D1");
+ {
+ S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]];
+ op = "";
+ destroy(a2);
+ assert(op == "D4D3D2D1", op);
}
}
-unittest
+// https://issues.dlang.org/show_bug.cgi?id=19218
+@system unittest
{
- static struct S1 { int* p; }
- static struct S2 { @disable this(); }
- static struct S3 { @disable this(this); }
-
- int dg1() pure nothrow @safe
+ static struct S
{
- {
- char[] m;
- string i;
- m = m.dup;
- i = i.idup;
- m = i.dup;
- i = m.idup;
- }
- {
- S1[] m;
- immutable(S1)[] i;
- m = m.dup;
- i = i.idup;
- static assert(!is(typeof(m.idup)));
- static assert(!is(typeof(i.dup)));
- }
- {
- S3[] m;
- immutable(S3)[] i;
- static assert(!is(typeof(m.dup)));
- static assert(!is(typeof(i.idup)));
- }
- {
- shared(S1)[] m;
- m = m.dup;
- static assert(!is(typeof(m.idup)));
- }
- {
- int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0);
- }
- return 1;
+ static dtorCount = 0;
+ ~this() { ++dtorCount; }
}
- int dg2() pure nothrow @safe
+ static interface I
{
- {
- S2[] m = [S2.init, S2.init];
- immutable(S2)[] i = [S2.init, S2.init];
- m = m.dup;
- m = i.dup;
- i = m.idup;
- i = i.idup;
- }
- return 2;
+ ref S[3] getArray();
+ alias getArray this;
}
- enum a = dg1();
- enum b = dg2();
- assert(dg1() == a);
- assert(dg2() == b);
-}
+ static class C : I
+ {
+ static dtorCount = 0;
+ ~this() { ++dtorCount; }
-unittest
-{
- static struct Sunpure { this(this) @safe nothrow {} }
- static struct Sthrow { this(this) @safe pure {} }
- static struct Sunsafe { this(this) @system pure nothrow {} }
+ S[3] a;
+ alias a this;
- static assert( __traits(compiles, () { [].dup!Sunpure; }));
- static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
- static assert( __traits(compiles, () { [].dup!Sthrow; }));
- static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
- static assert( __traits(compiles, () { [].dup!Sunsafe; }));
- static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
+ ref S[3] getArray() { return a; }
+ }
- static assert( __traits(compiles, () { [].idup!Sunpure; }));
- static assert(!__traits(compiles, () pure { [].idup!Sunpure; }));
- static assert( __traits(compiles, () { [].idup!Sthrow; }));
- static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; }));
- static assert( __traits(compiles, () { [].idup!Sunsafe; }));
- static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; }));
+ C c = new C();
+ destroy(c);
+ assert(S.dtorCount == 3);
+ assert(C.dtorCount == 1);
+
+ I i = new C();
+ destroy(i);
+ assert(S.dtorCount == 6);
+ assert(C.dtorCount == 2);
}
-unittest
+/// ditto
+void destroy(bool initialize = true, T)(ref T obj)
+ if (!is(T == struct) && !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T))
{
- static int*[] pureFoo() pure { return null; }
- { char[] s; immutable x = s.dup; }
- { immutable x = (cast(int*[])null).dup; }
- { immutable x = pureFoo(); }
- { immutable x = pureFoo().dup; }
+ static if (initialize)
+ obj = T.init;
}
-unittest
+@safe unittest
{
- auto a = [1, 2, 3];
- auto b = a.dup;
- debug(SENTINEL) {} else
- assert(b.capacity >= 3);
+ {
+ int a = 42;
+ destroy!false(a);
+ assert(a == 42);
+ destroy(a);
+ assert(a == 0);
+ }
+ {
+ float a = 42;
+ destroy!false(a);
+ assert(a == 42);
+ destroy(a);
+ assert(a != a); // isnan
+ }
}
-unittest
+@safe unittest
{
- // Bugzilla 12580
- void[] m = [0];
- shared(void)[] s = [cast(shared)1];
- immutable(void)[] i = [cast(immutable)2];
+ // Bugzilla 14746
+ static struct HasDtor
+ {
+ ~this() { assert(0); }
+ }
+ static struct Owner
+ {
+ HasDtor* ptr;
+ alias ptr this;
+ }
- s = s.dup;
- static assert(is(typeof(s.dup) == shared(void)[]));
+ Owner o;
+ assert(o.ptr is null);
+ destroy(o); // must not reach in HasDtor.__dtor()
+}
- m = i.dup;
- i = m.dup;
- i = i.idup;
- i = m.idup;
- i = s.idup;
- i = s.dup;
- static assert(!__traits(compiles, m = s.dup));
+/* ************************************************************************
+ COMPILER SUPPORT
+The compiler lowers certain expressions to instantiations of the following
+templates. They must be implicitly imported, which is why they are here
+in this file. They must also be `public` as they must be visible from the
+scope in which they are instantiated. They are explicitly undocumented as
+they are only intended to be instantiated by the compiler, not the user.
+**************************************************************************/
+
+public import core.internal.entrypoint : _d_cmain;
+
+public import core.internal.array.appending : _d_arrayappendTImpl;
+public import core.internal.array.appending : _d_arrayappendcTXImpl;
+public import core.internal.array.comparison : __cmp;
+public import core.internal.array.equality : __equals;
+public import core.internal.array.casting: __ArrayCast;
+public import core.internal.array.concatenation : _d_arraycatnTXImpl;
+public import core.internal.array.construction : _d_arrayctor;
+public import core.internal.array.construction : _d_arraysetctor;
+public import core.internal.array.capacity: _d_arraysetlengthTImpl;
+
+public import core.internal.dassert: _d_assert_fail;
+
+public import core.internal.destruction: __ArrayDtor;
+
+public import core.internal.moving: __move_post_blt;
+
+public import core.internal.postblit: __ArrayPostblit;
+
+public import core.internal.switch_: __switch;
+public import core.internal.switch_: __switch_error;
+
+public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable);
+
+// Compare class and interface objects for ordering.
+private int __cmp(Obj)(Obj lhs, Obj rhs)
+if (is(Obj : Object))
+{
+ if (lhs is rhs)
+ return 0;
+ // Regard null references as always being "less than"
+ if (!lhs)
+ return -1;
+ if (!rhs)
+ return 1;
+ return lhs.opCmp(rhs);
}
-unittest
+// objects
+@safe unittest
{
- // Bugzilla 13809
- static struct S
+ class C
{
- this(this) {}
- ~this() {}
+ int i;
+ this(int i) { this.i = i; }
+
+ override int opCmp(Object c) const @safe
+ {
+ return i - (cast(C)c).i;
+ }
}
- S[] arr;
- auto a = arr.dup;
+ auto c1 = new C(1);
+ auto c2 = new C(2);
+ assert(__cmp(c1, null) > 0);
+ assert(__cmp(null, c1) < 0);
+ assert(__cmp(c1, c1) == 0);
+ assert(__cmp(c1, c2) < 0);
+ assert(__cmp(c2, c1) > 0);
+
+ assert(__cmp([c1, c1][], [c2, c2][]) < 0);
+ assert(__cmp([c2, c2], [c1, c1]) > 0);
}
-unittest
+// structs
+@safe unittest
{
- // Bugzilla 16504
- static struct S
+ struct C
{
- __gshared int* gp;
- int* p;
- // postblit and hence .dup could escape
- this(this) { gp = p; }
+ ubyte i;
+ this(ubyte i) { this.i = i; }
}
- int p;
- scope arr = [S(&p)];
- auto a = arr.dup; // dup does escape
+ auto c1 = C(1);
+ auto c2 = C(2);
+
+ assert(__cmp([c1, c1][], [c2, c2][]) < 0);
+ assert(__cmp([c2, c2], [c1, c1]) > 0);
+ assert(__cmp([c2, c2], [c2, c1]) > 0);
}
-// compiler frontend lowers dynamic array comparison to this
-bool __ArrayEq(T1, T2)(T1[] a, T2[] b)
+@safe unittest
{
- if (a.length != b.length)
- return false;
- foreach (size_t i; 0 .. a.length)
- {
- if (a[i] != b[i])
- return false;
- }
- return true;
+ auto a = "hello"c;
+
+ assert(a > "hel");
+ assert(a >= "hel");
+ assert(a < "helloo");
+ assert(a <= "helloo");
+ assert(a > "betty");
+ assert(a >= "betty");
+ assert(a == "hello");
+ assert(a <= "hello");
+ assert(a >= "hello");
+ assert(a < "Ñ");
}
-// compiler frontend lowers struct array postblitting to this
-void __ArrayPostblit(T)(T[] a)
+// Used in Exception Handling LSDA tables to 'wrap' C++ type info
+// so it can be distinguished from D TypeInfo
+class __cpp_type_info_ptr
{
- foreach (ref T e; a)
- e.__xpostblit();
+ void* ptr; // opaque pointer to C++ RTTI type info
}
-// compiler frontend lowers dynamic array deconstruction to this
-void __ArrayDtor(T)(T[] a)
+// Compiler hook into the runtime implementation of array (vector) operations.
+template _arrayOp(Args...)
{
- foreach_reverse (ref T e; a)
- e.__xdtor();
+ import core.internal.array.operations;
+ alias _arrayOp = arrayOp!Args;
}
+
+public import core.builtins : __ctfeWrite;
diff --git a/libphobos/libdruntime/rt/aApply.d b/libphobos/libdruntime/rt/aApply.d
index f6657025b25..bea441f550a 100644
--- a/libphobos/libdruntime/rt/aApply.d
+++ b/libphobos/libdruntime/rt/aApply.d
@@ -4,13 +4,13 @@
* of those.
*
* Copyright: Copyright Digital Mars 2004 - 2010.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright
- * Source: $(DRUNTIMESRC src/rt/_aApply.d)
+ * Source: $(DRUNTIMESRC rt/_aApply.d)
*/
module rt.aApply;
-private import rt.util.utf : decode, toUTF8;
+import core.internal.utf : decode, toUTF8;
/**********************************************/
/* 1 argument versions */
diff --git a/libphobos/libdruntime/rt/aApplyR.d b/libphobos/libdruntime/rt/aApplyR.d
index b29d3706af7..6db653047d3 100644
--- a/libphobos/libdruntime/rt/aApplyR.d
+++ b/libphobos/libdruntime/rt/aApplyR.d
@@ -4,8 +4,9 @@
* of those.
*
* Copyright: Copyright Digital Mars 2004 - 2010.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright, Sean Kelly
+ * Source: $(DRUNTIMESRC rt/_aApplyR.d)
*/
/* Copyright Digital Mars 2004 - 2010.
@@ -20,7 +21,7 @@ module rt.aApplyR;
* and dchar, and 2 of each of those.
*/
-private import rt.util.utf;
+import core.internal.utf;
/**********************************************/
/* 1 argument versions */
diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d
index 0ccf90204c6..01810536a49 100644
--- a/libphobos/libdruntime/rt/aaA.d
+++ b/libphobos/libdruntime/rt/aaA.d
@@ -2,8 +2,9 @@
* Implementation of associative arrays.
*
* Copyright: Copyright Digital Mars 2000 - 2015.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Martin Nowak
+ * Source: $(DRUNTIMESRC rt/_aaA.d)
*/
module rt.aaA;
@@ -11,6 +12,7 @@ module rt.aaA;
extern (C) immutable int _aaVersion = 1;
import core.memory : GC;
+import core.internal.util.math : min, max;
// grow threshold
private enum GROW_NUM = 4;
@@ -48,13 +50,12 @@ struct AA
private struct Impl
{
private:
- this(in TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS)
+ this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS)
{
keysz = cast(uint) ti.key.tsize;
valsz = cast(uint) ti.value.tsize;
buckets = allocBuckets(sz);
firstUsed = cast(uint) buckets.length;
- entryTI = fakeEntryTI(ti.key, ti.value);
valoff = cast(uint) talign(keysz, ti.value.talign);
import rt.lifetime : hasPostblit, unqualify;
@@ -63,6 +64,8 @@ private:
flags |= Flags.keyHasPostblit;
if ((ti.key.flags | ti.value.flags) & 1)
flags |= Flags.hasPointers;
+
+ entryTI = fakeEntryTI(this, ti.key, ti.value);
}
Bucket[] buckets;
@@ -110,7 +113,7 @@ private:
}
// lookup a key
- inout(Bucket)* findSlotLookup(size_t hash, in void* pkey, in TypeInfo keyti) inout
+ inout(Bucket)* findSlotLookup(size_t hash, scope const void* pkey, scope const TypeInfo keyti) inout
{
for (size_t i = hash & mask, j = 1;; ++j)
{
@@ -122,7 +125,7 @@ private:
}
}
- void grow(in TypeInfo keyti)
+ void grow(scope const TypeInfo keyti)
{
// If there are so many deleted entries, that growing would push us
// below the shrink threshold, we just purge deleted entries instead.
@@ -132,7 +135,7 @@ private:
resize(GROW_FAC * dim);
}
- void shrink(in TypeInfo keyti)
+ void shrink(scope const TypeInfo keyti)
{
if (dim > INIT_NUM_BUCKETS)
resize(dim / GROW_FAC);
@@ -200,7 +203,7 @@ Bucket[] allocBuckets(size_t dim) @trusted pure nothrow
// Entry
//------------------------------------------------------------------------------
-private void* allocEntry(in Impl* aa, in void* pkey)
+private void* allocEntry(scope const Impl* aa, scope const void* pkey)
{
import rt.lifetime : _d_newitemU;
import core.stdc.string : memcpy, memset;
@@ -243,19 +246,45 @@ private bool hasDtor(const TypeInfo ti)
return false;
}
+private immutable(void)* getRTInfo(const TypeInfo ti)
+{
+ // classes are references
+ const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class);
+ return isNoClass ? ti.rtInfo() : rtinfoHasPointers;
+}
+
// build type info for Entry with additional key and value fields
-TypeInfo_Struct fakeEntryTI(const TypeInfo keyti, const TypeInfo valti)
+TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti)
{
import rt.lifetime : unqualify;
auto kti = unqualify(keyti);
auto vti = unqualify(valti);
- if (!hasDtor(kti) && !hasDtor(vti))
+
+ // figure out whether RTInfo has to be generated (indicated by rtisize > 0)
+ enum pointersPerWord = 8 * (void*).sizeof * (void*).sizeof;
+ auto rtinfo = rtinfoNoPointers;
+ size_t rtisize = 0;
+ immutable(size_t)* keyinfo = void;
+ immutable(size_t)* valinfo = void;
+ if (aa.flags & Impl.Flags.hasPointers)
+ {
+ // classes are references
+ keyinfo = cast(immutable(size_t)*) getRTInfo(keyti);
+ valinfo = cast(immutable(size_t)*) getRTInfo(valti);
+
+ if (keyinfo is rtinfoHasPointers && valinfo is rtinfoHasPointers)
+ rtinfo = rtinfoHasPointers;
+ else
+ rtisize = 1 + (aa.valoff + aa.valsz + pointersPerWord - 1) / pointersPerWord;
+ }
+ bool entryHasDtor = hasDtor(kti) || hasDtor(vti);
+ if (rtisize == 0 && !entryHasDtor)
return null;
// save kti and vti after type info for struct
enum sizeti = __traits(classInstanceSize, TypeInfo_Struct);
- void* p = GC.malloc(sizeti + 2 * (void*).sizeof);
+ void* p = GC.malloc(sizeti + (2 + rtisize) * (void*).sizeof);
import core.stdc.string : memcpy;
memcpy(p, typeid(TypeInfo_Struct).initializer().ptr, sizeti);
@@ -265,26 +294,146 @@ TypeInfo_Struct fakeEntryTI(const TypeInfo keyti, const TypeInfo valti)
extra[0] = cast() kti;
extra[1] = cast() vti;
- static immutable tiName = __MODULE__ ~ ".Entry!(...)";
- ti.name = tiName;
+ static immutable tiMangledName = "S2rt3aaA__T5EntryZ";
+ ti.mangledName = tiMangledName;
+
+ ti.m_RTInfo = rtisize > 0 ? rtinfoEntry(aa, keyinfo, valinfo, cast(size_t*)(extra + 2), rtisize) : rtinfo;
+ ti.m_flags = ti.m_RTInfo is rtinfoNoPointers ? cast(TypeInfo_Struct.StructFlags)0 : TypeInfo_Struct.StructFlags.hasPointers;
// we don't expect the Entry objects to be used outside of this module, so we have control
// over the non-usage of the callback methods and other entries and can keep these null
// xtoHash, xopEquals, xopCmp, xtoString and xpostblit
- ti.m_RTInfo = rtinfoNoPointers;
- immutable entrySize = talign(kti.tsize, vti.talign) + vti.tsize;
+ immutable entrySize = aa.valoff + aa.valsz;
ti.m_init = (cast(ubyte*) null)[0 .. entrySize]; // init length, but not ptr
- // xdtor needs to be built from the dtors of key and value for the GC
- ti.xdtorti = &entryDtor;
+ if (entryHasDtor)
+ {
+ // xdtor needs to be built from the dtors of key and value for the GC
+ ti.xdtorti = &entryDtor;
+ ti.m_flags |= TypeInfo_Struct.StructFlags.isDynamicType;
+ }
- ti.m_flags = TypeInfo_Struct.StructFlags.isDynamicType;
- ti.m_flags |= (keyti.flags | valti.flags) & TypeInfo_Struct.StructFlags.hasPointers;
ti.m_align = cast(uint) max(kti.talign, vti.talign);
return ti;
}
+// build appropriate RTInfo at runtime
+immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize)
+{
+ enum bitsPerWord = 8 * size_t.sizeof;
+
+ rtinfoData[0] = aa.valoff + aa.valsz;
+ rtinfoData[1..rtinfoSize] = 0;
+
+ void copyKeyInfo(string src)()
+ {
+ size_t pos = 1;
+ size_t keybits = aa.keysz / (void*).sizeof;
+ while (keybits >= bitsPerWord)
+ {
+ rtinfoData[pos] = mixin(src);
+ keybits -= bitsPerWord;
+ pos++;
+ }
+ if (keybits > 0)
+ rtinfoData[pos] = mixin(src) & ((cast(size_t) 1 << keybits) - 1);
+ }
+
+ if (keyinfo is rtinfoHasPointers)
+ copyKeyInfo!"~cast(size_t) 0"();
+ else if (keyinfo !is rtinfoNoPointers)
+ copyKeyInfo!"keyinfo[pos]"();
+
+ void copyValInfo(string src)()
+ {
+ size_t bitpos = aa.valoff / (void*).sizeof;
+ size_t pos = 1;
+ size_t dstpos = 1 + bitpos / bitsPerWord;
+ size_t begoff = bitpos % bitsPerWord;
+ size_t valbits = aa.valsz / (void*).sizeof;
+ size_t endoff = (bitpos + valbits) % bitsPerWord;
+ for (;;)
+ {
+ const bits = bitsPerWord - begoff;
+ size_t s = mixin(src);
+ rtinfoData[dstpos] |= s << begoff;
+ if (begoff > 0 && valbits > bits)
+ rtinfoData[dstpos+1] |= s >> bits;
+ if (valbits < bitsPerWord)
+ break;
+ valbits -= bitsPerWord;
+ dstpos++;
+ pos++;
+ }
+ if (endoff > 0)
+ rtinfoData[dstpos] &= ((cast(size_t) 1 << endoff) - 1);
+ }
+
+ if (valinfo is rtinfoHasPointers)
+ copyValInfo!"~cast(size_t) 0"();
+ else if (valinfo !is rtinfoNoPointers)
+ copyValInfo!"valinfo[pos]"();
+
+ return cast(immutable(void)*) rtinfoData;
+}
+
+unittest
+{
+ void test(K, V)()
+ {
+ static struct Entry
+ {
+ K key;
+ V val;
+ }
+ auto keyti = typeid(K);
+ auto valti = typeid(V);
+ auto valrti = getRTInfo(valti);
+ auto keyrti = getRTInfo(keyti);
+
+ auto impl = new Impl(typeid(V[K]));
+ if (valrti is rtinfoNoPointers && keyrti is rtinfoNoPointers)
+ {
+ assert(!(impl.flags & Impl.Flags.hasPointers));
+ assert(impl.entryTI is null);
+ }
+ else if (valrti is rtinfoHasPointers && keyrti is rtinfoHasPointers)
+ {
+ assert(impl.flags & Impl.Flags.hasPointers);
+ assert(impl.entryTI is null);
+ }
+ else
+ {
+ auto rtInfo = cast(size_t*) impl.entryTI.rtInfo();
+ auto refInfo = cast(size_t*) typeid(Entry).rtInfo();
+ assert(rtInfo[0] == refInfo[0]); // size
+ enum bytesPerWord = 8 * size_t.sizeof * (void*).sizeof;
+ size_t words = (rtInfo[0] + bytesPerWord - 1) / bytesPerWord;
+ foreach (i; 0 .. words)
+ assert(rtInfo[1 + i] == refInfo[i + 1]);
+ }
+ }
+ test!(long, int)();
+ test!(string, string);
+ test!(ubyte[16], Object);
+
+ static struct Small
+ {
+ ubyte[16] guid;
+ string name;
+ }
+ test!(string, Small);
+
+ static struct Large
+ {
+ ubyte[1024] data;
+ string[412] names;
+ ubyte[1024] moredata;
+ }
+ test!(Large, Large);
+}
+
//==============================================================================
// Helper functions
//------------------------------------------------------------------------------
@@ -307,14 +456,14 @@ private size_t mix(size_t h) @safe pure nothrow @nogc
return h;
}
-private size_t calcHash(in void* pkey, in TypeInfo keyti)
+private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti)
{
immutable hash = keyti.getHash(pkey);
// highest bit is set to distinguish empty/deleted from filled buckets
return mix(hash) | HASH_FILLED_MARK;
}
-private size_t nextpow2(in size_t n) pure nothrow @nogc
+private size_t nextpow2(const size_t n) pure nothrow @nogc
{
import core.bitop : bsr;
@@ -332,22 +481,12 @@ pure nothrow @nogc unittest
assert(nextpow2(n) == pow2);
}
-private T min(T)(T a, T b) pure nothrow @nogc
-{
- return a < b ? a : b;
-}
-
-private T max(T)(T a, T b) pure nothrow @nogc
-{
- return b < a ? a : b;
-}
-
//==============================================================================
// API Implementation
//------------------------------------------------------------------------------
/// Determine number of entries in associative array.
-extern (C) size_t _aaLen(in AA aa) pure nothrow @nogc
+extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc
{
return aa ? aa.length : 0;
}
@@ -356,7 +495,7 @@ extern (C) size_t _aaLen(in AA aa) pure nothrow @nogc
* Lookup *pkey in aa.
* Called only from implementation of (aa[key]) expressions when value is mutable.
* Params:
- * aa = associative array opaque pointer
+ * paa = associative array opaque pointer
* ti = TypeInfo for the associative array
* valsz = ignored
* pkey = pointer to the key value
@@ -365,18 +504,18 @@ extern (C) size_t _aaLen(in AA aa) pure nothrow @nogc
* If key was not in the aa, a mutable pointer to newly inserted value which
* is set to all zeros
*/
-extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti,
- in size_t valsz, in void* pkey)
+extern (C) void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti,
+ const size_t valsz, scope const void* pkey)
{
bool found;
- return _aaGetX(aa, ti, valsz, pkey, found);
+ return _aaGetX(paa, ti, valsz, pkey, found);
}
/******************************
* Lookup *pkey in aa.
* Called only from implementation of require
* Params:
- * aa = associative array opaque pointer
+ * paa = associative array opaque pointer
* ti = TypeInfo for the associative array
* valsz = ignored
* pkey = pointer to the key value
@@ -386,12 +525,16 @@ extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti,
* If key was not in the aa, a mutable pointer to newly inserted value which
* is set to all zeros
*/
-extern (C) void* _aaGetX(AA* aa, const TypeInfo_AssociativeArray ti,
- in size_t valsz, in void* pkey, out bool found)
+extern (C) void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti,
+ const size_t valsz, scope const void* pkey, out bool found)
{
// lazily alloc implementation
- if (aa.impl is null)
- aa.impl = new Impl(ti);
+ AA aa = *paa;
+ if (aa is null)
+ {
+ aa = new Impl(ti);
+ *paa = aa;
+ }
// get hash and bucket for key
immutable hash = calcHash(pkey, ti.key);
@@ -417,7 +560,7 @@ extern (C) void* _aaGetX(AA* aa, const TypeInfo_AssociativeArray ti,
// update search cache and allocate entry
aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
p.hash = hash;
- p.entry = allocEntry(aa.impl, pkey);
+ p.entry = allocEntry(aa, pkey);
// postblit for key
if (aa.flags & Impl.Flags.keyHasPostblit)
{
@@ -440,8 +583,8 @@ extern (C) void* _aaGetX(AA* aa, const TypeInfo_AssociativeArray ti,
* Returns:
* pointer to value if present, null otherwise
*/
-extern (C) inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz,
- in void* pkey)
+extern (C) inout(void)* _aaGetRvalueX(inout AA aa, scope const TypeInfo keyti, const size_t valsz,
+ scope const void* pkey)
{
return _aaInX(aa, keyti, pkey);
}
@@ -456,7 +599,7 @@ extern (C) inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t
* Returns:
* pointer to value if present, null otherwise
*/
-extern (C) inout(void)* _aaInX(inout AA aa, in TypeInfo keyti, in void* pkey)
+extern (C) inout(void)* _aaInX(inout AA aa, scope const TypeInfo keyti, scope const void* pkey)
{
if (aa.empty)
return null;
@@ -467,8 +610,8 @@ extern (C) inout(void)* _aaInX(inout AA aa, in TypeInfo keyti, in void* pkey)
return null;
}
-/// Delete entry in AA, return true if it was present
-extern (C) bool _aaDelX(AA aa, in TypeInfo keyti, in void* pkey)
+/// Delete entry scope const AA, return true if it was present
+extern (C) bool _aaDelX(AA aa, scope const TypeInfo keyti, scope const void* pkey)
{
if (aa.empty)
return false;
@@ -481,7 +624,9 @@ extern (C) bool _aaDelX(AA aa, in TypeInfo keyti, in void* pkey)
p.entry = null;
++aa.deleted;
- if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM)
+ // `shrink` reallocates, and allocating from a finalizer leads to
+ // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442
+ if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM && !GC.inFinalizer())
aa.shrink(keyti);
return true;
@@ -494,20 +639,21 @@ extern (C) void _aaClear(AA aa) pure nothrow
{
if (!aa.empty)
{
- aa.impl.clear();
+ aa.clear();
}
}
/// Rehash AA
-extern (C) void* _aaRehash(AA* paa, in TypeInfo keyti) pure nothrow
+extern (C) void* _aaRehash(AA* paa, scope const TypeInfo keyti) pure nothrow
{
- if (!paa.empty)
- paa.resize(nextpow2(INIT_DEN * paa.length / INIT_NUM));
- return *paa;
+ AA aa = *paa;
+ if (!aa.empty)
+ aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM));
+ return aa;
}
/// Return a GC allocated array of all values
-extern (C) inout(void[]) _aaValues(inout AA aa, in size_t keysz, in size_t valsz,
+extern (C) inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz,
const TypeInfo tiValueArray) pure nothrow
{
if (aa.empty)
@@ -531,7 +677,7 @@ extern (C) inout(void[]) _aaValues(inout AA aa, in size_t keysz, in size_t valsz
}
/// Return a GC allocated array of all keys
-extern (C) inout(void[]) _aaKeys(inout AA aa, in size_t keysz, const TypeInfo tiKeyArray) pure nothrow
+extern (C) inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow
{
if (aa.empty)
return null;
@@ -557,7 +703,7 @@ extern (D) alias dg_t = int delegate(void*);
extern (D) alias dg2_t = int delegate(void*, void*);
/// foreach opApply over all values
-extern (C) int _aaApply(AA aa, in size_t keysz, dg_t dg)
+extern (C) int _aaApply(AA aa, const size_t keysz, dg_t dg)
{
if (aa.empty)
return 0;
@@ -574,7 +720,7 @@ extern (C) int _aaApply(AA aa, in size_t keysz, dg_t dg)
}
/// foreach opApply over all key/value pairs
-extern (C) int _aaApply2(AA aa, in size_t keysz, dg2_t dg)
+extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg)
{
if (aa.empty)
return 0;
@@ -639,9 +785,9 @@ extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void
}
/// compares 2 AAs for equality
-extern (C) int _aaEqual(in TypeInfo tiRaw, in AA aa1, in AA aa2)
+extern (C) int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2)
{
- if (aa1.impl is aa2.impl)
+ if (aa1 is aa2)
return true;
immutable len = _aaLen(aa1);
@@ -669,8 +815,10 @@ extern (C) int _aaEqual(in TypeInfo tiRaw, in AA aa1, in AA aa2)
}
/// compute a hash
-extern (C) hash_t _aaGetHash(in AA* aa, in TypeInfo tiRaw) nothrow
+extern (C) hash_t _aaGetHash(scope const AA* paa, scope const TypeInfo tiRaw) nothrow
{
+ const AA aa = *paa;
+
if (aa.empty)
return 0;
@@ -707,7 +855,7 @@ struct Range
extern (C) pure nothrow @nogc @safe
{
- Range _aaRange(AA aa)
+ Range _aaRange(return AA aa)
{
if (!aa)
return Range();
@@ -715,7 +863,7 @@ extern (C) pure nothrow @nogc @safe
foreach (i; aa.firstUsed .. aa.dim)
{
if (aa.buckets[i].filled)
- return Range(aa.impl, i);
+ return Range(aa, i);
}
return Range(aa, aa.dim);
}
@@ -756,7 +904,7 @@ extern (C) pure nothrow @nogc @safe
}
}
-// Most tests are now in in test_aa.d
+// Most tests are now in test_aa.d
// test postblit for AA literals
unittest
diff --git a/libphobos/libdruntime/rt/adi.d b/libphobos/libdruntime/rt/adi.d
index 44f0e159276..ea5a78f9c13 100644
--- a/libphobos/libdruntime/rt/adi.d
+++ b/libphobos/libdruntime/rt/adi.d
@@ -6,7 +6,7 @@
* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
* (See accompanying file LICENSE)
* Authors: Walter Bright
- * Source: $(DRUNTIMESRC src/rt/_adi.d)
+ * Source: $(DRUNTIMESRC rt/_adi.d)
*/
module rt.adi;
@@ -16,66 +16,6 @@ module rt.adi;
private
{
debug(adi) import core.stdc.stdio;
- import core.stdc.string;
- import core.stdc.stdlib;
- import core.memory;
- import rt.util.utf;
-
- extern (C) void[] _adSort(void[] a, TypeInfo ti);
-}
-
-private dchar[] mallocUTF32(C)(in C[] s)
-{
- size_t j = 0;
- auto p = cast(dchar*)malloc(dchar.sizeof * s.length);
- auto r = p[0..s.length]; // r[] will never be longer than s[]
- foreach (dchar c; s)
- r[j++] = c;
- return r[0 .. j];
-}
-
-/**********************************************
- * Sort array of chars.
- */
-
-extern (C) char[] _adSortChar(char[] a)
-{
- if (a.length > 1)
- {
- auto da = mallocUTF32(a);
- _adSort(*cast(void[]*)&da, typeid(da[0]));
- size_t i = 0;
- foreach (dchar d; da)
- { char[4] buf;
- auto t = toUTF8(buf, d);
- a[i .. i + t.length] = t[];
- i += t.length;
- }
- free(da.ptr);
- }
- return a;
-}
-
-/**********************************************
- * Sort array of wchars.
- */
-
-extern (C) wchar[] _adSortWchar(wchar[] a)
-{
- if (a.length > 1)
- {
- auto da = mallocUTF32(a);
- _adSort(*cast(void[]*)&da, typeid(da[0]));
- size_t i = 0;
- foreach (dchar d; da)
- { wchar[2] buf;
- auto t = toUTF16(buf, d);
- a[i .. i + t.length] = t[];
- i += t.length;
- }
- free(da.ptr);
- }
- return a;
}
/***************************************
@@ -85,27 +25,6 @@ extern (C) wchar[] _adSortWchar(wchar[] a)
* 0 not equal
*/
-extern (C) int _adEq(void[] a1, void[] a2, TypeInfo ti)
-{
- debug(adi) printf("_adEq(a1.length = %d, a2.length = %d)\n", a1.length, a2.length);
- if (a1.length != a2.length)
- return 0; // not equal
- auto sz = ti.tsize;
- auto p1 = a1.ptr;
- auto p2 = a2.ptr;
-
- if (sz == 1)
- // We should really have a ti.isPOD() check for this
- return (memcmp(p1, p2, a1.length) == 0);
-
- for (size_t i = 0; i < a1.length; i++)
- {
- if (!ti.equals(p1 + i * sz, p2 + i * sz))
- return 0; // not equal
- }
- return 1; // equal
-}
-
extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti)
{
debug(adi) printf("_adEq2(a1.length = %d, a2.length = %d)\n", a1.length, a2.length);
@@ -115,218 +34,43 @@ extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti)
return 0;
return 1;
}
-unittest
+
+@safe unittest
{
debug(adi) printf("array.Eq unittest\n");
- auto a = "hello"c;
+ struct S(T) { T val; }
+ alias String = S!string;
+ alias Float = S!float;
+
+ String[1] a = [String("hello"c)];
- assert(a != "hel");
- assert(a != "helloo");
- assert(a != "betty");
- assert(a == "hello");
- assert(a != "hxxxx");
+ assert(a != [String("hel")]);
+ assert(a != [String("helloo")]);
+ assert(a != [String("betty")]);
+ assert(a == [String("hello")]);
+ assert(a != [String("hxxxx")]);
- float[] fa = [float.nan];
+ Float[1] fa = [Float(float.nan)];
assert(fa != fa);
}
-/***************************************
- * Support for array compare test.
- */
-
-extern (C) int _adCmp(void[] a1, void[] a2, TypeInfo ti)
+unittest
{
- debug(adi) printf("adCmp()\n");
- auto len = a1.length;
- if (a2.length < len)
- len = a2.length;
- auto sz = ti.tsize;
- void *p1 = a1.ptr;
- void *p2 = a2.ptr;
+ debug(adi) printf("struct.Eq unittest\n");
- if (sz == 1)
- { // We should really have a ti.isPOD() check for this
- auto c = memcmp(p1, p2, len);
- if (c)
- return c;
- }
- else
+ static struct TestStruct
{
- for (size_t i = 0; i < len; i++)
+ int value;
+
+ bool opEquals(const TestStruct rhs) const
{
- auto c = ti.compare(p1 + i * sz, p2 + i * sz);
- if (c)
- return c;
+ return value == rhs.value;
}
}
- if (a1.length == a2.length)
- return 0;
- return (a1.length > a2.length) ? 1 : -1;
-}
-
-extern (C) int _adCmp2(void[] a1, void[] a2, TypeInfo ti)
-{
- debug(adi) printf("_adCmp2(a1.length = %d, a2.length = %d)\n", a1.length, a2.length);
- return ti.compare(&a1, &a2);
-}
-unittest
-{
- debug(adi) printf("array.Cmp unittest\n");
-
- auto a = "hello"c;
-
- assert(a > "hel");
- assert(a >= "hel");
- assert(a < "helloo");
- assert(a <= "helloo");
- assert(a > "betty");
- assert(a >= "betty");
- assert(a == "hello");
- assert(a <= "hello");
- assert(a >= "hello");
- assert(a < "Ñ");
-}
-
-/***************************************
- * Support for array compare test.
- */
-
-extern (C) int _adCmpChar(void[] a1, void[] a2)
-{
- version (D_InlineAsm_X86)
- {
- asm
- { naked ;
-
- push EDI ;
- push ESI ;
-
- mov ESI,a1+4[4+ESP] ;
- mov EDI,a2+4[4+ESP] ;
-
- mov ECX,a1[4+ESP] ;
- mov EDX,a2[4+ESP] ;
-
- cmp ECX,EDX ;
- jb GotLength ;
-
- mov ECX,EDX ;
-
-GotLength:
- cmp ECX,4 ;
- jb DoBytes ;
-
- // Do alignment if neither is dword aligned
- test ESI,3 ;
- jz Aligned ;
-
- test EDI,3 ;
- jz Aligned ;
-DoAlign:
- mov AL,[ESI] ; //align ESI to dword bounds
- mov DL,[EDI] ;
-
- cmp AL,DL ;
- jnz Unequal ;
-
- inc ESI ;
- inc EDI ;
-
- test ESI,3 ;
-
- lea ECX,[ECX-1] ;
- jnz DoAlign ;
-Aligned:
- mov EAX,ECX ;
-
- // do multiple of 4 bytes at a time
-
- shr ECX,2 ;
- jz TryOdd ;
-
- repe ;
- cmpsd ;
-
- jnz UnequalQuad ;
-
-TryOdd:
- mov ECX,EAX ;
-DoBytes:
- // if still equal and not end of string, do up to 3 bytes slightly
- // slower.
-
- and ECX,3 ;
- jz Equal ;
-
- repe ;
- cmpsb ;
-
- jnz Unequal ;
-Equal:
- mov EAX,a1[4+ESP] ;
- mov EDX,a2[4+ESP] ;
-
- sub EAX,EDX ;
- pop ESI ;
-
- pop EDI ;
- ret ;
-
-UnequalQuad:
- mov EDX,[EDI-4] ;
- mov EAX,[ESI-4] ;
-
- cmp AL,DL ;
- jnz Unequal ;
-
- cmp AH,DH ;
- jnz Unequal ;
-
- shr EAX,16 ;
-
- shr EDX,16 ;
-
- cmp AL,DL ;
- jnz Unequal ;
-
- cmp AH,DH ;
-Unequal:
- sbb EAX,EAX ;
- pop ESI ;
-
- or EAX,1 ;
- pop EDI ;
-
- ret ;
- }
- }
- else
- {
- debug(adi) printf("adCmpChar()\n");
- auto len = a1.length;
- if (a2.length < len)
- len = a2.length;
- auto c = memcmp(cast(char *)a1.ptr, cast(char *)a2.ptr, len);
- if (!c)
- c = cast(int)a1.length - cast(int)a2.length;
- return c;
- }
-}
-
-unittest
-{
- debug(adi) printf("array.CmpChar unittest\n");
-
- auto a = "hello"c;
- assert(a > "hel");
- assert(a >= "hel");
- assert(a < "helloo");
- assert(a <= "helloo");
- assert(a > "betty");
- assert(a >= "betty");
- assert(a == "hello");
- assert(a <= "hello");
- assert(a >= "hello");
+ TestStruct[] b = [TestStruct(5)];
+ TestStruct[] c = [TestStruct(6)];
+ assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&c, typeid(TestStruct[])) == false);
+ assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&b, typeid(TestStruct[])) == true);
}
diff --git a/libphobos/libdruntime/rt/arrayassign.d b/libphobos/libdruntime/rt/arrayassign.d
index 389ff92ef78..21d50b0413a 100644
--- a/libphobos/libdruntime/rt/arrayassign.d
+++ b/libphobos/libdruntime/rt/arrayassign.d
@@ -6,14 +6,14 @@
* License: Distributed under the
* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
* Authors: Walter Bright, Kenji Hara
- * Source: $(DRUNTIMESRC src/rt/_arrayassign.d)
+ * Source: $(DRUNTIMESRC rt/_arrayassign.d)
*/
module rt.arrayassign;
private
{
- import rt.util.array;
+ import core.internal.util.array;
import core.stdc.string;
import core.stdc.stdlib;
debug(PRINTF) import core.stdc.stdio;
diff --git a/libphobos/libdruntime/rt/arraycast.d b/libphobos/libdruntime/rt/arraycast.d
deleted file mode 100644
index d16d30d5fa3..00000000000
--- a/libphobos/libdruntime/rt/arraycast.d
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * Implementation of array cast support routines.
- *
- * Copyright: Copyright Digital Mars 2004 - 2016.
- * License: Distributed under the
- * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- * Authors: Walter Bright, Sean Kelly
- * Source: $(DRUNTIMESRC src/rt/_arraycast.d)
- */
-
-module rt.arraycast;
-
-/******************************************
- * Runtime helper to convert dynamic array of one
- * type to dynamic array of another.
- * Adjusts the length of the array.
- * Throws an error if new length is not aligned.
- */
-
-extern (C)
-
-@trusted nothrow
-void[] _d_arraycast(size_t tsize, size_t fsize, void[] a)
-{
- auto length = a.length;
-
- auto nbytes = length * fsize;
- if (nbytes % tsize != 0)
- {
- throw new Error("array cast misalignment");
- }
- length = nbytes / tsize;
- *cast(size_t *)&a = length; // jam new length
- return a;
-}
-
-unittest
-{
- byte[int.sizeof * 3] b;
- int[] i;
- short[] s;
-
- i = cast(int[])b;
- assert(i.length == 3);
-
- s = cast(short[])b;
- assert(s.length == 6);
-
- s = cast(short[])i;
- assert(s.length == 6);
-}
-
diff --git a/libphobos/libdruntime/rt/arraycat.d b/libphobos/libdruntime/rt/arraycat.d
index f3f05c303d7..d8794809af3 100644
--- a/libphobos/libdruntime/rt/arraycat.d
+++ b/libphobos/libdruntime/rt/arraycat.d
@@ -5,7 +5,7 @@
* License: Distributed under the
* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
* Authors: Walter Bright, Sean Kelly
- * Source: $(DRUNTIMESRC src/rt/_arraycat.d)
+ * Source: $(DRUNTIMESRC rt/_arraycat.d)
*/
module rt.arraycat;
@@ -13,7 +13,7 @@ module rt.arraycat;
private
{
import core.stdc.string;
- import rt.util.array;
+ import core.internal.util.array;
debug(PRINTF) import core.stdc.stdio;
}
diff --git a/libphobos/libdruntime/rt/cast_.d b/libphobos/libdruntime/rt/cast_.d
index f34d82eed91..dcb4438c700 100644
--- a/libphobos/libdruntime/rt/cast_.d
+++ b/libphobos/libdruntime/rt/cast_.d
@@ -2,8 +2,9 @@
* Implementation of array assignment support routines.
*
* Copyright: Copyright Digital Mars 2004 - 2010.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright, Sean Kelly
+ * Source: $(DRUNTIMESRC rt/_cast_.d)
*/
/* Copyright Digital Mars 2004 - 2010.
@@ -14,6 +15,19 @@
module rt.cast_;
extern (C):
+@nogc:
+nothrow:
+pure:
+
+// Needed because ClassInfo.opEquals(Object) does a dynamic cast,
+// but we are trying to implement dynamic cast.
+extern (D) private bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) @safe
+{
+ if (a is b)
+ return true;
+ // take care of potential duplicates across binaries
+ return a.name == b.name;
+}
/******************************************
* Given a pointer:
@@ -22,7 +36,7 @@ extern (C):
* If it is null, return null.
* Else, undefined crash
*/
-Object _d_toObject(void* p)
+Object _d_toObject(return void* p)
{
if (!p)
return null;
@@ -74,21 +88,21 @@ void* _d_dynamic_cast(Object o, ClassInfo c)
return res;
}
-int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset)
+int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe
{
- if (oc is c)
+ if (areClassInfosEqual(oc, c))
return true;
do
{
- if (oc.base is c)
+ if (oc.base && areClassInfosEqual(oc.base, c))
return true;
// Bugzilla 2013: Use depth-first search to calculate offset
// from the derived (oc) to the base (c).
foreach (iface; oc.interfaces)
{
- if (iface.classinfo is c || _d_isbaseof2(iface.classinfo, c, offset))
+ if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2(iface.classinfo, c, offset))
{
offset += iface.offset;
return true;
@@ -101,19 +115,19 @@ int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset)
return false;
}
-int _d_isbaseof(ClassInfo oc, ClassInfo c)
+int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe
{
- if (oc is c)
+ if (areClassInfosEqual(oc, c))
return true;
do
{
- if (oc.base is c)
+ if (oc.base && areClassInfosEqual(oc.base, c))
return true;
foreach (iface; oc.interfaces)
{
- if (iface.classinfo is c || _d_isbaseof(iface.classinfo, c))
+ if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c))
return true;
}
@@ -122,20 +136,3 @@ int _d_isbaseof(ClassInfo oc, ClassInfo c)
return false;
}
-
-/*********************************
- * Find the vtbl[] associated with Interface ic.
- */
-void* _d_interface_vtbl(ClassInfo ic, Object o)
-{
- debug(cast_) printf("__d_interface_vtbl(o = %p, ic = %p)\n", o, ic);
-
- assert(o);
-
- foreach (iface; typeid(o).interfaces)
- {
- if (iface.classinfo is ic)
- return cast(void*) iface.vtbl;
- }
- assert(0);
-}
diff --git a/libphobos/libdruntime/rt/config.d b/libphobos/libdruntime/rt/config.d
index 904f7219240..f7682f31b63 100644
--- a/libphobos/libdruntime/rt/config.d
+++ b/libphobos/libdruntime/rt/config.d
@@ -1,68 +1,73 @@
/**
-* Configuration options for druntime
-*
-* Copyright: Copyright Digital Mars 2014.
-* License: Distributed under the
-* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
-* (See accompanying file LICENSE)
-* Authors: Rainer Schuetze
-* Source: $(DRUNTIMESRC src/rt/_config.d)
+Configuration options for druntime.
+
+The default way to configure the runtime is by passing command line arguments
+starting with `--DRT-` and followed by the option name, e.g. `--DRT-gcopt` to
+configure the GC.
+When command line parsing is enabled, command line options starting
+with `--DRT-` are filtered out before calling main, so the program
+will not see them. They are still available via `rt_args()`.
+
+Configuration via the command line can be disabled by declaring a variable for the
+linker to pick up before using it's default from the runtime:
+
+---
+extern(C) __gshared bool rt_cmdline_enabled = false;
+---
+
+Likewise, declare a boolean rt_envvars_enabled to enable configuration via the
+environment variable `DRT_` followed by the option name, e.g. `DRT_GCOPT`:
+
+---
+extern(C) __gshared bool rt_envvars_enabled = true;
+---
+
+Setting default configuration properties in the executable can be done by specifying an
+array of options named `rt_options`:
+
+---
+extern(C) __gshared string[] rt_options = [ "gcopt=precise:1 profile:1"];
+---
+
+Evaluation order of options is `rt_options`, then environment variables, then command
+line arguments, i.e. if command line arguments are not disabled, they can override
+options specified through the environment or embedded in the executable.
+
+Copyright: Copyright Digital Mars 2014.
+License: Distributed under the
+ $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ (See accompanying file LICENSE)
+Authors: Rainer Schuetze
+Source: $(DRUNTIMESRC rt/_config.d)
*/
module rt.config;
-// The default way to configure the runtime is by passing command line arguments
-// starting with "--DRT-" and followed by the option name, e.g. "--DRT-gcopt" to
-// configure the GC.
-// Command line options starting with "--DRT-" are filtered out before calling main,
-// so the program will not see them. They are still available via rt_args().
-//
-// Configuration via the command line can be disabled by declaring a variable for the
-// linker to pick up before using it's default from the runtime:
-//
-// extern(C) __gshared bool rt_cmdline_enabled = false;
-//
-// Likewise, declare a boolean rt_envvars_enabled to enable configuration via the
-// environment variable "DRT_" followed by the option name, e.g. "DRT_GCOPT":
-//
-// extern(C) __gshared bool rt_envvars_enabled = true;
-//
-// Setting default configuration properties in the executable can be done by specifying an
-// array of options named rt_options:
-//
-// extern(C) __gshared string[] rt_options = [ "gcopt=precise:1 profile:1"];
-//
-// Evaluation order of options is rt_options, then environment variables, then command
-// line arguments, i.e. if command line arguments are not disabled, they can override
-// options specified through the environment or embedded in the executable.
-
-import core.demangle : cPrefix;
-
// put each variable in its own COMDAT by making them template instances
template rt_envvars_enabled()
{
- pragma(mangle, cPrefix ~ "rt_envvars_enabled") __gshared bool rt_envvars_enabled = false;
+ extern(C) pragma(mangle, "rt_envvars_enabled") __gshared bool rt_envvars_enabled = false;
}
template rt_cmdline_enabled()
{
- pragma(mangle, cPrefix ~ "rt_cmdline_enabled") __gshared bool rt_cmdline_enabled = true;
+ extern(C) pragma(mangle, "rt_cmdline_enabled") __gshared bool rt_cmdline_enabled = true;
}
template rt_options()
{
- pragma(mangle, cPrefix ~ "rt_options") __gshared string[] rt_options = [];
+ extern(C) pragma(mangle, "rt_options") __gshared string[] rt_options = [];
}
import core.stdc.ctype : toupper;
import core.stdc.stdlib : getenv;
import core.stdc.string : strlen;
-extern extern(C) string[] rt_args() @nogc nothrow;
+extern extern(C) string[] rt_args() @nogc nothrow @system;
alias rt_configCallBack = string delegate(string) @nogc nothrow;
/**
* get a druntime config option using standard configuration options
-* opt name of the option to retreive
+* opt name of the option to retrieve
* dg if non-null, passes the option through this
* delegate and only returns its return value if non-null
* reverse reverse the default processing order cmdline/envvar/rt_options
diff --git a/libphobos/libdruntime/rt/critical_.d b/libphobos/libdruntime/rt/critical_.d
index 9404261131a..ae181228b5c 100644
--- a/libphobos/libdruntime/rt/critical_.d
+++ b/libphobos/libdruntime/rt/critical_.d
@@ -2,8 +2,9 @@
* Implementation of support routines for synchronized blocks.
*
* Copyright: Copyright Digital Mars 2000 - 2011.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright, Sean Kelly
+ * Source: $(DRUNTIMESRC rt/_critical_.d)
*/
/* Copyright Digital Mars 2000 - 2011.
diff --git a/libphobos/libdruntime/rt/deh.d b/libphobos/libdruntime/rt/deh.d
index 440e242c120..695f2ce9dd7 100644
--- a/libphobos/libdruntime/rt/deh.d
+++ b/libphobos/libdruntime/rt/deh.d
@@ -1,12 +1,37 @@
/**
- * Implementation of exception handling support routines.
+ * Entry point for exception handling support routines.
*
- * Copyright: Copyright Digital Mars 1999 - 2013.
+ * There are three style of exception handling being supported by DMD:
+ * DWARF, Win32, and Win64. The Win64 code also supports POSIX.
+ * Support for those scheme is in `rt.dwarfeh`, `rt.deh_win32`, and
+ * `rt.deh_win64_posix`, respectively, and publicly imported here.
+ *
+ * When an exception is thrown by the user, the compiler translates
+ * code like `throw e;` into either `_d_throwdwarf` (for DWARF exceptions)
+ * or `_d_throwc` (Win32 / Win64), with the `Exception` object as argument.
+ *
+ * During those functions' handling, they eventually call `_d_createTrace`,
+ * which will store inside the `Exception` object the return of
+ * `_d_traceContext`, which is an object implementing `Throwable.TraceInfo`.
+ * `_d_traceContext` is a configurable hook, and by default will call
+ * `core.runtime : defaultTraceHandler`, which itself will call `backtrace`
+ * or something similar to store an array of stack frames (`void*` pointers)
+ * in the object it returns.
+ * Note that `defaultTraceHandler` returns a GC-allocated instance,
+ * hence a GC allocation can happen in the middle of throwing an `Exception`.
+ *
+ * The `Throwable.TraceInfo`-implementing should not resolves function names,
+ * file and line number until its `opApply` function is called, avoiding the
+ * overhead of reading the debug infos until the user call `toString`.
+ * If the user only calls `Throwable.message` (or use `Throwable.msg` directly),
+ * only the overhead of `backtrace` will be paid, which is minimal enouh.
+ *
+ * Copyright: Copyright Digital Mars 1999 - 2020.
* License: Distributed under the
* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
* (See accompanying file LICENSE)
* Authors: Walter Bright
- * Source: $(DRUNTIMESRC src/rt/deh.d)
+ * Source: $(DRUNTIMESRC rt/deh.d)
*/
/* NOTE: This file has been patched from the original DMD distribution to
@@ -17,10 +42,8 @@ module rt.deh;
extern (C)
{
Throwable.TraceInfo _d_traceContext(void* ptr = null);
- void _d_createTrace(Object o, void* context)
+ void _d_createTrace(Throwable t, void* context)
{
- auto t = cast(Throwable) o;
-
if (t !is null && t.info is null &&
cast(byte*) t !is typeid(t).initializer.ptr)
{
@@ -39,4 +62,3 @@ else version (Posix)
public import rt.deh_win64_posix;
else
static assert (0, "Unsupported architecture");
-
diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d
index e6acbd5105f..328452e2431 100644
--- a/libphobos/libdruntime/rt/dmain2.d
+++ b/libphobos/libdruntime/rt/dmain2.d
@@ -1,12 +1,12 @@
/**
* Contains druntime startup and shutdown routines.
*
- * Copyright: Copyright Digital Mars 2000 - 2013.
+ * Copyright: Copyright Digital Mars 2000 - 2018.
* License: Distributed under the
* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
* (See accompanying file LICENSE)
* Authors: Walter Bright, Sean Kelly
- * Source: $(DRUNTIMESRC src/rt/_dmain2.d)
+ * Source: $(DRUNTIMESRC rt/_dmain2.d)
*/
/* NOTE: This file has been patched from the original DMD distribution to
@@ -14,22 +14,27 @@
*/
module rt.dmain2;
-private
-{
- import rt.memory;
- import rt.sections;
- import core.atomic;
- import core.stdc.stddef;
- import core.stdc.stdlib;
- import core.stdc.string;
- import core.stdc.stdio; // for printf()
- import core.stdc.errno : errno;
-}
+import rt.memory;
+import rt.sections;
+import core.atomic;
+import core.stdc.stddef;
+import core.stdc.stdlib;
+import core.stdc.string;
+import core.stdc.stdio; // for printf()
+import core.stdc.errno : errno;
version (Windows)
{
- private import core.stdc.wchar_;
- private import core.sys.windows.windows;
+ import core.stdc.wchar_;
+ import core.sys.windows.basetsd : HANDLE;
+ import core.sys.windows.shellapi : CommandLineToArgvW;
+ import core.sys.windows.winbase : FreeLibrary, GetCommandLineW, GetProcAddress,
+ IsDebuggerPresent, LoadLibraryW, LocalFree, WriteFile;
+ import core.sys.windows.wincon : CONSOLE_SCREEN_BUFFER_INFO, GetConsoleOutputCP,
+ GetConsoleScreenBufferInfo;
+ import core.sys.windows.winnls : CP_UTF8, MultiByteToWideChar, WideCharToMultiByte;
+ import core.sys.windows.winnt : WCHAR;
+ import core.sys.windows.winuser : MB_ICONERROR, MessageBoxW;
pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW
}
@@ -47,27 +52,33 @@ version (DragonFlyBSD)
import core.stdc.fenv;
}
+// not sure why we can't define this in one place, but this is to keep this
+// module from importing core.runtime.
+struct UnitTestResult
+{
+ size_t executed;
+ size_t passed;
+ bool runMain;
+ bool summarize;
+}
+
extern (C) void _d_monitor_staticctor();
extern (C) void _d_monitor_staticdtor();
extern (C) void _d_critical_init();
extern (C) void _d_critical_term();
extern (C) void gc_init();
extern (C) void gc_term();
+extern (C) void thread_init() @nogc;
+extern (C) void thread_term() @nogc;
extern (C) void lifetime_init();
extern (C) void rt_moduleCtor();
extern (C) void rt_moduleTlsCtor();
extern (C) void rt_moduleDtor();
extern (C) void rt_moduleTlsDtor();
extern (C) void thread_joinAll();
-extern (C) bool runModuleUnitTests();
+extern (C) UnitTestResult runModuleUnitTests();
extern (C) void _d_initMonoTime();
-version (OSX)
-{
- // The bottom of the stack
- extern (C) __gshared void* __osx_stack_end = cast(void*)0xC0000000;
-}
-
version (CRuntime_Microsoft)
{
extern(C) void init_msvc();
@@ -83,9 +94,6 @@ extern (C) string[] rt_args()
return _d_args;
}
-// make arguments passed to main available for being filtered by runtime initializers
-extern(C) __gshared char[][] _d_main_args = null;
-
// This variable is only ever set by a debugger on initialization so it should
// be fine to leave it as __gshared.
extern (C) __gshared bool rt_trapExceptions = true;
@@ -123,7 +131,8 @@ extern (C) int rt_init()
// this initializes mono time before anything else to allow usage
// in other druntime systems.
_d_initMonoTime();
- gc_init();
+ thread_init();
+ // TODO: fixme - calls GC.addRange -> Initializes GC
initStaticDataGC();
lifetime_init();
rt_moduleCtor();
@@ -132,7 +141,7 @@ extern (C) int rt_init()
}
catch (Throwable t)
{
- _initCount = 0;
+ atomicStore!(MemoryOrder.raw)(_initCount, 0);
_d_print_throwable(t);
}
_d_critical_term();
@@ -145,7 +154,7 @@ extern (C) int rt_init()
*/
extern (C) int rt_term()
{
- if (!_initCount) return 0; // was never initialized
+ if (atomicLoad!(MemoryOrder.raw)(_initCount) == 0) return 0; // was never initialized
if (atomicOp!"-="(_initCount, 1)) return 1;
try
@@ -154,6 +163,7 @@ extern (C) int rt_term()
thread_joinAll();
rt_moduleDtor();
gc_term();
+ thread_term();
return 1;
}
catch (Throwable t)
@@ -234,74 +244,22 @@ extern (C) CArgs rt_cArgs() @nogc
return _cArgs;
}
-/***********************************
- * Run the given main function.
- * Its purpose is to wrap the D main()
- * function and catch any unhandled exceptions.
- */
+/// Type of the D main() function (`_Dmain`).
private alias extern(C) int function(char[][] args) MainFunc;
-extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
+/**
+ * Sets up the D char[][] command-line args, initializes druntime,
+ * runs embedded unittests and then runs the given D main() function,
+ * optionally catching and printing any unhandled exceptions.
+ */
+extern (C) int _d_run_main(int argc, char** argv, MainFunc mainFunc)
{
+ // Set up _cArgs and array of D char[] slices, then forward to _d_run_main2
+
// Remember the original C argc/argv
_cArgs.argc = argc;
_cArgs.argv = argv;
- int result;
-
- version (OSX)
- { /* OSX does not provide a way to get at the top of the
- * stack, except for the magic value 0xC0000000.
- * But as far as the gc is concerned, argv is at the top
- * of the main thread's stack, so save the address of that.
- */
- __osx_stack_end = cast(void*)&argv;
- }
-
- version (FreeBSD) version (D_InlineAsm_X86)
- {
- /*
- * FreeBSD/i386 sets the FPU precision mode to 53 bit double.
- * Make it 64 bit extended.
- */
- ushort fpucw;
- asm
- {
- fstsw fpucw;
- or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision
- // 111111: mask all FP exceptions
- fldcw fpucw;
- }
- }
- version (CRuntime_Microsoft)
- {
- // enable full precision for reals
- version (D_InlineAsm_X86_64)
- {
- asm
- {
- push RAX;
- fstcw word ptr [RSP];
- or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision
- // 111111: mask all FP exceptions
- fldcw word ptr [RSP];
- pop RAX;
- }
- }
- else version (D_InlineAsm_X86)
- {
- asm
- {
- push EAX;
- fstcw word ptr [ESP];
- or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision
- // 111111: mask all FP exceptions
- fldcw word ptr [ESP];
- pop EAX;
- }
- }
- }
-
version (Windows)
{
/* Because we want args[] to be UTF-8, and Windows doesn't guarantee that,
@@ -309,10 +267,10 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
* Then, reparse into wargc/wargs, and then use Windows API to convert
* to UTF-8.
*/
- const wchar_t* wCommandLine = GetCommandLineW();
+ const wCommandLine = GetCommandLineW();
immutable size_t wCommandLineLength = wcslen(wCommandLine);
int wargc;
- wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc);
+ auto wargs = CommandLineToArgvW(wCommandLine, &wargc);
// assert(wargc == argc); /* argc can be broken by Unicode arguments */
// Allocate args[] on the stack - use wargc
@@ -357,6 +315,114 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
else
static assert(0);
+ return _d_run_main2(args, totalArgsLength, mainFunc);
+}
+
+/**
+ * Windows-specific version for wide command-line arguments, e.g.,
+ * from a wmain/wWinMain C entry point.
+ * This wide version uses the specified arguments, unlike narrow
+ * _d_run_main which uses the actual (wide) process arguments instead.
+ */
+version (Windows)
+extern (C) int _d_wrun_main(int argc, wchar** wargv, MainFunc mainFunc)
+{
+ // Allocate args[] on the stack
+ char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc];
+
+ // 1st pass: compute each argument's length as UTF-16 and UTF-8
+ size_t totalArgsLength = 0;
+ foreach (i; 0 .. argc)
+ {
+ const warg = wargv[i];
+ const size_t wlen = wcslen(warg) + 1; // incl. terminating null
+ assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max");
+ const int len = WideCharToMultiByte(CP_UTF8, 0, warg, cast(int) wlen, null, 0, null, null);
+ args[i] = (cast(char*) wlen)[0 .. len]; // args[i].ptr = wlen, args[i].length = len
+ totalArgsLength += len;
+ }
+
+ // Allocate a single buffer for all (null-terminated) argument strings in UTF-8 on the stack
+ char* utf8Buffer = cast(char*) alloca(totalArgsLength);
+
+ // 2nd pass: convert to UTF-8 and finalize `args`
+ char* utf8 = utf8Buffer;
+ foreach (i; 0 .. argc)
+ {
+ const wlen = cast(int) args[i].ptr;
+ const len = cast(int) args[i].length;
+ WideCharToMultiByte(CP_UTF8, 0, wargv[i], wlen, utf8, len, null, null);
+ args[i] = utf8[0 .. len-1]; // excl. terminating null
+ utf8 += len;
+ }
+
+ // Set C argc/argv; argv is a new stack-allocated array of UTF-8 C strings
+ char*[] argv = (cast(char**) alloca(argc * (char*).sizeof))[0 .. argc];
+ foreach (i, ref arg; argv)
+ arg = args[i].ptr;
+ _cArgs.argc = argc;
+ _cArgs.argv = argv.ptr;
+
+ totalArgsLength -= argc; // excl. null terminator per arg
+ return _d_run_main2(args, totalArgsLength, mainFunc);
+}
+
+private extern (C) int _d_run_main2(char[][] args, size_t totalArgsLength, MainFunc mainFunc)
+{
+ int result;
+
+ version (FreeBSD) version (D_InlineAsm_X86)
+ {
+ /*
+ * FreeBSD/i386 sets the FPU precision mode to 53 bit double.
+ * Make it 64 bit extended.
+ */
+ ushort fpucw;
+ asm
+ {
+ fstsw fpucw;
+ or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision
+ // 111111: mask all FP exceptions
+ fldcw fpucw;
+ }
+ }
+ version (CRuntime_Microsoft)
+ {
+ // enable full precision for reals
+ version (D_InlineAsm_X86_64)
+ {
+ asm
+ {
+ push RAX;
+ fstcw word ptr [RSP];
+ or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision
+ // 111111: mask all FP exceptions
+ fldcw word ptr [RSP];
+ pop RAX;
+ }
+ }
+ else version (D_InlineAsm_X86)
+ {
+ asm
+ {
+ push EAX;
+ fstcw word ptr [ESP];
+ or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision
+ // 111111: mask all FP exceptions
+ fldcw word ptr [ESP];
+ pop EAX;
+ }
+ }
+ else version (GNU_InlineAsm)
+ {
+ size_t fpu_cw;
+ asm { "fstcw %0" : "=m" (fpu_cw); }
+ fpu_cw |= 0b11_00_111111; // 11: use 64 bit extended-precision
+ // 111111: mask all FP exceptions
+ asm { "fldcw %0" : "=m" (fpu_cw); }
+ }
+ }
+
/* Create a copy of args[] on the stack to be used for main, so that rt_args()
* cannot be modified by the user.
* Note that when this function returns, _d_args will refer to garbage.
@@ -368,28 +434,33 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
char[][] argsCopy = buff[0 .. args.length];
auto argBuff = cast(char*) (buff + args.length);
size_t j = 0;
+ import rt.config : rt_cmdline_enabled;
+ bool parseOpts = rt_cmdline_enabled!();
foreach (arg; args)
{
- if (arg.length < 6 || arg[0..6] != "--DRT-") // skip D runtime options
- {
- argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]);
- argBuff += arg.length;
- }
+ // Do not pass Druntime options to the program
+ if (parseOpts && arg.length >= 6 && arg[0 .. 6] == "--DRT-")
+ continue;
+ // https://issues.dlang.org/show_bug.cgi?id=20459
+ if (arg == "--")
+ parseOpts = false;
+ argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]);
+ argBuff += arg.length;
}
args = argsCopy[0..j];
}
- bool trapExceptions = rt_trapExceptions;
+ auto useExceptionTrap = parseExceptionOptions();
version (Windows)
{
if (IsDebuggerPresent())
- trapExceptions = false;
+ useExceptionTrap = false;
}
void tryExec(scope void delegate() dg)
{
- if (trapExceptions)
+ if (useExceptionTrap)
{
try
{
@@ -417,8 +488,34 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
// thrown during cleanup, however, will abort the cleanup process.
void runAll()
{
- if (rt_init() && runModuleUnitTests())
- tryExec({ result = mainFunc(args); });
+ if (rt_init())
+ {
+ auto utResult = runModuleUnitTests();
+ assert(utResult.passed <= utResult.executed);
+ if (utResult.passed == utResult.executed)
+ {
+ if (utResult.summarize)
+ {
+ if (utResult.passed == 0)
+ .fprintf(.stderr, "No unittests run\n");
+ else
+ .fprintf(.stderr, "%d modules passed unittests\n",
+ cast(int)utResult.passed);
+ }
+ if (utResult.runMain)
+ tryExec({ result = mainFunc(args); });
+ else
+ result = EXIT_SUCCESS;
+ }
+ else
+ {
+ if (utResult.summarize)
+ .fprintf(.stderr, "%d/%d modules FAILED unittests\n",
+ cast(int)(utResult.executed - utResult.passed),
+ cast(int)utResult.executed);
+ result = EXIT_FAILURE;
+ }
+ }
else
result = EXIT_FAILURE;
@@ -441,17 +538,17 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
return result;
}
-private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothrow sink)
+private void formatThrowable(Throwable t, scope void delegate(const scope char[] s) nothrow sink)
{
- for (; t; t = t.next)
+ foreach (u; t)
{
- t.toString(sink); sink("\n");
+ u.toString(sink); sink("\n");
- auto e = cast(Error)t;
+ auto e = cast(Error)u;
if (e is null || e.bypassedException is null) continue;
sink("=== Bypassed ===\n");
- for (auto t2 = e.bypassedException; t2; t2 = t2.next)
+ foreach (t2; e.bypassedException)
{
t2.toString(sink); sink("\n");
}
@@ -459,6 +556,18 @@ private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothr
}
}
+private auto parseExceptionOptions()
+{
+ import rt.config : rt_configOption;
+ import core.internal.parseoptions : rt_parseOption;
+ const optName = "trapExceptions";
+ auto option = rt_configOption(optName);
+ auto trap = rt_trapExceptions;
+ if (option.length)
+ rt_parseOption(optName, option, trap, "");
+ return trap;
+}
+
extern (C) void _d_print_throwable(Throwable t)
{
// On Windows, a console may not be present to print the output to.
@@ -468,17 +577,17 @@ extern (C) void _d_print_throwable(Throwable t)
{
static struct WSink
{
- wchar_t* ptr; size_t len;
+ WCHAR* ptr; size_t len;
- void sink(in char[] s) scope nothrow
+ void sink(const scope char[] s) scope nothrow
{
if (!s.length) return;
int swlen = MultiByteToWideChar(
CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0);
if (!swlen) return;
- auto newPtr = cast(wchar_t*)realloc(ptr,
- (this.len + swlen + 1) * wchar_t.sizeof);
+ auto newPtr = cast(WCHAR*)realloc(ptr,
+ (this.len + swlen + 1) * WCHAR.sizeof);
if (!newPtr) return;
ptr = newPtr;
auto written = MultiByteToWideChar(
@@ -486,7 +595,7 @@ extern (C) void _d_print_throwable(Throwable t)
len += written;
}
- wchar_t* get() { if (ptr) ptr[len] = 0; return ptr; }
+ typeof(ptr) get() { if (ptr) ptr[len] = 0; return ptr; }
void free() { .free(ptr); }
}
@@ -558,7 +667,7 @@ extern (C) void _d_print_throwable(Throwable t)
}
}
- void sink(in char[] buf) scope nothrow
+ void sink(const scope char[] buf) scope nothrow
{
fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr);
}
diff --git a/libphobos/libdruntime/rt/dylib_fixes.c b/libphobos/libdruntime/rt/dylib_fixes.c
index 7bcf34b01c5..e484fed7fb1 100644
--- a/libphobos/libdruntime/rt/dylib_fixes.c
+++ b/libphobos/libdruntime/rt/dylib_fixes.c
@@ -2,7 +2,7 @@
* OS X support for dynamic libraries.
*
* Copyright: Copyright Digital Mars 2010 - 2010.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright
*/
diff --git a/libphobos/libdruntime/rt/ehalloc.d b/libphobos/libdruntime/rt/ehalloc.d
new file mode 100644
index 00000000000..1dcd69a284f
--- /dev/null
+++ b/libphobos/libdruntime/rt/ehalloc.d
@@ -0,0 +1,125 @@
+/**
+ * Exception allocation, cloning, and release compiler support routines.
+ *
+ * Copyright: Copyright (c) 2017 by D Language Foundation
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Walter Bright
+ * Source: $(DRUNTIMESRC rt/_ehalloc.d)
+ */
+
+module rt.ehalloc;
+
+//debug = PRINTF;
+
+debug(PRINTF)
+{
+ import core.stdc.stdio;
+}
+
+/**********************************************
+ * Allocate an exception of type `ci` from the exception pool.
+ * It has the same interface as `rt.lifetime._d_newclass()`.
+ * The class type must be Throwable or derived from it,
+ * and cannot be a COM or C++ class. The compiler must enforce
+ * this.
+ * Returns:
+ * default initialized instance of the type
+ */
+
+extern (C) Throwable _d_newThrowable(const TypeInfo_Class ci)
+{
+ debug(PRINTF) printf("_d_newThrowable(ci = %p, %s)\n", ci, cast(char *)ci.name);
+
+ assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass));
+ assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass));
+
+ import core.stdc.stdlib : malloc;
+ auto init = ci.initializer;
+ void* p = malloc(init.length);
+ if (!p)
+ {
+ import core.exception : onOutOfMemoryError;
+ onOutOfMemoryError();
+ }
+
+ debug(PRINTF) printf(" p = %p\n", p);
+
+ // initialize it
+ p[0 .. init.length] = init[];
+
+ if (!(ci.m_flags & TypeInfo_Class.ClassFlags.noPointers))
+ {
+ // Inform the GC about the pointers in the object instance
+ import core.memory : GC;
+
+ GC.addRange(p, init.length, ci);
+ }
+
+ debug(PRINTF) printf("initialization done\n");
+ Throwable t = cast(Throwable)p;
+ t.refcount() = 1;
+ return t;
+}
+
+
+/********************************************
+ * Delete exception instance `t` from the exception pool.
+ * Must have been allocated with `_d_newThrowable()`.
+ * This is meant to be called at the close of a catch block.
+ * It's nothrow because otherwise any function with a catch block could
+ * not be nothrow.
+ * Input:
+ * t = Throwable
+ */
+
+nothrow extern (C) void _d_delThrowable(Throwable t)
+{
+ if (t)
+ {
+ debug(PRINTF) printf("_d_delThrowable(%p)\n", t);
+
+ /* If allocated by the GC, don't free it.
+ * Let the GC handle it.
+ * Supporting this is necessary while transitioning
+ * to this new scheme for allocating exceptions.
+ */
+ auto refcount = t.refcount();
+ if (refcount == 0)
+ return; // it was allocated by the GC
+
+ if (refcount == 1)
+ assert(0); // no zombie objects
+
+ t.refcount() = --refcount;
+ if (refcount > 1)
+ return;
+
+ TypeInfo_Class **pc = cast(TypeInfo_Class **)t;
+ if (*pc)
+ {
+ TypeInfo_Class ci = **pc;
+
+ if (!(ci.m_flags & TypeInfo_Class.ClassFlags.noPointers))
+ {
+ // Inform the GC about the pointers in the object instance
+ import core.memory : GC;
+ GC.removeRange(cast(void*) t);
+ }
+ }
+
+ try
+ {
+ import rt.lifetime : rt_finalize;
+ rt_finalize(cast(void*) t);
+ }
+ catch (Throwable t)
+ {
+ assert(0); // should never happen since Throwable.~this() is nothrow
+ }
+ import core.stdc.stdlib : free;
+ debug(PRINTF) printf("free(%p)\n", t);
+ free(cast(void*) t);
+ }
+}
diff --git a/libphobos/libdruntime/rt/invariant.d b/libphobos/libdruntime/rt/invariant.d
index 4dddfad38d2..e536196e8c8 100644
--- a/libphobos/libdruntime/rt/invariant.d
+++ b/libphobos/libdruntime/rt/invariant.d
@@ -2,8 +2,9 @@
* Implementation of invariant support routines.
*
* Copyright: Copyright Digital Mars 2007 - 2010.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright
+ * Source: $(DRUNTIMESRC rt/_invariant.d)
*/
/* Copyright Digital Mars 2007 - 2010.
diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d
index 6a6eb50eefa..f1a9d873860 100644
--- a/libphobos/libdruntime/rt/lifetime.d
+++ b/libphobos/libdruntime/rt/lifetime.d
@@ -7,22 +7,18 @@
* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
* (See accompanying file LICENSE)
* Authors: Walter Bright, Sean Kelly, Steven Schveighoffer
- * Source: $(DRUNTIMESRC src/rt/_lifetime.d)
+ * Source: $(DRUNTIMESRC rt/_lifetime.d)
*/
module rt.lifetime;
-import core.stdc.stdlib;
-import core.stdc.string;
-import core.stdc.stdarg;
-import core.bitop;
+import core.attribute : weak;
import core.memory;
debug(PRINTF) import core.stdc.stdio;
static import rt.tlsgc;
alias BlkInfo = GC.BlkInfo;
alias BlkAttr = GC.BlkAttr;
-import core.exception : onOutOfMemoryError, onFinalizeError, onInvalidMemoryOperationError;
private
{
@@ -52,7 +48,7 @@ extern (C) void lifetime_init()
/**
*
*/
-extern (C) void* _d_allocmemory(size_t sz)
+extern (C) void* _d_allocmemory(size_t sz) @weak
{
return GC.malloc(sz);
}
@@ -60,9 +56,12 @@ extern (C) void* _d_allocmemory(size_t sz)
/**
*
*/
-extern (C) Object _d_newclass(const ClassInfo ci)
+extern (C) Object _d_newclass(const ClassInfo ci) @weak
{
+ import core.stdc.stdlib;
+ import core.exception : onOutOfMemoryError;
void* p;
+ auto init = ci.initializer;
debug(PRINTF) printf("_d_newclass(ci = %p, %s)\n", ci, cast(char *)ci.name);
if (ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass)
@@ -71,7 +70,7 @@ extern (C) Object _d_newclass(const ClassInfo ci)
* function called by Release() when Release()'s reference count goes
* to zero.
*/
- p = malloc(ci.initializer.length);
+ p = malloc(init.length);
if (!p)
onOutOfMemoryError();
}
@@ -85,26 +84,26 @@ extern (C) Object _d_newclass(const ClassInfo ci)
attr |= BlkAttr.FINALIZE;
if (ci.m_flags & TypeInfo_Class.ClassFlags.noPointers)
attr |= BlkAttr.NO_SCAN;
- p = GC.malloc(ci.initializer.length, attr, ci);
+ p = GC.malloc(init.length, attr, ci);
debug(PRINTF) printf(" p = %p\n", p);
}
debug(PRINTF)
{
printf("p = %p\n", p);
- printf("ci = %p, ci.init.ptr = %p, len = %llu\n", ci, ci.initializer.ptr, cast(ulong)ci.initializer.length);
- printf("vptr = %p\n", *cast(void**) ci.initializer);
- printf("vtbl[0] = %p\n", (*cast(void***) ci.initializer)[0]);
- printf("vtbl[1] = %p\n", (*cast(void***) ci.initializer)[1]);
- printf("init[0] = %x\n", (cast(uint*) ci.initializer)[0]);
- printf("init[1] = %x\n", (cast(uint*) ci.initializer)[1]);
- printf("init[2] = %x\n", (cast(uint*) ci.initializer)[2]);
- printf("init[3] = %x\n", (cast(uint*) ci.initializer)[3]);
- printf("init[4] = %x\n", (cast(uint*) ci.initializer)[4]);
+ printf("ci = %p, ci.init.ptr = %p, len = %llu\n", ci, init.ptr, cast(ulong)init.length);
+ printf("vptr = %p\n", *cast(void**) init);
+ printf("vtbl[0] = %p\n", (*cast(void***) init)[0]);
+ printf("vtbl[1] = %p\n", (*cast(void***) init)[1]);
+ printf("init[0] = %x\n", (cast(uint*) init)[0]);
+ printf("init[1] = %x\n", (cast(uint*) init)[1]);
+ printf("init[2] = %x\n", (cast(uint*) init)[2]);
+ printf("init[3] = %x\n", (cast(uint*) init)[3]);
+ printf("init[4] = %x\n", (cast(uint*) init)[4]);
}
// initialize it
- p[0 .. ci.initializer.length] = ci.initializer[];
+ p[0 .. init.length] = init[];
debug(PRINTF) printf("initialization done\n");
return cast(Object) p;
@@ -134,7 +133,7 @@ private extern (D) alias void function (Object) fp_t;
/**
*
*/
-extern (C) void _d_delclass(Object* p)
+extern (C) void _d_delclass(Object* p) @weak
{
if (*p)
{
@@ -169,7 +168,7 @@ extern (C) void _d_delclass(Object* p)
* being deleted is a pointer to a struct with a destructor
* but doesn't have an overloaded delete operator.
*/
-extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf)
+extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf) @weak
{
if (*p)
{
@@ -182,7 +181,7 @@ extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf)
}
// strip const/immutable/shared/inout from type info
-inout(TypeInfo) unqualify(inout(TypeInfo) cti) pure nothrow @nogc
+inout(TypeInfo) unqualify(return inout(TypeInfo) cti) pure nothrow @nogc
{
TypeInfo ti = cast() cti;
while (ti)
@@ -382,7 +381,7 @@ size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
/**
get the start of the array for the given block
*/
-void *__arrayStart(BlkInfo info) nothrow pure
+void *__arrayStart(return BlkInfo info) nothrow pure
{
return info.base + ((info.size & BIGLENGTHMASK) ? LARGEPREFIX : 0);
}
@@ -398,10 +397,26 @@ size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted
}
/**
+ clear padding that might not be zeroed by the GC (it assumes it is within the
+ requested size from the start, but it is actually at the end of the allocated block)
+ */
+private void __arrayClearPad(ref BlkInfo info, size_t arrsize, size_t padsize) nothrow pure
+{
+ import core.stdc.string;
+ if (padsize > MEDPAD && !(info.attr & BlkAttr.NO_SCAN) && info.base)
+ {
+ if (info.size < PAGESIZE)
+ memset(info.base + arrsize, 0, padsize);
+ else
+ memset(info.base, 0, LARGEPREFIX);
+ }
+}
+
+/**
allocate an array memory block by applying the proper padding and
assigning block attributes if not inherited from the existing block
*/
-BlkInfo __arrayAlloc(size_t arrsize, const TypeInfo ti, const TypeInfo tinext) nothrow pure
+BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure
{
import core.checkedint;
@@ -417,24 +432,30 @@ BlkInfo __arrayAlloc(size_t arrsize, const TypeInfo ti, const TypeInfo tinext) n
uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE;
if (typeInfoSize)
attr |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE;
- return GC.qalloc(padded_size, attr, ti);
+
+ auto bi = GC.qalloc(padded_size, attr, tinext);
+ __arrayClearPad(bi, arrsize, padsize);
+ return bi;
}
-BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const TypeInfo ti, const TypeInfo tinext)
+BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext)
{
import core.checkedint;
if (!info.base)
return __arrayAlloc(arrsize, ti, tinext);
+ immutable padsize = __arrayPad(arrsize, tinext);
bool overflow;
- auto padded_size = addu(arrsize, __arrayPad(arrsize, tinext), overflow);
+ auto padded_size = addu(arrsize, padsize, overflow);
if (overflow)
{
return BlkInfo();
}
- return GC.qalloc(padded_size, info.attr, ti);
+ auto bi = GC.qalloc(padded_size, info.attr, tinext);
+ __arrayClearPad(bi, arrsize, padsize);
+ return bi;
}
/**
@@ -468,6 +489,8 @@ else
{
if (!__blkcache_storage)
{
+ import core.stdc.stdlib;
+ import core.stdc.string;
// allocate the block cache for the first time
immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS;
__blkcache_storage = cast(BlkInfo *)malloc(size);
@@ -482,6 +505,7 @@ static ~this()
// free the blkcache
if (__blkcache_storage)
{
+ import core.stdc.stdlib;
free(__blkcache_storage);
__blkcache_storage = null;
}
@@ -670,7 +694,10 @@ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/
// Note: Since we "assume" the append is safe, it means it is not shared.
// Since it is not shared, we also know it won't throw (no lock).
if (!__setArrayAllocLength(info, newsize, false, tinext))
+ {
+ import core.exception : onInvalidMemoryOperationError;
onInvalidMemoryOperationError();
+ }
// cache the block if not already done.
if (!isshared && !bic)
@@ -720,14 +747,17 @@ void __doPostblit(void *ptr, size_t len, const TypeInfo ti)
* of 0 to get the current capacity. Returns the number of elements that can
* actually be stored once the resizing is done.
*/
-extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p)
+extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p) @weak
in
{
assert(ti);
assert(!(*p).length || (*p).ptr);
}
-body
+do
{
+ import core.stdc.string;
+ import core.exception : onOutOfMemoryError;
+
// step 1, get the block
auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
auto bic = isshared ? null : __getBlkInfo((*p).ptr);
@@ -890,8 +920,10 @@ Lcontinue:
* Allocate a new uninitialized array of length elements.
* ti is the type of the resulting array, or pointer to element.
*/
-extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow
+extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak
{
+ import core.exception : onOutOfMemoryError;
+
auto tinext = unqualify(ti.next);
auto size = tinext.tsize;
@@ -949,8 +981,10 @@ Lcontinue:
* ti is the type of the resulting array, or pointer to element.
* (For when the array is initialized to 0)
*/
-extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow
+extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @weak
{
+ import core.stdc.string;
+
void[] result = _d_newarrayU(ti, length);
auto tinext = unqualify(ti.next);
auto size = tinext.tsize;
@@ -962,7 +996,7 @@ extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow
/**
* For when the array has a non-zero initializer.
*/
-extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow
+extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @weak
{
import core.internal.traits : AliasSeq;
@@ -983,6 +1017,7 @@ extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow
default:
{
+ import core.stdc.string;
immutable sz = init.length;
for (size_t u = 0; u < size * length; u += sz)
memcpy(result.ptr + u, init.ptr, sz);
@@ -1036,7 +1071,7 @@ void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims)
/**
*
*/
-extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims)
+extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak
{
debug(PRINTF) printf("_d_newarraymT(dims.length = %d)\n", dims.length);
@@ -1052,7 +1087,7 @@ extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims)
/**
*
*/
-extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims)
+extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak
{
debug(PRINTF) printf("_d_newarraymiT(dims.length = %d)\n", dims.length);
@@ -1068,12 +1103,13 @@ extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims)
* Allocate an uninitialized non-array item.
* This is an optimization to avoid things needed for arrays like the __arrayPad(size).
*/
-extern (C) void* _d_newitemU(in TypeInfo _ti)
+extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak
{
auto ti = unqualify(_ti);
auto flags = !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0;
immutable tiSize = structTypeInfoSize(ti);
- immutable size = ti.tsize + tiSize;
+ immutable itemSize = ti.tsize;
+ immutable size = itemSize + tiSize;
if (tiSize)
flags |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE;
@@ -1081,22 +1117,27 @@ extern (C) void* _d_newitemU(in TypeInfo _ti)
auto p = blkInf.base;
if (tiSize)
+ {
+ *cast(TypeInfo*)(p + itemSize) = null; // the GC might not have cleared this area
*cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti;
+ }
return p;
}
/// Same as above, zero initializes the item.
-extern (C) void* _d_newitemT(in TypeInfo _ti)
+extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak
{
+ import core.stdc.string;
auto p = _d_newitemU(_ti);
memset(p, 0, _ti.tsize);
return p;
}
/// Same as above, for item with non-zero initializer.
-extern (C) void* _d_newitemiT(in TypeInfo _ti)
+extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak
{
+ import core.stdc.string;
auto p = _d_newitemU(_ti);
auto init = _ti.initializer();
assert(init.length <= _ti.tsize);
@@ -1113,15 +1154,6 @@ struct Array
byte* data;
}
-
-/**
- * This function has been replaced by _d_delarray_t
- */
-extern (C) void _d_delarray(void[]* p)
-{
- _d_delarray_t(p, null);
-}
-
debug(PRINTF)
{
extern(C) void printArrayCache()
@@ -1138,7 +1170,7 @@ debug(PRINTF)
/**
*
*/
-extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti)
+extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) @weak
{
if (p)
{
@@ -1164,7 +1196,7 @@ extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti)
}
}
-unittest
+deprecated unittest
{
__gshared size_t countDtor = 0;
struct S
@@ -1176,7 +1208,7 @@ unittest
auto x = new S[10000];
void* p = x.ptr;
assert(GC.addrOf(p) != null);
- delete x;
+ _d_delarray_t(cast(void[]*)&x, typeid(typeof(x[0]))); // delete x;
assert(GC.addrOf(p) == null);
assert(countDtor == 10000);
@@ -1185,7 +1217,7 @@ unittest
auto z = y[200 .. 300];
p = z.ptr;
assert(GC.addrOf(p) != null);
- delete z;
+ _d_delarray_t(cast(void[]*)&z, typeid(typeof(z[0]))); // delete z;
assert(GC.addrOf(p) == null);
assert(countDtor == 10000 + 400);
}
@@ -1193,7 +1225,7 @@ unittest
/**
*
*/
-extern (C) void _d_delmemory(void* *p)
+extern (C) void _d_delmemory(void* *p) @weak
{
if (*p)
{
@@ -1206,7 +1238,7 @@ extern (C) void _d_delmemory(void* *p)
/**
*
*/
-extern (C) void _d_callinterfacefinalizer(void *p)
+extern (C) void _d_callinterfacefinalizer(void *p) @weak
{
if (p)
{
@@ -1220,7 +1252,7 @@ extern (C) void _d_callinterfacefinalizer(void *p)
/**
*
*/
-extern (C) void _d_callfinalizer(void* p)
+extern (C) void _d_callfinalizer(void* p) @weak
{
rt_finalize( p );
}
@@ -1324,6 +1356,7 @@ void finalize_array2(void* p, size_t size) nothrow
}
catch (Exception e)
{
+ import core.exception : onFinalizeError;
onFinalizeError(si, e);
}
}
@@ -1353,6 +1386,7 @@ void finalize_struct(void* p, size_t size) nothrow
}
catch (Exception e)
{
+ import core.exception : onFinalizeError;
onFinalizeError(ti, e);
}
}
@@ -1393,6 +1427,7 @@ extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true)
}
catch (Exception e)
{
+ import core.exception : onFinalizeError;
onFinalizeError(*pc, e);
}
finally
@@ -1401,12 +1436,12 @@ extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true)
}
}
-extern (C) void rt_finalize(void* p, bool det = true)
+extern (C) void rt_finalize(void* p, bool det = true) nothrow
{
rt_finalize2(p, det, true);
}
-extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr)
+extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow
{
// to verify: reset memory necessary?
if (!(attr & BlkAttr.STRUCTFINAL))
@@ -1421,14 +1456,17 @@ extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr)
/**
* Resize dynamic arrays with 0 initializers.
*/
-extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p)
+extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) @weak
in
{
assert(ti);
assert(!(*p).length || (*p).ptr);
}
-body
+do
{
+ import core.stdc.string;
+ import core.exception : onOutOfMemoryError;
+
debug(PRINTF)
{
//printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
@@ -1436,170 +1474,179 @@ body
printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length);
}
- void* newdata = void;
- if (newlength)
+ if (newlength <= (*p).length)
{
- if (newlength <= (*p).length)
+ *p = (*p)[0 .. newlength];
+ void* newdata = (*p).ptr;
+ return newdata[0 .. newlength];
+ }
+ auto tinext = unqualify(ti.next);
+ size_t sizeelem = tinext.tsize;
+
+ /* Calculate: newsize = newlength * sizeelem
+ */
+ bool overflow = false;
+ version (D_InlineAsm_X86)
+ {
+ size_t newsize = void;
+
+ asm pure nothrow @nogc
{
- *p = (*p)[0 .. newlength];
- newdata = (*p).ptr;
- return newdata[0 .. newlength];
+ mov EAX, newlength;
+ mul EAX, sizeelem;
+ mov newsize, EAX;
+ setc overflow;
}
- auto tinext = unqualify(ti.next);
- size_t sizeelem = tinext.tsize;
- version (D_InlineAsm_X86)
- {
- size_t newsize = void;
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ size_t newsize = void;
- asm pure nothrow @nogc
- {
- mov EAX, newlength;
- mul EAX, sizeelem;
- mov newsize, EAX;
- jc Loverflow;
- }
- }
- else version (D_InlineAsm_X86_64)
+ asm pure nothrow @nogc
{
- size_t newsize = void;
-
- asm pure nothrow @nogc
- {
- mov RAX, newlength;
- mul RAX, sizeelem;
- mov newsize, RAX;
- jc Loverflow;
- }
+ mov RAX, newlength;
+ mul RAX, sizeelem;
+ mov newsize, RAX;
+ setc overflow;
}
- else
- {
- import core.checkedint : mulu;
+ }
+ else
+ {
+ import core.checkedint : mulu;
+ const size_t newsize = mulu(sizeelem, newlength, overflow);
+ }
+ if (overflow)
+ {
+ onOutOfMemoryError();
+ assert(0);
+ }
- bool overflow = false;
- size_t newsize = mulu(sizeelem, newlength, overflow);
- if (overflow)
- goto Loverflow;
- }
+ debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
- debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
+ const isshared = typeid(ti) is typeid(TypeInfo_Shared);
+
+ if (!(*p).ptr)
+ {
+ // pointer was null, need to allocate
+ auto info = __arrayAlloc(newsize, ti, tinext);
+ if (info.base is null)
+ {
+ onOutOfMemoryError();
+ assert(0);
+ }
+ __setArrayAllocLength(info, newsize, isshared, tinext);
+ if (!isshared)
+ __insertBlkInfoCache(info, null);
+ void* newdata = cast(byte *)__arrayStart(info);
+ memset(newdata, 0, newsize);
+ *p = newdata[0 .. newlength];
+ return *p;
+ }
- auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
+ const size_t size = (*p).length * sizeelem;
+ auto bic = isshared ? null : __getBlkInfo((*p).ptr);
+ auto info = bic ? *bic : GC.query((*p).ptr);
- if ((*p).ptr)
+ /* Attempt to extend past the end of the existing array.
+ * If not possible, allocate new space for entire array and copy.
+ */
+ bool allocateAndCopy = false;
+ void* newdata = (*p).ptr;
+ if (info.base && (info.attr & BlkAttr.APPENDABLE))
+ {
+ // calculate the extent of the array given the base.
+ const size_t offset = (*p).ptr - __arrayStart(info);
+ if (info.size >= PAGESIZE)
{
- newdata = (*p).ptr;
- if (newlength > (*p).length)
+ // size of array is at the front of the block
+ if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
{
- size_t size = (*p).length * sizeelem;
- auto bic = isshared ? null : __getBlkInfo((*p).ptr);
- auto info = bic ? *bic : GC.query((*p).ptr);
- if (info.base && (info.attr & BlkAttr.APPENDABLE))
+ // check to see if it failed because there is not
+ // enough space
+ if (*(cast(size_t*)info.base) == size + offset)
{
- // calculate the extent of the array given the base.
- size_t offset = (*p).ptr - __arrayStart(info);
- if (info.size >= PAGESIZE)
+ // not enough space, try extending
+ auto extendsize = newsize + offset + LARGEPAD - info.size;
+ auto u = GC.extend(info.base, extendsize, extendsize);
+ if (u)
{
- // size of array is at the front of the block
- if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // check to see if it failed because there is not
- // enough space
- if (*(cast(size_t*)info.base) == size + offset)
- {
- // not enough space, try extending
- auto extendsize = newsize + offset + LARGEPAD - info.size;
- auto u = GC.extend(info.base, extendsize, extendsize);
- if (u)
- {
- // extend worked, now try setting the length
- // again.
- info.size = u;
- if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- if (!isshared)
- __insertBlkInfoCache(info, bic);
- goto L1;
- }
- }
- }
-
- // couldn't do it, reallocate
- goto L2;
- }
- else if (!isshared && !bic)
+ // extend worked, now try setting the length
+ // again.
+ info.size = u;
+ if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
{
- // add this to the cache, it wasn't present previously.
- __insertBlkInfoCache(info, null);
+ if (!isshared)
+ __insertBlkInfoCache(info, bic);
+ memset(newdata + size, 0, newsize - size);
+ *p = newdata[0 .. newlength];
+ return *p;
}
}
- else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // could not resize in place
- goto L2;
- }
- else if (!isshared && !bic)
- {
- // add this to the cache, it wasn't present previously.
- __insertBlkInfoCache(info, null);
- }
}
- else
- {
- if (info.base)
- {
- L2:
- if (bic)
- {
- // a chance that flags have changed since this was cached, we should fetch the most recent flags
- info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
- }
- info = __arrayAlloc(newsize, info, ti, tinext);
- }
- else
- {
- info = __arrayAlloc(newsize, ti, tinext);
- }
- if (info.base is null)
- goto Loverflow;
-
- __setArrayAllocLength(info, newsize, isshared, tinext);
- if (!isshared)
- __insertBlkInfoCache(info, bic);
- newdata = cast(byte *)__arrayStart(info);
- newdata[0 .. size] = (*p).ptr[0 .. size];
-
- // do postblit processing
- __doPostblit(newdata, size, tinext);
- }
- L1:
- memset(newdata + size, 0, newsize - size);
+ // couldn't do it, reallocate
+ allocateAndCopy = true;
+ }
+ else if (!isshared && !bic)
+ {
+ // add this to the cache, it wasn't present previously.
+ __insertBlkInfoCache(info, null);
}
}
- else
+ else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
{
- // pointer was null, need to allocate
- auto info = __arrayAlloc(newsize, ti, tinext);
- if (info.base is null)
- goto Loverflow;
- __setArrayAllocLength(info, newsize, isshared, tinext);
- if (!isshared)
- __insertBlkInfoCache(info, null);
- newdata = cast(byte *)__arrayStart(info);
- memset(newdata, 0, newsize);
+ // could not resize in place
+ allocateAndCopy = true;
+ }
+ else if (!isshared && !bic)
+ {
+ // add this to the cache, it wasn't present previously.
+ __insertBlkInfoCache(info, null);
}
}
else
+ allocateAndCopy = true;
+
+ if (allocateAndCopy)
{
- newdata = (*p).ptr;
+ if (info.base)
+ {
+ if (bic)
+ {
+ // a chance that flags have changed since this was cached, we should fetch the most recent flags
+ info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
+ }
+ info = __arrayAlloc(newsize, info, ti, tinext);
+ }
+ else
+ {
+ info = __arrayAlloc(newsize, ti, tinext);
+ }
+
+ if (info.base is null)
+ {
+ onOutOfMemoryError();
+ assert(0);
+ }
+
+ __setArrayAllocLength(info, newsize, isshared, tinext);
+ if (!isshared)
+ __insertBlkInfoCache(info, bic);
+ newdata = cast(byte *)__arrayStart(info);
+ newdata[0 .. size] = (*p).ptr[0 .. size];
+
+ /* Do postblit processing, as we are making a copy and the
+ * original array may have references.
+ * Note that this may throw.
+ */
+ __doPostblit(newdata, size, tinext);
}
+ // Zero the unused portion of the newly allocated space
+ memset(newdata + size, 0, newsize - size);
+
*p = newdata[0 .. newlength];
return *p;
-
-Loverflow:
- onOutOfMemoryError();
- assert(0);
}
@@ -1611,205 +1658,221 @@ Loverflow:
* initsize size of initializer
* ... initializer
*/
-extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p)
+extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) @weak
in
{
assert(!(*p).length || (*p).ptr);
}
-body
+do
{
- void* newdata;
- auto tinext = unqualify(ti.next);
- auto sizeelem = tinext.tsize;
- auto initializer = tinext.initializer();
- auto initsize = initializer.length;
-
- assert(sizeelem);
- assert(initsize);
- assert(initsize <= sizeelem);
- assert((sizeelem / initsize) * initsize == sizeelem);
+ import core.stdc.string;
+ import core.exception : onOutOfMemoryError;
debug(PRINTF)
{
- printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d, initsize = %d)\n", p, sizeelem, newlength, initsize);
+ //printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
if (p)
- printf("\tp.data = %p, p.length = %d\n", (*p).ptr, (*p).length);
+ printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length);
}
- if (newlength)
+ if (newlength <= (*p).length)
{
- version (D_InlineAsm_X86)
- {
- size_t newsize = void;
+ *p = (*p)[0 .. newlength];
+ void* newdata = (*p).ptr;
+ return newdata[0 .. newlength];
+ }
+ auto tinext = unqualify(ti.next);
+ size_t sizeelem = tinext.tsize;
- asm
- {
- mov EAX,newlength ;
- mul EAX,sizeelem ;
- mov newsize,EAX ;
- jc Loverflow ;
- }
+ /* Calculate: newsize = newlength * sizeelem
+ */
+ bool overflow = false;
+ version (D_InlineAsm_X86)
+ {
+ size_t newsize = void;
+
+ asm pure nothrow @nogc
+ {
+ mov EAX, newlength;
+ mul EAX, sizeelem;
+ mov newsize, EAX;
+ setc overflow;
}
- else version (D_InlineAsm_X86_64)
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ size_t newsize = void;
+
+ asm pure nothrow @nogc
{
- size_t newsize = void;
+ mov RAX, newlength;
+ mul RAX, sizeelem;
+ mov newsize, RAX;
+ setc overflow;
+ }
+ }
+ else
+ {
+ import core.checkedint : mulu;
+ const size_t newsize = mulu(sizeelem, newlength, overflow);
+ }
+ if (overflow)
+ {
+ onOutOfMemoryError();
+ assert(0);
+ }
- asm
- {
- mov RAX,newlength ;
- mul RAX,sizeelem ;
- mov newsize,RAX ;
- jc Loverflow ;
- }
+ debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
+
+ const isshared = typeid(ti) is typeid(TypeInfo_Shared);
+
+ static void doInitialize(void *start, void *end, const void[] initializer)
+ {
+ if (initializer.length == 1)
+ {
+ memset(start, *(cast(ubyte*)initializer.ptr), end - start);
}
else
{
- import core.checkedint : mulu;
+ auto q = initializer.ptr;
+ immutable initsize = initializer.length;
+ for (; start < end; start += initsize)
+ {
+ memcpy(start, q, initsize);
+ }
+ }
+ }
- bool overflow = false;
- size_t newsize = mulu(sizeelem, newlength, overflow);
- if (overflow)
- goto Loverflow;
+ if (!(*p).ptr)
+ {
+ // pointer was null, need to allocate
+ auto info = __arrayAlloc(newsize, ti, tinext);
+ if (info.base is null)
+ {
+ onOutOfMemoryError();
+ assert(0);
}
- debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
+ __setArrayAllocLength(info, newsize, isshared, tinext);
+ if (!isshared)
+ __insertBlkInfoCache(info, null);
+ void* newdata = cast(byte *)__arrayStart(info);
+ doInitialize(newdata, newdata + newsize, tinext.initializer);
+ *p = newdata[0 .. newlength];
+ return *p;
+ }
+ const size_t size = (*p).length * sizeelem;
+ auto bic = isshared ? null : __getBlkInfo((*p).ptr);
+ auto info = bic ? *bic : GC.query((*p).ptr);
- size_t size = (*p).length * sizeelem;
- auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
- if ((*p).ptr)
+ /* Attempt to extend past the end of the existing array.
+ * If not possible, allocate new space for entire array and copy.
+ */
+ bool allocateAndCopy = false;
+ void* newdata = (*p).ptr;
+
+ if (info.base && (info.attr & BlkAttr.APPENDABLE))
+ {
+ // calculate the extent of the array given the base.
+ const size_t offset = (*p).ptr - __arrayStart(info);
+ if (info.size >= PAGESIZE)
{
- newdata = (*p).ptr;
- if (newlength > (*p).length)
+ // size of array is at the front of the block
+ if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
{
- auto bic = isshared ? null : __getBlkInfo((*p).ptr);
- auto info = bic ? *bic : GC.query((*p).ptr);
-
- // calculate the extent of the array given the base.
- size_t offset = (*p).ptr - __arrayStart(info);
- if (info.base && (info.attr & BlkAttr.APPENDABLE))
- {
- if (info.size >= PAGESIZE)
- {
- // size of array is at the front of the block
- if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // check to see if it failed because there is not
- // enough space
- if (*(cast(size_t*)info.base) == size + offset)
- {
- // not enough space, try extending
- auto extendsize = newsize + offset + LARGEPAD - info.size;
- auto u = GC.extend(info.base, extendsize, extendsize);
- if (u)
- {
- // extend worked, now try setting the length
- // again.
- info.size = u;
- if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- if (!isshared)
- __insertBlkInfoCache(info, bic);
- goto L1;
- }
- }
- }
-
- // couldn't do it, reallocate
- goto L2;
- }
- else if (!isshared && !bic)
- {
- // add this to the cache, it wasn't present previously.
- __insertBlkInfoCache(info, null);
- }
- }
- else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
- {
- // could not resize in place
- goto L2;
- }
- else if (!isshared && !bic)
- {
- // add this to the cache, it wasn't present previously.
- __insertBlkInfoCache(info, null);
- }
- }
- else
+ // check to see if it failed because there is not
+ // enough space
+ if (*(cast(size_t*)info.base) == size + offset)
{
- // not appendable or not part of the heap yet.
- if (info.base)
+ // not enough space, try extending
+ auto extendsize = newsize + offset + LARGEPAD - info.size;
+ auto u = GC.extend(info.base, extendsize, extendsize);
+ if (u)
{
- L2:
- if (bic)
+ // extend worked, now try setting the length
+ // again.
+ info.size = u;
+ if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
{
- // a chance that flags have changed since this was cached, we should fetch the most recent flags
- info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
+ if (!isshared)
+ __insertBlkInfoCache(info, bic);
+ doInitialize(newdata + size, newdata + newsize, tinext.initializer);
+ *p = newdata[0 .. newlength];
+ return *p;
}
- info = __arrayAlloc(newsize, info, ti, tinext);
}
- else
- {
- info = __arrayAlloc(newsize, ti, tinext);
- }
- __setArrayAllocLength(info, newsize, isshared, tinext);
- if (!isshared)
- __insertBlkInfoCache(info, bic);
- newdata = cast(byte *)__arrayStart(info);
- newdata[0 .. size] = (*p).ptr[0 .. size];
-
- // do postblit processing
- __doPostblit(newdata, size, tinext);
}
- L1: ;
+
+ // couldn't do it, reallocate
+ allocateAndCopy = true;
+ }
+ else if (!isshared && !bic)
+ {
+ // add this to the cache, it wasn't present previously.
+ __insertBlkInfoCache(info, null);
}
}
- else
+ else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
{
- // length was zero, need to allocate
- auto info = __arrayAlloc(newsize, ti, tinext);
- __setArrayAllocLength(info, newsize, isshared, tinext);
- if (!isshared)
- __insertBlkInfoCache(info, null);
- newdata = cast(byte *)__arrayStart(info);
+ // could not resize in place
+ allocateAndCopy = true;
}
-
- auto q = initializer.ptr; // pointer to initializer
-
- if (newsize > size)
+ else if (!isshared && !bic)
{
- if (initsize == 1)
- {
- debug(PRINTF) printf("newdata = %p, size = %d, newsize = %d, *q = %d\n", newdata, size, newsize, *cast(byte*)q);
- memset(newdata + size, *cast(byte*)q, newsize - size);
- }
- else
- {
- for (size_t u = size; u < newsize; u += initsize)
- {
- memcpy(newdata + u, q, initsize);
- }
- }
+ // add this to the cache, it wasn't present previously.
+ __insertBlkInfoCache(info, null);
}
}
else
+ allocateAndCopy = true;
+
+ if (allocateAndCopy)
{
- newdata = (*p).ptr;
+ if (info.base)
+ {
+ if (bic)
+ {
+ // a chance that flags have changed since this was cached, we should fetch the most recent flags
+ info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
+ }
+ info = __arrayAlloc(newsize, info, ti, tinext);
+ }
+ else
+ {
+ info = __arrayAlloc(newsize, ti, tinext);
+ }
+
+ if (info.base is null)
+ {
+ onOutOfMemoryError();
+ assert(0);
+ }
+
+ __setArrayAllocLength(info, newsize, isshared, tinext);
+ if (!isshared)
+ __insertBlkInfoCache(info, bic);
+ newdata = cast(byte *)__arrayStart(info);
+ newdata[0 .. size] = (*p).ptr[0 .. size];
+
+ /* Do postblit processing, as we are making a copy and the
+ * original array may have references.
+ * Note that this may throw.
+ */
+ __doPostblit(newdata, size, tinext);
}
+ // Initialize the unused portion of the newly allocated space
+ doInitialize(newdata + size, newdata + newsize, tinext.initializer);
*p = newdata[0 .. newlength];
return *p;
-
-Loverflow:
- onOutOfMemoryError();
- assert(0);
}
-
/**
* Append y[] to array x[]
*/
-extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y)
+extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y) @weak
{
+ import core.stdc.string;
auto length = x.length;
auto tinext = unqualify(ti.next);
auto sizeelem = tinext.tsize; // array element size
@@ -1879,6 +1942,7 @@ size_t newCapacity(size_t newlength, size_t size)
*/
//long mult = 100 + (1000L * size) / (6 * log2plus1(newcap));
//long mult = 100 + (1000L * size) / log2plus1(newcap);
+ import core.bitop;
long mult = 100 + (1000L) / (bsr(newcap) + 1);
// testing shows 1.02 for large arrays is about the point of diminishing return
@@ -1908,8 +1972,9 @@ size_t newCapacity(size_t newlength, size_t size)
* Caller must initialize those elements.
*/
extern (C)
-byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n)
+byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak
{
+ import core.stdc.string;
// This is a cut&paste job from _d_arrayappendT(). Should be refactored.
// only optimize array append where ti is not a shared type
@@ -2011,28 +2076,28 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n)
/**
* Append dchar to char[]
*/
-extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
+extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak
{
// c could encode into from 1 to 4 characters
char[4] buf = void;
- byte[] appendthis; // passed to appendT
+ char[] appendthis; // passed to appendT
if (c <= 0x7F)
{
buf.ptr[0] = cast(char)c;
- appendthis = (cast(byte *)buf.ptr)[0..1];
+ appendthis = buf[0..1];
}
else if (c <= 0x7FF)
{
buf.ptr[0] = cast(char)(0xC0 | (c >> 6));
buf.ptr[1] = cast(char)(0x80 | (c & 0x3F));
- appendthis = (cast(byte *)buf.ptr)[0..2];
+ appendthis = buf[0..2];
}
else if (c <= 0xFFFF)
{
buf.ptr[0] = cast(char)(0xE0 | (c >> 12));
buf.ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
buf.ptr[2] = cast(char)(0x80 | (c & 0x3F));
- appendthis = (cast(byte *)buf.ptr)[0..3];
+ appendthis = buf[0..3];
}
else if (c <= 0x10FFFF)
{
@@ -2040,7 +2105,7 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
buf.ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
buf.ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
buf.ptr[3] = cast(char)(0x80 | (c & 0x3F));
- appendthis = (cast(byte *)buf.ptr)[0..4];
+ appendthis = buf[0..4];
}
else
{
@@ -2053,7 +2118,12 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
// get a typeinfo from the compiler. Assuming shared is the safest option.
// Once the compiler is fixed, the proper typeinfo should be forwarded.
//
- return _d_arrayappendT(typeid(shared char[]), x, appendthis);
+
+ // Hack because _d_arrayappendT takes `x` as a reference
+ auto xx = cast(shared(char)[])x;
+ object._d_arrayappendTImpl!(shared(char)[])._d_arrayappendT(xx, cast(shared(char)[])appendthis);
+ x = cast(byte[])xx;
+ return x;
}
unittest
@@ -2088,25 +2158,21 @@ unittest
/**
* Append dchar to wchar[]
*/
-extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c)
+extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak
{
// c could encode into from 1 to 2 w characters
wchar[2] buf = void;
- byte[] appendthis; // passed to appendT
+ wchar[] appendthis; // passed to appendT
if (c <= 0xFFFF)
{
buf.ptr[0] = cast(wchar) c;
- // note that although we are passing only 1 byte here, appendT
- // interprets this as being an array of wchar, making the necessary
- // casts.
- appendthis = (cast(byte *)buf.ptr)[0..1];
+ appendthis = buf[0..1];
}
else
{
buf.ptr[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
buf.ptr[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
- // ditto from above.
- appendthis = (cast(byte *)buf.ptr)[0..2];
+ appendthis = buf[0..2];
}
//
@@ -2114,14 +2180,18 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c)
// get a typeinfo from the compiler. Assuming shared is the safest option.
// Once the compiler is fixed, the proper typeinfo should be forwarded.
//
- return _d_arrayappendT(typeid(shared wchar[]), x, appendthis);
+
+ auto xx = (cast(shared(wchar)*)x.ptr)[0 .. x.length];
+ object._d_arrayappendTImpl!(shared(wchar)[])._d_arrayappendT(xx, cast(shared(wchar)[])appendthis);
+ x = (cast(byte*)xx.ptr)[0 .. xx.length];
+ return x;
}
/**
*
*/
-extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y)
+extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak
out (result)
{
auto tinext = unqualify(ti.next);
@@ -2142,8 +2212,9 @@ out (result)
size_t cap = GC.sizeOf(result.ptr);
assert(!cap || cap > result.length * sizeelem);
}
-body
+do
{
+ import core.stdc.string;
version (none)
{
/* Cannot use this optimization because:
@@ -2186,8 +2257,10 @@ body
/**
*
*/
-extern (C) void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs)
+extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak
{
+ import core.stdc.string;
+
size_t length;
auto tinext = unqualify(ti.next);
auto size = tinext.tsize; // array element size
@@ -2225,7 +2298,7 @@ extern (C) void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs)
* Allocate the array, rely on the caller to do the initialization of the array.
*/
extern (C)
-void* _d_arrayliteralTX(const TypeInfo ti, size_t length)
+void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak
{
auto tinext = unqualify(ti.next);
auto sizeelem = tinext.tsize; // array element size
@@ -2333,7 +2406,7 @@ unittest
}
// cannot define structs inside unit test block, or they become nested structs.
-version (unittest)
+version (CoreUnittest)
{
struct S1
{
@@ -2383,12 +2456,22 @@ unittest
// Bugzilla 3454 - Inconsistent flag setting in GC.realloc()
static void test(size_t multiplier)
{
- auto p = GC.malloc(8 * multiplier, BlkAttr.NO_SCAN);
+ auto p = GC.malloc(8 * multiplier, 0);
+ assert(GC.getAttr(p) == 0);
+
+ // no move, set attr
+ p = GC.realloc(p, 8 * multiplier + 5, BlkAttr.NO_SCAN);
assert(GC.getAttr(p) == BlkAttr.NO_SCAN);
- p = GC.realloc(p, 2 * multiplier, BlkAttr.NO_SCAN);
+
+ // shrink, copy attr
+ p = GC.realloc(p, 2 * multiplier, 0);
+ assert(GC.getAttr(p) == BlkAttr.NO_SCAN);
+
+ // extend, copy attr
+ p = GC.realloc(p, 8 * multiplier, 0);
assert(GC.getAttr(p) == BlkAttr.NO_SCAN);
}
- test(1);
+ test(16);
test(1024 * 1024);
}
@@ -2505,7 +2588,7 @@ unittest
// test struct finalizers
debug(SENTINEL) {} else
-unittest
+deprecated unittest
{
__gshared int dtorCount;
static struct S1
@@ -2520,12 +2603,12 @@ unittest
dtorCount = 0;
S1* s1 = new S1;
- delete s1;
+ _d_delstruct(cast(void**)&s1, typeid(typeof(*s1))); // delete s1;
assert(dtorCount == 1);
dtorCount = 0;
S1[] arr1 = new S1[7];
- delete arr1;
+ _d_delarray_t(cast(void[]*)&arr1, typeid(typeof(arr1[0]))); // delete arr1;
assert(dtorCount == 7);
dtorCount = 0;
@@ -2596,6 +2679,77 @@ unittest
assert(dtorCount == 4);
}
+// test struct dtor handling not causing false pointers
+unittest
+{
+ // for 64-bit, allocate a struct of size 40
+ static struct S
+ {
+ size_t[4] data;
+ S* ptr4;
+ }
+ auto p1 = new S;
+ auto p2 = new S;
+ p2.ptr4 = p1;
+
+ // a struct with a dtor with size 32, but the dtor will cause
+ // allocation to be larger by a pointer
+ static struct A
+ {
+ size_t[3] data;
+ S* ptr3;
+
+ ~this() {}
+ }
+
+ GC.free(p2);
+ auto a = new A; // reuse same memory
+ if (cast(void*)a is cast(void*)p2) // reusage not guaranteed
+ {
+ auto ptr = cast(S**)(a + 1);
+ assert(*ptr != p1); // still same data as p2.ptr4?
+ }
+
+ // small array
+ static struct SArr
+ {
+ void*[10] data;
+ }
+ auto arr1 = new SArr;
+ arr1.data[] = p1;
+ GC.free(arr1);
+
+ // allocates 2*A.sizeof + (void*).sizeof (TypeInfo) + 1 (array length)
+ auto arr2 = new A[2];
+ if (cast(void*)arr1 is cast(void*)arr2.ptr) // reusage not guaranteed
+ {
+ auto ptr = cast(S**)(arr2.ptr + 2);
+ assert(*ptr != p1); // still same data as p2.ptr4?
+ }
+
+ // large array
+ static struct LArr
+ {
+ void*[1023] data;
+ }
+ auto larr1 = new LArr;
+ larr1.data[] = p1;
+ GC.free(larr1);
+
+ auto larr2 = new S[255];
+ if (cast(void*)larr1 is cast(void*)larr2.ptr - LARGEPREFIX) // reusage not guaranteed
+ {
+ auto ptr = cast(S**)larr1;
+ assert(ptr[0] != p1); // 16 bytes array header
+ assert(ptr[1] != p1);
+ version (D_LP64) {} else
+ {
+ assert(ptr[2] != p1);
+ assert(ptr[3] != p1);
+ }
+ }
+}
+
// test class finalizers exception handling
unittest
{
diff --git a/libphobos/libdruntime/rt/memory.d b/libphobos/libdruntime/rt/memory.d
index 220b3d24e5a..99b00c0c551 100644
--- a/libphobos/libdruntime/rt/memory.d
+++ b/libphobos/libdruntime/rt/memory.d
@@ -7,7 +7,7 @@
* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
* (See accompanying file LICENSE)
* Authors: Walter Bright, Sean Kelly
- * Source: $(DRUNTIMESRC src/rt/_memory.d)
+ * Source: $(DRUNTIMESRC rt/_memory.d)
*/
module rt.memory;
diff --git a/libphobos/libdruntime/rt/minfo.d b/libphobos/libdruntime/rt/minfo.d
index 47228663562..0d5cd22b1aa 100644
--- a/libphobos/libdruntime/rt/minfo.d
+++ b/libphobos/libdruntime/rt/minfo.d
@@ -7,7 +7,7 @@
* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
* (See accompanying file LICENSE)
* Authors: Walter Bright, Sean Kelly
- * Source: $(DRUNTIMESRC src/rt/_minfo.d)
+ * Source: $(DRUNTIMESRC rt/_minfo.d)
*/
module rt.minfo;
@@ -165,7 +165,7 @@ struct ModuleGroup
void sortCtors(string cycleHandling)
{
import core.bitop : bts, btr, bt, BitRange;
- import rt.util.container.hashtab;
+ import core.internal.container.hashtab;
enum OnCycle
{
@@ -287,7 +287,7 @@ struct ModuleGroup
else
enum EOL = "\n";
- sink("Cyclic dependency between module ");
+ sink("Cyclic dependency between module constructors/destructors of ");
sink(_modules[sourceIdx].name);
sink(" and ");
sink(_modules[cycleIdx].name);
@@ -544,7 +544,7 @@ struct ModuleGroup
* behavior.
*
* Params:
- * edges - The module edges as found in the `importedModules` member of
+ * edges = The module edges as found in the `importedModules` member of
* each ModuleInfo. Generated in sortCtors.
* Returns:
* true if no cycle is found, false if one was.
@@ -566,7 +566,7 @@ struct ModuleGroup
}
auto stack = (cast(StackRec*).calloc(len, StackRec.sizeof))[0 .. len];
- // TODO: reuse GCBits by moving it to rt.util.container or core.internal
+ // TODO: reuse GCBits by moving it to core.internal.container
immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
auto ctorstart = cast(size_t*).malloc(nwords * size_t.sizeof);
auto ctordone = cast(size_t*).malloc(nwords * size_t.sizeof);
diff --git a/libphobos/libdruntime/rt/monitor_.d b/libphobos/libdruntime/rt/monitor_.d
index 8cb3c3a476e..6bfce635c72 100644
--- a/libphobos/libdruntime/rt/monitor_.d
+++ b/libphobos/libdruntime/rt/monitor_.d
@@ -2,8 +2,9 @@
* Contains the implementation for object monitors.
*
* Copyright: Copyright Digital Mars 2000 - 2015.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright, Sean Kelly, Martin Nowak
+ * Source: $(DRUNTIMESRC rt/_monitor_.d)
*/
/* NOTE: This file has been patched from the original DMD distribution to
@@ -25,7 +26,7 @@ in
{
assert(ownee.__monitor is null);
}
-body
+do
{
auto m = ensureMonitor(cast(Object) owner);
if (m.impl is null)
@@ -81,7 +82,7 @@ in
{
assert(h !is null, "Synchronized object must not be null.");
}
-body
+do
{
auto m = cast(Monitor*) ensureMonitor(h);
auto i = m.impl;
@@ -185,6 +186,7 @@ version (GNU)
version (SingleThreaded)
{
+@nogc:
alias Mutex = int;
void initMutex(Mutex* mtx)
@@ -262,7 +264,7 @@ struct Monitor
private:
-@property ref shared(Monitor*) monitor(Object h) pure nothrow @nogc
+@property ref shared(Monitor*) monitor(return Object h) pure nothrow @nogc
{
return *cast(shared Monitor**)&h.__monitor;
}
diff --git a/libphobos/libdruntime/rt/obj.d b/libphobos/libdruntime/rt/obj.d
deleted file mode 100644
index 97dfbb595b1..00000000000
--- a/libphobos/libdruntime/rt/obj.d
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Contains object comparator functions called by generated code.
- *
- * Copyright: Copyright Digital Mars 2002 - 2010.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright
- */
-
-/* Copyright Digital Mars 2000 - 2010.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module rt.obj;
-
-extern (C):
-
-/********************************
- * Compiler helper for operator == for class objects.
- */
-
-int _d_obj_eq(Object o1, Object o2)
-{
- return o1 is o2 || (o1 && o1.opEquals(o2));
-}
-
-
-/********************************
- * Compiler helper for operator <, <=, >, >= for class objects.
- */
-
-int _d_obj_cmp(Object o1, Object o2)
-{
- return o1.opCmp(o2);
-}
diff --git a/libphobos/libdruntime/rt/profilegc.d b/libphobos/libdruntime/rt/profilegc.d
new file mode 100644
index 00000000000..45e0d51b711
--- /dev/null
+++ b/libphobos/libdruntime/rt/profilegc.d
@@ -0,0 +1,170 @@
+/*
+ * Data collection and report generation for
+ * -profile=gc
+ * switch
+ *
+ * Copyright: Copyright Digital Mars 2015 - 2015.
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Andrei Alexandrescu and Walter Bright
+ * Source: $(DRUNTIMESRC rt/_profilegc.d)
+ */
+
+module rt.profilegc;
+
+private:
+
+import core.stdc.stdio;
+import core.stdc.stdlib;
+import core.stdc.string;
+
+import core.exception : onOutOfMemoryError;
+import core.internal.container.hashtab;
+
+struct Entry { ulong count, size; }
+
+char[] buffer;
+HashTab!(const(char)[], Entry) newCounts;
+
+__gshared
+{
+ HashTab!(const(char)[], Entry) globalNewCounts;
+ string logfilename = "profilegc.log";
+}
+
+/****
+ * Set file name for output.
+ * A file name of "" means write results to stdout.
+ * Params:
+ * name = file name
+ */
+
+extern (C) void profilegc_setlogfilename(string name)
+{
+ logfilename = name ~ "\0";
+}
+
+public void accumulate(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow
+{
+ if (sz == 0)
+ return;
+
+ char[3 * line.sizeof + 1] buf = void;
+ auto buflen = snprintf(buf.ptr, buf.length, "%u", line);
+
+ auto length = type.length + 1 + funcname.length + 1 + file.length + 1 + buflen;
+ if (length > buffer.length)
+ {
+ // Enlarge buffer[] so it is big enough
+ assert(buffer.length > 0 || buffer.ptr is null);
+ auto p = cast(char*)realloc(buffer.ptr, length);
+ if (!p)
+ onOutOfMemoryError();
+ buffer = p[0 .. length];
+ }
+
+ // "type funcname file:line"
+ buffer[0 .. type.length] = type[];
+ buffer[type.length] = ' ';
+ buffer[type.length + 1 ..
+ type.length + 1 + funcname.length] = funcname[];
+ buffer[type.length + 1 + funcname.length] = ' ';
+ buffer[type.length + 1 + funcname.length + 1 ..
+ type.length + 1 + funcname.length + 1 + file.length] = file[];
+ buffer[type.length + 1 + funcname.length + 1 + file.length] = ':';
+ buffer[type.length + 1 + funcname.length + 1 + file.length + 1 ..
+ type.length + 1 + funcname.length + 1 + file.length + 1 + buflen] = buf[0 .. buflen];
+
+ if (auto pcount = cast(string)buffer[0 .. length] in newCounts)
+ { // existing entry
+ pcount.count++;
+ pcount.size += sz;
+ }
+ else
+ {
+ auto key = (cast(char*) malloc(char.sizeof * length))[0 .. length];
+ key[] = buffer[0..length];
+ newCounts[key] = Entry(1, sz); // new entry
+ }
+}
+
+// Merge thread local newCounts into globalNewCounts
+static ~this()
+{
+ if (newCounts.length)
+ {
+ synchronized
+ {
+ foreach (name, entry; newCounts)
+ {
+ if (!(name in globalNewCounts))
+ globalNewCounts[name] = Entry.init;
+
+ globalNewCounts[name].count += entry.count;
+ globalNewCounts[name].size += entry.size;
+ }
+ }
+ newCounts.reset();
+ }
+ free(buffer.ptr);
+ buffer = null;
+}
+
+// Write report to stderr
+shared static ~this()
+{
+ static struct Result
+ {
+ const(char)[] name;
+ Entry entry;
+
+ // qsort() comparator to sort by count field
+ extern (C) static int qsort_cmp(scope const void *r1, scope const void *r2) @nogc nothrow
+ {
+ auto result1 = cast(Result*)r1;
+ auto result2 = cast(Result*)r2;
+ long cmp = result2.entry.size - result1.entry.size;
+ if (cmp) return cmp < 0 ? -1 : 1;
+ cmp = result2.entry.count - result1.entry.count;
+ if (cmp) return cmp < 0 ? -1 : 1;
+ if (result2.name == result1.name) return 0;
+ // ascending order for names reads better
+ return result2.name > result1.name ? -1 : 1;
+ }
+ }
+
+ size_t size = globalNewCounts.length;
+ Result[] counts = (cast(Result*) malloc(size * Result.sizeof))[0 .. size];
+ scope(exit)
+ free(counts.ptr);
+
+ size_t i;
+ foreach (name, entry; globalNewCounts)
+ {
+ counts[i].name = name;
+ counts[i].entry = entry;
+ ++i;
+ }
+
+ if (counts.length)
+ {
+ qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp);
+
+ FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename).ptr, "w");
+ if (fp)
+ {
+ fprintf(fp, "bytes allocated, allocations, type, function, file:line\n");
+ foreach (ref c; counts)
+ {
+ fprintf(fp, "%15llu\t%15llu\t%8.*s\n",
+ cast(ulong)c.entry.size, cast(ulong)c.entry.count,
+ cast(int) c.name.length, c.name.ptr);
+ }
+ if (logfilename.length)
+ fclose(fp);
+ }
+ else
+ fprintf(stderr, "cannot write profilegc log file '%.*s'", cast(int) logfilename.length, logfilename.ptr);
+ }
+}
diff --git a/libphobos/libdruntime/rt/qsort.d b/libphobos/libdruntime/rt/qsort.d
deleted file mode 100644
index 079a0f574b9..00000000000
--- a/libphobos/libdruntime/rt/qsort.d
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
- * This is a public domain version of qsort.d. All it does is call C's
- * qsort().
- *
- * Copyright: Copyright Digital Mars 2000 - 2010.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, Martin Nowak
- */
-
-/* Copyright Digital Mars 2000 - 2010.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE_1_0.txt or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module rt.qsort;
-
-//debug=qsort;
-
-private import core.stdc.stdlib;
-
-version (OSX)
- version = Darwin;
-else version (iOS)
- version = Darwin;
-else version (TVOS)
- version = Darwin;
-else version (WatchOS)
- version = Darwin;
-
-// qsort_r was added in glibc in 2.8. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88127
-version (CRuntime_Glibc)
-{
- version (GNU)
- {
- import gcc.config : Have_Qsort_R;
- enum Glibc_Qsort_R = Have_Qsort_R;
- }
- else
- {
- enum Glibc_Qsort_R = true;
- }
-}
-else
-{
- enum Glibc_Qsort_R = false;
-}
-
-static if (Glibc_Qsort_R)
-{
- alias extern (C) int function(scope const void *, scope const void *, scope void *) Cmp;
- extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, Cmp cmp, scope void *arg);
-
- extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
- {
- extern (C) int cmp(scope const void* p1, scope const void* p2, scope void* ti)
- {
- return (cast(TypeInfo)ti).compare(p1, p2);
- }
- qsort_r(a.ptr, a.length, ti.tsize, &cmp, cast(void*)ti);
- return a;
- }
-}
-else version (FreeBSD)
-{
- alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp;
- extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp);
-
- extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
- {
- extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2)
- {
- return (cast(TypeInfo)ti).compare(p1, p2);
- }
- qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp);
- return a;
- }
-}
-else version (DragonFlyBSD)
-{
- alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp;
- extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp);
-
- extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
- {
- extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2)
- {
- return (cast(TypeInfo)ti).compare(p1, p2);
- }
- qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp);
- return a;
- }
-}
-else version (Darwin)
-{
- alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp;
- extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp);
-
- extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
- {
- extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2)
- {
- return (cast(TypeInfo)ti).compare(p1, p2);
- }
- qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp);
- return a;
- }
-}
-else version (CRuntime_UClibc)
-{
- alias extern (C) int function(scope const void *, scope const void *, scope void *) __compar_d_fn_t;
- extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, __compar_d_fn_t cmp, scope void *arg);
-
- extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
- {
- extern (C) int cmp(scope const void* p1, scope const void* p2, scope void* ti)
- {
- return (cast(TypeInfo)ti).compare(p1, p2);
- }
- qsort_r(a.ptr, a.length, ti.tsize, &cmp, cast(void*)ti);
- return a;
- }
-}
-else
-{
- private TypeInfo tiglobal;
-
- extern (C) void[] _adSort(return scope void[] a, TypeInfo ti)
- {
- extern (C) int cmp(scope const void* p1, scope const void* p2)
- {
- return tiglobal.compare(p1, p2);
- }
- tiglobal = ti;
- qsort(a.ptr, a.length, ti.tsize, &cmp);
- return a;
- }
-}
-
-
-
-unittest
-{
- debug(qsort) printf("array.sort.unittest()\n");
-
- int[] a = new int[10];
-
- a[0] = 23;
- a[1] = 1;
- a[2] = 64;
- a[3] = 5;
- a[4] = 6;
- a[5] = 5;
- a[6] = 17;
- a[7] = 3;
- a[8] = 0;
- a[9] = -1;
-
- _adSort(*cast(void[]*)&a, typeid(a[0]));
-
- for (int i = 0; i < a.length - 1; i++)
- {
- //printf("i = %d", i);
- //printf(" %d %d\n", a[i], a[i + 1]);
- assert(a[i] <= a[i + 1]);
- }
-}
diff --git a/libphobos/libdruntime/rt/sections.d b/libphobos/libdruntime/rt/sections.d
index 6009a79abc5..006d48d97f5 100644
--- a/libphobos/libdruntime/rt/sections.d
+++ b/libphobos/libdruntime/rt/sections.d
@@ -5,7 +5,7 @@
* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
* (See accompanying file LICENSE)
* Authors: Walter Bright, Sean Kelly, Martin Nowak
- * Source: $(DRUNTIMESRC src/rt/_sections.d)
+ * Source: $(DRUNTIMESRC rt/_sections.d)
*/
/* NOTE: This file has been patched from the original DMD distribution to
@@ -26,10 +26,23 @@ version (GNU)
public import gcc.sections;
else version (CRuntime_Glibc)
public import rt.sections_elf_shared;
+else version (CRuntime_Musl)
+ public import rt.sections_elf_shared;
else version (FreeBSD)
public import rt.sections_elf_shared;
else version (NetBSD)
public import rt.sections_elf_shared;
+else version (OpenBSD)
+{
+ /**
+ * OpenBSD is missing support needed for elf_shared.
+ * See the top of sections_solaris.d for more info.
+ */
+
+ public import rt.sections_solaris;
+}
+else version (DragonFlyBSD)
+ public import rt.sections_elf_shared;
else version (Solaris)
public import rt.sections_solaris;
else version (Darwin)
@@ -47,6 +60,8 @@ else version (CRuntime_Microsoft)
public import rt.sections_win64;
else version (CRuntime_Bionic)
public import rt.sections_android;
+else version (CRuntime_UClibc)
+ public import rt.sections_elf_shared;
else
static assert(0, "unimplemented");
diff --git a/libphobos/libdruntime/rt/switch_.d b/libphobos/libdruntime/rt/switch_.d
deleted file mode 100644
index 73ad6365aa0..00000000000
--- a/libphobos/libdruntime/rt/switch_.d
+++ /dev/null
@@ -1,424 +0,0 @@
-/**
- * Contains support code for switch blocks using string constants.
- *
- * Copyright: Copyright Digital Mars 2004 - 2010.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, Sean Kelly
- */
-
-/* Copyright Digital Mars 2004 - 2010.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module rt.switch_;
-
-private import core.stdc.string;
-
-/******************************************************
- * Support for switch statements switching on strings.
- * Input:
- * table[] sorted array of strings generated by compiler
- * ca string to look up in table
- * Output:
- * result index of match in table[]
- * -1 if not in table
- */
-
-extern (C):
-
-int _d_switch_string(char[][] table, char[] ca)
-in
-{
- //printf("in _d_switch_string()\n");
- assert(table.length >= 0);
- assert(ca.length >= 0);
-
- // Make sure table[] is sorted correctly
- for (size_t j = 1u; j < table.length; j++)
- {
- auto len1 = table[j - 1].length;
- auto len2 = table[j].length;
-
- assert(len1 <= len2);
- if (len1 == len2)
- {
- int ci;
-
- ci = memcmp(table[j - 1].ptr, table[j].ptr, len1);
- assert(ci < 0); // ci==0 means a duplicate
- }
- }
-}
-out (result)
-{
- int cj;
-
- //printf("out _d_switch_string()\n");
- if (result == -1)
- {
- // Not found
- for (auto i = 0u; i < table.length; i++)
- {
- if (table[i].length == ca.length)
- { cj = memcmp(table[i].ptr, ca.ptr, ca.length);
- assert(cj != 0);
- }
- }
- }
- else
- {
- assert(0 <= result && cast(size_t)result < table.length);
- for (auto i = 0u; 1; i++)
- {
- assert(i < table.length);
- if (table[i].length == ca.length)
- {
- cj = memcmp(table[i].ptr, ca.ptr, ca.length);
- if (cj == 0)
- {
- assert(i == result);
- break;
- }
- }
- }
- }
-}
-body
-{
- //printf("body _d_switch_string(%.*s)\n", ca.length, ca.ptr);
- size_t low = 0;
- size_t high = table.length;
-
- version (none)
- {
- // Print table
- printf("ca[] = '%s'\n", ca.length, ca.ptr);
- for (auto i = 0; i < high; i++)
- {
- auto pca = table[i];
- printf("table[%d] = %d, '%.*s'\n", i, pca.length, pca.length, pca.ptr);
- }
- }
- if (high &&
- ca.length >= table[0].length &&
- ca.length <= table[high - 1].length)
- {
- // Looking for 0 length string, which would only be at the beginning
- if (ca.length == 0)
- return 0;
-
- char c1 = ca[0];
-
- // Do binary search
- while (low < high)
- {
- auto mid = (low + high) >> 1;
- auto pca = table[mid];
- auto c = cast(sizediff_t)(ca.length - pca.length);
- if (c == 0)
- {
- c = cast(ubyte)c1 - cast(ubyte)pca[0];
- if (c == 0)
- {
- c = memcmp(ca.ptr, pca.ptr, ca.length);
- if (c == 0)
- { //printf("found %d\n", mid);
- return cast(int)mid;
- }
- }
- }
- if (c < 0)
- {
- high = mid;
- }
- else
- {
- low = mid + 1;
- }
- }
- }
-
- //printf("not found\n");
- return -1; // not found
-}
-
-unittest
-{
- switch (cast(char []) "c")
- {
- case "coo":
- default:
- break;
- }
-
- int bug5381(string s)
- {
- switch (s)
- {
- case "unittest": return 1;
- case "D_Version2": return 2;
- case "none": return 3;
- case "all": return 4;
- default: return 5;
- }
- }
- int rc = bug5381("none");
- assert(rc == 3);
-}
-
-/**********************************
- * Same thing, but for wide chars.
- */
-
-int _d_switch_ustring(wchar[][] table, wchar[] ca)
-in
-{
- //printf("in _d_switch_ustring()\n");
- assert(table.length >= 0);
- assert(ca.length >= 0);
-
- // Make sure table[] is sorted correctly
- for (size_t j = 1u; j < table.length; j++)
- {
- auto len1 = table[j - 1].length;
- auto len2 = table[j].length;
-
- assert(len1 <= len2);
- if (len1 == len2)
- {
- int c;
-
- c = memcmp(table[j - 1].ptr, table[j].ptr, len1 * wchar.sizeof);
- assert(c < 0); // c==0 means a duplicate
- }
- }
-}
-out (result)
-{
- int c;
-
- //printf("out _d_switch_ustring()\n");
- if (result == -1)
- {
- // Not found
- for (auto i = 0u; i < table.length; i++)
- {
- if (table[i].length == ca.length)
- { c = memcmp(table[i].ptr, ca.ptr, ca.length * wchar.sizeof);
- assert(c != 0);
- }
- }
- }
- else
- {
- assert(0 <= result && cast(size_t)result < table.length);
- for (auto i = 0u; 1; i++)
- {
- assert(i < table.length);
- if (table[i].length == ca.length)
- {
- c = memcmp(table[i].ptr, ca.ptr, ca.length * wchar.sizeof);
- if (c == 0)
- {
- assert(i == result);
- break;
- }
- }
- }
- }
-}
-body
-{
- //printf("body _d_switch_ustring()\n");
- size_t low = 0;
- auto high = table.length;
-
- version (none)
- {
- // Print table
- wprintf("ca[] = '%.*s'\n", ca.length, ca.ptr);
- for (auto i = 0; i < high; i++)
- {
- auto pca = table[i];
- wprintf("table[%d] = %d, '%.*s'\n", i, pca.length, pca.length, pca.ptr);
- }
- }
-
- // Do binary search
- while (low < high)
- {
- auto mid = (low + high) >> 1;
- auto pca = table[mid];
- auto c = cast(sizediff_t)(ca.length - pca.length);
- if (c == 0)
- {
- c = memcmp(ca.ptr, pca.ptr, ca.length * wchar.sizeof);
- if (c == 0)
- { //printf("found %d\n", mid);
- return cast(int)mid;
- }
- }
- if (c < 0)
- {
- high = mid;
- }
- else
- {
- low = mid + 1;
- }
- }
- //printf("not found\n");
- return -1; // not found
-}
-
-
-unittest
-{
- switch (cast(wchar []) "c")
- {
- case "coo":
- default:
- break;
- }
-
- int bug5381(wstring ws)
- {
- switch (ws)
- {
- case "unittest": return 1;
- case "D_Version2": return 2;
- case "none": return 3;
- case "all": return 4;
- default: return 5;
- }
- }
- int rc = bug5381("none"w);
- assert(rc == 3);
-}
-
-/**********************************
- * Same thing, but for wide chars.
- */
-
-int _d_switch_dstring(dchar[][] table, dchar[] ca)
-in
-{
- //printf("in _d_switch_dstring()\n");
- assert(table.length >= 0);
- assert(ca.length >= 0);
-
- // Make sure table[] is sorted correctly
- for (auto j = 1u; j < table.length; j++)
- {
- auto len1 = table[j - 1].length;
- auto len2 = table[j].length;
-
- assert(len1 <= len2);
- if (len1 == len2)
- {
- auto c = memcmp(table[j - 1].ptr, table[j].ptr, len1 * dchar.sizeof);
- assert(c < 0); // c==0 means a duplicate
- }
- }
-}
-out (result)
-{
- //printf("out _d_switch_dstring()\n");
- if (result == -1)
- {
- // Not found
- for (auto i = 0u; i < table.length; i++)
- {
- if (table[i].length == ca.length)
- { auto c = memcmp(table[i].ptr, ca.ptr, ca.length * dchar.sizeof);
- assert(c != 0);
- }
- }
- }
- else
- {
- assert(0 <= result && cast(size_t)result < table.length);
- for (auto i = 0u; 1; i++)
- {
- assert(i < table.length);
- if (table[i].length == ca.length)
- {
- auto c = memcmp(table[i].ptr, ca.ptr, ca.length * dchar.sizeof);
- if (c == 0)
- {
- assert(i == result);
- break;
- }
- }
- }
- }
-}
-body
-{
- //printf("body _d_switch_dstring()\n");
- size_t low = 0;
- auto high = table.length;
-
- version (none)
- {
- // Print table
- wprintf("ca[] = '%.*s'\n", ca.length, ca.ptr);
- for (auto i = 0; i < high; i++)
- {
- auto pca = table[i];
- wprintf("table[%d] = %d, '%.*s'\n", i, pca.length, pca.length, pca.ptr);
- }
- }
-
- // Do binary search
- while (low < high)
- {
- auto mid = (low + high) >> 1;
- auto pca = table[mid];
- auto c = cast(sizediff_t)(ca.length - pca.length);
- if (c == 0)
- {
- c = memcmp(ca.ptr, pca.ptr, ca.length * dchar.sizeof);
- if (c == 0)
- { //printf("found %d\n", mid);
- return cast(int)mid;
- }
- }
- if (c < 0)
- {
- high = mid;
- }
- else
- {
- low = mid + 1;
- }
- }
- //printf("not found\n");
- return -1; // not found
-}
-
-
-unittest
-{
- switch (cast(dchar []) "c")
- {
- case "coo":
- default:
- break;
- }
-
- int bug5381(dstring ds)
- {
- switch (ds)
- {
- case "unittest": return 1;
- case "D_Version2": return 2;
- case "none": return 3;
- case "all": return 4;
- default: return 5;
- }
- }
- int rc = bug5381("none"d);
- assert(rc == 3);
-}
diff --git a/libphobos/libdruntime/rt/tlsgc.d b/libphobos/libdruntime/rt/tlsgc.d
index db7347fbe15..b13a1b319cf 100644
--- a/libphobos/libdruntime/rt/tlsgc.d
+++ b/libphobos/libdruntime/rt/tlsgc.d
@@ -1,8 +1,9 @@
/**
*
* Copyright: Copyright Digital Mars 2011 - 2012.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Martin Nowak
+ * Source: $(DRUNTIMESRC rt/tlsgc.d)
*/
/* Copyright Digital Mars 2011.
diff --git a/libphobos/libdruntime/rt/util/array.d b/libphobos/libdruntime/rt/util/array.d
deleted file mode 100644
index b2cfb8ddd08..00000000000
--- a/libphobos/libdruntime/rt/util/array.d
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
-Array utilities.
-
-Copyright: Denis Shelomovskij 2013
-License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors: Denis Shelomovskij
-Source: $(DRUNTIMESRC src/rt/util/_array.d)
-*/
-module rt.util.array;
-
-
-import core.internal.string;
-import core.stdc.stdint;
-
-
-@safe /* pure dmd @@@BUG11461@@@ */ nothrow:
-
-void enforceTypedArraysConformable(T)(const char[] action,
- const T[] a1, const T[] a2, in bool allowOverlap = false)
-{
- _enforceSameLength(action, a1.length, a2.length);
- if (!allowOverlap)
- _enforceNoOverlap(action, arrayToPtr(a1), arrayToPtr(a2), T.sizeof * a1.length);
-}
-
-void enforceRawArraysConformable(const char[] action, in size_t elementSize,
- const void[] a1, const void[] a2, in bool allowOverlap = false)
-{
- _enforceSameLength(action, a1.length, a2.length);
- if (!allowOverlap)
- _enforceNoOverlap(action, arrayToPtr(a1), arrayToPtr(a2), elementSize * a1.length);
-}
-
-private void _enforceSameLength(const char[] action,
- in size_t length1, in size_t length2)
-{
- if (length1 == length2)
- return;
-
- UnsignedStringBuf tmpBuff = void;
- string msg = "Array lengths don't match for ";
- msg ~= action;
- msg ~= ": ";
- msg ~= length1.unsignedToTempString(tmpBuff, 10);
- msg ~= " != ";
- msg ~= length2.unsignedToTempString(tmpBuff, 10);
- throw new Error(msg);
-}
-
-private void _enforceNoOverlap(const char[] action,
- uintptr_t ptr1, uintptr_t ptr2, in size_t bytes)
-{
- const d = ptr1 > ptr2 ? ptr1 - ptr2 : ptr2 - ptr1;
- if (d >= bytes)
- return;
- const overlappedBytes = bytes - d;
-
- UnsignedStringBuf tmpBuff = void;
- string msg = "Overlapping arrays in ";
- msg ~= action;
- msg ~= ": ";
- msg ~= overlappedBytes.unsignedToTempString(tmpBuff, 10);
- msg ~= " byte(s) overlap of ";
- msg ~= bytes.unsignedToTempString(tmpBuff, 10);
- throw new Error(msg);
-}
-
-private uintptr_t arrayToPtr(const void[] array) @trusted
-{
- // Ok because the user will never dereference the pointer
- return cast(uintptr_t)array.ptr;
-}
diff --git a/libphobos/libdruntime/rt/util/container/array.d b/libphobos/libdruntime/rt/util/container/array.d
deleted file mode 100644
index f5aa3d7531e..00000000000
--- a/libphobos/libdruntime/rt/util/container/array.d
+++ /dev/null
@@ -1,232 +0,0 @@
-/**
- * Array container for internal usage.
- *
- * Copyright: Copyright Martin Nowak 2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Martin Nowak
- */
-module rt.util.container.array;
-
-static import common = rt.util.container.common;
-
-import core.exception : onOutOfMemoryErrorNoGC;
-
-struct Array(T)
-{
-nothrow:
- @disable this(this);
-
- ~this()
- {
- reset();
- }
-
- void reset()
- {
- length = 0;
- }
-
- @property size_t length() const
- {
- return _length;
- }
-
- @property void length(size_t nlength)
- {
- import core.checkedint : mulu;
-
- bool overflow = false;
- size_t reqsize = mulu(T.sizeof, nlength, overflow);
- if (!overflow)
- {
- if (nlength < _length)
- foreach (ref val; _ptr[nlength .. _length]) common.destroy(val);
- _ptr = cast(T*)common.xrealloc(_ptr, reqsize);
- if (nlength > _length)
- foreach (ref val; _ptr[_length .. nlength]) common.initialize(val);
- _length = nlength;
- }
- else
- onOutOfMemoryErrorNoGC();
-
- }
-
- @property bool empty() const
- {
- return !length;
- }
-
- @property ref inout(T) front() inout
- in { assert(!empty); }
- body
- {
- return _ptr[0];
- }
-
- @property ref inout(T) back() inout
- in { assert(!empty); }
- body
- {
- return _ptr[_length - 1];
- }
-
- ref inout(T) opIndex(size_t idx) inout
- in { assert(idx < length); }
- body
- {
- return _ptr[idx];
- }
-
- inout(T)[] opSlice() inout
- {
- return _ptr[0 .. _length];
- }
-
- inout(T)[] opSlice(size_t a, size_t b) inout
- in { assert(a < b && b <= length); }
- body
- {
- return _ptr[a .. b];
- }
-
- alias length opDollar;
-
- void insertBack()(auto ref T val)
- {
- import core.checkedint : addu;
-
- bool overflow = false;
- size_t newlength = addu(length, 1, overflow);
- if (!overflow)
- {
- length = newlength;
- back = val;
- }
- else
- onOutOfMemoryErrorNoGC();
- }
-
- void popBack()
- {
- length = length - 1;
- }
-
- void remove(size_t idx)
- in { assert(idx < length); }
- body
- {
- foreach (i; idx .. length - 1)
- _ptr[i] = _ptr[i+1];
- popBack();
- }
-
- void swap(ref Array other)
- {
- auto ptr = _ptr;
- _ptr = other._ptr;
- other._ptr = ptr;
- immutable len = _length;
- _length = other._length;
- other._length = len;
- }
-
- invariant
- {
- assert(!_ptr == !_length);
- }
-
-private:
- T* _ptr;
- size_t _length;
-}
-
-unittest
-{
- Array!size_t ary;
-
- assert(ary[] == []);
- ary.insertBack(5);
- assert(ary[] == [5]);
- assert(ary[$-1] == 5);
- ary.popBack();
- assert(ary[] == []);
- ary.insertBack(0);
- ary.insertBack(1);
- assert(ary[] == [0, 1]);
- assert(ary[0 .. 1] == [0]);
- assert(ary[1 .. 2] == [1]);
- assert(ary[$ - 2 .. $] == [0, 1]);
- size_t idx;
- foreach (val; ary) assert(idx++ == val);
- foreach_reverse (val; ary) assert(--idx == val);
- foreach (i, val; ary) assert(i == val);
- foreach_reverse (i, val; ary) assert(i == val);
-
- ary.insertBack(2);
- ary.remove(1);
- assert(ary[] == [0, 2]);
-
- assert(!ary.empty);
- ary.reset();
- assert(ary.empty);
- ary.insertBack(0);
- assert(!ary.empty);
- destroy(ary);
- assert(ary.empty);
-
- // not copyable
- static assert(!__traits(compiles, { Array!size_t ary2 = ary; }));
- Array!size_t ary2;
- static assert(!__traits(compiles, ary = ary2));
- static void foo(Array!size_t copy) {}
- static assert(!__traits(compiles, foo(ary)));
-
- ary2.insertBack(0);
- assert(ary.empty);
- assert(ary2[] == [0]);
- ary.swap(ary2);
- assert(ary[] == [0]);
- assert(ary2.empty);
-}
-
-unittest
-{
- alias RC = common.RC!();
- Array!RC ary;
-
- size_t cnt;
- assert(cnt == 0);
- ary.insertBack(RC(&cnt));
- assert(cnt == 1);
- ary.insertBack(RC(&cnt));
- assert(cnt == 2);
- ary.back = ary.front;
- assert(cnt == 2);
- ary.popBack();
- assert(cnt == 1);
- ary.popBack();
- assert(cnt == 0);
-}
-
-unittest
-{
- import core.exception;
- try
- {
- // Overflow ary.length.
- auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -1);
- ary.insertBack(0);
- }
- catch (OutOfMemoryError)
- {
- }
- try
- {
- // Overflow requested memory size for common.xrealloc().
- auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -2);
- ary.insertBack(0);
- }
- catch (OutOfMemoryError)
- {
- }
-}
diff --git a/libphobos/libdruntime/rt/util/container/common.d b/libphobos/libdruntime/rt/util/container/common.d
deleted file mode 100644
index 9e6c0138a4f..00000000000
--- a/libphobos/libdruntime/rt/util/container/common.d
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Common code for writing containers.
- *
- * Copyright: Copyright Martin Nowak 2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Martin Nowak
- */
-module rt.util.container.common;
-
-import core.stdc.stdlib : malloc, realloc;
-public import core.stdc.stdlib : free;
-import core.internal.traits : dtorIsNothrow;
-nothrow:
-
-void* xrealloc(void* ptr, size_t sz) nothrow @nogc
-{
- import core.exception;
-
- if (!sz) { .free(ptr); return null; }
- if (auto nptr = .realloc(ptr, sz)) return nptr;
- .free(ptr); onOutOfMemoryErrorNoGC();
- assert(0);
-}
-
-void* xmalloc(size_t sz) nothrow @nogc
-{
- import core.exception;
- if (auto nptr = .malloc(sz))
- return nptr;
- onOutOfMemoryErrorNoGC();
- assert(0);
-}
-
-void destroy(T)(ref T t) if (is(T == struct) && dtorIsNothrow!T)
-{
- scope (failure) assert(0); // nothrow hack
- object.destroy(t);
-}
-
-void destroy(T)(ref T t) if (!is(T == struct))
-{
- t = T.init;
-}
-
-void initialize(T)(ref T t) if (is(T == struct))
-{
- import core.stdc.string;
- if (auto p = typeid(T).initializer().ptr)
- memcpy(&t, p, T.sizeof);
- else
- memset(&t, 0, T.sizeof);
-}
-
-void initialize(T)(ref T t) if (!is(T == struct))
-{
- t = T.init;
-}
-
-version (unittest) struct RC()
-{
-nothrow:
- this(size_t* cnt) { ++*(_cnt = cnt); }
- ~this() { if (_cnt) --*_cnt; }
- this(this) { if (_cnt) ++*_cnt; }
- size_t* _cnt;
-}
diff --git a/libphobos/libdruntime/rt/util/container/hashtab.d b/libphobos/libdruntime/rt/util/container/hashtab.d
deleted file mode 100644
index fd9f0f7ac8e..00000000000
--- a/libphobos/libdruntime/rt/util/container/hashtab.d
+++ /dev/null
@@ -1,329 +0,0 @@
-/**
- * HashTab container for internal usage.
- *
- * Copyright: Copyright Martin Nowak 2013.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Martin Nowak
- */
-module rt.util.container.hashtab;
-
-import rt.util.container.array;
-static import common = rt.util.container.common;
-
-struct HashTab(Key, Value)
-{
- static struct Node
- {
- Key _key;
- Value _value;
- Node* _next;
- }
-
- @disable this(this);
-
- ~this()
- {
- reset();
- }
-
- void reset()
- {
- foreach (p; _buckets)
- {
- while (p !is null)
- {
- auto pn = p._next;
- common.destroy(*p);
- common.free(p);
- p = pn;
- }
- }
- _buckets.reset();
- _length = 0;
- }
-
- @property size_t length() const
- {
- return _length;
- }
-
- @property bool empty() const
- {
- return !_length;
- }
-
- void remove(in Key key)
- in { assert(key in this); }
- body
- {
- ensureNotInOpApply();
-
- immutable hash = hashOf(key) & mask;
- auto pp = &_buckets[hash];
- while (*pp)
- {
- auto p = *pp;
- if (p._key == key)
- {
- *pp = p._next;
- common.destroy(*p);
- common.free(p);
- if (--_length < _buckets.length && _length >= 4)
- shrink();
- return;
- }
- else
- {
- pp = &p._next;
- }
- }
- assert(0);
- }
-
- ref inout(Value) opIndex(Key key) inout
- {
- return *opIn_r(key);
- }
-
- void opIndexAssign(Value value, Key key)
- {
- *get(key) = value;
- }
-
- inout(Value)* opIn_r(in Key key) inout
- {
- if (_buckets.length)
- {
- immutable hash = hashOf(key) & mask;
- for (inout(Node)* p = _buckets[hash]; p !is null; p = p._next)
- {
- if (p._key == key)
- return &p._value;
- }
- }
- return null;
- }
-
- int opApply(scope int delegate(ref Key, ref Value) dg)
- {
- immutable save = _inOpApply;
- _inOpApply = true;
- scope (exit) _inOpApply = save;
- foreach (p; _buckets)
- {
- while (p !is null)
- {
- if (auto res = dg(p._key, p._value))
- return res;
- p = p._next;
- }
- }
- return 0;
- }
-
-private:
-
- Value* get(Key key)
- {
- if (auto p = opIn_r(key))
- return p;
-
- ensureNotInOpApply();
-
- if (!_buckets.length)
- _buckets.length = 4;
-
- immutable hash = hashOf(key) & mask;
- auto p = cast(Node*)common.xmalloc(Node.sizeof);
- common.initialize(*p);
- p._key = key;
- p._next = _buckets[hash];
- _buckets[hash] = p;
- if (++_length >= 2 * _buckets.length)
- grow();
- return &p._value;
- }
-
- static hash_t hashOf(in ref Key key) @trusted
- {
- static if (is(Key U : U[]))
- return .hashOf(key, 0);
- else
- return .hashOf((&key)[0 .. 1], 0);
- }
-
- @property hash_t mask() const
- {
- return _buckets.length - 1;
- }
-
- void grow()
- in
- {
- assert(_buckets.length);
- }
- body
- {
- immutable ocnt = _buckets.length;
- immutable nmask = 2 * ocnt - 1;
- _buckets.length = 2 * ocnt;
- for (size_t i = 0; i < ocnt; ++i)
- {
- auto pp = &_buckets[i];
- while (*pp)
- {
- auto p = *pp;
-
- immutable nidx = hashOf(p._key) & nmask;
- if (nidx != i)
- {
- *pp = p._next;
- p._next = _buckets[nidx];
- _buckets[nidx] = p;
- }
- else
- {
- pp = &p._next;
- }
- }
- }
- }
-
- void shrink()
- in
- {
- assert(_buckets.length >= 2);
- }
- body
- {
- immutable ocnt = _buckets.length;
- immutable ncnt = ocnt >> 1;
- immutable nmask = ncnt - 1;
-
- for (size_t i = ncnt; i < ocnt; ++i)
- {
- if (auto tail = _buckets[i])
- {
- immutable nidx = i & nmask;
- auto pp = &_buckets[nidx];
- while (*pp)
- pp = &(*pp)._next;
- *pp = tail;
- _buckets[i] = null;
- }
- }
- _buckets.length = ncnt;
- }
-
- void ensureNotInOpApply()
- {
- if (_inOpApply)
- assert(0, "Invalid HashTab manipulation during opApply iteration.");
- }
-
- Array!(Node*) _buckets;
- size_t _length;
- bool _inOpApply;
-}
-
-unittest
-{
- HashTab!(int, int) tab;
-
- foreach (i; 0 .. 100)
- tab[i] = 100 - i;
-
- foreach (i; 0 .. 100)
- assert(tab[i] == 100 - i);
-
- foreach (k, v; tab)
- assert(v == 100 - k);
-
- foreach (i; 0 .. 50)
- tab.remove(2 * i);
-
- assert(tab.length == 50);
-
- foreach (i; 0 .. 50)
- assert(tab[2 * i + 1] == 100 - 2 * i - 1);
-
- assert(tab.length == 50);
-
- tab.reset();
- assert(tab.empty);
- tab[0] = 0;
- assert(!tab.empty);
- destroy(tab);
- assert(tab.empty);
-
- // not copyable
- static assert(!__traits(compiles, { HashTab!(int, int) tab2 = tab; }));
- HashTab!(int, int) tab2;
- static assert(!__traits(compiles, tab = tab2));
- static void foo(HashTab!(int, int) copy) {}
- static assert(!__traits(compiles, foo(tab)));
-}
-
-unittest
-{
- HashTab!(string, size_t) tab;
-
- tab["foo"] = 0;
- assert(tab["foo"] == 0);
- ++tab["foo"];
- assert(tab["foo"] == 1);
- tab["foo"]++;
- assert(tab["foo"] == 2);
-
- auto s = "fo";
- s ~= "o";
- assert(tab[s] == 2);
- assert(tab.length == 1);
- tab[s] -= 2;
- assert(tab[s] == 0);
- tab["foo"] = 12;
- assert(tab[s] == 12);
-
- tab.remove("foo");
- assert(tab.empty);
-}
-
-unittest
-{
- alias RC = common.RC!();
- HashTab!(size_t, RC) tab;
-
- size_t cnt;
- assert(cnt == 0);
- tab[0] = RC(&cnt);
- assert(cnt == 1);
- tab[1] = tab[0];
- assert(cnt == 2);
- tab.remove(0);
- assert(cnt == 1);
- tab.remove(1);
- assert(cnt == 0);
-}
-
-unittest
-{
- import core.exception;
-
- HashTab!(uint, uint) tab;
- foreach (i; 0 .. 5)
- tab[i] = i;
- bool thrown;
- foreach (k, v; tab)
- {
- try
- {
- if (k == 3) tab.remove(k);
- }
- catch (AssertError e)
- {
- thrown = true;
- }
- }
- assert(thrown);
- assert(tab[3] == 3);
-}
diff --git a/libphobos/libdruntime/rt/util/container/treap.d b/libphobos/libdruntime/rt/util/container/treap.d
deleted file mode 100644
index f0c04fdc359..00000000000
--- a/libphobos/libdruntime/rt/util/container/treap.d
+++ /dev/null
@@ -1,338 +0,0 @@
-/**
- * Treap container for internal usage.
- *
- * Copyright: Copyright Digital Mars 2014 - 2014.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- */
-module rt.util.container.treap;
-
-static import common = rt.util.container.common;
-import rt.util.random;
-import rt.qsort;
-
-struct Treap(E)
-{
-nothrow:
- static struct Node
- {
- Node* left, right;
- E element;
- uint priority;
- }
-
- @disable this(this);
-
- ~this()
- {
- removeAll();
- }
-
- void initialize()
- {
- rand48.defaultSeed();
- }
-
- void insert(E element) @nogc
- {
- root = insert(root, element);
- }
-
- void remove(E element)
- {
- remove(&root, element);
- }
-
- int opApply(scope int delegate(ref E) nothrow dg)
- {
- return (cast(const)&this).opApply((ref const E e) => dg(*cast(E*)&e));
- }
-
- int opApply(scope int delegate(ref const E) nothrow dg) const
- {
- return opApplyHelper(root, dg);
- }
-
- version (unittest)
- bool opEquals(E[] elements)
- {
- size_t i;
- foreach (e; this)
- {
- if (i >= elements.length)
- return false;
- if (e != elements[i++])
- return false;
- }
- return i == elements.length;
- }
-
- void removeAll()
- {
- removeAll(root);
- root = null;
- }
-
- version (unittest)
- bool valid()
- {
- return valid(root);
- }
-
-
- version (none)
- uint height()
- {
- static uint height(Node* node)
- {
- if (!node)
- return 0;
- auto left = height(node.left);
- auto right = height(node.right);
- return 1 + (left > right ? left : right);
- }
- return height(root);
- }
-
- version (none)
- size_t count()
- {
- static size_t count(Node* node)
- {
- if (!node)
- return 0;
- return count(node.left) + count(node.right) + 1;
- }
- return count(root);
- }
-
-
-private:
- Node* root;
- Rand48 rand48;
-
- Node* allocNode(E element) @nogc
- {
- Node* node = cast(Node*)common.xmalloc(Node.sizeof);
- node.left = node.right = null;
- node.priority = rand48();
- node.element = element;
- return node;
- }
-
- Node* insert(Node* node, E element) @nogc
- {
- if (!node)
- return allocNode(element);
- else if (element < node.element)
- {
- node.left = insert(node.left, element);
- if (node.left.priority < node.priority)
- node = rotateR(node);
- }
- else if (element > node.element)
- {
- node.right = insert(node.right, element);
- if (node.right.priority < node.priority)
- node = rotateL(node);
- }
- else
- {} // ignore duplicate
-
- return node;
- }
-
-static:
-
- void freeNode(Node* node)
- {
- common.free(node);
- }
-
- Node* rotateL(Node* root)
- {
- auto pivot = root.right;
- root.right = pivot.left;
- pivot.left = root;
- return pivot;
- }
-
- Node* rotateR(Node* root)
- {
- auto pivot = root.left;
- root.left = pivot.right;
- pivot.right = root;
- return pivot;
- }
-
- void remove(Node** ppnode, E element)
- {
- Node* node = *ppnode;
- if (!node)
- return; // element not in treap
-
- if (element < node.element)
- {
- remove(&node.left, element);
- }
- else if (element > node.element)
- {
- remove(&node.right, element);
- }
- else
- {
- while (node.left && node.right)
- {
- if (node.left.priority < node.right.priority)
- {
- *ppnode = rotateR(node);
- ppnode = &(*ppnode).right;
- }
- else
- {
- *ppnode = rotateL(node);
- ppnode = &(*ppnode).left;
- }
- }
- if (!node.left)
- *ppnode = node.right;
- else
- *ppnode = node.left;
- freeNode(node);
- }
- }
-
- void removeAll(Node* node)
- {
- if (!node)
- return;
- removeAll(node.left);
- removeAll(node.right);
- freeNode(node);
- }
-
- int opApplyHelper(const Node* node, scope int delegate(ref const E) nothrow dg)
- {
- if (!node)
- return 0;
-
- int result = opApplyHelper(node.left, dg);
- if (result)
- return result;
- result = dg(node.element);
- if (result)
- return result;
- return opApplyHelper(node.right, dg);
- }
-
- version (unittest)
- bool valid(Node* node)
- {
- if (!node)
- return true;
-
- if (node.left)
- {
- if (node.left.priority < node.priority)
- return false;
- if (node.left.element > node.element)
- return false;
- }
- if (node.right)
- {
- if (node.right.priority < node.priority)
- return false;
- if (node.right.element < node.element)
- return false;
- }
- return valid(node.left) && valid(node.right);
- }
-}
-
-unittest
-{
- // randomized unittest for randomized data structure
- import /*cstdlib = */core.stdc.stdlib : rand, srand;
- import /*ctime = */core.stdc.time : time;
-
- enum OP { add, remove }
- enum initialSize = 1000;
- enum randOps = 1000;
-
- Treap!uint treap;
- OP[] ops;
- uint[] opdata;
-
- treap.initialize();
- srand(cast(uint)time(null));
-
- uint[] data;
-initialLoop:
- foreach (i; 0 .. initialSize)
- {
- data ~= rand();
- treap.insert(data[$-1]);
- foreach (e; data[0..$-1])
- if (e == data[$-1])
- {
- data = data[0..$-1];
- continue initialLoop;
- }
- }
- _adSort(*cast(void[]*)&data, typeid(data[0]));
- assert(treap == data);
- assert(treap.valid());
-
- for (int i = randOps; i > 0; --i)
- {
- ops ~= cast(OP)(rand() < uint.max / 2 ? OP.add: OP.remove);
- opdata ~= rand();
- }
-
- foreach (op; ops)
- {
- if (op == OP.add)
- {
- treap.insert(opdata[0]);
-
- size_t i;
- for (i = 0; i < data.length; ++i)
- if (data[i] >= opdata[0])
- break;
-
- if (i == data.length || data[i] != opdata[0])
- { // not a duplicate
- data.length++;
- uint tmp = opdata[0];
- for (; i < data.length; ++i)
- {
- uint tmp2 = data[i];
- data[i] = tmp;
- tmp = tmp2;
- }
- }
- }
- else if (!data.length) // nothing to remove
- {
- opdata = opdata[1..$];
- continue;
- }
- else
- {
- uint tmp = data[opdata[0]%data.length];
- treap.remove(tmp);
- size_t i;
- for (i = 0; data[i] < tmp; ++i)
- {}
- for (; i < data.length-1; ++i)
- data[i] = data[i+1];
- data.length--;
- }
- assert(treap.valid());
- assert(treap == data);
- opdata = opdata[1..$];
- }
-
- treap.removeAll();
- data.length = 0;
- assert(treap == data);
-}
diff --git a/libphobos/libdruntime/rt/util/random.d b/libphobos/libdruntime/rt/util/random.d
deleted file mode 100644
index 69e4cfe2ee5..00000000000
--- a/libphobos/libdruntime/rt/util/random.d
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * Random number generators for internal usage.
- *
- * Copyright: Copyright Digital Mars 2014.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- */
-module rt.util.random;
-
-struct Rand48
-{
- private ulong rng_state;
-
-@safe @nogc nothrow:
-
- void defaultSeed()
- {
- import ctime = core.stdc.time : time;
- seed(cast(uint)ctime.time(null));
- }
-
-pure:
-
- void seed(uint seedval)
- {
- assert(seedval);
- rng_state = cast(ulong)seedval << 16 | 0x330e;
- popFront();
- }
-
- auto opCall()
- {
- auto result = front;
- popFront();
- return result;
- }
-
- @property uint front()
- {
- return cast(uint)(rng_state >> 16);
- }
-
- void popFront()
- {
- immutable ulong a = 25214903917;
- immutable ulong c = 11;
- immutable ulong m_mask = (1uL << 48uL) - 1;
- rng_state = (a*rng_state+c) & m_mask;
- }
-
- enum empty = false;
-}
diff --git a/libphobos/libdruntime/rt/util/typeinfo.d b/libphobos/libdruntime/rt/util/typeinfo.d
index 31770a01946..d06254c092d 100644
--- a/libphobos/libdruntime/rt/util/typeinfo.d
+++ b/libphobos/libdruntime/rt/util/typeinfo.d
@@ -4,8 +4,10 @@
* Copyright: Copyright Kenji Hara 2014-.
* License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
* Authors: Kenji Hara
+ * Source: $(DRUNTIMESRC rt/util/_typeinfo.d)
*/
module rt.util.typeinfo;
+import rt.util.utility : d_cfloat, d_cdouble, d_creal, isComplex;
static import core.internal.hash;
template Floating(T)
@@ -35,14 +37,16 @@ if (is(T == float) || is(T == double) || is(T == real))
public alias hashOf = core.internal.hash.hashOf;
}
+
+// @@@DEPRECATED_2.105@@@
template Floating(T)
-if (is(T == cfloat) || is(T == cdouble) || is(T == creal))
+if (isComplex!T)
{
pure nothrow @safe:
bool equals(T f1, T f2)
{
- return f1 == f2;
+ return f1.re == f2.re && f1.im == f2.im;
}
int compare(T f1, T f2)
@@ -62,12 +66,14 @@ if (is(T == cfloat) || is(T == cdouble) || is(T == creal))
return result;
}
- public alias hashOf = core.internal.hash.hashOf;
+ size_t hashOf(scope const T val)
+ {
+ return core.internal.hash.hashOf(val.re, core.internal.hash.hashOf(val.im));
+ }
}
template Array(T)
-if (is(T == float) || is(T == double) || is(T == real) ||
- is(T == cfloat) || is(T == cdouble) || is(T == creal))
+if (is(T == float) || is(T == double) || is(T == real))
{
pure nothrow @safe:
@@ -94,17 +100,56 @@ if (is(T == float) || is(T == double) || is(T == real) ||
if (int c = Floating!T.compare(s1[u], s2[u]))
return c;
}
- if (s1.length < s2.length)
- return -1;
- else if (s1.length > s2.length)
- return 1;
- return 0;
+ return (s1.length > s2.length) - (s1.length < s2.length);
}
public alias hashOf = core.internal.hash.hashOf;
}
-version (unittest)
+// @@@DEPRECATED_2.105@@@
+template Array(T)
+if (isComplex!T)
+{
+ pure nothrow @safe:
+
+ bool equals(T[] s1, T[] s2)
+ {
+ size_t len = s1.length;
+ if (len != s2.length)
+ return false;
+ for (size_t u = 0; u < len; u++)
+ {
+ if (!Floating!T.equals(s1[u], s2[u]))
+ return false;
+ }
+ return true;
+ }
+
+ int compare(T[] s1, T[] s2)
+ {
+ size_t len = s1.length;
+ if (s2.length < len)
+ len = s2.length;
+ for (size_t u = 0; u < len; u++)
+ {
+ if (int c = Floating!T.compare(s1[u], s2[u]))
+ return c;
+ }
+ return (s1.length > s2.length) - (s1.length < s2.length);
+ }
+
+ size_t hashOf(scope const T[] val)
+ {
+ size_t hash = 0;
+ foreach (ref o; val)
+ {
+ hash = core.internal.hash.hashOf(Floating!T.hashOf(o), hash);
+ }
+ return hash;
+ }
+}
+
+version (CoreUnittest)
{
alias TypeTuple(T...) = T;
}
@@ -162,109 +207,6 @@ unittest
ti = typeid(S[3]);
assert(ti.getHash(&sa1) == ti.getHash(&sa2));
}();
-
- // imaginary types
- foreach (F; TypeTuple!(ifloat, idouble, ireal))
- (){ // workaround #2396
- alias S = SX!F;
- F f1 = +0.0i,
- f2 = -0.0i;
-
- assert(f1 == f2);
- assert(f1 !is f2);
- ti = typeid(F);
- assert(ti.getHash(&f1) == ti.getHash(&f2));
-
- F[] a1 = [f1, f1, f1];
- F[] a2 = [f2, f2, f2];
- assert(a1 == a2);
- assert(a1 !is a2);
- ti = typeid(F[]);
- assert(ti.getHash(&a1) == ti.getHash(&a2));
-
- F[][] aa1 = [a1, a1, a1];
- F[][] aa2 = [a2, a2, a2];
- assert(aa1 == aa2);
- assert(aa1 !is aa2);
- ti = typeid(F[][]);
- assert(ti.getHash(&aa1) == ti.getHash(&aa2));
-
- S s1 = {f1},
- s2 = {f2};
- assert(s1 == s2);
- assert(s1 !is s2);
- ti = typeid(S);
- assert(ti.getHash(&s1) == ti.getHash(&s2));
-
- S[] da1 = [S(f1), S(f1), S(f1)],
- da2 = [S(f2), S(f2), S(f2)];
- assert(da1 == da2);
- assert(da1 !is da2);
- ti = typeid(S[]);
- assert(ti.getHash(&da1) == ti.getHash(&da2));
-
- S[3] sa1 = {f1},
- sa2 = {f2};
- assert(sa1 == sa2);
- assert(sa1[] !is sa2[]);
- ti = typeid(S[3]);
- assert(ti.getHash(&sa1) == ti.getHash(&sa2));
- }();
-
- // complex types
- foreach (F; TypeTuple!(cfloat, cdouble, creal))
- (){ // workaround #2396
- alias S = SX!F;
- F[4] f = [+0.0 + 0.0i,
- +0.0 - 0.0i,
- -0.0 + 0.0i,
- -0.0 - 0.0i];
-
- foreach (i, f1; f) foreach (j, f2; f) if (i != j)
- {
- assert(f1 == 0 + 0i);
-
- assert(f1 == f2);
- assert(f1 !is f2);
- ti = typeid(F);
- assert(ti.getHash(&f1) == ti.getHash(&f2));
-
- F[] a1 = [f1, f1, f1];
- F[] a2 = [f2, f2, f2];
- assert(a1 == a2);
- assert(a1 !is a2);
- ti = typeid(F[]);
- assert(ti.getHash(&a1) == ti.getHash(&a2));
-
- F[][] aa1 = [a1, a1, a1];
- F[][] aa2 = [a2, a2, a2];
- assert(aa1 == aa2);
- assert(aa1 !is aa2);
- ti = typeid(F[][]);
- assert(ti.getHash(&aa1) == ti.getHash(&aa2));
-
- S s1 = {f1},
- s2 = {f2};
- assert(s1 == s2);
- assert(s1 !is s2);
- ti = typeid(S);
- assert(ti.getHash(&s1) == ti.getHash(&s2));
-
- S[] da1 = [S(f1), S(f1), S(f1)],
- da2 = [S(f2), S(f2), S(f2)];
- assert(da1 == da2);
- assert(da1 !is da2);
- ti = typeid(S[]);
- assert(ti.getHash(&da1) == ti.getHash(&da2));
-
- S[3] sa1 = {f1},
- sa2 = {f2};
- assert(sa1 == sa2);
- assert(sa1[] !is sa2[]);
- ti = typeid(S[3]);
- assert(ti.getHash(&sa1) == ti.getHash(&sa2));
- }
- }();
}
// Reduces to `T` if `cond` is `true` or `U` otherwise.
@@ -279,7 +221,7 @@ TypeInfo information for built-in types.
A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
-`float` and `ifloat` or `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
+`char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
during compilation whether they have different signedness and override appropriately. For initializer, we
detect if we need to override. The overriding initializer should be nonzero.
@@ -296,7 +238,7 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
static if (is(T == Base))
override size_t getHash(scope const void* p)
{
- static if (__traits(isFloating, T))
+ static if (__traits(isFloating, T) || isComplex!T)
return Floating!T.hashOf(*cast(T*)p);
else
return hashOf(*cast(const T *)p);
@@ -306,7 +248,7 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
static if (is(T == Base))
override bool equals(in void* p1, in void* p2)
{
- static if (__traits(isFloating, T))
+ static if (__traits(isFloating, T) || isComplex!T)
return Floating!T.equals(*cast(T*)p1, *cast(T*)p2);
else
return *cast(T *)p1 == *cast(T *)p2;
@@ -316,7 +258,7 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
override int compare(in void* p1, in void* p2)
{
- static if (__traits(isFloating, T))
+ static if (__traits(isFloating, T) || isComplex!T)
{
return Floating!T.compare(*cast(T*)p1, *cast(T*)p2);
}
@@ -375,9 +317,12 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
}
static if (is(T == Base))
- static if (__traits(isFloating, T) && T.mant_dig != 64)
+ {
+ static if ((__traits(isFloating, T) && T.mant_dig != 64) ||
+ (isComplex!T && T.re.mant_dig != 64))
// FP types except 80-bit X87 are passed in SIMD register.
override @property uint flags() const { return 2; }
+ }
}
unittest
@@ -414,7 +359,7 @@ TypeInfo information for arrays of built-in types.
A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
-`float` and `ifloat` or `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
+`char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
during compilation whether they have different signedness and override appropriately. For initializer, we
detect if we need to override. The overriding initializer should be nonzero.
@@ -429,7 +374,7 @@ private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInf
static if (is(T == Base))
override size_t getHash(scope const void* p) @trusted const
{
- static if (__traits(isFloating, T))
+ static if (__traits(isFloating, T) || isComplex!T)
return Array!T.hashOf(*cast(T[]*)p);
else
return hashOf(*cast(const T[]*) p);
@@ -438,7 +383,7 @@ private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInf
static if (is(T == Base))
override bool equals(in void* p1, in void* p2) const
{
- static if (__traits(isFloating, T))
+ static if (__traits(isFloating, T) || isComplex!T)
{
return Array!T.equals(*cast(T[]*)p1, *cast(T[]*)p2);
}
@@ -455,7 +400,7 @@ private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInf
static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
override int compare(in void* p1, in void* p2) const
{
- static if (__traits(isFloating, T))
+ static if (__traits(isFloating, T) || isComplex!T)
{
return Array!T.compare(*cast(T[]*)p1, *cast(T[]*)p2);
}
@@ -519,12 +464,12 @@ class TypeInfo_v : TypeInfoGeneric!ubyte
{
return 1;
}
+}
- unittest
- {
- assert(typeid(void).toString == "void");
- assert(typeid(void).flags == 1);
- }
+unittest
+{
+ assert(typeid(void).toString == "void");
+ assert(typeid(void).flags == 1);
}
// All integrals.
@@ -545,17 +490,36 @@ static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {}
// All simple floating-point types.
class TypeInfo_f : TypeInfoGeneric!float {}
-class TypeInfo_o : TypeInfoGeneric!(ifloat, float) {}
class TypeInfo_d : TypeInfoGeneric!double {}
-class TypeInfo_p : TypeInfoGeneric!(idouble, double) {}
class TypeInfo_e : TypeInfoGeneric!real {}
-class TypeInfo_j : TypeInfoGeneric!(ireal, real) {}
+
+// All imaginary floating-point types.
+
+// ifloat @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_o : TypeInfoGeneric!float
+{
+ override string toString() const pure nothrow @safe { return "ifloat"; }
+}
+
+// idouble @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_p : TypeInfoGeneric!double
+{
+ override string toString() const pure nothrow @safe { return "idouble"; }
+}
+
+// ireal @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_j : TypeInfoGeneric!real
+{
+ override string toString() const pure nothrow @safe { return "ireal"; }
+}
// All complex floating-point types.
-// cfloat
-class TypeInfo_q : TypeInfoGeneric!cfloat
+// cfloat @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_q : TypeInfoGeneric!d_cfloat
{
+ override string toString() const pure nothrow @safe { return "cfloat"; }
+
const: nothrow: pure: @trusted:
static if (__traits(hasMember, TypeInfo, "argTypes"))
override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
@@ -565,9 +529,11 @@ class TypeInfo_q : TypeInfoGeneric!cfloat
}
}
-// cdouble
-class TypeInfo_r : TypeInfoGeneric!cdouble
+// cdouble @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_r : TypeInfoGeneric!d_cdouble
{
+ override string toString() const pure nothrow @safe { return "cdouble"; }
+
const: nothrow: pure: @trusted:
static if (__traits(hasMember, TypeInfo, "argTypes"))
override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
@@ -578,9 +544,11 @@ class TypeInfo_r : TypeInfoGeneric!cdouble
}
}
-// creal
-class TypeInfo_c : TypeInfoGeneric!creal
+// creal @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_c : TypeInfoGeneric!d_creal
{
+ override string toString() const pure nothrow @safe { return "creal"; }
+
const: nothrow: pure: @trusted:
static if (__traits(hasMember, TypeInfo, "argTypes"))
override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
@@ -591,18 +559,6 @@ class TypeInfo_c : TypeInfoGeneric!creal
}
}
-static if (__traits(hasMember, TypeInfo, "argTypes"))
- unittest
- {
- TypeInfo t1, t2;
- assert(typeid(cfloat).argTypes(t1, t2) == 0 && t1 == typeid(double) &&
- t2 is null);
- assert(typeid(cdouble).argTypes(t1, t2) == 0 && t1 == typeid(double) &&
- t2 == typeid(double));
- assert(typeid(creal).argTypes(t1, t2) == 0 && t1 == typeid(real) &&
- t2 == typeid(real));
- }
-
// Arrays of all integrals.
class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {}
class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {}
@@ -623,7 +579,7 @@ class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {}
class TypeInfo_Am : TypeInfoArrayGeneric!ulong {}
class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {}
-version (unittest)
+version (CoreUnittest)
private extern (C) void[] _adSort(void[] a, TypeInfo ti);
unittest
@@ -662,16 +618,50 @@ unittest
assert(!(a1 < b1 && b1 < a1)); // Original failing case
}
-// Arrays of all floating point types.
+// Arrays of all simple floating-point types.
class TypeInfo_Af : TypeInfoArrayGeneric!float {}
-class TypeInfo_Ao : TypeInfoArrayGeneric!(ifloat, float) {}
class TypeInfo_Ad : TypeInfoArrayGeneric!double {}
-class TypeInfo_Ap : TypeInfoArrayGeneric!(idouble, double) {}
class TypeInfo_Ae : TypeInfoArrayGeneric!real {}
-class TypeInfo_Aj : TypeInfoArrayGeneric!(ireal, real) {}
-class TypeInfo_Aq : TypeInfoArrayGeneric!cfloat {}
-class TypeInfo_Ar : TypeInfoArrayGeneric!cdouble {}
-class TypeInfo_Ac : TypeInfoArrayGeneric!creal {}
+
+// Arrays of all imaginary floating-point types.
+
+// ifloat @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_Ao : TypeInfoArrayGeneric!float
+{
+ override string toString() const pure nothrow @safe { return "ifloat[]"; }
+}
+
+// idouble @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_Ap : TypeInfoArrayGeneric!double
+{
+ override string toString() const pure nothrow @safe { return "idouble[]"; }
+}
+
+// ireal @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_Aj : TypeInfoArrayGeneric!real
+{
+ override string toString() const pure nothrow @safe { return "ireal[]"; }
+}
+
+// Arrays of all complex floating-point types.
+
+// cfloat @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_Aq : TypeInfoArrayGeneric!d_cfloat
+{
+ override string toString() const pure nothrow @safe { return "cfloat[]"; }
+}
+
+// cdouble @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_Ar : TypeInfoArrayGeneric!d_cdouble
+{
+ override string toString() const pure nothrow @safe { return "cdouble[]"; }
+}
+
+// creal @@@DEPRECATED_2.105@@@
+deprecated class TypeInfo_Ac : TypeInfoArrayGeneric!d_creal
+{
+ override string toString() const pure nothrow @safe { return "creal[]"; }
+}
// void[] is a bit different, behaves like ubyte[] for comparison purposes.
class TypeInfo_Av : TypeInfo_Ah
diff --git a/libphobos/libdruntime/rt/util/utf.d b/libphobos/libdruntime/rt/util/utf.d
deleted file mode 100644
index 55869b37c8c..00000000000
--- a/libphobos/libdruntime/rt/util/utf.d
+++ /dev/null
@@ -1,920 +0,0 @@
-/********************************************
- * Encode and decode UTF-8, UTF-16 and UTF-32 strings.
- *
- * For Win32 systems, the C wchar_t type is UTF-16 and corresponds to the D
- * wchar type.
- * For Posix systems, the C wchar_t type is UTF-32 and corresponds to
- * the D utf.dchar type.
- *
- * UTF character support is restricted to (\u0000 &lt;= character &lt;= \U0010FFFF).
- *
- * See_Also:
- * $(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)<br>
- * $(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)<br>
- * $(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335)
- *
- * Copyright: Copyright Digital Mars 2003 - 2016.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, Sean Kelly
- * Source: $(DRUNTIMESRC src/rt/util/_utf.d)
- */
-
-module rt.util.utf;
-
-extern (C) void onUnicodeError( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__ ) @safe pure;
-
-/*******************************
- * Test if c is a valid UTF-32 character.
- *
- * \uFFFE and \uFFFF are considered valid by this function,
- * as they are permitted for internal use by an application,
- * but they are not allowed for interchange by the Unicode standard.
- *
- * Returns: true if it is, false if not.
- */
-
-@safe @nogc pure nothrow
-bool isValidDchar(dchar c)
-{
- /* Note: FFFE and FFFF are specifically permitted by the
- * Unicode standard for application internal use, but are not
- * allowed for interchange.
- * (thanks to Arcane Jill)
- */
-
- return c < 0xD800 ||
- (c > 0xDFFF && c <= 0x10FFFF /*&& c != 0xFFFE && c != 0xFFFF*/);
-}
-
-unittest
-{
- debug(utf) printf("utf.isValidDchar.unittest\n");
- assert(isValidDchar(cast(dchar)'a') == true);
- assert(isValidDchar(cast(dchar)0x1FFFFF) == false);
-}
-
-
-
-static immutable UTF8stride =
-[
- cast(ubyte)
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
- 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0xFF,0xFF,
-];
-
-/**
- * stride() returns the length of a UTF-8 sequence starting at index i
- * in string s.
- * Returns:
- * The number of bytes in the UTF-8 sequence or
- * 0xFF meaning s[i] is not the start of of UTF-8 sequence.
- */
-@safe @nogc pure nothrow
-uint stride(in char[] s, size_t i)
-{
- return UTF8stride[s[i]];
-}
-
-/**
- * stride() returns the length of a UTF-16 sequence starting at index i
- * in string s.
- */
-@safe @nogc pure nothrow
-uint stride(in wchar[] s, size_t i)
-{ uint u = s[i];
- return 1 + (u >= 0xD800 && u <= 0xDBFF);
-}
-
-/**
- * stride() returns the length of a UTF-32 sequence starting at index i
- * in string s.
- * Returns: The return value will always be 1.
- */
-@safe @nogc pure nothrow
-uint stride(in dchar[] s, size_t i)
-{
- return 1;
-}
-
-/*******************************************
- * Given an index i into an array of characters s[],
- * and assuming that index i is at the start of a UTF character,
- * determine the number of UCS characters up to that index i.
- */
-@safe pure
-size_t toUCSindex(in char[] s, size_t i)
-{
- size_t n;
- size_t j;
-
- for (j = 0; j < i; )
- {
- j += stride(s, j);
- n++;
- }
- if (j > i)
- {
- onUnicodeError("invalid UTF-8 sequence", j);
- }
- return n;
-}
-
-/** ditto */
-@safe pure
-size_t toUCSindex(in wchar[] s, size_t i)
-{
- size_t n;
- size_t j;
-
- for (j = 0; j < i; )
- {
- j += stride(s, j);
- n++;
- }
- if (j > i)
- {
- onUnicodeError("invalid UTF-16 sequence", j);
- }
- return n;
-}
-
-/** ditto */
-@safe @nogc pure nothrow
-size_t toUCSindex(in dchar[] s, size_t i)
-{
- return i;
-}
-
-/******************************************
- * Given a UCS index n into an array of characters s[], return the UTF index.
- */
-@safe pure
-size_t toUTFindex(in char[] s, size_t n)
-{
- size_t i;
-
- while (n--)
- {
- uint j = UTF8stride[s[i]];
- if (j == 0xFF)
- onUnicodeError("invalid UTF-8 sequence", i);
- i += j;
- }
- return i;
-}
-
-/** ditto */
-@safe @nogc pure nothrow
-size_t toUTFindex(in wchar[] s, size_t n)
-{
- size_t i;
-
- while (n--)
- { wchar u = s[i];
-
- i += 1 + (u >= 0xD800 && u <= 0xDBFF);
- }
- return i;
-}
-
-/** ditto */
-@safe @nogc pure nothrow
-size_t toUTFindex(in dchar[] s, size_t n)
-{
- return n;
-}
-
-/* =================== Decode ======================= */
-
-/***************
- * Decodes and returns character starting at s[idx]. idx is advanced past the
- * decoded character. If the character is not well formed, a UtfException is
- * thrown and idx remains unchanged.
- */
-@safe pure
-dchar decode(in char[] s, ref size_t idx)
- in
- {
- assert(idx >= 0 && idx < s.length);
- }
- out (result)
- {
- assert(isValidDchar(result));
- }
- body
- {
- size_t len = s.length;
- dchar V;
- size_t i = idx;
- char u = s[i];
-
- if (u & 0x80)
- { uint n;
- char u2;
-
- /* The following encodings are valid, except for the 5 and 6 byte
- * combinations:
- * 0xxxxxxx
- * 110xxxxx 10xxxxxx
- * 1110xxxx 10xxxxxx 10xxxxxx
- * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- */
- for (n = 1; ; n++)
- {
- if (n > 4)
- goto Lerr; // only do the first 4 of 6 encodings
- if (((u << n) & 0x80) == 0)
- {
- if (n == 1)
- goto Lerr;
- break;
- }
- }
-
- // Pick off (7 - n) significant bits of B from first byte of octet
- V = cast(dchar)(u & ((1 << (7 - n)) - 1));
-
- if (i + (n - 1) >= len)
- goto Lerr; // off end of string
-
- /* The following combinations are overlong, and illegal:
- * 1100000x (10xxxxxx)
- * 11100000 100xxxxx (10xxxxxx)
- * 11110000 1000xxxx (10xxxxxx 10xxxxxx)
- * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx)
- * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)
- */
- u2 = s[i + 1];
- if ((u & 0xFE) == 0xC0 ||
- (u == 0xE0 && (u2 & 0xE0) == 0x80) ||
- (u == 0xF0 && (u2 & 0xF0) == 0x80) ||
- (u == 0xF8 && (u2 & 0xF8) == 0x80) ||
- (u == 0xFC && (u2 & 0xFC) == 0x80))
- goto Lerr; // overlong combination
-
- for (uint j = 1; j != n; j++)
- {
- u = s[i + j];
- if ((u & 0xC0) != 0x80)
- goto Lerr; // trailing bytes are 10xxxxxx
- V = (V << 6) | (u & 0x3F);
- }
- if (!isValidDchar(V))
- goto Lerr;
- i += n;
- }
- else
- {
- V = cast(dchar) u;
- i++;
- }
-
- idx = i;
- return V;
-
- Lerr:
- onUnicodeError("invalid UTF-8 sequence", i);
- return V; // dummy return
- }
-
-unittest
-{ size_t i;
- dchar c;
-
- debug(utf) printf("utf.decode.unittest\n");
-
- static s1 = "abcd"c;
- i = 0;
- c = decode(s1, i);
- assert(c == cast(dchar)'a');
- assert(i == 1);
- c = decode(s1, i);
- assert(c == cast(dchar)'b');
- assert(i == 2);
-
- static s2 = "\xC2\xA9"c;
- i = 0;
- c = decode(s2, i);
- assert(c == cast(dchar)'\u00A9');
- assert(i == 2);
-
- static s3 = "\xE2\x89\xA0"c;
- i = 0;
- c = decode(s3, i);
- assert(c == cast(dchar)'\u2260');
- assert(i == 3);
-
- static s4 =
- [ "\xE2\x89"c[], // too short
- "\xC0\x8A",
- "\xE0\x80\x8A",
- "\xF0\x80\x80\x8A",
- "\xF8\x80\x80\x80\x8A",
- "\xFC\x80\x80\x80\x80\x8A",
- ];
-
- for (int j = 0; j < s4.length; j++)
- {
- try
- {
- i = 0;
- c = decode(s4[j], i);
- assert(0);
- }
- catch (Throwable o)
- {
- i = 23;
- }
- assert(i == 23);
- }
-}
-
-/** ditto */
-@safe pure
-dchar decode(in wchar[] s, ref size_t idx)
- in
- {
- assert(idx >= 0 && idx < s.length);
- }
- out (result)
- {
- assert(isValidDchar(result));
- }
- body
- {
- string msg;
- dchar V;
- size_t i = idx;
- uint u = s[i];
-
- if (u & ~0x7F)
- { if (u >= 0xD800 && u <= 0xDBFF)
- { uint u2;
-
- if (i + 1 == s.length)
- { msg = "surrogate UTF-16 high value past end of string";
- goto Lerr;
- }
- u2 = s[i + 1];
- if (u2 < 0xDC00 || u2 > 0xDFFF)
- { msg = "surrogate UTF-16 low value out of range";
- goto Lerr;
- }
- u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00);
- i += 2;
- }
- else if (u >= 0xDC00 && u <= 0xDFFF)
- { msg = "unpaired surrogate UTF-16 value";
- goto Lerr;
- }
- else if (u == 0xFFFE || u == 0xFFFF)
- { msg = "illegal UTF-16 value";
- goto Lerr;
- }
- else
- i++;
- }
- else
- {
- i++;
- }
-
- idx = i;
- return cast(dchar)u;
-
- Lerr:
- onUnicodeError(msg, i);
- return cast(dchar)u; // dummy return
- }
-
-/** ditto */
-@safe pure
-dchar decode(in dchar[] s, ref size_t idx)
- in
- {
- assert(idx >= 0 && idx < s.length);
- }
- body
- {
- size_t i = idx;
- dchar c = s[i];
-
- if (!isValidDchar(c))
- goto Lerr;
- idx = i + 1;
- return c;
-
- Lerr:
- onUnicodeError("invalid UTF-32 value", i);
- return c; // dummy return
- }
-
-
-/* =================== Encode ======================= */
-
-/*******************************
- * Encodes character c and appends it to array s[].
- */
-@safe pure nothrow
-void encode(ref char[] s, dchar c)
- in
- {
- assert(isValidDchar(c));
- }
- body
- {
- char[] r = s;
-
- if (c <= 0x7F)
- {
- r ~= cast(char) c;
- }
- else
- {
- char[4] buf;
- uint L;
-
- if (c <= 0x7FF)
- {
- buf[0] = cast(char)(0xC0 | (c >> 6));
- buf[1] = cast(char)(0x80 | (c & 0x3F));
- L = 2;
- }
- else if (c <= 0xFFFF)
- {
- buf[0] = cast(char)(0xE0 | (c >> 12));
- buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
- buf[2] = cast(char)(0x80 | (c & 0x3F));
- L = 3;
- }
- else if (c <= 0x10FFFF)
- {
- buf[0] = cast(char)(0xF0 | (c >> 18));
- buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
- buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
- buf[3] = cast(char)(0x80 | (c & 0x3F));
- L = 4;
- }
- else
- {
- assert(0);
- }
- r ~= buf[0 .. L];
- }
- s = r;
- }
-
-unittest
-{
- debug(utf) printf("utf.encode.unittest\n");
-
- char[] s = "abcd".dup;
- encode(s, cast(dchar)'a');
- assert(s.length == 5);
- assert(s == "abcda");
-
- encode(s, cast(dchar)'\u00A9');
- assert(s.length == 7);
- assert(s == "abcda\xC2\xA9");
- //assert(s == "abcda\u00A9"); // BUG: fix compiler
-
- encode(s, cast(dchar)'\u2260');
- assert(s.length == 10);
- assert(s == "abcda\xC2\xA9\xE2\x89\xA0");
-}
-
-/** ditto */
-@safe pure nothrow
-void encode(ref wchar[] s, dchar c)
- in
- {
- assert(isValidDchar(c));
- }
- body
- {
- wchar[] r = s;
-
- if (c <= 0xFFFF)
- {
- r ~= cast(wchar) c;
- }
- else
- {
- wchar[2] buf;
-
- buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
- buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
- r ~= buf;
- }
- s = r;
- }
-
-/** ditto */
-@safe pure nothrow
-void encode(ref dchar[] s, dchar c)
- in
- {
- assert(isValidDchar(c));
- }
- body
- {
- s ~= c;
- }
-
-/**
-Returns the code length of $(D c) in the encoding using $(D C) as a
-code point. The code is returned in character count, not in bytes.
- */
-@safe pure nothrow @nogc
-ubyte codeLength(C)(dchar c)
-{
- static if (C.sizeof == 1)
- {
- if (c <= 0x7F) return 1;
- if (c <= 0x7FF) return 2;
- if (c <= 0xFFFF) return 3;
- if (c <= 0x10FFFF) return 4;
- assert(false);
- }
- else static if (C.sizeof == 2)
- {
- return c <= 0xFFFF ? 1 : 2;
- }
- else
- {
- static assert(C.sizeof == 4);
- return 1;
- }
-}
-
-/* =================== Validation ======================= */
-
-/***********************************
-Checks to see if string is well formed or not. $(D S) can be an array
- of $(D char), $(D wchar), or $(D dchar). Throws a $(D UtfException)
- if it is not. Use to check all untrusted input for correctness.
- */
-@safe pure
-void validate(S)(in S s)
-{
- auto len = s.length;
- for (size_t i = 0; i < len; )
- {
- decode(s, i);
- }
-}
-
-/* =================== Conversion to UTF8 ======================= */
-
-@safe pure nothrow @nogc
-char[] toUTF8(char[] buf, dchar c)
- in
- {
- assert(isValidDchar(c));
- }
- body
- {
- if (c <= 0x7F)
- {
- buf[0] = cast(char) c;
- return buf[0 .. 1];
- }
- else if (c <= 0x7FF)
- {
- buf[0] = cast(char)(0xC0 | (c >> 6));
- buf[1] = cast(char)(0x80 | (c & 0x3F));
- return buf[0 .. 2];
- }
- else if (c <= 0xFFFF)
- {
- buf[0] = cast(char)(0xE0 | (c >> 12));
- buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
- buf[2] = cast(char)(0x80 | (c & 0x3F));
- return buf[0 .. 3];
- }
- else if (c <= 0x10FFFF)
- {
- buf[0] = cast(char)(0xF0 | (c >> 18));
- buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
- buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
- buf[3] = cast(char)(0x80 | (c & 0x3F));
- return buf[0 .. 4];
- }
- assert(0);
- }
-
-/*******************
- * Encodes string s into UTF-8 and returns the encoded string.
- */
-@safe pure nothrow
-string toUTF8(string s)
- in
- {
- validate(s);
- }
- body
- {
- return s;
- }
-
-/** ditto */
-@trusted pure
-string toUTF8(in wchar[] s)
-{
- char[] r;
- size_t i;
- size_t slen = s.length;
-
- r.length = slen;
-
- for (i = 0; i < slen; i++)
- { wchar c = s[i];
-
- if (c <= 0x7F)
- r[i] = cast(char)c; // fast path for ascii
- else
- {
- r.length = i;
- foreach (dchar ch; s[i .. slen])
- {
- encode(r, ch);
- }
- break;
- }
- }
- return cast(string)r;
-}
-
-/** ditto */
-@trusted pure
-string toUTF8(in dchar[] s)
-{
- char[] r;
- size_t i;
- size_t slen = s.length;
-
- r.length = slen;
-
- for (i = 0; i < slen; i++)
- { dchar c = s[i];
-
- if (c <= 0x7F)
- r[i] = cast(char)c; // fast path for ascii
- else
- {
- r.length = i;
- foreach (dchar d; s[i .. slen])
- {
- encode(r, d);
- }
- break;
- }
- }
- return cast(string)r;
-}
-
-/* =================== Conversion to UTF16 ======================= */
-
-@safe pure nothrow @nogc
-wchar[] toUTF16(wchar[] buf, dchar c)
- in
- {
- assert(isValidDchar(c));
- }
- body
- {
- if (c <= 0xFFFF)
- {
- buf[0] = cast(wchar) c;
- return buf[0 .. 1];
- }
- else
- {
- buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
- buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
- return buf[0 .. 2];
- }
- }
-
-/****************
- * Encodes string s into UTF-16 and returns the encoded string.
- * toUTF16z() is suitable for calling the 'W' functions in the Win32 API that take
- * an LPWSTR or LPCWSTR argument.
- */
-@trusted pure
-wstring toUTF16(in char[] s)
-{
- wchar[] r;
- size_t slen = s.length;
-
- r.length = slen;
- r.length = 0;
- for (size_t i = 0; i < slen; )
- {
- dchar c = s[i];
- if (c <= 0x7F)
- {
- i++;
- r ~= cast(wchar)c;
- }
- else
- {
- c = decode(s, i);
- encode(r, c);
- }
- }
- return cast(wstring)r;
-}
-
-alias const(wchar)* wptr;
-/** ditto */
-@safe pure
-wptr toUTF16z(in char[] s)
-{
- wchar[] r;
- size_t slen = s.length;
-
- r.length = slen + 1;
- r.length = 0;
- for (size_t i = 0; i < slen; )
- {
- dchar c = s[i];
- if (c <= 0x7F)
- {
- i++;
- r ~= cast(wchar)c;
- }
- else
- {
- c = decode(s, i);
- encode(r, c);
- }
- }
- r ~= '\000';
- return &r[0];
-}
-
-/** ditto */
-@safe pure nothrow
-wstring toUTF16(wstring s)
- in
- {
- validate(s);
- }
- body
- {
- return s;
- }
-
-/** ditto */
-@trusted pure nothrow
-wstring toUTF16(in dchar[] s)
-{
- wchar[] r;
- size_t slen = s.length;
-
- r.length = slen;
- r.length = 0;
- for (size_t i = 0; i < slen; i++)
- {
- encode(r, s[i]);
- }
- return cast(wstring)r;
-}
-
-/* =================== Conversion to UTF32 ======================= */
-
-/*****
- * Encodes string s into UTF-32 and returns the encoded string.
- */
-@trusted pure
-dstring toUTF32(in char[] s)
-{
- dchar[] r;
- size_t slen = s.length;
- size_t j = 0;
-
- r.length = slen; // r[] will never be longer than s[]
- for (size_t i = 0; i < slen; )
- {
- dchar c = s[i];
- if (c >= 0x80)
- c = decode(s, i);
- else
- i++; // c is ascii, no need for decode
- r[j++] = c;
- }
- return cast(dstring)r[0 .. j];
-}
-
-/** ditto */
-@trusted pure
-dstring toUTF32(in wchar[] s)
-{
- dchar[] r;
- size_t slen = s.length;
- size_t j = 0;
-
- r.length = slen; // r[] will never be longer than s[]
- for (size_t i = 0; i < slen; )
- {
- dchar c = s[i];
- if (c >= 0x80)
- c = decode(s, i);
- else
- i++; // c is ascii, no need for decode
- r[j++] = c;
- }
- return cast(dstring)r[0 .. j];
-}
-
-/** ditto */
-@safe pure nothrow
-dstring toUTF32(dstring s)
- in
- {
- validate(s);
- }
- body
- {
- return s;
- }
-
-/* ================================ tests ================================== */
-
-unittest
-{
- debug(utf) printf("utf.toUTF.unittest\n");
-
- auto c = "hello"c[];
- auto w = toUTF16(c);
- assert(w == "hello");
- auto d = toUTF32(c);
- assert(d == "hello");
-
- c = toUTF8(w);
- assert(c == "hello");
- d = toUTF32(w);
- assert(d == "hello");
-
- c = toUTF8(d);
- assert(c == "hello");
- w = toUTF16(d);
- assert(w == "hello");
-
-
- c = "hel\u1234o";
- w = toUTF16(c);
- assert(w == "hel\u1234o");
- d = toUTF32(c);
- assert(d == "hel\u1234o");
-
- c = toUTF8(w);
- assert(c == "hel\u1234o");
- d = toUTF32(w);
- assert(d == "hel\u1234o");
-
- c = toUTF8(d);
- assert(c == "hel\u1234o");
- w = toUTF16(d);
- assert(w == "hel\u1234o");
-
-
- c = "he\U000BAAAAllo";
- w = toUTF16(c);
- //foreach (wchar c; w) printf("c = x%x\n", c);
- //foreach (wchar c; cast(wstring)"he\U000BAAAAllo") printf("c = x%x\n", c);
- assert(w == "he\U000BAAAAllo");
- d = toUTF32(c);
- assert(d == "he\U000BAAAAllo");
-
- c = toUTF8(w);
- assert(c == "he\U000BAAAAllo");
- d = toUTF32(w);
- assert(d == "he\U000BAAAAllo");
-
- c = toUTF8(d);
- assert(c == "he\U000BAAAAllo");
- w = toUTF16(d);
- assert(w == "he\U000BAAAAllo");
-
- wchar[2] buf;
- auto ret = toUTF16(buf, '\U000BAAAA');
- assert(ret == "\U000BAAAA");
-}
diff --git a/libphobos/libdruntime/rt/util/utility.d b/libphobos/libdruntime/rt/util/utility.d
new file mode 100644
index 00000000000..b1796fddaf7
--- /dev/null
+++ b/libphobos/libdruntime/rt/util/utility.d
@@ -0,0 +1,44 @@
+/**
+ * Contains various utility functions used by the runtime implementation.
+ *
+ * Copyright: Copyright Digital Mars 2016.
+ * License: Distributed under the
+ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
+ * (See accompanying file LICENSE)
+ * Authors: Jacob Carlborg
+ * Source: $(DRUNTIMESRC rt/util/_utility.d)
+ */
+module rt.util.utility;
+
+/**
+ * Asserts that the given condition is `true`.
+ *
+ * The assertion is independent from -release, by abort()ing. Regular assertions
+ * throw an AssertError and thus require an initialized GC, which might not be
+ * the case (yet or anymore) for the startup/shutdown code in this package
+ * (called by CRT ctors/dtors etc.).
+ */
+package(rt) void safeAssert(
+ bool condition, scope string msg, scope string file = __FILE__, size_t line = __LINE__
+) nothrow @nogc @safe
+{
+ import core.internal.abort;
+ condition || abort(msg, file, line);
+}
+
+// @@@DEPRECATED_2.105@@@
+// Remove this when complex types have been removed from the language.
+package(rt)
+{
+ private struct _Complex(T) { T re; T im; }
+
+ enum __c_complex_float : _Complex!float;
+ enum __c_complex_double : _Complex!double;
+ enum __c_complex_real : _Complex!real; // This is why we don't use stdc.config
+
+ alias d_cfloat = __c_complex_float;
+ alias d_cdouble = __c_complex_double;
+ alias d_creal = __c_complex_real;
+
+ enum isComplex(T) = is(T == d_cfloat) || is(T == d_cdouble) || is(T == d_creal);
+}
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 01cf5943b03..927d3ee874a 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@
-55bb17543138a87c376a84745f2a30ec00bdecd9
+5ab9ad2561cea35ac33ebc0452c0e6a8d762f27d
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am
index 9f6251009f6..ba1579da8d7 100644
--- a/libphobos/src/Makefile.am
+++ b/libphobos/src/Makefile.am
@@ -19,7 +19,7 @@
include $(top_srcdir)/d_rules.am
# Make sure GDC can find libdruntime and libphobos include files
-D_EXTRA_DFLAGS=-nostdinc -I $(srcdir) \
+D_EXTRA_DFLAGS=-fpreview=dip1000 -fpreview=dtorfields -nostdinc -I $(srcdir) \
-I $(top_srcdir)/libdruntime -I ../libdruntime -I .
# D flags for compilation
@@ -83,12 +83,12 @@ PHOBOS_DSOURCES =
else
-PHOBOS_DSOURCES = etc/c/curl.d etc/c/sqlite3.d etc/c/zlib.d \
- std/algorithm/comparison.d std/algorithm/internal.d \
- std/algorithm/iteration.d std/algorithm/mutation.d \
- std/algorithm/package.d std/algorithm/searching.d \
- std/algorithm/setops.d std/algorithm/sorting.d std/array.d std/ascii.d \
- std/base64.d std/bigint.d std/bitmanip.d std/compiler.d std/complex.d \
+PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \
+ std/algorithm/internal.d std/algorithm/iteration.d \
+ std/algorithm/mutation.d std/algorithm/package.d \
+ std/algorithm/searching.d std/algorithm/setops.d \
+ std/algorithm/sorting.d std/array.d std/ascii.d std/base64.d \
+ std/bigint.d std/bitmanip.d std/compiler.d std/complex.d \
std/concurrency.d std/container/array.d std/container/binaryheap.d \
std/container/dlist.d std/container/package.d std/container/rbtree.d \
std/container/slist.d std/container/util.d std/conv.d std/csv.d \
@@ -99,7 +99,9 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/sqlite3.d etc/c/zlib.d \
std/digest/murmurhash.d std/digest/package.d std/digest/ripemd.d \
std/digest/sha.d std/encoding.d std/exception.d \
std/experimental/allocator/building_blocks/affix_allocator.d \
+ std/experimental/allocator/building_blocks/aligned_block_list.d \
std/experimental/allocator/building_blocks/allocator_list.d \
+ std/experimental/allocator/building_blocks/ascending_page_allocator.d \
std/experimental/allocator/building_blocks/bitmapped_block.d \
std/experimental/allocator/building_blocks/bucketizer.d \
std/experimental/allocator/building_blocks/fallback_allocator.d \
@@ -123,27 +125,34 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/sqlite3.d etc/c/zlib.d \
std/experimental/logger/core.d std/experimental/logger/filelogger.d \
std/experimental/logger/multilogger.d \
std/experimental/logger/nulllogger.d std/experimental/logger/package.d \
- std/experimental/typecons.d std/file.d std/format.d std/functional.d \
- std/getopt.d std/internal/cstring.d std/internal/math/biguintcore.d \
- std/internal/math/biguintnoasm.d std/internal/math/errorfunction.d \
- std/internal/math/gammafunction.d std/internal/scopebuffer.d \
+ std/experimental/typecons.d std/file.d std/format/internal/floats.d \
+ std/format/internal/read.d std/format/internal/write.d \
+ std/format/package.d std/format/read.d std/format/spec.d \
+ std/format/write.d std/functional.d std/getopt.d \
+ std/internal/attributes.d std/internal/cstring.d \
+ std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \
+ std/internal/math/errorfunction.d std/internal/math/gammafunction.d \
+ std/internal/memory.d std/internal/scopebuffer.d \
std/internal/test/dummyrange.d std/internal/test/range.d \
std/internal/test/uda.d std/internal/unicode_comp.d \
std/internal/unicode_decomp.d std/internal/unicode_grapheme.d \
std/internal/unicode_norm.d std/internal/unicode_tables.d \
- std/internal/windows/advapi32.d std/json.d std/math.d \
+ std/internal/windows/advapi32.d std/json.d std/math/algebraic.d \
+ std/math/constants.d std/math/exponential.d std/math/hardware.d \
+ std/math/operations.d std/math/package.d std/math/remainder.d \
+ std/math/rounding.d std/math/traits.d std/math/trigonometry.d \
std/mathspecial.d std/meta.d std/mmfile.d std/net/curl.d \
- std/net/isemail.d std/numeric.d std/outbuffer.d std/parallelism.d \
- std/path.d std/process.d std/random.d std/range/interfaces.d \
- std/range/package.d std/range/primitives.d \
+ std/net/isemail.d std/numeric.d std/outbuffer.d std/package.d \
+ std/parallelism.d std/path.d std/process.d std/random.d \
+ std/range/interfaces.d std/range/package.d std/range/primitives.d \
std/regex/internal/backtracking.d std/regex/internal/generator.d \
std/regex/internal/ir.d std/regex/internal/kickstart.d \
std/regex/internal/parser.d std/regex/internal/tests.d \
std/regex/internal/tests2.d std/regex/internal/thompson.d \
std/regex/package.d std/signals.d std/socket.d std/stdint.d \
- std/stdio.d std/string.d std/system.d std/traits.d std/typecons.d \
- std/typetuple.d std/uni.d std/uri.d std/utf.d std/uuid.d std/variant.d \
- std/windows/charset.d std/windows/registry.d std/windows/syserror.d \
- std/xml.d std/zip.d std/zlib.d
+ std/stdio.d std/string.d std/sumtype.d std/system.d std/traits.d \
+ std/typecons.d std/typetuple.d std/uni/package.d std/uri.d std/utf.d \
+ std/uuid.d std/variant.d std/windows/charset.d std/windows/registry.d \
+ std/windows/syserror.d std/xml.d std/zip.d std/zlib.d
endif
diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in
index f8b76486e6e..9e61bb309f8 100644
--- a/libphobos/src/Makefile.in
+++ b/libphobos/src/Makefile.in
@@ -148,7 +148,7 @@ LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
am__DEPENDENCIES_1 =
am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_LIBDRUNTIME_ONLY_FALSE@am__objects_1 = etc/c/curl.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ etc/c/sqlite3.lo etc/c/zlib.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ etc/c/zlib.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/comparison.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/internal.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/iteration.lo \
@@ -188,7 +188,9 @@ am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/encoding.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/exception.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/affix_allocator.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/aligned_block_list.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/allocator_list.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/ascending_page_allocator.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/bitmapped_block.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/bucketizer.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/fallback_allocator.lo \
@@ -216,13 +218,22 @@ am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/nulllogger.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/package.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/typecons.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/file.lo std/format.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/file.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/floats.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/read.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/write.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/package.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/read.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/spec.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/write.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/functional.lo std/getopt.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/attributes.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/cstring.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintcore.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintnoasm.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/errorfunction.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/gammafunction.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/memory.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/scopebuffer.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/dummyrange.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/range.lo \
@@ -233,11 +244,22 @@ am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_norm.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_tables.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/windows/advapi32.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/json.lo std/math.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/json.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/algebraic.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/constants.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/exponential.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/hardware.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/operations.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/package.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/remainder.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/rounding.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/traits.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/trigonometry.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/mathspecial.lo std/meta.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/mmfile.lo std/net/curl.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/net/isemail.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/numeric.lo std/outbuffer.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/package.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/parallelism.lo std/path.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/process.lo std/random.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/range/interfaces.lo \
@@ -254,11 +276,13 @@ am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/package.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/signals.lo std/socket.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/stdint.lo std/stdio.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/string.lo std/system.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/traits.lo std/typecons.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/typetuple.lo std/uni.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/uri.lo std/utf.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/uuid.lo std/variant.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/string.lo std/sumtype.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/system.lo std/traits.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/typecons.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/typetuple.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/uni/package.lo std/uri.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/utf.lo std/uuid.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/variant.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/charset.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/registry.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/syserror.lo \
@@ -476,7 +500,7 @@ LTDCOMPILE = $(LIBTOOL) --tag=D $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
# Include D build rules
# Make sure GDC can find libdruntime and libphobos include files
-D_EXTRA_DFLAGS = -nostdinc -I $(srcdir) \
+D_EXTRA_DFLAGS = -fpreview=dip1000 -fpreview=dtorfields -nostdinc -I $(srcdir) \
-I $(top_srcdir)/libdruntime -I ../libdruntime -I .
@@ -519,12 +543,12 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(GDC) $(AM_CFLAGS) $(CFLAGS) \
$(libgphobos_la_LDFLAGS) $(LDFLAGS) -o $@
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@PHOBOS_DSOURCES = etc/c/curl.d etc/c/sqlite3.d etc/c/zlib.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/comparison.d std/algorithm/internal.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/iteration.d std/algorithm/mutation.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/package.d std/algorithm/searching.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/setops.d std/algorithm/sorting.d std/array.d std/ascii.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/base64.d std/bigint.d std/bitmanip.d std/compiler.d std/complex.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/internal.d std/algorithm/iteration.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/mutation.d std/algorithm/package.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/searching.d std/algorithm/setops.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/algorithm/sorting.d std/array.d std/ascii.d std/base64.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/bigint.d std/bitmanip.d std/compiler.d std/complex.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/concurrency.d std/container/array.d std/container/binaryheap.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/container/dlist.d std/container/package.d std/container/rbtree.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/container/slist.d std/container/util.d std/conv.d std/csv.d \
@@ -535,7 +559,9 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/murmurhash.d std/digest/package.d std/digest/ripemd.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/digest/sha.d std/encoding.d std/exception.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/affix_allocator.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/aligned_block_list.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/allocator_list.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/ascending_page_allocator.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/bitmapped_block.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/bucketizer.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/allocator/building_blocks/fallback_allocator.d \
@@ -559,28 +585,35 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/core.d std/experimental/logger/filelogger.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/multilogger.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/nulllogger.d std/experimental/logger/package.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/typecons.d std/file.d std/format.d std/functional.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/getopt.d std/internal/cstring.d std/internal/math/biguintcore.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintnoasm.d std/internal/math/errorfunction.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/gammafunction.d std/internal/scopebuffer.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/typecons.d std/file.d std/format/internal/floats.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/read.d std/format/internal/write.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/package.d std/format/read.d std/format/spec.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/write.d std/functional.d std/getopt.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/attributes.d std/internal/cstring.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/errorfunction.d std/internal/math/gammafunction.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/memory.d std/internal/scopebuffer.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/dummyrange.d std/internal/test/range.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/uda.d std/internal/unicode_comp.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_decomp.d std/internal/unicode_grapheme.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_norm.d std/internal/unicode_tables.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/windows/advapi32.d std/json.d std/math.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/windows/advapi32.d std/json.d std/math/algebraic.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/constants.d std/math/exponential.d std/math/hardware.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/operations.d std/math/package.d std/math/remainder.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/rounding.d std/math/traits.d std/math/trigonometry.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/mathspecial.d std/meta.d std/mmfile.d std/net/curl.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/net/isemail.d std/numeric.d std/outbuffer.d std/parallelism.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/path.d std/process.d std/random.d std/range/interfaces.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/range/package.d std/range/primitives.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/net/isemail.d std/numeric.d std/outbuffer.d std/package.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/parallelism.d std/path.d std/process.d std/random.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/range/interfaces.d std/range/package.d std/range/primitives.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/backtracking.d std/regex/internal/generator.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/ir.d std/regex/internal/kickstart.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/parser.d std/regex/internal/tests.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/internal/tests2.d std/regex/internal/thompson.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/regex/package.d std/signals.d std/socket.d std/stdint.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/stdio.d std/string.d std/system.d std/traits.d std/typecons.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/typetuple.d std/uni.d std/uri.d std/utf.d std/uuid.d std/variant.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/charset.d std/windows/registry.d std/windows/syserror.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/xml.d std/zip.d std/zlib.d
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/stdio.d std/string.d std/sumtype.d std/system.d std/traits.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/typecons.d std/typetuple.d std/uni/package.d std/uri.d std/utf.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/uuid.d std/variant.d std/windows/charset.d std/windows/registry.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/windows/syserror.d std/xml.d std/zip.d std/zlib.d
# Source file definitions. Boring stuff, auto-generated with
@@ -663,7 +696,6 @@ etc/c/$(am__dirstamp):
@$(MKDIR_P) etc/c
@: > etc/c/$(am__dirstamp)
etc/c/curl.lo: etc/c/$(am__dirstamp)
-etc/c/sqlite3.lo: etc/c/$(am__dirstamp)
etc/c/zlib.lo: etc/c/$(am__dirstamp)
std/algorithm/$(am__dirstamp):
@$(MKDIR_P) std/algorithm
@@ -727,8 +759,12 @@ std/experimental/allocator/building_blocks/$(am__dirstamp):
@: > std/experimental/allocator/building_blocks/$(am__dirstamp)
std/experimental/allocator/building_blocks/affix_allocator.lo: \
std/experimental/allocator/building_blocks/$(am__dirstamp)
+std/experimental/allocator/building_blocks/aligned_block_list.lo: \
+ std/experimental/allocator/building_blocks/$(am__dirstamp)
std/experimental/allocator/building_blocks/allocator_list.lo: \
std/experimental/allocator/building_blocks/$(am__dirstamp)
+std/experimental/allocator/building_blocks/ascending_page_allocator.lo: \
+ std/experimental/allocator/building_blocks/$(am__dirstamp)
std/experimental/allocator/building_blocks/bitmapped_block.lo: \
std/experimental/allocator/building_blocks/$(am__dirstamp)
std/experimental/allocator/building_blocks/bucketizer.lo: \
@@ -791,12 +827,25 @@ std/experimental/logger/package.lo: \
std/experimental/logger/$(am__dirstamp)
std/experimental/typecons.lo: std/experimental/$(am__dirstamp)
std/file.lo: std/$(am__dirstamp)
-std/format.lo: std/$(am__dirstamp)
+std/format/internal/$(am__dirstamp):
+ @$(MKDIR_P) std/format/internal
+ @: > std/format/internal/$(am__dirstamp)
+std/format/internal/floats.lo: std/format/internal/$(am__dirstamp)
+std/format/internal/read.lo: std/format/internal/$(am__dirstamp)
+std/format/internal/write.lo: std/format/internal/$(am__dirstamp)
+std/format/$(am__dirstamp):
+ @$(MKDIR_P) std/format
+ @: > std/format/$(am__dirstamp)
+std/format/package.lo: std/format/$(am__dirstamp)
+std/format/read.lo: std/format/$(am__dirstamp)
+std/format/spec.lo: std/format/$(am__dirstamp)
+std/format/write.lo: std/format/$(am__dirstamp)
std/functional.lo: std/$(am__dirstamp)
std/getopt.lo: std/$(am__dirstamp)
std/internal/$(am__dirstamp):
@$(MKDIR_P) std/internal
@: > std/internal/$(am__dirstamp)
+std/internal/attributes.lo: std/internal/$(am__dirstamp)
std/internal/cstring.lo: std/internal/$(am__dirstamp)
std/internal/math/$(am__dirstamp):
@$(MKDIR_P) std/internal/math
@@ -805,6 +854,7 @@ std/internal/math/biguintcore.lo: std/internal/math/$(am__dirstamp)
std/internal/math/biguintnoasm.lo: std/internal/math/$(am__dirstamp)
std/internal/math/errorfunction.lo: std/internal/math/$(am__dirstamp)
std/internal/math/gammafunction.lo: std/internal/math/$(am__dirstamp)
+std/internal/memory.lo: std/internal/$(am__dirstamp)
std/internal/scopebuffer.lo: std/internal/$(am__dirstamp)
std/internal/test/$(am__dirstamp):
@$(MKDIR_P) std/internal/test
@@ -823,7 +873,19 @@ std/internal/windows/$(am__dirstamp):
std/internal/windows/advapi32.lo: \
std/internal/windows/$(am__dirstamp)
std/json.lo: std/$(am__dirstamp)
-std/math.lo: std/$(am__dirstamp)
+std/math/$(am__dirstamp):
+ @$(MKDIR_P) std/math
+ @: > std/math/$(am__dirstamp)
+std/math/algebraic.lo: std/math/$(am__dirstamp)
+std/math/constants.lo: std/math/$(am__dirstamp)
+std/math/exponential.lo: std/math/$(am__dirstamp)
+std/math/hardware.lo: std/math/$(am__dirstamp)
+std/math/operations.lo: std/math/$(am__dirstamp)
+std/math/package.lo: std/math/$(am__dirstamp)
+std/math/remainder.lo: std/math/$(am__dirstamp)
+std/math/rounding.lo: std/math/$(am__dirstamp)
+std/math/traits.lo: std/math/$(am__dirstamp)
+std/math/trigonometry.lo: std/math/$(am__dirstamp)
std/mathspecial.lo: std/$(am__dirstamp)
std/meta.lo: std/$(am__dirstamp)
std/mmfile.lo: std/$(am__dirstamp)
@@ -834,6 +896,7 @@ std/net/curl.lo: std/net/$(am__dirstamp)
std/net/isemail.lo: std/net/$(am__dirstamp)
std/numeric.lo: std/$(am__dirstamp)
std/outbuffer.lo: std/$(am__dirstamp)
+std/package.lo: std/$(am__dirstamp)
std/parallelism.lo: std/$(am__dirstamp)
std/path.lo: std/$(am__dirstamp)
std/process.lo: std/$(am__dirstamp)
@@ -865,11 +928,15 @@ std/socket.lo: std/$(am__dirstamp)
std/stdint.lo: std/$(am__dirstamp)
std/stdio.lo: std/$(am__dirstamp)
std/string.lo: std/$(am__dirstamp)
+std/sumtype.lo: std/$(am__dirstamp)
std/system.lo: std/$(am__dirstamp)
std/traits.lo: std/$(am__dirstamp)
std/typecons.lo: std/$(am__dirstamp)
std/typetuple.lo: std/$(am__dirstamp)
-std/uni.lo: std/$(am__dirstamp)
+std/uni/$(am__dirstamp):
+ @$(MKDIR_P) std/uni
+ @: > std/uni/$(am__dirstamp)
+std/uni/package.lo: std/uni/$(am__dirstamp)
std/uri.lo: std/$(am__dirstamp)
std/utf.lo: std/$(am__dirstamp)
std/uuid.lo: std/$(am__dirstamp)
@@ -909,6 +976,10 @@ mostlyclean-compile:
-rm -f std/experimental/allocator/building_blocks/*.lo
-rm -f std/experimental/logger/*.$(OBJEXT)
-rm -f std/experimental/logger/*.lo
+ -rm -f std/format/*.$(OBJEXT)
+ -rm -f std/format/*.lo
+ -rm -f std/format/internal/*.$(OBJEXT)
+ -rm -f std/format/internal/*.lo
-rm -f std/internal/*.$(OBJEXT)
-rm -f std/internal/*.lo
-rm -f std/internal/math/*.$(OBJEXT)
@@ -917,6 +988,8 @@ mostlyclean-compile:
-rm -f std/internal/test/*.lo
-rm -f std/internal/windows/*.$(OBJEXT)
-rm -f std/internal/windows/*.lo
+ -rm -f std/math/*.$(OBJEXT)
+ -rm -f std/math/*.lo
-rm -f std/net/*.$(OBJEXT)
-rm -f std/net/*.lo
-rm -f std/range/*.$(OBJEXT)
@@ -925,6 +998,8 @@ mostlyclean-compile:
-rm -f std/regex/*.lo
-rm -f std/regex/internal/*.$(OBJEXT)
-rm -f std/regex/internal/*.lo
+ -rm -f std/uni/*.$(OBJEXT)
+ -rm -f std/uni/*.lo
-rm -f std/windows/*.$(OBJEXT)
-rm -f std/windows/*.lo
@@ -946,14 +1021,18 @@ clean-libtool:
-rm -rf std/experimental/allocator/.libs std/experimental/allocator/_libs
-rm -rf std/experimental/allocator/building_blocks/.libs std/experimental/allocator/building_blocks/_libs
-rm -rf std/experimental/logger/.libs std/experimental/logger/_libs
+ -rm -rf std/format/.libs std/format/_libs
+ -rm -rf std/format/internal/.libs std/format/internal/_libs
-rm -rf std/internal/.libs std/internal/_libs
-rm -rf std/internal/math/.libs std/internal/math/_libs
-rm -rf std/internal/test/.libs std/internal/test/_libs
-rm -rf std/internal/windows/.libs std/internal/windows/_libs
+ -rm -rf std/math/.libs std/math/_libs
-rm -rf std/net/.libs std/net/_libs
-rm -rf std/range/.libs std/range/_libs
-rm -rf std/regex/.libs std/regex/_libs
-rm -rf std/regex/internal/.libs std/regex/internal/_libs
+ -rm -rf std/uni/.libs std/uni/_libs
-rm -rf std/windows/.libs std/windows/_libs
install-toolexeclibDATA: $(toolexeclib_DATA)
@$(NORMAL_INSTALL)
@@ -1071,14 +1150,18 @@ distclean-generic:
-rm -f std/experimental/allocator/$(am__dirstamp)
-rm -f std/experimental/allocator/building_blocks/$(am__dirstamp)
-rm -f std/experimental/logger/$(am__dirstamp)
+ -rm -f std/format/$(am__dirstamp)
+ -rm -f std/format/internal/$(am__dirstamp)
-rm -f std/internal/$(am__dirstamp)
-rm -f std/internal/math/$(am__dirstamp)
-rm -f std/internal/test/$(am__dirstamp)
-rm -f std/internal/windows/$(am__dirstamp)
+ -rm -f std/math/$(am__dirstamp)
-rm -f std/net/$(am__dirstamp)
-rm -f std/range/$(am__dirstamp)
-rm -f std/regex/$(am__dirstamp)
-rm -f std/regex/internal/$(am__dirstamp)
+ -rm -f std/uni/$(am__dirstamp)
-rm -f std/windows/$(am__dirstamp)
maintainer-clean-generic:
diff --git a/libphobos/src/etc/c/curl.d b/libphobos/src/etc/c/curl.d
index 2cc588ce4b5..98fe74af321 100644
--- a/libphobos/src/etc/c/curl.d
+++ b/libphobos/src/etc/c/curl.d
@@ -257,7 +257,7 @@ enum CurlChunkBgnFunc {
/** if splitting of data transfer is enabled, this callback is called before
download of an individual chunk started. Note that parameter "remains" works
only for FTP wildcard downloading (for now), otherwise is not used */
-alias curl_chunk_bgn_callback = c_long function(void *transfer_info, void *ptr, int remains);
+alias curl_chunk_bgn_callback = c_long function(const(void) *transfer_info, void *ptr, int remains);
/** return codes for CURLOPT_CHUNK_END_FUNCTION */
enum CurlChunkEndFunc {
@@ -281,7 +281,7 @@ enum CurlFnMAtchFunc {
/** callback type for wildcard downloading pattern matching. If the
string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */
-alias curl_fnmatch_callback = int function(void *ptr, in char *pattern, in char *string);
+alias curl_fnmatch_callback = int function(void *ptr, in const(char) *pattern, in const(char) *string);
/// seek whence...
enum CurlSeekPos {
@@ -378,7 +378,7 @@ alias curl_free_callback = void function(void *ptr);
/// ditto
alias curl_realloc_callback = void* function(void *ptr, size_t size);
/// ditto
-alias curl_strdup_callback = char * function(in char *str);
+alias curl_strdup_callback = char * function(in const(char) *str);
/// ditto
alias curl_calloc_callback = void* function(size_t nmemb, size_t size);
@@ -615,8 +615,8 @@ enum CurlKHMatch {
///
alias curl_sshkeycallback =
int function(CURL *easy, /** easy handle */
- curl_khkey *knownkey, /** known */
- curl_khkey *foundkey, /** found */
+ const(curl_khkey) *knownkey, /** known */
+ const(curl_khkey) *foundkey, /** found */
CurlKHMatch m, /** libcurl's view on the keys */
void *clientp /** custom pointer passed from app */
);
@@ -867,7 +867,7 @@ enum CurlOption {
/** We want the referrer field set automatically when following locations */
autoreferer = 58,
/** Port of the proxy, can be set in the proxy string as well with:
- "[host]:[port]" */
+ `[host]:[port]` */
proxyport,
/** size of the POST input data, if strlen() is not good to use */
postfieldsize,
@@ -1136,7 +1136,7 @@ enum CurlOption {
ftp_ssl_ccc = 154,
/** Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */
timeout_ms,
- connecttimeout_ms,
+ connecttimeout_ms, /// ditto
/** set to zero to disable the libcurl's decoding and thus pass the raw body
data to the application even when it is encoded/compressed */
http_transfer_decoding,
@@ -1365,9 +1365,9 @@ alias curl_TimeCond = int;
/** curl_strequal() and curl_strnequal() are subject for removal in a future
libcurl, see lib/README.curlx for details */
extern (C) {
-int curl_strequal(in char *s1, in char *s2);
+int curl_strequal(in const(char) *s1, in const(char) *s2);
/// ditto
-int curl_strnequal(in char *s1, in char *s2, size_t n);
+int curl_strnequal(in const(char) *s1, in const(char) *s2, size_t n);
}
enum CurlForm {
nothing, /********** the first one is unused ************/
@@ -1457,7 +1457,7 @@ CURLFORMcode curl_formadd(curl_httppost **httppost, curl_httppost **last_post,.
* Should return the buffer length passed to it as the argument "len" on
* success.
*/
-alias curl_formget_callback = size_t function(void *arg, in char *buf, size_t len);
+alias curl_formget_callback = size_t function(void *arg, in const(char) *buf, size_t len);
/**
* Name: curl_formget()
@@ -1487,7 +1487,7 @@ void curl_formfree(curl_httppost *form);
* Returns a malloc()'ed string that MUST be curl_free()ed after usage is
* complete. DEPRECATED - see lib/README.curlx
*/
-char * curl_getenv(in char *variable);
+char * curl_getenv(in const(char) *variable);
/**
* Name: curl_version()
@@ -1507,10 +1507,10 @@ char * curl_version();
* %XX versions). This function returns a new allocated string or NULL if an
* error occurred.
*/
-char * curl_easy_escape(CURL *handle, in char *string, int length);
+char * curl_easy_escape(CURL *handle, in const(char) *string, int length);
/** the previous version: */
-char * curl_escape(in char *string, int length);
+char * curl_escape(in const(char) *string, int length);
/**
@@ -1524,10 +1524,10 @@ char * curl_escape(in char *string, int length);
* Conversion Note: On non-ASCII platforms the ASCII %XX codes are
* converted into the host encoding.
*/
-char * curl_easy_unescape(CURL *handle, in char *string, int length, int *outlength);
+char * curl_easy_unescape(CURL *handle, in const(char) *string, int length, int *outlength);
/** the previous version */
-char * curl_unescape(in char *string, int length);
+char * curl_unescape(in const(char) *string, int length);
/**
* Name: curl_free()
@@ -1601,7 +1601,7 @@ struct curl_slist
* Appends a string to a linked list. If no list exists, it will be created
* first. Returns the new list, after appending.
*/
-curl_slist * curl_slist_append(curl_slist *, in char *);
+curl_slist * curl_slist_append(curl_slist *, in const(char) *);
/**
* Name: curl_slist_free_all()
@@ -1621,7 +1621,7 @@ void curl_slist_free_all(curl_slist *);
* the first argument. The time argument in the second parameter is unused
* and should be set to NULL.
*/
-time_t curl_getdate(char *p, time_t *unused);
+time_t curl_getdate(const(char) *p, const(time_t) *unused);
/** info about the certificate chain, only for OpenSSL builds. Asked
for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */
diff --git a/libphobos/src/etc/c/sqlite3.d b/libphobos/src/etc/c/sqlite3.d
deleted file mode 100644
index 43a72f52887..00000000000
--- a/libphobos/src/etc/c/sqlite3.d
+++ /dev/null
@@ -1,2126 +0,0 @@
-module etc.c.sqlite3;
-/*
-** 2001 September 15
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This header file defines the interface that the SQLite library
-** presents to client programs. If a C-function, structure, datatype,
-** or constant definition does not appear in this file, then it is
-** not a published API of SQLite, is subject to change without
-** notice, and should not be referenced by programs that use SQLite.
-**
-** Some of the definitions that are in this file are marked as
-** "experimental". Experimental interfaces are normally new
-** features recently added to SQLite. We do not anticipate changes
-** to experimental interfaces but reserve the right to make minor changes
-** if experience from use "in the wild" suggest such changes are prudent.
-**
-** The official C-language API documentation for SQLite is derived
-** from comments in this file. This file is the authoritative source
-** on how SQLite interfaces are suppose to operate.
-**
-** The name of this file under configuration management is "sqlite.h.in".
-** The makefile makes some minor changes to this file (such as inserting
-** the version number) and changes its name to "sqlite3.h" as
-** part of the build process.
-*/
-
-import core.stdc.stdarg : va_list;
-
-extern (C) __gshared nothrow:
-
-/**
-** CAPI3REF: Compile-Time Library Version Numbers
-*/
-enum SQLITE_VERSION = "3.10.2";
-/// Ditto
-enum SQLITE_VERSION_NUMBER = 3_010_002;
-/// Ditto
-enum SQLITE_SOURCE_ID = "2016-01-20 15:27:19 17efb4209f97fb4971656086b138599a91a75ff9";
-
-/**
-** CAPI3REF: Run-Time Library Version Numbers
-*/
-extern immutable(char)* sqlite3_version;
-/// Ditto
-immutable(char)* sqlite3_libversion();
-/// Ditto
-immutable(char)* sqlite3_sourceid();
-/// Ditto
-int sqlite3_libversion_number();
-
-/**
-** CAPI3REF: Run-Time Library Compilation Options Diagnostics
-*/
-int sqlite3_compileoption_used(const char *zOptName);
-/// Ditto
-immutable(char)* sqlite3_compileoption_get(int N);
-
-/**
-** CAPI3REF: Test To See If The Library Is Threadsafe
-*/
-int sqlite3_threadsafe();
-
-/**
-** CAPI3REF: Database Connection Handle
-*/
-struct sqlite3;
-
-///
-alias sqlite3_int64 = long;
-///
-alias sqlite3_uint64 = ulong;
-
-/**
-** CAPI3REF: Closing A Database Connection
-**
-*/
-int sqlite3_close(sqlite3 *);
-int sqlite3_close_v2(sqlite3*);
-
-/**
-** The type for a callback function.
-** This is legacy and deprecated. It is included for historical
-** compatibility and is not documented.
-*/
-alias sqlite3_callback = int function (void*,int,char**, char**);
-
-/**
-** CAPI3REF: One-Step Query Execution Interface
-*/
-int sqlite3_exec(
- sqlite3*, /** An open database */
- const(char)*sql, /** SQL to be evaluated */
- int function (void*,int,char**,char**) callback, /** Callback function */
- void *, /** 1st argument to callback */
- char **errmsg /** Error msg written here */
-);
-
-/**
-** CAPI3REF: Result Codes
-*/
-enum
-{
- SQLITE_OK = 0, /** Successful result */
-/* beginning-of-error-codes */
-/// Ditto
- SQLITE_ERROR = 1, /** SQL error or missing database */
- SQLITE_INTERNAL = 2, /** Internal logic error in SQLite */
- SQLITE_PERM = 3, /** Access permission denied */
- SQLITE_ABORT = 4, /** Callback routine requested an abort */
- SQLITE_BUSY = 5, /** The database file is locked */
- SQLITE_LOCKED = 6, /** A table in the database is locked */
- SQLITE_NOMEM = 7, /** A malloc() failed */
- SQLITE_READONLY = 8, /** Attempt to write a readonly database */
- SQLITE_INTERRUPT = 9, /** Operation terminated by sqlite3_interrupt()*/
- SQLITE_IOERR = 10, /** Some kind of disk I/O error occurred */
- SQLITE_CORRUPT = 11, /** The database disk image is malformed */
- SQLITE_NOTFOUND = 12, /** Unknown opcode in sqlite3_file_control() */
- SQLITE_FULL = 13, /** Insertion failed because database is full */
- SQLITE_CANTOPEN = 14, /** Unable to open the database file */
- SQLITE_PROTOCOL = 15, /** Database lock protocol error */
- SQLITE_EMPTY = 16, /** Database is empty */
- SQLITE_SCHEMA = 17, /** The database schema changed */
- SQLITE_TOOBIG = 18, /** String or BLOB exceeds size limit */
- SQLITE_CONSTRAINT = 19, /** Abort due to constraint violation */
- SQLITE_MISMATCH = 20, /** Data type mismatch */
- SQLITE_MISUSE = 21, /** Library used incorrectly */
- SQLITE_NOLFS = 22, /** Uses OS features not supported on host */
- SQLITE_AUTH = 23, /** Authorization denied */
- SQLITE_FORMAT = 24, /** Auxiliary database format error */
- SQLITE_RANGE = 25, /** 2nd parameter to sqlite3_bind out of range */
- SQLITE_NOTADB = 26, /** File opened that is not a database file */
- SQLITE_NOTICE = 27,
- SQLITE_WARNING = 28,
- SQLITE_ROW = 100, /** sqlite3_step() has another row ready */
- SQLITE_DONE = 101 /** sqlite3_step() has finished executing */
-}
-/* end-of-error-codes */
-
-/**
-** CAPI3REF: Extended Result Codes
-*/
-enum
-{
- SQLITE_IOERR_READ = (SQLITE_IOERR | (1 << 8)),
- SQLITE_IOERR_SHORT_READ = (SQLITE_IOERR | (2 << 8)),
- SQLITE_IOERR_WRITE = (SQLITE_IOERR | (3 << 8)),
- SQLITE_IOERR_FSYNC = (SQLITE_IOERR | (4 << 8)),
- SQLITE_IOERR_DIR_FSYNC = (SQLITE_IOERR | (5 << 8)),
- SQLITE_IOERR_TRUNCATE = (SQLITE_IOERR | (6 << 8)),
- SQLITE_IOERR_FSTAT = (SQLITE_IOERR | (7 << 8)),
- SQLITE_IOERR_UNLOCK = (SQLITE_IOERR | (8 << 8)),
- SQLITE_IOERR_RDLOCK = (SQLITE_IOERR | (9 << 8)),
- SQLITE_IOERR_DELETE = (SQLITE_IOERR | (10 << 8)),
- SQLITE_IOERR_BLOCKED = (SQLITE_IOERR | (11 << 8)),
- SQLITE_IOERR_NOMEM = (SQLITE_IOERR | (12 << 8)),
- SQLITE_IOERR_ACCESS = (SQLITE_IOERR | (13 << 8)),
- SQLITE_IOERR_CHECKRESERVEDLOCK = (SQLITE_IOERR | (14 << 8)),
- SQLITE_IOERR_LOCK = (SQLITE_IOERR | (15 << 8)),
- SQLITE_IOERR_CLOSE = (SQLITE_IOERR | (16 << 8)),
- SQLITE_IOERR_DIR_CLOSE = (SQLITE_IOERR | (17 << 8)),
- SQLITE_IOERR_SHMOPEN = (SQLITE_IOERR | (18 << 8)),
- SQLITE_IOERR_SHMSIZE = (SQLITE_IOERR | (19 << 8)),
- SQLITE_IOERR_SHMLOCK = (SQLITE_IOERR | (20 << 8)),
- SQLITE_IOERR_SHMMAP = (SQLITE_IOERR | (21 << 8)),
- SQLITE_IOERR_SEEK = (SQLITE_IOERR | (22 << 8)),
- SQLITE_IOERR_DELETE_NOENT = (SQLITE_IOERR | (23 << 8)),
- SQLITE_IOERR_MMAP = (SQLITE_IOERR | (24 << 8)),
- SQLITE_LOCKED_SHAREDCACHE = (SQLITE_LOCKED | (1 << 8)),
- SQLITE_BUSY_RECOVERY = (SQLITE_BUSY | (1 << 8)),
- SQLITE_CANTOPEN_NOTEMPDIR = (SQLITE_CANTOPEN | (1 << 8)),
- SQLITE_IOERR_GETTEMPPATH = (SQLITE_IOERR | (25 << 8)),
- SQLITE_IOERR_CONVPATH = (SQLITE_IOERR | (26 << 8)),
- SQLITE_BUSY_SNAPSHOT = (SQLITE_BUSY | (2 << 8)),
- SQLITE_CANTOPEN_ISDIR = (SQLITE_CANTOPEN | (2 << 8)),
- SQLITE_CANTOPEN_FULLPATH = (SQLITE_CANTOPEN | (3 << 8)),
- SQLITE_CANTOPEN_CONVPATH = (SQLITE_CANTOPEN | (4 << 8)),
- SQLITE_CORRUPT_VTAB = (SQLITE_CORRUPT | (1 << 8)),
- SQLITE_READONLY_RECOVERY = (SQLITE_READONLY | (1 << 8)),
- SQLITE_READONLY_CANTLOCK = (SQLITE_READONLY | (2 << 8)),
- SQLITE_READONLY_ROLLBACK = (SQLITE_READONLY | (3 << 8)),
- SQLITE_READONLY_DBMOVED = (SQLITE_READONLY | (4 << 8)),
- SQLITE_ABORT_ROLLBACK = (SQLITE_ABORT | (2 << 8)),
- SQLITE_CONSTRAINT_CHECK = (SQLITE_CONSTRAINT | (1 << 8)),
- SQLITE_CONSTRAINT_COMMITHOOK = (SQLITE_CONSTRAINT | (2 << 8)),
- SQLITE_CONSTRAINT_FOREIGNKEY = (SQLITE_CONSTRAINT | (3 << 8)),
- SQLITE_CONSTRAINT_FUNCTION = (SQLITE_CONSTRAINT | (4 << 8)),
- SQLITE_CONSTRAINT_NOTNULL = (SQLITE_CONSTRAINT | (5 << 8)),
- SQLITE_CONSTRAINT_PRIMARYKEY = (SQLITE_CONSTRAINT | (6 << 8)),
- SQLITE_CONSTRAINT_TRIGGER = (SQLITE_CONSTRAINT | (7 << 8)),
- SQLITE_CONSTRAINT_UNIQUE = (SQLITE_CONSTRAINT | (8 << 8)),
- SQLITE_CONSTRAINT_VTAB = (SQLITE_CONSTRAINT | (9 << 8)),
- SQLITE_CONSTRAINT_ROWID = (SQLITE_CONSTRAINT |(10 << 8)),
- SQLITE_NOTICE_RECOVER_WAL = (SQLITE_NOTICE | (1 << 8)),
- SQLITE_NOTICE_RECOVER_ROLLBACK = (SQLITE_NOTICE | (2 << 8)),
- SQLITE_WARNING_AUTOINDEX = (SQLITE_WARNING | (1 << 8)),
- SQLITE_AUTH_USER = (SQLITE_AUTH | (1 << 8))
-}
-
-/**
-** CAPI3REF: Flags For File Open Operations
-*/
-enum
-{
- SQLITE_OPEN_READONLY = 0x00000001, /** Ok for sqlite3_open_v2() */
- SQLITE_OPEN_READWRITE = 0x00000002, /** Ok for sqlite3_open_v2() */
- SQLITE_OPEN_CREATE = 0x00000004, /** Ok for sqlite3_open_v2() */
- SQLITE_OPEN_DELETEONCLOSE = 0x00000008, /** VFS only */
- SQLITE_OPEN_EXCLUSIVE = 0x00000010, /** VFS only */
- SQLITE_OPEN_AUTOPROXY = 0x00000020, /** VFS only */
- SQLITE_OPEN_URI = 0x00000040, /** Ok for sqlite3_open_v2() */
- SQLITE_OPEN_MEMORY = 0x00000080, /** Ok for sqlite3_open_v2() */
- SQLITE_OPEN_MAIN_DB = 0x00000100, /** VFS only */
- SQLITE_OPEN_TEMP_DB = 0x00000200, /** VFS only */
- SQLITE_OPEN_TRANSIENT_DB = 0x00000400, /** VFS only */
- SQLITE_OPEN_MAIN_JOURNAL = 0x00000800, /** VFS only */
- SQLITE_OPEN_TEMP_JOURNAL = 0x00001000, /** VFS only */
- SQLITE_OPEN_SUBJOURNAL = 0x00002000, /** VFS only */
- SQLITE_OPEN_MASTER_JOURNAL = 0x00004000, /** VFS only */
- SQLITE_OPEN_NOMUTEX = 0x00008000, /** Ok for sqlite3_open_v2() */
- SQLITE_OPEN_FULLMUTEX = 0x00010000, /** Ok for sqlite3_open_v2() */
- SQLITE_OPEN_SHAREDCACHE = 0x00020000, /** Ok for sqlite3_open_v2() */
- SQLITE_OPEN_PRIVATECACHE = 0x00040000, /** Ok for sqlite3_open_v2() */
- SQLITE_OPEN_WAL = 0x00080000 /** VFS only */
-}
-
-/**
-** CAPI3REF: Device Characteristics
-*/
-enum
-{
- SQLITE_IOCAP_ATOMIC = 0x00000001,
- SQLITE_IOCAP_ATOMIC512 = 0x00000002,
- SQLITE_IOCAP_ATOMIC1K = 0x00000004,
- SQLITE_IOCAP_ATOMIC2K = 0x00000008,
- SQLITE_IOCAP_ATOMIC4K = 0x00000010,
- SQLITE_IOCAP_ATOMIC8K = 0x00000020,
- SQLITE_IOCAP_ATOMIC16K = 0x00000040,
- SQLITE_IOCAP_ATOMIC32K = 0x00000080,
- SQLITE_IOCAP_ATOMIC64K = 0x00000100,
- SQLITE_IOCAP_SAFE_APPEND = 0x00000200,
- SQLITE_IOCAP_SEQUENTIAL = 0x00000400,
- SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN = 0x00000800,
- SQLITE_IOCAP_POWERSAFE_OVERWRITE = 0x00001000,
- SQLITE_IOCAP_IMMUTABLE = 0x00002000
-}
-
-/**
-** CAPI3REF: File Locking Levels
-*/
-enum
-{
- SQLITE_LOCK_NONE = 0,
- SQLITE_LOCK_SHARED = 1,
- SQLITE_LOCK_RESERVED = 2,
- SQLITE_LOCK_PENDING = 3,
- SQLITE_LOCK_EXCLUSIVE = 4
-}
-
-/**
-** CAPI3REF: Synchronization Type Flags
-*/
-enum
-{
- SQLITE_SYNC_NORMAL = 0x00002,
- SQLITE_SYNC_FULL = 0x00003,
- SQLITE_SYNC_DATAONLY = 0x00010
-}
-
-/**
-** CAPI3REF: OS Interface Open File Handle
-*/
-struct sqlite3_file
-{
- const(sqlite3_io_methods)*pMethods; /* Methods for an open file */
-}
-
-/**
-** CAPI3REF: OS Interface File Virtual Methods Object
-*/
-
-struct sqlite3_io_methods
-{
- int iVersion;
- int function (sqlite3_file*) xClose;
- int function (sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) xRead;
- int function (sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) xWrite;
- int function (sqlite3_file*, sqlite3_int64 size) xTruncate;
- int function (sqlite3_file*, int flags) xSync;
- int function (sqlite3_file*, sqlite3_int64 *pSize) xFileSize;
- int function (sqlite3_file*, int) xLock;
- int function (sqlite3_file*, int) xUnlock;
- int function (sqlite3_file*, int *pResOut) xCheckReservedLock;
- int function (sqlite3_file*, int op, void *pArg) xFileControl;
- int function (sqlite3_file*) xSectorSize;
- int function (sqlite3_file*) xDeviceCharacteristics;
- /* Methods above are valid for version 1 */
- int function (sqlite3_file*, int iPg, int pgsz, int, void **) xShmMap;
- int function (sqlite3_file*, int offset, int n, int flags) xShmLock;
- void function (sqlite3_file*) xShmBarrier;
- int function (sqlite3_file*, int deleteFlag) xShmUnmap;
- /* Methods above are valid for version 2 */
- /* Additional methods may be added in future releases */
- int function (sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp) xFetch;
- int function (sqlite3_file*, sqlite3_int64 iOfst, void *p) xUnfetch;
-}
-
-/**
-** CAPI3REF: Standard File Control Opcodes
-*/
-enum
-{
- SQLITE_FCNTL_LOCKSTATE = 1,
- SQLITE_GET_LOCKPROXYFILE = 2,
- SQLITE_SET_LOCKPROXYFILE = 3,
- SQLITE_LAST_ERRNO = 4,
- SQLITE_FCNTL_SIZE_HINT = 5,
- SQLITE_FCNTL_CHUNK_SIZE = 6,
- SQLITE_FCNTL_FILE_POINTER = 7,
- SQLITE_FCNTL_SYNC_OMITTED = 8,
- SQLITE_FCNTL_WIN32_AV_RETRY = 9,
- SQLITE_FCNTL_PERSIST_WAL = 10,
- SQLITE_FCNTL_OVERWRITE = 11,
- SQLITE_FCNTL_VFSNAME = 12,
- SQLITE_FCNTL_POWERSAFE_OVERWRITE = 13,
- SQLITE_FCNTL_PRAGMA = 14,
- SQLITE_FCNTL_BUSYHANDLER = 15,
- SQLITE_FCNTL_TEMPFILENAME = 16,
- SQLITE_FCNTL_MMAP_SIZE = 18,
- SQLITE_FCNTL_TRACE = 19,
- SQLITE_FCNTL_HAS_MOVED = 20,
- SQLITE_FCNTL_SYNC = 21,
- SQLITE_FCNTL_COMMIT_PHASETWO = 22,
- SQLITE_FCNTL_WIN32_SET_HANDLE = 23,
- SQLITE_FCNTL_WAL_BLOCK = 24,
- SQLITE_FCNTL_ZIPVFS = 25,
- SQLITE_FCNTL_RBU = 26,
- SQLITE_FCNTL_VFS_POINTER = 27,
-}
-
-/**
-** CAPI3REF: Mutex Handle
-*/
-struct sqlite3_mutex;
-
-/**
-** CAPI3REF: OS Interface Object
-*/
-
-alias xDlSymReturn = void * function();
-/// Ditto
-alias sqlite3_syscall_ptr = void function();
-
-struct sqlite3_vfs
-{
- int iVersion; /** Structure version number (currently 2) */
- int szOsFile; /** Size of subclassed sqlite3_file */
- int mxPathname; /** Maximum file pathname length */
- sqlite3_vfs *pNext; /** Next registered VFS */
- const(char)*zName; /** Name of this virtual file system */
- void *pAppData; /** Pointer to application-specific data */
- int function (sqlite3_vfs*, const char *zName, sqlite3_file*,
- int flags, int *pOutFlags) xOpen;
- int function (sqlite3_vfs*, const char *zName, int syncDir) xDelete;
- int function (sqlite3_vfs*, const char *zName, int flags, int *pResOut) xAccess;
- int function (sqlite3_vfs*, const char *zName, int nOut, char *zOut) xFullPathname;
- void* function (sqlite3_vfs*, const char *zFilename) xDlOpen;
- void function (sqlite3_vfs*, int nByte, char *zErrMsg) xDlError;
- xDlSymReturn function (sqlite3_vfs*,void*, const char *zSymbol) *xDlSym;
- void function (sqlite3_vfs*, void*) xDlClose;
- int function (sqlite3_vfs*, int nByte, char *zOut) xRandomness;
- int function (sqlite3_vfs*, int microseconds) xSleep;
- int function (sqlite3_vfs*, double*) xCurrentTime;
- int function (sqlite3_vfs*, int, char *) xGetLastError;
- /*
- ** The methods above are in version 1 of the sqlite_vfs object
- ** definition. Those that follow are added in version 2 or later
- */
- int function (sqlite3_vfs*, sqlite3_int64*) xCurrentTimeInt64;
- /*
- ** The methods above are in versions 1 and 2 of the sqlite_vfs object.
- ** Those below are for version 3 and greater.
- */
- int function(sqlite3_vfs*, const char * zName, sqlite3_syscall_ptr) xSetSystemCall;
- sqlite3_syscall_ptr function(sqlite3_vfs*, const char * zName) xGetSystemCall;
- const(char)* function(sqlite3_vfs*, const char * zName) xNextSystemCall;
- /*
- ** The methods above are in versions 1 through 3 of the sqlite_vfs object.
- ** New fields may be appended in figure versions. The iVersion
- ** value will increment whenever this happens.
- */
-}
-
-/**
-** CAPI3REF: Flags for the xAccess VFS method
-*/
-enum
-{
- SQLITE_ACCESS_EXISTS = 0,
-
- SQLITE_ACCESS_READWRITE = 1, /** Used by PRAGMA temp_store_directory */
- SQLITE_ACCESS_READ = 2 /** Unused */
-}
-
-/**
-** CAPI3REF: Flags for the xShmLock VFS method
-*/
-enum
-{
- SQLITE_SHM_UNLOCK = 1,
- SQLITE_SHM_LOCK = 2,
- SQLITE_SHM_SHARED = 4,
- SQLITE_SHM_EXCLUSIVE = 8
-}
-
-/**
-** CAPI3REF: Maximum xShmLock index
-*/
-enum SQLITE_SHM_NLOCK = 8;
-
-
-/**
-** CAPI3REF: Initialize The SQLite Library
-*/
-int sqlite3_initialize();
-/// Ditto
-int sqlite3_shutdown();
-/// Ditto
-int sqlite3_os_init();
-/// Ditto
-int sqlite3_os_end();
-
-/**
-** CAPI3REF: Configuring The SQLite Library
-*/
-int sqlite3_config(int, ...);
-
-/**
-** CAPI3REF: Configure database connections
-*/
-int sqlite3_db_config(sqlite3*, int op, ...);
-
-/**
-** CAPI3REF: Memory Allocation Routines
-*/
-struct sqlite3_mem_methods
-{
- void* function (int) xMalloc; /** Memory allocation function */
- void function (void*) xFree; /** Free a prior allocation */
- void* function (void*,int) xRealloc; /** Resize an allocation */
- int function (void*) xSize; /** Return the size of an allocation */
- int function (int) xRoundup; /** Round up request size to allocation size */
- int function (void*) xInit; /** Initialize the memory allocator */
- void function (void*) xShutdown; /** Deinitialize the memory allocator */
- void *pAppData; /** Argument to xInit() and xShutdown() */
-}
-
-/**
-** CAPI3REF: Configuration Options
-*/
-enum
-{
- SQLITE_CONFIG_SINGLETHREAD = 1, /** nil */
- SQLITE_CONFIG_MULTITHREAD = 2, /** nil */
- SQLITE_CONFIG_SERIALIZED = 3, /** nil */
- SQLITE_CONFIG_MALLOC = 4, /** sqlite3_mem_methods* */
- SQLITE_CONFIG_GETMALLOC = 5, /** sqlite3_mem_methods* */
- SQLITE_CONFIG_SCRATCH = 6, /** void*, int sz, int N */
- SQLITE_CONFIG_PAGECACHE = 7, /** void*, int sz, int N */
- SQLITE_CONFIG_HEAP = 8, /** void*, int nByte, int min */
- SQLITE_CONFIG_MEMSTATUS = 9, /** boolean */
- SQLITE_CONFIG_MUTEX = 10, /** sqlite3_mutex_methods* */
- SQLITE_CONFIG_GETMUTEX = 11, /** sqlite3_mutex_methods* */
-/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
- SQLITE_CONFIG_LOOKASIDE = 13, /** int int */
- SQLITE_CONFIG_PCACHE = 14, /** sqlite3_pcache_methods* */
- SQLITE_CONFIG_GETPCACHE = 15, /** sqlite3_pcache_methods* */
- SQLITE_CONFIG_LOG = 16, /** xFunc, void* */
- SQLITE_CONFIG_URI = 17,
- SQLITE_CONFIG_PCACHE2 = 18,
- SQLITE_CONFIG_GETPCACHE2 = 19,
- SQLITE_CONFIG_COVERING_INDEX_SCAN = 20,
- SQLITE_CONFIG_SQLLOG = 21,
- SQLITE_CONFIG_MMAP_SIZE = 22,
- SQLITE_CONFIG_WIN32_HEAPSIZE = 23,
- SQLITE_CONFIG_PCACHE_HDRSZ = 24,
- SQLITE_CONFIG_PMASZ = 25,
-}
-
-/**
-** CAPI3REF: Database Connection Configuration Options
-*/
-enum
-{
- SQLITE_DBCONFIG_LOOKASIDE = 1001, /** void* int int */
- SQLITE_DBCONFIG_ENABLE_FKEY = 1002, /** int int* */
- SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003 /** int int* */
-}
-
-
-/**
-** CAPI3REF: Enable Or Disable Extended Result Codes
-*/
-int sqlite3_extended_result_codes(sqlite3*, int onoff);
-
-/**
-** CAPI3REF: Last Insert Rowid
-*/
-sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
-
-/**
-** CAPI3REF: Count The Number Of Rows Modified
-*/
-int sqlite3_changes(sqlite3*);
-
-/**
-** CAPI3REF: Total Number Of Rows Modified
-*/
-int sqlite3_total_changes(sqlite3*);
-
-/**
-** CAPI3REF: Interrupt A Long-Running Query
-*/
-void sqlite3_interrupt(sqlite3*);
-
-/**
-** CAPI3REF: Determine If An SQL Statement Is Complete
-*/
-int sqlite3_complete(const char *sql);
-/// Ditto
-int sqlite3_complete16(const void *sql);
-
-/**
-** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors
-*/
-int sqlite3_busy_handler(sqlite3*, int function (void*,int), void*);
-
-/**
-** CAPI3REF: Set A Busy Timeout
-*/
-int sqlite3_busy_timeout(sqlite3*, int ms);
-
-/**
-** CAPI3REF: Convenience Routines For Running Queries
-*/
-int sqlite3_get_table(
- sqlite3 *db, /** An open database */
- const(char)*zSql, /** SQL to be evaluated */
- char ***pazResult, /** Results of the query */
- int *pnRow, /** Number of result rows written here */
- int *pnColumn, /** Number of result columns written here */
- char **pzErrmsg /** Error msg written here */
-);
-///
-void sqlite3_free_table(char **result);
-
-/**
-** CAPI3REF: Formatted String Printing Functions
-*/
-char *sqlite3_mprintf(const char*,...);
-char *sqlite3_vmprintf(const char*, va_list);
-char *sqlite3_snprintf(int,char*,const char*, ...);
-char *sqlite3_vsnprintf(int,char*,const char*, va_list);
-
-/**
-** CAPI3REF: Memory Allocation Subsystem
-*/
-void *sqlite3_malloc(int);
-/// Ditto
-void *sqlite3_malloc64(sqlite3_uint64);
-/// Ditto
-void *sqlite3_realloc(void*, int);
-/// Ditto
-void *sqlite3_realloc64(void*, sqlite3_uint64);
-/// Ditto
-void sqlite3_free(void*);
-/// Ditto
-sqlite3_uint64 sqlite3_msize(void*);
-
-/**
-** CAPI3REF: Memory Allocator Statistics
-*/
-sqlite3_int64 sqlite3_memory_used();
-sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
-
-/**
-** CAPI3REF: Pseudo-Random Number Generator
-*/
-void sqlite3_randomness(int N, void *P);
-
-/**
-** CAPI3REF: Compile-Time Authorization Callbacks
-*/
-int sqlite3_set_authorizer(
- sqlite3*,
- int function (void*,int,const char*,const char*,const char*,const char*) xAuth,
- void *pUserData
-);
-
-/**
-** CAPI3REF: Authorizer Return Codes
-*/
-enum
-{
- SQLITE_DENY = 1, /** Abort the SQL statement with an error */
- SQLITE_IGNORE = 2 /** Don't allow access, but don't generate an error */
-}
-
-/**
-** CAPI3REF: Authorizer Action Codes
-*/
-/******************************************* 3rd ************ 4th ***********/
-enum
-{
- SQLITE_CREATE_INDEX = 1, /** Index Name Table Name */
- SQLITE_CREATE_TABLE = 2, /** Table Name NULL */
- SQLITE_CREATE_TEMP_INDEX = 3, /** Index Name Table Name */
- SQLITE_CREATE_TEMP_TABLE = 4, /** Table Name NULL */
- SQLITE_CREATE_TEMP_TRIGGER = 5, /** Trigger Name Table Name */
- SQLITE_CREATE_TEMP_VIEW = 6, /** View Name NULL */
- SQLITE_CREATE_TRIGGER = 7, /** Trigger Name Table Name */
- SQLITE_CREATE_VIEW = 8, /** View Name NULL */
- SQLITE_DELETE = 9, /** Table Name NULL */
- SQLITE_DROP_INDEX = 10, /** Index Name Table Name */
- SQLITE_DROP_TABLE = 11, /** Table Name NULL */
- SQLITE_DROP_TEMP_INDEX = 12, /** Index Name Table Name */
- SQLITE_DROP_TEMP_TABLE = 13, /** Table Name NULL */
- SQLITE_DROP_TEMP_TRIGGER = 14, /** Trigger Name Table Name */
- SQLITE_DROP_TEMP_VIEW = 15, /** View Name NULL */
- SQLITE_DROP_TRIGGER = 16, /** Trigger Name Table Name */
- SQLITE_DROP_VIEW = 17, /** View Name NULL */
- SQLITE_INSERT = 18, /** Table Name NULL */
- SQLITE_PRAGMA = 19, /** Pragma Name 1st arg or NULL */
- SQLITE_READ = 20, /** Table Name Column Name */
- SQLITE_SELECT = 21, /** NULL NULL */
- SQLITE_TRANSACTION = 22, /** Operation NULL */
- SQLITE_UPDATE = 23, /** Table Name Column Name */
- SQLITE_ATTACH = 24, /** Filename NULL */
- SQLITE_DETACH = 25, /** Database Name NULL */
- SQLITE_ALTER_TABLE = 26, /** Database Name Table Name */
- SQLITE_REINDEX = 27, /** Index Name NULL */
- SQLITE_ANALYZE = 28, /** Table Name NULL */
- SQLITE_CREATE_VTABLE = 29, /** Table Name Module Name */
- SQLITE_DROP_VTABLE = 30, /** Table Name Module Name */
- SQLITE_FUNCTION = 31, /** NULL Function Name */
- SQLITE_SAVEPOINT = 32, /** Operation Savepoint Name */
- SQLITE_COPY = 0, /** No longer used */
- SQLITE_RECURSIVE = 33
-}
-
-/**
-** CAPI3REF: Tracing And Profiling Functions
-*/
-void *sqlite3_trace(sqlite3*, void function (void*,const char*) xTrace, void*);
-/// Ditto
-void *sqlite3_profile(sqlite3*, void function (void*,const char*,sqlite3_uint64) xProfile, void*);
-
-/**
-** CAPI3REF: Query Progress Callbacks
-*/
-void sqlite3_progress_handler(sqlite3*, int, int function (void*), void*);
-
-/**
-** CAPI3REF: Opening A New Database Connection
-*/
-int sqlite3_open(
- const(char)*filename, /** Database filename (UTF-8) */
- sqlite3 **ppDb /** OUT: SQLite db handle */
-);
-/// Ditto
-int sqlite3_open16(
- const(void)*filename, /** Database filename (UTF-16) */
- sqlite3 **ppDb /** OUT: SQLite db handle */
-);
-/// Ditto
-int sqlite3_open_v2(
- const(char)*filename, /** Database filename (UTF-8) */
- sqlite3 **ppDb, /** OUT: SQLite db handle */
- int flags, /** Flags */
- const(char)*zVfs /** Name of VFS module to use */
-);
-
-/*
-** CAPI3REF: Obtain Values For URI Parameters
-*/
-const(char)* sqlite3_uri_parameter(const(char)* zFilename, const(char)* zParam);
-/// Ditto
-int sqlite3_uri_boolean(const(char)* zFile, const(char)* zParam, int bDefault);
-/// Ditto
-sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
-
-/**
-** CAPI3REF: Error Codes And Messages
-*/
-int sqlite3_errcode(sqlite3 *db);
-/// Ditto
-int sqlite3_extended_errcode(sqlite3 *db);
-/// Ditto
-const(char)* sqlite3_errmsg(sqlite3*);
-/// Ditto
-const(void)* sqlite3_errmsg16(sqlite3*);
-/// Ditto
-const(char)* sqlite3_errstr(int);
-
-/**
-** CAPI3REF: SQL Statement Object
-*/
-struct sqlite3_stmt;
-
-/**
-** CAPI3REF: Run-time Limits
-*/
-int sqlite3_limit(sqlite3*, int id, int newVal);
-
-/**
-** CAPI3REF: Run-Time Limit Categories
-*/
-enum
-{
- SQLITE_LIMIT_LENGTH = 0,
- SQLITE_LIMIT_SQL_LENGTH = 1,
- SQLITE_LIMIT_COLUMN = 2,
- SQLITE_LIMIT_EXPR_DEPTH = 3,
- SQLITE_LIMIT_COMPOUND_SELECT = 4,
- SQLITE_LIMIT_VDBE_OP = 5,
- SQLITE_LIMIT_FUNCTION_ARG = 6,
- SQLITE_LIMIT_ATTACHED = 7,
- SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8,
- SQLITE_LIMIT_VARIABLE_NUMBER = 9,
- SQLITE_LIMIT_TRIGGER_DEPTH = 10,
- SQLITE_LIMIT_WORKER_THREADS = 11,
-}
-
-/**
-** CAPI3REF: Compiling An SQL Statement
-*/
-int sqlite3_prepare(
- sqlite3 *db, /** Database handle */
- const(char)*zSql, /** SQL statement, UTF-8 encoded */
- int nByte, /** Maximum length of zSql in bytes. */
- sqlite3_stmt **ppStmt, /** OUT: Statement handle */
- const(char*)*pzTail /** OUT: Pointer to unused portion of zSql */
-);
-/// Ditto
-int sqlite3_prepare_v2(
- sqlite3 *db, /** Database handle */
- const(char)*zSql, /** SQL statement, UTF-8 encoded */
- int nByte, /** Maximum length of zSql in bytes. */
- sqlite3_stmt **ppStmt, /** OUT: Statement handle */
- const(char*)*pzTail /** OUT: Pointer to unused portion of zSql */
-);
-/// Ditto
-int sqlite3_prepare16(
- sqlite3 *db, /** Database handle */
- const(void)*zSql, /** SQL statement, UTF-16 encoded */
- int nByte, /** Maximum length of zSql in bytes. */
- sqlite3_stmt **ppStmt, /** OUT: Statement handle */
- const(void*)*pzTail /** OUT: Pointer to unused portion of zSql */
-);
-/// Ditto
-int sqlite3_prepare16_v2(
- sqlite3 *db, /** Database handle */
- const(void)*zSql, /** SQL statement, UTF-16 encoded */
- int nByte, /** Maximum length of zSql in bytes. */
- sqlite3_stmt **ppStmt, /** OUT: Statement handle */
- const(void*)*pzTail /** OUT: Pointer to unused portion of zSql */
-);
-
-/**
-** CAPI3REF: Retrieving Statement SQL
-*/
-const(char)* sqlite3_sql(sqlite3_stmt *pStmt);
-
-/*
-** CAPI3REF: Determine If An SQL Statement Writes The Database
-*/
-int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
-
-/**
-** CAPI3REF: Determine If A Prepared Statement Has Been Reset
-*/
-int sqlite3_stmt_busy(sqlite3_stmt*);
-
-
-/**
-** CAPI3REF: Dynamically Typed Value Object
-*/
-struct sqlite3_value;
-
-/**
-** CAPI3REF: SQL Function Context Object
-*/
-struct sqlite3_context;
-
-/**
-** CAPI3REF: Binding Values To Prepared Statements
-*/
-int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void function (void*));
-/// Ditto
-int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,void function (void*));
-/// Ditto
-int sqlite3_bind_double(sqlite3_stmt*, int, double);
-/// Ditto
-int sqlite3_bind_int(sqlite3_stmt*, int, int);
-/// Ditto
-int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
-/// Ditto
-int sqlite3_bind_null(sqlite3_stmt*, int);
-/// Ditto
-int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void function (void*));
-/// Ditto
-int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void function (void*));
-/// Ditto
-int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,void function (void*), ubyte encoding);
-/// Ditto
-int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
-/// Ditto
-int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
-/// Ditto
-int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64 n);
-
-/**
-** CAPI3REF: Number Of SQL Parameters
-*/
-int sqlite3_bind_parameter_count(sqlite3_stmt*);
-
-/**
-** CAPI3REF: Name Of A Host Parameter
-*/
-const(char)* sqlite3_bind_parameter_name(sqlite3_stmt*, int);
-
-/**
-** CAPI3REF: Index Of A Parameter With A Given Name
-*/
-int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
-
-/**
-** CAPI3REF: Reset All Bindings On A Prepared Statement
-*/
-int sqlite3_clear_bindings(sqlite3_stmt*);
-
-/**
-** CAPI3REF: Number Of Columns In A Result Set
-*/
-int sqlite3_column_count(sqlite3_stmt *pStmt);
-
-/**
-** CAPI3REF: Column Names In A Result Set
-*/
-const(char)* sqlite3_column_name(sqlite3_stmt*, int N);
-/// Ditto
-const(void)* sqlite3_column_name16(sqlite3_stmt*, int N);
-
-/**
-** CAPI3REF: Source Of Data In A Query Result
-*/
-const(char)* sqlite3_column_database_name(sqlite3_stmt*,int);
-/// Ditto
-const(void)* sqlite3_column_database_name16(sqlite3_stmt*,int);
-/// Ditto
-const(char)* sqlite3_column_table_name(sqlite3_stmt*,int);
-/// Ditto
-const (void)* sqlite3_column_table_name16(sqlite3_stmt*,int);
-/// Ditto
-const (char)* sqlite3_column_origin_name(sqlite3_stmt*,int);
-/// Ditto
-const (void)* sqlite3_column_origin_name16(sqlite3_stmt*,int);
-
-/**
-** CAPI3REF: Declared Datatype Of A Query Result
-*/
-const (char)* sqlite3_column_decltype(sqlite3_stmt*,int);
-/// Ditto
-const (void)* sqlite3_column_decltype16(sqlite3_stmt*,int);
-
-/**
-** CAPI3REF: Evaluate An SQL Statement
-*/
-int sqlite3_step(sqlite3_stmt*);
-
-/**
-** CAPI3REF: Number of columns in a result set
-*/
-int sqlite3_data_count(sqlite3_stmt *pStmt);
-
-/**
-** CAPI3REF: Fundamental Datatypes
-*/
-enum
-{
- SQLITE_INTEGER = 1,
- SQLITE_FLOAT = 2,
- SQLITE_BLOB = 4,
- SQLITE_NULL = 5,
- SQLITE3_TEXT = 3
-}
-
-/**
-** CAPI3REF: Result Values From A Query
-*/
-const (void)* sqlite3_column_blob(sqlite3_stmt*, int iCol);
-/// Ditto
-int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
-/// Ditto
-int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
-/// Ditto
-double sqlite3_column_double(sqlite3_stmt*, int iCol);
-/// Ditto
-int sqlite3_column_int(sqlite3_stmt*, int iCol);
-/// Ditto
-sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
-/// Ditto
-const (char)* sqlite3_column_text(sqlite3_stmt*, int iCol);
-/// Ditto
-const (void)* sqlite3_column_text16(sqlite3_stmt*, int iCol);
-/// Ditto
-int sqlite3_column_type(sqlite3_stmt*, int iCol);
-/// Ditto
-sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
-
-/**
-** CAPI3REF: Destroy A Prepared Statement Object
-*/
-int sqlite3_finalize(sqlite3_stmt *pStmt);
-
-/**
-** CAPI3REF: Reset A Prepared Statement Object
-*/
-int sqlite3_reset(sqlite3_stmt *pStmt);
-
-/**
-** CAPI3REF: Create Or Redefine SQL Functions
-*/
-int sqlite3_create_function(
- sqlite3 *db,
- const(char)*zFunctionName,
- int nArg,
- int eTextRep,
- void *pApp,
- void function (sqlite3_context*,int,sqlite3_value**) xFunc,
- void function (sqlite3_context*,int,sqlite3_value**) xStep,
- void function (sqlite3_context*) xFinal
-);
-/// Ditto
-int sqlite3_create_function16(
- sqlite3 *db,
- const(void)*zFunctionName,
- int nArg,
- int eTextRep,
- void *pApp,
- void function (sqlite3_context*,int,sqlite3_value**) xFunc,
- void function (sqlite3_context*,int,sqlite3_value**) xStep,
- void function (sqlite3_context*) xFinal
-);
-/// Ditto
-int sqlite3_create_function_v2(
- sqlite3 *db,
- const(char)*zFunctionName,
- int nArg,
- int eTextRep,
- void *pApp,
- void function (sqlite3_context*,int,sqlite3_value**) xFunc,
- void function (sqlite3_context*,int,sqlite3_value**) xStep,
- void function (sqlite3_context*) xFinal,
- void function (void*) xDestroy
-);
-
-/**
-** CAPI3REF: Text Encodings
-**
-** These constant define integer codes that represent the various
-** text encodings supported by SQLite.
-*/
-enum
-{
- SQLITE_UTF8 = 1,
- SQLITE_UTF16LE = 2,
- SQLITE_UTF16BE = 3
-}
-/// Ditto
-enum
-{
- SQLITE_UTF16 = 4, /** Use native byte order */
- SQLITE_ANY = 5, /** sqlite3_create_function only */
- SQLITE_UTF16_ALIGNED = 8 /** sqlite3_create_collation only */
-}
-
-/**
-** CAPI3REF: Function Flags
-*/
-enum SQLITE_DETERMINISTIC = 0x800;
-
-/**
-** CAPI3REF: Deprecated Functions
-*/
-deprecated int sqlite3_aggregate_count(sqlite3_context*);
-deprecated int sqlite3_expired(sqlite3_stmt*);
-deprecated int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
-deprecated int sqlite3_global_recover();
-deprecated void sqlite3_thread_cleanup();
-deprecated int sqlite3_memory_alarm(void function(void*,sqlite3_int64,int),void*,sqlite3_int64);
-
-/**
-** CAPI3REF: Obtaining SQL Function Parameter Values
-*/
-const (void)* sqlite3_value_blob(sqlite3_value*);
-/// Ditto
-int sqlite3_value_bytes(sqlite3_value*);
-/// Ditto
-int sqlite3_value_bytes16(sqlite3_value*);
-/// Ditto
-double sqlite3_value_double(sqlite3_value*);
-/// Ditto
-int sqlite3_value_int(sqlite3_value*);
-/// Ditto
-sqlite3_int64 sqlite3_value_int64(sqlite3_value*);
-/// Ditto
-const (char)* sqlite3_value_text(sqlite3_value*);
-/// Ditto
-const (void)* sqlite3_value_text16(sqlite3_value*);
-/// Ditto
-const (void)* sqlite3_value_text16le(sqlite3_value*);
-/// Ditto
-const (void)* sqlite3_value_text16be(sqlite3_value*);
-/// Ditto
-int sqlite3_value_type(sqlite3_value*);
-/// Ditto
-int sqlite3_value_numeric_type(sqlite3_value*);
-
-/*
-** CAPI3REF: Finding The Subtype Of SQL Values
-*/
-uint sqlite3_value_subtype(sqlite3_value*);
-
-/*
-** CAPI3REF: Copy And Free SQL Values
-*/
-sqlite3_value* sqlite3_value_dup(const sqlite3_value*);
-void sqlite3_value_free(sqlite3_value*);
-
-/**
-** CAPI3REF: Obtain Aggregate Function Context
-*/
-void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
-
-/**
-** CAPI3REF: User Data For Functions
-*/
-void *sqlite3_user_data(sqlite3_context*);
-
-/**
-** CAPI3REF: Database Connection For Functions
-*/
-sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
-
-/**
-** CAPI3REF: Function Auxiliary Data
-*/
-void *sqlite3_get_auxdata(sqlite3_context*, int N);
-/// Ditto
-void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void function (void*));
-
-
-/**
-** CAPI3REF: Constants Defining Special Destructor Behavior
-*/
-alias sqlite3_destructor_type = void function (void*);
-/// Ditto
-enum
-{
- SQLITE_STATIC = (cast(sqlite3_destructor_type) 0),
- SQLITE_TRANSIENT = (cast (sqlite3_destructor_type) -1)
-}
-
-/**
-** CAPI3REF: Setting The Result Of An SQL Function
-*/
-void sqlite3_result_blob(sqlite3_context*, const void*, int, void function(void*));
-/// Ditto
-void sqlite3_result_blob64(sqlite3_context*,const void*,sqlite3_uint64,void function(void*));
-/// Ditto
-void sqlite3_result_double(sqlite3_context*, double);
-/// Ditto
-void sqlite3_result_error(sqlite3_context*, const char*, int);
-/// Ditto
-void sqlite3_result_error16(sqlite3_context*, const void*, int);
-/// Ditto
-void sqlite3_result_error_toobig(sqlite3_context*);
-/// Ditto
-void sqlite3_result_error_nomem(sqlite3_context*);
-/// Ditto
-void sqlite3_result_error_code(sqlite3_context*, int);
-/// Ditto
-void sqlite3_result_int(sqlite3_context*, int);
-/// Ditto
-void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
-/// Ditto
-void sqlite3_result_null(sqlite3_context*);
-/// Ditto
-void sqlite3_result_text(sqlite3_context*, const char*, int, void function(void*));
-/// Ditto
-void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64,void function(void*), ubyte encoding);
-/// Ditto
-void sqlite3_result_text16(sqlite3_context*, const void*, int, void function(void*));
-/// Ditto
-void sqlite3_result_text16le(sqlite3_context*, const void*, int, void function(void*));
-/// Ditto
-void sqlite3_result_text16be(sqlite3_context*, const void*, int, void function(void*));
-/// Ditto
-void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
-/// Ditto
-void sqlite3_result_zeroblob(sqlite3_context*, int n);
-/// Ditto
-int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
-
-/*
-** CAPI3REF: Setting The Subtype Of An SQL Function
-*/
-void sqlite3_result_subtype(sqlite3_context*,uint);
-
-/**
-** CAPI3REF: Define New Collating Sequences
-*/
-int sqlite3_create_collation(
- sqlite3*,
- const(char)*zName,
- int eTextRep,
- void *pArg,
- int function (void*,int,const void*,int,const void*) xCompare
-);
-/// Ditto
-int sqlite3_create_collation_v2(
- sqlite3*,
- const(char)*zName,
- int eTextRep,
- void *pArg,
- int function (void*,int,const void*,int,const void*) xCompare,
- void function (void*) xDestroy
-);
-/// Ditto
-int sqlite3_create_collation16(
- sqlite3*,
- const(void)*zName,
- int eTextRep,
- void *pArg,
- int function (void*,int,const void*,int,const void*) xCompare
-);
-
-/**
-** CAPI3REF: Collation Needed Callbacks
-*/
-int sqlite3_collation_needed(
- sqlite3*,
- void*,
- void function (void*,sqlite3*,int eTextRep,const char*)
-);
-/// Ditto
-int sqlite3_collation_needed16(
- sqlite3*,
- void*,
- void function (void*,sqlite3*,int eTextRep,const void*)
-);
-
-///
-int sqlite3_key(
- sqlite3 *db, /** Database to be rekeyed */
- const(void)*pKey, int nKey /** The key */
-);
-/// Ditto
-int sqlite3_key_v2(
- sqlite3 *db, /* Database to be rekeyed */
- const(char)* zDbName, /* Name of the database */
- const(void)* pKey, int nKey /* The key */
-);
-
-/**
-** Change the key on an open database. If the current database is not
-** encrypted, this routine will encrypt it. If pNew == 0 or nNew == 0, the
-** database is decrypted.
-**
-** The code to implement this API is not available in the public release
-** of SQLite.
-*/
-int sqlite3_rekey(
- sqlite3 *db, /** Database to be rekeyed */
- const(void)*pKey, int nKey /** The new key */
-);
-int sqlite3_rekey_v2(
- sqlite3 *db, /* Database to be rekeyed */
- const(char)* zDbName, /* Name of the database */
- const(void)* pKey, int nKey /* The new key */
-);
-
-/**
-** Specify the activation key for a SEE database. Unless
-** activated, none of the SEE routines will work.
-*/
-void sqlite3_activate_see(
- const(char)*zPassPhrase /** Activation phrase */
-);
-
-/**
-** Specify the activation key for a CEROD database. Unless
-** activated, none of the CEROD routines will work.
-*/
-void sqlite3_activate_cerod(
- const(char)*zPassPhrase /** Activation phrase */
-);
-
-/**
-** CAPI3REF: Suspend Execution For A Short Time
-*/
-int sqlite3_sleep(int);
-
-/**
-** CAPI3REF: Name Of The Folder Holding Temporary Files
-*/
-extern char *sqlite3_temp_directory;
-
-/**
-** CAPI3REF: Name Of The Folder Holding Database Files
-*/
-extern char *sqlite3_data_directory;
-
-/**
-** CAPI3REF: Test For Auto-Commit Mode
-*/
-int sqlite3_get_autocommit(sqlite3*);
-
-/**
-** CAPI3REF: Find The Database Handle Of A Prepared Statement
-*/
-sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
-
-/**
-** CAPI3REF: Return The Filename For A Database Connection
-*/
-const(char)* sqlite3_db_filename(sqlite3 *db, const char* zDbName);
-
-/**
-** CAPI3REF: Determine if a database is read-only
-*/
-int sqlite3_db_readonly(sqlite3 *db, const char * zDbName);
-
-/*
-** CAPI3REF: Find the next prepared statement
-*/
-sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
-
-/**
-** CAPI3REF: Commit And Rollback Notification Callbacks
-*/
-void *sqlite3_commit_hook(sqlite3*, int function (void*), void*);
-/// Ditto
-void *sqlite3_rollback_hook(sqlite3*, void function (void *), void*);
-
-/**
-** CAPI3REF: Data Change Notification Callbacks
-*/
-void *sqlite3_update_hook(
- sqlite3*,
- void function (void *,int ,char *, char *, sqlite3_int64),
- void*
-);
-
-/**
-** CAPI3REF: Enable Or Disable Shared Pager Cache
-*/
-int sqlite3_enable_shared_cache(int);
-
-/**
-** CAPI3REF: Attempt To Free Heap Memory
-*/
-int sqlite3_release_memory(int);
-
-/**
-** CAPI3REF: Free Memory Used By A Database Connection
-*/
-int sqlite3_db_release_memory(sqlite3*);
-
-/*
-** CAPI3REF: Impose A Limit On Heap Size
-*/
-sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N);
-
-/**
-** CAPI3REF: Deprecated Soft Heap Limit Interface
-*/
-deprecated void sqlite3_soft_heap_limit(int N);
-
-/**
-** CAPI3REF: Extract Metadata About A Column Of A Table
-*/
-int sqlite3_table_column_metadata(
- sqlite3 *db, /** Connection handle */
- const(char)*zDbName, /** Database name or NULL */
- const(char)*zTableName, /** Table name */
- const(char)*zColumnName, /** Column name */
- char **pzDataType, /** OUTPUT: Declared data type */
- char **pzCollSeq, /** OUTPUT: Collation sequence name */
- int *pNotNull, /** OUTPUT: True if NOT NULL constraint exists */
- int *pPrimaryKey, /** OUTPUT: True if column part of PK */
- int *pAutoinc /** OUTPUT: True if column is auto-increment */
-);
-
-/**
-** CAPI3REF: Load An Extension
-*/
-int sqlite3_load_extension(
- sqlite3 *db, /** Load the extension into this database connection */
- const(char)*zFile, /** Name of the shared library containing extension */
- const(char)*zProc, /** Entry point. Derived from zFile if 0 */
- char **pzErrMsg /** Put error message here if not 0 */
-);
-
-/**
-** CAPI3REF: Enable Or Disable Extension Loading
-*/
-int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
-
-/**
-** CAPI3REF: Automatically Load Statically Linked Extensions
-*/
-int sqlite3_auto_extension(void function () xEntryPoint);
-
-/**
-** CAPI3REF: Cancel Automatic Extension Loading
-*/
-int sqlite3_cancel_auto_extension(void function() xEntryPoint);
-
-/**
-** CAPI3REF: Reset Automatic Extension Loading
-*/
-void sqlite3_reset_auto_extension();
-
-/**
-** The interface to the virtual-table mechanism is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/**
-** CAPI3REF: Virtual Table Object
-*/
-
-alias mapFunction = void function (sqlite3_context*,int,sqlite3_value**);
-
-/// Ditto
-struct sqlite3_module
-{
- int iVersion;
- int function (sqlite3*, void *pAux,
- int argc, const char **argv,
- sqlite3_vtab **ppVTab, char**) xCreate;
- int function (sqlite3*, void *pAux,
- int argc, const char **argv,
- sqlite3_vtab **ppVTab, char**) xConnect;
- int function (sqlite3_vtab *pVTab, sqlite3_index_info*) xBestIndex;
- int function (sqlite3_vtab *pVTab) xDisconnect;
- int function (sqlite3_vtab *pVTab) xDestroy;
- int function (sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) xOpen;
- int function (sqlite3_vtab_cursor*) xClose;
- int function (sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
- int argc, sqlite3_value **argv) xFilter;
- int function (sqlite3_vtab_cursor*) xNext;
- int function (sqlite3_vtab_cursor*) xEof;
- int function (sqlite3_vtab_cursor*, sqlite3_context*, int) xColumn;
- int function (sqlite3_vtab_cursor*, sqlite3_int64 *pRowid) xRowid;
- int function (sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *) xUpdate;
- int function (sqlite3_vtab *pVTab) xBegin;
- int function (sqlite3_vtab *pVTab) xSync;
- int function (sqlite3_vtab *pVTab) xCommit;
- int function (sqlite3_vtab *pVTab) xRollback;
- int function (sqlite3_vtab *pVtab, int nArg, const char *zName,
- mapFunction*,
- void **ppArg) xFindFunction;
- int function (sqlite3_vtab *pVtab, const char *zNew) xRename;
- int function (sqlite3_vtab *pVTab, int) xSavepoint;
- int function (sqlite3_vtab *pVTab, int) xRelease;
- int function (sqlite3_vtab *pVTab, int) xRollbackTo;
-}
-
-/**
-** CAPI3REF: Virtual Table Indexing Information
-*/
-struct sqlite3_index_info
-{
- struct sqlite3_index_constraint
- {
- int iColumn; /** Column on left-hand side of constraint */
- char op; /** Constraint operator */
- char usable; /** True if this constraint is usable */
- int iTermOffset; /** Used internally - xBestIndex should ignore */
- }
- struct sqlite3_index_orderby
- {
- int iColumn; /** Column number */
- char desc; /** True for DESC. False for ASC. */
- }
- struct sqlite3_index_constraint_usage
- {
- int argvIndex; /** if >0, constraint is part of argv to xFilter */
- char omit; /** Do not code a test for this constraint */
- }
- /* Inputs */
- int nConstraint; /** Number of entries in aConstraint */
- sqlite3_index_constraint* aConstraint; /** Table of WHERE clause constraints */
- int nOrderBy; /** Number of terms in the ORDER BY clause */
- sqlite3_index_orderby *aOrderBy; /** The ORDER BY clause */
- /* Outputs */
- sqlite3_index_constraint_usage *aConstraintUsage;
- int idxNum; /** Number used to identify the index */
- char *idxStr; /** String, possibly obtained from sqlite3_malloc */
- int needToFreeIdxStr; /** Free idxStr using sqlite3_free() if true */
- int orderByConsumed; /** True if output is already ordered */
- double estimatedCost; /** Estimated cost of using this index */
- sqlite3_int64 estimatedRows;
- int idxFlags;
- sqlite3_uint64 colUsed;
-}
-
-/**
-** CAPI3REF: Virtual Table Constraint Operator Codes
-*/
-enum
-{
- SQLITE_INDEX_SCAN_UNIQUE = 1,
- SQLITE_INDEX_CONSTRAINT_EQ = 2,
- SQLITE_INDEX_CONSTRAINT_GT = 4,
- SQLITE_INDEX_CONSTRAINT_LE = 8,
- SQLITE_INDEX_CONSTRAINT_LT = 16,
- SQLITE_INDEX_CONSTRAINT_GE = 32,
- SQLITE_INDEX_CONSTRAINT_MATCH = 64,
- SQLITE_INDEX_CONSTRAINT_LIKE = 65,
- SQLITE_INDEX_CONSTRAINT_GLOB = 66,
- SQLITE_INDEX_CONSTRAINT_REGEXP = 67,
-}
-
-/**
-** CAPI3REF: Register A Virtual Table Implementation
-*/
-int sqlite3_create_module(
- sqlite3 *db, /* SQLite connection to register module with */
- const(char)*zName, /* Name of the module */
- const(sqlite3_module)*p, /* Methods for the module */
- void *pClientData /* Client data for xCreate/xConnect */
-);
-/// Ditto
-int sqlite3_create_module_v2(
- sqlite3 *db, /* SQLite connection to register module with */
- const(char)*zName, /* Name of the module */
- const(sqlite3_module)*p, /* Methods for the module */
- void *pClientData, /* Client data for xCreate/xConnect */
- void function (void*) xDestroy /* Module destructor function */
-);
-
-/**
-** CAPI3REF: Virtual Table Instance Object
-*/
-struct sqlite3_vtab
-{
- const(sqlite3_module)*pModule; /** The module for this virtual table */
- int nRef; /** NO LONGER USED */
- char *zErrMsg; /** Error message from sqlite3_mprintf() */
- /* Virtual table implementations will typically add additional fields */
-}
-
-/**
-** CAPI3REF: Virtual Table Cursor Object
-*/
-struct sqlite3_vtab_cursor
-{
- sqlite3_vtab *pVtab; /** Virtual table of this cursor */
- /* Virtual table implementations will typically add additional fields */
-}
-
-/**
-** CAPI3REF: Declare The Schema Of A Virtual Table
-*/
-int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
-
-/**
-** CAPI3REF: Overload A Function For A Virtual Table
-*/
-int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
-
-/**
-** The interface to the virtual-table mechanism defined above (back up
-** to a comment remarkably similar to this one) is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
-** CAPI3REF: A Handle To An Open BLOB
-*/
-struct sqlite3_blob;
-
-/**
-** CAPI3REF: Open A BLOB For Incremental I/O
-*/
-int sqlite3_blob_open(
- sqlite3*,
- const(char)* zDb,
- const(char)* zTable,
- const(char)* zColumn,
- sqlite3_int64 iRow,
- int flags,
- sqlite3_blob **ppBlob
-);
-
-/**
-** CAPI3REF: Move a BLOB Handle to a New Row
-*/
-int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
-
-/**
-** CAPI3REF: Close A BLOB Handle
-*/
-int sqlite3_blob_close(sqlite3_blob *);
-
-/**
-** CAPI3REF: Return The Size Of An Open BLOB
-*/
-int sqlite3_blob_bytes(sqlite3_blob *);
-
-/**
-** CAPI3REF: Read Data From A BLOB Incrementally
-*/
-int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
-
-/**
-** CAPI3REF: Write Data Into A BLOB Incrementally
-*/
-int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset);
-
-/**
-** CAPI3REF: Virtual File System Objects
-*/
-sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName);
-/// Ditto
-int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt);
-/// Ditto
-int sqlite3_vfs_unregister(sqlite3_vfs*);
-
-/**
-** CAPI3REF: Mutexes
-*/
-sqlite3_mutex *sqlite3_mutex_alloc(int);
-/// Ditto
-void sqlite3_mutex_free(sqlite3_mutex*);
-/// Ditto
-void sqlite3_mutex_enter(sqlite3_mutex*);
-/// Ditto
-int sqlite3_mutex_try(sqlite3_mutex*);
-/// Ditto
-void sqlite3_mutex_leave(sqlite3_mutex*);
-
-/**
-** CAPI3REF: Mutex Methods Object
-*/
-struct sqlite3_mutex_methods
-{
- int function () xMutexInit;
- int function () xMutexEnd;
- sqlite3_mutex* function (int) xMutexAlloc;
- void function (sqlite3_mutex *) xMutexFree;
- void function (sqlite3_mutex *) xMutexEnter;
- int function (sqlite3_mutex *) xMutexTry;
- void function (sqlite3_mutex *) xMutexLeave;
- int function (sqlite3_mutex *) xMutexHeld;
- int function (sqlite3_mutex *) xMutexNotheld;
-}
-
-/**
-** CAPI3REF: Mutex Verification Routines
-*/
-
-//#ifndef NDEBUG
-int sqlite3_mutex_held(sqlite3_mutex*);
-/// Ditto
-int sqlite3_mutex_notheld(sqlite3_mutex*);
-//#endif
-
-/**
-** CAPI3REF: Mutex Types
-*/
-enum
-{
- SQLITE_MUTEX_FAST = 0,
- SQLITE_MUTEX_RECURSIVE = 1,
- SQLITE_MUTEX_STATIC_MASTER = 2,
- SQLITE_MUTEX_STATIC_MEM = 3, /** sqlite3_malloc() */
- SQLITE_MUTEX_STATIC_MEM2 = 4, /** NOT USED */
- SQLITE_MUTEX_STATIC_OPEN = 4, /** sqlite3BtreeOpen() */
- SQLITE_MUTEX_STATIC_PRNG = 5, /** sqlite3_random() */
- SQLITE_MUTEX_STATIC_LRU = 6, /** lru page list */
- SQLITE_MUTEX_STATIC_LRU2 = 7, /** NOT USED */
- SQLITE_MUTEX_STATIC_PMEM = 7, /** sqlite3PageMalloc() */
- SQLITE_MUTEX_STATIC_APP1 = 8, /** For use by application */
- SQLITE_MUTEX_STATIC_APP2 = 9, /** For use by application */
- SQLITE_MUTEX_STATIC_APP3 = 10, /** For use by application */
- SQLITE_MUTEX_STATIC_VFS1 = 11, /** For use by built-in VFS */
- SQLITE_MUTEX_STATIC_VFS2 = 12, /** For use by extension VFS */
- SQLITE_MUTEX_STATIC_VFS3 = 13, /** For use by application VFS */
-}
-
-/**
-** CAPI3REF: Retrieve the mutex for a database connection
-*/
-sqlite3_mutex *sqlite3_db_mutex(sqlite3*);
-
-/**
-** CAPI3REF: Low-Level Control Of Database Files
-*/
-int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*);
-
-/**
-** CAPI3REF: Testing Interface
-*/
-int sqlite3_test_control(int op, ...);
-
-/**
-** CAPI3REF: Testing Interface Operation Codes
-*/
-enum
-{
- SQLITE_TESTCTRL_FIRST = 5,
- SQLITE_TESTCTRL_PRNG_SAVE = 5,
- SQLITE_TESTCTRL_PRNG_RESTORE = 6,
- SQLITE_TESTCTRL_PRNG_RESET = 7,
- SQLITE_TESTCTRL_BITVEC_TEST = 8,
- SQLITE_TESTCTRL_FAULT_INSTALL = 9,
- SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS = 10,
- SQLITE_TESTCTRL_PENDING_BYTE = 11,
- SQLITE_TESTCTRL_ASSERT = 12,
- SQLITE_TESTCTRL_ALWAYS = 13,
- SQLITE_TESTCTRL_RESERVE = 14,
- SQLITE_TESTCTRL_OPTIMIZATIONS = 15,
- SQLITE_TESTCTRL_ISKEYWORD = 16,
- SQLITE_TESTCTRL_SCRATCHMALLOC = 17,
- SQLITE_TESTCTRL_LOCALTIME_FAULT = 18,
- SQLITE_TESTCTRL_EXPLAIN_STMT = 19,
- SQLITE_TESTCTRL_NEVER_CORRUPT = 20,
- SQLITE_TESTCTRL_VDBE_COVERAGE = 21,
- SQLITE_TESTCTRL_BYTEORDER = 22,
- SQLITE_TESTCTRL_ISINIT = 23,
- SQLITE_TESTCTRL_SORTER_MMAP = 24,
- SQLITE_TESTCTRL_IMPOSTER = 25,
- SQLITE_TESTCTRL_LAST = 25,
-}
-
-/**
-** CAPI3REF: SQLite Runtime Status
-*/
-int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
-/// Ditto
-int sqlite3_status64(int op, long *pCurrent, long *pHighwater, int resetFlag);
-
-/**
-** CAPI3REF: Status Parameters
-*/
-enum
-{
- SQLITE_STATUS_MEMORY_USED = 0,
- SQLITE_STATUS_PAGECACHE_USED = 1,
- SQLITE_STATUS_PAGECACHE_OVERFLOW = 2,
- SQLITE_STATUS_SCRATCH_USED = 3,
- SQLITE_STATUS_SCRATCH_OVERFLOW = 4,
- SQLITE_STATUS_MALLOC_SIZE = 5,
- SQLITE_STATUS_PARSER_STACK = 6,
- SQLITE_STATUS_PAGECACHE_SIZE = 7,
- SQLITE_STATUS_SCRATCH_SIZE = 8,
- SQLITE_STATUS_MALLOC_COUNT = 9
-}
-
-/**
-** CAPI3REF: Database Connection Status
-*/
-int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
-
-/**
-** CAPI3REF: Status Parameters for database connections
-*/
-enum
-{
- SQLITE_DBSTATUS_LOOKASIDE_USED = 0,
- SQLITE_DBSTATUS_CACHE_USED = 1,
- SQLITE_DBSTATUS_SCHEMA_USED = 2,
- SQLITE_DBSTATUS_STMT_USED = 3,
- SQLITE_DBSTATUS_LOOKASIDE_HIT = 4,
- SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE = 5,
- SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL = 6,
- SQLITE_DBSTATUS_CACHE_HIT = 7,
- SQLITE_DBSTATUS_CACHE_MISS = 8,
- SQLITE_DBSTATUS_CACHE_WRITE = 9,
- SQLITE_DBSTATUS_DEFERRED_FKS = 10,
- SQLITE_DBSTATUS_MAX = 10 /** Largest defined DBSTATUS */
-}
-
-/**
-** CAPI3REF: Prepared Statement Status
-*/
-int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
-
-/**
-** CAPI3REF: Status Parameters for prepared statements
-*/
-enum
-{
- SQLITE_STMTSTATUS_FULLSCAN_STEP = 1,
- SQLITE_STMTSTATUS_SORT = 2,
- SQLITE_STMTSTATUS_AUTOINDEX = 3,
- SQLITE_STMTSTATUS_VM_STEP = 4
-}
-
-/**
-** CAPI3REF: Custom Page Cache Object
-*/
-struct sqlite3_pcache;
-
-/**
-** CAPI3REF: Custom Page Cache Object
-*/
-struct sqlite3_pcache_page
-{
- void *pBuf; /* The content of the page */
- void *pExtra; /* Extra information associated with the page */
-}
-
-/**
-** CAPI3REF: Application Defined Page Cache.
-*/
-struct sqlite3_pcache_methods2
-{
- int iVersion;
- void *pArg;
- int function(void*) xInit;
- void function(void*) xShutdown;
- sqlite3_pcache * function(int szPage, int szExtra, int bPurgeable) xCreate;
- void function(sqlite3_pcache*, int nCachesize) xCachesize;
- int function(sqlite3_pcache*) xPagecount;
- sqlite3_pcache_page * function(sqlite3_pcache*, uint key, int createFlag) xFetch;
- void function(sqlite3_pcache*, sqlite3_pcache_page*, int discard) xUnpin;
- void function(sqlite3_pcache*, sqlite3_pcache_page*,
- uint oldKey, uint newKey) xRekey;
- void function(sqlite3_pcache*, uint iLimit) xTruncate;
- void function(sqlite3_pcache*) xDestroy;
- void function(sqlite3_pcache*) xShrink;
-}
-
-struct sqlite3_pcache_methods
-{
- void *pArg;
- int function (void*) xInit;
- void function (void*) xShutdown;
- sqlite3_pcache* function (int szPage, int bPurgeable) xCreate;
- void function (sqlite3_pcache*, int nCachesize) xCachesize;
- int function (sqlite3_pcache*) xPagecount;
- void* function (sqlite3_pcache*, uint key, int createFlag) xFetch;
- void function (sqlite3_pcache*, void*, int discard) xUnpin;
- void function (sqlite3_pcache*, void*, uint oldKey, uint newKey) xRekey;
- void function (sqlite3_pcache*, uint iLimit) xTruncate;
- void function (sqlite3_pcache*) xDestroy;
-}
-
-/**
-** CAPI3REF: Online Backup Object
-*/
-struct sqlite3_backup;
-
-/**
-** CAPI3REF: Online Backup API.
-*/
-sqlite3_backup *sqlite3_backup_init(
- sqlite3 *pDest, /** Destination database handle */
- const(char)*zDestName, /** Destination database name */
- sqlite3 *pSource, /** Source database handle */
- const(char)*zSourceName /** Source database name */
-);
-/// Ditto
-int sqlite3_backup_step(sqlite3_backup *p, int nPage);
-/// Ditto
-int sqlite3_backup_finish(sqlite3_backup *p);
-/// Ditto
-int sqlite3_backup_remaining(sqlite3_backup *p);
-/// Ditto
-int sqlite3_backup_pagecount(sqlite3_backup *p);
-
-/**
-** CAPI3REF: Unlock Notification
-*/
-int sqlite3_unlock_notify(
- sqlite3 *pBlocked, /** Waiting connection */
- void function (void **apArg, int nArg) xNotify, /** Callback function to invoke */
- void *pNotifyArg /** Argument to pass to xNotify */
-);
-
-/**
-** CAPI3REF: String Comparison
-*/
-int sqlite3_stricmp(const char * , const char * );
-int sqlite3_strnicmp(const char * , const char * , int);
-
-/*
-** CAPI3REF: String Globbing
-*
-*/
-int sqlite3_strglob(const(char)* zGlob, const(char)* zStr);
-
-/*
-** CAPI3REF: String LIKE Matching
-*/
-int sqlite3_strlike(const(char)* zGlob, const(char)* zStr, uint cEsc);
-
-/**
-** CAPI3REF: Error Logging Interface
-*/
-void sqlite3_log(int iErrCode, const char *zFormat, ...);
-
-/**
-** CAPI3REF: Write-Ahead Log Commit Hook
-*/
-void *sqlite3_wal_hook(
- sqlite3*,
- int function (void *,sqlite3*,const char*,int),
- void*
-);
-
-/**
-** CAPI3REF: Configure an auto-checkpoint
-*/
-int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
-
-/**
-** CAPI3REF: Checkpoint a database
-*/
-int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
-
-/**
-** CAPI3REF: Checkpoint a database
-*/
-int sqlite3_wal_checkpoint_v2(
- sqlite3 *db, /** Database handle */
- const(char)*zDb, /** Name of attached database (or NULL) */
- int eMode, /** SQLITE_CHECKPOINT_* value */
- int *pnLog, /** OUT: Size of WAL log in frames */
- int *pnCkpt /** OUT: Total number of frames checkpointed */
-);
-
-/**
-** CAPI3REF: Checkpoint operation parameters
-*/
-enum
-{
- SQLITE_CHECKPOINT_PASSIVE = 0,
- SQLITE_CHECKPOINT_FULL = 1,
- SQLITE_CHECKPOINT_RESTART = 2,
- SQLITE_CHECKPOINT_TRUNCATE = 3,
-}
-
-/*
-** CAPI3REF: Virtual Table Interface Configuration
-*/
-int sqlite3_vtab_config(sqlite3*, int op, ...);
-
-/**
-** CAPI3REF: Virtual Table Configuration Options
-*/
-enum SQLITE_VTAB_CONSTRAINT_SUPPORT = 1;
-
-/*
-** 2010 August 30
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-*/
-
-//#ifndef _SQLITE3RTREE_H_
-//#define _SQLITE3RTREE_H_
-
-
-/*
-** CAPI3REF: Determine The Virtual Table Conflict Policy
-*/
-int sqlite3_vtab_on_conflict(sqlite3 *);
-
-/*
-** CAPI3REF: Conflict resolution modes
-*/
-enum
-{
- SQLITE_ROLLBACK = 1,
- SQLITE_FAIL = 3,
- SQLITE_REPLACE = 5
-}
-
-/*
-** CAPI3REF: Prepared Statement Scan Status Opcodes
-*/
-enum
-{
- SQLITE_SCANSTAT_NLOOP = 0,
- SQLITE_SCANSTAT_NVISIT = 1,
- SQLITE_SCANSTAT_EST = 2,
- SQLITE_SCANSTAT_NAME = 3,
- SQLITE_SCANSTAT_EXPLAIN = 4,
- SQLITE_SCANSTAT_SELECTID = 5,
-}
-
-/*
-** CAPI3REF: Prepared Statement Scan Status
-*/
-int sqlite3_stmt_scanstatus(sqlite3_stmt *pStmt, int idx, int iScanStatusOp, void *pOut);
-
-/*
-** CAPI3REF: Zero Scan-Status Counters
-*/
-void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *);
-
-/*
-** CAPI3REF: Flush caches to disk mid-transaction
-*/
-int sqlite3_db_cacheflush(sqlite3 *);
-
-struct sqlite3_snapshot;
-
-/*
-** CAPI3REF: Record A Database Snapshot
-*/
-int sqlite3_snapshot_get(sqlite3 *db, char *zSchema, sqlite3_snapshot **ppSnapshot);
-
-/*
-** CAPI3REF: Start a read transaction on an historical snapshot
-*/
-int sqlite3_snapshot_open(sqlite3 *db, char *zSchema, sqlite3_snapshot *pSnapshot);
-
-/*
-** CAPI3REF: Destroy a snapshot
-*/
-void sqlite3_snapshot_free(sqlite3_snapshot *);
-
-/**
-** Register a geometry callback named zGeom that can be used as part of an
-** R-Tree geometry query as follows:
-**
-** SELECT ... FROM $(LT)rtree$(GT) WHERE $(LT)rtree col$(GT) MATCH $zGeom(... params ...)
-*/
-int sqlite3_rtree_geometry_callback(
- sqlite3 *db,
- const(char)*zGeom,
- int function (sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes) xGeom,
- void *pContext
-);
-
-/**
-** A pointer to a structure of the following type is passed as the first
-** argument to callbacks registered using rtree_geometry_callback().
-*/
-struct sqlite3_rtree_geometry
-{
- void *pContext; /** Copy of pContext passed to s_r_g_c() */
- int nParam; /** Size of array aParam[] */
- double *aParam; /** Parameters passed to SQL geom function */
- void *pUser; /** Callback implementation user data */
- void function (void *) xDelUser; /** Called by SQLite to clean up pUser */
-}
-
-int sqlite3_rtree_query_callback(
- sqlite3 *db,
- const(char)* zQueryFunc,
- int function(sqlite3_rtree_query_info*) xQueryFunc,
- void *pContext,
- void function(void*) xDestructor
-);
-
-struct sqlite3_rtree_query_info
-{
- void *pContext; /* pContext from when function registered */
- int nParam; /* Number of function parameters */
- double*aParam; /* value of function parameters */
- void *pUser; /* callback can use this, if desired */
- void function(void*) xDelUser; /* function to free pUser */
- double*aCoord; /* Coordinates of node or entry to check */
- uint *anQueue; /* Number of pending entries in the queue */
- int nCoord; /* Number of coordinates */
- int iLevel; /* Level of current node or entry */
- int mxLevel; /* The largest iLevel value in the tree */
- sqlite3_int64 iRowid; /* Rowid for current entry */
- double rParentScore; /* Score of parent node */
- int eParentWithin; /* Visibility of parent node */
- int eWithin; /* OUT: Visiblity */
- double rScore; /* OUT: Write the score here */
- sqlite3_value **apSqlParam; /* Original SQL values of parameters */
-}
-
-enum
-{
- NOT_WITHIN = 0,
- PARTLY_WITHIN = 1,
- FULLY_WITHIN = 2
-}
-
-/******************************************************************************
-** Interfaces to extend FTS5.
-*/
-struct Fts5Context;
-/// Ditto
-alias fts5_extension_function = void function(
- const Fts5ExtensionApi *pApi,
- Fts5Context *pFts,
- sqlite3_context *pCtx,
- int nVal,
- sqlite3_value **apVal
-);
-/// Ditto
-struct Fts5PhraseIter
-{
- const(ubyte) *a;
- const(ubyte) *b;
-}
-/// Ditto
-struct Fts5ExtensionApi
-{
- int iVersion;
- void* function(Fts5Context*) xUserData;
- int function(Fts5Context*) xColumnCount;
- int function(Fts5Context*, sqlite3_int64 *pnRow) xRowCount;
- int function(Fts5Context*, int iCol, sqlite3_int64 *pnToken) xColumnTotalSize;
- int function(Fts5Context*,
- const char *pText, int nText,
- void *pCtx,
- int function(void*, int, const char*, int, int, int) xToken
- ) xTokenize;
- int function(Fts5Context*) xPhraseCount;
- int function(Fts5Context*, int iPhrase) xPhraseSize;
- int function(Fts5Context*, int *pnInst) xInstCount;
- int function(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff) xInst;
- sqlite3_int64 function(Fts5Context*) xRowid;
- int function(Fts5Context*, int iCol, const char **pz, int *pn) xColumnText;
- int function(Fts5Context*, int iCol, int *pnToken) xColumnSize;
- int function(Fts5Context*, int iPhrase, void *pUserData,
- int function(const Fts5ExtensionApi*,Fts5Context*,void*)
- ) xQueryPhrase;
- int function(Fts5Context*, void *pAux, void function(void*) xDelete) xSetAuxdata;
- void* function(Fts5Context*, int bClear) xGetAuxdata;
- void function(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*) xPhraseFirst;
- void function(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff) xPhraseNext;
-}
-/// Ditto
-struct Fts5Tokenizer;
-struct fts5_tokenizer
-{
- int function(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut) xCreate;
- void function(Fts5Tokenizer*) xDelete;
- int function(Fts5Tokenizer*,
- void *pCtx,
- int flags,
- const char *pText, int nText,
- int function(
- void *pCtx,
- int tflags,
- const char *pToken,
- int nToken,
- int iStart,
- int iEnd
- ) xToken
- ) xTokenize;
-}
-/// Ditto
-enum FTS5_TOKENIZE_QUERY = 0x0001;
-/// Ditto
-enum FTS5_TOKENIZE_PREFIX = 0x0002;
-/// Ditto
-enum FTS5_TOKENIZE_DOCUMENT = 0x0004;
-/// Ditto
-enum FTS5_TOKENIZE_AUX = 0x0008;
-/// Ditto
-enum FTS5_TOKEN_COLOCATED = 0x0001;
-/// Ditto
-struct fts5_api
-{
- int iVersion;
-
- int function(
- fts5_api *pApi,
- const char *zName,
- void *pContext,
- fts5_tokenizer *pTokenizer,
- void function(void*) xDestroy
- ) xCreateTokenizer;
-
- int function(
- fts5_api *pApi,
- const char *zName,
- void **ppContext,
- fts5_tokenizer *pTokenizer
- ) xFindTokenizer;
-
- int function(
- fts5_api *pApi,
- const char *zName,
- void *pContext,
- fts5_extension_function xFunction,
- void function(void*) xDestroy
- ) xCreateFunction;
-}
diff --git a/libphobos/src/etc/c/zlib.d b/libphobos/src/etc/c/zlib.d
index b3e9783389c..a1b20fedce9 100644
--- a/libphobos/src/etc/c/zlib.d
+++ b/libphobos/src/etc/c/zlib.d
@@ -38,10 +38,12 @@ import core.stdc.config;
*/
nothrow:
+@nogc:
extern (C):
-const char[] ZLIB_VERSION = "1.2.11";
-const ZLIB_VERNUM = 0x12b0;
+// Those are extern(D) as they should be mangled
+extern(D) immutable string ZLIB_VERSION = "1.2.11";
+extern(D) immutable ZLIB_VERNUM = 0x12b0;
/*
The 'zlib' compression library provides in-memory compression and
@@ -227,7 +229,8 @@ enum
}
/* The deflate compression method (the only one supported in this version) */
-const int Z_NULL = 0; /* for initializing zalloc, zfree, opaque */
+/// for initializing zalloc, zfree, opaque (extern(D) for mangling)
+extern(D) immutable void* Z_NULL = null;
/* basic functions */
diff --git a/libphobos/src/index.d b/libphobos/src/index.d
index 2792b7b0326..8613a3c2b66 100644
--- a/libphobos/src/index.d
+++ b/libphobos/src/index.d
@@ -3,9 +3,9 @@ Ddoc
$(P Phobos is the standard runtime library that comes with the D language
compiler.)
-$(P Generally, the $(D std) namespace is used for the main modules in the
-Phobos standard library. The $(D etc) namespace is used for external C/C++
-library bindings. The $(D core) namespace is used for low-level D runtime
+$(P Generally, the `std` namespace is used for the main modules in the
+Phobos standard library. The `etc` namespace is used for external C/C++
+library bindings. The `core` namespace is used for low-level D runtime
functions.)
$(P The following table is a quick reference guide for which Phobos modules to
@@ -41,7 +41,7 @@ $(BOOKTABLE ,
$(TD Convenient operations commonly used with built-in arrays.
Note that many common array operations are subsets of more generic
algorithms that work with arbitrary ranges, so they are found in
- $(D std.algorithm).
+ `std.algorithm`.
)
)
$(LEADINGROW Containers)
@@ -88,12 +88,12 @@ $(BOOKTABLE ,
$(TD Checked integral types.)
)
$(TR
- $(TDNW $(MREF std,digest,crc))
- $(TD Cyclic Redundancy Check (32-bit) implementation.)
+ $(TDNW $(MREF std,digest))
+ $(TD Compute digests such as md5, sha1 and crc32.)
)
$(TR
- $(TDNW $(MREF std,digest,digest))
- $(TD Compute digests such as md5, sha1 and crc32.)
+ $(TDNW $(MREF std,digest,crc))
+ $(TD Cyclic Redundancy Check (32-bit) implementation.)
)
$(TR
$(TDNW $(MREF std,digest,hmac))
@@ -444,12 +444,16 @@ $(BOOKTABLE ,
)
$(TR
$(TDNW $(MREF std,variant))
- $(TD Discriminated unions and algebraic types.)
+ $(TD Dynamically-typed variable that can hold a value of any type.)
)
$(TR
$(TDNW $(MREF core,bitop))
$(TD Low level bit manipulation.)
)
+ $(TR
+ $(TDNW $(MREF std,sumtype))
+ $(TD Type-safe discriminated union.)
+ )
$(LEADINGROW Vector programming)
$(TR
$(TDNW $(MREF core,simd))
diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d
index faa4d444fbd..c9525445ceb 100644
--- a/libphobos/src/std/algorithm/comparison.d
+++ b/libphobos/src/std/algorithm/comparison.d
@@ -1,48 +1,48 @@
// Written in the D programming language.
/**
This is a submodule of $(MREF std, algorithm).
-It contains generic _comparison algorithms.
+It contains generic comparison algorithms.
$(SCRIPT inhibitQuickIndex = 1;)
$(BOOKTABLE Cheat Sheet,
$(TR $(TH Function Name) $(TH Description))
$(T2 among,
Checks if a value is among a set of values, e.g.
- $(D if (v.among(1, 2, 3)) // `v` is 1, 2 or 3))
+ `if (v.among(1, 2, 3)) // `v` is 1, 2 or 3`)
$(T2 castSwitch,
- $(D (new A()).castSwitch((A a)=>1,(B b)=>2)) returns $(D 1).)
+ `(new A()).castSwitch((A a)=>1,(B b)=>2)` returns `1`.)
$(T2 clamp,
- $(D clamp(1, 3, 6)) returns $(D 3). $(D clamp(4, 3, 6)) returns $(D 4).)
+ `clamp(1, 3, 6)` returns `3`. `clamp(4, 3, 6)` returns `4`.)
$(T2 cmp,
- $(D cmp("abc", "abcd")) is $(D -1), $(D cmp("abc", "aba")) is $(D 1),
- and $(D cmp("abc", "abc")) is $(D 0).)
+ `cmp("abc", "abcd")` is `-1`, `cmp("abc", "aba")` is `1`,
+ and `cmp("abc", "abc")` is `0`.)
$(T2 either,
- Return first parameter $(D p) that passes an $(D if (p)) test, e.g.
- $(D either(0, 42, 43)) returns $(D 42).)
+ Return first parameter `p` that passes an `if (p)` test, e.g.
+ `either(0, 42, 43)` returns `42`.)
$(T2 equal,
Compares ranges for element-by-element equality, e.g.
- $(D equal([1, 2, 3], [1.0, 2.0, 3.0])) returns $(D true).)
+ `equal([1, 2, 3], [1.0, 2.0, 3.0])` returns `true`.)
$(T2 isPermutation,
- $(D isPermutation([1, 2], [2, 1])) returns $(D true).)
+ `isPermutation([1, 2], [2, 1])` returns `true`.)
$(T2 isSameLength,
- $(D isSameLength([1, 2, 3], [4, 5, 6])) returns $(D true).)
+ `isSameLength([1, 2, 3], [4, 5, 6])` returns `true`.)
$(T2 levenshteinDistance,
- $(D levenshteinDistance("kitten", "sitting")) returns $(D 3) by using
+ `levenshteinDistance("kitten", "sitting")` returns `3` by using
the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance,
- Levenshtein distance _algorithm).)
+ Levenshtein distance algorithm).)
$(T2 levenshteinDistanceAndPath,
- $(D levenshteinDistanceAndPath("kitten", "sitting")) returns
- $(D tuple(3, "snnnsni")) by using the
+ `levenshteinDistanceAndPath("kitten", "sitting")` returns
+ `tuple(3, "snnnsni")` by using the
$(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance,
- Levenshtein distance _algorithm).)
+ Levenshtein distance algorithm).)
$(T2 max,
- $(D max(3, 4, 2)) returns $(D 4).)
+ `max(3, 4, 2)` returns `4`.)
$(T2 min,
- $(D min(3, 4, 2)) returns $(D 2).)
+ `min(3, 4, 2)` returns `2`.)
$(T2 mismatch,
- $(D mismatch("oh hi", "ohayo")) returns $(D tuple(" hi", "ayo")).)
+ `mismatch("oh hi", "ohayo")` returns `tuple(" hi", "ayo")`.)
$(T2 predSwitch,
- $(D 2.predSwitch(1, "one", 2, "two", 3, "three")) returns $(D "two").)
+ `2.predSwitch(1, "one", 2, "two", 3, "three")` returns `"two"`.)
)
Copyright: Andrei Alexandrescu 2008-.
@@ -51,25 +51,25 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.com, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/algorithm/_comparison.d)
+Source: $(PHOBOSSRC std/algorithm/comparison.d)
Macros:
T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
*/
module std.algorithm.comparison;
-// FIXME
-import std.functional; // : unaryFun, binaryFun;
+import std.functional : unaryFun, binaryFun;
import std.range.primitives;
import std.traits;
-// FIXME
import std.meta : allSatisfy;
-import std.typecons; // : tuple, Tuple, Flag, Yes;
+import std.typecons : tuple, Tuple, Flag, Yes;
+
+import std.internal.attributes : betterC;
/**
-Find $(D value) _among $(D values), returning the 1-based index
-of the first matching value in $(D values), or $(D 0) if $(D value)
-is not _among $(D values). The predicate $(D pred) is used to
+Find `value` _among `values`, returning the 1-based index
+of the first matching value in `values`, or `0` if `value`
+is not _among `values`. The predicate `pred` is used to
compare values, and uses equality by default.
Params:
@@ -116,7 +116,7 @@ if (isExpressionTuple!values)
}
///
-@safe unittest
+@safe @nogc @betterC unittest
{
assert(3.among(1, 42, 24, 3, 2));
@@ -130,10 +130,10 @@ if (isExpressionTuple!values)
}
/**
-Alternatively, $(D values) can be passed at compile-time, allowing for a more
+Alternatively, `values` can be passed at compile-time, allowing for a more
efficient search, but one that only supports matching on equality:
*/
-@safe unittest
+@safe @nogc @betterC unittest
{
assert(3.among!(2, 3, 4));
assert("bar".among!("foo", "bar", "baz") == 2);
@@ -214,19 +214,19 @@ private template indexOfFirstOvershadowingChoiceOnLast(choices...)
Executes and returns one of a collection of handlers based on the type of the
switch object.
-The first choice that $(D switchObject) can be casted to the type
-of argument it accepts will be called with $(D switchObject) casted to that
-type, and the value it'll return will be returned by $(D castSwitch).
+The first choice that `switchObject` can be casted to the type
+of argument it accepts will be called with `switchObject` casted to that
+type, and the value it'll return will be returned by `castSwitch`.
If a choice's return type is void, the choice must throw an exception, unless
all the choices are void. In that case, castSwitch itself will return void.
-Throws: If none of the choice matches, a $(D SwitchError) will be thrown. $(D
+Throws: If none of the choice matches, a `SwitchError` will be thrown. $(D
SwitchError) will also be thrown if not all the choices are void and a void
choice was executed without throwing anything.
Params:
- choices = The $(D choices) needs to be composed of function or delegate
+ choices = The `choices` needs to be composed of function or delegate
handlers that accept one argument. There can also be a choice that
accepts zero arguments. That choice will be invoked if the $(D
switchObject) is null.
@@ -235,7 +235,7 @@ Params:
Returns:
The value of the selected choice.
-Note: $(D castSwitch) can only be used with object types.
+Note: `castSwitch` can only be used with object types.
*/
auto castSwitch(choices...)(Object switchObject)
{
@@ -254,7 +254,6 @@ auto castSwitch(choices...)(Object switchObject)
if (switchObject !is null)
{
-
// Checking for exact matches:
const classInfo = typeid(switchObject);
foreach (index, choice; choices)
@@ -517,7 +516,7 @@ auto castSwitch(choices...)(Object switchObject)
/** Clamps a value into the given bounds.
-This functions is equivalent to $(D max(lower, min(upper,val))).
+This function is equivalent to `max(lower, min(upper, val))`.
Params:
val = The value to _clamp.
@@ -525,23 +524,24 @@ Params:
upper = The _upper bound of the _clamp.
Returns:
- Returns $(D val), if it is between $(D lower) and $(D upper).
+ Returns `val`, if it is between `lower` and `upper`.
Otherwise returns the nearest of the two.
*/
auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper)
+if (is(typeof(max(min(val, upper), lower))))
in
{
import std.functional : greaterThan;
- assert(!lower.greaterThan(upper));
+ assert(!lower.greaterThan(upper), "Lower can't be greater than upper.");
}
-body
+do
{
- return max(lower, min(upper, val));
+ return max(min(val, upper), lower);
}
///
-@safe unittest
+@safe @nogc @betterC unittest
{
assert(clamp(2, 1, 3) == 2);
assert(clamp(0, 1, 3) == 1);
@@ -576,121 +576,160 @@ body
// UFCS style
assert(Date(1982, 1, 4).clamp(Date.min, Date.max) == Date(1982, 1, 4));
+ // Stability
+ struct A {
+ int x, y;
+ int opCmp(ref const A rhs) const { return (x > rhs.x) - (x < rhs.x); }
+ }
+ A x, lo, hi;
+ x.y = 42;
+ assert(x.clamp(lo, hi).y == 42);
}
// cmp
/**********************************
-Performs three-way lexicographical comparison on two
-$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives)
-according to predicate $(D pred). Iterating $(D r1) and $(D r2) in
-lockstep, $(D cmp) compares each element $(D e1) of $(D r1) with the
-corresponding element $(D e2) in $(D r2). If one of the ranges has been
-finished, $(D cmp) returns a negative value if $(D r1) has fewer
-elements than $(D r2), a positive value if $(D r1) has more elements
-than $(D r2), and $(D 0) if the ranges have the same number of
-elements.
-
-If the ranges are strings, $(D cmp) performs UTF decoding
+Performs a lexicographical comparison on two
+$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives).
+Iterating `r1` and `r2` in lockstep, `cmp` compares each element
+`e1` of `r1` with the corresponding element `e2` in `r2`. If one
+of the ranges has been finished, `cmp` returns a negative value
+if `r1` has fewer elements than `r2`, a positive value if `r1`
+has more elements than `r2`, and `0` if the ranges have the same
+number of elements.
+
+If the ranges are strings, `cmp` performs UTF decoding
appropriately and compares the ranges one code point at a time.
+A custom predicate may be specified, in which case `cmp` performs
+a three-way lexicographical comparison using `pred`. Otherwise
+the elements are compared using `opCmp`.
+
Params:
- pred = The predicate used for comparison.
+ pred = Predicate used for comparison. Without a predicate
+ specified the ordering implied by `opCmp` is used.
r1 = The first range.
r2 = The second range.
Returns:
- 0 if both ranges compare equal. -1 if the first differing element of $(D
- r1) is less than the corresponding element of $(D r2) according to $(D
- pred). 1 if the first differing element of $(D r2) is less than the
- corresponding element of $(D r1) according to $(D pred).
-
+ `0` if the ranges compare equal. A negative value if `r1` is a prefix of `r2` or
+ the first differing element of `r1` is less than the corresponding element of `r2`
+ according to `pred`. A positive value if `r2` is a prefix of `r1` or the first
+ differing element of `r2` is less than the corresponding element of `r1`
+ according to `pred`.
+
+Note:
+ An earlier version of the documentation incorrectly stated that `-1` is the
+ only negative value returned and `1` is the only positive value returned.
+ Whether that is true depends on the types being compared.
*/
-int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2)
-if (isInputRange!R1 && isInputRange!R2 && !(isSomeString!R1 && isSomeString!R2))
+auto cmp(R1, R2)(R1 r1, R2 r2)
+if (isInputRange!R1 && isInputRange!R2)
{
- for (;; r1.popFront(), r2.popFront())
+ alias E1 = ElementEncodingType!R1;
+ alias E2 = ElementEncodingType!R2;
+
+ static if (isDynamicArray!R1 && isDynamicArray!R2
+ && __traits(isUnsigned, E1) && __traits(isUnsigned, E2)
+ && E1.sizeof == 1 && E2.sizeof == 1
+ // Both or neither must auto-decode.
+ && (is(immutable E1 == immutable char) == is(immutable E2 == immutable char)))
{
- if (r1.empty) return -cast(int)!r2.empty;
- if (r2.empty) return !r1.empty;
- auto a = r1.front, b = r2.front;
- if (binaryFun!pred(a, b)) return -1;
- if (binaryFun!pred(b, a)) return 1;
+ // dstrcmp algorithm is correct for both ubyte[] and for char[].
+ import core.internal.string : dstrcmp;
+ return dstrcmp(cast(const char[]) r1, cast(const char[]) r2);
}
-}
-
-/// ditto
-int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2)
-if (isSomeString!R1 && isSomeString!R2)
-{
- import core.stdc.string : memcmp;
- import std.utf : decode;
-
- static if (is(typeof(pred) : string))
- enum isLessThan = pred == "a < b";
- else
- enum isLessThan = false;
-
- // For speed only
- static int threeWay(size_t a, size_t b)
+ else static if (!(isSomeString!R1 && isSomeString!R2))
{
- static if (size_t.sizeof == int.sizeof && isLessThan)
- return a - b;
- else
- return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0;
- }
- // For speed only
- // @@@BUG@@@ overloading should be allowed for nested functions
- static int threeWayInt(int a, int b)
- {
- static if (isLessThan)
- return a - b;
- else
- return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0;
+ for (;; r1.popFront(), r2.popFront())
+ {
+ static if (is(typeof(r1.front.opCmp(r2.front)) R))
+ alias Result = R;
+ else
+ alias Result = int;
+ if (r2.empty) return Result(!r1.empty);
+ if (r1.empty) return Result(-1);
+ static if (is(typeof(r1.front.opCmp(r2.front))))
+ {
+ auto c = r1.front.opCmp(r2.front);
+ if (c != 0) return c;
+ }
+ else
+ {
+ auto a = r1.front, b = r2.front;
+ if (auto result = (b < a) - (a < b)) return result;
+ }
+ }
}
-
- static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof && isLessThan)
+ else
{
- static if (typeof(r1[0]).sizeof == 1)
+ static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof)
{
- immutable len = min(r1.length, r2.length);
- immutable result = __ctfe ?
+ return () @trusted
+ {
+ auto p1 = r1.ptr, p2 = r2.ptr,
+ pEnd = p1 + min(r1.length, r2.length);
+ for (; p1 != pEnd; ++p1, ++p2)
{
- foreach (i; 0 .. len)
- {
- if (r1[i] != r2[i])
- return threeWayInt(r1[i], r2[i]);
- }
- return 0;
- }()
- : () @trusted { return memcmp(r1.ptr, r2.ptr, len); }();
- if (result) return result;
+ if (*p1 != *p2) return cast(int) *p1 - cast(int) *p2;
+ }
+ static if (typeof(r1[0]).sizeof >= 2 && size_t.sizeof <= uint.sizeof)
+ return cast(int) r1.length - cast(int) r2.length;
+ else
+ return int(r1.length > r2.length) - int(r1.length < r2.length);
+ }();
}
else
{
- auto p1 = r1.ptr, p2 = r2.ptr,
- pEnd = p1 + min(r1.length, r2.length);
- for (; p1 != pEnd; ++p1, ++p2)
+ import std.utf : decode;
+
+ for (size_t i1, i2;;)
{
- if (*p1 != *p2) return threeWayInt(cast(int) *p1, cast(int) *p2);
+ if (i1 == r1.length) return -int(i2 < r2.length);
+ if (i2 == r2.length) return int(1);
+ immutable c1 = decode(r1, i1),
+ c2 = decode(r2, i2);
+ if (c1 != c2) return cast(int) c1 - cast(int) c2;
}
}
- return threeWay(r1.length, r2.length);
+ }
+}
+
+/// ditto
+int cmp(alias pred, R1, R2)(R1 r1, R2 r2)
+if (isInputRange!R1 && isInputRange!R2)
+{
+ static if (!(isSomeString!R1 && isSomeString!R2))
+ {
+ for (;; r1.popFront(), r2.popFront())
+ {
+ if (r2.empty) return !r1.empty;
+ if (r1.empty) return -1;
+ auto a = r1.front, b = r2.front;
+ if (binaryFun!pred(a, b)) return -1;
+ if (binaryFun!pred(b, a)) return 1;
+ }
}
else
{
+ import std.utf : decode;
+
for (size_t i1, i2;;)
{
- if (i1 == r1.length) return threeWay(i2, r2.length);
- if (i2 == r2.length) return threeWay(r1.length, i1);
+ if (i1 == r1.length) return -int(i2 < r2.length);
+ if (i2 == r2.length) return 1;
immutable c1 = decode(r1, i1),
c2 = decode(r2, i2);
- if (c1 != c2) return threeWayInt(cast(int) c1, cast(int) c2);
+ if (c1 != c2)
+ {
+ if (binaryFun!pred(c2, c1)) return 1;
+ if (binaryFun!pred(c1, c2)) return -1;
+ }
}
}
}
///
-@safe unittest
+pure @safe unittest
{
int result;
@@ -712,6 +751,8 @@ if (isSomeString!R1 && isSomeString!R2)
assert(result > 0);
result = cmp("aaa", "aaa"d);
assert(result == 0);
+ result = cmp("aaa"d, "aaa"d);
+ assert(result == 0);
result = cmp(cast(int[])[], cast(int[])[]);
assert(result == 0);
result = cmp([1, 2, 3], [1, 2, 3]);
@@ -724,129 +765,237 @@ if (isSomeString!R1 && isSomeString!R2)
assert(result > 0);
}
+/// Example predicate that compares individual elements in reverse lexical order
+pure @safe unittest
+{
+ int result;
+
+ result = cmp!"a > b"("abc", "abc");
+ assert(result == 0);
+ result = cmp!"a > b"("", "");
+ assert(result == 0);
+ result = cmp!"a > b"("abc", "abcd");
+ assert(result < 0);
+ result = cmp!"a > b"("abcd", "abc");
+ assert(result > 0);
+ result = cmp!"a > b"("abc"d, "abd");
+ assert(result > 0);
+ result = cmp!"a > b"("bbc", "abc"w);
+ assert(result < 0);
+ result = cmp!"a > b"("aaa", "aaaa"d);
+ assert(result < 0);
+ result = cmp!"a > b"("aaaa", "aaa"d);
+ assert(result > 0);
+ result = cmp!"a > b"("aaa", "aaa"d);
+ assert(result == 0);
+ result = cmp("aaa"d, "aaa"d);
+ assert(result == 0);
+ result = cmp!"a > b"(cast(int[])[], cast(int[])[]);
+ assert(result == 0);
+ result = cmp!"a > b"([1, 2, 3], [1, 2, 3]);
+ assert(result == 0);
+ result = cmp!"a > b"([1, 3, 2], [1, 2, 3]);
+ assert(result < 0);
+ result = cmp!"a > b"([1, 2, 3], [1L, 2, 3, 4]);
+ assert(result < 0);
+ result = cmp!"a > b"([1L, 2, 3], [1, 2]);
+ assert(result > 0);
+}
+
+// cmp for string with custom predicate fails if distinct chars can compare equal
+// https://issues.dlang.org/show_bug.cgi?id=18286
+@nogc nothrow pure @safe unittest
+{
+ static bool ltCi(dchar a, dchar b)// less than, case insensitive
+ {
+ import std.ascii : toUpper;
+ return toUpper(a) < toUpper(b);
+ }
+ static assert(cmp!ltCi("apple2", "APPLE1") > 0);
+ static assert(cmp!ltCi("apple1", "APPLE2") < 0);
+ static assert(cmp!ltCi("apple", "APPLE1") < 0);
+ static assert(cmp!ltCi("APPLE", "apple1") < 0);
+ static assert(cmp!ltCi("apple", "APPLE") == 0);
+}
+
+// for non-string ranges check that opCmp is evaluated only once per pair.
+// https://issues.dlang.org/show_bug.cgi?id=18280
+@nogc nothrow @safe unittest
+{
+ static int ctr = 0;
+ struct S
+ {
+ int opCmp(ref const S rhs) const
+ {
+ ++ctr;
+ return 0;
+ }
+ bool opEquals(T)(T o) const { return false; }
+ size_t toHash() const { return 0; }
+ }
+ immutable S[4] a;
+ immutable S[4] b;
+ immutable result = cmp(a[], b[]);
+ assert(result == 0, "neither should compare greater than the other!");
+ assert(ctr == a.length, "opCmp should be called exactly once per pair of items!");
+}
+
+nothrow pure @safe @nogc unittest
+{
+ import std.array : staticArray;
+ // Test cmp when opCmp returns float.
+ struct F
+ {
+ float value;
+ float opCmp(const ref F rhs) const
+ {
+ return value - rhs.value;
+ }
+ bool opEquals(T)(T o) const { return false; }
+ size_t toHash() const { return 0; }
+ }
+ auto result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2), F(3)].staticArray[]);
+ assert(result == 0);
+ assert(is(typeof(result) == float));
+ result = cmp([F(1), F(3), F(2)].staticArray[], [F(1), F(2), F(3)].staticArray[]);
+ assert(result > 0);
+ result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2), F(3), F(4)].staticArray[]);
+ assert(result < 0);
+ result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2)].staticArray[]);
+ assert(result > 0);
+}
+
+nothrow pure @safe unittest
+{
+ // Parallelism (was broken by inferred return type "immutable int")
+ import std.parallelism : task;
+ auto t = task!cmp("foo", "bar");
+}
+
// equal
/**
-Compares two ranges for equality, as defined by predicate $(D pred)
-(which is $(D ==) by default).
+Compares two ranges for equality, as defined by predicate `pred`
+(which is `==` by default).
*/
template equal(alias pred = "a == b")
{
- enum isEmptyRange(R) =
- isInputRange!R && __traits(compiles, {static assert(R.empty);});
-
- enum hasFixedLength(T) = hasLength!T || isNarrowString!T;
-
/++
Compares two ranges for equality. The ranges may have
- different element types, as long as $(D pred(r1.front, r2.front))
- evaluates to $(D bool).
- Performs $(BIGOH min(r1.length, r2.length)) evaluations of $(D pred).
+ different element types, as long as `pred(r1.front, r2.front)`
+ evaluates to `bool`.
+ Performs $(BIGOH min(r1.length, r2.length)) evaluations of `pred`.
+
+ At least one of the ranges must be finite. If one range involved is infinite, the result is
+ (statically known to be) `false`.
+
+ If the two ranges are different kinds of UTF code unit (`char`, `wchar`, or
+ `dchar`), then the arrays are compared using UTF decoding to avoid
+ accidentally integer-promoting units.
Params:
r1 = The first range to be compared.
r2 = The second range to be compared.
Returns:
- $(D true) if and only if the two ranges compare _equal element
- for element, according to binary predicate $(D pred).
-
- See_Also:
- $(HTTP sgi.com/tech/stl/_equal.html, STL's _equal)
+ `true` if and only if the two ranges compare _equal element
+ for element, according to binary predicate `pred`.
+/
bool equal(Range1, Range2)(Range1 r1, Range2 r2)
if (isInputRange!Range1 && isInputRange!Range2 &&
+ !(isInfinite!Range1 && isInfinite!Range2) &&
is(typeof(binaryFun!pred(r1.front, r2.front))))
{
- static assert(!(isInfinite!Range1 && isInfinite!Range2),
- "Both ranges are known to be infinite");
-
- //No pred calls necessary
- static if (isEmptyRange!Range1 || isEmptyRange!Range2)
+ // Use code points when comparing two ranges of UTF code units that aren't
+ // the same type. This is for backwards compatibility with autodecode
+ // strings.
+ enum useCodePoint =
+ isSomeChar!(ElementEncodingType!Range1) && isSomeChar!(ElementEncodingType!Range2) &&
+ ElementEncodingType!Range1.sizeof != ElementEncodingType!Range2.sizeof;
+
+ static if (useCodePoint)
{
- return r1.empty && r2.empty;
+ import std.utf : byDchar;
+ return equal(r1.byDchar, r2.byDchar);
}
- else static if ((isInfinite!Range1 && hasFixedLength!Range2) ||
- (hasFixedLength!Range1 && isInfinite!Range2))
- {
- return false;
- }
- //Detect default pred and compatible dynamic array
- else static if (is(typeof(pred) == string) && pred == "a == b" &&
- isArray!Range1 && isArray!Range2 && is(typeof(r1 == r2)))
- {
- return r1 == r2;
- }
- // if one of the arguments is a string and the other isn't, then auto-decoding
- // can be avoided if they have the same ElementEncodingType
- else static if (is(typeof(pred) == string) && pred == "a == b" &&
- isAutodecodableString!Range1 != isAutodecodableString!Range2 &&
- is(ElementEncodingType!Range1 == ElementEncodingType!Range2))
+ else
{
- import std.utf : byCodeUnit;
-
- static if (isAutodecodableString!Range1)
+ static if (isInfinite!Range1 || isInfinite!Range2)
{
- return equal(r1.byCodeUnit, r2);
+ // No finite range can be ever equal to an infinite range.
+ return false;
}
- else
+ // Detect default pred and compatible dynamic arrays.
+ else static if (is(typeof(pred) == string) && pred == "a == b" &&
+ isArray!Range1 && isArray!Range2 && is(typeof(r1 == r2)))
{
- return equal(r2.byCodeUnit, r1);
+ return r1 == r2;
}
- }
- //Try a fast implementation when the ranges have comparable lengths
- else static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length)))
- {
- immutable len1 = r1.length;
- immutable len2 = r2.length;
- if (len1 != len2) return false; //Short circuit return
-
- //Lengths are the same, so we need to do an actual comparison
- //Good news is we can squeeze out a bit of performance by not checking if r2 is empty
- for (; !r1.empty; r1.popFront(), r2.popFront())
+ // If one of the arguments is a string and the other isn't, then auto-decoding
+ // can be avoided if they have the same ElementEncodingType.
+ else static if (is(typeof(pred) == string) && pred == "a == b" &&
+ isAutodecodableString!Range1 != isAutodecodableString!Range2 &&
+ is(immutable ElementEncodingType!Range1 == immutable ElementEncodingType!Range2))
{
- if (!binaryFun!(pred)(r1.front, r2.front)) return false;
+ import std.utf : byCodeUnit;
+
+ static if (isAutodecodableString!Range1)
+ return equal(r1.byCodeUnit, r2);
+ else
+ return equal(r2.byCodeUnit, r1);
}
- return true;
- }
- else
- {
- //Generic case, we have to walk both ranges making sure neither is empty
- for (; !r1.empty; r1.popFront(), r2.popFront())
+ // Try a fast implementation when the ranges have comparable lengths.
+ else static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length)))
{
- if (r2.empty) return false;
- if (!binaryFun!(pred)(r1.front, r2.front)) return false;
+ immutable len1 = r1.length;
+ immutable len2 = r2.length;
+ if (len1 != len2) return false; //Short circuit return
+
+ // Lengths are the same, so we need to do an actual comparison.
+ // Good news is we can squeeze out a bit of performance by not checking if r2 is empty.
+ for (; !r1.empty; r1.popFront(), r2.popFront())
+ {
+ if (!binaryFun!(pred)(r1.front, r2.front)) return false;
+ }
+ return true;
}
- static if (!isInfinite!Range1)
+ else
+ {
+ //Generic case, we have to walk both ranges making sure neither is empty
+ for (; !r1.empty; r1.popFront(), r2.popFront())
+ {
+ if (r2.empty || !binaryFun!(pred)(r1.front, r2.front)) return false;
+ }
return r2.empty;
+ }
}
}
}
///
-@safe unittest
+@safe @nogc unittest
{
import std.algorithm.comparison : equal;
- import std.math : approxEqual;
+ import std.math.operations : isClose;
- int[] a = [ 1, 2, 4, 3 ];
- assert(!equal(a, a[1..$]));
- assert(equal(a, a));
- assert(equal!((a, b) => a == b)(a, a));
+ int[4] a = [ 1, 2, 4, 3 ];
+ assert(!equal(a[], a[1..$]));
+ assert(equal(a[], a[]));
+ assert(equal!((a, b) => a == b)(a[], a[]));
// different types
- double[] b = [ 1.0, 2, 4, 3];
- assert(!equal(a, b[1..$]));
- assert(equal(a, b));
+ double[4] b = [ 1.0, 2, 4, 3];
+ assert(!equal(a[], b[1..$]));
+ assert(equal(a[], b[]));
// predicated: ensure that two vectors are approximately equal
- double[] c = [ 1.005, 2, 4, 3];
- assert(equal!approxEqual(b, c));
+ double[4] c = [ 1.0000000005, 2, 4, 3];
+ assert(equal!isClose(b[], c[]));
}
/++
-Tip: $(D equal) can itself be used as a predicate to other functions.
+Tip: `equal` can itself be used as a predicate to other functions.
This can be very useful when the element type of a range is itself a
-range. In particular, $(D equal) can be its own predicate, allowing
+range. In particular, `equal` can be its own predicate, allowing
range of range (of range...) comparisons.
+/
@safe unittest
@@ -864,7 +1013,7 @@ range of range (of range...) comparisons.
import std.algorithm.iteration : map;
import std.internal.test.dummyrange : ReferenceForwardRange,
ReferenceInputRange, ReferenceInfiniteForwardRange;
- import std.math : approxEqual;
+ import std.math.operations : isClose;
// various strings
assert(equal("æøå", "æøå")); //UTF8 vs UTF8
@@ -898,11 +1047,11 @@ range of range (of range...) comparisons.
int[] a = [ 1, 2, 4, 3 ];
assert(equal([2, 4, 8, 6], map!"a*2"(a)));
double[] b = [ 1.0, 2, 4, 3];
- double[] c = [ 1.005, 2, 4, 3];
- assert(equal!approxEqual(map!"a*2"(b), map!"a*2"(c)));
+ double[] c = [ 1.0000000005, 2, 4, 3];
+ assert(equal!isClose(map!"a*2"(b), map!"a*2"(c)));
assert(!equal([2, 4, 1, 3], map!"a*2"(a)));
assert(!equal([2, 4, 1], map!"a*2"(a)));
- assert(!equal!approxEqual(map!"a*3"(b), map!"a*2"(c)));
+ assert(!equal!isClose(map!"a*3"(b), map!"a*2"(c)));
//Tests with some fancy reference ranges.
ReferenceInputRange!int cir = new ReferenceInputRange!int([1, 2, 4, 3]);
@@ -923,19 +1072,33 @@ range of range (of range...) comparisons.
assert(!equal(cir, ifr));
}
-@safe pure unittest
+@safe @nogc pure unittest
{
- import std.utf : byChar, byWchar, byDchar;
+ import std.utf : byChar, byDchar, byWchar;
assert(equal("æøå".byChar, "æøå"));
+ assert(equal("æøå".byChar, "æøå"w));
+ assert(equal("æøå".byChar, "æøå"d));
assert(equal("æøå", "æøå".byChar));
+ assert(equal("æøå"w, "æøå".byChar));
+ assert(equal("æøå"d, "æøå".byChar));
+
+ assert(equal("æøå".byWchar, "æøå"));
assert(equal("æøå".byWchar, "æøå"w));
+ assert(equal("æøå".byWchar, "æøå"d));
+ assert(equal("æøå", "æøå".byWchar));
assert(equal("æøå"w, "æøå".byWchar));
+ assert(equal("æøå"d, "æøå".byWchar));
+
+ assert(equal("æøå".byDchar, "æøå"));
+ assert(equal("æøå".byDchar, "æøå"w));
assert(equal("æøå".byDchar, "æøå"d));
+ assert(equal("æøå", "æøå".byDchar));
+ assert(equal("æøå"w, "æøå".byDchar));
assert(equal("æøå"d, "æøå".byDchar));
}
-@safe pure unittest
+@safe @nogc pure unittest
{
struct R(bool _empty) {
enum empty = _empty;
@@ -956,37 +1119,14 @@ range of range (of range...) comparisons.
assert(!"bar".equal(E()));
}
-// MaxType
-private template MaxType(T...)
-if (T.length >= 1)
-{
- static if (T.length == 1)
- {
- alias MaxType = T[0];
- }
- else static if (T.length == 2)
- {
- static if (!is(typeof(T[0].min)))
- alias MaxType = CommonType!T;
- else static if (T[1].max > T[0].max)
- alias MaxType = T[1];
- else
- alias MaxType = T[0];
- }
- else
- {
- alias MaxType = MaxType!(MaxType!(T[0 .. ($+1)/2]), MaxType!(T[($+1)/2 .. $]));
- }
-}
-
// levenshteinDistance
/**
Encodes $(HTTP realityinteractive.com/rgrzywinski/archives/000249.html,
edit operations) necessary to transform one sequence into
-another. Given sequences $(D s) (source) and $(D t) (target), a
-sequence of $(D EditOp) encodes the steps that need to be taken to
-convert $(D s) into $(D t). For example, if $(D s = "cat") and $(D
-"cars"), the minimal sequence that transforms $(D s) into $(D t) is:
+another. Given sequences `s` (source) and `t` (target), a
+sequence of `EditOp` encodes the steps that need to be taken to
+convert `s` into `t`. For example, if `s = "cat"` and $(D
+"cars"), the minimal sequence that transforms `s` into `t` is:
skip two characters, replace 't' with 'r', and insert an 's'. Working
with edit operations is useful in applications such as spell-checkers
(to find the closest word to a given misspelled word), approximate
@@ -1074,7 +1214,8 @@ private:
import core.checkedint : mulu;
bool overflow;
const rc = mulu(r, c, overflow);
- if (overflow) assert(0);
+ assert(!overflow, "Overflow during multiplication to determine number "
+ ~ " of matrix elements");
rows = r;
cols = c;
if (_matrix.length < rc)
@@ -1082,7 +1223,8 @@ private:
import core.exception : onOutOfMemoryError;
import core.stdc.stdlib : realloc;
const nbytes = mulu(rc, _matrix[0].sizeof, overflow);
- if (overflow) assert(0);
+ assert(!overflow, "Overflow during multiplication to determine "
+ ~ " number of bytes of matrix");
auto m = cast(CostType *) realloc(_matrix.ptr, nbytes);
if (!m)
onOutOfMemoryError();
@@ -1193,10 +1335,10 @@ private:
/**
Returns the $(HTTP wikipedia.org/wiki/Levenshtein_distance, Levenshtein
-distance) between $(D s) and $(D t). The Levenshtein distance computes
-the minimal amount of edit operations necessary to transform $(D s)
-into $(D t). Performs $(BIGOH s.length * t.length) evaluations of $(D
-equals) and occupies $(BIGOH s.length * t.length) storage.
+distance) between `s` and `t`. The Levenshtein distance computes
+the minimal amount of edit operations necessary to transform `s`
+into `t`. Performs $(BIGOH s.length * t.length) evaluations of $(D
+equals) and occupies $(BIGOH min(s.length, t.length)) storage.
Params:
equals = The binary predicate to compare the elements of the two ranges.
@@ -1244,7 +1386,7 @@ if (isForwardRange!(Range1) && isForwardRange!(Range2))
return eq(s.front, t.front) ? 0 : 1;
}
- if (slen > tlen)
+ if (slen < tlen)
{
Levenshtein!(Range1, eq, size_t) lev;
return lev.distanceLowMem(s, t, slen, tlen);
@@ -1305,8 +1447,8 @@ if (isConvertibleToString!Range1 || isConvertibleToString!Range2)
}
/**
-Returns the Levenshtein distance and the edit path between $(D s) and
-$(D t).
+Returns the Levenshtein distance and the edit path between `s` and
+`t`.
Params:
equals = The binary predicate to compare the elements of the two ranges.
@@ -1367,50 +1509,73 @@ if (isConvertibleToString!Range1 || isConvertibleToString!Range2)
assert(levenshteinDistanceAndPath(S("cat"), "rat")[0] == 1);
}
+
// max
/**
-Iterates the passed arguments and return the maximum value.
+Iterates the passed arguments and returns the maximum value.
Params:
args = The values to select the maximum from. At least two arguments must
- be passed.
+ be passed, and they must be comparable with `<`.
Returns:
- The maximum of the passed-in args. The type of the returned value is
+ The maximum of the passed-in values. The type of the returned value is
the type among the passed arguments that is able to store the largest value.
+ If at least one of the arguments is NaN, the result is an unspecified value.
+ See $(REF maxElement, std,algorithm,searching) for examples on how to cope
+ with NaNs.
See_Also:
$(REF maxElement, std,algorithm,searching)
*/
-MaxType!T max(T...)(T args)
-if (T.length >= 2)
+auto max(T...)(T args)
+if (T.length >= 2 && !is(CommonType!T == void))
{
- //Get "a"
- static if (T.length <= 2)
+ // Get left-hand side of the comparison.
+ static if (T.length == 2)
alias a = args[0];
else
- auto a = max(args[0 .. ($+1)/2]);
+ auto a = max(args[0 .. ($ + 1) / 2]);
alias T0 = typeof(a);
- //Get "b"
+ // Get right-hand side.
static if (T.length <= 3)
- alias b = args[$-1];
+ alias b = args[$ - 1];
else
- auto b = max(args[($+1)/2 .. $]);
+ auto b = max(args[($ + 1) / 2 .. $]);
alias T1 = typeof(b);
- import std.algorithm.internal : algoFormat;
static assert(is(typeof(a < b)),
- algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof));
+ "Invalid arguments: Cannot compare types " ~ T0.stringof ~
+ " and " ~ T1.stringof ~ " for ordering.");
+
+ // Compute the returned type.
+ static if (is(typeof(mostNegative!T0 < mostNegative!T1)))
+ // Both are numeric (or character or Boolean), so we choose the one with the highest maximum.
+ // (We use mostNegative for num/bool/char testing purposes even if it's not used otherwise.)
+ alias Result = Select!(T1.max > T0.max, T1, T0);
+ else
+ // At least one is non-numeric, so just go with the common type.
+ alias Result = CommonType!(T0, T1);
- //Do the "max" proper with a and b
+ // Perform the computation.
import std.functional : lessThan;
immutable chooseB = lessThan!(T0, T1)(a, b);
- return cast(typeof(return)) (chooseB ? b : a);
+ return cast(Result) (chooseB ? b : a);
+}
+
+/// ditto
+T max(T, U)(T a, U b)
+if (is(T == U) && is(typeof(a < b)))
+{
+ /* Handle the common case without all the template expansions
+ * of the general case
+ */
+ return a < b ? b : a;
}
///
-@safe unittest
+@safe @betterC @nogc unittest
{
int a = 5;
short b = 6;
@@ -1423,7 +1588,7 @@ if (T.length >= 2)
assert(e == 2);
}
-@safe unittest
+@safe unittest // not @nogc due to `Date`
{
int a = 5;
short b = 6;
@@ -1452,77 +1617,76 @@ if (T.length >= 2)
assert(max(Date.max, Date.min) == Date.max);
}
-// MinType
-private template MinType(T...)
-if (T.length >= 1)
-{
- static if (T.length == 1)
- {
- alias MinType = T[0];
- }
- else static if (T.length == 2)
- {
- static if (!is(typeof(T[0].min)))
- alias MinType = CommonType!T;
- else
- {
- enum hasMostNegative = is(typeof(mostNegative!(T[0]))) &&
- is(typeof(mostNegative!(T[1])));
- static if (hasMostNegative && mostNegative!(T[1]) < mostNegative!(T[0]))
- alias MinType = T[1];
- else static if (hasMostNegative && mostNegative!(T[1]) > mostNegative!(T[0]))
- alias MinType = T[0];
- else static if (T[1].max < T[0].max)
- alias MinType = T[1];
- else
- alias MinType = T[0];
- }
- }
- else
- {
- alias MinType = MinType!(MinType!(T[0 .. ($+1)/2]), MinType!(T[($+1)/2 .. $]));
- }
-}
-
// min
/**
Iterates the passed arguments and returns the minimum value.
-Params: args = The values to select the minimum from. At least two arguments
- must be passed, and they must be comparable with `<`.
-Returns: The minimum of the passed-in values.
+Params:
+ args = The values to select the minimum from. At least two arguments must
+ be passed, and they must be comparable with `<`.
+
+Returns:
+ The minimum of the passed-in values. The type of the returned value is
+ the type among the passed arguments that is able to store the smallest value.
+ If at least one of the arguments is NaN, the result is an unspecified value.
+ See $(REF minElement, std,algorithm,searching) for examples on how to cope
+ with NaNs.
+
See_Also:
$(REF minElement, std,algorithm,searching)
*/
-MinType!T min(T...)(T args)
-if (T.length >= 2)
+auto min(T...)(T args)
+if (T.length >= 2 && !is(CommonType!T == void))
{
- //Get "a"
+ // Get the left-hand side of the comparison.
static if (T.length <= 2)
alias a = args[0];
else
- auto a = min(args[0 .. ($+1)/2]);
+ auto a = min(args[0 .. ($ + 1) / 2]);
alias T0 = typeof(a);
- //Get "b"
+ // Get the right-hand side.
static if (T.length <= 3)
- alias b = args[$-1];
+ alias b = args[$ - 1];
else
- auto b = min(args[($+1)/2 .. $]);
+ auto b = min(args[($ + 1) / 2 .. $]);
alias T1 = typeof(b);
- import std.algorithm.internal : algoFormat;
static assert(is(typeof(a < b)),
- algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof));
+ "Invalid arguments: Cannot compare types " ~ T0.stringof ~
+ " and " ~ T1.stringof ~ " for ordering.");
+
+ // Compute the returned type.
+ static if (is(typeof(mostNegative!T0 < mostNegative!T1)))
+ // Both are numeric (or character or Boolean), so we choose the one with the lowest minimum.
+ // If they have the same minimum, choose the one with the smallest size.
+ // If both mostNegative and sizeof are equal, go for stability: pick the type of the first one.
+ alias Result = Select!(mostNegative!T1 < mostNegative!T0 ||
+ mostNegative!T1 == mostNegative!T0 && T1.sizeof < T0.sizeof,
+ T1, T0);
+ else
+ // At least one is non-numeric, so just go with the common type.
+ alias Result = CommonType!(T0, T1);
- //Do the "min" proper with a and b
+ // Engage!
import std.functional : lessThan;
- immutable chooseA = lessThan!(T0, T1)(a, b);
- return cast(typeof(return)) (chooseA ? a : b);
+ immutable chooseB = lessThan!(T1, T0)(b, a);
+ return cast(Result) (chooseB ? b : a);
+}
+
+/// ditto
+T min(T, U)(T a, U b)
+if (is(T == U) && is(typeof(a < b)))
+{
+ /* Handle the common case without all the template expansions
+ * of the general case
+ */
+ return b < a ? b : a;
}
+
///
-@safe unittest
+@safe @nogc @betterC unittest
{
int a = 5;
short b = 6;
@@ -1533,15 +1697,31 @@ if (T.length >= 2)
auto e = min(a, b, c);
static assert(is(typeof(e) == double));
assert(e == 2);
+ ulong f = 0xffff_ffff_ffff;
+ const uint g = min(f, 0xffff_0000);
+ assert(g == 0xffff_0000);
+ dchar h = 100;
+ uint i = 101;
+ static assert(is(typeof(min(h, i)) == dchar));
+ static assert(is(typeof(min(i, h)) == uint));
+ assert(min(h, i) == 100);
+}
- // With arguments of mixed signedness, the return type is the one that can
- // store the lowest values.
- a = -10;
+/**
+With arguments of mixed signedness, the return type is the one that can
+store the lowest values.
+*/
+@safe @nogc @betterC unittest
+{
+ int a = -10;
uint f = 10;
static assert(is(typeof(min(a, f)) == int));
assert(min(a, f) == -10);
+}
- // User-defined types that support comparison with < are supported.
+/// User-defined types that support comparison with < are supported.
+@safe unittest // not @nogc due to `Date`
+{
import std.datetime;
assert(min(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(1982, 1, 4));
assert(min(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(1982, 1, 4));
@@ -1553,16 +1733,26 @@ if (T.length >= 2)
assert(min(Date.max, Date.min) == Date.min);
}
+// min must be stable: when in doubt, return the first argument.
+@safe unittest
+{
+ assert(min(1.0, double.nan) == 1.0);
+ assert(min(double.nan, 1.0) is double.nan);
+ static struct A {
+ int x;
+ string y;
+ int opCmp(const A a) const { return int(x > a.x) - int(x < a.x); }
+ }
+ assert(min(A(1, "first"), A(1, "second")) == A(1, "first"));
+}
+
// mismatch
/**
-Sequentially compares elements in $(D r1) and $(D r2) in lockstep, and
-stops at the first mismatch (according to $(D pred), by default
+Sequentially compares elements in `r1` and `r2` in lockstep, and
+stops at the first mismatch (according to `pred`, by default
equality). Returns a tuple with the reduced ranges that start with the
two mismatched values. Performs $(BIGOH min(r1.length, r2.length))
-evaluations of $(D pred).
-
-See_Also:
- $(HTTP sgi.com/tech/stl/_mismatch.html, STL's _mismatch)
+evaluations of `pred`.
*/
Tuple!(Range1, Range2)
mismatch(alias pred = "a == b", Range1, Range2)(Range1 r1, Range2 r2)
@@ -1576,32 +1766,34 @@ if (isInputRange!(Range1) && isInputRange!(Range2))
}
///
-@safe unittest
+@safe @nogc unittest
{
- int[] x = [ 1, 5, 2, 7, 4, 3 ];
- double[] y = [ 1.0, 5, 2, 7.3, 4, 8 ];
- auto m = mismatch(x, y);
+ int[6] x = [ 1, 5, 2, 7, 4, 3 ];
+ double[6] y = [ 1.0, 5, 2, 7.3, 4, 8 ];
+ auto m = mismatch(x[], y[]);
assert(m[0] == x[3 .. $]);
assert(m[1] == y[3 .. $]);
}
-@safe unittest
+@safe @nogc unittest
{
- int[] a = [ 1, 2, 3 ];
- int[] b = [ 1, 2, 4, 5 ];
- auto mm = mismatch(a, b);
- assert(mm[0] == [3]);
- assert(mm[1] == [4, 5]);
+ import std.range : only;
+
+ int[3] a = [ 1, 2, 3 ];
+ int[4] b = [ 1, 2, 4, 5 ];
+ auto mm = mismatch(a[], b[]);
+ assert(equal(mm[0], only(3)));
+ assert(equal(mm[1], only(4, 5)));
}
/**
Returns one of a collection of expressions based on the value of the switch
expression.
-$(D choices) needs to be composed of pairs of test expressions and return
-expressions. Each test-expression is compared with $(D switchExpression) using
-$(D pred)($(D switchExpression) is the first argument) and if that yields true
-- the return expression is returned.
+`choices` needs to be composed of pairs of test expressions and return
+expressions. Each test-expression is compared with `switchExpression` using
+`pred`(`switchExpression` is the first argument) and if that yields true -
+the return expression is returned.
Both the test and the return expressions are lazily evaluated.
@@ -1622,7 +1814,7 @@ made the predicate yield true, or the default return expression if no test
expression matched.
Throws: If there is no default return expression and the predicate does not
-yield true with any test expression - $(D SwitchError) is thrown. $(D
+yield true with any test expression - `SwitchError` is thrown. $(D
SwitchError) is also thrown if a void return expression was executed without
throwing anything.
*/
@@ -1734,74 +1926,48 @@ auto predSwitch(alias pred = "a == b", T, R ...)(T switchExpression, lazy R choi
/**
Checks if the two ranges have the same number of elements. This function is
-optimized to always take advantage of the $(D length) member of either range
+optimized to always take advantage of the `length` member of either range
if it exists.
If both ranges have a length member, this function is $(BIGOH 1). Otherwise,
this function is $(BIGOH min(r1.length, r2.length)).
+Infinite ranges are considered of the same length. An infinite range has never the same length as a
+finite range.
+
Params:
r1 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
r2 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
Returns:
- $(D true) if both ranges have the same length, $(D false) otherwise.
+ `true` if both ranges have the same length, `false` otherwise.
*/
bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2)
-if (isInputRange!Range1 &&
- isInputRange!Range2 &&
- !isInfinite!Range1 &&
- !isInfinite!Range2)
+if (isInputRange!Range1 && isInputRange!Range2)
{
- static if (hasLength!(Range1) && hasLength!(Range2))
+ static if (isInfinite!Range1 || isInfinite!Range2)
+ {
+ return isInfinite!Range1 && isInfinite!Range2;
+ }
+ else static if (hasLength!(Range1) && hasLength!(Range2))
{
return r1.length == r2.length;
}
else static if (hasLength!(Range1) && !hasLength!(Range2))
{
- size_t length;
-
- while (!r2.empty)
- {
- r2.popFront;
-
- if (++length > r1.length)
- {
- return false;
- }
- }
-
- return !(length < r1.length);
+ return r2.walkLength(r1.length + 1) == r1.length;
}
else static if (!hasLength!(Range1) && hasLength!(Range2))
{
- size_t length;
-
- while (!r1.empty)
- {
- r1.popFront;
-
- if (++length > r2.length)
- {
- return false;
- }
- }
-
- return !(length < r2.length);
+ return r1.walkLength(r2.length + 1) == r2.length;
}
else
{
- while (!r1.empty)
+ for (; !r1.empty; r1.popFront, r2.popFront)
{
if (r2.empty)
- {
return false;
- }
-
- r1.popFront;
- r2.popFront;
}
-
return r2.empty;
}
}
@@ -1823,7 +1989,7 @@ if (isInputRange!Range1 &&
}
// Test CTFE
-@safe pure unittest
+@safe @nogc pure @betterC unittest
{
enum result1 = isSameLength([1, 2, 3], [4, 5, 6]);
static assert(result1);
@@ -1832,6 +1998,14 @@ if (isInputRange!Range1 &&
static assert(!result2);
}
+@safe @nogc pure unittest
+{
+ import std.range : only;
+ assert(isSameLength(only(1, 2, 3), only(4, 5, 6)));
+ assert(isSameLength(only(0.3, 90.4, 23.7, 119.2), only(42.6, 23.6, 95.5, 6.3)));
+ assert(!isSameLength(only(1, 3, 3), only(4, 5)));
+}
+
@safe nothrow pure unittest
{
import std.internal.test.dummyrange;
@@ -1861,38 +2035,38 @@ if (isInputRange!Range1 &&
assert(!isSameLength(r11, r12));
}
-/// For convenience
+// Still functional but not documented anymore.
alias AllocateGC = Flag!"allocateGC";
/**
Checks if both ranges are permutations of each other.
-This function can allocate if the $(D Yes.allocateGC) flag is passed. This has
-the benefit of have better complexity than the $(D Yes.allocateGC) option. However,
+This function can allocate if the `Yes.allocateGC` flag is passed. This has
+the benefit of have better complexity than the `Yes.allocateGC` option. However,
this option is only available for ranges whose equality can be determined via each
-element's $(D toHash) method. If customized equality is needed, then the $(D pred)
+element's `toHash` method. If customized equality is needed, then the `pred`
template parameter can be passed, and the function will automatically switch to
the non-allocating algorithm. See $(REF binaryFun, std,functional) for more details on
-how to define $(D pred).
+how to define `pred`.
Non-allocating forward range option: $(BIGOH n^2)
-Non-allocating forward range option with custom $(D pred): $(BIGOH n^2)
+Non-allocating forward range option with custom `pred`: $(BIGOH n^2)
Allocating forward range option: amortized $(BIGOH r1.length) + $(BIGOH r2.length)
Params:
pred = an optional parameter to change how equality is defined
- allocate_gc = $(D Yes.allocateGC)/$(D No.allocateGC)
+ allocateGC = `Yes.allocateGC`/`No.allocateGC`
r1 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
r2 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
Returns:
- $(D true) if all of the elements in $(D r1) appear the same number of times in $(D r2).
- Otherwise, returns $(D false).
+ `true` if all of the elements in `r1` appear the same number of times in `r2`.
+ Otherwise, returns `false`.
*/
-bool isPermutation(AllocateGC allocate_gc, Range1, Range2)
+bool isPermutation(Flag!"allocateGC" allocateGC, Range1, Range2)
(Range1 r1, Range2 r2)
-if (allocate_gc == Yes.allocateGC &&
+if (allocateGC == Yes.allocateGC &&
isForwardRange!Range1 &&
isForwardRange!Range2 &&
!isInfinite!Range1 &&
@@ -2111,7 +2285,7 @@ if (alternatives.length >= 1 &&
}
///
-@safe pure unittest
+@safe pure @betterC unittest
{
const a = 1;
const b = 2;
@@ -2130,7 +2304,11 @@ if (alternatives.length >= 1 &&
auto ef = either(e, f);
static assert(is(typeof(ef) == int));
assert(ef == f);
+}
+///
+@safe pure unittest
+{
immutable p = 1;
immutable q = 2;
auto pq = either(p, q);
@@ -2141,7 +2319,11 @@ if (alternatives.length >= 1 &&
assert(either(0, 4) == 4);
assert(either(0, 0) == 0);
assert(either("", "a") == "");
+}
+///
+@safe pure unittest
+{
string r = null;
assert(either(r, "a") == "a");
assert(either("a", "") == "a");
diff --git a/libphobos/src/std/algorithm/internal.d b/libphobos/src/std/algorithm/internal.d
index ca03fd77b1f..3caeefebf9a 100644
--- a/libphobos/src/std/algorithm/internal.d
+++ b/libphobos/src/std/algorithm/internal.d
@@ -14,22 +14,16 @@ package template algoFormat()
}
// Internal random array generators
-version (unittest)
+version (StdUnittest)
{
package enum size_t maxArraySize = 50;
package enum size_t minArraySize = maxArraySize - 1;
package string[] rndstuff(T : string)()
{
- import std.random : Random, unpredictableSeed, uniform;
+ import std.random : Xorshift, uniform;
- static Random rnd;
- static bool first = true;
- if (first)
- {
- rnd = Random(unpredictableSeed);
- first = false;
- }
+ static rnd = Xorshift(234_567_891);
string[] result =
new string[uniform(minArraySize, maxArraySize, rnd)];
string alpha = "abcdefghijABCDEFGHIJ";
@@ -46,15 +40,9 @@ version (unittest)
package int[] rndstuff(T : int)()
{
- import std.random : Random, unpredictableSeed, uniform;
+ import std.random : Xorshift, uniform;
- static Random rnd;
- static bool first = true;
- if (first)
- {
- rnd = Random(unpredictableSeed);
- first = false;
- }
+ static rnd = Xorshift(345_678_912);
int[] result = new int[uniform(minArraySize, maxArraySize, rnd)];
foreach (ref i; result)
{
diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d
index 19cfb77053d..c3e848760ed 100644
--- a/libphobos/src/std/algorithm/iteration.d
+++ b/libphobos/src/std/algorithm/iteration.d
@@ -1,54 +1,60 @@
// Written in the D programming language.
/**
This is a submodule of $(MREF std, algorithm).
-It contains generic _iteration algorithms.
+It contains generic iteration algorithms.
$(SCRIPT inhibitQuickIndex = 1;)
$(BOOKTABLE Cheat Sheet,
$(TR $(TH Function Name) $(TH Description))
$(T2 cache,
- Eagerly evaluates and caches another range's $(D front).)
+ Eagerly evaluates and caches another range's `front`.)
$(T2 cacheBidirectional,
- As above, but also provides $(D back) and $(D popBack).)
+ As above, but also provides `back` and `popBack`.)
$(T2 chunkBy,
- $(D chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]]))
+ `chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]])`
returns a range containing 3 subranges: the first with just
- $(D [1, 1]); the second with the elements $(D [1, 2]) and $(D [2, 2]);
- and the third with just $(D [2, 1]).)
+ `[1, 1]`; the second with the elements `[1, 2]` and `[2, 2]`;
+ and the third with just `[2, 1]`.)
$(T2 cumulativeFold,
- $(D cumulativeFold!((a, b) => a + b)([1, 2, 3, 4])) returns a
+ `cumulativeFold!((a, b) => a + b)([1, 2, 3, 4])` returns a
lazily-evaluated range containing the successive reduced values `1`,
`3`, `6`, `10`.)
$(T2 each,
- $(D each!writeln([1, 2, 3])) eagerly prints the numbers $(D 1), $(D 2)
- and $(D 3) on their own lines.)
+ `each!writeln([1, 2, 3])` eagerly prints the numbers `1`, `2`
+ and `3` on their own lines.)
$(T2 filter,
- $(D filter!(a => a > 0)([1, -1, 2, 0, -3])) iterates over elements $(D 1)
- and $(D 2).)
+ `filter!(a => a > 0)([1, -1, 2, 0, -3])` iterates over elements `1`
+ and `2`.)
$(T2 filterBidirectional,
- Similar to $(D filter), but also provides $(D back) and $(D popBack) at
+ Similar to `filter`, but also provides `back` and `popBack` at
a small increase in cost.)
$(T2 fold,
- $(D fold!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10).)
+ `fold!((a, b) => a + b)([1, 2, 3, 4])` returns `10`.)
$(T2 group,
- $(D group([5, 2, 2, 3, 3])) returns a range containing the tuples
- $(D tuple(5, 1)), $(D tuple(2, 2)), and $(D tuple(3, 2)).)
+ `group([5, 2, 2, 3, 3])` returns a range containing the tuples
+ `tuple(5, 1)`, `tuple(2, 2)`, and `tuple(3, 2)`.)
$(T2 joiner,
- $(D joiner(["hello", "world!"], "; ")) returns a range that iterates
- over the characters $(D "hello; world!"). No new string is created -
+ `joiner(["hello", "world!"], "; ")` returns a range that iterates
+ over the characters `"hello; world!"`. No new string is created -
the existing inputs are iterated.)
$(T2 map,
- $(D map!(a => a * 2)([1, 2, 3])) lazily returns a range with the numbers
- $(D 2), $(D 4), $(D 6).)
+ `map!(a => a * 2)([1, 2, 3])` lazily returns a range with the numbers
+ `2`, `4`, `6`.)
+$(T2 mean,
+ Colloquially known as the average, `mean([1, 2, 3])` returns `2`.)
$(T2 permutations,
Lazily computes all permutations using Heap's algorithm.)
$(T2 reduce,
- $(D reduce!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10).
+ `reduce!((a, b) => a + b)([1, 2, 3, 4])` returns `10`.
This is the old implementation of `fold`.)
+$(T2 splitWhen,
+ Lazily splits a range by comparing adjacent elements.)
$(T2 splitter,
Lazily splits a range by a separator.)
+$(T2 substitute,
+ `[1, 2].substitute(1, 0.1)` returns `[0.1, 2]`.)
$(T2 sum,
- Same as $(D fold), but specialized for accurate summation.)
+ Same as `fold`, but specialized for accurate summation.)
$(T2 uniq,
Iterates over the unique elements in a range, which is assumed sorted.)
)
@@ -59,17 +65,17 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.com, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/algorithm/_iteration.d)
+Source: $(PHOBOSSRC std/algorithm/iteration.d)
Macros:
T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
*/
module std.algorithm.iteration;
-// FIXME
-import std.functional; // : unaryFun, binaryFun;
+import std.functional : unaryFun, binaryFun;
import std.range.primitives;
import std.traits;
+import std.typecons : Flag, Yes, No;
private template aggregate(fun...)
if (fun.length >= 1)
@@ -117,36 +123,36 @@ if (fun.length >= 1)
}
/++
-$(D cache) eagerly evaluates $(D front) of $(D range)
-on each construction or call to $(D popFront),
-to store the result in a cache.
-The result is then directly returned when $(D front) is called,
+`cache` eagerly evaluates $(REF_ALTTEXT front, front, std,range,primitives) of `range`
+on each construction or call to $(REF_ALTTEXT popFront, popFront, std,range,primitives),
+to store the result in a _cache.
+The result is then directly returned when $(REF_ALTTEXT front, front, std,range,primitives) is called,
rather than re-evaluated.
This can be a useful function to place in a chain, after functions
that have expensive evaluation, as a lazy alternative to $(REF array, std,array).
-In particular, it can be placed after a call to $(D map), or before a call
-to $(D filter).
+In particular, it can be placed after a call to $(LREF map), or before a call
+$(REF filter, std,range) or $(REF tee, std,range)
-$(D cache) may provide
+`cache` may provide
$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
iteration if needed, but since this comes at an increased cost, it must be explicitly requested via the
-call to $(D cacheBidirectional). Furthermore, a bidirectional cache will
+call to `cacheBidirectional`. Furthermore, a bidirectional _cache will
evaluate the "center" element twice, when there is only one element left in
the range.
-$(D cache) does not provide random access primitives,
-as $(D cache) would be unable to cache the random accesses.
-If $(D Range) provides slicing primitives,
-then $(D cache) will provide the same slicing primitives,
-but $(D hasSlicing!Cache) will not yield true (as the $(REF hasSlicing, std,_range,primitives)
+`cache` does not provide random access primitives,
+as `cache` would be unable to _cache the random accesses.
+If `Range` provides slicing primitives,
+then `cache` will provide the same slicing primitives,
+but `hasSlicing!Cache` will not yield true (as the $(REF hasSlicing, std,range,primitives)
trait also checks for random access).
Params:
range = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
Returns:
- an input range with the cached values of range
+ An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with the cached values of range
+/
auto cache(Range)(Range range)
if (isInputRange!Range)
@@ -203,16 +209,22 @@ if (isBidirectionalRange!Range)
assert(counter == iota(-4, 5).length);
}
+// https://issues.dlang.org/show_bug.cgi?id=15891
+@safe pure unittest
+{
+ assert([1].map!(x=>[x].map!(y=>y)).cache.front.front == 1);
+}
+
/++
-Tip: $(D cache) is eager when evaluating elements. If calling front on the
-underlying _range has a side effect, it will be observable before calling
-front on the actual cached _range.
+Tip: `cache` is eager when evaluating elements. If calling front on the
+underlying range has a side effect, it will be observable before calling
+front on the actual cached range.
-Furthermore, care should be taken composing $(D cache) with $(REF take, std,_range).
-By placing $(D take) before $(D cache), then $(D cache) will be "aware"
-of when the _range ends, and correctly stop caching elements when needed.
-If calling front has no side effect though, placing $(D take) after $(D cache)
-may yield a faster _range.
+Furthermore, care should be taken composing `cache` with $(REF take, std,range).
+By placing `take` before `cache`, then `cache` will be "aware"
+of when the range ends, and correctly stop caching elements when needed.
+If calling front has no side effect though, placing `take` after `cache`
+may yield a faster range.
Either way, the resulting ranges will be equivalent, but maybe not at the
same cost or side effects.
@@ -339,9 +351,17 @@ private struct _Cache(R, bool bidir)
source = range;
if (!range.empty)
{
- caches[0] = source.front;
- static if (bidir)
- caches[1] = source.back;
+ caches[0] = source.front;
+ static if (bidir)
+ caches[1] = source.back;
+ }
+ else
+ {
+ // needed, because the compiler cannot deduce, that 'caches' is initialized
+ // see https://issues.dlang.org/show_bug.cgi?id=15891
+ caches[0] = UE.init;
+ static if (bidir)
+ caches[1] = UE.init;
}
}
@@ -353,10 +373,7 @@ private struct _Cache(R, bool bidir)
return source.empty;
}
- static if (hasLength!R) auto length() @property
- {
- return source.length;
- }
+ mixin ImplementLength!source;
E front() @property
{
@@ -376,7 +393,12 @@ private struct _Cache(R, bool bidir)
if (!source.empty)
caches[0] = source.front;
else
- caches = CacheTypes.init;
+ {
+ // see https://issues.dlang.org/show_bug.cgi?id=15891
+ caches[0] = UE.init;
+ static if (bidir)
+ caches[1] = UE.init;
+ }
}
static if (bidir) void popBack()
{
@@ -385,7 +407,11 @@ private struct _Cache(R, bool bidir)
if (!source.empty)
caches[1] = source.back;
else
- caches = CacheTypes.init;
+ {
+ // see https://issues.dlang.org/show_bug.cgi?id=15891
+ caches[0] = UE.init;
+ caches[1] = UE.init;
+ }
}
static if (isForwardRange!R)
@@ -430,7 +456,7 @@ private struct _Cache(R, bool bidir)
{
assert(low <= high, "Bounds error when slicing cache.");
}
- body
+ do
{
import std.range : takeExactly;
return this[low .. $].takeExactly(high - low);
@@ -440,21 +466,14 @@ private struct _Cache(R, bool bidir)
}
/**
-$(D auto map(Range)(Range r) if (isInputRange!(Unqual!Range));)
-
-Implements the homonym function (also known as $(D transform)) present
-in many languages of functional flavor. The call $(D map!(fun)(range))
-returns a range of which elements are obtained by applying $(D fun(a))
-left to right for all elements $(D a) in $(D range). The original ranges are
+Implements the homonym function (also known as `transform`) present
+in many languages of functional flavor. The call `map!(fun)(range)`
+returns a range of which elements are obtained by applying `fun(a)`
+left to right for all elements `a` in `range`. The original ranges are
not changed. Evaluation is done lazily.
Params:
fun = one or more transformation functions
- r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
-
-Returns:
- a range with each fun applied to all the elements. If there is more than one
- fun, the element type will be $(D Tuple) containing one element for each fun.
See_Also:
$(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
@@ -462,6 +481,13 @@ See_Also:
template map(fun...)
if (fun.length >= 1)
{
+ /**
+ Params:
+ r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ Returns:
+ A range with each fun applied to all the elements. If there is more than one
+ fun, the element type will be `Tuple` containing one element for each fun.
+ */
auto map(Range)(Range r) if (isInputRange!(Unqual!Range))
{
import std.meta : AliasSeq, staticMap;
@@ -475,7 +501,9 @@ if (fun.length >= 1)
alias _funs = staticMap!(unaryFun, fun);
alias _fun = adjoin!_funs;
- // Once DMD issue #5710 is fixed, this validation loop can be moved into a template.
+ // Once https://issues.dlang.org/show_bug.cgi?id=5710 is fixed
+ // accross all compilers (as of 2020-04, it wasn't fixed in LDC and GDC),
+ // this validation loop can be moved into a template.
foreach (f; _funs)
{
static assert(!is(typeof(f(RE.init)) == void),
@@ -487,7 +515,8 @@ if (fun.length >= 1)
alias _fun = unaryFun!fun;
alias _funs = AliasSeq!(_fun);
- // Do the validation separately for single parameters due to DMD issue #15777.
+ // Do the validation separately for single parameters due to
+ // https://issues.dlang.org/show_bug.cgi?id=15777.
static assert(!is(typeof(_fun(RE.init)) == void),
"Mapping function(s) must not return void: " ~ _funs.stringof);
}
@@ -497,19 +526,18 @@ if (fun.length >= 1)
}
///
-@safe unittest
+@safe @nogc unittest
{
import std.algorithm.comparison : equal;
- import std.range : chain;
- int[] arr1 = [ 1, 2, 3, 4 ];
- int[] arr2 = [ 5, 6 ];
- auto squares = map!(a => a * a)(chain(arr1, arr2));
- assert(equal(squares, [ 1, 4, 9, 16, 25, 36 ]));
+ import std.range : chain, only;
+ auto squares =
+ chain(only(1, 2, 3, 4), only(5, 6)).map!(a => a * a);
+ assert(equal(squares, only(1, 4, 9, 16, 25, 36)));
}
/**
-Multiple functions can be passed to $(D map). In that case, the
-element type of $(D map) is a tuple containing one element for each
+Multiple functions can be passed to `map`. In that case, the
+element type of `map` is a tuple containing one element for each
function.
*/
@safe unittest
@@ -527,7 +555,7 @@ function.
}
/**
-You may alias $(D map) with some function(s) to a symbol and use
+You may alias `map` with some function(s) to a symbol and use
it separately:
*/
@safe unittest
@@ -539,10 +567,9 @@ it separately:
assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
}
+// Verify workaround for https://issues.dlang.org/show_bug.cgi?id=15777
@safe unittest
{
- // Verify workaround for DMD #15777
-
import std.algorithm.mutation, std.string;
auto foo(string[] args)
{
@@ -602,7 +629,7 @@ private struct MapResult(alias fun, Range)
static if (isRandomAccessRange!R)
{
- static if (is(typeof(_input[ulong.max])))
+ static if (is(typeof(Range.init[ulong.max])))
private alias opIndex_t = ulong;
else
private alias opIndex_t = uint;
@@ -613,15 +640,7 @@ private struct MapResult(alias fun, Range)
}
}
- static if (hasLength!R)
- {
- @property auto length()
- {
- return _input.length;
- }
-
- alias opDollar = length;
- }
+ mixin ImplementLength!_input;
static if (hasSlicing!R)
{
@@ -689,7 +708,7 @@ private struct MapResult(alias fun, Range)
import std.internal.test.dummyrange;
import std.range;
import std.typecons : tuple;
- import std.random : unpredictableSeed, uniform, Random;
+ import std.random : uniform, Random = Xorshift;
int[] arr1 = [ 1, 2, 3, 4 ];
const int[] arr1Const = arr1;
@@ -747,7 +766,7 @@ private struct MapResult(alias fun, Range)
assert(fibsSquares.front == 9);
auto repeatMap = map!"a"(repeat(1));
- auto gen = Random(unpredictableSeed);
+ auto gen = Random(123_456_789);
auto index = uniform(0, 1024, gen);
static assert(isInfinite!(typeof(repeatMap)));
assert(repeatMap[index] == 1);
@@ -779,7 +798,7 @@ private struct MapResult(alias fun, Range)
assert(equal(ms2[0 .. 2], "日本"w));
assert(equal(ms3[0 .. 2], "HE"));
- // Issue 5753
+ // https://issues.dlang.org/show_bug.cgi?id=5753
static void voidFun(int) {}
static int nonvoidFun(int) { return 0; }
static assert(!__traits(compiles, map!voidFun([1])));
@@ -788,7 +807,7 @@ private struct MapResult(alias fun, Range)
static assert(!__traits(compiles, map!(voidFun, nonvoidFun)([1])));
static assert(!__traits(compiles, map!(a => voidFun(a))([1])));
- // Phobos issue #15480
+ // https://issues.dlang.org/show_bug.cgi?id=15480
auto dd = map!(z => z * z, c => c * c * c)([ 1, 2, 3, 4 ]);
assert(dd[0] == tuple(1, 1));
assert(dd[1] == tuple(4, 8));
@@ -810,7 +829,7 @@ private struct MapResult(alias fun, Range)
{
import std.range : iota;
- // Issue #10130 - map of iota with const step.
+ // https://issues.dlang.org/show_bug.cgi?id=10130 - map of iota with const step.
const step = 2;
assert(map!(i => i)(iota(0, 10, step)).walkLength == 5);
@@ -842,35 +861,59 @@ private struct MapResult(alias fun, Range)
assert(m.front == immutable(S)(null));
}
+// Issue 20928
+@safe unittest
+{
+ struct Always3
+ {
+ enum empty = false;
+ auto save() { return this; }
+ long front() { return 3; }
+ void popFront() {}
+ long opIndex(ulong i) { return 3; }
+ long opIndex(ulong i) immutable { return 3; }
+ }
+
+ import std.algorithm.iteration : map;
+ Always3.init.map!(e => e)[ulong.max];
+}
+
// each
/**
-Eagerly iterates over $(D r) and calls $(D pred) over _each element.
+Eagerly iterates over `r` and calls `fun` with _each element.
+
+If no function to call is specified, `each` defaults to doing nothing but
+consuming the entire range. `r.front` will be evaluated, but that can be avoided
+by specifying a lambda with a `lazy` parameter.
-If no predicate is specified, $(D each) will default to doing nothing
-but consuming the entire range. $(D .front) will be evaluated, but this
-can be avoided by explicitly specifying a predicate lambda with a
-$(D lazy) parameter.
+`each` also supports `opApply`-based types, so it works with e.g. $(REF
+parallel, std,parallelism).
-$(D each) also supports $(D opApply)-based iterators, so it will work
-with e.g. $(REF parallel, std,parallelism).
+Normally the entire range is iterated. If partial iteration (early stopping) is
+desired, `fun` needs to return a value of type $(REF Flag,
+std,typecons)`!"each"` (`Yes.each` to continue iteration, or `No.each` to stop
+iteration).
Params:
- pred = predicate to apply to each element of the range
- r = range or iterable over which each iterates
+ fun = function to apply to _each element of the range
+ r = range or iterable over which `each` iterates
-See_Also: $(REF tee, std,range)
+Returns: `Yes.each` if the entire range was iterated, `No.each` in case of early
+stopping.
+See_Also: $(REF tee, std,range)
*/
-template each(alias pred = "a")
+template each(alias fun = "a")
{
import std.meta : AliasSeq;
import std.traits : Parameters;
+ import std.typecons : Flag, Yes, No;
private:
- alias BinaryArgs = AliasSeq!(pred, "i", "a");
+ alias BinaryArgs = AliasSeq!(fun, "i", "a");
enum isRangeUnaryIterable(R) =
- is(typeof(unaryFun!pred(R.init.front)));
+ is(typeof(unaryFun!fun(R.init.front)));
enum isRangeBinaryIterable(R) =
is(typeof(binaryFun!BinaryArgs(0, R.init.front)));
@@ -882,21 +925,32 @@ private:
enum isForeachUnaryIterable(R) =
is(typeof((R r) {
foreach (ref a; r)
- cast(void) unaryFun!pred(a);
+ cast(void) unaryFun!fun(a);
}));
- enum isForeachBinaryIterable(R) =
+ enum isForeachUnaryWithIndexIterable(R) =
is(typeof((R r) {
- foreach (ref i, ref a; r)
+ foreach (i, ref a; r)
cast(void) binaryFun!BinaryArgs(i, a);
}));
+ enum isForeachBinaryIterable(R) =
+ is(typeof((R r) {
+ foreach (ref a, ref b; r)
+ cast(void) binaryFun!fun(a, b);
+ }));
+
enum isForeachIterable(R) =
(!isForwardRange!R || isDynamicArray!R) &&
- (isForeachUnaryIterable!R || isForeachBinaryIterable!R);
+ (isForeachUnaryIterable!R || isForeachBinaryIterable!R ||
+ isForeachUnaryWithIndexIterable!R);
public:
- void each(Range)(Range r)
+ /**
+ Params:
+ r = range or iterable over which each iterates
+ */
+ Flag!"each" each(Range)(Range r)
if (!isForeachIterable!Range && (
isRangeIterable!Range ||
__traits(compiles, typeof(r.front).length)))
@@ -908,7 +962,15 @@ public:
{
while (!r.empty)
{
- cast(void) unaryFun!pred(r.front);
+ static if (!is(typeof(unaryFun!fun(r.front)) == Flag!"each"))
+ {
+ cast(void) unaryFun!fun(r.front);
+ }
+ else
+ {
+ if (unaryFun!fun(r.front) == No.each) return No.each;
+ }
+
r.popFront();
}
}
@@ -917,7 +979,14 @@ public:
size_t i = 0;
while (!r.empty)
{
- cast(void) binaryFun!BinaryArgs(i, r.front);
+ static if (!is(typeof(binaryFun!BinaryArgs(i, r.front)) == Flag!"each"))
+ {
+ cast(void) binaryFun!BinaryArgs(i, r.front);
+ }
+ else
+ {
+ if (binaryFun!BinaryArgs(i, r.front) == No.each) return No.each;
+ }
r.popFront();
i++;
}
@@ -927,71 +996,149 @@ public:
{
// range interface with >2 parameters.
for (auto range = r; !range.empty; range.popFront())
- pred(range.front.expand);
+ {
+ static if (!is(typeof(fun(r.front.expand)) == Flag!"each"))
+ {
+ cast(void) fun(range.front.expand);
+ }
+ else
+ {
+ if (fun(range.front.expand)) return No.each;
+ }
+ }
}
+ return Yes.each;
}
- void each(Iterable)(auto ref Iterable r)
+ /// ditto
+ Flag!"each" each(Iterable)(auto ref Iterable r)
if (isForeachIterable!Iterable ||
__traits(compiles, Parameters!(Parameters!(r.opApply))))
{
static if (isForeachIterable!Iterable)
{
- debug(each) pragma(msg, "Using foreach for ", Iterable.stringof);
static if (isForeachUnaryIterable!Iterable)
{
- foreach (ref e; r)
- cast(void) unaryFun!pred(e);
+ debug(each) pragma(msg, "Using foreach UNARY for ", Iterable.stringof);
+ {
+ foreach (ref e; r)
+ {
+ static if (!is(typeof(unaryFun!fun(e)) == Flag!"each"))
+ {
+ cast(void) unaryFun!fun(e);
+ }
+ else
+ {
+ if (unaryFun!fun(e) == No.each) return No.each;
+ }
+ }
+ }
+ }
+ else static if (isForeachBinaryIterable!Iterable)
+ {
+ debug(each) pragma(msg, "Using foreach BINARY for ", Iterable.stringof);
+ foreach (ref a, ref b; r)
+ {
+ static if (!is(typeof(binaryFun!fun(a, b)) == Flag!"each"))
+ {
+ cast(void) binaryFun!fun(a, b);
+ }
+ else
+ {
+ if (binaryFun!fun(a, b) == No.each) return No.each;
+ }
+ }
}
- else // if (isForeachBinaryIterable!Iterable)
+ else static if (isForeachUnaryWithIndexIterable!Iterable)
{
- foreach (ref i, ref e; r)
- cast(void) binaryFun!BinaryArgs(i, e);
+ debug(each) pragma(msg, "Using foreach INDEX for ", Iterable.stringof);
+ foreach (i, ref e; r)
+ {
+ static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each"))
+ {
+ cast(void) binaryFun!BinaryArgs(i, e);
+ }
+ else
+ {
+ if (binaryFun!BinaryArgs(i, e) == No.each) return No.each;
+ }
+ }
+ }
+ else
+ {
+ static assert(0, "Invalid foreach iteratable type " ~ Iterable.stringof ~ " met.");
}
+ return Yes.each;
}
else
{
// opApply with >2 parameters. count the delegate args.
// only works if it is not templated (otherwise we cannot count the args)
- auto dg(Parameters!(Parameters!(r.opApply)) params) {
- pred(params);
- return 0; // tells opApply to continue iteration
+ auto result = Yes.each;
+ auto dg(Parameters!(Parameters!(r.opApply)) params)
+ {
+ static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each"))
+ {
+ fun(params);
+ return 0; // tells opApply to continue iteration
+ }
+ else
+ {
+ result = fun(params);
+ return result == Yes.each ? 0 : -1;
+ }
}
r.opApply(&dg);
+ return result;
}
}
}
///
-@system unittest
+@safe unittest
{
import std.range : iota;
+ import std.typecons : No;
- long[] arr;
+ int[] arr;
iota(5).each!(n => arr ~= n);
assert(arr == [0, 1, 2, 3, 4]);
+ // stop iterating early
+ iota(5).each!((n) { arr ~= n; return No.each; });
+ assert(arr == [0, 1, 2, 3, 4, 0]);
+
// If the range supports it, the value can be mutated in place
arr.each!((ref n) => n++);
- assert(arr == [1, 2, 3, 4, 5]);
+ assert(arr == [1, 2, 3, 4, 5, 1]);
arr.each!"a++";
- assert(arr == [2, 3, 4, 5, 6]);
+ assert(arr == [2, 3, 4, 5, 6, 2]);
+ auto m = arr.map!(n => n);
// by-ref lambdas are not allowed for non-ref ranges
- static assert(!is(typeof(arr.map!(n => n).each!((ref n) => n++))));
+ static assert(!__traits(compiles, m.each!((ref n) => n++)));
// The default predicate consumes the range
- auto m = arr.map!(n => n);
(&m).each();
assert(m.empty);
+}
+
+/// `each` can pass an index variable for iterable objects which support this
+@safe unittest
+{
+ auto arr = new size_t[4];
- // Indexes are also available for in-place mutations
- arr[] = 0;
arr.each!"a=i"();
- assert(arr == [0, 1, 2, 3, 4]);
+ assert(arr == [0, 1, 2, 3]);
+
+ arr.each!((i, ref e) => e = i * 2);
+ assert(arr == [0, 2, 4, 6]);
+}
- // opApply iterators work as well
+/// opApply iterators work as well
+@system unittest
+{
static class S
{
int x;
@@ -1017,7 +1164,8 @@ public:
assert(b == [ 3, 4, 5 ]);
}
-// #15358: application of `each` with >2 args (opApply)
+// https://issues.dlang.org/show_bug.cgi?id=15358
+// application of `each` with >2 args (opApply)
@system unittest
{
import std.range : lockstep;
@@ -1032,7 +1180,8 @@ public:
assert(c == [7,8,9]);
}
-// #15358: application of `each` with >2 args (range interface)
+// https://issues.dlang.org/show_bug.cgi?id=15358
+// application of `each` with >2 args (range interface)
@safe unittest
{
import std.range : zip;
@@ -1047,7 +1196,8 @@ public:
assert(res == [9, 12, 15]);
}
-// #16255: `each` on opApply doesn't support ref
+// https://issues.dlang.org/show_bug.cgi?id=16255
+// `each` on opApply doesn't support ref
@safe unittest
{
int[] dynamicArray = [1, 2, 3, 4, 5];
@@ -1063,7 +1213,8 @@ public:
assert(staticArray == [3, 4, 5, 6, 7]);
}
-// #16255: `each` on opApply doesn't support ref
+// https://issues.dlang.org/show_bug.cgi?id=16255
+// `each` on opApply doesn't support ref
@system unittest
{
struct S
@@ -1079,21 +1230,94 @@ public:
assert(s.x == 2);
}
+// https://issues.dlang.org/show_bug.cgi?id=15357
+// `each` should behave similar to foreach
+@safe unittest
+{
+ import std.range : iota;
+
+ auto arr = [1, 2, 3, 4];
+
+ // 1 ref parameter
+ arr.each!((ref e) => e = 0);
+ assert(arr.sum == 0);
+
+ // 1 ref parameter and index
+ arr.each!((i, ref e) => e = cast(int) i);
+ assert(arr.sum == 4.iota.sum);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15357
+// `each` should behave similar to foreach
+@system unittest
+{
+ import std.range : iota, lockstep;
+
+ // 2 ref parameters and index
+ auto arrA = [1, 2, 3, 4];
+ auto arrB = [5, 6, 7, 8];
+ lockstep(arrA, arrB).each!((ref a, ref b) {
+ a = 0;
+ b = 1;
+ });
+ assert(arrA.sum == 0);
+ assert(arrB.sum == 4);
+
+ // 3 ref parameters
+ auto arrC = [3, 3, 3, 3];
+ lockstep(arrA, arrB, arrC).each!((ref a, ref b, ref c) {
+ a = 1;
+ b = 2;
+ c = 3;
+ });
+ assert(arrA.sum == 4);
+ assert(arrB.sum == 8);
+ assert(arrC.sum == 12);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15357
+// `each` should behave similar to foreach
+@system unittest
+{
+ import std.range : lockstep;
+ import std.typecons : Tuple;
+
+ auto a = "abc";
+ auto b = "def";
+
+ // print each character with an index
+ {
+ alias Element = Tuple!(size_t, "index", dchar, "value");
+ Element[] rForeach, rEach;
+ foreach (i, c ; a) rForeach ~= Element(i, c);
+ a.each!((i, c) => rEach ~= Element(i, c));
+ assert(rForeach == rEach);
+ assert(rForeach == [Element(0, 'a'), Element(1, 'b'), Element(2, 'c')]);
+ }
+
+ // print pairs of characters
+ {
+ alias Element = Tuple!(dchar, "a", dchar, "b");
+ Element[] rForeach, rEach;
+ foreach (c1, c2 ; a.lockstep(b)) rForeach ~= Element(c1, c2);
+ a.lockstep(b).each!((c1, c2) => rEach ~= Element(c1, c2));
+ assert(rForeach == rEach);
+ assert(rForeach == [Element('a', 'd'), Element('b', 'e'), Element('c', 'f')]);
+ }
+}
+
// filter
/**
-$(D auto filter(Range)(Range rs) if (isInputRange!(Unqual!Range));)
-
-Implements the higher order _filter function. The predicate is passed to
+Implements the higher order filter function. The predicate is passed to
$(REF unaryFun, std,functional), and can either accept a string, or any callable
-that can be executed via $(D pred(element)).
+that can be executed via `pred(element)`.
Params:
predicate = Function to apply to each element of range
- range = Input range of elements
Returns:
- $(D filter!(predicate)(range)) returns a new range containing only elements $(D x) in $(D range) for
- which $(D predicate(x)) returns $(D true).
+ `filter!(predicate)(range)` returns a new range containing only elements `x` in `range` for
+ which `predicate(x)` returns `true`.
See_Also:
$(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
@@ -1101,6 +1325,14 @@ See_Also:
template filter(alias predicate)
if (is(typeof(unaryFun!predicate)))
{
+ /**
+ Params:
+ range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ of elements
+ Returns:
+ A range containing only elements `x` in `range` for
+ which `predicate(x)` returns `true`.
+ */
auto filter(Range)(Range range) if (isInputRange!(Unqual!Range))
{
return FilterResult!(unaryFun!predicate, Range)(range);
@@ -1111,16 +1343,16 @@ if (is(typeof(unaryFun!predicate)))
@safe unittest
{
import std.algorithm.comparison : equal;
- import std.math : approxEqual;
+ import std.math.operations : isClose;
import std.range;
int[] arr = [ 1, 2, 3, 4, 5 ];
- // Sum all elements
+ // Filter below 3
auto small = filter!(a => a < 3)(arr);
assert(equal(small, [ 1, 2 ]));
- // Sum again, but with Uniform Function Call Syntax (UFCS)
+ // Filter again, but with Uniform Function Call Syntax (UFCS)
auto sum = arr.filter!(a => a < 3);
assert(equal(sum, [ 1, 2 ]));
@@ -1133,7 +1365,7 @@ if (is(typeof(unaryFun!predicate)))
// Mixing convertible types is fair game, too
double[] c = [ 2.5, 3.0 ];
auto r1 = chain(c, a, b).filter!(a => cast(int) a != a);
- assert(approxEqual(r1, [ 2.5 ]));
+ assert(isClose(r1, [ 2.5 ]));
}
private struct FilterResult(alias pred, Range)
@@ -1176,11 +1408,11 @@ private struct FilterResult(alias pred, Range)
void popFront()
{
+ prime;
do
{
_input.popFront();
} while (!_input.empty && !pred(_input.front));
- _primed = true;
}
@property auto ref front()
@@ -1298,10 +1530,25 @@ private struct FilterResult(alias pred, Range)
assert(equal(filter!underX(list), [ 1, 2, 3, 4 ]));
}
+// https://issues.dlang.org/show_bug.cgi?id=19823
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : dropOne;
+
+ auto a = [1, 2, 3, 4];
+ assert(a.filter!(a => a != 1).dropOne.equal([3, 4]));
+ assert(a.filter!(a => a != 2).dropOne.equal([3, 4]));
+ assert(a.filter!(a => a != 3).dropOne.equal([2, 4]));
+ assert(a.filter!(a => a != 4).dropOne.equal([2, 3]));
+ assert(a.filter!(a => a == 1).dropOne.empty);
+ assert(a.filter!(a => a == 2).dropOne.empty);
+ assert(a.filter!(a => a == 3).dropOne.empty);
+ assert(a.filter!(a => a == 4).dropOne.empty);
+}
+
/**
- * $(D auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range));)
- *
- * Similar to $(D filter), except it defines a
+ * Similar to `filter`, except it defines a
* $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives).
* There is a speed disadvantage - the constructor spends time
* finding the last element in the range that satisfies the filtering
@@ -1310,17 +1557,19 @@ private struct FilterResult(alias pred, Range)
* $(REF retro, std,range) can be applied against the filtered range.
*
* The predicate is passed to $(REF unaryFun, std,functional), and can either
- * accept a string, or any callable that can be executed via $(D pred(element)).
+ * accept a string, or any callable that can be executed via `pred(element)`.
*
* Params:
* pred = Function to apply to each element of range
- * r = Bidirectional range of elements
- *
- * Returns:
- * a new range containing only the elements in r for which pred returns $(D true).
*/
template filterBidirectional(alias pred)
{
+ /**
+ Params:
+ r = Bidirectional range of elements
+ Returns:
+ A range containing only the elements in `r` for which `pred` returns `true`.
+ */
auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range))
{
return FilterBidiResult!(unaryFun!pred, Range)(r);
@@ -1398,22 +1647,23 @@ private struct FilterBidiResult(alias pred, Range)
Groups consecutively equivalent elements into a single tuple of the element and
the number of its repetitions.
-Similarly to $(D uniq), $(D group) produces a range that iterates over unique
+Similarly to `uniq`, `group` produces a range that iterates over unique
consecutive elements of the given range. Each element of this range is a tuple
of the element and the number of times it is repeated in the original range.
-Equivalence of elements is assessed by using the predicate $(D pred), which
-defaults to $(D "a == b"). The predicate is passed to $(REF binaryFun, std,functional),
+Equivalence of elements is assessed by using the predicate `pred`, which
+defaults to `"a == b"`. The predicate is passed to $(REF binaryFun, std,functional),
and can either accept a string, or any callable that can be executed via
-$(D pred(element, element)).
+`pred(element, element)`.
Params:
pred = Binary predicate for determining equivalence of two elements.
+ R = The range type
r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
iterate over.
-Returns: A range of elements of type $(D Tuple!(ElementType!R, uint)),
+Returns: A range of elements of type `Tuple!(ElementType!R, uint)`,
representing each consecutively unique element and its respective number of
-occurrences in that run. This will be an input range if $(D R) is an input
+occurrences in that run. This will be an input range if `R` is an input
range, and a forward range in all other cases.
See_Also: $(LREF chunkBy), which chunks an input range into subranges
@@ -1457,6 +1707,12 @@ if (isInputRange!R)
if (!_input.empty) popFront();
}
+ private this(R input, Tuple!(MutableE, uint) current)
+ {
+ _input = input;
+ _current = current;
+ }
+
///
void popFront()
{
@@ -1490,7 +1746,7 @@ if (isInputRange!R)
}
}
- ///
+ /// Returns: the front of the range
@property auto ref front()
{
assert(!empty, "Attempting to fetch the front of an empty Group.");
@@ -1500,11 +1756,9 @@ if (isInputRange!R)
static if (isForwardRange!R)
{
///
- @property typeof(this) save() {
- typeof(this) ret = this;
- ret._input = this._input.save;
- ret._current = this._current;
- return ret;
+ @property typeof(this) save()
+ {
+ return Group(_input.save, _current);
}
}
}
@@ -1567,7 +1821,7 @@ if (isInputRange!R)
import std.algorithm.comparison : equal;
import std.typecons : tuple;
- // Issue 13857
+ // https://issues.dlang.org/show_bug.cgi?id=13857
immutable(int)[] a1 = [1,1,2,2,2,3,4,4,5,6,6,7,8,9,9,9];
auto g1 = group(a1);
assert(equal(g1, [ tuple(1, 2u), tuple(2, 3u), tuple(3, 1u),
@@ -1575,18 +1829,18 @@ if (isInputRange!R)
tuple(7, 1u), tuple(8, 1u), tuple(9, 3u)
]));
- // Issue 13162
+ // https://issues.dlang.org/show_bug.cgi?id=13162
immutable(ubyte)[] a2 = [1, 1, 1, 0, 0, 0];
auto g2 = a2.group;
assert(equal(g2, [ tuple(1, 3u), tuple(0, 3u) ]));
- // Issue 10104
+ // https://issues.dlang.org/show_bug.cgi?id=10104
const a3 = [1, 1, 2, 2];
auto g3 = a3.group;
assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ]));
interface I {}
- class C : I {}
+ class C : I { override size_t toHash() const nothrow @safe { return 0; } }
const C[] a4 = [new const C()];
auto g4 = a4.group!"a is b";
assert(g4.front[1] == 1);
@@ -1600,18 +1854,52 @@ if (isInputRange!R)
assert(equal(g6.front[0], [1]));
}
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
+ auto r = arr.group;
+ assert(r.equal([ tuple(1,1u), tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ]));
+ r.popFront;
+ assert(r.equal([ tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ]));
+ auto s = r.save;
+ r.popFrontN(2);
+ assert(r.equal([ tuple(4, 3u), tuple(5, 1u) ]));
+ assert(s.equal([ tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ]));
+ s.popFront;
+ auto t = s.save;
+ r.popFront;
+ s.popFront;
+ assert(r.equal([ tuple(5, 1u) ]));
+ assert(s.equal([ tuple(4, 3u), tuple(5, 1u) ]));
+ assert(t.equal([ tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ]));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18657
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : refRange;
+ string s = "foo";
+ auto r = refRange(&s).group;
+ assert(equal(r.save, "foo".group));
+ assert(equal(r, "foo".group));
+}
+
// Used by implementation of chunkBy for non-forward input ranges.
private struct ChunkByChunkImpl(alias pred, Range)
if (isInputRange!Range && !isForwardRange!Range)
{
alias fun = binaryFun!pred;
- private Range r;
+ private Range *r;
private ElementType!Range prev;
- this(Range range, ElementType!Range _prev)
+ this(ref Range range, ElementType!Range _prev)
{
- r = range;
+ r = &range;
prev = _prev;
}
@@ -1620,18 +1908,26 @@ if (isInputRange!Range && !isForwardRange!Range)
return r.empty || !fun(prev, r.front);
}
- @property ElementType!Range front() { return r.front; }
- void popFront() { r.popFront(); }
+ @property ElementType!Range front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty chunkBy chunk.");
+ return r.front;
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty chunkBy chunk.");
+ r.popFront();
+ }
}
private template ChunkByImplIsUnary(alias pred, Range)
{
- static if (is(typeof(binaryFun!pred(ElementType!Range.init,
- ElementType!Range.init)) : bool))
+ alias e = lvalueOf!(ElementType!Range);
+
+ static if (is(typeof(binaryFun!pred(e, e)) : bool))
enum ChunkByImplIsUnary = false;
- else static if (is(typeof(
- unaryFun!pred(ElementType!Range.init) ==
- unaryFun!pred(ElementType!Range.init))))
+ else static if (is(typeof(unaryFun!pred(e) == unaryFun!pred(e)) : bool))
enum ChunkByImplIsUnary = true;
else
static assert(0, "chunkBy expects either a binary predicate or "~
@@ -1652,6 +1948,7 @@ if (isInputRange!Range && !isForwardRange!Range)
private Range r;
private ElementType!Range _prev;
+ private bool openChunk = false;
this(Range _r)
{
@@ -1673,10 +1970,12 @@ if (isInputRange!Range && !isForwardRange!Range)
_prev = typeof(_prev).init;
}
}
- @property bool empty() { return r.empty; }
+ @property bool empty() { return r.empty && !openChunk; }
@property auto front()
{
+ assert(!empty, "Attempting to fetch the front of an empty chunkBy.");
+ openChunk = true;
static if (isUnary)
{
import std.typecons : tuple;
@@ -1691,6 +1990,8 @@ if (isInputRange!Range && !isForwardRange!Range)
void popFront()
{
+ assert(!empty, "Attempting to popFront an empty chunkBy.");
+ openChunk = false;
while (!r.empty)
{
if (!eq(_prev, r.front))
@@ -1702,105 +2003,137 @@ if (isInputRange!Range && !isForwardRange!Range)
}
}
}
+// Outer range for forward range version of chunkBy
+private struct ChunkByOuter(Range, bool eqEquivalenceAssured)
+{
+ size_t groupNum;
+ Range current;
+ Range next;
+ static if (!eqEquivalenceAssured)
+ {
+ bool nextUpdated;
+ }
+}
-// Single-pass implementation of chunkBy for forward ranges.
-private struct ChunkByImpl(alias pred, Range)
-if (isForwardRange!Range)
+// Inner range for forward range version of chunkBy
+private struct ChunkByGroup(alias eq, Range, bool eqEquivalenceAssured)
{
import std.typecons : RefCounted;
- enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
-
- static if (isUnary)
- alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
- else
- alias eq = binaryFun!pred;
-
- // Outer range
- static struct Impl
- {
- size_t groupNum;
- Range current;
- Range next;
- }
+ alias OuterRange = ChunkByOuter!(Range, eqEquivalenceAssured);
- // Inner range
- static struct Group
+ private size_t groupNum;
+ static if (eqEquivalenceAssured)
{
- private size_t groupNum;
private Range start;
- private Range current;
+ }
+ private Range current;
- private RefCounted!Impl mothership;
+ private RefCounted!(OuterRange) mothership;
- this(RefCounted!Impl origin)
+ this(RefCounted!(OuterRange) origin)
+ {
+ groupNum = origin.groupNum;
+ current = origin.current.save;
+ assert(!current.empty, "Passed range 'r' must not be empty");
+ static if (eqEquivalenceAssured)
{
- groupNum = origin.groupNum;
-
start = origin.current.save;
- current = origin.current.save;
- assert(!start.empty);
-
- mothership = origin;
- // Note: this requires reflexivity.
+ // Check for reflexivity.
assert(eq(start.front, current.front),
- "predicate is not reflexive");
+ "predicate is not reflexive");
}
- @property bool empty() { return groupNum == size_t.max; }
- @property auto ref front() { return current.front; }
+ mothership = origin;
+ }
- void popFront()
+ @property bool empty() { return groupNum == size_t.max; }
+ @property auto ref front() { return current.front; }
+
+ void popFront()
+ {
+ static if (!eqEquivalenceAssured)
{
- current.popFront();
+ auto prevElement = current.front;
+ }
+
+ current.popFront();
- // Note: this requires transitivity.
- if (current.empty || !eq(start.front, current.front))
+ static if (eqEquivalenceAssured)
+ {
+ //this requires transitivity from the predicate.
+ immutable nowEmpty = current.empty || !eq(start.front, current.front);
+ }
+ else
+ {
+ immutable nowEmpty = current.empty || !eq(prevElement, current.front);
+ }
+
+
+ if (nowEmpty)
+ {
+ if (groupNum == mothership.groupNum)
{
- if (groupNum == mothership.groupNum)
+ // If parent range hasn't moved on yet, help it along by
+ // saving location of start of next Group.
+ mothership.next = current.save;
+ static if (!eqEquivalenceAssured)
{
- // If parent range hasn't moved on yet, help it along by
- // saving location of start of next Group.
- mothership.next = current.save;
+ mothership.nextUpdated = true;
}
-
- groupNum = size_t.max;
}
- }
- @property auto save()
- {
- auto copy = this;
- copy.current = current.save;
- return copy;
+ groupNum = size_t.max;
}
}
- static assert(isForwardRange!Group);
- private RefCounted!Impl impl;
+ @property auto save()
+ {
+ auto copy = this;
+ copy.current = current.save;
+ return copy;
+ }
+}
+
+private enum GroupingOpType{binaryEquivalent, binaryAny, unary}
+
+// Single-pass implementation of chunkBy for forward ranges.
+private struct ChunkByImpl(alias pred, alias eq, GroupingOpType opType, Range)
+if (isForwardRange!Range)
+{
+ import std.typecons : RefCounted;
+
+ enum bool eqEquivalenceAssured = opType != GroupingOpType.binaryAny;
+ alias OuterRange = ChunkByOuter!(Range, eqEquivalenceAssured);
+ alias InnerRange = ChunkByGroup!(eq, Range, eqEquivalenceAssured);
+
+ static assert(isForwardRange!InnerRange);
+
+ private RefCounted!OuterRange impl;
this(Range r)
{
- impl = RefCounted!Impl(0, r, r.save);
+ static if (eqEquivalenceAssured)
+ {
+ impl = RefCounted!OuterRange(0, r, r.save);
+ }
+ else impl = RefCounted!OuterRange(0, r, r.save, false);
}
@property bool empty() { return impl.current.empty; }
- @property auto front()
+ static if (opType == GroupingOpType.unary) @property auto front()
{
- static if (isUnary)
- {
- import std.typecons : tuple;
- return tuple(unaryFun!pred(impl.current.front), Group(impl));
- }
- else
- {
- return Group(impl);
- }
+ import std.typecons : tuple;
+ return tuple(unaryFun!pred(impl.current.front), InnerRange(impl));
+ }
+ else @property auto front()
+ {
+ return InnerRange(impl);
}
- void popFront()
+ static if (eqEquivalenceAssured) void popFront()
{
// Scan for next group. If we're lucky, one of our Groups would have
// already set .next to the start of the next group, in which case the
@@ -1815,6 +2148,24 @@ if (isForwardRange!Range)
// Indicate to any remaining Groups that we have moved on.
impl.groupNum++;
}
+ else void popFront()
+ {
+ if (impl.nextUpdated)
+ {
+ impl.current = impl.next.save;
+ }
+ else while (true)
+ {
+ auto prevElement = impl.current.front;
+ impl.current.popFront();
+ if (impl.current.empty) break;
+ if (!eq(prevElement, impl.current.front)) break;
+ }
+
+ impl.nextUpdated = false;
+ // Indicate to any remaining Groups that we have moved on.
+ impl.groupNum++;
+ }
@property auto save()
{
@@ -1823,7 +2174,43 @@ if (isForwardRange!Range)
return typeof(this)(impl.current.save);
}
- static assert(isForwardRange!(typeof(this)));
+ static assert(isForwardRange!(typeof(this)), typeof(this).stringof
+ ~ " must be a forward range");
+}
+
+//Test for https://issues.dlang.org/show_bug.cgi?id=14909
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+ import std.stdio;
+ auto n = 3;
+ auto s = [1,2,3].chunkBy!(a => a+n);
+ auto t = s.save.map!(x=>x[0]);
+ auto u = s.map!(x=>x[1]);
+ assert(t.equal([4,5,6]));
+ assert(u.equal!equal([[1],[2],[3]]));
+}
+
+//Test for https://issues.dlang.org/show_bug.cgi?id=18751
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ string[] data = [ "abc", "abc", "def" ];
+ int[] indices = [ 0, 1, 2 ];
+
+ auto chunks = indices.chunkBy!((i, j) => data[i] == data[j]);
+ assert(chunks.equal!equal([ [ 0, 1 ], [ 2 ] ]));
+}
+
+//Additional test for fix for issues 14909 and 18751
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto v = [2,4,8,3,6,9,1,5,7];
+ auto i = 2;
+ assert(v.chunkBy!((a,b) => a % i == b % i).equal!equal([[2,4,8],[3],[6],[9,1,5,7]]));
}
@system unittest
@@ -1886,18 +2273,19 @@ if (isForwardRange!Range)
* In other languages this is often called `partitionBy`, `groupBy`
* or `sliceWhen`.
*
- * Equivalence is defined by the predicate $(D pred), which can be either
+ * Equivalence is defined by the predicate `pred`, which can be either
* binary, which is passed to $(REF binaryFun, std,functional), or unary, which is
- * passed to $(REF unaryFun, std,functional). In the binary form, two _range elements
- * $(D a) and $(D b) are considered equivalent if $(D pred(a,b)) is true. In
- * unary form, two elements are considered equivalent if $(D pred(a) == pred(b))
+ * passed to $(REF unaryFun, std,functional). In the binary form, two range elements
+ * `a` and `b` are considered equivalent if `pred(a,b)` is true. In
+ * unary form, two elements are considered equivalent if `pred(a) == pred(b)`
* is true.
*
* This predicate must be an equivalence relation, that is, it must be
- * reflexive ($(D pred(x,x)) is always true), symmetric
- * ($(D pred(x,y) == pred(y,x))), and transitive ($(D pred(x,y) && pred(y,z))
- * implies $(D pred(x,z))). If this is not the case, the range returned by
- * chunkBy may assert at runtime or behave erratically.
+ * reflexive (`pred(x,x)` is always true), symmetric
+ * (`pred(x,y) == pred(y,x)`), and transitive (`pred(x,y) && pred(y,z)`
+ * implies `pred(x,z)`). If this is not the case, the range returned by
+ * chunkBy may assert at runtime or behave erratically. Use $(LREF splitWhen)
+ * if you want to chunk by a predicate that is not an equivalence relation.
*
* Params:
* pred = Predicate for determining equivalence.
@@ -1907,7 +2295,8 @@ if (isForwardRange!Range)
* all elements in a given subrange are equivalent under the given predicate.
* With a unary predicate, a range of tuples is returned, with the tuple
* consisting of the result of the unary predicate for each subrange, and the
- * subrange itself.
+ * subrange itself. Copying the range currently has reference semantics, but this may
+ * change in the future.
*
* Notes:
*
@@ -1923,7 +2312,20 @@ if (isForwardRange!Range)
auto chunkBy(alias pred, Range)(Range r)
if (isInputRange!Range)
{
- return ChunkByImpl!(pred, Range)(r);
+ static if (ChunkByImplIsUnary!(pred, Range))
+ {
+ enum opType = GroupingOpType.unary;
+ alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
+ }
+ else
+ {
+ enum opType = GroupingOpType.binaryEquivalent;
+ alias eq = binaryFun!pred;
+ }
+ static if (isForwardRange!Range)
+ return ChunkByImpl!(pred, eq, opType, Range)(r);
+ else
+ return ChunkByImpl!(pred, Range)(r);
}
/// Showing usage with binary predicate:
@@ -1953,20 +2355,6 @@ if (isInputRange!Range)
]));
}
-version (none) // this example requires support for non-equivalence relations
-@safe unittest
-{
- // Grouping by maximum adjacent difference:
- import std.math : abs;
- auto r3 = [1, 3, 2, 5, 4, 9, 10].chunkBy!((a, b) => abs(a-b) < 3);
- assert(r3.equal!equal([
- [1, 3, 2],
- [5, 4],
- [9, 10]
- ]));
-
-}
-
/// Showing usage with unary predicate:
/* FIXME: pure @safe nothrow*/ @system unittest
{
@@ -2032,11 +2420,22 @@ version (none) // this example requires support for non-equivalence relations
R data;
this(R _data) pure @safe nothrow { data = _data; }
@property bool empty() pure @safe nothrow { return data.empty; }
- @property auto front() pure @safe nothrow { return data.front; }
- void popFront() pure @safe nothrow { data.popFront(); }
+ @property auto front() pure @safe nothrow { assert(!empty); return data.front; }
+ void popFront() pure @safe nothrow { assert(!empty); data.popFront(); }
}
auto refInputRange(R)(R range) { return new RefInputRange!R(range); }
+ // An input range API with value semantics.
+ struct ValInputRange(R)
+ {
+ R data;
+ this(R _data) pure @safe nothrow { data = _data; }
+ @property bool empty() pure @safe nothrow { return data.empty; }
+ @property auto front() pure @safe nothrow { assert(!empty); return data.front; }
+ void popFront() pure @safe nothrow { assert(!empty); data.popFront(); }
+ }
+ auto valInputRange(R)(R range) { return ValInputRange!R(range); }
+
{
auto arr = [ Item(1,2), Item(1,3), Item(2,3) ];
static assert(isForwardRange!(typeof(arr)));
@@ -2067,7 +2466,7 @@ version (none) // this example requires support for non-equivalence relations
assert(byY2.front[1].equal([ Item(1,2) ]));
}
- // Test non-forward input ranges.
+ // Test non-forward input ranges with reference semantics.
{
auto range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
auto byX = chunkBy!(a => a.x)(range);
@@ -2091,14 +2490,371 @@ version (none) // this example requires support for non-equivalence relations
assert(byY.empty);
assert(range.empty);
}
+
+ // Test non-forward input ranges with value semantics.
+ {
+ auto range = valInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
+ auto byX = chunkBy!(a => a.x)(range);
+ assert(byX.front[0] == 1);
+ assert(byX.front[1].equal([ Item(1,1), Item(1,2) ]));
+ byX.popFront();
+ assert(byX.front[0] == 2);
+ assert(byX.front[1].equal([ Item(2,2) ]));
+ byX.popFront();
+ assert(byX.empty);
+ assert(!range.empty); // Opposite of refInputRange test
+
+ range = valInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
+ auto byY = chunkBy!(a => a.y)(range);
+ assert(byY.front[0] == 1);
+ assert(byY.front[1].equal([ Item(1,1) ]));
+ byY.popFront();
+ assert(byY.front[0] == 2);
+ assert(byY.front[1].equal([ Item(1,2), Item(2,2) ]));
+ byY.popFront();
+ assert(byY.empty);
+ assert(!range.empty); // Opposite of refInputRange test
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=19532
+ * General behavior of non-forward input ranges.
+ *
+ * - If the same chunk is retrieved multiple times via front, the separate chunk
+ * instances refer to a shared range segment that advances as a single range.
+ * - Emptying a chunk via popFront does not implicitly popFront the chunk off
+ * main range. The chunk is still available via front, it is just empty.
+ */
+ {
+ import std.algorithm.comparison : equal;
+ import core.exception : AssertError;
+ import std.exception : assertThrown;
+
+ auto a = [[0, 0], [0, 1],
+ [1, 2], [1, 3], [1, 4],
+ [2, 5], [2, 6],
+ [3, 7],
+ [4, 8]];
+
+ // Value input range
+ {
+ auto r = valInputRange(a).chunkBy!((a, b) => a[0] == b[0]);
+
+ size_t numChunks = 0;
+ while (!r.empty)
+ {
+ ++numChunks;
+ auto chunk = r.front;
+ while (!chunk.empty)
+ {
+ assert(r.front.front[1] == chunk.front[1]);
+ chunk.popFront;
+ }
+ assert(!r.empty);
+ assert(r.front.empty);
+ r.popFront;
+ }
+
+ assert(numChunks == 5);
+
+ // Now front and popFront should assert.
+ bool thrown = false;
+ try r.front;
+ catch (AssertError) thrown = true;
+ assert(thrown);
+
+ thrown = false;
+ try r.popFront;
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+
+ // Reference input range
+ {
+ auto r = refInputRange(a).chunkBy!((a, b) => a[0] == b[0]);
+
+ size_t numChunks = 0;
+ while (!r.empty)
+ {
+ ++numChunks;
+ auto chunk = r.front;
+ while (!chunk.empty)
+ {
+ assert(r.front.front[1] == chunk.front[1]);
+ chunk.popFront;
+ }
+ assert(!r.empty);
+ assert(r.front.empty);
+ r.popFront;
+ }
+
+ assert(numChunks == 5);
+
+ // Now front and popFront should assert.
+ bool thrown = false;
+ try r.front;
+ catch (AssertError) thrown = true;
+ assert(thrown);
+
+ thrown = false;
+ try r.popFront;
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+
+ // Ensure that starting with an empty range doesn't create an empty chunk.
+ {
+ int[] emptyRange = [];
+
+ auto r1 = valInputRange(emptyRange).chunkBy!((a, b) => a == b);
+ auto r2 = refInputRange(emptyRange).chunkBy!((a, b) => a == b);
+
+ assert(r1.empty);
+ assert(r2.empty);
+
+ bool thrown = false;
+ try r1.front;
+ catch (AssertError) thrown = true;
+ assert(thrown);
+
+ thrown = false;
+ try r1.popFront;
+ catch (AssertError) thrown = true;
+ assert(thrown);
+
+ thrown = false;
+ try r2.front;
+ catch (AssertError) thrown = true;
+ assert(thrown);
+
+ thrown = false;
+ try r2.popFront;
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19532 - Using roundRobin/chunkBy
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : roundRobin;
+
+ auto a0 = [0, 1, 3, 6];
+ auto a1 = [0, 2, 4, 6, 7];
+ auto a2 = [1, 2, 4, 6, 8, 8, 9];
+
+ auto expected =
+ [[0, 0], [1, 1], [2, 2], [3], [4, 4], [6, 6, 6], [7], [8, 8], [9]];
+
+ auto r1 = roundRobin(valInputRange(a0), valInputRange(a1), valInputRange(a2))
+ .chunkBy!((a, b) => a == b);
+ assert(r1.equal!equal(expected));
+
+ auto r2 = roundRobin(refInputRange(a0), refInputRange(a1), refInputRange(a2))
+ .chunkBy!((a, b) => a == b);
+ assert(r2.equal!equal(expected));
+
+ auto r3 = roundRobin(a0, a1, a2).chunkBy!((a, b) => a == b);
+ assert(r3.equal!equal(expected));
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19532 - Using merge/chunkBy
+ {
+ import std.algorithm.comparison : equal;
+ import std.algorithm.sorting : merge;
+
+ auto a0 = [2, 3, 5];
+ auto a1 = [2, 4, 5];
+ auto a2 = [1, 2, 4, 5];
+
+ auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]];
+
+ auto r1 = merge(valInputRange(a0), valInputRange(a1), valInputRange(a2))
+ .chunkBy!((a, b) => a == b);
+ assert(r1.equal!equal(expected));
+
+ auto r2 = merge(refInputRange(a0), refInputRange(a1), refInputRange(a2))
+ .chunkBy!((a, b) => a == b);
+ assert(r2.equal!equal(expected));
+
+ auto r3 = merge(a0, a1, a2).chunkBy!((a, b) => a == b);
+ assert(r3.equal!equal(expected));
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19532 - Using chunkBy/map-fold
+ {
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : fold, map;
+
+ auto a = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8, 9];
+ auto expected = [0, 3, 4, 6, 8, 5, 18, 7, 16, 9];
+
+ auto r1 = a
+ .chunkBy!((a, b) => a == b)
+ .map!(c => c.fold!((a, b) => a + b));
+ assert(r1.equal(expected));
+
+ auto r2 = valInputRange(a)
+ .chunkBy!((a, b) => a == b)
+ .map!(c => c.fold!((a, b) => a + b));
+ assert(r2.equal(expected));
+
+ auto r3 = refInputRange(a)
+ .chunkBy!((a, b) => a == b)
+ .map!(c => c.fold!((a, b) => a + b));
+ assert(r3.equal(expected));
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=16169
+ // https://issues.dlang.org/show_bug.cgi?id=17966
+ // https://issues.dlang.org/show_bug.cgi?id=19532
+ // Using multiwayMerge/chunkBy
+ {
+ import std.algorithm.comparison : equal;
+ import std.algorithm.setops : multiwayMerge;
+
+ {
+ auto a0 = [2, 3, 5];
+ auto a1 = [2, 4, 5];
+ auto a2 = [1, 2, 4, 5];
+
+ auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]];
+ auto r = multiwayMerge([a0, a1, a2]).chunkBy!((a, b) => a == b);
+ assert(r.equal!equal(expected));
+ }
+ {
+ auto a0 = [2, 3, 5];
+ auto a1 = [2, 4, 5];
+ auto a2 = [1, 2, 4, 5];
+
+ auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]];
+ auto r =
+ multiwayMerge([valInputRange(a0), valInputRange(a1), valInputRange(a2)])
+ .chunkBy!((a, b) => a == b);
+ assert(r.equal!equal(expected));
+ }
+ {
+ auto a0 = [2, 3, 5];
+ auto a1 = [2, 4, 5];
+ auto a2 = [1, 2, 4, 5];
+
+ auto expected = [[1], [2, 2, 2], [3], [4, 4], [5, 5, 5]];
+ auto r =
+ multiwayMerge([refInputRange(a0), refInputRange(a1), refInputRange(a2)])
+ .chunkBy!((a, b) => a == b);
+ assert(r.equal!equal(expected));
+ }
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=20496
+ {
+ auto r = [1,1,1,2,2,2,3,3,3];
+ r.chunkBy!((ref e1, ref e2) => e1 == e2);
+ }
+}
+
+
+
+// https://issues.dlang.org/show_bug.cgi?id=13805
+@system unittest
+{
+ [""].map!((s) => s).chunkBy!((x, y) => true);
+}
+
+/**
+Splits a forward range into subranges in places determined by a binary
+predicate.
+
+When iterating, one element of `r` is compared with `pred` to the next
+element. If `pred` return true, a new subrange is started for the next element.
+Otherwise, they are part of the same subrange.
+
+If the elements are compared with an inequality (!=) operator, consider
+$(LREF chunkBy) instead, as it's likely faster to execute.
+
+Params:
+pred = Predicate for determining where to split. The earlier element in the
+source range is always given as the first argument.
+r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to be split.
+Returns: a range of subranges of `r`, split such that within a given subrange,
+calling `pred` with any pair of adjacent elements as arguments returns `false`.
+Copying the range currently has reference semantics, but this may change in the future.
+
+See_also:
+$(LREF splitter), which uses elements as splitters instead of element-to-element
+relations.
+*/
+
+auto splitWhen(alias pred, Range)(Range r)
+if (isForwardRange!Range)
+{ import std.functional : not;
+ return ChunkByImpl!(not!pred, not!pred, GroupingOpType.binaryAny, Range)(r);
+}
+
+//FIXME: these should be @safe
+///
+nothrow pure @system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : dropExactly;
+ auto source = [4, 3, 2, 11, 0, -3, -3, 5, 3, 0];
+
+ auto result1 = source.splitWhen!((a,b) => a <= b);
+ assert(result1.save.equal!equal([
+ [4, 3, 2],
+ [11, 0, -3],
+ [-3],
+ [5, 3, 0]
+ ]));
+
+ //splitWhen, like chunkBy, is currently a reference range (this may change
+ //in future). Remember to call `save` when appropriate.
+ auto result2 = result1.dropExactly(2);
+ assert(result1.save.equal!equal([
+ [-3],
+ [5, 3, 0]
+ ]));
+}
+
+//ensure we don't iterate the underlying range twice
+nothrow @system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.math.algebraic : abs;
+
+ struct SomeRange
+ {
+ int[] elements;
+ static int popfrontsSoFar;
+
+ auto front(){return elements[0];}
+ nothrow void popFront()
+ { popfrontsSoFar++;
+ elements = elements[1 .. $];
+ }
+ auto empty(){return elements.length == 0;}
+ auto save(){return this;}
+ }
+
+ auto result = SomeRange([10, 9, 8, 5, 0, 1, 0, 8, 11, 10, 8, 12])
+ .splitWhen!((a, b) => abs(a - b) >= 3);
+
+ assert(result.equal!equal([
+ [10, 9, 8],
+ [5],
+ [0, 1, 0],
+ [8],
+ [11, 10, 8],
+ [12]
+ ]));
+
+ assert(SomeRange.popfrontsSoFar == 12);
}
// Issue 13595
-version (none) // This requires support for non-equivalence relations
@system unittest
{
import std.algorithm.comparison : equal;
- auto r = [1, 2, 3, 4, 5, 6, 7, 8, 9].chunkBy!((x, y) => ((x*y) % 3) == 0);
+ auto r = [1, 2, 3, 4, 5, 6, 7, 8, 9].splitWhen!((x, y) => ((x*y) % 3) > 0);
assert(r.equal!equal([
[1],
[2, 3, 4],
@@ -2107,10 +2863,25 @@ version (none) // This requires support for non-equivalence relations
]));
}
-// Issue 13805
-@system unittest
+nothrow pure @system unittest
{
- [""].map!((s) => s).chunkBy!((x, y) => true);
+ // Grouping by maximum adjacent difference:
+ import std.math.algebraic : abs;
+ import std.algorithm.comparison : equal;
+ auto r3 = [1, 3, 2, 5, 4, 9, 10].splitWhen!((a, b) => abs(a-b) >= 3);
+ assert(r3.equal!equal([
+ [1, 3, 2],
+ [5, 4],
+ [9, 10]
+ ]));
+}
+
+// empty range splitWhen
+@nogc nothrow pure @system unittest
+{
+ int[1] sliceable;
+ auto result = sliceable[0 .. 0].splitWhen!((a,b) => a+b > 10);
+ assert(result.empty);
}
// joiner
@@ -2127,13 +2898,21 @@ Params:
element(s) to serve as separators in the joined range.
Returns:
-A range of elements in the joined range. This will be a forward range if
-both outer and inner ranges of $(D RoR) are forward ranges; otherwise it will
-be only an input range.
+A range of elements in the joined range. This will be a bidirectional range if
+both outer and inner ranges of `RoR` are at least bidirectional ranges. Else if
+both outer and inner ranges of `RoR` are forward ranges, the returned range will
+be likewise. Otherwise it will be only an input range. The
+$(REF_ALTTEXT range bidirectionality, isBidirectionalRange, std,range,primitives)
+is propagated if no separator is specified.
See_also:
$(REF chain, std,range), which chains a sequence of ranges with compatible elements
into a single range.
+
+Note:
+When both outer and inner ranges of `RoR` are bidirectional and the joiner is
+iterated from the back to the front, the separator will still be consumed from
+front to back, even if it is a bidirectional range too.
*/
auto joiner(RoR, Separator)(RoR r, Separator sep)
if (isInputRange!RoR && isInputRange!(ElementType!RoR)
@@ -2144,18 +2923,78 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
{
private RoR _items;
private ElementType!RoR _current;
- private Separator _sep, _currentSep;
-
- // This is a mixin instead of a function for the following reason (as
- // explained by Kenji Hara): "This is necessary from 2.061. If a
- // struct has a nested struct member, it must be directly initialized
- // in its constructor to avoid leaving undefined state. If you change
- // setItem to a function, the initialization of _current field is
- // wrapped into private member function, then compiler could not detect
- // that is correctly initialized while constructing. To avoid the
- // compiler error check, string mixin is used."
- private enum setItem =
- q{
+ bool inputStartsWithEmpty = false;
+ static if (isBidirectional)
+ {
+ private ElementType!RoR _currentBack;
+ bool inputEndsWithEmpty = false;
+ }
+ enum isBidirectional = isBidirectionalRange!RoR &&
+ isBidirectionalRange!(ElementType!RoR);
+ static if (isRandomAccessRange!Separator)
+ {
+ static struct CurrentSep
+ {
+ private Separator _sep;
+ private size_t sepIndex;
+ private size_t sepLength; // cache the length for performance
+ auto front() { return _sep[sepIndex]; }
+ void popFront() { sepIndex++; }
+ auto empty() { return sepIndex >= sepLength; }
+ auto save()
+ {
+ auto copy = this;
+ copy._sep = _sep;
+ return copy;
+ }
+ void reset()
+ {
+ sepIndex = 0;
+ }
+
+ void initialize(Separator sep)
+ {
+ _sep = sep;
+ sepIndex = sepLength = _sep.length;
+ }
+ }
+ }
+ else
+ {
+ static struct CurrentSep
+ {
+ private Separator _sep;
+ Separator payload;
+
+ alias payload this;
+
+ auto save()
+ {
+ auto copy = this;
+ copy._sep = _sep;
+ return copy;
+ }
+
+ void reset()
+ {
+ payload = _sep.save;
+ }
+
+ void initialize(Separator sep)
+ {
+ _sep = sep;
+ }
+ }
+ }
+
+ private CurrentSep _currentSep;
+ static if (isBidirectional)
+ {
+ private CurrentSep _currentBackSep;
+ }
+
+ private void setItem()
+ {
if (!_items.empty)
{
// If we're exporting .save, we must not consume any of the
@@ -2167,20 +3006,22 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
else
_current = _items.front;
}
- };
+ }
private void useSeparator()
{
// Separator must always come after an item.
- assert(_currentSep.empty && !_items.empty,
- "joiner: internal error");
+ assert(_currentSep.empty,
+ "Attempting to reset a non-empty separator");
+ assert(!_items.empty,
+ "Attempting to use a separator in an empty joiner");
_items.popFront();
// If there are no more items, we're done, since separators are not
// terminators.
if (_items.empty) return;
- if (_sep.empty)
+ if (_currentSep._sep.empty)
{
// Advance to the next range in the
// input
@@ -2189,41 +3030,29 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
_items.popFront();
if (_items.empty) return;
}
- mixin(setItem);
+ setItem;
}
else
{
- _currentSep = _sep.save;
- assert(!_currentSep.empty);
+ _currentSep.reset;
+ assert(!_currentSep.empty, "separator must not be empty");
}
}
- private enum useItem =
- q{
- // FIXME: this will crash if either _currentSep or _current are
- // class objects, because .init is null when the ctor invokes this
- // mixin.
- //assert(_currentSep.empty && _current.empty,
- // "joiner: internal error");
-
- // Use the input
- if (_items.empty) return;
- mixin(setItem);
- if (_current.empty)
- {
- // No data in the current item - toggle to use the separator
- useSeparator();
- }
- };
-
this(RoR items, Separator sep)
{
_items = items;
- _sep = sep;
+ _currentSep.initialize(sep);
+ static if (isBidirectional)
+ _currentBackSep.initialize(sep);
//mixin(useItem); // _current should be initialized in place
if (_items.empty)
+ {
_current = _current.init; // set invalid state
+ static if (isBidirectional)
+ _currentBack = _currentBack.init;
+ }
else
{
// If we're exporting .save, we must not consume any of the
@@ -2235,10 +3064,42 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
else
_current = _items.front;
+ static if (isBidirectional)
+ {
+ _currentBack = _items.back.save;
+
+ if (_currentBack.empty)
+ {
+ // No data in the currentBack item - toggle to use
+ // the separator
+ inputEndsWithEmpty = true;
+ }
+ }
+
if (_current.empty)
{
// No data in the current item - toggle to use the separator
- useSeparator();
+ inputStartsWithEmpty = true;
+
+ // If RoR contains a single empty element,
+ // the returned Result will always be empty
+ import std.range : dropOne;
+ static if (hasLength!RoR)
+ {
+ if (_items.length == 1)
+ _items.popFront;
+ }
+ else static if (isForwardRange!RoR)
+ {
+ if (_items.save.dropOne.empty)
+ _items.popFront;
+ }
+ else
+ {
+ auto _itemsCopy = _items;
+ if (_itemsCopy.dropOne.empty)
+ _items.popFront;
+ }
}
}
}
@@ -2248,8 +3109,18 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
return _items.empty;
}
+ //no data in the first item of the initial range - use the separator
+ private enum useSepIfFrontIsEmpty = q{
+ if (inputStartsWithEmpty)
+ {
+ useSeparator();
+ inputStartsWithEmpty = false;
+ }
+ };
+
@property ElementType!(ElementType!RoR) front()
{
+ mixin(useSepIfFrontIsEmpty);
if (!_currentSep.empty) return _currentSep.front;
assert(!_current.empty, "Attempting to fetch the front of an empty joiner.");
return _current.front;
@@ -2259,18 +3130,27 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
{
assert(!_items.empty, "Attempting to popFront an empty joiner.");
// Using separator?
+ mixin(useSepIfFrontIsEmpty);
+
if (!_currentSep.empty)
{
_currentSep.popFront();
- if (!_currentSep.empty) return;
- mixin(useItem);
+ if (_currentSep.empty && !_items.empty)
+ {
+ setItem;
+ if (_current.empty)
+ {
+ // No data in the current item - toggle to use the separator
+ useSeparator();
+ }
+ }
}
else
{
// we're using the range
_current.popFront();
- if (!_current.empty) return;
- useSeparator();
+ if (_current.empty)
+ useSeparator();
}
}
@@ -2281,11 +3161,103 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
Result copy = this;
copy._items = _items.save;
copy._current = _current.save;
- copy._sep = _sep.save;
copy._currentSep = _currentSep.save;
+ static if (isBidirectional)
+ {
+ copy._currentBack = _currentBack;
+ copy._currentBackSep = _currentBackSep;
+ }
return copy;
}
}
+
+ static if (isBidirectional)
+ {
+ //no data in the last item of the initial range - use the separator
+ private enum useSepIfBackIsEmpty = q{
+ if (inputEndsWithEmpty)
+ {
+ useBackSeparator;
+ inputEndsWithEmpty = false;
+ }
+ };
+
+ private void setBackItem()
+ {
+ if (!_items.empty)
+ {
+ _currentBack = _items.back.save;
+ }
+ }
+
+ private void useBackSeparator()
+ {
+ // Separator must always come after an item.
+ assert(_currentBackSep.empty,
+ "Attempting to reset a non-empty separator");
+ assert(!_items.empty,
+ "Attempting to use a separator in an empty joiner");
+ _items.popBack();
+
+ // If there are no more items, we're done, since separators are not
+ // terminators.
+ if (_items.empty) return;
+
+ if (_currentBackSep._sep.empty)
+ {
+ // Advance to the next range in the
+ // input
+ while (_items.back.empty)
+ {
+ _items.popBack();
+ if (_items.empty) return;
+ }
+ setBackItem;
+ }
+ else
+ {
+ _currentBackSep.reset;
+ assert(!_currentBackSep.empty, "separator must not be empty");
+ }
+ }
+
+ @property ElementType!(ElementType!RoR) back()
+ {
+ mixin(useSepIfBackIsEmpty);
+
+ if (!_currentBackSep.empty) return _currentBackSep.front;
+ assert(!_currentBack.empty, "Attempting to fetch the back of an empty joiner.");
+ return _currentBack.back;
+ }
+
+ void popBack()
+ {
+ assert(!_items.empty, "Attempting to popBack an empty joiner.");
+
+ mixin(useSepIfBackIsEmpty);
+
+ if (!_currentBackSep.empty)
+ {
+ _currentBackSep.popFront();
+ if (_currentBackSep.empty && !_items.empty)
+ {
+ setBackItem;
+ if (_currentBack.empty)
+ {
+ // No data in the current item - toggle to use the separator
+ useBackSeparator();
+ }
+ }
+ }
+ else
+ {
+ // we're using the range
+ _currentBack.popBack();
+ if (_currentBack.empty)
+ useBackSeparator();
+ }
+ }
+ }
}
return Result(r, sep);
}
@@ -2305,6 +3277,12 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
assert(["", ""].joiner("xyz").equal("xyz"));
}
+@safe pure nothrow unittest
+{
+ //joiner with separator can return a bidirectional range
+ assert(isBidirectionalRange!(typeof(["abc", "def"].joiner("..."))));
+}
+
@system unittest
{
import std.algorithm.comparison : equal;
@@ -2320,7 +3298,7 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
import std.algorithm.comparison : equal;
import std.range;
- // Related to issue 8061
+ // Related to https://issues.dlang.org/show_bug.cgi?id=8061
auto r = joiner([
inputRangeObject("abc"),
inputRangeObject("def"),
@@ -2357,7 +3335,7 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
assert(equal(u, "+-abc+-+-def+-"));
- // Issue 13441: only(x) as separator
+ // https://issues.dlang.org/show_bug.cgi?id=13441: only(x) as separator
string[][] lines = [null];
lines
.joiner(only("b"))
@@ -2417,6 +3395,174 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)
static assert(isForwardRange!(typeof(joiner([""], ""))));
}
+@safe pure unittest
+{
+ {
+ import std.algorithm.comparison : equal;
+ auto r = joiner(["abc", "def", "ghi"], "?!");
+ char[] res;
+ while (!r.empty)
+ {
+ res ~= r.back;
+ r.popBack;
+ }
+ assert(res.equal("ihg?!fed?!cba"));
+ }
+
+ {
+ wchar[] sep = ['Ș', 'Ț'];
+ auto r = joiner(["","abc",""],sep);
+ wchar[] resFront;
+ wchar[] resBack;
+
+ auto rCopy = r.save;
+ while (!r.empty)
+ {
+ resFront ~= r.front;
+ r.popFront;
+ }
+
+ while (!rCopy.empty)
+ {
+ resBack ~= rCopy.back;
+ rCopy.popBack;
+ }
+
+ import std.algorithm.comparison : equal;
+
+ assert(resFront.equal("ȘȚabcȘȚ"));
+ assert(resBack.equal("ȘȚcbaȘȚ"));
+ }
+
+ {
+ import std.algorithm.comparison : equal;
+ auto r = [""];
+ r.popBack;
+ assert(r.joiner("AB").equal(""));
+ }
+
+ {
+ auto r = ["", "", "", "abc", ""].joiner("../");
+ auto rCopy = r.save;
+
+ char[] resFront;
+ char[] resBack;
+
+ while (!r.empty)
+ {
+ resFront ~= r.front;
+ r.popFront;
+ }
+
+ while (!rCopy.empty)
+ {
+ resBack ~= rCopy.back;
+ rCopy.popBack;
+ }
+
+ import std.algorithm.comparison : equal;
+
+ assert(resFront.equal("../../../abc../"));
+ assert(resBack.equal("../cba../../../"));
+ }
+
+ {
+ auto r = ["", "abc", ""].joiner("./");
+ auto rCopy = r.save;
+ r.popBack;
+ rCopy.popFront;
+
+ auto rRev = r.save;
+ auto rCopyRev = rCopy.save;
+
+ char[] r1, r2, r3, r4;
+
+ while (!r.empty)
+ {
+ r1 ~= r.back;
+ r.popBack;
+ }
+
+ while (!rCopy.empty)
+ {
+ r2 ~= rCopy.front;
+ rCopy.popFront;
+ }
+
+ while (!rRev.empty)
+ {
+ r3 ~= rRev.front;
+ rRev.popFront;
+ }
+
+ while (!rCopyRev.empty)
+ {
+ r4 ~= rCopyRev.back;
+ rCopyRev.popBack;
+ }
+
+ import std.algorithm.comparison : equal;
+
+ assert(r1.equal("/cba./"));
+ assert(r2.equal("/abc./"));
+ assert(r3.equal("./abc"));
+ assert(r4.equal("./cba"));
+ }
+}
+
+@system unittest
+{
+ import std.range;
+ import std.algorithm.comparison : equal;
+
+ assert(inputRangeObject([""]).joiner("lz").equal(""));
+}
+
+@safe pure unittest
+{
+ struct inputRangeStrings
+ {
+ private string[] strings;
+
+ string front()
+ {
+ return strings[0];
+ }
+
+ void popFront()
+ {
+ strings = strings[1..$];
+ }
+
+ bool empty() const
+ {
+ return strings.length == 0;
+ }
+ }
+
+ auto arr = inputRangeStrings([""]);
+
+ import std.algorithm.comparison : equal;
+
+ assert(arr.joiner("./").equal(""));
+}
+
+@safe pure unittest
+{
+ auto r = joiner(["", "", "abc", "", ""], "");
+ char[] res;
+ while (!r.empty)
+ {
+ res ~= r.back;
+ r.popBack;
+ }
+
+ import std.algorithm.comparison : equal;
+
+ assert(res.equal("cba"));
+}
+
+
/// Ditto
auto joiner(RoR)(RoR r)
if (isInputRange!RoR && isInputRange!(ElementType!RoR))
@@ -2426,50 +3572,33 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
private:
RoR _items;
ElementType!RoR _current;
- enum prepare =
- q{
- // Skip over empty subranges.
- if (_items.empty) return;
- while (_items.front.empty)
- {
- _items.popFront();
- if (_items.empty) return;
- }
- // We cannot export .save method unless we ensure subranges are not
- // consumed when a .save'd copy of ourselves is iterated over. So
- // we need to .save each subrange we traverse.
- static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
- _current = _items.front.save;
- else
- _current = _items.front;
- };
+ enum isBidirectional = isForwardRange!RoR && isForwardRange!(ElementType!RoR) &&
+ isBidirectionalRange!RoR && isBidirectionalRange!(ElementType!RoR);
+ static if (isBidirectional)
+ {
+ ElementType!RoR _currentBack;
+ bool reachedFinalElement;
+ }
+
this(RoR items, ElementType!RoR current)
{
_items = items;
_current = current;
+ static if (isBidirectional && hasNested!Result)
+ _currentBack = typeof(_currentBack).init;
}
+
public:
this(RoR r)
{
_items = r;
- //mixin(prepare); // _current should be initialized in place
- // Skip over empty subranges.
- while (!_items.empty && _items.front.empty)
- _items.popFront();
-
- if (_items.empty)
- _current = _current.init; // set invalid state
- else
- {
- // We cannot export .save method unless we ensure subranges are not
- // consumed when a .save'd copy of ourselves is iterated over. So
- // we need to .save each subrange we traverse.
- static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
- _current = _items.front.save;
- else
- _current = _items.front;
- }
+ static if (isBidirectional && hasNested!Result)
+ _currentBack = typeof(_currentBack).init;
+ // field _current must be initialized in constructor, because it is nested struct
+ mixin(popFrontEmptyElements);
+ static if (isBidirectional)
+ mixin(popBackEmptyElements);
}
static if (isInfinite!RoR)
{
@@ -2493,16 +3622,45 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
_current.popFront();
if (_current.empty)
{
- assert(!_items.empty);
+ assert(!_items.empty, "Attempting to popFront an empty joiner.");
_items.popFront();
- mixin(prepare);
+ mixin(popFrontEmptyElements);
}
}
+
+ private enum popFrontEmptyElements = q{
+ // Skip over empty subranges.
+ while (!_items.empty && _items.front.empty)
+ {
+ _items.popFront();
+ }
+ if (!_items.empty)
+ {
+ // We cannot export .save method unless we ensure subranges are not
+ // consumed when a .save'd copy of ourselves is iterated over. So
+ // we need to .save each subrange we traverse.
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ _current = _items.front.save;
+ else
+ _current = _items.front;
+ }
+ else
+ {
+ _current = typeof(_current).init;
+ }
+ };
+
static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
{
@property auto save()
{
- return Result(_items.save, _current.save);
+ auto r = Result(_items.save, _current.save);
+ static if (isBidirectional)
+ {
+ r._currentBack = _currentBack.save;
+ r.reachedFinalElement = reachedFinalElement;
+ }
+ return r;
}
}
@@ -2520,28 +3678,134 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
_current.front = element;
}
}
+
+ static if (isBidirectional)
+ {
+ bool checkFinalElement()
+ {
+ import std.range : dropOne;
+
+ if (reachedFinalElement)
+ return true;
+
+ static if (hasLength!(typeof(_items)))
+ {
+ if (_items.length == 1)
+ reachedFinalElement = true;
+ }
+ else
+ {
+ if (_items.save.dropOne.empty)
+ reachedFinalElement = true;
+ }
+
+ return false;
+ }
+
+ @property auto ref back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty joiner.");
+ if (reachedFinalElement)
+ return _current.back;
+ else
+ return _currentBack.back;
+ }
+
+ void popBack()
+ {
+ assert(!_current.empty, "Attempting to popBack an empty joiner.");
+ if (checkFinalElement)
+ _current.popBack();
+ else
+ _currentBack.popBack();
+
+ bool isEmpty = reachedFinalElement ? _current.empty : _currentBack.empty;
+ if (isEmpty)
+ {
+ assert(!_items.empty, "Attempting to popBack an empty joiner.");
+ _items.popBack();
+ mixin(popBackEmptyElements);
+ }
+ }
+
+ private enum popBackEmptyElements = q{
+ // Skip over empty subranges.
+ while (!_items.empty && _items.back.empty)
+ {
+ _items.popBack();
+ }
+ if (!_items.empty)
+ {
+ checkFinalElement;
+ // We cannot export .save method unless we ensure subranges are not
+ // consumed when a .save'd copy of ourselves is iterated over. So
+ // we need to .save each subrange we traverse.
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ {
+ if (reachedFinalElement)
+ _current = _items.back.save;
+ else
+ _currentBack = _items.back.save;
+ }
+ else
+ {
+ if (reachedFinalElement)
+ _current = _items.back;
+ else
+ _currentBack = _items.back;
+ }
+ }
+ else
+ {
+ _current = typeof(_current).init;
+ _currentBack = typeof(_currentBack).init;
+ }
+ };
+
+ static if (hasAssignableElements!(ElementType!RoR))
+ {
+ @property void back(ElementType!(ElementType!RoR) element)
+ {
+ assert(!empty, "Attempting to assign to back of an empty joiner.");
+ if (reachedFinalElement)
+ _current.back = element;
+ else
+ _currentBack.back = element;
+ }
+
+ @property void back(ref ElementType!(ElementType!RoR) element)
+ {
+ assert(!empty, "Attempting to assign to back of an empty joiner.");
+ if (reachedFinalElement)
+ _current.back = element;
+ else
+ _currentBack.back = element;
+ }
+ }
+ }
}
return Result(r);
}
+///
@safe unittest
{
import std.algorithm.comparison : equal;
- import std.range.interfaces : inputRangeObject;
import std.range : repeat;
- static assert(isInputRange!(typeof(joiner([""]))));
- static assert(isForwardRange!(typeof(joiner([""]))));
- assert(equal(joiner([""]), ""));
- assert(equal(joiner(["", ""]), ""));
- assert(equal(joiner(["", "abc"]), "abc"));
- assert(equal(joiner(["abc", ""]), "abc"));
- assert(equal(joiner(["abc", "def"]), "abcdef"));
- assert(equal(joiner(["Mary", "has", "a", "little", "lamb"]),
- "Maryhasalittlelamb"));
- assert(equal(joiner(repeat("abc", 3)), "abcabcabc"));
-
- // joiner allows in-place mutation!
+ assert([""].joiner.equal(""));
+ assert(["", ""].joiner.equal(""));
+ assert(["", "abc"].joiner.equal("abc"));
+ assert(["abc", ""].joiner.equal("abc"));
+ assert(["abc", "def"].joiner.equal("abcdef"));
+ assert(["Mary", "has", "a", "little", "lamb"].joiner.equal("Maryhasalittlelamb"));
+ assert("abc".repeat(3).joiner.equal("abcabcabc"));
+}
+
+/// joiner allows in-place mutation!
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
auto a = [ [1, 2, 3], [42, 43] ];
auto j = joiner(a);
j.front = 44;
@@ -2549,16 +3813,61 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
assert(equal(j, [44, 2, 3, 42, 43]));
}
+/// insert characters fully lazily into a string
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : chain, cycle, iota, only, retro, take, zip;
+ import std.format : format;
+
+ static immutable number = "12345678";
+ static immutable delimiter = ",";
+ auto formatted = number.retro
+ .zip(3.iota.cycle.take(number.length))
+ .map!(z => chain(z[0].only, z[1] == 2 ? delimiter : null))
+ .joiner
+ .retro;
+ static immutable expected = "12,345,678";
+ assert(formatted.equal(expected));
+}
+
+@safe unittest
+{
+ import std.range.interfaces : inputRangeObject;
+ static assert(isInputRange!(typeof(joiner([""]))));
+ static assert(isForwardRange!(typeof(joiner([""]))));
+}
+
+@safe unittest
+{
+ // Initial version of PR #6115 caused a compilation failure for
+ // https://github.com/BlackEdder/ggplotd/blob/d4428c08db5ffdc05dfd29690bf7da9073ea1dc5/source/ggplotd/stat.d#L562-L583
+ import std.range : zip;
+ int[] xCoords = [1, 2, 3];
+ int[] yCoords = [4, 5, 6];
+ auto coords = zip(xCoords, xCoords[1..$]).map!( (xr) {
+ return zip(yCoords, yCoords[1..$]).map!( (yr) {
+ return [
+ [[xr[0], xr[0], xr[1]],
+ [yr[0], yr[1], yr[1]]],
+ [[xr[0], xr[1], xr[1]],
+ [yr[0], yr[0], yr[1]]]
+ ];
+ }).joiner;
+ }).joiner;
+}
@system unittest
{
import std.algorithm.comparison : equal;
import std.range.interfaces : inputRangeObject;
+ import std.range : retro;
- // bugzilla 8240
+ // https://issues.dlang.org/show_bug.cgi?id=8240
assert(equal(joiner([inputRangeObject("")]), ""));
+ assert(equal(joiner([inputRangeObject("")]).retro, ""));
- // issue 8792
+ // https://issues.dlang.org/show_bug.cgi?id=8792
auto b = [[1], [2], [3]];
auto jb = joiner(b);
auto js = jb.save;
@@ -2573,6 +3882,118 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
assert(!equal(js2, js));
}
+// https://issues.dlang.org/show_bug.cgi?id=19213
+@system unittest
+{
+ auto results = [[1,2], [3,4]].map!(q => q.chunkBy!"a").joiner;
+ int i = 1;
+ foreach (ref e; results)
+ assert(e[0] == i++);
+}
+
+/// joiner can be bidirectional
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+
+ auto a = [[1, 2, 3], [4, 5]];
+ auto j = a.joiner;
+ j.back = 44;
+ assert(a == [[1, 2, 3], [4, 44]]);
+ assert(equal(j.retro, [44, 4, 3, 2, 1]));
+}
+
+// bidirectional joiner: test for filtering empty elements
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+
+ alias El = (e) => new int(e);
+ auto a = [null, [null, El(1), null, El(2), null, El(3), null], null, [null, El(4), null, El(5), null]];
+ auto j = a.joiner;
+
+ alias deref = a => a is null ? -1 : *a;
+ auto expected = [-1, 5, -1, 4, -1, -1, 3, -1, 2, -1, 1, -1];
+ // works with .save.
+ assert(j.save.retro.map!deref.equal(expected));
+ // and without .save
+ assert(j.retro.map!deref.equal(expected));
+ assert(j.retro.map!deref.equal(expected));
+}
+
+// bidirectional joiner is @nogc
+@safe @nogc unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota, only, retro;
+
+ auto a = only(iota(1, 4), iota(4, 6));
+ auto j = a.joiner;
+ static immutable expected = [5 , 4, 3, 2, 1];
+ assert(equal(j.retro, expected));
+}
+
+// bidirectional joiner supports assignment to the back
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : popBackN;
+
+ auto a = [[1, 2, 3], [4, 5]];
+ auto j = a.joiner;
+ j.back = 55;
+ assert(a == [[1, 2, 3], [4, 55]]);
+ j.popBackN(2);
+ j.back = 33;
+ assert(a == [[1, 2, 33], [4, 55]]);
+}
+
+// bidirectional joiner works with auto-decoding
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+
+ auto a = ["😀ðŸ˜", "😠"];
+ auto j = a.joiner;
+ assert(j.retro.equal("😠ðŸ˜ðŸ˜€"));
+}
+
+// test two-side iteration
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : popBackN;
+
+ auto arrs = [
+ [[1], [2], [3], [4], [5]],
+ [[1], [2, 3, 4], [5]],
+ [[1, 2, 3, 4, 5]],
+ ];
+ foreach (arr; arrs)
+ {
+ auto a = arr.joiner;
+ assert(a.front == 1);
+ assert(a.back == 5);
+ a.popFront;
+ assert(a.front == 2);
+ assert(a.back == 5);
+ a.popBack;
+ assert(a.front == 2);
+ assert(a.back == 4);
+ a.popFront;
+ assert(a.front == 3);
+ assert(a.back == 4);
+ a.popBack;
+ assert(a.front == 3);
+ assert(a.back == 3);
+ a.popBack;
+ assert(a.empty);
+ }
+}
+
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -2667,7 +4088,7 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
to!string("Unexpected result: '%s'"d.algoFormat(result)));
}
-// Issue 8061
+// https://issues.dlang.org/show_bug.cgi?id=8061
@system unittest
{
import std.conv : to;
@@ -2692,17 +4113,23 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
{
return element;
}
+ alias back = front;
enum empty = false;
- void popFront()
+ auto save()
{
+ return this;
}
+ void popFront() {}
+ alias popBack = popFront;
+
@property void front(int newValue)
{
element = newValue;
}
+ alias back = front;
}
static assert(isInputRange!AssignableRange);
@@ -2712,34 +4139,53 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR))
auto range = new AssignableRange();
assert(range.element == 0);
+ {
+ auto joined = joiner(repeat(range));
+ joined.front = 5;
+ assert(range.element == 5);
+ assert(joined.front == 5);
+
+ joined.popFront;
+ int byRef = 7;
+ joined.front = byRef;
+ assert(range.element == byRef);
+ assert(joined.front == byRef);
+ }
+ {
+ auto joined = joiner(repeat(range));
+ joined.back = 5;
+ assert(range.element == 5);
+ assert(joined.back == 5);
- auto joined = joiner(repeat(range));
- joined.front = 5;
- assert(range.element == 5);
- assert(joined.front == 5);
+ joined.popBack;
+ int byRef = 7;
+ joined.back = byRef;
+ assert(range.element == byRef);
+ assert(joined.back == byRef);
+ }
+}
- joined.popFront;
- int byRef = 7;
- joined.front = byRef;
- assert(range.element == byRef);
- assert(joined.front == byRef);
+// https://issues.dlang.org/show_bug.cgi?id=19850
+@safe pure unittest
+{
+ assert([[0]].joiner.save.back == 0);
}
/++
-Implements the homonym function (also known as $(D accumulate), $(D
-compress), $(D inject), or $(D foldl)) present in various programming
+Implements the homonym function (also known as `accumulate`, $(D
+compress), `inject`, or `foldl`) present in various programming
languages of functional flavor. There is also $(LREF fold) which does
the same thing but with the opposite parameter order.
-The call $(D reduce!(fun)(seed, range)) first assigns $(D seed) to
-an internal variable $(D result), also called the accumulator.
-Then, for each element $(D x) in $(D range), $(D result = fun(result, x))
-gets evaluated. Finally, $(D result) is returned.
-The one-argument version $(D reduce!(fun)(range))
+The call `reduce!(fun)(seed, range)` first assigns `seed` to
+an internal variable `result`, also called the accumulator.
+Then, for each element `x` in `range`, `result = fun(result, x)`
+gets evaluated. Finally, `result` is returned.
+The one-argument version `reduce!(fun)(range)`
works similarly, but it uses the first element of the range as the
seed (the range must be non-empty).
Returns:
- the accumulated $(D result)
+ the accumulated `result`
Params:
fun = one or more functions
@@ -2747,11 +4193,11 @@ Params:
See_Also:
$(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
- $(LREF fold) is functionally equivalent to $(LREF reduce) with the argument order reversed,
- and without the need to use $(LREF tuple) for multiple seeds. This makes it easier
- to use in UFCS chains.
+ $(LREF fold) is functionally equivalent to $(LREF _reduce) with the argument
+ order reversed, and without the need to use $(REF_ALTTEXT `tuple`,tuple,std,typecons)
+ for multiple seeds. This makes it easier to use in UFCS chains.
- $(LREF sum) is similar to $(D reduce!((a, b) => a + b)) that offers
+ $(LREF sum) is similar to `reduce!((a, b) => a + b)` that offers
pairwise summing of floating point numbers.
+/
template reduce(fun...)
@@ -2764,23 +4210,23 @@ if (fun.length >= 1)
import std.typecons : tuple, isTuple;
/++
- No-seed version. The first element of $(D r) is used as the seed's value.
+ No-seed version. The first element of `r` is used as the seed's value.
- For each function $(D f) in $(D fun), the corresponding
- seed type $(D S) is $(D Unqual!(typeof(f(e, e)))), where $(D e) is an
- element of $(D r): $(D ElementType!R) for ranges,
- and $(D ForeachType!R) otherwise.
+ For each function `f` in `fun`, the corresponding
+ seed type `S` is `Unqual!(typeof(f(e, e)))`, where `e` is an
+ element of `r`: `ElementType!R` for ranges,
+ and `ForeachType!R` otherwise.
- Once S has been determined, then $(D S s = e;) and $(D s = f(s, e);)
+ Once S has been determined, then `S s = e;` and `s = f(s, e);`
must both be legal.
- If $(D r) is empty, an $(D Exception) is thrown.
-
Params:
- r = an iterable value as defined by $(D isIterable)
+ r = an iterable value as defined by `isIterable`
Returns:
the final result of the accumulator applied to the iterable
+
+ Throws: `Exception` if `r` is empty
+/
auto reduce(R)(R r)
if (isIterable!R)
@@ -2791,7 +4237,13 @@ if (fun.length >= 1)
static if (isInputRange!R)
{
- enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value.");
+ // no need to throw if range is statically known to be non-empty
+ static if (!__traits(compiles,
+ {
+ static assert(r.length > 0);
+ }))
+ enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value.");
+
Args result = r.front;
r.popFront();
return reduceImpl!false(r, result);
@@ -2804,19 +4256,19 @@ if (fun.length >= 1)
}
/++
- Seed version. The seed should be a single value if $(D fun) is a
- single function. If $(D fun) is multiple functions, then $(D seed)
- should be a $(REF Tuple, std,typecons), with one field per function in $(D f).
+ Seed version. The seed should be a single value if `fun` is a
+ single function. If `fun` is multiple functions, then `seed`
+ should be a $(REF Tuple, std,typecons), with one field per function in `f`.
For convenience, if the seed is const, or has qualified fields, then
- $(D reduce) will operate on an unqualified copy. If this happens
- then the returned type will not perfectly match $(D S).
+ `reduce` will operate on an unqualified copy. If this happens
+ then the returned type will not perfectly match `S`.
- Use $(D fold) instead of $(D reduce) to use the seed version in a UFCS chain.
+ Use `fold` instead of `reduce` to use the seed version in a UFCS chain.
Params:
seed = the initial value of the accumulator
- r = an iterable value as defined by $(D isIterable)
+ r = an iterable value as defined by `isIterable`
Returns:
the final result of the accumulator applied to the iterable
@@ -2853,7 +4305,7 @@ if (fun.length >= 1)
alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R);
static if (mustInitialize) bool initialized = false;
- foreach (/+auto ref+/ E e; r) // @@@4707@@@
+ foreach (/+auto ref+/ E e; r) // https://issues.dlang.org/show_bug.cgi?id=4707
{
foreach (i, f; binfuns)
{
@@ -2869,7 +4321,7 @@ if (fun.length >= 1)
static if (mustInitialize) if (initialized == false)
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
foreach (i, f; binfuns)
emplaceRef!(Args[i])(args[i], e);
initialized = true;
@@ -2880,8 +4332,15 @@ if (fun.length >= 1)
args[i] = f(args[i], e);
}
static if (mustInitialize)
- if (!initialized)
- throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value.");
+ // no need to throw if range is statically known to be non-empty
+ static if (!__traits(compiles,
+ {
+ static assert(r.length > 0);
+ }))
+ {
+ if (!initialized)
+ throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value.");
+ }
static if (Args.length == 1)
return args[0];
@@ -2891,14 +4350,14 @@ if (fun.length >= 1)
}
/**
-Many aggregate range operations turn out to be solved with $(D reduce)
-quickly and easily. The example below illustrates $(D reduce)'s
+Many aggregate range operations turn out to be solved with `reduce`
+quickly and easily. The example below illustrates `reduce`'s
remarkable power and flexibility.
*/
@safe unittest
{
import std.algorithm.comparison : max, min;
- import std.math : approxEqual;
+ import std.math.operations : isClose;
import std.range;
int[] arr = [ 1, 2, 3, 4, 5 ];
@@ -2935,38 +4394,39 @@ remarkable power and flexibility.
// Mixing convertible types is fair game, too
double[] c = [ 2.5, 3.0 ];
auto r1 = reduce!("a + b")(chain(a, b, c));
- assert(approxEqual(r1, 112.5));
+ assert(isClose(r1, 112.5));
// To minimize nesting of parentheses, Uniform Function Call Syntax can be used
auto r2 = chain(a, b, c).reduce!("a + b");
- assert(approxEqual(r2, 112.5));
+ assert(isClose(r2, 112.5));
}
/**
Sometimes it is very useful to compute multiple aggregates in one pass.
One advantage is that the computation is faster because the looping overhead
-is shared. That's why $(D reduce) accepts multiple functions.
-If two or more functions are passed, $(D reduce) returns a
+is shared. That's why `reduce` accepts multiple functions.
+If two or more functions are passed, `reduce` returns a
$(REF Tuple, std,typecons) object with one member per passed-in function.
The number of seeds must be correspondingly increased.
*/
@safe unittest
{
import std.algorithm.comparison : max, min;
- import std.math : approxEqual, sqrt;
+ import std.math.operations : isClose;
+ import std.math.algebraic : sqrt;
import std.typecons : tuple, Tuple;
double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ];
// Compute minimum and maximum in one pass
auto r = reduce!(min, max)(a);
// The type of r is Tuple!(int, int)
- assert(approxEqual(r[0], 2)); // minimum
- assert(approxEqual(r[1], 11)); // maximum
+ assert(isClose(r[0], 2)); // minimum
+ assert(isClose(r[1], 11)); // maximum
// Compute sum and sum of squares in one pass
r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a);
- assert(approxEqual(r[0], 35)); // sum
- assert(approxEqual(r[1], 233)); // sum of squares
+ assert(isClose(r[0], 35)); // sum
+ assert(isClose(r[1], 233)); // sum of squares
// Compute average and standard deviation from the above
auto avg = r[0] / a.length;
assert(avg == 5);
@@ -3003,7 +4463,7 @@ The number of seeds must be correspondingly increased.
assert(rep[2 .. $] == "1, 2, 3, 4, 5", "["~rep[2 .. $]~"]");
}
-@system unittest
+@safe unittest
{
import std.algorithm.comparison : max, min;
import std.exception : assertThrown;
@@ -3015,7 +4475,7 @@ The number of seeds must be correspondingly increased.
{
bool actEmpty;
- int opApply(scope int delegate(ref int) dg)
+ int opApply(scope int delegate(ref int) @safe dg)
{
int res;
if (actEmpty) return res;
@@ -3055,7 +4515,8 @@ The number of seeds must be correspondingly increased.
@safe unittest
{
- // Issue #10408 - Two-function reduce of a const array.
+ // https://issues.dlang.org/show_bug.cgi?id=10408
+ // Two-function reduce of a const array.
import std.algorithm.comparison : max, min;
import std.typecons : tuple, Tuple;
@@ -3068,7 +4529,7 @@ The number of seeds must be correspondingly increased.
@safe unittest
{
- //10709
+ // https://issues.dlang.org/show_bug.cgi?id=10709
import std.typecons : tuple, Tuple;
enum foo = "a + 0.5 * b";
@@ -3079,11 +4540,11 @@ The number of seeds must be correspondingly increased.
assert(r2 == tuple(3, 3));
}
-@system unittest
+@safe unittest
{
static struct OpApply
{
- int opApply(int delegate(ref int) dg)
+ int opApply(int delegate(ref int) @safe dg)
{
int[] a = [1, 2, 3];
@@ -3135,7 +4596,8 @@ The number of seeds must be correspondingly increased.
assert(minmaxElement([1, 2, 3]) == tuple(1, 3));
}
-@safe unittest //12569
+// https://issues.dlang.org/show_bug.cgi?id=12569
+@safe unittest
{
import std.algorithm.comparison : max, min;
import std.typecons : tuple;
@@ -3156,13 +4618,27 @@ The number of seeds must be correspondingly increased.
static assert(!is(typeof(reduce!(all, all)(tuple(1, 1), "hello"))));
}
-@safe unittest //13304
+// https://issues.dlang.org/show_bug.cgi?id=13304
+@safe unittest
{
int[] data;
static assert(is(typeof(reduce!((a, b) => a + b)(data))));
assert(data.length == 0);
}
+// https://issues.dlang.org/show_bug.cgi?id=13880
+// reduce shouldn't throw if the length is statically known
+pure nothrow @safe @nogc unittest
+{
+ import std.algorithm.comparison : min;
+ int[5] arr;
+ arr[2] = -1;
+ assert(arr.reduce!min == -1);
+
+ int[0] arr0;
+ assert(reduce!min(42, arr0) == 42);
+}
+
//Helper for Reduce
private template ReduceSeedType(E)
{
@@ -3187,31 +4663,39 @@ private template ReduceSeedType(E)
/++
-Implements the homonym function (also known as $(D accumulate), $(D
-compress), $(D inject), or $(D foldl)) present in various programming
-languages of functional flavor. The call $(D fold!(fun)(range, seed))
-first assigns $(D seed) to an internal variable $(D result),
-also called the accumulator. Then, for each element $(D x) in $(D
-range), $(D result = fun(result, x)) gets evaluated. Finally, $(D
-result) is returned. The one-argument version $(D fold!(fun)(range))
+Implements the homonym function (also known as `accumulate`, $(D
+compress), `inject`, or `foldl`) present in various programming
+languages of functional flavor. The call `fold!(fun)(range, seed)`
+first assigns `seed` to an internal variable `result`,
+also called the accumulator. Then, for each element `x` in $(D
+range), `result = fun(result, x)` gets evaluated. Finally, $(D
+result) is returned. The one-argument version `fold!(fun)(range)`
works similarly, but it uses the first element of the range as the
seed (the range must be non-empty).
-Returns:
- the accumulated $(D result)
+Params:
+ fun = the predicate function(s) to apply to the elements
See_Also:
$(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
- $(LREF sum) is similar to $(D fold!((a, b) => a + b)) that offers
+ $(LREF sum) is similar to `fold!((a, b) => a + b)` that offers
precise summing of floating point numbers.
- This is functionally equivalent to $(LREF reduce) with the argument order reversed,
- and without the need to use $(LREF tuple) for multiple seeds.
+ This is functionally equivalent to $(LREF reduce) with the argument order
+ reversed, and without the need to use $(REF_ALTTEXT `tuple`,tuple,std,typecons)
+ for multiple seeds.
+/
template fold(fun...)
if (fun.length >= 1)
{
+ /**
+ Params:
+ r = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to fold
+ seed = the initial value of the accumulator
+ Returns:
+ the accumulated `result`
+ */
auto fold(R, S...)(R r, S seed)
{
static if (S.length < 2)
@@ -3265,12 +4749,12 @@ if (fun.length >= 1)
/++
Similar to `fold`, but returns a range containing the successive reduced values.
-The call $(D cumulativeFold!(fun)(range, seed)) first assigns `seed` to an
+The call `cumulativeFold!(fun)(range, seed)` first assigns `seed` to an
internal variable `result`, also called the accumulator.
-The returned range contains the values $(D result = fun(result, x)) lazily
+The returned range contains the values `result = fun(result, x)` lazily
evaluated for each element `x` in `range`. Finally, the last element has the
-same value as $(D fold!(fun)(seed, range)).
-The one-argument version $(D cumulativeFold!(fun)(range)) works similarly, but
+same value as `fold!(fun)(seed, range)`.
+The one-argument version `cumulativeFold!(fun)(range)` works similarly, but
it returns the first element unchanged and uses it as seed for the next
elements.
This function is also known as
@@ -3289,6 +4773,11 @@ Returns:
See_Also:
$(HTTP en.wikipedia.org/wiki/Prefix_sum, Prefix Sum)
+
+Note:
+
+ In functional programming languages this is typically called `scan`, `scanl`,
+ `scanLeft` or `reductions`.
+/
template cumulativeFold(fun...)
if (fun.length >= 1)
@@ -3299,9 +4788,9 @@ if (fun.length >= 1)
/++
No-seed version. The first element of `r` is used as the seed's value.
For each function `f` in `fun`, the corresponding seed type `S` is
- $(D Unqual!(typeof(f(e, e)))), where `e` is an element of `r`:
+ `Unqual!(typeof(f(e, e)))`, where `e` is an element of `r`:
`ElementType!R`.
- Once `S` has been determined, then $(D S s = e;) and $(D s = f(s, e);) must
+ Once `S` has been determined, then `S s = e;` and `s = f(s, e);` must
both be legal.
Params:
@@ -3422,13 +4911,7 @@ if (fun.length >= 1)
}
}
- static if (hasLength!R)
- {
- @property size_t length()
- {
- return source.length;
- }
- }
+ mixin ImplementLength!source;
}
return Result(range, args);
@@ -3440,7 +4923,7 @@ if (fun.length >= 1)
{
import std.algorithm.comparison : max, min;
import std.array : array;
- import std.math : approxEqual;
+ import std.math.operations : isClose;
import std.range : chain;
int[] arr = [1, 2, 3, 4, 5];
@@ -3477,11 +4960,11 @@ if (fun.length >= 1)
// Mixing convertible types is fair game, too
double[] c = [2.5, 3.0];
auto r1 = cumulativeFold!"a + b"(chain(a, b, c));
- assert(approxEqual(r1, [3, 7, 107, 109.5, 112.5]));
+ assert(isClose(r1, [3, 7, 107, 109.5, 112.5]));
// To minimize nesting of parentheses, Uniform Function Call Syntax can be used
auto r2 = chain(a, b, c).cumulativeFold!"a + b";
- assert(approxEqual(r2, [3, 7, 107, 109.5, 112.5]));
+ assert(isClose(r2, [3, 7, 107, 109.5, 112.5]));
}
/**
@@ -3496,20 +4979,20 @@ The number of seeds must be correspondingly increased.
{
import std.algorithm.comparison : max, min;
import std.algorithm.iteration : map;
- import std.math : approxEqual;
+ import std.math.operations : isClose;
import std.typecons : tuple;
double[] a = [3.0, 4, 7, 11, 3, 2, 5];
// Compute minimum and maximum in one pass
auto r = a.cumulativeFold!(min, max);
// The type of r is Tuple!(int, int)
- assert(approxEqual(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2])); // minimum
- assert(approxEqual(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum
+ assert(isClose(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2])); // minimum
+ assert(isClose(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum
// Compute sum and sum of squares in one pass
auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0));
- assert(approxEqual(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35])); // sum
- assert(approxEqual(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares
+ assert(isClose(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35])); // sum
+ assert(isClose(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares
}
@safe unittest
@@ -3551,7 +5034,7 @@ The number of seeds must be correspondingly increased.
{
import std.algorithm.comparison : max, min;
import std.array : array;
- import std.math : approxEqual;
+ import std.math.operations : isClose;
import std.typecons : tuple;
const float a = 0.0;
@@ -3559,10 +5042,10 @@ The number of seeds must be correspondingly increased.
float[] c = [1.2, 3, 3.3];
auto r = cumulativeFold!"a + b"(b, a);
- assert(approxEqual(r, [1.2, 4.2, 7.5]));
+ assert(isClose(r, [1.2, 4.2, 7.5]));
auto r2 = cumulativeFold!"a + b"(c, a);
- assert(approxEqual(r2, [1.2, 4.2, 7.5]));
+ assert(isClose(r2, [1.2, 4.2, 7.5]));
const numbers = [10, 30, 20];
enum m = numbers.cumulativeFold!(min).array;
@@ -3573,16 +5056,16 @@ The number of seeds must be correspondingly increased.
@safe unittest
{
- import std.math : approxEqual;
+ import std.math.operations : isClose;
import std.typecons : tuple;
enum foo = "a + 0.5 * b";
auto r = [0, 1, 2, 3];
auto r1 = r.cumulativeFold!foo;
auto r2 = r.cumulativeFold!(foo, foo);
- assert(approxEqual(r1, [0, 0.5, 1.5, 3]));
- assert(approxEqual(r2.map!"a[0]", [0, 0.5, 1.5, 3]));
- assert(approxEqual(r2.map!"a[1]", [0, 0.5, 1.5, 3]));
+ assert(isClose(r1, [0, 0.5, 1.5, 3]));
+ assert(isClose(r2.map!"a[0]", [0, 0.5, 1.5, 3]));
+ assert(isClose(r2.map!"a[1]", [0, 0.5, 1.5, 3]));
}
@safe unittest
@@ -3602,7 +5085,8 @@ The number of seeds must be correspondingly increased.
assert(minmaxElement([1, 2, 3]).equal([tuple(1, 1), tuple(1, 2), tuple(1, 3)]));
}
-@safe unittest //12569
+// https://issues.dlang.org/show_bug.cgi?id=12569
+@safe unittest
{
import std.algorithm.comparison : equal, max, min;
import std.typecons : tuple;
@@ -3625,7 +5109,8 @@ The number of seeds must be correspondingly increased.
static assert(!__traits(compiles, cumulativeFold!(all, all)("hello", tuple(1, 1))));
}
-@safe unittest //13304
+// https://issues.dlang.org/show_bug.cgi?id=13304
+@safe unittest
{
int[] data;
assert(data.cumulativeFold!((a, b) => a + b).empty);
@@ -3652,55 +5137,66 @@ The number of seeds must be correspondingly increased.
// splitter
/**
-Lazily splits a range using an element as a separator. This can be used with
-any narrow string type or sliceable range type, but is most popular with string
-types.
+Lazily splits a range using an element or range as a separator.
+Separator ranges can be any narrow string type or sliceable range type.
Two adjacent separators are considered to surround an empty element in
-the split range. Use $(D filter!(a => !a.empty)) on the result to compress
+the split range. Use `filter!(a => !a.empty)` on the result to compress
empty elements.
-The predicate is passed to $(REF binaryFun, std,functional), and can either accept
-a string, or any callable that can be executed via $(D pred(element, s)).
+The predicate is passed to $(REF binaryFun, std,functional) and accepts
+any callable function that can be executed via `pred(element, s)`.
-If the empty range is given, the result is a range with one empty
-element. If a range with one separator is given, the result is a range
-with two empty elements.
+Notes:
+ If splitting a string on whitespace and token compression is desired,
+ consider using `splitter` without specifying a separator.
-If splitting a string on whitespace and token compression is desired,
-consider using $(D splitter) without specifying a separator (see fourth overload
-below).
+ If no separator is passed, the $(REF_ALTTEXT, unary, unaryFun, std,functional)
+ predicate `isTerminator` decides whether to accept an element of `r`.
Params:
pred = The predicate for comparing each element with the separator,
- defaulting to $(D "a == b").
+ defaulting to `"a == b"`.
r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be
- split. Must support slicing and $(D .length).
- s = The element to be treated as the separator between range segments to be
- split.
+ split. Must support slicing and `.length` or be a narrow string type.
+ s = The element (or range) to be treated as the separator
+ between range segments to be split.
+ isTerminator = The predicate for deciding where to split the range when no separator is passed
+ keepSeparators = The flag for deciding if the separators are kept
Constraints:
- The predicate $(D pred) needs to accept an element of $(D r) and the
- separator $(D s).
+ The predicate `pred` needs to accept an element of `r` and the
+ separator `s`.
Returns:
- An input range of the subranges of elements between separators. If $(D r)
+ An input range of the subranges of elements between separators. If `r`
is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
the returned range will be likewise.
+ When a range is used a separator, bidirectionality isn't possible.
+
+ If keepSeparators is equal to Yes.keepSeparators the output will also contain the
+ separators.
+
+ If an empty range is given, the result is an empty range. If a range with
+ one separator is given, the result is a range with two empty elements.
See_Also:
- $(REF _splitter, std,regex) for a version that splits using a regular
-expression defined separator.
+ $(REF _splitter, std,regex) for a version that splits using a regular expression defined separator,
+ $(REF _split, std,array) for a version that splits eagerly and
+ $(LREF splitWhen), which compares adjacent elements instead of element against separator.
*/
-auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
+auto splitter(alias pred = "a == b",
+ Flag!"keepSeparators" keepSeparators = No.keepSeparators,
+ Range,
+ Separator)(Range r, Separator s)
if (is(typeof(binaryFun!pred(r.front, s)) : bool)
&& ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range))
{
import std.algorithm.searching : find;
import std.conv : unsigned;
- static struct Result
+ struct Result
{
private:
Range _input;
@@ -3719,9 +5215,14 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
enum _separatorLength = 1;
}
+ static if (keepSeparators)
+ {
+ bool _wasSeparator = true;
+ }
+
static if (isBidirectionalRange!Range)
{
- static size_t lastIndexOf(Range haystack, Separator needle)
+ size_t lastIndexOf(Range haystack, Separator needle)
{
import std.range : retro;
auto r = haystack.retro().find!pred(needle);
@@ -3760,10 +5261,27 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
@property Range front()
{
assert(!empty, "Attempting to fetch the front of an empty splitter.");
- if (_frontLength == _unComputed)
+ static if (keepSeparators)
{
- auto r = _input.find!pred(_separator);
- _frontLength = _input.length - r.length;
+ if (!_wasSeparator)
+ {
+ _frontLength = _separatorLength;
+ _wasSeparator = true;
+ }
+ else if (_frontLength == _unComputed)
+ {
+ auto r = _input.find!pred(_separator);
+ _frontLength = _input.length - r.length;
+ _wasSeparator = false;
+ }
+ }
+ else
+ {
+ if (_frontLength == _unComputed)
+ {
+ auto r = _input.find!pred(_separator);
+ _frontLength = _input.length - r.length;
+ }
}
return _input[0 .. _frontLength];
}
@@ -3775,19 +5293,37 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
{
front;
}
- assert(_frontLength <= _input.length);
- if (_frontLength == _input.length)
+ assert(_frontLength <= _input.length, "The front position must"
+ ~ " not exceed the input.length");
+ static if (keepSeparators)
{
- // no more input and need to fetch => done
- _frontLength = _atEnd;
+ if (_frontLength == _input.length && !_wasSeparator)
+ {
+ _frontLength = _atEnd;
- // Probably don't need this, but just for consistency:
- _backLength = _atEnd;
+ _backLength = _atEnd;
+ }
+ else
+ {
+ _input = _input[_frontLength .. _input.length];
+ _frontLength = _unComputed;
+ }
}
else
{
- _input = _input[_frontLength + _separatorLength .. _input.length];
- _frontLength = _unComputed;
+ if (_frontLength == _input.length)
+ {
+ // no more input and need to fetch => done
+ _frontLength = _atEnd;
+
+ // Probably don't need this, but just for consistency:
+ _backLength = _atEnd;
+ }
+ else
+ {
+ _input = _input[_frontLength + _separatorLength .. _input.length];
+ _frontLength = _unComputed;
+ }
}
}
@@ -3806,16 +5342,40 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
@property Range back()
{
assert(!empty, "Attempting to fetch the back of an empty splitter.");
- if (_backLength == _unComputed)
+ static if (keepSeparators)
{
- immutable lastIndex = lastIndexOf(_input, _separator);
- if (lastIndex == -1)
+ if (!_wasSeparator)
{
- _backLength = _input.length;
+ _backLength = _separatorLength;
+ _wasSeparator = true;
}
- else
+ else if (_backLength == _unComputed)
+ {
+ immutable lastIndex = lastIndexOf(_input, _separator);
+ if (lastIndex == -1)
+ {
+ _backLength = _input.length;
+ }
+ else
+ {
+ _backLength = _input.length - lastIndex - 1;
+ }
+ _wasSeparator = false;
+ }
+ }
+ else
+ {
+ if (_backLength == _unComputed)
{
- _backLength = _input.length - lastIndex - 1;
+ immutable lastIndex = lastIndexOf(_input, _separator);
+ if (lastIndex == -1)
+ {
+ _backLength = _input.length;
+ }
+ else
+ {
+ _backLength = _input.length - lastIndex - 1;
+ }
}
}
return _input[_input.length - _backLength .. _input.length];
@@ -3829,17 +5389,34 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
// evaluate back to make sure it's computed
back;
}
- assert(_backLength <= _input.length);
- if (_backLength == _input.length)
+ assert(_backLength <= _input.length, "The end index must not"
+ ~ " exceed the length of the input");
+ static if (keepSeparators)
{
- // no more input and need to fetch => done
- _frontLength = _atEnd;
- _backLength = _atEnd;
+ if (_backLength == _input.length && !_wasSeparator)
+ {
+ _frontLength = _atEnd;
+ _backLength = _atEnd;
+ }
+ else
+ {
+ _input = _input[0 .. _input.length - _backLength];
+ _backLength = _unComputed;
+ }
}
else
{
- _input = _input[0 .. _input.length - _backLength - _separatorLength];
- _backLength = _unComputed;
+ if (_backLength == _input.length)
+ {
+ // no more input and need to fetch => done
+ _frontLength = _atEnd;
+ _backLength = _atEnd;
+ }
+ else
+ {
+ _input = _input[0 .. _input.length - _backLength - _separatorLength];
+ _backLength = _unComputed;
+ }
}
}
}
@@ -3848,21 +5425,239 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
return Result(r, s);
}
-///
+/// Basic splitting with characters and numbers.
@safe unittest
{
import std.algorithm.comparison : equal;
- assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ]));
+ assert("a|bc|def".splitter('|').equal([ "a", "bc", "def" ]));
+
+ int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
+ int[][] w = [ [1], [2, 3], [4, 5, 6] ];
+ assert(a.splitter(0).equal(w));
+}
+
+/// Basic splitting with characters and numbers and keeping sentinels.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : Yes;
+
+ assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
+ .equal([ "a", "|", "bc", "|", "def" ]));
+
+ int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
+ int[][] w = [ [1], [0], [2, 3], [0], [4, 5, 6] ];
+ assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
+}
+
+/// Adjacent separators.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
+ assert("ab".splitter('|').equal([ "ab" ]));
+
+ assert("a|b||c".splitter('|').equal([ "a", "b", "", "c" ]));
+ assert("hello world".splitter(' ').equal([ "hello", "", "world" ]));
+
+ auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+ auto w = [ [1, 2], [], [3], [4, 5], [] ];
+ assert(a.splitter(0).equal(w));
+}
+
+/// Adjacent separators and keeping sentinels.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : Yes;
+
+ assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
+ .equal([ "", "|", "ab", "|", "" ]));
+ assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
+ .equal([ "ab" ]));
+
+ assert("a|b||c".splitter!("a == b", Yes.keepSeparators)('|')
+ .equal([ "a", "|", "b", "|", "", "|", "c" ]));
+ assert("hello world".splitter!("a == b", Yes.keepSeparators)(' ')
+ .equal([ "hello", " ", "", " ", "world" ]));
+
+ auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+ auto w = [ [1, 2], [0], [], [0], [3], [0], [4, 5], [0], [] ];
+ assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
+}
+
+/// Empty and separator-only ranges.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : empty;
+
+ assert("".splitter('|').empty);
+ assert("|".splitter('|').equal([ "", "" ]));
+ assert("||".splitter('|').equal([ "", "", "" ]));
+}
+
+/// Empty and separator-only ranges and keeping sentinels.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : Yes;
+ import std.range : empty;
+
+ assert("".splitter!("a == b", Yes.keepSeparators)('|').empty);
+ assert("|".splitter!("a == b", Yes.keepSeparators)('|')
+ .equal([ "", "|", "" ]));
+ assert("||".splitter!("a == b", Yes.keepSeparators)('|')
+ .equal([ "", "|", "", "|", "" ]));
+}
+
+/// Use a range for splitting
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert("a=>bc=>def".splitter("=>").equal([ "a", "bc", "def" ]));
+ assert("a|b||c".splitter("||").equal([ "a|b", "c" ]));
+ assert("hello world".splitter(" ").equal([ "hello", "world" ]));
+
+ int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+ int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
+ assert(a.splitter([0, 0]).equal(w));
+
+ a = [ 0, 0 ];
+ assert(a.splitter([0, 0]).equal([ (int[]).init, (int[]).init ]));
+
+ a = [ 0, 0, 1 ];
+ assert(a.splitter([0, 0]).equal([ [], [1] ]));
+}
+
+/// Use a range for splitting
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : Yes;
+
+ assert("a=>bc=>def".splitter!("a == b", Yes.keepSeparators)("=>")
+ .equal([ "a", "=>", "bc", "=>", "def" ]));
+ assert("a|b||c".splitter!("a == b", Yes.keepSeparators)("||")
+ .equal([ "a|b", "||", "c" ]));
+ assert("hello world".splitter!("a == b", Yes.keepSeparators)(" ")
+ .equal([ "hello", " ", "world" ]));
+
+ int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+ int[][] w = [ [1, 2], [0, 0], [3, 0, 4, 5, 0] ];
+ assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]).equal(w));
+
+ a = [ 0, 0 ];
+ assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
+ .equal([ (int[]).init, [0, 0], (int[]).init ]));
+
+ a = [ 0, 0, 1 ];
+ assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
+ .equal([ [], [0, 0], [1] ]));
+}
+
+/// Custom predicate functions.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.ascii : toLower;
+
+ assert("abXcdxef".splitter!"a.toLower == b"('x').equal(
+ [ "ab", "cd", "ef" ]));
+
+ auto w = [ [0], [1], [2] ];
+ assert(w.splitter!"a.front == b"(1).equal([ [[0]], [[2]] ]));
+}
+
+/// Custom predicate functions.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : Yes;
+ import std.ascii : toLower;
+
+ assert("abXcdxef".splitter!("a.toLower == b", Yes.keepSeparators)('x')
+ .equal([ "ab", "X", "cd", "x", "ef" ]));
+
+ auto w = [ [0], [1], [2] ];
+ assert(w.splitter!("a.front == b", Yes.keepSeparators)(1)
+ .equal([ [[0]], [[1]], [[2]] ]));
+}
+
+/// Use splitter without a separator
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : front;
+
+ assert(equal(splitter!(a => a == '|')("a|bc|def"), [ "a", "bc", "def" ]));
+ assert(equal(splitter!(a => a == ' ')("hello world"), [ "hello", "", "world" ]));
+
int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
- assert(equal(splitter(a, 0), w));
+ assert(equal(splitter!(a => a == 0)(a), w));
+
a = [ 0 ];
- assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ]));
+ assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ]));
+
a = [ 0, 1 ];
- assert(equal(splitter(a, 0), [ [], [1] ]));
+ assert(equal(splitter!(a => a == 0)(a), [ [], [1] ]));
+
w = [ [0], [1], [2] ];
- assert(equal(splitter!"a.front == b"(w, 1), [ [[0]], [[2]] ]));
+ assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));
+}
+
+/// Leading separators, trailing separators, or no separators.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
+ assert("ab".splitter('|').equal([ "ab" ]));
+}
+
+/// Leading separators, trailing separators, or no separators.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : Yes;
+
+ assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
+ .equal([ "", "|", "ab", "|", "" ]));
+ assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
+ .equal([ "ab" ]));
+}
+
+/// Splitter returns bidirectional ranges if the delimiter is a single element
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+ assert("a|bc|def".splitter('|').retro.equal([ "def", "bc", "a" ]));
+}
+
+/// Splitter returns bidirectional ranges if the delimiter is a single element
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : Yes;
+ import std.range : retro;
+ assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
+ .retro.equal([ "def", "|", "bc", "|", "a" ]));
+}
+
+/// Splitting by word lazily
+@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!"]));
}
@safe unittest
@@ -3884,6 +5679,7 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
a = [ 0 ];
assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ][]));
a = [ 0, 1 ];
+ assert(equal(splitter(a, 0), [ [], [1] ]));
assert(equal(splitter(a, 0), [ [], [1] ][]));
// Thoroughly exercise the bidirectional stuff.
@@ -3912,7 +5708,9 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
assert(split.front == "b ");
assert(split.back == "r ");
- foreach (DummyType; AllDummyRanges) { // Bug 4408
+ // https://issues.dlang.org/show_bug.cgi?id=4408
+ foreach (DummyType; AllDummyRanges)
+ {
static if (isRandomAccessRange!DummyType)
{
static assert(isBidirectionalRange!DummyType);
@@ -3939,38 +5737,30 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool)
assert(s.empty);
}
-/**
-Similar to the previous overload of $(D splitter), except this one uses another
-range as a separator. This can be used with any narrow string type or sliceable
-range type, but is most popular with string types. The predicate is passed to
-$(REF binaryFun, std,functional), and can either accept a string, or any callable
-that can be executed via $(D pred(r.front, s.front)).
-
-Two adjacent separators are considered to surround an empty element in
-the split range. Use $(D filter!(a => !a.empty)) on the result to compress
-empty elements.
+// https://issues.dlang.org/show_bug.cgi?id=18470
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
-Params:
- pred = The predicate for comparing each element with the separator,
- defaulting to $(D "a == b").
- r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be
- split.
- s = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to
- be treated as the separator between segments of $(D r) to be split.
+ const w = [[0], [1], [2]];
+ assert(w.splitter!((a, b) => a.front() == b)(1).equal([[[0]], [[2]]]));
+}
-Constraints:
- The predicate $(D pred) needs to accept an element of $(D r) and an
- element of $(D s).
+// https://issues.dlang.org/show_bug.cgi?id=18470
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.ascii : toLower;
-Returns:
- An input range of the subranges of elements between separators. If $(D r)
- is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
- the returned range will be likewise.
+ assert("abXcdxef".splitter!"a.toLower == b"('x').equal(["ab", "cd", "ef"]));
+ assert("abXcdxef".splitter!((a, b) => a.toLower == b)('x').equal(["ab", "cd", "ef"]));
+}
-See_Also: $(REF _splitter, std,regex) for a version that splits using a regular
-expression defined separator.
- */
-auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
+/// ditto
+auto splitter(alias pred = "a == b",
+ Flag!"keepSeparators" keepSeparators = No.keepSeparators,
+ Range,
+ Separator)(Range r, Separator s)
if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
&& (hasSlicing!Range || isNarrowString!Range)
&& isForwardRange!Separator
@@ -3986,33 +5776,38 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
Separator _separator;
// _frontLength == size_t.max means empty
size_t _frontLength = size_t.max;
- static if (isBidirectionalRange!Range)
- size_t _backLength = size_t.max;
+
+ static if (keepSeparators)
+ {
+ bool _wasSeparator = true;
+ }
@property auto separatorLength() { return _separator.length; }
void ensureFrontLength()
{
if (_frontLength != _frontLength.max) return;
- assert(!_input.empty);
- // compute front length
- _frontLength = (_separator.empty) ? 1 :
- _input.length - find!pred(_input, _separator).length;
- static if (isBidirectionalRange!Range)
- if (_frontLength == _input.length) _backLength = _frontLength;
- }
-
- void ensureBackLength()
- {
- static if (isBidirectionalRange!Range)
- if (_backLength != _backLength.max) return;
- assert(!_input.empty);
- // compute back length
- static if (isBidirectionalRange!Range && isBidirectionalRange!Separator)
+ static if (keepSeparators)
{
- import std.range : retro;
- _backLength = _input.length -
- find!pred(retro(_input), retro(_separator)).source.length;
+ assert(!_input.empty || _wasSeparator, "The input must not be empty");
+ if (_wasSeparator)
+ {
+ _frontLength = _input.length -
+ find!pred(_input, _separator).length;
+ _wasSeparator = false;
+ }
+ else
+ {
+ _frontLength = separatorLength();
+ _wasSeparator = true;
+ }
+ }
+ else
+ {
+ assert(!_input.empty, "The input must not be empty");
+ // compute front length
+ _frontLength = (_separator.empty) ? 1 :
+ _input.length - find!pred(_input, _separator).length;
}
}
@@ -4038,7 +5833,14 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
{
@property bool empty()
{
- return _frontLength == size_t.max && _input.empty;
+ static if (keepSeparators)
+ {
+ return _frontLength == size_t.max && _input.empty && !_wasSeparator;
+ }
+ else
+ {
+ return _frontLength == size_t.max && _input.empty;
+ }
}
}
@@ -4046,28 +5848,32 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
{
assert(!empty, "Attempting to popFront an empty splitter.");
ensureFrontLength();
- if (_frontLength == _input.length)
+
+ static if (keepSeparators)
{
- // done, there's no separator in sight
- _input = _input[_frontLength .. _frontLength];
- _frontLength = _frontLength.max;
- static if (isBidirectionalRange!Range)
- _backLength = _backLength.max;
- return;
+ _input = _input[_frontLength .. _input.length];
}
- if (_frontLength + separatorLength == _input.length)
+ else
{
- // Special case: popping the first-to-last item; there is
- // an empty item right after this.
- _input = _input[_input.length .. _input.length];
- _frontLength = 0;
- static if (isBidirectionalRange!Range)
- _backLength = 0;
- return;
+ if (_frontLength == _input.length)
+ {
+ // done, there's no separator in sight
+ _input = _input[_frontLength .. _frontLength];
+ _frontLength = _frontLength.max;
+ return;
+ }
+ if (_frontLength + separatorLength == _input.length)
+ {
+ // Special case: popping the first-to-last item; there is
+ // an empty item right after this.
+ _input = _input[_input.length .. _input.length];
+ _frontLength = 0;
+ return;
+ }
+ // Normal case, pop one item and the separator, get ready for
+ // reading the next item
+ _input = _input[_frontLength + separatorLength .. _input.length];
}
- // Normal case, pop one item and the separator, get ready for
- // reading the next item
- _input = _input[_frontLength + separatorLength .. _input.length];
// mark _frontLength as uninitialized
_frontLength = _frontLength.max;
}
@@ -4086,21 +5892,6 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
return Result(r, s);
}
-///
-@safe unittest
-{
- import std.algorithm.comparison : equal;
-
- assert(equal(splitter("hello world", " "), [ "hello", "world" ]));
- int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
- int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
- assert(equal(splitter(a, [0, 0]), w));
- a = [ 0, 0 ];
- assert(equal(splitter(a, [0, 0]), [ (int[]).init, (int[]).init ]));
- a = [ 0, 0, 1 ];
- assert(equal(splitter(a, [0, 0]), [ [], [1] ]));
-}
-
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -4135,15 +5926,6 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
assert(e == w[i++]);
}
assert(i == w.length);
- // // Now go back
- // auto s2 = splitter(a, 0);
-
- // foreach (e; retro(s2))
- // {
- // assert(i > 0);
- // assert(equal(e, w[--i]), text(e));
- // }
- // assert(i == 0);
wstring names = ",peter,paul,jerry,";
auto words = split(names, ",");
@@ -4172,11 +5954,11 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
assert(equal(sp6, ["", ""][]));
}
+// https://issues.dlang.org/show_bug.cgi?id=10773
@safe unittest
{
import std.algorithm.comparison : equal;
- // Issue 10773
auto s = splitter("abc", "");
assert(s.equal(["a", "b", "c"]));
}
@@ -4193,7 +5975,7 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
@property empty() { return _impl.empty; }
@property auto front() { return _impl.front; }
void popFront() { _impl = _impl[1..$]; }
- @property RefSep save() { return new RefSep(_impl); }
+ @property RefSep save() scope { return new RefSep(_impl); }
@property auto length() { return _impl.length; }
}
auto sep = new RefSep("->");
@@ -4202,55 +5984,11 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
assert(words.equal([ "i", "am", "pointing" ]));
}
-/**
-
-Similar to the previous overload of $(D splitter), except this one does not use a separator.
-Instead, the predicate is an unary function on the input range's element type.
-The $(D isTerminator) predicate is passed to $(REF unaryFun, std,functional) and can
-either accept a string, or any callable that can be executed via $(D pred(element, s)).
-
-Two adjacent separators are considered to surround an empty element in
-the split range. Use $(D filter!(a => !a.empty)) on the result to compress
-empty elements.
-
-Params:
- isTerminator = The predicate for deciding where to split the range.
- input = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
- be split.
-
-Constraints:
- The predicate $(D isTerminator) needs to accept an element of $(D input).
-
-Returns:
- An input range of the subranges of elements between separators. If $(D input)
- is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
- the returned range will be likewise.
-
-See_Also: $(REF _splitter, std,regex) for a version that splits using a regular
-expression defined separator.
- */
-auto splitter(alias isTerminator, Range)(Range input)
-if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(input.front))))
+/// ditto
+auto splitter(alias isTerminator, Range)(Range r)
+if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(r.front))))
{
- return SplitterResult!(unaryFun!isTerminator, Range)(input);
-}
-
-///
-@safe unittest
-{
- import std.algorithm.comparison : equal;
- import std.range.primitives : front;
-
- assert(equal(splitter!(a => a == ' ')("hello world"), [ "hello", "", "world" ]));
- int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
- int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
- assert(equal(splitter!(a => a == 0)(a), w));
- a = [ 0 ];
- assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ]));
- a = [ 0, 1 ];
- assert(equal(splitter!(a => a == 0)(a), [ [], [1] ]));
- w = [ [0], [1], [2] ];
- assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));
+ return SplitterResult!(unaryFun!isTerminator, Range)(r);
}
private struct SplitterResult(alias isTerminator, Range)
@@ -4291,6 +6029,24 @@ private struct SplitterResult(alias isTerminator, Range)
_end = size_t.max;
}
+ static if (fullSlicing)
+ {
+ private this(Range input, size_t end)
+ {
+ _input = input;
+ _end = end;
+ }
+ }
+ else
+ {
+ private this(Range input, size_t end, Range next)
+ {
+ _input = input;
+ _end = end;
+ _next = next;
+ }
+ }
+
static if (isInfinite!Range)
{
enum bool empty = false; // Propagate infiniteness.
@@ -4355,11 +6111,10 @@ private struct SplitterResult(alias isTerminator, Range)
@property typeof(this) save()
{
- auto ret = this;
- ret._input = _input.save;
- static if (!fullSlicing)
- ret._next = _next.save;
- return ret;
+ static if (fullSlicing)
+ return SplitterResult(_input.save, _end);
+ else
+ return SplitterResult(_input.save, _end, _next.save);
}
}
@@ -4444,7 +6199,7 @@ private struct SplitterResult(alias isTerminator, Range)
import std.algorithm.comparison : equal;
import std.uni : isWhite;
- //@@@6791@@@
+ // https://issues.dlang.org/show_bug.cgi?id=6791
assert(equal(
splitter("là dove terminava quella valle"),
["là", "dove", "terminava", "quella", "valle"]
@@ -4456,54 +6211,126 @@ private struct SplitterResult(alias isTerminator, Range)
assert(equal(splitter!"a=='本'"("日本語"), ["日", "語"]));
}
+// https://issues.dlang.org/show_bug.cgi?id=18657
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : refRange;
+ string s = "foobar";
+ auto r = refRange(&s).splitter!(c => c == 'b');
+ assert(equal!equal(r.save, ["foo", "ar"]));
+ assert(equal!equal(r.save, ["foo", "ar"]));
+}
+
/++
-Lazily splits the string $(D s) into words, using whitespace as the delimiter.
+Lazily splits the character-based range `s` into words, using whitespace as the
+delimiter.
-This function is string specific and, contrary to
-$(D splitter!(std.uni.isWhite)), runs of whitespace will be merged together
+This function is character-range specific and, contrary to
+`splitter!(std.uni.isWhite)`, runs of whitespace will be merged together
(no empty tokens will be produced).
Params:
- s = The string to be split.
+ s = The character-based range to be split. Must be a string, or a
+ random-access range of character types.
Returns:
An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of slices of
- the original string split by whitespace.
+ the original range split by whitespace.
+/
-auto splitter(C)(C[] s)
-if (isSomeChar!C)
+auto splitter(Range)(Range s)
+if (isSomeString!Range ||
+ isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range &&
+ !isConvertibleToString!Range &&
+ isSomeChar!(ElementEncodingType!Range))
{
import std.algorithm.searching : find;
static struct Result
{
private:
import core.exception : RangeError;
- C[] _s;
+ Range _s;
size_t _frontLength;
- void getFirst() pure @safe
+ void getFirst()
{
import std.uni : isWhite;
+ import std.traits : Unqual;
+
+ static if (is(immutable ElementEncodingType!Range == immutable wchar) &&
+ is(immutable ElementType!Range == immutable dchar))
+ {
+ // all unicode whitespace characters fit into a wchar. However,
+ // this range is a wchar array, so we will treat it like a
+ // wchar array instead of decoding each code point.
+ _frontLength = _s.length; // default condition, no spaces
+ foreach (i; 0 .. _s.length)
+ if (isWhite(_s[i]))
+ {
+ _frontLength = i;
+ break;
+ }
+ }
+ else static if (is(immutable ElementType!Range == immutable dchar) ||
+ is(immutable ElementType!Range == immutable wchar))
+ {
+ // dchar or wchar range, we can just use find.
+ auto r = find!(isWhite)(_s.save);
+ _frontLength = _s.length - r.length;
+ }
+ else
+ {
+ // need to decode the characters until we find a space. This is
+ // ported from std.string.stripLeft.
+ static import std.ascii;
+ static import std.uni;
+ import std.utf : decodeFront;
+
+ auto input = _s.save;
+ size_t iLength = input.length;
+
+ while (!input.empty)
+ {
+ auto c = input.front;
+ if (std.ascii.isASCII(c))
+ {
+ if (std.ascii.isWhite(c))
+ break;
+ input.popFront();
+ --iLength;
+ }
+ else
+ {
+ auto dc = decodeFront(input);
+ if (std.uni.isWhite(dc))
+ break;
+ iLength = input.length;
+ }
+ }
+
+ // sanity check
+ assert(iLength <= _s.length, "The current index must not"
+ ~ " exceed the length of the input");
- auto r = find!(isWhite)(_s);
- _frontLength = _s.length - r.length;
+ _frontLength = _s.length - iLength;
+ }
}
public:
- this(C[] s) pure @safe
+ this(Range s)
{
- import std.string : strip;
- _s = s.strip();
+ import std.string : stripLeft;
+ _s = s.stripLeft();
getFirst();
}
- @property C[] front() pure @safe
+ @property auto front()
{
version (assert) if (empty) throw new RangeError();
return _s[0 .. _frontLength];
}
- void popFront() pure @safe
+ void popFront()
{
import std.string : stripLeft;
version (assert) if (empty) throw new RangeError();
@@ -4511,7 +6338,7 @@ if (isSomeChar!C)
getFirst();
}
- @property bool empty() const @safe pure nothrow
+ @property bool empty() const
{
return _s.empty;
}
@@ -4536,14 +6363,14 @@ if (isSomeChar!C)
{
import std.algorithm.comparison : equal;
import std.meta : AliasSeq;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
import std.conv : to;
- S a = " a bcd ef gh ";
+ S a = " a \u2028 bcd ef gh ";
assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")]));
a = "";
assert(splitter(a).empty);
- }
+ }}
immutable string s = " a bcd ef gh ";
assert(equal(splitter(s), ["a", "bcd", "ef", "gh"][]));
@@ -4575,6 +6402,56 @@ if (isSomeChar!C)
assert(dictionary["two"]== 2);
assert(dictionary["yah"]== 3);
assert(dictionary["last"]== 4);
+
+}
+
+@safe unittest
+{
+ // do it with byCodeUnit
+ import std.conv : to;
+ import std.string : strip;
+ import std.utf : byCodeUnit;
+
+ alias BCU = typeof("abc".byCodeUnit());
+
+ // TDPL example, page 8
+ uint[BCU] dictionary;
+ BCU[3] lines;
+ lines[0] = "line one".byCodeUnit;
+ lines[1] = "line \ttwo".byCodeUnit;
+ lines[2] = "yah last line\ryah".byCodeUnit;
+ foreach (line; lines)
+ {
+ foreach (word; splitter(strip(line)))
+ {
+ static assert(is(typeof(word) == BCU));
+ if (word in dictionary) continue; // Nothing to do
+ auto newID = dictionary.length;
+ dictionary[word] = cast(uint) newID;
+ }
+ }
+ assert(dictionary.length == 5);
+ assert(dictionary["line".byCodeUnit]== 0);
+ assert(dictionary["one".byCodeUnit]== 1);
+ assert(dictionary["two".byCodeUnit]== 2);
+ assert(dictionary["yah".byCodeUnit]== 3);
+ assert(dictionary["last".byCodeUnit]== 4);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19238
+@safe pure unittest
+{
+ import std.utf : byCodeUnit;
+ import std.algorithm.comparison : equal;
+ auto range = "hello world".byCodeUnit.splitter;
+ static assert(is(typeof(range.front()) == typeof("hello".byCodeUnit())));
+ assert(range.equal(["hello".byCodeUnit, "world".byCodeUnit]));
+
+ // test other space types, including unicode
+ auto u = " a\t\v\r bcd\u3000 \u2028\t\nef\U00010001 gh";
+ assert(equal(splitter(u), ["a", "bcd", "ef\U00010001", "gh"][]));
+ assert(equal(splitter(u.byCodeUnit), ["a".byCodeUnit, "bcd".byCodeUnit,
+ "ef\U00010001".byCodeUnit, "gh".byCodeUnit][]));
}
@safe unittest
@@ -4649,45 +6526,702 @@ if (isSomeChar!C)
}
}
+// In same combinations substitute needs to calculate the auto-decoded length
+// of its needles
+private template hasDifferentAutodecoding(Range, Needles...)
+{
+ import std.meta : anySatisfy;
+ /* iff
+ - the needles needs auto-decoding, but the incoming range doesn't (or vice versa)
+ - both (range, needle) need auto-decoding and don't share the same common type
+ */
+ enum needlesAreNarrow = anySatisfy!(isNarrowString, Needles);
+ enum sourceIsNarrow = isNarrowString!Range;
+ enum hasDifferentAutodecoding = sourceIsNarrow != needlesAreNarrow ||
+ (sourceIsNarrow && needlesAreNarrow &&
+ is(CommonType!(Range, Needles) == void));
+}
+
+@safe nothrow @nogc pure unittest
+{
+ import std.meta : AliasSeq; // used for better clarity
+
+ static assert(!hasDifferentAutodecoding!(string, AliasSeq!(string, string)));
+ static assert(!hasDifferentAutodecoding!(wstring, AliasSeq!(wstring, wstring)));
+ static assert(!hasDifferentAutodecoding!(dstring, AliasSeq!(dstring, dstring)));
+
+ // the needles needs auto-decoding, but the incoming range doesn't (or vice versa)
+ static assert(hasDifferentAutodecoding!(string, AliasSeq!(wstring, wstring)));
+ static assert(hasDifferentAutodecoding!(string, AliasSeq!(dstring, dstring)));
+ static assert(hasDifferentAutodecoding!(wstring, AliasSeq!(string, string)));
+ static assert(hasDifferentAutodecoding!(wstring, AliasSeq!(dstring, dstring)));
+ static assert(hasDifferentAutodecoding!(dstring, AliasSeq!(string, string)));
+ static assert(hasDifferentAutodecoding!(dstring, AliasSeq!(wstring, wstring)));
+
+ // both (range, needle) need auto-decoding and don't share the same common type
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
+ static assert(hasDifferentAutodecoding!(T, AliasSeq!(wstring, string)));
+ static assert(hasDifferentAutodecoding!(T, AliasSeq!(dstring, string)));
+ static assert(hasDifferentAutodecoding!(T, AliasSeq!(wstring, dstring)));
+ }
+}
+
+// substitute
+/**
+Returns a range with all occurrences of `substs` in `r`.
+replaced with their substitution.
+
+Single value replacements (`'ö'.substitute!('ä', 'a', 'ö', 'o', 'ü', 'u)`) are
+supported as well and in $(BIGOH 1).
+
+Params:
+ r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ value = a single value which can be substituted in $(BIGOH 1)
+ substs = a set of replacements/substitutions
+ pred = the equality function to test if element(s) are equal to
+ a substitution
+
+Returns: a range with the substitutions replaced.
+
+See_Also:
+$(REF replace, std, array) for an eager replace algorithm or
+$(REF translate, std, string), and $(REF tr, std, string)
+for string algorithms with translation tables.
+*/
+template substitute(substs...)
+if (substs.length >= 2 && isExpressions!substs)
+{
+ import std.range.primitives : ElementType;
+ import std.traits : CommonType;
+
+ static assert(!(substs.length & 1), "The number of substitution parameters must be even");
+
+ /**
+ Substitute single values with compile-time substitution mappings.
+ Complexity: $(BIGOH 1) due to D's `switch` guaranteeing $(BIGOH 1);
+ */
+ auto substitute(Value)(Value value)
+ if (isInputRange!Value || !is(CommonType!(Value, typeof(substs[0])) == void))
+ {
+ static if (isInputRange!Value)
+ {
+ static if (!is(CommonType!(ElementType!Value, typeof(substs[0])) == void))
+ {
+ // Substitute single range elements with compile-time substitution mappings
+ return value.map!(a => substitute(a));
+ }
+ else static if (isInputRange!Value &&
+ !is(CommonType!(ElementType!Value, ElementType!(typeof(substs[0]))) == void))
+ {
+ // not implemented yet, fallback to runtime variant for now
+ return .substitute(value, substs);
+ }
+ else
+ {
+ static assert(0, `Compile-time substitutions must be elements or ranges of the same type of ` ~
+ Value.stringof ~ `.`);
+ }
+ }
+ // Substitute single values with compile-time substitution mappings.
+ else // static if (!is(CommonType!(Value, typeof(substs[0])) == void))
+ {
+ switch (value)
+ {
+ static foreach (i; 0 .. substs.length / 2)
+ case substs[2 * i]:
+ return substs[2 * i + 1];
+
+ default: return value;
+ }
+ }
+ }
+}
+
+/// ditto
+auto substitute(alias pred = (a, b) => a == b, R, Substs...)(R r, Substs substs)
+if (isInputRange!R && Substs.length >= 2 && !is(CommonType!(Substs) == void))
+{
+ import std.range.primitives : ElementType;
+ import std.meta : allSatisfy;
+ import std.traits : CommonType;
+
+ static assert(!(Substs.length & 1), "The number of substitution parameters must be even");
+
+ enum n = Substs.length / 2;
+
+ // Substitute individual elements
+ static if (!is(CommonType!(ElementType!R, Substs) == void))
+ {
+ import std.functional : binaryFun;
+
+ // Imitate a value closure to be @nogc
+ static struct ReplaceElement
+ {
+ private Substs substs;
+
+ this(Substs substs)
+ {
+ this.substs = substs;
+ }
+
+ auto opCall(E)(E e)
+ {
+ static foreach (i; 0 .. n)
+ if (binaryFun!pred(e, substs[2 * i]))
+ return substs[2 * i + 1];
+
+ return e;
+ }
+ }
+ auto er = ReplaceElement(substs);
+ return r.map!er;
+ }
+ // Substitute subranges
+ else static if (!is(CommonType!(ElementType!R, ElementType!(Substs[0])) == void) &&
+ allSatisfy!(isForwardRange, Substs))
+ {
+ import std.range : choose, take;
+ import std.meta : Stride;
+
+ auto replaceElement(E)(E e)
+ {
+ alias ReturnA = typeof(e[0]);
+ alias ReturnB = typeof(substs[0 .. 1].take(1));
+
+ // 1-based index
+ const auto hitNr = e[1];
+ switch (hitNr)
+ {
+ // no hit
+ case 0:
+ // use choose trick for non-common range
+ static if (is(CommonType!(ReturnA, ReturnB) == void))
+ return choose(1, e[0], ReturnB.init);
+ else
+ return e[0];
+
+ // all replacements
+ static foreach (i; 0 .. n)
+ case i + 1:
+ // use choose trick for non-common ranges
+ static if (is(CommonType!(ReturnA, ReturnB) == void))
+ return choose(0, e[0], substs[2 * i + 1].take(size_t.max));
+ else
+ return substs[2 * i + 1].take(size_t.max);
+ default:
+ assert(0, "hitNr should always be found.");
+ }
+ }
+
+ alias Ins = Stride!(2, Substs);
+
+ static struct SubstituteSplitter
+ {
+ import std.range : drop;
+ import std.typecons : Tuple;
+
+ private
+ {
+ typeof(R.init.drop(0)) rest;
+ Ins needles;
+
+ typeof(R.init.take(0)) skip; // skip before next hit
+ alias Hit = size_t; // 0 iff no hit, otherwise hit in needles[index-1]
+ alias E = Tuple!(typeof(skip), Hit);
+ Hit hitNr; // hit number: 0 means no hit, otherwise index+1 to needles that matched
+ bool hasHit; // is there a replacement hit which should be printed?
+
+ enum hasDifferentAutodecoding = .hasDifferentAutodecoding!(typeof(rest), Ins);
+
+ // calculating the needle length for narrow strings might be expensive -> cache it
+ static if (hasDifferentAutodecoding)
+ ptrdiff_t[n] needleLengths = -1;
+ }
+
+ this(R haystack, Ins needles)
+ {
+ this.rest = haystack.drop(0);
+ this.needles = needles;
+ if (!haystack.empty)
+ {
+ hasHit = true;
+ popFront;
+ }
+ static if (hasNested!(typeof(skip)))
+ skip = rest.take(0);
+ }
+
+ /* If `skip` is non-empty, it's returned as (skip, 0) tuple
+ otherwise a similar (<empty>, hitNr) tuple is returned.
+ `replaceElement` maps based on the second item (`hitNr`).
+ */
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty substitute.");
+ return !skip.empty ? E(skip, 0) : E(typeof(skip).init, hitNr);
+ }
+
+ static if (isInfinite!R)
+ enum empty = false; // propagate infiniteness
+ else
+ @property bool empty()
+ {
+ return skip.empty && !hasHit;
+ }
+
+ /* If currently in a skipping phase => reset.
+ Otherwise try to find the next occurrence of `needles`
+ If valid match
+ - if there are elements before the match, set skip with these elements
+ (on the next popFront, the range will be in the skip state once)
+ - `rest`: advance to the end of the match
+ - set hasHit
+ Otherwise skip to the end
+ */
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty substitute.");
+ if (!skip.empty)
+ {
+ skip = typeof(skip).init; // jump over skip
+ }
+ else
+ {
+ import std.algorithm.searching : countUntil, find;
+
+ auto match = rest.find!pred(needles);
+
+ static if (needles.length >= 2) // variadic version of find (returns a tuple)
+ {
+ // find with variadic needles returns a (range, needleNr) tuple
+ // needleNr is a 1-based index
+ auto hitValue = match[0];
+ hitNr = match[1];
+ }
+ else
+ {
+ // find with one needle returns the range
+ auto hitValue = match;
+ hitNr = match.empty ? 0 : 1;
+ }
+
+ if (hitNr == 0) // no more hits
+ {
+ skip = rest.take(size_t.max);
+ hasHit = false;
+ rest = typeof(rest).init;
+ }
+ else
+ {
+ auto hitLength = size_t.max;
+ switchL: switch (hitNr - 1)
+ {
+ static foreach (i; 0 .. n)
+ {
+ case i:
+ static if (hasDifferentAutodecoding)
+ {
+ import std.utf : codeLength;
+
+ // cache calculated needle length
+ if (needleLengths[i] != -1)
+ hitLength = needleLengths[i];
+ else
+ hitLength = needleLengths[i] = codeLength!dchar(needles[i]);
+ }
+ else
+ {
+ hitLength = needles[i].length;
+ }
+ break switchL;
+ }
+ default:
+ assert(0, "hitNr should always be found");
+ }
+
+ const pos = rest.countUntil(hitValue);
+ if (pos > 0) // match not at start of rest
+ skip = rest.take(pos);
+
+ hasHit = true;
+
+ // iff the source range and the substitutions are narrow strings,
+ // we can avoid calling the auto-decoding `popFront` (via drop)
+ static if (isNarrowString!(typeof(hitValue)) && !hasDifferentAutodecoding)
+ rest = hitValue[hitLength .. $];
+ else
+ rest = hitValue.drop(hitLength);
+ }
+ }
+ }
+ }
+
+ // extract inputs
+ Ins ins;
+ static foreach (i; 0 .. n)
+ ins[i] = substs[2 * i];
+
+ return SubstituteSplitter(r, ins)
+ .map!(a => replaceElement(a))
+ .joiner;
+ }
+ else
+ {
+ static assert(0, "The substitutions must either substitute a single element or a save-able subrange.");
+ }
+}
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // substitute single elements
+ assert("do_it".substitute('_', ' ').equal("do it"));
+
+ // substitute multiple, single elements
+ assert("do_it".substitute('_', ' ',
+ 'd', 'g',
+ 'i', 't',
+ 't', 'o')
+ .equal("go to"));
+
+ // substitute subranges
+ assert("do_it".substitute("_", " ",
+ "do", "done")
+ .equal("done it"));
+
+ // substitution works for any ElementType
+ int[] x = [1, 2, 3];
+ auto y = x.substitute(1, 0.1);
+ assert(y.equal([0.1, 2, 3]));
+ static assert(is(typeof(y.front) == double));
+
+ import std.range : retro;
+ assert([1, 2, 3].substitute(1, 0.1).retro.equal([3, 2, 0.1]));
+}
+
+/// Use the faster compile-time overload
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // substitute subranges of a range
+ assert("apple_tree".substitute!("apple", "banana",
+ "tree", "shrub").equal("banana_shrub"));
+
+ // substitute subranges of a range
+ assert("apple_tree".substitute!('a', 'b',
+ 't', 'f').equal("bpple_free"));
+
+ // substitute values
+ assert('a'.substitute!('a', 'b', 't', 'f') == 'b');
+}
+
+/// Multiple substitutes
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : ElementType;
+
+ int[3] x = [1, 2, 3];
+ auto y = x[].substitute(1, 0.1)
+ .substitute(0.1, 0.2);
+ static assert(is(typeof(y.front) == double));
+ assert(y.equal([0.2, 2, 3]));
+
+ auto z = "42".substitute('2', '3')
+ .substitute('3', '1');
+ static assert(is(ElementType!(typeof(z)) == dchar));
+ assert(equal(z, "41"));
+}
+
+// Test the first example with compile-time overloads
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // substitute single elements
+ assert("do_it".substitute!('_', ' ').equal("do it"));
+
+ // substitute multiple, single elements
+ assert("do_it".substitute!('_', ' ',
+ 'd', 'g',
+ 'i', 't',
+ 't', 'o')
+ .equal(`go to`));
+
+ // substitute subranges
+ assert("do_it".substitute!("_", " ",
+ "do", "done")
+ .equal("done it"));
+
+ // substitution works for any ElementType
+ int[3] x = [1, 2, 3];
+ auto y = x[].substitute!(1, 0.1);
+ assert(y.equal([0.1, 2, 3]));
+ static assert(is(typeof(y.front) == double));
+
+ import std.range : retro;
+ assert([1, 2, 3].substitute!(1, 0.1).retro.equal([3, 2, 0.1]));
+}
+
+// test infinite ranges
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : cycle, take;
+
+ int[] x = [1, 2, 3];
+ assert(x.cycle.substitute!(1, 0.1).take(4).equal([0.1, 2, 3, 0.1]));
+ assert(x.cycle.substitute(1, 0.1).take(4).equal([0.1, 2, 3, 0.1]));
+}
+
+// test infinite ranges
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : AllDummyRanges;
+
+ foreach (R; AllDummyRanges)
+ {
+ assert(R.init
+ .substitute!(2, 22, 3, 33, 5, 55, 9, 99)
+ .equal([1, 22, 33, 4, 55, 6, 7, 8, 99, 10]));
+
+ assert(R.init
+ .substitute(2, 22, 3, 33, 5, 55, 9, 99)
+ .equal([1, 22, 33, 4, 55, 6, 7, 8, 99, 10]));
+ }
+}
+
+// test multiple replacements
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert("alpha.beta.gamma"
+ .substitute("alpha", "1",
+ "gamma", "3",
+ "beta", "2").equal("1.2.3"));
+
+ assert("alpha.beta.gamma."
+ .substitute("alpha", "1",
+ "gamma", "3",
+ "beta", "2").equal("1.2.3."));
+
+ assert("beta.beta.beta"
+ .substitute("alpha", "1",
+ "gamma", "3",
+ "beta", "2").equal("2.2.2"));
+
+ assert("alpha.alpha.alpha"
+ .substitute("alpha", "1",
+ "gamma", "3",
+ "beta", "2").equal("1.1.1"));
+}
+
+// test combination of subrange + element replacement
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(("abcDe".substitute("a", "AA",
+ "b", "DD")
+ .substitute('A', 'y',
+ 'D', 'x',
+ 'e', '1'))
+ .equal("yyxxcx1"));
+}
+
+// test const + immutable storage groups
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto xyz_abc(T)(T value)
+ {
+ immutable a = "a";
+ const b = "b";
+ auto c = "c";
+ return value.substitute!("x", a,
+ "y", b,
+ "z", c);
+ }
+ assert(xyz_abc("_x").equal("_a"));
+ assert(xyz_abc(".y.").equal(".b."));
+ assert(xyz_abc("z").equal("c"));
+ assert(xyz_abc("w").equal("w"));
+}
+
+// test with narrow strings (auto-decoding) and subranges
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert("äöü€".substitute("ä", "b", "ü", "u").equal("böu€"));
+ assert("äöü€".substitute!("ä", "b", "ü", "u").equal("böu€"));
+ assert("ä...öü€".substitute("ä", "b", "ü", "u").equal("b...öu€"));
+
+ auto expected = "emoticons😄😅.😇😈Rock";
+ assert("emoticons😄😅😆😇😈rock"
+ .substitute("r", "R", "😆", ".").equal(expected));
+ assert("emoticons😄😅😆😇😈rock"
+ .substitute!("r", "R", "😆", ".").equal(expected));
+}
+
+// test with narrow strings (auto-decoding) and single elements
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert("äöü€".substitute('ä', 'b', 'ü', 'u').equal("böu€"));
+ assert("äöü€".substitute!('ä', 'b', 'ü', 'u').equal("böu€"));
+
+ auto expected = "emoticons😄😅.😇😈Rock";
+ assert("emoticons😄😅😆😇😈rock"
+ .substitute('r', 'R', '😆', '.').equal(expected));
+ assert("emoticons😄😅😆😇😈rock"
+ .substitute!('r', 'R', '😆', '.').equal(expected));
+}
+
+// test auto-decoding {n,w,d} strings X {n,w,d} strings
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert("ääöü€".substitute("ä", "b", "ü", "u").equal("bböu€"));
+ assert("ääöü€".substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€"));
+ assert("ääöü€".substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€"));
+
+ assert("ääöü€"w.substitute("ä", "b", "ü", "u").equal("bböu€"));
+ assert("ääöü€"w.substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€"));
+ assert("ääöü€"w.substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€"));
+
+ assert("ääöü€"d.substitute("ä", "b", "ü", "u").equal("bböu€"));
+ assert("ääöü€"d.substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€"));
+ assert("ääöü€"d.substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€"));
+
+ // auto-decoding is done before by a different range
+ assert("ääöü€".filter!(a => true).substitute("ä", "b", "ü", "u").equal("bböu€"));
+ assert("ääöü€".filter!(a => true).substitute("ä"w, "b"w, "ü"w, "u"w).equal("bböu€"));
+ assert("ääöü€".filter!(a => true).substitute("ä"d, "b"d, "ü"d, "u"d).equal("bböu€"));
+}
+
+// test repeated replacement
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert([1, 2, 3, 1, 1, 2].substitute(1, 0).equal([0, 2, 3, 0, 0, 2]));
+ assert([1, 2, 3, 1, 1, 2].substitute!(1, 0).equal([0, 2, 3, 0, 0, 2]));
+ assert([1, 2, 3, 1, 1, 2].substitute(1, 2, 2, 9).equal([2, 9, 3, 2, 2, 9]));
+}
+
+// test @nogc for single element replacements
+@safe @nogc unittest
+{
+ import std.algorithm.comparison : equal;
+
+ static immutable arr = [1, 2, 3, 1, 1, 2];
+ static immutable expected = [0, 2, 3, 0, 0, 2];
+
+ assert(arr.substitute!(1, 0).equal(expected));
+ assert(arr.substitute(1, 0).equal(expected));
+}
+
+// test different range types
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : AllDummyRanges;
+
+ static foreach (DummyType; AllDummyRanges)
+ {{
+ DummyType dummyRange;
+
+ // single substitution
+ dummyRange.substitute (2, 22).equal([1, 22, 3, 4, 5, 6, 7, 8, 9, 10]);
+ dummyRange.substitute!(2, 22).equal([1, 22, 3, 4, 5, 6, 7, 8, 9, 10]);
+
+ // multiple substitution
+ dummyRange.substitute (2, 22, 5, 55, 7, 77).equal([1, 22, 3, 4, 55, 6, 77, 8, 9, 10]);
+ dummyRange.substitute!(2, 22, 5, 55, 7, 77).equal([1, 22, 3, 4, 55, 6, 77, 8, 9, 10]);
+ }}
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19207
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ assert([1, 2, 3, 4].substitute([1], [7]).equal([7, 2, 3, 4]));
+ assert([1, 2, 3, 4].substitute([2], [7]).equal([1, 7, 3, 4]));
+ assert([1, 2, 3, 4].substitute([4], [7]).equal([1, 2, 3, 7]));
+ assert([1, 2, 3, 4].substitute([2, 3], [7]).equal([1, 7, 4]));
+ assert([1, 2, 3, 4].substitute([3, 4], [7, 8]).equal([1, 2, 7, 8]));
+}
+
+// tests recognizing empty base ranges
+nothrow pure @safe unittest
+{
+ import std.utf : byCodeUnit;
+ import std.algorithm.comparison : equal;
+
+ assert("".byCodeUnit.substitute('4', 'A').empty);
+ assert("".byCodeUnit.substitute('0', 'O', '5', 'S', '1', 'l').empty);
+ assert("".byCodeUnit.substitute("PKM".byCodeUnit, "PoKeMon".byCodeUnit).empty);
+ assert("".byCodeUnit.substitute
+ ( "ding".byCodeUnit,
+ "dong".byCodeUnit,
+ "click".byCodeUnit,
+ "clack".byCodeUnit,
+ "ping".byCodeUnit,
+ "latency".byCodeUnit
+ ).empty);
+}
+
// sum
/**
-Sums elements of $(D r), which must be a finite
+Sums elements of `r`, which must be a finite
$(REF_ALTTEXT input range, isInputRange, std,range,primitives). Although
-conceptually $(D sum(r)) is equivalent to $(LREF fold)!((a, b) => a +
-b)(r, 0), $(D sum) uses specialized algorithms to maximize accuracy,
+conceptually `sum(r)` is equivalent to $(LREF fold)!((a, b) => a +
+b)(r, 0), `sum` uses specialized algorithms to maximize accuracy,
as follows.
$(UL
-$(LI If $(D $(REF ElementType, std,range,primitives)!R) is a floating-point
-type and $(D R) is a
+$(LI If $(REF ElementType, std,range,primitives)!R is a floating-point
+type and `R` is a
$(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) with
-length and slicing, then $(D sum) uses the
+length and slicing, then `sum` uses the
$(HTTP en.wikipedia.org/wiki/Pairwise_summation, pairwise summation)
algorithm.)
-$(LI If $(D ElementType!R) is a floating-point type and $(D R) is a
+$(LI If `ElementType!R` is a floating-point type and `R` is a
finite input range (but not a random-access range with slicing), then
-$(D sum) uses the $(HTTP en.wikipedia.org/wiki/Kahan_summation,
+`sum` uses the $(HTTP en.wikipedia.org/wiki/Kahan_summation,
Kahan summation) algorithm.)
$(LI In all other cases, a simple element by element addition is done.)
)
For floating point inputs, calculations are made in
-$(DDLINK spec/type, Types, $(D real))
-precision for $(D real) inputs and in $(D double) precision otherwise
-(Note this is a special case that deviates from $(D fold)'s behavior,
-which would have kept $(D float) precision for a $(D float) range).
+$(DDLINK spec/type, Types, `real`)
+precision for `real` inputs and in `double` precision otherwise
+(Note this is a special case that deviates from `fold`'s behavior,
+which would have kept `float` precision for a `float` range).
For all other types, the calculations are done in the same type obtained
from from adding two elements of the range, which may be a different
type from the elements themselves (for example, in case of
$(DDSUBLINK spec/type,integer-promotions, integral promotion)).
-A seed may be passed to $(D sum). Not only will this seed be used as an initial
+A seed may be passed to `sum`. Not only will this seed be used as an initial
value, but its type will override all the above, and determine the algorithm
-and precision used for summation.
+and precision used for summation. If a seed is not passed, one is created with
+the value of `typeof(r.front + r.front)(0)`, or `typeof(r.front + r.front).zero`
+if no constructor exists that takes an int.
Note that these specialized summing algorithms execute more primitive operations
than vanilla summation. Therefore, if in certain cases maximum speed is required
-at expense of precision, one can use $(D fold!((a, b) => a + b)(r, 0)), which
+at expense of precision, one can use `fold!((a, b) => a + b)(r, 0)`, which
is not specialized for summation.
Params:
@@ -4705,7 +7239,15 @@ if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front)))
alias Seed = typeof(E.init + 0.0); //biggest of double/real
else
alias Seed = typeof(r.front + r.front);
- return sum(r, Unqual!Seed(0));
+ static if (is(typeof(Unqual!Seed(0))))
+ enum seedValue = Unqual!Seed(0);
+ else static if (is(typeof({ Unqual!Seed tmp = Seed.zero; })))
+ enum Unqual!Seed seedValue = Seed.zero;
+ else
+ static assert(false,
+ "Could not initiate an initial value for " ~ (Unqual!Seed).stringof
+ ~ ". Please supply an initial value manually.");
+ return sum(r, seedValue);
}
/// ditto
auto sum(R, E)(R r, E seed)
@@ -4727,6 +7269,36 @@ if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front)))
}
}
+/// Ditto
+@safe pure nothrow unittest
+{
+ import std.range;
+
+ //simple integral sumation
+ assert(sum([ 1, 2, 3, 4]) == 10);
+
+ //with integral promotion
+ assert(sum([false, true, true, false, true]) == 3);
+ assert(sum(ubyte.max.repeat(100)) == 25500);
+
+ //The result may overflow
+ assert(uint.max.repeat(3).sum() == 4294967293U );
+ //But a seed can be used to change the sumation primitive
+ assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL);
+
+ //Floating point sumation
+ assert(sum([1.0, 2.0, 3.0, 4.0]) == 10);
+
+ //Floating point operations have double precision minimum
+ static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double));
+ assert(sum([1F, 2, 3, 4]) == 10);
+
+ //Force pair-wise floating point sumation on large integers
+ import std.math.operations : isClose;
+ assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0)
+ .isClose((ulong.max / 2) * 4096.0 + 4096^^2 / 2));
+}
+
// Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation
private auto sumPairwise(F, R)(R data)
if (isInputRange!R && !isInfinite!R)
@@ -4815,8 +7387,8 @@ if (isForwardRange!R && !isRandomAccessRange!R)
private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r)
if (isForwardRange!R && !isRandomAccessRange!R)
{
- import std.math : isPowerOf2;
- static assert(isPowerOf2(N));
+ import std.math.traits : isPowerOf2;
+ static assert(isPowerOf2(N), "N must be a power of 2");
static if (N == 2) return sumPair!(needEmptyChecks, F)(r);
else return sumPairwiseN!(N/2, needEmptyChecks, F)(r)
+ sumPairwiseN!(N/2, needEmptyChecks, F)(r);
@@ -4825,7 +7397,9 @@ if (isForwardRange!R && !isRandomAccessRange!R)
// Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm
private auto sumKahan(Result, R)(Result result, R r)
{
- static assert(isFloatingPoint!Result && isMutable!Result);
+ static assert(isFloatingPoint!Result && isMutable!Result, "The type of"
+ ~ " Result must be a mutable floating point, not "
+ ~ Result.stringof);
Result c = 0;
for (; !r.empty; r.popFront())
{
@@ -4837,36 +7411,6 @@ private auto sumKahan(Result, R)(Result result, R r)
return result;
}
-/// Ditto
-@safe pure nothrow unittest
-{
- import std.range;
-
- //simple integral sumation
- assert(sum([ 1, 2, 3, 4]) == 10);
-
- //with integral promotion
- assert(sum([false, true, true, false, true]) == 3);
- assert(sum(ubyte.max.repeat(100)) == 25500);
-
- //The result may overflow
- assert(uint.max.repeat(3).sum() == 4294967293U );
- //But a seed can be used to change the sumation primitive
- assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL);
-
- //Floating point sumation
- assert(sum([1.0, 2.0, 3.0, 4.0]) == 10);
-
- //Floating point operations have double precision minimum
- static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double));
- assert(sum([1F, 2, 3, 4]) == 10);
-
- //Force pair-wise floating point sumation on large integers
- import std.math : approxEqual;
- assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0)
- .approxEqual((ulong.max / 2) * 4096.0 + 4096^^2 / 2));
-}
-
@safe pure nothrow unittest
{
static assert(is(typeof(sum([cast( byte) 1])) == int));
@@ -4914,7 +7458,8 @@ private auto sumKahan(Result, R)(Result result, R r)
assert(sum(SList!double(1, 2, 3, 4)[]) == 10);
}
-@safe pure nothrow unittest // 12434
+// https://issues.dlang.org/show_bug.cgi?id=12434
+@safe pure nothrow unittest
{
immutable a = [10, 20];
auto s1 = sum(a);
@@ -4943,15 +7488,154 @@ private auto sumKahan(Result, R)(Result result, R r)
assert(repeat(1.0, n).sum == n);
}
+// Issue 19525
+@safe unittest
+{
+ import std.datetime : Duration, minutes;
+ assert([1.minutes].sum() == 1.minutes);
+}
+
+/**
+Finds the mean (colloquially known as the average) of a range.
+
+For built-in numerical types, accurate Knuth & Welford mean calculation
+is used. For user-defined types, element by element summation is used.
+Additionally an extra parameter `seed` is needed in order to correctly
+seed the summation with the equivalent to `0`.
+
+The first overload of this function will return `T.init` if the range
+is empty. However, the second overload will return `seed` on empty ranges.
+
+This function is $(BIGOH r.length).
+
+Params:
+ T = The type of the return value.
+ r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ seed = For user defined types. Should be equivalent to `0`.
+
+Returns:
+ The mean of `r` when `r` is non-empty.
+*/
+T mean(T = double, R)(R r)
+if (isInputRange!R &&
+ isNumeric!(ElementType!R) &&
+ !isInfinite!R)
+{
+ if (r.empty)
+ return T.init;
+
+ Unqual!T meanRes = 0;
+ size_t i = 1;
+
+ // Knuth & Welford mean calculation
+ // division per element is slower, but more accurate
+ for (; !r.empty; r.popFront())
+ {
+ T delta = r.front - meanRes;
+ meanRes += delta / i++;
+ }
+
+ return meanRes;
+}
+
+/// ditto
+auto mean(R, T)(R r, T seed)
+if (isInputRange!R &&
+ !isNumeric!(ElementType!R) &&
+ is(typeof(r.front + seed)) &&
+ is(typeof(r.front / size_t(1))) &&
+ !isInfinite!R)
+{
+ import std.algorithm.iteration : sum, reduce;
+
+ // per item division vis-a-vis the previous overload is too
+ // inaccurate for integer division, which the user defined
+ // types might be representing
+ static if (hasLength!R)
+ {
+ if (r.length == 0)
+ return seed;
+
+ return sum(r, seed) / r.length;
+ }
+ else
+ {
+ import std.typecons : tuple;
+
+ if (r.empty)
+ return seed;
+
+ auto pair = reduce!((a, b) => tuple(a[0] + 1, a[1] + b))
+ (tuple(size_t(0), seed), r);
+ return pair[1] / pair[0];
+ }
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isNaN;
+
+ static immutable arr1 = [1, 2, 3];
+ static immutable arr2 = [1.5, 2.5, 12.5];
+
+ assert(arr1.mean.isClose(2));
+ assert(arr2.mean.isClose(5.5));
+
+ assert(arr1[0 .. 0].mean.isNaN);
+}
+
+@safe pure nothrow unittest
+{
+ import std.internal.test.dummyrange : ReferenceInputRange;
+ import std.math.operations : isClose;
+
+ auto r1 = new ReferenceInputRange!int([1, 2, 3]);
+ assert(r1.mean.isClose(2));
+
+ auto r2 = new ReferenceInputRange!double([1.5, 2.5, 12.5]);
+ assert(r2.mean.isClose(5.5));
+}
+
+// Test user defined types
+@system pure unittest
+{
+ import std.bigint : BigInt;
+ import std.internal.test.dummyrange : ReferenceInputRange;
+ import std.math.operations : isClose;
+
+ auto bigint_arr = [BigInt("1"), BigInt("2"), BigInt("3"), BigInt("6")];
+ auto bigint_arr2 = new ReferenceInputRange!BigInt([
+ BigInt("1"), BigInt("2"), BigInt("3"), BigInt("6")
+ ]);
+ assert(bigint_arr.mean(BigInt(0)) == BigInt("3"));
+ assert(bigint_arr2.mean(BigInt(0)) == BigInt("3"));
+
+ BigInt[] bigint_arr3 = [];
+ assert(bigint_arr3.mean(BigInt(0)) == BigInt(0));
+
+ struct MyFancyDouble
+ {
+ double v;
+ alias v this;
+ }
+
+ // both overloads
+ auto d_arr = [MyFancyDouble(10), MyFancyDouble(15), MyFancyDouble(30)];
+ assert(mean!(double)(cast(double[]) d_arr).isClose(18.33333333));
+ assert(mean(d_arr, MyFancyDouble(0)).isClose(18.33333333));
+}
+
// uniq
/**
Lazily iterates unique consecutive elements of the given range (functionality
akin to the $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system
utility). Equivalence of elements is assessed by using the predicate
-$(D pred), by default $(D "a == b"). The predicate is passed to
+`pred`, by default `"a == b"`. The predicate is passed to
$(REF binaryFun, std,functional), and can either accept a string, or any callable
-that can be executed via $(D pred(element, element)). If the given range is
-bidirectional, $(D uniq) also yields a
+that can be executed via `pred(element, element)`. If the given range is
+bidirectional, `uniq` also yields a
$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives).
Params:
@@ -4961,7 +7645,7 @@ Params:
Returns:
An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
- consecutively unique elements in the original range. If $(D r) is also a
+ consecutively unique elements in the original range. If `r` is also a
forward range or bidirectional range, the returned range will be likewise.
*/
auto uniq(alias pred = "a == b", Range)(Range r)
@@ -5085,7 +7769,8 @@ private struct UniqResult(alias pred, Range)
}
}
-@safe unittest // https://issues.dlang.org/show_bug.cgi?id=17264
+// https://issues.dlang.org/show_bug.cgi?id=17264
+@safe unittest
{
import std.algorithm.comparison : equal;
@@ -5094,12 +7779,16 @@ private struct UniqResult(alias pred, Range)
}
/**
-Lazily computes all _permutations of $(D r) using $(HTTP
+Lazily computes all _permutations of `r` using $(HTTP
en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm).
+Params:
+ Range = the range type
+ r = the $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives)
+ to find the permutations for.
Returns:
-A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
-the elements of which are an $(REF indexed, std,range) view into $(D r).
+ A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ of elements of which are an $(REF indexed, std,range) view into `r`.
See_Also:
$(REF nextPermutation, std,algorithm,sorting).
@@ -5130,13 +7819,13 @@ if (isRandomAccessRange!Range && hasLength!Range)
_empty = r.length == 0;
}
- ///
+ /// Returns: `true` if the range is empty, `false` otherwise.
@property bool empty() const pure nothrow @safe @nogc
{
return _empty;
}
- ///
+ /// Returns: the front of the range
@property auto front()
{
import std.range : indexed;
diff --git a/libphobos/src/std/algorithm/mutation.d b/libphobos/src/std/algorithm/mutation.d
index 2b708adb035..88191bbf3f7 100644
--- a/libphobos/src/std/algorithm/mutation.d
+++ b/libphobos/src/std/algorithm/mutation.d
@@ -1,59 +1,59 @@
// Written in the D programming language.
/**
This is a submodule of $(MREF std, algorithm).
-It contains generic _mutation algorithms.
+It contains generic mutation algorithms.
$(SCRIPT inhibitQuickIndex = 1;)
$(BOOKTABLE Cheat Sheet,
$(TR $(TH Function Name) $(TH Description))
$(T2 bringToFront,
- If $(D a = [1, 2, 3]) and $(D b = [4, 5, 6, 7]),
- $(D bringToFront(a, b)) leaves $(D a = [4, 5, 6]) and
- $(D b = [7, 1, 2, 3]).)
+ If `a = [1, 2, 3]` and `b = [4, 5, 6, 7]`,
+ `bringToFront(a, b)` leaves `a = [4, 5, 6]` and
+ `b = [7, 1, 2, 3]`.)
$(T2 copy,
Copies a range to another. If
- $(D a = [1, 2, 3]) and $(D b = new int[5]), then $(D copy(a, b))
- leaves $(D b = [1, 2, 3, 0, 0]) and returns $(D b[3 .. $]).)
+ `a = [1, 2, 3]` and `b = new int[5]`, then `copy(a, b)`
+ leaves `b = [1, 2, 3, 0, 0]` and returns `b[3 .. $]`.)
$(T2 fill,
Fills a range with a pattern,
- e.g., if $(D a = new int[3]), then $(D fill(a, 4))
- leaves $(D a = [4, 4, 4]) and $(D fill(a, [3, 4])) leaves
- $(D a = [3, 4, 3]).)
+ e.g., if `a = new int[3]`, then `fill(a, 4)`
+ leaves `a = [4, 4, 4]` and `fill(a, [3, 4])` leaves
+ `a = [3, 4, 3]`.)
$(T2 initializeAll,
- If $(D a = [1.2, 3.4]), then $(D initializeAll(a)) leaves
- $(D a = [double.init, double.init]).)
+ If `a = [1.2, 3.4]`, then `initializeAll(a)` leaves
+ `a = [double.init, double.init]`.)
$(T2 move,
- $(D move(a, b)) moves $(D a) into $(D b). $(D move(a)) reads $(D a)
+ `move(a, b)` moves `a` into `b`. `move(a)` reads `a`
destructively when necessary.)
$(T2 moveEmplace,
- Similar to $(D move) but assumes `target` is uninitialized.)
+ Similar to `move` but assumes `target` is uninitialized.)
$(T2 moveAll,
Moves all elements from one range to another.)
$(T2 moveEmplaceAll,
- Similar to $(D moveAll) but assumes all elements in `target` are uninitialized.)
+ Similar to `moveAll` but assumes all elements in `target` are uninitialized.)
$(T2 moveSome,
Moves as many elements as possible from one range to another.)
$(T2 moveEmplaceSome,
- Similar to $(D moveSome) but assumes all elements in `target` are uninitialized.)
+ Similar to `moveSome` but assumes all elements in `target` are uninitialized.)
$(T2 remove,
Removes elements from a range in-place, and returns the shortened
range.)
$(T2 reverse,
- If $(D a = [1, 2, 3]), $(D reverse(a)) changes it to $(D [3, 2, 1]).)
+ If `a = [1, 2, 3]`, `reverse(a)` changes it to `[3, 2, 1]`.)
$(T2 strip,
Strips all leading and trailing elements equal to a value, or that
satisfy a predicate.
- If $(D a = [1, 1, 0, 1, 1]), then $(D strip(a, 1)) and
- $(D strip!(e => e == 1)(a)) returns $(D [0]).)
+ If `a = [1, 1, 0, 1, 1]`, then `strip(a, 1)` and
+ `strip!(e => e == 1)(a)` returns `[0]`.)
$(T2 stripLeft,
Strips all leading elements equal to a value, or that satisfy a
- predicate. If $(D a = [1, 1, 0, 1, 1]), then $(D stripLeft(a, 1)) and
- $(D stripLeft!(e => e == 1)(a)) returns $(D [0, 1, 1]).)
+ predicate. If `a = [1, 1, 0, 1, 1]`, then `stripLeft(a, 1)` and
+ `stripLeft!(e => e == 1)(a)` returns `[0, 1, 1]`.)
$(T2 stripRight,
Strips all trailing elements equal to a value, or that satisfy a
predicate.
- If $(D a = [1, 1, 0, 1, 1]), then $(D stripRight(a, 1)) and
- $(D stripRight!(e => e == 1)(a)) returns $(D [1, 1, 0]).)
+ If `a = [1, 1, 0, 1, 1]`, then `stripRight(a, 1)` and
+ `stripRight!(e => e == 1)(a)` returns `[1, 1, 0]`.)
$(T2 swap,
Swaps two values.)
$(T2 swapAt,
@@ -70,7 +70,7 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.com, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/algorithm/_mutation.d)
+Source: $(PHOBOSSRC std/algorithm/mutation.d)
Macros:
T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
@@ -78,36 +78,36 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
module std.algorithm.mutation;
import std.range.primitives;
-import std.traits : isArray, isBlitAssignable, isNarrowString, Unqual, isSomeChar;
-// FIXME
-import std.typecons; // : tuple, Tuple;
+import std.traits : isArray, isAssignable, isBlitAssignable, isNarrowString,
+ Unqual, isSomeChar, isMutable;
+import std.meta : allSatisfy;
+import std.typecons : tuple, Tuple;
// bringToFront
/**
-The $(D bringToFront) function has considerable flexibility and
-usefulness. It can rotate elements in one buffer left or right, swap
-buffers of equal length, and even move elements across disjoint
-buffers of different types and different lengths.
-
-$(D bringToFront) takes two ranges $(D front) and $(D back), which may
-be of different types. Considering the concatenation of $(D front) and
-$(D back) one unified range, $(D bringToFront) rotates that unified
-range such that all elements in $(D back) are brought to the beginning
-of the unified range. The relative ordering of elements in $(D front)
-and $(D back), respectively, remains unchanged.
-
-The $(D bringToFront) function treats strings at the code unit
+`bringToFront` takes two ranges `front` and `back`, which may
+be of different types. Considering the concatenation of `front` and
+`back` one unified range, `bringToFront` rotates that unified
+range such that all elements in `back` are brought to the beginning
+of the unified range. The relative ordering of elements in `front`
+and `back`, respectively, remains unchanged.
+
+The `bringToFront` function treats strings at the code unit
level and it is not concerned with Unicode character integrity.
-$(D bringToFront) is designed as a function for moving elements
+`bringToFront` is designed as a function for moving elements
in ranges, not as a string function.
Performs $(BIGOH max(front.length, back.length)) evaluations of $(D
swap).
+The `bringToFront` function can rotate elements in one buffer left or right, swap
+buffers of equal length, and even move elements across disjoint
+buffers of different types and different lengths.
+
Preconditions:
-Either $(D front) and $(D back) are disjoint, or $(D back) is
-reachable from $(D front) and $(D front) is not reachable from $(D
+Either `front` and `back` are disjoint, or `back` is
+reachable from `front` and `front` is not reachable from $(D
back).
Params:
@@ -115,10 +115,10 @@ Params:
back = a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
Returns:
- The number of elements brought to the front, i.e., the length of $(D back).
+ The number of elements brought to the front, i.e., the length of `back`.
See_Also:
- $(HTTP sgi.com/tech/stl/_rotate.html, STL's rotate)
+ $(LINK2 http://en.cppreference.com/w/cpp/algorithm/rotate, STL's `rotate`)
*/
size_t bringToFront(InputRange, ForwardRange)(InputRange front, ForwardRange back)
if (isInputRange!InputRange && isForwardRange!ForwardRange)
@@ -145,71 +145,8 @@ if (isInputRange!InputRange && isForwardRange!ForwardRange)
return bringToFrontImpl(frontW, backW);
}
-private size_t bringToFrontImpl(InputRange, ForwardRange)(InputRange front, ForwardRange back)
-if (isInputRange!InputRange && isForwardRange!ForwardRange)
-{
- import std.array : sameHead;
- import std.range : take, Take;
- enum bool sameHeadExists = is(typeof(front.sameHead(back)));
- size_t result;
-
- for (bool semidone; !front.empty && !back.empty; )
- {
- static if (sameHeadExists)
- {
- if (front.sameHead(back)) break; // shortcut
- }
- // Swap elements until front and/or back ends.
- auto back0 = back.save;
- size_t nswaps;
- do
- {
- static if (sameHeadExists)
- {
- // Detect the stepping-over condition.
- if (front.sameHead(back0)) back0 = back.save;
- }
- swapFront(front, back);
- ++nswaps;
- front.popFront();
- back.popFront();
- }
- while (!front.empty && !back.empty);
-
- if (!semidone) result += nswaps;
-
- // Now deal with the remaining elements.
- if (back.empty)
- {
- if (front.empty) break;
- // Right side was shorter, which means that we've brought
- // all the back elements to the front.
- semidone = true;
- // Next pass: bringToFront(front, back0) to adjust the rest.
- back = back0;
- }
- else
- {
- assert(front.empty);
- // Left side was shorter. Let's step into the back.
- static if (is(InputRange == Take!ForwardRange))
- {
- front = take(back0, nswaps);
- }
- else
- {
- immutable subresult = bringToFront(take(back0, nswaps),
- back);
- if (!semidone) result += subresult;
- break; // done
- }
- }
- }
- return result;
-}
-
/**
-The simplest use of $(D bringToFront) is for rotating elements in a
+The simplest use of `bringToFront` is for rotating elements in a
buffer. For example:
*/
@safe unittest
@@ -221,10 +158,10 @@ buffer. For example:
}
/**
-The $(D front) range may actually "step over" the $(D back)
+The `front` range may actually "step over" the `back`
range. This is very useful with forward ranges that cannot compute
-comfortably right-bounded subranges like $(D arr[0 .. 4]) above. In
-the example below, $(D r2) is a right subrange of $(D r1).
+comfortably right-bounded subranges like `arr[0 .. 4]` above. In
+the example below, `r2` is a right subrange of `r1`.
*/
@safe unittest
{
@@ -275,15 +212,78 @@ Unicode integrity is not preserved:
assert(b == "\247a");
}
+private size_t bringToFrontImpl(InputRange, ForwardRange)(InputRange front, ForwardRange back)
+if (isInputRange!InputRange && isForwardRange!ForwardRange)
+{
+ import std.array : sameHead;
+ import std.range : take, Take;
+ enum bool sameHeadExists = is(typeof(front.sameHead(back)));
+ size_t result;
+
+ for (bool semidone; !front.empty && !back.empty; )
+ {
+ static if (sameHeadExists)
+ {
+ if (front.sameHead(back)) break; // shortcut
+ }
+ // Swap elements until front and/or back ends.
+ auto back0 = back.save;
+ size_t nswaps;
+ do
+ {
+ static if (sameHeadExists)
+ {
+ // Detect the stepping-over condition.
+ if (front.sameHead(back0)) back0 = back.save;
+ }
+ swapFront(front, back);
+ ++nswaps;
+ front.popFront();
+ back.popFront();
+ }
+ while (!front.empty && !back.empty);
+
+ if (!semidone) result += nswaps;
+
+ // Now deal with the remaining elements.
+ if (back.empty)
+ {
+ if (front.empty) break;
+ // Right side was shorter, which means that we've brought
+ // all the back elements to the front.
+ semidone = true;
+ // Next pass: bringToFront(front, back0) to adjust the rest.
+ back = back0;
+ }
+ else
+ {
+ assert(front.empty, "Expected front to be empty");
+ // Left side was shorter. Let's step into the back.
+ static if (is(InputRange == Take!ForwardRange))
+ {
+ front = take(back0, nswaps);
+ }
+ else
+ {
+ immutable subresult = bringToFront(take(back0, nswaps),
+ back);
+ if (!semidone) result += subresult;
+ break; // done
+ }
+ }
+ }
+ return result;
+}
+
@safe unittest
{
import std.algorithm.comparison : equal;
import std.conv : text;
- import std.random : Random, unpredictableSeed, uniform;
+ import std.random : Random = Xorshift, uniform;
// a more elaborate test
{
- auto rnd = Random(unpredictableSeed);
+ auto rnd = Random(123_456_789);
int[] a = new int[uniform(100, 200, rnd)];
int[] b = new int[uniform(100, 200, rnd)];
foreach (ref e; a) e = uniform(-100, 100, rnd);
@@ -337,7 +337,7 @@ Unicode integrity is not preserved:
assert(equal(arr, [1, 2, 3, 4, 5, 6, 7]));
}
- // Bugzilla 16959
+ // https://issues.dlang.org/show_bug.cgi?id=16959
auto arr = ['4', '5', '6', '7', '1', '2', '3'];
auto p = bringToFront(arr[0 .. 4], arr[4 .. $]);
@@ -351,11 +351,11 @@ private enum bool areCopyCompatibleArrays(T1, T2) =
// copy
/**
-Copies the content of $(D source) into $(D target) and returns the
-remaining (unfilled) part of $(D target).
+Copies the content of `source` into `target` and returns the
+remaining (unfilled) part of `target`.
-Preconditions: $(D target) shall have enough room to accommodate
-the entirety of $(D source).
+Preconditions: `target` shall have enough room to accommodate
+the entirety of `source`.
Params:
source = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
@@ -363,62 +363,66 @@ Params:
Returns:
The unfilled part of target
-
-See_Also:
- $(HTTP sgi.com/tech/stl/_copy.html, STL's _copy)
*/
TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
-if (areCopyCompatibleArrays!(SourceRange, TargetRange))
+if (isInputRange!SourceRange && isOutputRange!(TargetRange, ElementType!SourceRange))
{
- const tlen = target.length;
- const slen = source.length;
- assert(tlen >= slen,
- "Cannot copy a source range into a smaller target range.");
-
- immutable overlaps = __ctfe || () @trusted {
- return source.ptr < target.ptr + tlen &&
- target.ptr < source.ptr + slen; }();
-
- if (overlaps)
+ static if (areCopyCompatibleArrays!(SourceRange, TargetRange))
{
- foreach (idx; 0 .. slen)
- target[idx] = source[idx];
- return target[slen .. tlen];
- }
- else
- {
- // Array specialization. This uses optimized memory copying
- // routines under the hood and is about 10-20x faster than the
- // generic implementation.
- target[0 .. slen] = source[];
- return target[slen .. $];
- }
-}
+ const tlen = target.length;
+ const slen = source.length;
+ assert(tlen >= slen,
+ "Cannot copy a source range into a smaller target range.");
-/// ditto
-TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
-if (!areCopyCompatibleArrays!(SourceRange, TargetRange) &&
- isInputRange!SourceRange &&
- isOutputRange!(TargetRange, ElementType!SourceRange))
-{
- // Specialize for 2 random access ranges.
- // Typically 2 random access ranges are faster iterated by common
- // index than by x.popFront(), y.popFront() pair
- static if (isRandomAccessRange!SourceRange &&
- hasLength!SourceRange &&
- hasSlicing!TargetRange &&
- isRandomAccessRange!TargetRange &&
- hasLength!TargetRange)
- {
- auto len = source.length;
- foreach (idx; 0 .. len)
- target[idx] = source[idx];
- return target[len .. target.length];
+ immutable overlaps = () @trusted {
+ return source.ptr < target.ptr + tlen &&
+ target.ptr < source.ptr + slen; }();
+
+ if (overlaps)
+ {
+ if (source.ptr < target.ptr)
+ {
+ foreach_reverse (idx; 0 .. slen)
+ target[idx] = source[idx];
+ }
+ else
+ {
+ foreach (idx; 0 .. slen)
+ target[idx] = source[idx];
+ }
+ return target[slen .. tlen];
+ }
+ else
+ {
+ // Array specialization. This uses optimized memory copying
+ // routines under the hood and is about 10-20x faster than the
+ // generic implementation.
+ target[0 .. slen] = source[];
+ return target[slen .. $];
+ }
}
else
{
- put(target, source);
- return target;
+ // Specialize for 2 random access ranges.
+ // Typically 2 random access ranges are faster iterated by common
+ // index than by x.popFront(), y.popFront() pair
+ static if (isRandomAccessRange!SourceRange &&
+ hasLength!SourceRange &&
+ hasSlicing!TargetRange &&
+ isRandomAccessRange!TargetRange &&
+ hasLength!TargetRange)
+ {
+ auto len = source.length;
+ foreach (idx; 0 .. len)
+ target[idx] = source[idx];
+ return target[len .. target.length];
+ }
+ else
+ {
+ foreach (element; source)
+ put(target, element);
+ return target;
+ }
}
}
@@ -446,7 +450,7 @@ range elements, different types of ranges are accepted:
}
/**
-To _copy at most $(D n) elements from a range, you may want to use
+To _copy at most `n` elements from a range, you may want to use
$(REF take, std,range):
*/
@safe unittest
@@ -475,7 +479,7 @@ use $(LREF filter):
/**
$(REF retro, std,range) can be used to achieve behavior similar to
-$(HTTP sgi.com/tech/stl/copy_backward.html, STL's copy_backward'):
+$(LINK2 http://en.cppreference.com/w/cpp/algorithm/copy_backward, STL's `copy_backward`'):
*/
@safe unittest
{
@@ -511,7 +515,15 @@ $(HTTP sgi.com/tech/stl/copy_backward.html, STL's copy_backward'):
assert(a[4 .. 9] == [6, 7, 8, 9, 10]);
}
- { // Test for bug 7898
+ // https://issues.dlang.org/show_bug.cgi?id=21724
+ {
+ int[] a = [1, 2, 3, 4];
+ copy(a[0 .. 2], a[1 .. 3]);
+ assert(a == [1, 1, 2, 4]);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=7898
+ {
enum v =
{
import std.algorithm;
@@ -524,29 +536,58 @@ $(HTTP sgi.com/tech/stl/copy_backward.html, STL's copy_backward'):
}
}
+// https://issues.dlang.org/show_bug.cgi?id=13650
@safe unittest
{
- // Issue 13650
import std.meta : AliasSeq;
- foreach (Char; AliasSeq!(char, wchar, dchar))
- {
+ static foreach (Char; AliasSeq!(char, wchar, dchar))
+ {{
Char[3] a1 = "123";
Char[6] a2 = "456789";
assert(copy(a1[], a2[]) is a2[3..$]);
assert(a1[] == "123");
assert(a2[] == "123789");
+ }}
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18804
+@safe unittest
+{
+ static struct NullSink
+ {
+ void put(E)(E) {}
+ }
+ int line = 0;
+ struct R
+ {
+ int front;
+ @property bool empty() { return line == 1; }
+ void popFront() { line = 1; }
}
+ R r;
+ copy(r, NullSink());
+ assert(line == 1);
}
/**
-Assigns $(D value) to each element of input _range $(D range).
+Assigns `value` to each element of input range `range`.
+
+Alternatively, instead of using a single `value` to fill the `range`,
+a `filter` $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+can be provided. The length of `filler` and `range` do not need to match, but
+`filler` must not be empty.
Params:
range = An
- $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
that exposes references to its elements and has assignable
elements
value = Assigned to each element of range
+ filler = A
+ $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ representing the _fill pattern.
+
+Throws: If `filler` is empty.
See_Also:
$(LREF uninitializedFill)
@@ -583,7 +624,8 @@ if ((isInputRange!Range && is(typeof(range.front = value)) ||
assert(a == [ 5, 5, 5, 5 ]);
}
-// issue 16342, test fallback on mutable narrow strings
+// test fallback on mutable narrow strings
+// https://issues.dlang.org/show_bug.cgi?id=16342
@safe unittest
{
char[] chars = ['a', 'b'];
@@ -646,7 +688,7 @@ if ((isInputRange!Range && is(typeof(range.front = value)) ||
chars[1 .. 3].fill(':');
assert(chars == "a::d");
}
-// end issue 16342
+// end https://issues.dlang.org/show_bug.cgi?id=16342
@safe unittest
{
@@ -712,18 +754,7 @@ if ((isInputRange!Range && is(typeof(range.front = value)) ||
}
}
-/**
-Fills $(D range) with a pattern copied from $(D filler). The length of
-$(D range) does not have to be a multiple of the length of $(D
-filler). If $(D filler) is empty, an exception is thrown.
-
-Params:
- range = An $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
- that exposes references to its elements and has assignable elements.
- filler = The
- $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives)
- representing the _fill pattern.
- */
+/// ditto
void fill(InputRange, ForwardRange)(InputRange range, ForwardRange filler)
if (isInputRange!InputRange
&& (isForwardRange!ForwardRange
@@ -832,12 +863,12 @@ if (isInputRange!InputRange
}
/**
-Initializes all elements of $(D range) with their $(D .init) value.
+Initializes all elements of `range` with their `.init` value.
Assumes that the elements of the range are uninitialized.
Params:
range = An
- $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
that exposes references to its elements and has assignable
elements
@@ -859,9 +890,9 @@ if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range
//We avoid calling emplace here, because our goal is to initialize to
//the static state of T.init,
//So we want to avoid any un-necassarilly CC'ing of T.init
- auto p = typeid(T).initializer();
- if (p.ptr)
+ static if (!__traits(isZeroInit, T))
{
+ auto p = typeid(T).initializer();
for ( ; !range.empty ; range.popFront() )
{
static if (__traits(isStaticArray, T))
@@ -972,7 +1003,7 @@ if (is(Range == char[]) || is(Range == wchar[]))
assert(!typeid(S3).initializer().ptr);
assert( typeid(S4).initializer().ptr);
- foreach (S; AliasSeq!(S1, S2, S3, S4))
+ static foreach (S; AliasSeq!(S1, S2, S3, S4))
{
//initializeAll
{
@@ -1033,8 +1064,8 @@ to its `.init` value after it is moved into target, otherwise it is
left unchanged.
Preconditions:
-If source has internal pointers that point to itself, it cannot be moved, and
-will trigger an assertion failure.
+If source has internal pointers that point to itself and doesn't define
+opPostMove, it cannot be moved, and will trigger an assertion failure.
Params:
source = Data to copy.
@@ -1043,11 +1074,7 @@ Params:
*/
void move(T)(ref T source, ref T target)
{
- // test @safe destructible
- static if (__traits(compiles, (T t) @safe {}))
- trustedMoveImpl(source, target);
- else
- moveImpl(source, target);
+ moveImpl(target, source);
}
/// For non-struct types, `move` just performs `target = source`:
@@ -1123,7 +1150,7 @@ pure nothrow @safe @nogc unittest
move(s21, s22);
assert(s21 == s22);
});
- // Issue 5661 test(1)
+ // https://issues.dlang.org/show_bug.cgi?id=5661 test(1)
static struct S3
{
static struct X { int n = 0; ~this(){n = 0;} }
@@ -1136,7 +1163,7 @@ pure nothrow @safe @nogc unittest
assert(s31.x.n == 0);
assert(s32.x.n == 1);
- // Issue 5661 test(2)
+ // https://issues.dlang.org/show_bug.cgi?id=5661 test(2)
static struct S4
{
static struct X { int n = 0; this(this){n = 0;} }
@@ -1149,7 +1176,7 @@ pure nothrow @safe @nogc unittest
assert(s41.x.n == 0);
assert(s42.x.n == 1);
- // Issue 13990 test
+ // https://issues.dlang.org/show_bug.cgi?id=13990 test
class S5;
S5 s51;
@@ -1160,13 +1187,9 @@ pure nothrow @safe @nogc unittest
}
/// Ditto
-T move(T)(ref T source)
+T move(T)(return scope ref T source)
{
- // test @safe destructible
- static if (__traits(compiles, (T t) @safe {}))
- return trustedMoveImpl(source);
- else
- return moveImpl(source);
+ return moveImpl(source);
}
/// Non-copyable structs can still be moved:
@@ -1185,34 +1208,71 @@ pure nothrow @safe @nogc unittest
assert(s2.a == 2);
}
-private void trustedMoveImpl(T)(ref T source, ref T target) @trusted
+/// `opPostMove` will be called if defined:
+pure nothrow @safe @nogc unittest
{
- moveImpl(source, target);
+ struct S
+ {
+ int a;
+ void opPostMove(const ref S old)
+ {
+ assert(a == old.a);
+ a++;
+ }
+ }
+ S s1;
+ s1.a = 41;
+ S s2 = move(s1);
+ assert(s2.a == 42);
}
-private void moveImpl(T)(ref T source, ref T target)
+// https://issues.dlang.org/show_bug.cgi?id=20869
+// `move` should propagate the attributes of `opPostMove`
+@system unittest
+{
+ static struct S
+ {
+ void opPostMove(const ref S old) nothrow @system
+ {
+ __gshared int i;
+ new int(i++); // Force @gc impure @system
+ }
+ }
+
+ alias T = void function() @system nothrow;
+ static assert(is(typeof({ S s; move(s); }) == T));
+ static assert(is(typeof({ S s; move(s, s); }) == T));
+}
+
+private void moveImpl(T)(ref scope T target, ref return scope T source)
{
import std.traits : hasElaborateDestructor;
static if (is(T == struct))
{
- if (&source == &target) return;
+ // Unsafe when compiling without -dip1000
+ if ((() @trusted => &source == &target)()) return;
+
// Destroy target before overwriting it
static if (hasElaborateDestructor!T) target.__xdtor();
}
// move and emplace source into target
- moveEmplace(source, target);
+ moveEmplaceImpl(target, source);
}
-private T trustedMoveImpl(T)(ref T source) @trusted
+private T moveImpl(T)(ref return scope T source)
{
- return moveImpl(source);
+ // Properly infer safety from moveEmplaceImpl as the implementation below
+ // might void-initialize pointers in result and hence needs to be @trusted
+ if (false) moveEmplaceImpl(source, source);
+
+ return trustedMoveImpl(source);
}
-private T moveImpl(T)(ref T source)
+private T trustedMoveImpl(T)(ref return scope T source) @trusted
{
T result = void;
- moveEmplace(source, result);
+ moveEmplaceImpl(result, source);
return result;
}
@@ -1239,7 +1299,7 @@ private T moveImpl(T)(ref T source)
assert(s21 == s22);
});
- // Issue 5661 test(1)
+ // https://issues.dlang.org/show_bug.cgi?id=5661 test(1)
static struct S3
{
static struct X { int n = 0; ~this(){n = 0;} }
@@ -1252,7 +1312,7 @@ private T moveImpl(T)(ref T source)
assert(s31.x.n == 0);
assert(s32.x.n == 1);
- // Issue 5661 test(2)
+ // https://issues.dlang.org/show_bug.cgi?id=5661 test(2)
static struct S4
{
static struct X { int n = 0; this(this){n = 0;} }
@@ -1265,7 +1325,7 @@ private T moveImpl(T)(ref T source)
assert(s41.x.n == 0);
assert(s42.x.n == 1);
- // Issue 13990 test
+ // https://issues.dlang.org/show_bug.cgi?id=13990 test
class S5;
S5 s51;
@@ -1289,14 +1349,16 @@ private T moveImpl(T)(ref T source)
assert(a.n == 0);
}
-@safe unittest//Issue 6217
+// https://issues.dlang.org/show_bug.cgi?id=6217
+@safe unittest
{
import std.algorithm.iteration : map;
auto x = map!"a"([1,2,3]);
x = move(x);
}
-@safe unittest// Issue 8055
+// https://issues.dlang.org/show_bug.cgi?id=8055
+@safe unittest
{
static struct S
{
@@ -1316,7 +1378,8 @@ private T moveImpl(T)(ref T source)
assert(b.x == 0);
}
-@system unittest// Issue 8057
+// https://issues.dlang.org/show_bug.cgi?id=8057
+@system unittest
{
int n = 10;
struct S
@@ -1338,7 +1401,7 @@ private T moveImpl(T)(ref T source)
auto b = foo(a);
assert(b.x == 1);
- // Regression 8171
+ // Regression https://issues.dlang.org/show_bug.cgi?id=8171
static struct Array(T)
{
// nested struct has no member
@@ -1352,37 +1415,34 @@ private T moveImpl(T)(ref T source)
move(x, x);
}
-/**
- * Similar to $(LREF move) but assumes `target` is uninitialized. This
- * is more efficient because `source` can be blitted over `target`
- * without destroying or initializing it first.
- *
- * Params:
- * source = value to be moved into target
- * target = uninitialized value to be filled by source
- */
-void moveEmplace(T)(ref T source, ref T target) @system
+private void moveEmplaceImpl(T)(ref scope T target, ref return scope T source)
{
import core.stdc.string : memcpy, memset;
import std.traits : hasAliasing, hasElaborateAssign,
hasElaborateCopyConstructor, hasElaborateDestructor,
- isAssignable;
+ hasElaborateMove,
+ isAssignable, isStaticArray;
static if (!is(T == class) && hasAliasing!T) if (!__ctfe)
{
import std.exception : doesPointTo;
- assert(!doesPointTo(source, source), "Cannot move object with internal pointer.");
+ assert(!(doesPointTo(source, source) && !hasElaborateMove!T),
+ "Cannot move object with internal pointer unless `opPostMove` is defined.");
}
static if (is(T == struct))
{
- assert(&source !is &target, "source and target must not be identical");
+ // Unsafe when compiling without -dip1000
+ assert((() @trusted => &source !is &target)(), "source and target must not be identical");
static if (hasElaborateAssign!T || !isAssignable!T)
- memcpy(&target, &source, T.sizeof);
+ () @trusted { memcpy(&target, &source, T.sizeof); }();
else
target = source;
+ static if (hasElaborateMove!T)
+ __move_post_blt(target, source);
+
// If the source defines a destructor or a postblit hook, we must obliterate the
// object in order to avoid double freeing and undue aliasing
static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T)
@@ -1393,13 +1453,20 @@ void moveEmplace(T)(ref T source, ref T target) @system
else
enum sz = T.sizeof;
- auto init = typeid(T).initializer();
- if (init.ptr is null) // null ptr means initialize to 0s
- memset(&source, 0, sz);
+ static if (__traits(isZeroInit, T))
+ () @trusted { memset(&source, 0, sz); }();
else
- memcpy(&source, init.ptr, sz);
+ {
+ auto init = typeid(T).initializer();
+ () @trusted { memcpy(&source, init.ptr, sz); }();
+ }
}
}
+ else static if (isStaticArray!T)
+ {
+ for (size_t i = 0; i < source.length; ++i)
+ move(source[i], target[i]);
+ }
else
{
// Primitive data (including pointers and arrays) or class -
@@ -1408,6 +1475,20 @@ void moveEmplace(T)(ref T source, ref T target) @system
}
}
+/**
+ * Similar to $(LREF move) but assumes `target` is uninitialized. This
+ * is more efficient because `source` can be blitted over `target`
+ * without destroying or initializing it first.
+ *
+ * Params:
+ * source = value to be moved into target
+ * target = uninitialized value to be filled by source
+ */
+void moveEmplace(T)(ref T source, ref T target) pure @system
+{
+ moveEmplaceImpl(target, source);
+}
+
///
pure nothrow @nogc @system unittest
{
@@ -1433,6 +1514,24 @@ pure nothrow @nogc @system unittest
assert(val == 0);
}
+// https://issues.dlang.org/show_bug.cgi?id=18913
+@safe unittest
+{
+ static struct NoCopy
+ {
+ int payload;
+ ~this() { }
+ @disable this(this);
+ }
+
+ static void f(NoCopy[2]) { }
+
+ NoCopy[2] ncarray = [ NoCopy(1), NoCopy(2) ];
+
+ static assert(!__traits(compiles, f(ncarray)));
+ f(move(ncarray));
+}
+
// moveAll
/**
Calls `move(a, b)` for each element `a` in `src` and the corresponding
@@ -1447,9 +1546,9 @@ Params:
src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with
movable elements.
tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with
- elements that elements from $(D src) can be moved into.
+ elements that elements from `src` can be moved into.
-Returns: The leftover portion of $(D tgt) after all elements from $(D src) have
+Returns: The leftover portion of `tgt` after all elements from `src` have
been moved.
*/
InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt)
@@ -1528,7 +1627,7 @@ private InputRange2 moveAllImpl(alias moveOp, InputRange1, InputRange2)(
&& hasSlicing!InputRange2 && isRandomAccessRange!InputRange2)
{
auto toMove = src.length;
- assert(toMove <= tgt.length);
+ assert(toMove <= tgt.length, "Source buffer needs to be smaller or equal to the target buffer.");
foreach (idx; 0 .. toMove)
moveOp(src[idx], tgt[idx]);
return tgt[toMove .. tgt.length];
@@ -1537,7 +1636,7 @@ private InputRange2 moveAllImpl(alias moveOp, InputRange1, InputRange2)(
{
for (; !src.empty; src.popFront(), tgt.popFront())
{
- assert(!tgt.empty);
+ assert(!tgt.empty, "Source buffer needs to be smaller or equal to the target buffer.");
moveOp(src.front, tgt.front);
}
return tgt;
@@ -1554,7 +1653,7 @@ Params:
src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with
movable elements.
tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with
- elements that elements from $(D src) can be moved into.
+ elements that elements from `src` can be moved into.
Returns: The leftover portions of the two ranges after one or the other of the
ranges have been exhausted.
@@ -1624,15 +1723,15 @@ Defines the swapping strategy for algorithms that need to swap
elements in a range (such as partition and sort). The strategy
concerns the swapping of elements that are not the core concern of the
algorithm. For example, consider an algorithm that sorts $(D [ "abc",
-"b", "aBc" ]) according to $(D toUpper(a) < toUpper(b)). That
-algorithm might choose to swap the two equivalent strings $(D "abc")
-and $(D "aBc"). That does not affect the sorting since both $(D [
-"abc", "aBc", "b" ]) and $(D [ "aBc", "abc", "b" ]) are valid
+"b", "aBc" ]) according to `toUpper(a) < toUpper(b)`. That
+algorithm might choose to swap the two equivalent strings `"abc"`
+and `"aBc"`. That does not affect the sorting since both
+`["abc", "aBc", "b" ]` and `[ "aBc", "abc", "b" ]` are valid
outcomes.
Some situations require that the algorithm must NOT ever change the
relative ordering of equivalent elements (in the example above, only
-$(D [ "abc", "aBc", "b" ]) would be the correct result). Such
+`[ "abc", "aBc", "b" ]` would be the correct result). Such
algorithms are called $(B stable). If the ordering algorithm may swap
equivalent elements discretionarily, the ordering is called $(B
unstable).
@@ -1642,12 +1741,12 @@ being stable only on a well-defined subrange of the range. There is no
established terminology for such behavior; this library calls it $(B
semistable).
-Generally, the $(D stable) ordering strategy may be more costly in
+Generally, the `stable` ordering strategy may be more costly in
time and/or space than the other two because it imposes additional
-constraints. Similarly, $(D semistable) may be costlier than $(D
+constraints. Similarly, `semistable` may be costlier than $(D
unstable). As (semi-)stability is not needed very often, the ordering
-algorithms in this module parameterized by $(D SwapStrategy) all
-choose $(D SwapStrategy.unstable) as the default.
+algorithms in this module parameterized by `SwapStrategy` all
+choose `SwapStrategy.unstable` as the default.
*/
enum SwapStrategy
@@ -1672,8 +1771,6 @@ enum SwapStrategy
///
@safe unittest
{
- import std.stdio;
- import std.algorithm.sorting : partition;
int[] a = [0, 1, 2, 3];
assert(remove!(SwapStrategy.stable)(a, 1) == [0, 2, 3]);
a = [0, 1, 2, 3];
@@ -1699,11 +1796,27 @@ enum SwapStrategy
assert(arr == [10, 9, 8, 4, 5, 6, 7, 3, 2, 1]);
}
+private template isValidIntegralTuple(T)
+{
+ import std.traits : isIntegral;
+ import std.typecons : isTuple;
+ static if (isTuple!T)
+ {
+ enum isValidIntegralTuple = T.length == 2 &&
+ isIntegral!(typeof(T.init[0])) && isIntegral!(typeof(T.init[0]));
+ }
+ else
+ {
+ enum isValidIntegralTuple = isIntegral!T;
+ }
+}
+
+
/**
Eliminates elements at given offsets from `range` and returns the shortened
range.
-For example, here is how to _remove a single element from an array:
+For example, here is how to remove a single element from an array:
----
string[] a = [ "a", "b", "c", "d" ];
@@ -1711,9 +1824,9 @@ a = a.remove(1); // remove element at offset 1
assert(a == [ "a", "c", "d"]);
----
-Note that `remove` does not change the length of the original _range directly;
-instead, it returns the shortened _range. If its return value is not assigned to
-the original _range, the original _range will retain its original length, though
+Note that `remove` does not change the length of the original range directly;
+instead, it returns the shortened range. If its return value is not assigned to
+the original range, the original range will retain its original length, though
its contents will have changed:
----
@@ -1722,7 +1835,7 @@ assert(remove(a, 1) == [ 3, 7, 8 ]);
assert(a == [ 3, 7, 8, 8 ]);
----
-The element at _offset `1` has been removed and the rest of the elements have
+The element at offset `1` has been removed and the rest of the elements have
shifted up to fill its place, however, the original array remains of the same
length. This is because all functions in `std.algorithm` only change $(I
content), not $(I topology). The value `8` is repeated because $(LREF move) was
@@ -1730,7 +1843,7 @@ invoked to rearrange elements, and on integers `move` simply copies the source
to the destination. To replace `a` with the effect of the removal, simply
assign the slice returned by `remove` to it, as shown in the first example.
-Multiple indices can be passed into $(D remove). In that case,
+Multiple indices can be passed into `remove`. In that case,
elements at the respective indices are all removed. The indices must
be passed in increasing order, otherwise an exception occurs.
@@ -1741,9 +1854,21 @@ assert(remove(a, 1, 3, 5) ==
----
(Note that all indices refer to slots in the $(I original) array, not
-in the array as it is being progressively shortened.) Finally, any
-combination of integral offsets and tuples composed of two integral
-offsets can be passed in.
+in the array as it is being progressively shortened.)
+
+Tuples of two integral offsets can be used to remove an indices range:
+
+----
+int[] a = [ 3, 4, 5, 6, 7];
+assert(remove(a, 1, tuple(1, 3), 9) == [ 3, 6, 7 ]);
+----
+
+The tuple passes in a range closed to the left and open to
+the right (consistent with built-in slices), e.g. `tuple(1, 3)`
+means indices `1` and `2` but not `3`.
+
+Finally, any combination of integral offsets and tuples composed of two integral
+offsets can be passed in:
----
int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
@@ -1751,56 +1876,247 @@ assert(remove(a, 1, tuple(3, 5), 9) == [ 0, 2, 5, 6, 7, 8, 10 ]);
----
In this case, the slots at positions 1, 3, 4, and 9 are removed from
-the array. The tuple passes in a range closed to the left and open to
-the right (consistent with built-in slices), e.g. $(D tuple(3, 5))
-means indices $(D 3) and $(D 4) but not $(D 5).
+the array.
If the need is to remove some elements in the range but the order of
the remaining elements does not have to be preserved, you may want to
-pass $(D SwapStrategy.unstable) to $(D remove).
+pass `SwapStrategy.unstable` to `remove`.
----
int[] a = [ 0, 1, 2, 3 ];
assert(remove!(SwapStrategy.unstable)(a, 1) == [ 0, 3, 2 ]);
----
-In the case above, the element at slot $(D 1) is removed, but replaced
+In the case above, the element at slot `1` is removed, but replaced
with the last element of the range. Taking advantage of the relaxation
-of the stability requirement, $(D remove) moved elements from the end
+of the stability requirement, `remove` moved elements from the end
of the array over the slots to be removed. This way there is less data
movement to be done which improves the execution time of the function.
-The function $(D remove) works on bidirectional ranges that have assignable
+The function `remove` works on bidirectional ranges that have assignable
lvalue elements. The moving strategy is (listed from fastest to slowest):
-$(UL $(LI If $(D s == SwapStrategy.unstable && isRandomAccessRange!Range &&
+
+$(UL
+ $(LI If $(D s == SwapStrategy.unstable && isRandomAccessRange!Range &&
hasLength!Range && hasLvalueElements!Range), then elements are moved from the
end of the range into the slots to be filled. In this case, the absolute
-minimum of moves is performed.) $(LI Otherwise, if $(D s ==
+minimum of moves is performed.)
+ $(LI Otherwise, if $(D s ==
SwapStrategy.unstable && isBidirectionalRange!Range && hasLength!Range
&& hasLvalueElements!Range), then elements are still moved from the
end of the range, but time is spent on advancing between slots by repeated
-calls to $(D range.popFront).) $(LI Otherwise, elements are moved
-incrementally towards the front of $(D range); a given element is never
+calls to `range.popFront`.)
+ $(LI Otherwise, elements are moved
+incrementally towards the front of `range`; a given element is never
moved several times, but more elements are moved than in the previous
-cases.))
+cases.)
+)
Params:
s = a SwapStrategy to determine if the original order needs to be preserved
- range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,_range,primitives)
+ range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
with a length member
offset = which element(s) to remove
Returns:
- a range containing all of the elements of range with offset removed
- */
+ A range containing all of the elements of range with offset removed.
+*/
+Range remove
+(SwapStrategy s = SwapStrategy.stable, Range, Offset ...)
+(Range range, Offset offset)
+if (Offset.length >= 1 && allSatisfy!(isValidIntegralTuple, Offset))
+{
+ // Activate this check when the deprecation of non-integral tuples is over
+ //import std.traits : isIntegral;
+ //import std.typecons : isTuple;
+ //static foreach (T; Offset)
+ //{
+ //static if (isTuple!T)
+ //{
+ //static assert(T.length == 2 &&
+ //isIntegral!(typeof(T.init[0])) && isIntegral!(typeof(T.init[0])),
+ //"Each offset must be an integral or a tuple of two integrals." ~
+ //"Use `arr.remove(pos1, pos2)` or `arr.remove(tuple(start, begin))`");
+ //}
+ //else
+ //{
+ //static assert(isIntegral!T,
+ //"Each offset must be an integral or a tuple of two integrals." ~
+ //"Use `arr.remove(pos1, pos2)` or `arr.remove(tuple(start, begin))`");
+ //}
+ //}
+ return removeImpl!s(range, offset);
+}
+
+deprecated("Use of non-integral tuples is deprecated. Use remove(tuple(start, end).")
Range remove
-(SwapStrategy s = SwapStrategy.stable, Range, Offset...)
+(SwapStrategy s = SwapStrategy.stable, Range, Offset ...)
(Range range, Offset offset)
-if (s != SwapStrategy.stable
- && isBidirectionalRange!Range
- && hasLvalueElements!Range
- && hasLength!Range
- && Offset.length >= 1)
+if (Offset.length >= 1 && !allSatisfy!(isValidIntegralTuple, Offset))
+{
+ return removeImpl!s(range, offset);
+}
+
+///
+@safe pure unittest
+{
+ import std.typecons : tuple;
+
+ auto a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5 ]);
+ a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.stable)(a, 1, 3) == [ 0, 2, 4, 5] );
+ a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 6)) == [ 0, 2 ]);
+
+ a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 5, 2, 3, 4]);
+ a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4)) == [0, 5, 4]);
+}
+
+///
+@safe pure unittest
+{
+ import std.typecons : tuple;
+
+ // Delete an index
+ assert([4, 5, 6].remove(1) == [4, 6]);
+
+ // Delete multiple indices
+ assert([4, 5, 6, 7, 8].remove(1, 3) == [4, 6, 8]);
+
+ // Use an indices range
+ assert([4, 5, 6, 7, 8].remove(tuple(1, 3)) == [4, 7, 8]);
+
+ // Use an indices range and individual indices
+ assert([4, 5, 6, 7, 8].remove(0, tuple(1, 3), 4) == [7]);
+}
+
+/// `SwapStrategy.unstable` is faster, but doesn't guarantee the same order of the original array
+@safe pure unittest
+{
+ assert([5, 6, 7, 8].remove!(SwapStrategy.stable)(1) == [5, 7, 8]);
+ assert([5, 6, 7, 8].remove!(SwapStrategy.unstable)(1) == [5, 8, 7]);
+}
+
+private auto removeImpl(SwapStrategy s, Range, Offset...)(Range range, Offset offset)
+{
+ static if (isNarrowString!Range)
+ {
+ static assert(isMutable!(typeof(range[0])),
+ "Elements must be mutable to remove");
+ static assert(s == SwapStrategy.stable,
+ "Only stable removing can be done for character arrays");
+ return removeStableString(range, offset);
+ }
+ else
+ {
+ static assert(isBidirectionalRange!Range,
+ "Range must be bidirectional");
+ static assert(hasLvalueElements!Range,
+ "Range must have Lvalue elements (see std.range.hasLvalueElements)");
+
+ static if (s == SwapStrategy.unstable)
+ {
+ static assert(hasLength!Range,
+ "Range must have `length` for unstable remove");
+ return removeUnstable(range, offset);
+ }
+ else static if (s == SwapStrategy.stable)
+ return removeStable(range, offset);
+ else
+ static assert(false,
+ "Only SwapStrategy.stable and SwapStrategy.unstable are supported");
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.range;
+
+ // https://issues.dlang.org/show_bug.cgi?id=10173
+ int[] test = iota(0, 10).array();
+ assertThrown(remove!(SwapStrategy.stable)(test, tuple(2, 4), tuple(1, 3)));
+ assertThrown(remove!(SwapStrategy.unstable)(test, tuple(2, 4), tuple(1, 3)));
+ assertThrown(remove!(SwapStrategy.stable)(test, 2, 4, 1, 3));
+ assertThrown(remove!(SwapStrategy.unstable)(test, 2, 4, 1, 3));
+}
+
+@safe unittest
+{
+ import std.range;
+ int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ assert(remove!(SwapStrategy.stable)(a, 1) ==
+ [ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]);
+
+ a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ assert(remove!(SwapStrategy.unstable)(a, 0, 10) ==
+ [ 9, 1, 2, 3, 4, 5, 6, 7, 8 ]);
+
+ a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ assert(remove!(SwapStrategy.unstable)(a, 0, tuple(9, 11)) ==
+ [ 8, 1, 2, 3, 4, 5, 6, 7 ]);
+ // https://issues.dlang.org/show_bug.cgi?id=5224
+ a = [ 1, 2, 3, 4 ];
+ assert(remove!(SwapStrategy.unstable)(a, 2) ==
+ [ 1, 2, 4 ]);
+
+ a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ assert(remove!(SwapStrategy.stable)(a, 1, 5) ==
+ [ 0, 2, 3, 4, 6, 7, 8, 9, 10 ]);
+
+ a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ assert(remove!(SwapStrategy.stable)(a, 1, 3, 5)
+ == [ 0, 2, 4, 6, 7, 8, 9, 10]);
+ a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 5))
+ == [ 0, 2, 5, 6, 7, 8, 9, 10]);
+
+ a = iota(0, 10).array();
+ assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4), tuple(6, 7))
+ == [0, 9, 8, 7, 4, 5]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=11576
+@safe unittest
+{
+ auto arr = [1,2,3];
+ arr = arr.remove!(SwapStrategy.unstable)(2);
+ assert(arr == [1,2]);
+
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=12889
+@safe unittest
+{
+ import std.range;
+ int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]];
+ auto orig = arr.dup;
+ foreach (i; iota(arr.length))
+ {
+ assert(orig == arr.remove!(SwapStrategy.unstable)(tuple(i,i)));
+ assert(orig == arr.remove!(SwapStrategy.stable)(tuple(i,i)));
+ }
+}
+
+@safe unittest
+{
+ char[] chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
+ remove(chars, 4);
+ assert(chars == ['a', 'b', 'c', 'd', 'f', 'g', 'h', 'h']);
+
+ char[] bigChars = "∑œ∆¬é˚˙ƒé∂ß¡¡".dup;
+ assert(remove(bigChars, tuple(4, 6), 8) == ("∑œ∆¬˙ƒ∂ß¡¡"));
+
+ import std.exception : assertThrown;
+ assertThrown(remove(bigChars.dup, 1, 0));
+ assertThrown(remove(bigChars.dup, tuple(4, 3)));
+}
+
+private Range removeUnstable(Range, Offset...)(Range range, Offset offset)
{
Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts;
foreach (i, v; offset)
@@ -1849,7 +2165,7 @@ if (s != SwapStrategy.stable
break;
}
// Advance to next blackout on the left
- assert(blackouts[left].pos >= tgtPos);
+ assert(blackouts[left].pos >= tgtPos, "Next blackout on the left shouldn't appear before the target.");
tgt.popFrontExactly(blackouts[left].pos - tgtPos);
tgtPos = blackouts[left].pos;
@@ -1879,14 +2195,7 @@ if (s != SwapStrategy.stable
return range;
}
-/// Ditto
-Range remove
-(SwapStrategy s = SwapStrategy.stable, Range, Offset...)
-(Range range, Offset offset)
-if (s == SwapStrategy.stable
- && isBidirectionalRange!Range
- && hasLvalueElements!Range
- && Offset.length >= 1)
+private Range removeStable(Range, Offset...)(Range range, Offset offset)
{
auto result = range;
auto src = range, tgt = range;
@@ -1930,149 +2239,121 @@ if (s == SwapStrategy.stable
return result;
}
-///
-@safe pure unittest
+private Range removeStableString(Range, Offset...)(Range range, Offset offsets)
{
- import std.typecons : tuple;
-
- auto a = [ 0, 1, 2, 3, 4, 5 ];
- assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5 ]);
- a = [ 0, 1, 2, 3, 4, 5 ];
- assert(remove!(SwapStrategy.stable)(a, 1, 3) == [ 0, 2, 4, 5] );
- a = [ 0, 1, 2, 3, 4, 5 ];
- assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 6)) == [ 0, 2 ]);
-
- a = [ 0, 1, 2, 3, 4, 5 ];
- assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 5, 2, 3, 4]);
- a = [ 0, 1, 2, 3, 4, 5 ];
- assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4)) == [0, 5, 4]);
-}
+ import std.utf : stride;
+ size_t charIdx = 0;
+ size_t dcharIdx = 0;
+ size_t charShift = 0;
-@safe unittest
-{
- import std.exception : assertThrown;
- import std.range;
+ void skipOne()
+ {
+ charIdx += stride(range[charIdx .. $]);
+ ++dcharIdx;
+ }
- // http://d.puremagic.com/issues/show_bug.cgi?id=10173
- int[] test = iota(0, 10).array();
- assertThrown(remove!(SwapStrategy.stable)(test, tuple(2, 4), tuple(1, 3)));
- assertThrown(remove!(SwapStrategy.unstable)(test, tuple(2, 4), tuple(1, 3)));
- assertThrown(remove!(SwapStrategy.stable)(test, 2, 4, 1, 3));
- assertThrown(remove!(SwapStrategy.unstable)(test, 2, 4, 1, 3));
-}
+ void copyBackOne()
+ {
+ auto encodedLen = stride(range[charIdx .. $]);
+ foreach (j; charIdx .. charIdx + encodedLen)
+ range[j - charShift] = range[j];
+ charIdx += encodedLen;
+ ++dcharIdx;
+ }
-@safe unittest
-{
- import std.range;
- int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
- a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
- assert(remove!(SwapStrategy.stable)(a, 1) ==
- [ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]);
+ foreach (pass, i; offsets)
+ {
+ static if (is(typeof(i[0])) && is(typeof(i[1])))
+ {
+ auto from = i[0];
+ auto delta = i[1] - i[0];
+ }
+ else
+ {
+ auto from = i;
+ enum delta = 1;
+ }
- a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
- assert(remove!(SwapStrategy.unstable)(a, 0, 10) ==
- [ 9, 1, 2, 3, 4, 5, 6, 7, 8 ]);
+ import std.exception : enforce;
+ enforce(dcharIdx <= from && delta >= 0,
+ "remove(): incorrect ordering of elements to remove");
- a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
- assert(remove!(SwapStrategy.unstable)(a, 0, tuple(9, 11)) ==
- [ 8, 1, 2, 3, 4, 5, 6, 7 ]);
- // http://d.puremagic.com/issues/show_bug.cgi?id=5224
- a = [ 1, 2, 3, 4 ];
- assert(remove!(SwapStrategy.unstable)(a, 2) ==
- [ 1, 2, 4 ]);
+ while (dcharIdx < from)
+ static if (pass == 0)
+ skipOne();
+ else
+ copyBackOne();
- a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
- assert(remove!(SwapStrategy.stable)(a, 1, 5) ==
- [ 0, 2, 3, 4, 6, 7, 8, 9, 10 ]);
+ auto mark = charIdx;
+ while (dcharIdx < from + delta)
+ skipOne();
+ charShift += charIdx - mark;
+ }
- a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
- assert(remove!(SwapStrategy.stable)(a, 1, 3, 5)
- == [ 0, 2, 4, 6, 7, 8, 9, 10]);
- a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
- assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 5))
- == [ 0, 2, 5, 6, 7, 8, 9, 10]);
+ foreach (i; charIdx .. range.length)
+ range[i - charShift] = range[i];
- a = iota(0, 10).array();
- assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4), tuple(6, 7))
- == [0, 9, 8, 7, 4, 5]);
+ return range[0 .. $ - charShift];
}
+// Use of dynamic arrays as offsets is too error-prone
+// https://issues.dlang.org/show_bug.cgi?id=12086
+// Activate these tests once the deprecation period of remove with non-integral tuples is over
@safe unittest
{
- // Issue 11576
- auto arr = [1,2,3];
- arr = arr.remove!(SwapStrategy.unstable)(2);
- assert(arr == [1,2]);
+ //static assert(!__traits(compiles, [0, 1, 2, 3, 4].remove([1, 3]) == [0, 3, 4]));
+ static assert(__traits(compiles, [0, 1, 2, 3, 4].remove(1, 3) == [0, 2, 4]));
+ //static assert(!__traits(compiles, assert([0, 1, 2, 3, 4].remove([1, 3, 4]) == [0, 3, 4])));
+ //static assert(!__traits(compiles, assert([0, 1, 2, 3, 4].remove(tuple(1, 3, 4)) == [0, 3, 4])));
-}
-
-@safe unittest
-{
- import std.range;
- // Bug# 12889
- int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]];
- auto orig = arr.dup;
- foreach (i; iota(arr.length))
- {
- assert(orig == arr.remove!(SwapStrategy.unstable)(tuple(i,i)));
- assert(orig == arr.remove!(SwapStrategy.stable)(tuple(i,i)));
- }
+ import std.range : only;
+ //static assert(!__traits(compiles, assert([0, 1, 2, 3, 4].remove(only(1, 3)) == [0, 3, 4])));
+ static assert(__traits(compiles, assert([0, 1, 2, 3, 4].remove(1, 3) == [0, 2, 4])));
}
/**
Reduces the length of the
-$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,_range,primitives) $(D range) by removing
-elements that satisfy $(D pred). If $(D s = SwapStrategy.unstable),
+$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) `range` by removing
+elements that satisfy `pred`. If `s = SwapStrategy.unstable`,
elements are moved from the right end of the range over the elements
-to eliminate. If $(D s = SwapStrategy.stable) (the default),
+to eliminate. If `s = SwapStrategy.stable` (the default),
elements are moved progressively to front such that their relative
order is preserved. Returns the filtered range.
Params:
range = a bidirectional ranges with lvalue elements
+ or mutable character arrays
Returns:
- the range with all of the elements where $(D pred) is $(D true)
+ the range with all of the elements where `pred` is `true`
removed
*/
-Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range)
-(Range range)
-if (isBidirectionalRange!Range
- && hasLvalueElements!Range)
+Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range)(Range range)
{
import std.functional : unaryFun;
- auto result = range;
- static if (s != SwapStrategy.stable)
+ alias pred_ = unaryFun!pred;
+ static if (isNarrowString!Range)
{
- for (;!range.empty;)
- {
- if (!unaryFun!pred(range.front))
- {
- range.popFront();
- continue;
- }
- move(range.back, range.front);
- range.popBack();
- result.popBack();
- }
+ static assert(isMutable!(typeof(range[0])),
+ "Elements must be mutable to remove");
+ static assert(s == SwapStrategy.stable,
+ "Only stable removing can be done for character arrays");
+ return removePredString!pred_(range);
}
else
{
- auto tgt = range;
- for (; !range.empty; range.popFront())
- {
- if (unaryFun!(pred)(range.front))
- {
- // yank this guy
- result.popBack();
- continue;
- }
- // keep this guy
- move(range.front, tgt.front);
- tgt.popFront();
- }
+ static assert(isBidirectionalRange!Range,
+ "Range must be bidirectional");
+ static assert(hasLvalueElements!Range,
+ "Range must have Lvalue elements (see std.range.hasLvalueElements)");
+ static if (s == SwapStrategy.unstable)
+ return removePredUnstable!pred_(range);
+ else static if (s == SwapStrategy.stable)
+ return removePredStable!pred_(range);
+ else
+ static assert(false,
+ "Only SwapStrategy.stable and SwapStrategy.unstable are supported");
}
- return result;
}
///
@@ -2104,10 +2385,10 @@ if (isBidirectionalRange!Range
[ 1, 3, 3, 4, 5, 5, 6 ]);
}
-@nogc @system unittest
+@nogc @safe unittest
{
// @nogc test
- int[10] arr = [0,1,2,3,4,5,6,7,8,9];
+ static int[] arr = [0,1,2,3,4,5,6,7,8,9];
alias pred = e => e < 5;
auto r = arr[].remove!(SwapStrategy.unstable)(0);
@@ -2125,19 +2406,20 @@ if (isBidirectionalRange!Range
import std.meta : AliasSeq;
import std.range : iota, only;
import std.typecons : Tuple;
- alias S = Tuple!(int[2]);
+ alias E = Tuple!(int, int);
+ alias S = Tuple!(E);
S[] soffsets;
foreach (start; 0 .. 5)
foreach (end; min(start+1,5) .. 5)
- soffsets ~= S([start,end]);
- alias D = Tuple!(int[2],int[2]);
+ soffsets ~= S(E(start,end));
+ alias D = Tuple!(E, E);
D[] doffsets;
foreach (start1; 0 .. 10)
foreach (end1; min(start1+1,10) .. 10)
foreach (start2; end1 .. 10)
foreach (end2; min(start2+1,10) .. 10)
- doffsets ~= D([start1,end1],[start2,end2]);
- alias T = Tuple!(int[2],int[2],int[2]);
+ doffsets ~= D(E(start1,end1),E(start2,end2));
+ alias T = Tuple!(E, E, E);
T[] toffsets;
foreach (start1; 0 .. 15)
foreach (end1; min(start1+1,15) .. 15)
@@ -2145,7 +2427,7 @@ if (isBidirectionalRange!Range
foreach (end2; min(start2+1,15) .. 15)
foreach (start3; end2 .. 15)
foreach (end3; min(start3+1,15) .. 15)
- toffsets ~= T([start1,end1],[start2,end2],[start3,end3]);
+ toffsets ~= T(E(start1,end1),E(start2,end2),E(start3,end3));
static void verify(O...)(int[] r, int len, int removed, bool stable, O offsets)
{
@@ -2154,7 +2436,7 @@ if (isBidirectionalRange!Range
assert(r.all!(e => all!(o => e < o[0] || e >= o[1])(offsets.only)));
}
- foreach (offsets; AliasSeq!(soffsets,doffsets,toffsets))
+ static foreach (offsets; AliasSeq!(soffsets,doffsets,toffsets))
foreach (os; offsets)
{
int len = 5*os.length;
@@ -2179,109 +2461,186 @@ if (isBidirectionalRange!Range
}
}
-// reverse
-/**
-Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D
-swap).
-Params:
- r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
- with swappable elements or a random access range with a length member
+@safe unittest
+{
+ char[] chars = "abcdefg".dup;
+ assert(chars.remove!(dc => dc == 'c' || dc == 'f') == "abdeg");
+ assert(chars == "abdegfg");
-See_Also:
- $(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view
-*/
-void reverse(Range)(Range r)
-if (isBidirectionalRange!Range && !isRandomAccessRange!Range
- && hasSwappableElements!Range)
+ assert(chars.remove!"a == 'd'" == "abegfg");
+
+ char[] bigChars = "¥^¨^©é√∆π".dup;
+ assert(bigChars.remove!(dc => dc == "¨"d[0] || dc == "é"d[0]) == "¥^^©√∆π");
+}
+
+private Range removePredUnstable(alias pred, Range)(Range range)
{
- while (!r.empty)
+ auto result = range;
+ for (;!range.empty;)
{
- swap(r.front, r.back);
- r.popFront();
- if (r.empty) break;
- r.popBack();
+ if (!pred(range.front))
+ {
+ range.popFront();
+ continue;
+ }
+ move(range.back, range.front);
+ range.popBack();
+ result.popBack();
}
+ return result;
}
-///
-@safe unittest
+private Range removePredStable(alias pred, Range)(Range range)
{
- int[] arr = [ 1, 2, 3 ];
- reverse(arr);
- assert(arr == [ 3, 2, 1 ]);
+ auto result = range;
+ auto tgt = range;
+ for (; !range.empty; range.popFront())
+ {
+ if (pred(range.front))
+ {
+ // yank this guy
+ result.popBack();
+ continue;
+ }
+ // keep this guy
+ move(range.front, tgt.front);
+ tgt.popFront();
+ }
+ return result;
}
-///ditto
-void reverse(Range)(Range r)
-if (isRandomAccessRange!Range && hasLength!Range)
+private Range removePredString(alias pred, SwapStrategy s = SwapStrategy.stable, Range)
+(Range range)
{
- //swapAt is in fact the only way to swap non lvalue ranges
- immutable last = r.length-1;
- immutable steps = r.length/2;
- for (size_t i = 0; i < steps; i++)
+ import std.utf : decode;
+ import std.functional : unaryFun;
+
+ alias pred_ = unaryFun!pred;
+
+ size_t charIdx = 0;
+ size_t charShift = 0;
+ while (charIdx < range.length)
+ {
+ size_t start = charIdx;
+ if (pred_(decode(range, charIdx)))
+ {
+ charShift += charIdx - start;
+ break;
+ }
+ }
+ while (charIdx < range.length)
{
- r.swapAt(i, last-i);
+ size_t start = charIdx;
+ auto doRemove = pred_(decode(range, charIdx));
+ auto encodedLen = charIdx - start;
+ if (doRemove)
+ charShift += encodedLen;
+ else
+ foreach (i; start .. charIdx)
+ range[i - charShift] = range[i];
}
-}
-@safe unittest
-{
- int[] range = null;
- reverse(range);
- range = [ 1 ];
- reverse(range);
- assert(range == [1]);
- range = [1, 2];
- reverse(range);
- assert(range == [2, 1]);
- range = [1, 2, 3];
- reverse(range);
- assert(range == [3, 2, 1]);
+ return range[0 .. $ - charShift];
}
+// reverse
/**
-Reverses $(D r) in-place, where $(D r) is a narrow string (having
-elements of type $(D char) or $(D wchar)). UTF sequences consisting of
-multiple code units are preserved properly.
+Reverses `r` in-place. Performs `r.length / 2` evaluations of `swap`.
+UTF sequences consisting of multiple code units are preserved properly.
Params:
- s = a narrow string
+ r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
+ with either swappable elements, a random access range with a length member,
+ or a narrow string
-Bugs:
- When passing a sting with unicode modifiers on characters, such as $(D \u0301),
+Returns: `r`
+
+Note:
+ When passing a string with unicode modifiers on characters, such as `\u0301`,
this function will not properly keep the position of the modifier. For example,
- reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("dÌab") instead of
- $(D da\u0301b) ("dáb").
-*/
-void reverse(Char)(Char[] s)
-if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable))
-{
- import std.string : representation;
- import std.utf : stride;
+ reversing `ba\u0301d` ("bád") will result in d\u0301ab ("dÌab") instead of
+ `da\u0301b` ("dáb").
- auto r = representation(s);
- for (size_t i = 0; i < s.length; )
+See_Also: $(REF retro, std,range) for a lazy reverse without changing `r`
+*/
+Range reverse(Range)(Range r)
+if (isBidirectionalRange!Range &&
+ (hasSwappableElements!Range ||
+ (hasAssignableElements!Range && hasLength!Range && isRandomAccessRange!Range) ||
+ (isNarrowString!Range && isAssignable!(ElementType!Range))))
+{
+ static if (isRandomAccessRange!Range && hasLength!Range)
+ {
+ //swapAt is in fact the only way to swap non lvalue ranges
+ immutable last = r.length - 1;
+ immutable steps = r.length / 2;
+ for (size_t i = 0; i < steps; i++)
+ {
+ r.swapAt(i, last - i);
+ }
+ return r;
+ }
+ else static if (isNarrowString!Range && isAssignable!(ElementType!Range))
{
- immutable step = stride(s, i);
- if (step > 1)
+ import std.string : representation;
+ import std.utf : stride;
+
+ auto raw = representation(r);
+ for (size_t i = 0; i < r.length;)
{
- .reverse(r[i .. i + step]);
- i += step;
+ immutable step = stride(r, i);
+ if (step > 1)
+ {
+ .reverse(raw[i .. i + step]);
+ i += step;
+ }
+ else
+ {
+ ++i;
+ }
}
- else
+ reverse(raw);
+ return r;
+ }
+ else
+ {
+ while (!r.empty)
{
- ++i;
+ swap(r.front, r.back);
+ r.popFront();
+ if (r.empty) break;
+ r.popBack();
}
+ return r;
}
- reverse(r);
+}
+
+///
+@safe unittest
+{
+ int[] arr = [ 1, 2, 3 ];
+ assert(arr.reverse == [ 3, 2, 1 ]);
+}
+
+@safe unittest
+{
+ int[] range = null;
+ reverse(range);
+ range = [ 1 ];
+ reverse(range);
+ assert(range == [1]);
+ range = [1, 2];
+ reverse(range);
+ assert(range == [2, 1]);
+ range = [1, 2, 3];
+ assert(range.reverse == [3, 2, 1]);
}
///
@safe unittest
{
char[] arr = "hello\U00010143\u0100\U00010143".dup;
- reverse(arr);
- assert(arr == "\U00010143\u0100\U00010143olleh");
+ assert(arr.reverse == "\U00010143\u0100\U00010143olleh");
}
@safe unittest
@@ -2307,12 +2666,12 @@ if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable))
The strip group of functions allow stripping of either leading, trailing,
or both leading and trailing elements.
- The $(D stripLeft) function will strip the $(D front) of the range,
- the $(D stripRight) function will strip the $(D back) of the range,
- while the $(D strip) function will strip both the $(D front) and $(D back)
+ The `stripLeft` function will strip the `front` of the range,
+ the `stripRight` function will strip the `back` of the range,
+ while the `strip` function will strip both the `front` and `back`
of the range.
- Note that the $(D strip) and $(D stripRight) functions require the range to
+ Note that the `strip` and `stripRight` functions require the range to
be a $(LREF BidirectionalRange) range.
All of these functions come in two varieties: one takes a target element,
@@ -2445,18 +2804,18 @@ if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool))
// swap
/**
-Swaps $(D lhs) and $(D rhs). The instances $(D lhs) and $(D rhs) are moved in
-memory, without ever calling $(D opAssign), nor any other function. $(D T)
+Swaps `lhs` and `rhs`. The instances `lhs` and `rhs` are moved in
+memory, without ever calling `opAssign`, nor any other function. `T`
need not be assignable at all to be swapped.
-If $(D lhs) and $(D rhs) reference the same instance, then nothing is done.
+If `lhs` and `rhs` reference the same instance, then nothing is done.
-$(D lhs) and $(D rhs) must be mutable. If $(D T) is a struct or union, then
+`lhs` and `rhs` must be mutable. If `T` is a struct or union, then
its fields must also all be (recursively) mutable.
Params:
- lhs = Data to be swapped with $(D rhs).
- rhs = Data to be swapped with $(D lhs).
+ lhs = Data to be swapped with `rhs`.
+ rhs = Data to be swapped with `lhs`.
*/
void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc
if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs))))
@@ -2499,8 +2858,15 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs))))
return;
}
- // For non-struct types, suffice to do the classic swap
- auto tmp = lhs;
+ // For non-elaborate-assign types, suffice to do the classic swap
+ static if (__traits(hasCopyConstructor, T))
+ {
+ // don't invoke any elaborate constructors either
+ T tmp = void;
+ tmp = lhs;
+ }
+ else
+ auto tmp = lhs;
lhs = rhs;
rhs = tmp;
}
@@ -2585,9 +2951,9 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs))))
static assert(!__traits(compiles, swap(const1, const2)));
}
+// https://issues.dlang.org/show_bug.cgi?id=4789
@safe unittest
{
- //Bug# 4789
int[1] s = [1];
swap(s, s);
@@ -2625,23 +2991,24 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs))))
assert(s.i3 == 2);
}
+// https://issues.dlang.org/show_bug.cgi?id=11853
@safe unittest
{
- //11853
import std.traits : isAssignable;
alias T = Tuple!(int, double);
static assert(isAssignable!T);
}
+// https://issues.dlang.org/show_bug.cgi?id=12024
@safe unittest
{
- // 12024
import std.datetime;
SysTime a, b;
swap(a, b);
}
-@system unittest // 9975
+// https://issues.dlang.org/show_bug.cgi?id=9975
+@system unittest
{
import std.exception : doesPointTo, mayPointTo;
static struct S2
@@ -2686,6 +3053,31 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs))))
swap(b1, b2);
}
+// issue 20732
+@safe unittest
+{
+ static struct A
+ {
+ int x;
+ this(scope ref return const A other)
+ {
+ import std.stdio;
+ x = other.x;
+ // note, struct functions inside @safe functions infer ALL
+ // attributes, so the following 3 lines are meant to prevent this.
+ new int; // prevent @nogc inference
+ writeln("impure"); // prevent pure inference
+ throw new Exception(""); // prevent nothrow inference
+ }
+ }
+
+ A a1, a2;
+ swap(a1, a2);
+
+ A[1] a3, a4;
+ swap(a3, a4);
+}
+
/// ditto
void swap(T)(ref T lhs, ref T rhs)
if (is(typeof(lhs.proxySwap(rhs))))
@@ -2820,16 +3212,16 @@ if (isInputRange!R1 && isInputRange!R2)
// swapRanges
/**
-Swaps all elements of $(D r1) with successive elements in $(D r2).
-Returns a tuple containing the remainder portions of $(D r1) and $(D
+Swaps all elements of `r1` with successive elements in `r2`.
+Returns a tuple containing the remainder portions of `r1` and $(D
r2) that were not swapped (one of them will be empty). The ranges may
be of different types but must have the same element type and support
swapping.
Params:
- r1 = an $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ r1 = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
with swappable elements
- r2 = an $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ r2 = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
with swappable elements
Returns:
@@ -2860,7 +3252,7 @@ if (hasSwappableElements!InputRange1 && hasSwappableElements!InputRange2
}
/**
-Initializes each element of $(D range) with $(D value).
+Initializes each element of `range` with `value`.
Assumes that the elements of the range are uninitialized.
This is of interest for structs that
define copy constructors (for all other types, $(LREF fill) and
@@ -2868,7 +3260,7 @@ uninitializedFill are equivalent).
Params:
range = An
- $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
that exposes references to its elements and has assignable
elements
value = Assigned to each element of range
@@ -2885,7 +3277,7 @@ if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = val
alias T = ElementType!Range;
static if (hasElaborateAssign!T)
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
// Must construct stuff by the book
for (; !range.empty; range.popFront())
diff --git a/libphobos/src/std/algorithm/package.d b/libphobos/src/std/algorithm/package.d
index 4c9a72f71c9..6aacd513fc5 100644
--- a/libphobos/src/std/algorithm/package.d
+++ b/libphobos/src/std/algorithm/package.d
@@ -79,9 +79,12 @@ $(TR
$(SUBREF iteration, group)
$(SUBREF iteration, joiner)
$(SUBREF iteration, map)
+ $(SUBREF iteration, mean)
$(SUBREF iteration, permutations)
$(SUBREF iteration, reduce)
+ $(SUBREF iteration, splitWhen)
$(SUBREF iteration, splitter)
+ $(SUBREF iteration, substitute)
$(SUBREF iteration, sum)
$(SUBREF iteration, uniq)
)
@@ -152,12 +155,12 @@ Many functions in this package are parameterized with a $(GLOSSARY predicate).
The predicate may be any suitable callable type
(a function, a delegate, a $(GLOSSARY functor), or a lambda), or a
compile-time string. The string may consist of $(B any) legal D
-expression that uses the symbol $(D a) (for unary functions) or the
-symbols $(D a) and $(D b) (for binary functions). These names will NOT
+expression that uses the symbol `a` (for unary functions) or the
+symbols `a` and `b` (for binary functions). These names will NOT
interfere with other homonym symbols in user code because they are
evaluated in a different context. The default for all binary
-comparison predicates is $(D "a == b") for unordered operations and
-$(D "a < b") for ordered operations.
+comparison predicates is `"a == b"` for unordered operations and
+`"a < b"` for ordered operations.
Example:
@@ -184,7 +187,7 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.com, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/_algorithm/package.d)
+Source: $(PHOBOSSRC std/algorithm/package.d)
*/
module std.algorithm;
diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d
index 09073f6d1aa..d6d02bad3bb 100644
--- a/libphobos/src/std/algorithm/searching.d
+++ b/libphobos/src/std/algorithm/searching.d
@@ -1,64 +1,64 @@
// Written in the D programming language.
/**
This is a submodule of $(MREF std, algorithm).
-It contains generic _searching algorithms.
+It contains generic searching algorithms.
$(SCRIPT inhibitQuickIndex = 1;)
$(BOOKTABLE Cheat Sheet,
$(TR $(TH Function Name) $(TH Description))
$(T2 all,
- $(D all!"a > 0"([1, 2, 3, 4])) returns $(D true) because all elements
+ `all!"a > 0"([1, 2, 3, 4])` returns `true` because all elements
are positive)
$(T2 any,
- $(D any!"a > 0"([1, 2, -3, -4])) returns $(D true) because at least one
+ `any!"a > 0"([1, 2, -3, -4])` returns `true` because at least one
element is positive)
$(T2 balancedParens,
- $(D balancedParens("((1 + 1) / 2)")) returns $(D true) because the
+ `balancedParens("((1 + 1) / 2)")` returns `true` because the
string has balanced parentheses.)
$(T2 boyerMooreFinder,
- $(D find("hello world", boyerMooreFinder("or"))) returns $(D "orld")
+ `find("hello world", boyerMooreFinder("or"))` returns `"orld"`
using the $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm,
Boyer-Moore _algorithm).)
$(T2 canFind,
- $(D canFind("hello world", "or")) returns $(D true).)
+ `canFind("hello world", "or")` returns `true`.)
$(T2 count,
Counts elements that are equal to a specified value or satisfy a
- predicate. $(D count([1, 2, 1], 1)) returns $(D 2) and
- $(D count!"a < 0"([1, -3, 0])) returns $(D 1).)
+ predicate. `count([1, 2, 1], 1)` returns `2` and
+ `count!"a < 0"([1, -3, 0])` returns `1`.)
$(T2 countUntil,
- $(D countUntil(a, b)) returns the number of steps taken in $(D a) to
- reach $(D b); for example, $(D countUntil("hello!", "o")) returns
- $(D 4).)
+ `countUntil(a, b)` returns the number of steps taken in `a` to
+ reach `b`; for example, `countUntil("hello!", "o")` returns
+ `4`.)
$(T2 commonPrefix,
- $(D commonPrefix("parakeet", "parachute")) returns $(D "para").)
+ `commonPrefix("parakeet", "parachute")` returns `"para"`.)
$(T2 endsWith,
- $(D endsWith("rocks", "ks")) returns $(D true).)
+ `endsWith("rocks", "ks")` returns `true`.)
$(T2 find,
- $(D find("hello world", "or")) returns $(D "orld") using linear search.
- (For binary search refer to $(REF sortedRange, std,range).))
+ `find("hello world", "or")` returns `"orld"` using linear search.
+ (For binary search refer to $(REF SortedRange, std,range).))
$(T2 findAdjacent,
- $(D findAdjacent([1, 2, 3, 3, 4])) returns the subrange starting with
- two equal adjacent elements, i.e. $(D [3, 3, 4]).)
+ `findAdjacent([1, 2, 3, 3, 4])` returns the subrange starting with
+ two equal adjacent elements, i.e. `[3, 3, 4]`.)
$(T2 findAmong,
- $(D findAmong("abcd", "qcx")) returns $(D "cd") because $(D 'c') is
- among $(D "qcx").)
+ `findAmong("abcd", "qcx")` returns `"cd"` because `'c'` is
+ among `"qcx"`.)
$(T2 findSkip,
- If $(D a = "abcde"), then $(D findSkip(a, "x")) returns $(D false) and
- leaves $(D a) unchanged, whereas $(D findSkip(a, "c")) advances $(D a)
- to $(D "de") and returns $(D true).)
+ If `a = "abcde"`, then `findSkip(a, "x")` returns `false` and
+ leaves `a` unchanged, whereas `findSkip(a, "c")` advances `a`
+ to `"de"` and returns `true`.)
$(T2 findSplit,
- $(D findSplit("abcdefg", "de")) returns the three ranges $(D "abc"),
- $(D "de"), and $(D "fg").)
+ `findSplit("abcdefg", "de")` returns a tuple of three ranges `"abc"`,
+ `"de"`, and `"fg"`.)
$(T2 findSplitAfter,
- $(D findSplitAfter("abcdefg", "de")) returns the two ranges
- $(D "abcde") and $(D "fg").)
+`findSplitAfter("abcdefg", "de")` returns a tuple of two ranges `"abcde"`
+ and `"fg"`.)
$(T2 findSplitBefore,
- $(D findSplitBefore("abcdefg", "de")) returns the two ranges $(D "abc")
- and $(D "defg").)
+ `findSplitBefore("abcdefg", "de")` returns a tuple of two ranges `"abc"`
+ and `"defg"`.)
$(T2 minCount,
- $(D minCount([2, 1, 1, 4, 1])) returns $(D tuple(1, 3)).)
+ `minCount([2, 1, 1, 4, 1])` returns `tuple(1, 3)`.)
$(T2 maxCount,
- $(D maxCount([2, 4, 1, 4, 1])) returns $(D tuple(4, 2)).)
+ `maxCount([2, 4, 1, 4, 1])` returns `tuple(4, 2)`.)
$(T2 minElement,
Selects the minimal element of a range.
`minElement([3, 4, 1, 2])` returns `1`.)
@@ -67,27 +67,24 @@ $(T2 maxElement,
`maxElement([3, 4, 1, 2])` returns `4`.)
$(T2 minIndex,
Index of the minimal element of a range.
- `minElement([3, 4, 1, 2])` returns `2`.)
+ `minIndex([3, 4, 1, 2])` returns `2`.)
$(T2 maxIndex,
Index of the maximal element of a range.
- `maxElement([3, 4, 1, 2])` returns `1`.)
+ `maxIndex([3, 4, 1, 2])` returns `1`.)
$(T2 minPos,
- $(D minPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [1, 3, 4, 1]),
+ `minPos([2, 3, 1, 3, 4, 1])` returns the subrange `[1, 3, 4, 1]`,
i.e., positions the range at the first occurrence of its minimal
element.)
$(T2 maxPos,
- $(D maxPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [4, 1]),
+ `maxPos([2, 3, 1, 3, 4, 1])` returns the subrange `[4, 1]`,
i.e., positions the range at the first occurrence of its maximal
element.)
-$(T2 mismatch,
- $(D mismatch("parakeet", "parachute")) returns the two ranges
- $(D "keet") and $(D "chute").)
$(T2 skipOver,
- Assume $(D a = "blah"). Then $(D skipOver(a, "bi")) leaves $(D a)
- unchanged and returns $(D false), whereas $(D skipOver(a, "bl"))
- advances $(D a) to refer to $(D "ah") and returns $(D true).)
+ Assume `a = "blah"`. Then `skipOver(a, "bi")` leaves `a`
+ unchanged and returns `false`, whereas `skipOver(a, "bl")`
+ advances `a` to refer to `"ah"` and returns `true`.)
$(T2 startsWith,
- $(D startsWith("hello, world", "hello")) returns $(D true).)
+ `startsWith("hello, world", "hello")` returns `true`.)
$(T2 until,
Lazily iterates a range until a specific value is found.)
)
@@ -98,33 +95,34 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.com, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/algorithm/_searching.d)
+Source: $(PHOBOSSRC std/algorithm/searching.d)
Macros:
T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
*/
module std.algorithm.searching;
-// FIXME
-import std.functional; // : unaryFun, binaryFun;
+import std.functional : unaryFun, binaryFun;
+import std.meta : allSatisfy;
import std.range.primitives;
import std.traits;
-// FIXME
-import std.typecons; // : Tuple, Flag, Yes, No;
+import std.typecons : Tuple, Flag, Yes, No, tuple;
/++
-Checks if $(I _all) of the elements verify $(D pred).
+Checks if $(I _all) of the elements satisfy `pred`.
+/
template all(alias pred = "a")
{
/++
- Returns $(D true) if and only if $(I _all) values $(D v) found in the
- input _range $(D range) satisfy the predicate $(D pred).
- Performs (at most) $(BIGOH range.length) evaluations of $(D pred).
+ Returns `true` if and only if the input range `range` is empty
+ or $(I _all) values found in `range` satisfy the predicate `pred`.
+ Performs (at most) $(BIGOH range.length) evaluations of `pred`.
+/
bool all(Range)(Range range)
- if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))))
+ if (isInputRange!Range)
{
+ static assert(is(typeof(unaryFun!pred(range.front))),
+ "`" ~ pred.stringof[1..$-1] ~ "` isn't a unary predicate function for range.front");
import std.functional : not;
return find!(not!(unaryFun!pred))(range).empty;
@@ -139,7 +137,7 @@ template all(alias pred = "a")
}
/++
-$(D all) can also be used without a predicate, if its items can be
+`all` can also be used without a predicate, if its items can be
evaluated to true or false in a conditional statement. This can be a
convenient way to quickly evaluate that $(I _all) of the elements of a range
are true.
@@ -154,20 +152,22 @@ are true.
{
int x = 1;
assert(all!(a => a > x)([2, 3]));
+ assert(all!"a == 0x00c9"("\xc3\x89")); // Test that `all` auto-decodes.
}
/++
-Checks if $(I _any) of the elements verifies $(D pred).
-$(D !any) can be used to verify that $(I none) of the elements verify
-$(D pred).
+Checks if $(I _any) of the elements satisfies `pred`.
+`!any` can be used to verify that $(I none) of the elements satisfy
+`pred`.
This is sometimes called `exists` in other languages.
+/
template any(alias pred = "a")
{
/++
- Returns $(D true) if and only if $(I _any) value $(D v) found in the
- input _range $(D range) satisfies the predicate $(D pred).
- Performs (at most) $(BIGOH range.length) evaluations of $(D pred).
+ Returns `true` if and only if the input range `range` is non-empty
+ and $(I _any) value found in `range` satisfies the predicate
+ `pred`.
+ Performs (at most) $(BIGOH range.length) evaluations of `pred`.
+/
bool any(Range)(Range range)
if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))))
@@ -185,8 +185,8 @@ template any(alias pred = "a")
}
/++
-$(D any) can also be used without a predicate, if its items can be
-evaluated to true or false in a conditional statement. $(D !any) can be a
+`any` can also be used without a predicate, if its items can be
+evaluated to true or false in a conditional statement. `!any` can be a
convenient way to quickly test that $(I none) of the elements of a range
evaluate to true.
+/
@@ -208,14 +208,15 @@ evaluate to true.
{
auto a = [ 1, 2, 0, 4 ];
assert(any!"a == 2"(a));
+ assert(any!"a == 0x3000"("\xe3\x80\x80")); // Test that `any` auto-decodes.
}
// balancedParens
/**
-Checks whether $(D r) has "balanced parentheses", i.e. all instances
-of $(D lPar) are closed by corresponding instances of $(D rPar). The
-parameter $(D maxNestingLevel) controls the nesting level allowed. The
-most common uses are the default or $(D 0). In the latter case, no
+Checks whether `r` has "balanced parentheses", i.e. all instances
+of `lPar` are closed by corresponding instances of `rPar`. The
+parameter `maxNestingLevel` controls the nesting level allowed. The
+most common uses are the default or `0`. In the latter case, no
nesting is allowed.
Params:
@@ -233,14 +234,25 @@ bool balancedParens(Range, E)(Range r, E lPar, E rPar,
if (isInputRange!(Range) && is(typeof(r.front == lPar)))
{
size_t count;
- for (; !r.empty; r.popFront())
+
+ static if (is(immutable ElementEncodingType!Range == immutable E) && isNarrowString!Range)
+ {
+ import std.utf : byCodeUnit;
+ auto rn = r.byCodeUnit;
+ }
+ else
+ {
+ alias rn = r;
+ }
+
+ for (; !rn.empty; rn.popFront())
{
- if (r.front == lPar)
+ if (rn.front == lPar)
{
if (count > maxNestingLevel) return false;
++count;
}
- else if (r.front == rPar)
+ else if (rn.front == rPar)
{
if (!count) return false;
--count;
@@ -250,7 +262,7 @@ if (isInputRange!(Range) && is(typeof(r.front == lPar)))
}
///
-@safe unittest
+@safe pure unittest
{
auto s = "1 + (2 * (3 + 1 / 2)";
assert(!balancedParens(s, '(', ')'));
@@ -260,21 +272,23 @@ if (isInputRange!(Range) && is(typeof(r.front == lPar)))
assert(!balancedParens(s, '(', ')', 0));
s = "1 + (2 * 3 + 1) / (2 - 5)";
assert(balancedParens(s, '(', ')', 0));
+ s = "f(x) = ⌈x⌉";
+ assert(balancedParens(s, '⌈', '⌉'));
}
/**
- * Sets up Boyer-Moore matching for use with $(D find) below.
+ * Sets up Boyer-Moore matching for use with `find` below.
* By default, elements are compared for equality.
*
- * $(D BoyerMooreFinder) allocates GC memory.
+ * `BoyerMooreFinder` allocates GC memory.
*
* Params:
* pred = Predicate used to compare elements.
* needle = A random-access range with length and slicing.
*
* Returns:
- * An instance of $(D BoyerMooreFinder) that can be used with $(D find()) to
- * invoke the Boyer-Moore matching algorithm for finding of $(D needle) in a
+ * An instance of `BoyerMooreFinder` that can be used with `find()` to
+ * invoke the Boyer-Moore matching algorithm for finding of `needle` in a
* given haystack.
*/
struct BoyerMooreFinder(alias pred, Range)
@@ -284,7 +298,7 @@ private:
ptrdiff_t[ElementType!(Range)] occ; // GC allocated
Range needle;
- ptrdiff_t occurrence(ElementType!(Range) c)
+ ptrdiff_t occurrence(ElementType!(Range) c) scope
{
auto p = c in occ;
return p ? *p : -1;
@@ -347,7 +361,7 @@ public:
}
///
- Range beFound(Range haystack)
+ Range beFound(Range haystack) scope
{
import std.algorithm.comparison : max;
@@ -363,7 +377,7 @@ public:
if (npos == 0) return haystack[hpos .. $];
--npos;
}
- hpos += max(skip[npos], cast(sizediff_t) npos - occurrence(haystack[npos+hpos]));
+ hpos += max(skip[npos], cast(ptrdiff_t) npos - occurrence(haystack[npos+hpos]));
}
return haystack[$ .. $];
}
@@ -407,7 +421,7 @@ Returns the common prefix of two ranges.
Params:
pred = The predicate to use in comparing elements for commonality. Defaults
- to equality $(D "a == b").
+ to equality `"a == b"`.
r1 = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of
elements.
@@ -416,9 +430,9 @@ Params:
elements.
Returns:
-A slice of $(D r1) which contains the characters that both ranges start with,
+A slice of `r1` which contains the characters that both ranges start with,
if the first argument is a string; otherwise, the same as the result of
-$(D takeExactly(r1, n)), where $(D n) is the number of elements in the common
+`takeExactly(r1, n)`, where `n` is the number of elements in the common
prefix of both ranges.
See_Also:
@@ -542,12 +556,12 @@ if (isNarrowString!R1 && isNarrowString!R2)
assert(commonPrefix(cast(int[]) null, [1, 2, 3]).empty);
assert(commonPrefix(cast(int[]) null, cast(int[]) null).empty);
- foreach (S; AliasSeq!(char[], const(char)[], string,
+ static foreach (S; AliasSeq!(char[], const(char)[], string,
wchar[], const(wchar)[], wstring,
dchar[], const(dchar)[], dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
assert(commonPrefix(to!S(""), to!T("")).empty);
assert(commonPrefix(to!S(""), to!T("hello")).empty);
assert(commonPrefix(to!S("hello"), to!T("")).empty);
@@ -557,7 +571,7 @@ if (isNarrowString!R1 && isNarrowString!R2)
assert(commonPrefix(to!S("hello, world"), to!T("hello, ")) == to!S("hello, "));
assert(commonPrefix(to!S("hello, world"), to!T("hello, world")) == to!S("hello, world"));
- //Bug# 8890
+ // https://issues.dlang.org/show_bug.cgi?id=8890
assert(commonPrefix(to!S("Пиво"), to!T("Пони"))== to!S("П"));
assert(commonPrefix(to!S("Пони"), to!T("Пиво"))== to!S("П"));
assert(commonPrefix(to!S("Пиво"), to!T("Пиво"))== to!S("Пиво"));
@@ -567,7 +581,7 @@ if (isNarrowString!R1 && isNarrowString!R2)
to!T("\U0010FFFF\U0010FFFB\U0010FFFE")) == to!S("\U0010FFFF\U0010FFFB"));
assert(commonPrefix!"a != b"(to!S("Пиво"), to!T("онво")) == to!S("Пи"));
assert(commonPrefix!"a != b"(to!S("онво"), to!T("Пиво")) == to!S("он"));
- }();
+ }
static assert(is(typeof(commonPrefix(to!S("Пиво"), filter!"true"("Пони"))) == S));
assert(equal(commonPrefix(to!S("Пиво"), filter!"true"("Пони")), to!S("П")));
@@ -591,26 +605,26 @@ if (isNarrowString!R1 && isNarrowString!R2)
// count
/**
-The first version counts the number of elements $(D x) in $(D r) for
-which $(D pred(x, value)) is $(D true). $(D pred) defaults to
-equality. Performs $(BIGOH haystack.length) evaluations of $(D pred).
+The first version counts the number of elements `x` in `r` for
+which `pred(x, value)` is `true`. `pred` defaults to
+equality. Performs $(BIGOH haystack.length) evaluations of `pred`.
-The second version returns the number of times $(D needle) occurs in
-$(D haystack). Throws an exception if $(D needle.empty), as the _count
+The second version returns the number of times `needle` occurs in
+`haystack`. Throws an exception if `needle.empty`, as the _count
of the empty range in any range would be infinite. Overlapped counts
-are not considered, for example $(D count("aaa", "aa")) is $(D 1), not
-$(D 2).
+are not considered, for example `count("aaa", "aa")` is `1`, not
+`2`.
-The third version counts the elements for which $(D pred(x)) is $(D
-true). Performs $(BIGOH haystack.length) evaluations of $(D pred).
+The third version counts the elements for which `pred(x)` is $(D
+true). Performs $(BIGOH haystack.length) evaluations of `pred`.
The fourth version counts the number of elements in a range. It is
an optimization for the third version: if the given range has the
`length` property the count is returned right away, otherwise
performs $(BIGOH haystack.length) to walk the range.
-Note: Regardless of the overload, $(D count) will not accept
-infinite ranges for $(D haystack).
+Note: Regardless of the overload, `count` will not accept
+infinite ranges for `haystack`.
Params:
pred = The predicate to evaluate.
@@ -723,7 +737,7 @@ if (isInputRange!R && !isInfinite!R)
assert(count("日本語") == 3);
}
-// Issue 11253
+// https://issues.dlang.org/show_bug.cgi?id=11253
@safe nothrow unittest
{
assert([1, 2, 3].count([2, 3]) == 1);
@@ -732,7 +746,7 @@ if (isInputRange!R && !isInfinite!R)
/++
Counts elements in the given
$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
- until the given predicate is true for one of the given $(D needles).
+ until the given predicate is true for one of the given `needles`.
Params:
pred = The predicate for determining when to stop counting.
@@ -742,13 +756,14 @@ if (isInputRange!R && !isInfinite!R)
needles = Either a single element, or a
$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
of elements, to be evaluated in turn against each
- element in $(D haystack) under the given predicate.
+ element in `haystack` under the given predicate.
Returns: The number of elements which must be popped from the front of
- $(D haystack) before reaching an element for which
- $(D startsWith!pred(haystack, needles)) is $(D true). If
- $(D startsWith!pred(haystack, needles)) is not $(D true) for any element in
- $(D haystack), then $(D -1) is returned.
+ `haystack` before reaching an element for which
+ `startsWith!pred(haystack, needles)` is `true`. If
+ `startsWith!pred(haystack, needles)` is not `true` for any element in
+ `haystack`, then `-1` is returned. If only `pred` is provided,
+ `pred(haystack)` is tested for each element.
See_Also: $(REF indexOf, std,string)
+/
@@ -756,9 +771,7 @@ ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
if (isForwardRange!R
&& Rs.length > 0
&& isForwardRange!(Rs[0]) == isInputRange!(Rs[0])
- && is(typeof(startsWith!pred(haystack, needles[0])))
- && (Rs.length == 1
- || is(typeof(countUntil!pred(haystack, needles[1 .. $])))))
+ && allSatisfy!(canTestStartsWith!(pred, R), Rs))
{
typeof(return) result;
@@ -834,8 +847,10 @@ if (isForwardRange!R
}
}
- //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement"
- static if (isInfinite!R) assert(0);
+ // Because of https://issues.dlang.org/show_bug.cgi?id=8804
+ // Avoids both "unreachable code" or "no return statement"
+ static if (isInfinite!R) assert(false, R.stringof ~ " must not be an"
+ ~ " infinite range");
else return -1;
}
@@ -898,18 +913,7 @@ if (isInputRange!R &&
assert(countUntil("hello world", "world", 'l') == 2);
}
-/++
- Similar to the previous overload of $(D countUntil), except that this one
- evaluates only the predicate $(D pred).
-
- Params:
- pred = Predicate to when to stop counting.
- haystack = An
- $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
- elements to be counted.
- Returns: The number of elements which must be popped from $(D haystack)
- before $(D pred(haystack.front)) is $(D true).
- +/
+/// ditto
ptrdiff_t countUntil(alias pred, R)(R haystack)
if (isInputRange!R &&
is(typeof(unaryFun!pred(haystack.front)) : bool))
@@ -948,8 +952,10 @@ if (isInputRange!R &&
}
}
- //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement"
- static if (isInfinite!R) assert(0);
+ // Because of https://issues.dlang.org/show_bug.cgi?id=8804
+ // Avoids both "unreachable code" or "no return statement"
+ static if (isInfinite!R) assert(false, R.stringof ~ " must not be an"
+ ~ " inifite range");
else return -1;
}
@@ -995,7 +1001,7 @@ if (isInputRange!R &&
/**
Checks if the given range ends with (one of) the given needle(s).
-The reciprocal of $(D startsWith).
+The reciprocal of `startsWith`.
Params:
pred = The predicate to use for comparing elements between the range and
@@ -1013,16 +1019,15 @@ Params:
Returns:
0 if the needle(s) do not occur at the end of the given range;
otherwise the position of the matching needle, that is, 1 if the range ends
-with $(D withOneOfThese[0]), 2 if it ends with $(D withOneOfThese[1]), and so
+with `withOneOfThese[0]`, 2 if it ends with `withOneOfThese[1]`, and so
on.
-In the case when no needle parameters are given, return $(D true) iff back of
-$(D doesThisStart) fulfils predicate $(D pred).
+In the case when no needle parameters are given, return `true` iff back of
+`doesThisStart` fulfils predicate `pred`.
*/
uint endsWith(alias pred = "a == b", Range, Needles...)(Range doesThisEnd, Needles withOneOfThese)
if (isBidirectionalRange!Range && Needles.length > 1 &&
- is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[0])) : bool) &&
- is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[1 .. $])) : uint))
+ allSatisfy!(canTestStartsWith!(pred, Range), Needles))
{
alias haystack = doesThisEnd;
alias needles = withOneOfThese;
@@ -1100,7 +1105,7 @@ if (isBidirectionalRange!R1 &&
enum isDefaultPred = false;
static if (isDefaultPred && isArray!R1 && isArray!R2 &&
- is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
+ is(immutable ElementEncodingType!R1 == immutable ElementEncodingType!R2))
{
if (haystack.length < needle.length) return false;
@@ -1121,16 +1126,27 @@ if (isBidirectionalRange!R &&
if (doesThisEnd.empty)
return false;
+ static if (is(typeof(pred) : string))
+ enum isDefaultPred = pred == "a == b";
+ else
+ enum isDefaultPred = false;
+
alias predFunc = binaryFun!pred;
// auto-decoding special case
static if (isNarrowString!R)
{
+ // statically determine decoding is unnecessary to evaluate pred
+ static if (isDefaultPred && isSomeChar!E && E.sizeof <= ElementEncodingType!R.sizeof)
+ return doesThisEnd[$ - 1] == withThis;
// specialize for ASCII as to not change previous behavior
- if (withThis <= 0x7F)
- return predFunc(doesThisEnd[$ - 1], withThis);
else
- return predFunc(doesThisEnd.back, withThis);
+ {
+ if (withThis <= 0x7F)
+ return predFunc(doesThisEnd[$ - 1], withThis);
+ else
+ return predFunc(doesThisEnd.back, withThis);
+ }
}
else
{
@@ -1180,16 +1196,17 @@ if (isInputRange!R &&
import std.conv : to;
import std.meta : AliasSeq;
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ (){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
assert(!endsWith(to!S("abc"), 'a'));
assert(endsWith(to!S("abc"), 'a', 'c') == 2);
assert(!endsWith(to!S("abc"), 'x', 'n', 'b'));
assert(endsWith(to!S("abc"), 'x', 'n', 'c') == 3);
assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2);
- foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
//Lots of strings
assert(endsWith(to!S("abc"), to!T("")));
assert(!endsWith(to!S("abc"), to!T("a")));
@@ -1219,11 +1236,11 @@ if (isInputRange!R &&
assert(endsWith(to!S("a"), T.init, "") == 1);
assert(endsWith(to!S("a"), T.init, 'a') == 1);
assert(endsWith(to!S("a"), 'a', T.init) == 2);
- }();
- }
+ }
+ }();
- foreach (T; AliasSeq!(int, short))
- {
+ static foreach (T; AliasSeq!(int, short))
+ {{
immutable arr = cast(T[])[0, 1, 2, 3, 4, 5];
//RA range
@@ -1254,7 +1271,30 @@ if (isInputRange!R &&
//Non-default pred
assert(endsWith!("a%10 == b%10")(arr, [14, 15]));
assert(!endsWith!("a%10 == b%10")(arr, [15, 14]));
- }
+ }}
+}
+
+@safe pure unittest
+{
+ //example from issue 19727
+ import std.path : asRelativePath;
+ string[] ext = ["abc", "def", "ghi"];
+ string path = "/foo/file.def";
+ assert(ext.any!(e => path.asRelativePath("/foo").endsWith(e)) == true);
+ assert(ext.any!(e => path.asRelativePath("/foo").startsWith(e)) == false);
+}
+
+private enum bool hasConstEmptyMember(T) = is(typeof(((const T* a) => (*a).empty)(null)) : bool);
+
+// Rebindable doesn't work with structs
+// see: https://github.com/dlang/phobos/pull/6136
+private template RebindableOrUnqual(T)
+{
+ import std.typecons : Rebindable;
+ static if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
+ alias RebindableOrUnqual = Rebindable!T;
+ else
+ alias RebindableOrUnqual = Unqual!T;
}
/**
@@ -1278,10 +1318,10 @@ in
{
assert(!r.empty, "r is an empty range");
}
-body
+do
{
alias Element = ElementType!Range;
- Unqual!Element seed = r.front;
+ RebindableOrUnqual!Element seed = r.front;
r.popFront();
return extremum!(map, selector)(r, seed);
}
@@ -1298,86 +1338,87 @@ if (isInputRange!Range && !isInfinite!Range &&
alias Element = ElementType!Range;
alias CommonElement = CommonType!(Element, RangeElementType);
- Unqual!CommonElement extremeElement = seedElement;
+ RebindableOrUnqual!CommonElement extremeElement = seedElement;
- alias MapType = Unqual!(typeof(mapFun(CommonElement.init)));
- MapType extremeElementMapped = mapFun(extremeElement);
- // direct access via a random access range is faster
- static if (isRandomAccessRange!Range)
+ // if we only have one statement in the loop, it can be optimized a lot better
+ static if (__traits(isSame, map, a => a))
{
- foreach (const i; 0 .. r.length)
+
+ // direct access via a random access range is faster
+ static if (isRandomAccessRange!Range)
+ {
+ foreach (const i; 0 .. r.length)
+ {
+ if (selectorFun(r[i], extremeElement))
+ {
+ extremeElement = r[i];
+ }
+ }
+ }
+ else
{
- MapType mapElement = mapFun(r[i]);
- if (selectorFun(mapElement, extremeElementMapped))
+ while (!r.empty)
{
- extremeElement = r[i];
- extremeElementMapped = mapElement;
+ if (selectorFun(r.front, extremeElement))
+ {
+ extremeElement = r.front;
+ }
+ r.popFront();
}
}
}
else
{
- while (!r.empty)
+ alias MapType = Unqual!(typeof(mapFun(CommonElement.init)));
+ MapType extremeElementMapped = mapFun(extremeElement);
+
+ // direct access via a random access range is faster
+ static if (isRandomAccessRange!Range)
{
- MapType mapElement = mapFun(r.front);
- if (selectorFun(mapElement, extremeElementMapped))
+ foreach (const i; 0 .. r.length)
{
- extremeElement = r.front;
- extremeElementMapped = mapElement;
+ MapType mapElement = mapFun(r[i]);
+ if (selectorFun(mapElement, extremeElementMapped))
+ {
+ extremeElement = r[i];
+ extremeElementMapped = mapElement;
+ }
+ }
+ }
+ else
+ {
+ while (!r.empty)
+ {
+ MapType mapElement = mapFun(r.front);
+ if (selectorFun(mapElement, extremeElementMapped))
+ {
+ extremeElement = r.front;
+ extremeElementMapped = mapElement;
+ }
+ r.popFront();
}
- r.popFront();
}
}
return extremeElement;
}
private auto extremum(alias selector = "a < b", Range)(Range r)
- if (isInputRange!Range && !isInfinite!Range &&
- !is(typeof(unaryFun!selector(ElementType!(Range).init))))
+if (isInputRange!Range && !isInfinite!Range &&
+ !is(typeof(unaryFun!selector(ElementType!(Range).init))))
{
- alias Element = ElementType!Range;
- Unqual!Element seed = r.front;
- r.popFront();
- return extremum!selector(r, seed);
+ return extremum!(a => a, selector)(r);
}
// if we only have one statement in the loop it can be optimized a lot better
private auto extremum(alias selector = "a < b", Range,
RangeElementType = ElementType!Range)
(Range r, RangeElementType seedElement)
- if (isInputRange!Range && !isInfinite!Range &&
- !is(CommonType!(ElementType!Range, RangeElementType) == void) &&
- !is(typeof(unaryFun!selector(ElementType!(Range).init))))
+if (isInputRange!Range && !isInfinite!Range &&
+ !is(CommonType!(ElementType!Range, RangeElementType) == void) &&
+ !is(typeof(unaryFun!selector(ElementType!(Range).init))))
{
- alias Element = ElementType!Range;
- alias CommonElement = CommonType!(Element, RangeElementType);
- Unqual!CommonElement extremeElement = seedElement;
- alias selectorFun = binaryFun!selector;
-
- // direct access via a random access range is faster
- static if (isRandomAccessRange!Range)
- {
- foreach (const i; 0 .. r.length)
- {
- if (selectorFun(r[i], extremeElement))
- {
- extremeElement = r[i];
- }
- }
- }
- else
- {
- while (!r.empty)
- {
- if (selectorFun(r.front, extremeElement))
- {
- extremeElement = r.front;
- }
- r.popFront();
- }
- }
- return extremeElement;
+ return extremum!(a => a, selector)(r, seedElement);
}
@safe pure unittest
@@ -1391,7 +1432,7 @@ private auto extremum(alias selector = "a < b", Range,
assert([[0, 4], [1, 2]].extremum!("a[1]", "a > b") == [0, 4]);
// use a custom comparator
- import std.math : cmp;
+ import std.math.operations : cmp;
assert([-2., 0, 5].extremum!cmp == 5.0);
assert([-2., 0, 2].extremum!`cmp(a, b) < 0` == -2.0);
@@ -1455,45 +1496,80 @@ private auto extremum(alias selector = "a < b", Range,
assert(arr2d.extremum!"a[1]" == arr2d[1]);
}
+// https://issues.dlang.org/show_bug.cgi?id=17982
+@safe unittest
+{
+ class B
+ {
+ int val;
+ this(int val){ this.val = val; }
+ }
+
+ const(B) doStuff(const(B)[] v)
+ {
+ return v.extremum!"a.val";
+ }
+ assert(doStuff([new B(1), new B(0), new B(2)]).val == 0);
+
+ const(B)[] arr = [new B(0), new B(1)];
+ // can't compare directly - https://issues.dlang.org/show_bug.cgi?id=1824
+ assert(arr.extremum!"a.val".val == 0);
+}
+
// find
/**
-Finds an individual element in an input range. Elements of $(D
-haystack) are compared with $(D needle) by using predicate $(D
-pred). Performs $(BIGOH walkLength(haystack)) evaluations of $(D
-pred).
+Finds an individual element in an $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
+Elements of `haystack` are compared with `needle` by using predicate
+`pred` with `pred(haystack.front, needle)`.
+`find` performs $(BIGOH walkLength(haystack)) evaluations of `pred`.
-To _find the last occurrence of $(D needle) in $(D haystack), call $(D
-find(retro(haystack), needle)). See $(REF retro, std,range).
+The predicate is passed to $(REF binaryFun, std, functional), and can either accept a
+string, or any callable that can be executed via `pred(element, element)`.
-Params:
+To _find the last occurrence of `needle` in a
+$(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives) `haystack`,
+call `find(retro(haystack), needle)`. See $(REF retro, std,range).
-pred = The predicate for comparing each element with the needle, defaulting to
-$(D "a == b").
-The negated predicate $(D "a != b") can be used to search instead for the first
-element $(I not) matching the needle.
+If no `needle` is provided, `pred(haystack.front)` will be evaluated on each
+element of the input range.
-haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
-searched in.
+If `input` is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives),
+`needle` can be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) too.
+In this case `startsWith!pred(haystack, needle)` is evaluated on each evaluation.
-needle = The element searched for.
+Note:
+ `find` behaves similar to `dropWhile` in other languages.
-Constraints:
+Complexity:
+ `find` performs $(BIGOH walkLength(haystack)) evaluations of `pred`.
+ There are specializations that improve performance by taking
+ advantage of $(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives)
+ or $(REF_ALTTEXT random access, isRandomAccess, std,range,primitives)
+ ranges (where possible).
-$(D isInputRange!InputRange && is(typeof(binaryFun!pred(haystack.front, needle)
-: bool)))
+Params:
+
+ pred = The predicate for comparing each element with the needle, defaulting to equality `"a == b"`.
+ The negated predicate `"a != b"` can be used to search instead for the first
+ element $(I not) matching the needle.
+
+ haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ searched in.
+
+ needle = The element searched for.
Returns:
-$(D haystack) advanced such that the front element is the one searched for;
-that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no
-such position exists, returns an empty $(D haystack).
+ `haystack` advanced such that the front element is the one searched for;
+ that is, until `binaryFun!pred(haystack.front, needle)` is `true`. If no
+ such position exists, returns an empty `haystack`.
-See_Also:
- $(HTTP sgi.com/tech/stl/_find.html, STL's _find)
- */
+See_ALso: $(LREF findAdjacent), $(LREF findAmong), $(LREF findSkip), $(LREF findSplit), $(LREF startsWith)
+*/
InputRange find(alias pred = "a == b", InputRange, Element)(InputRange haystack, scope Element needle)
if (isInputRange!InputRange &&
- is (typeof(binaryFun!pred(haystack.front, needle)) : bool))
+ is (typeof(binaryFun!pred(haystack.front, needle)) : bool) &&
+ !is (typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
{
alias R = InputRange;
alias E = Element;
@@ -1508,7 +1584,7 @@ if (isInputRange!InputRange &&
// If the haystack is a SortedRange we can use binary search to find the needle.
// Works only for the default find predicate and any SortedRange predicate.
- // 8829 enhancement
+ // https://issues.dlang.org/show_bug.cgi?id=8829
import std.range : SortedRange;
static if (is(InputRange : SortedRange!TT, TT) && isDefaultPred)
{
@@ -1535,7 +1611,8 @@ if (isInputRange!InputRange &&
{
if (!__ctfe && canSearchInCodeUnits!char(needle))
{
- static R trustedMemchr(ref R haystack, ref E needle) @trusted nothrow pure
+ static inout(R) trustedMemchr(ref return scope inout(R) haystack,
+ ref const scope E needle) @trusted nothrow pure
{
import core.stdc.string : memchr;
auto ptr = memchr(haystack.ptr, needle, haystack.length);
@@ -1562,7 +1639,7 @@ if (isInputRange!InputRange &&
}
}
- //Previous conditonal optimizations did not succeed. Fallback to
+ //Previous conditional optimizations did not succeed. Fallback to
//unconditional implementations
static if (isDefaultPred)
{
@@ -1594,12 +1671,12 @@ if (isInputRange!InputRange &&
}
else static if (isArray!R)
{
- //10403 optimization
+ // https://issues.dlang.org/show_bug.cgi?id=10403 optimization
static if (isDefaultPred && isIntegral!EType && EType.sizeof == 1 && isIntegralNeedle)
{
import std.algorithm.comparison : max, min;
- R findHelper(ref R haystack, ref E needle) @trusted nothrow pure
+ R findHelper(return scope ref R haystack, ref E needle) @trusted nothrow pure
{
import core.stdc.string : memchr;
@@ -1642,38 +1719,29 @@ if (isInputRange!InputRange &&
///
@safe unittest
{
- import std.algorithm.comparison : equal;
- import std.container : SList;
- import std.range;
- import std.range.primitives : empty;
+ import std.range.primitives;
- auto arr = assumeSorted!"a < b"([1, 2, 4, 4, 4, 4, 5, 6, 9]);
- assert(find(arr, 4) == assumeSorted!"a < b"([4, 4, 4, 4, 5, 6, 9]));
- assert(find(arr, 1) == arr);
- assert(find(arr, 9) == assumeSorted!"a < b"([9]));
- assert(find!"a > b"(arr, 4) == assumeSorted!"a < b"([5, 6, 9]));
- assert(find!"a < b"(arr, 4) == arr);
- assert(find(arr, 0).empty());
- assert(find(arr, 10).empty());
- assert(find(arr, 8).empty());
-
- auto r = assumeSorted!"a > b"([10, 7, 3, 1, 0, 0]);
- assert(find(r, 3) == assumeSorted!"a > b"([3, 1, 0, 0]));
- assert(find!"a > b"(r, 8) == r);
- assert(find!"a < b"(r, 5) == assumeSorted!"a > b"([3, 1, 0, 0]));
+ auto arr = [1, 2, 4, 4, 4, 4, 5, 6, 9];
+ assert(arr.find(4) == [4, 4, 4, 4, 5, 6, 9]);
+ assert(arr.find(1) == arr);
+ assert(arr.find(9) == [9]);
+ assert(arr.find!((a, b) => a > b)(4) == [5, 6, 9]);
+ assert(arr.find!((a, b) => a < b)(4) == arr);
+ assert(arr.find(0).empty);
+ assert(arr.find(10).empty);
+ assert(arr.find(8).empty);
assert(find("hello, world", ',') == ", world");
- assert(find([1, 2, 3, 5], 4) == []);
- assert(equal(find(SList!int(1, 2, 3, 4, 5)[], 4), SList!int(4, 5)[]));
- assert(find!"a > b"([1, 2, 3, 5], 2) == [3, 5]);
+}
- auto a = [ 1, 2, 3 ];
- assert(find(a, 5).empty); // not found
- assert(!find(a, 2).empty); // found
+/// Case-insensitive find of a string
+@safe unittest
+{
+ import std.range.primitives;
+ import std.uni : toLower;
- // Case-insensitive find of a string
- string[] s = [ "Hello", "world", "!" ];
- assert(!find!("toLower(a) == b")(s, "hello").empty);
+ string[] s = ["Hello", "world", "!"];
+ assert(s.find!((a, b) => toLower(a) == b)("hello") == s);
}
@safe unittest
@@ -1700,9 +1768,9 @@ if (isInputRange!InputRange &&
@safe pure unittest
{
import std.meta : AliasSeq;
- foreach (R; AliasSeq!(string, wstring, dstring))
+ static foreach (R; AliasSeq!(string, wstring, dstring))
{
- foreach (E; AliasSeq!(char, wchar, dchar))
+ static foreach (E; AliasSeq!(char, wchar, dchar))
{
assert(find ("hello world", 'w') == "world");
assert(find!((a,b)=>a == b)("hello world", 'w') == "world");
@@ -1741,9 +1809,9 @@ if (isInputRange!InputRange &&
{
byte[] sarr = [1, 2, 3, 4];
ubyte[] uarr = [1, 2, 3, 4];
- foreach (arr; AliasSeq!(sarr, uarr))
+ static foreach (arr; AliasSeq!(sarr, uarr))
{
- foreach (T; AliasSeq!(byte, ubyte, int, uint))
+ static foreach (T; AliasSeq!(byte, ubyte, int, uint))
{
assert(find(arr, cast(T) 3) == arr[2 .. $]);
assert(find(arr, cast(T) 9) == arr[$ .. $]);
@@ -1755,9 +1823,9 @@ if (isInputRange!InputRange &&
assertCTFEable!dg;
}
+// https://issues.dlang.org/show_bug.cgi?id=11603
@safe unittest
{
- // Bugzilla 11603
enum Foo : ubyte { A }
assert([Foo.A].find(Foo.A).empty == false);
@@ -1765,35 +1833,7 @@ if (isInputRange!InputRange &&
assert([x].find(x).empty == false);
}
-/**
-Advances the input range $(D haystack) by calling $(D haystack.popFront)
-until either $(D pred(haystack.front)), or $(D
-haystack.empty). Performs $(BIGOH haystack.length) evaluations of $(D
-pred).
-
-To _find the last element of a
-$(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives) $(D haystack) satisfying
-$(D pred), call $(D find!(pred)(retro(haystack))). See $(REF retro, std,range).
-
-`find` behaves similar to `dropWhile` in other languages.
-
-Params:
-
-pred = The predicate for determining if a given element is the one being
-searched for.
-
-haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
-search in.
-
-Returns:
-
-$(D haystack) advanced such that the front element is the one searched for;
-that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no
-such position exists, returns an empty $(D haystack).
-
-See_Also:
- $(HTTP sgi.com/tech/stl/find_if.html, STL's find_if)
-*/
+/// ditto
InputRange find(alias pred, InputRange)(InputRange haystack)
if (isInputRange!InputRange)
{
@@ -1847,31 +1887,7 @@ if (isInputRange!InputRange)
assert(find!(a=>a%4 == 0)("日本語") == "本語");
}
-/**
-Finds the first occurrence of a forward range in another forward range.
-
-Performs $(BIGOH walkLength(haystack) * walkLength(needle)) comparisons in the
-worst case. There are specializations that improve performance by taking
-advantage of $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
-or random access in the given ranges (where possible), depending on the statistics
-of the two ranges' content.
-
-Params:
-
-pred = The predicate to use for comparing respective elements from the haystack
-and the needle. Defaults to simple equality $(D "a == b").
-
-haystack = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
-searched in.
-
-needle = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
-searched for.
-
-Returns:
-
-$(D haystack) advanced such that $(D needle) is a prefix of it (if no
-such position exists, returns $(D haystack) advanced to termination).
- */
+/// ditto
R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle)
if (isForwardRange!R1 && isForwardRange!R2
&& is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
@@ -1887,7 +1903,7 @@ if (isForwardRange!R1 && isForwardRange!R2
Select!(haystack[0].sizeof == 1, ubyte[],
Select!(haystack[0].sizeof == 2, ushort[], uint[]));
// Will use the array specialization
- static TO force(TO, T)(T r) @trusted { return cast(TO) r; }
+ static TO force(TO, T)(inout T r) @trusted { return cast(TO) r; }
return force!R1(.find!(pred, Representation, Representation)
(force!Representation(haystack), force!Representation(needle)));
}
@@ -1975,7 +1991,7 @@ if (isForwardRange!R1 && isForwardRange!R2
// Binary search can be used to find the first occurence
// of the first element of the needle in haystack.
// When it is found O(walklength(needle)) steps are performed.
- // 8829 enhancement
+ // https://issues.dlang.org/show_bug.cgi?id=8829 enhancement
import std.algorithm.comparison : mismatch;
import std.range : SortedRange;
static if (is(R1 == R2)
@@ -2096,6 +2112,17 @@ if (isForwardRange!R1 && isForwardRange!R2
assert([C(1,0), C(2,0), C(3,1), C(4,0)].find!"a.x == b"(SList!int(2, 3)[]) == [C(2,0), C(3,1), C(4,0)]);
}
+// https://issues.dlang.org/show_bug.cgi?id=12470
+@safe unittest
+{
+ import std.array : replace;
+ inout(char)[] sanitize(inout(char)[] p)
+ {
+ return p.replace("\0", " ");
+ }
+ assert(sanitize("O\x00o") == "O o");
+}
+
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -2110,8 +2137,7 @@ if (isForwardRange!R1 && isForwardRange!R2
@safe unittest
{
- import std.range;
- import std.stdio;
+ import std.range : assumeSorted;
auto r1 = assumeSorted([1, 2, 3, 3, 3, 4, 5, 6, 7, 8, 8, 8, 10]);
auto r2 = assumeSorted([3, 3, 4, 5, 6, 7, 8, 8]);
@@ -2155,7 +2181,7 @@ if (isForwardRange!R1 && isForwardRange!R2
assert(find([ 1, 2, 1, 2, 3, 3 ], SList!int(2, 3)[]) == [ 2, 3, 3 ]);
}
-//Bug# 8334
+// https://issues.dlang.org/show_bug.cgi?id=8334
@safe unittest
{
import std.algorithm.iteration : filter;
@@ -2171,6 +2197,12 @@ if (isForwardRange!R1 && isForwardRange!R2
assert(find(haystack, filter!"true"(needle)).empty);
}
+// https://issues.dlang.org/show_bug.cgi?id=11013
+@safe unittest
+{
+ assert(find!"a == a"("abc","abc") == "abc");
+}
+
// Internally used by some find() overloads above
private R1 simpleMindedFind(alias pred, R1, R2)(R1 haystack, scope R2 needle)
{
@@ -2212,7 +2244,7 @@ private R1 simpleMindedFind(alias pred, R1, R2)(R1 haystack, scope R2 needle)
}
else
{
- assert(haystack.empty);
+ assert(haystack.empty, "Haystack must be empty by now");
return haystack;
}
}
@@ -2269,7 +2301,7 @@ private R1 simpleMindedFind(alias pred, R1, R2)(R1 haystack, scope R2 needle)
}
/**
-Finds two or more $(D needles) into a $(D haystack). The predicate $(D
+Finds two or more `needles` into a `haystack`. The predicate $(D
pred) is used throughout to compare elements. By default, elements are
compared for equality.
@@ -2278,41 +2310,41 @@ Params:
pred = The predicate to use for comparing elements.
haystack = The target of the search. Must be an input range.
-If any of $(D needles) is a range with elements comparable to
-elements in $(D haystack), then $(D haystack) must be a
+If any of `needles` is a range with elements comparable to
+elements in `haystack`, then `haystack` must be a
$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
such that the search can backtrack.
-needles = One or more items to search for. Each of $(D needles) must
-be either comparable to one element in $(D haystack), or be itself a
+needles = One or more items to search for. Each of `needles` must
+be either comparable to one element in `haystack`, or be itself a
forward range with elements comparable with elements in
-$(D haystack).
+`haystack`.
Returns:
-A tuple containing $(D haystack) positioned to match one of the
+A tuple containing `haystack` positioned to match one of the
needles and also the 1-based index of the matching element in $(D
-needles) (0 if none of $(D needles) matched, 1 if $(D needles[0])
-matched, 2 if $(D needles[1]) matched...). The first needle to be found
+needles) (0 if none of `needles` matched, 1 if `needles[0]`
+matched, 2 if `needles[1]` matched...). The first needle to be found
will be the one that matches. If multiple needles are found at the
same spot in the range, then the shortest one is the one which matches
(if multiple needles of the same length are found at the same spot (e.g
-$(D "a") and $(D 'a')), then the left-most of them in the argument list
+`"a"` and `'a'`), then the left-most of them in the argument list
matches).
-The relationship between $(D haystack) and $(D needles) simply means
-that one can e.g. search for individual $(D int)s or arrays of $(D
-int)s in an array of $(D int)s. In addition, if elements are
+The relationship between `haystack` and `needles` simply means
+that one can e.g. search for individual `int`s or arrays of $(D
+int)s in an array of `int`s. In addition, if elements are
individually comparable, searches of heterogeneous types are allowed
-as well: a $(D double[]) can be searched for an $(D int) or a $(D
-short[]), and conversely a $(D long) can be searched for a $(D float)
-or a $(D double[]). This makes for efficient searches without the need
+as well: a `double[]` can be searched for an `int` or a $(D
+short[]), and conversely a `long` can be searched for a `float`
+or a `double[]`. This makes for efficient searches without the need
to coerce one side of the comparison into the other's side type.
The complexity of the search is $(BIGOH haystack.length *
max(needles.length)). (For needles that are individual items, length
is considered to be 1.) The strategy used in searching several
-subranges at once maximizes cache usage by moving in $(D haystack) as
+subranges at once maximizes cache usage by moving in `haystack` as
few times as possible.
*/
Tuple!(Range, size_t) find(alias pred = "a == b", Range, Ranges...)
@@ -2418,7 +2450,7 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles))))
}
/**
- * Finds $(D needle) in $(D haystack) efficiently using the
+ * Finds `needle` in `haystack` efficiently using the
* $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm,
* Boyer-Moore) method.
*
@@ -2427,8 +2459,8 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles))))
* needle = A $(LREF BoyerMooreFinder).
*
* Returns:
- * $(D haystack) advanced such that $(D needle) is a prefix of it (if no
- * such position exists, returns $(D haystack) advanced to termination).
+ * `haystack` advanced such that `needle` is a prefix of it (if no
+ * such position exists, returns `haystack` advanced to termination).
*/
RandomAccessRange find(RandomAccessRange, alias pred, InputRange)(
RandomAccessRange haystack, scope BoyerMooreFinder!(pred, InputRange) needle)
@@ -2473,16 +2505,14 @@ Convenience function. Like find, but only returns whether or not the search
was successful.
See_Also:
-$(LREF among) for checking a value against multiple possibilities.
+$(REF among, std,algorithm,comparison) for checking a value against multiple possibilities.
+/
template canFind(alias pred="a == b")
{
- import std.meta : allSatisfy;
-
/++
- Returns $(D true) if and only if any value $(D v) found in the
- input range $(D range) satisfies the predicate $(D pred).
- Performs (at most) $(BIGOH haystack.length) evaluations of $(D pred).
+ Returns `true` if and only if any value `v` found in the
+ input range `range` satisfies the predicate `pred`.
+ Performs (at most) $(BIGOH haystack.length) evaluations of `pred`.
+/
bool canFind(Range)(Range haystack)
if (is(typeof(find!pred(haystack))))
@@ -2491,8 +2521,8 @@ template canFind(alias pred="a == b")
}
/++
- Returns $(D true) if and only if $(D needle) can be found in $(D
- range). Performs $(BIGOH haystack.length) evaluations of $(D pred).
+ Returns `true` if and only if `needle` can be found in $(D
+ range). Performs $(BIGOH haystack.length) evaluations of `pred`.
+/
bool canFind(Range, Element)(Range haystack, scope Element needle)
if (is(typeof(find!pred(haystack, needle))))
@@ -2501,14 +2531,14 @@ template canFind(alias pred="a == b")
}
/++
- Returns the 1-based index of the first needle found in $(D haystack). If no
- needle is found, then $(D 0) is returned.
+ Returns the 1-based index of the first needle found in `haystack`. If no
+ needle is found, then `0` is returned.
So, if used directly in the condition of an if statement or loop, the result
- will be $(D true) if one of the needles is found and $(D false) if none are
+ will be `true` if one of the needles is found and `false` if none are
found, whereas if the result is used elsewhere, it can either be cast to
- $(D bool) for the same effect or used to get which needle was found first
- without having to deal with the tuple that $(D LREF find) returns for the
+ `bool` for the same effect or used to get which needle was found first
+ without having to deal with the tuple that `LREF find` returns for the
same operation.
+/
size_t canFind(Range, Ranges...)(Range haystack, scope Ranges needles)
@@ -2549,6 +2579,17 @@ template canFind(alias pred="a == b")
assert( canFind!((string a, string b) => a.startsWith(b))(words, "bees"));
}
+/// Search for mutliple items in an array of items (search for needles in an array of hay stacks)
+@safe unittest
+{
+ string s1 = "aaa111aaa";
+ string s2 = "aaa222aaa";
+ string s3 = "aaa333aaa";
+ string s4 = "aaa444aaa";
+ const hay = [s1, s2, s3, s4];
+ assert(hay.canFind!(e => (e.canFind("111", "222"))));
+}
+
@safe unittest
{
import std.algorithm.internal : rndstuff;
@@ -2569,9 +2610,9 @@ template canFind(alias pred="a == b")
// findAdjacent
/**
-Advances $(D r) until it finds the first two adjacent elements $(D a),
-$(D b) that satisfy $(D pred(a, b)). Performs $(BIGOH r.length)
-evaluations of $(D pred).
+Advances `r` until it finds the first two adjacent elements `a`,
+`b` that satisfy `pred(a, b)`. Performs $(BIGOH r.length)
+evaluations of `pred`.
Params:
pred = The predicate to satisfy.
@@ -2579,12 +2620,12 @@ Params:
search in.
Returns:
-$(D r) advanced to the first occurrence of two adjacent elements that satisfy
-the given predicate. If there are no such two elements, returns $(D r) advanced
+`r` advanced to the first occurrence of two adjacent elements that satisfy
+the given predicate. If there are no such two elements, returns `r` advanced
until empty.
See_Also:
- $(HTTP sgi.com/tech/stl/adjacent_find.html, STL's adjacent_find)
+ $(LINK2 http://en.cppreference.com/w/cpp/algorithm/adjacent_find, STL's `adjacent_find`)
*/
Range findAdjacent(alias pred = "a == b", Range)(Range r)
if (isForwardRange!(Range))
@@ -2599,6 +2640,7 @@ if (isForwardRange!(Range))
}
static if (!isInfinite!Range)
return ahead;
+ assert(0);
}
///
@@ -2636,7 +2678,7 @@ if (isForwardRange!(Range))
ReferenceForwardRange!int rfr = new ReferenceForwardRange!int([1, 2, 3, 2, 2, 3]);
assert(equal(findAdjacent(rfr), [2, 2, 3]));
- // Issue 9350
+ // https://issues.dlang.org/show_bug.cgi?id=9350
assert(!repeat(1).findAdjacent().empty);
}
@@ -2644,9 +2686,9 @@ if (isForwardRange!(Range))
/**
Searches the given range for an element that matches one of the given choices.
-Advances $(D seq) by calling $(D seq.popFront) until either
-$(D find!(pred)(choices, seq.front)) is $(D true), or $(D seq) becomes empty.
-Performs $(BIGOH seq.length * choices.length) evaluations of $(D pred).
+Advances `seq` by calling `seq.popFront` until either
+`find!(pred)(choices, seq.front)` is `true`, or `seq` becomes empty.
+Performs $(BIGOH seq.length * choices.length) evaluations of `pred`.
Params:
pred = The predicate to use for determining a match.
@@ -2656,17 +2698,16 @@ Params:
of possible choices.
Returns:
-$(D seq) advanced to the first matching element, or until empty if there are no
+`seq` advanced to the first matching element, or until empty if there are no
matching elements.
-See_Also:
- $(HTTP sgi.com/tech/stl/find_first_of.html, STL's find_first_of)
+See_Also: $(LREF find), $(REF std,algorithm,comparison,among)
*/
InputRange findAmong(alias pred = "a == b", InputRange, ForwardRange)(
InputRange seq, ForwardRange choices)
if (isInputRange!InputRange && isForwardRange!ForwardRange)
{
- for (; !seq.empty && find!pred(choices, seq.front).empty; seq.popFront())
+ for (; !seq.empty && find!pred(choices.save, seq.front).empty; seq.popFront())
{
}
return seq;
@@ -2690,10 +2731,24 @@ if (isInputRange!InputRange && isForwardRange!ForwardRange)
assert(findAmong!("a == b")(b, [ 4, 6, 7 ][]).empty);
}
+// https://issues.dlang.org/show_bug.cgi?id=19765
+@system unittest
+{
+ import std.range.interfaces : inputRangeObject;
+ auto choices = inputRangeObject("b");
+ auto f = "foobar".findAmong(choices);
+ assert(f == "bar");
+}
+
// findSkip
/**
- * Finds $(D needle) in $(D haystack) and positions $(D haystack)
- * right after the first occurrence of $(D needle).
+ * Finds `needle` in `haystack` and positions `haystack`
+ * right after the first occurrence of `needle`.
+ *
+ * If no needle is provided, the `haystack` is advanced as long as `pred`
+ * evaluates to `true`.
+ * Similarly, the haystack is positioned so as `pred` evaluates to `false` for
+ * `haystack.front`.
*
* Params:
* haystack = The
@@ -2702,10 +2757,14 @@ if (isInputRange!InputRange && isForwardRange!ForwardRange)
* needle = The
* $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search
* for.
+ * pred = Custom predicate for comparison of haystack and needle
*
- * Returns: $(D true) if the needle was found, in which case $(D haystack) is
- * positioned after the end of the first occurrence of $(D needle); otherwise
- * $(D false), leaving $(D haystack) untouched.
+ * Returns: `true` if the needle was found, in which case `haystack` is
+ * positioned after the end of the first occurrence of `needle`; otherwise
+ * `false`, leaving `haystack` untouched. If no needle is provided, it returns
+ * the number of times `pred(haystack.front)` returned true.
+ *
+ * See_Also: $(LREF find)
*/
bool findSkip(alias pred = "a == b", R1, R2)(ref R1 haystack, R2 needle)
if (isForwardRange!R1 && isForwardRange!R2
@@ -2736,6 +2795,55 @@ if (isForwardRange!R1 && isForwardRange!R2
assert(findSkip(s, "def") && s.empty);
}
+// https://issues.dlang.org/show_bug.cgi?id=19020
+@safe unittest
+{
+ static struct WrapperRange
+ {
+ string _r;
+ @property auto empty() { return _r.empty(); }
+ @property auto front() { return _r.front(); }
+ auto popFront() { return _r.popFront(); }
+ @property auto save() { return WrapperRange(_r.save); }
+ }
+ auto tmp = WrapperRange("there is a bug here: *");
+ assert(!tmp.findSkip("*/"));
+ assert(tmp._r == "there is a bug here: *");
+}
+
+/// ditto
+size_t findSkip(alias pred, R1)(ref R1 haystack)
+if (isForwardRange!R1 && ifTestable!(typeof(haystack.front), unaryFun!pred))
+{
+ size_t result;
+ while (!haystack.empty && unaryFun!pred(haystack.front))
+ {
+ result++;
+ haystack.popFront;
+ }
+ return result;
+}
+
+///
+@safe unittest
+{
+ import std.ascii : isWhite;
+ string s = " abc";
+ assert(findSkip!isWhite(s) && s == "abc");
+ assert(!findSkip!isWhite(s) && s == "abc");
+
+ s = " ";
+ assert(findSkip!isWhite(s) == 2);
+}
+
+@safe unittest
+{
+ import std.ascii : isWhite;
+
+ auto s = " ";
+ assert(findSkip!isWhite(s) == 2);
+}
+
/**
These functions find the first occurrence of `needle` in `haystack` and then
split `haystack` as follows.
@@ -2776,16 +2884,9 @@ Returns:
A sub-type of `Tuple!()` of the split portions of `haystack` (see above for
details). This sub-type of `Tuple!()` has `opCast` defined for `bool`. This
`opCast` returns `true` when the separating `needle` was found
-(`!result[1].empty`) and `false` otherwise. This enables the convenient idiom
-shown in the following example.
+and `false` otherwise.
-Example:
----
-if (const split = haystack.findSplit(needle))
-{
- doSomethingWithSplit(split);
-}
----
+See_Also: $(LREF find)
*/
auto findSplit(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
if (isForwardRange!R1 && isForwardRange!R2)
@@ -2802,9 +2903,19 @@ if (isForwardRange!R1 && isForwardRange!R2)
asTuple = rhs;
}
Tuple!(S1, S1, S2) asTuple;
- bool opCast(T : bool)()
+ static if (hasConstEmptyMember!(typeof(asTuple[1])))
{
- return !asTuple[1].empty;
+ bool opCast(T : bool)() const
+ {
+ return !asTuple[1].empty;
+ }
+ }
+ else
+ {
+ bool opCast(T : bool)()
+ {
+ return !asTuple[1].empty;
+ }
}
alias asTuple this;
}
@@ -2843,6 +2954,10 @@ if (isForwardRange!R1 && isForwardRange!R2)
pos2 = ++pos1;
}
}
+ if (!n.empty) // incomplete match at the end of haystack
+ {
+ pos1 = pos2;
+ }
return Result!(typeof(takeExactly(original, pos1)),
typeof(h))(takeExactly(original, pos1),
takeExactly(haystack, pos2 - pos1),
@@ -2866,9 +2981,19 @@ if (isForwardRange!R1 && isForwardRange!R2)
asTuple = rhs;
}
Tuple!(S1, S2) asTuple;
- bool opCast(T : bool)()
+ static if (hasConstEmptyMember!(typeof(asTuple[1])))
{
- return !asTuple[0].empty;
+ bool opCast(T : bool)() const
+ {
+ return !asTuple[1].empty;
+ }
+ }
+ else
+ {
+ bool opCast(T : bool)()
+ {
+ return !asTuple[1].empty;
+ }
}
alias asTuple this;
}
@@ -2888,24 +3013,30 @@ if (isForwardRange!R1 && isForwardRange!R2)
auto original = haystack.save;
auto h = haystack.save;
auto n = needle.save;
- size_t pos;
+ size_t pos1, pos2;
while (!n.empty && !h.empty)
{
if (binaryFun!pred(h.front, n.front))
{
h.popFront();
n.popFront();
+ ++pos2;
}
else
{
haystack.popFront();
n = needle.save;
h = haystack.save;
- ++pos;
+ pos2 = ++pos1;
}
}
- return Result!(typeof(takeExactly(original, pos)),
- typeof(haystack))(takeExactly(original, pos),
+ if (!n.empty) // incomplete match at the end of haystack
+ {
+ pos1 = pos2;
+ haystack = h;
+ }
+ return Result!(typeof(takeExactly(original, pos1)),
+ typeof(haystack))(takeExactly(original, pos1),
haystack);
}
}
@@ -2926,9 +3057,19 @@ if (isForwardRange!R1 && isForwardRange!R2)
asTuple = rhs;
}
Tuple!(S1, S2) asTuple;
- bool opCast(T : bool)()
+ static if (hasConstEmptyMember!(typeof(asTuple[1])))
{
- return !asTuple[1].empty;
+ bool opCast(T : bool)() const
+ {
+ return !asTuple[0].empty;
+ }
+ }
+ else
+ {
+ bool opCast(T : bool)()
+ {
+ return !asTuple[0].empty;
+ }
}
alias asTuple this;
}
@@ -2978,6 +3119,29 @@ if (isForwardRange!R1 && isForwardRange!R2)
}
}
+/// Returning a subtype of $(REF Tuple, std,typecons) enables
+/// the following convenient idiom:
+@safe pure nothrow unittest
+{
+ // findSplit returns a triplet
+ if (auto split = "dlang-rocks".findSplit("-"))
+ {
+ assert(split[0] == "dlang");
+ assert(split[1] == "-");
+ assert(split[2] == "rocks");
+ }
+ else assert(0);
+
+ // works with const aswell
+ if (const split = "dlang-rocks".findSplit("-"))
+ {
+ assert(split[0] == "dlang");
+ assert(split[1] == "-");
+ assert(split[2] == "rocks");
+ }
+ else assert(0);
+}
+
///
@safe pure nothrow unittest
{
@@ -2996,14 +3160,18 @@ if (isForwardRange!R1 && isForwardRange!R2)
assert(r[0] == "Carl");
assert(r[1] == " ");
assert(r[2] == "Sagan Memorial Station");
- auto r1 = findSplitBefore(a, "Sagan");
- assert(r1);
- assert(r1[0] == "Carl ");
- assert(r1[1] == "Sagan Memorial Station");
- auto r2 = findSplitAfter(a, "Sagan");
- assert(r2);
- assert(r2[0] == "Carl Sagan");
- assert(r2[1] == " Memorial Station");
+ if (const r1 = findSplitBefore(a, "Sagan"))
+ {
+ assert(r1);
+ assert(r1[0] == "Carl ");
+ assert(r1[1] == "Sagan Memorial Station");
+ }
+ if (const r2 = findSplitAfter(a, "Sagan"))
+ {
+ assert(r2);
+ assert(r2[0] == "Carl Sagan");
+ assert(r2[1] == " Memorial Station");
+ }
}
/// Use $(REF only, std,range) to find single elements:
@@ -3017,7 +3185,7 @@ if (isForwardRange!R1 && isForwardRange!R2)
{
import std.range.primitives : empty;
- auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
+ immutable a = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
auto r = findSplit(a, [9, 1]);
assert(!r);
assert(r[0] == a);
@@ -3029,23 +3197,35 @@ if (isForwardRange!R1 && isForwardRange!R2)
assert(r[1] == a[2 .. 3]);
assert(r[2] == a[3 .. $]);
- auto r1 = findSplitBefore(a, [9, 1]);
- assert(r1);
- assert(r1[0] == a);
- assert(r1[1].empty);
- r1 = findSplitBefore(a, [3, 4]);
- assert(r1);
- assert(r1[0] == a[0 .. 2]);
- assert(r1[1] == a[2 .. $]);
-
- auto r2 = findSplitAfter(a, [9, 1]);
- assert(r2);
- assert(r2[0].empty);
- assert(r2[1] == a);
- r2 = findSplitAfter(a, [3, 4]);
- assert(r2);
- assert(r2[0] == a[0 .. 4]);
- assert(r2[1] == a[4 .. $]);
+ {
+ const r1 = findSplitBefore(a, [9, 1]);
+ assert(!r1);
+ assert(r1[0] == a);
+ assert(r1[1].empty);
+ }
+
+ if (immutable r1 = findSplitBefore(a, [3, 4]))
+ {
+ assert(r1);
+ assert(r1[0] == a[0 .. 2]);
+ assert(r1[1] == a[2 .. $]);
+ }
+ else assert(0);
+
+ {
+ const r2 = findSplitAfter(a, [9, 1]);
+ assert(!r2);
+ assert(r2[0].empty);
+ assert(r2[1] == a);
+ }
+
+ if (immutable r3 = findSplitAfter(a, [3, 4]))
+ {
+ assert(r3);
+ assert(r3[0] == a[0 .. 4]);
+ assert(r3[1] == a[4 .. $]);
+ }
+ else assert(0);
}
@safe pure nothrow unittest
@@ -3065,24 +3245,56 @@ if (isForwardRange!R1 && isForwardRange!R2)
assert(equal(r[0], a[0 .. 2]));
assert(equal(r[1], a[2 .. 3]));
assert(equal(r[2], a[3 .. $]));
+ r = findSplit(fwd, [8, 9]);
+ assert(!r);
+ assert(equal(r[0], a));
+ assert(r[1].empty);
+ assert(r[2].empty);
+
+ // auto variable `r2` cannot be `const` because `fwd.front` is mutable
+ {
+ auto r1 = findSplitBefore(fwd, [9, 1]);
+ assert(!r1);
+ assert(equal(r1[0], a));
+ assert(r1[1].empty);
+ }
+
+ if (auto r1 = findSplitBefore(fwd, [3, 4]))
+ {
+ assert(r1);
+ assert(equal(r1[0], a[0 .. 2]));
+ assert(equal(r1[1], a[2 .. $]));
+ }
+ else assert(0);
+
+ {
+ auto r1 = findSplitBefore(fwd, [8, 9]);
+ assert(!r1);
+ assert(equal(r1[0], a));
+ assert(r1[1].empty);
+ }
+
+ {
+ auto r2 = findSplitAfter(fwd, [9, 1]);
+ assert(!r2);
+ assert(r2[0].empty);
+ assert(equal(r2[1], a));
+ }
+
+ if (auto r2 = findSplitAfter(fwd, [3, 4]))
+ {
+ assert(r2);
+ assert(equal(r2[0], a[0 .. 4]));
+ assert(equal(r2[1], a[4 .. $]));
+ }
+ else assert(0);
- auto r1 = findSplitBefore(fwd, [9, 1]);
- assert(r1);
- assert(equal(r1[0], a));
- assert(r1[1].empty);
- r1 = findSplitBefore(fwd, [3, 4]);
- assert(r1);
- assert(equal(r1[0], a[0 .. 2]));
- assert(equal(r1[1], a[2 .. $]));
-
- auto r2 = findSplitAfter(fwd, [9, 1]);
- assert(r2);
- assert(r2[0].empty);
- assert(equal(r2[1], a));
- r2 = findSplitAfter(fwd, [3, 4]);
- assert(r2);
- assert(equal(r2[0], a[0 .. 4]));
- assert(equal(r2[1], a[4 .. $]));
+ {
+ auto r2 = findSplitAfter(fwd, [8, 9]);
+ assert(!r2);
+ assert(r2[0].empty);
+ assert(equal(r2[1], a));
+ }
}
@safe pure nothrow @nogc unittest
@@ -3116,22 +3328,31 @@ if (isForwardRange!R1 && isForwardRange!R2)
assert(split[1] == "one");
}
+// https://issues.dlang.org/show_bug.cgi?id=11013
+@safe pure unittest
+{
+ auto var = "abc";
+ auto split = var.findSplitBefore!q{a == a}(var);
+ assert(split[0] == "");
+ assert(split[1] == "abc");
+}
+
// minCount
/**
Computes the minimum (respectively maximum) of `range` along with its number of
occurrences. Formally, the minimum is a value `x` in `range` such that $(D
pred(a, x)) is `false` for all values `a` in `range`. Conversely, the maximum is
-a value `x` in `range` such that $(D pred(x, a)) is `false` for all values `a`
+a value `x` in `range` such that `pred(x, a)` is `false` for all values `a`
in `range` (note the swapped arguments to `pred`).
These functions may be used for computing arbitrary extrema by choosing `pred`
appropriately. For corrrect functioning, `pred` must be a strict partial order,
-i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and
-irreflexive ($(D pred(a, a)) is `false`). The $(LUCKY trichotomy property of
-inequality) is not required: these algoritms consider elements `a` and `b` equal
+i.e. transitive (if `pred(a, b) && pred(b, c)` then `pred(a, c)`) and
+irreflexive (`pred(a, a)` is `false`). The $(LUCKY trichotomy property of
+inequality) is not required: these algorithms consider elements `a` and `b` equal
(for the purpose of counting) if `pred` puts them in the same equivalence class,
-i.e. $(D !pred(a, b) && !pred(b, a)).
+i.e. `!pred(a, b) && !pred(b, a)`.
Params:
pred = The ordering predicate to use to determine the extremum (minimum
@@ -3141,7 +3362,13 @@ Params:
Returns: The minimum, respectively maximum element of a range together with the
number it occurs in the range.
+Limitations: If at least one of the arguments is NaN, the result is
+an unspecified value. See $(REF maxElement, std,algorithm,searching)
+for examples on how to cope with NaNs.
+
Throws: `Exception` if `range.empty`.
+
+See_Also: $(REF min, std,algorithm,comparison), $(LREF minIndex), $(LREF minElement), $(LREF minPos)
*/
Tuple!(ElementType!Range, size_t)
minCount(alias pred = "a < b", Range)(Range range)
@@ -3319,8 +3546,8 @@ if (isInputRange!Range && !isInfinite!Range &&
}
static assert(!isAssignable!S3);
- foreach (Type; AliasSeq!(S1, IS1, S2, IS2, S3))
- {
+ static foreach (Type; AliasSeq!(S1, IS1, S2, IS2, S3))
+ {{
static if (is(Type == immutable)) alias V = immutable int;
else alias V = int;
V one = 1, two = 2;
@@ -3329,7 +3556,7 @@ if (isInputRange!Range && !isInfinite!Range &&
assert(minCount!"a.i < b.i"(r1) == tuple(Type(one), 2));
assert(minCount!"a.i < b.i"(r2) == tuple(Type(one), 2));
assert(one == 1 && two == 2);
- }
+ }}
}
/**
@@ -3347,24 +3574,37 @@ Params:
Returns: The minimal element of the passed-in range.
+Note:
+ If at least one of the arguments is NaN, the result is an unspecified value.
+
+ If you want to ignore NaNs, you can use $(REF filter, std,algorithm,iteration)
+ and $(REF isNaN, std,math) to remove them, before applying minElement.
+ Add a suitable seed, to avoid error messages if all elements are NaNs:
+
+ ---
+ <range>.filter!(a=>!a.isNaN).minElement(<seed>);
+ ---
+
+ If you want to get NaN as a result if a NaN is present in the range,
+ you can use $(REF fold, std,algorithm,iteration) and $(REF isNaN, std,math):
+
+ ---
+ <range>.fold!((a,b)=>a.isNaN || b.isNaN ? real.nan : a < b ? a : b);
+ ---
+
See_Also:
- $(REF min, std,algorithm,comparison)
+
+ $(LREF maxElement), $(REF min, std,algorithm,comparison), $(LREF minCount),
+ $(LREF minIndex), $(LREF minPos)
*/
-auto minElement(alias map, Range)(Range r)
+auto minElement(alias map = (a => a), Range)(Range r)
if (isInputRange!Range && !isInfinite!Range)
{
return extremum!map(r);
}
/// ditto
-auto minElement(Range)(Range r)
- if (isInputRange!Range && !isInfinite!Range)
-{
- return extremum(r);
-}
-
-/// ditto
-auto minElement(alias map, Range, RangeElementType = ElementType!Range)
+auto minElement(alias map = (a => a), Range, RangeElementType = ElementType!Range)
(Range r, RangeElementType seed)
if (isInputRange!Range && !isInfinite!Range &&
!is(CommonType!(ElementType!Range, RangeElementType) == void))
@@ -3372,22 +3612,13 @@ if (isInputRange!Range && !isInfinite!Range &&
return extremum!map(r, seed);
}
-/// ditto
-auto minElement(Range, RangeElementType = ElementType!Range)
- (Range r, RangeElementType seed)
- if (isInputRange!Range && !isInfinite!Range &&
- !is(CommonType!(ElementType!Range, RangeElementType) == void))
-{
- return extremum(r, seed);
-}
-
///
@safe pure unittest
{
import std.range : enumerate;
import std.typecons : tuple;
- assert([2, 1, 4, 3].minElement == 1);
+ assert([2, 7, 1, 3].minElement == 1);
// allows to get the index of an element too
assert([5, 3, 7, 9].enumerate.minElement!"a.value" == tuple(1, 3));
@@ -3429,6 +3660,7 @@ auto minElement(Range, RangeElementType = ElementType!Range)
DummyType d;
assert(d.minElement == 1);
assert(d.minElement!(a => a) == 1);
+ assert(d.minElement!(a => -a) == 10);
}
// with empty, but seeded ranges
@@ -3446,39 +3678,71 @@ auto minElement(Range, RangeElementType = ElementType!Range)
assert(arr2d.minElement!"a[1]" == arr2d[1]);
}
+// https://issues.dlang.org/show_bug.cgi?id=17982
+@safe unittest
+{
+ struct A
+ {
+ int val;
+ }
+
+ const(A)[] v = [A(0)];
+ assert(v.minElement!"a.val" == A(0));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17982
+@safe unittest
+{
+ class B
+ {
+ int val;
+ this(int val){ this.val = val; }
+ }
+
+ const(B) doStuff(const(B)[] v)
+ {
+ return v.minElement!"a.val";
+ }
+ assert(doStuff([new B(1), new B(0), new B(2)]).val == 0);
+
+ const(B)[] arr = [new B(0), new B(1)];
+ // can't compare directly - https://issues.dlang.org/show_bug.cgi?id=1824
+ assert(arr.minElement!"a.val".val == 0);
+}
+
/**
Iterates the passed range and returns the maximal element.
A custom mapping function can be passed to `map`.
In other languages this is sometimes called `argmax`.
-Complexity:
+Complexity: O(n)
Exactly `n - 1` comparisons are needed.
Params:
map = custom accessor for the comparison key
- r = range from which the maximum will be selected
+ r = range from which the maximum element will be selected
seed = custom seed to use as initial element
Returns: The maximal element of the passed-in range.
+Note:
+ If at least one of the arguments is NaN, the result is an unspecified value.
+ See $(REF minElement, std,algorithm,searching) for examples on how to cope
+ with NaNs.
+
See_Also:
- $(REF max, std,algorithm,comparison)
+
+ $(LREF minElement), $(REF max, std,algorithm,comparison), $(LREF maxCount),
+ $(LREF maxIndex), $(LREF maxPos)
*/
-auto maxElement(alias map, Range)(Range r)
+auto maxElement(alias map = (a => a), Range)(Range r)
if (isInputRange!Range && !isInfinite!Range)
{
return extremum!(map, "a > b")(r);
}
/// ditto
-auto maxElement(Range)(Range r)
-if (isInputRange!Range && !isInfinite!Range)
-{
- return extremum!`a > b`(r);
-}
-
-/// ditto
-auto maxElement(alias map, Range, RangeElementType = ElementType!Range)
+auto maxElement(alias map = (a => a), Range, RangeElementType = ElementType!Range)
(Range r, RangeElementType seed)
if (isInputRange!Range && !isInfinite!Range &&
!is(CommonType!(ElementType!Range, RangeElementType) == void))
@@ -3486,15 +3750,6 @@ if (isInputRange!Range && !isInfinite!Range &&
return extremum!(map, "a > b")(r, seed);
}
-/// ditto
-auto maxElement(Range, RangeElementType = ElementType!Range)
- (Range r, RangeElementType seed)
-if (isInputRange!Range && !isInfinite!Range &&
- !is(CommonType!(ElementType!Range, RangeElementType) == void))
-{
- return extremum!`a > b`(r, seed);
-}
-
///
@safe pure unittest
{
@@ -3544,6 +3799,7 @@ if (isInputRange!Range && !isInfinite!Range &&
DummyType d;
assert(d.maxElement == 10);
assert(d.maxElement!(a => a) == 10);
+ assert(d.maxElement!(a => -a) == 1);
}
// with empty, but seeded ranges
@@ -3562,31 +3818,57 @@ if (isInputRange!Range && !isInfinite!Range &&
assert(arr2d.maxElement!"a[1]" == arr2d[1]);
}
+// https://issues.dlang.org/show_bug.cgi?id=17982
+@safe unittest
+{
+ class B
+ {
+ int val;
+ this(int val){ this.val = val; }
+ }
+
+ const(B) doStuff(const(B)[] v)
+ {
+ return v.maxElement!"a.val";
+ }
+ assert(doStuff([new B(1), new B(0), new B(2)]).val == 2);
+
+ const(B)[] arr = [new B(0), new B(1)];
+ // can't compare directly - https://issues.dlang.org/show_bug.cgi?id=1824
+ assert(arr.maxElement!"a.val".val == 1);
+}
+
// minPos
/**
Computes a subrange of `range` starting at the first occurrence of `range`'s
minimum (respectively maximum) and with the same ending as `range`, or the
empty range if `range` itself is empty.
-Formally, the minimum is a value `x` in `range` such that $(D pred(a, x)) is
+Formally, the minimum is a value `x` in `range` such that `pred(a, x)` is
`false` for all values `a` in `range`. Conversely, the maximum is a value `x` in
-`range` such that $(D pred(x, a)) is `false` for all values `a` in `range` (note
+`range` such that `pred(x, a)` is `false` for all values `a` in `range` (note
the swapped arguments to `pred`).
These functions may be used for computing arbitrary extrema by choosing `pred`
appropriately. For corrrect functioning, `pred` must be a strict partial order,
-i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and
-irreflexive ($(D pred(a, a)) is `false`).
+i.e. transitive (if `pred(a, b) && pred(b, c)` then `pred(a, c)`) and
+irreflexive (`pred(a, a)` is `false`).
Params:
pred = The ordering predicate to use to determine the extremum (minimum or
maximum) element.
- range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to search.
+ range = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search.
Returns: The position of the minimum (respectively maximum) element of forward
range `range`, i.e. a subrange of `range` starting at the position of its
smallest (respectively largest) element and with the same ending as `range`.
+Limitations: If at least one of the arguments is NaN, the result is
+an unspecified value. See $(REF maxElement, std,algorithm,searching)
+for examples on how to cope with NaNs.
+
+See_Also:
+ $(REF max, std,algorithm,comparison), $(LREF minCount), $(LREF minIndex), $(LREF minElement)
*/
Range minPos(alias pred = "a < b", Range)(Range range)
if (isForwardRange!Range && !isInfinite!Range &&
@@ -3688,23 +3970,28 @@ Params:
range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
to search.
-Complexity: O(n)
- Exactly `n - 1` comparisons are needed.
+Complexity: $(BIGOH range.length)
+ Exactly `range.length - 1` comparisons are needed.
Returns:
The index of the first encounter of the minimum element in `range`. If the
`range` is empty, -1 is returned.
+Limitations:
+ If at least one of the arguments is NaN, the result is
+ an unspecified value. See $(REF maxElement, std,algorithm,searching)
+ for examples on how to cope with NaNs.
+
See_Also:
- $(REF min, std,algorithm,comparison), $(LREF minCount), $(LREF minElement), $(LREF minPos)
+ $(LREF maxIndex), $(REF min, std,algorithm,comparison), $(LREF minCount), $(LREF minElement), $(LREF minPos)
*/
-sizediff_t minIndex(alias pred = "a < b", Range)(Range range)
-if (isForwardRange!Range && !isInfinite!Range &&
+ptrdiff_t minIndex(alias pred = "a < b", Range)(Range range)
+if (isInputRange!Range && !isInfinite!Range &&
is(typeof(binaryFun!pred(range.front, range.front))))
{
if (range.empty) return -1;
- sizediff_t minPos = 0;
+ ptrdiff_t minPos = 0;
static if (isRandomAccessRange!Range && hasLength!Range)
{
@@ -3718,7 +4005,7 @@ if (isForwardRange!Range && !isInfinite!Range &&
}
else
{
- sizediff_t curPos = 0;
+ ptrdiff_t curPos = 0;
Unqual!(typeof(range.front)) min = range.front;
for (range.popFront(); !range.empty; range.popFront())
{
@@ -3799,11 +4086,45 @@ if (isForwardRange!Range && !isInfinite!Range &&
assert(arr2d.minIndex!"a[1] < b[1]" == 2);
}
+@safe nothrow pure unittest
+{
+ // InputRange test
+
+ static struct InRange
+ {
+ @property int front()
+ {
+ return arr[index];
+ }
+
+ bool empty() const
+ {
+ return arr.length == index;
+ }
+
+ void popFront()
+ {
+ index++;
+ }
+
+ int[] arr;
+ size_t index = 0;
+ }
+
+ static assert(isInputRange!InRange);
+
+ auto arr1 = InRange([5, 2, 3, 4, 5, 3, 6]);
+ auto arr2 = InRange([7, 3, 8, 2, 1, 4]);
+
+ assert(arr1.minIndex == 1);
+ assert(arr2.minIndex == 4);
+}
+
/**
Computes the index of the first occurrence of `range`'s maximum element.
-Complexity: O(n)
- Exactly `n - 1` comparisons are needed.
+Complexity: $(BIGOH range)
+ Exactly `range.length - 1` comparisons are needed.
Params:
pred = The ordering predicate to use to determine the maximum element.
@@ -3813,10 +4134,15 @@ Returns:
The index of the first encounter of the maximum in `range`. If the
`range` is empty, -1 is returned.
+Limitations:
+ If at least one of the arguments is NaN, the result is
+ an unspecified value. See $(REF maxElement, std,algorithm,searching)
+ for examples on how to cope with NaNs.
+
See_Also:
- $(REF max, std,algorithm,comparison), $(LREF maxCount), $(LREF maxElement), $(LREF maxPos)
+ $(LREF minIndex), $(REF max, std,algorithm,comparison), $(LREF maxCount), $(LREF maxElement), $(LREF maxPos)
*/
-sizediff_t maxIndex(alias pred = "a < b", Range)(Range range)
+ptrdiff_t maxIndex(alias pred = "a < b", Range)(Range range)
if (isInputRange!Range && !isInfinite!Range &&
is(typeof(binaryFun!pred(range.front, range.front))))
{
@@ -3889,79 +4215,158 @@ if (isInputRange!Range && !isInfinite!Range &&
}
/**
-Skip over the initial portion of the first given range that matches the second
-range, or if no second range is given skip over the elements that fullfil pred.
+Skip over the initial portion of the first given range (`haystack`) that matches
+any of the additionally given ranges (`needles`) fully, or
+if no second range is given skip over the elements that fulfill pred.
Do nothing if there is no match.
Params:
pred = The predicate that determines whether elements from each respective
- range match. Defaults to equality $(D "a == b").
- r1 = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to
- move forward.
- r2 = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
- representing the initial segment of $(D r1) to skip over.
-
-Returns:
-true if the initial segment of $(D r1) matches $(D r2) or $(D pred) evaluates to true,
-and $(D r1) has been advanced to the point past this segment; otherwise false, and
-$(D r1) is left in its original position.
- */
-bool skipOver(R1, R2)(ref R1 r1, R2 r2)
-if (isForwardRange!R1 && isInputRange!R2
- && is(typeof(r1.front == r2.front)))
+ range match. Defaults to equality `"a == b"`.
+*/
+template skipOver(alias pred = (a, b) => a == b)
{
- static if (is(typeof(r1[0 .. $] == r2) : bool)
- && is(typeof(r2.length > r1.length) : bool)
- && is(typeof(r1 = r1[r2.length .. $])))
+ enum bool isPredComparable(T) = ifTestable!(T, binaryFun!pred);
+
+ /**
+ Params:
+ haystack = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to
+ move forward.
+ needles = The $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives)
+ representing the prefix of `r1` to skip over.
+ es = The element to match.
+
+ Returns:
+ `true` if the prefix of `haystack` matches any range of `needles` fully
+ or `pred` evaluates to true, and `haystack` has been advanced to the point past this segment;
+ otherwise false, and `haystack` is left in its original position.
+
+ Note:
+ By definition, empty ranges are matched fully and if `needles` contains an empty range,
+ `skipOver` will return `true`.
+ */
+ bool skipOver(Haystack, Needles...)(ref Haystack haystack, Needles needles)
+ if (is(typeof(binaryFun!pred(haystack.front, needles[0].front))) &&
+ isForwardRange!Haystack &&
+ allSatisfy!(isInputRange, Needles) &&
+ !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Needles))) == void))
{
- if (r2.length > r1.length || r1[0 .. r2.length] != r2)
+ static if (__traits(isSame, pred, (a, b) => a == b)
+ && is(typeof(haystack[0 .. $] == needles[0]) : bool)
+ && is(typeof(haystack = haystack[0 .. $]))
+ && hasLength!Haystack && allSatisfy!(hasLength, Needles))
{
+ ptrdiff_t longestMatch = -1;
+ static foreach (r2; needles)
+ {
+ if (r2.length <= haystack.length && longestMatch < ptrdiff_t(r2.length)
+ && (haystack[0 .. r2.length] == r2 || r2.length == 0))
+ longestMatch = r2.length;
+ }
+ if (longestMatch >= 0)
+ {
+ if (longestMatch > 0)
+ haystack = haystack[longestMatch .. $];
+
+ return true;
+ }
return false;
}
- r1 = r1[r2.length .. $];
- return true;
- }
- else
- {
- return skipOver!((a, b) => a == b)(r1, r2);
+ else
+ {
+ import std.algorithm.comparison : min;
+ auto r = haystack.save;
+
+ static if (hasLength!Haystack && allSatisfy!(hasLength, Needles))
+ {
+ import std.algorithm.iteration : map;
+ import std.algorithm.searching : minElement;
+ import std.range : only;
+ // Shortcut opportunity!
+ if (needles.only.map!(a => a.length).minElement > haystack.length)
+ return false;
+ }
+
+ // compatibility: return true if any range was empty
+ bool hasEmptyRanges;
+ static foreach (i, r2; needles)
+ {
+ if (r2.empty)
+ hasEmptyRanges = true;
+ }
+
+ bool hasNeedleMatch;
+ size_t inactiveNeedlesLen;
+ bool[Needles.length] inactiveNeedles;
+ for (; !r.empty; r.popFront)
+ {
+ static foreach (i, r2; needles)
+ {
+ if (!r2.empty && !inactiveNeedles[i])
+ {
+ if (binaryFun!pred(r.front, r2.front))
+ {
+ r2.popFront;
+ if (r2.empty)
+ {
+ // we skipped over a new match
+ hasNeedleMatch = true;
+ inactiveNeedlesLen++;
+ // skip over haystack
+ haystack = r;
+ }
+ }
+ else
+ {
+ inactiveNeedles[i] = true;
+ inactiveNeedlesLen++;
+ }
+ }
+ }
+
+ // are we done?
+ if (inactiveNeedlesLen == needles.length)
+ break;
+ }
+
+ if (hasNeedleMatch)
+ haystack.popFront;
+
+ return hasNeedleMatch || hasEmptyRanges;
+ }
}
-}
-/// Ditto
-bool skipOver(alias pred, R1, R2)(ref R1 r1, R2 r2)
-if (is(typeof(binaryFun!pred(r1.front, r2.front))) &&
- isForwardRange!R1 &&
- isInputRange!R2)
-{
- static if (hasLength!R1 && hasLength!R2)
+ /// Ditto
+ bool skipOver(R)(ref R r1)
+ if (isForwardRange!R &&
+ ifTestable!(typeof(r1.front), unaryFun!pred))
{
- // Shortcut opportunity!
- if (r2.length > r1.length)
+ if (r1.empty || !unaryFun!pred(r1.front))
return false;
+
+ do
+ r1.popFront();
+ while (!r1.empty && unaryFun!pred(r1.front));
+ return true;
}
- auto r = r1.save;
- while (!r2.empty && !r.empty && binaryFun!pred(r.front, r2.front))
+
+ /// Ditto
+ bool skipOver(R, Es...)(ref R r, Es es)
+ if (isInputRange!R && is(typeof(binaryFun!pred(r.front, es[0]))))
{
- r.popFront();
- r2.popFront();
- }
- if (r2.empty)
- r1 = r;
- return r2.empty;
-}
+ if (r.empty)
+ return false;
-/// Ditto
-bool skipOver(alias pred, R)(ref R r1)
-if (isForwardRange!R &&
- ifTestable!(typeof(r1.front), unaryFun!pred))
-{
- if (r1.empty || !unaryFun!pred(r1.front))
+ static foreach (e; es)
+ {
+ if (binaryFun!pred(r.front, e))
+ {
+ r.popFront();
+ return true;
+ }
+ }
return false;
-
- do
- r1.popFront();
- while (!r1.empty && unaryFun!pred(r1.front));
- return true;
+ }
}
///
@@ -3972,15 +4377,19 @@ if (isForwardRange!R &&
auto s1 = "Hello world";
assert(!skipOver(s1, "Ha"));
assert(s1 == "Hello world");
- assert(skipOver(s1, "Hell") && s1 == "o world");
+ assert(skipOver(s1, "Hell") && s1 == "o world", s1);
string[] r1 = ["abc", "def", "hij"];
dstring[] r2 = ["abc"d];
- assert(!skipOver!((a, b) => a.equal(b))(r1, ["def"d]));
+ assert(!skipOver!((a, b) => a.equal(b))(r1, ["def"d]), r1[0]);
assert(r1 == ["abc", "def", "hij"]);
assert(skipOver!((a, b) => a.equal(b))(r1, r2));
assert(r1 == ["def", "hij"]);
+}
+///
+@safe unittest
+{
import std.ascii : isWhite;
import std.range.primitives : empty;
@@ -3992,38 +4401,16 @@ if (isForwardRange!R &&
assert(s4.skipOver!isWhite && s3.empty);
}
-/**
-Skip over the first element of the given range if it matches the given element,
-otherwise do nothing.
-
-Params:
- pred = The predicate that determines whether an element from the range
- matches the given element.
-
- r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to skip
- over.
-
- e = The element to match.
-
-Returns:
-true if the first element matches the given element according to the given
-predicate, and the range has been advanced by one element; otherwise false, and
-the range is left untouched.
- */
-bool skipOver(R, E)(ref R r, E e)
-if (isInputRange!R && is(typeof(r.front == e) : bool))
+/// Variadic skipOver
+@safe unittest
{
- return skipOver!((a, b) => a == b)(r, e);
-}
+ auto s = "Hello world";
+ assert(!skipOver(s, "hello", "HellO"));
+ assert(s == "Hello world");
-/// Ditto
-bool skipOver(alias pred, R, E)(ref R r, E e)
-if (is(typeof(binaryFun!pred(r.front, e))) && isInputRange!R)
-{
- if (r.empty || !binaryFun!pred(r.front, e))
- return false;
- r.popFront();
- return true;
+ // the range is skipped over the longest matching needle is skipped
+ assert(skipOver(s, "foo", "hell", "Hello "));
+ assert(s == "world");
}
///
@@ -4047,11 +4434,154 @@ if (is(typeof(binaryFun!pred(r.front, e))) && isInputRange!R)
assert(!s2.skipOver('a'));
}
+/// Partial instantiation
+@safe unittest
+{
+ import std.ascii : isWhite;
+ import std.range.primitives : empty;
+
+ alias whitespaceSkiper = skipOver!isWhite;
+
+ auto s2 = "\t\tvalue";
+ auto s3 = "";
+ auto s4 = "\t\t\t";
+ assert(whitespaceSkiper(s2) && s2 == "value");
+ assert(!whitespaceSkiper(s2));
+ assert(whitespaceSkiper(s4) && s3.empty);
+}
+
+// variadic skipOver
+@safe unittest
+{
+ auto s = "DLang.rocks";
+ assert(!s.skipOver("dlang", "DLF", "DLang "));
+ assert(s == "DLang.rocks");
+
+ assert(s.skipOver("dlang", "DLANG", "DLF", "D", "DL", "DLanpp"));
+ assert(s == "ang.rocks");
+ s = "DLang.rocks";
+
+ assert(s.skipOver("DLang", "DLANG", "DLF", "D", "DL", "DLang "));
+ assert(s == ".rocks");
+ s = "DLang.rocks";
+
+ assert(s.skipOver("dlang", "DLANG", "DLF", "D", "DL", "DLang."));
+ assert(s == "rocks");
+}
+
+// variadic with custom pred
+@safe unittest
+{
+ import std.ascii : toLower;
+
+ auto s = "DLang.rocks";
+ assert(!s.skipOver("dlang", "DLF", "DLang "));
+ assert(s == "DLang.rocks");
+
+ assert(s.skipOver!((a, b) => a.toLower == b.toLower)("dlang", "DLF", "DLang "));
+ assert(s == ".rocks");
+}
+
+// variadic skipOver with mixed needles
+@safe unittest
+{
+ auto s = "DLang.rocks";
+ assert(!s.skipOver("dlang"d, "DLF", "DLang "w));
+ assert(s == "DLang.rocks");
+
+ assert(s.skipOver("dlang", "DLANG"d, "DLF"w, "D"d, "DL", "DLanp"));
+ assert(s == "ang.rocks");
+ s = "DLang.rocks";
+
+ assert(s.skipOver("DLang", "DLANG"w, "DLF"d, "D"d, "DL", "DLang "));
+ assert(s == ".rocks");
+ s = "DLang.rocks";
+
+ assert(s.skipOver("dlang", "DLANG"w, "DLF", "D"d, "DL"w, "DLang."d));
+ assert(s == "rocks");
+
+ import std.algorithm.iteration : filter;
+ s = "DLang.rocks";
+ assert(s.skipOver("dlang", "DLang".filter!(a => true)));
+ assert(s == ".rocks");
+}
+
+// variadic skipOver with auto-decoding
+@safe unittest
+{
+ auto s = "☢☣☠.☺";
+ assert(s.skipOver("a", "☢", "☢☣☠"));
+ assert(s == ".☺");
+}
+
+// skipOver with @nogc
+@safe @nogc pure nothrow unittest
+{
+ static immutable s = [0, 1, 2];
+ immutable(int)[] s2 = s[];
+
+ static immutable skip1 = [0, 2];
+ static immutable skip2 = [0, 1];
+ assert(s2.skipOver(skip1, skip2));
+ assert(s2 == s[2 .. $]);
+}
+
+// variadic skipOver with single elements
+@safe unittest
+{
+ auto s = "DLang.rocks";
+ assert(!s.skipOver('a', 'd', 'e'));
+ assert(s == "DLang.rocks");
+
+ assert(s.skipOver('a', 'D', 'd', 'D'));
+ assert(s == "Lang.rocks");
+ s = "DLang.rocks";
+
+ assert(s.skipOver(wchar('a'), dchar('D'), 'd'));
+ assert(s == "Lang.rocks");
+
+ dstring dstr = "+Foo";
+ assert(!dstr.skipOver('.', '-'));
+ assert(dstr == "+Foo");
+
+ assert(dstr.skipOver('+', '-'));
+ assert(dstr == "Foo");
+}
+
+// skipOver with empty ranges must return true (compatibility)
+@safe unittest
+{
+ auto s = "DLang.rocks";
+ assert(s.skipOver(""));
+ assert(s.skipOver("", ""));
+ assert(s.skipOver("", "foo"));
+
+ auto s2 = "DLang.rocks"d;
+ assert(s2.skipOver(""));
+ assert(s2.skipOver("", ""));
+ assert(s2.skipOver("", "foo"));
+}
+
+// dxml regression
+@safe unittest
+{
+ import std.utf : byCodeUnit;
+ import std.algorithm.comparison : equal;
+
+ bool stripStartsWith(Text)(ref Text text, string needle)
+ {
+ return text.skipOver(needle.byCodeUnit());
+ }
+ auto text = "<xml></xml>"d.byCodeUnit;
+ assert(stripStartsWith(text, "<xml>"));
+ assert(text.equal("</xml>"));
+}
+
/**
Checks whether the given
$(REF_ALTTEXT input range, isInputRange, std,range,primitives) starts with (one
of) the given needle(s) or, if no needles are given,
-if its front element fulfils predicate $(D pred).
+if its front element fulfils predicate `pred`.
Params:
@@ -4070,24 +4600,38 @@ Returns:
0 if the needle(s) do not occur at the beginning of the given range;
otherwise the position of the matching needle, that is, 1 if the range starts
-with $(D withOneOfThese[0]), 2 if it starts with $(D withOneOfThese[1]), and so
+with `withOneOfThese[0]`, 2 if it starts with `withOneOfThese[1]`, and so
on.
-In the case where $(D doesThisStart) starts with multiple of the ranges or
-elements in $(D withOneOfThese), then the shortest one matches (if there are
-two which match which are of the same length (e.g. $(D "a") and $(D 'a')), then
+In the case where `doesThisStart` starts with multiple of the ranges or
+elements in `withOneOfThese`, then the shortest one matches (if there are
+two which match which are of the same length (e.g. `"a"` and `'a'`), then
the left-most of them in the argument
list matches).
-In the case when no needle parameters are given, return $(D true) iff front of
-$(D doesThisStart) fulfils predicate $(D pred).
+In the case when no needle parameters are given, return `true` iff front of
+`doesThisStart` fulfils predicate `pred`.
*/
-uint startsWith(alias pred = "a == b", Range, Needles...)(Range doesThisStart, Needles withOneOfThese)
+uint startsWith(alias pred = (a, b) => a == b, Range, Needles...)(Range doesThisStart, Needles withOneOfThese)
if (isInputRange!Range && Needles.length > 1 &&
- is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[0])) : bool ) &&
- is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[1 .. $])) : uint))
+ allSatisfy!(canTestStartsWith!(pred, Range), Needles))
{
- alias haystack = doesThisStart;
+ template checkType(T)
+ {
+ enum checkType = is(immutable ElementEncodingType!Range == immutable T);
+ }
+
+ // auto-decoding special case
+ static if (__traits(isSame, binaryFun!pred, (a, b) => a == b) &&
+ isNarrowString!Range && allSatisfy!(checkType, Needles))
+ {
+ import std.utf : byCodeUnit;
+ auto haystack = doesThisStart.byCodeUnit;
+ }
+ else
+ {
+ alias haystack = doesThisStart;
+ }
alias needles = withOneOfThese;
// Make one pass looking for empty ranges in needles
@@ -4168,17 +4712,18 @@ if (isInputRange!R1 &&
else
enum isDefaultPred = false;
- //Note: While narrow strings don't have a "true" length, for a narrow string to start with another
- //narrow string *of the same type*, it must have *at least* as many code units.
+ // Note: Although narrow strings don't have a "true" length, for a narrow string to start with another
+ // narrow string, it must have *at least* as many code units.
static if ((hasLength!R1 && hasLength!R2) ||
- (isNarrowString!R1 && isNarrowString!R2 && ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof))
+ ((hasLength!R1 || isNarrowString!R1) && (hasLength!R2 || isNarrowString!R2)
+ && (ElementEncodingType!R1.sizeof <= ElementEncodingType!R2.sizeof)))
{
if (haystack.length < needle.length)
return false;
}
static if (isDefaultPred && isArray!R1 && isArray!R2 &&
- is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
+ is(immutable ElementEncodingType!R1 == immutable ElementEncodingType!R2))
{
//Array slice comparison mode
return haystack[0 .. needle.length] == needle;
@@ -4231,16 +4776,27 @@ if (isInputRange!R &&
if (doesThisStart.empty)
return false;
+ static if (is(typeof(pred) : string))
+ enum isDefaultPred = pred == "a == b";
+ else
+ enum isDefaultPred = false;
+
alias predFunc = binaryFun!pred;
// auto-decoding special case
static if (isNarrowString!R)
{
+ // statically determine decoding is unnecessary to evaluate pred
+ static if (isDefaultPred && isSomeChar!E && E.sizeof <= ElementEncodingType!R.sizeof)
+ return doesThisStart[0] == withThis;
// specialize for ASCII as to not change previous behavior
- if (withThis <= 0x7F)
- return predFunc(doesThisStart[0], withThis);
else
- return predFunc(doesThisStart.front, withThis);
+ {
+ if (withThis <= 0x7F)
+ return predFunc(doesThisStart[0], withThis);
+ else
+ return predFunc(doesThisStart.front, withThis);
+ }
}
else
{
@@ -4295,16 +4851,17 @@ if (isInputRange!R &&
import std.meta : AliasSeq;
import std.range;
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ (){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
assert(!startsWith(to!S("abc"), 'c'));
assert(startsWith(to!S("abc"), 'a', 'c') == 1);
assert(!startsWith(to!S("abc"), 'x', 'n', 'b'));
assert(startsWith(to!S("abc"), 'x', 'n', 'a') == 3);
assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2);
- foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
//Lots of strings
assert(startsWith(to!S("abc"), to!T("")));
assert(startsWith(to!S("ab"), to!T("a")));
@@ -4337,16 +4894,16 @@ if (isInputRange!R &&
assert(startsWith(to!S("a"), T.init, "") == 1);
assert(startsWith(to!S("a"), T.init, 'a') == 1);
assert(startsWith(to!S("a"), 'a', T.init) == 2);
- }();
- }
+ }
+ }();
//Length but no RA
assert(!startsWith("abc".takeExactly(3), "abcd".takeExactly(4)));
assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(3)));
assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(1)));
- foreach (T; AliasSeq!(int, short))
- {
+ static foreach (T; AliasSeq!(int, short))
+ {{
immutable arr = cast(T[])[0, 1, 2, 3, 4, 5];
//RA range
@@ -4377,12 +4934,18 @@ if (isInputRange!R &&
//Non-default pred
assert(startsWith!("a%10 == b%10")(arr, [10, 11]));
assert(!startsWith!("a%10 == b%10")(arr, [10, 12]));
- }
+ }}
+}
+
+private template canTestStartsWith(alias pred, Haystack)
+{
+ enum bool canTestStartsWith(Needle) = is(typeof(
+ (ref Haystack h, ref Needle n) => startsWith!pred(h, n)));
}
/* (Not yet documented.)
-Consume all elements from $(D r) that are equal to one of the elements
-$(D es).
+Consume all elements from `r` that are equal to one of the elements
+`es`.
*/
private void skipAll(alias pred = "a == b", R, Es...)(ref R r, Es es)
//if (is(typeof(binaryFun!pred(r1.front, es[0]))))
@@ -4411,34 +4974,34 @@ private void skipAll(alias pred = "a == b", R, Es...)(ref R r, Es es)
/**
Interval option specifier for `until` (below) and others.
-If set to $(D OpenRight.yes), then the interval is open to the right
+If set to `OpenRight.yes`, then the interval is open to the right
(last element is not included).
-Otherwise if set to $(D OpenRight.no), then the interval is closed to the right
+Otherwise if set to `OpenRight.no`, then the interval is closed to the right
(last element included).
*/
alias OpenRight = Flag!"openRight";
/**
-Lazily iterates $(D range) _until the element $(D e) for which
-$(D pred(e, sentinel)) is true.
+Lazily iterates `range` _until the element `e` for which
+`pred(e, sentinel)` is true.
This is similar to `takeWhile` in other languages.
Params:
pred = Predicate to determine when to stop.
- range = The $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
to iterate over.
sentinel = The element to stop at.
openRight = Determines whether the element for which the given predicate is
- true should be included in the resulting range ($(D No.openRight)), or
- not ($(D Yes.openRight)).
+ true should be included in the resulting range (`No.openRight`), or
+ not (`Yes.openRight`).
Returns:
- An $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) that
+ An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that
iterates over the original range's elements, but ends when the specified
predicate becomes true. If the original range is a
- $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives) or
+ $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) or
higher, this range will be a forward range.
*/
Until!(pred, Range, Sentinel)
@@ -4468,6 +5031,7 @@ if (isInputRange!Range)
private bool _done;
static if (!is(Sentinel == void))
+ {
///
this(Range input, Sentinel sentinel,
OpenRight openRight = Yes.openRight)
@@ -4477,7 +5041,17 @@ if (isInputRange!Range)
_openRight = openRight;
_done = _input.empty || openRight && predSatisfied();
}
+ private this(Range input, Sentinel sentinel, OpenRight openRight,
+ bool done)
+ {
+ _input = input;
+ _sentinel = sentinel;
+ _openRight = openRight;
+ _done = done;
+ }
+ }
else
+ {
///
this(Range input, OpenRight openRight = Yes.openRight)
{
@@ -4485,6 +5059,13 @@ if (isInputRange!Range)
_openRight = openRight;
_done = _input.empty || openRight && predSatisfied();
}
+ private this(Range input, OpenRight openRight, bool done)
+ {
+ _input = input;
+ _openRight = openRight;
+ _done = done;
+ }
+ }
///
@property bool empty()
@@ -4495,7 +5076,7 @@ if (isInputRange!Range)
///
@property auto ref front()
{
- assert(!empty);
+ assert(!empty, "Can not get the front of an empty Until");
return _input.front;
}
@@ -4510,7 +5091,7 @@ if (isInputRange!Range)
///
void popFront()
{
- assert(!empty);
+ assert(!empty, "Can not popFront of an empty Until");
if (!_openRight)
{
_done = predSatisfied();
@@ -4526,27 +5107,14 @@ if (isInputRange!Range)
static if (isForwardRange!Range)
{
- static if (!is(Sentinel == void))
- ///
- @property Until save()
- {
- Until result = this;
- result._input = _input.save;
- result._sentinel = _sentinel;
- result._openRight = _openRight;
- result._done = _done;
- return result;
- }
- else
- ///
- @property Until save()
- {
- Until result = this;
- result._input = _input.save;
- result._openRight = _openRight;
- result._done = _done;
- return result;
- }
+ ///
+ @property Until save()
+ {
+ static if (is(Sentinel == void))
+ return Until(_input.save, _openRight, _done);
+ else
+ return Until(_input.save, _sentinel, _openRight, _done);
+ }
}
}
@@ -4574,7 +5142,8 @@ if (isInputRange!Range)
assert(equal(until!"a == 2"(a, No.openRight), [1, 2]));
}
-@system unittest // bugzilla 13171
+// https://issues.dlang.org/show_bug.cgi?id=13171
+@system unittest
{
import std.algorithm.comparison : equal;
import std.range;
@@ -4583,7 +5152,8 @@ if (isInputRange!Range)
assert(a == [4]);
}
-@safe unittest // Issue 10460
+// https://issues.dlang.org/show_bug.cgi?id=10460
+@safe unittest
{
import std.algorithm.comparison : equal;
auto a = [1, 2, 3, 4];
@@ -4592,9 +5162,29 @@ if (isInputRange!Range)
assert(equal(a, [0, 0, 3, 4]));
}
-@safe unittest // Issue 13124
+// https://issues.dlang.org/show_bug.cgi?id=13124
+@safe unittest
{
import std.algorithm.comparison : among, equal;
auto s = "hello how\nare you";
assert(equal(s.until!(c => c.among!('\n', '\r')), "hello how"));
}
+
+// https://issues.dlang.org/show_bug.cgi?id=18657
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : refRange;
+ {
+ string s = "foobar";
+ auto r = refRange(&s).until("bar");
+ assert(equal(r.save, "foo"));
+ assert(equal(r.save, "foo"));
+ }
+ {
+ string s = "foobar";
+ auto r = refRange(&s).until!(e => e == 'b');
+ assert(equal(r.save, "foo"));
+ assert(equal(r.save, "foo"));
+ }
+}
diff --git a/libphobos/src/std/algorithm/setops.d b/libphobos/src/std/algorithm/setops.d
index 05a6e7e4dc9..ede1831028e 100644
--- a/libphobos/src/std/algorithm/setops.d
+++ b/libphobos/src/std/algorithm/setops.d
@@ -40,7 +40,7 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.com, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/algorithm/_setops.d)
+Source: $(PHOBOSSRC std/algorithm/setops.d)
Macros:
T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
@@ -49,19 +49,17 @@ module std.algorithm.setops;
import std.range.primitives;
-// FIXME
-import std.functional; // : unaryFun, binaryFun;
+import std.functional : unaryFun, binaryFun;
import std.traits;
-// FIXME
-import std.meta; // : AliasSeq, staticMap, allSatisfy, anySatisfy;
+import std.meta : AliasSeq, staticMap, allSatisfy, anySatisfy;
-import std.algorithm.sorting; // : Merge;
+import std.algorithm.sorting : Merge;
import std.typecons : No;
// cartesianProduct
/**
Lazily computes the Cartesian product of two or more ranges. The product is a
-_range of tuples of elements from each respective range.
+range of tuples of elements from each respective range.
The conditions for the two-range case are as follows:
@@ -69,8 +67,8 @@ If both ranges are finite, then one must be (at least) a
$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and the
other an $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
-If one _range is infinite and the other finite, then the finite _range must
-be a forward _range, and the infinite range can be an input _range.
+If one range is infinite and the other finite, then the finite range must
+be a forward range, and the infinite range can be an input range.
If both ranges are infinite, then both must be forward ranges.
@@ -348,7 +346,7 @@ if (!allSatisfy!(isForwardRange, R1, R2) ||
}
}
-// Issue 13091
+// https://issues.dlang.org/show_bug.cgi?id=13091
pure nothrow @safe @nogc unittest
{
int[1] a = [1];
@@ -393,7 +391,7 @@ if (ranges.length >= 2 &&
return mixin(algoFormat("tuple(%(current[%d].front%|,%))",
iota(0, current.length)));
}
- void popFront()
+ void popFront() scope
{
foreach_reverse (i, ref r; current)
{
@@ -406,25 +404,27 @@ if (ranges.length >= 2 &&
r = ranges[i].save; // rollover
}
}
- @property Result save()
+ @property Result save() scope return
{
Result copy = this;
foreach (i, r; ranges)
{
- copy.ranges[i] = r.save;
+ copy.ranges[i] = ranges[i].save;
copy.current[i] = current[i].save;
}
return copy;
}
}
- static assert(isForwardRange!Result);
+ static assert(isForwardRange!Result, Result.stringof ~ " must be a forward"
+ ~ " range");
return Result(ranges);
}
+// cartesian product of empty ranges should be empty
+// https://issues.dlang.org/show_bug.cgi?id=10693
@safe unittest
{
- // Issue 10693: cartesian product of empty ranges should be empty.
int[] a, b, c, d, e;
auto cprod = cartesianProduct(a,b,c,d,e);
assert(cprod.empty);
@@ -446,9 +446,9 @@ if (ranges.length >= 2 &&
assert(cprod.init.empty);
}
+// https://issues.dlang.org/show_bug.cgi?id=13393
@safe unittest
{
- // Issue 13393
assert(!cartesianProduct([0],[0],[0]).save.empty);
}
@@ -486,7 +486,7 @@ if (!allSatisfy!(isForwardRange, R1, R2, RR) ||
assert(is(ElementType!(typeof(N3)) == Tuple!(size_t,size_t,size_t)));
assert(canFind(N3, tuple(0, 27, 7)));
- assert(canFind(N3, tuple(50, 23, 71)));
+ assert(canFind(N3, tuple(50, 23, 11)));
assert(canFind(N3, tuple(9, 3, 0)));
}
@@ -504,10 +504,10 @@ if (!allSatisfy!(isForwardRange, R1, R2, RR) ||
assert(canFind(N4, tuple(1, 2, 3, 4)));
assert(canFind(N4, tuple(4, 3, 2, 1)));
- assert(canFind(N4, tuple(10, 31, 7, 12)));
+ assert(canFind(N4, tuple(10, 3, 1, 2)));
}
-// Issue 9878
+// https://issues.dlang.org/show_bug.cgi?id=9878
///
@safe unittest
{
@@ -546,7 +546,7 @@ pure @safe nothrow @nogc unittest
assert(D.front == front1);
}
-// Issue 13935
+// https://issues.dlang.org/show_bug.cgi?id=13935
@safe unittest
{
import std.algorithm.iteration : map;
@@ -554,12 +554,46 @@ pure @safe nothrow @nogc unittest
foreach (pair; cartesianProduct(seq, seq)) {}
}
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ static struct SystemRange
+ {
+ int[] data;
+
+ int front() @system @property inout
+ {
+ return data[0];
+ }
+
+ bool empty() @system @property inout
+ {
+ return data.length == 0;
+ }
+
+ void popFront() @system
+ {
+ data = data[1 .. $];
+ }
+
+ SystemRange save() @system
+ {
+ return this;
+ }
+ }
+
+ assert(SystemRange([1, 2]).cartesianProduct(SystemRange([3, 4]))
+ .equal([tuple(1, 3), tuple(1, 4), tuple(2, 3), tuple(2, 4)]));
+}
+
// largestPartialIntersection
/**
Given a range of sorted $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives)
-$(D ror), copies to $(D tgt) the elements that are common to most ranges, along with their number
-of occurrences. All ranges in $(D ror) are assumed to be sorted by $(D
-less). Only the most frequent $(D tgt.length) elements are returned.
+`ror`, copies to `tgt` the elements that are common to most ranges, along with their number
+of occurrences. All ranges in `ror` are assumed to be sorted by $(D
+less). Only the most frequent `tgt.length` elements are returned.
Params:
less = The predicate the ranges are sorted by.
@@ -567,27 +601,27 @@ Params:
tgt = The target range to copy common elements to.
sorted = Whether the elements copied should be in sorted order.
-The function $(D largestPartialIntersection) is useful for
+The function `largestPartialIntersection` is useful for
e.g. searching an $(LINK2 https://en.wikipedia.org/wiki/Inverted_index,
inverted index) for the documents most
likely to contain some terms of interest. The complexity of the search
-is $(BIGOH n * log(tgt.length)), where $(D n) is the sum of lengths of
+is $(BIGOH n * log(tgt.length)), where `n` is the sum of lengths of
all input ranges. This approach is faster than keeping an associative
array of the occurrences and then selecting its top items, and also
-requires less memory ($(D largestPartialIntersection) builds its
-result directly in $(D tgt) and requires no extra memory).
+requires less memory (`largestPartialIntersection` builds its
+result directly in `tgt` and requires no extra memory).
If at least one of the ranges is a multiset, then all occurences
of a duplicate element are taken into account. The result is
equivalent to merging all ranges and picking the most frequent
-$(D tgt.length) elements.
+`tgt.length` elements.
-Warning: Because $(D largestPartialIntersection) does not allocate
-extra memory, it will leave $(D ror) modified. Namely, $(D
-largestPartialIntersection) assumes ownership of $(D ror) and
+Warning: Because `largestPartialIntersection` does not allocate
+extra memory, it will leave `ror` modified. Namely, $(D
+largestPartialIntersection) assumes ownership of `ror` and
discretionarily swaps and advances elements of it. If you want $(D
ror) to preserve its contents after the call, you may want to pass a
-duplicate to $(D largestPartialIntersection) (and perhaps cache the
+duplicate to `largestPartialIntersection` (and perhaps cache the
duplicate in between calls).
*/
void largestPartialIntersection
@@ -652,13 +686,13 @@ import std.algorithm.sorting : SortOutput; // FIXME
// largestPartialIntersectionWeighted
/**
-Similar to $(D largestPartialIntersection), but associates a weight
+Similar to `largestPartialIntersection`, but associates a weight
with each distinct element in the intersection.
If at least one of the ranges is a multiset, then all occurences
of a duplicate element are taken into account. The result
is equivalent to merging all input ranges and picking the highest
-$(D tgt.length), weight-based ranking elements.
+`tgt.length`, weight-based ranking elements.
Params:
less = The predicate the ranges are sorted by.
@@ -798,15 +832,15 @@ void largestPartialIntersectionWeighted
Merges multiple sets. The input sets are passed as a
range of ranges and each is assumed to be sorted by $(D
less). Computation is done lazily, one union element at a time. The
-complexity of one $(D popFront) operation is $(BIGOH
-log(ror.length)). However, the length of $(D ror) decreases as ranges
+complexity of one `popFront` operation is $(BIGOH
+log(ror.length)). However, the length of `ror` decreases as ranges
in it are exhausted, so the complexity of a full pass through $(D
MultiwayMerge) is dependent on the distribution of the lengths of ranges
-contained within $(D ror). If all ranges have the same length $(D n)
+contained within `ror`. If all ranges have the same length `n`
(worst case scenario), the complexity of a full pass through $(D
MultiwayMerge) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D
log(ror.length)) times worse than just spanning all ranges in
-turn. The output comes sorted (unstably) by $(D less).
+turn. The output comes sorted (unstably) by `less`.
The length of the resulting range is the sum of all lengths of
the ranges passed as input. This means that all elements (duplicates
@@ -824,12 +858,15 @@ Params:
Returns:
A range of the union of the ranges in `ror`.
-Warning: Because $(D MultiwayMerge) does not allocate extra memory, it
-will leave $(D ror) modified. Namely, $(D MultiwayMerge) assumes ownership
-of $(D ror) and discretionarily swaps and advances elements of it. If
-you want $(D ror) to preserve its contents after the call, you may
-want to pass a duplicate to $(D MultiwayMerge) (and perhaps cache the
+Warning: Because `MultiwayMerge` does not allocate extra memory, it
+will leave `ror` modified. Namely, `MultiwayMerge` assumes ownership
+of `ror` and discretionarily swaps and advances elements of it. If
+you want `ror` to preserve its contents after the call, you may
+want to pass a duplicate to `MultiwayMerge` (and perhaps cache the
duplicate in between calls).
+
+See_Also: $(REF merge, std,algorithm,sorting) for an analogous function that
+ takes a static number of ranges of possibly disparate types.
*/
struct MultiwayMerge(alias less, RangeOfRanges)
{
@@ -883,7 +920,8 @@ struct MultiwayMerge(alias less, RangeOfRanges)
return;
}
// Put the popped range back in the heap
- _heap.conditionalInsert(_ror.back) || assert(false);
+ const bool worked = _heap.conditionalInsert(_ror.back);
+ assert(worked, "Failed to insert item into heap");
}
}
@@ -930,7 +968,8 @@ alias nWayUnion = multiwayMerge;
alias NWayUnion = MultiwayMerge;
/**
-Computes the union of multiple ranges. The input ranges are passed
+Computes the union of multiple ranges. The
+$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) are passed
as a range of ranges and each is assumed to be sorted by $(D
less). Computation is done lazily, one union element at a time.
`multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`.
@@ -949,7 +988,8 @@ See also: $(LREF multiwayMerge)
auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror)
{
import std.algorithm.iteration : uniq;
- return ror.multiwayMerge.uniq;
+ import std.functional : not;
+ return ror.multiwayMerge!(less).uniq!(not!less);
}
///
@@ -980,17 +1020,26 @@ auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror)
[ 7 ],
];
assert(equal(multiwayUnion(b), witness));
+
+ double[][] c =
+ [
+ [9, 8, 8, 8, 7, 6],
+ [9, 8, 6],
+ [9, 8, 5]
+ ];
+ auto witness2 = [9, 8, 7, 6, 5];
+ assert(equal(multiwayUnion!"a > b"(c), witness2));
}
/**
-Lazily computes the difference of $(D r1) and $(D r2). The two ranges
-are assumed to be sorted by $(D less). The element types of the two
+Lazily computes the difference of `r1` and `r2`. The two ranges
+are assumed to be sorted by `less`. The element types of the two
ranges must have a common type.
In the case of multisets, considering that element `a` appears `x`
-times in $(D r1) and `y` times and $(D r2), the number of occurences
-of `a` in the resulting range is going to be `x-y` if x > y or 0 othwerise.
+times in `r1` and `y` times and `r2`, the number of occurences
+of `a` in the resulting range is going to be `x-y` if x > y or 0 otherwise.
Params:
less = Predicate the given ranges are sorted by.
@@ -1048,7 +1097,7 @@ public:
///
@property auto ref front()
{
- assert(!empty);
+ assert(!empty, "Can not get front of empty SetDifference");
return r1.front;
}
@@ -1095,7 +1144,8 @@ SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2)
assert(setDifference(r, x).empty);
}
-@safe unittest // Issue 10460
+// https://issues.dlang.org/show_bug.cgi?id=10460
+@safe unittest
{
import std.algorithm.comparison : equal;
@@ -1107,8 +1157,9 @@ SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2)
}
/**
-Lazily computes the intersection of two or more input ranges $(D
-ranges). The ranges are assumed to be sorted by $(D less). The element
+Lazily computes the intersection of two or more
+$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives)
+`ranges`. The ranges are assumed to be sorted by `less`. The element
types of the ranges must have a common type.
In the case of multisets, the range with the minimum number of
@@ -1179,11 +1230,12 @@ public:
///
void popFront()
{
- assert(!empty);
+ assert(!empty, "Can not popFront of empty SetIntersection");
static if (Rs.length > 1) foreach (i, ref r; _input)
{
alias next = _input[(i + 1) % Rs.length];
- assert(!comp(r.front, next.front));
+ assert(!comp(r.front, next.front), "Set elements must not"
+ ~ " contradict the less predicate");
}
foreach (ref r; _input)
@@ -1196,7 +1248,7 @@ public:
///
@property ElementType front()
{
- assert(!empty);
+ assert(!empty, "Can not get front of empty SetIntersection");
return _input[0].front;
}
@@ -1274,20 +1326,20 @@ if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) &&
}
/**
-Lazily computes the symmetric difference of $(D r1) and $(D r2),
-i.e. the elements that are present in exactly one of $(D r1) and $(D
-r2). The two ranges are assumed to be sorted by $(D less), and the
-output is also sorted by $(D less). The element types of the two
+Lazily computes the symmetric difference of `r1` and `r2`,
+i.e. the elements that are present in exactly one of `r1` and $(D
+r2). The two ranges are assumed to be sorted by `less`, and the
+output is also sorted by `less`. The element types of the two
ranges must have a common type.
If both ranges are sets (without duplicated elements), the resulting
range is going to be a set. If at least one of the ranges is a multiset,
the number of occurences of an element `x` in the resulting range is `abs(a-b)`
-where `a` is the number of occurences of `x` in $(D r1), `b` is the number of
-occurences of `x` in $(D r2), and `abs` is the absolute value.
+where `a` is the number of occurences of `x` in `r1`, `b` is the number of
+occurences of `x` in `r2`, and `abs` is the absolute value.
If both arguments are ranges of L-values of the same type then
-$(D SetSymmetricDifference) will also be a range of L-values of
+`SetSymmetricDifference` will also be a range of L-values of
that type.
Params:
@@ -1336,7 +1388,7 @@ public:
///
void popFront()
{
- assert(!empty);
+ assert(!empty, "Can not popFront of empty SetSymmetricDifference");
if (r1.empty) r2.popFront();
else if (r2.empty) r1.popFront();
else
@@ -1348,7 +1400,8 @@ public:
}
else
{
- assert(comp(r2.front, r1.front));
+ assert(comp(r2.front, r1.front), "Elements of R1 and R2"
+ ~ " must be different");
r2.popFront();
}
}
@@ -1358,9 +1411,11 @@ public:
///
@property auto ref front()
{
- assert(!empty);
+ assert(!empty, "Can not get the front of an empty"
+ ~ " SetSymmetricDifference");
immutable chooseR1 = r2.empty || !r1.empty && comp(r1.front, r2.front);
- assert(chooseR1 || r1.empty || comp(r2.front, r1.front));
+ assert(chooseR1 || r1.empty || comp(r2.front, r1.front), "Failed to"
+ ~ " get appropriate front");
return chooseR1 ? r1.front : r2.front;
}
@@ -1410,7 +1465,8 @@ setSymmetricDifference(alias less = "a < b", R1, R2)
assert(equal(setSymmetricDifference(c, d), [1, 1, 2, 5, 6, 7, 9]));
}
-@safe unittest // Issue 10460
+// https://issues.dlang.org/show_bug.cgi?id=10460
+@safe unittest
{
import std.algorithm.comparison : equal;
@@ -1435,8 +1491,8 @@ TODO: once SetUnion got deprecated we can provide the usual definition
(= merge + filter after uniqs)
See: https://github.com/dlang/phobos/pull/4249
/**
-Lazily computes the union of two or more ranges $(D rs). The ranges
-are assumed to be sorted by $(D less). Elements in the output are
+Lazily computes the union of two or more ranges `rs`. The ranges
+are assumed to be sorted by `less`. Elements in the output are
unique. The element types of all ranges must have a common type.
Params:
diff --git a/libphobos/src/std/algorithm/sorting.d b/libphobos/src/std/algorithm/sorting.d
index 2400bca8bf7..dbfe37f2fb0 100644
--- a/libphobos/src/std/algorithm/sorting.d
+++ b/libphobos/src/std/algorithm/sorting.d
@@ -1,29 +1,28 @@
// Written in the D programming language.
/**
This is a submodule of $(MREF std, algorithm).
-It contains generic _sorting algorithms.
+It contains generic sorting algorithms.
$(SCRIPT inhibitQuickIndex = 1;)
$(BOOKTABLE Cheat Sheet,
$(TR $(TH Function Name) $(TH Description))
$(T2 completeSort,
- If $(D a = [10, 20, 30]) and $(D b = [40, 6, 15]), then
- $(D completeSort(a, b)) leaves $(D a = [6, 10, 15]) and $(D b = [20,
- 30, 40]).
- The range $(D a) must be sorted prior to the call, and as a result the
- combination $(D $(REF chain, std,range)(a, b)) is sorted.)
+ If `a = [10, 20, 30]` and `b = [40, 6, 15]`, then
+ `completeSort(a, b)` leaves `a = [6, 10, 15]` and `b = [20, 30, 40]`.
+ The range `a` must be sorted prior to the call, and as a result the
+ combination `$(REF chain, std,range)(a, b)` is sorted.)
$(T2 isPartitioned,
- $(D isPartitioned!"a < 0"([-1, -2, 1, 0, 2])) returns $(D true) because
- the predicate is $(D true) for a portion of the range and $(D false)
+ `isPartitioned!"a < 0"([-1, -2, 1, 0, 2])` returns `true` because
+ the predicate is `true` for a portion of the range and `false`
afterwards.)
$(T2 isSorted,
- $(D isSorted([1, 1, 2, 3])) returns $(D true).)
+ `isSorted([1, 1, 2, 3])` returns `true`.)
$(T2 isStrictlyMonotonic,
- $(D isStrictlyMonotonic([1, 1, 2, 3])) returns $(D false).)
+ `isStrictlyMonotonic([1, 1, 2, 3])` returns `false`.)
$(T2 ordered,
- $(D ordered(1, 1, 2, 3)) returns $(D true).)
+ `ordered(1, 1, 2, 3)` returns `true`.)
$(T2 strictlyOrdered,
- $(D strictlyOrdered(1, 1, 2, 3)) returns $(D false).)
+ `strictlyOrdered(1, 1, 2, 3)` returns `false`.)
$(T2 makeIndex,
Creates a separate index for a range.)
$(T2 merge,
@@ -36,10 +35,13 @@ $(T2 nextEvenPermutation,
$(T2 nextPermutation,
Computes the next lexicographically greater permutation of a range
in-place.)
+$(T2 nthPermutation,
+ Computes the nth permutation of a range
+ in-place.)
$(T2 partialSort,
- If $(D a = [5, 4, 3, 2, 1]), then $(D partialSort(a, 3)) leaves
- $(D a[0 .. 3] = [1, 2, 3]).
- The other elements of $(D a) are left in an unspecified order.)
+ If `a = [5, 4, 3, 2, 1]`, then `partialSort(a, 3)` leaves
+ `a[0 .. 3] = [1, 2, 3]`.
+ The other elements of `a` are left in an unspecified order.)
$(T2 partition,
Partitions a range according to a unary predicate.)
$(T2 partition3,
@@ -55,7 +57,7 @@ $(T2 schwartzSort,
$(T2 sort,
Sorts.)
$(T2 topN,
- Separates the top elements in a range.)
+ Separates the top elements in a range, akin to $(LINK2 https://en.wikipedia.org/wiki/Quickselect, Quickselect).)
$(T2 topNCopy,
Copies out the top elements of a range.)
$(T2 topNIndex,
@@ -68,7 +70,7 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.com, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/algorithm/_sorting.d)
+Source: $(PHOBOSSRC std/algorithm/sorting.d)
Macros:
T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
@@ -76,33 +78,34 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
module std.algorithm.sorting;
import std.algorithm.mutation : SwapStrategy;
-import std.functional; // : unaryFun, binaryFun;
+import std.functional : unaryFun, binaryFun;
import std.range.primitives;
-import std.typecons : Flag;
-// FIXME
-import std.meta; // : allSatisfy;
-import std.range; // : SortedRange;
+import std.typecons : Flag, No, Yes;
+import std.meta : allSatisfy;
+import std.range : SortedRange;
import std.traits;
/**
Specifies whether the output of certain algorithm is desired in sorted
format.
-If set to $(D SortOutput.no), the output should not be sorted.
+If set to `SortOutput.no`, the output should not be sorted.
-Otherwise if set to $(D SortOutput.yes), the output should be sorted.
+Otherwise if set to `SortOutput.yes`, the output should be sorted.
*/
alias SortOutput = Flag!"sortOutput";
// completeSort
/**
-Sorts the random-access range $(D chain(lhs, rhs)) according to
-predicate $(D less). The left-hand side of the range $(D lhs) is
-assumed to be already sorted; $(D rhs) is assumed to be unsorted. The
-exact strategy chosen depends on the relative sizes of $(D lhs) and
-$(D rhs). Performs $(BIGOH lhs.length + rhs.length * log(rhs.length))
+Sorts the random-access range `chain(lhs, rhs)` according to
+predicate `less`.
+
+The left-hand side of the range `lhs` is assumed to be already sorted;
+`rhs` is assumed to be unsorted.
+The exact strategy chosen depends on the relative sizes of `lhs` and
+`rhs`. Performs $(BIGOH lhs.length + rhs.length * log(rhs.length))
(best case) to $(BIGOH (lhs.length + rhs.length) * log(lhs.length +
-rhs.length)) (worst-case) evaluations of $(D swap).
+rhs.length)) (worst-case) evaluations of $(REF_ALTTEXT swap, swap, std,algorithm,mutation).
Params:
less = The predicate to sort by.
@@ -112,8 +115,9 @@ Params:
sorted.
*/
void completeSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
- RandomAccessRange1, RandomAccessRange2)(SortedRange!(RandomAccessRange1, less) lhs, RandomAccessRange2 rhs)
-if (hasLength!(RandomAccessRange2) && hasSlicing!(RandomAccessRange2))
+ Lhs , Rhs)(SortedRange!(Lhs, less) lhs, Rhs rhs)
+if (hasLength!(Rhs) && hasSlicing!(Rhs)
+ && hasSwappableElements!Lhs && hasSwappableElements!Rhs)
{
import std.algorithm.mutation : bringToFront;
import std.range : chain, assumeSorted;
@@ -143,8 +147,8 @@ if (hasLength!(RandomAccessRange2) && hasSlicing!(RandomAccessRange2))
// isSorted
/**
Checks whether a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
-is sorted according to the comparison operation $(D less). Performs $(BIGOH r.length)
-evaluations of $(D less).
+is sorted according to the comparison operation `less`. Performs $(BIGOH r.length)
+evaluations of `less`.
Unlike `isSorted`, `isStrictlyMonotonic` does not allow for equal values,
i.e. values for which both `less(a, b)` and `less(b, a)` are false.
@@ -222,7 +226,7 @@ if (isForwardRange!(Range))
{
import std.conv : to;
- // Issue 9457
+ // https://issues.dlang.org/show_bug.cgi?id=9457
auto x = "abcd";
assert(isSorted(x));
auto y = "acbd";
@@ -291,16 +295,16 @@ if (isForwardRange!Range)
}
/**
-Like $(D isSorted), returns $(D true) if the given $(D values) are ordered
-according to the comparison operation $(D less). Unlike $(D isSorted), takes values
+Like `isSorted`, returns `true` if the given `values` are ordered
+according to the comparison operation `less`. Unlike `isSorted`, takes values
directly instead of structured in a range.
-$(D ordered) allows repeated values, e.g. $(D ordered(1, 1, 2)) is $(D true). To verify
-that the values are ordered strictly monotonically, use $(D strictlyOrdered);
-$(D strictlyOrdered(1, 1, 2)) is $(D false).
+`ordered` allows repeated values, e.g. `ordered(1, 1, 2)` is `true`. To verify
+that the values are ordered strictly monotonically, use `strictlyOrdered`;
+`strictlyOrdered(1, 1, 2)` is `false`.
With either function, the predicate must be a strict ordering. For example,
-using $(D "a <= b") instead of $(D "a < b") is incorrect and will cause failed
+using `"a <= b"` instead of `"a < b"` is incorrect and will cause failed
assertions.
Params:
@@ -308,8 +312,8 @@ Params:
less = The comparison predicate
Returns:
- $(D true) if the values are ordered; $(D ordered) allows for duplicates,
- $(D strictlyOrdered) does not.
+ `true` if the values are ordered; `ordered` allows for duplicates,
+ `strictlyOrdered` does not.
*/
bool ordered(alias less = "a < b", T...)(T values)
@@ -366,16 +370,16 @@ if (is(typeof(ordered!less(values))))
// partition
/**
-Partitions a range in two using the given $(D predicate).
-Specifically, reorders the range $(D r = [left, right$(RPAREN)) using $(D swap)
-such that all elements $(D i) for which $(D predicate(i)) is $(D true) come
-before all elements $(D j) for which $(D predicate(j)) returns $(D false).
+Partitions a range in two using the given `predicate`.
+
+Specifically, reorders the range `r = [left, right$(RPAREN)` using $(REF_ALTTEXT swap, swap, std,algorithm,mutation)
+such that all elements `i` for which `predicate(i)` is `true` come
+before all elements `j` for which `predicate(j)` returns `false`.
Performs $(BIGOH r.length) (if unstable or semistable) or $(BIGOH
-r.length * log(r.length)) (if stable) evaluations of $(D less) and $(D
-swap). The unstable version computes the minimum possible evaluations
-of $(D swap) (roughly half of those performed by the semistable
-version).
+r.length * log(r.length)) (if stable) evaluations of `less` and $(REF_ALTTEXT swap, swap, std,algorithm,mutation).
+The unstable version computes the minimum possible evaluations of `swap`
+(roughly half of those performed by the semistable version).
Params:
predicate = The predicate to partition by.
@@ -384,20 +388,18 @@ Params:
Returns:
-The right part of $(D r) after partitioning.
+The right part of `r` after partitioning.
-If $(D ss == SwapStrategy.stable), $(D partition) preserves the relative
-ordering of all elements $(D a), $(D b) in $(D r) for which $(D predicate(a) ==
-predicate(b)). If $(D ss == SwapStrategy.semistable), $(D partition) preserves
-the relative ordering of all elements $(D a), $(D b) in the left part of $(D r)
-for which $(D predicate(a) == predicate(b)).
-
-See_Also:
- STL's $(HTTP sgi.com/tech/stl/_partition.html, _partition)$(BR)
- STL's $(HTTP sgi.com/tech/stl/stable_partition.html, stable_partition)
+If `ss == SwapStrategy.stable`, `partition` preserves the relative
+ordering of all elements `a`, `b` in `r` for which
+`predicate(a) == predicate(b)`.
+If `ss == SwapStrategy.semistable`, `partition` preserves
+the relative ordering of all elements `a`, `b` in the left part of `r`
+for which `predicate(a) == predicate(b)`.
*/
Range partition(alias predicate, SwapStrategy ss, Range)(Range r)
-if (ss == SwapStrategy.stable && isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range)
+if (ss == SwapStrategy.stable && isRandomAccessRange!(Range) && hasLength!Range &&
+ hasSlicing!Range && hasSwappableElements!Range)
{
import std.algorithm.mutation : bringToFront;
@@ -464,7 +466,7 @@ if (ss != SwapStrategy.stable && isInputRange!Range && hasSwappableElements!Rang
++lo;
}
// found the left bound
- assert(lo <= hi);
+ assert(lo <= hi, "lo must be <= hi");
for (;;)
{
if (lo == hi) return r[lo .. r.length];
@@ -489,7 +491,7 @@ if (ss != SwapStrategy.stable && isInputRange!Range && hasSwappableElements!Rang
result.popFront();
}
// found the left bound
- assert(!r.empty);
+ assert(!r.empty, "r must not be empty");
for (;;)
{
if (pred(r.back)) break;
@@ -572,22 +574,23 @@ if (ss != SwapStrategy.stable && isInputRange!Range && hasSwappableElements!Rang
// pivotPartition
/**
-
Partitions `r` around `pivot` using comparison function `less`, algorithm akin
to $(LINK2 https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme,
-Hoare partition). Specifically, permutes elements of `r` and returns
-an index $(D k < r.length) such that:
+Hoare partition).
+
+Specifically, permutes elements of `r` and returns
+an index `k < r.length` such that:
$(UL
$(LI `r[pivot]` is swapped to `r[k]`)
-$(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(r[k], e))
+$(LI All elements `e` in subrange `r[0 .. k]` satisfy `!less(r[k], e)`
(i.e. `r[k]` is greater than or equal to each element to its left according to
predicate `less`))
-$(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(e,
-r[k])) (i.e. `r[k]` is less than or equal to each element to its right
+$(LI All elements `e` in subrange `r[k .. $]` satisfy `!less(e, r[k])`
+(i.e. `r[k]` is less than or equal to each element to its right
according to predicate `less`)))
If `r` contains equivalent elements, multiple permutations of `r` satisfy these
@@ -602,7 +605,7 @@ less = The predicate used for comparison, modeled as a
equivalence)
r = The range being partitioned
pivot = The index of the pivot for partitioning, must be less than `r.length` or
-`0` is `r.length` is `0`
+`0` if `r.length` is `0`
Returns:
The new position of the pivot
@@ -615,9 +618,10 @@ Keynote), Andrei Alexandrescu.
*/
size_t pivotPartition(alias less = "a < b", Range)
(Range r, size_t pivot)
-if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range)
+if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && hasAssignableElements!Range)
{
- assert(pivot < r.length || r.length == 0 && pivot == 0);
+ assert(pivot < r.length || r.length == 0 && pivot == 0, "pivot must be"
+ ~ " less than the length of r or r must be empty and pivot zero");
if (r.length <= 1) return 0;
import std.algorithm.mutation : swapAt, move;
alias lt = binaryFun!less;
@@ -635,17 +639,20 @@ if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range)
auto p = r[0];
// Plant the pivot in the end as well as a sentinel
size_t lo = 0, hi = r.length - 1;
- auto save = move(r[hi]);
+ auto save = r.moveAt(hi);
r[hi] = p; // Vacancy is in r[$ - 1] now
// Start process
for (;;)
{
// Loop invariant
- version (unittest)
+ version (StdUnittest)
{
- import std.algorithm.searching : all;
- assert(r[0 .. lo].all!(x => !lt(p, x)));
- assert(r[hi + 1 .. r.length].all!(x => !lt(x, p)));
+ // this used to import std.algorithm.all, but we want to save
+ // imports when unittests are enabled if possible.
+ foreach (x; r[0 .. lo])
+ assert(!lt(p, x), "p must not be less than x");
+ foreach (x; r[hi+1 .. r.length])
+ assert(!lt(x, p), "x must not be less than p");
}
do ++lo; while (lt(r[lo], p));
r[hi] = r[lo];
@@ -656,17 +663,17 @@ if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range)
// Vacancy is not in r[hi]
}
// Fixup
- assert(lo - hi <= 2);
- assert(!lt(p, r[hi]));
+ assert(lo - hi <= 2, "Following compare not possible");
+ assert(!lt(p, r[hi]), "r[hi] must not be less than p");
if (lo == hi + 2)
{
- assert(!lt(r[hi + 1], p));
+ assert(!lt(r[hi + 1], p), "r[hi + 1] must not be less than p");
r[lo] = r[hi + 1];
--lo;
}
r[lo] = save;
if (lt(p, save)) --lo;
- assert(!lt(p, r[lo]));
+ assert(!lt(p, r[lo]), "r[lo] must not be less than p");
}
else
{
@@ -679,14 +686,14 @@ if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range)
if (!lt(r[lo], r[0])) break;
}
// found the left bound: r[lo] >= r[0]
- assert(lo <= hi);
+ assert(lo <= hi, "lo must be less or equal than hi");
for (;; --hi)
{
if (lo >= hi) break loop;
if (!lt(r[0], r[hi])) break;
}
// found the right bound: r[hi] <= r[0], swap & make progress
- assert(!lt(r[lo], r[hi]));
+ assert(!lt(r[lo], r[hi]), "r[lo] must not be less than r[hi]");
r.swapAt(lo, hi);
}
--lo;
@@ -747,17 +754,18 @@ if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range)
assert(a == [ 42, 42 ]);
import std.algorithm.iteration : map;
- import std.random;
- import std.stdio;
- auto s = unpredictableSeed;
- auto g = Random(s);
+ import std.array : array;
+ import std.format : format;
+ import std.random : Random, uniform, Xorshift;
+ import std.range : iota;
+ auto s = 123_456_789;
+ auto g = Xorshift(s);
a = iota(0, uniform(1, 1000, g))
.map!(_ => uniform(-1000, 1000, g))
.array;
- scope(failure) writeln("RNG seed was ", s);
pivot = pivotPartition!less(a, a.length / 2);
- assert(a[0 .. pivot].all!(x => x <= a[pivot]));
- assert(a[pivot .. $].all!(x => x >= a[pivot]));
+ assert(a[0 .. pivot].all!(x => x <= a[pivot]), "RNG seed: %d".format(s));
+ assert(a[pivot .. $].all!(x => x >= a[pivot]), "RNG seed: %d".format(s));
}
test!"a < b";
static bool myLess(int a, int b)
@@ -773,7 +781,7 @@ if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range)
Params:
pred = The predicate that the range should be partitioned by.
r = The range to check.
-Returns: $(D true) if $(D r) is partitioned according to predicate $(D pred).
+Returns: `true` if `r` is partitioned according to predicate `pred`.
*/
bool isPartitioned(alias pred, Range)(Range r)
if (isForwardRange!(Range))
@@ -799,13 +807,15 @@ if (isForwardRange!(Range))
// partition3
/**
-Rearranges elements in $(D r) in three adjacent ranges and returns
-them. The first and leftmost range only contains elements in $(D r)
-less than $(D pivot). The second and middle range only contains
-elements in $(D r) that are equal to $(D pivot). Finally, the third
-and rightmost range only contains elements in $(D r) that are greater
-than $(D pivot). The less-than test is defined by the binary function
-$(D less).
+Rearranges elements in `r` in three adjacent ranges and returns
+them.
+
+The first and leftmost range only contains elements in `r`
+less than `pivot`. The second and middle range only contains
+elements in `r` that are equal to `pivot`. Finally, the third
+and rightmost range only contains elements in `r` that are greater
+than `pivot`. The less-than test is defined by the binary function
+`less`.
Params:
less = The predicate to use for the rearrangement.
@@ -817,7 +827,7 @@ Returns:
A $(REF Tuple, std,typecons) of the three resulting ranges. These ranges are
slices of the original range.
-BUGS: stable $(D partition3) has not been implemented yet.
+BUGS: stable `partition3` has not been implemented yet.
*/
auto partition3(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, E)
(Range r, E pivot)
@@ -843,15 +853,15 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range
for (;; ++j)
{
if (j == k) break bigloop;
- assert(j < r.length);
+ assert(j < r.length, "j must be less than r.length");
if (lessFun(r[j], pivot)) continue;
if (lessFun(pivot, r[j])) break;
r.swapAt(i++, j);
}
- assert(j < k);
+ assert(j < k, "j must be less than k");
for (;;)
{
- assert(k > 0);
+ assert(k > 0, "k must be positive");
if (!lessFun(pivot, r[--k]))
{
if (lessFun(r[k], pivot)) break;
@@ -886,9 +896,9 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range
@safe unittest
{
- import std.random : Random, uniform, unpredictableSeed;
+ import std.random : Random = Xorshift, uniform;
- immutable uint[] seeds = [3923355730, 1927035882, unpredictableSeed];
+ immutable uint[] seeds = [3923355730, 1927035882];
foreach (s; seeds)
{
auto r = Random(s);
@@ -916,8 +926,9 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range
// makeIndex
/**
-Computes an index for $(D r) based on the comparison $(D less). The
-index is a sorted array of pointers or indices into the original
+Computes an index for `r` based on the comparison `less`.
+
+The index is a sorted array of pointers or indices into the original
range. This technique is similar to sorting, but it is more flexible
because (1) it allows "sorting" of immutable collections, (2) allows
binary search even if the original collection does not offer random
@@ -926,15 +937,15 @@ and (4) may be faster when dealing with large objects. However, using
an index may also be slower under certain circumstances due to the
extra indirection, and is always larger than a sorting-based solution
because it needs space for the index in addition to the original
-collection. The complexity is the same as $(D sort)'s.
+collection. The complexity is the same as `sort`'s.
-The first overload of $(D makeIndex) writes to a range containing
+The first overload of `makeIndex` writes to a range containing
pointers, and the second writes to a range containing offsets. The
-first overload requires $(D Range) to be a
+first overload requires `Range` to be a
$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives), and the
latter requires it to be a random-access range.
-$(D makeIndex) overwrites its second argument with the result, but
+`makeIndex` overwrites its second argument with the result, but
never reallocates it.
Params:
@@ -943,11 +954,12 @@ Params:
r = The range to index.
index = The resulting index.
-Returns: The pointer-based version returns a $(D SortedRange) wrapper
-over index, of type $(D SortedRange!(RangeIndex, (a, b) =>
-binaryFun!less(*a, *b))) thus reflecting the ordering of the
-index. The index-based version returns $(D void) because the ordering
-relation involves not only $(D index) but also $(D r).
+Returns: The pointer-based version returns a `SortedRange` wrapper
+over index, of type
+`SortedRange!(RangeIndex, (a, b) => binaryFun!less(*a, *b))`
+thus reflecting the ordering of the
+index. The index-based version returns `void` because the ordering
+relation involves not only `index` but also `r`.
Throws: If the second argument's length is less than that of the range
indexed, an exception is thrown.
@@ -960,7 +972,7 @@ makeIndex(
RangeIndex)
(Range r, RangeIndex index)
if (isForwardRange!(Range) && isRandomAccessRange!(RangeIndex)
- && is(ElementType!(RangeIndex) : ElementType!(Range)*))
+ && is(ElementType!(RangeIndex) : ElementType!(Range)*) && hasAssignableElements!RangeIndex)
{
import std.algorithm.internal : addressOf;
import std.exception : enforce;
@@ -984,7 +996,7 @@ void makeIndex(
(Range r, RangeIndex index)
if (isRandomAccessRange!Range && !isInfinite!Range &&
isRandomAccessRange!RangeIndex && !isInfinite!RangeIndex &&
- isIntegral!(ElementType!RangeIndex))
+ isIntegral!(ElementType!RangeIndex) && hasAssignableElements!RangeIndex)
{
import std.conv : to;
import std.exception : enforce;
@@ -1059,6 +1071,7 @@ if (isRandomAccessRange!Range && !isInfinite!Range &&
@safe unittest
{
import std.algorithm.comparison : equal;
+ import std.range : iota;
ubyte[256] index = void;
iota(256).makeIndex(index[]);
@@ -1085,8 +1098,8 @@ if (Rs.length >= 2 &&
}
import std.functional : binaryFun;
+ import std.meta : anySatisfy;
import std.traits : isCopyable;
- import std.typetuple : anySatisfy;
private alias comp = binaryFun!less;
private alias ElementType = CommonType!(staticMap!(.ElementType, Rs));
@@ -1119,7 +1132,7 @@ if (Rs.length >= 2 &&
foreach (i, _; Rs)
{
case i:
- assert(!source[i].empty);
+ assert(!source[i].empty, "Can not get front of empty Merge");
return source[i].front;
}
}
@@ -1158,8 +1171,7 @@ if (Rs.length >= 2 &&
{
if (!source[i].empty)
{
- assert(previousFront == source[i].front ||
- comp(previousFront, source[i].front),
+ assert(!comp(source[i].front, previousFront),
"Input " ~ i.stringof ~ " is unsorted"); // @nogc
}
}
@@ -1182,7 +1194,7 @@ if (Rs.length >= 2 &&
foreach (i, _; Rs)
{
case i:
- assert(!source[i].empty);
+ assert(!source[i].empty, "Can not get back of empty Merge");
return source[i].back;
}
}
@@ -1225,8 +1237,7 @@ if (Rs.length >= 2 &&
{
if (!source[i].empty)
{
- assert(previousBack == source[i].back ||
- comp(source[i].back, previousBack),
+ assert(!comp(previousBack, source[i].back),
"Input " ~ i.stringof ~ " is unsorted"); // @nogc
}
}
@@ -1273,7 +1284,9 @@ if (Rs.length >= 2 &&
/**
Merge multiple sorted ranges `rs` with less-than predicate function `pred`
into one single sorted output range containing the sorted union of the
- elements of inputs. Duplicates are not eliminated, meaning that the total
+ elements of inputs.
+
+ Duplicates are not eliminated, meaning that the total
number of elements in the output is the sum of all elements in the ranges
passed to it; the `length` member is offered if all inputs also have
`length`. The element types of all the inputs must have a common type
@@ -1308,6 +1321,9 @@ All of its inputs are assumed to be sorted. This can mean that inputs are
If any of the inputs `rs` is infinite so is the result (`empty` being always
`false`).
+
+See_Also: $(REF multiwayMerge, std,algorithm,setops) for an analogous function
+ that merges a dynamic number of ranges.
*/
Merge!(less, Rs) merge(alias less = "a < b", Rs...)(Rs rs)
if (Rs.length >= 2 &&
@@ -1427,6 +1443,37 @@ if (Rs.length >= 2 &&
assert(m.empty);
}
+// Issue 21810: Check for sortedness must not use `==`
+@nogc @safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ static immutable a = [
+ tuple(1, 1),
+ tuple(3, 1),
+ tuple(3, 2),
+ tuple(5, 1),
+ ];
+ static immutable b = [
+ tuple(2, 1),
+ tuple(3, 1),
+ tuple(4, 1),
+ tuple(4, 2),
+ ];
+ static immutable r = [
+ tuple(1, 1),
+ tuple(2, 1),
+ tuple(3, 1),
+ tuple(3, 2),
+ tuple(3, 1),
+ tuple(4, 1),
+ tuple(4, 2),
+ tuple(5, 1),
+ ];
+ assert(merge!"a[0] < b[0]"(a, b).equal(r));
+}
+
private template validPredicates(E, less...)
{
static if (less.length == 0)
@@ -1440,24 +1487,23 @@ private template validPredicates(E, less...)
}
/**
-$(D auto multiSort(Range)(Range r)
- if (validPredicates!(ElementType!Range, less));)
+Sorts a range by multiple keys.
-Sorts a range by multiple keys. The call $(D multiSort!("a.id < b.id",
-"a.date > b.date")(r)) sorts the range $(D r) by $(D id) ascending,
-and sorts elements that have the same $(D id) by $(D date)
+The call $(D multiSort!("a.id < b.id",
+"a.date > b.date")(r)) sorts the range `r` by `id` ascending,
+and sorts elements that have the same `id` by `date`
descending. Such a call is equivalent to $(D sort!"a.id != b.id ? a.id
-< b.id : a.date > b.date"(r)), but $(D multiSort) is faster because it
+< b.id : a.date > b.date"(r)), but `multiSort` is faster because it
does fewer comparisons (in addition to being more convenient).
Returns:
- The initial range wrapped as a $(D SortedRange) with its predicates
+ The initial range wrapped as a `SortedRange` with its predicates
converted to an equivalent single predicate.
*/
template multiSort(less...) //if (less.length > 1)
{
auto multiSort(Range)(Range r)
- if (validPredicates!(ElementType!Range, less))
+ if (validPredicates!(ElementType!Range, less) && hasSwappableElements!Range)
{
import std.meta : AliasSeq;
import std.range : assumeSorted;
@@ -1485,6 +1531,17 @@ template multiSort(less...) //if (less.length > 1)
}
}
+///
+@safe unittest
+{
+ import std.algorithm.mutation : SwapStrategy;
+ static struct Point { int x, y; }
+ auto pts1 = [ Point(0, 0), Point(5, 5), Point(0, 1), Point(0, 2) ];
+ auto pts2 = [ Point(0, 0), Point(0, 1), Point(0, 2), Point(5, 5) ];
+ multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1);
+ assert(pts1 == pts2);
+}
+
private bool multiSortPredFun(Range, funs...)(ElementType!Range a, ElementType!Range b)
{
foreach (f; funs)
@@ -1526,17 +1583,6 @@ private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r)
}
}
-///
-@safe unittest
-{
- import std.algorithm.mutation : SwapStrategy;
- static struct Point { int x, y; }
- auto pts1 = [ Point(0, 0), Point(5, 5), Point(0, 1), Point(0, 2) ];
- auto pts2 = [ Point(0, 0), Point(0, 1), Point(0, 2), Point(5, 5) ];
- multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1);
- assert(pts1 == pts2);
-}
-
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -1556,7 +1602,8 @@ private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r)
assert(pts4.multiSort!("a > b").release.equal(iota(10).retro));
}
-@safe unittest //issue 9160 (L-value only comparators)
+//https://issues.dlang.org/show_bug.cgi?id=9160 (L-value only comparators)
+@safe unittest
{
static struct A
{
@@ -1580,7 +1627,9 @@ private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r)
assert(points[1] == A(4, 1));
}
-@safe unittest // issue 16179 (cannot access frame of function)
+// cannot access frame of function
+// https://issues.dlang.org/show_bug.cgi?id=16179
+@safe unittest
{
auto arr = [[1, 2], [2, 0], [1, 0], [1, 1]];
int c = 3;
@@ -1592,7 +1641,8 @@ private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r)
assert(arr == [[1, 0], [1, 1], [1, 2], [2, 0]]);
}
-@safe unittest //Issue 16413 - @system comparison function
+// https://issues.dlang.org/show_bug.cgi?id=16413 - @system comparison function
+@safe unittest
{
bool lt(int a, int b) { return a < b; } static @system
auto a = [2, 1];
@@ -1676,7 +1726,7 @@ private void shortSort(alias less, Range)(Range r)
break;
}
- assert(r.length >= 6);
+ assert(r.length >= 6, "r must have more than 5 elements");
/* The last 5 elements of the range are sorted. Proceed with expanding the
sorted portion downward. */
immutable maxJ = r.length - 2;
@@ -1687,17 +1737,30 @@ private void shortSort(alias less, Range)(Range r)
auto t = r[0]; if (pred(t, r[0])) r[0] = r[0];
}))) // Can we afford to temporarily invalidate the array?
{
+ import core.lifetime : move;
+
size_t j = i + 1;
- auto temp = r[i];
+ static if (hasLvalueElements!Range)
+ auto temp = move(r[i]);
+ else
+ auto temp = r[i];
+
if (pred(r[j], temp))
{
do
{
- r[j - 1] = r[j];
+ static if (hasLvalueElements!Range)
+ trustedMoveEmplace(r[j], r[j - 1]);
+ else
+ r[j - 1] = r[j];
++j;
}
while (j < r.length && pred(r[j], temp));
- r[j - 1] = temp;
+
+ static if (hasLvalueElements!Range)
+ trustedMoveEmplace(temp, r[j - 1]);
+ else
+ r[j - 1] = move(temp);
}
}
else
@@ -1714,9 +1777,16 @@ private void shortSort(alias less, Range)(Range r)
}
}
+/// @trusted wrapper for moveEmplace
+private void trustedMoveEmplace(T)(ref T source, ref T target) @trusted
+{
+ import core.lifetime : moveEmplace;
+ moveEmplace(source, target);
+}
+
@safe unittest
{
- import std.random : Random, uniform;
+ import std.random : Random = Xorshift, uniform;
auto rnd = Random(1);
auto a = new int[uniform(100, 200, rnd)];
@@ -1734,7 +1804,7 @@ Sorts the first 5 elements exactly of range r.
*/
private void sort5(alias lt, Range)(Range r)
{
- assert(r.length >= 5);
+ assert(r.length >= 5, "r must have more than 4 elements");
import std.algorithm.mutation : swapAt;
@@ -1748,7 +1818,8 @@ private void sort5(alias lt, Range)(Range r)
r.swapAt(0, 2);
r.swapAt(1, 3);
}
- assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[3], r[2]));
+ assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[3], r[2]), "unexpected"
+ ~ " order");
// 3. Insert 4 into [0, 1, 3]
if (lt(r[4], r[1]))
@@ -1764,10 +1835,11 @@ private void sort5(alias lt, Range)(Range r)
{
r.swapAt(3, 4);
}
- assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[4], r[3]));
+ assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[4], r[3]), "unexpected"
+ ~ " order");
// 4. Insert 2 into [0, 1, 3, 4] (note: we already know the last is greater)
- assert(!lt(r[4], r[2]));
+ assert(!lt(r[4], r[2]), "unexpected order");
if (lt(r[2], r[1]))
{
r.swapAt(1, 2);
@@ -1787,6 +1859,7 @@ private void sort5(alias lt, Range)(Range r)
{
import std.algorithm.iteration : permutations;
import std.algorithm.mutation : copy;
+ import std.range : iota;
int[5] buf;
foreach (per; iota(5).permutations)
@@ -1799,14 +1872,15 @@ private void sort5(alias lt, Range)(Range r)
// sort
/**
-Sorts a random-access range according to the predicate $(D less). Performs
-$(BIGOH r.length * log(r.length)) evaluations of $(D less). If `less` involves
+Sorts a random-access range according to the predicate `less`.
+
+Performs $(BIGOH r.length * log(r.length)) evaluations of `less`. If `less` involves
expensive computations on the _sort key, it may be worthwhile to use
$(LREF schwartzSort) instead.
-Stable sorting requires $(D hasAssignableElements!Range) to be true.
+Stable sorting requires `hasAssignableElements!Range` to be true.
-$(D sort) returns a $(REF SortedRange, std,range) over the original range,
+`sort` returns a $(REF SortedRange, std,range) over the original range,
allowing functions that can take advantage of sorted data to know that the
range is sorted and adjust accordingly. The $(REF SortedRange, std,range) is a
wrapper around the original range, so both it and the original range are sorted.
@@ -1815,14 +1889,14 @@ they $(I can) know that $(REF SortedRange, std,range) has been sorted.
Preconditions:
-The predicate is expected to satisfy certain rules in order for $(D sort) to
+The predicate is expected to satisfy certain rules in order for `sort` to
behave as expected - otherwise, the program may fail on certain inputs (but not
-others) when not compiled in release mode, due to the cursory $(D assumeSorted)
-check. Specifically, $(D sort) expects $(D less(a,b) && less(b,c)) to imply
-$(D less(a,c)) (transitivity), and, conversely, $(D !less(a,b) && !less(b,c)) to
-imply $(D !less(a,c)). Note that the default predicate ($(D "a < b")) does not
+others) when not compiled in release mode, due to the cursory `assumeSorted`
+check. Specifically, `sort` expects `less(a,b) && less(b,c)` to imply
+`less(a,c)` (transitivity), and, conversely, `!less(a,b) && !less(b,c)` to
+imply `!less(a,c)`. Note that the default predicate (`"a < b"`) does not
always satisfy these conditions for floating point types, because the expression
-will always be $(D false) when either $(D a) or $(D b) is NaN.
+will always be `false` when either `a` or `b` is NaN.
Use $(REF cmp, std,math) instead.
Params:
@@ -1830,8 +1904,8 @@ Params:
ss = The swapping strategy to use.
r = The range to sort.
-Returns: The initial range wrapped as a $(D SortedRange) with the predicate
-$(D binaryFun!less).
+Returns: The initial range wrapped as a `SortedRange` with the predicate
+`binaryFun!less`.
Algorithms: $(HTTP en.wikipedia.org/wiki/Introsort, Introsort) is used for unstable sorting and
$(HTTP en.wikipedia.org/wiki/Timsort, Timsort) is used for stable sorting.
@@ -1916,7 +1990,8 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan];
import std.algorithm.comparison : equal;
- import std.math : cmp, isIdentical;
+ import std.math.operations : cmp;
+ import std.math.traits : isIdentical;
sort!((a, b) => cmp(a, b) < 0)(numbers);
@@ -1927,7 +2002,10 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
@safe unittest
{
// Simple regression benchmark
- import std.algorithm.iteration, std.algorithm.mutation, std.random;
+ import std.algorithm.iteration, std.algorithm.mutation;
+ import std.array : array;
+ import std.random : Random, uniform;
+ import std.range : iota;
Random rng;
int[] a = iota(20148).map!(_ => uniform(-1000, 1000, rng)).array;
static uint comps;
@@ -1941,7 +2019,7 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
// This should get smaller with time. On occasion it may go larger, but only
// if there's thorough justification.
- debug enum uint watermark = 1676280;
+ debug enum uint watermark = 1676220;
else enum uint watermark = 1676220;
import std.conv;
@@ -1955,12 +2033,12 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
{
import std.algorithm.internal : rndstuff;
import std.algorithm.mutation : swapRanges;
- import std.random : Random, unpredictableSeed, uniform;
+ import std.random : Random = Xorshift, uniform;
import std.uni : toUpper;
// sort using delegate
auto a = new int[100];
- auto rnd = Random(unpredictableSeed);
+ auto rnd = Random(123_456_789);
foreach (ref e; a)
{
e = uniform(-100, 100, rnd);
@@ -2002,14 +2080,14 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
assert(isSorted!("toUpper(a) < toUpper(b)")(b));
{
- // Issue 10317
+ // https://issues.dlang.org/show_bug.cgi?id=10317
enum E_10317 { a, b }
auto a_10317 = new E_10317[10];
sort(a_10317);
}
{
- // Issue 7767
+ // https://issues.dlang.org/show_bug.cgi?id=7767
// Unstable sort should complete without an excessive number of predicate calls
// This would suggest it's running in quadratic time
@@ -2050,16 +2128,29 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
r.sort();
assert(proxySwapCalled);
}
+
+ // https://issues.dlang.org/show_bug.cgi?id=20751
+ {
+ static bool refPred(ref int a, ref int b)
+ {
+ return a < b;
+ }
+
+ auto sortedArr = [5,4,3,2,1].sort!refPred;
+ sortedArr.equalRange(3);
+ }
}
private void quickSortImpl(alias less, Range)(Range r, size_t depth)
{
import std.algorithm.comparison : min, max;
import std.algorithm.mutation : swap, swapAt;
+ import std.conv : to;
alias Elem = ElementType!(Range);
enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof);
- static assert(shortSortGetsBetter >= 1);
+ static assert(shortSortGetsBetter >= 1, Elem.stringof ~ " "
+ ~ to!string(Elem.sizeof));
// partition
while (r.length > shortSortGetsBetter)
@@ -2114,13 +2205,15 @@ package(std) template HeapOps(alias less, Range)
{
import std.algorithm.mutation : swapAt;
- static assert(isRandomAccessRange!Range);
- static assert(hasLength!Range);
- static assert(hasSwappableElements!Range || hasAssignableElements!Range);
+ static assert(isRandomAccessRange!Range, Range.stringof ~ " must be a"
+ ~ " RandomAccessRange");
+ static assert(hasLength!Range, Range.stringof ~ " must have length");
+ static assert(hasSwappableElements!Range || hasAssignableElements!Range,
+ Range.stringof ~ " must have swappable of assignable Elements");
alias lessFun = binaryFun!less;
- //template because of @@@12410@@@
+ //template because of https://issues.dlang.org/show_bug.cgi?id=12410
void heapSort()(Range r)
{
// If true, there is nothing to do
@@ -2135,7 +2228,7 @@ package(std) template HeapOps(alias less, Range)
}
}
- //template because of @@@12410@@@
+ //template because of https://issues.dlang.org/show_bug.cgi?id=12410
void buildHeap()(Range r)
{
immutable n = r.length;
@@ -2143,7 +2236,7 @@ package(std) template HeapOps(alias less, Range)
{
siftDown(r, i, n);
}
- assert(isHeap(r));
+ assert(isHeap(r), "r is not a heap");
}
bool isHeap()(Range r)
@@ -2160,7 +2253,7 @@ package(std) template HeapOps(alias less, Range)
// Sifts down r[parent] (which is initially assumed to be messed up) so the
// heap property is restored for r[parent .. end].
- // template because of @@@12410@@@
+ // template because of https://issues.dlang.org/show_bug.cgi?id=12410
void siftDown()(Range r, size_t parent, immutable size_t end)
{
for (;;)
@@ -2188,7 +2281,7 @@ package(std) template HeapOps(alias less, Range)
// restored. So there are more swaps but fewer comparisons. Gains are made
// when the final position is likely to end toward the bottom of the heap,
// so not a lot of sifts back are performed.
- //template because of @@@12410@@@
+ //template because of https://issues.dlang.org/show_bug.cgi?id=12410
void percolate()(Range r, size_t parent, immutable size_t end)
{
immutable root = parent;
@@ -2232,17 +2325,19 @@ private template TimSortImpl(alias pred, R)
import core.bitop : bsr;
import std.array : uninitializedArray;
- static assert(isRandomAccessRange!R);
- static assert(hasLength!R);
- static assert(hasSlicing!R);
- static assert(hasAssignableElements!R);
+ static assert(isRandomAccessRange!R, R.stringof ~ " must be a"
+ ~ " RandomAccessRange");
+ static assert(hasLength!R, R.stringof ~ " must have a length");
+ static assert(hasSlicing!R, R.stringof ~ " must support slicing");
+ static assert(hasAssignableElements!R, R.stringof ~ " must support"
+ ~ " assigning elements");
alias T = ElementType!R;
alias less = binaryFun!pred;
- alias greater = (a, b) => less(b, a);
- alias greaterEqual = (a, b) => !less(a, b);
- alias lessEqual = (a, b) => !less(b, a);
+ bool greater()(auto ref T a, auto ref T b) { return less(b, a); }
+ bool greaterEqual()(auto ref T a, auto ref T b) { return !less(a, b); }
+ bool lessEqual()(auto ref T a, auto ref T b) { return !less(b, a); }
enum minimalMerge = 128;
enum minimalGallop = 7;
@@ -2255,6 +2350,7 @@ private template TimSortImpl(alias pred, R)
void sort()(R range, T[] temp)
{
import std.algorithm.comparison : min;
+ import std.format : format;
// Do insertion sort on small range
if (range.length <= minimalMerge)
@@ -2312,15 +2408,27 @@ private template TimSortImpl(alias pred, R)
}
// Assert that the code above established the invariant correctly
- version (assert)
+ version (StdUnittest)
{
- if (stackLen == 2) assert(stack[0].length > stack[1].length);
+ if (stackLen == 2)
+ {
+ assert(stack[0].length > stack[1].length, format!
+ "stack[0].length %s > stack[1].length %s"(
+ stack[0].length, stack[1].length
+ ));
+ }
else if (stackLen > 2)
{
foreach (k; 2 .. stackLen)
{
- assert(stack[k - 2].length > stack[k - 1].length + stack[k].length);
- assert(stack[k - 1].length > stack[k].length);
+ assert(stack[k - 2].length > stack[k - 1].length + stack[k].length,
+ format!"stack[k - 2].length %s > stack[k - 1].length %s + stack[k].length %s"(
+ stack[k - 2].length, stack[k - 1].length, stack[k].length
+ ));
+ assert(stack[k - 1].length > stack[k].length,
+ format!"stack[k - 1].length %s > stack[k].length %s"(
+ stack[k - 1].length, stack[k].length
+ ));
}
}
}
@@ -2352,9 +2460,10 @@ private template TimSortImpl(alias pred, R)
size_t firstRun()(R range)
out(ret)
{
- assert(ret <= range.length);
+ assert(ret <= range.length, "ret must be less or equal than"
+ ~ " range.length");
}
- body
+ do
{
import std.algorithm.mutation : reverse;
@@ -2377,9 +2486,9 @@ private template TimSortImpl(alias pred, R)
void binaryInsertionSort()(R range, size_t sortedLen = 1)
out
{
- if (!__ctfe) assert(isSorted!pred(range));
+ if (!__ctfe) assert(isSorted!pred(range), "range must be sorted");
}
- body
+ do
{
import std.algorithm.mutation : move;
@@ -2400,8 +2509,17 @@ private template TimSortImpl(alias pred, R)
//moveAll(retro(range[lower .. sortedLen]),
// retro(range[lower+1 .. sortedLen+1]));
for (upper=sortedLen; upper > lower; upper--)
- range[upper] = range.moveAt(upper - 1);
- range[lower] = move(item);
+ {
+ static if (hasLvalueElements!R)
+ move(range[upper -1], range[upper]);
+ else
+ range[upper] = range.moveAt(upper - 1);
+ }
+
+ static if (hasLvalueElements!R)
+ move(item, range[lower]);
+ else
+ range[lower] = move(item);
}
}
@@ -2409,10 +2527,12 @@ private template TimSortImpl(alias pred, R)
void mergeAt()(R range, Slice[] stack, immutable size_t at, ref size_t minGallop, ref T[] temp)
in
{
- assert(stack.length >= 2);
- assert(stack.length - at == 2 || stack.length - at == 3);
+ import std.format : format;
+ assert(stack.length >= 2, "stack be be greater than 1");
+ assert(stack.length - at == 2 || stack.length - at == 3,
+ format!"stack.length - at %s must be 2 or 3"(stack.length - at));
}
- body
+ do
{
immutable base = stack[at].base;
immutable mid = stack[at].length;
@@ -2433,13 +2553,16 @@ private template TimSortImpl(alias pred, R)
{
if (!__ctfe)
{
- assert(isSorted!pred(range[0 .. mid]));
- assert(isSorted!pred(range[mid .. range.length]));
+ assert(isSorted!pred(range[0 .. mid]), "range[0 .. mid] must be"
+ ~ " sorted");
+ assert(isSorted!pred(range[mid .. range.length]), "range[mid .."
+ ~ " range.length] must be sorted");
}
}
- body
+ do
{
- assert(mid < range.length);
+ assert(mid < range.length, "mid must be less than the length of the"
+ ~ " range");
// Reduce range of elements
immutable firstElement = gallopForwardUpper(range[0 .. mid], range[mid]);
@@ -2466,9 +2589,9 @@ private template TimSortImpl(alias pred, R)
T[] ensureCapacity()(size_t minCapacity, T[] temp)
out(ret)
{
- assert(ret.length >= minCapacity);
+ assert(ret.length >= minCapacity, "ensuring the capacity failed");
}
- body
+ do
{
if (temp.length < minCapacity)
{
@@ -2487,21 +2610,22 @@ private template TimSortImpl(alias pred, R)
size_t mergeLo()(R range, immutable size_t mid, size_t minGallop, T[] temp)
out
{
- if (!__ctfe) assert(isSorted!pred(range));
+ if (!__ctfe) assert(isSorted!pred(range), "the range must be sorted");
}
- body
+ do
{
import std.algorithm.mutation : copy;
- assert(mid <= range.length);
- assert(temp.length >= mid);
+ assert(mid <= range.length, "mid must be less than the length of the"
+ ~ " range");
+ assert(temp.length >= mid, "temp.length must be greater or equal to mid");
// Copy run into temporary memory
temp = temp[0 .. mid];
copy(range[0 .. mid], temp);
// Move first element into place
- range[0] = range[mid];
+ moveEntry(range, mid, range, 0);
size_t i = 1, lef = 0, rig = mid + 1;
size_t count_lef, count_rig;
@@ -2518,14 +2642,14 @@ private template TimSortImpl(alias pred, R)
{
if (lessEqual(temp[lef], range[rig]))
{
- range[i++] = temp[lef++];
+ moveEntry(temp, lef++, range, i++);
if (lef >= lef_end) break outer;
++count_lef;
count_rig = 0;
}
else
{
- range[i++] = range[rig++];
+ moveEntry(range, rig++, range, i++);
if (rig >= range.length) break outer;
count_lef = 0;
++count_rig;
@@ -2536,14 +2660,14 @@ private template TimSortImpl(alias pred, R)
do
{
count_lef = gallopForwardUpper(temp[lef .. $], range[rig]);
- foreach (j; 0 .. count_lef) range[i++] = temp[lef++];
+ foreach (j; 0 .. count_lef) moveEntry(temp, lef++, range, i++);
if (lef >= temp.length) break outer;
count_rig = gallopForwardLower(range[rig .. range.length], temp[lef]);
- foreach (j; 0 .. count_rig) range[i++] = range[rig++];
+ foreach (j; 0 .. count_rig) moveEntry(range, rig++, range, i++);
if (rig >= range.length) while (true)
{
- range[i++] = temp[lef++];
+ moveEntry(temp, lef++, range, i++);
if (lef >= temp.length) break outer;
}
@@ -2556,11 +2680,11 @@ private template TimSortImpl(alias pred, R)
// Move remaining elements from right
while (rig < range.length)
- range[i++] = range[rig++];
+ moveEntry(range, rig++, range, i++);
// Move remaining elements from left
while (lef < temp.length)
- range[i++] = temp[lef++];
+ moveEntry(temp, lef++, range, i++);
return minGallop > 0 ? minGallop : 1;
}
@@ -2570,21 +2694,24 @@ private template TimSortImpl(alias pred, R)
size_t mergeHi()(R range, immutable size_t mid, size_t minGallop, T[] temp)
out
{
- if (!__ctfe) assert(isSorted!pred(range));
+ if (!__ctfe) assert(isSorted!pred(range), "the range must be sorted");
}
- body
+ do
{
import std.algorithm.mutation : copy;
+ import std.format : format;
- assert(mid <= range.length);
- assert(temp.length >= range.length - mid);
+ assert(mid <= range.length, "mid must be less or equal to range.length");
+ assert(temp.length >= range.length - mid, format!
+ "temp.length %s >= range.length %s - mid %s"(temp.length,
+ range.length, mid));
// Copy run into temporary memory
temp = temp[0 .. range.length - mid];
copy(range[mid .. range.length], temp);
// Move first element into place
- range[range.length - 1] = range[mid - 1];
+ moveEntry(range, mid - 1, range, range.length - 1);
size_t i = range.length - 2, lef = mid - 2, rig = temp.length - 1;
size_t count_lef, count_rig;
@@ -2600,19 +2727,19 @@ private template TimSortImpl(alias pred, R)
{
if (greaterEqual(temp[rig], range[lef]))
{
- range[i--] = temp[rig];
+ moveEntry(temp, rig, range, i--);
if (rig == 1)
{
// Move remaining elements from left
while (true)
{
- range[i--] = range[lef];
+ moveEntry(range, lef, range, i--);
if (lef == 0) break;
--lef;
}
// Move last element into place
- range[i] = temp[0];
+ moveEntry(temp, 0, range, i);
break outer;
}
@@ -2622,10 +2749,10 @@ private template TimSortImpl(alias pred, R)
}
else
{
- range[i--] = range[lef];
+ moveEntry(range, lef, range, i--);
if (lef == 0) while (true)
{
- range[i--] = temp[rig];
+ moveEntry(temp, rig, range, i--);
if (rig == 0) break outer;
--rig;
}
@@ -2641,7 +2768,7 @@ private template TimSortImpl(alias pred, R)
count_rig = rig - gallopReverseLower(temp[0 .. rig], range[lef]);
foreach (j; 0 .. count_rig)
{
- range[i--] = temp[rig];
+ moveEntry(temp, rig, range, i--);
if (rig == 0) break outer;
--rig;
}
@@ -2649,10 +2776,10 @@ private template TimSortImpl(alias pred, R)
count_lef = lef - gallopReverseUpper(range[0 .. lef], temp[rig]);
foreach (j; 0 .. count_lef)
{
- range[i--] = range[lef];
+ moveEntry(range, lef, range, i--);
if (lef == 0) while (true)
{
- range[i--] = temp[rig];
+ moveEntry(temp, rig, range, i--);
if (rig == 0) break outer;
--rig;
}
@@ -2676,9 +2803,10 @@ private template TimSortImpl(alias pred, R)
size_t gallopSearch(R)(R range, T value)
out(ret)
{
- assert(ret <= range.length);
+ assert(ret <= range.length, "ret must be less or equal to"
+ ~ " range.length");
}
- body
+ do
{
size_t lower = 0, center = 1, upper = range.length;
alias gap = center;
@@ -2748,6 +2876,21 @@ private template TimSortImpl(alias pred, R)
alias gallopForwardUpper = gallopSearch!(false, true);
alias gallopReverseLower = gallopSearch!( true, false);
alias gallopReverseUpper = gallopSearch!( true, true);
+
+ /// Helper method that moves from[fIdx] into to[tIdx] if both are lvalues and
+ /// uses a plain assignment if not (necessary for backwards compatibility)
+ void moveEntry(X, Y)(ref X from, const size_t fIdx, ref Y to, const size_t tIdx)
+ {
+ // This template is instantiated with different combinations of range (R) and temp (T[]).
+ // T[] obviously has lvalue-elements, so checking R should be enough here
+ static if (hasLvalueElements!R)
+ {
+ import core.lifetime : move;
+ move(from[fIdx], to[tIdx]);
+ }
+ else
+ to[tIdx] = from[fIdx];
+ }
}
@safe unittest
@@ -2797,6 +2940,7 @@ private template TimSortImpl(alias pred, R)
// Tests the Timsort function for correctness and stability
static bool testSort(uint seed)
{
+ import std.format : format;
auto arr = genSampleData(seed);
// Now sort the array!
@@ -2808,12 +2952,15 @@ private template TimSortImpl(alias pred, R)
sort!(comp, SwapStrategy.stable)(arr);
// Test that the array was sorted correctly
- assert(isSorted!comp(arr));
+ assert(isSorted!comp(arr), "arr must be sorted");
// Test that the array was sorted stably
foreach (i; 0 .. arr.length - 1)
{
- if (arr[i].value == arr[i + 1].value) assert(arr[i].index < arr[i + 1].index);
+ if (arr[i].value == arr[i + 1].value)
+ assert(arr[i].index < arr[i + 1].index, format!
+ "arr[i %s].index %s < arr[i + 1].index %s"(
+ i, arr[i].index, arr[i + 1].index));
}
return true;
@@ -2823,11 +2970,12 @@ private template TimSortImpl(alias pred, R)
testSort(seed);
enum result = testSort(seed);
- assert(result == true);
+ assert(result == true, "invalid result");
}
+// https://issues.dlang.org/show_bug.cgi?id=4584
@safe unittest
-{//bugzilla 4584
+{
assert(isSorted!"a < b"(sort!("a < b", SwapStrategy.stable)(
[83, 42, 85, 86, 87, 22, 89, 30, 91, 46, 93, 94, 95, 6,
97, 14, 33, 10, 101, 102, 103, 26, 105, 106, 107, 6]
@@ -2847,18 +2995,54 @@ private template TimSortImpl(alias pred, R)
assert(y == "aebcd"d);
}
+// https://issues.dlang.org/show_bug.cgi?id=14223
@safe unittest
{
- // Issue 14223
import std.array, std.range;
auto arr = chain(iota(0, 384), iota(0, 256), iota(0, 80), iota(0, 64), iota(0, 96)).array;
sort!("a < b", SwapStrategy.stable)(arr);
}
+@safe unittest
+{
+ static struct NoCopy
+ {
+ pure nothrow @nogc @safe:
+
+ int key;
+ this(scope const ref NoCopy)
+ {
+ assert(false, "Tried to copy struct!");
+ }
+ ref opAssign()(scope const auto ref NoCopy other)
+ {
+ assert(false, "Tried to copy struct!");
+ }
+ this(this) {}
+ }
+
+ static NoCopy[] makeArray(const size_t size)
+ {
+ NoCopy[] array = new NoCopy[](size);
+ foreach (const i, ref t; array[0..$/2]) t.key = cast(int) (size - i);
+ foreach (const i, ref t; array[$/2..$]) t.key = cast(int) i;
+ return array;
+ }
+
+ alias cmp = (ref NoCopy a, ref NoCopy b) => a.key < b.key;
+ enum minMerge = TimSortImpl!(cmp, NoCopy[]).minimalMerge;
+
+ sort!(cmp, SwapStrategy.unstable)(makeArray(20));
+ sort!(cmp, SwapStrategy.stable)(makeArray(minMerge - 5));
+ sort!(cmp, SwapStrategy.stable)(makeArray(minMerge + 5));
+}
+
// schwartzSort
/**
Alternative sorting method that should be used when comparing keys involves an
-expensive computation. Instead of using `less(a, b)` for comparing elements,
+expensive computation.
+
+Instead of using `less(a, b)` for comparing elements,
`schwartzSort` uses `less(transform(a), transform(b))`. The values of the
`transform` function are precomputed in a temporary array, thus saving on
repeatedly computing it. Conversely, if the cost of `transform` is small
@@ -2882,46 +3066,75 @@ sort!((a, b) => hashFun(a) < hashFun(b))(array);
schwartzSort!(hashFun, "a < b")(array);
----
-The $(D schwartzSort) function might require less temporary data and
+The `schwartzSort` function might require less temporary data and
be faster than the Perl idiom or the decorate-sort-undecorate idiom
present in Python and Lisp. This is because sorting is done in-place
and only minimal extra data (one array of transformed elements) is
created.
To check whether an array was sorted and benefit of the speedup of
-Schwartz sorting, a function $(D schwartzIsSorted) is not provided
+Schwartz sorting, a function `schwartzIsSorted` is not provided
because the effect can be achieved by calling $(D
isSorted!less(map!transform(r))).
Params:
- transform = The transformation to apply.
- less = The predicate to sort by.
+ transform = The transformation to apply. Either a unary function
+ (`unaryFun!transform(element)`), or a binary function
+ (`binaryFun!transform(element, index)`).
+ less = The predicate to sort the transformed elements by.
ss = The swapping strategy to use.
r = The range to sort.
-Returns: The initial range wrapped as a $(D SortedRange) with the
-predicate $(D (a, b) => binaryFun!less(transform(a),
-transform(b))).
+Returns: The initial range wrapped as a `SortedRange` with the
+predicate `(a, b) => binaryFun!less(transform(a), transform(b))`.
*/
SortedRange!(R, ((a, b) => binaryFun!less(unaryFun!transform(a),
unaryFun!transform(b))))
schwartzSort(alias transform, alias less = "a < b",
SwapStrategy ss = SwapStrategy.unstable, R)(R r)
-if (isRandomAccessRange!R && hasLength!R)
+if (isRandomAccessRange!R && hasLength!R && hasSwappableElements!R &&
+ !is(typeof(binaryFun!less) == SwapStrategy))
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
import std.range : zip, SortedRange;
import std.string : representation;
- alias T = typeof(unaryFun!transform(r.front));
+ static if (is(typeof(unaryFun!transform(r.front))))
+ {
+ alias transformFun = unaryFun!transform;
+ alias TB = typeof(transformFun(r.front));
+ enum isBinary = false;
+ }
+ else static if (is(typeof(binaryFun!transform(r.front, 0))))
+ {
+ alias transformFun = binaryFun!transform;
+ alias TB = typeof(transformFun(r.front, 0));
+ enum isBinary = true;
+ }
+ else
+ static assert(false, "unsupported `transform` alias");
+
+ // The `transform` function might return a qualified type, e.g. const(int).
+ // Strip qualifiers if possible s.t. the temporary array is sortable.
+ static if (is(TB : Unqual!TB))
+ alias T = Unqual!TB;
+ else
+ static assert(false, "`transform` returns an unsortable qualified type: " ~ TB.stringof);
+
static trustedMalloc(size_t len) @trusted
{
import core.checkedint : mulu;
import core.stdc.stdlib : malloc;
bool overflow;
const nbytes = mulu(len, T.sizeof, overflow);
- if (overflow) assert(0);
- return (cast(T*) malloc(nbytes))[0 .. len];
+ if (overflow) assert(false, "multiplication overflowed");
+ T[] result = (cast(T*) malloc(nbytes))[0 .. len];
+ static if (hasIndirections!T)
+ {
+ import core.memory : GC;
+ GC.addRange(result.ptr, nbytes);
+ }
+ return result;
}
auto xform1 = trustedMalloc(r.length);
@@ -2935,13 +3148,21 @@ if (isRandomAccessRange!R && hasLength!R)
static void trustedFree(T[] p) @trusted
{
import core.stdc.stdlib : free;
+ static if (hasIndirections!T)
+ {
+ import core.memory : GC;
+ GC.removeRange(p.ptr);
+ }
free(p.ptr);
}
trustedFree(xform1);
}
for (; length != r.length; ++length)
{
- emplace(&xform1[length], unaryFun!transform(r[length]));
+ static if (isBinary)
+ emplace(&xform1[length], transformFun(r[length], length));
+ else
+ emplace(&xform1[length], transformFun(r[length]));
}
// Make sure we use ubyte[] and ushort[], not char[] and wchar[]
// for the intermediate array, lest zip gets confused.
@@ -2957,6 +3178,13 @@ if (isRandomAccessRange!R && hasLength!R)
return typeof(return)(r);
}
+/// ditto
+auto schwartzSort(alias transform, SwapStrategy ss, R)(R r)
+if (isRandomAccessRange!R && hasLength!R && hasSwappableElements!R)
+{
+ return schwartzSort!(transform, "a < b", ss, R)(r);
+}
+
///
@safe unittest
{
@@ -3002,27 +3230,109 @@ if (isRandomAccessRange!R && hasLength!R)
@safe unittest
{
- // issue 4909
+ // binary transform function
+ string[] strings = [ "one", "two", "three" ];
+ schwartzSort!((element, index) => size_t.max - index)(strings);
+ assert(strings == [ "three", "two", "one" ]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=4909
+@safe unittest
+{
import std.typecons : Tuple;
Tuple!(char)[] chars;
schwartzSort!"a[0]"(chars);
}
+// https://issues.dlang.org/show_bug.cgi?id=5924
@safe unittest
{
- // issue 5924
import std.typecons : Tuple;
Tuple!(char)[] chars;
schwartzSort!((Tuple!(char) c){ return c[0]; })(chars);
}
+// https://issues.dlang.org/show_bug.cgi?id=13965
+@safe unittest
+{
+ import std.typecons : Tuple;
+ Tuple!(char)[] chars;
+ schwartzSort!("a[0]", SwapStrategy.stable)(chars);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=13965
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.numeric : entropy;
+
+ auto lowEnt = [ 1.0, 0, 0 ],
+ midEnt = [ 0.1, 0.1, 0.8 ],
+ highEnt = [ 0.31, 0.29, 0.4 ];
+ auto arr = new double[][3];
+ arr[0] = midEnt;
+ arr[1] = lowEnt;
+ arr[2] = highEnt;
+
+ schwartzSort!(entropy, SwapStrategy.stable)(arr);
+
+ assert(arr[0] == lowEnt);
+ assert(arr[1] == midEnt);
+ assert(arr[2] == highEnt);
+ assert(isSorted!("a < b")(map!(entropy)(arr)));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20799
+@safe unittest
+{
+ import std.range : iota, retro;
+ import std.array : array;
+
+ auto arr = 1_000_000.iota.retro.array;
+ arr.schwartzSort!(
+ n => new int(n),
+ (a, b) => *a < *b
+ );
+ assert(arr.isSorted());
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21183
+@safe unittest
+{
+ static T get(T)(int) { return T.init; }
+
+ // There's no need to actually sort, just checking type interference
+ if (false)
+ {
+ int[] arr;
+
+ // Fine because there are no indirections
+ arr.schwartzSort!(get!(const int));
+
+ // Fine because it decays to immutable(int)*
+ arr.schwartzSort!(get!(immutable int*));
+
+ // Disallowed because it would require a non-const reference
+ static assert(!__traits(compiles, arr.schwartzSort!(get!(const Object))));
+
+ static struct Wrapper
+ {
+ int* ptr;
+ }
+
+ // Disallowed because Wrapper.ptr would become mutable
+ static assert(!__traits(compiles, arr.schwartzSort!(get!(const Wrapper))));
+ }
+}
+
// partialSort
/**
-Reorders the random-access range $(D r) such that the range $(D r[0
-.. mid]) is the same as if the entire $(D r) were sorted, and leaves
-the range $(D r[mid .. r.length]) in no particular order. Performs
-$(BIGOH r.length * log(mid)) evaluations of $(D pred). The
-implementation simply calls $(D topN!(less, ss)(r, n)) and then $(D
+Reorders the random-access range `r` such that the range `r[0 .. mid]`
+is the same as if the entire `r` were sorted, and leaves
+the range `r[mid .. r.length]` in no particular order.
+
+Performs $(BIGOH r.length * log(mid)) evaluations of `pred`. The
+implementation simply calls `topN!(less, ss)(r, n)` and then $(D
sort!(less, ss)(r[0 .. n])).
Params:
@@ -3077,17 +3387,20 @@ if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
// topN
/**
-Reorders the range $(D r) using $(D swap) such that $(D r[nth]) refers
-to the element that would fall there if the range were fully
-sorted. In addition, it also partitions $(D r) such that all elements
-$(D e1) from $(D r[0]) to $(D r[nth]) satisfy $(D !less(r[nth], e1)),
-and all elements $(D e2) from $(D r[nth]) to $(D r[r.length]) satisfy
-$(D !less(e2, r[nth])). Effectively, it finds the nth smallest
-(according to $(D less)) elements in $(D r). Performs an expected
+Reorders the range `r` using $(REF_ALTTEXT swap, swap, std,algorithm,mutation)
+such that `r[nth]` refers to the element that would fall there if the range
+were fully sorted.
+
+It is akin to $(LINK2 https://en.wikipedia.org/wiki/Quickselect, Quickselect),
+and partitions `r` such that all elements
+`e1` from `r[0]` to `r[nth]` satisfy `!less(r[nth], e1)`,
+and all elements `e2` from `r[nth]` to `r[r.length]` satisfy
+`!less(e2, r[nth])`. Effectively, it finds the `nth + 1` smallest
+(according to `less`) elements in `r`. Performs an expected
$(BIGOH r.length) (if unstable) or $(BIGOH r.length * log(r.length))
-(if stable) evaluations of $(D less) and $(D swap).
+(if stable) evaluations of `less` and $(REF_ALTTEXT swap, swap, std,algorithm,mutation).
-If $(D n >= r.length), the algorithm has no effect and returns
+If `n >= r.length`, the algorithm has no effect and returns
`r[0 .. r.length]`.
Params:
@@ -3097,9 +3410,10 @@ Params:
nth = The index of the element that should be in sorted position after the
function is done.
+Returns: a slice from `r[0]` to `r[nth]`, excluding `r[nth]` itself.
+
See_Also:
$(LREF topNIndex),
- $(HTTP sgi.com/tech/stl/nth_element.html, STL's nth_element)
BUGS:
@@ -3108,7 +3422,8 @@ Stable topN has not been implemented yet.
auto topN(alias less = "a < b",
SwapStrategy ss = SwapStrategy.unstable,
Range)(Range r, size_t nth)
-if (isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range)
+if (isRandomAccessRange!(Range) && hasLength!Range &&
+ hasSlicing!Range && hasAssignableElements!Range)
{
static assert(ss == SwapStrategy.unstable,
"Stable topN not yet implemented");
@@ -3119,10 +3434,11 @@ if (isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range)
// Workaround for https://issues.dlang.org/show_bug.cgi?id=16528
// Safety checks: enumerate all potentially unsafe generic primitives
// then use a @trusted implementation.
- binaryFun!less(r[0], r[r.length - 1]);
+ cast(void) binaryFun!less(r[0], r[r.length - 1]);
import std.algorithm.mutation : swapAt;
r.swapAt(size_t(0), size_t(0));
- static assert(is(typeof(r.length) == size_t));
+ static assert(is(typeof(r.length) == size_t),
+ typeof(r.length).stringof ~ " must be of type size_t");
pivotPartition!less(r, 0);
}
bool useSampling = true;
@@ -3130,6 +3446,28 @@ if (isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range)
return ret;
}
+///
+@safe unittest
+{
+ int[] v = [ 25, 7, 9, 2, 0, 5, 21 ];
+ topN!"a < b"(v, 100);
+ assert(v == [ 25, 7, 9, 2, 0, 5, 21 ]);
+ auto n = 4;
+ topN!((a, b) => a < b)(v, n);
+ assert(v[n] == 9);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=8341
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : zip;
+ import std.typecons : tuple;
+ auto a = [10, 30, 20];
+ auto b = ["c", "b", "a"];
+ assert(topN!"a[0] > b[0]"(zip(a, b), 2).equal([tuple(20, "a"), tuple(30, "b")]));
+}
+
private @trusted
void topNImpl(alias less, R)(R r, size_t n, ref bool useSampling)
{
@@ -3212,7 +3550,7 @@ void topNImpl(alias less, R)(R r, size_t n, ref bool useSampling)
}
}
- assert(pivot != size_t.max);
+ assert(pivot != size_t.max, "pivot must be not equal to size_t.max");
// See how the pivot fares
if (pivot == n)
{
@@ -3230,20 +3568,11 @@ void topNImpl(alias less, R)(R r, size_t n, ref bool useSampling)
}
}
-///
-@safe unittest
-{
- int[] v = [ 25, 7, 9, 2, 0, 5, 21 ];
- topN!"a < b"(v, 100);
- assert(v == [ 25, 7, 9, 2, 0, 5, 21 ]);
- auto n = 4;
- topN!"a < b"(v, n);
- assert(v[n] == 9);
-}
-
private size_t topNPartition(alias lp, R)(R r, size_t n, bool useSampling)
{
- assert(r.length >= 9 && n < r.length);
+ import std.format : format;
+ assert(r.length >= 9 && n < r.length, "length must be longer than 9"
+ ~ " and n must be less than r.length");
immutable ninth = r.length / 9;
auto pivot = ninth / 2;
// Position subrange r[lo .. hi] to have length equal to ninth and its upper
@@ -3252,9 +3581,11 @@ private size_t topNPartition(alias lp, R)(R r, size_t n, bool useSampling)
// the median in already sorted ranges.
immutable lo = r.length / 2 - pivot, hi = lo + ninth;
// We have either one straggler on the left, one on the right, or none.
- assert(lo - (r.length - hi) <= 1 || (r.length - hi) - lo <= 1);
- assert(lo >= ninth * 4);
- assert(r.length - hi >= ninth * 4);
+ assert(lo - (r.length - hi) <= 1 || (r.length - hi) - lo <= 1,
+ format!"straggler check failed lo %s, r.length %s, hi %s"(lo, r.length, hi));
+ assert(lo >= ninth * 4, format!"lo %s >= ninth * 4 %s"(lo, ninth * 4));
+ assert(r.length - hi >= ninth * 4,
+ format!"r.length %s - hi %s >= ninth * 4 %s"(r.length, hi, ninth * 4));
// Partition in groups of 3, and the mid tertile again in groups of 3
if (!useSampling)
@@ -3270,12 +3601,15 @@ private size_t topNPartition(alias lp, R)(R r, size_t n, bool useSampling)
private void p3(alias less, Range)(Range r, size_t lo, immutable size_t hi)
{
- assert(lo <= hi && hi < r.length);
+ import std.format : format;
+ assert(lo <= hi && hi < r.length,
+ format!"lo %s <= hi %s && hi < r.length %s"(lo, hi, r.length));
immutable ln = hi - lo;
for (; lo < hi; ++lo)
{
- assert(lo >= ln);
- assert(lo + ln < r.length);
+ assert(lo >= ln, format!"lo %s >= ln %s"(lo, ln));
+ assert(lo + ln < r.length, format!"lo %s + ln %s < r.length %s"(
+ lo, ln, r.length));
medianOf!less(r, lo - ln, lo, lo + ln);
}
}
@@ -3283,12 +3617,15 @@ private void p3(alias less, Range)(Range r, size_t lo, immutable size_t hi)
private void p4(alias less, Flag!"leanRight" f, Range)
(Range r, size_t lo, immutable size_t hi)
{
- assert(lo <= hi && hi < r.length);
+ import std.format : format;
+ assert(lo <= hi && hi < r.length, format!"lo %s <= hi %s && hi < r.length %s"(
+ lo, hi, r.length));
immutable ln = hi - lo, _2ln = ln * 2;
for (; lo < hi; ++lo)
{
- assert(lo >= ln);
- assert(lo + ln < r.length);
+ assert(lo >= ln, format!"lo %s >= ln %s"(lo, ln));
+ assert(lo + ln < r.length, format!"lo %s + ln %s < r.length %s"(
+ lo, ln, r.length));
static if (f == Yes.leanRight)
medianOf!(less, f)(r, lo - _2ln, lo - ln, lo, lo + ln);
else
@@ -3299,8 +3636,8 @@ private void p4(alias less, Flag!"leanRight" f, Range)
private size_t topNPartitionOffMedian(alias lp, Flag!"leanRight" f, R)
(R r, size_t n, bool useSampling)
{
- assert(r.length >= 12);
- assert(n < r.length);
+ assert(r.length >= 12, "The length of r must be greater than 11");
+ assert(n < r.length, "n must be less than the length of r");
immutable _4 = r.length / 4;
static if (f == Yes.leanRight)
immutable leftLimit = 2 * _4;
@@ -3337,19 +3674,23 @@ size_t expandPartition(alias lp, R)(R r, size_t lo, size_t pivot, size_t hi)
in
{
import std.algorithm.searching : all;
- assert(lo <= pivot);
- assert(pivot < hi);
- assert(hi <= r.length);
- assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)));
- assert(r[pivot + 1 .. hi].all!(x => !lp(x, r[pivot])));
+ assert(lo <= pivot, "lo must be less than or equal pivot");
+ assert(pivot < hi, "pivot must be less than hi");
+ assert(hi <= r.length, "hi must be less than or equal to the length of r");
+ assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)),
+ "r[lo .. pivot + 1] failed less than test");
+ assert(r[pivot + 1 .. hi].all!(x => !lp(x, r[pivot])),
+ "r[pivot + 1 .. hi] failed less than test");
}
out
{
import std.algorithm.searching : all;
- assert(r[0 .. pivot + 1].all!(x => !lp(r[pivot], x)));
- assert(r[pivot + 1 .. r.length].all!(x => !lp(x, r[pivot])));
+ assert(r[0 .. pivot + 1].all!(x => !lp(r[pivot], x)),
+ "r[0 .. pivot + 1] failed less than test");
+ assert(r[pivot + 1 .. r.length].all!(x => !lp(x, r[pivot])),
+ "r[pivot + 1 .. r.length] failed less than test");
}
-body
+do
{
import std.algorithm.mutation : swapAt;
import std.algorithm.searching : all;
@@ -3372,10 +3713,14 @@ body
r.swapAt(left, rite);
}
- assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)));
- assert(r[pivot + 1 .. hi + 1].all!(x => !lp(x, r[pivot])));
- assert(r[0 .. left].all!(x => !lp(r[pivot], x)));
- assert(r[rite + 1 .. r.length].all!(x => !lp(x, r[pivot])));
+ assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)),
+ "r[lo .. pivot + 1] failed less than test");
+ assert(r[pivot + 1 .. hi + 1].all!(x => !lp(x, r[pivot])),
+ "r[pivot + 1 .. hi + 1] failed less than test");
+ assert(r[0 .. left].all!(x => !lp(r[pivot], x)),
+ "r[0 .. left] failed less than test");
+ assert(r[rite + 1 .. r.length].all!(x => !lp(x, r[pivot])),
+ "r[rite + 1 .. r.length] failed less than test");
immutable oldPivot = pivot;
@@ -3387,7 +3732,7 @@ body
if (left == lo) goto done;
if (!lp(r[oldPivot], r[left])) continue;
--pivot;
- assert(!lp(r[oldPivot], r[pivot]));
+ assert(!lp(r[oldPivot], r[pivot]), "less check failed");
r.swapAt(left, pivot);
}
// Second loop: make left and pivot meet
@@ -3414,7 +3759,7 @@ body
if (rite == hi) goto done;
if (!lp(r[rite], r[oldPivot])) continue;
++pivot;
- assert(!lp(r[pivot], r[oldPivot]));
+ assert(!lp(r[pivot], r[oldPivot]), "less check failed");
r.swapAt(rite, pivot);
}
// Second loop: make left and pivot meet
@@ -3441,23 +3786,18 @@ done:
{
auto a = [ 10, 5, 3, 4, 8, 11, 13, 3, 9, 4, 10 ];
assert(expandPartition!((a, b) => a < b)(a, 4, 5, 6) == 9);
- a = randomArray;
+
+ import std.algorithm.iteration : map;
+ import std.array : array;
+ import std.random : uniform;
+ import std.range : iota;
+ auto size = uniform(1, 1000);
+ a = iota(0, size).map!(_ => uniform(0, 1000)).array;
if (a.length == 0) return;
expandPartition!((a, b) => a < b)(a, a.length / 2, a.length / 2,
a.length / 2 + 1);
}
-version (unittest)
-private T[] randomArray(Flag!"exactSize" flag = No.exactSize, T = int)(
- size_t maxSize = 1000,
- T minValue = 0, T maxValue = 255)
-{
- import std.algorithm.iteration : map;
- import std.random : unpredictableSeed, Random, uniform;
- auto size = flag == Yes.exactSize ? maxSize : uniform(1, maxSize);
- return iota(0, size).map!(_ => uniform(minValue, maxValue)).array;
-}
-
@safe unittest
{
import std.algorithm.comparison : max, min;
@@ -3509,9 +3849,9 @@ private T[] randomArray(Flag!"exactSize" flag = No.exactSize, T = int)(
{
import std.algorithm.comparison : max, min;
import std.algorithm.iteration : reduce;
- import std.random : Random, uniform, unpredictableSeed;
+ import std.random : Random = Xorshift, uniform;
- immutable uint[] seeds = [90027751, 2709791795, 1374631933, 995751648, 3541495258, 984840953, unpredictableSeed];
+ immutable uint[] seeds = [90027751, 2709791795, 1374631933, 995751648, 3541495258, 984840953];
foreach (s; seeds)
{
auto r = Random(s);
@@ -3534,7 +3874,7 @@ private T[] randomArray(Flag!"exactSize" flag = No.exactSize, T = int)(
}
}
-// bug 12987
+// https://issues.dlang.org/show_bug.cgi?id=12987
@safe unittest
{
int[] a = [ 25, 7, 9, 2, 0, 5, 21 ];
@@ -3584,7 +3924,7 @@ if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
assert(a == [0, 1, 2, 2, 3]);
}
-// bug 15421
+// https://issues.dlang.org/show_bug.cgi?id=15421
@system unittest
{
import std.algorithm.comparison : equal;
@@ -3628,7 +3968,7 @@ if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
}
}
-// bug 15421
+// https://issues.dlang.org/show_bug.cgi?id=15421
@system unittest
{
auto a = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ];
@@ -3647,7 +3987,7 @@ if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
assert(a == b);
}
-// bug 12987
+// https://issues.dlang.org/show_bug.cgi?id=12987
@system unittest
{
int[] a = [ 5, 7, 2, 6, 7 ];
@@ -3657,7 +3997,7 @@ if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
assert(t == [ 0, 1, 2, 2, 3 ]);
}
-// bug 15420
+// https://issues.dlang.org/show_bug.cgi?id=15420
@system unittest
{
int[] a = [ 5, 7, 2, 6, 7 ];
@@ -3668,11 +4008,12 @@ if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
}
/**
-Copies the top $(D n) elements of the
-$(REF_ALTTEXT input range, isInputRange, std,range,primitives) $(D source) into the
-random-access range $(D target), where $(D n =
-target.length). Elements of $(D source) are not touched. If $(D
-sorted) is $(D true), the target is sorted. Otherwise, the target
+Copies the top `n` elements of the
+$(REF_ALTTEXT input range, isInputRange, std,range,primitives) `source` into the
+random-access range `target`, where `n = target.length`.
+
+Elements of `source` are not touched. If $(D
+sorted) is `true`, the target is sorted. Otherwise, the target
respects the $(HTTP en.wikipedia.org/wiki/Binary_heap, heap property).
Params:
@@ -3714,10 +4055,10 @@ if (isInputRange!(SRange) && isRandomAccessRange!(TRange)
@system unittest
{
- import std.random : Random, unpredictableSeed, uniform, randomShuffle;
+ import std.random : Random = Xorshift, uniform, randomShuffle;
import std.typecons : Yes;
- auto r = Random(unpredictableSeed);
+ auto r = Random(123_456_789);
ptrdiff_t[] a = new ptrdiff_t[uniform(1, 1000, r)];
foreach (i, ref e; a) e = i;
randomShuffle(a, r);
@@ -3735,7 +4076,7 @@ Similar to $(LREF topN), except that the range is not modified.
Params:
less = A binary predicate that defines the ordering of range elements.
- Defaults to $(D a < b).
+ Defaults to `a < b`.
ss = $(RED (Not implemented yet.)) Specify the swapping strategy.
r = A
$(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives)
@@ -3743,13 +4084,13 @@ Params:
index = A
$(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives)
with assignable elements to build the index in. The length of this range
- determines how many top elements to index in $(D r).
+ determines how many top elements to index in `r`.
This index range can either have integral elements, in which case the
constructed index will consist of zero-based numerical indices into
- $(D r); or it can have pointers to the element type of $(D r), in which
+ `r`; or it can have pointers to the element type of `r`, in which
case the constructed index will be pointers to the top elements in
- $(D r).
+ `r`.
sorted = Determines whether to sort the index by the elements they refer
to.
@@ -3852,7 +4193,7 @@ Private for the time being.
Computes the median of 2 to 5 arbitrary indexes in random-access range `r`
using hand-written specialized algorithms. The indexes must be distinct (if not,
behavior is implementation-defined). The function also partitions the elements
-involved around the median, e.g. $(D medianOf(r, a, b, c)) not only fills `r[b]`
+involved around the median, e.g. `medianOf(r, a, b, c)` not only fills `r[b]`
with the median of `r[a]`, `r[b]`, and `r[c]`, but also puts the minimum in
`r[a]` and the maximum in `r[c]`.
@@ -3861,9 +4202,9 @@ less = The comparison predicate used, modeled as a
$(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, strict weak ordering)
(irreflexive, antisymmetric, transitive, and implying a transitive equivalence).
flag = Used only for even values of `T.length`. If `No.leanRight`, the median
-"leans left", meaning $(D medianOf(r, a, b, c, d)) puts the lower median of the
+"leans left", meaning `medianOf(r, a, b, c, d)` puts the lower median of the
four in `r[b]`, the minimum in `r[a]`, and the two others in `r[c]` and `r[d]`.
-Conversely, $(D median!("a < b", Yes.leanRight)(r, a, b, c, d)) puts the upper
+Conversely, `median!("a < b", Yes.leanRight)(r, a, b, c, d)` puts the upper
median of the four in `r[c]`, the maximum in `r[d]`, and the two others in
`r[a]` and `r[b]`.
r = The range containing the indexes.
@@ -3879,37 +4220,45 @@ if (isRandomAccessRange!Range && hasLength!Range &&
Indexes.length >= 2 && Indexes.length <= 5 &&
allSatisfy!(isUnsigned, Indexes))
{
- assert(r.length >= Indexes.length);
+ assert(r.length >= Indexes.length, "r.length must be greater equal to"
+ ~ " Indexes.length");
import std.functional : binaryFun;
alias lt = binaryFun!less;
enum k = Indexes.length;
import std.algorithm.mutation : swapAt;
+ import std.format : format;
alias a = i[0];
- static assert(is(typeof(a) == size_t));
+ static assert(is(typeof(a) == size_t), typeof(a).stringof ~ " must be"
+ ~ " of type size_t");
static if (k >= 2)
{
alias b = i[1];
- static assert(is(typeof(b) == size_t));
- assert(a != b);
+ static assert(is(typeof(b) == size_t), typeof(b).stringof ~ " must be"
+ ~ " of type size_t");
+ assert(a != b, "a != b ");
}
static if (k >= 3)
{
alias c = i[2];
- static assert(is(typeof(c) == size_t));
- assert(a != c && b != c);
+ static assert(is(typeof(c) == size_t), typeof(c).stringof ~ " must be"
+ ~ " of type size_t");
+ assert(a != c && b != c, "a != c && b != c");
}
static if (k >= 4)
{
alias d = i[3];
- static assert(is(typeof(d) == size_t));
- assert(a != d && b != d && c != d);
+ static assert(is(typeof(d) == size_t), typeof(d).stringof ~ " must be"
+ ~ " of type size_t");
+ assert(a != d && b != d && c != d, "a != d && b != d && c != d failed");
}
static if (k >= 5)
{
alias e = i[4];
- static assert(is(typeof(e) == size_t));
- assert(a != e && b != e && c != e && d != e);
+ static assert(is(typeof(e) == size_t), typeof(e).stringof ~ " must be"
+ ~ " of type size_t");
+ assert(a != e && b != e && c != e && d != e,
+ "a != e && b != e && c != e && d != e failed");
}
static if (k == 2)
@@ -3942,8 +4291,8 @@ if (isRandomAccessRange!Range && hasLength!Range &&
if (lt(r[c], r[b])) r.swapAt(b, c);
}
}
- assert(!lt(r[b], r[a]));
- assert(!lt(r[c], r[b]));
+ assert(!lt(r[b], r[a]), "less than check failed");
+ assert(!lt(r[c], r[b]), "less than check failed");
}
else static if (k == 4)
{
@@ -3965,12 +4314,12 @@ if (isRandomAccessRange!Range && hasLength!Range &&
else static if (k == 5)
{
// Credit: Teppo Niinimäki
- version (unittest) scope(success)
+ version (StdUnittest) scope(success)
{
- assert(!lt(r[c], r[a]));
- assert(!lt(r[c], r[b]));
- assert(!lt(r[d], r[c]));
- assert(!lt(r[e], r[c]));
+ assert(!lt(r[c], r[a]), "less than check failed");
+ assert(!lt(r[c], r[b]), "less than check failed");
+ assert(!lt(r[d], r[c]), "less than check failed");
+ assert(!lt(r[e], r[c]), "less than check failed");
}
if (lt(r[c], r[a])) r.swapAt(a, c);
@@ -4025,16 +4374,16 @@ if (isRandomAccessRange!Range && hasLength!Range &&
// nextPermutation
/**
- * Permutes $(D range) in-place to the next lexicographically greater
+ * Permutes `range` in-place to the next lexicographically greater
* permutation.
*
- * The predicate $(D less) defines the lexicographical ordering to be used on
+ * The predicate `less` defines the lexicographical ordering to be used on
* the range.
*
* If the range is currently the lexicographically greatest permutation, it is
* permuted back to the least permutation and false is returned. Otherwise,
* true is returned. One can thus generate all permutations of a range by
- * sorting it according to $(D less), which produces the lexicographically
+ * sorting it according to `less`, which produces the lexicographically
* least permutation, and then calling nextPermutation until it returns false.
* This is guaranteed to generate all distinct permutations of the range
* exactly once. If there are $(I N) elements in the range and all of them are
@@ -4094,7 +4443,7 @@ if (isBidirectionalRange!BidirectionalRange &&
auto j = find!((a) => binaryFun!less(i.front, a))(
takeExactly(retro(range), n));
- assert(!j.empty); // shouldn't happen since i.front < last.front
+ assert(!j.empty, "j must not be empty"); // shouldn't happen since i.front < last.front
swap(i.front, j.front);
reverse(takeExactly(retro(range), n));
@@ -4242,7 +4591,7 @@ if (isBidirectionalRange!BidirectionalRange &&
assert(a == [3,2,1]);
}
-// Issue 13594
+// https://issues.dlang.org/show_bug.cgi?id=13594
@safe unittest
{
int[3] a = [1,2,3];
@@ -4252,10 +4601,10 @@ if (isBidirectionalRange!BidirectionalRange &&
// nextEvenPermutation
/**
- * Permutes $(D range) in-place to the next lexicographically greater $(I even)
+ * Permutes `range` in-place to the next lexicographically greater $(I even)
* permutation.
*
- * The predicate $(D less) defines the lexicographical ordering to be used on
+ * The predicate `less` defines the lexicographical ordering to be used on
* the range.
*
* An even permutation is one which is produced by swapping an even number of
@@ -4351,7 +4700,7 @@ if (isBidirectionalRange!BidirectionalRange &&
takeExactly(retro(range), n));
// shouldn't happen since i.front < last.front
- assert(!j.empty);
+ assert(!j.empty, "j must not be empty");
swap(i.front, j.front);
oddParity = !oddParity;
@@ -4417,9 +4766,9 @@ if (isBidirectionalRange!BidirectionalRange &&
assert(b == [ 1, 3, 2 ]);
}
+// https://issues.dlang.org/show_bug.cgi?id=13594
@safe unittest
{
- // Issue 13594
int[3] a = [1,2,3];
assert(nextEvenPermutation(a[]));
assert(a == [2,3,1]);
@@ -4431,7 +4780,7 @@ shapes. Here's a non-trivial example:
*/
@safe unittest
{
- import std.math : sqrt;
+ import std.math.algebraic : sqrt;
// Print the 60 vertices of a uniform truncated icosahedron (soccer ball)
enum real Phi = (1.0 + sqrt(5.0)) / 2.0; // Golden ratio
@@ -4466,3 +4815,177 @@ shapes. Here's a non-trivial example:
}
assert(n == 60);
}
+
+/**
+Permutes `range` into the `perm` permutation.
+
+The algorithm has a constant runtime complexity with respect to the number of
+permutations created.
+Due to the number of unique values of `ulong` only the first 21 elements of
+`range` can be permuted. The rest of the range will therefore not be
+permuted.
+This algorithm uses the $(HTTP en.wikipedia.org/wiki/Lehmer_code, Lehmer
+Code).
+
+The algorithm works as follows:
+$(D_CODE
+ auto pem = [4,0,4,1,0,0,0]; // permutation 2982 in factorial
+ auto src = [0,1,2,3,4,5,6]; // the range to permutate
+
+ auto i = 0; // range index
+ // range index iterates pem and src in sync
+ // pem[i] + i is used as index into src
+ // first src[pem[i] + i] is stored in t
+ auto t = 4; // tmp value
+ src = [0,1,2,3,n,5,6];
+
+ // then the values between i and pem[i] + i are moved one
+ // to the right
+ src = [n,0,1,2,3,5,6];
+ // at last t is inserted into position i
+ src = [4,0,1,2,3,5,6];
+ // finally i is incremented
+ ++i;
+
+ // this process is repeated while i < pem.length
+
+ t = 0;
+ src = [4,n,1,2,3,5,6];
+ src = [4,0,1,2,3,5,6];
+ ++i;
+ t = 6;
+ src = [4,0,1,2,3,5,n];
+ src = [4,0,n,1,2,3,5];
+ src = [4,0,6,1,2,3,5];
+)
+
+Returns:
+ The permuted range.
+
+Params:
+ range = The Range to permute. The original ordering will be lost.
+ perm = The permutation to permutate `range` to.
+*/
+auto ref Range nthPermutation(Range)
+ (auto ref Range range, const ulong perm)
+if (isRandomAccessRange!Range && hasLength!Range)
+{
+ if (!nthPermutationImpl(range, perm))
+ {
+ throw new Exception(
+ "The range to permutate must not have less"
+ ~ " elements than the factorial number has digits");
+ }
+
+ return range;
+}
+
+///
+pure @safe unittest
+{
+ auto src = [0, 1, 2, 3, 4, 5, 6];
+ auto rslt = [4, 0, 6, 2, 1, 3, 5];
+
+ src = nthPermutation(src, 2982);
+ assert(src == rslt);
+}
+
+/**
+Returns: `true` in case the permutation worked, `false` in case `perm` had
+ more digits in the factorial number system than range had elements.
+ This case must not occur as this would lead to out of range accesses.
+*/
+bool nthPermutationImpl(Range)
+ (auto ref Range range, ulong perm)
+if (isRandomAccessRange!Range && hasLength!Range)
+{
+ import std.range.primitives : ElementType;
+ import std.numeric : decimalToFactorial;
+
+ // ulong.max has 21 digits in the factorial number system
+ ubyte[21] fac;
+ size_t idx = decimalToFactorial(perm, fac);
+
+ if (idx > range.length)
+ {
+ return false;
+ }
+
+ ElementType!Range tmp;
+ size_t i = 0;
+
+ for (; i < idx; ++i)
+ {
+ size_t re = fac[i];
+ tmp = range[re + i];
+ for (size_t j = re + i; j > i; --j)
+ {
+ range[j] = range[j - 1];
+ }
+ range[i] = tmp;
+ }
+
+ return true;
+}
+
+///
+pure @safe unittest
+{
+ auto src = [0, 1, 2, 3, 4, 5, 6];
+ auto rslt = [4, 0, 6, 2, 1, 3, 5];
+
+ bool worked = nthPermutationImpl(src, 2982);
+ assert(worked);
+ assert(src == rslt);
+}
+
+pure @safe unittest
+{
+ auto rslt = [4, 0, 6, 2, 1, 3, 5];
+
+ auto src = nthPermutation([0, 1, 2, 3, 4, 5, 6], 2982);
+ assert(src == rslt);
+}
+
+pure @safe unittest
+{
+ auto src = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ auto rslt = [4, 0, 6, 2, 1, 3, 5, 7, 8, 9, 10];
+
+ src = nthPermutation(src, 2982);
+ assert(src == rslt);
+}
+
+pure @safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto src = [0, 1, 2, 3];
+
+ assertThrown(nthPermutation(src, 2982));
+}
+
+pure @safe unittest
+{
+ import std.internal.test.dummyrange;
+ import std.meta : AliasSeq;
+
+ auto src = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ auto rsl = [4, 0, 6, 2, 1, 3, 5, 7, 8, 9, 10];
+
+ foreach (T; AliasSeq!(
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, int[]),
+ DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, int[])))
+ {
+ static assert(isRandomAccessRange!(T));
+ static assert(hasLength!(T));
+ auto dr = T(src.dup);
+ dr = nthPermutation(dr, 2982);
+
+ int idx;
+ foreach (it; dr)
+ {
+ assert(it == rsl[idx++]);
+ }
+ }
+}
diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d
index 179fa664795..ded1196da52 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");
+}
diff --git a/libphobos/src/std/ascii.d b/libphobos/src/std/ascii.d
index b430114e516..3b6face1dc1 100644
--- a/libphobos/src/std/ascii.d
+++ b/libphobos/src/std/ascii.d
@@ -3,9 +3,9 @@
/++
Functions which operate on ASCII characters.
- All of the functions in std._ascii accept Unicode characters but
- effectively ignore them if they're not ASCII. All $(D isX) functions return
- $(D false) for non-ASCII characters, and all $(D toX) functions do nothing
+ All of the functions in std.ascii accept Unicode characters but
+ effectively ignore them if they're not ASCII. All `isX` functions return
+ `false` for non-ASCII characters, and all `toX` functions do nothing
to non-ASCII characters.
For functions which operate on Unicode characters, see
@@ -46,6 +46,7 @@ $(TR $(TD Constants) $(TD
$(LREF whitespace)
))
$(TR $(TD Enums) $(TD
+ $(LREF ControlChar)
$(LREF LetterCase)
))
))
@@ -54,20 +55,12 @@ $(TR $(TD Enums) $(TD
$(HTTP en.wikipedia.org/wiki/Ascii, Wikipedia)
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: $(HTTP digitalmars.com, Walter Bright) and Jonathan M Davis
- Source: $(PHOBOSSRC std/_ascii.d)
+ Authors: $(HTTP digitalmars.com, Walter Bright) and
+ $(HTTP jmdavisprog.com, Jonathan M Davis)
+ Source: $(PHOBOSSRC std/ascii.d)
+/
module std.ascii;
-version (unittest)
-{
- // FIXME: When dmd bug #314 is fixed, make these selective.
- import std.meta; // : AliasSeq;
- import std.range; // : chain;
- import std.traits; // : functionAttributes, FunctionAttribute, isSafe;
-}
-
-
immutable fullHexDigits = "0123456789ABCDEFabcdef"; /// 0 .. 9A .. Fa .. f
immutable hexDigits = fullHexDigits[0 .. 16]; /// 0 .. 9A .. F
immutable lowerHexDigits = "0123456789abcdef"; /// 0 .. 9a .. f
@@ -97,10 +90,10 @@ enum LetterCase : bool
}
///
-@system unittest
+@safe unittest
{
import std.digest.hmac : hmac;
- import std.digest.digest : toHexString;
+ import std.digest : toHexString;
import std.digest.sha : SHA1;
import std.string : representation;
@@ -110,6 +103,75 @@ enum LetterCase : bool
assert(sha1HMAC == "49f2073c7bf58577e8c9ae59fe8cfd37c9ab94e5");
}
+/++
+ All control characters in the ASCII table ($(HTTPS www.asciitable.com, source)).
++/
+enum ControlChar : char
+{
+ nul = '\x00', /// Null
+ soh = '\x01', /// Start of heading
+ stx = '\x02', /// Start of text
+ etx = '\x03', /// End of text
+ eot = '\x04', /// End of transmission
+ enq = '\x05', /// Enquiry
+ ack = '\x06', /// Acknowledge
+ bel = '\x07', /// Bell
+ bs = '\x08', /// Backspace
+ tab = '\x09', /// Horizontal tab
+ lf = '\x0A', /// NL line feed, new line
+ vt = '\x0B', /// Vertical tab
+ ff = '\x0C', /// NP form feed, new page
+ cr = '\x0D', /// Carriage return
+ so = '\x0E', /// Shift out
+ si = '\x0F', /// Shift in
+ dle = '\x10', /// Data link escape
+ dc1 = '\x11', /// Device control 1
+ dc2 = '\x12', /// Device control 2
+ dc3 = '\x13', /// Device control 3
+ dc4 = '\x14', /// Device control 4
+ nak = '\x15', /// Negative acknowledge
+ syn = '\x16', /// Synchronous idle
+ etb = '\x17', /// End of transmission block
+ can = '\x18', /// Cancel
+ em = '\x19', /// End of medium
+ sub = '\x1A', /// Substitute
+ esc = '\x1B', /// Escape
+ fs = '\x1C', /// File separator
+ gs = '\x1D', /// Group separator
+ rs = '\x1E', /// Record separator
+ us = '\x1F', /// Unit separator
+ del = '\x7F' /// Delete
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.algorithm.comparison, std.algorithm.searching, std.range, std.traits;
+
+ // Because all ASCII characters fit in char, so do these
+ static assert(ControlChar.ack.sizeof == 1);
+
+ // All control characters except del are in row starting from 0
+ static assert(EnumMembers!ControlChar.only.until(ControlChar.del).equal(iota(32)));
+
+ static assert(ControlChar.nul == '\0');
+ static assert(ControlChar.bel == '\a');
+ static assert(ControlChar.bs == '\b');
+ static assert(ControlChar.ff == '\f');
+ static assert(ControlChar.lf == '\n');
+ static assert(ControlChar.cr == '\r');
+ static assert(ControlChar.tab == '\t');
+ static assert(ControlChar.vt == '\v');
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.conv;
+ //Control character table can be used in place of hexcodes.
+ with (ControlChar) assert(text("Phobos", us, "Deimos", us, "Tango", rs) == "Phobos\x1FDeimos\x1FTango\x1E");
+}
+
/// Newline sequence for this system.
version (Windows)
immutable newline = "\r\n";
@@ -121,7 +183,7 @@ else
/++
Params: c = The character to test.
- Returns: Whether $(D c) is a letter or a number (0 .. 9, a .. z, A .. Z).
+ Returns: Whether `c` is a letter or a number (0 .. 9, a .. z, A .. Z).
+/
bool isAlphaNum(dchar c) @safe pure nothrow @nogc
{
@@ -141,6 +203,7 @@ bool isAlphaNum(dchar c) @safe pure nothrow @nogc
@safe unittest
{
+ import std.range;
foreach (c; chain(digits, octalDigits, fullHexDigits, letters, lowercase, uppercase))
assert(isAlphaNum(c));
@@ -151,7 +214,7 @@ bool isAlphaNum(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether $(D c) is an ASCII letter (A .. Z, a .. z).
+ Returns: Whether `c` is an ASCII letter (A .. Z, a .. z).
+/
bool isAlpha(dchar c) @safe pure nothrow @nogc
{
@@ -172,6 +235,7 @@ bool isAlpha(dchar c) @safe pure nothrow @nogc
@safe unittest
{
+ import std.range;
foreach (c; chain(letters, lowercase, uppercase))
assert(isAlpha(c));
@@ -182,7 +246,7 @@ bool isAlpha(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether $(D c) is a lowercase ASCII letter (a .. z).
+ Returns: Whether `c` is a lowercase ASCII letter (a .. z).
+/
bool isLower(dchar c) @safe pure nothrow @nogc
{
@@ -203,6 +267,7 @@ bool isLower(dchar c) @safe pure nothrow @nogc
@safe unittest
{
+ import std.range;
foreach (c; lowercase)
assert(isLower(c));
@@ -213,7 +278,7 @@ bool isLower(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether $(D c) is an uppercase ASCII letter (A .. Z).
+ Returns: Whether `c` is an uppercase ASCII letter (A .. Z).
+/
bool isUpper(dchar c) @safe pure nothrow @nogc
{
@@ -234,6 +299,7 @@ bool isUpper(dchar c) @safe pure nothrow @nogc
@safe unittest
{
+ import std.range;
foreach (c; uppercase)
assert(isUpper(c));
@@ -244,7 +310,7 @@ bool isUpper(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether $(D c) is a digit (0 .. 9).
+ Returns: Whether `c` is a digit (0 .. 9).
+/
bool isDigit(dchar c) @safe pure nothrow @nogc
{
@@ -266,6 +332,7 @@ bool isDigit(dchar c) @safe pure nothrow @nogc
@safe unittest
{
+ import std.range;
foreach (c; digits)
assert(isDigit(c));
@@ -276,7 +343,7 @@ bool isDigit(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether $(D c) is a digit in base 8 (0 .. 7).
+ Returns: Whether `c` is a digit in base 8 (0 .. 7).
+/
bool isOctalDigit(dchar c) @safe pure nothrow @nogc
{
@@ -295,6 +362,7 @@ bool isOctalDigit(dchar c) @safe pure nothrow @nogc
@safe unittest
{
+ import std.range;
foreach (c; octalDigits)
assert(isOctalDigit(c));
@@ -305,7 +373,7 @@ bool isOctalDigit(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether $(D c) is a digit in base 16 (0 .. 9, A .. F, a .. f).
+ Returns: Whether `c` is a digit in base 16 (0 .. 9, A .. F, a .. f).
+/
bool isHexDigit(dchar c) @safe pure nothrow @nogc
{
@@ -325,6 +393,7 @@ bool isHexDigit(dchar c) @safe pure nothrow @nogc
@safe unittest
{
+ import std.range;
foreach (c; fullHexDigits)
assert(isHexDigit(c));
@@ -335,7 +404,7 @@ bool isHexDigit(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether or not $(D c) is a whitespace character. That includes the
+ Returns: Whether or not `c` is a whitespace character. That includes the
space, tab, vertical tab, form feed, carriage return, and linefeed
characters.
+/
@@ -362,6 +431,7 @@ bool isWhite(dchar c) @safe pure nothrow @nogc
@safe unittest
{
+ import std.range;
foreach (c; whitespace)
assert(isWhite(c));
@@ -372,7 +442,7 @@ bool isWhite(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether $(D c) is a control character.
+ Returns: Whether `c` is a control character.
+/
bool isControl(dchar c) @safe pure nothrow @nogc
{
@@ -398,6 +468,7 @@ bool isControl(dchar c) @safe pure nothrow @nogc
@safe unittest
{
+ import std.range;
foreach (dchar c; 0 .. 32)
assert(isControl(c));
assert(isControl(127));
@@ -409,7 +480,7 @@ bool isControl(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether or not $(D c) is a punctuation character. That includes
+ Returns: Whether or not `c` is a punctuation character. That includes
all ASCII characters which are not control characters, letters, digits, or
whitespace.
+/
@@ -454,7 +525,7 @@ bool isPunctuation(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether or not $(D c) is a printable character other than the
+ Returns: Whether or not `c` is a printable character other than the
space character.
+/
bool isGraphical(dchar c) @safe pure nothrow @nogc
@@ -490,7 +561,7 @@ bool isGraphical(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether or not $(D c) is a printable character - including the
+ Returns: Whether or not `c` is a printable character - including the
space character.
+/
bool isPrintable(dchar c) @safe pure nothrow @nogc
@@ -525,7 +596,7 @@ bool isPrintable(dchar c) @safe pure nothrow @nogc
/++
Params: c = The character to test.
- Returns: Whether or not $(D c) is in the ASCII character set - i.e. in the
+ Returns: Whether or not `c` is in the ASCII character set - i.e. in the
range 0 .. 0x7F.
+/
pragma(inline, true)
@@ -553,24 +624,23 @@ bool isASCII(dchar c) @safe pure nothrow @nogc
/++
Converts an ASCII letter to lowercase.
- Params: c = A character of any type that implicitly converts to $(D dchar).
+ Params: c = A character of any type that implicitly converts to `dchar`.
In the case where it's a built-in type, or an enum of a built-in type,
- $(D Unqual!(OriginalType!C)) is returned, whereas if it's a user-defined
- type, $(D dchar) is returned.
+ `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined
+ type, `dchar` is returned.
- Returns: The corresponding lowercase letter, if $(D c) is an uppercase
- ASCII character, otherwise $(D c) itself.
+ Returns: The corresponding lowercase letter, if `c` is an uppercase
+ ASCII character, otherwise `c` itself.
+/
auto toLower(C)(C c)
if (is(C : dchar))
{
- import std.traits : isAggregateType, OriginalType, Unqual;
+ import std.traits : OriginalType;
- alias OC = OriginalType!C;
- static if (isAggregateType!OC)
+ static if (!__traits(isScalar, C))
alias R = dchar;
- else
- alias R = Unqual!OC;
+ else static if (is(immutable OriginalType!C == immutable OC, OC))
+ alias R = OC;
return isUpper(c) ? cast(R)(cast(R) c + 'a' - 'A') : cast(R) c;
}
@@ -589,7 +659,8 @@ if (is(C : dchar))
@safe pure nothrow unittest
{
- foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
+ import std.meta;
+ static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
{
foreach (i, c; uppercase)
assert(toLower(cast(C) c) == lowercase[i]);
@@ -615,24 +686,23 @@ if (is(C : dchar))
/++
Converts an ASCII letter to uppercase.
- Params: c = Any type which implicitly converts to $(D dchar). In the case
+ Params: c = Any type which implicitly converts to `dchar`. In the case
where it's a built-in type, or an enum of a built-in type,
- $(D Unqual!(OriginalType!C)) is returned, whereas if it's a user-defined
- type, $(D dchar) is returned.
+ `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined
+ type, `dchar` is returned.
- Returns: The corresponding uppercase letter, if $(D c) is a lowercase ASCII
- character, otherwise $(D c) itself.
+ Returns: The corresponding uppercase letter, if `c` is a lowercase ASCII
+ character, otherwise `c` itself.
+/
auto toUpper(C)(C c)
if (is(C : dchar))
{
- import std.traits : isAggregateType, OriginalType, Unqual;
+ import std.traits : OriginalType;
- alias OC = OriginalType!C;
- static if (isAggregateType!OC)
+ static if (!__traits(isScalar, C))
alias R = dchar;
- else
- alias R = Unqual!OC;
+ else static if (is(immutable OriginalType!C == immutable OC, OC))
+ alias R = OC;
return isLower(c) ? cast(R)(cast(R) c - ('a' - 'A')) : cast(R) c;
}
@@ -650,7 +720,8 @@ if (is(C : dchar))
@safe pure nothrow unittest
{
- foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
+ import std.meta;
+ static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
{
foreach (i, c; lowercase)
assert(toUpper(cast(C) c) == uppercase[i]);
@@ -675,6 +746,9 @@ if (is(C : dchar))
@safe unittest //Test both toUpper and toLower with non-builtin
{
+ import std.meta;
+ import std.traits;
+
//User Defined [Char|Wchar|Dchar]
static struct UDC { char c; alias c this; }
static struct UDW { wchar c; alias c this; }
@@ -689,7 +763,7 @@ if (is(C : dchar))
enum UDDE : UDD {a = UDD('a'), A = UDD('A')}
//User defined types with implicit cast to dchar test.
- foreach (Char; AliasSeq!(UDC, UDW, UDD))
+ static foreach (Char; AliasSeq!(UDC, UDW, UDD))
{
assert(toLower(Char('a')) == 'a');
assert(toLower(Char('A')) == 'a');
@@ -700,7 +774,7 @@ if (is(C : dchar))
}
//Various enum tests.
- foreach (Enum; AliasSeq!(CE, WE, DE, UDCE, UDWE, UDDE))
+ static foreach (Enum; AliasSeq!(CE, WE, DE, UDCE, UDWE, UDDE))
{
assert(toLower(Enum.a) == 'a');
assert(toLower(Enum.A) == 'a');
@@ -713,15 +787,15 @@ if (is(C : dchar))
}
//Return value type tests for enum of non-UDT. These should be the original type.
- foreach (T; AliasSeq!(CE, WE, DE))
- {
+ static foreach (T; AliasSeq!(CE, WE, DE))
+ {{
alias C = OriginalType!T;
static assert(is(typeof(toLower(T.init)) == C));
static assert(is(typeof(toUpper(T.init)) == C));
- }
+ }}
//Return value tests for UDT and enum of UDT. These should be dchar
- foreach (T; AliasSeq!(UDC, UDW, UDD, UDCE, UDWE, UDDE))
+ static foreach (T; AliasSeq!(UDC, UDW, UDD, UDCE, UDWE, UDDE))
{
static assert(is(typeof(toLower(T.init)) == dchar));
static assert(is(typeof(toUpper(T.init)) == dchar));
diff --git a/libphobos/src/std/base64.d b/libphobos/src/std/base64.d
index 6211d10b72a..866f7005060 100644
--- a/libphobos/src/std/base64.d
+++ b/libphobos/src/std/base64.d
@@ -51,15 +51,16 @@
* Copyright: Masahiro Nakagawa 2010-.
* License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Masahiro Nakagawa, Daniel Murphy (Single value Encoder and Decoder)
- * Source: $(PHOBOSSRC std/_base64.d)
+ * Source: $(PHOBOSSRC std/base64.d)
* Macros:
- * LREF2=<a href="#$1">$(D $2)</a>
+ * LREF2=<a href="#$1">`$2`</a>
*/
module std.base64;
-import std.exception; // enforce
-import std.range.primitives; // isInputRange, isOutputRange, isForwardRange, ElementType, hasLength
-import std.traits; // isArray
+import std.exception : enforce;
+import std.range.primitives : empty, front, isInputRange, isOutputRange,
+ isForwardRange, ElementType, hasLength, popFront, put, save;
+import std.traits : isArray;
// Make sure module header code examples work correctly.
@safe unittest
@@ -138,8 +139,8 @@ alias Base64URLNoPadding = Base64Impl!('-', '_', Base64.NoPadding);
* -----
*
* NOTE:
- * Encoded strings will not have any padding if the $(D Padding) parameter is
- * set to $(D NoPadding).
+ * Encoded strings will not have any padding if the `Padding` parameter is
+ * set to `NoPadding`.
*/
template Base64Impl(char Map62th, char Map63th, char Padding = '=')
{
@@ -205,19 +206,19 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
/**
- * Encode $(D_PARAM source) into a $(D char[]) buffer using Base64
+ * Encode $(D_PARAM source) into a `char[]` buffer using Base64
* encoding.
*
* Params:
- * source = The $(LINK2 std_range_primitives.html#isInputRange, input
- * range) to _encode.
- * buffer = The $(D char[]) buffer to store the encoded result.
+ * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * to _encode.
+ * buffer = The `char[]` buffer to store the encoded result.
*
* Returns:
* The slice of $(D_PARAM buffer) that contains the encoded string.
*/
@trusted
- pure char[] encode(R1, R2)(in R1 source, R2 buffer) if (isArray!R1 && is(ElementType!R1 : ubyte) &&
+ pure char[] encode(R1, R2)(in R1 source, return scope R2 buffer) if (isArray!R1 && is(ElementType!R1 : ubyte) &&
is(R2 == char[]))
in
{
@@ -227,7 +228,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
{
assert(result.length == encodeLength(source.length), "The length of result is different from Base64");
}
- body
+ do
{
immutable srcLen = source.length;
if (srcLen == 0)
@@ -310,7 +311,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
// @@@BUG@@@ D's DbC can't caputre an argument of function and store the result of precondition.
//assert(result.length == encodeLength(source.length), "The length of result is different from Base64");
}
- body
+ do
{
immutable srcLen = source.length;
if (srcLen == 0)
@@ -362,7 +363,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
}
// @@@BUG@@@ Workaround for DbC problem. See comment on 'out'.
- version (unittest)
+ version (StdUnittest)
assert(
bufptr - buffer.ptr == encodeLength(srcLen),
"The length of result is different from Base64"
@@ -378,26 +379,25 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
/**
* Encodes $(D_PARAM source) into an
- * $(LINK2 std_range_primitives.html#isOutputRange, output range) using
+ * $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) using
* Base64 encoding.
*
* Params:
- * source = The $(LINK2 std_range_primitives.html#isInputRange, input
- * range) to _encode.
- * range = The $(LINK2 std_range_primitives.html#isOutputRange, output
- * range) to store the encoded result.
+ * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * to _encode.
+ * range = The $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
+ * to store the encoded result.
*
* Returns:
- * The number of times the output range's $(D put) method was invoked.
+ * The number of times the output range's `put` method was invoked.
*/
- size_t encode(R1, R2)(in R1 source, auto ref R2 range)
- if (isArray!R1 && is(ElementType!R1 : ubyte) &&
- !is(R2 == char[]) && isOutputRange!(R2, char))
+ size_t encode(E, R)(scope const(E)[] source, auto ref R range)
+ if (is(E : ubyte) && isOutputRange!(R, char) && !is(R == char[]))
out(result)
{
assert(result == encodeLength(source.length), "The number of put is different from the length of Base64");
}
- body
+ do
{
immutable srcLen = source.length;
if (srcLen == 0)
@@ -405,23 +405,23 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
immutable blocks = srcLen / 3;
immutable remain = srcLen % 3;
- auto srcptr = source.ptr;
- size_t pcount;
+ auto s = source; // copy for out contract length check
+ size_t pcount;
foreach (Unused; 0 .. blocks)
{
- immutable val = srcptr[0] << 16 | srcptr[1] << 8 | srcptr[2];
+ immutable val = s[0] << 16 | s[1] << 8 | s[2];
put(range, EncodeMap[val >> 18 ]);
put(range, EncodeMap[val >> 12 & 0x3f]);
put(range, EncodeMap[val >> 6 & 0x3f]);
put(range, EncodeMap[val & 0x3f]);
- srcptr += 3;
+ s = s[3 .. $];
pcount += 4;
}
if (remain)
{
- immutable val = srcptr[0] << 16 | (remain == 2 ? srcptr[1] << 8 : 0);
+ immutable val = s[0] << 16 | (remain == 2 ? s[1] << 8 : 0);
put(range, EncodeMap[val >> 18 ]);
put(range, EncodeMap[val >> 12 & 0x3f]);
pcount += 2;
@@ -453,22 +453,17 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
}
///
- @system unittest
+ @safe pure nothrow unittest
{
- // @system because encode for OutputRange is @system
- struct OutputRange
- {
- char[] result;
- void put(const(char) ch) @safe { result ~= ch; }
- }
+ import std.array : appender;
+ auto output = appender!string();
ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
// This overload of encode() returns the number of calls to the output
// range's put method.
- OutputRange output;
assert(Base64.encode(data, output) == 8);
- assert(output.result == "Gis8TV1u");
+ assert(output.data == "Gis8TV1u");
}
@@ -481,12 +476,6 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
size_t encode(R1, R2)(R1 source, auto ref R2 range)
if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) &&
hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char))
- out(result)
- {
- // @@@BUG@@@ Workaround for DbC problem.
- //assert(result == encodeLength(source.length), "The number of put is different from the length of Base64");
- }
- body
{
immutable srcLen = source.length;
if (srcLen == 0)
@@ -546,7 +535,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
}
// @@@BUG@@@ Workaround for DbC problem.
- version (unittest)
+ version (StdUnittest)
assert(
pcount == encodeLength(srcLen),
"The number of put is different from the length of Base64"
@@ -563,11 +552,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* buffers.
*
* Params:
- * source = The $(LINK2 std_range_primitives.html#isInputRange, input
- * range) to _encode.
+ * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * to _encode.
*
* Returns:
- * A newly-allocated $(D char[]) buffer containing the encoded string.
+ * A newly-allocated `char[]` buffer containing the encoded string.
*/
@safe
pure char[] encode(Range)(Range source) if (isArray!Range && is(ElementType!Range : ubyte))
@@ -594,12 +583,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
/**
- * An $(LINK2 std_range_primitives.html#isInputRange, input range) that
+ * An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that
* iterates over the respective Base64 encodings of a range of data items.
*
- * This range will be a $(LINK2 std_range_primitives.html#isForwardRange,
- * forward range) if the underlying data source is at least a forward
- * range.
+ * This range will be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ * if the underlying data source is at least a forward range.
*
* Note: This struct is not intended to be created in user code directly;
* use the $(LREF encoder) function instead.
@@ -616,7 +604,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
this(Range range)
{
range_ = range;
- doEncoding();
+ if (!empty)
+ doEncoding();
}
@@ -645,8 +634,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* Advance the range to the next chunk of encoded data.
*
* Throws:
- * $(D Base64Exception) If invoked when
- * $(LREF2 .Base64Impl.Encoder.empty, empty) returns $(D true).
+ * `Base64Exception` If invoked when
+ * $(LREF2 .Base64Impl.Encoder.empty, empty) returns `true`.
*/
void popFront()
{
@@ -671,11 +660,10 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* Save the current iteration state of the range.
*
* This method is only available if the underlying range is a
- * $(LINK2 std_range_primitives.html#isForwardRange, forward
- * range).
+ * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives).
*
* Returns:
- * A copy of $(D this).
+ * A copy of `this`.
*/
@property
typeof(this) save()
@@ -705,11 +693,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
/**
- * An $(LINK2 std_range_primitives.html#isInputRange, input range) that
+ * An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that
* iterates over the encoded bytes of the given source data.
*
- * It will be a $(LINK2 std_range_primitives.html#isForwardRange, forward
- * range) if the underlying data source is at least a forward range.
+ * It will be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ * if the underlying data source is at least a forward range.
*
* Note: This struct is not intended to be created in user code directly;
* use the $(LREF encoder) function instead.
@@ -764,8 +752,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* Advance to the next encoded character.
*
* Throws:
- * $(D Base64Exception) If invoked when $(LREF2 .Base64Impl.Encoder.empty.2,
- * empty) returns $(D true).
+ * `Base64Exception` If invoked when $(LREF2 .Base64Impl.Encoder.empty.2,
+ * empty) returns `true`.
*/
void popFront()
{
@@ -835,11 +823,10 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* Save the current iteration state of the range.
*
* This method is only available if the underlying range is a
- * $(LINK2 std_range_primitives.html#isForwardRange, forward
- * range).
+ * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives).
*
* Returns:
- * A copy of $(D this).
+ * A copy of `this`.
*/
@property
typeof(this) save()
@@ -853,23 +840,23 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
/**
- * Construct an $(D Encoder) that iterates over the Base64 encoding of the
- * given $(LINK2 std_range_primitives.html#isInputRange, input range).
+ * Construct an `Encoder` that iterates over the Base64 encoding of the
+ * given $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
*
* Params:
- * range = An $(LINK2 std_range_primitives.html#isInputRange, input
- * range) over the data to be encoded.
+ * range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * over the data to be encoded.
*
* Returns:
- * If $(D_PARAM range) is a range of bytes, an $(D Encoder) that iterates
+ * If $(D_PARAM range) is a range of bytes, an `Encoder` that iterates
* over the bytes of the corresponding Base64 encoding.
*
- * If $(D_PARAM range) is a range of ranges of bytes, an $(D Encoder) that
+ * If $(D_PARAM range) is a range of ranges of bytes, an `Encoder` that
* iterates over the Base64 encoded strings of each element of the range.
*
- * In both cases, the returned $(D Encoder) will be a
- * $(LINK2 std_range_primitives.html#isForwardRange, forward range) if the
- * given $(D range) is at least a forward range, otherwise it will be only
+ * In both cases, the returned `Encoder` will be a
+ * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) if the
+ * given `range` is at least a forward range, otherwise it will be only
* an input range.
*
* Example:
@@ -982,19 +969,19 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* Decodes $(D_PARAM source) into the given buffer.
*
* Params:
- * source = The $(LINK2 std_range_primitives.html#isInputRange, input
- * range) to _decode.
+ * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * to _decode.
* buffer = The buffer to store decoded result.
*
* Returns:
* The slice of $(D_PARAM buffer) containing the decoded result.
*
* Throws:
- * $(D Base64Exception) if $(D_PARAM source) contains characters outside the
+ * `Base64Exception` if $(D_PARAM source) contains characters outside the
* base alphabet of the current Base64 encoding scheme.
*/
@trusted
- pure ubyte[] decode(R1, R2)(in R1 source, R2 buffer) if (isArray!R1 && is(ElementType!R1 : dchar) &&
+ pure ubyte[] decode(R1, R2)(in R1 source, return scope R2 buffer) if (isArray!R1 && is(ElementType!R1 : dchar) &&
is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
in
{
@@ -1005,7 +992,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
immutable expect = realDecodeLength(source);
assert(result.length == expect, "The length of result is different from the expected length");
}
- body
+ do
{
immutable srcLen = source.length;
if (srcLen == 0)
@@ -1085,13 +1072,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
{
assert(buffer.length >= decodeLength(source.length), "Insufficient buffer for decoding");
}
- out(result)
- {
- // @@@BUG@@@ Workaround for DbC problem.
- //immutable expect = decodeLength(source.length) - 2;
- //assert(result.length >= expect, "The length of result is smaller than expected length");
- }
- body
+ do
{
immutable srcLen = source.length;
if (srcLen == 0)
@@ -1143,8 +1124,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
}
}
- // @@@BUG@@@ Workaround for DbC problem.
- version (unittest)
+ // We need to do the check here because we have consumed the length
+ version (StdUnittest)
assert(
(bufptr - buffer.ptr) >= (decodeLength(srcLen) - 2),
"The length of result is smaller than expected length"
@@ -1159,19 +1140,19 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
/**
* Decodes $(D_PARAM source) into a given
- * $(LINK2 std_range_primitives.html#isOutputRange, output range).
+ * $(REF_ALTTEXT output range, isOutputRange, std,range,primitives).
*
* Params:
- * source = The $(LINK2 std_range_primitives.html#isInputRange, input
- * range) to _decode.
- * range = The $(LINK2 std_range_primitives.html#isOutputRange, output
- * range) to store the decoded result.
+ * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * to _decode.
+ * range = The $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)
+ * to store the decoded result.
*
* Returns:
- * The number of times the output range's $(D put) method was invoked.
+ * The number of times the output range's `put` method was invoked.
*
* Throws:
- * $(D Base64Exception) if $(D_PARAM source) contains characters outside the
+ * `Base64Exception` if $(D_PARAM source) contains characters outside the
* base alphabet of the current Base64 encoding scheme.
*/
size_t decode(R1, R2)(in R1 source, auto ref R2 range)
@@ -1182,7 +1163,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
immutable expect = realDecodeLength(source);
assert(result == expect, "The result of decode is different from the expected");
}
- body
+ do
{
immutable srcLen = source.length;
if (srcLen == 0)
@@ -1271,7 +1252,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
//immutable expect = decodeLength(source.length) - 2;
//assert(result >= expect, "The length of result is smaller than expected length");
}
- body
+ do
{
immutable srcLen = source.length;
if (srcLen == 0)
@@ -1329,7 +1310,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
}
// @@@BUG@@@ Workaround for DbC problem.
- version (unittest)
+ version (StdUnittest)
assert(
pcount >= (decodeLength(srcLen) - 2),
"The length of result is smaller than expected length"
@@ -1346,11 +1327,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* buffers.
*
* Params:
- * source = The $(LINK2 std_range_primitives.html#isInputRange, input
- * range) to _decode.
+ * source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * to _decode.
*
* Returns:
- * A newly-allocated $(D ubyte[]) buffer containing the decoded string.
+ * A newly-allocated `ubyte[]` buffer containing the decoded string.
*/
@safe
pure ubyte[] decode(Range)(Range source) if (isArray!Range && is(ElementType!Range : dchar))
@@ -1377,12 +1358,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
/**
- * An $(LINK2 std_range_primitives.html#isInputRange, input range) that
+ * An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that
* iterates over the decoded data of a range of Base64 encodings.
*
- * This range will be a $(LINK2 std_range_primitives.html#isForwardRange,
- * forward range) if the underlying data source is at least a forward
- * range.
+ * This range will be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ * if the underlying data source is at least a forward range.
*
* Note: This struct is not intended to be created in user code directly;
* use the $(LREF decoder) function instead.
@@ -1399,7 +1379,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
this(Range range)
{
range_ = range;
- doDecoding();
+ if (!empty)
+ doDecoding();
}
@@ -1428,8 +1409,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* Advance to the next element in the input to be decoded.
*
* Throws:
- * $(D Base64Exception) if invoked when $(LREF2 .Base64Impl.Decoder.empty,
- * empty) returns $(D true).
+ * `Base64Exception` if invoked when $(LREF2 .Base64Impl.Decoder.empty,
+ * empty) returns `true`.
*/
void popFront()
{
@@ -1451,10 +1432,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* Saves the current iteration state.
*
* This method is only available if the underlying range is a
- * $(LINK2 std_range_primitives.html#isForwardRange, forward
- * range).
+ * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
*
- * Returns: A copy of $(D this).
+ * Returns: A copy of `this`.
*/
@property
typeof(this) save()
@@ -1488,6 +1468,7 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
while (data.length % 4 != 0)
{
range_.popFront();
+ enforce(!range_.empty, new Base64Exception("Invalid length of encoded data"));
data ~= cast(const(char)[])range_.front;
}
}
@@ -1502,12 +1483,11 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
/**
- * An $(LINK2 std_range_primitives.html#isInputRange, input range) that
+ * An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that
* iterates over the bytes of data decoded from a Base64 encoded string.
*
- * This range will be a $(LINK2 std_range_primitives.html#isForwardRange,
- * forward range) if the underlying data source is at least a forward
- * range.
+ * This range will be a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ * if the underlying data source is at least a forward range.
*
* Note: This struct is not intended to be created in user code directly;
* use the $(LREF decoder) function instead.
@@ -1562,8 +1542,8 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* Advance to the next decoded byte.
*
* Throws:
- * $(D Base64Exception) if invoked when $(LREF2 .Base64Impl.Decoder.empty,
- * empty) returns $(D true).
+ * `Base64Exception` if invoked when $(LREF2 .Base64Impl.Decoder.empty,
+ * empty) returns `true`.
*/
void popFront()
{
@@ -1644,10 +1624,9 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* Saves the current iteration state.
*
* This method is only available if the underlying range is a
- * $(LINK2 std_range_primitives.html#isForwardRange, forward
- * range).
+ * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
*
- * Returns: A copy of $(D this).
+ * Returns: A copy of `this`.
*/
@property
typeof(this) save()
@@ -1661,31 +1640,30 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
/**
- * Construct a $(D Decoder) that iterates over the decoding of the given
+ * Construct a `Decoder` that iterates over the decoding of the given
* Base64 encoded data.
*
* Params:
- * range = An $(LINK2 std_range_primitives.html#isInputRange, input
- * range) over the data to be decoded.
+ * range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * over the data to be decoded, or a `char` array. Will not accept
+ * `wchar[]` nor `dchar[]`.
*
* Returns:
- * If $(D_PARAM range) is a range of characters, a $(D Decoder) that
+ * If $(D_PARAM range) is a range or array of `char`, a `Decoder` that
* iterates over the bytes of the corresponding Base64 decoding.
*
- * If $(D_PARAM range) is a range of ranges of characters, a $(D Decoder)
+ * If $(D_PARAM range) is a range of ranges of characters, a `Decoder`
* that iterates over the decoded strings corresponding to each element of
- * the range. In this case, the length of each subrange must be a multiple
- * of 4; the returned _decoder does not keep track of Base64 decoding
- * state across subrange boundaries.
+ * the range.
*
- * In both cases, the returned $(D Decoder) will be a
- * $(LINK2 std_range_primitives.html#isForwardRange, forward range) if the
- * given $(D range) is at least a forward range, otherwise it will be only
+ * In both cases, the returned `Decoder` will be a
+ * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) if the
+ * given `range` is at least a forward range, otherwise it will be only
* an input range.
*
* If the input data contains characters not found in the base alphabet of
* the current Base64 encoding scheme, the returned range may throw a
- * $(D Base64Exception).
+ * `Base64Exception`.
*
* Example:
* This example shows decoding over a range of input data lines.
@@ -1696,7 +1674,6 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
* }
* -----
*
- * Example:
* This example shows decoding one byte at a time.
* -----
* auto encoded = Base64.encoder(cast(ubyte[])"0123456789");
@@ -1711,6 +1688,24 @@ template Base64Impl(char Map62th, char Map63th, char Padding = '=')
return typeof(return)(range);
}
+ /// ditto
+ Decoder!(const(ubyte)[]) decoder()(const(char)[] range)
+ {
+ import std.string : representation;
+ return typeof(return)(range.representation);
+ }
+
+ ///
+ @safe pure unittest
+ {
+ import std.algorithm.comparison : equal;
+ string encoded =
+ "VGhvdSBzaGFsdCBuZXZlciBjb250aW51ZSBhZnRlciBhc3NlcnRpbmcgbnVsbA==";
+
+ assert(Base64.decoder(encoded)
+ .equal("Thou shalt never continue after asserting null"));
+ }
+
private:
@safe
@@ -1766,18 +1761,18 @@ class Base64Exception : Exception
}
///
-@system unittest
+@safe unittest
{
import std.exception : assertThrown;
assertThrown!Base64Exception(Base64.decode("ab|c"));
}
-
@system unittest
{
import std.algorithm.comparison : equal;
import std.algorithm.sorting : sort;
import std.conv;
+ import std.exception : assertThrown;
import std.file;
import std.stdio;
@@ -1920,7 +1915,8 @@ class Base64Exception : Exception
assert(tv["foobar"] == b.data); a.clear(); b.clear();
}
- // @@@9543@@@ These tests were disabled because they actually relied on the input range having length.
+ // https://issues.dlang.org/show_bug.cgi?id=9543
+ // These tests were disabled because they actually relied on the input range having length.
// The implementation (currently) doesn't support encoding/decoding from a length-less source.
version (none)
{ // with InputRange
@@ -2039,7 +2035,7 @@ class Base64Exception : Exception
}
// Regression control for the output range ref bug in encode.
-@system unittest
+@safe unittest
{
struct InputRange
{
@@ -2064,12 +2060,14 @@ class Base64Exception : Exception
// Verify that any existing workaround that uses & still works.
InputRange ir2;
OutputRange or2;
- assert(Base64.encode(ir2, &or2) == 8);
+ () @trusted {
+ assert(Base64.encode(ir2, &or2) == 8);
+ }();
assert(or2.result == "Gis8TV1u");
}
// Regression control for the output range ref bug in decode.
-@system unittest
+@safe unittest
{
struct InputRange
{
@@ -2094,6 +2092,98 @@ class Base64Exception : Exception
// Verify that any existing workaround that uses & still works.
InputRange ir2;
OutputRange or2;
- assert(Base64.decode(ir2, &or2) == 6);
+ () @trusted {
+ assert(Base64.decode(ir2, &or2) == 6);
+ }();
assert(or2.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
}
+
+// https://issues.dlang.org/show_bug.cgi?id=21679
+// https://issues.dlang.org/show_bug.cgi?id=21706
+@safe unittest
+{
+ ubyte[][] input;
+ assert(Base64.encoder(input).empty);
+ assert(Base64.decoder(input).empty);
+}
+
+@safe unittest
+{
+ struct InputRange(ubyte[] data)
+ {
+ ubyte[] impl = data;
+ bool empty() { return impl.length == 0; }
+ ubyte front() { return impl[0]; }
+ void popFront() { impl = impl[1 .. $]; }
+ size_t length() { return impl.length; }
+ }
+
+ struct OutputRange
+ {
+ ubyte[] result;
+ void put(ubyte b) { result ~= b; }
+ }
+
+ void test_encode(ubyte[] data, string result)()
+ {
+ InputRange!data ir;
+ OutputRange or;
+ assert(Base64.encode(ir, or) == result.length);
+ assert(or.result == result);
+ }
+
+ void test_decode(ubyte[] data, string result)()
+ {
+ InputRange!data ir;
+ OutputRange or;
+ assert(Base64.decode(ir, or) == result.length);
+ assert(or.result == result);
+ }
+
+ test_encode!([], "");
+ test_encode!(['x'], "eA==");
+ test_encode!([123, 45], "ey0=");
+
+ test_decode!([], "");
+ test_decode!(['e', 'A', '=', '='], "x");
+ test_decode!(['e', 'y', '0', '='], "{-");
+}
+
+@system unittest
+{
+ // checking forward range
+ auto item = Base64.decoder(Base64.encoder(cast(ubyte[]) "foobar"));
+ auto copy = item.save();
+ item.popFront();
+ assert(item.front == 'o');
+ assert(copy.front == 'f');
+}
+
+@system unittest
+{
+ // checking invalid dchar
+ dchar[] c = cast(dchar[]) "ääää";
+
+ import std.exception : assertThrown;
+ assertThrown!Base64Exception(Base64.decode(c));
+}
+
+@safe unittest
+{
+ import std.array : array;
+
+ char[][] input = [['e', 'y'], ['0', '=']];
+ assert(Base64.decoder(input).array == [[123, 45]]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21707
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ char[][] t1 = [[ 'Z', 'g', '=' ]];
+ assertThrown!Base64Exception(Base64.decoder(t1));
+
+ char[][] t2 = [[ 'e', 'y', '0' ], ['=', '=']];
+ assertThrown!Base64Exception(Base64.decoder(t2));
+}
diff --git a/libphobos/src/std/bigint.d b/libphobos/src/std/bigint.d
index 4c518f2219f..bbb55c288cf 100644
--- a/libphobos/src/std/bigint.d
+++ b/libphobos/src/std/bigint.d
@@ -15,7 +15,7 @@
*
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Don Clugston
- * Source: $(PHOBOSSRC std/_bigint.d)
+ * Source: $(PHOBOSSRC std/bigint.d)
*/
/* Copyright Don Clugston 2008 - 2010.
* Distributed under the Boost Software License, Version 1.0.
@@ -27,15 +27,17 @@ module std.bigint;
import std.conv : ConvException;
-import std.format : FormatSpec, FormatException;
+import std.format.spec : FormatSpec;
+import std.format : FormatException;
import std.internal.math.biguintcore;
+import std.internal.math.biguintnoasm : BigDigit;
import std.range.primitives;
import std.traits;
/** A struct representing an arbitrary precision integer.
*
- * All arithmetic operations are supported, except unsigned shift right (>>>).
- * Bitwise operations (|, &, ^, ~) are supported, and behave as if BigInt was
+ * All arithmetic operations are supported, except unsigned shift right (`>>>`).
+ * Bitwise operations (`|`, `&`, `^`, `~`) are supported, and behave as if BigInt was
* an infinite length 2's complement number.
*
* BigInt implements value semantics using copy-on-write. This means that
@@ -50,7 +52,7 @@ private:
bool sign = false;
public:
/**
- * Construct a BigInt from a decimal or hexadecimal string. The number must
+ * Construct a `BigInt` from a decimal or hexadecimal string. The number must
* be in the form of a decimal or hex literal. It may have a leading `+`
* or `-` sign, followed by `0x` or `0X` if hexadecimal. Underscores are
* permitted in any location after the `0x` and/or the sign of the number.
@@ -65,7 +67,7 @@ public:
isBidirectionalRange!Range &&
isSomeChar!(ElementType!Range) &&
!isInfinite!Range &&
- !isSomeString!Range)
+ !isNarrowString!Range)
{
import std.algorithm.iteration : filterBidirectional;
import std.algorithm.searching : startsWith;
@@ -116,13 +118,14 @@ public:
}
/// ditto
- this(Range)(Range s) pure if (isSomeString!Range)
+ this(Range)(Range s) pure
+ if (isNarrowString!Range)
{
import std.utf : byCodeUnit;
this(s.byCodeUnit);
}
- @system unittest
+ @safe unittest
{
// system because of the dummy ranges eventually call std.array!string
import std.exception : assertThrown;
@@ -144,30 +147,62 @@ public:
assertThrown!ConvException(BigInt(r4));
}
- /// Construct a BigInt from a built-in integral type.
- this(T)(T x) pure nothrow if (isIntegral!T)
+ /**
+ * Construct a `BigInt` from a sign and a magnitude.
+ *
+ * The magnitude is an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * of unsigned integers that satisfies either $(REF hasLength, std,range,primitives)
+ * or $(REF isForwardRange, std,range,primitives). The first (leftmost)
+ * element of the magnitude is considered the most significant.
+ *
+ * Params:
+ * isNegative = true for negative, false for non-negative
+ * (ignored when magnitude is zero)
+ * magnitude = a finite range of unsigned integers
+ */
+ this(Range)(bool isNegative, Range magnitude) if (
+ isInputRange!Range &&
+ isUnsigned!(ElementType!Range) &&
+ (hasLength!Range || isForwardRange!Range) &&
+ !isInfinite!Range)
+ {
+ data.fromMagnitude(magnitude);
+ sign = isNegative && !data.isZero;
+ }
+
+ ///
+ pure @safe unittest
+ {
+ ubyte[] magnitude = [1, 2, 3, 4, 5, 6];
+ auto b1 = BigInt(false, magnitude);
+ assert(cast(long) b1 == 0x01_02_03_04_05_06L);
+ auto b2 = BigInt(true, magnitude);
+ assert(cast(long) b2 == -0x01_02_03_04_05_06L);
+ }
+
+ /// Construct a `BigInt` from a built-in integral type.
+ this(T)(T x) pure nothrow @safe if (isIntegral!T)
{
data = data.init; // @@@: Workaround for compiler bug
opAssign(x);
}
///
- @system unittest
+ @safe unittest
{
- // @system due to failure in FreeBSD32
ulong data = 1_000_000_000_000;
auto bigData = BigInt(data);
- assert(data == BigInt("1_000_000_000_000"));
+ assert(bigData == BigInt("1_000_000_000_000"));
}
- /// Construct a BigInt from another BigInt.
- this(T)(T x) pure nothrow if (is(Unqual!T == BigInt))
+ /// Construct a `BigInt` from another `BigInt`.
+ this(T)(T x) pure nothrow @safe if (is(immutable T == immutable BigInt))
{
opAssign(x);
}
///
- @system unittest
+ @safe unittest
{
const(BigInt) b1 = BigInt("1_234_567_890");
BigInt b2 = BigInt(b1);
@@ -175,7 +210,7 @@ public:
}
/// Assignment from built-in integer types.
- BigInt opAssign(T)(T x) pure nothrow if (isIntegral!T)
+ BigInt opAssign(T)(T x) pure nothrow @safe if (isIntegral!T)
{
data = cast(ulong) absUnsign(x);
sign = (x < 0);
@@ -183,7 +218,7 @@ public:
}
///
- @system unittest
+ @safe unittest
{
auto b = BigInt("123");
b = 456;
@@ -191,7 +226,7 @@ public:
}
/// Assignment from another BigInt.
- BigInt opAssign(T:BigInt)(T x) pure @nogc
+ BigInt opAssign(T:BigInt)(T x) pure @nogc @safe
{
data = x.data;
sign = x.sign;
@@ -199,7 +234,7 @@ public:
}
///
- @system unittest
+ @safe unittest
{
auto b1 = BigInt("123");
auto b2 = BigInt("456");
@@ -209,9 +244,9 @@ public:
/**
* Implements assignment operators from built-in integers of the form
- * $(D BigInt op= integer).
+ * `BigInt op= integer`.
*/
- BigInt opOpAssign(string op, T)(T y) pure nothrow
+ BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope
if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%"
|| op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T)
{
@@ -276,10 +311,11 @@ public:
else if ((y > 0) == (op=="<<"))
{
// Sign never changes during left shift
- data = data.opShl(u);
- } else
+ data = data.opBinary!(op)(u);
+ }
+ else
{
- data = data.opShr(u);
+ data = data.opBinary!(op)(u);
if (data.isZero())
sign = false;
}
@@ -289,7 +325,23 @@ public:
sign = (y & 1) ? sign : false;
data = BigUint.pow(data, u);
}
- else static if (op=="|" || op=="&" || op=="^")
+ else static if (op=="&")
+ {
+ if (y >= 0 && (y <= 1 || !sign)) // In these cases we can avoid some allocation.
+ {
+ static if (T.sizeof <= uint.sizeof && BigDigit.sizeof <= uint.sizeof)
+ data = cast(ulong) data.peekUint(0) & y;
+ else
+ data = data.peekUlong(0) & y;
+ sign = false;
+ }
+ else
+ {
+ BigInt b = y;
+ opOpAssign!op(b);
+ }
+ }
+ else static if (op=="|" || op=="^")
{
BigInt b = y;
opOpAssign!op(b);
@@ -299,9 +351,8 @@ public:
}
///
- @system unittest
+ @safe unittest
{
- //@system because opOpAssign is @system
auto b = BigInt("1_000_000_000");
b += 12345;
@@ -311,10 +362,59 @@ public:
assert(b == BigInt("200_002_469"));
}
+ // https://issues.dlang.org/show_bug.cgi?id=16264
+ @safe unittest
+ {
+ auto a = BigInt(
+ `335690982744637013564796917901053301979460129353374296317539383938630086938` ~
+ `465898213033510992292836631752875403891802201862860531801760096359705447768` ~
+ `957432600293361240407059207520920532482429912948952142341440301429494694368` ~
+ `264560802292927144211230021750155988283029753927847924288850436812178022006` ~
+ `408597793414273953252832688620479083497367463977081627995406363446761896298` ~
+ `967177607401918269561385622811274398143647535024987050366350585544531063531` ~
+ `7118554808325723941557169427279911052268935775`);
+
+ auto b = BigInt(
+ `207672245542926038535480439528441949928508406405023044025560363701392340829` ~
+ `852529131306106648201340460604257466180580583656068555417076345439694125326` ~
+ `843947164365500055567495554645796102453565953360564114634705366335703491527` ~
+ `429426780005741168078089657359833601261803592920462081364401456331489106355` ~
+ `199133982282631108670436696758342051198891939367812305559960349479160308314` ~
+ `068518200681530999860641597181672463704794566473241690395901768680673716414` ~
+ `243691584391572899147223065906633310537507956952626106509069491302359792769` ~
+ `378934570685117202046921464019396759638376362935855896435623442486036961070` ~
+ `534574698959398017332214518246531363445309522357827985468581166065335726996` ~
+ `711467464306784543112544076165391268106101754253962102479935962248302404638` ~
+ `21737237102628470475027851189594709504`);
+
+ BigInt c = a * b; // Crashes
+
+ assert(c == BigInt(
+ `697137001950904057507249234183127244116872349433141878383548259425589716813` ~
+ `135440660252012378417669596912108637127036044977634382385990472429604619344` ~
+ `738746224291111527200379708978133071390303850450970292020176369525401803474` ~
+ `998613408923490273129022167907826017408385746675184651576154302536663744109` ~
+ `111018961065316024005076097634601030334948684412785487182572502394847587887` ~
+ `507385831062796361152176364659197432600147716058873232435238712648552844428` ~
+ `058885217631715287816333209463171932255049134340904981280717725999710525214` ~
+ `161541960645335744430049558161514565159449390036287489478108344584188898872` ~
+ `434914159748515512161981956372737022393466624249130107254611846175580584736` ~
+ `276213025837422102290580044755202968610542057651282410252208599309841499843` ~
+ `672251048622223867183370008181364966502137725166782667358559333222947265344` ~
+ `524195551978394625568228658697170315141077913403482061673401937141405425042` ~
+ `283546509102861986303306729882186190883772633960389974665467972016939172303` ~
+ `653623175801495207204880400522581834672918935651426160175413277309985678579` ~
+ `830872397214091472424064274864210953551447463312267310436493480881235642109` ~
+ `668498742629676513172286703948381906930297135997498416573231570483993847269` ~
+ `479552708416124555462530834668011570929850407031109157206202741051573633443` ~
+ `58105600`
+ ));
+ }
+
/**
- * Implements assignment operators of the form $(D BigInt op= BigInt).
+ * Implements assignment operators of the form `BigInt op= BigInt`.
*/
- BigInt opOpAssign(string op, T)(T y) pure nothrow
+ BigInt opOpAssign(string op, T)(T y) pure nothrow @safe scope return
if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%")
&& is (T: BigInt))
{
@@ -361,9 +461,8 @@ public:
}
///
- @system unittest
+ @safe unittest
{
- // @system because opOpAssign is @system
auto x = BigInt("123");
auto y = BigInt("321");
x += y;
@@ -371,9 +470,9 @@ public:
}
/**
- * Implements binary operators between BigInts.
+ * Implements binary operators between `BigInt`s.
*/
- BigInt opBinary(string op, T)(T y) pure nothrow const
+ BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope
if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" ||
op=="/" || op=="%")
&& is (T: BigInt))
@@ -383,7 +482,7 @@ public:
}
///
- @system unittest
+ @safe unittest
{
auto x = BigInt("123");
auto y = BigInt("456");
@@ -392,19 +491,20 @@ public:
}
/**
- * Implements binary operators between BigInt's and built-in integers.
+ * Implements binary operators between `BigInt`'s and built-in integers.
*/
- BigInt opBinary(string op, T)(T y) pure nothrow const
+ BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope
if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" ||
op=="^"|| op==">>" || op=="<<" || op=="^^")
&& isIntegral!T)
{
BigInt r = this;
- return r.opOpAssign!(op)(y);
+ r.opOpAssign!(op)(y);
+ return r;
}
///
- @system unittest
+ @safe unittest
{
auto x = BigInt("123");
x *= 300;
@@ -418,24 +518,26 @@ public:
where applicable, according to the following table.
$(TABLE ,
+ $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `uint`) $(TD $(RARR)) $(TD `long`))
$(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `long`) $(TD $(RARR)) $(TD `long`))
$(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `ulong`) $(TD $(RARR)) $(TD `BigInt`))
$(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD other type) $(TD $(RARR)) $(TD `int`))
)
*/
- auto opBinary(string op, T)(T y) pure nothrow const
+ auto opBinary(string op, T)(T y) pure nothrow @safe const
if (op == "%" && isIntegral!T)
{
- assert(y != 0);
+ assert(y != 0, "% 0 not allowed");
+ // BigInt % uint => long
// BigInt % long => long
// BigInt % ulong => BigInt
// BigInt % other_type => int
- static if (is(Unqual!T == long) || is(Unqual!T == ulong))
+ static if (is(immutable T == immutable long) || is(immutable T == immutable ulong))
{
auto r = this % BigInt(y);
- static if (is(Unqual!T == long))
+ static if (is(immutable T == immutable long))
{
return r.toLong();
}
@@ -448,7 +550,11 @@ public:
else
{
immutable uint u = absUnsign(y);
- int rem = BigUint.modInt(data, u);
+ static if (is(immutable T == immutable uint))
+ alias R = long;
+ else
+ alias R = int;
+ R rem = BigUint.modInt(data, u);
// x%y always has the same sign as x.
// This is not the same as mathematical mod.
return sign ? -rem : rem;
@@ -456,7 +562,7 @@ public:
}
///
- @system unittest
+ @safe unittest
{
auto x = BigInt("1_000_000_500");
long l = 1_000_000L;
@@ -472,16 +578,16 @@ public:
/**
Implements operators with built-in integers on the left-hand side and
- BigInt on the right-hand side.
+ `BigInt` on the right-hand side.
*/
- BigInt opBinaryRight(string op, T)(T y) pure nothrow const
+ BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const
if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T)
{
return opBinary!(op)(y);
}
///
- @system unittest
+ @safe unittest
{
auto x = BigInt("100");
BigInt y = 123 + x;
@@ -499,7 +605,7 @@ public:
// BigInt = integer op BigInt
/// ditto
- BigInt opBinaryRight(string op, T)(T y) pure nothrow const
+ BigInt opBinaryRight(string op, T)(T y) pure nothrow @safe const
if (op == "-" && isIntegral!T)
{
ulong u = absUnsign(y);
@@ -515,7 +621,7 @@ public:
// integer = integer op BigInt
/// ditto
- T opBinaryRight(string op, T)(T x) pure nothrow const
+ T opBinaryRight(string op, T)(T x) pure nothrow @safe const
if ((op=="%" || op=="/") && isIntegral!T)
{
checkDivByZero();
@@ -540,9 +646,9 @@ public:
// const unary operations
/**
- Implements BigInt unary operators.
+ Implements `BigInt` unary operators.
*/
- BigInt opUnary(string op)() pure nothrow const if (op=="+" || op=="-" || op=="~")
+ BigInt opUnary(string op)() pure nothrow @safe const if (op=="+" || op=="-" || op=="~")
{
static if (op=="-")
{
@@ -560,7 +666,7 @@ public:
// non-const unary operations
/// ditto
- BigInt opUnary(string op)() pure nothrow if (op=="++" || op=="--")
+ BigInt opUnary(string op)() pure nothrow @safe if (op=="++" || op=="--")
{
static if (op=="++")
{
@@ -575,7 +681,7 @@ public:
}
///
- @system unittest
+ @safe unittest
{
auto x = BigInt("1234");
assert(-x == BigInt("-1234"));
@@ -585,24 +691,38 @@ public:
}
/**
- Implements BigInt equality test with other BigInt's and built-in
- integer types.
+ Implements `BigInt` equality test with other `BigInt`'s and built-in
+ numeric types.
*/
- bool opEquals()(auto ref const BigInt y) const pure @nogc
+ bool opEquals()(auto ref const BigInt y) const pure @nogc @safe
{
return sign == y.sign && y.data == data;
}
/// ditto
- bool opEquals(T)(T y) const pure nothrow @nogc if (isIntegral!T)
+ bool opEquals(T)(const T y) const pure nothrow @nogc @safe if (isIntegral!T)
{
if (sign != (y<0))
return 0;
return data.opEquals(cast(ulong) absUnsign(y));
}
+ /// ditto
+ bool opEquals(T)(const T y) const pure nothrow @nogc if (isFloatingPoint!T)
+ {
+ return 0 == opCmp(y);
+ }
+
///
- @system unittest
+ @safe unittest
+ {
+ // Note that when comparing a BigInt to a float or double the
+ // full precision of the BigInt is always considered, unlike
+ // when comparing an int to a float or a long to a double.
+ assert(BigInt(123456789) != cast(float) 123456789);
+ }
+
+ @safe unittest
{
auto x = BigInt("12345");
auto y = BigInt("12340");
@@ -616,16 +736,49 @@ public:
assert(x != w);
}
+ @safe unittest
+ {
+ import std.math.operations : nextDown, nextUp;
+
+ const x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
+ BigInt x1 = x + 1;
+ BigInt x2 = x - 1;
+
+ const d = 0x1.abcde8p124;
+ assert(x == d);
+ assert(x1 != d);
+ assert(x2 != d);
+ assert(x != nextUp(d));
+ assert(x != nextDown(d));
+ assert(x != double.nan);
+
+ const dL = 0x1.abcde8p124L;
+ assert(x == dL);
+ assert(x1 != dL);
+ assert(x2 != dL);
+ assert(x != nextUp(dL));
+ assert(x != nextDown(dL));
+ assert(x != real.nan);
+
+ assert(BigInt(0) == 0.0f);
+ assert(BigInt(0) == 0.0);
+ assert(BigInt(0) == 0.0L);
+ assert(BigInt(0) == -0.0f);
+ assert(BigInt(0) == -0.0);
+ assert(BigInt(0) == -0.0L);
+ assert(BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") != float.infinity);
+ }
+
/**
- Implements casting to bool.
+ Implements casting to `bool`.
*/
- T opCast(T:bool)() pure nothrow @nogc const
+ T opCast(T:bool)() pure nothrow @nogc @safe const
{
return !isZero();
}
///
- @system unittest
+ @safe unittest
{
// Non-zero values are regarded as true
auto x = BigInt("1");
@@ -644,7 +797,7 @@ public:
Throws: $(REF ConvOverflowException, std,conv) if the number exceeds
the target type's range.
*/
- T opCast(T:ulong)() /*pure*/ const
+ T opCast(T:ulong)() pure @safe const
{
if (isUnsigned!T && sign)
{ /* throw */ }
@@ -667,12 +820,12 @@ public:
import std.conv : ConvOverflowException;
import std.string : format;
throw new ConvOverflowException(
- "BigInt(%d) cannot be represented as a %s"
- .format(this, T.stringof));
+ "BigInt(%s) cannot be represented as a %s"
+ .format(this.toDecimalString, T.stringof));
}
///
- @system unittest
+ @safe unittest
{
import std.conv : to, ConvOverflowException;
import std.exception : assertThrown;
@@ -685,7 +838,7 @@ public:
assertThrown!ConvOverflowException(BigInt("-1").to!ubyte);
}
- @system unittest
+ @safe unittest
{
import std.conv : to, ConvOverflowException;
import std.exception : assertThrown;
@@ -720,19 +873,182 @@ public:
}
/**
- Implements casting to/from qualified BigInt's.
+ Implements casting to floating point types.
+ */
+ T opCast(T)() @safe nothrow @nogc const if (isFloatingPoint!T)
+ {
+ return toFloat!(T, "nearest");
+ }
+
+ ///
+ @system unittest
+ {
+ assert(cast(float) BigInt("35540592535949172786332045140593475584")
+ == 35540592535949172786332045140593475584.0f);
+ assert(cast(double) BigInt("35540601499647381470685035515422441472")
+ == 35540601499647381470685035515422441472.0);
+ assert(cast(real) BigInt("35540601499647381470685035515422441472")
+ == 35540601499647381470685035515422441472.0L);
+
+ assert(cast(float) BigInt("-0x1345_6780_0000_0000_0000_0000_0000") == -0x1.3456_78p+108f );
+ assert(cast(double) BigInt("-0x1345_678a_bcde_f000_0000_0000_0000") == -0x1.3456_78ab_cdefp+108 );
+ assert(cast(real) BigInt("-0x1345_678a_bcde_f000_0000_0000_0000") == -0x1.3456_78ab_cdefp+108L);
+ }
+
+ /// Rounding when casting to floating point
+ @system unittest
+ {
+ // BigInts whose values cannot be exactly represented as float/double/real
+ // are rounded when cast to float/double/real. When cast to float or
+ // double or 64-bit real the rounding is strictly defined. When cast
+ // to extended-precision real the rounding rules vary by environment.
+
+ // BigInts that fall somewhere between two non-infinite floats/doubles
+ // are rounded to the closer value when cast to float/double.
+ assert(cast(float) BigInt(0x1aaa_aae7) == 0x1.aaa_aaep+28f);
+ assert(cast(float) BigInt(0x1aaa_aaff) == 0x1.aaa_ab0p+28f);
+ assert(cast(float) BigInt(-0x1aaa_aae7) == -0x1.aaaaaep+28f);
+ assert(cast(float) BigInt(-0x1aaa_aaff) == -0x1.aaaab0p+28f);
+
+ assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aa77) == 0x1.aaa_aaaa_aaaa_aa00p+60);
+ assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aaff) == 0x1.aaa_aaaa_aaaa_ab00p+60);
+ assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aa77) == -0x1.aaa_aaaa_aaaa_aa00p+60);
+ assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aaff) == -0x1.aaa_aaaa_aaaa_ab00p+60);
+
+ // BigInts that fall exactly between two non-infinite floats/doubles
+ // are rounded away from zero when cast to float/double. (Note that
+ // in most environments this is NOT the same rounding rule rule used
+ // when casting int/long to float/double.)
+ assert(cast(float) BigInt(0x1aaa_aaf0) == 0x1.aaa_ab0p+28f);
+ assert(cast(float) BigInt(-0x1aaa_aaf0) == -0x1.aaaab0p+28f);
+
+ assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aa80) == 0x1.aaa_aaaa_aaaa_ab00p+60);
+ assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aa80) == -0x1.aaa_aaaa_aaaa_ab00p+60);
+
+ // BigInts that are bounded on one side by the largest positive or
+ // most negative finite float/double and on the other side by infinity
+ // or -infinity are rounded as if in place of infinity was the value
+ // `2^^(T.max_exp)` when cast to float/double.
+ assert(cast(float) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") == float.infinity);
+ assert(cast(float) BigInt("-999_999_999_999_999_999_999_999_999_999_999_999_999") == -float.infinity);
+
+ assert(cast(double) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < double.infinity);
+ assert(cast(real) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < real.infinity);
+ }
+
+ @safe unittest
+ {
+ // Test exponent overflow is correct.
+ assert(cast(float) BigInt(0x1fffffff) == 0x1.000000p+29f);
+ assert(cast(double) BigInt(0x1fff_ffff_ffff_fff0) == 0x1.000000p+61);
+ }
+
+ private T toFloat(T, string roundingMode)() @safe nothrow @nogc const
+ if (__traits(isFloating, T) && (roundingMode == "nearest" || roundingMode == "truncate"))
+ {
+ import core.bitop : bsr;
+ enum performRounding = (roundingMode == "nearest");
+ enum performTruncation = (roundingMode == "truncate");
+ static assert(performRounding || performTruncation, "unrecognized rounding mode");
+ enum int totalNeededBits = T.mant_dig + int(performRounding);
+ static if (totalNeededBits <= 64)
+ {
+ // We need to examine the top two 64-bit words, not just the top one,
+ // since the top word could have just a single significant bit.
+ const ulongLength = data.ulongLength;
+ const ulong w1 = data.peekUlong(ulongLength - 1);
+ if (w1 == 0)
+ return T(0); // Special: exponent should be all zero bits, plus bsr(w1) is undefined.
+ const ulong w2 = ulongLength < 2 ? 0 : data.peekUlong(ulongLength - 2);
+ const uint w1BitCount = bsr(w1) + 1;
+ ulong sansExponent = (w1 << (64 - w1BitCount)) | (w2 >>> (w1BitCount));
+ size_t exponent = (ulongLength - 1) * 64 + w1BitCount + 1;
+ static if (performRounding)
+ {
+ sansExponent += 1UL << (64 - totalNeededBits);
+ if (0 <= cast(long) sansExponent) // Use high bit to detect overflow.
+ {
+ // Do not bother filling in the high bit of sansExponent
+ // with 1. It will be discarded by float and double and 80
+ // bit real cannot be on this path with rounding enabled.
+ exponent += 1;
+ }
+ }
+ static if (T.mant_dig == float.mant_dig)
+ {
+ if (exponent >= T.max_exp)
+ return isNegative ? -T.infinity : T.infinity;
+ uint resultBits = (uint(isNegative) << 31) | // sign bit
+ ((0xFF & (exponent - float.min_exp)) << 23) | // exponent
+ cast(uint) ((sansExponent << 1) >>> (64 - 23)); // mantissa.
+ // TODO: remove @trusted lambda after DIP 1000 is enabled by default.
+ return (() @trusted => *cast(float*) &resultBits)();
+ }
+ else static if (T.mant_dig == double.mant_dig)
+ {
+ if (exponent >= T.max_exp)
+ return isNegative ? -T.infinity : T.infinity;
+ ulong resultBits = (ulong(isNegative) << 63) | // sign bit
+ ((0x7FFUL & (exponent - double.min_exp)) << 52) | // exponent
+ ((sansExponent << 1) >>> (64 - 52)); // mantissa.
+ // TODO: remove @trusted lambda after DIP 1000 is enabled by default.
+ return (() @trusted => *cast(double*) &resultBits)();
+ }
+ else
+ {
+ import core.math : ldexp;
+ return ldexp(isNegative ? -cast(real) sansExponent : cast(real) sansExponent,
+ cast(int) exponent - 65);
+ }
+ }
+ else
+ {
+ import core.math : ldexp;
+ const ulongLength = data.ulongLength;
+ if ((ulongLength - 1) * 64L > int.max)
+ return isNegative ? -T.infinity : T.infinity;
+ int scale = cast(int) ((ulongLength - 1) * 64);
+ const ulong w1 = data.peekUlong(ulongLength - 1);
+ if (w1 == 0)
+ return T(0); // Special: bsr(w1) is undefined.
+ int bitsStillNeeded = totalNeededBits - bsr(w1) - 1;
+ T acc = ldexp(cast(T) w1, scale);
+ for (ptrdiff_t i = ulongLength - 2; i >= 0 && bitsStillNeeded > 0; i--)
+ {
+ ulong w = data.peekUlong(i);
+ // To round towards zero we must make sure not to use too many bits.
+ if (bitsStillNeeded >= 64)
+ {
+ acc += ldexp(cast(T) w, scale -= 64);
+ bitsStillNeeded -= 64;
+ }
+ else
+ {
+ w = (w >>> (64 - bitsStillNeeded)) << (64 - bitsStillNeeded);
+ acc += ldexp(cast(T) w, scale -= 64);
+ break;
+ }
+ }
+ if (isNegative)
+ acc = -acc;
+ return cast(T) acc;
+ }
+ }
+
+ /**
+ Implements casting to/from qualified `BigInt`'s.
- Warning: Casting to/from $(D const) or $(D immutable) may break type
+ Warning: Casting to/from `const` or `immutable` may break type
system guarantees. Use with care.
*/
T opCast(T)() pure nothrow @nogc const
- if (is(Unqual!T == BigInt))
+ if (is(immutable T == immutable BigInt))
{
return this;
}
///
- @system unittest
+ @safe unittest
{
const(BigInt) x = BigInt("123");
BigInt y = cast() x; // cast away const
@@ -743,17 +1059,17 @@ public:
// Note that this must appear before the other opCmp overloads, otherwise
// DMD won't find it.
/**
- Implements 3-way comparisons of BigInt with BigInt or BigInt with
- built-in integers.
+ Implements 3-way comparisons of `BigInt` with `BigInt` or `BigInt` with
+ built-in numeric types.
*/
- int opCmp(ref const BigInt y) pure nothrow @nogc const
+ int opCmp(ref const BigInt y) pure nothrow @nogc @safe const
{
// Simply redirect to the "real" opCmp implementation.
return this.opCmp!BigInt(y);
}
/// ditto
- int opCmp(T)(T y) pure nothrow @nogc const if (isIntegral!T)
+ int opCmp(T)(const T y) pure nothrow @nogc @safe const if (isIntegral!T)
{
if (sign != (y<0) )
return sign ? -1 : 1;
@@ -761,7 +1077,38 @@ public:
return sign? -cmp: cmp;
}
/// ditto
- int opCmp(T:BigInt)(const T y) pure nothrow @nogc const
+ int opCmp(T)(const T y) nothrow @nogc @safe const if (isFloatingPoint!T)
+ {
+ import core.bitop : bsr;
+ import std.math.operations : cmp;
+ import std.math.traits : isFinite;
+
+ const asFloat = toFloat!(T, "truncate");
+ if (asFloat != y)
+ return cmp(asFloat, y); // handles +/- NaN.
+ if (!isFinite(y))
+ return isNegative ? 1 : -1;
+ const ulongLength = data.ulongLength;
+ const w1 = data.peekUlong(ulongLength - 1);
+ if (w1 == 0)
+ return 0; // Special: bsr(w1) is undefined.
+ const numSignificantBits = (ulongLength - 1) * 64 + bsr(w1) + 1;
+ for (ptrdiff_t bitsRemainingToCheck = numSignificantBits - T.mant_dig, i = 0;
+ bitsRemainingToCheck > 0; i++, bitsRemainingToCheck -= 64)
+ {
+ auto word = data.peekUlong(i);
+ if (word == 0)
+ continue;
+ // Make sure we're only checking digits that are beyond
+ // the precision of `y`.
+ if (bitsRemainingToCheck < 64 && (word << (64 - bitsRemainingToCheck)) == 0)
+ break; // This can only happen on the last loop iteration.
+ return isNegative ? -1 : 1;
+ }
+ return 0;
+ }
+ /// ditto
+ int opCmp(T:BigInt)(const T y) pure nothrow @nogc @safe const
{
if (sign != y.sign)
return sign ? -1 : 1;
@@ -770,7 +1117,7 @@ public:
}
///
- @system unittest
+ @safe unittest
{
auto x = BigInt("100");
auto y = BigInt("10");
@@ -783,9 +1130,60 @@ public:
assert(x < w);
}
+ ///
+ @safe unittest
+ {
+ auto x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
+ BigInt y = x - 1;
+ BigInt z = x + 1;
+
+ double d = 0x1.abcde8p124;
+ assert(y < d);
+ assert(z > d);
+ assert(x >= d && x <= d);
+
+ // Note that when comparing a BigInt to a float or double the
+ // full precision of the BigInt is always considered, unlike
+ // when comparing an int to a float or a long to a double.
+ assert(BigInt(123456789) < cast(float) 123456789);
+ }
+
+ @safe unittest
+ {
+ assert(BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < float.infinity);
+
+ // Test `real` works.
+ auto x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
+ BigInt y = x - 1;
+ BigInt z = x + 1;
+
+ real d = 0x1.abcde8p124;
+ assert(y < d);
+ assert(z > d);
+ assert(x >= d && x <= d);
+
+ // Test comparison for numbers of 64 bits or fewer.
+ auto w1 = BigInt(0x1abc_de80_0000_0000);
+ auto w2 = w1 - 1;
+ auto w3 = w1 + 1;
+ assert(w1.ulongLength == 1);
+ assert(w2.ulongLength == 1);
+ assert(w3.ulongLength == 1);
+
+ double e = 0x1.abcde8p+60;
+ assert(w1 >= e && w1 <= e);
+ assert(w2 < e);
+ assert(w3 > e);
+
+ real eL = 0x1.abcde8p+60;
+ assert(w1 >= eL && w1 <= eL);
+ assert(w2 < eL);
+ assert(w3 > eL);
+ }
+
/**
- Returns: The value of this BigInt as a long, or +/- long.max if outside
- the representable range.
+ Returns: The value of this `BigInt` as a `long`, or `long.max`/`long.min`
+ if outside the representable range.
*/
long toLong() @safe pure nothrow const @nogc
{
@@ -796,7 +1194,7 @@ public:
}
///
- @system unittest
+ @safe unittest
{
auto b = BigInt("12345");
long l = b.toLong();
@@ -804,7 +1202,7 @@ public:
}
/**
- Returns: The value of this BigInt as an int, or +/- int.max if outside
+ Returns: The value of this `BigInt` as an `int`, or `int.max`/`int.min` if outside
the representable range.
*/
int toInt() @safe pure nothrow @nogc const
@@ -816,7 +1214,7 @@ public:
}
///
- @system unittest
+ @safe unittest
{
auto big = BigInt("5_000_000");
auto i = big.toInt();
@@ -828,24 +1226,24 @@ public:
assert(i == int.max);
}
- /// Number of significant uints which are used in storing this number.
- /// The absolute value of this BigInt is always &lt; 2$(SUPERSCRIPT 32*uintLength)
+ /// Number of significant `uint`s which are used in storing this number.
+ /// The absolute value of this `BigInt` is always &lt; 2$(SUPERSCRIPT 32*uintLength)
@property size_t uintLength() @safe pure nothrow @nogc const
{
return data.uintLength;
}
- /// Number of significant ulongs which are used in storing this number.
- /// The absolute value of this BigInt is always &lt; 2$(SUPERSCRIPT 64*ulongLength)
+ /// Number of significant `ulong`s which are used in storing this number.
+ /// The absolute value of this `BigInt` is always &lt; 2$(SUPERSCRIPT 64*ulongLength)
@property size_t ulongLength() @safe pure nothrow @nogc const
{
return data.ulongLength;
}
- /** Convert the BigInt to string, passing it to the given sink.
+ /** Convert the `BigInt` to `string`, passing it to the given sink.
*
* Params:
- * sink = A delegate for accepting possibly piecewise segments of the
+ * sink = An OutputRange for accepting possibly piecewise segments of the
* formatted string.
* formatString = A format string specifying the output format.
*
@@ -858,30 +1256,32 @@ public:
* $(TR $(TD null) $(TD Default formatting (same as "d") ))
* )
*/
- void toString(scope void delegate(const (char)[]) sink, string formatString) const
+ void toString(Writer)(scope ref Writer sink, string formatString) const
{
auto f = FormatSpec!char(formatString);
f.writeUpToNextSpec(sink);
- toString(sink, f);
+ toString!Writer(sink, f);
}
/// ditto
- void toString(scope void delegate(const(char)[]) sink, ref FormatSpec!char f) const
+ void toString(Writer)(scope ref Writer sink, scope const ref FormatSpec!char f) const
{
- immutable hex = (f.spec == 'x' || f.spec == 'X');
- if (!(f.spec == 's' || f.spec == 'd' || f.spec =='o' || hex))
- throw new FormatException("Format specifier not understood: %" ~ f.spec);
+ import std.range.primitives : put;
+ const spec = f.spec;
+ immutable hex = (spec == 'x' || spec == 'X');
+ if (!(spec == 's' || spec == 'd' || spec =='o' || hex))
+ throw new FormatException("Format specifier not understood: %" ~ spec);
char[] buff;
- if (f.spec == 'X')
+ if (spec == 'X')
{
buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.upper);
}
- else if (f.spec == 'x')
+ else if (spec == 'x')
{
buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.lower);
}
- else if (f.spec == 'o')
+ else if (spec == 'o')
{
buff = data.toOctalString();
}
@@ -889,9 +1289,9 @@ public:
{
buff = data.toDecimalString(0);
}
- assert(buff.length > 0);
+ assert(buff.length > 0, "Invalid buffer length");
- char signChar = isNegative() ? '-' : 0;
+ char signChar = isNegative ? '-' : 0;
auto minw = buff.length + (signChar ? 1 : 0);
if (!hex && !signChar && (f.width == 0 || minw < f.width))
@@ -913,27 +1313,30 @@ public:
if (!f.flDash && !f.flZero)
foreach (i; 0 .. difw)
- sink(" ");
+ put(sink, " ");
if (signChar)
- sink((&signChar)[0 .. 1]);
+ {
+ scope char[1] buf = signChar;
+ put(sink, buf[]);
+ }
if (!f.flDash && f.flZero)
foreach (i; 0 .. difw)
- sink("0");
+ put(sink, "0");
- sink(buff);
+ put(sink, buff);
if (f.flDash)
foreach (i; 0 .. difw)
- sink(" ");
+ put(sink, " ");
}
/**
- $(D toString) is rarely directly invoked; the usual way of using it is via
+ `toString` is rarely directly invoked; the usual way of using it is via
$(REF format, std, format):
*/
- @system unittest
+ @safe unittest
{
import std.format : format;
@@ -946,21 +1349,55 @@ public:
assert(format("%o", x) == "133764340100");
}
+ // for backwards compatibility, see unittest below
+ /// ditto
+ void toString(scope void delegate(scope const(char)[]) sink, string formatString) const
+ {
+ toString!(void delegate(scope const(char)[]))(sink, formatString);
+ }
+
+ // for backwards compatibility, see unittest below
+ /// ditto
+ void toString(scope void delegate(scope const(char)[]) sink, scope const ref FormatSpec!char f) const
+ {
+ toString!(void delegate(scope const(char)[]))(sink, f);
+ }
+
+ // Backwards compatibility test
+ // BigInt.toString used to only accept a delegate sink function, but this does not work
+ // well with attributes such as @safe. A template function toString was added that
+ // works on OutputRanges, but when a delegate was passed in the form of an untyped
+ // lambda such as `str => dst.put(str)` the parameter type was inferred as `void` and
+ // the function failed to instantiate.
+ @system unittest
+ {
+ import std.format.spec : FormatSpec;
+ import std.array : appender;
+ BigInt num = 503;
+ auto dst = appender!string();
+ num.toString(str => dst.put(str), null);
+ assert(dst[] == "503");
+ num = 504;
+ auto f = FormatSpec!char("");
+ num.toString(str => dst.put(str), f);
+ assert(dst[] == "503504");
+ }
+
// Implement toHash so that BigInt works properly as an AA key.
/**
- Returns: A unique hash of the BigInt's value suitable for use in a hash
+ Returns: A unique hash of the `BigInt`'s value suitable for use in a hash
table.
*/
- size_t toHash() const @safe nothrow
+ size_t toHash() const @safe pure nothrow @nogc
{
return data.toHash() + sign;
}
/**
- $(D toHash) is rarely directly invoked; it is implicitly used when
+ `toHash` is rarely directly invoked; it is implicitly used when
BigInt is used as the key of an associative array.
*/
- @safe unittest
+ @safe pure unittest
{
string[BigInt] aa;
aa[BigInt(123)] = "abc";
@@ -970,31 +1407,74 @@ public:
assert(aa[BigInt(456)] == "def");
}
+ /**
+ * Gets the nth number in the underlying representation that makes up the whole
+ * `BigInt`.
+ *
+ * Params:
+ * T = the type to view the underlying representation as
+ * n = The nth number to retrieve. Must be less than $(LREF ulongLength) or
+ * $(LREF uintLength) with respect to `T`.
+ * Returns:
+ * The nth `ulong` in the representation of this `BigInt`.
+ */
+ T getDigit(T = ulong)(size_t n) const
+ if (is(T == ulong) || is(T == uint))
+ {
+ static if (is(T == ulong))
+ {
+ assert(n < ulongLength(), "getDigit index out of bounds");
+ return data.peekUlong(n);
+ }
+ else
+ {
+ assert(n < uintLength(), "getDigit index out of bounds");
+ return data.peekUint(n);
+ }
+ }
+
+ ///
+ @safe pure unittest
+ {
+ auto a = BigInt("1000");
+ assert(a.ulongLength() == 1);
+ assert(a.getDigit(0) == 1000);
+
+ assert(a.uintLength() == 1);
+ assert(a.getDigit!uint(0) == 1000);
+
+ auto b = BigInt("2_000_000_000_000_000_000_000_000_000");
+ assert(b.ulongLength() == 2);
+ assert(b.getDigit(0) == 4584946418820579328);
+ assert(b.getDigit(1) == 108420217);
+
+ assert(b.uintLength() == 3);
+ assert(b.getDigit!uint(0) == 3489660928);
+ assert(b.getDigit!uint(1) == 1067516025);
+ assert(b.getDigit!uint(2) == 108420217);
+ }
+
private:
- void negate() @safe pure nothrow @nogc
+ void negate() @safe pure nothrow @nogc scope
{
if (!data.isZero())
sign = !sign;
}
- bool isZero() pure const nothrow @nogc @safe
+ bool isZero() pure const nothrow @nogc @safe scope
{
return data.isZero();
}
- bool isNegative() pure const nothrow @nogc @safe
- {
- return sign;
- }
+ alias isNegative = sign;
// Generate a runtime error if division by zero occurs
- void checkDivByZero() pure const nothrow @safe
+ void checkDivByZero() pure const nothrow @safe scope
{
- if (isZero())
- throw new Error("BigInt division by zero");
+ assert(!isZero(), "BigInt division by zero");
}
}
///
-@system unittest
+@safe unittest
{
BigInt a = "9588669891916142";
BigInt b = "7452469135154800";
@@ -1020,27 +1500,29 @@ private:
assert(h == a);
assert(i == e);
BigInt j = "-0x9A56_57f4_7B83_AB78";
+ BigInt k = j;
j ^^= 11;
+ assert(k ^^ 11 == j);
}
/**
Params:
- x = The $(D BigInt) to convert to a decimal $(D string).
+ x = The `BigInt` to convert to a decimal `string`.
Returns:
- A $(D string) that represents the $(D BigInt) as a decimal number.
+ A `string` that represents the `BigInt` as a decimal number.
*/
-string toDecimalString(const(BigInt) x)
+string toDecimalString(const(BigInt) x) pure nothrow @safe
{
- string outbuff="";
- void sink(const(char)[] s) { outbuff ~= s; }
- x.toString(&sink, "%d");
- return outbuff;
+ auto buff = x.data.toDecimalString(x.isNegative ? 1 : 0);
+ if (x.isNegative)
+ buff[0] = '-';
+ return buff;
}
///
-@system unittest
+@safe pure unittest
{
auto x = BigInt("123");
x *= 1000;
@@ -1052,23 +1534,23 @@ string toDecimalString(const(BigInt) x)
/**
Params:
- x = The $(D BigInt) to convert to a hexadecimal $(D string).
+ x = The `BigInt` to convert to a hexadecimal `string`.
Returns:
- A $(D string) that represents the $(D BigInt) as a hexadecimal (base 16)
+ A `string` that represents the `BigInt` as a hexadecimal (base 16)
number in upper case.
*/
-string toHex(const(BigInt) x)
+string toHex(const(BigInt) x) @safe
{
- string outbuff="";
- void sink(const(char)[] s) { outbuff ~= s; }
- x.toString(&sink, "%X");
- return outbuff;
+ import std.array : appender;
+ auto outbuff = appender!string();
+ x.toString(outbuff, "%X");
+ return outbuff[];
}
///
-@system unittest
+@safe unittest
{
auto x = BigInt("123");
x *= 1000;
@@ -1107,32 +1589,35 @@ if (isIntegral!T)
}
///
-nothrow pure @system
+nothrow pure @safe
unittest
{
assert((-1).absUnsign == 1);
assert(1.absUnsign == 1);
}
-nothrow pure @system
+nothrow pure @safe
unittest
{
BigInt a, b;
a = 1;
b = 2;
auto c = a + b;
+ assert(c == 3);
}
-nothrow pure @system
+nothrow pure @safe
unittest
{
long a;
BigInt b;
auto c = a + b;
+ assert(c == 0);
auto d = b + a;
+ assert(d == 0);
}
-nothrow pure @system
+nothrow pure @safe
unittest
{
BigInt x = 1, y = 2;
@@ -1157,7 +1642,7 @@ unittest
assert(incr == BigInt(1));
}
-@system unittest
+@safe unittest
{
// Radix conversion
assert( toDecimalString(BigInt("-1_234_567_890_123_456_789"))
@@ -1174,7 +1659,7 @@ unittest
assert(BigInt(-0x1234_5678_9ABC_5A5AL).toLong() == -0x1234_5678_9ABC_5A5AL);
assert(BigInt(0xF234_5678_9ABC_5A5AL).toLong() == long.max);
assert(BigInt(-0x123456789ABCL).toInt() == -int.max);
- char[] s1 = "123".dup; // bug 8164
+ char[] s1 = "123".dup; // https://issues.dlang.org/show_bug.cgi?id=8164
assert(BigInt(s1) == 123);
char[] s2 = "0xABC".dup;
assert(BigInt(s2) == 2748);
@@ -1186,16 +1671,16 @@ unittest
b = long.max / a;
assert( b == long.max /(ulong.max - 5));
assert(BigInt(1) - 1 == 0);
- assert((-4) % BigInt(5) == -4); // bug 5928
+ assert((-4) % BigInt(5) == -4); // https://issues.dlang.org/show_bug.cgi?id=5928
assert(BigInt(-4) % BigInt(5) == -4);
- assert(BigInt(2)/BigInt(-3) == BigInt(0)); // bug 8022
- assert(BigInt("-1") > long.min); // bug 9548
+ assert(BigInt(2)/BigInt(-3) == BigInt(0)); // https://issues.dlang.org/show_bug.cgi?id=8022
+ assert(BigInt("-1") > long.min); // https://issues.dlang.org/show_bug.cgi?id=9548
assert(toDecimalString(BigInt("0000000000000000000000000000000000000000001234567"))
== "1234567");
}
-@system unittest // Minimum signed value bug tests.
+@safe unittest // Minimum signed value bug tests.
{
assert(BigInt("-0x8000000000000000") == BigInt(long.min));
assert(BigInt("-0x8000000000000000")+1 > BigInt(long.min));
@@ -1216,7 +1701,8 @@ unittest
assert((BigInt(int.min)-1)%int.min == -1);
}
-@system unittest // Recursive division, bug 5568
+ // Recursive division (https://issues.dlang.org/show_bug.cgi?id=5568)
+@safe unittest
{
enum Z = 4843;
BigInt m = (BigInt(1) << (Z*8) ) - 1;
@@ -1236,17 +1722,17 @@ unittest
BigInt w = c - b + a;
assert(w % m == 0);
- // Bug 6819. ^^
+ // https://issues.dlang.org/show_bug.cgi?id=6819
BigInt z1 = BigInt(10)^^64;
BigInt w1 = BigInt(10)^^128;
assert(z1^^2 == w1);
BigInt z2 = BigInt(1)<<64;
BigInt w2 = BigInt(1)<<128;
assert(z2^^2 == w2);
- // Bug 7993
+ // https://issues.dlang.org/show_bug.cgi?id=7993
BigInt n7793 = 10;
assert( n7793 / 1 == 10);
- // Bug 7973
+ // https://issues.dlang.org/show_bug.cgi?id=7973
auto a7973 = 10_000_000_000_000_000;
const c7973 = 10_000_000_000_000_000;
immutable i7973 = 10_000_000_000_000_000;
@@ -1257,15 +1743,15 @@ unittest
assert(v7973 == 2551700137);
v7973 %= i7973;
assert(v7973 == 2551700137);
- // 8165
+ // https://issues.dlang.org/show_bug.cgi?id=8165
BigInt[2] a8165;
a8165[0] = a8165[1] = 1;
}
-@system unittest
+@safe unittest
{
import std.array;
- import std.format;
+ import std.format.write : formattedWrite;
immutable string[][] table = [
/* fmt, +10 -10 */
@@ -1313,10 +1799,10 @@ unittest
}
}
-@system unittest
+@safe unittest
{
import std.array;
- import std.format;
+ import std.format.write : formattedWrite;
immutable string[][] table = [
/* fmt, +10 -10 */
@@ -1364,10 +1850,10 @@ unittest
}
}
-@system unittest
+@safe unittest
{
import std.array;
- import std.format;
+ import std.format.write : formattedWrite;
immutable string[][] table = [
/* fmt, +10 -10 */
@@ -1415,11 +1901,11 @@ unittest
}
}
-// 6448
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=6448
+@safe unittest
{
import std.array;
- import std.format;
+ import std.format.write : formattedWrite;
auto w1 = appender!string();
auto w2 = appender!string();
@@ -1429,7 +1915,7 @@ unittest
BigInt bx = x;
formattedWrite(w2, "%010d", bx);
assert(w1.data == w2.data);
- //8011
+ // https://issues.dlang.org/show_bug.cgi?id=8011
BigInt y = -3;
++y;
assert(y.toLong() == -2);
@@ -1444,13 +1930,13 @@ unittest
@safe unittest
{
- import std.math : abs;
- auto r = abs(BigInt(-1000)); // 6486
+ import std.math.algebraic : abs;
+ auto r = abs(BigInt(-1000)); // https://issues.dlang.org/show_bug.cgi?id=6486
assert(r == 1000);
- auto r2 = abs(const(BigInt)(-500)); // 11188
+ auto r2 = abs(const(BigInt)(-500)); // https://issues.dlang.org/show_bug.cgi?id=11188
assert(r2 == 500);
- auto r3 = abs(immutable(BigInt)(-733)); // 11188
+ auto r3 = abs(immutable(BigInt)(-733)); // https://issues.dlang.org/show_bug.cgi?id=11188
assert(r3 == 733);
// opCast!bool
@@ -1458,7 +1944,8 @@ unittest
assert(one && !zero);
}
-@system unittest // 6850
+// https://issues.dlang.org/show_bug.cgi?id=6850
+@safe unittest
{
pure long pureTest() {
BigInt a = 1;
@@ -1470,7 +1957,9 @@ unittest
assert(pureTest() == 1337);
}
-@system unittest // 8435 & 10118
+// https://issues.dlang.org/show_bug.cgi?id=8435
+// https://issues.dlang.org/show_bug.cgi?id=10118
+@safe unittest
{
auto i = BigInt(100);
auto j = BigInt(100);
@@ -1494,22 +1983,23 @@ unittest
assert(keys.empty);
}
-@system unittest // 11148
+// https://issues.dlang.org/show_bug.cgi?id=11148
+@safe unittest
{
void foo(BigInt) {}
const BigInt cbi = 3;
immutable BigInt ibi = 3;
- assert(__traits(compiles, foo(cbi)));
- assert(__traits(compiles, foo(ibi)));
+ foo(cbi);
+ foo(ibi);
import std.conv : to;
import std.meta : AliasSeq;
- foreach (T1; AliasSeq!(BigInt, const(BigInt), immutable(BigInt)))
+ static foreach (T1; AliasSeq!(BigInt, const(BigInt), immutable(BigInt)))
{
- foreach (T2; AliasSeq!(BigInt, const(BigInt), immutable(BigInt)))
- {
+ static foreach (T2; AliasSeq!(BigInt, const(BigInt), immutable(BigInt)))
+ {{
T1 t1 = 2;
T2 t2 = t1;
@@ -1524,20 +2014,24 @@ unittest
assert(t2_2 == t1);
assert(t2_2 == 2);
- }
+ }}
}
BigInt n = 2;
n *= 2;
+ assert(n == 4);
}
-@safe unittest // 8167
+// https://issues.dlang.org/show_bug.cgi?id=8167
+@safe unittest
{
BigInt a = BigInt(3);
BigInt b = BigInt(a);
+ assert(b == 3);
}
-@safe unittest // 9061
+// https://issues.dlang.org/show_bug.cgi?id=9061
+@safe unittest
{
long l1 = 0x12345678_90ABCDEF;
long l2 = 0xFEDCBA09_87654321;
@@ -1556,7 +2050,8 @@ unittest
assert(l5 == b5);
}
-@system unittest // 11600
+// https://issues.dlang.org/show_bug.cgi?id=11600
+@safe unittest
{
import std.conv;
import std.exception : assertThrown;
@@ -1571,20 +2066,22 @@ unittest
assertThrown!ConvException(to!BigInt("-123four"));
}
-@safe unittest // 11583
+// https://issues.dlang.org/show_bug.cgi?id=11583
+@safe unittest
{
BigInt x = 0;
assert((x > 0) == false);
}
-@system unittest // 13391
+// https://issues.dlang.org/show_bug.cgi?id=13391
+@safe unittest
{
BigInt x1 = "123456789";
BigInt x2 = "123456789123456789";
BigInt x3 = "123456789123456789123456789";
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
{
assert((x1 * T.max) / T.max == x1);
assert((x2 * T.max) / T.max == x2);
@@ -1608,20 +2105,30 @@ unittest
assert(x2 == 1);
}
-@system unittest // 13963
+// https://issues.dlang.org/show_bug.cgi?id=13963
+@safe unittest
{
BigInt x = 1;
import std.meta : AliasSeq;
- foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint))
+ static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int))
{
assert(is(typeof(x % Int(1)) == int));
}
+ assert(is(typeof(x % 1U) == long));
assert(is(typeof(x % 1L) == long));
assert(is(typeof(x % 1UL) == BigInt));
+ auto x0 = BigInt(uint.max - 1);
auto x1 = BigInt(8);
+ assert(x1 / x == x1);
auto x2 = -BigInt(long.min) + 1;
+ // uint
+ assert( x0 % uint.max == x0 % BigInt(uint.max));
+ assert(-x0 % uint.max == -x0 % BigInt(uint.max));
+ assert( x0 % uint.max == long(uint.max - 1));
+ assert(-x0 % uint.max == -long(uint.max - 1));
+
// long
assert(x1 % 2L == 0L);
assert(-x1 % 2L == 0L);
@@ -1650,31 +2157,32 @@ unittest
assert(-x2 % ulong.max == -x2);
}
-@system unittest // 14124
+// https://issues.dlang.org/show_bug.cgi?id=14124
+@safe unittest
{
auto x = BigInt(-3);
x %= 3;
- assert(!x.isNegative());
- assert(x.isZero());
+ assert(!x.isNegative);
+ assert(x.isZero);
x = BigInt(-3);
x %= cast(ushort) 3;
- assert(!x.isNegative());
- assert(x.isZero());
+ assert(!x.isNegative);
+ assert(x.isZero);
x = BigInt(-3);
x %= 3L;
- assert(!x.isNegative());
- assert(x.isZero());
+ assert(!x.isNegative);
+ assert(x.isZero);
x = BigInt(3);
x %= -3;
- assert(!x.isNegative());
- assert(x.isZero());
+ assert(!x.isNegative);
+ assert(x.isZero);
}
-// issue 15678
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=15678
+@safe unittest
{
import std.exception : assertThrown;
assertThrown!ConvException(BigInt(""));
@@ -1682,8 +2190,8 @@ unittest
assertThrown!ConvException(BigInt("1234PUKE"));
}
-// Issue 6447
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=6447
+@safe unittest
{
import std.algorithm.comparison : equal;
import std.range : iota;
@@ -1698,8 +2206,151 @@ unittest
]));
}
-// Issue 17330
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=17330
+@safe unittest
{
auto b = immutable BigInt("123");
+ assert(b == 123);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=14767
+@safe pure unittest
+{
+ static immutable a = BigInt("340282366920938463463374607431768211455");
+ assert(a == BigInt("340282366920938463463374607431768211455"));
+
+ BigInt plusTwo(in BigInt n)
+ {
+ return n + 2;
+ }
+
+ enum BigInt test1 = BigInt(123);
+ enum BigInt test2 = plusTwo(test1);
+ assert(test2 == 125);
+}
+
+/**
+ * Finds the quotient and remainder for the given dividend and divisor in one operation.
+ *
+ * Params:
+ * dividend = the $(LREF BigInt) to divide
+ * divisor = the $(LREF BigInt) to divide the dividend by
+ * quotient = is set to the result of the division
+ * remainder = is set to the remainder of the division
+ */
+void divMod(const BigInt dividend, const BigInt divisor, out BigInt quotient, out BigInt remainder) pure nothrow @safe
+{
+ BigUint q, r;
+ BigUint.divMod(dividend.data, divisor.data, q, r);
+ quotient.sign = dividend.sign != divisor.sign;
+ quotient.data = q;
+ remainder.sign = dividend.sign;
+ remainder.data = r;
+}
+
+///
+@safe pure nothrow unittest
+{
+ auto a = BigInt(123);
+ auto b = BigInt(25);
+ BigInt q, r;
+
+ divMod(a, b, q, r);
+
+ assert(q == 4);
+ assert(r == 23);
+ assert(q * b + r == a);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18086
+@safe pure nothrow unittest
+{
+ BigInt q = 1;
+ BigInt r = 1;
+ BigInt c = 1024;
+ BigInt d = 100;
+
+ divMod(c, d, q, r);
+ assert(q == 10);
+ assert(r == 24);
+ assert((q * d + r) == c);
+
+ divMod(c, -d, q, r);
+ assert(q == -10);
+ assert(r == 24);
+ assert(q * -d + r == c);
+
+ divMod(-c, -d, q, r);
+ assert(q == 10);
+ assert(r == -24);
+ assert(q * -d + r == -c);
+
+ divMod(-c, d, q, r);
+ assert(q == -10);
+ assert(r == -24);
+ assert(q * d + r == -c);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19740
+@safe unittest
+{
+ BigInt a = BigInt(
+ "241127122100380210001001124020210001001100000200003101000062221012075223052000021042250111300200000000000" ~
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+ BigInt b = BigInt(
+ "700200000000500418321000401140010110000022007221432000000141020011323301104104060202100200457210001600142" ~
+ "000001012245300100001110215200000000120000000000000000000000000000000000000000000000000000000000000000000" ~
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+
+ BigInt c = a * b;
+ assert(c == BigInt(
+ "1688372108948068874722901180228375682334987075822938736581472847151834613694489486296103575639363261807341" ~
+ "3910091006778604956808730652275328822700182498926542563654351871390166691461743896850906716336187966456064" ~
+ "2702007176328110013356024000000000000000000000000000000000000000000000000000000000000000000000000000000000" ~
+ "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ~
+ "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
+}
+
+@safe unittest
+{
+ auto n = BigInt("1234"d);
+}
+
+/**
+Fast power modulus calculation for $(LREF BigInt) operands.
+Params:
+ base = the $(LREF BigInt) is basic operands.
+ exponent = the $(LREF BigInt) is power exponent of base.
+ modulus = the $(LREF BigInt) is modules to be modular of base ^ exponent.
+Returns:
+ The power modulus value of (base ^ exponent) % modulus.
+*/
+BigInt powmod(BigInt base, BigInt exponent, BigInt modulus) pure nothrow @safe
+{
+ BigInt result = 1;
+
+ while (exponent)
+ {
+ if (exponent.data.peekUint(0) & 1)
+ {
+ result = (result * base) % modulus;
+ }
+
+ auto tmp = base % modulus;
+ base = (tmp * tmp) % modulus;
+ exponent >>= 1;
+ }
+
+ return result;
+}
+
+/// for powmod
+@safe unittest
+{
+ BigInt base = BigInt("123456789012345678901234567890");
+ BigInt exponent = BigInt("1234567890123456789012345678901234567");
+ BigInt modulus = BigInt("1234567");
+
+ BigInt result = powmod(base, exponent, modulus);
+ assert(result == 359079);
}
diff --git a/libphobos/src/std/bitmanip.d b/libphobos/src/std/bitmanip.d
index 7802dfff97a..2dd6211aa7c 100644
--- a/libphobos/src/std/bitmanip.d
+++ b/libphobos/src/std/bitmanip.d
@@ -4,6 +4,7 @@
Bit-level manipulation facilities.
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE,
$(TR $(TH Category) $(TH Functions))
$(TR $(TD Bit constructs) $(TD
@@ -32,58 +33,59 @@ $(TR $(TD Tagging) $(TD
$(LREF taggedClassRef)
$(LREF taggedPointer)
))
-)
+))
-Copyright: Copyright Digital Mars 2007 - 2011.
+Copyright: Copyright The D Language Foundation 2007 - 2011.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP digitalmars.com, Walter Bright),
$(HTTP erdani.org, Andrei Alexandrescu),
- Jonathan M Davis,
+ $(HTTP jmdavisprog.com, Jonathan M Davis),
Alex Rønne Petersen,
Damian Ziemba,
Amaury SECHET
-Source: $(PHOBOSSRC std/_bitmanip.d)
+Source: $(PHOBOSSRC std/bitmanip.d)
*/
/*
- Copyright Digital Mars 2007 - 2012.
+ Copyright The D Language Foundation 2007 - 2012.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
*/
module std.bitmanip;
-//debug = bitarray; // uncomment to turn on debugging printf's
-
import std.range.primitives;
public import std.system : Endian;
import std.traits;
-version (unittest)
-{
- import std.stdio;
-}
-
-
-private string myToString(ulong n)
+private string myToString(ulong n) pure @safe
{
import core.internal.string : UnsignedStringBuf, unsignedToTempString;
UnsignedStringBuf buf;
auto s = unsignedToTempString(n, buf);
- return cast(string) s ~ (n > uint.max ? "UL" : "U");
+ // pure allows implicit cast to string
+ return s ~ (n > uint.max ? "UL" : "U");
+}
+
+@safe pure unittest
+{
+ assert(myToString(5) == "5U");
+ assert(myToString(uint.max) == "4294967295U");
+ assert(myToString(uint.max + 1UL) == "4294967296UL");
}
+
private template createAccessors(
string store, T, string name, size_t len, size_t offset)
{
static if (!name.length)
{
// No need to create any accessor
- enum result = "";
+ enum createAccessors = "";
}
else static if (len == 0)
{
// Fields of length 0 are always zero
- enum result = "enum "~T.stringof~" "~name~" = 0;\n";
+ enum createAccessors = "enum "~T.stringof~" "~name~" = 0;\n";
}
else
{
@@ -107,8 +109,7 @@ private template createAccessors(
static if (is(T == bool))
{
- static assert(len == 1);
- enum result =
+ enum createAccessors =
// getter
"@property bool " ~ name ~ "() @safe pure nothrow @nogc const { return "
~"("~store~" & "~myToString(maskAllElse)~") != 0;}\n"
@@ -120,7 +121,7 @@ private template createAccessors(
else
{
// getter
- enum result = "@property "~T.stringof~" "~name~"() @safe pure nothrow @nogc const { auto result = "
+ enum createAccessors = "@property "~T.stringof~" "~name~"() @safe pure nothrow @nogc const { auto result = "
~"("~store~" & "
~ myToString(maskAllElse) ~ ") >>"
~ myToString(offset) ~ ";"
@@ -149,7 +150,7 @@ private template createAccessors(
private template createStoreName(Ts...)
{
static if (Ts.length < 2)
- enum createStoreName = "";
+ enum createStoreName = "_bf";
else
enum createStoreName = "_" ~ Ts[1] ~ createStoreName!(Ts[3 .. $]);
}
@@ -168,22 +169,24 @@ private template createStorageAndFields(Ts...)
alias StoreType = ulong;
else
{
- static assert(false, "Field widths must sum to 8, 16, 32, or 64");
+ import std.conv : to;
+ static assert(false, "Field widths must sum to 8, 16, 32, or 64, not " ~ to!string(Size));
alias StoreType = ulong; // just to avoid another error msg
}
- enum result
+
+ enum createStorageAndFields
= "private " ~ StoreType.stringof ~ " " ~ Name ~ ";"
- ~ createFields!(Name, 0, Ts).result;
+ ~ createFields!(Name, 0, Ts);
}
private template createFields(string store, size_t offset, Ts...)
{
static if (Ts.length > 0)
- enum result
- = createAccessors!(store, Ts[0], Ts[1], Ts[2], offset).result
- ~ createFields!(store, offset + Ts[2], Ts[3 .. $]).result;
+ enum createFields
+ = createAccessors!(store, Ts[0], Ts[1], Ts[2], offset)
+ ~ createFields!(store, offset + Ts[2], Ts[3 .. $]);
else
- enum result = "";
+ enum createFields = "";
}
private ulong getBitsForAlign(ulong a)
@@ -220,7 +223,7 @@ private template createReferenceAccessor(string store, T, ulong bits, string nam
~" (("~store~" & (cast(typeof("~store~")) "~myToString(mask)~"))"
~" | ((cast(typeof("~store~")) cast(void*) v) & (cast(typeof("~store~")) "~myToString(~mask)~")));}\n";
- enum result = storage ~ storage_accessor ~ ref_accessor;
+ enum createReferenceAccessor = storage ~ storage_accessor ~ ref_accessor;
}
private template sizeOfBitField(T...)
@@ -238,135 +241,154 @@ private template createTaggedReference(T, ulong a, string name, Ts...)
"Fields must fit in the bits know to be zero because of alignment."
);
enum StoreName = createStoreName!(T, name, 0, Ts);
- enum result
- = createReferenceAccessor!(StoreName, T, sizeOfBitField!Ts, name).result
- ~ createFields!(StoreName, 0, Ts, size_t, "", T.sizeof * 8 - sizeOfBitField!Ts).result;
+ enum createTaggedReference
+ = createReferenceAccessor!(StoreName, T, sizeOfBitField!Ts, name)
+ ~ createFields!(StoreName, 0, Ts, size_t, "", T.sizeof * 8 - sizeOfBitField!Ts);
}
/**
-Allows creating bit fields inside $(D_PARAM struct)s and $(D_PARAM
-class)es.
-
-Example:
+Allows creating `bitfields` inside `structs`, `classes` and `unions`.
+
+A `bitfield` consists of one or more entries with a fixed number of
+bits reserved for each of the entries. The types of the entries can
+be `bool`s, integral types or enumerated types, arbitrarily mixed.
+The most efficient type to store in `bitfields` is `bool`, followed
+by unsigned types, followed by signed types.
+
+Each non-`bool` entry of the `bitfield` will be represented by the
+number of bits specified by the user. The minimum and the maximum
+numbers that represent this domain can be queried by using the name
+of the variable followed by `_min` or `_max`.
+
+Limitation: The number of bits in a `bitfield` is limited to 8, 16,
+32 or 64. If padding is needed, an entry should be explicitly
+allocated with an empty name.
+
+Implementation_details: `Bitfields` are internally stored in an
+`ubyte`, `ushort`, `uint` or `ulong` depending on the number of bits
+used. The bits are filled in the order given by the parameters,
+starting with the lowest significant bit. The name of the (private)
+variable used for saving the `bitfield` is created by a prefix `_bf`
+and concatenating all of the variable names, each preceded by an
+underscore.
+
+Params: T = A list of template parameters divided into chunks of 3
+ items. Each chunk consists (in this order) of a type, a
+ name and a number. Together they define an entry
+ of the `bitfield`: a variable of the given type and name,
+ which can hold as many bits as the number denotes.
+
+Returns: A string that can be used in a `mixin` to add the `bitfield`.
+
+See_Also: $(REF BitFlags, std,typecons)
+*/
+string bitfields(T...)()
+{
+ import std.conv : to;
-----
-struct A
-{
- int a;
- mixin(bitfields!(
- uint, "x", 2,
- int, "y", 3,
- uint, "z", 2,
- bool, "flag", 1));
-}
-A obj;
-obj.x = 2;
-obj.z = obj.x;
-----
+ static assert(T.length % 3 == 0,
+ "Wrong number of arguments (" ~ to!string(T.length) ~ "): Must be a multiple of 3");
-The example above creates a bitfield pack of eight bits, which fit in
-one $(D_PARAM ubyte). The bitfields are allocated starting from the
-least significant bit, i.e. x occupies the two least significant bits
-of the bitfields storage.
+ static foreach (i, ARG; T)
+ {
+ static if (i % 3 == 0)
+ static assert(is (typeof({ARG tmp = cast (ARG)0; if (ARG.min < 0) {} }())),
+ "Integral type or `bool` expected, found " ~ ARG.stringof);
-The sum of all bit lengths in one $(D_PARAM bitfield) instantiation
-must be exactly 8, 16, 32, or 64. If padding is needed, just allocate
-one bitfield with an empty name.
+ // would be nice to check for valid variable names too
+ static if (i % 3 == 1)
+ static assert(is (typeof(ARG) : string),
+ "Variable name expected, found " ~ ARG.stringof);
-Example:
+ static if (i % 3 == 2)
+ {
+ static assert(is (typeof({ulong tmp = ARG;}())),
+ "Integral value expected, found " ~ ARG.stringof);
-----
-struct A
-{
- mixin(bitfields!(
- bool, "flag1", 1,
- bool, "flag2", 1,
- uint, "", 6));
-}
-----
+ static if (T[i-1] != "")
+ {
+ static assert(!is (T[i-2] : bool) || ARG <= 1,
+ "Type `bool` is only allowed for single-bit fields");
-The type of a bit field can be any integral type or enumerated
-type. The most efficient type to store in bitfields is $(D_PARAM
-bool), followed by unsigned types, followed by signed types.
-*/
+ static assert(ARG >= 0 && ARG <= T[i-2].sizeof * 8,
+ "Wrong size of bitfield: " ~ ARG.stringof);
+ }
+ }
+ }
-template bitfields(T...)
-{
- enum { bitfields = createStorageAndFields!T.result }
+ return createStorageAndFields!T;
}
/**
-This string mixin generator allows one to create tagged pointers inside $(D_PARAM struct)s and $(D_PARAM class)es.
-
-A tagged pointer uses the bits known to be zero in a normal pointer or class reference to store extra information.
-For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero.
-One can store a 2-bit integer there.
-
-The example above creates a tagged pointer in the struct A. The pointer is of type
-$(D uint*) as specified by the first argument, and is named x, as specified by the second
-argument.
-
-Following arguments works the same way as $(D bitfield)'s. The bitfield must fit into the
-bits known to be zero because of the pointer alignment.
+Create a `bitfield` pack of eight bits, which fit in
+one `ubyte`. The `bitfields` are allocated starting from the
+least significant bit, i.e. `x` occupies the two least significant bits
+of the `bitfields` storage.
*/
-
-template taggedPointer(T : T*, string name, Ts...) {
- enum taggedPointer = createTaggedReference!(T*, T.alignof, name, Ts).result;
-}
-
-///
@safe unittest
{
struct A
{
int a;
- mixin(taggedPointer!(
- uint*, "x",
- bool, "b1", 1,
- bool, "b2", 1));
+ mixin(bitfields!(
+ uint, "x", 2,
+ int, "y", 3,
+ uint, "z", 2,
+ bool, "flag", 1));
}
+
A obj;
- obj.x = new uint;
- obj.b1 = true;
- obj.b2 = false;
+ obj.x = 2;
+ obj.z = obj.x;
+
+ assert(obj.x == 2);
+ assert(obj.y == 0);
+ assert(obj.z == 2);
+ assert(obj.flag == false);
}
/**
-This string mixin generator allows one to create tagged class reference inside $(D_PARAM struct)s and $(D_PARAM class)es.
-
-A tagged class reference uses the bits known to be zero in a normal class reference to store extra information.
-For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero.
-One can store a 2-bit integer there.
-
-The example above creates a tagged reference to an Object in the struct A. This expects the same parameters
-as $(D taggedPointer), except the first argument which must be a class type instead of a pointer type.
+The sum of all bit lengths in one `bitfield` instantiation
+must be exactly 8, 16, 32, or 64. If padding is needed, just allocate
+one bitfield with an empty name.
*/
-
-template taggedClassRef(T, string name, Ts...)
-if (is(T == class))
+@safe unittest
{
- enum taggedClassRef = createTaggedReference!(T, 8, name, Ts).result;
+ struct A
+ {
+ mixin(bitfields!(
+ bool, "flag1", 1,
+ bool, "flag2", 1,
+ uint, "", 6));
+ }
+
+ A a;
+ assert(a.flag1 == 0);
+ a.flag1 = 1;
+ assert(a.flag1 == 1);
+ a.flag1 = 0;
+ assert(a.flag1 == 0);
}
-///
+/// enums can be used too
@safe unittest
{
- struct A
+ enum ABC { A, B, C }
+ struct EnumTest
{
- int a;
- mixin(taggedClassRef!(
- Object, "o",
- uint, "i", 2));
+ mixin(bitfields!(
+ ABC, "x", 2,
+ bool, "y", 1,
+ ubyte, "z", 5));
}
- A obj;
- obj.o = new Object();
- obj.i = 3;
}
@safe pure nothrow @nogc
unittest
{
- // Degenerate bitfields (#8474 / #11160) tests mixed with range tests
+ // Degenerate bitfields tests mixed with range tests
+ // https://issues.dlang.org/show_bug.cgi?id=8474
+ // https://issues.dlang.org/show_bug.cgi?id=11160
struct Test1
{
mixin(bitfields!(uint, "a", 32,
@@ -430,73 +452,9 @@ unittest
t4b.a = -5; assert(t4b.a == -5L);
}
-@system unittest
-{
- struct Test5
- {
- mixin(taggedPointer!(
- int*, "a",
- uint, "b", 2));
- }
-
- Test5 t5;
- t5.a = null;
- t5.b = 3;
- assert(t5.a is null);
- assert(t5.b == 3);
-
- int myint = 42;
- t5.a = &myint;
- assert(t5.a is &myint);
- assert(t5.b == 3);
-
- struct Test6
- {
- mixin(taggedClassRef!(
- Object, "o",
- bool, "b", 1));
- }
-
- Test6 t6;
- t6.o = null;
- t6.b = false;
- assert(t6.o is null);
- assert(t6.b == false);
-
- auto o = new Object();
- t6.o = o;
- t6.b = true;
- assert(t6.o is o);
- assert(t6.b == true);
-}
-
-@safe unittest
-{
- static assert(!__traits(compiles,
- taggedPointer!(
- int*, "a",
- uint, "b", 3)));
-
- static assert(!__traits(compiles,
- taggedClassRef!(
- Object, "a",
- uint, "b", 4)));
-
- struct S {
- mixin(taggedClassRef!(
- Object, "a",
- bool, "b", 1));
- }
-
- const S s;
- void bar(S s) {}
-
- static assert(!__traits(compiles, bar(s)));
-}
-
+// https://issues.dlang.org/show_bug.cgi?id=6686
@safe unittest
{
- // Bug #6686
union S {
ulong bits = ulong.max;
mixin (bitfields!(
@@ -511,9 +469,9 @@ unittest
assert(num.bits == 0xFFFF_FFFF_8000_0001uL);
}
+// https://issues.dlang.org/show_bug.cgi?id=5942
@safe unittest
{
- // Bug #5942
struct S
{
mixin(bitfields!(
@@ -576,7 +534,7 @@ unittest
assert(i.checkExpectations(true, 7, 7));
}
- //Bug# 8876
+ //https://issues.dlang.org/show_bug.cgi?id=8876
{
struct MoreIntegrals {
bool checkExpectations(uint eu, ushort es, uint ei) { return u == eu && s == es && i == ei; }
@@ -629,12 +587,11 @@ unittest
assert(f.checkExpectations(true));
}
-// Issue 12477
+// https://issues.dlang.org/show_bug.cgi?id=12477
@system unittest
{
import core.exception : AssertError;
import std.algorithm.searching : canFind;
- import std.bitmanip : bitfields;
static struct S
{
@@ -654,26 +611,184 @@ unittest
{ assert(ae.msg.canFind("Value is smaller than the minimum value of bitfield 'b'"), ae.msg); }
}
-/**
- Allows manipulating the fraction, exponent, and sign parts of a
- $(D_PARAM float) separately. The definition is:
+// https://issues.dlang.org/show_bug.cgi?id=15305
+@safe unittest
+{
+ struct S {
+ mixin(bitfields!(
+ bool, "alice", 1,
+ ulong, "bob", 63,
+ ));
+ }
-----
-struct FloatRep
+ S s;
+ s.bob = long.max - 1;
+ s.alice = false;
+ assert(s.bob == long.max - 1);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21634
+@safe unittest
{
- union
+ struct A
+ {
+ mixin(bitfields!(int, "", 1,
+ int, "gshared", 7));
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21725
+@safe unittest
+{
+ struct S
{
- float value;
mixin(bitfields!(
- uint, "fraction", 23,
- ubyte, "exponent", 8,
- bool, "sign", 1));
+ uint, q{foo}, 4,
+ uint, null, 4,
+ ));
}
- enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1;
}
-----
+
+/**
+This string mixin generator allows one to create tagged pointers inside $(D_PARAM struct)s and $(D_PARAM class)es.
+
+A tagged pointer uses the bits known to be zero in a normal pointer or class reference to store extra information.
+For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero.
+One can store a 2-bit integer there.
+
+The example above creates a tagged pointer in the struct A. The pointer is of type
+`uint*` as specified by the first argument, and is named x, as specified by the second
+argument.
+
+Following arguments works the same way as `bitfield`'s. The bitfield must fit into the
+bits known to be zero because of the pointer alignment.
+*/
+
+template taggedPointer(T : T*, string name, Ts...) {
+ enum taggedPointer = createTaggedReference!(T*, T.alignof, name, Ts);
+}
+
+///
+@safe unittest
+{
+ struct A
+ {
+ int a;
+ mixin(taggedPointer!(
+ uint*, "x",
+ bool, "b1", 1,
+ bool, "b2", 1));
+ }
+ A obj;
+ obj.x = new uint;
+ obj.b1 = true;
+ obj.b2 = false;
+}
+
+@system unittest
+{
+ struct Test5
+ {
+ mixin(taggedPointer!(
+ int*, "a",
+ uint, "b", 2));
+ }
+
+ Test5 t5;
+ t5.a = null;
+ t5.b = 3;
+ assert(t5.a is null);
+ assert(t5.b == 3);
+
+ int myint = 42;
+ t5.a = &myint;
+ assert(t5.a is &myint);
+ assert(t5.b == 3);
+}
+
+/**
+This string mixin generator allows one to create tagged class reference inside $(D_PARAM struct)s and $(D_PARAM class)es.
+
+A tagged class reference uses the bits known to be zero in a normal class reference to store extra information.
+For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero.
+One can store a 2-bit integer there.
+
+The example above creates a tagged reference to an Object in the struct A. This expects the same parameters
+as `taggedPointer`, except the first argument which must be a class type instead of a pointer type.
*/
+template taggedClassRef(T, string name, Ts...)
+if (is(T == class))
+{
+ enum taggedClassRef = createTaggedReference!(T, 8, name, Ts);
+}
+
+///
+@safe unittest
+{
+ struct A
+ {
+ int a;
+ mixin(taggedClassRef!(
+ Object, "o",
+ uint, "i", 2));
+ }
+ A obj;
+ obj.o = new Object();
+ obj.i = 3;
+}
+
+@system unittest
+{
+ struct Test6
+ {
+ mixin(taggedClassRef!(
+ Object, "o",
+ bool, "b", 1));
+ }
+
+ Test6 t6;
+ t6.o = null;
+ t6.b = false;
+ assert(t6.o is null);
+ assert(t6.b == false);
+
+ auto o = new Object();
+ t6.o = o;
+ t6.b = true;
+ assert(t6.o is o);
+ assert(t6.b == true);
+}
+
+@safe unittest
+{
+ static assert(!__traits(compiles,
+ taggedPointer!(
+ int*, "a",
+ uint, "b", 3)));
+
+ static assert(!__traits(compiles,
+ taggedClassRef!(
+ Object, "a",
+ uint, "b", 4)));
+
+ struct S {
+ mixin(taggedClassRef!(
+ Object, "a",
+ bool, "b", 1));
+ }
+
+ const S s;
+ void bar(S s) {}
+
+ static assert(!__traits(compiles, bar(s)));
+}
+
+/**
+ Allows manipulating the fraction, exponent, and sign parts of a
+ `float` separately. The definition is:
+
+----
struct FloatRep
{
union
@@ -686,10 +801,13 @@ struct FloatRep
}
enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1;
}
+----
+*/
+alias FloatRep = FloatingPointRepresentation!float;
/**
Allows manipulating the fraction, exponent, and sign parts of a
- $(D_PARAM double) separately. The definition is:
+ `double` separately. The definition is:
----
struct DoubleRep
@@ -706,23 +824,132 @@ struct DoubleRep
}
----
*/
+alias DoubleRep = FloatingPointRepresentation!double;
-struct DoubleRep
+private struct FloatingPointRepresentation(T)
{
+ static if (is(T == float))
+ {
+ enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1;
+ alias FractionType = uint;
+ alias ExponentType = ubyte;
+ }
+ else
+ {
+ enum uint bias = 1023, fractionBits = 52, exponentBits = 11, signBits = 1;
+ alias FractionType = ulong;
+ alias ExponentType = ushort;
+ }
+
union
{
- double value;
+ T value;
mixin(bitfields!(
- ulong, "fraction", 52,
- ushort, "exponent", 11,
- bool, "sign", 1));
+ FractionType, "fraction", fractionBits,
+ ExponentType, "exponent", exponentBits,
+ bool, "sign", signBits));
}
- enum uint bias = 1023, signBits = 1, fractionBits = 52, exponentBits = 11;
}
+///
+@safe unittest
+{
+ FloatRep rep = {value: 0};
+ assert(rep.fraction == 0);
+ assert(rep.exponent == 0);
+ assert(!rep.sign);
+
+ rep.value = 42;
+ assert(rep.fraction == 2621440);
+ assert(rep.exponent == 132);
+ assert(!rep.sign);
+
+ rep.value = 10;
+ assert(rep.fraction == 2097152);
+ assert(rep.exponent == 130);
+}
+
+///
+@safe unittest
+{
+ FloatRep rep = {value: 1};
+ assert(rep.fraction == 0);
+ assert(rep.exponent == 127);
+ assert(!rep.sign);
+
+ rep.exponent = 126;
+ assert(rep.value == 0.5);
+
+ rep.exponent = 130;
+ assert(rep.value == 8);
+}
+
+///
+@safe unittest
+{
+ FloatRep rep = {value: 1};
+ rep.value = -0.5;
+ assert(rep.fraction == 0);
+ assert(rep.exponent == 126);
+ assert(rep.sign);
+
+ rep.value = -1. / 3;
+ assert(rep.fraction == 2796203);
+ assert(rep.exponent == 125);
+ assert(rep.sign);
+}
+
+///
+@safe unittest
+{
+ DoubleRep rep = {value: 0};
+ assert(rep.fraction == 0);
+ assert(rep.exponent == 0);
+ assert(!rep.sign);
+
+ rep.value = 42;
+ assert(rep.fraction == 1407374883553280);
+ assert(rep.exponent == 1028);
+ assert(!rep.sign);
+
+ rep.value = 10;
+ assert(rep.fraction == 1125899906842624);
+ assert(rep.exponent == 1026);
+}
+
+///
+@safe unittest
+{
+ DoubleRep rep = {value: 1};
+ assert(rep.fraction == 0);
+ assert(rep.exponent == 1023);
+ assert(!rep.sign);
+
+ rep.exponent = 1022;
+ assert(rep.value == 0.5);
+
+ rep.exponent = 1026;
+ assert(rep.value == 8);
+}
+
+///
+@safe unittest
+{
+ DoubleRep rep = {value: 1};
+ rep.value = -0.5;
+ assert(rep.fraction == 0);
+ assert(rep.exponent == 1022);
+ assert(rep.sign);
+
+ rep.value = -1. / 3;
+ assert(rep.fraction == 1501199875790165);
+ assert(rep.exponent == 1021);
+ assert(rep.sign);
+}
+
+/// Reading
@safe unittest
{
- // test reading
DoubleRep x;
x.value = 1.0;
assert(x.fraction == 0 && x.exponent == 1023 && !x.sign);
@@ -730,50 +957,30 @@ struct DoubleRep
assert(x.fraction == 0 && x.exponent == 1022 && x.sign);
x.value = 0.5;
assert(x.fraction == 0 && x.exponent == 1022 && !x.sign);
+}
- // test writing
+/// Writing
+@safe unittest
+{
+ DoubleRep x;
x.fraction = 1125899906842624;
x.exponent = 1025;
x.sign = true;
assert(x.value == -5.0);
-
- // test enums
- enum ABC { A, B, C }
- struct EnumTest
- {
- mixin(bitfields!(
- ABC, "x", 2,
- bool, "y", 1,
- ubyte, "z", 5));
- }
-}
-
-@safe unittest
-{
- // Issue #15305
- struct S {
- mixin(bitfields!(
- bool, "alice", 1,
- ulong, "bob", 63,
- ));
- }
-
- S s;
- s.bob = long.max - 1;
- s.alice = false;
- assert(s.bob == long.max - 1);
}
/**
- * An array of bits.
- */
-
+A dynamic array of bits. Each bit in a `BitArray` can be manipulated individually
+or by the standard bitwise operators `&`, `|`, `^`, `~`, `>>`, `<<` and also by
+other effective member functions; most of them work relative to the `BitArray`'s
+dimension (see $(LREF dim)), instead of its $(LREF length).
+*/
struct BitArray
{
private:
- import core.bitop : bts, btr, bsf, bt;
- import std.format : FormatSpec;
+ import core.bitop : btc, bts, btr, bsf, bt;
+ import std.format.spec : FormatSpec;
size_t _len;
size_t* _ptr;
@@ -799,27 +1006,182 @@ private:
}
public:
- /**********************************************
- * Gets the amount of native words backing this $(D BitArray).
- */
- @property size_t dim() const @nogc pure nothrow @safe
+ /**
+ Creates a `BitArray` from a `bool` array, such that `bool` values read
+ from left to right correspond to subsequent bits in the `BitArray`.
+
+ Params: ba = Source array of `bool` values.
+ */
+ this(in bool[] ba) nothrow pure
+ {
+ length = ba.length;
+ foreach (i, b; ba)
+ {
+ this[i] = b;
+ }
+ }
+
+ ///
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+
+ bool[] input = [true, false, false, true, true];
+ auto a = BitArray(input);
+ assert(a.length == 5);
+ assert(a.bitsSet.equal([0, 3, 4]));
+
+ // This also works because an implicit cast to bool[] occurs for this array.
+ auto b = BitArray([0, 0, 1]);
+ assert(b.length == 3);
+ assert(b.bitsSet.equal([2]));
+ }
+
+ ///
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.array : array;
+ import std.range : iota, repeat;
+
+ BitArray a = true.repeat(70).array;
+ assert(a.length == 70);
+ assert(a.bitsSet.equal(iota(0, 70)));
+ }
+
+ /**
+ Creates a `BitArray` from the raw contents of the source array. The
+ source array is not copied but simply acts as the underlying array
+ of bits, which stores data as `size_t` units.
+
+ That means a particular care should be taken when passing an array
+ of a type different than `size_t`, firstly because its length should
+ be a multiple of `size_t.sizeof`, and secondly because how the bits
+ are mapped:
+ ---
+ size_t[] source = [1, 2, 3, 3424234, 724398, 230947, 389492];
+ enum sbits = size_t.sizeof * 8;
+ auto ba = BitArray(source, source.length * sbits);
+ foreach (n; 0 .. source.length * sbits)
+ {
+ auto nth_bit = cast(bool) (source[n / sbits] & (1L << (n % sbits)));
+ assert(ba[n] == nth_bit);
+ }
+ ---
+ The least significant bit in any `size_t` unit is the starting bit of this
+ unit, and the most significant bit is the last bit of this unit. Therefore,
+ passing e.g. an array of `int`s may result in a different `BitArray`
+ depending on the processor's endianness.
+
+ This constructor is the inverse of $(LREF opCast).
+
+ Params:
+ v = Source array. `v.length` must be a multple of `size_t.sizeof`.
+ numbits = Number of bits to be mapped from the source array, i.e.
+ length of the created `BitArray`.
+ */
+ this(void[] v, size_t numbits) @nogc nothrow pure
+ in
+ {
+ assert(numbits <= v.length * 8,
+ "numbits must be less than or equal to v.length * 8");
+ assert(v.length % size_t.sizeof == 0,
+ "v.length must be a multiple of the size of size_t");
+ }
+ do
+ {
+ _ptr = cast(size_t*) v.ptr;
+ _len = numbits;
+ }
+
+ ///
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+
+ auto a = BitArray([1, 0, 0, 1, 1]);
+
+ // Inverse of the cast.
+ auto v = cast(void[]) a;
+ auto b = BitArray(v, a.length);
+
+ assert(b.length == 5);
+ assert(b.bitsSet.equal([0, 3, 4]));
+
+ // a and b share the underlying data.
+ a[0] = 0;
+ assert(b[0] == 0);
+ assert(a == b);
+ }
+
+ ///
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+
+ size_t[] source = [0b1100, 0b0011];
+ enum sbits = size_t.sizeof * 8;
+ auto ba = BitArray(source, source.length * sbits);
+ // The least significant bit in each unit is this unit's starting bit.
+ assert(ba.bitsSet.equal([2, 3, sbits, sbits + 1]));
+ }
+
+ ///
+ @system unittest
+ {
+ // Example from the doc for this constructor.
+ static immutable size_t[] sourceData = [1, 0b101, 3, 3424234, 724398, 230947, 389492];
+ size_t[] source = sourceData.dup;
+ enum sbits = size_t.sizeof * 8;
+ auto ba = BitArray(source, source.length * sbits);
+ foreach (n; 0 .. source.length * sbits)
+ {
+ auto nth_bit = cast(bool) (source[n / sbits] & (1L << (n % sbits)));
+ assert(ba[n] == nth_bit);
+ }
+
+ // Example of mapping only part of the array.
+ import std.algorithm.comparison : equal;
+
+ auto bc = BitArray(source, sbits + 1);
+ assert(bc.bitsSet.equal([0, sbits]));
+ // Source array has not been modified.
+ assert(source == sourceData);
+ }
+
+ // Deliberately undocumented: raw initialization of bit array.
+ this(size_t len, size_t* ptr) @nogc nothrow pure
+ {
+ _len = len;
+ _ptr = ptr;
+ }
+
+ /**
+ Returns: Dimension i.e. the number of native words backing this `BitArray`.
+
+ Technically, this is the length of the underlying array storing bits, which
+ is equal to `ceil(length / (size_t.sizeof * 8))`, as bits are packed into
+ `size_t` units.
+ */
+ @property size_t dim() const @nogc nothrow pure @safe
{
return lenToDim(_len);
}
- /**********************************************
- * Gets the amount of bits in the $(D BitArray).
- */
- @property size_t length() const @nogc pure nothrow @safe
+ /**
+ Returns: Number of bits in the `BitArray`.
+ */
+ @property size_t length() const @nogc nothrow pure @safe
{
return _len;
}
/**********************************************
- * Sets the amount of bits in the $(D BitArray).
+ * Sets the amount of bits in the `BitArray`.
* $(RED Warning: increasing length may overwrite bits in
- * final word up to the next word boundary. i.e. D dynamic
- * array extension semantics are not followed.)
+ * the final word of the current underlying data regardless
+ * of whether it is shared between BitArray objects. i.e. D
+ * dynamic array extension semantics are not followed.)
*/
@property size_t length(size_t newlen) pure nothrow @system
{
@@ -836,29 +1198,54 @@ public:
_ptr = b.ptr;
}
+ auto oldlen = _len;
_len = newlen;
+ if (oldlen < newlen)
+ {
+ auto end = ((oldlen / bitsPerSizeT) + 1) * bitsPerSizeT;
+ if (end > newlen)
+ end = newlen;
+ this[oldlen .. end] = 0;
+ }
}
return _len;
}
+ // https://issues.dlang.org/show_bug.cgi?id=20240
+ @system unittest
+ {
+ BitArray ba;
+
+ ba.length = 1;
+ ba[0] = 1;
+ ba.length = 0;
+ ba.length = 1;
+ assert(ba[0] == 0); // OK
+
+ ba.length = 2;
+ ba[1] = 1;
+ ba.length = 1;
+ ba.length = 2;
+ assert(ba[1] == 0); // Fail
+ }
+
/**********************************************
- * Gets the $(D i)'th bit in the $(D BitArray).
+ * Gets the `i`'th bit in the `BitArray`.
*/
bool opIndex(size_t i) const @nogc pure nothrow
in
{
- assert(i < _len);
+ assert(i < _len, "i must be less than the length");
}
- body
+ do
{
return cast(bool) bt(_ptr, i);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opIndex.unittest\n");
-
- void Fun(const BitArray arr)
+ static void fun(const BitArray arr)
{
auto x = arr[0];
assert(x == 1);
@@ -866,18 +1253,18 @@ public:
BitArray a;
a.length = 3;
a[0] = 1;
- Fun(a);
+ fun(a);
}
/**********************************************
- * Sets the $(D i)'th bit in the $(D BitArray).
+ * Sets the `i`'th bit in the `BitArray`.
*/
bool opIndexAssign(bool b, size_t i) @nogc pure nothrow
in
{
- assert(i < _len);
+ assert(i < _len, "i must be less than the length");
}
- body
+ do
{
if (b)
bts(_ptr, i);
@@ -886,8 +1273,206 @@ public:
return b;
}
+ /**
+ Sets all the values in the `BitArray` to the
+ value specified by `val`.
+ */
+ void opSliceAssign(bool val) @nogc pure nothrow
+ {
+ _ptr[0 .. fullWords] = val ? ~size_t(0) : 0;
+ if (endBits)
+ {
+ if (val)
+ _ptr[fullWords] |= endMask;
+ else
+ _ptr[fullWords] &= ~endMask;
+ }
+ }
+
+ ///
+ @system pure nothrow unittest
+ {
+ import std.algorithm.comparison : equal;
+
+ auto b = BitArray([1, 0, 1, 0, 1, 1]);
+
+ b[] = true;
+ // all bits are set
+ assert(b.bitsSet.equal([0, 1, 2, 3, 4, 5]));
+
+ b[] = false;
+ // none of the bits are set
+ assert(b.bitsSet.empty);
+ }
+
+ /**
+ Sets the bits of a slice of `BitArray` starting
+ at index `start` and ends at index ($D end - 1)
+ with the values specified by `val`.
+ */
+ void opSliceAssign(bool val, size_t start, size_t end) @nogc pure nothrow
+ in
+ {
+ assert(start <= end, "start must be less or equal to end");
+ assert(end <= length, "end must be less or equal to the length");
+ }
+ do
+ {
+ size_t startBlock = start / bitsPerSizeT;
+ size_t endBlock = end / bitsPerSizeT;
+ size_t startOffset = start % bitsPerSizeT;
+ size_t endOffset = end % bitsPerSizeT;
+
+ if (startBlock == endBlock)
+ {
+ size_t startBlockMask = ~((size_t(1) << startOffset) - 1);
+ size_t endBlockMask = (size_t(1) << endOffset) - 1;
+ size_t joinMask = startBlockMask & endBlockMask;
+ if (val)
+ _ptr[startBlock] |= joinMask;
+ else
+ _ptr[startBlock] &= ~joinMask;
+ return;
+ }
+
+ if (startOffset != 0)
+ {
+ size_t startBlockMask = ~((size_t(1) << startOffset) - 1);
+ if (val)
+ _ptr[startBlock] |= startBlockMask;
+ else
+ _ptr[startBlock] &= ~startBlockMask;
+ ++startBlock;
+ }
+ if (endOffset != 0)
+ {
+ size_t endBlockMask = (size_t(1) << endOffset) - 1;
+ if (val)
+ _ptr[endBlock] |= endBlockMask;
+ else
+ _ptr[endBlock] &= ~endBlockMask;
+ }
+ _ptr[startBlock .. endBlock] = size_t(0) - size_t(val);
+ }
+
+ ///
+ @system pure nothrow unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+ import std.stdio;
+
+ auto b = BitArray([1, 0, 0, 0, 1, 1, 0]);
+ b[1 .. 3] = true;
+ assert(b.bitsSet.equal([0, 1, 2, 4, 5]));
+
+ bool[72] bitArray;
+ auto b1 = BitArray(bitArray);
+ b1[63 .. 67] = true;
+ assert(b1.bitsSet.equal([63, 64, 65, 66]));
+ b1[63 .. 67] = false;
+ assert(b1.bitsSet.empty);
+ b1[0 .. 64] = true;
+ assert(b1.bitsSet.equal(iota(0, 64)));
+ b1[0 .. 64] = false;
+ assert(b1.bitsSet.empty);
+
+ bool[256] bitArray2;
+ auto b2 = BitArray(bitArray2);
+ b2[3 .. 245] = true;
+ assert(b2.bitsSet.equal(iota(3, 245)));
+ b2[3 .. 245] = false;
+ assert(b2.bitsSet.empty);
+ }
+
+ /**
+ Flips all the bits in the `BitArray`
+ */
+ void flip() @nogc pure nothrow
+ {
+ foreach (i; 0 .. fullWords)
+ _ptr[i] = ~_ptr[i];
+
+ if (endBits)
+ _ptr[fullWords] = (~_ptr[fullWords]) & endMask;
+ }
+
+ ///
+ @system pure nothrow unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ // positions 0, 2, 4 are set
+ auto b = BitArray([1, 0, 1, 0, 1, 0]);
+ b.flip();
+ // after flipping, positions 1, 3, 5 are set
+ assert(b.bitsSet.equal([1, 3, 5]));
+
+ bool[270] bits;
+ auto b1 = BitArray(bits);
+ b1.flip();
+ assert(b1.bitsSet.equal(iota(0, 270)));
+ }
+
+ /**
+ Flips a single bit, specified by `pos`
+ */
+ void flip(size_t i) @nogc pure nothrow
+ {
+ bt(_ptr, i) ? btr(_ptr, i) : bts(_ptr, i);
+ }
+
+ ///
+ @system pure nothrow unittest
+ {
+ auto ax = BitArray([1, 0, 0, 1]);
+ ax.flip(0);
+ assert(ax[0] == 0);
+
+ bool[200] y;
+ y[90 .. 130] = true;
+ auto ay = BitArray(y);
+ ay.flip(100);
+ assert(ay[100] == 0);
+ }
+
+ /**********************************************
+ * Counts all the set bits in the `BitArray`
+ */
+ size_t count() const @nogc pure nothrow
+ {
+ if (_ptr)
+ {
+ size_t bitCount;
+ foreach (i; 0 .. fullWords)
+ bitCount += countBitsSet(_ptr[i]);
+ bitCount += countBitsSet(_ptr[fullWords] & endMask);
+ return bitCount;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ ///
+ @system pure nothrow unittest
+ {
+ auto a = BitArray([0, 1, 1, 0, 0, 1, 1]);
+ assert(a.count == 4);
+
+ BitArray b;
+ assert(b.count == 0);
+
+ bool[200] boolArray;
+ boolArray[45 .. 130] = true;
+ auto c = BitArray(boolArray);
+ assert(c.count == 85);
+ }
+
/**********************************************
- * Duplicates the $(D BitArray) and its contents.
+ * Duplicates the `BitArray` and its contents.
*/
@property BitArray dup() const pure nothrow
{
@@ -899,26 +1484,22 @@ public:
return ba;
}
+ ///
@system unittest
{
BitArray a;
BitArray b;
- int i;
-
- debug(bitarray) printf("BitArray.dup.unittest\n");
a.length = 3;
a[0] = 1; a[1] = 0; a[2] = 1;
b = a.dup;
assert(b.length == 3);
- for (i = 0; i < 3; i++)
- { debug(bitarray) printf("b[%d] = %d\n", i, b[i]);
+ foreach (i; 0 .. 3)
assert(b[i] == (((i ^ 1) & 1) ? true : false));
- }
}
/**********************************************
- * Support for $(D foreach) loops for $(D BitArray).
+ * Support for `foreach` loops for `BitArray`.
*/
int opApply(scope int delegate(ref bool) dg)
{
@@ -981,11 +1562,10 @@ public:
return result;
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opApply unittest\n");
-
- static bool[] ba = [1,0,1];
+ bool[] ba = [1,0,1];
auto a = BitArray(ba);
@@ -1016,14 +1596,14 @@ public:
/**********************************************
- * Reverses the bits of the $(D BitArray).
+ * Reverses the bits of the `BitArray`.
*/
- @property BitArray reverse() @nogc pure nothrow
+ @property BitArray reverse() @nogc pure nothrow return
out (result)
{
- assert(result == this);
+ assert(result == this, "the result must be equal to this");
}
- body
+ do
{
if (_len >= 2)
{
@@ -1042,32 +1622,28 @@ public:
return this;
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.reverse.unittest\n");
-
BitArray b;
- static bool[5] data = [1,0,1,1,0];
- int i;
+ bool[5] data = [1,0,1,1,0];
b = BitArray(data);
b.reverse;
- for (i = 0; i < data.length; i++)
- {
+ foreach (i; 0 .. data.length)
assert(b[i] == data[4 - i]);
- }
}
/**********************************************
- * Sorts the $(D BitArray)'s elements.
+ * Sorts the `BitArray`'s elements.
*/
- @property BitArray sort() @nogc pure nothrow
+ @property BitArray sort() @nogc pure nothrow return
out (result)
{
- assert(result == this);
+ assert(result == this, "the result must be equal to this");
}
- body
+ do
{
if (_len >= 2)
{
@@ -1106,22 +1682,21 @@ public:
return this;
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.sort.unittest\n");
-
- __gshared size_t x = 0b1100011000;
- __gshared ba = BitArray(10, &x);
+ size_t x = 0b1100011000;
+ auto ba = BitArray(10, &x);
ba.sort;
- for (size_t i = 0; i < 6; i++)
+ foreach (i; 0 .. 6)
assert(ba[i] == false);
- for (size_t i = 6; i < 10; i++)
+ foreach (i; 6 .. 10)
assert(ba[i] == true);
}
/***************************************
- * Support for operators == and != for $(D BitArray).
+ * Support for operators == and != for `BitArray`.
*/
bool opEquals(const ref BitArray a2) const @nogc pure nothrow
{
@@ -1140,17 +1715,16 @@ public:
return (p1[i] & endMask) == (p2[i] & endMask);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opEquals unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
- static bool[] bb = [1,0,1];
- static bool[] bc = [1,0,1,0,1,0,1];
- static bool[] bd = [1,0,1,1,1];
- static bool[] be = [1,0,1,0,1];
- static bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
- static bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
+ bool[] ba = [1,0,1,0,1];
+ bool[] bb = [1,0,1];
+ bool[] bc = [1,0,1,0,1,0,1];
+ bool[] bd = [1,0,1,1,1];
+ bool[] be = [1,0,1,0,1];
+ bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
+ bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1168,7 +1742,7 @@ public:
}
/***************************************
- * Supports comparison operators for $(D BitArray).
+ * Supports comparison operators for `BitArray`.
*/
int opCmp(BitArray a2) const @nogc pure nothrow
{
@@ -1206,17 +1780,16 @@ public:
return (this.length > a2.length) - (this.length < a2.length);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opCmp unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
- static bool[] bb = [1,0,1];
- static bool[] bc = [1,0,1,0,1,0,1];
- static bool[] bd = [1,0,1,1,1];
- static bool[] be = [1,0,1,0,1];
- static bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
- static bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0];
+ bool[] ba = [1,0,1,0,1];
+ bool[] bb = [1,0,1];
+ bool[] bc = [1,0,1,0,1,0,1];
+ bool[] bd = [1,0,1,1,1];
+ bool[] be = [1,0,1,0,1];
+ bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
+ bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1237,7 +1810,10 @@ public:
assert(a >= e);
assert(f < g);
assert(g <= g);
+ }
+ @system unittest
+ {
bool[] v;
foreach (i; 1 .. 256)
{
@@ -1269,7 +1845,7 @@ public:
}
/***************************************
- * Support for hashing for $(D BitArray).
+ * Support for hashing for `BitArray`.
*/
size_t toHash() const @nogc pure nothrow
{
@@ -1289,105 +1865,67 @@ public:
}
/***************************************
- * Set this $(D BitArray) to the contents of $(D ba).
+ * Convert to `void[]`.
*/
- this(bool[] ba) pure nothrow @system
- {
- length = ba.length;
- foreach (i, b; ba)
- {
- this[i] = b;
- }
- }
-
- // Deliberately undocumented: raw initialization of bit array.
- this(size_t len, size_t* ptr)
+ inout(void)[] opCast(T : const void[])() inout @nogc pure nothrow
{
- _len = len;
- _ptr = ptr;
+ return cast(inout void[]) _ptr[0 .. dim];
}
/***************************************
- * Map the $(D BitArray) onto $(D v), with $(D numbits) being the number of bits
- * in the array. Does not copy the data. $(D v.length) must be a multiple of
- * $(D size_t.sizeof). If there are unmapped bits in the final mapped word then
- * these will be set to 0.
- *
- * This is the inverse of $(D opCast).
+ * Convert to `size_t[]`.
*/
- this(void[] v, size_t numbits) pure nothrow
- in
+ inout(size_t)[] opCast(T : const size_t[])() inout @nogc pure nothrow
{
- assert(numbits <= v.length * 8);
- assert(v.length % size_t.sizeof == 0);
- }
- body
- {
- _ptr = cast(size_t*) v.ptr;
- _len = numbits;
- if (endBits)
- {
- // Need to mask away extraneous bits from v.
- _ptr[dim - 1] &= endMask;
- }
+ return _ptr[0 .. dim];
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.init unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
-
- auto a = BitArray(ba);
- void[] v;
-
- v = cast(void[]) a;
- auto b = BitArray(v, a.length);
-
- assert(b[0] == 1);
- assert(b[1] == 0);
- assert(b[2] == 1);
- assert(b[3] == 0);
- assert(b[4] == 1);
-
- a[0] = 0;
- assert(b[0] == 0);
+ import std.array : array;
+ import std.range : repeat, take;
- assert(a == b);
+ // bit array with 300 elements
+ auto a = BitArray(true.repeat.take(300).array);
+ size_t[] v = cast(size_t[]) a;
+ const blockSize = size_t.sizeof * 8;
+ assert(v.length == (a.length + blockSize - 1) / blockSize);
}
- /***************************************
- * Convert to $(D void[]).
- */
- void[] opCast(T : void[])() @nogc pure nothrow
+ // https://issues.dlang.org/show_bug.cgi?id=20606
+ @system unittest
{
- return cast(void[])_ptr[0 .. dim];
- }
+ import std.meta : AliasSeq;
- /***************************************
- * Convert to $(D size_t[]).
- */
- size_t[] opCast(T : size_t[])() @nogc pure nothrow
- {
- return _ptr[0 .. dim];
- }
+ static foreach (alias T; AliasSeq!(void, size_t))
+ {{
+ BitArray m;
+ T[] ma = cast(T[]) m;
- @system unittest
- {
- debug(bitarray) printf("BitArray.opCast unittest\n");
+ const BitArray c;
+ const(T)[] ca = cast(const T[]) c;
- static bool[] ba = [1,0,1,0,1];
+ immutable BitArray i;
+ immutable(T)[] ia = cast(immutable T[]) i;
- auto a = BitArray(ba);
- void[] v = cast(void[]) a;
+ // Cross-mutability
+ ca = cast(const T[]) m;
+ ca = cast(const T[]) i;
- assert(v.length == a.dim * size_t.sizeof);
+ // Invalid cast don't compile
+ static assert(!is(typeof(cast(T[]) c)));
+ static assert(!is(typeof(cast(T[]) i)));
+ static assert(!is(typeof(cast(immutable T[]) m)));
+ static assert(!is(typeof(cast(immutable T[]) c)));
+ }}
}
/***************************************
- * Support for unary operator ~ for $(D BitArray).
+ * Support for unary operator ~ for `BitArray`.
*/
- BitArray opCom() const pure nothrow
+ BitArray opUnary(string op)() const pure nothrow
+ if (op == "~")
{
auto dim = this.dim;
@@ -1404,11 +1942,10 @@ public:
return result;
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opCom unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
+ bool[] ba = [1,0,1,0,1];
auto a = BitArray(ba);
BitArray b = ~a;
@@ -1422,15 +1959,15 @@ public:
/***************************************
- * Support for binary bitwise operators for $(D BitArray).
+ * Support for binary bitwise operators for `BitArray`.
*/
BitArray opBinary(string op)(const BitArray e2) const pure nothrow
if (op == "-" || op == "&" || op == "|" || op == "^")
in
{
- assert(_len == e2.length);
+ assert(e2.length == _len, "e2 must have the same length as this");
}
- body
+ do
{
auto dim = this.dim;
@@ -1450,10 +1987,9 @@ public:
return result;
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opAnd unittest\n");
-
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1,1,0];
@@ -1469,12 +2005,11 @@ public:
assert(c[4] == 0);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opOr unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
- static bool[] bb = [1,0,1,1,0];
+ bool[] ba = [1,0,1,0,1];
+ bool[] bb = [1,0,1,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1488,12 +2023,11 @@ public:
assert(c[4] == 1);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opXor unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
- static bool[] bb = [1,0,1,1,0];
+ bool[] ba = [1,0,1,0,1];
+ bool[] bb = [1,0,1,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1507,12 +2041,11 @@ public:
assert(c[4] == 1);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opSub unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
- static bool[] bb = [1,0,1,1,0];
+ bool[] ba = [1,0,1,0,1];
+ bool[] bb = [1,0,1,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1528,15 +2061,15 @@ public:
/***************************************
- * Support for operator op= for $(D BitArray).
+ * Support for operator op= for `BitArray`.
*/
- BitArray opOpAssign(string op)(const BitArray e2) @nogc pure nothrow
+ BitArray opOpAssign(string op)(const BitArray e2) @nogc pure nothrow return scope
if (op == "-" || op == "&" || op == "|" || op == "^")
in
{
- assert(_len == e2.length);
+ assert(e2.length == _len, "e2 must have the same length as this");
}
- body
+ do
{
foreach (i; 0 .. fullWords)
{
@@ -1559,10 +2092,11 @@ public:
return this;
}
+ ///
@system unittest
{
- static bool[] ba = [1,0,1,0,1,1,0,1,0,1];
- static bool[] bb = [1,0,1,1,0];
+ bool[] ba = [1,0,1,0,1,1,0,1,0,1];
+ bool[] bb = [1,0,1,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
BitArray c = a;
@@ -1575,12 +2109,11 @@ public:
assert(a[9] == 1);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opAndAssign unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
- static bool[] bb = [1,0,1,1,0];
+ bool[] ba = [1,0,1,0,1];
+ bool[] bb = [1,0,1,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1593,12 +2126,11 @@ public:
assert(a[4] == 0);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opOrAssign unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
- static bool[] bb = [1,0,1,1,0];
+ bool[] ba = [1,0,1,0,1];
+ bool[] bb = [1,0,1,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1611,12 +2143,11 @@ public:
assert(a[4] == 1);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opXorAssign unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
- static bool[] bb = [1,0,1,1,0];
+ bool[] ba = [1,0,1,0,1];
+ bool[] bb = [1,0,1,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1629,12 +2160,11 @@ public:
assert(a[4] == 1);
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opSubAssign unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
- static bool[] bb = [1,0,1,1,0];
+ bool[] ba = [1,0,1,0,1];
+ bool[] bb = [1,0,1,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1648,25 +2178,24 @@ public:
}
/***************************************
- * Support for operator ~= for $(D BitArray).
+ * Support for operator ~= for `BitArray`.
* $(RED Warning: This will overwrite a bit in the final word
* of the current underlying data regardless of whether it is
* shared between BitArray objects. i.e. D dynamic array
* concatenation semantics are not followed)
*/
-
- BitArray opCatAssign(bool b) pure nothrow
+ BitArray opOpAssign(string op)(bool b) pure nothrow return scope
+ if (op == "~")
{
length = _len + 1;
this[_len - 1] = b;
return this;
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opCatAssign unittest\n");
-
- static bool[] ba = [1,0,1,0,1];
+ bool[] ba = [1,0,1,0,1];
auto a = BitArray(ba);
BitArray b;
@@ -1685,8 +2214,8 @@ public:
/***************************************
* ditto
*/
-
- BitArray opCatAssign(BitArray b) pure nothrow
+ BitArray opOpAssign(string op)(BitArray b) pure nothrow return scope
+ if (op == "~")
{
auto istart = _len;
length = _len + b.length;
@@ -1695,12 +2224,11 @@ public:
return this;
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opCatAssign unittest\n");
-
- static bool[] ba = [1,0];
- static bool[] bb = [0,1,0];
+ bool[] ba = [1,0];
+ bool[] bb = [0,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1718,9 +2246,10 @@ public:
}
/***************************************
- * Support for binary operator ~ for $(D BitArray).
+ * Support for binary operator ~ for `BitArray`.
*/
- BitArray opCat(bool b) const pure nothrow
+ BitArray opBinary(string op)(bool b) const pure nothrow
+ if (op == "~")
{
BitArray r;
@@ -1731,7 +2260,8 @@ public:
}
/** ditto */
- BitArray opCat_r(bool b) const pure nothrow
+ BitArray opBinaryRight(string op)(bool b) const pure nothrow
+ if (op == "~")
{
BitArray r;
@@ -1743,7 +2273,8 @@ public:
}
/** ditto */
- BitArray opCat(BitArray b) const pure nothrow
+ BitArray opBinary(string op)(BitArray b) const pure nothrow
+ if (op == "~")
{
BitArray r;
@@ -1752,12 +2283,11 @@ public:
return r;
}
+ ///
@system unittest
{
- debug(bitarray) printf("BitArray.opCat unittest\n");
-
- static bool[] ba = [1,0];
- static bool[] bb = [0,1,0];
+ bool[] ba = [1,0];
+ bool[] bb = [0,1,0];
auto a = BitArray(ba);
auto b = BitArray(bb);
@@ -1790,10 +2320,12 @@ public:
pure @safe nothrow @nogc
in
{
- assert(nbits < bitsPerSizeT);
+ assert(nbits < bitsPerSizeT, "nbits must be less than bitsPerSizeT");
}
- body
+ do
{
+ if (nbits == 0)
+ return lower;
return (upper << (bitsPerSizeT - nbits)) | (lower >> nbits);
}
@@ -1825,10 +2357,12 @@ public:
pure @safe nothrow @nogc
in
{
- assert(nbits < bitsPerSizeT);
+ assert(nbits < bitsPerSizeT, "nbits must be less than bitsPerSizeT");
}
- body
+ do
{
+ if (nbits == 0)
+ return upper;
return (upper << nbits) | (lower >> (bitsPerSizeT - nbits));
}
@@ -1853,7 +2387,7 @@ public:
}
/**
- * Operator $(D <<=) support.
+ * Operator `<<=` support.
*
* Shifts all the bits in the array to the left by the given number of
* bits. The leftmost bits are dropped, and 0's are appended to the end
@@ -1887,7 +2421,7 @@ public:
}
/**
- * Operator $(D >>=) support.
+ * Operator `>>=` support.
*
* Shifts all the bits in the array to the right by the given number of
* bits. The rightmost bits are dropped, and 0's are inserted at the back
@@ -1916,8 +2450,14 @@ public:
// end of the array.
if (wordsToShift < dim)
{
- _ptr[dim - wordsToShift - 1] = rollRight(0, _ptr[dim - 1] & endMask,
- bitsToShift);
+ if (bitsToShift == 0)
+ _ptr[dim - wordsToShift - 1] = _ptr[dim - 1];
+ else
+ {
+ // Special case: if endBits == 0, then also endMask == 0.
+ size_t lastWord = (endBits ? (_ptr[fullWords] & endMask) : _ptr[fullWords - 1]);
+ _ptr[dim - wordsToShift - 1] = rollRight(0, lastWord, bitsToShift);
+ }
}
import std.algorithm.comparison : min;
@@ -1927,6 +2467,57 @@ public:
}
}
+ // https://issues.dlang.org/show_bug.cgi?id=17467
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ bool[] buf = new bool[64*3];
+ buf[0 .. 64] = true;
+ BitArray b = BitArray(buf);
+ assert(equal(b.bitsSet, iota(0, 64)));
+ b <<= 64;
+ assert(equal(b.bitsSet, iota(64, 128)));
+
+ buf = new bool[64*3];
+ buf[64*2 .. 64*3] = true;
+ b = BitArray(buf);
+ assert(equal(b.bitsSet, iota(64*2, 64*3)));
+ b >>= 64;
+ assert(equal(b.bitsSet, iota(64, 128)));
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=18134
+ // shifting right when length is a multiple of 8 * size_t.sizeof.
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.array : array;
+ import std.range : repeat, iota;
+
+ immutable r = size_t.sizeof * 8;
+
+ BitArray a = true.repeat(r / 2).array;
+ a >>= 0;
+ assert(a.bitsSet.equal(iota(0, r / 2)));
+ a >>= 1;
+ assert(a.bitsSet.equal(iota(0, r / 2 - 1)));
+
+ BitArray b = true.repeat(r).array;
+ b >>= 0;
+ assert(b.bitsSet.equal(iota(0, r)));
+ b >>= 1;
+ assert(b.bitsSet.equal(iota(0, r - 1)));
+
+ BitArray c = true.repeat(2 * r).array;
+ c >>= 0;
+ assert(c.bitsSet.equal(iota(0, 2 * r)));
+ c >>= 10;
+ assert(c.bitsSet.equal(iota(0, 2 * r - 10)));
+ }
+
+ ///
@system unittest
{
import std.format : format;
@@ -2017,27 +2608,33 @@ public:
* $(LI $(B %s) which prints the bits as an array, and)
* $(LI $(B %b) which prints the bits as 8-bit byte packets)
* separated with an underscore.
+ *
+ * Params:
+ * sink = A `char` accepting
+ * $(REF_ALTTEXT output range, isOutputRange, std, range, primitives).
+ * fmt = A $(REF FormatSpec, std,format) which controls how the data
+ * is displayed.
*/
- void toString(scope void delegate(const(char)[]) sink,
- FormatSpec!char fmt) const
+ void toString(W)(ref W sink, scope const ref FormatSpec!char fmt) const
+ if (isOutputRange!(W, char))
{
- switch (fmt.spec)
+ const spec = fmt.spec;
+ switch (spec)
{
case 'b':
return formatBitString(sink);
case 's':
return formatBitArray(sink);
default:
- throw new Exception("Unknown format specifier: %" ~ fmt.spec);
+ throw new Exception("Unknown format specifier: %" ~ spec);
}
}
///
- @system unittest
+ @system pure unittest
{
import std.format : format;
- debug(bitarray) printf("BitArray.toString unittest\n");
auto b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
auto s1 = format("%s", b);
@@ -2053,12 +2650,16 @@ public:
@property auto bitsSet() const nothrow
{
import std.algorithm.iteration : filter, map, joiner;
- import std.range : iota;
-
- return iota(dim).
- filter!(i => _ptr[i])().
- map!(i => BitsSet!size_t(_ptr[i], i * bitsPerSizeT))().
- joiner();
+ import std.range : iota, chain;
+
+ return chain(
+ iota(fullWords)
+ .filter!(i => _ptr[i])()
+ .map!(i => BitsSet!size_t(_ptr[i], i * bitsPerSizeT))()
+ .joiner(),
+ iota(fullWords * bitsPerSizeT, _len)
+ .filter!(i => this[i])()
+ );
}
///
@@ -2082,7 +2683,6 @@ public:
import std.algorithm.comparison : equal;
import std.range : iota;
- debug(bitarray) printf("BitArray.bitsSet unittest\n");
BitArray b;
enum wordBits = size_t.sizeof * 8;
b = BitArray([size_t.max], 0);
@@ -2099,7 +2699,17 @@ public:
assert(b.bitsSet.equal(iota(wordBits * 2)));
}
- private void formatBitString(scope void delegate(const(char)[]) sink) const
+ // https://issues.dlang.org/show_bug.cgi?id=20241
+ @system unittest
+ {
+ BitArray ba;
+ ba.length = 2;
+ ba[1] = 1;
+ ba.length = 1;
+ assert(ba.bitsSet.empty);
+ }
+
+ private void formatBitString(Writer)(auto ref Writer sink) const
{
if (!length)
return;
@@ -2107,40 +2717,101 @@ public:
auto leftover = _len % 8;
foreach (idx; 0 .. leftover)
{
- char[1] res = cast(char)(this[idx] + '0');
- sink.put(res[]);
+ put(sink, cast(char)(this[idx] + '0'));
}
if (leftover && _len > 8)
- sink.put("_");
+ put(sink, "_");
size_t count;
foreach (idx; leftover .. _len)
{
- char[1] res = cast(char)(this[idx] + '0');
- sink.put(res[]);
+ put(sink, cast(char)(this[idx] + '0'));
if (++count == 8 && idx != _len - 1)
{
- sink.put("_");
+ put(sink, "_");
count = 0;
}
}
}
- private void formatBitArray(scope void delegate(const(char)[]) sink) const
+ private void formatBitArray(Writer)(auto ref Writer sink) const
{
- sink("[");
+ put(sink, "[");
foreach (idx; 0 .. _len)
{
- char[1] res = cast(char)(this[idx] + '0');
- sink(res[]);
- if (idx+1 < _len)
- sink(", ");
+ put(sink, cast(char)(this[idx] + '0'));
+ if (idx + 1 < _len)
+ put(sink, ", ");
}
- sink("]");
+ put(sink, "]");
}
+
+ // https://issues.dlang.org/show_bug.cgi?id=20639
+ // Separate @nogc test because public tests use array literals
+ // (and workarounds needlessly uglify those examples)
+ @system @nogc unittest
+ {
+ size_t[2] buffer;
+ BitArray b = BitArray(buffer[], buffer.sizeof * 8);
+
+ b[] = true;
+ b[0 .. 1] = true;
+ b.flip();
+ b.flip(1);
+ cast(void) b.count();
+ }
+}
+
+/// Slicing & bitsSet
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ bool[] buf = new bool[64 * 3];
+ buf[0 .. 64] = true;
+ BitArray b = BitArray(buf);
+ assert(b.bitsSet.equal(iota(0, 64)));
+ b <<= 64;
+ assert(b.bitsSet.equal(iota(64, 128)));
+}
+
+/// Concatenation and appending
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto b = BitArray([1, 0]);
+ b ~= true;
+ assert(b[2] == 1);
+ b ~= BitArray([0, 1]);
+ auto c = BitArray([1, 0, 1, 0, 1]);
+ assert(b == c);
+ assert(b.bitsSet.equal([0, 2, 4]));
+}
+
+/// Bit flipping
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto b = BitArray([1, 1, 0, 1]);
+ b &= BitArray([0, 1, 1, 0]);
+ assert(b.bitsSet.equal([1]));
+ b.flip;
+ assert(b.bitsSet.equal([0, 2, 3]));
+}
+
+/// String format of bitarrays
+@system unittest
+{
+ import std.format : format;
+ auto b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+ assert(format("%b", b) == "1_00001111_00001111");
}
+///
@system unittest
{
import std.format : format;
@@ -2173,51 +2844,61 @@ public:
assert(format("%b", b) == "1_00001111_00001111");
}
+@system unittest
+{
+ BitArray a;
+ a.length = 5;
+ foreach (ref bool b; a)
+ {
+ assert(b == 0);
+ b = 1;
+ }
+ foreach (bool b; a)
+ assert(b == 1);
+}
+
/++
Swaps the endianness of the given integral value or character.
+/
-T swapEndian(T)(T val) @safe pure nothrow @nogc
+T swapEndian(T)(const T val) @safe pure nothrow @nogc
if (isIntegral!T || isSomeChar!T || isBoolean!T)
{
+ import core.bitop : bswap, byteswap;
static if (val.sizeof == 1)
return val;
- else static if (isUnsigned!T)
- return swapEndianImpl(val);
- else static if (isIntegral!T)
- return cast(T) swapEndianImpl(cast(Unsigned!T) val);
- else static if (is(Unqual!T == wchar))
- return cast(T) swapEndian(cast(ushort) val);
- else static if (is(Unqual!T == dchar))
- return cast(T) swapEndian(cast(uint) val);
+ else static if (T.sizeof == 2)
+ return cast(T) byteswap(cast(ushort) val);
+ else static if (T.sizeof == 4)
+ return cast(T) bswap(cast(uint) val);
+ else static if (T.sizeof == 8)
+ return cast(T) bswap(cast(ulong) val);
else
static assert(0, T.stringof ~ " unsupported by swapEndian.");
}
-private ushort swapEndianImpl(ushort val) @safe pure nothrow @nogc
+///
+@safe unittest
{
- return ((val & 0xff00U) >> 8) |
- ((val & 0x00ffU) << 8);
-}
+ assert(42.swapEndian == 704643072);
+ assert(42.swapEndian.swapEndian == 42); // reflexive
+ assert(1.swapEndian == 16777216);
-private uint swapEndianImpl(uint val) @trusted pure nothrow @nogc
-{
- import core.bitop : bswap;
- return bswap(val);
-}
+ assert(true.swapEndian == true);
+ assert(byte(10).swapEndian == 10);
+ assert(char(10).swapEndian == 10);
-private ulong swapEndianImpl(ulong val) @trusted pure nothrow @nogc
-{
- import core.bitop : bswap;
- immutable ulong res = bswap(cast(uint) val);
- return res << 32 | bswap(cast(uint)(val >> 32));
+ assert(ushort(10).swapEndian == 2560);
+ assert(long(10).swapEndian == 720575940379279360);
+ assert(ulong(10).swapEndian == 720575940379279360);
}
@safe unittest
{
import std.meta;
- foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, char, wchar, dchar))
- {
- scope(failure) writefln("Failed type: %s", T.stringof);
+ import std.stdio;
+ static foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, char, wchar, dchar))
+ {{
+ scope(failure) writeln("Failed type: ", T.stringof);
T val;
const T cval;
immutable T ival;
@@ -2228,6 +2909,9 @@ private ulong swapEndianImpl(ulong val) @trusted pure nothrow @nogc
assert(swapEndian(swapEndian(T.min)) == T.min);
assert(swapEndian(swapEndian(T.max)) == T.max);
+ // Check CTFE compiles.
+ static assert(swapEndian(swapEndian(T(1))) is T(1));
+
foreach (i; 2 .. 10)
{
immutable T maxI = cast(T)(T.max / i);
@@ -2242,7 +2926,7 @@ private ulong swapEndianImpl(ulong val) @trusted pure nothrow @nogc
static if (isSigned!T)
assert(swapEndian(swapEndian(cast(T) 0)) == 0);
- // used to trigger BUG6354
+ // used to trigger https://issues.dlang.org/show_bug.cgi?id=6354
static if (T.sizeof > 1 && isUnsigned!T)
{
T left = 0xffU;
@@ -2257,7 +2941,7 @@ private ulong swapEndianImpl(ulong val) @trusted pure nothrow @nogc
right <<= 8;
}
}
- }
+ }}
}
@@ -2267,31 +2951,72 @@ if (canSwapEndianness!T)
Unqual!T value;
ubyte[T.sizeof] array;
- static if (is(FloatingPointTypeOf!T == float))
+ static if (is(FloatingPointTypeOf!(Unqual!T) == float))
uint intValue;
- else static if (is(FloatingPointTypeOf!T == double))
+ else static if (is(FloatingPointTypeOf!(Unqual!T) == double))
ulong intValue;
}
+// Can't use EndianSwapper union during CTFE.
+private auto ctfeRead(T)(const ubyte[T.sizeof] array)
+if (__traits(isIntegral, T))
+{
+ Unqual!T result;
+ version (LittleEndian)
+ foreach_reverse (b; array)
+ result = cast(Unqual!T) ((result << 8) | b);
+ else
+ foreach (b; array)
+ result = cast(Unqual!T) ((result << 8) | b);
+ return cast(T) result;
+}
+
+// Can't use EndianSwapper union during CTFE.
+private auto ctfeBytes(T)(const T value)
+if (__traits(isIntegral, T))
+{
+ ubyte[T.sizeof] result;
+ Unqual!T tmp = value;
+ version (LittleEndian)
+ {
+ foreach (i; 0 .. T.sizeof)
+ {
+ result[i] = cast(ubyte) tmp;
+ tmp = cast(Unqual!T) (tmp >>> 8);
+ }
+ }
+ else
+ {
+ foreach_reverse (i; 0 .. T.sizeof)
+ {
+ result[i] = cast(ubyte) tmp;
+ tmp = cast(Unqual!T) (tmp >>> 8);
+ }
+ }
+ return result;
+}
/++
Converts the given value from the native endianness to big endian and
- returns it as a $(D ubyte[n]) where $(D n) is the size of the given type.
+ returns it as a `ubyte[n]` where `n` is the size of the given type.
- Returning a $(D ubyte[n]) helps prevent accidentally using a swapped value
+ Returning a `ubyte[n]` helps prevent accidentally using a swapped value
as a regular one (and in the case of floating point values, it's necessary,
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
- $(D real) is not supported, because its size is implementation-dependent
+ `real` is not supported, because its size is implementation-dependent
and therefore could vary from machine to machine (which could make it
unusable if you tried to transfer it to another machine).
+/
-auto nativeToBigEndian(T)(T val) @safe pure nothrow @nogc
+auto nativeToBigEndian(T)(const T val) @safe pure nothrow @nogc
if (canSwapEndianness!T)
{
- return nativeToBigEndianImpl(val);
+ version (LittleEndian)
+ return nativeToEndianImpl!true(val);
+ else
+ return nativeToEndianImpl!false(val);
}
///
@@ -2301,37 +3026,48 @@ if (canSwapEndianness!T)
ubyte[4] swappedI = nativeToBigEndian(i);
assert(i == bigEndianToNative!int(swappedI));
+ float f = 123.45f;
+ ubyte[4] swappedF = nativeToBigEndian(f);
+ assert(f == bigEndianToNative!float(swappedF));
+
+ const float cf = 123.45f;
+ ubyte[4] swappedCF = nativeToBigEndian(cf);
+ assert(cf == bigEndianToNative!float(swappedCF));
+
double d = 123.45;
ubyte[8] swappedD = nativeToBigEndian(d);
assert(d == bigEndianToNative!double(swappedD));
-}
-private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc
-if (isIntegral!T || isSomeChar!T || isBoolean!T)
-{
- EndianSwapper!T es = void;
-
- version (LittleEndian)
- es.value = swapEndian(val);
- else
- es.value = val;
-
- return es.array;
+ const double cd = 123.45;
+ ubyte[8] swappedCD = nativeToBigEndian(cd);
+ assert(cd == bigEndianToNative!double(swappedCD));
}
-private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc
-if (isFloatOrDouble!T)
+private auto nativeToEndianImpl(bool swap, T)(const T val) @safe pure nothrow @nogc
+if (__traits(isIntegral, T))
{
- version (LittleEndian)
- return floatEndianImpl!(T, true)(val);
+ if (!__ctfe)
+ {
+ static if (swap)
+ return EndianSwapper!T(swapEndian(val)).array;
+ else
+ return EndianSwapper!T(val).array;
+ }
else
- return floatEndianImpl!(T, false)(val);
+ {
+ // Can't use EndianSwapper in CTFE.
+ static if (swap)
+ return ctfeBytes(swapEndian(val));
+ else
+ return ctfeBytes(val);
+ }
}
@safe unittest
{
import std.meta;
- foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ import std.stdio;
+ static foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong,
char, wchar, dchar
/* The trouble here is with floats and doubles being compared against nan
* using a bit compare. There are two kinds of nans, quiet and signaling.
@@ -2343,8 +3079,8 @@ if (isFloatOrDouble!T)
* I cannot think of a fix for this that makes consistent sense.
*/
/*,float, double*/))
- {
- scope(failure) writefln("Failed type: %s", T.stringof);
+ {{
+ scope(failure) writeln("Failed type: ", T.stringof);
T val;
const T cval;
immutable T ival;
@@ -2356,6 +3092,9 @@ if (isFloatOrDouble!T)
assert(bigEndianToNative!T(nativeToBigEndian(T.min)) == T.min);
assert(bigEndianToNative!T(nativeToBigEndian(T.max)) == T.max);
+ //Check CTFE compiles.
+ static assert(bigEndianToNative!T(nativeToBigEndian(T(1))) is T(1));
+
static if (isSigned!T)
assert(bigEndianToNative!T(nativeToBigEndian(cast(T) 0)) == 0);
@@ -2394,18 +3133,18 @@ if (isFloatOrDouble!T)
assert(nativeToBigEndian(T.min) == nativeToLittleEndian(T.min));
else
assert(nativeToBigEndian(T.min) != nativeToLittleEndian(T.min));
- }
+ }}
}
/++
Converts the given value from big endian to the native endianness and
- returns it. The value is given as a $(D ubyte[n]) where $(D n) is the size
+ returns it. The value is given as a `ubyte[n]` where `n` is the size
of the target type. You must give the target type as a template argument,
because there are multiple types with the same size and so the type of the
argument is not enough to determine the return type.
- Taking a $(D ubyte[n]) helps prevent accidentally using a swapped value
+ Taking a `ubyte[n]` helps prevent accidentally using a swapped value
as a regular one (and in the case of floating point values, it's necessary,
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
@@ -2413,7 +3152,10 @@ if (isFloatOrDouble!T)
T bigEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
if (canSwapEndianness!T && n == T.sizeof)
{
- return bigEndianToNativeImpl!(T, n)(val);
+ version (LittleEndian)
+ return endianToNativeImpl!(true, T, n)(val);
+ else
+ return endianToNativeImpl!(false, T, n)(val);
}
///
@@ -2428,44 +3170,22 @@ if (canSwapEndianness!T && n == T.sizeof)
assert(c == bigEndianToNative!dchar(swappedC));
}
-private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
-if ((isIntegral!T || isSomeChar!T || isBoolean!T) &&
- n == T.sizeof)
-{
- EndianSwapper!T es = void;
- es.array = val;
-
- version (LittleEndian)
- immutable retval = swapEndian(es.value);
- else
- immutable retval = es.value;
-
- return retval;
-}
-
-private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
-if (isFloatOrDouble!T && n == T.sizeof)
-{
- version (LittleEndian)
- return cast(T) floatEndianImpl!(n, true)(val);
- else
- return cast(T) floatEndianImpl!(n, false)(val);
-}
-
-
/++
Converts the given value from the native endianness to little endian and
- returns it as a $(D ubyte[n]) where $(D n) is the size of the given type.
+ returns it as a `ubyte[n]` where `n` is the size of the given type.
- Returning a $(D ubyte[n]) helps prevent accidentally using a swapped value
+ Returning a `ubyte[n]` helps prevent accidentally using a swapped value
as a regular one (and in the case of floating point values, it's necessary,
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
+/
-auto nativeToLittleEndian(T)(T val) @safe pure nothrow @nogc
+auto nativeToLittleEndian(T)(const T val) @safe pure nothrow @nogc
if (canSwapEndianness!T)
{
- return nativeToLittleEndianImpl(val);
+ version (BigEndian)
+ return nativeToEndianImpl!true(val);
+ else
+ return nativeToEndianImpl!false(val);
}
///
@@ -2480,36 +3200,15 @@ if (canSwapEndianness!T)
assert(d == littleEndianToNative!double(swappedD));
}
-private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc
-if (isIntegral!T || isSomeChar!T || isBoolean!T)
-{
- EndianSwapper!T es = void;
-
- version (BigEndian)
- es.value = swapEndian(val);
- else
- es.value = val;
-
- return es.array;
-}
-
-private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc
-if (isFloatOrDouble!T)
-{
- version (BigEndian)
- return floatEndianImpl!(T, true)(val);
- else
- return floatEndianImpl!(T, false)(val);
-}
-
@safe unittest
{
import std.meta;
- foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ import std.stdio;
+ static foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong,
char, wchar, dchar/*,
float, double*/))
- {
- scope(failure) writefln("Failed type: %s", T.stringof);
+ {{
+ scope(failure) writeln("Failed type: ", T.stringof);
T val;
const T cval;
immutable T ival;
@@ -2521,6 +3220,9 @@ if (isFloatOrDouble!T)
assert(littleEndianToNative!T(nativeToLittleEndian(T.min)) == T.min);
assert(littleEndianToNative!T(nativeToLittleEndian(T.max)) == T.max);
+ //Check CTFE compiles.
+ static assert(littleEndianToNative!T(nativeToLittleEndian(T(1))) is T(1));
+
static if (isSigned!T)
assert(littleEndianToNative!T(nativeToLittleEndian(cast(T) 0)) == 0);
@@ -2537,30 +3239,33 @@ if (isFloatOrDouble!T)
assert(littleEndianToNative!T(nativeToLittleEndian(minI)) == minI);
}
}
- }
+ }}
}
/++
Converts the given value from little endian to the native endianness and
- returns it. The value is given as a $(D ubyte[n]) where $(D n) is the size
+ returns it. The value is given as a `ubyte[n]` where `n` is the size
of the target type. You must give the target type as a template argument,
because there are multiple types with the same size and so the type of the
argument is not enough to determine the return type.
- Taking a $(D ubyte[n]) helps prevent accidentally using a swapped value
+ Taking a `ubyte[n]` helps prevent accidentally using a swapped value
as a regular one (and in the case of floating point values, it's necessary,
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
- $(D real) is not supported, because its size is implementation-dependent
+ `real` is not supported, because its size is implementation-dependent
and therefore could vary from machine to machine (which could make it
unusable if you tried to transfer it to another machine).
+/
T littleEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
if (canSwapEndianness!T && n == T.sizeof)
{
- return littleEndianToNativeImpl!T(val);
+ version (BigEndian)
+ return endianToNativeImpl!(true, T, n)(val);
+ else
+ return endianToNativeImpl!(false, T, n)(val);
}
///
@@ -2575,67 +3280,80 @@ if (canSwapEndianness!T && n == T.sizeof)
assert(c == littleEndianToNative!dchar(swappedC));
}
-private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
-if ((isIntegral!T || isSomeChar!T || isBoolean!T) &&
- n == T.sizeof)
-{
- EndianSwapper!T es = void;
- es.array = val;
-
- version (BigEndian)
- immutable retval = swapEndian(es.value);
- else
- immutable retval = es.value;
-
- return retval;
-}
-
-private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
-if (((isFloatOrDouble!T) &&
- n == T.sizeof))
+private T endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @nogc nothrow pure @safe
+if (__traits(isIntegral, T) && n == T.sizeof)
{
- version (BigEndian)
- return floatEndianImpl!(n, true)(val);
+ if (!__ctfe)
+ {
+ EndianSwapper!T es = { array: val };
+ static if (swap)
+ return swapEndian(es.value);
+ else
+ return es.value;
+ }
else
- return floatEndianImpl!(n, false)(val);
+ {
+ static if (swap)
+ return swapEndian(ctfeRead!T(val));
+ else
+ return ctfeRead!T(val);
+ }
}
-private auto floatEndianImpl(T, bool swap)(T val) @safe pure nothrow @nogc
+private auto nativeToEndianImpl(bool swap, T)(const T val) @trusted pure nothrow @nogc
if (isFloatOrDouble!T)
{
- EndianSwapper!T es = void;
- es.value = val;
-
- static if (swap)
- es.intValue = swapEndian(es.intValue);
-
- return es.array;
+ if (!__ctfe)
+ {
+ EndianSwapper!T es = EndianSwapper!T(val);
+ static if (swap)
+ es.intValue = swapEndian(es.intValue);
+ return es.array;
+ }
+ else
+ {
+ static if (T.sizeof == 4)
+ uint intValue = *cast(const uint*) &val;
+ else static if (T.sizeof == 8)
+ ulong intValue = *cast(const ulong*) & val;
+ static if (swap)
+ intValue = swapEndian(intValue);
+ return ctfeBytes(intValue);
+ }
}
-private auto floatEndianImpl(size_t n, bool swap)(ubyte[n] val) @safe pure nothrow @nogc
-if (n == 4 || n == 8)
+private auto endianToNativeImpl(bool swap, T, size_t n)(ubyte[n] val) @trusted pure nothrow @nogc
+if (isFloatOrDouble!T && n == T.sizeof)
{
- static if (n == 4) EndianSwapper!float es = void;
- else static if (n == 8) EndianSwapper!double es = void;
-
- es.array = val;
-
- static if (swap)
- es.intValue = swapEndian(es.intValue);
-
- return es.value;
+ if (!__ctfe)
+ {
+ EndianSwapper!T es = { array: val };
+ static if (swap)
+ es.intValue = swapEndian(es.intValue);
+ return es.value;
+ }
+ else
+ {
+ static if (n == 4)
+ uint x = ctfeRead!uint(val);
+ else static if (n == 8)
+ ulong x = ctfeRead!ulong(val);
+ static if (swap)
+ x = swapEndian(x);
+ return *cast(T*) &x;
+ }
}
private template isFloatOrDouble(T)
{
enum isFloatOrDouble = isFloatingPoint!T &&
- !is(Unqual!(FloatingPointTypeOf!T) == real);
+ !is(immutable FloatingPointTypeOf!T == immutable real);
}
@safe unittest
{
import std.meta;
- foreach (T; AliasSeq!(float, double))
+ static foreach (T; AliasSeq!(float, double))
{
static assert(isFloatOrDouble!(T));
static assert(isFloatOrDouble!(const T));
@@ -2664,7 +3382,7 @@ private template canSwapEndianness(T)
@safe unittest
{
import std.meta;
- foreach (T; AliasSeq!(bool, ubyte, byte, ushort, short, uint, int, ulong,
+ static foreach (T; AliasSeq!(bool, ubyte, byte, ushort, short, uint, int, ulong,
long, char, wchar, dchar, float, double))
{
static assert(canSwapEndianness!(T));
@@ -2676,7 +3394,7 @@ private template canSwapEndianness(T)
}
//!
- foreach (T; AliasSeq!(real, string, wstring, dstring))
+ static foreach (T; AliasSeq!(real, string, wstring, dstring))
{
static assert(!canSwapEndianness!(T));
static assert(!canSwapEndianness!(const T));
@@ -2688,18 +3406,18 @@ private template canSwapEndianness(T)
}
/++
- Takes a range of $(D ubyte)s and converts the first $(D T.sizeof) bytes to
- $(D T). The value returned is converted from the given endianness to the
+ Takes a range of `ubyte`s and converts the first `T.sizeof` bytes to
+ `T`. The value returned is converted from the given endianness to the
native endianness. The range is not consumed.
Params:
- T = The integral type to convert the first $(D T.sizeof) bytes to.
+ T = The integral type to convert the first `T.sizeof` bytes to.
endianness = The endianness that the bytes are assumed to be in.
range = The range to read from.
index = The index to start reading from (instead of starting at the
front). If index is a pointer, then it is updated to the index
after the bytes read. The overloads with index are only
- available if $(D hasSlicing!R) is $(D true).
+ available if `hasSlicing!R` is `true`.
+/
T peek(T, Endian endianness = Endian.bigEndian, R)(R range)
@@ -2745,7 +3463,7 @@ if (canSwapEndianness!T &&
hasSlicing!R &&
is(ElementType!R : const ubyte))
{
- assert(index);
+ assert(index, "index must not point to null");
immutable begin = *index;
immutable end = begin + T.sizeof;
@@ -2781,6 +3499,17 @@ if (canSwapEndianness!T &&
assert(index == 7);
}
+///
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ ubyte[] buffer = [1, 5, 22, 9, 44, 255, 7];
+ auto range = filter!"true"(buffer);
+ assert(range.peek!uint() == 17110537);
+ assert(range.peek!ushort() == 261);
+ assert(range.peek!ubyte() == 1);
+}
+
@system unittest
{
{
@@ -2982,25 +3711,14 @@ if (canSwapEndianness!T &&
}
}
-@safe unittest
-{
- import std.algorithm.iteration : filter;
- ubyte[] buffer = [1, 5, 22, 9, 44, 255, 7];
- auto range = filter!"true"(buffer);
- assert(range.peek!uint() == 17110537);
- assert(range.peek!ushort() == 261);
- assert(range.peek!ubyte() == 1);
-}
-
-
/++
- Takes a range of $(D ubyte)s and converts the first $(D T.sizeof) bytes to
- $(D T). The value returned is converted from the given endianness to the
- native endianness. The $(D T.sizeof) bytes which are read are consumed from
+ Takes a range of `ubyte`s and converts the first `T.sizeof` bytes to
+ `T`. The value returned is converted from the given endianness to the
+ native endianness. The `T.sizeof` bytes which are read are consumed from
the range.
Params:
- T = The integral type to convert the first $(D T.sizeof) bytes to.
+ T = The integral type to convert the first `T.sizeof` bytes to.
endianness = The endianness that the bytes are assumed to be in.
range = The range to read from.
+/
@@ -3219,24 +3937,7 @@ if (canSwapEndianness!T && isInputRange!R && is(ElementType!R : const ubyte))
}
}
-@safe unittest
-{
- import std.algorithm.iteration : filter;
- ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
- auto range = filter!"true"(buffer);
- assert(walkLength(range) == 7);
-
- assert(range.read!ushort() == 261);
- assert(walkLength(range) == 5);
-
- assert(range.read!uint() == 369700095);
- assert(walkLength(range) == 1);
-
- assert(range.read!ubyte() == 8);
- assert(range.empty);
-}
-
-// issue 17247
+// https://issues.dlang.org/show_bug.cgi?id=17247
@safe unittest
{
struct UbyteRange
@@ -3253,7 +3954,7 @@ if (canSwapEndianness!T && isInputRange!R && is(ElementType!R : const ubyte))
return UbyteRange(impl[start .. end]);
}
@property size_t length() { return impl.length; }
- size_t opDollar() { return impl.length; }
+ alias opDollar = length;
}
static assert(hasSlicing!UbyteRange);
@@ -3265,18 +3966,18 @@ if (canSwapEndianness!T && isInputRange!R && is(ElementType!R : const ubyte))
/++
Takes an integral value, converts it to the given endianness, and writes it
- to the given range of $(D ubyte)s as a sequence of $(D T.sizeof) $(D ubyte)s
- starting at index. $(D hasSlicing!R) must be $(D true).
+ to the given range of `ubyte`s as a sequence of `T.sizeof` `ubyte`s
+ starting at index. `hasSlicing!R` must be `true`.
Params:
- T = The integral type to convert the first $(D T.sizeof) bytes to.
+ T = The integral type to convert the first `T.sizeof` bytes to.
endianness = The endianness to _write the bytes in.
range = The range to _write to.
value = The value to _write.
index = The index to start writing to. If index is a pointer, then it
is updated to the index after the bytes read.
+/
-void write(T, Endian endianness = Endian.bigEndian, R)(R range, T value, size_t index)
+void write(T, Endian endianness = Endian.bigEndian, R)(R range, const T value, size_t index)
if (canSwapEndianness!T &&
isForwardRange!R &&
hasSlicing!R &&
@@ -3286,13 +3987,13 @@ if (canSwapEndianness!T &&
}
/++ Ditto +/
-void write(T, Endian endianness = Endian.bigEndian, R)(R range, T value, size_t* index)
+void write(T, Endian endianness = Endian.bigEndian, R)(R range, const T value, size_t* index)
if (canSwapEndianness!T &&
isForwardRange!R &&
hasSlicing!R &&
is(ElementType!R : ubyte))
{
- assert(index);
+ assert(index, "index must not point to null");
static if (endianness == Endian.bigEndian)
immutable bytes = nativeToBigEndian!T(value);
@@ -3308,319 +4009,328 @@ if (canSwapEndianness!T &&
///
@system unittest
{
- {
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
- buffer.write!uint(29110231u, 0);
- assert(buffer == [1, 188, 47, 215, 0, 0, 0, 0]);
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ buffer.write!uint(29110231u, 0);
+ assert(buffer == [1, 188, 47, 215, 0, 0, 0, 0]);
- buffer.write!ushort(927, 0);
- assert(buffer == [3, 159, 47, 215, 0, 0, 0, 0]);
+ buffer.write!ushort(927, 0);
+ assert(buffer == [3, 159, 47, 215, 0, 0, 0, 0]);
- buffer.write!ubyte(42, 0);
- assert(buffer == [42, 159, 47, 215, 0, 0, 0, 0]);
- }
+ buffer.write!ubyte(42, 0);
+ assert(buffer == [42, 159, 47, 215, 0, 0, 0, 0]);
+}
- {
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0];
- buffer.write!uint(142700095u, 2);
- assert(buffer == [0, 0, 8, 129, 110, 63, 0, 0, 0]);
+///
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0];
+ buffer.write!uint(142700095u, 2);
+ assert(buffer == [0, 0, 8, 129, 110, 63, 0, 0, 0]);
- buffer.write!ushort(19839, 2);
- assert(buffer == [0, 0, 77, 127, 110, 63, 0, 0, 0]);
+ buffer.write!ushort(19839, 2);
+ assert(buffer == [0, 0, 77, 127, 110, 63, 0, 0, 0]);
- buffer.write!ubyte(132, 2);
- assert(buffer == [0, 0, 132, 127, 110, 63, 0, 0, 0]);
- }
+ buffer.write!ubyte(132, 2);
+ assert(buffer == [0, 0, 132, 127, 110, 63, 0, 0, 0]);
+}
- {
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
- size_t index = 0;
- buffer.write!ushort(261, &index);
- assert(buffer == [1, 5, 0, 0, 0, 0, 0, 0]);
- assert(index == 2);
+///
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ size_t index = 0;
+ buffer.write!ushort(261, &index);
+ assert(buffer == [1, 5, 0, 0, 0, 0, 0, 0]);
+ assert(index == 2);
- buffer.write!uint(369700095u, &index);
- assert(buffer == [1, 5, 22, 9, 44, 255, 0, 0]);
- assert(index == 6);
+ buffer.write!uint(369700095u, &index);
+ assert(buffer == [1, 5, 22, 9, 44, 255, 0, 0]);
+ assert(index == 6);
- buffer.write!ubyte(8, &index);
- assert(buffer == [1, 5, 22, 9, 44, 255, 8, 0]);
- assert(index == 7);
- }
+ buffer.write!ubyte(8, &index);
+ assert(buffer == [1, 5, 22, 9, 44, 255, 8, 0]);
+ assert(index == 7);
}
+/// bool
@system unittest
{
- {
- //bool
- ubyte[] buffer = [0, 0];
+ ubyte[] buffer = [0, 0];
+ buffer.write!bool(false, 0);
+ assert(buffer == [0, 0]);
- buffer.write!bool(false, 0);
- assert(buffer == [0, 0]);
+ buffer.write!bool(true, 0);
+ assert(buffer == [1, 0]);
- buffer.write!bool(true, 0);
- assert(buffer == [1, 0]);
+ buffer.write!bool(true, 1);
+ assert(buffer == [1, 1]);
- buffer.write!bool(true, 1);
- assert(buffer == [1, 1]);
+ buffer.write!bool(false, 1);
+ assert(buffer == [1, 0]);
- buffer.write!bool(false, 1);
- assert(buffer == [1, 0]);
+ size_t index = 0;
+ buffer.write!bool(false, &index);
+ assert(buffer == [0, 0]);
+ assert(index == 1);
- size_t index = 0;
- buffer.write!bool(false, &index);
- assert(buffer == [0, 0]);
- assert(index == 1);
+ buffer.write!bool(true, &index);
+ assert(buffer == [0, 1]);
+ assert(index == 2);
+}
- buffer.write!bool(true, &index);
- assert(buffer == [0, 1]);
- assert(index == 2);
- }
+/// char(8-bit)
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0];
- {
- //char (8bit)
- ubyte[] buffer = [0, 0, 0];
+ buffer.write!char('a', 0);
+ assert(buffer == [97, 0, 0]);
- buffer.write!char('a', 0);
- assert(buffer == [97, 0, 0]);
+ buffer.write!char('b', 1);
+ assert(buffer == [97, 98, 0]);
- buffer.write!char('b', 1);
- assert(buffer == [97, 98, 0]);
+ size_t index = 0;
+ buffer.write!char('a', &index);
+ assert(buffer == [97, 98, 0]);
+ assert(index == 1);
- size_t index = 0;
- buffer.write!char('a', &index);
- assert(buffer == [97, 98, 0]);
- assert(index == 1);
+ buffer.write!char('b', &index);
+ assert(buffer == [97, 98, 0]);
+ assert(index == 2);
- buffer.write!char('b', &index);
- assert(buffer == [97, 98, 0]);
- assert(index == 2);
+ buffer.write!char('c', &index);
+ assert(buffer == [97, 98, 99]);
+ assert(index == 3);
+}
- buffer.write!char('c', &index);
- assert(buffer == [97, 98, 99]);
- assert(index == 3);
- }
+/// wchar (16bit - 2x ubyte)
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0];
- {
- //wchar (16bit - 2x ubyte)
- ubyte[] buffer = [0, 0, 0, 0];
+ buffer.write!wchar('Ä…', 0);
+ assert(buffer == [1, 5, 0, 0]);
- buffer.write!wchar('Ä…', 0);
- assert(buffer == [1, 5, 0, 0]);
+ buffer.write!wchar('â€', 2);
+ assert(buffer == [1, 5, 32, 29]);
- buffer.write!wchar('â€', 2);
- assert(buffer == [1, 5, 32, 29]);
+ size_t index = 0;
+ buffer.write!wchar('ć', &index);
+ assert(buffer == [1, 7, 32, 29]);
+ assert(index == 2);
- size_t index = 0;
- buffer.write!wchar('ć', &index);
- assert(buffer == [1, 7, 32, 29]);
- assert(index == 2);
+ buffer.write!wchar('Ä…', &index);
+ assert(buffer == [1, 7, 1, 5]);
+ assert(index == 4);
+}
- buffer.write!wchar('Ä…', &index);
- assert(buffer == [1, 7, 1, 5]);
- assert(index == 4);
- }
+/// dchar (32bit - 4x ubyte)
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
- {
- //dchar (32bit - 4x ubyte)
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ buffer.write!dchar('Ä…', 0);
+ assert(buffer == [0, 0, 1, 5, 0, 0, 0, 0]);
- buffer.write!dchar('Ä…', 0);
- assert(buffer == [0, 0, 1, 5, 0, 0, 0, 0]);
+ buffer.write!dchar('â€', 4);
+ assert(buffer == [0, 0, 1, 5, 0, 0, 32, 29]);
- buffer.write!dchar('â€', 4);
- assert(buffer == [0, 0, 1, 5, 0, 0, 32, 29]);
+ size_t index = 0;
+ buffer.write!dchar('ć', &index);
+ assert(buffer == [0, 0, 1, 7, 0, 0, 32, 29]);
+ assert(index == 4);
- size_t index = 0;
- buffer.write!dchar('ć', &index);
- assert(buffer == [0, 0, 1, 7, 0, 0, 32, 29]);
- assert(index == 4);
+ buffer.write!dchar('Ä…', &index);
+ assert(buffer == [0, 0, 1, 7, 0, 0, 1, 5]);
+ assert(index == 8);
+}
- buffer.write!dchar('Ä…', &index);
- assert(buffer == [0, 0, 1, 7, 0, 0, 1, 5]);
- assert(index == 8);
- }
+/// float (32bit - 4x ubyte)
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
- {
- //float (32bit - 4x ubyte)
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ buffer.write!float(32.0f, 0);
+ assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
- buffer.write!float(32.0f, 0);
- assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
+ buffer.write!float(25.0f, 4);
+ assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
- buffer.write!float(25.0f, 4);
- assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
+ size_t index = 0;
+ buffer.write!float(25.0f, &index);
+ assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
+ assert(index == 4);
- size_t index = 0;
- buffer.write!float(25.0f, &index);
- assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
- assert(index == 4);
+ buffer.write!float(32.0f, &index);
+ assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
+ assert(index == 8);
+}
- buffer.write!float(32.0f, &index);
- assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
- assert(index == 8);
- }
+/// double (64bit - 8x ubyte)
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
- {
- //double (64bit - 8x ubyte)
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ buffer.write!double(32.0, 0);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
- buffer.write!double(32.0, 0);
- assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+ buffer.write!double(25.0, 8);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
- buffer.write!double(25.0, 8);
- assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ size_t index = 0;
+ buffer.write!double(25.0, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ assert(index == 8);
- size_t index = 0;
- buffer.write!double(25.0, &index);
- assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
- assert(index == 8);
+ buffer.write!double(32.0, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+ assert(index == 16);
+}
- buffer.write!double(32.0, &index);
- assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
- assert(index == 16);
- }
+/// enum
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ enum Foo
{
- //enum
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ one = 10,
+ two = 20,
+ three = 30
+ }
- enum Foo
- {
- one = 10,
- two = 20,
- three = 30
- }
+ buffer.write!Foo(Foo.one, 0);
+ assert(buffer == [0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0]);
- buffer.write!Foo(Foo.one, 0);
- assert(buffer == [0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0]);
+ buffer.write!Foo(Foo.two, 4);
+ assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 0]);
- buffer.write!Foo(Foo.two, 4);
- assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 0]);
+ buffer.write!Foo(Foo.three, 8);
+ assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
- buffer.write!Foo(Foo.three, 8);
- assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
+ size_t index = 0;
+ buffer.write!Foo(Foo.three, &index);
+ assert(buffer == [0, 0, 0, 30, 0, 0, 0, 20, 0, 0, 0, 30]);
+ assert(index == 4);
- size_t index = 0;
- buffer.write!Foo(Foo.three, &index);
- assert(buffer == [0, 0, 0, 30, 0, 0, 0, 20, 0, 0, 0, 30]);
- assert(index == 4);
+ buffer.write!Foo(Foo.one, &index);
+ assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 30]);
+ assert(index == 8);
- buffer.write!Foo(Foo.one, &index);
- assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 30]);
- assert(index == 8);
+ buffer.write!Foo(Foo.two, &index);
+ assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 20]);
+ assert(index == 12);
+}
- buffer.write!Foo(Foo.two, &index);
- assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 20]);
- assert(index == 12);
- }
+// enum - bool
+@system unittest
+{
+ ubyte[] buffer = [0, 0];
+ enum Bool: bool
{
- //enum - bool
- ubyte[] buffer = [0, 0];
+ bfalse = false,
+ btrue = true,
+ }
- enum Bool: bool
- {
- bfalse = false,
- btrue = true,
- }
+ buffer.write!Bool(Bool.btrue, 0);
+ assert(buffer == [1, 0]);
- buffer.write!Bool(Bool.btrue, 0);
- assert(buffer == [1, 0]);
+ buffer.write!Bool(Bool.btrue, 1);
+ assert(buffer == [1, 1]);
- buffer.write!Bool(Bool.btrue, 1);
- assert(buffer == [1, 1]);
+ size_t index = 0;
+ buffer.write!Bool(Bool.bfalse, &index);
+ assert(buffer == [0, 1]);
+ assert(index == 1);
- size_t index = 0;
- buffer.write!Bool(Bool.bfalse, &index);
- assert(buffer == [0, 1]);
- assert(index == 1);
+ buffer.write!Bool(Bool.bfalse, &index);
+ assert(buffer == [0, 0]);
+ assert(index == 2);
+}
- buffer.write!Bool(Bool.bfalse, &index);
- assert(buffer == [0, 0]);
- assert(index == 2);
- }
+/// enum - float
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ enum Float: float
{
- //enum - float
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ one = 32.0f,
+ two = 25.0f
+ }
- enum Float: float
- {
- one = 32.0f,
- two = 25.0f
- }
+ buffer.write!Float(Float.one, 0);
+ assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
- buffer.write!Float(Float.one, 0);
- assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
+ buffer.write!Float(Float.two, 4);
+ assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
- buffer.write!Float(Float.two, 4);
- assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
+ size_t index = 0;
+ buffer.write!Float(Float.two, &index);
+ assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
+ assert(index == 4);
- size_t index = 0;
- buffer.write!Float(Float.two, &index);
- assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
- assert(index == 4);
+ buffer.write!Float(Float.one, &index);
+ assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
+ assert(index == 8);
+}
- buffer.write!Float(Float.one, &index);
- assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
- assert(index == 8);
- }
+/// enum - double
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ enum Double: double
{
- //enum - double
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ one = 32.0,
+ two = 25.0
+ }
- enum Double: double
- {
- one = 32.0,
- two = 25.0
- }
+ buffer.write!Double(Double.one, 0);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
- buffer.write!Double(Double.one, 0);
- assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+ buffer.write!Double(Double.two, 8);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
- buffer.write!Double(Double.two, 8);
- assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ size_t index = 0;
+ buffer.write!Double(Double.two, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ assert(index == 8);
- size_t index = 0;
- buffer.write!Double(Double.two, &index);
- assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
- assert(index == 8);
+ buffer.write!Double(Double.one, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+ assert(index == 16);
+}
- buffer.write!Double(Double.one, &index);
- assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
- assert(index == 16);
- }
+/// enum - real
+@system unittest
+{
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ enum Real: real
{
- //enum - real
- ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
-
- enum Real: real
- {
- one = 32.0,
- two = 25.0
- }
-
- static assert(!__traits(compiles, buffer.write!Real(Real.one)));
+ one = 32.0,
+ two = 25.0
}
+
+ static assert(!__traits(compiles, buffer.write!Real(Real.one)));
}
/++
Takes an integral value, converts it to the given endianness, and appends
- it to the given range of $(D ubyte)s (using $(D put)) as a sequence of
- $(D T.sizeof) $(D ubyte)s starting at index. $(D hasSlicing!R) must be
- $(D true).
+ it to the given range of `ubyte`s (using `put`) as a sequence of
+ `T.sizeof` `ubyte`s starting at index. `hasSlicing!R` must be
+ `true`.
Params:
- T = The integral type to convert the first $(D T.sizeof) bytes to.
+ T = The integral type to convert the first `T.sizeof` bytes to.
endianness = The endianness to write the bytes in.
range = The range to _append to.
value = The value to _append.
+/
-void append(T, Endian endianness = Endian.bigEndian, R)(R range, T value)
+void append(T, Endian endianness = Endian.bigEndian, R)(R range, const T value)
if (canSwapEndianness!T && isOutputRange!(R, ubyte))
{
static if (endianness == Endian.bigEndian)
@@ -3646,144 +4356,156 @@ if (canSwapEndianness!T && isOutputRange!(R, ubyte))
assert(buffer.data == [1, 5, 22, 9, 44, 255, 8]);
}
+/// bool
@safe unittest
{
- import std.array;
- {
- //bool
- auto buffer = appender!(const ubyte[])();
+ import std.array : appender;
+ auto buffer = appender!(const ubyte[])();
- buffer.append!bool(true);
- assert(buffer.data == [1]);
+ buffer.append!bool(true);
+ assert(buffer.data == [1]);
- buffer.append!bool(false);
- assert(buffer.data == [1, 0]);
- }
+ buffer.append!bool(false);
+ assert(buffer.data == [1, 0]);
+}
- {
- //char wchar dchar
- auto buffer = appender!(const ubyte[])();
+/// char wchar dchar
+@safe unittest
+{
+ import std.array : appender;
+ auto buffer = appender!(const ubyte[])();
- buffer.append!char('a');
- assert(buffer.data == [97]);
+ buffer.append!char('a');
+ assert(buffer.data == [97]);
- buffer.append!char('b');
- assert(buffer.data == [97, 98]);
+ buffer.append!char('b');
+ assert(buffer.data == [97, 98]);
- buffer.append!wchar('Ä…');
- assert(buffer.data == [97, 98, 1, 5]);
+ buffer.append!wchar('Ä…');
+ assert(buffer.data == [97, 98, 1, 5]);
- buffer.append!dchar('Ä…');
+ buffer.append!dchar('Ä…');
assert(buffer.data == [97, 98, 1, 5, 0, 0, 1, 5]);
- }
+}
- {
- //float double
- auto buffer = appender!(const ubyte[])();
+/// float double
+@safe unittest
+{
+ import std.array : appender;
+ auto buffer = appender!(const ubyte[])();
- buffer.append!float(32.0f);
- assert(buffer.data == [66, 0, 0, 0]);
+ buffer.append!float(32.0f);
+ assert(buffer.data == [66, 0, 0, 0]);
- buffer.append!double(32.0);
- assert(buffer.data == [66, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
- }
+ buffer.append!double(32.0);
+ assert(buffer.data == [66, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+}
+/// enum
+@safe unittest
+{
+ import std.array : appender;
+ auto buffer = appender!(const ubyte[])();
+
+ enum Foo
{
- //enum
- auto buffer = appender!(const ubyte[])();
+ one = 10,
+ two = 20,
+ three = 30
+ }
- enum Foo
- {
- one = 10,
- two = 20,
- three = 30
- }
+ buffer.append!Foo(Foo.one);
+ assert(buffer.data == [0, 0, 0, 10]);
- buffer.append!Foo(Foo.one);
- assert(buffer.data == [0, 0, 0, 10]);
+ buffer.append!Foo(Foo.two);
+ assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20]);
- buffer.append!Foo(Foo.two);
- assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20]);
+ buffer.append!Foo(Foo.three);
+ assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
+}
- buffer.append!Foo(Foo.three);
- assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
- }
+/// enum - bool
+@safe unittest
+{
+ import std.array : appender;
+ auto buffer = appender!(const ubyte[])();
+ enum Bool: bool
{
- //enum - bool
- auto buffer = appender!(const ubyte[])();
+ bfalse = false,
+ btrue = true,
+ }
- enum Bool: bool
- {
- bfalse = false,
- btrue = true,
- }
+ buffer.append!Bool(Bool.btrue);
+ assert(buffer.data == [1]);
- buffer.append!Bool(Bool.btrue);
- assert(buffer.data == [1]);
+ buffer.append!Bool(Bool.bfalse);
+ assert(buffer.data == [1, 0]);
- buffer.append!Bool(Bool.bfalse);
- assert(buffer.data == [1, 0]);
+ buffer.append!Bool(Bool.btrue);
+ assert(buffer.data == [1, 0, 1]);
+}
- buffer.append!Bool(Bool.btrue);
- assert(buffer.data == [1, 0, 1]);
- }
+/// enum - float
+@safe unittest
+{
+ import std.array : appender;
+ auto buffer = appender!(const ubyte[])();
+ enum Float: float
{
- //enum - float
- auto buffer = appender!(const ubyte[])();
+ one = 32.0f,
+ two = 25.0f
+ }
- enum Float: float
- {
- one = 32.0f,
- two = 25.0f
- }
+ buffer.append!Float(Float.one);
+ assert(buffer.data == [66, 0, 0, 0]);
- buffer.append!Float(Float.one);
- assert(buffer.data == [66, 0, 0, 0]);
+ buffer.append!Float(Float.two);
+ assert(buffer.data == [66, 0, 0, 0, 65, 200, 0, 0]);
+}
- buffer.append!Float(Float.two);
- assert(buffer.data == [66, 0, 0, 0, 65, 200, 0, 0]);
- }
+/// enum - double
+@safe unittest
+{
+ import std.array : appender;
+ auto buffer = appender!(const ubyte[])();
+ enum Double: double
{
- //enum - double
- auto buffer = appender!(const ubyte[])();
+ one = 32.0,
+ two = 25.0
+ }
- enum Double: double
- {
- one = 32.0,
- two = 25.0
- }
+ buffer.append!Double(Double.one);
+ assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0]);
- buffer.append!Double(Double.one);
- assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0]);
+ buffer.append!Double(Double.two);
+ assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+}
- buffer.append!Double(Double.two);
- assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
- }
+/// enum - real
+@safe unittest
+{
+ import std.array : appender;
+ auto buffer = appender!(const ubyte[])();
+ enum Real: real
{
- //enum - real
- auto buffer = appender!(const ubyte[])();
-
- enum Real: real
- {
- one = 32.0,
- two = 25.0
- }
-
- static assert(!__traits(compiles, buffer.append!Real(Real.one)));
+ one = 32.0,
+ two = 25.0
}
+
+ static assert(!__traits(compiles, buffer.append!Real(Real.one)));
}
@system unittest
{
import std.array;
import std.format : format;
- import std.meta;
- foreach (endianness; AliasSeq!(Endian.bigEndian, Endian.littleEndian))
- {
+ import std.meta : AliasSeq;
+ static foreach (endianness; [Endian.bigEndian, Endian.littleEndian])
+ {{
auto toWrite = appender!(ubyte[])();
alias Types = AliasSeq!(uint, int, long, ulong, short, ubyte, ushort, byte, uint);
ulong[] values = [42, -11, long.max, 1098911981329L, 16, 255, 19012, 2, 17];
@@ -3791,7 +4513,7 @@ if (canSwapEndianness!T && isOutputRange!(R, ubyte))
size_t index = 0;
size_t length = 0;
- foreach (T; Types)
+ static foreach (T; Types)
{
toWrite.append!(T, endianness)(cast(T) values[index++]);
length += T.sizeof;
@@ -3801,7 +4523,7 @@ if (canSwapEndianness!T && isOutputRange!(R, ubyte))
assert(toRead.length == length);
index = 0;
- foreach (T; Types)
+ static foreach (T; Types)
{
assert(toRead.peek!(T, endianness)() == values[index], format("Failed Index: %s", index));
assert(toRead.peek!(T, endianness)(0) == values[index], format("Failed Index: %s", index));
@@ -3814,34 +4536,27 @@ if (canSwapEndianness!T && isOutputRange!(R, ubyte))
++index;
}
assert(toRead.empty);
- }
+ }}
}
/**
-Counts the number of set bits in the binary representation of $(D value).
+Counts the number of set bits in the binary representation of `value`.
For signed integers, the sign bit is included in the count.
*/
-private uint countBitsSet(T)(T value) @nogc pure nothrow
+private uint countBitsSet(T)(const T value) @nogc pure nothrow
if (isIntegral!T)
{
- // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
static if (T.sizeof == 8)
{
- T c = value - ((value >> 1) & 0x55555555_55555555);
- c = ((c >> 2) & 0x33333333_33333333) + (c & 0x33333333_33333333);
- c = ((c >> 4) + c) & 0x0F0F0F0F_0F0F0F0F;
- c = ((c >> 8) + c) & 0x00FF00FF_00FF00FF;
- c = ((c >> 16) + c) & 0x0000FFFF_0000FFFF;
- c = ((c >> 32) + c) & 0x00000000_FFFFFFFF;
+ import core.bitop : popcnt;
+ const c = popcnt(cast(ulong) value);
}
else static if (T.sizeof == 4)
{
- T c = value - ((value >> 1) & 0x55555555);
- c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
- c = ((c >> 4) + c) & 0x0F0F0F0F;
- c = ((c >> 8) + c) & 0x00FF00FF;
- c = ((c >> 16) + c) & 0x0000FFFF;
+ import core.bitop : popcnt;
+ const c = popcnt(cast(uint) value);
}
+ // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
else static if (T.sizeof == 2)
{
uint c = value - ((value >> 1) & 0x5555);
@@ -3873,7 +4588,7 @@ if (isIntegral!T)
@safe unittest
{
import std.meta;
- foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
{
assert(countBitsSet(cast(T) 0) == 0);
assert(countBitsSet(cast(T) 1) == 1);
@@ -3891,6 +4606,8 @@ if (isIntegral!T)
{
assert(countBitsSet(T.max) == 8 * T.sizeof);
}
+ // Check CTFE compiles.
+ static assert(countBitsSet(cast(T) 1) == 1);
}
assert(countBitsSet(1_000_000) == 7);
foreach (i; 0 .. 63)
@@ -3916,7 +4633,7 @@ private struct BitsSet(T)
_index = startIndex + trailingZerosCount;
}
- @property size_t front()
+ @property size_t front() const
{
return _index;
}
@@ -3941,12 +4658,12 @@ private struct BitsSet(T)
_index += trailingZerosCount + 1;
}
- @property auto save()
+ @property BitsSet save() const
{
return this;
}
- @property size_t length()
+ @property size_t length() const
{
return countBitsSet(_value);
}
@@ -3956,11 +4673,11 @@ private struct BitsSet(T)
}
/**
-Range that iterates the indices of the set bits in $(D value).
+Range that iterates the indices of the set bits in `value`.
Index 0 corresponds to the least significant bit.
For signed integers, the highest index corresponds to the sign bit.
*/
-auto bitsSet(T)(T value) @nogc pure nothrow
+auto bitsSet(T)(const T value) @nogc pure nothrow
if (isIntegral!T)
{
return BitsSet!T(value);
@@ -3984,7 +4701,7 @@ if (isIntegral!T)
import std.range : iota;
import std.meta;
- foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
{
assert(bitsSet(cast(T) 0).empty);
assert(bitsSet(cast(T) 1).equal([0]));
diff --git a/libphobos/src/std/compiler.d b/libphobos/src/std/compiler.d
index cb038f9dbfe..2f983c58fe0 100644
--- a/libphobos/src/std/compiler.d
+++ b/libphobos/src/std/compiler.d
@@ -3,12 +3,12 @@
/**
* Identify the compiler used and its various features.
*
- * Copyright: Copyright Digital Mars 2000 - 2011.
+ * Copyright: Copyright The D Language Foundation 2000 - 2011.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright), Alex Rønne Petersen
- * Source: $(PHOBOSSRC std/_compiler.d)
+ * Source: $(PHOBOSSRC std/compiler.d)
*/
-/* Copyright Digital Mars 2000 - 2011.
+/* Copyright The D Language Foundation 2000 - 2011.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
diff --git a/libphobos/src/std/complex.d b/libphobos/src/std/complex.d
index 8e488db4162..756d1ca94bb 100644
--- a/libphobos/src/std/complex.d
+++ b/libphobos/src/std/complex.d
@@ -1,23 +1,32 @@
// Written in the D programming language.
/** This module contains the $(LREF Complex) type, which is used to represent
- _complex numbers, along with related mathematical operations and functions.
+ complex numbers, along with related mathematical operations and functions.
$(LREF Complex) will eventually
$(DDLINK deprecate, Deprecated Features, replace)
- the built-in types $(D cfloat), $(D cdouble), $(D creal), $(D ifloat),
- $(D idouble), and $(D ireal).
+ the built-in types `cfloat`, `cdouble`, `creal`, `ifloat`,
+ `idouble`, and `ireal`.
+
+ Macros:
+ TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
+ <caption>Special Values</caption>
+ $0</table>
+ PLUSMN = &plusmn;
+ NAN = $(RED NAN)
+ INFIN = &infin;
+ PI = &pi;
Authors: Lars Tandle Kyllingstad, Don Clugston
Copyright: Copyright (c) 2010, Lars T. Kyllingstad.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0)
- Source: $(PHOBOSSRC std/_complex.d)
+ Source: $(PHOBOSSRC std/complex.d)
*/
module std.complex;
import std.traits;
-/** Helper function that returns a _complex number with the specified
+/** Helper function that returns a complex number with the specified
real and imaginary parts.
Params:
@@ -28,13 +37,13 @@ import std.traits;
im = (optional) imaginary part of complex number, 0 if omitted.
Returns:
- $(D Complex) instance with real and imaginary parts set
- to the values provided as input. If neither $(D re) nor
- $(D im) are floating-point numbers, the return type will
- be $(D Complex!double). Otherwise, the return type is
+ `Complex` instance with real and imaginary parts set
+ to the values provided as input. If neither `re` nor
+ `im` are floating-point numbers, the return type will
+ be `Complex!double`. Otherwise, the return type is
deduced using $(D std.traits.CommonType!(R, I)).
*/
-auto complex(R)(R re) @safe pure nothrow @nogc
+auto complex(R)(const R re) @safe pure nothrow @nogc
if (is(R : double))
{
static if (isFloatingPoint!R)
@@ -44,7 +53,7 @@ if (is(R : double))
}
/// ditto
-auto complex(R, I)(R re, I im) @safe pure nothrow @nogc
+auto complex(R, I)(const R re, const I im) @safe pure nothrow @nogc
if (is(R : double) && is(I : double))
{
static if (isFloatingPoint!R || isFloatingPoint!I)
@@ -93,13 +102,13 @@ if (is(R : double) && is(I : double))
}
-/** A complex number parametrised by a type $(D T), which must be either
- $(D float), $(D double) or $(D real).
+/** A complex number parametrised by a type `T`, which must be either
+ `float`, `double` or `real`.
*/
struct Complex(T)
if (isFloatingPoint!T)
{
- import std.format : FormatSpec;
+ import std.format.spec : FormatSpec;
import std.range.primitives : isOutputRange;
/** The real part of the number. */
@@ -146,12 +155,11 @@ if (isFloatingPoint!T)
}
/// ditto
- void toString(Writer, Char)(scope Writer w,
- FormatSpec!Char formatSpec) const
+ void toString(Writer, Char)(scope Writer w, scope const ref FormatSpec!Char formatSpec) const
if (isOutputRange!(Writer, const(Char)[]))
{
- import std.format : formatValue;
- import std.math : signbit;
+ import std.format.write : formatValue;
+ import std.math.traits : signbit;
import std.range.primitives : put;
formatValue(w, re, formatSpec);
if (signbit(im) == 0)
@@ -174,14 +182,14 @@ if (isFloatingPoint!T)
}
/// ditto
- this(Rx : T, Ry : T)(Rx x, Ry y)
+ this(Rx : T, Ry : T)(const Rx x, const Ry y)
{
re = x;
im = y;
}
/// ditto
- this(R : T)(R r)
+ this(R : T)(const R r)
{
re = r;
im = 0;
@@ -198,7 +206,7 @@ if (isFloatingPoint!T)
}
// this = numeric
- ref Complex opAssign(R : T)(R r)
+ ref Complex opAssign(R : T)(const R r)
{
re = r;
im = 0;
@@ -214,7 +222,7 @@ if (isFloatingPoint!T)
}
// this == numeric
- bool opEquals(R : T)(R r) const
+ bool opEquals(R : T)(const R r) const
{
return re == r && im == 0;
}
@@ -246,7 +254,7 @@ if (isFloatingPoint!T)
}
// complex op numeric
- Complex!(CommonType!(T,R)) opBinary(string op, R)(R r) const
+ Complex!(CommonType!(T,R)) opBinary(string op, R)(const R r) const
if (isNumeric!R)
{
alias C = typeof(return);
@@ -255,50 +263,68 @@ if (isFloatingPoint!T)
}
// numeric + complex, numeric * complex
- Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) const
+ Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const
if ((op == "+" || op == "*") && (isNumeric!R))
{
return opBinary!(op)(r);
}
// numeric - complex
- Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) const
+ Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const
if (op == "-" && isNumeric!R)
{
return Complex(r - re, -im);
}
// numeric / complex
- Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) const
+ Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R r) const
if (op == "/" && isNumeric!R)
{
- import std.math : fabs;
- typeof(return) w = void;
- if (fabs(re) < fabs(im))
+ version (FastMath)
{
- immutable ratio = re/im;
- immutable rdivd = r/(re*ratio + im);
-
- w.re = rdivd*ratio;
- w.im = -rdivd;
+ // Compute norm(this)
+ immutable norm = re * re + im * im;
+ // Compute r * conj(this)
+ immutable prod_re = r * re;
+ immutable prod_im = r * -im;
+ // Divide the product by the norm
+ typeof(return) w = void;
+ w.re = prod_re / norm;
+ w.im = prod_im / norm;
+ return w;
}
else
{
- immutable ratio = im/re;
- immutable rdivd = r/(re + im*ratio);
-
- w.re = rdivd;
- w.im = -rdivd*ratio;
+ import core.math : fabs;
+ typeof(return) w = void;
+ if (fabs(re) < fabs(im))
+ {
+ immutable ratio = re/im;
+ immutable rdivd = r/(re*ratio + im);
+
+ w.re = rdivd*ratio;
+ w.im = -rdivd;
+ }
+ else
+ {
+ immutable ratio = im/re;
+ immutable rdivd = r/(re + im*ratio);
+
+ w.re = rdivd;
+ w.im = -rdivd*ratio;
+ }
+
+ return w;
}
-
- return w;
}
// numeric ^^ complex
- Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R lhs) const
+ Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(const R lhs) const
if (op == "^^" && isNumeric!R)
{
- import std.math : cos, exp, log, sin, PI;
+ import core.math : cos, sin;
+ import std.math.exponential : exp, log;
+ import std.math.constants : PI;
Unqual!(CommonType!(T, R)) ab = void, ar = void;
if (lhs >= 0)
@@ -322,7 +348,7 @@ if (isFloatingPoint!T)
// OP-ASSIGN OPERATORS
// complex += complex, complex -= complex
- ref Complex opOpAssign(string op, C)(C z)
+ ref Complex opOpAssign(string op, C)(const C z)
if ((op == "+" || op == "-") && is(C R == Complex!R))
{
mixin ("re "~op~"= z.re;");
@@ -331,7 +357,7 @@ if (isFloatingPoint!T)
}
// complex *= complex
- ref Complex opOpAssign(string op, C)(C z)
+ ref Complex opOpAssign(string op, C)(const C z)
if (op == "*" && is(C R == Complex!R))
{
auto temp = re*z.re - im*z.im;
@@ -341,36 +367,52 @@ if (isFloatingPoint!T)
}
// complex /= complex
- ref Complex opOpAssign(string op, C)(C z)
+ ref Complex opOpAssign(string op, C)(const C z)
if (op == "/" && is(C R == Complex!R))
{
- import std.math : fabs;
- if (fabs(z.re) < fabs(z.im))
+ version (FastMath)
{
- immutable ratio = z.re/z.im;
- immutable denom = z.re*ratio + z.im;
-
- immutable temp = (re*ratio + im)/denom;
- im = (im*ratio - re)/denom;
- re = temp;
+ // Compute norm(z)
+ immutable norm = z.re * z.re + z.im * z.im;
+ // Compute this * conj(z)
+ immutable prod_re = re * z.re - im * -z.im;
+ immutable prod_im = im * z.re + re * -z.im;
+ // Divide the product by the norm
+ re = prod_re / norm;
+ im = prod_im / norm;
+ return this;
}
else
{
- immutable ratio = z.im/z.re;
- immutable denom = z.re + z.im*ratio;
-
- immutable temp = (re + im*ratio)/denom;
- im = (im - re*ratio)/denom;
- re = temp;
+ import core.math : fabs;
+ if (fabs(z.re) < fabs(z.im))
+ {
+ immutable ratio = z.re/z.im;
+ immutable denom = z.re*ratio + z.im;
+
+ immutable temp = (re*ratio + im)/denom;
+ im = (im*ratio - re)/denom;
+ re = temp;
+ }
+ else
+ {
+ immutable ratio = z.im/z.re;
+ immutable denom = z.re + z.im*ratio;
+
+ immutable temp = (re + im*ratio)/denom;
+ im = (im - re*ratio)/denom;
+ re = temp;
+ }
+ return this;
}
- return this;
}
// complex ^^= complex
- ref Complex opOpAssign(string op, C)(C z)
+ ref Complex opOpAssign(string op, C)(const C z)
if (op == "^^" && is(C R == Complex!R))
{
- import std.math : exp, log, cos, sin;
+ import core.math : cos, sin;
+ import std.math.exponential : exp, log;
immutable r = abs(this);
immutable t = arg(this);
immutable ab = r^^z.re * exp(-t*z.im);
@@ -382,7 +424,7 @@ if (isFloatingPoint!T)
}
// complex += numeric, complex -= numeric
- ref Complex opOpAssign(string op, U : T)(U a)
+ ref Complex opOpAssign(string op, U : T)(const U a)
if (op == "+" || op == "-")
{
mixin ("re "~op~"= a;");
@@ -390,7 +432,7 @@ if (isFloatingPoint!T)
}
// complex *= numeric, complex /= numeric
- ref Complex opOpAssign(string op, U : T)(U a)
+ ref Complex opOpAssign(string op, U : T)(const U a)
if (op == "*" || op == "/")
{
mixin ("re "~op~"= a;");
@@ -399,10 +441,10 @@ if (isFloatingPoint!T)
}
// complex ^^= real
- ref Complex opOpAssign(string op, R)(R r)
+ ref Complex opOpAssign(string op, R)(const R r)
if (op == "^^" && isFloatingPoint!R)
{
- import std.math : cos, sin;
+ import core.math : cos, sin;
immutable ab = abs(this)^^r;
immutable ar = arg(this)*r;
re = ab*cos(ar);
@@ -411,7 +453,7 @@ if (isFloatingPoint!T)
}
// complex ^^= int
- ref Complex opOpAssign(string op, U)(U i)
+ ref Complex opOpAssign(string op, U)(const U i)
if (op == "^^" && isIntegral!U)
{
switch (i)
@@ -441,6 +483,7 @@ if (isFloatingPoint!T)
@safe pure nothrow unittest
{
import std.complex;
+ static import core.math;
import std.math;
enum EPS = double.epsilon;
@@ -465,16 +508,16 @@ if (isFloatingPoint!T)
assert(cmc.im == c1.im - c2.im);
auto ctc = c1 * c2;
- assert(approxEqual(abs(ctc), abs(c1)*abs(c2), EPS));
- assert(approxEqual(arg(ctc), arg(c1)+arg(c2), EPS));
+ assert(isClose(abs(ctc), abs(c1)*abs(c2), EPS));
+ assert(isClose(arg(ctc), arg(c1)+arg(c2), EPS));
auto cdc = c1 / c2;
- assert(approxEqual(abs(cdc), abs(c1)/abs(c2), EPS));
- assert(approxEqual(arg(cdc), arg(c1)-arg(c2), EPS));
+ assert(isClose(abs(cdc), abs(c1)/abs(c2), EPS));
+ assert(isClose(arg(cdc), arg(c1)-arg(c2), EPS));
auto cec = c1^^c2;
- assert(approxEqual(cec.re, 0.11524131979943839881, EPS));
- assert(approxEqual(cec.im, 0.21870790452746026696, EPS));
+ assert(isClose(cec.re, 0.1152413197994, 1e-12));
+ assert(isClose(cec.im, 0.2187079045274, 1e-12));
// Check complex-real operations.
double a = 123.456;
@@ -492,12 +535,12 @@ if (isFloatingPoint!T)
assert(ctr.im == c1.im*a);
auto cdr = c1 / a;
- assert(approxEqual(abs(cdr), abs(c1)/a, EPS));
- assert(approxEqual(arg(cdr), arg(c1), EPS));
+ assert(isClose(abs(cdr), abs(c1)/a, EPS));
+ assert(isClose(arg(cdr), arg(c1), EPS));
auto cer = c1^^3.0;
- assert(approxEqual(abs(cer), abs(c1)^^3, EPS));
- assert(approxEqual(arg(cer), arg(c1)*3, EPS));
+ assert(isClose(abs(cer), abs(c1)^^3, EPS));
+ assert(isClose(arg(cer), arg(c1)*3, EPS));
auto rpc = a + c1;
assert(rpc == cpr);
@@ -510,12 +553,12 @@ if (isFloatingPoint!T)
assert(rtc == ctr);
auto rdc = a / c1;
- assert(approxEqual(abs(rdc), a/abs(c1), EPS));
- assert(approxEqual(arg(rdc), -arg(c1), EPS));
+ assert(isClose(abs(rdc), a/abs(c1), EPS));
+ assert(isClose(arg(rdc), -arg(c1), EPS));
rdc = a / c2;
- assert(approxEqual(abs(rdc), a/abs(c2), EPS));
- assert(approxEqual(arg(rdc), -arg(c2), EPS));
+ assert(isClose(abs(rdc), a/abs(c2), EPS));
+ assert(isClose(arg(rdc), -arg(c2), EPS));
auto rec1a = 1.0 ^^ c1;
assert(rec1a.re == 1.0);
@@ -526,26 +569,26 @@ if (isFloatingPoint!T)
assert(rec2a.im == 0.0);
auto rec1b = (-1.0) ^^ c1;
- assert(approxEqual(abs(rec1b), std.math.exp(-PI * c1.im), EPS));
+ assert(isClose(abs(rec1b), std.math.exp(-PI * c1.im), EPS));
auto arg1b = arg(rec1b);
/* The argument _should_ be PI, but floating-point rounding error
* means that in fact the imaginary part is very slightly negative.
*/
- assert(approxEqual(arg1b, PI, EPS) || approxEqual(arg1b, -PI, EPS));
+ assert(isClose(arg1b, PI, EPS) || isClose(arg1b, -PI, EPS));
auto rec2b = (-1.0) ^^ c2;
- assert(approxEqual(abs(rec2b), std.math.exp(-2 * PI), EPS));
- assert(approxEqual(arg(rec2b), PI_2, EPS));
+ assert(isClose(abs(rec2b), std.math.exp(-2 * PI), EPS));
+ assert(isClose(arg(rec2b), PI_2, EPS));
auto rec3a = 0.79 ^^ complex(6.8, 5.7);
auto rec3b = complex(0.79, 0.0) ^^ complex(6.8, 5.7);
- assert(approxEqual(rec3a.re, rec3b.re, EPS));
- assert(approxEqual(rec3a.im, rec3b.im, EPS));
+ assert(isClose(rec3a.re, rec3b.re, 1e-14));
+ assert(isClose(rec3a.im, rec3b.im, 1e-14));
auto rec4a = (-0.79) ^^ complex(6.8, 5.7);
auto rec4b = complex(-0.79, 0.0) ^^ complex(6.8, 5.7);
- assert(approxEqual(rec4a.re, rec4b.re, EPS));
- assert(approxEqual(rec4a.im, rec4b.im, EPS));
+ assert(isClose(rec4a.re, rec4b.re, 1e-14));
+ assert(isClose(rec4a.im, rec4b.im, 1e-14));
auto rer = a ^^ complex(2.0, 0.0);
auto rcheck = a ^^ 2.0;
@@ -558,13 +601,13 @@ if (isFloatingPoint!T)
rcheck = (-a) ^^ 2.0;
assert(feqrel(rer2.re, rcheck) == double.mant_dig);
assert(isIdentical(rer2.re, rcheck));
- assert(approxEqual(rer2.im, 0.0, EPS));
+ assert(isClose(rer2.im, 0.0, 0.0, 1e-10));
auto rer3 = (-a) ^^ complex(-2.0, 0.0);
rcheck = (-a) ^^ (-2.0);
assert(feqrel(rer3.re, rcheck) == double.mant_dig);
assert(isIdentical(rer3.re, rcheck));
- assert(approxEqual(rer3.im, 0.0, EPS));
+ assert(isClose(rer3.im, 0.0, 0.0, EPS));
auto rer4 = a ^^ complex(-2.0, 0.0);
rcheck = a ^^ (-2.0);
@@ -576,10 +619,10 @@ if (isFloatingPoint!T)
foreach (i; 0 .. 6)
{
auto cei = c1^^i;
- assert(approxEqual(abs(cei), abs(c1)^^i, EPS));
+ assert(isClose(abs(cei), abs(c1)^^i, 1e-14));
// Use cos() here to deal with arguments that go outside
// the (-pi,pi] interval (only an issue for i>3).
- assert(approxEqual(std.math.cos(arg(cei)), std.math.cos(arg(c1)*i), EPS));
+ assert(isClose(core.math.cos(arg(cei)), core.math.cos(arg(c1)*i), 1e-14));
}
// Check operations between different complex types.
@@ -596,22 +639,22 @@ if (isFloatingPoint!T)
auto c2c = c2;
c1c /= c1;
- assert(approxEqual(c1c.re, 1.0, EPS));
- assert(approxEqual(c1c.im, 0.0, EPS));
+ assert(isClose(c1c.re, 1.0, EPS));
+ assert(isClose(c1c.im, 0.0, 0.0, EPS));
c1c = c1;
c1c /= c2;
- assert(approxEqual(c1c.re, 0.588235, EPS));
- assert(approxEqual(c1c.im, -0.352941, EPS));
+ assert(isClose(c1c.re, 0.5882352941177, 1e-12));
+ assert(isClose(c1c.im, -0.3529411764706, 1e-12));
c2c /= c1;
- assert(approxEqual(c2c.re, 1.25, EPS));
- assert(approxEqual(c2c.im, 0.75, EPS));
+ assert(isClose(c2c.re, 1.25, EPS));
+ assert(isClose(c2c.im, 0.75, EPS));
c2c = c2;
c2c /= c2;
- assert(approxEqual(c2c.re, 1.0, EPS));
- assert(approxEqual(c2c.im, 0.0, EPS));
+ assert(isClose(c2c.re, 1.0, EPS));
+ assert(isClose(c2c.im, 0.0, 0.0, EPS));
}
@safe pure nothrow unittest
@@ -697,19 +740,39 @@ if (is(T R == Complex!R))
*/
T abs(T)(Complex!T z) @safe pure nothrow @nogc
{
- import std.math : hypot;
+ import std.math.algebraic : hypot;
return hypot(z.re, z.im);
}
///
@safe pure nothrow unittest
{
- static import std.math;
+ static import core.math;
assert(abs(complex(1.0)) == 1.0);
assert(abs(complex(0.0, 1.0)) == 1.0);
- assert(abs(complex(1.0L, -2.0L)) == std.math.sqrt(5.0L));
+ assert(abs(complex(1.0L, -2.0L)) == core.math.sqrt(5.0L));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ static import core.math;
+ assert(abs(complex(0.0L, -3.2L)) == 3.2L);
+ assert(abs(complex(0.0L, 71.6L)) == 71.6L);
+ assert(abs(complex(-1.0L, 1.0L)) == core.math.sqrt(2.0L));
}
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ static import std.math;
+ Complex!T a = complex(T(-12), T(3));
+ T b = std.math.hypot(a.re, a.im);
+ assert(std.math.isClose(abs(a), b));
+ assert(std.math.isClose(abs(-a), b));
+ }}
+}
/++
Params:
@@ -726,17 +789,17 @@ T sqAbs(T)(Complex!T z) @safe pure nothrow @nogc
///
@safe pure nothrow unittest
{
- import std.math;
+ import std.math.operations : isClose;
assert(sqAbs(complex(0.0)) == 0.0);
assert(sqAbs(complex(1.0)) == 1.0);
assert(sqAbs(complex(0.0, 1.0)) == 1.0);
- assert(approxEqual(sqAbs(complex(1.0L, -2.0L)), 5.0L));
- assert(approxEqual(sqAbs(complex(-3.0L, 1.0L)), 10.0L));
- assert(approxEqual(sqAbs(complex(1.0f,-1.0f)), 2.0f));
+ assert(isClose(sqAbs(complex(1.0L, -2.0L)), 5.0L));
+ assert(isClose(sqAbs(complex(-3.0L, 1.0L)), 10.0L));
+ assert(isClose(sqAbs(complex(1.0f,-1.0f)), 2.0f));
}
/// ditto
-T sqAbs(T)(T x) @safe pure nothrow @nogc
+T sqAbs(T)(const T x) @safe pure nothrow @nogc
if (isFloatingPoint!T)
{
return x*x;
@@ -744,11 +807,11 @@ if (isFloatingPoint!T)
@safe pure nothrow unittest
{
- import std.math;
+ import std.math.operations : isClose;
assert(sqAbs(0.0) == 0.0);
assert(sqAbs(-1.0) == 1.0);
- assert(approxEqual(sqAbs(-3.0L), 9.0L));
- assert(approxEqual(sqAbs(-5.0f), 25.0f));
+ assert(isClose(sqAbs(-3.0L), 9.0L));
+ assert(isClose(sqAbs(-5.0f), 25.0f));
}
@@ -758,14 +821,14 @@ if (isFloatingPoint!T)
*/
T arg(T)(Complex!T z) @safe pure nothrow @nogc
{
- import std.math : atan2;
+ import std.math.trigonometry : atan2;
return atan2(z.im, z.re);
}
///
@safe pure nothrow unittest
{
- import std.math;
+ import std.math.constants : PI_2, PI_4;
assert(arg(complex(1.0)) == 0.0);
assert(arg(complex(0.0L, 1.0L)) == PI_2);
assert(arg(complex(1.0L, 1.0L)) == PI_4);
@@ -773,6 +836,30 @@ T arg(T)(Complex!T z) @safe pure nothrow @nogc
/**
+ * Extracts the norm of a complex number.
+ * Params:
+ * z = A complex number
+ * Returns:
+ * The squared magnitude of `z`.
+ */
+T norm(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ return z.re * z.re + z.im * z.im;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+ assert(norm(complex(3.0, 4.0)) == 25.0);
+ assert(norm(fromPolar(5.0, 0.0)) == 25.0);
+ assert(isClose(norm(fromPolar(5.0L, PI / 6)), 25.0L));
+ assert(isClose(norm(fromPolar(5.0L, 13 * PI / 6)), 25.0L));
+}
+
+
+/**
Params: z = A complex number.
Returns: The complex conjugate of `z`.
*/
@@ -788,6 +875,43 @@ Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc
assert(conj(complex(1.0, 2.0)) == complex(1.0, -2.0));
}
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ auto c = Complex!T(7, 3L);
+ assert(conj(c) == Complex!T(7, -3L));
+ auto z = Complex!T(0, -3.2L);
+ assert(conj(z) == -z);
+ }}
+}
+
+/**
+ * Returns the projection of `z` onto the Riemann sphere.
+ * Params:
+ * z = A complex number
+ * Returns:
+ * The projection of `z` onto the Riemann sphere.
+ */
+Complex!T proj(T)(Complex!T z)
+{
+ static import std.math;
+
+ if (std.math.isInfinity(z.re) || std.math.isInfinity(z.im))
+ return Complex!T(T.infinity, std.math.copysign(0.0, z.im));
+
+ return z;
+}
+
+///
+@safe pure nothrow unittest
+{
+ assert(proj(complex(1.0)) == complex(1.0));
+ assert(proj(complex(double.infinity, 5.0)) == complex(double.infinity, 0.0));
+ assert(proj(complex(5.0, -double.infinity)) == complex(double.infinity, -0.0));
+}
+
/**
Constructs a complex number given its absolute value and argument.
@@ -796,10 +920,10 @@ Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc
argument = The argument
Returns: The complex number with the given modulus and argument.
*/
-Complex!(CommonType!(T, U)) fromPolar(T, U)(T modulus, U argument)
+Complex!(CommonType!(T, U)) fromPolar(T, U)(const T modulus, const U argument)
@safe pure nothrow @nogc
{
- import std.math : sin, cos;
+ import core.math : sin, cos;
return Complex!(CommonType!(T,U))
(modulus*cos(argument), modulus*sin(argument));
}
@@ -807,22 +931,35 @@ Complex!(CommonType!(T, U)) fromPolar(T, U)(T modulus, U argument)
///
@safe pure nothrow unittest
{
- import std.math;
- auto z = fromPolar(std.math.sqrt(2.0), PI_4);
- assert(approxEqual(z.re, 1.0L, real.epsilon));
- assert(approxEqual(z.im, 1.0L, real.epsilon));
+ import core.math;
+ import std.math.operations : isClose;
+ import std.math.algebraic : sqrt;
+ import std.math.constants : PI_4;
+ auto z = fromPolar(core.math.sqrt(2.0), PI_4);
+ assert(isClose(z.re, 1.0L));
+ assert(isClose(z.im, 1.0L));
}
+version (StdUnittest)
+{
+ // Helper function for comparing two Complex numbers.
+ int ceqrel(T)(const Complex!T x, const Complex!T y) @safe pure nothrow @nogc
+ {
+ import std.math.operations : feqrel;
+ const r = feqrel(x.re, y.re);
+ const i = feqrel(x.im, y.im);
+ return r < i ? r : i;
+ }
+}
/**
Trigonometric functions on complex numbers.
Params: z = A complex number.
- Returns: The sine and cosine of `z`, respectively.
+ Returns: The sine, cosine and tangent of `z`, respectively.
*/
Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc
{
- import std.math : expi, coshisinh;
auto cs = expi(z.re);
auto csh = coshisinh(z.im);
return typeof(return)(cs.im * csh.re, cs.re * csh.im);
@@ -831,21 +968,20 @@ Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc
///
@safe pure nothrow unittest
{
- static import std.math;
- import std.math : feqrel;
+ static import core.math;
assert(sin(complex(0.0)) == 0.0);
- assert(sin(complex(2.0, 0)) == std.math.sin(2.0));
- auto c1 = sin(complex(2.0L, 0));
- auto c2 = complex(std.math.sin(2.0L), 0);
- assert(feqrel(c1.re, c2.re) >= real.mant_dig - 1 &&
- feqrel(c1.im, c2.im) >= real.mant_dig - 1);
+ assert(sin(complex(2.0, 0)) == core.math.sin(2.0));
}
+@safe pure nothrow unittest
+{
+ static import core.math;
+ assert(ceqrel(sin(complex(2.0L, 0)), complex(core.math.sin(2.0L))) >= real.mant_dig - 1);
+}
/// ditto
Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc
{
- import std.math : expi, coshisinh;
auto cs = expi(z.re);
auto csh = coshisinh(z.im);
return typeof(return)(cs.re * csh.re, - cs.im * csh.im);
@@ -854,18 +990,235 @@ Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc
///
@safe pure nothrow unittest
{
+ static import core.math;
static import std.math;
- import std.math : feqrel;
assert(cos(complex(0.0)) == 1.0);
- assert(cos(complex(1.3)) == std.math.cos(1.3));
- auto c1 = cos(complex(0, 5.2L));
- auto c2 = complex(std.math.cosh(5.2L), 0.0L);
- assert(feqrel(c1.re, c2.re) >= real.mant_dig - 1 &&
- feqrel(c1.im, c2.im) >= real.mant_dig - 1);
- auto c3 = cos(complex(1.3L));
- auto c4 = complex(std.math.cos(1.3L), 0.0L);
- assert(feqrel(c3.re, c4.re) >= real.mant_dig - 1 &&
- feqrel(c3.im, c4.im) >= real.mant_dig - 1);
+ assert(cos(complex(1.3, 0.0)) == core.math.cos(1.3));
+ assert(cos(complex(0.0, 5.2)) == std.math.cosh(5.2));
+}
+
+@safe pure nothrow unittest
+{
+ static import core.math;
+ static import std.math;
+ assert(ceqrel(cos(complex(0, 5.2L)), complex(std.math.cosh(5.2L), 0.0L)) >= real.mant_dig - 1);
+ assert(ceqrel(cos(complex(1.3L)), complex(core.math.cos(1.3L))) >= real.mant_dig - 1);
+}
+
+/// ditto
+Complex!T tan(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ return sin(z) / cos(z);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ static import std.math;
+ assert(ceqrel(tan(complex(1.0, 0.0)), complex(std.math.tan(1.0), 0.0)) >= double.mant_dig - 2);
+ assert(ceqrel(tan(complex(0.0, 1.0)), complex(0.0, std.math.tanh(1.0))) >= double.mant_dig - 2);
+}
+
+/**
+ Inverse trigonometric functions on complex numbers.
+
+ Params: z = A complex number.
+ Returns: The arcsine, arccosine and arctangent of `z`, respectively.
+*/
+Complex!T asin(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ auto ash = asinh(Complex!T(-z.im, z.re));
+ return Complex!T(ash.im, -ash.re);
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+ assert(asin(complex(0.0)) == 0.0);
+ assert(isClose(asin(complex(0.5L)), PI / 6));
+}
+
+@safe pure nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+ version (DigitalMars) {} else // Disabled because of issue 21376
+ assert(isClose(asin(complex(0.5f)), float(PI) / 6));
+}
+
+/// ditto
+Complex!T acos(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ static import std.math;
+ auto as = asin(z);
+ return Complex!T(T(std.math.PI_2) - as.re, as.im);
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+ import std.math.trigonometry : std_math_acos = acos;
+ assert(acos(complex(0.0)) == std_math_acos(0.0));
+ assert(isClose(acos(complex(0.5L)), PI / 3));
+}
+
+@safe pure nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+ version (DigitalMars) {} else // Disabled because of issue 21376
+ assert(isClose(acos(complex(0.5f)), float(PI) / 3));
+}
+
+/// ditto
+Complex!T atan(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ static import std.math;
+ const T re2 = z.re * z.re;
+ const T x = 1 - re2 - z.im * z.im;
+
+ T num = z.im + 1;
+ T den = z.im - 1;
+
+ num = re2 + num * num;
+ den = re2 + den * den;
+
+ return Complex!T(T(0.5) * std.math.atan2(2 * z.re, x),
+ T(0.25) * std.math.log(num / den));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+ assert(atan(complex(0.0)) == 0.0);
+ assert(isClose(atan(sqrt(complex(3.0L))), PI / 3));
+ assert(isClose(atan(sqrt(complex(3.0f))), float(PI) / 3));
+}
+
+/**
+ Hyperbolic trigonometric functions on complex numbers.
+
+ Params: z = A complex number.
+ Returns: The hyperbolic sine, cosine and tangent of `z`, respectively.
+*/
+Complex!T sinh(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ static import core.math, std.math;
+ return Complex!T(std.math.sinh(z.re) * core.math.cos(z.im),
+ std.math.cosh(z.re) * core.math.sin(z.im));
+}
+
+///
+@safe pure nothrow unittest
+{
+ static import std.math;
+ assert(sinh(complex(0.0)) == 0.0);
+ assert(sinh(complex(1.0L)) == std.math.sinh(1.0L));
+ assert(sinh(complex(1.0f)) == std.math.sinh(1.0f));
+}
+
+/// ditto
+Complex!T cosh(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ static import core.math, std.math;
+ return Complex!T(std.math.cosh(z.re) * core.math.cos(z.im),
+ std.math.sinh(z.re) * core.math.sin(z.im));
+}
+
+///
+@safe pure nothrow unittest
+{
+ static import std.math;
+ assert(cosh(complex(0.0)) == 1.0);
+ assert(cosh(complex(1.0L)) == std.math.cosh(1.0L));
+ assert(cosh(complex(1.0f)) == std.math.cosh(1.0f));
+}
+
+/// ditto
+Complex!T tanh(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ return sinh(z) / cosh(z);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ import std.math.trigonometry : std_math_tanh = tanh;
+ assert(tanh(complex(0.0)) == 0.0);
+ assert(isClose(tanh(complex(1.0L)), std_math_tanh(1.0L)));
+ assert(isClose(tanh(complex(1.0f)), std_math_tanh(1.0f)));
+}
+
+/**
+ Inverse hyperbolic trigonometric functions on complex numbers.
+
+ Params: z = A complex number.
+ Returns: The hyperbolic arcsine, arccosine and arctangent of `z`, respectively.
+*/
+Complex!T asinh(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ auto t = Complex!T((z.re - z.im) * (z.re + z.im) + 1, 2 * z.re * z.im);
+ return log(sqrt(t) + z);
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.trigonometry : std_math_asinh = asinh;
+ assert(asinh(complex(0.0)) == 0.0);
+ assert(isClose(asinh(complex(1.0L)), std_math_asinh(1.0L)));
+ assert(isClose(asinh(complex(1.0f)), std_math_asinh(1.0f)));
+}
+
+/// ditto
+Complex!T acosh(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ return 2 * log(sqrt(T(0.5) * (z + 1)) + sqrt(T(0.5) * (z - 1)));
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.trigonometry : std_math_acosh = acosh;
+ assert(acosh(complex(1.0)) == 0.0);
+ assert(isClose(acosh(complex(3.0L)), std_math_acosh(3.0L)));
+ assert(isClose(acosh(complex(3.0f)), std_math_acosh(3.0f)));
+}
+
+/// ditto
+Complex!T atanh(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ static import std.math;
+ const T im2 = z.im * z.im;
+ const T x = 1 - im2 - z.re * z.re;
+
+ T num = 1 + z.re;
+ T den = 1 - z.re;
+
+ num = im2 + num * num;
+ den = im2 + den * den;
+
+ return Complex!T(T(0.25) * (std.math.log(num) - std.math.log(den)),
+ T(0.5) * std.math.atan2(2 * z.im, x));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ import std.math.trigonometry : std_math_atanh = atanh;
+ assert(atanh(complex(0.0)) == 0.0);
+ assert(isClose(atanh(complex(0.5L)), std_math_atanh(0.5L)));
+ assert(isClose(atanh(complex(0.5f)), std_math_atanh(0.5f)));
}
/**
@@ -873,29 +1226,50 @@ Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc
Returns: The value of cos(y) + i sin(y).
Note:
- $(D expi) is included here for convenience and for easy migration of code
- that uses $(REF _expi, std,math). Unlike $(REF _expi, std,math), which uses the
- x87 $(I fsincos) instruction when possible, this function is no faster
- than calculating cos(y) and sin(y) separately.
+ `expi` is included here for convenience and for easy migration of code.
*/
Complex!real expi(real y) @trusted pure nothrow @nogc
{
- import std.math : cos, sin;
+ import core.math : cos, sin;
return Complex!real(cos(y), sin(y));
}
///
@safe pure nothrow unittest
{
- static import std.math;
-
- assert(expi(1.3e5L) == complex(std.math.cos(1.3e5L), std.math.sin(1.3e5L)));
+ import core.math : cos, sin;
assert(expi(0.0L) == 1.0L);
- auto z1 = expi(1.234);
- auto z2 = std.math.expi(1.234);
- assert(z1.re == z2.re && z1.im == z2.im);
+ assert(expi(1.3e5L) == complex(cos(1.3e5L), sin(1.3e5L)));
}
+/**
+ Params: y = A real number.
+ Returns: The value of cosh(y) + i sinh(y)
+
+ Note:
+ `coshisinh` is included here for convenience and for easy migration of code.
+*/
+Complex!real coshisinh(real y) @safe pure nothrow @nogc
+{
+ static import core.math;
+ static import std.math;
+ if (core.math.fabs(y) <= 0.5)
+ return Complex!real(std.math.cosh(y), std.math.sinh(y));
+ else
+ {
+ auto z = std.math.exp(y);
+ auto zi = 0.5 / z;
+ z = 0.5 * z;
+ return Complex!real(z + zi, z - zi);
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.trigonometry : cosh, sinh;
+ assert(coshisinh(3.0L) == complex(cosh(3.0L), sinh(3.0L)));
+}
/**
Params: z = A complex number.
@@ -903,7 +1277,7 @@ Complex!real expi(real y) @trusted pure nothrow @nogc
*/
Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc
{
- static import std.math;
+ static import core.math;
typeof(return) c;
real x,y,w,r;
@@ -916,19 +1290,19 @@ Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc
real z_re = z.re;
real z_im = z.im;
- x = std.math.fabs(z_re);
- y = std.math.fabs(z_im);
+ x = core.math.fabs(z_re);
+ y = core.math.fabs(z_im);
if (x >= y)
{
r = y / x;
- w = std.math.sqrt(x)
- * std.math.sqrt(0.5 * (1 + std.math.sqrt(1 + r * r)));
+ w = core.math.sqrt(x)
+ * core.math.sqrt(0.5 * (1 + core.math.sqrt(1 + r * r)));
}
else
{
r = x / y;
- w = std.math.sqrt(y)
- * std.math.sqrt(0.5 * (r + std.math.sqrt(1 + r * r)));
+ w = core.math.sqrt(y)
+ * core.math.sqrt(0.5 * (r + core.math.sqrt(1 + r * r)));
}
if (z_re >= 0)
@@ -948,29 +1322,31 @@ Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc
///
@safe pure nothrow unittest
{
- static import std.math;
+ static import core.math;
assert(sqrt(complex(0.0)) == 0.0);
- assert(sqrt(complex(1.0L, 0)) == std.math.sqrt(1.0L));
+ assert(sqrt(complex(1.0L, 0)) == core.math.sqrt(1.0L));
assert(sqrt(complex(-1.0L, 0)) == complex(0, 1.0L));
+ assert(sqrt(complex(-8.0, -6.0)) == complex(1.0, -3.0));
}
@safe pure nothrow unittest
{
- import std.math : approxEqual;
+ import std.math.operations : isClose;
auto c1 = complex(1.0, 1.0);
auto c2 = Complex!double(0.5, 2.0);
auto c1s = sqrt(c1);
- assert(approxEqual(c1s.re, 1.09868411));
- assert(approxEqual(c1s.im, 0.45508986));
+ assert(isClose(c1s.re, 1.09868411347));
+ assert(isClose(c1s.im, 0.455089860562));
auto c2s = sqrt(c2);
- assert(approxEqual(c2s.re, 1.1317134));
- assert(approxEqual(c2s.im, 0.8836155));
+ assert(isClose(c2s.re, 1.13171392428));
+ assert(isClose(c2s.im, 0.883615530876));
}
-// Issue 10881: support %f formatting of complex numbers
+// support %f formatting of complex numbers
+// https://issues.dlang.org/show_bug.cgi?id=10881
@safe unittest
{
import std.format : format;
@@ -985,7 +1361,7 @@ Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc
@safe unittest
{
// Test wide string formatting
- import std.format;
+ import std.format.write : formattedWrite;
wstring wformat(T)(string format, Complex!T c)
{
import std.array : appender;
@@ -1003,3 +1379,526 @@ Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc
// Test ease of use (vanilla toString() should be supported)
assert(complex(1.2, 3.4).toString() == "1.2+3.4i");
}
+
+@safe pure nothrow @nogc unittest
+{
+ auto c = complex(3.0L, 4.0L);
+ c = sqrt(c);
+ assert(c.re == 2.0L);
+ assert(c.im == 1.0L);
+}
+
+/**
+ * Calculates e$(SUPERSCRIPT x).
+ * Params:
+ * x = A complex number
+ * Returns:
+ * The complex base e exponential of `x`
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH exp(x)))
+ * $(TR $(TD ($(PLUSMN)0, +0)) $(TD (1, +0)))
+ * $(TR $(TD (any, +$(INFIN))) $(TD ($(NAN), $(NAN))))
+ * $(TR $(TD (any, $(NAN)) $(TD ($(NAN), $(NAN)))))
+ * $(TR $(TD (+$(INFIN), +0)) $(TD (+$(INFIN), +0)))
+ * $(TR $(TD (-$(INFIN), any)) $(TD ($(PLUSMN)0, cis(x.im))))
+ * $(TR $(TD (+$(INFIN), any)) $(TD ($(PLUSMN)$(INFIN), cis(x.im))))
+ * $(TR $(TD (-$(INFIN), +$(INFIN))) $(TD ($(PLUSMN)0, $(PLUSMN)0)))
+ * $(TR $(TD (+$(INFIN), +$(INFIN))) $(TD ($(PLUSMN)$(INFIN), $(NAN))))
+ * $(TR $(TD (-$(INFIN), $(NAN))) $(TD ($(PLUSMN)0, $(PLUSMN)0)))
+ * $(TR $(TD (+$(INFIN), $(NAN))) $(TD ($(PLUSMN)$(INFIN), $(NAN))))
+ * $(TR $(TD ($(NAN), +0)) $(TD ($(NAN), +0)))
+ * $(TR $(TD ($(NAN), any)) $(TD ($(NAN), $(NAN))))
+ * $(TR $(TD ($(NAN), $(NAN))) $(TD ($(NAN), $(NAN))))
+ * )
+ */
+Complex!T exp(T)(Complex!T x) @trusted pure nothrow @nogc // TODO: @safe
+{
+ static import std.math;
+
+ // Handle special cases explicitly here, as fromPolar will otherwise
+ // cause them to return Complex!T(NaN, NaN), or with the wrong sign.
+ if (std.math.isInfinity(x.re))
+ {
+ if (std.math.isNaN(x.im))
+ {
+ if (std.math.signbit(x.re))
+ return Complex!T(0, std.math.copysign(0, x.im));
+ else
+ return x;
+ }
+ if (std.math.isInfinity(x.im))
+ {
+ if (std.math.signbit(x.re))
+ return Complex!T(0, std.math.copysign(0, x.im));
+ else
+ return Complex!T(T.infinity, -T.nan);
+ }
+ if (x.im == 0.0)
+ {
+ if (std.math.signbit(x.re))
+ return Complex!T(0.0);
+ else
+ return Complex!T(T.infinity);
+ }
+ }
+ if (std.math.isNaN(x.re))
+ {
+ if (std.math.isNaN(x.im) || std.math.isInfinity(x.im))
+ return Complex!T(T.nan, T.nan);
+ if (x.im == 0.0)
+ return x;
+ }
+ if (x.re == 0.0)
+ {
+ if (std.math.isNaN(x.im) || std.math.isInfinity(x.im))
+ return Complex!T(T.nan, T.nan);
+ if (x.im == 0.0)
+ return Complex!T(1.0, 0.0);
+ }
+
+ return fromPolar!(T, T)(std.math.exp(x.re), x.im);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+
+ assert(exp(complex(0.0, 0.0)) == complex(1.0, 0.0));
+
+ auto a = complex(2.0, 1.0);
+ assert(exp(conj(a)) == conj(exp(a)));
+
+ auto b = exp(complex(0.0L, 1.0L) * PI);
+ assert(isClose(b, -1.0L, 0.0, 1e-15));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN, isInfinity;
+
+ auto a = exp(complex(0.0, double.infinity));
+ assert(a.re.isNaN && a.im.isNaN);
+ auto b = exp(complex(0.0, double.infinity));
+ assert(b.re.isNaN && b.im.isNaN);
+ auto c = exp(complex(0.0, double.nan));
+ assert(c.re.isNaN && c.im.isNaN);
+
+ auto d = exp(complex(+double.infinity, 0.0));
+ assert(d == complex(double.infinity, 0.0));
+ auto e = exp(complex(-double.infinity, 0.0));
+ assert(e == complex(0.0));
+ auto f = exp(complex(-double.infinity, 1.0));
+ assert(f == complex(0.0));
+ auto g = exp(complex(+double.infinity, 1.0));
+ assert(g == complex(double.infinity, double.infinity));
+ auto h = exp(complex(-double.infinity, +double.infinity));
+ assert(h == complex(0.0));
+ auto i = exp(complex(+double.infinity, +double.infinity));
+ assert(i.re.isInfinity && i.im.isNaN);
+ auto j = exp(complex(-double.infinity, double.nan));
+ assert(j == complex(0.0));
+ auto k = exp(complex(+double.infinity, double.nan));
+ assert(k.re.isInfinity && k.im.isNaN);
+
+ auto l = exp(complex(double.nan, 0));
+ assert(l.re.isNaN && l.im == 0.0);
+ auto m = exp(complex(double.nan, 1));
+ assert(m.re.isNaN && m.im.isNaN);
+ auto n = exp(complex(double.nan, double.nan));
+ assert(n.re.isNaN && n.im.isNaN);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.constants : PI;
+ import std.math.operations : isClose;
+
+ auto a = exp(complex(0.0, -PI));
+ assert(isClose(a, -1.0, 0.0, 1e-15));
+
+ auto b = exp(complex(0.0, -2.0 * PI / 3.0));
+ assert(isClose(b, complex(-0.5L, -0.866025403784438646763L)));
+
+ auto c = exp(complex(0.0, PI / 3.0));
+ assert(isClose(c, complex(0.5L, 0.866025403784438646763L)));
+
+ auto d = exp(complex(0.0, 2.0 * PI / 3.0));
+ assert(isClose(d, complex(-0.5L, 0.866025403784438646763L)));
+
+ auto e = exp(complex(0.0, PI));
+ assert(isClose(e, -1.0, 0.0, 1e-15));
+}
+
+/**
+ * Calculate the natural logarithm of x.
+ * The branch cut is along the negative axis.
+ * Params:
+ * x = A complex number
+ * Returns:
+ * The complex natural logarithm of `x`
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH log(x)))
+ * $(TR $(TD (-0, +0)) $(TD (-$(INFIN), $(PI))))
+ * $(TR $(TD (+0, +0)) $(TD (-$(INFIN), +0)))
+ * $(TR $(TD (any, +$(INFIN))) $(TD (+$(INFIN), $(PI)/2)))
+ * $(TR $(TD (any, $(NAN))) $(TD ($(NAN), $(NAN))))
+ * $(TR $(TD (-$(INFIN), any)) $(TD (+$(INFIN), $(PI))))
+ * $(TR $(TD (+$(INFIN), any)) $(TD (+$(INFIN), +0)))
+ * $(TR $(TD (-$(INFIN), +$(INFIN))) $(TD (+$(INFIN), 3$(PI)/4)))
+ * $(TR $(TD (+$(INFIN), +$(INFIN))) $(TD (+$(INFIN), $(PI)/4)))
+ * $(TR $(TD ($(PLUSMN)$(INFIN), $(NAN))) $(TD (+$(INFIN), $(NAN))))
+ * $(TR $(TD ($(NAN), any)) $(TD ($(NAN), $(NAN))))
+ * $(TR $(TD ($(NAN), +$(INFIN))) $(TD (+$(INFIN), $(NAN))))
+ * $(TR $(TD ($(NAN), $(NAN))) $(TD ($(NAN), $(NAN))))
+ * )
+ */
+Complex!T log(T)(Complex!T x) @safe pure nothrow @nogc
+{
+ static import std.math;
+
+ // Handle special cases explicitly here for better accuracy.
+ // The order here is important, so that the correct path is chosen.
+ if (std.math.isNaN(x.re))
+ {
+ if (std.math.isInfinity(x.im))
+ return Complex!T(T.infinity, T.nan);
+ else
+ return Complex!T(T.nan, T.nan);
+ }
+ if (std.math.isInfinity(x.re))
+ {
+ if (std.math.isNaN(x.im))
+ return Complex!T(T.infinity, T.nan);
+ else if (std.math.isInfinity(x.im))
+ {
+ if (std.math.signbit(x.re))
+ return Complex!T(T.infinity, std.math.copysign(3.0 * std.math.PI_4, x.im));
+ else
+ return Complex!T(T.infinity, std.math.copysign(std.math.PI_4, x.im));
+ }
+ else
+ {
+ if (std.math.signbit(x.re))
+ return Complex!T(T.infinity, std.math.copysign(std.math.PI, x.im));
+ else
+ return Complex!T(T.infinity, std.math.copysign(0.0, x.im));
+ }
+ }
+ if (std.math.isNaN(x.im))
+ return Complex!T(T.nan, T.nan);
+ if (std.math.isInfinity(x.im))
+ return Complex!T(T.infinity, std.math.copysign(std.math.PI_2, x.im));
+ if (x.re == 0.0 && x.im == 0.0)
+ {
+ if (std.math.signbit(x.re))
+ return Complex!T(-T.infinity, std.math.copysign(std.math.PI, x.im));
+ else
+ return Complex!T(-T.infinity, std.math.copysign(0.0, x.im));
+ }
+
+ return Complex!T(std.math.log(abs(x)), arg(x));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import core.math : sqrt;
+ import std.math.constants : PI;
+ import std.math.operations : isClose;
+
+ auto a = complex(2.0, 1.0);
+ assert(log(conj(a)) == conj(log(a)));
+
+ auto b = 2.0 * log10(complex(0.0, 1.0));
+ auto c = 4.0 * log10(complex(sqrt(2.0) / 2, sqrt(2.0) / 2));
+ assert(isClose(b, c, 0.0, 1e-15));
+
+ assert(log(complex(-1.0L, 0.0L)) == complex(0.0L, PI));
+ assert(log(complex(-1.0L, -0.0L)) == complex(0.0L, -PI));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN, isInfinity;
+ import std.math.constants : PI, PI_2, PI_4;
+
+ auto a = log(complex(-0.0L, 0.0L));
+ assert(a == complex(-real.infinity, PI));
+ auto b = log(complex(0.0L, 0.0L));
+ assert(b == complex(-real.infinity, +0.0L));
+ auto c = log(complex(1.0L, real.infinity));
+ assert(c == complex(real.infinity, PI_2));
+ auto d = log(complex(1.0L, real.nan));
+ assert(d.re.isNaN && d.im.isNaN);
+
+ auto e = log(complex(-real.infinity, 1.0L));
+ assert(e == complex(real.infinity, PI));
+ auto f = log(complex(real.infinity, 1.0L));
+ assert(f == complex(real.infinity, 0.0L));
+ auto g = log(complex(-real.infinity, real.infinity));
+ assert(g == complex(real.infinity, 3.0 * PI_4));
+ auto h = log(complex(real.infinity, real.infinity));
+ assert(h == complex(real.infinity, PI_4));
+ auto i = log(complex(real.infinity, real.nan));
+ assert(i.re.isInfinity && i.im.isNaN);
+
+ auto j = log(complex(real.nan, 1.0L));
+ assert(j.re.isNaN && j.im.isNaN);
+ auto k = log(complex(real.nan, real.infinity));
+ assert(k.re.isInfinity && k.im.isNaN);
+ auto l = log(complex(real.nan, real.nan));
+ assert(l.re.isNaN && l.im.isNaN);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.constants : PI;
+ import std.math.operations : isClose;
+
+ auto a = log(fromPolar(1.0, PI / 6.0));
+ assert(isClose(a, complex(0.0L, 0.523598775598298873077L), 0.0, 1e-15));
+
+ auto b = log(fromPolar(1.0, PI / 3.0));
+ assert(isClose(b, complex(0.0L, 1.04719755119659774615L), 0.0, 1e-15));
+
+ auto c = log(fromPolar(1.0, PI / 2.0));
+ assert(isClose(c, complex(0.0L, 1.57079632679489661923L), 0.0, 1e-15));
+
+ auto d = log(fromPolar(1.0, 2.0 * PI / 3.0));
+ assert(isClose(d, complex(0.0L, 2.09439510239319549230L), 0.0, 1e-15));
+
+ auto e = log(fromPolar(1.0, 5.0 * PI / 6.0));
+ assert(isClose(e, complex(0.0L, 2.61799387799149436538L), 0.0, 1e-15));
+
+ auto f = log(complex(-1.0L, 0.0L));
+ assert(isClose(f, complex(0.0L, PI), 0.0, 1e-15));
+}
+
+/**
+ * Calculate the base-10 logarithm of x.
+ * Params:
+ * x = A complex number
+ * Returns:
+ * The complex base 10 logarithm of `x`
+ */
+Complex!T log10(T)(Complex!T x) @safe pure nothrow @nogc
+{
+ static import std.math;
+
+ return log(x) / Complex!T(std.math.log(10.0));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import core.math : sqrt;
+ import std.math.constants : LN10, PI;
+ import std.math.operations : isClose;
+
+ auto a = complex(2.0, 1.0);
+ assert(log10(a) == log(a) / log(complex(10.0)));
+
+ auto b = log10(complex(0.0, 1.0)) * 2.0;
+ auto c = log10(complex(sqrt(2.0) / 2, sqrt(2.0) / 2)) * 4.0;
+ assert(isClose(b, c, 0.0, 1e-15));
+
+ assert(ceqrel(log10(complex(-100.0L, 0.0L)), complex(2.0L, PI / LN10)) >= real.mant_dig - 1);
+ assert(ceqrel(log10(complex(-100.0L, -0.0L)), complex(2.0L, -PI / LN10)) >= real.mant_dig - 1);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.constants : PI;
+ import std.math.operations : isClose;
+
+ auto a = log10(fromPolar(1.0, PI / 6.0));
+ assert(isClose(a, complex(0.0L, 0.227396058973640224580L), 0.0, 1e-15));
+
+ auto b = log10(fromPolar(1.0, PI / 3.0));
+ assert(isClose(b, complex(0.0L, 0.454792117947280449161L), 0.0, 1e-15));
+
+ auto c = log10(fromPolar(1.0, PI / 2.0));
+ assert(isClose(c, complex(0.0L, 0.682188176920920673742L), 0.0, 1e-15));
+
+ auto d = log10(fromPolar(1.0, 2.0 * PI / 3.0));
+ assert(isClose(d, complex(0.0L, 0.909584235894560898323L), 0.0, 1e-15));
+
+ auto e = log10(fromPolar(1.0, 5.0 * PI / 6.0));
+ assert(isClose(e, complex(0.0L, 1.13698029486820112290L), 0.0, 1e-15));
+
+ auto f = log10(complex(-1.0L, 0.0L));
+ assert(isClose(f, complex(0.0L, 1.36437635384184134748L), 0.0, 1e-15));
+}
+
+/**
+ * Calculates x$(SUPERSCRIPT n).
+ * The branch cut is on the negative axis.
+ * Params:
+ * x = base
+ * n = exponent
+ * Returns:
+ * `x` raised to the power of `n`
+ */
+Complex!T pow(T, Int)(Complex!T x, const Int n) @safe pure nothrow @nogc
+if (isIntegral!Int)
+{
+ alias UInt = Unsigned!(Unqual!Int);
+
+ UInt m = (n < 0) ? -cast(UInt) n : n;
+ Complex!T y = (m % 2) ? x : Complex!T(1);
+
+ while (m >>= 1)
+ {
+ x *= x;
+ if (m % 2)
+ y *= x;
+ }
+
+ return (n < 0) ? Complex!T(1) / y : y;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+
+ auto a = complex(1.0, 2.0);
+ assert(pow(a, 2) == a * a);
+ assert(pow(a, 3) == a * a * a);
+ assert(pow(a, -2) == 1.0 / (a * a));
+ assert(isClose(pow(a, -3), 1.0 / (a * a * a)));
+
+ auto b = complex(2.0);
+ assert(ceqrel(pow(b, 3), exp(3 * log(b))) >= double.mant_dig - 1);
+}
+
+/// ditto
+Complex!T pow(T)(Complex!T x, const T n) @trusted pure nothrow @nogc
+{
+ static import std.math;
+
+ if (x == 0.0)
+ return Complex!T(0.0);
+
+ if (x.im == 0 && x.re > 0.0)
+ return Complex!T(std.math.pow(x.re, n));
+
+ Complex!T t = log(x);
+ return fromPolar!(T, T)(std.math.exp(n * t.re), n * t.im);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ assert(pow(complex(0.0), 2.0) == complex(0.0));
+ assert(pow(complex(5.0), 2.0) == complex(25.0));
+
+ auto a = pow(complex(-1.0, 0.0), 0.5);
+ assert(isClose(a, complex(0.0, +1.0), 0.0, 1e-16));
+
+ auto b = pow(complex(-1.0, -0.0), 0.5);
+ assert(isClose(b, complex(0.0, -1.0), 0.0, 1e-16));
+}
+
+/// ditto
+Complex!T pow(T)(Complex!T x, Complex!T y) @trusted pure nothrow @nogc
+{
+ return (x == 0) ? Complex!T(0) : exp(y * log(x));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ import std.math.exponential : exp;
+ import std.math.constants : PI;
+ auto a = complex(0.0);
+ auto b = complex(2.0);
+ assert(pow(a, b) == complex(0.0));
+
+ auto c = complex(0.0L, 1.0L);
+ assert(isClose(pow(c, c), exp((-PI) / 2)));
+}
+
+/// ditto
+Complex!T pow(T)(const T x, Complex!T n) @trusted pure nothrow @nogc
+{
+ static import std.math;
+
+ return (x > 0.0)
+ ? fromPolar!(T, T)(std.math.pow(x, n.re), n.im * std.math.log(x))
+ : pow(Complex!T(x), n);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ assert(pow(2.0, complex(0.0)) == complex(1.0));
+ assert(pow(2.0, complex(5.0)) == complex(32.0));
+
+ auto a = pow(-2.0, complex(-1.0));
+ assert(isClose(a, complex(-0.5), 0.0, 1e-16));
+
+ auto b = pow(-0.5, complex(-1.0));
+ assert(isClose(b, complex(-2.0), 0.0, 1e-15));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.constants : PI;
+ import std.math.operations : isClose;
+
+ auto a = pow(complex(3.0, 4.0), 2);
+ assert(isClose(a, complex(-7.0, 24.0)));
+
+ auto b = pow(complex(3.0, 4.0), PI);
+ assert(ceqrel(b, complex(-152.91512205297134, 35.547499631917738)) >= double.mant_dig - 3);
+
+ auto c = pow(complex(3.0, 4.0), complex(-2.0, 1.0));
+ assert(ceqrel(c, complex(0.015351734187477306, -0.0038407695456661503)) >= double.mant_dig - 3);
+
+ auto d = pow(PI, complex(2.0, -1.0));
+ assert(ceqrel(d, complex(4.0790296880118296, -8.9872469554541869)) >= double.mant_dig - 1);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+ import std.math : RealFormat, floatTraits;
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ static if (floatTraits!T.realFormat == RealFormat.ibmExtended)
+ {
+ /* For IBM real, epsilon is too small (since 1.0 plus any double is
+ representable) to be able to expect results within epsilon * 100. */
+ }
+ else
+ {
+ T eps = T.epsilon * 100;
+
+ T a = -1.0;
+ T b = 0.5;
+ Complex!T ref1 = pow(complex(a), complex(b));
+ Complex!T res1 = pow(a, complex(b));
+ Complex!T res2 = pow(complex(a), b);
+ assert(abs(ref1 - res1) < eps);
+ assert(abs(ref1 - res2) < eps);
+ assert(abs(res1 - res2) < eps);
+
+ T c = -3.2;
+ T d = 1.4;
+ Complex!T ref2 = pow(complex(a), complex(b));
+ Complex!T res3 = pow(a, complex(b));
+ Complex!T res4 = pow(complex(a), b);
+ assert(abs(ref2 - res3) < eps);
+ assert(abs(ref2 - res4) < eps);
+ assert(abs(res3 - res4) < eps);
+ }
+ }}
+}
diff --git a/libphobos/src/std/concurrency.d b/libphobos/src/std/concurrency.d
index cf77911a5df..d101ce4abf4 100644
--- a/libphobos/src/std/concurrency.d
+++ b/libphobos/src/std/concurrency.d
@@ -1,4 +1,49 @@
/**
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ * $(DIVC quickindex,
+ * $(BOOKTABLE,
+ * $(TR $(TH Category) $(TH Symbols))
+ * $(TR $(TD Tid) $(TD
+ * $(MYREF locate)
+ * $(MYREF ownerTid)
+ * $(MYREF register)
+ * $(MYREF spawn)
+ * $(MYREF spawnLinked)
+ * $(MYREF thisTid)
+ * $(MYREF Tid)
+ * $(MYREF TidMissingException)
+ * $(MYREF unregister)
+ * ))
+ * $(TR $(TD Message passing) $(TD
+ * $(MYREF prioritySend)
+ * $(MYREF receive)
+ * $(MYREF receiveOnly)
+ * $(MYREF receiveTimeout)
+ * $(MYREF send)
+ * $(MYREF setMaxMailboxSize)
+ * ))
+ * $(TR $(TD Message-related types) $(TD
+ * $(MYREF LinkTerminated)
+ * $(MYREF MailboxFull)
+ * $(MYREF MessageMismatch)
+ * $(MYREF OnCrowding)
+ * $(MYREF OwnerTerminated)
+ * $(MYREF PriorityMessageException)
+ * ))
+ * $(TR $(TD Scheduler) $(TD
+ * $(MYREF FiberScheduler)
+ * $(MYREF Generator)
+ * $(MYREF Scheduler)
+ * $(MYREF scheduler)
+ * $(MYREF ThreadInfo)
+ * $(MYREF ThreadScheduler)
+ * $(MYREF yield)
+ * ))
+ * $(TR $(TD Misc) $(TD
+ * $(MYREF initOnce)
+ * ))
+ * ))
+ *
* This is a low-level messaging API upon which more structured or restrictive
* APIs may be built. The general idea is that every messageable entity is
* represented by a common handle type called a Tid, which allows messages to
@@ -22,7 +67,7 @@
* Copyright: Copyright Sean Kelly 2009 - 2014.
* License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
* Authors: Sean Kelly, Alex Rønne Petersen, Martin Nowak
- * Source: $(PHOBOSSRC std/_concurrency.d)
+ * Source: $(PHOBOSSRC std/concurrency.d)
*/
/* Copyright Sean Kelly 2009 - 2014.
* Distributed under the Boost Software License, Version 1.0.
@@ -72,13 +117,38 @@ import std.traits;
private
{
- template hasLocalAliasing(T...)
+ bool hasLocalAliasing(Types...)()
{
- static if (!T.length)
- enum hasLocalAliasing = false;
- else
- enum hasLocalAliasing = (std.traits.hasUnsharedAliasing!(T[0]) && !is(T[0] == Tid)) ||
- std.concurrency.hasLocalAliasing!(T[1 .. $]);
+ import std.typecons : Rebindable;
+
+ // Works around "statement is not reachable"
+ bool doesIt = false;
+ static foreach (T; Types)
+ {
+ static if (is(T == Tid))
+ { /* Allowed */ }
+ else static if (is(T : Rebindable!R, R))
+ doesIt |= hasLocalAliasing!R;
+ else static if (is(T == struct))
+ doesIt |= hasLocalAliasing!(typeof(T.tupleof));
+ else
+ doesIt |= std.traits.hasUnsharedAliasing!(T);
+ }
+ return doesIt;
+ }
+
+ @safe unittest
+ {
+ static struct Container { Tid t; }
+ static assert(!hasLocalAliasing!(Tid, Container, int));
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=20097
+ @safe unittest
+ {
+ import std.datetime.systime : SysTime;
+ static struct Container { SysTime time; }
+ static assert(!hasLocalAliasing!(SysTime, Container));
}
enum MsgType
@@ -159,9 +229,12 @@ private
void checkops(T...)(T ops)
{
+ import std.format : format;
+
foreach (i, t1; T)
{
- static assert(isFunctionPointer!t1 || isDelegate!t1);
+ static assert(isFunctionPointer!t1 || isDelegate!t1,
+ format!"T %d is not a function pointer or delegates"(i));
alias a1 = Parameters!(t1);
alias r1 = ReturnType!(t1);
@@ -173,7 +246,6 @@ private
foreach (t2; T[i + 1 .. $])
{
- static assert(isFunctionPointer!t2 || isDelegate!t2);
alias a2 = Parameters!(t2);
static assert(!is(a1 == a2),
@@ -199,7 +271,7 @@ static ~this()
// Exceptions
/**
- * Thrown on calls to $(D receiveOnly) if a message other than the type
+ * Thrown on calls to `receiveOnly` if a message other than the type
* the receiving thread expected is sent.
*/
class MessageMismatch : Exception
@@ -212,7 +284,7 @@ class MessageMismatch : Exception
}
/**
- * Thrown on calls to $(D receive) if the thread that spawned the receiving
+ * Thrown on calls to `receive` if the thread that spawned the receiving
* thread has terminated and no more messages exist.
*/
class OwnerTerminated : Exception
@@ -264,7 +336,7 @@ class PriorityMessageException : Exception
/**
* Thrown on mailbox crowding if the mailbox is configured with
- * $(D OnCrowding.throwException).
+ * `OnCrowding.throwException`.
*/
class MailboxFull : Exception
{
@@ -279,7 +351,7 @@ class MailboxFull : Exception
}
/**
- * Thrown when a Tid is missing, e.g. when $(D ownerTid) doesn't
+ * Thrown when a Tid is missing, e.g. when `ownerTid` doesn't
* find an owner thread.
*/
class TidMissingException : Exception
@@ -315,17 +387,17 @@ public:
* that a Tid executed in the future will have the same toString() output
* as another Tid that has already terminated.
*/
- void toString(scope void delegate(const(char)[]) sink)
+ void toString(W)(ref W w) const
{
- import std.format : formattedWrite;
- formattedWrite(sink, "Tid(%x)", cast(void*) mbox);
+ import std.format.write : formattedWrite;
+ auto p = () @trusted { return cast(void*) mbox; }();
+ formattedWrite(w, "Tid(%x)", p);
}
}
-@system unittest
+@safe unittest
{
- // text!Tid is @system
import std.conv : text;
Tid tid;
assert(text(tid) == "Tid(0)");
@@ -335,6 +407,15 @@ public:
assert(text(tid2) == text(tid3));
}
+// https://issues.dlang.org/show_bug.cgi?id=21512
+@system unittest
+{
+ import std.format : format;
+
+ const(Tid) b = spawn(() {});
+ assert(format!"%s"(b)[0 .. 4] == "Tid(");
+}
+
/**
* Returns: The $(LREF Tid) of the caller's thread.
*/
@@ -355,7 +436,7 @@ public:
/**
* Return the Tid of the thread which spawned the caller's thread.
*
- * Throws: A $(D TidMissingException) exception if
+ * Throws: A `TidMissingException` exception if
* there is no owner thread.
*/
@property Tid ownerTid()
@@ -412,10 +493,10 @@ private template isSpawnable(F, T...)
* Starts fn(args) in a new logical thread.
*
* Executes the supplied function in a new logical thread represented by
- * $(D Tid). The calling thread is designated as the owner of the new thread.
- * When the owner thread terminates an $(D OwnerTerminated) message will be
- * sent to the new thread, causing an $(D OwnerTerminated) exception to be
- * thrown on $(D receive()).
+ * `Tid`. The calling thread is designated as the owner of the new thread.
+ * When the owner thread terminates an `OwnerTerminated` message will be
+ * sent to the new thread, causing an `OwnerTerminated` exception to be
+ * thrown on `receive()`.
*
* Params:
* fn = The function to execute.
@@ -425,46 +506,69 @@ private template isSpawnable(F, T...)
* A Tid representing the new logical thread.
*
* Notes:
- * $(D args) must not have unshared aliasing. In other words, all arguments
- * to $(D fn) must either be $(D shared) or $(D immutable) or have no
+ * `args` must not have unshared aliasing. In other words, all arguments
+ * to `fn` must either be `shared` or `immutable` or have no
* pointer indirection. This is necessary for enforcing isolation among
* threads.
*
- * Example:
- * ---
- * import std.stdio, std.concurrency;
- *
- * void f1(string str)
- * {
- * writeln(str);
- * }
- *
- * void f2(char[] str)
- * {
- * writeln(str);
- * }
- *
- * void main()
- * {
- * auto str = "Hello, world";
- *
- * // Works: string is immutable.
- * auto tid1 = spawn(&f1, str);
- *
- * // Fails: char[] has mutable aliasing.
- * auto tid2 = spawn(&f2, str.dup);
- *
- * // New thread with anonymous function
- * spawn({ writeln("This is so great!"); });
- * }
- * ---
- */
-Tid spawn(F, T...)(F fn, T args) if (isSpawnable!(F, T))
+ * Similarly, if `fn` is a delegate, it must not have unshared aliases, meaning
+ * `fn` must be either `shared` or `immutable`. */
+Tid spawn(F, T...)(F fn, T args)
+if (isSpawnable!(F, T))
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
return _spawn(false, fn, args);
}
+///
+@system unittest
+{
+ static void f(string msg)
+ {
+ assert(msg == "Hello World");
+ }
+
+ auto tid = spawn(&f, "Hello World");
+}
+
+/// Fails: char[] has mutable aliasing.
+@system unittest
+{
+ string msg = "Hello, World!";
+
+ static void f1(string msg) {}
+ static assert(!__traits(compiles, spawn(&f1, msg.dup)));
+ static assert( __traits(compiles, spawn(&f1, msg.idup)));
+
+ static void f2(char[] msg) {}
+ static assert(!__traits(compiles, spawn(&f2, msg.dup)));
+ static assert(!__traits(compiles, spawn(&f2, msg.idup)));
+}
+
+/// New thread with anonymous function
+@system unittest
+{
+ spawn({
+ ownerTid.send("This is so great!");
+ });
+ assert(receiveOnly!string == "This is so great!");
+}
+
+@system unittest
+{
+ import core.thread : thread_joinAll;
+
+ __gshared string receivedMessage;
+ static void f1(string msg)
+ {
+ receivedMessage = msg;
+ }
+
+ auto tid1 = spawn(&f1, "Hello World");
+ thread_joinAll;
+ assert(receivedMessage == "Hello World");
+}
+
/**
* Starts fn(args) in a logical thread and will receive a LinkTerminated
* message when the operation terminates.
@@ -484,7 +588,8 @@ Tid spawn(F, T...)(F fn, T args) if (isSpawnable!(F, T))
* Returns:
* A Tid representing the new thread.
*/
-Tid spawnLinked(F, T...)(F fn, T args) if (isSpawnable!(F, T))
+Tid spawnLinked(F, T...)(F fn, T args)
+if (isSpawnable!(F, T))
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
return _spawn(true, fn, args);
@@ -493,7 +598,8 @@ Tid spawnLinked(F, T...)(F fn, T args) if (isSpawnable!(F, T))
/*
*
*/
-private Tid _spawn(F, T...)(bool linked, F fn, T args) if (isSpawnable!(F, T))
+private Tid _spawn(F, T...)(bool linked, F fn, T args)
+if (isSpawnable!(F, T))
{
// TODO: MessageList and &exec should be shared.
auto spawnTid = Tid(new MessageBox);
@@ -568,9 +674,10 @@ private Tid _spawn(F, T...)(bool linked, F fn, T args) if (isSpawnable!(F, T))
* Places the values as a message at the back of tid's message queue.
*
* Sends the supplied value to the thread represented by tid. As with
- * $(REF spawn, std,concurrency), $(D T) must not have unshared aliasing.
+ * $(REF spawn, std,concurrency), `T` must not have unshared aliasing.
*/
void send(T...)(Tid tid, T vals)
+in (tid.mbox !is null)
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
_send(tid, vals);
@@ -579,11 +686,12 @@ void send(T...)(Tid tid, T vals)
/**
* Places the values as a message on the front of tid's message queue.
*
- * Send a message to $(D tid) but place it at the front of $(D tid)'s message
+ * Send a message to `tid` but place it at the front of `tid`'s message
* queue instead of at the back. This function is typically used for
* out-of-band communication, to signal exceptional conditions, etc.
*/
void prioritySend(T...)(Tid tid, T vals)
+in (tid.mbox !is null)
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
_send(MsgType.priority, tid, vals);
@@ -593,6 +701,7 @@ void prioritySend(T...)(Tid tid, T vals)
* ditto
*/
private void _send(T...)(Tid tid, T vals)
+in (tid.mbox !is null)
{
_send(MsgType.standard, tid, vals);
}
@@ -602,6 +711,7 @@ private void _send(T...)(Tid tid, T vals)
* both Tid.send() and .send().
*/
private void _send(T...)(MsgType type, Tid tid, T vals)
+in (tid.mbox !is null)
{
auto msg = Message(type, vals);
tid.mbox.put(msg);
@@ -615,32 +725,16 @@ private void _send(T...)(MsgType type, Tid tid, T vals)
* a message against a set of delegates and executing the first match found.
*
* If a delegate that accepts a $(REF Variant, std,variant) is included as
- * the last argument to $(D receive), it will match any message that was not
+ * the last argument to `receive`, it will match any message that was not
* matched by an earlier delegate. If more than one argument is sent,
- * the $(D Variant) will contain a $(REF Tuple, std,typecons) of all values
+ * the `Variant` will contain a $(REF Tuple, std,typecons) of all values
* sent.
*
- * Example:
- * ---
- * import std.stdio;
- * import std.variant;
- * import std.concurrency;
- *
- * void spawnedFunction()
- * {
- * receive(
- * (int i) { writeln("Received an int."); },
- * (float f) { writeln("Received a float."); },
- * (Variant v) { writeln("Received some other type."); }
- * );
- * }
+ * Params:
+ * ops = Variadic list of function pointers and delegates. Entries
+ * in this list must not occlude later entries.
*
- * void main()
- * {
- * auto tid = spawn(&spawnedFunction);
- * send(tid, 42);
- * }
- * ---
+ * Throws: $(LREF OwnerTerminated) when the sending thread was terminated.
*/
void receive(T...)( T ops )
in
@@ -649,13 +743,45 @@ in
"Cannot receive a message until a thread was spawned "
~ "or thisTid was passed to a running thread.");
}
-body
+do
{
checkops( ops );
thisInfo.ident.mbox.get( ops );
}
+///
+@system unittest
+{
+ import std.variant : Variant;
+
+ auto process = ()
+ {
+ receive(
+ (int i) { ownerTid.send(1); },
+ (double f) { ownerTid.send(2); },
+ (Variant v) { ownerTid.send(3); }
+ );
+ };
+
+ {
+ auto tid = spawn(process);
+ send(tid, 42);
+ assert(receiveOnly!int == 1);
+ }
+
+ {
+ auto tid = spawn(process);
+ send(tid, 3.14);
+ assert(receiveOnly!int == 2);
+ }
+
+ {
+ auto tid = spawn(process);
+ send(tid, "something else");
+ assert(receiveOnly!int == 3);
+ }
+}
@safe unittest
{
@@ -677,7 +803,7 @@ body
}
// Make sure receive() works with free functions as well.
-version (unittest)
+version (StdUnittest)
{
private void receiveFunction(int x) {}
}
@@ -705,31 +831,17 @@ private template receiveOnlyRet(T...)
}
/**
- * Receives only messages with arguments of types $(D T).
+ * Receives only messages with arguments of the specified types.
*
- * Throws: $(D MessageMismatch) if a message of types other than $(D T)
- * is received.
+ * Params:
+ * T = Variadic list of types to be received.
*
- * Returns: The received message. If $(D T.length) is greater than one,
+ * Returns: The received message. If `T` has more than one entry,
* the message will be packed into a $(REF Tuple, std,typecons).
*
- * Example:
- * ---
- * import std.concurrency;
- *
- * void spawnedFunc()
- * {
- * auto msg = receiveOnly!(int, string)();
- * assert(msg[0] == 42);
- * assert(msg[1] == "42");
- * }
- *
- * void main()
- * {
- * auto tid = spawn(&spawnedFunc);
- * send(tid, 42, "42");
- * }
- * ---
+ * Throws: $(LREF MessageMismatch) if a message of types other than `T`
+ * is received,
+ * $(LREF OwnerTerminated) when the sending thread was terminated.
*/
receiveOnlyRet!(T) receiveOnly(T...)()
in
@@ -737,16 +849,27 @@ in
assert(thisInfo.ident.mbox !is null,
"Cannot receive a message until a thread was spawned or thisTid was passed to a running thread.");
}
-body
+do
{
import std.format : format;
+ import std.meta : allSatisfy;
import std.typecons : Tuple;
Tuple!(T) ret;
thisInfo.ident.mbox.get((T val) {
static if (T.length)
- ret.field = val;
+ {
+ static if (allSatisfy!(isAssignable, T))
+ {
+ ret.field = val;
+ }
+ else
+ {
+ import core.lifetime : emplace;
+ emplace(&ret, val);
+ }
+ }
},
(LinkTerminated e) { throw e; },
(OwnerTerminated e) { throw e; },
@@ -765,6 +888,42 @@ body
return ret;
}
+///
+@system unittest
+{
+ auto tid = spawn(
+ {
+ assert(receiveOnly!int == 42);
+ });
+ send(tid, 42);
+}
+
+///
+@system unittest
+{
+ auto tid = spawn(
+ {
+ assert(receiveOnly!string == "text");
+ });
+ send(tid, "text");
+}
+
+///
+@system unittest
+{
+ struct Record { string name; int age; }
+
+ auto tid = spawn(
+ {
+ auto msg = receiveOnly!(double, Record);
+ assert(msg[0] == 0.5);
+ assert(msg[1].name == "Alice");
+ assert(msg[1].age == 31);
+ });
+
+ send(tid, 0.5, Record("Alice", 31));
+}
+
@system unittest
{
static void t1(Tid mainTid)
@@ -786,14 +945,37 @@ body
assert(result == "Unexpected message type: expected 'string', got 'int'");
}
+// https://issues.dlang.org/show_bug.cgi?id=21663
+@safe unittest
+{
+ alias test = receiveOnly!(string, bool, bool);
+}
+
/**
- * Tries to receive but will give up if no matches arrive within duration.
- * Won't wait at all if provided $(REF Duration, core,time) is negative.
+ * Receives a message from another thread and gives up if no match
+ * arrives within a specified duration.
+ *
+ * Receive a message from another thread, or block until `duration` exceeds,
+ * if no messages of the specified types are available. This function works
+ * by pattern matching a message against a set of delegates and executing
+ * the first match found.
+ *
+ * If a delegate that accepts a $(REF Variant, std,variant) is included as
+ * the last argument, it will match any message that was not
+ * matched by an earlier delegate. If more than one argument is sent,
+ * the `Variant` will contain a $(REF Tuple, std,typecons) of all values
+ * sent.
+ *
+ * Params:
+ * duration = Duration, how long to wait. If `duration` is negative,
+ * won't wait at all.
+ * ops = Variadic list of function pointers and delegates. Entries
+ * in this list must not occlude later entries.
*
- * Same as $(D receive) except that rather than wait forever for a message,
- * it waits until either it receives a message or the given
- * $(REF Duration, core,time) has passed. It returns $(D true) if it received a
- * message and $(D false) if it timed out waiting for one.
+ * Returns: `true` if it received a message and `false` if it timed out waiting
+ * for one.
+ *
+ * Throws: $(LREF OwnerTerminated) when the sending thread was terminated.
*/
bool receiveTimeout(T...)(Duration duration, T ops)
in
@@ -801,7 +983,7 @@ in
assert(thisInfo.ident.mbox !is null,
"Cannot receive a message until a thread was spawned or thisTid was passed to a running thread.");
}
-body
+do
{
checkops(ops);
@@ -873,6 +1055,7 @@ private
* mailbox.
*/
void setMaxMailboxSize(Tid tid, size_t messages, OnCrowding doThis) @safe pure
+in (tid.mbox !is null)
{
final switch (doThis)
{
@@ -899,6 +1082,7 @@ void setMaxMailboxSize(Tid tid, size_t messages, OnCrowding doThis) @safe pure
* mailbox.
*/
void setMaxMailboxSize(Tid tid, size_t messages, bool function(Tid) onCrowdingDoThis)
+in (tid.mbox !is null)
{
tid.mbox.setMaxMsgs(messages, onCrowdingDoThis);
}
@@ -916,18 +1100,17 @@ private @property Mutex registryLock()
return impl;
}
-private void unregisterMe()
+private void unregisterMe(ref ThreadInfo me)
{
- auto me = thisInfo.ident;
- if (thisInfo.ident != Tid.init)
+ if (me.ident != Tid.init)
{
synchronized (registryLock)
{
- if (auto allNames = me in namesByTid)
+ if (auto allNames = me.ident in namesByTid)
{
foreach (name; *allNames)
tidByName.remove(name);
- namesByTid.remove(me);
+ namesByTid.remove(me.ident);
}
}
}
@@ -949,6 +1132,7 @@ private void unregisterMe()
* defunct thread.
*/
bool register(string name, Tid tid)
+in (tid.mbox !is null)
{
synchronized (registryLock)
{
@@ -1050,7 +1234,18 @@ struct ThreadInfo
_send(MsgType.linkDead, tid, ident);
if (owner != Tid.init)
_send(MsgType.linkDead, owner, ident);
- unregisterMe(); // clean up registry entries
+ unregisterMe(this); // clean up registry entries
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=20160
+ @system unittest
+ {
+ register("main_thread", thisTid());
+
+ ThreadInfo t;
+ t.cleanup();
+
+ assert(locate("main_thread") == thisTid());
}
}
@@ -1270,13 +1465,45 @@ class FiberScheduler : Scheduler
/**
* Returns a Condition analog that yields when wait or notify is called.
+ *
+ * Bug:
+ * For the default implementation, `notifyAll`will behave like `notify`.
+ *
+ * Params:
+ * m = A `Mutex` to use for locking if the condition needs to be waited on
+ * or notified from multiple `Thread`s.
+ * If `null`, no `Mutex` will be used and it is assumed that the
+ * `Condition` is only waited on/notified from one `Thread`.
*/
Condition newCondition(Mutex m) nothrow
{
return new FiberCondition(m);
}
-private:
+protected:
+ /**
+ * Creates a new Fiber which calls the given delegate.
+ *
+ * Params:
+ * op = The delegate the fiber should call
+ */
+ void create(void delegate() op) nothrow
+ {
+ void wrap()
+ {
+ scope (exit)
+ {
+ thisInfo.cleanup();
+ }
+ op();
+ }
+
+ m_fibers ~= new InfoFiber(&wrap);
+ }
+
+ /**
+ * Fiber which embeds a ThreadInfo
+ */
static class InfoFiber : Fiber
{
ThreadInfo info;
@@ -1285,8 +1512,14 @@ private:
{
super(op);
}
+
+ this(void delegate() op, size_t sz) nothrow
+ {
+ super(op, sz);
+ }
}
+private:
class FiberCondition : Condition
{
this(Mutex m) nothrow
@@ -1313,7 +1546,7 @@ private:
!notified && !period.isNegative;
period = limit - MonoTime.currTime)
{
- yield();
+ this.outer.yield();
}
return notified;
}
@@ -1333,9 +1566,11 @@ private:
private:
void switchContext() nothrow
{
- mutex_nothrow.unlock_nothrow();
- scope (exit) mutex_nothrow.lock_nothrow();
- yield();
+ if (mutex_nothrow) mutex_nothrow.unlock_nothrow();
+ scope (exit)
+ if (mutex_nothrow)
+ mutex_nothrow.lock_nothrow();
+ this.outer.yield();
}
private bool notified;
@@ -1365,20 +1600,6 @@ private:
}
}
- void create(void delegate() op) nothrow
- {
- void wrap()
- {
- scope (exit)
- {
- thisInfo.cleanup();
- }
- op();
- }
-
- m_fibers ~= new InfoFiber(&wrap);
- }
-
private:
Fiber[] m_fibers;
size_t m_pos;
@@ -1464,35 +1685,6 @@ private interface IsGenerator {}
/**
* A Generator is a Fiber that periodically returns values of type T to the
* caller via yield. This is represented as an InputRange.
- *
- * Example:
- * ---
- * import std.concurrency;
- * import std.stdio;
- *
- *
- * void main()
- * {
- * auto tid = spawn(
- * {
- * while (true)
- * {
- * writeln(receiveOnly!int());
- * }
- * });
- *
- * auto r = new Generator!int(
- * {
- * foreach (i; 1 .. 10)
- * yield(i);
- * });
- *
- * foreach (e; r)
- * {
- * tid.send(e);
- * }
- * }
- * ---
*/
class Generator(T) :
Fiber, IsGenerator, InputRange!T
@@ -1533,6 +1725,27 @@ class Generator(T) :
}
/**
+ * Initializes a generator object which is associated with a static
+ * D function. The function will be called once to prepare the range
+ * for iteration.
+ *
+ * Params:
+ * fn = The fiber function.
+ * sz = The stack size for this fiber.
+ * guardPageSize = size of the guard page to trap fiber's stack
+ * overflows. Refer to $(REF Fiber, core,thread)'s
+ * documentation for more details.
+ *
+ * In:
+ * fn must not be null.
+ */
+ this(void function() fn, size_t sz, size_t guardPageSize)
+ {
+ super(fn, sz, guardPageSize);
+ call();
+ }
+
+ /**
* Initializes a generator object which is associated with a dynamic
* D function. The function will be called once to prepare the range
* for iteration.
@@ -1568,6 +1781,27 @@ class Generator(T) :
}
/**
+ * Initializes a generator object which is associated with a dynamic
+ * D function. The function will be called once to prepare the range
+ * for iteration.
+ *
+ * Params:
+ * dg = The fiber function.
+ * sz = The stack size for this fiber.
+ * guardPageSize = size of the guard page to trap fiber's stack
+ * overflows. Refer to $(REF Fiber, core,thread)'s
+ * documentation for more details.
+ *
+ * In:
+ * dg must not be null.
+ */
+ this(void delegate() dg, size_t sz, size_t guardPageSize)
+ {
+ super(dg, sz, guardPageSize);
+ call();
+ }
+
+ /**
* Returns true if the generator is empty.
*/
final bool empty() @property
@@ -1634,6 +1868,28 @@ private:
T* m_value;
}
+///
+@system unittest
+{
+ auto tid = spawn({
+ int i;
+ while (i < 9)
+ i = receiveOnly!int;
+
+ ownerTid.send(i * 2);
+ });
+
+ auto r = new Generator!int({
+ foreach (i; 1 .. 10)
+ yield(i);
+ });
+
+ foreach (e; r)
+ tid.send(e);
+
+ assert(receiveOnly!int == 18);
+}
+
/**
* Yields a value of type T to the caller of the currently executing
* generator.
@@ -1865,7 +2121,7 @@ private
{
import std.meta : AliasSeq;
- static assert(T.length);
+ static assert(T.length, "T must not be empty");
static if (isImplicitlyConvertible!(T[0], Duration))
{
@@ -1906,7 +2162,8 @@ private
bool onLinkDeadMsg(ref Message msg)
{
- assert(msg.convertsTo!(Tid));
+ assert(msg.convertsTo!(Tid),
+ "Message could be converted to Tid");
auto tid = msg.get!(Tid);
if (bool* pDepends = tid in thisInfo.links)
@@ -2083,7 +2340,8 @@ private
{
static void onLinkDeadMsg(ref Message msg)
{
- assert(msg.convertsTo!(Tid));
+ assert(msg.convertsTo!(Tid),
+ "Message could be converted to Tid");
auto tid = msg.get!(Tid);
thisInfo.links.remove(tid);
@@ -2228,7 +2486,7 @@ private
{
import std.exception : enforce;
- assert(m_count);
+ assert(m_count, "Can not remove from empty Range");
Node* n = r.m_prev;
enforce(n && n.next, "attempting to remove invalid list node");
@@ -2295,7 +2553,7 @@ private
}
if (n)
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
emplace!Node(n, v);
}
else
@@ -2337,12 +2595,11 @@ private
}
}
-version (unittest)
+@system unittest
{
- import std.stdio;
import std.typecons : tuple, Tuple;
- void testfn(Tid tid)
+ static void testfn(Tid tid)
{
receive((float val) { assert(0); }, (int val, int val2) {
assert(val == 42 && val2 == 86);
@@ -2357,7 +2614,7 @@ version (unittest)
prioritySend(tid, "done");
}
- void runTest(Tid tid)
+ static void runTest(Tid tid)
{
send(tid, 42, 86);
send(tid, tuple(42, 86));
@@ -2366,7 +2623,7 @@ version (unittest)
receive((string val) { assert(val == "done"); });
}
- void simpleTest()
+ static void simpleTest()
{
auto tid = spawn(&testfn, thisTid);
runTest(tid);
@@ -2377,28 +2634,22 @@ version (unittest)
runTest(tid);
}
- @system unittest
- {
- simpleTest();
- }
+ simpleTest();
- @system unittest
- {
- scheduler = new ThreadScheduler;
- simpleTest();
- scheduler = null;
- }
+ scheduler = new ThreadScheduler;
+ simpleTest();
+ scheduler = null;
}
-private @property Mutex initOnceLock()
+private @property shared(Mutex) initOnceLock()
{
- __gshared Mutex lock;
- if (auto mtx = cast() atomicLoad!(MemoryOrder.acq)(*cast(shared)&lock))
+ static shared Mutex lock;
+ if (auto mtx = atomicLoad!(MemoryOrder.acq)(lock))
return mtx;
- auto mtx = new Mutex;
- if (cas(cast(shared)&lock, cast(shared) null, cast(shared) mtx))
+ auto mtx = new shared Mutex;
+ if (cas(&lock, cast(shared) null, mtx))
return mtx;
- return cast() atomicLoad!(MemoryOrder.acq)(*cast(shared)&lock);
+ return atomicLoad!(MemoryOrder.acq)(lock);
}
/**
@@ -2429,7 +2680,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init)
{
static MySingleton instance()
{
- static __gshared MySingleton inst;
+ __gshared MySingleton inst;
return initOnce!inst(new MySingleton);
}
}
@@ -2443,14 +2694,14 @@ auto ref initOnce(alias var)(lazy typeof(var) init)
{
static MySingleton instance()
{
- static __gshared MySingleton inst;
+ __gshared MySingleton inst;
return initOnce!inst(new MySingleton);
}
private:
this() { val = ++cnt; }
size_t val;
- static __gshared size_t cnt;
+ __gshared size_t cnt;
}
foreach (_; 0 .. 10)
@@ -2476,7 +2727,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init)
* Returns:
* A reference to the initialized variable
*/
-auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
+auto ref initOnce(alias var)(lazy typeof(var) init, shared Mutex mutex)
{
// check that var is global, can't take address of a TLS variable
static assert(is(typeof({ __gshared p = &var; })),
@@ -2488,7 +2739,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
{
synchronized (mutex)
{
- if (!atomicLoad!(MemoryOrder.acq)(flag))
+ if (!atomicLoad!(MemoryOrder.raw)(flag))
{
var = init;
atomicStore!(MemoryOrder.rel)(flag, true);
@@ -2498,14 +2749,20 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
return var;
}
+/// ditto
+auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
+{
+ return initOnce!var(init, cast(shared) mutex);
+}
+
/// Use a separate mutex when init blocks on another thread that might also call initOnce.
@system unittest
{
import core.sync.mutex : Mutex;
static shared bool varA, varB;
- __gshared Mutex m;
- m = new Mutex;
+ static shared Mutex m;
+ m = new shared Mutex;
spawn({
// use a different mutex for varB to avoid a dead-lock
@@ -2529,3 +2786,41 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
static assert(!__traits(compiles, initOnce!c(true))); // TLS
static assert(!__traits(compiles, initOnce!d(true))); // local variable
}
+
+// test ability to send shared arrays
+@system unittest
+{
+ static shared int[] x = new shared(int)[1];
+ auto tid = spawn({
+ auto arr = receiveOnly!(shared(int)[]);
+ arr[0] = 5;
+ ownerTid.send(true);
+ });
+ tid.send(x);
+ receiveOnly!(bool);
+ assert(x[0] == 5);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=13930
+@system unittest
+{
+ immutable aa = ["0":0];
+ thisTid.send(aa);
+ receiveOnly!(immutable int[string]); // compile error
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19345
+@system unittest
+{
+ static struct Aggregate { const int a; const int[5] b; }
+ static void t1(Tid mainTid)
+ {
+ const sendMe = Aggregate(42, [1, 2, 3, 4, 5]);
+ mainTid.send(sendMe);
+ }
+
+ spawn(&t1, thisTid);
+ auto result1 = receiveOnly!(const Aggregate)();
+ immutable expected = Aggregate(42, [1, 2, 3, 4, 5]);
+ assert(result1 == expected);
+}
diff --git a/libphobos/src/std/container/array.d b/libphobos/src/std/container/array.d
index eee890182ff..58de5b7e8b4 100644
--- a/libphobos/src/std/container/array.d
+++ b/libphobos/src/std/container/array.d
@@ -4,7 +4,7 @@
*
* This module is a submodule of $(MREF std, container).
*
- * Source: $(PHOBOSSRC std/container/_array.d)
+ * Source: $(PHOBOSSRC std/container/array.d)
*
* Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
*
@@ -25,7 +25,7 @@ import std.traits;
public import std.container.util;
///
-@system unittest
+pure @system unittest
{
auto arr = Array!int(0, 2, 3);
assert(arr[0] == 0);
@@ -52,7 +52,7 @@ public import std.container.util;
}
///
-@system unittest
+pure @system unittest
{
import std.algorithm.comparison : equal;
auto arr = Array!int(1, 2, 3);
@@ -71,16 +71,15 @@ public import std.container.util;
}
/// `Array!bool` packs together values efficiently by allocating one bit per element
-@system unittest
+pure @system unittest
{
- Array!bool arr;
- arr.insert([true, true, false, true, false]);
+ auto arr = Array!bool([true, true, false, true, false]);
assert(arr.length == 5);
}
private struct RangeT(A)
{
- /* Workaround for Issue 13629 at https://issues.dlang.org/show_bug.cgi?id=13629
+ /* Workaround for https://issues.dlang.org/show_bug.cgi?id=13629
See also: http://forum.dlang.org/post/vbmwhzvawhnkoxrhbnyb@forum.dlang.org
*/
private A[1] _outer_;
@@ -144,26 +143,34 @@ private struct RangeT(A)
E moveFront()
{
- assert(!empty && _a < _outer.length);
+ assert(!empty, "Attempting to moveFront an empty Array");
+ assert(_a < _outer.length,
+ "Attempting to moveFront using an out of bounds low index on an Array");
return move(_outer._data._payload[_a]);
}
E moveBack()
{
- assert(!empty && _b <= _outer.length);
+ assert(!empty, "Attempting to moveBack an empty Array");
+ assert(_b - 1 < _outer.length,
+ "Attempting to moveBack using an out of bounds high index on an Array");
return move(_outer._data._payload[_b - 1]);
}
E moveAt(size_t i)
{
- assert(_a + i < _b && _a + i < _outer.length);
+ assert(_a + i < _b,
+ "Attempting to moveAt using an out of bounds index on an Array");
+ assert(_a + i < _outer.length,
+ "Cannot move past the end of the array");
return move(_outer._data._payload[_a + i]);
}
}
ref inout(E) opIndex(size_t i) inout
{
- assert(_a + i < _b);
+ assert(_a + i < _b,
+ "Attempting to fetch using an out of bounds index on an Array");
return _outer[_a + i];
}
@@ -174,7 +181,8 @@ private struct RangeT(A)
RangeT opSlice(size_t i, size_t j)
{
- assert(i <= j && _a + j <= _b);
+ assert(i <= j && _a + j <= _b,
+ "Attempting to slice using an out of bounds indices on an Array");
return typeof(return)(_outer, _a + i, _a + j);
}
@@ -185,7 +193,8 @@ private struct RangeT(A)
RangeT!(const(A)) opSlice(size_t i, size_t j) const
{
- assert(i <= j && _a + j <= _b);
+ assert(i <= j && _a + j <= _b,
+ "Attempting to slice using an out of bounds indices on an Array");
return typeof(return)(_outer, _a + i, _a + j);
}
@@ -193,39 +202,45 @@ private struct RangeT(A)
{
void opSliceAssign(E value)
{
- assert(_b <= _outer.length);
+ assert(_b <= _outer.length,
+ "Attempting to assign using an out of bounds indices on an Array");
_outer[_a .. _b] = value;
}
void opSliceAssign(E value, size_t i, size_t j)
{
- assert(_a + j <= _b);
+ assert(_a + j <= _b,
+ "Attempting to slice assign using an out of bounds indices on an Array");
_outer[_a + i .. _a + j] = value;
}
void opSliceUnary(string op)()
if (op == "++" || op == "--")
{
- assert(_b <= _outer.length);
+ assert(_b <= _outer.length,
+ "Attempting to slice using an out of bounds indices on an Array");
mixin(op~"_outer[_a .. _b];");
}
void opSliceUnary(string op)(size_t i, size_t j)
if (op == "++" || op == "--")
{
- assert(_a + j <= _b);
+ assert(_a + j <= _b,
+ "Attempting to slice using an out of bounds indices on an Array");
mixin(op~"_outer[_a + i .. _a + j];");
}
void opSliceOpAssign(string op)(E value)
{
- assert(_b <= _outer.length);
+ assert(_b <= _outer.length,
+ "Attempting to slice using an out of bounds indices on an Array");
mixin("_outer[_a .. _b] "~op~"= value;");
}
void opSliceOpAssign(string op)(E value, size_t i, size_t j)
{
- assert(_a + j <= _b);
+ assert(_a + j <= _b,
+ "Attempting to slice using an out of bounds indices on an Array");
mixin("_outer[_a + i .. _a + j] "~op~"= value;");
}
}
@@ -249,9 +264,10 @@ private struct RangeT(A)
* instead of `array.map!`). The container itself is not a range.
*/
struct Array(T)
-if (!is(Unqual!T == bool))
+if (!is(immutable T == immutable bool))
{
- import core.stdc.stdlib : malloc, realloc, free;
+ import core.memory : free = pureFree;
+ import std.internal.memory : enforceMalloc, enforceRealloc;
import core.stdc.string : memcpy, memmove, memset;
import core.memory : GC;
@@ -306,23 +322,9 @@ if (!is(Unqual!T == bool))
return;
}
immutable startEmplace = length;
- if (_capacity < newLength)
- {
- // enlarge
- import core.checkedint : mulu;
-
- bool overflow;
- const nbytes = mulu(newLength, T.sizeof, overflow);
- if (overflow)
- assert(0);
- _payload = (cast(T*) realloc(_payload.ptr, nbytes))[0 .. newLength];
- _capacity = newLength;
- }
- else
- {
- _payload = _payload.ptr[0 .. newLength];
- }
+ reserve(newLength);
initializeAll(_payload.ptr[startEmplace .. newLength]);
+ _payload = _payload.ptr[0 .. newLength];
}
@property size_t capacity() const
@@ -333,11 +335,18 @@ if (!is(Unqual!T == bool))
void reserve(size_t elements)
{
if (elements <= capacity) return;
- import core.checkedint : mulu;
- bool overflow;
- const sz = mulu(elements, T.sizeof, overflow);
- if (overflow)
- assert(0);
+ static if (T.sizeof == 1)
+ {
+ const sz = elements;
+ }
+ else
+ {
+ import core.checkedint : mulu;
+ bool overflow;
+ const sz = mulu(elements, T.sizeof, overflow);
+ if (overflow)
+ assert(false, "Overflow");
+ }
static if (hasIndirections!T)
{
/* Because of the transactional nature of this
@@ -347,8 +356,7 @@ if (!is(Unqual!T == bool))
*/
immutable oldLength = length;
- auto newPayloadPtr = cast(T*) malloc(sz);
- newPayloadPtr || assert(false, "std.container.Array.reserve failed to allocate memory");
+ auto newPayloadPtr = cast(T*) enforceMalloc(sz);
auto newPayload = newPayloadPtr[0 .. oldLength];
// copy old data over to new array
@@ -365,8 +373,7 @@ if (!is(Unqual!T == bool))
else
{
// These can't have pointers, so no need to zero unused region
- auto newPayloadPtr = cast(T*) realloc(_payload.ptr, sz);
- newPayloadPtr || assert(false, "std.container.Array.reserve failed to allocate memory");
+ auto newPayloadPtr = cast(T*) enforceRealloc(_payload.ptr, sz);
auto newPayload = newPayloadPtr[0 .. length];
_payload = newPayload;
}
@@ -377,12 +384,21 @@ if (!is(Unqual!T == bool))
size_t insertBack(Elem)(Elem elem)
if (isImplicitlyConvertible!(Elem, T))
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
+ assert(_capacity >= length);
if (_capacity == length)
{
- reserve(1 + capacity * 3 / 2);
+ import core.checkedint : addu;
+
+ bool overflow;
+ immutable size_t newCapacity = addu(capacity, capacity / 2 + 1, overflow);
+ if (overflow)
+ assert(false, "Overflow");
+
+ reserve(newCapacity);
}
- assert(capacity > length && _payload.ptr);
+ assert(capacity > length && _payload.ptr,
+ "Failed to reserve memory");
emplace(_payload.ptr + _payload.length, elem);
_payload = _payload.ptr[0 .. _payload.length + 1];
return 1;
@@ -392,21 +408,27 @@ if (!is(Unqual!T == bool))
size_t insertBack(Range)(Range r)
if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T))
{
+ immutable size_t oldLength = length;
+
static if (hasLength!Range)
{
- immutable oldLength = length;
- reserve(oldLength + r.length);
+ immutable size_t rLength = r.length;
+ reserve(oldLength + rLength);
}
+
size_t result;
foreach (item; r)
{
insertBack(item);
++result;
}
+
static if (hasLength!Range)
- {
- assert(length == oldLength + r.length);
- }
+ assert(result == rLength, "insertBack: range might have changed length");
+
+ assert(length == oldLength + result,
+ "Failed to insertBack range");
+
return result;
}
}
@@ -419,27 +441,36 @@ if (!is(Unqual!T == bool))
this(U)(U[] values...)
if (isImplicitlyConvertible!(U, T))
{
- import core.checkedint : mulu;
- import std.conv : emplace;
- bool overflow;
- const nbytes = mulu(values.length, T.sizeof, overflow);
- if (overflow) assert(0);
- auto p = cast(T*) malloc(nbytes);
- static if (hasIndirections!T)
+ import core.lifetime : emplace;
+
+ static if (T.sizeof == 1)
{
- if (p)
- GC.addRange(p, T.sizeof * values.length);
+ const nbytes = values.length;
}
-
+ else
+ {
+ import core.checkedint : mulu;
+ bool overflow;
+ const nbytes = mulu(values.length, T.sizeof, overflow);
+ if (overflow)
+ assert(false, "Overflow");
+ }
+ auto p = cast(T*) enforceMalloc(nbytes);
+ // Before it is added to the gc, initialize the newly allocated memory
foreach (i, e; values)
{
emplace(p + i, e);
}
+ static if (hasIndirections!T)
+ {
+ if (p)
+ GC.addRange(p, T.sizeof * values.length);
+ }
_data = Data(p[0 .. values.length]);
}
/**
- * Constructor taking an input range
+ * Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
*/
this(Range)(Range r)
if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[]))
@@ -527,6 +558,17 @@ if (!is(Unqual!T == bool))
}
/**
+ * Returns: the internal representation of the array.
+ *
+ * Complexity: $(BIGOH 1).
+ */
+
+ T[] data() @system
+ {
+ return _data._payload;
+ }
+
+ /**
* Ensures sufficient capacity to accommodate `e` _elements.
* If `e < capacity`, this method does nothing.
*
@@ -542,14 +584,23 @@ if (!is(Unqual!T == bool))
if (!_data.refCountedStore.isInitialized)
{
if (!elements) return;
- import core.checkedint : mulu;
- bool overflow;
- const sz = mulu(elements, T.sizeof, overflow);
- if (overflow) assert(0);
- auto p = malloc(sz);
- p || assert(false, "std.container.Array.reserve failed to allocate memory");
+ static if (T.sizeof == 1)
+ {
+ const sz = elements;
+ }
+ else
+ {
+ import core.checkedint : mulu;
+ bool overflow;
+ const sz = mulu(elements, T.sizeof, overflow);
+ if (overflow)
+ assert(false, "Overflow");
+ }
+ auto p = enforceMalloc(sz);
static if (hasIndirections!T)
{
+ // Zero out unused capacity to prevent gc from seeing false pointers
+ memset(p, 0, sz);
GC.addRange(p, sz);
}
_data = Data(cast(T[]) p[0 .. 0]);
@@ -592,19 +643,19 @@ if (!is(Unqual!T == bool))
*/
Range opSlice(size_t i, size_t j)
{
- assert(i <= j && j <= length);
+ assert(i <= j && j <= length, "Invalid slice bounds");
return typeof(return)(this, i, j);
}
ConstRange opSlice(size_t i, size_t j) const
{
- assert(i <= j && j <= length);
+ assert(i <= j && j <= length, "Invalid slice bounds");
return typeof(return)(this, i, j);
}
ImmutableRange opSlice(size_t i, size_t j) immutable
{
- assert(i <= j && j <= length);
+ assert(i <= j && j <= length, "Invalid slice bounds");
return typeof(return)(this, i, j);
}
@@ -617,7 +668,8 @@ if (!is(Unqual!T == bool))
*/
@property ref inout(T) front() inout
{
- assert(_data.refCountedStore.isInitialized);
+ assert(_data.refCountedStore.isInitialized,
+ "Cannot get front of empty range");
return _data._payload[0];
}
@@ -630,7 +682,8 @@ if (!is(Unqual!T == bool))
*/
@property ref inout(T) back() inout
{
- assert(_data.refCountedStore.isInitialized);
+ assert(_data.refCountedStore.isInitialized,
+ "Cannot get back of empty range");
return _data._payload[$ - 1];
}
@@ -643,7 +696,8 @@ if (!is(Unqual!T == bool))
*/
ref inout(T) opIndex(size_t i) inout
{
- assert(_data.refCountedStore.isInitialized);
+ assert(_data.refCountedStore.isInitialized,
+ "Cannot index empty range");
return _data._payload[i];
}
@@ -879,10 +933,11 @@ if (!is(Unqual!T == bool))
size_t insertBefore(Stuff)(Range r, Stuff stuff)
if (isImplicitlyConvertible!(Stuff, T))
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
enforce(r._outer._data is _data && r._a <= length);
reserve(length + 1);
- assert(_data.refCountedStore.isInitialized);
+ assert(_data.refCountedStore.isInitialized,
+ "Failed to allocate capacity to insertBefore");
// Move elements over by one slot
memmove(_data._payload.ptr + r._a + 1,
_data._payload.ptr + r._a,
@@ -896,7 +951,7 @@ if (!is(Unqual!T == bool))
size_t insertBefore(Stuff)(Range r, Stuff stuff)
if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
enforce(r._outer._data is _data && r._a <= length);
static if (isForwardRange!Stuff)
{
@@ -904,7 +959,8 @@ if (!is(Unqual!T == bool))
auto extra = walkLength(stuff);
if (!extra) return 0;
reserve(length + extra);
- assert(_data.refCountedStore.isInitialized);
+ assert(_data.refCountedStore.isInitialized,
+ "Failed to allocate capacity to insertBefore");
// Move elements over by extra slots
memmove(_data._payload.ptr + r._a + extra,
_data._payload.ptr + r._a,
@@ -937,6 +993,8 @@ if (!is(Unqual!T == bool))
/// ditto
size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T) ||
+ isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
{
import std.algorithm.mutation : bringToFront;
enforce(r._outer._data is _data);
@@ -1115,13 +1173,21 @@ if (!is(Unqual!T == bool))
@safe unittest
{
- // REG https://issues.dlang.org/show_bug.cgi?id=13621
+ // https://issues.dlang.org/show_bug.cgi?id=13621
import std.container : Array, BinaryHeap;
alias Heap = BinaryHeap!(Array!int);
}
@system unittest
{
+ // https://issues.dlang.org/show_bug.cgi?id=18800
+ static struct S { void* p; }
+ Array!S a;
+ a.length = 10;
+}
+
+@system unittest
+{
Array!int a;
a.reserve(1000);
assert(a.length == 0);
@@ -1203,7 +1269,8 @@ if (!is(Unqual!T == bool))
assert(a.length == 7);
assert(equal(a[1 .. 4], [1, 2, 3]));
}
-// Test issue 5920
+
+// https://issues.dlang.org/show_bug.cgi?id=5920
@system unittest
{
struct structBug5920
@@ -1242,7 +1309,9 @@ if (!is(Unqual!T == bool))
}
assert(dMask == 0b1111_1111); // make sure the d'tor is called once only.
}
-// Test issue 5792 (mainly just to check if this piece of code is compilable)
+
+// Test for https://issues.dlang.org/show_bug.cgi?id=5792
+// (mainly just to check if this piece of code is compilable)
@system unittest
{
auto a = Array!(int[])([[1,2],[3,4]]);
@@ -1401,7 +1470,8 @@ if (!is(Unqual!T == bool))
arr ~= s;
}
-@safe unittest //11459
+// https://issues.dlang.org/show_bug.cgi?id=11459
+@safe unittest
{
static struct S
{
@@ -1412,18 +1482,21 @@ if (!is(Unqual!T == bool))
alias B = Array!(shared bool);
}
-@system unittest //11884
+// https://issues.dlang.org/show_bug.cgi?id=11884
+@system unittest
{
import std.algorithm.iteration : filter;
auto a = Array!int([1, 2, 2].filter!"true"());
}
-@safe unittest //8282
+// https://issues.dlang.org/show_bug.cgi?id=8282
+@safe unittest
{
auto arr = new Array!int;
}
-@system unittest //6998
+// https://issues.dlang.org/show_bug.cgi?id=6998
+@system unittest
{
static int i = 0;
class C
@@ -1448,7 +1521,9 @@ if (!is(Unqual!T == bool))
//Just to make sure the GC doesn't collect before the above test.
assert(c.dummy == 1);
}
-@system unittest //6998-2
+
+//https://issues.dlang.org/show_bug.cgi?id=6998 (2)
+@system unittest
{
static class C {int i;}
auto c = new C;
@@ -1508,6 +1583,39 @@ if (!is(Unqual!T == bool))
ai.insertBack(arr);
}
+/*
+ * typeof may give wrong result in case of classes defining `opCall` operator
+ * https://issues.dlang.org/show_bug.cgi?id=20589
+ *
+ * destructor std.container.array.Array!(MyClass).Array.~this is @system
+ * so the unittest is @system too
+ */
+@system unittest
+{
+ class MyClass
+ {
+ T opCall(T)(T p)
+ {
+ return p;
+ }
+ }
+
+ Array!MyClass arr;
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!int([1,2,3,4,5]);
+ assert(a.length == 5);
+
+ assert(a.insertAfter(a[0 .. 5], [7, 8]) == 2);
+ assert(equal(a[], [1,2,3,4,5,7,8]));
+
+ assert(a.insertAfter(a[0 .. 5], 6) == 1);
+ assert(equal(a[], [1,2,3,4,5,6,7,8]));
+}
+
////////////////////////////////////////////////////////////////////////////////
// Array!bool
@@ -1518,7 +1626,7 @@ if (!is(Unqual!T == bool))
* allocating one bit per element.
*/
struct Array(T)
-if (is(Unqual!T == bool))
+if (is(immutable T == immutable bool))
{
import std.exception : enforce;
import std.typecons : RefCounted, RefCountedAutoInitialize;
@@ -1533,7 +1641,8 @@ if (is(Unqual!T == bool))
private @property ref size_t[] data()
{
- assert(_store.refCountedStore.isInitialized);
+ assert(_store.refCountedStore.isInitialized,
+ "Cannot get data of uninitialized Array");
return _store._backend._payload;
}
@@ -1628,15 +1737,16 @@ if (is(Unqual!T == bool))
/// Ditto
@property size_t length() const
{
- assert(_a <= _b);
+ assert(_a <= _b, "Invalid bounds");
return _b - _a;
}
alias opDollar = length;
/// ditto
Range opSlice(size_t low, size_t high)
{
+ // Note: indexes start at 0, which is equivalent to _a
assert(
- _a <= low && low <= high && high <= _b,
+ low <= high && high <= (_b - _a),
"Using out of bounds indexes on an Array"
);
return Range(_outer, _a + low, _a + high);
@@ -1644,6 +1754,46 @@ if (is(Unqual!T == bool))
}
/**
+ * Constructor taking a number of items.
+ */
+ this(U)(U[] values...)
+ if (isImplicitlyConvertible!(U, T))
+ {
+ reserve(values.length);
+ foreach (i, v; values)
+ {
+ auto rem = i % bitsPerWord;
+ if (rem)
+ {
+ // Fits within the current array
+ if (v)
+ {
+ data[$ - 1] |= (cast(size_t) 1 << rem);
+ }
+ else
+ {
+ data[$ - 1] &= ~(cast(size_t) 1 << rem);
+ }
+ }
+ else
+ {
+ // Need to add more data
+ _store._backend.insertBack(v);
+ }
+ }
+ _store._length = values.length;
+ }
+
+ /**
+ * Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ */
+ this(Range)(Range r)
+ if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[]))
+ {
+ insertBack(r);
+ }
+
+ /**
* Property returning `true` if and only if the array has
* no elements.
*
@@ -1675,10 +1825,7 @@ if (is(Unqual!T == bool))
{
return _store.refCountedStore.isInitialized ? _store._length : 0;
}
- size_t opDollar() const
- {
- return length;
- }
+ alias opDollar = length;
/**
* Returns: The maximum number of elements the array can store without
@@ -1964,14 +2111,17 @@ if (is(Unqual!T == bool))
size_t insertBack(Stuff)(Stuff stuff)
if (isInputRange!Stuff && is(ElementType!Stuff : bool))
{
- static if (!hasLength!Stuff) size_t result;
+ size_t result;
+ static if (hasLength!Stuff)
+ result = stuff.length;
+
for (; !stuff.empty; stuff.popFront())
{
insertBack(stuff.front);
static if (!hasLength!Stuff) ++result;
}
- static if (!hasLength!Stuff) return result;
- else return stuff.length;
+
+ return result;
}
/// ditto
@@ -2076,6 +2226,8 @@ if (is(Unqual!T == bool))
/// ditto
size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T) ||
+ isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
{
import std.algorithm.mutation : bringToFront;
// TODO: make this faster, it moves one bit at a time
@@ -2132,14 +2284,32 @@ if (is(Unqual!T == bool))
@system unittest
{
+ import std.algorithm.comparison : equal;
+
+ auto a = Array!bool([true, true, false, false, true, false]);
+ assert(equal(a[], [true, true, false, false, true, false]));
+}
+
+// using Ranges
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+ bool[] arr = [true, true, false, false, true, false];
+
+ auto a = Array!bool(retro(arr));
+ assert(equal(a[], retro(arr)));
+}
+
+@system unittest
+{
Array!bool a;
assert(a.empty);
}
@system unittest
{
- Array!bool arr;
- arr.insert([false, false, false, false]);
+ auto arr = Array!bool([false, false, false, false]);
assert(arr.front == false);
assert(arr.back == false);
assert(arr[1] == false);
@@ -2149,6 +2319,7 @@ if (is(Unqual!T == bool))
slice.front = true;
slice.back = true;
slice[1] = true;
+ slice = slice[0 .. $]; // https://issues.dlang.org/show_bug.cgi?id=19171
assert(slice.front == true);
assert(slice.back == true);
assert(slice[1] == true);
@@ -2157,7 +2328,8 @@ if (is(Unqual!T == bool))
assert(slice.moveAt(1) == true);
}
-// issue 16331 - uncomparable values are valid values for an array
+// uncomparable values are valid values for an array
+// https://issues.dlang.org/show_bug.cgi?id=16331
@system unittest
{
double[] values = [double.nan, double.nan];
@@ -2243,22 +2415,19 @@ if (is(Unqual!T == bool))
@system unittest
{
- Array!bool a;
- a.insertBack([true, false, true, true]);
+ auto a = Array!bool([true, false, true, true]);
assert(a[0 .. 2].length == 2);
}
@system unittest
{
- Array!bool a;
- a.insertBack([true, false, true, true]);
+ auto a = Array!bool([true, false, true, true]);
assert(a[].length == 4);
}
@system unittest
{
- Array!bool a;
- a.insertBack([true, false, true, true]);
+ auto a = Array!bool([true, false, true, true]);
assert(a.front);
a.front = false;
assert(!a.front);
@@ -2266,15 +2435,13 @@ if (is(Unqual!T == bool))
@system unittest
{
- Array!bool a;
- a.insertBack([true, false, true, true]);
+ auto a = Array!bool([true, false, true, true]);
assert(a[].length == 4);
}
@system unittest
{
- Array!bool a;
- a.insertBack([true, false, true, true]);
+ auto a = Array!bool([true, false, true, true]);
assert(a.back);
a.back = false;
assert(!a.back);
@@ -2282,8 +2449,7 @@ if (is(Unqual!T == bool))
@system unittest
{
- Array!bool a;
- a.insertBack([true, false, true, true]);
+ auto a = Array!bool([true, false, true, true]);
assert(a[0] && !a[1]);
a[0] &= a[1];
assert(!a[0]);
@@ -2292,10 +2458,8 @@ if (is(Unqual!T == bool))
@system unittest
{
import std.algorithm.comparison : equal;
- Array!bool a;
- a.insertBack([true, false, true, true]);
- Array!bool b;
- b.insertBack([true, true, false, true]);
+ auto a = Array!bool([true, false, true, true]);
+ auto b = Array!bool([true, true, false, true]);
assert(equal((a ~ b)[],
[true, false, true, true, true, true, false, true]));
assert((a ~ [true, false])[].equal([true, false, true, true, true, false]));
@@ -2306,10 +2470,8 @@ if (is(Unqual!T == bool))
@system unittest
{
import std.algorithm.comparison : equal;
- Array!bool a;
- a.insertBack([true, false, true, true]);
- Array!bool b;
- a.insertBack([false, true, false, true, true]);
+ auto a = Array!bool([true, false, true, true]);
+ auto b = Array!bool([false, true, false, true, true]);
a ~= b;
assert(equal(
a[],
@@ -2318,8 +2480,7 @@ if (is(Unqual!T == bool))
@system unittest
{
- Array!bool a;
- a.insertBack([true, false, true, true]);
+ auto a = Array!bool([true, false, true, true]);
a.clear();
assert(a.capacity == 0);
}
@@ -2391,6 +2552,30 @@ if (is(Unqual!T == bool))
assert(a[].equal([false, true, true]));
}
+// https://issues.dlang.org/show_bug.cgi?id=21555
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ Array!bool arr;
+ size_t len = arr.insertBack([false, true]);
+ assert(len == 2);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21556
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ Array!bool a;
+ a.insertBack([true, true, false, false, true]);
+ assert(a.length == 5);
+
+ assert(a.insertAfter(a[0 .. 5], [false, false]) == 2);
+ assert(equal(a[], [true, true, false, false, true, false, false]));
+
+ assert(a.insertAfter(a[0 .. 5], true) == 1);
+ assert(equal(a[], [true, true, false, false, true, true, false, false]));
+}
+
@system unittest
{
import std.conv : to;
@@ -2417,3 +2602,35 @@ if (is(Unqual!T == bool))
assert(arr[0] == [1, 2, 3]);
assert(arr[1] == [4, 5, 6]);
}
+
+// Change of length reallocates without calling GC.
+// https://issues.dlang.org/show_bug.cgi?id=13642
+@system unittest
+{
+ import core.memory;
+ class ABC { void func() { int x = 5; } }
+
+ Array!ABC arr;
+ // Length only allocates if capacity is too low.
+ arr.reserve(4);
+ assert(arr.capacity == 4);
+
+ void func() @nogc
+ {
+ arr.length = 5;
+ }
+ func();
+
+ foreach (ref b; arr) b = new ABC;
+ GC.collect();
+ arr[1].func();
+}
+
+@system unittest
+{
+ Array!int arr = [1, 2, 4, 5];
+ int[] data = arr.data();
+
+ data[0] = 0;
+ assert(arr[0] == 0);
+}
diff --git a/libphobos/src/std/container/binaryheap.d b/libphobos/src/std/container/binaryheap.d
index 4adf6045436..e763357a012 100644
--- a/libphobos/src/std/container/binaryheap.d
+++ b/libphobos/src/std/container/binaryheap.d
@@ -1,10 +1,10 @@
/**
-This module provides a $(D BinaryHeap) (aka priority queue)
+This module provides a `BinaryHeap` (aka priority queue)
adaptor that makes a binary heap out of any user-provided random-access range.
This module is a submodule of $(MREF std, container).
-Source: $(PHOBOSSRC std/container/_binaryheap.d)
+Source: $(PHOBOSSRC std/container/binaryheap.d)
Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
@@ -37,29 +37,29 @@ public import std.container.util;
/**
Implements a $(HTTP en.wikipedia.org/wiki/Binary_heap, binary heap)
container on top of a given random-access range type (usually $(D
-T[])) or a random-access container type (usually $(D Array!T)). The
-documentation of $(D BinaryHeap) will refer to the underlying range or
+T[])) or a random-access container type (usually `Array!T`). The
+documentation of `BinaryHeap` will refer to the underlying range or
container as the $(I store) of the heap.
The binary heap induces structure over the underlying store such that
-accessing the largest element (by using the $(D front) property) is a
+accessing the largest element (by using the `front` property) is a
$(BIGOH 1) operation and extracting it (by using the $(D
removeFront()) method) is done fast in $(BIGOH log n) time.
-If $(D less) is the less-than operator, which is the default option,
-then $(D BinaryHeap) defines a so-called max-heap that optimizes
+If `less` is the less-than operator, which is the default option,
+then `BinaryHeap` defines a so-called max-heap that optimizes
extraction of the $(I largest) elements. To define a min-heap,
instantiate BinaryHeap with $(D "a > b") as its predicate.
-Simply extracting elements from a $(D BinaryHeap) container is
-tantamount to lazily fetching elements of $(D Store) in descending
-order. Extracting elements from the $(D BinaryHeap) to completion
+Simply extracting elements from a `BinaryHeap` container is
+tantamount to lazily fetching elements of `Store` in descending
+order. Extracting elements from the `BinaryHeap` to completion
leaves the underlying store sorted in ascending order but, again,
yields elements in descending order.
-If $(D Store) is a range, the $(D BinaryHeap) cannot grow beyond the
-size of that range. If $(D Store) is a container that supports $(D
-insertBack), the $(D BinaryHeap) may grow by adding elements to the
+If `Store` is a range, the `BinaryHeap` cannot grow beyond the
+size of that range. If `Store` is a container that supports $(D
+insertBack), the `BinaryHeap` may grow by adding elements to the
container.
*/
struct BinaryHeap(Store, alias less = "a < b")
@@ -95,12 +95,14 @@ if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[])))
// Convenience accessors
private @property ref Store _store()
{
- assert(_payload.refCountedStore.isInitialized);
+ assert(_payload.refCountedStore.isInitialized,
+ "BinaryHeap not initialized");
return _payload._store;
}
private @property ref size_t _length()
{
- assert(_payload.refCountedStore.isInitialized);
+ assert(_payload.refCountedStore.isInitialized,
+ "BinaryHeap not initialized");
return _payload._length;
}
@@ -135,12 +137,12 @@ if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[])))
public:
/**
- Converts the store $(D s) into a heap. If $(D initialSize) is
- specified, only the first $(D initialSize) elements in $(D s)
+ Converts the store `s` into a heap. If `initialSize` is
+ specified, only the first `initialSize` elements in `s`
are transformed into a heap, after which the heap can grow up
- to $(D r.length) (if $(D Store) is a range) or indefinitely (if
- $(D Store) is a container with $(D insertBack)). Performs
- $(BIGOH min(r.length, initialSize)) evaluations of $(D less).
+ to `r.length` (if `Store` is a range) or indefinitely (if
+ `Store` is a container with `insertBack`). Performs
+ $(BIGOH min(r.length, initialSize)) evaluations of `less`.
*/
this(Store s, size_t initialSize = size_t.max)
{
@@ -148,7 +150,7 @@ public:
}
/**
-Takes ownership of a store. After this, manipulating $(D s) may make
+Takes ownership of a store. After this, manipulating `s` may make
the heap work incorrectly.
*/
void acquire(Store s, size_t initialSize = size_t.max)
@@ -174,8 +176,8 @@ heap.
}
/**
-Clears the heap. Returns the portion of the store from $(D 0) up to
-$(D length), which satisfies the $(LINK2 https://en.wikipedia.org/wiki/Heap_(data_structure),
+Clears the heap. Returns the portion of the store from `0` up to
+`length`, which satisfies the $(LINK2 https://en.wikipedia.org/wiki/Heap_(data_structure),
heap property).
*/
auto release()
@@ -191,7 +193,7 @@ heap property).
}
/**
-Returns $(D true) if the heap is _empty, $(D false) otherwise.
+Returns `true` if the heap is _empty, `false` otherwise.
*/
@property bool empty()
{
@@ -199,7 +201,7 @@ Returns $(D true) if the heap is _empty, $(D false) otherwise.
}
/**
-Returns a duplicate of the heap. The $(D dup) method is available only if the
+Returns a duplicate of the heap. The `dup` method is available only if the
underlying store supports it.
*/
static if (is(typeof((Store s) { return s.dup; }(Store.init)) == Store))
@@ -241,7 +243,7 @@ underlying store (if the store is a container).
/**
Returns a copy of the _front of the heap, which is the largest element
-according to $(D less).
+according to `less`.
*/
@property ElementType!Store front()
{
@@ -258,7 +260,7 @@ Clears the heap by detaching it from the underlying store.
}
/**
-Inserts $(D value) into the store. If the underlying store is a range
+Inserts `value` into the store. If the underlying store is a range
and $(D length == capacity), throws an exception.
*/
size_t insert(ElementType!Store value)
@@ -331,7 +333,7 @@ Removes the largest element from the heap.
/**
Removes the largest element from the heap and returns a copy of
it. The element still resides in the heap's store. For performance
-reasons you may want to use $(D removeFront) with heaps of objects
+reasons you may want to use `removeFront` with heaps of objects
that are expensive to copy.
*/
ElementType!Store removeAny()
@@ -341,7 +343,7 @@ that are expensive to copy.
}
/**
-Replaces the largest element in the store with $(D value).
+Replaces the largest element in the store with `value`.
*/
void replaceFront(ElementType!Store value)
{
@@ -353,11 +355,11 @@ Replaces the largest element in the store with $(D value).
}
/**
-If the heap has room to grow, inserts $(D value) into the store and
-returns $(D true). Otherwise, if $(D less(value, front)), calls $(D
-replaceFront(value)) and returns again $(D true). Otherwise, leaves
-the heap unaffected and returns $(D false). This method is useful in
-scenarios where the smallest $(D k) elements of a set of candidates
+If the heap has room to grow, inserts `value` into the store and
+returns `true`. Otherwise, if $(D less(value, front)), calls $(D
+replaceFront(value)) and returns again `true`. Otherwise, leaves
+the heap unaffected and returns `false`. This method is useful in
+scenarios where the smallest `k` elements of a set of candidates
must be collected.
*/
bool conditionalInsert(ElementType!Store value)
@@ -380,13 +382,14 @@ must be collected.
/**
Swapping is allowed if the heap is full. If $(D less(value, front)), the
-method exchanges store.front and value and returns $(D true). Otherwise, it
-leaves the heap unaffected and returns $(D false).
+method exchanges store.front and value and returns `true`. Otherwise, it
+leaves the heap unaffected and returns `false`.
*/
bool conditionalSwap(ref ElementType!Store value)
{
_payload.refCountedStore.ensureInitialized();
- assert(_length == _store.length);
+ assert(_length == _store.length,
+ "length and number of stored items out of sync");
assert(!_store.empty, "Cannot swap front of an empty heap.");
if (!comp(value, _store.front)) return false; // value >= largest
@@ -413,7 +416,7 @@ leaves the heap unaffected and returns $(D false).
assert(equal(a, [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]));
}
-/// $(D BinaryHeap) implements the standard input range interface, allowing
+/// `BinaryHeap` implements the standard input range interface, allowing
/// lazy iteration of the underlying range in descending order.
@system unittest
{
@@ -425,8 +428,8 @@ leaves the heap unaffected and returns $(D false).
}
/**
-Convenience function that returns a $(D BinaryHeap!Store) object
-initialized with $(D s) and $(D initialSize).
+Convenience function that returns a `BinaryHeap!Store` object
+initialized with `s` and `initialSize`.
*/
BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s,
size_t initialSize = size_t.max)
@@ -477,7 +480,8 @@ BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s,
assert(h.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1]));
}
-@system unittest // 15675
+// https://issues.dlang.org/show_bug.cgi?id=15675
+@system unittest
{
import std.container.array : Array;
@@ -486,7 +490,8 @@ BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s,
assert(heap.front == 12);
}
-@system unittest // 16072
+// https://issues.dlang.org/show_bug.cgi?id=16072
+@system unittest
{
auto q = heapify!"a > b"([2, 4, 5]);
q.insert(1);
@@ -516,11 +521,7 @@ BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s,
static struct StructWithoutDup
{
int[] a;
- @disable StructWithoutDup dup()
- {
- StructWithoutDup d;
- return d;
- }
+ @disable StructWithoutDup dup();
alias a this;
}
@@ -585,7 +586,8 @@ BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s,
assert(equal(b, [10, 9, 8, 7, 6, 6, 7, 8, 9, 10]));
}
-@system unittest // Issue 17314
+// https://issues.dlang.org/show_bug.cgi?id=17314
+@system unittest
{
import std.algorithm.comparison : equal;
int[] a = [5];
diff --git a/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d
index 633371fa67f..cc3e2e85dbc 100644
--- a/libphobos/src/std/container/dlist.d
+++ b/libphobos/src/std/container/dlist.d
@@ -4,7 +4,7 @@ It can be used as a queue, dequeue or stack.
This module is a submodule of $(MREF std, container).
-Source: $(PHOBOSSRC std/container/_dlist.d)
+Source: $(PHOBOSSRC std/container/dlist.d)
Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
@@ -127,19 +127,19 @@ nothrow @safe pure:
}
@property
- bool empty() const
+ bool empty() const scope
{
assert((_first is null) == (_last is null), "DList.Range: Invalidated state");
return !_first;
}
- @property BaseNode* front()
+ @property BaseNode* front() return scope
{
assert(!empty, "DList.Range.front: Range is empty");
return _first;
}
- void popFront()
+ void popFront() scope
{
assert(!empty, "DList.Range.popFront: Range is empty");
if (_first is _last)
@@ -153,13 +153,13 @@ nothrow @safe pure:
}
}
- @property BaseNode* back()
+ @property BaseNode* back() return scope
{
assert(!empty, "DList.Range.front: Range is empty");
return _last;
}
- void popBack()
+ void popBack() scope
{
assert(!empty, "DList.Range.popBack: Range is empty");
if (_first is _last)
@@ -174,13 +174,13 @@ nothrow @safe pure:
}
/// Forward range primitive.
- @property DRange save() { return this; }
+ @property DRange save() return scope { return this; }
}
/**
Implements a doubly-linked list.
-$(D DList) uses reference semantics.
+`DList` uses reference semantics.
*/
struct DList(T)
{
@@ -222,12 +222,12 @@ struct DList(T)
}
ref inout(BaseNode*) _first() @property @safe nothrow pure inout
{
- assert(_root);
+ assert(_root, "Root pointer must not be null");
return _root._next;
}
ref inout(BaseNode*) _last() @property @safe nothrow pure inout
{
- assert(_root);
+ assert(_root, "Root pointer must not be null");
return _root._prev;
}
} //end private
@@ -241,7 +241,7 @@ Constructor taking a number of nodes
}
/**
-Constructor taking an input range
+Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
*/
this(Stuff)(Stuff stuff)
if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
@@ -252,8 +252,8 @@ Constructor taking an input range
/**
Comparison for equality.
-Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of
-elements in $(D rhs).
+Complexity: $(BIGOH min(n, n1)) where `n1` is the number of
+elements in `rhs`.
*/
bool opEquals()(ref const DList rhs) const
if (is(typeof(front == front)))
@@ -316,7 +316,7 @@ elements in $(D rhs).
}
/**
-Property returning $(D true) if and only if the container has no
+Property returning `true` if and only if the container has no
elements.
Complexity: $(BIGOH 1)
@@ -327,9 +327,9 @@ Complexity: $(BIGOH 1)
}
/**
-Removes all contents from the $(D DList).
+Removes all contents from the `DList`.
-Postcondition: $(D empty)
+Postcondition: `empty`
Complexity: $(BIGOH 1)
*/
@@ -365,7 +365,7 @@ Complexity: $(BIGOH 1)
}
/**
-Forward to $(D opSlice().front).
+Forward to `opSlice().front`.
Complexity: $(BIGOH 1)
*/
@@ -376,7 +376,7 @@ Complexity: $(BIGOH 1)
}
/**
-Forward to $(D opSlice().back).
+Forward to `opSlice().back`.
Complexity: $(BIGOH 1)
*/
@@ -391,8 +391,8 @@ Complexity: $(BIGOH 1)
/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
/**
-Returns a new $(D DList) that's the concatenation of $(D this) and its
-argument $(D rhs).
+Returns a new `DList` that's the concatenation of `this` and its
+argument `rhs`.
*/
DList opBinary(string op, Stuff)(Stuff rhs)
if (op == "~" && is(typeof(insertBack(rhs))))
@@ -403,8 +403,8 @@ argument $(D rhs).
}
/**
-Returns a new $(D DList) that's the concatenation of the argument $(D lhs)
-and $(D this).
+Returns a new `DList` that's the concatenation of the argument `lhs`
+and `this`.
*/
DList opBinaryRight(string op, Stuff)(Stuff lhs)
if (op == "~" && is(typeof(insertFront(lhs))))
@@ -415,7 +415,7 @@ and $(D this).
}
/**
-Appends the contents of the argument $(D rhs) into $(D this).
+Appends the contents of the argument `rhs` into `this`.
*/
DList opOpAssign(string op, Stuff)(Stuff rhs)
if (op == "~" && is(typeof(insertBack(rhs))))
@@ -429,8 +429,8 @@ Appends the contents of the argument $(D rhs) into $(D this).
/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
/**
-Inserts $(D stuff) to the front/back of the container. $(D stuff) can be a
-value convertible to $(D T) or a range of objects convertible to $(D
+Inserts `stuff` to the front/back of the container. `stuff` can be a
+value convertible to `T` or a range of objects convertible to $(D
T). The stable version behaves the same, but guarantees that ranges
iterating over the container are never invalidated.
@@ -464,18 +464,18 @@ Complexity: $(BIGOH log(n))
alias stableInsertBack = insertBack;
/**
-Inserts $(D stuff) after range $(D r), which must be a non-empty range
+Inserts `stuff` after range `r`, which must be a non-empty range
previously extracted from this container.
-$(D stuff) can be a value convertible to $(D T) or a range of objects
-convertible to $(D T). The stable version behaves the same, but
+`stuff` can be a value convertible to `T` or a range of objects
+convertible to `T`. The stable version behaves the same, but
guarantees that ranges iterating over the container are never
invalidated.
Returns: The number of values inserted.
-Complexity: $(BIGOH k + m), where $(D k) is the number of elements in
-$(D r) and $(D m) is the length of $(D stuff).
+Complexity: $(BIGOH k + m), where `k` is the number of elements in
+`r` and `m` is the length of `stuff`.
*/
size_t insertBefore(Stuff)(Range r, Stuff stuff)
{
@@ -515,7 +515,7 @@ Picks one value in an unspecified position in the container, removes
it from the container, and returns it. The stable version behaves the same,
but guarantees that ranges iterating over the container are never invalidated.
-Precondition: $(D !empty)
+Precondition: `!empty`
Returns: The element removed.
@@ -538,7 +538,7 @@ Removes the value at the front/back of the container. The stable version
behaves the same, but guarantees that ranges iterating over the
container are never invalidated.
-Precondition: $(D !empty)
+Precondition: `!empty`
Complexity: $(BIGOH 1).
*/
@@ -564,9 +564,9 @@ Complexity: $(BIGOH 1).
alias stableRemoveBack = removeBack;
/**
-Removes $(D howMany) values at the front or back of the
+Removes `howMany` values at the front or back of the
container. Unlike the unparameterized versions above, these functions
-do not throw if they could not remove $(D howMany) elements. Instead,
+do not throw if they could not remove `howMany` elements. Instead,
if $(D howMany > n), all elements are removed. The returned value is
the effective number of elements removed. The stable version behaves
the same, but guarantees that ranges iterating over the container are
@@ -612,11 +612,11 @@ Complexity: $(BIGOH howMany).
alias stableRemoveBack = removeBack;
/**
-Removes all elements belonging to $(D r), which must be a range
+Removes all elements belonging to `r`, which must be a range
obtained originally from this container.
Returns: A range spanning the remaining elements in the container that
-initially were right after $(D r).
+initially were right after `r`.
Complexity: $(BIGOH 1)
*/
@@ -642,9 +642,12 @@ Complexity: $(BIGOH 1)
return remove(r);
}
+ /// ditto
+ alias stableRemove = remove;
+
/**
-Removes first element of $(D r), wich must be a range obtained originally
-from this container, from both DList instance and range $(D r).
+Removes first element of `r`, wich must be a range obtained originally
+from this container, from both DList instance and range `r`.
Compexity: $(BIGOH 1)
*/
@@ -659,8 +662,8 @@ Compexity: $(BIGOH 1)
}
/**
-Removes last element of $(D r), wich must be a range obtained originally
-from this container, from both DList instance and range $(D r).
+Removes last element of `r`, wich must be a range obtained originally
+from this container, from both DList instance and range `r`.
Compexity: $(BIGOH 1)
*/
@@ -675,8 +678,8 @@ Compexity: $(BIGOH 1)
}
/**
-$(D linearRemove) functions as $(D remove), but also accepts ranges that are
-result the of a $(D take) operation. This is a convenient way to remove a
+`linearRemove` functions as `remove`, but also accepts ranges that are
+result the of a `take` operation. This is a convenient way to remove a
fixed amount of elements from the range.
Complexity: $(BIGOH r.walkLength)
@@ -698,12 +701,47 @@ Complexity: $(BIGOH r.walkLength)
}
/// ditto
- alias stableRemove = remove;
- /// ditto
alias stableLinearRemove = linearRemove;
+/**
+Removes the first occurence of an element from the list in linear time.
+
+Returns: True if the element existed and was successfully removed, false otherwise.
+
+Params:
+ value = value of the node to be removed
+
+Complexity: $(BIGOH n)
+ */
+ bool linearRemoveElement(T value)
+ {
+ auto n1 = findNodeByValue(_root, value);
+ if (n1)
+ {
+ auto n2 = n1._next._next;
+ BaseNode.connect(n1, n2);
+ return true;
+ }
+
+ return false;
+ }
+
+
private:
+ BaseNode* findNodeByValue(BaseNode* n, T value)
+ {
+ if (!n) return null;
+ auto ahead = n._next;
+ while (ahead && ahead.getPayload!T() != value)
+ {
+ n = ahead;
+ ahead = n._next;
+ if (ahead == _last._next) return null;
+ }
+ return n;
+ }
+
// Helper: Inserts stuff before the node n.
size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff)
if (isImplicitlyConvertible!(Stuff, T))
@@ -766,6 +804,38 @@ private:
{
import std.algorithm.comparison : equal;
+ auto e = DList!int();
+ auto b = e.linearRemoveElement(1);
+ assert(b == false);
+ assert(e.empty());
+ auto a = DList!int(-1, 1, 2, 1, 3, 4);
+ b = a.linearRemoveElement(1);
+ assert(equal(a[], [-1, 2, 1, 3, 4]));
+ assert(b == true);
+ b = a.linearRemoveElement(-1);
+ assert(b == true);
+ assert(equal(a[], [2, 1, 3, 4]));
+ b = a.linearRemoveElement(1);
+ assert(b == true);
+ assert(equal(a[], [2, 3, 4]));
+ b = a.linearRemoveElement(2);
+ assert(b == true);
+ b = a.linearRemoveElement(20);
+ assert(b == false);
+ assert(equal(a[], [3, 4]));
+ b = a.linearRemoveElement(4);
+ assert(b == true);
+ assert(equal(a[], [3]));
+ b = a.linearRemoveElement(3);
+ assert(b == true);
+ assert(a.empty());
+ a.linearRemoveElement(3);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
//Tests construction signatures
alias IntList = DList!int;
auto a0 = IntList();
@@ -917,7 +987,7 @@ private:
assert(r.back == 1);
}
-// Issue 8895
+// https://issues.dlang.org/show_bug.cgi?id=8895
@safe unittest
{
auto a = make!(DList!int)(1,2,3,4);
@@ -988,7 +1058,7 @@ private:
{
import std.algorithm.comparison : equal;
- //8905
+ // https://issues.dlang.org/show_bug.cgi?id=8905
auto a = DList!int([1, 2, 3, 4]);
auto r = a[];
a.stableRemoveBack();
@@ -996,7 +1066,8 @@ private:
assert(a[].equal([1, 2, 3, 7]));
}
-@safe unittest //12566
+// https://issues.dlang.org/show_bug.cgi?id=12566
+@safe unittest
{
auto dl2 = DList!int([2,7]);
dl2.removeFront();
@@ -1005,14 +1076,16 @@ private:
assert(dl2.empty, "not empty?!");
}
-@safe unittest //13076
+// https://issues.dlang.org/show_bug.cgi?id=13076
+@safe unittest
{
DList!int list;
assert(list.empty);
list.clear();
}
-@safe unittest //13425
+// https://issues.dlang.org/show_bug.cgi?id=13425
+@safe unittest
{
import std.range : drop, take;
auto list = DList!int([1,2,3,4,5]);
@@ -1022,7 +1095,8 @@ private:
assert(r.empty); // fails
}
-@safe unittest //14300
+// https://issues.dlang.org/show_bug.cgi?id=14300
+@safe unittest
{
interface ITest {}
static class Test : ITest {}
@@ -1030,7 +1104,8 @@ private:
DList!ITest().insertBack(new Test());
}
-@safe unittest //15263
+// https://issues.dlang.org/show_bug.cgi?id=15263
+@safe unittest
{
import std.range : iota;
auto a = DList!int();
diff --git a/libphobos/src/std/container/package.d b/libphobos/src/std/container/package.d
index fa0050555d3..763da8b52b0 100644
--- a/libphobos/src/std/container/package.d
+++ b/libphobos/src/std/container/package.d
@@ -6,7 +6,7 @@ This module defines generic containers.
Construction:
To implement the different containers both struct and class based
-approaches have been used. $(REF make, std,_container,util) allows for
+approaches have been used. $(REF make, std,container,util) allows for
uniform construction with either approach.
---
@@ -21,7 +21,7 @@ RedBlackTree!int rbTree2 = make!(RedBlackTree!int)(1, 2, 3);
Array!int array2 = make!(Array!int)(1, 2, 3);
---
-Note that $(D make) can infer the element type from the given arguments.
+Note that `make` can infer the element type from the given arguments.
---
import std.container;
@@ -34,7 +34,7 @@ Reference_semantics:
All containers have reference semantics, which means that after
assignment both variables refer to the same underlying data.
-To make a copy of a _container, use the $(D c._dup) _container primitive.
+To make a copy of a container, use the `c.dup` container primitive.
---
import std.container, std.range;
Array!int originalArray = make!(Array!int)(1, 2, 3);
@@ -52,7 +52,7 @@ secondArray[0] = 1;
assert(originalArray[0] == 12);
---
-$(B Attention:) If the _container is implemented as a class, using an
+$(B Attention:) If the container is implemented as a class, using an
uninitialized instance can cause a null pointer dereference.
---
@@ -62,8 +62,8 @@ RedBlackTree!int rbTree;
rbTree.insert(5); // null pointer dereference
---
-Using an uninitialized struct-based _container will work, because the struct
-intializes itself upon use; however, up to this point the _container will not
+Using an uninitialized struct-based container will work, because the struct
+intializes itself upon use; however, up to this point the container will not
have an identity and assignment does not create two references to the same
data.
@@ -85,11 +85,11 @@ array1.removeBack();
assert(array2.empty);
---
It is therefore recommended to always construct containers using
-$(REF make, std,_container,util).
+$(REF make, std,container,util).
-This is in fact necessary to put containers into another _container.
-For example, to construct an $(D Array) of ten empty $(D Array)s, use
-the following that calls $(D make) ten times.
+This is in fact necessary to put containers into another container.
+For example, to construct an `Array` of ten empty `Array`s, use
+the following that calls `make` ten times.
---
import std.container, std.range;
@@ -103,47 +103,47 @@ This module consists of the following submodules:
$(UL
$(LI
- The $(MREF std, _container, array) module provides
+ The $(MREF std, container, array) module provides
an array type with deterministic control of memory, not reliant on
the GC unlike built-in arrays.
)
$(LI
- The $(MREF std, _container, binaryheap) module
+ The $(MREF std, container, binaryheap) module
provides a binary heap implementation that can be applied to any
user-provided random-access range.
)
$(LI
- The $(MREF std, _container, dlist) module provides
+ The $(MREF std, container, dlist) module provides
a doubly-linked list implementation.
)
$(LI
- The $(MREF std, _container, rbtree) module
+ The $(MREF std, container, rbtree) module
implements red-black trees.
)
$(LI
- The $(MREF std, _container, slist) module
+ The $(MREF std, container, slist) module
implements singly-linked lists.
)
$(LI
- The $(MREF std, _container, util) module contains
- some generic tools commonly used by _container implementations.
+ The $(MREF std, container, util) module contains
+ some generic tools commonly used by container implementations.
)
)
The_primary_range_of_a_container:
-While some _containers offer direct access to their elements e.g. via
-$(D opIndex), $(D c.front) or $(D c.back), access
-and modification of a _container's contents is generally done through
+While some containers offer direct access to their elements e.g. via
+`opIndex`, `c.front` or `c.back`, access
+and modification of a container's contents is generally done through
its primary $(MREF_ALTTEXT range, std, range) type,
-which is aliased as $(D C.Range). For example, the primary range type of
-$(D Array!int) is $(D Array!int.Range).
-
-If the documentation of a member function of a _container takes
-a parameter of type $(D Range), then it refers to the primary range type of
-this _container. Oftentimes $(D Take!Range) will be used, in which case
-the range refers to a span of the elements in the _container. Arguments to
-these parameters $(B must) be obtained from the same _container instance
+which is aliased as `C.Range`. For example, the primary range type of
+`Array!int` is `Array!int.Range`.
+
+If the documentation of a member function of a container takes
+a parameter of type `Range`, then it refers to the primary range type of
+this container. Oftentimes `Take!Range` will be used, in which case
+the range refers to a span of the elements in the container. Arguments to
+these parameters $(B must) be obtained from the same container instance
as the one being worked with. It is important to note that many generic range
algorithms return the same range type as their input range.
@@ -171,7 +171,7 @@ assert(array[].equal([1, 3]));
When any $(MREF_ALTTEXT range, std, range) can be passed as an argument to
a member function, the documention usually refers to the parameter's templated
-type as $(D Stuff).
+type as `Stuff`.
---
import std.algorithm.comparison : equal;
@@ -192,28 +192,28 @@ Container_primitives:
Containers do not form a class hierarchy, instead they implement a
common set of primitives (see table below). These primitives each guarantee
a specific worst case complexity and thus allow generic code to be written
-independently of the _container implementation.
+independently of the container implementation.
-For example the primitives $(D c.remove(r)) and $(D c.linearRemove(r)) both
-remove the sequence of elements in range $(D r) from the _container $(D c).
-The primitive $(D c.remove(r)) guarantees
+For example the primitives `c.remove(r)` and `c.linearRemove(r)` both
+remove the sequence of elements in range `r` from the container `c`.
+The primitive `c.remove(r)` guarantees
$(BIGOH n$(SUBSCRIPT r) log n$(SUBSCRIPT c)) complexity in the worst case and
-$(D c.linearRemove(r)) relaxes this guarantee to $(BIGOH n$(SUBSCRIPT c)).
+`c.linearRemove(r)` relaxes this guarantee to $(BIGOH n$(SUBSCRIPT c)).
-Since a sequence of elements can be removed from a $(MREF_ALTTEXT doubly linked list,std,_container,dlist)
-in constant time, $(D DList) provides the primitive $(D c.remove(r))
-as well as $(D c.linearRemove(r)). On the other hand
-$(MREF_ALTTEXT Array, std,_container, array) only offers $(D c.linearRemove(r)).
+Since a sequence of elements can be removed from a $(MREF_ALTTEXT doubly linked list,std,container,dlist)
+in constant time, `DList` provides the primitive `c.remove(r)`
+as well as `c.linearRemove(r)`. On the other hand
+$(MREF_ALTTEXT Array, std,container, array) only offers `c.linearRemove(r)`.
The following table describes the common set of primitives that containers
-implement. A _container need not implement all primitives, but if a
+implement. A container need not implement all primitives, but if a
primitive is implemented, it must support the syntax described in the $(B
syntax) column with the semantics described in the $(B description) column, and
it must not have a worst-case complexity worse than denoted in big-O notation in
-the $(BIGOH &middot;) column. Below, $(D C) means a _container type, $(D c) is
-a value of _container type, $(D n$(SUBSCRIPT x)) represents the effective length of
-value $(D x), which could be a single element (in which case $(D n$(SUBSCRIPT x)) is
-$(D 1)), a _container, or a range.
+the $(BIGOH &middot;) column. Below, `C` means a container type, `c` is
+a value of container type, $(D n$(SUBSCRIPT x)) represents the effective length of
+value `x`, which could be a single element (in which case $(D n$(SUBSCRIPT x)) is
+`1`), a container, or a range.
$(BOOKTABLE Container primitives,
$(TR
@@ -222,284 +222,279 @@ $(TR
$(TH Description)
)
$(TR
- $(TDNW $(D C(x)))
+ $(TDNW `C(x)`)
$(TDNW $(D n$(SUBSCRIPT x)))
- $(TD Creates a _container of type $(D C) from either another _container or a range.
- The created _container must not be a null reference even if x is empty.)
+ $(TD Creates a container of type `C` from either another container or a range.
+ The created container must not be a null reference even if x is empty.)
)
$(TR
- $(TDNW $(D c.dup))
+ $(TDNW `c.dup`)
$(TDNW $(D n$(SUBSCRIPT c)))
- $(TD Returns a duplicate of the _container.)
+ $(TD Returns a duplicate of the container.)
)
$(TR
$(TDNW $(D c ~ x))
$(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x)))
- $(TD Returns the concatenation of $(D c) and $(D r). $(D x) may be a single
+ $(TD Returns the concatenation of `c` and `r`. `x` may be a single
element or an input range.)
)
$(TR
$(TDNW $(D x ~ c))
$(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x)))
- $(TD Returns the concatenation of $(D x) and $(D c). $(D x) may be a
+ $(TD Returns the concatenation of `x` and `c`. `x` may be a
single element or an input range type.)
)
$(LEADINGROWN 3, Iteration
)
$(TR
- $(TD $(D c.Range))
+ $(TD `c.Range`)
$(TD)
- $(TD The primary range type associated with the _container.)
+ $(TD The primary range type associated with the container.)
)
$(TR
- $(TD $(D c[]))
+ $(TD `c[]`)
$(TDNW $(D log n$(SUBSCRIPT c)))
$(TD Returns a range
- iterating over the entire _container, in a _container-defined order.)
+ iterating over the entire container, in a container-defined order.)
)
$(TR
$(TDNW $(D c[a .. b]))
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Fetches a portion of the _container from key $(D a) to key $(D b).)
+ $(TD Fetches a portion of the container from key `a` to key `b`.)
)
$(LEADINGROWN 3, Capacity
)
$(TR
- $(TD $(D c.empty))
- $(TD $(D 1))
- $(TD Returns $(D true) if the _container has no elements, $(D false) otherwise.)
+ $(TD `c.empty`)
+ $(TD `1`)
+ $(TD Returns `true` if the container has no elements, `false` otherwise.)
)
$(TR
- $(TD $(D c.length))
+ $(TD `c.length`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Returns the number of elements in the _container.)
+ $(TD Returns the number of elements in the container.)
)
$(TR
$(TDNW $(D c.length = n))
$(TDNW $(D n$(SUBSCRIPT c) + n))
- $(TD Forces the number of elements in the _container to $(D n).
- If the _container ends up growing, the added elements are initialized
- in a _container-dependent manner (usually with $(D T.init)).)
+ $(TD Forces the number of elements in the container to `n`.
+ If the container ends up growing, the added elements are initialized
+ in a container-dependent manner (usually with `T.init`).)
)
$(TR
- $(TD $(D c.capacity))
+ $(TD `c.capacity`)
$(TDNW $(D log n$(SUBSCRIPT c)))
$(TD Returns the maximum number of elements that can be stored in the
- _container without triggering a reallocation.)
+ container without triggering a reallocation.)
)
$(TR
- $(TD $(D c.reserve(x)))
+ $(TD `c.reserve(x)`)
$(TD $(D n$(SUBSCRIPT c)))
- $(TD Forces $(D capacity) to at least $(D x) without reducing it.)
+ $(TD Forces `capacity` to at least `x` without reducing it.)
)
$(LEADINGROWN 3, Access
)
$(TR
- $(TDNW $(D c.front))
+ $(TDNW `c.front`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Returns the first element of the _container, in a _container-defined order.)
+ $(TD Returns the first element of the container, in a container-defined order.)
)
$(TR
- $(TDNW $(D c.moveFront))
+ $(TDNW `c.moveFront`)
$(TDNW $(D log n$(SUBSCRIPT c)))
$(TD Destructively reads and returns the first element of the
- _container. The slot is not removed from the _container; it is left
- initialized with $(D T.init). This routine need not be defined if $(D
- front) returns a $(D ref).)
+ container. The slot is not removed from the container; it is left
+ initialized with `T.init`. This routine need not be defined if $(D
+ front) returns a `ref`.)
)
$(TR
$(TDNW $(D c.front = v))
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Assigns $(D v) to the first element of the _container.)
+ $(TD Assigns `v` to the first element of the container.)
)
$(TR
- $(TDNW $(D c.back))
+ $(TDNW `c.back`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Returns the last element of the _container, in a _container-defined order.)
+ $(TD Returns the last element of the container, in a container-defined order.)
)
$(TR
- $(TDNW $(D c.moveBack))
+ $(TDNW `c.moveBack`)
$(TDNW $(D log n$(SUBSCRIPT c)))
$(TD Destructively reads and returns the last element of the
- _container. The slot is not removed from the _container; it is left
- initialized with $(D T.init). This routine need not be defined if $(D
- front) returns a $(D ref).)
+ container. The slot is not removed from the container; it is left
+ initialized with `T.init`. This routine need not be defined if $(D
+ front) returns a `ref`.)
)
$(TR
$(TDNW $(D c.back = v))
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Assigns $(D v) to the last element of the _container.)
+ $(TD Assigns `v` to the last element of the container.)
)
$(TR
- $(TDNW $(D c[x]))
+ $(TDNW `c[x]`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Provides indexed access into the _container. The index type is
- _container-defined. A _container may define several index types (and
+ $(TD Provides indexed access into the container. The index type is
+ container-defined. A container may define several index types (and
consequently overloaded indexing).)
)
$(TR
- $(TDNW $(D c.moveAt(x)))
+ $(TDNW `c.moveAt(x)`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Destructively reads and returns the value at position $(D x). The slot
- is not removed from the _container; it is left initialized with $(D
+ $(TD Destructively reads and returns the value at position `x`. The slot
+ is not removed from the container; it is left initialized with $(D
T.init).)
)
$(TR
$(TDNW $(D c[x] = v))
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Sets element at specified index into the _container.)
+ $(TD Sets element at specified index into the container.)
)
$(TR
$(TDNW $(D c[x] $(I op)= v))
$(TDNW $(D log n$(SUBSCRIPT c)))
$(TD Performs read-modify-write operation at specified index into the
- _container.)
+ container.)
)
$(LEADINGROWN 3, Operations
)
$(TR
$(TDNW $(D e in c))
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Returns nonzero if e is found in $(D c).)
+ $(TD Returns nonzero if e is found in `c`.)
)
$(TR
- $(TDNW $(D c.lowerBound(v)))
+ $(TDNW `c.lowerBound(v)`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Returns a range of all elements strictly less than $(D v).)
+ $(TD Returns a range of all elements strictly less than `v`.)
)
$(TR
- $(TDNW $(D c.upperBound(v)))
+ $(TDNW `c.upperBound(v)`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Returns a range of all elements strictly greater than $(D v).)
+ $(TD Returns a range of all elements strictly greater than `v`.)
)
$(TR
- $(TDNW $(D c.equalRange(v)))
+ $(TDNW `c.equalRange(v)`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Returns a range of all elements in $(D c) that are equal to $(D v).)
+ $(TD Returns a range of all elements in `c` that are equal to `v`.)
)
$(LEADINGROWN 3, Modifiers
)
$(TR
$(TDNW $(D c ~= x))
$(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x)))
- $(TD Appends $(D x) to $(D c). $(D x) may be a single element or an input range type.)
+ $(TD Appends `x` to `c`. `x` may be a single element or an input range type.)
)
$(TR
- $(TDNW $(D c.clear()))
+ $(TDNW `c.clear()`)
$(TDNW $(D n$(SUBSCRIPT c)))
- $(TD Removes all elements in $(D c).)
+ $(TD Removes all elements in `c`.)
)
$(TR
- $(TDNW $(D c.insert(x)))
+ $(TDNW `c.insert(x)`)
$(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c)))
- $(TD Inserts $(D x) in $(D c) at a position (or positions) chosen by $(D c).)
+ $(TD Inserts `x` in `c` at a position (or positions) chosen by `c`.)
)
$(TR
- $(TDNW $(D c.stableInsert(x)))
+ $(TDNW `c.stableInsert(x)`)
$(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c)))
- $(TD Same as $(D c.insert(x)), but is guaranteed to not invalidate any ranges.)
+ $(TD Same as `c.insert(x)`, but is guaranteed to not invalidate any ranges.)
)
$(TR
- $(TDNW $(D c.linearInsert(v)))
+ $(TDNW `c.linearInsert(v)`)
$(TDNW $(D n$(SUBSCRIPT c)))
- $(TD Same as $(D c.insert(v)) but relaxes complexity to linear.)
+ $(TD Same as `c.insert(v)` but relaxes complexity to linear.)
)
$(TR
- $(TDNW $(D c.stableLinearInsert(v)))
+ $(TDNW `c.stableLinearInsert(v)`)
$(TDNW $(D n$(SUBSCRIPT c)))
- $(TD Same as $(D c.stableInsert(v)) but relaxes complexity to linear.)
+ $(TD Same as `c.stableInsert(v)` but relaxes complexity to linear.)
)
$(TR
- $(TDNW $(D c.removeAny()))
+ $(TDNW `c.removeAny()`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Removes some element from $(D c) and returns it.)
+ $(TD Removes some element from `c` and returns it.)
)
$(TR
- $(TDNW $(D c.stableRemoveAny()))
+ $(TDNW `c.stableRemoveAny()`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Same as $(D c.removeAny()), but is guaranteed to not invalidate any
+ $(TD Same as `c.removeAny()`, but is guaranteed to not invalidate any
iterators.)
)
$(TR
- $(TDNW $(D c.insertFront(v)))
+ $(TDNW `c.insertFront(v)`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Inserts $(D v) at the front of $(D c).)
+ $(TD Inserts `v` at the front of `c`.)
)
$(TR
- $(TDNW $(D c.stableInsertFront(v)))
+ $(TDNW `c.stableInsertFront(v)`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Same as $(D c.insertFront(v)), but guarantees no ranges will be
+ $(TD Same as `c.insertFront(v)`, but guarantees no ranges will be
invalidated.)
)
$(TR
- $(TDNW $(D c.insertBack(v)))
+ $(TDNW `c.insertBack(v)`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Inserts $(D v) at the back of $(D c).)
+ $(TD Inserts `v` at the back of `c`.)
)
$(TR
- $(TDNW $(D c.stableInsertBack(v)))
+ $(TDNW `c.stableInsertBack(v)`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Same as $(D c.insertBack(v)), but guarantees no ranges will be
+ $(TD Same as `c.insertBack(v)`, but guarantees no ranges will be
invalidated.)
)
$(TR
- $(TDNW $(D c.removeFront()))
+ $(TDNW `c.removeFront()`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Removes the element at the front of $(D c).)
+ $(TD Removes the element at the front of `c`.)
)
$(TR
- $(TDNW $(D c.stableRemoveFront()))
+ $(TDNW `c.stableRemoveFront()`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Same as $(D c.removeFront()), but guarantees no ranges will be
+ $(TD Same as `c.removeFront()`, but guarantees no ranges will be
invalidated.)
)
$(TR
- $(TDNW $(D c.removeBack()))
+ $(TDNW `c.removeBack()`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Removes the value at the back of $(D c).)
+ $(TD Removes the value at the back of `c`.)
)
$(TR
- $(TDNW $(D c.stableRemoveBack()))
+ $(TDNW `c.stableRemoveBack()`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Same as $(D c.removeBack()), but guarantees no ranges will be
+ $(TD Same as `c.removeBack()`, but guarantees no ranges will be
invalidated.)
)
$(TR
- $(TDNW $(D c.remove(r)))
+ $(TDNW `c.remove(r)`)
$(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c)))
- $(TD Removes range $(D r) from $(D c).)
+ $(TD Removes range `r` from `c`.)
)
$(TR
- $(TDNW $(D c.stableRemove(r)))
+ $(TDNW `c.stableRemove(r)`)
$(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c)))
- $(TD Same as $(D c.remove(r)), but guarantees iterators are not
+ $(TD Same as `c.remove(r)`, but guarantees iterators are not
invalidated.)
)
$(TR
- $(TDNW $(D c.linearRemove(r)))
+ $(TDNW `c.linearRemove(r)`)
$(TDNW $(D n$(SUBSCRIPT c)))
- $(TD Removes range $(D r) from $(D c).)
+ $(TD Removes range `r` from `c`.)
)
$(TR
- $(TDNW $(D c.stableLinearRemove(r)))
+ $(TDNW `c.stableLinearRemove(r)`)
$(TDNW $(D n$(SUBSCRIPT c)))
- $(TD Same as $(D c.linearRemove(r)), but guarantees iterators are not
+ $(TD Same as `c.linearRemove(r)`, but guarantees iterators are not
invalidated.)
)
$(TR
- $(TDNW $(D c.removeKey(k)))
+ $(TDNW `c.removeKey(k)`)
$(TDNW $(D log n$(SUBSCRIPT c)))
- $(TD Removes an element from $(D c) by using its key $(D k).
- The key's type is defined by the _container.)
-)
-$(TR
- $(TDNW $(D ))
- $(TDNW $(D ))
- $(TD )
+ $(TD Removes an element from `c` by using its key `k`.
+ The key's type is defined by the container.)
)
)
-Source: $(PHOBOSSRC std/_container/package.d)
+Source: $(PHOBOSSRC std/container/package.d)
Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code
copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
@@ -522,10 +517,10 @@ public import std.container.slist;
import std.meta;
-/* The following documentation and type $(D TotalContainer) are
+/* The following documentation and type `TotalContainer` are
intended for developers only.
-$(D TotalContainer) is an unimplemented container that illustrates a
+`TotalContainer` is an unimplemented container that illustrates a
host of primitives that a container may define. It is to some extent
the bottom of the conceptual container hierarchy. A given container
most often will choose to only implement a subset of these primitives,
@@ -533,7 +528,7 @@ and define its own additional ones. Adhering to the standard primitive
names below allows generic code to work independently of containers.
Things to remember: any container must be a reference type, whether
-implemented as a $(D class) or $(D struct). No primitive below
+implemented as a `class` or `struct`. No primitive below
requires the container to escape addresses of elements, which means
that compliant containers can be defined to use reference counting or
other deterministic memory management techniques.
@@ -545,32 +540,32 @@ the ones below, lest user code gets confused.
Complexity of operations should be interpreted as "at least as good
as". If an operation is required to have $(BIGOH n) complexity, it
could have anything lower than that, e.g. $(BIGOH log(n)). Unless
-specified otherwise, $(D n) inside a $(BIGOH) expression stands for
+specified otherwise, `n` inside a $(BIGOH) expression stands for
the number of elements in the container.
*/
struct TotalContainer(T)
{
/**
-If the container has a notion of key-value mapping, $(D KeyType)
+If the container has a notion of key-value mapping, `KeyType`
defines the type of the key of the container.
*/
alias KeyType = T;
/**
If the container has a notion of multikey-value mapping, $(D
-KeyTypes[k]), where $(D k) is a zero-based unsigned number, defines
-the type of the $(D k)th key of the container.
+KeyTypes[k]), where `k` is a zero-based unsigned number, defines
+the type of the `k`th key of the container.
-A container may define both $(D KeyType) and $(D KeyTypes), e.g. in
+A container may define both `KeyType` and `KeyTypes`, e.g. in
the case it has the notion of primary/preferred key.
*/
alias KeyTypes = AliasSeq!T;
/**
-If the container has a notion of key-value mapping, $(D ValueType)
+If the container has a notion of key-value mapping, `ValueType`
defines the type of the value of the container. Typically, a map-style
-container mapping values of type $(D K) to values of type $(D V)
-defines $(D KeyType) to be $(D K) and $(D ValueType) to be $(D V).
+container mapping values of type `K` to values of type `V`
+defines `KeyType` to be `K` and `ValueType` to be `V`.
*/
alias ValueType = T;
@@ -587,89 +582,89 @@ Generally a container may define several types of ranges.
+/
@property bool empty()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
@property ref T front() //ref return optional
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
@property void front(T value) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
T moveFront()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
void popFront()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
@property ref T back() //ref return optional
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
@property void back(T value) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
T moveBack()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
void popBack()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
T opIndex(size_t i) //ref return optional
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
void opIndexAssign(size_t i, T value) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
T opIndexUnary(string op)(size_t i) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
void opIndexOpAssign(string op)(size_t i, T value) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
T moveAt(size_t i)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
@property size_t length()
{
- assert(0);
+ assert(0, "Not implemented");
}
}
/**
-Property returning $(D true) if and only if the container has no
+Property returning `true` if and only if the container has no
elements.
Complexity: $(BIGOH 1)
*/
@property bool empty()
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
@@ -680,7 +675,7 @@ Complexity: $(BIGOH n).
*/
@property TotalContainer dup()
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
@@ -690,7 +685,7 @@ Complexity: $(BIGOH log(n)).
*/
@property size_t length()
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
@@ -701,11 +696,11 @@ Complexity: $(BIGOH log(n)).
*/
@property size_t capacity()
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Ensures sufficient capacity to accommodate $(D n) elements.
+Ensures sufficient capacity to accommodate `n` elements.
Postcondition: $(D capacity >= n)
@@ -714,19 +709,19 @@ $(BIGOH 1).
*/
void reserve(size_t e)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
Returns a range that iterates over all elements of the container, in a
container-defined order. The container should choose the most
-convenient and fast method of iteration for $(D opSlice()).
+convenient and fast method of iteration for `opSlice()`.
Complexity: $(BIGOH log(n))
*/
Range opSlice()
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
@@ -737,42 +732,42 @@ Complexity: $(BIGOH log(n))
*/
Range opSlice(size_t a, size_t b)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Forward to $(D opSlice().front) and $(D opSlice().back), respectively.
+Forward to `opSlice().front` and `opSlice().back`, respectively.
Complexity: $(BIGOH log(n))
*/
@property ref T front() //ref return optional
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
@property void front(T value) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
T moveFront()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
@property ref T back() //ref return optional
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
@property void back(T value) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// Ditto
T moveBack()
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
@@ -780,27 +775,27 @@ Indexing operators yield or modify the value at a specified index.
*/
ref T opIndex(KeyType) //ref return optional
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
void opIndexAssign(KeyType i, T value) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
T opIndexUnary(string op)(KeyType i) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
void opIndexOpAssign(string op)(KeyType i, T value) //Only when front does not return by ref
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
T moveAt(KeyType i)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
@@ -808,55 +803,55 @@ $(D k in container) returns true if the given key is in the container.
*/
bool opBinaryRight(string op)(KeyType k) if (op == "in")
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Returns a range of all elements containing $(D k) (could be empty or a
+Returns a range of all elements containing `k` (could be empty or a
singleton range).
*/
Range equalRange(KeyType k)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Returns a range of all elements with keys less than $(D k) (could be
+Returns a range of all elements with keys less than `k` (could be
empty or a singleton range). Only defined by containers that store
data sorted at all times.
*/
Range lowerBound(KeyType k)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Returns a range of all elements with keys larger than $(D k) (could be
+Returns a range of all elements with keys larger than `k` (could be
empty or a singleton range). Only defined by containers that store
data sorted at all times.
*/
Range upperBound(KeyType k)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Returns a new container that's the concatenation of $(D this) and its
-argument. $(D opBinaryRight) is only defined if $(D Stuff) does not
-define $(D opBinary).
+Returns a new container that's the concatenation of `this` and its
+argument. `opBinaryRight` is only defined if `Stuff` does not
+define `opBinary`.
Complexity: $(BIGOH n + m), where m is the number of elements in $(D
stuff)
*/
TotalContainer opBinary(string op)(Stuff rhs) if (op == "~")
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~")
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
@@ -864,25 +859,25 @@ Forwards to $(D insertAfter(this[], stuff)).
*/
void opOpAssign(string op)(Stuff stuff) if (op == "~")
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
Removes all contents from the container. The container decides how $(D
capacity) is affected.
-Postcondition: $(D empty)
+Postcondition: `empty`
Complexity: $(BIGOH n)
*/
void clear()
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Sets the number of elements in the container to $(D newSize). If $(D
-newSize) is greater than $(D length), the added elements are added to
+Sets the number of elements in the container to `newSize`. If $(D
+newSize) is greater than `length`, the added elements are added to
unspecified positions in the container and initialized with $(D
.init).
@@ -892,48 +887,48 @@ Postcondition: $(D _length == newLength)
*/
@property void length(size_t newLength)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Inserts $(D stuff) in an unspecified position in the
+Inserts `stuff` in an unspecified position in the
container. Implementations should choose whichever insertion means is
the most advantageous for the container, but document the exact
-behavior. $(D stuff) can be a value convertible to the element type of
+behavior. `stuff` can be a value convertible to the element type of
the container, or a range of values convertible to it.
-The $(D stable) version guarantees that ranges iterating over the
+The `stable` version guarantees that ranges iterating over the
container are never invalidated. Client code that counts on
-non-invalidating insertion should use $(D stableInsert). Such code would
+non-invalidating insertion should use `stableInsert`. Such code would
not compile against containers that don't support it.
Returns: The number of elements added.
-Complexity: $(BIGOH m * log(n)), where $(D m) is the number of
-elements in $(D stuff)
+Complexity: $(BIGOH m * log(n)), where `m` is the number of
+elements in `stuff`
*/
size_t insert(Stuff)(Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
///ditto
size_t stableInsert(Stuff)(Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Same as $(D insert(stuff)) and $(D stableInsert(stuff)) respectively,
+Same as `insert(stuff)` and `stableInsert(stuff)` respectively,
but relax the complexity constraint to linear.
*/
size_t linearInsert(Stuff)(Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
///ditto
size_t stableLinearInsert(Stuff)(Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
@@ -943,7 +938,7 @@ value that's the most advantageous for the container. The stable version
behaves the same, but guarantees that ranges iterating over the container
are never invalidated.
-Precondition: $(D !empty)
+Precondition: `!empty`
Returns: The element removed.
@@ -951,16 +946,16 @@ Complexity: $(BIGOH log(n)).
*/
T removeAny()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
T stableRemoveAny()
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Inserts $(D value) to the front or back of the container. $(D stuff)
+Inserts `value` to the front or back of the container. `stuff`
can be a value convertible to the container's element type or a range
of values convertible to it. The stable version behaves the same, but
guarantees that ranges iterating over the container are never
@@ -972,22 +967,22 @@ Complexity: $(BIGOH log(n)).
*/
size_t insertFront(Stuff)(Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t stableInsertFront(Stuff)(Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t insertBack(Stuff)(Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t stableInsertBack(T value)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
@@ -997,34 +992,34 @@ the container are never invalidated. The optional parameter $(D
howMany) instructs removal of that many elements. If $(D howMany > n),
all elements are removed and no exception is thrown.
-Precondition: $(D !empty)
+Precondition: `!empty`
Complexity: $(BIGOH log(n)).
*/
void removeFront()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
void stableRemoveFront()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
void removeBack()
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
void stableRemoveBack()
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Removes $(D howMany) values at the front or back of the
+Removes `howMany` values at the front or back of the
container. Unlike the unparameterized versions above, these functions
-do not throw if they could not remove $(D howMany) elements. Instead,
+do not throw if they could not remove `howMany` elements. Instead,
if $(D howMany > n), all elements are removed. The returned value is
the effective number of elements removed. The stable version behaves
the same, but guarantees that ranges iterating over the container are
@@ -1036,40 +1031,40 @@ Complexity: $(BIGOH howMany * log(n)).
*/
size_t removeFront(size_t howMany)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t stableRemoveFront(size_t howMany)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t removeBack(size_t howMany)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t stableRemoveBack(size_t howMany)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Removes all values corresponding to key $(D k).
+Removes all values corresponding to key `k`.
-Complexity: $(BIGOH m * log(n)), where $(D m) is the number of
+Complexity: $(BIGOH m * log(n)), where `m` is the number of
elements with the same key.
Returns: The number of elements removed.
*/
size_t removeKey(KeyType k)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Inserts $(D stuff) before, after, or instead range $(D r), which must
-be a valid range previously extracted from this container. $(D stuff)
+Inserts `stuff` before, after, or instead range `r`, which must
+be a valid range previously extracted from this container. `stuff`
can be a value convertible to the container's element type or a range
of objects convertible to it. The stable version behaves the same, but
guarantees that ranges iterating over the container are never
@@ -1077,76 +1072,76 @@ invalidated.
Returns: The number of values inserted.
-Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff)
+Complexity: $(BIGOH n + m), where `m` is the length of `stuff`
*/
size_t insertBefore(Stuff)(Range r, Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t stableInsertBefore(Stuff)(Range r, Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t insertAfter(Stuff)(Range r, Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t stableInsertAfter(Stuff)(Range r, Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t replace(Stuff)(Range r, Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
size_t stableReplace(Stuff)(Range r, Stuff stuff)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Removes all elements belonging to $(D r), which must be a range
+Removes all elements belonging to `r`, which must be a range
obtained originally from this container. The stable version behaves the
same, but guarantees that ranges iterating over the container are
never invalidated.
Returns: A range spanning the remaining elements in the container that
-initially were right after $(D r).
+initially were right after `r`.
-Complexity: $(BIGOH m * log(n)), where $(D m) is the number of
-elements in $(D r)
+Complexity: $(BIGOH m * log(n)), where `m` is the number of
+elements in `r`
*/
Range remove(Range r)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
Range stableRemove(Range r)
{
- assert(0);
+ assert(0, "Not implemented");
}
/**
-Same as $(D remove) above, but has complexity relaxed to linear.
+Same as `remove` above, but has complexity relaxed to linear.
Returns: A range spanning the remaining elements in the container that
-initially were right after $(D r).
+initially were right after `r`.
Complexity: $(BIGOH n)
*/
Range linearRemove(Range r)
{
- assert(0);
+ assert(0, "Not implemented");
}
/// ditto
Range stableLinearRemove(Range r)
{
- assert(0);
+ assert(0, "Not implemented");
}
}
diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d
index 5e31ac2989b..f8e70fc0882 100644
--- a/libphobos/src/std/container/rbtree.d
+++ b/libphobos/src/std/container/rbtree.d
@@ -3,7 +3,7 @@ This module implements a red-black tree container.
This module is a submodule of $(MREF std, container).
-Source: $(PHOBOSSRC std/container/_rbtree.d)
+Source: $(PHOBOSSRC std/container/rbtree.d)
Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code
copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
@@ -61,7 +61,7 @@ import std.functional : binaryFun;
public import std.container.util;
-version (unittest) debug = RBDoChecks;
+version (StdUnittest) debug = RBDoChecks;
//debug = RBDoChecks;
@@ -136,9 +136,12 @@ struct RBNode(V)
* Set the left child. Also updates the new child's parent node. This
* does not update the previous child.
*
+ * $(RED Warning: If the node this is called on is a local variable, a stack pointer can be
+ * escaped through `newNode.parent`. It's marked `@trusted` only for backwards compatibility.)
+ *
* Returns newNode
*/
- @property Node left(Node newNode)
+ @property Node left(return scope Node newNode) @trusted
{
_left = newNode;
if (newNode !is null)
@@ -150,9 +153,12 @@ struct RBNode(V)
* Set the right child. Also updates the new child's parent node. This
* does not update the previous child.
*
+ * $(RED Warning: If the node this is called on is a local variable, a stack pointer can be
+ * escaped through `newNode.parent`. It's marked `@trusted` only for backwards compatibility.)
+ *
* Returns newNode
*/
- @property Node right(Node newNode)
+ @property Node right(return scope Node newNode) @trusted
{
_right = newNode;
if (newNode !is null)
@@ -183,9 +189,9 @@ struct RBNode(V)
Node rotateR()
in
{
- assert(_left !is null);
+ assert(_left !is null, "left node must not be null");
}
- body
+ do
{
// sets _left._parent also
if (isLeftNode)
@@ -226,9 +232,9 @@ struct RBNode(V)
Node rotateL()
in
{
- assert(_right !is null);
+ assert(_right !is null, "right node must not be null");
}
- body
+ do
{
// sets _right._parent also
if (isLeftNode)
@@ -255,9 +261,9 @@ struct RBNode(V)
@property bool isLeftNode() const
in
{
- assert(_parent !is null);
+ assert(_parent !is null, "parent must not be null");
}
- body
+ do
{
return _parent._left is &this;
}
@@ -542,7 +548,8 @@ struct RBNode(V)
_parent.right = null;
}
- // clean references to help GC - Bugzilla 12915
+ // clean references to help GC
+ // https://issues.dlang.org/show_bug.cgi?id=12915
_left = _right = _parent = null;
return ret;
@@ -662,7 +669,7 @@ private struct RBRange(N)
}
/**
- * Returns $(D true) if the range is _empty
+ * Returns `true` if the range is _empty
*/
@property bool empty() const
{
@@ -706,7 +713,7 @@ private struct RBRange(N)
}
/**
- * Trivial _save implementation, needed for $(D isForwardRange).
+ * Trivial _save implementation, needed for `isForwardRange`.
*/
@property RBRange save()
{
@@ -723,15 +730,15 @@ private struct RBRange(N)
*
* To use a different comparison than $(D "a < b"), pass a different operator string
* that can be used by $(REF binaryFun, std,functional), or pass in a
- * function, delegate, functor, or any type where $(D less(a, b)) results in a $(D bool)
+ * function, delegate, functor, or any type where $(D less(a, b)) results in a `bool`
* value.
*
* Note that less should produce a strict ordering. That is, for two unequal
- * elements $(D a) and $(D b), $(D less(a, b) == !less(b, a)). $(D less(a, a)) should
- * always equal $(D false).
+ * elements `a` and `b`, $(D less(a, b) == !less(b, a)). $(D less(a, a)) should
+ * always equal `false`.
*
- * If $(D allowDuplicates) is set to $(D true), then inserting the same element more than
- * once continues to add more elements. If it is $(D false), duplicate elements are
+ * If `allowDuplicates` is set to `true`, then inserting the same element more than
+ * once continues to add more elements. If it is `false`, duplicate elements are
* ignored on insertion. If duplicates are allowed, then new elements are
* inserted after all existing duplicate elements.
*/
@@ -745,11 +752,11 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
alias _less = binaryFun!less;
- version (unittest)
+ version (StdUnittest)
{
static if (is(typeof(less) == string))
{
- private enum doUnittest = isIntegral!T && (less == "a < b" || less == "a > b");
+ private enum doUnittest = is(byte : T) && isIntegral!T && (less == "a < b" || less == "a > b");
}
else
enum doUnittest = false;
@@ -789,7 +796,8 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
private void _setup()
{
- assert(!_end); //Make sure that _setup isn't run more than once.
+ //Make sure that _setup isn't run more than once.
+ assert(!_end, "Setup must only be run once");
_begin = _end = allocate();
}
@@ -804,7 +812,7 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
}
/**
- * The range types for $(D RedBlackTree)
+ * The range types for `RedBlackTree`
*/
alias Range = RBRange!(RBNode*);
alias ConstRange = RBRange!(const(RBNode)*); /// Ditto
@@ -874,9 +882,12 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
}
}
- // add an element to the tree, returns the node added, or the existing node
- // if it has already been added and allowDuplicates is false
- private auto _add(Elem n)
+ /* add an element to the tree, returns the node added, or the existing node
+ * if it has already been added and allowDuplicates is false
+ * Returns:
+ * true if node was added
+ */
+ private bool _add(return Elem n)
{
Node result;
static if (!allowDuplicates)
@@ -884,7 +895,8 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
if (!_end.left)
{
- _end.left = _begin = result = allocate(n);
+ result = allocate(n);
+ (() @trusted { _end.left = _begin = result; }) ();
}
else
{
@@ -900,7 +912,8 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
//
// add to right of new parent
//
- newParent.left = result = allocate(n);
+ result = allocate(n);
+ (() @trusted { newParent.left = result; }) ();
break;
}
}
@@ -921,7 +934,8 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
//
// add to right of new parent
//
- newParent.right = result = allocate(n);
+ result = allocate(n);
+ (() @trusted { newParent.right = result; }) ();
break;
}
}
@@ -930,19 +944,16 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
if (_begin.left)
_begin = _begin.left;
}
-
static if (allowDuplicates)
{
result.setColor(_end);
debug(RBDoChecks)
check();
++_length;
- return result;
+ return true;
}
else
{
- import std.typecons : Tuple;
-
if (added)
{
++_length;
@@ -950,16 +961,16 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
}
debug(RBDoChecks)
check();
- return Tuple!(bool, "added", Node, "n")(added, result);
+ return added;
}
}
/**
- * Check if any elements exist in the container. Returns $(D false) if at least
+ * Check if any elements exist in the container. Returns `false` if at least
* one element exists.
*/
- @property bool empty()
+ @property bool empty() const // pure, nothrow, @safe, @nogc: are inferred
{
return _end.left is null;
}
@@ -1025,7 +1036,7 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
*
* Complexity: $(BIGOH 1)
*/
- Elem front()
+ inout(Elem) front() inout
{
return _begin.value;
}
@@ -1035,13 +1046,13 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
*
* Complexity: $(BIGOH log(n))
*/
- Elem back()
+ inout(Elem) back() inout
{
return _end.prev.value;
}
/++
- $(D in) operator. Check to see if the given element exists in the
+ `in` operator. Check to see if the given element exists in the
container.
Complexity: $(BIGOH log(n))
@@ -1075,7 +1086,7 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
auto thisRange = this[];
auto thatRange = that[];
- return equal!(function(Elem a, Elem b) => !_less(a,b) && !_less(b,a))
+ return equal!((Elem a, Elem b) => !_less(a,b) && !_less(b,a))
(thisRange, thatRange);
}
@@ -1095,6 +1106,134 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
}
/**
+ * Generates a hash for the tree. Note that with a custom comparison function
+ * it may not hold that if two rbtrees are equal, the hashes of the trees
+ * will be equal.
+ */
+ override size_t toHash() nothrow @safe
+ {
+ size_t hash = cast(size_t) 0x6b63_616c_4264_6552UL;
+ foreach (ref e; this[])
+ // As in boost::hash_combine
+ // https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine
+ hash += .hashOf(e) + 0x9e3779b9 + (hash << 6) + (hash >>> 2);
+ return hash;
+ }
+
+ static if (doUnittest) @system unittest
+ {
+ auto t1 = new RedBlackTree(1,2,3,4);
+ auto t2 = new RedBlackTree(1,2,3,4);
+ auto t3 = new RedBlackTree(1,2,3,5);
+ auto t4 = new RedBlackTree(1,2,3,4,5);
+
+ assert(t1.toHash() == t2.toHash);
+
+ assert(t1.toHash() != t3.toHash);
+ assert(t2.toHash() != t3.toHash);
+
+ assert(t3.toHash() != t4.toHash);
+ assert(t1.toHash() != t4.toHash);
+
+ // empty tree
+ auto t5 = new RedBlackTree();
+ auto t6 = new RedBlackTree();
+
+ assert(t5.toHash() == t6.toHash());
+
+ auto t7 = new RedBlackTree!string("red", "black");
+ auto t8 = new RedBlackTree!string("white", "black");
+ auto t9 = new RedBlackTree!string("red", "black");
+
+ assert(t7.toHash() == t9.toHash());
+ assert(t7.toHash() != t8.toHash());
+
+ static struct MyInt
+ {
+ int x;
+
+ @safe:
+
+ this(int init_x)
+ {
+ x = init_x;
+ }
+
+ size_t toHash() const nothrow
+ {
+ return typeid(x).getHash(&x) ^ 0xF0F0_F0F0;
+ }
+
+ int opCmp(const MyInt that) const
+ {
+ return (x > that.x) - (x < that.x);
+ }
+
+ bool opEquals(const MyInt that) const
+ {
+ return (this.x == that.x);
+ }
+ }
+
+ auto rbt1 = new RedBlackTree!MyInt(MyInt(1), MyInt(2), MyInt(3), MyInt(4));
+ auto rbt2 = new RedBlackTree!MyInt(MyInt(1), MyInt(2), MyInt(3), MyInt(4));
+
+ assert(rbt1.toHash() == rbt2.toHash());
+ assert(rbt1.toHash() != t1.toHash());
+
+ auto rbt3 = new RedBlackTree!MyInt(MyInt(4), MyInt(2), MyInt(3), MyInt(4));
+
+ assert(rbt1.toHash() != rbt3.toHash());
+
+ class MyInt2
+ {
+ int x;
+
+ this(int init_x)
+ {
+ x = init_x;
+ }
+
+ override size_t toHash() const @safe nothrow
+ {
+ return typeid(x).getHash(&x) ^ 0xF0F0_F0F0;
+ }
+
+ int opCmp(const MyInt2 that) const
+ {
+ return (x > that.x) - (x < that.x);
+ }
+
+ bool opEquals(const MyInt2 that) const
+ {
+ return (this.x == that.x);
+ }
+ }
+
+ static bool nullSafeLess(scope const MyInt2 a, scope const MyInt2 b)
+ {
+ return a is null ? b !is null : (b !is null && a < b);
+ }
+
+ auto rbt4 = new RedBlackTree!MyInt2(new MyInt2(1), new MyInt2(9), new MyInt2(3), new MyInt2(42));
+ auto rbt5 = new RedBlackTree!MyInt2(new MyInt2(1), new MyInt2(9), new MyInt2(3), new MyInt2(42));
+ auto rbt6 = new RedBlackTree!(MyInt2, nullSafeLess)(new MyInt2(9), new MyInt2(3), new MyInt2(42));
+ auto rbt7 = new RedBlackTree!(MyInt2, nullSafeLess)(null);
+
+ assert(rbt6.toHash() != rbt5.toHash());
+ assert(rbt6.toHash() != rbt4.toHash());
+ assert(rbt6.toHash() != rbt7.toHash());
+ assert(rbt4.toHash() == rbt5.toHash());
+
+ auto rbt8 = new RedBlackTree!(MyInt2, nullSafeLess)(null, new MyInt2(9), null, new MyInt2(42));
+ auto rbt9 = new RedBlackTree!(MyInt2, nullSafeLess)(null, new MyInt2(9), null, new MyInt2(42));
+ auto rbt10 = new RedBlackTree!(MyInt2, nullSafeLess)(new MyInt2(94), null, new MyInt2(147));
+
+ assert(rbt8.toHash() == rbt9.toHash());
+ assert(rbt8.toHash() != rbt10.toHash());
+ }
+
+ /**
* Removes all elements from the container.
*
* Complexity: $(BIGOH 1)
@@ -1131,7 +1270,7 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
}
else
{
- return(_add(stuff).added ? 1 : 0);
+ return _add(stuff);
}
}
@@ -1143,7 +1282,9 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
*
* Complexity: $(BIGOH m * log(n))
*/
- size_t stableInsert(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem))
+ size_t stableInsert(Stuff)(scope Stuff stuff)
+ if (isInputRange!Stuff &&
+ isImplicitlyConvertible!(ElementType!Stuff, Elem))
{
size_t result = 0;
static if (allowDuplicates)
@@ -1158,8 +1299,7 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
{
foreach (e; stuff)
{
- if (_add(e).added)
- ++result;
+ result += _add(e);
}
}
return result;
@@ -1322,7 +1462,7 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
}
/++
- Removes the given $(D Take!Range) from the container
+ Removes the given `Take!Range` from the container
Returns: A range containing all of the elements that were after the
given range.
@@ -1377,7 +1517,7 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
/++
Removes elements from the container that are equal to the given values
according to the less comparator. One element is removed for each value
- given which is in the container. If $(D allowDuplicates) is true,
+ given which is in the container. If `allowDuplicates` is true,
duplicates are removed only if duplicate values are given.
Returns: The number of elements removed.
@@ -1401,7 +1541,7 @@ assert(equal(rbt[], [5]));
}
/++ Ditto +/
- size_t removeKey(U)(U[] elems)
+ size_t removeKey(U)(scope U[] elems)
if (isImplicitlyConvertible!(U, Elem))
{
immutable lenBefore = length;
@@ -1649,7 +1789,7 @@ assert(equal(rbt[], [5]));
* Check the tree for validity. This is called after every add or remove.
* This should only be enabled to debug the implementation of the RB Tree.
*/
- void check()
+ void check() @trusted
{
//
// check implementation of the tree
@@ -1712,7 +1852,8 @@ assert(equal(rbt[], [5]));
*/
static if (is(typeof((){FormatSpec!(char) fmt; formatValue((const(char)[]) {}, ConstRange.init, fmt);})))
{
- void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const {
+ void toString(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt) const
+ {
sink("RedBlackTree(");
sink.formatValue(this[], fmt);
sink(")");
@@ -1814,11 +1955,18 @@ assert(equal(rbt[], [5]));
test!byte();
}
+// https://issues.dlang.org/show_bug.cgi?id=19626
+@safe pure unittest
+{
+ enum T { a, b }
+ alias t = RedBlackTree!T;
+}
+
import std.range.primitives : isInputRange, ElementType;
import std.traits : isArray, isSomeString;
/++
- Convenience function for creating a $(D RedBlackTree!E) from a list of
+ Convenience function for creating a `RedBlackTree!E` from a list of
values.
Params:
@@ -2022,8 +2170,12 @@ if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init
@safe pure unittest
{
const rt1 = redBlackTree(5,4,3,2,1);
- static assert(is(typeof(rt1.length)));
- static assert(is(typeof(5 in rt1)));
+ void allQualifiers() pure nothrow @safe @nogc {
+ assert(!rt1.empty);
+ assert(rt1.length == 5);
+ assert(5 in rt1);
+ }
+ allQualifiers();
static assert(is(typeof(rt1.upperBound(3).front) == const(int)));
import std.algorithm.comparison : equal;
@@ -2037,21 +2189,24 @@ if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init
@safe pure unittest
{
immutable rt1 = redBlackTree(5,4,3,2,1);
+ static assert(is(typeof(rt1.empty)));
static assert(is(typeof(rt1.length)));
+ static assert(is(typeof(5 in rt1)));
static assert(is(typeof(rt1.upperBound(3).front) == immutable(int)));
import std.algorithm.comparison : equal;
assert(rt1.upperBound(2).equal([3, 4, 5]));
}
-// issue 15941
+// https://issues.dlang.org/show_bug.cgi?id=15941
@safe pure unittest
{
class C {}
RedBlackTree!(C, "cast(void*)a < cast(void*) b") tree;
}
-@safe pure unittest // const/immutable elements (issue 17519)
+// const/immutable elements (https://issues.dlang.org/show_bug.cgi?id=17519)
+@safe pure unittest
{
RedBlackTree!(immutable int) t1;
RedBlackTree!(const int) t2;
@@ -2063,3 +2218,13 @@ if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init
static assert(!__traits(compiles, *t3.front.p = 4));
assert(*t3.front.p == 1);
}
+
+// make sure the comparator can be a delegate
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto t = new RedBlackTree!(int, delegate(a, b) => a > b);
+ t.insert([1, 3, 5, 4, 2]);
+ assert(t[].equal([5, 4, 3, 2, 1]));
+}
diff --git a/libphobos/src/std/container/slist.d b/libphobos/src/std/container/slist.d
index 820b0bbac45..0b504b474a8 100644
--- a/libphobos/src/std/container/slist.d
+++ b/libphobos/src/std/container/slist.d
@@ -4,7 +4,7 @@ It can be used as a stack.
This module is a submodule of $(MREF std, container).
-Source: $(PHOBOSSRC std/container/_slist.d)
+Source: $(PHOBOSSRC std/container/slist.d)
Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
@@ -51,9 +51,10 @@ public import std.container.util;
Implements a simple and fast singly-linked list.
It can be used as a stack.
- $(D SList) uses reference semantics.
+ `SList` uses reference semantics.
*/
struct SList(T)
+if (!is(T == shared))
{
import std.exception : enforce;
import std.range : Take;
@@ -81,13 +82,13 @@ struct SList(T)
private ref inout(Node*) _first() @property @safe nothrow pure inout
{
- assert(_root);
+ assert(_root, "root pointer must not be null");
return _root._next;
}
private static Node * findLastNode(Node * n)
{
- assert(n);
+ assert(n, "Node n pointer must not be null");
auto ahead = n._next;
while (ahead)
{
@@ -99,7 +100,8 @@ struct SList(T)
private static Node * findLastNode(Node * n, size_t limit)
{
- assert(n && limit);
+ assert(n, "Node n pointer must not be null");
+ assert(limit, "limit must be greater than 0");
auto ahead = n._next;
while (ahead)
{
@@ -112,7 +114,7 @@ struct SList(T)
private static Node * findNode(Node * n, Node * findMe)
{
- assert(n);
+ assert(n, "Node n pointer must not be null");
auto ahead = n._next;
while (ahead != findMe)
{
@@ -123,6 +125,60 @@ struct SList(T)
return n;
}
+ private static Node* findNodeByValue(Node* n, T value)
+ {
+ if (!n) return null;
+ auto ahead = n._next;
+ while (ahead && ahead._payload != value)
+ {
+ n = ahead;
+ ahead = n._next;
+ }
+ return n;
+ }
+
+ private static auto createNodeChain(Stuff)(Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ import std.range : only;
+ return createNodeChain(only(stuff));
+ }
+
+ private static auto createNodeChain(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ static struct Chain
+ {
+ Node* first;
+ Node* last;
+ size_t length;
+ }
+
+ Chain ch;
+
+ foreach (item; stuff)
+ {
+ auto newNode = new Node(null, item);
+ (ch.first ? ch.last._next : ch.first) = newNode;
+ ch.last = newNode;
+ ++ch.length;
+ }
+
+ return ch;
+ }
+
+ private static size_t insertAfterNode(Stuff)(Node* n, Stuff stuff)
+ {
+ auto ch = createNodeChain(stuff);
+
+ if (!ch.length) return 0;
+
+ ch.last._next = n._next;
+ n._next = ch.first;
+
+ return ch.length;
+ }
+
/**
Constructor taking a number of nodes
*/
@@ -132,7 +188,7 @@ Constructor taking a number of nodes
}
/**
-Constructor taking an input range
+Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
*/
this(Stuff)(Stuff stuff)
if (isInputRange!Stuff
@@ -145,8 +201,8 @@ Constructor taking an input range
/**
Comparison for equality.
-Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of
-elements in $(D rhs).
+Complexity: $(BIGOH min(n, n1)) where `n1` is the number of
+elements in `rhs`.
*/
bool opEquals(const SList rhs) const
{
@@ -217,7 +273,7 @@ Defines the container's primary range, which embodies a forward range.
}
/**
-Property returning $(D true) if and only if the container has no
+Property returning `true` if and only if the container has no
elements.
Complexity: $(BIGOH 1)
@@ -253,7 +309,7 @@ Complexity: $(BIGOH 1)
}
/**
-Forward to $(D opSlice().front).
+Forward to `opSlice().front`.
Complexity: $(BIGOH 1)
*/
@@ -271,37 +327,41 @@ Complexity: $(BIGOH 1)
}
/**
-Returns a new $(D SList) that's the concatenation of $(D this) and its
-argument. $(D opBinaryRight) is only defined if $(D Stuff) does not
-define $(D opBinary).
+Returns a new `SList` that's the concatenation of `this` and its
+argument. `opBinaryRight` is only defined if `Stuff` does not
+define `opBinary`.
*/
SList opBinary(string op, Stuff)(Stuff rhs)
if (op == "~" && is(typeof(SList(rhs))))
{
- auto toAdd = SList(rhs);
- if (empty) return toAdd;
- // TODO: optimize
- auto result = dup;
- auto n = findLastNode(result._first);
- n._next = toAdd._first;
- return result;
+ import std.range : chain, only;
+
+ static if (isInputRange!Stuff)
+ alias r = rhs;
+ else
+ auto r = only(rhs);
+
+ return SList(this[].chain(r));
}
/// ditto
SList opBinaryRight(string op, Stuff)(Stuff lhs)
if (op == "~" && !is(typeof(lhs.opBinary!"~"(this))) && is(typeof(SList(lhs))))
{
- auto toAdd = SList(lhs);
- if (empty) return toAdd;
- auto result = dup;
- result.insertFront(toAdd[]);
- return result;
+ import std.range : chain, only;
+
+ static if (isInputRange!Stuff)
+ alias r = lhs;
+ else
+ auto r = only(lhs);
+
+ return SList(r.chain(this[]));
}
/**
-Removes all contents from the $(D SList).
+Removes all contents from the `SList`.
-Postcondition: $(D empty)
+Postcondition: `empty`
Complexity: $(BIGOH 1)
*/
@@ -333,49 +393,26 @@ Complexity: $(BIGOH n)
}
/**
-Inserts $(D stuff) to the front of the container. $(D stuff) can be a
-value convertible to $(D T) or a range of objects convertible to $(D
+Inserts `stuff` to the front of the container. `stuff` can be a
+value convertible to `T` or a range of objects convertible to $(D
T). The stable version behaves the same, but guarantees that ranges
iterating over the container are never invalidated.
Returns: The number of elements inserted
-Complexity: $(BIGOH m), where $(D m) is the length of $(D stuff)
+Complexity: $(BIGOH m), where `m` is the length of `stuff`
*/
size_t insertFront(Stuff)(Stuff stuff)
- if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T))
{
initialize();
- size_t result;
- Node * n, newRoot;
- foreach (item; stuff)
- {
- auto newNode = new Node(null, item);
- (newRoot ? n._next : newRoot) = newNode;
- n = newNode;
- ++result;
- }
- if (!n) return 0;
- // Last node points to the old root
- n._next = _first;
- _first = newRoot;
- return result;
+ return insertAfterNode(_root, stuff);
}
/// ditto
- size_t insertFront(Stuff)(Stuff stuff)
- if (isImplicitlyConvertible!(Stuff, T))
- {
- initialize();
- auto newRoot = new Node(_first, stuff);
- _first = newRoot;
- return 1;
- }
-
-/// ditto
alias insert = insertFront;
-/// ditto
+ /// ditto
alias stableInsert = insert;
/// ditto
@@ -386,7 +423,7 @@ Picks one value in an unspecified position in the container, removes
it from the container, and returns it. The stable version behaves the same,
but guarantees that ranges iterating over the container are never invalidated.
-Precondition: $(D !empty)
+Precondition: `!empty`
Returns: The element removed.
@@ -409,7 +446,7 @@ Removes the value at the front of the container. The stable version
behaves the same, but guarantees that ranges iterating over the
container are never invalidated.
-Precondition: $(D !empty)
+Precondition: `!empty`
Complexity: $(BIGOH 1).
*/
@@ -423,9 +460,9 @@ Complexity: $(BIGOH 1).
alias stableRemoveFront = removeFront;
/**
-Removes $(D howMany) values at the front or back of the
+Removes `howMany` values at the front or back of the
container. Unlike the unparameterized versions above, these functions
-do not throw if they could not remove $(D howMany) elements. Instead,
+do not throw if they could not remove `howMany` elements. Instead,
if $(D howMany > n), all elements are removed. The returned value is
the effective number of elements removed. The stable version behaves
the same, but guarantees that ranges iterating over the container are
@@ -450,22 +487,22 @@ Complexity: $(BIGOH howMany * log(n)).
alias stableRemoveFront = removeFront;
/**
-Inserts $(D stuff) after range $(D r), which must be a range
+Inserts `stuff` after range `r`, which must be a range
previously extracted from this container. Given that all ranges for a
list end at the end of the list, this function essentially appends to
-the list and uses $(D r) as a potentially fast way to reach the last
-node in the list. Ideally $(D r) is positioned near or at the last
+the list and uses `r` as a potentially fast way to reach the last
+node in the list. Ideally `r` is positioned near or at the last
element of the list.
-$(D stuff) can be a value convertible to $(D T) or a range of objects
-convertible to $(D T). The stable version behaves the same, but
+`stuff` can be a value convertible to `T` or a range of objects
+convertible to `T`. The stable version behaves the same, but
guarantees that ranges iterating over the container are never
invalidated.
Returns: The number of values inserted.
-Complexity: $(BIGOH k + m), where $(D k) is the number of elements in
-$(D r) and $(D m) is the length of $(D stuff).
+Complexity: $(BIGOH k + m), where `k` is the number of elements in
+`r` and `m` is the length of `stuff`.
Example:
--------------------
@@ -478,6 +515,7 @@ assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"]));
*/
size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T))
{
initialize();
if (!_first)
@@ -487,27 +525,25 @@ assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"]));
}
enforce(r._head);
auto n = findLastNode(r._head);
- SList tmp;
- auto result = tmp.insertFront(stuff);
- n._next = tmp._first;
- return result;
+ return insertAfterNode(n, stuff);
}
/**
-Similar to $(D insertAfter) above, but accepts a range bounded in
+Similar to `insertAfter` above, but accepts a range bounded in
count. This is important for ensuring fast insertions in the middle of
-the list. For fast insertions after a specified position $(D r), use
+the list. For fast insertions after a specified position `r`, use
$(D insertAfter(take(r, 1), stuff)). The complexity of that operation
-only depends on the number of elements in $(D stuff).
+only depends on the number of elements in `stuff`.
Precondition: $(D r.original.empty || r.maxLength > 0)
Returns: The number of values inserted.
-Complexity: $(BIGOH k + m), where $(D k) is the number of elements in
-$(D r) and $(D m) is the length of $(D stuff).
+Complexity: $(BIGOH k + m), where `k` is the number of elements in
+`r` and `m` is the length of `stuff`.
*/
size_t insertAfter(Stuff)(Take!Range r, Stuff stuff)
+ if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T))
{
auto orig = r.source;
if (!orig._head)
@@ -524,12 +560,7 @@ $(D r) and $(D m) is the length of $(D stuff).
orig.popFront();
}
// insert here
- SList tmp;
- tmp.initialize();
- tmp._first = orig._head._next;
- auto result = tmp.insertFront(stuff);
- orig._head._next = tmp._first;
- return result;
+ return insertAfterNode(orig._head, stuff);
}
/// ditto
@@ -555,7 +586,7 @@ Complexity: $(BIGOH n)
}
/**
-Removes a $(D Take!Range) from the list in linear time.
+Removes a `Take!Range` from the list in linear time.
Returns: A range comprehending the elements after the removed range.
@@ -589,6 +620,61 @@ Complexity: $(BIGOH n)
/// ditto
alias stableLinearRemove = linearRemove;
+
+/**
+Removes the first occurence of an element from the list in linear time.
+
+Returns: True if the element existed and was successfully removed, false otherwise.
+
+Params:
+ value = value of the node to be removed
+
+Complexity: $(BIGOH n)
+ */
+ bool linearRemoveElement(T value)
+ {
+ auto n1 = findNodeByValue(_root, value);
+
+ if (n1 && n1._next)
+ {
+ n1._next = n1._next._next;
+ return true;
+ }
+
+ return false;
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto e = SList!int();
+ auto b = e.linearRemoveElement(2);
+ assert(b == false);
+ assert(e.empty());
+ auto a = SList!int(-1, 1, 2, 1, 3, 4);
+ b = a.linearRemoveElement(1);
+ assert(equal(a[], [-1, 2, 1, 3, 4]));
+ assert(b == true);
+ b = a.linearRemoveElement(-1);
+ assert(b == true);
+ assert(equal(a[], [2, 1, 3, 4]));
+ b = a.linearRemoveElement(1);
+ assert(b == true);
+ assert(equal(a[], [2, 3, 4]));
+ b = a.linearRemoveElement(2);
+ assert(b == true);
+ b = a.linearRemoveElement(20);
+ assert(b == false);
+ assert(equal(a[], [3, 4]));
+ b = a.linearRemoveElement(4);
+ assert(b == true);
+ assert(equal(a[], [3]));
+ b = a.linearRemoveElement(3);
+ assert(b == true);
+ assert(a.empty());
+ a.linearRemoveElement(3);
}
@safe unittest
@@ -793,9 +879,9 @@ Complexity: $(BIGOH n)
auto s = make!(SList!int)(1, 2, 3);
}
+// https://issues.dlang.org/show_bug.cgi?id=5193
@safe unittest
{
- // 5193
static struct Data
{
const int val;
@@ -813,17 +899,17 @@ Complexity: $(BIGOH n)
assert(r.front == 1);
}
+// https://issues.dlang.org/show_bug.cgi?id=14920
@safe unittest
{
- // issue 14920
SList!int s;
s.insertAfter(s[], 1);
assert(s.front == 1);
}
+// https://issues.dlang.org/show_bug.cgi?id=15659
@safe unittest
{
- // issue 15659
SList!int s;
s.clear();
}
@@ -844,3 +930,11 @@ Complexity: $(BIGOH n)
s.reverse();
assert(s[].equal([3, 2, 1]));
}
+
+@safe unittest
+{
+ auto s = SList!int([4, 6, 8, 12, 16]);
+ auto d = s.dup;
+ assert(d !is s);
+ assert(d == s);
+}
diff --git a/libphobos/src/std/container/util.d b/libphobos/src/std/container/util.d
index 5be9e7d5470..cc273a24b71 100644
--- a/libphobos/src/std/container/util.d
+++ b/libphobos/src/std/container/util.d
@@ -3,7 +3,7 @@ This module contains some common utilities used by containers.
This module is a submodule of $(MREF std, container).
-Source: $(PHOBOSSRC std/container/_util.d)
+Source: $(PHOBOSSRC std/container/util.d)
Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
@@ -32,7 +32,7 @@ if (is(T == struct) || is(T == class))
// does not initialize its payload and is equivalent
// to a null reference. We therefore construct an empty container
// by passing an empty array to its constructor.
- // Issue #13872.
+ // https://issues.dlang.org/show_bug.cgi?id=13872.
static if (arguments.length == 0)
{
import std.range.primitives : ElementType;
@@ -84,7 +84,7 @@ if (is(T == struct) || is(T == class))
assert(equal(rtb2[], "ehlo"d));
}
-// Issue 8895
+// https://issues.dlang.org/show_bug.cgi?id=8895
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -162,7 +162,7 @@ if (!is(Container))
assert(equal(rbtmin[], [1, 2, 3]));
}
-// Issue 13872
+// https://issues.dlang.org/show_bug.cgi?id=13872
@system unittest
{
import std.container;
diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d
index 3560d134f58..d9db9b08d6b 100644
--- a/libphobos/src/std/conv.d
+++ b/libphobos/src/std/conv.d
@@ -4,12 +4,12 @@
A one-stop shop for converting values from one type to another.
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE,
$(TR $(TH Category) $(TH Functions))
$(TR $(TD Generic) $(TD
$(LREF asOriginalType)
$(LREF castFrom)
- $(LREF emplace)
$(LREF parse)
$(LREF to)
$(LREF toChars)
@@ -30,9 +30,9 @@ $(TR $(TD Exceptions) $(TD
$(LREF ConvException)
$(LREF ConvOverflowException)
))
-)
+))
-Copyright: Copyright Digital Mars 2007-.
+Copyright: Copyright The D Language Foundation 2007-.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -42,7 +42,7 @@ Authors: $(HTTP digitalmars.com, Walter Bright),
Adam D. Ruppe,
Kenji Hara
-Source: $(PHOBOSSRC std/_conv.d)
+Source: $(PHOBOSSRC std/conv.d)
*/
module std.conv;
@@ -52,6 +52,7 @@ public import std.ascii : LetterCase;
import std.meta;
import std.range.primitives;
import std.traits;
+import std.typecons : Flag, Yes, No, tuple;
// Same as std.string.format, but "self-importing".
// Helps reduce code and imports, particularly in static asserts.
@@ -74,6 +75,13 @@ class ConvException : Exception
mixin basicExceptionCtors;
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ assertThrown!ConvException(to!int("abc"));
+}
+
private auto convError(S, T)(S source, string fn = __FILE__, size_t ln = __LINE__)
{
string msg;
@@ -118,7 +126,7 @@ private auto parseError(lazy string msg, string fn = __FILE__, size_t ln = __LIN
private void parseCheck(alias source)(dchar c, string fn = __FILE__, size_t ln = __LINE__)
{
if (source.empty)
- throw parseError(text("unexpected end of input when expecting", "\"", c, "\""));
+ throw parseError(text("unexpected end of input when expecting \"", c, "\""));
if (source.front != c)
throw parseError(text("\"", c, "\" is missing"), fn, ln);
source.popFront();
@@ -129,7 +137,7 @@ private
T toStr(T, S)(S src)
if (isSomeString!T)
{
- // workaround for Bugzilla 14198
+ // workaround for https://issues.dlang.org/show_bug.cgi?id=14198
static if (is(S == bool) && is(typeof({ T s = "string"; })))
{
return src ? "true" : "false";
@@ -137,7 +145,8 @@ private
else
{
import std.array : appender;
- import std.format : FormatSpec, formatValue;
+ import std.format.spec : FormatSpec;
+ import std.format.write : formatValue;
auto w = appender!T();
FormatSpec!(ElementEncodingType!T) f;
@@ -159,7 +168,7 @@ private
template isNullToStr(S, T)
{
enum isNullToStr = isImplicitlyConvertible!(S, T) &&
- (is(Unqual!S == typeof(null))) && isExactSomeString!T;
+ (is(immutable S == immutable typeof(null))) && isExactSomeString!T;
}
}
@@ -175,6 +184,13 @@ class ConvOverflowException : ConvException
}
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ assertThrown!ConvOverflowException(to!ubyte(1_000_000));
+}
+
/**
The `to` template converts a value from one type _to another.
The source type is deduced and the target type must be specified, for example the
@@ -290,6 +306,29 @@ template to(T)
}
/**
+ Conversion from string types to char types enforces the input
+ to consist of a single code point, and said code point must
+ fit in the target type. Otherwise, $(LREF ConvException) is thrown.
+ */
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+
+ assert(to!char("a") == 'a');
+ assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
+ assert(to!wchar("ñ") == 'ñ');
+ assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
+ assert(to!dchar("😃") == '😃');
+
+ // Using wstring or dstring as source type does not affect the result
+ assert(to!char("a"w) == 'a');
+ assert(to!char("a"d) == 'a');
+
+ // Two code points cannot be converted to a single one
+ assertThrown(to!char("ab"));
+}
+
+/**
* Converting an array _to another array type works by converting each
* element in turn. Associative arrays can be converted _to associative
* arrays as long as keys and values can in turn be converted.
@@ -347,21 +386,21 @@ template to(T)
* Stringize conversion from all types is supported.
* $(UL
* $(LI String _to string conversion works for any two string types having
- * ($(D char), $(D wchar), $(D dchar)) character widths and any
- * combination of qualifiers (mutable, $(D const), or $(D immutable)).)
+ * (`char`, `wchar`, `dchar`) character widths and any
+ * combination of qualifiers (mutable, `const`, or `immutable`).)
* $(LI Converts array (other than strings) _to string.
- * Each element is converted by calling $(D to!T).)
+ * Each element is converted by calling `to!T`.)
* $(LI Associative array _to string conversion.
- * Each element is printed by calling $(D to!T).)
- * $(LI Object _to string conversion calls $(D toString) against the object or
- * returns $(D "null") if the object is null.)
- * $(LI Struct _to string conversion calls $(D toString) against the struct if
+ * Each element is converted by calling `to!T`.)
+ * $(LI Object _to string conversion calls `toString` against the object or
+ * returns `"null"` if the object is null.)
+ * $(LI Struct _to string conversion calls `toString` against the struct if
* it is defined.)
- * $(LI For structs that do not define $(D toString), the conversion _to string
+ * $(LI For structs that do not define `toString`, the conversion _to string
* produces the list of fields.)
* $(LI Enumerated types are converted _to strings as their symbolic names.)
- * $(LI Boolean values are printed as $(D "true") or $(D "false").)
- * $(LI $(D char), $(D wchar), $(D dchar) _to a string type.)
+ * $(LI Boolean values are converted to `"true"` or `"false"`.)
+ * $(LI `char`, `wchar`, `dchar` _to a string type.)
* $(LI Unsigned or signed integers _to strings.
* $(DL $(DT [special case])
* $(DD Convert integral value _to string in $(D_PARAM radix) radix.
@@ -370,9 +409,10 @@ template to(T)
* The characters A through Z are used to represent values 10 through 36
* and their case is determined by the $(D_PARAM letterCase) parameter.)))
* $(LI All floating point types _to all string types.)
- * $(LI Pointer to string conversions prints the pointer as a $(D size_t) value.
- * If pointer is $(D char*), treat it as C-style strings.
- * In that case, this function is $(D @system).))
+ * $(LI Pointer to string conversions convert the pointer to a `size_t` value.
+ * If pointer is `char*`, treat it as C-style strings.
+ * In that case, this function is `@system`.))
+ * See $(REF formatValue, std,format) on how toString should be defined.
*/
@system pure unittest // @system due to cast and ptr
{
@@ -414,6 +454,21 @@ template to(T)
assert(text(null) == "null");
}
+// Test `scope` inference of parameters of `text`
+@safe unittest
+{
+ static struct S
+ {
+ int* x; // make S a type with pointers
+ string toString() const scope
+ {
+ return "S";
+ }
+ }
+ scope S s;
+ assert(text("a", s) == "aS");
+}
+
// Tests for issue 11390
@safe pure /+nothrow+/ unittest
{
@@ -427,12 +482,12 @@ template to(T)
@safe pure unittest
{
import std.exception;
- foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
{
assertThrown!ConvException(to!T(" 0"));
assertThrown!ConvException(to!T(" 0", 8));
}
- foreach (T; AliasSeq!(float, double, real))
+ static foreach (T; AliasSeq!(float, double, real))
{
assertThrown!ConvException(to!T(" 0"));
}
@@ -449,6 +504,39 @@ template to(T)
assertThrown!ConvException(to!AA(" [1:1]"));
}
+// https://issues.dlang.org/show_bug.cgi?id=20623
+@safe pure nothrow unittest
+{
+ // static class C
+ // {
+ // override string toString() const
+ // {
+ // return "C()";
+ // }
+ // }
+
+ static struct S
+ {
+ bool b;
+ int i;
+ float f;
+ int[] a;
+ int[int] aa;
+ S* p;
+ // C c; // TODO: Fails because of hasToString
+
+ void fun() inout
+ {
+ static foreach (const idx; 0 .. this.tupleof.length)
+ {
+ {
+ const _ = this.tupleof[idx].to!string();
+ }
+ }
+ }
+ }
+}
+
/**
If the source type is implicitly convertible to the target type, $(D
to) simply performs the implicit conversion.
@@ -480,9 +568,10 @@ if (isImplicitlyConvertible!(S, T) &&
return value;
}
+// https://issues.dlang.org/show_bug.cgi?id=9523: Allow identity enum conversion
@safe pure nothrow unittest
{
- enum E { a } // Issue 9523 - Allow identity enum conversion
+ enum E { a }
auto e = to!E(E.a);
assert(e == E.a);
}
@@ -494,18 +583,18 @@ if (isImplicitlyConvertible!(S, T) &&
assert(a == b);
}
-// Tests for issue 6377
+// https://issues.dlang.org/show_bug.cgi?id=6377
@safe pure unittest
{
import std.exception;
// Conversion between same size
- foreach (S; AliasSeq!(byte, short, int, long))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (S; AliasSeq!(byte, short, int, long))
+ {{
alias U = Unsigned!S;
- foreach (Sint; AliasSeq!(S, const S, immutable S))
- foreach (Uint; AliasSeq!(U, const U, immutable U))
- {
+ static foreach (Sint; AliasSeq!(S, const S, immutable S))
+ static foreach (Uint; AliasSeq!(U, const U, immutable U))
+ {{
// positive overflow
Uint un = Uint.max;
assertThrown!ConvOverflowException(to!Sint(un),
@@ -515,53 +604,53 @@ if (isImplicitlyConvertible!(S, T) &&
Sint sn = -1;
assertThrown!ConvOverflowException(to!Uint(sn),
text(Sint.stringof, ' ', Uint.stringof, ' ', un));
- }
- }();
+ }}
+ }}
// Conversion between different size
- foreach (i, S1; AliasSeq!(byte, short, int, long))
- foreach ( S2; AliasSeq!(byte, short, int, long)[i+1..$])
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (i, S1; AliasSeq!(byte, short, int, long))
+ static foreach ( S2; AliasSeq!(byte, short, int, long)[i+1..$])
+ {{
alias U1 = Unsigned!S1;
alias U2 = Unsigned!S2;
static assert(U1.sizeof < S2.sizeof);
// small unsigned to big signed
- foreach (Uint; AliasSeq!(U1, const U1, immutable U1))
- foreach (Sint; AliasSeq!(S2, const S2, immutable S2))
- {
+ static foreach (Uint; AliasSeq!(U1, const U1, immutable U1))
+ static foreach (Sint; AliasSeq!(S2, const S2, immutable S2))
+ {{
Uint un = Uint.max;
assertNotThrown(to!Sint(un));
assert(to!Sint(un) == un);
- }
+ }}
// big unsigned to small signed
- foreach (Uint; AliasSeq!(U2, const U2, immutable U2))
- foreach (Sint; AliasSeq!(S1, const S1, immutable S1))
- {
+ static foreach (Uint; AliasSeq!(U2, const U2, immutable U2))
+ static foreach (Sint; AliasSeq!(S1, const S1, immutable S1))
+ {{
Uint un = Uint.max;
assertThrown(to!Sint(un));
- }
+ }}
static assert(S1.sizeof < U2.sizeof);
// small signed to big unsigned
- foreach (Sint; AliasSeq!(S1, const S1, immutable S1))
- foreach (Uint; AliasSeq!(U2, const U2, immutable U2))
- {
+ static foreach (Sint; AliasSeq!(S1, const S1, immutable S1))
+ static foreach (Uint; AliasSeq!(U2, const U2, immutable U2))
+ {{
Sint sn = -1;
assertThrown!ConvOverflowException(to!Uint(sn));
- }
+ }}
// big signed to small unsigned
- foreach (Sint; AliasSeq!(S2, const S2, immutable S2))
- foreach (Uint; AliasSeq!(U1, const U1, immutable U1))
- {
+ static foreach (Sint; AliasSeq!(S2, const S2, immutable S2))
+ static foreach (Uint; AliasSeq!(U1, const U1, immutable U1))
+ {{
Sint sn = -1;
assertThrown!ConvOverflowException(to!Uint(sn));
- }
- }();
+ }}
+ }}
}
/*
@@ -631,7 +720,7 @@ if (!isImplicitlyConvertible!(S, T) &&
/**
When target type supports 'converting construction', it is used.
-$(UL $(LI If target type is struct, $(D T(value)) is used.)
+$(UL $(LI If target type is struct, `T(value)` is used.)
$(LI If target type is class, $(D new T(value)) is used.))
*/
private T toImpl(T, S)(S value)
@@ -641,7 +730,7 @@ if (!isImplicitlyConvertible!(S, T) &&
return T(value);
}
-// Bugzilla 3961
+// https://issues.dlang.org/show_bug.cgi?id=3961
@safe pure unittest
{
struct Int
@@ -670,7 +759,7 @@ if (!isImplicitlyConvertible!(S, T) &&
Int3 i3 = to!Int3(1);
}
-// Bugzilla 6808
+// https://issues.dlang.org/show_bug.cgi?id=6808
@safe pure unittest
{
static struct FakeBigInt
@@ -835,9 +924,9 @@ if (!isImplicitlyConvertible!(S, T) &&
class C : B, I, J {}
class D : I {}
- foreach (m1; AliasSeq!(0,1,2,3,4)) // enumerate modifiers
- foreach (m2; AliasSeq!(0,1,2,3,4)) // ditto
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (m1; 0 .. 5) // enumerate modifiers
+ static foreach (m2; 0 .. 5) // ditto
+ {{
alias srcmod = AddModifier!m1;
alias tgtmod = AddModifier!m2;
@@ -871,7 +960,7 @@ if (!isImplicitlyConvertible!(S, T) &&
static assert(!is(typeof(to!(tgtmod!C)(srcmod!I.init)))); // I to C
static assert(!is(typeof(to!(tgtmod!J)(srcmod!I.init)))); // I to J
}
- }();
+ }}
}
/**
@@ -965,7 +1054,8 @@ if (!(isImplicitlyConvertible!(S, T) &&
}
import std.array : appender;
- import std.format : FormatSpec, formatValue;
+ import std.format.spec : FormatSpec;
+ import std.format.write : formatValue;
//Default case, delegate to format
//Note: we don't call toStr directly, to avoid duplicate work.
@@ -982,18 +1072,18 @@ if (!(isImplicitlyConvertible!(S, T) &&
}
}
-// Bugzilla 14042
+// https://issues.dlang.org/show_bug.cgi?id=14042
@system unittest
{
immutable(char)* ptr = "hello".ptr;
auto result = ptr.to!(char[]);
}
-// Bugzilla 8384
+// https://issues.dlang.org/show_bug.cgi?id=8384
@system unittest
{
void test1(T)(T lp, string cmp)
{
- foreach (e; AliasSeq!(char, wchar, dchar))
+ static foreach (e; AliasSeq!(char, wchar, dchar))
{
test2!(e[])(lp, cmp);
test2!(const(e)[])(lp, cmp);
@@ -1006,12 +1096,12 @@ if (!(isImplicitlyConvertible!(S, T) &&
assert(to!string(to!D(lp)) == cmp);
}
- foreach (e; AliasSeq!("Hello, world!", "Hello, world!"w, "Hello, world!"d))
+ static foreach (e; AliasSeq!("Hello, world!", "Hello, world!"w, "Hello, world!"d))
{
test1(e, "Hello, world!");
test1(e.ptr, "Hello, world!");
}
- foreach (e; AliasSeq!("", ""w, ""d))
+ static foreach (e; AliasSeq!("", ""w, ""d))
{
test1(e, "");
test1(e.ptr, "");
@@ -1024,10 +1114,11 @@ if (!(isImplicitlyConvertible!(S, T) &&
private T toImpl(T, S)(ref S value)
if (!(isImplicitlyConvertible!(S, T) &&
!isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) &&
- !isInfinite!S && isExactSomeString!T && !isCopyable!S)
+ !isInfinite!S && isExactSomeString!T && !isCopyable!S && !isStaticArray!S)
{
import std.array : appender;
- import std.format : FormatSpec, formatValue;
+ import std.format.spec : FormatSpec;
+ import std.format.write : formatValue;
auto w = appender!T();
FormatSpec!(ElementEncodingType!T) f;
@@ -1035,7 +1126,7 @@ if (!(isImplicitlyConvertible!(S, T) &&
return w.data;
}
-// Bugzilla 16108
+// https://issues.dlang.org/show_bug.cgi?id=16108
@system unittest
{
static struct A
@@ -1063,8 +1154,20 @@ if (!(isImplicitlyConvertible!(S, T) &&
assert(to!string(b) == "B(0, false)");
}
+// https://issues.dlang.org/show_bug.cgi?id=20070
+@safe unittest
+{
+ void writeThem(T)(ref inout(T) them)
+ {
+ assert(them.to!string == "[1, 2, 3, 4]");
+ }
+
+ const(uint)[4] vals = [ 1, 2, 3, 4 ];
+ writeThem(vals);
+}
+
/*
- Check whether type $(D T) can be used in a switch statement.
+ Check whether type `T` can be used in a switch statement.
This is useful for compile-time generation of switch case statements.
*/
private template isSwitchable(E)
@@ -1176,14 +1279,14 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum))
import std.exception;
// Conversion representing integer values with string
- foreach (Int; AliasSeq!(ubyte, ushort, uint, ulong))
+ static foreach (Int; AliasSeq!(ubyte, ushort, uint, ulong))
{
assert(to!string(Int(0)) == "0");
assert(to!string(Int(9)) == "9");
assert(to!string(Int(123)) == "123");
}
- foreach (Int; AliasSeq!(byte, short, int, long))
+ static foreach (Int; AliasSeq!(byte, short, int, long))
{
assert(to!string(Int(0)) == "0");
assert(to!string(Int(9)) == "9");
@@ -1224,7 +1327,7 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum))
a = new A;
assert(to!string(a) == "an A");
- // Bug 7660
+ // https://issues.dlang.org/show_bug.cgi?id=7660
class C { override string toString() const { return "C"; } }
struct S { C c; alias c this; }
S s; s.c = new C();
@@ -1264,12 +1367,13 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum))
// Conversion representing enum value with string
enum EB : bool { a = true }
enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned
- enum EI : int { a = -1, b = 0, c = 1 } // base type is signed (bug 7909)
+ // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909)
+ enum EI : int { a = -1, b = 0, c = 1 }
enum EF : real { a = 1.414, b = 1.732, c = 2.236 }
enum EC : char { a = 'x', b = 'y' }
enum ES : string { a = "aaa", b = "bbb" }
- foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES))
+ static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES))
{
assert(to! string(E.a) == "a"c);
assert(to!wstring(E.a) == "a"w);
@@ -1297,23 +1401,23 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum))
assert(to!string(E.doo) == "foo");
assert(to!string(E.bar) == "bar");
- foreach (S; AliasSeq!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[])))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[])))
+ {{
auto s1 = to!S(E.foo);
auto s2 = to!S(E.foo);
assert(s1 == s2);
// ensure we don't allocate when it's unnecessary
assert(s1 is s2);
- }
+ }}
- foreach (S; AliasSeq!(char[], wchar[], dchar[]))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[]))
+ {{
auto s1 = to!S(E.foo);
auto s2 = to!S(E.foo);
assert(s1 == s2);
// ensure each mutable array is unique
assert(s1 !is s2);
- }
+ }}
}
// ditto
@@ -1322,9 +1426,9 @@ if (isIntegral!S &&
isExactSomeString!T)
in
{
- assert(radix >= 2 && radix <= 36);
+ assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]");
}
-body
+do
{
alias EEType = Unqual!(ElementEncodingType!T);
@@ -1373,7 +1477,7 @@ body
@safe pure nothrow unittest
{
- foreach (Int; AliasSeq!(uint, ulong))
+ static foreach (Int; AliasSeq!(uint, ulong))
{
assert(to!string(Int(16), 16) == "10");
assert(to!string(Int(15), 2u) == "1111");
@@ -1383,7 +1487,7 @@ body
assert(to!string(Int(0x1234AF), 16u, LetterCase.lower) == "1234af");
}
- foreach (Int; AliasSeq!(int, long))
+ static foreach (Int; AliasSeq!(int, long))
{
assert(to!string(Int(-10), 10u) == "-10");
}
@@ -1402,6 +1506,12 @@ if (!isImplicitlyConvertible!(S, T) &&
(isNumeric!S || isSomeChar!S || isBoolean!S) &&
(isNumeric!T || isSomeChar!T || isBoolean!T) && !is(T == enum))
{
+ static if (isFloatingPoint!S && isIntegral!T)
+ {
+ import std.math.traits : isNaN;
+ if (value.isNaN) throw new ConvException("Input was NaN");
+ }
+
enum sSmallest = mostNegative!S;
enum tSmallest = mostNegative!T;
static if (sSmallest < 0)
@@ -1413,7 +1523,8 @@ if (!isImplicitlyConvertible!(S, T) &&
}
else
{
- static assert(tSmallest < 0);
+ static assert(tSmallest < 0,
+ "minimum value of T must be smaller than 0");
immutable good = value >= tSmallest;
}
if (!good)
@@ -1486,9 +1597,24 @@ if (!isImplicitlyConvertible!(S, T) &&
}
+@safe unittest
+{
+ import std.exception;
+ import std.math.traits : isNaN;
+
+ double d = double.nan;
+ float f = to!float(d);
+ assert(f.isNaN);
+ assert(to!double(f).isNaN);
+ assertThrown!ConvException(to!int(d));
+ assertThrown!ConvException(to!int(f));
+ auto ex = collectException(d.to!int);
+ assert(ex.msg == "Input was NaN");
+}
+
/**
Array-to-array conversion (except when target is a string type)
-converts each element in turn by using $(D to).
+converts each element in turn by using `to`.
*/
private T toImpl(T, S)(S value)
if (!isImplicitlyConvertible!(S, T) &&
@@ -1510,7 +1636,7 @@ if (!isImplicitlyConvertible!(S, T) &&
import std.array : appender;
auto w = appender!(E[])();
w.reserve(value.length);
- foreach (i, ref e; value)
+ foreach (ref e; value)
{
w.put(to!E(e));
}
@@ -1535,7 +1661,7 @@ if (!isImplicitlyConvertible!(S, T) &&
auto f = to!(float[][])(e);
assert(f[0] == b && f[1] == b);
- // Test for bug 8264
+ // Test for https://issues.dlang.org/show_bug.cgi?id=8264
struct Wrap
{
string wrap;
@@ -1543,7 +1669,7 @@ if (!isImplicitlyConvertible!(S, T) &&
}
Wrap[] warr = to!(Wrap[])(["foo", "bar"]); // should work
- // Issue 12633
+ // https://issues.dlang.org/show_bug.cgi?id=12633
import std.conv : to;
const s2 = ["10", "20"];
@@ -1573,7 +1699,7 @@ Associative array to associative array conversion converts each key
and each value in turn.
*/
private T toImpl(T, S)(S value)
-if (isAssociativeArray!S &&
+if (!isImplicitlyConvertible!(S, T) && isAssociativeArray!S &&
isAssociativeArray!T && !is(T == enum))
{
/* This code is potentially unsafe.
@@ -1602,7 +1728,9 @@ if (isAssociativeArray!S &&
auto b = to!(double[dstring])(a);
assert(b["0"d] == 1 && b["1"d] == 2);
}
-@safe unittest // Bugzilla 8705, from doc
+
+// https://issues.dlang.org/show_bug.cgi?id=8705, from doc
+@safe unittest
{
import std.exception;
int[string][double[int[]]] a;
@@ -1619,82 +1747,91 @@ if (isAssociativeArray!S &&
auto d = to!(immutable(short[immutable wstring])[immutable string[double[]]])(c);
}
-private void testIntegralToFloating(Integral, Floating)()
+@safe unittest
{
- Integral a = 42;
- auto b = to!Floating(a);
- assert(a == b);
- assert(a == to!Integral(b));
+ import std.algorithm.comparison : equal;
+ import std.array : byPair;
+
+ int[int] a;
+ assert(a.to!(int[int]) == a);
+ assert(a.to!(const(int)[int]).byPair.equal(a.byPair));
}
-private void testFloatingToIntegral(Floating, Integral)()
+@safe pure unittest
{
- import std.math : floatTraits, RealFormat;
-
- bool convFails(Source, Target, E)(Source src)
- {
- try
- auto t = to!Target(src);
- catch (E)
- return true;
- return false;
- }
-
- // convert some value
- Floating a = 4.2e1;
- auto b = to!Integral(a);
- assert(is(typeof(b) == Integral) && b == 42);
- // convert some negative value (if applicable)
- a = -4.2e1;
- static if (Integral.min < 0)
- {
- b = to!Integral(a);
- assert(is(typeof(b) == Integral) && b == -42);
- }
- else
+ static void testIntegralToFloating(Integral, Floating)()
{
- // no go for unsigned types
- assert(convFails!(Floating, Integral, ConvOverflowException)(a));
+ Integral a = 42;
+ auto b = to!Floating(a);
+ assert(a == b);
+ assert(a == to!Integral(b));
}
- // convert to the smallest integral value
- a = 0.0 + Integral.min;
- static if (Integral.min < 0)
+ static void testFloatingToIntegral(Floating, Integral)()
{
- a = -a; // -Integral.min not representable as an Integral
+ import std.math : floatTraits, RealFormat;
+
+ bool convFails(Source, Target, E)(Source src)
+ {
+ try
+ cast(void) to!Target(src);
+ catch (E)
+ return true;
+ return false;
+ }
+
+ // convert some value
+ Floating a = 4.2e1;
+ auto b = to!Integral(a);
+ assert(is(typeof(b) == Integral) && b == 42);
+ // convert some negative value (if applicable)
+ a = -4.2e1;
+ static if (Integral.min < 0)
+ {
+ b = to!Integral(a);
+ assert(is(typeof(b) == Integral) && b == -42);
+ }
+ else
+ {
+ // no go for unsigned types
+ assert(convFails!(Floating, Integral, ConvOverflowException)(a));
+ }
+ // convert to the smallest integral value
+ a = 0.0 + Integral.min;
+ static if (Integral.min < 0)
+ {
+ a = -a; // -Integral.min not representable as an Integral
+ assert(convFails!(Floating, Integral, ConvOverflowException)(a)
+ || Floating.sizeof <= Integral.sizeof
+ || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
+ }
+ a = 0.0 + Integral.min;
+ assert(to!Integral(a) == Integral.min);
+ --a; // no more representable as an Integral
assert(convFails!(Floating, Integral, ConvOverflowException)(a)
|| Floating.sizeof <= Integral.sizeof
|| floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
+ a = 0.0 + Integral.max;
+ assert(to!Integral(a) == Integral.max
+ || Floating.sizeof <= Integral.sizeof
+ || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
+ ++a; // no more representable as an Integral
+ assert(convFails!(Floating, Integral, ConvOverflowException)(a)
+ || Floating.sizeof <= Integral.sizeof
+ || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
+ // convert a value with a fractional part
+ a = 3.14;
+ assert(to!Integral(a) == 3);
+ a = 3.99;
+ assert(to!Integral(a) == 3);
+ static if (Integral.min < 0)
+ {
+ a = -3.14;
+ assert(to!Integral(a) == -3);
+ a = -3.99;
+ assert(to!Integral(a) == -3);
+ }
}
- a = 0.0 + Integral.min;
- assert(to!Integral(a) == Integral.min);
- --a; // no more representable as an Integral
- assert(convFails!(Floating, Integral, ConvOverflowException)(a)
- || Floating.sizeof <= Integral.sizeof
- || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
- a = 0.0 + Integral.max;
- assert(to!Integral(a) == Integral.max
- || Floating.sizeof <= Integral.sizeof
- || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
- ++a; // no more representable as an Integral
- assert(convFails!(Floating, Integral, ConvOverflowException)(a)
- || Floating.sizeof <= Integral.sizeof
- || floatTraits!Floating.realFormat == RealFormat.ieeeExtended53);
- // convert a value with a fractional part
- a = 3.14;
- assert(to!Integral(a) == 3);
- a = 3.99;
- assert(to!Integral(a) == 3);
- static if (Integral.min < 0)
- {
- a = -3.14;
- assert(to!Integral(a) == -3);
- a = -3.99;
- assert(to!Integral(a) == -3);
- }
-}
-@safe pure unittest
-{
alias AllInts = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong);
alias AllFloats = AliasSeq!(float, double, real);
alias AllNumerics = AliasSeq!(AllInts, AllFloats);
@@ -1781,9 +1918,12 @@ private void testFloatingToIntegral(Floating, Integral)()
foreach (T; AllNumerics)
{
T a = 42;
- assert(to!string(a) == "42");
- assert(to!wstring(a) == "42"w);
- assert(to!dstring(a) == "42"d);
+ string s = to!string(a);
+ assert(s == "42", s);
+ wstring ws = to!wstring(a);
+ assert(ws == "42"w, to!string(ws));
+ dstring ds = to!dstring(a);
+ assert(ds == "42"d, to!string(ds));
// array test
T[] b = new T[2];
b[0] = 42;
@@ -1814,7 +1954,9 @@ $(UL
*/
private T toImpl(T, S)(S value)
if (isInputRange!S && isSomeChar!(ElementEncodingType!S) &&
- !isExactSomeString!T && is(typeof(parse!T(value))))
+ !isExactSomeString!T && is(typeof(parse!T(value))) &&
+ // issue 20539
+ !(is(T == enum) && is(typeof(value == OriginalType!T.init)) && !isSomeString!(OriginalType!T)))
{
scope(success)
{
@@ -1843,26 +1985,27 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S) &&
@safe pure unittest
{
- // Issue 6668 - ensure no collaterals thrown
+ // https://issues.dlang.org/show_bug.cgi?id=6668
+ // ensure no collaterals thrown
try { to!uint("-1"); }
catch (ConvException e) { assert(e.next is null); }
}
@safe pure unittest
{
- foreach (Str; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (Str; AliasSeq!(string, wstring, dstring))
+ {{
Str a = "123";
assert(to!int(a) == 123);
assert(to!double(a) == 123);
- }
+ }}
- // 6255
+ // https://issues.dlang.org/show_bug.cgi?id=6255
auto n = to!int("FF", 16);
assert(n == 255);
}
-// bugzilla 15800
+// https://issues.dlang.org/show_bug.cgi?id=15800
@safe unittest
{
import std.utf : byCodeUnit, byChar, byWchar, byDchar;
@@ -1881,6 +2024,49 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S) &&
}
/**
+String, or string-like input range, to char type not directly
+supported by parse parses the first dchar of the source.
+
+Returns: the first code point of the input range, converted
+ to type T.
+
+Throws: ConvException if the input range contains more than
+ a single code point, or if the code point does not
+ fit into a code unit of type T.
+*/
+private T toImpl(T, S)(S value)
+if (isSomeChar!T && !is(typeof(parse!T(value))) &&
+ is(typeof(parse!dchar(value))))
+{
+ import std.utf : encode;
+
+ immutable dchar codepoint = parse!dchar(value);
+ if (!value.empty)
+ throw new ConvException(convFormat("Cannot convert \"%s\" to %s because it " ~
+ "contains more than a single code point.",
+ value, T.stringof));
+ T[dchar.sizeof / T.sizeof] decodedCodepoint;
+ if (encode(decodedCodepoint, codepoint) != 1)
+ throw new ConvException(convFormat("First code point '%s' of \"%s\" does not fit into a " ~
+ "single %s code unit", codepoint, value, T.stringof));
+ return decodedCodepoint[0];
+}
+
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+
+ assert(toImpl!wchar("a") == 'a');
+
+ assert(toImpl!char("a"d) == 'a');
+ assert(toImpl!char("a"w) == 'a');
+ assert(toImpl!wchar("a"d) == 'a');
+
+ assertThrown!ConvException(toImpl!wchar("ab"));
+ assertThrown!ConvException(toImpl!char("😃"d));
+}
+
+/**
Convert a value that is implicitly convertible to the enum base type
into an Enum value. If the value does not match any enum member values
a ConvException is thrown.
@@ -1913,6 +2099,48 @@ if (is(T == enum) && !is(S == enum)
assert(m1 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]);
}
+// https://issues.dlang.org/show_bug.cgi?id=20539
+@safe pure unittest
+{
+ import std.exception : assertNotThrown;
+
+ // To test that the bug is fixed it is required that the struct is static,
+ // otherwise, the frame pointer makes the test pass even if the bug is not
+ // fixed.
+
+ static struct A
+ {
+ auto opEquals(U)(U)
+ {
+ return true;
+ }
+ }
+
+ enum ColorA
+ {
+ red = A()
+ }
+
+ assertNotThrown("xxx".to!ColorA);
+
+ // This is a guard for the future.
+
+ struct B
+ {
+ auto opEquals(U)(U)
+ {
+ return true;
+ }
+ }
+
+ enum ColorB
+ {
+ red = B()
+ }
+
+ assertNotThrown("xxx".to!ColorB);
+}
+
/***************************************************************
Rounded conversion from floating point to integral.
@@ -1923,10 +2151,19 @@ template roundTo(Target)
{
Target roundTo(Source)(Source value)
{
- import std.math : trunc;
+ import core.math : abs = fabs;
+ import std.math.exponential : log2;
+ import std.math.rounding : trunc;
static assert(isFloatingPoint!Source);
static assert(isIntegral!Target);
+
+ // If value >= 2 ^^ (real.mant_dig - 1), the number is an integer
+ // and adding 0.5 won't work, but we allready know, that we do
+ // not have to round anything.
+ if (log2(abs(value)) >= real.mant_dig - 1)
+ return to!Target(value);
+
return to!Target(trunc(value + (value < 0 ? -0.5L : 0.5L)));
}
}
@@ -1949,7 +2186,7 @@ template roundTo(Target)
{
import std.exception;
// boundary values
- foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint))
+ static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint))
{
assert(roundTo!Int(Int.min - 0.4L) == Int.min);
assert(roundTo!Int(Int.max + 0.4L) == Int.max);
@@ -1958,24 +2195,61 @@ template roundTo(Target)
}
}
+@safe unittest
+{
+ import std.exception;
+ assertThrown!ConvException(roundTo!int(float.init));
+ auto ex = collectException(roundTo!int(float.init));
+ assert(ex.msg == "Input was NaN");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=5232
+@safe pure unittest
+{
+ static if (real.mant_dig >= 64)
+ ulong maxOdd = ulong.max;
+ else
+ ulong maxOdd = (1UL << real.mant_dig) - 1;
+
+ real r1 = maxOdd;
+ assert(roundTo!ulong(r1) == maxOdd);
+
+ real r2 = maxOdd - 1;
+ assert(roundTo!ulong(r2) == maxOdd - 1);
+
+ real r3 = maxOdd / 2;
+ assert(roundTo!ulong(r3) == maxOdd / 2);
+
+ real r4 = maxOdd / 2 + 1;
+ assert(roundTo!ulong(r4) == maxOdd / 2 + 1);
+
+ // this is only an issue on computers where real == double
+ long l = -((1L << double.mant_dig) - 1);
+ double r5 = l;
+ assert(roundTo!long(r5) == l);
+}
+
/**
-The $(D parse) family of functions works quite like the $(D to)
+The `parse` family of functions works quite like the `to`
family, except that:
$(OL
$(LI It only works with character ranges as input.)
$(LI It takes the input by reference. (This means that rvalues - such
- as string literals - are not accepted: use $(D to) instead.))
+ as string literals - are not accepted: use `to` instead.))
$(LI It advances the input to the position following the conversion.)
$(LI It does not throw if it could not convert the entire input.))
-This overload converts an character input range to a `bool`.
+This overload converts a character input range to a `bool`.
Params:
Target = the type to convert to
source = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ doCount = the flag for deciding to report the number of consumed characters
Returns:
- A `bool`
+$(UL
+ $(LI A `bool` if `doCount` is set to `No.doCount`)
+ $(LI A `tuple` containing a `bool` and a `size_t` if `doCount` is set to `Yes.doCount`))
Throws:
A $(LREF ConvException) if the range does not represent a `bool`.
@@ -1984,10 +2258,10 @@ Note:
All character input range conversions using $(LREF to) are forwarded
to `parse` and do not require lvalues.
*/
-Target parse(Target, Source)(ref Source source)
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source)
if (isInputRange!Source &&
isSomeChar!(ElementType!Source) &&
- is(Unqual!Target == bool))
+ is(immutable Target == immutable bool))
{
import std.ascii : toLower;
@@ -2018,7 +2292,16 @@ if (isInputRange!Source &&
static if (isNarrowString!Source)
source = cast(Source) s;
- return result;
+ static if (doCount)
+ {
+ if (result)
+ return tuple!("data", "count")(result, 4);
+ return tuple!("data", "count")(result, 5);
+ }
+ else
+ {
+ return result;
+ }
}
}
Lerr:
@@ -2028,9 +2311,19 @@ Lerr:
///
@safe unittest
{
+ import std.typecons : Flag, Yes, No;
auto s = "true";
bool b = parse!bool(s);
assert(b);
+ auto s2 = "true";
+ bool b2 = parse!(bool, string, No.doCount)(s2);
+ assert(b2);
+ auto s3 = "true";
+ auto b3 = parse!(bool, string, Yes.doCount)(s3);
+ assert(b3.data && b3.count == 4);
+ auto s4 = "falSE";
+ auto b4 = parse!(bool, string, Yes.doCount)(s4);
+ assert(!b4.data && b4.count == 5);
}
@safe unittest
@@ -2073,25 +2366,37 @@ to an integral value.
Params:
Target = the integral type to convert to
s = the lvalue of an input range
+ doCount = the flag for deciding to report the number of consumed characters
Returns:
- A number of type `Target`
+$(UL
+ $(LI A number of type `Target` if `doCount` is set to `No.doCount`)
+ $(LI A `tuple` containing a number of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
Throws:
A $(LREF ConvException) If an overflow occurred during conversion or
if no character of the input was meaningfully converted.
*/
-Target parse(Target, Source)(ref Source s)
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
if (isSomeChar!(ElementType!Source) &&
isIntegral!Target && !is(Target == enum))
{
static if (Target.sizeof < int.sizeof)
{
// smaller types are handled like integers
- auto v = .parse!(Select!(Target.min < 0, int, uint))(s);
- auto result = ()@trusted{ return cast(Target) v; }();
- if (result == v)
- return result;
+ auto v = .parse!(Select!(Target.min < 0, int, uint), Source, Yes.doCount)(s);
+ auto result = (() @trusted => cast (Target) v.data)();
+ if (result == v.data)
+ {
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result, v.count);
+ }
+ else
+ {
+ return result;
+ }
+ }
throw new ConvOverflowException("Overflow in integral conversion");
}
else
@@ -2116,6 +2421,8 @@ if (isSomeChar!(ElementType!Source) &&
alias source = s;
}
+ size_t count = 0;
+
if (source.empty)
goto Lerr;
@@ -2129,6 +2436,7 @@ if (isSomeChar!(ElementType!Source) &&
sign = true;
goto case '+';
case '+':
+ ++count;
source.popFront();
if (source.empty)
@@ -2147,6 +2455,7 @@ if (isSomeChar!(ElementType!Source) &&
{
Target v = cast(Target) c;
+ ++count;
source.popFront();
while (!source.empty)
@@ -2162,7 +2471,7 @@ if (isSomeChar!(ElementType!Source) &&
// Note: `v` can become negative here in case of parsing
// the most negative value:
v = cast(Target) (v * 10 + c);
-
+ ++count;
source.popFront();
}
else
@@ -2175,7 +2484,14 @@ if (isSomeChar!(ElementType!Source) &&
static if (isNarrowString!Source)
s = cast(Source) source;
- return v;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(v, count);
+ }
+ else
+ {
+ return v;
+ }
}
Lerr:
static if (isNarrowString!Source)
@@ -2188,10 +2504,15 @@ Lerr:
///
@safe pure unittest
{
+ import std.typecons : Flag, Yes, No;
string s = "123";
auto a = parse!int(s);
assert(a == 123);
+ string s1 = "123";
+ auto a1 = parse!(int, string, Yes.doCount)(s1);
+ assert(a1.data == 123 && a1.count == 3);
+
// parse only accepts lvalues
static assert(!__traits(compiles, parse!int("123")));
}
@@ -2200,6 +2521,7 @@ Lerr:
@safe pure unittest
{
import std.string : tr;
+ import std.typecons : Flag, Yes, No;
string test = "123 \t 76.14";
auto a = parse!uint(test);
assert(a == 123);
@@ -2209,11 +2531,22 @@ Lerr:
auto b = parse!double(test);
assert(b == 76.14);
assert(test == "");
+
+ string test2 = "123 \t 76.14";
+ auto a2 = parse!(uint, string, Yes.doCount)(test2);
+ assert(a2.data == 123 && a2.count == 3);
+ assert(test2 == " \t 76.14");// parse bumps string
+ test2 = tr(test2, " \t\n\r", "", "d"); // skip ws
+ assert(test2 == "76.14");
+ auto b2 = parse!(double, string, Yes.doCount)(test2);
+ assert(b2.data == 76.14 && b2.count == 5);
+ assert(test2 == "");
+
}
@safe pure unittest
{
- foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
{
{
assert(to!Int("0") == 0);
@@ -2308,74 +2641,75 @@ Lerr:
@safe pure unittest
{
import std.exception;
+
+ immutable string[] errors =
+ [
+ "",
+ "-",
+ "+",
+ "-+",
+ " ",
+ " 0",
+ "0 ",
+ "- 0",
+ "1-",
+ "xx",
+ "123h",
+ "-+1",
+ "--1",
+ "+-1",
+ "++1",
+ ];
+
+ immutable string[] unsignedErrors =
+ [
+ "+5",
+ "-78",
+ ];
+
// parsing error check
- foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
{
- {
- immutable string[] errors1 =
- [
- "",
- "-",
- "+",
- "-+",
- " ",
- " 0",
- "0 ",
- "- 0",
- "1-",
- "xx",
- "123h",
- "-+1",
- "--1",
- "+-1",
- "++1",
- ];
- foreach (j, s; errors1)
- assertThrown!ConvException(to!Int(s));
- }
+ foreach (j, s; errors)
+ assertThrown!ConvException(to!Int(s));
// parse!SomeUnsigned cannot parse head sign.
static if (isUnsigned!Int)
{
- immutable string[] errors2 =
- [
- "+5",
- "-78",
- ];
- foreach (j, s; errors2)
+ foreach (j, s; unsignedErrors)
assertThrown!ConvException(to!Int(s));
}
}
+ immutable string[] positiveOverflowErrors =
+ [
+ "128", // > byte.max
+ "256", // > ubyte.max
+ "32768", // > short.max
+ "65536", // > ushort.max
+ "2147483648", // > int.max
+ "4294967296", // > uint.max
+ "9223372036854775808", // > long.max
+ "18446744073709551616", // > ulong.max
+ ];
// positive overflow check
- foreach (i, Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
- {
- immutable string[] errors =
- [
- "128", // > byte.max
- "256", // > ubyte.max
- "32768", // > short.max
- "65536", // > ushort.max
- "2147483648", // > int.max
- "4294967296", // > uint.max
- "9223372036854775808", // > long.max
- "18446744073709551616", // > ulong.max
- ];
- foreach (j, s; errors[i..$])
+ static foreach (i, Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ {
+ foreach (j, s; positiveOverflowErrors[i..$])
assertThrown!ConvOverflowException(to!Int(s));
}
+ immutable string[] negativeOverflowErrors =
+ [
+ "-129", // < byte.min
+ "-32769", // < short.min
+ "-2147483649", // < int.min
+ "-9223372036854775809", // < long.min
+ ];
// negative overflow check
- foreach (i, Int; AliasSeq!(byte, short, int, long))
- {
- immutable string[] errors =
- [
- "-129", // < byte.min
- "-32769", // < short.min
- "-2147483649", // < int.min
- "-9223372036854775809", // < long.min
- ];
- foreach (j, s; errors[i..$])
+ static foreach (i, Int; AliasSeq!(byte, short, int, long))
+ {
+ foreach (j, s; negativeOverflowErrors[i..$])
assertThrown!ConvOverflowException(to!Int(s));
}
}
@@ -2416,9 +2750,16 @@ Lerr:
assertCTFEable!({ string s = "1234abc"; assert(parse! int(s) == 1234 && s == "abc"); });
assertCTFEable!({ string s = "-1234abc"; assert(parse! int(s) == -1234 && s == "abc"); });
assertCTFEable!({ string s = "1234abc"; assert(parse!uint(s) == 1234 && s == "abc"); });
+
+ assertCTFEable!({ string s = "1234abc"; assert(parse!( int, string, Yes.doCount)(s) ==
+ tuple( 1234, 4) && s == "abc"); });
+ assertCTFEable!({ string s = "-1234abc"; assert(parse!( int, string, Yes.doCount)(s) ==
+ tuple(-1234, 5) && s == "abc"); });
+ assertCTFEable!({ string s = "1234abc"; assert(parse!(uint, string, Yes.doCount)(s) ==
+ tuple( 1234 ,4) && s == "abc"); });
}
-// Issue 13931
+// https://issues.dlang.org/show_bug.cgi?id=13931
@safe pure unittest
{
import std.exception;
@@ -2427,7 +2768,7 @@ Lerr:
assertThrown!ConvOverflowException("-92233720368547758080".to!long());
}
-// Issue 14396
+// https://issues.dlang.org/show_bug.cgi?id=14396
@safe pure unittest
{
struct StrInputRange
@@ -2441,23 +2782,39 @@ Lerr:
}
auto input = StrInputRange("777");
assert(parse!int(input) == 777);
+
+ auto input2 = StrInputRange("777");
+ assert(parse!(int, StrInputRange, Yes.doCount)(input2) == tuple(777, 3));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=9621
+@safe pure unittest
+{
+ string s1 = "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]";
+ assert(parse!(string[])(s1) == ["a", "\0", "!", "!8"]);
+
+ s1 = "[ \"\\141\", \"\\0\", \"\\41\", \"\\418\" ]";
+ auto len = s1.length;
+ assert(parse!(string[], string, Yes.doCount)(s1) == tuple(["a", "\0", "!", "!8"], len));
}
/// ditto
-Target parse(Target, Source)(ref Source source, uint radix)
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source, uint radix)
if (isSomeChar!(ElementType!Source) &&
isIntegral!Target && !is(Target == enum))
in
{
- assert(radix >= 2 && radix <= 36);
+ assert(radix >= 2 && radix <= 36, "radix must be in range [2,36]");
}
-body
+do
{
import core.checkedint : mulu, addu;
import std.exception : enforce;
if (radix == 10)
- return parse!Target(source);
+ {
+ return parse!(Target, Source, doCount)(source);
+ }
enforce!ConvException(!source.empty, "s must not be empty in integral parse");
@@ -2474,6 +2831,8 @@ body
alias s = source;
}
+ size_t count = 0;
+ auto found = false;
do
{
uint c = s.front;
@@ -2499,13 +2858,30 @@ body
auto nextv = v.mulu(radix, overflow).addu(c - '0', overflow);
enforce!ConvOverflowException(!overflow && nextv <= Target.max, "Overflow in integral conversion");
v = cast(Target) nextv;
+ ++count;
s.popFront();
+ found = true;
} while (!s.empty);
+ if (!found)
+ {
+ static if (isNarrowString!Source)
+ throw convError!(Source, Target)(cast(Source) source);
+ else
+ throw convError!(Source, Target)(source);
+ }
+
static if (isNarrowString!Source)
source = cast(Source) s;
- return v;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(v, count);
+ }
+ else
+ {
+ return v;
+ }
}
@safe pure unittest
@@ -2516,38 +2892,63 @@ body
assert(parse!int(s = "0", i) == 0);
assert(parse!int(s = "1", i) == 1);
assert(parse!byte(s = "10", i) == i);
+ assert(parse!(int, string, Yes.doCount)(s = "0", i) == tuple(0, 1));
+ assert(parse!(int, string, Yes.doCount)(s = "1", i) == tuple(1, 1));
+ assert(parse!(byte, string, Yes.doCount)(s = "10", i) == tuple(i, 2));
}
assert(parse!int(s = "0011001101101", 2) == 0b0011001101101);
assert(parse!int(s = "765", 8) == octal!765);
+ assert(parse!int(s = "000135", 8) == octal!"135");
assert(parse!int(s = "fCDe", 16) == 0xfcde);
- // 6609
+ // https://issues.dlang.org/show_bug.cgi?id=6609
assert(parse!int(s = "-42", 10) == -42);
assert(parse!ubyte(s = "ff", 16) == 0xFF);
}
-@safe pure unittest // bugzilla 7302
+// https://issues.dlang.org/show_bug.cgi?id=7302
+@safe pure unittest
{
import std.range : cycle;
auto r = cycle("2A!");
auto u = parse!uint(r, 16);
assert(u == 42);
assert(r.front == '!');
+
+ auto r2 = cycle("2A!");
+ auto u2 = parse!(uint, typeof(r2), Yes.doCount)(r2, 16);
+ assert(u2.data == 42 && u2.count == 2);
+ assert(r2.front == '!');
}
-@safe pure unittest // bugzilla 13163
+// https://issues.dlang.org/show_bug.cgi?id=13163
+@safe pure unittest
{
import std.exception;
foreach (s; ["fff", "123"])
assertThrown!ConvOverflowException(s.parse!ubyte(16));
}
-@safe pure unittest // bugzilla 17282
+// https://issues.dlang.org/show_bug.cgi?id=17282
+@safe pure unittest
{
auto str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n";
assert(parse!uint(str) == 0);
+
+ str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n";
+ assert(parse!(uint, string, Yes.doCount)(str) == tuple(0, 1));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18248
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+
+ auto str = ";";
+ assertThrown(str.parse!uint(16));
+ assertThrown(str.parse!(uint, string, Yes.doCount)(16));
}
/**
@@ -2556,20 +2957,25 @@ body
* Params:
* Target = the `enum` type to convert to
* s = the lvalue of the range to _parse
+ * doCount = the flag for deciding to report the number of consumed characters
*
* Returns:
- * An `enum` of type `Target`
+ $(UL
+ * $(LI An `enum` of type `Target` if `doCount` is set to `No.doCount`)
+ * $(LI A `tuple` containing an `enum` of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
*
* Throws:
* A $(LREF ConvException) if type `Target` does not have a member
* represented by `s`.
*/
-Target parse(Target, Source)(ref Source s)
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
if (isSomeString!Source && !is(Source == enum) &&
is(Target == enum))
{
import std.algorithm.searching : startsWith;
- Target result;
+ import std.traits : Unqual, EnumMembers;
+
+ Unqual!Target result;
size_t longest_match = 0;
foreach (i, e; EnumMembers!Target)
@@ -2585,7 +2991,14 @@ if (isSomeString!Source && !is(Source == enum) &&
if (longest_match > 0)
{
s = s[longest_match .. $];
- return result ;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result, longest_match);
+ }
+ else
+ {
+ return result;
+ }
}
throw new ConvException(
@@ -2596,10 +3009,16 @@ if (isSomeString!Source && !is(Source == enum) &&
///
@safe unittest
{
+ import std.typecons : Flag, Yes, No, tuple;
enum EnumType : bool { a = true, b = false, c = a }
auto str = "a";
assert(parse!EnumType(str) == EnumType.a);
+ auto str2 = "a";
+ assert(parse!(EnumType, string, No.doCount)(str2) == EnumType.a);
+ auto str3 = "a";
+ assert(parse!(EnumType, string, Yes.doCount)(str3) == tuple(EnumType.a, 1));
+
}
@safe unittest
@@ -2613,17 +3032,22 @@ if (isSomeString!Source && !is(Source == enum) &&
enum EC : char { a = 'a', b = 'b', c = 'c' }
enum ES : string { a = "aaa", b = "bbb", c = "ccc" }
- foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES))
+ static foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES))
{
assert(to!E("a"c) == E.a);
assert(to!E("b"w) == E.b);
assert(to!E("c"d) == E.c);
+ assert(to!(const E)("a") == E.a);
+ assert(to!(immutable E)("a") == E.a);
+ assert(to!(shared E)("a") == E.a);
+
assertThrown!ConvException(to!E("d"));
}
}
-@safe pure unittest // bugzilla 4744
+// https://issues.dlang.org/show_bug.cgi?id=4744
+@safe pure unittest
{
enum A { member1, member11, member111 }
assert(to!A("member1" ) == A.member1 );
@@ -2631,6 +3055,10 @@ if (isSomeString!Source && !is(Source == enum) &&
assert(to!A("member111") == A.member111);
auto s = "member1111";
assert(parse!A(s) == A.member111 && s == "1");
+ auto s2 = "member1111";
+ assert(parse!(A, string, No.doCount)(s2) == A.member111 && s2 == "1");
+ auto s3 = "member1111";
+ assert(parse!(A, string, Yes.doCount)(s3) == tuple(A.member111, 9) && s3 == "1");
}
/**
@@ -2639,15 +3067,19 @@ if (isSomeString!Source && !is(Source == enum) &&
* Params:
* Target = a floating point type
* source = the lvalue of the range to _parse
+ * doCount = the flag for deciding to report the number of consumed characters
*
* Returns:
- * A floating point number of type `Target`
+ $(UL
+ * $(LI A floating point number of type `Target` if `doCount` is set to `No.doCount`)
+ * $(LI A `tuple` containing a floating point number of·type `Target` and a `size_t`
+ * if `doCount` is set to `Yes.doCount`))
*
* Throws:
- * A $(LREF ConvException) if `p` is empty, if no number could be
+ * A $(LREF ConvException) if `source` is empty, if no number could be
* parsed, or if an overflow occurred.
*/
-Target parse(Target, Source)(ref Source source)
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source source)
if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
isFloatingPoint!Target && !is(Target == enum))
{
@@ -2678,36 +3110,48 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
return new ConvException(text(msg, " for input \"", source, "\"."), fn, ln);
}
-
enforce(!p.empty, bailOut());
+ size_t count = 0;
bool sign = false;
switch (p.front)
{
case '-':
sign = true;
+ ++count;
p.popFront();
enforce(!p.empty, bailOut());
if (toLower(p.front) == 'i')
goto case 'i';
break;
case '+':
+ ++count;
p.popFront();
enforce(!p.empty, bailOut());
break;
case 'i': case 'I':
// inf
+ ++count;
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'N',
bailOut("error converting input to floating point"));
+ ++count;
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'F',
bailOut("error converting input to floating point"));
// skip past the last 'f'
+ ++count;
p.popFront();
static if (isNarrowString!Source)
source = cast(Source) p;
- return sign ? -Target.infinity : Target.infinity;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(sign ? -Target.infinity : Target.infinity, count);
+ }
+ else
+ {
+ return sign ? -Target.infinity : Target.infinity;
+ }
default: {}
}
@@ -2715,31 +3159,53 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
bool startsWithZero = p.front == '0';
if (startsWithZero)
{
+ ++count;
p.popFront();
if (p.empty)
{
static if (isNarrowString!Source)
source = cast(Source) p;
- return sign ? -0.0 : 0.0;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(cast (Target) (sign ? -0.0 : 0.0), count);
+ }
+ else
+ {
+ return sign ? -0.0 : 0.0;
+ }
}
isHex = p.front == 'x' || p.front == 'X';
- if (isHex) p.popFront();
+ if (isHex)
+ {
+ ++count;
+ p.popFront();
+ }
}
else if (toLower(p.front) == 'n')
{
// nan
+ ++count;
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'A',
bailOut("error converting input to floating point"));
+ ++count;
p.popFront();
enforce(!p.empty && toUpper(p.front) == 'N',
bailOut("error converting input to floating point"));
// skip past the last 'n'
+ ++count;
p.popFront();
static if (isNarrowString!Source)
source = cast(Source) p;
- return typeof(return).nan;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(Target.nan, count);
+ }
+ else
+ {
+ return typeof(return).nan;
+ }
}
/*
@@ -2817,12 +3283,14 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
exp += expIter;
}
exp -= dot;
+ ++count;
p.popFront();
if (p.empty)
break;
i = p.front;
if (i == '_')
{
+ ++count;
p.popFront();
if (p.empty)
break;
@@ -2831,6 +3299,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
}
if (i == '.' && !dot)
{
+ ++count;
p.popFront();
dot += expIter;
}
@@ -2855,13 +3324,15 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
char sexp = 0;
int e = 0;
+ ++count;
p.popFront();
enforce(!p.empty, new ConvException("Unexpected end of input"));
switch (p.front)
{
case '-': sexp++;
goto case;
- case '+': p.popFront();
+ case '+': ++count;
+ p.popFront();
break;
default: {}
}
@@ -2872,6 +3343,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
{
e = e * 10 + p.front - '0';
}
+ ++count;
p.popFront();
sawDigits = true;
}
@@ -2884,7 +3356,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
ldval = ldval * msscale + lsdec;
if (isHex)
{
- import std.math : ldexp;
+ import core.math : ldexp;
// Exponent is power of 2, not power of 10
ldval = ldexp(ldval,exp);
@@ -2922,22 +3394,54 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
static if (isNarrowString!Source)
source = cast(Source) p;
- return sign ? -ldval : ldval;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(cast (Target) (sign ? -ldval : ldval), count);
+ }
+ else
+ {
+ return cast (Target) (sign ? -ldval : ldval);
+ }
}
///
@safe unittest
{
- import std.math : approxEqual;
+ import std.math.operations : isClose;
+ import std.math.traits : isNaN, isInfinity;
+ import std.typecons : Flag, Yes, No;
auto str = "123.456";
-
- assert(parse!double(str).approxEqual(123.456));
+ assert(parse!double(str).isClose(123.456));
+ auto str2 = "123.456";
+ assert(parse!(double, string, No.doCount)(str2).isClose(123.456));
+ auto str3 = "123.456";
+ auto r = parse!(double, string, Yes.doCount)(str3);
+ assert(r.data.isClose(123.456));
+ assert(r.count == 7);
+ auto str4 = "-123.456";
+ r = parse!(double, string, Yes.doCount)(str4);
+ assert(r.data.isClose(-123.456));
+ assert(r.count == 8);
+ auto str5 = "+123.456";
+ r = parse!(double, string, Yes.doCount)(str5);
+ assert(r.data.isClose(123.456));
+ assert(r.count == 8);
+ auto str6 = "inf0";
+ r = parse!(double, string, Yes.doCount)(str6);
+ assert(isInfinity(r.data) && r.count == 3 && str6 == "0");
+ auto str7 = "-0";
+ auto r2 = parse!(float, string, Yes.doCount)(str7);
+ assert(r2.data.isClose(0.0) && r2.count == 2);
+ auto str8 = "nan";
+ auto r3 = parse!(real, string, Yes.doCount)(str8);
+ assert(isNaN(r3.data) && r3.count == 3);
}
@safe unittest
{
import std.exception;
- import std.math : isNaN, fabs;
+ import std.math.traits : isNaN, isInfinity;
+ import std.math.algebraic : fabs;
// Compare reals with given precision
bool feq(in real rx, in real ry, in real precision = 0.000001L)
@@ -2960,14 +3464,14 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
return f;
}
- foreach (Float; AliasSeq!(float, double, real))
+ static foreach (Float; AliasSeq!(float, double, real))
{
assert(to!Float("123") == Literal!Float(123));
assert(to!Float("+123") == Literal!Float(+123));
assert(to!Float("-123") == Literal!Float(-123));
assert(to!Float("123e2") == Literal!Float(123e2));
assert(to!Float("123e+2") == Literal!Float(123e+2));
- assert(to!Float("123e-2") == Literal!Float(123e-2));
+ assert(to!Float("123e-2") == Literal!Float(123e-2L));
assert(to!Float("123.") == Literal!Float(123.0));
assert(to!Float(".375") == Literal!Float(.375));
@@ -2996,6 +3500,16 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
assert(to!string(d) == to!string(1.79769e+308));
assert(to!string(d) == to!string(double.max));
+ auto z = real.max / 2L;
+ static assert(is(typeof(z) == real));
+ assert(!isNaN(z));
+ assert(!isInfinity(z));
+ string a = to!string(z);
+ real b = to!real(a);
+ string c = to!string(b);
+
+ assert(c == a, "\n" ~ c ~ "\n" ~ a);
+
assert(to!string(to!real(to!string(real.max / 2L))) == to!string(real.max / 2L));
// min and max
@@ -3017,6 +3531,26 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
}
r = to!real(to!string(real.max));
assert(to!string(r) == to!string(real.max));
+
+ real pi = 3.1415926535897932384626433832795028841971693993751L;
+ string fullPrecision = "3.1415926535897932384626433832795028841971693993751";
+ assert(feq(parse!real(fullPrecision), pi, 2*real.epsilon));
+ string fullPrecision2 = "3.1415926535897932384626433832795028841971693993751";
+ assert(feq(parse!(real, string, No.doCount)(fullPrecision2), pi, 2*real.epsilon));
+ string fullPrecision3= "3.1415926535897932384626433832795028841971693993751";
+ auto len = fullPrecision3.length;
+ auto res = parse!(real, string, Yes.doCount)(fullPrecision3);
+ assert(feq(res.data, pi, 2*real.epsilon));
+ assert(res.count == len);
+
+ real x = 0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252L;
+ string full = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252";
+ assert(parse!real(full) == x);
+ string full2 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252";
+ assert(parse!(real, string, No.doCount)(full2) == x);
+ string full3 = "0x1.FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFAAFAAFAFAFAFAFAFAFAP-252";
+ auto len2 = full3.length;
+ assert(parse!(real, string, Yes.doCount)(full3) == tuple(x, len2));
}
// Tests for the double implementation
@@ -3037,6 +3571,10 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0xA_BCDE_F012_3456);
assert(strtod("0x1ABCDEF0123456p10", null) == x);
+ s = "0x1A_BCDE_F012_3456p10";
+ auto len = s.length;
+ assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len));
+
//Should be parsed exactly: 10 bit mantissa
s = "0x3FFp10";
x = parse!real(s);
@@ -3048,7 +3586,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
//60 bit mantissa, round up
s = "0xFFF_FFFF_FFFF_FFFFp10";
x = parse!real(s);
- assert(approxEqual(x, 0xFFF_FFFF_FFFF_FFFFp10));
+ assert(isClose(x, 0xFFF_FFFF_FFFF_FFFFp10));
//1 bit is implicit
assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0000);
assert(strtod("0xFFFFFFFFFFFFFFFp10", null) == x);
@@ -3056,7 +3594,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
//60 bit mantissa, round down
s = "0xFFF_FFFF_FFFF_FF90p10";
x = parse!real(s);
- assert(approxEqual(x, 0xFFF_FFFF_FFFF_FF90p10));
+ assert(isClose(x, 0xFFF_FFFF_FFFF_FF90p10));
//1 bit is implicit
assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_FFFF_FFFF_FFFF);
assert(strtod("0xFFFFFFFFFFFFF90p10", null) == x);
@@ -3064,7 +3602,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
//61 bit mantissa, round up 2
s = "0x1F0F_FFFF_FFFF_FFFFp10";
x = parse!real(s);
- assert(approxEqual(x, 0x1F0F_FFFF_FFFF_FFFFp10));
+ assert(isClose(x, 0x1F0F_FFFF_FFFF_FFFFp10));
//1 bit is implicit
assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_1000_0000_0000);
assert(strtod("0x1F0FFFFFFFFFFFFFp10", null) == x);
@@ -3072,7 +3610,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
//61 bit mantissa, round down 2
s = "0x1F0F_FFFF_FFFF_FF10p10";
x = parse!real(s);
- assert(approxEqual(x, 0x1F0F_FFFF_FFFF_FF10p10));
+ assert(isClose(x, 0x1F0F_FFFF_FFFF_FF10p10));
//1 bit is implicit
assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_0FFF_FFFF_FFFF);
assert(strtod("0x1F0FFFFFFFFFFF10p10", null) == x);
@@ -3092,6 +3630,10 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
x = parse!real(s);
assert(x == 0);
assert(strtod("0x1FFFFFFFFFFFFFp-2000", null) == x);
+
+ s = "0x1FFFFFFFFFFFFFp-2000";
+ len = s.length;
+ assert(parse!(real, string, Yes.doCount)(s) == tuple(x, len));
}
}
@@ -3128,8 +3670,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
int i;
static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
- // Our parser is currently limited to ieeeExtended precision
- enum s = "0x1.FFFFFFFFFFFFFFFEp-16382";
+ enum s = "0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382";
else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended)
enum s = "0x1.FFFFFFFFFFFFFFFEp-16382";
else static if (floatTraits!real.realFormat == RealFormat.ieeeExtended53)
@@ -3173,33 +3714,41 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
{
import std.exception;
- // Bugzilla 4959
+ // https://issues.dlang.org/show_bug.cgi?id=4959
{
auto s = "0 ";
auto x = parse!double(s);
assert(s == " ");
assert(x == 0.0);
}
+ {
+ auto s = "0 ";
+ auto x = parse!(double, string, Yes.doCount)(s);
+ assert(s == " ");
+ assert(x == tuple(0.0, 1));
+ }
- // Bugzilla 3369
+ // https://issues.dlang.org/show_bug.cgi?id=3369
assert(to!float("inf") == float.infinity);
assert(to!float("-inf") == -float.infinity);
- // Bugzilla 6160
+ // https://issues.dlang.org/show_bug.cgi?id=6160
assert(6_5.536e3L == to!real("6_5.536e3")); // 2^16
assert(0x1000_000_000_p10 == to!real("0x1000_000_000_p10")); // 7.03687e+13
- // Bugzilla 6258
+ // https://issues.dlang.org/show_bug.cgi?id=6258
assertThrown!ConvException(to!real("-"));
assertThrown!ConvException(to!real("in"));
- // Bugzilla 7055
+ // https://issues.dlang.org/show_bug.cgi?id=7055
assertThrown!ConvException(to!float("INF2"));
//extra stress testing
- auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1",
- "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2"];
- auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1", "+inf"];
+ auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1", "3.4_",
+ "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2",
+ "nan", "-NAN", "+NaN", "-nAna", "NAn2e2", "-naN2e2"];
+ auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1",
+ "+inf", "-in", "I", "+N", "-NaD", "0x3.F"];
foreach (s; ssOK)
parse!double(s);
foreach (s; ssKO)
@@ -3212,53 +3761,74 @@ Parsing one character off a range returns the first element and calls `popFront`
Params:
Target = the type to convert to
s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ doCount = the flag for deciding to report the number of consumed characters
Returns:
- A character of type `Target`
+$(UL
+ $(LI A character of type `Target` if `doCount` is set to `No.doCount`)
+ $(LI A `tuple` containing a character of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
Throws:
A $(LREF ConvException) if the range is empty.
*/
-Target parse(Target, Source)(ref Source s)
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
if (isSomeString!Source && !is(Source == enum) &&
- staticIndexOf!(Unqual!Target, dchar, Unqual!(ElementEncodingType!Source)) >= 0)
+ staticIndexOf!(immutable Target, immutable dchar, immutable ElementEncodingType!Source) >= 0)
{
if (s.empty)
throw convError!(Source, Target)(s);
- static if (is(Unqual!Target == dchar))
+ static if (is(immutable Target == immutable dchar))
{
Target result = s.front;
s.popFront();
- return result;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result, 1);
+ }
+ else
+ {
+ return result;
+ }
+
}
else
{
// Special case: okay so parse a Char off a Char[]
Target result = s[0];
s = s[1 .. $];
- return result;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result, 1);
+ }
+ else
+ {
+ return result;
+ }
}
}
@safe pure unittest
{
- foreach (Str; AliasSeq!(string, wstring, dstring))
+ static foreach (Str; AliasSeq!(string, wstring, dstring))
{
- foreach (Char; AliasSeq!(char, wchar, dchar))
- {
- static if (is(Unqual!Char == dchar) ||
+ static foreach (Char; AliasSeq!(char, wchar, dchar))
+ {{
+ static if (is(immutable Char == immutable dchar) ||
Char.sizeof == ElementEncodingType!Str.sizeof)
{
Str s = "aaa";
assert(parse!Char(s) == 'a');
assert(s == "aa");
+ assert(parse!(Char, typeof(s), No.doCount)(s) == 'a');
+ assert(s == "a");
+ assert(parse!(Char, typeof(s), Yes.doCount)(s) == tuple('a', 1) && s == "");
}
- }
+ }}
}
}
/// ditto
-Target parse(Target, Source)(ref Source s)
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
if (!isSomeString!Source && isInputRange!Source && isSomeChar!(ElementType!Source) &&
isSomeChar!Target && Target.sizeof >= ElementType!Source.sizeof && !is(Target == enum))
{
@@ -3266,16 +3836,30 @@ if (!isSomeString!Source && isInputRange!Source && isSomeChar!(ElementType!Sourc
throw convError!(Source, Target)(s);
Target result = s.front;
s.popFront();
- return result;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result, 1);
+ }
+ else
+ {
+ return result;
+ }
}
///
@safe pure unittest
{
+ import std.typecons : Flag, Yes, No;
auto s = "Hello, World!";
char first = parse!char(s);
assert(first == 'H');
assert(s == "ello, World!");
+ char second = parse!(char, string, No.doCount)(s);
+ assert(second == 'e');
+ assert(s == "llo, World!");
+ auto third = parse!(char, string, Yes.doCount)(s);
+ assert(third.data == 'l' && third.count == 1);
+ assert(s == "lo, World!");
}
@@ -3298,8 +3882,13 @@ if (!isSomeString!Source && isInputRange!Source && isSomeChar!(ElementType!Sourc
assert(parse!bool(f) == false);
assert(f == " killer whale"d);
+ f = "False killer whale"d;
+ assert(parse!(bool, dstring, Yes.doCount)(f) == tuple(false, 5));
+ assert(f == " killer whale"d);
+
auto m = "maybe";
assertThrown!ConvException(parse!bool(m));
+ assertThrown!ConvException(parse!(bool, string, Yes.doCount)(m));
assert(m == "maybe"); // m shouldn't change on failure
auto s = "true";
@@ -3314,17 +3903,20 @@ spells `"null"`. This function is case insensitive.
Params:
Target = the type to convert to
s = the lvalue of an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ doCount = the flag for deciding to report the number of consumed characters
Returns:
- `null`
+$(UL
+ $(LI `null` if `doCount` is set to `No.doCount`)
+ $(LI A `tuple` containing `null` and a `size_t` if `doCount` is set to `Yes.doCount`))
Throws:
A $(LREF ConvException) if the range doesn't represent `null`.
*/
-Target parse(Target, Source)(ref Source s)
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
if (isInputRange!Source &&
isSomeChar!(ElementType!Source) &&
- is(Unqual!Target == typeof(null)))
+ is(immutable Target == immutable typeof(null)))
{
import std.ascii : toLower;
foreach (c; "null")
@@ -3333,13 +3925,21 @@ if (isInputRange!Source &&
throw parseError("null should be case-insensitive 'null'");
s.popFront();
}
- return null;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(null, 4);
+ }
+ else
+ {
+ return null;
+ }
}
///
@safe pure unittest
{
import std.exception : assertThrown;
+ import std.typecons : Flag, Yes, No;
alias NullType = typeof(null);
auto s1 = "null";
@@ -3350,8 +3950,14 @@ if (isInputRange!Source &&
assert(parse!NullType(s2) is null);
assert(s2 == "");
+ auto s3 = "nuLlNULl";
+ assert(parse!(NullType, string, No.doCount)(s3) is null);
+ auto r = parse!(NullType, string, Yes.doCount)(s3);
+ assert(r.data is null && r.count == 4);
+
auto m = "maybe";
assertThrown!ConvException(parse!NullType(m));
+ assertThrown!ConvException(parse!(NullType, string, Yes.doCount)(m));
assert(m == "maybe"); // m shouldn't change on failure
auto s = "NULL";
@@ -3359,7 +3965,7 @@ if (isInputRange!Source &&
}
//Used internally by parse Array/AA, to remove ascii whites
-package void skipWS(R)(ref R r)
+package auto skipWS(R, Flag!"doCount" doCount = No.doCount)(ref R r)
{
import std.ascii : isWhite;
static if (isSomeString!R)
@@ -3370,34 +3976,58 @@ package void skipWS(R)(ref R r)
if (!isWhite(c))
{
r = r[i .. $];
- return;
+ static if (doCount)
+ {
+ return i;
+ }
+ else
+ {
+ return;
+ }
}
}
+ auto len = r.length;
r = r[0 .. 0]; //Empty string with correct type.
- return;
+ static if (doCount)
+ {
+ return len;
+ }
+ else
+ {
+ return;
+ }
}
else
{
- for (; !r.empty && isWhite(r.front); r.popFront())
- {}
+ size_t i = 0;
+ for (; !r.empty && isWhite(r.front); r.popFront(), ++i)
+ { }
+ static if (doCount)
+ {
+ return i;
+ }
}
}
/**
* Parses an array from a string given the left bracket (default $(D
- * '[')), right bracket (default $(D ']')), and element separator (by
- * default $(D ',')). A trailing separator is allowed.
+ * '[')), right bracket (default `']'`), and element separator (by
+ * default `','`). A trailing separator is allowed.
*
* Params:
* s = The string to parse
* lbracket = the character that starts the array
* rbracket = the character that ends the array
* comma = the character that separates the elements of the array
+ * doCount = the flag for deciding to report the number of consumed characters
*
* Returns:
- * An array of type `Target`
+ $(UL
+ * $(LI An array of type `Target` if `doCount` is set to `No.doCount`)
+ * $(LI A `tuple` containing an array of type `Target` and a `size_t` if `doCount` is set to `Yes.doCount`))
*/
-Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket = ']', dchar comma = ',')
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[',
+ dchar rbracket = ']', dchar comma = ',')
if (isSomeString!Source && !is(Source == enum) &&
isDynamicArray!Target && !is(Target == enum))
{
@@ -3406,33 +4036,48 @@ if (isSomeString!Source && !is(Source == enum) &&
auto result = appender!Target();
parseCheck!s(lbracket);
- skipWS(s);
+ size_t count = 1 + skipWS!(Source, Yes.doCount)(s);
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front == rbracket)
{
s.popFront();
- return result.data;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result.data, ++count);
+ }
+ else
+ {
+ return result.data;
+ }
}
- for (;; s.popFront(), skipWS(s))
+ for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s))
{
if (!s.empty && s.front == rbracket)
break;
- result ~= parseElement!(ElementType!Target)(s);
- skipWS(s);
+ auto r = parseElement!(WideElementType!Target, Source, Yes.doCount)(s);
+ result ~= r.data;
+ count += r.count + skipWS!(Source, Yes.doCount)(s);
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front != comma)
break;
}
parseCheck!s(rbracket);
-
- return result.data;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result.data, ++count);
+ }
+ else
+ {
+ return result.data;
+ }
}
///
@safe pure unittest
{
+ import std.typecons : Flag, Yes, No;
auto s1 = `[['h', 'e', 'l', 'l', 'o'], "world"]`;
auto a1 = parse!(string[])(s1);
assert(a1 == ["hello", "world"]);
@@ -3440,10 +4085,18 @@ if (isSomeString!Source && !is(Source == enum) &&
auto s2 = `["aaa", "bbb", "ccc"]`;
auto a2 = parse!(string[])(s2);
assert(a2 == ["aaa", "bbb", "ccc"]);
+
+ auto s3 = `[['h', 'e', 'l', 'l', 'o'], "world"]`;
+ auto len3 = s3.length;
+ auto a3 = parse!(string[], string, Yes.doCount)(s3);
+ assert(a3.data == ["hello", "world"]);
+ assert(a3.count == len3);
}
-@safe unittest // Bugzilla 9615
+// https://issues.dlang.org/show_bug.cgi?id=9615
+@safe unittest
{
+ import std.typecons : Flag, Yes, No, tuple;
string s0 = "[1,2, ]";
string s1 = "[1,2, \t\v\r\n]";
string s2 = "[1,2]";
@@ -3451,16 +4104,38 @@ if (isSomeString!Source && !is(Source == enum) &&
assert(s1.parse!(int[]) == [1,2]);
assert(s2.parse!(int[]) == [1,2]);
+ s0 = "[1,2, ]";
+ auto len0 = s0.length;
+ s1 = "[1,2, \t\v\r\n]";
+ auto len1 = s1.length;
+ s2 = "[1,2]";
+ auto len2 = s2.length;
+ assert(s0.parse!(int[], string, Yes.doCount) == tuple([1,2], len0));
+ assert(s1.parse!(int[], string, Yes.doCount) == tuple([1,2], len1));
+ assert(s2.parse!(int[], string, Yes.doCount) == tuple([1,2], len2));
+
string s3 = `["a","b",]`;
string s4 = `["a","b"]`;
assert(s3.parse!(string[]) == ["a","b"]);
assert(s4.parse!(string[]) == ["a","b"]);
+ s3 = `["a","b",]`;
+ auto len3 = s3.length;
+ assert(s3.parse!(string[], string, Yes.doCount) == tuple(["a","b"], len3));
+
+ s3 = `[ ]`;
+ assert(tuple([], s3.length) == s3.parse!(string[], string, Yes.doCount));
+
import std.exception : assertThrown;
string s5 = "[,]";
string s6 = "[, \t,]";
assertThrown!ConvException(parse!(string[])(s5));
assertThrown!ConvException(parse!(int[])(s6));
+
+ s5 = "[,]";
+ s6 = "[,·\t,]";
+ assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s5));
+ assertThrown!ConvException(parse!(string[], string, Yes.doCount)(s6));
}
@safe unittest
@@ -3491,15 +4166,20 @@ if (isSomeString!Source && !is(Source == enum) &&
@safe pure unittest
{
import std.exception;
+ import std.typecons : Flag, Yes, No;
//Check proper failure
auto s = "[ 1 , 2 , 3 ]";
+ auto s2 = s.save;
foreach (i ; 0 .. s.length-1)
{
auto ss = s[0 .. i];
assertThrown!ConvException(parse!(int[])(ss));
+ assertThrown!ConvException(parse!(int[], string, Yes.doCount)(ss));
}
int[] arr = parse!(int[])(s);
+ auto arr2 = parse!(int[], string, Yes.doCount)(s2);
+ arr = arr2.data;
}
@safe pure unittest
@@ -3526,12 +4206,15 @@ if (isSomeString!Source && !is(Source == enum) &&
"unicode æ—¥ sun",
"very long æ—¥ sun"
];
+ string s3 = s1.save;
assert(s2 == parse!(string[])(s1));
assert(s1.empty);
+ assert(tuple(s2, s3.length) == parse!(string[], string, Yes.doCount)(s3));
}
/// ditto
-Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket = ']', dchar comma = ',')
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[',
+ dchar rbracket = ']', dchar comma = ',')
if (isExactSomeString!Source &&
isStaticArray!Target && !is(Target == enum))
{
@@ -3541,7 +4224,7 @@ if (isExactSomeString!Source &&
Target result = void;
parseCheck!s(lbracket);
- skipWS(s);
+ size_t count = 1 + skipWS!(Source, Yes.doCount)(s);
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front == rbracket)
@@ -3551,15 +4234,23 @@ if (isExactSomeString!Source &&
else
{
s.popFront();
- return result;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result, ++count);
+ }
+ else
+ {
+ return result;
+ }
}
}
- for (size_t i = 0; ; s.popFront(), skipWS(s))
+ for (size_t i = 0; ; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s))
{
if (i == result.length)
goto Lmanyerr;
- result[i++] = parseElement!(ElementType!Target)(s);
- skipWS(s);
+ auto r = parseElement!(ElementType!Target, Source, Yes.doCount)(s);
+ result[i++] = r.data;
+ count += r.count + skipWS!(Source, Yes.doCount)(s);
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front != comma)
@@ -3570,8 +4261,15 @@ if (isExactSomeString!Source &&
}
}
parseCheck!s(rbracket);
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result, ++count);
+ }
+ else
+ {
+ return result;
+ }
- return result;
Lmanyerr:
throw parseError(text("Too many elements in input, ", result.length, " elements expected."));
@@ -3587,22 +4285,28 @@ Lfewerr:
auto s1 = "[1,2,3,4]";
auto sa1 = parse!(int[4])(s1);
assert(sa1 == [1,2,3,4]);
+ s1 = "[1,2,3,4]";
+ assert(tuple([1,2,3,4], s1.length) == parse!(int[4], string, Yes.doCount)(s1));
auto s2 = "[[1],[2,3],[4]]";
auto sa2 = parse!(int[][3])(s2);
assert(sa2 == [[1],[2,3],[4]]);
+ s2 = "[[1],[2,3],[4]]";
+ assert(tuple([[1],[2,3],[4]], s2.length) == parse!(int[][3], string, Yes.doCount)(s2));
auto s3 = "[1,2,3]";
assertThrown!ConvException(parse!(int[4])(s3));
+ assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s3));
auto s4 = "[1,2,3,4,5]";
assertThrown!ConvException(parse!(int[4])(s4));
+ assertThrown!ConvException(parse!(int[4], string, Yes.doCount)(s4));
}
/**
* Parses an associative array from a string given the left bracket (default $(D
- * '[')), right bracket (default $(D ']')), key-value separator (default $(D
- * ':')), and element seprator (by default $(D ',')).
+ * '[')), right bracket (default `']'`), key-value separator (default $(D
+ * ':')), and element seprator (by default `','`).
*
* Params:
* s = the string to parse
@@ -3610,11 +4314,15 @@ Lfewerr:
* rbracket = the character that ends the associative array
* keyval = the character that associates the key with the value
* comma = the character that separates the elements of the associative array
+ * doCount = the flag for deciding to report the number of consumed characters
*
* Returns:
- * An associative array of type `Target`
+ $(UL
+ * $(LI An associative array of type `Target` if `doCount` is set to `No.doCount`)
+ * $(LI A `tuple` containing an associative array of type `Target` and a `size_t`
+ * if `doCount` is set to `Yes.doCount`))
*/
-Target parse(Target, Source)(ref Source s, dchar lbracket = '[',
+auto parse(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s, dchar lbracket = '[',
dchar rbracket = ']', dchar keyval = ':', dchar comma = ',')
if (isSomeString!Source && !is(Source == enum) &&
isAssociativeArray!Target && !is(Target == enum))
@@ -3625,47 +4333,75 @@ if (isSomeString!Source && !is(Source == enum) &&
Target result;
parseCheck!s(lbracket);
- skipWS(s);
+ size_t count = 1 + skipWS!(Source, Yes.doCount)(s);
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front == rbracket)
{
s.popFront();
- return result;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result, ++count);
+ }
+ else
+ {
+ return result;
+ }
}
- for (;; s.popFront(), skipWS(s))
+ for (;; s.popFront(), count += 1 + skipWS!(Source, Yes.doCount)(s))
{
- auto key = parseElement!KeyType(s);
- skipWS(s);
+ auto key = parseElement!(KeyType, Source, Yes.doCount)(s);
+ count += key.count + skipWS!(Source, Yes.doCount)(s);
parseCheck!s(keyval);
- skipWS(s);
- auto val = parseElement!ValType(s);
- skipWS(s);
- result[key] = val;
+ count += 1 + skipWS!(Source, Yes.doCount)(s);
+ auto val = parseElement!(ValType, Source, Yes.doCount)(s);
+ count += val.count + skipWS!(Source, Yes.doCount)(s);
+ result[key.data] = val.data;
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front != comma)
break;
}
parseCheck!s(rbracket);
-
- return result;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result, ++count);
+ }
+ else
+ {
+ return result;
+ }
}
///
@safe pure unittest
{
+ import std.typecons : Flag, Yes, No, tuple;
+ import std.range.primitives : save;
+ import std.array : assocArray;
auto s1 = "[1:10, 2:20, 3:30]";
+ auto copyS1 = s1.save;
auto aa1 = parse!(int[int])(s1);
assert(aa1 == [1:10, 2:20, 3:30]);
+ assert(tuple([1:10, 2:20, 3:30], copyS1.length) == parse!(int[int], string, Yes.doCount)(copyS1));
auto s2 = `["aaa":10, "bbb":20, "ccc":30]`;
+ auto copyS2 = s2.save;
auto aa2 = parse!(int[string])(s2);
assert(aa2 == ["aaa":10, "bbb":20, "ccc":30]);
+ assert(tuple(["aaa":10, "bbb":20, "ccc":30], copyS2.length) ==
+ parse!(int[string], string, Yes.doCount)(copyS2));
auto s3 = `["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]`;
+ auto copyS3 = s3.save;
auto aa3 = parse!(int[][string])(s3);
assert(aa3 == ["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]);
+ assert(tuple(["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]], copyS3.length) ==
+ parse!(int[][string], string, Yes.doCount)(copyS3));
+
+ auto s4 = `[]`;
+ int[int] emptyAA;
+ assert(tuple(emptyAA, s4.length) == parse!(int[int], string, Yes.doCount)(s4));
}
@safe pure unittest
@@ -3674,21 +4410,28 @@ if (isSomeString!Source && !is(Source == enum) &&
//Check proper failure
auto s = "[1:10, 2:20, 3:30]";
+ auto s2 = s.save;
foreach (i ; 0 .. s.length-1)
{
auto ss = s[0 .. i];
assertThrown!ConvException(parse!(int[int])(ss));
+ assertThrown!ConvException(parse!(int[int], string, Yes.doCount)(ss));
}
int[int] aa = parse!(int[int])(s);
+ auto aa2 = parse!(int[int], string, Yes.doCount)(s2);
+ aa = aa2[0];
+
}
-private dchar parseEscape(Source)(ref Source s)
+private auto parseEscape(Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
if (isInputRange!Source && isSomeChar!(ElementType!Source))
{
parseCheck!s('\\');
+ size_t count = 1;
if (s.empty)
throw parseError("Unterminated escape sequence");
+ // consumes 1 element from Source
dchar getHexDigit()(ref Source s_ = s) // workaround
{
import std.ascii : isAlpha, isHexDigit;
@@ -3703,13 +4446,71 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
return isAlpha(c) ? ((c & ~0x20) - ('A' - 10)) : c - '0';
}
+ // We need to do octals separate, because they need a lookahead to find out,
+ // where the escape sequence ends.
+ auto first = s.front;
+ if (first >= '0' && first <= '7')
+ {
+ dchar c1 = s.front;
+ ++count;
+ s.popFront();
+ if (s.empty)
+ {
+ static if (doCount)
+ {
+ return tuple!("data", "count")(cast (dchar) (c1 - '0'), count);
+ }
+ else
+ {
+ return cast (dchar) (c1 - '0');
+ }
+ }
+ dchar c2 = s.front;
+ if (c2 < '0' || c2 > '7')
+ {
+ static if (doCount)
+ {
+ return tuple!("data", "count")(cast (dchar)(c1 - '0'), count);
+ }
+ else
+ {
+ return cast (dchar)(c1 - '0');
+ }
+ }
+ ++count;
+ s.popFront();
+ dchar c3 = s.front;
+ if (c3 < '0' || c3 > '7')
+ {
+ static if (doCount)
+ {
+ return tuple!("data", "count")(cast (dchar) (8 * (c1 - '0') + (c2 - '0')), count);
+ }
+ else
+ {
+ return cast (dchar) (8 * (c1 - '0') + (c2 - '0'));
+ }
+ }
+ ++count;
+ s.popFront();
+ if (c1 > '3')
+ throw parseError("Octal sequence is larger than \\377");
+ static if (doCount)
+ {
+ return tuple!("data", "count")(cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0')), count);
+ }
+ else
+ {
+ return cast (dchar) (64 * (c1 - '0') + 8 * (c2 - '0') + (c3 - '0'));
+ }
+ }
+
dchar result;
- switch (s.front)
+ switch (first)
{
case '"': result = '\"'; break;
case '\'': result = '\''; break;
- case '0': result = '\0'; break;
case '?': result = '\?'; break;
case '\\': result = '\\'; break;
case 'a': result = '\a'; break;
@@ -3722,12 +4523,14 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
case 'x':
result = getHexDigit() << 4;
result |= getHexDigit();
+ count += 2;
break;
case 'u':
result = getHexDigit() << 12;
result |= getHexDigit() << 8;
result |= getHexDigit() << 4;
result |= getHexDigit();
+ count += 4;
break;
case 'U':
result = getHexDigit() << 28;
@@ -3738,6 +4541,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
result |= getHexDigit() << 8;
result |= getHexDigit() << 4;
result |= getHexDigit();
+ count += 8;
break;
default:
throw parseError("Unknown escape character " ~ to!string(s.front));
@@ -3747,31 +4551,44 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
s.popFront();
- return result;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(cast (dchar) result, ++count);
+ }
+ else
+ {
+ return cast (dchar) result;
+ }
}
@safe pure unittest
{
string[] s1 = [
`\"`, `\'`, `\?`, `\\`, `\a`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v`, //Normal escapes
- //`\141`, //@@@9621@@@ Octal escapes.
+ `\141`,
`\x61`,
- `\u65E5`, `\U00012456`
- //`\&amp;`, `\&quot;`, //@@@9621@@@ Named Character Entities.
+ `\u65E5`, `\U00012456`,
+ // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities)
+ //`\&amp;`, `\&quot;`,
];
+ string[] copyS1 = s1 ~ s1[0 .. 0];
const(dchar)[] s2 = [
'\"', '\'', '\?', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v', //Normal escapes
- //'\141', //@@@9621@@@ Octal escapes.
+ '\141',
'\x61',
- '\u65E5', '\U00012456'
- //'\&amp;', '\&quot;', //@@@9621@@@ Named Character Entities.
+ '\u65E5', '\U00012456',
+ // https://issues.dlang.org/show_bug.cgi?id=9621 (Named Character Entities)
+ //'\&amp;', '\&quot;',
];
foreach (i ; 0 .. s1.length)
{
assert(s2[i] == parseEscape(s1[i]));
assert(s1[i].empty);
+
+ assert(tuple(s2[i], copyS1[i].length) == parseEscape!(string, Yes.doCount)(copyS1[i]));
+ assert(copyS1[i].empty);
}
}
@@ -3788,16 +4605,20 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source))
`\x0`, //Premature hex end
`\XB9`, //Not legal hex syntax
`\u!!`, //Not a unicode hex
- `\777`, //Octal is larger than a byte //Note: Throws, but simply because octals are unsupported
+ `\777`, //Octal is larger than a byte
+ `\80`, //Wrong digit at beginning of octal
`\u123`, //Premature hex end
`\U123123` //Premature hex end
];
foreach (s ; ss)
+ {
assertThrown!ConvException(parseEscape(s));
+ assertThrown!ConvException(parseEscape!(string, Yes.doCount)(s));
+ }
}
// Undocumented
-Target parseElement(Target, Source)(ref Source s)
+auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
isExactSomeString!Target)
{
@@ -3808,15 +4629,26 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front == '[')
- return parse!Target(s);
+ {
+ return parse!(Target, Source, doCount)(s);
+ }
parseCheck!s('\"');
+ size_t count = 1;
if (s.empty)
throw convError!(Source, Target)(s);
if (s.front == '\"')
{
s.popFront();
- return result.data;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result.data, ++count);
+ }
+ else
+ {
+ return result.data;
+ }
+
}
while (true)
{
@@ -3826,29 +4658,41 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
{
case '\"':
s.popFront();
- return result.data;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(result.data, ++count);
+ }
+ else
+ {
+ return result.data;
+ }
case '\\':
- result.put(parseEscape(s));
+ auto r = parseEscape!(typeof(s), Yes.doCount)(s);
+ result.put(r[0]);
+ count += r[1];
break;
default:
result.put(s.front);
+ ++count;
s.popFront();
break;
}
}
- assert(0);
+ assert(false, "Unexpected fallthrough");
}
// ditto
-Target parseElement(Target, Source)(ref Source s)
+auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
- isSomeChar!Target && !is(Target == enum))
+ is(CharTypeOf!Target == dchar) && !is(Target == enum))
{
- Target c;
+ Unqual!Target c;
parseCheck!s('\'');
+ size_t count = 1;
if (s.empty)
throw convError!(Source, Target)(s);
+ ++count; // for the following if-else sequence
if (s.front != '\\')
{
c = s.front;
@@ -3857,16 +4701,33 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum
else
c = parseEscape(s);
parseCheck!s('\'');
-
- return c;
+ static if (doCount)
+ {
+ return tuple!("data", "count")(c, ++count);
+ }
+ else
+ {
+ return c;
+ }
}
// ditto
-Target parseElement(Target, Source)(ref Source s)
+auto parseElement(Target, Source, Flag!"doCount" doCount = No.doCount)(ref Source s)
if (isInputRange!Source && isSomeChar!(ElementType!Source) &&
!isSomeString!Target && !isSomeChar!Target)
{
- return parse!Target(s);
+ return parse!(Target, Source, doCount)(s);
+}
+
+// Use this when parsing a type that will ultimately be appended to a
+// string.
+package template WideElementType(T)
+{
+ alias E = ElementType!T;
+ static if (isSomeChar!E)
+ alias WideElementType = dchar;
+ else
+ alias WideElementType = E;
}
@@ -3925,26 +4786,47 @@ private S textImpl(S, U...)(U args)
else
{
import std.array : appender;
+ import std.traits : isSomeChar, isSomeString;
auto app = appender!S();
- foreach (arg; args)
- app.put(to!S(arg));
+ // assume that on average, parameters will have less
+ // than 20 elements
+ app.reserve(U.length * 20);
+ // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209
+ static foreach (arg; args)
+ {
+ static if (
+ isSomeChar!(typeof(arg)) || isSomeString!(typeof(arg)) ||
+ ( isInputRange!(typeof(arg)) && isSomeChar!(ElementType!(typeof(arg))) )
+ )
+ app.put(arg);
+ else static if (
+
+ is(immutable typeof(arg) == immutable uint) || is(immutable typeof(arg) == immutable ulong) ||
+ is(immutable typeof(arg) == immutable int) || is(immutable typeof(arg) == immutable long)
+ )
+ // https://issues.dlang.org/show_bug.cgi?id=17712#c15
+ app.put(textImpl!(S)(arg));
+ else
+ app.put(to!S(arg));
+ }
+
return app.data;
}
}
/***************************************************************
-The $(D octal) facility provides a means to declare a number in base 8.
-Using $(D octal!177) or $(D octal!"177") for 127 represented in octal
+The `octal` facility provides a means to declare a number in base 8.
+Using `octal!177` or `octal!"177"` for 127 represented in octal
(same as 0177 in C).
The rules for strings are the usual for literals: If it can fit in an
-$(D int), it is an $(D int). Otherwise, it is a $(D long). But, if the
-user specifically asks for a $(D long) with the $(D L) suffix, always
-give the $(D long). Give an unsigned iff it is asked for with the $(D
-U) or $(D u) suffix. _Octals created from integers preserve the type
+`int`, it is an `int`. Otherwise, it is a `long`. But, if the
+user specifically asks for a `long` with the `L` suffix, always
+give the `long`. Give an unsigned iff it is asked for with the $(D
+U) or `u` suffix. _Octals created from integers preserve the type
of the passed-in integral.
See_Also:
@@ -3962,7 +4844,7 @@ if (isOctalLiteral(num))
else static if ((!octalFitsInInt!(num) || literalIsLong!(num)) && literalIsUnsigned!(num))
enum octal = octal!ulong(num);
else
- static assert(false);
+ static assert(false, "Unusable input " ~ num);
}
/// Ditto
@@ -3975,12 +4857,14 @@ if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger)))
///
@safe unittest
{
- // same as 0177
- auto x = octal!177;
+ // Same as 0177
+ auto a = octal!177;
// octal is a compile-time device
- enum y = octal!160;
+ enum b = octal!160;
// Create an unsigned octal
- auto z = octal!"1_000_000u";
+ auto c = octal!"1_000_000u";
+ // Leading zeros are allowed when converting from a string
+ auto d = octal!"0001_200_000";
}
/*
@@ -3989,7 +4873,7 @@ if (is(typeof(decimalInteger)) && isIntegral!(typeof(decimalInteger)))
*/
private T octal(T)(const string num)
{
- assert(isOctalLiteral(num));
+ assert(isOctalLiteral(num), num ~ " is not an octal literal");
T value = 0;
@@ -4011,6 +4895,9 @@ private T octal(T)(const string num)
{
int a = octal!int("10");
assert(a == 8);
+
+ int b = octal!int("000137");
+ assert(b == 95);
}
/*
@@ -4030,12 +4917,41 @@ private template octalFitsInInt(string octalNum)
private string strippedOctalLiteral(string original)
{
string stripped = "";
+ bool leading_zeros = true;
foreach (c; original)
- if (c >= '0' && c <= '7')
- stripped ~= c;
+ {
+ if (!('0' <= c && c <= '7'))
+ continue;
+ if (c == '0')
+ {
+ if (leading_zeros)
+ continue;
+ }
+ else
+ {
+ leading_zeros = false;
+ }
+ stripped ~= c;
+ }
+ if (stripped.length == 0)
+ {
+ assert(leading_zeros);
+ return "0";
+ }
return stripped;
}
+@safe unittest
+{
+ static assert(strippedOctalLiteral("7") == "7");
+ static assert(strippedOctalLiteral("123") == "123");
+ static assert(strippedOctalLiteral("00123") == "123");
+ static assert(strippedOctalLiteral("01230") == "1230");
+ static assert(strippedOctalLiteral("0") == "0");
+ static assert(strippedOctalLiteral("00_000") == "0");
+ static assert(strippedOctalLiteral("000_000_12_300") == "12300");
+}
+
private template literalIsLong(string num)
{
static if (num.length > 1)
@@ -4059,8 +4975,8 @@ private template literalIsUnsigned(string num)
/*
Returns if the given string is a correctly formatted octal literal.
-The format is specified in spec/lex.html. The leading zero is allowed, but
-not required.
+The format is specified in spec/lex.html. The leading zeros are allowed,
+but not required.
*/
@safe pure nothrow @nogc
private bool isOctalLiteral(const string num)
@@ -4068,34 +4984,30 @@ private bool isOctalLiteral(const string num)
if (num.length == 0)
return false;
- // Must start with a number. To avoid confusion, literals that
- // start with a '0' are not allowed
- if (num[0] == '0' && num.length > 1)
- return false;
+ // Must start with a digit.
if (num[0] < '0' || num[0] > '7')
return false;
foreach (i, c; num)
{
- if ((c < '0' || c > '7') && c != '_') // not a legal character
+ if (('0' <= c && c <= '7') || c == '_') // a legal character
+ continue;
+
+ if (i < num.length - 2)
+ return false;
+
+ // gotta check for those suffixes
+ if (c != 'U' && c != 'u' && c != 'L')
+ return false;
+ if (i != num.length - 1)
{
- if (i < num.length - 2)
- return false;
- else // gotta check for those suffixes
- {
- if (c != 'U' && c != 'u' && c != 'L')
- return false;
- if (i != num.length - 1)
- {
- // if we're not the last one, the next one must
- // also be a suffix to be valid
- char c2 = num[$-1];
- if (c2 != 'U' && c2 != 'u' && c2 != 'L')
- return false; // spam at the end of the string
- if (c2 == c)
- return false; // repeats are disallowed
- }
- }
+ // if we're not the last one, the next one must
+ // also be a suffix to be valid
+ char c2 = num[$-1];
+ if (c2 != 'U' && c2 != 'u' && c2 != 'L')
+ return false; // spam at the end of the string
+ if (c2 == c)
+ return false; // repeats are disallowed
}
}
@@ -4115,6 +5027,9 @@ private bool isOctalLiteral(const string num)
static assert(octal!"7" == 7);
static assert(octal!"10" == 8);
static assert(octal!"666" == 438);
+ static assert(octal!"0004001" == 2049);
+ static assert(octal!"00" == 0);
+ static assert(octal!"0_0" == 0);
static assert(octal!45 == 37);
static assert(octal!0 == 0);
@@ -4123,6 +5038,7 @@ private bool isOctalLiteral(const string num)
static assert(octal!666 == 438);
static assert(octal!"66_6" == 438);
+ static assert(octal!"0_0_66_6" == 438);
static assert(octal!2520046213 == 356535435);
static assert(octal!"2520046213" == 356535435);
@@ -4167,964 +5083,12 @@ private bool isOctalLiteral(const string num)
assert(b == 1);
}
-/+
-emplaceRef is a package function for phobos internal use. It works like
-emplace, but takes its argument by ref (as opposed to "by pointer").
-
-This makes it easier to use, easier to be safe, and faster in a non-inline
-build.
-
-Furthermore, emplaceRef optionally takes a type paremeter, which specifies
-the type we want to build. This helps to build qualified objects on mutable
-buffer, without breaking the type system with unsafe casts.
-+/
-package void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args)
-{
- static if (args.length == 0)
- {
- static assert(is(typeof({static T i;})),
- convFormat("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof));
- static if (is(T == class)) static assert(!isAbstractClass!T,
- T.stringof ~ " is abstract and it can't be emplaced");
- emplaceInitializer(chunk);
- }
- else static if (
- !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */
- ||
- Args.length == 1 && is(typeof({T t = args[0];})) /* conversions */
- ||
- is(typeof(T(args))) /* general constructors */)
- {
- static struct S
- {
- T payload;
- this(ref Args x)
- {
- static if (Args.length == 1)
- static if (is(typeof(payload = x[0])))
- payload = x[0];
- else
- payload = T(x[0]);
- else
- payload = T(x);
- }
- }
- if (__ctfe)
- {
- static if (is(typeof(chunk = T(args))))
- chunk = T(args);
- else static if (args.length == 1 && is(typeof(chunk = args[0])))
- chunk = args[0];
- else assert(0, "CTFE emplace doesn't support "
- ~ T.stringof ~ " from " ~ Args.stringof);
- }
- else
- {
- S* p = () @trusted { return cast(S*) &chunk; }();
- emplaceInitializer(*p);
- p.__ctor(args);
- }
- }
- else static if (is(typeof(chunk.__ctor(args))))
- {
- // This catches the rare case of local types that keep a frame pointer
- emplaceInitializer(chunk);
- chunk.__ctor(args);
- }
- else
- {
- //We can't emplace. Try to diagnose a disabled postblit.
- static assert(!(Args.length == 1 && is(Args[0] : T)),
- convFormat("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof));
-
- //We can't emplace.
- static assert(false,
- convFormat("%s cannot be emplaced from %s.", T.stringof, Args[].stringof));
- }
-}
-// ditto
-package void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
-if (is(UT == Unqual!UT))
-{
- emplaceRef!(UT, UT)(chunk, args);
-}
-
-//emplace helper functions
-private void emplaceInitializer(T)(ref T chunk) @trusted pure nothrow
-{
- static if (!hasElaborateAssign!T && isAssignable!T)
- chunk = T.init;
- else
- {
- import core.stdc.string : memcpy;
- static immutable T init = T.init;
- memcpy(&chunk, &init, T.sizeof);
- }
-}
-
-// emplace
-/**
-Given a pointer $(D chunk) to uninitialized memory (but already typed
-as $(D T)), constructs an object of non-$(D class) type $(D T) at that
-address. If `T` is a class, initializes the class reference to null.
-
-Returns: A pointer to the newly constructed object (which is the same
-as $(D chunk)).
- */
-T* emplace(T)(T* chunk) @safe pure nothrow
-{
- emplaceRef!T(*chunk);
- return chunk;
-}
-
-///
-@system unittest
-{
- static struct S
- {
- int i = 42;
- }
- S[2] s2 = void;
- emplace(&s2);
- assert(s2[0].i == 42 && s2[1].i == 42);
-}
-
-///
-@system unittest
-{
- interface I {}
- class K : I {}
-
- K k = void;
- emplace(&k);
- assert(k is null);
-
- I i = void;
- emplace(&i);
- assert(i is null);
-}
-
-/**
-Given a pointer $(D chunk) to uninitialized memory (but already typed
-as a non-class type $(D T)), constructs an object of type $(D T) at
-that address from arguments $(D args). If `T` is a class, initializes
-the class reference to `args[0]`.
-
-This function can be $(D @trusted) if the corresponding constructor of
-$(D T) is $(D @safe).
-
-Returns: A pointer to the newly constructed object (which is the same
-as $(D chunk)).
- */
-T* emplace(T, Args...)(T* chunk, auto ref Args args)
-if (is(T == struct) || Args.length == 1)
-{
- emplaceRef!T(*chunk, args);
- return chunk;
-}
-
-///
-@system unittest
-{
- int a;
- int b = 42;
- assert(*emplace!int(&a, b) == 42);
-}
-
-@system unittest
-{
- shared int i;
- emplace(&i, 42);
- assert(i == 42);
-}
-
-private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) @nogc pure nothrow
-{
- assert(chunk.length >= typeSize, "emplace: Chunk size too small.");
- assert((cast(size_t) chunk.ptr) % typeAlignment == 0, "emplace: Chunk is not aligned.");
-}
-
-/**
-Given a raw memory area $(D chunk), constructs an object of $(D class)
-type $(D T) at that address. The constructor is passed the arguments
-$(D Args).
-
-If `T` is an inner class whose `outer` field can be used to access an instance
-of the enclosing class, then `Args` must not be empty, and the first member of it
-must be a valid initializer for that `outer` field. Correct initialization of
-this field is essential to access members of the outer class inside `T` methods.
-
-Preconditions:
-$(D chunk) must be at least as large as $(D T) needs
-and should have an alignment multiple of $(D T)'s alignment. (The size
-of a $(D class) instance is obtained by using $(D
-__traits(classInstanceSize, T))).
-
-Note:
-This function can be $(D @trusted) if the corresponding constructor of
-$(D T) is $(D @safe).
-
-Returns: The newly constructed object.
- */
-T emplace(T, Args...)(void[] chunk, auto ref Args args)
-if (is(T == class))
-{
- static assert(!isAbstractClass!T, T.stringof ~
- " is abstract and it can't be emplaced");
-
- enum classSize = __traits(classInstanceSize, T);
- testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof);
- auto result = cast(T) chunk.ptr;
-
- // Initialize the object in its pre-ctor state
- chunk[0 .. classSize] = typeid(T).initializer[];
-
- static if (isInnerClass!T)
- {
- static assert(Args.length > 0,
- "Initializing an inner class requires a pointer to the outer class");
- static assert(is(Args[0] : typeof(T.outer)),
- "The first argument must be a pointer to the outer class");
-
- result.outer = args[0];
- alias args1 = args[1..$];
- }
- else alias args1 = args;
-
- // Call the ctor if any
- static if (is(typeof(result.__ctor(args1))))
- {
- // T defines a genuine constructor accepting args
- // Go the classic route: write .init first, then call ctor
- result.__ctor(args1);
- }
- else
- {
- static assert(args1.length == 0 && !is(typeof(&T.__ctor)),
- "Don't know how to initialize an object of type "
- ~ T.stringof ~ " with arguments " ~ typeof(args1).stringof);
- }
- return result;
-}
-
-///
-@system unittest
-{
- static class C
- {
- int i;
- this(int i){this.i = i;}
- }
- auto buf = new void[__traits(classInstanceSize, C)];
- auto c = emplace!C(buf, 5);
- assert(c.i == 5);
-}
-
-@system unittest
-{
- class Outer
- {
- int i = 3;
- class Inner
- {
- auto getI() { return i; }
- }
- }
- auto outerBuf = new void[__traits(classInstanceSize, Outer)];
- auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)];
- auto inner = innerBuf.emplace!(Outer.Inner)(outerBuf.emplace!Outer);
- assert(inner.getI == 3);
-}
-
-@nogc pure nothrow @system unittest
-{
- int var = 6;
- align(__conv_EmplaceTestClass.alignof) ubyte[__traits(classInstanceSize, __conv_EmplaceTestClass)] buf;
- auto k = emplace!__conv_EmplaceTestClass(buf, 5, var);
- assert(k.i == 5);
- assert(var == 7);
-}
-
-/**
-Given a raw memory area $(D chunk), constructs an object of non-$(D
-class) type $(D T) at that address. The constructor is passed the
-arguments $(D args), if any.
-
-Preconditions:
-$(D chunk) must be at least as large
-as $(D T) needs and should have an alignment multiple of $(D T)'s
-alignment.
-
-Note:
-This function can be $(D @trusted) if the corresponding constructor of
-$(D T) is $(D @safe).
-
-Returns: A pointer to the newly constructed object.
- */
-T* emplace(T, Args...)(void[] chunk, auto ref Args args)
-if (!is(T == class))
-{
- testEmplaceChunk(chunk, T.sizeof, T.alignof, T.stringof);
- emplaceRef!(T, Unqual!T)(*cast(Unqual!T*) chunk.ptr, args);
- return cast(T*) chunk.ptr;
-}
-
-///
-@system unittest
-{
- struct S
- {
- int a, b;
- }
- auto buf = new void[S.sizeof];
- S s;
- s.a = 42;
- s.b = 43;
- auto s1 = emplace!S(buf, s);
- assert(s1.a == 42 && s1.b == 43);
-}
-
-// Bulk of emplace unittests starts here
-
-@system unittest /* unions */
-{
- static union U
- {
- string a;
- int b;
- struct
- {
- long c;
- int[] d;
- }
- }
- U u1 = void;
- U u2 = { "hello" };
- emplace(&u1, u2);
- assert(u1.a == "hello");
-}
-
-version (unittest) private struct __conv_EmplaceTest
-{
- int i = 3;
- this(int i)
- {
- assert(this.i == 3 && i == 5);
- this.i = i;
- }
- this(int i, ref int j)
- {
- assert(i == 5 && j == 6);
- this.i = i;
- ++j;
- }
-
-@disable:
- this();
- this(this);
- void opAssign();
-}
-
-version (unittest) private class __conv_EmplaceTestClass
-{
- int i = 3;
- this(int i) @nogc @safe pure nothrow
- {
- assert(this.i == 3 && i == 5);
- this.i = i;
- }
- this(int i, ref int j) @nogc @safe pure nothrow
- {
- assert(i == 5 && j == 6);
- this.i = i;
- ++j;
- }
-}
-
-@system unittest // bugzilla 15772
-{
- abstract class Foo {}
- class Bar: Foo {}
- void[] memory;
- // test in emplaceInitializer
- static assert(!is(typeof(emplace!Foo(cast(Foo*) memory.ptr))));
- static assert( is(typeof(emplace!Bar(cast(Bar*) memory.ptr))));
- // test in the emplace overload that takes void[]
- static assert(!is(typeof(emplace!Foo(memory))));
- static assert( is(typeof(emplace!Bar(memory))));
-}
-
-@system unittest
-{
- struct S { @disable this(); }
- S s = void;
- static assert(!__traits(compiles, emplace(&s)));
- emplace(&s, S.init);
-}
-
-@system unittest
-{
- struct S1
- {}
-
- struct S2
- {
- void opAssign(S2);
- }
-
- S1 s1 = void;
- S2 s2 = void;
- S1[2] as1 = void;
- S2[2] as2 = void;
- emplace(&s1);
- emplace(&s2);
- emplace(&as1);
- emplace(&as2);
-}
-
-@system unittest
-{
- static struct S1
- {
- this(this) @disable;
- }
- static struct S2
- {
- this() @disable;
- }
- S1[2] ss1 = void;
- S2[2] ss2 = void;
- emplace(&ss1);
- static assert(!__traits(compiles, emplace(&ss2)));
- S1 s1 = S1.init;
- S2 s2 = S2.init;
- static assert(!__traits(compiles, emplace(&ss1, s1)));
- emplace(&ss2, s2);
-}
-
-@system unittest
-{
- struct S
- {
- immutable int i;
- }
- S s = void;
- S[2] ss1 = void;
- S[2] ss2 = void;
- emplace(&s, 5);
- assert(s.i == 5);
- emplace(&ss1, s);
- assert(ss1[0].i == 5 && ss1[1].i == 5);
- emplace(&ss2, ss1);
- assert(ss2 == ss1);
-}
-
-//Start testing emplace-args here
-
-@system unittest
-{
- interface I {}
- class K : I {}
-
- K k = null, k2 = new K;
- assert(k !is k2);
- emplace!K(&k, k2);
- assert(k is k2);
-
- I i = null;
- assert(i !is k);
- emplace!I(&i, k);
- assert(i is k);
-}
-
-@system unittest
-{
- static struct S
- {
- int i = 5;
- void opAssign(S){assert(0);}
- }
- S[2] sa = void;
- S[2] sb;
- emplace(&sa, sb);
- assert(sa[0].i == 5 && sa[1].i == 5);
-}
-
-//Start testing emplace-struct here
-
-// Test constructor branch
-@system unittest
-{
- struct S
- {
- double x = 5, y = 6;
- this(int a, int b)
- {
- assert(x == 5 && y == 6);
- x = a;
- y = b;
- }
- }
-
- auto s1 = new void[S.sizeof];
- auto s2 = S(42, 43);
- assert(*emplace!S(cast(S*) s1.ptr, s2) == s2);
- assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45));
-}
-
-@system unittest
-{
- __conv_EmplaceTest k = void;
- emplace(&k, 5);
- assert(k.i == 5);
-}
-
-@system unittest
-{
- int var = 6;
- __conv_EmplaceTest k = void;
- emplace(&k, 5, var);
- assert(k.i == 5);
- assert(var == 7);
-}
-
-// Test matching fields branch
-@system unittest
-{
- struct S { uint n; }
- S s;
- emplace!S(&s, 2U);
- assert(s.n == 2);
-}
+// emplace() used to be here but was moved to druntime
+public import core.lifetime : emplace;
+// https://issues.dlang.org/show_bug.cgi?id=9559
@safe unittest
{
- struct S { int a, b; this(int){} }
- S s;
- static assert(!__traits(compiles, emplace!S(&s, 2, 3)));
-}
-
-@system unittest
-{
- struct S { int a, b = 7; }
- S s1 = void, s2 = void;
-
- emplace!S(&s1, 2);
- assert(s1.a == 2 && s1.b == 7);
-
- emplace!S(&s2, 2, 3);
- assert(s2.a == 2 && s2.b == 3);
-}
-
-//opAssign
-@system unittest
-{
- static struct S
- {
- int i = 5;
- void opAssign(int){assert(0);}
- void opAssign(S){assert(0);}
- }
- S sa1 = void;
- S sa2 = void;
- S sb1 = S(1);
- emplace(&sa1, sb1);
- emplace(&sa2, 2);
- assert(sa1.i == 1);
- assert(sa2.i == 2);
-}
-
-//postblit precedence
-@system unittest
-{
- //Works, but breaks in "-w -O" because of @@@9332@@@.
- //Uncomment test when 9332 is fixed.
- static struct S
- {
- int i;
-
- this(S other){assert(false);}
- this(int i){this.i = i;}
- this(this){}
- }
- S a = void;
- assert(is(typeof({S b = a;}))); //Postblit
- assert(is(typeof({S b = S(a);}))); //Constructor
- auto b = S(5);
- emplace(&a, b);
- assert(a.i == 5);
-
- static struct S2
- {
- int* p;
- this(const S2){}
- }
- static assert(!is(immutable S2 : S2));
- S2 s2 = void;
- immutable is2 = (immutable S2).init;
- emplace(&s2, is2);
-}
-
-//nested structs and postblit
-@system unittest
-{
- static struct S
- {
- int* p;
- this(int i){p = [i].ptr;}
- this(this)
- {
- if (p)
- p = [*p].ptr;
- }
- }
- static struct SS
- {
- S s;
- void opAssign(const SS)
- {
- assert(0);
- }
- }
- SS ssa = void;
- SS ssb = SS(S(5));
- emplace(&ssa, ssb);
- assert(*ssa.s.p == 5);
- assert(ssa.s.p != ssb.s.p);
-}
-
-//disabled postblit
-@system unittest
-{
- static struct S1
- {
- int i;
- @disable this(this);
- }
- S1 s1 = void;
- emplace(&s1, 1);
- assert(s1.i == 1);
- static assert(!__traits(compiles, emplace(&s1, S1.init)));
-
- static struct S2
- {
- int i;
- @disable this(this);
- this(ref S2){}
- }
- S2 s2 = void;
- static assert(!__traits(compiles, emplace(&s2, 1)));
- emplace(&s2, S2.init);
-
- static struct SS1
- {
- S1 s;
- }
- SS1 ss1 = void;
- emplace(&ss1);
- static assert(!__traits(compiles, emplace(&ss1, SS1.init)));
-
- static struct SS2
- {
- S2 s;
- }
- SS2 ss2 = void;
- emplace(&ss2);
- static assert(!__traits(compiles, emplace(&ss2, SS2.init)));
-
-
- // SS1 sss1 = s1; //This doesn't compile
- // SS1 sss1 = SS1(s1); //This doesn't compile
- // So emplace shouldn't compile either
- static assert(!__traits(compiles, emplace(&sss1, s1)));
- static assert(!__traits(compiles, emplace(&sss2, s2)));
-}
-
-//Imutability
-@system unittest
-{
- //Castable immutability
- {
- static struct S1
- {
- int i;
- }
- static assert(is( immutable(S1) : S1));
- S1 sa = void;
- auto sb = immutable(S1)(5);
- emplace(&sa, sb);
- assert(sa.i == 5);
- }
- //Un-castable immutability
- {
- static struct S2
- {
- int* p;
- }
- static assert(!is(immutable(S2) : S2));
- S2 sa = void;
- auto sb = immutable(S2)(null);
- assert(!__traits(compiles, emplace(&sa, sb)));
- }
-}
-
-@system unittest
-{
- static struct S
- {
- immutable int i;
- immutable(int)* j;
- }
- S s = void;
- emplace(&s, 1, null);
- emplace(&s, 2, &s.i);
- assert(s is S(2, &s.i));
-}
-
-//Context pointer
-@system unittest
-{
- int i = 0;
- {
- struct S1
- {
- void foo(){++i;}
- }
- S1 sa = void;
- S1 sb;
- emplace(&sa, sb);
- sa.foo();
- assert(i == 1);
- }
- {
- struct S2
- {
- void foo(){++i;}
- this(this){}
- }
- S2 sa = void;
- S2 sb;
- emplace(&sa, sb);
- sa.foo();
- assert(i == 2);
- }
-}
-
-//Alias this
-@system unittest
-{
- static struct S
- {
- int i;
- }
- //By Ref
- {
- static struct SS1
- {
- int j;
- S s;
- alias s this;
- }
- S s = void;
- SS1 ss = SS1(1, S(2));
- emplace(&s, ss);
- assert(s.i == 2);
- }
- //By Value
- {
- static struct SS2
- {
- int j;
- S s;
- S foo() @property{return s;}
- alias foo this;
- }
- S s = void;
- SS2 ss = SS2(1, S(2));
- emplace(&s, ss);
- assert(s.i == 2);
- }
-}
-version (unittest)
-{
- //Ambiguity
- struct __std_conv_S
- {
- int i;
- this(__std_conv_SS ss) {assert(0);}
- static opCall(__std_conv_SS ss)
- {
- __std_conv_S s; s.i = ss.j;
- return s;
- }
- }
- struct __std_conv_SS
- {
- int j;
- __std_conv_S s;
- ref __std_conv_S foo() return @property {s.i = j; return s;}
- alias foo this;
- }
- static assert(is(__std_conv_SS : __std_conv_S));
- @system unittest
- {
- __std_conv_S s = void;
- __std_conv_SS ss = __std_conv_SS(1);
-
- __std_conv_S sTest1 = ss; //this calls "SS alias this" (and not "S.this(SS)")
- emplace(&s, ss); //"alias this" should take precedence in emplace over "opCall"
- assert(s.i == 1);
- }
-}
-
-//Nested classes
-@system unittest
-{
- class A{}
- static struct S
- {
- A a;
- }
- S s1 = void;
- S s2 = S(new A);
- emplace(&s1, s2);
- assert(s1.a is s2.a);
-}
-
-//safety & nothrow & CTFE
-@system unittest
-{
- //emplace should be safe for anything with no elaborate opassign
- static struct S1
- {
- int i;
- }
- static struct S2
- {
- int i;
- this(int j)@safe nothrow{i = j;}
- }
-
- int i;
- S1 s1 = void;
- S2 s2 = void;
-
- auto pi = &i;
- auto ps1 = &s1;
- auto ps2 = &s2;
-
- void foo() @safe nothrow
- {
- emplace(pi);
- emplace(pi, 5);
- emplace(ps1);
- emplace(ps1, 5);
- emplace(ps1, S1.init);
- emplace(ps2);
- emplace(ps2, 5);
- emplace(ps2, S2.init);
- }
- foo();
-
- T bar(T)() @property
- {
- T t/+ = void+/; //CTFE void illegal
- emplace(&t, 5);
- return t;
- }
- // CTFE
- enum a = bar!int;
- static assert(a == 5);
- enum b = bar!S1;
- static assert(b.i == 5);
- enum c = bar!S2;
- static assert(c.i == 5);
- // runtime
- auto aa = bar!int;
- assert(aa == 5);
- auto bb = bar!S1;
- assert(bb.i == 5);
- auto cc = bar!S2;
- assert(cc.i == 5);
-}
-
-
-@system unittest
-{
- struct S
- {
- int[2] get(){return [1, 2];}
- alias get this;
- }
- struct SS
- {
- int[2] ii;
- }
- struct ISS
- {
- int[2] ii;
- }
- S s;
- SS ss = void;
- ISS iss = void;
- emplace(&ss, s);
- emplace(&iss, s);
- assert(ss.ii == [1, 2]);
- assert(iss.ii == [1, 2]);
-}
-
-//disable opAssign
-@system unittest
-{
- static struct S
- {
- @disable void opAssign(S);
- }
- S s;
- emplace(&s, S.init);
-}
-
-//opCall
-@system unittest
-{
- int i;
- //Without constructor
- {
- static struct S1
- {
- int i;
- static S1 opCall(int*){assert(0);}
- }
- S1 s = void;
- static assert(!__traits(compiles, emplace(&s, 1)));
- }
- //With constructor
- {
- static struct S2
- {
- int i = 0;
- static S2 opCall(int*){assert(0);}
- static S2 opCall(int){assert(0);}
- this(int i){this.i = i;}
- }
- S2 s = void;
- emplace(&s, 1);
- assert(s.i == 1);
- }
- //With postblit ambiguity
- {
- static struct S3
- {
- int i = 0;
- static S3 opCall(ref S3){assert(0);}
- }
- S3 s = void;
- emplace(&s, S3.init);
- }
-}
-
-@safe unittest //@@@9559@@@
-{
import std.algorithm.iteration : map;
import std.array : array;
import std.typecons : Nullable;
@@ -5137,7 +5101,7 @@ version (unittest)
{
import std.array : array;
import std.datetime : SysTime, UTC;
- import std.math : isNaN;
+ import std.math.traits : isNaN;
static struct A
{
@@ -5173,235 +5137,11 @@ version (unittest)
auto a2 = arr.array(); // << bang, invariant is raised, also if b2 and b3 are good
}
-//static arrays
-@system unittest
-{
- static struct S
- {
- int[2] ii;
- }
- static struct IS
- {
- immutable int[2] ii;
- }
- int[2] ii;
- S s = void;
- IS ims = void;
- ubyte ub = 2;
- emplace(&s, ub);
- emplace(&s, ii);
- emplace(&ims, ub);
- emplace(&ims, ii);
- uint[2] uu;
- static assert(!__traits(compiles, {S ss = S(uu);}));
- static assert(!__traits(compiles, emplace(&s, uu)));
-}
-
-@system unittest
-{
- int[2] sii;
- int[2] sii2;
- uint[2] uii;
- uint[2] uii2;
- emplace(&sii, 1);
- emplace(&sii, 1U);
- emplace(&uii, 1);
- emplace(&uii, 1U);
- emplace(&sii, sii2);
- //emplace(&sii, uii2); //Sorry, this implementation doesn't know how to...
- //emplace(&uii, sii2); //Sorry, this implementation doesn't know how to...
- emplace(&uii, uii2);
- emplace(&sii, sii2[]);
- //emplace(&sii, uii2[]); //Sorry, this implementation doesn't know how to...
- //emplace(&uii, sii2[]); //Sorry, this implementation doesn't know how to...
- emplace(&uii, uii2[]);
-}
-
-@system unittest
-{
- bool allowDestruction = false;
- struct S
- {
- int i;
- this(this){}
- ~this(){assert(allowDestruction);}
- }
- S s = S(1);
- S[2] ss1 = void;
- S[2] ss2 = void;
- S[2] ss3 = void;
- emplace(&ss1, s);
- emplace(&ss2, ss1);
- emplace(&ss3, ss2[]);
- assert(ss1[1] == s);
- assert(ss2[1] == s);
- assert(ss3[1] == s);
- allowDestruction = true;
-}
-
-@system unittest
-{
- //Checks postblit, construction, and context pointer
- int count = 0;
- struct S
- {
- this(this)
- {
- ++count;
- }
- ~this()
- {
- --count;
- }
- }
-
- S s;
- {
- S[4] ss = void;
- emplace(&ss, s);
- assert(count == 4);
- }
- assert(count == 0);
-}
-
-@system unittest
-{
- struct S
- {
- int i;
- }
- S s;
- S[2][2][2] sss = void;
- emplace(&sss, s);
-}
-
-@system unittest //Constness
-{
- import std.stdio;
-
- int a = void;
- emplaceRef!(const int)(a, 5);
-
- immutable i = 5;
- const(int)* p = void;
- emplaceRef!(const int*)(p, &i);
-
- struct S
- {
- int* p;
- }
- alias IS = immutable(S);
- S s = void;
- emplaceRef!IS(s, IS());
- S[2] ss = void;
- emplaceRef!(IS[2])(ss, IS());
-
- IS[2] iss = IS.init;
- emplaceRef!(IS[2])(ss, iss);
- emplaceRef!(IS[2])(ss, iss[]);
-}
-
-pure nothrow @safe @nogc unittest
-{
- int i;
- emplaceRef(i);
- emplaceRef!int(i);
- emplaceRef(i, 5);
- emplaceRef!int(i, 5);
-}
-
-// Test attribute propagation for UDTs
-pure nothrow @safe /* @nogc */ unittest
-{
- static struct Safe
- {
- this(this) pure nothrow @safe @nogc {}
- }
-
- Safe safe = void;
- emplaceRef(safe, Safe());
-
- Safe[1] safeArr = [Safe()];
- Safe[1] uninitializedSafeArr = void;
- emplaceRef(uninitializedSafeArr, safe);
- emplaceRef(uninitializedSafeArr, safeArr);
-
- static struct Unsafe
- {
- this(this) @system {}
- }
-
- Unsafe unsafe = void;
- static assert(!__traits(compiles, emplaceRef(unsafe, Unsafe())));
-
- Unsafe[1] unsafeArr = [Unsafe()];
- Unsafe[1] uninitializedUnsafeArr = void;
- static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafe)));
- static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafeArr)));
-}
-
-@system unittest
-{
- // Issue 15313
- static struct Node
- {
- int payload;
- Node* next;
- uint refs;
- }
-
- import core.stdc.stdlib : malloc;
- void[] buf = malloc(Node.sizeof)[0 .. Node.sizeof];
-
- import std.conv : emplace;
- const Node* n = emplace!(const Node)(buf, 42, null, 10);
- assert(n.payload == 42);
- assert(n.next == null);
- assert(n.refs == 10);
-}
-
-@system unittest
-{
- int var = 6;
- auto k = emplace!__conv_EmplaceTest(new void[__conv_EmplaceTest.sizeof], 5, var);
- assert(k.i == 5);
- assert(var == 7);
-}
-
-@system unittest
-{
- class A
- {
- int x = 5;
- int y = 42;
- this(int z)
- {
- assert(x == 5 && y == 42);
- x = y = z;
- }
- }
- void[] buf;
-
- static align(A.alignof) byte[__traits(classInstanceSize, A)] sbuf;
- buf = sbuf[];
- auto a = emplace!A(buf, 55);
- assert(a.x == 55 && a.y == 55);
-
- // emplace in bigger buffer
- buf = new byte[](__traits(classInstanceSize, A) + 10);
- a = emplace!A(buf, 55);
- assert(a.x == 55 && a.y == 55);
-
- // need ctor args
- static assert(!is(typeof(emplace!A(buf))));
-}
-// Bulk of emplace unittests ends here
-
@safe unittest
{
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;
- // Check fix for http://d.puremagic.com/issues/show_bug.cgi?id=2971
+ // Check fix for https://issues.dlang.org/show_bug.cgi?id=2971
assert(equal(map!(to!int)(["42", "34", "345"]), [42, 34, 345]));
}
@@ -5415,12 +5155,12 @@ if (isIntegral!T && isOutputRange!(W, char))
if (value < 0)
{
SignedStringBuf buf = void;
- put(writer, signedToTempString(value, buf, 10));
+ put(writer, signedToTempString(value, buf));
}
else
{
UnsignedStringBuf buf = void;
- put(writer, unsignedToTempString(value, buf, 10));
+ put(writer, unsignedToTempString(value, buf));
}
}
@@ -5434,10 +5174,10 @@ if (isIntegral!T && isOutputRange!(W, char))
/**
- Returns the corresponding _unsigned value for $(D x) (e.g. if $(D x) has type
- $(D int), it returns $(D cast(uint) x)). The advantage compared to the cast
- is that you do not need to rewrite the cast if $(D x) later changes type
- (e.g from $(D int) to $(D long)).
+ Returns the corresponding _unsigned value for `x` (e.g. if `x` has type
+ `int`, it returns $(D cast(uint) x)). The advantage compared to the cast
+ is that you do not need to rewrite the cast if `x` later changes type
+ (e.g from `int` to `long`).
Note that the result is always mutable even if the original type was const
or immutable. In order to retain the constness, use $(REF Unsigned, std,traits).
@@ -5460,30 +5200,39 @@ if (isIntegral!T)
immutable u3 = unsigned(s); //explicitly qualified
}
+/// Ditto
+auto unsigned(T)(T x)
+if (isSomeChar!T)
+{
+ // All characters are unsigned
+ static assert(T.min == 0, T.stringof ~ ".min must be zero");
+ return cast(Unqual!T) x;
+}
+
@safe unittest
{
- foreach (T; AliasSeq!(byte, ubyte))
+ static foreach (T; AliasSeq!(byte, ubyte))
{
static assert(is(typeof(unsigned(cast(T) 1)) == ubyte));
static assert(is(typeof(unsigned(cast(const T) 1)) == ubyte));
static assert(is(typeof(unsigned(cast(immutable T) 1)) == ubyte));
}
- foreach (T; AliasSeq!(short, ushort))
+ static foreach (T; AliasSeq!(short, ushort))
{
static assert(is(typeof(unsigned(cast(T) 1)) == ushort));
static assert(is(typeof(unsigned(cast(const T) 1)) == ushort));
static assert(is(typeof(unsigned(cast(immutable T) 1)) == ushort));
}
- foreach (T; AliasSeq!(int, uint))
+ static foreach (T; AliasSeq!(int, uint))
{
static assert(is(typeof(unsigned(cast(T) 1)) == uint));
static assert(is(typeof(unsigned(cast(const T) 1)) == uint));
static assert(is(typeof(unsigned(cast(immutable T) 1)) == uint));
}
- foreach (T; AliasSeq!(long, ulong))
+ static foreach (T; AliasSeq!(long, ulong))
{
static assert(is(typeof(unsigned(cast(T) 1)) == ulong));
static assert(is(typeof(unsigned(cast(const T) 1)) == ulong));
@@ -5491,17 +5240,9 @@ if (isIntegral!T)
}
}
-auto unsigned(T)(T x)
-if (isSomeChar!T)
-{
- // All characters are unsigned
- static assert(T.min == 0);
- return cast(Unqual!T) x;
-}
-
@safe unittest
{
- foreach (T; AliasSeq!(char, wchar, dchar))
+ static foreach (T; AliasSeq!(char, wchar, dchar))
{
static assert(is(typeof(unsigned(cast(T)'A')) == T));
static assert(is(typeof(unsigned(cast(const T)'A')) == T));
@@ -5511,10 +5252,10 @@ if (isSomeChar!T)
/**
- Returns the corresponding _signed value for $(D x) (e.g. if $(D x) has type
- $(D uint), it returns $(D cast(int) x)). The advantage compared to the cast
- is that you do not need to rewrite the cast if $(D x) later changes type
- (e.g from $(D uint) to $(D ulong)).
+ Returns the corresponding _signed value for `x` (e.g. if `x` has type
+ `uint`, it returns $(D cast(int) x)). The advantage compared to the cast
+ is that you do not need to rewrite the cast if `x` later changes type
+ (e.g from `uint` to `ulong`).
Note that the result is always mutable even if the original type was const
or immutable. In order to retain the constness, use $(REF Signed, std,traits).
@@ -5540,28 +5281,28 @@ if (isIntegral!T)
@system unittest
{
- foreach (T; AliasSeq!(byte, ubyte))
+ static foreach (T; AliasSeq!(byte, ubyte))
{
static assert(is(typeof(signed(cast(T) 1)) == byte));
static assert(is(typeof(signed(cast(const T) 1)) == byte));
static assert(is(typeof(signed(cast(immutable T) 1)) == byte));
}
- foreach (T; AliasSeq!(short, ushort))
+ static foreach (T; AliasSeq!(short, ushort))
{
static assert(is(typeof(signed(cast(T) 1)) == short));
static assert(is(typeof(signed(cast(const T) 1)) == short));
static assert(is(typeof(signed(cast(immutable T) 1)) == short));
}
- foreach (T; AliasSeq!(int, uint))
+ static foreach (T; AliasSeq!(int, uint))
{
static assert(is(typeof(signed(cast(T) 1)) == int));
static assert(is(typeof(signed(cast(const T) 1)) == int));
static assert(is(typeof(signed(cast(immutable T) 1)) == int));
}
- foreach (T; AliasSeq!(long, ulong))
+ static foreach (T; AliasSeq!(long, ulong))
{
static assert(is(typeof(signed(cast(T) 1)) == long));
static assert(is(typeof(signed(cast(const T) 1)) == long));
@@ -5569,9 +5310,9 @@ if (isIntegral!T)
}
}
+// https://issues.dlang.org/show_bug.cgi?id=10874
@safe unittest
{
- // issue 10874
enum Test { a = 0 }
ulong l = 0;
auto t = l.to!Test;
@@ -5582,7 +5323,8 @@ if (isIntegral!T)
Returns the representation of an enumerated value, i.e. the value converted to
the base type of the enumeration.
*/
-OriginalType!E asOriginalType(E)(E value) if (is(E == enum))
+OriginalType!E asOriginalType(E)(E value)
+if (is(E == enum))
{
return value;
}
@@ -5615,7 +5357,7 @@ template castFrom(From)
/**
Params:
To = The type _to cast _to.
- value = The value _to cast. It must be of type $(D From),
+ value = The value _to cast. It must be of type `From`,
otherwise a compile-time error is emitted.
Returns:
@@ -5680,7 +5422,7 @@ template castFrom(From)
}
/**
-Check the correctness of a string for $(D hexString).
+Check the correctness of a string for `hexString`.
The result is true if and only if the input string is composed of whitespace
characters (\f\n\r\t\v lineSep paraSep nelSep) and
an even number of hexadecimal digits (regardless of the case).
@@ -5784,32 +5526,32 @@ The input string can also include white characters, which can be used
to keep the literal string readable in the source code.
The function is intended to replace the hexadecimal literal strings
-starting with $(D 'x'), which could be removed to simplify the core language.
+starting with `'x'`, which could be removed to simplify the core language.
Params:
hexData = string to be converted.
Returns:
- a $(D string), a $(D wstring) or a $(D dstring), according to the type of hexData.
+ a `string`, a `wstring` or a `dstring`, according to the type of hexData.
*/
template hexString(string hexData)
if (hexData.isHexLiteral)
{
- immutable hexString = hexStrImpl(hexData);
+ enum hexString = mixin(hexToString(hexData));
}
/// ditto
template hexString(wstring hexData)
if (hexData.isHexLiteral)
{
- immutable hexString = hexStrImpl(hexData);
+ enum wstring hexString = mixin(hexToString(hexData));
}
/// ditto
template hexString(dstring hexData)
if (hexData.isHexLiteral)
{
- immutable hexString = hexStrImpl(hexData);
+ enum dstring hexString = mixin(hexToString(hexData));
}
///
@@ -5824,45 +5566,70 @@ if (hexData.isHexLiteral)
assert(string3 == "0J1K"d);
}
+@safe nothrow pure private
+{
+ /* These are meant to be used with CTFE.
+ * They cause the instantiations of hexStrLiteral()
+ * to be in Phobos, not user code.
+ */
+ string hexToString(string s)
+ {
+ return hexStrLiteral(s);
+ }
+
+ wstring hexToString(wstring s)
+ {
+ return hexStrLiteral(s);
+ }
+
+ dstring hexToString(dstring s)
+ {
+ return hexStrLiteral(s);
+ }
+}
+
/*
- Takes a hexadecimal string literal and returns its representation.
- hexData is granted to be a valid string by the caller.
- C is granted to be a valid char type by the caller.
-*/
-@safe nothrow pure
-private auto hexStrImpl(String)(scope String hexData)
+ Turn a hexadecimal string into a regular string literal.
+ I.e. "dead beef" is transformed into "\xde\xad\xbe\xef"
+ suitable for use in a mixin.
+ Params:
+ hexData is string, wstring, or dstring and validated by isHexLiteral()
+ */
+@trusted nothrow pure
+private auto hexStrLiteral(String)(scope String hexData)
{
import std.ascii : isHexDigit;
- alias C = Unqual!(ElementEncodingType!String);
+ alias C = Unqual!(ElementEncodingType!String); // char, wchar or dchar
C[] result;
- result.length = hexData.length / 2;
- size_t cnt;
- ubyte v;
+ result.length = 1 + hexData.length * 2 + 1; // don't forget the " "
+ /* Use a pointer because we know it won't overrun,
+ * and this will reduce the size of the function substantially
+ * by not doing the array bounds checks.
+ * This is why this function is @trusted.
+ */
+ auto r = result.ptr;
+ r[0] = '"';
+ size_t cnt = 0;
foreach (c; hexData)
{
if (c.isHexDigit)
{
- ubyte x;
- if (c >= '0' && c <= '9')
- x = cast(ubyte)(c - '0');
- else if (c >= 'a' && c <= 'f')
- x = cast(ubyte)(c - ('a' - 10));
- else if (c >= 'A' && c <= 'F')
- x = cast(ubyte)(c - ('A' - 10));
- if (cnt & 1)
+ if ((cnt & 1) == 0)
{
- v = cast(ubyte)((v << 4) | x);
- result[cnt / 2] = v;
+ r[1 + cnt] = '\\';
+ r[1 + cnt + 1] = 'x';
+ cnt += 2;
}
- else
- v = x;
+ r[1 + cnt] = c;
++cnt;
}
}
- result.length = cnt / 2;
+ r[1 + cnt] = '"';
+ result.length = 1 + cnt + 1; // trim off any excess length
return result;
}
+
@safe unittest
{
// compile time
@@ -5889,8 +5656,8 @@ private auto hexStrImpl(String)(scope String hexData)
auto toChars(ubyte radix = 10, Char = char, LetterCase letterCase = LetterCase.lower, T)(T value)
pure nothrow @nogc @safe
if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) &&
- (is(Unqual!T == uint) || is(Unqual!T == ulong) ||
- radix == 10 && (is(Unqual!T == int) || is(Unqual!T == long))))
+ (is(immutable T == immutable uint) || is(immutable T == immutable ulong) ||
+ radix == 10 && (is(immutable T == immutable int) || is(immutable T == immutable long))))
{
alias UT = Unqual!T;
@@ -5967,7 +5734,7 @@ if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) &&
char[(UT.sizeof == 4) ? 10 + isSigned!T : 20] buf = void;
}
- Result result = void;
+ Result result;
result.initialize(value);
return result;
}
@@ -5980,7 +5747,7 @@ if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) &&
else static if (radix == 16)
enum SHIFT = 4;
else
- static assert(0);
+ static assert(false, "radix must be 2, 8, 10, or 16");
static struct Result
{
this(UT value)
@@ -6036,12 +5803,27 @@ if ((radix == 2 || radix == 8 || radix == 10 || radix == 16) &&
}
}
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(toChars(1).equal("1"));
+ assert(toChars(1_000_000).equal("1000000"));
+
+ assert(toChars!(2)(2U).equal("10"));
+ assert(toChars!(16)(255U).equal("ff"));
+ assert(toChars!(16, char, LetterCase.upper)(255U).equal("FF"));
+}
+
@safe unittest
{
import std.array;
import std.range;
+ assert(toChars(123) == toChars(123));
+
{
assert(toChars!2(0u).array == "0");
assert(toChars!2(0Lu).array == "0");
diff --git a/libphobos/src/std/csv.d b/libphobos/src/std/csv.d
index b10f1e65b60..7f5c2b24c01 100644
--- a/libphobos/src/std/csv.d
+++ b/libphobos/src/std/csv.d
@@ -2,7 +2,7 @@
/**
* Implements functionality to read Comma Separated Values and its variants
- * from an input range of $(D dchar).
+ * from an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of `dchar`.
*
* Comma Separated Values provide a simple means to transfer and store
* tabular data. It has been common for programs to use their own
@@ -53,11 +53,11 @@
* }
* -------
*
- * When an input contains a header the $(D Contents) can be specified as an
+ * When an input contains a header the `Contents` can be specified as an
* associative array. Passing null to signify that a header is present.
*
* -------
- * auto text = "Name,Occupation,Salary\r"
+ * auto text = "Name,Occupation,Salary\r" ~
* "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n";
*
* foreach (record; csvReader!(string[string])
@@ -67,6 +67,18 @@
* record["Name"], record["Occupation"],
* record["Salary"]);
* }
+ *
+ * // To read the same string from the file "filename.csv":
+ *
+ * auto file = File("filename.csv", "r");
+ *
+ * foreach (record; csvReader!(string[string])
+ * (file.byLine.joiner("\n"), null))
+ * {
+ * writefln("%s works as a %s and earns $%s per year.",
+ * record["Name"], record["Occupation"],
+ * record["Salary"]);
+ * }
* -------
*
* This module allows content to be iterated by record stored in a struct,
@@ -87,12 +99,12 @@
* Copyright: Copyright 2011
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Jesse Phillips
- * Source: $(PHOBOSSRC std/_csv.d)
+ * Source: $(PHOBOSSRC std/csv.d)
*/
module std.csv;
import std.conv;
-import std.exception; // basicExceptionCtors
+import std.exception : basicExceptionCtors;
import std.range.primitives;
import std.traits;
@@ -105,14 +117,15 @@ import std.traits;
* HeaderMismatchException) for details.
*
* When performing type conversions, $(REF ConvException, std,conv) is stored in
- * the $(D next) field.
+ * the `next` field.
*/
class CSVException : Exception
{
///
size_t row, col;
- // FIXME: Use std.exception.basicExceptionCtors here once bug #11500 is fixed
+ // FIXME: Use std.exception.basicExceptionCtors here once
+ // https://issues.dlang.org/show_bug.cgi?id=11500 is fixed
this(string msg, string file = __FILE__, size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow
@@ -141,6 +154,27 @@ class CSVException : Exception
}
}
+///
+@safe unittest
+{
+ import std.exception : collectException;
+ import std.algorithm.searching : count;
+ string text = "a,b,c\nHello,65";
+ auto ex = collectException!CSVException(csvReader(text).count);
+ assert(ex.toString == "(Row: 0, Col: 0) Row 2's length 2 does not match previous length of 3.");
+}
+
+///
+@safe unittest
+{
+ import std.exception : collectException;
+ import std.algorithm.searching : count;
+ import std.typecons : Tuple;
+ string text = "a,b\nHello,65";
+ auto ex = collectException!CSVException(csvReader!(Tuple!(string,int))(text).count);
+ assert(ex.toString == "(Row: 1, Col: 2) Unexpected 'b' when converting from type string to type int");
+}
+
@safe pure unittest
{
import std.string;
@@ -179,6 +213,14 @@ class IncompleteCellException : CSVException
mixin basicExceptionCtors;
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ string text = "a,\"b,c\nHello,65,2.5";
+ assertThrown!IncompleteCellException(text.csvReader(["a","b","c"]));
+}
+
@safe pure unittest
{
auto e1 = new Exception("Foobar");
@@ -211,6 +253,14 @@ class HeaderMismatchException : CSVException
mixin basicExceptionCtors;
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ string text = "a,b,c\nHello,65,2.5";
+ assertThrown!HeaderMismatchException(text.csvReader(["b","c","invalid"]));
+}
+
@safe pure unittest
{
auto e1 = new Exception("Foobar");
@@ -240,188 +290,252 @@ enum Malformed
throwException /// Use exceptions when input has incorrect CSV.
}
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.searching : count;
+ import std.exception : assertThrown;
+
+ string text = "a,b,c\nHello,65,\"2.5";
+ assertThrown!IncompleteCellException(text.csvReader.count);
+
+ // ignore the exceptions and try to handle invalid CSV
+ auto firstLine = text.csvReader!(string, Malformed.ignore)(null).front;
+ assert(firstLine.equal(["Hello", "65", "2.5"]));
+}
+
/**
- * Returns an input range for iterating over records found in $(D
- * input).
- *
- * The $(D Contents) of the input can be provided if all the records are the
- * same type such as all integer data:
- *
- * -------
- * string str = `76,26,22`;
- * int[] ans = [76,26,22];
- * auto records = csvReader!int(str);
- *
- * foreach (record; records)
- * {
- * assert(equal(record, ans));
- * }
- * -------
- *
- * Example using a struct with modified delimiter:
- *
- * -------
- * string str = "Hello;65;63.63\nWorld;123;3673.562";
- * struct Layout
- * {
- * string name;
- * int value;
- * double other;
- * }
- *
- * auto records = csvReader!Layout(str,';');
- *
- * foreach (record; records)
- * {
- * writeln(record.name);
- * writeln(record.value);
- * writeln(record.other);
- * }
- * -------
- *
- * Specifying $(D ErrorLevel) as Malformed.ignore will lift restrictions
- * on the format. This example shows that an exception is not thrown when
- * finding a quote in a field not quoted.
- *
- * -------
- * string str = "A \" is now part of the data";
- * auto records = csvReader!(string,Malformed.ignore)(str);
- * auto record = records.front;
- *
- * assert(record.front == str);
- * -------
- *
- * Returns:
- * An input range R as defined by
- * $(REF isInputRange, std,range,primitives). When $(D Contents) is a
- * struct, class, or an associative array, the element type of R is
- * $(D Contents), otherwise the element type of R is itself a range with
- * element type $(D Contents).
- *
- * Throws:
- * $(LREF CSVException) When a quote is found in an unquoted field,
- * data continues after a closing quote, the quoted field was not
- * closed before data was empty, a conversion failed, or when the row's
- * length does not match the previous length.
- *
- * $(LREF HeaderMismatchException) when a header is provided but a
- * matching column is not found or the order did not match that found in
- * the input. Read the exception documentation for specific details of
- * when the exception is thrown for different types of $(D Contents).
- */
+Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+for iterating over records found in `input`.
+
+An optional `header` can be provided. The first record will be read in
+as the header. If `Contents` is a struct then the header provided is
+expected to correspond to the fields in the struct. When `Contents` is
+not a type which can contain the entire record, the `header` must be
+provided in the same order as the input or an exception is thrown.
+
+Returns:
+ An input range R as defined by
+ $(REF isInputRange, std,range,primitives). When `Contents` is a
+ struct, class, or an associative array, the element type of R is
+ `Contents`, otherwise the element type of R is itself a range with
+ element type `Contents`.
+
+ If a `header` argument is provided,
+ the returned range provides a `header` field for accessing the header
+ from the input in array form.
+
+Throws:
+ $(LREF CSVException) When a quote is found in an unquoted field,
+ data continues after a closing quote, the quoted field was not
+ closed before data was empty, a conversion failed, or when the row's
+ length does not match the previous length.
+
+ $(LREF HeaderMismatchException) when a header is provided but a
+ matching column is not found or the order did not match that found in
+ the input. Read the exception documentation for specific details of
+ when the exception is thrown for different types of `Contents`.
+*/
auto csvReader(Contents = string,Malformed ErrorLevel = Malformed.throwException, Range, Separator = char)(Range input,
- Separator delimiter = ',', Separator quote = '"')
-if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)
+ Separator delimiter = ',', Separator quote = '"',
+ bool allowInconsistentDelimiterCount = false)
+if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar)
&& isSomeChar!(Separator)
&& !is(Contents T : T[U], U : string))
{
return CsvReader!(Contents,ErrorLevel,Range,
Unqual!(ElementType!Range),string[])
- (input, delimiter, quote);
+ (input, delimiter, quote, allowInconsistentDelimiterCount);
}
-/**
- * An optional $(D header) can be provided. The first record will be read in
- * as the header. If $(D Contents) is a struct then the header provided is
- * expected to correspond to the fields in the struct. When $(D Contents) is
- * not a type which can contain the entire record, the $(D header) must be
- * provided in the same order as the input or an exception is thrown.
- *
- * Read only column "b":
- *
- * -------
- * string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
- * auto records = csvReader!int(str, ["b"]);
- *
- * auto ans = [[65],[123]];
- * foreach (record; records)
- * {
- * assert(equal(record, ans.front));
- * ans.popFront();
- * }
- * -------
- *
- * Read from header of different order:
- *
- * -------
- * string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
- * struct Layout
- * {
- * int value;
- * double other;
- * string name;
- * }
- *
- * auto records = csvReader!Layout(str, ["b","c","a"]);
- * -------
- *
- * The header can also be left empty if the input contains a header but
- * all columns should be iterated. The header from the input can always
- * be accessed from the header field.
- *
- * -------
- * string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
- * auto records = csvReader(str, null);
- *
- * assert(records.header == ["a","b","c"]);
- * -------
- *
- * Returns:
- * An input range R as defined by
- * $(REF isInputRange, std,range,primitives). When $(D Contents) is a
- * struct, class, or an associative array, the element type of R is
- * $(D Contents), otherwise the element type of R is itself a range with
- * element type $(D Contents).
- *
- * The returned range provides a header field for accessing the header
- * from the input in array form.
- *
- * -------
- * string str = "a,b,c\nHello,65,63.63";
- * auto records = csvReader(str, ["a"]);
- *
- * assert(records.header == ["a","b","c"]);
- * -------
- *
- * Throws:
- * $(LREF CSVException) When a quote is found in an unquoted field,
- * data continues after a closing quote, the quoted field was not
- * closed before data was empty, a conversion failed, or when the row's
- * length does not match the previous length.
- *
- * $(LREF HeaderMismatchException) when a header is provided but a
- * matching column is not found or the order did not match that found in
- * the input. Read the exception documentation for specific details of
- * when the exception is thrown for different types of $(D Contents).
- */
+/// ditto
auto csvReader(Contents = string,
Malformed ErrorLevel = Malformed.throwException,
Range, Header, Separator = char)
(Range input, Header header,
- Separator delimiter = ',', Separator quote = '"')
-if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)
+ Separator delimiter = ',', Separator quote = '"',
+ bool allowInconsistentDelimiterCount = false)
+if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar)
&& isSomeChar!(Separator)
&& isForwardRange!Header
&& isSomeString!(ElementType!Header))
{
return CsvReader!(Contents,ErrorLevel,Range,
Unqual!(ElementType!Range),Header)
- (input, header, delimiter, quote);
+ (input, header, delimiter, quote, allowInconsistentDelimiterCount);
}
-///
+/// ditto
auto csvReader(Contents = string,
Malformed ErrorLevel = Malformed.throwException,
Range, Header, Separator = char)
(Range input, Header header,
- Separator delimiter = ',', Separator quote = '"')
-if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)
+ Separator delimiter = ',', Separator quote = '"',
+ bool allowInconsistentDelimiterCount = false)
+if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar)
&& isSomeChar!(Separator)
&& is(Header : typeof(null)))
{
return CsvReader!(Contents,ErrorLevel,Range,
Unqual!(ElementType!Range),string[])
- (input, cast(string[]) null, delimiter, quote);
+ (input, cast(string[]) null, delimiter, quote,
+ allowInconsistentDelimiterCount);
+}
+
+
+/**
+The `Contents` of the input can be provided if all the records are the
+same type such as all integer data:
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ string text = "76,26,22";
+ auto records = text.csvReader!int;
+ assert(records.equal!equal([
+ [76, 26, 22],
+ ]));
+}
+
+/**
+Using a struct with modified delimiter:
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ string text = "Hello;65;2.5\nWorld;123;7.5";
+ struct Layout
+ {
+ string name;
+ int value;
+ double other;
+ }
+
+ auto records = text.csvReader!Layout(';');
+ assert(records.equal([
+ Layout("Hello", 65, 2.5),
+ Layout("World", 123, 7.5),
+ ]));
+}
+
+/**
+Specifying `ErrorLevel` as $(LREF Malformed.ignore) will lift restrictions
+on the format. This example shows that an exception is not thrown when
+finding a quote in a field not quoted.
+*/
+@safe unittest
+{
+ string text = "A \" is now part of the data";
+ auto records = text.csvReader!(string, Malformed.ignore);
+ auto record = records.front;
+
+ assert(record.front == text);
+}
+
+/// Read only column "b"
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ string text = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
+ auto records = text.csvReader!int(["b"]);
+
+ assert(records.equal!equal([
+ [65],
+ [123],
+ ]));
+}
+
+/// Read while rearranging the columns by specifying a header with a different order"
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ string text = "a,b,c\nHello,65,2.5\nWorld,123,7.5";
+ struct Layout
+ {
+ int value;
+ double other;
+ string name;
+ }
+
+ auto records = text.csvReader!Layout(["b","c","a"]);
+ assert(records.equal([
+ Layout(65, 2.5, "Hello"),
+ Layout(123, 7.5, "World")
+ ]));
+}
+
+/**
+The header can also be left empty if the input contains a header row
+and all columns should be iterated.
+The header from the input can always be accessed from the `header` field.
+*/
+@safe unittest
+{
+ string text = "a,b,c\nHello,65,63.63";
+ auto records = text.csvReader(null);
+
+ assert(records.header == ["a","b","c"]);
+}
+
+/**
+Handcrafted csv files tend to have an variable amount of columns.
+
+By default `std.csv` will throw if the number of columns on a line
+is unequal to the number of columns of the first line.
+To allow, or disallow, a variable amount of columns a `bool` can be passed to
+all overloads of the `csvReader` function as shown below.
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ string text = "76,26,22\n1,2\n3,4,5,6";
+ auto records = text.csvReader!int(',', '"', true);
+
+ assert(records.equal!equal([
+ [76, 26, 22],
+ [1, 2],
+ [3, 4, 5, 6]
+ ]));
+}
+
+/// ditto
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ static struct Three
+ {
+ int a;
+ int b;
+ int c;
+ }
+
+ string text = "76,26,22\n1,2\n3,4,5,6";
+ auto records = text.csvReader!Three(',', '"', true);
+
+ assert(records.equal([
+ Three(76, 26, 22),
+ Three(1, 2, 0),
+ Three(3, 4, 5)
+ ]));
+}
+
+/// ditto
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto text = "Name,Occupation,Salary\r" ~
+ "Joe,Carpenter,300000\nFred,Blacksmith\r\n";
+
+ auto r = csvReader!(string[string])(text, null, ',', '"', true);
+
+ assert(r.equal([
+ [ "Name" : "Joe", "Occupation" : "Carpenter", "Salary" : "300000" ],
+ [ "Name" : "Fred", "Occupation" : "Blacksmith" ]
+ ]));
}
// Test standard iteration over input.
@@ -506,7 +620,7 @@ if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)
// Test structure conversion interface with unicode.
@safe pure unittest
{
- import std.math : abs;
+ import std.math.algebraic : abs;
wstring str = "\U00010143Hello,65,63.63\nWorld,123,3673.562"w;
struct Layout
@@ -554,7 +668,7 @@ if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)
// Test struct & header interface and same unicode
@safe unittest
{
- import std.math : abs;
+ import std.math.algebraic : abs;
string str = "a,b,c\nHello,65,63.63\n➊➋➂â¹,123,3673.562";
struct Layout
@@ -779,12 +893,12 @@ private pure struct Input(Range, Malformed ErrorLevel)
* Range for iterating CSV records.
*
* This range is returned by the $(LREF csvReader) functions. It can be
- * created in a similar manner to allow $(D ErrorLevel) be set to $(LREF
+ * created in a similar manner to allow `ErrorLevel` be set to $(LREF
* Malformed).ignore if best guess processing should take place.
*/
private struct CsvReader(Contents, Malformed ErrorLevel, Range, Separator, Header)
if (isSomeChar!Separator && isInputRange!Range
- && is(Unqual!(ElementType!Range) == dchar)
+ && is(immutable ElementType!Range == immutable dchar)
&& isForwardRange!Header && isSomeString!(ElementType!Header))
{
private:
@@ -793,6 +907,7 @@ private:
Separator _quote;
size_t[] indices;
bool _empty;
+ bool _allowInconsistentDelimiterCount;
static if (is(Contents == struct) || is(Contents == class))
{
Contents recordContent;
@@ -834,11 +949,19 @@ public:
* }
* -------
*/
- this(Range input, Separator delimiter, Separator quote)
+ this(Range input, Separator delimiter, Separator quote,
+ bool allowInconsistentDelimiterCount)
{
_input = new Input!(Range, ErrorLevel)(input);
_separator = delimiter;
_quote = quote;
+ _allowInconsistentDelimiterCount = allowInconsistentDelimiterCount;
+
+ if (_input.range.empty)
+ {
+ _empty = true;
+ return;
+ }
prime();
}
@@ -864,11 +987,19 @@ public:
* matching column is not found or the order did not match that found
* in the input (non-struct).
*/
- this(Range input, Header colHeaders, Separator delimiter, Separator quote)
+ this(Range input, Header colHeaders, Separator delimiter, Separator quote,
+ bool allowInconsistentDelimiterCount)
{
_input = new Input!(Range, ErrorLevel)(input);
_separator = delimiter;
_quote = quote;
+ _allowInconsistentDelimiterCount = allowInconsistentDelimiterCount;
+
+ if (_input.range.empty)
+ {
+ _empty = true;
+ return;
+ }
size_t[string] colToIndex;
foreach (h; colHeaders)
@@ -877,7 +1008,8 @@ public:
}
auto r = CsvRecord!(string, ErrorLevel, Range, Separator)
- (_input, _separator, _quote, indices);
+ (_input, _separator, _quote, indices,
+ _allowInconsistentDelimiterCount);
size_t colIndex;
foreach (col; r)
@@ -890,6 +1022,8 @@ public:
}
// The above loop empties the header row.
recordRange._empty = true;
+ recordRange._allowInconsistentDelimiterCount =
+ allowInconsistentDelimiterCount;
indices.length = colToIndex.length;
int i;
@@ -940,19 +1074,19 @@ public:
* $(REF isInputRange, std,range,primitives).
*
* Returns:
- * If $(D Contents) is a struct, will be filled with record data.
+ * If `Contents` is a struct, will be filled with record data.
*
- * If $(D Contents) is a class, will be filled with record data.
+ * If `Contents` is a class, will be filled with record data.
*
- * If $(D Contents) is a associative array, will be filled
+ * If `Contents` is a associative array, will be filled
* with record data.
*
- * If $(D Contents) is non-struct, a $(LREF CsvRecord) will be
+ * If `Contents` is non-struct, a $(LREF CsvRecord) will be
* returned.
*/
@property auto front()
{
- assert(!empty);
+ assert(!empty, "Attempting to fetch the front of an empty CsvReader");
static if (is(Contents == struct) || is(Contents == class))
{
return recordContent;
@@ -1028,12 +1162,14 @@ public:
static if (is(Contents == struct) || is(Contents == class))
{
recordRange = typeof(recordRange)
- (_input, _separator, _quote, null);
+ (_input, _separator, _quote, null,
+ _allowInconsistentDelimiterCount);
}
else
{
recordRange = typeof(recordRange)
- (_input, _separator, _quote, indices);
+ (_input, _separator, _quote, indices,
+ _allowInconsistentDelimiterCount);
}
static if (is(Contents T : T[U], U : string))
@@ -1106,7 +1242,7 @@ public:
string str = `76;^26^;22`;
int[] ans = [76,26,22];
auto records = CsvReader!(int,Malformed.ignore,string,char,string[])
- (str, ';', '^');
+ (str, ';', '^', false);
foreach (record; records)
{
@@ -1114,7 +1250,7 @@ public:
}
}
-// Bugzilla 15545
+// https://issues.dlang.org/show_bug.cgi?id=15545
// @system due to the catch for Throwable
@system pure unittest
{
@@ -1128,7 +1264,7 @@ public:
/*
* This input range is accessible through $(LREF CsvReader) when the
- * requested $(D Contents) type is neither a structure or an associative array.
+ * requested `Contents` type is neither a structure or an associative array.
*/
private struct CsvRecord(Contents, Malformed ErrorLevel, Range, Separator)
if (!is(Contents == class) && !is(Contents == struct))
@@ -1141,24 +1277,28 @@ private:
Contents curContentsoken;
typeof(appender!(dchar[])()) _front;
bool _empty;
+ bool _allowInconsistentDelimiterCount;
size_t[] _popCount;
public:
/*
* Params:
- * input = Pointer to a character input range
+ * input = Pointer to a character $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
* delimiter = Separator for each column
* quote = Character used for quotation
* indices = An array containing which columns will be returned.
* If empty, all columns are returned. List must be in order.
*/
this(Input!(Range, ErrorLevel)* input, Separator delimiter,
- Separator quote, size_t[] indices)
+ Separator quote, size_t[] indices,
+ bool allowInconsistentDelimiterCount)
{
_input = input;
_separator = delimiter;
_quote = quote;
+
_front = appender!(dchar[])();
_popCount = indices.dup;
+ _allowInconsistentDelimiterCount = allowInconsistentDelimiterCount;
// If a header was given, each call to popFront will need
// to eliminate so many tokens. This calculates
@@ -1187,7 +1327,7 @@ public:
*/
@property Contents front() @safe pure
{
- assert(!empty);
+ assert(!empty, "Attempting to fetch the front of an empty CsvRecord");
return curContentsoken;
}
@@ -1241,23 +1381,38 @@ public:
{
_empty = true;
static if (ErrorLevel == Malformed.throwException)
- if (_input.rowLength != 0)
- if (_input.col != _input.rowLength)
- throw new CSVException(
- format("Row %s's length %s does not match "~
- "previous length of %s.", _input.row,
- _input.col, _input.rowLength));
+ {
+ if (_input.rowLength != 0 && _input.col != _input.rowLength
+ && !_allowInconsistentDelimiterCount)
+ {
+ throw new CSVException(
+ format("Row %s's length %s does not match "~
+ "previous length of %s.", _input.row,
+ _input.col, _input.rowLength));
+ }
+ }
return;
}
else
{
static if (ErrorLevel == Malformed.throwException)
- if (_input.rowLength != 0)
- if (_input.col > _input.rowLength)
+ {
+ if (_input.rowLength != 0 && _input.col > _input.rowLength)
+ {
+ if (!_allowInconsistentDelimiterCount)
+ {
throw new CSVException(
format("Row %s's length %s does not match "~
"previous length of %s.", _input.row,
_input.col, _input.rowLength));
+ }
+ else
+ {
+ _empty = true;
+ return;
+ }
+ }
+ }
}
// Separator is left on the end of input from the last call.
@@ -1367,7 +1522,7 @@ void csvNextToken(Range, Malformed ErrorLevel = Malformed.throwException,
Separator sep, Separator quote,
bool startQuoted = false)
if (isSomeChar!Separator && isInputRange!Range
- && is(Unqual!(ElementType!Range) == dchar)
+ && is(immutable ElementType!Range == immutable dchar)
&& isOutputRange!(Output, dchar))
{
bool quoted = startQuoted;
@@ -1388,7 +1543,8 @@ if (isSomeChar!Separator && isInputRange!Range
while (!input.empty)
{
- assert(!(quoted && escQuote));
+ assert(!(quoted && escQuote),
+ "Invalid quotation state in csvNextToken");
if (!quoted)
{
// When not quoted the token ends at sep
@@ -1667,7 +1823,7 @@ if (isSomeChar!Separator && isInputRange!Range
assert(a.data == ""d);
}
-// Bugzilla 8908
+// https://issues.dlang.org/show_bug.cgi?id=8908
@safe pure unittest
{
string csv = ` 1.0, 2.0, 3.0
@@ -1699,3 +1855,23 @@ if (isSomeChar!Separator && isInputRange!Range
++i;
}
}
+
+// https://issues.dlang.org/show_bug.cgi?id=21629
+@safe pure unittest
+{
+ import std.typecons : Tuple;
+ struct Reccord
+ {
+ string a;
+ string b;
+ }
+
+ auto header = ["a" ,"b"];
+ string input = "";
+ assert(csvReader!Reccord(input).empty, "This should be empty");
+ assert(csvReader!Reccord(input, header).empty, "This should be empty");
+ assert(csvReader!(Tuple!(string,string))(input).empty, "This should be empty");
+ assert(csvReader!(string[string])(input, header).empty, "This should be empty");
+ assert(csvReader!(string[string])(input, null).empty, "This should be empty");
+ assert(csvReader!(int)(input, null).empty, "This should be empty");
+}
diff --git a/libphobos/src/std/datetime/date.d b/libphobos/src/std/datetime/date.d
index d571f5fdd33..ebdaba42a9d 100644
--- a/libphobos/src/std/datetime/date.d
+++ b/libphobos/src/std/datetime/date.d
@@ -1,18 +1,52 @@
// Written in the D programming language
-
/++
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Main date types) $(TD
+ $(LREF Date)
+ $(LREF DateTime)
+))
+$(TR $(TD Other date types) $(TD
+ $(LREF Month)
+ $(LREF DayOfWeek)
+ $(LREF TimeOfDay)
+))
+$(TR $(TD Date checking) $(TD
+ $(LREF valid)
+ $(LREF validTimeUnits)
+ $(LREF yearIsLeapYear)
+ $(LREF isTimePoint)
+ $(LREF enforceValid)
+))
+$(TR $(TD Date conversion) $(TD
+ $(LREF daysToDayOfWeek)
+ $(LREF monthsToMonth)
+))
+$(TR $(TD Time units) $(TD
+ $(LREF cmpTimeUnits)
+ $(LREF timeStrings)
+))
+$(TR $(TD Other) $(TD
+ $(LREF AllowDayOverflow)
+ $(LREF DateTimeException)
+))
+))
+
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: Jonathan M Davis
- Source: $(PHOBOSSRC std/datetime/_date.d)
+ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis)
+ Source: $(PHOBOSSRC std/datetime/date.d)
+/
module std.datetime.date;
-import core.time;
+import core.time : TimeException;
import std.traits : isSomeString, Unqual;
import std.typecons : Flag;
+import std.range.primitives : isOutputRange;
-version (unittest) import std.exception : assertThrown;
-
+version (StdUnittest) import std.exception : assertThrown;
@safe unittest
{
@@ -47,6 +81,13 @@ enum Month : ubyte
dec ///
}
+///
+@safe pure unittest
+{
+ assert(Date(2018, 10, 1).month == Month.oct);
+ assert(DateTime(1, 1, 1).month == Month.jan);
+}
+
/++
Represents the 7 days of the Gregorian week (Sunday is 0).
@@ -62,6 +103,12 @@ enum DayOfWeek : ubyte
sat ///
}
+///
+@safe pure unittest
+{
+ assert(Date(2018, 10, 1).dayOfWeek == DayOfWeek.mon);
+ assert(DateTime(5, 5, 5).dayOfWeek == DayOfWeek.thu);
+}
/++
In some date calculations, adding months or years can cause the date to fall
@@ -75,9 +122,9 @@ enum DayOfWeek : ubyte
AllowDayOverflow only applies to calculations involving months or years.
- If set to $(D AllowDayOverflow.no), then day overflow is not allowed.
+ If set to `AllowDayOverflow.no`, then day overflow is not allowed.
- Otherwise, if set to $(D AllowDayOverflow.yes), then day overflow is
+ Otherwise, if set to `AllowDayOverflow.yes`, then day overflow is
allowed.
+/
alias AllowDayOverflow = Flag!"allowDayOverflow";
@@ -85,26 +132,27 @@ alias AllowDayOverflow = Flag!"allowDayOverflow";
/++
Array of the strings representing time units, starting with the smallest
- unit and going to the largest. It does not include $(D "nsecs").
+ unit and going to the largest. It does not include `"nsecs"`.
- Includes $(D "hnsecs") (hecto-nanoseconds (100 ns)),
- $(D "usecs") (microseconds), $(D "msecs") (milliseconds), $(D "seconds"),
- $(D "minutes"), $(D "hours"), $(D "days"), $(D "weeks"), $(D "months"), and
- $(D "years")
+ Includes `"hnsecs"` (hecto-nanoseconds (100 ns)),
+ `"usecs"` (microseconds), `"msecs"` (milliseconds), `"seconds"`,
+ `"minutes"`, `"hours"`, `"days"`, `"weeks"`, `"months"`, and
+ `"years"`
+/
immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes",
"hours", "days", "weeks", "months", "years"];
/++
- Combines the $(REF Date,std,datetime,date) and
- $(REF TimeOfDay,std,datetime,date) structs to give an object which holds
- both the date and the time. It is optimized for calendar-based operations and
- has no concept of time zone. For an object which is optimized for time
- operations based on the system time, use $(REF SysTime,std,datetime,systime).
- $(REF SysTime,std,datetime,systime) has a concept of time zone and has much
- higher precision (hnsecs). $(D DateTime) is intended primarily for
- calendar-based uses rather than precise time operations.
+ Combines the $(REF Date,std,datetime,date) and
+ $(REF TimeOfDay,std,datetime,date) structs to give an object which holds
+ both the date and the time. It is optimized for calendar-based operations
+ and has no concept of time zone. For an object which is optimized for time
+ operations based on the system time, use
+ $(REF SysTime,std,datetime,systime). $(REF SysTime,std,datetime,systime) has
+ a concept of time zone and has much higher precision (hnsecs). `DateTime`
+ is intended primarily for calendar-based uses rather than precise time
+ operations.
+/
struct DateTime
{
@@ -115,7 +163,7 @@ public:
date = The date portion of $(LREF DateTime).
tod = The time portion of $(LREF DateTime).
+/
- this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
+ this(Date date, TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
{
_date = date;
_tod = tod;
@@ -175,7 +223,7 @@ public:
/++
- Compares this $(LREF DateTime) with the given $(D DateTime.).
+ Compares this $(LREF DateTime) with the given `DateTime.`.
Returns:
$(BOOKTABLE,
@@ -184,7 +232,7 @@ public:
$(TR $(TD this &gt; rhs) $(TD &gt; 0))
)
+/
- int opCmp(in DateTime rhs) const @safe pure nothrow @nogc
+ int opCmp(DateTime rhs) const @safe pure nothrow @nogc
{
immutable dateResult = _date.opCmp(rhs._date);
@@ -424,7 +472,7 @@ public:
Params:
date = The Date to set this $(LREF DateTime)'s date portion to.
+/
- @property void date(in Date date) @safe pure nothrow @nogc
+ @property void date(Date date) @safe pure nothrow @nogc
{
_date = date;
}
@@ -477,7 +525,7 @@ public:
tod = The $(REF TimeOfDay,std,datetime,date) to set this
$(LREF DateTime)'s time portion to.
+/
- @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow @nogc
+ @property void timeOfDay(TimeOfDay tod) @safe pure nothrow @nogc
{
_tod = tod;
}
@@ -544,7 +592,7 @@ public:
@safe unittest
{
- static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__)
+ static void testDT(DateTime dt, int year, DateTime expected, size_t line = __LINE__)
{
dt.year = year;
assert(dt == expected);
@@ -571,7 +619,7 @@ public:
Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
Throws:
- $(REF DateTimeException,std,datetime,date) if $(D isAD) is true.
+ $(REF DateTimeException,std,datetime,date) if `isAD` is true.
+/
@property short yearBC() const @safe pure
{
@@ -588,7 +636,7 @@ public:
@safe unittest
{
- assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1))));
+ assertThrown!DateTimeException((DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1))));
auto dt = DateTime(1999, 7, 6, 12, 30, 33);
const cdt = DateTime(1999, 7, 6, 12, 30, 33);
@@ -686,7 +734,7 @@ public:
@safe unittest
{
- static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__)
+ static void testDT(DateTime dt, Month month, DateTime expected = DateTime.init, size_t line = __LINE__)
{
dt.month = month;
assert(expected != DateTime.init);
@@ -1004,8 +1052,8 @@ public:
/++
- Adds the given number of years or months to this $(LREF DateTime). A
- negative number will subtract.
+ Adds the given number of years or months to this $(LREF DateTime),
+ mutating it. A negative number will subtract.
Note that if day overflow is allowed, and the date with the adjusted
year/month overflows the number of days in the new month, then the month
@@ -1021,6 +1069,9 @@ public:
$(LREF DateTime).
allowOverflow = Whether the days should be allowed to overflow,
causing the month to increment.
+
+ Returns:
+ A reference to the `DateTime` (`this`).
+/
ref DateTime add(string units)
(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
@@ -1066,8 +1117,8 @@ public:
/++
- Adds the given number of years or months to this $(LREF DateTime). A
- negative number will subtract.
+ Adds the given number of years or months to this $(LREF DateTime),
+ mutating it. A negative number will subtract.
The difference between rolling and adding is that rolling does not
affect larger units. Rolling a $(LREF DateTime) 12 months
@@ -1083,6 +1134,9 @@ public:
$(LREF DateTime).
allowOverflow = Whether the days should be allowed to overflow,
causing the month to increment.
+
+ Returns:
+ A reference to the `DateTime` (`this`).
+/
ref DateTime roll(string units)
(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
@@ -1136,20 +1190,23 @@ public:
/++
- Adds the given number of units to this $(LREF DateTime). A negative
- number will subtract.
+ Adds the given number of units to this $(LREF DateTime), mutating it. A
+ negative number will subtract.
The difference between rolling and adding is that rolling does not
affect larger units. For instance, rolling a $(LREF DateTime) one
year's worth of days gets the exact same $(LREF DateTime).
- Accepted units are $(D "days"), $(D "minutes"), $(D "hours"),
- $(D "minutes"), and $(D "seconds").
+ Accepted units are `"days"`, `"minutes"`, `"hours"`,
+ `"minutes"`, and `"seconds"`.
Params:
units = The units to add.
value = The number of $(D_PARAM units) to add to this
$(LREF DateTime).
+
+ Returns:
+ A reference to the `DateTime` (`this`).
+/
ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
if (units == "days")
@@ -1191,7 +1248,7 @@ public:
}
- // Shares documentation with "days" version.
+ /// ditto
ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
if (units == "hours" ||
units == "minutes" ||
@@ -1204,7 +1261,7 @@ public:
// Test roll!"hours"().
@safe unittest
{
- static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__)
+ static void testDT(DateTime orig, int hours, DateTime expected, size_t line = __LINE__)
{
orig.roll!"hours"(hours);
assert(orig == expected);
@@ -1507,7 +1564,7 @@ public:
// Test roll!"minutes"().
@safe unittest
{
- static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__)
+ static void testDT(DateTime orig, int minutes, DateTime expected, size_t line = __LINE__)
{
orig.roll!"minutes"(minutes);
assert(orig == expected);
@@ -1807,7 +1864,7 @@ public:
// Test roll!"seconds"().
@safe unittest
{
- static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__)
+ static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
{
orig.roll!"seconds"(seconds);
assert(orig == expected);
@@ -2063,6 +2120,7 @@ public:
}
+ import core.time : Duration;
/++
Gives the result of adding or subtracting a $(REF Duration, core,time)
from this $(LREF DateTime).
@@ -2107,6 +2165,8 @@ public:
@safe unittest
{
+ import core.time : dur;
+
auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
@@ -2186,6 +2246,7 @@ public:
@safe unittest
{
+ import core.time : dur;
assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) ==
DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) ==
@@ -2277,12 +2338,13 @@ public:
$(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration))
)
+/
- Duration opBinary(string op)(in DateTime rhs) const @safe pure nothrow @nogc
+ Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc
if (op == "-")
{
immutable dateResult = _date - rhs.date;
immutable todResult = _tod - rhs._tod;
+ import core.time : dur;
return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs");
}
@@ -2290,6 +2352,7 @@ public:
{
auto dt = DateTime(1999, 7, 6, 12, 30, 33);
+ import core.time : dur;
assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) ==
dur!"seconds"(31_536_000));
assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
@@ -2362,7 +2425,7 @@ public:
Params:
rhs = The $(LREF DateTime) to subtract from this one.
+/
- int diffMonths(in DateTime rhs) const @safe pure nothrow @nogc
+ int diffMonths(DateTime rhs) const @safe pure nothrow @nogc
{
return _date.diffMonths(rhs._date);
}
@@ -2598,6 +2661,18 @@ public:
/++
+ The year of the ISO 8601 week calendar that this $(LREF DateTime) is in.
+
+ See_Also:
+ $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
+ +/
+ @property short isoWeekYear() const @safe pure nothrow
+ {
+ return _date.isoWeekYear;
+ }
+
+
+ /++
$(LREF DateTime) for the last day in the month that this
$(LREF DateTime) is in. The time portion of endOfMonth is always
23:59:59.
@@ -2790,24 +2865,39 @@ public:
/++
- Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS.
+ Converts this $(LREF DateTime) to a string with the format `YYYYMMDDTHHMMSS`.
+ If `writer` is set, the resulting string will be written directly to it.
+
+ Params:
+ writer = A `char` accepting
+ $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
string toISOString() const @safe pure nothrow
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(18);
try
- {
- return format!("%sT%02d%02d%02d")(
- _date.toISOString(),
- _tod._hour,
- _tod._minute,
- _tod._second
- );
- }
+ toISOString(w);
catch (Exception e)
- {
- assert(0, "format() threw.");
- }
+ assert(0, "toISOString() threw.");
+ return w.data;
+ }
+
+ /// ditto
+ void toISOString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ import std.format.write : formattedWrite;
+ _date.toISOString(writer);
+ formattedWrite!("T%02d%02d%02d")(
+ writer,
+ _tod._hour,
+ _tod._minute,
+ _tod._second
+ );
}
///
@@ -2852,24 +2942,39 @@ public:
/++
Converts this $(LREF DateTime) to a string with the format
- YYYY-MM-DDTHH:MM:SS.
+ `YYYY-MM-DDTHH:MM:SS`. If `writer` is set, the resulting
+ string will be written directly to it.
+
+ Params:
+ writer = A `char` accepting
+ $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
string toISOExtString() const @safe pure nothrow
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(20);
try
- {
- return format!("%sT%02d:%02d:%02d")(
- _date.toISOExtString(),
- _tod._hour,
- _tod._minute,
- _tod._second
- );
- }
+ toISOExtString(w);
catch (Exception e)
- {
- assert(0, "format() threw.");
- }
+ assert(0, "toISOExtString() threw.");
+ return w.data;
+ }
+
+ /// ditto
+ void toISOExtString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ import std.format.write : formattedWrite;
+ _date.toISOExtString(writer);
+ formattedWrite!("T%02d:%02d:%02d")(
+ writer,
+ _tod._hour,
+ _tod._minute,
+ _tod._second
+ );
}
///
@@ -2913,24 +3018,39 @@ public:
/++
Converts this $(LREF DateTime) to a string with the format
- YYYY-Mon-DD HH:MM:SS.
+ `YYYY-Mon-DD HH:MM:SS`. If `writer` is set, the resulting
+ string will be written directly to it.
+
+ Params:
+ writer = A `char` accepting
+ $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
string toSimpleString() const @safe pure nothrow
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(22);
try
- {
- return format!("%s %02d:%02d:%02d")(
- _date.toSimpleString(),
- _tod._hour,
- _tod._minute,
- _tod._second
- );
- }
+ toSimpleString(w);
catch (Exception e)
- {
- assert(0, "format() threw.");
- }
+ assert(0, "toSimpleString() threw.");
+ return w.data;
+ }
+
+ /// ditto
+ void toSimpleString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ import std.format.write : formattedWrite;
+ _date.toSimpleString(writer);
+ formattedWrite!(" %02d:%02d:%02d")(
+ writer,
+ _tod._hour,
+ _tod._minute,
+ _tod._second
+ );
}
///
@@ -3011,7 +3131,12 @@ public:
assert(idt.toString());
}
-
+ /// ditto
+ void toString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ toSimpleString(writer);
+ }
/++
Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS.
@@ -3025,18 +3150,19 @@ public:
not in the ISO format or if the resulting $(LREF DateTime) would not
be valid.
+/
- static DateTime fromISOString(S)(in S isoString) @safe pure
+ static DateTime fromISOString(S)(scope const S isoString) @safe pure
if (isSomeString!S)
{
import std.algorithm.searching : countUntil;
import std.exception : enforce;
import std.format : format;
import std.string : strip;
+ import std.utf : byCodeUnit;
auto str = strip(isoString);
enforce(str.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString)));
- auto t = str.countUntil('T');
+ auto t = str.byCodeUnit.countUntil('T');
enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString)));
@@ -3090,7 +3216,7 @@ public:
assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201"));
assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01"));
- assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01)));
+ assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
@@ -3099,14 +3225,14 @@ public:
assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
assert(DateTime.fromISOString(to!S("20121221T141516")) == DateTime(2012, 12, 21, 14, 15, 16));
}
}
@@ -3125,18 +3251,19 @@ public:
not in the ISO Extended format or if the resulting $(LREF DateTime)
would not be valid.
+/
- static DateTime fromISOExtString(S)(in S isoExtString) @safe pure
+ static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure
if (isSomeString!(S))
{
import std.algorithm.searching : countUntil;
import std.exception : enforce;
import std.format : format;
import std.string : strip;
+ import std.utf : byCodeUnit;
auto str = strip(isoExtString);
enforce(str.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- auto t = str.countUntil('T');
+ auto t = str.byCodeUnit.countUntil('T');
enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
@@ -3189,7 +3316,7 @@ public:
assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201"));
assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01"));
- assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01)));
+ assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
@@ -3198,14 +3325,14 @@ public:
assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
}
}
@@ -3224,18 +3351,19 @@ public:
not in the correct format or if the resulting $(LREF DateTime)
would not be valid.
+/
- static DateTime fromSimpleString(S)(in S simpleString) @safe pure
+ static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure
if (isSomeString!(S))
{
import std.algorithm.searching : countUntil;
import std.exception : enforce;
import std.format : format;
import std.string : strip;
+ import std.utf : byCodeUnit;
auto str = strip(simpleString);
enforce(str.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString)));
- auto t = str.countUntil(' ');
+ auto t = str.byCodeUnit.countUntil(' ');
enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString)));
@@ -3286,7 +3414,7 @@ public:
assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201"));
assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") ==
- DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01)));
+ DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") ==
DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") ==
@@ -3301,14 +3429,14 @@ public:
DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
}
}
@@ -3324,7 +3452,7 @@ public:
assert(result._date == Date.min);
assert(result._tod == TimeOfDay.min);
}
- body
+ do
{
auto dt = DateTime.init;
dt._date._year = short.min;
@@ -3351,7 +3479,7 @@ public:
assert(result._date == Date.max);
assert(result._tod == TimeOfDay.max);
}
- body
+ do
{
auto dt = DateTime.init;
dt._date._year = short.max;
@@ -3384,6 +3512,7 @@ private:
+/
ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc
{
+ import core.time : convert;
long hnsecs = convert!("seconds", "hnsecs")(seconds);
hnsecs += convert!("hours", "hnsecs")(_tod._hour);
hnsecs += convert!("minutes", "hnsecs")(_tod._minute);
@@ -3412,7 +3541,7 @@ private:
@safe unittest
{
- static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__)
+ static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
{
orig._addSeconds(seconds);
assert(orig == expected);
@@ -3585,6 +3714,29 @@ private:
TimeOfDay _tod;
}
+///
+@safe pure unittest
+{
+ import core.time : days, seconds;
+
+ auto dt = DateTime(2000, 6, 1, 10, 30, 0);
+
+ assert(dt.date == Date(2000, 6, 1));
+ assert(dt.timeOfDay == TimeOfDay(10, 30, 0));
+ assert(dt.dayOfYear == 153);
+ assert(dt.dayOfWeek == DayOfWeek.thu);
+
+ dt += 10.days + 100.seconds;
+ assert(dt == DateTime(2000, 6, 11, 10, 31, 40));
+
+ assert(dt.toISOExtString() == "2000-06-11T10:31:40");
+ assert(dt.toISOString() == "20000611T103140");
+ assert(dt.toSimpleString() == "2000-Jun-11 10:31:40");
+
+ assert(DateTime.fromISOExtString("2018-01-01T12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
+ assert(DateTime.fromISOString("20180101T120000") == DateTime(2018, 1, 1, 12, 0, 0));
+ assert(DateTime.fromSimpleString("2018-Jan-01 12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
+}
/++
Represents a date in the
@@ -3592,10 +3744,10 @@ private:
Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years
are A.D. Non-positive years are B.C.
- Year, month, and day are kept separately internally so that $(D Date) is
+ Year, month, and day are kept separately internally so that `Date` is
optimized for calendar-based operations.
- $(D Date) uses the Proleptic Gregorian Calendar, so it assumes the Gregorian
+ `Date` uses the Proleptic Gregorian Calendar, so it assumes the Gregorian
leap year calculations for its entire length. As per
$(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as
year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C.
@@ -3634,7 +3786,7 @@ public:
import std.exception : assertNotThrown;
assert(Date(1, 1, 1) == Date.init);
- static void testDate(in Date date, int year, int month, int day)
+ static void testDate(Date date, int year, int month, int day)
{
assert(date._year == year);
assert(date._month == month);
@@ -3829,7 +3981,7 @@ public:
$(TR $(TD this &gt; rhs) $(TD &gt; 0))
)
+/
- int opCmp(in Date rhs) const @safe pure nothrow @nogc
+ int opCmp(Date rhs) const @safe pure nothrow @nogc
{
if (_year < rhs._year)
return -1;
@@ -3994,7 +4146,7 @@ public:
date.year = year;
}
- static void testDate(Date date, int year, in Date expected)
+ static void testDate(Date date, int year, Date expected)
{
date.year = year;
assert(date == expected);
@@ -4017,7 +4169,7 @@ public:
Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
Throws:
- $(REF DateTimeException,std,datetime,date) if $(D isAD) is true.
+ $(REF DateTimeException,std,datetime,date) if `isAD` is true.
+/
@property ushort yearBC() const @safe pure
{
@@ -4038,7 +4190,7 @@ public:
@safe unittest
{
- assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1)));
+ assertThrown!DateTimeException((Date date){date.yearBC;}(Date(1, 1, 1)));
auto date = Date(0, 7, 6);
const cdate = Date(0, 7, 6);
@@ -4139,7 +4291,7 @@ public:
@safe unittest
{
- static void testDate(Date date, Month month, in Date expected = Date.init)
+ static void testDate(Date date, Month month, Date expected = Date.init)
{
date.month = month;
assert(expected != Date.init);
@@ -4304,8 +4456,8 @@ public:
/++
- Adds the given number of years or months to this $(LREF Date). A
- negative number will subtract.
+ Adds the given number of years or months to this $(LREF Date), mutating
+ it. A negative number will subtract.
Note that if day overflow is allowed, and the date with the adjusted
year/month overflows the number of days in the new month, then the month
@@ -4321,6 +4473,9 @@ public:
$(LREF Date).
allowOverflow = Whether the day should be allowed to overflow,
causing the month to increment.
+
+ Returns:
+ A reference to the `Date` (`this`).
+/
@safe pure nothrow @nogc
ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
@@ -5086,8 +5241,8 @@ public:
/++
- Adds the given number of years or months to this $(LREF Date). A negative
- number will subtract.
+ Adds the given number of years or months to this $(LREF Date), mutating
+ it. A negative number will subtract.
The difference between rolling and adding is that rolling does not
affect larger units. Rolling a $(LREF Date) 12 months gets
@@ -5103,6 +5258,9 @@ public:
$(LREF Date).
allowOverflow = Whether the day should be allowed to overflow,
causing the month to increment.
+
+ Returns:
+ A reference to the `Date` (`this`).
+/
@safe pure nothrow @nogc
ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
@@ -5731,18 +5889,21 @@ public:
/++
- Adds the given number of units to this $(LREF Date). A negative number
- will subtract.
+ Adds the given number of units to this $(LREF Date), mutating it. A
+ negative number will subtract.
The difference between rolling and adding is that rolling does not
affect larger units. For instance, rolling a $(LREF Date) one
year's worth of days gets the exact same $(LREF Date).
- The only accepted units are $(D "days").
+ The only accepted units are `"days"`.
Params:
- units = The units to add. Must be $(D "days").
+ units = The units to add. Must be `"days"`.
days = The number of days to add to this $(LREF Date).
+
+ Returns:
+ A reference to the `Date` (`this`).
+/
ref Date roll(string units)(long days) @safe pure nothrow @nogc
if (units == "days")
@@ -5966,7 +6127,7 @@ public:
static assert(!__traits(compiles, idate.roll!"days"(12)));
}
-
+ import core.time : Duration;
/++
Gives the result of adding or subtracting a $(REF Duration, core,time)
from
@@ -6006,6 +6167,7 @@ public:
{
auto date = Date(1999, 7, 6);
+ import core.time : dur;
assert(date + dur!"weeks"(7) == Date(1999, 8, 24));
assert(date + dur!"weeks"(-7) == Date(1999, 5, 18));
assert(date + dur!"days"(7) == Date(1999, 7, 13));
@@ -6080,6 +6242,7 @@ public:
@safe unittest
{
+ import core.time : dur;
assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24));
assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18));
assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13));
@@ -6145,9 +6308,10 @@ public:
$(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration))
)
+/
- Duration opBinary(string op)(in Date rhs) const @safe pure nothrow @nogc
+ Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc
if (op == "-")
{
+ import core.time : dur;
return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
}
@@ -6155,6 +6319,7 @@ public:
{
auto date = Date(1999, 7, 6);
+ import core.time : dur;
assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365));
assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365));
assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31));
@@ -6199,7 +6364,7 @@ public:
Params:
rhs = The $(LREF Date) to subtract from this one.
+/
- int diffMonths(in Date rhs) const @safe pure nothrow @nogc
+ int diffMonths(Date rhs) const @safe pure nothrow @nogc
{
immutable yearDiff = _year - rhs._year;
immutable monthDiff = _month - rhs._month;
@@ -6729,13 +6894,19 @@ public:
/++
- The ISO 8601 week of the year that this $(LREF Date) is in.
+ The ISO 8601 week and year of the year that this $(LREF Date) is in.
+
+ Returns:
+ An anonymous struct with the members $(D isoWeekYear) for the
+ resulting year and $(D isoWeek) for the resulting ISO week.
See_Also:
$(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
+/
- @property ubyte isoWeek() const @safe pure nothrow
+ @property auto isoWeekAndYear() const @safe pure nothrow
{
+ struct ISOWeekAndYear { short isoWeekYear; ubyte isoWeek; }
+
immutable weekday = dayOfWeek;
immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
immutable week = (dayOfYear - adjustedWeekday + 10) / 7;
@@ -6750,24 +6921,35 @@ public:
case DayOfWeek.tue:
case DayOfWeek.wed:
case DayOfWeek.thu:
- return 1;
+ return ISOWeekAndYear(cast(short) (_year + 1), 1);
case DayOfWeek.fri:
case DayOfWeek.sat:
case DayOfWeek.sun:
- return 53;
+ return ISOWeekAndYear(_year, 53);
default:
assert(0, "Invalid ISO Week");
}
}
else if (week > 0)
- return cast(ubyte) week;
+ return ISOWeekAndYear(_year, cast(ubyte) week);
else
- return Date(_year - 1, 12, 31).isoWeek;
+ return Date(_year - 1, 12, 31).isoWeekAndYear;
}
catch (Exception e)
assert(0, "Date's constructor threw.");
}
+ /++
+ The ISO 8601 week of the year that this $(LREF Date) is in.
+
+ See_Also:
+ $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
+ +/
+ @property ubyte isoWeek() const @safe pure nothrow
+ {
+ return isoWeekAndYear().isoWeek;
+ }
+
@safe unittest
{
// Test A.D.
@@ -6829,6 +7011,105 @@ public:
static assert(!__traits(compiles, idate.isoWeek = 3));
}
+ /++
+ The year inside the ISO 8601 week calendar that this $(LREF Date) is in.
+
+ May differ from $(LREF year) between 28 December and 4 January.
+
+ See_Also:
+ $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
+ +/
+ @property short isoWeekYear() const @safe pure nothrow
+ {
+ return isoWeekAndYear().isoWeekYear;
+ }
+
+ @safe unittest
+ {
+ // Test A.D.
+ assert(Date(2009, 12, 28).isoWeekYear == 2009);
+ assert(Date(2009, 12, 29).isoWeekYear == 2009);
+ assert(Date(2009, 12, 30).isoWeekYear == 2009);
+ assert(Date(2009, 12, 31).isoWeekYear == 2009);
+ assert(Date(2010, 1, 1).isoWeekYear == 2009);
+ assert(Date(2010, 1, 2).isoWeekYear == 2009);
+ assert(Date(2010, 1, 3).isoWeekYear == 2009);
+ assert(Date(2010, 1, 4).isoWeekYear == 2010);
+ assert(Date(2010, 1, 5).isoWeekYear == 2010);
+ assert(Date(2010, 1, 6).isoWeekYear == 2010);
+ assert(Date(2010, 1, 7).isoWeekYear == 2010);
+ assert(Date(2010, 1, 8).isoWeekYear == 2010);
+ assert(Date(2010, 1, 9).isoWeekYear == 2010);
+ assert(Date(2010, 1, 10).isoWeekYear == 2010);
+ assert(Date(2010, 1, 11).isoWeekYear == 2010);
+ assert(Date(2010, 12, 31).isoWeekYear == 2010);
+
+ assert(Date(2004, 12, 26).isoWeekYear == 2004);
+ assert(Date(2004, 12, 27).isoWeekYear == 2004);
+ assert(Date(2004, 12, 28).isoWeekYear == 2004);
+ assert(Date(2004, 12, 29).isoWeekYear == 2004);
+ assert(Date(2004, 12, 30).isoWeekYear == 2004);
+ assert(Date(2004, 12, 31).isoWeekYear == 2004);
+ assert(Date(2005, 1, 1).isoWeekYear == 2004);
+ assert(Date(2005, 1, 2).isoWeekYear == 2004);
+ assert(Date(2005, 1, 3).isoWeekYear == 2005);
+
+ assert(Date(2005, 12, 31).isoWeekYear == 2005);
+ assert(Date(2007, 1, 1).isoWeekYear == 2007);
+
+ assert(Date(2007, 12, 30).isoWeekYear == 2007);
+ assert(Date(2007, 12, 31).isoWeekYear == 2008);
+ assert(Date(2008, 1, 1).isoWeekYear == 2008);
+
+ assert(Date(2008, 12, 28).isoWeekYear == 2008);
+ assert(Date(2008, 12, 29).isoWeekYear == 2009);
+ assert(Date(2008, 12, 30).isoWeekYear == 2009);
+ assert(Date(2008, 12, 31).isoWeekYear == 2009);
+ assert(Date(2009, 1, 1).isoWeekYear == 2009);
+ assert(Date(2009, 1, 2).isoWeekYear == 2009);
+ assert(Date(2009, 1, 3).isoWeekYear == 2009);
+ assert(Date(2009, 1, 4).isoWeekYear == 2009);
+
+ // Test B.C.
+ assert(Date(0, 12, 31).isoWeekYear == 0);
+ assert(Date(0, 1, 4).isoWeekYear == 0);
+ assert(Date(0, 1, 1).isoWeekYear == -1);
+
+ const cdate = Date(1999, 7, 6);
+ immutable idate = Date(1999, 7, 6);
+ assert(cdate.isoWeekYear == 1999);
+ assert(idate.isoWeekYear == 1999);
+ }
+
+ static Date fromISOWeek(short isoWeekYear, ubyte isoWeek, DayOfWeek weekday) @safe pure nothrow @nogc
+ {
+ immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
+ immutable dayOffset = (isoWeek - 1) * 7 + adjustedWeekday;
+
+ Date date;
+ date._year = isoWeekYear;
+ date._month = Month.jan;
+ date._day = 3;
+ immutable startOfYear = date.dayOfWeek;
+ return date._addDays(dayOffset - startOfYear);
+ }
+
+ @safe unittest
+ {
+ // Test -30000 days to 30000 days for matching construction <-> deconstruction
+ Date date = Date(1, 1, 1);
+ date._addDays(-30_000);
+ foreach (day; 0 .. 60_000)
+ {
+ const year = date.isoWeekYear;
+ const dow = date.dayOfWeek;
+ const isoWeek = date.isoWeek;
+ const reversed = Date.fromISOWeek(year, isoWeek, dow);
+ assert(reversed == date, date.toISOExtString ~ " != " ~ reversed.toISOExtString);
+ date = date._addDays(1);
+ }
+ }
+
/++
$(LREF Date) for the last day in the month that this $(LREF Date) is in.
@@ -7027,27 +7308,25 @@ public:
/++
- Converts this $(LREF Date) to a string with the format YYYYMMDD.
+ Converts this $(LREF Date) to a string with the format `YYYYMMDD`.
+ If `writer` is set, the resulting string will be written directly
+ to it.
+
+ Params:
+ writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
string toISOString() const @safe pure nothrow
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(8);
try
- {
- if (_year >= 0)
- {
- if (_year < 10_000)
- return format("%04d%02d%02d", _year, _month, _day);
- else
- return format("+%05d%02d%02d", _year, _month, _day);
- }
- else if (_year > -10_000)
- return format("%05d%02d%02d", _year, _month, _day);
- else
- return format("%06d%02d%02d", _year, _month, _day);
- }
+ toISOString(w);
catch (Exception e)
- assert(0, "format() threw.");
+ assert(0, "toISOString() threw.");
+ return w.data;
}
///
@@ -7082,28 +7361,56 @@ public:
assert(idate.toISOString() == "19990706");
}
+ /// ditto
+ void toISOString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ import std.format.write : formattedWrite;
+ if (_year >= 0)
+ {
+ if (_year < 10_000)
+ formattedWrite(writer, "%04d%02d%02d", _year, _month, _day);
+ else
+ formattedWrite(writer, "+%05d%02d%02d", _year, _month, _day);
+ }
+ else if (_year > -10_000)
+ formattedWrite(writer, "%05d%02d%02d", _year, _month, _day);
+ else
+ formattedWrite(writer, "%06d%02d%02d", _year, _month, _day);
+ }
+
+ @safe pure unittest
+ {
+ import std.array : appender;
+
+ auto w = appender!(char[])();
+ Date(2010, 7, 4).toISOString(w);
+ assert(w.data == "20100704");
+ w.clear();
+ Date(1998, 12, 25).toISOString(w);
+ assert(w.data == "19981225");
+ }
+
/++
- Converts this $(LREF Date) to a string with the format YYYY-MM-DD.
+ Converts this $(LREF Date) to a string with the format `YYYY-MM-DD`.
+ If `writer` is set, the resulting string will be written directly
+ to it.
+
+ Params:
+ writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
string toISOExtString() const @safe pure nothrow
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(10);
try
- {
- if (_year >= 0)
- {
- if (_year < 10_000)
- return format("%04d-%02d-%02d", _year, _month, _day);
- else
- return format("+%05d-%02d-%02d", _year, _month, _day);
- }
- else if (_year > -10_000)
- return format("%05d-%02d-%02d", _year, _month, _day);
- else
- return format("%06d-%02d-%02d", _year, _month, _day);
- }
+ toISOExtString(w);
catch (Exception e)
- assert(0, "format() threw.");
+ assert(0, "toISOExtString() threw.");
+ return w.data;
}
///
@@ -7138,28 +7445,56 @@ public:
assert(idate.toISOExtString() == "1999-07-06");
}
+ /// ditto
+ void toISOExtString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ import std.format.write : formattedWrite;
+ if (_year >= 0)
+ {
+ if (_year < 10_000)
+ formattedWrite(writer, "%04d-%02d-%02d", _year, _month, _day);
+ else
+ formattedWrite(writer, "+%05d-%02d-%02d", _year, _month, _day);
+ }
+ else if (_year > -10_000)
+ formattedWrite(writer, "%05d-%02d-%02d", _year, _month, _day);
+ else
+ formattedWrite(writer, "%06d-%02d-%02d", _year, _month, _day);
+ }
+
+ @safe pure unittest
+ {
+ import std.array : appender;
+
+ auto w = appender!(char[])();
+ Date(2010, 7, 4).toISOExtString(w);
+ assert(w.data == "2010-07-04");
+ w.clear();
+ Date(-4, 1, 5).toISOExtString(w);
+ assert(w.data == "-0004-01-05");
+ }
+
/++
- Converts this $(LREF Date) to a string with the format YYYY-Mon-DD.
+ Converts this $(LREF Date) to a string with the format `YYYY-Mon-DD`.
+ If `writer` is set, the resulting string will be written directly
+ to it.
+
+ Params:
+ writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
string toSimpleString() const @safe pure nothrow
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(11);
try
- {
- if (_year >= 0)
- {
- if (_year < 10_000)
- return format("%04d-%s-%02d", _year, monthToString(_month), _day);
- else
- return format("+%05d-%s-%02d", _year, monthToString(_month), _day);
- }
- else if (_year > -10_000)
- return format("%05d-%s-%02d", _year, monthToString(_month), _day);
- else
- return format("%06d-%s-%02d", _year, monthToString(_month), _day);
- }
+ toSimpleString(w);
catch (Exception e)
- assert(0, "format() threw.");
+ assert(0, "toSimpleString() threw.");
+ return w.data;
}
///
@@ -7194,6 +7529,35 @@ public:
assert(idate.toSimpleString() == "1999-Jul-06");
}
+ /// ditto
+ void toSimpleString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ import std.format.write : formattedWrite;
+ if (_year >= 0)
+ {
+ if (_year < 10_000)
+ formattedWrite(writer, "%04d-%s-%02d", _year, monthToString(_month), _day);
+ else
+ formattedWrite(writer, "+%05d-%s-%02d", _year, monthToString(_month), _day);
+ }
+ else if (_year > -10_000)
+ formattedWrite(writer, "%05d-%s-%02d", _year, monthToString(_month), _day);
+ else
+ formattedWrite(writer, "%06d-%s-%02d", _year, monthToString(_month), _day);
+ }
+
+ @safe pure unittest
+ {
+ import std.array : appender;
+
+ auto w = appender!(char[])();
+ Date(9, 12, 4).toSimpleString(w);
+ assert(w.data == "0009-Dec-04");
+ w.clear();
+ Date(-10000, 10, 20).toSimpleString(w);
+ assert(w.data == "-10000-Oct-20");
+ }
/++
Converts this $(LREF Date) to a string.
@@ -7233,6 +7597,12 @@ public:
assert(idate.toString());
}
+ /// ditto
+ void toString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ toSimpleString(writer);
+ }
/++
Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace
@@ -7246,7 +7616,7 @@ public:
not in the ISO format or if the resulting $(LREF Date) would not be
valid.
+/
- static Date fromISOString(S)(in S isoString) @safe pure
+ static Date fromISOString(S)(scope const S isoString) @safe pure
if (isSomeString!S)
{
import std.algorithm.searching : startsWith;
@@ -7363,14 +7733,14 @@ public:
assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6));
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21));
}
}
@@ -7389,43 +7759,40 @@ public:
not in the ISO Extended format or if the resulting $(LREF Date)
would not be valid.
+/
- static Date fromISOExtString(S)(in S isoExtString) @safe pure
+ static Date fromISOExtString(S)(scope const S isoExtString) @safe pure
if (isSomeString!(S))
{
- import std.algorithm.searching : all, startsWith;
- import std.ascii : isDigit;
- import std.conv : to;
- import std.exception : enforce;
+ import std.algorithm.searching : startsWith;
+ import std.conv : to, ConvException;
import std.format : format;
import std.string : strip;
- auto dstr = to!dstring(strip(isoExtString));
-
- enforce(dstr.length >= 10, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
+ auto str = strip(isoExtString);
+ short year;
+ ubyte month, day;
- auto day = dstr[$-2 .. $];
- auto month = dstr[$-5 .. $-3];
- auto year = dstr[0 .. $-6];
+ if (str.length < 10 || str[$-3] != '-' || str[$-6] != '-')
+ throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
- enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- enforce(dstr[$-6] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- enforce(all!isDigit(day),
- new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- enforce(all!isDigit(month),
- new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
+ auto yearStr = str[0 .. $-6];
+ auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
+ if ((yearStr.length > 4) != signAtBegining)
+ {
+ throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
+ }
- if (year.length > 4)
+ try
{
- enforce(year.startsWith('-', '+'),
- new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- enforce(all!isDigit(year[1..$]),
- new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
+ day = to!ubyte(str[$-2 .. $]);
+ month = to!ubyte(str[$-5 .. $-3]);
+ year = to!short(yearStr);
+ }
+ catch (ConvException)
+ {
+ throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
}
- else
- enforce(all!isDigit(year),
- new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- return Date(to!short(year), to!ubyte(month), to!ubyte(day));
+ return Date(year, month, day);
}
///
@@ -7504,14 +7871,14 @@ public:
assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6));
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21));
}
}
@@ -7530,40 +7897,40 @@ public:
not in the correct format or if the resulting $(LREF Date) would not
be valid.
+/
- static Date fromSimpleString(S)(in S simpleString) @safe pure
+ static Date fromSimpleString(S)(scope const S simpleString) @safe pure
if (isSomeString!(S))
{
- import std.algorithm.searching : all, startsWith;
- import std.ascii : isDigit;
- import std.conv : to;
- import std.exception : enforce;
+ import std.algorithm.searching : startsWith;
+ import std.conv : to, ConvException;
import std.format : format;
import std.string : strip;
- auto dstr = to!dstring(strip(simpleString));
-
- enforce(dstr.length >= 11, new DateTimeException(format("Invalid string format: %s", simpleString)));
+ auto str = strip(simpleString);
- auto day = dstr[$-2 .. $];
- auto month = monthFromString(to!string(dstr[$-6 .. $-3]));
- auto year = dstr[0 .. $-7];
+ if (str.length < 11 || str[$-3] != '-' || str[$-7] != '-')
+ throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
- enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid string format: %s", simpleString)));
- enforce(dstr[$-7] == '-', new DateTimeException(format("Invalid string format: %s", simpleString)));
- enforce(all!isDigit(day), new DateTimeException(format("Invalid string format: %s", simpleString)));
+ int year;
+ uint day;
+ auto month = monthFromString(str[$ - 6 .. $ - 3]);
+ auto yearStr = str[0 .. $ - 7];
+ auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
+ if ((yearStr.length > 4) != signAtBegining)
+ {
+ throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
+ }
- if (year.length > 4)
+ try
{
- enforce(year.startsWith('-', '+'),
- new DateTimeException(format("Invalid string format: %s", simpleString)));
- enforce(all!isDigit(year[1..$]),
- new DateTimeException(format("Invalid string format: %s", simpleString)));
+ day = to!uint(str[$ - 2 .. $]);
+ year = to!int(yearStr);
+ }
+ catch (ConvException)
+ {
+ throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
}
- else
- enforce(all!isDigit(year),
- new DateTimeException(format("Invalid string format: %s", simpleString)));
- return Date(to!short(year), month, to!ubyte(day));
+ return Date(year, month, day);
}
///
@@ -7642,14 +8009,14 @@ public:
assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6));
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21));
}
}
@@ -7727,7 +8094,7 @@ package:
decrease) to the month would cause it to overflow (or underflow) the
current year.
- $(D _addDays(numDays)) is effectively equivalent to
+ `_addDays(numDays)` is effectively equivalent to
$(D date.dayOfGregorianCal = date.dayOfGregorianCal + days).
Params:
@@ -7912,6 +8279,28 @@ package:
ubyte _day = 1;
}
+///
+@safe pure unittest
+{
+ import core.time : days;
+
+ auto d = Date(2000, 6, 1);
+
+ assert(d.dayOfYear == 153);
+ assert(d.dayOfWeek == DayOfWeek.thu);
+
+ d += 10.days;
+ assert(d == Date(2000, 6, 11));
+
+ assert(d.toISOExtString() == "2000-06-11");
+ assert(d.toISOString() == "20000611");
+ assert(d.toSimpleString() == "2000-Jun-11");
+
+ assert(Date.fromISOExtString("2018-01-01") == Date(2018, 1, 1));
+ assert(Date.fromISOString("20180101") == Date(2018, 1, 1));
+ assert(Date.fromSimpleString("2018-Jan-01") == Date(2018, 1, 1));
+}
+
/++
Represents a time of day with hours, minutes, and seconds. It uses 24 hour
@@ -7983,7 +8372,7 @@ public:
$(TR $(TD this &gt; rhs) $(TD &gt; 0))
)
+/
- int opCmp(in TimeOfDay rhs) const @safe pure nothrow @nogc
+ int opCmp(TimeOfDay rhs) const @safe pure nothrow @nogc
{
if (_hour < rhs._hour)
return -1;
@@ -8194,24 +8583,28 @@ public:
/++
- Adds the given number of units to this $(LREF TimeOfDay). A negative
- number will subtract.
+ Adds the given number of units to this $(LREF TimeOfDay), mutating it. A
+ negative number will subtract.
The difference between rolling and adding is that rolling does not
affect larger units. For instance, rolling a $(LREF TimeOfDay)
one hours's worth of minutes gets the exact same
$(LREF TimeOfDay).
- Accepted units are $(D "hours"), $(D "minutes"), and $(D "seconds").
+ Accepted units are `"hours"`, `"minutes"`, and `"seconds"`.
Params:
units = The units to add.
value = The number of $(D_PARAM units) to add to this
$(LREF TimeOfDay).
+
+ Returns:
+ A reference to the `TimeOfDay` (`this`).
+/
ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
if (units == "hours")
{
+ import core.time : dur;
return this += dur!"hours"(value);
}
@@ -8256,7 +8649,7 @@ public:
}
- // Shares documentation with "hours" version.
+ /// ditto
ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
if (units == "minutes" || units == "seconds")
{
@@ -8281,7 +8674,7 @@ public:
// Test roll!"minutes"().
@safe unittest
{
- static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__)
+ static void testTOD(TimeOfDay orig, int minutes, TimeOfDay expected, size_t line = __LINE__)
{
orig.roll!"minutes"(minutes);
assert(orig == expected);
@@ -8365,7 +8758,7 @@ public:
// Test roll!"seconds"().
@safe unittest
{
- static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__)
+ static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
{
orig.roll!"seconds"(seconds);
assert(orig == expected);
@@ -8436,6 +8829,7 @@ public:
}
+ import core.time : Duration;
/++
Gives the result of adding or subtracting a $(REF Duration, core,time)
from this $(LREF TimeOfDay).
@@ -8480,6 +8874,7 @@ public:
{
auto tod = TimeOfDay(12, 30, 33);
+ import core.time : dur;
assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33));
assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
@@ -8547,6 +8942,7 @@ public:
@safe unittest
{
+ import core.time : dur;
auto duration = dur!"hours"(12);
assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33));
@@ -8603,12 +8999,13 @@ public:
Params:
rhs = The $(LREF TimeOfDay) to subtract from this one.
+/
- Duration opBinary(string op)(in TimeOfDay rhs) const @safe pure nothrow @nogc
+ Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc
if (op == "-")
{
immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second;
+ import core.time : dur;
return dur!"seconds"(lhsSec - rhsSec);
}
@@ -8616,6 +9013,7 @@ public:
{
auto tod = TimeOfDay(12, 30, 33);
+ import core.time : dur;
assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061));
assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061));
assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200));
@@ -8642,15 +9040,32 @@ public:
/++
- Converts this $(LREF TimeOfDay) to a string with the format HHMMSS.
+ Converts this $(LREF TimeOfDay) to a string with the format `HHMMSS`.
+ If `writer` is set, the resulting string will be written directly to it.
+
+ Params:
+ writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
string toISOString() const @safe pure nothrow
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(6);
try
- return format("%02d%02d%02d", _hour, _minute, _second);
+ toISOString(w);
catch (Exception e)
- assert(0, "format() threw.");
+ assert(0, "toISOString() threw.");
+ return w.data;
+ }
+
+ /// ditto
+ void toISOString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ import std.format.write : formattedWrite;
+ formattedWrite(writer, "%02d%02d%02d", _hour, _minute, _second);
}
///
@@ -8672,15 +9087,32 @@ public:
/++
- Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS.
+ Converts this $(LREF TimeOfDay) to a string with the format `HH:MM:SS`.
+ If `writer` is set, the resulting string will be written directly to it.
+
+ Params:
+ writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
string toISOExtString() const @safe pure nothrow
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(8);
try
- return format("%02d:%02d:%02d", _hour, _minute, _second);
+ toISOExtString(w);
catch (Exception e)
- assert(0, "format() threw.");
+ assert(0, "toISOExtString() threw.");
+ return w.data;
+ }
+
+ /// ditto
+ void toISOExtString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ import std.format.write : formattedWrite;
+ formattedWrite(writer, "%02d:%02d:%02d", _hour, _minute, _second);
}
///
@@ -8723,12 +9155,24 @@ public:
`fromISOString` and `fromISOExtString`.
The format returned by toString may or may not change in the future.
+
+ Params:
+ writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
string toString() const @safe pure nothrow
{
return toISOExtString();
}
+ /// ditto
+ void toString(W)(ref W writer) const
+ if (isOutputRange!(W, char))
+ {
+ toISOExtString(writer);
+ }
+
@safe unittest
{
auto tod = TimeOfDay(12, 30, 33);
@@ -8752,7 +9196,7 @@ public:
not in the ISO format or if the resulting $(LREF TimeOfDay) would
not be valid.
+/
- static TimeOfDay fromISOString(S)(in S isoString) @safe pure
+ static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure
if (isSomeString!S)
{
import std.conv : to, text, ConvException;
@@ -8851,14 +9295,14 @@ public:
assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17));
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16));
}
}
@@ -8877,34 +9321,32 @@ public:
not in the ISO Extended format or if the resulting $(LREF TimeOfDay)
would not be valid.
+/
- static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure
+ static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure
if (isSomeString!S)
{
- import std.algorithm.searching : all;
- import std.ascii : isDigit;
- import std.conv : to;
- import std.exception : enforce;
- import std.format : format;
+ import std.conv : ConvException, text, to;
import std.string : strip;
- auto dstr = to!dstring(strip(isoExtString));
-
- enforce(dstr.length == 8, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
+ auto str = strip(isoExtString);
+ int hours, minutes, seconds;
- auto hours = dstr[0 .. 2];
- auto minutes = dstr[3 .. 5];
- auto seconds = dstr[6 .. $];
+ if (str.length != 8 || str[2] != ':' || str[5] != ':')
+ throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString));
- enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- enforce(all!isDigit(hours),
- new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- enforce(all!isDigit(minutes),
- new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- enforce(all!isDigit(seconds),
- new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
+ try
+ {
+ // cast to int from uint is used because it checks for
+ // non digits without extra loops
+ hours = cast(int) to!uint(str[0 .. 2]);
+ minutes = cast(int) to!uint(str[3 .. 5]);
+ seconds = cast(int) to!uint(str[6 .. $]);
+ }
+ catch (ConvException)
+ {
+ throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString));
+ }
- return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds));
+ return TimeOfDay(hours, minutes, seconds);
}
///
@@ -8978,14 +9420,14 @@ public:
assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17));
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16));
}
}
@@ -9045,6 +9487,7 @@ private:
+/
ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc
{
+ import core.time : convert;
long hnsecs = convert!("seconds", "hnsecs")(seconds);
hnsecs += convert!("hours", "hnsecs")(_hour);
hnsecs += convert!("minutes", "hnsecs")(_minute);
@@ -9068,7 +9511,7 @@ private:
@safe unittest
{
- static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__)
+ static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
{
orig._addSeconds(seconds);
assert(orig == expected);
@@ -9170,6 +9613,22 @@ package:
enum ubyte maxSecond = 60 - 1;
}
+///
+@safe pure unittest
+{
+ import core.time : minutes, seconds;
+
+ auto t = TimeOfDay(12, 30, 0);
+
+ t += 10.minutes + 100.seconds;
+ assert(t == TimeOfDay(12, 41, 40));
+
+ assert(t.toISOExtString() == "12:41:40");
+ assert(t.toISOString() == "124140");
+
+ assert(TimeOfDay.fromISOExtString("15:00:00") == TimeOfDay(15, 0, 0));
+ assert(TimeOfDay.fromISOString("015000") == TimeOfDay(1, 50, 0));
+}
/++
Returns whether the given value is valid for the given unit type when in a
@@ -9240,7 +9699,7 @@ if (units == "days")
thrown.
Throws:
- $(LREF DateTimeException) if $(D valid!units(value)) is false.
+ $(LREF DateTimeException) if `valid!units(value)` is false.
+/
void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure
if (units == "months" ||
@@ -9272,8 +9731,26 @@ if (units == "months" ||
}
}
+///
+@safe pure unittest
+{
+ import std.exception : assertThrown, assertNotThrown;
+
+ assertNotThrown(enforceValid!"months"(10));
+ assertNotThrown(enforceValid!"seconds"(40));
+
+ assertThrown!DateTimeException(enforceValid!"months"(0));
+ assertThrown!DateTimeException(enforceValid!"hours"(24));
+ assertThrown!DateTimeException(enforceValid!"minutes"(60));
+ assertThrown!DateTimeException(enforceValid!"seconds"(60));
+}
+
/++
+ Because the validity of the day number depends on both on the year
+ and month of which the day is occurring, take all three variables
+ to validate the day.
+
Params:
units = The units of time to validate.
year = The year of the day to validate.
@@ -9295,6 +9772,20 @@ if (units == "days")
throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line);
}
+///
+@safe pure unittest
+{
+ import std.exception : assertThrown, assertNotThrown;
+
+ assertNotThrown(enforceValid!"days"(2000, Month.jan, 1));
+ // leap year
+ assertNotThrown(enforceValid!"days"(2000, Month.feb, 29));
+
+ assertThrown!DateTimeException(enforceValid!"days"(2001, Month.feb, 29));
+ assertThrown!DateTimeException(enforceValid!"days"(2000, Month.jan, 32));
+ assertThrown!DateTimeException(enforceValid!"days"(2000, Month.apr, 31));
+}
+
/++
Returns the number of days from the current day of the week to the given
@@ -9518,20 +10009,20 @@ bool yearIsLeapYear(int year) @safe pure nothrow @nogc
Whether the given type defines all of the necessary functions for it to
function as a time point.
- 1. $(D T) must define a static property named $(D min) which is the smallest
- value of $(D T) as $(D Unqual!T).
+ 1. `T` must define a static property named `min` which is the smallest
+ value of `T` as `Unqual!T`.
- 2. $(D T) must define a static property named $(D max) which is the largest
- value of $(D T) as $(D Unqual!T).
+ 2. `T` must define a static property named `max` which is the largest
+ value of `T` as `Unqual!T`.
- 3. $(D T) must define an $(D opBinary) for addition and subtraction that
- accepts $(REF Duration, core,time) and returns $(D Unqual!T).
+ 3. `T` must define an `opBinary` for addition and subtraction that
+ accepts $(REF Duration, core,time) and returns `Unqual!T`.
- 4. $(D T) must define an $(D opOpAssign) for addition and subtraction that
+ 4. `T` must define an `opOpAssign` for addition and subtraction that
accepts $(REF Duration, core,time) and returns $(D ref Unqual!T).
- 5. $(D T) must define a $(D opBinary) for subtraction which accepts $(D T)
- and returns returns $(REF Duration, core,time).
+ 5. `T` must define a `opBinary` for subtraction which accepts `T`
+ and returns $(REF Duration, core,time).
+/
template isTimePoint(T)
{
@@ -9598,13 +10089,13 @@ private:
import std.datetime.systime;
import std.meta : AliasSeq;
- foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay))
+ static foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay))
{
static assert(isTimePoint!(const TP), TP.stringof);
static assert(isTimePoint!(immutable TP), TP.stringof);
}
- foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date))
+ static foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date))
static assert(!isTimePoint!T, T.stringof);
}
@@ -9612,7 +10103,7 @@ private:
/++
Whether all of the given strings are valid units of time.
- $(D "nsecs") is not considered a valid unit of time. Nothing in std.datetime
+ `"nsecs"` is not considered a valid unit of time. Nothing in std.datetime
can handle precision greater than hnsecs, and the few functions in core.time
which deal with "nsecs" deal with it explicitly.
+/
@@ -9637,8 +10128,8 @@ bool validTimeUnits(string[] units...) @safe pure nothrow @nogc
/++
- Compares two time unit strings. $(D "years") are the largest units and
- $(D "hnsecs") are the smallest.
+ Compares two time unit strings. `"years"` are the largest units and
+ `"hnsecs"` are the smallest.
Returns:
$(BOOKTABLE,
@@ -9657,12 +10148,11 @@ int cmpTimeUnits(string lhs, string rhs) @safe pure
import std.exception : enforce;
import std.format : format;
- auto tstrings = timeStrings;
- immutable indexOfLHS = countUntil(tstrings, lhs);
- immutable indexOfRHS = countUntil(tstrings, rhs);
+ immutable indexOfLHS = countUntil(timeStrings, lhs);
+ immutable indexOfRHS = countUntil(timeStrings, rhs);
- enforce(indexOfLHS != -1, format("%s is not a valid TimeString", lhs));
- enforce(indexOfRHS != -1, format("%s is not a valid TimeString", rhs));
+ enforce!DateTimeException(indexOfLHS != -1, format("%s is not a valid TimeString", lhs));
+ enforce!DateTimeException(indexOfRHS != -1, format("%s is not a valid TimeString", rhs));
if (indexOfLHS < indexOfRHS)
return -1;
@@ -9675,9 +10165,13 @@ int cmpTimeUnits(string lhs, string rhs) @safe pure
///
@safe pure unittest
{
+ import std.exception : assertThrown;
+
assert(cmpTimeUnits("hours", "hours") == 0);
assert(cmpTimeUnits("hours", "weeks") < 0);
assert(cmpTimeUnits("months", "seconds") > 0);
+
+ assertThrown!DateTimeException(cmpTimeUnits("month", "second"));
}
@safe unittest
@@ -9700,11 +10194,11 @@ int cmpTimeUnits(string lhs, string rhs) @safe pure
/++
- Compares two time unit strings at compile time. $(D "years") are the largest
- units and $(D "hnsecs") are the smallest.
+ Compares two time unit strings at compile time. `"years"` are the largest
+ units and `"hnsecs"` are the smallest.
- This template is used instead of $(D cmpTimeUnits) because exceptions
- can't be thrown at compile time and $(D cmpTimeUnits) must enforce that
+ This template is used instead of `cmpTimeUnits` because exceptions
+ can't be thrown at compile time and `cmpTimeUnits` must enforce that
the strings it's given are valid time unit strings. This template uses a
template constraint instead.
@@ -9721,6 +10215,13 @@ if (validTimeUnits(lhs, rhs))
enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs);
}
+///
+@safe pure unittest
+{
+ static assert(CmpTimeUnits!("years", "weeks") > 0);
+ static assert(CmpTimeUnits!("days", "days") == 0);
+ static assert(CmpTimeUnits!("seconds", "hours") < 0);
+}
// Helper function for CmpTimeUnits.
private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
@@ -9740,26 +10241,16 @@ private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
@safe unittest
{
- import std.format : format;
- import std.meta : AliasSeq;
-
- static string genTest(size_t index)
+ static foreach (i; 0 .. timeStrings.length)
{
- auto currUnits = timeStrings[index];
- auto test = format(`assert(CmpTimeUnits!("%s", "%s") == 0);`, currUnits, currUnits);
-
- foreach (units; timeStrings[index + 1 .. $])
- test ~= format(`assert(CmpTimeUnits!("%s", "%s") == -1);`, currUnits, units);
+ static assert(CmpTimeUnits!(timeStrings[i], timeStrings[i]) == 0);
- foreach (units; timeStrings[0 .. index])
- test ~= format(`assert(CmpTimeUnits!("%s", "%s") == 1);`, currUnits, units);
+ static foreach (next; timeStrings[i + 1 .. $])
+ static assert(CmpTimeUnits!(timeStrings[i], next) == -1);
- return test;
+ static foreach (prev; timeStrings[0 .. i])
+ static assert(CmpTimeUnits!(timeStrings[i], prev) == 1);
}
-
- static assert(timeStrings.length == 10);
- foreach (n; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
- mixin(genTest(n));
}
@@ -9794,7 +10285,7 @@ in
{
assert(valid!"months"(month));
}
-body
+do
{
switch (month)
{
@@ -10021,7 +10512,8 @@ string monthToString(Month month) @safe pure
$(REF DateTimeException,std,datetime,date) if the given month is not a
valid month string.
+/
-Month monthFromString(string monthStr) @safe pure
+Month monthFromString(T)(T monthStr) @safe pure
+if (isSomeString!T)
{
import std.format : format;
switch (monthStr)
@@ -10051,31 +10543,33 @@ Month monthFromString(string monthStr) @safe pure
case "Dec":
return Month.dec;
default:
- throw new DateTimeException(format("Invalid month %s", monthStr));
+ throw new DateTimeException(format!"Invalid month %s"(monthStr));
}
}
@safe unittest
{
- import std.stdio : writeln;
+ import std.conv : to;
import std.traits : EnumMembers;
foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY",
"JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"])
{
- scope(failure) writeln(badStr);
- assertThrown!DateTimeException(monthFromString(badStr));
+ assertThrown!DateTimeException(monthFromString(badStr), badStr);
}
foreach (month; EnumMembers!Month)
{
- scope(failure) writeln(month);
- assert(monthFromString(monthToString(month)) == month);
+ assert(monthFromString(monthToString(month)) == month, month.to!string);
}
}
-version (unittest)
+// NOTE: all the non-simple array literals are wrapped in functions, because
+// otherwise importing causes re-evaluation of the static initializers using
+// CTFE with unittests enabled
+version (StdUnittest)
{
+private @safe:
// All of these helper arrays are sorted in ascending order.
auto testYearsBC = [-1999, -1200, -600, -4, -1, 0];
auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012];
@@ -10093,31 +10587,43 @@ version (unittest)
}
}
- MonthDay[] testMonthDays = [MonthDay(1, 1),
+ MonthDay[] testMonthDays()
+ {
+ static MonthDay[] result = [MonthDay(1, 1),
MonthDay(1, 2),
MonthDay(3, 17),
MonthDay(7, 4),
MonthDay(10, 27),
MonthDay(12, 30),
MonthDay(12, 31)];
+ return result;
+ }
auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31];
- auto testTODs = [TimeOfDay(0, 0, 0),
+ TimeOfDay[] testTODs()
+ {
+ static result = [TimeOfDay(0, 0, 0),
TimeOfDay(0, 0, 1),
TimeOfDay(0, 1, 0),
TimeOfDay(1, 0, 0),
TimeOfDay(13, 13, 13),
TimeOfDay(23, 59, 59)];
+ return result;
+ }
auto testHours = [0, 1, 12, 22, 23];
auto testMinSecs = [0, 1, 30, 58, 59];
// Throwing exceptions is incredibly expensive, so we want to use a smaller
// set of values for tests using assertThrown.
- auto testTODsThrown = [TimeOfDay(0, 0, 0),
+ TimeOfDay[] testTODsThrown()
+ {
+ static result = [TimeOfDay(0, 0, 0),
TimeOfDay(13, 13, 13),
TimeOfDay(23, 59, 59)];
+ return result;
+ }
Date[] testDatesBC;
Date[] testDatesAD;
@@ -10127,7 +10633,9 @@ version (unittest)
// I'd use a Tuple, but I get forward reference errors if I try.
struct GregDay { int day; Date date; }
- auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
+ GregDay[] testGregDaysBC()
+ {
+ static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
GregDay(-735_233, Date(-2012, 1, 1)),
GregDay(-735_202, Date(-2012, 2, 1)),
GregDay(-735_175, Date(-2012, 2, 28)),
@@ -10209,8 +10717,12 @@ version (unittest)
GregDay(-30, Date(0, 12, 1)),
GregDay(-1, Date(0, 12, 30)),
GregDay(0, Date(0, 12, 31))];
+ return result;
+ }
- auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)),
+ GregDay[] testGregDaysAD()
+ {
+ static result = [GregDay(1, Date(1, 1, 1)),
GregDay(2, Date(1, 1, 2)),
GregDay(32, Date(1, 2, 1)),
GregDay(365, Date(1, 12, 31)),
@@ -10278,10 +10790,14 @@ version (unittest)
GregDay(734_562, Date(2012, 2, 29)),
GregDay(734_563, Date(2012, 3, 1)),
GregDay(734_858, Date(2012, 12, 21))];
+ return result;
+ }
// I'd use a Tuple, but I get forward reference errors if I try.
struct DayOfYear { int day; MonthDay md; }
- auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)),
+ DayOfYear[] testDaysOfYear()
+ {
+ static result = [DayOfYear(1, MonthDay(1, 1)),
DayOfYear(2, MonthDay(1, 2)),
DayOfYear(3, MonthDay(1, 3)),
DayOfYear(31, MonthDay(1, 31)),
@@ -10309,8 +10825,12 @@ version (unittest)
DayOfYear(363, MonthDay(12, 29)),
DayOfYear(364, MonthDay(12, 30)),
DayOfYear(365, MonthDay(12, 31))];
+ return result;
+ }
- auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)),
+ DayOfYear[] testDaysOfLeapYear()
+ {
+ static result = [DayOfYear(1, MonthDay(1, 1)),
DayOfYear(2, MonthDay(1, 2)),
DayOfYear(3, MonthDay(1, 3)),
DayOfYear(31, MonthDay(1, 31)),
@@ -10339,8 +10859,10 @@ version (unittest)
DayOfYear(364, MonthDay(12, 29)),
DayOfYear(365, MonthDay(12, 30)),
DayOfYear(366, MonthDay(12, 31))];
+ return result;
+ }
- void initializeTests() @safe
+ void initializeTests()
{
foreach (year; testYearsBC)
{
diff --git a/libphobos/src/std/datetime/interval.d b/libphobos/src/std/datetime/interval.d
index d1eec4533e6..741088a72dc 100644
--- a/libphobos/src/std/datetime/interval.d
+++ b/libphobos/src/std/datetime/interval.d
@@ -1,9 +1,36 @@
// Written in the D programming language
/++
+$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Main types) $(TD
+ $(LREF Interval)
+ $(LREF Direction)
+))
+$(TR $(TD Special intervals) $(TD
+ $(LREF everyDayOfWeek)
+ $(LREF everyMonth)
+ $(LREF everyDuration)
+))
+$(TR $(TD Special intervals) $(TD
+ $(LREF NegInfInterval)
+ $(LREF PosInfInterval)
+))
+$(TR $(TD Underlying ranges) $(TD
+ $(LREF IntervalRange)
+ $(LREF NegInfIntervalRange)
+ $(LREF PosInfIntervalRange)
+))
+$(TR $(TD Flags) $(TD
+ $(LREF PopFirst)
+))
+))
+
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: Jonathan M Davis
- Source: $(PHOBOSSRC std/datetime/_interval.d)
+ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis)
+ Source: $(PHOBOSSRC std/datetime/interval.d)
+/
module std.datetime.interval;
@@ -11,10 +38,11 @@ import core.time : Duration, dur;
import std.datetime.date : AllowDayOverflow, DateTimeException, daysToDayOfWeek,
DayOfWeek, isTimePoint, Month;
import std.exception : enforce;
-import std.traits : isIntegral, Unqual;
+import std.range.primitives : isOutputRange;
+import std.traits : isIntegral;
import std.typecons : Flag;
-version (unittest) import std.exception : assertThrown;
+version (StdUnittest) import std.exception : assertThrown;
/++
@@ -37,36 +65,36 @@ enum Direction
/++
- Used to indicate whether $(D popFront) should be called immediately upon
+ Used to indicate whether `popFront` should be called immediately upon
creating a range. The idea is that for some functions used to generate a
- range for an interval, $(D front) is not necessarily a time point which
+ range for an interval, `front` is not necessarily a time point which
would ever be generated by the range (e.g. if the range were every Sunday
within an interval, but the interval started on a Monday), so there needs
to be a way to deal with that. To get the first time point in the range to
- match what the function generates, then use $(D PopFirst.yes) to indicate
- that the range should have $(D popFront) called on it before the range is
- returned so that $(D front) is a time point which the function would
+ match what the function generates, then use `PopFirst.yes` to indicate
+ that the range should have `popFront` called on it before the range is
+ returned so that `front` is a time point which the function would
generate. To let the first time point not match the generator function,
- use $(D PopFront.no).
+ use `PopFront.no`.
For instance, if the function used to generate a range of time points
generated successive Easters (i.e. you're iterating over all of the Easters
within the interval), the initial date probably isn't an Easter. Using
- $(D PopFirst.yes) would tell the function which returned the range that
- $(D popFront) was to be called so that front would then be an Easter - the
+ `PopFirst.yes` would tell the function which returned the range that
+ `popFront` was to be called so that front would then be an Easter - the
next one generated by the function (which when iterating forward would be
- the Easter following the original $(D front), while when iterating backward,
- it would be the Easter prior to the original $(D front)). If
- $(D PopFirst.no) were used, then $(D front) would remain the original time
+ the Easter following the original `front`, while when iterating backward,
+ it would be the Easter prior to the original `front`). If
+ `PopFirst.no` were used, then `front` would remain the original time
point and it would not necessarily be a time point which would be generated
by the range-generating function (which in many cases is exactly what is
desired - e.g. if iterating over every day starting at the beginning of the
interval).
- If set to $(D PopFirst.no), then popFront is not called before returning
+ If set to `PopFirst.no`, then popFront is not called before returning
the range.
- Otherwise, if set to $(D PopFirst.yes), then popFront is called before
+ Otherwise, if set to `PopFirst.yes`, then popFront is called before
returning the range.
+/
alias PopFirst = Flag!"popFirst";
@@ -75,7 +103,7 @@ alias PopFirst = Flag!"popFirst";
/++
Represents an interval of time.
- An $(D Interval) has a starting point and an end point. The interval of time
+ An `Interval` has a starting point and an end point. The interval of time
is therefore the time starting at the starting point up to, but not
including, the end point. e.g.
@@ -85,7 +113,7 @@ alias PopFirst = Flag!"popFirst";
$(TR $(TD [1982-01-04T08:59:00 - 2010-07-04T12:00:00$(RPAREN)))
)
- A range can be obtained from an $(D Interval), allowing iteration over
+ A range can be obtained from an `Interval`, allowing iteration over
that interval, with the exact time points which are iterated over depending
on the function which generates the range.
+/
@@ -108,8 +136,8 @@ public:
Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1));
--------------------
+/
- this(U)(in TP begin, in U end) pure
- if (is(Unqual!TP == Unqual!U))
+ this(U)(scope const TP begin, scope const U end) pure
+ if (is(immutable TP == immutable U))
{
if (!_valid(begin, end))
throw new DateTimeException("Arguments would result in an invalid Interval.");
@@ -125,7 +153,7 @@ public:
Throws:
$(REF DateTimeException,std,datetime,date) if the resulting
- $(D end) is before $(D begin).
+ `end` is before `begin`.
Example:
--------------------
@@ -133,7 +161,7 @@ public:
Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5)));
--------------------
+/
- this(D)(in TP begin, in D duration) pure
+ this(D)(scope const TP begin, scope const D duration) pure
if (__traits(compiles, begin + duration))
{
_begin = cast(TP) begin;
@@ -186,7 +214,7 @@ public:
The starting point of the interval. It is included in the interval.
Params:
- timePoint = The time point to set $(D begin) to.
+ timePoint = The time point to set `begin` to.
Throws:
$(REF DateTimeException,std,datetime,date) if the resulting
@@ -234,7 +262,7 @@ public:
/++
- Returns the duration between $(D begin) and $(D end).
+ Returns the duration between `begin` and `end`.
Example:
--------------------
@@ -284,7 +312,7 @@ public:
Date(2012, 3, 1)));
--------------------
+/
- bool contains(in TP timePoint) const pure
+ bool contains(scope const TP timePoint) const pure
{
_enforceNotEmpty();
return timePoint >= _begin && timePoint < _end;
@@ -313,7 +341,7 @@ public:
Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1))));
--------------------
+/
- bool contains(in Interval interval) const pure
+ bool contains(scope const Interval interval) const pure
{
_enforceNotEmpty();
interval._enforceNotEmpty();
@@ -343,7 +371,7 @@ public:
PosInfInterval!Date(Date(1999, 5, 4))));
--------------------
+/
- bool contains(in PosInfInterval!TP interval) const pure
+ bool contains(scope const PosInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return false;
@@ -370,7 +398,7 @@ public:
NegInfInterval!Date(Date(1996, 5, 4))));
--------------------
+/
- bool contains(in NegInfInterval!TP interval) const pure
+ bool contains(scope const NegInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return false;
@@ -400,7 +428,7 @@ public:
Date(2012, 3, 1)));
--------------------
+/
- bool isBefore(in TP timePoint) const pure
+ bool isBefore(scope const TP timePoint) const pure
{
_enforceNotEmpty();
return _end <= timePoint;
@@ -430,7 +458,7 @@ public:
Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1))));
--------------------
+/
- bool isBefore(in Interval interval) const pure
+ bool isBefore(scope const Interval interval) const pure
{
_enforceNotEmpty();
interval._enforceNotEmpty();
@@ -458,7 +486,7 @@ public:
PosInfInterval!Date(Date(2013, 3, 7))));
--------------------
+/
- bool isBefore(in PosInfInterval!TP interval) const pure
+ bool isBefore(scope const PosInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return _end <= interval._begin;
@@ -485,7 +513,7 @@ public:
NegInfInterval!Date(Date(1996, 5, 4))));
--------------------
+/
- bool isBefore(in NegInfInterval!TP interval) const pure
+ bool isBefore(scope const NegInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return false;
@@ -515,7 +543,7 @@ public:
Date(2012, 3, 1)));
--------------------
+/
- bool isAfter(in TP timePoint) const pure
+ bool isAfter(scope const TP timePoint) const pure
{
_enforceNotEmpty();
return timePoint < _begin;
@@ -545,7 +573,7 @@ public:
Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2))));
--------------------
+/
- bool isAfter(in Interval interval) const pure
+ bool isAfter(scope const Interval interval) const pure
{
_enforceNotEmpty();
interval._enforceNotEmpty();
@@ -573,7 +601,7 @@ public:
PosInfInterval!Date(Date(1999, 5, 4))));
--------------------
+/
- bool isAfter(in PosInfInterval!TP interval) const pure
+ bool isAfter(scope const PosInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return false;
@@ -597,7 +625,7 @@ public:
NegInfInterval!Date(Date(1996, 1, 2))));
--------------------
+/
- bool isAfter(in NegInfInterval!TP interval) const pure
+ bool isAfter(scope const NegInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return _begin >= interval._end;
@@ -626,7 +654,7 @@ public:
Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2))));
--------------------
+/
- bool intersects(in Interval interval) const pure
+ bool intersects(scope const Interval interval) const pure
{
_enforceNotEmpty();
interval._enforceNotEmpty();
@@ -653,7 +681,7 @@ public:
PosInfInterval!Date(Date(2012, 3, 1))));
--------------------
+/
- bool intersects(in PosInfInterval!TP interval) const pure
+ bool intersects(scope const PosInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return _end > interval._begin;
@@ -679,7 +707,7 @@ public:
NegInfInterval!Date(Date(2000, 1, 2))));
--------------------
+/
- bool intersects(in NegInfInterval!TP interval) const pure
+ bool intersects(scope const NegInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return _begin < interval._end;
@@ -707,7 +735,7 @@ public:
Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17)));
--------------------
+/
- Interval intersection(in Interval interval) const
+ Interval intersection(scope const Interval interval) const
{
import std.format : format;
@@ -742,7 +770,7 @@ public:
Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1)));
--------------------
+/
- Interval intersection(in PosInfInterval!TP interval) const
+ Interval intersection(scope const PosInfInterval!TP interval) const
{
import std.format : format;
@@ -774,7 +802,7 @@ public:
Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1)));
--------------------
+/
- Interval intersection(in NegInfInterval!TP interval) const
+ Interval intersection(scope const NegInfInterval!TP interval) const
{
import std.format : format;
@@ -808,7 +836,7 @@ public:
Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1))));
--------------------
+/
- bool isAdjacent(in Interval interval) const pure
+ bool isAdjacent(scope const Interval interval) const pure
{
_enforceNotEmpty();
interval._enforceNotEmpty();
@@ -836,7 +864,7 @@ public:
PosInfInterval!Date(Date(2012, 3, 1))));
--------------------
+/
- bool isAdjacent(in PosInfInterval!TP interval) const pure
+ bool isAdjacent(scope const PosInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return _end == interval._begin;
@@ -863,7 +891,7 @@ public:
NegInfInterval!Date(Date(2000, 1, 2))));
--------------------
+/
- bool isAdjacent(in NegInfInterval!TP interval) const pure
+ bool isAdjacent(scope const NegInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return _begin == interval._end;
@@ -891,7 +919,7 @@ public:
Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7)));
--------------------
+/
- Interval merge(in Interval interval) const
+ Interval merge(scope const Interval interval) const
{
import std.format : format;
@@ -926,7 +954,7 @@ public:
PosInfInterval!Date(Date(1996, 1 , 2)));
--------------------
+/
- PosInfInterval!TP merge(in PosInfInterval!TP interval) const
+ PosInfInterval!TP merge(scope const PosInfInterval!TP interval) const
{
import std.format : format;
@@ -958,7 +986,7 @@ public:
NegInfInterval!Date(Date(2013, 1 , 12)));
--------------------
+/
- NegInfInterval!TP merge(in NegInfInterval!TP interval) const
+ NegInfInterval!TP merge(scope const NegInfInterval!TP interval) const
{
import std.format : format;
@@ -992,7 +1020,7 @@ public:
Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7)));
--------------------
+/
- Interval span(in Interval interval) const pure
+ Interval span(scope const Interval interval) const pure
{
_enforceNotEmpty();
interval._enforceNotEmpty();
@@ -1027,7 +1055,7 @@ public:
PosInfInterval!Date(Date(1996, 1 , 2)));
--------------------
+/
- PosInfInterval!TP span(in PosInfInterval!TP interval) const pure
+ PosInfInterval!TP span(scope const PosInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin);
@@ -1057,7 +1085,7 @@ public:
NegInfInterval!Date(Date(2013, 1 , 12)));
--------------------
+/
- NegInfInterval!TP span(in NegInfInterval!TP interval) const pure
+ NegInfInterval!TP span(scope const NegInfInterval!TP interval) const pure
{
_enforceNotEmpty();
return NegInfInterval!TP(_end > interval._end ? _end : interval._end);
@@ -1113,14 +1141,14 @@ public:
of years and/or months (a positive number of years and months shifts
the interval forward; a negative number shifts it backward).
It adds the years the given years and months to both begin and end.
- It effectively calls $(D add!"years"()) and then $(D add!"months"())
+ It effectively calls `add!"years"()` and then `add!"months"()`
on begin and end with the given number of years and months.
Params:
years = The number of years to shift the interval by.
months = The number of months to shift the interval by.
allowOverflow = Whether the days should be allowed to overflow
- on $(D begin) and $(D end), causing their month
+ on `begin` and `end`, causing their month
to increment.
Throws:
@@ -1236,15 +1264,15 @@ public:
{
/++
Expands the interval forwards and/or backwards in time. Effectively,
- it subtracts the given number of months/years from $(D begin) and
- adds them to $(D end). Whether it expands forwards and/or backwards
+ it subtracts the given number of months/years from `begin` and
+ adds them to `end`. Whether it expands forwards and/or backwards
in time is determined by $(D_PARAM dir).
Params:
years = The number of years to expand the interval by.
months = The number of months to expand the interval by.
allowOverflow = Whether the days should be allowed to overflow
- on $(D begin) and $(D end), causing their month
+ on `begin` and `end`, causing their month
to increment.
dir = The direction in time to expand the interval.
@@ -1325,29 +1353,29 @@ public:
/++
Returns a range which iterates forward over the interval, starting
- at $(D begin), using $(D_PARAM func) to generate each successive time
+ at `begin`, using $(D_PARAM func) to generate each successive time
point.
- The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is
- used to generate the next $(D front) when $(D popFront) is called. If
- $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called
- before the range is returned (so that $(D front) is a time point which
+ The range's `front` is the interval's `begin`. $(D_PARAM func) is
+ used to generate the next `front` when `popFront` is called. If
+ $(D_PARAM popFirst) is `PopFirst.yes`, then `popFront` is called
+ before the range is returned (so that `front` is a time point which
$(D_PARAM func) would generate).
If $(D_PARAM func) ever generates a time point less than or equal to the
- current $(D front) of the range, then a
+ current `front` of the range, then a
$(REF DateTimeException,std,datetime,date) will be thrown. The range
will be empty and iteration complete when $(D_PARAM func) generates a
- time point equal to or beyond the $(D end) of the interval.
+ time point equal to or beyond the `end` of the interval.
There are helper functions in this module which generate common
- delegates to pass to $(D fwdRange). Their documentation starts with
+ delegates to pass to `fwdRange`. Their documentation starts with
"Range-generating function," making them easily searchable.
Params:
func = The function used to generate the time points of the
range over the interval.
- popFirst = Whether $(D popFront) should be called on the range
+ popFirst = Whether `popFront` should be called on the range
before returning it.
Throws:
@@ -1359,14 +1387,14 @@ public:
would be a function pointer to a pure function, but forcing
$(D_PARAM func) to be pure is far too restrictive to be useful, and
in order to have the ease of use of having functions which generate
- functions to pass to $(D fwdRange), $(D_PARAM func) must be a
+ functions to pass to `fwdRange`, $(D_PARAM func) must be a
delegate.
If $(D_PARAM func) retains state which changes as it is called, then
some algorithms will not work correctly, because the range's
- $(D save) will have failed to have really saved the range's state.
+ `save` will have failed to have really saved the range's state.
To avoid such bugs, don't pass a delegate which is
- not logically pure to $(D fwdRange). If $(D_PARAM func) is given the
+ not logically pure to `fwdRange`. If $(D_PARAM func) is given the
same time point with two different calls, it must return the same
result both times.
@@ -1376,7 +1404,7 @@ public:
Example:
--------------------
auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9));
- auto func = delegate (in Date date) // For iterating over even-numbered days.
+ auto func = delegate (scope const Date date) // For iterating over even-numbered days.
{
if ((date.day & 1) == 0)
return date + dur!"days"(2);
@@ -1404,7 +1432,7 @@ public:
assert(range.empty);
--------------------
+/
- IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const
+ IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const
{
_enforceNotEmpty();
@@ -1419,29 +1447,29 @@ public:
/++
Returns a range which iterates backwards over the interval, starting
- at $(D end), using $(D_PARAM func) to generate each successive time
+ at `end`, using $(D_PARAM func) to generate each successive time
point.
- The range's $(D front) is the interval's $(D end). $(D_PARAM func) is
- used to generate the next $(D front) when $(D popFront) is called. If
- $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called
- before the range is returned (so that $(D front) is a time point which
+ The range's `front` is the interval's `end`. $(D_PARAM func) is
+ used to generate the next `front` when `popFront` is called. If
+ $(D_PARAM popFirst) is `PopFirst.yes`, then `popFront` is called
+ before the range is returned (so that `front` is a time point which
$(D_PARAM func) would generate).
If $(D_PARAM func) ever generates a time point greater than or equal to
- the current $(D front) of the range, then a
+ the current `front` of the range, then a
$(REF DateTimeException,std,datetime,date) will be thrown. The range
will be empty and iteration complete when $(D_PARAM func) generates a
- time point equal to or less than the $(D begin) of the interval.
+ time point equal to or less than the `begin` of the interval.
There are helper functions in this module which generate common
- delegates to pass to $(D bwdRange). Their documentation starts with
+ delegates to pass to `bwdRange`. Their documentation starts with
"Range-generating function," making them easily searchable.
Params:
func = The function used to generate the time points of the
range over the interval.
- popFirst = Whether $(D popFront) should be called on the range
+ popFirst = Whether `popFront` should be called on the range
before returning it.
Throws:
@@ -1453,14 +1481,14 @@ public:
would be a function pointer to a pure function, but forcing
$(D_PARAM func) to be pure is far too restrictive to be useful, and
in order to have the ease of use of having functions which generate
- functions to pass to $(D fwdRange), $(D_PARAM func) must be a
+ functions to pass to `fwdRange`, $(D_PARAM func) must be a
delegate.
If $(D_PARAM func) retains state which changes as it is called, then
some algorithms will not work correctly, because the range's
- $(D save) will have failed to have really saved the range's state.
+ `save` will have failed to have really saved the range's state.
To avoid such bugs, don't pass a delegate which is
- not logically pure to $(D fwdRange). If $(D_PARAM func) is given the
+ not logically pure to `fwdRange`. If $(D_PARAM func) is given the
same time point with two different calls, it must return the same
result both times.
@@ -1470,7 +1498,7 @@ public:
Example:
--------------------
auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9));
- auto func = delegate (in Date date) // For iterating over even-numbered days.
+ auto func = delegate (scope const Date date) // For iterating over even-numbered days.
{
if ((date.day & 1) == 0)
return date - dur!"days"(2);
@@ -1498,7 +1526,7 @@ public:
assert(range.empty);
--------------------
+/
- IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const
+ IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const
{
_enforceNotEmpty();
@@ -1510,47 +1538,38 @@ public:
return range;
}
-
- /+
- Converts this interval to a string.
- +/
- // Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't
- // have versions of toString() with extra modifiers, so we define one version
- // with modifiers and one without.
- string toString()
- {
- return _toStringImpl();
- }
-
-
/++
Converts this interval to a string.
+ Params:
+ w = A `char` accepting
+ $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
- // Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't
- // have versions of toString() with extra modifiers, so we define one version
- // with modifiers and one without.
- string toString() const nothrow
- {
- return _toStringImpl();
- }
-
-
-private:
-
- /+
- Since we have two versions of toString, we have _toStringImpl
- so that they can share implementations.
- +/
- string _toStringImpl() const nothrow
+ string toString() const @safe nothrow
{
- import std.format : format;
+ import std.array : appender;
+ auto app = appender!string();
try
- return format("[%s - %s)", _begin, _end);
+ toString(app);
catch (Exception e)
- assert(0, "format() threw.");
+ assert(0, "toString() threw.");
+ return app.data;
}
+ /// ditto
+ void toString(Writer)(ref Writer w) const
+ if (isOutputRange!(Writer, char))
+ {
+ import std.range.primitives : put;
+ put(w, '[');
+ _begin.toString(w);
+ put(w, " - ");
+ _end.toString(w);
+ put(w, ')');
+ }
+private:
/+
Throws:
$(REF DateTimeException,std,datetime,date) if this interval is
@@ -1570,7 +1589,7 @@ private:
begin = The starting point of the interval.
end = The end point of the interval.
+/
- static bool _valid(in TP begin, in TP end) pure nothrow
+ static bool _valid(scope const TP begin, scope const TP end) pure nothrow @trusted
{
return begin <= end;
}
@@ -2350,7 +2369,7 @@ private:
auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7));
- static void testInterval(in Interval!Date interval1, in Interval!Date interval2)
+ static void testInterval(scope const Interval!Date interval1, scope const Interval!Date interval2)
{
interval1.isAdjacent(interval2);
}
@@ -2459,7 +2478,7 @@ private:
auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7));
- static void testInterval(I)(in Interval!Date interval1, in I interval2)
+ static void testInterval(I)(scope const Interval!Date interval1, scope const I interval2)
{
interval1.merge(interval2);
}
@@ -2604,7 +2623,7 @@ private:
auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7));
- static void testInterval(in Interval!Date interval1, in Interval!Date interval2)
+ static void testInterval(scope const Interval!Date interval1, scope const Interval!Date interval2)
{
interval1.span(interval2);
}
@@ -2739,14 +2758,15 @@ private:
auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7));
- static void testIntervalFail(Interval!Date interval, in Duration duration)
+ static void testIntervalFail(Interval!Date interval, scope const Duration duration)
{
interval.shift(duration);
}
assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1)));
- static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__)
+ static void testInterval(I)(I interval, scope const Duration duration,
+ scope const I expected, size_t line = __LINE__)
{
interval.shift(duration);
assert(interval == expected);
@@ -2832,7 +2852,7 @@ private:
auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7));
- static void testIntervalFail(I)(I interval, in Duration duration)
+ static void testIntervalFail(I)(I interval, scope const Duration duration)
{
interval.expand(duration);
}
@@ -2840,7 +2860,8 @@ private:
assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1)));
assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5)), dur!"days"(-5)));
- static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__)
+ static void testInterval(I)(I interval, scope const Duration duration,
+ scope const I expected, size_t line = __LINE__)
{
interval.expand(duration);
assert(interval == expected);
@@ -3011,7 +3032,7 @@ private:
// Verify Examples.
{
auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9));
- auto func = delegate (in Date date)
+ auto func = delegate (scope const Date date)
{
if ((date.day & 1) == 0)
return date + dur!"days"(2);
@@ -3079,7 +3100,7 @@ private:
// Verify Examples.
{
auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9));
- auto func = delegate (in Date date)
+ auto func = delegate (scope const Date date)
{
if ((date.day & 1) == 0)
return date - dur!"days"(2);
@@ -3129,8 +3150,8 @@ private:
/++
Represents an interval of time which has positive infinity as its end point.
- Any ranges which iterate over a $(D PosInfInterval) are infinite. So, the
- main purpose of using $(D PosInfInterval) is to create an infinite range
+ Any ranges which iterate over a `PosInfInterval` are infinite. So, the
+ main purpose of using `PosInfInterval` is to create an infinite range
which starts at a fixed point in time and goes to positive infinity.
+/
struct PosInfInterval(TP)
@@ -3146,7 +3167,7 @@ public:
auto interval = PosInfInterval!Date(Date(1996, 1, 2));
--------------------
+/
- this(in TP begin) pure nothrow
+ this(scope const TP begin) pure nothrow
{
_begin = cast(TP) begin;
}
@@ -3154,7 +3175,7 @@ auto interval = PosInfInterval!Date(Date(1996, 1, 2));
/++
Params:
- rhs = The $(D PosInfInterval) to assign to this one.
+ rhs = The `PosInfInterval` to assign to this one.
+/
ref PosInfInterval opAssign(const ref PosInfInterval rhs) pure nothrow
{
@@ -3165,7 +3186,7 @@ auto interval = PosInfInterval!Date(Date(1996, 1, 2));
/++
Params:
- rhs = The $(D PosInfInterval) to assign to this one.
+ rhs = The `PosInfInterval` to assign to this one.
+/
ref PosInfInterval opAssign(PosInfInterval rhs) pure nothrow
{
@@ -3192,7 +3213,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2));
The starting point of the interval. It is included in the interval.
Params:
- timePoint = The time point to set $(D begin) to.
+ timePoint = The time point to set `begin` to.
+/
@property void begin(TP timePoint) pure nothrow
{
@@ -3251,7 +3272,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(
Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1))));
--------------------
+/
- bool contains(in Interval!TP interval) const pure
+ bool contains(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return interval._begin >= _begin;
@@ -3273,7 +3294,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(
PosInfInterval!Date(Date(1995, 7, 2))));
--------------------
+/
- bool contains(in PosInfInterval interval) const pure nothrow
+ bool contains(scope const PosInfInterval interval) const pure nothrow
{
return interval._begin >= _begin;
}
@@ -3294,7 +3315,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(
NegInfInterval!Date(Date(1996, 5, 4))));
--------------------
+/
- bool contains(in NegInfInterval!TP interval) const pure nothrow
+ bool contains(scope const NegInfInterval!TP interval) const pure nothrow
{
return false;
}
@@ -3316,7 +3337,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24)));
assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5)));
--------------------
+/
- bool isBefore(in TP timePoint) const pure nothrow
+ bool isBefore(scope const TP timePoint) const pure nothrow
{
return false;
}
@@ -3346,7 +3367,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(
Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))));
--------------------
+/
- bool isBefore(in Interval!TP interval) const pure
+ bool isBefore(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return false;
@@ -3372,7 +3393,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(
PosInfInterval!Date(Date(2013, 3, 7))));
--------------------
+/
- bool isBefore(in PosInfInterval interval) const pure nothrow
+ bool isBefore(scope const PosInfInterval interval) const pure nothrow
{
return false;
}
@@ -3394,7 +3415,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(
NegInfInterval!Date(Date(1996, 5, 4))));
--------------------
+/
- bool isBefore(in NegInfInterval!TP interval) const pure nothrow
+ bool isBefore(scope const NegInfInterval!TP interval) const pure nothrow
{
return false;
}
@@ -3413,7 +3434,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24)));
assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5)));
--------------------
+/
- bool isAfter(in TP timePoint) const pure nothrow
+ bool isAfter(scope const TP timePoint) const pure nothrow
{
return timePoint < _begin;
}
@@ -3442,7 +3463,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(
Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2))));
--------------------
+/
- bool isAfter(in Interval!TP interval) const pure
+ bool isAfter(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return _begin >= interval._end;
@@ -3468,7 +3489,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(
PosInfInterval!Date(Date(1999, 5, 4))));
--------------------
+/
- bool isAfter(in PosInfInterval interval) const pure nothrow
+ bool isAfter(scope const PosInfInterval interval) const pure nothrow
{
return false;
}
@@ -3490,7 +3511,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(
NegInfInterval!Date(Date(2000, 7, 1))));
--------------------
+/
- bool isAfter(in NegInfInterval!TP interval) const pure nothrow
+ bool isAfter(scope const NegInfInterval!TP interval) const pure nothrow
{
return _begin >= interval._end;
}
@@ -3518,7 +3539,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(
Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2))));
--------------------
+/
- bool intersects(in Interval!TP interval) const pure
+ bool intersects(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return interval._end > _begin;
@@ -3544,7 +3565,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(
PosInfInterval!Date(Date(1999, 5, 4))));
--------------------
+/
- bool intersects(in PosInfInterval interval) const pure nothrow
+ bool intersects(scope const PosInfInterval interval) const pure nothrow
{
return true;
}
@@ -3566,7 +3587,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(
NegInfInterval!Date(Date(2000, 7, 1))));
--------------------
+/
- bool intersects(in NegInfInterval!TP interval) const pure nothrow
+ bool intersects(scope const NegInfInterval!TP interval) const pure nothrow
{
return _begin < interval._end;
}
@@ -3593,7 +3614,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(
Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17)));
--------------------
+/
- Interval!TP intersection(in Interval!TP interval) const
+ Interval!TP intersection(scope const Interval!TP interval) const
{
import std.format : format;
@@ -3623,7 +3644,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(
PosInfInterval!Date(Date(1999, 1 , 12)));
--------------------
+/
- PosInfInterval intersection(in PosInfInterval interval) const pure nothrow
+ PosInfInterval intersection(scope const PosInfInterval interval) const pure nothrow
{
return PosInfInterval(_begin < interval._begin ? interval._begin : _begin);
}
@@ -3650,7 +3671,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(
Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12)));
--------------------
+/
- Interval!TP intersection(in NegInfInterval!TP interval) const
+ Interval!TP intersection(scope const NegInfInterval!TP interval) const
{
import std.format : format;
@@ -3681,7 +3702,7 @@ assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(
Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))));
--------------------
+/
- bool isAdjacent(in Interval!TP interval) const pure
+ bool isAdjacent(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return _begin == interval._end;
@@ -3707,7 +3728,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(
PosInfInterval!Date(Date(1996, 1, 2))));
--------------------
+/
- bool isAdjacent(in PosInfInterval interval) const pure nothrow
+ bool isAdjacent(scope const PosInfInterval interval) const pure nothrow
{
return false;
}
@@ -3729,7 +3750,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(
NegInfInterval!Date(Date(2000, 7, 1))));
--------------------
+/
- bool isAdjacent(in NegInfInterval!TP interval) const pure nothrow
+ bool isAdjacent(scope const NegInfInterval!TP interval) const pure nothrow
{
return _begin == interval._end;
}
@@ -3747,8 +3768,8 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(
empty.
Note:
- There is no overload for $(D merge) which takes a
- $(D NegInfInterval), because an interval
+ There is no overload for `merge` which takes a
+ `NegInfInterval`, because an interval
going from negative infinity to positive infinity
is not possible.
@@ -3763,7 +3784,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(
PosInfInterval!Date(Date(1996, 1 , 2)));
--------------------
+/
- PosInfInterval merge(in Interval!TP interval) const
+ PosInfInterval merge(scope const Interval!TP interval) const
{
import std.format : format;
@@ -3781,8 +3802,8 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(
interval = The interval to merge with this interval.
Note:
- There is no overload for $(D merge) which takes a
- $(D NegInfInterval), because an interval
+ There is no overload for `merge` which takes a
+ `NegInfInterval`, because an interval
going from negative infinity to positive infinity
is not possible.
@@ -3797,7 +3818,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(
PosInfInterval!Date(Date(1996, 1 , 2)));
--------------------
+/
- PosInfInterval merge(in PosInfInterval interval) const pure nothrow
+ PosInfInterval merge(scope const PosInfInterval interval) const pure nothrow
{
return PosInfInterval(_begin < interval._begin ? _begin : interval._begin);
}
@@ -3817,8 +3838,8 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(
is empty.
Note:
- There is no overload for $(D span) which takes a
- $(D NegInfInterval), because an interval
+ There is no overload for `span` which takes a
+ `NegInfInterval`, because an interval
going from negative infinity to positive infinity
is not possible.
@@ -3837,7 +3858,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).span(
PosInfInterval!Date(Date(1996, 1 , 2)));
--------------------
+/
- PosInfInterval span(in Interval!TP interval) const pure
+ PosInfInterval span(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return PosInfInterval(_begin < interval._begin ? _begin : interval._begin);
@@ -3854,8 +3875,8 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).span(
interval.
Note:
- There is no overload for $(D span) which takes a
- $(D NegInfInterval), because an interval
+ There is no overload for `span` which takes a
+ `NegInfInterval`, because an interval
going from negative infinity to positive infinity
is not possible.
@@ -3870,14 +3891,14 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).span(
PosInfInterval!Date(Date(1996, 1 , 2)));
--------------------
+/
- PosInfInterval span(in PosInfInterval interval) const pure nothrow
+ PosInfInterval span(scope const PosInfInterval interval) const pure nothrow
{
return PosInfInterval(_begin < interval._begin ? _begin : interval._begin);
}
/++
- Shifts the $(D begin) of this interval forward or backwards in time by
+ Shifts the `begin` of this interval forward or backwards in time by
the given duration (a positive duration shifts the interval forward; a
negative duration shifts it backward). Effectively, it does
$(D begin += duration).
@@ -3908,19 +3929,19 @@ assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13)));
__traits(compiles, begin.add!"years"(1)))
{
/++
- Shifts the $(D begin) of this interval forward or backwards in time
+ Shifts the `begin` of this interval forward or backwards in time
by the given number of years and/or months (a positive number of
years and months shifts the interval forward; a negative number
shifts it backward). It adds the years the given years and months to
- $(D begin). It effectively calls $(D add!"years"()) and then
- $(D add!"months"()) on $(D begin) with the given number of years and
+ `begin`. It effectively calls `add!"years"()` and then
+ `add!"months"()` on `begin` with the given number of years and
months.
Params:
years = The number of years to shift the interval by.
months = The number of months to shift the interval by.
allowOverflow = Whether the days should be allowed to overflow
- on $(D begin), causing its month to increment.
+ on `begin`, causing its month to increment.
Throws:
$(REF DateTimeException,std,datetime,date) if this interval is
@@ -3982,13 +4003,13 @@ assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4)));
{
/++
Expands the interval forwards and/or backwards in time. Effectively,
- it subtracts the given number of months/years from $(D begin).
+ it subtracts the given number of months/years from `begin`.
Params:
years = The number of years to expand the interval by.
months = The number of months to expand the interval by.
allowOverflow = Whether the days should be allowed to overflow
- on $(D begin), causing its month to increment.
+ on `begin`, causing its month to increment.
Throws:
$(REF DateTimeException,std,datetime,date) if this interval is
@@ -4021,27 +4042,27 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2)));
/++
Returns a range which iterates forward over the interval, starting
- at $(D begin), using $(D_PARAM func) to generate each successive time
+ at `begin`, using $(D_PARAM func) to generate each successive time
point.
- The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is
- used to generate the next $(D front) when $(D popFront) is called. If
- $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called
- before the range is returned (so that $(D front) is a time point which
+ The range's `front` is the interval's `begin`. $(D_PARAM func) is
+ used to generate the next `front` when `popFront` is called. If
+ $(D_PARAM popFirst) is `PopFirst.yes`, then `popFront` is called
+ before the range is returned (so that `front` is a time point which
$(D_PARAM func) would generate).
If $(D_PARAM func) ever generates a time point less than or equal to the
- current $(D front) of the range, then a
+ current `front` of the range, then a
$(REF DateTimeException,std,datetime,date) will be thrown.
There are helper functions in this module which generate common
- delegates to pass to $(D fwdRange). Their documentation starts with
+ delegates to pass to `fwdRange`. Their documentation starts with
"Range-generating function," to make them easily searchable.
Params:
func = The function used to generate the time points of the
range over the interval.
- popFirst = Whether $(D popFront) should be called on the range
+ popFirst = Whether `popFront` should be called on the range
before returning it.
Throws:
@@ -4053,14 +4074,14 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2)));
would be a function pointer to a pure function, but forcing
$(D_PARAM func) to be pure is far too restrictive to be useful, and
in order to have the ease of use of having functions which generate
- functions to pass to $(D fwdRange), $(D_PARAM func) must be a
+ functions to pass to `fwdRange`, $(D_PARAM func) must be a
delegate.
If $(D_PARAM func) retains state which changes as it is called, then
some algorithms will not work correctly, because the range's
- $(D save) will have failed to have really saved the range's state.
+ `save` will have failed to have really saved the range's state.
To avoid such bugs, don't pass a delegate which is
- not logically pure to $(D fwdRange). If $(D_PARAM func) is given the
+ not logically pure to `fwdRange`. If $(D_PARAM func) is given the
same time point with two different calls, it must return the same
result both times.
@@ -4070,7 +4091,7 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2)));
Example:
--------------------
auto interval = PosInfInterval!Date(Date(2010, 9, 1));
-auto func = delegate (in Date date) //For iterating over even-numbered days.
+auto func = delegate (scope const Date date) //For iterating over even-numbered days.
{
if ((date.day & 1) == 0)
return date + dur!"days"(2);
@@ -4098,7 +4119,7 @@ range.popFront();
assert(!range.empty);
--------------------
+/
- PosInfIntervalRange!(TP) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const
+ PosInfIntervalRange!(TP) fwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const
{
auto range = PosInfIntervalRange!(TP)(this, func);
@@ -4112,9 +4133,9 @@ assert(!range.empty);
/+
Converts this interval to a string.
+/
- //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't
- //have versions of toString() with extra modifiers, so we define one version
- //with modifiers and one without.
+ // Due to bug https://issues.dlang.org/show_bug.cgi?id=3715 , we can't
+ // have versions of toString() with extra modifiers,
+ // so we define one version with modifiers and one without.
string toString()
{
return _toStringImpl();
@@ -4124,9 +4145,9 @@ assert(!range.empty);
/++
Converts this interval to a string.
+/
- //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't
- //have versions of toString() with extra modifiers, so we define one version
- //with modifiers and one without.
+ // Due to bug https://issues.dlang.org/show_bug.cgi?id=3715 , we can't
+ // have versions of toString() with extra modifiers,
+ // so we define one version with modifiers and one without.
string toString() const nothrow
{
return _toStringImpl();
@@ -4240,7 +4261,7 @@ private:
auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4));
- static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval)
+ static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval)
{
posInfInterval.contains(interval);
}
@@ -4365,7 +4386,7 @@ private:
auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4));
- static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval)
+ static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval)
{
posInfInterval.isBefore(interval);
}
@@ -4489,7 +4510,7 @@ private:
auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4));
- static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval)
+ static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval)
{
posInfInterval.isAfter(interval);
}
@@ -4586,7 +4607,7 @@ private:
auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4));
- static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval)
+ static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval)
{
posInfInterval.intersects(interval);
}
@@ -4683,7 +4704,7 @@ private:
auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4));
- static void testInterval(I, J)(in I interval1, in J interval2)
+ static void testInterval(I, J)(scope const I interval1, scope const J interval2)
{
interval1.intersection(interval2);
}
@@ -4801,7 +4822,7 @@ private:
auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4));
- static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval)
+ static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval)
{
posInfInterval.isAdjacent(interval);
}
@@ -4897,7 +4918,7 @@ private:
auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4));
- static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval)
+ static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval)
{
posInfInterval.merge(interval);
}
@@ -5006,7 +5027,7 @@ private:
auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4));
- static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval)
+ static void testInterval(scope const PosInfInterval!Date posInfInterval, scope const Interval!Date interval)
{
posInfInterval.span(interval);
}
@@ -5117,7 +5138,8 @@ private:
auto interval = PosInfInterval!Date(Date(2010, 7, 4));
- static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__)
+ static void testInterval(I)(I interval, scope const Duration duration,
+ scope const I expected, size_t line = __LINE__)
{
interval.shift(duration);
assert(interval == expected);
@@ -5196,7 +5218,8 @@ private:
auto interval = PosInfInterval!Date(Date(2000, 7, 4));
- static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__)
+ static void testInterval(I)(I interval, scope const Duration duration,
+ scope const I expected, size_t line = __LINE__)
{
interval.expand(duration);
assert(interval == expected);
@@ -5290,7 +5313,7 @@ private:
//Verify Examples.
auto interval = PosInfInterval!Date(Date(2010, 9, 1));
- auto func = delegate (in Date date)
+ auto func = delegate (scope const Date date)
{
if ((date.day & 1) == 0)
return date + dur!"days"(2);
@@ -5338,8 +5361,8 @@ private:
Represents an interval of time which has negative infinity as its starting
point.
- Any ranges which iterate over a $(D NegInfInterval) are infinite. So, the
- main purpose of using $(D NegInfInterval) is to create an infinite range
+ Any ranges which iterate over a `NegInfInterval` are infinite. So, the
+ main purpose of using `NegInfInterval` is to create an infinite range
which starts at negative infinity and goes to a fixed end point.
Iterate over it in reverse.
+/
@@ -5356,7 +5379,7 @@ public:
auto interval = PosInfInterval!Date(Date(1996, 1, 2));
--------------------
+/
- this(in TP end) pure nothrow
+ this(scope const TP end) pure nothrow
{
_end = cast(TP) end;
}
@@ -5364,7 +5387,7 @@ auto interval = PosInfInterval!Date(Date(1996, 1, 2));
/++
Params:
- rhs = The $(D NegInfInterval) to assign to this one.
+ rhs = The `NegInfInterval` to assign to this one.
+/
ref NegInfInterval opAssign(const ref NegInfInterval rhs) pure nothrow
{
@@ -5375,7 +5398,7 @@ auto interval = PosInfInterval!Date(Date(1996, 1, 2));
/++
Params:
- rhs = The $(D NegInfInterval) to assign to this one.
+ rhs = The `NegInfInterval` to assign to this one.
+/
ref NegInfInterval opAssign(NegInfInterval rhs) pure nothrow
{
@@ -5462,7 +5485,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(
Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1))));
--------------------
+/
- bool contains(in Interval!TP interval) const pure
+ bool contains(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return interval._end <= _end;
@@ -5484,7 +5507,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(
PosInfInterval!Date(Date(1999, 5, 4))));
--------------------
+/
- bool contains(in PosInfInterval!TP interval) const pure nothrow
+ bool contains(scope const PosInfInterval!TP interval) const pure nothrow
{
return false;
}
@@ -5505,7 +5528,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(
NegInfInterval!Date(Date(2013, 7, 9))));
--------------------
+/
- bool contains(in NegInfInterval interval) const pure nothrow
+ bool contains(scope const NegInfInterval interval) const pure nothrow
{
return interval._end <= _end;
}
@@ -5525,7 +5548,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5)));
assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1)));
--------------------
+/
- bool isBefore(in TP timePoint) const pure nothrow
+ bool isBefore(scope const TP timePoint) const pure nothrow
{
return timePoint >= _end;
}
@@ -5554,7 +5577,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(
Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3))));
--------------------
+/
- bool isBefore(in Interval!TP interval) const pure
+ bool isBefore(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return _end <= interval._begin;
@@ -5577,7 +5600,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(
PosInfInterval!Date(Date(2012, 3, 1))));
--------------------
+/
- bool isBefore(in PosInfInterval!TP interval) const pure nothrow
+ bool isBefore(scope const PosInfInterval!TP interval) const pure nothrow
{
return _end <= interval._begin;
}
@@ -5603,7 +5626,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(
NegInfInterval!Date(Date(2013, 7, 9))));
--------------------
+/
- bool isBefore(in NegInfInterval interval) const pure nothrow
+ bool isBefore(scope const NegInfInterval interval) const pure nothrow
{
return false;
}
@@ -5626,7 +5649,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5)));
assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1)));
--------------------
+/
- bool isAfter(in TP timePoint) const pure nothrow
+ bool isAfter(scope const TP timePoint) const pure nothrow
{
return false;
}
@@ -5659,7 +5682,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(
Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3))));
--------------------
+/
- bool isAfter(in Interval!TP interval) const pure
+ bool isAfter(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return false;
@@ -5685,7 +5708,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(
PosInfInterval!Date(Date(2012, 3, 1))));
--------------------
+/
- bool isAfter(in PosInfInterval!TP interval) const pure nothrow
+ bool isAfter(scope const PosInfInterval!TP interval) const pure nothrow
{
return false;
}
@@ -5710,7 +5733,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(
NegInfInterval!Date(Date(2013, 7, 9))));
--------------------
+/
- bool isAfter(in NegInfInterval interval) const pure nothrow
+ bool isAfter(scope const NegInfInterval interval) const pure nothrow
{
return false;
}
@@ -5738,7 +5761,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(
Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3))));
--------------------
+/
- bool intersects(in Interval!TP interval) const pure
+ bool intersects(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return interval._begin < _end;
@@ -5761,7 +5784,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(
PosInfInterval!Date(Date(2012, 3, 1))));
--------------------
+/
- bool intersects(in PosInfInterval!TP interval) const pure nothrow
+ bool intersects(scope const PosInfInterval!TP interval) const pure nothrow
{
return interval._begin < _end;
}
@@ -5785,7 +5808,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(
NegInfInterval!Date(Date(2013, 7, 9))));
--------------------
+/
- bool intersects(in NegInfInterval!TP interval) const pure nothrow
+ bool intersects(scope const NegInfInterval!TP interval) const pure nothrow
{
return true;
}
@@ -5812,7 +5835,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(
Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1)));
--------------------
+/
- Interval!TP intersection(in Interval!TP interval) const
+ Interval!TP intersection(scope const Interval!TP interval) const
{
import std.format : format;
@@ -5846,7 +5869,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(
Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1)));
--------------------
+/
- Interval!TP intersection(in PosInfInterval!TP interval) const
+ Interval!TP intersection(scope const PosInfInterval!TP interval) const
{
import std.format : format;
@@ -5874,7 +5897,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(
NegInfInterval!Date(Date(2012, 3 , 1)));
--------------------
+/
- NegInfInterval intersection(in NegInfInterval interval) const nothrow
+ NegInfInterval intersection(scope const NegInfInterval interval) const nothrow
{
return NegInfInterval(_end < interval._end ? _end : interval._end);
}
@@ -5906,7 +5929,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(
Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3))));
--------------------
+/
- bool isAdjacent(in Interval!TP interval) const pure
+ bool isAdjacent(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return interval._begin == _end;
@@ -5929,7 +5952,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(
PosInfInterval!Date(Date(2012, 3, 1))));
--------------------
+/
- bool isAdjacent(in PosInfInterval!TP interval) const pure nothrow
+ bool isAdjacent(scope const PosInfInterval!TP interval) const pure nothrow
{
return interval._begin == _end;
}
@@ -5954,7 +5977,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(
NegInfInterval!Date(Date(2012, 3, 1))));
--------------------
+/
- bool isAdjacent(in NegInfInterval interval) const pure nothrow
+ bool isAdjacent(scope const NegInfInterval interval) const pure nothrow
{
return false;
}
@@ -5971,8 +5994,8 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(
not intersect and are not adjacent or if the given interval is empty.
Note:
- There is no overload for $(D merge) which takes a
- $(D PosInfInterval), because an interval
+ There is no overload for `merge` which takes a
+ `PosInfInterval`, because an interval
going from negative infinity to positive infinity
is not possible.
@@ -5987,7 +6010,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(
NegInfInterval!Date(Date(2015, 9 , 2)));
--------------------
+/
- NegInfInterval merge(in Interval!TP interval) const
+ NegInfInterval merge(scope const Interval!TP interval) const
{
import std.format : format;
@@ -6005,8 +6028,8 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(
interval = The interval to merge with this interval.
Note:
- There is no overload for $(D merge) which takes a
- $(D PosInfInterval), because an interval
+ There is no overload for `merge` which takes a
+ `PosInfInterval`, because an interval
going from negative infinity to positive infinity
is not possible.
@@ -6021,7 +6044,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(
NegInfInterval!Date(Date(2013, 1 , 12)));
--------------------
+/
- NegInfInterval merge(in NegInfInterval interval) const pure nothrow
+ NegInfInterval merge(scope const NegInfInterval interval) const pure nothrow
{
return NegInfInterval(_end > interval._end ? _end : interval._end);
}
@@ -6041,8 +6064,8 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(
is empty.
Note:
- There is no overload for $(D span) which takes a
- $(D PosInfInterval), because an interval
+ There is no overload for `span` which takes a
+ `PosInfInterval`, because an interval
going from negative infinity to positive infinity
is not possible.
@@ -6061,7 +6084,7 @@ assert(NegInfInterval!Date(Date(1600, 1, 7)).span(
NegInfInterval!Date(Date(2017, 7 , 1)));
--------------------
+/
- NegInfInterval span(in Interval!TP interval) const pure
+ NegInfInterval span(scope const Interval!TP interval) const pure
{
interval._enforceNotEmpty();
return NegInfInterval(_end > interval._end ? _end : interval._end);
@@ -6078,8 +6101,8 @@ assert(NegInfInterval!Date(Date(1600, 1, 7)).span(
interval.
Note:
- There is no overload for $(D span) which takes a
- $(D PosInfInterval), because an interval
+ There is no overload for `span` which takes a
+ `PosInfInterval`, because an interval
going from negative infinity to positive infinity
is not possible.
@@ -6094,14 +6117,14 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).span(
NegInfInterval!Date(Date(2013, 1 , 12)));
--------------------
+/
- NegInfInterval span(in NegInfInterval interval) const pure nothrow
+ NegInfInterval span(scope const NegInfInterval interval) const pure nothrow
{
return NegInfInterval(_end > interval._end ? _end : interval._end);
}
/++
- Shifts the $(D end) of this interval forward or backwards in time by the
+ Shifts the `end` of this interval forward or backwards in time by the
given duration (a positive duration shifts the interval forward; a
negative duration shifts it backward). Effectively, it does
$(D end += duration).
@@ -6132,18 +6155,18 @@ assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15)));
__traits(compiles, end.add!"years"(1)))
{
/++
- Shifts the $(D end) of this interval forward or backwards in time by
+ Shifts the `end` of this interval forward or backwards in time by
the given number of years and/or months (a positive number of years
and months shifts the interval forward; a negative number shifts it
backward). It adds the years the given years and months to end. It
- effectively calls $(D add!"years"()) and then $(D add!"months"())
+ effectively calls `add!"years"()` and then `add!"months"()`
on end with the given number of years and months.
Params:
years = The number of years to shift the interval by.
months = The number of months to shift the interval by.
allowOverflow = Whether the days should be allowed to overflow
- on $(D end), causing its month to increment.
+ on `end`, causing its month to increment.
Throws:
$(REF DateTimeException,std,datetime,date) if empty is true or
@@ -6211,7 +6234,7 @@ assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28)));
years = The number of years to expand the interval by.
months = The number of months to expand the interval by.
allowOverflow = Whether the days should be allowed to overflow
- on $(D end), causing their month to increment.
+ on `end`, causing their month to increment.
Throws:
$(REF DateTimeException,std,datetime,date) if empty is true or
@@ -6244,27 +6267,27 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1)));
/++
Returns a range which iterates backwards over the interval, starting
- at $(D end), using $(D_PARAM func) to generate each successive time
+ at `end`, using $(D_PARAM func) to generate each successive time
point.
- The range's $(D front) is the interval's $(D end). $(D_PARAM func) is
- used to generate the next $(D front) when $(D popFront) is called. If
- $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called
- before the range is returned (so that $(D front) is a time point which
+ The range's `front` is the interval's `end`. $(D_PARAM func) is
+ used to generate the next `front` when `popFront` is called. If
+ $(D_PARAM popFirst) is `PopFirst.yes`, then `popFront` is called
+ before the range is returned (so that `front` is a time point which
$(D_PARAM func) would generate).
If $(D_PARAM func) ever generates a time point greater than or equal to
- the current $(D front) of the range, then a
+ the current `front` of the range, then a
$(REF DateTimeException,std,datetime,date) will be thrown.
There are helper functions in this module which generate common
- delegates to pass to $(D bwdRange). Their documentation starts with
+ delegates to pass to `bwdRange`. Their documentation starts with
"Range-generating function," to make them easily searchable.
Params:
func = The function used to generate the time points of the
range over the interval.
- popFirst = Whether $(D popFront) should be called on the range
+ popFirst = Whether `popFront` should be called on the range
before returning it.
Throws:
@@ -6276,14 +6299,14 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1)));
would be a function pointer to a pure function, but forcing
$(D_PARAM func) to be pure is far too restrictive to be useful, and
in order to have the ease of use of having functions which generate
- functions to pass to $(D fwdRange), $(D_PARAM func) must be a
+ functions to pass to `fwdRange`, $(D_PARAM func) must be a
delegate.
If $(D_PARAM func) retains state which changes as it is called, then
some algorithms will not work correctly, because the range's
- $(D save) will have failed to have really saved the range's state.
+ `save` will have failed to have really saved the range's state.
To avoid such bugs, don't pass a delegate which is
- not logically pure to $(D fwdRange). If $(D_PARAM func) is given the
+ not logically pure to `fwdRange`. If $(D_PARAM func) is given the
same time point with two different calls, it must return the same
result both times.
@@ -6293,7 +6316,7 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1)));
Example:
--------------------
auto interval = NegInfInterval!Date(Date(2010, 9, 9));
-auto func = delegate (in Date date) //For iterating over even-numbered days.
+auto func = delegate (scope const Date date) //For iterating over even-numbered days.
{
if ((date.day & 1) == 0)
return date - dur!"days"(2);
@@ -6320,7 +6343,7 @@ range.popFront();
assert(!range.empty);
--------------------
+/
- NegInfIntervalRange!(TP) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const
+ NegInfIntervalRange!(TP) bwdRange(TP delegate(scope const TP) func, PopFirst popFirst = PopFirst.no) const
{
auto range = NegInfIntervalRange!(TP)(this, func);
@@ -6334,9 +6357,9 @@ assert(!range.empty);
/+
Converts this interval to a string.
+/
- //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't
- //have versions of toString() with extra modifiers, so we define one version
- //with modifiers and one without.
+ // Due to bug https://issues.dlang.org/show_bug.cgi?id=3715 , we can't
+ // have versions of toString() with extra modifiers,
+ // so we define one version with modifiers and one without.
string toString()
{
return _toStringImpl();
@@ -6346,9 +6369,9 @@ assert(!range.empty);
/++
Converts this interval to a string.
+/
- //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't
- //have versions of toString() with extra modifiers, so we define one version
- //with modifiers and one without.
+ // Due to bug https://issues.dlang.org/show_bug.cgi?id=3715 , we can't
+ // have versions of toString() with extra modifiers,
+ // so we define one version with modifiers and one without.
string toString() const nothrow
{
return _toStringImpl();
@@ -6460,7 +6483,7 @@ private:
auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval)
+ static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval)
{
negInfInterval.contains(interval);
}
@@ -6586,7 +6609,7 @@ private:
auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval)
+ static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval)
{
negInfInterval.isBefore(interval);
}
@@ -6708,7 +6731,7 @@ private:
auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval)
+ static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval)
{
negInfInterval.isAfter(interval);
}
@@ -6809,7 +6832,7 @@ private:
auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval)
+ static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval)
{
negInfInterval.intersects(interval);
}
@@ -6906,7 +6929,7 @@ private:
auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(I, J)(in I interval1, in J interval2)
+ static void testInterval(I, J)(scope const I interval1, scope const J interval2)
{
interval1.intersection(interval2);
}
@@ -7024,7 +7047,7 @@ private:
auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval)
+ static void testInterval(scope const NegInfInterval!Date negInfInterval, scope const Interval!Date interval)
{
negInfInterval.isAdjacent(interval);
}
@@ -7122,7 +7145,7 @@ private:
auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(I, J)(in I interval1, in J interval2)
+ static void testInterval(I, J)(scope const I interval1, scope const J interval2)
{
interval1.merge(interval2);
}
@@ -7231,7 +7254,7 @@ private:
auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(I, J)(in I interval1, in J interval2)
+ static void testInterval(I, J)(scope const I interval1, scope const J interval2)
{
interval1.span(interval2);
}
@@ -7342,7 +7365,8 @@ private:
auto interval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__)
+ static void testInterval(I)(I interval, scope const Duration duration,
+ scope const I expected, size_t line = __LINE__)
{
interval.shift(duration);
assert(interval == expected);
@@ -7426,7 +7450,8 @@ private:
auto interval = NegInfInterval!Date(Date(2012, 1, 7));
- static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__)
+ static void testInterval(I)(I interval,
+ scope const Duration duration, scope const I expected, size_t line = __LINE__)
{
interval.expand(duration);
assert(interval == expected);
@@ -7520,7 +7545,7 @@ private:
//Verify Examples.
auto interval = NegInfInterval!Date(Date(2010, 9, 9));
- auto func = delegate (in Date date)
+ auto func = delegate (scope const Date date)
{
if ((date.day & 1) == 0)
return date - dur!"days"(2);
@@ -7570,27 +7595,27 @@ private:
Range-generating function.
Returns a delegate which returns the next time point with the given
- $(D DayOfWeek) in a range.
+ `DayOfWeek` in a range.
Using this delegate allows iteration over successive time points which
- are all the same day of the week. e.g. passing $(D DayOfWeek.mon) to
- $(D everyDayOfWeek) would result in a delegate which could be used to
+ are all the same day of the week. e.g. passing `DayOfWeek.mon` to
+ `everyDayOfWeek` would result in a delegate which could be used to
iterate over all of the Mondays in a range.
Params:
dir = The direction to iterate in. If passing the return value to
- $(D fwdRange), use $(D Direction.fwd). If passing it to
- $(D bwdRange), use $(D Direction.bwd).
+ `fwdRange`, use `Direction.fwd`. If passing it to
+ `bwdRange`, use `Direction.bwd`.
dayOfWeek = The week that each time point in the range will be.
+/
-TP delegate(in TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow
+TP delegate(scope const TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow
if (isTimePoint!TP &&
(dir == Direction.fwd || dir == Direction.bwd) &&
__traits(hasMember, TP, "dayOfWeek") &&
!__traits(isStaticFunction, TP.dayOfWeek) &&
is(typeof(TP.dayOfWeek) == DayOfWeek))
{
- TP func(in TP tp)
+ TP func(scope const TP tp)
{
TP retval = cast(TP) tp;
immutable days = daysToDayOfWeek(retval.dayOfWeek, dayOfWeek);
@@ -7678,22 +7703,22 @@ if (isTimePoint!TP &&
So, using this delegate allows iteration over successive time points
which are in the same month but different years. For example,
iterate over each successive December 25th in an interval by starting with a
- date which had the 25th as its day and passed $(D Month.dec) to
- $(D everyMonth) to create the delegate.
+ date which had the 25th as its day and passed `Month.dec` to
+ `everyMonth` to create the delegate.
Since it wouldn't really make sense to be iterating over a specific month
and end up with some of the time points in the succeeding month or two years
- after the previous time point, $(D AllowDayOverflow.no) is always used when
+ after the previous time point, `AllowDayOverflow.no` is always used when
calculating the next time point.
Params:
dir = The direction to iterate in. If passing the return value to
- $(D fwdRange), use $(D Direction.fwd). If passing it to
- $(D bwdRange), use $(D Direction.bwd).
+ `fwdRange`, use `Direction.fwd`. If passing it to
+ `bwdRange`, use `Direction.bwd`.
month = The month that each time point in the range will be in
(January is 1).
+/
-TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int month)
+TP delegate(scope const TP) everyMonth(TP, Direction dir = Direction.fwd)(int month)
if (isTimePoint!TP &&
(dir == Direction.fwd || dir == Direction.bwd) &&
__traits(hasMember, TP, "month") &&
@@ -7704,7 +7729,7 @@ if (isTimePoint!TP &&
enforceValid!"months"(month);
- TP func(in TP tp)
+ TP func(scope const TP tp)
{
TP retval = cast(TP) tp;
immutable months = monthsToMonth(retval.month, month);
@@ -7812,23 +7837,23 @@ if (isTimePoint!TP &&
duration later.
Using this delegate allows iteration over successive time points which
- are apart by the given duration e.g. passing $(D dur!"days"(3)) to
- $(D everyDuration) would result in a delegate which could be used to iterate
+ are apart by the given duration e.g. passing `dur!"days"(3)` to
+ `everyDuration` would result in a delegate which could be used to iterate
over a range of days which are each 3 days apart.
Params:
dir = The direction to iterate in. If passing the return value to
- $(D fwdRange), use $(D Direction.fwd). If passing it to
- $(D bwdRange), use $(D Direction.bwd).
+ `fwdRange`, use `Direction.fwd`. If passing it to
+ `bwdRange`, use `Direction.bwd`.
duration = The duration which separates each successive time point in
the range.
+/
-TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D)(D duration) nothrow
+TP delegate(scope const TP) everyDuration(TP, Direction dir = Direction.fwd, D)(D duration) nothrow
if (isTimePoint!TP &&
__traits(compiles, TP.init + duration) &&
(dir == Direction.fwd || dir == Direction.bwd))
{
- TP func(in TP tp)
+ TP func(scope const TP tp)
{
static if (dir == Direction.fwd)
return tp + duration;
@@ -7896,39 +7921,39 @@ if (isTimePoint!TP &&
Returns a delegate which returns the next time point which is the given
number of years, month, and duration later.
- The difference between this version of $(D everyDuration) and the version
+ The difference between this version of `everyDuration` and the version
which just takes a $(REF Duration, core,time) is that this one also takes
- the number of years and months (along with an $(D AllowDayOverflow) to
+ the number of years and months (along with an `AllowDayOverflow` to
indicate whether adding years and months should allow the days to overflow).
- Note that if iterating forward, $(D add!"years"()) is called on the given
- time point, then $(D add!"months"()), and finally the duration is added
+ Note that if iterating forward, `add!"years"()` is called on the given
+ time point, then `add!"months"()`, and finally the duration is added
to it. However, if iterating backwards, the duration is added first, then
- $(D add!"months"()) is called, and finally $(D add!"years"()) is called.
+ `add!"months"()` is called, and finally `add!"years"()` is called.
That way, going backwards generates close to the same time points that
iterating forward does, but since adding years and months is not entirely
reversible (due to possible day overflow, regardless of whether
- $(D AllowDayOverflow.yes) or $(D AllowDayOverflow.no) is used), it can't be
+ `AllowDayOverflow.yes` or `AllowDayOverflow.no` is used), it can't be
guaranteed that iterating backwards will give the same time points as
iterating forward would have (even assuming that the end of the range is a
time point which would be returned by the delegate when iterating forward
- from $(D begin)).
+ from `begin`).
Params:
dir = The direction to iterate in. If passing the return
- value to $(D fwdRange), use $(D Direction.fwd). If
- passing it to $(D bwdRange), use $(D Direction.bwd).
+ value to `fwdRange`, use `Direction.fwd`. If
+ passing it to `bwdRange`, use `Direction.bwd`.
years = The number of years to add to the time point passed to
the delegate.
months = The number of months to add to the time point passed to
the delegate.
allowOverflow = Whether the days should be allowed to overflow on
- $(D begin) and $(D end), causing their month to
+ `begin` and `end`, causing their month to
increment.
duration = The duration to add to the time point passed to the
delegate.
+/
-TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D)
+TP delegate(scope const TP) everyDuration(TP, Direction dir = Direction.fwd, D)
(int years,
int months = 0,
AllowDayOverflow allowOverflow = AllowDayOverflow.yes,
@@ -7939,7 +7964,7 @@ if (isTimePoint!TP &&
__traits(compiles, TP.init.add!"months"(months)) &&
(dir == Direction.fwd || dir == Direction.bwd))
{
- TP func(in TP tp)
+ TP func(scope const TP tp)
{
static if (dir == Direction.fwd)
{
@@ -8039,24 +8064,24 @@ if (isTimePoint!TP &&
/++
A range over an $(LREF Interval).
- $(D IntervalRange) is only ever constructed by $(LREF Interval). However, when
- it is constructed, it is given a function, $(D func), which is used to
- generate the time points which are iterated over. $(D func) takes a time
+ `IntervalRange` is only ever constructed by $(LREF Interval). However, when
+ it is constructed, it is given a function, `func`, which is used to
+ generate the time points which are iterated over. `func` takes a time
point and returns a time point of the same type. For instance,
to iterate over all of the days in
- the interval $(D Interval!Date), pass a function to $(LREF Interval)'s
- $(D fwdRange) where that function took a $(REF Date,std,datetime,date) and
+ the interval `Interval!Date`, pass a function to $(LREF Interval)'s
+ `fwdRange` where that function took a $(REF Date,std,datetime,date) and
returned a $(REF Date,std,datetime,date) which was one day later. That
- function would then be used by $(D IntervalRange)'s $(D popFront) to iterate
+ function would then be used by `IntervalRange`'s `popFront` to iterate
over the $(REF Date,std,datetime,date)s in the interval.
If $(D dir == Direction.fwd), then a range iterates forward in time, whereas
if $(D dir == Direction.bwd), then it iterates backwards in time. So, if
$(D dir == Direction.fwd) then $(D front == interval.begin), whereas if
- $(D dir == Direction.bwd) then $(D front == interval.end). $(D func) must
+ $(D dir == Direction.bwd) then $(D front == interval.end). `func` must
generate a time point going in the proper direction of iteration, or a
$(REF DateTimeException,std,datetime,date) will be thrown. So, to iterate
- forward in time, the time point that $(D func) generates must be later in
+ forward in time, the time point that `func` generates must be later in
time than the one passed to it. If it's either identical or earlier in time,
then a $(REF DateTimeException,std,datetime,date) will be thrown. To
iterate backwards, then the generated time point must be before the time
@@ -8065,20 +8090,20 @@ if (isTimePoint!TP &&
If the generated time point is ever passed the edge of the range in the
proper direction, then the edge of that range will be used instead. So, if
iterating forward, and the generated time point is past the interval's
- $(D end), then $(D front) becomes $(D end). If iterating backwards, and the
- generated time point is before $(D begin), then $(D front) becomes
- $(D begin). In either case, the range would then be empty.
+ `end`, then `front` becomes `end`. If iterating backwards, and the
+ generated time point is before `begin`, then `front` becomes
+ `begin`. In either case, the range would then be empty.
- Also note that while normally the $(D begin) of an interval is included in
- it and its $(D end) is excluded from it, if $(D dir == Direction.bwd), then
- $(D begin) is treated as excluded and $(D end) is treated as included. This
+ Also note that while normally the `begin` of an interval is included in
+ it and its `end` is excluded from it, if $(D dir == Direction.bwd), then
+ `begin` is treated as excluded and `end` is treated as included. This
allows for the same behavior in both directions. This works because none of
- $(LREF Interval)'s functions which care about whether $(D begin) or $(D end)
- is included or excluded are ever called by $(D IntervalRange). $(D interval)
+ $(LREF Interval)'s functions which care about whether `begin` or `end`
+ is included or excluded are ever called by `IntervalRange`. `interval`
returns a normal interval, regardless of whether $(D dir == Direction.fwd)
or if $(D dir == Direction.bwd), so any $(LREF Interval) functions which are
- called on it which care about whether $(D begin) or $(D end) are included or
- excluded will treat $(D begin) as included and $(D end) as excluded.
+ called on it which care about whether `begin` or `end` are included or
+ excluded will treat `begin` as included and `end` as excluded.
+/
struct IntervalRange(TP, Direction dir)
if (isTimePoint!TP && dir != Direction.both)
@@ -8087,7 +8112,7 @@ public:
/++
Params:
- rhs = The $(D IntervalRange) to assign to this one.
+ rhs = The `IntervalRange` to assign to this one.
+/
ref IntervalRange opAssign(ref IntervalRange rhs) pure nothrow
{
@@ -8105,7 +8130,7 @@ public:
/++
- Whether this $(D IntervalRange) is empty.
+ Whether this `IntervalRange` is empty.
+/
@property bool empty() const pure nothrow
{
@@ -8131,20 +8156,20 @@ public:
/++
- Pops $(D front) from the range, using $(D func) to generate the next
+ Pops `front` from the range, using `func` to generate the next
time point in the range. If the generated time point is beyond the edge
- of the range, then $(D front) is set to that edge, and the range is then
+ of the range, then `front` is set to that edge, and the range is then
empty. So, if iterating forwards, and the generated time point is
- greater than the interval's $(D end), then $(D front) is set to
- $(D end). If iterating backwards, and the generated time point is less
- than the interval's $(D begin), then $(D front) is set to $(D begin).
+ greater than the interval's `end`, then `front` is set to
+ `end`. If iterating backwards, and the generated time point is less
+ than the interval's `begin`, then `front` is set to `begin`.
Throws:
$(REF DateTimeException,std,datetime,date) if the range is empty
or if the generated time point is in the wrong direction (i.e. if
- iterating forward and the generated time point is before $(D front),
+ iterating forward and the generated time point is before `front`,
or if iterating backwards and the generated time point is after
- $(D front)).
+ `front`).
+/
void popFront()
{
@@ -8176,7 +8201,7 @@ public:
/++
- Returns a copy of $(D this).
+ Returns a copy of `this`.
+/
@property IntervalRange save() pure nothrow
{
@@ -8185,7 +8210,7 @@ public:
/++
- The interval that this $(D IntervalRange) currently covers.
+ The interval that this `IntervalRange` currently covers.
+/
@property Interval!TP interval() const pure nothrow
{
@@ -8196,14 +8221,14 @@ public:
/++
The function used to generate the next time point in the range.
+/
- TP delegate(in TP) func() pure nothrow @property
+ TP delegate(scope const TP) func() pure nothrow @property
{
return _func;
}
/++
- The $(D Direction) that this range iterates in.
+ The `Direction` that this range iterates in.
+/
@property Direction direction() const pure nothrow
{
@@ -8219,7 +8244,7 @@ private:
func = The function used to generate the time points which are
iterated over.
+/
- this(in Interval!TP interval, TP delegate(in TP) func) pure nothrow
+ this(const Interval!TP interval, TP delegate(scope const TP) func) pure nothrow @safe
{
_func = func;
_interval = interval;
@@ -8243,7 +8268,7 @@ private:
$(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is
in the wrong direction.
+/
- void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const
+ void _enforceCorrectDirection(scope const TP newTP, size_t line = __LINE__) const
{
import std.format : format;
@@ -8269,7 +8294,7 @@ private:
Interval!TP _interval;
- TP delegate(in TP) _func;
+ TP delegate(scope const TP) _func;
}
//Test that IntervalRange satisfies the range predicates that it's supposed to satisfy.
@@ -8282,8 +8307,8 @@ private:
static assert(isInputRange!(IntervalRange!(Date, Direction.fwd)));
static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd)));
- //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895
- //static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date));
+ // Commented out due to bug https://issues.dlang.org/show_bug.cgi?id=4895
+ // static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date));
static assert(!isBidirectionalRange!(IntervalRange!(Date, Direction.fwd)));
static assert(!isRandomAccessRange!(IntervalRange!(Date, Direction.fwd)));
@@ -8306,25 +8331,25 @@ private:
import std.datetime.systime;
{
- Date dateFunc(in Date date) { return date; }
+ Date dateFunc(scope const Date date) { return date; }
auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7));
auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc);
}
{
- TimeOfDay todFunc(in TimeOfDay tod) { return tod; }
+ TimeOfDay todFunc(scope const TimeOfDay tod) { return tod; }
auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0));
auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc);
}
{
- DateTime dtFunc(in DateTime dt) { return dt; }
+ DateTime dtFunc(scope const DateTime dt) { return dt; }
auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0));
auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc);
}
{
- SysTime stFunc(in SysTime st) { return cast(SysTime) st; }
+ SysTime stFunc(scope const SysTime st) { return cast(SysTime) st; }
auto interval = Interval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)),
SysTime(DateTime(2012, 1, 7, 14, 0, 0)));
auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc);
@@ -8378,7 +8403,8 @@ private:
{
auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange(
everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes);
- assertThrown!DateTimeException((in IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange));
+ assertThrown!DateTimeException(
+ (scope const IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange));
auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed));
assert(range.front == Date(2010, 7, 4));
@@ -8395,7 +8421,8 @@ private:
{
auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange(
everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes);
- assertThrown!DateTimeException((in IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange));
+ assertThrown!DateTimeException(
+ (scope const IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange));
auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(
everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed));
@@ -8573,27 +8600,27 @@ private:
/++
- A range over a $(D PosInfInterval). It is an infinite range.
+ A range over a `PosInfInterval`. It is an infinite range.
- $(D PosInfIntervalRange) is only ever constructed by $(D PosInfInterval).
- However, when it is constructed, it is given a function, $(D func), which
- is used to generate the time points which are iterated over. $(D func)
+ `PosInfIntervalRange` is only ever constructed by `PosInfInterval`.
+ However, when it is constructed, it is given a function, `func`, which
+ is used to generate the time points which are iterated over. `func`
takes a time point and returns a time point of the same type. For
instance, to iterate
- over all of the days in the interval $(D PosInfInterval!Date), pass a
- function to $(D PosInfInterval)'s $(D fwdRange) where that function took a
+ over all of the days in the interval `PosInfInterval!Date`, pass a
+ function to `PosInfInterval`'s `fwdRange` where that function took a
$(REF Date,std,datetime,date) and returned a $(REF Date,std,datetime,date)
which was one day later. That function would then be used by
- $(D PosInfIntervalRange)'s $(D popFront) to iterate over the
+ `PosInfIntervalRange`'s `popFront` to iterate over the
$(REF Date,std,datetime,date)s in the interval - though obviously, since the
- range is infinite, use a function such as $(D std.range.take) with it rather
+ range is infinite, use a function such as `std.range.take` with it rather
than iterating over $(I all) of the dates.
As the interval goes to positive infinity, the range is always iterated over
- forwards, never backwards. $(D func) must generate a time point going in
+ forwards, never backwards. `func` must generate a time point going in
the proper direction of iteration, or a
$(REF DateTimeException,std,datetime,date) will be thrown. So, the time
- points that $(D func) generates must be later in time than the one passed to
+ points that `func` generates must be later in time than the one passed to
it. If it's either identical or earlier in time, then a
$(REF DateTimeException,std,datetime,date) will be thrown.
+/
@@ -8604,7 +8631,7 @@ public:
/++
Params:
- rhs = The $(D PosInfIntervalRange) to assign to this one.
+ rhs = The `PosInfIntervalRange` to assign to this one.
+/
ref PosInfIntervalRange opAssign(ref PosInfIntervalRange rhs) pure nothrow
{
@@ -8637,12 +8664,12 @@ public:
/++
- Pops $(D front) from the range, using $(D func) to generate the next
+ Pops `front` from the range, using `func` to generate the next
time point in the range.
Throws:
$(REF DateTimeException,std,datetime,date) if the generated time
- point is less than $(D front).
+ point is less than `front`.
+/
void popFront()
{
@@ -8653,7 +8680,7 @@ public:
/++
- Returns a copy of $(D this).
+ Returns a copy of `this`.
+/
@property PosInfIntervalRange save() pure nothrow
{
@@ -8673,7 +8700,7 @@ public:
/++
The function used to generate the next time point in the range.
+/
- TP delegate(in TP) func() pure nothrow @property
+ TP delegate(scope const TP) func() pure nothrow @property
{
return _func;
}
@@ -8687,7 +8714,7 @@ private:
func = The function used to generate the time points which are
iterated over.
+/
- this(in PosInfInterval!TP interval, TP delegate(in TP) func) pure nothrow
+ this(const PosInfInterval!TP interval, TP delegate(scope const TP) func) pure nothrow
{
_func = func;
_interval = interval;
@@ -8699,7 +8726,7 @@ private:
$(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is
in the wrong direction.
+/
- void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const
+ void _enforceCorrectDirection(scope const TP newTP, size_t line = __LINE__) const
{
import std.format : format;
@@ -8713,7 +8740,7 @@ private:
PosInfInterval!TP _interval;
- TP delegate(in TP) _func;
+ TP delegate(scope const TP) _func;
}
//Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy.
@@ -8727,8 +8754,8 @@ private:
static assert(isForwardRange!(PosInfIntervalRange!Date));
static assert(isInfinite!(PosInfIntervalRange!Date));
- //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895
- //static assert(!isOutputRange!(PosInfIntervalRange!Date, Date));
+ // Commented out due to bug https://issues.dlang.org/show_bug.cgi?id=4895
+ // static assert(!isOutputRange!(PosInfIntervalRange!Date, Date));
static assert(!isBidirectionalRange!(PosInfIntervalRange!Date));
static assert(!isRandomAccessRange!(PosInfIntervalRange!Date));
static assert(!hasSwappableElements!(PosInfIntervalRange!Date));
@@ -8749,25 +8776,25 @@ private:
import std.datetime.systime;
{
- Date dateFunc(in Date date) { return date; }
+ Date dateFunc(scope const Date date) { return date; }
auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4));
auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc);
}
{
- TimeOfDay todFunc(in TimeOfDay tod) { return tod; }
+ TimeOfDay todFunc(scope const TimeOfDay tod) { return tod; }
auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7));
auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc);
}
{
- DateTime dtFunc(in DateTime dt) { return dt; }
+ DateTime dtFunc(scope const DateTime dt) { return dt; }
auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7));
auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc);
}
{
- SysTime stFunc(in SysTime st) { return cast(SysTime) st; }
+ SysTime stFunc(scope const SysTime st) { return cast(SysTime) st; }
auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)));
auto ir = PosInfIntervalRange!SysTime(posInfInterval, &stFunc);
}
@@ -8848,37 +8875,37 @@ private:
/++
- A range over a $(D NegInfInterval). It is an infinite range.
+ A range over a `NegInfInterval`. It is an infinite range.
- $(D NegInfIntervalRange) is only ever constructed by $(D NegInfInterval).
- However, when it is constructed, it is given a function, $(D func), which
- is used to generate the time points which are iterated over. $(D func)
+ `NegInfIntervalRange` is only ever constructed by `NegInfInterval`.
+ However, when it is constructed, it is given a function, `func`, which
+ is used to generate the time points which are iterated over. `func`
takes a time point and returns a time point of the same type. For
instance, to iterate over all of the days in the interval
- $(D NegInfInterval!Date), pass a function to $(D NegInfInterval)'s
- $(D bwdRange) where that function took a $(REF Date,std,datetime,date) and
+ `NegInfInterval!Date`, pass a function to `NegInfInterval`'s
+ `bwdRange` where that function took a $(REF Date,std,datetime,date) and
returned a $(REF Date,std,datetime,date) which was one day earlier. That
- function would then be used by $(D NegInfIntervalRange)'s $(D popFront) to
+ function would then be used by `NegInfIntervalRange`'s `popFront` to
iterate over the $(REF Date,std,datetime,date)s in the interval - though
obviously, since the range is infinite, use a function such as
- $(D std.range.take) with it rather than iterating over $(I all) of the dates.
+ `std.range.take` with it rather than iterating over $(I all) of the dates.
As the interval goes to negative infinity, the range is always iterated over
- backwards, never forwards. $(D func) must generate a time point going in
+ backwards, never forwards. `func` must generate a time point going in
the proper direction of iteration, or a
$(REF DateTimeException,std,datetime,date) will be thrown. So, the time
- points that $(D func) generates must be earlier in time than the one passed
+ points that `func` generates must be earlier in time than the one passed
to it. If it's either identical or later in time, then a
$(REF DateTimeException,std,datetime,date) will be thrown.
- Also note that while normally the $(D end) of an interval is excluded from
- it, $(D NegInfIntervalRange) treats it as if it were included. This allows
- for the same behavior as with $(D PosInfIntervalRange). This works
- because none of $(D NegInfInterval)'s functions which care about whether
- $(D end) is included or excluded are ever called by
- $(D NegInfIntervalRange). $(D interval) returns a normal interval, so any
- $(D NegInfInterval) functions which are called on it which care about
- whether $(D end) is included or excluded will treat $(D end) as excluded.
+ Also note that while normally the `end` of an interval is excluded from
+ it, `NegInfIntervalRange` treats it as if it were included. This allows
+ for the same behavior as with `PosInfIntervalRange`. This works
+ because none of `NegInfInterval`'s functions which care about whether
+ `end` is included or excluded are ever called by
+ `NegInfIntervalRange`. `interval` returns a normal interval, so any
+ `NegInfInterval` functions which are called on it which care about
+ whether `end` is included or excluded will treat `end` as excluded.
+/
struct NegInfIntervalRange(TP)
if (isTimePoint!TP)
@@ -8887,7 +8914,7 @@ public:
/++
Params:
- rhs = The $(D NegInfIntervalRange) to assign to this one.
+ rhs = The `NegInfIntervalRange` to assign to this one.
+/
ref NegInfIntervalRange opAssign(ref NegInfIntervalRange rhs) pure nothrow
{
@@ -8921,12 +8948,12 @@ public:
/++
- Pops $(D front) from the range, using $(D func) to generate the next
+ Pops `front` from the range, using `func` to generate the next
time point in the range.
Throws:
$(REF DateTimeException,std,datetime,date) if the generated time
- point is greater than $(D front).
+ point is greater than `front`.
+/
void popFront()
{
@@ -8937,7 +8964,7 @@ public:
/++
- Returns a copy of $(D this).
+ Returns a copy of `this`.
+/
@property NegInfIntervalRange save() pure nothrow
{
@@ -8957,7 +8984,7 @@ public:
/++
The function used to generate the next time point in the range.
+/
- TP delegate(in TP) func() pure nothrow @property
+ TP delegate(scope const TP) func() pure nothrow @property
{
return _func;
}
@@ -8971,7 +8998,7 @@ private:
func = The function used to generate the time points which are
iterated over.
+/
- this(in NegInfInterval!TP interval, TP delegate(in TP) func) pure nothrow
+ this(const NegInfInterval!TP interval, TP delegate(scope const TP) func) pure nothrow
{
_func = func;
_interval = interval;
@@ -8983,7 +9010,7 @@ private:
$(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is
in the wrong direction.
+/
- void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const
+ void _enforceCorrectDirection(scope const TP newTP, size_t line = __LINE__) const
{
import std.format : format;
@@ -8997,7 +9024,7 @@ private:
NegInfInterval!TP _interval;
- TP delegate(in TP) _func;
+ TP delegate(scope const TP) _func;
}
//Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy.
@@ -9010,8 +9037,8 @@ private:
static assert(isForwardRange!(NegInfIntervalRange!Date));
static assert(isInfinite!(NegInfIntervalRange!Date));
- //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895
- //static assert(!isOutputRange!(NegInfIntervalRange!Date, Date));
+ // Commented out due to bug https://issues.dlang.org/show_bug.cgi?id=4895
+ // static assert(!isOutputRange!(NegInfIntervalRange!Date, Date));
static assert(!isBidirectionalRange!(NegInfIntervalRange!Date));
static assert(!isRandomAccessRange!(NegInfIntervalRange!Date));
static assert(!hasSwappableElements!(NegInfIntervalRange!Date));
@@ -9031,25 +9058,25 @@ private:
import std.datetime.systime;
{
- Date dateFunc(in Date date) { return date; }
+ Date dateFunc(scope const Date date) { return date; }
auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7));
auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc);
}
{
- TimeOfDay todFunc(in TimeOfDay tod) { return tod; }
+ TimeOfDay todFunc(scope const TimeOfDay tod) { return tod; }
auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0));
auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc);
}
{
- DateTime dtFunc(in DateTime dt) { return dt; }
+ DateTime dtFunc(scope const DateTime dt) { return dt; }
auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0));
auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc);
}
{
- SysTime stFunc(in SysTime st) { return cast(SysTime)(st); }
+ SysTime stFunc(scope const SysTime st) { return cast(SysTime)(st); }
auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0)));
auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc);
}
diff --git a/libphobos/src/std/datetime/package.d b/libphobos/src/std/datetime/package.d
index 976d06ddb79..8e9f5ae83be 100644
--- a/libphobos/src/std/datetime/package.d
+++ b/libphobos/src/std/datetime/package.d
@@ -1,102 +1,80 @@
// Written in the D programming language
/++
- Module containing Date/Time functionality.
+ $(SCRIPT inhibitQuickIndex = 1;)
- This module provides:
- $(UL
- $(LI Types to represent points in time:
- $(REF SysTime,std,_datetime,systime),
- $(REF Date,std,_datetime,date),
- $(REF TimeOfDay,std,_datetime,date),
- $(REF DateTime,std,_datetime,date).)
- $(LI Types to represent intervals of time.)
- $(LI Types to represent ranges over intervals of time.)
- $(LI Types to represent time zones (used by
- $(REF SysTime,std,_datetime,systime)).)
- $(LI A platform-independent, high precision stopwatch type:
- $(LREF StopWatch))
- $(LI Benchmarking functions.)
- $(LI Various helper functions.)
- )
-
- Closely related to std.datetime is <a href="core_time.html">$(D core.time)</a>,
- and some of the time types used in std.datetime come from there - such as
- $(REF Duration, core,time), $(REF TickDuration, core,time), and
- $(REF FracSec, core,time).
- core.time is publically imported into std.datetime, it isn't necessary
- to import it separately.
-
- Three of the main concepts used in this module are time points, time
- durations, and time intervals.
-
- A time point is a specific point in time. e.g. January 5th, 2010
- or 5:00.
-
- A time duration is a length of time with units. e.g. 5 days or 231 seconds.
+ Phobos provides the following functionality for time:
- A time interval indicates a period of time associated with a fixed point in
- time. It is either two time points associated with each other,
- indicating the time starting at the first point up to, but not including,
- the second point - e.g. [January 5th, 2010 - March 10th, 2010$(RPAREN) - or
- it is a time point and a time duration associated with one another. e.g.
- January 5th, 2010 and 5 days, indicating [January 5th, 2010 -
- January 10th, 2010$(RPAREN).
-
- Various arithmetic operations are supported between time points and
- durations (e.g. the difference between two time points is a time duration),
- and ranges can be gotten from time intervals, so range-based operations may
- be done on a series of time points.
-
- The types that the typical user is most likely to be interested in are
- $(REF Date,std,_datetime,date) (if they want dates but don't care about
- time), $(REF DateTime,std,_datetime,date) (if they want dates and times
- but don't care about time zones), $(REF SysTime,std,_datetime,systime) (if
- they want the date and time from the OS and/or do care about time zones),
- and StopWatch (a platform-independent, high precision stop watch).
- $(REF Date,std,_datetime,date) and $(REF DateTime,std,_datetime,date) are
- optimized for calendar-based operations, while
- $(REF SysTime,std,_datetime,systime) is designed for dealing with time from
- the OS. Check out their specific documentation for more details.
-
- To get the current time, use $(REF Clock.currTime,std,_datetime,systime).
- It will return the current time as a $(REF SysTime,std,_datetime,systime). To
- print it, $(D toString) is sufficient, but if using $(D toISOString),
- $(D toISOExtString), or $(D toSimpleString), use the corresponding
- $(D fromISOString), $(D fromISOExtString), or $(D fromSimpleString) to
- create a $(REF SysTime,std,_datetime,systime) from the string.
-
---------------------
-auto currentTime = Clock.currTime();
-auto timeString = currentTime.toISOExtString();
-auto restoredTime = SysTime.fromISOExtString(timeString);
---------------------
+ $(DIVC quickindex,
+ $(BOOKTABLE ,
+ $(TR $(TH Functionality) $(TH Symbols)
+ )
+ $(TR
+ $(TD Points in Time)
+ $(TD
+ $(REF_ALTTEXT Date, Date, std, datetime, date)$(NBSP)
+ $(REF_ALTTEXT TimeOfDay, TimeOfDay, std, datetime, date)$(NBSP)
+ $(REF_ALTTEXT DateTime, DateTime, std, datetime, date)$(NBSP)
+ $(REF_ALTTEXT SysTime, SysTime, std, datetime, systime)$(NBSP)
+ )
+ )
+ $(TR
+ $(TD Timezones)
+ $(TD
+ $(REF_ALTTEXT TimeZone, TimeZone, std, datetime, timezone)$(NBSP)
+ $(REF_ALTTEXT UTC, UTC, std, datetime, timezone)$(NBSP)
+ $(REF_ALTTEXT LocalTime, LocalTime, std, datetime, timezone)$(NBSP)
+ $(REF_ALTTEXT PosixTimeZone, PosixTimeZone, std, datetime, timezone)$(NBSP)
+ $(REF_ALTTEXT WindowsTimeZone, WindowsTimeZone, std, datetime, timezone)$(NBSP)
+ $(REF_ALTTEXT SimpleTimeZone, SimpleTimeZone, std, datetime, timezone)$(NBSP)
+ )
+ )
+ $(TR
+ $(TD Intervals and Ranges of Time)
+ $(TD
+ $(REF_ALTTEXT Interval, Interval, std, datetime, interval)$(NBSP)
+ $(REF_ALTTEXT PosInfInterval, PosInfInterval, std, datetime, interval)$(NBSP)
+ $(REF_ALTTEXT NegInfInterval, NegInfInterval, std, datetime, interval)$(NBSP)
+ )
+ )
+ $(TR
+ $(TD Durations of Time)
+ $(TD
+ $(REF_ALTTEXT Duration, Duration, core, time)$(NBSP)
+ $(REF_ALTTEXT weeks, weeks, core, time)$(NBSP)
+ $(REF_ALTTEXT days, days, core, time)$(NBSP)
+ $(REF_ALTTEXT hours, hours, core, time)$(NBSP)
+ $(REF_ALTTEXT minutes, minutes, core, time)$(NBSP)
+ $(REF_ALTTEXT seconds, seconds, core, time)$(NBSP)
+ $(REF_ALTTEXT msecs, msecs, core, time)$(NBSP)
+ $(REF_ALTTEXT usecs, usecs, core, time)$(NBSP)
+ $(REF_ALTTEXT hnsecs, hnsecs, core, time)$(NBSP)
+ $(REF_ALTTEXT nsecs, nsecs, core, time)$(NBSP)
+ )
+ )
+ $(TR
+ $(TD Time Measurement and Benchmarking)
+ $(TD
+ $(REF_ALTTEXT MonoTime, MonoTime, core, time)$(NBSP)
+ $(REF_ALTTEXT StopWatch, StopWatch, std, datetime, stopwatch)$(NBSP)
+ $(REF_ALTTEXT benchmark, benchmark, std, datetime, stopwatch)$(NBSP)
+ )
+ )
+ ))
- Various functions take a string (or strings) to represent a unit of time
- (e.g. $(D convert!("days", "hours")(numDays))). The valid strings to use
- with such functions are $(D "years"), $(D "months"), $(D "weeks"),
- $(D "days"), $(D "hours"), $(D "minutes"), $(D "seconds"),
- $(D "msecs") (milliseconds), $(D "usecs") (microseconds),
- $(D "hnsecs") (hecto-nanoseconds - i.e. 100 ns), or some subset thereof.
- There are a few functions in core.time which take $(D "nsecs"), but because
- nothing in std.datetime has precision greater than hnsecs, and very little
- in core.time does, no functions in std.datetime accept $(D "nsecs").
- To remember which units are abbreviated and which aren't,
- all units seconds and greater use their full names, and all
- sub-second units are abbreviated (since they'd be rather long if they
- weren't).
+ This functionality is separated into the following modules
- Note:
- $(REF DateTimeException,std,_datetime,date) is an alias for
- $(REF TimeException, core,time), so you don't need to worry about
- core.time functions and std.datetime functions throwing different
- exception types (except in the rare case that they throw something other
- than $(REF TimeException, core,time) or
- $(REF DateTimeException,std,_datetime,date)).
+ $(UL
+ $(LI $(MREF std, datetime, date) for points in time without timezones.)
+ $(LI $(MREF std, datetime, timezone) for classes which represent timezones.)
+ $(LI $(MREF std, datetime, systime) for a point in time with a timezone.)
+ $(LI $(MREF std, datetime, interval) for types which represent series of points in time.)
+ $(LI $(MREF std, datetime, stopwatch) for measuring time.)
+ )
See_Also:
- $(DDLINK intro-to-_datetime, Introduction to std.datetime,
- Introduction to std&#46;_datetime)<br>
+ $(DDLINK intro-to-datetime, Introduction to std.datetime,
+ Introduction to std&#46;datetime)<br>
$(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601)<br>
$(HTTP en.wikipedia.org/wiki/Tz_database,
Wikipedia entry on TZ Database)<br>
@@ -104,11 +82,48 @@ auto restoredTime = SysTime.fromISOExtString(timeString);
List of Time Zones)<br>
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: Jonathan M Davis and Kato Shoichi
- Source: $(PHOBOSSRC std/_datetime/package.d)
+ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) and Kato Shoichi
+ Source: $(PHOBOSSRC std/datetime/package.d)
+/
module std.datetime;
+/// Get the current time from the system clock
+@safe unittest
+{
+ import std.datetime.systime : SysTime, Clock;
+
+ SysTime currentTime = Clock.currTime();
+}
+
+/**
+Construct a specific point in time without timezone information
+and get its ISO string.
+ */
+@safe unittest
+{
+ import std.datetime.date : DateTime;
+
+ auto dt = DateTime(2018, 1, 1, 12, 30, 10);
+ assert(dt.toISOString() == "20180101T123010");
+ assert(dt.toISOExtString() == "2018-01-01T12:30:10");
+}
+
+/**
+Construct a specific point in time in the UTC timezone and
+add two days.
+ */
+@safe unittest
+{
+ import std.datetime.systime : SysTime;
+ import std.datetime.timezone : UTC;
+ import core.time : days;
+
+ auto st = SysTime(DateTime(2018, 1, 1, 12, 30, 10), UTC());
+ assert(st.toISOExtString() == "2018-01-01T12:30:10Z");
+ st += 2.days;
+ assert(st.toISOExtString() == "2018-01-03T12:30:10Z");
+}
+
public import core.time;
public import std.datetime.date;
public import std.datetime.interval;
@@ -142,592 +157,9 @@ import std.typecons : Flag, Yes, No;
@safe unittest
{
import std.traits : hasUnsharedAliasing;
- /* Issue 6642 */
+ /* https://issues.dlang.org/show_bug.cgi?id=6642 */
static assert(!hasUnsharedAliasing!Date);
static assert(!hasUnsharedAliasing!TimeOfDay);
static assert(!hasUnsharedAliasing!DateTime);
static assert(!hasUnsharedAliasing!SysTime);
}
-
-
-//==============================================================================
-// Everything after here will be deprecated after we have replacements which
-// use MonoTime and Duration.
-//==============================================================================
-
-
-/++
- Used by StopWatch to indicate whether it should start immediately upon
- construction.
-
- If set to $(D AutoStart.no), then the stopwatch is not started when it is
- constructed.
-
- Otherwise, if set to $(D AutoStart.yes), then the stopwatch is started when
- it is constructed.
- +/
-alias AutoStart = Flag!"autoStart";
-
-
-/++
- $(RED This will be deprecated in 2.076. Please use
- $(REF StopWatch,std,datetime,stopwatch) instead. It uses
- $(REF Monotime,core,time) and $(REF Duration,core,time) rather
- than $(REF TickDuration,core,time), which will also be deprecated in
- 2.076.)
-
- $(D StopWatch) measures time as precisely as possible.
-
- This class uses a high-performance counter. On Windows systems, it uses
- $(D QueryPerformanceCounter), and on Posix systems, it uses
- $(D clock_gettime) if available, and $(D gettimeofday) otherwise.
-
- But the precision of $(D StopWatch) differs from system to system. It is
- impossible to for it to be the same from system to system since the precision
- of the system clock varies from system to system, and other system-dependent
- and situation-dependent stuff (such as the overhead of a context switch
- between threads) can also affect $(D StopWatch)'s accuracy.
- +/
-@safe struct StopWatch
-{
-public:
-
- /++
- Auto start with constructor.
- +/
- this(AutoStart autostart) @nogc
- {
- if (autostart)
- start();
- }
-
- @nogc @safe unittest
- {
- auto sw = StopWatch(Yes.autoStart);
- sw.stop();
- }
-
-
- ///
- bool opEquals(const StopWatch rhs) const pure nothrow @nogc
- {
- return opEquals(rhs);
- }
-
- /// ditto
- bool opEquals(const ref StopWatch rhs) const pure nothrow @nogc
- {
- return _timeStart == rhs._timeStart &&
- _timeMeasured == rhs._timeMeasured;
- }
-
-
- /++
- Resets the stop watch.
- +/
- void reset() @nogc
- {
- if (_flagStarted)
- {
- // Set current system time if StopWatch is measuring.
- _timeStart = TickDuration.currSystemTick;
- }
- else
- {
- // Set zero if StopWatch is not measuring.
- _timeStart.length = 0;
- }
-
- _timeMeasured.length = 0;
- }
-
- ///
- @nogc @safe unittest
- {
- StopWatch sw;
- sw.start();
- sw.stop();
- sw.reset();
- assert(sw.peek().to!("seconds", real)() == 0);
- }
-
-
- /++
- Starts the stop watch.
- +/
- void start() @nogc
- {
- assert(!_flagStarted);
- _flagStarted = true;
- _timeStart = TickDuration.currSystemTick;
- }
-
- @nogc @system unittest
- {
- StopWatch sw;
- sw.start();
- auto t1 = sw.peek();
- bool doublestart = true;
- try
- sw.start();
- catch (AssertError e)
- doublestart = false;
- assert(!doublestart);
- sw.stop();
- assert((t1 - sw.peek()).to!("seconds", real)() <= 0);
- }
-
-
- /++
- Stops the stop watch.
- +/
- void stop() @nogc
- {
- assert(_flagStarted);
- _flagStarted = false;
- _timeMeasured += TickDuration.currSystemTick - _timeStart;
- }
-
- @nogc @system unittest
- {
- StopWatch sw;
- sw.start();
- sw.stop();
- auto t1 = sw.peek();
- bool doublestop = true;
- try
- sw.stop();
- catch (AssertError e)
- doublestop = false;
- assert(!doublestop);
- assert((t1 - sw.peek()).to!("seconds", real)() == 0);
- }
-
-
- /++
- Peek at the amount of time which has passed since the stop watch was
- started.
- +/
- TickDuration peek() const @nogc
- {
- if (_flagStarted)
- return TickDuration.currSystemTick - _timeStart + _timeMeasured;
-
- return _timeMeasured;
- }
-
- @nogc @safe unittest
- {
- StopWatch sw;
- sw.start();
- auto t1 = sw.peek();
- sw.stop();
- auto t2 = sw.peek();
- auto t3 = sw.peek();
- assert(t1 <= t2);
- assert(t2 == t3);
- }
-
-
- /++
- Set the amount of time which has been measured since the stop watch was
- started.
- +/
- void setMeasured(TickDuration d) @nogc
- {
- reset();
- _timeMeasured = d;
- }
-
- @nogc @safe unittest
- {
- StopWatch sw;
- TickDuration t0;
- t0.length = 100;
- sw.setMeasured(t0);
- auto t1 = sw.peek();
- assert(t0 == t1);
- }
-
-
- /++
- Confirm whether this stopwatch is measuring time.
- +/
- bool running() @property const pure nothrow @nogc
- {
- return _flagStarted;
- }
-
- @nogc @safe unittest
- {
- StopWatch sw1;
- assert(!sw1.running);
- sw1.start();
- assert(sw1.running);
- sw1.stop();
- assert(!sw1.running);
- StopWatch sw2 = Yes.autoStart;
- assert(sw2.running);
- sw2.stop();
- assert(!sw2.running);
- sw2.start();
- assert(sw2.running);
- }
-
-
-
-
-private:
-
- // true if observing.
- bool _flagStarted = false;
-
- // TickDuration at the time of StopWatch starting measurement.
- TickDuration _timeStart;
-
- // Total time that StopWatch ran.
- TickDuration _timeMeasured;
-}
-
-///
-@safe unittest
-{
- void writeln(S...)(S args){}
- static void bar() {}
-
- StopWatch sw;
- enum n = 100;
- TickDuration[n] times;
- TickDuration last = TickDuration.from!"seconds"(0);
- foreach (i; 0 .. n)
- {
- sw.start(); //start/resume mesuring.
- foreach (unused; 0 .. 1_000_000)
- bar();
- sw.stop(); //stop/pause measuring.
- //Return value of peek() after having stopped are the always same.
- writeln((i + 1) * 1_000_000, " times done, lap time: ",
- sw.peek().msecs, "[ms]");
- times[i] = sw.peek() - last;
- last = sw.peek();
- }
- real sum = 0;
- // To get the number of seconds,
- // use properties of TickDuration.
- // (seconds, msecs, usecs, hnsecs)
- foreach (t; times)
- sum += t.hnsecs;
- writeln("Average time: ", sum/n, " hnsecs");
-}
-
-
-/++
- $(RED This will be deprecated in 2.076. Please use
- $(REF benchmark,std,datetime,stopwatch) instead. It uses
- $(REF Monotime,core,time) and $(REF Duration,core,time) rather
- than $(REF TickDuration,core,time), which will also be deprecated in
- 2.076.)
-
- Benchmarks code for speed assessment and comparison.
-
- Params:
- fun = aliases of callable objects (e.g. function names). Each should
- take no arguments.
- n = The number of times each function is to be executed.
-
- Returns:
- The amount of time (as a $(REF TickDuration, core,time)) that it took to
- call each function $(D n) times. The first value is the length of time
- that it took to call $(D fun[0]) $(D n) times. The second value is the
- length of time it took to call $(D fun[1]) $(D n) times. Etc.
-
- Note that casting the TickDurations to $(REF Duration, core,time)s will make
- the results easier to deal with (and it may change in the future that
- benchmark will return an array of Durations rather than TickDurations).
-
- See_Also:
- $(LREF measureTime)
- +/
-TickDuration[fun.length] benchmark(fun...)(uint n)
-{
- TickDuration[fun.length] result;
- StopWatch sw;
- sw.start();
-
- foreach (i, unused; fun)
- {
- sw.reset();
- foreach (j; 0 .. n)
- fun[i]();
- result[i] = sw.peek();
- }
-
- return result;
-}
-
-///
-@safe unittest
-{
- import std.conv : to;
- int a;
- void f0() {}
- void f1() {auto b = a;}
- void f2() {auto b = to!string(a);}
- auto r = benchmark!(f0, f1, f2)(10_000);
- auto f0Result = to!Duration(r[0]); // time f0 took to run 10,000 times
- auto f1Result = to!Duration(r[1]); // time f1 took to run 10,000 times
- auto f2Result = to!Duration(r[2]); // time f2 took to run 10,000 times
-}
-
-@safe unittest
-{
- int a;
- void f0() {}
- //void f1() {auto b = to!(string)(a);}
- void f2() {auto b = (a);}
- auto r = benchmark!(f0, f2)(100);
-}
-
-
-/++
- Return value of benchmark with two functions comparing.
- +/
-@safe struct ComparingBenchmarkResult
-{
- /++
- Evaluation value
-
- This returns the evaluation value of performance as the ratio of
- baseFunc's time over targetFunc's time. If performance is high, this
- returns a high value.
- +/
- @property real point() const pure nothrow
- {
- return _baseTime.length / cast(const real)_targetTime.length;
- }
-
-
- /++
- The time required of the base function
- +/
- @property public TickDuration baseTime() const pure nothrow
- {
- return _baseTime;
- }
-
-
- /++
- The time required of the target function
- +/
- @property public TickDuration targetTime() const pure nothrow
- {
- return _targetTime;
- }
-
-private:
-
- this(TickDuration baseTime, TickDuration targetTime) pure nothrow
- {
- _baseTime = baseTime;
- _targetTime = targetTime;
- }
-
- TickDuration _baseTime;
- TickDuration _targetTime;
-}
-
-
-/++
- $(RED This will be deprecated in 2.076. Please use
- $(REF benchmark,std,datetime,stopwatch) instead. This function has
- not been ported to $(REF Monotime,core,time) and
- $(REF Duration,core,time), because it is a trivial wrapper around
- benchmark.)
-
- Benchmark with two functions comparing.
-
- Params:
- baseFunc = The function to become the base of the speed.
- targetFunc = The function that wants to measure speed.
- times = The number of times each function is to be executed.
- +/
-ComparingBenchmarkResult comparingBenchmark(alias baseFunc,
- alias targetFunc,
- int times = 0xfff)()
-{
- auto t = benchmark!(baseFunc, targetFunc)(times);
- return ComparingBenchmarkResult(t[0], t[1]);
-}
-
-///
-@safe unittest
-{
- void f1x() {}
- void f2x() {}
- @safe void f1o() {}
- @safe void f2o() {}
- auto b1 = comparingBenchmark!(f1o, f2o, 1)(); // OK
- //writeln(b1.point);
-}
-
-//Bug# 8450
-@system unittest
-{
- @safe void safeFunc() {}
- @trusted void trustFunc() {}
- @system void sysFunc() {}
- auto safeResult = comparingBenchmark!((){safeFunc();}, (){safeFunc();})();
- auto trustResult = comparingBenchmark!((){trustFunc();}, (){trustFunc();})();
- auto sysResult = comparingBenchmark!((){sysFunc();}, (){sysFunc();})();
- auto mixedResult1 = comparingBenchmark!((){safeFunc();}, (){trustFunc();})();
- auto mixedResult2 = comparingBenchmark!((){trustFunc();}, (){sysFunc();})();
- auto mixedResult3 = comparingBenchmark!((){safeFunc();}, (){sysFunc();})();
-}
-
-
-/++
- $(RED This will be deprecated in 2.076. Please use
- $(REF StopWatch,std,datetime,stopwatch) instead. This function has
- not been ported to $(REF Monotime,core,time) and
- $(REF Duration,core,time), because it is a trivial wrapper around
- StopWatch.)
-
- Function for starting to a stop watch time when the function is called
- and stopping it when its return value goes out of scope and is destroyed.
-
- When the value that is returned by this function is destroyed,
- $(D func) will run. $(D func) is a unary function that takes a
- $(REF TickDuration, core,time).
-
- Example:
---------------------
-{
- auto mt = measureTime!((TickDuration a)
- { /+ do something when the scope is exited +/ });
- // do something that needs to be timed
-}
---------------------
-
- which is functionally equivalent to
-
---------------------
-{
- auto sw = StopWatch(Yes.autoStart);
- scope(exit)
- {
- TickDuration a = sw.peek();
- /+ do something when the scope is exited +/
- }
- // do something that needs to be timed
-}
---------------------
-
- See_Also:
- $(LREF benchmark)
-+/
-@safe auto measureTime(alias func)()
-if (isSafe!((){StopWatch sw; unaryFun!func(sw.peek());}))
-{
- struct Result
- {
- private StopWatch _sw = void;
- this(AutoStart as)
- {
- _sw = StopWatch(as);
- }
- ~this()
- {
- unaryFun!(func)(_sw.peek());
- }
- }
- return Result(Yes.autoStart);
-}
-
-auto measureTime(alias func)()
-if (!isSafe!((){StopWatch sw; unaryFun!func(sw.peek());}))
-{
- struct Result
- {
- private StopWatch _sw = void;
- this(AutoStart as)
- {
- _sw = StopWatch(as);
- }
- ~this()
- {
- unaryFun!(func)(_sw.peek());
- }
- }
- return Result(Yes.autoStart);
-}
-
-// Verify Example.
-@safe unittest
-{
- {
- auto mt = measureTime!((TickDuration a)
- { /+ do something when the scope is exited +/ });
- // do something that needs to be timed
- }
-
- {
- auto sw = StopWatch(Yes.autoStart);
- scope(exit)
- {
- TickDuration a = sw.peek();
- /+ do something when the scope is exited +/
- }
- // do something that needs to be timed
- }
-}
-
-@safe unittest
-{
- import std.math : isNaN;
-
- @safe static void func(TickDuration td)
- {
- assert(!td.to!("seconds", real)().isNaN());
- }
-
- auto mt = measureTime!(func)();
-
- /+
- with (measureTime!((a){assert(a.seconds);}))
- {
- // doSomething();
- // @@@BUG@@@ doesn't work yet.
- }
- +/
-}
-
-@safe unittest
-{
- import std.math : isNaN;
-
- static void func(TickDuration td)
- {
- assert(!td.to!("seconds", real)().isNaN());
- }
-
- auto mt = measureTime!(func)();
-
- /+
- with (measureTime!((a){assert(a.seconds);}))
- {
- // doSomething();
- // @@@BUG@@@ doesn't work yet.
- }
- +/
-}
-
-//Bug# 8450
-@system unittest
-{
- @safe void safeFunc() {}
- @trusted void trustFunc() {}
- @system void sysFunc() {}
- auto safeResult = measureTime!((a){safeFunc();})();
- auto trustResult = measureTime!((a){trustFunc();})();
- auto sysResult = measureTime!((a){sysFunc();})();
-}
diff --git a/libphobos/src/std/datetime/stopwatch.d b/libphobos/src/std/datetime/stopwatch.d
index 5d2a980afaf..d8ecf7dbfbb 100644
--- a/libphobos/src/std/datetime/stopwatch.d
+++ b/libphobos/src/std/datetime/stopwatch.d
@@ -5,17 +5,46 @@
For convenience, this module publicly imports $(MREF core,time).
+$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Main functionality) $(TD
+ $(LREF StopWatch)
+ $(LREF benchmark)
+))
+$(TR $(TD Flags) $(TD
+ $(LREF AutoStart)
+))
+))
+
$(RED Unlike the other modules in std.datetime, this module is not currently
publicly imported in std.datetime.package, because the old
versions of this functionality which use
$(REF TickDuration,core,time) are in std.datetime.package and would
conflict with the symbols in this module. After the old symbols have
- gone through the deprecation cycle and have been removed, then this
- module will be publicly imported in std.datetime.package.)
+ gone through the deprecation cycle and have been fully removed, then
+ this module will be publicly imported in std.datetime.package. The
+ old, deprecated symbols has been removed from the documentation in
+ December 2019 and currently scheduled to be fully removed from Phobos
+ after 2.094.)
+
+ So, for now, when using std.datetime.stopwatch, if other modules from
+ std.datetime are needed, then either import them individually rather than
+ importing std.datetime, or use selective or static imports to import
+ std.datetime.stopwatch. e.g.
+
+ ----------------------------------------------------------------------------
+ import std.datetime;
+ import std.datetime.stopwatch : benchmark, StopWatch;
+ ----------------------------------------------------------------------------
+
+ The compiler will then know to use the symbols from std.datetime.stopwatch
+ rather than the deprecated ones from std.datetime.package.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: Jonathan M Davis and Kato Shoichi
- Source: $(PHOBOSSRC std/datetime/_stopwatch.d)
+ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis) and Kato Shoichi
+ Source: $(PHOBOSSRC std/datetime/stopwatch.d)
+/
module std.datetime.stopwatch;
@@ -23,75 +52,42 @@ public import core.time;
import std.typecons : Flag;
/++
- Used by StopWatch to indicate whether it should start immediately upon
- construction.
+ Used by StopWatch to indicate whether it should start immediately upon
+ construction.
- If set to $(D AutoStart.no), then the StopWatch is not started when it is
- constructed.
+ If set to `AutoStart.no`, then the StopWatch is not started when it is
+ constructed.
- Otherwise, if set to $(D AutoStart.yes), then the StopWatch is started when
- it is constructed.
+ Otherwise, if set to `AutoStart.yes`, then the StopWatch is started when
+ it is constructed.
+/
alias AutoStart = Flag!"autoStart";
/++
- StopWatch is used to measure time just like one would do with a physical
- stopwatch, including stopping, restarting, and/or resetting it.
-
- $(REF MonoTime,core,time) is used to hold the time, and it uses the system's
- monotonic clock, which is high precision and never counts backwards (unlike
- the wall clock time, which $(I can) count backwards, which is why
- $(REF SysTime,std,datetime,systime) should not be used for timing).
-
- Note that the precision of StopWatch differs from system to system. It is
- impossible for it to be the same for all systems, since the precision of the
- system clock and other system-dependent and situation-dependent factors
- (such as the overhead of a context switch between threads) varies from system
- to system and can affect StopWatch's accuracy.
+ StopWatch is used to measure time just like one would do with a physical
+ stopwatch, including stopping, restarting, and/or resetting it.
+
+ $(REF MonoTime,core,time) is used to hold the time, and it uses the system's
+ monotonic clock, which is high precision and never counts backwards (unlike
+ the wall clock time, which $(I can) count backwards, which is why
+ $(REF SysTime,std,datetime,systime) should not be used for timing).
+
+ Note that the precision of StopWatch differs from system to system. It is
+ impossible for it to be the same for all systems, since the precision of the
+ system clock and other system-dependent and situation-dependent factors
+ (such as the overhead of a context switch between threads) varies from
+ system to system and can affect StopWatch's accuracy.
+/
struct StopWatch
{
public:
- ///
- @system nothrow @nogc unittest
- {
- import core.thread : Thread;
-
- auto sw = StopWatch(AutoStart.yes);
-
- Duration t1 = sw.peek();
- Thread.sleep(usecs(1));
- Duration t2 = sw.peek();
- assert(t2 > t1);
-
- Thread.sleep(usecs(1));
- sw.stop();
-
- Duration t3 = sw.peek();
- assert(t3 > t2);
- Duration t4 = sw.peek();
- assert(t3 == t4);
-
- sw.start();
- Thread.sleep(usecs(1));
-
- Duration t5 = sw.peek();
- assert(t5 > t4);
-
- // If stopping or resetting the StopWatch is not required, then
- // MonoTime can easily be used by itself without StopWatch.
- auto before = MonoTime.currTime;
- // do stuff...
- auto timeElapsed = MonoTime.currTime - before;
- }
-
/++
Constructs a StopWatch. Whether it starts immediately depends on the
$(LREF AutoStart) argument.
- If $(D StopWatch.init) is used, then the constructed StopWatch isn't
+ If `StopWatch.init` is used, then the constructed StopWatch isn't
running (and can't be, since no constructor ran).
+/
this(AutoStart autostart) @safe nothrow @nogc
@@ -183,7 +179,7 @@ public:
+/
void start() @safe nothrow @nogc
in { assert(!_running, "start was called when the StopWatch was already running."); }
- body
+ do
{
_running = true;
_timeStarted = MonoTime.currTime;
@@ -211,7 +207,7 @@ public:
+/
void stop() @safe nothrow @nogc
in { assert(_running, "stop was called when the StopWatch was not running."); }
- body
+ do
{
_running = false;
_ticksElapsed += MonoTime.currTime.ticks - _timeStarted.ticks;
@@ -244,7 +240,7 @@ public:
does include $(I all) of the time that it was running and not just the
time since it was started last.
- Calling $(LREF reset) will reset this to $(D Duration.zero).
+ Calling $(LREF reset) will reset this to `Duration.zero`.
+/
Duration peek() @safe const nothrow @nogc
{
@@ -349,6 +345,55 @@ private:
long _ticksElapsed; // Total time that the StopWatch ran before it was stopped last.
}
+/// Measure a time in milliseconds, microseconds, or nanoseconds
+@safe nothrow @nogc unittest
+{
+ auto sw = StopWatch(AutoStart.no);
+ sw.start();
+ // ... Insert operations to be timed here ...
+ sw.stop();
+
+ long msecs = sw.peek.total!"msecs";
+ long usecs = sw.peek.total!"usecs";
+ long nsecs = sw.peek.total!"nsecs";
+
+ assert(usecs >= msecs * 1000);
+ assert(nsecs >= usecs * 1000);
+}
+
+///
+@system nothrow @nogc unittest
+{
+ import core.thread : Thread;
+
+ auto sw = StopWatch(AutoStart.yes);
+
+ Duration t1 = sw.peek();
+ Thread.sleep(usecs(1));
+ Duration t2 = sw.peek();
+ assert(t2 > t1);
+
+ Thread.sleep(usecs(1));
+ sw.stop();
+
+ Duration t3 = sw.peek();
+ assert(t3 > t2);
+ Duration t4 = sw.peek();
+ assert(t3 == t4);
+
+ sw.start();
+ Thread.sleep(usecs(1));
+
+ Duration t5 = sw.peek();
+ assert(t5 > t4);
+
+ // If stopping or resetting the StopWatch is not required, then
+ // MonoTime can easily be used by itself without StopWatch.
+ auto before = MonoTime.currTime;
+ // do stuff...
+ auto timeElapsed = MonoTime.currTime - before;
+}
+
/++
Benchmarks code for speed assessment and comparison.
@@ -360,9 +405,9 @@ private:
Returns:
The amount of time (as a $(REF Duration,core,time)) that it took to call
- each function $(D n) times. The first value is the length of time that
- it took to call $(D fun[0]) $(D n) times. The second value is the length
- of time it took to call $(D fun[1]) $(D n) times. Etc.
+ each function `n` times. The first value is the length of time that
+ it took to call `fun[0]` `n` times. The second value is the length
+ of time it took to call `fun[1]` `n` times. Etc.
+/
Duration[fun.length] benchmark(fun...)(uint n)
{
@@ -401,13 +446,22 @@ Duration[fun.length] benchmark(fun...)(uint n)
int a;
void f0() nothrow {}
- void f1() nothrow { auto b = to!string(a); }
+ void f1() nothrow @trusted {
+ // do not allow any optimizer to optimize this function away
+ import core.thread : getpid;
+ import core.stdc.stdio : printf;
+ auto b = getpid.to!string;
+ if (getpid == 1) // never happens, but prevents optimization
+ printf("%p", &b);
+ }
+
+ auto sw = StopWatch(AutoStart.yes);
auto r = benchmark!(f0, f1)(1000);
+ auto total = sw.peek();
assert(r[0] >= Duration.zero);
- assert(r[1] > Duration.zero);
- assert(r[1] > r[0]);
- assert(r[0] < seconds(1));
- assert(r[1] < seconds(1));
+ assert(r[1] >= Duration.zero);
+ assert(r[0] <= total);
+ assert(r[1] <= total);
}
@safe nothrow @nogc unittest
diff --git a/libphobos/src/std/datetime/systime.d b/libphobos/src/std/datetime/systime.d
index 913d360c655..18b8524d7ad 100644
--- a/libphobos/src/std/datetime/systime.d
+++ b/libphobos/src/std/datetime/systime.d
@@ -1,9 +1,34 @@
// Written in the D programming language
/++
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Types) $(TD
+ $(LREF Clock)
+ $(LREF SysTime)
+ $(LREF DosFileTime)
+))
+$(TR $(TD Conversion) $(TD
+ $(LREF parseRFC822DateTime)
+ $(LREF DosFileTimeToSysTime)
+ $(LREF FILETIMEToStdTime)
+ $(LREF FILETIMEToSysTime)
+ $(LREF stdTimeToFILETIME)
+ $(LREF stdTimeToUnixTime)
+ $(LREF SYSTEMTIMEToSysTime)
+ $(LREF SysTimeToDosFileTime)
+ $(LREF SysTimeToFILETIME)
+ $(LREF SysTimeToSYSTEMTIME)
+ $(LREF unixTimeToStdTime)
+))
+))
+
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: Jonathan M Davis
- Source: $(PHOBOSSRC std/datetime/_systime.d)
+ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis)
+ Source: $(PHOBOSSRC std/datetime/systime.d)
+/
module std.datetime.systime;
@@ -16,18 +41,58 @@ else version (TVOS)
else version (WatchOS)
version = Darwin;
-import core.time;
-import std.datetime.date;
-import std.datetime.timezone;
+/// Get the current time as a $(LREF SysTime)
+@safe unittest
+{
+ import std.datetime.timezone : LocalTime;
+ SysTime today = Clock.currTime();
+ assert(today.timezone is LocalTime());
+}
+
+/// Construct a $(LREF SysTime) from a ISO time string
+@safe unittest
+{
+ import std.datetime.date : DateTime;
+ import std.datetime.timezone : UTC;
+
+ auto st = SysTime.fromISOExtString("2018-01-01T10:30:00Z");
+ assert(st == SysTime(DateTime(2018, 1, 1, 10, 30, 0), UTC()));
+}
+
+/// Make a specific point in time in the New York timezone
+@safe unittest
+{
+ import core.time : hours;
+ import std.datetime.date : DateTime;
+ import std.datetime.timezone : SimpleTimeZone;
+
+ auto ny = SysTime(
+ DateTime(2018, 1, 1, 10, 30, 0),
+ new immutable SimpleTimeZone(-5.hours, "America/New_York")
+ );
+
+ // ISO standard time strings
+ assert(ny.toISOString() == "20180101T103000-05:00");
+ assert(ny.toISOExtString() == "2018-01-01T10:30:00-05:00");
+}
+
+// Note: reconsider using specific imports below after
+// https://issues.dlang.org/show_bug.cgi?id=17630 has been fixed
+import core.time;// : ClockType, convert, dur, Duration, seconds, TimeException;
+import std.datetime.date;// : _monthNames, AllowDayOverflow, CmpTimeUnits, Date,
+ //DateTime, DateTimeException, DayOfWeek, enforceValid, getDayOfWeek, maxDay,
+ //Month, splitUnitsFromHNSecs, TimeOfDay, validTimeUnits, yearIsLeapYear;
+import std.datetime.timezone;// : LocalTime, SimpleTimeZone, TimeZone, UTC;
import std.exception : enforce;
import std.format : format;
import std.range.primitives;
-import std.traits : isIntegral, isSigned, isSomeString, Unqual;
+import std.traits : isIntegral, isSigned, isSomeString, isNarrowString;
version (Windows)
{
import core.stdc.time : time_t;
- import core.sys.windows.windows;
+ import core.sys.windows.winbase;
+ import core.sys.windows.winnt;
import core.sys.windows.winsock2;
}
else version (Posix)
@@ -36,7 +101,7 @@ else version (Posix)
import core.sys.posix.sys.types : time_t;
}
-version (unittest)
+version (StdUnittest)
{
import core.exception : AssertError;
import std.exception : assertThrown;
@@ -88,7 +153,7 @@ public:
@safe unittest
{
import std.format : format;
- import std.stdio : writefln;
+ import core.time;
assert(currTime().timezone is LocalTime());
assert(currTime(UTC()).timezone is UTC());
@@ -111,9 +176,8 @@ public:
assert(abs(norm1 - norm2) <= seconds(2));
import std.meta : AliasSeq;
- foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second))
- {
- scope(failure) writefln("ClockType.%s", ct);
+ static foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second))
+ {{
static if (clockSupported(ct))
{
auto value1 = Clock.currTime!ct;
@@ -121,7 +185,7 @@ public:
assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct));
assert(abs(value1 - value2) <= seconds(2), format("ClockType.%s", ct));
}
- }
+ }}
}
@@ -334,9 +398,8 @@ public:
@safe unittest
{
import std.format : format;
- import std.math : abs;
+ import std.math.algebraic : abs;
import std.meta : AliasSeq;
- import std.stdio : writefln;
enum limit = convert!("seconds", "hnsecs")(2);
auto norm1 = Clock.currStdTime;
@@ -344,9 +407,8 @@ public:
assert(norm1 <= norm2, format("%s %s", norm1, norm2));
assert(abs(norm1 - norm2) <= limit);
- foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second))
- {
- scope(failure) writefln("ClockType.%s", ct);
+ static foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second))
+ {{
static if (clockSupported(ct))
{
auto value1 = Clock.currStdTime!ct;
@@ -354,21 +416,29 @@ public:
assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct));
assert(abs(value1 - value2) <= limit);
}
- }
+ }}
}
private:
- @disable this() {}
+ @disable this();
+}
+
+/// Get the current time as a $(LREF SysTime)
+@safe unittest
+{
+ import std.datetime.timezone : LocalTime;
+ SysTime today = Clock.currTime();
+ assert(today.timezone is LocalTime());
}
/++
- $(D SysTime) is the type used to get the current time from the
+ `SysTime` is the type used to get the current time from the
system or doing anything that involves time zones. Unlike
$(REF DateTime,std,datetime,date), the time zone is an integral part of
- $(D SysTime) (though for local time applications, time zones can be ignored
+ `SysTime` (though for local time applications, time zones can be ignored
and it will work, since it defaults to using the local time zone). It holds
its internal time in std time (hnsecs since midnight, January 1st, 1 A.D.
UTC), so it interfaces well with the system time. However, that means that,
@@ -376,39 +446,41 @@ private:
calendar-based operations, and getting individual units from it such as
years or days is going to involve conversions and be less efficient.
+ An $(I hnsec) (hecto-nanosecond) is 100 nanoseconds. There are 10,000,000 hnsecs in a second.
+
For calendar-based operations that don't
care about time zones, then $(REF DateTime,std,datetime,date) would be
- the type to use. For system time, use $(D SysTime).
+ the type to use. For system time, use `SysTime`.
- $(LREF Clock.currTime) will return the current time as a $(D SysTime).
- To convert a $(D SysTime) to a $(REF Date,std,datetime,date) or
+ $(LREF Clock.currTime) will return the current time as a `SysTime`.
+ To convert a `SysTime` to a $(REF Date,std,datetime,date) or
$(REF DateTime,std,datetime,date), simply cast it. To convert a
$(REF Date,std,datetime,date) or $(REF DateTime,std,datetime,date) to a
- $(D SysTime), use $(D SysTime)'s constructor, and pass in the ntended time
+ `SysTime`, use `SysTime`'s constructor, and pass in the intended time
zone with it (or don't pass in a $(REF TimeZone,std,datetime,timezone), and
the local time zone will be used). Be aware, however, that converting from a
- $(REF DateTime,std,datetime,date) to a $(D SysTime) will not necessarily
+ $(REF DateTime,std,datetime,date) to a `SysTime` will not necessarily
be 100% accurate due to DST (one hour of the year doesn't exist and another
occurs twice). To not risk any conversion errors, keep times as
- $(D SysTime)s. Aside from DST though, there shouldn't be any conversion
+ `SysTime`s. Aside from DST though, there shouldn't be any conversion
problems.
For using time zones other than local time or UTC, use
$(REF PosixTimeZone,std,datetime,timezone) on Posix systems (or on Windows,
if providing the TZ Database files), and use
$(REF WindowsTimeZone,std,datetime,timezone) on Windows systems. The time in
- $(D SysTime) is kept internally in hnsecs from midnight, January 1st, 1 A.D.
+ `SysTime` is kept internally in hnsecs from midnight, January 1st, 1 A.D.
UTC. Conversion error cannot happen when changing the time zone of a
- $(D SysTime). $(REF LocalTime,std,datetime,timezone) is the
+ `SysTime`. $(REF LocalTime,std,datetime,timezone) is the
$(REF TimeZone,std,datetime,timezone) class which represents the local time,
- and $(D UTC) is the $(REF TimeZone,std,datetime,timezone) class which
- represents UTC. $(D SysTime) uses $(REF LocalTime,std,datetime,timezone) if
+ and `UTC` is the $(REF TimeZone,std,datetime,timezone) class which
+ represents UTC. `SysTime` uses $(REF LocalTime,std,datetime,timezone) if
no $(REF TimeZone,std,datetime,timezone) is provided. For more details on
time zones, see the documentation for $(REF TimeZone,std,datetime,timezone),
$(REF PosixTimeZone,std,datetime,timezone), and
$(REF WindowsTimeZone,std,datetime,timezone).
- $(D SysTime)'s range is from approximately 29,000 B.C. to approximately
+ `SysTime`'s range is from approximately 29,000 B.C. to approximately
29,000 A.D.
+/
struct SysTime
@@ -431,7 +503,7 @@ public:
given $(REF DateTime,std,datetime,date) is assumed to
be in the given time zone.
+/
- this(in DateTime dateTime, immutable TimeZone tz = null) @safe nothrow
+ this(DateTime dateTime, immutable TimeZone tz = null) @safe nothrow
{
try
this(dateTime, Duration.zero, tz);
@@ -458,6 +530,11 @@ public:
test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(-60)), 36_000_000_000L);
test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(Duration.zero), 0);
test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(60)), -36_000_000_000L);
+
+ static void testScope(scope ref DateTime dt) @safe
+ {
+ auto st = SysTime(dt);
+ }
}
/++
@@ -474,10 +551,10 @@ public:
be in the given time zone.
Throws:
- $(REF DateTimeException,std,datetime,date) if $(D fracSecs) is negative or if it's
+ $(REF DateTimeException,std,datetime,date) if `fracSecs` is negative or if it's
greater than or equal to one second.
+/
- this(in DateTime dateTime, in Duration fracSecs, immutable TimeZone tz = null) @safe
+ this(DateTime dateTime, Duration fracSecs, immutable TimeZone tz = null) @safe
{
enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds."));
enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second."));
@@ -494,6 +571,7 @@ public:
@safe unittest
{
+ import core.time;
static void test(DateTime dt, Duration fracSecs, immutable TimeZone tz, long expected)
{
auto sysTime = SysTime(dt, fracSecs, tz);
@@ -514,6 +592,11 @@ public:
assertThrown!DateTimeException(SysTime(DateTime.init, hnsecs(-1), UTC()));
assertThrown!DateTimeException(SysTime(DateTime.init, seconds(1), UTC()));
+
+ static void testScope(scope ref DateTime dt, scope ref Duration d) @safe
+ {
+ auto st = SysTime(dt, d);
+ }
}
/++
@@ -528,7 +611,7 @@ public:
given $(REF Date,std,datetime,date) is assumed to be in the
given time zone.
+/
- this(in Date date, immutable TimeZone tz = null) @safe nothrow
+ this(Date date, immutable TimeZone tz = null) @safe nothrow
{
_timezone = tz is null ? LocalTime() : tz;
@@ -556,6 +639,11 @@ public:
test(Date(1, 1, 1), UTC(), 0);
test(Date(1, 1, 2), UTC(), 864000000000);
test(Date(0, 12, 31), UTC(), -864000000000);
+
+ static void testScope(scope ref Date d) @safe
+ {
+ auto st = SysTime(d);
+ }
}
/++
@@ -598,28 +686,37 @@ public:
}
}
+
/++
Params:
rhs = The $(LREF SysTime) to assign to this one.
+
+ Returns: The `this` of this `SysTime`.
+/
- ref SysTime opAssign(const ref SysTime rhs) return @safe pure nothrow
+ ref SysTime opAssign()(auto ref const(SysTime) rhs) return @safe pure nothrow scope
{
_stdTime = rhs._stdTime;
_timezone = rhs._timezone;
return this;
}
- /++
- Params:
- rhs = The $(LREF SysTime) to assign to this one.
- +/
- ref SysTime opAssign(SysTime rhs) scope return @safe pure nothrow
+ @safe unittest
{
- _stdTime = rhs._stdTime;
- _timezone = rhs._timezone;
- return this;
+ SysTime st;
+ st = SysTime(DateTime(2012, 12, 21, 1, 2, 3), UTC());
+ assert(st == SysTime(DateTime(2012, 12, 21, 1, 2, 3), UTC()));
+
+ const other = SysTime(DateTime(19, 1, 7, 13, 14, 15), LocalTime());
+ st = other;
+ assert(st == other);
+
+ static void testScope(scope ref SysTime left, const scope SysTime right) @safe
+ {
+ left = right;
+ }
}
+
/++
Checks for equality between this $(LREF SysTime) and the given
$(LREF SysTime).
@@ -627,13 +724,7 @@ public:
Note that the time zone is ignored. Only the internal
std times (which are in UTC) are compared.
+/
- bool opEquals(const SysTime rhs) @safe const pure nothrow
- {
- return opEquals(rhs);
- }
-
- /// ditto
- bool opEquals(const ref SysTime rhs) @safe const pure nothrow
+ bool opEquals()(auto ref const(SysTime) rhs) @safe const pure nothrow scope
{
return _stdTime == rhs._stdTime;
}
@@ -669,18 +760,25 @@ public:
auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
assert(st == st);
assert(st == cst);
- //assert(st == ist);
+ assert(st == ist);
assert(cst == st);
assert(cst == cst);
- //assert(cst == ist);
- //assert(ist == st);
- //assert(ist == cst);
- //assert(ist == ist);
+ assert(cst == ist);
+ assert(ist == st);
+ assert(ist == cst);
+ assert(ist == ist);
+
+ static void testScope(scope ref SysTime left, const scope SysTime right) @safe
+ {
+ assert(left == right);
+ assert(right == left);
+ }
}
+
/++
Compares this $(LREF SysTime) with the given $(LREF SysTime).
@@ -693,7 +791,7 @@ public:
$(TR $(TD this &gt; rhs) $(TD &gt; 0))
)
+/
- int opCmp(in SysTime rhs) @safe const pure nothrow
+ int opCmp()(auto ref const(SysTime) rhs) @safe const pure nothrow scope
{
if (_stdTime < rhs._stdTime)
return -1;
@@ -757,22 +855,29 @@ public:
auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
assert(st.opCmp(st) == 0);
assert(st.opCmp(cst) == 0);
- //assert(st.opCmp(ist) == 0);
+ assert(st.opCmp(ist) == 0);
assert(cst.opCmp(st) == 0);
assert(cst.opCmp(cst) == 0);
- //assert(cst.opCmp(ist) == 0);
- //assert(ist.opCmp(st) == 0);
- //assert(ist.opCmp(cst) == 0);
- //assert(ist.opCmp(ist) == 0);
+ assert(cst.opCmp(ist) == 0);
+ assert(ist.opCmp(st) == 0);
+ assert(ist.opCmp(cst) == 0);
+ assert(ist.opCmp(ist) == 0);
+
+ static void testScope(scope ref SysTime left, const scope SysTime right) @safe
+ {
+ assert(left < right);
+ assert(right > left);
+ }
}
- /**
- * Returns: A hash of the $(LREF SysTime)
- */
- size_t toHash() const @nogc pure nothrow @safe
+
+ /++
+ Returns: A hash of the $(LREF SysTime).
+ +/
+ size_t toHash() const @nogc pure nothrow @safe scope
{
static if (is(size_t == ulong))
return _stdTime;
@@ -809,13 +914,19 @@ public:
immutable zone = new SimpleTimeZone(dur!"minutes"(60));
assert(SysTime(DateTime(2000, 1, 1, 1), zone).toHash == SysTime(DateTime(2000, 1, 1), UTC()).toHash);
assert(SysTime(DateTime(2000, 1, 1), zone).toHash != SysTime(DateTime(2000, 1, 1), UTC()).toHash);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toHash();
+ }
}
+
/++
Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
are B.C.
+/
- @property short year() @safe const nothrow
+ @property short year() @safe const nothrow scope
{
return (cast(Date) this).year;
}
@@ -849,9 +960,14 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.year == 1999);
- //assert(ist.year == 1999);
+ assert(ist.year == 1999);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.year;
+ }
}
/++
@@ -865,7 +981,7 @@ public:
$(REF DateTimeException,std,datetime,date) if the new year is not
a leap year and the resulting date would be on February 29th.
+/
- @property void year(int year) @safe
+ @property void year(int year) @safe scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
@@ -897,7 +1013,7 @@ public:
{
import std.range : chain;
- static void test(SysTime st, int year, in SysTime expected)
+ static void test(SysTime st, int year, SysTime expected)
{
st.year = year;
assert(st == expected);
@@ -937,18 +1053,23 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.year = 7));
- //static assert(!__traits(compiles, ist.year = 7));
+ static assert(!__traits(compiles, ist.year = 7));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.year = 42;
+ }
}
/++
Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
Throws:
- $(REF DateTimeException,std,datetime,date) if $(D isAD) is true.
+ $(REF DateTimeException,std,datetime,date) if `isAD` is true.
+/
- @property ushort yearBC() @safe const
+ @property ushort yearBC() @safe const scope
{
return (cast(Date) this).yearBC;
}
@@ -978,11 +1099,16 @@ public:
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
st.year = 12;
assert(st.year == 12);
static assert(!__traits(compiles, cst.year = 12));
- //static assert(!__traits(compiles, ist.year = 12));
+ static assert(!__traits(compiles, ist.year = 12));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.yearBC;
+ }
}
@@ -996,7 +1122,7 @@ public:
$(REF DateTimeException,std,datetime,date) if a non-positive value
is given.
+/
- @property void yearBC(int year) @safe
+ @property void yearBC(int year) @safe scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
@@ -1027,7 +1153,7 @@ public:
@safe unittest
{
import std.range : chain;
- static void test(SysTime st, int year, in SysTime expected)
+ static void test(SysTime st, int year, SysTime expected)
{
st.yearBC = year;
assert(st == expected, format("SysTime: %s", st));
@@ -1074,17 +1200,23 @@ public:
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
st.yearBC = 12;
assert(st.yearBC == 12);
static assert(!__traits(compiles, cst.yearBC = 12));
- //static assert(!__traits(compiles, ist.yearBC = 12));
+ static assert(!__traits(compiles, ist.yearBC = 12));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.yearBC = 42;
+ }
}
+
/++
Month of a Gregorian Year.
+/
- @property Month month() @safe const nothrow
+ @property Month month() @safe const nothrow scope
{
return (cast(Date) this).month;
}
@@ -1129,9 +1261,14 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.month == 7);
- //assert(ist.month == 7);
+ assert(ist.month == 7);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.month;
+ }
}
@@ -1145,7 +1282,7 @@ public:
$(REF DateTimeException,std,datetime,date) if the given month is
not a valid month.
+/
- @property void month(Month month) @safe
+ @property void month(Month month) @safe scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
@@ -1168,7 +1305,7 @@ public:
import std.algorithm.iteration : filter;
import std.range : chain;
- static void test(SysTime st, Month month, in SysTime expected)
+ static void test(SysTime st, Month month, SysTime expected)
{
st.month = cast(Month) month;
assert(st == expected);
@@ -1237,15 +1374,20 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- static assert(!__traits(compiles, cst.month = 12));
- //static assert(!__traits(compiles, ist.month = 12));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ static assert(!__traits(compiles, cst.month = Month.dec));
+ static assert(!__traits(compiles, ist.month = Month.dec));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.month = Month.dec;
+ }
}
/++
Day of a Gregorian Month.
+/
- @property ubyte day() @safe const nothrow
+ @property ubyte day() @safe const nothrow scope
{
return (cast(Date) this).day;
}
@@ -1291,9 +1433,14 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.day == 6);
- //assert(ist.day == 6);
+ assert(ist.day == 6);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.day;
+ }
}
@@ -1307,7 +1454,7 @@ public:
$(REF DateTimeException,std,datetime,date) if the given day is not
a valid day of the current month.
+/
- @property void day(int day) @safe
+ @property void day(int day) @safe scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
@@ -1391,16 +1538,21 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.day = 27));
- //static assert(!__traits(compiles, ist.day = 27));
+ static assert(!__traits(compiles, ist.day = 27));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.day = 12;
+ }
}
/++
Hours past midnight.
+/
- @property ubyte hour() @safe const nothrow
+ @property ubyte hour() @safe const nothrow scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
@@ -1450,9 +1602,14 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.hour == 12);
- //assert(ist.hour == 12);
+ assert(ist.hour == 12);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.hour;
+ }
}
@@ -1466,7 +1623,7 @@ public:
$(REF DateTimeException,std,datetime,date) if the given hour are
not a valid hour of the day.
+/
- @property void hour(int hour) @safe
+ @property void hour(int hour) @safe scope
{
enforceValid!"hours"(hour);
@@ -1509,16 +1666,21 @@ public:
assertThrown!DateTimeException(st.hour = 60);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.hour = 27));
- //static assert(!__traits(compiles, ist.hour = 27));
+ static assert(!__traits(compiles, ist.hour = 27));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.hour = 12;
+ }
}
/++
Minutes past the current hour.
+/
- @property ubyte minute() @safe const nothrow
+ @property ubyte minute() @safe const nothrow scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
@@ -1570,9 +1732,14 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.minute == 30);
- //assert(ist.minute == 30);
+ assert(ist.minute == 30);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.minute;
+ }
}
@@ -1586,7 +1753,7 @@ public:
$(REF DateTimeException,std,datetime,date) if the given minute are
not a valid minute of an hour.
+/
- @property void minute(int minute) @safe
+ @property void minute(int minute) @safe scope
{
enforceValid!"minutes"(minute);
@@ -1632,16 +1799,21 @@ public:
assertThrown!DateTimeException(st.minute = 60);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.minute = 27));
- //static assert(!__traits(compiles, ist.minute = 27));
+ static assert(!__traits(compiles, ist.minute = 27));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.minute = 12;
+ }
}
/++
Seconds past the current minute.
+/
- @property ubyte second() @safe const nothrow
+ @property ubyte second() @safe const nothrow scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
@@ -1694,9 +1866,14 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.second == 33);
- //assert(ist.second == 33);
+ assert(ist.second == 33);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.second;
+ }
}
@@ -1710,7 +1887,7 @@ public:
$(REF DateTimeException,std,datetime,date) if the given second are
not a valid second of a minute.
+/
- @property void second(int second) @safe
+ @property void second(int second) @safe scope
{
enforceValid!"seconds"(second);
@@ -1758,9 +1935,14 @@ public:
assertThrown!DateTimeException(st.second = 60);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.seconds = 27));
- //static assert(!__traits(compiles, ist.seconds = 27));
+ static assert(!__traits(compiles, ist.seconds = 27));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.second = 12;
+ }
}
@@ -1768,7 +1950,7 @@ public:
Fractional seconds past the second (i.e. the portion of a
$(LREF SysTime) which is less than a second).
+/
- @property Duration fracSecs() @safe const nothrow
+ @property Duration fracSecs() @safe const nothrow scope
{
auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime);
@@ -1797,6 +1979,7 @@ public:
@safe unittest
{
import std.range : chain;
+ import core.time;
assert(SysTime(0, UTC()).fracSecs == Duration.zero);
assert(SysTime(1, UTC()).fracSecs == hnsecs(1));
@@ -1825,9 +2008,14 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.fracSecs == Duration.zero);
- //assert(ist.fracSecs == Duration.zero);
+ assert(ist.fracSecs == Duration.zero);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.fracSecs;
+ }
}
@@ -1843,7 +2031,7 @@ public:
$(REF DateTimeException,std,datetime,date) if the given duration
is negative or if it's greater than or equal to one second.
+/
- @property void fracSecs(Duration fracSecs) @safe
+ @property void fracSecs(Duration fracSecs) @safe scope
{
enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds."));
enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second."));
@@ -1890,6 +2078,7 @@ public:
@safe unittest
{
import std.range : chain;
+ import core.time;
foreach (fracSec; testFracSecs)
{
@@ -1907,9 +2096,14 @@ public:
assertThrown!DateTimeException(st.fracSecs = seconds(1));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.fracSecs = msecs(7)));
- //static assert(!__traits(compiles, ist.fracSecs = msecs(7)));
+ static assert(!__traits(compiles, ist.fracSecs = msecs(7)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.fracSecs = Duration.zero;
+ }
}
@@ -1917,13 +2111,14 @@ public:
The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the
internal representation of $(LREF SysTime).
+/
- @property long stdTime() @safe const pure nothrow
+ @property long stdTime() @safe const pure nothrow scope @nogc
{
return _stdTime;
}
@safe unittest
{
+ import core.time;
assert(SysTime(0).stdTime == 0);
assert(SysTime(1).stdTime == 1);
assert(SysTime(-1).stdTime == -1);
@@ -1931,9 +2126,14 @@ public:
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime == 621_355_968_000_000_000L);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.stdTime > 0);
- //assert(ist.stdTime > 0);
+ assert(ist.stdTime > 0);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.stdTime;
+ }
}
@@ -1944,14 +2144,15 @@ public:
Params:
stdTime = The number of hnsecs since January 1st, 1 A.D. UTC.
+/
- @property void stdTime(long stdTime) @safe pure nothrow
+ @property void stdTime(long stdTime) @safe pure nothrow scope
{
_stdTime = stdTime;
}
@safe unittest
{
- static void test(long stdTime, in SysTime expected, size_t line = __LINE__)
+ import core.time;
+ static void test(long stdTime, SysTime expected, size_t line = __LINE__)
{
auto st = SysTime(0, UTC());
st.stdTime = stdTime;
@@ -1965,9 +2166,14 @@ public:
test(621_355_968_000_000_000L, SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.stdTime = 27));
- //static assert(!__traits(compiles, ist.stdTime = 27));
+ static assert(!__traits(compiles, ist.stdTime = 27));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.stdTime = 42;
+ }
}
@@ -1978,11 +2184,22 @@ public:
hours - adjust the time to this $(LREF SysTime)'s time zone before
returning.
+/
- @property immutable(TimeZone) timezone() @safe const pure nothrow
+ @property immutable(TimeZone) timezone() @safe const pure nothrow scope
{
return _timezone;
}
+ @safe unittest
+ {
+ assert(SysTime.init.timezone is InitTimeZone());
+ assert(SysTime(DateTime.init, UTC()).timezone is UTC());
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.timezone;
+ }
+ }
+
/++
The current time zone of this $(LREF SysTime). It's internal time is
@@ -1995,7 +2212,7 @@ public:
timezone = The $(REF _TimeZone,std,datetime,_timezone) to set this
$(LREF SysTime)'s time zone to.
+/
- @property void timezone(immutable TimeZone timezone) @safe pure nothrow
+ @property void timezone(immutable TimeZone timezone) @safe pure nothrow scope
{
if (timezone is null)
_timezone = LocalTime();
@@ -2003,14 +2220,40 @@ public:
_timezone = timezone;
}
+ @safe unittest
+ {
+ SysTime st;
+ st.timezone = null;
+ assert(st.timezone is LocalTime());
+ st.timezone = UTC();
+ assert(st.timezone is UTC());
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.timezone = UTC();
+ }
+ }
+
/++
Returns whether DST is in effect for this $(LREF SysTime).
+/
- @property bool dstInEffect() @safe const nothrow
+ @property bool dstInEffect() @safe const nothrow scope
{
return _timezone.dstInEffect(_stdTime);
- // This function's unit testing is done in the time zone classes.
+ }
+
+ // This function's full unit testing is done in the time zone classes, but
+ // this verifies that SysTime.init works correctly, since historically, it
+ // has segfaulted due to a null _timezone.
+ @safe unittest
+ {
+ assert(!SysTime.init.dstInEffect);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.dstInEffect;
+ }
}
@@ -2018,23 +2261,37 @@ public:
Returns what the offset from UTC is for this $(LREF SysTime).
It includes the DST offset in effect at that time (if any).
+/
- @property Duration utcOffset() @safe const nothrow
+ @property Duration utcOffset() @safe const nothrow scope
{
return _timezone.utcOffsetAt(_stdTime);
}
+ // This function's full unit testing is done in the time zone classes, but
+ // this verifies that SysTime.init works correctly, since historically, it
+ // has segfaulted due to a null _timezone.
+ @safe unittest
+ {
+ assert(SysTime.init.utcOffset == Duration.zero);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.utcOffset;
+ }
+ }
+
/++
Returns a $(LREF SysTime) with the same std time as this one, but with
$(REF LocalTime,std,datetime,timezone) as its time zone.
+/
- SysTime toLocalTime() @safe const pure nothrow
+ SysTime toLocalTime() @safe const pure nothrow scope
{
return SysTime(_stdTime, LocalTime());
}
@safe unittest
{
+ import core.time;
{
auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27));
assert(sysTime == sysTime.toLocalTime());
@@ -2053,26 +2310,37 @@ public:
assert(sysTime.toLocalTime().timezone !is UTC());
assert(sysTime.toLocalTime().timezone !is stz);
}
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toLocalTime();
+ }
}
/++
Returns a $(LREF SysTime) with the same std time as this one, but with
- $(D UTC) as its time zone.
+ `UTC` as its time zone.
+/
- SysTime toUTC() @safe const pure nothrow
+ SysTime toUTC() @safe const pure nothrow scope
{
return SysTime(_stdTime, UTC());
}
@safe unittest
{
+ import core.time;
auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27));
assert(sysTime == sysTime.toUTC());
assert(sysTime._stdTime == sysTime.toUTC()._stdTime);
assert(sysTime.toUTC().timezone is UTC());
assert(sysTime.toUTC().timezone !is LocalTime());
assert(sysTime.toUTC().timezone !is sysTime.timezone);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toUTC();
+ }
}
@@ -2080,7 +2348,7 @@ public:
Returns a $(LREF SysTime) with the same std time as this one, but with
given time zone as its time zone.
+/
- SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow
+ SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow scope
{
if (tz is null)
return SysTime(_stdTime, LocalTime());
@@ -2090,6 +2358,7 @@ public:
@safe unittest
{
+ import core.time;
auto stz = new immutable SimpleTimeZone(dur!"minutes"(11 * 60));
auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27));
assert(sysTime == sysTime.toOtherTZ(stz));
@@ -2097,6 +2366,12 @@ public:
assert(sysTime.toOtherTZ(stz).timezone is stz);
assert(sysTime.toOtherTZ(stz).timezone !is LocalTime());
assert(sysTime.toOtherTZ(stz).timezone !is UTC());
+ assert(sysTime.toOtherTZ(null).timezone is LocalTime());
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toOtherTZ(null);
+ }
}
@@ -2116,8 +2391,8 @@ public:
argument to get the desired size.
If the return type is int, and the result can't fit in an int, then the
- closest value that can be held in 32 bits will be used (so $(D int.max)
- if it goes over and $(D int.min) if it goes under). However, no attempt
+ closest value that can be held in 32 bits will be used (so `int.max`
+ if it goes over and `int.min` if it goes under). However, no attempt
is made to deal with integer overflow if the return type is long.
Params:
@@ -2129,7 +2404,7 @@ public:
A signed integer representing the unix time which is equivalent to
this SysTime.
+/
- T toUnixTime(T = time_t)() @safe const pure nothrow
+ T toUnixTime(T = time_t)() @safe const pure nothrow scope
if (is(T == int) || is(T == long))
{
return stdTimeToUnixTime!T(_stdTime);
@@ -2152,13 +2427,19 @@ public:
auto ca = SysTime(DateTime(2007, 12, 22, 8, 14, 45), pst);
assert(ca.toUnixTime() == 1_198_340_085);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toUnixTime();
+ }
}
@safe unittest
{
import std.meta : AliasSeq;
+ import core.time;
assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0);
- foreach (units; AliasSeq!("hnsecs", "usecs", "msecs"))
+ static foreach (units; ["hnsecs", "usecs", "msecs"])
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), dur!units(1), UTC()).toUnixTime() == 0);
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime() == 1);
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toUnixTime() == 0);
@@ -2214,6 +2495,7 @@ public:
@safe unittest
{
+ import core.time;
assert(SysTime.fromUnixTime(0) == SysTime(DateTime(1970, 1, 1), UTC()));
assert(SysTime.fromUnixTime(1) == SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()));
assert(SysTime.fromUnixTime(-1) == SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()));
@@ -2229,17 +2511,17 @@ public:
/++
- Returns a $(D timeval) which represents this $(LREF SysTime).
+ Returns a `timeval` which represents this $(LREF SysTime).
Note that like all conversions in std.datetime, this is a truncating
conversion.
- If $(D timeval.tv_sec) is int, and the result can't fit in an int, then
+ If `timeval.tv_sec` is int, and the result can't fit in an int, then
the closest value that can be held in 32 bits will be used for
- $(D tv_sec). (so $(D int.max) if it goes over and $(D int.min) if it
+ `tv_sec`. (so `int.max` if it goes over and `int.min` if it
goes under).
+/
- timeval toTimeVal() @safe const pure nothrow
+ timeval toTimeVal() @safe const pure nothrow scope
{
immutable tv_sec = toUnixTime!(typeof(timeval.tv_sec))();
immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L);
@@ -2249,6 +2531,7 @@ public:
@safe unittest
{
+ import core.time;
assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeVal() == timeval(0, 0));
assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeVal() == timeval(0, 0));
assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeVal() == timeval(0, 1));
@@ -2267,22 +2550,27 @@ public:
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeVal() == timeval(0, -1000));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeVal() == timeval(-1, -999_983));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toTimeVal();
+ }
}
version (StdDdoc)
{
- private struct timespec {}
+ version (Windows) private struct timespec {}
/++
- Returns a $(D timespec) which represents this $(LREF SysTime).
+ Returns a `timespec` which represents this $(LREF SysTime).
$(BLUE This function is Posix-Only.)
+/
- timespec toTimeSpec() @safe const pure nothrow;
+ timespec toTimeSpec() @safe const pure nothrow scope;
}
else version (Posix)
{
- timespec toTimeSpec() @safe const pure nothrow
+ timespec toTimeSpec() @safe const pure nothrow scope
{
immutable tv_sec = toUnixTime!(typeof(timespec.tv_sec))();
immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L);
@@ -2292,6 +2580,7 @@ public:
@safe unittest
{
+ import core.time;
assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeSpec() == timespec(0, 0));
assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(0, 900));
assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(0, 1000));
@@ -2317,13 +2606,18 @@ public:
timespec(-1, 0));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeSpec() ==
timespec(-1, -999_983_000));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toTimeSpec();
+ }
}
}
/++
- Returns a $(D tm) which represents this $(LREF SysTime).
+ Returns a `tm` which represents this $(LREF SysTime).
+/
- tm toTM() @safe const nothrow
+ tm toTM() @safe const nothrow scope
{
auto dateTime = cast(DateTime) this;
tm timeInfo;
@@ -2342,7 +2636,7 @@ public:
{
import std.utf : toUTFz;
timeInfo.tm_gmtoff = cast(int) convert!("hnsecs", "seconds")(adjTime - _stdTime);
- auto zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName);
+ auto zone = timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName;
timeInfo.tm_zone = zone.toUTFz!(char*)();
}
@@ -2352,11 +2646,13 @@ public:
@system unittest
{
import std.conv : to;
+ import core.time;
version (Posix)
{
- scope(exit) clearTZEnvVar();
+ import std.datetime.timezone : clearTZEnvVar, setTZEnvVar;
setTZEnvVar("America/Los_Angeles");
+ scope(exit) clearTZEnvVar();
}
{
@@ -2406,6 +2702,34 @@ public:
assert(to!string(timeInfo.tm_zone) == "PDT");
}
}
+
+ // This is more to verify that SysTime.init.toTM() doesn't segfault and
+ // does something sane rather than that the value is anything
+ // particularly useful.
+ {
+ auto timeInfo = SysTime.init.toTM();
+
+ assert(timeInfo.tm_sec == 0);
+ assert(timeInfo.tm_min == 0);
+ assert(timeInfo.tm_hour == 0);
+ assert(timeInfo.tm_mday == 1);
+ assert(timeInfo.tm_mon == 0);
+ assert(timeInfo.tm_year == -1899);
+ assert(timeInfo.tm_wday == 1);
+ assert(timeInfo.tm_yday == 0);
+ assert(timeInfo.tm_isdst == 0);
+
+ version (Posix)
+ {
+ assert(timeInfo.tm_gmtoff == 0);
+ assert(to!string(timeInfo.tm_zone) == "SysTime.init's timezone");
+ }
+ }
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toTM();
+ }
}
@@ -2428,7 +2752,7 @@ public:
allowOverflow = Whether the days should be allowed to overflow,
causing the month to increment.
+/
- ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow
+ ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope
if (units == "years" || units == "months")
{
auto hnsecs = adjTime;
@@ -2479,6 +2803,7 @@ public:
// Test add!"years"() with AllowDayOverflow.yes
@safe unittest
{
+ import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
@@ -2673,14 +2998,20 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.add!"years"(4)));
- //static assert(!__traits(compiles, ist.add!"years"(4)));
+ static assert(!__traits(compiles, ist.add!"years"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.add!"years"(42);
+ }
}
// Test add!"years"() with AllowDayOverflow.no
@safe unittest
{
+ import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
@@ -2886,6 +3217,7 @@ public:
// Test add!"months"() with AllowDayOverflow.yes
@safe unittest
{
+ import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
@@ -3224,14 +3556,20 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.add!"months"(4)));
- //static assert(!__traits(compiles, ist.add!"months"(4)));
+ static assert(!__traits(compiles, ist.add!"months"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.add!"months"(42);
+ }
}
// Test add!"months"() with AllowDayOverflow.no
@safe unittest
{
+ import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
@@ -3590,8 +3928,9 @@ public:
allowOverflow = Whether the days should be allowed to overflow,
causing the month to increment.
+/
- ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow
- if (units == "years")
+ ref SysTime roll(string units)
+ (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope
+ if (units == "years")
{
return add!"years"(value, allowOverflow);
}
@@ -3630,16 +3969,22 @@ public:
{
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
st.roll!"years"(4);
static assert(!__traits(compiles, cst.roll!"years"(4)));
- //static assert(!__traits(compiles, ist.roll!"years"(4)));
+ static assert(!__traits(compiles, ist.roll!"years"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.roll!"years"(42);
+ }
}
// Shares documentation with "years" overload.
- ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow
- if (units == "months")
+ ref SysTime roll(string units)
+ (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope
+ if (units == "months")
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
@@ -3668,6 +4013,7 @@ public:
// Test roll!"months"() with AllowDayOverflow.yes
@safe unittest
{
+ import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
@@ -4038,14 +4384,20 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.roll!"months"(4)));
- //static assert(!__traits(compiles, ist.roll!"months"(4)));
+ static assert(!__traits(compiles, ist.roll!"months"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.roll!"months"(42);
+ }
}
// Test roll!"months"() with AllowDayOverflow.no
@safe unittest
{
+ import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
@@ -4425,9 +4777,9 @@ public:
affect larger units. For instance, rolling a $(LREF SysTime) one
year's worth of days gets the exact same $(LREF SysTime).
- Accepted units are $(D "days"), $(D "minutes"), $(D "hours"),
- $(D "minutes"), $(D "seconds"), $(D "msecs"), $(D "usecs"), and
- $(D "hnsecs").
+ Accepted units are `"days"`, `"minutes"`, `"hours"`,
+ `"minutes"`, `"seconds"`, `"msecs"`, `"usecs"`, and
+ `"hnsecs"`.
Note that when rolling msecs, usecs or hnsecs, they all add up to a
second. So, for example, rolling 1000 msecs is exactly the same as
@@ -4438,7 +4790,7 @@ public:
value = The number of $(D_PARAM units) to add to this
$(LREF SysTime).
+/
- ref SysTime roll(string units)(long value) @safe nothrow
+ ref SysTime roll(string units)(long value) @safe nothrow scope
if (units == "days")
{
auto hnsecs = adjTime;
@@ -4523,6 +4875,7 @@ public:
@safe unittest
{
+ import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 2, 28));
@@ -4790,14 +5143,19 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.roll!"days"(4)));
- //static assert(!__traits(compiles, ist.roll!"days"(4)));
+ static assert(!__traits(compiles, ist.roll!"days"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.roll!"days"(42);
+ }
}
// Shares documentation with "days" version.
- ref SysTime roll(string units)(long value) @safe nothrow
+ ref SysTime roll(string units)(long value) @safe nothrow scope
if (units == "hours" || units == "minutes" || units == "seconds")
{
try
@@ -4841,7 +5199,8 @@ public:
// Test roll!"hours"().
@safe unittest
{
- static void testST(SysTime orig, int hours, in SysTime expected, size_t line = __LINE__)
+ import core.time;
+ static void testST(SysTime orig, int hours, SysTime expected, size_t line = __LINE__) @safe
{
orig.roll!"hours"(hours);
if (orig != expected)
@@ -5050,15 +5409,21 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.roll!"hours"(4)));
- //static assert(!__traits(compiles, ist.roll!"hours"(4)));
+ static assert(!__traits(compiles, ist.roll!"hours"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.roll!"hours"(42);
+ }
}
// Test roll!"minutes"().
@safe unittest
{
- static void testST(SysTime orig, int minutes, in SysTime expected, size_t line = __LINE__)
+ import core.time;
+ static void testST(SysTime orig, int minutes, SysTime expected, size_t line = __LINE__) @safe
{
orig.roll!"minutes"(minutes);
if (orig != expected)
@@ -5260,15 +5625,21 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.roll!"minutes"(4)));
- //static assert(!__traits(compiles, ist.roll!"minutes"(4)));
+ static assert(!__traits(compiles, ist.roll!"minutes"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.roll!"minutes"(42);
+ }
}
// Test roll!"seconds"().
@safe unittest
{
- static void testST(SysTime orig, int seconds, in SysTime expected, size_t line = __LINE__)
+ import core.time;
+ static void testST(SysTime orig, int seconds, SysTime expected, size_t line = __LINE__) @safe
{
orig.roll!"seconds"(seconds);
if (orig != expected)
@@ -5448,14 +5819,19 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.roll!"seconds"(4)));
- //static assert(!__traits(compiles, ist.roll!"seconds"(4)));
+ static assert(!__traits(compiles, ist.roll!"seconds"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.roll!"seconds"(42);
+ }
}
// Shares documentation with "days" version.
- ref SysTime roll(string units)(long value) @safe nothrow
+ ref SysTime roll(string units)(long value) @safe nothrow scope
if (units == "msecs" || units == "usecs" || units == "hnsecs")
{
auto hnsecs = adjTime;
@@ -5485,7 +5861,8 @@ public:
// Test roll!"msecs"().
@safe unittest
{
- static void testST(SysTime orig, int milliseconds, in SysTime expected, size_t line = __LINE__)
+ import core.time;
+ static void testST(SysTime orig, int milliseconds, SysTime expected, size_t line = __LINE__) @safe
{
orig.roll!"msecs"(milliseconds);
if (orig != expected)
@@ -5582,15 +5959,21 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- static assert(!__traits(compiles, cst.addMSecs(4)));
- //static assert(!__traits(compiles, ist.addMSecs(4)));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ static assert(!__traits(compiles, cst.roll!"msecs"(4)));
+ static assert(!__traits(compiles, ist.roll!"msecs"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.roll!"msecs"(42);
+ }
}
// Test roll!"usecs"().
@safe unittest
{
- static void testST(SysTime orig, long microseconds, in SysTime expected, size_t line = __LINE__)
+ import core.time;
+ static void testST(SysTime orig, long microseconds, SysTime expected, size_t line = __LINE__) @safe
{
orig.roll!"usecs"(microseconds);
if (orig != expected)
@@ -5711,15 +6094,21 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.roll!"usecs"(4)));
- //static assert(!__traits(compiles, ist.roll!"usecs"(4)));
+ static assert(!__traits(compiles, ist.roll!"usecs"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.roll!"usecs"(42);
+ }
}
// Test roll!"hnsecs"().
@safe unittest
{
- static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__)
+ import core.time;
+ static void testST(SysTime orig, long hnsecs, SysTime expected, size_t line = __LINE__) @safe
{
orig.roll!"hnsecs"(hnsecs);
if (orig != expected)
@@ -5852,9 +6241,14 @@ public:
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.roll!"hnsecs"(4)));
- //static assert(!__traits(compiles, ist.roll!"hnsecs"(4)));
+ static assert(!__traits(compiles, ist.roll!"hnsecs"(4)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.roll!"hnsecs"(42);
+ }
}
@@ -5874,7 +6268,7 @@ public:
duration = The $(REF Duration, core,time) to add to or subtract from
this $(LREF SysTime).
+/
- SysTime opBinary(string op)(Duration duration) @safe const pure nothrow
+ SysTime opBinary(string op)(Duration duration) @safe const pure nothrow scope
if (op == "+" || op == "-")
{
SysTime retval = SysTime(this._stdTime, this._timezone);
@@ -5904,6 +6298,7 @@ public:
@safe unittest
{
+ import core.time;
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678));
assert(st + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678)));
@@ -5940,7 +6335,7 @@ public:
assert(st - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685)));
assert(st - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671)));
- static void testST(in SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__)
+ static void testST(SysTime orig, long hnsecs, SysTime expected, size_t line = __LINE__) @safe
{
auto result = orig + dur!"hnsecs"(hnsecs);
if (result != expected)
@@ -6064,11 +6459,16 @@ public:
auto duration = dur!"seconds"(12);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45)));
- //assert(ist + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45)));
+ assert(ist + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45)));
assert(cst - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21)));
- //assert(ist - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21)));
+ assert(ist - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21)));
+
+ static void testScope(scope ref SysTime st, scope ref Duration d) @safe
+ {
+ auto result = st + d;
+ }
}
@@ -6088,7 +6488,7 @@ public:
duration = The $(REF Duration, core,time) to add to or subtract from
this $(LREF SysTime).
+/
- ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow
+ ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow scope
if (op == "+" || op == "-")
{
immutable hnsecs = duration.total!"hnsecs";
@@ -6098,6 +6498,7 @@ public:
@safe unittest
{
+ import core.time;
auto before = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(before + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33)));
assert(before + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33)));
@@ -6135,7 +6536,7 @@ public:
assert(before - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7)));
assert(before - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993)));
- static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__)
+ static void testST(SysTime orig, long hnsecs, SysTime expected, size_t line = __LINE__) @safe
{
auto r = orig += dur!"hnsecs"(hnsecs);
if (orig != expected)
@@ -6267,11 +6668,17 @@ public:
auto duration = dur!"seconds"(12);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst += duration));
- //static assert(!__traits(compiles, ist += duration));
+ static assert(!__traits(compiles, ist += duration));
static assert(!__traits(compiles, cst -= duration));
- //static assert(!__traits(compiles, ist -= duration));
+ static assert(!__traits(compiles, ist -= duration));
+
+ static void testScope(scope ref SysTime st, scope ref Duration d) @safe
+ {
+ auto result1 = st += d;
+ auto result2 = st -= d;
+ }
}
@@ -6285,7 +6692,7 @@ public:
$(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration))
)
+/
- Duration opBinary(string op)(in SysTime rhs) @safe const pure nothrow
+ Duration opBinary(string op)(SysTime rhs) @safe const pure nothrow scope
if (op == "-")
{
return dur!"hnsecs"(_stdTime - rhs._stdTime);
@@ -6293,6 +6700,7 @@ public:
@safe unittest
{
+ import core.time;
assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1998, 7, 6, 12, 30, 33)) ==
dur!"seconds"(31_536_000));
assert(SysTime(DateTime(1998, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) ==
@@ -6346,9 +6754,15 @@ public:
dur!"hnsecs"(-1));
version (Posix)
+ {
+ import std.datetime.timezone : PosixTimeZone;
immutable tz = PosixTimeZone.getTimeZone("America/Los_Angeles");
+ }
else version (Windows)
+ {
+ import std.datetime.timezone : WindowsTimeZone;
immutable tz = WindowsTimeZone.getTimeZone("Pacific Standard Time");
+ }
{
auto dt = DateTime(2011, 1, 13, 8, 17, 2);
@@ -6360,18 +6774,23 @@ public:
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(st - st == Duration.zero);
assert(cst - st == Duration.zero);
- //assert(ist - st == Duration.zero);
+ assert(ist - st == Duration.zero);
assert(st - cst == Duration.zero);
assert(cst - cst == Duration.zero);
- //assert(ist - cst == Duration.zero);
+ assert(ist - cst == Duration.zero);
- //assert(st - ist == Duration.zero);
- //assert(cst - ist == Duration.zero);
- //assert(ist - ist == Duration.zero);
+ assert(st - ist == Duration.zero);
+ assert(cst - ist == Duration.zero);
+ assert(ist - ist == Duration.zero);
+
+ static void testScope(scope ref SysTime left, scope ref SysTime right) @safe
+ {
+ auto result = left - right;
+ }
}
@@ -6396,7 +6815,7 @@ public:
Params:
rhs = The $(LREF SysTime) to subtract from this one.
+/
- int diffMonths(in SysTime rhs) @safe const nothrow
+ int diffMonths(scope SysTime rhs) @safe const nothrow scope
{
return (cast(Date) this).diffMonths(cast(Date) rhs);
}
@@ -6404,6 +6823,7 @@ public:
///
@safe unittest
{
+ import core.time;
import std.datetime.date : Date;
assert(SysTime(Date(1999, 2, 1)).diffMonths(
@@ -6421,65 +6841,83 @@ public:
@safe unittest
{
+ import core.time;
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(st.diffMonths(st) == 0);
assert(cst.diffMonths(st) == 0);
- //assert(ist.diffMonths(st) == 0);
+ assert(ist.diffMonths(st) == 0);
assert(st.diffMonths(cst) == 0);
assert(cst.diffMonths(cst) == 0);
- //assert(ist.diffMonths(cst) == 0);
+ assert(ist.diffMonths(cst) == 0);
- //assert(st.diffMonths(ist) == 0);
- //assert(cst.diffMonths(ist) == 0);
- //assert(ist.diffMonths(ist) == 0);
+ assert(st.diffMonths(ist) == 0);
+ assert(cst.diffMonths(ist) == 0);
+ assert(ist.diffMonths(ist) == 0);
+
+ static void testScope(scope ref SysTime left, scope ref SysTime right) @safe
+ {
+ auto result = left.diffMonths(right);
+ }
}
/++
Whether this $(LREF SysTime) is in a leap year.
+/
- @property bool isLeapYear() @safe const nothrow
+ @property bool isLeapYear() @safe const nothrow scope
{
return (cast(Date) this).isLeapYear;
}
@safe unittest
{
+ import core.time;
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(!st.isLeapYear);
assert(!cst.isLeapYear);
- //assert(!ist.isLeapYear);
+ assert(!ist.isLeapYear);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.isLeapYear;
+ }
}
/++
Day of the week this $(LREF SysTime) is on.
+/
- @property DayOfWeek dayOfWeek() @safe const nothrow
+ @property DayOfWeek dayOfWeek() @safe const nothrow scope
{
return getDayOfWeek(dayOfGregorianCal);
}
@safe unittest
{
+ import core.time;
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(st.dayOfWeek == DayOfWeek.tue);
assert(cst.dayOfWeek == DayOfWeek.tue);
- //assert(ist.dayOfWeek == DayOfWeek.tue);
+ assert(ist.dayOfWeek == DayOfWeek.tue);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.dayOfWeek;
+ }
}
/++
Day of the year this $(LREF SysTime) is on.
+/
- @property ushort dayOfYear() @safe const nothrow
+ @property ushort dayOfYear() @safe const nothrow scope
{
return (cast(Date) this).dayOfYear;
}
@@ -6487,6 +6925,7 @@ public:
///
@safe unittest
{
+ import core.time;
import std.datetime.date : DateTime;
assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1);
@@ -6496,12 +6935,18 @@ public:
@safe unittest
{
+ import core.time;
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(st.dayOfYear == 187);
assert(cst.dayOfYear == 187);
- //assert(ist.dayOfYear == 187);
+ assert(ist.dayOfYear == 187);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.dayOfYear;
+ }
}
@@ -6512,7 +6957,7 @@ public:
day = The day of the year to set which day of the year this
$(LREF SysTime) is on.
+/
- @property void dayOfYear(int day) @safe
+ @property void dayOfYear(int day) @safe scope
{
immutable hnsecs = adjTime;
immutable days = convert!("hnsecs", "days")(hnsecs);
@@ -6528,20 +6973,26 @@ public:
@safe unittest
{
+ import core.time;
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
st.dayOfYear = 12;
assert(st.dayOfYear == 12);
static assert(!__traits(compiles, cst.dayOfYear = 12));
- //static assert(!__traits(compiles, ist.dayOfYear = 12));
+ static assert(!__traits(compiles, ist.dayOfYear = 12));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.dayOfYear = 42;
+ }
}
/++
The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on.
+/
- @property int dayOfGregorianCal() @safe const nothrow
+ @property int dayOfGregorianCal() @safe const nothrow scope
{
immutable adjustedTime = adjTime;
@@ -6560,6 +7011,7 @@ public:
///
@safe unittest
{
+ import core.time;
import std.datetime.date : DateTime;
assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1);
@@ -6576,6 +7028,7 @@ public:
@safe unittest
{
+ import core.time;
// Test A.D.
assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1);
assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 1);
@@ -6737,9 +7190,14 @@ public:
assert(SysTime(DateTime(-3760, 9, 7, 0, 0, 0)).dayOfGregorianCal == -1_373_427);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.dayOfGregorianCal == 729_941);
- //assert(ist.dayOfGregorianCal == 729_941);
+ assert(ist.dayOfGregorianCal == 729_941);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.dayOfGregorianCal;
+ }
}
@@ -6747,6 +7205,7 @@ public:
// between Date and SysTime.
@safe unittest
{
+ import core.time;
void test(Date date, SysTime st, size_t line = __LINE__)
{
if (date.dayOfGregorianCal != st.dayOfGregorianCal)
@@ -6912,7 +7371,7 @@ public:
days = The day of the Gregorian Calendar to set this $(LREF SysTime)
to.
+/
- @property void dayOfGregorianCal(int days) @safe nothrow
+ @property void dayOfGregorianCal(int days) @safe nothrow scope
{
auto hnsecs = adjTime;
hnsecs = removeUnitsFromHNSecs!"days"(hnsecs);
@@ -6934,6 +7393,7 @@ public:
///
@safe unittest
{
+ import core.time;
import std.datetime.date : DateTime;
auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0));
@@ -6964,7 +7424,8 @@ public:
@safe unittest
{
- void testST(SysTime orig, int day, in SysTime expected, size_t line = __LINE__)
+ import core.time;
+ void testST(SysTime orig, int day, SysTime expected, size_t line = __LINE__) @safe
{
orig.dayOfGregorianCal = day;
if (orig != expected)
@@ -7001,7 +7462,7 @@ public:
auto st = SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212));
- void testST2(int day, in SysTime expected, size_t line = __LINE__)
+ void testST2(int day, SysTime expected, size_t line = __LINE__) @safe
{
st.dayOfGregorianCal = day;
if (st != expected)
@@ -7156,9 +7617,14 @@ public:
testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), msecs(212)));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.dayOfGregorianCal = 7));
- //static assert(!__traits(compiles, ist.dayOfGregorianCal = 7));
+ static assert(!__traits(compiles, ist.dayOfGregorianCal = 7));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ st.dayOfGregorianCal = 42;
+ }
}
@@ -7168,7 +7634,7 @@ public:
See_Also:
$(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date).
+/
- @property ubyte isoWeek() @safe const nothrow
+ @property ubyte isoWeek() @safe const nothrow scope
{
return (cast(Date) this).isoWeek;
}
@@ -7176,6 +7642,7 @@ public:
///
@safe unittest
{
+ import core.time;
import std.datetime.date : Date;
auto st = SysTime(Date(1999, 7, 6));
@@ -7187,12 +7654,20 @@ public:
assert(ist.isoWeek == 41);
}
+ @safe unittest
+ {
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.isoWeek;
+ }
+ }
+
/++
$(LREF SysTime) for the last day in the month that this Date is in.
The time portion of endOfMonth is always 23:59:59.9999999.
+/
- @property SysTime endOfMonth() @safe const nothrow
+ @property SysTime endOfMonth() @safe const nothrow scope
{
immutable hnsecs = adjTime;
immutable days = getUnitsFromHNSecs!"days"(hnsecs);
@@ -7238,6 +7713,7 @@ public:
@safe unittest
{
+ import core.time;
// Test A.D.
assert(SysTime(Date(1999, 1, 1)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999)));
assert(SysTime(Date(1999, 2, 1)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999)));
@@ -7272,16 +7748,21 @@ public:
SysTime(DateTime(-1999, 12, 31, 23, 59, 59), hnsecs(9_999_999)));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999)));
- //assert(ist.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999)));
+ assert(ist.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999)));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.endOfMonth;
+ }
}
/++
The last day in the month that this $(LREF SysTime) is in.
+/
- @property ubyte daysInMonth() @safe const nothrow
+ @property ubyte daysInMonth() @safe const nothrow scope
{
return Date(dayOfGregorianCal).daysInMonth;
}
@@ -7289,6 +7770,7 @@ public:
///
@safe unittest
{
+ import core.time;
import std.datetime.date : DateTime;
assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31);
@@ -7299,6 +7781,7 @@ public:
@safe unittest
{
+ import core.time;
// Test A.D.
assert(SysTime(DateTime(1999, 1, 1, 12, 1, 13)).daysInMonth == 31);
assert(SysTime(DateTime(1999, 2, 1, 17, 13, 12)).daysInMonth == 28);
@@ -7330,16 +7813,21 @@ public:
assert(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).daysInMonth == 31);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.daysInMonth == 31);
- //assert(ist.daysInMonth == 31);
+ assert(ist.daysInMonth == 31);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.daysInMonth;
+ }
}
/++
Whether the current year is a date in A.D.
+/
- @property bool isAD() @safe const nothrow
+ @property bool isAD() @safe const nothrow scope
{
return adjTime >= 0;
}
@@ -7347,6 +7835,7 @@ public:
///
@safe unittest
{
+ import core.time;
import std.datetime.date : DateTime;
assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD);
@@ -7357,6 +7846,7 @@ public:
@safe unittest
{
+ import core.time;
assert(SysTime(DateTime(2010, 7, 4, 12, 0, 9)).isAD);
assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).isAD);
assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD);
@@ -7365,9 +7855,14 @@ public:
assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.isAD);
- //assert(ist.isAD);
+ assert(ist.isAD);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.isAD;
+ }
}
@@ -7378,7 +7873,7 @@ public:
this function returns 2_450_173, while from noon onward, the Julian
day number would be 2_450_174, so this function returns 2_450_174.
+/
- @property long julianDay() @safe const nothrow
+ @property long julianDay() @safe const nothrow scope
{
immutable jd = dayOfGregorianCal + 1_721_425;
return hour < 12 ? jd - 1 : jd;
@@ -7386,6 +7881,7 @@ public:
@safe unittest
{
+ import core.time;
assert(SysTime(DateTime(-4713, 11, 24, 0, 0, 0)).julianDay == -1);
assert(SysTime(DateTime(-4713, 11, 24, 12, 0, 0)).julianDay == 0);
@@ -7411,9 +7907,14 @@ public:
assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay == 2_455_433);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.julianDay == 2_451_366);
- //assert(ist.julianDay == 2_451_366);
+ assert(ist.julianDay == 2_451_366);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.julianDay;
+ }
}
@@ -7422,13 +7923,14 @@ public:
any time on this date (since, the modified Julian day changes at
midnight).
+/
- @property long modJulianDay() @safe const nothrow
+ @property long modJulianDay() @safe const nothrow scope
{
return dayOfGregorianCal + 1_721_425 - 2_400_001;
}
@safe unittest
{
+ import core.time;
assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).modJulianDay == 0);
assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).modJulianDay == 0);
@@ -7436,23 +7938,29 @@ public:
assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay == 55_432);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.modJulianDay == 51_365);
- //assert(ist.modJulianDay == 51_365);
+ assert(ist.modJulianDay == 51_365);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.modJulianDay;
+ }
}
/++
Returns a $(REF Date,std,datetime,date) equivalent to this $(LREF SysTime).
+/
- Date opCast(T)() @safe const nothrow
- if (is(Unqual!T == Date))
+ Date opCast(T)() @safe const nothrow scope
+ if (is(immutable T == immutable Date))
{
return Date(dayOfGregorianCal);
}
@safe unittest
{
+ import core.time;
assert(cast(Date) SysTime(Date(1999, 7, 6)) == Date(1999, 7, 6));
assert(cast(Date) SysTime(Date(2000, 12, 31)) == Date(2000, 12, 31));
assert(cast(Date) SysTime(Date(2001, 1, 1)) == Date(2001, 1, 1));
@@ -7470,9 +7978,14 @@ public:
assert(cast(Date) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == Date(-2001, 1, 1));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cast(Date) cst != Date.init);
- //assert(cast(Date) ist != Date.init);
+ assert(cast(Date) ist != Date.init);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = cast(Date) st;
+ }
}
@@ -7480,8 +7993,8 @@ public:
Returns a $(REF DateTime,std,datetime,date) equivalent to this
$(LREF SysTime).
+/
- DateTime opCast(T)() @safe const nothrow
- if (is(Unqual!T == DateTime))
+ DateTime opCast(T)() @safe const nothrow scope
+ if (is(immutable T == immutable DateTime))
{
try
{
@@ -7506,6 +8019,7 @@ public:
@safe unittest
{
+ import core.time;
assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22)) == DateTime(1, 1, 6, 7, 12, 22));
assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(1, 1, 6, 7, 12, 22));
assert(cast(DateTime) SysTime(Date(1999, 7, 6)) == DateTime(1999, 7, 6, 0, 0, 0));
@@ -7530,9 +8044,14 @@ public:
DateTime(2011, 1, 13, 8, 17, 2));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cast(DateTime) cst != DateTime.init);
- //assert(cast(DateTime) ist != DateTime.init);
+ assert(cast(DateTime) ist != DateTime.init);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = cast(DateTime) st;
+ }
}
@@ -7540,8 +8059,8 @@ public:
Returns a $(REF TimeOfDay,std,datetime,date) equivalent to this
$(LREF SysTime).
+/
- TimeOfDay opCast(T)() @safe const nothrow
- if (is(Unqual!T == TimeOfDay))
+ TimeOfDay opCast(T)() @safe const nothrow scope
+ if (is(immutable T == immutable TimeOfDay))
{
try
{
@@ -7563,6 +8082,7 @@ public:
@safe unittest
{
+ import core.time;
assert(cast(TimeOfDay) SysTime(Date(1999, 7, 6)) == TimeOfDay(0, 0, 0));
assert(cast(TimeOfDay) SysTime(Date(2000, 12, 31)) == TimeOfDay(0, 0, 0));
assert(cast(TimeOfDay) SysTime(Date(2001, 1, 1)) == TimeOfDay(0, 0, 0));
@@ -7580,23 +8100,36 @@ public:
assert(cast(TimeOfDay) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cast(TimeOfDay) cst != TimeOfDay.init);
- //assert(cast(TimeOfDay) ist != TimeOfDay.init);
+ assert(cast(TimeOfDay) ist != TimeOfDay.init);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = cast(TimeOfDay) st;
+ }
}
- // Temporary hack until bug http://d.puremagic.com/issues/show_bug.cgi?id=4867 is fixed.
+ // Temporary hack until bug https://issues.dlang.org/show_bug.cgi?id=4867 is fixed.
// This allows assignment from const(SysTime) to SysTime.
// It may be a good idea to keep it though, since casting from a type to itself
// should be allowed, and it doesn't work without this opCast() since opCast()
// has already been defined for other types.
- SysTime opCast(T)() @safe const pure nothrow
- if (is(Unqual!T == SysTime))
+ SysTime opCast(T)() @safe const pure nothrow scope
+ if (is(immutable T == immutable SysTime))
{
return SysTime(_stdTime, _timezone);
}
+ @safe unittest
+ {
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = cast(SysTime) st;
+ }
+ }
+
/++
Converts this $(LREF SysTime) to a string with the format
@@ -7611,7 +8144,7 @@ public:
If this $(LREF SysTime)'s time zone is
$(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time
- zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC
+ zone is `UTC`, then it is "Z". Otherwise, it is the offset from UTC
(e.g. +0100 or -0700). Note that the offset from UTC is $(I not) enough
to uniquely identify the time zone.
@@ -7628,45 +8161,67 @@ public:
writing out the result of toISOString to read in later will continue
to work. The current behavior will be kept until July 2019 at which
point, fromISOString will be fixed to be standards compliant.)
+
+ Params:
+ writer = A `char` accepting
+ $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
- string toISOString() @safe const nothrow
+ string toISOString() @safe const nothrow scope
{
+ import std.array : appender;
+ auto app = appender!string();
+ app.reserve(30);
try
- {
- immutable adjustedTime = adjTime;
- long hnsecs = adjustedTime;
-
- auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
+ toISOString(app);
+ catch (Exception e)
+ assert(0, "toISOString() threw.");
+ return app.data;
+ }
- if (hnsecs < 0)
- {
- hnsecs += convert!("hours", "hnsecs")(24);
- --days;
- }
+ /// ditto
+ void toISOString(W)(ref W writer) const scope
+ if (isOutputRange!(W, char))
+ {
+ immutable adjustedTime = adjTime;
+ long hnsecs = adjustedTime;
- auto hour = splitUnitsFromHNSecs!"hours"(hnsecs);
- auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs);
- auto second = splitUnitsFromHNSecs!"seconds"(hnsecs);
+ auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
- auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour,
- cast(int) minute, cast(int) second));
- auto fracSecStr = fracSecsToISOString(cast(int) hnsecs);
+ if (hnsecs < 0)
+ {
+ hnsecs += convert!("hours", "hnsecs")(24);
+ --days;
+ }
- if (_timezone is LocalTime())
- return dateTime.toISOString() ~ fracSecStr;
+ immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs);
+ immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs);
+ immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs);
- if (_timezone is UTC())
- return dateTime.toISOString() ~ fracSecStr ~ "Z";
+ auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour,
+ cast(int) minute, cast(int) second));
- immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime);
+ if (_timezone is LocalTime())
+ {
+ dateTime.toISOString(writer);
+ fracSecsToISOString(writer, cast(int) hnsecs);
+ return;
+ }
- return format("%s%s%s",
- dateTime.toISOString(),
- fracSecStr,
- SimpleTimeZone.toISOExtString(utcOffset));
+ if (_timezone is UTC())
+ {
+ dateTime.toISOString(writer);
+ fracSecsToISOString(writer, cast(int) hnsecs);
+ put(writer, 'Z');
+ return;
}
- catch (Exception e)
- assert(0, "format() threw.");
+
+ immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime);
+
+ dateTime.toISOString(writer);
+ fracSecsToISOString(writer, cast(int) hnsecs);
+ SimpleTimeZone.toISOExtString(writer, utcOffset);
}
///
@@ -7690,6 +8245,7 @@ public:
@safe unittest
{
+ import core.time;
// Test A.D.
assert(SysTime(DateTime.init, UTC()).toISOString() == "00010101T000000Z");
assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOString() == "00010101T000000.0000001Z");
@@ -7735,9 +8291,14 @@ public:
assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "-100001020T010101.050789");
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- assert(cast(TimeOfDay) cst != TimeOfDay.init);
- //assert(cast(TimeOfDay) ist != TimeOfDay.init);
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ assert(cst.toISOString() == "19990706T123033");
+ assert(ist.toISOString() == "19990706T123033");
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toISOString();
+ }
}
@@ -7755,50 +8316,72 @@ public:
If this $(LREF SysTime)'s time zone is
$(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time
- zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC
+ zone is `UTC`, then it is "Z". Otherwise, it is the offset from UTC
(e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not)
enough to uniquely identify the time zone.
Time zone offsets will be in the form +HH:MM or -HH:MM.
+
+ Params:
+ writer = A `char` accepting
+ $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
- string toISOExtString() @safe const nothrow
+ string toISOExtString() @safe const nothrow scope
{
+ import std.array : appender;
+ auto app = appender!string();
+ app.reserve(35);
try
- {
- immutable adjustedTime = adjTime;
- long hnsecs = adjustedTime;
-
- auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
+ toISOExtString(app);
+ catch (Exception e)
+ assert(0, "toISOExtString() threw.");
+ return app.data;
+ }
- if (hnsecs < 0)
- {
- hnsecs += convert!("hours", "hnsecs")(24);
- --days;
- }
+ /// ditto
+ void toISOExtString(W)(ref W writer) const scope
+ if (isOutputRange!(W, char))
+ {
+ immutable adjustedTime = adjTime;
+ long hnsecs = adjustedTime;
- auto hour = splitUnitsFromHNSecs!"hours"(hnsecs);
- auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs);
- auto second = splitUnitsFromHNSecs!"seconds"(hnsecs);
+ auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
- auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour,
- cast(int) minute, cast(int) second));
- auto fracSecStr = fracSecsToISOString(cast(int) hnsecs);
+ if (hnsecs < 0)
+ {
+ hnsecs += convert!("hours", "hnsecs")(24);
+ --days;
+ }
- if (_timezone is LocalTime())
- return dateTime.toISOExtString() ~ fracSecStr;
+ immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs);
+ immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs);
+ immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs);
- if (_timezone is UTC())
- return dateTime.toISOExtString() ~ fracSecStr ~ "Z";
+ immutable dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour,
+ cast(int) minute, cast(int) second));
- immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime);
+ if (_timezone is LocalTime())
+ {
+ dateTime.toISOExtString(writer);
+ fracSecsToISOString(writer, cast(int) hnsecs);
+ return;
+ }
- return format("%s%s%s",
- dateTime.toISOExtString(),
- fracSecStr,
- SimpleTimeZone.toISOExtString(utcOffset));
+ if (_timezone is UTC())
+ {
+ dateTime.toISOExtString(writer);
+ fracSecsToISOString(writer, cast(int) hnsecs);
+ put(writer, 'Z');
+ return;
}
- catch (Exception e)
- assert(0, "format() threw.");
+
+ immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime);
+
+ dateTime.toISOExtString(writer);
+ fracSecsToISOString(writer, cast(int) hnsecs);
+ SimpleTimeZone.toISOExtString(writer, utcOffset);
}
///
@@ -7822,6 +8405,7 @@ public:
@safe unittest
{
+ import core.time;
// Test A.D.
assert(SysTime(DateTime.init, UTC()).toISOExtString() == "0001-01-01T00:00:00Z");
assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOExtString() ==
@@ -7873,9 +8457,14 @@ public:
"-10000-10-20T01:01:01.050789");
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- assert(cast(TimeOfDay) cst != TimeOfDay.init);
- //assert(cast(TimeOfDay) ist != TimeOfDay.init);
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ assert(cst.toISOExtString() == "1999-07-06T12:30:33");
+ assert(ist.toISOExtString() == "1999-07-06T12:30:33");
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toISOExtString();
+ }
}
/++
@@ -7891,50 +8480,72 @@ public:
If this $(LREF SysTime)'s time zone is
$(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time
- zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC
+ zone is `UTC`, then it is "Z". Otherwise, it is the offset from UTC
(e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not)
enough to uniquely identify the time zone.
Time zone offsets will be in the form +HH:MM or -HH:MM.
+
+ Params:
+ writer = A `char` accepting
+ $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
- string toSimpleString() @safe const nothrow
+ string toSimpleString() @safe const nothrow scope
{
+ import std.array : appender;
+ auto app = appender!string();
+ app.reserve(35);
try
- {
- immutable adjustedTime = adjTime;
- long hnsecs = adjustedTime;
-
- auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
+ toSimpleString(app);
+ catch (Exception e)
+ assert(0, "toSimpleString() threw.");
+ return app.data;
+ }
- if (hnsecs < 0)
- {
- hnsecs += convert!("hours", "hnsecs")(24);
- --days;
- }
+ /// ditto
+ void toSimpleString(W)(ref W writer) const scope
+ if (isOutputRange!(W, char))
+ {
+ immutable adjustedTime = adjTime;
+ long hnsecs = adjustedTime;
- auto hour = splitUnitsFromHNSecs!"hours"(hnsecs);
- auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs);
- auto second = splitUnitsFromHNSecs!"seconds"(hnsecs);
+ auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
- auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour,
- cast(int) minute, cast(int) second));
- auto fracSecStr = fracSecsToISOString(cast(int) hnsecs);
+ if (hnsecs < 0)
+ {
+ hnsecs += convert!("hours", "hnsecs")(24);
+ --days;
+ }
- if (_timezone is LocalTime())
- return dateTime.toSimpleString() ~ fracSecStr;
+ immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs);
+ immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs);
+ immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs);
- if (_timezone is UTC())
- return dateTime.toSimpleString() ~ fracSecStr ~ "Z";
+ immutable dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour,
+ cast(int) minute, cast(int) second));
- immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime);
+ if (_timezone is LocalTime())
+ {
+ dateTime.toSimpleString(writer);
+ fracSecsToISOString(writer, cast(int) hnsecs);
+ return;
+ }
- return format("%s%s%s",
- dateTime.toSimpleString(),
- fracSecStr,
- SimpleTimeZone.toISOExtString(utcOffset));
+ if (_timezone is UTC())
+ {
+ dateTime.toSimpleString(writer);
+ fracSecsToISOString(writer, cast(int) hnsecs);
+ put(writer, 'Z');
+ return;
}
- catch (Exception e)
- assert(0, "format() threw.");
+
+ immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime);
+
+ dateTime.toSimpleString(writer);
+ fracSecsToISOString(writer, cast(int) hnsecs);
+ SimpleTimeZone.toISOExtString(writer, utcOffset);
}
///
@@ -7958,6 +8569,7 @@ public:
@safe unittest
{
+ import core.time;
// Test A.D.
assert(SysTime(DateTime.init, UTC()).toString() == "0001-Jan-01 00:00:00Z");
assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toString() == "0001-Jan-01 00:00:00.0000001Z");
@@ -8010,9 +8622,14 @@ public:
"-10000-Oct-20 01:01:01.050789");
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- assert(cast(TimeOfDay) cst != TimeOfDay.init);
- //assert(cast(TimeOfDay) ist != TimeOfDay.init);
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ assert(cst.toSimpleString() == "1999-Jul-06 12:30:33");
+ assert(ist.toSimpleString() == "1999-Jul-06 12:30:33");
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toSimpleString();
+ }
}
@@ -8038,20 +8655,39 @@ public:
`fromISOString`, `fromISOExtString`, and `fromSimpleString`.
The format returned by toString may or may not change in the future.
+
+ Params:
+ writer = A `char` accepting
+ $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ Returns:
+ A `string` when not using an output range; `void` otherwise.
+/
- string toString() @safe const nothrow
+ string toString() @safe const nothrow scope
{
return toSimpleString();
}
+ /// ditto
+ void toString(W)(ref W writer) const scope
+ if (isOutputRange!(W, char))
+ {
+ toSimpleString(writer);
+ }
+
@safe unittest
{
+ import core.time;
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
- assert(st.toString());
- assert(cst.toString());
- //assert(ist.toString());
+ immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
+ static assert(__traits(compiles, st.toString()));
+ static assert(__traits(compiles, cst.toString()));
+ static assert(__traits(compiles, ist.toString()));
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = st.toString();
+ }
}
@@ -8060,7 +8696,7 @@ public:
YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds is the time
zone). Whitespace is stripped from the given string.
- The exact format is exactly as described in $(D toISOString) except that
+ The exact format is exactly as described in `toISOString` except that
trailing zeroes are permitted - including having fractional seconds with
all zeroes. However, a decimal point with nothing following it is
invalid. Also, while $(LREF toISOString) will never generate a string
@@ -8071,7 +8707,7 @@ public:
If there is no time zone in the string, then
$(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z",
- then $(D UTC) is used. Otherwise, a
+ then `UTC` is used. Otherwise, a
$(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the
given offset from UTC is used. To get the returned $(LREF SysTime) to be
a particular time zone, pass in that time zone and the $(LREF SysTime)
@@ -8103,44 +8739,65 @@ public:
not in the ISO format or if the resulting $(LREF SysTime) would not
be valid.
+/
- static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe
+ static SysTime fromISOString(S)(scope const S isoString, immutable TimeZone tz = null) @safe
if (isSomeString!S)
{
import std.algorithm.searching : startsWith, find;
import std.conv : to;
import std.string : strip;
+ import std.utf : byCodeUnit;
- auto dstr = to!dstring(strip(isoString));
- immutable skipFirst = dstr.startsWith('+', '-') != 0;
+ auto str = strip(isoString);
+ immutable skipFirst = str.startsWith('+', '-');
- auto found = (skipFirst ? dstr[1..$] : dstr).find('.', 'Z', '+', '-');
- auto dateTimeStr = dstr[0 .. $ - found[0].length];
+ auto found = (skipFirst ? str[1..$] : str).byCodeUnit.find('.', 'Z', '+', '-');
+ auto dateTimeStr = str[0 .. $ - found[0].length];
- dstring fracSecStr;
- dstring zoneStr;
+ typeof(str.byCodeUnit) foundTZ; // needs to have longer lifetime than zoneStr
+ typeof(str) fracSecStr;
+ typeof(str) zoneStr;
if (found[1] != 0)
{
if (found[1] == 1)
{
- auto foundTZ = found[0].find('Z', '+', '-');
+ foundTZ = found[0].find('Z', '+', '-')[0];
- if (foundTZ[1] != 0)
+ if (foundTZ.length != 0)
{
- fracSecStr = found[0][0 .. $ - foundTZ[0].length];
- zoneStr = foundTZ[0];
+ static if (isNarrowString!S)
+ {
+ fracSecStr = found[0][0 .. $ - foundTZ.length].source;
+ zoneStr = foundTZ.source;
+ }
+ else
+ {
+ fracSecStr = found[0][0 .. $ - foundTZ.length];
+ zoneStr = foundTZ;
+ }
}
else
- fracSecStr = found[0];
+ {
+ static if (isNarrowString!S)
+ fracSecStr = found[0].source;
+ else
+ fracSecStr = found[0];
+ }
}
else
- zoneStr = found[0];
+ {
+ static if (isNarrowString!S)
+ zoneStr = found[0].source;
+ else
+ zoneStr = found[0];
+ }
}
try
{
auto dateTime = DateTime.fromISOString(dateTimeStr);
auto fracSec = fracSecsFromISOString(fracSecStr);
+
Rebindable!(immutable TimeZone) parsedZone;
if (zoneStr.empty)
@@ -8205,6 +8862,7 @@ public:
@safe unittest
{
+ import core.time;
foreach (str; ["", "20100704000000", "20100704 000000", "20100704t000000",
"20100704T000000.", "20100704T000000.A", "20100704T000000.Z",
"20100704T000000.0000000A", "20100704T000000.00000000A",
@@ -8245,7 +8903,7 @@ public:
throw new AssertError("unittest failure", __FILE__, line);
}
- test("20101222T172201", SysTime(DateTime(2010, 12, 22, 17, 22, 01)));
+ test("20101222T172201", SysTime(DateTime(2010, 12, 22, 17, 22, 1)));
test("19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
test("-19990706T123033", SysTime(DateTime(-1999, 7, 6, 12, 30, 33)));
test("+019990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
@@ -8253,16 +8911,16 @@ public:
test(" 19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
test(" 19990706T123033 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
- test("19070707T121212.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12)));
- test("19070707T121212.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12)));
- test("19070707T121212.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1)));
- test("20100704T000000.00000000", SysTime(Date(2010, 07, 04)));
- test("20100704T000000.00000009", SysTime(Date(2010, 07, 04)));
- test("20100704T000000.00000019", SysTime(DateTime(2010, 07, 04), hnsecs(1)));
- test("19070707T121212.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1)));
- test("19070707T121212.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1)));
- test("19070707T121212.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1)));
- test("19070707T121212.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1)));
+ test("19070707T121212.0", SysTime(DateTime(1907, 7, 7, 12, 12, 12)));
+ test("19070707T121212.0000000", SysTime(DateTime(1907, 7, 7, 12, 12, 12)));
+ test("19070707T121212.0000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), hnsecs(1)));
+ test("20100704T000000.00000000", SysTime(Date(2010, 7, 4)));
+ test("20100704T000000.00000009", SysTime(Date(2010, 7, 4)));
+ test("20100704T000000.00000019", SysTime(DateTime(2010, 7, 4), hnsecs(1)));
+ test("19070707T121212.000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1)));
+ test("19070707T121212.0000010", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1)));
+ test("19070707T121212.001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1)));
+ test("19070707T121212.0010000", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1)));
auto west60 = new immutable SimpleTimeZone(hours(-1));
auto west90 = new immutable SimpleTimeZone(minutes(-90));
@@ -8271,60 +8929,71 @@ public:
auto east90 = new immutable SimpleTimeZone(minutes(90));
auto east480 = new immutable SimpleTimeZone(hours(8));
- test("20101222T172201Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC()));
- test("20101222T172201-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60));
- test("20101222T172201-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60));
- test("20101222T172201-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90));
- test("20101222T172201-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480));
- test("20101222T172201+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("20101222T172201+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("20101222T172201+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90));
- test("20101222T172201+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480));
+ test("20101222T172201Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), UTC()));
+ test("20101222T172201-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60));
+ test("20101222T172201-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60));
+ test("20101222T172201-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west90));
+ test("20101222T172201-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west480));
+ test("20101222T172201+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("20101222T172201+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("20101222T172201+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90));
+ test("20101222T172201+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east480));
test("20101103T065106.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC()));
- test("20101222T172201.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC()));
- test("20101222T172201.23112-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60));
- test("20101222T172201.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60));
- test("20101222T172201.1-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90));
- test("20101222T172201.55-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480));
- test("20101222T172201.1234567+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60));
- test("20101222T172201.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("20101222T172201.0000000+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90));
- test("20101222T172201.45+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480));
+ test("20101222T172201.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_341_200), UTC()));
+ test("20101222T172201.23112-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60));
+ test("20101222T172201.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), west60));
+ test("20101222T172201.1-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_000_000), west90));
+ test("20101222T172201.55-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(5_500_000), west480));
+ test("20101222T172201.1234567+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_234_567), east60));
+ test("20101222T172201.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("20101222T172201.0000000+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90));
+ test("20101222T172201.45+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), east480));
+
+ // for dstring coverage
+ assert(SysTime.fromISOString("20101222T172201.23112-0100"d) == SysTime(
+ DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60));
+ assert(SysTime.fromISOString("19070707T121212.0010000"d) == SysTime(
+ DateTime(1907, 7, 7, 12, 12, 12), msecs(1)));
// @@@DEPRECATED_2019-07@@@
// This isn't deprecated per se, but that text will make it so that it
// pops up when deprecations are moved along around July 2019. At that
// time, we will update fromISOString so that it is conformant with ISO
// 8601, and it will no longer accept ISO extended time zones (it does
- // currently because of issue #15654 - toISOString used to incorrectly
- // use the ISO extended time zone format). These tests will then start
- // failing will need to be updated accordingly. Also, the notes about
- // this issue in toISOString and fromISOString's documentation will need
- // to be removed.
- test("20101222T172201-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60));
- test("20101222T172201-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90));
- test("20101222T172201-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480));
- test("20101222T172201+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("20101222T172201+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90));
- test("20101222T172201+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480));
-
- test("20101222T172201.23112-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60));
- test("20101222T172201.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90));
- test("20101222T172201.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480));
- test("20101222T172201.1234567+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60));
- test("20101222T172201.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90));
- test("20101222T172201.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480));
- }
-
- // bug# 17801
+ // currently because of https://issues.dlang.org/show_bug.cgi?id=15654
+ // toISOString used to incorrectly use the ISO extended time zone format).
+ // These tests will then start failing will need to be updated accordingly.
+ // Also, the notes about this issue in toISOString and fromISOString's
+ // documentation will need to be removed.
+ test("20101222T172201-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60));
+ test("20101222T172201-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west90));
+ test("20101222T172201-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west480));
+ test("20101222T172201+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("20101222T172201+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90));
+ test("20101222T172201+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east480));
+
+ test("20101222T172201.23112-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60));
+ test("20101222T172201.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_000_000), west90));
+ test("20101222T172201.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(5_500_000), west480));
+ test("20101222T172201.1234567+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_234_567), east60));
+ test("20101222T172201.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90));
+ test("20101222T172201.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), east480));
+
+ static void testScope(scope ref string str) @safe
+ {
+ auto result = SysTime.fromISOString(str);
+ }
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
{
assert(SysTime.fromISOString(to!S("20121221T141516Z")) ==
SysTime(DateTime(2012, 12, 21, 14, 15, 16), UTC()));
@@ -8338,7 +9007,7 @@ public:
YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the
time zone). Whitespace is stripped from the given string.
- The exact format is exactly as described in $(D toISOExtString)
+ The exact format is exactly as described in `toISOExtString`
except that trailing zeroes are permitted - including having fractional
seconds with all zeroes. However, a decimal point with nothing following
it is invalid. Also, while $(LREF toISOExtString) will never generate a
@@ -8349,7 +9018,7 @@ public:
If there is no time zone in the string, then
$(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z",
- then $(D UTC) is used. Otherwise, a
+ then `UTC` is used. Otherwise, a
$(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the
given offset from UTC is used. To get the returned $(LREF SysTime) to be
a particular time zone, pass in that time zone and the $(LREF SysTime)
@@ -8370,34 +9039,35 @@ public:
not in the ISO format or if the resulting $(LREF SysTime) would not
be valid.
+/
- static SysTime fromISOExtString(S)(in S isoExtString, immutable TimeZone tz = null) @safe
+ static SysTime fromISOExtString(S)(scope const S isoExtString, immutable TimeZone tz = null) @safe
if (isSomeString!(S))
{
import std.algorithm.searching : countUntil, find;
import std.conv : to;
- import std.string : strip;
+ import std.string : strip, indexOf;
- auto dstr = to!dstring(strip(isoExtString));
+ auto str = strip(isoExtString);
- auto tIndex = dstr.countUntil('T');
+ auto tIndex = str.indexOf('T');
enforce(tIndex != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
- auto found = dstr[tIndex + 1 .. $].find('.', 'Z', '+', '-');
- auto dateTimeStr = dstr[0 .. $ - found[0].length];
+ auto found = str[tIndex + 1 .. $].find('.', 'Z', '+', '-');
+ auto dateTimeStr = str[0 .. $ - found[0].length];
- dstring fracSecStr;
- dstring zoneStr;
+ typeof(str) foundTZ; // needs to have longer lifetime than zoneStr
+ typeof(str) fracSecStr;
+ typeof(str) zoneStr;
if (found[1] != 0)
{
if (found[1] == 1)
{
- auto foundTZ = found[0].find('Z', '+', '-');
+ foundTZ = found[0].find('Z', '+', '-')[0];
- if (foundTZ[1] != 0)
+ if (foundTZ.length != 0)
{
- fracSecStr = found[0][0 .. $ - foundTZ[0].length];
- zoneStr = foundTZ[0];
+ fracSecStr = found[0][0 .. $ - foundTZ.length];
+ zoneStr = foundTZ;
}
else
fracSecStr = found[0];
@@ -8468,6 +9138,7 @@ public:
@safe unittest
{
+ import core.time;
foreach (str; ["", "20100704000000", "20100704 000000",
"20100704t000000", "20100704T000000.", "20100704T000000.0",
"2010-07:0400:00:00", "2010-07-04 00:00:00",
@@ -8508,7 +9179,7 @@ public:
throw new AssertError("unittest failure", __FILE__, line);
}
- test("2010-12-22T17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 01)));
+ test("2010-12-22T17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 1)));
test("1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
test("-1999-07-06T12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33)));
test("+01999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
@@ -8516,16 +9187,16 @@ public:
test(" 1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
test(" 1999-07-06T12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
- test("1907-07-07T12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12)));
- test("1907-07-07T12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12)));
- test("1907-07-07T12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1)));
- test("2010-07-04T00:00:00.00000000", SysTime(Date(2010, 07, 04)));
- test("2010-07-04T00:00:00.00000009", SysTime(Date(2010, 07, 04)));
- test("2010-07-04T00:00:00.00000019", SysTime(DateTime(2010, 07, 04), hnsecs(1)));
- test("1907-07-07T12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1)));
- test("1907-07-07T12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1)));
- test("1907-07-07T12:12:12.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1)));
- test("1907-07-07T12:12:12.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1)));
+ test("1907-07-07T12:12:12.0", SysTime(DateTime(1907, 7, 7, 12, 12, 12)));
+ test("1907-07-07T12:12:12.0000000", SysTime(DateTime(1907, 7, 7, 12, 12, 12)));
+ test("1907-07-07T12:12:12.0000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), hnsecs(1)));
+ test("2010-07-04T00:00:00.00000000", SysTime(Date(2010, 7, 4)));
+ test("2010-07-04T00:00:00.00000009", SysTime(Date(2010, 7, 4)));
+ test("2010-07-04T00:00:00.00000019", SysTime(DateTime(2010, 7, 4), hnsecs(1)));
+ test("1907-07-07T12:12:12.000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1)));
+ test("1907-07-07T12:12:12.0000010", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1)));
+ test("1907-07-07T12:12:12.001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1)));
+ test("1907-07-07T12:12:12.0010000", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1)));
auto west60 = new immutable SimpleTimeZone(hours(-1));
auto west90 = new immutable SimpleTimeZone(minutes(-90));
@@ -8534,38 +9205,44 @@ public:
auto east90 = new immutable SimpleTimeZone(minutes(90));
auto east480 = new immutable SimpleTimeZone(hours(8));
- test("2010-12-22T17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC()));
- test("2010-12-22T17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60));
- test("2010-12-22T17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60));
- test("2010-12-22T17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90));
- test("2010-12-22T17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480));
- test("2010-12-22T17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("2010-12-22T17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("2010-12-22T17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90));
- test("2010-12-22T17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480));
+ test("2010-12-22T17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), UTC()));
+ test("2010-12-22T17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60));
+ test("2010-12-22T17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60));
+ test("2010-12-22T17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west90));
+ test("2010-12-22T17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west480));
+ test("2010-12-22T17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("2010-12-22T17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("2010-12-22T17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90));
+ test("2010-12-22T17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east480));
test("2010-11-03T06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC()));
- test("2010-12-22T17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC()));
+ test("2010-12-22T17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_341_200), UTC()));
test("2010-12-22T17:22:01.23112-01:00",
- SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60));
- test("2010-12-22T17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60));
- test("2010-12-22T17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90));
- test("2010-12-22T17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480));
+ SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60));
+ test("2010-12-22T17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), west60));
+ test("2010-12-22T17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_000_000), west90));
+ test("2010-12-22T17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(5_500_000), west480));
test("2010-12-22T17:22:01.1234567+01:00",
- SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60));
- test("2010-12-22T17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("2010-12-22T17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90));
- test("2010-12-22T17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480));
+ SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_234_567), east60));
+ test("2010-12-22T17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("2010-12-22T17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90));
+ test("2010-12-22T17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), east480));
+
+ static void testScope(scope ref string str) @safe
+ {
+ auto result = SysTime.fromISOExtString(str);
+ }
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
+ import core.time;
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
{
assert(SysTime.fromISOExtString(to!S("2012-12-21T14:15:16Z")) ==
SysTime(DateTime(2012, 12, 21, 14, 15, 16), UTC()));
@@ -8579,7 +9256,7 @@ public:
YYYY-MM-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the
time zone). Whitespace is stripped from the given string.
- The exact format is exactly as described in $(D toSimpleString) except
+ The exact format is exactly as described in `toSimpleString` except
that trailing zeroes are permitted - including having fractional seconds
with all zeroes. However, a decimal point with nothing following it is
invalid. Also, while $(LREF toSimpleString) will never generate a
@@ -8590,7 +9267,7 @@ public:
If there is no time zone in the string, then
$(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z",
- then $(D UTC) is used. Otherwise, a
+ then `UTC` is used. Otherwise, a
$(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the
given offset from UTC is used. To get the returned $(LREF SysTime) to be
a particular time zone, pass in that time zone and the $(LREF SysTime)
@@ -8602,7 +9279,7 @@ public:
Params:
simpleString = A string formatted in the way that
- $(D toSimpleString) formats dates and times.
+ `toSimpleString` formats dates and times.
tz = The time zone to convert the given time to (no
conversion occurs if null).
@@ -8611,34 +9288,35 @@ public:
not in the ISO format or if the resulting $(LREF SysTime) would not
be valid.
+/
- static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) @safe
+ static SysTime fromSimpleString(S)(scope const S simpleString, immutable TimeZone tz = null) @safe
if (isSomeString!(S))
{
- import std.algorithm.searching : countUntil, find;
+ import std.algorithm.searching : find;
import std.conv : to;
- import std.string : strip;
+ import std.string : strip, indexOf;
- auto dstr = to!dstring(strip(simpleString));
+ auto str = strip(simpleString);
- auto spaceIndex = dstr.countUntil(' ');
+ auto spaceIndex = str.indexOf(' ');
enforce(spaceIndex != -1, new DateTimeException(format("Invalid Simple String: %s", simpleString)));
- auto found = dstr[spaceIndex + 1 .. $].find('.', 'Z', '+', '-');
- auto dateTimeStr = dstr[0 .. $ - found[0].length];
+ auto found = str[spaceIndex + 1 .. $].find('.', 'Z', '+', '-');
+ auto dateTimeStr = str[0 .. $ - found[0].length];
- dstring fracSecStr;
- dstring zoneStr;
+ typeof(str) foundTZ; // needs to have longer lifetime than zoneStr
+ typeof(str) fracSecStr;
+ typeof(str) zoneStr;
if (found[1] != 0)
{
if (found[1] == 1)
{
- auto foundTZ = found[0].find('Z', '+', '-');
+ foundTZ = found[0].find('Z', '+', '-')[0];
- if (foundTZ[1] != 0)
+ if (foundTZ.length != 0)
{
- fracSecStr = found[0][0 .. $ - foundTZ[0].length];
- zoneStr = foundTZ[0];
+ fracSecStr = found[0][0 .. $ - foundTZ.length];
+ zoneStr = foundTZ;
}
else
fracSecStr = found[0];
@@ -8710,6 +9388,7 @@ public:
@safe unittest
{
+ import core.time;
foreach (str; ["", "20100704000000", "20100704 000000",
"20100704t000000", "20100704T000000.", "20100704T000000.0",
"2010-07-0400:00:00", "2010-07-04 00:00:00", "2010-07-04t00:00:00",
@@ -8752,7 +9431,7 @@ public:
throw new AssertError("unittest failure", __FILE__, line);
}
- test("2010-Dec-22 17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 01)));
+ test("2010-Dec-22 17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 1)));
test("1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
test("-1999-Jul-06 12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33)));
test("+01999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
@@ -8760,16 +9439,16 @@ public:
test(" 1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
test(" 1999-Jul-06 12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33)));
- test("1907-Jul-07 12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12)));
- test("1907-Jul-07 12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12)));
- test("2010-Jul-04 00:00:00.00000000", SysTime(Date(2010, 07, 04)));
- test("2010-Jul-04 00:00:00.00000009", SysTime(Date(2010, 07, 04)));
- test("2010-Jul-04 00:00:00.00000019", SysTime(DateTime(2010, 07, 04), hnsecs(1)));
- test("1907-Jul-07 12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1)));
- test("1907-Jul-07 12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1)));
- test("1907-Jul-07 12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1)));
- test("1907-Jul-07 12:12:12.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1)));
- test("1907-Jul-07 12:12:12.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1)));
+ test("1907-Jul-07 12:12:12.0", SysTime(DateTime(1907, 7, 7, 12, 12, 12)));
+ test("1907-Jul-07 12:12:12.0000000", SysTime(DateTime(1907, 7, 7, 12, 12, 12)));
+ test("2010-Jul-04 00:00:00.00000000", SysTime(Date(2010, 7, 4)));
+ test("2010-Jul-04 00:00:00.00000009", SysTime(Date(2010, 7, 4)));
+ test("2010-Jul-04 00:00:00.00000019", SysTime(DateTime(2010, 7, 4), hnsecs(1)));
+ test("1907-Jul-07 12:12:12.0000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), hnsecs(1)));
+ test("1907-Jul-07 12:12:12.000001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1)));
+ test("1907-Jul-07 12:12:12.0000010", SysTime(DateTime(1907, 7, 7, 12, 12, 12), usecs(1)));
+ test("1907-Jul-07 12:12:12.001", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1)));
+ test("1907-Jul-07 12:12:12.0010000", SysTime(DateTime(1907, 7, 7, 12, 12, 12), msecs(1)));
auto west60 = new immutable SimpleTimeZone(hours(-1));
auto west90 = new immutable SimpleTimeZone(minutes(-90));
@@ -8778,38 +9457,44 @@ public:
auto east90 = new immutable SimpleTimeZone(minutes(90));
auto east480 = new immutable SimpleTimeZone(hours(8));
- test("2010-Dec-22 17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC()));
- test("2010-Dec-22 17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60));
- test("2010-Dec-22 17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60));
- test("2010-Dec-22 17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90));
- test("2010-Dec-22 17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480));
- test("2010-Dec-22 17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("2010-Dec-22 17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("2010-Dec-22 17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90));
- test("2010-Dec-22 17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480));
+ test("2010-Dec-22 17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), UTC()));
+ test("2010-Dec-22 17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60));
+ test("2010-Dec-22 17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west60));
+ test("2010-Dec-22 17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west90));
+ test("2010-Dec-22 17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), west480));
+ test("2010-Dec-22 17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("2010-Dec-22 17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("2010-Dec-22 17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90));
+ test("2010-Dec-22 17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east480));
test("2010-Nov-03 06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC()));
- test("2010-Dec-22 17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC()));
+ test("2010-Dec-22 17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_341_200), UTC()));
test("2010-Dec-22 17:22:01.23112-01:00",
- SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60));
- test("2010-Dec-22 17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60));
- test("2010-Dec-22 17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90));
- test("2010-Dec-22 17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480));
+ SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(2_311_200), west60));
+ test("2010-Dec-22 17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), west60));
+ test("2010-Dec-22 17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_000_000), west90));
+ test("2010-Dec-22 17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(5_500_000), west480));
test("2010-Dec-22 17:22:01.1234567+01:00",
- SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60));
- test("2010-Dec-22 17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60));
- test("2010-Dec-22 17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90));
- test("2010-Dec-22 17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480));
+ SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(1_234_567), east60));
+ test("2010-Dec-22 17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east60));
+ test("2010-Dec-22 17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 1), east90));
+ test("2010-Dec-22 17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 1), hnsecs(4_500_000), east480));
+
+ static void testScope(scope ref string str) @safe
+ {
+ auto result = SysTime.fromSimpleString(str);
+ }
}
- // bug# 17801
+ // https://issues.dlang.org/show_bug.cgi?id=17801
@safe unittest
{
+ import core.time;
import std.conv : to;
import std.meta : AliasSeq;
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
- foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
{
assert(SysTime.fromSimpleString(to!S("2012-Dec-21 14:15:16Z")) ==
SysTime(DateTime(2012, 12, 21, 14, 15, 16), UTC()));
@@ -8857,9 +9542,9 @@ public:
private:
/+
- Returns $(D stdTime) converted to $(LREF SysTime)'s time zone.
+ Returns `stdTime` converted to $(LREF SysTime)'s time zone.
+/
- @property long adjTime() @safe const nothrow
+ @property long adjTime() @safe const nothrow scope
{
return _timezone.utcToTZ(_stdTime);
}
@@ -8868,24 +9553,95 @@ private:
/+
Converts the given hnsecs from $(LREF SysTime)'s time zone to std time.
+/
- @property void adjTime(long adjTime) @safe nothrow
+ @property void adjTime(long adjTime) @safe nothrow scope
{
_stdTime = _timezone.tzToUTC(adjTime);
}
- // Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5058
- /+
- invariant()
+ final class InitTimeZone : TimeZone
{
- assert(_timezone !is null, "Invariant Failure: timezone is null. Were you foolish enough to use " ~
- "SysTime.init? (since timezone for SysTime.init can't be set at compile time).");
+ public:
+
+ static immutable(InitTimeZone) opCall() @safe pure nothrow @nogc { return _initTimeZone; }
+
+ @property override bool hasDST() @safe const nothrow @nogc { return false; }
+
+ override bool dstInEffect(long stdTime) @safe const nothrow @nogc { return false; }
+
+ override long utcToTZ(long stdTime) @safe const nothrow @nogc { return 0; }
+
+ override long tzToUTC(long adjTime) @safe const nothrow @nogc { return 0; }
+
+ override Duration utcOffsetAt(long stdTime) @safe const nothrow @nogc { return Duration.zero; }
+
+ private:
+
+ this() @safe immutable pure
+ {
+ super("SysTime.init's timezone", "SysTime.init's timezone", "SysTime.init's timezone");
+ }
+
+ static immutable InitTimeZone _initTimeZone = new immutable(InitTimeZone);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=17732
+ @safe unittest
+ {
+ assert(SysTime.init.timezone is InitTimeZone());
+ assert(SysTime.init.toISOString() == "00010101T000000+00:00");
+ assert(SysTime.init.toISOExtString() == "0001-01-01T00:00:00+00:00");
+ assert(SysTime.init.toSimpleString() == "0001-Jan-01 00:00:00+00:00");
+ assert(SysTime.init.toString() == "0001-Jan-01 00:00:00+00:00");
+ }
+
+ // Assigning a value to _timezone in SysTime.init currently doesn't work due
+ // to https://issues.dlang.org/show_bug.cgi?id=17740. So, to hack around
+ // that problem, these accessors have been added so that we can insert a
+ // runtime check for null and then use InitTimeZone for SysTime.init (which
+ // which is the only case where _timezone would be null). This thus fixes
+ // the problem with segfaulting when using SysTime.init but at the cost of
+ // what should be an unnecessary null check. Once 17740 has finally been
+ // fixed, _timezoneStorage should be removed, these accessors should be
+ // removed, and the _timezone variable declaration should be restored.
+ pragma(inline, true) @property _timezone() @safe const pure nothrow @nogc
+ {
+ return _timezoneStorage is null ? InitTimeZone() : _timezoneStorage;
+ }
+
+ pragma(inline, true) @property void _timezone(immutable TimeZone tz) @safe pure nothrow @nogc scope
+ {
+ _timezoneStorage = tz;
}
- +/
long _stdTime;
- Rebindable!(immutable TimeZone) _timezone;
+ Rebindable!(immutable TimeZone) _timezoneStorage;
+ //Rebindable!(immutable TimeZone) _timezone = InitTimeZone();
+}
+
+///
+@safe unittest
+{
+ import core.time : days, hours, seconds;
+ import std.datetime.date : DateTime;
+ import std.datetime.timezone : SimpleTimeZone, UTC;
+
+ // make a specific point in time in the UTC timezone
+ auto st = SysTime(DateTime(2018, 1, 1, 10, 30, 0), UTC());
+ // make a specific point in time in the New York timezone
+ auto ny = SysTime(
+ DateTime(2018, 1, 1, 10, 30, 0),
+ new immutable SimpleTimeZone(-5.hours, "America/New_York")
+ );
+
+ // ISO standard time strings
+ assert(st.toISOString() == "20180101T103000Z");
+ assert(st.toISOExtString() == "2018-01-01T10:30:00Z");
+
+ // add two days and 30 seconds
+ st += 2.days + 30.seconds;
+ assert(st.toISOExtString() == "2018-01-03T10:30:30Z");
}
@@ -8902,7 +9658,7 @@ private:
"std time"'s epoch is based on the Proleptic Gregorian Calendar per ISO
8601 and is what $(LREF SysTime) uses internally. However, holding the time
- as an integer in hnescs since that epoch technically isn't actually part of
+ as an integer in hnsecs since that epoch technically isn't actually part of
the standard, much as it's based on it, so the name "std time" isn't
particularly good, but there isn't an official name for it. C# uses "ticks"
for the same thing, but they aren't actually clock ticks, and the term
@@ -8916,7 +9672,7 @@ private:
See_Also:
SysTime.fromUnixTime
+/
-long unixTimeToStdTime(long unixTime) @safe pure nothrow
+long unixTimeToStdTime(long unixTime) @safe pure nothrow @nogc
{
return 621_355_968_000_000_000L + convert!("seconds", "hnsecs")(unixTime);
}
@@ -8934,7 +9690,7 @@ long unixTimeToStdTime(long unixTime) @safe pure nothrow
assert(unixTimeToStdTime(int.max) == 642_830_804_470_000_000L);
assert(SysTime(unixTimeToStdTime(int.max)) ==
- SysTime(DateTime(2038, 1, 19, 3, 14, 07), UTC()));
+ SysTime(DateTime(2038, 1, 19, 3, 14, 7), UTC()));
assert(unixTimeToStdTime(-127_127) == 621_354_696_730_000_000L);
assert(SysTime(unixTimeToStdTime(-127_127)) ==
@@ -8983,8 +9739,8 @@ long unixTimeToStdTime(long unixTime) @safe pure nothrow
argument to get the desired size.
If the return type is int, and the result can't fit in an int, then the
- closest value that can be held in 32 bits will be used (so $(D int.max)
- if it goes over and $(D int.min) if it goes under). However, no attempt
+ closest value that can be held in 32 bits will be used (so `int.max`
+ if it goes over and `int.min` if it goes under). However, no attempt
is made to deal with integer overflow if the return type is long.
Params:
@@ -9079,31 +9835,31 @@ version (StdDdoc)
/++
$(BLUE This function is Windows-Only.)
- Converts a $(D SYSTEMTIME) struct to a $(LREF SysTime).
+ Converts a `SYSTEMTIME` struct to a $(LREF SysTime).
Params:
- st = The $(D SYSTEMTIME) struct to convert.
- tz = The time zone that the time in the $(D SYSTEMTIME) struct is
- assumed to be (if the $(D SYSTEMTIME) was supplied by a Windows
- system call, the $(D SYSTEMTIME) will either be in local time
+ st = The `SYSTEMTIME` struct to convert.
+ tz = The time zone that the time in the `SYSTEMTIME` struct is
+ assumed to be (if the `SYSTEMTIME` was supplied by a Windows
+ system call, the `SYSTEMTIME` will either be in local time
or UTC, depending on the call).
Throws:
$(REF DateTimeException,std,datetime,date) if the given
- $(D SYSTEMTIME) will not fit in a $(LREF SysTime), which is highly
- unlikely to happen given that $(D SysTime.max) is in 29,228 A.D. and
- the maximum $(D SYSTEMTIME) is in 30,827 A.D.
+ `SYSTEMTIME` will not fit in a $(LREF SysTime), which is highly
+ unlikely to happen given that `SysTime.max` is in 29,228 A.D. and
+ the maximum `SYSTEMTIME` is in 30,827 A.D.
+/
- SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe;
+ SysTime SYSTEMTIMEToSysTime(const scope SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe;
/++
$(BLUE This function is Windows-Only.)
- Converts a $(LREF SysTime) to a $(D SYSTEMTIME) struct.
+ Converts a $(LREF SysTime) to a `SYSTEMTIME` struct.
- The $(D SYSTEMTIME) which is returned will be set using the given
- $(LREF SysTime)'s time zone, so to get the $(D SYSTEMTIME) in
+ The `SYSTEMTIME` which is returned will be set using the given
+ $(LREF SysTime)'s time zone, so to get the `SYSTEMTIME` in
UTC, set the $(LREF SysTime)'s time zone to UTC.
Params:
@@ -9111,24 +9867,24 @@ version (StdDdoc)
Throws:
$(REF DateTimeException,std,datetime,date) if the given
- $(LREF SysTime) will not fit in a $(D SYSTEMTIME). This will only
+ $(LREF SysTime) will not fit in a `SYSTEMTIME`. This will only
happen if the $(LREF SysTime)'s date is prior to 1601 A.D.
+/
- SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe;
+ SYSTEMTIME SysTimeToSYSTEMTIME(scope SysTime sysTime) @safe;
/++
$(BLUE This function is Windows-Only.)
- Converts a $(D FILETIME) struct to the number of hnsecs since midnight,
+ Converts a `FILETIME` struct to the number of hnsecs since midnight,
January 1st, 1 A.D.
Params:
- ft = The $(D FILETIME) struct to convert.
+ ft = The `FILETIME` struct to convert.
Throws:
$(REF DateTimeException,std,datetime,date) if the given
- $(D FILETIME) cannot be represented as the return value.
+ `FILETIME` cannot be represented as the return value.
+/
long FILETIMEToStdTime(scope const FILETIME* ft) @safe;
@@ -9136,16 +9892,16 @@ version (StdDdoc)
/++
$(BLUE This function is Windows-Only.)
- Converts a $(D FILETIME) struct to a $(LREF SysTime).
+ Converts a `FILETIME` struct to a $(LREF SysTime).
Params:
- ft = The $(D FILETIME) struct to convert.
+ ft = The `FILETIME` struct to convert.
tz = The time zone that the $(LREF SysTime) will be in
- ($(D FILETIME)s are in UTC).
+ (`FILETIME`s are in UTC).
Throws:
$(REF DateTimeException,std,datetime,date) if the given
- $(D FILETIME) will not fit in a $(LREF SysTime).
+ `FILETIME` will not fit in a $(LREF SysTime).
+/
SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe;
@@ -9154,7 +9910,7 @@ version (StdDdoc)
$(BLUE This function is Windows-Only.)
Converts a number of hnsecs since midnight, January 1st, 1 A.D. to a
- $(D FILETIME) struct.
+ `FILETIME` struct.
Params:
stdTime = The number of hnsecs since midnight, January 1st, 1 A.D.
@@ -9162,7 +9918,7 @@ version (StdDdoc)
Throws:
$(REF DateTimeException,std,datetime,date) if the given value will
- not fit in a $(D FILETIME).
+ not fit in a `FILETIME`.
+/
FILETIME stdTimeToFILETIME(long stdTime) @safe;
@@ -9170,22 +9926,22 @@ version (StdDdoc)
/++
$(BLUE This function is Windows-Only.)
- Converts a $(LREF SysTime) to a $(D FILETIME) struct.
+ Converts a $(LREF SysTime) to a `FILETIME` struct.
- $(D FILETIME)s are always in UTC.
+ `FILETIME`s are always in UTC.
Params:
sysTime = The $(LREF SysTime) to convert.
Throws:
$(REF DateTimeException,std,datetime,date) if the given
- $(LREF SysTime) will not fit in a $(D FILETIME).
+ $(LREF SysTime) will not fit in a `FILETIME`.
+/
- FILETIME SysTimeToFILETIME(SysTime sysTime) @safe;
+ FILETIME SysTimeToFILETIME(scope SysTime sysTime) @safe;
}
else version (Windows)
{
- SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe
+ SysTime SYSTEMTIMEToSysTime(const scope SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe
{
const max = SysTime.max;
@@ -9229,6 +9985,7 @@ else version (Windows)
auto dt = DateTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
+ import core.time : msecs;
return SysTime(dt, msecs(st.wMilliseconds), tz);
}
@@ -9238,12 +9995,17 @@ else version (Windows)
SYSTEMTIME st = void;
GetSystemTime(&st);
auto converted = SYSTEMTIMEToSysTime(&st, UTC());
-
+ import core.time : abs;
assert(abs((converted - sysTime)) <= dur!"seconds"(2));
+
+ static void testScope(scope SYSTEMTIME* st) @safe
+ {
+ auto result = SYSTEMTIMEToSysTime(st);
+ }
}
- SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe
+ SYSTEMTIME SysTimeToSYSTEMTIME(scope SysTime sysTime) @safe
{
immutable dt = cast(DateTime) sysTime;
@@ -9280,6 +10042,11 @@ else version (Windows)
assert(st.wMinute == result.wMinute);
assert(st.wSecond == result.wSecond);
assert(st.wMilliseconds == result.wMilliseconds);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = SysTimeToSYSTEMTIME(st);
+ }
}
private enum hnsecsFrom1601 = 504_911_232_000_000_000L;
@@ -9315,7 +10082,13 @@ else version (Windows)
auto converted = FILETIMEToSysTime(&ft);
+ import core.time : abs;
assert(abs((converted - sysTime)) <= dur!"seconds"(2));
+
+ static void testScope(scope FILETIME* ft) @safe
+ {
+ auto result = FILETIMEToSysTime(ft);
+ }
}
@@ -9334,7 +10107,7 @@ else version (Windows)
return ft;
}
- FILETIME SysTimeToFILETIME(SysTime sysTime) @safe
+ FILETIME SysTimeToFILETIME(scope SysTime sysTime) @safe
{
return stdTimeToFILETIME(sysTime.stdTime);
}
@@ -9352,6 +10125,11 @@ else version (Windows)
assert(ft.dwLowDateTime == result.dwLowDateTime);
assert(ft.dwHighDateTime == result.dwHighDateTime);
+
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = SysTimeToFILETIME(st);
+ }
}
}
@@ -9369,7 +10147,7 @@ alias DosFileTime = uint;
tz = The time zone which the DOS file time is assumed to be in.
Throws:
- $(REF DateTimeException,std,datetime,date) if the $(D DosFileTime) is
+ $(REF DateTimeException,std,datetime,date) if the `DosFileTime` is
invalid.
+/
SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) @safe
@@ -9392,13 +10170,24 @@ SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime(
throw new DateTimeException("Invalid DosFileTime", __FILE__, __LINE__, dte);
}
+///
@safe unittest
{
+ import std.datetime.date : DateTime;
+
assert(DosFileTimeToSysTime(0b00000000001000010000000000000000) == SysTime(DateTime(1980, 1, 1, 0, 0, 0)));
assert(DosFileTimeToSysTime(0b11111111100111111011111101111101) == SysTime(DateTime(2107, 12, 31, 23, 59, 58)));
assert(DosFileTimeToSysTime(0x3E3F8456) == SysTime(DateTime(2011, 1, 31, 16, 34, 44)));
}
+@safe unittest
+{
+ static void testScope(scope ref DosFileTime dft) @safe
+ {
+ auto result = DosFileTimeToSysTime(dft);
+ }
+}
+
/++
Converts from $(LREF SysTime) to DOS file date/time.
@@ -9408,9 +10197,9 @@ SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime(
Throws:
$(REF DateTimeException,std,datetime,date) if the given
- $(LREF SysTime) cannot be converted to a $(D DosFileTime).
+ $(LREF SysTime) cannot be converted to a `DosFileTime`.
+/
-DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe
+DosFileTime SysTimeToDosFileTime(scope SysTime sysTime) @safe
{
auto dateTime = cast(DateTime) sysTime;
@@ -9431,17 +10220,28 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe
return cast(DosFileTime) retval;
}
+///
@safe unittest
{
+ import std.datetime.date : DateTime;
+
assert(SysTimeToDosFileTime(SysTime(DateTime(1980, 1, 1, 0, 0, 0))) == 0b00000000001000010000000000000000);
assert(SysTimeToDosFileTime(SysTime(DateTime(2107, 12, 31, 23, 59, 58))) == 0b11111111100111111011111101111101);
assert(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))) == 0x3E3F8456);
}
+@safe unittest
+{
+ static void testScope(scope ref SysTime st) @safe
+ {
+ auto result = SysTimeToDosFileTime(st);
+ }
+}
+
/++
- The given array of $(D char) or random-access range of $(D char) or
- $(D ubyte) is expected to be in the format specified in
+ The given array of `char` or random-access range of `char` or
+ `ubyte` is expected to be in the format specified in
$(HTTP tools.ietf.org/html/rfc5322, RFC 5322) section 3.3 with the
grammar rule $(I date-time). It is the date-time format commonly used in
internet messages such as e-mail and HTTP. The corresponding
@@ -9456,10 +10256,10 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe
of the given date (though it is technically invalid per the spec if the
day of the week doesn't match the actual day of the week of the given date).
- If the time zone is $(D "-0000") (or considered to be equivalent to
- $(D "-0000") by section 4.3 of the spec), a
- $(REF SimpleTimeZone,std,datetime,timezone) with a utc offset of $(D 0) is
- used rather than $(REF UTC,std,datetime,timezone), whereas $(D "+0000") uses
+ If the time zone is `"-0000"` (or considered to be equivalent to
+ `"-0000"` by section 4.3 of the spec), a
+ $(REF SimpleTimeZone,std,datetime,timezone) with a utc offset of `0` is
+ used rather than $(REF UTC,std,datetime,timezone), whereas `"+0000"` uses
$(REF UTC,std,datetime,timezone).
Note that because $(LREF SysTime) does not currently support having a second
@@ -9467,7 +10267,7 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe
does have a value of 60 for the seconds, it is treated as 59.
The one area in which this function violates RFC 5322 is that it accepts
- $(D "\n") in folding whitespace in the place of $(D "\r\n"), because the
+ `"\n"` in folding whitespace in the place of `"\r\n"`, because the
HTTP spec requires it.
Throws:
@@ -9475,16 +10275,16 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe
follow the grammar for a date-time field or if the resulting
$(LREF SysTime) is invalid.
+/
-SysTime parseRFC822DateTime()(in char[] value) @safe
+SysTime parseRFC822DateTime()(scope const char[] value) @safe
{
import std.string : representation;
return parseRFC822DateTime(value.representation);
}
/++ Ditto +/
-SysTime parseRFC822DateTime(R)(R value) @safe
+SysTime parseRFC822DateTime(R)(scope R value)
if (isRandomAccessRange!R && hasSlicing!R && hasLength!R &&
- (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte)))
+ (is(immutable ElementType!R == immutable char) || is(immutable ElementType!R == immutable ubyte)))
{
import std.algorithm.searching : find, all;
import std.ascii : isDigit, isAlpha, isPrintable;
@@ -9704,7 +10504,7 @@ afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length);
assertThrown!DateTimeException(parseRFC822DateTime(badStr));
}
-version (unittest) void testParse822(alias cr)(string str, SysTime expected, size_t line = __LINE__)
+version (StdUnittest) private void testParse822(alias cr)(string str, SysTime expected, size_t line = __LINE__)
{
import std.format : format;
auto value = cr(str);
@@ -9713,7 +10513,7 @@ version (unittest) void testParse822(alias cr)(string str, SysTime expected, siz
throw new AssertError(format("wrong result. expected [%s], actual[%s]", expected, result), __FILE__, line);
}
-version (unittest) void testBadParse822(alias cr)(string str, size_t line = __LINE__)
+version (StdUnittest) private void testBadParse822(alias cr)(string str, size_t line = __LINE__)
{
try
parseRFC822DateTime(cr(str));
@@ -9724,6 +10524,7 @@ version (unittest) void testBadParse822(alias cr)(string str, size_t line = __LI
@system unittest
{
+ import core.time;
import std.algorithm.iteration : filter, map;
import std.algorithm.searching : canFind;
import std.array : array;
@@ -9748,11 +10549,12 @@ version (unittest) void testBadParse822(alias cr)(string str, size_t line = __LI
static auto start() { Rand3Letters retval; retval.popFront(); return retval; }
}
- foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;},
+ static foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;},
function(string a){return cast(ubyte[]) a;},
function(string a){return a;},
function(string a){return map!(b => cast(char) b)(a.representation);}))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ {(){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
scope(failure) writeln(typeof(cr).stringof);
alias test = testParse822!cr;
alias testBad = testBadParse822!cr;
@@ -9990,7 +10792,12 @@ version (unittest) void testBadParse822(alias cr)(string str, size_t line = __LI
testBad(cast(string) currStr);
testBad((cast(string) currStr) ~ " ");
}
- }();
+ }();}
+
+ static void testScope(scope ref string str) @safe
+ {
+ auto result = parseRFC822DateTime(str);
+ }
}
// Obsolete Format per section 4.3 of RFC 5322.
@@ -10014,11 +10821,12 @@ version (unittest) void testBadParse822(alias cr)(string str, size_t line = __LI
auto tooLate1 = SysTime(Date(10_000, 1, 1), UTC());
auto tooLate2 = SysTime(DateTime(12_007, 12, 31, 12, 22, 19), UTC());
- foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;},
+ static foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;},
function(string a){return cast(ubyte[]) a;},
function(string a){return a;},
function(string a){return map!(b => cast(char) b)(a.representation);}))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ {(){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
scope(failure) writeln(typeof(cr).stringof);
alias test = testParse822!cr;
{
@@ -10128,7 +10936,7 @@ version (unittest) void testBadParse822(alias cr)(string str, size_t line = __LI
// test time zones
{
- auto dt = DateTime(1982, 05, 03, 12, 22, 04);
+ auto dt = DateTime(1982, 5, 3, 12, 22, 4);
test("Wed, 03 May 1982 12:22:04 UT", SysTime(dt, UTC()));
test("Wed, 03 May 1982 12:22:04 GMT", SysTime(dt, UTC()));
test("Wed, 03 May 1982 12:22:04 EST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5))));
@@ -10189,13 +10997,13 @@ version (unittest) void testBadParse822(alias cr)(string str, size_t line = __LI
// test that the checks for minimum length work correctly and avoid
// any RangeErrors.
- test("7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00),
+ test("7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 0, 0, 0),
new immutable SimpleTimeZone(Duration.zero)));
- test("Fri,7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00),
+ test("Fri,7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 0, 0, 0),
new immutable SimpleTimeZone(Duration.zero)));
- test("7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00),
+ test("7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 0, 0, 0),
new immutable SimpleTimeZone(Duration.zero)));
- test("Fri,7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00),
+ test("Fri,7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 0, 0, 0),
new immutable SimpleTimeZone(Duration.zero)));
auto tooShortMsg = collectExceptionMsg!DateTimeException(parseRFC822DateTime(""));
@@ -10208,7 +11016,7 @@ version (unittest) void testBadParse822(alias cr)(string str, size_t line = __LI
assert(collectExceptionMsg!DateTimeException(parseRFC822DateTime(value)) == tooShortMsg);
}
}
- }();
+ }();}
}
@@ -10217,24 +11025,32 @@ private:
/+
Returns the given hnsecs as an ISO string of fractional seconds.
+/
-static string fracSecsToISOString(int hnsecs) @safe pure nothrow
+string fracSecsToISOString(int hnsecs) @safe pure nothrow
{
- assert(hnsecs >= 0);
-
+ import std.array : appender;
+ auto w = appender!string();
try
- {
- if (hnsecs == 0)
- return "";
+ fracSecsToISOString(w, hnsecs);
+ catch (Exception e)
+ assert(0, "fracSecsToISOString() threw.");
+ return w.data;
+}
- string isoString = format(".%07d", hnsecs);
+void fracSecsToISOString(W)(ref W writer, int hnsecs)
+{
+ import std.conv : toChars;
+ import std.range : padLeft;
- while (isoString[$ - 1] == '0')
- isoString.popBack();
+ assert(hnsecs >= 0);
- return isoString;
- }
- catch (Exception e)
- assert(0, "format() threw.");
+ if (hnsecs == 0)
+ return;
+
+ put(writer, '.');
+ auto chars = hnsecs.toChars.padLeft('0', 7);
+ while (chars.back == '0')
+ chars.popBack();
+ put(writer, chars);
}
@safe unittest
@@ -10269,7 +11085,7 @@ static string fracSecsToISOString(int hnsecs) @safe pure nothrow
Returns a Duration corresponding to to the given ISO string of
fractional seconds.
+/
-static Duration fracSecsFromISOString(S)(in S isoString) @trusted pure
+static Duration fracSecsFromISOString(S)(scope const S isoString) @safe pure
if (isSomeString!S)
{
import std.algorithm.searching : all;
@@ -10301,6 +11117,7 @@ if (isSomeString!S)
@safe unittest
{
+ import core.time;
static void testFSInvalid(string isoString)
{
fracSecsFromISOString(isoString);
@@ -10417,7 +11234,7 @@ if (validTimeUnits(units) &&
/+
Strips what RFC 5322, section 3.2.2 refers to as CFWS from the left-hand
side of the given range (it strips comments delimited by $(D '(') and
- $(D ')') as well as folding whitespace).
+ `'`') as well as folding whitespace).
It is assumed that the given range contains the value of a header field and
no terminating CRLF for the line (though the CRLF for folding whitespace is
@@ -10438,7 +11255,7 @@ if (validTimeUnits(units) &&
+/
R _stripCFWS(R)(R range)
if (isRandomAccessRange!R && hasSlicing!R && hasLength!R &&
- (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte)))
+ (is(immutable ElementType!R == immutable char) || is(immutable ElementType!R == immutable ubyte)))
{
immutable e = range.length;
outer: for (size_t i = 0; i < e; )
@@ -10506,9 +11323,9 @@ if (isRandomAccessRange!R && hasSlicing!R && hasLength!R &&
import std.stdio : writeln;
import std.string : representation;
- foreach (cr; AliasSeq!(function(string a){return cast(ubyte[]) a;},
+ static foreach (cr; AliasSeq!(function(string a){return cast(ubyte[]) a;},
function(string a){return map!(b => cast(char) b)(a.representation);}))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ {
scope(failure) writeln(typeof(cr).stringof);
assert(_stripCFWS(cr("")).empty);
@@ -10596,7 +11413,7 @@ if (isRandomAccessRange!R && hasSlicing!R && hasLength!R &&
assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n \n hello")), cr("hello")));
assert(equal(_stripCFWS(cr(" \n \n (hello)\t\n (hello) \n hello")), cr("hello")));
assert(equal(_stripCFWS(cr(" \n\t\n\t(hello)\t\n (hello) \n hello")), cr("hello")));
- }();
+ }
}
// This is so that we don't have to worry about std.conv.to throwing. It also
@@ -10624,22 +11441,23 @@ if (isIntegral!T && isSigned!T) // The constraints on R were already covered by
{
import std.conv : to;
import std.range : chain, iota;
- import std.stdio : writeln;
foreach (i; chain(iota(0, 101), [250, 999, 1000, 1001, 2345, 9999]))
{
- scope(failure) writeln(i);
- assert(_convDigits!int(to!string(i)) == i);
+ assert(_convDigits!int(to!string(i)) == i, i.to!string);
}
foreach (str; ["-42", "+42", "1a", "1 ", " ", " 42 "])
{
- scope(failure) writeln(str);
- assert(_convDigits!int(str) == -1);
+ assert(_convDigits!int(str) == -1, str);
}
}
-version (unittest)
+// NOTE: all the non-simple array literals are wrapped in functions, because
+// otherwise importing causes re-evaluation of the static initializers using
+// CTFE with unittests enabled
+version (StdUnittest)
{
+private @safe:
// Variables to help in testing.
Duration currLocalDiffFromUTC;
immutable (TimeZone)[] testTZs;
@@ -10661,31 +11479,43 @@ version (unittest)
}
}
- MonthDay[] testMonthDays = [MonthDay(1, 1),
+ MonthDay[] testMonthDays()
+ {
+ static result = [MonthDay(1, 1),
MonthDay(1, 2),
MonthDay(3, 17),
MonthDay(7, 4),
MonthDay(10, 27),
MonthDay(12, 30),
MonthDay(12, 31)];
+ return result;
+ }
auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31];
- auto testTODs = [TimeOfDay(0, 0, 0),
+ TimeOfDay[] testTODs()
+ {
+ static result = [TimeOfDay(0, 0, 0),
TimeOfDay(0, 0, 1),
TimeOfDay(0, 1, 0),
TimeOfDay(1, 0, 0),
TimeOfDay(13, 13, 13),
TimeOfDay(23, 59, 59)];
+ return result;
+ }
auto testHours = [0, 1, 12, 22, 23];
auto testMinSecs = [0, 1, 30, 58, 59];
// Throwing exceptions is incredibly expensive, so we want to use a smaller
// set of values for tests using assertThrown.
- auto testTODsThrown = [TimeOfDay(0, 0, 0),
+ TimeOfDay[] testTODsThrown()
+ {
+ static result = [TimeOfDay(0, 0, 0),
TimeOfDay(13, 13, 13),
TimeOfDay(23, 59, 59)];
+ return result;
+ }
Date[] testDatesBC;
Date[] testDatesAD;
@@ -10700,7 +11530,9 @@ version (unittest)
// I'd use a Tuple, but I get forward reference errors if I try.
struct GregDay { int day; Date date; }
- auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
+ GregDay[] testGregDaysBC()
+ {
+ static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
GregDay(-735_233, Date(-2012, 1, 1)),
GregDay(-735_202, Date(-2012, 2, 1)),
GregDay(-735_175, Date(-2012, 2, 28)),
@@ -10782,8 +11614,12 @@ version (unittest)
GregDay(-30, Date(0, 12, 1)),
GregDay(-1, Date(0, 12, 30)),
GregDay(0, Date(0, 12, 31))];
+ return result;
+ }
- auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)),
+ GregDay[] testGregDaysAD()
+ {
+ static result = [GregDay(1, Date(1, 1, 1)),
GregDay(2, Date(1, 1, 2)),
GregDay(32, Date(1, 2, 1)),
GregDay(365, Date(1, 12, 31)),
@@ -10851,10 +11687,14 @@ version (unittest)
GregDay(734_562, Date(2012, 2, 29)),
GregDay(734_563, Date(2012, 3, 1)),
GregDay(734_858, Date(2012, 12, 21))];
+ return result;
+ }
// I'd use a Tuple, but I get forward reference errors if I try.
struct DayOfYear { int day; MonthDay md; }
- auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)),
+ DayOfYear[] testDaysOfYear()
+ {
+ static result = [DayOfYear(1, MonthDay(1, 1)),
DayOfYear(2, MonthDay(1, 2)),
DayOfYear(3, MonthDay(1, 3)),
DayOfYear(31, MonthDay(1, 31)),
@@ -10882,8 +11722,12 @@ version (unittest)
DayOfYear(363, MonthDay(12, 29)),
DayOfYear(364, MonthDay(12, 30)),
DayOfYear(365, MonthDay(12, 31))];
+ return result;
+ }
- auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)),
+ DayOfYear[] testDaysOfLeapYear()
+ {
+ static result = [DayOfYear(1, MonthDay(1, 1)),
DayOfYear(2, MonthDay(1, 2)),
DayOfYear(3, MonthDay(1, 3)),
DayOfYear(31, MonthDay(1, 31)),
@@ -10912,8 +11756,10 @@ version (unittest)
DayOfYear(364, MonthDay(12, 29)),
DayOfYear(365, MonthDay(12, 30)),
DayOfYear(366, MonthDay(12, 31))];
+ return result;
+ }
- void initializeTests() @safe
+ void initializeTests()
{
import std.algorithm.sorting : sort;
import std.typecons : Rebindable;
@@ -10922,11 +11768,13 @@ version (unittest)
version (Posix)
{
+ import std.datetime.timezone : PosixTimeZone;
immutable otherTZ = lt < 0 ? PosixTimeZone.getTimeZone("Australia/Sydney")
: PosixTimeZone.getTimeZone("America/Denver");
}
else version (Windows)
{
+ import std.datetime.timezone : WindowsTimeZone;
immutable otherTZ = lt < 0 ? WindowsTimeZone.getTimeZone("AUS Eastern Standard Time")
: WindowsTimeZone.getTimeZone("Mountain Standard Time");
}
diff --git a/libphobos/src/std/datetime/timezone.d b/libphobos/src/std/datetime/timezone.d
index 9b744ff7ab0..5df42e728c3 100644
--- a/libphobos/src/std/datetime/timezone.d
+++ b/libphobos/src/std/datetime/timezone.d
@@ -1,18 +1,37 @@
// Written in the D programming language
/++
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Time zones) $(TD
+ $(LREF TimeZone)
+ $(LREF UTC)
+ $(LREF LocalTime)
+ $(LREF PosixTimeZone)
+ $(LREF WindowsTimeZone)
+ $(LREF SimpleTimeZone)
+))
+$(TR $(TD Utilities) $(TD
+ $(LREF clearTZEnvVar)
+ $(LREF parseTZConversions)
+ $(LREF setTZEnvVar)
+ $(LREF TZConversions)
+))
+))
+
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: Jonathan M Davis
- Source: $(PHOBOSSRC std/datetime/_timezone.d)
+ Authors: $(HTTP jmdavisprog.com, Jonathan M Davis)
+ Source: $(PHOBOSSRC std/datetime/timezone.d)
+/
module std.datetime.timezone;
-import core.time;
-import std.datetime.date;
-import std.datetime.systime;
-import std.exception : enforce;
-import std.range.primitives;
-import std.traits : isIntegral, isSomeString, Unqual;
+import core.time : abs, convert, dur, Duration, hours, minutes;
+import std.datetime.systime : Clock, stdTimeToUnixTime, SysTime;
+import std.range.primitives : back, empty, front, isOutputRange, popFront;
+import std.traits : isIntegral, isSomeString;
version (OSX)
version = Darwin;
@@ -26,7 +45,7 @@ else version (WatchOS)
version (Windows)
{
import core.stdc.time : time_t;
- import core.sys.windows.windows;
+ import core.sys.windows.winbase;
import core.sys.windows.winsock2;
import std.windows.registry;
@@ -42,7 +61,7 @@ else version (Posix)
import core.sys.posix.sys.types : time_t;
}
-version (unittest) import std.exception : assertThrown;
+version (StdUnittest) import std.exception : assertThrown;
/++
@@ -54,8 +73,13 @@ abstract class TimeZone
public:
/++
- The name of the time zone per the TZ Database. This is the name used to
- get a $(LREF TimeZone) by name with $(D TimeZone.getTimeZone).
+ The name of the time zone. Exactly how the time zone name is formatted
+ depends on the derived class. In the case of $(LREF PosixTimeZone), it's
+ the TZ Database name, whereas with $(LREF WindowsTimeZone), it's the
+ name that Windows chose to give the registry key for that time zone
+ (typically the name that they give $(LREF stdTime) if the OS is in
+ English). For other time zone types, what it is depends on how they're
+ implemented.
See_Also:
$(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ
@@ -98,7 +122,7 @@ public:
/++
Whether this time zone has Daylight Savings Time at any point in time.
Note that for some time zone types it may not have DST for current dates
- but will still return true for $(D hasDST) because the time zone did at
+ but will still return true for `hasDST` because the time zone did at
some point have DST.
+/
@property abstract bool hasDST() @safe const nothrow;
@@ -213,6 +237,7 @@ public:
//assert(tz.dstName == dstName); //Locale-dependent
assert(tz.hasDST == hasDST);
+ import std.datetime.date : DateTime;
immutable stdDate = DateTime(2010, north ? 1 : 7, 1, 6, 0, 0);
immutable dstDate = DateTime(2010, north ? 7 : 1, 1, 6, 0, 0);
auto std = SysTime(stdDate, tz);
@@ -233,11 +258,14 @@ public:
{
setTZEnvVar(tzName);
- static void testTM(in SysTime st)
+ static void testTM(scope const SysTime st)
{
- import core.stdc.time : localtime, tm;
+ import core.stdc.time : tm;
+ import core.sys.posix.time : localtime_r;
+
time_t unixTime = st.toUnixTime();
- tm* osTimeInfo = localtime(&unixTime);
+ tm osTimeInfo = void;
+ localtime_r(&unixTime, &osTimeInfo);
tm ourTimeInfo = st.toTM();
assert(ourTimeInfo.tm_sec == osTimeInfo.tm_sec);
@@ -292,6 +320,7 @@ public:
return tz;
}
+ import std.datetime.date : DateTime;
auto dstSwitches = [/+America/Los_Angeles+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2),
/+America/New_York+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2),
///+America/Santiago+/ tuple(DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0),
@@ -299,6 +328,7 @@ public:
/+Europe/Paris+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3),
/+Australia/Adelaide+/ tuple(DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)];
+ import std.datetime.date : DateTimeException;
version (Posix)
{
version (FreeBSD) enum utcZone = "Etc/UTC";
@@ -360,6 +390,7 @@ public:
// a DST switch.
foreach (hour; -12 .. 13)
{
+ import std.exception : enforce;
auto st = SysTime(dstSwitches[i][0] + dur!"hours"(hour), tz);
immutable targetHour = hour < 0 ? hour + 24 : hour;
@@ -419,7 +450,7 @@ public:
bool first = true;
auto springSwitch = SysTime(dstSwitches[i][0] + dur!"hours"(spring), UTC()) - stdOffset;
auto fallSwitch = SysTime(dstSwitches[i][1] + dur!"hours"(fall), UTC()) - dstOffset;
- // @@@BUG@@@ 3659 makes this necessary.
+ // https://issues.dlang.org/show_bug.cgi?id=3659 makes this necessary.
auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1);
foreach (hour; -24 .. 25)
@@ -435,6 +466,7 @@ public:
__FILE__, line);
}
+ import std.exception : enforce;
enforce((utc + offset).hour == local.hour, msg("1"));
enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2"));
}
@@ -510,7 +542,7 @@ public:
+/
static immutable(LocalTime) opCall() @trusted pure nothrow
{
- alias FuncType = @safe pure nothrow immutable(LocalTime) function();
+ alias FuncType = immutable(LocalTime) function() @safe pure nothrow;
return (cast(FuncType)&singleton)();
}
@@ -518,14 +550,12 @@ public:
version (StdDdoc)
{
/++
- The name of the time zone per the TZ Database. This is the name used
- to get a $(LREF TimeZone) by name with $(D TimeZone.getTimeZone).
-
- Note that this always returns the empty string. This is because time
- zones cannot be uniquely identified by the attributes given by the
- OS (such as the $(D stdName) and $(D dstName)), and neither Posix
- systems nor Windows systems provide an easy way to get the TZ
- Database name of the local time zone.
+ In principle, this is the name of the local time zone. However,
+ this always returns the empty string. This is because time zones
+ cannot be uniquely identified by the attributes given by the
+ OS (such as the `stdName` and `dstName`), and neither Posix systems
+ nor Windows systems provide an easy way to get the TZ Database name
+ of the local time zone.
See_Also:
$(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ
@@ -566,7 +596,7 @@ public:
GetTimeZoneInformation(&tzInfo);
// Cannot use to!string() like this should, probably due to bug
- // http://d.puremagic.com/issues/show_bug.cgi?id=5016
+ // https://issues.dlang.org/show_bug.cgi?id=5016
//return to!string(tzInfo.StandardName);
wchar[32] str;
@@ -651,7 +681,7 @@ public:
GetTimeZoneInformation(&tzInfo);
// Cannot use to!string() like this should, probably due to bug
- // http://d.puremagic.com/issues/show_bug.cgi?id=5016
+ // https://issues.dlang.org/show_bug.cgi?id=5016
//return to!string(tzInfo.DaylightName);
wchar[32] str;
@@ -714,7 +744,7 @@ public:
/++
Whether this time zone has Daylight Savings Time at any point in time.
Note that for some time zone types it may not have DST for current
- dates but will still return true for $(D hasDST) because the time zone
+ dates but will still return true for `hasDST` because the time zone
did at some point have DST.
+/
@property override bool hasDST() @trusted const nothrow
@@ -727,6 +757,7 @@ public:
{
try
{
+ import std.datetime.date : Date;
auto currYear = (cast(Date) Clock.currTime()).year;
auto janOffset = SysTime(Date(currYear, 1, 4), cast(immutable) this).stdTime -
SysTime(Date(currYear, 1, 4), UTC()).stdTime;
@@ -779,17 +810,23 @@ public:
+/
override bool dstInEffect(long stdTime) @trusted const nothrow
{
- import core.stdc.time : localtime, tm;
+ import core.stdc.time : tm;
+
time_t unixTime = stdTimeToUnixTime(stdTime);
version (Posix)
{
- tm* timeInfo = localtime(&unixTime);
+ import core.sys.posix.time : localtime_r;
+
+ tm timeInfo = void;
+ localtime_r(&unixTime, &timeInfo);
return cast(bool)(timeInfo.tm_isdst);
}
else version (Windows)
{
+ import core.stdc.time : localtime;
+
// Apparently Windows isn't smart enough to deal with negative time_t.
if (unixTime >= 0)
{
@@ -823,7 +860,7 @@ public:
time.
See_Also:
- $(D TimeZone.utcToTZ)
+ `TimeZone.utcToTZ`
+/
override long utcToTZ(long stdTime) @trusted const nothrow
{
@@ -831,9 +868,11 @@ public:
return stdTime + convert!("seconds", "hnsecs")(tm_gmtoff(stdTime));
else version (Posix)
{
- import core.stdc.time : localtime, tm;
+ import core.stdc.time : tm;
+ import core.sys.posix.time : localtime_r;
time_t unixTime = stdTimeToUnixTime(stdTime);
- tm* timeInfo = localtime(&unixTime);
+ tm timeInfo = void;
+ localtime_r(&unixTime, &timeInfo);
return stdTime + convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff);
}
@@ -858,7 +897,7 @@ public:
time to UTC from the appropriate time zone.
See_Also:
- $(D TimeZone.tzToUTC)
+ `TimeZone.tzToUTC`
Params:
adjTime = The time in this time zone that needs to be adjusted to
@@ -868,15 +907,17 @@ public:
{
version (Posix)
{
- import core.stdc.time : localtime, tm;
+ import core.stdc.time : tm;
+ import core.sys.posix.time : localtime_r;
time_t unixTime = stdTimeToUnixTime(adjTime);
immutable past = unixTime - cast(time_t) convert!("days", "seconds")(1);
- tm* timeInfo = localtime(past < unixTime ? &past : &unixTime);
+ tm timeInfo = void;
+ localtime_r(past < unixTime ? &past : &unixTime, &timeInfo);
immutable pastOffset = timeInfo.tm_gmtoff;
immutable future = unixTime + cast(time_t) convert!("days", "seconds")(1);
- timeInfo = localtime(future > unixTime ? &future : &unixTime);
+ localtime_r(future > unixTime ? &future : &unixTime, &timeInfo);
immutable futureOffset = timeInfo.tm_gmtoff;
if (pastOffset == futureOffset)
@@ -886,7 +927,7 @@ public:
unixTime -= cast(time_t) convert!("hours", "seconds")(1);
unixTime -= pastOffset;
- timeInfo = localtime(&unixTime);
+ localtime_r(&unixTime, &timeInfo);
return adjTime - convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff);
}
@@ -915,6 +956,7 @@ public:
{
scope(exit) clearTZEnvVar();
+ import std.datetime.date : DateTime;
auto tzInfos = [tuple("America/Los_Angeles", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2),
tuple("America/New_York", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2),
//tuple("America/Santiago", DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0),
@@ -925,6 +967,7 @@ public:
foreach (i; 0 .. tzInfos.length)
{
+ import std.exception : enforce;
auto tzName = tzInfos[i][0];
setTZEnvVar(tzName);
immutable spring = tzInfos[i][3];
@@ -996,7 +1039,7 @@ public:
bool first = true;
auto springSwitch = SysTime(tzInfos[i][1] + dur!"hours"(spring), UTC()) - stdOffset;
auto fallSwitch = SysTime(tzInfos[i][2] + dur!"hours"(fall), UTC()) - dstOffset;
- // @@@BUG@@@ 3659 makes this necessary.
+ // https://issues.dlang.org/show_bug.cgi?id=3659 makes this necessary.
auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1);
foreach (hour; -24 .. 25)
@@ -1070,13 +1113,14 @@ private:
{
long tm_gmtoff(long stdTime) @trusted const nothrow
{
- import core.stdc.time : localtime, gmtime, tm;
+ import core.stdc.time : tm;
+ import core.sys.posix.time : localtime_r, gmtime_r;
time_t unixTime = stdTimeToUnixTime(stdTime);
- tm* buf = localtime(&unixTime);
- tm timeInfo = *buf;
- buf = gmtime(&unixTime);
- tm timeInfoGmt = *buf;
+ tm timeInfo = void;
+ localtime_r(&unixTime, &timeInfo);
+ tm timeInfoGmt = void;
+ gmtime_r(&unixTime, &timeInfoGmt);
return timeInfo.tm_sec - timeInfoGmt.tm_sec +
convert!("minutes", "seconds")(timeInfo.tm_min - timeInfoGmt.tm_min) +
@@ -1094,7 +1138,7 @@ final class UTC : TimeZone
public:
/++
- $(D UTC) is a singleton class. $(D UTC) returns its only instance.
+ `UTC` is a singleton class. `UTC` returns its only instance.
+/
static immutable(UTC) opCall() @safe pure nothrow
{
@@ -1128,7 +1172,7 @@ public:
time.
See_Also:
- $(D TimeZone.utcToTZ)
+ `TimeZone.utcToTZ`
+/
override long utcToTZ(long stdTime) @safe const nothrow
{
@@ -1144,6 +1188,7 @@ public:
scope(exit) clearTZEnvVar();
setTZEnvVar("UTC");
+ import std.datetime.date : Date;
auto std = SysTime(Date(2010, 1, 1));
auto dst = SysTime(Date(2010, 7, 1));
assert(UTC().utcToTZ(std.stdTime) == std.stdTime);
@@ -1156,7 +1201,7 @@ public:
Returns the given hnsecs without changing them at all.
See_Also:
- $(D TimeZone.tzToUTC)
+ `TimeZone.tzToUTC`
Params:
adjTime = The time in this time zone that needs to be adjusted to
@@ -1176,6 +1221,7 @@ public:
scope(exit) clearTZEnvVar();
setTZEnvVar("UTC");
+ import std.datetime.date : Date;
auto std = SysTime(Date(2010, 1, 1));
auto dst = SysTime(Date(2010, 7, 1));
assert(UTC().tzToUTC(std.stdTime) == std.stdTime);
@@ -1214,10 +1260,10 @@ private:
UTC but no DST.
It's primarily used as the time zone in the result of
- $(REF SysTime,std,datetime,systime)'s $(D fromISOString),
- $(D fromISOExtString), and $(D fromSimpleString).
+ $(REF SysTime,std,datetime,systime)'s `fromISOString`,
+ `fromISOExtString`, and `fromSimpleString`.
- $(D name) and $(D dstName) are always the empty string since this time zone
+ `name` and `dstName` are always the empty string since this time zone
has no DST, and while it may be meant to represent a time zone which is in
the TZ Database, obviously it's not likely to be following the exact rules
of any of the time zones in the TZ Database, so it makes no sense to set it.
@@ -1315,11 +1361,13 @@ public:
Params:
utcOffset = This time zone's offset from UTC with west of UTC being
negative (it is added to UTC to get the adjusted time).
- stdName = The $(D stdName) for this time zone.
+ stdName = The `stdName` for this time zone.
+/
this(Duration utcOffset, string stdName = "") @safe immutable pure
{
// FIXME This probably needs to be changed to something like (-12 - 13).
+ import std.datetime.date : DateTimeException;
+ import std.exception : enforce;
enforce!DateTimeException(abs(utcOffset) < dur!"minutes"(1440),
"Offset from UTC must be within range (-24:00 - 24:00).");
super("", stdName, "");
@@ -1359,14 +1407,32 @@ package:
+/
static string toISOString(Duration utcOffset) @safe pure
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(5);
+ toISOString(w, utcOffset);
+ return w.data;
+ }
+
+ // ditto
+ static void toISOString(W)(ref W writer, Duration utcOffset)
+ if (isOutputRange!(W, char))
+ {
+ import std.datetime.date : DateTimeException;
+ import std.exception : enforce;
+ import std.format.write : formattedWrite;
immutable absOffset = abs(utcOffset);
enforce!DateTimeException(absOffset < dur!"minutes"(1440),
"Offset from UTC must be within range (-24:00 - 24:00).");
int hours;
int minutes;
absOffset.split!("hours", "minutes")(hours, minutes);
- return format(utcOffset < Duration.zero ? "-%02d%02d" : "+%02d%02d", hours, minutes);
+ formattedWrite(
+ writer,
+ utcOffset < Duration.zero ? "-%02d%02d" : "+%02d%02d",
+ hours,
+ minutes
+ );
}
@safe unittest
@@ -1376,6 +1442,7 @@ package:
return SimpleTimeZone.toISOString(offset);
}
+ import std.datetime.date : DateTimeException;
assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440)));
assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440)));
@@ -1411,7 +1478,19 @@ package:
+/
static string toISOExtString(Duration utcOffset) @safe pure
{
- import std.format : format;
+ import std.array : appender;
+ auto w = appender!string();
+ w.reserve(6);
+ toISOExtString(w, utcOffset);
+ return w.data;
+ }
+
+ // ditto
+ static void toISOExtString(W)(ref W writer, Duration utcOffset)
+ {
+ import std.datetime.date : DateTimeException;
+ import std.format.write : formattedWrite;
+ import std.exception : enforce;
immutable absOffset = abs(utcOffset);
enforce!DateTimeException(absOffset < dur!"minutes"(1440),
@@ -1419,7 +1498,12 @@ package:
int hours;
int minutes;
absOffset.split!("hours", "minutes")(hours, minutes);
- return format(utcOffset < Duration.zero ? "-%02d:%02d" : "+%02d:%02d", hours, minutes);
+ formattedWrite(
+ writer,
+ utcOffset < Duration.zero ? "-%02d:%02d" : "+%02d:%02d",
+ hours,
+ minutes
+ );
}
@safe unittest
@@ -1429,6 +1513,7 @@ package:
return SimpleTimeZone.toISOExtString(offset);
}
+ import std.datetime.date : DateTimeException;
assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440)));
assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440)));
@@ -1466,34 +1551,43 @@ package:
static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure
if (isSomeString!S)
{
- import std.algorithm.searching : startsWith, countUntil, all;
- import std.ascii : isDigit;
- import std.conv : to;
- import std.format : format;
-
- auto dstr = to!dstring(isoString);
-
- enforce!DateTimeException(dstr.startsWith('-', '+'), "Invalid ISO String");
+ import std.algorithm.searching : startsWith;
+ import std.conv : text, to, ConvException;
+ import std.datetime.date : DateTimeException;
+ import std.exception : enforce;
- auto sign = dstr.startsWith('-') ? -1 : 1;
-
- dstr.popFront();
- enforce!DateTimeException(all!isDigit(dstr), format("Invalid ISO String: %s", dstr));
+ auto whichSign = isoString.startsWith('-', '+');
+ enforce!DateTimeException(whichSign > 0, text("Invalid ISO String ", isoString));
+ isoString = isoString[1 .. $];
+ auto sign = whichSign == 1 ? -1 : 1;
int hours;
int minutes;
- if (dstr.length == 2)
- hours = to!int(dstr);
- else if (dstr.length == 4)
+ try
{
- hours = to!int(dstr[0 .. 2]);
- minutes = to!int(dstr[2 .. 4]);
+ // cast to int from uint is used because it checks for
+ // non digits without extra loops
+ if (isoString.length == 2)
+ {
+ hours = cast(int) to!uint(isoString);
+ }
+ else if (isoString.length == 4)
+ {
+ hours = cast(int) to!uint(isoString[0 .. 2]);
+ minutes = cast(int) to!uint(isoString[2 .. 4]);
+ }
+ else
+ {
+ throw new DateTimeException(text("Invalid ISO String ", isoString));
+ }
+ }
+ catch (ConvException)
+ {
+ throw new DateTimeException(text("Invalid ISO String ", isoString));
}
- else
- throw new DateTimeException(format("Invalid ISO String: %s", dstr));
- enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", dstr));
+ enforce!DateTimeException(hours < 24 && minutes < 60, text("Invalid ISO String ", isoString));
return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes)));
}
@@ -1517,6 +1611,7 @@ package:
"-ab:cd", "+abcd", "-0Z:00", "-Z", "-00Z",
"01:00", "12:00", "23:59"])
{
+ import std.datetime.date : DateTimeException;
assertThrown!DateTimeException(SimpleTimeZone.fromISOString(str), format("[%s]", str));
}
@@ -1563,7 +1658,7 @@ package:
import core.exception : AssertError;
import std.format : format;
- static void test(in string isoString, int expectedOffset, size_t line = __LINE__)
+ static void test(scope const string isoString, int expectedOffset, size_t line = __LINE__)
{
auto stz = SimpleTimeZone.fromISOExtString(isoString);
if (stz.utcOffset != dur!"minutes"(expectedOffset))
@@ -1607,44 +1702,54 @@ package:
Params:
isoExtString = A string which represents a time zone in the ISO format.
+/
- static immutable(SimpleTimeZone) fromISOExtString(S)(S isoExtString) @safe pure
+ static immutable(SimpleTimeZone) fromISOExtString(S)(scope S isoExtString) @safe pure
if (isSomeString!S)
{
- import std.algorithm.searching : startsWith, countUntil, all;
- import std.ascii : isDigit;
- import std.conv : to;
+ import std.algorithm.searching : startsWith;
+ import std.conv : ConvException, to;
+ import std.datetime.date : DateTimeException;
+ import std.exception : enforce;
import std.format : format;
+ import std.string : indexOf;
- auto dstr = to!dstring(isoExtString);
-
- enforce!DateTimeException(dstr.startsWith('-', '+'), "Invalid ISO String");
+ auto whichSign = isoExtString.startsWith('-', '+');
+ enforce!DateTimeException(whichSign > 0, format("Invalid ISO String: %s", isoExtString));
+ auto sign = whichSign == 1 ? -1 : 1;
- auto sign = dstr.startsWith('-') ? -1 : 1;
+ isoExtString = isoExtString[1 .. $];
+ enforce!DateTimeException(!isoExtString.empty, format("Invalid ISO String: %s", isoExtString));
- dstr.popFront();
- enforce!DateTimeException(!dstr.empty, "Invalid ISO String");
-
- immutable colon = dstr.countUntil(':');
-
- dstring hoursStr;
- dstring minutesStr;
+ immutable colon = isoExtString.indexOf(':');
+ S hoursStr;
+ S minutesStr;
+ int hours, minutes;
if (colon != -1)
{
- hoursStr = dstr[0 .. colon];
- minutesStr = dstr[colon + 1 .. $];
- enforce!DateTimeException(minutesStr.length == 2, format("Invalid ISO String: %s", dstr));
+ hoursStr = isoExtString[0 .. colon];
+ minutesStr = isoExtString[colon + 1 .. $];
+ enforce!DateTimeException(minutesStr.length == 2, format("Invalid ISO String: %s", isoExtString));
}
else
- hoursStr = dstr;
+ {
+ hoursStr = isoExtString;
+ }
+
+ enforce!DateTimeException(hoursStr.length == 2, format("Invalid ISO String: %s", isoExtString));
- enforce!DateTimeException(hoursStr.length == 2, format("Invalid ISO String: %s", dstr));
- enforce!DateTimeException(all!isDigit(hoursStr), format("Invalid ISO String: %s", dstr));
- enforce!DateTimeException(all!isDigit(minutesStr), format("Invalid ISO String: %s", dstr));
+ try
+ {
+ // cast to int from uint is used because it checks for
+ // non digits without extra loops
+ hours = cast(int) to!uint(hoursStr);
+ minutes = cast(int) (minutesStr.empty ? 0 : to!uint(minutesStr));
+ }
+ catch (ConvException)
+ {
+ throw new DateTimeException(format("Invalid ISO String: %s", isoExtString));
+ }
- immutable hours = to!int(hoursStr);
- immutable minutes = minutesStr.empty ? 0 : to!int(minutesStr);
- enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", dstr));
+ enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", isoExtString));
return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes)));
}
@@ -1668,6 +1773,7 @@ package:
"-ab:cd", "abcd", "-0Z:00", "-Z", "-00Z",
"0100", "1200", "2359"])
{
+ import std.datetime.date : DateTimeException;
assertThrown!DateTimeException(SimpleTimeZone.fromISOExtString(str), format("[%s]", str));
}
@@ -1714,7 +1820,7 @@ package:
import core.exception : AssertError;
import std.format : format;
- static void test(in string isoExtString, int expectedOffset, size_t line = __LINE__)
+ static void test(scope const string isoExtString, int expectedOffset, size_t line = __LINE__)
{
auto stz = SimpleTimeZone.fromISOExtString(isoExtString);
if (stz.utcOffset != dur!"minutes"(expectedOffset))
@@ -1758,21 +1864,19 @@ private:
Represents a time zone from a TZ Database time zone file. Files from the TZ
Database are how Posix systems hold their time zone information.
Unfortunately, Windows does not use the TZ Database. To use the TZ Database,
- use $(D PosixTimeZone) (which reads its information from the TZ Database
+ use `PosixTimeZone` (which reads its information from the TZ Database
files on disk) on Windows by providing the TZ Database files and telling
- $(D PosixTimeZone.getTimeZone) where the directory holding them is.
+ `PosixTimeZone.getTimeZone` where the directory holding them is.
- To get a $(D PosixTimeZone), either call $(D PosixTimeZone.getTimeZone)
- (which allows specifying the location the time zone files) or call
- $(D TimeZone.getTimeZone) (which will give a $(D PosixTimeZone) on Posix
- systems and a $(LREF WindowsTimeZone) on Windows systems).
+ To get a `PosixTimeZone`, call `PosixTimeZone.getTimeZone`
+ (which allows specifying the location the time zone files).
Note:
Unless your system's local time zone deals with leap seconds (which is
highly unlikely), then the only way to get a time zone which
- takes leap seconds into account is to use $(D PosixTimeZone) with a
+ takes leap seconds into account is to use `PosixTimeZone` with a
time zone whose name starts with "right/". Those time zone files do
- include leap seconds, and $(D PosixTimeZone) will take them into account
+ include leap seconds, and `PosixTimeZone` will take them into account
(though posix systems which use a "right/" time zone as their local time
zone will $(I not) take leap seconds into account even though they're
in the file).
@@ -1796,7 +1900,7 @@ public:
/++
Whether this time zone has Daylight Savings Time at any point in time.
Note that for some time zone types it may not have DST for current
- dates but will still return true for $(D hasDST) because the time zone
+ dates but will still return true for `hasDST` because the time zone
did at some point have DST.
+/
@property override bool hasDST() @safe const nothrow
@@ -1865,7 +1969,7 @@ public:
+/
override long tzToUTC(long adjTime) @safe const nothrow
{
- assert(!_transitions.empty);
+ assert(!_transitions.empty, "UTC offset's not available");
immutable leapSecs = calculateLeapSeconds(adjTime);
time_t unixTime = stdTimeToUnixTime(adjTime);
@@ -1901,32 +2005,37 @@ public:
}
- version (Android)
+ version (StdDdoc)
+ {
+ /++
+ The default directory where the TZ Database files are stored. It's
+ empty for Windows, since Windows doesn't have them. You can also use
+ the TZDatabaseDir version to pass an arbitrary path at compile-time,
+ rather than hard-coding it here. Android concatenates all time zone
+ data into a single file called tzdata and stores it in the directory
+ below.
+ +/
+ enum defaultTZDatabaseDir = "";
+ }
+ else version (TZDatabaseDir)
+ {
+ import std.string : strip;
+ enum defaultTZDatabaseDir = strip(import("TZDatabaseDirFile"));
+ }
+ else version (Android)
{
- // Android concatenates all time zone data into a single file and stores it here.
enum defaultTZDatabaseDir = "/system/usr/share/zoneinfo/";
}
else version (Solaris)
{
- /++
- The default directory where the TZ Database files are. It's empty
- for Windows, since Windows doesn't have them.
- +/
enum defaultTZDatabaseDir = "/usr/share/lib/zoneinfo/";
}
else version (Posix)
{
- /++
- The default directory where the TZ Database files are. It's empty
- for Windows, since Windows doesn't have them.
- +/
enum defaultTZDatabaseDir = "/usr/share/zoneinfo/";
}
else version (Windows)
{
- /++ The default directory where the TZ Database files are. It's empty
- for Windows, since Windows doesn't have them.
- +/
enum defaultTZDatabaseDir = "";
}
@@ -1952,7 +2061,7 @@ public:
Throws:
$(REF DateTimeException,std,datetime,date) if the given time zone
- could not be found or $(D FileException) if the TZ Database file
+ could not be found or `FileException` if the TZ Database file
could not be opened.
+/
// TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed
@@ -1961,6 +2070,8 @@ public:
{
import std.algorithm.sorting : sort;
import std.conv : to;
+ import std.datetime.date : DateTimeException;
+ import std.exception : enforce;
import std.format : format;
import std.path : asNormalizedPath, chainPath;
import std.range : retro;
@@ -1987,6 +2098,7 @@ public:
version (Android) tzFile.seek(*tzfileOffset);
immutable gmtZone = name.representation().canFind("GMT");
+ import std.datetime.date : DateTimeException;
try
{
_enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif");
@@ -2303,12 +2415,13 @@ public:
located.
Throws:
- $(D FileException) if it fails to read from disk.
+ `FileException` if it fails to read from disk.
+/
- static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @trusted
+ static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @safe
{
import std.algorithm.sorting : sort;
import std.array : appender;
+ import std.exception : enforce;
import std.format : format;
version (Posix)
@@ -2320,6 +2433,7 @@ public:
subName = replace(strip(subName), "/", dirSeparator);
}
+ import std.datetime.date : DateTimeException;
enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir)));
enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir)));
@@ -2329,27 +2443,35 @@ public:
{
import std.algorithm.iteration : filter;
import std.algorithm.mutation : copy;
- tzdataIndex(tzDatabaseDir).byKey.filter!(a => a.startsWith(subName)).copy(timezones);
+
+ const index = () @trusted { return tzdataIndex(tzDatabaseDir); }();
+ index.byKey.filter!(a => a.startsWith(subName)).copy(timezones);
}
else
{
- foreach (DirEntry de; dirEntries(tzDatabaseDir, SpanMode.depth))
- {
- if (de.isFile)
+ import std.path : baseName;
+ // dirEntries is @system because it uses a DirIterator with a
+ // RefCounted variable, but here, no references to the payload is
+ // escaped to the outside, so this should be @trusted
+ () @trusted {
+ foreach (DirEntry de; dirEntries(tzDatabaseDir, SpanMode.depth))
{
- auto tzName = de.name[tzDatabaseDir.length .. $];
-
- if (!tzName.extension().empty ||
- !tzName.startsWith(subName) ||
- tzName == "leapseconds" ||
- tzName == "+VERSION")
+ if (de.isFile)
{
- continue;
- }
+ auto tzName = de.name[tzDatabaseDir.length .. $];
+
+ if (!tzName.extension().empty ||
+ !tzName.startsWith(subName) ||
+ baseName(tzName) == "leapseconds" ||
+ tzName == "+VERSION")
+ {
+ continue;
+ }
- timezones.put(tzName);
+ timezones.put(tzName);
+ }
}
- }
+ }();
}
sort(timezones.data);
@@ -2377,6 +2499,7 @@ public:
auto tzNames = getInstalledTZNames();
+ import std.datetime.date : DateTimeException;
foreach (tzName; tzNames)
assertNotThrown!DateTimeException(testPTZSuccess(tzName));
@@ -2403,7 +2526,7 @@ private:
/+
Holds information on when a time transition occures (usually a
- transition to or from DST) as well as a pointer to the $(D TTInfo) which
+ transition to or from DST) as well as a pointer to the `TTInfo` which
holds information on the utc offset past the transition.
+/
struct Transition
@@ -2440,7 +2563,7 @@ private:
+/
struct TTInfo
{
- this(in TempTTInfo tempTTInfo, string abbrev) @safe immutable pure
+ this(scope const TempTTInfo tempTTInfo, string abbrev) @safe immutable pure
{
utcOffset = tempTTInfo.tt_gmtoff;
isDST = tempTTInfo.tt_isdst;
@@ -2454,7 +2577,7 @@ private:
/+
- Struct used to hold information relating to $(D TTInfo) while organizing
+ Struct used to hold information relating to `TTInfo` while organizing
the time zone information prior to putting it in its final form.
+/
struct TempTTInfo
@@ -2473,7 +2596,7 @@ private:
/+
- Struct used to hold information relating to $(D Transition) while
+ Struct used to hold information relating to `Transition` while
organizing the time zone information prior to putting it in its final
form.
+/
@@ -2493,8 +2616,8 @@ private:
/+
- Struct used to hold information relating to $(D Transition) and
- $(D TTInfo) while organizing the time zone information prior to putting
+ Struct used to hold information relating to `Transition` and
+ `TTInfo` while organizing the time zone information prior to putting
it in its final form.
+/
struct TransitionType
@@ -2517,7 +2640,7 @@ private:
Reads an int from a TZ file.
+/
static T readVal(T)(ref File tzFile) @trusted
- if ((isIntegral!T || isSomeChar!T) || is(Unqual!T == bool))
+ if ((isIntegral!T || isSomeChar!T) || is(immutable T == immutable bool))
{
import std.bitmanip : bigEndianToNative;
T[1] buff;
@@ -2544,7 +2667,7 @@ private:
/+
- Reads a $(D TempTTInfo) from a TZ file.
+ Reads a `TempTTInfo` from a TZ file.
+/
static T readVal(T)(ref File tzFile) @safe
if (is(T == TempTTInfo))
@@ -2557,10 +2680,11 @@ private:
/+
Throws:
- $(REF DateTimeException,std,datetime,date) if $(D result) is false.
+ $(REF DateTimeException,std,datetime,date) if `result` is false.
+/
static void _enforceValidTZFile(bool result, size_t line = __LINE__) @safe pure
{
+ import std.datetime.date : DateTimeException;
if (!result)
throw new DateTimeException("Not a valid tzdata file.", __FILE__, line);
}
@@ -2631,12 +2755,13 @@ private:
{
import std.concurrency : initOnce;
- static __gshared uint[string] _tzIndex;
+ __gshared uint[string] _tzIndex;
// _tzIndex is initialized once and then shared across all threads.
initOnce!_tzIndex(
{
import std.conv : to;
+ import std.datetime.date : DateTimeException;
import std.format : format;
import std.path : asNormalizedPath, chainPath;
@@ -2709,24 +2834,21 @@ version (StdDdoc)
does not use the TZ Database. To use the TZ Database, use
$(LREF PosixTimeZone) (which reads its information from the TZ Database
files on disk) on Windows by providing the TZ Database files and telling
- $(D PosixTimeZone.getTimeZone) where the directory holding them is.
+ `PosixTimeZone.getTimeZone` where the directory holding them is.
The TZ Database files and Windows' time zone information frequently
do not match. Windows has many errors with regards to when DST switches
occur (especially for historical dates). Also, the TZ Database files
include far more time zones than Windows does. So, for accurate
time zone information, use the TZ Database files with
- $(LREF PosixTimeZone) rather than $(D WindowsTimeZone). However, because
- $(D WindowsTimeZone) uses Windows system calls to deal with the time,
+ $(LREF PosixTimeZone) rather than `WindowsTimeZone`. However, because
+ `WindowsTimeZone` uses Windows system calls to deal with the time,
it's far more likely to match the behavior of other Windows programs.
Be aware of the differences when selecting a method.
- $(D WindowsTimeZone) does not exist on Posix systems.
+ `WindowsTimeZone` does not exist on Posix systems.
- To get a $(D WindowsTimeZone), either call
- $(D WindowsTimeZone.getTimeZone) or call $(D TimeZone.getTimeZone)
- (which will give a $(LREF PosixTimeZone) on Posix systems and a
- $(D WindowsTimeZone) on Windows systems).
+ To get a `WindowsTimeZone`, call `WindowsTimeZone.getTimeZone`.
See_Also:
$(HTTP www.iana.org/time-zones, Home of the TZ Database files)
@@ -2738,7 +2860,7 @@ version (StdDdoc)
/++
Whether this time zone has Daylight Savings Time at any point in
time. Note that for some time zone types it may not have DST for
- current dates but will still return true for $(D hasDST) because the
+ current dates but will still return true for `hasDST` because the
time zone did at some point have DST.
+/
@property override bool hasDST() @safe const nothrow;
@@ -2810,7 +2932,7 @@ version (StdDdoc)
Returns a list of the names of the time zones installed on the
system. The list returned by WindowsTimeZone contains the Windows
TZ names, not the TZ Database names. However,
- $(D TimeZone.getinstalledTZNames) will return the TZ Database names
+ `TimeZone.getinstalledTZNames` will return the TZ Database names
which are equivalent to the Windows TZ names.
+/
static string[] getInstalledTZNames() @safe;
@@ -2887,7 +3009,8 @@ else version (Windows)
scope tziVal = tzKey.getValue("TZI");
auto binVal = tziVal.value_BINARY;
- assert(binVal.length == REG_TZI_FORMAT.sizeof);
+ assert(binVal.length == REG_TZI_FORMAT.sizeof,
+ "Unexpected size while getTimeZone with name " ~ name);
auto tziFmt = cast(REG_TZI_FORMAT*) binVal.ptr;
TIME_ZONE_INFORMATION tzInfo;
@@ -2909,6 +3032,7 @@ else version (Windows)
return new immutable WindowsTimeZone(name, tzInfo);
}
+ import std.datetime.date : DateTimeException;
throw new DateTimeException(format("Failed to find time zone: %s", name));
}
@@ -2938,6 +3062,7 @@ else version (Windows)
auto tzNames = getInstalledTZNames();
+ import std.datetime.date : DateTimeException;
foreach (tzName; tzNames)
assertNotThrown!DateTimeException(testWTZSuccess(tzName));
}
@@ -2952,11 +3077,13 @@ else version (Windows)
if (tzInfo.DaylightDate.wMonth == 0)
return false;
+ import std.datetime.date : DateTime, Month;
auto utcDateTime = cast(DateTime) SysTime(stdTime, UTC());
//The limits of what SystemTimeToTzSpecificLocalTime will accept.
if (utcDateTime.year < 1601)
{
+ import std.datetime.date : Month;
if (utcDateTime.month == Month.feb && utcDateTime.day == 29)
utcDateTime.day = 28;
utcDateTime.year = 1601;
@@ -2994,7 +3121,7 @@ else version (Windows)
immutable result = SystemTimeToTzSpecificLocalTime(cast(TIME_ZONE_INFORMATION*) tzInfo,
&utcTime,
&otherTime);
- assert(result);
+ assert(result, "Failed to create SystemTimeToTzSpecificLocalTime");
immutable otherDateTime = DateTime(otherTime.wYear,
otherTime.wMonth,
@@ -3008,7 +3135,7 @@ else version (Windows)
if (minutes == tzInfo.DaylightBias)
return true;
- assert(minutes == tzInfo.StandardBias);
+ assert(minutes == tzInfo.StandardBias, "Unexpected difference");
return false;
}
@@ -3021,6 +3148,7 @@ else version (Windows)
TIME_ZONE_INFORMATION tzInfo;
GetTimeZoneInformation(&tzInfo);
+ import std.datetime.date : DateTime;
foreach (year; [1600, 1601, 30_827, 30_828])
WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(year, 1, 1)).stdTime);
}
@@ -3041,6 +3169,7 @@ else version (Windows)
{
try
{
+ import std.datetime.date : DateTime, Month;
bool dstInEffectForLocalDateTime(DateTime localDateTime)
{
// The limits of what SystemTimeToTzSpecificLocalTime will accept.
@@ -3086,6 +3215,7 @@ else version (Windows)
&localTime,
&utcTime);
assert(result);
+ assert(result, "Failed to create _tzToUTC");
immutable utcDateTime = DateTime(utcTime.wYear,
utcTime.wMonth,
@@ -3100,11 +3230,12 @@ else version (Windows)
if (minutes == tzInfo.DaylightBias)
return true;
- assert(minutes == tzInfo.StandardBias);
+ assert(minutes == tzInfo.StandardBias, "Unexpected difference");
return false;
}
+ import std.datetime.date : DateTime;
auto localDateTime = cast(DateTime) SysTime(adjTime, UTC());
auto localDateTimeBefore = localDateTime - dur!"hours"(1);
auto localDateTimeAfter = localDateTime + dur!"hours"(1);
@@ -3280,6 +3411,7 @@ TZConversions parseTZConversions(string windowsZonesXMLText) @safe pure
foreach (line; windowsZonesXMLText.lineSplitter())
{
+ import std.exception : enforce;
// Sample line:
// <mapZone other="Canada Central Standard Time" territory="CA" type="America/Regina America/Swift_Current"/>
@@ -3362,7 +3494,8 @@ For terms of use, see http://www.unicode.org/copyright.html
<!-- (UTC-09:00) Alaska -->
<mapZone other="Alaskan Standard Time" territory="001" type="America/Anchorage"/>
- <mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Nome America/Sitka America/Yakutat"/>
+ <mapZone other="Alaskan Standard Time" territory="US" `
+ ~ `type="America/Anchorage America/Juneau America/Nome America/Sitka America/Yakutat"/>
</mapTimezones>
</windowsZones>
</supplementalData>`;
diff --git a/libphobos/src/std/demangle.d b/libphobos/src/std/demangle.d
index e49bb9f5215..a78d3d3605f 100644
--- a/libphobos/src/std/demangle.d
+++ b/libphobos/src/std/demangle.d
@@ -3,87 +3,67 @@
/**
* Demangle D mangled names.
*
- * Copyright: Copyright Digital Mars 2000 - 2009.
+ * Copyright: Copyright The D Language Foundation 2000 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright),
* Thomas K$(UUML)hne, Frits van Bommel
- * Source: $(PHOBOSSRC std/_demangle.d)
+ * Source: $(PHOBOSSRC std/demangle.d)
* $(SCRIPT inhibitQuickIndex = 1;)
*/
/*
- * Copyright Digital Mars 2000 - 2009.
+ * Copyright The D Language Foundation 2000 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
module std.demangle;
-/+
-private class MangleException : Exception
+/**
+Demangle D mangled names.
+
+Params:
+ name = the mangled name
+Returns:
+ A `string`. If it is not a D mangled name, it returns its argument name.
+ */
+string demangle(string name) @safe pure nothrow
{
- this()
- {
- super("MangleException");
- }
+ import core.demangle : demangle;
+ import std.exception : assumeUnique;
+ auto ret = demangle(name);
+ return () @trusted { return ret.assumeUnique; } ();
}
-+/
-
-/*****************************
- * Demangle D mangled names.
- *
- * If it is not a D mangled name, it returns its argument name.
- * Example:
- * This program reads standard in and writes it to standard out,
- * pretty-printing any found D mangled names.
--------------------
-import core.stdc.stdio : stdin;
-import std.stdio;
-import std.ascii;
-import std.demangle;
-void test(int x, float y) { }
+///
+@safe pure unittest
+{
+ // int b in module a
+ assert(demangle("_D1a1bi") == "int a.b");
+ // char array foo in module test
+ assert(demangle("_D4test3fooAa") == "char[] test.foo");
+}
-int main()
+/**
+This program reads standard in and writes it to standard out,
+pretty-printing any found D mangled names.
+ */
+@system unittest
{
- string buffer;
- bool inword;
- int c;
+ import std.ascii : isAlphaNum;
+ import std.algorithm.iteration : chunkBy, joiner, map;
+ import std.algorithm.mutation : copy;
+ import std.conv : to;
+ import std.demangle : demangle;
+ import std.functional : pipe;
+ import std.stdio : stdin, stdout;
- writefln("Try typing in: %s", test.mangleof);
- while ((c = fgetc(stdin)) != EOF)
+ void main()
{
- if (inword)
- {
- if (c == '_' || isAlphaNum(c))
- buffer ~= cast(char) c;
- else
- {
- inword = false;
- write(demangle(buffer), cast(char) c);
- }
- }
- else
- { if (c == '_' || isAlpha(c))
- {
- inword = true;
- buffer.length = 0;
- buffer ~= cast(char) c;
- }
- else
- write(cast(char) c);
- }
+ stdin.byLineCopy
+ .map!(
+ l => l.chunkBy!(a => isAlphaNum(a) || a == '_')
+ .map!(a => a[1].pipe!(to!string, demangle)).joiner
+ )
+ .copy(stdout.lockingTextWriter);
}
- if (inword)
- write(demangle(buffer));
- return 0;
-}
--------------------
- */
-
-string demangle(string name)
-{
- import core.demangle : demangle;
- import std.exception : assumeUnique;
- auto ret = demangle(name);
- return assumeUnique(ret);
}
diff --git a/libphobos/src/std/digest/crc.d b/libphobos/src/std/digest/crc.d
index c606f4c1108..38563b14cd0 100644
--- a/libphobos/src/std/digest/crc.d
+++ b/libphobos/src/std/digest/crc.d
@@ -18,7 +18,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of) $(MYREF crc64EC
)
*
- * This module conforms to the APIs defined in $(D std.digest). To understand the
+ * This module conforms to the APIs defined in `std.digest`. To understand the
* differences between the template and the OOP API, see $(MREF std, digest).
*
* This module publicly imports $(MREF std, digest) and can be used as a stand-alone
@@ -38,7 +38,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of) $(MYREF crc64EC
* References:
* $(LINK2 http://en.wikipedia.org/wiki/Cyclic_redundancy_check, Wikipedia on CRC)
*
- * Source: $(PHOBOSSRC std/digest/_crc.d)
+ * Source: $(PHOBOSSRC std/digest/crc.d)
*
* Standards:
* Implements the 'common' IEEE CRC32 variant
@@ -60,10 +60,6 @@ module std.digest.crc;
public import std.digest;
-version (unittest)
- import std.exception;
-
-
///
@safe unittest
{
@@ -133,19 +129,19 @@ private T[256][8] genTables(T)(T polynomial)
/**
* Template API CRC32 implementation.
- * See $(D std.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*/
alias CRC32 = CRC!(32, 0xEDB88320);
/**
* Template API CRC64-ECMA implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*/
alias CRC64ECMA = CRC!(64, 0xC96C5795D7870F42);
/**
* Template API CRC64-ISO implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*/
alias CRC64ISO = CRC!(64, 0xD800000000000000);
@@ -158,9 +154,10 @@ alias CRC64ISO = CRC!(64, 0xD800000000000000);
* You may want to use the CRC32, CRC65ECMA and CRC64ISO aliases
* for convenience.
*
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*/
-struct CRC(uint N, ulong P) if (N == 32 || N == 64)
+struct CRC(uint N, ulong P)
+if (N == 32 || N == 64)
{
private:
static if (N == 32)
@@ -187,7 +184,7 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64)
/**
* Use this to feed the digest with data.
* Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
+ * interface for `ubyte` and `const(ubyte)[]`.
*/
void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc
{
@@ -206,15 +203,19 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64)
enum hasLittleEndianUnalignedReads = true;
else
enum hasLittleEndianUnalignedReads = false; // leave decision to optimizer
- static if (hasLittleEndianUnalignedReads)
+
+ uint one = void;
+ uint two = void;
+
+ if (!__ctfe && hasLittleEndianUnalignedReads)
{
- uint one = (cast(uint*) data.ptr)[0];
- uint two = (cast(uint*) data.ptr)[1];
+ one = (cast(uint*) data.ptr)[0];
+ two = (cast(uint*) data.ptr)[1];
}
else
{
- uint one = (data.ptr[3] << 24 | data.ptr[2] << 16 | data.ptr[1] << 8 | data.ptr[0]);
- uint two = (data.ptr[7] << 24 | data.ptr[6] << 16 | data.ptr[5] << 8 | data.ptr[4]);
+ one = (data.ptr[3] << 24 | data.ptr[2] << 16 | data.ptr[1] << 8 | data.ptr[0]);
+ two = (data.ptr[7] << 24 | data.ptr[6] << 16 | data.ptr[5] << 8 | data.ptr[4]);
}
static if (N == 32)
@@ -271,7 +272,7 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64)
}
/**
- * Works like $(D finish) but does not reset the internal state, so it's possible
+ * Works like `finish` but does not reset the internal state, so it's possible
* to continue putting data into this CRC after a call to peek.
*/
R peek() const @safe pure nothrow @nogc
@@ -282,6 +283,22 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64)
}
}
+@safe unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=13471
+ static ubyte[4] foo(string str)
+ {
+ ubyte[4] result = str.crc32Of();
+ if (result == (ubyte[4]).init)
+ throw new Exception("this should not be thrown");
+ return result;
+ }
+ enum buggy1 = foo("Hello World!");
+ enum buggy2 = crc32Of("Hello World!");
+ assert(buggy1 == buggy2);
+ assert(buggy1 == "Hello World!".crc32Of());
+}
+
///
@safe unittest
{
@@ -347,117 +364,123 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64)
@system unittest
{
+ import std.conv : hexString;
ubyte[4] digest;
CRC32 crc;
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
- assert(crc.peek() == cast(ubyte[]) x"bd50274c");
+ assert(crc.peek() == cast(ubyte[]) hexString!"bd50274c");
crc.start();
crc.put(cast(ubyte[])"");
- assert(crc.finish() == cast(ubyte[]) x"00000000");
+ assert(crc.finish() == cast(ubyte[]) hexString!"00000000");
digest = crc32Of("");
- assert(digest == cast(ubyte[]) x"00000000");
+ assert(digest == cast(ubyte[]) hexString!"00000000");
//Test vector from http://rosettacode.org/wiki/CRC-32
assert(crcHexString(crc32Of("The quick brown fox jumps over the lazy dog")) == "414FA339");
digest = crc32Of("a");
- assert(digest == cast(ubyte[]) x"43beb7e8");
+ assert(digest == cast(ubyte[]) hexString!"43beb7e8");
digest = crc32Of("abc");
- assert(digest == cast(ubyte[]) x"c2412435");
+ assert(digest == cast(ubyte[]) hexString!"c2412435");
digest = crc32Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
- assert(digest == cast(ubyte[]) x"5f3f1a17");
+ assert(digest == cast(ubyte[]) hexString!"5f3f1a17");
digest = crc32Of("message digest");
- assert(digest == cast(ubyte[]) x"7f9d1520");
+ assert(digest == cast(ubyte[]) hexString!"7f9d1520");
digest = crc32Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
- assert(digest == cast(ubyte[]) x"d2e6c21f");
+ assert(digest == cast(ubyte[]) hexString!"d2e6c21f");
digest = crc32Of("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
- assert(digest == cast(ubyte[]) x"724aa97c");
+ assert(digest == cast(ubyte[]) hexString!"724aa97c");
- assert(crcHexString(cast(ubyte[4]) x"c3fcd3d7") == "D7D3FCC3");
+ enum ubyte[4] input = cast(ubyte[4]) hexString!"c3fcd3d7";
+ assert(crcHexString(input) == "D7D3FCC3");
}
@system unittest
{
+ import std.conv : hexString;
ubyte[8] digest;
CRC64ECMA crc;
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
- assert(crc.peek() == cast(ubyte[]) x"2f121b7575789626");
+ assert(crc.peek() == cast(ubyte[]) hexString!"2f121b7575789626");
crc.start();
crc.put(cast(ubyte[])"");
- assert(crc.finish() == cast(ubyte[]) x"0000000000000000");
+ assert(crc.finish() == cast(ubyte[]) hexString!"0000000000000000");
digest = crc64ECMAOf("");
- assert(digest == cast(ubyte[]) x"0000000000000000");
+ assert(digest == cast(ubyte[]) hexString!"0000000000000000");
//Test vector from http://rosettacode.org/wiki/CRC-32
assert(crcHexString(crc64ECMAOf("The quick brown fox jumps over the lazy dog")) == "5B5EB8C2E54AA1C4");
digest = crc64ECMAOf("a");
- assert(digest == cast(ubyte[]) x"052b652e77840233");
+ assert(digest == cast(ubyte[]) hexString!"052b652e77840233");
digest = crc64ECMAOf("abc");
- assert(digest == cast(ubyte[]) x"2776271a4a09d82c");
+ assert(digest == cast(ubyte[]) hexString!"2776271a4a09d82c");
digest = crc64ECMAOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
- assert(digest == cast(ubyte[]) x"4b7cdce3746c449f");
+ assert(digest == cast(ubyte[]) hexString!"4b7cdce3746c449f");
digest = crc64ECMAOf("message digest");
- assert(digest == cast(ubyte[]) x"6f9b8a3156c9bc5d");
+ assert(digest == cast(ubyte[]) hexString!"6f9b8a3156c9bc5d");
digest = crc64ECMAOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
- assert(digest == cast(ubyte[]) x"2656b716e1bf0503");
+ assert(digest == cast(ubyte[]) hexString!"2656b716e1bf0503");
digest = crc64ECMAOf("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
- assert(digest == cast(ubyte[]) x"bd3eb7765d0a22ae");
+ assert(digest == cast(ubyte[]) hexString!"bd3eb7765d0a22ae");
- assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3");
+ enum ubyte[8] input = cast(ubyte[8]) hexString!"c3fcd3d7efbeadde";
+ assert(crcHexString(input) == "DEADBEEFD7D3FCC3");
}
@system unittest
{
+ import std.conv : hexString;
ubyte[8] digest;
CRC64ISO crc;
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
- assert(crc.peek() == cast(ubyte[]) x"f0494ab780989b42");
+ assert(crc.peek() == cast(ubyte[]) hexString!"f0494ab780989b42");
crc.start();
crc.put(cast(ubyte[])"");
- assert(crc.finish() == cast(ubyte[]) x"0000000000000000");
+ assert(crc.finish() == cast(ubyte[]) hexString!"0000000000000000");
digest = crc64ISOOf("");
- assert(digest == cast(ubyte[]) x"0000000000000000");
+ assert(digest == cast(ubyte[]) hexString!"0000000000000000");
//Test vector from http://rosettacode.org/wiki/CRC-32
assert(crcHexString(crc64ISOOf("The quick brown fox jumps over the lazy dog")) == "4EF14E19F4C6E28E");
digest = crc64ISOOf("a");
- assert(digest == cast(ubyte[]) x"0000000000002034");
+ assert(digest == cast(ubyte[]) hexString!"0000000000002034");
digest = crc64ISOOf("abc");
- assert(digest == cast(ubyte[]) x"0000000020c47637");
+ assert(digest == cast(ubyte[]) hexString!"0000000020c47637");
digest = crc64ISOOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
- assert(digest == cast(ubyte[]) x"5173f717971365e5");
+ assert(digest == cast(ubyte[]) hexString!"5173f717971365e5");
digest = crc64ISOOf("message digest");
- assert(digest == cast(ubyte[]) x"a2c355bbc0b93f86");
+ assert(digest == cast(ubyte[]) hexString!"a2c355bbc0b93f86");
digest = crc64ISOOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
- assert(digest == cast(ubyte[]) x"598B258292E40084");
+ assert(digest == cast(ubyte[]) hexString!"598B258292E40084");
digest = crc64ISOOf("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
- assert(digest == cast(ubyte[]) x"760cd2d3588bf809");
+ assert(digest == cast(ubyte[]) hexString!"760cd2d3588bf809");
- assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3");
+ enum ubyte[8] input = cast(ubyte[8]) hexString!"c3fcd3d7efbeadde";
+ assert(crcHexString(input) == "DEADBEEFD7D3FCC3");
}
/**
@@ -465,8 +488,8 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64)
* CRC32 implementation.
*
* Params:
- * data = $(D InputRange) of $(D ElementType) implicitly convertible to
- * $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays
+ * data = `InputRange` of `ElementType` implicitly convertible to
+ * `ubyte`, `ubyte[]` or `ubyte[num]` or one or more arrays
* of any type.
*
* Returns:
@@ -500,8 +523,8 @@ ubyte[4] crc32Of(T...)(T data)
* CRC64-ECMA implementation.
*
* Params:
- * data = $(D InputRange) of $(D ElementType) implicitly convertible to
- * $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays
+ * data = `InputRange` of `ElementType` implicitly convertible to
+ * `ubyte`, `ubyte[]` or `ubyte[num]` or one or more arrays
* of any type.
*
* Returns:
@@ -536,8 +559,8 @@ ubyte[8] crc64ECMAOf(T...)(T data)
* CRC64-ISO implementation.
*
* Params:
- * data = $(D InputRange) of $(D ElementType) implicitly convertible to
- * $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays
+ * data = `InputRange` of `ElementType` implicitly convertible to
+ * `ubyte`, `ubyte[]` or `ubyte[num]` or one or more arrays
* of any type.
*
* Returns:
@@ -577,7 +600,7 @@ public alias crcHexString = toHexString!(Order.decreasing, 16);
/**
* OOP API CRC32 implementation.
- * See $(D std.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*
* This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC32), see
* there for more information.
@@ -586,7 +609,7 @@ alias CRC32Digest = WrapperDigest!CRC32;
/**
* OOP API CRC64-ECMA implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*
* This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ECMA),
* see there for more information.
@@ -595,7 +618,7 @@ alias CRC64ECMADigest = WrapperDigest!CRC64ECMA;
/**
* OOP API CRC64-ISO implementation.
- * See $(D std.digest.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*
* This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ISO),
* see there for more information.
@@ -653,53 +676,55 @@ alias CRC64ISODigest = WrapperDigest!CRC64ISO;
@system unittest
{
+ import std.conv : hexString;
import std.range;
+ import std.exception;
auto crc = new CRC32Digest();
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
- assert(crc.peek() == cast(ubyte[]) x"bd50274c");
+ assert(crc.peek() == cast(ubyte[]) hexString!"bd50274c");
crc.reset();
crc.put(cast(ubyte[])"");
- assert(crc.finish() == cast(ubyte[]) x"00000000");
+ assert(crc.finish() == cast(ubyte[]) hexString!"00000000");
crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
ubyte[20] result;
auto result2 = crc.finish(result[]);
- assert(result[0 .. 4] == result2 && result2 == cast(ubyte[]) x"bd50274c");
+ assert(result[0 .. 4] == result2 && result2 == cast(ubyte[]) hexString!"bd50274c");
debug
assertThrown!Error(crc.finish(result[0 .. 3]));
assert(crc.length == 4);
- assert(crc.digest("") == cast(ubyte[]) x"00000000");
+ assert(crc.digest("") == cast(ubyte[]) hexString!"00000000");
- assert(crc.digest("a") == cast(ubyte[]) x"43beb7e8");
+ assert(crc.digest("a") == cast(ubyte[]) hexString!"43beb7e8");
- assert(crc.digest("abc") == cast(ubyte[]) x"c2412435");
+ assert(crc.digest("abc") == cast(ubyte[]) hexString!"c2412435");
assert(crc.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
- == cast(ubyte[]) x"5f3f1a17");
+ == cast(ubyte[]) hexString!"5f3f1a17");
- assert(crc.digest("message digest") == cast(ubyte[]) x"7f9d1520");
+ assert(crc.digest("message digest") == cast(ubyte[]) hexString!"7f9d1520");
assert(crc.digest("abcdefghijklmnopqrstuvwxyz")
- == cast(ubyte[]) x"bd50274c");
+ == cast(ubyte[]) hexString!"bd50274c");
assert(crc.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
- == cast(ubyte[]) x"d2e6c21f");
+ == cast(ubyte[]) hexString!"d2e6c21f");
assert(crc.digest("1234567890123456789012345678901234567890",
"1234567890123456789012345678901234567890")
- == cast(ubyte[]) x"724aa97c");
+ == cast(ubyte[]) hexString!"724aa97c");
ubyte[] onemilliona = new ubyte[1000000];
onemilliona[] = 'a';
auto digest = crc32Of(onemilliona);
- assert(digest == cast(ubyte[]) x"BCBF25DC");
+ assert(digest == cast(ubyte[]) hexString!"BCBF25DC");
auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
digest = crc32Of(oneMillionRange);
- assert(digest == cast(ubyte[]) x"BCBF25DC");
+ assert(digest == cast(ubyte[]) hexString!"BCBF25DC");
}
diff --git a/libphobos/src/std/digest/digest.d b/libphobos/src/std/digest/digest.d
index 325afd4b60b..01fdbd75e4b 100644
--- a/libphobos/src/std/digest/digest.d
+++ b/libphobos/src/std/digest/digest.d
@@ -1,21 +1,3 @@
+// @@@DEPRECATED_2.101@@@
+deprecated("import std.digest instead of std.digest.digest. std.digest.digest will be removed in 2.101")
module std.digest.digest;
-
-import _newDigest = std.digest;
-
-// scheduled for deprecation in 2.077
-// See also: https://github.com/dlang/phobos/pull/5013#issuecomment-313987845
-alias isDigest = _newDigest.isDigest;
-alias DigestType = _newDigest.DigestType;
-alias hasPeek = _newDigest.hasPeek;
-alias hasBlockSize = _newDigest.hasBlockSize;
-alias digest = _newDigest.digest;
-alias hexDigest = _newDigest.hexDigest;
-alias makeDigest = _newDigest.makeDigest;
-alias Digest = _newDigest.Digest;
-alias Order = _newDigest.Order;
-alias toHexString = _newDigest.toHexString;
-alias asArray = _newDigest.asArray;
-alias digestLength = _newDigest.digestLength;
-alias WrapperDigest = _newDigest.WrapperDigest;
-alias secureEqual = _newDigest.secureEqual;
-alias LetterCase = _newDigest.LetterCase;
diff --git a/libphobos/src/std/digest/hmac.d b/libphobos/src/std/digest/hmac.d
index bf0cbf3605b..6864fc37c4b 100644
--- a/libphobos/src/std/digest/hmac.d
+++ b/libphobos/src/std/digest/hmac.d
@@ -11,7 +11,7 @@ Macros:
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Source: $(PHOBOSSRC std/digest/_hmac.d)
+Source: $(PHOBOSSRC std/digest/hmac.d)
*/
module std.digest.hmac;
@@ -73,18 +73,20 @@ if (hashBlockSize % 8 == 0)
{
// if secret is too long, shorten it by computing its hash
typeof(digest.finish()) buffer = void;
+ typeof(secret) secretBytes = secret;
+
if (secret.length > blockSize / 8)
{
digest.start();
digest.put(secret);
buffer = digest.finish();
- secret = buffer[];
+ secretBytes = buffer[];
}
// if secret is too short, it will be padded with zeroes
// (the key buffer is already zero-initialized)
import std.algorithm.mutation : copy;
- secret.copy(key[]);
+ secretBytes.copy(key[]);
start();
}
@@ -92,7 +94,7 @@ if (hashBlockSize % 8 == 0)
///
@safe pure nothrow @nogc unittest
{
- import std.digest.hmac, std.digest.sha;
+ import std.digest.sha : SHA1;
import std.string : representation;
auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
hmac.put("Hello, world".representation);
@@ -129,7 +131,7 @@ if (hashBlockSize % 8 == 0)
///
@safe pure nothrow @nogc unittest
{
- import std.digest.hmac, std.digest.sha;
+ import std.digest.sha : SHA1;
import std.string : representation;
string data1 = "Hello, world", data2 = "Hola mundo";
auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
@@ -196,7 +198,7 @@ if (hashBlockSize % 8 == 0)
///
@safe pure nothrow @nogc unittest
{
- import std.digest.hmac, std.digest.sha;
+ import std.digest.sha : SHA1;
import std.string : representation;
string data1 = "Hello, world", data2 = "Hola mundo";
auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
@@ -211,7 +213,7 @@ if (hashBlockSize % 8 == 0)
}
}
-/// Convenience constructor for $(LREF HMAC).
+/// ditto
template hmac(H)
if (isDigest!H && hasBlockSize!H)
{
@@ -237,7 +239,7 @@ if (isDigest!H)
///
@safe pure nothrow @nogc unittest
{
- import std.digest.hmac, std.digest.sha;
+ import std.digest.sha : SHA1;
import std.string : representation;
string data1 = "Hello, world", data2 = "Hola mundo";
auto digest = hmac!SHA1("My s3cR3T keY".representation)
@@ -272,7 +274,7 @@ if (isDigest!H)
@safe pure nothrow @nogc unittest
{
import std.algorithm.iteration : map;
- import std.digest.hmac, std.digest.sha;
+ import std.digest.sha : SHA1;
import std.string : representation;
string data = "Hello, world";
auto digest = data.representation
@@ -287,10 +289,21 @@ if (isDigest!H)
}
}
-version (unittest)
+///
+@safe pure nothrow @nogc unittest
{
- import std.digest : toHexString, LetterCase;
- alias hex = toHexString!(LetterCase.lower);
+ import std.digest.sha : SHA1;
+ import std.string : representation;
+ string data1 = "Hello, world", data2 = "Hola mundo";
+ auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
+ auto digest = hmac.put(data1.representation)
+ .put(data2.representation)
+ .finish();
+ static immutable expected = [
+ 197, 57, 52, 3, 13, 194, 13,
+ 36, 117, 228, 8, 11, 111, 51,
+ 165, 3, 123, 31, 251, 113];
+ assert(digest == expected);
}
@safe pure nothrow @nogc
@@ -309,10 +322,15 @@ unittest
import std.digest.md : MD5;
import std.digest.sha : SHA1, SHA256;
+ // Note, can't be UFCS because we don't want to import inside
+ // version (StdUnittest).
+ import std.digest : toHexString, LetterCase;
+ alias hex = toHexString!(LetterCase.lower);
+
ubyte[] nada;
- assert(hmac!MD5 (nada, nada).hex == "74e6f7298a9c2d168935f58c001bad88");
- assert(hmac!SHA1 (nada, nada).hex == "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d");
- assert(hmac!SHA256(nada, nada).hex == "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad");
+ assert(hex(hmac!MD5 (nada, nada)) == "74e6f7298a9c2d168935f58c001bad88");
+ assert(hex(hmac!SHA1 (nada, nada)) == "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d");
+ assert(hex(hmac!SHA256(nada, nada)) == "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad");
import std.string : representation;
auto key = "key".representation,
@@ -322,13 +340,13 @@ unittest
data2 = "jumps over the lazy dog".representation,
data = data1 ~ data2;
- assert(data.hmac!MD5 (key).hex == "80070713463e7749b90c2dc24911e275");
- assert(data.hmac!SHA1 (key).hex == "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9");
- assert(data.hmac!SHA256(key).hex == "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");
+ assert(hex(data.hmac!MD5 (key)) == "80070713463e7749b90c2dc24911e275");
+ assert(hex(data.hmac!SHA1 (key)) == "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9");
+ assert(hex(data.hmac!SHA256(key)) == "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");
- assert(data.hmac!MD5 (long_key).hex == "e1728d68e05beae186ea768561963778");
- assert(data.hmac!SHA1 (long_key).hex == "560d3cd77316e57ab4bba0c186966200d2b37ba3");
- assert(data.hmac!SHA256(long_key).hex == "a1b0065a5d1edd93152c677e1bc1b1e3bc70d3a76619842e7f733f02b8135c04");
+ assert(hex(data.hmac!MD5 (long_key)) == "e1728d68e05beae186ea768561963778");
+ assert(hex(data.hmac!SHA1 (long_key)) == "560d3cd77316e57ab4bba0c186966200d2b37ba3");
+ assert(hex(data.hmac!SHA256(long_key)) == "a1b0065a5d1edd93152c677e1bc1b1e3bc70d3a76619842e7f733f02b8135c04");
assert(hmac!MD5 (key).put(data1).put(data2).finish == data.hmac!MD5 (key));
assert(hmac!SHA1 (key).put(data1).put(data2).finish == data.hmac!SHA1 (key));
diff --git a/libphobos/src/std/digest/md.d b/libphobos/src/std/digest/md.d
index 1b621cfe4a9..0c4e42b5f7e 100644
--- a/libphobos/src/std/digest/md.d
+++ b/libphobos/src/std/digest/md.d
@@ -18,7 +18,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF md5Of))
)
)
- * This module conforms to the APIs defined in $(D std.digest). To understand the
+ * This module conforms to the APIs defined in `std.digest`. To understand the
* differences between the template and the OOP API, see $(MREF std, digest).
*
* This module publicly imports $(MREF std, digest) and can be used as a stand-alone
@@ -36,7 +36,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF md5Of))
* References:
* $(LINK2 http://en.wikipedia.org/wiki/Md5, Wikipedia on MD5)
*
- * Source: $(PHOBOSSRC std/digest/_md.d)
+ * Source: $(PHOBOSSRC std/digest/md.d)
*
*/
@@ -81,20 +81,13 @@ public import std.digest;
hash = md5.finish();
}
-//rotateLeft rotates x left n bits
-private uint rotateLeft(uint x, uint n) @safe pure nothrow @nogc
-{
- // With recently added optimization to DMD (commit 32ea0206 at 07/28/11), this is translated to rol.
- // No assembler required.
- return (x << n) | (x >> (32-n));
-}
-
/**
* Template API MD5 implementation.
- * See $(D std.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*/
struct MD5
{
+ import core.bitop : rol;
private:
// magic initialization constants
uint[4] _state = [0x67452301,0xefcdab89,0x98badcfe,0x10325476]; // state (ABCD)
@@ -126,7 +119,7 @@ struct MD5
@safe pure nothrow @nogc
{
a += F (b, c, d) + x + ac;
- a = rotateLeft(a, s);
+ a = rol(a, s);
a += b;
}
@@ -134,7 +127,7 @@ struct MD5
@safe pure nothrow @nogc
{
a += G (b, c, d) + x + ac;
- a = rotateLeft(a, s);
+ a = rol(a, s);
a += b;
}
@@ -142,7 +135,7 @@ struct MD5
@safe pure nothrow @nogc
{
a += H (b, c, d) + x + ac;
- a = rotateLeft(a, s);
+ a = rol(a, s);
a += b;
}
@@ -150,7 +143,7 @@ struct MD5
@safe pure nothrow @nogc
{
a += I (b, c, d) + x + ac;
- a = rotateLeft(a, s);
+ a = rol(a, s);
a += b;
}
@@ -289,7 +282,7 @@ struct MD5
/**
* Use this to feed the digest with data.
* Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
+ * interface for `ubyte` and `const(ubyte)[]`.
*
* Example:
* ----
@@ -445,6 +438,7 @@ struct MD5
@system unittest
{
import std.range;
+ import std.conv : hexString;
ubyte[16] digest;
@@ -452,44 +446,45 @@ struct MD5
md5.put(cast(ubyte[])"abcdef");
md5.start();
md5.put(cast(ubyte[])"");
- assert(md5.finish() == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e");
+ assert(md5.finish() == cast(ubyte[]) hexString!"d41d8cd98f00b204e9800998ecf8427e");
digest = md5Of("");
- assert(digest == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e");
+ assert(digest == cast(ubyte[]) hexString!"d41d8cd98f00b204e9800998ecf8427e");
digest = md5Of("a");
- assert(digest == cast(ubyte[]) x"0cc175b9c0f1b6a831c399e269772661");
+ assert(digest == cast(ubyte[]) hexString!"0cc175b9c0f1b6a831c399e269772661");
digest = md5Of("abc");
- assert(digest == cast(ubyte[]) x"900150983cd24fb0d6963f7d28e17f72");
+ assert(digest == cast(ubyte[]) hexString!"900150983cd24fb0d6963f7d28e17f72");
digest = md5Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
- assert(digest == cast(ubyte[]) x"8215ef0796a20bcaaae116d3876c664a");
+ assert(digest == cast(ubyte[]) hexString!"8215ef0796a20bcaaae116d3876c664a");
digest = md5Of("message digest");
- assert(digest == cast(ubyte[]) x"f96b697d7cb7938d525a2f31aaf161d0");
+ assert(digest == cast(ubyte[]) hexString!"f96b697d7cb7938d525a2f31aaf161d0");
digest = md5Of("abcdefghijklmnopqrstuvwxyz");
- assert(digest == cast(ubyte[]) x"c3fcd3d76192e4007dfb496cca67e13b");
+ assert(digest == cast(ubyte[]) hexString!"c3fcd3d76192e4007dfb496cca67e13b");
digest = md5Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
- assert(digest == cast(ubyte[]) x"d174ab98d277d9f5a5611c2c9f419d9f");
+ assert(digest == cast(ubyte[]) hexString!"d174ab98d277d9f5a5611c2c9f419d9f");
digest = md5Of("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
- assert(digest == cast(ubyte[]) x"57edf4a22be3c955ac49da2e2107b67a");
+ assert(digest == cast(ubyte[]) hexString!"57edf4a22be3c955ac49da2e2107b67a");
- assert(toHexString(cast(ubyte[16]) x"c3fcd3d76192e4007dfb496cca67e13b")
+ enum ubyte[16] input = cast(ubyte[16]) hexString!"c3fcd3d76192e4007dfb496cca67e13b";
+ assert(toHexString(input)
== "C3FCD3D76192E4007DFB496CCA67E13B");
ubyte[] onemilliona = new ubyte[1000000];
onemilliona[] = 'a';
digest = md5Of(onemilliona);
- assert(digest == cast(ubyte[]) x"7707D6AE4E027C70EEA2A935C2296F21");
+ assert(digest == cast(ubyte[]) hexString!"7707D6AE4E027C70EEA2A935C2296F21");
auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
digest = md5Of(oneMillionRange);
- assert(digest == cast(ubyte[]) x"7707D6AE4E027C70EEA2A935C2296F21");
+ assert(digest == cast(ubyte[]) hexString!"7707D6AE4E027C70EEA2A935C2296F21");
}
/**
@@ -511,7 +506,7 @@ auto md5Of(T...)(T data)
/**
* OOP API MD5 implementation.
- * See $(D std.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*
* This is an alias for $(D $(REF WrapperDigest, std,digest)!MD5), see
* there for more information.
@@ -547,17 +542,18 @@ alias MD5Digest = WrapperDigest!MD5;
@system unittest
{
+ import std.conv : hexString;
auto md5 = new MD5Digest();
md5.put(cast(ubyte[])"abcdef");
md5.reset();
md5.put(cast(ubyte[])"");
- assert(md5.finish() == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e");
+ assert(md5.finish() == cast(ubyte[]) hexString!"d41d8cd98f00b204e9800998ecf8427e");
md5.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
ubyte[20] result;
auto result2 = md5.finish(result[]);
- assert(result[0 .. 16] == result2 && result2 == cast(ubyte[]) x"c3fcd3d76192e4007dfb496cca67e13b");
+ assert(result[0 .. 16] == result2 && result2 == cast(ubyte[]) hexString!"c3fcd3d76192e4007dfb496cca67e13b");
debug
{
@@ -567,24 +563,24 @@ alias MD5Digest = WrapperDigest!MD5;
assert(md5.length == 16);
- assert(md5.digest("") == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e");
+ assert(md5.digest("") == cast(ubyte[]) hexString!"d41d8cd98f00b204e9800998ecf8427e");
- assert(md5.digest("a") == cast(ubyte[]) x"0cc175b9c0f1b6a831c399e269772661");
+ assert(md5.digest("a") == cast(ubyte[]) hexString!"0cc175b9c0f1b6a831c399e269772661");
- assert(md5.digest("abc") == cast(ubyte[]) x"900150983cd24fb0d6963f7d28e17f72");
+ assert(md5.digest("abc") == cast(ubyte[]) hexString!"900150983cd24fb0d6963f7d28e17f72");
assert(md5.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
- == cast(ubyte[]) x"8215ef0796a20bcaaae116d3876c664a");
+ == cast(ubyte[]) hexString!"8215ef0796a20bcaaae116d3876c664a");
- assert(md5.digest("message digest") == cast(ubyte[]) x"f96b697d7cb7938d525a2f31aaf161d0");
+ assert(md5.digest("message digest") == cast(ubyte[]) hexString!"f96b697d7cb7938d525a2f31aaf161d0");
assert(md5.digest("abcdefghijklmnopqrstuvwxyz")
- == cast(ubyte[]) x"c3fcd3d76192e4007dfb496cca67e13b");
+ == cast(ubyte[]) hexString!"c3fcd3d76192e4007dfb496cca67e13b");
assert(md5.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
- == cast(ubyte[]) x"d174ab98d277d9f5a5611c2c9f419d9f");
+ == cast(ubyte[]) hexString!"d174ab98d277d9f5a5611c2c9f419d9f");
assert(md5.digest("1234567890123456789012345678901234567890",
"1234567890123456789012345678901234567890")
- == cast(ubyte[]) x"57edf4a22be3c955ac49da2e2107b67a");
+ == cast(ubyte[]) hexString!"57edf4a22be3c955ac49da2e2107b67a");
}
diff --git a/libphobos/src/std/digest/murmurhash.d b/libphobos/src/std/digest/murmurhash.d
index b5b5bc74f98..533db92181c 100644
--- a/libphobos/src/std/digest/murmurhash.d
+++ b/libphobos/src/std/digest/murmurhash.d
@@ -685,7 +685,7 @@ the 'put' method.
assert(hashed == [181, 151, 88, 252]);
}
-version (unittest)
+version (StdUnittest)
{
private auto hash(H, Element = H.Element)(string data)
{
diff --git a/libphobos/src/std/digest/package.d b/libphobos/src/std/digest/package.d
index f4646ae13a7..ea3738b2f82 100644
--- a/libphobos/src/std/digest/package.d
+++ b/libphobos/src/std/digest/package.d
@@ -1,7 +1,7 @@
/**
- * This module describes the _digest APIs used in Phobos. All digests follow
+ * This module describes the digest APIs used in Phobos. All digests follow
* these APIs. Additionally, this module contains useful helper methods which
- * can be used with every _digest type.
+ * can be used with every digest type.
*
$(SCRIPT inhibitQuickIndex = 1;)
@@ -11,13 +11,13 @@ $(TR $(TH Category) $(TH Functions)
)
$(TR $(TDNW Template API) $(TD $(MYREF isDigest) $(MYREF DigestType) $(MYREF hasPeek)
$(MYREF hasBlockSize)
- $(MYREF ExampleDigest) $(MYREF _digest) $(MYREF hexDigest) $(MYREF makeDigest)
+ $(MYREF ExampleDigest) $(MYREF digest) $(MYREF hexDigest) $(MYREF makeDigest)
)
)
$(TR $(TDNW OOP API) $(TD $(MYREF Digest)
)
)
-$(TR $(TDNW Helper functions) $(TD $(MYREF toHexString))
+$(TR $(TDNW Helper functions) $(TD $(MYREF toHexString) $(MYREF secureEqual))
)
$(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDigest))
)
@@ -28,18 +28,18 @@ $(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDi
* There are two APIs for digests: The template API and the OOP API. The template API uses structs
* and template helpers like $(LREF isDigest). The OOP API implements digests as classes inheriting
* the $(LREF Digest) interface. All digests are named so that the template API struct is called "$(B x)"
- * and the OOP API class is called "$(B x)Digest". For example we have $(D MD5) <--> $(D MD5Digest),
- * $(D CRC32) <--> $(D CRC32Digest), etc.
+ * and the OOP API class is called "$(B x)Digest". For example we have `MD5` <--> `MD5Digest`,
+ * `CRC32` <--> `CRC32Digest`, etc.
*
* The template API is slightly more efficient. It does not have to allocate memory dynamically,
* all memory is allocated on the stack. The OOP API has to allocate in the finish method if no
* buffer was provided. If you provide a buffer to the OOP APIs finish function, it doesn't allocate,
- * but the $(LREF Digest) classes still have to be created using $(D new) which allocates them using the GC.
+ * but the $(LREF Digest) classes still have to be created using `new` which allocates them using the GC.
*
- * The OOP API is useful to change the _digest function and/or _digest backend at 'runtime'. The benefit here
+ * The OOP API is useful to change the digest function and/or digest backend at 'runtime'. The benefit here
* is that switching e.g. Phobos MD5Digest and an OpenSSLMD5Digest implementation is ABI compatible.
*
- * If just one specific _digest type and backend is needed, the template API is usually a good fit.
+ * If just one specific digest type and backend is needed, the template API is usually a good fit.
* In this simplest case, the template API can even be used without templates: Just use the "$(B x)" structs
* directly.
*
@@ -47,7 +47,7 @@ $(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDi
* Authors:
* Johannes Pfau
*
- * Source: $(PHOBOSSRC std/_digest/_package.d)
+ * Source: $(PHOBOSSRC std/digest/package.d)
*
* CTFE:
* Digests do not work in CTFE
@@ -199,7 +199,7 @@ version (ExampleDigest)
* Note:
* $(UL
* $(LI A digest must be a struct (value type) to pass the $(LREF isDigest) test.)
- * $(LI A digest passing the $(LREF isDigest) test is always an $(D OutputRange))
+ * $(LI A digest passing the $(LREF isDigest) test is always an `OutputRange`)
* )
*/
struct ExampleDigest
@@ -208,8 +208,8 @@ version (ExampleDigest)
/**
* Use this to feed the digest with data.
* Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
- * The following usages of $(D put) must work for any type which
+ * interface for `ubyte` and `const(ubyte)[]`.
+ * The following usages of `put` must work for any type which
* passes $(LREF isDigest):
* Example:
* ----
@@ -240,7 +240,7 @@ version (ExampleDigest)
*
* Note:
* The actual type returned by finish depends on the digest implementation.
- * $(D ubyte[16]) is just used as an example. It is guaranteed that the type is a
+ * `ubyte[16]` is just used as an example. It is guaranteed that the type is a
* static array of ubytes.
*
* $(UL
@@ -350,7 +350,7 @@ template DigestType(T)
}
/**
- * Used to check if a digest supports the $(D peek) method.
+ * Used to check if a digest supports the `peek` method.
* Peek has exactly the same function signatures as finish, but it doesn't reset
* the digest's internal state.
*
@@ -392,7 +392,7 @@ template hasPeek(T)
}
/**
- * Checks whether the digest has a $(D blockSize) member, which contains the
+ * Checks whether the digest has a `blockSize` member, which contains the
* digest's internal block size in bits. It is primarily used by $(REF HMAC, std,digest,hmac).
*/
@@ -427,17 +427,68 @@ package template isDigestibleRange(Range)
* Every digest passing the $(LREF isDigest) test can be used with this function.
*
* Params:
- * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num])
+ * range= an `InputRange` with `ElementType` `ubyte`, `ubyte[]` or `ubyte[num]`
*/
DigestType!Hash digest(Hash, Range)(auto ref Range range)
if (!isArray!Range
&& isDigestibleRange!Range)
{
- import std.algorithm.mutation : copy;
Hash hash;
hash.start();
- copy(range, &hash);
- return hash.finish();
+ alias E = ElementType!Range; // Not necessarily ubyte. Could be ubyte[N] or ubyte[] or something w/alias this.
+ static if (!(__traits(isScalar, E) && E.sizeof == 1))
+ {
+ foreach (e; range)
+ hash.put(e);
+ return hash.finish();
+ }
+ else
+ {
+ static if (hasBlockSize!Hash)
+ enum bufferBytes = Hash.blockSize >= (8192 * 8) ? 8192 : Hash.blockSize <= 64 ? 8 : (Hash.blockSize / 8);
+ else
+ enum bufferBytes = 8;
+ ubyte[bufferBytes] buffer = void;
+ static if (isRandomAccessRange!Range && hasLength!Range)
+ {
+ const end = range.length;
+ size_t i = 0;
+ while (end - i >= buffer.length)
+ {
+ foreach (ref e; buffer)
+ e = range[i++];
+ hash.put(buffer);
+ }
+ if (const remaining = end - i)
+ {
+ foreach (ref e; buffer[0 .. remaining])
+ e = range[i++];
+ hash.put(buffer[0 .. remaining]);
+ }
+ return hash.finish();
+ }
+ else
+ {
+ for (;;)
+ {
+ size_t n = buffer.length;
+ foreach (i, ref e; buffer)
+ {
+ if (range.empty)
+ {
+ n = i;
+ break;
+ }
+ e = range.front;
+ range.popFront();
+ }
+ if (n)
+ hash.put(buffer[0 .. n]);
+ if (n != buffer.length)
+ return hash.finish();
+ }
+ }
+ }
}
///
@@ -490,7 +541,7 @@ if (allSatisfy!(isArray, typeof(data)))
*
* Params:
* order= the order in which the bytes are processed (see $(LREF toHexString))
- * range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num])
+ * range= an `InputRange` with `ElementType` `ubyte`, `ubyte[]` or `ubyte[num]`
*/
char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, Range)(ref Range range)
if (!isArray!Range && isDigestibleRange!Range)
@@ -562,7 +613,7 @@ Hash makeDigest(Hash)()
* The Digest interface is the base interface which is implemented by all digests.
*
* Note:
- * A Digest implementation is always an $(D OutputRange)
+ * A Digest implementation is always an `OutputRange`
*/
interface Digest
{
@@ -570,7 +621,7 @@ interface Digest
/**
* Use this to feed the digest with data.
* Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
+ * interface for `ubyte` and `const(ubyte)[]`.
*
* Example:
* ----
@@ -589,7 +640,7 @@ interface Digest
* Resets the internal state of the digest.
* Note:
* $(LREF finish) calls this internally, so it's not necessary to call
- * $(D reset) manually after a call to $(LREF finish).
+ * `reset` manually after a call to $(LREF finish).
*/
@trusted nothrow void reset();
@@ -606,7 +657,7 @@ interface Digest
@trusted nothrow ubyte[] finish();
///ditto
nothrow ubyte[] finish(ubyte[] buf);
- //@@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=6549
+ // https://issues.dlang.org/show_bug.cgi?id=6549
/*in
{
assert(buf.length >= this.length);
@@ -686,6 +737,16 @@ enum Order : bool
decreasing ///
}
+///
+@safe unittest
+{
+ import std.digest.crc : CRC32;
+
+ auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
+ assert(crc32.toHexString!(Order.decreasing) == "414FA339");
+ assert(crc32.toHexString!(LetterCase.lower, Order.decreasing) == "414fa339");
+}
+
/**
* Used to convert a hash value (a static or dynamic array of ubytes) to a string.
@@ -705,39 +766,12 @@ enum Order : bool
* the return value, effectively avoiding dynamic allocation.
*/
char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase letterCase = LetterCase.upper)
-(in ubyte[num] digest)
+(const ubyte[num] digest)
{
- static if (letterCase == LetterCase.upper)
- {
- import std.ascii : hexDigits = hexDigits;
- }
- else
- {
- import std.ascii : hexDigits = lowerHexDigits;
- }
-
char[num*2] result;
size_t i;
-
- static if (order == Order.increasing)
- {
- foreach (u; digest)
- {
- result[i++] = hexDigits[u >> 4];
- result[i++] = hexDigits[u & 15];
- }
- }
- else
- {
- size_t j = num - 1;
- while (i < num*2)
- {
- result[i++] = hexDigits[digest[j] >> 4];
- result[i++] = hexDigits[digest[j] & 15];
- j--;
- }
- }
+ toHexStringImpl!(order, letterCase)(digest, result);
return result;
}
@@ -751,35 +785,8 @@ char[num*2] toHexString(LetterCase letterCase, Order order = Order.increasing, s
string toHexString(Order order = Order.increasing, LetterCase letterCase = LetterCase.upper)
(in ubyte[] digest)
{
- static if (letterCase == LetterCase.upper)
- {
- import std.ascii : hexDigits = hexDigits;
- }
- else
- {
- import std.ascii : hexDigits = lowerHexDigits;
- }
-
auto result = new char[digest.length*2];
- size_t i;
-
- static if (order == Order.increasing)
- {
- foreach (u; digest)
- {
- result[i++] = hexDigits[u >> 4];
- result[i++] = hexDigits[u & 15];
- }
- }
- else
- {
- import std.range : retro;
- foreach (u; retro(digest))
- {
- result[i++] = hexDigits[u >> 4];
- result[i++] = hexDigits[u & 15];
- }
- }
+ toHexStringImpl!(order, letterCase)(digest, result);
import std.exception : assumeUnique;
// memory was just created, so casting to immutable is safe
return () @trusted { return assumeUnique(result); }();
@@ -842,6 +849,42 @@ ref T[N] asArray(size_t N, T)(ref T[] source, string errorMsg = "")
}
/*
+ * Fill in a preallocated buffer with the ASCII hex representation from a byte buffer
+ */
+private void toHexStringImpl(Order order, LetterCase letterCase, BB, HB)
+(scope const ref BB byteBuffer, ref HB hexBuffer){
+ static if (letterCase == LetterCase.upper)
+ {
+ import std.ascii : hexDigits = hexDigits;
+ }
+ else
+ {
+ import std.ascii : hexDigits = lowerHexDigits;
+ }
+
+ size_t i;
+ static if (order == Order.increasing)
+ {
+ foreach (u; byteBuffer)
+ {
+ hexBuffer[i++] = hexDigits[u >> 4];
+ hexBuffer[i++] = hexDigits[u & 15];
+ }
+ }
+ else
+ {
+ size_t j = byteBuffer.length -1;
+ while (i < byteBuffer.length*2)
+ {
+ hexBuffer[i++] = hexDigits[byteBuffer[j] >> 4];
+ hexBuffer[i++] = hexDigits[byteBuffer[j] & 15];
+ j--;
+ }
+ }
+}
+
+
+/*
* Returns the length (in bytes) of the hash value produced by T.
*/
template digestLength(T)
@@ -884,7 +927,7 @@ if (isDigest!T) : Digest
/**
* Use this to feed the digest with data.
* Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
+ * interface for `ubyte` and `const(ubyte)[]`.
*/
@trusted nothrow void put(scope const(ubyte)[] data...)
{
@@ -895,7 +938,7 @@ if (isDigest!T) : Digest
* Resets the internal state of the digest.
* Note:
* $(LREF finish) calls this internally, so it's not necessary to call
- * $(D reset) manually after a call to $(LREF finish).
+ * `reset` manually after a call to $(LREF finish).
*/
@trusted nothrow void reset()
{
@@ -931,9 +974,9 @@ if (isDigest!T) : Digest
nothrow ubyte[] finish(ubyte[] buf)
in
{
- assert(buf.length >= this.length);
+ assert(buf.length >= this.length, "Given buffer is smaller than the local buffer.");
}
- body
+ do
{
enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~
"big, check " ~ typeof(this).stringof ~ ".length!";
@@ -953,10 +996,10 @@ if (isDigest!T) : Digest
version (StdDdoc)
{
/**
- * Works like $(D finish) but does not reset the internal state, so it's possible
+ * Works like `finish` but does not reset the internal state, so it's possible
* to continue putting data into this WrapperDigest after a call to peek.
*
- * These functions are only available if $(D hasPeek!T) is true.
+ * These functions are only available if `hasPeek!T` is true.
*/
@trusted ubyte[] peek(ubyte[] buf) const;
///ditto
@@ -967,9 +1010,9 @@ if (isDigest!T) : Digest
@trusted ubyte[] peek(ubyte[] buf) const
in
{
- assert(buf.length >= this.length);
+ assert(buf.length >= this.length, "Given buffer is smaller than the local buffer.");
}
- body
+ do
{
enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~
"big, check " ~ typeof(this).stringof ~ ".length!";
@@ -1123,12 +1166,12 @@ if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 &&
auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation;
auto data = "data".representation;
- string hex1 = data.hmac!SHA1(secret).toHexString;
- string hex2 = data.hmac!SHA1(secret).toHexString;
- string hex3 = "data1".representation.hmac!SHA1(secret).toHexString;
+ auto hex1 = data.hmac!SHA1(secret).toHexString;
+ auto hex2 = data.hmac!SHA1(secret).toHexString;
+ auto hex3 = "data1".representation.hmac!SHA1(secret).toHexString;
- assert( secureEqual(hex1, hex2));
- assert(!secureEqual(hex1, hex3));
+ assert( secureEqual(hex1[], hex2[]));
+ assert(!secureEqual(hex1[], hex3[]));
}
@system pure unittest
diff --git a/libphobos/src/std/digest/ripemd.d b/libphobos/src/std/digest/ripemd.d
index c47a741742a..4a5deca6884 100644
--- a/libphobos/src/std/digest/ripemd.d
+++ b/libphobos/src/std/digest/ripemd.d
@@ -21,7 +21,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of))
* This module conforms to the APIs defined in $(MREF std, digest). To understand the
* differences between the template and the OOP API, see $(MREF std, digest).
*
- * This module publicly imports $(D std.digest) and can be used as a stand-alone
+ * This module publicly imports `std.digest` and can be used as a stand-alone
* module.
*
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -40,7 +40,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of))
* $(LI $(LINK2 http://en.wikipedia.org/wiki/RIPEMD-160, Wikipedia on RIPEMD-160))
* )
*
- * Source: $(PHOBOSSRC std/digest/_ripemd.d)
+ * Source: $(PHOBOSSRC std/digest/ripemd.d)
*
*/
@@ -85,20 +85,13 @@ public import std.digest;
hash = md.finish();
}
-//rotateLeft rotates x left n bits
-private uint rotateLeft(uint x, uint n) @safe pure nothrow @nogc
-{
- // With recently added optimization to DMD (commit 32ea0206 at 07/28/11), this is translated to rol.
- // No assembler required.
- return (x << n) | (x >> (32-n));
-}
-
/**
* Template API RIPEMD160 implementation.
- * See $(D std.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*/
struct RIPEMD160
{
+ import core.bitop : rol;
private:
// magic initialization constants
uint[5] _state = [0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]; // state (ABCDE)
@@ -132,40 +125,40 @@ struct RIPEMD160
@safe pure nothrow @nogc
{
a += F(b, c, d) + x;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
static void GG(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
@safe pure nothrow @nogc
{
a += G(b, c, d) + x + 0x5a827999UL;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
static void HH(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
@safe pure nothrow @nogc
{
a += H(b, c, d) + x + 0x6ed9eba1UL;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
static void II(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
@safe pure nothrow @nogc
{
a += I(b, c, d) + x + 0x8f1bbcdcUL;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
static void JJ(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
@safe pure nothrow @nogc
{
a += J(b, c, d) + x + 0xa953fd4eUL;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
/*
@@ -177,40 +170,40 @@ struct RIPEMD160
@safe pure nothrow @nogc
{
a += F(b, c, d) + x;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
static void GGG(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
@safe pure nothrow @nogc
{
a += G(b, c, d) + x + 0x7a6d76e9UL;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
static void HHH(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
@safe pure nothrow @nogc
{
a += H(b, c, d) + x + 0x6d703ef3UL;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
static void III(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
@safe pure nothrow @nogc
{
a += I(b, c, d) + x + 0x5c4dd124UL;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
static void JJJ(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
@safe pure nothrow @nogc
{
a += J(b, c, d) + x + 0x50a28be6UL;
- a = rotateLeft(a, s) + e;
- c = rotateLeft(c, 10);
+ a = rol(a, s) + e;
+ c = rol(c, 10);
}
/*
@@ -445,7 +438,7 @@ struct RIPEMD160
/**
* Use this to feed the digest with data.
* Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
+ * interface for `ubyte` and `const(ubyte)[]`.
*
* Example:
* ----
@@ -613,6 +606,7 @@ struct RIPEMD160
@system unittest
{
+ import std.conv : hexString;
import std.range;
ubyte[20] digest;
@@ -621,44 +615,45 @@ struct RIPEMD160
md.put(cast(ubyte[])"abcdef");
md.start();
md.put(cast(ubyte[])"");
- assert(md.finish() == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31");
+ assert(md.finish() == cast(ubyte[]) hexString!"9c1185a5c5e9fc54612808977ee8f548b2258d31");
digest = ripemd160Of("");
- assert(digest == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31");
+ assert(digest == cast(ubyte[]) hexString!"9c1185a5c5e9fc54612808977ee8f548b2258d31");
digest = ripemd160Of("a");
- assert(digest == cast(ubyte[]) x"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe");
+ assert(digest == cast(ubyte[]) hexString!"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe");
digest = ripemd160Of("abc");
- assert(digest == cast(ubyte[]) x"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc");
+ assert(digest == cast(ubyte[]) hexString!"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc");
digest = ripemd160Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
- assert(digest == cast(ubyte[]) x"12a053384a9c0c88e405a06c27dcf49ada62eb2b");
+ assert(digest == cast(ubyte[]) hexString!"12a053384a9c0c88e405a06c27dcf49ada62eb2b");
digest = ripemd160Of("message digest");
- assert(digest == cast(ubyte[]) x"5d0689ef49d2fae572b881b123a85ffa21595f36");
+ assert(digest == cast(ubyte[]) hexString!"5d0689ef49d2fae572b881b123a85ffa21595f36");
digest = ripemd160Of("abcdefghijklmnopqrstuvwxyz");
- assert(digest == cast(ubyte[]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc");
+ assert(digest == cast(ubyte[]) hexString!"f71c27109c692c1b56bbdceb5b9d2865b3708dbc");
digest = ripemd160Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
- assert(digest == cast(ubyte[]) x"b0e20b6e3116640286ed3a87a5713079b21f5189");
+ assert(digest == cast(ubyte[]) hexString!"b0e20b6e3116640286ed3a87a5713079b21f5189");
digest = ripemd160Of("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
- assert(digest == cast(ubyte[]) x"9b752e45573d4b39f4dbd3323cab82bf63326bfb");
+ assert(digest == cast(ubyte[]) hexString!"9b752e45573d4b39f4dbd3323cab82bf63326bfb");
- assert(toHexString(cast(ubyte[20]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc")
+ enum ubyte[20] input = cast(ubyte[20]) hexString!"f71c27109c692c1b56bbdceb5b9d2865b3708dbc";
+ assert(toHexString(input)
== "F71C27109C692C1B56BBDCEB5B9D2865B3708DBC");
ubyte[] onemilliona = new ubyte[1000000];
onemilliona[] = 'a';
digest = ripemd160Of(onemilliona);
- assert(digest == cast(ubyte[]) x"52783243c1697bdbe16d37f97f68f08325dc1528");
+ assert(digest == cast(ubyte[]) hexString!"52783243c1697bdbe16d37f97f68f08325dc1528");
auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
digest = ripemd160Of(oneMillionRange);
- assert(digest == cast(ubyte[]) x"52783243c1697bdbe16d37f97f68f08325dc1528");
+ assert(digest == cast(ubyte[]) hexString!"52783243c1697bdbe16d37f97f68f08325dc1528");
}
/**
@@ -680,7 +675,7 @@ auto ripemd160Of(T...)(T data)
/**
* OOP API RIPEMD160 implementation.
- * See $(D std.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*
* This is an alias for $(D $(REF WrapperDigest, std,digest)!RIPEMD160),
* see there for more information.
@@ -716,17 +711,18 @@ alias RIPEMD160Digest = WrapperDigest!RIPEMD160;
@system unittest
{
+ import std.conv : hexString;
auto md = new RIPEMD160Digest();
md.put(cast(ubyte[])"abcdef");
md.reset();
md.put(cast(ubyte[])"");
- assert(md.finish() == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31");
+ assert(md.finish() == cast(ubyte[]) hexString!"9c1185a5c5e9fc54612808977ee8f548b2258d31");
md.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
ubyte[20] result;
auto result2 = md.finish(result[]);
- assert(result[0 .. 20] == result2 && result2 == cast(ubyte[]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc");
+ assert(result[0 .. 20] == result2 && result2 == cast(ubyte[]) hexString!"f71c27109c692c1b56bbdceb5b9d2865b3708dbc");
debug
{
@@ -736,27 +732,27 @@ alias RIPEMD160Digest = WrapperDigest!RIPEMD160;
assert(md.length == 20);
- assert(md.digest("") == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31");
+ assert(md.digest("") == cast(ubyte[]) hexString!"9c1185a5c5e9fc54612808977ee8f548b2258d31");
- assert(md.digest("a") == cast(ubyte[]) x"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe");
+ assert(md.digest("a") == cast(ubyte[]) hexString!"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe");
- assert(md.digest("abc") == cast(ubyte[]) x"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc");
+ assert(md.digest("abc") == cast(ubyte[]) hexString!"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc");
assert(md.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
- == cast(ubyte[]) x"12a053384a9c0c88e405a06c27dcf49ada62eb2b");
+ == cast(ubyte[]) hexString!"12a053384a9c0c88e405a06c27dcf49ada62eb2b");
- assert(md.digest("message digest") == cast(ubyte[]) x"5d0689ef49d2fae572b881b123a85ffa21595f36");
+ assert(md.digest("message digest") == cast(ubyte[]) hexString!"5d0689ef49d2fae572b881b123a85ffa21595f36");
assert(md.digest("abcdefghijklmnopqrstuvwxyz")
- == cast(ubyte[]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc");
+ == cast(ubyte[]) hexString!"f71c27109c692c1b56bbdceb5b9d2865b3708dbc");
assert(md.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
- == cast(ubyte[]) x"b0e20b6e3116640286ed3a87a5713079b21f5189");
+ == cast(ubyte[]) hexString!"b0e20b6e3116640286ed3a87a5713079b21f5189");
assert(md.digest("1234567890123456789012345678901234567890",
"1234567890123456789012345678901234567890")
- == cast(ubyte[]) x"9b752e45573d4b39f4dbd3323cab82bf63326bfb");
+ == cast(ubyte[]) hexString!"9b752e45573d4b39f4dbd3323cab82bf63326bfb");
assert(md.digest(new ubyte[160/8]) // 160 zero bits
- == cast(ubyte[]) x"5c00bd4aca04a9057c09b20b05f723f2e23deb65");
+ == cast(ubyte[]) hexString!"5c00bd4aca04a9057c09b20b05f723f2e23deb65");
}
diff --git a/libphobos/src/std/digest/sha.d b/libphobos/src/std/digest/sha.d
index 671e07bd3f2..5bbf7ea20c3 100644
--- a/libphobos/src/std/digest/sha.d
+++ b/libphobos/src/std/digest/sha.d
@@ -26,7 +26,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF sha1Of))
* This module conforms to the APIs defined in $(MREF std, digest). To understand the
* differences between the template and the OOP API, see $(MREF std, digest).
*
- * This module publicly imports $(D std.digest) and can be used as a stand-alone
+ * This module publicly imports `std.digest` and can be used as a stand-alone
* module.
*
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -46,7 +46,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF sha1Of))
* $(LI $(LINK2 http://en.wikipedia.org/wiki/Secure_Hash_Algorithm, Wikipedia article about SHA))
* )
*
- * Source: $(PHOBOSSRC std/digest/_sha.d)
+ * Source: $(PHOBOSSRC std/digest/sha.d)
*
*/
@@ -101,13 +101,9 @@ module std.digest.sha;
hash1 = sha1.finish();
}
-version (Win64)
+version (D_InlineAsm_X86)
{
- // wrong calling convention
-}
-else version (D_InlineAsm_X86)
-{
- version (D_PIC) {} // Bugzilla 9378
+ version (D_PIC) {} // https://issues.dlang.org/show_bug.cgi?id=9378
else private version = USE_SSSE3;
}
else version (D_InlineAsm_X86_64)
@@ -115,14 +111,7 @@ else version (D_InlineAsm_X86_64)
private version = USE_SSSE3;
}
-version (LittleEndian) import core.bitop : bswap;
-
-
-version (unittest)
-{
- import std.exception;
-}
-
+import core.bitop;
public import std.digest;
@@ -130,59 +119,16 @@ public import std.digest;
* Helper methods for encoding the buffer.
* Can be removed if the optimizer can inline the methods from std.bitmanip.
*/
-private ubyte[8] nativeToBigEndian(ulong val) @trusted pure nothrow @nogc
-{
- version (LittleEndian)
- immutable ulong res = (cast(ulong) bswap(cast(uint) val)) << 32 | bswap(cast(uint) (val >> 32));
- else
- immutable ulong res = val;
- return *cast(ubyte[8]*) &res;
-}
-
-private ubyte[4] nativeToBigEndian(uint val) @trusted pure nothrow @nogc
+version (LittleEndian)
{
- version (LittleEndian)
- immutable uint res = bswap(val);
- else
- immutable uint res = val;
- return *cast(ubyte[4]*) &res;
+ private alias nativeToBigEndian = bswap;
+ private alias bigEndianToNative = bswap;
}
-
-private ulong bigEndianToNative(ubyte[8] val) @trusted pure nothrow @nogc
+else pragma(inline, true) private pure @nogc nothrow @safe
{
- version (LittleEndian)
- {
- import std.bitmanip : bigEndianToNative;
- return bigEndianToNative!ulong(val);
- }
- else
- return *cast(ulong*) &val;
-}
-
-private uint bigEndianToNative(ubyte[4] val) @trusted pure nothrow @nogc
-{
- version (LittleEndian)
- return bswap(*cast(uint*) &val);
- else
- return *cast(uint*) &val;
-}
-
-//rotateLeft rotates x left n bits
-private uint rotateLeft(uint x, uint n) @safe pure nothrow @nogc
-{
- // With recently added optimization to DMD (commit 32ea0206 at 07/28/11), this is translated to rol.
- // No assembler required.
- return (x << n) | (x >> (32-n));
-}
-
-//rotateRight rotates x right n bits
-private uint rotateRight(uint x, uint n) @safe pure nothrow @nogc
-{
- return (x >> n) | (x << (32-n));
-}
-private ulong rotateRight(ulong x, uint n) @safe pure nothrow @nogc
-{
- return (x >> n) | (x << (64-n));
+ uint nativeToBigEndian(uint val) { return val; }
+ ulong nativeToBigEndian(ulong val) { return val; }
+ alias bigEndianToNative = nativeToBigEndian;
}
/**
@@ -193,7 +139,7 @@ private ulong rotateRight(ulong x, uint n) @safe pure nothrow @nogc
* simply use the convenience aliases: SHA1, SHA224, SHA256, SHA384, SHA512,
* SHA512_224 and SHA512_256.
*
- * See $(D std.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*/
struct SHA(uint hashBlockSize, uint digestSize)
{
@@ -220,7 +166,8 @@ struct SHA(uint hashBlockSize, uint digestSize)
if (ssse3)
{
version (D_InlineAsm_X86_64)
- // constants as extra argument for PIC, see Bugzilla 9378
+ // constants as extra argument for PIC
+ // see https://issues.dlang.org/show_bug.cgi?id=9378
transformSSSE3(state, block, &sse3_constants);
else
transformSSSE3(state, block);
@@ -364,6 +311,7 @@ struct SHA(uint hashBlockSize, uint digestSize)
/*
* Basic SHA1/SHA2 functions.
*/
+ pragma(inline, true)
static @safe pure nothrow @nogc
{
/* All SHA1/SHA2 */
@@ -374,16 +322,16 @@ struct SHA(uint hashBlockSize, uint digestSize)
uint Parity(uint x, uint y, uint z) { return x ^ y ^ z; }
/* SHA-224, SHA-256 */
- uint BigSigma0(uint x) { return rotateRight(x, 2) ^ rotateRight(x, 13) ^ rotateRight(x, 22); }
- uint BigSigma1(uint x) { return rotateRight(x, 6) ^ rotateRight(x, 11) ^ rotateRight(x, 25); }
- uint SmSigma0(uint x) { return rotateRight(x, 7) ^ rotateRight(x, 18) ^ x >> 3; }
- uint SmSigma1(uint x) { return rotateRight(x, 17) ^ rotateRight(x, 19) ^ x >> 10; }
+ uint BigSigma0(uint x) { return core.bitop.ror(x, 2) ^ core.bitop.ror(x, 13) ^ core.bitop.ror(x, 22); }
+ uint BigSigma1(uint x) { return core.bitop.ror(x, 6) ^ core.bitop.ror(x, 11) ^ core.bitop.ror(x, 25); }
+ uint SmSigma0(uint x) { return core.bitop.ror(x, 7) ^ core.bitop.ror(x, 18) ^ x >> 3; }
+ uint SmSigma1(uint x) { return core.bitop.ror(x, 17) ^ core.bitop.ror(x, 19) ^ x >> 10; }
/* SHA-384, SHA-512, SHA-512/224, SHA-512/256 */
- ulong BigSigma0(ulong x) { return rotateRight(x, 28) ^ rotateRight(x, 34) ^ rotateRight(x, 39); }
- ulong BigSigma1(ulong x) { return rotateRight(x, 14) ^ rotateRight(x, 18) ^ rotateRight(x, 41); }
- ulong SmSigma0(ulong x) { return rotateRight(x, 1) ^ rotateRight(x, 8) ^ x >> 7; }
- ulong SmSigma1(ulong x) { return rotateRight(x, 19) ^ rotateRight(x, 61) ^ x >> 6; }
+ ulong BigSigma0(ulong x) { return core.bitop.ror(x, 28) ^ core.bitop.ror(x, 34) ^ core.bitop.ror(x, 39); }
+ ulong BigSigma1(ulong x) { return core.bitop.ror(x, 14) ^ core.bitop.ror(x, 18) ^ core.bitop.ror(x, 41); }
+ ulong SmSigma0(ulong x) { return core.bitop.ror(x, 1) ^ core.bitop.ror(x, 8) ^ x >> 7; }
+ ulong SmSigma1(ulong x) { return core.bitop.ror(x, 19) ^ core.bitop.ror(x, 61) ^ x >> 6; }
}
/*
@@ -392,41 +340,41 @@ struct SHA(uint hashBlockSize, uint digestSize)
static void T_0_15(int i, const(ubyte[64])* input, ref uint[16] W, uint A, ref uint B, uint C, uint D,
uint E, ref uint T) pure nothrow @nogc
{
- uint Wi = W[i] = bigEndianToNative(*cast(ubyte[4]*)&((*input)[i*4]));
- T = Ch(B, C, D) + E + rotateLeft(A, 5) + Wi + 0x5a827999;
- B = rotateLeft(B, 30);
+ uint Wi = W[i] = bigEndianToNative(*cast(uint*) &((*input)[i*4]));
+ T = Ch(B, C, D) + E + core.bitop.rol(A, 5) + Wi + 0x5a827999;
+ B = core.bitop.rol(B, 30);
}
static void T_16_19(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E, ref uint T)
pure nothrow @nogc
{
- W[i&15] = rotateLeft(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
- T = Ch(B, C, D) + E + rotateLeft(A, 5) + W[i&15] + 0x5a827999;
- B = rotateLeft(B, 30);
+ W[i&15] = core.bitop.rol(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
+ T = Ch(B, C, D) + E + core.bitop.rol(A, 5) + W[i&15] + 0x5a827999;
+ B = core.bitop.rol(B, 30);
}
static void T_20_39(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E,
ref uint T) pure nothrow @nogc
{
- W[i&15] = rotateLeft(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
- T = Parity(B, C, D) + E + rotateLeft(A, 5) + W[i&15] + 0x6ed9eba1;
- B = rotateLeft(B, 30);
+ W[i&15] = core.bitop.rol(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
+ T = Parity(B, C, D) + E + core.bitop.rol(A, 5) + W[i&15] + 0x6ed9eba1;
+ B = core.bitop.rol(B, 30);
}
static void T_40_59(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E,
ref uint T) pure nothrow @nogc
{
- W[i&15] = rotateLeft(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
- T = Maj(B, C, D) + E + rotateLeft(A, 5) + W[i&15] + 0x8f1bbcdc;
- B = rotateLeft(B, 30);
+ W[i&15] = core.bitop.rol(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
+ T = Maj(B, C, D) + E + core.bitop.rol(A, 5) + W[i&15] + 0x8f1bbcdc;
+ B = core.bitop.rol(B, 30);
}
static void T_60_79(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E,
ref uint T) pure nothrow @nogc
{
- W[i&15] = rotateLeft(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
- T = Parity(B, C, D) + E + rotateLeft(A, 5) + W[i&15] + 0xca62c1d6;
- B = rotateLeft(B, 30);
+ W[i&15] = core.bitop.rol(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
+ T = Parity(B, C, D) + E + core.bitop.rol(A, 5) + W[i&15] + 0xca62c1d6;
+ B = core.bitop.rol(B, 30);
}
private static void transformX86(uint[5]* state, const(ubyte[64])* block) pure nothrow @nogc
@@ -534,17 +482,20 @@ struct SHA(uint hashBlockSize, uint digestSize)
/*
* SHA2 basic transformation. Transforms state based on block.
*/
+ pragma(inline, true)
static void T_SHA2_0_15(Word)(int i, const(ubyte[blockSize/8])* input, ref Word[16] W,
Word A, Word B, Word C, ref Word D, Word E, Word F, Word G, ref Word H, Word K)
pure nothrow @nogc
{
- Word Wi = W[i] = bigEndianToNative(*cast(ubyte[Word.sizeof]*)&((*input)[i*Word.sizeof]));
+ Word Wi = W[i] = bigEndianToNative(*cast(Word*) &((*input)[i*Word.sizeof]));
Word T1 = H + BigSigma1(E) + Ch(E, F, G) + K + Wi;
Word T2 = BigSigma0(A) + Maj(A, B, C);
D += T1;
H = T1 + T2;
}
+ // Temporarily disable inlining because it increases build speed by 10x.
+ // pragma(inline, true)
static void T_SHA2_16_79(Word)(int i, ref Word[16] W,
Word A, Word B, Word C, ref Word D, Word E, Word F, Word G, ref Word H, Word K)
pure nothrow @nogc
@@ -694,7 +645,7 @@ struct SHA(uint hashBlockSize, uint digestSize)
/**
* Use this to feed the digest with data.
* Also implements the $(REF isOutputRange, std,range,primitives)
- * interface for $(D ubyte) and $(D const(ubyte)[]).
+ * interface for `ubyte` and `const(ubyte)[]`.
*/
void put(scope const(ubyte)[] input...) @trusted pure nothrow @nogc
{
@@ -758,11 +709,11 @@ struct SHA(uint hashBlockSize, uint digestSize)
{
static if (blockSize == 512)
{
- ubyte[32] data = void;
+ uint[8] data = void;
uint index, padLen;
/* Save number of bits */
- ubyte[8] bits = nativeToBigEndian(count[0]);
+ ulong bits = nativeToBigEndian(count[0]);
/* Pad out to 56 mod 64. */
index = (cast(uint) count[0] >> 3) & (64 - 1);
@@ -770,25 +721,23 @@ struct SHA(uint hashBlockSize, uint digestSize)
put(padding[0 .. padLen]);
/* Append length (before padding) */
- put(bits);
+ put((cast(ubyte*) &bits)[0 .. bits.sizeof]);
/* Store state in digest */
- for (auto i = 0; i < ((digestSize == 160)? 5 : 8); i++)
- data[i*4..(i+1)*4] = nativeToBigEndian(state[i])[];
+ static foreach (i; 0 .. (digestSize == 160) ? 5 : 8)
+ data[i] = nativeToBigEndian(state[i]);
/* Zeroize sensitive information. */
start();
- return data[0 .. digestSize/8];
+ return (cast(ubyte*) data.ptr)[0 .. digestSize/8];
}
else static if (blockSize == 1024)
{
- ubyte[64] data = void;
+ ulong[8] data = void;
uint index, padLen;
/* Save number of bits */
- ubyte[16] bits;
- bits[ 0 .. 8] = nativeToBigEndian(count[1]);
- bits[8 .. 16] = nativeToBigEndian(count[0]);
+ ulong[2] bits = [nativeToBigEndian(count[1]), nativeToBigEndian(count[0])];
/* Pad out to 112 mod 128. */
index = (cast(uint) count[0] >> 3) & (128 - 1);
@@ -796,15 +745,15 @@ struct SHA(uint hashBlockSize, uint digestSize)
put(padding[0 .. padLen]);
/* Append length (before padding) */
- put(bits);
+ put((cast(ubyte*) &bits)[0 .. bits.sizeof]);
/* Store state in digest */
- for (auto i = 0; i < 8; i++)
- data[i*8..(i+1)*8] = nativeToBigEndian(state[i])[];
+ static foreach (i; 0 .. 8)
+ data[i] = nativeToBigEndian(state[i]);
/* Zeroize sensitive information. */
start();
- return data[0 .. digestSize/8];
+ return (cast(ubyte*) data.ptr)[0 .. digestSize/8];
}
else
static assert(0);
@@ -820,14 +769,6 @@ struct SHA(uint hashBlockSize, uint digestSize)
}
}
-alias SHA1 = SHA!(512, 160); /// SHA alias for SHA-1, hash is ubyte[20]
-alias SHA224 = SHA!(512, 224); /// SHA alias for SHA-224, hash is ubyte[28]
-alias SHA256 = SHA!(512, 256); /// SHA alias for SHA-256, hash is ubyte[32]
-alias SHA384 = SHA!(1024, 384); /// SHA alias for SHA-384, hash is ubyte[48]
-alias SHA512 = SHA!(1024, 512); /// SHA alias for SHA-512, hash is ubyte[64]
-alias SHA512_224 = SHA!(1024, 224); /// SHA alias for SHA-512/224, hash is ubyte[28]
-alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte[32]
-
///
@safe unittest
{
@@ -869,6 +810,14 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
assert(toHexString(sha.finish()) == "5BA93C9DB0CFF93F52B521D7420E43F6EDA2784F");
}
+alias SHA1 = SHA!(512, 160); /// SHA alias for SHA-1, hash is ubyte[20]
+alias SHA224 = SHA!(512, 224); /// SHA alias for SHA-224, hash is ubyte[28]
+alias SHA256 = SHA!(512, 256); /// SHA alias for SHA-256, hash is ubyte[32]
+alias SHA384 = SHA!(1024, 384); /// SHA alias for SHA-384, hash is ubyte[48]
+alias SHA512 = SHA!(1024, 512); /// SHA alias for SHA-512, hash is ubyte[64]
+alias SHA512_224 = SHA!(1024, 224); /// SHA alias for SHA-512/224, hash is ubyte[28]
+alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte[32]
+
@safe unittest
{
assert(isDigest!SHA1);
@@ -897,19 +846,20 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
sha.put(cast(ubyte[])"abcdef");
sha.start();
sha.put(cast(ubyte[])"");
- assert(sha.finish() == cast(ubyte[]) x"da39a3ee5e6b4b0d3255bfef95601890afd80709");
+ assert(sha.finish() == cast(ubyte[]) hexString!"da39a3ee5e6b4b0d3255bfef95601890afd80709");
SHA224 sha224;
sha224.put(cast(ubyte[])"abcdef");
sha224.start();
sha224.put(cast(ubyte[])"");
- assert(sha224.finish() == cast(ubyte[]) x"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f");
+ assert(sha224.finish() == cast(ubyte[]) hexString!"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f");
SHA256 sha256;
sha256.put(cast(ubyte[])"abcdef");
sha256.start();
sha256.put(cast(ubyte[])"");
- assert(sha256.finish() == cast(ubyte[]) x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ assert(sha256.finish() == cast(ubyte[])
+ hexString!"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
SHA384 sha384;
sha384.put(cast(ubyte[])"abcdef");
@@ -922,20 +872,22 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
sha512.put(cast(ubyte[])"abcdef");
sha512.start();
sha512.put(cast(ubyte[])"");
- assert(sha512.finish() == cast(ubyte[]) hexString!("cf83e1357eefb8bdf1542850d66d8007d620e4050b571"
+ assert(sha512.finish() == cast(ubyte[])
+ hexString!("cf83e1357eefb8bdf1542850d66d8007d620e4050b571"
~"5dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"));
SHA512_224 sha512_224;
sha512_224.put(cast(ubyte[])"abcdef");
sha512_224.start();
sha512_224.put(cast(ubyte[])"");
- assert(sha512_224.finish() == cast(ubyte[]) x"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4");
+ assert(sha512_224.finish() == cast(ubyte[]) hexString!"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4");
SHA512_256 sha512_256;
sha512_256.put(cast(ubyte[])"abcdef");
sha512_256.start();
sha512_256.put(cast(ubyte[])"");
- assert(sha512_256.finish() == cast(ubyte[]) x"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a");
+ assert(sha512_256.finish() == cast(ubyte[])
+ hexString!"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a");
digest = sha1Of ("");
digest224 = sha224Of ("");
@@ -944,15 +896,15 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
digest512 = sha512Of ("");
digest512_224 = sha512_224Of("");
digest512_256 = sha512_256Of("");
- assert(digest == cast(ubyte[]) x"da39a3ee5e6b4b0d3255bfef95601890afd80709");
- assert(digest224 == cast(ubyte[]) x"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f");
- assert(digest256 == cast(ubyte[]) x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ assert(digest == cast(ubyte[]) hexString!"da39a3ee5e6b4b0d3255bfef95601890afd80709");
+ assert(digest224 == cast(ubyte[]) hexString!"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f");
+ assert(digest256 == cast(ubyte[]) hexString!"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
assert(digest384 == cast(ubyte[]) hexString!("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c"
~"0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"));
assert(digest512 == cast(ubyte[]) hexString!("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83"
~"f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"));
- assert(digest512_224 == cast(ubyte[]) x"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4");
- assert(digest512_256 == cast(ubyte[]) x"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a");
+ assert(digest512_224 == cast(ubyte[]) hexString!"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4");
+ assert(digest512_256 == cast(ubyte[]) hexString!"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a");
digest = sha1Of ("a");
digest224 = sha224Of ("a");
@@ -961,15 +913,15 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
digest512 = sha512Of ("a");
digest512_224 = sha512_224Of("a");
digest512_256 = sha512_256Of("a");
- assert(digest == cast(ubyte[]) x"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8");
- assert(digest224 == cast(ubyte[]) x"abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5");
- assert(digest256 == cast(ubyte[]) x"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb");
+ assert(digest == cast(ubyte[]) hexString!"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8");
+ assert(digest224 == cast(ubyte[]) hexString!"abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5");
+ assert(digest256 == cast(ubyte[]) hexString!"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb");
assert(digest384 == cast(ubyte[]) hexString!("54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9"
~"cd697e85175033caa88e6d57bc35efae0b5afd3145f31"));
assert(digest512 == cast(ubyte[]) hexString!("1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05ab"
~"c54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"));
- assert(digest512_224 == cast(ubyte[]) x"d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327");
- assert(digest512_256 == cast(ubyte[]) x"455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8");
+ assert(digest512_224 == cast(ubyte[]) hexString!"d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327");
+ assert(digest512_256 == cast(ubyte[]) hexString!"455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8");
digest = sha1Of ("abc");
digest224 = sha224Of ("abc");
@@ -978,15 +930,15 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
digest512 = sha512Of ("abc");
digest512_224 = sha512_224Of("abc");
digest512_256 = sha512_256Of("abc");
- assert(digest == cast(ubyte[]) x"a9993e364706816aba3e25717850c26c9cd0d89d");
- assert(digest224 == cast(ubyte[]) x"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7");
- assert(digest256 == cast(ubyte[]) x"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
+ assert(digest == cast(ubyte[]) hexString!"a9993e364706816aba3e25717850c26c9cd0d89d");
+ assert(digest224 == cast(ubyte[]) hexString!"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7");
+ assert(digest256 == cast(ubyte[]) hexString!"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
assert(digest384 == cast(ubyte[]) hexString!("cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a"
~"8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"));
assert(digest512 == cast(ubyte[]) hexString!("ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9"
~"eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"));
- assert(digest512_224 == cast(ubyte[]) x"4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa");
- assert(digest512_256 == cast(ubyte[]) x"53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23");
+ assert(digest512_224 == cast(ubyte[]) hexString!"4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa");
+ assert(digest512_256 == cast(ubyte[]) hexString!"53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23");
digest = sha1Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
digest224 = sha224Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
@@ -995,15 +947,15 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
digest512 = sha512Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
digest512_224 = sha512_224Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
digest512_256 = sha512_256Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
- assert(digest == cast(ubyte[]) x"84983e441c3bd26ebaae4aa1f95129e5e54670f1");
- assert(digest224 == cast(ubyte[]) x"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525");
- assert(digest256 == cast(ubyte[]) x"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
+ assert(digest == cast(ubyte[]) hexString!"84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+ assert(digest224 == cast(ubyte[]) hexString!"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525");
+ assert(digest256 == cast(ubyte[]) hexString!"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
assert(digest384 == cast(ubyte[]) hexString!("3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe"
~"8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b"));
assert(digest512 == cast(ubyte[]) hexString!("204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a827"
~"9be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"));
- assert(digest512_224 == cast(ubyte[]) x"e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174");
- assert(digest512_256 == cast(ubyte[]) x"bde8e1f9f19bb9fd3406c90ec6bc47bd36d8ada9f11880dbc8a22a7078b6a461");
+ assert(digest512_224 == cast(ubyte[]) hexString!"e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174");
+ assert(digest512_256 == cast(ubyte[]) hexString!"bde8e1f9f19bb9fd3406c90ec6bc47bd36d8ada9f11880dbc8a22a7078b6a461");
digest = sha1Of ("message digest");
digest224 = sha224Of ("message digest");
@@ -1012,15 +964,15 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
digest512 = sha512Of ("message digest");
digest512_224 = sha512_224Of("message digest");
digest512_256 = sha512_256Of("message digest");
- assert(digest == cast(ubyte[]) x"c12252ceda8be8994d5fa0290a47231c1d16aae3");
- assert(digest224 == cast(ubyte[]) x"2cb21c83ae2f004de7e81c3c7019cbcb65b71ab656b22d6d0c39b8eb");
- assert(digest256 == cast(ubyte[]) x"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650");
+ assert(digest == cast(ubyte[]) hexString!"c12252ceda8be8994d5fa0290a47231c1d16aae3");
+ assert(digest224 == cast(ubyte[]) hexString!"2cb21c83ae2f004de7e81c3c7019cbcb65b71ab656b22d6d0c39b8eb");
+ assert(digest256 == cast(ubyte[]) hexString!"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650");
assert(digest384 == cast(ubyte[]) hexString!("473ed35167ec1f5d8e550368a3db39be54639f828868e9454c"
~"239fc8b52e3c61dbd0d8b4de1390c256dcbb5d5fd99cd5"));
assert(digest512 == cast(ubyte[]) hexString!("107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c134"
~"92ea45b0199f3309e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c"));
- assert(digest512_224 == cast(ubyte[]) x"ad1a4db188fe57064f4f24609d2a83cd0afb9b398eb2fcaeaae2c564");
- assert(digest512_256 == cast(ubyte[]) x"0cf471fd17ed69d990daf3433c89b16d63dec1bb9cb42a6094604ee5d7b4e9fb");
+ assert(digest512_224 == cast(ubyte[]) hexString!"ad1a4db188fe57064f4f24609d2a83cd0afb9b398eb2fcaeaae2c564");
+ assert(digest512_256 == cast(ubyte[]) hexString!"0cf471fd17ed69d990daf3433c89b16d63dec1bb9cb42a6094604ee5d7b4e9fb");
digest = sha1Of ("abcdefghijklmnopqrstuvwxyz");
digest224 = sha224Of ("abcdefghijklmnopqrstuvwxyz");
@@ -1029,15 +981,15 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
digest512 = sha512Of ("abcdefghijklmnopqrstuvwxyz");
digest512_224 = sha512_224Of("abcdefghijklmnopqrstuvwxyz");
digest512_256 = sha512_256Of("abcdefghijklmnopqrstuvwxyz");
- assert(digest == cast(ubyte[]) x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89");
- assert(digest224 == cast(ubyte[]) x"45a5f72c39c5cff2522eb3429799e49e5f44b356ef926bcf390dccc2");
- assert(digest256 == cast(ubyte[]) x"71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73");
+ assert(digest == cast(ubyte[]) hexString!"32d10c7b8cf96570ca04ce37f2a19d84240d3a89");
+ assert(digest224 == cast(ubyte[]) hexString!"45a5f72c39c5cff2522eb3429799e49e5f44b356ef926bcf390dccc2");
+ assert(digest256 == cast(ubyte[]) hexString!"71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73");
assert(digest384 == cast(ubyte[]) hexString!("feb67349df3db6f5924815d6c3dc133f091809213731fe5c7b5"
~"f4999e463479ff2877f5f2936fa63bb43784b12f3ebb4"));
assert(digest512 == cast(ubyte[]) hexString!("4dbff86cc2ca1bae1e16468a05cb9881c97f1753bce3619034"
~"898faa1aabe429955a1bf8ec483d7421fe3c1646613a59ed5441fb0f321389f77f48a879c7b1f1"));
- assert(digest512_224 == cast(ubyte[]) x"ff83148aa07ec30655c1b40aff86141c0215fe2a54f767d3f38743d8");
- assert(digest512_256 == cast(ubyte[]) x"fc3189443f9c268f626aea08a756abe7b726b05f701cb08222312ccfd6710a26");
+ assert(digest512_224 == cast(ubyte[]) hexString!"ff83148aa07ec30655c1b40aff86141c0215fe2a54f767d3f38743d8");
+ assert(digest512_256 == cast(ubyte[]) hexString!"fc3189443f9c268f626aea08a756abe7b726b05f701cb08222312ccfd6710a26");
digest = sha1Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
digest224 = sha224Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
@@ -1046,15 +998,15 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
digest512 = sha512Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
digest512_224 = sha512_224Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
digest512_256 = sha512_256Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
- assert(digest == cast(ubyte[]) x"761c457bf73b14d27e9e9265c46f4b4dda11f940");
- assert(digest224 == cast(ubyte[]) x"bff72b4fcb7d75e5632900ac5f90d219e05e97a7bde72e740db393d9");
- assert(digest256 == cast(ubyte[]) x"db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0");
+ assert(digest == cast(ubyte[]) hexString!"761c457bf73b14d27e9e9265c46f4b4dda11f940");
+ assert(digest224 == cast(ubyte[]) hexString!"bff72b4fcb7d75e5632900ac5f90d219e05e97a7bde72e740db393d9");
+ assert(digest256 == cast(ubyte[]) hexString!"db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0");
assert(digest384 == cast(ubyte[]) hexString!("1761336e3f7cbfe51deb137f026f89e01a448e3b1fafa64039"
~"c1464ee8732f11a5341a6f41e0c202294736ed64db1a84"));
assert(digest512 == cast(ubyte[]) hexString!("1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f"
~"536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894"));
- assert(digest512_224 == cast(ubyte[]) x"a8b4b9174b99ffc67d6f49be9981587b96441051e16e6dd036b140d3");
- assert(digest512_256 == cast(ubyte[]) x"cdf1cc0effe26ecc0c13758f7b4a48e000615df241284185c39eb05d355bb9c8");
+ assert(digest512_224 == cast(ubyte[]) hexString!"a8b4b9174b99ffc67d6f49be9981587b96441051e16e6dd036b140d3");
+ assert(digest512_256 == cast(ubyte[]) hexString!"cdf1cc0effe26ecc0c13758f7b4a48e000615df241284185c39eb05d355bb9c8");
digest = sha1Of ("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
@@ -1070,15 +1022,15 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
"1234567890123456789012345678901234567890");
digest512_256 = sha512_256Of("1234567890123456789012345678901234567890"~
"1234567890123456789012345678901234567890");
- assert(digest == cast(ubyte[]) x"50abf5706a150990a08b2c5ea40fa0e585554732");
- assert(digest224 == cast(ubyte[]) x"b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e");
- assert(digest256 == cast(ubyte[]) x"f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e");
+ assert(digest == cast(ubyte[]) hexString!"50abf5706a150990a08b2c5ea40fa0e585554732");
+ assert(digest224 == cast(ubyte[]) hexString!"b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e");
+ assert(digest256 == cast(ubyte[]) hexString!"f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e");
assert(digest384 == cast(ubyte[]) hexString!("b12932b0627d1c060942f5447764155655bd4da0c9afa6dd9b"
~"9ef53129af1b8fb0195996d2de9ca0df9d821ffee67026"));
assert(digest512 == cast(ubyte[]) hexString!("72ec1ef1124a45b047e8b7c75a932195135bb61de24ec0d191"
~"4042246e0aec3a2354e093d76f3048b456764346900cb130d2a4fd5dd16abb5e30bcb850dee843"));
- assert(digest512_224 == cast(ubyte[]) x"ae988faaa47e401a45f704d1272d99702458fea2ddc6582827556dd2");
- assert(digest512_256 == cast(ubyte[]) x"2c9fdbc0c90bdd87612ee8455474f9044850241dc105b1e8b94b8ddf5fac9148");
+ assert(digest512_224 == cast(ubyte[]) hexString!"ae988faaa47e401a45f704d1272d99702458fea2ddc6582827556dd2");
+ assert(digest512_256 == cast(ubyte[]) hexString!"2c9fdbc0c90bdd87612ee8455474f9044850241dc105b1e8b94b8ddf5fac9148");
ubyte[] onemilliona = new ubyte[1000000];
onemilliona[] = 'a';
@@ -1089,15 +1041,15 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
digest512 = sha512Of(onemilliona);
digest512_224 = sha512_224Of(onemilliona);
digest512_256 = sha512_256Of(onemilliona);
- assert(digest == cast(ubyte[]) x"34aa973cd4c4daa4f61eeb2bdbad27316534016f");
- assert(digest224 == cast(ubyte[]) x"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67");
- assert(digest256 == cast(ubyte[]) x"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
+ assert(digest == cast(ubyte[]) hexString!"34aa973cd4c4daa4f61eeb2bdbad27316534016f");
+ assert(digest224 == cast(ubyte[]) hexString!"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67");
+ assert(digest256 == cast(ubyte[]) hexString!"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
assert(digest384 == cast(ubyte[]) hexString!("9d0e1809716474cb086e834e310a4a1ced149e9c00f2485279"
~"72cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985"));
assert(digest512 == cast(ubyte[]) hexString!("e718483d0ce769644e2e42c7bc15b4638e1f98b13b20442856"
~"32a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"));
- assert(digest512_224 == cast(ubyte[]) x"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287");
- assert(digest512_256 == cast(ubyte[]) x"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21");
+ assert(digest512_224 == cast(ubyte[]) hexString!"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287");
+ assert(digest512_256 == cast(ubyte[]) hexString!"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21");
auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
digest = sha1Of(oneMillionRange);
@@ -1107,17 +1059,18 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte
digest512 = sha512Of(oneMillionRange);
digest512_224 = sha512_224Of(oneMillionRange);
digest512_256 = sha512_256Of(oneMillionRange);
- assert(digest == cast(ubyte[]) x"34aa973cd4c4daa4f61eeb2bdbad27316534016f");
- assert(digest224 == cast(ubyte[]) x"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67");
- assert(digest256 == cast(ubyte[]) x"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
+ assert(digest == cast(ubyte[]) hexString!"34aa973cd4c4daa4f61eeb2bdbad27316534016f");
+ assert(digest224 == cast(ubyte[]) hexString!"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67");
+ assert(digest256 == cast(ubyte[]) hexString!"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
assert(digest384 == cast(ubyte[]) hexString!("9d0e1809716474cb086e834e310a4a1ced149e9c00f2485279"
~"72cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985"));
assert(digest512 == cast(ubyte[]) hexString!("e718483d0ce769644e2e42c7bc15b4638e1f98b13b20442856"
~"32a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"));
- assert(digest512_224 == cast(ubyte[]) x"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287");
- assert(digest512_256 == cast(ubyte[]) x"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21");
+ assert(digest512_224 == cast(ubyte[]) hexString!"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287");
+ assert(digest512_256 == cast(ubyte[]) hexString!"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21");
- assert(toHexString(cast(ubyte[20]) x"a9993e364706816aba3e25717850c26c9cd0d89d")
+ enum ubyte[20] input = cast(ubyte[20]) hexString!"a9993e364706816aba3e25717850c26c9cd0d89d";
+ assert(toHexString(input)
== "A9993E364706816ABA3E25717850C26C9CD0D89D");
}
@@ -1190,16 +1143,16 @@ auto sha512_256Of(T...)(T data)
{
string a = "Mary has ", b = "a little lamb";
int[] c = [ 1, 2, 3, 4, 5 ];
- string d = toHexString(sha1Of(a, b, c));
+ auto d = toHexString(sha1Of(a, b, c));
version (LittleEndian)
- assert(d == "CDBB611D00AC2387B642D3D7BDF4C3B342237110", d);
+ assert(d[] == "CDBB611D00AC2387B642D3D7BDF4C3B342237110", d.dup);
else
- assert(d == "A0F1196C7A379C09390476D9CA4AA11B71FD11C8", d);
+ assert(d[] == "A0F1196C7A379C09390476D9CA4AA11B71FD11C8", d.dup);
}
/**
* OOP API SHA1 and SHA2 implementations.
- * See $(D std.digest) for differences between template and OOP API.
+ * See `std.digest` for differences between template and OOP API.
*
* This is an alias for $(D $(REF WrapperDigest, std,digest)!SHA1), see
* there for more information.
@@ -1247,45 +1200,47 @@ alias SHA512_256Digest = WrapperDigest!SHA512_256; ///ditto
@system unittest
{
+ import std.conv : hexString;
+ import std.exception;
auto sha = new SHA1Digest();
sha.put(cast(ubyte[])"abcdef");
sha.reset();
sha.put(cast(ubyte[])"");
- assert(sha.finish() == cast(ubyte[]) x"da39a3ee5e6b4b0d3255bfef95601890afd80709");
+ assert(sha.finish() == cast(ubyte[]) hexString!"da39a3ee5e6b4b0d3255bfef95601890afd80709");
sha.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
ubyte[22] result;
auto result2 = sha.finish(result[]);
- assert(result[0 .. 20] == result2 && result2 == cast(ubyte[]) x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89");
+ assert(result[0 .. 20] == result2 && result2 == cast(ubyte[]) hexString!"32d10c7b8cf96570ca04ce37f2a19d84240d3a89");
debug
assertThrown!Error(sha.finish(result[0 .. 15]));
assert(sha.length == 20);
- assert(sha.digest("") == cast(ubyte[]) x"da39a3ee5e6b4b0d3255bfef95601890afd80709");
+ assert(sha.digest("") == cast(ubyte[]) hexString!"da39a3ee5e6b4b0d3255bfef95601890afd80709");
- assert(sha.digest("a") == cast(ubyte[]) x"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8");
+ assert(sha.digest("a") == cast(ubyte[]) hexString!"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8");
- assert(sha.digest("abc") == cast(ubyte[]) x"a9993e364706816aba3e25717850c26c9cd0d89d");
+ assert(sha.digest("abc") == cast(ubyte[]) hexString!"a9993e364706816aba3e25717850c26c9cd0d89d");
assert(sha.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
- == cast(ubyte[]) x"84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+ == cast(ubyte[]) hexString!"84983e441c3bd26ebaae4aa1f95129e5e54670f1");
- assert(sha.digest("message digest") == cast(ubyte[]) x"c12252ceda8be8994d5fa0290a47231c1d16aae3");
+ assert(sha.digest("message digest") == cast(ubyte[]) hexString!"c12252ceda8be8994d5fa0290a47231c1d16aae3");
assert(sha.digest("abcdefghijklmnopqrstuvwxyz")
- == cast(ubyte[]) x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89");
+ == cast(ubyte[]) hexString!"32d10c7b8cf96570ca04ce37f2a19d84240d3a89");
assert(sha.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
- == cast(ubyte[]) x"761c457bf73b14d27e9e9265c46f4b4dda11f940");
+ == cast(ubyte[]) hexString!"761c457bf73b14d27e9e9265c46f4b4dda11f940");
assert(sha.digest("1234567890123456789012345678901234567890",
"1234567890123456789012345678901234567890")
- == cast(ubyte[]) x"50abf5706a150990a08b2c5ea40fa0e585554732");
+ == cast(ubyte[]) hexString!"50abf5706a150990a08b2c5ea40fa0e585554732");
ubyte[] onemilliona = new ubyte[1000000];
onemilliona[] = 'a';
- assert(sha.digest(onemilliona) == cast(ubyte[]) x"34aa973cd4c4daa4f61eeb2bdbad27316534016f");
+ assert(sha.digest(onemilliona) == cast(ubyte[]) hexString!"34aa973cd4c4daa4f61eeb2bdbad27316534016f");
}
diff --git a/libphobos/src/std/encoding.d b/libphobos/src/std/encoding.d
index 5bbd0d474e0..9334f3183f6 100644
--- a/libphobos/src/std/encoding.d
+++ b/libphobos/src/std/encoding.d
@@ -3,14 +3,16 @@
/**
Classes and functions for handling and transcoding between various encodings.
-For cases where the _encoding is known at compile-time, functions are provided
-for arbitrary _encoding and decoding of characters, arbitrary transcoding
+For cases where the encoding is known at compile-time, functions are provided
+for arbitrary encoding and decoding of characters, arbitrary transcoding
between strings of different type, as well as validation and sanitization.
Encodings currently supported are UTF-8, UTF-16, UTF-32, ASCII, ISO-8859-1
-(also known as LATIN-1), ISO-8859-2 (LATIN-2), WINDOWS-1250 and WINDOWS-1252.
+(also known as LATIN-1), ISO-8859-2 (LATIN-2), WINDOWS-1250, WINDOWS-1251
+and WINDOWS-1252.
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE,
$(TR $(TH Category) $(TH Functions))
$(TR $(TD Decode) $(TD
@@ -53,6 +55,7 @@ $(TR $(TD Encoding schemes) $(TD
$(LREF EncodingSchemeUtf32Native)
$(LREF EncodingSchemeUtf8)
$(LREF EncodingSchemeWindows1250)
+ $(LREF EncodingSchemeWindows1251)
$(LREF EncodingSchemeWindows1252)
))
$(TR $(TD Representation) $(TD
@@ -64,6 +67,8 @@ $(TR $(TD Representation) $(TD
$(LREF Latin2String)
$(LREF Windows1250Char)
$(LREF Windows1250String)
+ $(LREF Windows1251Char)
+ $(LREF Windows1251String)
$(LREF Windows1252Char)
$(LREF Windows1252String)
))
@@ -71,9 +76,9 @@ $(TR $(TD Exceptions) $(TD
$(LREF INVALID_SEQUENCE)
$(LREF EncodingException)
))
-)
+))
-For cases where the _encoding is not known at compile-time, but is
+For cases where the encoding is not known at compile-time, but is
known at run-time, the abstract class $(LREF EncodingScheme)
and its subclasses is provided. To construct a run-time encoder/decoder,
one does e.g.
@@ -84,16 +89,16 @@ auto e = EncodingScheme.create("utf-8");
This library supplies $(LREF EncodingScheme) subclasses for ASCII,
ISO-8859-1 (also known as LATIN-1), ISO-8859-2 (LATIN-2), WINDOWS-1250,
-WINDOWS-1252, UTF-8, and (on little-endian architectures) UTF-16LE and
-UTF-32LE; or (on big-endian architectures) UTF-16BE and UTF-32BE.
+WINDOWS-1251, WINDOWS-1252, UTF-8, and (on little-endian architectures)
+UTF-16LE and UTF-32LE; or (on big-endian architectures) UTF-16BE and UTF-32BE.
This library provides a mechanism whereby other modules may add $(LREF
-EncodingScheme) subclasses for any other _encoding.
+EncodingScheme) subclasses for any other encoding.
Copyright: Copyright Janice Caron 2008 - 2009.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Janice Caron
-Source: $(PHOBOSSRC std/_encoding.d)
+Source: $(PHOBOSSRC std/encoding.d)
*/
/*
Copyright Janice Caron 2008 - 2009.
@@ -277,6 +282,55 @@ import std.typecons;
"\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD",
];
+ // HELPER FUNCTIONS
+ // we can probably do this better...
+ static char toHexDigit(int n)
+ {
+ return "0123456789ABCDEF"[n & 0xF];
+ }
+
+ static string makeReadable(string s)
+ {
+ string r = "\"";
+ foreach (char c;s)
+ {
+ if (c >= 0x20 && c < 0x80)
+ {
+ r ~= c;
+ }
+ else
+ {
+ r ~= "\\x";
+ r ~= toHexDigit(c >> 4);
+ r ~= toHexDigit(c);
+ }
+ }
+ r ~= "\"";
+ return r;
+ }
+
+ void transcodeReverse(Src,Dst)(immutable(Src)[] s, out immutable(Dst)[] r)
+ {
+ static if (is(Src == Dst))
+ {
+ return s;
+ }
+ else static if (is(Src == AsciiChar))
+ {
+ transcodeReverse!(char,Dst)(cast(string) s,r);
+ }
+ else
+ {
+ foreach_reverse (d;codePoints(s))
+ {
+ foreach_reverse (c;codeUnits!(Dst)(d))
+ {
+ r = c ~ r;
+ }
+ }
+ }
+ }
+
// Make sure everything that should be valid, is
foreach (a;validStrings)
{
@@ -395,6 +449,10 @@ import std.typecons;
Windows1250String y;
transcode(s,y);
assert(y == cast(Windows1250Char[])[0x8e, 'l', 'u', 0x9d, 'o', 'u', 0xe8, 'k', 0xfd, ' ', 'k', 0xf9, 0xf2]);
+ s = "\u0402lu\u0403ou\u201D\u045C k\u0414\u044F";
+ Windows1251String s51;
+ transcode(s,s51);
+ assert(s51 == cast(Windows1251Char[])[0x80, 'l', 'u', 0x81, 'o', 'u', 0x94, 0x9d, ' ', 'k', 0xc4, 0xff]);
}
// Make sure we can count properly
@@ -419,7 +477,7 @@ import std.typecons;
//=============================================================================
-/** Special value returned by $(D safeDecode) */
+/** Special value returned by `safeDecode` */
enum dchar INVALID_SEQUENCE = cast(dchar) 0xFFFFFFFF;
template EncoderFunctions()
@@ -595,7 +653,7 @@ struct CodePoints(E)
{
assert(isValid(s));
}
- body
+ do
{
this.s = s;
}
@@ -663,7 +721,7 @@ struct CodeUnits(E)
{
assert(isValidCodePoint(d));
}
- body
+ do
{
s = encode!(E)(d);
}
@@ -727,7 +785,7 @@ private template GenericEncoder()
{
assert(canEncode(c));
}
- body
+ do
{
return 1;
}
@@ -790,7 +848,7 @@ private template GenericEncoder()
//=============================================================================
/** Defines various character sets. */
-enum AsciiChar : ubyte { init }
+enum AsciiChar : ubyte { _init }
/// Ditto
alias AsciiString = immutable(AsciiChar)[];
@@ -819,7 +877,7 @@ template EncoderInstance(CharType : AsciiChar)
{
assert(canEncode(c));
}
- body
+ do
{
return 1;
}
@@ -870,7 +928,7 @@ template EncoderInstance(CharType : AsciiChar)
//=============================================================================
/** Defines an Latin1-encoded character. */
-enum Latin1Char : ubyte { init }
+enum Latin1Char : ubyte { _init }
/**
Defines an Latin1-encoded string (as an array of $(D
immutable(Latin1Char))).
@@ -902,7 +960,7 @@ template EncoderInstance(CharType : Latin1Char)
{
assert(canEncode(c));
}
- body
+ do
{
return 1;
}
@@ -946,7 +1004,7 @@ template EncoderInstance(CharType : Latin1Char)
//=============================================================================
/// Defines a Latin2-encoded character.
-enum Latin2Char : ubyte { init }
+enum Latin2Char : ubyte { _init }
/**
* Defines an Latin2-encoded string (as an array of $(D
@@ -1026,7 +1084,7 @@ private template EncoderInstance(CharType : Latin2Char)
//=============================================================================
/// Defines a Windows1250-encoded character.
-enum Windows1250Char : ubyte { init }
+enum Windows1250Char : ubyte { _init }
/**
* Defines an Windows1250-encoded string (as an array of $(D
@@ -1115,11 +1173,106 @@ private template EncoderInstance(CharType : Windows1250Char)
}
//=============================================================================
+// WINDOWS-1251
+//=============================================================================
+
+/// Defines a Windows1251-encoded character.
+enum Windows1251Char : ubyte { _init }
+
+/**
+ * Defines an Windows1251-encoded string (as an array of $(D
+ * immutable(Windows1251Char))).
+ */
+alias Windows1251String = immutable(Windows1251Char)[];
+
+private template EncoderInstance(CharType : Windows1251Char)
+{
+ import std.typecons : Tuple, tuple;
+
+ alias E = Windows1251Char;
+ alias EString = Windows1251String;
+
+ @property string encodingName() @safe pure nothrow @nogc
+ {
+ return "windows-1251";
+ }
+
+ private static immutable dchar m_charMapStart = 0x80;
+ private static immutable dchar m_charMapEnd = 0xff;
+
+ private immutable wstring charMap =
+ "\u0402\u0403\u201A\u0453\u201E\u2026\u2020\u2021"~
+ "\u20AC\u2030\u0409\u2039\u040A\u040C\u040B\u040F"~
+ "\u0452\u2018\u2019\u201C\u201D\u2022\u2013\u2014"~
+ "\uFFFD\u2122\u0459\u203A\u045A\u045C\u045B\u045F"~
+ "\u00A0\u040E\u045E\u0408\u00A4\u0490\u00A6\u00A7"~
+ "\u0401\u00A9\u0404\u00AB\u00AC\u00AD\u00AE\u0407"~
+ "\u00B0\u00B1\u0406\u0456\u0491\u00B5\u00B6\u00B7"~
+ "\u0451\u2116\u0454\u00BB\u0458\u0405\u0455\u0457"~
+ "\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417"~
+ "\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F"~
+ "\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427"~
+ "\u0428\u0429\u042A\u042B\u042C\u042D\u042E\u042F"~
+ "\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437"~
+ "\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F"~
+ "\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447"~
+ "\u0448\u0449\u044A\u044B\u044C\u044D\u044E\u044F";
+
+ private immutable Tuple!(wchar, char)[] bstMap = [
+ tuple('\u0432','\xE2'),tuple('\u0412','\xC2'),tuple('\u0453','\x83'),
+ tuple('\u0401','\xA8'),tuple('\u0422','\xD2'),tuple('\u0442','\xF2'),
+ tuple('\u2018','\x91'),tuple('\u00AD','\xAD'),tuple('\u0409','\x8A'),
+ tuple('\u041A','\xCA'),tuple('\u042A','\xDA'),tuple('\u043A','\xEA'),
+ tuple('\u044A','\xFA'),tuple('\u045B','\x9E'),tuple('\u2022','\x95'),
+ tuple('\u00A7','\xA7'),tuple('\u00B5','\xB5'),tuple('\u0405','\xBD'),
+ tuple('\u040E','\xA1'),tuple('\u0416','\xC6'),tuple('\u041E','\xCE'),
+ tuple('\u0426','\xD6'),tuple('\u042E','\xDE'),tuple('\u0436','\xE6'),
+ tuple('\u043E','\xEE'),tuple('\u0446','\xF6'),tuple('\u044E','\xFE'),
+ tuple('\u0457','\xBF'),tuple('\u0490','\xA5'),tuple('\u201D','\x94'),
+ tuple('\u203A','\x9B'),tuple('\u00A4','\xA4'),tuple('\u00AB','\xAB'),
+ tuple('\u00B0','\xB0'),tuple('\u00B7','\xB7'),tuple('\u0403','\x81'),
+ tuple('\u0407','\xAF'),tuple('\u040B','\x8E'),tuple('\u0410','\xC0'),
+ tuple('\u0414','\xC4'),tuple('\u0418','\xC8'),tuple('\u041C','\xCC'),
+ tuple('\u0420','\xD0'),tuple('\u0424','\xD4'),tuple('\u0428','\xD8'),
+ tuple('\u042C','\xDC'),tuple('\u0430','\xE0'),tuple('\u0434','\xE4'),
+ tuple('\u0438','\xE8'),tuple('\u043C','\xEC'),tuple('\u0440','\xF0'),
+ tuple('\u0444','\xF4'),tuple('\u0448','\xF8'),tuple('\u044C','\xFC'),
+ tuple('\u0451','\xB8'),tuple('\u0455','\xBE'),tuple('\u0459','\x9A'),
+ tuple('\u045E','\xA2'),tuple('\u2013','\x96'),tuple('\u201A','\x82'),
+ tuple('\u2020','\x86'),tuple('\u2030','\x89'),tuple('\u2116','\xB9'),
+ tuple('\u00A0','\xA0'),tuple('\u00A6','\xA6'),tuple('\u00A9','\xA9'),
+ tuple('\u00AC','\xAC'),tuple('\u00AE','\xAE'),tuple('\u00B1','\xB1'),
+ tuple('\u00B6','\xB6'),tuple('\u00BB','\xBB'),tuple('\u0402','\x80'),
+ tuple('\u0404','\xAA'),tuple('\u0406','\xB2'),tuple('\u0408','\xA3'),
+ tuple('\u040A','\x8C'),tuple('\u040C','\x8D'),tuple('\u040F','\x8F'),
+ tuple('\u0411','\xC1'),tuple('\u0413','\xC3'),tuple('\u0415','\xC5'),
+ tuple('\u0417','\xC7'),tuple('\u0419','\xC9'),tuple('\u041B','\xCB'),
+ tuple('\u041D','\xCD'),tuple('\u041F','\xCF'),tuple('\u0421','\xD1'),
+ tuple('\u0423','\xD3'),tuple('\u0425','\xD5'),tuple('\u0427','\xD7'),
+ tuple('\u0429','\xD9'),tuple('\u042B','\xDB'),tuple('\u042D','\xDD'),
+ tuple('\u042F','\xDF'),tuple('\u0431','\xE1'),tuple('\u0433','\xE3'),
+ tuple('\u0435','\xE5'),tuple('\u0437','\xE7'),tuple('\u0439','\xE9'),
+ tuple('\u043B','\xEB'),tuple('\u043D','\xED'),tuple('\u043F','\xEF'),
+ tuple('\u0441','\xF1'),tuple('\u0443','\xF3'),tuple('\u0445','\xF5'),
+ tuple('\u0447','\xF7'),tuple('\u0449','\xF9'),tuple('\u044B','\xFB'),
+ tuple('\u044D','\xFD'),tuple('\u044F','\xFF'),tuple('\u0452','\x90'),
+ tuple('\u0454','\xBA'),tuple('\u0456','\xB3'),tuple('\u0458','\xBC'),
+ tuple('\u045A','\x9C'),tuple('\u045C','\x9D'),tuple('\u045F','\x9F'),
+ tuple('\u0491','\xB4'),tuple('\u2014','\x97'),tuple('\u2019','\x92'),
+ tuple('\u201C','\x93'),tuple('\u201E','\x84'),tuple('\u2021','\x87'),
+ tuple('\u2026','\x85'),tuple('\u2039','\x8B'),tuple('\u20AC','\x88'),
+ tuple('\u2122','\x99')
+ ];
+
+ mixin GenericEncoder!();
+}
+
+//=============================================================================
// WINDOWS-1252
//=============================================================================
/// Defines a Windows1252-encoded character.
-enum Windows1252Char : ubyte { init }
+enum Windows1252Char : ubyte { _init }
/**
* Defines an Windows1252-encoded string (as an array of $(D
@@ -1204,7 +1357,7 @@ template EncoderInstance(CharType : char)
{
assert(c >= 0x80);
}
- body
+ do
{
return tailTable[c-0x80];
}
@@ -1214,7 +1367,7 @@ template EncoderInstance(CharType : char)
{
assert(canEncode(c));
}
- body
+ do
{
if (c < 0x80) return 1;
if (c < 0x800) return 2;
@@ -1358,7 +1511,7 @@ template EncoderInstance(CharType : wchar)
{
assert(canEncode(c));
}
- body
+ do
{
return (c < 0x10000) ? 1 : 2;
}
@@ -1455,7 +1608,7 @@ template EncoderInstance(CharType : dchar)
{
assert(canEncode(c));
}
- body
+ do
{
return 1;
}
@@ -1505,10 +1658,10 @@ Returns true if c is a valid code point
characters).
Supersedes:
- This function supersedes $(D std.utf.startsValidDchar()).
+ This function supersedes `std.utf.startsValidDchar()`.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
c = the code point to be tested
@@ -1525,7 +1678,7 @@ bool isValidCodePoint(dchar c) @safe pure nothrow @nogc
explicitly specify the encoding type.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
*/
@property string encodingName(T)()
{
@@ -1542,6 +1695,7 @@ bool isValidCodePoint(dchar c) @safe pure nothrow @nogc
assert(encodingName!(Latin1Char) == "ISO-8859-1");
assert(encodingName!(Latin2Char) == "ISO-8859-2");
assert(encodingName!(Windows1250Char) == "windows-1250");
+ assert(encodingName!(Windows1251Char) == "windows-1251");
assert(encodingName!(Windows1252Char) == "windows-1252");
}
@@ -1553,7 +1707,7 @@ bool isValidCodePoint(dchar c) @safe pure nothrow @nogc
explicitly specify the encoding type.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
*/
bool canEncode(E)(dchar c)
{
@@ -1571,6 +1725,9 @@ bool canEncode(E)(dchar c)
assert( canEncode!(Windows1250Char)('\u20AC'));
assert(!canEncode!(Windows1250Char)('\u20AD'));
assert(!canEncode!(Windows1250Char)('\uFFFD'));
+ assert( canEncode!(Windows1251Char)('\u0402'));
+ assert(!canEncode!(Windows1251Char)('\u20AD'));
+ assert(!canEncode!(Windows1251Char)('\uFFFD'));
assert( canEncode!(Windows1252Char)('\u20AC'));
assert(!canEncode!(Windows1252Char)('\u20AD'));
assert(!canEncode!(Windows1252Char)('\uFFFD'));
@@ -1595,7 +1752,7 @@ bool canEncode(E)(dchar c)
0x00 to 0x7F.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
c = the code unit to be tested
@@ -1615,6 +1772,8 @@ bool isValidCodeUnit(E)(E c)
assert(!isValidCodeUnit(cast(AsciiChar) 0xA0));
assert( isValidCodeUnit(cast(Windows1250Char) 0x80));
assert(!isValidCodeUnit(cast(Windows1250Char) 0x81));
+ assert( isValidCodeUnit(cast(Windows1251Char) 0x80));
+ assert(!isValidCodeUnit(cast(Windows1251Char) 0x98));
assert( isValidCodeUnit(cast(Windows1252Char) 0x80));
assert(!isValidCodeUnit(cast(Windows1252Char) 0x81));
}
@@ -1628,7 +1787,7 @@ bool isValidCodeUnit(E)(E c)
whereas the older function would throw an exception.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string to be tested
@@ -1650,7 +1809,7 @@ bool isValid(E)(const(E)[] s)
the first code unit, which is validly encoded.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string to be tested
@@ -1679,7 +1838,7 @@ size_t validLength(E)(const(E)[] s)
replaced with '?'.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string to be sanitized
@@ -1736,7 +1895,7 @@ immutable(E)[] sanitize(E)(immutable(E)[] s)
This is enforced by the function's in-contract.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string to be sliced
@@ -1748,7 +1907,7 @@ in
const(E)[] u = s;
assert(safeDecode(u) != INVALID_SEQUENCE);
}
-body
+do
{
auto before = s.length;
EncoderInstance!(E).skip(s);
@@ -1769,7 +1928,7 @@ body
This is enforced by the function's in-contract.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string to be sliced
@@ -1780,7 +1939,7 @@ in
assert(s.length != 0);
assert(isValid(s));
}
-body
+do
{
const(E)[] t = s;
EncoderInstance!(E).decodeReverse(s);
@@ -1804,7 +1963,7 @@ body
This function supersedes std.utf.toUTFindex().
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string to be counted
@@ -1816,7 +1975,7 @@ in
assert(isValid(s));
assert(n >= 0);
}
-body
+do
{
const(E)[] t = s;
for (size_t i=0; i<n; ++i) EncoderInstance!(E).skip(s);
@@ -1844,7 +2003,7 @@ body
function codePoints() supersedes it more conveniently.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string whose first code point is to be decoded
@@ -1856,7 +2015,7 @@ in
auto u = s;
assert(safeDecode(u) != INVALID_SEQUENCE);
}
-body
+do
{
return EncoderInstance!(typeof(s[0])).decode(s);
}
@@ -1871,7 +2030,7 @@ body
This is enforced by the function's in-contract.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string whose first code point is to be decoded
@@ -1882,7 +2041,7 @@ in
assert(s.length != 0);
assert(isValid(s));
}
-body
+do
{
return EncoderInstance!(E).decodeReverse(s);
}
@@ -1898,7 +2057,7 @@ body
function will remove it, and return the value INVALID_SEQUENCE.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string whose first code point is to be decoded
@@ -1908,7 +2067,7 @@ in
{
assert(s.length != 0);
}
-body
+do
{
return EncoderInstance!(typeof(s[0])).safeDecode(s);
}
@@ -1923,7 +2082,7 @@ body
explicitly specify the encoding as a template parameter.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
c = the code point to be encoded
@@ -1933,7 +2092,7 @@ in
{
assert(isValidCodePoint(c));
}
-body
+do
{
return EncoderInstance!(E).encodedLength(c);
}
@@ -1955,7 +2114,7 @@ body
function codeUnits() supersedes it more conveniently.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
c = the code point to be encoded
@@ -1965,7 +2124,7 @@ in
{
assert(isValidCodePoint(c));
}
-body
+do
{
return EncoderInstance!(E).encode(c);
}
@@ -1988,7 +2147,7 @@ body
function codeUnits() supersedes it more conveniently.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
c = the code point to be encoded
@@ -2002,7 +2161,7 @@ in
{
assert(isValidCodePoint(c));
}
-body
+do
{
E[] t = array;
EncoderInstance!(E).encode(c,t);
@@ -2010,13 +2169,13 @@ body
}
/*
-Encodes $(D c) in units of type $(D E) and writes the result to the
-output range $(D R). Returns the number of $(D E)s written.
+Encodes `c` in units of type `E` and writes the result to the
+output range `R`. Returns the number of `E`s written.
*/
size_t encode(E, R)(dchar c, auto ref R range)
if (isNativeOutputRange!(R, E))
{
- static if (is(Unqual!E == char))
+ static if (is(immutable E == immutable char))
{
if (c <= 0x7F)
{
@@ -2049,7 +2208,7 @@ if (isNativeOutputRange!(R, E))
assert(0);
}
}
- else static if (is(Unqual!E == wchar))
+ else static if (is(immutable E == immutable wchar))
{
if (c <= 0xFFFF)
{
@@ -2060,7 +2219,7 @@ if (isNativeOutputRange!(R, E))
range.put(cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00));
return 2;
}
- else static if (is(Unqual!E == dchar))
+ else static if (is(immutable E == immutable dchar))
{
range.put(c);
return 1;
@@ -2097,7 +2256,7 @@ if (isNativeOutputRange!(R, E))
function codeUnits() supersedes it more conveniently.
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
c = the code point to be encoded
@@ -2108,18 +2267,18 @@ in
{
assert(isValidCodePoint(c));
}
-body
+do
{
EncoderInstance!(E).encode(c,dg);
}
/**
-Encodes the contents of $(D s) in units of type $(D Tgt), writing the result to an
+Encodes the contents of `s` in units of type `Tgt`, writing the result to an
output range.
-Returns: The number of $(D Tgt) elements written.
+Returns: The number of `Tgt` elements written.
Params:
-Tgt = Element type of $(D range).
+Tgt = Element type of `range`.
s = Input array.
range = Output range.
*/
@@ -2149,7 +2308,7 @@ size_t encode(Tgt, Src, R)(in Src[] s, R range)
This function supersedes std.utf.decode().
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = the string to be decoded
@@ -2171,7 +2330,7 @@ in
{
assert(isValid(s));
}
-body
+do
{
return CodePoints!(E)(s);
}
@@ -2202,7 +2361,7 @@ body
This function supersedes std.utf.encode().
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
c = the code point to be encoded
@@ -2212,7 +2371,7 @@ in
{
assert(isValidCodePoint(c));
}
-body
+do
{
return CodeUnits!(E)(c);
}
@@ -2240,7 +2399,7 @@ body
(but note that to!() supersedes it more conveniently).
Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
- WINDOWS-1252
+ WINDOWS-1251, WINDOWS-1252
Params:
s = Source string. $(B Must) be validly encoded.
@@ -2255,23 +2414,23 @@ in
{
assert(isValid(s));
}
-body
+do
{
static if (is(Src == Dst) && is(Src == immutable))
{
r = s;
}
- else static if (is(Unqual!Src == AsciiChar))
+ else static if (is(immutable Src == immutable AsciiChar))
{
transcode(cast(const(char)[])s, r);
}
else
{
- static if (is(Unqual!Dst == wchar))
+ static if (is(immutable Dst == immutable wchar))
{
immutable minReservePlace = 2;
}
- else static if (is(Unqual!Dst == dchar))
+ else static if (is(immutable Dst == immutable dchar))
{
immutable minReservePlace = 1;
}
@@ -2309,7 +2468,7 @@ body
Latin1String ls;
// transcode from UTF-16 to ISO-8859-1
transcode(ws, ls);
- assert(ws == "hello world");
+ assert(ls == "hello world");
}
@system pure unittest
@@ -2322,7 +2481,7 @@ body
string asciiCharString = to!string(iota(0, 128, 1));
alias Types = AliasSeq!(string, Latin1String, Latin2String, AsciiString,
- Windows1250String, Windows1252String, dstring, wstring);
+ Windows1250String, Windows1251String, Windows1252String, dstring, wstring);
foreach (S; Types)
foreach (D; Types)
{
@@ -2356,8 +2515,8 @@ body
{
import std.meta : AliasSeq;
- foreach (O; AliasSeq!(Latin1Char, const Latin1Char, immutable Latin1Char))
- {
+ static foreach (O; AliasSeq!(Latin1Char, const Latin1Char, immutable Latin1Char))
+ {{
O[] output;
char[] mutableInput = "äbc".dup;
@@ -2371,17 +2530,17 @@ body
immutable char[] immutInput = "übc";
transcode(immutInput, output);
assert(output == [0xFC, 'b', 'c']);
- }
+ }}
// Make sure that const/mutable input is copied.
- foreach (C; AliasSeq!(char, const char))
- {
+ static foreach (C; AliasSeq!(char, const char))
+ {{
C[] input = "foo".dup;
C[] output;
transcode(input, output);
assert(input == output);
assert(input !is output);
- }
+ }}
// But immutable input should not be copied.
string input = "foo";
@@ -2466,6 +2625,7 @@ abstract class EncodingScheme
EncodingScheme.register!EncodingSchemeLatin1;
EncodingScheme.register!EncodingSchemeLatin2;
EncodingScheme.register!EncodingSchemeWindows1250;
+ EncodingScheme.register!EncodingSchemeWindows1251;
EncodingScheme.register!EncodingSchemeWindows1252;
EncodingScheme.register!EncodingSchemeUtf8;
EncodingScheme.register!EncodingSchemeUtf16Native;
@@ -2680,7 +2840,7 @@ abstract class EncodingScheme
const(ubyte)[] u = s;
assert(safeDecode(u) != INVALID_SEQUENCE);
}
- body
+ do
{
const(ubyte)[] t = s;
decode(s);
@@ -2701,7 +2861,7 @@ abstract class EncodingScheme
{
assert(isValid(s));
}
- body
+ do
{
size_t n = 0;
while (s.length != 0)
@@ -2728,7 +2888,7 @@ abstract class EncodingScheme
assert(isValid(s));
assert(n >= 0);
}
- body
+ do
{
const(ubyte)[] t = s;
for (size_t i=0; i<n; ++i) decode(s);
@@ -3056,6 +3216,74 @@ class EncodingSchemeWindows1250 : EncodingScheme
}
/**
+ EncodingScheme to handle Windows-1251
+
+ This scheme recognises the following names:
+ "windows-1251"
+ */
+class EncodingSchemeWindows1251 : EncodingScheme
+{
+ /* // moved to std.internal.phobosinit
+ shared static this()
+ {
+ EncodingScheme.register("std.encoding.EncodingSchemeWindows1251");
+ }*/
+
+ const
+ {
+ override string[] names() @safe pure nothrow
+ {
+ return
+ [
+ "windows-1251"
+ ];
+ }
+
+ override string toString() @safe pure nothrow @nogc
+ {
+ return "windows-1251";
+ }
+
+ override bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.canEncode!(Windows1251Char)(c);
+ }
+
+ override size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.encodedLength!(Windows1251Char)(c);
+ }
+
+ override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc
+ {
+ auto r = cast(Windows1251Char[]) buffer;
+ return std.encoding.encode(c,r);
+ }
+
+ override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ {
+ auto t = cast(const(Windows1251Char)[]) s;
+ dchar c = std.encoding.decode(t);
+ s = s[$-t.length..$];
+ return c;
+ }
+
+ override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ {
+ auto t = cast(const(Windows1251Char)[]) s;
+ dchar c = std.encoding.safeDecode(t);
+ s = s[$-t.length..$];
+ return c;
+ }
+
+ override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc
+ {
+ return cast(immutable(ubyte)[])"?";
+ }
+ }
+}
+
+/**
EncodingScheme to handle Windows-1252
This scheme recognises the following names:
@@ -3123,6 +3351,75 @@ class EncodingSchemeWindows1252 : EncodingScheme
}
}
+@system unittest
+{
+ static string[] schemeNames =
+ [
+ "ASCII",
+ "ISO-8859-1",
+ "ISO-8859-2",
+ "windows-1250",
+ "windows-1251",
+ "windows-1252"
+ ];
+
+ EncodingScheme[] schemes;
+
+ foreach (name;schemeNames)
+ {
+ schemes ~= EncodingScheme.create(name);
+ }
+
+ ubyte[1] buffer;
+ static dchar[][] valid =
+ [
+ //Valid ASCII
+ ['\u0001','\u0020','\u0040','\u0060','\u007F'],
+ //Vaild 8859-1
+ ['\u0001','\u0020','\u0070','\u00DA','\u00FF'],
+ //Valid 8859-2
+ ['\u0020','\u00D7','\u00DF','\u010F','\u02D9'],
+ //Valid 1250
+ ['\u0020','\u20AC','\u201E','\u2021','\u2039'],
+ //Valid 1251
+ ['\u0402','\u00A4','\u0415','\u0439','\u044F'],
+ //Valid 1252
+ ['\u20AC','\u0160','\u2019','\u2122','\u0178'],
+ ];
+
+ static const(ubyte)[] invalid = [0xA0,0xFF,0xFF,0x81,0x98,0x81];
+
+ foreach (i,scheme;schemes)
+ {
+ assert(scheme.toString() == schemeNames[i],"Error in the name of encoding scheme"~schemeNames[i]);
+ assert(!scheme.canEncode('\uFFFD'));
+ assert(scheme.encodedLength('A') == 1);
+ const(ubyte)[] encodeStr;
+ dchar[] decStr;
+ foreach (chr;valid[i])
+ {
+ assert(scheme.encode(chr,buffer) == 1);
+ encodeStr ~= buffer;
+ const(ubyte)[] buf = buffer;
+ decStr ~= scheme.decode(buf);
+ }
+
+ assert(scheme.isValid(encodeStr),"Not correctly encoded UTF => " ~ schemeNames[i]);
+ assert(valid[i] == decStr,"Error encode/decode UTF8 <=> " ~ schemeNames[i]);
+
+ if (schemeNames[i] == "ISO-8859-1" || schemeNames[i] == "ISO-8859-2")
+ {
+ assert(scheme.safeDecode(invalid) != INVALID_SEQUENCE);
+ }
+ else
+ {
+ assert(scheme.safeDecode(invalid) == INVALID_SEQUENCE);
+ }
+ assert(scheme.replacementSequence() == cast(immutable(ubyte)[])"?");
+ }
+ assert(invalid.length == 0);
+}
+
/**
EncodingScheme to handle UTF-8
@@ -3242,7 +3539,7 @@ class EncodingSchemeUtf16Native : EncodingScheme
{
assert((s.length & 1) == 0);
}
- body
+ do
{
auto t = cast(const(wchar)[]) s;
dchar c = std.encoding.decode(t);
@@ -3255,7 +3552,7 @@ class EncodingSchemeUtf16Native : EncodingScheme
{
assert((s.length & 1) == 0);
}
- body
+ do
{
auto t = cast(const(wchar)[]) s;
dchar c = std.encoding.safeDecode(t);
@@ -3338,7 +3635,7 @@ class EncodingSchemeUtf32Native : EncodingScheme
{
assert((s.length & 3) == 0);
}
- body
+ do
{
auto t = cast(const(dchar)[]) s;
dchar c = std.encoding.decode(t);
@@ -3351,7 +3648,7 @@ class EncodingSchemeUtf32Native : EncodingScheme
{
assert((s.length & 3) == 0);
}
- body
+ do
{
auto t = cast(const(dchar)[]) s;
dchar c = std.encoding.safeDecode(t);
@@ -3386,121 +3683,16 @@ class EncodingSchemeUtf32Native : EncodingScheme
//=============================================================================
-// Helper functions
-version (unittest)
-{
- void transcodeReverse(Src,Dst)(immutable(Src)[] s, out immutable(Dst)[] r)
- {
- static if (is(Src == Dst))
- {
- return s;
- }
- else static if (is(Src == AsciiChar))
- {
- transcodeReverse!(char,Dst)(cast(string) s,r);
- }
- else
- {
- foreach_reverse (d;codePoints(s))
- {
- foreach_reverse (c;codeUnits!(Dst)(d))
- {
- r = c ~ r;
- }
- }
- }
- }
-
- string makeReadable(string s)
- {
- string r = "\"";
- foreach (char c;s)
- {
- if (c >= 0x20 && c < 0x80)
- {
- r ~= c;
- }
- else
- {
- r ~= "\\x";
- r ~= toHexDigit(c >> 4);
- r ~= toHexDigit(c);
- }
- }
- r ~= "\"";
- return r;
- }
-
- string makeReadable(wstring s)
- {
- string r = "\"";
- foreach (wchar c;s)
- {
- if (c >= 0x20 && c < 0x80)
- {
- r ~= cast(char) c;
- }
- else
- {
- r ~= "\\u";
- r ~= toHexDigit(c >> 12);
- r ~= toHexDigit(c >> 8);
- r ~= toHexDigit(c >> 4);
- r ~= toHexDigit(c);
- }
- }
- r ~= "\"w";
- return r;
- }
-
- string makeReadable(dstring s)
- {
- string r = "\"";
- foreach (dchar c; s)
- {
- if (c >= 0x20 && c < 0x80)
- {
- r ~= cast(char) c;
- }
- else if (c < 0x10000)
- {
- r ~= "\\u";
- r ~= toHexDigit(c >> 12);
- r ~= toHexDigit(c >> 8);
- r ~= toHexDigit(c >> 4);
- r ~= toHexDigit(c);
- }
- else
- {
- r ~= "\\U00";
- r ~= toHexDigit(c >> 20);
- r ~= toHexDigit(c >> 16);
- r ~= toHexDigit(c >> 12);
- r ~= toHexDigit(c >> 8);
- r ~= toHexDigit(c >> 4);
- r ~= toHexDigit(c);
- }
- }
- r ~= "\"d";
- return r;
- }
-
- char toHexDigit(int n)
- {
- return "0123456789ABCDEF"[n & 0xF];
- }
-}
-
/** Definitions of common Byte Order Marks.
-The elements of the $(D enum) can used as indices into $(D bomTable) to get
-matching $(D BOMSeq).
+The elements of the `enum` can used as indices into `bomTable` to get
+matching `BOMSeq`.
*/
enum BOM
{
none = 0, /// no BOM was found
utf32be = 1, /// [0x00, 0x00, 0xFE, 0xFF]
utf32le = 2, /// [0xFF, 0xFE, 0x00, 0x00]
- utf7 = 3, /* [0x2B, 0x2F, 0x76, 0x38]
+ utf7 = 3, /** [0x2B, 0x2F, 0x76, 0x38]
[0x2B, 0x2F, 0x76, 0x39],
[0x2B, 0x2F, 0x76, 0x2B],
[0x2B, 0x2F, 0x76, 0x2F],
@@ -3516,7 +3708,7 @@ enum BOM
utf16le = 15 /// [0xFF, 0xFE]
}
-/// The type stored inside $(D bomTable).
+/// The type stored inside `bomTable`.
alias BOMSeq = Tuple!(BOM, "schema", ubyte[], "sequence");
/** Mapping of a byte sequence to $(B Byte Order Mark (BOM))
@@ -3540,20 +3732,20 @@ immutable bomTable = [
BOMSeq(BOM.utf16le, cast(ubyte[])([0xFF, 0xFE]))
];
-/** Returns a $(D BOMSeq) for a given $(D input).
-If no $(D BOM) is present the $(D BOMSeq) for $(D BOM.none) is
-returned. The $(D BOM) sequence at the beginning of the range will
+/** Returns a `BOMSeq` for a given `input`.
+If no `BOM` is present the `BOMSeq` for `BOM.none` is
+returned. The `BOM` sequence at the beginning of the range will
not be comsumed from the passed range. If you pass a reference type
-range make sure that $(D save) creates a deep copy.
+range make sure that `save` creates a deep copy.
Params:
- input = The sequence to check for the $(D BOM)
+ input = The sequence to check for the `BOM`
Returns:
- the found $(D BOMSeq) corresponding to the passed $(D input).
+ the found `BOMSeq` corresponding to the passed `input`.
*/
immutable(BOMSeq) getBOM(Range)(Range input)
-if (isForwardRange!Range && is(Unqual!(ElementType!Range) == ubyte))
+if (isForwardRange!Range && is(immutable ElementType!Range == immutable ubyte))
{
import std.algorithm.searching : startsWith;
foreach (it; bomTable[1 .. $])
diff --git a/libphobos/src/std/exception.d b/libphobos/src/std/exception.d
index 56133c9ad89..5fcee2ab598 100644
--- a/libphobos/src/std/exception.d
+++ b/libphobos/src/std/exception.d
@@ -5,6 +5,7 @@
handling. It also defines functions intended to aid in unit testing.
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE,
$(TR $(TH Category) $(TH Functions))
$(TR $(TD Assumptions) $(TD
@@ -17,7 +18,6 @@ $(TR $(TD Assumptions) $(TD
$(TR $(TD Enforce) $(TD
$(LREF doesPointTo)
$(LREF enforce)
- $(LREF enforceEx)
$(LREF errnoEnforce)
))
$(TR $(TD Handlers) $(TD
@@ -32,68 +32,107 @@ $(TR $(TD Other) $(TD
$(LREF ErrnoException)
$(LREF RangePrimitive)
))
-)
+))
- Synopsis of some of std.exception's functions:
- --------------------
- string synopsis()
- {
- FILE* f = enforce(fopen("some/file"));
- // f is not null from here on
- FILE* g = enforce!WriteException(fopen("some/other/file", "w"));
- // g is not null from here on
+ Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-.
+ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ Authors: $(HTTP erdani.org, Andrei Alexandrescu) and
+ $(HTTP jmdavisprog.com, Jonathan M Davis)
+ Source: $(PHOBOSSRC std/exception.d)
- Exception e = collectException(write(g, readln(f)));
- if (e)
- {
- ... an exception occurred...
- ... We have the exception to play around with...
- }
+ +/
+module std.exception;
- string msg = collectExceptionMsg(write(g, readln(f)));
- if (msg)
- {
- ... an exception occurred...
- ... We have the message from the exception but not the exception...
- }
+/// Synopis
+@system unittest
+{
+ import core.stdc.stdlib : malloc, free;
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map, splitter;
+ import std.algorithm.searching : endsWith;
+ import std.conv : ConvException, to;
+ import std.range : front, retro;
+
+ // use enforce like assert
+ int a = 3;
+ enforce(a > 2, "a needs to be higher than 2.");
+
+ // enforce can throw a custom exception
+ enforce!ConvException(a > 2, "a needs to be higher than 2.");
+
+ // enforce will return it's input
+ enum size = 42;
+ auto memory = enforce(malloc(size), "malloc failed")[0 .. size];
+ scope(exit) free(memory.ptr);
+
+ // collectException can be used to test for exceptions
+ Exception e = collectException("abc".to!int);
+ assert(e.file.endsWith("conv.d"));
+
+ // and just for the exception message
+ string msg = collectExceptionMsg("abc".to!int);
+ assert(msg == "Unexpected 'a' when converting from type string to type int");
+
+ // assertThrown can be used to assert that an exception is thrown
+ assertThrown!ConvException("abc".to!int);
- char[] line;
- enforce(readln(f, line));
- return assumeUnique(line);
+ // ifThrown can be used to provide a default value if an exception is thrown
+ assert("x".to!int().ifThrown(0) == 0);
+
+ // handle is a more advanced version of ifThrown for ranges
+ auto r = "12,1337z32,54".splitter(',').map!(a => to!int(a));
+ auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0);
+ assert(h.equal([12, 0, 54]));
+ assertThrown!ConvException(h.retro.equal([54, 0, 12]));
+
+ // basicExceptionCtors avoids the boilerplate when creating custom exceptions
+ static class MeaCulpa : Exception
+ {
+ mixin basicExceptionCtors;
}
- --------------------
+ e = collectException((){throw new MeaCulpa("diagnostic message");}());
+ assert(e.msg == "diagnostic message");
+ assert(e.file == __FILE__);
+ assert(e.line == __LINE__ - 3);
- Copyright: Copyright Andrei Alexandrescu 2008-, 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
- Source: $(PHOBOSSRC std/_exception.d)
+ // assumeWontThrow can be used to cast throwing code into `nothrow`
+ void exceptionFreeCode() nothrow
+ {
+ // auto-decoding only throws if an invalid UTF char is given
+ assumeWontThrow("abc".front);
+ }
- +/
-module std.exception;
+ // assumeUnique can be used to cast mutable instance to an `immutable` one
+ // use with care
+ char[] str = " mutable".dup;
+ str[0 .. 2] = "im";
+ immutable res = assumeUnique(str);
+ assert(res == "immutable");
+}
import std.range.primitives;
import std.traits;
/++
Asserts that the given expression does $(I not) throw the given type
- of $(D Throwable). If a $(D Throwable) of the given type is thrown,
+ of `Throwable`. If a `Throwable` of the given type is thrown,
it is caught and does not escape assertNotThrown. Rather, an
- $(D AssertError) is thrown. However, any other $(D Throwable)s will escape.
+ `AssertError` is thrown. However, any other `Throwable`s will escape.
Params:
- T = The $(D Throwable) to test for.
+ T = The `Throwable` to test for.
expression = The expression to test.
msg = Optional message to output on test failure.
If msg is empty, and the thrown exception has a
non-empty msg field, the exception's msg field
will be output on test failure.
file = The file where the error occurred.
- Defaults to $(D __FILE__).
+ Defaults to `__FILE__`.
line = The line where the error occurred.
- Defaults to $(D __LINE__).
+ Defaults to `__LINE__`.
Throws:
- $(D AssertError) if the given $(D Throwable) is thrown.
+ `AssertError` if the given `Throwable` is thrown.
Returns:
the result of `expression`.
@@ -226,22 +265,22 @@ auto assertNotThrown(T : Throwable = Exception, E)
}
/++
- Asserts that the given expression throws the given type of $(D Throwable).
- The $(D Throwable) is caught and does not escape assertThrown. However,
- any other $(D Throwable)s $(I will) escape, and if no $(D Throwable)
- of the given type is thrown, then an $(D AssertError) is thrown.
+ Asserts that the given expression throws the given type of `Throwable`.
+ The `Throwable` is caught and does not escape assertThrown. However,
+ any other `Throwable`s $(I will) escape, and if no `Throwable`
+ of the given type is thrown, then an `AssertError` is thrown.
Params:
- T = The $(D Throwable) to test for.
+ T = The `Throwable` to test for.
expression = The expression to test.
msg = Optional message to output on test failure.
file = The file where the error occurred.
- Defaults to $(D __FILE__).
+ Defaults to `__FILE__`.
line = The line where the error occurred.
- Defaults to $(D __LINE__).
+ Defaults to `__LINE__`.
Throws:
- $(D AssertError) if the given $(D Throwable) is not thrown.
+ `AssertError` if the given `Throwable` is not thrown.
+/
void assertThrown(T : Throwable = Exception, E)
(lazy E expression,
@@ -355,55 +394,52 @@ void assertThrown(T : Throwable = Exception, E)
/++
Enforces that the given value is true.
+ If the given value is false, an exception is thrown.
+ The
+ $(UL
+ $(LI `msg` - error message as a `string`)
+ $(LI `dg` - custom delegate that return a string and is only called if an exception occurred)
+ $(LI `ex` - custom exception to be thrown. It is `lazy` and is only created if an exception occurred)
+ )
Params:
value = The value to test.
- E = Exception type to throw if the value evalues to false.
+ E = Exception type to throw if the value evaluates to false.
msg = The error message to put in the exception if it is thrown.
+ dg = The delegate to be called if the value evaluates to false.
+ ex = The exception to throw if the value evaluates to false.
file = The source file of the caller.
line = The line number of the caller.
- Returns: $(D value), if `cast(bool) value` is true. Otherwise,
- $(D new Exception(msg)) is thrown.
+ Returns: `value`, if `cast(bool) value` is true. Otherwise,
+ depending on the chosen overload, `new Exception(msg)`, `dg()` or `ex` is thrown.
Note:
- $(D enforce) is used to throw exceptions and is therefore intended to
+ `enforce` is used to throw exceptions and is therefore intended to
aid in error handling. It is $(I not) intended for verifying the logic
- of your program. That is what $(D assert) is for. Also, do not use
- $(D enforce) inside of contracts (i.e. inside of $(D in) and $(D out)
- blocks and $(D invariant)s), because they will be compiled out when
- compiling with $(I -release). Use $(D assert) in contracts.
-
- Example:
- --------------------
- auto f = enforce(fopen("data.txt"));
- auto line = readln(f);
- enforce(line.length, "Expected a non-empty line.");
- --------------------
+ of your program. That is what `assert` is for. Also, do not use
+ `enforce` inside of contracts (i.e. inside of `in` and `out`
+ blocks and `invariant`s), because contracts are compiled out when
+ compiling with $(I -release).
+
+ If a delegate is passed, the safety and purity of this function are inferred
+ from `Dg`'s safety and purity.
+/
-T enforce(E : Throwable = Exception, T)(T value, lazy const(char)[] msg = null,
-string file = __FILE__, size_t line = __LINE__)
-if (is(typeof({ if (!value) {} })))
+template enforce(E : Throwable = Exception)
+if (is(typeof(new E("", string.init, size_t.init)) : Throwable) ||
+ is(typeof(new E(string.init, size_t.init)) : Throwable))
{
- if (!value) bailOut!E(file, line, msg);
- return value;
+ ///
+ T enforce(T)(T value, lazy const(char)[] msg = null,
+ string file = __FILE__, size_t line = __LINE__)
+ if (is(typeof({ if (!value) {} })))
+ {
+ if (!value) bailOut!E(file, line, msg);
+ return value;
+ }
}
-/++
- Enforces that the given value is true.
-
- Params:
- value = The value to test.
- dg = The delegate to be called if the value evaluates to false.
- file = The source file of the caller.
- line = The line number of the caller.
-
- Returns: $(D value), if `cast(bool) value` is true. Otherwise, the given
- delegate is called.
-
- The safety and purity of this function are inferred from $(D Dg)'s safety
- and purity.
- +/
+/// ditto
T enforce(T, Dg, string file = __FILE__, size_t line = __LINE__)
(T value, scope Dg dg)
if (isSomeFunction!Dg && is(typeof( dg() )) &&
@@ -413,23 +449,40 @@ if (isSomeFunction!Dg && is(typeof( dg() )) &&
return value;
}
-private void bailOut(E : Throwable = Exception)(string file, size_t line, in char[] msg)
+/// ditto
+T enforce(T)(T value, lazy Throwable ex)
{
- static if (is(typeof(new E(string.init, string.init, size_t.init))))
- {
- throw new E(msg ? msg.idup : "Enforcement failed", file, line);
- }
- else static if (is(typeof(new E(string.init, size_t.init))))
- {
- throw new E(file, line);
- }
- else
- {
- static assert(0, "Expected this(string, string, size_t) or this(string, size_t)" ~
- " constructor for " ~ __traits(identifier, E));
- }
+ if (!value) throw ex();
+ return value;
+}
+
+///
+@system unittest
+{
+ import core.stdc.stdlib : malloc, free;
+ import std.conv : ConvException, to;
+
+ // use enforce like assert
+ int a = 3;
+ enforce(a > 2, "a needs to be higher than 2.");
+
+ // enforce can throw a custom exception
+ enforce!ConvException(a > 2, "a needs to be higher than 2.");
+
+ // enforce will return it's input
+ enum size = 42;
+ auto memory = enforce(malloc(size), "malloc failed")[0 .. size];
+ scope(exit) free(memory.ptr);
+}
+
+///
+@safe unittest
+{
+ assertNotThrown(enforce(true, new Exception("this should not be thrown")));
+ assertThrown(enforce(false, new Exception("this should be thrown")));
}
+///
@safe unittest
{
assert(enforce(123) == 123);
@@ -447,9 +500,35 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha
}
}
+/// Alias your own enforce function
+@safe unittest
+{
+ import std.conv : ConvException;
+ alias convEnforce = enforce!ConvException;
+ assertNotThrown(convEnforce(true));
+ assertThrown!ConvException(convEnforce(false, "blah"));
+}
+
+private noreturn bailOut(E : Throwable = Exception)(string file, size_t line, scope const(char)[] msg)
+{
+ static if (is(typeof(new E(string.init, string.init, size_t.init))))
+ {
+ throw new E(msg ? msg.idup : "Enforcement failed", file, line);
+ }
+ else static if (is(typeof(new E(string.init, size_t.init))))
+ {
+ throw new E(file, line);
+ }
+ else
+ {
+ static assert(0, "Expected this(string, string, size_t) or this(string, size_t)" ~
+ " constructor for " ~ __traits(identifier, E));
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=10510
@safe unittest
{
- // Issue 10510
extern(C) void cFoo() { }
enforce(false, &cFoo);
}
@@ -457,14 +536,12 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha
// purity and safety inference test
@system unittest
{
- import std.meta : AliasSeq;
-
- foreach (EncloseSafe; AliasSeq!(false, true))
- foreach (EnclosePure; AliasSeq!(false, true))
+ static foreach (EncloseSafe; [false, true])
+ static foreach (EnclosePure; [false, true])
{
- foreach (BodySafe; AliasSeq!(false, true))
- foreach (BodyPure; AliasSeq!(false, true))
- {
+ static foreach (BodySafe; [false, true])
+ static foreach (BodyPure; [false, true])
+ {{
enum code =
"delegate void() " ~
(EncloseSafe ? "@safe " : "") ~
@@ -485,11 +562,11 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha
"code = ", code);
static assert(__traits(compiles, mixin(code)()) == expect);
- }
+ }}
}
}
-// Test for bugzilla 8637
+// Test for https://issues.dlang.org/show_bug.cgi?id=8637
@system unittest
{
struct S
@@ -523,10 +600,9 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha
enforce!E2(s);
}
+// https://issues.dlang.org/show_bug.cgi?id=14685
@safe unittest
{
- // Issue 14685
-
class E : Exception
{
this() { super("Not found"); }
@@ -535,35 +611,6 @@ private void bailOut(E : Throwable = Exception)(string file, size_t line, in cha
}
/++
- Enforces that the given value is true.
-
- Params:
- value = The value to test.
- ex = The exception to throw if the value evaluates to false.
-
- Returns: $(D value), if `cast(bool) value` is true. Otherwise, $(D ex) is
- thrown.
-
- Example:
- --------------------
- auto f = enforce(fopen("data.txt"));
- auto line = readln(f);
- enforce(line.length, new IOException); // expect a non-empty line
- --------------------
- +/
-T enforce(T)(T value, lazy Throwable ex)
-{
- if (!value) throw ex();
- return value;
-}
-
-@safe unittest
-{
- assertNotThrown(enforce(true, new Exception("this should not be thrown")));
- assertThrown(enforce(false, new Exception("this should be thrown")));
-}
-
-/++
Enforces that the given value is true, throwing an `ErrnoException` if it
is not.
@@ -571,125 +618,37 @@ T enforce(T)(T value, lazy Throwable ex)
value = The value to test.
msg = The message to include in the `ErrnoException` if it is thrown.
- Returns: $(D value), if `cast(bool) value` is true. Otherwise,
+ Returns: `value`, if `cast(bool) value` is true. Otherwise,
$(D new ErrnoException(msg)) is thrown. It is assumed that the last
- operation set $(D errno) to an error code corresponding with the failed
+ operation set `errno` to an error code corresponding with the failed
condition.
-
- Example:
- --------------------
- auto f = errnoEnforce(fopen("data.txt"));
- auto line = readln(f);
- enforce(line.length); // expect a non-empty line
- --------------------
- +/
-T errnoEnforce(T, string file = __FILE__, size_t line = __LINE__)
- (T value, lazy string msg = null)
-{
- if (!value) throw new ErrnoException(msg, file, line);
- return value;
-}
-
-
-/++
- If $(D !value) is $(D false), $(D value) is returned. Otherwise,
- $(D new E(msg, file, line)) is thrown. Or if $(D E) doesn't take a message
- and can be constructed with $(D new E(file, line)), then
- $(D new E(file, line)) will be thrown.
-
- This is legacy name, it is recommended to use $(D enforce!E) instead.
-
- Example:
- --------------------
- auto f = enforceEx!FileMissingException(fopen("data.txt"));
- auto line = readln(f);
- enforceEx!DataCorruptionException(line.length);
- --------------------
+/
-template enforceEx(E : Throwable)
-if (is(typeof(new E("", __FILE__, __LINE__))))
-{
- /++ Ditto +/
- T enforceEx(T)(T value, lazy string msg = "", string file = __FILE__, size_t line = __LINE__)
- {
- if (!value) throw new E(msg, file, line);
- return value;
- }
-}
-
-/++ Ditto +/
-template enforceEx(E : Throwable)
-if (is(typeof(new E(__FILE__, __LINE__))) && !is(typeof(new E("", __FILE__, __LINE__))))
-{
- /++ Ditto +/
- T enforceEx(T)(T value, string file = __FILE__, size_t line = __LINE__)
- {
- if (!value) throw new E(file, line);
- return value;
- }
-}
+alias errnoEnforce = enforce!ErrnoException;
+///
@system unittest
{
- import core.exception : OutOfMemoryError;
- import std.array : empty;
- assertNotThrown(enforceEx!Exception(true));
- assertNotThrown(enforceEx!Exception(true, "blah"));
- assertNotThrown(enforceEx!OutOfMemoryError(true));
-
- {
- auto e = collectException(enforceEx!Exception(false));
- assert(e !is null);
- assert(e.msg.empty);
- assert(e.file == __FILE__);
- assert(e.line == __LINE__ - 4);
- }
+ import core.stdc.stdio : fclose, fgets, fopen;
+ import std.file : thisExePath;
+ import std.string : toStringz;
- {
- auto e = collectException(enforceEx!Exception(false, "hello", "file", 42));
- assert(e !is null);
- assert(e.msg == "hello");
- assert(e.file == "file");
- assert(e.line == 42);
- }
-
- {
- auto e = collectException!Error(enforceEx!OutOfMemoryError(false));
- assert(e !is null);
- assert(e.msg == "Memory allocation failed");
- assert(e.file == __FILE__);
- assert(e.line == __LINE__ - 4);
- }
-
- {
- auto e = collectException!Error(enforceEx!OutOfMemoryError(false, "file", 42));
- assert(e !is null);
- assert(e.msg == "Memory allocation failed");
- assert(e.file == "file");
- assert(e.line == 42);
- }
-
- static assert(!is(typeof(enforceEx!int(true))));
+ auto f = fopen(thisExePath.toStringz, "r").errnoEnforce;
+ scope(exit) fclose(f);
+ char[100] buf;
+ auto line = fgets(buf.ptr, buf.length, f);
+ enforce(line !is null); // expect a non-empty line
}
-@safe unittest
-{
- alias enf = enforceEx!Exception;
- assertNotThrown(enf(true));
- assertThrown(enf(false, "blah"));
-}
-
-
/++
Catches and returns the exception thrown from the given expression.
- If no exception is thrown, then null is returned and $(D result) is
+ If no exception is thrown, then null is returned and `result` is
set to the result of the expression.
- Note that while $(D collectException) $(I can) be used to collect any
- $(D Throwable) and not just $(D Exception)s, it is generally ill-advised to
- catch anything that is neither an $(D Exception) nor a type derived from
- $(D Exception). So, do not use $(D collectException) to collect
- non-$(D Exception)s unless you're sure that that's what you really want to
+ Note that while `collectException` $(I can) be used to collect any
+ `Throwable` and not just `Exception`s, it is generally ill-advised to
+ catch anything that is neither an `Exception` nor a type derived from
+ `Exception`. So, do not use `collectException` to collect
+ non-`Exception`s unless you're sure that that's what you really want to
do.
Params:
@@ -723,14 +682,14 @@ T collectException(T = Exception, E)(lazy E expression, ref E result)
/++
Catches and returns the exception thrown from the given expression.
- If no exception is thrown, then null is returned. $(D E) can be
- $(D void).
-
- Note that while $(D collectException) $(I can) be used to collect any
- $(D Throwable) and not just $(D Exception)s, it is generally ill-advised to
- catch anything that is neither an $(D Exception) nor a type derived from
- $(D Exception). So, do not use $(D collectException) to collect
- non-$(D Exception)s unless you're sure that that's what you really want to
+ If no exception is thrown, then null is returned. `E` can be
+ `void`.
+
+ Note that while `collectException` $(I can) be used to collect any
+ `Throwable` and not just `Exception`s, it is generally ill-advised to
+ catch anything that is neither an `Exception` nor a type derived from
+ `Exception`. So, do not use `collectException` to collect
+ non-`Exception`s unless you're sure that that's what you really want to
do.
Params:
@@ -750,25 +709,26 @@ T collectException(T : Throwable = Exception, E)(lazy E expression)
return null;
}
+///
@safe unittest
{
int foo() { throw new Exception("blah"); }
- assert(collectException(foo()));
+ assert(collectException(foo()).msg == "blah");
}
/++
Catches the exception thrown from the given expression and returns the
msg property of that exception. If no exception is thrown, then null is
- returned. $(D E) can be $(D void).
+ returned. `E` can be `void`.
If an exception is thrown but it has an empty message, then
- $(D emptyExceptionMsg) is returned.
+ `emptyExceptionMsg` is returned.
- Note that while $(D collectExceptionMsg) $(I can) be used to collect any
- $(D Throwable) and not just $(D Exception)s, it is generally ill-advised to
- catch anything that is neither an $(D Exception) nor a type derived from
- $(D Exception). So, do not use $(D collectExceptionMsg) to collect
- non-$(D Exception)s unless you're sure that that's what you really want to
+ Note that while `collectExceptionMsg` $(I can) be used to collect any
+ `Throwable` and not just `Exception`s, it is generally ill-advised to
+ catch anything that is neither an `Exception` nor a type derived from
+ `Exception`. So, do not use `collectExceptionMsg` to collect
+ non-`Exception`s unless you're sure that that's what you really want to
do.
Params:
@@ -808,18 +768,18 @@ enum emptyExceptionMsg = "<Empty Exception Message>";
/**
* Casts a mutable array to an immutable array in an idiomatic
- * manner. Technically, $(D assumeUnique) just inserts a cast,
+ * manner. Technically, `assumeUnique` just inserts a cast,
* but its name documents assumptions on the part of the
- * caller. $(D assumeUnique(arr)) should only be called when
+ * caller. `assumeUnique(arr)` should only be called when
* there are no more active mutable aliases to elements of $(D
- * arr). To strengthen this assumption, $(D assumeUnique(arr))
- * also clears $(D arr) before returning. Essentially $(D
+ * arr). To strengthen this assumption, `assumeUnique(arr)`
+ * also clears `arr` before returning. Essentially $(D
* assumeUnique(arr)) indicates commitment from the caller that there
- * is no more mutable access to any of $(D arr)'s elements
+ * is no more mutable access to any of `arr`'s elements
* (transitively), and that all future accesses will be done through
- * the immutable array returned by $(D assumeUnique).
+ * the immutable array returned by `assumeUnique`.
*
- * Typically, $(D assumeUnique) is used to return arrays from
+ * Typically, `assumeUnique` is used to return arrays from
* functions that have allocated and built them.
*
* Params:
@@ -829,6 +789,7 @@ enum emptyExceptionMsg = "<Empty Exception Message>";
*
* Example:
*
+ * $(RUNNABLE_EXAMPLE
* ----
* string letters()
* {
@@ -840,14 +801,16 @@ enum emptyExceptionMsg = "<Empty Exception Message>";
* return assumeUnique(result);
* }
* ----
+ * )
*
- * The use in the example above is correct because $(D result)
- * was private to $(D letters) and is inaccessible in writing
+ * The use in the example above is correct because `result`
+ * was private to `letters` and is inaccessible in writing
* after the function returns. The following example shows an
- * incorrect use of $(D assumeUnique).
+ * incorrect use of `assumeUnique`.
*
* Bad:
*
+ * $(RUNNABLE_EXAMPLE
* ----
* private char[] buffer;
* string letters(char first, char last)
@@ -862,11 +825,13 @@ enum emptyExceptionMsg = "<Empty Exception Message>";
* return assumeUnique(sneaky); // BAD
* }
* ----
+ * )
*
* The example above wreaks havoc on client code because it is
* modifying arrays that callers considered immutable. To obtain an
- * immutable array from the writable array $(D buffer), replace
+ * immutable array from the writable array `buffer`, replace
* the last line with:
+ *
* ----
* return to!(string)(sneaky); // not that sneaky anymore
* ----
@@ -878,6 +843,8 @@ enum emptyExceptionMsg = "<Empty Exception Message>";
* marked as a pure function. The following example does not
* need to call assumeUnique because the compiler can infer the
* uniqueness of the array in the pure function:
+ *
+ * $(RUNNABLE_EXAMPLE
* ----
* string letters() pure
* {
@@ -889,16 +856,17 @@ enum emptyExceptionMsg = "<Empty Exception Message>";
* return result;
* }
* ----
+ * )
*
* For more on infering uniqueness see the $(B unique) and
* $(B lent) keywords in the
- * $(HTTP archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf, ArchJava)
+ * $(HTTP www.cs.cmu.edu/~aldrich/papers/aldrich-dissertation.pdf, ArchJava)
* language.
*
- * The downside of using $(D assumeUnique)'s
+ * The downside of using `assumeUnique`'s
* convention-based usage is that at this time there is no
* formal checking of the correctness of the assumption;
- * on the upside, the idiomatic use of $(D assumeUnique) is
+ * on the upside, the idiomatic use of `assumeUnique` is
* simple and rare enough to be tolerable.
*
*/
@@ -921,34 +889,38 @@ immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow
return result;
}
+///
@system unittest
{
- // @system due to assumeUnique
int[] arr = new int[1];
- auto arr1 = assumeUnique(arr);
- assert(is(typeof(arr1) == immutable(int)[]) && arr == null);
+ auto arr1 = arr.assumeUnique;
+ static assert(is(typeof(arr1) == immutable(int)[]));
+ assert(arr == null);
+ assert(arr1 == [0]);
}
-// @@@BUG@@@
-version (none) @system unittest
+///
+@system unittest
{
int[string] arr = ["a":1];
- auto arr1 = assumeUnique(arr);
- assert(is(typeof(arr1) == immutable(int[string])) && arr == null);
+ auto arr1 = arr.assumeUnique;
+ static assert(is(typeof(arr1) == immutable(int[string])));
+ assert(arr == null);
+ assert(arr1.keys == ["a"]);
}
/**
- * Wraps a possibly-throwing expression in a $(D nothrow) wrapper so that it
- * can be called by a $(D nothrow) function.
+ * Wraps a possibly-throwing expression in a `nothrow` wrapper so that it
+ * can be called by a `nothrow` function.
*
* This wrapper function documents commitment on the part of the caller that
* the appropriate steps have been taken to avoid whatever conditions may
- * trigger an exception during the evaluation of $(D expr). If it turns out
+ * trigger an exception during the evaluation of `expr`. If it turns out
* that the expression $(I does) throw at runtime, the wrapper will throw an
- * $(D AssertError).
+ * `AssertError`.
*
- * (Note that $(D Throwable) objects such as $(D AssertError) that do not
- * subclass $(D Exception) may be thrown even from $(D nothrow) functions,
+ * (Note that `Throwable` objects such as `AssertError` that do not
+ * subclass `Exception` may be thrown even from `nothrow` functions,
* since they are considered to be serious runtime problems that cannot be
* recovered from.)
*
@@ -984,7 +956,7 @@ T assumeWontThrow(T)(lazy T expr,
///
@safe unittest
{
- import std.math : sqrt;
+ import std.math.algebraic : sqrt;
// This function may throw.
int squareRoot(int x)
@@ -1030,37 +1002,41 @@ Params:
source = The source object
target = The target object
-Returns: $(D true) if $(D source)'s representation embeds a pointer
-that points to $(D target)'s representation or somewhere inside
+Bugs:
+ The function is explicitly annotated `@nogc` because inference could fail,
+ see $(LINK2 https://issues.dlang.org/show_bug.cgi?id=17084, issue 17084).
+
+Returns: `true` if `source`'s representation embeds a pointer
+that points to `target`'s representation or somewhere inside
it.
-If $(D source) is or contains a dynamic array, then, then these functions will check
-if there is overlap between the dynamic array and $(D target)'s representation.
+If `source` is or contains a dynamic array, then, then these functions will check
+if there is overlap between the dynamic array and `target`'s representation.
-If $(D source) is a class, then it will be handled as a pointer.
+If `source` is a class, then it will be handled as a pointer.
-If $(D target) is a pointer, a dynamic array or a class, then these functions will only
-check if $(D source) points to $(D target), $(I not) what $(D target) references.
+If `target` is a pointer, a dynamic array or a class, then these functions will only
+check if `source` points to `target`, $(I not) what `target` references.
-If $(D source) is or contains a union, then there may be either false positives or
+If `source` is or contains a union or `void[n]`, then there may be either false positives or
false negatives:
-$(D doesPointTo) will return $(D true) if it is absolutely certain
-$(D source) points to $(D target). It may produce false negatives, but never
+`doesPointTo` will return `true` if it is absolutely certain
+`source` points to `target`. It may produce false negatives, but never
false positives. This function should be prefered when trying to validate
input data.
-$(D mayPointTo) will return $(D false) if it is absolutely certain
-$(D source) does not point to $(D target). It may produce false positives, but never
+`mayPointTo` will return `false` if it is absolutely certain
+`source` does not point to `target`. It may produce false positives, but never
false negatives. This function should be prefered for defensively choosing a
code path.
-Note: Evaluating $(D doesPointTo(x, x)) checks whether $(D x) has
+Note: Evaluating $(D doesPointTo(x, x)) checks whether `x` has
internal pointers. This should only be done as an assertive test,
as the language is free to assume objects don't have internal pointers
(TDPL 7.1.3.5).
*/
-bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow
+bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @nogc @trusted pure nothrow
if (__traits(isRef, source) || isDynamicArray!S ||
isPointer!S || is(S == class))
{
@@ -1080,8 +1056,11 @@ if (__traits(isRef, source) || isDynamicArray!S ||
}
else static if (isStaticArray!S)
{
- foreach (size_t i; 0 .. S.length)
- if (doesPointTo(source[i], target)) return true;
+ static if (!is(S == void[n], size_t n))
+ {
+ foreach (ref s; source)
+ if (doesPointTo(s, target)) return true;
+ }
return false;
}
else static if (isDynamicArray!S)
@@ -1122,8 +1101,38 @@ if (__traits(isRef, source) || isDynamicArray!S ||
}
else static if (isStaticArray!S)
{
- foreach (size_t i; 0 .. S.length)
- if (mayPointTo(source[i], target)) return true;
+ static if (is(S == void[n], size_t n))
+ {
+ static if (n >= (void[]).sizeof)
+ {
+ // could contain a slice, which could point at anything.
+ // But a void[N] that is all 0 cannot point anywhere
+ import std.algorithm.searching : any;
+ if (__ctfe || any(cast(ubyte[]) source[]))
+ return true;
+ }
+ else static if (n >= (void*).sizeof)
+ {
+ // Reinterpreting cast is impossible during ctfe
+ if (__ctfe)
+ return true;
+
+ // Only check for properly aligned pointers
+ enum al = (void*).alignof - 1;
+ const base = cast(size_t) &source;
+ const alBase = (base + al) & ~al;
+
+ if ((n - (alBase - base)) >= (void*).sizeof &&
+ mayPointTo(*(cast(void**) alBase), target))
+ return true;
+ }
+ }
+ else
+ {
+ foreach (size_t i; 0 .. S.length)
+ if (mayPointTo(source[i], target)) return true;
+ }
+
return false;
}
else static if (isDynamicArray!S)
@@ -1179,9 +1188,12 @@ bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target)
@system unittest
{
int i;
+ // trick the compiler when initializing slice
+ // https://issues.dlang.org/show_bug.cgi?id=18637
+ int* p = &i;
int[] slice = [0, 1, 2, 3, 4];
int[5] arr = [0, 1, 2, 3, 4];
- int*[] slicep = [&i];
+ int*[] slicep = [p];
int*[1] arrp = [&i];
// A slice points to all of its members:
@@ -1241,6 +1253,37 @@ bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target)
assert(b.doesPointTo(*aLoc)); // b points to where a is pointing
}
+
+version (StdUnittest)
+{
+ // https://issues.dlang.org/show_bug.cgi?id=17084
+ // the bug doesn't happen if these declarations are in the unittest block
+ // (static or not).
+ private struct Page17084
+ {
+ URL17084 url;
+ int opCmp(P)(P) { return 0; }
+ int opCmp(P)(shared(P)) shared { return 0; }
+ }
+
+ private struct URL17084
+ {
+ int[] queryParams;
+ string toString()() const { return ""; }
+ alias toString this;
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17084
+@system unittest
+{
+ import std.algorithm.sorting : sort;
+ Page17084[] s;
+ sort(s);
+ shared(Page17084)[] p;
+ sort(p);
+}
+
@system unittest
{
struct S1 { int a; S1 * b; }
@@ -1337,6 +1380,57 @@ bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target)
assert( doesPointTo(ss, a)); //The array contains a struct that points to a
assert(!doesPointTo(ss, b)); //The array doesn't contains a struct that points to b
assert(!doesPointTo(ss, ss)); //The array doesn't point itself.
+
+ // https://issues.dlang.org/show_bug.cgi?id=20426
+ align((void*).alignof) void[32] voidArr = void;
+ (cast(void*[]) voidArr[])[] = null; // Ensure no false pointers
+
+ // zeroed void ranges can't point at anything
+ assert(!mayPointTo(voidArr, a));
+ assert(!mayPointTo(voidArr, b));
+
+ *cast(void**) &voidArr[16] = &a; // Pointers should be found
+
+ alias SA = void[size_t.sizeof + 3];
+ SA *smallArr1 = cast(SA*)&voidArr;
+ SA *smallArr2 = cast(SA*)&(voidArr[16]);
+
+ // But it should only consider properly aligned pointers
+ // Write single bytes to avoid issues due to misaligned writes
+ void*[1] tmp = [&b];
+ (cast(ubyte[]) voidArr[3 .. 3 + (void*).sizeof])[] = cast(ubyte[]) tmp[];
+
+
+ assert( mayPointTo(*smallArr2, a));
+ assert(!mayPointTo(*smallArr1, b));
+
+ assert(!doesPointTo(voidArr, a)); // Value might be a false pointer
+ assert(!doesPointTo(voidArr, b));
+
+ SA *smallArr3 = cast(SA *) &voidArr[13]; // Works for weird sizes/alignments
+ assert( mayPointTo(*smallArr3, a));
+ assert(!mayPointTo(*smallArr3, b));
+
+ assert(!doesPointTo(*smallArr3, a));
+ assert(!doesPointTo(*smallArr3, b));
+
+ auto v3 = cast(void[3]*) &voidArr[16]; // Arrays smaller than pointers are ignored
+ assert(!mayPointTo(*v3, a));
+ assert(!mayPointTo(*v3, b));
+
+ assert(!doesPointTo(*v3, a));
+ assert(!doesPointTo(*v3, b));
+
+ assert(mayPointTo(voidArr, a)); // slice-contiaining void[N] might point at anything
+ assert(mayPointTo(voidArr, b));
+
+ static assert(() {
+ void[16] arr1 = void;
+ void[size_t.sizeof] arr2 = void;
+ int var;
+ return mayPointTo(arr1, var) && !doesPointTo(arr1, var) &&
+ mayPointTo(arr2, var) && !doesPointTo(arr2, var);
+ }());
}
@@ -1425,7 +1519,7 @@ bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target)
}
/+
-Returns true if the field at index $(D i) in ($D T) shares its address with another field.
+Returns true if the field at index `i` in ($D T) shares its address with another field.
Note: This does not merelly check if the field is a member of an union, but also that
it is not a single child.
@@ -1510,53 +1604,57 @@ package string errnoString(int errno) nothrow @trusted
}
/*********************
- * Thrown if errors that set $(D errno) occur.
+ * Thrown if errors that set `errno` occur.
*/
class ErrnoException : Exception
{
- final @property uint errno() { return _errno; } /// Operating system error code.
+ /// Operating system error code.
+ final @property uint errno() nothrow pure @nogc @safe { return _errno; }
private uint _errno;
/// Constructor which takes an error message. The current global $(REF errno, core,stdc,errno) value is used as error code.
- this(string msg, string file = null, size_t line = 0) @trusted
+ this(string msg, string file = null, size_t line = 0) @safe
{
import core.stdc.errno : errno;
this(msg, errno, file, line);
}
/// Constructor which takes an error message and error code.
- this(string msg, int errno, string file = null, size_t line = 0) @trusted
+ this(string msg, int errno, string file = null, size_t line = 0) @safe
{
_errno = errno;
super(msg ~ " (" ~ errnoString(errno) ~ ")", file, line);
}
+}
- @system unittest
- {
- import core.stdc.errno : errno, EAGAIN;
+///
+@safe unittest
+{
+ import core.stdc.errno : EAGAIN;
+ auto ex = new ErrnoException("oh no", EAGAIN);
+ assert(ex.errno == EAGAIN);
+}
- auto old = errno;
- scope(exit) errno = old;
+/// errno is used by default if no explicit error code is provided
+@safe unittest
+{
+ import core.stdc.errno : errno, EAGAIN;
- errno = EAGAIN;
- auto ex = new ErrnoException("oh no");
- assert(ex.errno == EAGAIN);
- }
+ auto old = errno;
+ scope(exit) errno = old;
- @system unittest
- {
- import core.stdc.errno : EAGAIN;
- auto ex = new ErrnoException("oh no", EAGAIN);
- assert(ex.errno == EAGAIN);
- }
+ // fake that errno got set by the callee
+ errno = EAGAIN;
+ auto ex = new ErrnoException("oh no");
+ assert(ex.errno == EAGAIN);
}
/++
ML-style functional exception handling. Runs the supplied expression and
- returns its result. If the expression throws a $(D Throwable), runs the
+ returns its result. If the expression throws a `Throwable`, runs the
supplied error handler instead and return its result. The error handler's
type must be the same as the expression's type.
Params:
- E = The type of $(D Throwable)s to catch. Defaults to $(D Exception)
+ E = The type of `Throwable`s to catch. Defaults to `Exception`
T1 = The type of the expression.
T2 = The return type of the error handler.
expression = The expression to run and return its result.
@@ -1565,54 +1663,7 @@ class ErrnoException : Exception
Returns:
expression, if it does not throw. Otherwise, returns the result of
errorHandler.
-
- Example:
- --------------------
- //Revert to a default value upon an error:
- assert("x".to!int().ifThrown(0) == 0);
- --------------------
-
- You can also chain multiple calls to ifThrown, each capturing errors from the
- entire preceding expression.
-
- Example:
- --------------------
- //Chaining multiple calls to ifThrown to attempt multiple things in a row:
- string s="true";
- assert(s.to!int().
- ifThrown(cast(int) s.to!double()).
- ifThrown(cast(int) s.to!bool())
- == 1);
-
- //Respond differently to different types of errors
- assert(enforce("x".to!int() < 1).to!string()
- .ifThrown!ConvException("not a number")
- .ifThrown!Exception("number too small")
- == "not a number");
- --------------------
-
- The expression and the errorHandler must have a common type they can both
- be implicitly casted to, and that type will be the type of the compound
- expression.
-
- Example:
- --------------------
- //null and new Object have a common type(Object).
- static assert(is(typeof(null.ifThrown(new Object())) == Object));
- static assert(is(typeof((new Object()).ifThrown(null)) == Object));
-
- //1 and new Object do not have a common type.
- static assert(!__traits(compiles, 1.ifThrown(new Object())));
- static assert(!__traits(compiles, (new Object()).ifThrown(1)));
- --------------------
-
- If you need to use the actual thrown exception, you can use a delegate.
- Example:
- --------------------
- //Use a lambda to get the thrown object.
- assert("%s".format().ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException");
- --------------------
- +/
++/
//lazy version
CommonType!(T1, T2) ifThrown(E : Throwable = Exception, T1, T2)(lazy scope T1 expression, lazy scope T2 errorHandler)
{
@@ -1675,6 +1726,59 @@ CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate
}
}
+/// Revert to a default value upon an error:
+@safe unittest
+{
+ import std.conv : to;
+ assert("x".to!int.ifThrown(0) == 0);
+}
+
+/**
+Chain multiple calls to ifThrown, each capturing errors from the
+entire preceding expression.
+*/
+@safe unittest
+{
+ import std.conv : ConvException, to;
+ string s = "true";
+ assert(s.to!int.ifThrown(cast(int) s.to!double)
+ .ifThrown(cast(int) s.to!bool) == 1);
+
+ s = "2.0";
+ assert(s.to!int.ifThrown(cast(int) s.to!double)
+ .ifThrown(cast(int) s.to!bool) == 2);
+
+ // Respond differently to different types of errors
+ alias orFallback = (lazy a) => a.ifThrown!ConvException("not a number")
+ .ifThrown!Exception("number too small");
+
+ assert(orFallback(enforce("x".to!int < 1).to!string) == "not a number");
+ assert(orFallback(enforce("2".to!int < 1).to!string) == "number too small");
+}
+
+/**
+The expression and the errorHandler must have a common type they can both
+be implicitly casted to, and that type will be the type of the compound
+expression.
+*/
+@safe unittest
+{
+ // null and new Object have a common type(Object).
+ static assert(is(typeof(null.ifThrown(new Object())) == Object));
+ static assert(is(typeof((new Object()).ifThrown(null)) == Object));
+
+ // 1 and new Object do not have a common type.
+ static assert(!__traits(compiles, 1.ifThrown(new Object())));
+ static assert(!__traits(compiles, (new Object()).ifThrown(1)));
+}
+
+/// Use a lambda to get the thrown object.
+@system unittest
+{
+ import std.format : format;
+ assert("%s".format.ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException");
+}
+
//Verify Examples
@system unittest
{
@@ -1745,22 +1849,22 @@ CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate
static assert(!__traits(compiles, (new Object()).ifThrown(e=>1)));
}
-version (unittest) package
-@property void assertCTFEable(alias dg)()
+version (StdUnittest) package
+void assertCTFEable(alias dg)()
{
static assert({ cast(void) dg(); return true; }());
cast(void) dg();
}
-/** This $(D enum) is used to select the primitives of the range to handle by the
- $(LREF handle) range wrapper. The values of the $(D enum) can be $(D OR)'d to
+/** This `enum` is used to select the primitives of the range to handle by the
+ $(LREF handle) range wrapper. The values of the `enum` can be `OR`'d to
select multiple primitives to be handled.
- $(D RangePrimitive.access) is a shortcut for the access primitives; $(D front),
- $(D back) and $(D opIndex).
+ `RangePrimitive.access` is a shortcut for the access primitives; `front`,
+ `back` and `opIndex`.
- $(D RangePrimitive.pop) is a shortcut for the mutating primitives;
- $(D popFront) and $(D popBack).
+ `RangePrimitive.pop` is a shortcut for the mutating primitives;
+ `popFront` and `popBack`.
*/
enum RangePrimitive
{
@@ -1778,31 +1882,65 @@ enum RangePrimitive
pop = popFront | popBack, /// Ditto
}
+///
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map, splitter;
+ import std.conv : to, ConvException;
+
+ auto s = "12,1337z32,54,2,7,9,1z,6,8";
+
+ // The next line composition will throw when iterated
+ // as some elements of the input do not convert to integer
+ auto r = s.splitter(',').map!(a => to!int(a));
+
+ // Substitute 0 for cases of ConvException
+ auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0);
+ assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8]));
+}
+
+///
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+ import std.utf : UTFException;
+
+ auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit
+
+ auto handled = str.handle!(UTFException, RangePrimitive.access,
+ (e, r) => ' '); // Replace invalid code points with spaces
+
+ assert(handled.equal("hello world")); // `front` is handled,
+ assert(handled.retro.equal("dlrow olleh")); // as well as `back`
+}
+
/** Handle exceptions thrown from range primitives.
Use the $(LREF RangePrimitive) enum to specify which primitives to _handle.
-Multiple range primitives can be handled at once by using the $(D OR) operator
-or the pseudo-primitives $(D RangePrimitive.access) and $(D RangePrimitive.pop).
+Multiple range primitives can be handled at once by using the `OR` operator
+or the pseudo-primitives `RangePrimitive.access` and `RangePrimitive.pop`.
All handled primitives must have return types or values compatible with the
user-supplied handler.
Params:
- E = The type of $(D Throwable) to _handle.
+ E = The type of `Throwable` to _handle.
primitivesToHandle = Set of range primitives to _handle.
handler = The callable that is called when a handled primitive throws a
- $(D Throwable) of type $(D E). The handler must accept arguments of
+ `Throwable` of type `E`. The handler must accept arguments of
the form $(D E, ref IRange) and its return value is used as the primitive's
- return value whenever $(D E) is thrown. For $(D opIndex), the handler can
+ return value whenever `E` is thrown. For `opIndex`, the handler can
optionally recieve a third argument; the index that caused the exception.
input = The range to _handle.
-Returns: A wrapper $(D struct) that preserves the range interface of $(D input).
+Returns: A wrapper `struct` that preserves the range interface of `input`.
Note:
Infinite ranges with slicing support must return an instance of
$(REF Take, std,range) when sliced with a specific lower and upper
-bound (see $(REF hasSlicing, std,range,primitives)); $(D handle) deals with
-this by $(D take)ing 0 from the return value of the handler function and
+bound (see $(REF hasSlicing, std,range,primitives)); `handle` deals with
+this by `take`ing 0 from the return value of the handler function and
returning that when an exception is caught.
*/
auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Range)(Range input)
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d
index cfeae0cdb77..1e3df91f95e 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d
@@ -1,31 +1,34 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/affix_allocator.d)
+*/
module std.experimental.allocator.building_blocks.affix_allocator;
/**
-Allocator that adds some extra data before (of type $(D Prefix)) and/or after
-(of type $(D Suffix)) any allocation made with its parent allocator. This is
+Allocator that adds some extra data before (of type `Prefix`) and/or after
+(of type `Suffix`) any allocation made with its parent allocator. This is
useful for uses where additional allocation-related information is needed, such
as mutexes, reference counts, or walls for debugging memory corruption errors.
-If $(D Prefix) is not $(D void), $(D Allocator) must guarantee an alignment at
-least as large as $(D Prefix.alignof).
+If `Prefix` is not `void`, `Allocator` must guarantee an alignment at
+least as large as `Prefix.alignof`.
Suffixes are slower to get at because of alignment rounding, so prefixes should
be preferred. However, small prefixes blunt the alignment so if a large
alignment with a small affix is needed, suffixes should be chosen.
-The following methods are defined if $(D Allocator) defines them, and forward to it: $(D deallocateAll), $(D empty), $(D owns).
+The following methods are defined if `Allocator` defines them, and forward to it: `deallocateAll`, `empty`, `owns`.
*/
struct AffixAllocator(Allocator, Prefix, Suffix = void)
{
import std.algorithm.comparison : min;
- import std.conv : emplace;
- import std.experimental.allocator : IAllocator, theAllocator;
+ import core.lifetime : emplace;
+ import std.experimental.allocator : RCIAllocator, theAllocator;
import std.experimental.allocator.common : stateSize, forwardToMember,
roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf,
hasStaticallyKnownAlignment;
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
import std.traits : hasMember;
import std.typecons : Ternary;
@@ -40,7 +43,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
"This restriction could be relaxed in the future.");
/**
- If $(D Prefix) is $(D void), the alignment is that of the parent. Otherwise, the alignment is the same as the $(D Prefix)'s alignment.
+ If `Prefix` is `void`, the alignment is that of the parent. Otherwise, the alignment is the same as the `Prefix`'s alignment.
*/
static if (hasStaticallyKnownAlignment!Allocator)
{
@@ -58,20 +61,45 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
}
/**
- If the parent allocator $(D Allocator) is stateful, an instance of it is
- stored as a member. Otherwise, $(D AffixAllocator) uses
- `Allocator.instance`. In either case, the name $(D _parent) is uniformly
+ If the parent allocator `Allocator` is stateful, an instance of it is
+ stored as a member. Otherwise, `AffixAllocator` uses
+ `Allocator.instance`. In either case, the name `_parent` is uniformly
used for accessing the parent allocator.
*/
static if (stateSize!Allocator)
{
Allocator _parent;
- static if (is(Allocator == IAllocator))
+ static if (is(Allocator == RCIAllocator))
{
+ @nogc nothrow pure @safe
Allocator parent()
{
- if (_parent is null) _parent = theAllocator;
- assert(alignment <= _parent.alignment);
+ static @nogc nothrow
+ RCIAllocator wrapAllocatorObject()
+ {
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator : allocatorObject;
+
+ return allocatorObject(GCAllocator.instance);
+ }
+
+ if (_parent.isNull)
+ {
+ // If the `_parent` allocator is `null` we will assign
+ // an object that references the GC as the `parent`.
+ auto fn = (() @trusted =>
+ cast(RCIAllocator function() @nogc nothrow pure @safe)(&wrapAllocatorObject))();
+ _parent = fn();
+ }
+
+ // `RCIAllocator.alignment` currently doesn't have any attributes
+ // so we must cast; throughout the allocators module, `alignment`
+ // is defined as an `enum` for the existing allocators.
+ // `alignment` should always be `@nogc nothrow pure @safe`; once
+ // this is enforced by the interface we can remove the cast
+ auto pureAlign = (() @trusted =>
+ cast(uint delegate() @nogc nothrow pure @safe)(&_parent.alignment))();
+ assert(alignment <= pureAlign());
return _parent;
}
}
@@ -119,10 +147,9 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
[0 .. actualAllocationSize(b.length)];
}
- void[] allocate(size_t bytes)
- {
- if (!bytes) return null;
- auto result = parent.allocate(actualAllocationSize(bytes));
+ // Common code shared between allocate and allocateZeroed.
+ private enum _processAndReturnAllocateResult =
+ q{
if (result is null) return null;
static if (stateSize!Prefix)
{
@@ -136,6 +163,21 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
emplace!Suffix(cast(Suffix*)(suffixP));
}
return result[stateSize!Prefix .. stateSize!Prefix + bytes];
+ };
+
+ void[] allocate(size_t bytes)
+ {
+ if (!bytes) return null;
+ auto result = parent.allocate(actualAllocationSize(bytes));
+ mixin(_processAndReturnAllocateResult);
+ }
+
+ static if (hasMember!(Allocator, "allocateZeroed"))
+ package(std) void[] allocateZeroed()(size_t bytes)
+ {
+ if (!bytes) return null;
+ auto result = parent.allocateZeroed(actualAllocationSize(bytes));
+ mixin(_processAndReturnAllocateResult);
}
static if (hasMember!(Allocator, "allocateAll"))
@@ -171,7 +213,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
Ternary owns(void[] b)
{
if (b is null) return Ternary.no;
- return parent.owns(actualAllocation(b));
+ return parent.owns((() @trusted => actualAllocation(b))());
}
static if (hasMember!(Allocator, "resolveInternalPointer"))
@@ -182,20 +224,22 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
if (r != Ternary.yes || p1 is null)
return r;
p1 = p1[stateSize!Prefix .. $];
- auto p2 = (p1.ptr + p1.length - stateSize!Suffix)
- .alignDownTo(Suffix.alignof);
- result = p1[0 .. p2 - p1.ptr];
+ auto p2 = (() @trusted => (&p1[0] + p1.length - stateSize!Suffix)
+ .alignDownTo(Suffix.alignof))();
+ result = p1[0 .. p2 - &p1[0]];
return Ternary.yes;
}
- static if (!stateSize!Suffix && hasMember!(Allocator, "expand"))
+ static if (!stateSize!Suffix && hasMember!(Allocator, "expand")
+ && hasMember!(Allocator, "owns"))
bool expand(ref void[] b, size_t delta)
{
- if (!b.ptr) return delta == 0;
- auto t = actualAllocation(b);
+ if (!b || delta == 0) return delta == 0;
+ if (owns(b) == Ternary.no) return false;
+ auto t = (() @trusted => actualAllocation(b))();
const result = parent.expand(t, delta);
if (!result) return false;
- b = b.ptr[0 .. b.length + delta];
+ b = (() @trusted => b.ptr[0 .. b.length + delta])();
return true;
}
@@ -221,8 +265,8 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
return parent.deallocate(actualAllocation(b));
}
- /* The following methods are defined if $(D ParentAllocator) defines
- them, and forward to it: $(D deallocateAll), $(D empty).*/
+ /* The following methods are defined if `ParentAllocator` defines
+ them, and forward to it: `deallocateAll`, `empty`.*/
mixin(forwardToMember("parent",
"deallocateAll", "empty"));
@@ -267,7 +311,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
{
/**
Standard allocator methods. Each is defined if and only if the parent
- allocator defines the homonym method (except for $(D goodAllocSize),
+ allocator defines the homonym method (except for `goodAllocSize`,
which may use the global default). Also, the methods will be $(D
shared) if the parent allocator defines them as such.
*/
@@ -344,14 +388,20 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
}
else static if (is(typeof(Allocator.instance) == shared))
{
+ static assert(stateSize!Allocator == 0);
static shared AffixAllocator instance;
shared { mixin Impl!(); }
}
+ else static if (is(Allocator == shared))
+ {
+ static assert(stateSize!Allocator != 0);
+ shared { mixin Impl!(); }
+ }
else
{
mixin Impl!();
static if (stateSize!Allocator == 0)
- static __gshared AffixAllocator instance;
+ __gshared AffixAllocator instance;
}
}
@@ -371,10 +421,10 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
@system unittest
{
import std.experimental.allocator.gc_allocator : GCAllocator;
- import std.experimental.allocator : theAllocator, IAllocator;
+ import std.experimental.allocator : theAllocator, RCIAllocator;
// One word before and after each allocation.
- auto A = AffixAllocator!(IAllocator, size_t, size_t)(theAllocator);
+ auto A = AffixAllocator!(RCIAllocator, size_t, size_t)(theAllocator);
auto a = A.allocate(11);
A.prefix(a) = 0xCAFE_BABE;
A.suffix(a) = 0xDEAD_BEEF;
@@ -382,7 +432,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
&& A.suffix(a) == 0xDEAD_BEEF);
// One word before and after each allocation.
- auto B = AffixAllocator!(IAllocator, size_t, size_t)();
+ auto B = AffixAllocator!(RCIAllocator, size_t, size_t)();
auto b = B.allocate(11);
B.prefix(b) = 0xCAFE_BABE;
B.suffix(b) = 0xDEAD_BEEF;
@@ -390,6 +440,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
&& B.suffix(b) == 0xDEAD_BEEF);
}
+version (StdUnittest)
@system unittest
{
import std.experimental.allocator.building_blocks.bitmapped_block
@@ -402,6 +453,20 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
});
}
+// Test empty
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+ import std.typecons : Ternary;
+
+ auto a = AffixAllocator!(BitmappedBlock!128, ulong, ulong)
+ (BitmappedBlock!128(new ubyte[128 * 4096]));
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no);
+}
+
@system unittest
{
import std.experimental.allocator.mallocator : Mallocator;
@@ -435,7 +500,63 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
static assert(is(typeof(&MyAllocator.instance.prefix(e)) == const(uint)*));
void[] p;
- assert(MyAllocator.instance.resolveInternalPointer(null, p) == Ternary.no);
- Ternary r = MyAllocator.instance.resolveInternalPointer(d.ptr, p);
+ assert((() nothrow @safe @nogc => MyAllocator.instance.resolveInternalPointer(null, p))() == Ternary.no);
+ assert((() nothrow @safe => MyAllocator.instance.resolveInternalPointer(&d[0], p))() == Ternary.yes);
assert(p.ptr is d.ptr && p.length >= d.length);
}
+
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator;
+ alias a = AffixAllocator!(GCAllocator, uint).instance;
+
+ // Check that goodAllocSize inherits from parent, i.e. GCAllocator
+ assert(__traits(compiles, (() nothrow @safe @nogc => a.goodAllocSize(1))()));
+
+ // Ensure deallocate inherits from parent
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ () nothrow @nogc { a.deallocate(b); }();
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+
+ auto a = AffixAllocator!(Region!(), uint)(Region!()(new ubyte[1024 * 64]));
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ // Test that expand infers from parent
+ assert((() pure nothrow @safe @nogc => a.expand(b, 58))());
+ assert(b.length == 100);
+ // Test that deallocateAll infers from parent
+ assert((() nothrow @nogc => a.deallocateAll())());
+}
+
+// Test that reallocate infers from parent
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ alias a = AffixAllocator!(Mallocator, uint).instance;
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert((() nothrow @nogc => a.reallocate(b, 100))());
+ assert(b.length == 100);
+ assert((() nothrow @nogc => a.deallocate(b))());
+}
+
+@system unittest
+{
+ import std.experimental.allocator : processAllocator, RCISharedAllocator;
+ import std.traits;
+
+ alias SharedAllocT = shared AffixAllocator!(RCISharedAllocator, int);
+ static assert(is(RCISharedAllocator == shared));
+ static assert(!is(SharedAllocT.instance));
+
+ SharedAllocT a = SharedAllocT(processAllocator);
+ auto buf = a.allocate(10);
+ static assert(is(typeof(a.allocate) == shared));
+ assert(buf.length == 10);
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d b/libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d
new file mode 100644
index 00000000000..14c6de4696f
--- /dev/null
+++ b/libphobos/src/std/experimental/allocator/building_blocks/aligned_block_list.d
@@ -0,0 +1,699 @@
+// Written in the D programming language.
+/**
+`AlignedBlockList` represents a wrapper around a chain of allocators, allowing for fast deallocations
+and preserving a low degree of fragmentation by means of aligned allocations.
+
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/aligned_block_list.d)
+*/
+module std.experimental.allocator.building_blocks.aligned_block_list;
+
+import std.experimental.allocator.common;
+import std.experimental.allocator.building_blocks.null_allocator;
+
+// Common function implementation for thread local and shared AlignedBlockList
+private mixin template AlignedBlockListImpl(bool isShared)
+{
+ import std.traits : hasMember;
+ import std.typecons : Ternary;
+
+ static if (isShared)
+ import core.internal.spinlock : SpinLock;
+
+private:
+ // Doubly linked list of 'AlignedBlockNode'
+ // Each node contains an `Allocator` followed by its payload
+ static struct AlignedBlockNode
+ {
+ AlignedBlockNode* next, prev;
+ Allocator bAlloc;
+
+ static if (isShared)
+ {
+ shared(size_t) bytesUsed;
+ // Since the lock is not taken when allocating, this acts like a refcount
+ // keeping the node alive
+ uint keepAlive;
+ }
+ else
+ {
+ size_t bytesUsed;
+ }
+ }
+
+ // Root of the internal doubly linked list
+ AlignedBlockNode* root;
+
+ // Number of active nodes
+ uint numNodes;
+
+ // If the numNodes exceeds this limit, we will start deallocating nodes
+ enum uint maxNodes = 64;
+
+ // This lock is always taken when changing the list
+ // To improve performance, the lock is not taken when the allocation logic is called
+ static if (isShared)
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+
+ // Moves a node to the front of the list, allowing for quick allocations
+ void moveToFront(AlignedBlockNode* tmp)
+ {
+ auto localRoot = cast(AlignedBlockNode*) root;
+ if (tmp == localRoot)
+ return;
+
+ if (tmp.prev) tmp.prev.next = tmp.next;
+ if (tmp.next) tmp.next.prev = tmp.prev;
+ if (localRoot) localRoot.prev = tmp;
+ tmp.next = localRoot;
+ tmp.prev = null;
+
+ root = cast(typeof(root)) tmp;
+ }
+
+ // Removes a node from the list, including its payload
+ // The payload is deallocated by calling 'parent.deallocate'
+ void removeNode(AlignedBlockNode* tmp)
+ {
+ auto next = tmp.next;
+ if (tmp.prev) tmp.prev.next = tmp.next;
+ if (tmp.next) tmp.next.prev = tmp.prev;
+ parent.deallocate((cast(void*) tmp)[0 .. theAlignment]);
+
+ if (tmp == cast(AlignedBlockNode*) root)
+ root = cast(typeof(root)) next;
+
+ static if (isShared)
+ {
+ import core.atomic : atomicOp;
+ atomicOp!"-="(numNodes, 1);
+ }
+ else
+ {
+ numNodes--;
+ }
+ }
+
+ // If the nodes do not have available space, a new node is created
+ // by drawing memory from the parent allocator with aligned allocations.
+ // The new node is inserted at the front of the list
+ bool insertNewNode()
+ {
+ void[] buf = parent.alignedAllocate(theAlignment, theAlignment);
+ if (buf is null)
+ return false;
+
+ auto localRoot = cast(AlignedBlockNode*) root;
+ auto newNode = cast(AlignedBlockNode*) buf;
+
+ // The first part of the allocation represent the node contents
+ // followed by the actual payload
+ ubyte[] payload = cast(ubyte[]) buf[AlignedBlockNode.sizeof .. $];
+ newNode.bAlloc = Allocator(payload);
+
+ newNode.next = localRoot;
+ newNode.prev = null;
+ if (localRoot)
+ localRoot.prev = newNode;
+ root = cast(typeof(root)) newNode;
+
+ static if (isShared)
+ {
+ import core.atomic : atomicOp;
+ atomicOp!"+="(numNodes, 1);
+ }
+ else
+ {
+ numNodes++;
+ }
+
+ return true;
+ }
+
+public:
+ static if (stateSize!ParentAllocator) ParentAllocator parent;
+ else alias parent = ParentAllocator.instance;
+
+ enum ulong alignment = Allocator.alignment;
+
+ // Since all memory is drawn from ParentAllocator, we can
+ // forward this to the parent
+ static if (hasMember!(ParentAllocator, "owns"))
+ Ternary owns(void[] b)
+ {
+ return parent.owns(b);
+ }
+
+ // Use `theAlignment` to find the node which allocated this block
+ bool deallocate(void[] b)
+ {
+ if (b is null)
+ return true;
+
+ // Round buffer to nearest `theAlignment` multiple to quickly find
+ // the `parent` `AlignedBlockNode`
+ enum ulong mask = ~(theAlignment - 1);
+ ulong ptr = ((cast(ulong) b.ptr) & mask);
+ AlignedBlockNode *node = cast(AlignedBlockNode*) ptr;
+ if (node.bAlloc.deallocate(b))
+ {
+ static if (isShared)
+ {
+ import core.atomic : atomicOp;
+ atomicOp!"-="(node.bytesUsed, b.length);
+ }
+ else
+ {
+ node.bytesUsed -= b.length;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // Allocate works only if memory can be provided via `alignedAllocate` from the parent
+ static if (hasMember!(ParentAllocator, "alignedAllocate"))
+ void[] allocate(size_t n)
+ {
+ static if (isShared)
+ import core.atomic : atomicOp, atomicLoad;
+
+ if (n == 0 || n > theAlignment)
+ return null;
+
+ static if (isShared)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+ }
+
+ auto tmp = cast(AlignedBlockNode*) root;
+
+ // Iterate through list and find first node which has memory available
+ while (tmp)
+ {
+ auto next = tmp.next;
+ static if (isShared)
+ {
+ // Allocations can happen outside the lock
+ // Make sure nobody deletes this node while using it
+ tmp.keepAlive++;
+ if (next) next.keepAlive++;
+ lock.unlock();
+ }
+
+ auto result = tmp.bAlloc.allocate(n);
+ if (result.length == n)
+ {
+ // Success
+ static if (isShared)
+ {
+ atomicOp!"+="(tmp.bytesUsed, n);
+ lock.lock();
+ }
+ else
+ {
+ tmp.bytesUsed += n;
+ }
+
+ // Most likely this node has memory for more allocations
+ // Move it to the front
+ moveToFront(tmp);
+
+ static if (isShared)
+ {
+ tmp.keepAlive--;
+ if (next) next.keepAlive--;
+ }
+
+ return result;
+ }
+
+ // This node can now be removed if necessary
+ static if (isShared)
+ {
+ lock.lock();
+ tmp.keepAlive--;
+ if (next) next.keepAlive--;
+ }
+
+ if (!next)
+ break;
+
+ tmp = next;
+ next = tmp.next;
+
+ // If there are too many nodes, free memory by removing empty nodes
+ static if (isShared)
+ {
+ if (atomicLoad(numNodes) > maxNodes &&
+ atomicLoad(tmp.bytesUsed) == 0 &&
+ tmp.keepAlive == 0)
+ {
+ removeNode(tmp);
+ }
+ }
+ else
+ {
+ if (numNodes > maxNodes && tmp.bytesUsed == 0)
+ {
+ removeNode(tmp);
+ }
+ }
+
+ tmp = next;
+ }
+
+ // Cannot create new AlignedBlockNode. Most likely the ParentAllocator ran out of resources
+ if (!insertNewNode())
+ return null;
+
+ tmp = cast(typeof(tmp)) root;
+ void[] result = tmp.bAlloc.allocate(n);
+
+ static if (isShared)
+ {
+ atomicOp!"+="(root.bytesUsed, result.length);
+ }
+ else
+ {
+ root.bytesUsed += result.length;
+ }
+
+ return result;
+ }
+
+ // goodAllocSize should not use state
+ size_t goodAllocSize(const size_t n)
+ {
+ Allocator a = null;
+ return a.goodAllocSize(n);
+ }
+}
+
+/**
+`AlignedBlockList` represents a wrapper around a chain of allocators, allowing for fast deallocations
+and preserving a low degree of fragmentation.
+The allocator holds internally a doubly linked list of `Allocator` objects, which will serve allocations
+in a most-recently-used fashion. Most recent allocators used for `allocate` calls, will be
+moved to the front of the list.
+
+Although allocations are in theory served in linear searching time, `deallocate` calls take
+$(BIGOH 1) time, by using aligned allocations. `ParentAllocator` must implement `alignedAllocate`
+and it must be able to allocate `theAlignment` bytes at the same alignment. Each aligned allocation
+done by `ParentAllocator` will contain metadata for an `Allocator`, followed by its payload.
+
+Params:
+ Allocator = the allocator which is used to manage each node; it must have a constructor which receives
+ `ubyte[]` and it must not have any parent allocators, except for the `NullAllocator`
+ ParentAllocator = each node draws memory from the parent allocator; it must support `alignedAllocate`
+ theAlignment = alignment of each block and at the same time length of each node
+*/
+struct AlignedBlockList(Allocator, ParentAllocator, ulong theAlignment = (1 << 21))
+{
+ version (StdDdoc)
+ {
+ import std.typecons : Ternary;
+ import std.traits : hasMember;
+
+ /**
+ Returns a chunk of memory of size `n`
+ It finds the first node in the `AlignedBlockNode` list which has available memory,
+ and moves it to the front of the list.
+
+ All empty nodes which cannot return new memory, are removed from the list.
+
+ Params:
+ n = bytes to allocate
+ Returns:
+ A chunk of memory of the required length or `null` on failure or
+ */
+ static if (hasMember!(ParentAllocator, "alignedAllocate"))
+ void[] allocate(size_t n);
+
+ /**
+ Deallocates the buffer `b` given as parameter. Deallocations take place in constant
+ time, regardless of the number of nodes in the list. `b.ptr` is rounded down
+ to the nearest multiple of the `alignment` to quickly find the corresponding
+ `AlignedBlockNode`.
+
+ Params:
+ b = buffer candidate for deallocation
+ Returns:
+ `true` on success and `false` on failure
+ */
+ bool deallocate(void[] b);
+
+ /**
+ Returns `Ternary.yes` if the buffer belongs to the parent allocator and
+ `Ternary.no` otherwise.
+
+ Params:
+ b = buffer tested if owned by this allocator
+ Returns:
+ `Ternary.yes` if owned by this allocator and `Ternary.no` otherwise
+ */
+ static if (hasMember!(ParentAllocator, "owns"))
+ Ternary owns(void[] b);
+ }
+ else
+ {
+ import std.math.traits : isPowerOf2;
+ static assert(isPowerOf2(alignment));
+ mixin AlignedBlockListImpl!false;
+ }
+}
+
+///
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.experimental.allocator.building_blocks.segregator : Segregator;
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+ import std.typecons : Ternary;
+
+ /*
+ In this example we use 'AlignedBlockList' in conjunction with other allocators
+ in order to create a more complex allocator.
+
+ The 'SuperAllocator' uses a 'Segregator' to distribute allocations to sub-allocators,
+ based on the requested size.
+
+ Each sub-allocator is represented by an 'AlignedBlockList' of 'BitmappedBlocks'.
+ Each 'AlignedBlockList' draws memory from a root allocator which in this case is an 'AscendingPageAllocator'
+
+ Such an allocator not only provides good performance, but also a low degree of memory fragmentation.
+ */
+ alias SuperAllocator = Segregator!(
+ 32,
+ AlignedBlockList!(BitmappedBlock!32, AscendingPageAllocator*, 1 << 12),
+ Segregator!(
+
+ 64,
+ AlignedBlockList!(BitmappedBlock!64, AscendingPageAllocator*, 1 << 12),
+ Segregator!(
+
+ 128,
+ AlignedBlockList!(BitmappedBlock!128, AscendingPageAllocator*, 1 << 12),
+ AscendingPageAllocator*
+ )));
+
+ SuperAllocator a;
+ auto pageAlloc = AscendingPageAllocator(128 * 4096);
+
+ // Set the parent allocator for all the sub allocators
+ a.allocatorForSize!256 = &pageAlloc;
+ a.allocatorForSize!128.parent = &pageAlloc;
+ a.allocatorForSize!64.parent = &pageAlloc;
+ a.allocatorForSize!32.parent = &pageAlloc;
+
+ enum testNum = 10;
+ void[][testNum] buf;
+
+ // Allocations of size 32 will go to the first 'AlignedBlockList'
+ foreach (j; 0 .. testNum)
+ {
+ buf[j] = a.allocate(32);
+ assert(buf[j].length == 32);
+
+ // This is owned by the first 'AlignedBlockList'
+ assert(a.allocatorForSize!32.owns(buf[j]) == Ternary.yes);
+ }
+
+ // Free the memory
+ foreach (j; 0 .. testNum)
+ assert(a.deallocate(buf[j]));
+
+ // Allocations of size 64 will go to the second 'AlignedBlockList'
+ foreach (j; 0 .. testNum)
+ {
+ buf[j] = a.allocate(64);
+ assert(buf[j].length == 64);
+
+ // This is owned by the second 'AlignedBlockList'
+ assert(a.allocatorForSize!64.owns(buf[j]) == Ternary.yes);
+ }
+
+ // Free the memory
+ foreach (j; 0 .. testNum)
+ assert(a.deallocate(buf[j]));
+
+ // Allocations of size 128 will go to the third 'AlignedBlockList'
+ foreach (j; 0 .. testNum)
+ {
+ buf[j] = a.allocate(128);
+ assert(buf[j].length == 128);
+
+ // This is owned by the third 'AlignedBlockList'
+ assert(a.allocatorForSize!128.owns(buf[j]) == Ternary.yes);
+ }
+
+ // Free the memory
+ foreach (j; 0 .. testNum)
+ assert(a.deallocate(buf[j]));
+
+ // Allocations which exceed 128, will go to the 'AscendingPageAllocator*'
+ void[] b = a.allocate(256);
+ assert(b.length == 256);
+ a.deallocate(b);
+}
+
+/**
+`SharedAlignedBlockList` is the threadsafe version of `AlignedBlockList`.
+The `Allocator` template parameter must refer a shared allocator.
+Also, `ParentAllocator` must be a shared allocator, supporting `alignedAllocate`.
+
+Params:
+ Allocator = the shared allocator which is used to manage each node; it must have a constructor which receives
+ `ubyte[]` and it must not have any parent allocators, except for the `NullAllocator`
+ ParentAllocator = each node draws memory from the parent allocator; it must be shared and support `alignedAllocate`
+ theAlignment = alignment of each block and at the same time length of each node
+*/
+shared struct SharedAlignedBlockList(Allocator, ParentAllocator, ulong theAlignment = (1 << 21))
+{
+ version (StdDdoc)
+ {
+ import std.typecons : Ternary;
+ import std.traits : hasMember;
+
+ /**
+ Returns a chunk of memory of size `n`
+ It finds the first node in the `AlignedBlockNode` list which has available memory,
+ and moves it to the front of the list.
+
+ All empty nodes which cannot return new memory, are removed from the list.
+
+ Params:
+ n = bytes to allocate
+ Returns:
+ A chunk of memory of the required length or `null` on failure or
+ */
+ static if (hasMember!(ParentAllocator, "alignedAllocate"))
+ void[] allocate(size_t n);
+
+ /**
+ Deallocates the buffer `b` given as parameter. Deallocations take place in constant
+ time, regardless of the number of nodes in the list. `b.ptr` is rounded down
+ to the nearest multiple of the `alignment` to quickly find the corresponding
+ `AlignedBlockNode`.
+
+ Params:
+ b = buffer candidate for deallocation
+ Returns:
+ `true` on success and `false` on failure
+ */
+ bool deallocate(void[] b);
+
+ /**
+ Returns `Ternary.yes` if the buffer belongs to the parent allocator and
+ `Ternary.no` otherwise.
+
+ Params:
+ b = buffer tested if owned by this allocator
+ Returns:
+ `Ternary.yes` if owned by this allocator and `Ternary.no` otherwise
+ */
+ static if (hasMember!(ParentAllocator, "owns"))
+ Ternary owns(void[] b);
+ }
+ else
+ {
+ import std.math.traits : isPowerOf2;
+ static assert(isPowerOf2(alignment));
+ mixin AlignedBlockListImpl!true;
+ }
+}
+
+///
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : SharedRegion;
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : SharedAscendingPageAllocator;
+ import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
+ import core.thread : ThreadGroup;
+
+ enum numThreads = 8;
+ enum size = 2048;
+ enum maxIter = 10;
+
+ /*
+ In this example we use 'SharedAlignedBlockList' together with 'SharedRegion',
+ in order to create a fast, thread-safe allocator.
+ */
+ alias SuperAllocator = SharedAlignedBlockList!(
+ SharedRegion!(NullAllocator, 1),
+ SharedAscendingPageAllocator,
+ 4096);
+
+ SuperAllocator a;
+ // The 'SuperAllocator' will draw memory from a 'SharedAscendingPageAllocator'
+ a.parent = SharedAscendingPageAllocator(4096 * 1024);
+
+ // Launch 'numThreads', each performing allocations
+ void fun()
+ {
+ foreach (i; 0 .. maxIter)
+ {
+ void[] b = a.allocate(size);
+ assert(b.length == size);
+ }
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+}
+
+version (StdUnittest)
+{
+ static void testrw(void[] b)
+ {
+ ubyte* buf = cast(ubyte*) b.ptr;
+ size_t len = (b.length).roundUpToMultipleOf(4096);
+ for (int i = 0; i < len; i += 4096)
+ {
+ buf[i] = (cast(ubyte) i % 256);
+ assert(buf[i] == (cast(ubyte) i % 256));
+ }
+ }
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region;
+ import std.experimental.allocator.building_blocks.ascending_page_allocator;
+ import std.random;
+ import std.algorithm.sorting : sort;
+ import core.thread : ThreadGroup;
+ import core.internal.spinlock : SpinLock;
+
+ enum pageSize = 4096;
+ enum numThreads = 10;
+ enum maxIter = 20;
+ enum totalAllocs = maxIter * numThreads;
+ size_t count = 0;
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+
+ alias SuperAllocator = SharedAlignedBlockList!(
+ SharedRegion!(NullAllocator, 1),
+ SharedAscendingPageAllocator,
+ 1 << 16);
+ void[][totalAllocs] buf;
+
+ SuperAllocator a;
+ a.parent = SharedAscendingPageAllocator(4096 * 1024);
+
+ void fun()
+ {
+ auto rnd = Random(1000);
+
+ foreach (i; 0 .. maxIter)
+ {
+ auto size = uniform(1, pageSize + 1, rnd);
+ void[] b = a.allocate(size);
+ assert(b.length == size);
+ testrw(b);
+
+ lock.lock();
+ buf[count++] = b;
+ lock.unlock();
+ }
+ }
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ sort!((a, b) => a.ptr < b.ptr)(buf[0 .. totalAllocs]);
+ foreach (i; 0 .. totalAllocs - 1)
+ {
+ assert(buf[i].ptr + a.goodAllocSize(buf[i].length) <= buf[i + 1].ptr);
+ }
+
+ foreach (i; 0 .. totalAllocs)
+ {
+ assert(a.deallocate(buf[totalAllocs - 1 - i]));
+ }
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.experimental.allocator.building_blocks.segregator : Segregator;
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+ import std.random;
+
+ alias SuperAllocator = Segregator!(
+ 256,
+ AlignedBlockList!(BitmappedBlock!256, AscendingPageAllocator*, 1 << 16),
+ Segregator!(
+
+ 512,
+ AlignedBlockList!(BitmappedBlock!512, AscendingPageAllocator*, 1 << 16),
+ Segregator!(
+
+ 1024,
+ AlignedBlockList!(BitmappedBlock!1024, AscendingPageAllocator*, 1 << 16),
+ Segregator!(
+
+ 2048,
+ AlignedBlockList!(BitmappedBlock!2048, AscendingPageAllocator*, 1 << 16),
+ AscendingPageAllocator*
+ ))));
+
+ SuperAllocator a;
+ auto pageAlloc = AscendingPageAllocator(4096 * 4096);
+ a.allocatorForSize!4096 = &pageAlloc;
+ a.allocatorForSize!2048.parent = &pageAlloc;
+ a.allocatorForSize!1024.parent = &pageAlloc;
+ a.allocatorForSize!512.parent = &pageAlloc;
+ a.allocatorForSize!256.parent = &pageAlloc;
+
+ auto rnd = Random(1000);
+
+ size_t maxIter = 10;
+ enum testNum = 10;
+ void[][testNum] buf;
+ int maxSize = 8192;
+ foreach (i; 0 .. maxIter)
+ {
+ foreach (j; 0 .. testNum)
+ {
+ auto size = uniform(1, maxSize + 1, rnd);
+ buf[j] = a.allocate(size);
+ assert(buf[j].length == size);
+ testrw(buf[j]);
+ }
+
+ randomShuffle(buf[]);
+
+ foreach (j; 0 .. testNum)
+ {
+ assert(a.deallocate(buf[j]));
+ }
+ }
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
index 2d0e6708002..bcab16d6d8f 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
@@ -1,10 +1,14 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/allocator_list.d)
+*/
module std.experimental.allocator.building_blocks.allocator_list;
+import core.memory : pageSize;
+
import std.experimental.allocator.building_blocks.null_allocator;
import std.experimental.allocator.common;
import std.experimental.allocator.gc_allocator;
-version (unittest) import std.stdio;
// Turn this on for debugging
// debug = allocator_list;
@@ -21,7 +25,7 @@ An embedded list builds a most-recently-used strategy: the most recent
allocators used in calls to either `allocate`, `owns` (successful calls
only), or `deallocate` are tried for new allocations in order of their most
recent use. Thus, although core operations take in theory $(BIGOH k) time for
-$(D k) allocators in current use, in many workloads the factor is sublinear.
+`k` allocators in current use, in many workloads the factor is sublinear.
Details of the actual strategy may change in future releases.
`AllocatorList` is primarily intended for coarse-grained handling of
@@ -45,18 +49,18 @@ needs state, a `Factory` object should be used.
BookkeepingAllocator = Allocator used for storing bookkeeping data. The size of
bookkeeping data is proportional to the number of allocators. If $(D
-BookkeepingAllocator) is $(D NullAllocator), then $(D AllocatorList) is
+BookkeepingAllocator) is `NullAllocator`, then `AllocatorList` is
"ouroboros-style", i.e. it keeps the bookkeeping data in memory obtained from
the allocators themselves. Note that for ouroboros-style management, the size
-$(D n) passed to $(D make) will be occasionally different from the size
+`n` passed to `make` will be occasionally different from the size
requested by client code.
Factory = Type of a factory object that returns new allocators on a need
-basis. For an object $(D sweatshop) of type $(D Factory), `sweatshop(n)` should
+basis. For an object `sweatshop` of type `Factory`, `sweatshop(n)` should
return an allocator able to allocate at least `n` bytes (i.e. `Factory` must
define `opCall(size_t)` to return an allocator object). Usually the capacity of
-allocators created should be much larger than $(D n) such that an allocator can
-be used for many subsequent allocations. $(D n) is passed only to ensure the
+allocators created should be much larger than `n` such that an allocator can
+be used for many subsequent allocations. `n` is passed only to ensure the
minimum necessary for the next allocation. The factory object is allowed to hold
state, which will be stored inside `AllocatorList` as a direct `public` member
called `factory`.
@@ -64,7 +68,7 @@ called `factory`.
*/
struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
import std.experimental.allocator.building_blocks.stats_collector
: StatsCollector, Options;
import std.traits : hasMember;
@@ -97,7 +101,7 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
}
/**
- If $(D BookkeepingAllocator) is not $(D NullAllocator), $(D bkalloc) is
+ If `BookkeepingAllocator` is not `NullAllocator`, `bkalloc` is
defined and accessible.
*/
@@ -155,11 +159,11 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
enum uint alignment = Allocator.alignment;
/**
- Allocate a block of size $(D s). First tries to allocate from the existing
+ Allocate a block of size `s`. First tries to allocate from the existing
list of already-created allocators. If neither can satisfy the request,
- creates a new allocator by calling $(D make(s)) and delegates the request
+ creates a new allocator by calling `make(s)` and delegates the request
to it. However, if the allocation fresh off a newly created allocator
- fails, subsequent calls to $(D allocate) will not cause more calls to $(D
+ fails, subsequent calls to `allocate` will not cause more calls to $(D
make).
*/
void[] allocate(size_t s)
@@ -177,17 +181,85 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
}
return result;
}
- // Can't allocate from the current pool. Check if we just added a new
- // allocator, in that case it won't do any good to add yet another.
- if (root && root.empty == Ternary.yes)
+
+ // Add a new allocator
+ if (auto a = addAllocator(s))
{
- // no can do
- return null;
+ auto result = a.allocate(s);
+ assert(owns(result) == Ternary.yes || !result.ptr);
+ return result;
}
+ return null;
+ }
+
+ static if (hasMember!(Allocator, "allocateZeroed"))
+ package(std) void[] allocateZeroed()(size_t s)
+ {
+ for (auto p = &root, n = *p; n; p = &n.next, n = *p)
+ {
+ auto result = n.allocateZeroed(s);
+ if (result.length != s) continue;
+ // Bring to front if not already
+ if (root != n)
+ {
+ *p = n.next;
+ n.next = root;
+ root = n;
+ }
+ return result;
+ }
+
// Add a new allocator
if (auto a = addAllocator(s))
{
- auto result = a.allocate(s);
+ auto result = a.allocateZeroed(s);
+ assert(owns(result) == Ternary.yes || !result.ptr);
+ return result;
+ }
+ return null;
+ }
+
+ /**
+ Allocate a block of size `s` with alignment `a`. First tries to allocate
+ from the existing list of already-created allocators. If neither can
+ satisfy the request, creates a new allocator by calling `make(s + a - 1)`
+ and delegates the request to it. However, if the allocation fresh off a
+ newly created allocator fails, subsequent calls to `alignedAllocate`
+ will not cause more calls to `make`.
+ */
+ static if (hasMember!(Allocator, "alignedAllocate"))
+ void[] alignedAllocate(size_t s, uint theAlignment)
+ {
+ import std.algorithm.comparison : max;
+ import core.checkedint : addu;
+
+ if (theAlignment == 0 || s == 0)
+ return null;
+
+ for (auto p = &root, n = *p; n; p = &n.next, n = *p)
+ {
+ auto result = n.alignedAllocate(s, theAlignment);
+ if (result.length != s) continue;
+ // Bring to front if not already
+ if (root != n)
+ {
+ *p = n.next;
+ n.next = root;
+ root = n;
+ }
+ return result;
+ }
+
+ bool overflow = false;
+ size_t maxSize = addu(s - 1, cast(size_t) theAlignment, overflow);
+ assert(!overflow, "Requested size is too large");
+ if (overflow)
+ return null;
+
+ // Add a new allocator
+ if (auto a = addAllocator(maxSize))
+ {
+ auto result = a.alignedAllocate(s, theAlignment);
assert(owns(result) == Ternary.yes || !result.ptr);
return result;
}
@@ -373,15 +445,15 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
}
/**
- Defined only if $(D Allocator.expand) is defined. Finds the owner of $(D b)
- and calls $(D expand) for it. The owner is not brought to the head of the
+ Defined only if `Allocator.expand` is defined. Finds the owner of `b`
+ and calls `expand` for it. The owner is not brought to the head of the
list.
*/
static if (hasMember!(Allocator, "expand")
&& hasMember!(Allocator, "owns"))
bool expand(ref void[] b, size_t delta)
{
- if (!b.ptr) return delta == 0;
+ if (!b) return delta == 0;
for (auto p = &root, n = *p; n; p = &n.next, n = *p)
{
if (n.owns(b) == Ternary.yes) return n.expand(b, delta);
@@ -390,9 +462,9 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
}
/**
- Defined only if $(D Allocator.reallocate) is defined. Finds the owner of
- $(D b) and calls $(D reallocate) for it. If that fails, calls the global
- $(D reallocate), which allocates a new block and moves memory.
+ Defined only if `Allocator.reallocate` is defined. Finds the owner of
+ `b` and calls `reallocate` for it. If that fails, calls the global
+ `reallocate`, which allocates a new block and moves memory.
*/
static if (hasMember!(Allocator, "reallocate"))
bool reallocate(ref void[] b, size_t s)
@@ -412,7 +484,7 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
}
/**
- Defined if $(D Allocator.deallocate) and $(D Allocator.owns) are defined.
+ Defined if `Allocator.deallocate` and `Allocator.owns` are defined.
*/
static if (hasMember!(Allocator, "deallocate")
&& hasMember!(Allocator, "owns"))
@@ -454,7 +526,7 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
}
/**
- Defined only if $(D Allocator.owns) and $(D Allocator.deallocateAll) are
+ Defined only if `Allocator.owns` and `Allocator.deallocateAll` are
defined.
*/
static if (ouroboros && hasMember!(Allocator, "deallocateAll")
@@ -476,7 +548,19 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
assert(special || !allocators.ptr);
if (special)
{
- special.deallocate(allocators);
+ static if (stateSize!SAllocator)
+ {
+ import core.stdc.string : memcpy;
+ SAllocator specialCopy;
+ assert(special.a.sizeof == specialCopy.sizeof);
+ memcpy(&specialCopy, &special.a, specialCopy.sizeof);
+ emplace(&special.a);
+ specialCopy.deallocateAll();
+ }
+ else
+ {
+ special.deallocateAll();
+ }
}
allocators = null;
root = null;
@@ -503,6 +587,7 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
Returns `Ternary.yes` if no allocators are currently active,
`Ternary.no` otherwise. This methods never returns `Ternary.unknown`.
*/
+ pure nothrow @safe @nogc
Ternary empty() const
{
return Ternary(!allocators.length);
@@ -596,26 +681,119 @@ version (Posix) @system unittest
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.region : Region;
AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
- auto b1 = a.allocate(1024 * 8192);
+ auto b1 = a.alignedAllocate(1024 * 8192, 1024);
assert(b1 !is null); // still works due to overdimensioning
+ assert(b1.length == 1024 * 8192);
+ assert(b1.ptr.alignedAt(1024));
+ assert(a.allocators.length == 1);
+
+ b1 = a.alignedAllocate(0, 1024);
+ assert(b1.length == 0);
+ assert(a.allocators.length == 1);
+
b1 = a.allocate(1024 * 10);
assert(b1.length == 1024 * 10);
+
+ assert(a.reallocate(b1, 1024));
+ assert(b1.length == 1024);
+
a.deallocateAll();
}
@system unittest
{
+ import core.exception : AssertError;
+ import std.exception : assertThrown;
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.region : Region;
+ AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+ auto b1 = a.alignedAllocate(0, 1);
+ assert(b1 is null);
+
+ b1 = a.alignedAllocate(1, 0);
+ assert(b1 is null);
+
+ b1 = a.alignedAllocate(0, 0);
+ assert(b1 is null);
+
+ assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024));
+ a.deallocateAll();
+}
+
+@system unittest
+{
import std.typecons : Ternary;
+
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.region : Region;
+ AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
+ auto b0 = a.alignedAllocate(1, 1024);
+ assert(b0.length == 1);
+ assert(b0.ptr.alignedAt(1024));
+ assert(a.allocators.length == 1);
+
+ auto b1 = a.alignedAllocate(1024 * 4096, 1024);
+ assert(b1.length == 1024 * 4096);
+ assert(b1.ptr.alignedAt(1024));
+ assert(a.allocators.length == 2);
+
+ auto b2 = a.alignedAllocate(1024, 128);
+ assert(b2.length == 1024);
+ assert(b2.ptr.alignedAt(128));
+ assert(a.allocators.length == 2);
+
+ auto b3 = a.allocate(1024);
+ assert(b3.length == 1024);
+ assert(a.allocators.length == 2);
+
+ auto b4 = a.allocate(1024 * 4096);
+ assert(b4.length == 1024 * 4096);
+ assert(a.allocators.length == 3);
+
+ assert(a.root.empty == Ternary.no);
+ assert(a.deallocate(b4));
+ assert(a.root.empty == Ternary.yes);
+
+ assert(a.deallocate(b1));
+ a.deallocateAll();
+}
+
+@system unittest
+{
+ // Create an allocator based upon 4MB regions, fetched from the GC heap.
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.region : Region;
AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a;
auto b1 = a.allocate(1024 * 8192);
+ assert(b1 !is null); // still works due to overdimensioning
+ b1 = a.allocate(1024 * 10);
+ assert(b1.length == 1024 * 10);
+ assert(a.reallocate(b1, 1024));
+ assert(b1.length == 1024);
+ a.deallocateAll();
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+ AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)]), Mallocator) a;
+ auto b1 = a.allocate(1024 * 8192);
assert(b1 !is null);
b1 = a.allocate(1024 * 10);
assert(b1.length == 1024 * 10);
+ assert((() pure nothrow @safe @nogc => a.expand(b1, 10))());
+ assert(b1.length == 1025 * 10);
a.allocate(1024 * 4095);
- a.deallocateAll();
- assert(a.empty == Ternary.yes);
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no);
+ // Ensure deallocateAll infers from parent
+ assert((() nothrow @nogc => a.deallocateAll())());
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
}
@system unittest
@@ -632,9 +810,191 @@ version (Posix) @system unittest
auto b3 = a.allocate(192 * bs);
assert(b3.length == 192 * bs);
assert(a.allocators.length == 2);
- a.deallocate(b1);
+ // Ensure deallocate inherits from parent allocators
+ () nothrow @nogc { a.deallocate(b1); }();
b1 = a.allocate(64 * bs);
assert(b1.length == 64 * bs);
assert(a.allocators.length == 2);
a.deallocateAll();
}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.algorithm.comparison : max;
+ import std.typecons : Ternary;
+
+ static void testrw(void[] b)
+ {
+ ubyte* buf = cast(ubyte*) b.ptr;
+ for (int i = 0; i < b.length; i += pageSize)
+ {
+ buf[i] = cast(ubyte) (i % 256);
+ assert(buf[i] == cast(ubyte) (i % 256));
+ }
+ }
+
+ enum numPages = 2;
+ AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), Mallocator) a;
+
+ void[] b1 = a.allocate(1);
+ assert(b1.length == 1);
+ b1 = a.allocate(2);
+ assert(b1.length == 2);
+ testrw(b1);
+ assert(a.root.a.parent.getAvailableSize() == 0);
+
+ void[] b2 = a.allocate((numPages + 1) * pageSize);
+ assert(b2.length == (numPages + 1) * pageSize);
+ testrw(b2);
+
+ void[] b3 = a.allocate(3);
+ assert(b3.length == 3);
+ testrw(b3);
+
+ void[] b4 = a.allocate(0);
+ assert(b4.length == 0);
+
+ assert(a.allocators.length == 3);
+ assert(a.owns(b1) == Ternary.yes);
+ assert(a.owns(b2) == Ternary.yes);
+ assert(a.owns(b3) == Ternary.yes);
+
+ assert(a.expand(b1, pageSize - b1.length));
+ assert(b1.length == pageSize);
+ assert(!a.expand(b1, 1));
+ assert(!a.expand(b2, 1));
+
+ testrw(b1);
+ testrw(b2);
+ testrw(b3);
+
+ assert(a.deallocate(b1));
+ assert(a.deallocate(b2));
+
+ assert(a.deallocateAll());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.algorithm.comparison : max;
+ import std.typecons : Ternary;
+
+ static void testrw(void[] b)
+ {
+ ubyte* buf = cast(ubyte*) b.ptr;
+ for (int i = 0; i < b.length; i += pageSize)
+ {
+ buf[i] = cast(ubyte) (i % 256);
+ assert(buf[i] == cast(ubyte) (i % 256));
+ }
+ }
+
+ enum numPages = 2;
+ AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a;
+
+ void[] b1 = a.allocate(1);
+ assert(b1.length == 1);
+ b1 = a.allocate(2);
+ assert(b1.length == 2);
+ testrw(b1);
+
+ void[] b2 = a.allocate((numPages + 1) * pageSize);
+ assert(b2.length == (numPages + 1) * pageSize);
+ testrw(b2);
+
+ void[] b3 = a.allocate(3);
+ assert(b3.length == 3);
+ testrw(b3);
+
+ void[] b4 = a.allocate(0);
+ assert(b4.length == 0);
+
+ assert(a.allocators.length == 3);
+ assert(a.owns(b1) == Ternary.yes);
+ assert(a.owns(b2) == Ternary.yes);
+ assert(a.owns(b3) == Ternary.yes);
+
+ assert(a.expand(b1, pageSize - b1.length));
+ assert(b1.length == pageSize);
+ assert(!a.expand(b1, 1));
+ assert(!a.expand(b2, 1));
+
+ testrw(b1);
+ testrw(b2);
+ testrw(b3);
+
+ assert(a.deallocate(b1));
+ assert(a.deallocate(b2));
+
+ const alignment = cast(uint) (70 * pageSize);
+ b3 = a.alignedAllocate(70 * pageSize, alignment);
+ assert(b3.length == 70 * pageSize);
+ assert(b3.ptr.alignedAt(alignment));
+ testrw(b3);
+ assert(a.allocators.length == 4);
+ assert(a.deallocate(b3));
+
+
+ assert(a.deallocateAll());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.algorithm.comparison : max;
+ import std.typecons : Ternary;
+
+ static void testrw(void[] b)
+ {
+ ubyte* buf = cast(ubyte*) b.ptr;
+ for (int i = 0; i < b.length; i += pageSize)
+ {
+ buf[i] = cast(ubyte) (i % 256);
+ assert(buf[i] == cast(ubyte) (i % 256));
+ }
+ }
+
+ enum numPages = 5;
+ AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a;
+ const alignment = cast(uint) (2 * pageSize);
+ auto b = a.alignedAllocate(1, alignment);
+ assert(b.length == 1);
+ assert(a.expand(b, pageSize - 1));
+ assert(b.ptr.alignedAt(alignment));
+ assert(b.length == pageSize);
+
+ b = a.allocate(pageSize);
+ assert(b.length == pageSize);
+ assert(a.allocators.length == 1);
+
+ assert(a.allocate(pageSize * 5).length == pageSize * 5);
+ assert(a.allocators.length == 2);
+
+ assert(a.deallocateAll());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+ import std.algorithm.comparison : max;
+
+ enum maxIter = 100;
+ enum numPages = 10;
+ const chunkSize = pageSize / 8;
+
+ AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a;
+ foreach (i; 0 .. maxIter)
+ {
+ auto b1 = a.allocate(chunkSize);
+ assert(b1.length == chunkSize);
+
+ assert(a.deallocate(b1));
+ }
+
+ assert(a.deallocateAll());
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/ascending_page_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/ascending_page_allocator.d
new file mode 100644
index 00000000000..bfb78c587e4
--- /dev/null
+++ b/libphobos/src/std/experimental/allocator/building_blocks/ascending_page_allocator.d
@@ -0,0 +1,1007 @@
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/ascending_page_allocator.d)
+*/
+module std.experimental.allocator.building_blocks.ascending_page_allocator;
+
+import core.memory : pageSize;
+
+import std.experimental.allocator.common;
+
+// Common implementations for shared and thread local AscendingPageAllocator
+private mixin template AscendingPageAllocatorImpl(bool isShared)
+{
+ bool deallocate(void[] buf) nothrow @nogc
+ {
+ size_t goodSize = goodAllocSize(buf.length);
+ version (Posix)
+ {
+ import core.sys.posix.sys.mman : mmap, MAP_FAILED, MAP_PRIVATE,
+ MAP_ANON, MAP_FIXED, PROT_NONE, munmap;
+
+ auto ptr = mmap(buf.ptr, goodSize, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
+ if (ptr == MAP_FAILED)
+ return false;
+ }
+ else version (Windows)
+ {
+ import core.sys.windows.winbase : VirtualFree;
+ import core.sys.windows.winnt : MEM_DECOMMIT;
+
+ auto ret = VirtualFree(buf.ptr, goodSize, MEM_DECOMMIT);
+ if (ret == 0)
+ return false;
+ }
+ else
+ {
+ static assert(0, "Unsupported OS");
+ }
+
+ static if (!isShared)
+ {
+ pagesUsed -= goodSize / pageSize;
+ }
+
+ return true;
+ }
+
+ Ternary owns(void[] buf) nothrow @nogc
+ {
+ if (!data)
+ return Ternary.no;
+ return Ternary(buf.ptr >= data && buf.ptr < buf.ptr + numPages * pageSize);
+ }
+
+ bool deallocateAll() nothrow @nogc
+ {
+ version (Posix)
+ {
+ import core.sys.posix.sys.mman : munmap;
+ auto ret = munmap(cast(void*) data, numPages * pageSize);
+ if (ret != 0)
+ assert(0, "Failed to unmap memory, munmap failure");
+ }
+ else version (Windows)
+ {
+ import core.sys.windows.winbase : VirtualFree;
+ import core.sys.windows.winnt : MEM_RELEASE;
+ auto ret = VirtualFree(cast(void*) data, 0, MEM_RELEASE);
+ if (ret == 0)
+ assert(0, "Failed to unmap memory, VirtualFree failure");
+ }
+ else
+ {
+ static assert(0, "Unsupported OS version");
+ }
+ data = null;
+ offset = null;
+ return true;
+ }
+
+ size_t goodAllocSize(size_t n) nothrow @nogc
+ {
+ return n.roundUpToMultipleOf(cast(uint) pageSize);
+ }
+
+ this(size_t n) nothrow @nogc
+ {
+ static if (isShared)
+ {
+ lock = SpinLock(SpinLock.Contention.brief);
+ }
+
+ pageSize = .pageSize;
+ numPages = n.roundUpToMultipleOf(cast(uint) pageSize) / pageSize;
+
+ version (Posix)
+ {
+ import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_NONE,
+ MAP_PRIVATE, MAP_FAILED;
+
+ data = cast(typeof(data)) mmap(null, pageSize * numPages,
+ PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (data == MAP_FAILED)
+ assert(0, "Failed to mmap memory");
+ }
+ else version (Windows)
+ {
+ import core.sys.windows.winbase : VirtualAlloc;
+ import core.sys.windows.winnt : MEM_RESERVE, PAGE_NOACCESS;
+
+ data = cast(typeof(data)) VirtualAlloc(null, pageSize * numPages,
+ MEM_RESERVE, PAGE_NOACCESS);
+ if (!data)
+ assert(0, "Failed to VirtualAlloc memory");
+ }
+ else
+ {
+ static assert(0, "Unsupported OS version");
+ }
+
+ offset = data;
+ readWriteLimit = data;
+ }
+
+ size_t getAvailableSize() nothrow @nogc
+ {
+ static if (isShared)
+ {
+ lock.lock();
+ }
+
+ auto size = numPages * pageSize + data - offset;
+ static if (isShared)
+ {
+ lock.unlock();
+ }
+ return size;
+ }
+
+ // Sets the protection of a memory range to read/write
+ private bool extendMemoryProtection(void* start, size_t size) nothrow @nogc
+ {
+ version (Posix)
+ {
+ import core.sys.posix.sys.mman : mprotect, PROT_WRITE, PROT_READ;
+
+ auto ret = mprotect(start, size, PROT_WRITE | PROT_READ);
+ return ret == 0;
+ }
+ else version (Windows)
+ {
+ import core.sys.windows.winbase : VirtualAlloc;
+ import core.sys.windows.winnt : MEM_COMMIT, PAGE_READWRITE;
+
+ auto ret = VirtualAlloc(start, size, MEM_COMMIT, PAGE_READWRITE);
+ return ret != null;
+ }
+ else
+ {
+ static assert(0, "Unsupported OS");
+ }
+ }
+}
+
+/**
+`AscendingPageAllocator` is a fast and safe allocator that rounds all allocations
+to multiples of the system's page size. It reserves a range of virtual addresses
+(using `mmap` on Posix and `VirtualAlloc` on Windows) and allocates memory at consecutive virtual
+addresses.
+
+When a chunk of memory is requested, the allocator finds a range of
+virtual pages that satisfy the requested size, changing their protection to
+read/write using OS primitives (`mprotect` and `VirtualProtect`, respectively).
+The physical memory is allocated on demand, when the pages are accessed.
+
+Deallocation removes any read/write permissions from the target pages
+and notifies the OS to reclaim the physical memory, while keeping the virtual
+memory.
+
+Because the allocator does not reuse memory, any dangling references to
+deallocated memory will always result in deterministically crashing the process.
+
+See_Also:
+$(HTTPS microsoft.com/en-us/research/wp-content/uploads/2017/03/kedia2017mem.pdf, Simple Fast and Safe Manual Memory Management) for the general approach.
+*/
+struct AscendingPageAllocator
+{
+ import std.typecons : Ternary;
+
+ // Docs for mixin functions
+ version (StdDdoc)
+ {
+ /**
+ Rounds the mapping size to the next multiple of the page size and calls
+ the OS primitive responsible for creating memory mappings: `mmap` on POSIX and
+ `VirtualAlloc` on Windows.
+
+ Params:
+ n = mapping size in bytes
+ */
+ this(size_t n) nothrow @nogc;
+
+ /**
+ Rounds the requested size to the next multiple of the page size.
+ */
+ size_t goodAllocSize(size_t n) nothrow @nogc;
+
+ /**
+ Decommit all physical memory associated with the buffer given as parameter,
+ but keep the range of virtual addresses.
+
+ On POSIX systems `deallocate` calls `mmap` with `MAP_FIXED' a second time to decommit the memory.
+ On Windows, it uses `VirtualFree` with `MEM_DECOMMIT`.
+ */
+ void deallocate(void[] b) nothrow @nogc;
+
+ /**
+ Returns `Ternary.yes` if the passed buffer is inside the range of virtual adresses.
+ Does not guarantee that the passed buffer is still valid.
+ */
+ Ternary owns(void[] buf) nothrow @nogc;
+
+ /**
+ Removes the memory mapping causing all physical memory to be decommited and
+ the virtual address space to be reclaimed.
+ */
+ bool deallocateAll() nothrow @nogc;
+
+ /**
+ Returns the available size for further allocations in bytes.
+ */
+ size_t getAvailableSize() nothrow @nogc;
+ }
+
+private:
+ size_t pageSize;
+ size_t numPages;
+
+ // The start of the virtual address range
+ void* data;
+
+ // Keeps track of there the next allocation should start
+ void* offset;
+
+ // Number of pages which contain alive objects
+ size_t pagesUsed;
+
+ // On allocation requests, we allocate an extra 'extraAllocPages' pages
+ // The address up to which we have permissions is stored in 'readWriteLimit'
+ void* readWriteLimit;
+ enum extraAllocPages = 1000;
+
+public:
+ enum uint alignment = 4096;
+
+ // Inject common function implementations
+ mixin AscendingPageAllocatorImpl!false;
+
+ /**
+ Rounds the allocation size to the next multiple of the page size.
+ The allocation only reserves a range of virtual pages but the actual
+ physical memory is allocated on demand, when accessing the memory.
+
+ Params:
+ n = Bytes to allocate
+
+ Returns:
+ `null` on failure or if the requested size exceeds the remaining capacity.
+ */
+ void[] allocate(size_t n) nothrow @nogc
+ {
+ import std.algorithm.comparison : min;
+
+ immutable pagedBytes = numPages * pageSize;
+ size_t goodSize = goodAllocSize(n);
+
+ // Requested exceeds the virtual memory range
+ if (goodSize > pagedBytes || offset - data > pagedBytes - goodSize)
+ return null;
+
+ // Current allocation exceeds readable/writable memory area
+ if (offset + goodSize > readWriteLimit)
+ {
+ // Extend r/w memory range to new limit
+ void* newReadWriteLimit = min(data + pagedBytes,
+ offset + goodSize + extraAllocPages * pageSize);
+ if (newReadWriteLimit != readWriteLimit)
+ {
+ assert(newReadWriteLimit > readWriteLimit);
+ if (!extendMemoryProtection(readWriteLimit, newReadWriteLimit - readWriteLimit))
+ return null;
+
+ readWriteLimit = newReadWriteLimit;
+ }
+ }
+
+ void* result = offset;
+ offset += goodSize;
+ pagesUsed += goodSize / pageSize;
+
+ return cast(void[]) result[0 .. n];
+ }
+
+ /**
+ Rounds the allocation size to the next multiple of the page size.
+ The allocation only reserves a range of virtual pages but the actual
+ physical memory is allocated on demand, when accessing the memory.
+
+ The allocated memory is aligned to the specified alignment `a`.
+
+ Params:
+ n = Bytes to allocate
+ a = Alignment
+
+ Returns:
+ `null` on failure or if the requested size exceeds the remaining capacity.
+ */
+ void[] alignedAllocate(size_t n, uint a) nothrow @nogc
+ {
+ void* alignedStart = cast(void*) roundUpToMultipleOf(cast(size_t) offset, a);
+ assert(alignedStart.alignedAt(a));
+ immutable pagedBytes = numPages * pageSize;
+ size_t goodSize = goodAllocSize(n);
+ if (goodSize > pagedBytes ||
+ alignedStart - data > pagedBytes - goodSize)
+ return null;
+
+ // Same logic as allocate, only that the buffer must be properly aligned
+ auto oldOffset = offset;
+ offset = alignedStart;
+ auto result = allocate(n);
+ if (!result)
+ offset = oldOffset;
+ return result;
+ }
+
+ /**
+ If the passed buffer is not the last allocation, then `delta` can be
+ at most the number of bytes left on the last page.
+ Otherwise, we can expand the last allocation until the end of the virtual
+ address range.
+ */
+ bool expand(ref void[] b, size_t delta) nothrow @nogc
+ {
+ import std.algorithm.comparison : min;
+
+ if (!delta) return true;
+ if (b is null) return false;
+
+ size_t goodSize = goodAllocSize(b.length);
+ size_t bytesLeftOnPage = goodSize - b.length;
+
+ // If this is not the last allocation, we can only expand until
+ // completely filling the last page covered by this buffer
+ if (b.ptr + goodSize != offset && delta > bytesLeftOnPage)
+ return false;
+
+ size_t extraPages = 0;
+
+ // If the extra `delta` bytes requested do not fit the last page
+ // compute how many extra pages are neeeded
+ if (delta > bytesLeftOnPage)
+ {
+ extraPages = goodAllocSize(delta - bytesLeftOnPage) / pageSize;
+ }
+ else
+ {
+ b = cast(void[]) b.ptr[0 .. b.length + delta];
+ return true;
+ }
+
+ if (extraPages > numPages || offset - data > pageSize * (numPages - extraPages))
+ return false;
+
+ void* newPtrEnd = b.ptr + goodSize + extraPages * pageSize;
+ if (newPtrEnd > readWriteLimit)
+ {
+ void* newReadWriteLimit = min(data + numPages * pageSize,
+ newPtrEnd + extraAllocPages * pageSize);
+ if (newReadWriteLimit > readWriteLimit)
+ {
+ if (!extendMemoryProtection(readWriteLimit, newReadWriteLimit - readWriteLimit))
+ return false;
+
+ readWriteLimit = newReadWriteLimit;
+ }
+ }
+
+ pagesUsed += extraPages;
+ offset += extraPages * pageSize;
+ b = cast(void[]) b.ptr[0 .. b.length + delta];
+ return true;
+ }
+
+ /**
+ Returns `Ternary.yes` if the allocator does not contain any alive objects
+ and `Ternary.no` otherwise.
+ */
+ Ternary empty() nothrow @nogc
+ {
+ return Ternary(pagesUsed == 0);
+ }
+
+ /**
+ Unmaps the whole virtual address range on destruction.
+ */
+ ~this() nothrow @nogc
+ {
+ if (data)
+ deallocateAll();
+ }
+}
+
+///
+@system @nogc nothrow unittest
+{
+ import core.memory : pageSize;
+
+ size_t numPages = 100;
+ void[] buf;
+ void[] prevBuf = null;
+ AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize);
+
+ foreach (i; 0 .. numPages)
+ {
+ // Allocation is rounded up to page size
+ buf = a.allocate(pageSize - 100);
+ assert(buf.length == pageSize - 100);
+
+ // Allocations are served at increasing addresses
+ if (prevBuf)
+ assert(prevBuf.ptr + pageSize == buf.ptr);
+
+ assert(a.deallocate(buf));
+ prevBuf = buf;
+ }
+}
+
+/**
+`SharedAscendingPageAllocator` is the threadsafe version of `AscendingPageAllocator`.
+*/
+shared struct SharedAscendingPageAllocator
+{
+ import std.typecons : Ternary;
+ import core.internal.spinlock : SpinLock;
+
+ // Docs for mixin functions
+ version (StdDdoc)
+ {
+ /**
+ Rounds the mapping size to the next multiple of the page size and calls
+ the OS primitive responsible for creating memory mappings: `mmap` on POSIX and
+ `VirtualAlloc` on Windows.
+
+ Params:
+ n = mapping size in bytes
+ */
+ this(size_t n) nothrow @nogc;
+
+ /**
+ Rounds the requested size to the next multiple of the page size.
+ */
+ size_t goodAllocSize(size_t n) nothrow @nogc;
+
+ /**
+ Decommit all physical memory associated with the buffer given as parameter,
+ but keep the range of virtual addresses.
+
+ On POSIX systems `deallocate` calls `mmap` with `MAP_FIXED' a second time to decommit the memory.
+ On Windows, it uses `VirtualFree` with `MEM_DECOMMIT`.
+ */
+ void deallocate(void[] b) nothrow @nogc;
+
+ /**
+ Returns `Ternary.yes` if the passed buffer is inside the range of virtual adresses.
+ Does not guarantee that the passed buffer is still valid.
+ */
+ Ternary owns(void[] buf) nothrow @nogc;
+
+ /**
+ Removes the memory mapping causing all physical memory to be decommited and
+ the virtual address space to be reclaimed.
+ */
+ bool deallocateAll() nothrow @nogc;
+
+ /**
+ Returns the available size for further allocations in bytes.
+ */
+ size_t getAvailableSize() nothrow @nogc;
+ }
+
+private:
+ size_t pageSize;
+ size_t numPages;
+
+ // The start of the virtual address range
+ shared void* data;
+
+ // Keeps track of there the next allocation should start
+ shared void* offset;
+
+ // On allocation requests, we allocate an extra 'extraAllocPages' pages
+ // The address up to which we have permissions is stored in 'readWriteLimit'
+ shared void* readWriteLimit;
+ enum extraAllocPages = 1000;
+ SpinLock lock;
+
+public:
+ enum uint alignment = 4096;
+
+ // Inject common function implementations
+ mixin AscendingPageAllocatorImpl!true;
+
+ /**
+ Rounds the allocation size to the next multiple of the page size.
+ The allocation only reserves a range of virtual pages but the actual
+ physical memory is allocated on demand, when accessing the memory.
+
+ Params:
+ n = Bytes to allocate
+
+ Returns:
+ `null` on failure or if the requested size exceeds the remaining capacity.
+ */
+ void[] allocate(size_t n) nothrow @nogc
+ {
+ return allocateImpl(n, 1);
+ }
+
+ /**
+ Rounds the allocation size to the next multiple of the page size.
+ The allocation only reserves a range of virtual pages but the actual
+ physical memory is allocated on demand, when accessing the memory.
+
+ The allocated memory is aligned to the specified alignment `a`.
+
+ Params:
+ n = Bytes to allocate
+ a = Alignment
+
+ Returns:
+ `null` on failure or if the requested size exceeds the remaining capacity.
+ */
+ void[] alignedAllocate(size_t n, uint a) nothrow @nogc
+ {
+ // For regular `allocate` calls, `a` will be set to 1
+ return allocateImpl(n, a);
+ }
+
+ private void[] allocateImpl(size_t n, uint a) nothrow @nogc
+ {
+ import std.algorithm.comparison : min;
+
+ size_t localExtraAlloc;
+ void* localOffset;
+ immutable pagedBytes = numPages * pageSize;
+ size_t goodSize = goodAllocSize(n);
+
+ if (goodSize > pagedBytes)
+ return null;
+
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ localOffset = cast(void*) offset;
+ void* alignedStart = cast(void*) roundUpToMultipleOf(cast(size_t) localOffset, a);
+ assert(alignedStart.alignedAt(a));
+ if (alignedStart - data > pagedBytes - goodSize)
+ return null;
+
+ localOffset = alignedStart + goodSize;
+ if (localOffset > readWriteLimit)
+ {
+ void* newReadWriteLimit = min(cast(void*) data + pagedBytes,
+ cast(void*) localOffset + extraAllocPages * pageSize);
+ assert(newReadWriteLimit > readWriteLimit);
+ localExtraAlloc = newReadWriteLimit - readWriteLimit;
+ if (!extendMemoryProtection(cast(void*) readWriteLimit, localExtraAlloc))
+ return null;
+ readWriteLimit = cast(shared(void*)) newReadWriteLimit;
+ }
+
+ offset = cast(typeof(offset)) localOffset;
+ return cast(void[]) alignedStart[0 .. n];
+ }
+
+ /**
+ If the passed buffer is not the last allocation, then `delta` can be
+ at most the number of bytes left on the last page.
+ Otherwise, we can expand the last allocation until the end of the virtual
+ address range.
+ */
+ bool expand(ref void[] b, size_t delta) nothrow @nogc
+ {
+ import std.algorithm.comparison : min;
+
+ if (!delta) return true;
+ if (b is null) return false;
+
+ void* localOffset;
+ size_t localExtraAlloc;
+ size_t goodSize = goodAllocSize(b.length);
+ size_t bytesLeftOnPage = goodSize - b.length;
+
+ if (bytesLeftOnPage >= delta)
+ {
+ b = cast(void[]) b.ptr[0 .. b.length + delta];
+ return true;
+ }
+
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ localOffset = cast(void*) offset;
+ if (b.ptr + goodSize != localOffset)
+ return false;
+
+ size_t extraPages = goodAllocSize(delta - bytesLeftOnPage) / pageSize;
+ if (extraPages > numPages || localOffset - data > pageSize * (numPages - extraPages))
+ return false;
+
+
+ localOffset = b.ptr + goodSize + extraPages * pageSize;
+ if (localOffset > readWriteLimit)
+ {
+ void* newReadWriteLimit = min(cast(void*) data + numPages * pageSize,
+ localOffset + extraAllocPages * pageSize);
+ assert(newReadWriteLimit > readWriteLimit);
+ localExtraAlloc = newReadWriteLimit - readWriteLimit;
+ if (!extendMemoryProtection(cast(void*) readWriteLimit, localExtraAlloc))
+ return false;
+ readWriteLimit = cast(shared(void*)) newReadWriteLimit;
+ }
+
+ offset = cast(typeof(offset)) localOffset;
+ b = cast(void[]) b.ptr[0 .. b.length + delta];
+ return true;
+ }
+}
+
+///
+@system unittest
+{
+ import core.memory : pageSize;
+ import core.thread : ThreadGroup;
+
+ enum numThreads = 100;
+ shared SharedAscendingPageAllocator a = SharedAscendingPageAllocator(pageSize * numThreads);
+
+ void fun()
+ {
+ void[] b = a.allocate(pageSize);
+ assert(b.length == pageSize);
+
+ assert(a.deallocate(b));
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+}
+
+version (StdUnittest)
+{
+ private static void testrw(void[] b) @nogc nothrow
+ {
+ ubyte* buf = cast(ubyte*) b.ptr;
+ buf[0] = 100;
+ assert(buf[0] == 100);
+ buf[b.length - 1] = 101;
+ assert(buf[b.length - 1] == 101);
+ }
+}
+
+@system @nogc nothrow unittest
+{
+ static void testAlloc(Allocator)(ref Allocator a) @nogc nothrow
+ {
+ void[] b1 = a.allocate(1);
+ assert(a.getAvailableSize() == 3 * pageSize);
+ testrw(b1);
+ void[] b2 = a.allocate(2);
+ assert(a.getAvailableSize() == 2 * pageSize);
+ testrw(b2);
+ void[] b3 = a.allocate(pageSize + 1);
+ assert(a.getAvailableSize() == 0);
+
+ testrw(b3);
+ assert(b1.length == 1);
+ assert(b2.length == 2);
+ assert(b3.length == pageSize + 1);
+
+ assert(a.offset - a.data == 4 * pageSize);
+ void[] b4 = a.allocate(4);
+ assert(!b4);
+
+ a.deallocate(b1);
+ assert(a.data);
+ a.deallocate(b2);
+ assert(a.data);
+ a.deallocate(b3);
+ }
+
+ AscendingPageAllocator a = AscendingPageAllocator(4 * pageSize);
+ shared SharedAscendingPageAllocator aa = SharedAscendingPageAllocator(4 * pageSize);
+
+ testAlloc(a);
+ testAlloc(aa);
+}
+
+@system @nogc nothrow unittest
+{
+ size_t numPages = 26214;
+ AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize);
+ foreach (i; 0 .. numPages)
+ {
+ void[] buf = a.allocate(pageSize);
+ assert(buf.length == pageSize);
+ testrw(buf);
+ a.deallocate(buf);
+ }
+
+ assert(!a.allocate(1));
+ assert(a.getAvailableSize() == 0);
+}
+
+@system @nogc nothrow unittest
+{
+ size_t numPages = 26214;
+ uint alignment = cast(uint) pageSize;
+ AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize);
+
+ foreach (i; 0 .. numPages)
+ {
+ void[] buf = a.alignedAllocate(pageSize, alignment);
+ assert(buf.length == pageSize);
+ testrw(buf);
+ a.deallocate(buf);
+ }
+
+ assert(!a.allocate(1));
+ assert(a.getAvailableSize() == 0);
+}
+
+@system @nogc nothrow unittest
+{
+ static void testAlloc(Allocator)(ref Allocator a) @nogc nothrow
+ {
+ import std.traits : hasMember;
+
+ size_t numPages = 5;
+ uint alignment = cast(uint) pageSize;
+
+ void[] b1 = a.allocate(pageSize / 2);
+ assert(b1.length == pageSize / 2);
+
+ void[] b2 = a.alignedAllocate(pageSize / 2, alignment);
+ assert(a.expand(b1, pageSize / 2));
+ assert(a.expand(b1, 0));
+ assert(!a.expand(b1, 1));
+ testrw(b1);
+
+ assert(a.expand(b2, pageSize / 2));
+ testrw(b2);
+ assert(b2.length == pageSize);
+
+ assert(a.getAvailableSize() == pageSize * 3);
+
+ void[] b3 = a.allocate(pageSize / 2);
+ assert(a.reallocate(b1, b1.length));
+ assert(a.reallocate(b2, b2.length));
+ assert(a.reallocate(b3, b3.length));
+
+ assert(b3.length == pageSize / 2);
+ testrw(b3);
+ assert(a.expand(b3, pageSize / 4));
+ testrw(b3);
+ assert(a.expand(b3, 0));
+ assert(b3.length == pageSize / 2 + pageSize / 4);
+ assert(a.expand(b3, pageSize / 4 - 1));
+ testrw(b3);
+ assert(a.expand(b3, 0));
+ assert(b3.length == pageSize - 1);
+ assert(a.expand(b3, 2));
+ assert(a.expand(b3, 0));
+ assert(a.getAvailableSize() == pageSize);
+ assert(b3.length == pageSize + 1);
+ testrw(b3);
+
+ assert(a.reallocate(b1, b1.length));
+ assert(a.reallocate(b2, b2.length));
+ assert(a.reallocate(b3, b3.length));
+
+ assert(a.reallocate(b3, 2 * pageSize));
+ testrw(b3);
+ assert(a.reallocate(b1, pageSize - 1));
+ testrw(b1);
+ assert(a.expand(b1, 1));
+ testrw(b1);
+ assert(!a.expand(b1, 1));
+
+ a.deallocate(b1);
+ a.deallocate(b2);
+ a.deallocate(b3);
+ }
+
+ size_t numPages = 5;
+ uint alignment = cast(uint) pageSize;
+ AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize);
+ shared SharedAscendingPageAllocator aa = SharedAscendingPageAllocator(numPages * pageSize);
+
+ testAlloc(a);
+ testAlloc(aa);
+}
+
+@system @nogc nothrow unittest
+{
+ size_t numPages = 21000;
+ enum testNum = 100;
+ enum allocPages = 10;
+ void[][testNum] buf;
+ AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize);
+
+ for (int i = 0; i < numPages; i += testNum * allocPages)
+ {
+ foreach (j; 0 .. testNum)
+ {
+ buf[j] = a.allocate(pageSize * allocPages);
+ testrw(buf[j]);
+ }
+
+ foreach (j; 0 .. testNum)
+ {
+ a.deallocate(buf[j]);
+ }
+ }
+}
+
+@system @nogc nothrow unittest
+{
+ size_t numPages = 21000;
+ enum testNum = 100;
+ enum allocPages = 10;
+ void[][testNum] buf;
+ shared SharedAscendingPageAllocator a = SharedAscendingPageAllocator(numPages * pageSize);
+
+ for (int i = 0; i < numPages; i += testNum * allocPages)
+ {
+ foreach (j; 0 .. testNum)
+ {
+ buf[j] = a.allocate(pageSize * allocPages);
+ testrw(buf[j]);
+ }
+
+ foreach (j; 0 .. testNum)
+ {
+ a.deallocate(buf[j]);
+ }
+ }
+}
+
+@system @nogc nothrow unittest
+{
+ enum numPages = 2;
+ AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize);
+ void[] b = a.allocate((numPages + 1) * pageSize);
+ assert(b is null);
+ b = a.allocate(1);
+ assert(b.length == 1);
+ assert(a.getAvailableSize() == pageSize);
+ a.deallocateAll();
+ assert(!a.data && !a.offset);
+}
+
+@system @nogc nothrow unittest
+{
+ enum numPages = 26;
+ AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize);
+ uint alignment = cast(uint) ((numPages / 2) * pageSize);
+ void[] b = a.alignedAllocate(pageSize, alignment);
+ assert(b.length == pageSize);
+ testrw(b);
+ assert(b.ptr.alignedAt(alignment));
+ a.deallocateAll();
+ assert(!a.data && !a.offset);
+}
+
+@system @nogc nothrow unittest
+{
+ enum numPages = 10;
+ AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize);
+ uint alignment = cast(uint) (2 * pageSize);
+
+ void[] b1 = a.alignedAllocate(pageSize, alignment);
+ assert(b1.length == pageSize);
+ testrw(b1);
+ assert(b1.ptr.alignedAt(alignment));
+
+ void[] b2 = a.alignedAllocate(pageSize, alignment);
+ assert(b2.length == pageSize);
+ testrw(b2);
+ assert(b2.ptr.alignedAt(alignment));
+
+ void[] b3 = a.alignedAllocate(pageSize, alignment);
+ assert(b3.length == pageSize);
+ testrw(b3);
+ assert(b3.ptr.alignedAt(alignment));
+
+ void[] b4 = a.allocate(pageSize);
+ assert(b4.length == pageSize);
+ testrw(b4);
+
+ assert(a.deallocate(b1));
+ assert(a.deallocate(b2));
+ assert(a.deallocate(b3));
+ assert(a.deallocate(b4));
+
+ a.deallocateAll();
+ assert(!a.data && !a.offset);
+}
+
+@system unittest
+{
+ import core.thread : ThreadGroup;
+ import std.algorithm.sorting : sort;
+ import core.internal.spinlock : SpinLock;
+
+ enum numThreads = 100;
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+ ulong[numThreads] ptrVals;
+ size_t count = 0;
+ shared SharedAscendingPageAllocator a = SharedAscendingPageAllocator(pageSize * numThreads);
+
+ void fun()
+ {
+ void[] b = a.allocate(4000);
+ assert(b.length == 4000);
+
+ assert(a.expand(b, 96));
+ assert(b.length == 4096);
+
+ lock.lock();
+ ptrVals[count] = cast(ulong) b.ptr;
+ count++;
+ lock.unlock();
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ ptrVals[].sort();
+ foreach (i; 0 .. numThreads - 1)
+ {
+ assert(ptrVals[i] + pageSize == ptrVals[i + 1]);
+ }
+}
+
+@system unittest
+{
+ import core.thread : ThreadGroup;
+ import std.algorithm.sorting : sort;
+ import core.internal.spinlock : SpinLock;
+
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+ enum numThreads = 100;
+ void[][numThreads] buf;
+ size_t count = 0;
+ shared SharedAscendingPageAllocator a = SharedAscendingPageAllocator(2 * pageSize * numThreads);
+
+ void fun()
+ {
+ enum expand = 96;
+ void[] b = a.allocate(pageSize - expand);
+ assert(b.length == pageSize - expand);
+
+ assert(a.expand(b, expand));
+ assert(b.length == pageSize);
+
+ a.expand(b, pageSize);
+ assert(b.length == pageSize || b.length == pageSize * 2);
+
+ lock.lock();
+ buf[count] = b;
+ count++;
+ lock.unlock();
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ sort!((a, b) => a.ptr < b.ptr)(buf[0 .. 100]);
+ foreach (i; 0 .. numThreads - 1)
+ {
+ assert(buf[i].ptr + buf[i].length == buf[i + 1].ptr);
+ }
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/bitmapped_block.d b/libphobos/src/std/experimental/allocator/building_blocks/bitmapped_block.d
index 24e27a9c5ed..571e4e69161 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/bitmapped_block.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/bitmapped_block.d
@@ -1,87 +1,48 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/bitmapped_block.d)
+*/
module std.experimental.allocator.building_blocks.bitmapped_block;
import std.experimental.allocator.building_blocks.null_allocator;
import std.experimental.allocator.common;
+import std.typecons : Flag, Yes, No;
-/**
-
-$(D BitmappedBlock) implements a simple heap consisting of one contiguous area
-of memory organized in blocks, each of size $(D theBlockSize). A block is a unit
-of allocation. A bitmap serves as bookkeeping data, more precisely one bit per
-block indicating whether that block is currently allocated or not.
-
-Passing $(D NullAllocator) as $(D ParentAllocator) (the default) means user code
-manages allocation of the memory block from the outside; in that case
-$(D BitmappedBlock) must be constructed with a $(D void[]) preallocated block and
-has no responsibility regarding the lifetime of its support underlying storage.
-If another allocator type is passed, $(D BitmappedBlock) defines a destructor that
-uses the parent allocator to release the memory block. That makes the combination of $(D AllocatorList), $(D BitmappedBlock), and a back-end allocator such as $(D MmapAllocator) a simple and scalable solution for memory allocation.
-
-There are advantages to storing bookkeeping data separated from the payload
-(as opposed to e.g. using $(D AffixAllocator) to store metadata together with
-each allocation). The layout is more compact (overhead is one bit per block),
-searching for a free block during allocation enjoys better cache locality, and
-deallocation does not touch memory around the payload being deallocated (which
-is often cold).
-
-Allocation requests are handled on a first-fit basis. Although linear in
-complexity, allocation is in practice fast because of the compact bookkeeping
-representation, use of simple and fast bitwise routines, and caching of the
-first available block position. A known issue with this general approach is
-fragmentation, partially mitigated by coalescing. Since $(D BitmappedBlock) does
-not need to maintain the allocated size, freeing memory implicitly coalesces
-free blocks together. Also, tuning $(D blockSize) has a considerable impact on
-both internal and external fragmentation.
-The size of each block can be selected either during compilation or at run
-time. Statically-known block sizes are frequent in practice and yield slightly
-better performance. To choose a block size statically, pass it as the $(D
-blockSize) parameter as in $(D BitmappedBlock!(Allocator, 4096)). To choose a block
-size parameter, use $(D BitmappedBlock!(Allocator, chooseAtRuntime)) and pass the
-block size to the constructor.
-
-*/
-struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
- ParentAllocator = NullAllocator)
+// Common implementation for shared and non-shared versions of the BitmappedBlock
+private mixin template BitmappedBlockImpl(bool isShared, bool multiBlock)
{
import std.conv : text;
import std.traits : hasMember;
import std.typecons : Ternary;
import std.typecons : tuple, Tuple;
- @system unittest
- {
- import std.algorithm.comparison : max;
- import std.experimental.allocator.mallocator : AlignedMallocator;
- auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64,
- max(theAlignment, cast(uint) size_t.sizeof)));
- scope(exit) AlignedMallocator.instance.deallocate(m);
- testAllocator!(() => BitmappedBlock(m));
- }
+ static if (isShared && multiBlock)
+ import core.internal.spinlock : SpinLock;
+
static assert(theBlockSize > 0 && theAlignment.isGoodStaticAlignment);
- static assert(theBlockSize == chooseAtRuntime
- || theBlockSize % theAlignment == 0,
- "Block size must be a multiple of the alignment");
+ static assert(theBlockSize == chooseAtRuntime ||
+ theBlockSize % theAlignment == 0, "Block size must be a multiple of the alignment");
- /**
- If $(D blockSize == chooseAtRuntime), $(D BitmappedBlock) offers a read/write
- property $(D blockSize). It must be set before any use of the allocator.
- Otherwise (i.e. $(D theBlockSize) is a legit constant), $(D blockSize) is
- an alias for $(D theBlockSize). Whether constant or variable, must also be
- a multiple of $(D alignment). This constraint is $(D assert)ed statically
- and dynamically.
- */
static if (theBlockSize != chooseAtRuntime)
{
alias blockSize = theBlockSize;
}
else
{
+ // It is the caller's responsibilty to synchronize this with
+ // allocate/deallocate in shared environments
@property uint blockSize() { return _blockSize; }
@property void blockSize(uint s)
{
- assert(!_control && s % alignment == 0);
+ static if (multiBlock)
+ {
+ assert((cast(BitVector) _control).length == 0 && s % alignment == 0);
+ }
+ else
+ {
+ assert(_control.length == 0 && s % alignment == 0);
+ }
_blockSize = s;
}
private uint _blockSize;
@@ -97,18 +58,8 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
static assert(parentAlignment >= ulong.alignof);
}
- /**
- The _alignment offered is user-configurable statically through parameter
- $(D theAlignment), defaulted to $(D platformAlignment).
- */
alias alignment = theAlignment;
- // state {
- /**
- The _parent allocator. Depending on whether $(D ParentAllocator) holds state
- or not, this is a member variable or an alias for
- `ParentAllocator.instance`.
- */
static if (stateSize!ParentAllocator)
{
ParentAllocator parent;
@@ -117,42 +68,44 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
{
alias parent = ParentAllocator.instance;
}
- private uint _blocks;
- private BitVector _control;
+
+ private size_t _blocks;
private void[] _payload;
private size_t _startIdx;
- // }
+ // For multiblock, '_control' is a BitVector, otherwise just a regular ulong[]
+ static if (multiBlock)
+ {
+ // Keeps track of first block which has never been used in an allocation.
+ // All blocks which are located right to the '_freshBit', should have never been
+ // allocated
+ private ulong _freshBit;
+ private BitVector _control;
+ }
+ else
+ {
+ private ulong[] _control;
+ }
+
+ static if (multiBlock && isShared)
+ {
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+ }
+
+ pure nothrow @safe @nogc
private size_t totalAllocation(size_t capacity)
{
auto blocks = capacity.divideRoundUp(blockSize);
auto leadingUlongs = blocks.divideRoundUp(64);
import std.algorithm.comparison : min;
immutable initialAlignment = min(parentAlignment,
- 1U << trailingZeros(leadingUlongs * 8));
+ 1U << min(31U, trailingZeros(leadingUlongs * 8)));
auto maxSlack = alignment <= initialAlignment
? 0
: alignment - initialAlignment;
- //writeln(maxSlack);
return leadingUlongs * 8 + maxSlack + blockSize * blocks;
}
- /**
- Constructs a block allocator given a hunk of memory, or a desired capacity
- in bytes.
-
- $(UL
- $(LI If $(D ParentAllocator) is $(D NullAllocator), only the constructor
- taking $(D data) is defined and the user is responsible for freeing $(D
- data) if desired.)
- $(LI Otherwise, both constructors are defined. The $(D data)-based
- constructor assumes memory has been allocated with the parent allocator.
- The $(D capacity)-based constructor uses $(D ParentAllocator) to allocate
- an appropriate contiguous hunk of memory. Regardless of the constructor
- used, the destructor releases the memory by using $(D
- ParentAllocator.deallocate).)
- )
- */
this(ubyte[] data)
{
immutable a = data.ptr.effectiveAlignment;
@@ -161,31 +114,49 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
immutable ulong totalBits = data.length * 8;
immutable ulong bitsPerBlock = blockSize * 8 + 1;
- // Get a first estimate
- import std.conv : to;
- _blocks = to!uint(totalBits / bitsPerBlock);
+ _blocks = totalBits / bitsPerBlock;
// Reality is a bit more complicated, iterate until a good number of
// blocks found.
- for (; _blocks; --_blocks)
+ size_t localBlocks;
+ for (localBlocks = _blocks; localBlocks; --localBlocks)
{
- immutable controlWords = _blocks.divideRoundUp(64);
+ immutable controlWords = localBlocks.divideRoundUp(64);
auto payload = data[controlWords * 8 .. $].roundStartToMultipleOf(
alignment);
- if (payload.length < _blocks * blockSize)
+ if (payload.length < localBlocks * blockSize)
{
// Overestimated
continue;
}
- _control = BitVector((cast(ulong*) data.ptr)[0 .. controlWords]);
- _control[] = 0;
- _payload = payload;
+
+ // Need the casts for shared versions
+ static if (multiBlock)
+ {
+ _control = cast(typeof(_control)) BitVector((cast(ulong*) data.ptr)[0 .. controlWords]);
+ (cast(BitVector) _control)[] = 0;
+ }
+ else
+ {
+ _control = (cast(typeof(_control.ptr)) data.ptr)[0 .. controlWords];
+ _control[] = 0;
+ }
+
+ _payload = cast(typeof(_payload)) payload;
break;
}
+
+ _blocks = cast(typeof(_blocks)) localBlocks;
}
- /// Ditto
- static if (!is(ParentAllocator == NullAllocator))
+ static if (chooseAtRuntime == theBlockSize)
+ this(ubyte[] data, uint blockSize)
+ {
+ this._blockSize = blockSize;
+ this(data);
+ }
+
+ static if (!is(ParentAllocator == NullAllocator) && !stateSize!ParentAllocator)
this(size_t capacity)
{
size_t toAllocate = totalAllocation(capacity);
@@ -194,498 +165,1225 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
assert(_blocks * blockSize >= capacity);
}
- /**
- If $(D ParentAllocator) is not $(D NullAllocator) and defines $(D
- deallocate), the destructor is defined to deallocate the block held.
- */
- static if (!is(ParentAllocator == NullAllocator)
- && hasMember!(ParentAllocator, "deallocate"))
- ~this()
+ static if (!is(ParentAllocator == NullAllocator) && stateSize!ParentAllocator)
+ this(ParentAllocator parent, size_t capacity)
{
- auto start = _control.rep.ptr, end = _payload.ptr + _payload.length;
- parent.deallocate(start[0 .. end - start]);
+ this.parent = parent;
+ size_t toAllocate = totalAllocation(capacity);
+ auto data = cast(ubyte[])(parent.allocate(toAllocate));
+ this(data);
}
- /*
- Adjusts the memoized _startIdx to the leftmost control word that has at
- least one zero bit. Assumes all control words to the left of $(D
- _control[_startIdx]) are already occupied.
- */
- private void adjustStartIdx()
+ static if (!is(ParentAllocator == NullAllocator) &&
+ chooseAtRuntime == theBlockSize &&
+ !stateSize!ParentAllocator)
+ this(size_t capacity, uint blockSize)
{
- while (_startIdx < _control.rep.length
- && _control.rep[_startIdx] == ulong.max)
- {
- ++_startIdx;
- }
+ this._blockSize = blockSize;
+ this(capacity);
}
- /*
- Returns the blocks corresponding to the control bits starting at word index
- wordIdx and bit index msbIdx (MSB=0) for a total of howManyBlocks.
- */
- private void[] blocksFor(size_t wordIdx, uint msbIdx, size_t howManyBlocks)
+ static if (!is(ParentAllocator == NullAllocator) &&
+ chooseAtRuntime == theBlockSize &&
+ stateSize!ParentAllocator)
+ this(ParentAllocator parent, size_t capacity, uint blockSize)
{
- assert(msbIdx <= 63);
- const start = (wordIdx * 64 + msbIdx) * blockSize;
- const end = start + blockSize * howManyBlocks;
- if (end <= _payload.length) return _payload[start .. end];
- // This could happen if we have more control bits than available memory.
- // That's possible because the control bits are rounded up to fit in
- // 64-bit words.
- return null;
+ this._blockSize = blockSize;
+ this(parent, capacity);
}
- /**
- Returns the actual bytes allocated when $(D n) bytes are requested, i.e.
- $(D n.roundUpToMultipleOf(blockSize)).
- */
+ static if (!is(ParentAllocator == NullAllocator)
+ && hasMember!(ParentAllocator, "deallocate"))
+ ~this()
+ {
+ // multiblock bitmapped blocks use a BitVector
+ static if (multiBlock)
+ {
+ void* start = cast(void*) _control.rep.ptr;
+ }
+ else
+ {
+ void* start = cast(void*) _control.ptr;
+ }
+ void* end = cast(void*) (_payload.ptr + _payload.length);
+ parent.deallocate(start[0 .. end - start]);
+ }
+
+ pure nothrow @safe @nogc
size_t goodAllocSize(size_t n)
{
return n.roundUpToMultipleOf(blockSize);
}
- /**
- Allocates $(D s) bytes of memory and returns it, or $(D null) if memory
- could not be allocated.
-
- The following information might be of help with choosing the appropriate
- block size. Actual allocation occurs in sizes multiple of the block size.
- Allocating one block is the fastest because only one 0 bit needs to be
- found in the metadata. Allocating 2 through 64 blocks is the next cheapest
- because it affects a maximum of two $(D ulong)s in the metadata.
- Allocations greater than 64 blocks require a multiword search through the
- metadata.
- */
- @trusted void[] allocate(const size_t s)
+ // Implementation of the 'multiBlock' BitmappedBlock
+ // For the shared version, the methods are protected by a common lock
+ static if (multiBlock)
{
- const blocks = s.divideRoundUp(blockSize);
- void[] result = void;
-
- switcharoo:
- switch (blocks)
+ /*
+ Adjusts the memoized _startIdx to the leftmost control word that has at
+ least one zero bit. Assumes all control words to the left of $(D
+ _control[_startIdx]) are already occupied.
+ */
+ private void adjustStartIdx()
{
- case 1:
- // inline code here for speed
- // find the next available block
- foreach (i; _startIdx .. _control.rep.length)
+ while (_startIdx < _control.rep.length && _control.rep[_startIdx] == ulong.max)
{
- const w = _control.rep[i];
- if (w == ulong.max) continue;
- uint j = leadingOnes(w);
- assert(j < 64);
- assert((_control.rep[i] & ((1UL << 63) >> j)) == 0);
- _control.rep[i] |= (1UL << 63) >> j;
- if (i == _startIdx)
+ static if (isShared)
{
- adjustStartIdx();
+ // Shared demands atomic increment, however this is protected
+ // by a lock. Regular increment is fine
+ auto localStart = _startIdx + 1;
+ _startIdx = localStart;
}
- result = blocksFor(i, j, 1);
- break switcharoo;
+ else
+ {
+ ++_startIdx;
+ }
+ }
+ }
+
+ /*
+ Based on the latest allocated bit, 'newBit', it adjusts '_freshBit'
+ */
+ pure nothrow @safe @nogc
+ private void adjustFreshBit(const ulong newBit)
+ {
+ import std.algorithm.comparison : max;
+ static if (isShared)
+ {
+ auto localFreshBit = max(newBit, _freshBit);
+ _freshBit = localFreshBit;
+ }
+ else
+ {
+ _freshBit = max(newBit, _freshBit);
}
- goto case 0; // fall through
- case 0:
+ }
+
+ /*
+ Returns the blocks corresponding to the control bits starting at word index
+ wordIdx and bit index msbIdx (MSB=0) for a total of howManyBlocks.
+ */
+ @trusted
+ private void[] blocksFor(this _)(size_t wordIdx, uint msbIdx, size_t howManyBlocks)
+ {
+ assert(msbIdx <= 63);
+ const start = (wordIdx * 64 + msbIdx) * blockSize;
+ const end = start + blockSize * howManyBlocks;
+ if (start == end) return null;
+ if (end <= _payload.length) return cast(void[]) _payload[start .. end];
+ // This could happen if we have more control bits than available memory.
+ // That's possible because the control bits are rounded up to fit in
+ // 64-bit words.
return null;
- case 2: .. case 64:
- result = smallAlloc(cast(uint) blocks);
- break;
- default:
- result = hugeAlloc(blocks);
- break;
}
- return result.ptr ? result.ptr[0 .. s] : null;
- }
- /**
- Allocates a block with specified alignment $(D a). The alignment must be a
- power of 2. If $(D a <= alignment), function forwards to $(D allocate).
- Otherwise, it attempts to overallocate and then adjust the result for
- proper alignment. In the worst case the slack memory is around two blocks.
- */
- void[] alignedAllocate(size_t n, uint a)
- {
- import std.math : isPowerOf2;
- assert(a.isPowerOf2);
- if (a <= alignment) return allocate(n);
+ static if (isShared)
+ nothrow @safe @nogc
+ void[] allocate(const size_t s)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
- // Overallocate to make sure we can get an aligned block
- auto b = allocate((n + a - alignment).roundUpToMultipleOf(blockSize));
- if (!b.ptr) return null;
- auto result = b.roundStartToMultipleOf(a);
- assert(result.length >= n);
- result = result.ptr[0 .. n]; // final result
+ return allocateImpl(s);
+ }
- // Free any blocks that might be slack at the beginning
- auto slackHeadingBlocks = (result.ptr - b.ptr) / blockSize;
- if (slackHeadingBlocks)
+ static if (!isShared)
+ pure nothrow @safe @nogc
+ void[] allocate(const size_t s)
{
- deallocate(b[0 .. slackHeadingBlocks * blockSize]);
+ return allocateImpl(s);
}
- // Free any blocks that might be slack at the end
- auto slackTrailingBlocks = ((b.ptr + b.length)
- - (result.ptr + result.length)) / blockSize;
- if (slackTrailingBlocks)
+
+ // If shared, this is protected by a lock inside 'allocate'
+ pure nothrow @trusted @nogc
+ private void[] allocateImpl(const size_t s)
{
- deallocate(b[$ - slackTrailingBlocks * blockSize .. $]);
+ const blocks = s.divideRoundUp(blockSize);
+ void[] result;
+
+ Lswitch:
+ switch (blocks)
+ {
+ case 1:
+ // inline code here for speed
+ // find the next available block
+ foreach (i; _startIdx .. _control.rep.length)
+ {
+ const w = _control.rep[i];
+ if (w == ulong.max) continue;
+ uint j = leadingOnes(w);
+ assert(j < 64, "Invalid number of blocks");
+ assert((_control.rep[i] & ((1UL << 63) >> j)) == 0, "Corrupted bitmap");
+ static if (isShared)
+ {
+ // Need the cast because shared does not recognize the lock
+ *(cast(ulong*) &_control._rep[i]) |= (1UL << 63) >> j;
+ }
+ else
+ {
+ _control.rep[i] |= (1UL << 63) >> j;
+ }
+ if (i == _startIdx)
+ {
+ adjustStartIdx();
+ }
+ result = blocksFor(i, j, 1);
+ break Lswitch;
+ }
+ goto case 0; // fall through
+ case 0:
+ return null;
+ case 2: .. case 64:
+ result = smallAlloc(cast(uint) blocks);
+ break;
+ default:
+ result = hugeAlloc(blocks);
+ break;
+ }
+ if (result)
+ {
+ adjustFreshBit((result.ptr - _payload.ptr) / blockSize + blocks);
+ }
+ return result.ptr ? result.ptr[0 .. s] : null;
}
- return result;
- }
+ @trusted void[] allocateFresh(const size_t s)
+ {
+ static if (isShared)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+ }
- /**
- If the $(D BitmappedBlock) object is empty (has no active allocation), allocates
- all memory within and returns a slice to it. Otherwise, returns $(D null)
- (i.e. no attempt is made to allocate the largest available block).
- */
- void[] allocateAll()
- {
- if (empty != Ternary.yes) return null;
- _control[] = 1;
- return _payload;
- }
+ const blocks = s.divideRoundUp(blockSize);
- /**
- Returns `Ternary.yes` if `b` belongs to the `BitmappedBlock` object,
- `Ternary.no` otherwise. Never returns `Ternary.unkown`. (This
- method is somewhat tolerant in that accepts an interior slice.)
- */
- Ternary owns(void[] b) const
- {
- //if (!b.ptr) return Ternary.no;
- assert(b.ptr !is null || b.length == 0, "Corrupt block.");
- return Ternary(b.ptr >= _payload.ptr
- && b.ptr + b.length <= _payload.ptr + _payload.length);
- }
+ void[] result = blocksFor(cast(size_t) (_freshBit / 64),
+ cast(uint) (_freshBit % 64), blocks);
+ if (result)
+ {
+ (cast(BitVector) _control)[_freshBit .. _freshBit + blocks] = 1;
+ static if (isShared)
+ {
+ ulong localFreshBit = _freshBit;
+ localFreshBit += blocks;
+ _freshBit = localFreshBit;
+ }
+ else
+ {
+ _freshBit += blocks;
+ }
+ }
+ return result;
+ }
- /*
- Tries to allocate "blocks" blocks at the exact position indicated by the
- position wordIdx/msbIdx (msbIdx counts from MSB, i.e. MSB has index 0). If
- it succeeds, fills "result" with the result and returns tuple(size_t.max,
- 0). Otherwise, returns a tuple with the next position to search.
- */
- private Tuple!(size_t, uint) allocateAt(size_t wordIdx, uint msbIdx,
- size_t blocks, ref void[] result)
- {
- assert(blocks > 0);
- assert(wordIdx < _control.rep.length);
- assert(msbIdx <= 63);
- if (msbIdx + blocks <= 64)
+ void[] alignedAllocate(size_t n, uint a)
{
- // Allocation should fit this control word
- if (setBitsIfZero(_control.rep[wordIdx],
- cast(uint) (64 - msbIdx - blocks), 63 - msbIdx))
+ static if (isShared)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+ }
+
+ return alignedAllocateImpl(n, a);
+ }
+
+ // If shared, this is protected by a lock inside 'alignedAllocate'
+ private void[] alignedAllocateImpl(size_t n, uint a)
+ {
+ import std.math.traits : isPowerOf2;
+ assert(a.isPowerOf2);
+ if (a <= alignment) return allocate(n);
+
+ // Overallocate to make sure we can get an aligned block
+ auto b = allocateImpl((n + a - alignment).roundUpToMultipleOf(blockSize));
+ if (!b.ptr) return null;
+ auto result = b.roundStartToMultipleOf(a);
+ assert(result.length >= n);
+ result = result.ptr[0 .. n]; // final result
+
+ // Free any blocks that might be slack at the beginning
+ auto slackHeadingBlocks = (result.ptr - b.ptr) / blockSize;
+ if (slackHeadingBlocks)
+ {
+ deallocateImpl(b[0 .. slackHeadingBlocks * blockSize]);
+ }
+
+ // Free any blocks that might be slack at the end
+ auto slackTrailingBlocks = ((b.ptr + b.length)
+ - (result.ptr + result.length)) / blockSize;
+ if (slackTrailingBlocks)
+ {
+ deallocateImpl(b[$ - slackTrailingBlocks * blockSize .. $]);
+ }
+
+ return result;
+ }
+
+ /*
+ Tries to allocate "blocks" blocks at the exact position indicated by the
+ position wordIdx/msbIdx (msbIdx counts from MSB, i.e. MSB has index 0). If
+ it succeeds, fills "result" with the result and returns tuple(size_t.max,
+ 0). Otherwise, returns a tuple with the next position to search.
+ */
+ private Tuple!(size_t, uint) allocateAt(size_t wordIdx, uint msbIdx,
+ size_t blocks, ref void[] result)
+ {
+ assert(blocks > 0);
+ assert(wordIdx < _control.rep.length);
+ assert(msbIdx <= 63);
+ void[] tmpResult;
+ result = null;
+ if (msbIdx + blocks <= 64)
+ {
+ // Allocation should fit this control word
+ static if (isShared)
+ {
+ ulong localControl = _control.rep[wordIdx];
+ bool didSetBit = setBitsIfZero(localControl,
+ cast(uint) (64 - msbIdx - blocks), 63 - msbIdx);
+ _control.rep[wordIdx] = localControl;
+ }
+ else
+ {
+ bool didSetBit = setBitsIfZero(_control.rep[wordIdx],
+ cast(uint) (64 - msbIdx - blocks), 63 - msbIdx);
+ }
+ if (didSetBit)
+ {
+ tmpResult = blocksFor(wordIdx, msbIdx, blocks);
+ if (!tmpResult)
+ {
+ static if (isShared)
+ {
+ localControl = _control.rep[wordIdx];
+ resetBits(localControl,
+ cast(uint) (64 - msbIdx - blocks), 63 - msbIdx);
+ _control.rep[wordIdx] = localControl;
+ }
+ else
+ {
+ resetBits(_control.rep[wordIdx],
+ cast(uint) (64 - msbIdx - blocks), 63 - msbIdx);
+ }
+ return tuple(size_t.max - 1, 0u);
+ }
+ result = tmpResult;
+ tmpResult = null;
+ return tuple(size_t.max, 0u);
+ }
+ // Can't allocate, make a suggestion
+ return msbIdx + blocks == 64
+ ? tuple(wordIdx + 1, 0u)
+ : tuple(wordIdx, cast(uint) (msbIdx + blocks));
+ }
+ // Allocation spans two control words or more
+ immutable mask = ulong.max >> msbIdx;
+ if (_control.rep[wordIdx] & mask)
+ {
+ // We can't allocate the rest of this control word,
+ // return a suggestion.
+ return tuple(wordIdx + 1, 0u);
+ }
+ // We can allocate the rest of this control word, but we first need to
+ // make sure we can allocate the tail.
+ if (wordIdx + 1 == _control.rep.length)
+ {
+ // No more memory
+ return tuple(_control.rep.length, 0u);
+ }
+ auto hint = allocateAt(wordIdx + 1, 0, blocks - 64 + msbIdx, result);
+ if (hint[0] == size_t.max)
{
- // Success
- result = blocksFor(wordIdx, msbIdx, blocks);
+ tmpResult = blocksFor(wordIdx, msbIdx, blocks);
+ if (!tmpResult)
+ {
+ return tuple(size_t.max - 1, 0u);
+ }
+ static if (isShared)
+ {
+ // Dont want atomics, because this is protected by 'lock'
+ ulong localControl = _control.rep[wordIdx];
+ localControl |= mask;
+ _control.rep[wordIdx] = localControl;
+ }
+ else
+ {
+ _control.rep[wordIdx] |= mask;
+ }
+ result = tmpResult;
+ tmpResult = null;
return tuple(size_t.max, 0u);
}
- // Can't allocate, make a suggestion
- return msbIdx + blocks == 64
- ? tuple(wordIdx + 1, 0u)
- : tuple(wordIdx, cast(uint) (msbIdx + blocks));
+ // Failed, return a suggestion that skips this whole run.
+ return hint;
}
- // Allocation spans two control words or more
- immutable mask = ulong.max >> msbIdx;
- if (_control.rep[wordIdx] & mask)
+
+ /* Allocates as many blocks as possible at the end of the blocks indicated
+ by wordIdx. Returns the number of blocks allocated. */
+ private uint allocateAtTail(size_t wordIdx)
{
- // We can't allocate the rest of this control word,
- // return a suggestion.
- return tuple(wordIdx + 1, 0u);
+ assert(wordIdx < _control.rep.length);
+ const available = trailingZeros(_control.rep[wordIdx]);
+ static if (isShared)
+ {
+ ulong localControl = _control.rep[wordIdx];
+ localControl |= ulong.max >> available;
+ _control.rep[wordIdx] = localControl;
+ }
+ else
+ {
+ _control.rep[wordIdx] |= ulong.max >> available;
+ }
+ return available;
}
- // We can allocate the rest of this control word, but we first need to
- // make sure we can allocate the tail.
- if (wordIdx + 1 == _control.rep.length)
+
+ pure nothrow @safe @nogc
+ private void[] smallAlloc(uint blocks) return scope
{
- // No more memory
- return tuple(_control.rep.length, 0u);
+ assert(blocks >= 2 && blocks <= 64);
+ void[] result;
+ foreach (i; _startIdx .. _control.rep.length)
+ {
+ // Test within the current 64-bit word
+ const v = _control.rep[i];
+ if (v == ulong.max) continue;
+ auto j = findContigOnes(~v, blocks);
+ if (j < 64)
+ {
+ // yay, found stuff
+ result = blocksFor(i, j, blocks);
+ if (result)
+ {
+ static if (isShared)
+ {
+ ulong localControl = _control.rep[i];
+ setBits(localControl, 64 - j - blocks, 63 - j);
+ _control.rep[i] = localControl;
+ }
+ else
+ {
+ setBits(_control.rep[i], 64 - j - blocks, 63 - j);
+ }
+ }
+ return result;
+ }
+ // Next, try allocations that cross a word
+ auto available = trailingZeros(v);
+ if (available == 0) continue;
+ if (i + 1 >= _control.rep.length) break;
+ assert(available < blocks); // otherwise we should have found it
+ auto needed = blocks - available;
+ assert(needed > 0 && needed < 64);
+ result = blocksFor(i, 64 - available, blocks);
+ if (result && allocateAtFront(i + 1, needed))
+ {
+ static if (isShared)
+ {
+ ulong localControl = _control.rep[i];
+ localControl |= (1UL << available) - 1;
+ _control.rep[i] = localControl;
+ }
+ else
+ {
+ _control.rep[i] |= (1UL << available) - 1;
+ }
+ return result;
+ }
+ }
+ return null;
}
- auto hint = allocateAt(wordIdx + 1, 0, blocks - 64 + msbIdx, result);
- if (hint[0] == size_t.max)
+
+ pure nothrow @trusted @nogc
+ private void[] hugeAlloc(size_t blocks) return scope
{
- // We did it!
- _control.rep[wordIdx] |= mask;
- result = blocksFor(wordIdx, msbIdx, blocks);
- return tuple(size_t.max, 0u);
+ assert(blocks > 64);
+ if (_startIdx == _control._rep.length)
+ {
+ assert((cast(BitVector) _control).allAre1);
+ return null;
+ }
+
+ auto i = (cast(BitVector)_control).findZeros(blocks, _startIdx * 64);
+ if (i == i.max || i + blocks > _blocks) return null;
+ // Allocate those bits
+ (cast(BitVector) _control)[i .. i + blocks] = 1;
+ return cast(void[]) _payload[cast(size_t) (i * blockSize)
+ .. cast(size_t) ((i + blocks) * blockSize)];
}
- // Failed, return a suggestion that skips this whole run.
- return hint;
- }
- /* Allocates as many blocks as possible at the end of the blocks indicated
- by wordIdx. Returns the number of blocks allocated. */
- private uint allocateAtTail(size_t wordIdx)
- {
- assert(wordIdx < _control.rep.length);
- const available = trailingZeros(_control.rep[wordIdx]);
- _control.rep[wordIdx] |= ulong.max >> available;
- return available;
- }
+ // Rounds sizeInBytes to a multiple of blockSize.
+ private size_t bytes2blocks(size_t sizeInBytes)
+ {
+ return (sizeInBytes + blockSize - 1) / blockSize;
+ }
- private void[] smallAlloc(uint blocks)
- {
- assert(blocks >= 2 && blocks <= 64, text(blocks));
- foreach (i; _startIdx .. _control.rep.length)
- {
- // Test within the current 64-bit word
- const v = _control.rep[i];
- if (v == ulong.max) continue;
- auto j = findContigOnes(~v, blocks);
- if (j < 64)
+ /* Allocates given blocks at the beginning blocks indicated by wordIdx.
+ Returns true if allocation was possible, false otherwise. */
+ private bool allocateAtFront(size_t wordIdx, uint blocks)
+ {
+ assert(wordIdx < _control.rep.length && blocks >= 1 && blocks <= 64);
+ const mask = (1UL << (64 - blocks)) - 1;
+ if (_control.rep[wordIdx] > mask) return false;
+ static if (isShared)
{
- // yay, found stuff
- setBits(_control.rep[i], 64 - j - blocks, 63 - j);
- return blocksFor(i, j, blocks);
+ ulong localControl = _control.rep[wordIdx];
+ localControl |= ~mask;
+ _control.rep[wordIdx] = localControl;
}
- // Next, try allocations that cross a word
- auto available = trailingZeros(v);
- if (available == 0) continue;
- if (i + 1 >= _control.rep.length) break;
- assert(available < blocks); // otherwise we should have found it
- auto needed = blocks - available;
- assert(needed > 0 && needed < 64);
- if (allocateAtFront(i + 1, needed))
+ else
{
- // yay, found a block crossing two words
- _control.rep[i] |= (1UL << available) - 1;
- return blocksFor(i, 64 - available, blocks);
+ _control.rep[wordIdx] |= ~mask;
}
+ return true;
}
- return null;
- }
- private void[] hugeAlloc(size_t blocks)
- {
- assert(blocks > 64);
- if (_startIdx == _control._rep.length)
+ // Since the lock is not pure, only the single threaded 'expand' is pure
+ static if (isShared)
{
- assert(_control.allAre1);
- return null;
+ nothrow @trusted @nogc
+ bool expand(ref void[] b, immutable size_t delta)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return expandImpl(b, delta);
+ }
+ }
+ else
+ {
+ pure nothrow @trusted @nogc
+ bool expand(ref void[] b, immutable size_t delta)
+ {
+ return expandImpl(b, delta);
+ }
}
- auto i = _control.findZeros(blocks, _startIdx * 64);
- if (i == i.max) return null;
- // Allocate those bits
- _control[i .. i + blocks] = 1;
- return _payload[cast(size_t) (i * blockSize)
- .. cast(size_t) ((i + blocks) * blockSize)];
- }
- // Rounds sizeInBytes to a multiple of blockSize.
- private size_t bytes2blocks(size_t sizeInBytes)
- {
- return (sizeInBytes + blockSize - 1) / blockSize;
- }
+ // If shared, this is protected by a lock inside 'expand'
+ pure nothrow @trusted @nogc
+ private bool expandImpl(ref void[] b, immutable size_t delta)
+ {
+ // Dispose with trivial corner cases
+ if (b is null || delta == 0) return delta == 0;
- /* Allocates given blocks at the beginning blocks indicated by wordIdx.
- Returns true if allocation was possible, false otherwise. */
- private bool allocateAtFront(size_t wordIdx, uint blocks)
- {
- assert(wordIdx < _control.rep.length && blocks >= 1 && blocks <= 64);
- const mask = (1UL << (64 - blocks)) - 1;
- if (_control.rep[wordIdx] > mask) return false;
- // yay, works
- _control.rep[wordIdx] |= ~mask;
- return true;
- }
+ /* To simplify matters, refuse to expand buffers that don't start at a block start (this may be the case for blocks allocated with alignedAllocate).
+ */
+ if ((b.ptr - _payload.ptr) % blockSize) return false;
- /**
- Expands an allocated block in place.
- */
- @trusted bool expand(ref void[] b, immutable size_t delta)
- {
- // Dispose with trivial corner cases
- if (delta == 0) return true;
- if (b is null) return false;
+ const blocksOld = bytes2blocks(b.length);
+ const blocksNew = bytes2blocks(b.length + delta);
+ assert(blocksOld <= blocksNew);
- /* To simplify matters, refuse to expand buffers that don't start at a block start (this may be the case for blocks allocated with alignedAllocate).
- */
- if ((b.ptr - _payload.ptr) % blockSize) return false;
+ // Possibly we have enough slack at the end of the block!
+ if (blocksOld == blocksNew)
+ {
+ b = b.ptr[0 .. b.length + delta];
+ return true;
+ }
- const blocksOld = bytes2blocks(b.length);
- const blocksNew = bytes2blocks(b.length + delta);
- assert(blocksOld <= blocksNew);
+ assert((b.ptr - _payload.ptr) % blockSize == 0);
+ const blockIdx = (b.ptr - _payload.ptr) / blockSize;
+ const blockIdxAfter = blockIdx + blocksOld;
- // Possibly we have enough slack at the end of the block!
- if (blocksOld == blocksNew)
- {
+ // Try the maximum
+ const wordIdx = blockIdxAfter / 64,
+ msbIdx = cast(uint) (blockIdxAfter % 64);
+ void[] p;
+ auto hint = allocateAt(wordIdx, msbIdx, blocksNew - blocksOld, p);
+ if (hint[0] != size_t.max)
+ {
+ return false;
+ }
+ // Expansion successful
+ assert(p.ptr == b.ptr + blocksOld * blockSize);
b = b.ptr[0 .. b.length + delta];
+ adjustFreshBit(blockIdx + blocksNew);
return true;
}
- assert((b.ptr - _payload.ptr) % blockSize == 0);
- const blockIdx = (b.ptr - _payload.ptr) / blockSize;
- const blockIdxAfter = blockIdx + blocksOld;
+ @system bool reallocate(ref void[] b, size_t newSize)
+ {
+ static if (isShared)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+ }
+
+ return reallocateImpl(b, newSize);
+ }
- // Try the maximum
- const wordIdx = blockIdxAfter / 64,
- msbIdx = cast(uint) (blockIdxAfter % 64);
- void[] p;
- auto hint = allocateAt(wordIdx, msbIdx, blocksNew - blocksOld, p);
- if (hint[0] != size_t.max)
+ // If shared, this is protected by a lock inside 'reallocate'
+ private @system bool reallocateImpl(ref void[] b, size_t newSize)
{
- return false;
+ static bool slowReallocate(Allocator)(ref Allocator a, ref void[] b, size_t s)
+ {
+ if (b.length == s) return true;
+ if (b.length <= s && a.expandImpl(b, s - b.length)) return true;
+ auto newB = a.allocateImpl(s);
+ if (newB.length != s) return false;
+ if (newB.length <= b.length) newB[] = b[0 .. newB.length];
+ else newB[0 .. b.length] = b[];
+ a.deallocateImpl(b);
+ b = newB;
+ return true;
+ }
+
+ if (!b.ptr)
+ {
+ b = allocateImpl(newSize);
+ return b.length == newSize;
+ }
+ if (newSize == 0)
+ {
+ deallocateImpl(b);
+ b = null;
+ return true;
+ }
+ if (newSize < b.length)
+ {
+ // Shrink. Will shrink in place by deallocating the trailing part.
+ auto newCapacity = bytes2blocks(newSize) * blockSize;
+ deallocateImpl(b[newCapacity .. $]);
+ b = b[0 .. newSize];
+ return true;
+ }
+ // Go the slow route
+ return slowReallocate(this, b, newSize);
}
- // Expansion successful
- assert(p.ptr == b.ptr + blocksOld * blockSize,
- text(p.ptr, " != ", b.ptr + blocksOld * blockSize));
- b = b.ptr[0 .. b.length + delta];
- return true;
- }
- /**
- Reallocates a previously-allocated block. Contractions occur in place.
- */
- @system bool reallocate(ref void[] b, size_t newSize)
- {
- if (!b.ptr)
+ @system bool alignedReallocate(ref void[] b, size_t newSize, uint a)
{
- b = allocate(newSize);
- return b.length == newSize;
+ static if (isShared)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+ }
+
+ return alignedReallocateImpl(b, newSize, a);
}
- if (newSize == 0)
+
+ // If shared, this is protected by a lock inside 'alignedReallocate'
+ private @system bool alignedReallocateImpl(ref void[] b, size_t newSize, uint a)
{
- deallocate(b);
- b = null;
- return true;
+ static bool slowAlignedReallocate(Allocator)(ref Allocator alloc,
+ ref void[] b, size_t s, uint a)
+ {
+ if (b.length <= s && b.ptr.alignedAt(a)
+ && alloc.expandImpl(b, s - b.length)) return true;
+
+ auto newB = alloc.alignedAllocateImpl(s, a);
+ if (newB.length != s) return false;
+ if (newB.length <= b.length) newB[] = b[0 .. newB.length];
+ else newB[0 .. b.length] = b[];
+ alloc.deallocateImpl(b);
+ b = newB;
+ return true;
+ }
+
+ if (newSize == 0)
+ {
+ deallocateImpl(b);
+ b = null;
+ return true;
+ }
+ // Go the slow route
+ return slowAlignedReallocate(this, b, newSize, a);
}
- if (newSize < b.length)
+
+ nothrow @nogc
+ bool deallocate(void[] b)
{
- // Shrink. Will shrink in place by deallocating the trailing part.
- auto newCapacity = bytes2blocks(newSize) * blockSize;
- deallocate(b[newCapacity .. $]);
- b = b[0 .. newSize];
- return true;
+ static if (isShared)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+ }
+
+ return deallocateImpl(b);
}
- // Go the slow route
- return .reallocate(this, b, newSize);
- }
- /**
- Reallocates a block previously allocated with $(D alignedAllocate). Contractions do not occur in place.
- */
- @system bool alignedReallocate(ref void[] b, size_t newSize, uint a)
- {
- if (newSize == 0)
+ // If shared, this is protected by a lock inside 'deallocate'
+ nothrow @nogc
+ private bool deallocateImpl(void[] b)
{
- deallocate(b);
- b = null;
+ if (b is null) return true;
+
+ // Locate position
+ immutable pos = b.ptr - _payload.ptr;
+ immutable blockIdx = pos / blockSize;
+
+ // Adjust pointer, might be inside a block due to alignedAllocate
+ void* begin = cast(void*) (_payload.ptr + blockIdx * blockSize),
+ end = cast(void*) (b.ptr + b.length);
+ b = begin[0 .. end - begin];
+ // Round up size to multiple of block size
+ auto blocks = b.length.divideRoundUp(blockSize);
+
+ // Get into details
+ auto wordIdx = blockIdx / 64, msbIdx = cast(uint) (blockIdx % 64);
+ if (_startIdx > wordIdx) _startIdx = wordIdx;
+
+ // Three stages: heading bits, full words, leftover bits
+ if (msbIdx)
+ {
+ if (blocks + msbIdx <= 64)
+ {
+ static if (isShared)
+ {
+ ulong localControl = _control.rep[wordIdx];
+ resetBits(localControl,
+ cast(uint) (64 - msbIdx - blocks),
+ 63 - msbIdx);
+ _control.rep[wordIdx] = localControl;
+ }
+ else
+ {
+ resetBits(_control.rep[wordIdx],
+ cast(uint) (64 - msbIdx - blocks),
+ 63 - msbIdx);
+ }
+ return true;
+ }
+ else
+ {
+ static if (isShared)
+ {
+ ulong localControl = _control.rep[wordIdx];
+ localControl &= ulong.max << (64 - msbIdx);
+ _control.rep[wordIdx] = localControl;
+ }
+ else
+ {
+ _control.rep[wordIdx] &= ulong.max << (64 - msbIdx);
+ }
+ blocks -= 64 - msbIdx;
+ ++wordIdx;
+ msbIdx = 0;
+ }
+ }
+
+ // Stage 2: reset one word at a time
+ for (; blocks >= 64; blocks -= 64)
+ {
+ _control.rep[wordIdx++] = 0;
+ }
+
+ // Stage 3: deal with leftover bits, if any
+ assert(wordIdx <= _control.rep.length);
+ if (blocks)
+ {
+ static if (isShared)
+ {
+ ulong localControl = _control.rep[wordIdx];
+ localControl &= ulong.max >> blocks;
+ _control.rep[wordIdx] = localControl;
+ }
+ else
+ {
+ _control.rep[wordIdx] &= ulong.max >> blocks;
+ }
+ }
return true;
}
- // Go the slow route
- return .alignedReallocate(this, b, newSize, a);
- }
- /**
- Deallocates a block previously allocated with this allocator.
- */
- bool deallocate(void[] b)
+ // Since the lock is not pure, only the single threaded version is pure
+ static if (isShared)
+ {
+ nothrow @nogc
+ bool deallocateAll()
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ (cast(BitVector) _control)[] = 0;
+ _startIdx = 0;
+ return true;
+ }
+ }
+ else
+ {
+ pure nothrow @nogc
+ bool deallocateAll()
+ {
+ _control[] = 0;
+ _startIdx = 0;
+ return true;
+ }
+ }
+
+ // Since the lock is not pure, only the single threaded version is pure
+ static if (isShared)
+ {
+ nothrow @safe @nogc
+ Ternary empty()
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ return emptyImpl();
+ }
+ }
+ else
+ {
+ pure nothrow @safe @nogc
+ Ternary empty()
+ {
+ return Ternary(_control.allAre0());
+ }
+ }
+
+ pure nothrow @trusted @nogc
+ private Ternary emptyImpl()
+ {
+ return Ternary((cast(BitVector) _control).allAre0());
+ }
+
+ // Debug helper
+ debug(StdBitmapped)
+ private void dump()
+ {
+ import std.stdio : writefln, writeln;
+
+ ulong controlLen = (cast(BitVector) _control).length;
+ writefln("%s @ %s {", typeid(this), cast(void*) (cast(BitVector) _control)._rep.ptr);
+ scope(exit) writeln("}");
+ assert(_payload.length >= blockSize * _blocks);
+ assert(controlLen >= _blocks);
+ writefln(" _startIdx=%s; blockSize=%s; blocks=%s",
+ _startIdx, blockSize, _blocks);
+ if (!controlLen) return;
+ uint blockCount = 1;
+ bool inAllocatedStore = (cast(BitVector) _control)[0];
+ void* start = cast(void*) _payload.ptr;
+ for (size_t i = 1;; ++i)
+ {
+ if (i >= _blocks || (cast(BitVector) _control)[i] != inAllocatedStore)
+ {
+ writefln(" %s block at 0x%s, length: %s (%s*%s)",
+ inAllocatedStore ? "Busy" : "Free",
+ cast(void*) start,
+ blockCount * blockSize,
+ blockCount, blockSize);
+ if (i >= _blocks) break;
+ assert(i < controlLen);
+ inAllocatedStore = (cast(BitVector) _control)[i];
+ start = cast(void*) (_payload.ptr + blockCount * blockSize);
+ blockCount = 1;
+ }
+ else
+ {
+ ++blockCount;
+ }
+ }
+ }
+
+ void[] allocateAll() return scope
+ {
+ static if (isShared)
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+ }
+
+ if (emptyImpl != Ternary.yes) return null;
+ (cast(BitVector) _control)[] = 1;
+ return cast(void[]) _payload;
+ }
+ } // Finish Yes.multiblock implementation specifics
+ else
{
- if (b is null) return true;
+ static if (isShared)
+ pure nothrow @trusted @nogc
+ void[] allocate(const size_t s)
+ {
+ import core.atomic : cas, atomicLoad, atomicOp;
+ import core.bitop : bsr;
+ import std.range : iota;
+ import std.algorithm.iteration : map;
+ import std.array : array;
- // Locate position
- immutable pos = b.ptr - _payload.ptr;
- immutable blockIdx = pos / blockSize;
+ if (s.divideRoundUp(blockSize) != 1)
+ return null;
- // Adjust pointer, might be inside a block due to alignedAllocate
- auto begin = _payload.ptr + blockIdx * blockSize,
- end = b.ptr + b.length;
- b = begin[0 .. end - begin];
- // Round up size to multiple of block size
- auto blocks = b.length.divideRoundUp(blockSize);
+ // First zero bit position for all values in the 0 - 255 range
+ // for fast lookup
+ static immutable ubyte[255] firstZero = iota(255U).map!
+ (x => (7 - (bsr((~x) & 0x000000ff)))).array;
- // Get into details
- auto wordIdx = blockIdx / 64, msbIdx = cast(uint) (blockIdx % 64);
- if (_startIdx > wordIdx) _startIdx = wordIdx;
+ foreach (size_t i; 0 .. _control.length)
+ {
+ ulong controlVal, newControlVal, bitIndex;
+ do
+ {
+ bitIndex = 0;
+ newControlVal = 0;
+ controlVal = atomicLoad(_control[i]);
+
+ // skip all control words which have all bits set
+ if (controlVal == ulong.max)
+ break;
+
+ // fast lookup of first byte which has at least one zero bit
+ foreach (byteIndex; 0 .. 8)
+ {
+ ulong mask = (0xFFUL << (8 * (7 - byteIndex)));
+ if ((mask & controlVal) != mask)
+ {
+ ubyte byteVal = cast(ubyte) ((mask & controlVal) >> (8 * (7 - byteIndex)));
+ bitIndex += firstZero[byteVal];
+ newControlVal = controlVal | (1UL << (63 - bitIndex));
+ break;
+ }
+ bitIndex += 8;
+ }
+ } while (!cas(&_control[i], controlVal, newControlVal));
+
+ auto blockIndex = bitIndex + 64 * i;
+ if (controlVal != ulong.max && blockIndex < _blocks)
+ {
+ size_t payloadBlockStart = cast(size_t) blockIndex * blockSize;
+ return cast(void[]) _payload[payloadBlockStart .. payloadBlockStart + s];
+ }
+ }
- // Three stages: heading bits, full words, leftover bits
- if (msbIdx)
+ return null;
+ }
+
+ static if (!isShared)
+ pure nothrow @trusted @nogc
+ void[] allocate(const size_t s)
{
- if (blocks + msbIdx <= 64)
+ import core.bitop : bsr;
+ import std.range : iota;
+ import std.algorithm.iteration : map;
+ import std.array : array;
+
+ if (s.divideRoundUp(blockSize) != 1)
+ return null;
+
+ // First zero bit position for all values in the 0 - 255 range
+ // for fast lookup
+ static immutable ubyte[255] firstZero = iota(255U).map!
+ (x => (7 - (bsr((~x) & 0x000000ff)))).array;
+
+ _startIdx = (_startIdx + 1) % _control.length;
+ foreach (size_t idx; 0 .. _control.length)
{
- resetBits(_control.rep[wordIdx],
- cast(uint) (64 - msbIdx - blocks),
- 63 - msbIdx);
+ size_t i = (idx + _startIdx) % _control.length;
+ size_t bitIndex = 0;
+ // skip all control words which have all bits set
+ if (_control[i] == ulong.max)
+ continue;
+
+ // fast lookup of first byte which has at least one zero bit
+ foreach (byteIndex; 0 .. 8)
+ {
+ ulong mask = (0xFFUL << (8 * (7 - byteIndex)));
+ if ((mask & _control[i]) != mask)
+ {
+ ubyte byteVal = cast(ubyte) ((mask & _control[i]) >> (8 * (7 - byteIndex)));
+ bitIndex += firstZero[byteVal];
+ _control[i] |= (1UL << (63 - bitIndex));
+ break;
+ }
+ bitIndex += 8;
+ }
+
+ auto blockIndex = bitIndex + 64 * i;
+ if (blockIndex < _blocks)
+ {
+ size_t payloadBlockStart = cast(size_t) blockIndex * blockSize;
+ return cast(void[]) _payload[payloadBlockStart .. payloadBlockStart + s];
+ }
+ }
+
+ return null;
+ }
+
+ nothrow @nogc
+ bool deallocate(void[] b)
+ {
+ static if (isShared)
+ import core.atomic : atomicOp;
+
+ if (b is null)
return true;
+
+ auto blockIndex = (b.ptr - _payload.ptr) / blockSize;
+ auto controlIndex = blockIndex / 64;
+ auto bitIndex = blockIndex % 64;
+ static if (isShared)
+ {
+ atomicOp!"&="(_control[controlIndex], ~(1UL << (63 - bitIndex)));
}
else
{
- _control.rep[wordIdx] &= ulong.max << 64 - msbIdx;
- blocks -= 64 - msbIdx;
- ++wordIdx;
- msbIdx = 0;
+ _control[controlIndex] &= ~(1UL << (63 - bitIndex));
}
- }
- // Stage 2: reset one word at a time
- for (; blocks >= 64; blocks -= 64)
- {
- _control.rep[wordIdx++] = 0;
+ return true;
}
- // Stage 3: deal with leftover bits, if any
- assert(wordIdx <= _control.rep.length);
- if (blocks)
+ pure nothrow @trusted @nogc
+ bool expand(ref void[] b, immutable size_t delta)
{
- _control.rep[wordIdx] &= ulong.max >> blocks;
+ if (delta == 0)
+ return true;
+
+ immutable newLength = delta + b.length;
+ if (b is null || newLength > blockSize)
+ return false;
+
+ b = b.ptr[0 .. newLength];
+ return true;
}
- return true;
- }
+ } // Finish No.multiblock implementation specifics
- /**
- Forcibly deallocates all memory allocated by this allocator, making it
- available for further allocations. Does not return memory to $(D
- ParentAllocator).
- */
- bool deallocateAll()
+ pure nothrow @trusted @nogc
+ Ternary owns(const void[] b) const
{
- _control[] = 0;
- _startIdx = 0;
- return true;
+ assert(b || b.length == 0, "Corrupt block.");
+ return Ternary(b && _payload && (&b[0] >= &_payload[0])
+ && (&b[0] + b.length) <= (&_payload[0] + _payload.length));
}
+}
- /**
- Returns `Ternary.yes` if no memory is currently allocated with this
- allocator, otherwise `Ternary.no`. This method never returns
- `Ternary.unknown`.
- */
- Ternary empty()
+/**
+`BitmappedBlock` implements a simple heap consisting of one contiguous area
+of memory organized in blocks, each of size `theBlockSize`. A block is a unit
+of allocation. A bitmap serves as bookkeeping data, more precisely one bit per
+block indicating whether that block is currently allocated or not.
+
+Passing `NullAllocator` as `ParentAllocator` (the default) means user code
+manages allocation of the memory block from the outside; in that case
+`BitmappedBlock` must be constructed with a `ubyte[]` preallocated block and
+has no responsibility regarding the lifetime of its support underlying storage.
+If another allocator type is passed, `BitmappedBlock` defines a destructor that
+uses the parent allocator to release the memory block. That makes the combination of `AllocatorList`,
+`BitmappedBlock`, and a back-end allocator such as `MmapAllocator`
+a simple and scalable solution for memory allocation.
+
+There are advantages to storing bookkeeping data separated from the payload
+(as opposed to e.g. using `AffixAllocator` to store metadata together with
+each allocation). The layout is more compact (overhead is one bit per block),
+searching for a free block during allocation enjoys better cache locality, and
+deallocation does not touch memory around the payload being deallocated (which
+is often cold).
+
+Allocation requests are handled on a first-fit basis. Although linear in
+complexity, allocation is in practice fast because of the compact bookkeeping
+representation, use of simple and fast bitwise routines, and caching of the
+first available block position. A known issue with this general approach is
+fragmentation, partially mitigated by coalescing. Since `BitmappedBlock` does
+not need to maintain the allocated size, freeing memory implicitly coalesces
+free blocks together. Also, tuning `blockSize` has a considerable impact on
+both internal and external fragmentation.
+
+If the last template parameter is set to `No.multiblock`, the allocator will only serve
+allocations which require at most `theBlockSize`. The `BitmappedBlock` has a specialized
+implementation for single-block allocations which allows for greater performance,
+at the cost of not being able to allocate more than one block at a time.
+
+The size of each block can be selected either during compilation or at run
+time. Statically-known block sizes are frequent in practice and yield slightly
+better performance. To choose a block size statically, pass it as the `blockSize`
+parameter as in `BitmappedBlock!(4096)`. To choose a block
+size parameter, use `BitmappedBlock!(chooseAtRuntime)` and pass the
+block size to the constructor.
+
+Params:
+ theBlockSize = the length of a block, which must be a multiple of `theAlignment`
+
+ theAlignment = alignment of each block
+
+ ParentAllocator = allocator from which the `BitmappedBlock` will draw memory.
+ If set to `NullAllocator`, the storage must be passed via the constructor
+
+ f = `Yes.multiblock` to support allocations spanning across multiple blocks and
+ `No.multiblock` to support single block allocations.
+ Although limited by single block allocations, `No.multiblock` will generally
+ provide higher performance.
+*/
+struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
+ ParentAllocator = NullAllocator, Flag!"multiblock" f = Yes.multiblock)
+{
+ version (StdDdoc)
{
- return Ternary(_control.allAre0());
- }
+ /**
+ Constructs a block allocator given a hunk of memory, or a desired capacity
+ in bytes.
+ $(UL
+ $(LI If `ParentAllocator` is $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator),
+ only the constructor taking `data` is defined and the user is responsible for freeing `data` if desired.)
+ $(LI Otherwise, both constructors are defined. The `data`-based
+ constructor assumes memory has been allocated with the parent allocator.
+ The `capacity`-based constructor uses `ParentAllocator` to allocate
+ an appropriate contiguous hunk of memory. Regardless of the constructor
+ used, the destructor releases the memory by using `ParentAllocator.deallocate`.)
+ )
+ */
+ this(ubyte[] data);
+
+ /// Ditto
+ this(ubyte[] data, uint blockSize);
+
+ /// Ditto
+ this(size_t capacity);
+
+ /// Ditto
+ this(ParentAllocator parent, size_t capacity);
+
+ /// Ditto
+ this(size_t capacity, uint blockSize);
+
+ /// Ditto
+ this(ParentAllocator parent, size_t capacity, uint blockSize);
+
+ /**
+ If `blockSize == chooseAtRuntime`, `BitmappedBlock` offers a read/write
+ property `blockSize`. It must be set before any use of the allocator.
+ Otherwise (i.e. `theBlockSize` is a legit constant), `blockSize` is
+ an alias for `theBlockSize`. Whether constant or variable, must also be
+ a multiple of `alignment`. This constraint is `assert`ed statically
+ and dynamically.
+ */
+ alias blockSize = theBlockSize;
+
+ /**
+ The _alignment offered is user-configurable statically through parameter
+ `theAlignment`, defaulted to `platformAlignment`.
+ */
+ alias alignment = theAlignment;
+
+ /**
+ The _parent allocator. Depending on whether `ParentAllocator` holds state
+ or not, this is a member variable or an alias for
+ `ParentAllocator.instance`.
+ */
+ ParentAllocator parent;
+
+ /**
+ Returns the actual bytes allocated when `n` bytes are requested, i.e.
+ `n.roundUpToMultipleOf(blockSize)`.
+ */
+ pure nothrow @safe @nogc
+ size_t goodAllocSize(size_t n);
+
+ /**
+ Returns `Ternary.yes` if `b` belongs to the `BitmappedBlock` object,
+ `Ternary.no` otherwise. Never returns `Ternary.unkown`. (This
+ method is somewhat tolerant in that accepts an interior slice.)
+ */
+ pure nothrow @trusted @nogc
+ Ternary owns(const void[] b) const;
+
+ /**
+ Expands in place a buffer previously allocated by `BitmappedBlock`.
+ If instantiated with `No.multiblock`, the expansion fails if the new length
+ exceeds `theBlockSize`.
+ */
+ pure nothrow @trusted @nogc
+ bool expand(ref void[] b, immutable size_t delta);
+
+ /**
+ Deallocates a block previously allocated with this allocator.
+ */
+ nothrow @nogc
+ bool deallocate(void[] b);
+
+ /**
+ Allocates `s` bytes of memory and returns it, or `null` if memory
+ could not be allocated.
+
+ The following information might be of help with choosing the appropriate
+ block size. Actual allocation occurs in sizes multiple of the block size.
+ Allocating one block is the fastest because only one 0 bit needs to be
+ found in the metadata. Allocating 2 through 64 blocks is the next cheapest
+ because it affects a maximum of two `ulong` in the metadata.
+ Allocations greater than 64 blocks require a multiword search through the
+ metadata.
+
+ If instantiated with `No.multiblock`, it performs a search for the first zero
+ bit in the bitmap and sets it.
+ */
+ pure nothrow @trusted @nogc
+ void[] allocate(const size_t s);
+
+ /**
+ Allocates s bytes of memory and returns it, or `null` if memory could not be allocated.
+ `allocateFresh` behaves just like allocate, the only difference being that this always
+ returns unused(fresh) memory. Although there may still be available space in the `BitmappedBlock`,
+ `allocateFresh` could still return null, because all the available blocks have been previously deallocated.
+ */
+ @trusted void[] allocateFresh(const size_t s);
+
+ /**
+ If the `BitmappedBlock` object is empty (has no active allocation), allocates
+ all memory within and returns a slice to it. Otherwise, returns `null`
+ (i.e. no attempt is made to allocate the largest available block).
+ */
+ void[] allocateAll();
+
+ /**
+ Returns `Ternary.yes` if no memory is currently allocated with this
+ allocator, otherwise `Ternary.no`. This method never returns
+ `Ternary.unknown`.
+ */
+ pure nothrow @safe @nogc
+ Ternary empty();
+
+ /**
+ Forcibly deallocates all memory allocated by this allocator, making it
+ available for further allocations. Does not return memory to `ParentAllocator`.
+ */
+ pure nothrow @nogc
+ bool deallocateAll();
+
+ /**
+ Reallocates a block previously allocated with `alignedAllocate`. Contractions do not occur in place.
+ */
+ @system bool alignedReallocate(ref void[] b, size_t newSize, uint a);
- void dump()
+ /**
+ Reallocates a previously-allocated block. Contractions occur in place.
+ */
+ @system bool reallocate(ref void[] b, size_t newSize);
+
+ /**
+ Allocates a block with specified alignment `a`. The alignment must be a
+ power of 2. If `a <= alignment`, function forwards to `allocate`.
+ Otherwise, it attempts to overallocate and then adjust the result for
+ proper alignment. In the worst case the slack memory is around two blocks.
+ */
+ void[] alignedAllocate(size_t n, uint a);
+
+ /**
+ If `ParentAllocator` is not `NullAllocator` and defines `deallocate`,
+ the destructor is defined to deallocate the block held.
+ */
+ ~this();
+ }
+ else
{
- import std.stdio : writefln, writeln;
- writefln("%s @ %s {", typeid(this), cast(void*) _control._rep.ptr);
- scope(exit) writeln("}");
- assert(_payload.length == blockSize * _blocks);
- assert(_control.length >= _blocks);
- writefln(" _startIdx=%s; blockSize=%s; blocks=%s",
- _startIdx, blockSize, _blocks);
- if (!_control.length) return;
- uint blockCount = 1;
- bool inAllocatedStore = _control[0];
- void* start = _payload.ptr;
- for (size_t i = 1;; ++i)
- {
- if (i >= _blocks || _control[i] != inAllocatedStore)
+ version (StdUnittest)
+ @system unittest
+ {
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.mallocator : AlignedMallocator;
+ auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64,
+ max(theAlignment, cast(uint) size_t.sizeof)));
+ scope(exit) () nothrow @nogc { AlignedMallocator.instance.deallocate(m); }();
+ static if (theBlockSize == chooseAtRuntime)
{
- writefln(" %s block at 0x%s, length: %s (%s*%s)",
- inAllocatedStore ? "Busy" : "Free",
- cast(void*) start,
- blockCount * blockSize,
- blockCount, blockSize);
- if (i >= _blocks) break;
- assert(i < _control.length);
- inAllocatedStore = _control[i];
- start = _payload.ptr + blockCount * blockSize;
- blockCount = 1;
+ testAllocator!(() => BitmappedBlock!(theBlockSize, theAlignment, NullAllocator)(m, 64));
}
else
{
- ++blockCount;
+ testAllocator!(() => BitmappedBlock!(theBlockSize, theAlignment, NullAllocator)(m));
}
}
+ mixin BitmappedBlockImpl!(false, f == Yes.multiblock);
}
}
@@ -702,77 +1400,493 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
assert(b.length == 100);
}
+///
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Flag, Yes;
+
+ enum blockSize = 64;
+ enum numBlocks = 10;
+
+ // The 'BitmappedBlock' is implicitly instantiated with Yes.multiblock
+ auto a = BitmappedBlock!(blockSize, 8, Mallocator, Yes.multiblock)(numBlocks * blockSize);
+
+ // Instantiated with Yes.multiblock, can allocate more than one block at a time
+ void[] buf = a.allocate(2 * blockSize);
+ assert(buf.length == 2 * blockSize);
+ assert(a.deallocate(buf));
+
+ // Can also allocate less than one block
+ buf = a.allocate(blockSize / 2);
+ assert(buf.length == blockSize / 2);
+
+ // Expands inside the same block
+ assert(a.expand(buf, blockSize / 2));
+ assert(buf.length == blockSize);
+
+ // If Yes.multiblock, can expand past the size of a single block
+ assert(a.expand(buf, 3 * blockSize));
+ assert(buf.length == 4 * blockSize);
+ assert(a.deallocate(buf));
+}
+
+///
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Flag, No;
+
+ enum blockSize = 64;
+ auto a = BitmappedBlock!(blockSize, 8, Mallocator, No.multiblock)(1024 * blockSize);
+
+ // Since instantiated with No.multiblock, can only allocate at most the block size
+ void[] buf = a.allocate(blockSize + 1);
+ assert(buf is null);
+
+ buf = a.allocate(blockSize);
+ assert(buf.length == blockSize);
+ assert(a.deallocate(buf));
+
+ // This is also fine, because it's less than the block size
+ buf = a.allocate(blockSize / 2);
+ assert(buf.length == blockSize / 2);
+
+ // Can expand the buffer until its length is at most 64
+ assert(a.expand(buf, blockSize / 2));
+ assert(buf.length == blockSize);
+
+ // Cannot expand anymore
+ assert(!a.expand(buf, 1));
+ assert(a.deallocate(buf));
+}
+
+// Test instantiation with stateful allocators
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.experimental.allocator.building_blocks.region : Region;
+ auto r = Region!Mallocator(1024 * 96);
+ auto a = BitmappedBlock!(chooseAtRuntime, 8, Region!Mallocator*, No.multiblock)(&r, 1024 * 64, 1024);
+}
+
+/**
+The threadsafe version of the $(LREF BitmappedBlock).
+The semantics of the `SharedBitmappedBlock` are identical to the regular $(LREF BitmappedBlock).
+
+Params:
+ theBlockSize = the length of a block, which must be a multiple of `theAlignment`
+
+ theAlignment = alignment of each block
+
+ ParentAllocator = allocator from which the `BitmappedBlock` will draw memory.
+ If set to `NullAllocator`, the storage must be passed via the constructor
+
+ f = `Yes.multiblock` to support allocations spanning across multiple blocks and
+ `No.multiblock` to support single block allocations.
+ Although limited by single block allocations, `No.multiblock` will generally
+ provide higher performance.
+*/
+shared struct SharedBitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
+ ParentAllocator = NullAllocator, Flag!"multiblock" f = Yes.multiblock)
+{
+ version (StdDdoc)
+ {
+ /**
+ Constructs a block allocator given a hunk of memory, or a desired capacity
+ in bytes.
+ $(UL
+ $(LI If `ParentAllocator` is $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator),
+ only the constructor taking `data` is defined and the user is responsible for freeing `data` if desired.)
+ $(LI Otherwise, both constructors are defined. The `data`-based
+ constructor assumes memory has been allocated with the parent allocator.
+ The `capacity`-based constructor uses `ParentAllocator` to allocate
+ an appropriate contiguous hunk of memory. Regardless of the constructor
+ used, the destructor releases the memory by using `ParentAllocator.deallocate`.)
+ )
+ */
+ this(ubyte[] data);
+
+ /// Ditto
+ this(ubyte[] data, uint blockSize);
+
+ /// Ditto
+ this(size_t capacity);
+
+ /// Ditto
+ this(ParentAllocator parent, size_t capacity);
+
+ /// Ditto
+ this(size_t capacity, uint blockSize);
+
+ /// Ditto
+ this(ParentAllocator parent, size_t capacity, uint blockSize);
+
+ /**
+ If `blockSize == chooseAtRuntime`, `SharedBitmappedBlock` offers a read/write
+ property `blockSize`. It must be set before any use of the allocator.
+ Otherwise (i.e. `theBlockSize` is a legit constant), `blockSize` is
+ an alias for `theBlockSize`. Whether constant or variable, must also be
+ a multiple of `alignment`. This constraint is `assert`ed statically
+ and dynamically.
+ */
+ alias blockSize = theBlockSize;
+
+ /**
+ The _alignment offered is user-configurable statically through parameter
+ `theAlignment`, defaulted to `platformAlignment`.
+ */
+ alias alignment = theAlignment;
+
+ /**
+ The _parent allocator. Depending on whether `ParentAllocator` holds state
+ or not, this is a member variable or an alias for
+ `ParentAllocator.instance`.
+ */
+ ParentAllocator parent;
+
+ /**
+ Returns the actual bytes allocated when `n` bytes are requested, i.e.
+ `n.roundUpToMultipleOf(blockSize)`.
+ */
+ pure nothrow @safe @nogc
+ size_t goodAllocSize(size_t n);
+
+ /**
+ Returns `Ternary.yes` if `b` belongs to the `SharedBitmappedBlock` object,
+ `Ternary.no` otherwise. Never returns `Ternary.unkown`. (This
+ method is somewhat tolerant in that accepts an interior slice.)
+ */
+ pure nothrow @trusted @nogc
+ Ternary owns(const void[] b) const;
+
+ /**
+ Expands in place a buffer previously allocated by `SharedBitmappedBlock`.
+ Expansion fails if the new length exceeds the block size.
+ */
+ bool expand(ref void[] b, immutable size_t delta);
+
+ /**
+ Deallocates the given buffer `b`, by atomically setting the corresponding
+ bit to `0`. `b` must be valid, and cannot contain multiple adjacent `blocks`.
+ */
+ nothrow @nogc
+ bool deallocate(void[] b);
+
+ /**
+ Allocates `s` bytes of memory and returns it, or `null` if memory
+ could not be allocated.
+
+ The `SharedBitmappedBlock` cannot allocate more than the given block size.
+ Allocations are satisfied by searching the first unset bit in the bitmap,
+ and atomically setting it.
+ In rare memory pressure scenarios, the allocation could fail.
+ */
+ nothrow @trusted @nogc
+ void[] allocate(const size_t s);
+
+ /**
+ Allocates s bytes of memory and returns it, or `null` if memory could not be allocated.
+ `allocateFresh` behaves just like allocate, the only difference being that this always
+ returns unused(fresh) memory. Although there may still be available space in the `SharedBitmappedBlock`,
+ `allocateFresh` could still return null, because all the available blocks have been previously deallocated.
+ */
+ @trusted void[] allocateFresh(const size_t s);
+
+ /**
+ If the `SharedBitmappedBlock` object is empty (has no active allocation), allocates
+ all memory within and returns a slice to it. Otherwise, returns `null`
+ (i.e. no attempt is made to allocate the largest available block).
+ */
+ void[] allocateAll();
+
+ /**
+ Returns `Ternary.yes` if no memory is currently allocated with this
+ allocator, otherwise `Ternary.no`. This method never returns
+ `Ternary.unknown`.
+ */
+ nothrow @safe @nogc
+ Ternary empty();
+
+ /**
+ Forcibly deallocates all memory allocated by this allocator, making it
+ available for further allocations. Does not return memory to `ParentAllocator`.
+ */
+ nothrow @nogc
+ bool deallocateAll();
+
+ /**
+ Reallocates a block previously allocated with `alignedAllocate`. Contractions do not occur in place.
+ */
+ @system bool alignedReallocate(ref void[] b, size_t newSize, uint a);
+
+ /**
+ Reallocates a previously-allocated block. Contractions occur in place.
+ */
+ @system bool reallocate(ref void[] b, size_t newSize);
+
+ /**
+ Allocates a block with specified alignment `a`. The alignment must be a
+ power of 2. If `a <= alignment`, function forwards to `allocate`.
+ Otherwise, it attempts to overallocate and then adjust the result for
+ proper alignment. In the worst case the slack memory is around two blocks.
+ */
+ void[] alignedAllocate(size_t n, uint a);
+
+ /**
+ If `ParentAllocator` is not `NullAllocator` and defines `deallocate`,
+ the destructor is defined to deallocate the block held.
+ */
+ ~this();
+ }
+ else
+ {
+ version (StdUnittest)
+ @system unittest
+ {
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.mallocator : AlignedMallocator;
+ auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64,
+ max(theAlignment, cast(uint) size_t.sizeof)));
+ scope(exit) () nothrow @nogc { AlignedMallocator.instance.deallocate(m); }();
+ static if (theBlockSize == chooseAtRuntime)
+ {
+ testAllocator!(() => SharedBitmappedBlock!(theBlockSize, theAlignment, NullAllocator)(m, 64));
+ }
+ else
+ {
+ testAllocator!(() => SharedBitmappedBlock!(theBlockSize, theAlignment, NullAllocator)(m));
+ }
+ }
+ mixin BitmappedBlockImpl!(true, f == Yes.multiblock);
+ }
+}
+
+///
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.experimental.allocator.common : platformAlignment;
+ import std.typecons : Flag, Yes, No;
+
+ // Create 'numThreads' threads, each allocating in parallel a chunk of memory
+ static void testAlloc(Allocator)(ref Allocator a, size_t allocSize)
+ {
+ import core.thread : ThreadGroup;
+ import std.algorithm.sorting : sort;
+ import core.internal.spinlock : SpinLock;
+
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+ enum numThreads = 10;
+ void[][numThreads] buf;
+ size_t count = 0;
+
+ // Each threads allocates 'allocSize'
+ void fun()
+ {
+ void[] b = a.allocate(allocSize);
+ assert(b.length == allocSize);
+
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ buf[count] = b;
+ count++;
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ // Sorting the allocations made by each thread, we expect the buffers to be
+ // adjacent inside the SharedBitmappedBlock
+ sort!((a, b) => a.ptr < b.ptr)(buf[0 .. numThreads]);
+ foreach (i; 0 .. numThreads - 1)
+ {
+ assert(buf[i].ptr + a.goodAllocSize(buf[i].length) <= buf[i + 1].ptr);
+ }
+
+ // Deallocate everything
+ foreach (i; 0 .. numThreads)
+ {
+ assert(a.deallocate(buf[i]));
+ }
+ }
+
+ enum blockSize = 64;
+ auto alloc1 = SharedBitmappedBlock!(blockSize, platformAlignment, Mallocator, Yes.multiblock)(1024 * 1024);
+ auto alloc2 = SharedBitmappedBlock!(blockSize, platformAlignment, Mallocator, No.multiblock)(1024 * 1024);
+ testAlloc(alloc1, 2 * blockSize);
+ testAlloc(alloc2, blockSize);
+}
+
+@system unittest
+{
+ // Test chooseAtRuntime
+ // Create a block allocator on top of a 10KB stack region.
+ import std.experimental.allocator.building_blocks.region : InSituRegion;
+ import std.traits : hasMember;
+ InSituRegion!(10_240, 64) r;
+ uint blockSize = 64;
+ auto a = BitmappedBlock!(chooseAtRuntime, 64)(cast(ubyte[])(r.allocateAll()), blockSize);
+ static assert(hasMember!(InSituRegion!(10_240, 64), "allocateAll"));
+ const b = (() pure nothrow @safe @nogc => a.allocate(100))();
+ assert(b.length == 100);
+}
+
+pure @safe unittest
+{
+ import std.typecons : Ternary;
+
+ auto a = (() @trusted => BitmappedBlock!(64, 64, NullAllocator, Yes.multiblock)(new ubyte[10_240]))();
+ () nothrow @nogc {
+ assert(a.empty == Ternary.yes);
+ const b = a.allocate(100);
+ assert(b.length == 100);
+ assert(a.empty == Ternary.no);
+ }();
+}
+
+@safe unittest
+{
+ import std.typecons : Ternary;
+
+ auto a = (() @trusted => SharedBitmappedBlock!(64, 64, NullAllocator, Yes.multiblock)(new ubyte[10_240]))();
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
+ const b = a.allocate(100);
+ assert(b.length == 100);
+}
+
+version (StdUnittest)
@system unittest
{
import std.experimental.allocator.gc_allocator : GCAllocator;
testAllocator!(() => BitmappedBlock!(64, 8, GCAllocator)(1024 * 64));
}
+version (StdUnittest)
+@system unittest
+{
+ // Test chooseAtRuntime
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ uint blockSize = 64;
+ testAllocator!(() => BitmappedBlock!(chooseAtRuntime, 8, GCAllocator, Yes.multiblock)(1024 * 64, blockSize));
+ testAllocator!(() => BitmappedBlock!(chooseAtRuntime, 8, GCAllocator, No.multiblock)(1024 * 64, blockSize));
+}
+
+version (StdUnittest)
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ testAllocator!(() => SharedBitmappedBlock!(64, 8, Mallocator, Yes.multiblock)(1024 * 64));
+ testAllocator!(() => SharedBitmappedBlock!(64, 8, Mallocator, No.multiblock)(1024 * 64));
+}
+
+version (StdUnittest)
+@system unittest
+{
+ // Test chooseAtRuntime
+ import std.experimental.allocator.mallocator : Mallocator;
+ uint blockSize = 64;
+ testAllocator!(() => SharedBitmappedBlock!(chooseAtRuntime, 8, Mallocator, Yes.multiblock)(1024 * 64, blockSize));
+ testAllocator!(() => SharedBitmappedBlock!(chooseAtRuntime, 8, Mallocator, No.multiblock)(1024 * 64, blockSize));
+}
+
@system unittest
{
- static void testAllocateAll(size_t bs)(uint blocks, uint blocksAtATime)
+ static void testAllocateAll(size_t bs, bool isShared = true)(size_t blocks, uint blocksAtATime)
{
- import std.algorithm.comparison : min;
+ template attribAllocate(string size)
+ {
+ static if (isShared)
+ {
+ const char[] attribAllocate = "(() nothrow @safe @nogc => a.allocate(" ~ size ~ "))()";
+ }
+ else
+ {
+ const char[] attribAllocate = "(() pure nothrow @safe @nogc => a.allocate(" ~ size ~ "))()";
+ }
+ }
+
assert(bs);
+ import std.typecons : Ternary;
+ import std.algorithm.comparison : min;
import std.experimental.allocator.gc_allocator : GCAllocator;
- auto a = BitmappedBlock!(bs, min(bs, platformAlignment))(
- cast(ubyte[])(GCAllocator.instance.allocate((blocks * bs * 8 +
- blocks) / 8))
- );
+
+ static if (isShared)
+ {
+ auto a = SharedBitmappedBlock!(bs, min(bs, platformAlignment), NullAllocator)(
+ cast(ubyte[])(GCAllocator.instance.allocate((blocks * bs * 8 + blocks) / 8)));
+ }
+ else
+ {
+ auto a = BitmappedBlock!(bs, min(bs, platformAlignment), NullAllocator)(
+ cast(ubyte[])(GCAllocator.instance.allocate((blocks * bs * 8 + blocks) / 8)));
+ }
+
import std.conv : text;
assert(blocks >= a._blocks, text(blocks, " < ", a._blocks));
blocks = a._blocks;
// test allocation of 0 bytes
- auto x = a.allocate(0);
+ auto x = mixin(attribAllocate!("0"));
assert(x is null);
// test allocation of 1 byte
- x = a.allocate(1);
- assert(x.length == 1 || blocks == 0,
- text(x.ptr, " ", x.length, " ", a));
- a.deallocateAll();
-
+ x = mixin(attribAllocate!("1"));
+ assert(x.length == 1 || blocks == 0);
+ assert((() nothrow @nogc => a.deallocateAll())());
+ assert(a.empty() == Ternary.yes);
bool twice = true;
begin:
foreach (i; 0 .. blocks / blocksAtATime)
{
- auto b = a.allocate(bs * blocksAtATime);
+ auto b = mixin(attribAllocate!("bs * blocksAtATime"));
assert(b.length == bs * blocksAtATime, text(i, ": ", b.length));
}
- assert(a.allocate(bs * blocksAtATime) is null);
- assert(a.allocate(1) is null);
+
+ assert(mixin(attribAllocate!("bs * blocksAtATime")) is null);
+ if (a._blocks % blocksAtATime == 0)
+ {
+ assert(mixin(attribAllocate!("1")) is null);
+ }
// Now deallocate all and do it again!
- a.deallocateAll();
+ assert((() nothrow @nogc => a.deallocateAll())());
// Test deallocation
auto v = new void[][blocks / blocksAtATime];
foreach (i; 0 .. blocks / blocksAtATime)
{
- auto b = a.allocate(bs * blocksAtATime);
+ auto b = mixin(attribAllocate!("bs * blocksAtATime"));
assert(b.length == bs * blocksAtATime, text(i, ": ", b.length));
v[i] = b;
}
- assert(a.allocate(bs * blocksAtATime) is null);
- assert(a.allocate(1) is null);
+ assert(mixin(attribAllocate!("bs * blocksAtATime")) is null);
+ if (a._blocks % blocksAtATime == 0)
+ {
+ assert(mixin(attribAllocate!("1")) is null);
+ }
foreach (i; 0 .. blocks / blocksAtATime)
{
- a.deallocate(v[i]);
+ () nothrow @nogc { a.deallocate(v[i]); }();
}
foreach (i; 0 .. blocks / blocksAtATime)
{
- auto b = a.allocate(bs * blocksAtATime);
+ auto b = mixin(attribAllocate!("bs * blocksAtATime"));
assert(b.length == bs * blocksAtATime, text(i, ": ", b.length));
v[i] = b;
}
foreach (i; 0 .. v.length)
{
- a.deallocate(v[i]);
+ () nothrow @nogc { a.deallocate(v[i]); }();
}
if (twice)
@@ -781,18 +1895,26 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
goto begin;
}
- a.deallocateAll;
+ assert((() nothrow @nogc => a.deallocateAll())());
// test expansion
if (blocks >= blocksAtATime)
{
foreach (i; 0 .. blocks / blocksAtATime - 1)
{
- auto b = a.allocate(bs * blocksAtATime);
+ auto b = mixin(attribAllocate!("bs * blocksAtATime"));
assert(b.length == bs * blocksAtATime, text(i, ": ", b.length));
(cast(ubyte[]) b)[] = 0xff;
- a.expand(b, blocksAtATime * bs)
- || assert(0, text(i));
+ static if (isShared)
+ {
+ assert((() nothrow @safe @nogc => a.expand(b, blocksAtATime * bs))()
+ , text(i));
+ }
+ else
+ {
+ assert((() pure nothrow @safe @nogc => a.expand(b, blocksAtATime * bs))()
+ , text(i));
+ }
(cast(ubyte[]) b)[] = 0xfe;
assert(b.length == bs * blocksAtATime * 2, text(i, ": ", b.length));
a.reallocate(b, blocksAtATime * bs) || assert(0);
@@ -802,31 +1924,203 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
}
testAllocateAll!(1)(0, 1);
+ testAllocateAll!(1, false)(0, 1);
testAllocateAll!(1)(8, 1);
+ testAllocateAll!(1, false)(8, 1);
+
testAllocateAll!(4096)(128, 1);
+ testAllocateAll!(4096, false)(128, 1);
testAllocateAll!(1)(0, 2);
testAllocateAll!(1)(128, 2);
testAllocateAll!(4096)(128, 2);
+ testAllocateAll!(1, false)(0, 2);
+ testAllocateAll!(1, false)(128, 2);
+ testAllocateAll!(4096, false)(128, 2);
+
testAllocateAll!(1)(0, 4);
testAllocateAll!(1)(128, 4);
testAllocateAll!(4096)(128, 4);
+ testAllocateAll!(1, false)(0, 4);
+ testAllocateAll!(1, false)(128, 4);
+ testAllocateAll!(4096, false)(128, 4);
+
testAllocateAll!(1)(0, 3);
testAllocateAll!(1)(24, 3);
testAllocateAll!(3008)(100, 1);
testAllocateAll!(3008)(100, 3);
+ testAllocateAll!(1, false)(0, 3);
+ testAllocateAll!(1, false)(24, 3);
+ testAllocateAll!(3008, false)(100, 1);
+ testAllocateAll!(3008, false)(100, 3);
+
testAllocateAll!(1)(0, 128);
testAllocateAll!(1)(128 * 1, 128);
testAllocateAll!(128 * 20)(13 * 128, 128);
+
+ testAllocateAll!(1, false)(0, 128);
+ testAllocateAll!(1, false)(128 * 1, 128);
+ testAllocateAll!(128 * 20, false)(13 * 128, 128);
}
-// Test totalAllocation
-@safe unittest
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ enum blocks = 10000;
+ int count = 0;
+
+ ubyte[] payload = cast(ubyte[]) Mallocator.instance.allocate(blocks * 16);
+ auto a = BitmappedBlock!(16, 16)(payload);
+ void[][] buf = cast(void[][]) Mallocator.instance.allocate((void[]).sizeof * blocks);
+
+ assert(!a.allocateFresh(0));
+ assert(!a._control[0]);
+
+ void[] b = a.allocate(256 * 16);
+ assert(b.length == 256 * 16);
+ count += 256;
+
+ assert(!a._control[count]);
+ b = a.allocateFresh(16);
+ assert(b.length == 16);
+ count++;
+ assert(a._control[count - 1]);
+
+ b = a.allocateFresh(16 * 300);
+ assert(b.length == 16 * 300);
+ count += 300;
+
+ for (int i = 0; i < count; i++)
+ assert(a._control[i]);
+ assert(!a._control[count]);
+
+ assert(a.expand(b, 313 * 16));
+ count += 313;
+
+ for (int i = 0; i < count; i++)
+ assert(a._control[i]);
+ assert(!a._control[count]);
+
+ b = a.allocate(64 * 16);
+ assert(b.length == 64 * 16);
+ count += 64;
+
+ b = a.allocateFresh(16);
+ assert(b.length == 16);
+ count++;
+
+ for (int i = 0; i < count; i++)
+ assert(a._control[i]);
+ assert(!a._control[count]);
+
+ assert(a.deallocateAll());
+ for (int i = 0; i < a._blocks; i++)
+ assert(!a._control[i]);
+
+ b = a.allocateFresh(257 * 16);
+ assert(b.length == 257 * 16);
+ for (int i = 0; i < count; i++)
+ assert(!a._control[i]);
+ for (int i = count; i < count + 257; i++)
+ assert(a._control[i]);
+ count += 257;
+ assert(!a._control[count]);
+
+ while (true)
+ {
+ b = a.allocate(16);
+ if (!b)
+ break;
+ assert(b.length == 16);
+ }
+
+ assert(!a.allocateFresh(16));
+ assert(a.deallocateAll());
+
+ assert(a.allocate(16).length == 16);
+ assert(!a.allocateFresh(16));
+}
+
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.random;
+
+ static void testAlloc(Allocator)()
+ {
+ auto numBlocks = [1, 64, 256];
+ enum blocks = 10000;
+ int iter = 0;
+
+ ubyte[] payload = cast(ubyte[]) Mallocator.instance.allocate(blocks * 16);
+ auto a = Allocator(payload);
+ void[][] buf = cast(void[][]) Mallocator.instance.allocate((void[]).sizeof * blocks);
+
+ auto rnd = Random();
+ while (iter < blocks)
+ {
+ int event = uniform(0, 2, rnd);
+ int doExpand = uniform(0, 2, rnd);
+ int allocSize = numBlocks[uniform(0, 3, rnd)] * 16;
+ int expandSize = numBlocks[uniform(0, 3, rnd)] * 16;
+ int doDeallocate = uniform(0, 2, rnd);
+
+ if (event) buf[iter] = a.allocate(allocSize);
+ else buf[iter] = a.allocateFresh(allocSize);
+
+ if (!buf[iter])
+ break;
+ assert(buf[iter].length == allocSize);
+
+ auto oldSize = buf[iter].length;
+ if (doExpand && a.expand(buf[iter], expandSize))
+ assert(buf[iter].length == expandSize + oldSize);
+
+ if (doDeallocate)
+ {
+ assert(a.deallocate(buf[iter]));
+ buf[iter] = null;
+ }
+
+ iter++;
+ }
+
+ while (iter < blocks)
+ {
+ buf[iter++] = a.allocate(16);
+ if (!buf[iter - 1])
+ break;
+ assert(buf[iter - 1].length == 16);
+ }
+
+ for (size_t i = 0; i < a._blocks; i++)
+ assert((cast(BitVector) a._control)[i]);
+
+ assert(!a.allocate(16));
+ for (size_t i = 0; i < iter; i++)
+ {
+ if (buf[i])
+ assert(a.deallocate(buf[i]));
+ }
+
+ for (size_t i = 0; i < a._blocks; i++)
+ assert(!(cast(BitVector) a._control)[i]);
+ }
+
+ testAlloc!(BitmappedBlock!(16, 16))();
+ testAlloc!(SharedBitmappedBlock!(16, 16))();
+}
+
+// Test totalAllocation and goodAllocSize
+nothrow @safe @nogc unittest
{
BitmappedBlock!(8, 8, NullAllocator) h1;
+ assert(h1.goodAllocSize(1) == 8);
assert(h1.totalAllocation(1) >= 8);
assert(h1.totalAllocation(64) >= 64);
assert(h1.totalAllocation(8 * 64) >= 8 * 64);
@@ -834,24 +2128,39 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
assert(h1.totalAllocation(8 * 64 + 1) >= 8 * 65);
BitmappedBlock!(64, 8, NullAllocator) h2;
+ assert(h2.goodAllocSize(1) == 64);
assert(h2.totalAllocation(1) >= 64);
assert(h2.totalAllocation(64 * 64) >= 64 * 64);
BitmappedBlock!(4096, 4096, NullAllocator) h3;
+ assert(h3.goodAllocSize(1) == 4096);
assert(h3.totalAllocation(1) >= 4096);
assert(h3.totalAllocation(64 * 4096) >= 64 * 4096);
assert(h3.totalAllocation(64 * 4096 + 1) >= 65 * 4096);
}
+// Test owns
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.typecons : Ternary;
+
+ auto a = BitmappedBlock!(64, 8, GCAllocator)(1024 * 64);
+ const void[] buff = (() pure nothrow @safe @nogc => a.allocate(42))();
+
+ assert((() nothrow @safe @nogc => a.owns(buff))() == Ternary.yes);
+ assert((() nothrow @safe @nogc => a.owns(null))() == Ternary.no);
+}
+
// BitmappedBlockWithInternalPointers
/**
-A $(D BitmappedBlock) with additional structure for supporting $(D
-resolveInternalPointer). To that end, $(D BitmappedBlockWithInternalPointers) adds a
+A `BitmappedBlock` with additional structure for supporting `resolveInternalPointer`.
+To that end, `BitmappedBlockWithInternalPointers` adds a
bitmap (one bit per block) that marks object starts. The bitmap itself has
variable size and is allocated together with regular allocations.
-The time complexity of $(D resolveInternalPointer) is $(BIGOH k), where $(D k)
+The time complexity of `resolveInternalPointer` is $(BIGOH k), where `k`
is the size of the object within which the internal pointer is looked up.
*/
@@ -861,31 +2170,42 @@ struct BitmappedBlockWithInternalPointers(
{
import std.conv : text;
import std.typecons : Ternary;
+
+ static if (!stateSize!ParentAllocator)
+ version (StdUnittest)
@system unittest
{
import std.experimental.allocator.mallocator : AlignedMallocator;
auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64,
theAlignment));
- scope(exit) AlignedMallocator.instance.deallocate(m);
+ scope(exit) () nothrow @nogc { AlignedMallocator.instance.deallocate(m); }();
testAllocator!(() => BitmappedBlockWithInternalPointers(m));
}
// state {
- private BitmappedBlock!(theBlockSize, theAlignment, NullAllocator) _heap;
+ private BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator) _heap;
private BitVector _allocStart;
// }
/**
Constructors accepting desired capacity or a preallocated buffer, similar
- in semantics to those of $(D BitmappedBlock).
+ in semantics to those of `BitmappedBlock`.
*/
+ static if (!stateSize!ParentAllocator)
this(ubyte[] data)
{
_heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator)(data);
}
+ static if (stateSize!ParentAllocator)
+ this(ParentAllocator parent, ubyte[] data)
+ {
+ _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator)(data);
+ _heap.parent = parent;
+ }
+
/// Ditto
- static if (!is(ParentAllocator == NullAllocator))
+ static if (!is(ParentAllocator == NullAllocator) && !stateSize!ParentAllocator)
this(size_t capacity)
{
// Add room for the _allocStart vector
@@ -893,7 +2213,17 @@ struct BitmappedBlockWithInternalPointers(
(capacity + capacity.divideRoundUp(64));
}
+ /// Ditto
+ static if (!is(ParentAllocator == NullAllocator) && stateSize!ParentAllocator)
+ this(ParentAllocator parent, size_t capacity)
+ {
+ // Add room for the _allocStart vector
+ _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator)
+ (parent, capacity + capacity.divideRoundUp(64));
+ }
+
// Makes sure there's enough room for _allocStart
+ @safe
private bool ensureRoomForAllocStart(size_t len)
{
if (_allocStart.length >= len) return true;
@@ -901,9 +2231,9 @@ struct BitmappedBlockWithInternalPointers(
immutable oldLength = _allocStart.rep.length;
immutable bits = len.roundUpToMultipleOf(64);
void[] b = _allocStart.rep;
- if (!_heap.reallocate(b, bits / 8)) return false;
- assert(b.length * 8 == bits, text(b.length * 8, " != ", bits));
- _allocStart = BitVector(cast(ulong[]) b);
+ if ((() @trusted => !_heap.reallocate(b, bits / 8))()) return false;
+ assert(b.length * 8 == bits);
+ _allocStart = BitVector((() @trusted => cast(ulong[]) b)());
assert(_allocStart.rep.length * 64 == bits);
_allocStart.rep[oldLength .. $] = ulong.max;
return true;
@@ -915,6 +2245,7 @@ struct BitmappedBlockWithInternalPointers(
alias alignment = theAlignment;
/// Ditto
+ pure nothrow @safe @nogc
size_t goodAllocSize(size_t n)
{
return n.roundUpToMultipleOf(_heap.blockSize);
@@ -925,13 +2256,13 @@ struct BitmappedBlockWithInternalPointers(
{
auto r = _heap.allocate(bytes);
if (!r.ptr) return r;
- immutable block = (r.ptr - _heap._payload.ptr) / _heap.blockSize;
+ immutable block = (() @trusted => (r.ptr - _heap._payload.ptr) / _heap.blockSize)();
immutable blocks =
(r.length + _heap.blockSize - 1) / _heap.blockSize;
if (!ensureRoomForAllocStart(block + blocks))
{
// Failed, free r and bailout
- _heap.deallocate(r);
+ () @trusted { _heap.deallocate(r); r = null; }();
return null;
}
assert(block < _allocStart.length);
@@ -973,7 +2304,7 @@ struct BitmappedBlockWithInternalPointers(
immutable newBlocks =
(b.length + bytes + _heap.blockSize - 1) / _heap.blockSize;
assert(newBlocks >= oldBlocks);
- immutable block = (b.ptr - _heap._payload.ptr) / _heap.blockSize;
+ immutable block = (() @trusted => (b.ptr - _heap._payload.ptr) / _heap.blockSize)();
assert(_allocStart[block]);
if (!ensureRoomForAllocStart(block + newBlocks)
|| !_heap.expand(b, bytes))
@@ -997,22 +2328,24 @@ struct BitmappedBlockWithInternalPointers(
}
/// Ditto
+ nothrow @safe @nogc
Ternary resolveInternalPointer(const void* p, ref void[] result)
{
- if (p < _heap._payload.ptr
- || p >= _heap._payload.ptr + _heap._payload.length)
+ if ((() @trusted => _heap._payload
+ && (p < &_heap._payload[0]
+ || p >= &_heap._payload[0] + _heap._payload.length))())
{
return Ternary.no;
}
// Find block start
- auto block = (p - _heap._payload.ptr) / _heap.blockSize;
+ auto block = (() @trusted => (p - &_heap._payload[0]) / _heap.blockSize)();
if (block >= _allocStart.length) return Ternary.no;
// Within an allocation, must find the 1 just to the left of it
auto i = _allocStart.find1Backward(block);
if (i == i.max) return Ternary.no;
auto j = _allocStart.find1(i + 1);
- result = _heap._payload.ptr[cast(size_t) (_heap.blockSize * i)
- .. cast(size_t) (_heap.blockSize * j)];
+ result = (() @trusted => _heap._payload.ptr[cast(size_t) (_heap.blockSize * i)
+ .. cast(size_t) (_heap.blockSize * j)])();
return Ternary.yes;
}
@@ -1066,47 +2399,69 @@ struct BitmappedBlockWithInternalPointers(
import std.typecons : Ternary;
auto h = BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]);
- auto b = h.allocate(123);
+ assert((() nothrow @safe @nogc => h.empty)() == Ternary.yes);
+ auto b = (() pure nothrow @safe @nogc => h.allocate(123))();
assert(b.length == 123);
+ assert((() nothrow @safe @nogc => h.empty)() == Ternary.no);
void[] p;
- Ternary r = h.resolveInternalPointer(b.ptr + 17, p);
+ void* offset = &b[0] + 17;
+ assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes);
assert(p.ptr is b.ptr);
assert(p.length >= b.length);
- b = h.allocate(4096);
+ b = (() pure nothrow @safe @nogc => h.allocate(4096))();
- h.resolveInternalPointer(b.ptr, p);
+ offset = &b[0];
+ assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes);
assert(p is b);
- h.resolveInternalPointer(b.ptr + 11, p);
+ offset = &b[0] + 11;
+ assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes);
assert(p is b);
void[] unchanged = p;
- h.resolveInternalPointer(b.ptr - 40_970, p);
+ offset = &b[0] - 40_970;
+ assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.no);
assert(p is unchanged);
- assert(h.expand(b, 1));
+ assert((() @safe => h.expand(b, 1))());
assert(b.length == 4097);
- h.resolveInternalPointer(b.ptr + 4096, p);
+ offset = &b[0] + 4096;
+ assert((() nothrow @safe @nogc => h.resolveInternalPointer(offset, p))() == Ternary.yes);
assert(p.ptr is b.ptr);
+
+ // Ensure deallocate inherits from parent
+ () nothrow @nogc { h.deallocate(b); }();
+}
+
+@system unittest
+{
+ auto h = BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]);
+ assert((() pure nothrow @safe @nogc => h.goodAllocSize(1))() == 4096);
+}
+
+// Test instantiation with stateful allocators
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.experimental.allocator.building_blocks.region : Region;
+ auto r = Region!Mallocator(1024 * 1024);
+ auto h = BitmappedBlockWithInternalPointers!(4096, 8, Region!Mallocator*)(&r, 4096 * 1024);
}
/**
-Returns the number of most significant ones before a zero can be found in $(D
-x). If $(D x) contains no zeros (i.e. is equal to $(D ulong.max)), returns 64.
+Returns the number of most significant ones before a zero can be found in `x`.
+If `x` contains no zeros (i.e. is equal to `ulong.max`), returns 64.
*/
+pure nothrow @safe @nogc
private uint leadingOnes(ulong x)
{
- uint result = 0;
- while (cast(long) x < 0)
- {
- ++result;
- x <<= 1;
- }
- return result;
+ import core.bitop : bsr;
+ const x_ = ~x;
+ return x_ == 0 ? 64 : (63 - bsr(x_));
}
-@system unittest
+@safe unittest
{
assert(leadingOnes(0) == 0);
assert(leadingOnes(~0UL) == 64);
@@ -1118,8 +2473,9 @@ private uint leadingOnes(ulong x)
}
/**
-Finds a run of contiguous ones in $(D x) of length at least $(D n).
+Finds a run of contiguous ones in `x` of length at least `n`.
*/
+pure nothrow @safe @nogc
private uint findContigOnes(ulong x, uint n)
{
while (n > 1)
@@ -1131,7 +2487,7 @@ private uint findContigOnes(ulong x, uint n)
return leadingOnes(~x);
}
-@system unittest
+@safe unittest
{
assert(findContigOnes(0x0000_0000_0000_0300, 2) == 54);
@@ -1148,6 +2504,7 @@ private uint findContigOnes(ulong x, uint n)
/*
Unconditionally sets the bits from lsb through msb in w to zero.
*/
+pure nothrow @safe @nogc
private void setBits(ref ulong w, uint lsb, uint msb)
{
assert(lsb <= msb && msb < 64);
@@ -1155,7 +2512,7 @@ private void setBits(ref ulong w, uint lsb, uint msb)
w |= mask;
}
-@system unittest
+@safe unittest
{
ulong w;
w = 0; setBits(w, 0, 63); assert(w == ulong.max);
@@ -1167,6 +2524,7 @@ private void setBits(ref ulong w, uint lsb, uint msb)
/* Are bits from lsb through msb in w zero? If so, make then 1
and return the resulting w. Otherwise, just return 0.
*/
+pure nothrow @safe @nogc
private bool setBitsIfZero(ref ulong w, uint lsb, uint msb)
{
assert(lsb <= msb && msb < 64);
@@ -1177,6 +2535,7 @@ private bool setBitsIfZero(ref ulong w, uint lsb, uint msb)
}
// Assigns bits in w from lsb through msb to zero.
+pure nothrow @safe @nogc
private void resetBits(ref ulong w, uint lsb, uint msb)
{
assert(lsb <= msb && msb < 64);
@@ -1191,12 +2550,15 @@ private struct BitVector
{
ulong[] _rep;
- auto rep() { return _rep; }
+ auto rep(this _)() { return _rep; }
+ pure nothrow @safe @nogc
this(ulong[] data) { _rep = data; }
+ pure nothrow @safe @nogc
void opSliceAssign(bool b) { _rep[] = b ? ulong.max : 0; }
+ pure nothrow @safe @nogc
void opSliceAssign(bool b, ulong x, ulong y)
{
assert(x <= y && y <= _rep.length * 64);
@@ -1222,12 +2584,13 @@ private struct BitVector
assert(i1 < i2);
if (b) setBits(_rep[i1], 0, b1);
else resetBits(_rep[i1], 0, b1);
- _rep[i1 + 1 .. i2] = b;
+ _rep[i1 + 1 .. i2] = (b ? ulong.max : 0);
if (b) setBits(_rep[i2], b2, 63);
else resetBits(_rep[i2], b2, 63);
}
}
+ pure nothrow @safe @nogc
bool opIndex(ulong x)
{
assert(x < length);
@@ -1235,6 +2598,7 @@ private struct BitVector
& (0x8000_0000_0000_0000UL >> (x % 64))) != 0;
}
+ pure nothrow @safe @nogc
void opIndexAssign(bool b, ulong x)
{
assert(x / 64 <= size_t.max);
@@ -1244,6 +2608,7 @@ private struct BitVector
else _rep[i] &= ~j;
}
+ pure nothrow @safe @nogc
ulong length() const
{
return _rep.length * 64;
@@ -1252,6 +2617,7 @@ private struct BitVector
/* Returns the index of the first 1 to the right of i (including i itself),
or length if not found.
*/
+ pure nothrow @safe @nogc
ulong find1(ulong i)
{
assert(i < length);
@@ -1279,6 +2645,7 @@ private struct BitVector
/* Returns the index of the first 1 to the left of i (including i itself),
or ulong.max if not found.
*/
+ pure nothrow @safe @nogc
ulong find1Backward(ulong i)
{
assert(i < length);
@@ -1304,6 +2671,7 @@ private struct BitVector
}
/// Are all bits zero?
+ pure nothrow @safe @nogc
bool allAre0() const
{
foreach (w; _rep) if (w) return false;
@@ -1311,12 +2679,14 @@ private struct BitVector
}
/// Are all bits one?
+ pure nothrow @safe @nogc
bool allAre1() const
{
foreach (w; _rep) if (w != ulong.max) return false;
return true;
}
+ pure nothrow @safe @nogc
ulong findZeros(immutable size_t howMany, ulong start)
{
assert(start < length);
@@ -1353,7 +2723,7 @@ private struct BitVector
}
}
-@system unittest
+@safe unittest
{
auto v = BitVector(new ulong[10]);
assert(v.length == 640);
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/bucketizer.d b/libphobos/src/std/experimental/allocator/building_blocks/bucketizer.d
index 64067ddc0ea..aab1f60eb72 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/bucketizer.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/bucketizer.d
@@ -1,23 +1,27 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/bucketizer.d)
+*/
module std.experimental.allocator.building_blocks.bucketizer;
/**
-A $(D Bucketizer) uses distinct allocators for handling allocations of sizes in
+A `Bucketizer` uses distinct allocators for handling allocations of sizes in
the intervals $(D [min, min + step - 1]), $(D [min + step, min + 2 * step - 1]),
-$(D [min + 2 * step, min + 3 * step - 1]), $(D ...), $(D [max - step + 1, max]).
+$(D [min + 2 * step, min + 3 * step - 1]), `...`, $(D [max - step + 1, max]).
-$(D Bucketizer) holds a fixed-size array of allocators and dispatches calls to
+`Bucketizer` holds a fixed-size array of allocators and dispatches calls to
them appropriately. The size of the array is $(D (max + 1 - min) / step), which
must be an exact division.
-Allocations for sizes smaller than $(D min) or larger than $(D max) are illegal
-for $(D Bucketizer). To handle them separately, $(D Segregator) may be of use.
+Allocations for sizes smaller than `min` or larger than `max` are illegal
+for `Bucketizer`. To handle them separately, `Segregator` may be of use.
*/
struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
{
- import common = std.experimental.allocator.common : roundUpToMultipleOf;
+ import common = std.experimental.allocator.common : roundUpToMultipleOf,
+ alignedAt;
import std.traits : hasMember;
import std.typecons : Ternary;
@@ -31,20 +35,22 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
*/
Allocator[(max + 1 - min) / step] buckets;
+ pure nothrow @safe @nogc
private Allocator* allocatorFor(size_t n)
{
const i = (n - min) / step;
- return i < buckets.length ? buckets.ptr + i : null;
+ return i < buckets.length ? &buckets[i] : null;
}
/**
- The alignment offered is the same as $(D Allocator.alignment).
+ The alignment offered is the same as `Allocator.alignment`.
*/
enum uint alignment = Allocator.alignment;
/**
- Rounds up to the maximum size of the bucket in which $(D bytes) falls.
+ Rounds up to the maximum size of the bucket in which `bytes` falls.
*/
+ pure nothrow @safe @nogc
size_t goodAllocSize(size_t bytes) const
{
// round up bytes such that bytes - min + 1 is a multiple of step
@@ -54,7 +60,7 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
}
/**
- Directs the call to either one of the $(D buckets) allocators.
+ Directs the call to either one of the `buckets` allocators.
*/
void[] allocate(size_t bytes)
{
@@ -68,42 +74,56 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
return null;
}
+ static if (hasMember!(Allocator, "allocateZeroed"))
+ package(std) void[] allocateZeroed()(size_t bytes)
+ {
+ if (!bytes) return null;
+ if (auto a = allocatorFor(bytes))
+ {
+ const actual = goodAllocSize(bytes);
+ auto result = a.allocateZeroed(actual);
+ return result.ptr ? result.ptr[0 .. bytes] : null;
+ }
+ return null;
+ }
+
/**
- Directs the call to either one of the $(D buckets) allocators. Defined only
+ Allocates the requested `bytes` of memory with specified `alignment`.
+ Directs the call to either one of the `buckets` allocators. Defined only
if `Allocator` defines `alignedAllocate`.
*/
static if (hasMember!(Allocator, "alignedAllocate"))
- void[] alignedAllocate(size_t bytes, uint a)
+ void[] alignedAllocate(size_t bytes, uint alignment)
{
if (!bytes) return null;
- if (auto a = allocatorFor(b.length))
+ if (auto a = allocatorFor(bytes))
{
const actual = goodAllocSize(bytes);
- auto result = a.alignedAllocate(actual);
- return result.ptr ? result.ptr[0 .. bytes] : null;
+ auto result = a.alignedAllocate(actual, alignment);
+ return result !is null ? (() @trusted => (&result[0])[0 .. bytes])() : null;
}
return null;
}
/**
This method allows expansion within the respective bucket range. It succeeds
- if both $(D b.length) and $(D b.length + delta) fall in a range of the form
+ if both `b.length` and $(D b.length + delta) fall in a range of the form
$(D [min + k * step, min + (k + 1) * step - 1]).
*/
bool expand(ref void[] b, size_t delta)
{
- if (!b.ptr) return delta == 0;
+ if (!b || delta == 0) return delta == 0;
assert(b.length >= min && b.length <= max);
const available = goodAllocSize(b.length);
const desired = b.length + delta;
if (available < desired) return false;
- b = b.ptr[0 .. desired];
+ b = (() @trusted => b.ptr[0 .. desired])();
return true;
}
/**
This method allows reallocation within the respective bucket range. If both
- $(D b.length) and $(D size) fall in a range of the form $(D [min + k *
+ `b.length` and `size` fall in a range of the form $(D [min + k *
step, min + (k + 1) * step - 1]), then reallocation is in place. Otherwise,
reallocation with moving is attempted.
*/
@@ -115,9 +135,9 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
b = null;
return true;
}
- if (size >= b.length)
+ if (size >= b.length && expand(b, size - b.length))
{
- return expand(b, size - b.length);
+ return true;
}
assert(b.length >= min && b.length <= max);
if (goodAllocSize(size) == goodAllocSize(b.length))
@@ -142,18 +162,18 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
b = null;
return true;
}
- if (size >= b.length)
+ if (size >= b.length && b.ptr.alignedAt(a) && expand(b, size - b.length))
{
- return expand(b, size - b.length);
+ return true;
}
assert(b.length >= min && b.length <= max);
- if (goodAllocSize(size) == goodAllocSize(b.length))
+ if (goodAllocSize(size) == goodAllocSize(b.length) && b.ptr.alignedAt(a))
{
b = b.ptr[0 .. size];
return true;
}
// Move cross buckets
- return .alignedReallocate(this, b, size, a);
+ return common.alignedReallocate(this, b, size, a);
}
/**
@@ -172,7 +192,7 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
}
/**
- This method is only defined if $(D Allocator) defines $(D deallocate).
+ This method is only defined if `Allocator` defines `deallocate`.
*/
static if (hasMember!(Allocator, "deallocate"))
bool deallocate(void[] b)
@@ -236,6 +256,93 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
auto b = a.allocate(400);
assert(b.length == 400);
assert(a.owns(b) == Ternary.yes);
- void[] p;
a.deallocate(b);
}
+
+@system unittest
+{
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.allocator_list : AllocatorList;
+ import std.experimental.allocator.building_blocks.free_list : FreeList;
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.common : unbounded;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+
+ Bucketizer!(
+ FreeList!(
+ AllocatorList!(
+ (size_t n) => Region!Mallocator(max(n, 1024 * 1024)), Mallocator),
+ 0, unbounded),
+ 65, 512, 64) a;
+
+ assert((() pure nothrow @safe @nogc => a.goodAllocSize(65))() == 128);
+
+ auto b = a.allocate(100);
+ assert(b.length == 100);
+ // Make reallocate use extend
+ assert((() nothrow @nogc => a.reallocate(b, 101))());
+ assert(b.length == 101);
+ // Move cross buckets
+ assert((() nothrow @nogc => a.reallocate(b, 200))());
+ assert(b.length == 200);
+ // Free through realloc
+ assert((() nothrow @nogc => a.reallocate(b, 0))());
+ assert(b is null);
+ // Ensure deallocate inherits from parent allocators
+ assert((() nothrow @nogc => a.deallocate(b))());
+ assert((() nothrow @nogc => a.deallocateAll())());
+}
+
+// Test alignedAllocate
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+
+ Bucketizer!(BitmappedBlock!(64, 8, GCAllocator), 65, 512, 64) a;
+ foreach (ref bucket; a.buckets)
+ {
+ bucket = BitmappedBlock!(64, 8, GCAllocator)(new ubyte[1024]);
+ }
+
+ auto b = a.alignedAllocate(100, 16);
+ assert(b.length == 100);
+ assert(a.alignedAllocate(42, 16) is null);
+ assert(a.alignedAllocate(0, 16) is null);
+ assert((() pure nothrow @safe @nogc => a.expand(b, 0))());
+ assert(b.length == 100);
+ assert((() pure nothrow @safe @nogc => a.expand(b, 28))());
+ assert(b.length == 128);
+ assert((() pure nothrow @safe @nogc => !a.expand(b, 1))());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+
+ Bucketizer!(BitmappedBlock!(64, 8, GCAllocator), 1, 512, 64) a;
+ foreach (ref bucket; a.buckets)
+ {
+ bucket = BitmappedBlock!(64, 8, GCAllocator)(new ubyte[1024]);
+ }
+
+ auto b = a.alignedAllocate(1, 4);
+ assert(b.length == 1);
+ // Make reallocate use extend
+ assert(a.alignedReallocate(b, 11, 4));
+ assert(b.length == 11);
+ // Make reallocate use use realloc because of alignment change
+ assert(a.alignedReallocate(b, 21, 16));
+ assert(b.length == 21);
+ // Make reallocate use extend
+ assert(a.alignedReallocate(b, 22, 16));
+ assert(b.length == 22);
+ // Move cross buckets
+ assert(a.alignedReallocate(b, 101, 16));
+ assert(b.length == 101);
+ // Free through realloc
+ assert(a.alignedReallocate(b, 0, 16));
+ assert(b is null);
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d
index ca7961b9274..b413d738dab 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d
@@ -1,22 +1,25 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/fallback_allocator.d)
+*/
module std.experimental.allocator.building_blocks.fallback_allocator;
import std.experimental.allocator.common;
/**
-$(D FallbackAllocator) is the allocator equivalent of an "or" operator in
-algebra. An allocation request is first attempted with the $(D Primary)
-allocator. If that returns $(D null), the request is forwarded to the $(D
+`FallbackAllocator` is the allocator equivalent of an "or" operator in
+algebra. An allocation request is first attempted with the `Primary`
+allocator. If that returns `null`, the request is forwarded to the $(D
Fallback) allocator. All other requests are dispatched appropriately to one of
the two allocators.
-In order to work, $(D FallbackAllocator) requires that $(D Primary) defines the
-$(D owns) method. This is needed in order to decide which allocator was
+In order to work, `FallbackAllocator` requires that `Primary` defines the
+`owns` method. This is needed in order to decide which allocator was
responsible for a given allocation.
-$(D FallbackAllocator) is useful for fast, special-purpose allocators backed up
+`FallbackAllocator` is useful for fast, special-purpose allocators backed up
by general-purpose allocators. The example below features a stack region backed
-up by the $(D GCAllocator).
+up by the `GCAllocator`.
*/
struct FallbackAllocator(Primary, Fallback)
{
@@ -24,6 +27,10 @@ struct FallbackAllocator(Primary, Fallback)
import std.traits : hasMember;
import std.typecons : Ternary;
+ // Need both allocators to be stateless
+ // This is to avoid using default initialized stateful allocators
+ static if (!stateSize!Primary && !stateSize!Fallback)
+ version (StdUnittest)
@system unittest
{
testAllocator!(() => FallbackAllocator());
@@ -38,7 +45,7 @@ struct FallbackAllocator(Primary, Fallback)
else alias fallback = Fallback.instance;
/**
- If both $(D Primary) and $(D Fallback) are stateless, $(D FallbackAllocator)
+ If both `Primary` and `Fallback` are stateless, `FallbackAllocator`
defines a static instance called `instance`.
*/
static if (!stateSize!Primary && !stateSize!Fallback)
@@ -61,8 +68,40 @@ struct FallbackAllocator(Primary, Fallback)
return result.length == s ? result : fallback.allocate(s);
}
+ static if (hasMember!(Primary, "allocateZeroed")
+ || (hasMember!(Fallback, "allocateZeroed")))
+ package(std) void[] allocateZeroed()(size_t s)
+ {
+ // Try to allocate with primary.
+ static if (hasMember!(Primary, "allocateZeroed"))
+ {
+ void[] result = primary.allocateZeroed(s);
+ if (result.length == s) return result;
+ }
+ else
+ {
+ void[] result = primary.allocate(s);
+ if (result.length == s)
+ {
+ (() @trusted => (cast(ubyte[]) result)[] = 0)();
+ return result;
+ }
+ }
+ // Allocate with fallback.
+ static if (hasMember!(Fallback, "allocateZeroed"))
+ {
+ return fallback.allocateZeroed(s);
+ }
+ else
+ {
+ result = fallback.allocate(s);
+ (() @trusted => (cast(ubyte[]) result)[] = 0)(); // OK even if result is null.
+ return result;
+ }
+ }
+
/**
- $(D FallbackAllocator) offers $(D alignedAllocate) iff at least one of the
+ `FallbackAllocator` offers `alignedAllocate` iff at least one of the
allocators also offers it. It attempts to allocate using either or both.
*/
static if (hasMember!(Primary, "alignedAllocate")
@@ -84,12 +123,12 @@ struct FallbackAllocator(Primary, Fallback)
/**
- $(D expand) is defined if and only if at least one of the allocators
- defines $(D expand). It works as follows. If $(D primary.owns(b)), then the
- request is forwarded to $(D primary.expand) if it is defined, or fails
- (returning $(D false)) otherwise. If $(D primary) does not own $(D b), then
- the request is forwarded to $(D fallback.expand) if it is defined, or fails
- (returning $(D false)) otherwise.
+ `expand` is defined if and only if at least one of the allocators
+ defines `expand`. It works as follows. If `primary.owns(b)`, then the
+ request is forwarded to `primary.expand` if it is defined, or fails
+ (returning `false`) otherwise. If `primary` does not own `b`, then
+ the request is forwarded to `fallback.expand` if it is defined, or fails
+ (returning `false`) otherwise.
*/
static if (hasMember!(Primary, "owns")
@@ -113,13 +152,13 @@ struct FallbackAllocator(Primary, Fallback)
/**
- $(D reallocate) works as follows. If $(D primary.owns(b)), then $(D
+ `reallocate` works as follows. If `primary.owns(b)`, then $(D
primary.reallocate(b, newSize)) is attempted. If it fails, an attempt is
- made to move the allocation from $(D primary) to $(D fallback).
+ made to move the allocation from `primary` to `fallback`.
- If $(D primary) does not own $(D b), then $(D fallback.reallocate(b,
+ If `primary` does not own `b`, then $(D fallback.reallocate(b,
newSize)) is attempted. If that fails, an attempt is made to move the
- allocation from $(D fallback) to $(D primary).
+ allocation from `fallback` to `primary`.
*/
static if (hasMember!(Primary, "owns"))
@@ -192,7 +231,7 @@ struct FallbackAllocator(Primary, Fallback)
}
/**
- $(D owns) is defined if and only if both allocators define $(D owns).
+ `owns` is defined if and only if both allocators define `owns`.
Returns $(D primary.owns(b) | fallback.owns(b)).
*/
static if (hasMember!(Primary, "owns") && hasMember!(Fallback, "owns"))
@@ -202,7 +241,7 @@ struct FallbackAllocator(Primary, Fallback)
}
/**
- $(D resolveInternalPointer) is defined if and only if both allocators
+ `resolveInternalPointer` is defined if and only if both allocators
define it.
*/
static if (hasMember!(Primary, "resolveInternalPointer")
@@ -214,11 +253,11 @@ struct FallbackAllocator(Primary, Fallback)
}
/**
- $(D deallocate) is defined if and only if at least one of the allocators
- define $(D deallocate). It works as follows. If $(D primary.owns(b)),
- then the request is forwarded to $(D primary.deallocate) if it is defined,
- or is a no-op otherwise. If $(D primary) does not own $(D b), then the
- request is forwarded to $(D fallback.deallocate) if it is defined, or is a
+ `deallocate` is defined if and only if at least one of the allocators
+ define `deallocate`. It works as follows. If `primary.owns(b)`,
+ then the request is forwarded to `primary.deallocate` if it is defined,
+ or is a no-op otherwise. If `primary` does not own `b`, then the
+ request is forwarded to `fallback.deallocate` if it is defined, or is a
no-op otherwise.
*/
static if (hasMember!(Primary, "owns") &&
@@ -243,11 +282,12 @@ struct FallbackAllocator(Primary, Fallback)
}
/**
- $(D empty) is defined if both allocators also define it.
+ `empty` is defined if both allocators also define it.
Returns: $(D primary.empty & fallback.empty)
*/
- static if (hasMember!(Primary, "empty") && hasMember!(Fallback, "empty"))
+ static if (hasMember!(Primary, "empty")
+ && hasMember!(Fallback, "empty"))
Ternary empty()
{
return primary.empty & fallback.empty;
@@ -264,12 +304,89 @@ struct FallbackAllocator(Primary, Fallback)
// This allocation uses the stack
auto b1 = a.allocate(1024);
assert(b1.length == 1024, text(b1.length));
- assert(a.primary.owns(b1) == Ternary.yes);
- // This large allocation will go to the Mallocator
+ assert((() pure nothrow @safe @nogc => a.primary.owns(b1))() == Ternary.yes);
+ assert((() nothrow => a.reallocate(b1, 2048))());
+ assert(b1.length == 2048, text(b1.length));
+ assert((() pure nothrow @safe @nogc => a.primary.owns(b1))() == Ternary.yes);
+ // This large allocation will go to the GCAllocator
auto b2 = a.allocate(1024 * 1024);
- assert(a.primary.owns(b2) == Ternary.no);
- a.deallocate(b1);
- a.deallocate(b2);
+ assert((() pure nothrow @safe @nogc => a.primary.owns(b2))() == Ternary.no);
+ // Ensure deallocate inherits from parent allocators
+ () nothrow @nogc { a.deallocate(b1); }();
+ () nothrow @nogc { a.deallocate(b2); }();
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlockWithInternalPointers;
+ import std.typecons : Ternary;
+
+ alias A =
+ FallbackAllocator!(
+ BitmappedBlockWithInternalPointers!(4096),
+ BitmappedBlockWithInternalPointers!(4096)
+ );
+
+ A a = A(
+ BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]),
+ BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024])
+ );
+
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
+ auto b = a.allocate(201);
+ assert(b.length == 201);
+ assert(a.reallocate(b, 202));
+ assert(b.length == 202);
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.no);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.typecons : Ternary;
+
+ auto a = FallbackAllocator!(Region!(), Region!())(
+ Region!()(new ubyte[4096 * 1024]),
+ Region!()(new ubyte[4096 * 1024]));
+
+ auto b = a.alignedAllocate(42, 8);
+ assert(b.length == 42);
+ assert((() nothrow @nogc => a.alignedReallocate(b, 100, 8))());
+ assert(b.length == 100);
+}
+
+version (StdUnittest)
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlockWithInternalPointers;
+ import std.typecons : Ternary;
+
+ alias A =
+ FallbackAllocator!(
+ BitmappedBlockWithInternalPointers!(4096),
+ BitmappedBlockWithInternalPointers!(4096)
+ );
+
+ // Run testAllocator here since both allocators stateful
+ testAllocator!(
+ () => A(
+ BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]),
+ BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024])
+ )
+ );
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+
+ alias a = FallbackAllocator!(Mallocator, Mallocator).instance;
+
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert((() nothrow @nogc => a.reallocate(b, 100))());
+ assert(b.length == 100);
}
/*
@@ -319,8 +436,8 @@ private auto ref forward(alias arg)()
/**
Convenience function that uses type deduction to return the appropriate
-$(D FallbackAllocator) instance. To initialize with allocators that don't have
-state, use their $(D it) static member.
+`FallbackAllocator` instance. To initialize with allocators that don't have
+state, use their `it` static member.
*/
FallbackAllocator!(Primary, Fallback)
fallbackAllocator(Primary, Fallback)(auto ref Primary p, auto ref Fallback f)
@@ -353,3 +470,51 @@ fallbackAllocator(Primary, Fallback)(auto ref Primary p, auto ref Fallback f)
assert(b2.length == 10);
assert(a.primary.owns(b2) == Ternary.no);
}
+
+version (StdUnittest)
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ testAllocator!(() => fallbackAllocator(Region!GCAllocator(1024), GCAllocator.instance));
+}
+
+// Ensure `owns` inherits function attributes
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : InSituRegion;
+ import std.typecons : Ternary;
+
+ FallbackAllocator!(InSituRegion!16_384, InSituRegion!16_384) a;
+ auto buff = a.allocate(42);
+ assert((() pure nothrow @safe @nogc => a.owns(buff))() == Ternary.yes);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.typecons : Ternary;
+
+ auto a = fallbackAllocator(GCAllocator.instance, GCAllocator.instance);
+ auto b = a.allocate(1020);
+ assert(b.length == 1020);
+
+ void[] p;
+ assert((() nothrow @safe @nogc => a.resolveInternalPointer(null, p))() == Ternary.no);
+ assert((() nothrow @safe @nogc => a.resolveInternalPointer(&b[0], p))() == Ternary.yes);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.typecons : Ternary;
+
+ alias A = FallbackAllocator!(Region!(), Region!());
+ auto a = A(Region!()(new ubyte[16_384]), Region!()(new ubyte[16_384]));
+
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+ assert((() nothrow @safe @nogc => a.expand(b, 58))());
+ assert(b.length == 100);
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/free_list.d b/libphobos/src/std/experimental/allocator/building_blocks/free_list.d
index 88608060107..7055d666fb1 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/free_list.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/free_list.d
@@ -1,4 +1,7 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/free_list.d)
+*/
module std.experimental.allocator.building_blocks.free_list;
import std.experimental.allocator.common;
@@ -7,21 +10,23 @@ import std.typecons : Flag, Yes, No;
/**
$(HTTP en.wikipedia.org/wiki/Free_list, Free list allocator), stackable on top of
-another allocator. Allocation requests between $(D min) and $(D max) bytes are
-rounded up to $(D max) and served from a singly-linked list of buffers
+another allocator. Allocation requests between `min` and `max` bytes are
+rounded up to `max` and served from a singly-linked list of buffers
deallocated in the past. All other allocations are directed to $(D
ParentAllocator). Due to the simplicity of free list management, allocations
-from the free list are fast.
+from the free list are fast. If `adaptive` is set to `Yes.adaptive`,
+the free list gradually reduces its size if allocations tend to use the parent
+allocator much more than the lists' available nodes.
One instantiation is of particular interest: $(D FreeList!(0, unbounded)) puts
every deallocation in the freelist, and subsequently serves any allocation from
the freelist (if not empty). There is no checking of size matching, which would
be incorrect for a freestanding allocator but is both correct and fast when an
-owning allocator on top of the free list allocator (such as $(D Segregator)) is
+owning allocator on top of the free list allocator (such as `Segregator`) is
already in charge of handling size checking.
-The following methods are defined if $(D ParentAllocator) defines them, and
-forward to it: $(D expand), $(D owns), $(D reallocate).
+The following methods are defined if `ParentAllocator` defines them, and
+forward to it: `expand`, `owns`, `reallocate`.
*/
struct FreeList(ParentAllocator,
@@ -32,6 +37,7 @@ struct FreeList(ParentAllocator,
import std.exception : enforce;
import std.traits : hasMember;
import std.typecons : Ternary;
+ import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
static assert(minSize != unbounded, "Use minSize = 0 for no low bound.");
static assert(maxSize >= (void*).sizeof,
@@ -47,7 +53,7 @@ struct FreeList(ParentAllocator,
/**
Returns the smallest allocation size eligible for allocation from the
freelist. (If $(D minSize != chooseAtRuntime), this is simply an alias
- for $(D minSize).)
+ for `minSize`.)
*/
@property size_t min() const
{
@@ -55,15 +61,15 @@ struct FreeList(ParentAllocator,
return _min;
}
/**
- If $(D FreeList) has been instantiated with $(D minSize ==
- chooseAtRuntime), then the $(D min) property is writable. Setting it
+ If `FreeList` has been instantiated with $(D minSize ==
+ chooseAtRuntime), then the `min` property is writable. Setting it
must precede any allocation.
Params:
- low = new value for $(D min)
+ low = new value for `min`
Precondition: $(D low <= max), or $(D maxSize == chooseAtRuntime) and
- $(D max) has not yet been initialized. Also, no allocation has been
+ `max` has not yet been initialized. Also, no allocation has been
yet done with this allocator.
Postcondition: $(D min == low)
@@ -85,24 +91,24 @@ struct FreeList(ParentAllocator,
/**
Returns the largest allocation size eligible for allocation from the
freelist. (If $(D maxSize != chooseAtRuntime), this is simply an alias
- for $(D maxSize).) All allocation requests for sizes greater than or
- equal to $(D min) and less than or equal to $(D max) are rounded to $(D
+ for `maxSize`.) All allocation requests for sizes greater than or
+ equal to `min` and less than or equal to `max` are rounded to $(D
max) and forwarded to the parent allocator. When the block fitting the
same constraint gets deallocated, it is put in the freelist with the
- allocated size assumed to be $(D max).
+ allocated size assumed to be `max`.
*/
@property size_t max() const { return _max; }
/**
- If $(D FreeList) has been instantiated with $(D maxSize ==
- chooseAtRuntime), then the $(D max) property is writable. Setting it
+ If `FreeList` has been instantiated with $(D maxSize ==
+ chooseAtRuntime), then the `max` property is writable. Setting it
must precede any allocation.
Params:
- high = new value for $(D max)
+ high = new value for `max`
Precondition: $(D high >= min), or $(D minSize == chooseAtRuntime) and
- $(D min) has not yet been initialized. Also $(D high >= (void*).sizeof). Also, no allocation has been yet done with this allocator.
+ `min` has not yet been initialized. Also $(D high >= (void*).sizeof). Also, no allocation has been yet done with this allocator.
Postcondition: $(D max == high)
*/
@@ -114,8 +120,7 @@ struct FreeList(ParentAllocator,
_max = high;
}
- ///
- @safe unittest
+ @system unittest
{
import std.experimental.allocator.common : chooseAtRuntime;
import std.experimental.allocator.mallocator : Mallocator;
@@ -209,7 +214,7 @@ struct FreeList(ParentAllocator,
// state
/**
- The parent allocator. Depending on whether $(D ParentAllocator) holds state
+ The parent allocator. Depending on whether `ParentAllocator` holds state
or not, this is a member variable or an alias for
`ParentAllocator.instance`.
*/
@@ -225,12 +230,12 @@ struct FreeList(ParentAllocator,
alias alignment = ParentAllocator.alignment;
/**
- If $(D maxSize == unbounded), returns $(D parent.goodAllocSize(bytes)).
- Otherwise, returns $(D max) for sizes in the interval $(D [min, max]), and
- $(D parent.goodAllocSize(bytes)) otherwise.
+ If $(D maxSize == unbounded), returns `parent.goodAllocSize(bytes)`.
+ Otherwise, returns `max` for sizes in the interval $(D [min, max]), and
+ `parent.goodAllocSize(bytes)` otherwise.
Precondition:
- If set at runtime, $(D min) and/or $(D max) must be initialized
+ If set at runtime, `min` and/or `max` must be initialized
appropriately.
Postcondition:
@@ -252,14 +257,17 @@ struct FreeList(ParentAllocator,
return parent.goodAllocSize(bytes);
}
- private void[] allocateEligible(size_t bytes)
+ private void[] allocateEligible(string fillMode)(size_t bytes)
+ if (fillMode == "void" || fillMode == "zero")
{
+ enum bool isFillZero = fillMode == "zero";
assert(bytes);
if (root)
{
// faster
auto result = (cast(ubyte*) root)[0 .. bytes];
root = root.next;
+ static if (isFillZero) result[0 .. bytes] = 0;
return result;
}
// slower
@@ -272,7 +280,10 @@ struct FreeList(ParentAllocator,
alias toAllocate = bytes;
}
assert(toAllocate == max || max == unbounded);
- auto result = parent.allocate(toAllocate);
+ static if (isFillZero)
+ auto result = parent.allocateZeroed(toAllocate);
+ else
+ auto result = parent.allocate(toAllocate);
static if (hasTolerance)
{
if (result) result = result.ptr[0 .. bytes];
@@ -287,20 +298,20 @@ struct FreeList(ParentAllocator,
/**
Allocates memory either off of the free list or from the parent allocator.
- If $(D n) is within $(D [min, max]) or if the free list is unchecked
+ If `n` is within $(D [min, max]) or if the free list is unchecked
($(D minSize == 0 && maxSize == size_t.max)), then the free list is
consulted first. If not empty (hit), the block at the front of the free
list is removed from the list and returned. Otherwise (miss), a new block
- of $(D max) bytes is allocated, truncated to $(D n) bytes, and returned.
+ of `max` bytes is allocated, truncated to `n` bytes, and returned.
Params:
n = number of bytes to allocate
Returns:
- The allocated block, or $(D null).
+ The allocated block, or `null`.
Precondition:
- If set at runtime, $(D min) and/or $(D max) must be initialized
+ If set at runtime, `min` and/or `max` must be initialized
appropriately.
Postcondition: $(D result.length == bytes || result is null)
@@ -312,7 +323,7 @@ struct FreeList(ParentAllocator,
// fast path
if (freeListEligible(n))
{
- return allocateEligible(n);
+ return allocateEligible!"void"(n);
}
// slower
static if (adaptive == Yes.adaptive)
@@ -322,23 +333,41 @@ struct FreeList(ParentAllocator,
return parent.allocate(n);
}
+ static if (hasMember!(ParentAllocator, "allocateZeroed"))
+ package(std) void[] allocateZeroed()(size_t n)
+ {
+ static if (adaptive == Yes.adaptive) ++accumSamples;
+ assert(n < size_t.max / 2);
+ // fast path
+ if (freeListEligible(n))
+ {
+ return allocateEligible!"zero"(n);
+ }
+ // slower
+ static if (adaptive == Yes.adaptive)
+ {
+ updateStats;
+ }
+ return parent.allocateZeroed(n);
+ }
+
// Forwarding methods
mixin(forwardToMember("parent",
"expand", "owns", "reallocate"));
/**
- If $(D block.length) is within $(D [min, max]) or if the free list is
+ If `block.length` is within $(D [min, max]) or if the free list is
unchecked ($(D minSize == 0 && maxSize == size_t.max)), then inserts the
block at the front of the free list. For all others, forwards to $(D
- parent.deallocate) if $(D Parent.deallocate) is defined.
+ parent.deallocate) if `Parent.deallocate` is defined.
Params:
block = Block to deallocate.
Precondition:
- If set at runtime, $(D min) and/or $(D max) must be initialized
+ If set at runtime, `min` and/or `max` must be initialized
appropriately. The block must have been allocated with this
- freelist, and no dynamic changing of $(D min) or $(D max) is allowed to
+ freelist, and no dynamic changing of `min` or `max` is allowed to
occur between allocation and deallocation.
*/
bool deallocate(void[] block)
@@ -362,7 +391,7 @@ struct FreeList(ParentAllocator,
}
/**
- Defined only if $(D ParentAllocator) defines $(D deallocateAll). If so,
+ Defined only if `ParentAllocator` defines `deallocateAll`. If so,
forwards to it and resets the freelist.
*/
static if (hasMember!(ParentAllocator, "deallocateAll"))
@@ -374,8 +403,8 @@ struct FreeList(ParentAllocator,
/**
Nonstandard function that minimizes the memory usage of the freelist by
- freeing each element in turn. Defined only if $(D ParentAllocator) defines
- $(D deallocate).
+ freeing each element in turn. Defined only if `ParentAllocator` defines
+ `deallocate`. $(D FreeList!(0, unbounded)) does not have this function.
*/
static if (hasMember!(ParentAllocator, "deallocate") && !unchecked)
void minimize()
@@ -387,6 +416,48 @@ struct FreeList(ParentAllocator,
parent.deallocate(nuke);
}
}
+
+ /**
+ If `ParentAllocator` defines `deallocate`, the list frees all nodes
+ on destruction. $(D FreeList!(0, unbounded)) does not deallocate the memory
+ on destruction.
+ */
+ static if (!is(ParentAllocator == NullAllocator) &&
+ hasMember!(ParentAllocator, "deallocate") && !unchecked)
+ ~this()
+ {
+ minimize();
+ }
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.experimental.allocator.building_blocks.stats_collector
+ : StatsCollector, Options;
+
+ struct StatsCollectorWrapper {
+ ~this()
+ {
+ // buf2 should still be around and buf1 deallocated
+ assert(parent.numDeallocate == 1);
+ assert(parent.bytesUsed == 16);
+ }
+ static StatsCollector!(Mallocator, Options.all) parent;
+ alias parent this;
+ }
+
+ FreeList!(StatsCollectorWrapper, 16, 16) fl;
+ auto buf1 = fl.allocate(16);
+ auto buf2 = fl.allocate(16);
+ assert(fl.parent.bytesUsed == 32);
+
+ // After this, the list has 1 node, so no actual deallocation by Mallocator
+ fl.deallocate(buf1);
+ assert(fl.parent.bytesUsed == 32);
+
+ // Destruction should only deallocate the node
+ destroy(fl);
}
@system unittest
@@ -397,31 +468,55 @@ struct FreeList(ParentAllocator,
auto b1 = fl.allocate(7);
fl.allocate(8);
assert(fl.root is null);
- fl.deallocate(b1);
+ // Ensure deallocate inherits from parent
+ () nothrow @nogc { fl.deallocate(b1); }();
assert(fl.root !is null);
fl.allocate(8);
assert(fl.root is null);
}
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ FreeList!(GCAllocator, 0, 16) fl;
+ // Not @nogc because of std.conv.text
+ assert((() nothrow @safe /*@nogc*/ => fl.goodAllocSize(1))() == 16);
+}
+
+// Test that deallocateAll infers from parent
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+
+ auto fl = FreeList!(Region!(), 0, 16)(Region!()(new ubyte[1024 * 64]));
+ auto b = fl.allocate(42);
+ assert(b.length == 42);
+ assert((() pure nothrow @safe @nogc => fl.expand(b, 48))());
+ assert(b.length == 90);
+ assert((() nothrow @nogc => fl.reallocate(b, 100))());
+ assert(b.length == 100);
+ assert((() nothrow @nogc => fl.deallocateAll())());
+}
+
/**
Free list built on top of exactly one contiguous block of memory. The block is
-assumed to have been allocated with $(D ParentAllocator), and is released in
-$(D ContiguousFreeList)'s destructor (unless $(D ParentAllocator) is $(D
+assumed to have been allocated with `ParentAllocator`, and is released in
+`ContiguousFreeList`'s destructor (unless `ParentAllocator` is $(D
NullAllocator)).
-$(D ContiguousFreeList) has most advantages of $(D FreeList) but fewer
+`ContiguousFreeList` has most advantages of `FreeList` but fewer
disadvantages. It has better cache locality because items are closer to one
another. It imposes less fragmentation on its parent allocator.
-The disadvantages of $(D ContiguousFreeList) over $(D FreeList) are its pay
-upfront model (as opposed to $(D FreeList)'s pay-as-you-go approach), and a
+The disadvantages of `ContiguousFreeList` over `FreeList` are its pay
+upfront model (as opposed to `FreeList`'s pay-as-you-go approach), and a
hard limit on the number of nodes in the list. Thus, a large number of long-
lived objects may occupy the entire block, making it unavailable for serving
allocations from the free list. However, an absolute cap on the free list size
may be beneficial.
The options $(D minSize == unbounded) and $(D maxSize == unbounded) are not
-available for $(D ContiguousFreeList).
+available for `ContiguousFreeList`.
*/
struct ContiguousFreeList(ParentAllocator,
size_t minSize, size_t maxSize = minSize)
@@ -441,7 +536,7 @@ struct ContiguousFreeList(ParentAllocator,
// state
/**
- The parent allocator. Depending on whether $(D ParentAllocator) holds state
+ The parent allocator. Depending on whether `ParentAllocator` holds state
or not, this is a member variable or an alias for
`ParentAllocator.instance`.
*/
@@ -481,8 +576,8 @@ struct ContiguousFreeList(ParentAllocator,
Constructors setting up the memory structured as a free list.
Params:
- buffer = Buffer to structure as a free list. If $(D ParentAllocator) is not
- $(D NullAllocator), the buffer is assumed to be allocated by $(D parent)
+ buffer = Buffer to structure as a free list. If `ParentAllocator` is not
+ `NullAllocator`, the buffer is assumed to be allocated by `parent`
and will be freed in the destructor.
parent = Parent allocator. For construction from stateless allocators, use
their `instance` static member.
@@ -493,8 +588,8 @@ struct ContiguousFreeList(ParentAllocator,
== unbounded).
min = Minimum size eligible for freelisting. Construction with this
parameter is defined only if $(D minSize == chooseAtRuntime). If this
- condition is met and no $(D min) parameter is present, $(D min) is
- initialized with $(D max).
+ condition is met and no `min` parameter is present, `min` is
+ initialized with `max`.
*/
static if (!stateSize!ParentAllocator)
this(ubyte[] buffer)
@@ -573,11 +668,11 @@ struct ContiguousFreeList(ParentAllocator,
}
/**
- If $(D n) is eligible for freelisting, returns $(D max). Otherwise, returns
- $(D parent.goodAllocSize(n)).
+ If `n` is eligible for freelisting, returns `max`. Otherwise, returns
+ `parent.goodAllocSize(n)`.
Precondition:
- If set at runtime, $(D min) and/or $(D max) must be initialized
+ If set at runtime, `min` and/or `max` must be initialized
appropriately.
Postcondition:
@@ -590,7 +685,7 @@ struct ContiguousFreeList(ParentAllocator,
}
/**
- Allocate $(D n) bytes of memory. If $(D n) is eligible for freelist and the
+ Allocate `n` bytes of memory. If `n` is eligible for freelist and the
freelist is not empty, pops the memory off the free list. In all other
cases, uses the parent allocator.
*/
@@ -612,9 +707,13 @@ struct ContiguousFreeList(ParentAllocator,
belongs to this allocator.
*/
static if (hasMember!(SParent, "owns") || unchecked)
+ // Ternary owns(const void[] b) const ?
Ternary owns(void[] b)
{
- if (support.ptr <= b.ptr && b.ptr < support.ptr + support.length)
+ if ((() @trusted => support && b
+ && (&support[0] <= &b[0])
+ && (&b[0] < &support[0] + support.length)
+ )())
return Ternary.yes;
static if (unchecked)
return Ternary.no;
@@ -623,10 +722,10 @@ struct ContiguousFreeList(ParentAllocator,
}
/**
- Deallocates $(D b). If it's of eligible size, it's put on the free list.
- Otherwise, it's returned to $(D parent).
+ Deallocates `b`. If it's of eligible size, it's put on the free list.
+ Otherwise, it's returned to `parent`.
- Precondition: $(D b) has been allocated with this allocator, or is $(D
+ Precondition: `b` has been allocated with this allocator, or is $(D
null).
*/
bool deallocate(void[] b)
@@ -634,8 +733,7 @@ struct ContiguousFreeList(ParentAllocator,
if (support.ptr <= b.ptr && b.ptr < support.ptr + support.length)
{
// we own this guy
- import std.conv : text;
- assert(fl.freeListEligible(b.length), text(b.length));
+ assert(fl.freeListEligible(b.length));
assert(allocated);
--allocated;
// Put manually in the freelist
@@ -692,21 +790,23 @@ struct ContiguousFreeList(ParentAllocator,
alias A = ContiguousFreeList!(NullAllocator, 0, 64);
auto a = A(new ubyte[1024]);
- assert(a.empty == Ternary.yes);
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
- assert(a.goodAllocSize(15) == 64);
- assert(a.goodAllocSize(65) == NullAllocator.instance.goodAllocSize(65));
+ assert((() pure nothrow @safe @nogc => a.goodAllocSize(15))() == 64);
+ assert((() pure nothrow @safe @nogc => a.goodAllocSize(65))()
+ == (() nothrow @safe @nogc => NullAllocator.instance.goodAllocSize(65))());
auto b = a.allocate(100);
- assert(a.empty == Ternary.yes);
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
assert(b.length == 0);
- a.deallocate(b);
+ // Ensure deallocate inherits from parent
+ () nothrow @nogc { a.deallocate(b); }();
b = a.allocate(64);
- assert(a.empty == Ternary.no);
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.no);
assert(b.length == 64);
- assert(a.owns(b) == Ternary.yes);
- assert(a.owns(null) == Ternary.no);
- a.deallocate(b);
+ assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+ assert((() nothrow @safe @nogc => a.owns(null))() == Ternary.no);
+ () nothrow @nogc { a.deallocate(b); }();
}
@system unittest
@@ -717,23 +817,29 @@ struct ContiguousFreeList(ParentAllocator,
alias A = ContiguousFreeList!(Region!GCAllocator, 0, 64);
auto a = A(Region!GCAllocator(1024 * 4), 1024);
- assert(a.empty == Ternary.yes);
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
- assert(a.goodAllocSize(15) == 64);
- assert(a.goodAllocSize(65) == a.parent.goodAllocSize(65));
+ assert((() pure nothrow @safe @nogc => a.goodAllocSize(15))() == 64);
+ assert((() pure nothrow @safe @nogc => a.goodAllocSize(65))()
+ == (() pure nothrow @safe @nogc => a.parent.goodAllocSize(65))());
auto b = a.allocate(100);
- assert(a.empty == Ternary.no);
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.no);
assert(a.allocated == 0);
assert(b.length == 100);
- a.deallocate(b);
- assert(a.empty == Ternary.yes);
+ // Ensure deallocate inherits from parent
+ assert((() nothrow @nogc => a.deallocate(b))());
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
b = a.allocate(64);
- assert(a.empty == Ternary.no);
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.no);
assert(b.length == 64);
- assert(a.owns(b) == Ternary.yes);
- assert(a.owns(null) == Ternary.no);
- a.deallocate(b);
+ assert(a.reallocate(b, 100));
+ assert(b.length == 100);
+ assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+ assert((() nothrow @safe @nogc => a.owns(null))() == Ternary.no);
+ // Test deallocate infers from parent
+ assert((() nothrow @nogc => a.deallocate(b))());
+ assert((() nothrow @nogc => a.deallocateAll())());
}
@system unittest
@@ -747,10 +853,10 @@ struct ContiguousFreeList(ParentAllocator,
/**
FreeList shared across threads. Allocation and deallocation are lock-free. The
-parameters have the same semantics as for $(D FreeList).
+parameters have the same semantics as for `FreeList`.
-$(D expand) is defined to forward to $(D ParentAllocator.expand)
-(it must be also $(D shared)).
+`expand` is defined to forward to `ParentAllocator.expand`
+(it must be also `shared`).
*/
struct SharedFreeList(ParentAllocator,
size_t minSize, size_t maxSize = minSize, size_t approxMaxNodes = unbounded)
@@ -759,6 +865,11 @@ struct SharedFreeList(ParentAllocator,
import std.exception : enforce;
import std.traits : hasMember;
+ static if (hasMember!(ParentAllocator, "owns"))
+ {
+ import std.typecons : Ternary;
+ }
+
static assert(approxMaxNodes, "approxMaxNodes must not be null.");
static assert(minSize != unbounded, "Use minSize = 0 for no low bound.");
static assert(maxSize >= (void*).sizeof,
@@ -884,7 +995,7 @@ struct SharedFreeList(ParentAllocator,
/**
Properties for getting (and possibly setting) the bounds. Setting bounds
is allowed only once , and before any allocation takes place. Otherwise,
- the primitives have the same semantics as those of $(D FreeList).
+ the primitives have the same semantics as those of `FreeList`.
*/
@property size_t min();
/// Ditto
@@ -895,20 +1006,6 @@ struct SharedFreeList(ParentAllocator,
@property void max(size_t newMaxSize);
/// Ditto
void setBounds(size_t newMin, size_t newMax);
- ///
- @safe unittest
- {
- import std.experimental.allocator.common : chooseAtRuntime;
- import std.experimental.allocator.mallocator : Mallocator;
-
- shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a;
- // Set the maxSize first so setting the minSize doesn't throw
- a.max = 128;
- a.min = 64;
- a.setBounds(64, 128); // equivalent
- assert(a.max == 128);
- assert(a.min == 64);
- }
/**
Properties for getting (and possibly setting) the approximate maximum length of a shared freelist.
@@ -916,25 +1013,10 @@ struct SharedFreeList(ParentAllocator,
@property size_t approxMaxLength() const shared;
/// ditto
@property void approxMaxLength(size_t x) shared;
- ///
- @safe unittest
- {
- import std.experimental.allocator.common : chooseAtRuntime;
- import std.experimental.allocator.mallocator : Mallocator;
-
- shared SharedFreeList!(Mallocator, 50, 50, chooseAtRuntime) a;
- // Set the maxSize first so setting the minSize doesn't throw
- a.approxMaxLength = 128;
- assert(a.approxMaxLength == 128);
- a.approxMaxLength = 1024;
- assert(a.approxMaxLength == 1024);
- a.approxMaxLength = 1;
- assert(a.approxMaxLength == 1);
- }
}
/**
- The parent allocator. Depending on whether $(D ParentAllocator) holds state
+ The parent allocator. Depending on whether `ParentAllocator` holds state
or not, this is a member variable or an alias for
`ParentAllocator.instance`.
*/
@@ -961,7 +1043,7 @@ struct SharedFreeList(ParentAllocator,
/// Ditto
static if (hasMember!(ParentAllocator, "owns"))
- Ternary owns(void[] b) shared const
+ Ternary owns(const void[] b) shared const
{
return parent.owns(b);
}
@@ -1050,8 +1132,8 @@ struct SharedFreeList(ParentAllocator,
/**
Nonstandard function that minimizes the memory usage of the freelist by
- freeing each element in turn. Defined only if $(D ParentAllocator) defines
- $(D deallocate).
+ freeing each element in turn. Defined only if `ParentAllocator` defines
+ `deallocate`.
*/
static if (hasMember!(ParentAllocator, "deallocate") && !unchecked)
void minimize() shared
@@ -1071,6 +1153,34 @@ struct SharedFreeList(ParentAllocator,
}
}
+///
+@safe unittest
+{
+ import std.experimental.allocator.common : chooseAtRuntime;
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a;
+ a.setBounds(64, 128);
+ assert(a.max == 128);
+ assert(a.min == 64);
+}
+
+///
+@safe unittest
+{
+ import std.experimental.allocator.common : chooseAtRuntime;
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ shared SharedFreeList!(Mallocator, 50, 50, chooseAtRuntime) a;
+ // Set the maxSize first so setting the minSize doesn't throw
+ a.approxMaxLength = 128;
+ assert(a.approxMaxLength == 128);
+ a.approxMaxLength = 1024;
+ assert(a.approxMaxLength == 1024);
+ a.approxMaxLength = 1;
+ assert(a.approxMaxLength == 1);
+}
+
@system unittest
{
import core.thread : ThreadGroup;
@@ -1080,10 +1190,11 @@ struct SharedFreeList(ParentAllocator,
static shared SharedFreeList!(Mallocator, 64, 128, 10) a;
- assert(a.goodAllocSize(1) == platformAlignment);
+ assert((() nothrow @safe @nogc => a.goodAllocSize(1))() == platformAlignment);
auto b = a.allocate(96);
- a.deallocate(b);
+ // Ensure deallocate inherits from parent
+ () nothrow @nogc { a.deallocate(b); }();
void fun()
{
@@ -1091,7 +1202,7 @@ struct SharedFreeList(ParentAllocator,
b[] = cast(size_t) &b;
assert(b.equal(repeat(cast(size_t) &b, b.length)));
- a.deallocate(b);
+ () nothrow @nogc { a.deallocate(b); }();
}
auto tg = new ThreadGroup;
@@ -1108,10 +1219,11 @@ struct SharedFreeList(ParentAllocator,
import std.experimental.allocator.mallocator : Mallocator;
static shared SharedFreeList!(Mallocator, 64, 128, 10) a;
auto b = a.allocate(100);
- a.deallocate(b);
+ // Ensure deallocate inherits from parent
+ () nothrow @nogc { a.deallocate(b); }();
assert(a.nodes == 1);
b = [];
- a.deallocateAll();
+ assert((() nothrow @nogc => a.deallocateAll())());
assert(a.nodes == 0);
}
@@ -1121,12 +1233,13 @@ struct SharedFreeList(ParentAllocator,
static shared SharedFreeList!(Mallocator, 64, 128, 10) a;
auto b = a.allocate(100);
auto c = a.allocate(100);
- a.deallocate(c);
+ // Ensure deallocate inherits from parent
+ () nothrow @nogc { a.deallocate(c); }();
assert(a.nodes == 1);
c = [];
a.minimize();
assert(a.nodes == 0);
- a.deallocate(b);
+ () nothrow @nogc { a.deallocate(b); }();
assert(a.nodes == 1);
b = [];
a.minimize();
@@ -1140,8 +1253,9 @@ struct SharedFreeList(ParentAllocator,
auto b = a.allocate(100);
auto c = a.allocate(100);
assert(a.nodes == 0);
- a.deallocate(b);
- a.deallocate(c);
+ // Ensure deallocate inherits from parent
+ () nothrow @nogc { a.deallocate(b); }();
+ () nothrow @nogc { a.deallocate(c); }();
assert(a.nodes == 2);
b = [];
c = [];
@@ -1153,18 +1267,19 @@ struct SharedFreeList(ParentAllocator,
{
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a;
- scope(exit) a.deallocateAll();
+ scope(exit) assert((() nothrow @nogc => a.deallocateAll())());
auto c = a.allocate(64);
- assert(a.reallocate(c, 96));
+ assert((() nothrow @nogc => a.reallocate(c, 96))());
assert(c.length == 96);
- a.deallocate(c);
+ // Ensure deallocate inherits from parent
+ () nothrow @nogc { a.deallocate(c); }();
}
@system unittest
{
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime, chooseAtRuntime) a;
- scope(exit) a.deallocateAll;
+ scope(exit) assert((() nothrow @nogc => a.deallocateAll())());
a.allocate(64);
}
@@ -1172,7 +1287,7 @@ struct SharedFreeList(ParentAllocator,
{
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, 30, 40) a;
- scope(exit) a.deallocateAll;
+ scope(exit) assert((() nothrow @nogc => a.deallocateAll())());
a.allocate(64);
}
@@ -1180,7 +1295,7 @@ struct SharedFreeList(ParentAllocator,
{
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, 30, 40, chooseAtRuntime) a;
- scope(exit) a.deallocateAll;
+ scope(exit) assert((() nothrow @nogc => a.deallocateAll())());
a.allocate(64);
}
@@ -1189,7 +1304,7 @@ struct SharedFreeList(ParentAllocator,
// Pull request #5556
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, 0, chooseAtRuntime) a;
- scope(exit) a.deallocateAll;
+ scope(exit) assert((() nothrow @nogc => a.deallocateAll())());
a.max = 64;
a.allocate(64);
}
@@ -1199,7 +1314,7 @@ struct SharedFreeList(ParentAllocator,
// Pull request #5556
import std.experimental.allocator.mallocator : Mallocator;
shared SharedFreeList!(Mallocator, chooseAtRuntime, 64) a;
- scope(exit) a.deallocateAll;
+ scope(exit) assert((() nothrow @nogc => a.deallocateAll())());
a.min = 32;
a.allocate(64);
}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d b/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d
index 6b64659297e..bd4bb9511ea 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d
@@ -1,4 +1,7 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/_free_tree.d)
+*/
module std.experimental.allocator.building_blocks.free_tree;
import std.experimental.allocator.common;
@@ -13,10 +16,10 @@ previously freed blocks, it maintains a binary search tree. This allows the
Free Tree allocator to manage blocks of arbitrary lengths and search them
efficiently.
-Common uses of $(D FreeTree) include:
+Common uses of `FreeTree` include:
$(UL
-$(LI Adding $(D deallocate) capability to an allocator that lacks it (such as simple regions).)
+$(LI Adding `deallocate` capability to an allocator that lacks it (such as simple regions).)
$(LI Getting the benefits of multiple adaptable freelists that do not need to
be tuned for one specific size but insted automatically adapts itself to
frequently used sizes.)
@@ -24,14 +27,14 @@ frequently used sizes.)
The free tree has special handling of duplicates (a singly-linked list per
node) in anticipation of large number of duplicates. Allocation time from the
-free tree is expected to be $(BIGOH log n) where $(D n) is the number of
+free tree is expected to be $(BIGOH log n) where `n` is the number of
distinct sizes (not total nodes) kept in the free tree.
Allocation requests first search the tree for a buffer of suitable size
deallocated in the past. If a match is found, the node is removed from the tree
and the memory is returned. Otherwise, the allocation is directed to $(D
-ParentAllocator). If at this point $(D ParentAllocator) also fails to allocate,
-$(D FreeTree) frees everything and then tries the parent allocator again.
+ParentAllocator). If at this point `ParentAllocator` also fails to allocate,
+`FreeTree` frees everything and then tries the parent allocator again.
Upon deallocation, the deallocated block is inserted in the internally
maintained free tree (not returned to the parent). The free tree is not kept
@@ -40,12 +43,12 @@ blocks are rotated to the root of the tree. That way allocations are cache
friendly and also frequently used sizes are more likely to be found quickly,
whereas seldom used sizes migrate to the leaves of the tree.
-$(D FreeTree) rounds up small allocations to at least $(D 4 * size_t.sizeof),
+`FreeTree` rounds up small allocations to at least $(D 4 * size_t.sizeof),
which on 64-bit system is one cache line size. If very small objects need to
-be efficiently allocated, the $(D FreeTree) should be fronted with an
+be efficiently allocated, the `FreeTree` should be fronted with an
appropriate small object allocator.
-The following methods are defined if $(D ParentAllocator) defines them, and forward to it: $(D allocateAll), $(D expand), $(D owns), $(D reallocate).
+The following methods are defined if `ParentAllocator` defines them, and forward to it: `allocateAll`, `expand`, `owns`, `reallocate`.
*/
struct FreeTree(ParentAllocator)
{
@@ -240,17 +243,17 @@ struct FreeTree(ParentAllocator)
}
/**
- The $(D FreeTree) is word aligned.
+ The `FreeTree` is word aligned.
*/
enum uint alignment = size_t.alignof;
/**
- The $(D FreeTree) allocator is noncopyable.
+ The `FreeTree` allocator is noncopyable.
*/
this(this) @disable;
/**
- The destructor of $(D FreeTree) releases all memory back to the parent
+ The destructor of `FreeTree` releases all memory back to the parent
allocator.
*/
static if (hasMember!(ParentAllocator, "deallocate"))
@@ -275,14 +278,14 @@ struct FreeTree(ParentAllocator)
/**
- Allocates $(D n) bytes of memory. First consults the free tree, and returns
+ Allocates `n` bytes of memory. First consults the free tree, and returns
from it if a suitably sized block is found. Otherwise, the parent allocator
is tried. If allocation from the parent succeeds, the allocated block is
returned. Otherwise, the free tree tries an alternate strategy: If $(D
- ParentAllocator) defines $(D deallocate), $(D FreeTree) releases all of its
+ ParentAllocator) defines `deallocate`, `FreeTree` releases all of its
contents and tries again.
- TODO: Splitting and coalescing should be implemented if $(D ParentAllocator) does not defined $(D deallocate).
+ TODO: Splitting and coalescing should be implemented if `ParentAllocator` does not defined `deallocate`.
*/
void[] allocate(size_t n)
@@ -320,7 +323,7 @@ struct FreeTree(ParentAllocator)
mixin(forwardToMember("parent",
"allocateAll", "expand", "owns", "reallocate"));
- /** Places $(D b) into the free tree. */
+ /** Places `b` into the free tree. */
bool deallocate(void[] b)
{
if (!b.ptr) return true;
@@ -340,9 +343,9 @@ struct FreeTree(ParentAllocator)
auto b2 = a.allocate(20000);
auto b3 = a.allocate(30000);
assert(b1.ptr && b2.ptr && b3.ptr);
- a.deallocate(b1);
- a.deallocate(b3);
- a.deallocate(b2);
+ () nothrow @nogc { a.deallocate(b1); }();
+ () nothrow @nogc { a.deallocate(b3); }();
+ () nothrow @nogc { a.deallocate(b2); }();
assert(a.formatSizes == "(20480 (12288 32768))", a.formatSizes);
b1 = a.allocate(10000);
@@ -365,7 +368,7 @@ struct FreeTree(ParentAllocator)
foreach_reverse (b; allocs)
{
assert(b.ptr);
- a.deallocate(b);
+ () nothrow @nogc { a.deallocate(b); }();
}
a.assertValid;
allocs = null;
@@ -375,7 +378,7 @@ struct FreeTree(ParentAllocator)
a.assertValid;
}
- /** Defined if $(D ParentAllocator.deallocate) exists, and returns to it
+ /** Defined if `ParentAllocator.deallocate` exists, and returns to it
all memory held in the free tree. */
static if (hasMember!(ParentAllocator, "deallocate"))
void clear()
@@ -393,7 +396,7 @@ struct FreeTree(ParentAllocator)
/**
- Defined if $(D ParentAllocator.deallocateAll) exists, and forwards to it.
+ Defined if `ParentAllocator.deallocateAll` exists, and forwards to it.
Also nullifies the free tree (it's assumed the parent frees all memory
stil managed by the free tree).
@@ -408,13 +411,15 @@ struct FreeTree(ParentAllocator)
}
}
+version (StdUnittest)
@system unittest
{
import std.experimental.allocator.gc_allocator;
testAllocator!(() => FreeTree!GCAllocator());
}
-@system unittest // issue 16506
+// https://issues.dlang.org/show_bug.cgi?id=16506
+@system unittest
{
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.mallocator : Mallocator;
@@ -425,7 +430,7 @@ struct FreeTree(ParentAllocator)
byte[] _payload = cast(byte[]) myAlloc.allocate(sz);
assert(_payload, "_payload is null");
_payload[] = 0;
- myAlloc.deallocate(_payload);
+ () nothrow @nogc { myAlloc.deallocate(_payload); }();
}
f!Mallocator(33);
@@ -433,7 +438,8 @@ struct FreeTree(ParentAllocator)
f!GCAllocator(1);
}
-@system unittest // issue 16507
+// https://issues.dlang.org/show_bug.cgi?id=16507
+@system unittest
{
static struct MyAllocator
{
@@ -446,7 +452,7 @@ struct FreeTree(ParentAllocator)
FreeTree!MyAllocator ft;
void[] x = ft.allocate(1);
- ft.deallocate(x);
+ () nothrow @nogc { ft.deallocate(x); }();
ft.allocate(1000);
MyAllocator.alive = false;
}
@@ -475,7 +481,7 @@ struct FreeTree(ParentAllocator)
FreeTree!MyAllocator ft;
void[] x = ft.allocate(1);
- ft.deallocate(x);
+ () nothrow @nogc { ft.deallocate(x); }();
assert(myDeallocCounter == 0);
x = ft.allocate(1000); // Triggers "desperation mode".
assert(myDeallocCounter == 1);
@@ -485,3 +491,25 @@ struct FreeTree(ParentAllocator)
assert(myDeallocCounter == 1);
assert(y.ptr is null);
}
+
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator;
+ FreeTree!GCAllocator a;
+
+ assert((() nothrow @safe @nogc => a.goodAllocSize(1))() == typeof(*a.root).sizeof);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+
+ auto a = FreeTree!(Region!())(Region!()(new ubyte[1024 * 64]));
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
+ assert(b.length == 64);
+ assert((() nothrow @nogc => a.reallocate(b, 100))());
+ assert(b.length == 100);
+ assert((() nothrow @nogc => a.deallocateAll())());
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d
index 555daba3141..65226e7f4cb 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d
@@ -1,14 +1,17 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/kernighan_ritchie.d)
+*/
module std.experimental.allocator.building_blocks.kernighan_ritchie;
-import std.experimental.allocator.building_blocks.null_allocator;
+import std.experimental.allocator.building_blocks.null_allocator :
+ NullAllocator;
//debug = KRRegion;
-version (unittest) import std.conv : text;
debug(KRRegion) import std.stdio;
// KRRegion
/**
-$(D KRRegion) draws inspiration from the $(MREF_ALTTEXT region allocation
+`KRRegion` draws inspiration from the $(MREF_ALTTEXT region allocation
strategy, std,experimental,allocator,building_blocks,region) and also the
$(HTTP stackoverflow.com/questions/13159564/explain-this-implementation-of-malloc-from-the-kr-book,
famed allocator) described by Brian Kernighan and Dennis Ritchie in section 8.7
@@ -19,11 +22,11 @@ $(H4 `KRRegion` = `Region` + Kernighan-Ritchie Allocator)
Initially, `KRRegion` starts in "region" mode: allocations are served from
the memory chunk in a region fashion. Thus, as long as there is enough memory
-left, $(D KRRegion.allocate) has the performance profile of a region allocator.
+left, `KRRegion.allocate` has the performance profile of a region allocator.
Deallocation inserts (in $(BIGOH 1) time) the deallocated blocks in an
unstructured freelist, which is not read in region mode.
-Once the region cannot serve an $(D allocate) request, $(D KRRegion) switches
+Once the region cannot serve an `allocate` request, `KRRegion` switches
to "free list" mode. It sorts the list of previously deallocated blocks by
address and serves allocation requests off that free list. The allocation and
deallocation follow the pattern described by Kernighan and Ritchie.
@@ -47,17 +50,17 @@ systems, 8 bytes on 32-bit systems). This is because the free list management
needs two words (one for the length, the other for the next pointer in the
singly-linked list).
-The $(D ParentAllocator) type parameter is the type of the allocator used to
-allocate the memory chunk underlying the $(D KRRegion) object. Choosing the
-default ($(D NullAllocator)) means the user is responsible for passing a buffer
-at construction (and for deallocating it if necessary). Otherwise, $(D KRRegion)
+The `ParentAllocator` type parameter is the type of the allocator used to
+allocate the memory chunk underlying the `KRRegion` object. Choosing the
+default (`NullAllocator`) means the user is responsible for passing a buffer
+at construction (and for deallocating it if necessary). Otherwise, `KRRegion`
automatically deallocates the buffer during destruction. For that reason, if
-$(D ParentAllocator) is not $(D NullAllocator), then $(D KRRegion) is not
+`ParentAllocator` is not `NullAllocator`, then `KRRegion` is not
copyable.
$(H4 Implementation Details)
-In free list mode, $(D KRRegion) embeds a free blocks list onto the chunk of
+In free list mode, `KRRegion` embeds a free blocks list onto the chunk of
memory. The free list is circular, coalesced, and sorted by address at all
times. Allocations and deallocations take time proportional to the number of
previously deallocated blocks. (In practice the cost may be lower, e.g. if
@@ -84,9 +87,9 @@ Differences from the Kernighan-Ritchie allocator:
$(UL
$(LI Once the chunk is exhausted, the Kernighan-Ritchie allocator allocates
another chunk using operating system primitives. For better composability, $(D
-KRRegion) just gets full (returns $(D null) on new allocation requests). The
+KRRegion) just gets full (returns `null` on new allocation requests). The
decision to allocate more blocks is deferred to a higher-level entity. For an
-example, see the example below using $(D AllocatorList) in conjunction with $(D
+example, see the example below using `AllocatorList` in conjunction with $(D
KRRegion).)
$(LI Allocated blocks do not hold a size prefix. This is because in D the size
information is available in client code at deallocation time.)
@@ -163,15 +166,17 @@ struct KRRegion(ParentAllocator = NullAllocator)
// state
/**
- If $(D ParentAllocator) holds state, $(D parent) is a public member of type
- $(D KRRegion). Otherwise, $(D parent) is an $(D alias) for
+ If `ParentAllocator` holds state, `parent` is a public member of type
+ `KRRegion`. Otherwise, `parent` is an `alias` for
`ParentAllocator.instance`.
*/
static if (stateSize!ParentAllocator) ParentAllocator parent;
else alias parent = ParentAllocator.instance;
private void[] payload;
private Node* root;
- private bool regionMode = true;
+ private bool regionMode() const { return bytesUsedRegionMode != size_t.max; }
+ private void cancelRegionMode() { bytesUsedRegionMode = size_t.max; }
+ private size_t bytesUsedRegionMode = 0;
auto byNodePtr()
{
@@ -302,14 +307,14 @@ struct KRRegion(ParentAllocator = NullAllocator)
}
/**
- Create a $(D KRRegion). If $(D ParentAllocator) is not $(D NullAllocator),
- $(D KRRegion)'s destructor will call $(D parent.deallocate).
+ Create a `KRRegion`. If `ParentAllocator` is not `NullAllocator`,
+ `KRRegion`'s destructor will call `parent.deallocate`.
Params:
b = Block of memory to serve as support for the allocator. Memory must be
larger than two words and word-aligned.
n = Capacity desired. This constructor is defined only if $(D
- ParentAllocator) is not $(D NullAllocator).
+ ParentAllocator) is not `NullAllocator`.
*/
this(ubyte[] b)
{
@@ -334,7 +339,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
}
/// Ditto
- static if (!is(ParentAllocator == NullAllocator))
+ static if (!is(ParentAllocator == NullAllocator) && !stateSize!ParentAllocator)
this(size_t n)
{
assert(n > Node.sizeof);
@@ -342,6 +347,15 @@ struct KRRegion(ParentAllocator = NullAllocator)
}
/// Ditto
+ static if (!is(ParentAllocator == NullAllocator) && stateSize!ParentAllocator)
+ this(ParentAllocator parent, size_t n)
+ {
+ assert(n > Node.sizeof);
+ this.parent = parent;
+ this(cast(ubyte[])(parent.allocate(n)));
+ }
+
+ /// Ditto
static if (!is(ParentAllocator == NullAllocator)
&& hasMember!(ParentAllocator, "deallocate"))
~this()
@@ -357,7 +371,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
void switchToFreeList()
{
if (!regionMode) return;
- regionMode = false;
+ cancelRegionMode;
if (!root) return;
root = sortFreelist(root);
coalesceAndMakeCircular;
@@ -374,13 +388,13 @@ struct KRRegion(ParentAllocator = NullAllocator)
enum alignment = Node.alignof;
/**
- Allocates $(D n) bytes. Allocation searches the list of available blocks
- until a free block with $(D n) or more bytes is found (first fit strategy).
+ Allocates `n` bytes. Allocation searches the list of available blocks
+ until a free block with `n` or more bytes is found (first fit strategy).
The block is split (if larger) and returned.
Params: n = number of bytes to _allocate
- Returns: A word-aligned buffer of $(D n) bytes, or $(D null).
+ Returns: A word-aligned buffer of `n` bytes, or `null`.
*/
void[] allocate(size_t n)
{
@@ -394,6 +408,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
if (root.size >= actualBytes)
{
// Enough room for allocation
+ bytesUsedRegionMode += actualBytes;
void* result = root;
immutable balance = root.size - actualBytes;
if (balance >= Node.sizeof)
@@ -436,13 +451,14 @@ struct KRRegion(ParentAllocator = NullAllocator)
}
/**
- Deallocates $(D b), which is assumed to have been previously allocated with
+ Deallocates `b`, which is assumed to have been previously allocated with
this allocator. Deallocation performs a linear search in the free list to
preserve its sorting order. It follows that blocks with higher addresses in
allocators with many free blocks are slower to deallocate.
Params: b = block to be deallocated
*/
+ nothrow @nogc
bool deallocate(void[] b)
{
debug(KRRegion) writefln("KRRegion@%s: deallocate(%s[%s])", &this,
@@ -461,6 +477,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
{
assert(root);
// Insert right after root
+ bytesUsedRegionMode -= n.size;
n.next = root.next;
root.next = n;
return true;
@@ -489,9 +506,10 @@ struct KRRegion(ParentAllocator = NullAllocator)
assert(pnode && pnode.next);
assert(pnode != n);
assert(pnode.next != n);
+
if (pnode < pnode.next)
{
- if (pnode >= n || n >= pnode.next) continue;
+ if (pnode > n || n > pnode.next) continue;
// Insert in between pnode and pnode.next
n.next = pnode.next;
pnode.next = n;
@@ -528,13 +546,13 @@ struct KRRegion(ParentAllocator = NullAllocator)
/**
Allocates all memory available to this allocator. If the allocator is empty,
returns the entire available block of memory. Otherwise, it still performs
- a best-effort allocation: if there is no fragmentation (e.g. $(D allocate)
- has been used but not $(D deallocate)), allocates and returns the only
+ a best-effort allocation: if there is no fragmentation (e.g. `allocate`
+ has been used but not `deallocate`), allocates and returns the only
available block of memory.
The operation takes time proportional to the number of adjacent free blocks
at the front of the free list. These blocks get coalesced, whether
- $(D allocateAll) succeeds or fails due to fragmentation.
+ `allocateAll` succeeds or fails due to fragmentation.
*/
void[] allocateAll()
{
@@ -559,37 +577,42 @@ struct KRRegion(ParentAllocator = NullAllocator)
Deallocates all memory currently allocated, making the allocator ready for
other allocations. This is a $(BIGOH 1) operation.
*/
+ pure nothrow @nogc
bool deallocateAll()
{
debug(KRRegion) assertValid("deallocateAll");
debug(KRRegion) scope(exit) assertValid("deallocateAll");
root = cast(Node*) payload.ptr;
- // Initialize the free list with all list
+
+ // Reset to regionMode
+ bytesUsedRegionMode = 0;
if (root)
{
- root.next = root;
+ root.next = null;
root.size = payload.length;
}
return true;
}
/**
- Checks whether the allocator is responsible for the allocation of $(D b).
- It does a simple $(BIGOH 1) range check. $(D b) should be a buffer either
- allocated with $(D this) or obtained through other means.
+ Checks whether the allocator is responsible for the allocation of `b`.
+ It does a simple $(BIGOH 1) range check. `b` should be a buffer either
+ allocated with `this` or obtained through other means.
*/
+ pure nothrow @trusted @nogc
Ternary owns(void[] b)
{
debug(KRRegion) assertValid("owns");
debug(KRRegion) scope(exit) assertValid("owns");
- return Ternary(b.ptr >= payload.ptr
- && b.ptr < payload.ptr + payload.length);
+ return Ternary(b && payload && (&b[0] >= &payload[0])
+ && (&b[0] < &payload[0] + payload.length));
}
/**
- Adjusts $(D n) to a size suitable for allocation (two words or larger,
+ Adjusts `n` to a size suitable for allocation (two words or larger,
word-aligned).
*/
+ pure nothrow @safe @nogc
static size_t goodAllocSize(size_t n)
{
import std.experimental.allocator.common : roundUpToMultipleOf;
@@ -601,16 +624,20 @@ struct KRRegion(ParentAllocator = NullAllocator)
Returns: `Ternary.yes` if the allocator is empty, `Ternary.no` otherwise.
Never returns `Ternary.unknown`.
*/
+ pure nothrow @safe @nogc
Ternary empty()
{
+ if (regionMode)
+ return Ternary(bytesUsedRegionMode == 0);
+
return Ternary(root && root.size == payload.length);
}
}
/**
-$(D KRRegion) is preferable to $(D Region) as a front for a general-purpose
-allocator if $(D deallocate) is needed, yet the actual deallocation traffic is
-relatively low. The example below shows a $(D KRRegion) using stack storage
+`KRRegion` is preferable to `Region` as a front for a general-purpose
+allocator if `deallocate` is needed, yet the actual deallocation traffic is
+relatively low. The example below shows a `KRRegion` using stack storage
fronting the GC allocator.
*/
@system unittest
@@ -624,7 +651,7 @@ fronting the GC allocator.
auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance);
auto b = alloc.allocate(100);
assert(b.length == 100);
- assert(alloc.primary.owns(b) == Ternary.yes);
+ assert((() pure nothrow @safe @nogc => alloc.primary.owns(b))() == Ternary.yes);
}
/**
@@ -642,7 +669,6 @@ it actually returns memory to the operating system when possible.
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
- import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.mmap_allocator : MmapAllocator;
AllocatorList!(n => KRRegion!MmapAllocator(max(n * 16, 1024 * 1024))) alloc;
}
@@ -652,7 +678,6 @@ it actually returns memory to the operating system when possible.
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
- import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.mallocator : Mallocator;
import std.typecons : Ternary;
/*
@@ -675,8 +700,8 @@ it actually returns memory to the operating system when possible.
foreach (i; 0 .. array.length)
{
assert(array[i].ptr);
- assert(alloc.owns(array[i]) == Ternary.yes);
- alloc.deallocate(array[i]);
+ assert((() pure nothrow @safe @nogc => alloc.owns(array[i]))() == Ternary.yes);
+ () nothrow @nogc { alloc.deallocate(array[i]); }();
}
}
@@ -685,7 +710,6 @@ it actually returns memory to the operating system when possible.
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
- import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.mmap_allocator : MmapAllocator;
import std.typecons : Ternary;
/*
@@ -713,11 +737,12 @@ it actually returns memory to the operating system when possible.
randomShuffle(array[]);
foreach (i; 0 .. array.length)
{
- assert(alloc.owns(array[i]) == Ternary.yes);
- alloc.deallocate(array[i]);
+ assert((() pure nothrow @safe @nogc => alloc.owns(array[i]))() == Ternary.yes);
+ () nothrow @nogc { alloc.deallocate(array[i]); }();
}
}
+version (StdUnittest)
@system unittest
{
import std.algorithm.comparison : max;
@@ -741,9 +766,9 @@ it actually returns memory to the operating system when possible.
array ~= alloc.allocate(i);
assert(array[$ - 1].length == i);
}
- alloc.deallocate(array[1]);
- alloc.deallocate(array[0]);
- alloc.deallocate(array[2]);
+ () nothrow @nogc { alloc.deallocate(array[1]); }();
+ () nothrow @nogc { alloc.deallocate(array[0]); }();
+ () nothrow @nogc { alloc.deallocate(array[2]); }();
assert(alloc.allocateAll().length == 1024 * 1024);
}
@@ -755,9 +780,9 @@ it actually returns memory to the operating system when possible.
cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024)));
const store = alloc.allocate(KRRegion!().sizeof);
auto p = cast(KRRegion!()* ) store.ptr;
+ import core.lifetime : emplace;
import core.stdc.string : memcpy;
- import std.algorithm.mutation : move;
- import std.conv : emplace;
+ import std.conv : text;
memcpy(p, &alloc, alloc.sizeof);
emplace(&alloc);
@@ -768,14 +793,14 @@ it actually returns memory to the operating system when possible.
auto length = 100 * i + 1;
array[i] = p.allocate(length);
assert(array[i].length == length, text(array[i].length));
- assert(p.owns(array[i]) == Ternary.yes);
+ assert((() pure nothrow @safe @nogc => p.owns(array[i]))() == Ternary.yes);
}
import std.random : randomShuffle;
randomShuffle(array[]);
foreach (i; 0 .. array.length)
{
- assert(p.owns(array[i]) == Ternary.yes);
- p.deallocate(array[i]);
+ assert((() pure nothrow @safe @nogc => p.owns(array[i]))() == Ternary.yes);
+ () nothrow @nogc { p.deallocate(array[i]); }();
}
auto b = p.allocateAll();
assert(b.length == 1024 * 1024 - KRRegion!().sizeof, text(b.length));
@@ -783,20 +808,21 @@ it actually returns memory to the operating system when possible.
@system unittest
{
+ import std.typecons : Ternary;
import std.experimental.allocator.gc_allocator : GCAllocator;
auto alloc = KRRegion!()(
cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024)));
auto p = alloc.allocateAll();
assert(p.length == 1024 * 1024);
- alloc.deallocateAll();
+ assert((() nothrow @nogc => alloc.deallocateAll())());
+ assert(alloc.empty() == Ternary.yes);
p = alloc.allocateAll();
assert(p.length == 1024 * 1024);
}
@system unittest
{
- import std.experimental.allocator.building_blocks;
- import std.random;
+ import std.random : randomCover;
import std.typecons : Ternary;
// Both sequences must work on either system
@@ -825,10 +851,10 @@ it actually returns memory to the operating system when possible.
foreach (b; bufs.randomCover)
{
- a.deallocate(b);
+ () nothrow @nogc { a.deallocate(b); }();
}
- assert(a.empty == Ternary.yes);
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
}
test(sizes64);
@@ -837,8 +863,6 @@ it actually returns memory to the operating system when possible.
@system unittest
{
- import std.experimental.allocator.building_blocks;
- import std.random;
import std.typecons : Ternary;
// For 64 bits, we allocate in multiples of 8, but the minimum alloc size is 16.
@@ -865,18 +889,51 @@ it actually returns memory to the operating system when possible.
bufs ~= a.allocate(size);
}
- a.deallocate(bufs[1]);
+ () nothrow @nogc { a.deallocate(bufs[1]); }();
bufs ~= a.allocate(sizes[1] - word);
- a.deallocate(bufs[0]);
+ () nothrow @nogc { a.deallocate(bufs[0]); }();
foreach (i; 2 .. bufs.length)
{
- a.deallocate(bufs[i]);
+ () nothrow @nogc { a.deallocate(bufs[i]); }();
}
- assert(a.empty == Ternary.yes);
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
}
test(sizes64, word64);
test(sizes32, word32);
}
+
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+
+ auto a = KRRegion!GCAllocator(1024 * 1024);
+ assert((() pure nothrow @safe @nogc => a.goodAllocSize(1))() == typeof(*a.root).sizeof);
+}
+
+@system unittest
+{ import std.typecons : Ternary;
+
+ ubyte[1024] b;
+ auto alloc = KRRegion!()(b);
+
+ auto k = alloc.allocate(128);
+ assert(k.length == 128);
+ assert(alloc.empty == Ternary.no);
+ assert(alloc.deallocate(k));
+ assert(alloc.empty == Ternary.yes);
+
+ k = alloc.allocate(512);
+ assert(k.length == 512);
+ assert(alloc.empty == Ternary.no);
+ assert(alloc.deallocate(k));
+ assert(alloc.empty == Ternary.yes);
+
+ k = alloc.allocate(1024);
+ assert(k.length == 1024);
+ assert(alloc.empty == Ternary.no);
+ assert(alloc.deallocate(k));
+ assert(alloc.empty == Ternary.yes);
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/null_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/null_allocator.d
index 68bab708a87..06b4e1547b3 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/null_allocator.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/null_allocator.d
@@ -1,32 +1,37 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/null_allocator.d)
+*/
module std.experimental.allocator.building_blocks.null_allocator;
/**
-$(D NullAllocator) is an emphatically empty implementation of the allocator
+`NullAllocator` is an emphatically empty implementation of the allocator
interface. Although it has no direct use, it is useful as a "terminator" in
composite allocators.
*/
struct NullAllocator
{
import std.typecons : Ternary;
+
+ nothrow @nogc pure @safe:
/**
- $(D NullAllocator) advertises a relatively large _alignment equal to 64 KB.
- This is because $(D NullAllocator) never actually needs to honor this
- alignment and because composite allocators using $(D NullAllocator)
+ `NullAllocator` advertises a relatively large _alignment equal to 64 KB.
+ This is because `NullAllocator` never actually needs to honor this
+ alignment and because composite allocators using `NullAllocator`
shouldn't be unnecessarily constrained.
*/
enum uint alignment = 64 * 1024;
- // /// Returns $(D n).
+ // /// Returns `n`.
//size_t goodAllocSize(size_t n) shared const
//{ return .goodAllocSize(this, n); }
- /// Always returns $(D null).
+ /// Always returns `null`.
void[] allocate(size_t) shared { return null; }
- /// Always returns $(D null).
+ /// Always returns `null`.
void[] alignedAllocate(size_t, uint) shared { return null; }
- /// Always returns $(D null).
+ /// Always returns `null`.
void[] allocateAll() shared { return null; }
/**
- These methods return $(D false).
+ These methods return `false`.
Precondition: $(D b is null). This is because there is no other possible
legitimate input.
*/
@@ -38,10 +43,10 @@ struct NullAllocator
/// Ditto
bool alignedReallocate(ref void[] b, size_t, uint) shared
{ assert(b is null); return false; }
- /// Returns $(D Ternary.no).
- Ternary owns(void[]) shared const { return Ternary.no; }
+ /// Returns `Ternary.no`.
+ Ternary owns(const void[]) shared const { return Ternary.no; }
/**
- Returns $(D Ternary.no).
+ Returns `Ternary.no`.
*/
Ternary resolveInternalPointer(const void*, ref void[]) shared const
{ return Ternary.no; }
@@ -55,31 +60,34 @@ struct NullAllocator
*/
bool deallocateAll() shared { return true; }
/**
- Returns $(D Ternary.yes).
+ Returns `Ternary.yes`.
*/
Ternary empty() shared const { return Ternary.yes; }
/**
- Returns the $(D shared) global instance of the $(D NullAllocator).
+ Returns the `shared` global instance of the `NullAllocator`.
*/
static shared NullAllocator instance;
}
-@system unittest
+nothrow @nogc pure @safe unittest
{
- assert(NullAllocator.instance.alignedAllocate(100, 0) is null);
- assert(NullAllocator.instance.allocateAll() is null);
- auto b = NullAllocator.instance.allocate(100);
+ alias a = NullAllocator.instance;
+
+ assert(a.alignedAllocate(100, 0) is null);
+ assert(a.allocateAll() is null);
+ auto b = a.allocate(100);
assert(b is null);
- assert(NullAllocator.instance.expand(b, 0));
- assert(!NullAllocator.instance.expand(b, 42));
- assert(!NullAllocator.instance.reallocate(b, 42));
- assert(!NullAllocator.instance.alignedReallocate(b, 42, 0));
- NullAllocator.instance.deallocate(b);
- NullAllocator.instance.deallocateAll();
+ assert(a.expand(b, 0));
+ assert(!a.expand(b, 42));
+ assert(!a.reallocate(b, 42));
+ assert(!a.alignedReallocate(b, 42, 0));
+ assert(a.deallocate(b));
+ assert(a.deallocateAll());
import std.typecons : Ternary;
- assert(NullAllocator.instance.empty() == Ternary.yes);
- assert(NullAllocator.instance.owns(null) == Ternary.no);
+ assert(a.empty == Ternary.yes);
+ assert(a.owns(null) == Ternary.no);
+
void[] p;
- assert(NullAllocator.instance.resolveInternalPointer(null, p) == Ternary.no);
+ assert(a.resolveInternalPointer(null, p) == Ternary.no);
}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/package.d b/libphobos/src/std/experimental/allocator/building_blocks/package.d
index d55a16b4ab0..962ed91b882 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/package.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/package.d
@@ -1,9 +1,10 @@
+// Written in the D programming language.
/**
$(H2 Assembling Your Own Allocator)
-In addition to defining the interfaces above, this package also implements
+This package also implements
untyped composable memory allocators. They are $(I untyped) because they deal
-exclusively in $(D void[]) and have no notion of what type the memory allocated
+exclusively in `void[]` and have no notion of what type the memory allocated
would be destined for. They are $(I composable) because the included allocators
are building blocks that can be assembled in complex nontrivial allocators.
@@ -16,11 +17,11 @@ performance implications, and is virtually always redundant because client code
needs knowledge of the allocated size in order to avoid buffer overruns. (See
more discussion in a $(HTTP open-
std.org/JTC1/SC22/WG21/docs/papers/2013/n3536.html, proposal) for sized
-deallocation in C++.) For this reason, allocators herein traffic in $(D void[])
-as opposed to $(D void*).)
+deallocation in C++.) For this reason, allocators herein traffic in `void[]`
+as opposed to `void*`.)
$(P In order to be usable as an _allocator, a type should implement the
-following methods with their respective semantics. Only $(D alignment) and $(D
+following methods with their respective semantics. Only `alignment` and $(D
allocate) are required. If any of the other methods is missing, the _allocator
is assumed to not have that capability (for example some allocators do not offer
manual deallocation of memory). Allocators should NOT implement
@@ -35,23 +36,23 @@ $(TR $(TH Method name) $(TH Semantics))
$(TR $(TDC uint alignment;, $(POST $(RES) > 0)) $(TD Returns the minimum
alignment of all data returned by the allocator. An allocator may implement $(D
-alignment) as a statically-known $(D enum) value only. Applications that need
-dynamically-chosen alignment values should use the $(D alignedAllocate) and $(D
+alignment) as a statically-known `enum` value only. Applications that need
+dynamically-chosen alignment values should use the `alignedAllocate` and $(D
alignedReallocate) APIs.))
$(TR $(TDC size_t goodAllocSize(size_t n);, $(POST $(RES) >= n)) $(TD Allocators
customarily allocate memory in discretely-sized chunks. Therefore, a request for
-$(D n) bytes may result in a larger allocation. The extra memory allocated goes
+`n` bytes may result in a larger allocation. The extra memory allocated goes
unused and adds to the so-called $(HTTP goo.gl/YoKffF,internal fragmentation).
-The function $(D goodAllocSize(n)) returns the actual number of bytes that would
-be allocated upon a request for $(D n) bytes. This module defines a default
-implementation that returns $(D n) rounded up to a multiple of the allocator's
+The function `goodAllocSize(n)` returns the actual number of bytes that would
+be allocated upon a request for `n` bytes. This module defines a default
+implementation that returns `n` rounded up to a multiple of the allocator's
alignment.))
$(TR $(TDC void[] allocate(size_t s);, $(POST $(RES) is null || $(RES).length ==
s)) $(TD If $(D s == 0), the call may return any empty slice (including $(D
-null)). Otherwise, the call allocates $(D s) bytes of memory and returns the
-allocated block, or $(D null) if the request could not be satisfied.))
+null)). Otherwise, the call allocates `s` bytes of memory and returns the
+allocated block, or `null` if the request could not be satisfied.))
$(TR $(TDC void[] alignedAllocate(size_t s, uint a);, $(POST $(RES) is null ||
$(RES).length == s)) $(TD Similar to `allocate`, with the additional
@@ -60,41 +61,41 @@ must be a power of 2.))
$(TR $(TDC void[] allocateAll();) $(TD Offers all of allocator's memory to the
caller, so it's usually defined by fixed-size allocators. If the allocator is
-currently NOT managing any memory, then $(D allocateAll()) shall allocate and
+currently NOT managing any memory, then `allocateAll()` shall allocate and
return all memory available to the allocator, and subsequent calls to all
-allocation primitives should not succeed (e.g. $(D allocate) shall return $(D
-null) etc). Otherwise, $(D allocateAll) only works on a best-effort basis, and
-the allocator is allowed to return $(D null) even if does have available memory.
-Memory allocated with $(D allocateAll) is not otherwise special (e.g. can be
+allocation primitives should not succeed (e.g. `allocate` shall return $(D
+null) etc). Otherwise, `allocateAll` only works on a best-effort basis, and
+the allocator is allowed to return `null` even if does have available memory.
+Memory allocated with `allocateAll` is not otherwise special (e.g. can be
reallocated or deallocated with the usual primitives, if defined).))
$(TR $(TDC bool expand(ref void[] b, size_t delta);, $(POST !$(RES) || b.length
-== $(I old)(b).length + delta)) $(TD Expands $(D b) by $(D delta) bytes. If $(D
-delta == 0), succeeds without changing $(D b). If $(D b is null), returns
+== $(I old)(b).length + delta)) $(TD Expands `b` by `delta` bytes. If $(D
+delta == 0), succeeds without changing `b`. If $(D b is null), returns
`false` (the null pointer cannot be expanded in place). Otherwise, $(D
b) must be a buffer previously allocated with the same allocator. If expansion
-was successful, $(D expand) changes $(D b)'s length to $(D b.length + delta) and
-returns $(D true). Upon failure, the call effects no change upon the allocator
-object, leaves $(D b) unchanged, and returns $(D false).))
+was successful, `expand` changes `b`'s length to $(D b.length + delta) and
+returns `true`. Upon failure, the call effects no change upon the allocator
+object, leaves `b` unchanged, and returns `false`.))
$(TR $(TDC bool reallocate(ref void[] b, size_t s);, $(POST !$(RES) || b.length
-== s)) $(TD Reallocates $(D b) to size $(D s), possibly moving memory around.
-$(D b) must be $(D null) or a buffer allocated with the same allocator. If
-reallocation was successful, $(D reallocate) changes $(D b) appropriately and
-returns $(D true). Upon failure, the call effects no change upon the allocator
-object, leaves $(D b) unchanged, and returns $(D false). An allocator should
-implement $(D reallocate) if it can derive some advantage from doing so;
-otherwise, this module defines a $(D reallocate) free function implemented in
-terms of $(D expand), $(D allocate), and $(D deallocate).))
+== s)) $(TD Reallocates `b` to size `s`, possibly moving memory around.
+`b` must be `null` or a buffer allocated with the same allocator. If
+reallocation was successful, `reallocate` changes `b` appropriately and
+returns `true`. Upon failure, the call effects no change upon the allocator
+object, leaves `b` unchanged, and returns `false`. An allocator should
+implement `reallocate` if it can derive some advantage from doing so;
+otherwise, this module defines a `reallocate` free function implemented in
+terms of `expand`, `allocate`, and `deallocate`.))
$(TR $(TDC bool alignedReallocate(ref void[] b,$(BR) size_t s, uint a);, $(POST
-!$(RES) || b.length == s)) $(TD Similar to $(D reallocate), but guarantees the
-reallocated memory is aligned at $(D a) bytes. The buffer must have been
-originated with a call to $(D alignedAllocate). $(D a) must be a power of 2
-greater than $(D (void*).sizeof). An allocator should implement $(D
+!$(RES) || b.length == s)) $(TD Similar to `reallocate`, but guarantees the
+reallocated memory is aligned at `a` bytes. The buffer must have been
+originated with a call to `alignedAllocate`. `a` must be a power of 2
+greater than `(void*).sizeof`. An allocator should implement $(D
alignedReallocate) if it can derive some advantage from doing so; otherwise,
-this module defines a $(D alignedReallocate) free function implemented in terms
-of $(D expand), $(D alignedAllocate), and $(D deallocate).))
+this module defines a `alignedReallocate` free function implemented in terms
+of `expand`, `alignedAllocate`, and `deallocate`.))
$(TR $(TDC Ternary owns(void[] b);) $(TD Returns `Ternary.yes` if `b` has been
allocated with this allocator. An allocator should define this method only if it
@@ -128,23 +129,23 @@ $(TR $(TDC static Allocator instance;, $(POST instance $(I is a valid)
Allocator $(I object))) $(TD Some allocators are $(I monostate), i.e. have only
an instance and hold only global state. (Notable examples are C's own
`malloc`-based allocator and D's garbage-collected heap.) Such allocators must
-define a static $(D instance) instance that serves as the symbolic placeholder
+define a static `instance` instance that serves as the symbolic placeholder
for the global instance of the allocator. An allocator should not hold state
and define `instance` simultaneously. Depending on whether the allocator is
-thread-safe or not, this instance may be $(D shared).))
+thread-safe or not, this instance may be `shared`.))
)
$(H2 Sample Assembly)
The example below features an _allocator modeled after $(HTTP goo.gl/m7329l,
jemalloc), which uses a battery of free-list allocators spaced so as to keep
-internal fragmentation to a minimum. The $(D FList) definitions specify no
-bounds for the freelist because the $(D Segregator) does all size selection in
+internal fragmentation to a minimum. The `FList` definitions specify no
+bounds for the freelist because the `Segregator` does all size selection in
advance.
Sizes through 3584 bytes are handled via freelists of staggered sizes. Sizes
-from 3585 bytes through 4072 KB are handled by a $(D BitmappedBlock) with a
-block size of 4 KB. Sizes above that are passed direct to the $(D GCAllocator).
+from 3585 bytes through 4072 KB are handled by a `BitmappedBlock` with a
+block size of 4 KB. Sizes above that are passed direct to the `GCAllocator`.
----
alias FList = FreeList!(GCAllocator, 0, unbounded);
@@ -176,23 +177,23 @@ One allocation pattern used in multithreaded applications is to share memory
across threads, and to deallocate blocks in a different thread than the one that
allocated it.
-All allocators in this module accept and return $(D void[]) (as opposed to
+All allocators in this module accept and return `void[]` (as opposed to
$(D shared void[])). This is because at the time of allocation, deallocation, or
-reallocation, the memory is effectively not $(D shared) (if it were, it would
+reallocation, the memory is effectively not `shared` (if it were, it would
reveal a bug at the application level).
-The issue remains of calling $(D a.deallocate(b)) from a different thread than
-the one that allocated $(D b). It follows that both threads must have access to
-the same instance $(D a) of the respective allocator type. By definition of D,
-this is possible only if $(D a) has the $(D shared) qualifier. It follows that
-the allocator type must implement $(D allocate) and $(D deallocate) as $(D
-shared) methods. That way, the allocator commits to allowing usable $(D shared)
+The issue remains of calling `a.deallocate(b)` from a different thread than
+the one that allocated `b`. It follows that both threads must have access to
+the same instance `a` of the respective allocator type. By definition of D,
+this is possible only if `a` has the `shared` qualifier. It follows that
+the allocator type must implement `allocate` and `deallocate` as $(D
+shared) methods. That way, the allocator commits to allowing usable `shared`
instances.
-Conversely, allocating memory with one non-$(D shared) allocator, passing it
-across threads (by casting the obtained buffer to $(D shared)), and later
+Conversely, allocating memory with one non-`shared` allocator, passing it
+across threads (by casting the obtained buffer to `shared`), and later
deallocating it in a different thread (either with a different allocator object
-or with the same allocator object after casting it to $(D shared)) is illegal.
+or with the same allocator object after casting it to `shared`) is illegal.
$(H2 Building Blocks)
@@ -212,17 +213,20 @@ starting point for defining other allocators or for studying the API.))
$(TR $(TDC3 GCAllocator, gc_allocator) $(TD The system-provided garbage-collector allocator.
This should be the default fallback allocator tapping into system memory. It
-offers manual $(D free) and dutifully collects litter.))
+offers manual `free` and dutifully collects litter.))
$(TR $(TDC3 Mallocator, mallocator) $(TD The C heap _allocator, a.k.a. $(D
-malloc)/$(D realloc)/$(D free). Use sparingly and only for code that is unlikely
+malloc)/`realloc`/`free`. Use sparingly and only for code that is unlikely
to leak.))
$(TR $(TDC3 AlignedMallocator, mallocator) $(TD Interface to OS-specific _allocators that
support specifying alignment:
-$(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, $(D posix_memalign))
+$(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, `posix_memalign`)
on Posix and $(HTTP msdn.microsoft.com/en-us/library/fs9stz4e(v=vs.80).aspx,
-$(D __aligned_xxx)) on Windows.))
+`__aligned_xxx`) on Windows.))
+
+$(TR $(TDC2 AlignedBlockList, aligned_block_list) $(TD A wrapper around a list of allocators
+which allow for very fast deallocations.))
$(TR $(TDC2 AffixAllocator, affix_allocator) $(TD Allocator that allows and manages allocating
extra prefix and/or a suffix bytes for each block allocated.))
@@ -231,8 +235,8 @@ $(TR $(TDC2 BitmappedBlock, bitmapped_block) $(TD Organizes one contiguous chunk
equal-size blocks and tracks allocation status at the cost of one bit per
block.))
-$(TR $(TDC2 FallbackAllocator, fallback_allocator) $(TD Allocator that combines two other allocators
- - primary and fallback. Allocation requests are first tried with primary, and
+$(TR $(TDC2 FallbackAllocator, fallback_allocator) $(TD Allocator that combines two other
+ allocators - primary and fallback. Allocation requests are first tried with primary, and
upon failure are passed to the fallback. Useful for small and fast allocators
fronting general-purpose ones.))
@@ -241,10 +245,10 @@ wikipedia.org/wiki/Free_list, free list) on top of any other allocator. The
preferred size, tolerance, and maximum elements are configurable at compile- and
run time.))
-$(TR $(TDC2 SharedFreeList, free_list) $(TD Same features as $(D FreeList), but packaged as
-a $(D shared) structure that is accessible to several threads.))
+$(TR $(TDC2 SharedFreeList, free_list) $(TD Same features as `FreeList`, but packaged as
+a `shared` structure that is accessible to several threads.))
-$(TR $(TDC2 FreeTree, free_tree) $(TD Allocator similar to $(D FreeList) that uses a
+$(TR $(TDC2 FreeTree, free_tree) $(TD Allocator similar to `FreeList` that uses a
binary search tree to adaptively store not one, but many free lists.))
$(TR $(TDC2 Region, region) $(TD Region allocator organizes a chunk of memory as a
@@ -276,27 +280,34 @@ and dispatches them to distinct allocators.))
$(TR $(TDC2 Bucketizer, bucketizer) $(TD Divides allocation sizes in discrete buckets and
uses an array of allocators, one per bucket, to satisfy requests.))
+$(TR $(TDC2 AscendingPageAllocator, ascending_page_allocator) $(TD A memory safe allocator
+where sizes are rounded to a multiple of the page size and allocations are satisfied at increasing addresses.))
+
$(COMMENT $(TR $(TDC2 InternalPointersTree) $(TD Adds support for resolving internal
pointers on top of another allocator.)))
)
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/package.d)
+
Macros:
MYREF2 = $(REF_SHORT $1, std,experimental,allocator,building_blocks,$2)
MYREF3 = $(REF_SHORT $1, std,experimental,allocator,$2)
-TDC = $(TDNW $(D $1)$+)
+TDC = $(TDNW `$1`$+)
TDC2 = $(TDNW $(D $(MYREF2 $1,$+))$(BR)$(SMALL
-$(D std.experimental.allocator.building_blocks.$2)))
+`std.experimental.allocator.building_blocks.$2`))
TDC3 = $(TDNW $(D $(MYREF3 $1,$+))$(BR)$(SMALL
-$(D std.experimental.allocator.$2)))
+`std.experimental.allocator.$2`))
RES = $(I result)
-POST = $(BR)$(SMALL $(I Post:) $(BLUE $(D $0)))
+POST = $(BR)$(SMALL $(I Post:) $(BLUE `$0`))
*/
module std.experimental.allocator.building_blocks;
public import
std.experimental.allocator.building_blocks.affix_allocator,
+ std.experimental.allocator.building_blocks.aligned_block_list,
std.experimental.allocator.building_blocks.allocator_list,
+ std.experimental.allocator.building_blocks.ascending_page_allocator,
std.experimental.allocator.building_blocks.bucketizer,
std.experimental.allocator.building_blocks.fallback_allocator,
std.experimental.allocator.building_blocks.free_list,
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d b/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d
index b3f205dcc61..762b3797b41 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d
@@ -1,37 +1,40 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/quantizer.d)
+*/
module std.experimental.allocator.building_blocks.quantizer;
import std.experimental.allocator.common;
/**
-This allocator sits on top of $(D ParentAllocator) and quantizes allocation
-sizes, usually from arbitrary positive numbers to a small set of round numbers
-(e.g. powers of two, page sizes etc). This technique is commonly used to:
+This allocator sits on top of `ParentAllocator` and quantizes allocation sizes,
+usually from arbitrary positive numbers to a small set of round numbers (e.g.
+powers of two, page sizes etc). This technique is commonly used to:
$(UL
$(LI Preallocate more memory than requested such that later on, when
reallocation is needed (e.g. to grow an array), expansion can be done quickly
in place. Reallocation to smaller sizes is also fast (in-place) when the new
size requested is within the same quantum as the existing size. Code that's
-reallocation-heavy can therefore benefit from fronting a generic allocator
-with a $(D Quantizer). These advantages are present even if
-$(D ParentAllocator) does not support reallocation at all.)
-$(LI Improve behavior of allocators sensitive to allocation sizes, such as $(D
-FreeList) and $(D FreeTree). Rounding allocation requests up makes for smaller
+reallocation-heavy can therefore benefit from fronting a generic allocator with
+a `Quantizer`. These advantages are present even if `ParentAllocator` does not
+support reallocation at all.)
+$(LI Improve behavior of allocators sensitive to allocation sizes, such as
+`FreeList` and `FreeTree`. Rounding allocation requests up makes for smaller
free lists/trees at the cost of slack memory (internal fragmentation).)
)
The following methods are forwarded to the parent allocator if present:
-$(D allocateAll), $(D owns), $(D deallocateAll), $(D empty).
+`allocateAll`, `owns`, `deallocateAll`, `empty`.
-Preconditions: $(D roundingFunction) must satisfy three constraints. These are
-not enforced (save for the use of $(D assert)) for the sake of efficiency.
+Preconditions: `roundingFunction` must satisfy three constraints. These are
+not enforced (save for the use of `assert`) for the sake of efficiency.
$(OL
-$(LI $(D roundingFunction(n) >= n) for all $(D n) of type $(D size_t);)
-$(LI $(D roundingFunction) must be monotonically increasing, i.e. $(D
+$(LI $(D roundingFunction(n) >= n) for all `n` of type `size_t`;)
+$(LI `roundingFunction` must be monotonically increasing, i.e. $(D
roundingFunction(n1) <= roundingFunction(n2)) for all $(D n1 < n2);)
-$(LI $(D roundingFunction) must be $(D pure), i.e. always return the same
-value for a given $(D n).)
+$(LI `roundingFunction` must be `nothrow`, `@safe`, `@nogc` and `pure`, i.e.
+always return the same value for a given `n`.)
)
*/
struct Quantizer(ParentAllocator, alias roundingFunction)
@@ -39,7 +42,7 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
import std.traits : hasMember;
/**
- The parent allocator. Depending on whether $(D ParentAllocator) holds state
+ The parent allocator. Depending on whether `ParentAllocator` holds state
or not, this is a member variable or an alias for
`ParentAllocator.instance`.
*/
@@ -50,11 +53,11 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
else
{
alias parent = ParentAllocator.instance;
- static __gshared Quantizer instance;
+ __gshared Quantizer instance;
}
/**
- Returns $(D roundingFunction(n)).
+ Returns `roundingFunction(n)`.
*/
size_t goodAllocSize(size_t n)
{
@@ -69,9 +72,9 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
enum alignment = ParentAllocator.alignment;
/**
- Gets a larger buffer $(D buf) by calling
- $(D parent.allocate(goodAllocSize(n))). If $(D buf) is $(D null), returns
- $(D null). Otherwise, returns $(D buf[0 .. n]).
+ Gets a larger buffer `buf` by calling
+ `parent.allocate(goodAllocSize(n))`. If `buf` is `null`, returns
+ `null`. Otherwise, returns $(D buf[0 .. n]).
*/
void[] allocate(size_t n)
{
@@ -79,27 +82,34 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
return result.ptr ? result.ptr[0 .. n] : null;
}
+ static if (hasMember!(ParentAllocator, "allocateZeroed"))
+ package(std) void[] allocateZeroed()(size_t n)
+ {
+ auto result = parent.allocateZeroed(goodAllocSize(n));
+ return result.ptr ? result.ptr[0 .. n] : null;
+ }
+
/**
- Defined only if $(D parent.alignedAllocate) exists and works similarly to
- $(D allocate) by forwarding to
+ Defined only if `parent.alignedAllocate` exists and works similarly to
+ `allocate` by forwarding to
$(D parent.alignedAllocate(goodAllocSize(n), a)).
*/
static if (hasMember!(ParentAllocator, "alignedAllocate"))
- void[] alignedAllocate(size_t n, uint)
+ void[] alignedAllocate(size_t n, uint a)
{
- auto result = parent.alignedAllocate(goodAllocSize(n));
+ auto result = parent.alignedAllocate(goodAllocSize(n), a);
return result.ptr ? result.ptr[0 .. n] : null;
}
/**
- First checks whether there's enough slack memory preallocated for $(D b)
+ First checks whether there's enough slack memory preallocated for `b`
by evaluating $(D b.length + delta <= goodAllocSize(b.length)). If that's
- the case, expands $(D b) in place. Otherwise, attempts to use
- $(D parent.expand) appropriately if present.
+ the case, expands `b` in place. Otherwise, attempts to use
+ `parent.expand` appropriately if present.
*/
bool expand(ref void[] b, size_t delta)
{
- if (!b.ptr) return delta == 0;
+ if (!b || delta == 0) return delta == 0;
immutable allocated = goodAllocSize(b.length),
needed = b.length + delta,
neededAllocation = goodAllocSize(needed);
@@ -110,19 +120,19 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
if (allocated == neededAllocation)
{
// Nice!
- b = b.ptr[0 .. needed];
+ b = (() @trusted => b.ptr[0 .. needed])();
return true;
}
// Hail Mary
static if (hasMember!(ParentAllocator, "expand"))
{
// Expand to the appropriate quantum
- auto original = b.ptr[0 .. allocated];
+ auto original = (() @trusted => b.ptr[0 .. allocated])();
assert(goodAllocSize(needed) >= allocated);
if (!parent.expand(original, neededAllocation - allocated))
return false;
// Dial back the size
- b = original.ptr[0 .. needed];
+ b = (() @trusted => original.ptr[0 .. needed])();
return true;
}
else
@@ -134,7 +144,7 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
/**
Expands or shrinks allocated block to an allocated size of $(D
goodAllocSize(s)). Expansion occurs in place under the conditions required
- by $(D expand). Shrinking occurs in place if $(D goodAllocSize(b.length)
+ by `expand`. Shrinking occurs in place if $(D goodAllocSize(b.length)
== goodAllocSize(s)).
*/
bool reallocate(ref void[] b, size_t s)
@@ -162,8 +172,8 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
}
/**
- Defined only if $(D ParentAllocator.alignedAllocate) exists. Expansion
- occurs in place under the conditions required by $(D expand). Shrinking
+ Defined only if `ParentAllocator.alignedAllocate` exists. Expansion
+ occurs in place under the conditions required by `expand`. Shrinking
occurs in place if $(D goodAllocSize(b.length) == goodAllocSize(s)).
*/
static if (hasMember!(ParentAllocator, "alignedAllocate"))
@@ -171,14 +181,14 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
{
if (!b.ptr)
{
- b = alignedAllocate(s);
+ b = alignedAllocate(s, a);
return b.length == s;
}
- if (s >= b.length && expand(b, s - b.length)) return true;
+ if (s >= b.length && b.ptr.alignedAt(a) && expand(b, s - b.length)) return true;
immutable toAllocate = goodAllocSize(s),
allocated = goodAllocSize(b.length);
// Are the lengths within the same quantum?
- if (allocated == toAllocate)
+ if (allocated == toAllocate && b.ptr.alignedAt(a))
{
assert(b.ptr); // code above must have caught this
// Reallocation (whether up or down) will be done in place
@@ -193,7 +203,7 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
}
/**
- Defined if $(D ParentAllocator.deallocate) exists and forwards to
+ Defined if `ParentAllocator.deallocate` exists and forwards to
$(D parent.deallocate(b.ptr[0 .. goodAllocSize(b.length)])).
*/
static if (hasMember!(ParentAllocator, "deallocate"))
@@ -212,23 +222,113 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
@system unittest
{
import std.experimental.allocator.building_blocks.free_tree : FreeTree;
- import std.experimental.allocator.common : roundUpToMultipleOf;
import std.experimental.allocator.gc_allocator : GCAllocator;
+ size_t roundUpToMultipleOf(size_t s, uint base)
+ {
+ auto rem = s % base;
+ return rem ? s + base - rem : s;
+ }
+
// Quantize small allocations to a multiple of cache line, large ones to a
// multiple of page size
alias MyAlloc = Quantizer!(
FreeTree!GCAllocator,
- n => n.roundUpToMultipleOf(n <= 16_384 ? 64 : 4096));
+ n => roundUpToMultipleOf(n, n <= 16_384 ? 64 : 4096));
MyAlloc alloc;
const buf = alloc.allocate(256);
assert(buf.ptr);
}
+version (StdUnittest)
@system unittest
{
import std.experimental.allocator.gc_allocator : GCAllocator;
alias MyAlloc = Quantizer!(GCAllocator,
(size_t n) => n.roundUpToMultipleOf(64));
testAllocator!(() => MyAlloc());
+
+ assert((() pure nothrow @safe @nogc => MyAlloc().goodAllocSize(1))() == 64);
+
+ auto a = MyAlloc();
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ // Inplace expand, since goodAllocSize is 64
+ assert((() @safe => a.expand(b, 22))());
+ //assert((() nothrow @safe => a.expand(b, 22))());
+ assert(b.length == 64);
+ // Trigger parent.expand, which may or may not succed
+ //() nothrow @safe { a.expand(b, 1); }();
+ () @safe { a.expand(b, 1); }();
+ assert(a.reallocate(b, 100));
+ assert(b.length == 100);
+ // Ensure deallocate inherits from parent
+ () nothrow @nogc { a.deallocate(b); }();
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+
+ alias Alloc = Quantizer!(Region!(Mallocator),
+ (size_t n) => n.roundUpToMultipleOf(64));
+ auto a = Alloc(Region!Mallocator(1024 * 64));
+ const b = a.allocate(42);
+ assert(b.length == 42);
+ // Check that owns inherits from parent, i.e. Region
+ assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+ assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no);
+
+ auto c = a.allocate(42);
+ assert(c.length == 42);
+ assert((() pure nothrow @safe @nogc => a.owns(c))() == Ternary.yes);
+ // Inplace expand, since goodAllocSize is 64
+ assert((() nothrow @safe => a.expand(c, 22))());
+ assert(c.length == 64);
+ // Trigger parent.expand
+ assert((() nothrow @safe => a.expand(c, 1))());
+ assert(c.length == 65);
+ // Check that reallocate inherits from parent
+ assert((() nothrow @nogc => a.reallocate(c, 100))());
+ assert(c.length == 100);
+}
+
+version (StdUnittest)
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ alias MyAlloc = Quantizer!(Region!(Mallocator),
+ (size_t n) => n.roundUpToMultipleOf(64));
+ testAllocator!(() => MyAlloc(Region!Mallocator(1024 * 64)));
+
+ auto a = MyAlloc(Region!Mallocator(1024 * 64));
+ void[] b;
+ assert((() nothrow @nogc => a.alignedReallocate(b, 42, 16))());
+ assert(b.length == 42);
+ assert(alignedAt(&b[0], 16));
+}
+
+version (StdUnittest)
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.typecons : Ternary;
+
+ alias MyAlloc = Quantizer!(Region!(),
+ (size_t n) => n.roundUpToMultipleOf(64));
+ testAllocator!(() => MyAlloc(Region!()(new ubyte[1024 * 64])));
+
+ auto a = MyAlloc(Region!()(new ubyte[1024 * 64]));
+ // Check that empty inherits from parent
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no);
+ // Check that deallocateAll inherits from parent
+ assert((() nothrow @nogc => a.deallocateAll())());
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/region.d b/libphobos/src/std/experimental/allocator/building_blocks/region.d
index 53f5ef988d4..be0d274633c 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/region.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/region.d
@@ -1,4 +1,7 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/region.d)
+*/
module std.experimental.allocator.building_blocks.region;
import std.experimental.allocator.building_blocks.null_allocator;
@@ -15,9 +18,9 @@ else version (WatchOS)
version = Darwin;
/**
-A $(D Region) allocator allocates memory straight from one contiguous chunk.
+A `Region` allocator allocates memory straight from one contiguous chunk.
There is no deallocation, and once the region is full, allocation requests
-return $(D null). Therefore, $(D Region)s are often used (a) in conjunction with
+return `null`. Therefore, `Region`s are often used (a) in conjunction with
more sophisticated allocators; or (b) for batch-style very fast allocations
that deallocate everything at once.
@@ -26,11 +29,11 @@ the store and the limits. One allocation entails rounding up the allocation
size for alignment purposes, bumping the current pointer, and comparing it
against the limit.
-If $(D ParentAllocator) is different from $(D NullAllocator), $(D Region)
+If `ParentAllocator` is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), `Region`
deallocates the chunk of memory during destruction.
-The $(D minAlign) parameter establishes alignment. If $(D minAlign > 1), the
-sizes of all allocation requests are rounded up to a multiple of $(D minAlign).
+The `minAlign` parameter establishes alignment. If $(D minAlign > 1), the
+sizes of all allocation requests are rounded up to a multiple of `minAlign`.
Applications aiming at maximum speed may want to choose $(D minAlign = 1) and
control alignment externally.
@@ -47,7 +50,7 @@ struct Region(ParentAllocator = NullAllocator,
// state
/**
- The _parent allocator. Depending on whether $(D ParentAllocator) holds state
+ The _parent allocator. Depending on whether `ParentAllocator` holds state
or not, this is a member variable or an alias for
`ParentAllocator.instance`.
*/
@@ -59,51 +62,65 @@ struct Region(ParentAllocator = NullAllocator,
{
alias parent = ParentAllocator.instance;
}
+
private void* _current, _begin, _end;
+ private void* roundedBegin() const pure nothrow @trusted @nogc
+ {
+ return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+ }
+
+ private void* roundedEnd() const pure nothrow @trusted @nogc
+ {
+ return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
+ }
/**
- Constructs a region backed by a user-provided store. Assumes $(D store) is
- aligned at $(D minAlign). Also assumes the memory was allocated with $(D
- ParentAllocator) (if different from $(D NullAllocator)).
+ Constructs a region backed by a user-provided store.
+ Assumes the memory was allocated with `ParentAllocator`
+ (if different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator)).
Params:
- store = User-provided store backing up the region. $(D store) must be
- aligned at $(D minAlign) (enforced with $(D assert)). If $(D
- ParentAllocator) is different from $(D NullAllocator), memory is assumed to
- have been allocated with $(D ParentAllocator).
- n = Bytes to allocate using $(D ParentAllocator). This constructor is only
- defined If $(D ParentAllocator) is different from $(D NullAllocator). If
- $(D parent.allocate(n)) returns $(D null), the region will be initialized
- as empty (correctly initialized but unable to allocate).
- */
- this(ubyte[] store)
- {
- store = cast(ubyte[])(store.roundUpToAlignment(alignment));
- store = store[0 .. $.roundDownToAlignment(alignment)];
- assert(store.ptr.alignedAt(minAlign));
- assert(store.length % minAlign == 0);
+ store = User-provided store backing up the region. If $(D
+ ParentAllocator) is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), memory is assumed to
+ have been allocated with `ParentAllocator`.
+ n = Bytes to allocate using `ParentAllocator`. This constructor is only
+ defined If `ParentAllocator` is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator). If
+ `parent.allocate(n)` returns `null`, the region will be initialized
+ as empty (correctly initialized but unable to allocate).
+ */
+ this(ubyte[] store) pure nothrow @nogc
+ {
_begin = store.ptr;
_end = store.ptr + store.length;
static if (growDownwards)
- _current = _end;
+ _current = roundedEnd();
else
- _current = store.ptr;
+ _current = roundedBegin();
}
/// Ditto
- static if (!is(ParentAllocator == NullAllocator))
+ static if (!is(ParentAllocator == NullAllocator) && !stateSize!ParentAllocator)
this(size_t n)
{
- this(cast(ubyte[])(parent.allocate(n.roundUpToAlignment(alignment))));
+ this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment))));
+ }
+
+ /// Ditto
+ static if (!is(ParentAllocator == NullAllocator) && stateSize!ParentAllocator)
+ this(ParentAllocator parent, size_t n)
+ {
+ this.parent = parent;
+ this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment))));
}
/*
- TODO: The postblit of $(D BasicRegion) should be disabled because such objects
+ TODO: The postblit of `BasicRegion` should be disabled because such objects
should not be copied around naively.
*/
/**
- If `ParentAllocator` is not `NullAllocator` and defines `deallocate`, the region defines a destructor that uses `ParentAllocator.delete` to free the
+ If `ParentAllocator` is not $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator) and defines `deallocate`,
+ the region defines a destructor that uses `ParentAllocator.deallocate` to free the
memory chunk.
*/
static if (!is(ParentAllocator == NullAllocator)
@@ -113,6 +130,13 @@ struct Region(ParentAllocator = NullAllocator,
parent.deallocate(_begin[0 .. _end - _begin]);
}
+ /**
+ Rounds the given size to a multiple of the `alignment`
+ */
+ size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc
+ {
+ return n.roundUpToAlignment(alignment);
+ }
/**
Alignment offered.
@@ -120,77 +144,75 @@ struct Region(ParentAllocator = NullAllocator,
alias alignment = minAlign;
/**
- Allocates $(D n) bytes of memory. The shortest path involves an alignment
+ Allocates `n` bytes of memory. The shortest path involves an alignment
adjustment (if $(D alignment > 1)), an increment, and a comparison.
Params:
- n = number of bytes to allocate
+ n = number of bytes to allocate
Returns:
- A properly-aligned buffer of size $(D n) or $(D null) if request could not
- be satisfied.
+ A properly-aligned buffer of size `n` or `null` if request could not
+ be satisfied.
*/
- void[] allocate(size_t n)
+ void[] allocate(size_t n) pure nothrow @trusted @nogc
{
+ const rounded = goodAllocSize(n);
+ if (n == 0 || rounded < n || available < rounded) return null;
+
static if (growDownwards)
{
- if (available < n) return null;
- static if (minAlign > 1)
- const rounded = n.roundUpToAlignment(alignment);
- else
- alias rounded = n;
assert(available >= rounded);
auto result = (_current - rounded)[0 .. n];
assert(result.ptr >= _begin);
_current = result.ptr;
assert(owns(result) == Ternary.yes);
- return result;
}
else
{
auto result = _current[0 .. n];
- static if (minAlign > 1)
- const rounded = n.roundUpToAlignment(alignment);
- else
- alias rounded = n;
_current += rounded;
- if (_current <= _end) return result;
- // Slow path, backtrack
- _current -= rounded;
- return null;
}
+
+ return result;
}
/**
- Allocates $(D n) bytes of memory aligned at alignment $(D a).
+ Allocates `n` bytes of memory aligned at alignment `a`.
Params:
- n = number of bytes to allocate
- a = alignment for the allocated block
+ n = number of bytes to allocate
+ a = alignment for the allocated block
Returns:
- Either a suitable block of $(D n) bytes aligned at $(D a), or $(D null).
+ Either a suitable block of `n` bytes aligned at `a`, or `null`.
*/
- void[] alignedAllocate(size_t n, uint a)
+ void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
assert(a.isPowerOf2);
+
+ const rounded = goodAllocSize(n);
+ if (n == 0 || rounded < n || available < rounded) return null;
+
static if (growDownwards)
{
- const available = _current - _begin;
- if (available < n) return null;
- auto result = (_current - n).alignDownTo(a)[0 .. n];
- if (result.ptr >= _begin)
+ auto tmpCurrent = _current - rounded;
+ auto result = tmpCurrent.alignDownTo(a);
+ if (result <= tmpCurrent && result >= _begin)
{
- _current = result.ptr;
- return result;
+ _current = result;
+ return cast(void[]) result[0 .. n];
}
}
else
{
// Just bump the pointer to the next good allocation
+ auto newCurrent = _current.alignUpTo(a);
+ if (newCurrent < _current || newCurrent > _end)
+ return null;
+
auto save = _current;
- _current = _current.alignUpTo(a);
+ _current = newCurrent;
auto result = allocate(n);
if (result.ptr)
{
@@ -204,7 +226,7 @@ struct Region(ParentAllocator = NullAllocator,
}
/// Allocates and returns all memory available to this region.
- void[] allocateAll()
+ void[] allocateAll() pure nothrow @trusted @nogc
{
static if (growDownwards)
{
@@ -225,20 +247,23 @@ struct Region(ParentAllocator = NullAllocator,
`No.growDownwards`.
*/
static if (growDownwards == No.growDownwards)
- bool expand(ref void[] b, size_t delta)
+ bool expand(ref void[] b, size_t delta) pure nothrow @safe @nogc
{
- assert(owns(b) == Ternary.yes || b.ptr is null);
- assert(b.ptr + b.length <= _current || b.ptr is null);
- if (!b.ptr) return delta == 0;
+ assert(owns(b) == Ternary.yes || b is null);
+ assert((() @trusted => b.ptr + b.length <= _current)() || b is null);
+ if (b is null || delta == 0) return delta == 0;
auto newLength = b.length + delta;
- if (_current < b.ptr + b.length + alignment)
+ if ((() @trusted => _current < b.ptr + b.length + alignment)())
{
+ immutable currentGoodSize = this.goodAllocSize(b.length);
+ immutable newGoodSize = this.goodAllocSize(newLength);
+ immutable goodDelta = newGoodSize - currentGoodSize;
// This was the last allocation! Allocate some more and we're done.
- if (this.goodAllocSize(b.length) == this.goodAllocSize(newLength)
- || allocate(delta).length == delta)
+ if (goodDelta == 0
+ || (() @trusted => allocate(goodDelta).length == goodDelta)())
{
- b = b.ptr[0 .. newLength];
- assert(_current < b.ptr + b.length + alignment);
+ b = (() @trusted => b.ptr[0 .. newLength])();
+ assert((() @trusted => _current < b.ptr + b.length + alignment)());
return true;
}
}
@@ -246,30 +271,29 @@ struct Region(ParentAllocator = NullAllocator,
}
/**
- Deallocates $(D b). This works only if $(D b) was obtained as the last call
- to $(D allocate); otherwise (i.e. another allocation has occurred since) it
- does nothing. This semantics is tricky and therefore $(D deallocate) is
- defined only if $(D Region) is instantiated with $(D Yes.defineDeallocate)
- as the third template argument.
+ Deallocates `b`. This works only if `b` was obtained as the last call
+ to `allocate`; otherwise (i.e. another allocation has occurred since) it
+ does nothing.
Params:
- b = Block previously obtained by a call to $(D allocate) against this
- allocator ($(D null) is allowed).
+ b = Block previously obtained by a call to `allocate` against this
+ allocator (`null` is allowed).
*/
- bool deallocate(void[] b)
+ bool deallocate(void[] b) pure nothrow @nogc
{
assert(owns(b) == Ternary.yes || b.ptr is null);
+ auto rounded = goodAllocSize(b.length);
static if (growDownwards)
{
if (b.ptr == _current)
{
- _current += this.goodAllocSize(b.length);
+ _current += rounded;
return true;
}
}
else
{
- if (b.ptr + this.goodAllocSize(b.length) == _current)
+ if (b.ptr + rounded == _current)
{
assert(b.ptr !is null || _current is null);
_current = b.ptr;
@@ -283,46 +307,48 @@ struct Region(ParentAllocator = NullAllocator,
Deallocates all memory allocated by this region, which can be subsequently
reused for new allocations.
*/
- bool deallocateAll()
+ bool deallocateAll() pure nothrow @nogc
{
static if (growDownwards)
{
- _current = _end;
+ _current = roundedEnd();
}
else
{
- _current = _begin;
+ _current = roundedBegin();
}
return true;
}
/**
- Queries whether $(D b) has been allocated with this region.
+ Queries whether `b` has been allocated with this region.
Params:
- b = Arbitrary block of memory ($(D null) is allowed; $(D owns(null))
- returns $(D false)).
+ b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns
+ `false`).
Returns:
- $(D true) if $(D b) has been allocated with this region, $(D false)
- otherwise.
+ `true` if `b` has been allocated with this region, `false` otherwise.
*/
- Ternary owns(void[] b) const
+ Ternary owns(const void[] b) const pure nothrow @trusted @nogc
{
- return Ternary(b.ptr >= _begin && b.ptr + b.length <= _end);
+ return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end));
}
/**
Returns `Ternary.yes` if no memory has been allocated in this region,
`Ternary.no` otherwise. (Never returns `Ternary.unknown`.)
*/
- Ternary empty() const
+ Ternary empty() const pure nothrow @safe @nogc
{
- return Ternary(_current == _begin);
+ static if (growDownwards)
+ return Ternary(_current == roundedEnd());
+ else
+ return Ternary(_current == roundedBegin());
}
/// Nonstandard property that returns bytes available for allocation.
- size_t available() const
+ size_t available() const @safe pure nothrow @nogc
{
static if (growDownwards)
{
@@ -336,45 +362,143 @@ struct Region(ParentAllocator = NullAllocator,
}
///
-@system unittest
+@system nothrow unittest
{
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
// Create a scalable list of regions. Each gets at least 1MB at a time by
// using malloc.
auto batchAllocator = AllocatorList!(
(size_t n) => Region!Mallocator(max(n, 1024 * 1024))
)();
+ assert(batchAllocator.empty == Ternary.yes);
auto b = batchAllocator.allocate(101);
assert(b.length == 101);
+ assert(batchAllocator.empty == Ternary.no);
// This will cause a second allocation
b = batchAllocator.allocate(2 * 1024 * 1024);
assert(b.length == 2 * 1024 * 1024);
// Destructor will free the memory
}
-@system unittest
+@system nothrow @nogc unittest
{
import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+
+ static void testAlloc(Allocator)(ref Allocator a)
+ {
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
+ const b = a.allocate(101);
+ assert(b.length == 101);
+ assert((() nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+
+ // Ensure deallocate inherits from parent allocators
+ auto c = a.allocate(42);
+ assert(c.length == 42);
+ assert((() nothrow @nogc => a.deallocate(c))());
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no);
+ }
+
// Create a 64 KB region allocated with malloc
auto reg = Region!(Mallocator, Mallocator.alignment,
Yes.growDownwards)(1024 * 64);
- const b = reg.allocate(101);
+ testAlloc(reg);
+
+ // Create a 64 KB shared region allocated with malloc
+ auto sharedReg = SharedRegion!(Mallocator, Mallocator.alignment,
+ Yes.growDownwards)(1024 * 64);
+ testAlloc(sharedReg);
+}
+
+@system nothrow @nogc unittest
+{
+ import std.experimental.allocator.mallocator : AlignedMallocator;
+ import std.typecons : Ternary;
+
+ ubyte[] buf = cast(ubyte[]) AlignedMallocator.instance.alignedAllocate(64, 64);
+ auto reg = Region!(NullAllocator, 64, Yes.growDownwards)(buf);
+ assert(reg.alignedAllocate(10, 32).length == 10);
+ assert(!reg.available);
+}
+
+@system nothrow @nogc unittest
+{
+ // test 'this(ubyte[] store)' constructed regions properly clean up
+ // their inner storage after destruction
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ static shared struct LocalAllocator
+ {
+ nothrow @nogc:
+ enum alignment = Mallocator.alignment;
+ void[] buf;
+ bool deallocate(void[] b)
+ {
+ assert(buf.ptr == b.ptr && buf.length == b.length);
+ return true;
+ }
+
+ void[] allocate(size_t n)
+ {
+ return null;
+ }
+
+ }
+
+ enum bufLen = 10 * Mallocator.alignment;
+ void[] tmp = Mallocator.instance.allocate(bufLen);
+
+ LocalAllocator a;
+ a.buf = cast(typeof(a.buf)) tmp[1 .. $];
+
+ auto reg = Region!(LocalAllocator, Mallocator.alignment,
+ Yes.growDownwards)(cast(ubyte[]) a.buf);
+ auto sharedReg = SharedRegion!(LocalAllocator, Mallocator.alignment,
+ Yes.growDownwards)(cast(ubyte[]) a.buf);
+ reg.parent = a;
+ sharedReg.parent = a;
+
+ Mallocator.instance.deallocate(tmp);
+}
+
+version (StdUnittest)
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ testAllocator!(() => Region!(Mallocator)(1024 * 64));
+ testAllocator!(() => Region!(Mallocator, Mallocator.alignment, Yes.growDownwards)(1024 * 64));
+
+ testAllocator!(() => SharedRegion!(Mallocator)(1024 * 64));
+ testAllocator!(() => SharedRegion!(Mallocator, Mallocator.alignment, Yes.growDownwards)(1024 * 64));
+}
+
+@system nothrow @nogc unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ auto reg = Region!(Mallocator)(1024 * 64);
+ auto b = reg.allocate(101);
assert(b.length == 101);
- // Destructor will free the memory
+ assert((() pure nothrow @safe @nogc => reg.expand(b, 20))());
+ assert((() pure nothrow @safe @nogc => reg.expand(b, 73))());
+ assert((() pure nothrow @safe @nogc => !reg.expand(b, 1024 * 64))());
+ assert((() nothrow @nogc => reg.deallocateAll())());
}
/**
-$(D InSituRegion) is a convenient region that carries its storage within itself
+`InSituRegion` is a convenient region that carries its storage within itself
(in the form of a statically-sized array).
The first template argument is the size of the region and the second is the
needed alignment. Depending on the alignment requested and platform details,
the actual available storage may be smaller than the compile-time parameter. To
-make sure that at least $(D n) bytes are available in the region, use
+make sure that at least `n` bytes are available in the region, use
$(D InSituRegion!(n + a - 1, a)).
Given that the most frequent use of `InSituRegion` is as a stack allocator, it
@@ -399,10 +523,10 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
else version (HPPA) enum growDownwards = No.growDownwards;
else version (PPC) enum growDownwards = Yes.growDownwards;
else version (PPC64) enum growDownwards = Yes.growDownwards;
- else version (MIPS32) enum growDownwards = Yes.growDownwards;
- else version (MIPS64) enum growDownwards = Yes.growDownwards;
else version (RISCV32) enum growDownwards = Yes.growDownwards;
else version (RISCV64) enum growDownwards = Yes.growDownwards;
+ else version (MIPS32) enum growDownwards = Yes.growDownwards;
+ else version (MIPS64) enum growDownwards = Yes.growDownwards;
else version (SPARC) enum growDownwards = Yes.growDownwards;
else version (SPARC64) enum growDownwards = Yes.growDownwards;
else version (SystemZ) enum growDownwards = Yes.growDownwards;
@@ -415,12 +539,12 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
union
{
private ubyte[size] _store = void;
- private double _forAlignmentOnly1 = void;
+ private double _forAlignmentOnly1;
}
// }
/**
- An alias for $(D minAlign), which must be a valid alignment (nonzero power
+ An alias for `minAlign`, which must be a valid alignment (nonzero power
of 2). The start of the region and all allocation requests will be rounded
up to a multiple of the alignment.
@@ -441,7 +565,7 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
}
/**
- Allocates $(D bytes) and returns them, or $(D null) if the region cannot
+ Allocates `bytes` and returns them, or `null` if the region cannot
accommodate the request. For efficiency reasons, if $(D bytes == 0) the
function returns an empty non-null slice.
*/
@@ -459,7 +583,7 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
}
/**
- As above, but the memory allocated is aligned at $(D a) bytes.
+ As above, but the memory allocated is aligned at `a` bytes.
*/
void[] alignedAllocate(size_t n, uint a)
{
@@ -475,15 +599,15 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
}
/**
- Deallocates $(D b). This works only if $(D b) was obtained as the last call
- to $(D allocate); otherwise (i.e. another allocation has occurred since) it
- does nothing. This semantics is tricky and therefore $(D deallocate) is
- defined only if $(D Region) is instantiated with $(D Yes.defineDeallocate)
+ Deallocates `b`. This works only if `b` was obtained as the last call
+ to `allocate`; otherwise (i.e. another allocation has occurred since) it
+ does nothing. This semantics is tricky and therefore `deallocate` is
+ defined only if `Region` is instantiated with `Yes.defineDeallocate`
as the third template argument.
Params:
- b = Block previously obtained by a call to $(D allocate) against this
- allocator ($(D null) is allowed).
+ b = Block previously obtained by a call to `allocate` against this
+ allocator (`null` is allowed).
*/
bool deallocate(void[] b)
{
@@ -495,7 +619,7 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
Returns `Ternary.yes` if `b` is the result of a previous allocation,
`Ternary.no` otherwise.
*/
- Ternary owns(void[] b)
+ Ternary owns(const void[] b) pure nothrow @safe @nogc
{
if (!_impl._current) return Ternary.no;
return _impl.owns(b);
@@ -563,30 +687,38 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
// Reap with GC fallback.
InSituRegion!(128 * 1024, 8) tmp3;
FallbackAllocator!(BitmappedBlock!(64, 8), GCAllocator) r3;
- r3.primary = BitmappedBlock!(64, 8)(cast(ubyte[])(tmp3.allocateAll()));
+ r3.primary = BitmappedBlock!(64, 8)(cast(ubyte[]) (tmp3.allocateAll()));
const a3 = r3.allocate(103);
assert(a3.length == 103);
// Reap/GC with a freelist for small objects up to 16 bytes.
InSituRegion!(128 * 1024, 64) tmp4;
FreeList!(FallbackAllocator!(BitmappedBlock!(64, 64), GCAllocator), 0, 16) r4;
- r4.parent.primary = BitmappedBlock!(64, 64)(cast(ubyte[])(tmp4.allocateAll()));
+ r4.parent.primary = BitmappedBlock!(64, 64)(cast(ubyte[]) (tmp4.allocateAll()));
const a4 = r4.allocate(104);
assert(a4.length == 104);
}
-@system unittest
+@system pure nothrow unittest
{
+ import std.typecons : Ternary;
+
InSituRegion!(4096, 1) r1;
auto a = r1.allocate(2001);
assert(a.length == 2001);
import std.conv : text;
assert(r1.available == 2095, text(r1.available));
+ // Ensure deallocate inherits from parent
+ assert((() nothrow @nogc => r1.deallocate(a))());
+ assert((() nothrow @nogc => r1.deallocateAll())());
InSituRegion!(65_536, 1024*4) r2;
assert(r2.available <= 65_536);
a = r2.allocate(2001);
assert(a.length == 2001);
+ const void[] buff = r2.allocate(42);
+ assert((() nothrow @safe @nogc => r2.owns(buff))() == Ternary.yes);
+ assert((() nothrow @nogc => r2.deallocateAll())());
}
version (CRuntime_Musl)
@@ -613,10 +745,10 @@ else
/**
Allocator backed by $(D $(LINK2 https://en.wikipedia.org/wiki/Sbrk, sbrk))
-for Posix systems. Due to the fact that $(D sbrk) is not thread-safe
+for Posix systems. Due to the fact that `sbrk` is not thread-safe
$(HTTP lifecs.likai.org/2010/02/sbrk-is-not-thread-safe.html, by design),
-$(D SbrkRegion) uses a mutex internally. This implies
-that uncontrolled calls to $(D brk) and $(D sbrk) may affect the workings of $(D
+`SbrkRegion` uses a mutex internally. This implies
+that uncontrolled calls to `brk` and `sbrk` may affect the workings of $(D
SbrkRegion) adversely.
*/
@@ -645,13 +777,20 @@ version (Posix) struct SbrkRegion(uint minAlign = platformAlignment)
*/
enum uint alignment = minAlign;
+ /**
+ Rounds the given size to a multiple of thew `alignment`
+ */
+ size_t goodAllocSize(size_t n) shared const pure nothrow @safe @nogc
+ {
+ return n.roundUpToMultipleOf(alignment);
+ }
+
/// Ditto
- void[] allocate(size_t bytes) shared
+ void[] allocate(size_t bytes) shared @trusted nothrow @nogc
{
- static if (minAlign > 1)
- const rounded = bytes.roundUpToMultipleOf(alignment);
- else
- alias rounded = bytes;
+ // Take alignment rounding into account
+ const rounded = goodAllocSize(bytes);
+
pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0);
scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0
|| assert(0);
@@ -674,7 +813,7 @@ version (Posix) struct SbrkRegion(uint minAlign = platformAlignment)
}
/// Ditto
- void[] alignedAllocate(size_t bytes, uint a) shared
+ void[] alignedAllocate(size_t bytes, uint a) shared @trusted nothrow @nogc
{
pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0);
scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0
@@ -705,56 +844,64 @@ version (Posix) struct SbrkRegion(uint minAlign = platformAlignment)
/**
- The $(D expand) method may only succeed if the argument is the last block
- allocated. In that case, $(D expand) attempts to push the break pointer to
+ The `expand` method may only succeed if the argument is the last block
+ allocated. In that case, `expand` attempts to push the break pointer to
the right.
*/
- bool expand(ref void[] b, size_t delta) shared
+ bool expand(ref void[] b, size_t delta) shared nothrow @trusted @nogc
{
- if (b is null) return delta == 0;
+ if (b is null || delta == 0) return delta == 0;
assert(_brkInitial && _brkCurrent); // otherwise where did b come from?
pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0);
scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0
|| assert(0);
- if (_brkCurrent != b.ptr + b.length) return false;
+
+ // Take alignment rounding into account
+ const rounded = goodAllocSize(b.length);
+
+ const slack = rounded - b.length;
+ if (delta <= slack)
+ {
+ b = b.ptr[0 .. b.length + delta];
+ return true;
+ }
+
+ if (_brkCurrent != b.ptr + rounded) return false;
// Great, can expand the last block
- static if (minAlign > 1)
- const rounded = delta.roundUpToMultipleOf(alignment);
- else
- alias rounded = bytes;
- auto p = sbrk(rounded);
+ delta -= slack;
+
+ const roundedDelta = goodAllocSize(delta);
+ auto p = sbrk(roundedDelta);
if (p == cast(void*) -1)
{
return false;
}
- _brkCurrent = cast(shared) (p + rounded);
- b = b.ptr[0 .. b.length + delta];
+ _brkCurrent = cast(shared) (p + roundedDelta);
+ b = b.ptr[0 .. b.length + slack + delta];
return true;
}
/// Ditto
- Ternary owns(void[] b) shared
+ Ternary owns(const void[] b) shared pure nothrow @trusted @nogc
{
// No need to lock here.
- assert(!_brkCurrent || b.ptr + b.length <= _brkCurrent);
- return Ternary(_brkInitial && b.ptr >= _brkInitial);
+ assert(!_brkCurrent || !b || &b[0] + b.length <= _brkCurrent);
+ return Ternary(_brkInitial && b && (&b[0] >= _brkInitial));
}
/**
- The $(D deallocate) method only works (and returns $(D true)) on systems
- that support reducing the break address (i.e. accept calls to $(D sbrk)
+ The `deallocate` method only works (and returns `true`) on systems
+ that support reducing the break address (i.e. accept calls to `sbrk`
with negative offsets). OSX does not accept such. In addition the argument
must be the last block allocated.
*/
- bool deallocate(void[] b) shared
+ bool deallocate(void[] b) shared nothrow @nogc
{
- static if (minAlign > 1)
- const rounded = b.length.roundUpToMultipleOf(alignment);
- else
- const rounded = b.length;
+ // Take alignment rounding into account
+ const rounded = goodAllocSize(b.length);
pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0);
scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0
|| assert(0);
@@ -767,10 +914,11 @@ version (Posix) struct SbrkRegion(uint minAlign = platformAlignment)
}
/**
- The $(D deallocateAll) method only works (and returns $(D true)) on systems
- that support reducing the break address (i.e. accept calls to $(D sbrk)
+ The `deallocateAll` method only works (and returns `true`) on systems
+ that support reducing the break address (i.e. accept calls to `sbrk`
with negative offsets). OSX does not accept such.
*/
+ nothrow @nogc
bool deallocateAll() shared
{
pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0);
@@ -780,7 +928,7 @@ version (Posix) struct SbrkRegion(uint minAlign = platformAlignment)
}
/// Standard allocator API.
- Ternary empty()
+ Ternary empty() shared pure nothrow @safe @nogc
{
// Also works when they're both null.
return Ternary(_brkCurrent == _brkInitial);
@@ -806,17 +954,446 @@ version (DragonFlyBSD) {} else
version (Posix) @system nothrow @nogc unittest
{
import std.typecons : Ternary;
- alias alloc = SbrkRegion!(8).instance;
+ import std.algorithm.comparison : min;
+ alias alloc = SbrkRegion!(min(8, platformAlignment)).instance;
+ assert((() nothrow @safe @nogc => alloc.empty)() == Ternary.yes);
auto a = alloc.alignedAllocate(2001, 4096);
assert(a.length == 2001);
+ assert((() nothrow @safe @nogc => alloc.empty)() == Ternary.no);
+ auto oldBrkCurr = alloc._brkCurrent;
auto b = alloc.allocate(2001);
assert(b.length == 2001);
- assert(alloc.owns(a) == Ternary.yes);
- assert(alloc.owns(b) == Ternary.yes);
+ assert((() nothrow @safe @nogc => alloc.expand(b, 0))());
+ assert(b.length == 2001);
+ // Expand with a small size to fit the rounded slack due to alignment
+ assert((() nothrow @safe @nogc => alloc.expand(b, 1))());
+ assert(b.length == 2002);
+ // Exceed the rounded slack due to alignment
+ assert((() nothrow @safe @nogc => alloc.expand(b, 10))());
+ assert(b.length == 2012);
+ assert((() nothrow @safe @nogc => alloc.owns(a))() == Ternary.yes);
+ assert((() nothrow @safe @nogc => alloc.owns(b))() == Ternary.yes);
// reducing the brk does not work on OSX
version (Darwin) {} else
{
- assert(alloc.deallocate(b));
- assert(alloc.deallocateAll);
+ assert((() nothrow @nogc => alloc.deallocate(b))());
+ // Check that expand and deallocate work well
+ assert(oldBrkCurr == alloc._brkCurrent);
+ assert((() nothrow @nogc => alloc.deallocate(a))());
+ assert((() nothrow @nogc => alloc.deallocateAll())());
+ }
+ const void[] c = alloc.allocate(2001);
+ assert(c.length == 2001);
+ assert((() nothrow @safe @nogc => alloc.owns(c))() == Ternary.yes);
+ assert((() nothrow @safe @nogc => alloc.owns(null))() == Ternary.no);
+}
+
+/**
+The threadsafe version of the `Region` allocator.
+Allocations and deallocations are lock-free based using $(REF cas, core,atomic).
+*/
+shared struct SharedRegion(ParentAllocator = NullAllocator,
+ uint minAlign = platformAlignment,
+ Flag!"growDownwards" growDownwards = No.growDownwards)
+{
+ static assert(minAlign.isGoodStaticAlignment);
+ static assert(ParentAllocator.alignment >= minAlign);
+
+ import std.traits : hasMember;
+ import std.typecons : Ternary;
+
+ // state
+ /**
+ The _parent allocator. Depending on whether `ParentAllocator` holds state
+ or not, this is a member variable or an alias for
+ `ParentAllocator.instance`.
+ */
+ static if (stateSize!ParentAllocator)
+ {
+ ParentAllocator parent;
+ }
+ else
+ {
+ alias parent = ParentAllocator.instance;
}
+ private shared void* _current, _begin, _end;
+
+ private void* roundedBegin() const pure nothrow @trusted @nogc
+ {
+ return cast(void*) roundUpToAlignment(cast(size_t) _begin, alignment);
+ }
+
+ private void* roundedEnd() const pure nothrow @trusted @nogc
+ {
+ return cast(void*) roundDownToAlignment(cast(size_t) _end, alignment);
+ }
+
+
+ /**
+ Constructs a region backed by a user-provided store.
+ Assumes the memory was allocated with `ParentAllocator`
+ (if different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator)).
+
+ Params:
+ store = User-provided store backing up the region. If `ParentAllocator`
+ is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator), memory is assumed to
+ have been allocated with `ParentAllocator`.
+ n = Bytes to allocate using `ParentAllocator`. This constructor is only
+ defined If `ParentAllocator` is different from $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator). If
+ `parent.allocate(n)` returns `null`, the region will be initialized
+ as empty (correctly initialized but unable to allocate).
+ */
+ this(ubyte[] store) pure nothrow @nogc
+ {
+ _begin = cast(typeof(_begin)) store.ptr;
+ _end = cast(typeof(_end)) (store.ptr + store.length);
+ static if (growDownwards)
+ _current = cast(typeof(_current)) roundedEnd();
+ else
+ _current = cast(typeof(_current)) roundedBegin();
+ }
+
+ /// Ditto
+ static if (!is(ParentAllocator == NullAllocator))
+ this(size_t n)
+ {
+ this(cast(ubyte[]) (parent.allocate(n.roundUpToAlignment(alignment))));
+ }
+
+ /**
+ Rounds the given size to a multiple of the `alignment`
+ */
+ size_t goodAllocSize(size_t n) const pure nothrow @safe @nogc
+ {
+ return n.roundUpToAlignment(alignment);
+ }
+
+ /**
+ Alignment offered.
+ */
+ alias alignment = minAlign;
+
+ /**
+ Allocates `n` bytes of memory. The allocation is served by atomically incrementing
+ a pointer which keeps track of the current used space.
+
+ Params:
+ n = number of bytes to allocate
+
+ Returns:
+ A properly-aligned buffer of size `n`, or `null` if request could not
+ be satisfied.
+ */
+ void[] allocate(size_t n) pure nothrow @trusted @nogc
+ {
+ import core.atomic : cas, atomicLoad;
+
+ if (n == 0) return null;
+ const rounded = goodAllocSize(n);
+
+ shared void* localCurrent, localNewCurrent;
+ static if (growDownwards)
+ {
+ do
+ {
+ localCurrent = atomicLoad(_current);
+ localNewCurrent = localCurrent - rounded;
+ if (localNewCurrent > localCurrent || localNewCurrent < _begin)
+ return null;
+ } while (!cas(&_current, localCurrent, localNewCurrent));
+
+ return cast(void[]) localNewCurrent[0 .. n];
+ }
+ else
+ {
+ do
+ {
+ localCurrent = atomicLoad(_current);
+ localNewCurrent = localCurrent + rounded;
+ if (localNewCurrent < localCurrent || localNewCurrent > _end)
+ return null;
+ } while (!cas(&_current, localCurrent, localNewCurrent));
+
+ return cast(void[]) localCurrent[0 .. n];
+ }
+
+ assert(0, "Unexpected error in SharedRegion.allocate");
+ }
+
+ /**
+ Deallocates `b`. This works only if `b` was obtained as the last call
+ to `allocate`; otherwise (i.e. another allocation has occurred since) it
+ does nothing.
+
+ Params:
+ b = Block previously obtained by a call to `allocate` against this
+ allocator (`null` is allowed).
+ */
+ bool deallocate(void[] b) pure nothrow @nogc
+ {
+ import core.atomic : cas, atomicLoad;
+
+ const rounded = goodAllocSize(b.length);
+ shared void* localCurrent, localNewCurrent;
+
+ // The cas is done only once, because only the last allocation can be reverted
+ localCurrent = atomicLoad(_current);
+ static if (growDownwards)
+ {
+ localNewCurrent = localCurrent + rounded;
+ if (b.ptr == localCurrent)
+ return cas(&_current, localCurrent, localNewCurrent);
+ }
+ else
+ {
+ localNewCurrent = localCurrent - rounded;
+ if (b.ptr == localNewCurrent)
+ return cas(&_current, localCurrent, localNewCurrent);
+ }
+
+ return false;
+ }
+
+ /**
+ Deallocates all memory allocated by this region, which can be subsequently
+ reused for new allocations.
+ */
+ bool deallocateAll() pure nothrow @nogc
+ {
+ import core.atomic : atomicStore;
+ static if (growDownwards)
+ {
+ atomicStore(_current, cast(shared(void*)) roundedEnd());
+ }
+ else
+ {
+ atomicStore(_current, cast(shared(void*)) roundedBegin());
+ }
+ return true;
+ }
+
+ /**
+ Allocates `n` bytes of memory aligned at alignment `a`.
+ Params:
+ n = number of bytes to allocate
+ a = alignment for the allocated block
+
+ Returns:
+ Either a suitable block of `n` bytes aligned at `a`, or `null`.
+ */
+ void[] alignedAllocate(size_t n, uint a) pure nothrow @trusted @nogc
+ {
+ import core.atomic : cas, atomicLoad;
+ import std.math.traits : isPowerOf2;
+
+ assert(a.isPowerOf2);
+ if (n == 0) return null;
+
+ const rounded = goodAllocSize(n);
+ shared void* localCurrent, localNewCurrent;
+
+ static if (growDownwards)
+ {
+ do
+ {
+ localCurrent = atomicLoad(_current);
+ auto alignedCurrent = cast(void*)(localCurrent - rounded);
+ localNewCurrent = cast(shared(void*)) alignedCurrent.alignDownTo(a);
+ if (alignedCurrent > localCurrent || localNewCurrent > alignedCurrent ||
+ localNewCurrent < _begin)
+ return null;
+ } while (!cas(&_current, localCurrent, localNewCurrent));
+
+ return cast(void[]) localNewCurrent[0 .. n];
+ }
+ else
+ {
+ do
+ {
+ localCurrent = atomicLoad(_current);
+ auto alignedCurrent = alignUpTo(cast(void*) localCurrent, a);
+ localNewCurrent = cast(shared(void*)) (alignedCurrent + rounded);
+ if (alignedCurrent < localCurrent || localNewCurrent < alignedCurrent ||
+ localNewCurrent > _end)
+ return null;
+ } while (!cas(&_current, localCurrent, localNewCurrent));
+
+ return cast(void[]) (localNewCurrent - rounded)[0 .. n];
+ }
+
+ assert(0, "Unexpected error in SharedRegion.alignedAllocate");
+ }
+
+ /**
+ Queries whether `b` has been allocated with this region.
+
+ Params:
+ b = Arbitrary block of memory (`null` is allowed; `owns(null)` returns
+ `false`).
+
+ Returns:
+ `true` if `b` has been allocated with this region, `false` otherwise.
+ */
+ Ternary owns(const void[] b) const pure nothrow @trusted @nogc
+ {
+ return Ternary(b && (&b[0] >= _begin) && (&b[0] + b.length <= _end));
+ }
+
+ /**
+ Returns `Ternary.yes` if no memory has been allocated in this region,
+ `Ternary.no` otherwise. (Never returns `Ternary.unknown`.)
+ */
+ Ternary empty() const pure nothrow @safe @nogc
+ {
+ import core.atomic : atomicLoad;
+
+ auto localCurrent = atomicLoad(_current);
+ static if (growDownwards)
+ return Ternary(localCurrent == roundedEnd());
+ else
+ return Ternary(localCurrent == roundedBegin());
+ }
+
+ /**
+ If `ParentAllocator` is not $(REF_ALTTEXT `NullAllocator`, NullAllocator, std,experimental,allocator,building_blocks,null_allocator) and defines `deallocate`,
+ the region defines a destructor that uses `ParentAllocator.deallocate` to free the
+ memory chunk.
+ */
+ static if (!is(ParentAllocator == NullAllocator)
+ && hasMember!(ParentAllocator, "deallocate"))
+ ~this()
+ {
+ parent.deallocate(cast(void[]) _begin[0 .. _end - _begin]);
+ }
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ static void testAlloc(Allocator)(ref Allocator a, bool growDownwards)
+ {
+ import core.thread : ThreadGroup;
+ import std.algorithm.sorting : sort;
+ import core.internal.spinlock : SpinLock;
+
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+ enum numThreads = 100;
+ void[][numThreads] buf;
+ size_t count = 0;
+
+ void fun()
+ {
+ void[] b = a.allocate(63);
+ assert(b.length == 63);
+
+ lock.lock();
+ buf[count] = b;
+ count++;
+ lock.unlock();
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ sort!((a, b) => a.ptr < b.ptr)(buf[0 .. numThreads]);
+ foreach (i; 0 .. numThreads - 1)
+ {
+ assert(buf[i].ptr + a.goodAllocSize(buf[i].length) == buf[i + 1].ptr);
+ }
+
+ assert(!a.deallocate(buf[1]));
+
+ foreach (i; 0 .. numThreads)
+ {
+ if (!growDownwards)
+ assert(a.deallocate(buf[numThreads - 1 - i]));
+ else
+ assert(a.deallocate(buf[i]));
+ }
+
+ assert(a.deallocateAll());
+ void[] b = a.allocate(63);
+ assert(b.length == 63);
+ assert(a.deallocate(b));
+ }
+
+ auto a1 = SharedRegion!(Mallocator, Mallocator.alignment,
+ Yes.growDownwards)(1024 * 64);
+
+ auto a2 = SharedRegion!(Mallocator, Mallocator.alignment,
+ No.growDownwards)(1024 * 64);
+
+ testAlloc(a1, true);
+ testAlloc(a2, false);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ static void testAlloc(Allocator)(ref Allocator a, bool growDownwards)
+ {
+ import core.thread : ThreadGroup;
+ import std.algorithm.sorting : sort;
+ import core.internal.spinlock : SpinLock;
+
+ SpinLock lock = SpinLock(SpinLock.Contention.brief);
+ enum numThreads = 100;
+ void[][2 * numThreads] buf;
+ size_t count = 0;
+
+ void fun()
+ {
+ void[] b = a.allocate(63);
+ assert(b.length == 63);
+
+ lock.lock();
+ buf[count] = b;
+ count++;
+ lock.unlock();
+
+ b = a.alignedAllocate(63, 32);
+ assert(b.length == 63);
+ assert(cast(size_t) b.ptr % 32 == 0);
+
+ lock.lock();
+ buf[count] = b;
+ count++;
+ lock.unlock();
+ }
+
+ auto tg = new ThreadGroup;
+ foreach (i; 0 .. numThreads)
+ {
+ tg.create(&fun);
+ }
+ tg.joinAll();
+
+ sort!((a, b) => a.ptr < b.ptr)(buf[0 .. 2 * numThreads]);
+ foreach (i; 0 .. 2 * numThreads - 1)
+ {
+ assert(buf[i].ptr + buf[i].length <= buf[i + 1].ptr);
+ }
+
+ assert(!a.deallocate(buf[1]));
+ assert(a.deallocateAll());
+
+ void[] b = a.allocate(13);
+ assert(b.length == 13);
+ assert(a.deallocate(b));
+ }
+
+ auto a1 = SharedRegion!(Mallocator, Mallocator.alignment,
+ Yes.growDownwards)(1024 * 64);
+
+ auto a2 = SharedRegion!(Mallocator, Mallocator.alignment,
+ No.growDownwards)(1024 * 64);
+
+ testAlloc(a1, true);
+ testAlloc(a2, false);
}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d
index ff3261f3e05..8fc7584f796 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d
@@ -1,25 +1,33 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/scoped_allocator.d)
+*/
module std.experimental.allocator.building_blocks.scoped_allocator;
import std.experimental.allocator.common;
/**
-$(D ScopedAllocator) delegates all allocation requests to $(D ParentAllocator).
-When destroyed, the $(D ScopedAllocator) object automatically calls $(D
+`ScopedAllocator` delegates all allocation requests to `ParentAllocator`.
+When destroyed, the `ScopedAllocator` object automatically calls $(D
deallocate) for all memory allocated through its lifetime. (The $(D
deallocateAll) function is also implemented with the same semantics.)
-$(D deallocate) is also supported, which is where most implementation effort
-and overhead of $(D ScopedAllocator) go. If $(D deallocate) is not needed, a
-simpler design combining $(D AllocatorList) with $(D Region) is recommended.
+`deallocate` is also supported, which is where most implementation effort
+and overhead of `ScopedAllocator` go. If `deallocate` is not needed, a
+simpler design combining `AllocatorList` with `Region` is recommended.
*/
struct ScopedAllocator(ParentAllocator)
{
- @system unittest
+ static if (!stateSize!ParentAllocator)
{
- testAllocator!(() => ScopedAllocator());
+ // This test is available only for stateless allocators
+ version (StdUnittest)
+ @system unittest
+ {
+ testAllocator!(() => ScopedAllocator());
+ }
}
import std.experimental.allocator.building_blocks.affix_allocator
@@ -38,8 +46,8 @@ struct ScopedAllocator(ParentAllocator)
// state
/**
- If $(D ParentAllocator) is stateful, $(D parent) is a property giving access
- to an $(D AffixAllocator!ParentAllocator). Otherwise, $(D parent) is an alias for `AffixAllocator!ParentAllocator.instance`.
+ If `ParentAllocator` is stateful, `parent` is a property giving access
+ to an `AffixAllocator!ParentAllocator`. Otherwise, `parent` is an alias for `AffixAllocator!ParentAllocator.instance`.
*/
static if (stateSize!ParentAllocator)
{
@@ -52,12 +60,12 @@ struct ScopedAllocator(ParentAllocator)
private Node* root;
/**
- $(D ScopedAllocator) is not copyable.
+ `ScopedAllocator` is not copyable.
*/
@disable this(this);
/**
- $(D ScopedAllocator)'s destructor releases all memory allocated during its
+ `ScopedAllocator`'s destructor releases all memory allocated during its
lifetime.
*/
~this()
@@ -69,7 +77,7 @@ struct ScopedAllocator(ParentAllocator)
enum alignment = Allocator.alignment;
/**
- Forwards to $(D parent.goodAllocSize) (which accounts for the management
+ Forwards to `parent.goodAllocSize` (which accounts for the management
overhead).
*/
size_t goodAllocSize(size_t n)
@@ -77,14 +85,10 @@ struct ScopedAllocator(ParentAllocator)
return parent.goodAllocSize(n);
}
- /**
- Allocates memory. For management it actually allocates extra memory from
- the parent.
- */
- void[] allocate(size_t n)
- {
- auto b = parent.allocate(n);
- if (!b.ptr) return b;
+ // Common code shared between allocate and allocateZeroed.
+ private enum _processAndReturnAllocateResult =
+ q{
+ if (!b.ptr) return b;
Node* toInsert = & parent.prefix(b);
toInsert.prev = null;
toInsert.next = root;
@@ -93,6 +97,23 @@ struct ScopedAllocator(ParentAllocator)
if (root) root.prev = toInsert;
root = toInsert;
return b;
+ };
+
+ /**
+ Allocates memory. For management it actually allocates extra memory from
+ the parent.
+ */
+ void[] allocate(size_t n)
+ {
+ auto b = parent.allocate(n);
+ mixin(_processAndReturnAllocateResult);
+ }
+
+ static if (hasMember!(Allocator, "allocateZeroed"))
+ package(std) void[] allocateZeroed()(size_t n)
+ {
+ auto b = parent.allocateZeroed(n);
+ mixin(_processAndReturnAllocateResult);
}
/**
@@ -102,15 +123,15 @@ struct ScopedAllocator(ParentAllocator)
bool expand(ref void[] b, size_t delta)
{
auto result = parent.expand(b, delta);
- if (result && b.ptr)
+ if (result && b)
{
- parent.prefix(b).length = b.length;
+ () @trusted { parent.prefix(b).length = b.length; }();
}
return result;
}
/**
- Reallocates $(D b) to new size $(D s).
+ Reallocates `b` to new size `s`.
*/
bool reallocate(ref void[] b, size_t s)
{
@@ -137,7 +158,7 @@ struct ScopedAllocator(ParentAllocator)
}
/**
- Forwards to $(D parent.owns(b)).
+ Forwards to `parent.owns(b)`.
*/
static if (hasMember!(Allocator, "owns"))
Ternary owns(void[] b)
@@ -146,7 +167,7 @@ struct ScopedAllocator(ParentAllocator)
}
/**
- Deallocates $(D b).
+ Deallocates `b`.
*/
static if (hasMember!(Allocator, "deallocate"))
bool deallocate(void[] b)
@@ -184,6 +205,7 @@ struct ScopedAllocator(ParentAllocator)
Returns `Ternary.yes` if this allocator is not responsible for any memory,
`Ternary.no` otherwise. (Never returns `Ternary.unknown`.)
*/
+ pure nothrow @safe @nogc
Ternary empty() const
{
return Ternary(root is null);
@@ -202,6 +224,7 @@ struct ScopedAllocator(ParentAllocator)
assert(alloc.empty == Ternary.no);
}
+version (StdUnittest)
@system unittest
{
import std.experimental.allocator.gc_allocator : GCAllocator;
@@ -219,3 +242,62 @@ struct ScopedAllocator(ParentAllocator)
alloc.dispose(foo);
alloc.dispose(bar); // segfault here
}
+
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ ScopedAllocator!GCAllocator a;
+
+ assert(__traits(compiles, (() nothrow @safe @nogc => a.goodAllocSize(0))()));
+
+ // Ensure deallocate inherits from parent allocators
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ () nothrow @nogc { a.deallocate(b); }();
+}
+
+// Test that deallocateAll infers from parent
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+
+ ScopedAllocator!(Region!()) a;
+ a.parent.parent = Region!()(new ubyte[1024 * 64]);
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
+ assert(b.length == 64);
+ assert((() nothrow @nogc => a.reallocate(b, 100))());
+ assert(b.length == 100);
+ assert((() nothrow @nogc => a.deallocateAll())());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+
+ auto a = Region!(Mallocator)(1024 * 64);
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
+ assert(b.length == 64);
+ assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+ assert((() nothrow @nogc => a.reallocate(b, 100))());
+ assert(b.length == 100);
+ assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+ assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no);
+}
+
+// Test empty
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+ ScopedAllocator!Mallocator alloc;
+
+ assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.yes);
+ const b = alloc.allocate(10);
+ assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.no);
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/segregator.d b/libphobos/src/std/experimental/allocator/building_blocks/segregator.d
index 76ca6f409d0..655db4567ce 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/segregator.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/segregator.d
@@ -1,21 +1,24 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/segregator.d)
+*/
module std.experimental.allocator.building_blocks.segregator;
import std.experimental.allocator.common;
/**
Dispatches allocations (and deallocations) between two allocators ($(D
-SmallAllocator) and $(D LargeAllocator)) depending on the size allocated, as
-follows. All allocations smaller than or equal to $(D threshold) will be
-dispatched to $(D SmallAllocator). The others will go to $(D LargeAllocator).
+SmallAllocator) and `LargeAllocator`) depending on the size allocated, as
+follows. All allocations smaller than or equal to `threshold` will be
+dispatched to `SmallAllocator`. The others will go to `LargeAllocator`.
-If both allocators are $(D shared), the $(D Segregator) will also offer $(D
+If both allocators are `shared`, the `Segregator` will also offer $(D
shared) methods.
*/
struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
{
import std.algorithm.comparison : min;
- import std.traits : hasMember;
+ import std.traits : hasMember, ReturnType;
import std.typecons : Ternary;
static if (stateSize!SmallAllocator) private SmallAllocator _small;
@@ -31,73 +34,73 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
enum uint alignment;
/**
This method is defined only if at least one of the allocators defines
- it. The good allocation size is obtained from $(D SmallAllocator) if $(D
- s <= threshold), or $(D LargeAllocator) otherwise. (If one of the
- allocators does not define $(D goodAllocSize), the default
+ it. The good allocation size is obtained from `SmallAllocator` if $(D
+ s <= threshold), or `LargeAllocator` otherwise. (If one of the
+ allocators does not define `goodAllocSize`, the default
implementation in this module applies.)
*/
static size_t goodAllocSize(size_t s);
/**
- The memory is obtained from $(D SmallAllocator) if $(D s <= threshold),
- or $(D LargeAllocator) otherwise.
+ The memory is obtained from `SmallAllocator` if $(D s <= threshold),
+ or `LargeAllocator` otherwise.
*/
void[] allocate(size_t);
/**
This method is defined if both allocators define it, and forwards to
- $(D SmallAllocator) or $(D LargeAllocator) appropriately.
+ `SmallAllocator` or `LargeAllocator` appropriately.
*/
void[] alignedAllocate(size_t, uint);
/**
This method is defined only if at least one of the allocators defines
- it. If $(D SmallAllocator) defines $(D expand) and $(D b.length +
- delta <= threshold), the call is forwarded to $(D SmallAllocator). If $(D
- LargeAllocator) defines $(D expand) and $(D b.length > threshold), the
- call is forwarded to $(D LargeAllocator). Otherwise, the call returns
- $(D false).
+ it. If `SmallAllocator` defines `expand` and $(D b.length +
+ delta <= threshold), the call is forwarded to `SmallAllocator`. If $(D
+ LargeAllocator) defines `expand` and $(D b.length > threshold), the
+ call is forwarded to `LargeAllocator`. Otherwise, the call returns
+ `false`.
*/
bool expand(ref void[] b, size_t delta);
/**
This method is defined only if at least one of the allocators defines
- it. If $(D SmallAllocator) defines $(D reallocate) and $(D b.length <=
+ it. If `SmallAllocator` defines `reallocate` and $(D b.length <=
threshold && s <= threshold), the call is forwarded to $(D
- SmallAllocator). If $(D LargeAllocator) defines $(D expand) and $(D
+ SmallAllocator). If `LargeAllocator` defines `expand` and $(D
b.length > threshold && s > threshold), the call is forwarded to $(D
- LargeAllocator). Otherwise, the call returns $(D false).
+ LargeAllocator). Otherwise, the call returns `false`.
*/
bool reallocate(ref void[] b, size_t s);
/**
This method is defined only if at least one of the allocators defines
- it, and work similarly to $(D reallocate).
+ it, and work similarly to `reallocate`.
*/
- bool alignedReallocate(ref void[] b, size_t s);
+ bool alignedReallocate(ref void[] b, size_t s, uint a);
/**
This method is defined only if both allocators define it. The call is
- forwarded to $(D SmallAllocator) if $(D b.length <= threshold), or $(D
+ forwarded to `SmallAllocator` if $(D b.length <= threshold), or $(D
LargeAllocator) otherwise.
*/
Ternary owns(void[] b);
/**
This function is defined only if both allocators define it, and forwards
- appropriately depending on $(D b.length).
+ appropriately depending on `b.length`.
*/
bool deallocate(void[] b);
/**
This function is defined only if both allocators define it, and calls
- $(D deallocateAll) for them in turn.
+ `deallocateAll` for them in turn.
*/
bool deallocateAll();
/**
This function is defined only if both allocators define it, and returns
- the conjunction of $(D empty) calls for the two.
+ the conjunction of `empty` calls for the two.
*/
Ternary empty();
}
/**
- Composite allocators involving nested instantiations of $(D Segregator) make
+ Composite allocators involving nested instantiations of `Segregator` make
it difficult to access individual sub-allocators stored within. $(D
allocatorForSize) simplifies the task by supplying the allocator nested
- inside a $(D Segregator) that is responsible for a specific size $(D s).
+ inside a `Segregator` that is responsible for a specific size `s`.
Example:
----
@@ -195,22 +198,50 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
static if (hasMember!(SmallAllocator, "alignedReallocate")
|| hasMember!(LargeAllocator, "alignedReallocate"))
- bool reallocate(ref void[] b, size_t s)
+ bool alignedReallocate(ref void[] b, size_t s, uint a)
{
static if (hasMember!(SmallAllocator, "alignedReallocate"))
if (b.length <= threshold && s <= threshold)
{
// Old and new allocations handled by _small
- return _small.alignedReallocate(b, s);
+ return _small.alignedReallocate(b, s, a);
}
static if (hasMember!(LargeAllocator, "alignedReallocate"))
if (b.length > threshold && s > threshold)
{
// Old and new allocations handled by _large
- return _large.alignedReallocate(b, s);
+ return _large.alignedReallocate(b, s, a);
}
// Cross-allocator transgression
- return .alignedReallocate(this, b, s);
+ return .alignedReallocate(this, b, s, a);
+ }
+
+ static if (hasMember!(SmallAllocator, "allocateZeroed")
+ || hasMember!(LargeAllocator, "allocateZeroed"))
+ package(std) void[] allocateZeroed()(size_t s)
+ {
+ if (s <= threshold)
+ {
+ static if (hasMember!(SmallAllocator, "allocateZeroed"))
+ return _small.allocateZeroed(s);
+ else
+ {
+ auto b = _small.allocate(s);
+ (() @trusted => (cast(ubyte[]) b)[] = 0)(); // OK even if b is null.
+ return b;
+ }
+ }
+ else
+ {
+ static if (hasMember!(LargeAllocator, "allocateZeroed"))
+ return _large.allocateZeroed(s);
+ else
+ {
+ auto b = _large.allocate(s);
+ (() @trusted => (cast(ubyte[]) b)[] = 0)(); // OK even if b is null.
+ return b;
+ }
+ }
}
static if (hasMember!(SmallAllocator, "owns")
@@ -242,7 +273,7 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
&& hasMember!(LargeAllocator, "empty"))
Ternary empty()
{
- return _small.empty && _large.empty;
+ return _small.empty & _large.empty;
}
static if (hasMember!(SmallAllocator, "resolveInternalPointer")
@@ -268,7 +299,7 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
else
{
static if (!stateSize!SmallAllocator && !stateSize!LargeAllocator)
- static __gshared Segregator instance;
+ __gshared Segregator instance;
mixin Impl!();
}
}
@@ -296,8 +327,8 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
}
/**
-A $(D Segregator) with more than three arguments expands to a composition of
-elemental $(D Segregator)s, as illustrated by the following example:
+A `Segregator` with more than three arguments expands to a composition of
+elemental `Segregator`s, as illustrated by the following example:
----
alias A =
@@ -309,11 +340,11 @@ alias A =
);
----
-With this definition, allocation requests for $(D n1) bytes or less are directed
-to $(D A1); requests between $(D n1 + 1) and $(D n2) bytes (inclusive) are
-directed to $(D A2); requests between $(D n2 + 1) and $(D n3) bytes (inclusive)
-are directed to $(D A3); and requests for more than $(D n3) bytes are directed
-to $(D A4). If some particular range should not be handled, $(D NullAllocator)
+With this definition, allocation requests for `n1` bytes or less are directed
+to `A1`; requests between $(D n1 + 1) and `n2` bytes (inclusive) are
+directed to `A2`; requests between $(D n2 + 1) and `n3` bytes (inclusive)
+are directed to `A3`; and requests for more than `n3` bytes are directed
+to `A4`. If some particular range should not be handled, `NullAllocator`
may be used appropriately.
*/
@@ -359,3 +390,131 @@ if (Args.length > 3)
assert(b.length == 201);
a.deallocate(b);
}
+
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator.building_blocks.kernighan_ritchie : KRRegion;
+ Segregator!(128, GCAllocator, KRRegion!GCAllocator) alloc;
+ assert((() nothrow @safe @nogc => alloc.goodAllocSize(1))()
+ == GCAllocator.instance.goodAllocSize(1));
+
+ // Note: we infer `shared` from GCAllocator.goodAllocSize so we need a
+ // shared object in order to be able to use the function
+ shared Segregator!(128, GCAllocator, GCAllocator) sharedAlloc;
+ assert((() nothrow @safe @nogc => sharedAlloc.goodAllocSize(1))()
+ == GCAllocator.instance.goodAllocSize(1));
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+ import std.typecons : Ternary;
+
+ alias A =
+ Segregator!(
+ 128, BitmappedBlock!(4096),
+ BitmappedBlock!(4096)
+ );
+
+ A a = A(
+ BitmappedBlock!(4096)(new ubyte[4096 * 1024]),
+ BitmappedBlock!(4096)(new ubyte[4096 * 1024])
+ );
+
+ assert(a.empty == Ternary.yes);
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert(a.empty == Ternary.no);
+ assert(a.alignedReallocate(b, 256, 512));
+ assert(b.length == 256);
+ assert(a.alignedReallocate(b, 42, 512));
+ assert(b.length == 42);
+ assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
+ assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no);
+ // Ensure deallocate inherits from parent allocators
+ assert((() nothrow @nogc => a.deallocate(b))());
+ assert(a.empty == Ternary.yes);
+
+ // Test that deallocateAll inherits from parents
+ auto c = a.allocate(42);
+ assert(c.length == 42);
+ assert((() pure nothrow @safe @nogc => a.expand(c, 58))());
+ assert(c.length == 100);
+ assert(a.empty == Ternary.no);
+ assert((() nothrow @nogc => a.deallocateAll())());
+ assert(a.empty == Ternary.yes);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.typecons : Ternary;
+
+ shared Segregator!(1024 * 4, GCAllocator, GCAllocator) a;
+
+ auto b = a.allocate(201);
+ assert(b.length == 201);
+
+ void[] p;
+ assert((() nothrow @safe @nogc => a.resolveInternalPointer(&b[0], p))() == Ternary.yes);
+ assert((() nothrow @safe @nogc => a.resolveInternalPointer(null, p))() == Ternary.no);
+
+ // Ensure deallocate inherits from parent allocators
+ assert((() nothrow @nogc => a.deallocate(b))());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlockWithInternalPointers;
+ import std.typecons : Ternary;
+
+ alias A =
+ Segregator!(
+ 10_240, BitmappedBlockWithInternalPointers!(4096),
+ BitmappedBlockWithInternalPointers!(4096)
+ );
+
+ A a = A(
+ BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]),
+ BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024])
+ );
+
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
+ auto b = a.allocate(201);
+ assert(b.length == 201);
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.no);
+ assert((() nothrow @nogc => a.deallocate(b))());
+}
+
+// Test that reallocate infers from parent
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ alias a = Segregator!(10_240, Mallocator, Mallocator).instance;
+
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ assert((() nothrow @nogc => a.reallocate(b, 100))());
+ assert(b.length == 100);
+ assert((() nothrow @nogc => a.deallocate(b))());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.typecons : Ternary;
+
+ auto a = Segregator!(10_240, Region!(), Region!())(
+ Region!()(new ubyte[4096 * 1024]),
+ Region!()(new ubyte[4096 * 1024]));
+
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.yes);
+ auto b = a.alignedAllocate(42, 8);
+ assert(b.length == 42);
+ assert((() nothrow @nogc => a.alignedReallocate(b, 100, 8))());
+ assert(b.length == 100);
+ assert((() nothrow @safe @nogc => a.empty)() == Ternary.no);
+ assert((() nothrow @nogc => a.deallocate(b))());
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d b/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d
index aba084e8129..d57b3edd16f 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d
@@ -4,60 +4,63 @@ Allocator that collects useful statistics about allocations, both global and per
calling point. The statistics collected can be configured statically by choosing
combinations of `Options` appropriately.
-Example:
-----
-import std.experimental.allocator.gc_allocator : GCAllocator;
-import std.experimental.allocator.building_blocks.free_list : FreeList;
-alias Allocator = StatsCollector!(GCAllocator, Options.bytesUsed);
-----
+Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/stats_collector.d)
*/
module std.experimental.allocator.building_blocks.stats_collector;
+///
+@safe unittest
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator.building_blocks.free_list : FreeList;
+ alias Allocator = StatsCollector!(GCAllocator, Options.bytesUsed);
+}
+
import std.experimental.allocator.common;
/**
-_Options for $(D StatsCollector) defined below. Each enables during
+_Options for `StatsCollector` defined below. Each enables during
compilation one specific counter, statistic, or other piece of information.
*/
enum Options : ulong
{
/**
- Counts the number of calls to $(D owns).
+ Counts the number of calls to `owns`.
*/
numOwns = 1u << 0,
/**
- Counts the number of calls to $(D allocate). All calls are counted,
+ Counts the number of calls to `allocate`. All calls are counted,
including requests for zero bytes or failed requests.
*/
numAllocate = 1u << 1,
/**
- Counts the number of calls to $(D allocate) that succeeded, i.e. they
+ Counts the number of calls to `allocate` that succeeded, i.e. they
returned a block as large as requested. (N.B. requests for zero bytes count
as successful.)
*/
numAllocateOK = 1u << 2,
/**
- Counts the number of calls to $(D expand), regardless of arguments or
+ Counts the number of calls to `expand`, regardless of arguments or
result.
*/
numExpand = 1u << 3,
/**
- Counts the number of calls to $(D expand) that resulted in a successful
+ Counts the number of calls to `expand` that resulted in a successful
expansion.
*/
numExpandOK = 1u << 4,
/**
- Counts the number of calls to $(D reallocate), regardless of arguments or
+ Counts the number of calls to `reallocate`, regardless of arguments or
result.
*/
numReallocate = 1u << 5,
/**
- Counts the number of calls to $(D reallocate) that succeeded.
+ Counts the number of calls to `reallocate` that succeeded.
(Reallocations to zero bytes count as successful.)
*/
numReallocateOK = 1u << 6,
/**
- Counts the number of calls to $(D reallocate) that resulted in an in-place
+ Counts the number of calls to `reallocate` that resulted in an in-place
reallocation (no memory moved). If this number is close to the total number
of reallocations, that indicates the allocator finds room at the current
block's end in a large fraction of the cases, but also that internal
@@ -66,91 +69,102 @@ enum Options : ulong
*/
numReallocateInPlace = 1u << 7,
/**
- Counts the number of calls to $(D deallocate).
+ Counts the number of calls to `deallocate`.
*/
numDeallocate = 1u << 8,
/**
- Counts the number of calls to $(D deallocateAll).
+ Counts the number of calls to `deallocateAll`.
*/
numDeallocateAll = 1u << 9,
/**
- Chooses all $(D numXxx) flags.
+ Counts the number of calls to `alignedAllocate`. All calls are counted,
+ including requests for zero bytes or failed requests.
+ */
+ numAlignedAllocate = 1u << 10,
+ /**
+ Counts the number of calls to `alignedAllocate` that succeeded, i.e. they
+ returned a block as large as requested. (N.B. requests for zero bytes count
+ as successful.)
*/
- numAll = (1u << 10) - 1,
+ numAlignedAllocateOk = 1u << 11,
+ /**
+ Chooses all `numXxx` flags.
+ */
+ numAll = (1u << 12) - 1,
/**
Tracks bytes currently allocated by this allocator. This number goes up
and down as memory is allocated and deallocated, and is zero if the
allocator currently has no active allocation.
*/
- bytesUsed = 1u << 10,
+ bytesUsed = 1u << 12,
/**
- Tracks total cumulative bytes allocated by means of $(D allocate),
- $(D expand), and $(D reallocate) (when resulting in an expansion). This
+ Tracks total cumulative bytes allocated by means of `allocate`,
+ `expand`, and `reallocate` (when resulting in an expansion). This
number always grows and indicates allocation traffic. To compute bytes
- deallocated cumulatively, subtract $(D bytesUsed) from $(D bytesAllocated).
+ deallocated cumulatively, subtract `bytesUsed` from `bytesAllocated`.
*/
- bytesAllocated = 1u << 11,
+ bytesAllocated = 1u << 13,
/**
- Tracks the sum of all $(D delta) values in calls of the form
- $(D expand(b, delta)) that succeed (return $(D true)).
+ Tracks the sum of all `delta` values in calls of the form
+ $(D expand(b, delta)) that succeed (return `true`).
*/
- bytesExpanded = 1u << 12,
+ bytesExpanded = 1u << 14,
/**
Tracks the sum of all $(D b.length - s) with $(D b.length > s) in calls of
- the form $(D realloc(b, s)) that succeed (return $(D true)). In per-call
+ the form $(D realloc(b, s)) that succeed (return `true`). In per-call
statistics, also unambiguously counts the bytes deallocated with
- $(D deallocate).
+ `deallocate`.
*/
- bytesContracted = 1u << 13,
+ bytesContracted = 1u << 15,
/**
- Tracks the sum of all bytes moved as a result of calls to $(D realloc) that
+ Tracks the sum of all bytes moved as a result of calls to `realloc` that
were unable to reallocate in place. A large number (relative to $(D
bytesAllocated)) indicates that the application should use larger
preallocations.
*/
- bytesMoved = 1u << 14,
+ bytesMoved = 1u << 16,
/**
- Tracks the sum of all bytes NOT moved as result of calls to $(D realloc)
+ Tracks the sum of all bytes NOT moved as result of calls to `realloc`
that managed to reallocate in place. A large number (relative to $(D
bytesAllocated)) indicates that the application is expansion-intensive and
is saving a good amount of moves. However, if this number is relatively
- small and $(D bytesSlack) is high, it means the application is
+ small and `bytesSlack` is high, it means the application is
overallocating for little benefit.
*/
- bytesNotMoved = 1u << 15,
+ bytesNotMoved = 1u << 17,
/**
Measures the sum of extra bytes allocated beyond the bytes requested, i.e.
the $(HTTP goo.gl/YoKffF, internal fragmentation). This is the current
effective number of slack bytes, and it goes up and down with time.
*/
- bytesSlack = 1u << 16,
+ bytesSlack = 1u << 18,
/**
Measures the maximum bytes allocated over the time. This is useful for
dimensioning allocators.
*/
- bytesHighTide = 1u << 17,
+ bytesHighTide = 1u << 19,
/**
- Chooses all $(D byteXxx) flags.
+ Chooses all `byteXxx` flags.
*/
- bytesAll = ((1u << 18) - 1) & ~numAll,
+ bytesAll = ((1u << 20) - 1) & ~numAll,
/**
Combines all flags above.
*/
- all = (1u << 18) - 1
+ all = (1u << 20) - 1
}
/**
Allocator that collects extra data about allocations. Since each piece of
information adds size and time overhead, statistics can be individually enabled
-or disabled through compile-time $(D flags).
+or disabled through compile-time `flags`.
-All stats of the form $(D numXxx) record counts of events occurring, such as
-calls to functions and specific results. The stats of the form $(D bytesXxx)
+All stats of the form `numXxx` record counts of events occurring, such as
+calls to functions and specific results. The stats of the form `bytesXxx`
collect cumulative sizes.
-In addition, the data $(D callerSize), $(D callerModule), $(D callerFile), $(D
-callerLine), and $(D callerTime) is associated with each specific allocation.
+In addition, the data `callerSize`, `callerModule`, `callerFile`, $(D
+callerLine), and `callerTime` is associated with each specific allocation.
This data prefixes each allocation.
*/
@@ -188,7 +202,7 @@ private:
version (StdDdoc)
{
/**
- Read-only properties enabled by the homonym $(D flags) chosen by the
+ Read-only properties enabled by the homonym `flags` chosen by the
user.
Example:
@@ -225,6 +239,10 @@ private:
/// Ditto
@property ulong numDeallocateAll() const;
/// Ditto
+ @property ulong numAlignedAllocate() const;
+ /// Ditto
+ @property ulong numAlignedAllocateOk() const;
+ /// Ditto
@property ulong bytesUsed() const;
/// Ditto
@property ulong bytesAllocated() const;
@@ -264,6 +282,8 @@ private:
"numReallocateInPlace",
"numDeallocate",
"numDeallocateAll",
+ "numAlignedAllocate",
+ "numAlignedAllocateOk",
"bytesUsed",
"bytesAllocated",
"bytesExpanded",
@@ -276,11 +296,11 @@ private:
public:
- /// Alignment offered is equal to $(D Allocator.alignment).
+ /// Alignment offered is equal to `Allocator.alignment`.
alias alignment = Allocator.alignment;
/**
- Increments $(D numOwns) (per instance and and per call) and forwards to $(D
+ Increments `numOwns` (per instance and and per call) and forwards to $(D
parent.owns(b)).
*/
static if (hasMember!(Allocator, "owns"))
@@ -289,7 +309,7 @@ public:
Ternary owns(void[] b)
{ return ownsImpl(b); }
else
- Ternary owns(string f = __FILE, uint n = line)(void[] b)
+ Ternary owns(string f = __FILE__, uint n = __LINE__)(void[] b)
{ return ownsImpl!(f, n)(b); }
}
@@ -301,10 +321,10 @@ public:
}
/**
- Forwards to $(D parent.allocate). Affects per instance: $(D numAllocate),
- $(D bytesUsed), $(D bytesAllocated), $(D bytesSlack), $(D numAllocateOK),
- and $(D bytesHighTide). Affects per call: $(D numAllocate), $(D
- numAllocateOK), and $(D bytesAllocated).
+ Forwards to `parent.allocate`. Affects per instance: `numAllocate`,
+ `bytesUsed`, `bytesAllocated`, `bytesSlack`, `numAllocateOK`,
+ and `bytesHighTide`. Affects per call: `numAllocate`, $(D
+ numAllocateOK), and `bytesAllocated`.
*/
static if (!(perCallFlags
& (Options.numAllocate | Options.numAllocateOK
@@ -320,9 +340,9 @@ public:
{ return allocateImpl!(f, n)(bytes); }
}
- private void[] allocateImpl(string f = null, ulong n = 0)(size_t bytes)
- {
- auto result = parent.allocate(bytes);
+ // Common code currently shared between allocateImpl and allocateZeroedImpl.
+ private enum _updateStatsForAllocateResult =
+ q{
add!"bytesUsed"(result.length);
add!"bytesAllocated"(result.length);
immutable slack = this.goodAllocSize(result.length) - result.length;
@@ -331,15 +351,92 @@ public:
add!"numAllocateOK"(result.length == bytes); // allocating 0 bytes is OK
addPerCall!(f, n, "numAllocate", "numAllocateOK", "bytesAllocated")
(1, result.length == bytes, result.length);
+ };
+
+ private void[] allocateImpl(string f = null, ulong n = 0)(size_t bytes)
+ {
+ auto result = parent.allocate(bytes);
+ mixin(_updateStatsForAllocateResult);
+ return result;
+ }
+
+ static if (hasMember!(Allocator, "allocateZeroed"))
+ {
+ static if (!(perCallFlags
+ & (Options.numAllocate | Options.numAllocateOK
+ | Options.bytesAllocated)))
+ {
+ package(std) void[] allocateZeroed()(size_t n)
+ { return allocateZeroedImpl(n); }
+ }
+ else
+ {
+ package(std) void[] allocateZeroed(string f = __FILE__, ulong n = __LINE__)
+ (size_t bytes)
+ { return allocateZeroedImpl!(f, n)(bytes); }
+ }
+
+ private void[] allocateZeroedImpl(string f = null, ulong n = 0)(size_t bytes)
+ {
+ auto result = parent.allocateZeroed(bytes);
+ // Note: calls to `allocateZeroed` are counted for statistical purposes
+ // as if they were calls to `allocate`. If/when `allocateZeroed` is made
+ // public it might be of interest to count such calls separately.
+ mixin(_updateStatsForAllocateResult);
+ return result;
+ }
+ }
+
+ /**
+ Forwards to `parent.alignedAllocate`. Affects per instance: `numAlignedAllocate`,
+ `bytesUsed`, `bytesAllocated`, `bytesSlack`, `numAlignedAllocateOk`,
+ and `bytesHighTide`. Affects per call: `numAlignedAllocate`, `numAlignedAllocateOk`,
+ and `bytesAllocated`.
+ */
+ static if (!(perCallFlags
+ & (Options.numAlignedAllocate | Options.numAlignedAllocateOk
+ | Options.bytesAllocated)))
+ {
+ void[] alignedAllocate(size_t n, uint a)
+ { return alignedAllocateImpl(n, a); }
+ }
+ else
+ {
+ void[] alignedAllocate(string f = __FILE__, ulong n = __LINE__)
+ (size_t bytes, uint a)
+ { return alignedAllocateImpl!(f, n)(bytes, a); }
+ }
+
+ private void[] alignedAllocateImpl(string f = null, ulong n = 0)(size_t bytes, uint a)
+ {
+ up!"numAlignedAllocate";
+ static if (!hasMember!(Allocator, "alignedAllocate"))
+ {
+ if (bytes == 0)
+ up!"numAlignedAllocateOk";
+ void[] result = null;
+ }
+ else
+ {
+ auto result = parent.alignedAllocate(bytes, a);
+ add!"bytesUsed"(result.length);
+ add!"bytesAllocated"(result.length);
+ immutable slack = this.goodAllocSize(result.length) - result.length;
+ add!"bytesSlack"(slack);
+ add!"numAlignedAllocateOk"(result.length == bytes); // allocating 0 bytes is OK
+ }
+ addPerCall!(f, n, "numAlignedAllocate", "numAlignedAllocateOk", "bytesAllocated")
+ (1, result.length == bytes, result.length);
+
return result;
}
/**
- Defined whether or not $(D Allocator.expand) is defined. Affects
- per instance: $(D numExpand), $(D numExpandOK), $(D bytesExpanded),
- $(D bytesSlack), $(D bytesAllocated), and $(D bytesUsed). Affects per call:
- $(D numExpand), $(D numExpandOK), $(D bytesExpanded), and
- $(D bytesAllocated).
+ Defined whether or not `Allocator.expand` is defined. Affects
+ per instance: `numExpand`, `numExpandOK`, `bytesExpanded`,
+ `bytesSlack`, `bytesAllocated`, and `bytesUsed`. Affects per call:
+ `numExpand`, `numExpandOK`, `bytesExpanded`, and
+ `bytesAllocated`.
*/
static if (!(perCallFlags
& (Options.numExpand | Options.numExpandOK | Options.bytesExpanded)))
@@ -385,13 +482,13 @@ public:
}
/**
- Defined whether or not $(D Allocator.reallocate) is defined. Affects
- per instance: $(D numReallocate), $(D numReallocateOK), $(D
- numReallocateInPlace), $(D bytesNotMoved), $(D bytesAllocated), $(D
- bytesSlack), $(D bytesExpanded), and $(D bytesContracted). Affects per call:
- $(D numReallocate), $(D numReallocateOK), $(D numReallocateInPlace),
- $(D bytesNotMoved), $(D bytesExpanded), $(D bytesContracted), and
- $(D bytesMoved).
+ Defined whether or not `Allocator.reallocate` is defined. Affects
+ per instance: `numReallocate`, `numReallocateOK`, $(D
+ numReallocateInPlace), `bytesNotMoved`, `bytesAllocated`, $(D
+ bytesSlack), `bytesExpanded`, and `bytesContracted`. Affects per call:
+ `numReallocate`, `numReallocateOK`, `numReallocateInPlace`,
+ `bytesNotMoved`, `bytesExpanded`, `bytesContracted`, and
+ `bytesMoved`.
*/
static if (!(perCallFlags
& (Options.numReallocate | Options.numReallocateOK
@@ -465,9 +562,9 @@ public:
}
/**
- Defined whether or not $(D Allocator.deallocate) is defined. Affects
- per instance: $(D numDeallocate), $(D bytesUsed), and $(D bytesSlack).
- Affects per call: $(D numDeallocate) and $(D bytesContracted).
+ Defined whether or not `Allocator.deallocate` is defined. Affects
+ per instance: `numDeallocate`, `bytesUsed`, and `bytesSlack`.
+ Affects per call: `numDeallocate` and `bytesContracted`.
*/
static if (!(perCallFlags &
(Options.numDeallocate | Options.bytesContracted)))
@@ -492,8 +589,8 @@ public:
static if (hasMember!(Allocator, "deallocateAll"))
{
/**
- Defined only if $(D Allocator.deallocateAll) is defined. Affects
- per instance and per call $(D numDeallocateAll).
+ Defined only if `Allocator.deallocateAll` is defined. Affects
+ per instance and per call `numDeallocateAll`.
*/
static if (!(perCallFlags & Options.numDeallocateAll))
bool deallocateAll()
@@ -513,19 +610,20 @@ public:
}
/**
- Defined only if $(D Options.bytesUsed) is defined. Returns $(D bytesUsed ==
+ Defined only if `Options.bytesUsed` is defined. Returns $(D bytesUsed ==
0).
*/
static if (flags & Options.bytesUsed)
+ pure nothrow @safe @nogc
Ternary empty()
{
return Ternary(_bytesUsed == 0);
}
/**
- Reports per instance statistics to $(D output) (e.g. $(D stdout)). The
+ Reports per instance statistics to `output` (e.g. `stdout`). The
format is simple: one kind and value per line, separated by a colon, e.g.
- $(D bytesAllocated:7395404)
+ `bytesAllocated:7395404`
*/
void reportStatistics(R)(auto ref R output)
{
@@ -542,7 +640,7 @@ public:
static if (perCallFlags)
{
/**
- Defined if $(D perCallFlags) is nonzero.
+ Defined if `perCallFlags` is nonzero.
*/
struct PerCallStatistics
{
@@ -552,7 +650,7 @@ public:
uint line;
/// The options corresponding to the statistics collected.
Options[] opts;
- /// The values of the statistics. Has the same length as $(D opts).
+ /// The values of the statistics. Has the same length as `opts`.
ulong[] values;
// Next in the chain.
private PerCallStatistics* next;
@@ -578,7 +676,7 @@ public:
private static PerCallStatistics* root;
/**
- Defined if $(D perCallFlags) is nonzero. Iterates all monitored
+ Defined if `perCallFlags` is nonzero. Iterates all monitored
file/line instances. The order of iteration is not meaningful (items
are inserted at the front of a list upon the first call), so
preprocessing the statistics after collection might be appropriate.
@@ -597,7 +695,7 @@ public:
}
/**
- Defined if $(D perCallFlags) is nonzero. Outputs (e.g. to a $(D File))
+ Defined if `perCallFlags` is nonzero. Outputs (e.g. to a `File`)
a simple report of the collected per-call statistics.
*/
static void reportPerCallStatistics(R)(auto ref R output)
@@ -631,7 +729,7 @@ public:
private void addPerCall(string f, uint n, names...)(ulong[] values...)
{
import std.array : join;
- enum uint mask = mixin("Options."~[names].join("|Options."));
+ enum ulong mask = mixin("Options."~[names].join("|Options."));
static if (perCallFlags & mask)
{
// Per allocation info
@@ -673,7 +771,7 @@ public:
scope(exit) remove(f);
Allocator.reportPerCallStatistics(File(f, "w"));
alloc.reportStatistics(File(f, "a"));
- assert(File(f).byLine.walkLength == 22);
+ assert(File(f).byLine.walkLength == 24);
}
@system unittest
@@ -681,11 +779,13 @@ public:
void test(Allocator)()
{
import std.range : walkLength;
- import std.stdio : writeln;
+ import std.typecons : Ternary;
+
Allocator a;
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.yes);
auto b1 = a.allocate(100);
assert(a.numAllocate == 1);
- assert(a.expand(b1, 0));
+ assert((() nothrow @safe => a.expand(b1, 0))());
assert(a.reallocate(b1, b1.length + 1));
auto b2 = a.allocate(101);
assert(a.numAllocate == 2);
@@ -694,12 +794,13 @@ public:
auto b3 = a.allocate(202);
assert(a.numAllocate == 3);
assert(a.bytesAllocated == 404);
+ assert((() pure nothrow @safe @nogc => a.empty)() == Ternary.no);
- a.deallocate(b2);
+ () nothrow @nogc { a.deallocate(b2); }();
assert(a.numDeallocate == 1);
- a.deallocate(b1);
+ () nothrow @nogc { a.deallocate(b1); }();
assert(a.numDeallocate == 2);
- a.deallocate(b3);
+ () nothrow @nogc { a.deallocate(b3); }();
assert(a.numDeallocate == 3);
assert(a.numAllocate == a.numDeallocate);
assert(a.bytesUsed == 0);
@@ -717,19 +818,73 @@ public:
void test(Allocator)()
{
import std.range : walkLength;
- import std.stdio : writeln;
Allocator a;
auto b1 = a.allocate(100);
- assert(a.expand(b1, 0));
+ assert((() nothrow @safe => a.expand(b1, 0))());
assert(a.reallocate(b1, b1.length + 1));
auto b2 = a.allocate(101);
auto b3 = a.allocate(202);
- a.deallocate(b2);
- a.deallocate(b1);
- a.deallocate(b3);
+ () nothrow @nogc { a.deallocate(b2); }();
+ () nothrow @nogc { a.deallocate(b1); }();
+ () nothrow @nogc { a.deallocate(b3); }();
}
import std.experimental.allocator.building_blocks.free_list : FreeList;
import std.experimental.allocator.gc_allocator : GCAllocator;
test!(StatsCollector!(GCAllocator, 0, 0));
}
+
+@system unittest
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ StatsCollector!(GCAllocator, 0, 0) a;
+
+ // calls std.experimental.allocator.common.goodAllocSize
+ assert((() pure nothrow @safe @nogc => a.goodAllocSize(1))());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+
+ auto a = StatsCollector!(Region!(), Options.all, Options.all)(Region!()(new ubyte[1024 * 64]));
+ auto b = a.allocate(42);
+ assert(b.length == 42);
+ // Test that reallocate infers from parent
+ assert((() nothrow @nogc => a.reallocate(b, 100))());
+ assert(b.length == 100);
+ // Test that deallocateAll infers from parent
+ assert((() nothrow @nogc => a.deallocateAll())());
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+
+ auto a = StatsCollector!(Region!(), Options.all)(Region!()(new ubyte[1024 * 64]));
+ auto b = a.alignedAllocate(42, 128);
+ assert(b.length == 42);
+ assert(b.ptr.alignedAt(128));
+ assert(a.numAlignedAllocate == 1);
+ assert(a.numAlignedAllocateOk == 1);
+ assert(a.bytesUsed == 42);
+
+ b = a.alignedAllocate(23, 256);
+ assert(b.length == 23);
+ assert(b.ptr.alignedAt(256));
+ assert(a.numAlignedAllocate == 2);
+ assert(a.numAlignedAllocateOk == 2);
+ assert(a.bytesUsed == 65);
+
+ b = a.alignedAllocate(0, 512);
+ assert(b.length == 0);
+ assert(a.numAlignedAllocate == 3);
+ assert(a.numAlignedAllocateOk == 3);
+ assert(a.bytesUsed == 65);
+
+ b = a.alignedAllocate(1024 * 1024, 512);
+ assert(b is null);
+ assert(a.numAlignedAllocate == 4);
+ assert(a.numAlignedAllocateOk == 3);
+ assert(a.bytesUsed == 65);
+}
diff --git a/libphobos/src/std/experimental/allocator/common.d b/libphobos/src/std/experimental/allocator/common.d
index 0eec0d3cf85..8acd763b97c 100644
--- a/libphobos/src/std/experimental/allocator/common.d
+++ b/libphobos/src/std/experimental/allocator/common.d
@@ -1,16 +1,19 @@
+// Written in the D programming language.
/**
Utility and ancillary artifacts of `std.experimental.allocator`. This module
shouldn't be used directly; its functionality will be migrated into more
appropriate parts of `std`.
Authors: $(HTTP erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`)
+
+Source: $(PHOBOSSRC std/experimental/allocator/common.d)
*/
module std.experimental.allocator.common;
import std.algorithm.comparison, std.traits;
/**
Returns the size in bytes of the state that needs to be allocated to hold an
-object of type $(D T). $(D stateSize!T) is zero for $(D struct)s that are not
+object of type `T`. `stateSize!T` is zero for `struct`s that are not
nested and have no nonstatic member variables.
*/
template stateSize(T)
@@ -54,7 +57,7 @@ template hasStaticallyKnownAlignment(Allocator)
}
/**
-$(D chooseAtRuntime) is a compile-time constant of type $(D size_t) that several
+`chooseAtRuntime` is a compile-time constant of type `size_t` that several
parameterized structures in this module recognize to mean deferral to runtime of
the exact value. For example, $(D BitmappedBlock!(Allocator, 4096)) (described in
detail below) defines a block allocator with block size of 4096 bytes, whereas
@@ -64,11 +67,11 @@ field storing the block size, initialized by the user.
enum chooseAtRuntime = size_t.max - 1;
/**
-$(D unbounded) is a compile-time constant of type $(D size_t) that several
+`unbounded` is a compile-time constant of type `size_t` that several
parameterized structures in this module recognize to mean "infinite" bounds for
-the parameter. For example, $(D Freelist) (described in detail below) accepts a
-$(D maxNodes) parameter limiting the number of freelist items. If $(D unbounded)
-is passed for $(D maxNodes), then there is no limit and no checking for the
+the parameter. For example, `Freelist` (described in detail below) accepts a
+`maxNodes` parameter limiting the number of freelist items. If `unbounded`
+is passed for `maxNodes`, then there is no limit and no checking for the
number of nodes.
*/
enum unbounded = size_t.max;
@@ -80,7 +83,7 @@ current platform.
enum uint platformAlignment = std.algorithm.comparison.max(double.alignof, real.alignof);
/**
-The default good size allocation is deduced as $(D n) rounded up to the
+The default good size allocation is deduced as `n` rounded up to the
allocator's alignment.
*/
size_t goodAllocSize(A)(auto ref A a, size_t n)
@@ -88,7 +91,7 @@ size_t goodAllocSize(A)(auto ref A a, size_t n)
return n.roundUpToMultipleOf(a.alignment);
}
-/**
+/*
Returns s rounded up to a multiple of base.
*/
@safe @nogc nothrow pure
@@ -108,13 +111,13 @@ unittest
assert(118.roundUpToMultipleOf(11) == 121);
}
-/**
+/*
Returns `n` rounded up to a multiple of alignment, which must be a power of 2.
*/
@safe @nogc nothrow pure
package size_t roundUpToAlignment(size_t n, uint alignment)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
assert(alignment.isPowerOf2);
immutable uint slack = cast(uint) n & (alignment - 1);
const result = slack
@@ -133,13 +136,13 @@ unittest
assert(118.roundUpToAlignment(64) == 128);
}
-/**
+/*
Returns `n` rounded down to a multiple of alignment, which must be a power of 2.
*/
@safe @nogc nothrow pure
package size_t roundDownToAlignment(size_t n, uint alignment)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
assert(alignment.isPowerOf2);
return n & ~size_t(alignment - 1);
}
@@ -153,7 +156,7 @@ unittest
assert(63.roundDownToAlignment(64) == 0);
}
-/**
+/*
Advances the beginning of `b` to start at alignment `a`. The resulting buffer
may therefore be shorter. Returns the adjusted buffer, or null if obtaining a
non-empty buffer is impossible.
@@ -177,7 +180,7 @@ package void[] roundUpToAlignment(void[] b, uint a)
assert(roundUpToAlignment(buf, 128) !is null);
}
-/**
+/*
Like `a / b` but rounds the result up, not down.
*/
@safe @nogc nothrow pure
@@ -187,7 +190,7 @@ package size_t divideRoundUp(size_t a, size_t b)
return (a + b - 1) / b;
}
-/**
+/*
Returns `s` rounded up to a multiple of `base`.
*/
@nogc nothrow pure
@@ -209,8 +212,8 @@ nothrow pure
assert(roundStartToMultipleOf(p, 16) is p);
}
-/**
-Returns $(D s) rounded up to the nearest power of 2.
+/*
+Returns `s` rounded up to the nearest power of 2.
*/
@safe @nogc nothrow pure
package size_t roundUpToPowerOf2(size_t s)
@@ -246,18 +249,14 @@ unittest
assert(((size_t.max >> 1) + 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1);
}
-/**
-Returns the number of trailing zeros of $(D x).
+/*
+Returns the number of trailing zeros of `x`.
*/
@safe @nogc nothrow pure
package uint trailingZeros(ulong x)
{
- uint result;
- while (result < 64 && !(x & (1UL << result)))
- {
- ++result;
- }
- return result;
+ import core.bitop : bsf;
+ return x == 0 ? 64 : bsf(x);
}
@safe @nogc nothrow pure
@@ -270,7 +269,7 @@ unittest
assert(trailingZeros(4) == 2);
}
-/**
+/*
Returns `true` if `ptr` is aligned at `alignment`.
*/
@nogc nothrow pure
@@ -279,14 +278,14 @@ package bool alignedAt(T)(T* ptr, uint alignment)
return cast(size_t) ptr % alignment == 0;
}
-/**
+/*
Returns the effective alignment of `ptr`, i.e. the largest power of two that is
a divisor of `ptr`.
*/
@nogc nothrow pure
-package uint effectiveAlignment(void* ptr)
+package size_t effectiveAlignment(void* ptr)
{
- return 1U << trailingZeros(cast(size_t) ptr);
+ return (cast(size_t) 1) << trailingZeros(cast(size_t) ptr);
}
@nogc nothrow pure
@@ -294,28 +293,31 @@ package uint effectiveAlignment(void* ptr)
{
int x;
assert(effectiveAlignment(&x) >= int.alignof);
+
+ const max = (cast(size_t) 1) << (size_t.sizeof * 8 - 1);
+ assert(effectiveAlignment(cast(void*) max) == max);
}
-/**
+/*
Aligns a pointer down to a specified alignment. The resulting pointer is less
than or equal to the given pointer.
*/
@nogc nothrow pure
-package void* alignDownTo(void* ptr, uint alignment)
+package void* alignDownTo(return scope void* ptr, uint alignment)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
assert(alignment.isPowerOf2);
return cast(void*) (cast(size_t) ptr & ~(alignment - 1UL));
}
-/**
+/*
Aligns a pointer up to a specified alignment. The resulting pointer is greater
than or equal to the given pointer.
*/
@nogc nothrow pure
-package void* alignUpTo(void* ptr, uint alignment)
+package void* alignUpTo(return scope void* ptr, uint alignment)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
assert(alignment.isPowerOf2);
immutable uint slack = cast(size_t) ptr & (alignment - 1U);
return slack ? ptr + alignment - slack : ptr;
@@ -324,27 +326,27 @@ package void* alignUpTo(void* ptr, uint alignment)
@safe @nogc nothrow pure
package bool isGoodStaticAlignment(uint x)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
return x.isPowerOf2;
}
@safe @nogc nothrow pure
package bool isGoodDynamicAlignment(uint x)
{
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
return x.isPowerOf2 && x >= (void*).sizeof;
}
/**
-The default $(D reallocate) function first attempts to use $(D expand). If $(D
-Allocator.expand) is not defined or returns $(D false), $(D reallocate)
+The default `reallocate` function first attempts to use `expand`. If $(D
+Allocator.expand) is not defined or returns `false`, `reallocate`
allocates a new block of memory of appropriate size and copies data from the old
-block to the new block. Finally, if $(D Allocator) defines $(D deallocate), $(D
+block to the new block. Finally, if `Allocator` defines `deallocate`, $(D
reallocate) uses it to free the old memory block.
-$(D reallocate) does not attempt to use $(D Allocator.reallocate) even if
+`reallocate` does not attempt to use `Allocator.reallocate` even if
defined. This is deliberate so allocators may use it internally within their own
-implementation of $(D reallocate).
+implementation of `reallocate`.
*/
bool reallocate(Allocator)(ref Allocator a, ref void[] b, size_t s)
@@ -366,20 +368,21 @@ bool reallocate(Allocator)(ref Allocator a, ref void[] b, size_t s)
/**
-The default $(D alignedReallocate) function first attempts to use $(D expand).
-If $(D Allocator.expand) is not defined or returns $(D false), $(D
+The default `alignedReallocate` function first attempts to use `expand`.
+If `Allocator.expand` is not defined or returns `false`, $(D
alignedReallocate) allocates a new block of memory of appropriate size and
-copies data from the old block to the new block. Finally, if $(D Allocator)
-defines $(D deallocate), $(D alignedReallocate) uses it to free the old memory
+copies data from the old block to the new block. Finally, if `Allocator`
+defines `deallocate`, `alignedReallocate` uses it to free the old memory
block.
-$(D alignedReallocate) does not attempt to use $(D Allocator.reallocate) even if
+`alignedReallocate` does not attempt to use `Allocator.reallocate` even if
defined. This is deliberate so allocators may use it internally within their own
-implementation of $(D reallocate).
+implementation of `reallocate`.
*/
bool alignedReallocate(Allocator)(ref Allocator alloc,
ref void[] b, size_t s, uint a)
+if (hasMember!(Allocator, "alignedAllocate"))
{
static if (hasMember!(Allocator, "expand"))
{
@@ -388,9 +391,10 @@ bool alignedReallocate(Allocator)(ref Allocator alloc,
}
else
{
- if (b.length == s) return true;
+ if (b.length == s && b.ptr.alignedAt(a)) return true;
}
auto newB = alloc.alignedAllocate(s, a);
+ if (newB.length != s) return false;
if (newB.length <= b.length) newB[] = b[0 .. newB.length];
else newB[0 .. b.length] = b[];
static if (hasMember!(Allocator, "deallocate"))
@@ -399,6 +403,63 @@ bool alignedReallocate(Allocator)(ref Allocator alloc,
return true;
}
+@system unittest
+{
+ bool called = false;
+ struct DummyAllocator
+ {
+ void[] alignedAllocate(size_t size, uint alignment)
+ {
+ called = true;
+ return null;
+ }
+ }
+
+ struct DummyAllocatorExpand
+ {
+ void[] alignedAllocate(size_t size, uint alignment)
+ {
+ return null;
+ }
+
+ bool expand(ref void[] b, size_t length)
+ {
+ called = true;
+ return true;
+ }
+ }
+
+ char[128] buf;
+ uint alignment = 32;
+ auto alignedPtr = roundUpToMultipleOf(cast(size_t) buf.ptr, alignment);
+ auto diff = alignedPtr - cast(size_t) buf.ptr;
+
+ // Align the buffer to 'alignment'
+ void[] b = cast(void[]) (buf.ptr + diff)[0 .. buf.length - diff];
+
+ DummyAllocator a1;
+ // Ask for same length and alignment, should not call 'alignedAllocate'
+ assert(alignedReallocate(a1, b, b.length, alignment));
+ assert(!called);
+
+ // Ask for same length, different alignment
+ // should call 'alignedAllocate' if not aligned to new value
+ alignedReallocate(a1, b, b.length, alignment + 1);
+ assert(b.ptr.alignedAt(alignment + 1) || called);
+ called = false;
+
+ DummyAllocatorExpand a2;
+ // Ask for bigger length, same alignment, should call 'expand'
+ assert(alignedReallocate(a2, b, b.length + 1, alignment));
+ assert(called);
+ called = false;
+
+ // Ask for bigger length, different alignment
+ // should call 'alignedAllocate' if not aligned to new value
+ alignedReallocate(a2, b, b.length + 1, alignment + 1);
+ assert(b.ptr.alignedAt(alignment + 1) || !called);
+}
+
/**
Forwards each of the methods in `funs` (if defined) to `member`.
*/
@@ -417,14 +478,13 @@ Forwards each of the methods in `funs` (if defined) to `member`.
return result;
}
-version (unittest)
+version (StdUnittest)
{
- import std.experimental.allocator : IAllocator, ISharedAllocator;
package void testAllocator(alias make)()
{
import std.conv : text;
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
import std.stdio : writeln, stderr;
import std.typecons : Ternary;
alias A = typeof(make());
@@ -450,6 +510,18 @@ version (unittest)
assert(b2.length == 2);
assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr);
+ // Test allocateZeroed
+ static if (hasMember!(A, "allocateZeroed"))
+ {{
+ auto b3 = a.allocateZeroed(8);
+ if (b3 !is null)
+ {
+ assert(b3.length == 8);
+ foreach (e; cast(ubyte[]) b3)
+ assert(e == 0);
+ }
+ }}
+
// Test alignedAllocate
static if (hasMember!(A, "alignedAllocate"))
{{
@@ -545,18 +617,22 @@ version (unittest)
}}
}
- package void testAllocatorObject(AllocInterface)(AllocInterface a)
- if (is(AllocInterface : IAllocator)
- || is (AllocInterface : shared ISharedAllocator))
+ package void testAllocatorObject(RCAllocInterface)(RCAllocInterface a)
{
+ // this used to be a template constraint, but moving it inside prevents
+ // unnecessary import of std.experimental.allocator
+ import std.experimental.allocator : RCIAllocator, RCISharedAllocator;
+ static assert(is(RCAllocInterface == RCIAllocator)
+ || is (RCAllocInterface == RCISharedAllocator));
+
import std.conv : text;
- import std.math : isPowerOf2;
+ import std.math.traits : isPowerOf2;
import std.stdio : writeln, stderr;
import std.typecons : Ternary;
scope(failure) stderr.writeln("testAllocatorObject failed for ",
- AllocInterface.stringof);
+ RCAllocInterface.stringof);
- assert(a);
+ assert(!a.isNull);
// Test alignment
assert(a.alignment.isPowerOf2);
diff --git a/libphobos/src/std/experimental/allocator/gc_allocator.d b/libphobos/src/std/experimental/allocator/gc_allocator.d
index 41894568f03..d356c737633 100644
--- a/libphobos/src/std/experimental/allocator/gc_allocator.d
+++ b/libphobos/src/std/experimental/allocator/gc_allocator.d
@@ -1,28 +1,33 @@
-///
+// Written in the D programming language.
+/**
+D's built-in garbage-collected allocator.
+
+Source: $(PHOBOSSRC std/experimental/allocator/_gc_allocator.d)
+*/
module std.experimental.allocator.gc_allocator;
import std.experimental.allocator.common;
/**
D's built-in garbage-collected allocator.
- */
+*/
struct GCAllocator
{
import core.memory : GC;
import std.typecons : Ternary;
- @system unittest { testAllocator!(() => GCAllocator.instance); }
+ version (StdUnittest) @system unittest { testAllocator!(() => GCAllocator.instance); }
/**
- The alignment is a static constant equal to $(D platformAlignment), which
+ The alignment is a static constant equal to `platformAlignment`, which
ensures proper alignment for any D data type.
*/
enum uint alignment = platformAlignment;
/**
Standard allocator methods per the semantics defined above. The $(D
- deallocate) and $(D reallocate) methods are $(D @system) because they may
+ deallocate) and `reallocate` methods are `@system` because they may
move memory around, leaving dangling pointers in user code.
*/
- pure nothrow @trusted void[] allocate(size_t bytes) shared
+ pure nothrow @trusted void[] allocate(size_t bytes) shared const
{
if (!bytes) return null;
auto p = GC.malloc(bytes);
@@ -30,7 +35,7 @@ struct GCAllocator
}
/// Ditto
- @system bool expand(ref void[] b, size_t delta) shared
+ pure nothrow @trusted bool expand(ref void[] b, size_t delta) shared const
{
if (delta == 0) return true;
if (b is null) return false;
@@ -53,7 +58,7 @@ struct GCAllocator
}
/// Ditto
- pure nothrow @system bool reallocate(ref void[] b, size_t newSize) shared
+ pure nothrow @system bool reallocate(ref void[] b, size_t newSize) shared const
{
import core.exception : OutOfMemoryError;
try
@@ -70,8 +75,8 @@ struct GCAllocator
}
/// Ditto
- pure nothrow
- Ternary resolveInternalPointer(const void* p, ref void[] result) shared
+ pure nothrow @trusted @nogc
+ Ternary resolveInternalPointer(const void* p, ref void[] result) shared const
{
auto r = GC.addrOf(cast(void*) p);
if (!r) return Ternary.no;
@@ -80,14 +85,16 @@ struct GCAllocator
}
/// Ditto
- pure nothrow @system bool deallocate(void[] b) shared
+ pure nothrow @system @nogc
+ bool deallocate(void[] b) shared const
{
GC.free(b.ptr);
return true;
}
/// Ditto
- size_t goodAllocSize(size_t n) shared
+ pure nothrow @safe @nogc
+ size_t goodAllocSize(size_t n) shared const
{
if (n == 0)
return 0;
@@ -104,23 +111,30 @@ struct GCAllocator
return ((n + 4095) / 4096) * 4096;
}
+ package pure nothrow @trusted void[] allocateZeroed()(size_t bytes) shared const
+ {
+ if (!bytes) return null;
+ auto p = GC.calloc(bytes);
+ return p ? p[0 .. bytes] : null;
+ }
+
/**
Returns the global instance of this allocator type. The garbage collected
allocator is thread-safe, therefore all of its methods and `instance` itself
- are $(D shared).
+ are `shared`.
*/
- static shared GCAllocator instance;
+ static shared const GCAllocator instance;
// Leave it undocummented for now.
- nothrow @trusted void collect() shared
+ nothrow @trusted void collect() shared const
{
GC.collect();
}
}
///
-@system unittest
+pure @system unittest
{
auto buffer = GCAllocator.instance.allocate(1024 * 1024 * 4);
// deallocate upon scope's end (alternatively: leave it to collection)
@@ -128,40 +142,58 @@ struct GCAllocator
//...
}
-@system unittest
+pure @safe unittest
{
auto b = GCAllocator.instance.allocate(10_000);
assert(GCAllocator.instance.expand(b, 1));
}
-@system unittest
+pure @system unittest
{
import core.memory : GC;
import std.typecons : Ternary;
// test allocation sizes
- assert(GCAllocator.instance.goodAllocSize(1) == 16);
+ assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(1))() == 16);
for (size_t s = 16; s <= 8192; s *= 2)
{
- assert(GCAllocator.instance.goodAllocSize(s) == s);
- assert(GCAllocator.instance.goodAllocSize(s - (s / 2) + 1) == s);
+ assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(s))() == s);
+ assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(s - (s / 2) + 1))() == s);
auto buffer = GCAllocator.instance.allocate(s);
- scope(exit) GCAllocator.instance.deallocate(buffer);
+ scope(exit) () nothrow @nogc { GCAllocator.instance.deallocate(buffer); }();
void[] p;
- assert(GCAllocator.instance.resolveInternalPointer(null, p) == Ternary.no);
- Ternary r = GCAllocator.instance.resolveInternalPointer(buffer.ptr, p);
+ assert((() nothrow @safe => GCAllocator.instance.resolveInternalPointer(null, p))() == Ternary.no);
+ assert((() nothrow @safe => GCAllocator.instance.resolveInternalPointer(&buffer[0], p))() == Ternary.yes);
assert(p.ptr is buffer.ptr && p.length >= buffer.length);
assert(GC.sizeOf(buffer.ptr) == s);
- auto buffer2 = GCAllocator.instance.allocate(s - (s / 2) + 1);
- scope(exit) GCAllocator.instance.deallocate(buffer2);
+ // the GC should provide power of 2 as "good" sizes, but other sizes are allowed, too
+ version (none)
+ {
+ auto buffer2 = GCAllocator.instance.allocate(s - (s / 2) + 1);
+ scope(exit) () nothrow @nogc { GCAllocator.instance.deallocate(buffer2); }();
- assert(GC.sizeOf(buffer2.ptr) == s);
+ assert(GC.sizeOf(buffer2.ptr) == s);
+ }
}
// anything above a page is simply rounded up to next page
- assert(GCAllocator.instance.goodAllocSize(4096 * 4 + 1) == 4096 * 5);
+ assert((() nothrow @safe @nogc => GCAllocator.instance.goodAllocSize(4096 * 4 + 1))() == 4096 * 5);
+}
+
+pure nothrow @safe unittest
+{
+ import std.typecons : Ternary;
+
+ void[] buffer = GCAllocator.instance.allocate(42);
+ void[] result;
+ Ternary found = GCAllocator.instance.resolveInternalPointer(&buffer[0], result);
+
+ assert(found == Ternary.yes && &result[0] == &buffer[0] && result.length >= buffer.length);
+ assert(GCAllocator.instance.resolveInternalPointer(null, result) == Ternary.no);
+ void *badPtr = (() @trusted => cast(void*)(0xdeadbeef))();
+ assert(GCAllocator.instance.resolveInternalPointer(badPtr, result) == Ternary.no);
}
diff --git a/libphobos/src/std/experimental/allocator/mallocator.d b/libphobos/src/std/experimental/allocator/mallocator.d
index 2d1dec39a74..895d5883f52 100644
--- a/libphobos/src/std/experimental/allocator/mallocator.d
+++ b/libphobos/src/std/experimental/allocator/mallocator.d
@@ -1,4 +1,9 @@
-///
+// Written in the D programming language.
+/**
+The C heap allocator.
+
+Source: $(PHOBOSSRC std/experimental/allocator/mallocator.d)
+*/
module std.experimental.allocator.mallocator;
import std.experimental.allocator.common;
@@ -7,44 +12,44 @@ import std.experimental.allocator.common;
*/
struct Mallocator
{
- @system unittest { testAllocator!(() => Mallocator.instance); }
+ version (StdUnittest) @system unittest { testAllocator!(() => Mallocator.instance); }
/**
- The alignment is a static constant equal to $(D platformAlignment), which
+ The alignment is a static constant equal to `platformAlignment`, which
ensures proper alignment for any D data type.
*/
enum uint alignment = platformAlignment;
/**
Standard allocator methods per the semantics defined above. The
- $(D deallocate) and $(D reallocate) methods are $(D @system) because they
+ `deallocate` and `reallocate` methods are `@system` because they
may move memory around, leaving dangling pointers in user code. Somewhat
- paradoxically, $(D malloc) is $(D @safe) but that's only useful to safe
+ paradoxically, `malloc` is `@safe` but that's only useful to safe
programs that can afford to leak memory allocated.
*/
- @trusted @nogc nothrow
- void[] allocate(size_t bytes) shared
+ @trusted @nogc nothrow pure
+ void[] allocate(size_t bytes) shared const
{
- import core.stdc.stdlib : malloc;
+ import core.memory : pureMalloc;
if (!bytes) return null;
- auto p = malloc(bytes);
+ auto p = pureMalloc(bytes);
return p ? p[0 .. bytes] : null;
}
/// Ditto
- @system @nogc nothrow
- bool deallocate(void[] b) shared
+ @system @nogc nothrow pure
+ bool deallocate(void[] b) shared const
{
- import core.stdc.stdlib : free;
- free(b.ptr);
+ import core.memory : pureFree;
+ pureFree(b.ptr);
return true;
}
/// Ditto
- @system @nogc nothrow
- bool reallocate(ref void[] b, size_t s) shared
+ @system @nogc nothrow pure
+ bool reallocate(ref void[] b, size_t s) shared const
{
- import core.stdc.stdlib : realloc;
+ import core.memory : pureRealloc;
if (!s)
{
// fuzzy area in the C standard, see http://goo.gl/ZpWeSE
@@ -53,46 +58,52 @@ struct Mallocator
b = null;
return true;
}
- auto p = cast(ubyte*) realloc(b.ptr, s);
+ auto p = cast(ubyte*) pureRealloc(b.ptr, s);
if (!p) return false;
b = p[0 .. s];
return true;
}
+ @trusted @nogc nothrow pure
+ package void[] allocateZeroed()(size_t bytes) shared const
+ {
+ import core.memory : pureCalloc;
+ if (!bytes) return null;
+ auto p = pureCalloc(1, bytes);
+ return p ? p[0 .. bytes] : null;
+ }
+
/**
Returns the global instance of this allocator type. The C heap allocator is
thread-safe, therefore all of its methods and `it` itself are
- $(D shared).
+ `shared`.
*/
static shared Mallocator instance;
}
///
-@nogc nothrow
-@system unittest
+@nogc @system nothrow unittest
{
auto buffer = Mallocator.instance.allocate(1024 * 1024 * 4);
scope(exit) Mallocator.instance.deallocate(buffer);
//...
}
-@nogc nothrow
-@system unittest
+@nogc @system nothrow pure unittest
{
- @nogc nothrow
+ @nogc nothrow pure
static void test(A)()
{
int* p = null;
p = cast(int*) A.instance.allocate(int.sizeof);
- scope(exit) A.instance.deallocate(p[0 .. int.sizeof]);
+ scope(exit) () nothrow @nogc { A.instance.deallocate(p[0 .. int.sizeof]); }();
*p = 42;
assert(*p == 42);
}
test!Mallocator();
}
-@nogc nothrow
-@system unittest
+@nogc @system nothrow pure unittest
{
static void test(A)()
{
@@ -199,10 +210,10 @@ version (Windows)
*/
struct AlignedMallocator
{
- @system unittest { testAllocator!(() => typeof(this).instance); }
+ version (StdUnittest) @system unittest { testAllocator!(() => typeof(this).instance); }
/**
- The default alignment is $(D platformAlignment).
+ The default alignment is `platformAlignment`.
*/
enum uint alignment = platformAlignment;
@@ -218,9 +229,9 @@ struct AlignedMallocator
/**
Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html,
- $(D posix_memalign)) on Posix and
+ `posix_memalign`) on Posix and
$(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx,
- $(D __aligned_malloc)) on Windows.
+ `__aligned_malloc`) on Windows.
*/
version (Posix)
@trusted @nogc nothrow
@@ -231,6 +242,15 @@ struct AlignedMallocator
assert(a.isGoodDynamicAlignment);
void* result;
auto code = posix_memalign(&result, a, bytes);
+
+version (OSX)
+version (LDC_AddressSanitizer)
+{
+ // The return value with AddressSanitizer may be -1 instead of ENOMEM
+ // or EINVAL. See https://bugs.llvm.org/show_bug.cgi?id=36510
+ if (code == -1)
+ return null;
+}
if (code == ENOMEM)
return null;
@@ -255,9 +275,9 @@ struct AlignedMallocator
else static assert(0);
/**
- Calls $(D free(b.ptr)) on Posix and
+ Calls `free(b.ptr)` on Posix and
$(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx,
- $(D __aligned_free(b.ptr))) on Windows.
+ `__aligned_free(b.ptr)`) on Windows.
*/
version (Posix)
@system @nogc nothrow
@@ -277,16 +297,10 @@ struct AlignedMallocator
else static assert(0);
/**
- On Posix, forwards to $(D realloc). On Windows, forwards to
- $(D alignedReallocate(b, newSize, platformAlignment)).
+ Forwards to $(D alignedReallocate(b, newSize, platformAlignment)).
+ Should be used with blocks obtained with `allocate` otherwise the custom
+ alignment passed with `alignedAllocate` can be lost.
*/
- version (Posix)
- @system @nogc nothrow
- bool reallocate(ref void[] b, size_t newSize) shared
- {
- return Mallocator.instance.reallocate(b, newSize);
- }
- version (Windows)
@system @nogc nothrow
bool reallocate(ref void[] b, size_t newSize) shared
{
@@ -294,9 +308,10 @@ struct AlignedMallocator
}
/**
- On Posix, uses $(D alignedAllocate) and copies data around because there is
- no realloc for aligned memory. On Windows, calls
- $(HTTP msdn.microsoft.com/en-US/library/y69db7sx(v=vs.80).aspx,
+ On Posix there is no `realloc` for aligned memory, so `alignedReallocate` emulates
+ the needed behavior by using `alignedAllocate` to get a new block. The existing
+ block is copied to the new block and then freed.
+ On Windows, calls $(HTTPS msdn.microsoft.com/en-us/library/y69db7sx.aspx,
$(D __aligned_realloc(b.ptr, newSize, a))).
*/
version (Windows)
@@ -315,17 +330,40 @@ struct AlignedMallocator
return true;
}
+ /// ditto
+ version (Posix)
+ @system @nogc nothrow
+ bool alignedReallocate(ref void[] b, size_t s, uint a) shared
+ {
+ if (!s)
+ {
+ deallocate(b);
+ b = null;
+ return true;
+ }
+ auto p = alignedAllocate(s, a);
+ if (!p.ptr)
+ {
+ return false;
+ }
+ import std.algorithm.comparison : min;
+ const upTo = min(s, b.length);
+ p[0 .. upTo] = b[0 .. upTo];
+ deallocate(b);
+ b = p;
+ return true;
+ }
+
/**
Returns the global instance of this allocator type. The C heap allocator is
thread-safe, therefore all of its methods and `instance` itself are
- $(D shared).
+ `shared`.
*/
static shared AlignedMallocator instance;
}
///
-@nogc nothrow
-@system unittest
+@nogc @system nothrow unittest
{
auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4,
128);
@@ -333,34 +371,59 @@ struct AlignedMallocator
//...
}
-version (unittest) version (CRuntime_DigitalMars)
-@nogc nothrow
-size_t addr(ref void* ptr) { return cast(size_t) ptr; }
+version (Posix)
+@nogc @system nothrow unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=16398
+ // test the "pseudo" alignedReallocate for Posix
+ void[] s = AlignedMallocator.instance.alignedAllocate(16, 32);
+ (cast(ubyte[]) s)[] = ubyte(1);
+ AlignedMallocator.instance.alignedReallocate(s, 32, 32);
+ ubyte[16] o;
+ o[] = 1;
+ assert((cast(ubyte[]) s)[0 .. 16] == o);
+ AlignedMallocator.instance.alignedReallocate(s, 4, 32);
+ assert((cast(ubyte[]) s)[0 .. 3] == o[0 .. 3]);
+ AlignedMallocator.instance.alignedReallocate(s, 128, 32);
+ assert((cast(ubyte[]) s)[0 .. 3] == o[0 .. 3]);
+ AlignedMallocator.instance.deallocate(s);
+
+ void[] c;
+ AlignedMallocator.instance.alignedReallocate(c, 32, 32);
+ assert(c.ptr);
+
+ version (DragonFlyBSD) {} else /* FIXME: Malloc on DragonFly does not return NULL when allocating more than UINTPTR_MAX
+ * $(LINK: https://bugs.dragonflybsd.org/issues/3114, dragonfly bug report)
+ * $(LINK: https://github.com/dlang/druntime/pull/1999#discussion_r157536030, PR Discussion) */
+ assert(!AlignedMallocator.instance.alignedReallocate(c, size_t.max, 4096));
+ AlignedMallocator.instance.deallocate(c);
+}
version (CRuntime_DigitalMars)
-@nogc nothrow
-@system unittest
+@nogc @system nothrow unittest
{
void* m;
+ size_t m_addr() { return cast(size_t) m; }
+
m = _aligned_malloc(16, 0x10);
if (m)
{
- assert((m.addr & 0xF) == 0);
+ assert((m_addr & 0xF) == 0);
_aligned_free(m);
}
m = _aligned_malloc(16, 0x100);
if (m)
{
- assert((m.addr & 0xFF) == 0);
+ assert((m_addr & 0xFF) == 0);
_aligned_free(m);
}
m = _aligned_malloc(16, 0x1000);
if (m)
{
- assert((m.addr & 0xFFF) == 0);
+ assert((m_addr & 0xFFF) == 0);
_aligned_free(m);
}
@@ -369,7 +432,7 @@ version (CRuntime_DigitalMars)
{
assert((cast(size_t) m & 0xF) == 0);
m = _aligned_realloc(m, 32, 0x10000);
- if (m) assert((m.addr & 0xFFFF) == 0);
+ if (m) assert((m_addr & 0xFFFF) == 0);
_aligned_free(m);
}
diff --git a/libphobos/src/std/experimental/allocator/mmap_allocator.d b/libphobos/src/std/experimental/allocator/mmap_allocator.d
index e07d444c32c..4151d0e0504 100644
--- a/libphobos/src/std/experimental/allocator/mmap_allocator.d
+++ b/libphobos/src/std/experimental/allocator/mmap_allocator.d
@@ -1,23 +1,23 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/allocator/_mmap_allocator.d)
+*/
module std.experimental.allocator.mmap_allocator;
-// MmapAllocator
/**
-
Allocator (currently defined only for Posix and Windows) using
$(D $(LINK2 https://en.wikipedia.org/wiki/Mmap, mmap))
and $(D $(LUCKY munmap)) directly (or their Windows equivalents). There is no
-additional structure: each call to $(D allocate(s)) issues a call to
+additional structure: each call to `allocate(s)` issues a call to
$(D mmap(null, s, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)),
-and each call to $(D deallocate(b)) issues $(D munmap(b.ptr, b.length)).
-So $(D MmapAllocator) is usually intended for allocating large chunks to be
+and each call to `deallocate(b)` issues $(D munmap(b.ptr, b.length)).
+So `MmapAllocator` is usually intended for allocating large chunks to be
managed by fine-granular allocators.
-
*/
struct MmapAllocator
{
/// The one shared instance.
- static shared MmapAllocator instance;
+ static shared const MmapAllocator instance;
/**
Alignment is page-size and hardcoded to 4096 (even though on certain systems
@@ -28,22 +28,29 @@ struct MmapAllocator
version (Posix)
{
/// Allocator API.
- void[] allocate(size_t bytes) shared
+ pure nothrow @nogc @safe
+ void[] allocate(size_t bytes) shared const
{
- import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_READ,
+ import core.sys.posix.sys.mman : MAP_ANON, PROT_READ,
PROT_WRITE, MAP_PRIVATE, MAP_FAILED;
if (!bytes) return null;
- auto p = mmap(null, bytes, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON, -1, 0);
- if (p is MAP_FAILED) return null;
- return p[0 .. bytes];
+ const errnosave = (() @trusted => fakePureErrno())(); // For purity revert changes to errno.
+ auto p = (() @trusted => fakePureMmap(null, bytes, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0))();
+ if (p is MAP_FAILED)
+ {
+ (() @trusted => fakePureErrno() = errnosave)(); // errno only changed on MAP_FAILED.
+ return null;
+ }
+ return (() @trusted => p[0 .. bytes])();
}
/// Ditto
- bool deallocate(void[] b) shared
+ pure nothrow @nogc
+ bool deallocate(void[] b) shared const
{
- import core.sys.posix.sys.mman : munmap;
- if (b.ptr) munmap(b.ptr, b.length) == 0 || assert(0);
+ // Because we assert(0) on error we don't need to reset errno for purity.
+ if (b.ptr) fakePureMunmap(b.ptr, b.length) == 0 || assert(0);
return true;
}
@@ -64,22 +71,31 @@ struct MmapAllocator
}
else version (Windows)
{
- import core.sys.windows.windows : VirtualAlloc, VirtualFree, MEM_COMMIT,
- PAGE_READWRITE, MEM_RELEASE;
+ import core.sys.windows.winnt : MEM_COMMIT, PAGE_READWRITE, MEM_RELEASE;
/// Allocator API.
- void[] allocate(size_t bytes) shared
+ pure nothrow @nogc @safe
+ void[] allocate(size_t bytes) shared const
{
if (!bytes) return null;
- auto p = VirtualAlloc(null, bytes, MEM_COMMIT, PAGE_READWRITE);
+ // For purity ensure last-error does not visibly change.
+ const lastErrorSave = (() @trusted => GetLastError())();
+ auto p = (() @trusted => VirtualAlloc(null, bytes, MEM_COMMIT, PAGE_READWRITE))();
if (p == null)
+ {
+ // Last-error only changed if allocation failed.
+ (() @trusted => SetLastError(lastErrorSave))();
return null;
- return p[0 .. bytes];
+ }
+ return (() @trusted => p[0 .. bytes])();
}
/// Ditto
- bool deallocate(void[] b) shared
+ pure nothrow @nogc
+ bool deallocate(void[] b) shared const
{
+ const lastErrorSave = GetLastError(); // For purity ensure last-error does not visibly change.
+ scope(exit) SetLastError(lastErrorSave);
return b.ptr is null || VirtualFree(b.ptr, 0, MEM_RELEASE) != 0;
}
@@ -87,10 +103,36 @@ struct MmapAllocator
}
}
-@system unittest
+// pure wrappers around `mmap` and `munmap` because they are used here locally
+// solely to perform allocation and deallocation which in this case is `pure`
+version (Posix)
+extern (C) private pure @system @nogc nothrow
+{
+ import core.sys.posix.sys.types : off_t;
+ pragma(mangle, "fakePureErrnoImpl") ref int fakePureErrno();
+ pragma(mangle, "mmap") void* fakePureMmap(void*, size_t, int, int, int, off_t);
+ pragma(mangle, "munmap") int fakePureMunmap(void*, size_t);
+}
+
+// Pure wrappers around VirtualAlloc/VirtualFree for use here only. Their use is sound
+// because when we call them we ensure that last-error is not visibly changed.
+version (Windows)
+extern (Windows) private pure @system @nogc nothrow
+{
+ import core.sys.windows.basetsd : SIZE_T;
+ import core.sys.windows.windef : BOOL, DWORD;
+ import core.sys.windows.winnt : LPVOID, PVOID;
+
+ DWORD GetLastError();
+ void SetLastError(DWORD);
+ PVOID VirtualAlloc(PVOID, SIZE_T, DWORD, DWORD);
+ BOOL VirtualFree(PVOID, SIZE_T, DWORD);
+}
+
+pure nothrow @safe @nogc unittest
{
alias alloc = MmapAllocator.instance;
auto p = alloc.allocate(100);
assert(p.length == 100);
- alloc.deallocate(p);
+ () @trusted { alloc.deallocate(p); p = null; }();
}
diff --git a/libphobos/src/std/experimental/allocator/package.d b/libphobos/src/std/experimental/allocator/package.d
index 11c85474365..2804829abe4 100644
--- a/libphobos/src/std/experimental/allocator/package.d
+++ b/libphobos/src/std/experimental/allocator/package.d
@@ -63,13 +63,13 @@ D's allocators have a layered structure in both implementation and documentation
$(OL
$(LI A high-level, dynamically-typed layer (described further down in this
-module). It consists of an interface called $(LREF IAllocator), which concret;
+module). It consists of an interface called $(LREF IAllocator), which concrete
allocators need to implement. The interface primitives themselves are oblivious
to the type of the objects being allocated; they only deal in `void[]`, by
necessity of the interface being dynamic (as opposed to type-parameterized).
Each thread has a current allocator it uses by default, which is a thread-local
variable $(LREF theAllocator) of type $(LREF IAllocator). The process has a
-global _allocator called $(LREF processAllocator), also of type $(LREF
+global allocator called $(LREF processAllocator), also of type $(LREF
IAllocator). When a new thread is created, $(LREF processAllocator) is copied
into $(LREF theAllocator). An application can change the objects to which these
references point. By default, at application startup, $(LREF processAllocator)
@@ -82,7 +82,7 @@ $(LI A mid-level, statically-typed layer for assembling several allocators into
one. It uses properties of the type of the objects being created to route
allocation requests to possibly specialized allocators. This layer is relatively
thin and implemented and documented in the $(MREF
-std,experimental,_allocator,typed) module. It allows an interested user to e.g.
+std,experimental,allocator,typed) module. It allows an interested user to e.g.
use different allocators for arrays versus fixed-sized objects, to the end of
better overall performance.)
@@ -91,27 +91,27 @@ Lego-like pieces that can be used to assemble application-specific allocators.
The real allocation smarts are occurring at this level. This layer is of
interest to advanced applications that want to configure their own allocators.
A good illustration of typical uses of these building blocks is module $(MREF
-std,experimental,_allocator,showcase) which defines a collection of frequently-
+std,experimental,allocator,showcase) which defines a collection of frequently-
used preassembled allocator objects. The implementation and documentation entry
-point is $(MREF std,experimental,_allocator,building_blocks). By design, the
+point is $(MREF std,experimental,allocator,building_blocks). By design, the
primitives of the static interface have the same signatures as the $(LREF
IAllocator) primitives but are for the most part optional and driven by static
introspection. The parameterized class $(LREF CAllocatorImpl) offers an
-immediate and useful means to package a static low-level _allocator into an
+immediate and useful means to package a static low-level allocator into an
implementation of $(LREF IAllocator).)
-$(LI Core _allocator objects that interface with D's garbage collected heap
-($(MREF std,experimental,_allocator,gc_allocator)), the C `malloc` family
-($(MREF std,experimental,_allocator,mallocator)), and the OS ($(MREF
-std,experimental,_allocator,mmap_allocator)). Most custom allocators would
+$(LI Core allocator objects that interface with D's garbage collected heap
+($(MREF std,experimental,allocator,gc_allocator)), the C `malloc` family
+($(MREF std,experimental,allocator,mallocator)), and the OS ($(MREF
+std,experimental,allocator,mmap_allocator)). Most custom allocators would
ultimately obtain memory from one of these core allocators.)
)
-$(H2 Idiomatic Use of $(D std.experimental._allocator))
+$(H2 Idiomatic Use of `std.experimental.allocator`)
-As of this time, $(D std.experimental._allocator) is not integrated with D's
+As of this time, `std.experimental.allocator` is not integrated with D's
built-in operators that allocate memory, such as `new`, array literals, or
-array concatenation operators. That means $(D std.experimental._allocator) is
+array concatenation operators. That means `std.experimental.allocator` is
opt-in$(MDASH)applications need to make explicit use of it.
For casual creation and disposal of dynamically-allocated objects, use $(LREF
@@ -133,9 +133,9 @@ void fun(size_t n)
To experiment with alternative allocators, set $(LREF theAllocator) for the
current thread. For example, consider an application that allocates many 8-byte
-objects. These are not well supported by the default _allocator, so a
-$(MREF_ALTTEXT free list _allocator,
-std,experimental,_allocator,building_blocks,free_list) would be recommended.
+objects. These are not well supported by the default allocator, so a
+$(MREF_ALTTEXT free list allocator,
+std,experimental,allocator,building_blocks,free_list) would be recommended.
To install one in `main`, the application would use:
----
@@ -158,27 +158,27 @@ last through the application.
To avoid this, long-lived objects that need to perform allocations,
reallocations, and deallocations relatively often may want to store a reference
-to the _allocator object they use throughout their lifetime. Then, instead of
+to the allocator object they use throughout their lifetime. Then, instead of
using `theAllocator` for internal allocation-related tasks, they'd use the
internally held reference. For example, consider a user-defined hash table:
----
struct HashTable
{
- private IAllocator _allocator;
+ private IAllocator allocator;
this(size_t buckets, IAllocator allocator = theAllocator) {
- this._allocator = allocator;
+ this.allocator = allocator;
...
}
// Getter and setter
- IAllocator allocator() { return _allocator; }
- void allocator(IAllocator a) { assert(empty); _allocator = a; }
+ IAllocator allocator() { return allocator; }
+ void allocator(IAllocator a) { assert(empty); allocator = a; }
}
----
Following initialization, the `HashTable` object would consistently use its
-$(D _allocator) object for acquiring memory. Furthermore, setting
-$(D HashTable._allocator) to point to a different _allocator should be legal but
+`allocator` object for acquiring memory. Furthermore, setting
+`HashTable.allocator` to point to a different allocator should be legal but
only if the object is empty; otherwise, the object wouldn't be able to
deallocate its existing state.
@@ -217,7 +217,7 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.com, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/experimental/_allocator)
+Source: $(PHOBOSSRC std/experimental/allocator)
*/
@@ -226,6 +226,19 @@ module std.experimental.allocator;
public import std.experimental.allocator.common,
std.experimental.allocator.typed;
+// Fix https://issues.dlang.org/show_bug.cgi?id=17806
+// this should always be the first unittest in this module in order to ensure
+// that we use the `processAllocator` setter before the getter
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ auto newAlloc = sharedAllocatorObject(Mallocator.instance);
+ processAllocator = newAlloc;
+ assert(processAllocator is newAlloc);
+ processAllocator = sharedAllocatorObject(GCAllocator.instance);
+}
+
// Example in the synopsis above
@system unittest
{
@@ -276,16 +289,17 @@ encapsulating various allocator implementations.
Composition of allocators is not recommended at this level due to
inflexibility of dynamic interfaces and inefficiencies caused by cascaded
multiple calls. Instead, compose allocators using the static interface defined
-in $(A std_experimental_allocator_building_blocks.html,
-`std.experimental.allocator.building_blocks`), then adapt the composed
+in $(MREF std,experimental,allocator,building_blocks),
+then adapt the composed
allocator to `IAllocator` (possibly by using $(LREF CAllocatorImpl) below).
-Methods returning $(D Ternary) return $(D Ternary.yes) upon success,
-$(D Ternary.no) upon failure, and $(D Ternary.unknown) if the primitive is not
+Methods returning `Ternary` return `Ternary.yes` upon success,
+`Ternary.no` upon failure, and `Ternary.unknown` if the primitive is not
implemented by the allocator instance.
*/
interface IAllocator
{
+nothrow:
/**
Returns the alignment offered.
*/
@@ -329,8 +343,8 @@ interface IAllocator
bool alignedReallocate(ref void[] b, size_t size, uint alignment);
/**
- Returns $(D Ternary.yes) if the allocator owns $(D b), $(D Ternary.no) if
- the allocator doesn't own $(D b), and $(D Ternary.unknown) if ownership
+ Returns `Ternary.yes` if the allocator owns `b`, `Ternary.no` if
+ the allocator doesn't own `b`, and `Ternary.unknown` if ownership
cannot be determined. Implementations that don't support this primitive
should always return `Ternary.unknown`.
*/
@@ -345,7 +359,7 @@ interface IAllocator
/**
Deallocates a memory block. Implementations that don't support this
primitive should always return `false`. A simple way to check that an
- allocator supports deallocation is to call $(D deallocate(null)).
+ allocator supports deallocation is to call `deallocate(null)`.
*/
bool deallocate(void[] b);
@@ -356,11 +370,257 @@ interface IAllocator
bool deallocateAll();
/**
- Returns $(D Ternary.yes) if no memory is currently allocated from this
- allocator, $(D Ternary.no) if some allocations are currently active, or
- $(D Ternary.unknown) if not supported.
+ Returns `Ternary.yes` if no memory is currently allocated from this
+ allocator, `Ternary.no` if some allocations are currently active, or
+ `Ternary.unknown` if not supported.
*/
Ternary empty();
+
+ /**
+ Increases the reference count of the concrete class that implements this
+ interface.
+
+ For stateless allocators, this does nothing.
+ */
+ @safe @nogc pure
+ void incRef();
+
+ /**
+ Decreases the reference count of the concrete class that implements this
+ interface.
+ When the reference count is `0`, the object self-destructs.
+
+ Returns: `true` if the reference count is greater than `0` and `false` when
+ it hits `0`. For stateless allocators, it always returns `true`.
+ */
+ @safe @nogc pure
+ bool decRef();
+}
+
+/**
+A reference counted struct that wraps the dynamic allocator interface.
+This should be used wherever a uniform type is required for encapsulating
+various allocator implementations.
+
+Code that defines allocators ultimately implements the $(LREF IAllocator)
+interface, possibly by using $(LREF CAllocatorImpl) below, and then build a
+`RCIAllocator` out of this.
+
+Composition of allocators is not recommended at this level due to
+inflexibility of dynamic interfaces and inefficiencies caused by cascaded
+multiple calls. Instead, compose allocators using the static interface defined
+in $(A std_experimental_allocator_building_blocks.html,
+`std.experimental.allocator.building_blocks`), then adapt the composed
+allocator to `RCIAllocator` (possibly by using $(LREF allocatorObject) below).
+*/
+struct RCIAllocator
+{
+ private IAllocator _alloc;
+
+nothrow:
+ private @nogc pure @safe
+ this(this _)(IAllocator alloc)
+ {
+ assert(alloc);
+ _alloc = alloc;
+ }
+
+ @nogc pure @safe
+ this(this)
+ {
+ if (_alloc !is null)
+ {
+ _alloc.incRef();
+ }
+ }
+
+ @nogc pure @safe
+ ~this()
+ {
+ if (_alloc !is null)
+ {
+ bool isLast = !_alloc.decRef();
+ if (isLast) _alloc = null;
+ }
+ }
+
+ @nogc pure @safe
+ auto ref opAssign()(typeof(this) rhs)
+ {
+ if (_alloc is rhs._alloc)
+ {
+ return this;
+ }
+ // incRef was allready called by rhs posblit, so we're just moving
+ // calling dtor is the equivalent of decRef
+ __dtor();
+ _alloc = rhs._alloc;
+ // move
+ rhs._alloc = null;
+ return this;
+ }
+
+ @nogc pure @safe
+ bool isNull(this _)()
+ {
+ return _alloc is null;
+ }
+
+ @property uint alignment()
+ {
+ assert(_alloc);
+ return _alloc.alignment();
+ }
+
+ size_t goodAllocSize(size_t s)
+ {
+ assert(_alloc);
+ return _alloc.goodAllocSize(s);
+ }
+
+ void[] allocate(size_t n, TypeInfo ti = null)
+ {
+ assert(_alloc);
+ return _alloc.allocate(n, ti);
+ }
+
+ void[] alignedAllocate(size_t n, uint a)
+ {
+ assert(_alloc);
+ return _alloc.alignedAllocate(n, a);
+ }
+
+ void[] allocateAll()
+ {
+ assert(_alloc);
+ return _alloc.allocateAll();
+ }
+
+ bool expand(ref void[] b, size_t size)
+ {
+ assert(_alloc);
+ return _alloc.expand(b, size);
+ }
+
+ bool reallocate(ref void[] b, size_t size)
+ {
+ assert(_alloc);
+ return _alloc.reallocate(b, size);
+ }
+
+ bool alignedReallocate(ref void[] b, size_t size, uint alignment)
+ {
+ assert(_alloc);
+ return _alloc.alignedReallocate(b, size, alignment);
+ }
+
+ Ternary owns(void[] b)
+ {
+ assert(_alloc);
+ return _alloc.owns(b);
+ }
+
+ Ternary resolveInternalPointer(const void* p, ref void[] result)
+ {
+ assert(_alloc);
+ return _alloc.resolveInternalPointer(p, result);
+ }
+
+ bool deallocate(void[] b)
+ {
+ assert(_alloc);
+ return _alloc.deallocate(b);
+ }
+
+ bool deallocateAll()
+ {
+ assert(_alloc);
+ return _alloc.deallocateAll();
+ }
+
+ Ternary empty()
+ {
+ assert(_alloc);
+ return _alloc.empty();
+ }
+}
+
+@system unittest
+{
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.conv : emplace;
+
+ auto reg = Region!()(new ubyte[1024]);
+ auto state = reg.allocate(stateSize!(CAllocatorImpl!(Region!(), Yes.indirect)));
+ auto regObj = emplace!(CAllocatorImpl!(Region!(), Yes.indirect))(state, &reg);
+
+ auto rcalloc = RCIAllocator(regObj);
+ auto b = rcalloc.allocate(10);
+ assert(b.length == 10);
+
+ // The reference counting is zero based
+ assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
+ {
+ auto rca2 = rcalloc;
+ assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 2);
+ }
+ assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
+}
+
+@system unittest
+{
+ import std.conv;
+ import std.experimental.allocator.mallocator;
+ import std.experimental.allocator.building_blocks.stats_collector;
+
+ alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
+ SCAlloc statsCollectorAlloc;
+
+ ulong bytesUsed = statsCollectorAlloc.bytesUsed;
+ assert(bytesUsed == 0);
+
+ {
+ auto _allocator = allocatorObject(&statsCollectorAlloc);
+ bytesUsed = statsCollectorAlloc.bytesUsed;
+ assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc, Yes.indirect)));
+ }
+
+ bytesUsed = statsCollectorAlloc.bytesUsed;
+ assert(bytesUsed == 0, "RCIAllocator leaks memory; leaked "
+ ~ to!string(bytesUsed) ~ " bytes");
+}
+
+@system unittest
+{
+ import std.conv;
+ import std.experimental.allocator.mallocator;
+ import std.experimental.allocator.building_blocks.stats_collector;
+
+ alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
+ SCAlloc statsCollectorAlloc;
+
+ ulong bytesUsed = statsCollectorAlloc.bytesUsed;
+ assert(bytesUsed == 0);
+
+ {
+ auto _allocator = allocatorObject(statsCollectorAlloc);
+
+ // Ensure that the allocator was passed through in CAllocatorImpl
+ // This allocator was used to allocate the chunk that holds the
+ // CAllocatorImpl object; which is it's own wrapper
+ bytesUsed = (cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed;
+ assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)),
+ "RCIAllocator leaks memory; leaked " ~ to!string(bytesUsed) ~ " bytes");
+ _allocator.allocate(1);
+ bytesUsed = (cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed;
+ assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)) + 1,
+ "RCIAllocator leaks memory; leaked " ~ to!string(bytesUsed) ~ " bytes");
+ }
+
+ bytesUsed = statsCollectorAlloc.bytesUsed;
+ assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)),
+ "RCIAllocator leaks memory; leaked "
+ ~ to!string(bytesUsed) ~ " bytes");
}
/**
@@ -372,16 +632,17 @@ implementations.
Composition of allocators is not recommended at this level due to
inflexibility of dynamic interfaces and inefficiencies caused by cascaded
multiple calls. Instead, compose allocators using the static interface defined
-in $(A std_experimental_allocator_building_blocks.html,
-`std.experimental.allocator.building_blocks`), then adapt the composed
+in $(MREF std,experimental,allocator,building_blocks),
+then adapt the composed
allocator to `ISharedAllocator` (possibly by using $(LREF CSharedAllocatorImpl) below).
-Methods returning $(D Ternary) return $(D Ternary.yes) upon success,
-$(D Ternary.no) upon failure, and $(D Ternary.unknown) if the primitive is not
+Methods returning `Ternary` return `Ternary.yes` upon success,
+`Ternary.no` upon failure, and `Ternary.unknown` if the primitive is not
implemented by the allocator instance.
*/
interface ISharedAllocator
{
+nothrow:
/**
Returns the alignment offered.
*/
@@ -425,8 +686,8 @@ interface ISharedAllocator
bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared;
/**
- Returns $(D Ternary.yes) if the allocator owns $(D b), $(D Ternary.no) if
- the allocator doesn't own $(D b), and $(D Ternary.unknown) if ownership
+ Returns `Ternary.yes` if the allocator owns `b`, `Ternary.no` if
+ the allocator doesn't own `b`, and `Ternary.unknown` if ownership
cannot be determined. Implementations that don't support this primitive
should always return `Ternary.unknown`.
*/
@@ -441,7 +702,7 @@ interface ISharedAllocator
/**
Deallocates a memory block. Implementations that don't support this
primitive should always return `false`. A simple way to check that an
- allocator supports deallocation is to call $(D deallocate(null)).
+ allocator supports deallocation is to call `deallocate(null)`.
*/
bool deallocate(void[] b) shared;
@@ -452,113 +713,324 @@ interface ISharedAllocator
bool deallocateAll() shared;
/**
- Returns $(D Ternary.yes) if no memory is currently allocated from this
- allocator, $(D Ternary.no) if some allocations are currently active, or
- $(D Ternary.unknown) if not supported.
+ Returns `Ternary.yes` if no memory is currently allocated from this
+ allocator, `Ternary.no` if some allocations are currently active, or
+ `Ternary.unknown` if not supported.
*/
Ternary empty() shared;
+
+ /**
+ Increases the reference count of the concrete class that implements this
+ interface.
+
+ For stateless allocators, this does nothing.
+ */
+ @safe @nogc pure
+ void incRef() shared;
+
+ /**
+ Decreases the reference count of the concrete class that implements this
+ interface.
+ When the reference count is `0`, the object self-destructs.
+
+ For stateless allocators, this does nothing.
+
+ Returns: `true` if the reference count is greater than `0` and `false` when
+ it hits `0`. For stateless allocators, it always returns `true`.
+ */
+ @safe @nogc pure
+ bool decRef() shared;
}
-private shared ISharedAllocator _processAllocator;
-private IAllocator _threadAllocator;
+/**
+A reference counted struct that wraps the dynamic shared allocator interface.
+This should be used wherever a uniform type is required for encapsulating
+various allocator implementations.
-private IAllocator setupThreadAllocator() nothrow @nogc @safe
+Code that defines allocators shareable across threads ultimately implements the
+$(LREF ISharedAllocator) interface, possibly by using
+$(LREF CSharedAllocatorImpl) below, and then build a `RCISharedAllocator` out
+of this.
+
+Composition of allocators is not recommended at this level due to
+inflexibility of dynamic interfaces and inefficiencies caused by cascaded
+multiple calls. Instead, compose allocators using the static interface defined
+in $(A std_experimental_allocator_building_blocks.html,
+`std.experimental.allocator.building_blocks`), then adapt the composed allocator
+to `RCISharedAllocator` (possibly by using $(LREF sharedAllocatorObject) below).
+*/
+shared struct RCISharedAllocator
+{
+ private ISharedAllocator _alloc;
+
+nothrow:
+ private @nogc pure @safe
+ this(shared ISharedAllocator alloc)
+ {
+ assert(alloc);
+ _alloc = alloc;
+ }
+
+ @nogc pure @safe
+ this(this)
+ {
+ if (_alloc !is null)
+ {
+ _alloc.incRef();
+ }
+ }
+
+ @nogc pure @safe
+ ~this()
+ {
+ if (_alloc !is null)
+ {
+ bool isLast = !_alloc.decRef();
+ if (isLast) _alloc = null;
+ }
+ }
+
+ @nogc pure @safe
+ auto ref opAssign()(RCISharedAllocator rhs)
+ {
+ if (_alloc is rhs._alloc)
+ {
+ return this;
+ }
+ // incRef was allready called by rhs posblit, so we're just moving
+ if (_alloc !is null)
+ {
+ _alloc.decRef();
+ }
+ _alloc = rhs._alloc;
+ // move
+ rhs._alloc = null;
+ return this;
+ }
+
+ @nogc pure @safe
+ bool isNull(this _)()
+ {
+ return _alloc is null;
+ }
+
+ @property uint alignment()
+ {
+ assert(_alloc);
+ return _alloc.alignment();
+ }
+
+ size_t goodAllocSize(size_t s)
+ {
+ assert(_alloc);
+ return _alloc.goodAllocSize(s);
+ }
+
+ void[] allocate(size_t n, TypeInfo ti = null)
+ {
+ assert(_alloc);
+ return _alloc.allocate(n, ti);
+ }
+
+ void[] alignedAllocate(size_t n, uint a)
+ {
+ assert(_alloc);
+ return _alloc.alignedAllocate(n, a);
+ }
+
+ void[] allocateAll()
+ {
+ assert(_alloc);
+ return _alloc.allocateAll();
+ }
+
+ bool expand(ref void[] b, size_t size)
+ {
+ assert(_alloc);
+ return _alloc.expand(b, size);
+ }
+
+ bool reallocate(ref void[] b, size_t size)
+ {
+ assert(_alloc);
+ return _alloc.reallocate(b, size);
+ }
+
+ bool alignedReallocate(ref void[] b, size_t size, uint alignment)
+ {
+ assert(_alloc);
+ return _alloc.alignedReallocate(b, size, alignment);
+ }
+
+ Ternary owns(void[] b)
+ {
+ assert(_alloc);
+ return _alloc.owns(b);
+ }
+
+ Ternary resolveInternalPointer(const void* p, ref void[] result)
+ {
+ assert(_alloc);
+ return _alloc.resolveInternalPointer(p, result);
+ }
+
+ bool deallocate(void[] b)
+ {
+ assert(_alloc);
+ return _alloc.deallocate(b);
+ }
+
+ bool deallocateAll()
+ {
+ assert(_alloc);
+ return _alloc.deallocateAll();
+ }
+
+ Ternary empty()
+ {
+ assert(_alloc);
+ return _alloc.empty();
+ }
+}
+
+private RCISharedAllocator _processAllocator;
+private RCIAllocator _threadAllocator;
+
+@nogc nothrow @safe
+private ref RCIAllocator setupThreadAllocator()
{
/*
Forwards the `_threadAllocator` calls to the `processAllocator`
*/
static class ThreadAllocator : IAllocator
{
+ nothrow:
+ private RCISharedAllocator _allocator;
+
+ @nogc @safe
+ this(ref RCISharedAllocator procAlloc)
+ {
+ _allocator = procAlloc;
+ }
+
override @property uint alignment()
{
- return processAllocator.alignment();
+ return _allocator.alignment();
}
override size_t goodAllocSize(size_t s)
{
- return processAllocator.goodAllocSize(s);
+ return _allocator.goodAllocSize(s);
}
override void[] allocate(size_t n, TypeInfo ti = null)
{
- return processAllocator.allocate(n, ti);
+ return _allocator.allocate(n, ti);
}
override void[] alignedAllocate(size_t n, uint a)
{
- return processAllocator.alignedAllocate(n, a);
+ return _allocator.alignedAllocate(n, a);
}
override void[] allocateAll()
{
- return processAllocator.allocateAll();
+ return _allocator.allocateAll();
}
override bool expand(ref void[] b, size_t size)
{
- return processAllocator.expand(b, size);
+ return _allocator.expand(b, size);
}
override bool reallocate(ref void[] b, size_t size)
{
- return processAllocator.reallocate(b, size);
+ return _allocator.reallocate(b, size);
}
override bool alignedReallocate(ref void[] b, size_t size, uint alignment)
{
- return processAllocator.alignedReallocate(b, size, alignment);
+ return _allocator.alignedReallocate(b, size, alignment);
}
override Ternary owns(void[] b)
{
- return processAllocator.owns(b);
+ return _allocator.owns(b);
}
override Ternary resolveInternalPointer(const void* p, ref void[] result)
{
- return processAllocator.resolveInternalPointer(p, result);
+ return _allocator.resolveInternalPointer(p, result);
}
override bool deallocate(void[] b)
{
- return processAllocator.deallocate(b);
+ return _allocator.deallocate(b);
}
override bool deallocateAll()
{
- return processAllocator.deallocateAll();
+ return _allocator.deallocateAll();
}
override Ternary empty()
{
- return processAllocator.empty();
+ return _allocator.empty();
+ }
+
+ @nogc pure @safe
+ override void incRef()
+ {
+ _allocator._alloc.incRef();
+ }
+
+ @nogc pure @safe
+ override bool decRef()
+ {
+ return _allocator._alloc.decRef();
}
}
- assert(!_threadAllocator);
- import std.conv : emplace;
+ assert(_threadAllocator.isNull);
+ import core.lifetime : emplace;
static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState;
- _threadAllocator = () @trusted { return emplace!(ThreadAllocator)(_threadAllocatorState[]); } ();
+ () @trusted {
+ _threadAllocator = RCIAllocator(emplace!(ThreadAllocator)(_threadAllocatorState[], processAllocator()));
+ }();
return _threadAllocator;
}
+// Fix threadAllocator bug: the threadAllocator should hold an internal reference
+// to the processAllocator that it's using
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ auto a = sharedAllocatorObject(Mallocator.instance);
+ auto buf = theAllocator.allocate(42);
+ processAllocator = a;
+ theAllocator.deallocate(buf);
+}
+
/**
Gets/sets the allocator for the current thread. This is the default allocator
that should be used for allocating thread-local memory. For allocating memory
-to be shared across threads, use $(D processAllocator) (below). By default,
-$(D theAllocator) ultimately fetches memory from $(D processAllocator), which
+to be shared across threads, use `processAllocator` (below). By default,
+`theAllocator` ultimately fetches memory from `processAllocator`, which
in turn uses the garbage collected heap.
*/
-nothrow @safe @nogc @property IAllocator theAllocator()
+@nogc nothrow @safe
+@property ref RCIAllocator theAllocator()
{
- auto p = _threadAllocator;
- return p !is null ? p : setupThreadAllocator();
+ alias p = _threadAllocator;
+ return !p.isNull() ? p : setupThreadAllocator();
}
/// Ditto
-nothrow @safe @nogc @property void theAllocator(IAllocator a)
+nothrow @system @nogc
+@property void theAllocator(RCIAllocator a)
{
- assert(a);
+ assert(!a.isNull);
_threadAllocator = a;
}
@@ -580,21 +1052,29 @@ nothrow @safe @nogc @property void theAllocator(IAllocator a)
/**
Gets/sets the allocator for the current process. This allocator must be used
for allocating memory shared across threads. Objects created using this
-allocator can be cast to $(D shared).
+allocator can be cast to `shared`.
*/
-@property shared(ISharedAllocator) processAllocator()
+@nogc nothrow @trusted
+@property ref RCISharedAllocator processAllocator()
{
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.concurrency : initOnce;
- return initOnce!_processAllocator(
- sharedAllocatorObject(GCAllocator.instance));
+
+ static RCISharedAllocator* forceAttributes()
+ {
+ return &initOnce!_processAllocator(
+ sharedAllocatorObject(GCAllocator.instance));
+ }
+
+ return *(cast(RCISharedAllocator* function() @nogc nothrow)(&forceAttributes))();
}
/// Ditto
-@property void processAllocator(shared ISharedAllocator a)
+@nogc nothrow @system
+@property void processAllocator(ref RCISharedAllocator a)
{
- assert(a);
- _processAllocator = a;
+ assert(!a.isNull);
+ processAllocator() = a;
}
@system unittest
@@ -604,97 +1084,122 @@ allocator can be cast to $(D shared).
import std.experimental.allocator.building_blocks.free_list : SharedFreeList;
import std.experimental.allocator.mallocator : Mallocator;
- assert(processAllocator);
- assert(theAllocator);
+ assert(!processAllocator.isNull);
+ assert(!theAllocator.isNull);
testAllocatorObject(processAllocator);
testAllocatorObject(theAllocator);
shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL;
- shared ISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL);
- assert(sharedFLObj);
+ RCISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL);
+ alias SharedAllocT = CSharedAllocatorImpl!(
+ shared SharedFreeList!(
+ Mallocator, chooseAtRuntime, chooseAtRuntime));
+
+ assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 1);
+ assert(!sharedFLObj.isNull);
testAllocatorObject(sharedFLObj);
// Test processAllocator setter
- shared ISharedAllocator oldProcessAllocator = processAllocator;
+ RCISharedAllocator oldProcessAllocator = processAllocator;
processAllocator = sharedFLObj;
- assert(processAllocator is sharedFLObj);
+ assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 2);
+ assert(processAllocator._alloc is sharedFLObj._alloc);
testAllocatorObject(processAllocator);
testAllocatorObject(theAllocator);
- assertThrown!AssertError(processAllocator = null);
+ assertThrown!AssertError(processAllocator = RCISharedAllocator(null));
// Restore initial processAllocator state
processAllocator = oldProcessAllocator;
+ assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 1);
assert(processAllocator is oldProcessAllocator);
- shared ISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL);
+ RCISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL);
testAllocatorObject(indirectShFLObj);
+ alias IndirectSharedAllocT = CSharedAllocatorImpl!(
+ shared SharedFreeList!(
+ Mallocator, chooseAtRuntime, chooseAtRuntime)
+ , Yes.indirect);
- IAllocator indirectMallocator = allocatorObject(&Mallocator.instance);
+ assert((cast(IndirectSharedAllocT)(indirectShFLObj._alloc)).rc == 1);
+
+ RCIAllocator indirectMallocator = allocatorObject(&Mallocator.instance);
testAllocatorObject(indirectMallocator);
}
/**
-Dynamically allocates (using $(D alloc)) and then creates in the memory
-allocated an object of type $(D T), using $(D args) (if any) for its
+Dynamically allocates (using `alloc`) and then creates in the memory
+allocated an object of type `T`, using `args` (if any) for its
initialization. Initialization occurs in the memory allocated and is otherwise
-semantically the same as $(D T(args)).
-(Note that using $(D alloc.make!(T[])) creates a pointer to an (empty) array
-of $(D T)s, not an array. To use an allocator to allocate and initialize an
-array, use $(D alloc.makeArray!T) described below.)
+semantically the same as `T(args)`.
+(Note that using `alloc.make!(T[])` creates a pointer to an (empty) array
+of `T`s, not an array. To use an allocator to allocate and initialize an
+array, use `alloc.makeArray!T` described below.)
Params:
T = Type of the object being created.
alloc = The allocator used for getting the needed memory. It may be an object
-implementing the static interface for allocators, or an $(D IAllocator)
+implementing the static interface for allocators, or an `IAllocator`
reference.
args = Optional arguments used for initializing the created object. If not
present, the object is default constructed.
-Returns: If $(D T) is a class type, returns a reference to the created $(D T)
-object. Otherwise, returns a $(D T*) pointing to the created object. In all
-cases, returns $(D null) if allocation failed.
+Returns: If `T` is a class type, returns a reference to the created `T`
+object. Otherwise, returns a `T*` pointing to the created object. In all
+cases, returns `null` if allocation failed.
-Throws: If $(D T)'s constructor throws, deallocates the allocated memory and
+Throws: If `T`'s constructor throws, deallocates the allocated memory and
propagates the exception.
*/
auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args)
{
import std.algorithm.comparison : max;
- import std.conv : emplace, emplaceRef;
- auto m = alloc.allocate(max(stateSize!T, 1));
- if (!m.ptr) return null;
-
- // make can only be @safe if emplace or emplaceRef is `pure`
- auto construct()
+ static if (!is(T == class) && !is(T == interface) && A.length == 0
+ && __traits(compiles, {T t;}) && __traits(isZeroInit, T)
+ && is(typeof(alloc.allocateZeroed(size_t.max))))
{
- static if (is(T == class)) return emplace!T(m, args);
- else
- {
- // Assume cast is safe as allocation succeeded for `stateSize!T`
- auto p = () @trusted { return cast(T*) m.ptr; }();
- emplaceRef(*p, args);
- return p;
- }
+ auto m = alloc.allocateZeroed(max(T.sizeof, 1));
+ return (() @trusted => cast(T*) m.ptr)();
}
-
- scope(failure)
+ else
{
- static if (is(typeof(() pure { return construct(); })))
+ import core.internal.lifetime : emplaceRef;
+ import core.lifetime : emplace;
+
+ auto m = alloc.allocate(max(stateSize!T, 1));
+ if (!m.ptr) return null;
+
+ // make can only be @safe if emplace or emplaceRef is `pure`
+ auto construct()
{
- // Assume deallocation is safe because:
- // 1) in case of failure, `m` is the only reference to this memory
- // 2) `m` is known to originate from `alloc`
- () @trusted { alloc.deallocate(m); }();
+ static if (is(T == class)) return emplace!T(m, args);
+ else
+ {
+ // Assume cast is safe as allocation succeeded for `stateSize!T`
+ auto p = () @trusted { return cast(T*) m.ptr; }();
+ emplaceRef!T(*p, args);
+ return p;
+ }
}
- else
+
+ scope(failure)
{
- alloc.deallocate(m);
+ static if (is(typeof(() pure { return construct(); })))
+ {
+ // Assume deallocation is safe because:
+ // 1) in case of failure, `m` is the only reference to this memory
+ // 2) `m` is known to originate from `alloc`
+ () @trusted { alloc.deallocate(m); }();
+ }
+ else
+ {
+ alloc.deallocate(m);
+ }
}
- }
- return construct();
+ return construct();
+ }
}
///
@@ -744,7 +1249,9 @@ auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args)
assert(outer.x == inner.getX);
}
-@system unittest // bugzilla 15639 & 15772
+// https://issues.dlang.org/show_bug.cgi?id=15639
+// https://issues.dlang.org/show_bug.cgi?id=15772
+@system unittest
{
abstract class Foo {}
class Bar: Foo {}
@@ -769,7 +1276,7 @@ auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args)
A* b = alloc.make!A(42);
assert(b.x == 42);
assert(b.y is null);
- import std.math : isNaN;
+ import std.math.traits : isNaN;
assert(b.z.isNaN);
b = alloc.make!A(43, "44", 45);
@@ -895,7 +1402,47 @@ nothrow @safe @nogc unittest
assertThrown(make!InvalidImpureStruct(Mallocator.instance, 42));
}
-private void fillWithMemcpy(T)(void[] array, auto ref T filler) nothrow
+// Don't allow zero-ctor-args `make` for structs with `@disable this();`
+@system unittest
+{
+ struct NoDefaultCtor
+ {
+ int i;
+ @disable this();
+ }
+ import std.experimental.allocator.mallocator : Mallocator;
+ static assert(!__traits(compiles, make!NoDefaultCtor(Mallocator.instance)),
+ "Don't allow zero-ctor-args `make` for structs with `@disable this();`");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18937
+@safe unittest
+{
+ static struct S
+ {
+ ubyte[16 * 1024] data;
+ }
+
+ static struct SomeAllocator
+ {
+ ubyte[] allocate(size_t) { return []; }
+ void deallocate(void[]) {}
+ }
+
+ auto x = SomeAllocator().make!S();
+}
+
+private void fillWithMemcpy(T)(scope void[] array, auto ref T filler) nothrow
+if (T.sizeof == 1)
+{
+ import core.stdc.string : memset;
+ import std.traits : CopyConstness;
+ if (!array.length) return;
+ memset(array.ptr, *cast(CopyConstness!(T*, ubyte*)) &filler, array.length);
+}
+
+private void fillWithMemcpy(T)(scope void[] array, auto ref T filler) nothrow
+if (T.sizeof != 1)
{
import core.stdc.string : memcpy;
import std.algorithm.comparison : min;
@@ -912,6 +1459,17 @@ private void fillWithMemcpy(T)(void[] array, auto ref T filler) nothrow
@system unittest
{
+ // Test T.sizeof == 1 path of fillWithMemcpy.
+ ubyte[] a;
+ fillWithMemcpy(a, ubyte(42));
+ assert(a.length == 0);
+ a = [ 1, 2, 3, 4, 5 ];
+ fillWithMemcpy(a, ubyte(42));
+ assert(a == [ 42, 42, 42, 42, 42]);
+}
+
+@system unittest
+{
int[] a;
fillWithMemcpy(a, 42);
assert(a.length == 0);
@@ -920,11 +1478,36 @@ private void fillWithMemcpy(T)(void[] array, auto ref T filler) nothrow
assert(a == [ 42, 42, 42, 42, 42]);
}
+//Make shared object
+@system unittest
+{
+ import core.atomic : atomicLoad;
+ auto psi = theAllocator.make!(shared(int))(10);
+ assert(10 == (*psi).atomicLoad());
+}
+
private T[] uninitializedFillDefault(T)(T[] array) nothrow
{
- T t = T.init;
- fillWithMemcpy(array, t);
- return array;
+ static if (__traits(isZeroInit, T))
+ {
+ import core.stdc.string : memset;
+ if (array !is null)
+ memset(array.ptr, 0, T.sizeof * array.length);
+ return array;
+ }
+ else static if (is(immutable T == immutable char) || is(immutable T == immutable wchar))
+ {
+ import core.stdc.string : memset;
+ if (array !is null)
+ memset(array.ptr, 0xff, T.sizeof * array.length);
+ return array;
+ }
+ else
+ {
+ T t = T.init;
+ fillWithMemcpy(array, t);
+ return array;
+ }
}
pure nothrow @nogc
@@ -943,10 +1526,28 @@ pure nothrow @nogc
int[] a = [1, 2, 4];
uninitializedFillDefault(a);
assert(a == [0, 0, 0]);
+
+ char[] b = [1, 2, 4];
+ uninitializedFillDefault(b);
+ assert(b == [0xff, 0xff, 0xff]);
+
+ wchar[] c = [1, 2, 4];
+ uninitializedFillDefault(c);
+ assert(c == [0xffff, 0xffff, 0xffff]);
+}
+
+@system unittest
+{
+ static struct P { float x = 0; float y = 0; }
+
+ static assert(__traits(isZeroInit, P));
+ P[] a = [P(10, 11), P(20, 21), P(40, 41)];
+ uninitializedFillDefault(a);
+ assert(a == [P.init, P.init, P.init]);
}
/**
-Create an array of $(D T) with $(D length) elements using $(D alloc). The array is either default-initialized, filled with copies of $(D init), or initialized with values fetched from `range`.
+Create an array of `T` with `length` elements using `alloc`. The array is either default-initialized, filled with copies of `init`, or initialized with values fetched from `range`.
Params:
T = element type of the array being created
@@ -956,7 +1557,7 @@ init = element used for filling the array
range = range used for initializing the array elements
Returns:
-The newly-created array, or $(D null) if either $(D length) was $(D 0) or
+The newly-created array, or `null` if either `length` was `0` or
allocation failed.
Throws:
@@ -967,10 +1568,30 @@ exception if the copy operation throws.
T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length)
{
if (!length) return null;
- auto m = alloc.allocate(T.sizeof * length);
- if (!m.ptr) return null;
- alias U = Unqual!T;
- return () @trusted { return cast(T[]) uninitializedFillDefault(cast(U[]) m); }();
+ static if (T.sizeof <= 1)
+ {
+ const nAlloc = length * T.sizeof;
+ }
+ else
+ {
+ import core.checkedint : mulu;
+ bool overflow;
+ const nAlloc = mulu(length, T.sizeof, overflow);
+ if (overflow) return null;
+ }
+
+ static if (__traits(isZeroInit, T) && hasMember!(Allocator, "allocateZeroed"))
+ {
+ auto m = alloc.allocateZeroed(nAlloc);
+ return (() @trusted => cast(T[]) m)();
+ }
+ else
+ {
+ auto m = alloc.allocate(nAlloc);
+ if (!m.ptr) return null;
+ alias U = Unqual!T;
+ return () @trusted { return cast(T[]) uninitializedFillDefault(cast(U[]) m); }();
+ }
}
@system unittest
@@ -1021,6 +1642,16 @@ T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length)
assert(c.equal([0, 0, 0, 0, 0]));
}
+// https://issues.dlang.org/show_bug.cgi?id=19085 - makeArray with void
+@system unittest
+{
+ auto b = theAllocator.makeArray!void(5);
+ scope(exit) theAllocator.dispose(b);
+ auto c = cast(ubyte[]) b;
+ assert(c.length == 5);
+ assert(c == [0, 0, 0, 0, 0]); // default initialization
+}
+
private enum hasPurePostblit(T) = !hasElaborateCopyConstructor!T ||
is(typeof(() pure { T.init.__xpostblit(); }));
@@ -1031,8 +1662,7 @@ private enum hasPureDtor(T) = !hasElaborateDestructor!T ||
private enum canSafelyDeallocPostRewind(T) = hasPurePostblit!T && hasPureDtor!T;
/// Ditto
-T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length,
- auto ref T init)
+T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length, T init)
{
if (!length) return null;
auto m = alloc.allocate(T.sizeof * length);
@@ -1060,7 +1690,7 @@ T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length,
}
}
}
- import std.conv : emplace;
+ import core.lifetime : emplace;
for (; i < length; ++i)
{
emplace!T(&result[i], init);
@@ -1096,6 +1726,18 @@ T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length,
@system unittest
{
+ void test(T)(in T initialValue)
+ {
+ auto t = theAllocator.makeArray!T(100, initialValue);
+ //auto t = theAllocator.makeArray(100, initialValue); // works well with the old code
+ }
+
+ const int init = 3;
+ test(init);
+}
+
+@system unittest
+{
void test(A)(auto ref A alloc)
{
long[] a = alloc.makeArray!long(0, 42);
@@ -1196,7 +1838,7 @@ if (isInputRange!R && !isInfinite!R)
alloc.deallocate(m);
}
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
static if (isNarrowString!R || isRandomAccessRange!R)
{
foreach (j; 0 .. range.length)
@@ -1253,7 +1895,7 @@ if (isInputRange!R && !isInfinite!R)
}
result = () @trusted { return cast(T[]) m; } ();
}
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
emplaceRef(result[initialized], range.front);
}
@@ -1377,7 +2019,7 @@ if (isInputRange!R && !isInfinite!R)
j++;
}
}
- assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange()));
+ makeArray!NoCopy(Mallocator.instance, NoCopyRange()); // rvalue elements are forwarded/moved
}
// test failure with an impure, failing struct
@@ -1407,9 +2049,8 @@ if (isInputRange!R && !isInfinite!R)
auto arr = [NoCopy(1), NoCopy(2)];
assertThrown(makeArray!NoCopy(Mallocator.instance, arr));
- // allow more copies and thus force reallocation
i = 0;
- maxElements = 30;
+ maxElements = 0; // disallow any postblit
static j = 0;
struct NoCopyRange
@@ -1429,25 +2070,19 @@ if (isInputRange!R && !isInfinite!R)
j++;
}
}
- assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange()));
- maxElements = 300;
auto arr2 = makeArray!NoCopy(Mallocator.instance, NoCopyRange());
-
- import std.algorithm.comparison : equal;
- import std.algorithm.iteration : map;
- import std.range : iota;
- assert(arr2.map!`a.val`.equal(iota(32, 204, 2)));
+ assert(i == j && i == 101); // all 101 rvalue elements forwarded/moved
}
-version (unittest)
+version (StdUnittest)
{
- private struct ForcedInputRange
+ private struct ForcedInputRange(T)
{
- int[]* array;
+ T[]* array;
pure nothrow @safe @nogc:
bool empty() { return !array || (*array).empty; }
- ref int front() { return (*array)[0]; }
+ ref T front() { return (*array)[0]; }
void popFront() { *array = (*array)[1 .. $]; }
}
}
@@ -1460,7 +2095,7 @@ version (unittest)
void test(A)(auto ref A alloc)
{
- ForcedInputRange r;
+ ForcedInputRange!int r;
long[] a = alloc.makeArray!long(r);
assert(a.length == 0 && a.ptr is null);
auto arr2 = arr;
@@ -1475,23 +2110,23 @@ version (unittest)
}
/**
-Grows $(D array) by appending $(D delta) more elements. The needed memory is
-allocated using $(D alloc). The extra elements added are either default-
-initialized, filled with copies of $(D init), or initialized with values
+Grows `array` by appending `delta` more elements. The needed memory is
+allocated using `alloc`. The extra elements added are either default-
+initialized, filled with copies of `init`, or initialized with values
fetched from `range`.
Params:
T = element type of the array being created
alloc = the allocator used for getting memory
array = a reference to the array being grown
-delta = number of elements to add (upon success the new length of $(D array) is
+delta = number of elements to add (upon success the new length of `array` is
$(D array.length + delta))
init = element used for filling the array
range = range used for initializing the array elements
Returns:
-$(D true) upon success, $(D false) if memory could not be allocated. In the
-latter case $(D array) is left unaffected.
+`true` upon success, `false` if memory could not be allocated. In the
+latter case `array` is left unaffected.
Throws:
The first two overloads throw only if `alloc`'s primitives do. The
@@ -1581,13 +2216,13 @@ if (isInputRange!R)
toFill.uninitializedFillDefault;
}
- for (; !range.empty; range.popFront, toFill.popFront)
+ for (; !range.empty; range.popFront, toFill = toFill[1 .. $])
{
- assert(!toFill.empty);
- import std.conv : emplace;
- emplace!T(&toFill.front, range.front);
+ assert(toFill.length > 0);
+ import core.lifetime : emplace;
+ emplace!T(&toFill[0], range.front);
}
- assert(toFill.empty);
+ assert(toFill.length == 0);
}
else
{
@@ -1604,7 +2239,7 @@ if (isInputRange!R)
array = cast(T[]) buf;
return false;
}
- import std.conv : emplace;
+ import core.lifetime : emplace;
emplace!T(buf[$ - T.sizeof .. $], range.front);
}
@@ -1627,7 +2262,7 @@ if (isInputRange!R)
@system unittest
{
auto arr = theAllocator.makeArray!int([1, 2, 3]);
- ForcedInputRange r;
+ ForcedInputRange!int r;
int[] b = [ 1, 2, 3, 4 ];
auto temp = b;
r.array = &temp;
@@ -1635,8 +2270,38 @@ if (isInputRange!R)
assert(arr == [1, 2, 3, 1, 2, 3, 4]);
}
+// Regression test for https://issues.dlang.org/show_bug.cgi?id=20929
+@system unittest
+{
+ static void test(Char, Allocator)(auto ref Allocator alloc)
+ {
+ auto arr = alloc.makeArray!Char(1, Char('f'));
+
+ import std.utf : byUTF;
+ auto forwardRange = "oo".byUTF!Char();
+ static assert(isForwardRange!(typeof(forwardRange)));
+ // Test the forward-range code-path.
+ assert(alloc.expandArray(arr, forwardRange));
+
+ assert(arr == "foo");
+
+ immutable(Char)[] temp = "bar";
+ auto inputRange = ForcedInputRange!(immutable(Char))(&temp);
+ // Test the input-range code-path.
+ assert(alloc.expandArray(arr, inputRange));
+
+ assert(arr == "foobar");
+ }
+
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ test!char(GCAllocator.instance);
+ test!wchar(GCAllocator.instance);
+ test!char(theAllocator);
+ test!wchar(theAllocator);
+}
+
/**
-Shrinks an array by $(D delta) elements.
+Shrinks an array by `delta` elements.
If $(D array.length < delta), does nothing and returns `false`. Otherwise,
destroys the last $(D array.length - delta) elements in the array and then
@@ -1647,7 +2312,7 @@ Params:
T = element type of the array being created
alloc = the allocator used for getting memory
array = a reference to the array being shrunk
-delta = number of elements to remove (upon success the new length of $(D array) is $(D array.length - delta))
+delta = number of elements to remove (upon success the new length of `array` is $(D array.length - delta))
Returns:
`true` upon success, `false` if memory could not be reallocated. In the latter
@@ -1725,23 +2390,25 @@ bool shrinkArray(T, Allocator)(auto ref Allocator alloc,
/**
-Destroys and then deallocates (using $(D alloc)) the object pointed to by a
-pointer, the class object referred to by a $(D class) or $(D interface)
+Destroys and then deallocates (using `alloc`) the object pointed to by a
+pointer, the class object referred to by a `class` or `interface`
reference, or an entire array. It is assumed the respective entities had been
allocated with the same allocator.
*/
-void dispose(A, T)(auto ref A alloc, T* p)
+void dispose(A, T)(auto ref A alloc, auto ref T* p)
{
static if (hasElaborateDestructor!T)
{
destroy(*p);
}
alloc.deallocate((cast(void*) p)[0 .. T.sizeof]);
+ static if (__traits(isRef, p))
+ p = null;
}
/// Ditto
-void dispose(A, T)(auto ref A alloc, T p)
+void dispose(A, T)(auto ref A alloc, auto ref T p)
if (is(T == class) || is(T == interface))
{
if (!p) return;
@@ -1760,10 +2427,12 @@ if (is(T == class) || is(T == interface))
auto support = (cast(void*) ob)[0 .. typeid(ob).initializer.length];
destroy(p);
alloc.deallocate(support);
+ static if (__traits(isRef, p))
+ p = null;
}
/// Ditto
-void dispose(A, T)(auto ref A alloc, T[] array)
+void dispose(A, T)(auto ref A alloc, auto ref T[] array)
{
static if (hasElaborateDestructor!(typeof(array[0])))
{
@@ -1773,6 +2442,8 @@ void dispose(A, T)(auto ref A alloc, T[] array)
}
}
alloc.deallocate(array);
+ static if (__traits(isRef, array))
+ array = null;
}
@system unittest
@@ -1813,7 +2484,30 @@ void dispose(A, T)(auto ref A alloc, T[] array)
theAllocator.dispose(arr);
}
-@system unittest //bugzilla 15721
+// https://issues.dlang.org/show_bug.cgi?id=16512
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ int* i = Mallocator.instance.make!int(0);
+ Mallocator.instance.dispose(i);
+ assert(i is null);
+
+ Object o = Mallocator.instance.make!Object();
+ Mallocator.instance.dispose(o);
+ assert(o is null);
+
+ uint* u = Mallocator.instance.make!uint(0);
+ Mallocator.instance.dispose((){return u;}());
+ assert(u !is null);
+
+ uint[] ua = Mallocator.instance.makeArray!uint([0,1,2]);
+ Mallocator.instance.dispose(ua);
+ assert(ua is null);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15721
+@system unittest
{
import std.experimental.allocator.mallocator : Mallocator;
@@ -1886,7 +2580,7 @@ T = element type of an element of the multidimensional array
alloc = the allocator used for getting memory
array = the multidimensional array that is to be deallocated
*/
-void disposeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc, T[] array)
+void disposeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc, auto ref T[] array)
{
static if (isArray!T)
{
@@ -1895,6 +2589,8 @@ void disposeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc, T[] ar
}
dispose(alloc, array);
+ static if (__traits(isRef, array))
+ array = null;
}
///
@@ -1953,84 +2649,74 @@ void disposeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc, T[] ar
/**
-Returns a dynamically-typed $(D CAllocator) built around a given statically-
-typed allocator $(D a) of type $(D A). Passing a pointer to the allocator
+Returns a dynamically-typed `CAllocator` built around a given statically-
+typed allocator `a` of type `A`. Passing a pointer to the allocator
creates a dynamic allocator around the allocator pointed to by the pointer,
without attempting to copy or move it. Passing the allocator by value or
reference behaves as follows.
$(UL
-$(LI If $(D A) has no state, the resulting object is allocated in static
+$(LI If `A` has no state, the resulting object is allocated in static
shared storage.)
-$(LI If $(D A) has state and is copyable, the result will store a copy of it
-within. The result itself is allocated in its own statically-typed allocator.)
-$(LI If $(D A) has state and is not copyable, the result will move the
-passed-in argument into the result. The result itself is allocated in its own
-statically-typed allocator.)
+$(LI If `A` has state, the result will $(REF move, std,algorithm,mutation)
+the supplied allocator $(D A a) within. The result itself is allocated in its
+own statically-typed allocator.)
)
*/
-CAllocatorImpl!A allocatorObject(A)(auto ref A a)
+RCIAllocator allocatorObject(A)(auto ref A a)
if (!isPointer!A)
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
static if (stateSize!A == 0)
{
enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof);
- static __gshared ulong[s] state;
- static __gshared CAllocatorImpl!A result;
- if (!result)
+ __gshared ulong[s] state;
+ __gshared RCIAllocator result;
+ if (result.isNull)
{
// Don't care about a few races
- result = emplace!(CAllocatorImpl!A)(state[]);
+ result = RCIAllocator(emplace!(CAllocatorImpl!A)(state[]));
}
- assert(result);
+ assert(!result.isNull);
return result;
}
- else static if (is(typeof({ A b = a; A c = b; }))) // copyable
+ else
{
auto state = a.allocate(stateSize!(CAllocatorImpl!A));
+ import std.algorithm.mutation : move;
import std.traits : hasMember;
static if (hasMember!(A, "deallocate"))
{
scope(failure) a.deallocate(state);
}
- return cast(CAllocatorImpl!A) emplace!(CAllocatorImpl!A)(state);
- }
- else // the allocator object is not copyable
- {
- // This is sensitive... create on the stack and then move
- enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof);
- ulong[s] state;
- import std.algorithm.mutation : move;
- emplace!(CAllocatorImpl!A)(state[], move(a));
- auto dynState = a.allocate(stateSize!(CAllocatorImpl!A));
- // Bitblast the object in its final destination
- dynState[] = state[];
- return cast(CAllocatorImpl!A) dynState.ptr;
+ auto tmp = cast(CAllocatorImpl!A) emplace!(CAllocatorImpl!A)(state);
+ move(a, tmp.impl);
+ return RCIAllocator(tmp);
}
}
/// Ditto
-CAllocatorImpl!(A, Yes.indirect) allocatorObject(A)(A* pa)
+RCIAllocator allocatorObject(A)(A* pa)
{
assert(pa);
- import std.conv : emplace;
+ import core.lifetime : emplace;
auto state = pa.allocate(stateSize!(CAllocatorImpl!(A, Yes.indirect)));
import std.traits : hasMember;
static if (hasMember!(A, "deallocate"))
{
scope(failure) pa.deallocate(state);
}
- return emplace!(CAllocatorImpl!(A, Yes.indirect))
- (state, pa);
+ return RCIAllocator(emplace!(CAllocatorImpl!(A, Yes.indirect))
+ (state, pa));
}
///
@system unittest
{
import std.experimental.allocator.mallocator : Mallocator;
- IAllocator a = allocatorObject(Mallocator.instance);
+
+ RCIAllocator a = allocatorObject(Mallocator.instance);
auto b = a.allocate(100);
assert(b.length == 100);
assert(a.deallocate(b));
@@ -2045,52 +2731,82 @@ CAllocatorImpl!(A, Yes.indirect) allocatorObject(A)(A* pa)
assert(a.deallocate(b));
}
+@system unittest
+{
+ import std.conv;
+ import std.experimental.allocator.mallocator;
+ import std.experimental.allocator.building_blocks.stats_collector;
+
+ alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
+ SCAlloc statsCollectorAlloc;
+ assert(statsCollectorAlloc.bytesUsed == 0);
+
+ auto _allocator = allocatorObject(statsCollectorAlloc);
+ // Ensure that the allocator was passed through in CAllocatorImpl
+ // This allocator was used to allocate the chunk that holds the
+ // CAllocatorImpl object; which is it's own wrapper
+ assert((cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed
+ == stateSize!(CAllocatorImpl!(SCAlloc)));
+ _allocator.allocate(1);
+ assert((cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed
+ == stateSize!(CAllocatorImpl!(SCAlloc)) + 1);
+}
+
/**
-Returns a dynamically-typed $(D CSharedAllocator) built around a given statically-
-typed allocator $(D a) of type $(D A). Passing a pointer to the allocator
+Returns a dynamically-typed `CSharedAllocator` built around a given statically-
+typed allocator `a` of type `A`. Passing a pointer to the allocator
creates a dynamic allocator around the allocator pointed to by the pointer,
without attempting to copy or move it. Passing the allocator by value or
reference behaves as follows.
$(UL
-$(LI If $(D A) has no state, the resulting object is allocated in static
+$(LI If `A` has no state, the resulting object is allocated in static
shared storage.)
-$(LI If $(D A) has state and is copyable, the result will store a copy of it
-within. The result itself is allocated in its own statically-typed allocator.)
-$(LI If $(D A) has state and is not copyable, the result will move the
+$(LI If `A` has state and is copyable, the result will
+$(REF move, std,algorithm,mutation) the supplied allocator $(D A a) within.
+The result itself is allocated in its own statically-typed allocator.)
+$(LI If `A` has state and is not copyable, the result will move the
passed-in argument into the result. The result itself is allocated in its own
statically-typed allocator.)
)
*/
-shared(CSharedAllocatorImpl!A) sharedAllocatorObject(A)(auto ref A a)
+//nothrow @safe
+//nothrow @nogc @safe
+nothrow
+RCISharedAllocator sharedAllocatorObject(A)(auto ref A a)
if (!isPointer!A)
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
static if (stateSize!A == 0)
{
enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof);
- static __gshared ulong[s] state;
- static shared CSharedAllocatorImpl!A result;
- if (!result)
+ static shared ulong[s] state;
+ static RCISharedAllocator result;
+ if (result.isNull)
{
// Don't care about a few races
- result = cast(shared
- CSharedAllocatorImpl!A)(emplace!(CSharedAllocatorImpl!A)(state[]));
+ result = RCISharedAllocator(
+ (cast(shared CSharedAllocatorImpl!A)(
+ emplace!(CSharedAllocatorImpl!A)(
+ (() @trusted => cast(ulong[]) state[])()))));
}
- assert(result);
+ assert(!result.isNull);
return result;
}
else static if (is(typeof({ shared A b = a; shared A c = b; }))) // copyable
{
auto state = a.allocate(stateSize!(CSharedAllocatorImpl!A));
+ import std.algorithm.mutation : move;
import std.traits : hasMember;
static if (hasMember!(A, "deallocate"))
{
scope(failure) a.deallocate(state);
}
- return emplace!(shared CSharedAllocatorImpl!A)(state);
+ auto tmp = emplace!(shared CSharedAllocatorImpl!A)(state);
+ move(a, tmp.impl);
+ return RCISharedAllocator(tmp);
}
else // the allocator object is not copyable
{
@@ -2099,17 +2815,17 @@ if (!isPointer!A)
}
/// Ditto
-shared(CSharedAllocatorImpl!(A, Yes.indirect)) sharedAllocatorObject(A)(A* pa)
+RCISharedAllocator sharedAllocatorObject(A)(A* pa)
{
assert(pa);
- import std.conv : emplace;
+ import core.lifetime : emplace;
auto state = pa.allocate(stateSize!(CSharedAllocatorImpl!(A, Yes.indirect)));
import std.traits : hasMember;
static if (hasMember!(A, "deallocate"))
{
scope(failure) pa.deallocate(state);
}
- return emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa);
+ return RCISharedAllocator(emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa));
}
@@ -2126,16 +2842,23 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
{
import std.traits : hasMember;
+ static if (stateSize!Allocator) private size_t rc = 1;
+
/**
The implementation is available as a public member.
*/
static if (indirect)
{
+ nothrow:
private Allocator* pimpl;
+
+ @nogc pure @safe
ref Allocator impl()
{
return *pimpl;
}
+
+ @nogc pure @safe
this(Allocator* pa)
{
pimpl = pa;
@@ -2147,6 +2870,7 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
else alias impl = Allocator.instance;
}
+nothrow:
/// Returns `impl.alignment`.
override @property uint alignment()
{
@@ -2293,6 +3017,44 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
return null;
}
}
+
+ @nogc nothrow pure @safe
+ override void incRef()
+ {
+ static if (stateSize!Allocator) ++rc;
+ }
+
+ @nogc nothrow pure @trusted
+ override bool decRef()
+ {
+ static if (stateSize!Allocator)
+ {
+ import core.stdc.string : memcpy;
+
+ if (rc == 1)
+ {
+ static if (indirect)
+ {
+ Allocator* tmp = pimpl;
+ }
+ else
+ {
+ Allocator tmp;
+ memcpy(&tmp, &this.impl, Allocator.sizeof);
+ }
+ void[] support = (cast(void*) this)[0 .. stateSize!(typeof(this))];
+ tmp.deallocate(support);
+ return false;
+ }
+
+ --rc;
+ return true;
+ }
+ else
+ {
+ return true;
+ }
+ }
}
/**
@@ -2308,17 +3070,25 @@ class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
: ISharedAllocator
{
import std.traits : hasMember;
+ import core.atomic : atomicOp, atomicLoad;
+
+ static if (stateSize!Allocator) shared size_t rc = 1;
/**
The implementation is available as a public member.
*/
static if (indirect)
{
+ nothrow:
private shared Allocator* pimpl;
+
+ @nogc pure @safe
ref Allocator impl() shared
{
return *pimpl;
}
+
+ @nogc pure @safe
this(Allocator* pa) shared
{
pimpl = pa;
@@ -2330,6 +3100,7 @@ class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
else alias impl = Allocator.instance;
}
+nothrow:
/// Returns `impl.alignment`.
override @property uint alignment() shared
{
@@ -2476,6 +3247,45 @@ class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
return null;
}
}
+
+ @nogc nothrow pure @safe
+ override void incRef() shared
+ {
+ static if (stateSize!Allocator) atomicOp!"+="(rc, 1);
+ }
+
+ @nogc nothrow pure @trusted
+ override bool decRef() shared
+ {
+ static if (stateSize!Allocator)
+ {
+ import core.stdc.string : memcpy;
+
+ // rc starts as 1 to avoid comparing with size_t(0) - 1
+ if (atomicOp!"-="(rc, 1) == 0)
+ {
+ static if (indirect)
+ {
+ Allocator* tmp = pimpl;
+ }
+ else
+ {
+ Allocator tmp;
+ memcpy(cast(void*) &tmp, cast(void*) &this.impl, Allocator.sizeof);
+ Allocator empty;
+ memcpy(cast(void*) &this.impl, cast(void*) &empty, Allocator.sizeof);
+ }
+ void[] support = (cast(void*) this)[0 .. stateSize!(typeof(this))];
+ (cast(bool delegate(void[]) @nogc nothrow pure)(&tmp.deallocate))(support);
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ return true;
+ }
+ }
}
@@ -2513,17 +3323,15 @@ class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
theAllocator.dispose(arr);
}
-__EOF__
-
/**
-Stores an allocator object in thread-local storage (i.e. non-$(D shared) D
-global). $(D ThreadLocal!A) is a subtype of $(D A) so it appears to implement
-$(D A)'s allocator primitives.
+Stores an allocator object in thread-local storage (i.e. non-`shared` D
+global). `ThreadLocal!A` is a subtype of `A` so it appears to implement
+`A`'s allocator primitives.
-$(D A) must hold state, otherwise $(D ThreadLocal!A) refuses instantiation. This
-means e.g. $(D ThreadLocal!Mallocator) does not work because $(D Mallocator)'s
-state is not stored as members of $(D Mallocator), but instead is hidden in the
+`A` must hold state, otherwise `ThreadLocal!A` refuses instantiation. This
+means e.g. `ThreadLocal!Mallocator` does not work because `Mallocator`'s
+state is not stored as members of `Mallocator`, but instead is hidden in the
C library implementation.
*/
@@ -2554,23 +3362,28 @@ struct ThreadLocal(A)
}
///
+@system
unittest
{
+ import std.experimental.allocator.building_blocks.free_list : FreeList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+
static assert(!is(ThreadLocal!Mallocator));
static assert(!is(ThreadLocal!GCAllocator));
- alias ThreadLocal!(FreeList!(GCAllocator, 0, 8)) Allocator;
+ alias Allocator = ThreadLocal!(FreeList!(GCAllocator, 0, 8));
auto b = Allocator.instance.allocate(5);
- static assert(hasMember!(Allocator, "allocate"));
+ static assert(__traits(hasMember, Allocator, "allocate"));
}
/*
(Not public.)
A binary search tree that uses no allocation of its own. Instead, it relies on
-user code to allocate nodes externally. Then $(D EmbeddedTree)'s primitives wire
+user code to allocate nodes externally. Then `EmbeddedTree`'s primitives wire
the nodes appropriately.
-Warning: currently $(D EmbeddedTree) is not using rebalancing, so it may
+Warning: currently `EmbeddedTree` is not using rebalancing, so it may
degenerate. A red-black tree implementation storing the color with one of the
pointers is planned for the future.
*/
@@ -2716,12 +3529,17 @@ private struct EmbeddedTree(T, alias less)
void dump()
{
+ import std.stdio : writeln;
writeln(typeid(this), " @ ", cast(void*) &this);
dump(root, 3);
}
void dump(Node* r, uint indent)
{
+ import std.stdio : write, writeln;
+ import std.range : repeat;
+ import std.array : array;
+
write(repeat(' ', indent).array);
if (!r)
{
@@ -2749,12 +3567,15 @@ private struct EmbeddedTree(T, alias less)
}
}
+@system
unittest
{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+
alias a = GCAllocator.instance;
alias Tree = EmbeddedTree!(int, (a, b) => a.payload < b.payload);
Tree t;
- assert(t.empty);
+ assert(t.empty == Ternary.yes);
int[] vals = [ 6, 3, 9, 1, 0, 2, 8, 11 ];
foreach (v; vals)
{
@@ -2763,21 +3584,21 @@ unittest
assert(n);
t.assertSane;
}
- assert(!t.empty);
+ assert(t.empty != Ternary.yes);
foreach (v; vals)
{
Tree.Node n = { v };
assert(t.remove(&n));
t.assertSane;
}
- assert(t.empty);
+ assert(t.empty == Ternary.yes);
}
/*
-$(D InternalPointersTree) adds a primitive on top of another allocator: calling
-$(D resolveInternalPointer(p)) returns the block within which the internal
-pointer $(D p) lies. Pointers right after the end of allocated blocks are also
+`InternalPointersTree` adds a primitive on top of another allocator: calling
+`resolveInternalPointer(p)` returns the block within which the internal
+pointer `p` lies. Pointers right after the end of allocated blocks are also
considered internal.
The implementation stores three additional words with each allocation (one for
@@ -2786,6 +3607,8 @@ the block size and two for search management).
*/
private struct InternalPointersTree(Allocator)
{
+ import std.experimental.allocator.building_blocks.affix_allocator : AffixAllocator;
+
alias Tree = EmbeddedTree!(size_t,
(a, b) => cast(void*) a + a.payload < cast(void*) b);
alias Parent = AffixAllocator!(Allocator, Tree.Node);
@@ -2815,7 +3638,7 @@ private struct InternalPointersTree(Allocator)
/// Ditto
bool deallocate(void[] b)
{
- if (!b.ptr) return;
+ if (!b.ptr) return true;
Tree.Node* n = &parent.prefix(b);
blockMap.remove(n) || assert(false);
parent.deallocate(b);
@@ -2856,9 +3679,10 @@ private struct InternalPointersTree(Allocator)
return Ternary(blockMap.empty);
}
- /** Returns the block inside which $(D p) resides, or $(D null) if the
+ /** Returns the block inside which `p` resides, or `null` if the
pointer does not belong.
*/
+ pure nothrow @safe @nogc
Ternary resolveInternalPointer(const void* p, ref void[] result)
{
// Must define a custom find
@@ -2870,7 +3694,7 @@ private struct InternalPointersTree(Allocator)
{
n = n.left;
}
- else if (p > (cast(void*) (n + 1)) + n.payload)
+ else if ((() @trusted => p > (cast(void*) (n + 1)) + n.payload)())
{
n = n.right;
}
@@ -2884,13 +3708,17 @@ private struct InternalPointersTree(Allocator)
auto n = find();
if (!n) return Ternary.no;
- result = (cast(void*) (n + 1))[0 .. n.payload];
+ result = (() @trusted => (cast(void*) (n + 1))[0 .. n.payload])();
return Ternary.yes;
}
}
+@system
unittest
{
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.random : randomCover;
+
InternalPointersTree!(Mallocator) a;
int[] vals = [ 6, 3, 9, 1, 2, 8, 11 ];
void[][] allox;
@@ -2902,28 +3730,40 @@ unittest
foreach (b; allox)
{
- void[] p;
- Ternary r = a.resolveInternalPointer(b.ptr, p);
- assert(p.ptr is b.ptr && p.length >= b.length);
- r = a.resolveInternalPointer(b.ptr + b.length, p);
- assert(p.ptr is b.ptr && p.length >= b.length);
- r = a.resolveInternalPointer(b.ptr + b.length / 2, p);
- assert(p.ptr is b.ptr && p.length >= b.length);
- auto bogus = new void[b.length];
- assert(a.resolveInternalPointer(bogus.ptr, p) == Ternary.no);
+ () pure nothrow @safe {
+ void[] p;
+ Ternary r = (() @nogc => a.resolveInternalPointer(&b[0], p))();
+ assert(&p[0] == &b[0] && p.length >= b.length);
+ r = a.resolveInternalPointer((() @trusted => &b[0] + b.length)(), p);
+ assert(&p[0] == &b[0] && p.length >= b.length);
+ r = a.resolveInternalPointer((() @trusted => &b[0] + b.length / 2)(), p);
+ assert(&p[0] == &b[0] && p.length >= b.length);
+ auto bogus = new void[b.length];
+ assert(a.resolveInternalPointer(&bogus[0], p) == Ternary.no);
+ }();
}
foreach (b; allox.randomCover)
{
- a.deallocate(b);
+ () nothrow @nogc { a.deallocate(b); }();
}
- assert(a.empty);
+ assert(a.empty == Ternary.yes);
}
//version (std_allocator_benchmark)
+@system
unittest
{
+ import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
+ import std.experimental.allocator.building_blocks.allocator_list : AllocatorList;
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+ import std.experimental.allocator.building_blocks.segregator : Segregator;
+ import std.experimental.allocator.building_blocks.bucketizer : Bucketizer;
+ import std.experimental.allocator.building_blocks.free_list : FreeList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+
static void testSpeed(A)()
{
static if (stateSize!A) A a;
@@ -2938,11 +3778,11 @@ unittest
switch (uniform(0, 2))
{
case 0:
- a.deallocate(bufs[j]);
+ () nothrow @nogc { a.deallocate(bufs[j]); }();
bufs[j] = a.allocate(uniform(0, 4096));
break;
case 1:
- a.deallocate(bufs[j]);
+ () nothrow @nogc { a.deallocate(bufs[j]); }();
bufs[j] = null;
break;
default:
@@ -2951,6 +3791,8 @@ unittest
}
}
+ import std.algorithm.comparison : max;
+
alias FList = FreeList!(GCAllocator, 0, unbounded);
alias A = Segregator!(
8, FreeList!(GCAllocator, 0, 8),
@@ -2961,23 +3803,34 @@ unittest
2048, Bucketizer!(FList, 1025, 2048, 256),
3584, Bucketizer!(FList, 2049, 3584, 512),
4072 * 1024, AllocatorList!(
- (size_t n) => BitmappedBlock!(4096)(GCAllocator.instance.allocate(
+ (size_t n) => BitmappedBlock!(4096)(cast(ubyte[]) GCAllocator.instance.allocate(
max(n, 4072 * 1024)))),
GCAllocator
);
- import std.datetime, std.experimental.allocator.null_allocator;
+ import std.stdio;
+ import std.conv : to;
+ import std.datetime.stopwatch;
+ import std.algorithm.iteration : map;
+
if (false) writeln(benchmark!(
testSpeed!NullAllocator,
testSpeed!Mallocator,
testSpeed!GCAllocator,
testSpeed!(ThreadLocal!A),
testSpeed!(A),
- )(20)[].map!(t => t.to!("seconds", double)));
+ )(20)[].map!(t => t.to!Duration));
}
+@system
unittest
{
+ import std.experimental.allocator.building_blocks.free_list : FreeList;
+ import std.experimental.allocator.building_blocks.region : InSituRegion;
+ import std.experimental.allocator.building_blocks.fallback_allocator : FallbackAllocator;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+
auto a = allocatorObject(Mallocator.instance);
auto b = a.allocate(100);
assert(b.length == 100);
@@ -2995,16 +3848,24 @@ unittest
}
///
+@system
unittest
{
+ import std.experimental.allocator.building_blocks.allocator_list : AllocatorList;
+ import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+ import std.experimental.allocator.building_blocks.segregator : Segregator;
+ import std.experimental.allocator.building_blocks.bucketizer : Bucketizer;
+ import std.experimental.allocator.building_blocks.free_list : FreeList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+
/// Define an allocator bound to the built-in GC.
- IAllocator alloc = allocatorObject(GCAllocator.instance);
+ auto alloc = allocatorObject(GCAllocator.instance);
auto b = alloc.allocate(42);
assert(b.length == 42);
- assert(alloc.deallocate(b) == Ternary.yes);
+ assert(alloc.deallocate(b));
+ import std.algorithm.comparison : max;
// Define an elaborate allocator and bind it to the class API.
- // Note that the same variable "alloc" is used.
alias FList = FreeList!(GCAllocator, 0, unbounded);
alias A = ThreadLocal!(
Segregator!(
@@ -3016,13 +3877,13 @@ unittest
2048, Bucketizer!(FList, 1025, 2048, 256),
3584, Bucketizer!(FList, 2049, 3584, 512),
4072 * 1024, AllocatorList!(
- (n) => BitmappedBlock!(4096)(GCAllocator.instance.allocate(
+ (n) => BitmappedBlock!(4096)(cast(ubyte[]) GCAllocator.instance.allocate(
max(n, 4072 * 1024)))),
GCAllocator
)
);
auto alloc2 = allocatorObject(A.instance);
- b = alloc.allocate(101);
- assert(alloc.deallocate(b) == Ternary.yes);
+ b = alloc2.allocate(101);
+ assert(alloc2.deallocate(b));
}
diff --git a/libphobos/src/std/experimental/allocator/showcase.d b/libphobos/src/std/experimental/allocator/showcase.d
index 6985e5dad14..3f08c565d34 100644
--- a/libphobos/src/std/experimental/allocator/showcase.d
+++ b/libphobos/src/std/experimental/allocator/showcase.d
@@ -1,9 +1,10 @@
+// Written in the D programming language.
/**
-
Collection of typical and useful prebuilt allocators using the given
components. User code would typically import this module and use its
facilities, or import individual heap building blocks and assemble them.
+Source: $(PHOBOSSRC std/experimental/allocator/_showcase.d)
*/
module std.experimental.allocator.showcase;
@@ -14,8 +15,8 @@ import std.traits : hasMember;
/**
-Allocator that uses stack allocation for up to $(D stackSize) bytes and
-then falls back to $(D Allocator). Defined as:
+Allocator that uses stack allocation for up to `stackSize` bytes and
+then falls back to `Allocator`. Defined as:
----
alias StackFront(size_t stackSize, Allocator) =
diff --git a/libphobos/src/std/experimental/allocator/typed.d b/libphobos/src/std/experimental/allocator/typed.d
index 92828f3879e..85cc6493b7b 100644
--- a/libphobos/src/std/experimental/allocator/typed.d
+++ b/libphobos/src/std/experimental/allocator/typed.d
@@ -1,3 +1,4 @@
+// Written in the D programming language.
/**
This module defines `TypedAllocator`, a statically-typed allocator that
aggregates multiple untyped allocators and uses them depending on the static
@@ -5,8 +6,10 @@ properties of the types allocated. For example, distinct allocators may be used
for thread-local vs. thread-shared data, or for fixed-size data (`struct`,
`class` objects) vs. resizable data (arrays).
+Source: $(PHOBOSSRC std/experimental/allocator/typed.d)
+
Macros:
-T2=$(TR <td style="text-align:left">$(D $1)</td> $(TD $(ARGS $+)))
+T2=$(TR <td style="text-align:left">`$1`</td> $(TD $(ARGS $+)))
*/
module std.experimental.allocator.typed;
@@ -25,7 +28,7 @@ allocator accordingly.
*/
enum AllocFlag : uint
{
- init = 0,
+ _init = 0,
/**
Fixed-size allocation (unlikely to get reallocated later). Examples: `int`,
`double`, any `struct` or `class` type. By default it is assumed that the
@@ -80,12 +83,12 @@ not $(D Tuple!(int, string)), which contains an indirection.)
$(T2 AllocFlag.threadLocal |$(NBSP)AllocFlag.hasNoIndirections,
As above, but may be reallocated later. Examples of types fitting this
-description are $(D int[]), $(D double[]), $(D Tuple!(int, long)[]), but not
+description are `int[]`, `double[]`, $(D Tuple!(int, long)[]), but not
$(D Tuple!(int, string)[]), which contains an indirection.)
$(T2 AllocFlag.threadLocal,
As above, but may embed indirections. Examples of types fitting this
-description are $(D int*[]), $(D Object[]), $(D Tuple!(int, string)[]).)
+description are `int*[]`, `Object[]`, $(D Tuple!(int, string)[]).)
$(T2 AllocFlag.immutableShared |$(NBSP)AllocFlag.hasNoIndirections
|$(NBSP)AllocFlag.fixedSize,
@@ -402,9 +405,10 @@ struct TypedAllocator(PrimaryAllocator, Policies...)
| AllocFlag.hasNoIndirections,
MmapAllocator,
);
+
MyAllocator a;
auto b = &a.allocatorFor!0();
- static assert(is(typeof(*b) == shared GCAllocator));
+ static assert(is(typeof(*b) == shared const(GCAllocator)));
enum f1 = AllocFlag.fixedSize | AllocFlag.threadLocal;
auto c = &a.allocatorFor!f1();
static assert(is(typeof(*c) == Mallocator));
diff --git a/libphobos/src/std/experimental/checkedint.d b/libphobos/src/std/experimental/checkedint.d
index 48ed2f7a07b..b33c2ed354d 100644
--- a/libphobos/src/std/experimental/checkedint.d
+++ b/libphobos/src/std/experimental/checkedint.d
@@ -1,3 +1,4 @@
+// Written in the D programming language.
/**
$(SCRIPT inhibitQuickIndex = 1;)
@@ -185,14 +186,20 @@ $(TR $(TD `onUpperBound`) $(TD If defined, $(D hook.onUpperBound(value, bound))
binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
and `>>>=` is larger than the largest value representable by `T`.)
)
+$(TR $(TD `hookToHash`) $(TD If defined, $(D hook.hookToHash(payload))
+(where `payload` is a reference to the value wrapped by Checked) is forwarded
+to when `toHash` is called on a Checked type. Custom hashing can be implemented
+in a `Hook`, otherwise the built-in hashing is used.)
+)
)
+Source: $(PHOBOSSRC std/experimental/checkedint.d)
*/
module std.experimental.checkedint;
import std.traits : isFloatingPoint, isIntegral, isNumeric, isUnsigned, Unqual;
///
-@system unittest
+@safe unittest
{
int[] concatAndAdd(int[] a, int[] b, int offset)
{
@@ -208,6 +215,44 @@ import std.traits : isFloatingPoint, isIntegral, isNumeric, isUnsigned, Unqual;
assert(concatAndAdd([1, 2, 3], [4, 5], -1) == [0, 1, 2, 3, 4]);
}
+
+/// `Saturate` stops at an overflow
+@safe unittest
+{
+ auto x = (cast(byte) 127).checked!Saturate;
+ assert(x == 127);
+ x++;
+ assert(x == 127);
+}
+
+/// `WithNaN` has a special "Not a Number" (NaN) value akin to the homonym value reserved for floating-point values
+@safe unittest
+{
+ auto x = 100.checked!WithNaN;
+ assert(x == 100);
+ x /= 0;
+ assert(x.isNaN);
+}
+
+/// `ProperCompare` fixes the comparison operators ==, !=, <, <=, >, and >= to return correct results
+@safe unittest
+{
+ uint x = 1;
+ auto y = x.checked!ProperCompare;
+ assert(x < -1); // built-in comparison
+ assert(y > -1); // ProperCompare
+}
+
+/// `Throw` fails every incorrect operation by throwing an exception
+@safe unittest
+{
+ import std.exception : assertThrown;
+ auto x = -1.checked!Throw;
+ assertThrown(x / 0);
+ assertThrown(x + int.min);
+ assertThrown(x == uint.max);
+}
+
/**
Checked integral type wraps an integral `T` and customizes its behavior with the
help of a `Hook` type. The type wrapped must be one of the predefined integrals
@@ -218,7 +263,9 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
{
import std.algorithm.comparison : among;
import std.experimental.allocator.common : stateSize;
- import std.traits : hasMember;
+ import std.format.spec : FormatSpec;
+ import std.range.primitives : isInputRange, ElementType;
+ import std.traits : hasMember, isSomeChar;
/**
The type of the integral subject to checking.
@@ -262,7 +309,7 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
{
enum Checked!(T, Hook) min = Checked!(T, Hook)(Hook.min!T);
///
- @system unittest
+ @safe unittest
{
assert(Checked!short.min == -32768);
assert(Checked!(short, WithNaN).min == -32767);
@@ -295,7 +342,7 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
payload = rhs.payload;
}
///
- @system unittest
+ @safe unittest
{
auto a = checked(42L);
assert(a == 42);
@@ -306,15 +353,17 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
/**
Assignment operator. Has the same constraints as the constructor.
*/
- void opAssign(U)(U rhs) if (is(typeof(Checked!(T, Hook)(rhs))))
+ ref Checked opAssign(U)(U rhs) return
+ if (is(typeof(Checked!(T, Hook)(rhs))))
{
static if (isIntegral!U)
payload = rhs;
else
payload = rhs.payload;
+ return this;
}
///
- @system unittest
+ @safe unittest
{
Checked!long a;
a = 42L;
@@ -323,6 +372,42 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
assert(a == 4242);
}
+ ///
+ @safe unittest
+ {
+ Checked!long a, b;
+ a = b = 3;
+ assert(a == 3 && b == 3);
+ }
+
+ /**
+ Construct from a decimal string. The conversion follows the same rules as
+ $(REF to, std, conv) converting a string to the wrapped `T` type.
+
+ Params:
+ str = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ of characters
+ */
+ this(Range)(Range str)
+ if (isInputRange!Range && isSomeChar!(ElementType!Range))
+ {
+ import std.conv : to;
+
+ this(to!T(str));
+ }
+
+ /**
+ $(REF to, std, conv) can convert a string to a `Checked!T`:
+ */
+ @system unittest
+ {
+ import std.conv : to;
+
+ const a = to!long("1234");
+ const b = to!(Checked!long)("1234");
+ assert(a == b);
+ }
+
// opCast
/**
Casting operator to integral, `bool`, or floating point type. If `Hook`
@@ -379,7 +464,7 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
}
}
///
- @system unittest
+ @safe unittest
{
assert(cast(uint) checked(42) == 42);
assert(cast(uint) checked!WithNaN(-42) == uint.max);
@@ -435,6 +520,8 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
///
static if (is(T == int) && is(Hook == void)) @safe unittest
{
+ import std.traits : isUnsigned;
+
static struct MyHook
{
static bool thereWereErrors;
@@ -470,6 +557,110 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
assert(!MyHook.thereWereErrors);
}
+ // toHash
+ /**
+ Generates a hash for `this`. If `Hook` defines `hookToHash`, the call
+ immediately returns `hook.hookToHash(payload)`. If `Hook` does not
+ implement `hookToHash`, but it has state, a hash will be generated for
+ the `Hook` using the built-in function and it will be xored with the
+ hash of the `payload`.
+ */
+ size_t toHash() const nothrow @safe
+ {
+ static if (hasMember!(Hook, "hookToHash"))
+ {
+ return hook.hookToHash(payload);
+ }
+ else static if (stateSize!Hook > 0)
+ {
+ static if (hasMember!(typeof(payload), "toHash"))
+ {
+ return payload.toHash() ^ hashOf(hook);
+ }
+ else
+ {
+ return hashOf(payload) ^ hashOf(hook);
+ }
+ }
+ else static if (hasMember!(typeof(payload), "toHash"))
+ {
+ return payload.toHash();
+ }
+ else
+ {
+ return .hashOf(payload);
+ }
+ }
+
+ /// ditto
+ size_t toHash(this _)() shared const nothrow @safe
+ {
+ import core.atomic : atomicLoad, MemoryOrder;
+ static if (is(typeof(this.payload.atomicLoad!(MemoryOrder.acq)) P))
+ {
+ auto payload = __ctfe ? cast(P) this.payload
+ : this.payload.atomicLoad!(MemoryOrder.acq);
+ }
+ else
+ {
+ alias payload = this.payload;
+ }
+
+ static if (hasMember!(Hook, "hookToHash"))
+ {
+ return hook.hookToHash(payload);
+ }
+ else static if (stateSize!Hook > 0)
+ {
+ static if (hasMember!(typeof(payload), "toHash"))
+ {
+ return payload.toHash() ^ hashOf(hook);
+ }
+ else
+ {
+ return hashOf(payload) ^ hashOf(hook);
+ }
+ }
+ else static if (hasMember!(typeof(payload), "toHash"))
+ {
+ return payload.toHash();
+ }
+ else
+ {
+ return .hashOf(payload);
+ }
+ }
+
+ /**
+ Writes a string representation of this to a `sink`.
+
+ Params:
+ sink = A `Char` accepting
+ $(REF_ALTTEXT output range, isOutputRange, std,range,primitives).
+ fmt = A $(REF FormatSpec, std, format) which controls how this
+ is formatted.
+ */
+ void toString(Writer, Char)(scope ref Writer sink, scope const ref FormatSpec!Char fmt) const
+ {
+ import std.format.write : formatValue;
+ if (fmt.spec == 's')
+ return formatValue(sink, this, fmt);
+ else
+ return formatValue(sink, payload, fmt);
+ }
+
+ /**
+ `toString` is rarely directly invoked; the usual way of using it is via
+ $(REF format, std, format):
+ */
+ @system unittest
+ {
+ import std.format;
+
+ assert(format("%04d", checked(15)) == "0015");
+ assert(format("0x%02x", checked(15)) == "0x0f");
+ }
+
// opCmp
/**
@@ -536,6 +727,8 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
///
static if (is(T == int) && is(Hook == void)) @safe unittest
{
+ import std.traits : isUnsigned;
+
static struct MyHook
{
static bool thereWereErrors;
@@ -576,7 +769,7 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
}
// For coverage
- static if (is(T == int) && is(Hook == void)) @system unittest
+ static if (is(T == int) && is(Hook == void)) @safe unittest
{
assert(checked(42) <= checked!void(42));
assert(checked!void(42) <= checked(42u));
@@ -787,7 +980,7 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
}
}
- static if (is(T == int) && is(Hook == void)) @system unittest
+ static if (is(T == int) && is(Hook == void)) @safe unittest
{
const a = checked(42);
assert(a + 1 == 43);
@@ -854,7 +1047,7 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
{
bool overflow;
auto r = opChecked!op(lhs, T(payload), overflow);
- if (overflow) r = hook.onOverflow!op(42);
+ if (overflow) r = hook.onOverflow!op(lhs, payload);
return Checked!(typeof(r), Hook)(r);
}
else
@@ -865,7 +1058,7 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
}
}
- static if (is(T == int) && is(Hook == void)) @system unittest
+ static if (is(T == int) && is(Hook == void)) @safe unittest
{
assert(1 + checked(1) == 2);
static uint tally;
@@ -905,6 +1098,10 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
Hook).max) and if `Hook` defines `onUpperBound`, the payload is assigned
from $(D hook.onUpperBound(result, min)).
+ If the right-hand side is also a Checked but with a different hook or
+ underlying type, the hook and underlying type of this Checked takes
+ precedence.
+
In all other cases, the built-in behavior is carried out.
Params:
@@ -953,6 +1150,13 @@ if (isIntegral!T || is(T == Checked!(U, H), U, H))
return this;
}
+ /// ditto
+ ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return
+ if (is(Rhs == Checked!(RhsT, RhsHook), RhsT, RhsHook))
+ {
+ return opOpAssign!(op, typeof(rhs.payload))(rhs.payload);
+ }
+
///
static if (is(T == int) && is(Hook == void)) @safe unittest
{
@@ -994,7 +1198,7 @@ if (is(typeof(Checked!(T, Hook)(value))))
}
///
-@system unittest
+@safe unittest
{
static assert(is(typeof(checked(42)) == Checked!int));
assert(checked(42) == Checked!int(42));
@@ -1014,6 +1218,18 @@ if (is(typeof(Checked!(T, Hook)(value))))
test!(immutable ubyte);
}
+@system unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=21758
+ assert(4 * checked(5L) == 20);
+ assert(20 / checked(5L) == 4);
+ assert(2 ^^ checked(3L) == 8);
+ assert(12 % checked(5L) == 2);
+ assert((0xff & checked(3L)) == 3);
+ assert((0xf0 | checked(3L)) == 0xf3);
+ assert((0xff ^ checked(3L)) == 0xfc);
+}
+
// Abort
/**
@@ -1157,7 +1373,7 @@ static:
}
}
-@system unittest
+@safe unittest
{
void test(T)()
{
@@ -1366,7 +1582,7 @@ default behavior.
*/
struct Warn
{
- import std.stdio : stderr;
+ import std.stdio : writefln;
static:
/**
@@ -1383,7 +1599,7 @@ static:
*/
Dst onBadCast(Dst, Src)(Src src)
{
- stderr.writefln("Erroneous cast: cast(%s) %s(%s)",
+ trustedStderr.writefln("Erroneous cast: cast(%s) %s(%s)",
Dst.stringof, Src.stringof, src);
return cast(Dst) src;
}
@@ -1402,14 +1618,14 @@ static:
*/
Lhs onLowerBound(Rhs, T)(Rhs rhs, T bound)
{
- stderr.writefln("Lower bound error: %s(%s) < %s(%s)",
+ trustedStderr.writefln("Lower bound error: %s(%s) < %s(%s)",
Rhs.stringof, rhs, T.stringof, bound);
return cast(T) rhs;
}
/// ditto
T onUpperBound(Rhs, T)(Rhs rhs, T bound)
{
- stderr.writefln("Upper bound error: %s(%s) > %s(%s)",
+ trustedStderr.writefln("Upper bound error: %s(%s) > %s(%s)",
Rhs.stringof, rhs, T.stringof, bound);
return cast(T) rhs;
}
@@ -1436,7 +1652,7 @@ static:
auto result = opChecked!"=="(lhs, rhs, error);
if (error)
{
- stderr.writefln("Erroneous comparison: %s(%s) == %s(%s)",
+ trustedStderr.writefln("Erroneous comparison: %s(%s) == %s(%s)",
Lhs.stringof, lhs, Rhs.stringof, rhs);
return lhs == rhs;
}
@@ -1444,7 +1660,7 @@ static:
}
///
- @system unittest
+ @safe unittest
{
auto x = checked!Warn(-42);
// Passes
@@ -1475,7 +1691,7 @@ static:
auto result = opChecked!"cmp"(lhs, rhs, error);
if (error)
{
- stderr.writefln("Erroneous ordering comparison: %s(%s) and %s(%s)",
+ trustedStderr.writefln("Erroneous ordering comparison: %s(%s) and %s(%s)",
Lhs.stringof, lhs, Rhs.stringof, rhs);
return lhs < rhs ? -1 : lhs > rhs;
}
@@ -1483,7 +1699,7 @@ static:
}
///
- @system unittest
+ @safe unittest
{
auto x = checked!Warn(-42);
// Passes
@@ -1508,21 +1724,34 @@ static:
*/
typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs)
{
- stderr.writefln("Overflow on unary operator: %s%s(%s)",
+ trustedStderr.writefln("Overflow on unary operator: %s%s(%s)",
x, Lhs.stringof, lhs);
return mixin(x ~ "lhs");
}
/// ditto
typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
{
- stderr.writefln("Overflow on binary operator: %s(%s) %s %s(%s)",
+ trustedStderr.writefln("Overflow on binary operator: %s(%s) %s %s(%s)",
Lhs.stringof, lhs, x, Rhs.stringof, rhs);
- return mixin("lhs" ~ x ~ "rhs");
+ static if (x == "/") // Issue 20743: mixin below would cause SIGFPE on POSIX
+ return typeof(lhs / rhs).min; // or EXCEPTION_INT_OVERFLOW on Windows
+ else
+ return mixin("lhs" ~ x ~ "rhs");
+ }
+
+ // This is safe because we do not assign to the reference returned by
+ // `stderr`. The ability for the caller to do that is why `stderr` is not
+ // safe in the general case.
+ private @property auto ref trustedStderr() @trusted
+ {
+ import std.stdio : stderr;
+
+ return stderr;
}
}
///
-@system unittest
+@safe unittest
{
auto x = checked!Warn(42);
short x1 = cast(short) x;
@@ -1531,6 +1760,39 @@ static:
short y1 = cast(const byte) y;
}
+@system unittest
+{
+ auto a = checked!Warn(int.min);
+ auto b = checked!Warn(-1);
+ auto x = checked!Abort(int.min);
+ auto y = checked!Abort(-1);
+
+ // Temporarily redirect output to stderr to make sure we get the right output.
+ import std.file : exists, remove;
+ import std.process : uniqueTempPath;
+ import std.stdio : stderr;
+ auto tmpname = uniqueTempPath;
+ scope(exit) if (exists(tmpname)) remove(tmpname);
+ auto t = stderr;
+ stderr.open(tmpname, "w");
+ // Open a new scope to minimize code ran with stderr redirected.
+ {
+ scope(exit) stderr = t;
+ assert(a / b == a * b);
+ import std.exception : assertThrown;
+ import core.exception : AssertError;
+ assertThrown!AssertError(x / y);
+ }
+ import std.file : readText;
+ import std.ascii : newline;
+ auto witness = readText(tmpname);
+ auto expected =
+"Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline ~
+"Overflow on binary operator: int(-2147483648) * const(int)(-1)" ~ newline ~
+"Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline;
+ assert(witness == expected, "'" ~ witness ~ "'");
+}
+
// ProperCompare
/**
@@ -1668,7 +1930,7 @@ struct ProperCompare
assert(opCmpProper(42, 42.0) == 0);
assert(opCmpProper(41, 42.0) < 0);
assert(opCmpProper(42, 41.0) > 0);
- import std.math : isNaN;
+ import std.math.traits : isNaN;
assert(isNaN(opCmpProper(41, double.init)));
assert(opCmpProper(42u, 42) == 0);
assert(opCmpProper(42, 42u) == 0);
@@ -1707,9 +1969,9 @@ static:
*/
enum T defaultValue(T) = T.min == 0 ? T.max : T.min;
/**
- The maximum value representable is $(D T.max) for signed integrals, $(D
+ The maximum value representable is `T.max` for signed integrals, $(D
T.max - 1) for unsigned integrals. The minimum value representable is $(D
- T.min + 1) for signed integrals, $(D 0) for unsigned integrals.
+ T.min + 1) for signed integrals, `0` for unsigned integrals.
*/
enum T max(T) = cast(T) (T.min == 0 ? T.max - 1 : T.max);
/// ditto
@@ -1893,8 +2155,8 @@ static:
left-hand side operand. If $(D lhs == WithNaN.defaultValue!Lhs), returns
$(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the
operand. Otherwise, evaluates the operand. If evaluation does not overflow,
- returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs
- + rhs))).
+ returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs +
+ rhs))).
Params:
x = The operator symbol
@@ -1932,8 +2194,8 @@ static:
right-hand side operand. If $(D rhs == WithNaN.defaultValue!Rhs), returns
$(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the
operand. Otherwise, evaluates the operand. If evaluation does not overflow,
- returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs
- + rhs))).
+ returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs +
+ rhs))).
Params:
x = The operator symbol
@@ -2179,7 +2441,7 @@ static:
Returns: The saturated result of the operator.
*/
- typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs)
+ auto onOverflow(string x, Lhs)(Lhs lhs)
{
static assert(x == "-" || x == "++" || x == "--");
return x == "--" ? Lhs.min : Lhs.max;
@@ -2618,7 +2880,7 @@ if (isIntegral!T && T.sizeof >= 4)
testPow!ulong(3, 41);
}
-version (unittest) private struct CountOverflows
+version (StdUnittest) private struct CountOverflows
{
uint calls;
auto onOverflow(string op, Lhs)(Lhs lhs)
@@ -2643,19 +2905,18 @@ version (unittest) private struct CountOverflows
}
}
-version (unittest) private struct CountOpBinary
-{
- uint calls;
- auto hookOpBinary(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs)
- {
- ++calls;
- return mixin("lhs" ~ op ~ "rhs");
- }
-}
-
// opBinary
@nogc nothrow pure @safe unittest
{
+ static struct CountOpBinary
+ {
+ uint calls;
+ auto hookOpBinary(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs)
+ {
+ ++calls;
+ return mixin("lhs" ~ op ~ "rhs");
+ }
+ }
auto x = Checked!(const int, void)(42), y = Checked!(immutable int, void)(142);
assert(x + y == 184);
assert(x + 100 == 142);
@@ -3061,3 +3322,140 @@ version (unittest) private struct CountOpBinary
x = -1;
assert(x > y);
}
+
+@nogc nothrow pure @safe unittest
+{
+ alias cint = Checked!(int, void);
+ cint a = 1, b = 2;
+ a += b;
+ assert(a == cint(3));
+
+ alias ccint = Checked!(cint, Saturate);
+ ccint c = 14;
+ a += c;
+ assert(a == cint(17));
+}
+
+// toHash
+@safe unittest
+{
+ assert(checked(42).toHash() == checked(42).toHash());
+ assert(checked(12).toHash() != checked(19).toHash());
+
+ static struct Hook1
+ {
+ static size_t hookToHash(T)(T payload) nothrow @trusted
+ {
+ static if (size_t.sizeof == 4)
+ {
+ return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF;
+ }
+ else
+ {
+ return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF_FFFF_FFFF;
+ }
+
+ }
+ }
+
+ auto a = checked!Hook1(78);
+ auto b = checked!Hook1(78);
+ assert(a.toHash() == b.toHash());
+
+ assert(checked!Hook1(12).toHash() != checked!Hook1(13).toHash());
+
+ static struct Hook2
+ {
+ static if (size_t.sizeof == 4)
+ {
+ static size_t hashMask = 0xFFFF_0000;
+ }
+ else
+ {
+ static size_t hashMask = 0xFFFF_0000_FFFF_0000;
+ }
+
+ static size_t hookToHash(T)(T payload) nothrow @trusted
+ {
+ return typeid(payload).getHash(&payload) ^ hashMask;
+ }
+ }
+
+ auto x = checked!Hook2(1901);
+ auto y = checked!Hook2(1989);
+
+ assert((() nothrow @safe => x.toHash() == x.toHash())());
+
+ assert(x.toHash() == x.toHash());
+ assert(x.toHash() != y.toHash());
+ assert(checked!Hook1(1901).toHash() != x.toHash());
+
+ immutable z = checked!Hook1(1901);
+ immutable t = checked!Hook1(1901);
+ immutable w = checked!Hook2(1901);
+
+ assert(z.toHash() == t.toHash());
+ assert(z.toHash() != x.toHash());
+ assert(z.toHash() != w.toHash());
+
+ const long c = 0xF0F0F0F0;
+ const long d = 0xF0F0F0F0;
+
+ assert(checked!Hook1(c).toHash() != checked!Hook2(c));
+ assert(checked!Hook1(c).toHash() != checked!Hook1(d));
+
+ // Hook with state, does not implement hookToHash
+ static struct Hook3
+ {
+ ulong var1 = ulong.max;
+ uint var2 = uint.max;
+ }
+
+ assert(checked!Hook3(12).toHash() != checked!Hook3(13).toHash());
+ assert(checked!Hook3(13).toHash() == checked!Hook3(13).toHash());
+
+ // Hook with no state and no hookToHash, payload has its own hashing function
+ auto x1 = Checked!(Checked!int, ProperCompare)(123);
+ auto x2 = Checked!(Checked!int, ProperCompare)(123);
+ auto x3 = Checked!(Checked!int, ProperCompare)(144);
+
+ assert(x1.toHash() == x2.toHash());
+ assert(x1.toHash() != x3.toHash());
+ assert(x2.toHash() != x3.toHash());
+
+ // Check shared.
+ {
+ shared shared0 = checked(12345678);
+ shared shared1 = checked!Hook1(123456789);
+ shared shared2 = checked!Hook2(234567891);
+ shared shared3 = checked!Hook3(345678912);
+ assert(shared0.toHash() == hashOf(shared0));
+ assert(shared1.toHash() == hashOf(shared1));
+ assert(shared2.toHash() == hashOf(shared2));
+ assert(shared3.toHash() == hashOf(shared3));
+ }
+}
+
+///
+@safe unittest
+{
+ struct MyHook
+ {
+ static size_t hookToHash(T)(const T payload) nothrow @trusted
+ {
+ return .hashOf(payload);
+ }
+ }
+
+ int[Checked!(int, MyHook)] aa;
+ Checked!(int, MyHook) var = 42;
+ aa[var] = 100;
+
+ assert(aa[var] == 100);
+
+ int[Checked!(int, Abort)] bb;
+ Checked!(int, Abort) var2 = 42;
+ bb[var2] = 100;
+
+ assert(bb[var2] == 100);
+}
diff --git a/libphobos/src/std/experimental/logger/core.d b/libphobos/src/std/experimental/logger/core.d
index 2f857c6da7d..08d6cbede2d 100644
--- a/libphobos/src/std/experimental/logger/core.d
+++ b/libphobos/src/std/experimental/logger/core.d
@@ -1,4 +1,7 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/logger/core.d)
+*/
module std.experimental.logger.core;
import core.sync.mutex : Mutex;
@@ -9,9 +12,9 @@ import std.traits;
import std.experimental.logger.filelogger;
-/** This template evaluates if the passed $(D LogLevel) is active.
+/** This template evaluates if the passed `LogLevel` is active.
The previously described version statements are used to decide if the
-$(D LogLevel) is active. The version statements only influence the compile
+`LogLevel` is active. The version statements only influence the compile
unit they are used with, therefore this function can only disable logging this
specific compile unit.
*/
@@ -56,10 +59,10 @@ template isLoggingActiveAt(LogLevel ll)
}
}
-/// This compile-time flag is $(D true) if logging is not statically disabled.
+/// This compile-time flag is `true` if logging is not statically disabled.
enum isLoggingActive = isLoggingActiveAt!(LogLevel.all);
-/** This functions is used at runtime to determine if a $(D LogLevel) is
+/** This functions is used at runtime to determine if a `LogLevel` is
active. The same previously defined version statements are used to disable
certain levels. Again the version statements are associated with a compile
unit and can therefore not disable logging in other compile units.
@@ -96,19 +99,19 @@ bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL,
&& condition;
}
-/** This template returns the $(D LogLevel) named "logLevel" of type $(D
+/** This template returns the `LogLevel` named "logLevel" of type $(D
LogLevel) defined in a user defined module where the filename has the
-suffix "_loggerconfig.d". This $(D LogLevel) sets the minimal $(D LogLevel)
+suffix "_loggerconfig.d". This `LogLevel` sets the minimal `LogLevel`
of the module.
-A minimal $(D LogLevel) can be defined on a per module basis.
-In order to define a module $(D LogLevel) a file with a modulename
+A minimal `LogLevel` can be defined on a per module basis.
+In order to define a module `LogLevel` a file with a modulename
"MODULENAME_loggerconfig" must be found. If no such module exists and the
module is a nested module, it is checked if there exists a
"PARENT_MODULE_loggerconfig" module with such a symbol.
-If this module exists and it contains a $(D LogLevel) called logLevel this $(D
+If this module exists and it contains a `LogLevel` called logLevel this $(D
LogLevel) will be used. This parent lookup is continued until there is no
-parent module. Then the moduleLogLevel is $(D LogLevel.all).
+parent module. Then the moduleLogLevel is `LogLevel.all`.
*/
template moduleLogLevel(string moduleName)
if (!moduleName.length)
@@ -156,16 +159,16 @@ private string parentOf(string mod)
return null;
}
-/* This function formates a $(D SysTime) into an $(D OutputRange).
+/* This function formates a `SysTime` into an `OutputRange`.
-The $(D SysTime) is formatted similar to
+The `SysTime` is formatted similar to
$(LREF std.datatime.DateTime.toISOExtString) except the fractional second part.
The fractional second part is in milliseconds and is always 3 digits.
*/
void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time)
if (isOutputRange!(OutputRange,string))
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
const auto dt = cast(DateTime) time;
const auto fsec = time.fracSecs.total!"msecs";
@@ -177,13 +180,13 @@ if (isOutputRange!(OutputRange,string))
/** This function logs data.
-In order for the data to be processed, the $(D LogLevel) of the log call must
-be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the
-$(D defaultLogLevel); additionally the condition passed must be $(D true).
+In order for the data to be processed, the `LogLevel` of the log call must
+be greater or equal to the `LogLevel` of the `sharedLog` and the
+`defaultLogLevel`; additionally the condition passed must be `true`.
Params:
- ll = The $(D LogLevel) used by this log call.
- condition = The condition must be $(D true) for the data to be logged.
+ ll = The `LogLevel` used by this log call.
+ condition = The condition must be `true` for the data to be logged.
args = The data that should be logged.
Example:
@@ -224,11 +227,11 @@ void log(T, string moduleName = __MODULE__)(const LogLevel ll,
/** This function logs data.
-In order for the data to be processed the $(D LogLevel) of the log call must
-be greater or equal to the $(D LogLevel) of the $(D sharedLog).
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `LogLevel` of the `sharedLog`.
Params:
- ll = The $(D LogLevel) used by this log call.
+ ll = The `LogLevel` used by this log call.
args = The data that should be logged.
Example:
@@ -268,12 +271,12 @@ void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy T arg,
/** This function logs data.
-In order for the data to be processed the $(D LogLevel) of the
-$(D sharedLog) must be greater or equal to the $(D defaultLogLevel)
-add the condition passed must be $(D true).
+In order for the data to be processed the `LogLevel` of the
+`sharedLog` must be greater or equal to the `defaultLogLevel`
+add the condition passed must be `true`.
Params:
- condition = The condition must be $(D true) for the data to be logged.
+ condition = The condition must be `true` for the data to be logged.
args = The data that should be logged.
Example:
@@ -307,8 +310,8 @@ void log(T, string moduleName = __MODULE__)(lazy bool condition, lazy T arg,
/** This function logs data.
-In order for the data to be processed the $(D LogLevel) of the
-$(D sharedLog) must be greater or equal to the $(D defaultLogLevel).
+In order for the data to be processed the `LogLevel` of the
+`sharedLog` must be greater or equal to the `defaultLogLevel`.
Params:
args = The data that should be logged.
@@ -343,16 +346,16 @@ void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
}
}
-/** This function logs data in a $(D printf)-style manner.
+/** This function logs data in a `printf`-style manner.
-In order for the data to be processed the $(D LogLevel) of the log call must
-be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the
-$(D defaultLogLevel) additionally the condition passed must be $(D true).
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `LogLevel` of the `sharedLog` and the
+`defaultLogLevel` additionally the condition passed must be `true`.
Params:
- ll = The $(D LogLevel) used by this log call.
- condition = The condition must be $(D true) for the data to be logged.
- msg = The $(D printf)-style string.
+ ll = The `LogLevel` used by this log call.
+ condition = The condition must be `true` for the data to be logged.
+ msg = The `printf`-style string.
args = The data that should be logged.
Example:
@@ -376,15 +379,15 @@ void logf(int line = __LINE__, string file = __FILE__,
}
}
-/** This function logs data in a $(D printf)-style manner.
+/** This function logs data in a `printf`-style manner.
-In order for the data to be processed the $(D LogLevel) of the log call must
-be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the
-$(D defaultLogLevel).
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `LogLevel` of the `sharedLog` and the
+`defaultLogLevel`.
Params:
- ll = The $(D LogLevel) used by this log call.
- msg = The $(D printf)-style string.
+ ll = The `LogLevel` used by this log call.
+ msg = The `printf`-style string.
args = The data that should be logged.
Example:
@@ -407,15 +410,15 @@ void logf(int line = __LINE__, string file = __FILE__,
}
}
-/** This function logs data in a $(D printf)-style manner.
+/** This function logs data in a `printf`-style manner.
-In order for the data to be processed the $(D LogLevel) of the log call must
-be greater or equal to the $(D defaultLogLevel) additionally the condition
-passed must be $(D true).
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `defaultLogLevel` additionally the condition
+passed must be `true`.
Params:
- condition = The condition must be $(D true) for the data to be logged.
- msg = The $(D printf)-style string.
+ condition = The condition must be `true` for the data to be logged.
+ msg = The `printf`-style string.
args = The data that should be logged.
Example:
@@ -435,13 +438,13 @@ void logf(int line = __LINE__, string file = __FILE__,
}
}
-/** This function logs data in a $(D printf)-style manner.
+/** This function logs data in a `printf`-style manner.
-In order for the data to be processed the $(D LogLevel) of the log call must
-be greater or equal to the $(D defaultLogLevel).
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `defaultLogLevel`.
Params:
- msg = The $(D printf)-style string.
+ msg = The `printf`-style string.
args = The data that should be logged.
Example:
@@ -461,7 +464,7 @@ void logf(int line = __LINE__, string file = __FILE__,
}
}
-/** This template provides the global log functions with the $(D LogLevel)
+/** This template provides the global log functions with the `LogLevel`
is encoded in the function name.
The aliases following this template create the public names of these log
@@ -495,18 +498,18 @@ template defaultLogFunction(LogLevel ll)
}
}
-/** This function logs data to the $(D stdThreadLocalLog), optionally depending
+/** This function logs data to the `stdThreadLocalLog`, optionally depending
on a condition.
-In order for the resulting log message to be logged the $(D LogLevel) must
-be greater or equal than the $(D LogLevel) of the $(D stdThreadLocalLog) and
-must be greater or equal than the global $(D LogLevel).
-Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel)
-of the $(D stdSharedLogger).
-If a condition is given, it must evaluate to $(D true).
+In order for the resulting log message to be logged the `LogLevel` must
+be greater or equal than the `LogLevel` of the `stdThreadLocalLog` and
+must be greater or equal than the global `LogLevel`.
+Additionally the `LogLevel` must be greater or equal than the `LogLevel`
+of the `stdSharedLogger`.
+If a condition is given, it must evaluate to `true`.
Params:
- condition = The condition must be $(D true) for the data to be logged.
+ condition = The condition must be `true` for the data to be logged.
args = The data that should be logged.
Example:
@@ -535,8 +538,8 @@ alias critical = defaultLogFunction!(LogLevel.critical);
/// Ditto
alias fatal = defaultLogFunction!(LogLevel.fatal);
-/** This template provides the global $(D printf)-style log functions with
-the $(D LogLevel) is encoded in the function name.
+/** This template provides the global `printf`-style log functions with
+the `LogLevel` is encoded in the function name.
The aliases following this template create the public names of the log
functions.
@@ -569,17 +572,17 @@ template defaultLogFunctionf(LogLevel ll)
}
}
-/** This function logs data to the $(D sharedLog) in a $(D printf)-style
+/** This function logs data to the `sharedLog` in a `printf`-style
manner.
-In order for the resulting log message to be logged the $(D LogLevel) must
-be greater or equal than the $(D LogLevel) of the $(D sharedLog) and
-must be greater or equal than the global $(D LogLevel).
-Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel)
-of the $(D stdSharedLogger).
+In order for the resulting log message to be logged the `LogLevel` must
+be greater or equal than the `LogLevel` of the `sharedLog` and
+must be greater or equal than the global `LogLevel`.
+Additionally the `LogLevel` must be greater or equal than the `LogLevel`
+of the `stdSharedLogger`.
Params:
- msg = The $(D printf)-style string.
+ msg = The `printf`-style string.
args = The data that should be logged.
Example:
@@ -591,18 +594,18 @@ criticalf("is number %d", 4);
fatalf("is number %d", 5);
--------------------
-The second version of the function logs data to the $(D sharedLog) in a $(D
+The second version of the function logs data to the `sharedLog` in a $(D
printf)-style manner.
-In order for the resulting log message to be logged the $(D LogLevel) must
-be greater or equal than the $(D LogLevel) of the $(D sharedLog) and
-must be greater or equal than the global $(D LogLevel).
-Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel)
-of the $(D stdSharedLogger).
+In order for the resulting log message to be logged the `LogLevel` must
+be greater or equal than the `LogLevel` of the `sharedLog` and
+must be greater or equal than the global `LogLevel`.
+Additionally the `LogLevel` must be greater or equal than the `LogLevel`
+of the `stdSharedLogger`.
Params:
- condition = The condition must be $(D true) for the data to be logged.
- msg = The $(D printf)-style string.
+ condition = The condition must be `true` for the data to be logged.
+ msg = The `printf`-style string.
args = The data that should be logged.
Example:
@@ -654,7 +657,7 @@ private struct MsgRange
private void formatString(A...)(MsgRange oRange, A args)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
foreach (arg; args)
{
@@ -677,13 +680,13 @@ private void formatString(A...)(MsgRange oRange, A args)
/**
There are eight usable logging level. These level are $(I all), $(I trace),
$(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off).
-If a log function with $(D LogLevel.fatal) is called the shutdown handler of
+If a log function with `LogLevel.fatal` is called the shutdown handler of
that logger is called.
*/
enum LogLevel : ubyte
{
- all = 1, /** Lowest possible assignable $(D LogLevel). */
- trace = 32, /** $(D LogLevel) for tracing the execution of the program. */
+ all = 1, /** Lowest possible assignable `LogLevel`. */
+ trace = 32, /** `LogLevel` for tracing the execution of the program. */
info = 64, /** This level is used to display information about the
program. */
warning = 96, /** warnings about the program should be displayed with this
@@ -694,15 +697,15 @@ enum LogLevel : ubyte
logged with this level. */
fatal = 192, /** Log messages that describe fatal errors should use this
level. */
- off = ubyte.max /** Highest possible $(D LogLevel). */
+ off = ubyte.max /** Highest possible `LogLevel`. */
}
/** This class is the base of every logger. In order to create a new kind of
-logger a deriving class needs to implement the $(D writeLogMsg) method. By
+logger a deriving class needs to implement the `writeLogMsg` method. By
default this is not thread-safe.
-It is also possible to $(D override) the three methods $(D beginLogMsg),
-$(D logMsgPart) and $(D finishLogMsg) together, this option gives more
+It is also possible to `override` the three methods `beginLogMsg`,
+`logMsgPart` and `finishLogMsg` together, this option gives more
flexibility.
*/
abstract class Logger
@@ -727,7 +730,7 @@ abstract class Logger
string prettyFuncName;
/// the name of the module the log message is coming from
string moduleName;
- /// the $(D LogLevel) associated with the log message
+ /// the `LogLevel` associated with the log message
LogLevel logLevel;
/// thread id of the log message
Tid threadId;
@@ -735,7 +738,7 @@ abstract class Logger
SysTime timestamp;
/// the message of the log message
string msg;
- /// A refernce to the $(D Logger) used to create this $(D LogEntry)
+ /// A refernce to the `Logger` used to create this `LogEntry`
Logger logger;
}
@@ -759,7 +762,7 @@ abstract class Logger
}
/** A custom logger must implement this method in order to work in a
- $(D MultiLogger) and $(D ArrayLogger).
+ `MultiLogger` and `ArrayLogger`.
Params:
payload = All information associated with call to log function.
@@ -768,14 +771,14 @@ abstract class Logger
*/
abstract protected void writeLogMsg(ref LogEntry payload) @safe;
- /* The default implementation will use an $(D std.array.appender)
+ /* The default implementation will use an `std.array.appender`
internally to construct the message string. This means dynamic,
GC memory allocation. A logger can avoid this allocation by
- reimplementing $(D beginLogMsg), $(D logMsgPart) and $(D finishLogMsg).
- $(D beginLogMsg) is always called first, followed by any number of calls
- to $(D logMsgPart) and one call to $(D finishLogMsg).
+ reimplementing `beginLogMsg`, `logMsgPart` and `finishLogMsg`.
+ `beginLogMsg` is always called first, followed by any number of calls
+ to `logMsgPart` and one call to `finishLogMsg`.
- As an example for such a custom $(D Logger) compare this:
+ As an example for such a custom `Logger` compare this:
----------------
class CLogger : Logger
{
@@ -822,7 +825,7 @@ abstract class Logger
}
/** Logs a part of the log message. */
- protected void logMsgPart(const(char)[] msg) @safe
+ protected void logMsgPart(scope const(char)[] msg) @safe
{
static if (isLoggingActive)
{
@@ -831,7 +834,7 @@ abstract class Logger
}
/** Signals that the message has been written and no more calls to
- $(D logMsgPart) follow. */
+ `logMsgPart` follow. */
protected void finishLogMsg() @safe
{
static if (isLoggingActive)
@@ -841,12 +844,12 @@ abstract class Logger
}
}
- /** The $(D LogLevel) determines if the log call are processed or dropped
- by the $(D Logger). In order for the log call to be processed the
- $(D LogLevel) of the log call must be greater or equal to the $(D LogLevel)
- of the $(D logger).
+ /** The `LogLevel` determines if the log call are processed or dropped
+ by the `Logger`. In order for the log call to be processed the
+ `LogLevel` of the log call must be greater or equal to the `LogLevel`
+ of the `logger`.
- These two methods set and get the $(D LogLevel) of the used $(D Logger).
+ These two methods set and get the `LogLevel` of the used `Logger`.
Example:
-----------
@@ -866,10 +869,10 @@ abstract class Logger
synchronized (mutex) this.logLevel_ = lv;
}
- /** This $(D delegate) is called in case a log message with
- $(D LogLevel.fatal) gets logged.
+ /** This `delegate` is called in case a log message with
+ `LogLevel.fatal` gets logged.
- By default an $(D Error) will be thrown.
+ By default an `Error` will be thrown.
*/
@property final void delegate() fatalHandler() @safe @nogc
{
@@ -884,10 +887,10 @@ abstract class Logger
/** This method allows forwarding log entries from one logger to another.
- $(D forwardMsg) will ensure proper synchronization and then call
- $(D writeLogMsg). This is an API for implementing your own loggers and
+ `forwardMsg` will ensure proper synchronization and then call
+ `writeLogMsg`. This is an API for implementing your own loggers and
should not be called by normal user code. A notable difference from other
- logging functions is that the $(D globalLogLevel) wont be evaluated again
+ logging functions is that the `globalLogLevel` wont be evaluated again
since it is assumed that the caller already checked that.
*/
void forwardMsg(ref LogEntry payload) @trusted
@@ -905,8 +908,8 @@ abstract class Logger
}
}
- /** This template provides the log functions for the $(D Logger) $(D class)
- with the $(D LogLevel) encoded in the function name.
+ /** This template provides the log functions for the `Logger` `class`
+ with the `LogLevel` encoded in the function name.
For further information see the the two functions defined inside of this
template.
@@ -916,11 +919,11 @@ abstract class Logger
*/
template memLogFunctions(LogLevel ll)
{
- /** This function logs data to the used $(D Logger).
+ /** This function logs data to the used `Logger`.
- In order for the resulting log message to be logged the $(D LogLevel)
- must be greater or equal than the $(D LogLevel) of the used $(D Logger)
- and must be greater or equal than the global $(D LogLevel).
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel`.
Params:
args = The data that should be logged.
@@ -960,16 +963,16 @@ abstract class Logger
}
}
- /** This function logs data to the used $(D Logger) depending on a
+ /** This function logs data to the used `Logger` depending on a
condition.
- In order for the resulting log message to be logged the $(D LogLevel) must
- be greater or equal than the $(D LogLevel) of the used $(D Logger) and
- must be greater or equal than the global $(D LogLevel) additionally the
- condition passed must be $(D true).
+ In order for the resulting log message to be logged the `LogLevel` must
+ be greater or equal than the `LogLevel` of the used `Logger` and
+ must be greater or equal than the global `LogLevel` additionally the
+ condition passed must be `true`.
Params:
- condition = The condition must be $(D true) for the data to be logged.
+ condition = The condition must be `true` for the data to be logged.
args = The data that should be logged.
Example:
@@ -1008,17 +1011,17 @@ abstract class Logger
}
}
- /** This function logs data to the used $(D Logger) in a
- $(D printf)-style manner.
+ /** This function logs data to the used `Logger` in a
+ `printf`-style manner.
- In order for the resulting log message to be logged the $(D LogLevel)
- must be greater or equal than the $(D LogLevel) of the used $(D Logger)
- and must be greater or equal than the global $(D LogLevel) additionally
- the passed condition must be $(D true).
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel` additionally
+ the passed condition must be `true`.
Params:
- condition = The condition must be $(D true) for the data to be logged.
- msg = The $(D printf)-style string.
+ condition = The condition must be `true` for the data to be logged.
+ msg = The `printf`-style string.
args = The data that should be logged.
Example:
@@ -1040,7 +1043,7 @@ abstract class Logger
static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
synchronized (mutex)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
condition))
@@ -1059,15 +1062,15 @@ abstract class Logger
}
}
- /** This function logs data to the used $(D Logger) in a
- $(D printf)-style manner.
+ /** This function logs data to the used `Logger` in a
+ `printf`-style manner.
- In order for the resulting log message to be logged the $(D LogLevel) must
- be greater or equal than the $(D LogLevel) of the used $(D Logger) and
- must be greater or equal than the global $(D LogLevel).
+ In order for the resulting log message to be logged the `LogLevel` must
+ be greater or equal than the `LogLevel` of the used `Logger` and
+ must be greater or equal than the global `LogLevel`.
Params:
- msg = The $(D printf)-style string.
+ msg = The `printf`-style string.
args = The data that should be logged.
Example:
@@ -1088,7 +1091,7 @@ abstract class Logger
static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
synchronized (mutex)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
{
@@ -1132,15 +1135,15 @@ abstract class Logger
/// Ditto
alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf;
- /** This method logs data with the $(D LogLevel) of the used $(D Logger).
+ /** This method logs data with the `LogLevel` of the used `Logger`.
- This method takes a $(D bool) as first argument. In order for the
- data to be processed the $(D bool) must be $(D true) and the $(D LogLevel)
- of the Logger must be greater or equal to the global $(D LogLevel).
+ This method takes a `bool` as first argument. In order for the
+ data to be processed the `bool` must be `true` and the `LogLevel`
+ of the Logger must be greater or equal to the global `LogLevel`.
Params:
args = The data that should be logged.
- condition = The condition must be $(D true) for the data to be logged.
+ condition = The condition must be `true` for the data to be logged.
args = The data that is to be logged.
Returns: The logger used by the logging function as reference.
@@ -1200,15 +1203,15 @@ abstract class Logger
}
}
- /** This function logs data to the used $(D Logger) with a specific
- $(D LogLevel).
+ /** This function logs data to the used `Logger` with a specific
+ `LogLevel`.
- In order for the resulting log message to be logged the $(D LogLevel)
- must be greater or equal than the $(D LogLevel) of the used $(D Logger)
- and must be greater or equal than the global $(D LogLevel).
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel`.
Params:
- ll = The specific $(D LogLevel) used for logging the log message.
+ ll = The specific `LogLevel` used for logging the log message.
args = The data that should be logged.
Example:
@@ -1268,16 +1271,16 @@ abstract class Logger
}
}
- /** This function logs data to the used $(D Logger) depending on a
- explicitly passed condition with the $(D LogLevel) of the used
- $(D Logger).
+ /** This function logs data to the used `Logger` depending on a
+ explicitly passed condition with the `LogLevel` of the used
+ `Logger`.
- In order for the resulting log message to be logged the $(D LogLevel)
- of the used $(D Logger) must be greater or equal than the global
- $(D LogLevel) and the condition must be $(D true).
+ In order for the resulting log message to be logged the `LogLevel`
+ of the used `Logger` must be greater or equal than the global
+ `LogLevel` and the condition must be `true`.
Params:
- condition = The condition must be $(D true) for the data to be logged.
+ condition = The condition must be `true` for the data to be logged.
args = The data that should be logged.
Example:
@@ -1339,12 +1342,12 @@ abstract class Logger
}
}
- /** This function logs data to the used $(D Logger) with the $(D LogLevel)
- of the used $(D Logger).
+ /** This function logs data to the used `Logger` with the `LogLevel`
+ of the used `Logger`.
- In order for the resulting log message to be logged the $(D LogLevel)
- of the used $(D Logger) must be greater or equal than the global
- $(D LogLevel).
+ In order for the resulting log message to be logged the `LogLevel`
+ of the used `Logger` must be greater or equal than the global
+ `LogLevel`.
Params:
args = The data that should be logged.
@@ -1365,7 +1368,7 @@ abstract class Logger
string moduleName = __MODULE__, A...)(lazy A args)
if ((args.length > 1
&& !is(Unqual!(A[0]) : bool)
- && !is(Unqual!(A[0]) == LogLevel))
+ && !is(immutable A[0] == immutable LogLevel))
|| args.length == 0)
{
static if (isLoggingActive) synchronized (mutex)
@@ -1409,17 +1412,17 @@ abstract class Logger
}
}
- /** This function logs data to the used $(D Logger) with a specific
- $(D LogLevel) and depending on a condition in a $(D printf)-style manner.
+ /** This function logs data to the used `Logger` with a specific
+ `LogLevel` and depending on a condition in a `printf`-style manner.
- In order for the resulting log message to be logged the $(D LogLevel)
- must be greater or equal than the $(D LogLevel) of the used $(D Logger)
- and must be greater or equal than the global $(D LogLevel) and the
- condition must be $(D true).
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel` and the
+ condition must be `true`.
Params:
- ll = The specific $(D LogLevel) used for logging the log message.
- condition = The condition must be $(D true) for the data to be logged.
+ ll = The specific `LogLevel` used for logging the log message.
+ condition = The condition must be `true` for the data to be logged.
msg = The format string used for this log call.
args = The data that should be logged.
@@ -1441,7 +1444,7 @@ abstract class Logger
{
static if (isLoggingActive) synchronized (mutex)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
{
@@ -1459,15 +1462,15 @@ abstract class Logger
}
}
- /** This function logs data to the used $(D Logger) with a specific
- $(D LogLevel) in a $(D printf)-style manner.
+ /** This function logs data to the used `Logger` with a specific
+ `LogLevel` in a `printf`-style manner.
- In order for the resulting log message to be logged the $(D LogLevel)
- must be greater or equal than the $(D LogLevel) of the used $(D Logger)
- and must be greater or equal than the global $(D LogLevel).
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel`.
Params:
- ll = The specific $(D LogLevel) used for logging the log message.
+ ll = The specific `LogLevel` used for logging the log message.
msg = The format string used for this log call.
args = The data that should be logged.
@@ -1489,7 +1492,7 @@ abstract class Logger
{
static if (isLoggingActive) synchronized (mutex)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
{
@@ -1507,16 +1510,16 @@ abstract class Logger
}
}
- /** This function logs data to the used $(D Logger) depending on a
- condition with the $(D LogLevel) of the used $(D Logger) in a
- $(D printf)-style manner.
+ /** This function logs data to the used `Logger` depending on a
+ condition with the `LogLevel` of the used `Logger` in a
+ `printf`-style manner.
- In order for the resulting log message to be logged the $(D LogLevel)
- of the used $(D Logger) must be greater or equal than the global
- $(D LogLevel) and the condition must be $(D true).
+ In order for the resulting log message to be logged the `LogLevel`
+ of the used `Logger` must be greater or equal than the global
+ `LogLevel` and the condition must be `true`.
Params:
- condition = The condition must be $(D true) for the data to be logged.
+ condition = The condition must be `true` for the data to be logged.
msg = The format string used for this log call.
args = The data that should be logged.
@@ -1538,7 +1541,7 @@ abstract class Logger
{
static if (isLoggingActive) synchronized (mutex)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
condition))
@@ -1557,11 +1560,11 @@ abstract class Logger
}
}
- /** This method logs data to the used $(D Logger) with the $(D LogLevel)
- of the this $(D Logger) in a $(D printf)-style manner.
+ /** This method logs data to the used `Logger` with the `LogLevel`
+ of the this `Logger` in a `printf`-style manner.
- In order for the data to be processed the $(D LogLevel) of the $(D Logger)
- must be greater or equal to the global $(D LogLevel).
+ In order for the data to be processed the `LogLevel` of the `Logger`
+ must be greater or equal to the global `LogLevel`.
Params:
msg = The format string used for this log call.
@@ -1584,7 +1587,7 @@ abstract class Logger
{
static if (isLoggingActive) synchronized (mutex)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
if (isLoggingEnabled(this.logLevel_, this.logLevel_,
globalLogLevel))
@@ -1622,10 +1625,10 @@ private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all;
*/
private @property Logger defaultSharedLoggerImpl() @trusted
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
import std.stdio : stderr;
- static __gshared align(FileLogger.alignof) void[__traits(classInstanceSize, FileLogger)] _buffer;
+ __gshared align(FileLogger.alignof) void[__traits(classInstanceSize, FileLogger)] _buffer;
import std.concurrency : initOnce;
initOnce!stdSharedDefaultLogger({
@@ -1636,25 +1639,25 @@ private @property Logger defaultSharedLoggerImpl() @trusted
return stdSharedDefaultLogger;
}
-/** This property sets and gets the default $(D Logger).
+/** This property sets and gets the default `Logger`.
Example:
-------------
sharedLog = new FileLogger(yourFile);
-------------
-The example sets a new $(D FileLogger) as new $(D sharedLog).
+The example sets a new `FileLogger` as new `sharedLog`.
If at some point you want to use the original default logger again, you can
use $(D sharedLog = null;). This will put back the original.
Note:
-While getting and setting $(D sharedLog) is thread-safe, it has to be considered
+While getting and setting `sharedLog` is thread-safe, it has to be considered
that the returned reference is only a current snapshot and in the following
code, you must make sure no other thread reassigns to it between reading and
-writing $(D sharedLog).
+writing `sharedLog`.
-$(D sharedLog) is only thread-safe if the the used $(D Logger) is thread-safe.
-The default $(D Logger) is thread-safe.
+`sharedLog` is only thread-safe if the the used `Logger` is thread-safe.
+The default `Logger` is thread-safe.
-------------
if (sharedLog !is myLogger)
sharedLog = new myLogger;
@@ -1688,11 +1691,11 @@ if (sharedLog !is myLogger)
atomicStore!(MemoryOrder.rel)(stdSharedLogger, cast(shared) logger);
}
-/** This methods get and set the global $(D LogLevel).
+/** This methods get and set the global `LogLevel`.
-Every log message with a $(D LogLevel) lower as the global $(D LogLevel)
-will be discarded before it reaches $(D writeLogMessage) method of any
-$(D Logger).
+Every log message with a `LogLevel` lower as the global `LogLevel`
+will be discarded before it reaches `writeLogMessage` method of any
+`Logger`.
*/
/* Implementation note:
For any public logging call, the global log level shall only be queried once on
@@ -1712,18 +1715,18 @@ different levels at different spots in the code.
// Thread Local
-/** The $(D StdForwardLogger) will always forward anything to the sharedLog.
+/** The `StdForwardLogger` will always forward anything to the sharedLog.
-The $(D StdForwardLogger) will not throw if data is logged with $(D
+The `StdForwardLogger` will not throw if data is logged with $(D
LogLevel.fatal).
*/
class StdForwardLogger : Logger
{
- /** The default constructor for the $(D StdForwardLogger).
+ /** The default constructor for the `StdForwardLogger`.
Params:
- lv = The $(D LogLevel) for the $(D MultiLogger). By default the $(D
- LogLevel) is $(D all).
+ lv = The `LogLevel` for the `MultiLogger`. By default the $(D
+ LogLevel) is `all`.
*/
this(const LogLevel lv = LogLevel.all) @safe
{
@@ -1743,9 +1746,9 @@ class StdForwardLogger : Logger
auto nl1 = new StdForwardLogger(LogLevel.all);
}
-/** This $(D LogLevel) is unqiue to every thread.
+/** This `LogLevel` is unqiue to every thread.
-The thread local $(D Logger) will use this $(D LogLevel) to filter log calls
+The thread local `Logger` will use this `LogLevel` to filter log calls
every same way as presented earlier.
*/
//public LogLevel threadLogLevel = LogLevel.all;
@@ -1756,7 +1759,7 @@ private Logger stdLoggerDefaultThreadLogger;
*/
private @property Logger stdThreadLocalLogImpl() @trusted
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
static void*[(__traits(classInstanceSize, StdForwardLogger) - 1) / (void*).sizeof + 1] _buffer;
@@ -1769,14 +1772,14 @@ private @property Logger stdThreadLocalLogImpl() @trusted
return stdLoggerDefaultThreadLogger;
}
-/** This function returns a thread unique $(D Logger), that by default
-propergates all data logged to it to the $(D sharedLog).
+/** This function returns a thread unique `Logger`, that by default
+propergates all data logged to it to the `sharedLog`.
-These properties can be used to set and get this $(D Logger). Every
-modification to this $(D Logger) will only be visible in the thread the
+These properties can be used to set and get this `Logger`. Every
+modification to this `Logger` will only be visible in the thread the
modification has been done from.
-This $(D Logger) is called by the free standing log functions. This allows to
+This `Logger` is called by the free standing log functions. This allows to
create thread local redirections and still use the free standing log
functions.
*/
@@ -1843,7 +1846,7 @@ package class TestLogger : Logger
}
}
-version (unittest) private void testFuncNames(Logger logger) @safe
+version (StdUnittest) private void testFuncNames(Logger logger) @safe
{
string s = "I'm here";
logger.log(s);
@@ -3068,7 +3071,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted
stdThreadLocalLog.logLevel = LogLevel.all;
}
-// Issue 14940
+// https://issues.dlang.org/show_bug.cgi?id=14940
@safe unittest
{
import std.typecons : Nullable;
@@ -3098,7 +3101,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted
assert(tl.msg == SystemToStringMsg);
}
-// Issue 17328
+// https://issues.dlang.org/show_bug.cgi?id=17328
@safe unittest
{
import std.format : format;
@@ -3120,7 +3123,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted
assert(fs.length == 2);
}
-// Issue 15954
+// https://issues.dlang.org/show_bug.cgi?id=15954
@safe unittest
{
import std.conv : to;
@@ -3129,7 +3132,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted
assert(tl.msg == "123456789");
}
-// Issue 16256
+// https://issues.dlang.org/show_bug.cgi?id=16256
@safe unittest
{
import std.conv : to;
@@ -3138,14 +3141,15 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted
assert(tl.msg == "123456789");
}
-// Issue 15517
+// https://issues.dlang.org/show_bug.cgi?id=15517
@system unittest
{
- import std.file : exists, remove;
+ import std.file : exists, remove, tempDir;
+ import std.path : buildPath;
import std.stdio : File;
import std.string : indexOf;
- string fn = "logfile.log";
+ string fn = tempDir.buildPath("logfile.log");
if (exists(fn))
{
remove(fn);
diff --git a/libphobos/src/std/experimental/logger/filelogger.d b/libphobos/src/std/experimental/logger/filelogger.d
index 8f97b5ba7ea..6fd7e5ff66b 100644
--- a/libphobos/src/std/experimental/logger/filelogger.d
+++ b/libphobos/src/std/experimental/logger/filelogger.d
@@ -1,4 +1,7 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/logger/filelogger.d)
+*/
module std.experimental.logger.filelogger;
import std.experimental.logger.core;
@@ -10,7 +13,7 @@ import std.typecons : Flag;
*/
alias CreateFolder = Flag!"CreateFolder";
-/** This $(D Logger) implementation writes log messages to the associated
+/** This `Logger` implementation writes log messages to the associated
file. The name of the file has to be passed on construction time. If the file
is already present new log messages will be append at its end.
*/
@@ -18,14 +21,14 @@ class FileLogger : Logger
{
import std.concurrency : Tid;
import std.datetime.systime : SysTime;
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
- /** A constructor for the $(D FileLogger) Logger.
+ /** A constructor for the `FileLogger` Logger.
Params:
- fn = The filename of the output file of the $(D FileLogger). If that
+ fn = The filename of the output file of the `FileLogger`. If that
file can not be opened for writting an exception will be thrown.
- lv = The $(D LogLevel) for the $(D FileLogger). By default the
+ lv = The `LogLevel` for the `FileLogger`. By default the
Example:
-------------
@@ -34,22 +37,22 @@ class FileLogger : Logger
auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes);
-------------
*/
- this(in string fn, const LogLevel lv = LogLevel.all) @safe
+ this(const string fn, const LogLevel lv = LogLevel.all) @safe
{
this(fn, lv, CreateFolder.yes);
}
- /** A constructor for the $(D FileLogger) Logger that takes a reference to
- a $(D File).
+ /** A constructor for the `FileLogger` Logger that takes a reference to
+ a `File`.
- The $(D File) passed must be open for all the log call to the
- $(D FileLogger). If the $(D File) gets closed, using the $(D FileLogger)
+ The `File` passed must be open for all the log call to the
+ `FileLogger`. If the `File` gets closed, using the `FileLogger`
for logging will result in undefined behaviour.
Params:
fn = The file used for logging.
- lv = The $(D LogLevel) for the $(D FileLogger). By default the
- $(D LogLevel) for $(D FileLogger) is $(D LogLevel.all).
+ lv = The `LogLevel` for the `FileLogger`. By default the
+ `LogLevel` for `FileLogger` is `LogLevel.all`.
createFileNameFolder = if yes and fn contains a folder name, this
folder will be created.
@@ -60,7 +63,7 @@ class FileLogger : Logger
auto l2 = new FileLogger(file, LogLevel.fatal);
-------------
*/
- this(in string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe
+ this(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe
{
import std.file : exists, mkdirRecurse;
import std.path : dirName;
@@ -80,17 +83,17 @@ class FileLogger : Logger
this.file_.open(this.filename, "a");
}
- /** A constructor for the $(D FileLogger) Logger that takes a reference to
- a $(D File).
+ /** A constructor for the `FileLogger` Logger that takes a reference to
+ a `File`.
- The $(D File) passed must be open for all the log call to the
- $(D FileLogger). If the $(D File) gets closed, using the $(D FileLogger)
+ The `File` passed must be open for all the log call to the
+ `FileLogger`. If the `File` gets closed, using the `FileLogger`
for logging will result in undefined behaviour.
Params:
file = The file used for logging.
- lv = The $(D LogLevel) for the $(D FileLogger). By default the
- $(D LogLevel) for $(D FileLogger) is $(D LogLevel.all).
+ lv = The `LogLevel` for the `FileLogger`. By default the
+ `LogLevel` for `FileLogger` is `LogLevel.all`.
Example:
-------------
@@ -105,7 +108,7 @@ class FileLogger : Logger
this.file_ = file;
}
- /** If the $(D FileLogger) is managing the $(D File) it logs to, this
+ /** If the `FileLogger` is managing the `File` it logs to, this
method will return a reference to this File.
*/
@property File file() @safe
@@ -114,7 +117,7 @@ class FileLogger : Logger
}
/* This method overrides the base class method in order to log to a file
- without requiring heap allocated memory. Additionally, the $(D FileLogger)
+ without requiring heap allocated memory. Additionally, the `FileLogger`
local mutex is logged to serialize the log calls.
*/
override protected void beginLogMsg(string file, int line, string funcName,
@@ -128,21 +131,22 @@ class FileLogger : Logger
auto lt = this.file_.lockingTextWriter();
systimeToISOString(lt, timestamp);
- formattedWrite(lt, ":%s:%s:%u ", file[fnIdx .. $],
- funcName[funIdx .. $], line);
+ import std.conv : to;
+ formattedWrite(lt, " [%s] %s:%u:%s ", logLevel.to!string,
+ file[fnIdx .. $], line, funcName[funIdx .. $]);
}
/* This methods overrides the base class method and writes the parts of
the log call directly to the file.
*/
- override protected void logMsgPart(const(char)[] msg)
+ override protected void logMsgPart(scope const(char)[] msg)
{
formattedWrite(this.file_.lockingTextWriter(), "%s", msg);
}
/* This methods overrides the base class method and finalizes the active
- log call. This requires flushing the $(D File) and releasing the
- $(D FileLogger) local mutex.
+ log call. This requires flushing the `File` and releasing the
+ `FileLogger` local mutex.
*/
override protected void finishLogMsg()
{
@@ -151,7 +155,7 @@ class FileLogger : Logger
}
/* This methods overrides the base class method and delegates the
- $(D LogEntry) data to the actual implementation.
+ `LogEntry` data to the actual implementation.
*/
override protected void writeLogMsg(ref LogEntry payload)
{
@@ -162,16 +166,19 @@ class FileLogger : Logger
this.finishLogMsg();
}
- /** If the $(D FileLogger) was constructed with a filename, this method
- returns this filename. Otherwise an empty $(D string) is returned.
+ /** If the `FileLogger` was constructed with a filename, this method
+ returns this filename. Otherwise an empty `string` is returned.
*/
string getFilename()
{
return this.filename;
}
- private File file_;
- private string filename;
+ /** The `File` log messages are written to. */
+ protected File file_;
+
+ /** The filename of the `File` log messages are written to. */
+ protected string filename;
}
@system unittest
diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d
index ed9cfd9b1ef..0751cb86357 100644
--- a/libphobos/src/std/experimental/logger/multilogger.d
+++ b/libphobos/src/std/experimental/logger/multilogger.d
@@ -1,34 +1,37 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/logger/multilogger.d)
+*/
module std.experimental.logger.multilogger;
import std.experimental.logger.core;
import std.experimental.logger.filelogger;
-/** This Element is stored inside the $(D MultiLogger) and associates a
-$(D Logger) to a $(D string).
+/** This Element is stored inside the `MultiLogger` and associates a
+`Logger` to a `string`.
*/
struct MultiLoggerEntry
{
- string name; /// The name if the $(D Logger)
- Logger logger; /// The stored $(D Logger)
+ string name; /// The name if the `Logger`
+ Logger logger; /// The stored `Logger`
}
-/** MultiLogger logs to multiple $(D Logger). The $(D Logger)s are stored in an
-$(D Logger[]) in their order of insertion.
+/** MultiLogger logs to multiple `Logger`. The `Logger`s are stored in an
+`Logger[]` in their order of insertion.
-Every data logged to this $(D MultiLogger) will be distributed to all the $(D
-Logger)s inserted into it. This $(D MultiLogger) implementation can
-hold multiple $(D Logger)s with the same name. If the method $(D removeLogger)
-is used to remove a $(D Logger) only the first occurrence with that name will
+Every data logged to this `MultiLogger` will be distributed to all the $(D
+Logger)s inserted into it. This `MultiLogger` implementation can
+hold multiple `Logger`s with the same name. If the method `removeLogger`
+is used to remove a `Logger` only the first occurrence with that name will
be removed.
*/
class MultiLogger : Logger
{
- /** A constructor for the $(D MultiLogger) Logger.
+ /** A constructor for the `MultiLogger` Logger.
Params:
- lv = The $(D LogLevel) for the $(D MultiLogger). By default the
- $(D LogLevel) for $(D MultiLogger) is $(D LogLevel.all).
+ lv = The `LogLevel` for the `MultiLogger`. By default the
+ `LogLevel` for `MultiLogger` is `LogLevel.all`.
Example:
-------------
@@ -40,32 +43,32 @@ class MultiLogger : Logger
super(lv);
}
- /** This member holds all $(D Logger)s stored in the $(D MultiLogger).
+ /** This member holds all `Logger`s stored in the `MultiLogger`.
- When inheriting from $(D MultiLogger) this member can be used to gain
- access to the stored $(D Logger).
+ When inheriting from `MultiLogger` this member can be used to gain
+ access to the stored `Logger`.
*/
protected MultiLoggerEntry[] logger;
- /** This method inserts a new Logger into the $(D MultiLogger).
+ /** This method inserts a new Logger into the `MultiLogger`.
Params:
- name = The name of the $(D Logger) to insert.
- newLogger = The $(D Logger) to insert.
+ name = The name of the `Logger` to insert.
+ newLogger = The `Logger` to insert.
*/
void insertLogger(string name, Logger newLogger) @safe
{
this.logger ~= MultiLoggerEntry(name, newLogger);
}
- /** This method removes a Logger from the $(D MultiLogger).
+ /** This method removes a Logger from the `MultiLogger`.
Params:
- toRemove = The name of the $(D Logger) to remove. If the $(D Logger)
- is not found $(D null) will be returned. Only the first occurrence of
- a $(D Logger) with the given name will be removed.
+ toRemove = The name of the `Logger` to remove. If the `Logger`
+ is not found `null` will be returned. Only the first occurrence of
+ a `Logger` with the given name will be removed.
- Returns: The removed $(D Logger).
+ Returns: The removed `Logger`.
*/
Logger removeLogger(in char[] toRemove) @safe
{
@@ -87,7 +90,7 @@ class MultiLogger : Logger
}
/* The override to pass the payload to all children of the
- $(D MultiLoggerBase).
+ `MultiLoggerBase`.
*/
override protected void writeLogMsg(ref LogEntry payload) @safe
{
diff --git a/libphobos/src/std/experimental/logger/nulllogger.d b/libphobos/src/std/experimental/logger/nulllogger.d
index 0c55377d0f9..0ff7663bd92 100644
--- a/libphobos/src/std/experimental/logger/nulllogger.d
+++ b/libphobos/src/std/experimental/logger/nulllogger.d
@@ -1,21 +1,24 @@
-///
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/experimental/logger/nulllogger.d)
+*/
module std.experimental.logger.nulllogger;
import std.experimental.logger.core;
-/** The $(D NullLogger) will not process any log messages.
+/** The `NullLogger` will not process any log messages.
-In case of a log message with $(D LogLevel.fatal) nothing will happen.
+In case of a log message with `LogLevel.fatal` nothing will happen.
*/
class NullLogger : Logger
{
- /** The default constructor for the $(D NullLogger).
+ /** The default constructor for the `NullLogger`.
Independent of the parameter this Logger will never log a message.
Params:
- lv = The $(D LogLevel) for the $(D NullLogger). By default the $(D LogLevel)
- for $(D NullLogger) is $(D LogLevel.all).
+ lv = The `LogLevel` for the `NullLogger`. By default the `LogLevel`
+ for `NullLogger` is `LogLevel.all`.
*/
this(const LogLevel lv = LogLevel.all) @safe
{
@@ -32,7 +35,6 @@ class NullLogger : Logger
@safe unittest
{
import std.experimental.logger.core : LogLevel;
-
auto nl1 = new NullLogger(LogLevel.all);
nl1.info("You will never read this.");
nl1.fatal("You will never read this, either and it will not throw");
diff --git a/libphobos/src/std/experimental/logger/package.d b/libphobos/src/std/experimental/logger/package.d
index b9a075c9f9f..79245ec4767 100644
--- a/libphobos/src/std/experimental/logger/package.d
+++ b/libphobos/src/std/experimental/logger/package.d
@@ -1,3 +1,4 @@
+// Written in the D programming language.
/**
Implements logging facilities.
@@ -9,7 +10,7 @@ $(H3 Basic Logging)
Message logging is a common approach to expose runtime information of a
program. Logging should be easy, but also flexible and powerful, therefore
-$(D D) provides a standard interface for logging.
+`D` provides a standard interface for logging.
The easiest way to create a log message is to write:
-------------
@@ -19,7 +20,7 @@ void main() {
log("Hello World");
}
-------------
-This will print a message to the $(D stderr) device. The message will contain
+This will print a message to the `stderr` device. The message will contain
the filename, the line number, the name of the surrounding function, the time
and the message.
@@ -43,85 +44,85 @@ fLogger.critical("Logging to the fileLogger with its info LogLevel");
fLogger.log(LogLevel.trace, 5 < 6, "Logging to the fileLogger"," with its default LogLevel if 5 is less than 6");
fLogger.fatal("Logging to the fileLogger with its warning LogLevel");
-------------
-Additionally, this example shows how a new $(D FileLogger) is created.
-Individual $(D Logger) and the global log functions share commonly named
+Additionally, this example shows how a new `FileLogger` is created.
+Individual `Logger` and the global log functions share commonly named
functions to log data.
The names of the functions are as follows:
$(UL
- $(LI $(D log))
- $(LI $(D trace))
- $(LI $(D info))
- $(LI $(D warning))
- $(LI $(D critical))
- $(LI $(D fatal))
+ $(LI `log`)
+ $(LI `trace`)
+ $(LI `info`)
+ $(LI `warning`)
+ $(LI `critical`)
+ $(LI `fatal`)
)
-The default $(D Logger) will by default log to $(D stderr) and has a default
-$(D LogLevel) of $(D LogLevel.all). The default Logger can be accessed by
-using the property called $(D sharedLog). This property is a reference to the
-current default $(D Logger). This reference can be used to assign a new
-default $(D Logger).
+The default `Logger` will by default log to `stderr` and has a default
+`LogLevel` of `LogLevel.all`. The default Logger can be accessed by
+using the property called `sharedLog`. This property is a reference to the
+current default `Logger`. This reference can be used to assign a new
+default `Logger`.
-------------
sharedLog = new FileLogger("New_Default_Log_File.log");
-------------
-Additional $(D Logger) can be created by creating a new instance of the
-required $(D Logger).
+Additional `Logger` can be created by creating a new instance of the
+required `Logger`.
$(H3 Logging Fundamentals)
$(H4 LogLevel)
-The $(D LogLevel) of a log call can be defined in two ways. The first is by
-calling $(D log) and passing the $(D LogLevel) explicitly as the first argument.
-The second way of setting the $(D LogLevel) of a
-log call, is by calling either $(D trace), $(D info), $(D warning),
-$(D critical), or $(D fatal). The log call will then have the respective
-$(D LogLevel). If no $(D LogLevel) is defined the log call will use the
-current $(D LogLevel) of the used $(D Logger). If data is logged with
-$(D LogLevel) $(D fatal) by default an $(D Error) will be thrown.
-This behaviour can be modified by using the member $(D fatalHandler) to
-assign a custom delegate to handle log call with $(D LogLevel) $(D fatal).
+The `LogLevel` of a log call can be defined in two ways. The first is by
+calling `log` and passing the `LogLevel` explicitly as the first argument.
+The second way of setting the `LogLevel` of a
+log call, is by calling either `trace`, `info`, `warning`,
+`critical`, or `fatal`. The log call will then have the respective
+`LogLevel`. If no `LogLevel` is defined the log call will use the
+current `LogLevel` of the used `Logger`. If data is logged with
+`LogLevel` `fatal` by default an `Error` will be thrown.
+This behaviour can be modified by using the member `fatalHandler` to
+assign a custom delegate to handle log call with `LogLevel` `fatal`.
$(H4 Conditional Logging)
-Conditional logging can be achieved be passing a $(D bool) as first
+Conditional logging can be achieved be passing a `bool` as first
argument to a log function. If conditional logging is used the condition must
-be $(D true) in order to have the log message logged.
+be `true` in order to have the log message logged.
-In order to combine an explicit $(D LogLevel) passing with conditional
-logging, the $(D LogLevel) has to be passed as first argument followed by the
-$(D bool).
+In order to combine an explicit `LogLevel` passing with conditional
+logging, the `LogLevel` has to be passed as first argument followed by the
+`bool`.
$(H4 Filtering Log Messages)
-Messages are logged if the $(D LogLevel) of the log message is greater than or
-equal to the $(D LogLevel) of the used $(D Logger) and additionally if the
-$(D LogLevel) of the log message is greater than or equal to the global $(D LogLevel).
+Messages are logged if the `LogLevel` of the log message is greater than or
+equal to the `LogLevel` of the used `Logger` and additionally if the
+`LogLevel` of the log message is greater than or equal to the global `LogLevel`.
If a condition is passed into the log call, this condition must be true.
-The global $(D LogLevel) is accessible by using $(D globalLogLevel).
-To assign a $(D LogLevel) of a $(D Logger) use the $(D logLevel) property of
+The global `LogLevel` is accessible by using `globalLogLevel`.
+To assign a `LogLevel` of a `Logger` use the `logLevel` property of
the logger.
$(H4 Printf Style Logging)
-If $(D printf)-style logging is needed add a $(B f) to the logging call, such as
+If `printf`-style logging is needed add a $(B f) to the logging call, such as
$(D myLogger.infof("Hello %s", "world");) or $(D fatalf("errno %d", 1337)).
-The additional $(B f) appended to the function name enables $(D printf)-style
-logging for all combinations of explicit $(D LogLevel) and conditional
+The additional $(B f) appended to the function name enables `printf`-style
+logging for all combinations of explicit `LogLevel` and conditional
logging functions and methods.
$(H4 Thread Local Redirection)
Calls to the free standing log functions are not directly forwarded to the
-global $(D Logger) $(D sharedLog). Actually, a thread local $(D Logger) of
-type $(D StdForwardLogger) processes the log call and then, by default, forwards
-the created $(D Logger.LogEntry) to the $(D sharedLog) $(D Logger).
-The thread local $(D Logger) is accessible by the $(D stdThreadLocalLog)
-property. This property allows to assign user defined $(D Logger). The default
-$(D LogLevel) of the $(D stdThreadLocalLog) $(D Logger) is $(D LogLevel.all)
-and it will therefore forward all messages to the $(D sharedLog) $(D Logger).
-The $(D LogLevel) of the $(D stdThreadLocalLog) can be used to filter log
-calls before they reach the $(D sharedLog) $(D Logger).
+global `Logger` `sharedLog`. Actually, a thread local `Logger` of
+type `StdForwardLogger` processes the log call and then, by default, forwards
+the created `Logger.LogEntry` to the `sharedLog` `Logger`.
+The thread local `Logger` is accessible by the `stdThreadLocalLog`
+property. This property allows to assign user defined `Logger`. The default
+`LogLevel` of the `stdThreadLocalLog` `Logger` is `LogLevel.all`
+and it will therefore forward all messages to the `sharedLog` `Logger`.
+The `LogLevel` of the `stdThreadLocalLog` can be used to filter log
+calls before they reach the `sharedLog` `Logger`.
$(H3 User Defined Logger)
-To customize the $(D Logger) behavior, create a new $(D class) that inherits from
-the abstract $(D Logger) $(D class), and implements the $(D writeLogMsg)
+To customize the `Logger` behavior, create a new `class` that inherits from
+the abstract `Logger` `class`, and implements the `writeLogMsg`
method.
-------------
class MyCustomLogger : Logger
@@ -142,40 +143,42 @@ logger.log("Awesome log message with LogLevel.info");
-------------
To gain more precise control over the logging process, additionally to
-overriding the $(D writeLogMsg) method the methods $(D beginLogMsg),
-$(D logMsgPart) and $(D finishLogMsg) can be overridden.
+overriding the `writeLogMsg` method the methods `beginLogMsg`,
+`logMsgPart` and `finishLogMsg` can be overridden.
-$(H3 Compile Time Disabling of $(D Logger))
-In order to disable logging at compile time, pass $(D StdLoggerDisableLogging) as a
-version argument to the $(D D) compiler when compiling your program code.
+$(H3 Compile Time Disabling of `Logger`)
+In order to disable logging at compile time, pass `StdLoggerDisableLogging` as a
+version argument to the `D` compiler when compiling your program code.
This will disable all logging functionality.
-Specific $(D LogLevel) can be disabled at compile time as well.
-In order to disable logging with the $(D trace) $(D LogLevel) pass
-$(D StdLoggerDisableTrace) as a version.
+Specific `LogLevel` can be disabled at compile time as well.
+In order to disable logging with the `trace` `LogLevel` pass
+`StdLoggerDisableTrace` as a version.
The following table shows which version statement disables which
-$(D LogLevel).
+`LogLevel`.
$(TABLE
- $(TR $(TD $(D LogLevel.trace) ) $(TD StdLoggerDisableTrace))
- $(TR $(TD $(D LogLevel.info) ) $(TD StdLoggerDisableInfo))
- $(TR $(TD $(D LogLevel.warning) ) $(TD StdLoggerDisableWarning))
- $(TR $(TD $(D LogLevel.error) ) $(TD StdLoggerDisableError))
- $(TR $(TD $(D LogLevel.critical) ) $(TD StdLoggerDisableCritical))
- $(TR $(TD $(D LogLevel.fatal) ) $(TD StdLoggerDisableFatal))
+ $(TR $(TD `LogLevel.trace` ) $(TD StdLoggerDisableTrace))
+ $(TR $(TD `LogLevel.info` ) $(TD StdLoggerDisableInfo))
+ $(TR $(TD `LogLevel.warning` ) $(TD StdLoggerDisableWarning))
+ $(TR $(TD `LogLevel.error` ) $(TD StdLoggerDisableError))
+ $(TR $(TD `LogLevel.critical` ) $(TD StdLoggerDisableCritical))
+ $(TR $(TD `LogLevel.fatal` ) $(TD StdLoggerDisableFatal))
)
Such a version statement will only disable logging in the associated compile
unit.
$(H3 Provided Logger)
-By default four $(D Logger) implementations are given. The $(D FileLogger)
-logs data to files. It can also be used to log to $(D stdout) and $(D stderr)
-as these devices are files as well. A $(D Logger) that logs to $(D stdout) can
+By default four `Logger` implementations are given. The `FileLogger`
+logs data to files. It can also be used to log to `stdout` and `stderr`
+as these devices are files as well. A `Logger` that logs to `stdout` can
therefore be created by $(D new FileLogger(stdout)).
-The $(D MultiLogger) is basically an associative array of $(D string)s to
-$(D Logger). It propagates log calls to its stored $(D Logger). The
-$(D ArrayLogger) contains an array of $(D Logger) and also propagates log
-calls to its stored $(D Logger). The $(D NullLogger) does not do anything. It
-will never log a message and will never throw on a log call with $(D LogLevel)
-$(D error).
+The `MultiLogger` is basically an associative array of `string`s to
+`Logger`. It propagates log calls to its stored `Logger`. The
+`ArrayLogger` contains an array of `Logger` and also propagates log
+calls to its stored `Logger`. The `NullLogger` does not do anything. It
+will never log a message and will never throw on a log call with `LogLevel`
+`error`.
+
+Source: $(PHOBOSSRC std/experimental/logger/package.d)
*/
module std.experimental.logger;
diff --git a/libphobos/src/std/experimental/typecons.d b/libphobos/src/std/experimental/typecons.d
index 07eed8fad69..46e21e77e7a 100644
--- a/libphobos/src/std/experimental/typecons.d
+++ b/libphobos/src/std/experimental/typecons.d
@@ -1,14 +1,14 @@
// Written in the D programming language.
/**
-This module implements experimental additions/modifications to $(MREF std, _typecons).
+This module implements experimental additions/modifications to $(MREF std, typecons).
-Use this module to test out new functionality for $(REF wrap, std, _typecons)
+Use this module to test out new functionality for $(REF wrap, std, typecons)
which allows for a struct to be wrapped against an interface; the
-implementation in $(MREF std, _typecons) only allows for classes to use the wrap
+implementation in $(MREF std, typecons) only allows for classes to use the wrap
functionality.
-Source: $(PHOBOSSRC std/experimental/_typecons.d)
+Source: $(PHOBOSSRC std/experimental/typecons.d)
Copyright: Copyright the respective authors, 2008-
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
@@ -23,8 +23,7 @@ module std.experimental.typecons;
import std.meta; // : AliasSeq, allSatisfy;
import std.traits;
-import std.typecons : Tuple, tuple, Bind, DerivedFunctionType, mixinAll, staticIota,
- GetOverloadedMethods;
+import std.typecons : Tuple, tuple, Bind, DerivedFunctionType, GetOverloadedMethods;
private
{
@@ -59,14 +58,14 @@ if (is(T == class) || is(T == interface))
@system unittest
{
- class C { @disable opCast(T)() {} }
+ class C { @disable void opCast(T)(); }
auto c = new C;
static assert(!__traits(compiles, cast(Object) c));
auto o = dynamicCast!Object(c);
assert(c is o);
- interface I { @disable opCast(T)() {} Object instance(); }
- interface J { @disable opCast(T)() {} Object instance(); }
+ interface I { @disable void opCast(T)(); Object instance(); }
+ interface J { @disable void opCast(T)(); Object instance(); }
class D : I, J { Object instance() { return this; } }
I i = new D();
static assert(!__traits(compiles, cast(J) i));
@@ -112,13 +111,15 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
else
{
enum foundFunc = findCovariantFunction!(TargetMembers[i], Source, SourceMembers);
- version (unittest) {}
+
+ version (StdUnittest) {}
else debug
{
static if (foundFunc == -1)
pragma(msg, "Could not locate matching function for: ",
TargetMembers[i].stringof);
}
+
enum hasRequiredMethods =
foundFunc != -1 &&
hasRequiredMethods!(i + 1);
@@ -290,7 +291,7 @@ if (Targets.length >= 1 && allSatisfy!(isInterface, Targets))
}
import std.conv : to;
- import std.functional : forward;
+ import core.lifetime : forward;
template generateFun(size_t i)
{
enum name = TargetMembers[i].name;
@@ -299,7 +300,7 @@ if (Targets.length >= 1 && allSatisfy!(isInterface, Targets))
{
string r;
bool first = true;
- foreach (i; staticIota!(0, num))
+ foreach (i; 0 .. num)
{
import std.conv : to;
r ~= (first ? "" : ", ") ~ " a" ~ (i+1).to!string;
@@ -324,8 +325,8 @@ if (Targets.length >= 1 && allSatisfy!(isInterface, Targets))
}
public:
- mixin mixinAll!(
- staticMap!(generateFun, staticIota!(0, TargetMembers.length)));
+ static foreach (i; 0 .. TargetMembers.length)
+ mixin(generateFun!i);
}
}
}
@@ -662,9 +663,10 @@ template unwrap(Target)
assert(d.draw(10) == 10);
}
}
+
+// https://issues.dlang.org/show_bug.cgi?id=10377
@system unittest
{
- // Bugzilla 10377
import std.algorithm, std.range;
interface MyInputRange(T)
@@ -679,9 +681,10 @@ template unwrap(Target)
auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)();
assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
}
+
+// https://issues.dlang.org/show_bug.cgi?id=10536
@system unittest
{
- // Bugzilla 10536
interface Interface
{
int foo();
@@ -695,9 +698,10 @@ template unwrap(Target)
Interface i = new Pluggable().wrap!Interface;
assert(i.foo() == 1);
}
+
+// https://issues.dlang.org/show_bug.cgi?id=10538
@system unittest
{
- // Enhancement 10538
interface Interface
{
int foo();
@@ -980,9 +984,6 @@ pure nothrow @safe unittest
Final!A a = new A;
static assert(!__traits(compiles, a = new A));
- static void foo(ref A a) pure nothrow @safe @nogc {}
- static assert(!__traits(compiles, foo(a)));
-
assert(a.i == 0);
a.i = 42;
assert(a.i == 42);
@@ -1051,7 +1052,7 @@ pure nothrow @safe unittest
assert((arr ~ 4) == [1, 2, 3, 4]);
}
-// issue 17270
+// https://issues.dlang.org/show_bug.cgi?id=17270
pure nothrow @nogc @system unittest
{
int i = 1;
diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d
index 99530cbbeb0..741656d74a7 100644
--- a/libphobos/src/std/file.d
+++ b/libphobos/src/std/file.d
@@ -2,11 +2,12 @@
/**
Utilities for manipulating files and scanning directories. Functions
-in this module handle files as a unit, e.g., read or write one _file
+in this module handle files as a unit, e.g., read or write one file
at a time. For opening files and manipulating them via handles refer
to module $(MREF std, stdio).
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE,
$(TR $(TH Category) $(TH Functions))
$(TR $(TD General) $(TD
@@ -54,17 +55,20 @@ $(TR $(TD Timestamp) $(TD
$(LREF getTimesWin)
$(LREF setTimes)
$(LREF timeLastModified)
+ $(LREF timeLastAccessed)
+ $(LREF timeStatusChanged)
))
$(TR $(TD Other) $(TD
$(LREF DirEntry)
$(LREF FileException)
$(LREF PreserveAttributes)
$(LREF SpanMode)
+ $(LREF getAvailableDiskSpace)
+))
))
-)
-Copyright: Copyright Digital Mars 2007 - 2011.
+Copyright: Copyright The D Language Foundation 2007 - 2011.
See_Also: The $(HTTP ddili.org/ders/d.en/files.html, official tutorial) for an
introduction to working with files in D, module
$(MREF std, stdio) for opening files and manipulating them via handles,
@@ -73,8 +77,8 @@ and module $(MREF std, path) for manipulating path strings.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP digitalmars.com, Walter Bright),
$(HTTP erdani.org, Andrei Alexandrescu),
- Jonathan M Davis
-Source: $(PHOBOSSRC std/_file.d)
+ $(HTTP jmdavisprog.com, Jonathan M Davis)
+Source: $(PHOBOSSRC std/file.d)
*/
module std.file;
@@ -89,9 +93,18 @@ import std.range.primitives;
import std.traits;
import std.typecons;
+version (OSX)
+ version = Darwin;
+else version (iOS)
+ version = Darwin;
+else version (TVOS)
+ version = Darwin;
+else version (WatchOS)
+ version = Darwin;
+
version (Windows)
{
- import core.sys.windows.windows, std.windows.syserror;
+ import core.sys.windows.winbase, core.sys.windows.winnt, std.windows.syserror;
}
else version (Posix)
{
@@ -104,7 +117,7 @@ else
// Character type used for operating system filesystem APIs
version (Windows)
{
- private alias FSChar = wchar;
+ private alias FSChar = WCHAR; // WCHAR can be aliased to wchar or wchar_t
}
else version (Posix)
{
@@ -116,25 +129,21 @@ else
// Purposefully not documented. Use at your own risk
@property string deleteme() @safe
{
- import std.conv : to;
+ import std.conv : text;
import std.path : buildPath;
import std.process : thisProcessID;
- static _deleteme = "deleteme.dmd.unittest.pid";
- static _first = true;
+ enum base = "deleteme.dmd.unittest.pid";
+ static string fileName;
- if (_first)
- {
- _deleteme = buildPath(tempDir(), _deleteme) ~ to!string(thisProcessID);
- _first = false;
- }
-
- return _deleteme;
+ if (!fileName)
+ fileName = text(buildPath(tempDir(), base), thisProcessID);
+ return fileName;
}
-version (unittest) private struct TestAliasedString
+version (StdUnittest) private struct TestAliasedString
{
- string get() @safe @nogc pure nothrow { return _s; }
+ string get() @safe @nogc pure nothrow return scope { return _s; }
alias get this;
@disable this(this);
string _s;
@@ -164,7 +173,7 @@ class FileException : Exception
+/
immutable uint errno;
- private this(in char[] name, in char[] msg, string file, size_t line, uint errno) @safe pure
+ private this(scope const(char)[] name, scope const(char)[] msg, string file, size_t line, uint errno) @safe pure
{
if (msg.empty)
super(name.idup, file, line);
@@ -180,34 +189,34 @@ class FileException : Exception
Params:
name = Name of file for which the error occurred.
msg = Message describing the error.
- file = The _file where the error occurred.
+ file = The file where the error occurred.
line = The _line where the error occurred.
+/
- this(in char[] name, in char[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure
+ this(scope const(char)[] name, scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure
{
this(name, msg, file, line, 0);
}
/++
Constructor which takes the error number ($(LUCKY GetLastError)
- in Windows, $(D_PARAM errno) in Posix).
+ in Windows, $(D_PARAM errno) in POSIX).
Params:
name = Name of file for which the error occurred.
errno = The error number.
- file = The _file where the error occurred.
- Defaults to $(D __FILE__).
+ file = The file where the error occurred.
+ Defaults to `__FILE__`.
line = The _line where the error occurred.
- Defaults to $(D __LINE__).
+ Defaults to `__LINE__`.
+/
- version (Windows) this(in char[] name,
+ version (Windows) this(scope const(char)[] name,
uint errno = .GetLastError(),
string file = __FILE__,
size_t line = __LINE__) @safe
{
this(name, sysErrorString(errno), file, line, errno);
}
- else version (Posix) this(in char[] name,
+ else version (Posix) this(scope const(char)[] name,
uint errno = .errno,
string file = __FILE__,
size_t line = __LINE__) @trusted
@@ -217,7 +226,15 @@ class FileException : Exception
}
}
-private T cenforce(T)(T condition, lazy const(char)[] name, string file = __FILE__, size_t line = __LINE__)
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ assertThrown!FileException("non.existing.file.".readText);
+}
+
+private T cenforce(T)(T condition, lazy scope const(char)[] name, string file = __FILE__, size_t line = __LINE__)
{
if (condition)
return condition;
@@ -233,7 +250,7 @@ private T cenforce(T)(T condition, lazy const(char)[] name, string file = __FILE
version (Windows)
@trusted
-private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
+private T cenforce(T)(T condition, scope const(char)[] name, scope const(FSChar)* namez,
string file = __FILE__, size_t line = __LINE__)
{
if (condition)
@@ -251,7 +268,7 @@ private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
version (Posix)
@trusted
-private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
+private T cenforce(T)(T condition, scope const(char)[] name, scope const(FSChar)* namez,
string file = __FILE__, size_t line = __LINE__)
{
if (condition)
@@ -266,9 +283,9 @@ private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
throw new FileException(name, .errno, file, line);
}
+// https://issues.dlang.org/show_bug.cgi?id=17102
@safe unittest
{
- // issue 17102
try
{
cenforce(false, null, null,
@@ -282,8 +299,8 @@ private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez,
*/
/********************************************
-Read entire contents of file $(D name) and returns it as an untyped
-array. If the file size is larger than $(D upTo), only $(D upTo)
+Read entire contents of file `name` and returns it as an untyped
+array. If the file size is larger than `upTo`, only `upTo`
bytes are _read.
Params:
@@ -299,7 +316,7 @@ void[] read(R)(R name, size_t upTo = size_t.max)
if (isInputRange!R && isSomeChar!(ElementEncodingType!R) && !isInfinite!R &&
!isConvertibleToString!R)
{
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
return readImpl(name, name.tempCString!FSChar(), upTo);
else
return readImpl(null, name.tempCString!FSChar(), upTo);
@@ -315,7 +332,7 @@ if (isInputRange!R && isSomeChar!(ElementEncodingType!R) && !isInfinite!R &&
remove(deleteme);
}
- write(deleteme, "1234"); // deleteme is the name of a temporary file
+ std.file.write(deleteme, "1234"); // deleteme is the name of a temporary file
assert(read(deleteme, 2) == "12");
assert(read(deleteme.byChar) == "1234");
assert((cast(const(ubyte)[])read(deleteme)).length == 4);
@@ -333,12 +350,13 @@ if (isConvertibleToString!R)
static assert(__traits(compiles, read(TestAliasedString(null))));
}
-version (Posix) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @trusted
+version (Posix) private void[] readImpl(scope const(char)[] name, scope const(FSChar)* namez,
+ size_t upTo = size_t.max) @trusted
{
import core.memory : GC;
import std.algorithm.comparison : min;
- import std.array : uninitializedArray;
import std.conv : to;
+ import std.experimental.checkedint : checked;
// A few internal configuration parameters {
enum size_t
@@ -359,48 +377,49 @@ version (Posix) private void[] readImpl(const(char)[] name, const(FSChar)* namez
immutable initialAlloc = min(upTo, to!size_t(statbuf.st_size
? min(statbuf.st_size + 1, maxInitialAlloc)
: minInitialAlloc));
- void[] result = uninitializedArray!(ubyte[])(initialAlloc);
+ void[] result = GC.malloc(initialAlloc, GC.BlkAttr.NO_SCAN)[0 .. initialAlloc];
scope(failure) GC.free(result.ptr);
- size_t size = 0;
+
+ auto size = checked(size_t(0));
for (;;)
{
- immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size,
- min(result.length, upTo) - size);
+ immutable actual = core.sys.posix.unistd.read(fd, result.ptr + size.get,
+ (min(result.length, upTo) - size).get);
cenforce(actual != -1, name, namez);
if (actual == 0) break;
size += actual;
if (size >= upTo) break;
if (size < result.length) continue;
immutable newAlloc = size + sizeIncrement;
- result = GC.realloc(result.ptr, newAlloc, GC.BlkAttr.NO_SCAN)[0 .. newAlloc];
+ result = GC.realloc(result.ptr, newAlloc.get, GC.BlkAttr.NO_SCAN)[0 .. newAlloc.get];
}
return result.length - size >= maxSlackMemoryAllowed
- ? GC.realloc(result.ptr, size, GC.BlkAttr.NO_SCAN)[0 .. size]
- : result[0 .. size];
+ ? GC.realloc(result.ptr, size.get, GC.BlkAttr.NO_SCAN)[0 .. size.get]
+ : result[0 .. size.get];
}
-version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @safe
+version (Windows) private void[] readImpl(scope const(char)[] name, scope const(FSChar)* namez,
+ size_t upTo = size_t.max) @trusted
{
import core.memory : GC;
import std.algorithm.comparison : min;
- import std.array : uninitializedArray;
- static trustedCreateFileW(const(wchar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
+ static trustedCreateFileW(scope const(wchar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
- DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted
+ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
return CreateFileW(namez, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}
- static trustedCloseHandle(HANDLE hObject) @trusted
+ static trustedCloseHandle(HANDLE hObject)
{
return CloseHandle(hObject);
}
- static trustedGetFileSize(HANDLE hFile, out ulong fileSize) @trusted
+ static trustedGetFileSize(HANDLE hFile, out ulong fileSize)
{
DWORD sizeHigh;
DWORD sizeLow = GetFileSize(hFile, &sizeHigh);
@@ -409,7 +428,7 @@ version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* nam
fileSize = makeUlong(sizeLow, sizeHigh);
return result;
}
- static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead) @trusted
+ static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead)
{
// Read by chunks of size < 4GB (Windows API limit)
ulong totalNumRead = 0;
@@ -437,11 +456,11 @@ version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* nam
ulong fileSize = void;
cenforce(trustedGetFileSize(h, fileSize), name, namez);
size_t size = min(upTo, fileSize);
- auto buf = uninitializedArray!(ubyte[])(size);
+ auto buf = () { return GC.malloc(size, GC.BlkAttr.NO_SCAN)[0 .. size]; } ();
scope(failure)
{
- () @trusted { GC.free(buf.ptr); } ();
+ () { GC.free(buf.ptr); } ();
}
if (size)
@@ -452,7 +471,7 @@ version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* nam
version (linux) @safe unittest
{
// A file with "zero" length that doesn't have 0 length at all
- auto s = std.file.readText("/proc/sys/kernel/osrelease");
+ auto s = std.file.readText("/proc/cpuinfo");
assert(s.length > 0);
//writefln("'%s'", s);
}
@@ -466,49 +485,143 @@ version (linux) @safe unittest
assert(read(deleteme) == "abcd");
}
-/********************************************
-Read and validates (using $(REF validate, std,utf)) a text file. $(D S)
-can be a type of array of characters of any width and constancy. No
-width conversion is performed; if the width of the characters in file
-$(D name) is different from the width of elements of $(D S),
-validation will fail.
-
-Params:
- name = string or range of characters representing the file _name
+/++
+ Reads and validates (using $(REF validate, std, utf)) a text file. S can be
+ an array of any character type. However, no width or endian conversions are
+ performed. So, if the width or endianness of the characters in the given
+ file differ from the width or endianness of the element type of S, then
+ validation will fail.
-Returns: Array of characters read.
+ Params:
+ S = the string type of the file
+ name = string or range of characters representing the file _name
-Throws: $(D FileException) on file error, $(D UTFException) on UTF
-decoding error.
- */
+ Returns: Array of characters read.
-S readText(S = string, R)(R name)
-if (isSomeString!S &&
- (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
- !isConvertibleToString!R)
+ Throws: $(LREF FileException) if there is an error reading the file,
+ $(REF UTFException, std, utf) on UTF decoding error.
++/
+S readText(S = string, R)(auto ref R name)
+if (isSomeString!S && (isInputRange!R && !isInfinite!R && isSomeChar!(ElementType!R) || is(StringTypeOf!R)))
{
- import std.utf : validate;
- static auto trustedCast(void[] buf) @trusted { return cast(S) buf; }
- auto result = trustedCast(read(name));
+ import std.algorithm.searching : startsWith;
+ import std.encoding : getBOM, BOM;
+ import std.exception : enforce;
+ import std.format : format;
+ import std.utf : UTFException, validate;
+
+ static if (is(StringTypeOf!R))
+ StringTypeOf!R filename = name;
+ else
+ auto filename = name;
+
+ static auto trustedCast(T)(void[] buf) @trusted { return cast(T) buf; }
+ auto data = trustedCast!(ubyte[])(read(filename));
+
+ immutable bomSeq = getBOM(data);
+ immutable bom = bomSeq.schema;
+
+ static if (is(immutable ElementEncodingType!S == immutable char))
+ {
+ with(BOM) switch (bom)
+ {
+ case utf16be:
+ case utf16le: throw new UTFException("UTF-8 requested. BOM is for UTF-16");
+ case utf32be:
+ case utf32le: throw new UTFException("UTF-8 requested. BOM is for UTF-32");
+ default: break;
+ }
+ }
+ else static if (is(immutable ElementEncodingType!S == immutable wchar))
+ {
+ with(BOM) switch (bom)
+ {
+ case utf8: throw new UTFException("UTF-16 requested. BOM is for UTF-8");
+ case utf16be:
+ {
+ version (BigEndian)
+ break;
+ else
+ throw new UTFException("BOM is for UTF-16 LE on Big Endian machine");
+ }
+ case utf16le:
+ {
+ version (BigEndian)
+ throw new UTFException("BOM is for UTF-16 BE on Little Endian machine");
+ else
+ break;
+ }
+ case utf32be:
+ case utf32le: throw new UTFException("UTF-8 requested. BOM is for UTF-32");
+ default: break;
+ }
+ }
+ else
+ {
+ with(BOM) switch (bom)
+ {
+ case utf8: throw new UTFException("UTF-16 requested. BOM is for UTF-8");
+ case utf16be:
+ case utf16le: throw new UTFException("UTF-8 requested. BOM is for UTF-16");
+ case utf32be:
+ {
+ version (BigEndian)
+ break;
+ else
+ throw new UTFException("BOM is for UTF-32 LE on Big Endian machine");
+ }
+ case utf32le:
+ {
+ version (BigEndian)
+ throw new UTFException("BOM is for UTF-32 BE on Little Endian machine");
+ else
+ break;
+ }
+ default: break;
+ }
+ }
+
+ if (data.length % ElementEncodingType!S.sizeof != 0)
+ throw new UTFException(format!"The content of %s is not UTF-%s"(filename, ElementEncodingType!S.sizeof * 8));
+
+ auto result = trustedCast!S(data);
validate(result);
return result;
}
-///
+/// Read file with UTF-8 text.
@safe unittest
{
- import std.exception : enforce;
write(deleteme, "abc"); // deleteme is the name of a temporary file
scope(exit) remove(deleteme);
string content = readText(deleteme);
- enforce(content == "abc");
+ assert(content == "abc");
}
-/// ditto
-S readText(S = string, R)(auto ref R name)
-if (isConvertibleToString!R)
+// Read file with UTF-8 text but try to read it as UTF-16.
+@safe unittest
{
- return readText!(S, StringTypeOf!R)(name);
+ import std.exception : assertThrown;
+ import std.utf : UTFException;
+
+ write(deleteme, "abc");
+ scope(exit) remove(deleteme);
+ // Throws because the file is not valid UTF-16.
+ assertThrown!UTFException(readText!wstring(deleteme));
+}
+
+// Read file with UTF-16 text.
+@safe unittest
+{
+ import std.algorithm.searching : skipOver;
+
+ write(deleteme, "\uFEFFabc"w); // With BOM
+ scope(exit) remove(deleteme);
+ auto content = readText!wstring(deleteme);
+ assert(content == "\uFEFFabc"w);
+ // Strips BOM if present.
+ content.skipOver('\uFEFF');
+ assert(content == "abc"w);
}
@safe unittest
@@ -516,8 +629,103 @@ if (isConvertibleToString!R)
static assert(__traits(compiles, readText(TestAliasedString(null))));
}
+@safe unittest
+{
+ import std.array : appender;
+ import std.bitmanip : append, Endian;
+ import std.exception : assertThrown;
+ import std.path : buildPath;
+ import std.string : representation;
+ import std.utf : UTFException;
+
+ mkdir(deleteme);
+ scope(exit) rmdirRecurse(deleteme);
+
+ immutable none8 = buildPath(deleteme, "none8");
+ immutable none16 = buildPath(deleteme, "none16");
+ immutable utf8 = buildPath(deleteme, "utf8");
+ immutable utf16be = buildPath(deleteme, "utf16be");
+ immutable utf16le = buildPath(deleteme, "utf16le");
+ immutable utf32be = buildPath(deleteme, "utf32be");
+ immutable utf32le = buildPath(deleteme, "utf32le");
+ immutable utf7 = buildPath(deleteme, "utf7");
+
+ write(none8, "京都市");
+ write(none16, "京都市"w);
+ write(utf8, (cast(char[])[0xEF, 0xBB, 0xBF]) ~ "京都市");
+ {
+ auto str = "\uFEFF京都市"w;
+ auto arr = appender!(ubyte[])();
+ foreach (c; str)
+ arr.append(c);
+ write(utf16be, arr.data);
+ }
+ {
+ auto str = "\uFEFF京都市"w;
+ auto arr = appender!(ubyte[])();
+ foreach (c; str)
+ arr.append!(ushort, Endian.littleEndian)(c);
+ write(utf16le, arr.data);
+ }
+ {
+ auto str = "\U0000FEFF京都市"d;
+ auto arr = appender!(ubyte[])();
+ foreach (c; str)
+ arr.append(c);
+ write(utf32be, arr.data);
+ }
+ {
+ auto str = "\U0000FEFF京都市"d;
+ auto arr = appender!(ubyte[])();
+ foreach (c; str)
+ arr.append!(uint, Endian.littleEndian)(c);
+ write(utf32le, arr.data);
+ }
+ write(utf7, (cast(ubyte[])[0x2B, 0x2F, 0x76, 0x38, 0x2D]) ~ "foobar".representation);
+
+ assertThrown!UTFException(readText(none16));
+ assert(readText(utf8) == (cast(char[])[0xEF, 0xBB, 0xBF]) ~ "京都市");
+ assertThrown!UTFException(readText(utf16be));
+ assertThrown!UTFException(readText(utf16le));
+ assertThrown!UTFException(readText(utf32be));
+ assertThrown!UTFException(readText(utf32le));
+ assert(readText(utf7) == (cast(char[])[0x2B, 0x2F, 0x76, 0x38, 0x2D]) ~ "foobar");
+
+ assertThrown!UTFException(readText!wstring(none8));
+ assert(readText!wstring(none16) == "京都市"w);
+ assertThrown!UTFException(readText!wstring(utf8));
+ version (BigEndian)
+ {
+ assert(readText!wstring(utf16be) == "\uFEFF京都市"w);
+ assertThrown!UTFException(readText!wstring(utf16le));
+ }
+ else
+ {
+ assertThrown!UTFException(readText!wstring(utf16be));
+ assert(readText!wstring(utf16le) == "\uFEFF京都市"w);
+ }
+ assertThrown!UTFException(readText!wstring(utf32be));
+ assertThrown!UTFException(readText!wstring(utf32le));
+ assertThrown!UTFException(readText!wstring(utf7));
+
+ assertThrown!UTFException(readText!dstring(utf8));
+ assertThrown!UTFException(readText!dstring(utf16be));
+ assertThrown!UTFException(readText!dstring(utf16le));
+ version (BigEndian)
+ {
+ assert(readText!dstring(utf32be) == "\U0000FEFF京都市"d);
+ assertThrown!UTFException(readText!dstring(utf32le));
+ }
+ else
+ {
+ assertThrown!UTFException(readText!dstring(utf32be));
+ assert(readText!dstring(utf32le) == "\U0000FEFF京都市"d);
+ }
+ assertThrown!UTFException(readText!dstring(utf7));
+}
+
/*********************************************
-Write $(D buffer) to file $(D name).
+Write `buffer` to file `name`.
Creates the file if it does not already exist.
@@ -525,7 +733,7 @@ Params:
name = string or range of characters representing the file _name
buffer = data to be written to file
-Throws: $(D FileException) on error.
+Throws: $(LREF FileException) on error.
See_also: $(REF toFile, std,stdio)
*/
@@ -533,14 +741,14 @@ void write(R)(R name, const void[] buffer)
if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
!isConvertibleToString!R)
{
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
writeImpl(name, name.tempCString!FSChar(), buffer, false);
else
writeImpl(null, name.tempCString!FSChar(), buffer, false);
}
///
-@system unittest
+@safe unittest
{
scope(exit)
{
@@ -550,7 +758,9 @@ if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || is
int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
write(deleteme, a); // deleteme is the name of a temporary file
- assert(cast(int[]) read(deleteme) == a);
+ const bytes = read(deleteme);
+ const fileInts = () @trusted { return cast(int[]) bytes; }();
+ assert(fileInts == a);
}
/// ditto
@@ -566,7 +776,7 @@ if (isConvertibleToString!R)
}
/*********************************************
-Appends $(D buffer) to file $(D name).
+Appends `buffer` to file `name`.
Creates the file if it does not already exist.
@@ -574,20 +784,20 @@ Params:
name = string or range of characters representing the file _name
buffer = data to be appended to file
-Throws: $(D FileException) on error.
+Throws: $(LREF FileException) on error.
*/
void append(R)(R name, const void[] buffer)
if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || isSomeString!R) &&
!isConvertibleToString!R)
{
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
writeImpl(name, name.tempCString!FSChar(), buffer, true);
else
writeImpl(null, name.tempCString!FSChar(), buffer, true);
}
///
-@system unittest
+@safe unittest
{
scope(exit)
{
@@ -599,7 +809,9 @@ if ((isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) || is
write(deleteme, a); // deleteme is the name of a temporary file
int[] b = [ 13, 21 ];
append(deleteme, b);
- assert(cast(int[]) read(deleteme) == a ~ b);
+ const bytes = read(deleteme);
+ const fileInts = () @trusted { return cast(int[]) bytes; }();
+ assert(fileInts == a ~ b);
}
/// ditto
@@ -614,10 +826,10 @@ if (isConvertibleToString!R)
static assert(__traits(compiles, append(TestAliasedString("foo"), [0, 1, 2, 3])));
}
-// Posix implementation helper for write and append
+// POSIX implementation helper for write and append
-version (Posix) private void writeImpl(const(char)[] name, const(FSChar)* namez,
- in void[] buffer, bool append) @trusted
+version (Posix) private void writeImpl(scope const(char)[] name, scope const(FSChar)* namez,
+ scope const(void)[] buffer, bool append) @trusted
{
import std.conv : octal;
@@ -647,8 +859,8 @@ version (Posix) private void writeImpl(const(char)[] name, const(FSChar)* namez,
// Windows implementation helper for write and append
-version (Windows) private void writeImpl(const(char)[] name, const(FSChar)* namez,
- in void[] buffer, bool append) @trusted
+version (Windows) private void writeImpl(scope const(char)[] name, scope const(FSChar)* namez,
+ scope const(void)[] buffer, bool append) @trusted
{
HANDLE h;
if (append)
@@ -688,12 +900,21 @@ version (Windows) private void writeImpl(const(char)[] name, const(FSChar)* name
}
/***************************************************
- * Rename file $(D from) _to $(D to).
+ * Rename file `from` _to `to`, moving it between directories if required.
* If the target file exists, it is overwritten.
+ *
+ * It is not possible to rename a file across different mount points
+ * or drives. On POSIX, the operation is atomic. That means, if `to`
+ * already exists there will be no time period during the operation
+ * where `to` is missing. See
+ * $(HTTP man7.org/linux/man-pages/man2/rename.2.html, manpage for rename)
+ * for more details.
+ *
* Params:
* from = string or range of characters representing the existing file name
* to = string or range of characters representing the target file name
- * Throws: $(D FileException) on error.
+ *
+ * Throws: $(LREF FileException) on error.
*/
void rename(RF, RT)(RF from, RT to)
if ((isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) || isSomeString!RF)
@@ -705,12 +926,12 @@ if ((isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) ||
auto fromz = from.tempCString!FSChar();
auto toz = to.tempCString!FSChar();
- static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char))
+ static if (isNarrowString!RF && is(immutable ElementEncodingType!RF == immutable char))
alias f = from;
else
enum string f = null;
- static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char))
+ static if (isNarrowString!RT && is(immutable ElementEncodingType!RT == immutable char))
alias t = to;
else
enum string t = null;
@@ -736,7 +957,23 @@ if (isConvertibleToString!RF || isConvertibleToString!RT)
static assert(__traits(compiles, rename(TestAliasedString(null), "".byChar)));
}
-private void renameImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz) @trusted
+///
+@safe unittest
+{
+ auto t1 = deleteme, t2 = deleteme~"2";
+ scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
+
+ t1.write("1");
+ t1.rename(t2);
+ assert(t2.readText == "1");
+
+ t1.write("2");
+ t1.rename(t2);
+ assert(t2.readText == "2");
+}
+
+private void renameImpl(scope const(char)[] f, scope const(char)[] t,
+ scope const(FSChar)* fromz, scope const(FSChar)* toz) @trusted
{
version (Windows)
{
@@ -773,28 +1010,29 @@ private void renameImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz,
auto t1 = deleteme, t2 = deleteme~"2";
scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
+
write(t1, "1");
rename(t1, t2);
assert(readText(t2) == "1");
+
write(t1, "2");
rename(t1, t2.byWchar);
assert(readText(t2) == "2");
}
-
/***************************************************
-Delete file $(D name).
+Delete file `name`.
Params:
name = string or range of characters representing the file _name
-Throws: $(D FileException) on error.
+Throws: $(LREF FileException) on error.
*/
void remove(R)(R name)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
!isConvertibleToString!R)
{
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
removeImpl(name, name.tempCString!FSChar());
else
removeImpl(null, name.tempCString!FSChar());
@@ -807,12 +1045,24 @@ if (isConvertibleToString!R)
remove!(StringTypeOf!R)(name);
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ deleteme.write("Hello");
+ assert(deleteme.readText == "Hello");
+
+ deleteme.remove;
+ assertThrown!FileException(deleteme.readText);
+}
+
@safe unittest
{
static assert(__traits(compiles, remove(TestAliasedString("foo"))));
}
-private void removeImpl(const(char)[] name, const(FSChar)* namez) @trusted
+private void removeImpl(scope const(char)[] name, scope const(FSChar)* namez) @trusted
{
version (Windows)
{
@@ -840,9 +1090,10 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
WIN32_FILE_ATTRIBUTE_DATA fad = void;
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
{
- static void getFA(const(char)[] name, const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
+ static void getFA(scope const(char)[] name, scope const(FSChar)* namez,
+ out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
{
import std.exception : enforce;
enforce(GetFileAttributesExW(namez, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad),
@@ -852,7 +1103,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
}
else
{
- static void getFA(const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
+ static void getFA(scope const(FSChar)* namez, out WIN32_FILE_ATTRIBUTE_DATA fad) @trusted
{
import core.stdc.wchar_ : wcslen;
import std.conv : to;
@@ -874,13 +1125,15 @@ version (Windows) private ulong makeUlong(DWORD dwLow, DWORD dwHigh) @safe pure
return li.QuadPart;
}
-/***************************************************
-Get size of file $(D name) in bytes.
+/**
+Get size of file `name` in bytes.
Params:
name = string or range of characters representing the file _name
-
-Throws: $(D FileException) on error (e.g., file not found).
+Returns:
+ The size of file in bytes.
+Throws:
+ $(LREF FileException) on error (e.g., file not found).
*/
ulong getSize(R)(R name)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -899,7 +1152,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
{
return stat(namez, &buf);
}
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias names = name;
else
string names = null;
@@ -921,11 +1174,25 @@ if (isConvertibleToString!R)
static assert(__traits(compiles, getSize(TestAliasedString("foo"))));
}
+///
@safe unittest
{
+ scope(exit) deleteme.remove;
+
// create a file of size 1
write(deleteme, "a");
- scope(exit) { assert(exists(deleteme)); remove(deleteme); }
+ assert(getSize(deleteme) == 1);
+
+ // create a file of size 3
+ write(deleteme, "abc");
+ assert(getSize(deleteme) == 3);
+}
+
+@safe unittest
+{
+ // create a file of size 1
+ write(deleteme, "a");
+ scope(exit) deleteme.exists && deleteme.remove;
assert(getSize(deleteme) == 1);
// create a file of size 3
write(deleteme, "abc");
@@ -933,10 +1200,9 @@ if (isConvertibleToString!R)
assert(getSize(deleteme.byChar) == 3);
}
-
// Reads a time field from a stat_t with full precision.
version (Posix)
-private SysTime statTimeToStdTime(char which)(ref stat_t statbuf)
+private SysTime statTimeToStdTime(char which)(ref const stat_t statbuf)
{
auto unixTime = mixin(`statbuf.st_` ~ which ~ `time`);
long stdTime = unixTimeToStdTime(unixTime);
@@ -957,7 +1223,7 @@ private SysTime statTimeToStdTime(char which)(ref stat_t statbuf)
}
/++
- Get the access and modified times of file or folder $(D name).
+ Get the access and modified times of file or folder `name`.
Params:
name = File/Folder _name to get times for.
@@ -965,7 +1231,7 @@ private SysTime statTimeToStdTime(char which)(ref stat_t statbuf)
modificationTime = Time the file/folder was last modified.
Throws:
- $(D FileException) on error.
+ $(LREF FileException) on error.
+/
void getTimes(R)(R name,
out SysTime accessTime,
@@ -993,7 +1259,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
}
stat_t statbuf = void;
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias names = name;
else
string names = null;
@@ -1013,27 +1279,49 @@ if (isConvertibleToString!R)
return getTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
}
+///
+@safe unittest
+{
+ import std.datetime : abs, SysTime;
+
+ scope(exit) deleteme.remove;
+ write(deleteme, "a");
+
+ SysTime accessTime, modificationTime;
+
+ getTimes(deleteme, accessTime, modificationTime);
+
+ import std.datetime : Clock, seconds;
+ auto currTime = Clock.currTime();
+ enum leeway = 5.seconds;
+
+ auto diffAccess = accessTime - currTime;
+ auto diffModification = modificationTime - currTime;
+ assert(abs(diffAccess) <= leeway);
+ assert(abs(diffModification) <= leeway);
+}
+
@safe unittest
{
SysTime atime, mtime;
static assert(__traits(compiles, getTimes(TestAliasedString("foo"), atime, mtime)));
}
-@system unittest
+@safe unittest
{
import std.stdio : writefln;
auto currTime = Clock.currTime();
write(deleteme, "a");
- scope(exit) { assert(exists(deleteme)); remove(deleteme); }
+ scope(exit) assert(deleteme.exists), deleteme.remove;
- SysTime accessTime1 = void;
- SysTime modificationTime1 = void;
+ SysTime accessTime1;
+ SysTime modificationTime1;
getTimes(deleteme, accessTime1, modificationTime1);
- enum leeway = dur!"seconds"(5);
+ enum leeway = 5.seconds;
{
auto diffa = accessTime1 - currTime;
@@ -1079,10 +1367,10 @@ version (StdDdoc)
/++
$(BLUE This function is Windows-Only.)
- Get creation/access/modified times of file $(D name).
+ Get creation/access/modified times of file `name`.
- This is the same as $(D getTimes) except that it also gives you the file
- creation time - which isn't possible on Posix systems.
+ This is the same as `getTimes` except that it also gives you the file
+ creation time - which isn't possible on POSIX systems.
Params:
name = File _name to get times for.
@@ -1091,7 +1379,7 @@ version (StdDdoc)
fileModificationTime = Time the file was last modified.
Throws:
- $(D FileException) on error.
+ $(LREF FileException) on error.
+/
void getTimesWin(R)(R name,
out SysTime fileCreationTime,
@@ -1199,9 +1487,23 @@ version (Windows) @system unittest
}
}
+version (Darwin)
+private
+{
+ import core.stdc.config : c_ulong;
+ enum ATTR_CMN_MODTIME = 0x00000400, ATTR_CMN_ACCTIME = 0x00001000;
+ alias attrgroup_t = uint;
+ static struct attrlist
+ {
+ ushort bitmapcount, reserved;
+ attrgroup_t commonattr, volattr, dirattr, fileattr, forkattr;
+ }
+ extern(C) int setattrlist(in char* path, scope ref attrlist attrs,
+ scope void* attrbuf, size_t attrBufSize, c_ulong options) nothrow @nogc @system;
+}
/++
- Set access/modified times of file or folder $(D name).
+ Set access/modified times of file or folder `name`.
Params:
name = File/Folder _name to get times for.
@@ -1209,7 +1511,7 @@ version (Windows) @system unittest
modificationTime = Time the file/folder was last modified.
Throws:
- $(D FileException) on error.
+ $(LREF FileException) on error.
+/
void setTimes(R)(R name,
SysTime accessTime,
@@ -1217,30 +1519,48 @@ void setTimes(R)(R name,
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
!isConvertibleToString!R)
{
- version (Windows)
- {
- import std.datetime.systime : SysTimeToFILETIME;
+ auto namez = name.tempCString!FSChar();
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
+ alias names = name;
+ else
+ string names = null;
+ setTimesImpl(names, namez, accessTime, modificationTime);
+}
- auto namez = name.tempCString!FSChar();
- static auto trustedCreateFileW(const(FSChar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
- SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
- DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted
- {
- return CreateFileW(namez, dwDesiredAccess, dwShareMode,
- lpSecurityAttributes, dwCreationDisposition,
- dwFlagsAndAttributes, hTemplateFile);
+///
+@safe unittest
+{
+ import std.datetime : DateTime, hnsecs, SysTime;
- }
- static auto trustedCloseHandle(HANDLE hObject) @trusted
- {
- return CloseHandle(hObject);
- }
- static auto trustedSetFileTime(HANDLE hFile, in FILETIME *lpCreationTime,
- in ref FILETIME lpLastAccessTime, in ref FILETIME lpLastWriteTime) @trusted
- {
- return SetFileTime(hFile, lpCreationTime, &lpLastAccessTime, &lpLastWriteTime);
- }
+ scope(exit) deleteme.remove;
+ write(deleteme, "a");
+
+ SysTime accessTime = SysTime(DateTime(2010, 10, 4, 0, 0, 30));
+ SysTime modificationTime = SysTime(DateTime(2018, 10, 4, 0, 0, 30));
+ setTimes(deleteme, accessTime, modificationTime);
+
+ SysTime accessTimeResolved, modificationTimeResolved;
+ getTimes(deleteme, accessTimeResolved, modificationTimeResolved);
+
+ assert(accessTime == accessTimeResolved);
+ assert(modificationTime == modificationTimeResolved);
+}
+
+/// ditto
+void setTimes(R)(auto ref R name,
+ SysTime accessTime,
+ SysTime modificationTime)
+if (isConvertibleToString!R)
+{
+ setTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
+}
+private void setTimesImpl(scope const(char)[] names, scope const(FSChar)* namez,
+ SysTime accessTime, SysTime modificationTime) @trusted
+{
+ version (Windows)
+ {
+ import std.datetime.systime : SysTimeToFILETIME;
const ta = SysTimeToFILETIME(accessTime);
const tm = SysTimeToFILETIME(modificationTime);
alias defaults =
@@ -1252,75 +1572,55 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
FILE_ATTRIBUTE_DIRECTORY |
FILE_FLAG_BACKUP_SEMANTICS,
HANDLE.init);
- auto h = trustedCreateFileW(namez, defaults);
+ auto h = CreateFileW(namez, defaults);
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
- alias names = name;
- else
- string names = null;
cenforce(h != INVALID_HANDLE_VALUE, names, namez);
scope(exit)
- cenforce(trustedCloseHandle(h), names, namez);
+ cenforce(CloseHandle(h), names, namez);
- cenforce(trustedSetFileTime(h, null, ta, tm), names, namez);
+ cenforce(SetFileTime(h, null, &ta, &tm), names, namez);
}
- else version (Posix)
+ else
{
- auto namez = name.tempCString!FSChar();
static if (is(typeof(&utimensat)))
{
- static auto trustedUtimensat(int fd, const(FSChar)* namez, const ref timespec[2] times, int flags) @trusted
- {
- return utimensat(fd, namez, times, flags);
- }
timespec[2] t = void;
-
t[0] = accessTime.toTimeSpec();
t[1] = modificationTime.toTimeSpec();
-
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
- alias names = name;
- else
- string names = null;
- cenforce(trustedUtimensat(AT_FDCWD, namez, t, 0) == 0, names, namez);
+ cenforce(utimensat(AT_FDCWD, namez, t, 0) == 0, names, namez);
}
else
{
- static auto trustedUtimes(const(FSChar)* namez, const ref timeval[2] times) @trusted
+ version (Darwin)
{
- return utimes(namez, times);
+ // Set modification & access times with setattrlist to avoid precision loss.
+ attrlist attrs = { bitmapcount: 5, reserved: 0,
+ commonattr: ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME,
+ volattr: 0, dirattr: 0, fileattr: 0, forkattr: 0 };
+ timespec[2] attrbuf = [modificationTime.toTimeSpec(), accessTime.toTimeSpec()];
+ if (0 == setattrlist(namez, attrs, &attrbuf, attrbuf.sizeof, 0))
+ return;
+ if (.errno != ENOTSUP)
+ cenforce(false, names, namez);
+ // Not all volumes support setattrlist. In such cases
+ // fall through to the utimes implementation.
}
timeval[2] t = void;
-
t[0] = accessTime.toTimeVal();
t[1] = modificationTime.toTimeVal();
-
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
- alias names = name;
- else
- string names = null;
- cenforce(trustedUtimes(namez, t) == 0, names, namez);
+ cenforce(utimes(namez, t) == 0, names, namez);
}
}
}
-/// ditto
-void setTimes(R)(auto ref R name,
- SysTime accessTime,
- SysTime modificationTime)
-if (isConvertibleToString!R)
-{
- setTimes!(StringTypeOf!R)(name, accessTime, modificationTime);
-}
-
@safe unittest
{
if (false) // Test instatiation
setTimes(TestAliasedString("foo"), SysTime.init, SysTime.init);
}
-@system unittest
+@safe unittest
{
import std.stdio : File;
string newdir = deleteme ~ r".dir";
@@ -1356,8 +1656,12 @@ if (isConvertibleToString!R)
/++
Returns the time that the given file was last modified.
+ Params:
+ name = the name of the file to check
+ Returns:
+ A $(REF SysTime,std,datetime,systime).
Throws:
- $(D FileException) if the given file does not exist.
+ $(LREF FileException) if the given file does not exist.
+/
SysTime timeLastModified(R)(R name)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -1381,7 +1685,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
}
stat_t statbuf = void;
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias names = name;
else
string names = null;
@@ -1398,6 +1702,19 @@ if (isConvertibleToString!R)
return timeLastModified!(StringTypeOf!R)(name);
}
+///
+@safe unittest
+{
+ import std.datetime : abs, DateTime, hnsecs, SysTime;
+ scope(exit) deleteme.remove;
+
+ import std.datetime : Clock, seconds;
+ auto currTime = Clock.currTime();
+ enum leeway = 5.seconds;
+ deleteme.write("bb");
+ assert(abs(deleteme.timeLastModified - currTime) <= leeway);
+}
+
@safe unittest
{
static assert(__traits(compiles, timeLastModified(TestAliasedString("foo"))));
@@ -1405,25 +1722,27 @@ if (isConvertibleToString!R)
/++
Returns the time that the given file was last modified. If the
- file does not exist, returns $(D returnIfMissing).
+ file does not exist, returns `returnIfMissing`.
A frequent usage pattern occurs in build automation tools such as
$(HTTP gnu.org/software/make, make) or $(HTTP
en.wikipedia.org/wiki/Apache_Ant, ant). To check whether file $(D
- target) must be rebuilt from file $(D source) (i.e., $(D target) is
- older than $(D source) or does not exist), use the comparison
- below. The code throws a $(D FileException) if $(D source) does not
- exist (as it should). On the other hand, the $(D SysTime.min) default
- makes a non-existing $(D target) seem infinitely old so the test
+ target) must be rebuilt from file `source` (i.e., `target` is
+ older than `source` or does not exist), use the comparison
+ below. The code throws a $(LREF FileException) if `source` does not
+ exist (as it should). On the other hand, the `SysTime.min` default
+ makes a non-existing `target` seem infinitely old so the test
correctly prompts building it.
Params:
- name = The _name of the file to get the modification time for.
+ name = The name of the file to get the modification time for.
returnIfMissing = The time to return if the given file does not exist.
+ Returns:
+ A $(REF SysTime,std,datetime,systime).
Example:
--------------------
-if (timeLastModified(source) >= timeLastModified(target, SysTime.min))
+if (source.timeLastModified >= target.timeLastModified(SysTime.min))
{
// must (re)build
}
@@ -1463,9 +1782,77 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
}
}
+///
@safe unittest
{
- //std.process.system("echo a > deleteme") == 0 || assert(false);
+ import std.datetime : SysTime;
+
+ assert("file.does.not.exist".timeLastModified(SysTime.min) == SysTime.min);
+
+ auto source = deleteme ~ "source";
+ auto target = deleteme ~ "target";
+ scope(exit) source.remove, target.remove;
+
+ source.write(".");
+ assert(target.timeLastModified(SysTime.min) < source.timeLastModified);
+ target.write(".");
+ assert(target.timeLastModified(SysTime.min) >= source.timeLastModified);
+}
+
+version (StdDdoc)
+{
+ /++
+ $(BLUE This function is POSIX-Only.)
+
+ Returns the time that the given file was last modified.
+ Params:
+ statbuf = stat_t retrieved from file.
+ +/
+ SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow {assert(false);}
+ /++
+ $(BLUE This function is POSIX-Only.)
+
+ Returns the time that the given file was last accessed.
+ Params:
+ statbuf = stat_t retrieved from file.
+ +/
+ SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow {assert(false);}
+ /++
+ $(BLUE This function is POSIX-Only.)
+
+ Returns the time that the given file was last changed.
+ Params:
+ statbuf = stat_t retrieved from file.
+ +/
+ SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow {assert(false);}
+}
+else version (Posix)
+{
+ SysTime timeLastModified()(auto ref stat_t statbuf) pure nothrow
+ {
+ return statTimeToStdTime!'m'(statbuf);
+ }
+ SysTime timeLastAccessed()(auto ref stat_t statbuf) pure nothrow
+ {
+ return statTimeToStdTime!'a'(statbuf);
+ }
+ SysTime timeStatusChanged()(auto ref stat_t statbuf) pure nothrow
+ {
+ return statTimeToStdTime!'c'(statbuf);
+ }
+
+ @safe unittest
+ {
+ stat_t statbuf;
+ // check that both lvalues and rvalues work
+ timeLastAccessed(statbuf);
+ cast(void) timeLastAccessed(stat_t.init);
+ }
+}
+
+@safe unittest
+{
+ //std.process.executeShell("echo a > deleteme");
if (exists(deleteme))
remove(deleteme);
@@ -1502,7 +1889,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
version (FreeBSD) {} else
version (DragonFlyBSD) {} else
version (OSX) {} else
-@system unittest
+@safe unittest
{
import core.thread;
@@ -1517,7 +1904,7 @@ version (OSX) {} else
remove(deleteme);
assert(time != lastTime);
lastTime = time;
- Thread.sleep(20.msecs);
+ () @trusted { Thread.sleep(20.msecs); }();
}
}
@@ -1543,6 +1930,19 @@ if (isConvertibleToString!R)
return exists!(StringTypeOf!R)(name);
}
+///
+@safe unittest
+{
+ auto f = deleteme ~ "does.not.exist";
+ assert(!f.exists);
+
+ f.write("hello");
+ assert(f.exists);
+
+ f.remove;
+ assert(!f.exists);
+}
+
private bool existsImpl(const(FSChar)* namez) @trusted nothrow @nogc
{
version (Windows)
@@ -1580,16 +1980,18 @@ private bool existsImpl(const(FSChar)* namez) @trusted nothrow @nogc
static assert(0);
}
+///
@safe unittest
{
- assert(exists("."));
- assert(!exists("this file does not exist"));
- write(deleteme, "a\n");
- scope(exit) { assert(exists(deleteme)); remove(deleteme); }
- assert(exists(deleteme));
+ assert(".".exists);
+ assert(!"this file does not exist".exists);
+ deleteme.write("a\n");
+ scope(exit) deleteme.remove;
+ assert(deleteme.exists);
}
-@safe unittest // Bugzilla 16573
+// https://issues.dlang.org/show_bug.cgi?id=16573
+@safe unittest
{
enum S : string { foo = "foo" }
assert(__traits(compiles, S.foo.exists));
@@ -1598,22 +2000,23 @@ private bool existsImpl(const(FSChar)* namez) @trusted nothrow @nogc
/++
Returns the attributes of the given file.
- Note that the file attributes on Windows and Posix systems are
+ Note that the file attributes on Windows and POSIX systems are
completely different. On Windows, they're what is returned by
$(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx,
- GetFileAttributes), whereas on Posix systems, they're the $(LUCKY
- st_mode) value which is part of the $(D stat struct) gotten by
- calling the $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, $(D stat))
+ GetFileAttributes), whereas on POSIX systems, they're the
+ `st_mode` value which is part of the $(D stat struct) gotten by
+ calling the $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, `stat`)
function.
- On Posix systems, if the given file is a symbolic link, then
+ On POSIX systems, if the given file is a symbolic link, then
attributes are the attributes of the file pointed to by the symbolic
link.
Params:
- name = The file to get the attributes of.
-
- Throws: $(D FileException) on error.
+ name = The file to get the attributes of.
+ Returns:
+ The attributes of the file as a `uint`.
+ Throws: $(LREF FileException) on error.
+/
uint getAttributes(R)(R name)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -1628,7 +2031,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
}
immutable result = trustedGetFileAttributesW(namez);
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias names = name;
else
string names = null;
@@ -1645,7 +2048,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
}
stat_t statbuf = void;
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias names = name;
else
string names = null;
@@ -1662,6 +2065,40 @@ if (isConvertibleToString!R)
return getAttributes!(StringTypeOf!R)(name);
}
+/// getAttributes with a file
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto f = deleteme ~ "file";
+ scope(exit) f.remove;
+
+ assert(!f.exists);
+ assertThrown!FileException(f.getAttributes);
+
+ f.write(".");
+ auto attributes = f.getAttributes;
+ assert(!attributes.attrIsDir);
+ assert(attributes.attrIsFile);
+}
+
+/// getAttributes with a directory
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto dir = deleteme ~ "dir";
+ scope(exit) dir.rmdir;
+
+ assert(!dir.exists);
+ assertThrown!FileException(dir.getAttributes);
+
+ dir.mkdir;
+ auto attributes = dir.getAttributes;
+ assert(attributes.attrIsDir);
+ assert(!attributes.attrIsFile);
+}
+
@safe unittest
{
static assert(__traits(compiles, getAttributes(TestAliasedString(null))));
@@ -1684,7 +2121,7 @@ if (isConvertibleToString!R)
the attributes
Throws:
- $(D FileException) on error.
+ $(LREF FileException) on error.
+/
uint getLinkAttributes(R)(R name)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -1702,7 +2139,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
return lstat(namez, &buf);
}
stat_t lstatbuf = void;
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias names = name;
else
string names = null;
@@ -1718,6 +2155,64 @@ if (isConvertibleToString!R)
return getLinkAttributes!(StringTypeOf!R)(name);
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto source = deleteme ~ "source";
+ auto target = deleteme ~ "target";
+
+ assert(!source.exists);
+ assertThrown!FileException(source.getLinkAttributes);
+
+ // symlinking isn't available on Windows
+ version (Posix)
+ {
+ scope(exit) source.remove, target.remove;
+
+ target.write("target");
+ target.symlink(source);
+ assert(source.readText == "target");
+ assert(source.isSymlink);
+ assert(source.getLinkAttributes.attrIsSymlink);
+ }
+}
+
+/// if the file is no symlink, getLinkAttributes behaves like getAttributes
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto f = deleteme ~ "file";
+ scope(exit) f.remove;
+
+ assert(!f.exists);
+ assertThrown!FileException(f.getLinkAttributes);
+
+ f.write(".");
+ auto attributes = f.getLinkAttributes;
+ assert(!attributes.attrIsDir);
+ assert(attributes.attrIsFile);
+}
+
+/// if the file is no symlink, getLinkAttributes behaves like getAttributes
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto dir = deleteme ~ "dir";
+ scope(exit) dir.rmdir;
+
+ assert(!dir.exists);
+ assertThrown!FileException(dir.getLinkAttributes);
+
+ dir.mkdir;
+ auto attributes = dir.getLinkAttributes;
+ assert(attributes.attrIsDir);
+ assert(!attributes.attrIsFile);
+}
+
@safe unittest
{
static assert(__traits(compiles, getLinkAttributes(TestAliasedString(null))));
@@ -1726,12 +2221,16 @@ if (isConvertibleToString!R)
/++
Set the _attributes of the given file.
+ For example, a programmatic equivalent of Unix's `chmod +x name`
+ to make a file executable is
+ `name.setAttributes(name.getAttributes | octal!700)`.
+
Params:
name = the file _name
attributes = the _attributes to set the file to
Throws:
- $(D FileException) if the given file does not exist.
+ $(LREF FileException) if the given file does not exist.
+/
void setAttributes(R)(R name, uint attributes)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -1744,7 +2243,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
{
return SetFileAttributesW(namez, dwFileAttributes);
}
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias names = name;
else
string names = null;
@@ -1758,7 +2257,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
return chmod(namez, mode);
}
assert(attributes <= mode_t.max);
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias names = name;
else
string names = null;
@@ -1778,6 +2277,58 @@ if (isConvertibleToString!R)
static assert(__traits(compiles, setAttributes(TestAliasedString(null), 0)));
}
+/// setAttributes with a file
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.conv : octal;
+
+ auto f = deleteme ~ "file";
+ version (Posix)
+ {
+ scope(exit) f.remove;
+
+ assert(!f.exists);
+ assertThrown!FileException(f.setAttributes(octal!777));
+
+ f.write(".");
+ auto attributes = f.getAttributes;
+ assert(!attributes.attrIsDir);
+ assert(attributes.attrIsFile);
+
+ f.setAttributes(octal!777);
+ attributes = f.getAttributes;
+
+ assert((attributes & 1023) == octal!777);
+ }
+}
+
+/// setAttributes with a directory
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.conv : octal;
+
+ auto dir = deleteme ~ "dir";
+ version (Posix)
+ {
+ scope(exit) dir.rmdir;
+
+ assert(!dir.exists);
+ assertThrown!FileException(dir.setAttributes(octal!777));
+
+ dir.mkdir;
+ auto attributes = dir.getAttributes;
+ assert(attributes.attrIsDir);
+ assert(!attributes.attrIsFile);
+
+ dir.setAttributes(octal!777);
+ attributes = dir.getAttributes;
+
+ assert((attributes & 1023) == octal!777);
+ }
+}
+
/++
Returns whether the given file is a directory.
@@ -1788,13 +2339,7 @@ if (isConvertibleToString!R)
true if name specifies a directory
Throws:
- $(D FileException) if the given file does not exist.
-
-Example:
---------------------
-assert(!"/etc/fonts/fonts.conf".isDir);
-assert("/usr/share/include".isDir);
---------------------
+ $(LREF FileException) if the given file does not exist.
+/
@property bool isDir(R)(R name)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -1817,6 +2362,26 @@ if (isConvertibleToString!R)
return name.isDir!(StringTypeOf!R);
}
+///
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto dir = deleteme ~ "dir";
+ auto f = deleteme ~ "f";
+ scope(exit) dir.rmdir, f.remove;
+
+ assert(!dir.exists);
+ assertThrown!FileException(dir.isDir);
+
+ dir.mkdir;
+ assert(dir.isDir);
+
+ f.write(".");
+ assert(!f.isDir);
+}
+
@safe unittest
{
static assert(__traits(compiles, TestAliasedString(null).isDir));
@@ -1842,7 +2407,7 @@ if (isConvertibleToString!R)
}
}
-@system unittest
+@safe unittest
{
version (Windows)
enum dir = "C:\\Program Files\\";
@@ -1865,13 +2430,7 @@ if (isConvertibleToString!R)
Returns:
true if attributes specifies a directory
-
-Example:
---------------------
-assert(!attrIsDir(getAttributes("/etc/fonts/fonts.conf")));
-assert(!attrIsDir(getLinkAttributes("/etc/fonts/fonts.conf")));
---------------------
- +/
++/
bool attrIsDir(uint attributes) @safe pure nothrow @nogc
{
version (Windows)
@@ -1884,6 +2443,27 @@ bool attrIsDir(uint attributes) @safe pure nothrow @nogc
}
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto dir = deleteme ~ "dir";
+ auto f = deleteme ~ "f";
+ scope(exit) dir.rmdir, f.remove;
+
+ assert(!dir.exists);
+ assertThrown!FileException(dir.getAttributes.attrIsDir);
+
+ dir.mkdir;
+ assert(dir.isDir);
+ assert(dir.getAttributes.attrIsDir);
+
+ f.write(".");
+ assert(!f.isDir);
+ assert(!f.getAttributes.attrIsDir);
+}
+
@safe unittest
{
version (Windows)
@@ -1921,15 +2501,15 @@ bool attrIsDir(uint attributes) @safe pure nothrow @nogc
Returns whether the given file (or directory) is a file.
On Windows, if a file is not a directory, then it's a file. So,
- either $(D isFile) or $(D isDir) will return true for any given file.
+ either `isFile` or `isDir` will return true for any given file.
- On Posix systems, if $(D isFile) is $(D true), that indicates that the file
- is a regular file (e.g. not a block not device). So, on Posix systems, it's
- possible for both $(D isFile) and $(D isDir) to be $(D false) for a
+ On POSIX systems, if `isFile` is `true`, that indicates that the file
+ is a regular file (e.g. not a block not device). So, on POSIX systems, it's
+ possible for both `isFile` and `isDir` to be `false` for a
particular file (in which case, it's a special file). You can use
- $(D getAttributes) to get the attributes to figure out what type of special
- it is, or you can use $(D DirEntry) to get at its $(D statBuf), which is the
- result from $(D stat). In either case, see the man page for $(D stat) for
+ `getAttributes` to get the attributes to figure out what type of special
+ it is, or you can use `DirEntry` to get at its `statBuf`, which is the
+ result from `stat`. In either case, see the man page for `stat` for
more information.
Params:
@@ -1939,14 +2519,8 @@ bool attrIsDir(uint attributes) @safe pure nothrow @nogc
true if name specifies a file
Throws:
- $(D FileException) if the given file does not exist.
-
-Example:
---------------------
-assert("/etc/fonts/fonts.conf".isFile);
-assert(!"/usr/share/include".isFile);
---------------------
- +/
+ $(LREF FileException) if the given file does not exist.
++/
@property bool isFile(R)(R name)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
!isConvertibleToString!R)
@@ -1964,7 +2538,27 @@ if (isConvertibleToString!R)
return isFile!(StringTypeOf!R)(name);
}
-@system unittest // bugzilla 15658
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto dir = deleteme ~ "dir";
+ auto f = deleteme ~ "f";
+ scope(exit) dir.rmdir, f.remove;
+
+ dir.mkdir;
+ assert(!dir.isFile);
+
+ assert(!f.exists);
+ assertThrown!FileException(f.isFile);
+
+ f.write(".");
+ assert(f.isFile);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15658
+@safe unittest
{
DirEntry e = DirEntry(".");
static assert(is(typeof(isFile(e))));
@@ -2000,15 +2594,15 @@ if (isConvertibleToString!R)
Returns whether the given file _attributes are for a file.
On Windows, if a file is not a directory, it's a file. So, either
- $(D attrIsFile) or $(D attrIsDir) will return $(D true) for the
+ `attrIsFile` or `attrIsDir` will return `true` for the
_attributes of any given file.
- On Posix systems, if $(D attrIsFile) is $(D true), that indicates that the
- file is a regular file (e.g. not a block not device). So, on Posix systems,
- it's possible for both $(D attrIsFile) and $(D attrIsDir) to be $(D false)
+ On POSIX systems, if `attrIsFile` is `true`, that indicates that the
+ file is a regular file (e.g. not a block not device). So, on POSIX systems,
+ it's possible for both `attrIsFile` and `attrIsDir` to be `false`
for a particular file (in which case, it's a special file). If a file is a
special file, you can use the _attributes to check what type of special file
- it is (see the man page for $(D stat) for more information).
+ it is (see the man page for `stat` for more information).
Params:
attributes = The file _attributes.
@@ -2034,6 +2628,27 @@ bool attrIsFile(uint attributes) @safe pure nothrow @nogc
}
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto dir = deleteme ~ "dir";
+ auto f = deleteme ~ "f";
+ scope(exit) dir.rmdir, f.remove;
+
+ dir.mkdir;
+ assert(!dir.isFile);
+ assert(!dir.getAttributes.attrIsFile);
+
+ assert(!f.exists);
+ assertThrown!FileException(f.getAttributes.attrIsFile);
+
+ f.write(".");
+ assert(f.isFile);
+ assert(f.getAttributes.attrIsFile);
+}
+
@safe unittest
{
version (Windows)
@@ -2070,7 +2685,7 @@ bool attrIsFile(uint attributes) @safe pure nothrow @nogc
/++
Returns whether the given file is a symbolic link.
- On Windows, returns $(D true) when the file is either a symbolic link or a
+ On Windows, returns `true` when the file is either a symbolic link or a
junction point.
Params:
@@ -2080,7 +2695,7 @@ bool attrIsFile(uint attributes) @safe pure nothrow @nogc
true if name is a symbolic link
Throws:
- $(D FileException) if the given file does not exist.
+ $(LREF FileException) if the given file does not exist.
+/
@property bool isSymlink(R)(R name)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -2104,6 +2719,30 @@ if (isConvertibleToString!R)
static assert(__traits(compiles, TestAliasedString(null).isSymlink));
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto source = deleteme ~ "source";
+ auto target = deleteme ~ "target";
+
+ assert(!source.exists);
+ assertThrown!FileException(source.isSymlink);
+
+ // symlinking isn't available on Windows
+ version (Posix)
+ {
+ scope(exit) source.remove, target.remove;
+
+ target.write("target");
+ target.symlink(source);
+ assert(source.readText == "target");
+ assert(source.isSymlink);
+ assert(source.getLinkAttributes.attrIsSymlink);
+ }
+}
+
@system unittest
{
version (Windows)
@@ -2181,7 +2820,7 @@ if (isConvertibleToString!R)
/++
Returns whether the given file attributes are for a symbolic link.
- On Windows, return $(D true) when the file is either a symbolic link or a
+ On Windows, return `true` when the file is either a symbolic link or a
junction point.
Params:
@@ -2206,10 +2845,38 @@ bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc
return (attributes & S_IFMT) == S_IFLNK;
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
-/****************************************************
- * Change directory to $(D pathname).
- * Throws: $(D FileException) on error.
+ auto source = deleteme ~ "source";
+ auto target = deleteme ~ "target";
+
+ assert(!source.exists);
+ assertThrown!FileException(source.getLinkAttributes.attrIsSymlink);
+
+ // symlinking isn't available on Windows
+ version (Posix)
+ {
+ scope(exit) source.remove, target.remove;
+
+ target.write("target");
+ target.symlink(source);
+ assert(source.readText == "target");
+ assert(source.isSymlink);
+ assert(source.getLinkAttributes.attrIsSymlink);
+ }
+}
+
+/**
+Change directory to `pathname`. Equivalent to `cd` on
+Windows and POSIX.
+
+Params:
+ pathname = the directory to step into
+
+Throws: $(LREF FileException) on error.
*/
void chdir(R)(R pathname)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -2232,7 +2899,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
return core.sys.posix.unistd.chdir(pathz) == 0;
}
}
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias pathStr = pathname;
else
string pathStr = null;
@@ -2246,16 +2913,41 @@ if (isConvertibleToString!R)
return chdir!(StringTypeOf!R)(pathname);
}
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.sorting : sort;
+ import std.array : array;
+ import std.path : buildPath;
+
+ auto cwd = getcwd;
+ auto dir = deleteme ~ "dir";
+ dir.mkdir;
+ scope(exit) cwd.chdir, dir.rmdirRecurse;
+
+ dir.buildPath("a").write(".");
+ dir.chdir; // step into dir
+ "b".write(".");
+ assert(dirEntries(".", SpanMode.shallow).array.sort.equal(
+ [".".buildPath("a"), ".".buildPath("b")]
+ ));
+}
+
@safe unittest
{
static assert(__traits(compiles, chdir(TestAliasedString(null))));
}
-/****************************************************
-Make directory $(D pathname).
+/**
+Make a new directory `pathname`.
-Throws: $(D FileException) on Posix or $(D WindowsException) on Windows
- if an error occured.
+Params:
+ pathname = the path of the directory to make
+
+Throws:
+ $(LREF FileException) on POSIX or $(LREF WindowsException) on Windows
+ if an error occured.
*/
void mkdir(R)(R pathname)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -2270,7 +2962,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
{
return CreateDirectoryW(pathz, null);
}
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias pathStr = pathname;
else
string pathStr = null;
@@ -2284,7 +2976,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
{
return core.sys.posix.sys.stat.mkdir(pathz, mode);
}
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias pathStr = pathname;
else
string pathStr = null;
@@ -2305,10 +2997,29 @@ if (isConvertibleToString!R)
static assert(__traits(compiles, mkdir(TestAliasedString(null))));
}
+///
+@safe unittest
+{
+ import std.file : mkdir;
+
+ auto dir = deleteme ~ "dir";
+ scope(exit) dir.rmdir;
+
+ dir.mkdir;
+ assert(dir.exists);
+}
+
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ assertThrown("a/b/c/d/e".mkdir);
+}
+
// Same as mkdir but ignores "already exists" errors.
// Returns: "true" if the directory was created,
// "false" if it already existed.
-private bool ensureDirExists()(in char[] pathname)
+private bool ensureDirExists()(scope const(char)[] pathname)
{
import std.exception : enforce;
const pathz = pathname.tempCString!FSChar();
@@ -2331,16 +3042,18 @@ private bool ensureDirExists()(in char[] pathname)
return false;
}
-/****************************************************
- * Make directory and all parent directories as needed.
- *
- * Does nothing if the directory specified by
- * $(D pathname) already exists.
- *
- * Throws: $(D FileException) on error.
- */
+/**
+Make directory and all parent directories as needed.
+
+Does nothing if the directory specified by
+`pathname` already exists.
-void mkdirRecurse(in char[] pathname) @safe
+Params:
+ pathname = the full path of the directory to create
+
+Throws: $(LREF FileException) on error.
+ */
+void mkdirRecurse(scope const(char)[] pathname) @safe
{
import std.path : dirName, baseName;
@@ -2355,6 +3068,36 @@ void mkdirRecurse(in char[] pathname) @safe
}
}
+///
+@safe unittest
+{
+ import std.path : buildPath;
+
+ auto dir = deleteme ~ "dir";
+ scope(exit) dir.rmdirRecurse;
+
+ dir.mkdir;
+ assert(dir.exists);
+ dir.mkdirRecurse; // does nothing
+
+ // creates all parent directories as needed
+ auto nested = dir.buildPath("a", "b", "c");
+ nested.mkdirRecurse;
+ assert(nested.exists);
+}
+
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ scope(exit) deleteme.remove;
+ deleteme.write("a");
+
+ // cannot make directory as it's already a file
+ assertThrown!FileException(deleteme.mkdirRecurse);
+}
+
@safe unittest
{
import std.exception : assertThrown;
@@ -2383,7 +3126,7 @@ void mkdirRecurse(in char[] pathname) @safe
assertThrown!FileException(mkdirRecurse(`1:\foobar`));
}
- // bug3570
+ // https://issues.dlang.org/show_bug.cgi?id=3570
{
immutable basepath = deleteme ~ "_dir";
version (Windows)
@@ -2403,12 +3146,12 @@ void mkdirRecurse(in char[] pathname) @safe
}
/****************************************************
-Remove directory $(D pathname).
+Remove directory `pathname`.
Params:
pathname = Range or string specifying the directory name
-Throws: $(D FileException) on error.
+Throws: $(LREF FileException) on error.
*/
void rmdir(R)(R pathname)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
@@ -2431,7 +3174,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
return core.sys.posix.unistd.rmdir(pathz) == 0;
}
}
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias pathStr = pathname;
else
string pathStr = null;
@@ -2450,8 +3193,19 @@ if (isConvertibleToString!R)
static assert(__traits(compiles, rmdir(TestAliasedString(null))));
}
+///
+@safe unittest
+{
+ auto dir = deleteme ~ "dir";
+
+ dir.mkdir;
+ assert(dir.exists);
+ dir.rmdir;
+ assert(!dir.exists);
+}
+
/++
- $(BLUE This function is Posix-Only.)
+ $(BLUE This function is POSIX-Only.)
Creates a symbolic _link (_symlink).
@@ -2463,7 +3217,7 @@ if (isConvertibleToString!R)
current working directory.
Throws:
- $(D FileException) on error (which includes if the _symlink already
+ $(LREF FileException) on error (which includes if the _symlink already
exists).
+/
version (StdDdoc) void symlink(RO, RL)(RO original, RL link)
@@ -2545,7 +3299,7 @@ version (Posix) @safe unittest
/++
- $(BLUE This function is Posix-Only.)
+ $(BLUE This function is POSIX-Only.)
Returns the path to the file pointed to by a symlink. Note that the
path could be either relative or absolute depending on the symlink.
@@ -2553,7 +3307,7 @@ version (Posix) @safe unittest
working directory.
Throws:
- $(D FileException) on error.
+ $(LREF FileException) on error.
+/
version (StdDdoc) string readLink(R)(R link)
if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) ||
@@ -2642,7 +3396,7 @@ version (Posix) @system unittest // input range of dchars
import std.utf : byChar;
immutable string link = deleteme ~ "/l";
symlink("f", link);
- InputRange!dchar linkr = inputRangeObject(link);
+ InputRange!(ElementType!string) linkr = inputRangeObject(link);
alias R = typeof(linkr);
static assert(isInputRange!R);
static assert(!isForwardRange!R);
@@ -2652,11 +3406,12 @@ version (Posix) @system unittest // input range of dchars
/****************************************************
* Get the current working directory.
- * Throws: $(D FileException) on error.
+ * Throws: $(LREF FileException) on error.
*/
-version (Windows) string getcwd()
+version (Windows) string getcwd() @trusted
{
import std.conv : to;
+ import std.experimental.checkedint : checked;
/* GetCurrentDirectory's return value:
1. function succeeds: the number of characters that are written to
the buffer, not including the terminating null character.
@@ -2664,7 +3419,11 @@ version (Windows) string getcwd()
3. the buffer (lpBuffer) is not large enough: the required size of
the buffer, in characters, including the null-terminating character.
*/
- wchar[4096] buffW = void; //enough for most common case
+ version (StdUnittest)
+ enum BUF_SIZE = 10; // trigger reallocation code
+ else
+ enum BUF_SIZE = 4096; // enough for most common case
+ wchar[BUF_SIZE] buffW = void;
immutable n = cenforce(GetCurrentDirectoryW(to!DWORD(buffW.length), buffW.ptr),
"getcwd");
// we can do it because toUTFX always produces a fresh string
@@ -2674,14 +3433,15 @@ version (Windows) string getcwd()
}
else //staticBuff isn't enough
{
- auto ptr = cast(wchar*) malloc(wchar.sizeof * n);
+ auto cn = checked(n);
+ auto ptr = cast(wchar*) malloc((cn * wchar.sizeof).get);
scope(exit) free(ptr);
- immutable n2 = GetCurrentDirectoryW(n, ptr);
- cenforce(n2 && n2 < n, "getcwd");
+ immutable n2 = GetCurrentDirectoryW(cn.get, ptr);
+ cenforce(n2 && n2 < cn, "getcwd");
return ptr[0 .. n2].to!string;
}
}
-else version (Solaris) string getcwd()
+else version (Solaris) string getcwd() @trusted
{
/* BUF_SIZE >= PATH_MAX */
enum BUF_SIZE = 4096;
@@ -2691,7 +3451,7 @@ else version (Solaris) string getcwd()
scope(exit) core.stdc.stdlib.free(p);
return p[0 .. core.stdc.string.strlen(p)].idup;
}
-else version (Posix) string getcwd()
+else version (Posix) string getcwd() @trusted
{
auto p = cenforce(core.sys.posix.unistd.getcwd(null, 0),
"cannot get cwd");
@@ -2699,31 +3459,27 @@ else version (Posix) string getcwd()
return p[0 .. core.stdc.string.strlen(p)].idup;
}
-@system unittest
+///
+@safe unittest
{
auto s = getcwd();
assert(s.length);
}
-version (OSX)
- private extern (C) int _NSGetExecutablePath(char* buf, uint* bufsize);
-else version (FreeBSD)
- private extern (C) int sysctl (const int* name, uint namelen, void* oldp,
- size_t* oldlenp, const void* newp, size_t newlen);
-else version (NetBSD)
- private extern (C) int sysctl (const int* name, uint namelen, void* oldp,
- size_t* oldlenp, const void* newp, size_t newlen);
-
/**
* Returns the full path of the current executable.
*
+ * Returns:
+ * The path of the executable as a `string`.
+ *
* Throws:
* $(REF1 Exception, object)
*/
-@trusted string thisExePath ()
+@trusted string thisExePath()
{
- version (OSX)
+ version (Darwin)
{
+ import core.sys.darwin.mach.dyld : _NSGetExecutablePath;
import core.sys.posix.stdlib : realpath;
import std.conv : to;
import std.exception : errnoEnforce;
@@ -2867,6 +3623,7 @@ else version (NetBSD)
static assert(0, "thisExePath is not supported on this platform");
}
+///
@safe unittest
{
import std.path : isAbsolute;
@@ -2880,18 +3637,19 @@ else version (NetBSD)
version (StdDdoc)
{
/++
- Info on a file, similar to what you'd get from stat on a Posix system.
+ Info on a file, similar to what you'd get from stat on a POSIX system.
+/
struct DirEntry
{
+ @safe:
/++
- Constructs a $(D DirEntry) for the given file (or directory).
+ Constructs a `DirEntry` for the given file (or directory).
Params:
path = The file (or directory) to get a DirEntry for.
Throws:
- $(D FileException) if the file does not exist.
+ $(LREF FileException) if the file does not exist.
+/
this(string path);
@@ -2905,7 +3663,7 @@ version (StdDdoc)
}
/++
- Returns the path to the file represented by this $(D DirEntry).
+ Returns the path to the file represented by this `DirEntry`.
Example:
--------------------
@@ -2916,11 +3674,11 @@ auto de2 = DirEntry("/usr/share/include");
assert(de2.name == "/usr/share/include");
--------------------
+/
- @property string name() const;
+ @property string name() const return scope;
/++
- Returns whether the file represented by this $(D DirEntry) is a
+ Returns whether the file represented by this `DirEntry` is a
directory.
Example:
@@ -2932,20 +3690,20 @@ auto de2 = DirEntry("/usr/share/include");
assert(de2.isDir);
--------------------
+/
- @property bool isDir();
+ @property bool isDir() scope;
/++
- Returns whether the file represented by this $(D DirEntry) is a file.
+ Returns whether the file represented by this `DirEntry` is a file.
On Windows, if a file is not a directory, then it's a file. So,
- either $(D isFile) or $(D isDir) will return $(D true).
+ either `isFile` or `isDir` will return `true`.
- On Posix systems, if $(D isFile) is $(D true), that indicates that
+ On POSIX systems, if `isFile` is `true`, that indicates that
the file is a regular file (e.g. not a block not device). So, on
- Posix systems, it's possible for both $(D isFile) and $(D isDir) to
- be $(D false) for a particular file (in which case, it's a special
- file). You can use $(D attributes) or $(D statBuf) to get more
+ POSIX systems, it's possible for both `isFile` and `isDir` to
+ be `false` for a particular file (in which case, it's a special
+ file). You can use `attributes` or `statBuf` to get more
information about a special file (see the stat man page for more
details).
@@ -2958,91 +3716,100 @@ auto de2 = DirEntry("/usr/share/include");
assert(!de2.isFile);
--------------------
+/
- @property bool isFile();
+ @property bool isFile() scope;
/++
- Returns whether the file represented by this $(D DirEntry) is a
+ Returns whether the file represented by this `DirEntry` is a
symbolic link.
- On Windows, return $(D true) when the file is either a symbolic
+ On Windows, return `true` when the file is either a symbolic
link or a junction point.
+/
- @property bool isSymlink();
+ @property bool isSymlink() scope;
/++
- Returns the size of the the file represented by this $(D DirEntry)
+ Returns the size of the the file represented by this `DirEntry`
in bytes.
+/
- @property ulong size();
+ @property ulong size() scope;
/++
$(BLUE This function is Windows-Only.)
Returns the creation time of the file represented by this
- $(D DirEntry).
+ `DirEntry`.
+/
- @property SysTime timeCreated() const;
+ @property SysTime timeCreated() const scope;
/++
- Returns the time that the file represented by this $(D DirEntry) was
+ Returns the time that the file represented by this `DirEntry` was
last accessed.
Note that many file systems do not update the access time for files
(generally for performance reasons), so there's a good chance that
- $(D timeLastAccessed) will return the same value as
- $(D timeLastModified).
+ `timeLastAccessed` will return the same value as
+ `timeLastModified`.
+/
- @property SysTime timeLastAccessed();
+ @property SysTime timeLastAccessed() scope;
/++
- Returns the time that the file represented by this $(D DirEntry) was
+ Returns the time that the file represented by this `DirEntry` was
last modified.
+/
- @property SysTime timeLastModified();
+ @property SysTime timeLastModified() scope;
/++
- Returns the _attributes of the file represented by this $(D DirEntry).
+ $(BLUE This function is POSIX-Only.)
- Note that the file _attributes on Windows and Posix systems are
+ Returns the time that the file represented by this `DirEntry` was
+ last changed (not only in contents, but also in permissions or ownership).
+ +/
+ @property SysTime timeStatusChanged() const scope;
+
+ /++
+ Returns the _attributes of the file represented by this `DirEntry`.
+
+ Note that the file _attributes on Windows and POSIX systems are
completely different. On, Windows, they're what is returned by
- $(D GetFileAttributes)
+ `GetFileAttributes`
$(HTTP msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx, GetFileAttributes)
- Whereas, an Posix systems, they're the $(D st_mode) value which is
- part of the $(D stat) struct gotten by calling $(D stat).
+ Whereas, an POSIX systems, they're the `st_mode` value which is
+ part of the `stat` struct gotten by calling `stat`.
- On Posix systems, if the file represented by this $(D DirEntry) is a
+ On POSIX systems, if the file represented by this `DirEntry` is a
symbolic link, then _attributes are the _attributes of the file
pointed to by the symbolic link.
+/
- @property uint attributes();
+ @property uint attributes() scope;
/++
- On Posix systems, if the file represented by this $(D DirEntry) is a
- symbolic link, then $(D linkAttributes) are the attributes of the
- symbolic link itself. Otherwise, $(D linkAttributes) is identical to
- $(D attributes).
+ On POSIX systems, if the file represented by this `DirEntry` is a
+ symbolic link, then `linkAttributes` are the attributes of the
+ symbolic link itself. Otherwise, `linkAttributes` is identical to
+ `attributes`.
- On Windows, $(D linkAttributes) is identical to $(D attributes). It
+ On Windows, `linkAttributes` is identical to `attributes`. It
exists on Windows so that you don't have to special-case code for
Windows when dealing with symbolic links.
+/
- @property uint linkAttributes();
+ @property uint linkAttributes() scope;
version (Windows)
alias stat_t = void*;
/++
- $(BLUE This function is Posix-Only.)
+ $(BLUE This function is POSIX-Only.)
- The $(D stat) struct gotten from calling $(D stat).
+ The `stat` struct gotten from calling `stat`.
+/
- @property stat_t statBuf();
+ @property stat_t statBuf() scope;
}
}
else version (Windows)
{
struct DirEntry
{
+ @safe:
public:
alias name this;
@@ -3065,14 +3832,16 @@ else version (Windows)
}
}
- private this(string path, in WIN32_FIND_DATAW *fd)
+ private this(string path, WIN32_FIND_DATAW *fd) @trusted
{
import core.stdc.wchar_ : wcslen;
import std.conv : to;
import std.datetime.systime : FILETIMEToSysTime;
import std.path : buildPath;
- size_t clength = wcslen(fd.cFileName.ptr);
+ fd.cFileName[$ - 1] = 0;
+
+ size_t clength = wcslen(&fd.cFileName[0]);
_name = buildPath(path, fd.cFileName[0 .. clength].to!string);
_size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
_timeCreated = FILETIMEToSysTime(&fd.ftCreationTime);
@@ -3081,17 +3850,17 @@ else version (Windows)
_attributes = fd.dwFileAttributes;
}
- @property string name() const pure nothrow
+ @property string name() const pure nothrow return scope
{
return _name;
}
- @property bool isDir() const pure nothrow
+ @property bool isDir() const pure nothrow scope
{
return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
- @property bool isFile() const pure nothrow
+ @property bool isFile() const pure nothrow scope
{
//Are there no options in Windows other than directory and file?
//If there are, then this probably isn't the best way to determine
@@ -3099,37 +3868,37 @@ else version (Windows)
return !isDir;
}
- @property bool isSymlink() const pure nothrow
+ @property bool isSymlink() const pure nothrow scope
{
return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
}
- @property ulong size() const pure nothrow
+ @property ulong size() const pure nothrow scope
{
return _size;
}
- @property SysTime timeCreated() const pure nothrow
+ @property SysTime timeCreated() const pure nothrow scope
{
return cast(SysTime)_timeCreated;
}
- @property SysTime timeLastAccessed() const pure nothrow
+ @property SysTime timeLastAccessed() const pure nothrow scope
{
return cast(SysTime)_timeLastAccessed;
}
- @property SysTime timeLastModified() const pure nothrow
+ @property SysTime timeLastModified() const pure nothrow scope
{
return cast(SysTime)_timeLastModified;
}
- @property uint attributes() const pure nothrow
+ @property uint attributes() const pure nothrow scope
{
return _attributes;
}
- @property uint linkAttributes() const pure nothrow
+ @property uint linkAttributes() const pure nothrow scope
{
return _attributes;
}
@@ -3149,6 +3918,7 @@ else version (Posix)
{
struct DirEntry
{
+ @safe:
public:
alias name this;
@@ -3164,7 +3934,7 @@ else version (Posix)
_dTypeSet = false;
}
- private this(string path, core.sys.posix.dirent.dirent* fd)
+ private this(string path, core.sys.posix.dirent.dirent* fd) @safe
{
import std.path : buildPath;
@@ -3203,74 +3973,74 @@ else version (Posix)
}
}
- @property string name() const pure nothrow
+ @property string name() const pure nothrow return scope
{
return _name;
}
- @property bool isDir()
+ @property bool isDir() scope
{
_ensureStatOrLStatDone();
return (_statBuf.st_mode & S_IFMT) == S_IFDIR;
}
- @property bool isFile()
+ @property bool isFile() scope
{
_ensureStatOrLStatDone();
return (_statBuf.st_mode & S_IFMT) == S_IFREG;
}
- @property bool isSymlink()
+ @property bool isSymlink() scope
{
_ensureLStatDone();
return (_lstatMode & S_IFMT) == S_IFLNK;
}
- @property ulong size()
+ @property ulong size() scope
{
_ensureStatDone();
return _statBuf.st_size;
}
- @property SysTime timeStatusChanged()
+ @property SysTime timeStatusChanged() scope
{
_ensureStatDone();
return statTimeToStdTime!'c'(_statBuf);
}
- @property SysTime timeLastAccessed()
+ @property SysTime timeLastAccessed() scope
{
_ensureStatDone();
return statTimeToStdTime!'a'(_statBuf);
}
- @property SysTime timeLastModified()
+ @property SysTime timeLastModified() scope
{
_ensureStatDone();
return statTimeToStdTime!'m'(_statBuf);
}
- @property uint attributes()
+ @property uint attributes() scope
{
_ensureStatDone();
return _statBuf.st_mode;
}
- @property uint linkAttributes()
+ @property uint linkAttributes() scope
{
_ensureLStatDone();
return _lstatMode;
}
- @property stat_t statBuf()
+ @property stat_t statBuf() scope
{
_ensureStatDone();
@@ -3282,18 +4052,14 @@ else version (Posix)
This is to support lazy evaluation, because doing stat's is
expensive and not always needed.
+/
- void _ensureStatDone() @safe
+ void _ensureStatDone() @trusted scope
{
import std.exception : enforce;
- static auto trustedStat(in char[] path, stat_t* buf) @trusted
- {
- return stat(path.tempCString(), buf);
- }
if (_didStat)
return;
- enforce(trustedStat(_name, &_statBuf) == 0,
+ enforce(stat(_name.tempCString(), &_statBuf) == 0,
"Failed to stat file `" ~ _name ~ "'");
_didStat = true;
@@ -3306,12 +4072,12 @@ else version (Posix)
Try both stat and lstat for isFile and isDir
to detect broken symlinks.
+/
- void _ensureStatOrLStatDone()
+ void _ensureStatOrLStatDone() @trusted scope
{
if (_didStat)
return;
- if ( stat(_name.tempCString(), &_statBuf) != 0 )
+ if (stat(_name.tempCString(), &_statBuf) != 0)
{
_ensureLStatDone();
@@ -3328,7 +4094,7 @@ else version (Posix)
This is to support lazy evaluation, because doing stat's is
expensive and not always needed.
+/
- void _ensureLStatDone()
+ void _ensureLStatDone() @trusted scope
{
import std.exception : enforce;
@@ -3336,7 +4102,6 @@ else version (Posix)
return;
stat_t statbuf = void;
-
enforce(lstat(_name.tempCString(), &statbuf) == 0,
"Failed to stat file `" ~ _name ~ "'");
@@ -3348,9 +4113,9 @@ else version (Posix)
string _name; /// The file or directory represented by this DirEntry.
- stat_t _statBuf = void; /// The result of stat().
- uint _lstatMode; /// The stat mode from lstat().
- ubyte _dType; /// The type of the file.
+ stat_t _statBuf = void; /// The result of stat().
+ uint _lstatMode; /// The stat mode from lstat().
+ ubyte _dType; /// The type of the file.
bool _didLStat = false; /// Whether lstat() has been called for this DirEntry.
bool _didStat = false; /// Whether stat() has been called for this DirEntry.
@@ -3413,7 +4178,7 @@ else version (Posix)
core.sys.posix.unistd.symlink((deleteme ~ "_broken_symlink\0").ptr, symfile.ptr);
{
- //Issue 8298
+ // https://issues.dlang.org/show_bug.cgi?id=8298
DirEntry de = DirEntry(symfile);
assert(!de.isFile);
@@ -3444,7 +4209,7 @@ alias PreserveAttributes = Flag!"preserveAttributes";
version (StdDdoc)
{
- /// Defaults to $(D Yes.preserveAttributes) on Windows, and the opposite on all other platforms.
+ /// Defaults to `Yes.preserveAttributes` on Windows, and the opposite on all other platforms.
PreserveAttributes preserveAttributesDefault;
}
else version (Windows)
@@ -3457,9 +4222,9 @@ else
}
/***************************************************
-Copy file $(D from) _to file $(D to). File timestamps are preserved.
-File attributes are preserved, if $(D preserve) equals $(D Yes.preserveAttributes).
-On Windows only $(D Yes.preserveAttributes) (the default on Windows) is supported.
+Copy file `from` _to file `to`. File timestamps are preserved.
+File attributes are preserved, if `preserve` equals `Yes.preserveAttributes`.
+On Windows only `Yes.preserveAttributes` (the default on Windows) is supported.
If the target file exists, it is overwritten.
Params:
@@ -3467,7 +4232,7 @@ Params:
to = string or range of characters representing the target file name
preserve = whether to _preserve the file attributes
-Throws: $(D FileException) on error.
+Throws: $(LREF FileException) on error.
*/
void copy(RF, RT)(RF from, RT to, PreserveAttributes preserve = preserveAttributesDefault)
if (isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) && !isConvertibleToString!RF &&
@@ -3477,12 +4242,12 @@ if (isInputRange!RF && !isInfinite!RF && isSomeChar!(ElementEncodingType!RF) &&
auto fromz = from.tempCString!FSChar();
auto toz = to.tempCString!FSChar();
- static if (isNarrowString!RF && is(Unqual!(ElementEncodingType!RF) == char))
+ static if (isNarrowString!RF && is(immutable ElementEncodingType!RF == immutable char))
alias f = from;
else
enum string f = null;
- static if (isNarrowString!RT && is(Unqual!(ElementEncodingType!RT) == char))
+ static if (isNarrowString!RT && is(immutable ElementEncodingType!RT == immutable char))
alias t = to;
else
enum string t = null;
@@ -3499,13 +4264,36 @@ if (isConvertibleToString!RF || isConvertibleToString!RT)
copy!Types(from, to, preserve);
}
-@safe unittest // issue 15319
+///
+@safe unittest
+{
+ auto source = deleteme ~ "source";
+ auto target = deleteme ~ "target";
+ auto targetNonExistent = deleteme ~ "target2";
+
+ scope(exit) source.remove, target.remove, targetNonExistent.remove;
+
+ source.write("source");
+ target.write("target");
+
+ assert(target.readText == "target");
+
+ source.copy(target);
+ assert(target.readText == "source");
+
+ source.copy(targetNonExistent);
+ assert(targetNonExistent.readText == "source");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15319
+@safe unittest
{
assert(__traits(compiles, copy("from.txt", "to.txt")));
}
-private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, const(FSChar)* toz,
- PreserveAttributes preserve) @trusted
+private void copyImpl(scope const(char)[] f, scope const(char)[] t,
+ scope const(FSChar)* fromz, scope const(FSChar)* toz,
+ PreserveAttributes preserve) @trusted
{
version (Windows)
{
@@ -3515,11 +4303,19 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
{
import core.stdc.wchar_ : wcslen;
import std.conv : to;
+ import std.format : format;
+ /++
+ Reference resources: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-copyfilew
+ Because OS copyfilew handles both source and destination paths,
+ the GetLastError does not accurately locate whether the error is for the source or destination.
+ +/
+ if (!f)
+ f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]);
if (!t)
t = to!(typeof(t))(toz[0 .. wcslen(toz)]);
- throw new FileException(t);
+ throw new FileException(format!"Copy from %s to %s"(f, t));
}
}
else version (Posix)
@@ -3582,17 +4378,14 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
- utimbuf utim = void;
- utim.actime = cast(time_t) statbufr.st_atime;
- utim.modtime = cast(time_t) statbufr.st_mtime;
-
- cenforce(utime(toz, &utim) != -1, f, fromz);
+ setTimesImpl(t, toz, statbufr.statTimeToStdTime!'a', statbufr.statTimeToStdTime!'m');
}
}
+// https://issues.dlang.org/show_bug.cgi?id=14817
@safe unittest
{
- import std.algorithm, std.file; // issue 14817
+ import std.algorithm, std.file;
auto t1 = deleteme, t2 = deleteme~"2";
scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
write(t1, "11");
@@ -3605,9 +4398,18 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
import std.utf : byChar;
copy(t1.byChar, t2.byChar);
assert(readText(t2.byChar) == "2");
+
+// https://issues.dlang.org/show_bug.cgi?id=20370
+ version (Windows)
+ assert(t1.timeLastModified == t2.timeLastModified);
+ else static if (is(typeof(&utimensat)) || is(typeof(&setattrlist)))
+ assert(t1.timeLastModified == t2.timeLastModified);
+ else
+ assert(abs(t1.timeLastModified - t2.timeLastModified) < dur!"usecs"(1));
}
-@safe version (Posix) @safe unittest //issue 11434
+// https://issues.dlang.org/show_bug.cgi?id=11434
+@safe version (Posix) @safe unittest
{
import std.conv : octal;
auto t1 = deleteme, t2 = deleteme~"2";
@@ -3619,7 +4421,8 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
assert(getAttributes(t2) == octal!100767);
}
-@safe unittest // issue 15865
+// https://issues.dlang.org/show_bug.cgi?id=15865
+@safe unittest
{
import std.exception : assertThrown;
auto t = deleteme;
@@ -3629,30 +4432,40 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
assert(readText(t) == "a");
}
+// https://issues.dlang.org/show_bug.cgi?id=19834
+version (Windows) @safe unittest
+{
+ import std.exception : collectException;
+ import std.algorithm.searching : startsWith;
+ import std.format : format;
+
+ auto f = deleteme;
+ auto t = f ~ "2";
+ auto ex = collectException(copy(f, t));
+ assert(ex.msg.startsWith(format!"Copy from %s to %s"(f, t)));
+}
+
/++
Remove directory and all of its content and subdirectories,
recursively.
+ Params:
+ pathname = the path of the directory to completely remove
+ de = The $(LREF DirEntry) to remove
+
Throws:
- $(D FileException) if there is an error (including if the given
+ $(LREF FileException) if there is an error (including if the given
file is not a directory).
+/
-void rmdirRecurse(in char[] pathname)
+void rmdirRecurse(scope const(char)[] pathname) @safe
{
//No references to pathname will be kept after rmdirRecurse,
//so the cast is safe
- rmdirRecurse(DirEntry(cast(string) pathname));
+ rmdirRecurse(DirEntry((() @trusted => cast(string) pathname)()));
}
-/++
- Remove directory and all of its content and subdirectories,
- recursively.
-
- Throws:
- $(D FileException) if there is an error (including if the given
- file is not a directory).
- +/
-void rmdirRecurse(ref DirEntry de)
+/// ditto
+void rmdirRecurse(ref DirEntry de) @safe
{
if (!de.isDir)
throw new FileException(de.name, "Not a directory");
@@ -3666,11 +4479,16 @@ void rmdirRecurse(ref DirEntry de)
}
else
{
- // all children, recursively depth-first
- foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
- {
- attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
- }
+ // dirEntries is @system because it uses a DirIterator with a
+ // RefCounted variable, but here, no references to the payload is
+ // escaped to the outside, so this should be @trusted
+ () @trusted {
+ // all children, recursively depth-first
+ foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
+ {
+ attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
+ }
+ }();
// the dir itself
rmdir(de.name);
@@ -3682,11 +4500,26 @@ void rmdirRecurse(ref DirEntry de)
//"rmdirRecurse(in char[] pathname)" implementation. That is needlessly
//expensive.
//A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable.
-void rmdirRecurse(DirEntry de)
+void rmdirRecurse(DirEntry de) @safe
{
rmdirRecurse(de);
}
+///
+@system unittest
+{
+ import std.path : buildPath;
+
+ auto dir = deleteme.buildPath("a", "b", "c");
+
+ dir.mkdirRecurse;
+ assert(dir.exists);
+
+ deleteme.rmdirRecurse;
+ assert(!dir.exists);
+ assert(!deleteme.exists);
+}
+
version (Windows) @system unittest
{
import std.exception : enforce;
@@ -3699,7 +4532,7 @@ version (Windows) @system unittest
version (Posix) @system unittest
{
import std.exception : enforce, collectException;
- import std.process : executeShell;
+
collectException(rmdirRecurse(deleteme));
auto d = deleteme~"/a/b/c/d/e/f/g";
enforce(collectException(mkdir(d)));
@@ -3713,9 +4546,8 @@ version (Posix) @system unittest
d = deleteme~"/a/b/c/d/e/f/g";
mkdirRecurse(d);
- version (Android) string link_cmd = "ln -s ";
- else string link_cmd = "ln -sf ";
- executeShell(link_cmd~deleteme~"/a/b/c "~deleteme~"/link");
+ const linkTarget = deleteme ~ "/link";
+ symlink(deleteme ~ "/a/b/c", linkTarget);
rmdirRecurse(deleteme);
enforce(!exists(deleteme));
}
@@ -3761,7 +4593,7 @@ enum SpanMode
$(B pre)-order), i.e. the content of any subdirectory is spanned
right after that subdirectory itself.
- Note that $(D SpanMode.breadth) will not result in all directory
+ Note that `SpanMode.breadth` will not result in all directory
members occurring before any subdirectory members, i.e. it is not
_true
$(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
@@ -3770,9 +4602,39 @@ enum SpanMode
breadth,
}
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ import std.algorithm.sorting : sort;
+ import std.array : array;
+ import std.path : buildPath, relativePath;
+
+ auto root = deleteme ~ "root";
+ scope(exit) root.rmdirRecurse;
+ root.mkdir;
+
+ root.buildPath("animals").mkdir;
+ root.buildPath("animals", "cat").mkdir;
+
+ alias removeRoot = (return scope e) => e.relativePath(root);
+
+ assert(root.dirEntries(SpanMode.depth).map!removeRoot.equal(
+ [buildPath("animals", "cat"), "animals"]));
+
+ assert(root.dirEntries(SpanMode.breadth).map!removeRoot.equal(
+ ["animals", buildPath("animals", "cat")]));
+
+ root.buildPath("plants").mkdir;
+
+ assert(root.dirEntries(SpanMode.shallow).array.sort.map!removeRoot.equal(
+ ["animals", "plants"]));
+}
+
private struct DirIteratorImpl
{
- import std.array : Appender, appender;
+ @safe:
SpanMode _mode;
// Whether we should follow symlinked directories while iterating.
// It also indicates whether we should avoid functions which call
@@ -3780,82 +4642,95 @@ private struct DirIteratorImpl
// be more efficient to not call stat in addition to lstat).
bool _followSymlink;
DirEntry _cur;
- Appender!(DirHandle[]) _stack;
- Appender!(DirEntry[]) _stashed; //used in depth first mode
+ DirHandle[] _stack;
+ DirEntry[] _stashed; //used in depth first mode
+
//stack helpers
- void pushExtra(DirEntry de){ _stashed.put(de); }
+ void pushExtra(DirEntry de)
+ {
+ _stashed ~= de;
+ }
+
//ditto
- bool hasExtra(){ return !_stashed.data.empty; }
+ bool hasExtra()
+ {
+ return _stashed.length != 0;
+ }
+
//ditto
DirEntry popExtra()
{
DirEntry de;
- de = _stashed.data[$-1];
- _stashed.shrinkTo(_stashed.data.length - 1);
+ de = _stashed[$-1];
+ _stashed.popBack();
return de;
-
}
+
version (Windows)
{
+ WIN32_FIND_DATAW _findinfo;
struct DirHandle
{
string dirpath;
HANDLE h;
}
- bool stepIn(string directory)
+ bool stepIn(string directory) @safe
{
import std.path : chainPath;
+ auto searchPattern = chainPath(directory, "*.*");
- auto search_pattern = chainPath(directory, "*.*");
- WIN32_FIND_DATAW findinfo;
- HANDLE h = FindFirstFileW(search_pattern.tempCString!FSChar(), &findinfo);
+ static auto trustedFindFirstFileW(typeof(searchPattern) pattern, WIN32_FIND_DATAW* findinfo) @trusted
+ {
+ return FindFirstFileW(pattern.tempCString!FSChar(), findinfo);
+ }
+
+ HANDLE h = trustedFindFirstFileW(searchPattern, &_findinfo);
cenforce(h != INVALID_HANDLE_VALUE, directory);
- _stack.put(DirHandle(directory, h));
- return toNext(false, &findinfo);
+ _stack ~= DirHandle(directory, h);
+ return toNext(false, &_findinfo);
}
bool next()
{
- if (_stack.data.empty)
+ if (_stack.length == 0)
return false;
- WIN32_FIND_DATAW findinfo;
- return toNext(true, &findinfo);
+ return toNext(true, &_findinfo);
}
- bool toNext(bool fetch, WIN32_FIND_DATAW* findinfo)
+ bool toNext(bool fetch, WIN32_FIND_DATAW* findinfo) @trusted
{
import core.stdc.wchar_ : wcscmp;
if (fetch)
{
- if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
+ if (FindNextFileW(_stack[$-1].h, findinfo) == FALSE)
{
popDirStack();
return false;
}
}
- while ( wcscmp(findinfo.cFileName.ptr, ".") == 0
- || wcscmp(findinfo.cFileName.ptr, "..") == 0)
- if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
+ while (wcscmp(&findinfo.cFileName[0], ".") == 0 ||
+ wcscmp(&findinfo.cFileName[0], "..") == 0)
+ if (FindNextFileW(_stack[$-1].h, findinfo) == FALSE)
{
popDirStack();
return false;
}
- _cur = DirEntry(_stack.data[$-1].dirpath, findinfo);
+ _cur = DirEntry(_stack[$-1].dirpath, findinfo);
return true;
}
- void popDirStack()
+ void popDirStack() @trusted
{
- assert(!_stack.data.empty);
- FindClose(_stack.data[$-1].h);
- _stack.shrinkTo(_stack.data.length-1);
+ assert(_stack.length != 0);
+ FindClose(_stack[$-1].h);
+ _stack.popBack();
}
- void releaseDirStack()
+ void releaseDirStack() @trusted
{
- foreach ( d; _stack.data)
+ foreach (d; _stack)
FindClose(d.h);
}
@@ -3874,40 +4749,47 @@ private struct DirIteratorImpl
bool stepIn(string directory)
{
- auto h = directory.length ? opendir(directory.tempCString()) : opendir(".");
+ static auto trustedOpendir(string dir) @trusted
+ {
+ return opendir(dir.tempCString());
+ }
+
+ auto h = directory.length ? trustedOpendir(directory) : trustedOpendir(".");
cenforce(h, directory);
- _stack.put(DirHandle(directory, h));
+ _stack ~= (DirHandle(directory, h));
return next();
}
- bool next()
+ bool next() @trusted
{
- if (_stack.data.empty)
+ if (_stack.length == 0)
return false;
- for (dirent* fdata; (fdata = readdir(_stack.data[$-1].h)) != null; )
+
+ for (dirent* fdata; (fdata = readdir(_stack[$-1].h)) != null; )
{
// Skip "." and ".."
- if (core.stdc.string.strcmp(fdata.d_name.ptr, ".") &&
- core.stdc.string.strcmp(fdata.d_name.ptr, "..") )
+ if (core.stdc.string.strcmp(&fdata.d_name[0], ".") &&
+ core.stdc.string.strcmp(&fdata.d_name[0], ".."))
{
- _cur = DirEntry(_stack.data[$-1].dirpath, fdata);
+ _cur = DirEntry(_stack[$-1].dirpath, fdata);
return true;
}
}
+
popDirStack();
return false;
}
- void popDirStack()
+ void popDirStack() @trusted
{
- assert(!_stack.data.empty);
- closedir(_stack.data[$-1].h);
- _stack.shrinkTo(_stack.data.length-1);
+ assert(_stack.length != 0);
+ closedir(_stack[$-1].h);
+ _stack.popBack();
}
- void releaseDirStack()
+ void releaseDirStack() @trusted
{
- foreach ( d; _stack.data)
+ foreach (d; _stack)
closedir(d.h);
}
@@ -3922,11 +4804,8 @@ private struct DirIteratorImpl
{
_mode = mode;
_followSymlink = followSymlink;
- _stack = appender(cast(DirHandle[])[]);
- if (_mode == SpanMode.depth)
- _stashed = appender(cast(DirEntry[])[]);
- static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ static if (isNarrowString!R && is(immutable ElementEncodingType!R == immutable char))
alias pathnameStr = pathname;
else
{
@@ -3948,8 +4827,17 @@ private struct DirIteratorImpl
}
}
}
- @property bool empty(){ return _stashed.data.empty && _stack.data.empty; }
- @property DirEntry front(){ return _cur; }
+
+ @property bool empty()
+ {
+ return _stashed.length == 0 && _stack.length == 0;
+ }
+
+ @property DirEntry front()
+ {
+ return _cur;
+ }
+
void popFront()
{
switch (_mode)
@@ -3993,25 +4881,29 @@ private struct DirIteratorImpl
struct DirIterator
{
+@safe:
private:
RefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl;
- this(string pathname, SpanMode mode, bool followSymlink)
+ this(string pathname, SpanMode mode, bool followSymlink) @trusted
{
impl = typeof(impl)(pathname, mode, followSymlink);
}
public:
- @property bool empty(){ return impl.empty; }
- @property DirEntry front(){ return impl.front; }
- void popFront(){ impl.popFront(); }
-
+ @property bool empty() { return impl.empty; }
+ @property DirEntry front() { return impl.front; }
+ void popFront() { impl.popFront(); }
}
/++
- Returns an input range of $(D DirEntry) that lazily iterates a given directory,
+ Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ of `DirEntry` that lazily iterates a given directory,
also provides two ways of foreach iteration. The iteration variable can be of
- type $(D string) if only the name is needed, or $(D DirEntry)
+ type `string` if only the name is needed, or `DirEntry`
if additional details are needed. The span _mode dictates how the
directory is traversed. The name of each iterated directory entry
- contains the absolute _path.
+ contains the absolute or relative _path (depending on _pathname).
+
+ Note: The order of returned directory entries is as it is provided by the
+ operating system / filesystem, and may not follow any particular sorting.
Params:
path = The directory to iterate over.
@@ -4024,7 +4916,7 @@ public:
std,_path).
mode = Whether the directory's sub-directories should be
- iterated in depth-first port-order ($(LREF depth)),
+ iterated in depth-first post-order ($(LREF depth)),
depth-first pre-order ($(LREF breadth)), or not at all
($(LREF shallow)).
@@ -4032,8 +4924,12 @@ public:
should be treated as directories and their contents
iterated over.
+ Returns:
+ An $(REF_ALTTEXT input range, isInputRange,std,range,primitives) of
+ $(LREF DirEntry).
+
Throws:
- $(D FileException) if the directory does not exist.
+ $(LREF FileException) if the directory does not exist.
Example:
--------------------
@@ -4065,7 +4961,7 @@ foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
{
string cmd = "dmd -c " ~ d.name;
writeln(cmd);
- std.process.system(cmd);
+ std.process.executeShell(cmd);
}
// Iterate over all D source files in current directory and all its
@@ -4080,7 +4976,7 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
return DirIterator(path, mode, followSymlink);
}
-/// Duplicate functionality of D1's $(D std.file.listdir()):
+/// Duplicate functionality of D1's `std.file.listdir()`:
@safe unittest
{
string[] listdir(string pathname)
@@ -4092,7 +4988,7 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
return std.file.dirEntries(pathname, SpanMode.shallow)
.filter!(a => a.isFile)
- .map!(a => std.path.baseName(a.name))
+ .map!((return a) => std.path.baseName(a.name))
.array;
}
@@ -4122,7 +5018,7 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
// called from a shared library on Android,
// ie an apk
else
- string testdir = "deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID); // needs to be relative
+ string testdir = tempDir.buildPath("deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID));
mkdirRecurse(buildPath(testdir, "somedir"));
scope(exit) rmdirRecurse(testdir);
write(buildPath(testdir, "somefile"), null);
@@ -4135,7 +5031,7 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
auto len = enforce(walkLength(dirEntries(absolutePath(relpath), mode)));
assert(walkLength(dirEntries(relpath, mode)) == len);
assert(equal(
- map!(a => absolutePath(a.name))(dirEntries(relpath, mode)),
+ map!((return a) => absolutePath(a.name))(dirEntries(relpath, mode)),
map!(a => a.name)(dirEntries(absolutePath(relpath), mode))));
return len;
}
@@ -4156,7 +5052,7 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
assert(e.isFile || e.isDir, e.name);
}
- //issue 7264
+ // https://issues.dlang.org/show_bug.cgi?id=7264
foreach (string name; dirEntries(testdir, "*.d", SpanMode.breadth))
{
@@ -4165,14 +5061,14 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
{
static assert(is(typeof(entry) == DirEntry));
}
- //issue 7138
+ // https://issues.dlang.org/show_bug.cgi?id=7138
auto a = array(dirEntries(testdir, SpanMode.shallow));
- // issue 11392
+ // https://issues.dlang.org/show_bug.cgi?id=11392
auto dFiles = dirEntries(testdir, SpanMode.shallow);
foreach (d; dFiles){}
- // issue 15146
+ // https://issues.dlang.org/show_bug.cgi?id=15146
dirEntries("", SpanMode.shallow).walkLength();
}
@@ -4263,6 +5159,40 @@ auto dirEntries(string path, string pattern, SpanMode mode,
}
}
+// Make sure that dirEntries does not butcher Unicode file names
+// https://issues.dlang.org/show_bug.cgi?id=17962
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ import std.algorithm.sorting : sort;
+ import std.array : array;
+ import std.path : buildPath;
+ import std.uni : normalize;
+
+ // The Unicode normalization is required to make the tests pass on Mac OS X.
+ auto dir = deleteme ~ normalize("ð·");
+ scope(exit) if (dir.exists) rmdirRecurse(dir);
+ mkdir(dir);
+ auto files = ["Hello World",
+ "Ma Chérie.jpeg",
+ "ã•ã„ã”ã®æžœå®Ÿ.txt"].map!(a => buildPath(dir, normalize(a)))().array();
+ sort(files);
+ foreach (file; files)
+ write(file, "nothing");
+
+ auto result = dirEntries(dir, SpanMode.shallow).map!((return a) => a.name.normalize()).array();
+ sort(result);
+
+ assert(equal(files, result));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21250
+@system unittest
+{
+ import std.exception : assertThrown;
+ assertThrown!Exception(dirEntries("237f5babd6de21f40915826699582e36", "*.bin", SpanMode.depth));
+}
/**
* Reads a file line by line and parses the line into a single value or a
@@ -4287,13 +5217,14 @@ auto dirEntries(string path, string pattern, SpanMode mode,
* with extra characters are allowed.
*/
Select!(Types.length == 1, Types[0][], Tuple!(Types)[])
-slurp(Types...)(string filename, in char[] format)
+slurp(Types...)(string filename, scope const(char)[] format)
{
import std.array : appender;
import std.conv : text;
import std.exception : enforce;
- import std.format : formattedRead;
+ import std.format.read : formattedRead;
import std.stdio : File;
+ import std.string : stripRight;
auto app = appender!(typeof(return))();
ElementType!(typeof(return)) toAdd;
@@ -4302,7 +5233,7 @@ slurp(Types...)(string filename, in char[] format)
foreach (line; f.byLine())
{
formattedRead(line, format, &toAdd);
- enforce(line.empty,
+ enforce(line.stripRight("\r").empty,
text("Trailing characters at the end of line: `", line,
"'"));
app.put(toAdd);
@@ -4331,35 +5262,49 @@ slurp(Types...)(string filename, in char[] format)
assert(a[1] == tuple(345, 1.125));
}
+@system unittest
+{
+ import std.typecons : tuple;
-/**
-Returns the path to a directory for temporary files.
-
-On Windows, this function returns the result of calling the Windows API function
-$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, $(D GetTempPath)).
+ scope(exit)
+ {
+ assert(exists(deleteme));
+ remove(deleteme);
+ }
+ write(deleteme, "10\r\n20");
+ assert(slurp!(int)(deleteme, "%d") == [10, 20]);
+}
-On POSIX platforms, it searches through the following list of directories
-and returns the first one which is found to exist:
-$(OL
- $(LI The directory given by the $(D TMPDIR) environment variable.)
- $(LI The directory given by the $(D TEMP) environment variable.)
- $(LI The directory given by the $(D TMP) environment variable.)
- $(LI $(D /tmp))
- $(LI $(D /var/tmp))
- $(LI $(D /usr/tmp))
-)
-On all platforms, $(D tempDir) returns $(D ".") on failure, representing
-the current working directory.
+/**
+Returns the path to a directory for temporary files.
The return value of the function is cached, so the procedures described
-above will only be performed the first time the function is called. All
+below will only be performed the first time the function is called. All
subsequent runs will return the same string, regardless of whether
environment variables and directory structures have changed in the
meantime.
-The POSIX $(D tempDir) algorithm is inspired by Python's
-$(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, $(D tempfile.tempdir)).
+The POSIX `tempDir` algorithm is inspired by Python's
+$(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, `tempfile.tempdir`).
+
+Returns:
+ On Windows, this function returns the result of calling the Windows API function
+ $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, `GetTempPath`).
+
+ On POSIX platforms, it searches through the following list of directories
+ and returns the first one which is found to exist:
+ $(OL
+ $(LI The directory given by the `TMPDIR` environment variable.)
+ $(LI The directory given by the `TEMP` environment variable.)
+ $(LI The directory given by the `TMP` environment variable.)
+ $(LI `/tmp`)
+ $(LI `/var/tmp`)
+ $(LI `/usr/tmp`)
+ )
+
+ On all platforms, `tempDir` returns `"."` on failure, representing
+ the current working directory.
*/
string tempDir() @trusted
{
@@ -4399,3 +5344,92 @@ string tempDir() @trusted
}
return cache;
}
+
+///
+@safe unittest
+{
+ import std.ascii : letters;
+ import std.conv : to;
+ import std.path : buildPath;
+ import std.random : randomSample;
+ import std.utf : byCodeUnit;
+
+ // random id with 20 letters
+ auto id = letters.byCodeUnit.randomSample(20).to!string;
+ auto myFile = tempDir.buildPath(id ~ "my_tmp_file");
+ scope(exit) myFile.remove;
+
+ myFile.write("hello");
+ assert(myFile.readText == "hello");
+}
+
+/**
+Returns the available disk space based on a given path.
+On Windows, `path` must be a directory; on POSIX systems, it can be a file or directory.
+
+Params:
+ path = on Windows, it must be a directory; on POSIX it can be a file or directory
+Returns:
+ Available space in bytes
+
+Throws:
+ $(LREF FileException) in case of failure
+*/
+ulong getAvailableDiskSpace(scope const(char)[] path) @safe
+{
+ version (Windows)
+ {
+ import core.sys.windows.winbase : GetDiskFreeSpaceExW;
+ import core.sys.windows.winnt : ULARGE_INTEGER;
+ import std.internal.cstring : tempCStringW;
+
+ ULARGE_INTEGER freeBytesAvailable;
+ auto err = () @trusted {
+ return GetDiskFreeSpaceExW(path.tempCStringW(), &freeBytesAvailable, null, null);
+ } ();
+ cenforce(err != 0, "Cannot get available disk space");
+
+ return freeBytesAvailable.QuadPart;
+ }
+ else version (Posix)
+ {
+ import std.internal.cstring : tempCString;
+
+ version (FreeBSD)
+ {
+ import core.sys.freebsd.sys.mount : statfs, statfs_t;
+
+ statfs_t stats;
+ auto err = () @trusted {
+ return statfs(path.tempCString(), &stats);
+ } ();
+ cenforce(err == 0, "Cannot get available disk space");
+
+ return stats.f_bavail * stats.f_bsize;
+ }
+ else
+ {
+ import core.sys.posix.sys.statvfs : statvfs, statvfs_t;
+
+ statvfs_t stats;
+ auto err = () @trusted {
+ return statvfs(path.tempCString(), &stats);
+ } ();
+ cenforce(err == 0, "Cannot get available disk space");
+
+ return stats.f_bavail * stats.f_frsize;
+ }
+ }
+ else static assert(0, "Unsupported platform");
+}
+
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto space = getAvailableDiskSpace(".");
+ assert(space > 0);
+
+ assertThrown!FileException(getAvailableDiskSpace("ThisFileDoesNotExist123123"));
+}
diff --git a/libphobos/src/std/format.d b/libphobos/src/std/format.d
deleted file mode 100644
index 17e5906baf7..00000000000
--- a/libphobos/src/std/format.d
+++ /dev/null
@@ -1,6028 +0,0 @@
-// Written in the D programming language.
-
-/**
- This module implements the formatting functionality for strings and
- I/O. It's comparable to C99's $(D vsprintf()) and uses a similar
- _format encoding scheme.
-
- For an introductory look at $(B std._format)'s capabilities and how to use
- this module see the dedicated
- $(LINK2 http://wiki.dlang.org/Defining_custom_print_format_specifiers, DWiki article).
-
- This module centers around two functions:
-
-$(BOOKTABLE ,
-$(TR $(TH Function Name) $(TH Description)
-)
- $(TR $(TD $(LREF formattedRead))
- $(TD Reads values according to the _format string from an InputRange.
- ))
- $(TR $(TD $(LREF formattedWrite))
- $(TD Formats its arguments according to the _format string and puts them
- to an OutputRange.
- ))
-)
-
- Please see the documentation of function $(LREF formattedWrite) for a
- description of the _format string.
-
- Two functions have been added for convenience:
-
-$(BOOKTABLE ,
-$(TR $(TH Function Name) $(TH Description)
-)
- $(TR $(TD $(LREF _format))
- $(TD Returns a GC-allocated string with the formatting result.
- ))
- $(TR $(TD $(LREF sformat))
- $(TD Puts the formatting result into a preallocated array.
- ))
-)
-
- These two functions are publicly imported by $(MREF std, string)
- to be easily available.
-
- The functions $(LREF formatValue) and $(LREF unformatValue) are
- used for the plumbing.
- Copyright: Copyright Digital Mars 2000-2013.
-
- License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-
- Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
- Andrei Alexandrescu), and Kenji Hara
-
- Source: $(PHOBOSSRC std/_format.d)
- */
-module std.format;
-
-//debug=format; // uncomment to turn on debugging printf's
-
-import core.vararg;
-import std.exception;
-import std.meta;
-import std.range.primitives;
-import std.traits;
-
-
-/**********************************************************************
- * Signals a mismatch between a format and its corresponding argument.
- */
-class FormatException : Exception
-{
- @safe pure nothrow
- this()
- {
- super("format error");
- }
-
- @safe pure nothrow
- this(string msg, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null)
- {
- super(msg, fn, ln, next);
- }
-}
-
-private alias enforceFmt = enforceEx!FormatException;
-
-
-/**********************************************************************
- Interprets variadic argument list $(D args), formats them according
- to $(D fmt), and sends the resulting characters to $(D w). The
- encoding of the output is the same as $(D Char). The type $(D Writer)
- must satisfy $(D $(REF isOutputRange, std,range,primitives)!(Writer, Char)).
-
- The variadic arguments are normally consumed in order. POSIX-style
- $(HTTP opengroup.org/onlinepubs/009695399/functions/printf.html,
- positional parameter syntax) is also supported. Each argument is
- formatted into a sequence of chars according to the format
- specification, and the characters are passed to $(D w). As many
- arguments as specified in the format string are consumed and
- formatted. If there are fewer arguments than format specifiers, a
- $(D FormatException) is thrown. If there are more remaining arguments
- than needed by the format specification, they are ignored but only
- if at least one argument was formatted.
-
- The format string supports the formatting of array and nested array elements
- via the grouping format specifiers $(B %&#40;) and $(B %&#41;). Each
- matching pair of $(B %&#40;) and $(B %&#41;) corresponds with a single array
- argument. The enclosed sub-format string is applied to individual array
- elements. The trailing portion of the sub-format string following the
- conversion specifier for the array element is interpreted as the array
- delimiter, and is therefore omitted following the last array element. The
- $(B %|) specifier may be used to explicitly indicate the start of the
- delimiter, so that the preceding portion of the string will be included
- following the last array element. (See below for explicit examples.)
-
- Params:
-
- w = Output is sent to this writer. Typical output writers include
- $(REF Appender!string, std,array) and $(REF LockingTextWriter, std,stdio).
-
- fmt = Format string.
-
- args = Variadic argument list.
-
- Returns: Formatted number of arguments.
-
- Throws: Mismatched arguments and formats result in a $(D
- FormatException) being thrown.
-
- Format_String: <a name="format-string">$(I Format strings)</a>
- consist of characters interspersed with $(I format
- specifications). Characters are simply copied to the output (such
- as putc) after any necessary conversion to the corresponding UTF-8
- sequence.
-
- The format string has the following grammar:
-
-$(PRE
-$(I FormatString):
- $(I FormatStringItem)*
-$(I FormatStringItem):
- $(B '%%')
- $(B '%') $(I Position) $(I Flags) $(I Width) $(I Separator) $(I Precision) $(I FormatChar)
- $(B '%$(LPAREN)') $(I FormatString) $(B '%$(RPAREN)')
- $(I OtherCharacterExceptPercent)
-$(I Position):
- $(I empty)
- $(I Integer) $(B '$')
-$(I Flags):
- $(I empty)
- $(B '-') $(I Flags)
- $(B '+') $(I Flags)
- $(B '#') $(I Flags)
- $(B '0') $(I Flags)
- $(B ' ') $(I Flags)
-$(I Width):
- $(I empty)
- $(I Integer)
- $(B '*')
-$(I Separator):
- $(I empty)
- $(B ',')
- $(B ',') $(B '?')
- $(B ',') $(B '*') $(B '?')
- $(B ',') $(I Integer) $(B '?')
- $(B ',') $(B '*')
- $(B ',') $(I Integer)
-$(I Precision):
- $(I empty)
- $(B '.')
- $(B '.') $(I Integer)
- $(B '.*')
-$(I Integer):
- $(I Digit)
- $(I Digit) $(I Integer)
-$(I Digit):
- $(B '0')|$(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9')
-$(I FormatChar):
- $(B 's')|$(B 'c')|$(B 'b')|$(B 'd')|$(B 'o')|$(B 'x')|$(B 'X')|$(B 'e')|$(B 'E')|$(B 'f')|$(B 'F')|$(B 'g')|$(B 'G')|$(B 'a')|$(B 'A')|$(B '|')
-)
-
- $(BOOKTABLE Flags affect formatting depending on the specifier as
- follows., $(TR $(TH Flag) $(TH Types&nbsp;affected) $(TH Semantics))
-
- $(TR $(TD $(B '-')) $(TD numeric) $(TD Left justify the result in
- the field. It overrides any $(B 0) flag.))
-
- $(TR $(TD $(B '+')) $(TD numeric) $(TD Prefix positive numbers in
- a signed conversion with a $(B +). It overrides any $(I space)
- flag.))
-
- $(TR $(TD $(B '#')) $(TD integral ($(B 'o'))) $(TD Add to
- precision as necessary so that the first digit of the octal
- formatting is a '0', even if both the argument and the $(I
- Precision) are zero.))
-
- $(TR $(TD $(B '#')) $(TD integral ($(B 'x'), $(B 'X'))) $(TD If
- non-zero, prefix result with $(B 0x) ($(B 0X)).))
-
- $(TR $(TD $(B '#')) $(TD floating) $(TD Always insert the decimal
- point and print trailing zeros.))
-
- $(TR $(TD $(B '0')) $(TD numeric) $(TD Use leading
- zeros to pad rather than spaces (except for the floating point
- values $(D nan) and $(D infinity)). Ignore if there's a $(I
- Precision).))
-
- $(TR $(TD $(B ' ')) $(TD numeric) $(TD Prefix positive
- numbers in a signed conversion with a space.)))
-
- $(DL
- $(DT $(I Width))
- $(DD
- Specifies the minimum field width.
- If the width is a $(B *), an additional argument of type $(B int),
- preceding the actual argument, is taken as the width.
- If the width is negative, it is as if the $(B -) was given
- as a $(I Flags) character.)
-
- $(DT $(I Precision))
- $(DD Gives the precision for numeric conversions.
- If the precision is a $(B *), an additional argument of type $(B int),
- preceding the actual argument, is taken as the precision.
- If it is negative, it is as if there was no $(I Precision) specifier.)
-
- $(DT $(I Separator))
- $(DD Inserts the separator symbols ',' every $(I X) digits, from right
- to left, into numeric values to increase readability.
- The fractional part of floating point values inserts the separator
- from left to right.
- Entering an integer after the ',' allows to specify $(I X).
- If a '*' is placed after the ',' then $(I X) is specified by an
- additional parameter to the format function.
- Adding a '?' after the ',' or $(I X) specifier allows to specify
- the separator character as an additional parameter.
- )
-
- $(DT $(I FormatChar))
- $(DD
- $(DL
- $(DT $(B 's'))
- $(DD The corresponding argument is formatted in a manner consistent
- with its type:
- $(DL
- $(DT $(B bool))
- $(DD The result is $(D "true") or $(D "false").)
- $(DT integral types)
- $(DD The $(B %d) format is used.)
- $(DT floating point types)
- $(DD The $(B %g) format is used.)
- $(DT string types)
- $(DD The result is the string converted to UTF-8.
- A $(I Precision) specifies the maximum number of characters
- to use in the result.)
- $(DT structs)
- $(DD If the struct defines a $(B toString()) method the result is
- the string returned from this function. Otherwise the result is
- StructName(field<sub>0</sub>, field<sub>1</sub>, ...) where
- field<sub>n</sub> is the nth element formatted with the default
- format.)
- $(DT classes derived from $(B Object))
- $(DD The result is the string returned from the class instance's
- $(B .toString()) method.
- A $(I Precision) specifies the maximum number of characters
- to use in the result.)
- $(DT unions)
- $(DD If the union defines a $(B toString()) method the result is
- the string returned from this function. Otherwise the result is
- the name of the union, without its contents.)
- $(DT non-string static and dynamic arrays)
- $(DD The result is [s<sub>0</sub>, s<sub>1</sub>, ...]
- where s<sub>n</sub> is the nth element
- formatted with the default format.)
- $(DT associative arrays)
- $(DD The result is the equivalent of what the initializer
- would look like for the contents of the associative array,
- e.g.: ["red" : 10, "blue" : 20].)
- ))
-
- $(DT $(B 'c'))
- $(DD The corresponding argument must be a character type.)
-
- $(DT $(B 'b','d','o','x','X'))
- $(DD The corresponding argument must be an integral type
- and is formatted as an integer. If the argument is a signed type
- and the $(I FormatChar) is $(B d) it is converted to
- a signed string of characters, otherwise it is treated as
- unsigned. An argument of type $(B bool) is formatted as '1'
- or '0'. The base used is binary for $(B b), octal for $(B o),
- decimal
- for $(B d), and hexadecimal for $(B x) or $(B X).
- $(B x) formats using lower case letters, $(B X) uppercase.
- If there are fewer resulting digits than the $(I Precision),
- leading zeros are used as necessary.
- If the $(I Precision) is 0 and the number is 0, no digits
- result.)
-
- $(DT $(B 'e','E'))
- $(DD A floating point number is formatted as one digit before
- the decimal point, $(I Precision) digits after, the $(I FormatChar),
- &plusmn;, followed by at least a two digit exponent:
- $(I d.dddddd)e$(I &plusmn;dd).
- If there is no $(I Precision), six
- digits are generated after the decimal point.
- If the $(I Precision) is 0, no decimal point is generated.)
-
- $(DT $(B 'f','F'))
- $(DD A floating point number is formatted in decimal notation.
- The $(I Precision) specifies the number of digits generated
- after the decimal point. It defaults to six. At least one digit
- is generated before the decimal point. If the $(I Precision)
- is zero, no decimal point is generated.)
-
- $(DT $(B 'g','G'))
- $(DD A floating point number is formatted in either $(B e) or
- $(B f) format for $(B g); $(B E) or $(B F) format for
- $(B G).
- The $(B f) format is used if the exponent for an $(B e) format
- is greater than -5 and less than the $(I Precision).
- The $(I Precision) specifies the number of significant
- digits, and defaults to six.
- Trailing zeros are elided after the decimal point, if the fractional
- part is zero then no decimal point is generated.)
-
- $(DT $(B 'a','A'))
- $(DD A floating point number is formatted in hexadecimal
- exponential notation 0x$(I h.hhhhhh)p$(I &plusmn;d).
- There is one hexadecimal digit before the decimal point, and as
- many after as specified by the $(I Precision).
- If the $(I Precision) is zero, no decimal point is generated.
- If there is no $(I Precision), as many hexadecimal digits as
- necessary to exactly represent the mantissa are generated.
- The exponent is written in as few digits as possible,
- but at least one, is in decimal, and represents a power of 2 as in
- $(I h.hhhhhh)*2<sup>$(I &plusmn;d)</sup>.
- The exponent for zero is zero.
- The hexadecimal digits, x and p are in upper case if the
- $(I FormatChar) is upper case.)
- ))
- )
-
- Floating point NaN's are formatted as $(B nan) if the
- $(I FormatChar) is lower case, or $(B NAN) if upper.
- Floating point infinities are formatted as $(B inf) or
- $(B infinity) if the
- $(I FormatChar) is lower case, or $(B INF) or $(B INFINITY) if upper.
-
- The positional and non-positional styles can be mixed in the same
- format string. (POSIX leaves this behavior undefined.) The internal
- counter for non-positional parameters tracks the next parameter after
- the largest positional parameter already used.
-
- Example using array and nested array formatting:
- -------------------------
- import std.stdio;
-
- void main()
- {
- writefln("My items are %(%s %).", [1,2,3]);
- writefln("My items are %(%s, %).", [1,2,3]);
- }
- -------------------------
- The output is:
-$(CONSOLE
-My items are 1 2 3.
-My items are 1, 2, 3.
-)
-
- The trailing end of the sub-format string following the specifier for each
- item is interpreted as the array delimiter, and is therefore omitted
- following the last array item. The $(B %|) delimiter specifier may be used
- to indicate where the delimiter begins, so that the portion of the format
- string prior to it will be retained in the last array element:
- -------------------------
- import std.stdio;
-
- void main()
- {
- writefln("My items are %(-%s-%|, %).", [1,2,3]);
- }
- -------------------------
- which gives the output:
-$(CONSOLE
-My items are -1-, -2-, -3-.
-)
-
- These compound format specifiers may be nested in the case of a nested
- array argument:
- -------------------------
- import std.stdio;
- void main() {
- auto mat = [[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9]];
-
- writefln("%(%(%d %)\n%)", mat);
- writeln();
-
- writefln("[%(%(%d %)\n %)]", mat);
- writeln();
-
- writefln("[%([%(%d %)]%|\n %)]", mat);
- writeln();
- }
- -------------------------
- The output is:
-$(CONSOLE
-1 2 3
-4 5 6
-7 8 9
-
-[1 2 3
- 4 5 6
- 7 8 9]
-
-[[1 2 3]
- [4 5 6]
- [7 8 9]]
-)
-
- Inside a compound format specifier, strings and characters are escaped
- automatically. To avoid this behavior, add $(B '-') flag to
- $(D "%$(LPAREN)").
- -------------------------
- import std.stdio;
-
- void main()
- {
- writefln("My friends are %s.", ["John", "Nancy"]);
- writefln("My friends are %(%s, %).", ["John", "Nancy"]);
- writefln("My friends are %-(%s, %).", ["John", "Nancy"]);
- }
- -------------------------
- which gives the output:
-$(CONSOLE
-My friends are ["John", "Nancy"].
-My friends are "John", "Nancy".
-My friends are John, Nancy.
-)
- */
-uint formattedWrite(alias fmt, Writer, A...)(auto ref Writer w, A args)
-if (isSomeString!(typeof(fmt)))
-{
- alias e = checkFormatException!(fmt, A);
- static assert(!e, e.msg);
- return .formattedWrite(w, fmt, args);
-}
-
-/// The format string can be checked at compile-time (see $(LREF format) for details):
-@safe pure unittest
-{
- import std.array : appender;
- import std.format : formattedWrite;
-
- auto writer = appender!string();
- writer.formattedWrite!"%s is the ultimate %s."(42, "answer");
- assert(writer.data == "42 is the ultimate answer.");
-
- // Clear the writer
- writer = appender!string();
- formattedWrite(writer, "Date: %2$s %1$s", "October", 5);
- assert(writer.data == "Date: 5 October");
-}
-
-/// ditto
-uint formattedWrite(Writer, Char, A...)(auto ref Writer w, in Char[] fmt, A args)
-{
- import std.conv : text;
-
- auto spec = FormatSpec!Char(fmt);
-
- // Are we already done with formats? Then just dump each parameter in turn
- uint currentArg = 0;
- while (spec.writeUpToNextSpec(w))
- {
- if (currentArg == A.length && !spec.indexStart)
- {
- // leftover spec?
- enforceFmt(fmt.length == 0,
- text("Orphan format specifier: %", spec.spec));
- break;
- }
-
- if (spec.width == spec.DYNAMIC)
- {
- auto width = getNthInt!"integer width"(currentArg, args);
- if (width < 0)
- {
- spec.flDash = true;
- width = -width;
- }
- spec.width = width;
- ++currentArg;
- }
- else if (spec.width < 0)
- {
- // means: get width as a positional parameter
- auto index = cast(uint) -spec.width;
- assert(index > 0);
- auto width = getNthInt!"integer width"(index - 1, args);
- if (currentArg < index) currentArg = index;
- if (width < 0)
- {
- spec.flDash = true;
- width = -width;
- }
- spec.width = width;
- }
-
- if (spec.precision == spec.DYNAMIC)
- {
- auto precision = getNthInt!"integer precision"(currentArg, args);
- if (precision >= 0) spec.precision = precision;
- // else negative precision is same as no precision
- else spec.precision = spec.UNSPECIFIED;
- ++currentArg;
- }
- else if (spec.precision < 0)
- {
- // means: get precision as a positional parameter
- auto index = cast(uint) -spec.precision;
- assert(index > 0);
- auto precision = getNthInt!"integer precision"(index- 1, args);
- if (currentArg < index) currentArg = index;
- if (precision >= 0) spec.precision = precision;
- // else negative precision is same as no precision
- else spec.precision = spec.UNSPECIFIED;
- }
-
- if (spec.separators == spec.DYNAMIC)
- {
- auto separators = getNthInt!"separator digit width"(currentArg, args);
- spec.separators = separators;
- ++currentArg;
- }
-
- if (spec.separatorCharPos == spec.DYNAMIC)
- {
- auto separatorChar =
- getNth!("separator character", isSomeChar, dchar)(currentArg, args);
- spec.separatorChar = separatorChar;
- ++currentArg;
- }
-
- if (currentArg == A.length && !spec.indexStart)
- {
- // leftover spec?
- enforceFmt(fmt.length == 0,
- text("Orphan format specifier: %", spec.spec));
- break;
- }
-
- // Format an argument
- // This switch uses a static foreach to generate a jump table.
- // Currently `spec.indexStart` use the special value '0' to signal
- // we should use the current argument. An enhancement would be to
- // always store the index.
- size_t index = currentArg;
- if (spec.indexStart != 0)
- index = spec.indexStart - 1;
- else
- ++currentArg;
- SWITCH: switch (index)
- {
- foreach (i, Tunused; A)
- {
- case i:
- formatValue(w, args[i], spec);
- if (currentArg < spec.indexEnd)
- currentArg = spec.indexEnd;
- // A little know feature of format is to format a range
- // of arguments, e.g. `%1:3$` will format the first 3
- // arguments. Since they have to be consecutive we can
- // just use explicit fallthrough to cover that case.
- if (i + 1 < spec.indexEnd)
- {
- // You cannot goto case if the next case is the default
- static if (i + 1 < A.length)
- goto case;
- else
- goto default;
- }
- else
- break SWITCH;
- }
- default:
- throw new FormatException(
- text("Positional specifier %", spec.indexStart, '$', spec.spec,
- " index exceeds ", A.length));
- }
- }
- return currentArg;
-}
-
-///
-@safe unittest
-{
- assert(format("%,d", 1000) == "1,000");
- assert(format("%,f", 1234567.891011) == "1,234,567.891,011");
- assert(format("%,?d", '?', 1000) == "1?000");
- assert(format("%,1d", 1000) == "1,0,0,0", format("%,1d", 1000));
- assert(format("%,*d", 4, -12345) == "-1,2345");
- assert(format("%,*?d", 4, '_', -12345) == "-1_2345");
- assert(format("%,6?d", '_', -12345678) == "-12_345678");
- assert(format("%12,3.3f", 1234.5678) == " 1,234.568", "'" ~
- format("%12,3.3f", 1234.5678) ~ "'");
-}
-
-@safe pure unittest
-{
- import std.array;
- auto w = appender!string();
- formattedWrite(w, "%s %d", "@safe/pure", 42);
- assert(w.data == "@safe/pure 42");
-}
-
-/**
-Reads characters from input range $(D r), converts them according
-to $(D fmt), and writes them to $(D args).
-
-Params:
- r = The range to read from.
- fmt = The format of the data to read.
- args = The drain of the data read.
-
-Returns:
-
-On success, the function returns the number of variables filled. This count
-can match the expected number of readings or fewer, even zero, if a
-matching failure happens.
-
-Throws:
- An `Exception` if `S.length == 0` and `fmt` has format specifiers.
- */
-uint formattedRead(alias fmt, R, S...)(ref R r, auto ref S args)
-if (isSomeString!(typeof(fmt)))
-{
- alias e = checkFormatException!(fmt, S);
- static assert(!e, e.msg);
- return .formattedRead(r, fmt, args);
-}
-
-/// ditto
-uint formattedRead(R, Char, S...)(ref R r, const(Char)[] fmt, auto ref S args)
-{
- import std.typecons : isTuple;
-
- auto spec = FormatSpec!Char(fmt);
- static if (!S.length)
- {
- spec.readUpToNextSpec(r);
- enforce(spec.trailing.empty, "Trailing characters in formattedRead format string");
- return 0;
- }
- else
- {
- enum hasPointer = isPointer!(typeof(args[0]));
-
- // The function below accounts for '*' == fields meant to be
- // read and skipped
- void skipUnstoredFields()
- {
- for (;;)
- {
- spec.readUpToNextSpec(r);
- if (spec.width != spec.DYNAMIC) break;
- // must skip this field
- skipData(r, spec);
- }
- }
-
- skipUnstoredFields();
- if (r.empty)
- {
- // Input is empty, nothing to read
- return 0;
- }
- static if (hasPointer)
- alias A = typeof(*args[0]);
- else
- alias A = typeof(args[0]);
-
- static if (isTuple!A)
- {
- foreach (i, T; A.Types)
- {
- static if (hasPointer)
- (*args[0])[i] = unformatValue!(T)(r, spec);
- else
- args[0][i] = unformatValue!(T)(r, spec);
- skipUnstoredFields();
- }
- }
- else
- {
- static if (hasPointer)
- *args[0] = unformatValue!(A)(r, spec);
- else
- args[0] = unformatValue!(A)(r, spec);
- }
- return 1 + formattedRead(r, spec.trailing, args[1 .. $]);
- }
-}
-
-/// The format string can be checked at compile-time (see $(LREF format) for details):
-@safe pure unittest
-{
- string s = "hello!124:34.5";
- string a;
- int b;
- double c;
- s.formattedRead!"%s!%s:%s"(a, b, c);
- assert(a == "hello" && b == 124 && c == 34.5);
-}
-
-@safe unittest
-{
- import std.math;
- string s = " 1.2 3.4 ";
- double x, y, z;
- assert(formattedRead(s, " %s %s %s ", x, y, z) == 2);
- assert(s.empty);
- assert(approxEqual(x, 1.2));
- assert(approxEqual(y, 3.4));
- assert(isNaN(z));
-}
-
-// for backwards compatibility
-@system pure unittest
-{
- string s = "hello!124:34.5";
- string a;
- int b;
- double c;
- formattedRead(s, "%s!%s:%s", &a, &b, &c);
- assert(a == "hello" && b == 124 && c == 34.5);
-
- // mix pointers and auto-ref
- s = "world!200:42.25";
- formattedRead(s, "%s!%s:%s", a, &b, &c);
- assert(a == "world" && b == 200 && c == 42.25);
-
- s = "world1!201:42.5";
- formattedRead(s, "%s!%s:%s", &a, &b, c);
- assert(a == "world1" && b == 201 && c == 42.5);
-
- s = "world2!202:42.75";
- formattedRead(s, "%s!%s:%s", a, b, &c);
- assert(a == "world2" && b == 202 && c == 42.75);
-}
-
-// for backwards compatibility
-@system pure unittest
-{
- import std.math;
- string s = " 1.2 3.4 ";
- double x, y, z;
- assert(formattedRead(s, " %s %s %s ", &x, &y, &z) == 2);
- assert(s.empty);
- assert(approxEqual(x, 1.2));
- assert(approxEqual(y, 3.4));
- assert(isNaN(z));
-}
-
-@system pure unittest
-{
- string line;
-
- bool f1;
-
- line = "true";
- formattedRead(line, "%s", &f1);
- assert(f1);
-
- line = "TrUE";
- formattedRead(line, "%s", &f1);
- assert(f1);
-
- line = "false";
- formattedRead(line, "%s", &f1);
- assert(!f1);
-
- line = "fALsE";
- formattedRead(line, "%s", &f1);
- assert(!f1);
-
- line = "1";
- formattedRead(line, "%d", &f1);
- assert(f1);
-
- line = "-1";
- formattedRead(line, "%d", &f1);
- assert(f1);
-
- line = "0";
- formattedRead(line, "%d", &f1);
- assert(!f1);
-
- line = "-0";
- formattedRead(line, "%d", &f1);
- assert(!f1);
-}
-
-@system pure unittest
-{
- union B
- {
- char[int.sizeof] untyped;
- int typed;
- }
- B b;
- b.typed = 5;
- char[] input = b.untyped[];
- int witness;
- formattedRead(input, "%r", &witness);
- assert(witness == b.typed);
-}
-
-@system pure unittest
-{
- union A
- {
- char[float.sizeof] untyped;
- float typed;
- }
- A a;
- a.typed = 5.5;
- char[] input = a.untyped[];
- float witness;
- formattedRead(input, "%r", &witness);
- assert(witness == a.typed);
-}
-
-@system pure unittest
-{
- import std.typecons;
- char[] line = "1 2".dup;
- int a, b;
- formattedRead(line, "%s %s", &a, &b);
- assert(a == 1 && b == 2);
-
- line = "10 2 3".dup;
- formattedRead(line, "%d ", &a);
- assert(a == 10);
- assert(line == "2 3");
-
- Tuple!(int, float) t;
- line = "1 2.125".dup;
- formattedRead(line, "%d %g", &t);
- assert(t[0] == 1 && t[1] == 2.125);
-
- line = "1 7643 2.125".dup;
- formattedRead(line, "%s %*u %s", &t);
- assert(t[0] == 1 && t[1] == 2.125);
-}
-
-@system pure unittest
-{
- string line;
-
- char c1, c2;
-
- line = "abc";
- formattedRead(line, "%s%c", &c1, &c2);
- assert(c1 == 'a' && c2 == 'b');
- assert(line == "c");
-}
-
-@system pure unittest
-{
- string line;
-
- line = "[1,2,3]";
- int[] s1;
- formattedRead(line, "%s", &s1);
- assert(s1 == [1,2,3]);
-}
-
-@system pure unittest
-{
- string line;
-
- line = "[1,2,3]";
- int[] s1;
- formattedRead(line, "[%(%s,%)]", &s1);
- assert(s1 == [1,2,3]);
-
- line = `["hello", "world"]`;
- string[] s2;
- formattedRead(line, "[%(%s, %)]", &s2);
- assert(s2 == ["hello", "world"]);
-
- line = "123 456";
- int[] s3;
- formattedRead(line, "%(%s %)", &s3);
- assert(s3 == [123, 456]);
-
- line = "h,e,l,l,o; w,o,r,l,d";
- string[] s4;
- formattedRead(line, "%(%(%c,%); %)", &s4);
- assert(s4 == ["hello", "world"]);
-}
-
-@system pure unittest
-{
- string line;
-
- int[4] sa1;
- line = `[1,2,3,4]`;
- formattedRead(line, "%s", &sa1);
- assert(sa1 == [1,2,3,4]);
-
- int[4] sa2;
- line = `[1,2,3]`;
- assertThrown(formattedRead(line, "%s", &sa2));
-
- int[4] sa3;
- line = `[1,2,3,4,5]`;
- assertThrown(formattedRead(line, "%s", &sa3));
-}
-
-@system pure unittest
-{
- string input;
-
- int[4] sa1;
- input = `[1,2,3,4]`;
- formattedRead(input, "[%(%s,%)]", &sa1);
- assert(sa1 == [1,2,3,4]);
-
- int[4] sa2;
- input = `[1,2,3]`;
- assertThrown(formattedRead(input, "[%(%s,%)]", &sa2));
-}
-
-@system pure unittest
-{
- string line;
-
- string s1, s2;
-
- line = "hello, world";
- formattedRead(line, "%s", &s1);
- assert(s1 == "hello, world", s1);
-
- line = "hello, world;yah";
- formattedRead(line, "%s;%s", &s1, &s2);
- assert(s1 == "hello, world", s1);
- assert(s2 == "yah", s2);
-
- line = `['h','e','l','l','o']`;
- string s3;
- formattedRead(line, "[%(%s,%)]", &s3);
- assert(s3 == "hello");
-
- line = `"hello"`;
- string s4;
- formattedRead(line, "\"%(%c%)\"", &s4);
- assert(s4 == "hello");
-}
-
-@system pure unittest
-{
- string line;
-
- string[int] aa1;
- line = `[1:"hello", 2:"world"]`;
- formattedRead(line, "%s", &aa1);
- assert(aa1 == [1:"hello", 2:"world"]);
-
- int[string] aa2;
- line = `{"hello"=1; "world"=2}`;
- formattedRead(line, "{%(%s=%s; %)}", &aa2);
- assert(aa2 == ["hello":1, "world":2]);
-
- int[string] aa3;
- line = `{[hello=1]; [world=2]}`;
- formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3);
- assert(aa3 == ["hello":1, "world":2]);
-}
-
-template FormatSpec(Char)
-if (!is(Unqual!Char == Char))
-{
- alias FormatSpec = FormatSpec!(Unqual!Char);
-}
-
-/**
- * A General handler for $(D printf) style format specifiers. Used for building more
- * specific formatting functions.
- */
-struct FormatSpec(Char)
-if (is(Unqual!Char == Char))
-{
- import std.algorithm.searching : startsWith;
- import std.ascii : isDigit, isPunctuation, isAlpha;
- import std.conv : parse, text, to;
-
- /**
- Minimum _width, default $(D 0).
- */
- int width = 0;
-
- /**
- Precision. Its semantics depends on the argument type. For
- floating point numbers, _precision dictates the number of
- decimals printed.
- */
- int precision = UNSPECIFIED;
-
- /**
- Number of digits printed between _separators.
- */
- int separators = UNSPECIFIED;
-
- /**
- Set to `DYNAMIC` when the separator character is supplied at runtime.
- */
- int separatorCharPos = UNSPECIFIED;
-
- /**
- Character to insert between digits.
- */
- dchar separatorChar = ',';
-
- /**
- Special value for width and precision. $(D DYNAMIC) width or
- precision means that they were specified with $(D '*') in the
- format string and are passed at runtime through the varargs.
- */
- enum int DYNAMIC = int.max;
-
- /**
- Special value for precision, meaning the format specifier
- contained no explicit precision.
- */
- enum int UNSPECIFIED = DYNAMIC - 1;
-
- /**
- The actual format specifier, $(D 's') by default.
- */
- char spec = 's';
-
- /**
- Index of the argument for positional parameters, from $(D 1) to
- $(D ubyte.max). ($(D 0) means not used).
- */
- ubyte indexStart;
-
- /**
- Index of the last argument for positional parameter range, from
- $(D 1) to $(D ubyte.max). ($(D 0) means not used).
- */
- ubyte indexEnd;
-
- version (StdDdoc)
- {
- /**
- The format specifier contained a $(D '-') ($(D printf)
- compatibility).
- */
- bool flDash;
-
- /**
- The format specifier contained a $(D '0') ($(D printf)
- compatibility).
- */
- bool flZero;
-
- /**
- The format specifier contained a $(D ' ') ($(D printf)
- compatibility).
- */
- bool flSpace;
-
- /**
- The format specifier contained a $(D '+') ($(D printf)
- compatibility).
- */
- bool flPlus;
-
- /**
- The format specifier contained a $(D '#') ($(D printf)
- compatibility).
- */
- bool flHash;
-
- /**
- The format specifier contained a $(D ',')
- */
- bool flSeparator;
-
- // Fake field to allow compilation
- ubyte allFlags;
- }
- else
- {
- union
- {
- import std.bitmanip : bitfields;
- mixin(bitfields!(
- bool, "flDash", 1,
- bool, "flZero", 1,
- bool, "flSpace", 1,
- bool, "flPlus", 1,
- bool, "flHash", 1,
- bool, "flSeparator", 1,
- ubyte, "", 2));
- ubyte allFlags;
- }
- }
-
- /**
- In case of a compound format specifier starting with $(D
- "%$(LPAREN)") and ending with $(D "%$(RPAREN)"), $(D _nested)
- contains the string contained within the two separators.
- */
- const(Char)[] nested;
-
- /**
- In case of a compound format specifier, $(D _sep) contains the
- string positioning after $(D "%|").
- `sep is null` means no separator else `sep.empty` means 0 length
- separator.
- */
- const(Char)[] sep;
-
- /**
- $(D _trailing) contains the rest of the format string.
- */
- const(Char)[] trailing;
-
- /*
- This string is inserted before each sequence (e.g. array)
- formatted (by default $(D "[")).
- */
- enum immutable(Char)[] seqBefore = "[";
-
- /*
- This string is inserted after each sequence formatted (by
- default $(D "]")).
- */
- enum immutable(Char)[] seqAfter = "]";
-
- /*
- This string is inserted after each element keys of a sequence (by
- default $(D ":")).
- */
- enum immutable(Char)[] keySeparator = ":";
-
- /*
- This string is inserted in between elements of a sequence (by
- default $(D ", ")).
- */
- enum immutable(Char)[] seqSeparator = ", ";
-
- /**
- Construct a new $(D FormatSpec) using the format string $(D fmt), no
- processing is done until needed.
- */
- this(in Char[] fmt) @safe pure
- {
- trailing = fmt;
- }
-
- bool writeUpToNextSpec(OutputRange)(ref OutputRange writer)
- {
- if (trailing.empty)
- return false;
- for (size_t i = 0; i < trailing.length; ++i)
- {
- if (trailing[i] != '%') continue;
- put(writer, trailing[0 .. i]);
- trailing = trailing[i .. $];
- enforceFmt(trailing.length >= 2, `Unterminated format specifier: "%"`);
- trailing = trailing[1 .. $];
-
- if (trailing[0] != '%')
- {
- // Spec found. Fill up the spec, and bailout
- fillUp();
- return true;
- }
- // Doubled! Reset and Keep going
- i = 0;
- }
- // no format spec found
- put(writer, trailing);
- trailing = null;
- return false;
- }
-
- @safe unittest
- {
- import std.array;
- auto w = appender!(char[])();
- auto f = FormatSpec("abc%sdef%sghi");
- f.writeUpToNextSpec(w);
- assert(w.data == "abc", w.data);
- assert(f.trailing == "def%sghi", text(f.trailing));
- f.writeUpToNextSpec(w);
- assert(w.data == "abcdef", w.data);
- assert(f.trailing == "ghi");
- // test with embedded %%s
- f = FormatSpec("ab%%cd%%ef%sg%%h%sij");
- w.clear();
- f.writeUpToNextSpec(w);
- assert(w.data == "ab%cd%ef" && f.trailing == "g%%h%sij", w.data);
- f.writeUpToNextSpec(w);
- assert(w.data == "ab%cd%efg%h" && f.trailing == "ij");
- // bug4775
- f = FormatSpec("%%%s");
- w.clear();
- f.writeUpToNextSpec(w);
- assert(w.data == "%" && f.trailing == "");
- f = FormatSpec("%%%%%s%%");
- w.clear();
- while (f.writeUpToNextSpec(w)) continue;
- assert(w.data == "%%%");
-
- f = FormatSpec("a%%b%%c%");
- w.clear();
- assertThrown!FormatException(f.writeUpToNextSpec(w));
- assert(w.data == "a%b%c" && f.trailing == "%");
- }
-
- private void fillUp()
- {
- // Reset content
- if (__ctfe)
- {
- flDash = false;
- flZero = false;
- flSpace = false;
- flPlus = false;
- flHash = false;
- flSeparator = false;
- }
- else
- {
- allFlags = 0;
- }
-
- width = 0;
- precision = UNSPECIFIED;
- nested = null;
- // Parse the spec (we assume we're past '%' already)
- for (size_t i = 0; i < trailing.length; )
- {
- switch (trailing[i])
- {
- case '(':
- // Embedded format specifier.
- auto j = i + 1;
- // Get the matching balanced paren
- for (uint innerParens;;)
- {
- enforceFmt(j + 1 < trailing.length,
- text("Incorrect format specifier: %", trailing[i .. $]));
- if (trailing[j++] != '%')
- {
- // skip, we're waiting for %( and %)
- continue;
- }
- if (trailing[j] == '-') // for %-(
- {
- ++j; // skip
- enforceFmt(j < trailing.length,
- text("Incorrect format specifier: %", trailing[i .. $]));
- }
- if (trailing[j] == ')')
- {
- if (innerParens-- == 0) break;
- }
- else if (trailing[j] == '|')
- {
- if (innerParens == 0) break;
- }
- else if (trailing[j] == '(')
- {
- ++innerParens;
- }
- }
- if (trailing[j] == '|')
- {
- auto k = j;
- for (++j;;)
- {
- if (trailing[j++] != '%')
- continue;
- if (trailing[j] == '%')
- ++j;
- else if (trailing[j] == ')')
- break;
- else
- throw new Exception(
- text("Incorrect format specifier: %",
- trailing[j .. $]));
- }
- nested = trailing[i + 1 .. k - 1];
- sep = trailing[k + 1 .. j - 1];
- }
- else
- {
- nested = trailing[i + 1 .. j - 1];
- sep = null; // no separator
- }
- //this = FormatSpec(innerTrailingSpec);
- spec = '(';
- // We practically found the format specifier
- trailing = trailing[j + 1 .. $];
- return;
- case '-': flDash = true; ++i; break;
- case '+': flPlus = true; ++i; break;
- case '#': flHash = true; ++i; break;
- case '0': flZero = true; ++i; break;
- case ' ': flSpace = true; ++i; break;
- case '*':
- if (isDigit(trailing[++i]))
- {
- // a '*' followed by digits and '$' is a
- // positional format
- trailing = trailing[1 .. $];
- width = -parse!(typeof(width))(trailing);
- i = 0;
- enforceFmt(trailing[i++] == '$',
- "$ expected");
- }
- else
- {
- // read result
- width = DYNAMIC;
- }
- break;
- case '1': .. case '9':
- auto tmp = trailing[i .. $];
- const widthOrArgIndex = parse!uint(tmp);
- enforceFmt(tmp.length,
- text("Incorrect format specifier %", trailing[i .. $]));
- i = arrayPtrDiff(tmp, trailing);
- if (tmp.startsWith('$'))
- {
- // index of the form %n$
- indexEnd = indexStart = to!ubyte(widthOrArgIndex);
- ++i;
- }
- else if (tmp.startsWith(':'))
- {
- // two indexes of the form %m:n$, or one index of the form %m:$
- indexStart = to!ubyte(widthOrArgIndex);
- tmp = tmp[1 .. $];
- if (tmp.startsWith('$'))
- {
- indexEnd = indexEnd.max;
- }
- else
- {
- indexEnd = parse!(typeof(indexEnd))(tmp);
- }
- i = arrayPtrDiff(tmp, trailing);
- enforceFmt(trailing[i++] == '$',
- "$ expected");
- }
- else
- {
- // width
- width = to!int(widthOrArgIndex);
- }
- break;
- case ',':
- // Precision
- ++i;
- flSeparator = true;
-
- if (trailing[i] == '*')
- {
- ++i;
- // read result
- separators = DYNAMIC;
- }
- else if (isDigit(trailing[i]))
- {
- auto tmp = trailing[i .. $];
- separators = parse!int(tmp);
- i = arrayPtrDiff(tmp, trailing);
- }
- else
- {
- // "," was specified, but nothing after it
- separators = 3;
- }
-
- if (trailing[i] == '?')
- {
- separatorCharPos = DYNAMIC;
- ++i;
- }
-
- break;
- case '.':
- // Precision
- if (trailing[++i] == '*')
- {
- if (isDigit(trailing[++i]))
- {
- // a '.*' followed by digits and '$' is a
- // positional precision
- trailing = trailing[i .. $];
- i = 0;
- precision = -parse!int(trailing);
- enforceFmt(trailing[i++] == '$',
- "$ expected");
- }
- else
- {
- // read result
- precision = DYNAMIC;
- }
- }
- else if (trailing[i] == '-')
- {
- // negative precision, as good as 0
- precision = 0;
- auto tmp = trailing[i .. $];
- parse!int(tmp); // skip digits
- i = arrayPtrDiff(tmp, trailing);
- }
- else if (isDigit(trailing[i]))
- {
- auto tmp = trailing[i .. $];
- precision = parse!int(tmp);
- i = arrayPtrDiff(tmp, trailing);
- }
- else
- {
- // "." was specified, but nothing after it
- precision = 0;
- }
- break;
- default:
- // this is the format char
- spec = cast(char) trailing[i++];
- trailing = trailing[i .. $];
- return;
- } // end switch
- } // end for
- throw new Exception(text("Incorrect format specifier: ", trailing));
- }
-
- //--------------------------------------------------------------------------
- private bool readUpToNextSpec(R)(ref R r)
- {
- import std.ascii : isLower, isWhite;
- import std.utf : stride;
-
- // Reset content
- if (__ctfe)
- {
- flDash = false;
- flZero = false;
- flSpace = false;
- flPlus = false;
- flHash = false;
- flSeparator = false;
- }
- else
- {
- allFlags = 0;
- }
- width = 0;
- precision = UNSPECIFIED;
- nested = null;
- // Parse the spec
- while (trailing.length)
- {
- const c = trailing[0];
- if (c == '%' && trailing.length > 1)
- {
- const c2 = trailing[1];
- if (c2 == '%')
- {
- assert(!r.empty);
- // Require a '%'
- if (r.front != '%') break;
- trailing = trailing[2 .. $];
- r.popFront();
- }
- else
- {
- enforce(isLower(c2) || c2 == '*' ||
- c2 == '(',
- text("'%", c2,
- "' not supported with formatted read"));
- trailing = trailing[1 .. $];
- fillUp();
- return true;
- }
- }
- else
- {
- if (c == ' ')
- {
- while (!r.empty && isWhite(r.front)) r.popFront();
- //r = std.algorithm.find!(not!(isWhite))(r);
- }
- else
- {
- enforce(!r.empty,
- text("parseToFormatSpec: Cannot find character '",
- c, "' in the input string."));
- if (r.front != trailing.front) break;
- r.popFront();
- }
- trailing = trailing[stride(trailing, 0) .. $];
- }
- }
- return false;
- }
-
- private string getCurFmtStr() const
- {
- import std.array : appender;
- auto w = appender!string();
- auto f = FormatSpec!Char("%s"); // for stringnize
-
- put(w, '%');
- if (indexStart != 0)
- {
- formatValue(w, indexStart, f);
- put(w, '$');
- }
- if (flDash) put(w, '-');
- if (flZero) put(w, '0');
- if (flSpace) put(w, ' ');
- if (flPlus) put(w, '+');
- if (flHash) put(w, '#');
- if (flSeparator) put(w, ',');
- if (width != 0)
- formatValue(w, width, f);
- if (precision != FormatSpec!Char.UNSPECIFIED)
- {
- put(w, '.');
- formatValue(w, precision, f);
- }
- put(w, spec);
- return w.data;
- }
-
- @safe unittest
- {
- // issue 5237
- import std.array;
- auto w = appender!string();
- auto f = FormatSpec!char("%.16f");
- f.writeUpToNextSpec(w); // dummy eating
- assert(f.spec == 'f');
- auto fmt = f.getCurFmtStr();
- assert(fmt == "%.16f");
- }
-
- private const(Char)[] headUpToNextSpec()
- {
- import std.array : appender;
- auto w = appender!(typeof(return))();
- auto tr = trailing;
-
- while (tr.length)
- {
- if (tr[0] == '%')
- {
- if (tr.length > 1 && tr[1] == '%')
- {
- tr = tr[2 .. $];
- w.put('%');
- }
- else
- break;
- }
- else
- {
- w.put(tr.front);
- tr.popFront();
- }
- }
- return w.data;
- }
-
- string toString()
- {
- return text("address = ", cast(void*) &this,
- "\nwidth = ", width,
- "\nprecision = ", precision,
- "\nspec = ", spec,
- "\nindexStart = ", indexStart,
- "\nindexEnd = ", indexEnd,
- "\nflDash = ", flDash,
- "\nflZero = ", flZero,
- "\nflSpace = ", flSpace,
- "\nflPlus = ", flPlus,
- "\nflHash = ", flHash,
- "\nflSeparator = ", flSeparator,
- "\nnested = ", nested,
- "\ntrailing = ", trailing, "\n");
- }
-}
-
-///
-@safe pure unittest
-{
- import std.array;
- auto a = appender!(string)();
- auto fmt = "Number: %2.4e\nString: %s";
- auto f = FormatSpec!char(fmt);
-
- f.writeUpToNextSpec(a);
-
- assert(a.data == "Number: ");
- assert(f.trailing == "\nString: %s");
- assert(f.spec == 'e');
- assert(f.width == 2);
- assert(f.precision == 4);
-
- f.writeUpToNextSpec(a);
-
- assert(a.data == "Number: \nString: ");
- assert(f.trailing == "");
- assert(f.spec == 's');
-}
-
-// Issue 14059
-@safe unittest
-{
- import std.array : appender;
- auto a = appender!(string)();
-
- auto f = FormatSpec!char("%-(%s%"); // %)")
- assertThrown(f.writeUpToNextSpec(a));
-
- f = FormatSpec!char("%(%-"); // %)")
- assertThrown(f.writeUpToNextSpec(a));
-}
-
-@safe unittest
-{
- import std.array : appender;
- auto a = appender!(string)();
-
- auto f = FormatSpec!char("%,d");
- f.writeUpToNextSpec(a);
-
- assert(f.spec == 'd', format("%s", f.spec));
- assert(f.precision == FormatSpec!char.UNSPECIFIED);
- assert(f.separators == 3);
-
- f = FormatSpec!char("%5,10f");
- f.writeUpToNextSpec(a);
- assert(f.spec == 'f', format("%s", f.spec));
- assert(f.separators == 10);
- assert(f.width == 5);
-
- f = FormatSpec!char("%5,10.4f");
- f.writeUpToNextSpec(a);
- assert(f.spec == 'f', format("%s", f.spec));
- assert(f.separators == 10);
- assert(f.width == 5);
- assert(f.precision == 4);
-}
-
-/**
-Helper function that returns a $(D FormatSpec) for a single specifier given
-in $(D fmt).
-
-Params:
- fmt = A format specifier.
-
-Returns:
- A $(D FormatSpec) with the specifier parsed.
-Throws:
- An `Exception` when more than one specifier is given or the specifier
- is malformed.
- */
-FormatSpec!Char singleSpec(Char)(Char[] fmt)
-{
- import std.conv : text;
- enforce(fmt.length >= 2, "fmt must be at least 2 characters long");
- enforce(fmt.front == '%', "fmt must start with a '%' character");
-
- static struct DummyOutputRange {
- void put(C)(C[] buf) {} // eat elements
- }
- auto a = DummyOutputRange();
- auto spec = FormatSpec!Char(fmt);
- //dummy write
- spec.writeUpToNextSpec(a);
-
- enforce(spec.trailing.empty,
- text("Trailing characters in fmt string: '", spec.trailing));
-
- return spec;
-}
-
-///
-@safe pure unittest
-{
- import std.exception : assertThrown;
- auto spec = singleSpec("%2.3e");
-
- assert(spec.trailing == "");
- assert(spec.spec == 'e');
- assert(spec.width == 2);
- assert(spec.precision == 3);
-
- assertThrown(singleSpec(""));
- assertThrown(singleSpec("2.3e"));
- assertThrown(singleSpec("%2.3eTest"));
-}
-
-/**
-$(D bool)s are formatted as "true" or "false" with %s and as "1" or
-"0" with integral-specific format specs.
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(BooleanTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
-{
- BooleanTypeOf!T val = obj;
-
- if (f.spec == 's')
- {
- string s = val ? "true" : "false";
- if (!f.flDash)
- {
- // right align
- if (f.width > s.length)
- foreach (i ; 0 .. f.width - s.length) put(w, ' ');
- put(w, s);
- }
- else
- {
- // left align
- put(w, s);
- if (f.width > s.length)
- foreach (i ; 0 .. f.width - s.length) put(w, ' ');
- }
- }
- else
- formatValue(w, cast(int) val, f);
-}
-
-///
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%s");
- formatValue(w, true, spec);
-
- assert(w.data == "true");
-}
-
-@safe pure unittest
-{
- assertCTFEable!(
- {
- formatTest( false, "false" );
- formatTest( true, "true" );
- });
-}
-@system unittest
-{
- class C1 { bool val; alias val this; this(bool v){ val = v; } }
- class C2 { bool val; alias val this; this(bool v){ val = v; }
- override string toString() const { return "C"; } }
- formatTest( new C1(false), "false" );
- formatTest( new C1(true), "true" );
- formatTest( new C2(false), "C" );
- formatTest( new C2(true), "C" );
-
- struct S1 { bool val; alias val this; }
- struct S2 { bool val; alias val this;
- string toString() const { return "S"; } }
- formatTest( S1(false), "false" );
- formatTest( S1(true), "true" );
- formatTest( S2(false), "S" );
- formatTest( S2(true), "S" );
-}
-
-@safe pure unittest
-{
- string t1 = format("[%6s] [%6s] [%-6s]", true, false, true);
- assert(t1 == "[ true] [ false] [true ]");
-
- string t2 = format("[%3s] [%-2s]", true, false);
- assert(t2 == "[true] [false]");
-}
-
-/**
-$(D null) literal is formatted as $(D "null").
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(Unqual!T == typeof(null)) && !is(T == enum) && !hasToString!(T, Char))
-{
- enforceFmt(f.spec == 's',
- "null literal cannot match %" ~ f.spec);
-
- put(w, "null");
-}
-
-///
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%s");
- formatValue(w, null, spec);
-
- assert(w.data == "null");
-}
-
-@safe pure unittest
-{
- assert(collectExceptionMsg!FormatException(format("%p", null)).back == 'p');
-
- assertCTFEable!(
- {
- formatTest( null, "null" );
- });
-}
-
-/**
-Integrals are formatted like $(D printf) does.
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
-{
- alias U = IntegralTypeOf!T;
- U val = obj; // Extracting alias this may be impure/system/may-throw
-
- if (f.spec == 'r')
- {
- // raw write, skip all else and write the thing
- auto raw = (ref val)@trusted{
- return (cast(const char*) &val)[0 .. val.sizeof];
- }(val);
- if (needToSwapEndianess(f))
- {
- foreach_reverse (c; raw)
- put(w, c);
- }
- else
- {
- foreach (c; raw)
- put(w, c);
- }
- return;
- }
-
- immutable uint base =
- f.spec == 'x' || f.spec == 'X' ? 16 :
- f.spec == 'o' ? 8 :
- f.spec == 'b' ? 2 :
- f.spec == 's' || f.spec == 'd' || f.spec == 'u' ? 10 :
- 0;
- enforceFmt(base > 0,
- "incompatible format character for integral argument: %" ~ f.spec);
-
- // Forward on to formatIntegral to handle both U and const(U)
- // Saves duplication of code for both versions.
- static if (is(ucent) && (is(U == cent) || is(U == ucent)))
- alias C = U;
- else static if (isSigned!U)
- alias C = long;
- else
- alias C = ulong;
- formatIntegral(w, cast(C) val, f, base, Unsigned!U.max);
-}
-
-///
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%d");
- formatValue(w, 1337, spec);
-
- assert(w.data == "1337");
-}
-
-private void formatIntegral(Writer, T, Char)(ref Writer w, const(T) val, const ref FormatSpec!Char fs,
- uint base, ulong mask)
-{
- T arg = val;
-
- immutable negative = (base == 10 && arg < 0);
- if (negative)
- {
- arg = -arg;
- }
-
- // All unsigned integral types should fit in ulong.
- static if (is(ucent) && is(typeof(arg) == ucent))
- formatUnsigned(w, (cast(ucent) arg) & mask, fs, base, negative);
- else
- formatUnsigned(w, (cast(ulong) arg) & mask, fs, base, negative);
-}
-
-private void formatUnsigned(Writer, T, Char)
-(ref Writer w, T arg, const ref FormatSpec!Char fs, uint base, bool negative)
-{
- /* Write string:
- * leftpad prefix1 prefix2 zerofill digits rightpad
- */
-
- /* Convert arg to digits[].
- * Note that 0 becomes an empty digits[]
- */
- char[64] buffer = void; // 64 bits in base 2 at most
- char[] digits;
- if (arg < base && base <= 10 && arg)
- {
- // Most numbers are a single digit - avoid expensive divide
- buffer[0] = cast(char)(arg + '0');
- digits = buffer[0 .. 1];
- }
- else
- {
- size_t i = buffer.length;
- while (arg)
- {
- --i;
- char c = cast(char) (arg % base);
- arg /= base;
- if (c < 10)
- buffer[i] = cast(char)(c + '0');
- else
- buffer[i] = cast(char)(c + (fs.spec == 'x' ? 'a' - 10 : 'A' - 10));
- }
- digits = buffer[i .. $]; // got the digits without the sign
- }
-
-
- immutable precision = (fs.precision == fs.UNSPECIFIED) ? 1 : fs.precision;
-
- char padChar = 0;
- if (!fs.flDash)
- {
- padChar = (fs.flZero && fs.precision == fs.UNSPECIFIED) ? '0' : ' ';
- }
-
- // Compute prefix1 and prefix2
- char prefix1 = 0;
- char prefix2 = 0;
- if (base == 10)
- {
- if (negative)
- prefix1 = '-';
- else if (fs.flPlus)
- prefix1 = '+';
- else if (fs.flSpace)
- prefix1 = ' ';
- }
- else if (base == 16 && fs.flHash && digits.length)
- {
- prefix1 = '0';
- prefix2 = fs.spec == 'x' ? 'x' : 'X';
- }
- // adjust precision to print a '0' for octal if alternate format is on
- else if (base == 8 && fs.flHash &&
- (precision <= 1 || precision <= digits.length) && // too low precision
- digits.length > 0)
- prefix1 = '0';
-
- size_t zerofill = precision > digits.length ? precision - digits.length : 0;
- size_t leftpad = 0;
- size_t rightpad = 0;
-
- immutable ptrdiff_t spacesToPrint =
- fs.width - (
- (prefix1 != 0)
- + (prefix2 != 0)
- + zerofill
- + digits.length
- + ((fs.flSeparator != 0) * (digits.length / fs.separators))
- );
- if (spacesToPrint > 0) // need to do some padding
- {
- if (padChar == '0')
- zerofill += spacesToPrint;
- else if (padChar)
- leftpad = spacesToPrint;
- else
- rightpad = spacesToPrint;
- }
-
- // Print
- foreach (i ; 0 .. leftpad)
- put(w, ' ');
-
- if (prefix1) put(w, prefix1);
- if (prefix2) put(w, prefix2);
-
- foreach (i ; 0 .. zerofill)
- put(w, '0');
-
- if (fs.flSeparator)
- {
- for (size_t j = 0; j < digits.length; ++j)
- {
- if (j != 0 && (digits.length - j) % fs.separators == 0)
- {
- put(w, fs.separatorChar);
- }
- put(w, digits[j]);
- }
- }
- else
- {
- put(w, digits);
- }
-
- foreach (i ; 0 .. rightpad)
- put(w, ' ');
-}
-
-@safe pure unittest
-{
- assert(collectExceptionMsg!FormatException(format("%c", 5)).back == 'c');
-
- assertCTFEable!(
- {
- formatTest(9, "9");
- formatTest( 10, "10" );
- });
-}
-
-@system unittest
-{
- class C1 { long val; alias val this; this(long v){ val = v; } }
- class C2 { long val; alias val this; this(long v){ val = v; }
- override string toString() const { return "C"; } }
- formatTest( new C1(10), "10" );
- formatTest( new C2(10), "C" );
-
- struct S1 { long val; alias val this; }
- struct S2 { long val; alias val this;
- string toString() const { return "S"; } }
- formatTest( S1(10), "10" );
- formatTest( S2(10), "S" );
-}
-
-// bugzilla 9117
-@safe unittest
-{
- static struct Frop {}
-
- static struct Foo
- {
- int n = 0;
- alias n this;
- T opCast(T) () if (is(T == Frop))
- {
- return Frop();
- }
- string toString()
- {
- return "Foo";
- }
- }
-
- static struct Bar
- {
- Foo foo;
- alias foo this;
- string toString()
- {
- return "Bar";
- }
- }
-
- const(char)[] result;
- void put(const char[] s){ result ~= s; }
-
- Foo foo;
- formattedWrite(&put, "%s", foo); // OK
- assert(result == "Foo");
-
- result = null;
-
- Bar bar;
- formattedWrite(&put, "%s", bar); // NG
- assert(result == "Bar");
-
- result = null;
-
- int i = 9;
- formattedWrite(&put, "%s", 9);
- assert(result == "9");
-}
-
-private enum ctfpMessage = "Cannot format floating point types at compile-time";
-
-/**
-Floating-point values are formatted like $(D printf) does.
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
-{
- import std.algorithm.comparison : min;
- import std.algorithm.searching : find;
- import std.string : indexOf, indexOfAny, indexOfNeither;
-
- FormatSpec!Char fs = f; // fs is copy for change its values.
- FloatingPointTypeOf!T val = obj;
-
- if (fs.spec == 'r')
- {
- // raw write, skip all else and write the thing
- auto raw = (ref val)@trusted{
- return (cast(const char*) &val)[0 .. val.sizeof];
- }(val);
- if (needToSwapEndianess(f))
- {
- foreach_reverse (c; raw)
- put(w, c);
- }
- else
- {
- foreach (c; raw)
- put(w, c);
- }
- return;
- }
- enforceFmt(find("fgFGaAeEs", fs.spec).length,
- "incompatible format character for floating point argument: %" ~ fs.spec);
- enforceFmt(!__ctfe, ctfpMessage);
-
- version (CRuntime_Microsoft)
- {
- import std.math : isNaN, isInfinity;
- immutable double tval = val; // convert early to get "inf" in case of overflow
- string s;
- if (isNaN(tval))
- s = "nan"; // snprintf writes 1.#QNAN
- else if (isInfinity(tval))
- s = val >= 0 ? "inf" : "-inf"; // snprintf writes 1.#INF
-
- if (s.length > 0)
- {
- version (none)
- {
- return formatValue(w, s, f);
- }
- else // FIXME:workaround
- {
- s = s[0 .. f.precision < $ ? f.precision : $];
- if (!f.flDash)
- {
- // right align
- if (f.width > s.length)
- foreach (j ; 0 .. f.width - s.length) put(w, ' ');
- put(w, s);
- }
- else
- {
- // left align
- put(w, s);
- if (f.width > s.length)
- foreach (j ; 0 .. f.width - s.length) put(w, ' ');
- }
- return;
- }
- }
- }
- else
- alias tval = val;
- if (fs.spec == 's') fs.spec = 'g';
- char[1 /*%*/ + 5 /*flags*/ + 3 /*width.prec*/ + 2 /*format*/
- + 1 /*\0*/] sprintfSpec = void;
- sprintfSpec[0] = '%';
- uint i = 1;
- if (fs.flDash) sprintfSpec[i++] = '-';
- if (fs.flPlus) sprintfSpec[i++] = '+';
- if (fs.flZero) sprintfSpec[i++] = '0';
- if (fs.flSpace) sprintfSpec[i++] = ' ';
- if (fs.flHash) sprintfSpec[i++] = '#';
- sprintfSpec[i .. i + 3] = "*.*";
- i += 3;
- if (is(Unqual!(typeof(val)) == real)) sprintfSpec[i++] = 'L';
- sprintfSpec[i++] = fs.spec;
- sprintfSpec[i] = 0;
- //printf("format: '%s'; geeba: %g\n", sprintfSpec.ptr, val);
- char[512] buf = void;
-
- immutable n = ()@trusted{
- import core.stdc.stdio : snprintf;
- return snprintf(buf.ptr, buf.length,
- sprintfSpec.ptr,
- fs.width,
- // negative precision is same as no precision specified
- fs.precision == fs.UNSPECIFIED ? -1 : fs.precision,
- tval);
- }();
-
- enforceFmt(n >= 0,
- "floating point formatting failure");
-
- auto len = min(n, buf.length-1);
- ptrdiff_t dot = buf[0 .. len].indexOf('.');
- if (fs.flSeparator && dot != -1)
- {
- ptrdiff_t firstDigit = buf[0 .. len].indexOfAny("0123456789");
- ptrdiff_t ePos = buf[0 .. len].indexOf('e');
- size_t j;
-
- ptrdiff_t firstLen = dot - firstDigit;
-
- size_t separatorScoreCnt = firstLen / fs.separators;
-
- size_t afterDotIdx;
- if (ePos != -1)
- {
- afterDotIdx = ePos;
- }
- else
- {
- afterDotIdx = len;
- }
-
- if (dot != -1)
- {
- ptrdiff_t mantissaLen = afterDotIdx - (dot + 1);
- separatorScoreCnt += (mantissaLen > 0) ? (mantissaLen - 1) / fs.separators : 0;
- }
-
- // plus, minus prefix
- ptrdiff_t digitsBegin = buf[0 .. separatorScoreCnt].indexOfNeither(" ");
- if (digitsBegin == -1)
- {
- digitsBegin = separatorScoreCnt;
- }
- put(w, buf[digitsBegin .. firstDigit]);
-
- // digits until dot with separator
- for (j = 0; j < firstLen; ++j)
- {
- if (j > 0 && (firstLen - j) % fs.separators == 0)
- {
- put(w, fs.separatorChar);
- }
- put(w, buf[j + firstDigit]);
- }
- put(w, '.');
-
- // digits after dot
- for (j = dot + 1; j < afterDotIdx; ++j)
- {
- auto realJ = (j - (dot + 1));
- if (realJ != 0 && realJ % fs.separators == 0)
- {
- put(w, fs.separatorChar);
- }
- put(w, buf[j]);
- }
-
- // rest
- if (ePos != -1)
- {
- put(w, buf[afterDotIdx .. len]);
- }
- }
- else
- {
- put(w, buf[0 .. len]);
- }
-}
-
-///
-@safe unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%.1f");
- formatValue(w, 1337.7, spec);
-
- assert(w.data == "1337.7");
-}
-
-@safe /*pure*/ unittest // formatting floating point values is now impure
-{
- import std.conv : to;
-
- assert(collectExceptionMsg!FormatException(format("%d", 5.1)).back == 'd');
-
- foreach (T; AliasSeq!(float, double, real))
- {
- formatTest( to!( T)(5.5), "5.5" );
- formatTest( to!( const T)(5.5), "5.5" );
- formatTest( to!(immutable T)(5.5), "5.5" );
-
- formatTest( T.nan, "nan" );
- }
-}
-
-@system unittest
-{
- formatTest( 2.25, "2.25" );
-
- class C1 { double val; alias val this; this(double v){ val = v; } }
- class C2 { double val; alias val this; this(double v){ val = v; }
- override string toString() const { return "C"; } }
- formatTest( new C1(2.25), "2.25" );
- formatTest( new C2(2.25), "C" );
-
- struct S1 { double val; alias val this; }
- struct S2 { double val; alias val this;
- string toString() const { return "S"; } }
- formatTest( S1(2.25), "2.25" );
- formatTest( S2(2.25), "S" );
-}
-
-/*
-Formatting a $(D creal) is deprecated but still kept around for a while.
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(Unqual!T : creal) && !is(T == enum) && !hasToString!(T, Char))
-{
- immutable creal val = obj;
-
- formatValue(w, val.re, f);
- if (val.im >= 0)
- {
- put(w, '+');
- }
- formatValue(w, val.im, f);
- put(w, 'i');
-}
-
-@safe /*pure*/ unittest // formatting floating point values is now impure
-{
- import std.conv : to;
- foreach (T; AliasSeq!(cfloat, cdouble, creal))
- {
- formatTest( to!( T)(1 + 1i), "1+1i" );
- formatTest( to!( const T)(1 + 1i), "1+1i" );
- formatTest( to!(immutable T)(1 + 1i), "1+1i" );
- }
- foreach (T; AliasSeq!(cfloat, cdouble, creal))
- {
- formatTest( to!( T)(0 - 3i), "0-3i" );
- formatTest( to!( const T)(0 - 3i), "0-3i" );
- formatTest( to!(immutable T)(0 - 3i), "0-3i" );
- }
-}
-
-@system unittest
-{
- formatTest( 3+2.25i, "3+2.25i" );
-
- class C1 { cdouble val; alias val this; this(cdouble v){ val = v; } }
- class C2 { cdouble val; alias val this; this(cdouble v){ val = v; }
- override string toString() const { return "C"; } }
- formatTest( new C1(3+2.25i), "3+2.25i" );
- formatTest( new C2(3+2.25i), "C" );
-
- struct S1 { cdouble val; alias val this; }
- struct S2 { cdouble val; alias val this;
- string toString() const { return "S"; } }
- formatTest( S1(3+2.25i), "3+2.25i" );
- formatTest( S2(3+2.25i), "S" );
-}
-
-/*
- Formatting an $(D ireal) is deprecated but still kept around for a while.
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(Unqual!T : ireal) && !is(T == enum) && !hasToString!(T, Char))
-{
- immutable ireal val = obj;
-
- formatValue(w, val.im, f);
- put(w, 'i');
-}
-
-@safe /*pure*/ unittest // formatting floating point values is now impure
-{
- import std.conv : to;
- foreach (T; AliasSeq!(ifloat, idouble, ireal))
- {
- formatTest( to!( T)(1i), "1i" );
- formatTest( to!( const T)(1i), "1i" );
- formatTest( to!(immutable T)(1i), "1i" );
- }
-}
-
-@system unittest
-{
- formatTest( 2.25i, "2.25i" );
-
- class C1 { idouble val; alias val this; this(idouble v){ val = v; } }
- class C2 { idouble val; alias val this; this(idouble v){ val = v; }
- override string toString() const { return "C"; } }
- formatTest( new C1(2.25i), "2.25i" );
- formatTest( new C2(2.25i), "C" );
-
- struct S1 { idouble val; alias val this; }
- struct S2 { idouble val; alias val this;
- string toString() const { return "S"; } }
- formatTest( S1(2.25i), "2.25i" );
- formatTest( S2(2.25i), "S" );
-}
-
-/**
-Individual characters ($(D char), $(D wchar), or $(D dchar)) are formatted as
-Unicode characters with %s and as integers with integral-specific format
-specs.
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(CharTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
-{
- CharTypeOf!T val = obj;
-
- if (f.spec == 's' || f.spec == 'c')
- {
- put(w, val);
- }
- else
- {
- alias U = AliasSeq!(ubyte, ushort, uint)[CharTypeOf!T.sizeof/2];
- formatValue(w, cast(U) val, f);
- }
-}
-
-///
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%c");
- formatValue(w, 'a', spec);
-
- assert(w.data == "a");
-}
-
-@safe pure unittest
-{
- assertCTFEable!(
- {
- formatTest( 'c', "c" );
- });
-}
-
-@system unittest
-{
- class C1 { char val; alias val this; this(char v){ val = v; } }
- class C2 { char val; alias val this; this(char v){ val = v; }
- override string toString() const { return "C"; } }
- formatTest( new C1('c'), "c" );
- formatTest( new C2('c'), "C" );
-
- struct S1 { char val; alias val this; }
- struct S2 { char val; alias val this;
- string toString() const { return "S"; } }
- formatTest( S1('c'), "c" );
- formatTest( S2('c'), "S" );
-}
-
-@safe unittest
-{
- //Little Endian
- formatTest( "%-r", cast( char)'c', ['c' ] );
- formatTest( "%-r", cast(wchar)'c', ['c', 0 ] );
- formatTest( "%-r", cast(dchar)'c', ['c', 0, 0, 0] );
- formatTest( "%-r", '本', ['\x2c', '\x67'] );
-
- //Big Endian
- formatTest( "%+r", cast( char)'c', [ 'c'] );
- formatTest( "%+r", cast(wchar)'c', [0, 'c'] );
- formatTest( "%+r", cast(dchar)'c', [0, 0, 0, 'c'] );
- formatTest( "%+r", '本', ['\x67', '\x2c'] );
-}
-
-/**
-Strings are formatted like $(D printf) does.
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(StringTypeOf!T) && !is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
-{
- Unqual!(StringTypeOf!T) val = obj; // for `alias this`, see bug5371
- formatRange(w, val, f);
-}
-
-///
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%s");
- formatValue(w, "hello", spec);
-
- assert(w.data == "hello");
-}
-
-@safe unittest
-{
- formatTest( "abc", "abc" );
-}
-
-@system unittest
-{
- // Test for bug 5371 for classes
- class C1 { const string var; alias var this; this(string s){ var = s; } }
- class C2 { string var; alias var this; this(string s){ var = s; } }
- formatTest( new C1("c1"), "c1" );
- formatTest( new C2("c2"), "c2" );
-
- // Test for bug 5371 for structs
- struct S1 { const string var; alias var this; }
- struct S2 { string var; alias var this; }
- formatTest( S1("s1"), "s1" );
- formatTest( S2("s2"), "s2" );
-}
-
-@system unittest
-{
- class C3 { string val; alias val this; this(string s){ val = s; }
- override string toString() const { return "C"; } }
- formatTest( new C3("c3"), "C" );
-
- struct S3 { string val; alias val this;
- string toString() const { return "S"; } }
- formatTest( S3("s3"), "S" );
-}
-
-@safe pure unittest
-{
- //Little Endian
- formatTest( "%-r", "ab"c, ['a' , 'b' ] );
- formatTest( "%-r", "ab"w, ['a', 0 , 'b', 0 ] );
- formatTest( "%-r", "ab"d, ['a', 0, 0, 0, 'b', 0, 0, 0] );
- formatTest( "%-r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', '\xe8', '\xaa', '\x9e'] );
- formatTest( "%-r", "日本語"w, ['\xe5', '\x65', '\x2c', '\x67', '\x9e', '\x8a']);
- formatTest( "%-r", "日本語"d, ['\xe5', '\x65', '\x00', '\x00', '\x2c', '\x67',
- '\x00', '\x00', '\x9e', '\x8a', '\x00', '\x00'] );
-
- //Big Endian
- formatTest( "%+r", "ab"c, [ 'a', 'b'] );
- formatTest( "%+r", "ab"w, [ 0, 'a', 0, 'b'] );
- formatTest( "%+r", "ab"d, [0, 0, 0, 'a', 0, 0, 0, 'b'] );
- formatTest( "%+r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac', '\xe8', '\xaa', '\x9e'] );
- formatTest( "%+r", "日本語"w, ['\x65', '\xe5', '\x67', '\x2c', '\x8a', '\x9e'] );
- formatTest( "%+r", "日本語"d, ['\x00', '\x00', '\x65', '\xe5', '\x00', '\x00',
- '\x67', '\x2c', '\x00', '\x00', '\x8a', '\x9e'] );
-}
-
-/**
-Static-size arrays are formatted as dynamic arrays.
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T obj, const ref FormatSpec!Char f)
-if (is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
-{
- formatValue(w, obj[], f);
-}
-
-///
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%s");
- char[2] two = ['a', 'b'];
- formatValue(w, two, spec);
-
- assert(w.data == "ab");
-}
-
-@safe unittest // Test for issue 8310
-{
- import std.array : appender;
- FormatSpec!char f;
- auto w = appender!string();
-
- char[2] two = ['a', 'b'];
- formatValue(w, two, f);
-
- char[2] getTwo(){ return two; }
- formatValue(w, getTwo(), f);
-}
-
-/**
-Dynamic arrays are formatted as input ranges.
-
-Specializations:
- $(UL $(LI $(D void[]) is formatted like $(D ubyte[]).)
- $(LI Const array is converted to input range by removing its qualifier.))
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
-{
- static if (is(const(ArrayTypeOf!T) == const(void[])))
- {
- formatValue(w, cast(const ubyte[]) obj, f);
- }
- else static if (!isInputRange!T)
- {
- alias U = Unqual!(ArrayTypeOf!T);
- static assert(isInputRange!U);
- U val = obj;
- formatValue(w, val, f);
- }
- else
- {
- formatRange(w, obj, f);
- }
-}
-
-///
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%s");
- auto two = [1, 2];
- formatValue(w, two, spec);
-
- assert(w.data == "[1, 2]");
-}
-
-// alias this, input range I/F, and toString()
-@system unittest
-{
- struct S(int flags)
- {
- int[] arr;
- static if (flags & 1)
- alias arr this;
-
- static if (flags & 2)
- {
- @property bool empty() const { return arr.length == 0; }
- @property int front() const { return arr[0] * 2; }
- void popFront() { arr = arr[1..$]; }
- }
-
- static if (flags & 4)
- string toString() const { return "S"; }
- }
- formatTest(S!0b000([0, 1, 2]), "S!0([0, 1, 2])");
- formatTest(S!0b001([0, 1, 2]), "[0, 1, 2]"); // Test for bug 7628
- formatTest(S!0b010([0, 1, 2]), "[0, 2, 4]");
- formatTest(S!0b011([0, 1, 2]), "[0, 2, 4]");
- formatTest(S!0b100([0, 1, 2]), "S");
- formatTest(S!0b101([0, 1, 2]), "S"); // Test for bug 7628
- formatTest(S!0b110([0, 1, 2]), "S");
- formatTest(S!0b111([0, 1, 2]), "S");
-
- class C(uint flags)
- {
- int[] arr;
- static if (flags & 1)
- alias arr this;
-
- this(int[] a) { arr = a; }
-
- static if (flags & 2)
- {
- @property bool empty() const { return arr.length == 0; }
- @property int front() const { return arr[0] * 2; }
- void popFront() { arr = arr[1..$]; }
- }
-
- static if (flags & 4)
- override string toString() const { return "C"; }
- }
- formatTest(new C!0b000([0, 1, 2]), (new C!0b000([])).toString());
- formatTest(new C!0b001([0, 1, 2]), "[0, 1, 2]"); // Test for bug 7628
- formatTest(new C!0b010([0, 1, 2]), "[0, 2, 4]");
- formatTest(new C!0b011([0, 1, 2]), "[0, 2, 4]");
- formatTest(new C!0b100([0, 1, 2]), "C");
- formatTest(new C!0b101([0, 1, 2]), "C"); // Test for bug 7628
- formatTest(new C!0b110([0, 1, 2]), "C");
- formatTest(new C!0b111([0, 1, 2]), "C");
-}
-
-@system unittest
-{
- // void[]
- void[] val0;
- formatTest( val0, "[]" );
-
- void[] val = cast(void[]) cast(ubyte[])[1, 2, 3];
- formatTest( val, "[1, 2, 3]" );
-
- void[0] sval0 = [];
- formatTest( sval0, "[]");
-
- void[3] sval = cast(void[3]) cast(ubyte[3])[1, 2, 3];
- formatTest( sval, "[1, 2, 3]" );
-}
-
-@safe unittest
-{
- // const(T[]) -> const(T)[]
- const short[] a = [1, 2, 3];
- formatTest( a, "[1, 2, 3]" );
-
- struct S { const(int[]) arr; alias arr this; }
- auto s = S([1,2,3]);
- formatTest( s, "[1, 2, 3]" );
-}
-
-@safe unittest
-{
- // 6640
- struct Range
- {
- @safe:
- string value;
- @property bool empty() const { return !value.length; }
- @property dchar front() const { return value.front; }
- void popFront() { value.popFront(); }
-
- @property size_t length() const { return value.length; }
- }
- immutable table =
- [
- ["[%s]", "[string]"],
- ["[%10s]", "[ string]"],
- ["[%-10s]", "[string ]"],
- ["[%(%02x %)]", "[73 74 72 69 6e 67]"],
- ["[%(%c %)]", "[s t r i n g]"],
- ];
- foreach (e; table)
- {
- formatTest(e[0], "string", e[1]);
- formatTest(e[0], Range("string"), e[1]);
- }
-}
-
-@system unittest
-{
- // string literal from valid UTF sequence is encoding free.
- foreach (StrType; AliasSeq!(string, wstring, dstring))
- {
- // Valid and printable (ASCII)
- formatTest( [cast(StrType)"hello"],
- `["hello"]` );
-
- // 1 character escape sequences (' is not escaped in strings)
- formatTest( [cast(StrType)"\"'\0\\\a\b\f\n\r\t\v"],
- `["\"'\0\\\a\b\f\n\r\t\v"]` );
-
- // 1 character optional escape sequences
- formatTest( [cast(StrType)"\'\?"],
- `["'?"]` );
-
- // Valid and non-printable code point (<= U+FF)
- formatTest( [cast(StrType)"\x10\x1F\x20test"],
- `["\x10\x1F test"]` );
-
- // Valid and non-printable code point (<= U+FFFF)
- formatTest( [cast(StrType)"\u200B..\u200F"],
- `["\u200B..\u200F"]` );
-
- // Valid and non-printable code point (<= U+10FFFF)
- formatTest( [cast(StrType)"\U000E0020..\U000E007F"],
- `["\U000E0020..\U000E007F"]` );
- }
-
- // invalid UTF sequence needs hex-string literal postfix (c/w/d)
- {
- // U+FFFF with UTF-8 (Invalid code point for interchange)
- formatTest( [cast(string)[0xEF, 0xBF, 0xBF]],
- `[x"EF BF BF"c]` );
-
- // U+FFFF with UTF-16 (Invalid code point for interchange)
- formatTest( [cast(wstring)[0xFFFF]],
- `[x"FFFF"w]` );
-
- // U+FFFF with UTF-32 (Invalid code point for interchange)
- formatTest( [cast(dstring)[0xFFFF]],
- `[x"FFFF"d]` );
- }
-}
-
-@safe unittest
-{
- // nested range formatting with array of string
- formatTest( "%({%(%02x %)}%| %)", ["test", "msg"],
- `{74 65 73 74} {6d 73 67}` );
-}
-
-@safe unittest
-{
- // stop auto escaping inside range formatting
- auto arr = ["hello", "world"];
- formatTest( "%(%s, %)", arr, `"hello", "world"` );
- formatTest( "%-(%s, %)", arr, `hello, world` );
-
- auto aa1 = [1:"hello", 2:"world"];
- formatTest( "%(%s:%s, %)", aa1, [`1:"hello", 2:"world"`, `2:"world", 1:"hello"`] );
- formatTest( "%-(%s:%s, %)", aa1, [`1:hello, 2:world`, `2:world, 1:hello`] );
-
- auto aa2 = [1:["ab", "cd"], 2:["ef", "gh"]];
- formatTest( "%-(%s:%s, %)", aa2, [`1:["ab", "cd"], 2:["ef", "gh"]`, `2:["ef", "gh"], 1:["ab", "cd"]`] );
- formatTest( "%-(%s:%(%s%), %)", aa2, [`1:"ab""cd", 2:"ef""gh"`, `2:"ef""gh", 1:"ab""cd"`] );
- formatTest( "%-(%s:%-(%s%)%|, %)", aa2, [`1:abcd, 2:efgh`, `2:efgh, 1:abcd`] );
-}
-
-// input range formatting
-private void formatRange(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f)
-if (isInputRange!T)
-{
- import std.conv : text;
-
- // Formatting character ranges like string
- if (f.spec == 's')
- {
- alias E = ElementType!T;
-
- static if (!is(E == enum) && is(CharTypeOf!E))
- {
- static if (is(StringTypeOf!T))
- {
- auto s = val[0 .. f.precision < $ ? f.precision : $];
- if (!f.flDash)
- {
- // right align
- if (f.width > s.length)
- foreach (i ; 0 .. f.width - s.length) put(w, ' ');
- put(w, s);
- }
- else
- {
- // left align
- put(w, s);
- if (f.width > s.length)
- foreach (i ; 0 .. f.width - s.length) put(w, ' ');
- }
- }
- else
- {
- if (!f.flDash)
- {
- static if (hasLength!T)
- {
- // right align
- auto len = val.length;
- }
- else static if (isForwardRange!T && !isInfinite!T)
- {
- auto len = walkLength(val.save);
- }
- else
- {
- enforce(f.width == 0, "Cannot right-align a range without length");
- size_t len = 0;
- }
- if (f.precision != f.UNSPECIFIED && len > f.precision)
- len = f.precision;
-
- if (f.width > len)
- foreach (i ; 0 .. f.width - len)
- put(w, ' ');
- if (f.precision == f.UNSPECIFIED)
- put(w, val);
- else
- {
- size_t printed = 0;
- for (; !val.empty && printed < f.precision; val.popFront(), ++printed)
- put(w, val.front);
- }
- }
- else
- {
- size_t printed = void;
-
- // left align
- if (f.precision == f.UNSPECIFIED)
- {
- static if (hasLength!T)
- {
- printed = val.length;
- put(w, val);
- }
- else
- {
- printed = 0;
- for (; !val.empty; val.popFront(), ++printed)
- put(w, val.front);
- }
- }
- else
- {
- printed = 0;
- for (; !val.empty && printed < f.precision; val.popFront(), ++printed)
- put(w, val.front);
- }
-
- if (f.width > printed)
- foreach (i ; 0 .. f.width - printed)
- put(w, ' ');
- }
- }
- }
- else
- {
- put(w, f.seqBefore);
- if (!val.empty)
- {
- formatElement(w, val.front, f);
- val.popFront();
- for (size_t i; !val.empty; val.popFront(), ++i)
- {
- put(w, f.seqSeparator);
- formatElement(w, val.front, f);
- }
- }
- static if (!isInfinite!T) put(w, f.seqAfter);
- }
- }
- else if (f.spec == 'r')
- {
- static if (is(DynamicArrayTypeOf!T))
- {
- alias ARR = DynamicArrayTypeOf!T;
- foreach (e ; cast(ARR) val)
- {
- formatValue(w, e, f);
- }
- }
- else
- {
- for (size_t i; !val.empty; val.popFront(), ++i)
- {
- formatValue(w, val.front, f);
- }
- }
- }
- else if (f.spec == '(')
- {
- if (val.empty)
- return;
- // Nested specifier is to be used
- for (;;)
- {
- auto fmt = FormatSpec!Char(f.nested);
- fmt.writeUpToNextSpec(w);
- if (f.flDash)
- formatValue(w, val.front, fmt);
- else
- formatElement(w, val.front, fmt);
- if (f.sep !is null)
- {
- put(w, fmt.trailing);
- val.popFront();
- if (val.empty)
- break;
- put(w, f.sep);
- }
- else
- {
- val.popFront();
- if (val.empty)
- break;
- put(w, fmt.trailing);
- }
- }
- }
- else
- throw new Exception(text("Incorrect format specifier for range: %", f.spec));
-}
-
-@safe pure unittest
-{
- assert(collectExceptionMsg(format("%d", "hi")).back == 'd');
-}
-
-// character formatting with ecaping
-private void formatChar(Writer)(ref Writer w, in dchar c, in char quote)
-{
- import std.uni : isGraphical;
-
- string fmt;
- if (isGraphical(c))
- {
- if (c == quote || c == '\\')
- put(w, '\\');
- put(w, c);
- return;
- }
- else if (c <= 0xFF)
- {
- if (c < 0x20)
- {
- foreach (i, k; "\n\r\t\a\b\f\v\0")
- {
- if (c == k)
- {
- put(w, '\\');
- put(w, "nrtabfv0"[i]);
- return;
- }
- }
- }
- fmt = "\\x%02X";
- }
- else if (c <= 0xFFFF)
- fmt = "\\u%04X";
- else
- fmt = "\\U%08X";
-
- formattedWrite(w, fmt, cast(uint) c);
-}
-
-// undocumented because of deprecation
-// string elements are formatted like UTF-8 string literals.
-void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
-if (is(StringTypeOf!T) && !is(T == enum))
-{
- import std.array : appender;
- import std.utf : UTFException;
-
- StringTypeOf!T str = val; // bug 8015
-
- if (f.spec == 's')
- {
- try
- {
- // ignore other specifications and quote
- auto app = appender!(typeof(val[0])[])();
- put(app, '\"');
- for (size_t i = 0; i < str.length; )
- {
- import std.utf : decode;
-
- auto c = decode(str, i);
- // \uFFFE and \uFFFF are considered valid by isValidDchar,
- // so need checking for interchange.
- if (c == 0xFFFE || c == 0xFFFF)
- goto LinvalidSeq;
- formatChar(app, c, '"');
- }
- put(app, '\"');
- put(w, app.data);
- return;
- }
- catch (UTFException)
- {
- }
-
- // If val contains invalid UTF sequence, formatted like HexString literal
- LinvalidSeq:
- static if (is(typeof(str[0]) : const(char)))
- {
- enum postfix = 'c';
- alias IntArr = const(ubyte)[];
- }
- else static if (is(typeof(str[0]) : const(wchar)))
- {
- enum postfix = 'w';
- alias IntArr = const(ushort)[];
- }
- else static if (is(typeof(str[0]) : const(dchar)))
- {
- enum postfix = 'd';
- alias IntArr = const(uint)[];
- }
- formattedWrite(w, "x\"%(%02X %)\"%s", cast(IntArr) str, postfix);
- }
- else
- formatValue(w, str, f);
-}
-
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%s");
- formatElement(w, "Hello World", spec);
-
- assert(w.data == "\"Hello World\"");
-}
-
-@safe unittest
-{
- // Test for bug 8015
- import std.typecons;
-
- struct MyStruct {
- string str;
- @property string toStr() {
- return str;
- }
- alias toStr this;
- }
-
- Tuple!(MyStruct) t;
-}
-
-// undocumented because of deprecation
-// Character elements are formatted like UTF-8 character literals.
-void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
-if (is(CharTypeOf!T) && !is(T == enum))
-{
- if (f.spec == 's')
- {
- put(w, '\'');
- formatChar(w, val, '\'');
- put(w, '\'');
- }
- else
- formatValue(w, val, f);
-}
-
-///
-@safe unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%s");
- formatElement(w, "H", spec);
-
- assert(w.data == "\"H\"", w.data);
-}
-
-// undocumented
-// Maybe T is noncopyable struct, so receive it by 'auto ref'.
-void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, const ref FormatSpec!Char f)
-if (!is(StringTypeOf!T) && !is(CharTypeOf!T) || is(T == enum))
-{
- formatValue(w, val, f);
-}
-
-/**
- Associative arrays are formatted by using $(D ':') and $(D ", ") as
- separators, and enclosed by $(D '[') and $(D ']').
-
-Params:
- w = The $(D OutputRange) to write to.
- obj = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
-if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
-{
- AssocArrayTypeOf!T val = obj;
-
- enforceFmt(f.spec == 's' || f.spec == '(',
- "incompatible format character for associative array argument: %" ~ f.spec);
-
- enum const(Char)[] defSpec = "%s" ~ f.keySeparator ~ "%s" ~ f.seqSeparator;
- auto fmtSpec = f.spec == '(' ? f.nested : defSpec;
-
- size_t i = 0;
- immutable end = val.length;
-
- if (f.spec == 's')
- put(w, f.seqBefore);
- foreach (k, ref v; val)
- {
- auto fmt = FormatSpec!Char(fmtSpec);
- fmt.writeUpToNextSpec(w);
- if (f.flDash)
- {
- formatValue(w, k, fmt);
- fmt.writeUpToNextSpec(w);
- formatValue(w, v, fmt);
- }
- else
- {
- formatElement(w, k, fmt);
- fmt.writeUpToNextSpec(w);
- formatElement(w, v, fmt);
- }
- if (f.sep !is null)
- {
- fmt.writeUpToNextSpec(w);
- if (++i != end)
- put(w, f.sep);
- }
- else
- {
- if (++i != end)
- fmt.writeUpToNextSpec(w);
- }
- }
- if (f.spec == 's')
- put(w, f.seqAfter);
-}
-
-///
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%s");
- auto aa = ["H":"W"];
- formatElement(w, aa, spec);
-
- assert(w.data == "[\"H\":\"W\"]", w.data);
-}
-
-@safe unittest
-{
- assert(collectExceptionMsg!FormatException(format("%d", [0:1])).back == 'd');
-
- int[string] aa0;
- formatTest( aa0, `[]` );
-
- // elements escaping
- formatTest( ["aaa":1, "bbb":2],
- [`["aaa":1, "bbb":2]`, `["bbb":2, "aaa":1]`] );
- formatTest( ['c':"str"],
- `['c':"str"]` );
- formatTest( ['"':"\"", '\'':"'"],
- [`['"':"\"", '\'':"'"]`, `['\'':"'", '"':"\""]`] );
-
- // range formatting for AA
- auto aa3 = [1:"hello", 2:"world"];
- // escape
- formatTest( "{%(%s:%s $ %)}", aa3,
- [`{1:"hello" $ 2:"world"}`, `{2:"world" $ 1:"hello"}`]);
- // use range formatting for key and value, and use %|
- formatTest( "{%([%04d->%(%c.%)]%| $ %)}", aa3,
- [`{[0001->h.e.l.l.o] $ [0002->w.o.r.l.d]}`, `{[0002->w.o.r.l.d] $ [0001->h.e.l.l.o]}`] );
-
- // issue 12135
- formatTest("%(%s:<%s>%|,%)", [1:2], "1:<2>");
- formatTest("%(%s:<%s>%|%)" , [1:2], "1:<2>");
-}
-
-@system unittest
-{
- class C1 { int[char] val; alias val this; this(int[char] v){ val = v; } }
- class C2 { int[char] val; alias val this; this(int[char] v){ val = v; }
- override string toString() const { return "C"; } }
- formatTest( new C1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`] );
- formatTest( new C2(['c':1, 'd':2]), "C" );
-
- struct S1 { int[char] val; alias val this; }
- struct S2 { int[char] val; alias val this;
- string toString() const { return "S"; } }
- formatTest( S1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`] );
- formatTest( S2(['c':1, 'd':2]), "S" );
-}
-
-@safe unittest // Issue 8921
-{
- enum E : char { A = 'a', B = 'b', C = 'c' }
- E[3] e = [E.A, E.B, E.C];
- formatTest(e, "[A, B, C]");
-
- E[] e2 = [E.A, E.B, E.C];
- formatTest(e2, "[A, B, C]");
-}
-
-template hasToString(T, Char)
-{
- static if (isPointer!T && !isAggregateType!T)
- {
- // X* does not have toString, even if X is aggregate type has toString.
- enum hasToString = 0;
- }
- else static if (is(typeof({ T val = void; FormatSpec!Char f; val.toString((const(char)[] s){}, f); })))
- {
- enum hasToString = 4;
- }
- else static if (is(typeof({ T val = void; val.toString((const(char)[] s){}, "%s"); })))
- {
- enum hasToString = 3;
- }
- else static if (is(typeof({ T val = void; val.toString((const(char)[] s){}); })))
- {
- enum hasToString = 2;
- }
- else static if (is(typeof({ T val = void; return val.toString(); }()) S) && isSomeString!S)
- {
- enum hasToString = 1;
- }
- else
- {
- enum hasToString = 0;
- }
-}
-
-// object formatting with toString
-private void formatObject(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f)
-if (hasToString!(T, Char))
-{
- static if (is(typeof(val.toString((const(char)[] s){}, f))))
- {
- val.toString((const(char)[] s) { put(w, s); }, f);
- }
- else static if (is(typeof(val.toString((const(char)[] s){}, "%s"))))
- {
- val.toString((const(char)[] s) { put(w, s); }, f.getCurFmtStr());
- }
- else static if (is(typeof(val.toString((const(char)[] s){}))))
- {
- val.toString((const(char)[] s) { put(w, s); });
- }
- else static if (is(typeof(val.toString()) S) && isSomeString!S)
- {
- put(w, val.toString());
- }
- else
- static assert(0);
-}
-
-void enforceValidFormatSpec(T, Char)(const ref FormatSpec!Char f)
-{
- static if (!isInputRange!T && hasToString!(T, Char) != 4)
- {
- enforceFmt(f.spec == 's',
- "Expected '%s' format specifier for type '" ~ T.stringof ~ "'");
- }
-}
-
-@system unittest
-{
- static interface IF1 { }
- class CIF1 : IF1 { }
- static struct SF1 { }
- static union UF1 { }
- static class CF1 { }
-
- static interface IF2 { string toString(); }
- static class CIF2 : IF2 { override string toString() { return ""; } }
- static struct SF2 { string toString() { return ""; } }
- static union UF2 { string toString() { return ""; } }
- static class CF2 { override string toString() { return ""; } }
-
- static interface IK1 { void toString(scope void delegate(const(char)[]) sink,
- FormatSpec!char) const; }
- static class CIK1 : IK1 { override void toString(scope void delegate(const(char)[]) sink,
- FormatSpec!char) const { sink("CIK1"); } }
- static struct KS1 { void toString(scope void delegate(const(char)[]) sink,
- FormatSpec!char) const { sink("KS1"); } }
-
- static union KU1 { void toString(scope void delegate(const(char)[]) sink,
- FormatSpec!char) const { sink("KU1"); } }
-
- static class KC1 { void toString(scope void delegate(const(char)[]) sink,
- FormatSpec!char) const { sink("KC1"); } }
-
- IF1 cif1 = new CIF1;
- assertThrown!FormatException(format("%f", cif1));
- assertThrown!FormatException(format("%f", SF1()));
- assertThrown!FormatException(format("%f", UF1()));
- assertThrown!FormatException(format("%f", new CF1()));
-
- IF2 cif2 = new CIF2;
- assertThrown!FormatException(format("%f", cif2));
- assertThrown!FormatException(format("%f", SF2()));
- assertThrown!FormatException(format("%f", UF2()));
- assertThrown!FormatException(format("%f", new CF2()));
-
- IK1 cik1 = new CIK1;
- assert(format("%f", cik1) == "CIK1");
- assert(format("%f", KS1()) == "KS1");
- assert(format("%f", KU1()) == "KU1");
- assert(format("%f", new KC1()) == "KC1");
-}
-
-/**
- Aggregates ($(D struct), $(D union), $(D class), and $(D interface)) are
- basically formatted by calling $(D toString).
- $(D toString) should have one of the following signatures:
-
----
-const void toString(scope void delegate(const(char)[]) sink, FormatSpec fmt);
-const void toString(scope void delegate(const(char)[]) sink, string fmt);
-const void toString(scope void delegate(const(char)[]) sink);
-const string toString();
----
-
- For the class objects which have input range interface,
- $(UL $(LI If the instance $(D toString) has overridden
- $(D Object.toString), it is used.)
- $(LI Otherwise, the objects are formatted as input range.))
-
- For the struct and union objects which does not have $(D toString),
- $(UL $(LI If they have range interface, formatted as input range.)
- $(LI Otherwise, they are formatted like $(D Type(field1, filed2, ...)).))
-
- Otherwise, are formatted just as their type name.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
-if (is(T == class) && !is(T == enum))
-{
- enforceValidFormatSpec!(T, Char)(f);
- // TODO: Change this once toString() works for shared objects.
- static assert(!is(T == shared), "unable to format shared objects");
-
- if (val is null)
- put(w, "null");
- else
- {
- static if (hasToString!(T, Char) > 1 || (!isInputRange!T && !is(BuiltinTypeOf!T)))
- {
- formatObject!(Writer, T, Char)(w, val, f);
- }
- else
- {
- //string delegate() dg = &val.toString;
- Object o = val; // workaround
- string delegate() dg = &o.toString;
- if (dg.funcptr != &Object.toString) // toString is overridden
- {
- formatObject(w, val, f);
- }
- else static if (isInputRange!T)
- {
- formatRange(w, val, f);
- }
- else static if (is(BuiltinTypeOf!T X))
- {
- X x = val;
- formatValue(w, x, f);
- }
- else
- {
- formatObject(w, val, f);
- }
- }
- }
-}
-
-/++
- $(D formatValue) allows to reuse existing format specifiers:
- +/
-@system unittest
-{
- import std.format;
-
- struct Point
- {
- int x, y;
-
- void toString(scope void delegate(const(char)[]) sink,
- FormatSpec!char fmt) const
- {
- sink("(");
- sink.formatValue(x, fmt);
- sink(",");
- sink.formatValue(y, fmt);
- sink(")");
- }
- }
-
- auto p = Point(16,11);
- assert(format("%03d", p) == "(016,011)");
- assert(format("%02x", p) == "(10,0b)");
-}
-
-/++
- The following code compares the use of $(D formatValue) and $(D formattedWrite).
- +/
-@safe pure unittest
-{
- import std.array : appender;
- import std.format;
-
- auto writer1 = appender!string();
- writer1.formattedWrite("%08b", 42);
-
- auto writer2 = appender!string();
- auto f = singleSpec("%08b");
- writer2.formatValue(42, f);
-
- assert(writer1.data == writer2.data && writer1.data == "00101010");
-}
-
-@system unittest
-{
- import std.array : appender;
- import std.range.interfaces;
- // class range (issue 5154)
- auto c = inputRangeObject([1,2,3,4]);
- formatTest( c, "[1, 2, 3, 4]" );
- assert(c.empty);
- c = null;
- formatTest( c, "null" );
-}
-
-@system unittest
-{
- // 5354
- // If the class has both range I/F and custom toString, the use of custom
- // toString routine is prioritized.
-
- // Enable the use of custom toString that gets a sink delegate
- // for class formatting.
-
- enum inputRangeCode =
- q{
- int[] arr;
- this(int[] a){ arr = a; }
- @property int front() const { return arr[0]; }
- @property bool empty() const { return arr.length == 0; }
- void popFront(){ arr = arr[1..$]; }
- };
-
- class C1
- {
- mixin(inputRangeCode);
- void toString(scope void delegate(const(char)[]) dg, const ref FormatSpec!char f) const { dg("[012]"); }
- }
- class C2
- {
- mixin(inputRangeCode);
- void toString(scope void delegate(const(char)[]) dg, string f) const { dg("[012]"); }
- }
- class C3
- {
- mixin(inputRangeCode);
- void toString(scope void delegate(const(char)[]) dg) const { dg("[012]"); }
- }
- class C4
- {
- mixin(inputRangeCode);
- override string toString() const { return "[012]"; }
- }
- class C5
- {
- mixin(inputRangeCode);
- }
-
- formatTest( new C1([0, 1, 2]), "[012]" );
- formatTest( new C2([0, 1, 2]), "[012]" );
- formatTest( new C3([0, 1, 2]), "[012]" );
- formatTest( new C4([0, 1, 2]), "[012]" );
- formatTest( new C5([0, 1, 2]), "[0, 1, 2]" );
-}
-
-/// ditto
-void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
-if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum))
-{
- enforceValidFormatSpec!(T, Char)(f);
- if (val is null)
- put(w, "null");
- else
- {
- static if (hasToString!(T, Char))
- {
- formatObject(w, val, f);
- }
- else static if (isInputRange!T)
- {
- formatRange(w, val, f);
- }
- else
- {
- version (Windows)
- {
- import core.sys.windows.com : IUnknown;
- static if (is(T : IUnknown))
- {
- formatValue(w, *cast(void**)&val, f);
- }
- else
- {
- formatValue(w, cast(Object) val, f);
- }
- }
- else
- {
- formatValue(w, cast(Object) val, f);
- }
- }
- }
-}
-
-@system unittest
-{
- // interface
- import std.range.interfaces;
- InputRange!int i = inputRangeObject([1,2,3,4]);
- formatTest( i, "[1, 2, 3, 4]" );
- assert(i.empty);
- i = null;
- formatTest( i, "null" );
-
- // interface (downcast to Object)
- interface Whatever {}
- class C : Whatever
- {
- override @property string toString() const { return "ab"; }
- }
- Whatever val = new C;
- formatTest( val, "ab" );
-
- // Issue 11175
- version (Windows)
- {
- import core.sys.windows.com : IUnknown, IID;
- import core.sys.windows.windows : HRESULT;
-
- interface IUnknown2 : IUnknown { }
-
- class D : IUnknown2
- {
- extern(Windows) HRESULT QueryInterface(const(IID)* riid, void** pvObject) { return typeof(return).init; }
- extern(Windows) uint AddRef() { return 0; }
- extern(Windows) uint Release() { return 0; }
- }
-
- IUnknown2 d = new D;
- string expected = format("%X", cast(void*) d);
- formatTest(d, expected);
- }
-}
-
-/// ditto
-// Maybe T is noncopyable struct, so receive it by 'auto ref'.
-void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T val, const ref FormatSpec!Char f)
-if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum))
-{
- enforceValidFormatSpec!(T, Char)(f);
- static if (hasToString!(T, Char))
- {
- formatObject(w, val, f);
- }
- else static if (isInputRange!T)
- {
- formatRange(w, val, f);
- }
- else static if (is(T == struct))
- {
- enum left = T.stringof~"(";
- enum separator = ", ";
- enum right = ")";
-
- put(w, left);
- foreach (i, e; val.tupleof)
- {
- static if (0 < i && val.tupleof[i-1].offsetof == val.tupleof[i].offsetof)
- {
- static if (i == val.tupleof.length - 1 || val.tupleof[i].offsetof != val.tupleof[i+1].offsetof)
- put(w, separator~val.tupleof[i].stringof[4..$]~"}");
- else
- put(w, separator~val.tupleof[i].stringof[4..$]);
- }
- else
- {
- static if (i+1 < val.tupleof.length && val.tupleof[i].offsetof == val.tupleof[i+1].offsetof)
- put(w, (i > 0 ? separator : "")~"#{overlap "~val.tupleof[i].stringof[4..$]);
- else
- {
- static if (i > 0)
- put(w, separator);
- formatElement(w, e, f);
- }
- }
- }
- put(w, right);
- }
- else
- {
- put(w, T.stringof);
- }
-}
-
-@safe unittest
-{
- // bug 4638
- struct U8 { string toString() const { return "blah"; } }
- struct U16 { wstring toString() const { return "blah"; } }
- struct U32 { dstring toString() const { return "blah"; } }
- formatTest( U8(), "blah" );
- formatTest( U16(), "blah" );
- formatTest( U32(), "blah" );
-}
-
-@safe unittest
-{
- // 3890
- struct Int{ int n; }
- struct Pair{ string s; Int i; }
- formatTest( Pair("hello", Int(5)),
- `Pair("hello", Int(5))` );
-}
-
-@system unittest
-{
- // union formatting without toString
- union U1
- {
- int n;
- string s;
- }
- U1 u1;
- formatTest( u1, "U1" );
-
- // union formatting with toString
- union U2
- {
- int n;
- string s;
- string toString() const { return s; }
- }
- U2 u2;
- u2.s = "hello";
- formatTest( u2, "hello" );
-}
-
-@system unittest
-{
- import std.array;
- // 7230
- static struct Bug7230
- {
- string s = "hello";
- union {
- string a;
- int b;
- double c;
- }
- long x = 10;
- }
-
- Bug7230 bug;
- bug.b = 123;
-
- FormatSpec!char f;
- auto w = appender!(char[])();
- formatValue(w, bug, f);
- assert(w.data == `Bug7230("hello", #{overlap a, b, c}, 10)`);
-}
-
-@safe unittest
-{
- import std.array;
- static struct S{ @disable this(this); }
- S s;
-
- FormatSpec!char f;
- auto w = appender!string();
- formatValue(w, s, f);
- assert(w.data == "S()");
-}
-
-/**
-$(D enum) is formatted like its base value.
-
-Params:
- w = The $(D OutputRange) to write to.
- val = The value to write.
- f = The $(D FormatSpec) defining how to write the value.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
-if (is(T == enum))
-{
- if (f.spec == 's')
- {
- foreach (i, e; EnumMembers!T)
- {
- if (val == e)
- {
- formatValue(w, __traits(allMembers, T)[i], f);
- return;
- }
- }
-
- // val is not a member of T, output cast(T) rawValue instead.
- put(w, "cast(" ~ T.stringof ~ ")");
- static assert(!is(OriginalType!T == T));
- }
- formatValue(w, cast(OriginalType!T) val, f);
-}
-
-///
-@safe pure unittest
-{
- import std.array : appender;
- auto w = appender!string();
- auto spec = singleSpec("%s");
-
- enum A { first, second, third }
-
- formatElement(w, A.second, spec);
-
- assert(w.data == "second");
-}
-
-@safe unittest
-{
- enum A { first, second, third }
- formatTest( A.second, "second" );
- formatTest( cast(A) 72, "cast(A)72" );
-}
-@safe unittest
-{
- enum A : string { one = "uno", two = "dos", three = "tres" }
- formatTest( A.three, "three" );
- formatTest( cast(A)"mill\&oacute;n", "cast(A)mill\&oacute;n" );
-}
-@safe unittest
-{
- enum A : bool { no, yes }
- formatTest( A.yes, "yes" );
- formatTest( A.no, "no" );
-}
-@safe unittest
-{
- // Test for bug 6892
- enum Foo { A = 10 }
- formatTest("%s", Foo.A, "A");
- formatTest(">%4s<", Foo.A, "> A<");
- formatTest("%04d", Foo.A, "0010");
- formatTest("%+2u", Foo.A, "+10");
- formatTest("%02x", Foo.A, "0a");
- formatTest("%3o", Foo.A, " 12");
- formatTest("%b", Foo.A, "1010");
-}
-
-/**
- Pointers are formatted as hex integers.
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
-if (isPointer!T && !is(T == enum) && !hasToString!(T, Char))
-{
- static if (isInputRange!T)
- {
- if (val !is null)
- {
- formatRange(w, *val, f);
- return;
- }
- }
-
- static if (is(typeof({ shared const void* p = val; })))
- alias SharedOf(T) = shared(T);
- else
- alias SharedOf(T) = T;
-
- const SharedOf!(void*) p = val;
- const pnum = ()@trusted{ return cast(ulong) p; }();
-
- if (f.spec == 's')
- {
- if (p is null)
- {
- put(w, "null");
- return;
- }
- FormatSpec!Char fs = f; // fs is copy for change its values.
- fs.spec = 'X';
- formatValue(w, pnum, fs);
- }
- else
- {
- enforceFmt(f.spec == 'X' || f.spec == 'x',
- "Expected one of %s, %x or %X for pointer type.");
- formatValue(w, pnum, f);
- }
-}
-
-@safe pure unittest
-{
- // pointer
- import std.range;
- auto r = retro([1,2,3,4]);
- auto p = ()@trusted{ auto p = &r; return p; }();
- formatTest( p, "[4, 3, 2, 1]" );
- assert(p.empty);
- p = null;
- formatTest( p, "null" );
-
- auto q = ()@trusted{ return cast(void*) 0xFFEECCAA; }();
- formatTest( q, "FFEECCAA" );
-}
-
-@system pure unittest
-{
- // Test for issue 7869
- struct S
- {
- string toString() const { return ""; }
- }
- S* p = null;
- formatTest( p, "null" );
-
- S* q = cast(S*) 0xFFEECCAA;
- formatTest( q, "FFEECCAA" );
-}
-
-@system unittest
-{
- // Test for issue 8186
- class B
- {
- int*a;
- this(){ a = new int; }
- alias a this;
- }
- formatTest( B.init, "null" );
-}
-
-@system pure unittest
-{
- // Test for issue 9336
- shared int i;
- format("%s", &i);
-}
-
-@system pure unittest
-{
- // Test for issue 11778
- int* p = null;
- assertThrown(format("%d", p));
- assertThrown(format("%04d", p + 2));
-}
-
-@safe pure unittest
-{
- // Test for issue 12505
- void* p = null;
- formatTest( "%08X", p, "00000000" );
-}
-
-/**
- Delegates are formatted by 'ReturnType delegate(Parameters) FunctionAttributes'
- */
-void formatValue(Writer, T, Char)(auto ref Writer w, scope T, const ref FormatSpec!Char f)
-if (isDelegate!T)
-{
- formatValue(w, T.stringof, f);
-}
-
-///
-@safe pure unittest
-{
- import std.conv : to;
-
- int i;
-
- int foo(short k) @nogc
- {
- return i + k;
- }
-
- @system int delegate(short) @nogc bar() nothrow pure
- {
- int* p = new int;
- return &foo;
- }
-
- assert(to!string(&bar) == "int delegate(short) @nogc delegate() pure nothrow @system");
-}
-
-@safe unittest
-{
- void func() @system { __gshared int x; ++x; throw new Exception("msg"); }
- version (linux) formatTest( &func, "void delegate() @system" );
-}
-
-@safe pure unittest
-{
- int[] a = [ 1, 3, 2 ];
- formatTest( "testing %(%s & %) embedded", a,
- "testing 1 & 3 & 2 embedded");
- formatTest( "testing %((%s) %)) wyda3", a,
- "testing (1) (3) (2) wyda3" );
-
- int[0] empt = [];
- formatTest( "(%s)", empt,
- "([])" );
-}
-
-//------------------------------------------------------------------------------
-// Fix for issue 1591
-private int getNthInt(string kind, A...)(uint index, A args)
-{
- return getNth!(kind, isIntegral,int)(index, args);
-}
-
-private T getNth(string kind, alias Condition, T, A...)(uint index, A args)
-{
- import std.conv : text, to;
-
- switch (index)
- {
- foreach (n, _; A)
- {
- case n:
- static if (Condition!(typeof(args[n])))
- {
- return to!T(args[n]);
- }
- else
- {
- throw new FormatException(
- text(kind, " expected, not ", typeof(args[n]).stringof,
- " for argument #", index + 1));
- }
- }
- default:
- throw new FormatException(
- text("Missing ", kind, " argument"));
- }
-}
-
-@safe unittest
-{
- // width/precision
- assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2))
- == "integer width expected, not double for argument #1");
- assert(collectExceptionMsg!FormatException(format("%-1*.d", 5.1, 2))
- == "integer width expected, not double for argument #1");
-
- assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2))
- == "integer precision expected, not char for argument #1");
- assert(collectExceptionMsg!FormatException(format("%-1.*d", 4.7, 3))
- == "integer precision expected, not double for argument #1");
- assert(collectExceptionMsg!FormatException(format("%.*d", 5))
- == "Orphan format specifier: %d");
- assert(collectExceptionMsg!FormatException(format("%*.*d", 5))
- == "Missing integer precision argument");
-
- // separatorCharPos
- assert(collectExceptionMsg!FormatException(format("%,?d", 5))
- == "separator character expected, not int for argument #1");
- assert(collectExceptionMsg!FormatException(format("%,?d", '?'))
- == "Orphan format specifier: %d");
- assert(collectExceptionMsg!FormatException(format("%.*,*?d", 5))
- == "Missing separator digit width argument");
-}
-
-/* ======================== Unit Tests ====================================== */
-
-version (unittest)
-void formatTest(T)(T val, string expected, size_t ln = __LINE__, string fn = __FILE__)
-{
- import core.exception : AssertError;
- import std.array : appender;
- import std.conv : text;
- FormatSpec!char f;
- auto w = appender!string();
- formatValue(w, val, f);
- enforce!AssertError(
- w.data == expected,
- text("expected = `", expected, "`, result = `", w.data, "`"), fn, ln);
-}
-
-version (unittest)
-void formatTest(T)(string fmt, T val, string expected, size_t ln = __LINE__, string fn = __FILE__) @safe
-{
- import core.exception : AssertError;
- import std.array : appender;
- import std.conv : text;
- auto w = appender!string();
- formattedWrite(w, fmt, val);
- enforce!AssertError(
- w.data == expected,
- text("expected = `", expected, "`, result = `", w.data, "`"), fn, ln);
-}
-
-version (unittest)
-void formatTest(T)(T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__)
-{
- import core.exception : AssertError;
- import std.array : appender;
- import std.conv : text;
- FormatSpec!char f;
- auto w = appender!string();
- formatValue(w, val, f);
- foreach (cur; expected)
- {
- if (w.data == cur) return;
- }
- enforce!AssertError(
- false,
- text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln);
-}
-
-version (unittest)
-void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) @safe
-{
- import core.exception : AssertError;
- import std.array : appender;
- import std.conv : text;
- auto w = appender!string();
- formattedWrite(w, fmt, val);
- foreach (cur; expected)
- {
- if (w.data == cur) return;
- }
- enforce!AssertError(
- false,
- text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln);
-}
-
-@safe /*pure*/ unittest // formatting floating point values is now impure
-{
- import std.array;
-
- auto stream = appender!string();
- formattedWrite(stream, "%s", 1.1);
- assert(stream.data == "1.1", stream.data);
-}
-
-@safe pure unittest
-{
- import std.algorithm;
- import std.array;
-
- auto stream = appender!string();
- formattedWrite(stream, "%s", map!"a*a"([2, 3, 5]));
- assert(stream.data == "[4, 9, 25]", stream.data);
-
- // Test shared data.
- stream = appender!string();
- shared int s = 6;
- formattedWrite(stream, "%s", s);
- assert(stream.data == "6");
-}
-
-@safe pure unittest
-{
- import std.array;
- auto stream = appender!string();
- formattedWrite(stream, "%u", 42);
- assert(stream.data == "42", stream.data);
-}
-
-@safe pure unittest
-{
- // testing raw writes
- import std.array;
- auto w = appender!(char[])();
- uint a = 0x02030405;
- formattedWrite(w, "%+r", a);
- assert(w.data.length == 4 && w.data[0] == 2 && w.data[1] == 3
- && w.data[2] == 4 && w.data[3] == 5);
- w.clear();
- formattedWrite(w, "%-r", a);
- assert(w.data.length == 4 && w.data[0] == 5 && w.data[1] == 4
- && w.data[2] == 3 && w.data[3] == 2);
-}
-
-@safe pure unittest
-{
- // testing positional parameters
- import std.array;
- auto w = appender!(char[])();
- formattedWrite(w,
- "Numbers %2$s and %1$s are reversed and %1$s%2$s repeated",
- 42, 0);
- assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated",
- w.data);
- assert(collectExceptionMsg!FormatException(formattedWrite(w, "%1$s, %3$s", 1, 2))
- == "Positional specifier %3$s index exceeds 2");
-
- w.clear();
- formattedWrite(w, "asd%s", 23);
- assert(w.data == "asd23", w.data);
- w.clear();
- formattedWrite(w, "%s%s", 23, 45);
- assert(w.data == "2345", w.data);
-}
-
-@safe unittest
-{
- import core.stdc.string : strlen;
- import std.array : appender;
- import std.conv : text, octal;
- import core.stdc.stdio : snprintf;
-
- debug(format) printf("std.format.format.unittest\n");
-
- auto stream = appender!(char[])();
- //goto here;
-
- formattedWrite(stream,
- "hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo");
- assert(stream.data == "hello world! true 57 ",
- stream.data);
-
- stream.clear();
- formattedWrite(stream, "%g %A %s", 1.67, -1.28, float.nan);
- // core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr);
-
- /* The host C library is used to format floats. C99 doesn't
- * specify what the hex digit before the decimal point is for
- * %A. */
-
- version (CRuntime_Glibc)
- {
- assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
- stream.data);
- }
- else version (OSX)
- {
- assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
- stream.data);
- }
- else version (MinGW)
- {
- assert(stream.data == "1.67 -0XA.3D70A3D70A3D8P-3 nan",
- stream.data);
- }
- else version (CRuntime_Microsoft)
- {
- assert(stream.data == "1.67 -0X1.47AE14P+0 nan"
- || stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", // MSVCRT 14+ (VS 2015)
- stream.data);
- }
- else
- {
- assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
- stream.data);
- }
- stream.clear();
-
- formattedWrite(stream, "%x %X", 0x1234AF, 0xAFAFAFAF);
- assert(stream.data == "1234af AFAFAFAF");
- stream.clear();
-
- formattedWrite(stream, "%b %o", 0x1234AF, 0xAFAFAFAF);
- assert(stream.data == "100100011010010101111 25753727657");
- stream.clear();
-
- formattedWrite(stream, "%d %s", 0x1234AF, 0xAFAFAFAF);
- assert(stream.data == "1193135 2947526575");
- stream.clear();
-
- // formattedWrite(stream, "%s", 1.2 + 3.4i);
- // assert(stream.data == "1.2+3.4i");
- // stream.clear();
-
- formattedWrite(stream, "%a %A", 1.32, 6.78f);
- //formattedWrite(stream, "%x %X", 1.32);
- version (CRuntime_Microsoft)
- assert(stream.data == "0x1.51eb85p+0 0X1.B1EB86P+2"
- || stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB860000000P+2"); // MSVCRT 14+ (VS 2015)
- else
- assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2");
- stream.clear();
-
- formattedWrite(stream, "%#06.*f",2,12.345);
- assert(stream.data == "012.35");
- stream.clear();
-
- formattedWrite(stream, "%#0*.*f",6,2,12.345);
- assert(stream.data == "012.35");
- stream.clear();
-
- const real constreal = 1;
- formattedWrite(stream, "%g",constreal);
- assert(stream.data == "1");
- stream.clear();
-
- formattedWrite(stream, "%7.4g:", 12.678);
- assert(stream.data == " 12.68:");
- stream.clear();
-
- formattedWrite(stream, "%7.4g:", 12.678L);
- assert(stream.data == " 12.68:");
- stream.clear();
-
- formattedWrite(stream, "%04f|%05d|%#05x|%#5x",-4.0,-10,1,1);
- assert(stream.data == "-4.000000|-0010|0x001| 0x1",
- stream.data);
- stream.clear();
-
- int i;
- string s;
-
- i = -10;
- formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(stream.data == "-10|-10|-10|-10|-10.0000");
- stream.clear();
-
- i = -5;
- formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(stream.data == "-5| -5|-05|-5|-5.0000");
- stream.clear();
-
- i = 0;
- formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(stream.data == "0| 0|000|0|0.0000");
- stream.clear();
-
- i = 5;
- formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(stream.data == "5| 5|005|5|5.0000");
- stream.clear();
-
- i = 10;
- formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(stream.data == "10| 10|010|10|10.0000");
- stream.clear();
-
- formattedWrite(stream, "%.0d", 0);
- assert(stream.data == "");
- stream.clear();
-
- formattedWrite(stream, "%.g", .34);
- assert(stream.data == "0.3");
- stream.clear();
-
- stream.clear(); formattedWrite(stream, "%.0g", .34);
- assert(stream.data == "0.3");
-
- stream.clear(); formattedWrite(stream, "%.2g", .34);
- assert(stream.data == "0.34");
-
- stream.clear(); formattedWrite(stream, "%0.0008f", 1e-08);
- assert(stream.data == "0.00000001");
-
- stream.clear(); formattedWrite(stream, "%0.0008f", 1e-05);
- assert(stream.data == "0.00001000");
-
- //return;
- //core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr);
-
- s = "helloworld";
- string r;
- stream.clear(); formattedWrite(stream, "%.2s", s[0 .. 5]);
- assert(stream.data == "he");
- stream.clear(); formattedWrite(stream, "%.20s", s[0 .. 5]);
- assert(stream.data == "hello");
- stream.clear(); formattedWrite(stream, "%8s", s[0 .. 5]);
- assert(stream.data == " hello");
-
- byte[] arrbyte = new byte[4];
- arrbyte[0] = 100;
- arrbyte[1] = -99;
- arrbyte[3] = 0;
- stream.clear(); formattedWrite(stream, "%s", arrbyte);
- assert(stream.data == "[100, -99, 0, 0]", stream.data);
-
- ubyte[] arrubyte = new ubyte[4];
- arrubyte[0] = 100;
- arrubyte[1] = 200;
- arrubyte[3] = 0;
- stream.clear(); formattedWrite(stream, "%s", arrubyte);
- assert(stream.data == "[100, 200, 0, 0]", stream.data);
-
- short[] arrshort = new short[4];
- arrshort[0] = 100;
- arrshort[1] = -999;
- arrshort[3] = 0;
- stream.clear(); formattedWrite(stream, "%s", arrshort);
- assert(stream.data == "[100, -999, 0, 0]");
- stream.clear(); formattedWrite(stream, "%s",arrshort);
- assert(stream.data == "[100, -999, 0, 0]");
-
- ushort[] arrushort = new ushort[4];
- arrushort[0] = 100;
- arrushort[1] = 20_000;
- arrushort[3] = 0;
- stream.clear(); formattedWrite(stream, "%s", arrushort);
- assert(stream.data == "[100, 20000, 0, 0]");
-
- int[] arrint = new int[4];
- arrint[0] = 100;
- arrint[1] = -999;
- arrint[3] = 0;
- stream.clear(); formattedWrite(stream, "%s", arrint);
- assert(stream.data == "[100, -999, 0, 0]");
- stream.clear(); formattedWrite(stream, "%s",arrint);
- assert(stream.data == "[100, -999, 0, 0]");
-
- long[] arrlong = new long[4];
- arrlong[0] = 100;
- arrlong[1] = -999;
- arrlong[3] = 0;
- stream.clear(); formattedWrite(stream, "%s", arrlong);
- assert(stream.data == "[100, -999, 0, 0]");
- stream.clear(); formattedWrite(stream, "%s",arrlong);
- assert(stream.data == "[100, -999, 0, 0]");
-
- ulong[] arrulong = new ulong[4];
- arrulong[0] = 100;
- arrulong[1] = 999;
- arrulong[3] = 0;
- stream.clear(); formattedWrite(stream, "%s", arrulong);
- assert(stream.data == "[100, 999, 0, 0]");
-
- string[] arr2 = new string[4];
- arr2[0] = "hello";
- arr2[1] = "world";
- arr2[3] = "foo";
- stream.clear(); formattedWrite(stream, "%s", arr2);
- assert(stream.data == `["hello", "world", "", "foo"]`, stream.data);
-
- stream.clear(); formattedWrite(stream, "%.8d", 7);
- assert(stream.data == "00000007");
-
- stream.clear(); formattedWrite(stream, "%.8x", 10);
- assert(stream.data == "0000000a");
-
- stream.clear(); formattedWrite(stream, "%-3d", 7);
- assert(stream.data == "7 ");
-
- stream.clear(); formattedWrite(stream, "%*d", -3, 7);
- assert(stream.data == "7 ");
-
- stream.clear(); formattedWrite(stream, "%.*d", -3, 7);
- //writeln(stream.data);
- assert(stream.data == "7");
-
- stream.clear(); formattedWrite(stream, "%s", "abc"c);
- assert(stream.data == "abc");
- stream.clear(); formattedWrite(stream, "%s", "def"w);
- assert(stream.data == "def", text(stream.data.length));
- stream.clear(); formattedWrite(stream, "%s", "ghi"d);
- assert(stream.data == "ghi");
-
-here:
- @trusted void* deadBeef() { return cast(void*) 0xDEADBEEF; }
- stream.clear(); formattedWrite(stream, "%s", deadBeef());
- assert(stream.data == "DEADBEEF", stream.data);
-
- stream.clear(); formattedWrite(stream, "%#x", 0xabcd);
- assert(stream.data == "0xabcd");
- stream.clear(); formattedWrite(stream, "%#X", 0xABCD);
- assert(stream.data == "0XABCD");
-
- stream.clear(); formattedWrite(stream, "%#o", octal!12345);
- assert(stream.data == "012345");
- stream.clear(); formattedWrite(stream, "%o", 9);
- assert(stream.data == "11");
-
- stream.clear(); formattedWrite(stream, "%+d", 123);
- assert(stream.data == "+123");
- stream.clear(); formattedWrite(stream, "%+d", -123);
- assert(stream.data == "-123");
- stream.clear(); formattedWrite(stream, "% d", 123);
- assert(stream.data == " 123");
- stream.clear(); formattedWrite(stream, "% d", -123);
- assert(stream.data == "-123");
-
- stream.clear(); formattedWrite(stream, "%%");
- assert(stream.data == "%");
-
- stream.clear(); formattedWrite(stream, "%d", true);
- assert(stream.data == "1");
- stream.clear(); formattedWrite(stream, "%d", false);
- assert(stream.data == "0");
-
- stream.clear(); formattedWrite(stream, "%d", 'a');
- assert(stream.data == "97", stream.data);
- wchar wc = 'a';
- stream.clear(); formattedWrite(stream, "%d", wc);
- assert(stream.data == "97");
- dchar dc = 'a';
- stream.clear(); formattedWrite(stream, "%d", dc);
- assert(stream.data == "97");
-
- byte b = byte.max;
- stream.clear(); formattedWrite(stream, "%x", b);
- assert(stream.data == "7f");
- stream.clear(); formattedWrite(stream, "%x", ++b);
- assert(stream.data == "80");
- stream.clear(); formattedWrite(stream, "%x", ++b);
- assert(stream.data == "81");
-
- short sh = short.max;
- stream.clear(); formattedWrite(stream, "%x", sh);
- assert(stream.data == "7fff");
- stream.clear(); formattedWrite(stream, "%x", ++sh);
- assert(stream.data == "8000");
- stream.clear(); formattedWrite(stream, "%x", ++sh);
- assert(stream.data == "8001");
-
- i = int.max;
- stream.clear(); formattedWrite(stream, "%x", i);
- assert(stream.data == "7fffffff");
- stream.clear(); formattedWrite(stream, "%x", ++i);
- assert(stream.data == "80000000");
- stream.clear(); formattedWrite(stream, "%x", ++i);
- assert(stream.data == "80000001");
-
- stream.clear(); formattedWrite(stream, "%x", 10);
- assert(stream.data == "a");
- stream.clear(); formattedWrite(stream, "%X", 10);
- assert(stream.data == "A");
- stream.clear(); formattedWrite(stream, "%x", 15);
- assert(stream.data == "f");
- stream.clear(); formattedWrite(stream, "%X", 15);
- assert(stream.data == "F");
-
- @trusted void ObjectTest()
- {
- Object c = null;
- stream.clear(); formattedWrite(stream, "%s", c);
- assert(stream.data == "null");
- }
- ObjectTest();
-
- enum TestEnum
- {
- Value1, Value2
- }
- stream.clear(); formattedWrite(stream, "%s", TestEnum.Value2);
- assert(stream.data == "Value2", stream.data);
- stream.clear(); formattedWrite(stream, "%s", cast(TestEnum) 5);
- assert(stream.data == "cast(TestEnum)5", stream.data);
-
- //immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
- //stream.clear(); formattedWrite(stream, "%s", aa.values);
- //core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr);
- //assert(stream.data == "[[h,e,l,l,o],[b,e,t,t,y]]");
- //stream.clear(); formattedWrite(stream, "%s", aa);
- //assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]");
-
- static const dchar[] ds = ['a','b'];
- for (int j = 0; j < ds.length; ++j)
- {
- stream.clear(); formattedWrite(stream, " %d", ds[j]);
- if (j == 0)
- assert(stream.data == " 97");
- else
- assert(stream.data == " 98");
- }
-
- stream.clear(); formattedWrite(stream, "%.-3d", 7);
- assert(stream.data == "7", ">" ~ stream.data ~ "<");
-}
-
-@safe unittest
-{
- import std.array;
- import std.stdio;
-
- immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
- assert(aa[3] == "hello");
- assert(aa[4] == "betty");
-
- auto stream = appender!(char[])();
- alias AllNumerics =
- AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong,
- float, double, real);
- foreach (T; AllNumerics)
- {
- T value = 1;
- stream.clear();
- formattedWrite(stream, "%s", value);
- assert(stream.data == "1");
- }
-
- stream.clear();
- formattedWrite(stream, "%s", aa);
-}
-
-@system unittest
-{
- string s = "hello!124:34.5";
- string a;
- int b;
- double c;
- formattedRead(s, "%s!%s:%s", &a, &b, &c);
- assert(a == "hello" && b == 124 && c == 34.5);
-}
-
-version (unittest)
-void formatReflectTest(T)(ref T val, string fmt, string formatted, string fn = __FILE__, size_t ln = __LINE__)
-{
- import core.exception : AssertError;
- import std.array : appender;
- auto w = appender!string();
- formattedWrite(w, fmt, val);
-
- auto input = w.data;
- enforce!AssertError(
- input == formatted,
- input, fn, ln);
-
- T val2;
- formattedRead(input, fmt, &val2);
- static if (isAssociativeArray!T)
- if (__ctfe)
- {
- alias aa1 = val;
- alias aa2 = val2;
- assert(aa1 == aa2);
-
- assert(aa1.length == aa2.length);
-
- assert(aa1.keys == aa2.keys);
-
- assert(aa1.values == aa2.values);
- assert(aa1.values.length == aa2.values.length);
- foreach (i; 0 .. aa1.values.length)
- assert(aa1.values[i] == aa2.values[i]);
-
- foreach (i, key; aa1.keys)
- assert(aa1.values[i] == aa1[key]);
- foreach (i, key; aa2.keys)
- assert(aa2.values[i] == aa2[key]);
- return;
- }
- enforce!AssertError(
- val == val2,
- input, fn, ln);
-}
-
-version (unittest)
-void formatReflectTest(T)(ref T val, string fmt, string[] formatted, string fn = __FILE__, size_t ln = __LINE__)
-{
- import core.exception : AssertError;
- import std.array : appender;
- auto w = appender!string();
- formattedWrite(w, fmt, val);
-
- auto input = w.data;
-
- foreach (cur; formatted)
- {
- if (input == cur) return;
- }
- enforce!AssertError(
- false,
- input,
- fn,
- ln);
-
- T val2;
- formattedRead(input, fmt, &val2);
- static if (isAssociativeArray!T)
- if (__ctfe)
- {
- alias aa1 = val;
- alias aa2 = val2;
- assert(aa1 == aa2);
-
- assert(aa1.length == aa2.length);
-
- assert(aa1.keys == aa2.keys);
-
- assert(aa1.values == aa2.values);
- assert(aa1.values.length == aa2.values.length);
- foreach (i; 0 .. aa1.values.length)
- assert(aa1.values[i] == aa2.values[i]);
-
- foreach (i, key; aa1.keys)
- assert(aa1.values[i] == aa1[key]);
- foreach (i, key; aa2.keys)
- assert(aa2.values[i] == aa2[key]);
- return;
- }
- enforce!AssertError(
- val == val2,
- input, fn, ln);
-}
-
-@system unittest
-{
- void booleanTest()
- {
- auto b = true;
- formatReflectTest(b, "%s", `true`);
- formatReflectTest(b, "%b", `1`);
- formatReflectTest(b, "%o", `1`);
- formatReflectTest(b, "%d", `1`);
- formatReflectTest(b, "%u", `1`);
- formatReflectTest(b, "%x", `1`);
- }
-
- void integerTest()
- {
- auto n = 127;
- formatReflectTest(n, "%s", `127`);
- formatReflectTest(n, "%b", `1111111`);
- formatReflectTest(n, "%o", `177`);
- formatReflectTest(n, "%d", `127`);
- formatReflectTest(n, "%u", `127`);
- formatReflectTest(n, "%x", `7f`);
- }
-
- void floatingTest()
- {
- auto f = 3.14;
- formatReflectTest(f, "%s", `3.14`);
- version (MinGW)
- formatReflectTest(f, "%e", `3.140000e+000`);
- else
- formatReflectTest(f, "%e", `3.140000e+00`);
- formatReflectTest(f, "%f", `3.140000`);
- formatReflectTest(f, "%g", `3.14`);
- }
-
- void charTest()
- {
- auto c = 'a';
- formatReflectTest(c, "%s", `a`);
- formatReflectTest(c, "%c", `a`);
- formatReflectTest(c, "%b", `1100001`);
- formatReflectTest(c, "%o", `141`);
- formatReflectTest(c, "%d", `97`);
- formatReflectTest(c, "%u", `97`);
- formatReflectTest(c, "%x", `61`);
- }
-
- void strTest()
- {
- auto s = "hello";
- formatReflectTest(s, "%s", `hello`);
- formatReflectTest(s, "%(%c,%)", `h,e,l,l,o`);
- formatReflectTest(s, "%(%s,%)", `'h','e','l','l','o'`);
- formatReflectTest(s, "[%(<%c>%| $ %)]", `[<h> $ <e> $ <l> $ <l> $ <o>]`);
- }
-
- void daTest()
- {
- auto a = [1,2,3,4];
- formatReflectTest(a, "%s", `[1, 2, 3, 4]`);
- formatReflectTest(a, "[%(%s; %)]", `[1; 2; 3; 4]`);
- formatReflectTest(a, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`);
- }
-
- void saTest()
- {
- int[4] sa = [1,2,3,4];
- formatReflectTest(sa, "%s", `[1, 2, 3, 4]`);
- formatReflectTest(sa, "[%(%s; %)]", `[1; 2; 3; 4]`);
- formatReflectTest(sa, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`);
- }
-
- void aaTest()
- {
- auto aa = [1:"hello", 2:"world"];
- formatReflectTest(aa, "%s", [`[1:"hello", 2:"world"]`, `[2:"world", 1:"hello"]`]);
- formatReflectTest(aa, "[%(%s->%s, %)]", [`[1->"hello", 2->"world"]`, `[2->"world", 1->"hello"]`]);
- formatReflectTest(aa, "{%([%s=%(%c%)]%|; %)}", [`{[1=hello]; [2=world]}`, `{[2=world]; [1=hello]}`]);
- }
-
- import std.exception;
- assertCTFEable!(
- {
- booleanTest();
- integerTest();
- if (!__ctfe) floatingTest(); // snprintf
- charTest();
- strTest();
- daTest();
- saTest();
- aaTest();
- return true;
- });
-}
-
-//------------------------------------------------------------------------------
-private void skipData(Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-{
- import std.ascii : isDigit;
- import std.conv : text;
-
- switch (spec.spec)
- {
- case 'c': input.popFront(); break;
- case 'd':
- if (input.front == '+' || input.front == '-') input.popFront();
- goto case 'u';
- case 'u':
- while (!input.empty && isDigit(input.front)) input.popFront();
- break;
- default:
- assert(false,
- text("Format specifier not understood: %", spec.spec));
- }
-}
-
-private template acceptedSpecs(T)
-{
- static if (isIntegral!T) enum acceptedSpecs = "bdosuxX";
- else static if (isFloatingPoint!T) enum acceptedSpecs = "seEfgG";
- else static if (isSomeChar!T) enum acceptedSpecs = "bcdosuxX"; // integral + 'c'
- else enum acceptedSpecs = "";
-}
-
-/**
- * Reads a value from the given _input range according to spec
- * and returns it as type `T`.
- *
- * Params:
- * T = the type to return
- * input = the _input range to read from
- * spec = the `FormatSpec` to use when reading from `input`
- * Returns:
- * A value from `input` of type `T`
- * Throws:
- * An `Exception` if `spec` cannot read a type `T`
- * See_Also:
- * $(REF parse, std, conv) and $(REF to, std, conv)
- */
-T unformatValue(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-{
- return unformatValueImpl!T(input, spec);
-}
-
-/// Booleans
-@safe pure unittest
-{
- auto str = "false";
- auto spec = singleSpec("%s");
- assert(unformatValue!bool(str, spec) == false);
-
- str = "1";
- spec = singleSpec("%d");
- assert(unformatValue!bool(str, spec));
-}
-
-/// Null values
-@safe pure unittest
-{
- auto str = "null";
- auto spec = singleSpec("%s");
- assert(str.unformatValue!(typeof(null))(spec) == null);
-}
-
-/// Integrals
-@safe pure unittest
-{
- auto str = "123";
- auto spec = singleSpec("%s");
- assert(str.unformatValue!int(spec) == 123);
-
- str = "ABC";
- spec = singleSpec("%X");
- assert(str.unformatValue!int(spec) == 2748);
-
- str = "11610";
- spec = singleSpec("%o");
- assert(str.unformatValue!int(spec) == 5000);
-}
-
-/// Floating point numbers
-@safe pure unittest
-{
- import std.math : approxEqual;
-
- auto str = "123.456";
- auto spec = singleSpec("%s");
- assert(str.unformatValue!double(spec).approxEqual(123.456));
-}
-
-/// Character input ranges
-@safe pure unittest
-{
- auto str = "aaa";
- auto spec = singleSpec("%s");
- assert(str.unformatValue!char(spec) == 'a');
-
- // Using a numerical format spec reads a Unicode value from a string
- str = "65";
- spec = singleSpec("%d");
- assert(str.unformatValue!char(spec) == 'A');
-
- str = "41";
- spec = singleSpec("%x");
- assert(str.unformatValue!char(spec) == 'A');
-
- str = "10003";
- spec = singleSpec("%d");
- assert(str.unformatValue!dchar(spec) == '✓');
-}
-
-/// Arrays and static arrays
-@safe pure unittest
-{
- string str = "aaa";
- auto spec = singleSpec("%s");
- assert(str.unformatValue!(dchar[])(spec) == "aaa"d);
-
- str = "aaa";
- spec = singleSpec("%s");
- dchar[3] ret = ['a', 'a', 'a'];
- assert(str.unformatValue!(dchar[3])(spec) == ret);
-
- str = "[1, 2, 3, 4]";
- spec = singleSpec("%s");
- assert(str.unformatValue!(int[])(spec) == [1, 2, 3, 4]);
-
- str = "[1, 2, 3, 4]";
- spec = singleSpec("%s");
- int[4] ret2 = [1, 2, 3, 4];
- assert(str.unformatValue!(int[4])(spec) == ret2);
-}
-
-/// Associative arrays
-@safe pure unittest
-{
- auto str = `["one": 1, "two": 2]`;
- auto spec = singleSpec("%s");
- assert(str.unformatValue!(int[string])(spec) == ["one": 1, "two": 2]);
-}
-
-@safe pure unittest
-{
- // 7241
- string input = "a";
- auto spec = FormatSpec!char("%s");
- spec.readUpToNextSpec(input);
- auto result = unformatValue!(dchar[1])(input, spec);
- assert(result[0] == 'a');
-}
-
-private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-if (isInputRange!Range && is(Unqual!T == bool))
-{
- import std.algorithm.searching : find;
- import std.conv : parse, text;
-
- if (spec.spec == 's') return parse!T(input);
-
- enforce(find(acceptedSpecs!long, spec.spec).length,
- text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
-
- return unformatValue!long(input, spec) != 0;
-}
-
-private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-if (isInputRange!Range && is(T == typeof(null)))
-{
- import std.conv : parse, text;
- enforce(spec.spec == 's',
- text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
-
- return parse!T(input);
-}
-
-/// ditto
-private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-if (isInputRange!Range && isIntegral!T && !is(T == enum) && isSomeChar!(ElementType!Range))
-{
-
- import std.algorithm.searching : find;
- import std.conv : parse, text;
-
- if (spec.spec == 'r')
- {
- static if (is(Unqual!(ElementEncodingType!Range) == char)
- || is(Unqual!(ElementEncodingType!Range) == byte)
- || is(Unqual!(ElementEncodingType!Range) == ubyte))
- return rawRead!T(input);
- else
- throw new Exception("The raw read specifier %r may only be used with narrow strings and ranges of bytes.");
- }
-
- enforce(find(acceptedSpecs!T, spec.spec).length,
- text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
-
- enforce(spec.width == 0, "Parsing integers with a width specification is not implemented"); // TODO
-
- immutable uint base =
- spec.spec == 'x' || spec.spec == 'X' ? 16 :
- spec.spec == 'o' ? 8 :
- spec.spec == 'b' ? 2 :
- spec.spec == 's' || spec.spec == 'd' || spec.spec == 'u' ? 10 : 0;
- assert(base != 0);
-
- return parse!T(input, base);
-
-}
-
-/// ditto
-private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-if (isFloatingPoint!T && !is(T == enum) && isInputRange!Range
- && isSomeChar!(ElementType!Range)&& !is(Range == enum))
-{
- import std.algorithm.searching : find;
- import std.conv : parse, text;
-
- if (spec.spec == 'r')
- {
- static if (is(Unqual!(ElementEncodingType!Range) == char)
- || is(Unqual!(ElementEncodingType!Range) == byte)
- || is(Unqual!(ElementEncodingType!Range) == ubyte))
- return rawRead!T(input);
- else
- throw new Exception("The raw read specifier %r may only be used with narrow strings and ranges of bytes.");
- }
-
- enforce(find(acceptedSpecs!T, spec.spec).length,
- text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
-
- return parse!T(input);
-}
-
-/// ditto
-private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range))
-{
- import std.algorithm.searching : find;
- import std.conv : to, text;
- if (spec.spec == 's' || spec.spec == 'c')
- {
- auto result = to!T(input.front);
- input.popFront();
- return result;
- }
- enforce(find(acceptedSpecs!T, spec.spec).length,
- text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
-
- static if (T.sizeof == 1)
- return unformatValue!ubyte(input, spec);
- else static if (T.sizeof == 2)
- return unformatValue!ushort(input, spec);
- else static if (T.sizeof == 4)
- return unformatValue!uint(input, spec);
- else
- static assert(0);
-}
-
-/// ditto
-private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-if (isInputRange!Range && is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum))
-{
- import std.conv : text;
-
- if (spec.spec == '(')
- {
- return unformatRange!T(input, spec);
- }
- enforce(spec.spec == 's',
- text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
-
- static if (isStaticArray!T)
- {
- T result;
- auto app = result[];
- }
- else
- {
- import std.array : appender;
- auto app = appender!T();
- }
- if (spec.trailing.empty)
- {
- for (; !input.empty; input.popFront())
- {
- static if (isStaticArray!T)
- if (app.empty)
- break;
- app.put(input.front);
- }
- }
- else
- {
- immutable end = spec.trailing.front;
- for (; !input.empty && input.front != end; input.popFront())
- {
- static if (isStaticArray!T)
- if (app.empty)
- break;
- app.put(input.front);
- }
- }
- static if (isStaticArray!T)
- {
- enforce(app.empty, "need more input");
- return result;
- }
- else
- return app.data;
-}
-
-/// ditto
-private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-if (isInputRange!Range && isArray!T && !is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum))
-{
- import std.conv : parse, text;
- if (spec.spec == '(')
- {
- return unformatRange!T(input, spec);
- }
- enforce(spec.spec == 's',
- text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
-
- return parse!T(input);
-}
-
-/// ditto
-private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-if (isInputRange!Range && isAssociativeArray!T && !is(T == enum))
-{
- import std.conv : parse, text;
- if (spec.spec == '(')
- {
- return unformatRange!T(input, spec);
- }
- enforce(spec.spec == 's',
- text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
-
- return parse!T(input);
-}
-
-/**
- * Function that performs raw reading. Used by unformatValue
- * for integral and float types.
- */
-private T rawRead(T, Range)(ref Range input)
-if (is(Unqual!(ElementEncodingType!Range) == char)
- || is(Unqual!(ElementEncodingType!Range) == byte)
- || is(Unqual!(ElementEncodingType!Range) == ubyte))
-{
- union X
- {
- ubyte[T.sizeof] raw;
- T typed;
- }
- X x;
- foreach (i; 0 .. T.sizeof)
- {
- static if (isSomeString!Range)
- {
- x.raw[i] = input[0];
- input = input[1 .. $];
- }
- else
- {
- // TODO: recheck this
- x.raw[i] = input.front;
- input.popFront();
- }
- }
- return x.typed;
-}
-
-//debug = unformatRange;
-
-private T unformatRange(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-in
-{
- assert(spec.spec == '(');
-}
-body
-{
- debug (unformatRange) printf("unformatRange:\n");
-
- T result;
- static if (isStaticArray!T)
- {
- size_t i;
- }
-
- const(Char)[] cont = spec.trailing;
- for (size_t j = 0; j < spec.trailing.length; ++j)
- {
- if (spec.trailing[j] == '%')
- {
- cont = spec.trailing[0 .. j];
- break;
- }
- }
- debug (unformatRange) printf("\t");
- debug (unformatRange) if (!input.empty) printf("input.front = %c, ", input.front);
- debug (unformatRange) printf("cont = %.*s\n", cast(int) cont.length, cont.ptr);
-
- bool checkEnd()
- {
- return input.empty || !cont.empty && input.front == cont.front;
- }
-
- if (!checkEnd())
- {
- for (;;)
- {
- auto fmt = FormatSpec!Char(spec.nested);
- fmt.readUpToNextSpec(input);
- enforce(!input.empty, "Unexpected end of input when parsing range");
-
- debug (unformatRange) printf("\t) spec = %c, front = %c ", fmt.spec, input.front);
- static if (isStaticArray!T)
- {
- result[i++] = unformatElement!(typeof(T.init[0]))(input, fmt);
- }
- else static if (isDynamicArray!T)
- {
- result ~= unformatElement!(ElementType!T)(input, fmt);
- }
- else static if (isAssociativeArray!T)
- {
- auto key = unformatElement!(typeof(T.init.keys[0]))(input, fmt);
- fmt.readUpToNextSpec(input); // eat key separator
-
- result[key] = unformatElement!(typeof(T.init.values[0]))(input, fmt);
- }
- debug (unformatRange) {
- if (input.empty) printf("-> front = [empty] ");
- else printf("-> front = %c ", input.front);
- }
-
- static if (isStaticArray!T)
- {
- debug (unformatRange) printf("i = %u < %u\n", i, T.length);
- enforce(i <= T.length, "Too many format specifiers for static array of length %d".format(T.length));
- }
-
- if (spec.sep !is null)
- fmt.readUpToNextSpec(input);
- auto sep = spec.sep !is null ? spec.sep
- : fmt.trailing;
- debug (unformatRange) {
- if (!sep.empty && !input.empty) printf("-> %c, sep = %.*s\n", input.front, cast(int) sep.length, sep.ptr);
- else printf("\n");
- }
-
- if (checkEnd())
- break;
-
- if (!sep.empty && input.front == sep.front)
- {
- while (!sep.empty)
- {
- enforce(!input.empty, "Unexpected end of input when parsing range separator");
- enforce(input.front == sep.front, "Unexpected character when parsing range separator");
- input.popFront();
- sep.popFront();
- }
- debug (unformatRange) printf("input.front = %c\n", input.front);
- }
- }
- }
- static if (isStaticArray!T)
- {
- enforce(i == T.length, "Too few (%d) format specifiers for static array of length %d".format(i, T.length));
- }
- return result;
-}
-
-// Undocumented
-T unformatElement(T, Range, Char)(ref Range input, const ref FormatSpec!Char spec)
-if (isInputRange!Range)
-{
- import std.conv : parseElement;
- static if (isSomeString!T)
- {
- if (spec.spec == 's')
- {
- return parseElement!T(input);
- }
- }
- else static if (isSomeChar!T)
- {
- if (spec.spec == 's')
- {
- return parseElement!T(input);
- }
- }
-
- return unformatValue!T(input, spec);
-}
-
-
-// Legacy implementation
-
-enum Mangle : char
-{
- Tvoid = 'v',
- Tbool = 'b',
- Tbyte = 'g',
- Tubyte = 'h',
- Tshort = 's',
- Tushort = 't',
- Tint = 'i',
- Tuint = 'k',
- Tlong = 'l',
- Tulong = 'm',
- Tfloat = 'f',
- Tdouble = 'd',
- Treal = 'e',
-
- Tifloat = 'o',
- Tidouble = 'p',
- Tireal = 'j',
- Tcfloat = 'q',
- Tcdouble = 'r',
- Tcreal = 'c',
-
- Tchar = 'a',
- Twchar = 'u',
- Tdchar = 'w',
-
- Tarray = 'A',
- Tsarray = 'G',
- Taarray = 'H',
- Tpointer = 'P',
- Tfunction = 'F',
- Tident = 'I',
- Tclass = 'C',
- Tstruct = 'S',
- Tenum = 'E',
- Ttypedef = 'T',
- Tdelegate = 'D',
-
- Tconst = 'x',
- Timmutable = 'y',
-}
-
-// return the TypeInfo for a primitive type and null otherwise. This
-// is required since for arrays of ints we only have the mangled char
-// to work from. If arrays always subclassed TypeInfo_Array this
-// routine could go away.
-private TypeInfo primitiveTypeInfo(Mangle m)
-{
- // BUG: should fix this in static this() to avoid double checked locking bug
- __gshared TypeInfo[Mangle] dic;
- if (!dic.length)
- {
- dic = [
- Mangle.Tvoid : typeid(void),
- Mangle.Tbool : typeid(bool),
- Mangle.Tbyte : typeid(byte),
- Mangle.Tubyte : typeid(ubyte),
- Mangle.Tshort : typeid(short),
- Mangle.Tushort : typeid(ushort),
- Mangle.Tint : typeid(int),
- Mangle.Tuint : typeid(uint),
- Mangle.Tlong : typeid(long),
- Mangle.Tulong : typeid(ulong),
- Mangle.Tfloat : typeid(float),
- Mangle.Tdouble : typeid(double),
- Mangle.Treal : typeid(real),
- Mangle.Tifloat : typeid(ifloat),
- Mangle.Tidouble : typeid(idouble),
- Mangle.Tireal : typeid(ireal),
- Mangle.Tcfloat : typeid(cfloat),
- Mangle.Tcdouble : typeid(cdouble),
- Mangle.Tcreal : typeid(creal),
- Mangle.Tchar : typeid(char),
- Mangle.Twchar : typeid(wchar),
- Mangle.Tdchar : typeid(dchar)
- ];
- }
- auto p = m in dic;
- return p ? *p : null;
-}
-
-private bool needToSwapEndianess(Char)(const ref FormatSpec!Char f)
-{
- import std.system : endian, Endian;
-
- return endian == Endian.littleEndian && f.flPlus
- || endian == Endian.bigEndian && f.flDash;
-}
-
-/* ======================== Unit Tests ====================================== */
-
-@system unittest
-{
- import std.conv : octal;
-
- int i;
- string s;
-
- debug(format) printf("std.format.format.unittest\n");
-
- s = format("hello world! %s %s %s%s%s", true, 57, 1_000_000_000, 'x', " foo");
- assert(s == "hello world! true 57 1000000000x foo");
-
- s = format("%s %A %s", 1.67, -1.28, float.nan);
- /* The host C library is used to format floats.
- * C99 doesn't specify what the hex digit before the decimal point
- * is for %A.
- */
- //version (linux)
- // assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan");
- //else version (OSX)
- // assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s);
- //else
- version (MinGW)
- assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s);
- else version (CRuntime_Microsoft)
- assert(s == "1.67 -0X1.47AE14P+0 nan"
- || s == "1.67 -0X1.47AE147AE147BP+0 nan", s); // MSVCRT 14+ (VS 2015)
- else
- assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s);
-
- s = format("%x %X", 0x1234AF, 0xAFAFAFAF);
- assert(s == "1234af AFAFAFAF");
-
- s = format("%b %o", 0x1234AF, 0xAFAFAFAF);
- assert(s == "100100011010010101111 25753727657");
-
- s = format("%d %s", 0x1234AF, 0xAFAFAFAF);
- assert(s == "1193135 2947526575");
-
- //version (X86_64)
- //{
- // pragma(msg, "several format tests disabled on x86_64 due to bug 5625");
- //}
- //else
- //{
- s = format("%s", 1.2 + 3.4i);
- assert(s == "1.2+3.4i", s);
-
- //s = format("%x %X", 1.32, 6.78f);
- //assert(s == "3ff51eb851eb851f 40D8F5C3");
-
- //}
-
- s = format("%#06.*f",2,12.345);
- assert(s == "012.35");
-
- s = format("%#0*.*f",6,2,12.345);
- assert(s == "012.35");
-
- s = format("%7.4g:", 12.678);
- assert(s == " 12.68:");
-
- s = format("%7.4g:", 12.678L);
- assert(s == " 12.68:");
-
- s = format("%04f|%05d|%#05x|%#5x",-4.0,-10,1,1);
- assert(s == "-4.000000|-0010|0x001| 0x1");
-
- i = -10;
- s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(s == "-10|-10|-10|-10|-10.0000");
-
- i = -5;
- s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(s == "-5| -5|-05|-5|-5.0000");
-
- i = 0;
- s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(s == "0| 0|000|0|0.0000");
-
- i = 5;
- s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(s == "5| 5|005|5|5.0000");
-
- i = 10;
- s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
- assert(s == "10| 10|010|10|10.0000");
-
- s = format("%.0d", 0);
- assert(s == "");
-
- s = format("%.g", .34);
- assert(s == "0.3");
-
- s = format("%.0g", .34);
- assert(s == "0.3");
-
- s = format("%.2g", .34);
- assert(s == "0.34");
-
- s = format("%0.0008f", 1e-08);
- assert(s == "0.00000001");
-
- s = format("%0.0008f", 1e-05);
- assert(s == "0.00001000");
-
- s = "helloworld";
- string r;
- r = format("%.2s", s[0 .. 5]);
- assert(r == "he");
- r = format("%.20s", s[0 .. 5]);
- assert(r == "hello");
- r = format("%8s", s[0 .. 5]);
- assert(r == " hello");
-
- byte[] arrbyte = new byte[4];
- arrbyte[0] = 100;
- arrbyte[1] = -99;
- arrbyte[3] = 0;
- r = format("%s", arrbyte);
- assert(r == "[100, -99, 0, 0]");
-
- ubyte[] arrubyte = new ubyte[4];
- arrubyte[0] = 100;
- arrubyte[1] = 200;
- arrubyte[3] = 0;
- r = format("%s", arrubyte);
- assert(r == "[100, 200, 0, 0]");
-
- short[] arrshort = new short[4];
- arrshort[0] = 100;
- arrshort[1] = -999;
- arrshort[3] = 0;
- r = format("%s", arrshort);
- assert(r == "[100, -999, 0, 0]");
-
- ushort[] arrushort = new ushort[4];
- arrushort[0] = 100;
- arrushort[1] = 20_000;
- arrushort[3] = 0;
- r = format("%s", arrushort);
- assert(r == "[100, 20000, 0, 0]");
-
- int[] arrint = new int[4];
- arrint[0] = 100;
- arrint[1] = -999;
- arrint[3] = 0;
- r = format("%s", arrint);
- assert(r == "[100, -999, 0, 0]");
-
- long[] arrlong = new long[4];
- arrlong[0] = 100;
- arrlong[1] = -999;
- arrlong[3] = 0;
- r = format("%s", arrlong);
- assert(r == "[100, -999, 0, 0]");
-
- ulong[] arrulong = new ulong[4];
- arrulong[0] = 100;
- arrulong[1] = 999;
- arrulong[3] = 0;
- r = format("%s", arrulong);
- assert(r == "[100, 999, 0, 0]");
-
- string[] arr2 = new string[4];
- arr2[0] = "hello";
- arr2[1] = "world";
- arr2[3] = "foo";
- r = format("%s", arr2);
- assert(r == `["hello", "world", "", "foo"]`);
-
- r = format("%.8d", 7);
- assert(r == "00000007");
- r = format("%.8x", 10);
- assert(r == "0000000a");
-
- r = format("%-3d", 7);
- assert(r == "7 ");
-
- r = format("%-1*d", 4, 3);
- assert(r == "3 ");
-
- r = format("%*d", -3, 7);
- assert(r == "7 ");
-
- r = format("%.*d", -3, 7);
- assert(r == "7");
-
- r = format("%-1.*f", 2, 3.1415);
- assert(r == "3.14");
-
- r = format("abc"c);
- assert(r == "abc");
-
- //format() returns the same type as inputted.
- wstring wr;
- wr = format("def"w);
- assert(wr == "def"w);
-
- dstring dr;
- dr = format("ghi"d);
- assert(dr == "ghi"d);
-
- void* p = cast(void*) 0xDEADBEEF;
- r = format("%s", p);
- assert(r == "DEADBEEF");
-
- r = format("%#x", 0xabcd);
- assert(r == "0xabcd");
- r = format("%#X", 0xABCD);
- assert(r == "0XABCD");
-
- r = format("%#o", octal!12345);
- assert(r == "012345");
- r = format("%o", 9);
- assert(r == "11");
- r = format("%#o", 0); // issue 15663
- assert(r == "0");
-
- r = format("%+d", 123);
- assert(r == "+123");
- r = format("%+d", -123);
- assert(r == "-123");
- r = format("% d", 123);
- assert(r == " 123");
- r = format("% d", -123);
- assert(r == "-123");
-
- r = format("%%");
- assert(r == "%");
-
- r = format("%d", true);
- assert(r == "1");
- r = format("%d", false);
- assert(r == "0");
-
- r = format("%d", 'a');
- assert(r == "97");
- wchar wc = 'a';
- r = format("%d", wc);
- assert(r == "97");
- dchar dc = 'a';
- r = format("%d", dc);
- assert(r == "97");
-
- byte b = byte.max;
- r = format("%x", b);
- assert(r == "7f");
- r = format("%x", ++b);
- assert(r == "80");
- r = format("%x", ++b);
- assert(r == "81");
-
- short sh = short.max;
- r = format("%x", sh);
- assert(r == "7fff");
- r = format("%x", ++sh);
- assert(r == "8000");
- r = format("%x", ++sh);
- assert(r == "8001");
-
- i = int.max;
- r = format("%x", i);
- assert(r == "7fffffff");
- r = format("%x", ++i);
- assert(r == "80000000");
- r = format("%x", ++i);
- assert(r == "80000001");
-
- r = format("%x", 10);
- assert(r == "a");
- r = format("%X", 10);
- assert(r == "A");
- r = format("%x", 15);
- assert(r == "f");
- r = format("%X", 15);
- assert(r == "F");
-
- Object c = null;
- r = format("%s", c);
- assert(r == "null");
-
- enum TestEnum
- {
- Value1, Value2
- }
- r = format("%s", TestEnum.Value2);
- assert(r == "Value2");
-
- immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
- r = format("%s", aa.values);
- assert(r == `["hello", "betty"]` || r == `["betty", "hello"]`);
- r = format("%s", aa);
- assert(r == `[3:"hello", 4:"betty"]` || r == `[4:"betty", 3:"hello"]`);
-
- static const dchar[] ds = ['a','b'];
- for (int j = 0; j < ds.length; ++j)
- {
- r = format(" %d", ds[j]);
- if (j == 0)
- assert(r == " 97");
- else
- assert(r == " 98");
- }
-
- r = format(">%14d<, %s", 15, [1,2,3]);
- assert(r == "> 15<, [1, 2, 3]");
-
- assert(format("%8s", "bar") == " bar");
- assert(format("%8s", "b\u00e9ll\u00f4") == " b\u00e9ll\u00f4");
-}
-
-@safe unittest
-{
- // bugzilla 3479
- import std.array;
- auto stream = appender!(char[])();
- formattedWrite(stream, "%2$.*1$d", 12, 10);
- assert(stream.data == "000000000010", stream.data);
-}
-
-@safe unittest
-{
- // bug 6893
- import std.array;
- enum E : ulong { A, B, C }
- auto stream = appender!(char[])();
- formattedWrite(stream, "%s", E.C);
- assert(stream.data == "C");
-}
-
-// Used to check format strings are compatible with argument types
-package static const checkFormatException(alias fmt, Args...) =
-{
- try
- .format(fmt, Args.init);
- catch (Exception e)
- return (e.msg == ctfpMessage) ? null : e;
- return null;
-}();
-
-/*****************************************************
- * Format arguments into a string.
- *
- * Params: fmt = Format string. For detailed specification, see $(LREF formattedWrite).
- * args = Variadic list of arguments to _format into returned string.
- */
-typeof(fmt) format(alias fmt, Args...)(Args args)
-if (isSomeString!(typeof(fmt)))
-{
- alias e = checkFormatException!(fmt, Args);
- static assert(!e, e.msg);
- return .format(fmt, args);
-}
-
-/// Type checking can be done when fmt is known at compile-time:
-@safe unittest
-{
- auto s = format!"%s is %s"("Pi", 3.14);
- assert(s == "Pi is 3.14");
-
- static assert(!__traits(compiles, {s = format!"%l"();})); // missing arg
- static assert(!__traits(compiles, {s = format!""(404);})); // surplus arg
- static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg
-}
-
-/// ditto
-immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args)
-if (isSomeChar!Char)
-{
- import std.array : appender;
- import std.format : formattedWrite, FormatException;
- auto w = appender!(immutable(Char)[]);
- auto n = formattedWrite(w, fmt, args);
- version (all)
- {
- // In the future, this check will be removed to increase consistency
- // with formattedWrite
- import std.conv : text;
- import std.exception : enforce;
- enforce(n == args.length, new FormatException(
- text("Orphan format arguments: args[", n, "..", args.length, "]")));
- }
- return w.data;
-}
-
-@safe pure unittest
-{
- import core.exception;
- import std.exception;
- import std.format;
- assertCTFEable!(
- {
-// assert(format(null) == "");
- assert(format("foo") == "foo");
- assert(format("foo%%") == "foo%");
- assert(format("foo%s", 'C') == "fooC");
- assert(format("%s foo", "bar") == "bar foo");
- assert(format("%s foo %s", "bar", "abc") == "bar foo abc");
- assert(format("foo %d", -123) == "foo -123");
- assert(format("foo %d", 123) == "foo 123");
-
- assertThrown!FormatException(format("foo %s"));
- assertThrown!FormatException(format("foo %s", 123, 456));
-
- assert(format("hel%slo%s%s%s", "world", -138, 'c', true) ==
- "helworldlo-138ctrue");
- });
-
- assert(is(typeof(format("happy")) == string));
- assert(is(typeof(format("happy"w)) == wstring));
- assert(is(typeof(format("happy"d)) == dstring));
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=16661
-@safe unittest
-{
- assert(format("%.2f"d, 0.4) == "0.40");
- assert("%02d"d.format(1) == "01"d);
-}
-
-/*****************************************************
- * Format arguments into buffer $(I buf) which must be large
- * enough to hold the result.
- *
- * Returns:
- * The slice of `buf` containing the formatted string.
- *
- * Throws:
- * A `RangeError` if `buf` isn't large enough to hold the
- * formatted string.
- *
- * A $(LREF FormatException) if the length of `args` is different
- * than the number of format specifiers in `fmt`.
- */
-char[] sformat(alias fmt, Args...)(char[] buf, Args args)
-if (isSomeString!(typeof(fmt)))
-{
- alias e = checkFormatException!(fmt, Args);
- static assert(!e, e.msg);
- return .sformat(buf, fmt, args);
-}
-
-/// ditto
-char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args)
-{
- import core.exception : RangeError;
- import std.format : formattedWrite, FormatException;
- import std.utf : encode;
-
- size_t i;
-
- struct Sink
- {
- void put(dchar c)
- {
- char[4] enc;
- auto n = encode(enc, c);
-
- if (buf.length < i + n)
- throw new RangeError(__FILE__, __LINE__);
-
- buf[i .. i + n] = enc[0 .. n];
- i += n;
- }
- void put(const(char)[] s)
- {
- if (buf.length < i + s.length)
- throw new RangeError(__FILE__, __LINE__);
-
- buf[i .. i + s.length] = s[];
- i += s.length;
- }
- void put(const(wchar)[] s)
- {
- for (; !s.empty; s.popFront())
- put(s.front);
- }
- void put(const(dchar)[] s)
- {
- for (; !s.empty; s.popFront())
- put(s.front);
- }
- }
- auto n = formattedWrite(Sink(), fmt, args);
- version (all)
- {
- // In the future, this check will be removed to increase consistency
- // with formattedWrite
- import std.conv : text;
- import std.exception : enforce;
- enforce!FormatException(
- n == args.length,
- text("Orphan format arguments: args[", n, " .. ", args.length, "]")
- );
- }
- return buf[0 .. i];
-}
-
-/// The format string can be checked at compile-time (see $(LREF format) for details):
-@system unittest
-{
- char[10] buf;
-
- assert(buf[].sformat!"foo%s"('C') == "fooC");
- assert(sformat(buf[], "%s foo", "bar") == "bar foo");
-}
-
-@system unittest
-{
- import core.exception;
- import std.format;
-
- debug(string) trustedPrintf("std.string.sformat.unittest\n");
-
- import std.exception;
- assertCTFEable!(
- {
- char[10] buf;
-
- assert(sformat(buf[], "foo") == "foo");
- assert(sformat(buf[], "foo%%") == "foo%");
- assert(sformat(buf[], "foo%s", 'C') == "fooC");
- assert(sformat(buf[], "%s foo", "bar") == "bar foo");
- assertThrown!RangeError(sformat(buf[], "%s foo %s", "bar", "abc"));
- assert(sformat(buf[], "foo %d", -123) == "foo -123");
- assert(sformat(buf[], "foo %d", 123) == "foo 123");
-
- assertThrown!FormatException(sformat(buf[], "foo %s"));
- assertThrown!FormatException(sformat(buf[], "foo %s", 123, 456));
-
- assert(sformat(buf[], "%s %s %s", "c"c, "w"w, "d"d) == "c w d");
- });
-}
-
-/*****************************
- * The .ptr is unsafe because it could be dereferenced and the length of the array may be 0.
- * Returns:
- * the difference between the starts of the arrays
- */
-@trusted private pure nothrow @nogc
- ptrdiff_t arrayPtrDiff(T)(const T[] array1, const T[] array2)
-{
- return array1.ptr - array2.ptr;
-}
-
-@safe unittest
-{
- assertCTFEable!({
- auto tmp = format("%,d", 1000);
- assert(tmp == "1,000", "'" ~ tmp ~ "'");
-
- tmp = format("%,?d", 'z', 1234567);
- assert(tmp == "1z234z567", "'" ~ tmp ~ "'");
-
- tmp = format("%10,?d", 'z', 1234567);
- assert(tmp == " 1z234z567", "'" ~ tmp ~ "'");
-
- tmp = format("%11,2?d", 'z', 1234567);
- assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'");
-
- tmp = format("%11,*?d", 2, 'z', 1234567);
- assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'");
-
- tmp = format("%11,*d", 2, 1234567);
- assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'");
-
- tmp = format("%11,2d", 1234567);
- assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'");
- });
-}
-
-@safe unittest
-{
- auto tmp = format("%,f", 1000.0);
- assert(tmp == "1,000.000,000", "'" ~ tmp ~ "'");
-
- tmp = format("%,f", 1234567.891011);
- assert(tmp == "1,234,567.891,011", "'" ~ tmp ~ "'");
-
- tmp = format("%,f", -1234567.891011);
- assert(tmp == "-1,234,567.891,011", "'" ~ tmp ~ "'");
-
- tmp = format("%,2f", 1234567.891011);
- assert(tmp == "1,23,45,67.89,10,11", "'" ~ tmp ~ "'");
-
- tmp = format("%18,f", 1234567.891011);
- assert(tmp == " 1,234,567.891,011", "'" ~ tmp ~ "'");
-
- tmp = format("%18,?f", '.', 1234567.891011);
- assert(tmp == " 1.234.567.891.011", "'" ~ tmp ~ "'");
-
- tmp = format("%,?.3f", 'ä', 1234567.891011);
- assert(tmp == "1ä234ä567.891", "'" ~ tmp ~ "'");
-
- tmp = format("%,*?.3f", 1, 'ä', 1234567.891011);
- assert(tmp == "1ä2ä3ä4ä5ä6ä7.8ä9ä1", "'" ~ tmp ~ "'");
-
- tmp = format("%,4?.3f", '_', 1234567.891011);
- assert(tmp == "123_4567.891", "'" ~ tmp ~ "'");
-
- tmp = format("%12,3.3f", 1234.5678);
- assert(tmp == " 1,234.568", "'" ~ tmp ~ "'");
-
- tmp = format("%,e", 3.141592653589793238462);
- assert(tmp == "3.141,593e+00", "'" ~ tmp ~ "'");
-
- tmp = format("%15,e", 3.141592653589793238462);
- assert(tmp == " 3.141,593e+00", "'" ~ tmp ~ "'");
-
- tmp = format("%15,e", -3.141592653589793238462);
- assert(tmp == " -3.141,593e+00", "'" ~ tmp ~ "'");
-
- tmp = format("%.4,*e", 2, 3.141592653589793238462);
- assert(tmp == "3.14,16e+00", "'" ~ tmp ~ "'");
-
- tmp = format("%13.4,*e", 2, 3.141592653589793238462);
- assert(tmp == " 3.14,16e+00", "'" ~ tmp ~ "'");
-
- tmp = format("%,.0f", 3.14);
- assert(tmp == "3", "'" ~ tmp ~ "'");
-
- tmp = format("%3,g", 1_000_000.123456);
- assert(tmp == "1e+06", "'" ~ tmp ~ "'");
-
- tmp = format("%19,?f", '.', -1234567.891011);
- assert(tmp == " -1.234.567.891.011", "'" ~ tmp ~ "'");
-}
-
-// Test for multiple indexes
-@safe unittest
-{
- auto tmp = format("%2:5$s", 1, 2, 3, 4, 5);
- assert(tmp == "2345", tmp);
-}
diff --git a/libphobos/src/std/format/internal/floats.d b/libphobos/src/std/format/internal/floats.d
new file mode 100644
index 00000000000..81b21dc4995
--- /dev/null
+++ b/libphobos/src/std/format/internal/floats.d
@@ -0,0 +1,2930 @@
+// Written in the D programming language.
+
+/*
+ Helper functions for formatting floating point numbers.
+
+ Copyright: Copyright The D Language Foundation 2019 -
+
+ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+ Authors: Bernhard Seckinger
+
+ Source: $(PHOBOSSRC std/format/internal/floats.d)
+ */
+
+module std.format.internal.floats;
+
+import std.format.spec : FormatSpec;
+
+// wrapper for unittests
+private auto printFloat(T, Char)(T val, FormatSpec!Char f)
+if (is(T == float) || is(T == double)
+ || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
+{
+ import std.array : appender;
+ auto w = appender!string();
+
+ printFloat(w, val, f);
+ return w.data;
+}
+
+package(std.format) void printFloat(Writer, T, Char)(auto ref Writer w, T val, FormatSpec!Char f)
+if (is(T == float) || is(T == double)
+ || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
+{
+ import std.math.operations : extractBitpattern, FloatingPointBitpattern;
+
+ auto bp = extractBitpattern(val);
+
+ ulong mnt = bp.mantissa;
+ int exp = bp.exponent;
+ string sgn = bp.negative ? "-" : "";
+
+ if (sgn == "" && f.flPlus) sgn = "+";
+ if (sgn == "" && f.flSpace) sgn = " ";
+
+ assert(f.spec == 'a' || f.spec == 'A'
+ || f.spec == 'e' || f.spec == 'E'
+ || f.spec == 'f' || f.spec == 'F'
+ || f.spec == 'g' || f.spec == 'G', "unsupported format specifier");
+ bool is_upper = f.spec == 'A' || f.spec == 'E' || f.spec=='F' || f.spec=='G';
+
+ // special treatment for nan and inf
+ if (exp == T.max_exp)
+ {
+ import std.format.internal.write : writeAligned;
+
+ f.flZero = false;
+ writeAligned(w, sgn, "", (mnt == 0) ? ( is_upper ? "INF" : "inf" ) : ( is_upper ? "NAN" : "nan" ), f);
+ return;
+ }
+
+ final switch (f.spec)
+ {
+ case 'a': case 'A':
+ printFloatA(w, val, f, sgn, exp, mnt, is_upper);
+ break;
+ case 'e': case 'E':
+ printFloatE!false(w, val, f, sgn, exp, mnt, is_upper);
+ break;
+ case 'f': case 'F':
+ printFloatF!false(w, val, f, sgn, exp, mnt, is_upper);
+ break;
+ case 'g': case 'G':
+ printFloatG(w, val, f, sgn, exp, mnt, is_upper);
+ break;
+ }
+}
+
+private void printFloatA(Writer, T, Char)(auto ref Writer w, T val,
+ FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper)
+if (is(T == float) || is(T == double)
+ || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
+{
+ import std.algorithm.comparison : max;
+ import std.format.internal.write : writeAligned, PrecisionType;
+
+ char[3] prefix;
+ if (sgn != "") prefix[0] = sgn[0];
+ prefix[1] = '0';
+ prefix[2] = is_upper ? 'X' : 'x';
+
+ // print exponent
+ if (mnt == 0)
+ {
+ if (f.precision == f.UNSPECIFIED)
+ f.precision = 0;
+ writeAligned(w, prefix[1 - sgn.length .. $], "0", ".", is_upper ? "P+0" : "p+0",
+ f, PrecisionType.fractionalDigits);
+ return;
+ }
+
+ // save integer part
+ char first = '0' + ((mnt >> (T.mant_dig - 1)) & 1);
+ mnt &= (1L << (T.mant_dig - 1)) - 1;
+
+ static if (is(T == float) || (is(T == real) && T.mant_dig == 64))
+ {
+ mnt <<= 1; // make mnt dividable by 4
+ enum mant_len = T.mant_dig;
+ }
+ else
+ enum mant_len = T.mant_dig - 1;
+ static assert(mant_len % 4 == 0, "mantissa with wrong length");
+
+ // print full mantissa
+ char[(mant_len - 1) / 4 + 3] hex_mant;
+ size_t hex_mant_pos = 2;
+ size_t pos = mant_len;
+
+ auto gap = 39 - 32 * is_upper;
+ while (pos >= 4 && (mnt & (((1L << (pos - 1)) - 1) << 1) + 1) != 0)
+ {
+ pos -= 4;
+ size_t tmp = (mnt >> pos) & 15;
+ // For speed reasons the better readable
+ // ... = tmp < 10 ? ('0' + tmp) : ((is_upper ? 'A' : 'a') + tmp - 10))
+ // has been replaced with an expression without branches, doing the same
+ hex_mant[hex_mant_pos++] = cast(char) (tmp + gap * ((tmp + 6) >> 4) + '0');
+ }
+ hex_mant[0] = first;
+ hex_mant[1] = '.';
+
+ if (f.precision == f.UNSPECIFIED)
+ f.precision = cast(int) hex_mant_pos - 2;
+
+ auto exp_sgn = exp >= 0 ? '+' : '-';
+ if (exp < 0) exp = -exp;
+
+ static if (is(T == real) && real.mant_dig == 64)
+ enum max_exp_digits = 8;
+ else static if (is(T == float))
+ enum max_exp_digits = 5;
+ else
+ enum max_exp_digits = 6;
+
+ char[max_exp_digits] exp_str;
+ size_t exp_pos = max_exp_digits;
+
+ do
+ {
+ exp_str[--exp_pos] = '0' + exp % 10;
+ exp /= 10;
+ } while (exp > 0);
+
+ exp_str[--exp_pos] = exp_sgn;
+ exp_str[--exp_pos] = is_upper ? 'P' : 'p';
+
+ if (f.precision < hex_mant_pos - 2)
+ {
+ import std.format.internal.write : RoundingClass, round;
+
+ RoundingClass rc;
+
+ if (hex_mant[f.precision + 2] == '0')
+ rc = RoundingClass.ZERO;
+ else if (hex_mant[f.precision + 2] < '8')
+ rc = RoundingClass.LOWER;
+ else if (hex_mant[f.precision + 2] > '8')
+ rc = RoundingClass.UPPER;
+ else
+ rc = RoundingClass.FIVE;
+
+ if (rc == RoundingClass.ZERO || rc == RoundingClass.FIVE)
+ {
+ foreach (i;f.precision + 3 .. hex_mant_pos)
+ {
+ if (hex_mant[i] > '0')
+ {
+ rc = rc == RoundingClass.ZERO ? RoundingClass.LOWER : RoundingClass.UPPER;
+ break;
+ }
+ }
+ }
+
+ hex_mant_pos = f.precision + 2;
+
+ round(hex_mant, 0, hex_mant_pos, rc, sgn == "-", is_upper ? 'F' : 'f');
+ }
+
+ writeAligned(w, prefix[1 - sgn.length .. $], hex_mant[0 .. 1], hex_mant[1 .. hex_mant_pos],
+ exp_str[exp_pos .. $], f, PrecisionType.fractionalDigits);
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ assert(printFloat(float.nan, f) == "nan");
+ assert(printFloat(-float.nan, f) == "-nan");
+ assert(printFloat(float.infinity, f) == "inf");
+ assert(printFloat(-float.infinity, f) == "-inf");
+ assert(printFloat(0.0f, f) == "0x0p+0");
+ assert(printFloat(-0.0f, f) == "-0x0p+0");
+
+ assert(printFloat(double.nan, f) == "nan");
+ assert(printFloat(-double.nan, f) == "-nan");
+ assert(printFloat(double.infinity, f) == "inf");
+ assert(printFloat(-double.infinity, f) == "-inf");
+ assert(printFloat(0.0, f) == "0x0p+0");
+ assert(printFloat(-0.0, f) == "-0x0p+0");
+
+ static if (real.mant_dig > 64)
+ {
+ pragma(msg, "printFloat tests disabled because of unsupported `real` format");
+ }
+ else
+ {
+ assert(printFloat(real.nan, f) == "nan");
+ assert(printFloat(-real.nan, f) == "-nan");
+ assert(printFloat(real.infinity, f) == "inf");
+ assert(printFloat(-real.infinity, f) == "-inf");
+ assert(printFloat(0.0L, f) == "0x0p+0");
+ assert(printFloat(-0.0L, f) == "-0x0p+0");
+ }
+
+ import std.math.operations : nextUp;
+
+ assert(printFloat(nextUp(0.0f), f) == "0x0.000002p-126");
+ assert(printFloat(float.epsilon, f) == "0x1p-23");
+ assert(printFloat(float.min_normal, f) == "0x1p-126");
+ assert(printFloat(float.max, f) == "0x1.fffffep+127");
+
+ assert(printFloat(nextUp(0.0), f) == "0x0.0000000000001p-1022");
+ assert(printFloat(double.epsilon, f) == "0x1p-52");
+ assert(printFloat(double.min_normal, f) == "0x1p-1022");
+ assert(printFloat(double.max, f) == "0x1.fffffffffffffp+1023");
+
+ static if (real.mant_dig == 64)
+ {
+ assert(printFloat(nextUp(0.0L), f) == "0x0.0000000000000002p-16382");
+ assert(printFloat(real.epsilon, f) == "0x1p-63");
+ assert(printFloat(real.min_normal, f) == "0x1p-16382");
+ assert(printFloat(real.max, f) == "0x1.fffffffffffffffep+16383");
+ }
+
+ import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI,
+ LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2;
+
+ assert(printFloat(cast(float) E, f) == "0x1.5bf0a8p+1");
+ assert(printFloat(cast(float) PI, f) == "0x1.921fb6p+1");
+ assert(printFloat(cast(float) PI_2, f) == "0x1.921fb6p+0");
+ assert(printFloat(cast(float) PI_4, f) == "0x1.921fb6p-1");
+ assert(printFloat(cast(float) M_1_PI, f) == "0x1.45f306p-2");
+ assert(printFloat(cast(float) M_2_PI, f) == "0x1.45f306p-1");
+ assert(printFloat(cast(float) M_2_SQRTPI, f) == "0x1.20dd76p+0");
+ assert(printFloat(cast(float) LN10, f) == "0x1.26bb1cp+1");
+ assert(printFloat(cast(float) LN2, f) == "0x1.62e43p-1");
+ assert(printFloat(cast(float) LOG2, f) == "0x1.344136p-2");
+ assert(printFloat(cast(float) LOG2E, f) == "0x1.715476p+0");
+ assert(printFloat(cast(float) LOG2T, f) == "0x1.a934fp+1");
+ assert(printFloat(cast(float) LOG10E, f) == "0x1.bcb7b2p-2");
+ assert(printFloat(cast(float) SQRT2, f) == "0x1.6a09e6p+0");
+ assert(printFloat(cast(float) SQRT1_2, f) == "0x1.6a09e6p-1");
+
+ assert(printFloat(cast(double) E, f) == "0x1.5bf0a8b145769p+1");
+ assert(printFloat(cast(double) PI, f) == "0x1.921fb54442d18p+1");
+ assert(printFloat(cast(double) PI_2, f) == "0x1.921fb54442d18p+0");
+ assert(printFloat(cast(double) PI_4, f) == "0x1.921fb54442d18p-1");
+ assert(printFloat(cast(double) M_1_PI, f) == "0x1.45f306dc9c883p-2");
+ assert(printFloat(cast(double) M_2_PI, f) == "0x1.45f306dc9c883p-1");
+ assert(printFloat(cast(double) M_2_SQRTPI, f) == "0x1.20dd750429b6dp+0");
+ assert(printFloat(cast(double) LN10, f) == "0x1.26bb1bbb55516p+1");
+ assert(printFloat(cast(double) LN2, f) == "0x1.62e42fefa39efp-1");
+ assert(printFloat(cast(double) LOG2, f) == "0x1.34413509f79ffp-2");
+ assert(printFloat(cast(double) LOG2E, f) == "0x1.71547652b82fep+0");
+ assert(printFloat(cast(double) LOG2T, f) == "0x1.a934f0979a371p+1");
+ assert(printFloat(cast(double) LOG10E, f) == "0x1.bcb7b1526e50ep-2");
+ assert(printFloat(cast(double) SQRT2, f) == "0x1.6a09e667f3bcdp+0");
+ assert(printFloat(cast(double) SQRT1_2, f) == "0x1.6a09e667f3bcdp-1");
+
+ static if (real.mant_dig == 64)
+ {
+ assert(printFloat(E, f) == "0x1.5bf0a8b145769536p+1");
+ assert(printFloat(PI, f) == "0x1.921fb54442d1846ap+1");
+ assert(printFloat(PI_2, f) == "0x1.921fb54442d1846ap+0");
+ assert(printFloat(PI_4, f) == "0x1.921fb54442d1846ap-1");
+ assert(printFloat(M_1_PI, f) == "0x1.45f306dc9c882a54p-2");
+ assert(printFloat(M_2_PI, f) == "0x1.45f306dc9c882a54p-1");
+ assert(printFloat(M_2_SQRTPI, f) == "0x1.20dd750429b6d11ap+0");
+ assert(printFloat(LN10, f) == "0x1.26bb1bbb5551582ep+1");
+ assert(printFloat(LN2, f) == "0x1.62e42fefa39ef358p-1");
+ assert(printFloat(LOG2, f) == "0x1.34413509f79fef32p-2");
+ assert(printFloat(LOG2E, f) == "0x1.71547652b82fe178p+0");
+ assert(printFloat(LOG2T, f) == "0x1.a934f0979a3715fcp+1");
+ assert(printFloat(LOG10E, f) == "0x1.bcb7b1526e50e32ap-2");
+ assert(printFloat(SQRT2, f) == "0x1.6a09e667f3bcc908p+0");
+ assert(printFloat(SQRT1_2, f) == "0x1.6a09e667f3bcc908p-1");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.precision = 3;
+
+ assert(printFloat(1.0f, f) == "0x1.000p+0");
+ assert(printFloat(3.3f, f) == "0x1.a66p+1");
+ assert(printFloat(2.9f, f) == "0x1.733p+1");
+
+ assert(printFloat(1.0, f) == "0x1.000p+0");
+ assert(printFloat(3.3, f) == "0x1.a66p+1");
+ assert(printFloat(2.9, f) == "0x1.733p+1");
+
+ static if (real.mant_dig == 64)
+ {
+ assert(printFloat(1.0L, f) == "0x1.000p+0");
+ assert(printFloat(3.3L, f) == "0x1.a66p+1");
+ assert(printFloat(2.9L, f) == "0x1.733p+1");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.precision = 0;
+
+ assert(printFloat(1.0f, f) == "0x1p+0");
+ assert(printFloat(3.3f, f) == "0x2p+1");
+ assert(printFloat(2.9f, f) == "0x1p+1");
+
+ assert(printFloat(1.0, f) == "0x1p+0");
+ assert(printFloat(3.3, f) == "0x2p+1");
+ assert(printFloat(2.9, f) == "0x1p+1");
+
+ static if (real.mant_dig == 64)
+ {
+ assert(printFloat(1.0L, f) == "0x1p+0");
+ assert(printFloat(3.3L, f) == "0x2p+1");
+ assert(printFloat(2.9L, f) == "0x1p+1");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.precision = 0;
+ f.flHash = true;
+
+ assert(printFloat(1.0f, f) == "0x1.p+0");
+ assert(printFloat(3.3f, f) == "0x2.p+1");
+ assert(printFloat(2.9f, f) == "0x1.p+1");
+
+ assert(printFloat(1.0, f) == "0x1.p+0");
+ assert(printFloat(3.3, f) == "0x2.p+1");
+ assert(printFloat(2.9, f) == "0x1.p+1");
+
+ static if (real.mant_dig == 64)
+ {
+ assert(printFloat(1.0L, f) == "0x1.p+0");
+ assert(printFloat(3.3L, f) == "0x2.p+1");
+ assert(printFloat(2.9L, f) == "0x1.p+1");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.width = 22;
+
+ assert(printFloat(1.0f, f) == " 0x1p+0");
+ assert(printFloat(3.3f, f) == " 0x1.a66666p+1");
+ assert(printFloat(2.9f, f) == " 0x1.733334p+1");
+
+ assert(printFloat(1.0, f) == " 0x1p+0");
+ assert(printFloat(3.3, f) == " 0x1.a666666666666p+1");
+ assert(printFloat(2.9, f) == " 0x1.7333333333333p+1");
+
+ static if (real.mant_dig == 64)
+ {
+ f.width = 25;
+ assert(printFloat(1.0L, f) == " 0x1p+0");
+ assert(printFloat(3.3L, f) == " 0x1.a666666666666666p+1");
+ assert(printFloat(2.9L, f) == " 0x1.7333333333333334p+1");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.width = 22;
+ f.flDash = true;
+
+ assert(printFloat(1.0f, f) == "0x1p+0 ");
+ assert(printFloat(3.3f, f) == "0x1.a66666p+1 ");
+ assert(printFloat(2.9f, f) == "0x1.733334p+1 ");
+
+ assert(printFloat(1.0, f) == "0x1p+0 ");
+ assert(printFloat(3.3, f) == "0x1.a666666666666p+1 ");
+ assert(printFloat(2.9, f) == "0x1.7333333333333p+1 ");
+
+ static if (real.mant_dig == 64)
+ {
+ f.width = 25;
+ assert(printFloat(1.0L, f) == "0x1p+0 ");
+ assert(printFloat(3.3L, f) == "0x1.a666666666666666p+1 ");
+ assert(printFloat(2.9L, f) == "0x1.7333333333333334p+1 ");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.width = 22;
+ f.flZero = true;
+
+ assert(printFloat(1.0f, f) == "0x00000000000000001p+0");
+ assert(printFloat(3.3f, f) == "0x0000000001.a66666p+1");
+ assert(printFloat(2.9f, f) == "0x0000000001.733334p+1");
+
+ assert(printFloat(1.0, f) == "0x00000000000000001p+0");
+ assert(printFloat(3.3, f) == "0x001.a666666666666p+1");
+ assert(printFloat(2.9, f) == "0x001.7333333333333p+1");
+
+ static if (real.mant_dig == 64)
+ {
+ f.width = 25;
+ assert(printFloat(1.0L, f) == "0x00000000000000000001p+0");
+ assert(printFloat(3.3L, f) == "0x001.a666666666666666p+1");
+ assert(printFloat(2.9L, f) == "0x001.7333333333333334p+1");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.width = 22;
+ f.flPlus = true;
+
+ assert(printFloat(1.0f, f) == " +0x1p+0");
+ assert(printFloat(3.3f, f) == " +0x1.a66666p+1");
+ assert(printFloat(2.9f, f) == " +0x1.733334p+1");
+
+ assert(printFloat(1.0, f) == " +0x1p+0");
+ assert(printFloat(3.3, f) == " +0x1.a666666666666p+1");
+ assert(printFloat(2.9, f) == " +0x1.7333333333333p+1");
+
+ static if (real.mant_dig == 64)
+ {
+ f.width = 25;
+ assert(printFloat(1.0L, f) == " +0x1p+0");
+ assert(printFloat(3.3L, f) == " +0x1.a666666666666666p+1");
+ assert(printFloat(2.9L, f) == " +0x1.7333333333333334p+1");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.width = 22;
+ f.flDash = true;
+ f.flSpace = true;
+
+ assert(printFloat(1.0f, f) == " 0x1p+0 ");
+ assert(printFloat(3.3f, f) == " 0x1.a66666p+1 ");
+ assert(printFloat(2.9f, f) == " 0x1.733334p+1 ");
+
+ assert(printFloat(1.0, f) == " 0x1p+0 ");
+ assert(printFloat(3.3, f) == " 0x1.a666666666666p+1 ");
+ assert(printFloat(2.9, f) == " 0x1.7333333333333p+1 ");
+
+ static if (real.mant_dig == 64)
+ {
+ f.width = 25;
+ assert(printFloat(1.0L, f) == " 0x1p+0 ");
+ assert(printFloat(3.3L, f) == " 0x1.a666666666666666p+1 ");
+ assert(printFloat(2.9L, f) == " 0x1.7333333333333334p+1 ");
+ }
+}
+
+@safe unittest
+{
+ import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
+
+ // std.math's FloatingPointControl isn't available on all target platforms
+ static if (is(FloatingPointControl))
+ {
+ FloatingPointControl fpctrl;
+
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.precision = 1;
+
+ fpctrl.rounding = FloatingPointControl.roundToNearest;
+
+ /* tiesAwayFromZero currently not supported
+ assert(printFloat(0x1.18p0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.28p0, f) == "0x1.3p+0");
+ assert(printFloat(0x1.1ap0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.16p0, f) == "0x1.1p+0");
+ assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
+ assert(printFloat(-0x1.18p0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.28p0, f) == "-0x1.3p+0");
+ assert(printFloat(-0x1.1ap0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0");
+ assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
+ */
+
+ assert(printFloat(0x1.18p0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.28p0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.1ap0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.16p0, f) == "0x1.1p+0");
+ assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
+ assert(printFloat(-0x1.18p0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.28p0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.1ap0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0");
+ assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
+
+ fpctrl.rounding = FloatingPointControl.roundToZero;
+
+ assert(printFloat(0x1.18p0, f) == "0x1.1p+0");
+ assert(printFloat(0x1.28p0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.1ap0, f) == "0x1.1p+0");
+ assert(printFloat(0x1.16p0, f) == "0x1.1p+0");
+ assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
+ assert(printFloat(-0x1.18p0, f) == "-0x1.1p+0");
+ assert(printFloat(-0x1.28p0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.1ap0, f) == "-0x1.1p+0");
+ assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0");
+ assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+
+ assert(printFloat(0x1.18p0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.28p0, f) == "0x1.3p+0");
+ assert(printFloat(0x1.1ap0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.16p0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
+ assert(printFloat(-0x1.18p0, f) == "-0x1.1p+0");
+ assert(printFloat(-0x1.28p0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.1ap0, f) == "-0x1.1p+0");
+ assert(printFloat(-0x1.16p0, f) == "-0x1.1p+0");
+ assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
+
+ fpctrl.rounding = FloatingPointControl.roundDown;
+
+ assert(printFloat(0x1.18p0, f) == "0x1.1p+0");
+ assert(printFloat(0x1.28p0, f) == "0x1.2p+0");
+ assert(printFloat(0x1.1ap0, f) == "0x1.1p+0");
+ assert(printFloat(0x1.16p0, f) == "0x1.1p+0");
+ assert(printFloat(0x1.10p0, f) == "0x1.1p+0");
+ assert(printFloat(-0x1.18p0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.28p0, f) == "-0x1.3p+0");
+ assert(printFloat(-0x1.1ap0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.16p0, f) == "-0x1.2p+0");
+ assert(printFloat(-0x1.10p0, f) == "-0x1.1p+0");
+ }
+}
+
+// for 100% coverage
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ f.precision = 3;
+
+ assert(printFloat(0x1.19f81p0, f) == "0x1.1a0p+0");
+ assert(printFloat(0x1.19f01p0, f) == "0x1.19fp+0");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'A';
+ f.precision = 3;
+
+ assert(printFloat(0x1.19f81p0, f) == "0X1.1A0P+0");
+ assert(printFloat(0x1.19f01p0, f) == "0X1.19FP+0");
+}
+
+private void printFloatE(bool g, Writer, T, Char)(auto ref Writer w, T val,
+ FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper)
+if (is(T == float) || is(T == double)
+ || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
+{
+ import std.format.internal.write : writeAligned, PrecisionType, RoundingClass, round;
+
+ static if (!g)
+ {
+ if (f.precision == f.UNSPECIFIED)
+ f.precision = 6;
+ }
+
+ // special treatment for 0.0
+ if (mnt == 0)
+ {
+ static if (g)
+ writeAligned(w, sgn, "0", ".", "", f, PrecisionType.allDigits);
+ else
+ writeAligned(w, sgn, "0", ".", is_upper ? "E+00" : "e+00", f, PrecisionType.fractionalDigits);
+ return;
+ }
+
+ char[T.mant_dig + T.max_exp] dec_buf;
+ char[T.max_10_exp.stringof.length + 2] exp_buf;
+
+ int final_exp = 0;
+
+ RoundingClass rc;
+
+ // Depending on exp, we will use one of three algorithms:
+ //
+ // Algorithm A: For large exponents (exp >= T.mant_dig)
+ // Algorithm B: For small exponents (exp < T.mant_dig - 61)
+ // Algorithm C: For exponents close to 0.
+ //
+ // Algorithm A:
+ // The number to print looks like this: mantissa followed by several zeros.
+ //
+ // We know, that there is no fractional part, so we can just use integer division,
+ // consecutivly dividing by 10 and writing down the remainder from right to left.
+ // Unfortunately the integer is too large to fit in an ulong, so we use something
+ // like BigInt: An array of ulongs. We only use 60 bits of that ulongs, because
+ // this simplifies (and speeds up) the division to come.
+ //
+ // For the division we use integer division with reminder for each ulong and put
+ // the reminder of each step in the first 4 bits of ulong of the next step (think of
+ // long division for the rationale behind this). The final reminder is the next
+ // digit (from right to left).
+ //
+ // This results in the output we would have for the %f specifier. We now adjust this
+ // for %e: First we calculate the place, where the exponent should be printed, filling
+ // up with zeros if needed and second we move the leftmost digit one to the left
+ // and inserting a dot.
+ //
+ // After that we decide on the rounding type, using the digits right of the position,
+ // where the exponent will be printed (currently they are still there, but will be
+ // overwritten later).
+ //
+ // Algorithm B:
+ // The number to print looks like this: zero dot several zeros followed by the mantissa
+ //
+ // We know, that the number has no integer part. The algorithm consecutivly multiplies
+ // by 10. The integer part (rounded down) after the multiplication is the next digit
+ // (from left to right). This integer part is removed after each step.
+ // Again, the number is represented as an array of ulongs, with only 60 bits used of
+ // every ulong.
+ //
+ // For the multiplication we use normal integer multiplication, which can result in digits
+ // in the uppermost 4 bits. These 4 digits are the carry which is added to the result
+ // of the next multiplication and finally the last carry is the next digit.
+ //
+ // Other than for the %f specifier, this multiplication is splitted into two almost
+ // identical parts. The first part lasts as long as we find zeros. We need to do this
+ // to calculate the correct exponent.
+ //
+ // The second part will stop, when only zeros remain or when we've got enough digits
+ // for the requested precision. In the second case, we have to find out, which rounding
+ // we have. Aside from special cases we do this by calculating one more digit.
+ //
+ // Algorithm C:
+ // This time, we know, that the integral part and the fractional part each fit into a
+ // ulong. The mantissa might be partially in both parts or completely in the fractional
+ // part.
+ //
+ // We first calculate the integral part by consecutive division by 10. Depending on the
+ // precision this might result in more digits, than we need. In that case we calculate
+ // the position of the exponent and the rounding type.
+ //
+ // If there is no integral part, we need to find the first non zero digit. We do this by
+ // consecutive multiplication by 10, saving the first non zero digit followed by a dot.
+ //
+ // In either case, we continue filling up with the fractional part until we have enough
+ // digits. If still necessary, we decide the rounding type, mainly by looking at the
+ // next digit.
+
+ size_t right = 1;
+ size_t start = 1;
+ size_t left = 1;
+
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ enum small_bound = 0;
+ enum max_buf = 275;
+ }
+ else
+ {
+ enum small_bound = T.mant_dig - 61;
+ static if (is(T == float))
+ enum max_buf = 4;
+ else
+ enum max_buf = 18;
+ }
+
+ ulong[max_buf] bigbuf;
+ if (exp >= T.mant_dig)
+ {
+ start = left = right = dec_buf.length;
+
+ // large number without fractional digits
+ //
+ // As this number does not fit in a ulong, we use an array of ulongs. We only use 60 of the 64 bits,
+ // because this makes it much more easy to implement the division by 10.
+ int count = exp / 60 + 1;
+
+ // only the first few ulongs contain the mantiassa. The rest are zeros.
+ int lower = 60 - (exp - T.mant_dig + 1) % 60;
+
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ // for x87 reals, the lowest ulong may contain more than 60 bits,
+ // because the mantissa is 63 (>60) bits long
+ // therefore we need one ulong less
+ if (lower <= 3) count--;
+ }
+
+ // saved in big endian format
+ ulong[] mybig = bigbuf[0 .. count];
+
+ if (lower < T.mant_dig)
+ {
+ mybig[0] = mnt >> lower;
+ mybig[1] = (mnt & ((1L << lower) - 1)) << 60 - lower;
+ }
+ else
+ mybig[0] = (mnt & ((1L << lower) - 1)) << 60 - lower;
+
+ // Generation of digits by consecutive division with reminder by 10.
+ int msu = 0; // Most significant ulong; when it get's zero, we can ignore it further on
+ while (msu < count - 1 || mybig[$ - 1] != 0)
+ {
+ ulong mod = 0;
+ foreach (i;msu .. count)
+ {
+ mybig[i] |= mod << 60;
+ mod = mybig[i] % 10;
+ mybig[i] /= 10;
+ }
+ if (mybig[msu] == 0)
+ ++msu;
+
+ dec_buf[--left] = cast(byte) ('0' + mod);
+ ++final_exp;
+ }
+ --final_exp;
+
+ static if (g)
+ start = left + f.precision;
+ else
+ start = left + f.precision + 1;
+
+ // move leftmost digit one more left and add dot between
+ dec_buf[left - 1] = dec_buf[left];
+ dec_buf[left] = '.';
+ --left;
+
+ // rounding type
+ if (start >= right)
+ rc = RoundingClass.ZERO;
+ else if (dec_buf[start] != '0' && dec_buf[start] != '5')
+ rc = dec_buf[start] > '5' ? RoundingClass.UPPER : RoundingClass.LOWER;
+ else
+ {
+ rc = dec_buf[start] == '5' ? RoundingClass.FIVE : RoundingClass.ZERO;
+ foreach (i; start + 1 .. right)
+ if (dec_buf[i] > '0')
+ {
+ rc = rc == RoundingClass.FIVE ? RoundingClass.UPPER : RoundingClass.LOWER;
+ break;
+ }
+ }
+
+ if (start < right) right = start;
+ }
+ else if (exp < small_bound)
+ {
+ // small number without integer digits
+ //
+ // Again this number does not fit in a ulong and we use an array of ulongs. And again we
+ // only use 60 bits, because this simplifies the multiplication by 10.
+ int count = (T.mant_dig - exp - 2) / 60 + 1;
+
+ // saved in little endian format
+ ulong[] mybig = bigbuf[0 .. count];
+
+ // only the last few ulongs contain the mantiassa. Because of little endian
+ // format these are the ulongs at index 0 and 1 (and 2 in case of x87 reals).
+ // The rest are zeros.
+ int upper = 60 - (-exp - 1) % 60;
+
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ if (upper < 4)
+ {
+ mybig[0] = (mnt & ((1L << (4 - upper)) - 1)) << 56 + upper;
+ mybig[1] = (mnt >> (4 - upper)) & ((1L << 60) - 1);
+ mybig[2] = mnt >> 64 - upper;
+ }
+ else
+ {
+ mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper);
+ mybig[1] = mnt >> (T.mant_dig - upper);
+ }
+ }
+ else
+ {
+ if (upper < T.mant_dig)
+ {
+ mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper);
+ mybig[1] = mnt >> (T.mant_dig - upper);
+ }
+ else
+ mybig[0] = mnt << (upper - T.mant_dig);
+ }
+
+ int lsu = 0; // Least significant ulong; when it get's zero, we can ignore it further on
+
+ // adding zeros, until we reach first nonzero
+ while (lsu < count - 1 || mybig[$ - 1]!=0)
+ {
+ ulong over = 0;
+ foreach (i; lsu .. count)
+ {
+ mybig[i] = mybig[i] * 10 + over;
+ over = mybig[i] >> 60;
+ mybig[i] &= (1L << 60) - 1;
+ }
+ if (mybig[lsu] == 0)
+ ++lsu;
+ --final_exp;
+
+ if (over != 0)
+ {
+ dec_buf[right++] = cast(byte) ('0' + over);
+ dec_buf[right++] = '.';
+ break;
+ }
+ }
+
+ // adding more digits
+ static if (g)
+ start = right - 1;
+ else
+ start = right;
+ while ((lsu < count - 1 || mybig[$ - 1] != 0) && right - start < f.precision)
+ {
+ ulong over = 0;
+ foreach (i;lsu .. count)
+ {
+ mybig[i] = mybig[i] * 10 + over;
+ over = mybig[i] >> 60;
+ mybig[i] &= (1L << 60) - 1;
+ }
+ if (mybig[lsu] == 0)
+ ++lsu;
+
+ dec_buf[right++] = cast(byte) ('0' + over);
+ }
+
+ // rounding type
+ if (lsu >= count - 1 && mybig[count - 1] == 0)
+ rc = RoundingClass.ZERO;
+ else if (lsu == count - 1 && mybig[lsu] == 1L << 59)
+ rc = RoundingClass.FIVE;
+ else
+ {
+ ulong over = 0;
+ foreach (i;lsu .. count)
+ {
+ mybig[i] = mybig[i] * 10 + over;
+ over = mybig[i] >> 60;
+ mybig[i] &= (1L << 60) - 1;
+ }
+ rc = over >= 5 ? RoundingClass.UPPER : RoundingClass.LOWER;
+ }
+ }
+ else
+ {
+ // medium sized number, probably with integer and fractional digits
+ // this is fastest, because both parts fit into a ulong each
+ ulong int_part = mnt >> (T.mant_dig - 1 - exp);
+ ulong frac_part = mnt & ((1L << (T.mant_dig - 1 - exp)) - 1);
+
+ // for x87 reals the mantiassa might be up to 3 bits too long
+ // we need to save these bits as a tail and handle this separately
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ ulong tail = 0;
+ ulong tail_length = 0;
+ if (exp < 3)
+ {
+ tail = frac_part & ((1L << (3 - exp)) - 1);
+ tail_length = 3 - exp;
+ frac_part >>= 3 - exp;
+ exp = 3;
+ }
+ }
+
+ start = 0;
+
+ // could we already decide on the rounding mode in the integer part?
+ bool found = false;
+
+ if (int_part > 0)
+ {
+ import core.bitop : bsr;
+ left = right = int_part.bsr * 100 / 332 + 4;
+
+ // integer part, if there is something to print
+ while (int_part >= 10)
+ {
+ dec_buf[--left] = '0' + (int_part % 10);
+ int_part /= 10;
+ ++final_exp;
+ ++start;
+ }
+
+ dec_buf[--left] = '.';
+ dec_buf[--left] = cast(byte) ('0' + int_part);
+
+ static if (g)
+ auto limit = f.precision + 1;
+ else
+ auto limit = f.precision + 2;
+
+ if (right - left > limit)
+ {
+ auto old_right = right;
+ right = left + limit;
+
+ if (dec_buf[right] == '5' || dec_buf[right] == '0')
+ {
+ rc = dec_buf[right] == '5' ? RoundingClass.FIVE : RoundingClass.ZERO;
+ if (frac_part != 0)
+ rc = rc == RoundingClass.FIVE ? RoundingClass.UPPER : RoundingClass.LOWER;
+ else
+ foreach (i;right + 1 .. old_right)
+ if (dec_buf[i] > '0')
+ {
+ rc = rc == RoundingClass.FIVE ? RoundingClass.UPPER : RoundingClass.LOWER;
+ break;
+ }
+ }
+ else
+ rc = dec_buf[right] > '5' ? RoundingClass.UPPER : RoundingClass.LOWER;
+ found = true;
+ }
+ }
+ else
+ {
+ // fractional part, skipping leading zeros
+ while (frac_part != 0)
+ {
+ --final_exp;
+ frac_part *= 10;
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ if (tail_length > 0)
+ {
+ // together this is *= 10;
+ tail *= 5;
+ tail_length--;
+
+ frac_part += tail >> tail_length;
+ if (tail_length > 0)
+ tail &= (1L << tail_length) - 1;
+ }
+ }
+ auto tmp = frac_part >> (T.mant_dig - 1 - exp);
+ frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
+ if (tmp > 0)
+ {
+ dec_buf[right++] = cast(byte) ('0' + tmp);
+ dec_buf[right++] = '.';
+ break;
+ }
+ }
+
+ rc = RoundingClass.ZERO;
+ }
+
+ static if (g)
+ size_t limit = f.precision - 1;
+ else
+ size_t limit = f.precision;
+
+ // the fractional part after the zeros
+ while (frac_part != 0 && start < limit)
+ {
+ frac_part *= 10;
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ if (tail_length > 0)
+ {
+ // together this is *= 10;
+ tail *= 5;
+ tail_length--;
+
+ frac_part += tail >> tail_length;
+ if (tail_length > 0)
+ tail &= (1L << tail_length) - 1;
+ }
+ }
+ dec_buf[right++] = cast(byte) ('0' + (frac_part >> (T.mant_dig - 1 - exp)));
+ frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
+ ++start;
+ }
+
+ static if (g)
+ limit = right - left - 1;
+ else
+ limit = start;
+
+ // rounding mode, if not allready known
+ if (frac_part != 0 && !found)
+ {
+ frac_part *= 10;
+ auto nextDigit = frac_part >> (T.mant_dig - 1 - exp);
+ frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
+
+ if (nextDigit == 5 && frac_part == 0)
+ rc = RoundingClass.FIVE;
+ else if (nextDigit >= 5)
+ rc = RoundingClass.UPPER;
+ else
+ rc = RoundingClass.LOWER;
+ }
+ }
+
+ if (round(dec_buf, left, right, rc, sgn == "-"))
+ {
+ left--;
+ right--;
+ dec_buf[left + 2] = dec_buf[left + 1];
+ dec_buf[left + 1] = '.';
+ final_exp++;
+ }
+
+ // printing exponent
+ auto neg = final_exp < 0;
+ if (neg) final_exp = -final_exp;
+
+ size_t exp_pos = exp_buf.length;
+
+ do
+ {
+ exp_buf[--exp_pos] = '0' + final_exp%10;
+ final_exp /= 10;
+ } while (final_exp > 0);
+ if (exp_buf.length - exp_pos == 1)
+ exp_buf[--exp_pos] = '0';
+ exp_buf[--exp_pos] = neg ? '-' : '+';
+ exp_buf[--exp_pos] = is_upper ? 'E' : 'e';
+
+ while (right > left + 1 && dec_buf[right - 1] == '0') right--;
+
+ if (right == left + 1)
+ dec_buf[right++] = '.';
+
+ static if (g)
+ writeAligned(w, sgn, dec_buf[left .. left + 1], dec_buf[left + 1 .. right],
+ exp_buf[exp_pos .. $], f, PrecisionType.allDigits);
+ else
+ writeAligned(w, sgn, dec_buf[left .. left + 1], dec_buf[left + 1 .. right],
+ exp_buf[exp_pos .. $], f, PrecisionType.fractionalDigits);
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ assert(printFloat(float.nan, f) == "nan");
+ assert(printFloat(-float.nan, f) == "-nan");
+ assert(printFloat(float.infinity, f) == "inf");
+ assert(printFloat(-float.infinity, f) == "-inf");
+ assert(printFloat(0.0f, f) == "0.000000e+00");
+ assert(printFloat(-0.0f, f) == "-0.000000e+00");
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "9.999946e-41");
+ assert(printFloat(cast(float) -1e-40, f) == "-9.999946e-41");
+ assert(printFloat(1e-30f, f) == "1.000000e-30");
+ assert(printFloat(-1e-30f, f) == "-1.000000e-30");
+ assert(printFloat(1e-10f, f) == "1.000000e-10");
+ assert(printFloat(-1e-10f, f) == "-1.000000e-10");
+ assert(printFloat(0.1f, f) == "1.000000e-01");
+ assert(printFloat(-0.1f, f) == "-1.000000e-01");
+ assert(printFloat(10.0f, f) == "1.000000e+01");
+ assert(printFloat(-10.0f, f) == "-1.000000e+01");
+ assert(printFloat(1e30f, f) == "1.000000e+30");
+ assert(printFloat(-1e30f, f) == "-1.000000e+30");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "1.401298e-45");
+ assert(printFloat(nextDown(-0.0f), f) == "-1.401298e-45");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ f.width = 20;
+ f.precision = 10;
+
+ assert(printFloat(float.nan, f) == " nan");
+ assert(printFloat(-float.nan, f) == " -nan");
+ assert(printFloat(float.infinity, f) == " inf");
+ assert(printFloat(-float.infinity, f) == " -inf");
+ assert(printFloat(0.0f, f) == " 0.0000000000e+00");
+ assert(printFloat(-0.0f, f) == " -0.0000000000e+00");
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == " 9.9999461011e-41");
+ assert(printFloat(cast(float) -1e-40, f) == " -9.9999461011e-41");
+ assert(printFloat(1e-30f, f) == " 1.0000000032e-30");
+ assert(printFloat(-1e-30f, f) == " -1.0000000032e-30");
+ assert(printFloat(1e-10f, f) == " 1.0000000134e-10");
+ assert(printFloat(-1e-10f, f) == " -1.0000000134e-10");
+ assert(printFloat(0.1f, f) == " 1.0000000149e-01");
+ assert(printFloat(-0.1f, f) == " -1.0000000149e-01");
+ assert(printFloat(10.0f, f) == " 1.0000000000e+01");
+ assert(printFloat(-10.0f, f) == " -1.0000000000e+01");
+ assert(printFloat(1e30f, f) == " 1.0000000150e+30");
+ assert(printFloat(-1e30f, f) == " -1.0000000150e+30");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == " 1.4012984643e-45");
+ assert(printFloat(nextDown(-0.0f), f) == " -1.4012984643e-45");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ f.width = 20;
+ f.precision = 10;
+ f.flDash = true;
+
+ assert(printFloat(float.nan, f) == "nan ");
+ assert(printFloat(-float.nan, f) == "-nan ");
+ assert(printFloat(float.infinity, f) == "inf ");
+ assert(printFloat(-float.infinity, f) == "-inf ");
+ assert(printFloat(0.0f, f) == "0.0000000000e+00 ");
+ assert(printFloat(-0.0f, f) == "-0.0000000000e+00 ");
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "9.9999461011e-41 ");
+ assert(printFloat(cast(float) -1e-40, f) == "-9.9999461011e-41 ");
+ assert(printFloat(1e-30f, f) == "1.0000000032e-30 ");
+ assert(printFloat(-1e-30f, f) == "-1.0000000032e-30 ");
+ assert(printFloat(1e-10f, f) == "1.0000000134e-10 ");
+ assert(printFloat(-1e-10f, f) == "-1.0000000134e-10 ");
+ assert(printFloat(0.1f, f) == "1.0000000149e-01 ");
+ assert(printFloat(-0.1f, f) == "-1.0000000149e-01 ");
+ assert(printFloat(10.0f, f) == "1.0000000000e+01 ");
+ assert(printFloat(-10.0f, f) == "-1.0000000000e+01 ");
+ assert(printFloat(1e30f, f) == "1.0000000150e+30 ");
+ assert(printFloat(-1e30f, f) == "-1.0000000150e+30 ");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "1.4012984643e-45 ");
+ assert(printFloat(nextDown(-0.0f), f) == "-1.4012984643e-45 ");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ f.width = 20;
+ f.precision = 10;
+ f.flZero = true;
+
+ assert(printFloat(float.nan, f) == " nan");
+ assert(printFloat(-float.nan, f) == " -nan");
+ assert(printFloat(float.infinity, f) == " inf");
+ assert(printFloat(-float.infinity, f) == " -inf");
+ assert(printFloat(0.0f, f) == "00000.0000000000e+00");
+ assert(printFloat(-0.0f, f) == "-0000.0000000000e+00");
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "00009.9999461011e-41");
+ assert(printFloat(cast(float) -1e-40, f) == "-0009.9999461011e-41");
+ assert(printFloat(1e-30f, f) == "00001.0000000032e-30");
+ assert(printFloat(-1e-30f, f) == "-0001.0000000032e-30");
+ assert(printFloat(1e-10f, f) == "00001.0000000134e-10");
+ assert(printFloat(-1e-10f, f) == "-0001.0000000134e-10");
+ assert(printFloat(0.1f, f) == "00001.0000000149e-01");
+ assert(printFloat(-0.1f, f) == "-0001.0000000149e-01");
+ assert(printFloat(10.0f, f) == "00001.0000000000e+01");
+ assert(printFloat(-10.0f, f) == "-0001.0000000000e+01");
+ assert(printFloat(1e30f, f) == "00001.0000000150e+30");
+ assert(printFloat(-1e30f, f) == "-0001.0000000150e+30");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "00001.4012984643e-45");
+ assert(printFloat(nextDown(-0.0f), f) == "-0001.4012984643e-45");
+}
+
+@safe unittest
+{
+ import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
+
+ // std.math's FloatingPointControl isn't available on all target platforms
+ static if (is(FloatingPointControl))
+ {
+ FloatingPointControl fpctrl;
+
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ f.precision = 1;
+
+ fpctrl.rounding = FloatingPointControl.roundToNearest;
+
+ /*
+ assert(printFloat(11.5f, f) == "1.2e+01");
+ assert(printFloat(12.5f, f) == "1.3e+01");
+ assert(printFloat(11.7f, f) == "1.2e+01");
+ assert(printFloat(11.3f, f) == "1.1e+01");
+ assert(printFloat(11.0f, f) == "1.1e+01");
+ assert(printFloat(-11.5f, f) == "-1.2e+01");
+ assert(printFloat(-12.5f, f) == "-1.3e+01");
+ assert(printFloat(-11.7f, f) == "-1.2e+01");
+ assert(printFloat(-11.3f, f) == "-1.1e+01");
+ assert(printFloat(-11.0f, f) == "-1.1e+01");
+ */
+
+ assert(printFloat(11.5f, f) == "1.2e+01");
+ assert(printFloat(12.5f, f) == "1.2e+01");
+ assert(printFloat(11.7f, f) == "1.2e+01");
+ assert(printFloat(11.3f, f) == "1.1e+01");
+ assert(printFloat(11.0f, f) == "1.1e+01");
+ assert(printFloat(-11.5f, f) == "-1.2e+01");
+ assert(printFloat(-12.5f, f) == "-1.2e+01");
+ assert(printFloat(-11.7f, f) == "-1.2e+01");
+ assert(printFloat(-11.3f, f) == "-1.1e+01");
+ assert(printFloat(-11.0f, f) == "-1.1e+01");
+
+ fpctrl.rounding = FloatingPointControl.roundToZero;
+
+ assert(printFloat(11.5f, f) == "1.1e+01");
+ assert(printFloat(12.5f, f) == "1.2e+01");
+ assert(printFloat(11.7f, f) == "1.1e+01");
+ assert(printFloat(11.3f, f) == "1.1e+01");
+ assert(printFloat(11.0f, f) == "1.1e+01");
+ assert(printFloat(-11.5f, f) == "-1.1e+01");
+ assert(printFloat(-12.5f, f) == "-1.2e+01");
+ assert(printFloat(-11.7f, f) == "-1.1e+01");
+ assert(printFloat(-11.3f, f) == "-1.1e+01");
+ assert(printFloat(-11.0f, f) == "-1.1e+01");
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+
+ assert(printFloat(11.5f, f) == "1.2e+01");
+ assert(printFloat(12.5f, f) == "1.3e+01");
+ assert(printFloat(11.7f, f) == "1.2e+01");
+ assert(printFloat(11.3f, f) == "1.2e+01");
+ assert(printFloat(11.0f, f) == "1.1e+01");
+ assert(printFloat(-11.5f, f) == "-1.1e+01");
+ assert(printFloat(-12.5f, f) == "-1.2e+01");
+ assert(printFloat(-11.7f, f) == "-1.1e+01");
+ assert(printFloat(-11.3f, f) == "-1.1e+01");
+ assert(printFloat(-11.0f, f) == "-1.1e+01");
+
+ fpctrl.rounding = FloatingPointControl.roundDown;
+
+ assert(printFloat(11.5f, f) == "1.1e+01");
+ assert(printFloat(12.5f, f) == "1.2e+01");
+ assert(printFloat(11.7f, f) == "1.1e+01");
+ assert(printFloat(11.3f, f) == "1.1e+01");
+ assert(printFloat(11.0f, f) == "1.1e+01");
+ assert(printFloat(-11.5f, f) == "-1.2e+01");
+ assert(printFloat(-12.5f, f) == "-1.3e+01");
+ assert(printFloat(-11.7f, f) == "-1.2e+01");
+ assert(printFloat(-11.3f, f) == "-1.2e+01");
+ assert(printFloat(-11.0f, f) == "-1.1e+01");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ assert(printFloat(double.nan, f) == "nan");
+ assert(printFloat(-double.nan, f) == "-nan");
+ assert(printFloat(double.infinity, f) == "inf");
+ assert(printFloat(-double.infinity, f) == "-inf");
+ assert(printFloat(0.0, f) == "0.000000e+00");
+ assert(printFloat(-0.0, f) == "-0.000000e+00");
+ // / 1000 needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(1e-307 / 1000, f) == "1.000000e-310");
+ assert(printFloat(-1e-307 / 1000, f) == "-1.000000e-310");
+ assert(printFloat(1e-30, f) == "1.000000e-30");
+ assert(printFloat(-1e-30, f) == "-1.000000e-30");
+ assert(printFloat(1e-10, f) == "1.000000e-10");
+ assert(printFloat(-1e-10, f) == "-1.000000e-10");
+ assert(printFloat(0.1, f) == "1.000000e-01");
+ assert(printFloat(-0.1, f) == "-1.000000e-01");
+ assert(printFloat(10.0, f) == "1.000000e+01");
+ assert(printFloat(-10.0, f) == "-1.000000e+01");
+ assert(printFloat(1e300, f) == "1.000000e+300");
+ assert(printFloat(-1e300, f) == "-1.000000e+300");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0), f) == "4.940656e-324");
+ assert(printFloat(nextDown(-0.0), f) == "-4.940656e-324");
+}
+
+@safe unittest
+{
+ static if (real.mant_dig > 64)
+ {
+ pragma(msg, "printFloat tests disabled because of unsupported `real` format");
+ }
+ else
+ {
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ assert(printFloat(real.nan, f) == "nan");
+ assert(printFloat(-real.nan, f) == "-nan");
+ assert(printFloat(real.infinity, f) == "inf");
+ assert(printFloat(-real.infinity, f) == "-inf");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+
+ import std.math.operations : nextUp;
+
+ double eps = nextUp(0.0);
+ f.precision = 1000;
+ assert(printFloat(eps, f) ==
+ "4.9406564584124654417656879286822137236505980261432476442558568250067550727020875186529983636163599"
+ ~"23797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036"
+ ~"88718636056998730723050006387409153564984387312473397273169615140031715385398074126238565591171026"
+ ~"65855668676818703956031062493194527159149245532930545654440112748012970999954193198940908041656332"
+ ~"45247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431"
+ ~"93609238289345836806010601150616980975307834227731832924790498252473077637592724787465608477820373"
+ ~"44696995336470179726777175851256605511991315048911014510378627381672509558373897335989936648099411"
+ ~"64205702637090279242767544565229087538682506419718265533447265625000000000000000000000000000000000"
+ ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ ~"000000000000000000000e-324");
+
+ f.precision = 50;
+ assert(printFloat(double.max, f) ==
+ "1.79769313486231570814527423731704356798070567525845e+308");
+ assert(printFloat(double.epsilon, f) ==
+ "2.22044604925031308084726333618164062500000000000000e-16");
+
+ f.precision = 10;
+ assert(printFloat(1.0/3.0, f) == "3.3333333333e-01");
+ assert(printFloat(1.0/7.0, f) == "1.4285714286e-01");
+ assert(printFloat(1.0/9.0, f) == "1.1111111111e-01");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ f.precision = 15;
+
+ import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI,
+ LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2;
+
+ assert(printFloat(cast(double) E, f) == "2.718281828459045e+00");
+ assert(printFloat(cast(double) PI, f) == "3.141592653589793e+00");
+ assert(printFloat(cast(double) PI_2, f) == "1.570796326794897e+00");
+ assert(printFloat(cast(double) PI_4, f) == "7.853981633974483e-01");
+ assert(printFloat(cast(double) M_1_PI, f) == "3.183098861837907e-01");
+ assert(printFloat(cast(double) M_2_PI, f) == "6.366197723675814e-01");
+ assert(printFloat(cast(double) M_2_SQRTPI, f) == "1.128379167095513e+00");
+ assert(printFloat(cast(double) LN10, f) == "2.302585092994046e+00");
+ assert(printFloat(cast(double) LN2, f) == "6.931471805599453e-01");
+ assert(printFloat(cast(double) LOG2, f) == "3.010299956639812e-01");
+ assert(printFloat(cast(double) LOG2E, f) == "1.442695040888963e+00");
+ assert(printFloat(cast(double) LOG2T, f) == "3.321928094887362e+00");
+ assert(printFloat(cast(double) LOG10E, f) == "4.342944819032518e-01");
+ assert(printFloat(cast(double) SQRT2, f) == "1.414213562373095e+00");
+ assert(printFloat(cast(double) SQRT1_2, f) == "7.071067811865476e-01");
+}
+
+// for 100% coverage
+@safe unittest
+{
+ import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
+
+ auto f = FormatSpec!dchar("");
+ f.spec = 'E';
+ f.precision = 80;
+ assert(printFloat(5.62776e+12f, f) ==
+ "5.62775982080000000000000000000000000000000000000000000000000000000000000000000000E+12");
+
+ f.precision = 49;
+ assert(printFloat(2.5997869e-12f, f) ==
+ "2.5997869221999758693186777236405760049819946289062E-12");
+
+ f.precision = 6;
+ assert(printFloat(-1.1418613e+07f, f) == "-1.141861E+07");
+ assert(printFloat(-1.368281e+07f, f) == "-1.368281E+07");
+
+ f.precision = 1;
+ assert(printFloat(-245.666f, f) == "-2.5E+02");
+
+ static if (is(FloatingPointControl))
+ {
+ FloatingPointControl fpctrl;
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+
+ f.precision = 0;
+ assert(printFloat(709422.0f, f) == "8E+05");
+ }
+}
+
+@safe unittest
+{
+ static if (real.mant_dig > 64)
+ {
+ pragma(msg, "printFloat tests disabled because of unsupported `real` format");
+ }
+ else
+ {
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ assert(printFloat(real.nan, f) == "nan");
+ assert(printFloat(-real.nan, f) == "-nan");
+ assert(printFloat(real.infinity, f) == "inf");
+ assert(printFloat(-real.infinity, f) == "-inf");
+ assert(printFloat(0.0L, f) == "0.000000e+00");
+ assert(printFloat(-0.0L, f) == "-0.000000e+00");
+ }
+
+ static if (real.mant_dig == 64)
+ {
+ assert(printFloat(1e-4940L, f) == "1.000000e-4940");
+ assert(printFloat(-1e-4940L, f) == "-1.000000e-4940");
+ assert(printFloat(1e-30L, f) == "1.000000e-30");
+ assert(printFloat(-1e-30L, f) == "-1.000000e-30");
+ assert(printFloat(1e-10L, f) == "1.000000e-10");
+ assert(printFloat(-1e-10L, f) == "-1.000000e-10");
+ assert(printFloat(0.1L, f) == "1.000000e-01");
+ assert(printFloat(-0.1L, f) == "-1.000000e-01");
+ assert(printFloat(10.0L, f) == "1.000000e+01");
+ assert(printFloat(-10.0L, f) == "-1.000000e+01");
+ version (Windows) {} // issue 20972
+ else
+ {
+ assert(printFloat(1e4000L, f) == "1.000000e+4000");
+ assert(printFloat(-1e4000L, f) == "-1.000000e+4000");
+ }
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0L), f) == "3.645200e-4951");
+ assert(printFloat(nextDown(-0.0L), f) == "-3.645200e-4951");
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+ import std.math.exponential : log2;
+ import std.math.operations : nextDown;
+
+ assertCTFEable!(
+ {
+ // log2 is broken for x87-reals on some computers in CTFE
+ // the following tests excludes these computers from the tests
+ // (issue 21757)
+ enum test = cast(int) log2(3.05e2312L);
+ static if (real.mant_dig == 64 && test == 7681)
+ {
+ auto f = FormatSpec!dchar("");
+ f.spec = 'e';
+ assert(printFloat(real.infinity, f) == "inf");
+ assert(printFloat(10.0L, f) == "1.000000e+01");
+ assert(printFloat(2.6080L, f) == "2.608000e+00");
+ assert(printFloat(3.05e2312L, f) == "3.050000e+2312");
+
+ f.precision = 60;
+ assert(printFloat(2.65e-54L, f) ==
+ "2.650000000000000000059009987400547013941028940935296547599415e-54");
+
+ /*
+ commented out, because CTFE is currently too slow for 5000 digits with extreme values
+
+ f.precision = 5000;
+ auto result2 = printFloat(1.2119e-4822L, f);
+ assert(result2.length == 5008);
+ assert(result2[$ - 20 .. $] == "60729486595339e-4822");
+ auto result3 = printFloat(real.min_normal, f);
+ assert(result3.length == 5008);
+ assert(result3[$ - 20 .. $] == "20781410082267e-4932");
+ auto result4 = printFloat(real.min_normal.nextDown, f);
+ assert(result4.length == 5008);
+ assert(result4[$ - 20 .. $] == "81413263331006e-4932");
+ */
+ }
+ });
+}
+
+private void printFloatF(bool g, Writer, T, Char)(auto ref Writer w, T val,
+ FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper)
+if (is(T == float) || is(T == double)
+ || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
+{
+ import std.format.internal.write : writeAligned, PrecisionType, RoundingClass, round;
+
+ static if (!g)
+ {
+ if (f.precision == f.UNSPECIFIED)
+ f.precision = 6;
+ }
+
+ // special treatment for 0.0
+ if (exp == 0 && mnt == 0)
+ {
+ writeAligned(w, sgn, "0", ".", "", f, PrecisionType.fractionalDigits);
+ return;
+ }
+
+ char[T.max_exp + T.mant_dig + 1] dec_buf;
+
+ RoundingClass rc;
+
+ // Depending on exp, we will use one of three algorithms:
+ //
+ // Algorithm A: For large exponents (exp >= T.mant_dig)
+ // Algorithm B: For small exponents (exp < T.mant_dig - 61)
+ // Algorithm C: For exponents close to 0.
+ //
+ // Algorithm A:
+ // The number to print looks like this: mantissa followed by several zeros.
+ //
+ // We know, that there is no fractional part, so we can just use integer division,
+ // consecutivly dividing by 10 and writing down the remainder from right to left.
+ // Unfortunately the integer is too large to fit in an ulong, so we use something
+ // like BigInt: An array of ulongs. We only use 60 bits of that ulongs, because
+ // this simplifies (and speeds up) the division to come.
+ //
+ // For the division we use integer division with reminder for each ulong and put
+ // the reminder of each step in the first 4 bits of ulong of the next step (think of
+ // long division for the rationale behind this). The final reminder is the next
+ // digit (from right to left).
+ //
+ // Algorithm B:
+ // The number to print looks like this: zero dot several zeros followed by the mantissa
+ //
+ // We know, that the number has no integer part. The algorithm consecutivly multiplies
+ // by 10. The integer part (rounded down) after the multiplication is the next digit
+ // (from left to right). This integer part is removed after each step.
+ // Again, the number is represented as an array of ulongs, with only 60 bits used of
+ // every ulong.
+ //
+ // For the multiplication we use normal integer multiplication, which can result in digits
+ // in the uppermost 4 bits. These 4 digits are the carry which is added to the result
+ // of the next multiplication and finally the last carry is the next digit.
+ //
+ // The calculation will stop, when only zeros remain or when we've got enough digits
+ // for the requested precision. In the second case, we have to find out, which rounding
+ // we have. Aside from special cases we do this by calculating one more digit.
+ //
+ // Algorithm C:
+ // This time, we know, that the integral part and the fractional part each fit into a
+ // ulong. The mantissa might be partially in both parts or completely in the fractional
+ // part.
+ //
+ // We first calculate the integral part by consecutive division by 10. Then we calculate
+ // the fractional part by consecutive multiplication by 10. Again only until we have enough
+ // digits. Finally, we decide the rounding type, mainly by looking at the next digit.
+
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ enum small_bound = 0;
+ enum max_buf = 275;
+ }
+ else
+ {
+ enum small_bound = T.mant_dig - 61;
+ static if (is(T == float))
+ enum max_buf = 4;
+ else
+ enum max_buf = 18;
+ }
+
+ size_t start = 2;
+ size_t left = 2;
+ size_t right = 2;
+
+ ulong[max_buf] bigbuf;
+ if (exp >= T.mant_dig)
+ {
+ left = start = dec_buf.length - 1;
+ right = dec_buf.length;
+ dec_buf[start] = '.';
+
+ // large number without fractional digits
+ //
+ // As this number does not fit in a ulong, we use an array of ulongs. We only use 60 of the 64 bits,
+ // because this makes it much more easy to implement the division by 10.
+ int count = exp / 60 + 1;
+
+ // only the first few ulongs contain the mantiassa. The rest are zeros.
+ int lower = 60 - (exp - T.mant_dig + 1) % 60;
+
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ // for x87 reals, the lowest ulong may contain more than 60 bits,
+ // because the mantissa is 63 (>60) bits long
+ // therefore we need one ulong less
+ if (lower <= 3) count--;
+ }
+
+ // saved in big endian format
+ ulong[] mybig = bigbuf[0 .. count];
+
+ if (lower < T.mant_dig)
+ {
+ mybig[0] = mnt >> lower;
+ mybig[1] = (mnt & ((1L << lower) - 1)) << 60 - lower;
+ }
+ else
+ mybig[0] = (mnt & ((1L << lower) - 1)) << 60 - lower;
+
+ // Generation of digits by consecutive division with reminder by 10.
+ int msu = 0; // Most significant ulong; when it get's zero, we can ignore it furtheron
+ while (msu < count - 1 || mybig[$ - 1] != 0)
+ {
+ ulong mod = 0;
+ foreach (i;msu .. count)
+ {
+ mybig[i] |= mod << 60;
+ mod = mybig[i] % 10;
+ mybig[i] /= 10;
+ }
+ if (mybig[msu] == 0)
+ ++msu;
+
+ dec_buf[--left] = cast(byte) ('0' + mod);
+ }
+
+ rc = RoundingClass.ZERO;
+ }
+ else if (exp < small_bound)
+ {
+ // small number without integer digits
+ //
+ // Again this number does not fit in a ulong and we use an array of ulongs. And again we
+ // only use 60 bits, because this simplifies the multiplication by 10.
+ int count = (T.mant_dig - exp - 2) / 60 + 1;
+
+ // saved in little endian format
+ ulong[] mybig = bigbuf[0 .. count];
+
+ // only the last few ulongs contain the mantiassa. Because of little endian
+ // format these are the ulongs at index 0 and 1 (and 2 in case of x87 reals).
+ // The rest are zeros.
+ int upper = 60 - (-exp - 1) % 60;
+
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ if (upper < 4)
+ {
+ mybig[0] = (mnt & ((1L << (4 - upper)) - 1)) << 56 + upper;
+ mybig[1] = (mnt >> (4 - upper)) & ((1L << 60) - 1);
+ mybig[2] = mnt >> 64 - upper;
+ }
+ else
+ {
+ mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper);
+ mybig[1] = mnt >> (T.mant_dig - upper);
+ }
+ }
+ else
+ {
+ if (upper < T.mant_dig)
+ {
+ mybig[0] = (mnt & ((1L << (T.mant_dig - upper)) - 1)) << 60 - (T.mant_dig - upper);
+ mybig[1] = mnt >> (T.mant_dig - upper);
+ }
+ else
+ mybig[0] = mnt << (upper - T.mant_dig);
+ }
+
+ dec_buf[--left] = '0'; // 0 left of the dot
+ dec_buf[right++] = '.';
+
+ static if (g)
+ {
+ // precision starts at first non zero, so we move start
+ // to the right, until we found first non zero, thus avoiding
+ // a premature break of the loop
+ bool found = false;
+ start = left + 1;
+ }
+
+ // Generation of digits by consecutive multiplication by 10.
+ int lsu = 0; // Least significant ulong; when it get's zero, we can ignore it furtheron
+ while ((lsu < count - 1 || mybig[$ - 1] != 0) && right - start - 1 < f.precision)
+ {
+ ulong over = 0;
+ foreach (i;lsu .. count)
+ {
+ mybig[i] = mybig[i] * 10 + over;
+ over = mybig[i] >> 60;
+ mybig[i] &= (1L << 60) - 1;
+ }
+ if (mybig[lsu] == 0)
+ ++lsu;
+
+ dec_buf[right++] = cast(byte) ('0' + over);
+
+ static if (g)
+ {
+ if (dec_buf[right - 1] != '0')
+ found = true;
+ else if (!found)
+ start++;
+ }
+ }
+
+ static if (g) start = 2;
+
+ if (lsu >= count - 1 && mybig[count - 1] == 0)
+ rc = RoundingClass.ZERO;
+ else if (lsu == count - 1 && mybig[lsu] == 1L << 59)
+ rc = RoundingClass.FIVE;
+ else
+ {
+ ulong over = 0;
+ foreach (i;lsu .. count)
+ {
+ mybig[i] = mybig[i] * 10 + over;
+ over = mybig[i] >> 60;
+ mybig[i] &= (1L << 60) - 1;
+ }
+ rc = over >= 5 ? RoundingClass.UPPER : RoundingClass.LOWER;
+ }
+ }
+ else
+ {
+ // medium sized number, probably with integer and fractional digits
+ // this is fastest, because both parts fit into a ulong each
+ ulong int_part = mnt >> (T.mant_dig - 1 - exp);
+ ulong frac_part = mnt & ((1L << (T.mant_dig - 1 - exp)) - 1);
+
+ // for x87 reals the mantiassa might be up to 3 bits too long
+ // we need to save these bits as a tail and handle this separately
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ ulong tail = 0;
+ ulong tail_length = 0;
+ if (exp < 3)
+ {
+ tail = frac_part & ((1L << (3 - exp)) - 1);
+ tail_length = 3 - exp;
+ frac_part >>= 3 - exp;
+ exp = 3;
+ }
+ }
+
+ static if (g) auto found = int_part > 0; // searching first non zero
+
+ // creating int part
+ if (int_part == 0)
+ dec_buf[--left] = '0';
+ else
+ {
+ import core.bitop : bsr;
+ left = right = start = int_part.bsr * 100 / 332 + 4;
+
+ while (int_part > 0)
+ {
+ dec_buf[--left] = '0' + (int_part % 10);
+ int_part /= 10;
+ }
+ }
+
+ static if (g) size_t save_start = right;
+
+ dec_buf[right++] = '.';
+
+ // creating frac part
+ static if (g) start = left + (found ? 0 : 1);
+ while (frac_part != 0 && right - start - 1 < f.precision)
+ {
+ frac_part *= 10;
+ static if (is(T == real) && real.mant_dig == 64)
+ {
+ if (tail_length > 0)
+ {
+ // together this is *= 10;
+ tail *= 5;
+ tail_length--;
+
+ frac_part += tail >> tail_length;
+ if (tail_length > 0)
+ tail &= (1L << tail_length) - 1;
+ }
+ }
+ dec_buf[right++] = cast(byte)('0' + (frac_part >> (T.mant_dig - 1 - exp)));
+
+ static if (g)
+ {
+ if (dec_buf[right - 1] != '0')
+ found = true;
+ else if (!found)
+ start++;
+ }
+
+ frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
+ }
+
+ static if (g) start = save_start;
+
+ if (frac_part == 0)
+ rc = RoundingClass.ZERO;
+ else
+ {
+ frac_part *= 10;
+ auto nextDigit = frac_part >> (T.mant_dig - 1 - exp);
+ frac_part &= ((1L << (T.mant_dig - 1 - exp)) - 1);
+
+ if (nextDigit == 5 && frac_part == 0)
+ rc = RoundingClass.FIVE;
+ else if (nextDigit >= 5)
+ rc = RoundingClass.UPPER;
+ else
+ rc = RoundingClass.LOWER;
+ }
+ }
+
+ if (round(dec_buf, left, right, rc, sgn == "-")) left--;
+
+ while (right > start + 1 && dec_buf[right - 1] == '0') right--;
+
+ static if (g)
+ writeAligned(w, sgn, dec_buf[left .. start], dec_buf[start .. right], "", f, PrecisionType.allDigits);
+ else
+ writeAligned(w, sgn, dec_buf[left .. start], dec_buf[start .. right], "", f, PrecisionType.fractionalDigits);
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ assert(printFloat(float.nan, f) == "nan");
+ assert(printFloat(-float.nan, f) == "-nan");
+ assert(printFloat(float.infinity, f) == "inf");
+ assert(printFloat(-float.infinity, f) == "-inf");
+ assert(printFloat(0.0f, f) == "0.000000");
+ assert(printFloat(-0.0f, f) == "-0.000000");
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "0.000000");
+ assert(printFloat(cast(float) -1e-40, f) == "-0.000000");
+ assert(printFloat(1e-30f, f) == "0.000000");
+ assert(printFloat(-1e-30f, f) == "-0.000000");
+ assert(printFloat(1e-10f, f) == "0.000000");
+ assert(printFloat(-1e-10f, f) == "-0.000000");
+ assert(printFloat(0.1f, f) == "0.100000");
+ assert(printFloat(-0.1f, f) == "-0.100000");
+ assert(printFloat(10.0f, f) == "10.000000");
+ assert(printFloat(-10.0f, f) == "-10.000000");
+ assert(printFloat(1e30f, f) == "1000000015047466219876688855040.000000");
+ assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.000000");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "0.000000");
+ assert(printFloat(nextDown(-0.0f), f) == "-0.000000");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ f.width = 20;
+ f.precision = 10;
+
+ assert(printFloat(float.nan, f) == " nan");
+ assert(printFloat(-float.nan, f) == " -nan");
+ assert(printFloat(float.infinity, f) == " inf");
+ assert(printFloat(-float.infinity, f) == " -inf");
+ assert(printFloat(0.0f, f) == " 0.0000000000");
+ assert(printFloat(-0.0f, f) == " -0.0000000000");
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == " 0.0000000000");
+ assert(printFloat(cast(float) -1e-40, f) == " -0.0000000000");
+ assert(printFloat(1e-30f, f) == " 0.0000000000");
+ assert(printFloat(-1e-30f, f) == " -0.0000000000");
+ assert(printFloat(1e-10f, f) == " 0.0000000001");
+ assert(printFloat(-1e-10f, f) == " -0.0000000001");
+ assert(printFloat(0.1f, f) == " 0.1000000015");
+ assert(printFloat(-0.1f, f) == " -0.1000000015");
+ assert(printFloat(10.0f, f) == " 10.0000000000");
+ assert(printFloat(-10.0f, f) == " -10.0000000000");
+ assert(printFloat(1e30f, f) == "1000000015047466219876688855040.0000000000");
+ assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.0000000000");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == " 0.0000000000");
+ assert(printFloat(nextDown(-0.0f), f) == " -0.0000000000");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ f.width = 20;
+ f.precision = 10;
+ f.flDash = true;
+
+ assert(printFloat(float.nan, f) == "nan ");
+ assert(printFloat(-float.nan, f) == "-nan ");
+ assert(printFloat(float.infinity, f) == "inf ");
+ assert(printFloat(-float.infinity, f) == "-inf ");
+ assert(printFloat(0.0f, f) == "0.0000000000 ");
+ assert(printFloat(-0.0f, f) == "-0.0000000000 ");
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "0.0000000000 ");
+ assert(printFloat(cast(float) -1e-40, f) == "-0.0000000000 ");
+ assert(printFloat(1e-30f, f) == "0.0000000000 ");
+ assert(printFloat(-1e-30f, f) == "-0.0000000000 ");
+ assert(printFloat(1e-10f, f) == "0.0000000001 ");
+ assert(printFloat(-1e-10f, f) == "-0.0000000001 ");
+ assert(printFloat(0.1f, f) == "0.1000000015 ");
+ assert(printFloat(-0.1f, f) == "-0.1000000015 ");
+ assert(printFloat(10.0f, f) == "10.0000000000 ");
+ assert(printFloat(-10.0f, f) == "-10.0000000000 ");
+ assert(printFloat(1e30f, f) == "1000000015047466219876688855040.0000000000");
+ assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.0000000000");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "0.0000000000 ");
+ assert(printFloat(nextDown(-0.0f), f) == "-0.0000000000 ");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ f.width = 20;
+ f.precision = 10;
+ f.flZero = true;
+
+ assert(printFloat(float.nan, f) == " nan");
+ assert(printFloat(-float.nan, f) == " -nan");
+ assert(printFloat(float.infinity, f) == " inf");
+ assert(printFloat(-float.infinity, f) == " -inf");
+ assert(printFloat(0.0f, f) == "000000000.0000000000");
+ assert(printFloat(-0.0f, f) == "-00000000.0000000000");
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "000000000.0000000000");
+ assert(printFloat(cast(float) -1e-40, f) == "-00000000.0000000000");
+ assert(printFloat(1e-30f, f) == "000000000.0000000000");
+ assert(printFloat(-1e-30f, f) == "-00000000.0000000000");
+ assert(printFloat(1e-10f, f) == "000000000.0000000001");
+ assert(printFloat(-1e-10f, f) == "-00000000.0000000001");
+ assert(printFloat(0.1f, f) == "000000000.1000000015");
+ assert(printFloat(-0.1f, f) == "-00000000.1000000015");
+ assert(printFloat(10.0f, f) == "000000010.0000000000");
+ assert(printFloat(-10.0f, f) == "-00000010.0000000000");
+ assert(printFloat(1e30f, f) == "1000000015047466219876688855040.0000000000");
+ assert(printFloat(-1e30f, f) == "-1000000015047466219876688855040.0000000000");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "000000000.0000000000");
+ assert(printFloat(nextDown(-0.0f), f) == "-00000000.0000000000");
+}
+
+@safe unittest
+{
+ import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
+
+ // std.math's FloatingPointControl isn't available on all target platforms
+ static if (is(FloatingPointControl))
+ {
+ FloatingPointControl fpctrl;
+
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ f.precision = 0;
+
+ fpctrl.rounding = FloatingPointControl.roundToNearest;
+
+ /*
+ assert(printFloat(11.5f, f) == "12");
+ assert(printFloat(12.5f, f) == "13");
+ assert(printFloat(11.7f, f) == "12");
+ assert(printFloat(11.3f, f) == "11");
+ assert(printFloat(11.0f, f) == "11");
+ assert(printFloat(-11.5f, f) == "-12");
+ assert(printFloat(-12.5f, f) == "-13");
+ assert(printFloat(-11.7f, f) == "-12");
+ assert(printFloat(-11.3f, f) == "-11");
+ assert(printFloat(-11.0f, f) == "-11");
+ */
+
+ assert(printFloat(11.5f, f) == "12");
+ assert(printFloat(12.5f, f) == "12");
+ assert(printFloat(11.7f, f) == "12");
+ assert(printFloat(11.3f, f) == "11");
+ assert(printFloat(11.0f, f) == "11");
+ assert(printFloat(-11.5f, f) == "-12");
+ assert(printFloat(-12.5f, f) == "-12");
+ assert(printFloat(-11.7f, f) == "-12");
+ assert(printFloat(-11.3f, f) == "-11");
+ assert(printFloat(-11.0f, f) == "-11");
+
+ fpctrl.rounding = FloatingPointControl.roundToZero;
+
+ assert(printFloat(11.5f, f) == "11");
+ assert(printFloat(12.5f, f) == "12");
+ assert(printFloat(11.7f, f) == "11");
+ assert(printFloat(11.3f, f) == "11");
+ assert(printFloat(11.0f, f) == "11");
+ assert(printFloat(-11.5f, f) == "-11");
+ assert(printFloat(-12.5f, f) == "-12");
+ assert(printFloat(-11.7f, f) == "-11");
+ assert(printFloat(-11.3f, f) == "-11");
+ assert(printFloat(-11.0f, f) == "-11");
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+
+ assert(printFloat(11.5f, f) == "12");
+ assert(printFloat(12.5f, f) == "13");
+ assert(printFloat(11.7f, f) == "12");
+ assert(printFloat(11.3f, f) == "12");
+ assert(printFloat(11.0f, f) == "11");
+ assert(printFloat(-11.5f, f) == "-11");
+ assert(printFloat(-12.5f, f) == "-12");
+ assert(printFloat(-11.7f, f) == "-11");
+ assert(printFloat(-11.3f, f) == "-11");
+ assert(printFloat(-11.0f, f) == "-11");
+
+ fpctrl.rounding = FloatingPointControl.roundDown;
+
+ assert(printFloat(11.5f, f) == "11");
+ assert(printFloat(12.5f, f) == "12");
+ assert(printFloat(11.7f, f) == "11");
+ assert(printFloat(11.3f, f) == "11");
+ assert(printFloat(11.0f, f) == "11");
+ assert(printFloat(-11.5f, f) == "-12");
+ assert(printFloat(-12.5f, f) == "-13");
+ assert(printFloat(-11.7f, f) == "-12");
+ assert(printFloat(-11.3f, f) == "-12");
+ assert(printFloat(-11.0f, f) == "-11");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ assert(printFloat(double.nan, f) == "nan");
+ assert(printFloat(-double.nan, f) == "-nan");
+ assert(printFloat(double.infinity, f) == "inf");
+ assert(printFloat(-double.infinity, f) == "-inf");
+ assert(printFloat(0.0, f) == "0.000000");
+ assert(printFloat(-0.0, f) == "-0.000000");
+ // / 1000 needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(1e-307 / 1000, f) == "0.000000");
+ assert(printFloat(-1e-307 / 1000, f) == "-0.000000");
+ assert(printFloat(1e-30, f) == "0.000000");
+ assert(printFloat(-1e-30, f) == "-0.000000");
+ assert(printFloat(1e-10, f) == "0.000000");
+ assert(printFloat(-1e-10, f) == "-0.000000");
+ assert(printFloat(0.1, f) == "0.100000");
+ assert(printFloat(-0.1, f) == "-0.100000");
+ assert(printFloat(10.0, f) == "10.000000");
+ assert(printFloat(-10.0, f) == "-10.000000");
+ assert(printFloat(1e300, f) ==
+ "100000000000000005250476025520442024870446858110815915491585411551180245798890819578637137508044786"
+ ~"404370444383288387817694252323536043057564479218478670698284838720092657580373783023379478809005936"
+ ~"895323497079994508111903896764088007465274278014249457925878882005684283811566947219638686545940054"
+ ~"0160.000000");
+ assert(printFloat(-1e300, f) ==
+ "-100000000000000005250476025520442024870446858110815915491585411551180245798890819578637137508044786"
+ ~"404370444383288387817694252323536043057564479218478670698284838720092657580373783023379478809005936"
+ ~"895323497079994508111903896764088007465274278014249457925878882005684283811566947219638686545940054"
+ ~"0160.000000");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0), f) == "0.000000");
+ assert(printFloat(nextDown(-0.0), f) == "-0.000000");
+}
+
+@safe unittest
+{
+ static if (real.mant_dig > 64)
+ {
+ pragma(msg, "printFloat tests disabled because of unsupported `real` format");
+ }
+ else
+ {
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ assert(printFloat(real.nan, f) == "nan");
+ assert(printFloat(-real.nan, f) == "-nan");
+ assert(printFloat(real.infinity, f) == "inf");
+ assert(printFloat(-real.infinity, f) == "-inf");
+ assert(printFloat(0.0L, f) == "0.000000");
+ assert(printFloat(-0.0L, f) == "-0.000000");
+ }
+
+ static if (real.mant_dig == 64)
+ {
+ assert(printFloat(1e-4940L, f) == "0.000000");
+ assert(printFloat(-1e-4940L, f) == "-0.000000");
+ assert(printFloat(1e-30L, f) == "0.000000");
+ assert(printFloat(-1e-30L, f) == "-0.000000");
+ assert(printFloat(1e-10L, f) == "0.000000");
+ assert(printFloat(-1e-10L, f) == "-0.000000");
+ assert(printFloat(0.1L, f) == "0.100000");
+ assert(printFloat(-0.1L, f) == "-0.100000");
+ assert(printFloat(10.0L, f) == "10.000000");
+ assert(printFloat(-10.0L, f) == "-10.000000");
+ version (Windows) {} // issue 20972
+ else
+ {
+ auto result1 = printFloat(1e4000L, f);
+ assert(result1.length == 4007 && result1[0 .. 40] == "9999999999999999999965463873099623784932");
+ auto result2 = printFloat(-1e4000L, f);
+ assert(result2.length == 4008 && result2[0 .. 40] == "-999999999999999999996546387309962378493");
+ }
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0L), f) == "0.000000");
+ assert(printFloat(nextDown(-0.0L), f) == "-0.000000");
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+ import std.math.exponential : log2;
+ import std.math.operations : nextDown;
+
+ assertCTFEable!(
+ {
+ // log2 is broken for x87-reals on some computers in CTFE
+ // the following tests excludes these computers from the tests
+ // (issue 21757)
+ enum test = cast(int) log2(3.05e2312L);
+ static if (real.mant_dig == 64 && test == 7681)
+ {
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ assert(printFloat(real.infinity, f) == "inf");
+ assert(printFloat(10.0L, f) == "10.000000");
+ assert(printFloat(2.6080L, f) == "2.608000");
+ auto result1 = printFloat(3.05e2312L, f);
+ assert(result1.length == 2320);
+ assert(result1[0 .. 20] == "30499999999999999999");
+
+ f.precision = 60;
+ assert(printFloat(2.65e-54L, f) ==
+ "0.000000000000000000000000000000000000000000000000000002650000");
+
+ /*
+ commented out, because CTFE is currently too slow for 5000 digits with extreme values
+
+ f.precision = 5000;
+ auto result2 = printFloat(1.2119e-4822L, f);
+ assert(result2.length == 5002);
+ assert(result2[$ - 20 .. $] == "60076763752233836613");
+ auto result3 = printFloat(real.min_normal, f);
+ assert(result3.length == 5002);
+ assert(result3[$ - 20 .. $] == "47124010882722980874");
+ auto result4 = printFloat(real.min_normal.nextDown, f);
+ assert(result4.length == 5002);
+ assert(result4[$ - 20 .. $] == "52925846892214823939");
+ */
+ }
+ });
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+
+ import std.math.operations : nextUp;
+
+ double eps = nextUp(0.0);
+ f.precision = 1000;
+ assert(printFloat(eps, f) ==
+ "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ ~"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ ~"00000000000000000000000000000049406564584124654417656879286822137236505980261432476442558568250067"
+ ~"55072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131"
+ ~"90311404527845817167848982103688718636056998730723050006387409153564984387312473397273169615140031"
+ ~"71538539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748012"
+ ~"97099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107"
+ ~"49170333222684475333572083243193609238289345836806010601150616980975307834227731832924790498252473"
+ ~"07763759272478746560847782037344696995336470179726777175851256605511991315048911014510378627381672"
+ ~"509558373897335989937");
+
+ f.precision = 0;
+ assert(printFloat(double.max, f) ==
+ "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878"
+ ~"17154045895351438246423432132688946418276846754670353751698604991057655128207624549009038932894407"
+ ~"58685084551339423045832369032229481658085593321233482747978262041447231687381771809192998812504040"
+ ~"26184124858368");
+
+ f.precision = 50;
+ assert(printFloat(double.epsilon, f) ==
+ "0.00000000000000022204460492503130808472633361816406");
+
+ f.precision = 10;
+ assert(printFloat(1.0/3.0, f) == "0.3333333333");
+ assert(printFloat(1.0/7.0, f) == "0.1428571429");
+ assert(printFloat(1.0/9.0, f) == "0.1111111111");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ f.precision = 15;
+
+ import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI,
+ LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2;
+
+ assert(printFloat(cast(double) E, f) == "2.718281828459045");
+ assert(printFloat(cast(double) PI, f) == "3.141592653589793");
+ assert(printFloat(cast(double) PI_2, f) == "1.570796326794897");
+ assert(printFloat(cast(double) PI_4, f) == "0.785398163397448");
+ assert(printFloat(cast(double) M_1_PI, f) == "0.318309886183791");
+ assert(printFloat(cast(double) M_2_PI, f) == "0.636619772367581");
+ assert(printFloat(cast(double) M_2_SQRTPI, f) == "1.128379167095513");
+ assert(printFloat(cast(double) LN10, f) == "2.302585092994046");
+ assert(printFloat(cast(double) LN2, f) == "0.693147180559945");
+ assert(printFloat(cast(double) LOG2, f) == "0.301029995663981");
+ assert(printFloat(cast(double) LOG2E, f) == "1.442695040888963");
+ assert(printFloat(cast(double) LOG2T, f) == "3.321928094887362");
+ assert(printFloat(cast(double) LOG10E, f) == "0.434294481903252");
+ assert(printFloat(cast(double) SQRT2, f) == "1.414213562373095");
+ assert(printFloat(cast(double) SQRT1_2, f) == "0.707106781186548");
+}
+
+// for 100% coverage
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'f';
+ f.precision = 1;
+ assert(printFloat(9.99, f) == "10.0");
+
+ import std.math.operations : nextUp;
+
+ float eps = nextUp(0.0f);
+
+ f.precision = 148;
+ assert(printFloat(eps, f) ==
+ "0.0000000000000000000000000000000000000000000014012984643248170709237295832899161312802619418765157"
+ ~"717570682838897910826858606014866381883621215820312");
+
+ f.precision = 149;
+ assert(printFloat(eps, f) ==
+ "0.0000000000000000000000000000000000000000000014012984643248170709237295832899161312802619418765157"
+ ~"7175706828388979108268586060148663818836212158203125");
+}
+
+private void printFloatG(Writer, T, Char)(auto ref Writer w, T val,
+ FormatSpec!Char f, string sgn, int exp, ulong mnt, bool is_upper)
+if (is(T == float) || is(T == double)
+ || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
+{
+ import core.math : abs = fabs;
+
+ if (f.precision == f.UNSPECIFIED)
+ f.precision = 6;
+
+ if (f.precision == 0)
+ f.precision = 1;
+
+ import std.math.hardware;
+ import std.format.internal.write : RoundingMode;
+
+ auto rm = RoundingMode.toNearestTiesToEven;
+
+ if (!__ctfe)
+ {
+ // std.math's FloatingPointControl isn't available on all target platforms
+ static if (is(FloatingPointControl))
+ {
+ switch (FloatingPointControl.rounding)
+ {
+ case FloatingPointControl.roundUp:
+ rm = RoundingMode.up;
+ break;
+ case FloatingPointControl.roundDown:
+ rm = RoundingMode.down;
+ break;
+ case FloatingPointControl.roundToZero:
+ rm = RoundingMode.toZero;
+ break;
+ case FloatingPointControl.roundToNearest:
+ rm = RoundingMode.toNearestTiesToEven;
+ break;
+ default: assert(false, "Unknown floating point rounding mode");
+ }
+ }
+ }
+
+ bool useE = false;
+
+ final switch (rm)
+ {
+ case RoundingMode.up:
+ useE = abs(val) >= 10.0 ^^ f.precision - (val > 0 ? 1 : 0)
+ || abs(val) < 0.0001 - (val > 0 ? (10.0 ^^ (-4 - f.precision)) : 0);
+ break;
+ case RoundingMode.down:
+ useE = abs(val) >= 10.0 ^^ f.precision - (val < 0 ? 1 : 0)
+ || abs(val) < 0.0001 - (val < 0 ? (10.0 ^^ (-4 - f.precision)) : 0);
+ break;
+ case RoundingMode.toZero:
+ useE = abs(val) >= 10.0 ^^ f.precision
+ || abs(val) < 0.0001;
+ break;
+ case RoundingMode.toNearestTiesToEven:
+ case RoundingMode.toNearestTiesAwayFromZero:
+ useE = abs(val) >= 10.0 ^^ f.precision - 0.5
+ || abs(val) < 0.0001 - 0.5 * (10.0 ^^ (-4 - f.precision));
+ break;
+ }
+
+ if (useE)
+ return printFloatE!true(w, val, f, sgn, exp, mnt, is_upper);
+ else
+ return printFloatF!true(w, val, f, sgn, exp, mnt, is_upper);
+}
+
+@safe unittest
+{
+ // This one tests the switch between e-like and f-like output.
+ // There is a small gap left between the two, where the used
+ // variation is not clearly defined. This is intentional and due
+ // to the way, D handles floating point numbers. On different
+ // computers with different reals the results may vary in this gap.
+
+ import std.math.operations : nextDown, nextUp;
+ import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
+
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+
+ double val = 999999.5;
+ assert(printFloat(val.nextUp, f) == "1e+06");
+ val = nextDown(val);
+ assert(printFloat(val.nextDown, f) == "999999");
+
+ val = 0.00009999995;
+ assert(printFloat(val.nextUp, f) == "0.0001");
+ val = nextDown(val);
+ assert(printFloat(val.nextDown, f) == "9.99999e-05");
+
+ static if (is(FloatingPointControl))
+ {
+ FloatingPointControl fpctrl;
+
+ fpctrl.rounding = FloatingPointControl.roundToZero;
+
+ val = 1000000;
+ assert(printFloat(val.nextUp, f) == "1e+06");
+ val = nextDown(val);
+ assert(printFloat(val.nextDown, f) == "999999");
+
+ val = 0.0001;
+ assert(printFloat(val.nextUp, f) == "0.0001");
+ val = nextDown(val);
+ assert(printFloat(val.nextDown, f) == "9.99999e-05");
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+
+ val = 999999;
+ assert(printFloat(val.nextUp, f) == "1e+06");
+ val = nextDown(val);
+ assert(printFloat(val.nextDown, f) == "999999");
+
+ // 0.0000999999 is actually represented as 0.0000999998999..., which is
+ // less than 0.0000999999, so we need to use nextUp to get the corner case here
+ val = nextUp(0.0000999999);
+ assert(printFloat(val.nextUp, f) == "0.0001");
+ val = nextDown(val);
+ assert(printFloat(val.nextDown, f) == "9.99999e-05");
+
+ fpctrl.rounding = FloatingPointControl.roundDown;
+
+ val = 1000000;
+ assert(printFloat(val.nextUp, f) == "1e+06");
+ val = nextDown(val);
+ assert(printFloat(val.nextDown, f) == "999999");
+
+ val = 0.0001;
+ assert(printFloat(val.nextUp, f) == "0.0001");
+ val = nextDown(val);
+ assert(printFloat(val.nextDown, f) == "9.99999e-05");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ assert(printFloat(float.nan, f) == "nan");
+ assert(printFloat(-float.nan, f) == "-nan");
+ assert(printFloat(float.infinity, f) == "inf");
+ assert(printFloat(-float.infinity, f) == "-inf");
+ assert(printFloat(0.0f, f) == "0");
+ assert(printFloat(-0.0f, f) == "-0");
+
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "9.99995e-41");
+ assert(printFloat(cast(float) -1e-40, f) == "-9.99995e-41");
+ assert(printFloat(1e-30f, f) == "1e-30");
+ assert(printFloat(-1e-30f, f) == "-1e-30");
+ assert(printFloat(1e-10f, f) == "1e-10");
+ assert(printFloat(-1e-10f, f) == "-1e-10");
+ assert(printFloat(0.1f, f) == "0.1");
+ assert(printFloat(-0.1f, f) == "-0.1");
+ assert(printFloat(10.0f, f) == "10");
+ assert(printFloat(-10.0f, f) == "-10");
+ assert(printFloat(1e30f, f) == "1e+30");
+ assert(printFloat(-1e30f, f) == "-1e+30");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "1.4013e-45");
+ assert(printFloat(nextDown(-0.0f), f) == "-1.4013e-45");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ f.width = 20;
+ f.precision = 10;
+
+ assert(printFloat(float.nan, f) == " nan");
+ assert(printFloat(-float.nan, f) == " -nan");
+ assert(printFloat(float.infinity, f) == " inf");
+ assert(printFloat(-float.infinity, f) == " -inf");
+ assert(printFloat(0.0f, f) == " 0");
+ assert(printFloat(-0.0f, f) == " -0");
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == " 9.999946101e-41");
+ assert(printFloat(cast(float) -1e-40, f) == " -9.999946101e-41");
+ assert(printFloat(1e-30f, f) == " 1.000000003e-30");
+ assert(printFloat(-1e-30f, f) == " -1.000000003e-30");
+ assert(printFloat(1e-10f, f) == " 1.000000013e-10");
+ assert(printFloat(-1e-10f, f) == " -1.000000013e-10");
+ assert(printFloat(0.1f, f) == " 0.1000000015");
+ assert(printFloat(-0.1f, f) == " -0.1000000015");
+ assert(printFloat(10.0f, f) == " 10");
+ assert(printFloat(-10.0f, f) == " -10");
+ assert(printFloat(1e30f, f) == " 1.000000015e+30");
+ assert(printFloat(-1e30f, f) == " -1.000000015e+30");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == " 1.401298464e-45");
+ assert(printFloat(nextDown(-0.0f), f) == " -1.401298464e-45");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ f.width = 20;
+ f.precision = 10;
+ f.flDash = true;
+
+ assert(printFloat(float.nan, f) == "nan ");
+ assert(printFloat(-float.nan, f) == "-nan ");
+ assert(printFloat(float.infinity, f) == "inf ");
+ assert(printFloat(-float.infinity, f) == "-inf ");
+ assert(printFloat(0.0f, f) == "0 ");
+ assert(printFloat(-0.0f, f) == "-0 ");
+
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "9.999946101e-41 ");
+ assert(printFloat(cast(float) -1e-40, f) == "-9.999946101e-41 ");
+ assert(printFloat(1e-30f, f) == "1.000000003e-30 ");
+ assert(printFloat(-1e-30f, f) == "-1.000000003e-30 ");
+ assert(printFloat(1e-10f, f) == "1.000000013e-10 ");
+ assert(printFloat(-1e-10f, f) == "-1.000000013e-10 ");
+ assert(printFloat(0.1f, f) == "0.1000000015 ");
+ assert(printFloat(-0.1f, f) == "-0.1000000015 ");
+ assert(printFloat(10.0f, f) == "10 ");
+ assert(printFloat(-10.0f, f) == "-10 ");
+ assert(printFloat(1e30f, f) == "1.000000015e+30 ");
+ assert(printFloat(-1e30f, f) == "-1.000000015e+30 ");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "1.401298464e-45 ");
+ assert(printFloat(nextDown(-0.0f), f) == "-1.401298464e-45 ");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ f.width = 20;
+ f.precision = 10;
+ f.flZero = true;
+
+ assert(printFloat(float.nan, f) == " nan");
+ assert(printFloat(-float.nan, f) == " -nan");
+ assert(printFloat(float.infinity, f) == " inf");
+ assert(printFloat(-float.infinity, f) == " -inf");
+ assert(printFloat(0.0f, f) == "00000000000000000000");
+ assert(printFloat(-0.0f, f) == "-0000000000000000000");
+
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "000009.999946101e-41");
+ assert(printFloat(cast(float) -1e-40, f) == "-00009.999946101e-41");
+ assert(printFloat(1e-30f, f) == "000001.000000003e-30");
+ assert(printFloat(-1e-30f, f) == "-00001.000000003e-30");
+ assert(printFloat(1e-10f, f) == "000001.000000013e-10");
+ assert(printFloat(-1e-10f, f) == "-00001.000000013e-10");
+ assert(printFloat(0.1f, f) == "000000000.1000000015");
+ assert(printFloat(-0.1f, f) == "-00000000.1000000015");
+ assert(printFloat(10.0f, f) == "00000000000000000010");
+ assert(printFloat(-10.0f, f) == "-0000000000000000010");
+ assert(printFloat(1e30f, f) == "000001.000000015e+30");
+ assert(printFloat(-1e30f, f) == "-00001.000000015e+30");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "000001.401298464e-45");
+ assert(printFloat(nextDown(-0.0f), f) == "-00001.401298464e-45");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ f.precision = 10;
+ f.flHash = true;
+
+ assert(printFloat(float.nan, f) == "nan");
+ assert(printFloat(-float.nan, f) == "-nan");
+ assert(printFloat(float.infinity, f) == "inf");
+ assert(printFloat(-float.infinity, f) == "-inf");
+ assert(printFloat(0.0f, f) == "0.000000000");
+ assert(printFloat(-0.0f, f) == "-0.000000000");
+
+ // cast needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(cast(float) 1e-40, f) == "9.999946101e-41");
+ assert(printFloat(cast(float) -1e-40, f) == "-9.999946101e-41");
+ assert(printFloat(1e-30f, f) == "1.000000003e-30");
+ assert(printFloat(-1e-30f, f) == "-1.000000003e-30");
+ assert(printFloat(1e-10f, f) == "1.000000013e-10");
+ assert(printFloat(-1e-10f, f) == "-1.000000013e-10");
+ assert(printFloat(0.1f, f) == "0.1000000015");
+ assert(printFloat(-0.1f, f) == "-0.1000000015");
+ assert(printFloat(10.0f, f) == "10.00000000");
+ assert(printFloat(-10.0f, f) == "-10.00000000");
+ assert(printFloat(1e30f, f) == "1.000000015e+30");
+ assert(printFloat(-1e30f, f) == "-1.000000015e+30");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0f), f) == "1.401298464e-45");
+ assert(printFloat(nextDown(-0.0f), f) == "-1.401298464e-45");
+}
+
+@safe unittest
+{
+ import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
+
+ // std.math's FloatingPointControl isn't available on all target platforms
+ static if (is(FloatingPointControl))
+ {
+ FloatingPointControl fpctrl;
+
+ char[256] buf;
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ f.precision = 2;
+
+ fpctrl.rounding = FloatingPointControl.roundToNearest;
+
+ /*
+ assert(printFloat(11.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "12");
+ assert(printFloat(12.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "13");
+ assert(printFloat(11.7f, f, RoundingMode.toNearestTiesAwayFromZero) == "12");
+ assert(printFloat(11.3f, f, RoundingMode.toNearestTiesAwayFromZero) == "11");
+ assert(printFloat(11.0f, f, RoundingMode.toNearestTiesAwayFromZero) == "11");
+ assert(printFloat(-11.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "-12");
+ assert(printFloat(-12.5f, f, RoundingMode.toNearestTiesAwayFromZero) == "-13");
+ assert(printFloat(-11.7f, f, RoundingMode.toNearestTiesAwayFromZero) == "-12");
+ assert(printFloat(-11.3f, f, RoundingMode.toNearestTiesAwayFromZero) == "-11");
+ assert(printFloat(-11.0f, f, RoundingMode.toNearestTiesAwayFromZero) == "-11");
+ */
+
+ // ties to even
+ assert(printFloat(11.5f, f) == "12");
+ assert(printFloat(12.5f, f) == "12");
+ assert(printFloat(11.7f, f) == "12");
+ assert(printFloat(11.3f, f) == "11");
+ assert(printFloat(11.0f, f) == "11");
+ assert(printFloat(-11.5f, f) == "-12");
+ assert(printFloat(-12.5f, f) == "-12");
+ assert(printFloat(-11.7f, f) == "-12");
+ assert(printFloat(-11.3f, f) == "-11");
+ assert(printFloat(-11.0f, f) == "-11");
+
+ fpctrl.rounding = FloatingPointControl.roundToZero;
+
+ assert(printFloat(11.5f, f) == "11");
+ assert(printFloat(12.5f, f) == "12");
+ assert(printFloat(11.7f, f) == "11");
+ assert(printFloat(11.3f, f) == "11");
+ assert(printFloat(11.0f, f) == "11");
+ assert(printFloat(-11.5f, f) == "-11");
+ assert(printFloat(-12.5f, f) == "-12");
+ assert(printFloat(-11.7f, f) == "-11");
+ assert(printFloat(-11.3f, f) == "-11");
+ assert(printFloat(-11.0f, f) == "-11");
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+
+ assert(printFloat(11.5f, f) == "12");
+ assert(printFloat(12.5f, f) == "13");
+ assert(printFloat(11.7f, f) == "12");
+ assert(printFloat(11.3f, f) == "12");
+ assert(printFloat(11.0f, f) == "11");
+ assert(printFloat(-11.5f, f) == "-11");
+ assert(printFloat(-12.5f, f) == "-12");
+ assert(printFloat(-11.7f, f) == "-11");
+ assert(printFloat(-11.3f, f) == "-11");
+ assert(printFloat(-11.0f, f) == "-11");
+
+ fpctrl.rounding = FloatingPointControl.roundDown;
+
+ assert(printFloat(11.5f, f) == "11");
+ assert(printFloat(12.5f, f) == "12");
+ assert(printFloat(11.7f, f) == "11");
+ assert(printFloat(11.3f, f) == "11");
+ assert(printFloat(11.0f, f) == "11");
+ assert(printFloat(-11.5f, f) == "-12");
+ assert(printFloat(-12.5f, f) == "-13");
+ assert(printFloat(-11.7f, f) == "-12");
+ assert(printFloat(-11.3f, f) == "-12");
+ assert(printFloat(-11.0f, f) == "-11");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+
+ assert(printFloat(double.nan, f) == "nan");
+ assert(printFloat(-double.nan, f) == "-nan");
+ assert(printFloat(double.infinity, f) == "inf");
+ assert(printFloat(-double.infinity, f) == "-inf");
+ assert(printFloat(0.0, f) == "0");
+ assert(printFloat(-0.0, f) == "-0");
+
+ // / 1000 needed due to https://issues.dlang.org/show_bug.cgi?id=20361
+ assert(printFloat(1e-307 / 1000, f) == "1e-310");
+ assert(printFloat(-1e-307 / 1000, f) == "-1e-310");
+ assert(printFloat(1e-30, f) == "1e-30");
+ assert(printFloat(-1e-30, f) == "-1e-30");
+ assert(printFloat(1e-10, f) == "1e-10");
+ assert(printFloat(-1e-10, f) == "-1e-10");
+ assert(printFloat(0.1, f) == "0.1");
+ assert(printFloat(-0.1, f) == "-0.1");
+ assert(printFloat(10.0, f) == "10");
+ assert(printFloat(-10.0, f) == "-10");
+ assert(printFloat(1e300, f) == "1e+300");
+ assert(printFloat(-1e300, f) == "-1e+300");
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0), f) == "4.94066e-324");
+ assert(printFloat(nextDown(-0.0), f) == "-4.94066e-324");
+}
+
+@safe unittest
+{
+ static if (real.mant_dig > 64)
+ {
+ pragma(msg, "printFloat tests disabled because of unsupported `real` format");
+ }
+ else
+ {
+ char[256] buf;
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+
+ assert(printFloat(real.nan, f) == "nan");
+ assert(printFloat(-real.nan, f) == "-nan");
+ assert(printFloat(real.infinity, f) == "inf");
+ assert(printFloat(-real.infinity, f) == "-inf");
+ }
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+
+ import std.math.operations : nextUp;
+
+ double eps = nextUp(0.0);
+ f.precision = 1000;
+ assert(printFloat(eps, f) ==
+ "4.940656458412465441765687928682213723650598026143247644255856825006"
+ ~ "755072702087518652998363616359923797965646954457177309266567103559"
+ ~ "397963987747960107818781263007131903114045278458171678489821036887"
+ ~ "186360569987307230500063874091535649843873124733972731696151400317"
+ ~ "153853980741262385655911710266585566867681870395603106249319452715"
+ ~ "914924553293054565444011274801297099995419319894090804165633245247"
+ ~ "571478690147267801593552386115501348035264934720193790268107107491"
+ ~ "703332226844753335720832431936092382893458368060106011506169809753"
+ ~ "078342277318329247904982524730776375927247874656084778203734469699"
+ ~ "533647017972677717585125660551199131504891101451037862738167250955"
+ ~ "837389733598993664809941164205702637090279242767544565229087538682"
+ ~ "506419718265533447265625e-324");
+
+ f.precision = 50;
+ assert(printFloat(double.max, f) ==
+ "1.7976931348623157081452742373170435679807056752584e+308");
+ assert(printFloat(double.epsilon, f) ==
+ "2.220446049250313080847263336181640625e-16");
+
+ f.precision = 10;
+ assert(printFloat(1.0/3.0, f) == "0.3333333333");
+ assert(printFloat(1.0/7.0, f) == "0.1428571429");
+ assert(printFloat(1.0/9.0, f) == "0.1111111111");
+}
+
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ f.precision = 15;
+
+ import std.math.constants : E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI,
+ LN10, LN2, LOG2, LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2;
+
+ assert(printFloat(cast(double) E, f) == "2.71828182845905");
+ assert(printFloat(cast(double) PI, f) == "3.14159265358979");
+ assert(printFloat(cast(double) PI_2, f) == "1.5707963267949");
+ assert(printFloat(cast(double) PI_4, f) == "0.785398163397448");
+ assert(printFloat(cast(double) M_1_PI, f) == "0.318309886183791");
+ assert(printFloat(cast(double) M_2_PI, f) == "0.636619772367581");
+ assert(printFloat(cast(double) M_2_SQRTPI, f) == "1.12837916709551");
+ assert(printFloat(cast(double) LN10, f) == "2.30258509299405");
+ assert(printFloat(cast(double) LN2, f) == "0.693147180559945");
+ assert(printFloat(cast(double) LOG2, f) == "0.301029995663981");
+ assert(printFloat(cast(double) LOG2E, f) == "1.44269504088896");
+ assert(printFloat(cast(double) LOG2T, f) == "3.32192809488736");
+ assert(printFloat(cast(double) LOG10E, f) == "0.434294481903252");
+ assert(printFloat(cast(double) SQRT2, f) == "1.4142135623731");
+ assert(printFloat(cast(double) SQRT1_2, f) == "0.707106781186548");
+}
+
+// for 100% coverage
+@safe unittest
+{
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ f.precision = 0;
+
+ assert(printFloat(0.009999, f) == "0.01");
+}
+
+@safe unittest
+{
+ static if (real.mant_dig > 64)
+ {
+ pragma(msg, "printFloat tests disabled because of unsupported `real` format");
+ }
+ else
+ {
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ assert(printFloat(real.nan, f) == "nan");
+ assert(printFloat(-real.nan, f) == "-nan");
+ assert(printFloat(real.infinity, f) == "inf");
+ assert(printFloat(-real.infinity, f) == "-inf");
+ assert(printFloat(0.0L, f) == "0");
+ assert(printFloat(-0.0L, f) == "-0");
+ }
+
+ static if (real.mant_dig == 64)
+ {
+ assert(printFloat(1e-4940L, f) == "1e-4940");
+ assert(printFloat(-1e-4940L, f) == "-1e-4940");
+ assert(printFloat(1e-30L, f) == "1e-30");
+ assert(printFloat(-1e-30L, f) == "-1e-30");
+ assert(printFloat(1e-10L, f) == "1e-10");
+ assert(printFloat(-1e-10L, f) == "-1e-10");
+ assert(printFloat(0.1L, f) == "0.1");
+ assert(printFloat(-0.1L, f) == "-0.1");
+ assert(printFloat(10.0L, f) == "10");
+ assert(printFloat(-10.0L, f) == "-10");
+ version (Windows) {} // issue 20972
+ else
+ {
+ assert(printFloat(1e4000L, f) == "1e+4000");
+ assert(printFloat(-1e4000L, f) == "-1e+4000");
+ }
+
+ import std.math.operations : nextUp, nextDown;
+ assert(printFloat(nextUp(0.0L), f) == "3.6452e-4951");
+ assert(printFloat(nextDown(-0.0L), f) == "-3.6452e-4951");
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+ import std.math.exponential : log2;
+ import std.math.operations : nextDown;
+
+ assertCTFEable!(
+ {
+ // log2 is broken for x87-reals on some computers in CTFE
+ // the following tests excludes these computers from the tests
+ // (issue 21757)
+ enum test = cast(int) log2(3.05e2312L);
+ static if (real.mant_dig == 64 && test == 7681)
+ {
+ auto f = FormatSpec!dchar("");
+ f.spec = 'g';
+ assert(printFloat(real.infinity, f) == "inf");
+ assert(printFloat(10.0L, f) == "10");
+ assert(printFloat(2.6080L, f) == "2.608");
+ assert(printFloat(3.05e2312L, f) == "3.05e+2312");
+
+ f.precision = 60;
+ assert(printFloat(2.65e-54L, f) ==
+ "2.65000000000000000005900998740054701394102894093529654759941e-54");
+
+ /*
+ commented out, because CTFE is currently too slow for 5000 digits with extreme values
+
+ f.precision = 5000;
+ auto result2 = printFloat(1.2119e-4822L, f);
+ assert(result2.length == 5007);
+ assert(result2[$ - 20 .. $] == "26072948659534e-4822");
+ auto result3 = printFloat(real.min_normal, f);
+ assert(result3.length == 5007);
+ assert(result3[$ - 20 .. $] == "72078141008227e-4932");
+ auto result4 = printFloat(real.min_normal.nextDown, f);
+ assert(result4.length == 5007);
+ assert(result4[$ - 20 .. $] == "48141326333101e-4932");
+ */
+ }
+ });
+}
+
+// check no allocations
+@safe unittest
+{
+ import std.format : NoOpSink;
+ auto w = NoOpSink();
+
+ import core.memory;
+ auto stats = () @trusted { return GC.stats; } ();
+
+ auto f = FormatSpec!dchar("");
+ f.spec = 'a';
+ printFloat(w, float.nan, f);
+ printFloat(w, -float.infinity, f);
+ printFloat(w, 0.0f, f);
+
+ printFloat(w, -double.nan, f);
+ printFloat(w, double.infinity, f);
+ printFloat(w, -0.0, f);
+
+ import std.math.operations : nextUp;
+ import std.math.constants : E;
+
+ printFloat(w, nextUp(0.0f), f);
+ printFloat(w, cast(float) E, f);
+
+ f.precision = 1000;
+ printFloat(w, float.nan, f);
+ printFloat(w, 0.0, f);
+ printFloat(w, 1.23456789e+100, f);
+
+ f.spec = 'E';
+ f.precision = 80;
+ printFloat(w, 5.62776e+12f, f);
+
+ f.precision = 6;
+ printFloat(w, -1.1418613e+07f, f);
+
+ f.precision = 20;
+ printFloat(w, double.max, f);
+ printFloat(w, nextUp(0.0), f);
+
+ f.precision = 1000;
+ printFloat(w, 1.0, f);
+
+ f.spec = 'f';
+ f.precision = 15;
+ printFloat(w, cast(double) E, f);
+
+ f.precision = 20;
+ printFloat(w, double.max, f);
+ printFloat(w, nextUp(0.0), f);
+
+ f.precision = 1000;
+ printFloat(w, 1.0, f);
+
+ f.spec = 'g';
+ f.precision = 15;
+ printFloat(w, cast(double) E, f);
+
+ f.precision = 20;
+ printFloat(w, double.max, f);
+ printFloat(w, nextUp(0.0), f);
+
+ f.flHash = true;
+ f.precision = 1000;
+ printFloat(w, 1.0, f);
+
+ assert(() @trusted { return GC.stats.usedSize; } () == stats.usedSize);
+}
diff --git a/libphobos/src/std/format/internal/read.d b/libphobos/src/std/format/internal/read.d
new file mode 100644
index 00000000000..102e59fcd5c
--- /dev/null
+++ b/libphobos/src/std/format/internal/read.d
@@ -0,0 +1,410 @@
+// Written in the D programming language.
+
+/*
+ Copyright: Copyright The D Language Foundation 2000-2013.
+
+ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+ Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
+ Andrei Alexandrescu), and Kenji Hara
+
+ Source: $(PHOBOSSRC std/format/internal/read.d)
+ */
+module std.format.internal.read;
+
+import std.range.primitives : ElementEncodingType, ElementType, isInputRange;
+
+import std.traits : isAggregateType, isArray, isAssociativeArray,
+ isDynamicArray, isFloatingPoint, isIntegral, isSomeChar, isSomeString,
+ isStaticArray, StringTypeOf;
+
+import std.format.spec : FormatSpec;
+
+package(std.format):
+
+void skipData(Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+{
+ import std.ascii : isDigit;
+ import std.conv : text;
+ import std.range.primitives : empty, front, popFront;
+
+ switch (spec.spec)
+ {
+ case 'c': input.popFront(); break;
+ case 'd':
+ if (input.front == '+' || input.front == '-') input.popFront();
+ goto case 'u';
+ case 'u':
+ while (!input.empty && isDigit(input.front)) input.popFront();
+ break;
+ default:
+ assert(false,
+ text("Format specifier not understood: %", spec.spec));
+ }
+}
+
+private template acceptedSpecs(T)
+{
+ static if (isIntegral!T)
+ enum acceptedSpecs = "bdosuxX";
+ else static if (isFloatingPoint!T)
+ enum acceptedSpecs = "seEfgG";
+ else static if (isSomeChar!T)
+ enum acceptedSpecs = "bcdosuxX"; // integral + 'c'
+ else
+ enum acceptedSpecs = "";
+}
+
+T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+if (isInputRange!Range && is(immutable T == immutable bool))
+{
+ import std.algorithm.searching : find;
+ import std.conv : parse, text;
+ import std.format : enforceFmt, unformatValue;
+
+ if (spec.spec == 's') return parse!T(input);
+
+ enforceFmt(find(acceptedSpecs!long, spec.spec).length,
+ text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
+
+ return unformatValue!long(input, spec) != 0;
+}
+
+T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+if (isInputRange!Range && is(T == typeof(null)))
+{
+ import std.conv : parse, text;
+ import std.format : enforceFmt;
+
+ enforceFmt(spec.spec == 's',
+ text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
+
+ return parse!T(input);
+}
+
+T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+if (isInputRange!Range && isIntegral!T && !is(T == enum) && isSomeChar!(ElementType!Range))
+{
+ import std.algorithm.searching : find;
+ import std.conv : parse, text;
+ import std.format : enforceFmt, FormatException;
+
+ if (spec.spec == 'r')
+ {
+ static if (is(immutable ElementEncodingType!Range == immutable char)
+ || is(immutable ElementEncodingType!Range == immutable byte)
+ || is(immutable ElementEncodingType!Range == immutable ubyte))
+ return rawRead!T(input);
+ else
+ throw new FormatException(
+ "The raw read specifier %r may only be used with narrow strings and ranges of bytes."
+ );
+ }
+
+ enforceFmt(find(acceptedSpecs!T, spec.spec).length,
+ text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
+
+ enforceFmt(spec.width == 0, "Parsing integers with a width specification is not implemented"); // TODO
+
+ immutable uint base =
+ spec.spec == 'x' || spec.spec == 'X' ? 16 :
+ spec.spec == 'o' ? 8 :
+ spec.spec == 'b' ? 2 :
+ spec.spec == 's' || spec.spec == 'd' || spec.spec == 'u' ? 10 : 0;
+ assert(base != 0, "base must be not equal to zero");
+
+ return parse!T(input, base);
+
+}
+
+T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+if (isFloatingPoint!T && !is(T == enum) && isInputRange!Range
+ && isSomeChar!(ElementType!Range)&& !is(Range == enum))
+{
+ import std.algorithm.searching : find;
+ import std.conv : parse, text;
+ import std.format : enforceFmt, FormatException;
+
+ if (spec.spec == 'r')
+ {
+ static if (is(immutable ElementEncodingType!Range == immutable char)
+ || is(immutable ElementEncodingType!Range == immutable byte)
+ || is(immutable ElementEncodingType!Range == immutable ubyte))
+ return rawRead!T(input);
+ else
+ throw new FormatException(
+ "The raw read specifier %r may only be used with narrow strings and ranges of bytes."
+ );
+ }
+
+ enforceFmt(find(acceptedSpecs!T, spec.spec).length,
+ text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
+
+ return parse!T(input);
+}
+
+T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range))
+{
+ import std.algorithm.searching : find;
+ import std.conv : to, text;
+ import std.range.primitives : empty, front, popFront;
+ import std.format : enforceFmt, unformatValue;
+
+ if (spec.spec == 's' || spec.spec == 'c')
+ {
+ auto result = to!T(input.front);
+ input.popFront();
+ return result;
+ }
+
+ enforceFmt(find(acceptedSpecs!T, spec.spec).length,
+ text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
+
+ static if (T.sizeof == 1)
+ return unformatValue!ubyte(input, spec);
+ else static if (T.sizeof == 2)
+ return unformatValue!ushort(input, spec);
+ else static if (T.sizeof == 4)
+ return unformatValue!uint(input, spec);
+ else
+ static assert(false, T.stringof ~ ".sizeof must be 1, 2, or 4 not " ~
+ to!string(T.sizeof));
+}
+
+T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt)
+if (isInputRange!Range && is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum))
+{
+ import std.conv : text;
+ import std.range.primitives : empty, front, popFront, put;
+ import std.format : enforceFmt;
+
+ const spec = fmt.spec;
+ if (spec == '(')
+ {
+ return unformatRange!T(input, fmt);
+ }
+ enforceFmt(spec == 's',
+ text("Wrong unformat specifier '%", spec , "' for ", T.stringof));
+
+ static if (isStaticArray!T)
+ {
+ T result;
+ auto app = result[];
+ }
+ else
+ {
+ import std.array : appender;
+ auto app = appender!T();
+ }
+ if (fmt.trailing.empty)
+ {
+ for (; !input.empty; input.popFront())
+ {
+ static if (isStaticArray!T)
+ if (app.empty)
+ break;
+ app.put(input.front);
+ }
+ }
+ else
+ {
+ immutable end = fmt.trailing.front;
+ for (; !input.empty && input.front != end; input.popFront())
+ {
+ static if (isStaticArray!T)
+ if (app.empty)
+ break;
+ app.put(input.front);
+ }
+ }
+ static if (isStaticArray!T)
+ {
+ enforceFmt(app.empty, "need more input");
+ return result;
+ }
+ else
+ return app.data;
+}
+
+T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt)
+if (isInputRange!Range && isArray!T && !is(StringTypeOf!T) && !isAggregateType!T
+ && !is(T == enum))
+{
+ import std.conv : parse, text;
+ import std.format : enforceFmt;
+
+ const spec = fmt.spec;
+ if (spec == '(')
+ {
+ return unformatRange!T(input, fmt);
+ }
+
+ enforceFmt(spec == 's',
+ text("Wrong unformat specifier '%", spec , "' for ", T.stringof));
+
+ return parse!T(input);
+}
+
+T unformatValueImpl(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char fmt)
+if (isInputRange!Range && isAssociativeArray!T && !is(T == enum))
+{
+ import std.conv : parse, text;
+ import std.format : enforceFmt;
+
+ const spec = fmt.spec;
+ if (spec == '(')
+ {
+ return unformatRange!T(input, fmt);
+ }
+
+ enforceFmt(spec == 's',
+ text("Wrong unformat specifier '%", spec , "' for ", T.stringof));
+
+ return parse!T(input);
+}
+
+/*
+ * Function that performs raw reading. Used by unformatValue
+ * for integral and float types.
+ */
+private T rawRead(T, Range)(ref Range input)
+if (is(immutable ElementEncodingType!Range == immutable char)
+ || is(immutable ElementEncodingType!Range == immutable byte)
+ || is(immutable ElementEncodingType!Range == immutable ubyte))
+{
+ import std.range.primitives : popFront;
+
+ union X
+ {
+ ubyte[T.sizeof] raw;
+ T typed;
+ }
+ X x;
+ foreach (i; 0 .. T.sizeof)
+ {
+ static if (isSomeString!Range)
+ {
+ x.raw[i] = input[0];
+ input = input[1 .. $];
+ }
+ else
+ {
+ // TODO: recheck this
+ x.raw[i] = input.front;
+ input.popFront();
+ }
+ }
+ return x.typed;
+}
+
+private T unformatRange(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+in (spec.spec == '(', "spec.spec must be '(' not " ~ spec.spec)
+{
+ import std.range.primitives : empty, front, popFront;
+ import std.format : enforceFmt, format;
+
+ T result;
+ static if (isStaticArray!T)
+ {
+ size_t i;
+ }
+
+ const(Char)[] cont = spec.trailing;
+ for (size_t j = 0; j < spec.trailing.length; ++j)
+ {
+ if (spec.trailing[j] == '%')
+ {
+ cont = spec.trailing[0 .. j];
+ break;
+ }
+ }
+
+ bool checkEnd()
+ {
+ return input.empty || !cont.empty && input.front == cont.front;
+ }
+
+ if (!checkEnd())
+ {
+ for (;;)
+ {
+ auto fmt = FormatSpec!Char(spec.nested);
+ fmt.readUpToNextSpec(input);
+ enforceFmt(!input.empty, "Unexpected end of input when parsing range");
+
+ static if (isStaticArray!T)
+ {
+ result[i++] = unformatElement!(typeof(T.init[0]))(input, fmt);
+ }
+ else static if (isDynamicArray!T)
+ {
+ import std.conv : WideElementType;
+ result ~= unformatElement!(WideElementType!T)(input, fmt);
+ }
+ else static if (isAssociativeArray!T)
+ {
+ auto key = unformatElement!(typeof(T.init.keys[0]))(input, fmt);
+ fmt.readUpToNextSpec(input); // eat key separator
+
+ result[key] = unformatElement!(typeof(T.init.values[0]))(input, fmt);
+ }
+
+ static if (isStaticArray!T)
+ {
+ enforceFmt(i <= T.length,
+ "Too many format specifiers for static array of length %d".format(T.length));
+ }
+
+ if (spec.sep !is null)
+ fmt.readUpToNextSpec(input);
+ auto sep = spec.sep !is null ? spec.sep : fmt.trailing;
+
+ if (checkEnd())
+ break;
+
+ if (!sep.empty && input.front == sep.front)
+ {
+ while (!sep.empty)
+ {
+ enforceFmt(!input.empty,
+ "Unexpected end of input when parsing range separator");
+ enforceFmt(input.front == sep.front,
+ "Unexpected character when parsing range separator");
+ input.popFront();
+ sep.popFront();
+ }
+ }
+ }
+ }
+ static if (isStaticArray!T)
+ {
+ enforceFmt(i == T.length,
+ "Too few (%d) format specifiers for static array of length %d".format(i, T.length));
+ }
+ return result;
+}
+
+T unformatElement(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+if (isInputRange!Range)
+{
+ import std.conv : parseElement;
+ import std.format.read : unformatValue;
+
+ static if (isSomeString!T)
+ {
+ if (spec.spec == 's')
+ {
+ return parseElement!T(input);
+ }
+ }
+ else static if (isSomeChar!T)
+ {
+ if (spec.spec == 's')
+ {
+ return parseElement!T(input);
+ }
+ }
+
+ return unformatValue!T(input, spec);
+}
diff --git a/libphobos/src/std/format/internal/write.d b/libphobos/src/std/format/internal/write.d
new file mode 100644
index 00000000000..5883d8d3259
--- /dev/null
+++ b/libphobos/src/std/format/internal/write.d
@@ -0,0 +1,3980 @@
+// Written in the D programming language.
+
+/*
+ Copyright: Copyright The D Language Foundation 2000-2013.
+
+ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+ Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
+ Andrei Alexandrescu), and Kenji Hara
+
+ Source: $(PHOBOSSRC std/format/internal/write.d)
+ */
+module std.format.internal.write;
+
+import std.format.spec : FormatSpec;
+import std.range.primitives : isInputRange;
+import std.traits;
+
+version (StdUnittest)
+{
+ import std.exception : assertCTFEable;
+ import std.format : format;
+}
+
+package(std.format):
+
+/*
+ `bool`s are formatted as `"true"` or `"false"` with `%s` and as `1` or
+ `0` with integral-specific format specs.
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f)
+if (is(BooleanTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
+{
+ BooleanTypeOf!T val = obj;
+
+ if (f.spec == 's')
+ writeAligned(w, val ? "true" : "false", f);
+ else
+ formatValueImpl(w, cast(byte) val, f);
+}
+
+@safe pure unittest
+{
+ assertCTFEable!(
+ {
+ formatTest(false, "false");
+ formatTest(true, "true");
+ });
+}
+
+@safe unittest
+{
+ class C1
+ {
+ bool val;
+ alias val this;
+ this(bool v){ val = v; }
+ }
+
+ class C2 {
+ bool val;
+ alias val this;
+ this(bool v){ val = v; }
+ override string toString() const { return "C"; }
+ }
+
+ () @trusted {
+ formatTest(new C1(false), "false");
+ formatTest(new C1(true), "true");
+ formatTest(new C2(false), "C");
+ formatTest(new C2(true), "C");
+ } ();
+
+ struct S1
+ {
+ bool val;
+ alias val this;
+ }
+
+ struct S2
+ {
+ bool val;
+ alias val this;
+ string toString() const { return "S"; }
+ }
+
+ formatTest(S1(false), "false");
+ formatTest(S1(true), "true");
+ formatTest(S2(false), "S");
+ formatTest(S2(true), "S");
+}
+
+@safe pure unittest
+{
+ string t1 = format("[%6s] [%6s] [%-6s]", true, false, true);
+ assert(t1 == "[ true] [ false] [true ]");
+
+ string t2 = format("[%3s] [%-2s]", true, false);
+ assert(t2 == "[true] [false]");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20534
+@safe pure unittest
+{
+ assert(format("%r",false) == "\0");
+}
+
+@safe pure unittest
+{
+ assert(format("%07s",true) == " true");
+}
+
+@safe pure unittest
+{
+ assert(format("%=8s",true) == " true ");
+ assert(format("%=9s",false) == " false ");
+ assert(format("%=9s",true) == " true ");
+ assert(format("%-=9s",true) == " true ");
+ assert(format("%=10s",false) == " false ");
+ assert(format("%-=10s",false) == " false ");
+}
+
+/*
+ `null` literal is formatted as `"null"`
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f)
+if (is(immutable T == immutable typeof(null)) && !is(T == enum) && !hasToString!(T, Char))
+{
+ import std.format : enforceFmt;
+
+ const spec = f.spec;
+ enforceFmt(spec == 's', "null literal cannot match %" ~ spec);
+
+ writeAligned(w, "null", f);
+}
+
+@safe pure unittest
+{
+ import std.exception : collectExceptionMsg;
+ import std.format : FormatException;
+ import std.range.primitives : back;
+
+ assert(collectExceptionMsg!FormatException(format("%p", null)).back == 'p');
+
+ assertCTFEable!(
+ {
+ formatTest(null, "null");
+ });
+}
+
+@safe pure unittest
+{
+ string t = format("[%6s] [%-6s]", null, null);
+ assert(t == "[ null] [null ]");
+}
+
+/*
+ Integrals are formatted like $(REF printf, core, stdc, stdio).
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f)
+if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
+{
+ import std.range.primitives : put;
+
+ alias U = IntegralTypeOf!T;
+ U val = obj; // Extracting alias this may be impure/system/may-throw
+
+ if (f.spec == 'r')
+ {
+ // raw write, skip all else and write the thing
+ auto raw = (ref val) @trusted {
+ return (cast(const char*) &val)[0 .. val.sizeof];
+ }(val);
+
+ if (needToSwapEndianess(f))
+ {
+ foreach_reverse (c; raw)
+ put(w, c);
+ }
+ else
+ {
+ foreach (c; raw)
+ put(w, c);
+ }
+ return;
+ }
+
+ immutable uint base =
+ f.spec == 'x' || f.spec == 'X' || f.spec == 'a' || f.spec == 'A' ? 16 :
+ f.spec == 'o' ? 8 :
+ f.spec == 'b' ? 2 :
+ f.spec == 's' || f.spec == 'd' || f.spec == 'u'
+ || f.spec == 'e' || f.spec == 'E' || f.spec == 'f' || f.spec == 'F'
+ || f.spec == 'g' || f.spec == 'G' ? 10 :
+ 0;
+
+ import std.format : enforceFmt;
+ enforceFmt(base > 0,
+ "incompatible format character for integral argument: %" ~ f.spec);
+
+ import std.math.algebraic : abs;
+
+ bool negative = false;
+ ulong arg = val;
+ static if (isSigned!U)
+ {
+ if (f.spec != 'x' && f.spec != 'X' && f.spec != 'b' && f.spec != 'o' && f.spec != 'u')
+ {
+ if (val < 0)
+ {
+ negative = true;
+ arg = cast(ulong) abs(val);
+ }
+ }
+ }
+
+ arg &= Unsigned!U.max;
+
+ char[64] digits = void;
+ size_t pos = digits.length - 1;
+ do
+ {
+ digits[pos--] = '0' + arg % base;
+ if (base > 10 && digits[pos + 1] > '9')
+ digits[pos + 1] += ((f.spec == 'x' || f.spec == 'a') ? 'a' : 'A') - '0' - 10;
+ arg /= base;
+ } while (arg > 0);
+
+ char[3] prefix = void;
+ size_t left = 2;
+ size_t right = 2;
+
+ // add sign
+ if (f.spec != 'x' && f.spec != 'X' && f.spec != 'b' && f.spec != 'o' && f.spec != 'u')
+ {
+ if (negative)
+ prefix[right++] = '-';
+ else if (f.flPlus)
+ prefix[right++] = '+';
+ else if (f.flSpace)
+ prefix[right++] = ' ';
+ }
+
+ // not a floating point like spec
+ if (f.spec == 'x' || f.spec == 'X' || f.spec == 'b' || f.spec == 'o' || f.spec == 'u'
+ || f.spec == 'd' || f.spec == 's')
+ {
+ if (f.flHash && (base == 16) && obj != 0)
+ {
+ prefix[--left] = f.spec;
+ prefix[--left] = '0';
+ }
+ if (f.flHash && (base == 8) && obj != 0
+ && (digits.length - (pos + 1) >= f.precision || f.precision == f.UNSPECIFIED))
+ prefix[--left] = '0';
+
+ writeAligned(w, prefix[left .. right], digits[pos + 1 .. $], "", f, true);
+ return;
+ }
+
+ FormatSpec!Char fs = f;
+ if (f.precision == f.UNSPECIFIED)
+ fs.precision = cast(typeof(fs.precision)) (digits.length - pos - 2);
+
+ // %f like output
+ if (f.spec == 'f' || f.spec == 'F'
+ || ((f.spec == 'g' || f.spec == 'G') && (fs.precision >= digits.length - pos - 2)))
+ {
+ if (f.precision == f.UNSPECIFIED)
+ fs.precision = 0;
+
+ writeAligned(w, prefix[left .. right], digits[pos + 1 .. $], ".", "", fs,
+ (f.spec == 'g' || f.spec == 'G') ? PrecisionType.allDigits : PrecisionType.fractionalDigits);
+
+ return;
+ }
+
+ import std.algorithm.searching : all;
+
+ // at least one digit for %g
+ if ((f.spec == 'g' || f.spec == 'G') && fs.precision == 0)
+ fs.precision = 1;
+
+ // rounding
+ size_t digit_end = pos + fs.precision + ((f.spec == 'g' || f.spec == 'G') ? 1 : 2);
+ if (digit_end <= digits.length)
+ {
+ RoundingClass rt = RoundingClass.ZERO;
+ if (digit_end < digits.length)
+ {
+ auto tie = (f.spec == 'a' || f.spec == 'A') ? '8' : '5';
+ if (digits[digit_end] >= tie)
+ {
+ rt = RoundingClass.UPPER;
+ if (digits[digit_end] == tie && digits[digit_end + 1 .. $].all!(a => a == '0'))
+ rt = RoundingClass.FIVE;
+ }
+ else
+ {
+ rt = RoundingClass.LOWER;
+ if (digits[digit_end .. $].all!(a => a == '0'))
+ rt = RoundingClass.ZERO;
+ }
+ }
+
+ if (round(digits, pos + 1, digit_end, rt, negative,
+ f.spec == 'a' ? 'f' : (f.spec == 'A' ? 'F' : '9')))
+ {
+ pos--;
+ digit_end--;
+ }
+ }
+
+ // convert to scientific notation
+ char[1] int_digit = void;
+ int_digit[0] = digits[pos + 1];
+ digits[pos + 1] = '.';
+
+ char[4] suffix = void;
+
+ if (f.spec == 'e' || f.spec == 'E' || f.spec == 'g' || f.spec == 'G')
+ {
+ suffix[0] = (f.spec == 'e' || f.spec == 'g') ? 'e' : 'E';
+ suffix[1] = '+';
+ suffix[2] = cast(char) ('0' + (digits.length - pos - 2) / 10);
+ suffix[3] = cast(char) ('0' + (digits.length - pos - 2) % 10);
+ }
+ else
+ {
+ if (right == 3)
+ prefix[0] = prefix[2];
+ prefix[1] = '0';
+ prefix[2] = f.spec == 'a' ? 'x' : 'X';
+
+ left = right == 3 ? 0 : 1;
+ right = 3;
+
+ suffix[0] = f.spec == 'a' ? 'p' : 'P';
+ suffix[1] = '+';
+ suffix[2] = cast(char) ('0' + ((digits.length - pos - 2) * 4) / 10);
+ suffix[3] = cast(char) ('0' + ((digits.length - pos - 2) * 4) % 10);
+ }
+
+ import std.algorithm.comparison : min;
+
+ // remove trailing zeros
+ if ((f.spec == 'g' || f.spec == 'G') && !f.flHash)
+ {
+ digit_end = min(digit_end, digits.length);
+ while (digit_end > pos + 1 &&
+ (digits[digit_end - 1] == '0' || digits[digit_end - 1] == '.'))
+ digit_end--;
+ }
+
+ writeAligned(w, prefix[left .. right], int_digit[0 .. $],
+ digits[pos + 1 .. min(digit_end, $)],
+ suffix[0 .. $], fs,
+ (f.spec == 'g' || f.spec == 'G') ? PrecisionType.allDigits : PrecisionType.fractionalDigits);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18838
+@safe pure unittest
+{
+ assert("%12,d".format(0) == " 0");
+}
+
+@safe pure unittest
+{
+ import std.exception : collectExceptionMsg;
+ import std.format : FormatException;
+ import std.range.primitives : back;
+
+ assert(collectExceptionMsg!FormatException(format("%c", 5)).back == 'c');
+
+ assertCTFEable!(
+ {
+ formatTest(9, "9");
+ formatTest(10, "10");
+ });
+}
+
+@safe unittest
+{
+ class C1
+ {
+ long val;
+ alias val this;
+ this(long v){ val = v; }
+ }
+
+ class C2
+ {
+ long val;
+ alias val this;
+ this(long v){ val = v; }
+ override string toString() const { return "C"; }
+ }
+
+ () @trusted {
+ formatTest(new C1(10), "10");
+ formatTest(new C2(10), "C");
+ } ();
+
+ struct S1
+ {
+ long val;
+ alias val this;
+ }
+
+ struct S2
+ {
+ long val;
+ alias val this;
+ string toString() const { return "S"; }
+ }
+
+ formatTest(S1(10), "10");
+ formatTest(S2(10), "S");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20064
+@safe unittest
+{
+ assert(format( "%03,d", 1234) == "1,234");
+ assert(format( "%04,d", 1234) == "1,234");
+ assert(format( "%05,d", 1234) == "1,234");
+ assert(format( "%06,d", 1234) == "01,234");
+ assert(format( "%07,d", 1234) == "001,234");
+ assert(format( "%08,d", 1234) == "0,001,234");
+ assert(format( "%09,d", 1234) == "0,001,234");
+ assert(format("%010,d", 1234) == "00,001,234");
+ assert(format("%011,d", 1234) == "000,001,234");
+ assert(format("%012,d", 1234) == "0,000,001,234");
+ assert(format("%013,d", 1234) == "0,000,001,234");
+ assert(format("%014,d", 1234) == "00,000,001,234");
+ assert(format("%015,d", 1234) == "000,000,001,234");
+ assert(format("%016,d", 1234) == "0,000,000,001,234");
+ assert(format("%017,d", 1234) == "0,000,000,001,234");
+
+ assert(format( "%03,d", -1234) == "-1,234");
+ assert(format( "%04,d", -1234) == "-1,234");
+ assert(format( "%05,d", -1234) == "-1,234");
+ assert(format( "%06,d", -1234) == "-1,234");
+ assert(format( "%07,d", -1234) == "-01,234");
+ assert(format( "%08,d", -1234) == "-001,234");
+ assert(format( "%09,d", -1234) == "-0,001,234");
+ assert(format("%010,d", -1234) == "-0,001,234");
+ assert(format("%011,d", -1234) == "-00,001,234");
+ assert(format("%012,d", -1234) == "-000,001,234");
+ assert(format("%013,d", -1234) == "-0,000,001,234");
+ assert(format("%014,d", -1234) == "-0,000,001,234");
+ assert(format("%015,d", -1234) == "-00,000,001,234");
+ assert(format("%016,d", -1234) == "-000,000,001,234");
+ assert(format("%017,d", -1234) == "-0,000,000,001,234");
+}
+
+@safe pure unittest
+{
+ string t1 = format("[%6s] [%-6s]", 123, 123);
+ assert(t1 == "[ 123] [123 ]");
+
+ string t2 = format("[%6s] [%-6s]", -123, -123);
+ assert(t2 == "[ -123] [-123 ]");
+}
+
+@safe pure unittest
+{
+ formatTest(byte.min, "-128");
+ formatTest(short.min, "-32768");
+ formatTest(int.min, "-2147483648");
+ formatTest(long.min, "-9223372036854775808");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21777
+@safe pure unittest
+{
+ assert(format!"%20.5,d"(cast(short) 120) == " 00,120");
+ assert(format!"%20.5,o"(cast(short) 120) == " 00,170");
+ assert(format!"%20.5,x"(cast(short) 120) == " 00,078");
+ assert(format!"%20.5,2d"(cast(short) 120) == " 0,01,20");
+ assert(format!"%20.5,2o"(cast(short) 120) == " 0,01,70");
+ assert(format!"%20.5,4d"(cast(short) 120) == " 0,0120");
+ assert(format!"%20.5,4o"(cast(short) 120) == " 0,0170");
+ assert(format!"%20.5,4x"(cast(short) 120) == " 0,0078");
+ assert(format!"%20.5,2x"(3000) == " 0,0b,b8");
+ assert(format!"%20.5,4d"(3000) == " 0,3000");
+ assert(format!"%20.5,4o"(3000) == " 0,5670");
+ assert(format!"%20.5,4x"(3000) == " 0,0bb8");
+ assert(format!"%20.5,d"(-400) == " -00,400");
+ assert(format!"%20.30d"(-400) == "-000000000000000000000000000400");
+ assert(format!"%20.5,4d"(0) == " 0,0000");
+ assert(format!"%0#.8,2s"(12345) == "00,01,23,45");
+ assert(format!"%0#.9,3x"(55) == "0x000,000,037");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21814
+@safe pure unittest
+{
+ assert(format("%,0d",1000) == "1000");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21817
+@safe pure unittest
+{
+ assert(format!"%u"(-5) == "4294967291");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21820
+@safe pure unittest
+{
+ assert(format!"%#.0o"(0) == "0");
+}
+
+@safe pure unittest
+{
+ assert(format!"%e"(10000) == "1.0000e+04");
+ assert(format!"%.2e"(10000) == "1.00e+04");
+ assert(format!"%.10e"(10000) == "1.0000000000e+04");
+
+ assert(format!"%e"(9999) == "9.999e+03");
+ assert(format!"%.2e"(9999) == "1.00e+04");
+ assert(format!"%.10e"(9999) == "9.9990000000e+03");
+
+ assert(format!"%f"(10000) == "10000");
+ assert(format!"%.2f"(10000) == "10000.00");
+
+ assert(format!"%g"(10000) == "10000");
+ assert(format!"%.2g"(10000) == "1e+04");
+ assert(format!"%.10g"(10000) == "10000");
+
+ assert(format!"%#g"(10000) == "10000.");
+ assert(format!"%#.2g"(10000) == "1.0e+04");
+ assert(format!"%#.10g"(10000) == "10000.00000");
+
+ assert(format!"%g"(9999) == "9999");
+ assert(format!"%.2g"(9999) == "1e+04");
+ assert(format!"%.10g"(9999) == "9999");
+
+ assert(format!"%a"(0x10000) == "0x1.0000p+16");
+ assert(format!"%.2a"(0x10000) == "0x1.00p+16");
+ assert(format!"%.10a"(0x10000) == "0x1.0000000000p+16");
+
+ assert(format!"%a"(0xffff) == "0xf.fffp+12");
+ assert(format!"%.2a"(0xffff) == "0x1.00p+16");
+ assert(format!"%.10a"(0xffff) == "0xf.fff0000000p+12");
+}
+
+@safe pure unittest
+{
+ assert(format!"%.3e"(ulong.max) == "1.845e+19");
+ assert(format!"%.3f"(ulong.max) == "18446744073709551615.000");
+ assert(format!"%.3g"(ulong.max) == "1.84e+19");
+ assert(format!"%.3a"(ulong.max) == "0x1.000p+64");
+
+ assert(format!"%.3e"(long.min) == "-9.223e+18");
+ assert(format!"%.3f"(long.min) == "-9223372036854775808.000");
+ assert(format!"%.3g"(long.min) == "-9.22e+18");
+ assert(format!"%.3a"(long.min) == "-0x8.000p+60");
+
+ assert(format!"%e"(0) == "0e+00");
+ assert(format!"%f"(0) == "0");
+ assert(format!"%g"(0) == "0");
+ assert(format!"%a"(0) == "0x0p+00");
+}
+
+@safe pure unittest
+{
+ assert(format!"%.0g"(1500) == "2e+03");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21900#
+@safe pure unittest
+{
+ assert(format!"%.1a"(472) == "0x1.ep+08");
+}
+
+/*
+ Floating-point values are formatted like $(REF printf, core, stdc, stdio)
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f)
+if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
+{
+ import std.algorithm.searching : find;
+ import std.format : enforceFmt;
+ import std.range.primitives : put;
+
+ FloatingPointTypeOf!T val = obj;
+ const char spec = f.spec;
+
+ if (spec == 'r')
+ {
+ // raw write, skip all else and write the thing
+ auto raw = (ref val) @trusted {
+ return (cast(const char*) &val)[0 .. val.sizeof];
+ }(val);
+
+ if (needToSwapEndianess(f))
+ {
+ foreach_reverse (c; raw)
+ put(w, c);
+ }
+ else
+ {
+ foreach (c; raw)
+ put(w, c);
+ }
+ return;
+ }
+
+ enforceFmt(find("fgFGaAeEs", spec).length,
+ "incompatible format character for floating point argument: %" ~ spec);
+
+ FormatSpec!Char fs = f; // fs is copy for change its values.
+ fs.spec = spec == 's' ? 'g' : spec;
+
+ static if (is(T == float) || is(T == double)
+ || (is(T == real) && (T.mant_dig == double.mant_dig || T.mant_dig == 64)))
+ {
+ alias tval = val;
+ }
+ else
+ {
+ import std.math.traits : isInfinity;
+ import std.math.operations : nextUp;
+
+ // reals that are not supported by printFloat are cast to double.
+ double tval = val;
+
+ // Numbers greater than double.max are converted to double.max:
+ if (val > double.max && !isInfinity(val))
+ tval = double.max;
+ if (val < -double.max && !isInfinity(val))
+ tval = -double.max;
+
+ // Numbers between the smallest representable double subnormal and 0.0
+ // are converted to the smallest representable double subnormal:
+ enum doubleLowest = nextUp(0.0);
+ if (val > 0 && val < doubleLowest)
+ tval = doubleLowest;
+ if (val < 0 && val > -doubleLowest)
+ tval = -doubleLowest;
+ }
+
+ import std.format.internal.floats : printFloat;
+ printFloat(w, tval, fs);
+}
+
+@safe unittest
+{
+ assert(format("%.1f", 1337.7) == "1337.7");
+ assert(format("%,3.2f", 1331.982) == "1,331.98");
+ assert(format("%,3.0f", 1303.1982) == "1,303");
+ assert(format("%#,3.4f", 1303.1982) == "1,303.1982");
+ assert(format("%#,3.0f", 1303.1982) == "1,303.");
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception : collectExceptionMsg;
+ import std.format : FormatException;
+ import std.meta : AliasSeq;
+ import std.range.primitives : back;
+
+ assert(collectExceptionMsg!FormatException(format("%d", 5.1)).back == 'd');
+
+ static foreach (T; AliasSeq!(float, double, real))
+ {
+ formatTest(to!( T)(5.5), "5.5");
+ formatTest(to!( const T)(5.5), "5.5");
+ formatTest(to!(immutable T)(5.5), "5.5");
+
+ formatTest(T.nan, "nan");
+ }
+}
+
+@safe unittest
+{
+ formatTest(2.25, "2.25");
+
+ class C1
+ {
+ double val;
+ alias val this;
+ this(double v){ val = v; }
+ }
+
+ class C2
+ {
+ double val;
+ alias val this;
+ this(double v){ val = v; }
+ override string toString() const { return "C"; }
+ }
+
+ () @trusted {
+ formatTest(new C1(2.25), "2.25");
+ formatTest(new C2(2.25), "C");
+ } ();
+
+ struct S1
+ {
+ double val;
+ alias val this;
+ }
+ struct S2
+ {
+ double val;
+ alias val this;
+ string toString() const { return "S"; }
+ }
+
+ formatTest(S1(2.25), "2.25");
+ formatTest(S2(2.25), "S");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19939
+@safe unittest
+{
+ assert(format("^%13,3.2f$", 1.00) == "^ 1.00$");
+ assert(format("^%13,3.2f$", 10.00) == "^ 10.00$");
+ assert(format("^%13,3.2f$", 100.00) == "^ 100.00$");
+ assert(format("^%13,3.2f$", 1_000.00) == "^ 1,000.00$");
+ assert(format("^%13,3.2f$", 10_000.00) == "^ 10,000.00$");
+ assert(format("^%13,3.2f$", 100_000.00) == "^ 100,000.00$");
+ assert(format("^%13,3.2f$", 1_000_000.00) == "^ 1,000,000.00$");
+ assert(format("^%13,3.2f$", 10_000_000.00) == "^10,000,000.00$");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20069
+@safe unittest
+{
+ assert(format("%012,f", -1234.0) == "-1,234.000000");
+ assert(format("%013,f", -1234.0) == "-1,234.000000");
+ assert(format("%014,f", -1234.0) == "-01,234.000000");
+ assert(format("%011,f", 1234.0) == "1,234.000000");
+ assert(format("%012,f", 1234.0) == "1,234.000000");
+ assert(format("%013,f", 1234.0) == "01,234.000000");
+ assert(format("%014,f", 1234.0) == "001,234.000000");
+ assert(format("%015,f", 1234.0) == "0,001,234.000000");
+ assert(format("%016,f", 1234.0) == "0,001,234.000000");
+
+ assert(format( "%08,.2f", -1234.0) == "-1,234.00");
+ assert(format( "%09,.2f", -1234.0) == "-1,234.00");
+ assert(format("%010,.2f", -1234.0) == "-01,234.00");
+ assert(format("%011,.2f", -1234.0) == "-001,234.00");
+ assert(format("%012,.2f", -1234.0) == "-0,001,234.00");
+ assert(format("%013,.2f", -1234.0) == "-0,001,234.00");
+ assert(format("%014,.2f", -1234.0) == "-00,001,234.00");
+ assert(format( "%08,.2f", 1234.0) == "1,234.00");
+ assert(format( "%09,.2f", 1234.0) == "01,234.00");
+ assert(format("%010,.2f", 1234.0) == "001,234.00");
+ assert(format("%011,.2f", 1234.0) == "0,001,234.00");
+ assert(format("%012,.2f", 1234.0) == "0,001,234.00");
+ assert(format("%013,.2f", 1234.0) == "00,001,234.00");
+ assert(format("%014,.2f", 1234.0) == "000,001,234.00");
+ assert(format("%015,.2f", 1234.0) == "0,000,001,234.00");
+ assert(format("%016,.2f", 1234.0) == "0,000,001,234.00");
+}
+
+@safe unittest
+{
+ import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
+
+ // std.math's FloatingPointControl isn't available on all target platforms
+ static if (is(FloatingPointControl))
+ {
+ assert(FloatingPointControl.rounding == FloatingPointControl.roundToNearest);
+ }
+
+ // issue 20320
+ real a = 0.16;
+ real b = 0.016;
+ assert(format("%.1f", a) == "0.2");
+ assert(format("%.2f", b) == "0.02");
+
+ double a1 = 0.16;
+ double b1 = 0.016;
+ assert(format("%.1f", a1) == "0.2");
+ assert(format("%.2f", b1) == "0.02");
+
+ // issue 9889
+ assert(format("%.1f", 0.09) == "0.1");
+ assert(format("%.1f", -0.09) == "-0.1");
+ assert(format("%.1f", 0.095) == "0.1");
+ assert(format("%.1f", -0.095) == "-0.1");
+ assert(format("%.1f", 0.094) == "0.1");
+ assert(format("%.1f", -0.094) == "-0.1");
+}
+
+@safe unittest
+{
+ double a = 123.456;
+ double b = -123.456;
+ double c = 123.0;
+
+ assert(format("%10.4f",a) == " 123.4560");
+ assert(format("%-10.4f",a) == "123.4560 ");
+ assert(format("%+10.4f",a) == " +123.4560");
+ assert(format("% 10.4f",a) == " 123.4560");
+ assert(format("%010.4f",a) == "00123.4560");
+ assert(format("%#10.4f",a) == " 123.4560");
+
+ assert(format("%10.4f",b) == " -123.4560");
+ assert(format("%-10.4f",b) == "-123.4560 ");
+ assert(format("%+10.4f",b) == " -123.4560");
+ assert(format("% 10.4f",b) == " -123.4560");
+ assert(format("%010.4f",b) == "-0123.4560");
+ assert(format("%#10.4f",b) == " -123.4560");
+
+ assert(format("%10.0f",c) == " 123");
+ assert(format("%-10.0f",c) == "123 ");
+ assert(format("%+10.0f",c) == " +123");
+ assert(format("% 10.0f",c) == " 123");
+ assert(format("%010.0f",c) == "0000000123");
+ assert(format("%#10.0f",c) == " 123.");
+
+ assert(format("%+010.4f",a) == "+0123.4560");
+ assert(format("% 010.4f",a) == " 0123.4560");
+ assert(format("% +010.4f",a) == "+0123.4560");
+}
+
+@safe unittest
+{
+ string t1 = format("[%6s] [%-6s]", 12.3, 12.3);
+ assert(t1 == "[ 12.3] [12.3 ]");
+
+ string t2 = format("[%6s] [%-6s]", -12.3, -12.3);
+ assert(t2 == "[ -12.3] [-12.3 ]");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20396
+@safe unittest
+{
+ import std.math.operations : nextUp;
+
+ assert(format!"%a"(nextUp(0.0f)) == "0x0.000002p-126");
+ assert(format!"%a"(nextUp(0.0)) == "0x0.0000000000001p-1022");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20371
+@safe unittest
+{
+ assert(format!"%.1000a"(1.0).length == 1007);
+ assert(format!"%.600f"(0.1).length == 602);
+ assert(format!"%.600e"(0.1L).length == 606);
+}
+
+@safe unittest
+{
+ import std.math.hardware; // cannot be selective, because FloatingPointControl might not be defined
+
+ // std.math's FloatingPointControl isn't available on all target platforms
+ static if (is(FloatingPointControl))
+ {
+ FloatingPointControl fpctrl;
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+ assert(format!"%.0e"(3.5) == "4e+00");
+ assert(format!"%.0e"(4.5) == "5e+00");
+ assert(format!"%.0e"(-3.5) == "-3e+00");
+ assert(format!"%.0e"(-4.5) == "-4e+00");
+
+ fpctrl.rounding = FloatingPointControl.roundDown;
+ assert(format!"%.0e"(3.5) == "3e+00");
+ assert(format!"%.0e"(4.5) == "4e+00");
+ assert(format!"%.0e"(-3.5) == "-4e+00");
+ assert(format!"%.0e"(-4.5) == "-5e+00");
+
+ fpctrl.rounding = FloatingPointControl.roundToZero;
+ assert(format!"%.0e"(3.5) == "3e+00");
+ assert(format!"%.0e"(4.5) == "4e+00");
+ assert(format!"%.0e"(-3.5) == "-3e+00");
+ assert(format!"%.0e"(-4.5) == "-4e+00");
+
+ fpctrl.rounding = FloatingPointControl.roundToNearest;
+ assert(format!"%.0e"(3.5) == "4e+00");
+ assert(format!"%.0e"(4.5) == "4e+00");
+ assert(format!"%.0e"(-3.5) == "-4e+00");
+ assert(format!"%.0e"(-4.5) == "-4e+00");
+ }
+}
+
+@safe pure unittest
+{
+ static assert(format("%e",1.0) == "1.000000e+00");
+ static assert(format("%e",-1.234e156) == "-1.234000e+156");
+ static assert(format("%a",1.0) == "0x1p+0");
+ static assert(format("%a",-1.234e156) == "-0x1.7024c96ca3ce4p+518");
+ static assert(format("%f",1.0) == "1.000000");
+ static assert(format("%f",-1.234e156) ==
+ "-123399999999999990477495546305353609103201879173427886566531" ~
+ "0740685826234179310516880117527217443004051984432279880308552" ~
+ "009640198043032289366552939010719744.000000");
+ static assert(format("%g",1.0) == "1");
+ static assert(format("%g",-1.234e156) == "-1.234e+156");
+
+ static assert(format("%e",1.0f) == "1.000000e+00");
+ static assert(format("%e",-1.234e23f) == "-1.234000e+23");
+ static assert(format("%a",1.0f) == "0x1p+0");
+ static assert(format("%a",-1.234e23f) == "-0x1.a2187p+76");
+ static assert(format("%f",1.0f) == "1.000000");
+ static assert(format("%f",-1.234e23f) == "-123399998884238311030784.000000");
+ static assert(format("%g",1.0f) == "1");
+ static assert(format("%g",-1.234e23f) == "-1.234e+23");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21641
+@safe unittest
+{
+ float a = -999999.8125;
+ assert(format("%#.5g",a) == "-1.0000e+06");
+ assert(format("%#.6g",a) == "-1.00000e+06");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=8424
+@safe pure unittest
+{
+ static assert(format("%s", 0.6f) == "0.6");
+ static assert(format("%s", 0.6) == "0.6");
+ static assert(format("%s", 0.6L) == "0.6");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=9297
+@safe pure unittest
+{
+ static if (real.mant_dig == 64) // 80 bit reals
+ {
+ assert(format("%.25f", 1.6180339887_4989484820_4586834365L) == "1.6180339887498948482072100");
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21853
+@safe pure unittest
+{
+ import std.math.exponential : log2;
+
+ // log2 is broken for x87-reals on some computers in CTFE
+ // the following test excludes these computers from the test
+ // (issue 21757)
+ enum test = cast(int) log2(3.05e2312L);
+ static if (real.mant_dig == 64 && test == 7681) // 80 bit reals
+ {
+ static assert(format!"%e"(real.max) == "1.189731e+4932");
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21842
+@safe pure unittest
+{
+ assert(format!"%-+05,g"(1.0) == "+1 ");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20536
+@safe pure unittest
+{
+ real r = .00000095367431640625L;
+ assert(format("%a", r) == "0x1p-20");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21840
+@safe pure unittest
+{
+ assert(format!"% 0,e"(0.0) == " 0.000000e+00");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21841
+@safe pure unittest
+{
+ assert(format!"%0.0,e"(0.0) == "0e+00");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21836
+@safe pure unittest
+{
+ assert(format!"%-5,1g"(0.0) == "0 ");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21838
+@safe pure unittest
+{
+ assert(format!"%#,a"(0.0) == "0x0.p+0");
+}
+
+/*
+ Formatting a `creal` is deprecated but still kept around for a while.
+ */
+deprecated("Use of complex types is deprecated. Use std.complex")
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f)
+if (is(immutable T : immutable creal) && !is(T == enum) && !hasToString!(T, Char))
+{
+ import std.range.primitives : put;
+
+ immutable creal val = obj;
+
+ formatValueImpl(w, val.re, f);
+ if (val.im >= 0)
+ {
+ put(w, '+');
+ }
+ formatValueImpl(w, val.im, f);
+ put(w, 'i');
+}
+
+/*
+ Formatting an `ireal` is deprecated but still kept around for a while.
+ */
+deprecated("Use of imaginary types is deprecated. Use std.complex")
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f)
+if (is(immutable T : immutable ireal) && !is(T == enum) && !hasToString!(T, Char))
+{
+ import std.range.primitives : put;
+
+ immutable ireal val = obj;
+
+ formatValueImpl(w, val.im, f);
+ put(w, 'i');
+}
+
+/*
+ Individual characters are formatted as Unicode characters with `%s`
+ and as integers with integral-specific format specs
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f)
+if (is(CharTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
+{
+ import std.meta : AliasSeq;
+
+ CharTypeOf!T[1] val = obj;
+
+ if (f.spec == 's' || f.spec == 'c')
+ writeAligned(w, val[], f);
+ else
+ {
+ alias U = AliasSeq!(ubyte, ushort, uint)[CharTypeOf!T.sizeof/2];
+ formatValueImpl(w, cast(U) val[0], f);
+ }
+}
+
+@safe pure unittest
+{
+ assertCTFEable!(
+ {
+ formatTest('c', "c");
+ });
+}
+
+@safe unittest
+{
+ class C1
+ {
+ char val;
+ alias val this;
+ this(char v){ val = v; }
+ }
+
+ class C2
+ {
+ char val;
+ alias val this;
+ this(char v){ val = v; }
+ override string toString() const { return "C"; }
+ }
+
+ () @trusted {
+ formatTest(new C1('c'), "c");
+ formatTest(new C2('c'), "C");
+ } ();
+
+ struct S1
+ {
+ char val;
+ alias val this;
+ }
+
+ struct S2
+ {
+ char val;
+ alias val this;
+ string toString() const { return "S"; }
+ }
+
+ formatTest(S1('c'), "c");
+ formatTest(S2('c'), "S");
+}
+
+@safe unittest
+{
+ //Little Endian
+ formatTest("%-r", cast( char)'c', ['c' ]);
+ formatTest("%-r", cast(wchar)'c', ['c', 0 ]);
+ formatTest("%-r", cast(dchar)'c', ['c', 0, 0, 0]);
+ formatTest("%-r", '本', ['\x2c', '\x67'] );
+
+ //Big Endian
+ formatTest("%+r", cast( char)'c', [ 'c']);
+ formatTest("%+r", cast(wchar)'c', [0, 'c']);
+ formatTest("%+r", cast(dchar)'c', [0, 0, 0, 'c']);
+ formatTest("%+r", '本', ['\x67', '\x2c']);
+}
+
+
+@safe pure unittest
+{
+ string t1 = format("[%6s] [%-6s]", 'A', 'A');
+ assert(t1 == "[ A] [A ]");
+ string t2 = format("[%6s] [%-6s]", '本', '本');
+ assert(t2 == "[ 本] [本 ]");
+}
+
+/*
+ Strings are formatted like $(REF printf, core, stdc, stdio)
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, scope T obj,
+ scope const ref FormatSpec!Char f)
+if (is(StringTypeOf!T) && !is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
+{
+ Unqual!(StringTypeOf!T) val = obj; // for `alias this`, see bug5371
+ formatRange(w, val, f);
+}
+
+@safe unittest
+{
+ formatTest("abc", "abc");
+}
+
+@safe pure unittest
+{
+ import std.exception : collectExceptionMsg;
+ import std.range.primitives : back;
+
+ assert(collectExceptionMsg(format("%d", "hi")).back == 'd');
+}
+
+@safe unittest
+{
+ // Test for bug 5371 for classes
+ class C1
+ {
+ const string var;
+ alias var this;
+ this(string s){ var = s; }
+ }
+
+ class C2
+ {
+ string var;
+ alias var this;
+ this(string s){ var = s; }
+ }
+
+ () @trusted {
+ formatTest(new C1("c1"), "c1");
+ formatTest(new C2("c2"), "c2");
+ } ();
+
+ // Test for bug 5371 for structs
+ struct S1
+ {
+ const string var;
+ alias var this;
+ }
+
+ struct S2
+ {
+ string var;
+ alias var this;
+ }
+
+ formatTest(S1("s1"), "s1");
+ formatTest(S2("s2"), "s2");
+}
+
+@safe unittest
+{
+ class C3
+ {
+ string val;
+ alias val this;
+ this(string s){ val = s; }
+ override string toString() const { return "C"; }
+ }
+
+ () @trusted { formatTest(new C3("c3"), "C"); } ();
+
+ struct S3
+ {
+ string val; alias val this;
+ string toString() const { return "S"; }
+ }
+
+ formatTest(S3("s3"), "S");
+}
+
+@safe pure unittest
+{
+ //Little Endian
+ formatTest("%-r", "ab"c, ['a' , 'b' ]);
+ formatTest("%-r", "ab"w, ['a', 0 , 'b', 0 ]);
+ formatTest("%-r", "ab"d, ['a', 0, 0, 0, 'b', 0, 0, 0]);
+ formatTest("%-r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac',
+ '\xe8', '\xaa', '\x9e']);
+ formatTest("%-r", "日本語"w, ['\xe5', '\x65', '\x2c', '\x67', '\x9e', '\x8a']);
+ formatTest("%-r", "日本語"d, ['\xe5', '\x65', '\x00', '\x00', '\x2c', '\x67',
+ '\x00', '\x00', '\x9e', '\x8a', '\x00', '\x00']);
+
+ //Big Endian
+ formatTest("%+r", "ab"c, [ 'a', 'b']);
+ formatTest("%+r", "ab"w, [ 0, 'a', 0, 'b']);
+ formatTest("%+r", "ab"d, [0, 0, 0, 'a', 0, 0, 0, 'b']);
+ formatTest("%+r", "日本語"c, ['\xe6', '\x97', '\xa5', '\xe6', '\x9c', '\xac',
+ '\xe8', '\xaa', '\x9e']);
+ formatTest("%+r", "日本語"w, ['\x65', '\xe5', '\x67', '\x2c', '\x8a', '\x9e']);
+ formatTest("%+r", "日本語"d, ['\x00', '\x00', '\x65', '\xe5', '\x00', '\x00',
+ '\x67', '\x2c', '\x00', '\x00', '\x8a', '\x9e']);
+}
+
+@safe pure unittest
+{
+ string t1 = format("[%6s] [%-6s]", "AB", "AB");
+ assert(t1 == "[ AB] [AB ]");
+ string t2 = format("[%6s] [%-6s]", "本Ä", "本Ä");
+ assert(t2 == "[ 本Ä] [本Ä ]");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=6640
+@safe unittest
+{
+ import std.range.primitives : front, popFront;
+
+ struct Range
+ {
+ @safe:
+
+ string value;
+ @property bool empty() const { return !value.length; }
+ @property dchar front() const { return value.front; }
+ void popFront() { value.popFront(); }
+
+ @property size_t length() const { return value.length; }
+ }
+ immutable table =
+ [
+ ["[%s]", "[string]"],
+ ["[%10s]", "[ string]"],
+ ["[%-10s]", "[string ]"],
+ ["[%(%02x %)]", "[73 74 72 69 6e 67]"],
+ ["[%(%c %)]", "[s t r i n g]"],
+ ];
+ foreach (e; table)
+ {
+ formatTest(e[0], "string", e[1]);
+ formatTest(e[0], Range("string"), e[1]);
+ }
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+
+ // string literal from valid UTF sequence is encoding free.
+ static foreach (StrType; AliasSeq!(string, wstring, dstring))
+ {
+ // Valid and printable (ASCII)
+ formatTest([cast(StrType)"hello"],
+ `["hello"]`);
+
+ // 1 character escape sequences (' is not escaped in strings)
+ formatTest([cast(StrType)"\"'\0\\\a\b\f\n\r\t\v"],
+ `["\"'\0\\\a\b\f\n\r\t\v"]`);
+
+ // 1 character optional escape sequences
+ formatTest([cast(StrType)"\'\?"],
+ `["'?"]`);
+
+ // Valid and non-printable code point (<= U+FF)
+ formatTest([cast(StrType)"\x10\x1F\x20test"],
+ `["\x10\x1F test"]`);
+
+ // Valid and non-printable code point (<= U+FFFF)
+ formatTest([cast(StrType)"\u200B..\u200F"],
+ `["\u200B..\u200F"]`);
+
+ // Valid and non-printable code point (<= U+10FFFF)
+ formatTest([cast(StrType)"\U000E0020..\U000E007F"],
+ `["\U000E0020..\U000E007F"]`);
+ }
+
+ // invalid UTF sequence needs hex-string literal postfix (c/w/d)
+ () @trusted
+ {
+ // U+FFFF with UTF-8 (Invalid code point for interchange)
+ formatTest([cast(string)[0xEF, 0xBF, 0xBF]],
+ `[[cast(char) 0xEF, cast(char) 0xBF, cast(char) 0xBF]]`);
+
+ // U+FFFF with UTF-16 (Invalid code point for interchange)
+ formatTest([cast(wstring)[0xFFFF]],
+ `[[cast(wchar) 0xFFFF]]`);
+
+ // U+FFFF with UTF-32 (Invalid code point for interchange)
+ formatTest([cast(dstring)[0xFFFF]],
+ `[[cast(dchar) 0xFFFF]]`);
+ } ();
+}
+
+/*
+ Static-size arrays are formatted as dynamic arrays.
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, auto ref T obj,
+ scope const ref FormatSpec!Char f)
+if (is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
+{
+ formatValueImpl(w, obj[], f);
+}
+
+// Test for https://issues.dlang.org/show_bug.cgi?id=8310
+@safe unittest
+{
+ import std.array : appender;
+ import std.format : formatValue;
+
+ FormatSpec!char f;
+ auto w = appender!string();
+
+ char[2] two = ['a', 'b'];
+ formatValue(w, two, f);
+
+ char[2] getTwo() { return two; }
+ formatValue(w, getTwo(), f);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18205
+@safe pure unittest
+{
+ assert("|%8s|".format("abc") == "| abc|");
+ assert("|%8s|".format("αβγ") == "| αβγ|");
+ assert("|%8s|".format(" ") == "| |");
+ assert("|%8s|".format("été"d) == "| été|");
+ assert("|%8s|".format("été 2018"w) == "|été 2018|");
+
+ assert("%2s".format("e\u0301"w) == " e\u0301");
+ assert("%2s".format("a\u0310\u0337"d) == " a\u0310\u0337");
+}
+
+/*
+ Dynamic arrays are formatted as input ranges.
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f)
+if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
+{
+ static if (is(immutable(ArrayTypeOf!T) == immutable(void[])))
+ {
+ formatValueImpl(w, cast(const ubyte[]) obj, f);
+ }
+ else static if (!isInputRange!T)
+ {
+ alias U = Unqual!(ArrayTypeOf!T);
+ static assert(isInputRange!U, U.stringof ~ " must be an InputRange");
+ U val = obj;
+ formatValueImpl(w, val, f);
+ }
+ else
+ {
+ formatRange(w, obj, f);
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20848
+@safe unittest
+{
+ class C
+ {
+ immutable(void)[] data;
+ }
+
+ import std.typecons : Nullable;
+ Nullable!C c;
+}
+
+// alias this, input range I/F, and toString()
+@safe unittest
+{
+ struct S(int flags)
+ {
+ int[] arr;
+ static if (flags & 1)
+ alias arr this;
+
+ static if (flags & 2)
+ {
+ @property bool empty() const { return arr.length == 0; }
+ @property int front() const { return arr[0] * 2; }
+ void popFront() { arr = arr[1 .. $]; }
+ }
+
+ static if (flags & 4)
+ string toString() const { return "S"; }
+ }
+
+ formatTest(S!0b000([0, 1, 2]), "S!0([0, 1, 2])");
+ formatTest(S!0b001([0, 1, 2]), "[0, 1, 2]"); // Test for bug 7628
+ formatTest(S!0b010([0, 1, 2]), "[0, 2, 4]");
+ formatTest(S!0b011([0, 1, 2]), "[0, 2, 4]");
+ formatTest(S!0b100([0, 1, 2]), "S");
+ formatTest(S!0b101([0, 1, 2]), "S"); // Test for bug 7628
+ formatTest(S!0b110([0, 1, 2]), "S");
+ formatTest(S!0b111([0, 1, 2]), "S");
+
+ class C(uint flags)
+ {
+ int[] arr;
+ static if (flags & 1)
+ alias arr this;
+
+ this(int[] a) { arr = a; }
+
+ static if (flags & 2)
+ {
+ @property bool empty() const { return arr.length == 0; }
+ @property int front() const { return arr[0] * 2; }
+ void popFront() { arr = arr[1 .. $]; }
+ }
+
+ static if (flags & 4)
+ override string toString() const { return "C"; }
+ }
+
+ () @trusted {
+ formatTest(new C!0b000([0, 1, 2]), (new C!0b000([])).toString());
+ formatTest(new C!0b001([0, 1, 2]), "[0, 1, 2]"); // Test for bug 7628
+ formatTest(new C!0b010([0, 1, 2]), "[0, 2, 4]");
+ formatTest(new C!0b011([0, 1, 2]), "[0, 2, 4]");
+ formatTest(new C!0b100([0, 1, 2]), "C");
+ formatTest(new C!0b101([0, 1, 2]), "C"); // Test for bug 7628
+ formatTest(new C!0b110([0, 1, 2]), "C");
+ formatTest(new C!0b111([0, 1, 2]), "C");
+ } ();
+}
+
+@safe unittest
+{
+ // void[]
+ void[] val0;
+ formatTest(val0, "[]");
+
+ void[] val = cast(void[]) cast(ubyte[])[1, 2, 3];
+ formatTest(val, "[1, 2, 3]");
+
+ void[0] sval0 = [];
+ formatTest(sval0, "[]");
+
+ void[3] sval = () @trusted { return cast(void[3]) cast(ubyte[3])[1, 2, 3]; } ();
+ formatTest(sval, "[1, 2, 3]");
+}
+
+@safe unittest
+{
+ // const(T[]) -> const(T)[]
+ const short[] a = [1, 2, 3];
+ formatTest(a, "[1, 2, 3]");
+
+ struct S
+ {
+ const(int[]) arr;
+ alias arr this;
+ }
+
+ auto s = S([1,2,3]);
+ formatTest(s, "[1, 2, 3]");
+}
+
+@safe unittest
+{
+ // nested range formatting with array of string
+ formatTest("%({%(%02x %)}%| %)", ["test", "msg"],
+ `{74 65 73 74} {6d 73 67}`);
+}
+
+@safe unittest
+{
+ // stop auto escaping inside range formatting
+ auto arr = ["hello", "world"];
+ formatTest("%(%s, %)", arr, `"hello", "world"`);
+ formatTest("%-(%s, %)", arr, `hello, world`);
+
+ auto aa1 = [1:"hello", 2:"world"];
+ formatTest("%(%s:%s, %)", aa1, [`1:"hello", 2:"world"`, `2:"world", 1:"hello"`]);
+ formatTest("%-(%s:%s, %)", aa1, [`1:hello, 2:world`, `2:world, 1:hello`]);
+
+ auto aa2 = [1:["ab", "cd"], 2:["ef", "gh"]];
+ formatTest("%-(%s:%s, %)", aa2, [`1:["ab", "cd"], 2:["ef", "gh"]`, `2:["ef", "gh"], 1:["ab", "cd"]`]);
+ formatTest("%-(%s:%(%s%), %)", aa2, [`1:"ab""cd", 2:"ef""gh"`, `2:"ef""gh", 1:"ab""cd"`]);
+ formatTest("%-(%s:%-(%s%)%|, %)", aa2, [`1:abcd, 2:efgh`, `2:efgh, 1:abcd`]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18778
+@safe pure unittest
+{
+ assert(format("%-(%1$s - %1$s, %)", ["A", "B", "C"]) == "A - A, B - B, C - C");
+}
+
+@safe pure unittest
+{
+ int[] a = [ 1, 3, 2 ];
+ formatTest("testing %(%s & %) embedded", a,
+ "testing 1 & 3 & 2 embedded");
+ formatTest("testing %((%s) %)) wyda3", a,
+ "testing (1) (3) (2) wyda3");
+
+ int[0] empt = [];
+ formatTest("(%s)", empt, "([])");
+}
+
+// input range formatting
+private void formatRange(Writer, T, Char)(ref Writer w, ref T val, scope const ref FormatSpec!Char f)
+if (isInputRange!T)
+{
+ import std.conv : text;
+ import std.format : FormatException, formatValue, NoOpSink;
+ import std.range.primitives : ElementType, empty, front, hasLength,
+ walkLength, isForwardRange, isInfinite, popFront, put;
+
+ // in this mode, we just want to do a representative print to discover
+ // if the format spec is valid
+ enum formatTestMode = is(Writer == NoOpSink);
+
+ // Formatting character ranges like string
+ if (f.spec == 's')
+ {
+ alias E = ElementType!T;
+
+ static if (!is(E == enum) && is(CharTypeOf!E))
+ {
+ static if (is(StringTypeOf!T))
+ writeAligned(w, val[0 .. f.precision < $ ? f.precision : $], f);
+ else
+ {
+ if (!f.flDash)
+ {
+ static if (hasLength!T)
+ {
+ // right align
+ auto len = val.length;
+ }
+ else static if (isForwardRange!T && !isInfinite!T)
+ {
+ auto len = walkLength(val.save);
+ }
+ else
+ {
+ import std.format : enforceFmt;
+ enforceFmt(f.width == 0, "Cannot right-align a range without length");
+ size_t len = 0;
+ }
+ if (f.precision != f.UNSPECIFIED && len > f.precision)
+ len = f.precision;
+
+ if (f.width > len)
+ foreach (i ; 0 .. f.width - len)
+ put(w, ' ');
+ if (f.precision == f.UNSPECIFIED)
+ put(w, val);
+ else
+ {
+ size_t printed = 0;
+ for (; !val.empty && printed < f.precision; val.popFront(), ++printed)
+ put(w, val.front);
+ }
+ }
+ else
+ {
+ size_t printed = void;
+
+ // left align
+ if (f.precision == f.UNSPECIFIED)
+ {
+ static if (hasLength!T)
+ {
+ printed = val.length;
+ put(w, val);
+ }
+ else
+ {
+ printed = 0;
+ for (; !val.empty; val.popFront(), ++printed)
+ {
+ put(w, val.front);
+ static if (formatTestMode) break; // one is enough to test
+ }
+ }
+ }
+ else
+ {
+ printed = 0;
+ for (; !val.empty && printed < f.precision; val.popFront(), ++printed)
+ put(w, val.front);
+ }
+
+ if (f.width > printed)
+ foreach (i ; 0 .. f.width - printed)
+ put(w, ' ');
+ }
+ }
+ }
+ else
+ {
+ put(w, f.seqBefore);
+ if (!val.empty)
+ {
+ formatElement(w, val.front, f);
+ val.popFront();
+ for (size_t i; !val.empty; val.popFront(), ++i)
+ {
+ put(w, f.seqSeparator);
+ formatElement(w, val.front, f);
+ static if (formatTestMode) break; // one is enough to test
+ }
+ }
+ static if (!isInfinite!T) put(w, f.seqAfter);
+ }
+ }
+ else if (f.spec == 'r')
+ {
+ static if (is(DynamicArrayTypeOf!T))
+ {
+ alias ARR = DynamicArrayTypeOf!T;
+ scope a = cast(ARR) val;
+ foreach (e ; a)
+ {
+ formatValue(w, e, f);
+ static if (formatTestMode) break; // one is enough to test
+ }
+ }
+ else
+ {
+ for (size_t i; !val.empty; val.popFront(), ++i)
+ {
+ formatValue(w, val.front, f);
+ static if (formatTestMode) break; // one is enough to test
+ }
+ }
+ }
+ else if (f.spec == '(')
+ {
+ if (val.empty)
+ return;
+ // Nested specifier is to be used
+ for (;;)
+ {
+ auto fmt = FormatSpec!Char(f.nested);
+ w: while (true)
+ {
+ immutable r = fmt.writeUpToNextSpec(w);
+ // There was no format specifier, so break
+ if (!r)
+ break;
+ if (f.flDash)
+ formatValue(w, val.front, fmt);
+ else
+ formatElement(w, val.front, fmt);
+ // Check if there will be a format specifier farther on in the
+ // string. If so, continue the loop, otherwise break. This
+ // prevents extra copies of the `sep` from showing up.
+ foreach (size_t i; 0 .. fmt.trailing.length)
+ if (fmt.trailing[i] == '%')
+ continue w;
+ break w;
+ }
+ static if (formatTestMode)
+ {
+ break; // one is enough to test
+ }
+ else
+ {
+ if (f.sep !is null)
+ {
+ put(w, fmt.trailing);
+ val.popFront();
+ if (val.empty)
+ break;
+ put(w, f.sep);
+ }
+ else
+ {
+ val.popFront();
+ if (val.empty)
+ break;
+ put(w, fmt.trailing);
+ }
+ }
+ }
+ }
+ else
+ throw new FormatException(text("Incorrect format specifier for range: %", f.spec));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20218
+@safe pure unittest
+{
+ void notCalled()
+ {
+ import std.range : repeat;
+
+ auto value = 1.repeat;
+
+ // test that range is not evaluated to completion at compiletime
+ format!"%s"(value);
+ }
+}
+
+// character formatting with ecaping
+void formatChar(Writer)(ref Writer w, in dchar c, in char quote)
+{
+ import std.format : formattedWrite;
+ import std.range.primitives : put;
+ import std.uni : isGraphical;
+
+ string fmt;
+ if (isGraphical(c))
+ {
+ if (c == quote || c == '\\')
+ put(w, '\\');
+ put(w, c);
+ return;
+ }
+ else if (c <= 0xFF)
+ {
+ if (c < 0x20)
+ {
+ foreach (i, k; "\n\r\t\a\b\f\v\0")
+ {
+ if (c == k)
+ {
+ put(w, '\\');
+ put(w, "nrtabfv0"[i]);
+ return;
+ }
+ }
+ }
+ fmt = "\\x%02X";
+ }
+ else if (c <= 0xFFFF)
+ fmt = "\\u%04X";
+ else
+ fmt = "\\U%08X";
+
+ formattedWrite(w, fmt, cast(uint) c);
+}
+
+/*
+ Associative arrays are formatted by using `':'` and $(D ", ") as
+ separators, and enclosed by `'['` and `']'`.
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f)
+if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
+{
+ import std.format : enforceFmt, formatValue;
+ import std.range.primitives : put;
+
+ AssocArrayTypeOf!T val = obj;
+ const spec = f.spec;
+
+ enforceFmt(spec == 's' || spec == '(',
+ "incompatible format character for associative array argument: %" ~ spec);
+
+ enum const(Char)[] defSpec = "%s" ~ f.keySeparator ~ "%s" ~ f.seqSeparator;
+ auto fmtSpec = spec == '(' ? f.nested : defSpec;
+
+ auto key_first = true;
+
+ // testing correct nested format spec
+ import std.format : NoOpSink;
+ auto noop = NoOpSink();
+ auto test = FormatSpec!Char(fmtSpec);
+ enforceFmt(test.writeUpToNextSpec(noop),
+ "nested format string for associative array contains no format specifier");
+ enforceFmt(test.indexStart <= 2,
+ "positional parameter in nested format string for associative array may only be 1 or 2");
+ if (test.indexStart == 2)
+ key_first = false;
+
+ enforceFmt(test.writeUpToNextSpec(noop),
+ "nested format string for associative array contains only one format specifier");
+ enforceFmt(test.indexStart <= 2,
+ "positional parameter in nested format string for associative array may only be 1 or 2");
+ enforceFmt(test.indexStart == 0 || ((test.indexStart == 2) == key_first),
+ "wrong combination of positional parameters in nested format string");
+
+ enforceFmt(!test.writeUpToNextSpec(noop),
+ "nested format string for associative array contains more than two format specifiers");
+
+ size_t i = 0;
+ immutable end = val.length;
+
+ if (spec == 's')
+ put(w, f.seqBefore);
+ foreach (k, ref v; val)
+ {
+ auto fmt = FormatSpec!Char(fmtSpec);
+
+ foreach (pos; 1 .. 3)
+ {
+ fmt.writeUpToNextSpec(w);
+
+ if (key_first == (pos == 1))
+ {
+ if (f.flDash)
+ formatValue(w, k, fmt);
+ else
+ formatElement(w, k, fmt);
+ }
+ else
+ {
+ if (f.flDash)
+ formatValue(w, v, fmt);
+ else
+ formatElement(w, v, fmt);
+ }
+ }
+
+ if (f.sep !is null)
+ {
+ fmt.writeUpToNextSpec(w);
+ if (++i != end)
+ put(w, f.sep);
+ }
+ else
+ {
+ if (++i != end)
+ fmt.writeUpToNextSpec(w);
+ }
+ }
+ if (spec == 's')
+ put(w, f.seqAfter);
+}
+
+@safe unittest
+{
+ import std.exception : collectExceptionMsg;
+ import std.format : FormatException;
+ import std.range.primitives : back;
+
+ assert(collectExceptionMsg!FormatException(format("%d", [0:1])).back == 'd');
+
+ int[string] aa0;
+ formatTest(aa0, `[]`);
+
+ // elements escaping
+ formatTest(["aaa":1, "bbb":2],
+ [`["aaa":1, "bbb":2]`, `["bbb":2, "aaa":1]`]);
+ formatTest(['c':"str"],
+ `['c':"str"]`);
+ formatTest(['"':"\"", '\'':"'"],
+ [`['"':"\"", '\'':"'"]`, `['\'':"'", '"':"\""]`]);
+
+ // range formatting for AA
+ auto aa3 = [1:"hello", 2:"world"];
+ // escape
+ formatTest("{%(%s:%s $ %)}", aa3,
+ [`{1:"hello" $ 2:"world"}`, `{2:"world" $ 1:"hello"}`]);
+ // use range formatting for key and value, and use %|
+ formatTest("{%([%04d->%(%c.%)]%| $ %)}", aa3,
+ [`{[0001->h.e.l.l.o] $ [0002->w.o.r.l.d]}`,
+ `{[0002->w.o.r.l.d] $ [0001->h.e.l.l.o]}`]);
+
+ // https://issues.dlang.org/show_bug.cgi?id=12135
+ formatTest("%(%s:<%s>%|,%)", [1:2], "1:<2>");
+ formatTest("%(%s:<%s>%|%)" , [1:2], "1:<2>");
+}
+
+@safe unittest
+{
+ class C1
+ {
+ int[char] val;
+ alias val this;
+ this(int[char] v){ val = v; }
+ }
+
+ class C2
+ {
+ int[char] val;
+ alias val this;
+ this(int[char] v){ val = v; }
+ override string toString() const { return "C"; }
+ }
+
+ () @trusted {
+ formatTest(new C1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`]);
+ formatTest(new C2(['c':1, 'd':2]), "C");
+ } ();
+
+ struct S1
+ {
+ int[char] val;
+ alias val this;
+ }
+
+ struct S2
+ {
+ int[char] val;
+ alias val this;
+ string toString() const { return "S"; }
+ }
+
+ formatTest(S1(['c':1, 'd':2]), [`['c':1, 'd':2]`, `['d':2, 'c':1]`]);
+ formatTest(S2(['c':1, 'd':2]), "S");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21875
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+
+ auto aa = [ 1 : "x", 2 : "y", 3 : "z" ];
+
+ assertThrown!FormatException(format("%(%)", aa));
+ assertThrown!FormatException(format("%(%s%)", aa));
+ assertThrown!FormatException(format("%(%s%s%s%)", aa));
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+
+ auto aa = [ 1 : "x", 2 : "y", 3 : "z" ];
+
+ assertThrown!FormatException(format("%(%3$s%s%)", aa));
+ assertThrown!FormatException(format("%(%s%3$s%)", aa));
+ assertThrown!FormatException(format("%(%1$s%1$s%)", aa));
+ assertThrown!FormatException(format("%(%2$s%2$s%)", aa));
+ assertThrown!FormatException(format("%(%s%1$s%)", aa));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21808
+@safe unittest
+{
+ auto spelled = [ 1 : "one" ];
+ assert(format("%-(%2$s (%1$s)%|, %)", spelled) == "one (1)");
+
+ spelled[2] = "two";
+ auto result = format("%-(%2$s (%1$s)%|, %)", spelled);
+ assert(result == "one (1), two (2)" || result == "two (2), one (1)");
+}
+
+enum HasToStringResult
+{
+ none,
+ hasSomeToString,
+ inCharSink,
+ inCharSinkFormatString,
+ inCharSinkFormatSpec,
+ constCharSink,
+ constCharSinkFormatString,
+ constCharSinkFormatSpec,
+ customPutWriter,
+ customPutWriterFormatSpec,
+}
+
+private enum hasPreviewIn = !is(typeof(mixin(q{(in ref int a) => a})));
+
+template hasToString(T, Char)
+{
+ static if (isPointer!T)
+ {
+ // X* does not have toString, even if X is aggregate type has toString.
+ enum hasToString = HasToStringResult.none;
+ }
+ else static if (is(typeof(
+ {
+ T val = void;
+ const FormatSpec!Char f;
+ static struct S {void put(scope Char s){}}
+ S s;
+ val.toString(s, f);
+ static assert(!__traits(compiles, val.toString(s, FormatSpec!Char())),
+ "force toString to take parameters by ref");
+ static assert(!__traits(compiles, val.toString(S(), f)),
+ "force toString to take parameters by ref");
+ })))
+ {
+ enum hasToString = HasToStringResult.customPutWriterFormatSpec;
+ }
+ else static if (is(typeof(
+ {
+ T val = void;
+ static struct S {void put(scope Char s){}}
+ S s;
+ val.toString(s);
+ static assert(!__traits(compiles, val.toString(S())),
+ "force toString to take parameters by ref");
+ })))
+ {
+ enum hasToString = HasToStringResult.customPutWriter;
+ }
+ else static if (is(typeof({ T val = void; FormatSpec!Char f; val.toString((scope const(char)[] s){}, f); })))
+ {
+ enum hasToString = HasToStringResult.constCharSinkFormatSpec;
+ }
+ else static if (is(typeof({ T val = void; val.toString((scope const(char)[] s){}, "%s"); })))
+ {
+ enum hasToString = HasToStringResult.constCharSinkFormatString;
+ }
+ else static if (is(typeof({ T val = void; val.toString((scope const(char)[] s){}); })))
+ {
+ enum hasToString = HasToStringResult.constCharSink;
+ }
+
+ else static if (hasPreviewIn &&
+ is(typeof({ T val = void; FormatSpec!Char f; val.toString((in char[] s){}, f); })))
+ {
+ enum hasToString = HasToStringResult.inCharSinkFormatSpec;
+ }
+ else static if (hasPreviewIn &&
+ is(typeof({ T val = void; val.toString((in char[] s){}, "%s"); })))
+ {
+ enum hasToString = HasToStringResult.inCharSinkFormatString;
+ }
+ else static if (hasPreviewIn &&
+ is(typeof({ T val = void; val.toString((in char[] s){}); })))
+ {
+ enum hasToString = HasToStringResult.inCharSink;
+ }
+
+ else static if (is(typeof({ T val = void; return val.toString(); }()) S) && isSomeString!S)
+ {
+ enum hasToString = HasToStringResult.hasSomeToString;
+ }
+ else
+ {
+ enum hasToString = HasToStringResult.none;
+ }
+}
+
+@safe unittest
+{
+ import std.range.primitives : isOutputRange;
+
+ static struct A
+ {
+ void toString(Writer)(ref Writer w)
+ if (isOutputRange!(Writer, string))
+ {}
+ }
+ static struct B
+ {
+ void toString(scope void delegate(scope const(char)[]) sink, scope FormatSpec!char fmt) {}
+ }
+ static struct C
+ {
+ void toString(scope void delegate(scope const(char)[]) sink, string fmt) {}
+ }
+ static struct D
+ {
+ void toString(scope void delegate(scope const(char)[]) sink) {}
+ }
+ static struct E
+ {
+ string toString() {return "";}
+ }
+ static struct F
+ {
+ void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt)
+ if (isOutputRange!(Writer, string))
+ {}
+ }
+ static struct G
+ {
+ string toString() {return "";}
+ void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {}
+ }
+ static struct H
+ {
+ string toString() {return "";}
+ void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt)
+ if (isOutputRange!(Writer, string))
+ {}
+ }
+ static struct I
+ {
+ void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, string)) {}
+ void toString(Writer)(ref Writer w, scope const ref FormatSpec!char fmt)
+ if (isOutputRange!(Writer, string))
+ {}
+ }
+ static struct J
+ {
+ string toString() {return "";}
+ void toString(Writer)(ref Writer w, scope ref FormatSpec!char fmt)
+ if (isOutputRange!(Writer, string))
+ {}
+ }
+ static struct K
+ {
+ void toString(Writer)(Writer w, scope const ref FormatSpec!char fmt)
+ if (isOutputRange!(Writer, string))
+ {}
+ }
+ static struct L
+ {
+ void toString(Writer)(ref Writer w, scope const FormatSpec!char fmt)
+ if (isOutputRange!(Writer, string))
+ {}
+ }
+ static struct M
+ {
+ void toString(scope void delegate(in char[]) sink, in FormatSpec!char fmt) {}
+ }
+ static struct N
+ {
+ void toString(scope void delegate(in char[]) sink, string fmt) {}
+ }
+ static struct O
+ {
+ void toString(scope void delegate(in char[]) sink) {}
+ }
+
+ with(HasToStringResult)
+ {
+ static assert(hasToString!(A, char) == customPutWriter);
+ static assert(hasToString!(B, char) == constCharSinkFormatSpec);
+ static assert(hasToString!(C, char) == constCharSinkFormatString);
+ static assert(hasToString!(D, char) == constCharSink);
+ static assert(hasToString!(E, char) == hasSomeToString);
+ static assert(hasToString!(F, char) == customPutWriterFormatSpec);
+ static assert(hasToString!(G, char) == customPutWriter);
+ static assert(hasToString!(H, char) == customPutWriterFormatSpec);
+ static assert(hasToString!(I, char) == customPutWriterFormatSpec);
+ static assert(hasToString!(J, char) == hasSomeToString);
+ static assert(hasToString!(K, char) == constCharSinkFormatSpec);
+ static assert(hasToString!(L, char) == none);
+ static if (hasPreviewIn)
+ {
+ static assert(hasToString!(M, char) == inCharSinkFormatSpec);
+ static assert(hasToString!(N, char) == inCharSinkFormatString);
+ static assert(hasToString!(O, char) == inCharSink);
+ }
+ }
+}
+
+// object formatting with toString
+private void formatObject(Writer, T, Char)(ref Writer w, ref T val, scope const ref FormatSpec!Char f)
+if (hasToString!(T, Char))
+{
+ import std.format : NoOpSink;
+ import std.range.primitives : put;
+
+ enum overload = hasToString!(T, Char);
+
+ enum noop = is(Writer == NoOpSink);
+
+ static if (overload == HasToStringResult.customPutWriterFormatSpec)
+ {
+ static if (!noop) val.toString(w, f);
+ }
+ else static if (overload == HasToStringResult.customPutWriter)
+ {
+ static if (!noop) val.toString(w);
+ }
+ else static if (overload == HasToStringResult.constCharSinkFormatSpec)
+ {
+ static if (!noop) val.toString((scope const(char)[] s) { put(w, s); }, f);
+ }
+ else static if (overload == HasToStringResult.constCharSinkFormatString)
+ {
+ static if (!noop) val.toString((scope const(char)[] s) { put(w, s); }, f.getCurFmtStr());
+ }
+ else static if (overload == HasToStringResult.constCharSink)
+ {
+ static if (!noop) val.toString((scope const(char)[] s) { put(w, s); });
+ }
+ else static if (overload == HasToStringResult.inCharSinkFormatSpec)
+ {
+ static if (!noop) val.toString((in char[] s) { put(w, s); }, f);
+ }
+ else static if (overload == HasToStringResult.inCharSinkFormatString)
+ {
+ static if (!noop) val.toString((in char[] s) { put(w, s); }, f.getCurFmtStr());
+ }
+ else static if (overload == HasToStringResult.inCharSink)
+ {
+ static if (!noop) val.toString((in char[] s) { put(w, s); });
+ }
+ else static if (overload == HasToStringResult.hasSomeToString)
+ {
+ static if (!noop) put(w, val.toString());
+ }
+ else
+ {
+ static assert(0, "No way found to format " ~ T.stringof ~ " as string");
+ }
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+
+ static interface IF1 { }
+ class CIF1 : IF1 { }
+ static struct SF1 { }
+ static union UF1 { }
+ static class CF1 { }
+
+ static interface IF2 { string toString(); }
+ static class CIF2 : IF2 { override string toString() { return ""; } }
+ static struct SF2 { string toString() { return ""; } }
+ static union UF2 { string toString() { return ""; } }
+ static class CF2 { override string toString() { return ""; } }
+
+ static interface IK1 { void toString(scope void delegate(scope const(char)[]) sink,
+ FormatSpec!char) const; }
+ static class CIK1 : IK1 { override void toString(scope void delegate(scope const(char)[]) sink,
+ FormatSpec!char) const { sink("CIK1"); } }
+ static struct KS1 { void toString(scope void delegate(scope const(char)[]) sink,
+ FormatSpec!char) const { sink("KS1"); } }
+
+ static union KU1 { void toString(scope void delegate(scope const(char)[]) sink,
+ FormatSpec!char) const { sink("KU1"); } }
+
+ static class KC1 { void toString(scope void delegate(scope const(char)[]) sink,
+ FormatSpec!char) const { sink("KC1"); } }
+
+ IF1 cif1 = new CIF1;
+ assertThrown!FormatException(format("%f", cif1));
+ assertThrown!FormatException(format("%f", SF1()));
+ assertThrown!FormatException(format("%f", UF1()));
+ assertThrown!FormatException(format("%f", new CF1()));
+
+ IF2 cif2 = new CIF2;
+ assertThrown!FormatException(format("%f", cif2));
+ assertThrown!FormatException(format("%f", SF2()));
+ assertThrown!FormatException(format("%f", UF2()));
+ assertThrown!FormatException(format("%f", new CF2()));
+
+ IK1 cik1 = new CIK1;
+ assert(format("%f", cik1) == "CIK1");
+ assert(format("%f", KS1()) == "KS1");
+ assert(format("%f", KU1()) == "KU1");
+ assert(format("%f", new KC1()) == "KC1");
+}
+
+/*
+ Aggregates
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f)
+if (is(T == class) && !is(T == enum))
+{
+ import std.range.primitives : put;
+
+ enforceValidFormatSpec!(T, Char)(f);
+
+ // TODO: remove this check once `@disable override` deprecation cycle is finished
+ static if (__traits(hasMember, T, "toString") && isSomeFunction!(val.toString))
+ static assert(!__traits(isDisabled, T.toString), T.stringof ~
+ " cannot be formatted because its `toString` is marked with `@disable`");
+
+ if (val is null)
+ put(w, "null");
+ else
+ {
+ import std.algorithm.comparison : among;
+ enum overload = hasToString!(T, Char);
+ with(HasToStringResult)
+ static if ((is(T == immutable) || is(T == const) || is(T == shared)) && overload == none)
+ {
+ // Remove this when Object gets const toString
+ // https://issues.dlang.org/show_bug.cgi?id=7879
+ static if (is(T == immutable))
+ put(w, "immutable(");
+ else static if (is(T == const))
+ put(w, "const(");
+ else static if (is(T == shared))
+ put(w, "shared(");
+
+ put(w, typeid(Unqual!T).name);
+ put(w, ')');
+ }
+ else static if (overload.among(constCharSink, constCharSinkFormatString, constCharSinkFormatSpec) ||
+ (!isInputRange!T && !is(BuiltinTypeOf!T)))
+ {
+ formatObject!(Writer, T, Char)(w, val, f);
+ }
+ else
+ {
+ static if (!is(__traits(parent, T.toString) == Object)) // not inherited Object.toString
+ {
+ formatObject(w, val, f);
+ }
+ else static if (isInputRange!T)
+ {
+ formatRange(w, val, f);
+ }
+ else static if (is(BuiltinTypeOf!T X))
+ {
+ X x = val;
+ formatValueImpl(w, x, f);
+ }
+ else
+ {
+ formatObject(w, val, f);
+ }
+ }
+ }
+}
+
+@system unittest
+{
+ import std.array : appender;
+ import std.range.interfaces : inputRangeObject;
+
+ // class range (https://issues.dlang.org/show_bug.cgi?id=5154)
+ auto c = inputRangeObject([1,2,3,4]);
+ formatTest(c, "[1, 2, 3, 4]");
+ assert(c.empty);
+ c = null;
+ formatTest(c, "null");
+}
+
+@system unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=5354
+ // If the class has both range I/F and custom toString, the use of custom
+ // toString routine is prioritized.
+
+ // Enable the use of custom toString that gets a sink delegate
+ // for class formatting.
+
+ enum inputRangeCode =
+ q{
+ int[] arr;
+ this(int[] a){ arr = a; }
+ @property int front() const { return arr[0]; }
+ @property bool empty() const { return arr.length == 0; }
+ void popFront(){ arr = arr[1 .. $]; }
+ };
+
+ class C1
+ {
+ mixin(inputRangeCode);
+ void toString(scope void delegate(scope const(char)[]) dg,
+ scope const ref FormatSpec!char f) const
+ {
+ dg("[012]");
+ }
+ }
+ class C2
+ {
+ mixin(inputRangeCode);
+ void toString(scope void delegate(const(char)[]) dg, string f) const { dg("[012]"); }
+ }
+ class C3
+ {
+ mixin(inputRangeCode);
+ void toString(scope void delegate(const(char)[]) dg) const { dg("[012]"); }
+ }
+ class C4
+ {
+ mixin(inputRangeCode);
+ override string toString() const { return "[012]"; }
+ }
+ class C5
+ {
+ mixin(inputRangeCode);
+ }
+
+ formatTest(new C1([0, 1, 2]), "[012]");
+ formatTest(new C2([0, 1, 2]), "[012]");
+ formatTest(new C3([0, 1, 2]), "[012]");
+ formatTest(new C4([0, 1, 2]), "[012]");
+ formatTest(new C5([0, 1, 2]), "[0, 1, 2]");
+}
+
+// outside the unittest block, otherwise the FQN of the
+// class contains the line number of the unittest
+version (StdUnittest)
+{
+ private class C {}
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=7879
+@safe unittest
+{
+ const(C) c;
+ auto s = format("%s", c);
+ assert(s == "null");
+
+ immutable(C) c2 = new C();
+ s = format("%s", c2);
+ assert(s == "immutable(std.format.internal.write.C)");
+
+ const(C) c3 = new C();
+ s = format("%s", c3);
+ assert(s == "const(std.format.internal.write.C)");
+
+ shared(C) c4 = new C();
+ s = format("%s", c4);
+ assert(s == "shared(std.format.internal.write.C)");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=7879
+@safe unittest
+{
+ class F
+ {
+ override string toString() const @safe
+ {
+ return "Foo";
+ }
+ }
+
+ const(F) c;
+ auto s = format("%s", c);
+ assert(s == "null");
+
+ const(F) c2 = new F();
+ s = format("%s", c2);
+ assert(s == "Foo", s);
+}
+
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f)
+if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is(T == enum))
+{
+ import std.range.primitives : put;
+
+ enforceValidFormatSpec!(T, Char)(f);
+ if (val is null)
+ put(w, "null");
+ else
+ {
+ static if (__traits(hasMember, T, "toString") && isSomeFunction!(val.toString))
+ static assert(!__traits(isDisabled, T.toString), T.stringof ~
+ " cannot be formatted because its `toString` is marked with `@disable`");
+
+ static if (hasToString!(T, Char) != HasToStringResult.none)
+ {
+ formatObject(w, val, f);
+ }
+ else static if (isInputRange!T)
+ {
+ formatRange(w, val, f);
+ }
+ else
+ {
+ version (Windows)
+ {
+ import core.sys.windows.com : IUnknown;
+ static if (is(T : IUnknown))
+ {
+ formatValueImpl(w, *cast(void**)&val, f);
+ }
+ else
+ {
+ formatValueImpl(w, cast(Object) val, f);
+ }
+ }
+ else
+ {
+ formatValueImpl(w, cast(Object) val, f);
+ }
+ }
+ }
+}
+
+@system unittest
+{
+ import std.range.interfaces : InputRange, inputRangeObject;
+
+ // interface
+ InputRange!int i = inputRangeObject([1,2,3,4]);
+ formatTest(i, "[1, 2, 3, 4]");
+ assert(i.empty);
+ i = null;
+ formatTest(i, "null");
+
+ // interface (downcast to Object)
+ interface Whatever {}
+ class C : Whatever
+ {
+ override @property string toString() const { return "ab"; }
+ }
+ Whatever val = new C;
+ formatTest(val, "ab");
+
+ // https://issues.dlang.org/show_bug.cgi?id=11175
+ version (Windows)
+ {
+ import core.sys.windows.com : IID, IUnknown;
+ import core.sys.windows.windef : HRESULT;
+
+ interface IUnknown2 : IUnknown { }
+
+ class D : IUnknown2
+ {
+ extern(Windows) HRESULT QueryInterface(const(IID)* riid, void** pvObject) { return typeof(return).init; }
+ extern(Windows) uint AddRef() { return 0; }
+ extern(Windows) uint Release() { return 0; }
+ }
+
+ IUnknown2 d = new D;
+ string expected = format("%X", cast(void*) d);
+ formatTest(d, expected);
+ }
+}
+
+// Maybe T is noncopyable struct, so receive it by 'auto ref'.
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, auto ref T val,
+ scope const ref FormatSpec!Char f)
+if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T))
+ && !is(T == enum))
+{
+ import std.range.primitives : put;
+
+ static if (__traits(hasMember, T, "toString") && isSomeFunction!(val.toString))
+ static assert(!__traits(isDisabled, T.toString), T.stringof ~
+ " cannot be formatted because its `toString` is marked with `@disable`");
+
+ enforceValidFormatSpec!(T, Char)(f);
+ static if (hasToString!(T, Char))
+ {
+ formatObject(w, val, f);
+ }
+ else static if (isInputRange!T)
+ {
+ formatRange(w, val, f);
+ }
+ else static if (is(T == struct))
+ {
+ enum left = T.stringof~"(";
+ enum separator = ", ";
+ enum right = ")";
+
+ put(w, left);
+ foreach (i, e; val.tupleof)
+ {
+ static if (__traits(identifier, val.tupleof[i]) == "this")
+ continue;
+ else static if (0 < i && val.tupleof[i-1].offsetof == val.tupleof[i].offsetof)
+ {
+ static if (i == val.tupleof.length - 1 || val.tupleof[i].offsetof != val.tupleof[i+1].offsetof)
+ put(w, separator~val.tupleof[i].stringof[4 .. $]~"}");
+ else
+ put(w, separator~val.tupleof[i].stringof[4 .. $]);
+ }
+ else static if (i+1 < val.tupleof.length && val.tupleof[i].offsetof == val.tupleof[i+1].offsetof)
+ put(w, (i > 0 ? separator : "")~"#{overlap "~val.tupleof[i].stringof[4 .. $]);
+ else
+ {
+ static if (i > 0)
+ put(w, separator);
+ formatElement(w, e, f);
+ }
+ }
+ put(w, right);
+ }
+ else
+ {
+ put(w, T.stringof);
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=9588
+@safe pure unittest
+{
+ struct S { int x; bool empty() { return false; } }
+ formatTest(S(), "S(0)");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=4638
+@safe unittest
+{
+ struct U8 { string toString() const { return "blah"; } }
+ struct U16 { wstring toString() const { return "blah"; } }
+ struct U32 { dstring toString() const { return "blah"; } }
+ formatTest(U8(), "blah");
+ formatTest(U16(), "blah");
+ formatTest(U32(), "blah");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=3890
+@safe unittest
+{
+ struct Int{ int n; }
+ struct Pair{ string s; Int i; }
+ formatTest(Pair("hello", Int(5)),
+ `Pair("hello", Int(5))`);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=9117
+@safe unittest
+{
+ import std.format : formattedWrite;
+
+ static struct Frop {}
+
+ static struct Foo
+ {
+ int n = 0;
+ alias n this;
+ T opCast(T) () if (is(T == Frop))
+ {
+ return Frop();
+ }
+ string toString()
+ {
+ return "Foo";
+ }
+ }
+
+ static struct Bar
+ {
+ Foo foo;
+ alias foo this;
+ string toString()
+ {
+ return "Bar";
+ }
+ }
+
+ const(char)[] result;
+ void put(scope const char[] s) { result ~= s; }
+
+ Foo foo;
+ formattedWrite(&put, "%s", foo); // OK
+ assert(result == "Foo");
+
+ result = null;
+
+ Bar bar;
+ formattedWrite(&put, "%s", bar); // NG
+ assert(result == "Bar");
+
+ result = null;
+
+ int i = 9;
+ formattedWrite(&put, "%s", 9);
+ assert(result == "9");
+}
+
+@safe unittest
+{
+ // union formatting without toString
+ union U1
+ {
+ int n;
+ string s;
+ }
+ U1 u1;
+ formatTest(u1, "U1");
+
+ // union formatting with toString
+ union U2
+ {
+ int n;
+ string s;
+ string toString() const { return s; }
+ }
+ U2 u2;
+ () @trusted { u2.s = "hello"; } ();
+ formatTest(u2, "hello");
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.format : formatValue;
+
+ // https://issues.dlang.org/show_bug.cgi?id=7230
+ static struct Bug7230
+ {
+ string s = "hello";
+ union {
+ string a;
+ int b;
+ double c;
+ }
+ long x = 10;
+ }
+
+ Bug7230 bug;
+ bug.b = 123;
+
+ FormatSpec!char f;
+ auto w = appender!(char[])();
+ formatValue(w, bug, f);
+ assert(w.data == `Bug7230("hello", #{overlap a, b, c}, 10)`);
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.format : formatValue;
+
+ static struct S{ @disable this(this); }
+ S s;
+
+ FormatSpec!char f;
+ auto w = appender!string();
+ formatValue(w, s, f);
+ assert(w.data == "S()");
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.format : formatValue;
+
+ //struct Foo { @disable string toString(); }
+ //Foo foo;
+
+ interface Bar { @disable string toString(); }
+ Bar bar;
+
+ auto w = appender!(char[])();
+ FormatSpec!char f;
+
+ // NOTE: structs cant be tested : the assertion is correct so compilation
+ // continues and fails when trying to link the unimplemented toString.
+ //static assert(!__traits(compiles, formatValue(w, foo, f)));
+ static assert(!__traits(compiles, formatValue(w, bar, f)));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21722
+@safe unittest
+{
+ struct Bar
+ {
+ void toString (scope void delegate (scope const(char)[]) sink, string fmt)
+ {
+ sink("Hello");
+ }
+ }
+
+ Bar b;
+ auto result = () @trusted { return format("%b", b); } ();
+ assert(result == "Hello");
+
+ static if (hasPreviewIn)
+ {
+ struct Foo
+ {
+ void toString(scope void delegate(in char[]) sink, in FormatSpec!char fmt)
+ {
+ sink("Hello");
+ }
+ }
+
+ Foo f;
+ assert(format("%b", f) == "Hello");
+
+ struct Foo2
+ {
+ void toString(scope void delegate(in char[]) sink, string fmt)
+ {
+ sink("Hello");
+ }
+ }
+
+ Foo2 f2;
+ assert(format("%b", f2) == "Hello");
+ }
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.format : singleSpec;
+
+ // Bug #17269. Behavior similar to `struct A { Nullable!string B; }`
+ struct StringAliasThis
+ {
+ @property string value() const { assert(0); }
+ alias value this;
+ string toString() { return "helloworld"; }
+ private string _value;
+ }
+ struct TestContainer
+ {
+ StringAliasThis testVar;
+ }
+
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ formatElement(w, TestContainer(), spec);
+
+ assert(w.data == "TestContainer(helloworld)", w.data);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17269
+@safe unittest
+{
+ import std.typecons : Nullable;
+
+ struct Foo
+ {
+ Nullable!string bar;
+ }
+
+ Foo f;
+ formatTest(f, "Foo(Nullable.null)");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19003
+@safe unittest
+{
+ struct S
+ {
+ int i;
+
+ @disable this();
+
+ invariant { assert(this.i); }
+
+ this(int i) @safe in { assert(i); } do { this.i = i; }
+
+ string toString() { return "S"; }
+ }
+
+ S s = S(1);
+
+ format!"%s"(s);
+}
+
+void enforceValidFormatSpec(T, Char)(scope const ref FormatSpec!Char f)
+{
+ import std.format : enforceFmt;
+ import std.range : isInputRange;
+ import std.format.internal.write : hasToString, HasToStringResult;
+
+ enum overload = hasToString!(T, Char);
+ static if (
+ overload != HasToStringResult.constCharSinkFormatSpec &&
+ overload != HasToStringResult.constCharSinkFormatString &&
+ overload != HasToStringResult.inCharSinkFormatSpec &&
+ overload != HasToStringResult.inCharSinkFormatString &&
+ overload != HasToStringResult.customPutWriterFormatSpec &&
+ !isInputRange!T)
+ {
+ enforceFmt(f.spec == 's',
+ "Expected '%s' format specifier for type '" ~ T.stringof ~ "'");
+ }
+}
+
+/*
+ `enum`s are formatted like their base value
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f)
+if (is(T == enum))
+{
+ import std.array : appender;
+ import std.range.primitives : put;
+
+ if (f.spec == 's')
+ {
+ foreach (i, e; EnumMembers!T)
+ {
+ if (val == e)
+ {
+ formatValueImpl(w, __traits(allMembers, T)[i], f);
+ return;
+ }
+ }
+
+ auto w2 = appender!string();
+
+ // val is not a member of T, output cast(T) rawValue instead.
+ put(w2, "cast(" ~ T.stringof ~ ")");
+ static assert(!is(OriginalType!T == T), "OriginalType!" ~ T.stringof ~
+ "must not be equal to " ~ T.stringof);
+
+ FormatSpec!Char f2 = f;
+ f2.width = 0;
+ formatValueImpl(w2, cast(OriginalType!T) val, f2);
+ writeAligned(w, w2.data, f);
+ return;
+ }
+ formatValueImpl(w, cast(OriginalType!T) val, f);
+}
+
+@safe unittest
+{
+ enum A { first, second, third }
+ formatTest(A.second, "second");
+ formatTest(cast(A) 72, "cast(A)72");
+}
+@safe unittest
+{
+ enum A : string { one = "uno", two = "dos", three = "tres" }
+ formatTest(A.three, "three");
+ formatTest(cast(A)"mill\&oacute;n", "cast(A)mill\&oacute;n");
+}
+@safe unittest
+{
+ enum A : bool { no, yes }
+ formatTest(A.yes, "yes");
+ formatTest(A.no, "no");
+}
+@safe unittest
+{
+ // Test for bug 6892
+ enum Foo { A = 10 }
+ formatTest("%s", Foo.A, "A");
+ formatTest(">%4s<", Foo.A, "> A<");
+ formatTest("%04d", Foo.A, "0010");
+ formatTest("%+2u", Foo.A, "10");
+ formatTest("%02x", Foo.A, "0a");
+ formatTest("%3o", Foo.A, " 12");
+ formatTest("%b", Foo.A, "1010");
+}
+
+@safe pure unittest
+{
+ enum A { one, two, three }
+
+ string t1 = format("[%6s] [%-6s]", A.one, A.one);
+ assert(t1 == "[ one] [one ]");
+ string t2 = format("[%10s] [%-10s]", cast(A) 10, cast(A) 10);
+ assert(t2 == "[ cast(A)" ~ "10] [cast(A)" ~ "10 ]"); // due to bug in style checker
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=8921
+@safe unittest
+{
+ enum E : char { A = 'a', B = 'b', C = 'c' }
+ E[3] e = [E.A, E.B, E.C];
+ formatTest(e, "[A, B, C]");
+
+ E[] e2 = [E.A, E.B, E.C];
+ formatTest(e2, "[A, B, C]");
+}
+
+/*
+ Pointers are formatted as hex integers.
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, scope T val, scope const ref FormatSpec!Char f)
+if (isPointer!T && !is(T == enum) && !hasToString!(T, Char))
+{
+ static if (is(typeof({ shared const void* p = val; })))
+ alias SharedOf(T) = shared(T);
+ else
+ alias SharedOf(T) = T;
+
+ const SharedOf!(void*) p = val;
+ const pnum = () @trusted { return cast(ulong) p; }();
+
+ if (f.spec == 's')
+ {
+ if (p is null)
+ {
+ writeAligned(w, "null", f);
+ return;
+ }
+ FormatSpec!Char fs = f; // fs is copy for change its values.
+ fs.spec = 'X';
+ formatValueImpl(w, pnum, fs);
+ }
+ else
+ {
+ import std.format : enforceFmt;
+ enforceFmt(f.spec == 'X' || f.spec == 'x',
+ "Expected one of %s, %x or %X for pointer type.");
+ formatValueImpl(w, pnum, f);
+ }
+}
+
+@safe pure unittest
+{
+ int* p;
+
+ string t1 = format("[%6s] [%-6s]", p, p);
+ assert(t1 == "[ null] [null ]");
+}
+
+@safe pure unittest
+{
+ int* p = null;
+ formatTest(p, "null");
+
+ auto q = () @trusted { return cast(void*) 0xFFEECCAA; }();
+ formatTest(q, "FFEECCAA");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=11782
+@safe pure unittest
+{
+ import std.range : iota;
+
+ auto a = iota(0, 10);
+ auto b = iota(0, 10);
+ auto p = () @trusted { auto p = &a; return p; }();
+
+ assert(format("%s",p) != format("%s",b));
+}
+
+@safe pure unittest
+{
+ // Test for https://issues.dlang.org/show_bug.cgi?id=7869
+ struct S
+ {
+ string toString() const { return ""; }
+ }
+ S* p = null;
+ formatTest(p, "null");
+
+ S* q = () @trusted { return cast(S*) 0xFFEECCAA; } ();
+ formatTest(q, "FFEECCAA");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=8186
+@system unittest
+{
+ class B
+ {
+ int* a;
+ this() { a = new int; }
+ alias a this;
+ }
+ formatTest(B.init, "null");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=9336
+@system pure unittest
+{
+ shared int i;
+ format("%s", &i);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=11778
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+
+ int* p = null;
+ assertThrown!FormatException(format("%d", p));
+ assertThrown!FormatException(format("%04d", () @trusted { return p + 2; } ()));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=12505
+@safe pure unittest
+{
+ void* p = null;
+ formatTest("%08X", p, "00000000");
+}
+
+/*
+ SIMD vectors are formatted as arrays.
+ */
+void formatValueImpl(Writer, V, Char)(auto ref Writer w, V val, scope const ref FormatSpec!Char f)
+if (isSIMDVector!V)
+{
+ formatValueImpl(w, val.array, f);
+}
+
+@safe unittest
+{
+ import core.simd; // cannot be selective, because float4 might not be defined
+
+ static if (is(float4))
+ {
+ version (X86)
+ {
+ version (OSX) {/* https://issues.dlang.org/show_bug.cgi?id=17823 */}
+ }
+ else
+ {
+ float4 f;
+ f.array[0] = 1;
+ f.array[1] = 2;
+ f.array[2] = 3;
+ f.array[3] = 4;
+ formatTest(f, "[1, 2, 3, 4]");
+ }
+ }
+}
+
+/*
+ Delegates are formatted by `ReturnType delegate(Parameters) FunctionAttributes`
+
+ Known bug: Because of issue https://issues.dlang.org/show_bug.cgi?id=18269
+ the FunctionAttributes might be wrong.
+ */
+void formatValueImpl(Writer, T, Char)(auto ref Writer w, scope T, scope const ref FormatSpec!Char f)
+if (isDelegate!T)
+{
+ formatValueImpl(w, T.stringof, f);
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.format : formatValue;
+
+ void func() @system { __gshared int x; ++x; throw new Exception("msg"); }
+ version (linux)
+ {
+ FormatSpec!char f;
+ auto w = appender!string();
+ formatValue(w, &func, f);
+ assert(w.data.length >= 15 && w.data[0 .. 15] == "void delegate()");
+ }
+}
+
+// string elements are formatted like UTF-8 string literals.
+void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f)
+if (is(StringTypeOf!T) && !hasToString!(T, Char) && !is(T == enum))
+{
+ import std.array : appender;
+ import std.format.write : formattedWrite, formatValue;
+ import std.range.primitives : put;
+ import std.utf : decode, UTFException;
+
+ StringTypeOf!T str = val; // https://issues.dlang.org/show_bug.cgi?id=8015
+
+ if (f.spec == 's')
+ {
+ try
+ {
+ // ignore other specifications and quote
+ for (size_t i = 0; i < str.length; )
+ {
+ auto c = decode(str, i);
+ // \uFFFE and \uFFFF are considered valid by isValidDchar,
+ // so need checking for interchange.
+ if (c == 0xFFFE || c == 0xFFFF)
+ goto LinvalidSeq;
+ }
+ put(w, '\"');
+ for (size_t i = 0; i < str.length; )
+ {
+ auto c = decode(str, i);
+ formatChar(w, c, '"');
+ }
+ put(w, '\"');
+ return;
+ }
+ catch (UTFException)
+ {
+ }
+
+ // If val contains invalid UTF sequence, formatted like HexString literal
+ LinvalidSeq:
+ static if (is(typeof(str[0]) : const(char)))
+ {
+ enum type = "";
+ alias IntArr = const(ubyte)[];
+ }
+ else static if (is(typeof(str[0]) : const(wchar)))
+ {
+ enum type = "w";
+ alias IntArr = const(ushort)[];
+ }
+ else static if (is(typeof(str[0]) : const(dchar)))
+ {
+ enum type = "d";
+ alias IntArr = const(uint)[];
+ }
+ formattedWrite(w, "[%(cast(" ~ type ~ "char) 0x%X%|, %)]", cast(IntArr) str);
+ }
+ else
+ formatValue(w, str, f);
+}
+
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ formatElement(w, "Hello World", spec);
+
+ assert(w.data == "\"Hello World\"");
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ formatElement(w, "H", spec);
+
+ assert(w.data == "\"H\"", w.data);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15888
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ ushort[] a = [0xFF_FE, 0x42];
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ formatElement(w, cast(wchar[]) a, spec);
+ assert(w.data == `[cast(wchar) 0xFFFE, cast(wchar) 0x42]`);
+
+ uint[] b = [0x0F_FF_FF_FF, 0x42];
+ w = appender!string();
+ spec = singleSpec("%s");
+ formatElement(w, cast(dchar[]) b, spec);
+ assert(w.data == `[cast(dchar) 0xFFFFFFF, cast(dchar) 0x42]`);
+}
+
+// Character elements are formatted like UTF-8 character literals.
+void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f)
+if (is(CharTypeOf!T) && !is(T == enum))
+{
+ import std.range.primitives : put;
+ import std.format.write : formatValue;
+
+ if (f.spec == 's')
+ {
+ put(w, '\'');
+ formatChar(w, val, '\'');
+ put(w, '\'');
+ }
+ else
+ formatValue(w, val, f);
+}
+
+// Maybe T is noncopyable struct, so receive it by 'auto ref'.
+void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, scope const ref FormatSpec!Char f)
+if ((!is(StringTypeOf!T) || hasToString!(T, Char)) && !is(CharTypeOf!T) || is(T == enum))
+{
+ import std.format.write : formatValue;
+
+ formatValue(w, val, f);
+}
+
+// Fix for https://issues.dlang.org/show_bug.cgi?id=1591
+int getNthInt(string kind, A...)(uint index, A args)
+{
+ return getNth!(kind, isIntegral, int)(index, args);
+}
+
+T getNth(string kind, alias Condition, T, A...)(uint index, A args)
+{
+ import std.conv : text, to;
+ import std.format : FormatException;
+
+ switch (index)
+ {
+ foreach (n, _; A)
+ {
+ case n:
+ static if (Condition!(typeof(args[n])))
+ {
+ return to!T(args[n]);
+ }
+ else
+ {
+ throw new FormatException(
+ text(kind, " expected, not ", typeof(args[n]).stringof,
+ " for argument #", index + 1));
+ }
+ }
+ default:
+ throw new FormatException(text("Missing ", kind, " argument"));
+ }
+}
+
+private bool needToSwapEndianess(Char)(scope const ref FormatSpec!Char f)
+{
+ import std.system : endian, Endian;
+
+ return endian == Endian.littleEndian && f.flPlus
+ || endian == Endian.bigEndian && f.flDash;
+}
+
+void writeAligned(Writer, T, Char)(auto ref Writer w, T s, scope const ref FormatSpec!Char f)
+if (isSomeString!T)
+{
+ FormatSpec!Char fs = f;
+ fs.flZero = false;
+ writeAligned(w, "", "", s, fs);
+}
+
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ writeAligned(w, "a本Ä", spec);
+ assert(w.data == "a本Ä", w.data);
+}
+
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%10s");
+ writeAligned(w, "a本Ä", spec);
+ assert(w.data == " a本Ä", "|" ~ w.data ~ "|");
+}
+
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%-10s");
+ writeAligned(w, "a本Ä", spec);
+ assert(w.data == "a本Ä ", w.data);
+}
+
+enum PrecisionType
+{
+ none,
+ integer,
+ fractionalDigits,
+ allDigits,
+}
+
+void writeAligned(Writer, T1, T2, T3, Char)(auto ref Writer w,
+ T1 prefix, T2 grouped, T3 suffix, scope const ref FormatSpec!Char f,
+ bool integer_precision = false)
+if (isSomeString!T1 && isSomeString!T2 && isSomeString!T3)
+{
+ writeAligned(w, prefix, grouped, "", suffix, f,
+ integer_precision ? PrecisionType.integer : PrecisionType.none);
+}
+
+void writeAligned(Writer, T1, T2, T3, T4, Char)(auto ref Writer w,
+ T1 prefix, T2 grouped, T3 fracts, T4 suffix, scope const ref FormatSpec!Char f,
+ PrecisionType p = PrecisionType.none)
+if (isSomeString!T1 && isSomeString!T2 && isSomeString!T3 && isSomeString!T4)
+{
+ // writes: left padding, prefix, leading zeros, grouped, fracts, suffix, right padding
+
+ if (p == PrecisionType.integer && f.precision == f.UNSPECIFIED)
+ p = PrecisionType.none;
+
+ import std.range.primitives : put;
+
+ long prefixWidth;
+ long groupedWidth = grouped.length; // TODO: does not take graphemes into account
+ long fractsWidth = fracts.length; // TODO: does not take graphemes into account
+ long suffixWidth;
+
+ // TODO: remove this workaround which hides issue 21815
+ if (f.width > 0)
+ {
+ prefixWidth = getWidth(prefix);
+ suffixWidth = getWidth(suffix);
+ }
+
+ auto doGrouping = f.flSeparator && groupedWidth > 0
+ && f.separators > 0 && f.separators != f.UNSPECIFIED;
+ // front = number of symbols left of the leftmost separator
+ long front = doGrouping ? (groupedWidth - 1) % f.separators + 1 : 0;
+ // sepCount = number of separators to be inserted
+ long sepCount = doGrouping ? (groupedWidth - 1) / f.separators : 0;
+
+ long trailingZeros = 0;
+ if (p == PrecisionType.fractionalDigits)
+ trailingZeros = f.precision - (fractsWidth - 1);
+ if (p == PrecisionType.allDigits && f.flHash)
+ {
+ if (grouped != "0")
+ trailingZeros = f.precision - (fractsWidth - 1) - groupedWidth;
+ else
+ {
+ trailingZeros = f.precision - fractsWidth;
+ foreach (i;0 .. fracts.length)
+ if (fracts[i] != '0' && fracts[i] != '.')
+ {
+ trailingZeros = f.precision - (fracts.length - i);
+ break;
+ }
+ }
+ }
+
+ auto nodot = fracts == "." && trailingZeros == 0 && !f.flHash;
+
+ if (nodot) fractsWidth = 0;
+
+ long width = prefixWidth + sepCount + groupedWidth + fractsWidth + trailingZeros + suffixWidth;
+ long delta = f.width - width;
+
+ // with integers, precision is considered the minimum number of digits;
+ // if digits are missing, we have to recalculate everything
+ long pregrouped = 0;
+ if (p == PrecisionType.integer && groupedWidth < f.precision)
+ {
+ pregrouped = f.precision - groupedWidth;
+ delta -= pregrouped;
+ if (doGrouping)
+ {
+ front = ((front - 1) + pregrouped) % f.separators + 1;
+ delta -= (f.precision - 1) / f.separators - sepCount;
+ }
+ }
+
+ // left padding
+ if ((!f.flZero || p == PrecisionType.integer) && delta > 0)
+ {
+ if (f.flEqual)
+ {
+ foreach (i ; 0 .. delta / 2 + ((delta % 2 == 1 && !f.flDash) ? 1 : 0))
+ put(w, ' ');
+ }
+ else if (!f.flDash)
+ {
+ foreach (i ; 0 .. delta)
+ put(w, ' ');
+ }
+ }
+
+ // prefix
+ put(w, prefix);
+
+ // leading grouped zeros
+ if (f.flZero && p != PrecisionType.integer && !f.flDash && delta > 0)
+ {
+ if (doGrouping)
+ {
+ // front2 and sepCount2 are the same as above for the leading zeros
+ long front2 = (delta + front - 1) % (f.separators + 1) + 1;
+ long sepCount2 = (delta + front - 1) / (f.separators + 1);
+ delta -= sepCount2;
+
+ // according to POSIX: if the first symbol is a separator,
+ // an additional zero is put left of it, even if that means, that
+ // the total width is one more then specified
+ if (front2 > f.separators) { front2 = 1; }
+
+ foreach (i ; 0 .. delta)
+ {
+ if (front2 == 0)
+ {
+ put(w, f.separatorChar);
+ front2 = f.separators;
+ }
+ front2--;
+
+ put(w, '0');
+ }
+
+ // separator between zeros and grouped
+ if (front == f.separators)
+ put(w, f.separatorChar);
+ }
+ else
+ foreach (i ; 0 .. delta)
+ put(w, '0');
+ }
+
+ // grouped content
+ if (doGrouping)
+ {
+ // TODO: this does not take graphemes into account
+ foreach (i;0 .. pregrouped + grouped.length)
+ {
+ if (front == 0)
+ {
+ put(w, f.separatorChar);
+ front = f.separators;
+ }
+ front--;
+
+ put(w, i < pregrouped ? '0' : grouped[cast(size_t) (i - pregrouped)]);
+ }
+ }
+ else
+ {
+ foreach (i;0 .. pregrouped)
+ put(w, '0');
+ put(w, grouped);
+ }
+
+ // fracts
+ if (!nodot)
+ put(w, fracts);
+
+ // trailing zeros
+ foreach (i ; 0 .. trailingZeros)
+ put(w, '0');
+
+ // suffix
+ put(w, suffix);
+
+ // right padding
+ if (delta > 0)
+ {
+ if (f.flEqual)
+ {
+ foreach (i ; 0 .. delta / 2 + ((delta % 2 == 1 && f.flDash) ? 1 : 0))
+ put(w, ' ');
+ }
+ else if (f.flDash)
+ {
+ foreach (i ; 0 .. delta)
+ put(w, ' ');
+ }
+ }
+}
+
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pregroupingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%20s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == " pregroupingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%-20s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pregroupingsuf ", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%020s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pre000000groupingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%-020s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pregroupingsuf ", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%20,1s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "preg,r,o,u,p,i,n,gsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%20,2s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == " pregr,ou,pi,ngsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%20,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == " pregr,oup,ingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%20,10s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == " pregroupingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%020,1s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "preg,r,o,u,p,i,n,gsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%020,2s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pre00,gr,ou,pi,ngsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%020,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pre00,0gr,oup,ingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%020,10s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pre000,00groupingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%021,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pre000,0gr,oup,ingsuf", w.data);
+
+ // According to https://github.com/dlang/phobos/pull/7112 this
+ // is defined by POSIX standard:
+ w = appender!string();
+ spec = singleSpec("%022,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pre0,000,0gr,oup,ingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%023,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pre0,000,0gr,oup,ingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec);
+ assert(w.data == "pregr,oup,ingsuf", w.data);
+}
+
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%.10s");
+ writeAligned(w, "pre", "grouping", "suf", spec, true);
+ assert(w.data == "pre00groupingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%.10,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec, true);
+ assert(w.data == "pre0,0gr,oup,ingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%25.10,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec, true);
+ assert(w.data == " pre0,0gr,oup,ingsuf", w.data);
+
+ // precision has precedence over zero flag
+ w = appender!string();
+ spec = singleSpec("%025.12,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec, true);
+ assert(w.data == " pre000,0gr,oup,ingsuf", w.data);
+
+ w = appender!string();
+ spec = singleSpec("%025.13,3s");
+ writeAligned(w, "pre", "grouping", "suf", spec, true);
+ assert(w.data == " pre0,000,0gr,oup,ingsuf", w.data);
+}
+
+@safe unittest
+{
+ assert(format("%,d", 1000) == "1,000");
+ assert(format("%,f", 1234567.891011) == "1,234,567.891011");
+ assert(format("%,?d", '?', 1000) == "1?000");
+ assert(format("%,1d", 1000) == "1,0,0,0", format("%,1d", 1000));
+ assert(format("%,*d", 4, -12345) == "-1,2345");
+ assert(format("%,*?d", 4, '_', -12345) == "-1_2345");
+ assert(format("%,6?d", '_', -12345678) == "-12_345678");
+ assert(format("%12,3.3f", 1234.5678) == " 1,234.568", "'" ~
+ format("%12,3.3f", 1234.5678) ~ "'");
+}
+
+private long getWidth(T)(T s)
+{
+ import std.algorithm.searching : all;
+ import std.uni : graphemeStride;
+
+ // check for non-ascii character
+ if (s.all!(a => a <= 0x7F)) return s.length;
+
+ //TODO: optimize this
+ long width = 0;
+ for (size_t i; i < s.length; i += graphemeStride(s, i))
+ ++width;
+ return width;
+}
+
+enum RoundingClass { ZERO, LOWER, FIVE, UPPER }
+enum RoundingMode { up, down, toZero, toNearestTiesToEven, toNearestTiesAwayFromZero }
+
+bool round(T)(ref T sequence, size_t left, size_t right, RoundingClass type, bool negative, char max = '9')
+in (left >= 0) // should be left > 0, but if you know ahead, that there's no carry, left == 0 is fine
+in (left < sequence.length)
+in (right >= 0)
+in (right <= sequence.length)
+in (right >= left)
+in (max == '9' || max == 'f' || max == 'F')
+{
+ import std.math.hardware;
+
+ auto mode = RoundingMode.toNearestTiesToEven;
+
+ if (!__ctfe)
+ {
+ // std.math's FloatingPointControl isn't available on all target platforms
+ static if (is(FloatingPointControl))
+ {
+ switch (FloatingPointControl.rounding)
+ {
+ case FloatingPointControl.roundUp:
+ mode = RoundingMode.up;
+ break;
+ case FloatingPointControl.roundDown:
+ mode = RoundingMode.down;
+ break;
+ case FloatingPointControl.roundToZero:
+ mode = RoundingMode.toZero;
+ break;
+ case FloatingPointControl.roundToNearest:
+ mode = RoundingMode.toNearestTiesToEven;
+ break;
+ default: assert(false, "Unknown floating point rounding mode");
+ }
+ }
+ }
+
+ bool roundUp = false;
+ if (mode == RoundingMode.up)
+ roundUp = type != RoundingClass.ZERO && !negative;
+ else if (mode == RoundingMode.down)
+ roundUp = type != RoundingClass.ZERO && negative;
+ else if (mode == RoundingMode.toZero)
+ roundUp = false;
+ else
+ {
+ roundUp = type == RoundingClass.UPPER;
+
+ if (type == RoundingClass.FIVE)
+ {
+ // IEEE754 allows for two different ways of implementing roundToNearest:
+
+ if (mode == RoundingMode.toNearestTiesAwayFromZero)
+ roundUp = true;
+ else
+ {
+ // Round to nearest, ties to even
+ auto last = sequence[right - 1];
+ if (last == '.') last = sequence[right - 2];
+ roundUp = (last <= '9' && last % 2 != 0) || (last > '9' && last % 2 == 0);
+ }
+ }
+ }
+
+ if (!roundUp) return false;
+
+ foreach_reverse (i;left .. right)
+ {
+ if (sequence[i] == '.') continue;
+ if (sequence[i] == max)
+ sequence[i] = '0';
+ else
+ {
+ if (max != '9' && sequence[i] == '9')
+ sequence[i] = max == 'f' ? 'a' : 'A';
+ else
+ sequence[i]++;
+ return false;
+ }
+ }
+
+ sequence[left - 1] = '1';
+ return true;
+}
+
+@safe unittest
+{
+ char[10] c;
+ size_t left = 5;
+ size_t right = 8;
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.UPPER, false) == true);
+ assert(c[4 .. 8] == "1.00");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.FIVE, false) == true);
+ assert(c[4 .. 8] == "1.00");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.LOWER, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.ZERO, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ import std.math.hardware;
+ static if (is(FloatingPointControl))
+ {
+ FloatingPointControl fpctrl;
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.UPPER, false) == true);
+ assert(c[4 .. 8] == "1.00");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.FIVE, false) == true);
+ assert(c[4 .. 8] == "1.00");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.LOWER, false) == true);
+ assert(c[4 .. 8] == "1.00");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.ZERO, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ fpctrl.rounding = FloatingPointControl.roundDown;
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.UPPER, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.FIVE, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.LOWER, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.ZERO, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ fpctrl.rounding = FloatingPointControl.roundToZero;
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.UPPER, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.FIVE, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.LOWER, false) == false);
+ assert(c[4 .. 8] == "x.99");
+
+ c[4 .. 8] = "x.99";
+ assert(round(c, left, right, RoundingClass.ZERO, false) == false);
+ assert(c[4 .. 8] == "x.99");
+ }
+}
+
+@safe unittest
+{
+ char[10] c;
+ size_t left = 5;
+ size_t right = 8;
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.UPPER, true) == false);
+ assert(c[4 .. 8] == "x8.6");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.FIVE, true) == false);
+ assert(c[4 .. 8] == "x8.6");
+
+ c[4 .. 8] = "x8.4";
+ assert(round(c, left, right, RoundingClass.FIVE, true) == false);
+ assert(c[4 .. 8] == "x8.4");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.LOWER, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.ZERO, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ import std.math.hardware;
+ static if (is(FloatingPointControl))
+ {
+ FloatingPointControl fpctrl;
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.UPPER, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.FIVE, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.LOWER, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.ZERO, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ fpctrl.rounding = FloatingPointControl.roundDown;
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.UPPER, true) == false);
+ assert(c[4 .. 8] == "x8.6");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.FIVE, true) == false);
+ assert(c[4 .. 8] == "x8.6");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.LOWER, true) == false);
+ assert(c[4 .. 8] == "x8.6");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.ZERO, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ fpctrl.rounding = FloatingPointControl.roundToZero;
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.UPPER, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.FIVE, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.LOWER, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+
+ c[4 .. 8] = "x8.5";
+ assert(round(c, left, right, RoundingClass.ZERO, true) == false);
+ assert(c[4 .. 8] == "x8.5");
+ }
+}
+
+@safe unittest
+{
+ char[10] c;
+ size_t left = 5;
+ size_t right = 8;
+
+ c[4 .. 8] = "x8.9";
+ assert(round(c, left, right, RoundingClass.UPPER, true, 'f') == false);
+ assert(c[4 .. 8] == "x8.a");
+
+ c[4 .. 8] = "x8.9";
+ assert(round(c, left, right, RoundingClass.UPPER, true, 'F') == false);
+ assert(c[4 .. 8] == "x8.A");
+
+ c[4 .. 8] = "x8.f";
+ assert(round(c, left, right, RoundingClass.UPPER, true, 'f') == false);
+ assert(c[4 .. 8] == "x9.0");
+}
+
+version (StdUnittest)
+private void formatTest(T)(T val, string expected, size_t ln = __LINE__, string fn = __FILE__)
+{
+ formatTest(val, [expected], ln, fn);
+}
+
+version (StdUnittest)
+private void formatTest(T)(string fmt, T val, string expected, size_t ln = __LINE__, string fn = __FILE__) @safe
+{
+ formatTest(fmt, val, [expected], ln, fn);
+}
+
+version (StdUnittest)
+private void formatTest(T)(T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__)
+{
+ import core.exception : AssertError;
+ import std.algorithm.searching : canFind;
+ import std.array : appender;
+ import std.conv : text;
+ import std.exception : enforce;
+ import std.format.write : formatValue;
+
+ FormatSpec!char f;
+ auto w = appender!string();
+ formatValue(w, val, f);
+ enforce!AssertError(expected.canFind(w.data),
+ text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln);
+}
+
+version (StdUnittest)
+private void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) @safe
+{
+ import core.exception : AssertError;
+ import std.algorithm.searching : canFind;
+ import std.array : appender;
+ import std.conv : text;
+ import std.exception : enforce;
+ import std.format.write : formattedWrite;
+
+ auto w = appender!string();
+ formattedWrite(w, fmt, val);
+ enforce!AssertError(expected.canFind(w.data),
+ text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln);
+}
diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d
new file mode 100644
index 00000000000..2d57e489781
--- /dev/null
+++ b/libphobos/src/std/format/package.d
@@ -0,0 +1,1787 @@
+// Written in the D programming language.
+
+/**
+This package provides string formatting functionality using
+`printf` style format strings.
+
+$(BOOKTABLE ,
+$(TR $(TH Submodule) $(TH Function Name) $(TH Description))
+$(TR
+ $(TD $(I package))
+ $(TD $(LREF format))
+ $(TD Converts its arguments according to a format string into a string.)
+)
+$(TR
+ $(TD $(I package))
+ $(TD $(LREF sformat))
+ $(TD Converts its arguments according to a format string into a buffer.)
+)
+$(TR
+ $(TD $(I package))
+ $(TD $(LREF FormatException))
+ $(TD Signals a problem while formatting.)
+)
+$(TR
+ $(TD $(MREF_ALTTEXT $(D write), std, format, write))
+ $(TD $(REF_ALTTEXT $(D formattedWrite), formattedWrite, std, format, write))
+ $(TD Converts its arguments according to a format string and writes
+ the result to an output range.)
+)
+$(TR
+ $(TD $(MREF_ALTTEXT $(D write), std, format, write))
+ $(TD $(REF_ALTTEXT $(D formatValue), formatValue, std, format, write))
+ $(TD Formats a value of any type according to a format specifier and
+ writes the result to an output range.)
+)
+$(TR
+ $(TD $(MREF_ALTTEXT $(D read), std, format, read))
+ $(TD $(REF_ALTTEXT $(D formattedRead), formattedRead, std, format, read))
+ $(TD Reads an input range according to a format string and stores the read
+ values into its arguments.)
+)
+$(TR
+ $(TD $(MREF_ALTTEXT $(D read), std, format, read))
+ $(TD $(REF_ALTTEXT $(D unformatValue), unformatValue, std, format, read))
+ $(TD Reads a value from the given input range and converts it according to
+ a format specifier.)
+)
+$(TR
+ $(TD $(MREF_ALTTEXT $(D spec), std, format, spec))
+ $(TD $(REF_ALTTEXT $(D FormatSpec), FormatSpec, std, format, spec))
+ $(TD A general handler for format strings.)
+)
+$(TR
+ $(TD $(MREF_ALTTEXT $(D spec), std, format, spec))
+ $(TD $(REF_ALTTEXT $(D singleSpec), singleSpec, std, format, spec))
+ $(TD Helper function that returns a `FormatSpec` for a single format specifier.)
+))
+
+Limitation: This package does not support localization, but
+ adheres to the rounding mode of the floating point unit, if
+ available.
+
+$(SECTION3 Format Strings)
+
+The functions contained in this package use $(I format strings). A
+format string describes the layout of another string for reading or
+writing purposes. A format string is composed of normal text
+interspersed with $(I format specifiers). A format specifier starts
+with a percentage sign $(B '%'), optionally followed by one or more
+$(I parameters) and ends with a $(I format indicator). A format
+indicator may be a simple $(I format character) or a $(I compound
+indicator).
+
+$(I Format strings) are composed according to the following grammar:
+
+$(PRE
+$(I FormatString):
+ $(I FormatStringItem) $(I FormatString)
+$(I FormatStringItem):
+ $(I Character)
+ $(I FormatSpecifier)
+$(I FormatSpecifier):
+ $(B '%') $(I Parameters) $(I FormatIndicator)
+
+$(I FormatIndicator):
+ $(I FormatCharacter)
+ $(I CompoundIndicator)
+$(I FormatCharacter):
+ $(I see remark below)
+$(I CompoundIndicator):
+ $(B '$(LPAREN)') $(I FormatString) $(B '%$(RPAREN)')
+ $(B '$(LPAREN)') $(I FormatString) $(B '%|') $(I Delimiter) $(B '%$(RPAREN)')
+$(I Delimiter)
+ $(I empty)
+ $(I Character) $(I Delimiter)
+
+$(I Parameters):
+ $(I Position) $(I Flags) $(I Width) $(I Precision) $(I Separator)
+$(I Position):
+ $(I empty)
+ $(I Integer) $(B '$')
+ $(I Integer) $(B ':') $(I Integer) $(B '$')
+ $(I Integer) $(B ':') $(B '$')
+$(I Flags):
+ $(I empty)
+ $(I Flag) $(I Flags)
+$(I Flag):
+ $(B '-')|$(B '+')|$(B '&nbsp;')|$(B '0')|$(B '#')|$(B '=')
+$(I Width):
+ $(I OptionalPositionalInteger)
+$(I Precision):
+ $(I empty)
+ $(B '.') $(I OptionalPositionalInteger)
+$(I Separator):
+ $(I empty)
+ $(B ',') $(I OptionalInteger)
+ $(B ',') $(I OptionalInteger) $(B '?')
+$(I OptionalInteger):
+ $(I empty)
+ $(I Integer)
+ $(B '*')
+$(I OptionalPositionalInteger):
+ $(I OptionalInteger)
+ $(B '*') $(I Integer) $(B '$')
+
+$(I Character)
+ $(B '%%')
+ $(I AnyCharacterExceptPercent)
+$(I Integer):
+ $(I NonZeroDigit) $(I Digits)
+$(I Digits):
+ $(I empty)
+ $(I Digit) $(I Digits)
+$(I NonZeroDigit):
+ $(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9')
+$(I Digit):
+ $(B '0')|$(B '1')|$(B '2')|$(B '3')|$(B '4')|$(B '5')|$(B '6')|$(B '7')|$(B '8')|$(B '9')
+)
+
+Note: $(I FormatCharacter) is unspecified. It can be any character
+that has no other purpose in this grammar, but it is
+recommended to assign (lower- and uppercase) letters.
+
+Note: The $(I Parameters) of a $(I CompoundIndicator) are currently
+limited to a $(B '-') flag.
+
+$(SECTION4 Format Indicator)
+
+The $(I format indicator) can either be a single character or an
+expression surrounded by $(B %\() and $(B %\)). It specifies the
+basic manner in which a value will be formatted and is the minimum
+requirement to format a value.
+
+The following characters can be used as $(I format characters):
+
+$(BOOKTABLE ,
+ $(TR $(TH FormatCharacter) $(TH Semantics))
+ $(TR $(TD $(B 's'))
+ $(TD To be formatted in a human readable format.
+ Can be used with all types.))
+ $(TR $(TD $(B 'c'))
+ $(TD To be formatted as a character.))
+ $(TR $(TD $(B 'd'))
+ $(TD To be formatted as a signed decimal integer.))
+ $(TR $(TD $(B 'u'))
+ $(TD To be formatted as a decimal image of the underlying bit representation.))
+ $(TR $(TD $(B 'b'))
+ $(TD To be formatted as a binary image of the underlying bit representation.))
+ $(TR $(TD $(B 'o'))
+ $(TD To be formatted as an octal image of the underlying bit representation.))
+ $(TR $(TD $(B 'x') / $(B 'X'))
+ $(TD To be formatted as a hexadecimal image of the underlying bit representation.))
+ $(TR $(TD $(B 'e') / $(B 'E'))
+ $(TD To be formatted as a real number in decimal scientific notation.))
+ $(TR $(TD $(B 'f') / $(B 'F'))
+ $(TD To be formatted as a real number in decimal natural notation.))
+ $(TR $(TD $(B 'g') / $(B 'G'))
+ $(TD To be formatted as a real number in decimal short notation.
+ Depending on the number, a scientific notation or
+ a natural notation is used.))
+ $(TR $(TD $(B 'a') / $(B 'A'))
+ $(TD To be formatted as a real number in hexadezimal scientific notation.))
+ $(TR $(TD $(B 'r'))
+ $(TD To be formatted as raw bytes.
+ The output may not be printable and depends on endianess.))
+)
+
+The $(I compound indicator) can be used to describe compound types
+like arrays or structs in more detail. A compound type is enclosed
+within $(B '%\(') and $(B '%\)'). The enclosed sub-format string is
+applied to individual elements. The trailing portion of the
+sub-format string following the specifier for the element is
+interpreted as the delimiter, and is therefore omitted following the
+last element. The $(B '%|') specifier may be used to explicitly
+indicate the start of the delimiter, so that the preceding portion of
+the string will be included following the last element.
+
+The $(I format string) inside of the $(I compound indicator) should
+contain exactly one $(I format specifier) (two in case of associative
+arrays), which specifies the formatting mode of the elements of the
+compound type. This $(I format specifier) can be a $(I compound
+indicator) itself.
+
+Note: Inside a $(I compound indicator), strings and characters are
+escaped automatically. To avoid this behavior, use `"%-$(LPAREN)"`
+instead of `"%$(LPAREN)"`.
+
+$(SECTION4 Flags)
+
+There are several flags that affect the outcome of the formatting.
+
+$(BOOKTABLE ,
+ $(TR $(TH Flag) $(TH Semantics))
+ $(TR $(TD $(B '-'))
+ $(TD When the formatted result is shorter then the value
+ given by the width parameter, the output is right
+ justified. With the $(B '-') flag this is changed
+ to left justification.
+
+ There are two exceptions where the $(B '-') flag has a
+ different meaning: (1) with $(B 'r') it denotes to use little
+ endian and (2) in case of a compound indicator it means that
+ no special handling of the members is applied.))
+ $(TR $(TD $(B '='))
+ $(TD When the formatted result is shorter then the value
+ given by the width parameter, the output is centered.
+ If the central position is not possible it is moved slightly
+ to the right. In this case, if $(B '-') flag is present in
+ addition to the $(B '=') flag, it is moved slightly to the left.))
+ $(TR $(TD $(B '+')&nbsp;/&nbsp;$(B '&nbsp;'))
+ $(TD Applies to numerical values. By default, positive numbers are not
+ formatted to include the `+` sign. With one of these two flags present,
+ positive numbers are preceded by a plus sign or a space.
+ When both flags are present, a plus sign is used.
+
+ In case of $(B 'r'), a big endian format is used.))
+ $(TR $(TD $(B '0'))
+ $(TD Is applied to numerical values that are printed right justified.
+ If the zero flag is present, the space left to the number is
+ filled with zeros instead of spaces.))
+ $(TR $(TD $(B '#'))
+ $(TD Denotes that an alternative output must be used. This depends on the type
+ to be formatted and the $(I format character) used. See the
+ sections below for more information.))
+)
+
+$(SECTION4 Width$(COMMA) Precision and Separator)
+
+The $(I width) parameter specifies the minimum width of the result.
+
+The meaning of $(I precision) depends on the format indicator. For
+integers it denotes the minimum number of digits printed, for
+real numbers it denotes the number of fractional digits and for
+strings and compound types it denotes the maximum number of elements
+that are included in the output.
+
+A $(I separator) is used for formatting numbers. If it is specified,
+the output is divided into chunks of three digits, separated by a $(B
+','). The number of digits in a chunk can be given explicitly by
+providing a number or a $(B '*') after the $(B ',').
+
+In all three cases the number of digits can be replaced by a $(B
+'*'). In this scenario, the next argument is used as the number of
+digits. If the argument is a negative number, the $(I precision) and
+$(I separator) parameters are considered unspecified. For $(I width),
+the absolute value is used and the $(B '-') flag is set.
+
+The $(I separator) can also be followed by a $(B '?'). In that case,
+an additional argument is used to specify the symbol that should be
+used to separate the chunks.
+
+$(SECTION4 Position)
+
+By default, the arguments are processed in the provided order. With
+the $(I position) parameter it is possible to address arguments
+directly. It is also possible to denote a series of arguments with
+two numbers separated by $(B ':'), that are all processed in the same
+way. The second number can be omitted. In that case the series ends
+with the last argument.
+
+It's also possible to use positional arguments for $(I width), $(I
+precision) and $(I separator) by adding a number and a $(B
+'$(DOLLAR)') after the $(B '*').
+
+$(SECTION4 Types)
+
+This section describes the result of combining types with format
+characters. It is organized in 2 subsections: a list of general
+information regarding the formatting of types in the presence of
+format characters and a table that contains details for every
+available combination of type and format character.
+
+When formatting types, the following rules apply:
+
+$(UL
+ $(LI If the format character is upper case, the resulting string will
+ be formatted using upper case letters.)
+ $(LI The default precision for floating point numbers is 6 digits.)
+ $(LI Rounding of floating point numbers adheres to the rounding mode
+ of the floating point unit, if available.)
+ $(LI The floating point values `NaN` and `Infinity` are formatted as
+ `nan` and `inf`, possibly preceded by $(B '+') or $(B '-') sign.)
+ $(LI Formatting reals is only supported for 64 bit reals and 80 bit reals.
+ All other reals are cast to double before they are formatted. This will
+ cause the result to be `inf` for very large numbers.)
+ $(LI Characters and strings formatted with the $(B 's') format character
+ inside of compound types are surrounded by single and double quotes
+ and unprintable characters are escaped. To avoid this, a $(B '-')
+ flag can be specified for the compound specifier
+ $(LPAREN)e.g. `"%-$(LPAREN)%s%$(RPAREN)"` instead of `"%$(LPAREN)%s%$(RPAREN)"` $(RPAREN).)
+ $(LI Structs, unions, classes and interfaces are formatted by calling a
+ `toString` method if available.
+ See $(MREF_ALTTEXT $(D module std.format.write), std, format, write) for more
+ details.)
+ $(LI Only part of these combinations can be used for reading. See
+ $(MREF_ALTTEXT $(D module std.format.read), std, format, read) for more
+ detailed information.)
+)
+
+This table contains descriptions for every possible combination of
+type and format character:
+
+$(BOOKTABLE ,
+ $(TR $(THMINWIDTH Type) $(THMINWIDTH Format Character) $(TH Formatted as...))
+ $(TR $(MULTIROW_CELL 1, `null`)
+ $(TD $(B 's'))
+ $(TD `null`)
+ )
+ $(TR $(MULTIROW_CELL 3, `bool`)
+ $(TD $(B 's'))
+ $(TD `false` or `true`)
+ )
+ $(TR $(TD $(B 'b'), $(B 'd'), $(B 'o'), $(B 'u'), $(B 'x'), $(B 'X'))
+ $(TD As the integrals 0 or 1 with the same format character.
+
+ $(I Please note, that $(B 'o') and $(B 'x') with $(B '#') flag
+ might produce unexpected results due to special handling of
+ the value 0.))
+ )
+ $(TR $(TD $(B 'r'))
+ $(TD `\0` or `\1`)
+ )
+ $(TR $(MULTIROW_CELL 4, $(I Integral))
+ $(TD $(B 's'), $(B 'd'))
+ $(TD A signed decimal number. The $(B '#') flag is ignored.)
+ )
+ $(TR $(TD $(B 'b'), $(B 'o'), $(B 'u'), $(B 'x'), $(B 'X'))
+ $(TD An unsigned binary, decimal, octal or hexadecimal number.
+
+ In case of $(B 'o') and $(B 'x'), the $(B '#') flag
+ denotes that the number must be preceded by `0` and `0x`, with
+ the exception of the value 0, where this does not apply. For
+ $(B 'b') and $(B 'u') the $(B '#') flag has no effect.)
+ )
+ $(TR $(TD $(B 'e'), $(B 'E'), $(B 'f'), $(B 'F'), $(B 'g'), $(B 'G'), $(B 'a'), $(B 'A'))
+ $(TD As a floating point value with the same specifier.
+
+ Default precision is large enough to add all digits
+ of the integral value.
+
+ In case of ($B 'a') and $(B 'A'), the integral digit can be
+ any hexadecimal digit.
+ )
+ )
+ $(TR $(TD $(B 'r'))
+ $(TD Characters taken directly from the binary representation.)
+ )
+ $(TR $(MULTIROW_CELL 5, $(I Floating Point))
+ $(TD $(B 'e'), $(B 'E'))
+ $(TD Scientific notation: Exactly one integral digit followed by a dot
+ and fractional digits, followed by the exponent.
+ The exponent is formatted as $(B 'e') followed by
+ a $(B '+') or $(B '-') sign, followed by at least
+ two digits.
+
+ When there are no fractional digits and the $(B '#') flag
+ is $(I not) present, the dot is omitted.)
+ )
+ $(TR $(TD $(B 'f'), $(B 'F'))
+ $(TD Natural notation: Integral digits followed by a dot and
+ fractional digits.
+
+ When there are no fractional digits and the $(B '#') flag
+ is $(I not) present, the dot is omitted.
+
+ $(I Please note: the difference between $(B 'f') and $(B 'F')
+ is only visible for `NaN` and `Infinity`.))
+ )
+ $(TR $(TD $(B 's'), $(B 'g'), $(B 'G'))
+ $(TD Short notation: If the absolute value is larger than `10 ^^ precision`
+ or smaller than `0.0001`, the scientific notation is used.
+ If not, the natural notation is applied.
+
+ In both cases $(I precision) denotes the count of all digits, including
+ the integral digits. Trailing zeros (including a trailing dot) are removed.
+
+ If $(B '#') flag is present, trailing zeros are not removed.)
+ )
+ $(TR $(TD $(B 'a'), $(B 'A'))
+ $(TD Hexadecimal scientific notation: `0x` followed by `1`
+ (or `0` in case of value zero or denormalized number)
+ followed by a dot, fractional digits in hexadecimal
+ notation and an exponent. The exponent is build by `p`,
+ followed by a sign and the exponent in $(I decimal) notation.
+
+ When there are no fractional digits and the $(B '#') flag
+ is $(I not) present, the dot is omitted.)
+ )
+ $(TR $(TD $(B 'r'))
+ $(TD Characters taken directly from the binary representation.)
+ )
+ $(TR $(MULTIROW_CELL 3, $(I Character))
+ $(TD $(B 's'), $(B 'c'))
+ $(TD As the character.
+
+ Inside of a compound indicator $(B 's') is treated differently: The
+ character is surrounded by single quotes and non printable
+ characters are escaped. This can be avoided by preceding
+ the compound indicator with a $(B '-') flag
+ $(LPAREN)e.g. `"%-$(LPAREN)%s%$(RPAREN)"`$(RPAREN).)
+ )
+ $(TR $(TD $(B 'b'), $(B 'd'), $(B 'o'), $(B 'u'), $(B 'x'), $(B 'X'))
+ $(TD As the integral that represents the character.)
+ )
+ $(TR $(TD $(B 'r'))
+ $(TD Characters taken directly from the binary representation.)
+ )
+ $(TR $(MULTIROW_CELL 3, $(I String))
+ $(TD $(B 's'))
+ $(TD The sequence of characters that form the string.
+
+ Inside of a compound indicator the string is surrounded by double quotes
+ and non printable characters are escaped. This can be avoided
+ by preceding the compound indicator with a $(B '-') flag
+ $(LPAREN)e.g. `"%-$(LPAREN)%s%$(RPAREN)"`$(RPAREN).)
+ )
+ $(TR $(TD $(B 'r'))
+ $(TD The sequence of characters, each formatted with $(B 'r').)
+ )
+ $(TR $(TD compound)
+ $(TD As an array of characters.)
+ )
+ $(TR $(MULTIROW_CELL 3, $(I Array))
+ $(TD $(B 's'))
+ $(TD When the elements are characters, the array is formatted as
+ a string. In all other cases the array is surrounded by square brackets
+ and the elements are separated by a comma and a space. If the elements
+ are strings, they are surrounded by double quotes and non
+ printable characters are escaped.)
+ )
+ $(TR $(TD $(B 'r'))
+ $(TD The sequence of the elements, each formatted with $(B 'r').)
+ )
+ $(TR $(TD compound)
+ $(TD The sequence of the elements, each formatted according to the specifications
+ given inside of the compound specifier.)
+ )
+ $(TR $(MULTIROW_CELL 2, $(I Associative Array))
+ $(TD $(B 's'))
+ $(TD As a sequence of the elements in unpredictable order. The output is
+ surrounded by square brackets. The elements are separated by a
+ comma and a space. The elements are formatted as `key:value`.)
+ )
+ $(TR $(TD compound)
+ $(TD As a sequence of the elements in unpredictable order. Each element
+ is formatted according to the specifications given inside of the
+ compound specifier. The first specifier is used for formatting
+ the key and the second specifier is used for formatting the value.
+ The order can be changed with positional arguments. For example
+ `"%(%2$s (%1$s), %)"` will write the value, followed by the key in
+ parenthesis.)
+ )
+ $(TR $(MULTIROW_CELL 2, $(I Enum))
+ $(TD $(B 's'))
+ $(TD The name of the value. If the name is not available, the base value
+ is used, preceeded by a cast.)
+ )
+ $(TR $(TD All, but $(B 's'))
+ $(TD Enums can be formatted with all format characters that can be used
+ with the base value. In that case they are formatted like the base value.)
+ )
+ $(TR $(MULTIROW_CELL 3, $(I Input Range))
+ $(TD $(B 's'))
+ $(TD When the elements of the range are characters, they are written like a string.
+ In all other cases, the elements are enclosed by square brackets and separated
+ by a comma and a space.)
+ )
+ $(TR $(TD $(B 'r'))
+ $(TD The sequence of the elements, each formatted with $(B 'r').)
+ )
+ $(TR $(TD compound)
+ $(TD The sequence of the elements, each formatted according to the specifications
+ given inside of the compound specifier.)
+ )
+ $(TR $(MULTIROW_CELL 1, $(I Struct))
+ $(TD $(B 's'))
+ $(TD When the struct has neither an applicable `toString`
+ nor is an input range, it is formatted as follows:
+ `StructType(field1, field2, ...)`.)
+ )
+ $(TR $(MULTIROW_CELL 1, $(I Class))
+ $(TD $(B 's'))
+ $(TD When the class has neither an applicable `toString`
+ nor is an input range, it is formatted as the
+ fully qualified name of the class.)
+ )
+ $(TR $(MULTIROW_CELL 1, $(I Union))
+ $(TD $(B 's'))
+ $(TD When the union has neither an applicable `toString`
+ nor is an input range, it is formatted as its base name.)
+ )
+ $(TR $(MULTIROW_CELL 2, $(I Pointer))
+ $(TD $(B 's'))
+ $(TD A null pointer is formatted as 'null'. All other pointers are
+ formatted as hexadecimal numbers with the format character $(B 'X').)
+ )
+ $(TR $(TD $(B 'x'), $(B 'X'))
+ $(TD Formatted as a hexadecimal number.)
+ )
+ $(TR $(MULTIROW_CELL 3, $(I SIMD vector))
+ $(TD $(B 's'))
+ $(TD The array is surrounded by square brackets
+ and the elements are separated by a comma and a space.)
+ )
+ $(TR $(TD $(B 'r'))
+ $(TD The sequence of the elements, each formatted with $(B 'r').)
+ )
+ $(TR $(TD compound)
+ $(TD The sequence of the elements, each formatted according to the specifications
+ given inside of the compound specifier.)
+ )
+ $(TR $(MULTIROW_CELL 1, $(I Delegate))
+ $(TD $(B 's'), $(B 'r'), compound)
+ $(TD As the `.stringof` of this delegate treated as a string.
+
+ $(I Please note: The implementation is currently buggy
+ and its use is discouraged.))
+ )
+)
+
+Copyright: Copyright The D Language Foundation 2000-2021.
+
+Macros:
+SUBREF = $(REF_ALTTEXT $2, $2, std, format, $1)$(NBSP)
+MULTIROW_CELL = <td rowspan="$1">$+</td>
+THMINWIDTH = <th scope="col" width="20%">$0</th>
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
+Andrei Alexandrescu), and Kenji Hara
+
+Source: $(PHOBOSSRC std/format.d)
+ */
+module std.format;
+
+/// Simple use:
+@safe unittest
+{
+ // Easiest way is to use `%s` everywhere:
+ assert(format("I got %s %s for %s euros.", 30, "eggs", 5.27) == "I got 30 eggs for 5.27 euros.");
+
+ // Other format characters provide more control:
+ assert(format("I got %b %(%X%) for %f euros.", 30, "eggs", 5.27) == "I got 11110 65676773 for 5.270000 euros.");
+}
+
+/// Compound specifiers allow formatting arrays and other compound types:
+@safe unittest
+{
+/*
+The trailing end of the sub-format string following the specifier for
+each item is interpreted as the array delimiter, and is therefore
+omitted following the last array item:
+ */
+ assert(format("My items are %(%s %).", [1,2,3]) == "My items are 1 2 3.");
+ assert(format("My items are %(%s, %).", [1,2,3]) == "My items are 1, 2, 3.");
+
+/*
+The "%|" delimiter specifier may be used to indicate where the
+delimiter begins, so that the portion of the format string prior to
+it will be retained in the last array element:
+ */
+ assert(format("My items are %(-%s-%|, %).", [1,2,3]) == "My items are -1-, -2-, -3-.");
+
+/*
+These compound format specifiers may be nested in the case of a
+nested array argument:
+ */
+ auto mat = [[1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9]];
+
+ assert(format("%(%(%d %) - %)", mat), "1 2 3 - 4 5 6 - 7 8 9");
+ assert(format("[%(%(%d %) - %)]", mat), "[1 2 3 - 4 5 6 - 7 8 9]");
+ assert(format("[%([%(%d %)]%| - %)]", mat), "[1 2 3] - [4 5 6] - [7 8 9]");
+
+/*
+Strings and characters are escaped automatically inside compound
+format specifiers. To avoid this behavior, use "%-(" instead of "%(":
+ */
+ assert(format("My friends are %s.", ["John", "Nancy"]) == `My friends are ["John", "Nancy"].`);
+ assert(format("My friends are %(%s, %).", ["John", "Nancy"]) == `My friends are "John", "Nancy".`);
+ assert(format("My friends are %-(%s, %).", ["John", "Nancy"]) == `My friends are John, Nancy.`);
+}
+
+/// Using parameters:
+@safe unittest
+{
+ // Flags can be used to influence to outcome:
+ assert(format("%g != %+#g", 3.14, 3.14) == "3.14 != +3.14000");
+
+ // Width and precision help to arrange the formatted result:
+ assert(format(">%10.2f<", 1234.56789) == "> 1234.57<");
+
+ // Numbers can be grouped:
+ assert(format("%,4d", int.max) == "21,4748,3647");
+
+ // It's possible to specify the position of an argument:
+ assert(format("%3$s %1$s", 3, 17, 5) == "5 3");
+}
+
+/// Providing parameters as arguments:
+@safe unittest
+{
+ // Width as argument
+ assert(format(">%*s<", 10, "abc") == "> abc<");
+
+ // Precision as argument
+ assert(format(">%.*f<", 5, 123.2) == ">123.20000<");
+
+ // Grouping as argument
+ assert(format("%,*d", 1, int.max) == "2,1,4,7,4,8,3,6,4,7");
+
+ // Grouping separator as argument
+ assert(format("%,3?d", '_', int.max) == "2_147_483_647");
+
+ // All at once
+ assert(format("%*.*,*?d", 20, 15, 6, '/', int.max) == " 000/002147/483647");
+}
+
+public import std.format.read;
+public import std.format.spec;
+public import std.format.write;
+
+import std.exception : enforce;
+import std.range.primitives : isInputRange;
+import std.traits : CharTypeOf, isSomeChar, isSomeString, StringTypeOf;
+import std.format.internal.write : hasToString;
+
+/**
+Signals an issue encountered while formatting.
+ */
+class FormatException : Exception
+{
+ /// Generic constructor.
+ @safe @nogc pure nothrow
+ this()
+ {
+ super("format error");
+ }
+
+ /**
+ Creates a new instance of `FormatException`.
+
+ Params:
+ msg = message of the exception
+ fn = file name of the file where the exception was created (optional)
+ ln = line number of the file where the exception was created (optional)
+ next = for internal use, should always be null (optional)
+ */
+ @safe @nogc pure nothrow
+ this(string msg, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null)
+ {
+ super(msg, fn, ln, next);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ assertThrown!FormatException(format("%d", "foo"));
+}
+
+package alias enforceFmt = enforce!FormatException;
+
+// @@@DEPRECATED_[2.107.0]@@@
+deprecated("formatElement was accidentally made public and will be removed in 2.107.0")
+void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f)
+if (is(StringTypeOf!T) && !hasToString!(T, Char) && !is(T == enum))
+{
+ import std.format.internal.write : fe = formatElement;
+
+ fe(w, val, f);
+}
+
+// @@@DEPRECATED_[2.107.0]@@@
+deprecated("formatElement was accidentally made public and will be removed in 2.107.0")
+void formatElement(Writer, T, Char)(auto ref Writer w, T val, scope const ref FormatSpec!Char f)
+if (is(CharTypeOf!T) && !is(T == enum))
+{
+ import std.format.internal.write : fe = formatElement;
+
+ fe(w, val, f);
+}
+
+// @@@DEPRECATED_[2.107.0]@@@
+deprecated("formatElement was accidentally made public and will be removed in 2.107.0")
+void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, scope const ref FormatSpec!Char f)
+if ((!is(StringTypeOf!T) || hasToString!(T, Char)) && !is(CharTypeOf!T) || is(T == enum))
+{
+ import std.format.internal.write : fe = formatElement;
+
+ fe(w, val, f);
+}
+
+// Like NullSink, but toString() isn't even called at all. Used to test the format string.
+package struct NoOpSink
+{
+ void put(E)(scope const E) pure @safe @nogc nothrow {}
+}
+
+// @@@DEPRECATED_[2.107.0]@@@
+deprecated("unformatElement was accidentally made public and will be removed in 2.107.0")
+T unformatElement(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+if (isInputRange!Range)
+{
+ import std.format.internal.read : ue = unformatElement;
+
+ return ue(input, spec);
+}
+
+// Used to check format strings are compatible with argument types
+package(std) enum checkFormatException(alias fmt, Args...) =
+{
+ import std.conv : text;
+
+ try
+ {
+ auto n = .formattedWrite(NoOpSink(), fmt, Args.init);
+
+ enforceFmt(n == Args.length, text("Orphan format arguments: args[", n, "..", Args.length, "]"));
+ }
+ catch (Exception e)
+ return e.msg;
+ return null;
+}();
+
+/**
+Converts its arguments according to a format string into a string.
+
+The second version of `format` takes the format string as template
+argument. In this case, it is checked for consistency at
+compile-time and produces slightly faster code, because the length of
+the output buffer can be estimated in advance.
+
+Params:
+ fmt = a $(MREF_ALTTEXT format string, std,format)
+ args = a variadic list of arguments to be formatted
+ Char = character type of `fmt`
+ Args = a variadic list of types of the arguments
+
+Returns:
+ The formatted string.
+
+Throws:
+ A $(LREF FormatException) if formatting did not succeed.
+
+See_Also:
+ $(LREF sformat) for a variant, that tries to avoid garbage collection.
+ */
+immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args)
+if (isSomeChar!Char)
+{
+ import std.array : appender;
+
+ auto w = appender!(immutable(Char)[]);
+ auto n = formattedWrite(w, fmt, args);
+ version (all)
+ {
+ // In the future, this check will be removed to increase consistency
+ // with formattedWrite
+ import std.conv : text;
+ enforceFmt(n == args.length, text("Orphan format arguments: args[", n, "..", args.length, "]"));
+ }
+ return w.data;
+}
+
+///
+@safe pure unittest
+{
+ assert(format("Here are %d %s.", 3, "apples") == "Here are 3 apples.");
+
+ assert("Increase: %7.2f %%".format(17.4285) == "Increase: 17.43 %");
+}
+
+@safe pure unittest
+{
+ import std.exception : assertCTFEable, assertThrown;
+
+ assertCTFEable!(
+ {
+ assert(format("foo") == "foo");
+ assert(format("foo%%") == "foo%");
+ assert(format("foo%s", 'C') == "fooC");
+ assert(format("%s foo", "bar") == "bar foo");
+ assert(format("%s foo %s", "bar", "abc") == "bar foo abc");
+ assert(format("foo %d", -123) == "foo -123");
+ assert(format("foo %d", 123) == "foo 123");
+
+ assertThrown!FormatException(format("foo %s"));
+ assertThrown!FormatException(format("foo %s", 123, 456));
+
+ assert(format("hel%slo%s%s%s", "world", -138, 'c', true) == "helworldlo-138ctrue");
+ });
+
+ assert(is(typeof(format("happy")) == string));
+ assert(is(typeof(format("happy"w)) == wstring));
+ assert(is(typeof(format("happy"d)) == dstring));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=16661
+@safe pure unittest
+{
+ assert(format("%.2f"d, 0.4) == "0.40");
+ assert("%02d"d.format(1) == "01"d);
+}
+
+@safe unittest
+{
+ int i;
+ string s;
+
+ s = format("hello world! %s %s %s%s%s", true, 57, 1_000_000_000, 'x', " foo");
+ assert(s == "hello world! true 57 1000000000x foo");
+
+ s = format("%s %A %s", 1.67, -1.28, float.nan);
+ assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s);
+
+ s = format("%x %X", 0x1234AF, 0xAFAFAFAF);
+ assert(s == "1234af AFAFAFAF");
+
+ s = format("%b %o", 0x1234AF, 0xAFAFAFAF);
+ assert(s == "100100011010010101111 25753727657");
+
+ s = format("%d %s", 0x1234AF, 0xAFAFAFAF);
+ assert(s == "1193135 2947526575");
+}
+
+@safe unittest
+{
+ import std.conv : octal;
+
+ string s;
+ int i;
+
+ s = format("%#06.*f", 2, 12.345);
+ assert(s == "012.35");
+
+ s = format("%#0*.*f", 6, 2, 12.345);
+ assert(s == "012.35");
+
+ s = format("%7.4g:", 12.678);
+ assert(s == " 12.68:");
+
+ s = format("%7.4g:", 12.678L);
+ assert(s == " 12.68:");
+
+ s = format("%04f|%05d|%#05x|%#5x", -4.0, -10, 1, 1);
+ assert(s == "-4.000000|-0010|0x001| 0x1");
+
+ i = -10;
+ s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(s == "-10|-10|-10|-10|-10.0000");
+
+ i = -5;
+ s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(s == "-5| -5|-05|-5|-5.0000");
+
+ i = 0;
+ s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(s == "0| 0|000|0|0.0000");
+
+ i = 5;
+ s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(s == "5| 5|005|5|5.0000");
+
+ i = 10;
+ s = format("%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(s == "10| 10|010|10|10.0000");
+
+ s = format("%.0d", 0);
+ assert(s == "0");
+
+ s = format("%.g", .34);
+ assert(s == "0.3");
+
+ s = format("%.0g", .34);
+ assert(s == "0.3");
+
+ s = format("%.2g", .34);
+ assert(s == "0.34");
+
+ s = format("%0.0008f", 1e-08);
+ assert(s == "0.00000001");
+
+ s = format("%0.0008f", 1e-05);
+ assert(s == "0.00001000");
+
+ s = "helloworld";
+ string r;
+ r = format("%.2s", s[0 .. 5]);
+ assert(r == "he");
+ r = format("%.20s", s[0 .. 5]);
+ assert(r == "hello");
+ r = format("%8s", s[0 .. 5]);
+ assert(r == " hello");
+
+ byte[] arrbyte = new byte[4];
+ arrbyte[0] = 100;
+ arrbyte[1] = -99;
+ arrbyte[3] = 0;
+ r = format("%s", arrbyte);
+ assert(r == "[100, -99, 0, 0]");
+
+ ubyte[] arrubyte = new ubyte[4];
+ arrubyte[0] = 100;
+ arrubyte[1] = 200;
+ arrubyte[3] = 0;
+ r = format("%s", arrubyte);
+ assert(r == "[100, 200, 0, 0]");
+
+ short[] arrshort = new short[4];
+ arrshort[0] = 100;
+ arrshort[1] = -999;
+ arrshort[3] = 0;
+ r = format("%s", arrshort);
+ assert(r == "[100, -999, 0, 0]");
+
+ ushort[] arrushort = new ushort[4];
+ arrushort[0] = 100;
+ arrushort[1] = 20_000;
+ arrushort[3] = 0;
+ r = format("%s", arrushort);
+ assert(r == "[100, 20000, 0, 0]");
+
+ int[] arrint = new int[4];
+ arrint[0] = 100;
+ arrint[1] = -999;
+ arrint[3] = 0;
+ r = format("%s", arrint);
+ assert(r == "[100, -999, 0, 0]");
+
+ long[] arrlong = new long[4];
+ arrlong[0] = 100;
+ arrlong[1] = -999;
+ arrlong[3] = 0;
+ r = format("%s", arrlong);
+ assert(r == "[100, -999, 0, 0]");
+
+ ulong[] arrulong = new ulong[4];
+ arrulong[0] = 100;
+ arrulong[1] = 999;
+ arrulong[3] = 0;
+ r = format("%s", arrulong);
+ assert(r == "[100, 999, 0, 0]");
+
+ string[] arr2 = new string[4];
+ arr2[0] = "hello";
+ arr2[1] = "world";
+ arr2[3] = "foo";
+ r = format("%s", arr2);
+ assert(r == `["hello", "world", "", "foo"]`);
+
+ r = format("%.8d", 7);
+ assert(r == "00000007");
+ r = format("%.8x", 10);
+ assert(r == "0000000a");
+
+ r = format("%-3d", 7);
+ assert(r == "7 ");
+
+ r = format("%-1*d", 4, 3);
+ assert(r == "3 ");
+
+ r = format("%*d", -3, 7);
+ assert(r == "7 ");
+
+ r = format("%.*d", -3, 7);
+ assert(r == "7");
+
+ r = format("%-1.*f", 2, 3.1415);
+ assert(r == "3.14");
+
+ r = format("abc"c);
+ assert(r == "abc");
+
+ //format() returns the same type as inputted.
+ wstring wr;
+ wr = format("def"w);
+ assert(wr == "def"w);
+
+ dstring dr;
+ dr = format("ghi"d);
+ assert(dr == "ghi"d);
+
+ // Empty static character arrays work as well
+ const char[0] cempty;
+ assert(format("test%spath", cempty) == "testpath");
+ const wchar[0] wempty;
+ assert(format("test%spath", wempty) == "testpath");
+ const dchar[0] dempty;
+ assert(format("test%spath", dempty) == "testpath");
+
+ void* p = () @trusted { return cast(void*) 0xDEADBEEF; } ();
+ r = format("%s", p);
+ assert(r == "DEADBEEF");
+
+ r = format("%#x", 0xabcd);
+ assert(r == "0xabcd");
+ r = format("%#X", 0xABCD);
+ assert(r == "0XABCD");
+
+ r = format("%#o", octal!12345);
+ assert(r == "012345");
+ r = format("%o", 9);
+ assert(r == "11");
+ r = format("%#o", 0); // https://issues.dlang.org/show_bug.cgi?id=15663
+ assert(r == "0");
+
+ r = format("%+d", 123);
+ assert(r == "+123");
+ r = format("%+d", -123);
+ assert(r == "-123");
+ r = format("% d", 123);
+ assert(r == " 123");
+ r = format("% d", -123);
+ assert(r == "-123");
+
+ r = format("%%");
+ assert(r == "%");
+
+ r = format("%d", true);
+ assert(r == "1");
+ r = format("%d", false);
+ assert(r == "0");
+
+ r = format("%d", 'a');
+ assert(r == "97");
+ wchar wc = 'a';
+ r = format("%d", wc);
+ assert(r == "97");
+ dchar dc = 'a';
+ r = format("%d", dc);
+ assert(r == "97");
+
+ byte b = byte.max;
+ r = format("%x", b);
+ assert(r == "7f");
+ r = format("%x", ++b);
+ assert(r == "80");
+ r = format("%x", ++b);
+ assert(r == "81");
+
+ short sh = short.max;
+ r = format("%x", sh);
+ assert(r == "7fff");
+ r = format("%x", ++sh);
+ assert(r == "8000");
+ r = format("%x", ++sh);
+ assert(r == "8001");
+
+ i = int.max;
+ r = format("%x", i);
+ assert(r == "7fffffff");
+ r = format("%x", ++i);
+ assert(r == "80000000");
+ r = format("%x", ++i);
+ assert(r == "80000001");
+
+ r = format("%x", 10);
+ assert(r == "a");
+ r = format("%X", 10);
+ assert(r == "A");
+ r = format("%x", 15);
+ assert(r == "f");
+ r = format("%X", 15);
+ assert(r == "F");
+
+ Object c = null;
+ r = () @trusted { return format("%s", c); } ();
+ assert(r == "null");
+
+ enum TestEnum
+ {
+ Value1, Value2
+ }
+ r = format("%s", TestEnum.Value2);
+ assert(r == "Value2");
+
+ immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
+ r = () @trusted { return format("%s", aa.values); } ();
+ assert(r == `["hello", "betty"]` || r == `["betty", "hello"]`);
+ r = format("%s", aa);
+ assert(r == `[3:"hello", 4:"betty"]` || r == `[4:"betty", 3:"hello"]`);
+
+ static const dchar[] ds = ['a','b'];
+ for (int j = 0; j < ds.length; ++j)
+ {
+ r = format(" %d", ds[j]);
+ if (j == 0)
+ assert(r == " 97");
+ else
+ assert(r == " 98");
+ }
+
+ r = format(">%14d<, %s", 15, [1,2,3]);
+ assert(r == "> 15<, [1, 2, 3]");
+
+ assert(format("%8s", "bar") == " bar");
+ assert(format("%8s", "b\u00e9ll\u00f4") == " b\u00e9ll\u00f4");
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ auto tmp = format("%,d", 1000);
+ assert(tmp == "1,000", "'" ~ tmp ~ "'");
+
+ tmp = format("%,?d", 'z', 1234567);
+ assert(tmp == "1z234z567", "'" ~ tmp ~ "'");
+
+ tmp = format("%10,?d", 'z', 1234567);
+ assert(tmp == " 1z234z567", "'" ~ tmp ~ "'");
+
+ tmp = format("%11,2?d", 'z', 1234567);
+ assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'");
+
+ tmp = format("%11,*?d", 2, 'z', 1234567);
+ assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'");
+
+ tmp = format("%11,*d", 2, 1234567);
+ assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'");
+
+ tmp = format("%11,2d", 1234567);
+ assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'");
+ });
+}
+
+@safe unittest
+{
+ auto tmp = format("%,f", 1000.0);
+ assert(tmp == "1,000.000000", "'" ~ tmp ~ "'");
+
+ tmp = format("%,f", 1234567.891011);
+ assert(tmp == "1,234,567.891011", "'" ~ tmp ~ "'");
+
+ tmp = format("%,f", -1234567.891011);
+ assert(tmp == "-1,234,567.891011", "'" ~ tmp ~ "'");
+
+ tmp = format("%,2f", 1234567.891011);
+ assert(tmp == "1,23,45,67.891011", "'" ~ tmp ~ "'");
+
+ tmp = format("%18,f", 1234567.891011);
+ assert(tmp == " 1,234,567.891011", "'" ~ tmp ~ "'");
+
+ tmp = format("%18,?f", '.', 1234567.891011);
+ assert(tmp == " 1.234.567.891011", "'" ~ tmp ~ "'");
+
+ tmp = format("%,?.3f", 'ä', 1234567.891011);
+ assert(tmp == "1ä234ä567.891", "'" ~ tmp ~ "'");
+
+ tmp = format("%,*?.3f", 1, 'ä', 1234567.891011);
+ assert(tmp == "1ä2ä3ä4ä5ä6ä7.891", "'" ~ tmp ~ "'");
+
+ tmp = format("%,4?.3f", '_', 1234567.891011);
+ assert(tmp == "123_4567.891", "'" ~ tmp ~ "'");
+
+ tmp = format("%12,3.3f", 1234.5678);
+ assert(tmp == " 1,234.568", "'" ~ tmp ~ "'");
+
+ tmp = format("%,e", 3.141592653589793238462);
+ assert(tmp == "3.141593e+00", "'" ~ tmp ~ "'");
+
+ tmp = format("%15,e", 3.141592653589793238462);
+ assert(tmp == " 3.141593e+00", "'" ~ tmp ~ "'");
+
+ tmp = format("%15,e", -3.141592653589793238462);
+ assert(tmp == " -3.141593e+00", "'" ~ tmp ~ "'");
+
+ tmp = format("%.4,*e", 2, 3.141592653589793238462);
+ assert(tmp == "3.1416e+00", "'" ~ tmp ~ "'");
+
+ tmp = format("%13.4,*e", 2, 3.141592653589793238462);
+ assert(tmp == " 3.1416e+00", "'" ~ tmp ~ "'");
+
+ tmp = format("%,.0f", 3.14);
+ assert(tmp == "3", "'" ~ tmp ~ "'");
+
+ tmp = format("%3,g", 1_000_000.123456);
+ assert(tmp == "1e+06", "'" ~ tmp ~ "'");
+
+ tmp = format("%19,?f", '.', -1234567.891011);
+ assert(tmp == " -1.234.567.891011", "'" ~ tmp ~ "'");
+}
+
+// Test for multiple indexes
+@safe unittest
+{
+ auto tmp = format("%2:5$s", 1, 2, 3, 4, 5);
+ assert(tmp == "2345", tmp);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18047
+@safe unittest
+{
+ auto cmp = " 123,456";
+ assert(cmp.length == 12, format("%d", cmp.length));
+ auto tmp = format("%12,d", 123456);
+ assert(tmp.length == 12, format("%d", tmp.length));
+
+ assert(tmp == cmp, "'" ~ tmp ~ "'");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17459
+@safe unittest
+{
+ auto cmp = "100";
+ auto tmp = format("%0d", 100);
+ assert(tmp == cmp, tmp);
+
+ cmp = "0100";
+ tmp = format("%04d", 100);
+ assert(tmp == cmp, tmp);
+
+ cmp = "0,000,000,100";
+ tmp = format("%012,3d", 100);
+ assert(tmp == cmp, tmp);
+
+ cmp = "0,000,001,000";
+ tmp = format("%012,3d", 1_000);
+ assert(tmp == cmp, tmp);
+
+ cmp = "0,000,100,000";
+ tmp = format("%012,3d", 100_000);
+ assert(tmp == cmp, tmp);
+
+ cmp = "0,001,000,000";
+ tmp = format("%012,3d", 1_000_000);
+ assert(tmp == cmp, tmp);
+
+ cmp = "0,100,000,000";
+ tmp = format("%012,3d", 100_000_000);
+ assert(tmp == cmp, tmp);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17459
+@safe unittest
+{
+ auto cmp = "100,000";
+ auto tmp = format("%06,d", 100_000);
+ assert(tmp == cmp, tmp);
+
+ cmp = "100,000";
+ tmp = format("%07,d", 100_000);
+ assert(tmp == cmp, tmp);
+
+ cmp = "0,100,000";
+ tmp = format("%08,d", 100_000);
+ assert(tmp == cmp, tmp);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20288
+@safe unittest
+{
+ string s = format("%,.2f", double.nan);
+ assert(s == "nan", s);
+
+ s = format("%,.2F", double.nan);
+ assert(s == "NAN", s);
+
+ s = format("%,.2f", -double.nan);
+ assert(s == "-nan", s);
+
+ s = format("%,.2F", -double.nan);
+ assert(s == "-NAN", s);
+
+ string g = format("^%13s$", "nan");
+ string h = "^ nan$";
+ assert(g == h, "\ngot:" ~ g ~ "\nexp:" ~ h);
+ string a = format("^%13,3.2f$", double.nan);
+ string b = format("^%13,3.2F$", double.nan);
+ string c = format("^%13,3.2f$", -double.nan);
+ string d = format("^%13,3.2F$", -double.nan);
+ assert(a == "^ nan$", "\ngot:'"~ a ~ "'\nexp:'^ nan$'");
+ assert(b == "^ NAN$", "\ngot:'"~ b ~ "'\nexp:'^ NAN$'");
+ assert(c == "^ -nan$", "\ngot:'"~ c ~ "'\nexp:'^ -nan$'");
+ assert(d == "^ -NAN$", "\ngot:'"~ d ~ "'\nexp:'^ -NAN$'");
+
+ a = format("^%-13,3.2f$", double.nan);
+ b = format("^%-13,3.2F$", double.nan);
+ c = format("^%-13,3.2f$", -double.nan);
+ d = format("^%-13,3.2F$", -double.nan);
+ assert(a == "^nan $", "\ngot:'"~ a ~ "'\nexp:'^nan $'");
+ assert(b == "^NAN $", "\ngot:'"~ b ~ "'\nexp:'^NAN $'");
+ assert(c == "^-nan $", "\ngot:'"~ c ~ "'\nexp:'^-nan $'");
+ assert(d == "^-NAN $", "\ngot:'"~ d ~ "'\nexp:'^-NAN $'");
+
+ a = format("^%+13,3.2f$", double.nan);
+ b = format("^%+13,3.2F$", double.nan);
+ c = format("^%+13,3.2f$", -double.nan);
+ d = format("^%+13,3.2F$", -double.nan);
+ assert(a == "^ +nan$", "\ngot:'"~ a ~ "'\nexp:'^ +nan$'");
+ assert(b == "^ +NAN$", "\ngot:'"~ b ~ "'\nexp:'^ +NAN$'");
+ assert(c == "^ -nan$", "\ngot:'"~ c ~ "'\nexp:'^ -nan$'");
+ assert(d == "^ -NAN$", "\ngot:'"~ d ~ "'\nexp:'^ -NAN$'");
+
+ a = format("^%-+13,3.2f$", double.nan);
+ b = format("^%-+13,3.2F$", double.nan);
+ c = format("^%-+13,3.2f$", -double.nan);
+ d = format("^%-+13,3.2F$", -double.nan);
+ assert(a == "^+nan $", "\ngot:'"~ a ~ "'\nexp:'^+nan $'");
+ assert(b == "^+NAN $", "\ngot:'"~ b ~ "'\nexp:'^+NAN $'");
+ assert(c == "^-nan $", "\ngot:'"~ c ~ "'\nexp:'^-nan $'");
+ assert(d == "^-NAN $", "\ngot:'"~ d ~ "'\nexp:'^-NAN $'");
+
+ a = format("^%- 13,3.2f$", double.nan);
+ b = format("^%- 13,3.2F$", double.nan);
+ c = format("^%- 13,3.2f$", -double.nan);
+ d = format("^%- 13,3.2F$", -double.nan);
+ assert(a == "^ nan $", "\ngot:'"~ a ~ "'\nexp:'^ nan $'");
+ assert(b == "^ NAN $", "\ngot:'"~ b ~ "'\nexp:'^ NAN $'");
+ assert(c == "^-nan $", "\ngot:'"~ c ~ "'\nexp:'^-nan $'");
+ assert(d == "^-NAN $", "\ngot:'"~ d ~ "'\nexp:'^-NAN $'");
+}
+
+@safe unittest
+{
+ struct S
+ {
+ int a;
+
+ void toString(void delegate(const(char)[]) sink, string fmt)
+ {
+ auto spec = singleSpec(fmt);
+ sink.formatValue(a, spec);
+ }
+ }
+
+ S s = S(1);
+ auto result = () @trusted { return format!"%5,3d"(s); } ();
+ assert(result == " 1");
+}
+
+/// ditto
+typeof(fmt) format(alias fmt, Args...)(Args args)
+if (isSomeString!(typeof(fmt)))
+{
+ import std.array : appender;
+ import std.range.primitives : ElementEncodingType;
+ import std.traits : Unqual;
+
+ alias e = checkFormatException!(fmt, Args);
+ alias Char = Unqual!(ElementEncodingType!(typeof(fmt)));
+
+ static assert(!e, e);
+ auto w = appender!(immutable(Char)[]);
+
+ // no need to traverse the string twice during compile time
+ if (!__ctfe)
+ {
+ enum len = guessLength!Char(fmt);
+ w.reserve(len);
+ }
+ else
+ {
+ w.reserve(fmt.length);
+ }
+
+ formattedWrite(w, fmt, args);
+ return w.data;
+}
+
+/// The format string can be checked at compile-time:
+@safe pure unittest
+{
+ auto s = format!"%s is %s"("Pi", 3.14);
+ assert(s == "Pi is 3.14");
+
+ // This line doesn't compile, because 3.14 cannot be formatted with %d:
+ // s = format!"%s is %d"("Pi", 3.14);
+}
+
+@safe pure unittest
+{
+ string s;
+ static assert(!__traits(compiles, {s = format!"%l"();})); // missing arg
+ static assert(!__traits(compiles, {s = format!""(404);})); // surplus arg
+ static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17381
+@safe pure unittest
+{
+ static assert(!__traits(compiles, format!"%s"(1.5, 2)));
+ static assert(!__traits(compiles, format!"%f"(1.5, 2)));
+ static assert(!__traits(compiles, format!"%s"(1.5L, 2)));
+ static assert(!__traits(compiles, format!"%f"(1.5L, 2)));
+}
+
+// called during compilation to guess the length of the
+// result of format
+private size_t guessLength(Char, S)(S fmtString)
+{
+ import std.array : appender;
+
+ size_t len;
+ auto output = appender!(immutable(Char)[])();
+ auto spec = FormatSpec!Char(fmtString);
+ while (spec.writeUpToNextSpec(output))
+ {
+ // take a guess
+ if (spec.width == 0 && (spec.precision == spec.UNSPECIFIED || spec.precision == spec.DYNAMIC))
+ {
+ switch (spec.spec)
+ {
+ case 'c':
+ ++len;
+ break;
+ case 'd':
+ case 'x':
+ case 'X':
+ len += 3;
+ break;
+ case 'b':
+ len += 8;
+ break;
+ case 'f':
+ case 'F':
+ len += 10;
+ break;
+ case 's':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ len += 12;
+ break;
+ default: break;
+ }
+
+ continue;
+ }
+
+ if ((spec.spec == 'e' || spec.spec == 'E' || spec.spec == 'g' ||
+ spec.spec == 'G' || spec.spec == 'f' || spec.spec == 'F') &&
+ spec.precision != spec.UNSPECIFIED && spec.precision != spec.DYNAMIC &&
+ spec.width == 0
+ )
+ {
+ len += spec.precision + 5;
+ continue;
+ }
+
+ if (spec.width == spec.precision)
+ len += spec.width;
+ else if (spec.width > 0 && spec.width != spec.DYNAMIC &&
+ (spec.precision == spec.UNSPECIFIED || spec.width > spec.precision))
+ {
+ len += spec.width;
+ }
+ else if (spec.precision != spec.UNSPECIFIED && spec.precision > spec.width)
+ len += spec.precision;
+ }
+ len += output.data.length;
+ return len;
+}
+
+@safe pure
+unittest
+{
+ assert(guessLength!char("%c") == 1);
+ assert(guessLength!char("%d") == 3);
+ assert(guessLength!char("%x") == 3);
+ assert(guessLength!char("%b") == 8);
+ assert(guessLength!char("%f") == 10);
+ assert(guessLength!char("%s") == 12);
+ assert(guessLength!char("%02d") == 2);
+ assert(guessLength!char("%02d") == 2);
+ assert(guessLength!char("%4.4d") == 4);
+ assert(guessLength!char("%2.4f") == 4);
+ assert(guessLength!char("%02d:%02d:%02d") == 8);
+ assert(guessLength!char("%0.2f") == 7);
+ assert(guessLength!char("%0*d") == 0);
+}
+
+/**
+Converts its arguments according to a format string into a buffer.
+The buffer has to be large enough to hold the formatted string.
+
+The second version of `sformat` takes the format string as a template
+argument. In this case, it is checked for consistency at
+compile-time.
+
+Params:
+ buf = the buffer where the formatted string should go
+ fmt = a $(MREF_ALTTEXT format string, std,format)
+ args = a variadic list of arguments to be formatted
+ Char = character type of `fmt`
+ Args = a variadic list of types of the arguments
+
+Returns:
+ A slice of `buf` containing the formatted string.
+
+Throws:
+ A $(REF_ALTTEXT RangeError, RangeError, core, exception) if `buf`
+ isn't large enough to hold the formatted string
+ and a $(LREF FormatException) if formatting did not succeed.
+
+Note:
+ In theory this function should be `@nogc`. But with the current
+ implementation there are some cases where allocations occur:
+
+ $(UL
+ $(LI An exception is thrown.)
+ $(LI A custom `toString` function of a compound type allocates.))
+ */
+char[] sformat(Char, Args...)(return scope char[] buf, scope const(Char)[] fmt, Args args)
+{
+ import core.exception : RangeError;
+ import std.range.primitives;
+ import std.utf : encode;
+
+ static struct Sink
+ {
+ char[] buf;
+ size_t i;
+ void put(dchar c)
+ {
+ char[4] enc;
+ auto n = encode(enc, c);
+
+ if (buf.length < i + n)
+ throw new RangeError(__FILE__, __LINE__);
+
+ buf[i .. i + n] = enc[0 .. n];
+ i += n;
+ }
+ void put(scope const(char)[] s)
+ {
+ if (buf.length < i + s.length)
+ throw new RangeError(__FILE__, __LINE__);
+
+ buf[i .. i + s.length] = s[];
+ i += s.length;
+ }
+ void put(scope const(wchar)[] s)
+ {
+ for (; !s.empty; s.popFront())
+ put(s.front);
+ }
+ void put(scope const(dchar)[] s)
+ {
+ for (; !s.empty; s.popFront())
+ put(s.front);
+ }
+ }
+ auto sink = Sink(buf);
+ auto n = formattedWrite(sink, fmt, args);
+ version (all)
+ {
+ // In the future, this check will be removed to increase consistency
+ // with formattedWrite
+ import std.conv : text;
+ enforceFmt(
+ n == args.length,
+ text("Orphan format arguments: args[", n, " .. ", args.length, "]")
+ );
+ }
+ return buf[0 .. sink.i];
+}
+
+/// ditto
+char[] sformat(alias fmt, Args...)(char[] buf, Args args)
+if (isSomeString!(typeof(fmt)))
+{
+ alias e = checkFormatException!(fmt, Args);
+ static assert(!e, e.msg);
+ return .sformat(buf, fmt, args);
+}
+
+///
+@safe pure unittest
+{
+ char[20] buf;
+ assert(sformat(buf[], "Here are %d %s.", 3, "apples") == "Here are 3 apples.");
+
+ assert(buf[].sformat("Increase: %7.2f %%", 17.4285) == "Increase: 17.43 %");
+}
+
+/// The format string can be checked at compile-time:
+@safe pure unittest
+{
+ char[20] buf;
+
+ assert(sformat!"Here are %d %s."(buf[], 3, "apples") == "Here are 3 apples.");
+
+ // This line doesn't compile, because 3.14 cannot be formatted with %d:
+ // writeln(sformat!"Here are %d %s."(buf[], 3.14, "apples"));
+}
+
+// checking, what is implicitly and explicitly stated in the public unittest
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ char[20] buf;
+ assertThrown!FormatException(sformat(buf[], "Here are %d %s.", 3.14, "apples"));
+ assert(!__traits(compiles, sformat!"Here are %d %s."(buf[], 3.14, "apples")));
+}
+
+@safe unittest
+{
+ import core.exception : RangeError;
+ import std.exception : assertCTFEable, assertThrown;
+
+ assertCTFEable!(
+ {
+ char[10] buf;
+
+ assert(sformat(buf[], "foo") == "foo");
+ assert(sformat(buf[], "foo%%") == "foo%");
+ assert(sformat(buf[], "foo%s", 'C') == "fooC");
+ assert(sformat(buf[], "%s foo", "bar") == "bar foo");
+ () @trusted {
+ assertThrown!RangeError(sformat(buf[], "%s foo %s", "bar", "abc"));
+ } ();
+ assert(sformat(buf[], "foo %d", -123) == "foo -123");
+ assert(sformat(buf[], "foo %d", 123) == "foo 123");
+
+ assertThrown!FormatException(sformat(buf[], "foo %s"));
+ assertThrown!FormatException(sformat(buf[], "foo %s", 123, 456));
+
+ assert(sformat(buf[], "%s %s %s", "c"c, "w"w, "d"d) == "c w d");
+ });
+}
+
+@safe unittest // ensure that sformat avoids the GC
+{
+ import core.memory : GC;
+
+ const a = ["foo", "bar"];
+ const u = () @trusted { return GC.stats().usedSize; } ();
+ char[20] buf;
+ sformat(buf, "%d", 123);
+ sformat(buf, "%s", a);
+ sformat(buf, "%s", 'c');
+ const v = () @trusted { return GC.stats().usedSize; } ();
+ assert(u == v);
+}
+
+version (StdUnittest)
+private void formatReflectTest(T)(ref T val, string fmt, string formatted, string fn = __FILE__, size_t ln = __LINE__)
+{
+ formatReflectTest(val, fmt, [formatted], fn, ln);
+}
+
+version (StdUnittest)
+private void formatReflectTest(T)(ref T val, string fmt, string[] formatted, string fn = __FILE__, size_t ln = __LINE__)
+{
+ import core.exception : AssertError;
+ import std.algorithm.searching : canFind;
+ import std.array : appender;
+ import std.math.operations : isClose;
+ import std.traits : FloatingPointTypeOf;
+
+ auto w = appender!string();
+ formattedWrite(w, fmt, val);
+
+ auto input = w.data;
+ enforce!AssertError(formatted.canFind(input), input, fn, ln);
+
+ T val2;
+ formattedRead(input, fmt, val2);
+
+ static if (is(FloatingPointTypeOf!T))
+ enforce!AssertError(isClose(val, val2), input, fn, ln);
+ else
+ enforce!AssertError(val == val2, input, fn, ln);
+}
+
+@safe unittest
+{
+ void booleanTest()
+ {
+ auto b = true;
+ formatReflectTest(b, "%s", `true`);
+ formatReflectTest(b, "%b", `1`);
+ formatReflectTest(b, "%o", `1`);
+ formatReflectTest(b, "%d", `1`);
+ formatReflectTest(b, "%u", `1`);
+ formatReflectTest(b, "%x", `1`);
+ }
+
+ void integerTest()
+ {
+ auto n = 127;
+ formatReflectTest(n, "%s", `127`);
+ formatReflectTest(n, "%b", `1111111`);
+ formatReflectTest(n, "%o", `177`);
+ formatReflectTest(n, "%d", `127`);
+ formatReflectTest(n, "%u", `127`);
+ formatReflectTest(n, "%x", `7f`);
+ }
+
+ void floatingTest()
+ {
+ auto f = 3.14;
+ formatReflectTest(f, "%s", `3.14`);
+ formatReflectTest(f, "%e", `3.140000e+00`);
+ formatReflectTest(f, "%f", `3.140000`);
+ formatReflectTest(f, "%g", `3.14`);
+ }
+
+ void charTest()
+ {
+ auto c = 'a';
+ formatReflectTest(c, "%s", `a`);
+ formatReflectTest(c, "%c", `a`);
+ formatReflectTest(c, "%b", `1100001`);
+ formatReflectTest(c, "%o", `141`);
+ formatReflectTest(c, "%d", `97`);
+ formatReflectTest(c, "%u", `97`);
+ formatReflectTest(c, "%x", `61`);
+ }
+
+ void strTest()
+ {
+ auto s = "hello";
+ formatReflectTest(s, "%s", `hello`);
+ formatReflectTest(s, "%(%c,%)", `h,e,l,l,o`);
+ formatReflectTest(s, "%(%s,%)", `'h','e','l','l','o'`);
+ formatReflectTest(s, "[%(<%c>%| $ %)]", `[<h> $ <e> $ <l> $ <l> $ <o>]`);
+ }
+
+ void daTest()
+ {
+ auto a = [1,2,3,4];
+ formatReflectTest(a, "%s", `[1, 2, 3, 4]`);
+ formatReflectTest(a, "[%(%s; %)]", `[1; 2; 3; 4]`);
+ formatReflectTest(a, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`);
+ }
+
+ void saTest()
+ {
+ int[4] sa = [1,2,3,4];
+ formatReflectTest(sa, "%s", `[1, 2, 3, 4]`);
+ formatReflectTest(sa, "[%(%s; %)]", `[1; 2; 3; 4]`);
+ formatReflectTest(sa, "[%(<%s>%| $ %)]", `[<1> $ <2> $ <3> $ <4>]`);
+ }
+
+ void aaTest()
+ {
+ auto aa = [1:"hello", 2:"world"];
+ formatReflectTest(aa, "%s", [`[1:"hello", 2:"world"]`, `[2:"world", 1:"hello"]`]);
+ formatReflectTest(aa, "[%(%s->%s, %)]", [`[1->"hello", 2->"world"]`, `[2->"world", 1->"hello"]`]);
+ formatReflectTest(aa, "{%([%s=%(%c%)]%|; %)}", [`{[1=hello]; [2=world]}`, `{[2=world]; [1=hello]}`]);
+ }
+
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ booleanTest();
+ integerTest();
+ floatingTest();
+ charTest();
+ strTest();
+ daTest();
+ saTest();
+ aaTest();
+ });
+}
diff --git a/libphobos/src/std/format/read.d b/libphobos/src/std/format/read.d
new file mode 100644
index 00000000000..0de88183fb2
--- /dev/null
+++ b/libphobos/src/std/format/read.d
@@ -0,0 +1,721 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, format).
+
+It provides two functions for reading formatted input: $(LREF
+unformatValue) and $(LREF formattedRead). The former reads a single
+value. The latter reads several values at once and matches the
+characters found between format specifiers.
+
+Parameters are ignored, except for the ones consisting of a single
+$(B '*'). See $(LREF formattedRead) for more information.
+
+A space outside of a format specifier has a special meaning: it
+matches any sequence of whitespace characters, not just a single
+space.
+
+The following combinations of format characters and types are
+available:
+
+$(BOOKTABLE ,
+$(TR $(TH) $(TH s) $(TH c) $(TH d, u, b, o, x, X) $(TH e, E, f, g, G) $(TH r) $(TH compound))
+$(TR $(TD `bool`) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
+$(TR $(TD `null`) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
+$(TR $(TD $(I integer)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)))
+$(TR $(TD $(I floating point)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)))
+$(TR $(TD $(I character)) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
+$(TR $(TD $(I string)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
+$(TR $(TD $(I array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
+$(TR $(TD $(I associative array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
+)
+
+Below are highlighted examples on how these combinations are used
+with $(LREF unformatValue), however, they apply for $(LREF
+formattedRead) also
+
+Copyright: Copyright The D Language Foundation 2000-2013.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
+Andrei Alexandrescu), and Kenji Hara
+
+Source: $(PHOBOSSRC std/format/read.d)
+ */
+module std.format.read;
+
+/// Booleans
+@safe pure unittest
+{
+ import std.format.spec : singleSpec;
+
+ auto str = "false";
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!bool(spec) == false);
+
+ str = "1";
+ spec = singleSpec("%d");
+ assert(str.unformatValue!bool(spec) == true);
+}
+
+/// Null values
+@safe pure unittest
+{
+ import std.format.spec : singleSpec;
+
+ auto str = "null";
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!(typeof(null))(spec) == null);
+}
+
+/// Integrals
+@safe pure unittest
+{
+ import std.format.spec : singleSpec;
+
+ // signed decimal values
+ auto str = "123";
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!int(spec) == 123);
+
+ // hexadecimal values
+ str = "ABC";
+ spec = singleSpec("%X");
+ assert(str.unformatValue!int(spec) == 2748);
+
+ // octal values
+ str = "11610";
+ spec = singleSpec("%o");
+ assert(str.unformatValue!int(spec) == 5000);
+
+ // raw read, depends on endianess
+ str = "\x75\x01";
+ spec = singleSpec("%r");
+ auto result = str.unformatValue!short(spec);
+ assert(result == 373 /* little endian */ || result == 29953 /* big endian */ );
+}
+
+/// Floating point numbers
+@safe pure unittest
+{
+ import std.format.spec : singleSpec;
+ import std.math.operations : isClose;
+
+ // natural notation
+ auto str = "123.456";
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!double(spec).isClose(123.456));
+
+ // scientific notation
+ str = "1e17";
+ spec = singleSpec("%e");
+ assert(str.unformatValue!double(spec).isClose(1e17));
+
+ // raw read, depends on endianess
+ str = "\x40\x00\x00\xBF";
+ spec = singleSpec("%r");
+ auto result = str.unformatValue!float(spec);
+ assert(isClose(result, -0.5) /* little endian */ || isClose(result, 2.0) /* big endian */ );
+}
+
+/// Characters
+@safe pure unittest
+{
+ import std.format.spec : singleSpec;
+
+ // only the first character is read
+ auto str = "abc";
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!char(spec) == 'a');
+
+ // using a numerical format character treats the read number as unicode code point
+ str = "65";
+ spec = singleSpec("%d");
+ assert(str.unformatValue!char(spec) == 'A');
+
+ str = "41";
+ spec = singleSpec("%x");
+ assert(str.unformatValue!char(spec) == 'A');
+
+ str = "10003";
+ spec = singleSpec("%d");
+ assert(str.unformatValue!dchar(spec) == '✓');
+}
+
+/// Arrays
+@safe pure unittest
+{
+ import std.format.spec : singleSpec;
+
+ // string value
+ string str = "aaa";
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!(dchar[])(spec) == "aaa"d);
+
+ // fixed size array with characters
+ str = "aaa";
+ spec = singleSpec("%s");
+ dchar[3] ret = ['a', 'a', 'a'];
+ assert(str.unformatValue!(dchar[3])(spec) == ret);
+
+ // dynamic array
+ str = "[1, 2, 3, 4]";
+ spec = singleSpec("%s");
+ assert(str.unformatValue!(int[])(spec) == [1, 2, 3, 4]);
+
+ // fixed size array with integers
+ str = "[1, 2, 3, 4]";
+ spec = singleSpec("%s");
+ int[4] ret2 = [1, 2, 3, 4];
+ assert(str.unformatValue!(int[4])(spec) == ret2);
+
+ // compound specifiers can be used for more control
+ str = "1,2,3";
+ spec = singleSpec("%(%s,%)");
+ assert(str.unformatValue!(int[])(spec) == [1, 2, 3]);
+
+ str = "cool";
+ spec = singleSpec("%(%c%)");
+ assert(str.unformatValue!(char[])(spec) == ['c', 'o', 'o', 'l']);
+}
+
+/// Associative arrays
+@safe pure unittest
+{
+ import std.format.spec : singleSpec;
+
+ // as single value
+ auto str = `["one": 1, "two": 2]`;
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!(int[string])(spec) == ["one": 1, "two": 2]);
+
+ // with compound specifier for more control
+ str = "1/1, 2/4, 3/9";
+ spec = singleSpec("%(%d/%d%|, %)");
+ assert(str.unformatValue!(int[int])(spec) == [1: 1, 2: 4, 3: 9]);
+}
+
+import std.format.spec : FormatSpec;
+import std.format.internal.read;
+import std.traits : isSomeString;
+
+/**
+Reads an input range according to a format string and stores the read
+values into its arguments.
+
+Format specifiers with format character $(B 'd'), $(B 'u') and $(B
+'c') can take a $(B '*') parameter for skipping values.
+
+The second version of `formattedRead` takes the format string as
+template argument. In this case, it is checked for consistency at
+compile-time.
+
+Params:
+ r = an $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
+ where the formatted input is read from
+ fmt = a $(MREF_ALTTEXT format string, std,format)
+ args = a variadic list of arguments where the read values are stored
+ Range = the type of the input range `r`
+ Char = the character type used for `fmt`
+ Args = a variadic list of types of the arguments
+
+Returns:
+ The number of variables filled. If the input range `r` ends early,
+ this number will be less than the number of variables provided.
+
+Throws:
+ A $(REF_ALTTEXT FormatException, FormatException, std, format)
+ if reading did not succeed.
+
+Note:
+ For backward compatibility the arguments `args` can be given as pointers
+ to that variable, but it is not recommended to do so, because this
+ option might be removed in the future.
+ */
+uint formattedRead(Range, Char, Args...)(auto ref Range r, const(Char)[] fmt, auto ref Args args)
+{
+ import std.format : enforceFmt;
+ import std.range.primitives : empty;
+ import std.traits : isPointer;
+ import std.typecons : isTuple;
+
+ auto spec = FormatSpec!Char(fmt);
+ static if (!Args.length)
+ {
+ spec.readUpToNextSpec(r);
+ enforceFmt(spec.trailing.empty, "Trailing characters in formattedRead format string");
+ return 0;
+ }
+ else
+ {
+ enum hasPointer = isPointer!(typeof(args[0]));
+
+ // The function below accounts for '*' == fields meant to be
+ // read and skipped
+ void skipUnstoredFields()
+ {
+ for (;;)
+ {
+ spec.readUpToNextSpec(r);
+ if (spec.width != spec.DYNAMIC) break;
+ // must skip this field
+ skipData(r, spec);
+ }
+ }
+
+ skipUnstoredFields();
+ if (r.empty)
+ {
+ // Input is empty, nothing to read
+ return 0;
+ }
+
+ static if (hasPointer)
+ alias A = typeof(*args[0]);
+ else
+ alias A = typeof(args[0]);
+
+ static if (isTuple!A)
+ {
+ foreach (i, T; A.Types)
+ {
+ static if (hasPointer)
+ (*args[0])[i] = unformatValue!(T)(r, spec);
+ else
+ args[0][i] = unformatValue!(T)(r, spec);
+ skipUnstoredFields();
+ }
+ }
+ else
+ {
+ static if (hasPointer)
+ *args[0] = unformatValue!(A)(r, spec);
+ else
+ args[0] = unformatValue!(A)(r, spec);
+ }
+ return 1 + formattedRead(r, spec.trailing, args[1 .. $]);
+ }
+}
+
+/// ditto
+uint formattedRead(alias fmt, Range, Args...)(auto ref Range r, auto ref Args args)
+if (isSomeString!(typeof(fmt)))
+{
+ import std.format : checkFormatException;
+
+ alias e = checkFormatException!(fmt, Args);
+ static assert(!e, e.msg);
+ return .formattedRead(r, fmt, args);
+}
+
+///
+@safe pure unittest
+{
+ string object;
+ char cmp;
+ int value;
+
+ assert(formattedRead("angle < 36", "%s %c %d", object, cmp, value) == 3);
+ assert(object == "angle");
+ assert(cmp == '<');
+ assert(value == 36);
+
+ // reading may end early:
+ assert(formattedRead("length >", "%s %c %d", object, cmp, value) == 2);
+ assert(object == "length");
+ assert(cmp == '>');
+ // value is not changed:
+ assert(value == 36);
+}
+
+/// The format string can be checked at compile-time:
+@safe pure unittest
+{
+ string a;
+ int b;
+ double c;
+
+ assert("hello!124:34.5".formattedRead!"%s!%s:%s"(a, b, c) == 3);
+ assert(a == "hello");
+ assert(b == 124);
+ assert(c == 34.5);
+}
+
+/// Skipping values
+@safe pure unittest
+{
+ string item;
+ double amount;
+
+ assert("orange: (12%) 15.25".formattedRead("%s: (%*d%%) %f", item, amount) == 2);
+ assert(item == "orange");
+ assert(amount == 15.25);
+
+ // can also be used with tuples
+ import std.typecons : Tuple;
+
+ Tuple!(int, float) t;
+ char[] line = "1 7643 2.125".dup;
+ formattedRead(line, "%s %*u %s", t);
+ assert(t[0] == 1 && t[1] == 2.125);
+}
+
+@safe unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isNaN;
+ import std.range.primitives : empty;
+
+ string s = " 1.2 3.4 ";
+ double x, y, z;
+ assert(formattedRead(s, " %s %s %s ", x, y, z) == 2);
+ assert(s.empty);
+ assert(isClose(x, 1.2));
+ assert(isClose(y, 3.4));
+ assert(isNaN(z));
+}
+
+// for backwards compatibility
+@safe pure unittest
+{
+ string s = "hello!124:34.5";
+ string a;
+ int b;
+ double c;
+ formattedRead(s, "%s!%s:%s", &a, &b, &c);
+ assert(a == "hello" && b == 124 && c == 34.5);
+
+ // mix pointers and auto-ref
+ s = "world!200:42.25";
+ formattedRead(s, "%s!%s:%s", a, &b, &c);
+ assert(a == "world" && b == 200 && c == 42.25);
+
+ s = "world1!201:42.5";
+ formattedRead(s, "%s!%s:%s", &a, &b, c);
+ assert(a == "world1" && b == 201 && c == 42.5);
+
+ s = "world2!202:42.75";
+ formattedRead(s, "%s!%s:%s", a, b, &c);
+ assert(a == "world2" && b == 202 && c == 42.75);
+}
+
+// for backwards compatibility
+@safe pure unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isNaN;
+ import std.range.primitives : empty;
+
+ string s = " 1.2 3.4 ";
+ double x, y, z;
+ assert(formattedRead(s, " %s %s %s ", &x, &y, &z) == 2);
+ assert(s.empty);
+ assert(isClose(x, 1.2));
+ assert(isClose(y, 3.4));
+ assert(isNaN(z));
+}
+
+@safe unittest
+{
+ string s = "hello!124:34.5";
+ string a;
+ int b;
+ double c;
+ formattedRead(s, "%s!%s:%s", &a, &b, &c);
+ assert(a == "hello" && b == 124 && c == 34.5);
+}
+
+@safe pure unittest
+{
+ string line;
+
+ bool f1;
+
+ line = "true";
+ formattedRead(line, "%s", &f1);
+ assert(f1);
+
+ line = "TrUE";
+ formattedRead(line, "%s", &f1);
+ assert(f1);
+
+ line = "false";
+ formattedRead(line, "%s", &f1);
+ assert(!f1);
+
+ line = "fALsE";
+ formattedRead(line, "%s", &f1);
+ assert(!f1);
+
+ line = "1";
+ formattedRead(line, "%d", &f1);
+ assert(f1);
+
+ line = "-1";
+ formattedRead(line, "%d", &f1);
+ assert(f1);
+
+ line = "0";
+ formattedRead(line, "%d", &f1);
+ assert(!f1);
+
+ line = "-0";
+ formattedRead(line, "%d", &f1);
+ assert(!f1);
+}
+
+@safe pure unittest
+{
+ union B
+ {
+ char[int.sizeof] untyped;
+ int typed;
+ }
+
+ B b;
+ b.typed = 5;
+ char[] input = b.untyped[];
+ int witness;
+ formattedRead(input, "%r", &witness);
+ assert(witness == b.typed);
+}
+
+@safe pure unittest
+{
+ union A
+ {
+ char[float.sizeof] untyped;
+ float typed;
+ }
+
+ A a;
+ a.typed = 5.5;
+ char[] input = a.untyped[];
+ float witness;
+ formattedRead(input, "%r", &witness);
+ assert(witness == a.typed);
+}
+
+@safe pure unittest
+{
+ import std.typecons : Tuple;
+
+ char[] line = "1 2".dup;
+ int a, b;
+ formattedRead(line, "%s %s", &a, &b);
+ assert(a == 1 && b == 2);
+
+ line = "10 2 3".dup;
+ formattedRead(line, "%d ", &a);
+ assert(a == 10);
+ assert(line == "2 3");
+
+ Tuple!(int, float) t;
+ line = "1 2.125".dup;
+ formattedRead(line, "%d %g", &t);
+ assert(t[0] == 1 && t[1] == 2.125);
+
+ line = "1 7643 2.125".dup;
+ formattedRead(line, "%s %*u %s", &t);
+ assert(t[0] == 1 && t[1] == 2.125);
+}
+
+@safe pure unittest
+{
+ string line;
+
+ char c1, c2;
+
+ line = "abc";
+ formattedRead(line, "%s%c", &c1, &c2);
+ assert(c1 == 'a' && c2 == 'b');
+ assert(line == "c");
+}
+
+@safe pure unittest
+{
+ string line;
+
+ line = "[1,2,3]";
+ int[] s1;
+ formattedRead(line, "%s", &s1);
+ assert(s1 == [1,2,3]);
+}
+
+@safe pure unittest
+{
+ string line;
+
+ line = "[1,2,3]";
+ int[] s1;
+ formattedRead(line, "[%(%s,%)]", &s1);
+ assert(s1 == [1,2,3]);
+
+ line = `["hello", "world"]`;
+ string[] s2;
+ formattedRead(line, "[%(%s, %)]", &s2);
+ assert(s2 == ["hello", "world"]);
+
+ line = "123 456";
+ int[] s3;
+ formattedRead(line, "%(%s %)", &s3);
+ assert(s3 == [123, 456]);
+
+ line = "h,e,l,l,o; w,o,r,l,d";
+ string[] s4;
+ formattedRead(line, "%(%(%c,%); %)", &s4);
+ assert(s4 == ["hello", "world"]);
+}
+
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+
+ string line;
+
+ int[4] sa1;
+ line = `[1,2,3,4]`;
+ formattedRead(line, "%s", &sa1);
+ assert(sa1 == [1,2,3,4]);
+
+ int[4] sa2;
+ line = `[1,2,3]`;
+ assertThrown(formattedRead(line, "%s", &sa2));
+
+ int[4] sa3;
+ line = `[1,2,3,4,5]`;
+ assertThrown(formattedRead(line, "%s", &sa3));
+}
+
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+
+ string input;
+
+ int[4] sa1;
+ input = `[1,2,3,4]`;
+ formattedRead(input, "[%(%s,%)]", &sa1);
+ assert(sa1 == [1,2,3,4]);
+
+ int[4] sa2;
+ input = `[1,2,3]`;
+ assertThrown!FormatException(formattedRead(input, "[%(%s,%)]", &sa2));
+}
+
+@safe pure unittest
+{
+ string line;
+
+ string s1, s2;
+
+ line = "hello, world";
+ formattedRead(line, "%s", &s1);
+ assert(s1 == "hello, world", s1);
+
+ line = "hello, world;yah";
+ formattedRead(line, "%s;%s", &s1, &s2);
+ assert(s1 == "hello, world", s1);
+ assert(s2 == "yah", s2);
+
+ line = `['h','e','l','l','o']`;
+ string s3;
+ formattedRead(line, "[%(%s,%)]", &s3);
+ assert(s3 == "hello");
+
+ line = `"hello"`;
+ string s4;
+ formattedRead(line, "\"%(%c%)\"", &s4);
+ assert(s4 == "hello");
+}
+
+@safe pure unittest
+{
+ string line;
+
+ string[int] aa1;
+ line = `[1:"hello", 2:"world"]`;
+ formattedRead(line, "%s", &aa1);
+ assert(aa1 == [1:"hello", 2:"world"]);
+
+ int[string] aa2;
+ line = `{"hello"=1; "world"=2}`;
+ formattedRead(line, "{%(%s=%s; %)}", &aa2);
+ assert(aa2 == ["hello":1, "world":2]);
+
+ int[string] aa3;
+ line = `{[hello=1]; [world=2]}`;
+ formattedRead(line, "{%([%(%c%)=%s]%|; %)}", &aa3);
+ assert(aa3 == ["hello":1, "world":2]);
+}
+
+// test rvalue using
+@safe pure unittest
+{
+ string[int] aa1;
+ formattedRead!("%s")(`[1:"hello", 2:"world"]`, aa1);
+ assert(aa1 == [1:"hello", 2:"world"]);
+
+ int[string] aa2;
+ formattedRead(`{"hello"=1; "world"=2}`, "{%(%s=%s; %)}", aa2);
+ assert(aa2 == ["hello":1, "world":2]);
+}
+
+/**
+Reads a value from the given _input range and converts it according to a
+format specifier.
+
+Params:
+ input = the $(REF_ALTTEXT input range, isInputRange, std, range, primitives),
+ to read from
+ spec = a $(MREF_ALTTEXT format string, std,format)
+ T = type to return
+ Range = the type of the input range `input`
+ Char = the character type used for `spec`
+
+Returns:
+ A value from `input` of type `T`.
+
+Throws:
+ A $(REF_ALTTEXT FormatException, FormatException, std, format)
+ if reading did not succeed.
+
+See_Also:
+ $(REF parse, std, conv) and $(REF to, std, conv)
+ */
+T unformatValue(T, Range, Char)(ref Range input, scope const ref FormatSpec!Char spec)
+{
+ return unformatValueImpl!T(input, spec);
+}
+
+///
+@safe pure unittest
+{
+ import std.format.spec : singleSpec;
+
+ string s = "42";
+ auto spec = singleSpec("%s");
+ assert(unformatValue!int(s, spec) == 42);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=7241
+@safe pure unittest
+{
+ string input = "a";
+ auto spec = FormatSpec!char("%s");
+ spec.readUpToNextSpec(input);
+ auto result = unformatValue!(dchar[1])(input, spec);
+ assert(result[0] == 'a');
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20393
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+ string str = "foo 12a-buzz";
+ string a, c;
+ int b;
+ assertThrown(formattedRead(str, "%s %d-%s", &a, &b, &c));
+}
diff --git a/libphobos/src/std/format/spec.d b/libphobos/src/std/format/spec.d
new file mode 100644
index 00000000000..b129686f8a3
--- /dev/null
+++ b/libphobos/src/std/format/spec.d
@@ -0,0 +1,949 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, format).
+
+It centers around a struct called $(LREF FormatSpec), which takes a
+$(MREF_ALTTEXT format string, std,format) and provides tools for
+parsing this string. Additionally this module contains a function
+$(LREF singleSpec) which helps treating a single format specifier.
+
+Copyright: Copyright The D Language Foundation 2000-2013.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
+Andrei Alexandrescu), and Kenji Hara
+
+Source: $(PHOBOSSRC std/format/spec.d)
+ */
+module std.format.spec;
+
+import std.traits : Unqual;
+
+template FormatSpec(Char)
+if (!is(Unqual!Char == Char))
+{
+ alias FormatSpec = FormatSpec!(Unqual!Char);
+}
+
+/**
+A general handler for format strings.
+
+This handler centers around the function $(LREF writeUpToNextSpec),
+which parses the $(MREF_ALTTEXT format string, std,format) until the
+next format specifier is found. After the call, it provides
+information about this format specifier in its numerous variables.
+
+Params:
+ Char = the character type of the format string
+ */
+struct FormatSpec(Char)
+if (is(Unqual!Char == Char))
+{
+ import std.algorithm.searching : startsWith;
+ import std.ascii : isDigit;
+ import std.conv : parse, text, to;
+ import std.range.primitives;
+
+ /**
+ Minimum width.
+
+ _Default: `0`.
+ */
+ int width = 0;
+
+ /**
+ Precision. Its semantic depends on the format character.
+
+ See $(MREF_ALTTEXT format string, std,format) for more details.
+ _Default: `UNSPECIFIED`.
+ */
+ int precision = UNSPECIFIED;
+
+ /**
+ Number of elements between separators.
+
+ _Default: `UNSPECIFIED`.
+ */
+ int separators = UNSPECIFIED;
+
+ /**
+ The separator charactar is supplied at runtime.
+
+ _Default: false.
+ */
+ bool dynamicSeparatorChar = false;
+
+ /**
+ Set to `DYNAMIC` when the separator character is supplied at runtime.
+
+ _Default: `UNSPECIFIED`.
+
+ $(RED Warning:
+ `separatorCharPos` is deprecated. It will be removed in 2.107.0.
+ Please use `dynamicSeparatorChar` instead.)
+ */
+ // @@@DEPRECATED_[2.107.0]@@@
+ deprecated("separatorCharPos will be removed in 2.107.0. Please use dynamicSeparatorChar instead.")
+ int separatorCharPos() { return dynamicSeparatorChar ? DYNAMIC : UNSPECIFIED; }
+
+ /// ditto
+ // @@@DEPRECATED_[2.107.0]@@@
+ deprecated("separatorCharPos will be removed in 2.107.0. Please use dynamicSeparatorChar instead.")
+ void separatorCharPos(int value) { dynamicSeparatorChar = value == DYNAMIC; }
+
+ /**
+ Character to use as separator.
+
+ _Default: `','`.
+ */
+ dchar separatorChar = ',';
+
+ /**
+ Special value for `width`, `precision` and `separators`.
+
+ It flags that these values will be passed at runtime through
+ variadic arguments.
+ */
+ enum int DYNAMIC = int.max;
+
+ /**
+ Special value for `precision` and `separators`.
+
+ It flags that these values have not been specified.
+ */
+ enum int UNSPECIFIED = DYNAMIC - 1;
+
+ /**
+ The format character.
+
+ _Default: `'s'`.
+ */
+ char spec = 's';
+
+ /**
+ Index of the argument for positional parameters.
+
+ Counting starts with `1`. Set to `0` if not used. Default: `0`.
+ */
+ ubyte indexStart;
+
+ /**
+ Index of the last argument for positional parameter ranges.
+
+ Counting starts with `1`. Set to `0` if not used. Default: `0`.
+ */
+ ubyte indexEnd;
+
+ version (StdDdoc)
+ {
+ /// The format specifier contained a `'-'`.
+ bool flDash;
+
+ /// The format specifier contained a `'0'`.
+ bool flZero;
+
+ /// The format specifier contained a space.
+ bool flSpace;
+
+ /// The format specifier contained a `'+'`.
+ bool flPlus;
+
+ /// The format specifier contained a `'#'`.
+ bool flHash;
+
+ /// The format specifier contained a `'='`.
+ bool flEqual;
+
+ /// The format specifier contained a `','`.
+ bool flSeparator;
+
+ // Fake field to allow compilation
+ ubyte allFlags;
+ }
+ else
+ {
+ union
+ {
+ import std.bitmanip : bitfields;
+ mixin(bitfields!(
+ bool, "flDash", 1,
+ bool, "flZero", 1,
+ bool, "flSpace", 1,
+ bool, "flPlus", 1,
+ bool, "flHash", 1,
+ bool, "flEqual", 1,
+ bool, "flSeparator", 1,
+ ubyte, "", 1));
+ ubyte allFlags;
+ }
+ }
+
+ /// The inner format string of a nested format specifier.
+ const(Char)[] nested;
+
+ /**
+ The separator of a nested format specifier.
+
+ `null` means, there is no separator. `empty`, but not `null`,
+ means zero length separator.
+ */
+ const(Char)[] sep;
+
+ /// Contains the part of the format string, that has not yet been parsed.
+ const(Char)[] trailing;
+
+ /// Sequence `"["` inserted before each range or range like structure.
+ enum immutable(Char)[] seqBefore = "[";
+
+ /// Sequence `"]"` inserted after each range or range like structure.
+ enum immutable(Char)[] seqAfter = "]";
+
+ /**
+ Sequence `":"` inserted between element key and element value of
+ an associative array.
+ */
+ enum immutable(Char)[] keySeparator = ":";
+
+ /**
+ Sequence `", "` inserted between elements of a range, a range like
+ structure or the elements of an associative array.
+ */
+ enum immutable(Char)[] seqSeparator = ", ";
+
+ /**
+ Creates a new `FormatSpec`.
+
+ The string is lazily evaluated. That means, nothing is done,
+ until $(LREF writeUpToNextSpec) is called.
+
+ Params:
+ fmt = a $(MREF_ALTTEXT format string, std,format)
+ */
+ this(in Char[] fmt) @safe pure
+ {
+ trailing = fmt;
+ }
+
+ /**
+ Writes the format string to an output range until the next format
+ specifier is found and parse that format specifier.
+
+ See the $(MREF_ALTTEXT description of format strings, std,format) for more
+ details about the format specifier.
+
+ Params:
+ writer = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives),
+ where the format string is written to
+ OutputRange = type of the output range
+
+ Returns:
+ True, if a format specifier is found and false, if the end of the
+ format string has been reached.
+
+ Throws:
+ A $(REF_ALTTEXT FormatException, FormatException, std,format)
+ when parsing the format specifier did not succeed.
+ */
+ bool writeUpToNextSpec(OutputRange)(ref OutputRange writer) scope
+ {
+ import std.format : enforceFmt;
+
+ if (trailing.empty)
+ return false;
+ for (size_t i = 0; i < trailing.length; ++i)
+ {
+ if (trailing[i] != '%') continue;
+ put(writer, trailing[0 .. i]);
+ trailing = trailing[i .. $];
+ enforceFmt(trailing.length >= 2, `Unterminated format specifier: "%"`);
+ trailing = trailing[1 .. $];
+
+ if (trailing[0] != '%')
+ {
+ // Spec found. Fill up the spec, and bailout
+ fillUp();
+ return true;
+ }
+ // Doubled! Reset and Keep going
+ i = 0;
+ }
+ // no format spec found
+ put(writer, trailing);
+ trailing = null;
+ return false;
+ }
+
+ private void fillUp() scope
+ {
+ import std.format : enforceFmt, FormatException;
+
+ // Reset content
+ if (__ctfe)
+ {
+ flDash = false;
+ flZero = false;
+ flSpace = false;
+ flPlus = false;
+ flEqual = false;
+ flHash = false;
+ flSeparator = false;
+ }
+ else
+ {
+ allFlags = 0;
+ }
+
+ width = 0;
+ precision = UNSPECIFIED;
+ nested = null;
+ // Parse the spec (we assume we're past '%' already)
+ for (size_t i = 0; i < trailing.length; )
+ {
+ switch (trailing[i])
+ {
+ case '(':
+ // Embedded format specifier.
+ auto j = i + 1;
+ // Get the matching balanced paren
+ for (uint innerParens;;)
+ {
+ enforceFmt(j + 1 < trailing.length,
+ text("Incorrect format specifier: %", trailing[i .. $]));
+ if (trailing[j++] != '%')
+ {
+ // skip, we're waiting for %( and %)
+ continue;
+ }
+ if (trailing[j] == '-') // for %-(
+ {
+ ++j; // skip
+ enforceFmt(j < trailing.length,
+ text("Incorrect format specifier: %", trailing[i .. $]));
+ }
+ if (trailing[j] == ')')
+ {
+ if (innerParens-- == 0) break;
+ }
+ else if (trailing[j] == '|')
+ {
+ if (innerParens == 0) break;
+ }
+ else if (trailing[j] == '(')
+ {
+ ++innerParens;
+ }
+ }
+ if (trailing[j] == '|')
+ {
+ auto k = j;
+ for (++j;;)
+ {
+ if (trailing[j++] != '%')
+ continue;
+ if (trailing[j] == '%')
+ ++j;
+ else if (trailing[j] == ')')
+ break;
+ else
+ throw new FormatException(
+ text("Incorrect format specifier: %",
+ trailing[j .. $]));
+ }
+ nested = trailing[i + 1 .. k - 1];
+ sep = trailing[k + 1 .. j - 1];
+ }
+ else
+ {
+ nested = trailing[i + 1 .. j - 1];
+ sep = null; // no separator
+ }
+ //this = FormatSpec(innerTrailingSpec);
+ spec = '(';
+ // We practically found the format specifier
+ trailing = trailing[j + 1 .. $];
+ return;
+ case '-': flDash = true; ++i; break;
+ case '+': flPlus = true; ++i; break;
+ case '=': flEqual = true; ++i; break;
+ case '#': flHash = true; ++i; break;
+ case '0': flZero = true; ++i; break;
+ case ' ': flSpace = true; ++i; break;
+ case '*':
+ if (isDigit(trailing[++i]))
+ {
+ // a '*' followed by digits and '$' is a
+ // positional format
+ trailing = trailing[1 .. $];
+ width = -parse!(typeof(width))(trailing);
+ i = 0;
+ enforceFmt(trailing[i++] == '$',
+ text("$ expected after '*", -width, "' in format string"));
+ }
+ else
+ {
+ // read result
+ width = DYNAMIC;
+ }
+ break;
+ case '1': .. case '9':
+ auto tmp = trailing[i .. $];
+ const widthOrArgIndex = parse!uint(tmp);
+ enforceFmt(tmp.length,
+ text("Incorrect format specifier %", trailing[i .. $]));
+ i = trailing.length - tmp.length;
+ if (tmp.startsWith('$'))
+ {
+ // index of the form %n$
+ indexEnd = indexStart = to!ubyte(widthOrArgIndex);
+ ++i;
+ }
+ else if (tmp.startsWith(':'))
+ {
+ // two indexes of the form %m:n$, or one index of the form %m:$
+ indexStart = to!ubyte(widthOrArgIndex);
+ tmp = tmp[1 .. $];
+ if (tmp.startsWith('$'))
+ {
+ indexEnd = indexEnd.max;
+ }
+ else
+ {
+ indexEnd = parse!(typeof(indexEnd))(tmp);
+ }
+ i = trailing.length - tmp.length;
+ enforceFmt(trailing[i++] == '$',
+ "$ expected");
+ }
+ else
+ {
+ // width
+ width = to!int(widthOrArgIndex);
+ }
+ break;
+ case ',':
+ // Precision
+ ++i;
+ flSeparator = true;
+
+ if (trailing[i] == '*')
+ {
+ ++i;
+ // read result
+ separators = DYNAMIC;
+ }
+ else if (isDigit(trailing[i]))
+ {
+ auto tmp = trailing[i .. $];
+ separators = parse!int(tmp);
+ i = trailing.length - tmp.length;
+ }
+ else
+ {
+ // "," was specified, but nothing after it
+ separators = 3;
+ }
+
+ if (trailing[i] == '?')
+ {
+ dynamicSeparatorChar = true;
+ ++i;
+ }
+
+ break;
+ case '.':
+ // Precision
+ if (trailing[++i] == '*')
+ {
+ if (isDigit(trailing[++i]))
+ {
+ // a '.*' followed by digits and '$' is a
+ // positional precision
+ trailing = trailing[i .. $];
+ i = 0;
+ precision = -parse!int(trailing);
+ enforceFmt(trailing[i++] == '$',
+ "$ expected");
+ }
+ else
+ {
+ // read result
+ precision = DYNAMIC;
+ }
+ }
+ else if (trailing[i] == '-')
+ {
+ // negative precision, as good as 0
+ precision = 0;
+ auto tmp = trailing[i .. $];
+ parse!int(tmp); // skip digits
+ i = trailing.length - tmp.length;
+ }
+ else if (isDigit(trailing[i]))
+ {
+ auto tmp = trailing[i .. $];
+ precision = parse!int(tmp);
+ i = trailing.length - tmp.length;
+ }
+ else
+ {
+ // "." was specified, but nothing after it
+ precision = 0;
+ }
+ break;
+ default:
+ // this is the format char
+ spec = cast(char) trailing[i++];
+ trailing = trailing[i .. $];
+ return;
+ } // end switch
+ } // end for
+ throw new FormatException(text("Incorrect format specifier: ", trailing));
+ }
+
+ //--------------------------------------------------------------------------
+ package bool readUpToNextSpec(R)(ref R r) scope
+ {
+ import std.ascii : isLower, isWhite;
+ import std.format : enforceFmt;
+ import std.utf : stride;
+
+ // Reset content
+ if (__ctfe)
+ {
+ flDash = false;
+ flZero = false;
+ flSpace = false;
+ flPlus = false;
+ flHash = false;
+ flEqual = false;
+ flSeparator = false;
+ }
+ else
+ {
+ allFlags = 0;
+ }
+ width = 0;
+ precision = UNSPECIFIED;
+ nested = null;
+ // Parse the spec
+ while (trailing.length)
+ {
+ const c = trailing[0];
+ if (c == '%' && trailing.length > 1)
+ {
+ const c2 = trailing[1];
+ if (c2 == '%')
+ {
+ assert(!r.empty, "Required at least one more input");
+ // Require a '%'
+ enforceFmt (r.front == '%',
+ text("parseToFormatSpec: Cannot find character '",
+ c2, "' in the input string."));
+ trailing = trailing[2 .. $];
+ r.popFront();
+ }
+ else
+ {
+ enforceFmt(isLower(c2) || c2 == '*' || c2 == '(',
+ text("'%", c2, "' not supported with formatted read"));
+ trailing = trailing[1 .. $];
+ fillUp();
+ return true;
+ }
+ }
+ else
+ {
+ if (c == ' ')
+ {
+ while (!r.empty && isWhite(r.front)) r.popFront();
+ //r = std.algorithm.find!(not!(isWhite))(r);
+ }
+ else
+ {
+ enforceFmt(!r.empty && r.front == trailing.front,
+ text("parseToFormatSpec: Cannot find character '",
+ c, "' in the input string."));
+ r.popFront();
+ }
+ trailing = trailing[stride(trailing, 0) .. $];
+ }
+ }
+ return false;
+ }
+
+ package string getCurFmtStr() const
+ {
+ import std.array : appender;
+ import std.format.write : formatValue;
+
+ auto w = appender!string();
+ auto f = FormatSpec!Char("%s"); // for stringnize
+
+ put(w, '%');
+ if (indexStart != 0)
+ {
+ formatValue(w, indexStart, f);
+ put(w, '$');
+ }
+ if (flDash) put(w, '-');
+ if (flZero) put(w, '0');
+ if (flSpace) put(w, ' ');
+ if (flPlus) put(w, '+');
+ if (flEqual) put(w, '=');
+ if (flHash) put(w, '#');
+ if (width != 0)
+ formatValue(w, width, f);
+ if (precision != FormatSpec!Char.UNSPECIFIED)
+ {
+ put(w, '.');
+ formatValue(w, precision, f);
+ }
+ if (flSeparator) put(w, ',');
+ if (separators != FormatSpec!Char.UNSPECIFIED)
+ formatValue(w, separators, f);
+ put(w, spec);
+ return w.data;
+ }
+
+ /**
+ Provides a string representation.
+
+ Returns:
+ The string representation.
+ */
+ string toString() const @safe pure
+ {
+ import std.array : appender;
+
+ auto app = appender!string();
+ app.reserve(200 + trailing.length);
+ toString(app);
+ return app.data;
+ }
+
+ /**
+ Writes a string representation to an output range.
+
+ Params:
+ writer = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives),
+ where the representation is written to
+ OutputRange = type of the output range
+ */
+ void toString(OutputRange)(ref OutputRange writer) const
+ if (isOutputRange!(OutputRange, char))
+ {
+ import std.format.write : formatValue;
+
+ auto s = singleSpec("%s");
+
+ put(writer, "address = ");
+ formatValue(writer, &this, s);
+ put(writer, "\nwidth = ");
+ formatValue(writer, width, s);
+ put(writer, "\nprecision = ");
+ formatValue(writer, precision, s);
+ put(writer, "\nspec = ");
+ formatValue(writer, spec, s);
+ put(writer, "\nindexStart = ");
+ formatValue(writer, indexStart, s);
+ put(writer, "\nindexEnd = ");
+ formatValue(writer, indexEnd, s);
+ put(writer, "\nflDash = ");
+ formatValue(writer, flDash, s);
+ put(writer, "\nflZero = ");
+ formatValue(writer, flZero, s);
+ put(writer, "\nflSpace = ");
+ formatValue(writer, flSpace, s);
+ put(writer, "\nflPlus = ");
+ formatValue(writer, flPlus, s);
+ put(writer, "\nflEqual = ");
+ formatValue(writer, flEqual, s);
+ put(writer, "\nflHash = ");
+ formatValue(writer, flHash, s);
+ put(writer, "\nflSeparator = ");
+ formatValue(writer, flSeparator, s);
+ put(writer, "\nnested = ");
+ formatValue(writer, nested, s);
+ put(writer, "\ntrailing = ");
+ formatValue(writer, trailing, s);
+ put(writer, '\n');
+ }
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+
+ auto a = appender!(string)();
+ auto fmt = "Number: %6.4e\nString: %s";
+ auto f = FormatSpec!char(fmt);
+
+ assert(f.writeUpToNextSpec(a) == true);
+
+ assert(a.data == "Number: ");
+ assert(f.trailing == "\nString: %s");
+ assert(f.spec == 'e');
+ assert(f.width == 6);
+ assert(f.precision == 4);
+
+ assert(f.writeUpToNextSpec(a) == true);
+
+ assert(a.data == "Number: \nString: ");
+ assert(f.trailing == "");
+ assert(f.spec == 's');
+
+ assert(f.writeUpToNextSpec(a) == false);
+
+ assert(a.data == "Number: \nString: ");
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.conv : text;
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+
+ auto w = appender!(char[])();
+ auto f = FormatSpec!char("abc%sdef%sghi");
+ f.writeUpToNextSpec(w);
+ assert(w.data == "abc", w.data);
+ assert(f.trailing == "def%sghi", text(f.trailing));
+ f.writeUpToNextSpec(w);
+ assert(w.data == "abcdef", w.data);
+ assert(f.trailing == "ghi");
+ // test with embedded %%s
+ f = FormatSpec!char("ab%%cd%%ef%sg%%h%sij");
+ w.clear();
+ f.writeUpToNextSpec(w);
+ assert(w.data == "ab%cd%ef" && f.trailing == "g%%h%sij", w.data);
+ f.writeUpToNextSpec(w);
+ assert(w.data == "ab%cd%efg%h" && f.trailing == "ij");
+ // https://issues.dlang.org/show_bug.cgi?id=4775
+ f = FormatSpec!char("%%%s");
+ w.clear();
+ f.writeUpToNextSpec(w);
+ assert(w.data == "%" && f.trailing == "");
+ f = FormatSpec!char("%%%%%s%%");
+ w.clear();
+ while (f.writeUpToNextSpec(w)) continue;
+ assert(w.data == "%%%");
+
+ f = FormatSpec!char("a%%b%%c%");
+ w.clear();
+ assertThrown!FormatException(f.writeUpToNextSpec(w));
+ assert(w.data == "a%b%c" && f.trailing == "%");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=5237
+@safe unittest
+{
+ import std.array : appender;
+
+ auto w = appender!string();
+ auto f = FormatSpec!char("%.16f");
+ f.writeUpToNextSpec(w); // dummy eating
+ assert(f.spec == 'f');
+ auto fmt = f.getCurFmtStr();
+ assert(fmt == "%.16f");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=14059
+@safe unittest
+{
+ import std.array : appender;
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+
+ auto a = appender!(string)();
+
+ auto f = FormatSpec!char("%-(%s%"); // %)")
+ assertThrown!FormatException(f.writeUpToNextSpec(a));
+
+ f = FormatSpec!char("%(%-"); // %)")
+ assertThrown!FormatException(f.writeUpToNextSpec(a));
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.format : format;
+
+ auto a = appender!(string)();
+
+ auto f = FormatSpec!char("%,d");
+ f.writeUpToNextSpec(a);
+
+ assert(f.spec == 'd', format("%s", f.spec));
+ assert(f.precision == FormatSpec!char.UNSPECIFIED);
+ assert(f.separators == 3);
+
+ f = FormatSpec!char("%5,10f");
+ f.writeUpToNextSpec(a);
+ assert(f.spec == 'f', format("%s", f.spec));
+ assert(f.separators == 10);
+ assert(f.width == 5);
+
+ f = FormatSpec!char("%5,10.4f");
+ f.writeUpToNextSpec(a);
+ assert(f.spec == 'f', format("%s", f.spec));
+ assert(f.separators == 10);
+ assert(f.width == 5);
+ assert(f.precision == 4);
+}
+
+@safe pure unittest
+{
+ import std.algorithm.searching : canFind, findSplitBefore;
+
+ auto expected = "width = 2" ~
+ "\nprecision = 5" ~
+ "\nspec = f" ~
+ "\nindexStart = 0" ~
+ "\nindexEnd = 0" ~
+ "\nflDash = false" ~
+ "\nflZero = false" ~
+ "\nflSpace = false" ~
+ "\nflPlus = false" ~
+ "\nflEqual = false" ~
+ "\nflHash = false" ~
+ "\nflSeparator = false" ~
+ "\nnested = " ~
+ "\ntrailing = \n";
+ auto spec = singleSpec("%2.5f");
+ auto res = spec.toString();
+ // make sure the address exists, then skip it
+ assert(res.canFind("address"));
+ assert(res.findSplitBefore("width")[1] == expected);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15348
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.exception : collectExceptionMsg;
+ import std.format : FormatException;
+
+ auto w = appender!(char[])();
+ auto f = FormatSpec!char("%*10d");
+
+ assert(collectExceptionMsg!FormatException(f.writeUpToNextSpec(w))
+ == "$ expected after '*10' in format string");
+}
+
+/**
+Helper function that returns a `FormatSpec` for a single format specifier.
+
+Params:
+ fmt = a $(MREF_ALTTEXT format string, std,format)
+ containing a single format specifier
+ Char = character type of `fmt`
+
+Returns:
+ A $(LREF FormatSpec) with the format specifier parsed.
+
+Throws:
+ A $(REF_ALTTEXT FormatException, FormatException, std,format) when the
+ format string contains no format specifier or more than a single format
+ specifier or when the format specifier is malformed.
+ */
+FormatSpec!Char singleSpec(Char)(Char[] fmt)
+{
+ import std.conv : text;
+ import std.format : enforceFmt;
+ import std.range.primitives : empty, front;
+
+ enforceFmt(fmt.length >= 2, "fmt must be at least 2 characters long");
+ enforceFmt(fmt.front == '%', "fmt must start with a '%' character");
+ enforceFmt(fmt[1] != '%', "'%%' is not a permissible format specifier");
+
+ static struct DummyOutputRange
+ {
+ void put(C)(scope const C[] buf) {} // eat elements
+ }
+ auto a = DummyOutputRange();
+ auto spec = FormatSpec!Char(fmt);
+ //dummy write
+ spec.writeUpToNextSpec(a);
+
+ enforceFmt(spec.trailing.empty,
+ text("Trailing characters in fmt string: '", spec.trailing));
+
+ return spec;
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.write : formatValue;
+
+ auto spec = singleSpec("%10.3e");
+ auto writer = appender!string();
+ writer.formatValue(42.0, spec);
+
+ assert(writer.data == " 4.200e+01");
+}
+
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+ import std.format : FormatException;
+
+ auto spec = singleSpec("%2.3e");
+
+ assert(spec.trailing == "");
+ assert(spec.spec == 'e');
+ assert(spec.width == 2);
+ assert(spec.precision == 3);
+
+ assertThrown!FormatException(singleSpec(""));
+ assertThrown!FormatException(singleSpec("%"));
+ assertThrown!FormatException(singleSpec("%2.3"));
+ assertThrown!FormatException(singleSpec("2.3e"));
+ assertThrown!FormatException(singleSpec("Test%2.3e"));
+ assertThrown!FormatException(singleSpec("%2.3eTest"));
+ assertThrown!FormatException(singleSpec("%%"));
+}
+
+// @@@DEPRECATED_[2.107.0]@@@
+deprecated("enforceValidFormatSpec was accidentally made public and will be removed in 2.107.0")
+void enforceValidFormatSpec(T, Char)(scope const ref FormatSpec!Char f)
+{
+ import std.format.internal.write : evfs = enforceValidFormatSpec;
+
+ evfs!T(f);
+}
+
+@safe unittest
+{
+ import std.exception : collectExceptionMsg;
+ import std.format : format, FormatException;
+
+ // width/precision
+ assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2))
+ == "integer width expected, not double for argument #1");
+ assert(collectExceptionMsg!FormatException(format("%-1*.d", 5.1, 2))
+ == "integer width expected, not double for argument #1");
+
+ assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2))
+ == "integer precision expected, not char for argument #1");
+ assert(collectExceptionMsg!FormatException(format("%-1.*d", 4.7, 3))
+ == "integer precision expected, not double for argument #1");
+ assert(collectExceptionMsg!FormatException(format("%.*d", 5))
+ == "Orphan format specifier: %d");
+ assert(collectExceptionMsg!FormatException(format("%*.*d", 5))
+ == "Missing integer precision argument");
+
+ // dynamicSeparatorChar
+ assert(collectExceptionMsg!FormatException(format("%,?d", 5))
+ == "separator character expected, not int for argument #1");
+ assert(collectExceptionMsg!FormatException(format("%,?d", '?'))
+ == "Orphan format specifier: %d");
+ assert(collectExceptionMsg!FormatException(format("%.*,*?d", 5))
+ == "Missing separator digit width argument");
+}
+
diff --git a/libphobos/src/std/format/write.d b/libphobos/src/std/format/write.d
new file mode 100644
index 00000000000..c7587688fc0
--- /dev/null
+++ b/libphobos/src/std/format/write.d
@@ -0,0 +1,1289 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, format).
+
+It provides two functions for writing formatted output: $(LREF
+formatValue) and $(LREF formattedWrite). The former writes a single
+value. The latter writes several values at once, interspersed with
+unformatted text.
+
+The following combinations of format characters and types are
+available:
+
+$(BOOKTABLE ,
+$(TR $(TH) $(TH s) $(TH c) $(TH d, u, b, o) $(TH x, X) $(TH e, E, f, F, g, G, a, A) $(TH r) $(TH compound))
+$(TR $(TD `bool`) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)))
+$(TR $(TD `null`) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
+$(TR $(TD $(I integer)) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)))
+$(TR $(TD $(I floating point)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes) $(TD $(MDASH)))
+$(TR $(TD $(I character)) $(TD yes) $(TD yes) $(TD yes) $(TD yes) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)))
+$(TR $(TD $(I string)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))
+$(TR $(TD $(I array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))
+$(TR $(TD $(I associative array)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes))
+$(TR $(TD $(I pointer)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)))
+$(TR $(TD $(I SIMD vectors)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))
+$(TR $(TD $(I delegates)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH)) $(TD yes) $(TD yes))
+)
+
+Enums can be used with all format characters of the base type.
+
+$(SECTION3 Structs$(COMMA) Unions$(COMMA) Classes$(COMMA) and Interfaces)
+
+Aggregate types can define various `toString` functions. If this
+function takes a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format,
+spec) or a $(I format string) as argument, the function decides
+which format characters are accepted. If no `toString` is defined and
+the aggregate is an $(REF_ALTTEXT input range, isInputRange, std,
+range, primitives), it is treated like a range, that is $(B 's'), $(B
+'r') and a compound specifier are accepted. In all other cases
+aggregate types only accept $(B 's').
+
+`toString` should have one of the following signatures:
+
+---
+void toString(Writer, Char)(ref Writer w, const ref FormatSpec!Char fmt)
+void toString(Writer)(ref Writer w)
+string toString();
+---
+
+Where `Writer` is an $(REF_ALTTEXT output range, isOutputRange,
+std,range,primitives) which accepts characters $(LPAREN)of type
+`Char` in the first version$(RPAREN). The template type does not have
+to be called `Writer`.
+
+Sometimes it's not possible to use a template, for example when
+`toString` overrides `Object.toString`. In this case, the following
+$(LPAREN)slower and less flexible$(RPAREN) functions can be used:
+
+---
+void toString(void delegate(const(char)[]) sink, const ref FormatSpec!char fmt);
+void toString(void delegate(const(char)[]) sink, string fmt);
+void toString(void delegate(const(char)[]) sink);
+---
+
+When several of the above `toString` versions are available, the
+versions with `Writer` take precedence over the versions with a
+`sink`. `string toString()` has the lowest priority.
+
+If none of the above mentioned `toString` versions are available, the
+aggregates will be formatted by other means, in the following
+order:
+
+If an aggregate is an $(REF_ALTTEXT input range, isInputRange, std,
+range, primitives), it is formatted like an input range.
+
+If an aggregate is a builtin type (using `alias this`), it is formatted
+like the builtin type.
+
+If all else fails, structs are formatted like `Type(field1, field2, ...)`,
+classes and interfaces are formatted with their fully qualified name
+and unions with their base name.
+
+Copyright: Copyright The D Language Foundation 2000-2013.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
+Andrei Alexandrescu), and Kenji Hara
+
+Source: $(PHOBOSSRC std/format/write.d)
+ */
+module std.format.write;
+
+/**
+`bool`s are formatted as `"true"` or `"false"` with `%s` and like the
+`byte`s 1 and 0 with all other format characters.
+ */
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%s");
+ formatValue(w1, true, spec1);
+
+ assert(w1.data == "true");
+
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%#x");
+ formatValue(w2, true, spec2);
+
+ assert(w2.data == "0x1");
+}
+
+/// The `null` literal is formatted as `"null"`.
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ formatValue(w, null, spec);
+
+ assert(w.data == "null");
+}
+
+/**
+Integrals are formatted in (signed) every day notation with `%s` and
+`%d` and as an (unsigned) image of the underlying bit representation
+with `%b` (binary), `%u` (decimal), `%o` (octal), and `%x` (hexadecimal).
+ */
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%d");
+ formatValue(w1, -1337, spec1);
+
+ assert(w1.data == "-1337");
+
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%x");
+ formatValue(w2, -1337, spec2);
+
+ assert(w2.data == "fffffac7");
+}
+
+/**
+Floating-point values are formatted in natural notation with `%f`, in
+scientific notation with `%e`, in short notation with `%g`, and in
+hexadecimal scientific notation with `%a`. If a rounding mode is
+available, they are rounded according to this rounding mode, otherwise
+they are rounded to the nearest value, ties to even.
+ */
+@safe unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%.3f");
+ formatValue(w1, 1337.7779, spec1);
+
+ assert(w1.data == "1337.778");
+
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%.3e");
+ formatValue(w2, 1337.7779, spec2);
+
+ assert(w2.data == "1.338e+03");
+
+ auto w3 = appender!string();
+ auto spec3 = singleSpec("%.3g");
+ formatValue(w3, 1337.7779, spec3);
+
+ assert(w3.data == "1.34e+03");
+
+ auto w4 = appender!string();
+ auto spec4 = singleSpec("%.3a");
+ formatValue(w4, 1337.7779, spec4);
+
+ assert(w4.data == "0x1.4e7p+10");
+}
+
+/**
+Individual characters (`char`, `wchar`, or `dchar`) are formatted as
+Unicode characters with `%s` and `%c` and as integers (`ubyte`,
+`ushort`, `uint`) with all other format characters. With
+$(MREF_ALTTEXT compound specifiers, std,format) characters are
+treated differently.
+ */
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%c");
+ formatValue(w1, 'ì', spec1);
+
+ assert(w1.data == "ì");
+
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%#x");
+ formatValue(w2, 'ì', spec2);
+
+ assert(w2.data == "0xec");
+}
+
+/**
+Strings are formatted as a sequence of characters with `%s`.
+Non-printable characters are not escaped. With a compound specifier
+the string is treated like a range of characters. With $(MREF_ALTTEXT
+compound specifiers, std,format) strings are treated differently.
+ */
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%s");
+ formatValue(w1, "hello", spec1);
+
+ assert(w1.data == "hello");
+
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%(%#x%|/%)");
+ formatValue(w2, "hello", spec2);
+
+ assert(w2.data == "0x68/0x65/0x6c/0x6c/0x6f");
+}
+
+/// Static arrays are formatted as dynamic arrays.
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ int[2] two = [1, 2];
+ formatValue(w, two, spec);
+
+ assert(w.data == "[1, 2]");
+}
+
+/**
+Dynamic arrays are formatted as input ranges.
+ */
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%s");
+ auto two = [1, 2];
+ formatValue(w1, two, spec1);
+
+ assert(w1.data == "[1, 2]");
+
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%(%g%|, %)");
+ auto consts = [3.1415926, 299792458, 6.67430e-11];
+ formatValue(w2, consts, spec2);
+
+ assert(w2.data == "3.14159, 2.99792e+08, 6.6743e-11");
+
+ // void[] is treated like ubyte[]
+ auto w3 = appender!string();
+ auto spec3 = singleSpec("%s");
+ void[] val = cast(void[]) cast(ubyte[])[1, 2, 3];
+ formatValue(w3, val, spec3);
+
+ assert(w3.data == "[1, 2, 3]");
+}
+
+/**
+Associative arrays are formatted by using `':'` and `", "` as
+separators, enclosed by `'['` and `']'` when used with `%s`. It's
+also possible to use a compound specifier for better control.
+
+Please note, that the order of the elements is not defined, therefore
+the result of this function might differ.
+ */
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto aa = [10:17.5, 20:9.99];
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%s");
+ formatValue(w1, aa, spec1);
+
+ assert(w1.data == "[10:17.5, 20:9.99]" || w1.data == "[20:9.99, 10:17.5]");
+
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%(%x = %.0e%| # %)");
+ formatValue(w2, aa, spec2);
+
+ assert(w2.data == "a = 2e+01 # 14 = 1e+01" || w2.data == "14 = 1e+01 # a = 2e+01");
+}
+
+/**
+`enum`s are formatted as their name when used with `%s` and like
+their base value else.
+ */
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ enum A { first, second, third }
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%s");
+ formatValue(w1, A.second, spec1);
+
+ assert(w1.data == "second");
+
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%d");
+ formatValue(w2, A.second, spec2);
+
+ assert(w2.data == "1");
+
+ // values of an enum that have no name are formatted with %s using a cast
+ A a = A.third;
+ a++;
+
+ auto w3 = appender!string();
+ auto spec3 = singleSpec("%s");
+ formatValue(w3, a, spec3);
+
+ assert(w3.data == "cast(A)3");
+}
+
+/**
+`structs`, `unions`, `classes` and `interfaces` can be formatted in
+several different ways. The following example highlights `struct`
+formatting, however, it applies to other aggregates as well.
+ */
+@safe unittest
+{
+ import std.array : appender;
+ import std.format.spec : FormatSpec, singleSpec;
+
+ // Using a `toString` with a writer
+ static struct Point1
+ {
+ import std.range.primitives : isOutputRange, put;
+
+ int x, y;
+
+ void toString(W)(ref W writer, scope const ref FormatSpec!char f)
+ if (isOutputRange!(W, char))
+ {
+ put(writer, "(");
+ formatValue(writer, x, f);
+ put(writer, ",");
+ formatValue(writer, y, f);
+ put(writer, ")");
+ }
+ }
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%s");
+ auto p1 = Point1(16, 11);
+
+ formatValue(w1, p1, spec1);
+ assert(w1.data == "(16,11)");
+
+ // Using a `toString` with a sink
+ static struct Point2
+ {
+ int x, y;
+
+ void toString(scope void delegate(scope const(char)[]) @safe sink,
+ scope const FormatSpec!char fmt) const
+ {
+ sink("(");
+ sink.formatValue(x, fmt);
+ sink(",");
+ sink.formatValue(y, fmt);
+ sink(")");
+ }
+ }
+
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%03d");
+ auto p2 = Point2(16,11);
+
+ formatValue(w2, p2, spec2);
+ assert(w2.data == "(016,011)");
+
+ // Using `string toString()`
+ static struct Point3
+ {
+ int x, y;
+
+ string toString()
+ {
+ import std.conv : to;
+
+ return "(" ~ to!string(x) ~ "," ~ to!string(y) ~ ")";
+ }
+ }
+
+ auto w3 = appender!string();
+ auto spec3 = singleSpec("%s"); // has to be %s
+ auto p3 = Point3(16,11);
+
+ formatValue(w3, p3, spec3);
+ assert(w3.data == "(16,11)");
+
+ // without `toString`
+ static struct Point4
+ {
+ int x, y;
+ }
+
+ auto w4 = appender!string();
+ auto spec4 = singleSpec("%s"); // has to be %s
+ auto p4 = Point4(16,11);
+
+ formatValue(w4, p4, spec3);
+ assert(w4.data == "Point4(16, 11)");
+}
+
+/// Pointers are formatted as hexadecimal integers.
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w1 = appender!string();
+ auto spec1 = singleSpec("%s");
+ auto p1 = () @trusted { return cast(void*) 0xFFEECCAA; } ();
+ formatValue(w1, p1, spec1);
+
+ assert(w1.data == "FFEECCAA");
+
+ // null pointers are printed as `"null"` when used with `%s` and as hexadecimal integer else
+ auto w2 = appender!string();
+ auto spec2 = singleSpec("%s");
+ auto p2 = () @trusted { return cast(void*) 0x00000000; } ();
+ formatValue(w2, p2, spec2);
+
+ assert(w2.data == "null");
+
+ auto w3 = appender!string();
+ auto spec3 = singleSpec("%x");
+ formatValue(w3, p2, spec3);
+
+ assert(w3.data == "0");
+}
+
+/// SIMD vectors are formatted as arrays.
+@safe unittest
+{
+ import core.simd; // cannot be selective, because float4 might not be defined
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+
+ static if (is(float4))
+ {
+ version (X86) {}
+ else
+ {
+ float4 f4;
+ f4.array[0] = 1;
+ f4.array[1] = 2;
+ f4.array[2] = 3;
+ f4.array[3] = 4;
+
+ formatValue(w, f4, spec);
+ assert(w.data == "[1, 2, 3, 4]");
+ }
+ }
+}
+
+import std.format.internal.write;
+
+import std.format.spec : FormatSpec;
+import std.traits : isSomeString;
+
+/**
+Converts its arguments according to a format string and writes
+the result to an output range.
+
+The second version of `formattedWrite` takes the format string as a
+template argument. In this case, it is checked for consistency at
+compile-time.
+
+Params:
+ w = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives),
+ where the formatted result is written to
+ fmt = a $(MREF_ALTTEXT format string, std,format)
+ args = a variadic list of arguments to be formatted
+ Writer = the type of the writer `w`
+ Char = character type of `fmt`
+ Args = a variadic list of types of the arguments
+
+Returns:
+ The index of the last argument that was formatted. If no positional
+ arguments are used, this is the number of arguments that where formatted.
+
+Throws:
+ A $(REF_ALTTEXT FormatException, FormatException, std, format)
+ if formatting did not succeed.
+
+Note:
+ In theory this function should be `@nogc`. But with the current
+ implementation there are some cases where allocations occur.
+ See $(REF_ALTTEXT $(D sformat), sformat, std, format) for more details.
+ */
+uint formattedWrite(Writer, Char, Args...)(auto ref Writer w, const scope Char[] fmt, Args args)
+{
+ import std.conv : text;
+ import std.format : enforceFmt, FormatException;
+ import std.traits : isSomeChar;
+
+ auto spec = FormatSpec!Char(fmt);
+
+ // Are we already done with formats? Then just dump each parameter in turn
+ uint currentArg = 0;
+ while (spec.writeUpToNextSpec(w))
+ {
+ if (currentArg == Args.length && !spec.indexStart)
+ {
+ // leftover spec?
+ enforceFmt(fmt.length == 0,
+ text("Orphan format specifier: %", spec.spec));
+ break;
+ }
+
+ if (spec.width == spec.DYNAMIC)
+ {
+ auto width = getNthInt!"integer width"(currentArg, args);
+ if (width < 0)
+ {
+ spec.flDash = true;
+ width = -width;
+ }
+ spec.width = width;
+ ++currentArg;
+ }
+ else if (spec.width < 0)
+ {
+ // means: get width as a positional parameter
+ auto index = cast(uint) -spec.width;
+ assert(index > 0, "The index must be greater than zero");
+ auto width = getNthInt!"integer width"(index - 1, args);
+ if (currentArg < index) currentArg = index;
+ if (width < 0)
+ {
+ spec.flDash = true;
+ width = -width;
+ }
+ spec.width = width;
+ }
+
+ if (spec.precision == spec.DYNAMIC)
+ {
+ auto precision = getNthInt!"integer precision"(currentArg, args);
+ if (precision >= 0) spec.precision = precision;
+ // else negative precision is same as no precision
+ else spec.precision = spec.UNSPECIFIED;
+ ++currentArg;
+ }
+ else if (spec.precision < 0)
+ {
+ // means: get precision as a positional parameter
+ auto index = cast(uint) -spec.precision;
+ assert(index > 0, "The precision must be greater than zero");
+ auto precision = getNthInt!"integer precision"(index- 1, args);
+ if (currentArg < index) currentArg = index;
+ if (precision >= 0) spec.precision = precision;
+ // else negative precision is same as no precision
+ else spec.precision = spec.UNSPECIFIED;
+ }
+
+ if (spec.separators == spec.DYNAMIC)
+ {
+ auto separators = getNthInt!"separator digit width"(currentArg, args);
+ spec.separators = separators;
+ ++currentArg;
+ }
+
+ if (spec.dynamicSeparatorChar)
+ {
+ auto separatorChar =
+ getNth!("separator character", isSomeChar, dchar)(currentArg, args);
+ spec.separatorChar = separatorChar;
+ spec.dynamicSeparatorChar = false;
+ ++currentArg;
+ }
+
+ if (currentArg == Args.length && !spec.indexStart)
+ {
+ // leftover spec?
+ enforceFmt(fmt.length == 0,
+ text("Orphan format specifier: %", spec.spec));
+ break;
+ }
+
+ // Format an argument
+ // This switch uses a static foreach to generate a jump table.
+ // Currently `spec.indexStart` use the special value '0' to signal
+ // we should use the current argument. An enhancement would be to
+ // always store the index.
+ size_t index = currentArg;
+ if (spec.indexStart != 0)
+ index = spec.indexStart - 1;
+ else
+ ++currentArg;
+ SWITCH: switch (index)
+ {
+ foreach (i, Tunused; Args)
+ {
+ case i:
+ formatValue(w, args[i], spec);
+ if (currentArg < spec.indexEnd)
+ currentArg = spec.indexEnd;
+ // A little know feature of format is to format a range
+ // of arguments, e.g. `%1:3$` will format the first 3
+ // arguments. Since they have to be consecutive we can
+ // just use explicit fallthrough to cover that case.
+ if (i + 1 < spec.indexEnd)
+ {
+ // You cannot goto case if the next case is the default
+ static if (i + 1 < Args.length)
+ goto case;
+ else
+ goto default;
+ }
+ else
+ break SWITCH;
+ }
+ default:
+ throw new FormatException(
+ text("Positional specifier %", spec.indexStart, '$', spec.spec,
+ " index exceeds ", Args.length));
+ }
+ }
+ return currentArg;
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+
+ auto writer1 = appender!string();
+ formattedWrite(writer1, "%s is the ultimate %s.", 42, "answer");
+ assert(writer1[] == "42 is the ultimate answer.");
+
+ auto writer2 = appender!string();
+ formattedWrite(writer2, "Increase: %7.2f %%", 17.4285);
+ assert(writer2[] == "Increase: 17.43 %");
+}
+
+/// ditto
+uint formattedWrite(alias fmt, Writer, Args...)(auto ref Writer w, Args args)
+if (isSomeString!(typeof(fmt)))
+{
+ import std.format : checkFormatException;
+
+ alias e = checkFormatException!(fmt, Args);
+ static assert(!e, e.msg);
+ return .formattedWrite(w, fmt, args);
+}
+
+/// The format string can be checked at compile-time:
+@safe pure unittest
+{
+ import std.array : appender;
+
+ auto writer = appender!string();
+ writer.formattedWrite!"%d is the ultimate %s."(42, "answer");
+ assert(writer[] == "42 is the ultimate answer.");
+
+ // This line doesn't compile, because 3.14 cannot be formatted with %d:
+ // writer.formattedWrite!"%d is the ultimate %s."(3.14, "answer");
+}
+
+@safe pure unittest
+{
+ import std.array : appender;
+
+ auto stream = appender!string();
+ formattedWrite(stream, "%s", 1.1);
+ assert(stream.data == "1.1", stream.data);
+}
+
+@safe pure unittest
+{
+ import std.array;
+
+ auto w = appender!string();
+ formattedWrite(w, "%s %d", "@safe/pure", 42);
+ assert(w.data == "@safe/pure 42");
+}
+
+@safe pure unittest
+{
+ char[20] buf;
+ auto w = buf[];
+ formattedWrite(w, "%s %d", "@safe/pure", 42);
+ assert(buf[0 .. $ - w.length] == "@safe/pure 42");
+}
+
+@safe pure unittest
+{
+ import std.algorithm.iteration : map;
+ import std.array : appender;
+
+ auto stream = appender!string();
+ formattedWrite(stream, "%s", map!"a*a"([2, 3, 5]));
+ assert(stream.data == "[4, 9, 25]", stream.data);
+
+ // Test shared data.
+ stream = appender!string();
+ shared int s = 6;
+ formattedWrite(stream, "%s", s);
+ assert(stream.data == "6");
+}
+
+@safe pure unittest
+{
+ // testing positional parameters
+ import std.array : appender;
+ import std.exception : collectExceptionMsg;
+ import std.format : FormatException;
+
+ auto w = appender!(char[])();
+ formattedWrite(w,
+ "Numbers %2$s and %1$s are reversed and %1$s%2$s repeated",
+ 42, 0);
+ assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated",
+ w.data);
+ assert(collectExceptionMsg!FormatException(formattedWrite(w, "%1$s, %3$s", 1, 2))
+ == "Positional specifier %3$s index exceeds 2");
+
+ w.clear();
+ formattedWrite(w, "asd%s", 23);
+ assert(w.data == "asd23", w.data);
+ w.clear();
+ formattedWrite(w, "%s%s", 23, 45);
+ assert(w.data == "2345", w.data);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=3479
+@safe unittest
+{
+ import std.array : appender;
+
+ auto stream = appender!(char[])();
+ formattedWrite(stream, "%2$.*1$d", 12, 10);
+ assert(stream.data == "000000000010", stream.data);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=6893
+@safe unittest
+{
+ import std.array : appender;
+
+ enum E : ulong { A, B, C }
+ auto stream = appender!(char[])();
+ formattedWrite(stream, "%s", E.C);
+ assert(stream.data == "C");
+}
+
+@safe pure unittest
+{
+ import std.array : appender;
+
+ auto stream = appender!string();
+ formattedWrite(stream, "%u", 42);
+ assert(stream.data == "42", stream.data);
+}
+
+@safe pure unittest
+{
+ // testing raw writes
+ import std.array : appender;
+
+ auto w = appender!(char[])();
+ uint a = 0x02030405;
+ formattedWrite(w, "%+r", a);
+ assert(w.data.length == 4 && w.data[0] == 2 && w.data[1] == 3
+ && w.data[2] == 4 && w.data[3] == 5);
+
+ w.clear();
+ formattedWrite(w, "%-r", a);
+ assert(w.data.length == 4 && w.data[0] == 5 && w.data[1] == 4
+ && w.data[2] == 3 && w.data[3] == 2);
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.conv : text, octal;
+
+ auto stream = appender!(char[])();
+
+ formattedWrite(stream, "hello world! %s %s ", true, 57, 1_000_000_000, 'x', " foo");
+ assert(stream.data == "hello world! true 57 ", stream.data);
+ stream.clear();
+
+ formattedWrite(stream, "%g %A %s", 1.67, -1.28, float.nan);
+ assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", stream.data);
+ stream.clear();
+
+ formattedWrite(stream, "%x %X", 0x1234AF, 0xAFAFAFAF);
+ assert(stream.data == "1234af AFAFAFAF");
+ stream.clear();
+
+ formattedWrite(stream, "%b %o", 0x1234AF, 0xAFAFAFAF);
+ assert(stream.data == "100100011010010101111 25753727657");
+ stream.clear();
+
+ formattedWrite(stream, "%d %s", 0x1234AF, 0xAFAFAFAF);
+ assert(stream.data == "1193135 2947526575");
+ stream.clear();
+
+ formattedWrite(stream, "%a %A", 1.32, 6.78f);
+ assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2");
+ stream.clear();
+
+ formattedWrite(stream, "%#06.*f", 2, 12.345);
+ assert(stream.data == "012.35");
+ stream.clear();
+
+ formattedWrite(stream, "%#0*.*f", 6, 2, 12.345);
+ assert(stream.data == "012.35");
+ stream.clear();
+
+ const real constreal = 1;
+ formattedWrite(stream, "%g",constreal);
+ assert(stream.data == "1");
+ stream.clear();
+
+ formattedWrite(stream, "%7.4g:", 12.678);
+ assert(stream.data == " 12.68:");
+ stream.clear();
+
+ formattedWrite(stream, "%7.4g:", 12.678L);
+ assert(stream.data == " 12.68:");
+ stream.clear();
+
+ formattedWrite(stream, "%04f|%05d|%#05x|%#5x", -4.0, -10, 1, 1);
+ assert(stream.data == "-4.000000|-0010|0x001| 0x1", stream.data);
+ stream.clear();
+
+ int i;
+ string s;
+
+ i = -10;
+ formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(stream.data == "-10|-10|-10|-10|-10.0000");
+ stream.clear();
+
+ i = -5;
+ formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(stream.data == "-5| -5|-05|-5|-5.0000");
+ stream.clear();
+
+ i = 0;
+ formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(stream.data == "0| 0|000|0|0.0000");
+ stream.clear();
+
+ i = 5;
+ formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(stream.data == "5| 5|005|5|5.0000");
+ stream.clear();
+
+ i = 10;
+ formattedWrite(stream, "%d|%3d|%03d|%1d|%01.4f", i, i, i, i, cast(double) i);
+ assert(stream.data == "10| 10|010|10|10.0000");
+ stream.clear();
+
+ formattedWrite(stream, "%.0d", 0);
+ assert(stream.data == "0");
+ stream.clear();
+
+ formattedWrite(stream, "%.g", .34);
+ assert(stream.data == "0.3");
+ stream.clear();
+
+ stream.clear();
+ formattedWrite(stream, "%.0g", .34);
+ assert(stream.data == "0.3");
+
+ stream.clear();
+ formattedWrite(stream, "%.2g", .34);
+ assert(stream.data == "0.34");
+
+ stream.clear();
+ formattedWrite(stream, "%0.0008f", 1e-08);
+ assert(stream.data == "0.00000001");
+
+ stream.clear();
+ formattedWrite(stream, "%0.0008f", 1e-05);
+ assert(stream.data == "0.00001000");
+
+ s = "helloworld";
+ string r;
+ stream.clear();
+ formattedWrite(stream, "%.2s", s[0 .. 5]);
+ assert(stream.data == "he");
+ stream.clear();
+ formattedWrite(stream, "%.20s", s[0 .. 5]);
+ assert(stream.data == "hello");
+ stream.clear();
+ formattedWrite(stream, "%8s", s[0 .. 5]);
+ assert(stream.data == " hello");
+
+ byte[] arrbyte = new byte[4];
+ arrbyte[0] = 100;
+ arrbyte[1] = -99;
+ arrbyte[3] = 0;
+ stream.clear();
+ formattedWrite(stream, "%s", arrbyte);
+ assert(stream.data == "[100, -99, 0, 0]", stream.data);
+
+ ubyte[] arrubyte = new ubyte[4];
+ arrubyte[0] = 100;
+ arrubyte[1] = 200;
+ arrubyte[3] = 0;
+ stream.clear();
+ formattedWrite(stream, "%s", arrubyte);
+ assert(stream.data == "[100, 200, 0, 0]", stream.data);
+
+ short[] arrshort = new short[4];
+ arrshort[0] = 100;
+ arrshort[1] = -999;
+ arrshort[3] = 0;
+ stream.clear();
+ formattedWrite(stream, "%s", arrshort);
+ assert(stream.data == "[100, -999, 0, 0]");
+ stream.clear();
+ formattedWrite(stream, "%s", arrshort);
+ assert(stream.data == "[100, -999, 0, 0]");
+
+ ushort[] arrushort = new ushort[4];
+ arrushort[0] = 100;
+ arrushort[1] = 20_000;
+ arrushort[3] = 0;
+ stream.clear();
+ formattedWrite(stream, "%s", arrushort);
+ assert(stream.data == "[100, 20000, 0, 0]");
+
+ int[] arrint = new int[4];
+ arrint[0] = 100;
+ arrint[1] = -999;
+ arrint[3] = 0;
+ stream.clear();
+ formattedWrite(stream, "%s", arrint);
+ assert(stream.data == "[100, -999, 0, 0]");
+ stream.clear();
+ formattedWrite(stream, "%s", arrint);
+ assert(stream.data == "[100, -999, 0, 0]");
+
+ long[] arrlong = new long[4];
+ arrlong[0] = 100;
+ arrlong[1] = -999;
+ arrlong[3] = 0;
+ stream.clear();
+ formattedWrite(stream, "%s", arrlong);
+ assert(stream.data == "[100, -999, 0, 0]");
+ stream.clear();
+ formattedWrite(stream, "%s",arrlong);
+ assert(stream.data == "[100, -999, 0, 0]");
+
+ ulong[] arrulong = new ulong[4];
+ arrulong[0] = 100;
+ arrulong[1] = 999;
+ arrulong[3] = 0;
+ stream.clear();
+ formattedWrite(stream, "%s", arrulong);
+ assert(stream.data == "[100, 999, 0, 0]");
+
+ string[] arr2 = new string[4];
+ arr2[0] = "hello";
+ arr2[1] = "world";
+ arr2[3] = "foo";
+ stream.clear();
+ formattedWrite(stream, "%s", arr2);
+ assert(stream.data == `["hello", "world", "", "foo"]`, stream.data);
+
+ stream.clear();
+ formattedWrite(stream, "%.8d", 7);
+ assert(stream.data == "00000007");
+
+ stream.clear();
+ formattedWrite(stream, "%.8x", 10);
+ assert(stream.data == "0000000a");
+
+ stream.clear();
+ formattedWrite(stream, "%-3d", 7);
+ assert(stream.data == "7 ");
+
+ stream.clear();
+ formattedWrite(stream, "%*d", -3, 7);
+ assert(stream.data == "7 ");
+
+ stream.clear();
+ formattedWrite(stream, "%.*d", -3, 7);
+ assert(stream.data == "7");
+
+ stream.clear();
+ formattedWrite(stream, "%s", "abc"c);
+ assert(stream.data == "abc");
+ stream.clear();
+ formattedWrite(stream, "%s", "def"w);
+ assert(stream.data == "def", text(stream.data.length));
+ stream.clear();
+ formattedWrite(stream, "%s", "ghi"d);
+ assert(stream.data == "ghi");
+
+ @trusted void* deadBeef() { return cast(void*) 0xDEADBEEF; }
+ stream.clear();
+ formattedWrite(stream, "%s", deadBeef());
+ assert(stream.data == "DEADBEEF", stream.data);
+
+ stream.clear();
+ formattedWrite(stream, "%#x", 0xabcd);
+ assert(stream.data == "0xabcd");
+ stream.clear();
+ formattedWrite(stream, "%#X", 0xABCD);
+ assert(stream.data == "0XABCD");
+
+ stream.clear();
+ formattedWrite(stream, "%#o", octal!12345);
+ assert(stream.data == "012345");
+ stream.clear();
+ formattedWrite(stream, "%o", 9);
+ assert(stream.data == "11");
+
+ stream.clear();
+ formattedWrite(stream, "%+d", 123);
+ assert(stream.data == "+123");
+ stream.clear();
+ formattedWrite(stream, "%+d", -123);
+ assert(stream.data == "-123");
+ stream.clear();
+ formattedWrite(stream, "% d", 123);
+ assert(stream.data == " 123");
+ stream.clear();
+ formattedWrite(stream, "% d", -123);
+ assert(stream.data == "-123");
+
+ stream.clear();
+ formattedWrite(stream, "%%");
+ assert(stream.data == "%");
+
+ stream.clear();
+ formattedWrite(stream, "%d", true);
+ assert(stream.data == "1");
+ stream.clear();
+ formattedWrite(stream, "%d", false);
+ assert(stream.data == "0");
+
+ stream.clear();
+ formattedWrite(stream, "%d", 'a');
+ assert(stream.data == "97", stream.data);
+ wchar wc = 'a';
+ stream.clear();
+ formattedWrite(stream, "%d", wc);
+ assert(stream.data == "97");
+ dchar dc = 'a';
+ stream.clear();
+ formattedWrite(stream, "%d", dc);
+ assert(stream.data == "97");
+
+ byte b = byte.max;
+ stream.clear();
+ formattedWrite(stream, "%x", b);
+ assert(stream.data == "7f");
+ stream.clear();
+ formattedWrite(stream, "%x", ++b);
+ assert(stream.data == "80");
+ stream.clear();
+ formattedWrite(stream, "%x", ++b);
+ assert(stream.data == "81");
+
+ short sh = short.max;
+ stream.clear();
+ formattedWrite(stream, "%x", sh);
+ assert(stream.data == "7fff");
+ stream.clear();
+ formattedWrite(stream, "%x", ++sh);
+ assert(stream.data == "8000");
+ stream.clear();
+ formattedWrite(stream, "%x", ++sh);
+ assert(stream.data == "8001");
+
+ i = int.max;
+ stream.clear();
+ formattedWrite(stream, "%x", i);
+ assert(stream.data == "7fffffff");
+ stream.clear();
+ formattedWrite(stream, "%x", ++i);
+ assert(stream.data == "80000000");
+ stream.clear();
+ formattedWrite(stream, "%x", ++i);
+ assert(stream.data == "80000001");
+
+ stream.clear();
+ formattedWrite(stream, "%x", 10);
+ assert(stream.data == "a");
+ stream.clear();
+ formattedWrite(stream, "%X", 10);
+ assert(stream.data == "A");
+ stream.clear();
+ formattedWrite(stream, "%x", 15);
+ assert(stream.data == "f");
+ stream.clear();
+ formattedWrite(stream, "%X", 15);
+ assert(stream.data == "F");
+
+ @trusted void ObjectTest()
+ {
+ Object c = null;
+ stream.clear();
+ formattedWrite(stream, "%s", c);
+ assert(stream.data == "null");
+ }
+ ObjectTest();
+
+ enum TestEnum
+ {
+ Value1, Value2
+ }
+ stream.clear();
+ formattedWrite(stream, "%s", TestEnum.Value2);
+ assert(stream.data == "Value2", stream.data);
+ stream.clear();
+ formattedWrite(stream, "%s", cast(TestEnum) 5);
+ assert(stream.data == "cast(TestEnum)5", stream.data);
+
+ //immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
+ //stream.clear();
+ //formattedWrite(stream, "%s", aa.values);
+ //assert(stream.data == "[[h,e,l,l,o],[b,e,t,t,y]]");
+ //stream.clear();
+ //formattedWrite(stream, "%s", aa);
+ //assert(stream.data == "[3:[h,e,l,l,o],4:[b,e,t,t,y]]");
+
+ static const dchar[] ds = ['a','b'];
+ for (int j = 0; j < ds.length; ++j)
+ {
+ stream.clear(); formattedWrite(stream, " %d", ds[j]);
+ if (j == 0)
+ assert(stream.data == " 97");
+ else
+ assert(stream.data == " 98");
+ }
+
+ stream.clear();
+ formattedWrite(stream, "%.-3d", 7);
+ assert(stream.data == "7", ">" ~ stream.data ~ "<");
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ import std.meta : AliasSeq;
+
+ immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
+ assert(aa[3] == "hello");
+ assert(aa[4] == "betty");
+
+ auto stream = appender!(char[])();
+ alias AllNumerics =
+ AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong,
+ float, double, real);
+ foreach (T; AllNumerics)
+ {
+ T value = 1;
+ stream.clear();
+ formattedWrite(stream, "%s", value);
+ assert(stream.data == "1");
+ }
+
+ stream.clear();
+ formattedWrite(stream, "%s", aa);
+}
+
+/**
+Formats a value of any type according to a format specifier and
+writes the result to an output range.
+
+More details about how types are formatted, and how the format
+specifier influences the outcome, can be found in the definition of a
+$(MREF_ALTTEXT format string, std,format).
+
+Params:
+ w = an $(REF_ALTTEXT output range, isOutputRange, std, range, primitives) where
+ the formatted value is written to
+ val = the value to write
+ f = a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format, spec) defining the
+ format specifier
+ Writer = the type of the output range `w`
+ T = the type of value `val`
+ Char = the character type used for `f`
+
+Throws:
+ A $(LREF FormatException) if formatting did not succeed.
+
+Note:
+ In theory this function should be `@nogc`. But with the current
+ implementation there are some cases where allocations occur.
+ See $(REF_ALTTEXT $(D sformat), sformat, std, format) for more details.
+
+See_Also:
+ $(LREF formattedWrite) which formats several values at once.
+ */
+void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T val, scope const ref FormatSpec!Char f)
+{
+ import std.format : enforceFmt;
+
+ enforceFmt(f.width != f.DYNAMIC && f.precision != f.DYNAMIC
+ && f.separators != f.DYNAMIC && !f.dynamicSeparatorChar,
+ "Dynamic argument not allowed for `formatValue`");
+
+ formatValueImpl(w, val, f);
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : singleSpec;
+
+ auto writer = appender!string();
+ auto spec = singleSpec("%08b");
+ writer.formatValue(42, spec);
+ assert(writer.data == "00101010");
+
+ spec = singleSpec("%2s");
+ writer.formatValue('=', spec);
+ assert(writer.data == "00101010 =");
+
+ spec = singleSpec("%+14.6e");
+ writer.formatValue(42.0, spec);
+ assert(writer.data == "00101010 = +4.200000e+01");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15386
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format.spec : FormatSpec;
+ import std.format : FormatException;
+ import std.exception : assertThrown;
+
+ auto w = appender!(char[])();
+ auto dor = appender!(char[])();
+ auto fs = FormatSpec!char("%.*s");
+ fs.writeUpToNextSpec(dor);
+ assertThrown!FormatException(formatValue(w, 0, fs));
+
+ fs = FormatSpec!char("%*s");
+ fs.writeUpToNextSpec(dor);
+ assertThrown!FormatException(formatValue(w, 0, fs));
+
+ fs = FormatSpec!char("%,*s");
+ fs.writeUpToNextSpec(dor);
+ assertThrown!FormatException(formatValue(w, 0, fs));
+
+ fs = FormatSpec!char("%,?s");
+ fs.writeUpToNextSpec(dor);
+ assertThrown!FormatException(formatValue(w, 0, fs));
+
+ assertThrown!FormatException(formattedWrite(w, "%(%0*d%)", new int[1]));
+}
diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d
index f35d6ffdf39..cec61fef575 100644
--- a/libphobos/src/std/functional.d
+++ b/libphobos/src/std/functional.d
@@ -8,6 +8,7 @@ functions are helpful when constructing predicates for the algorithms in
$(MREF std, algorithm) or $(MREF std, range).
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE ,
$(TR $(TH Function Name) $(TH Description)
)
@@ -20,9 +21,6 @@ $(TR $(TH Function Name) $(TH Description)
functions one after the other, using one function's result for the next
function's argument.
))
- $(TR $(TD $(LREF forward))
- $(TD Forwards function arguments while saving ref-ness.
- ))
$(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo))
$(TD Ready-made predicate functions to compare two values.
))
@@ -36,7 +34,11 @@ $(TR $(TH Function Name) $(TH Description)
$(TD Creates a function that binds the first argument of a given function
to a given value.
))
- $(TR $(TD $(LREF reverseArgs), $(LREF binaryReverseArgs))
+ $(TR $(TD $(LREF curry))
+ $(TD Converts a multi-argument function into a series of single-argument
+ functions. f(x, y) == curry(f)(x)(y)
+ ))
+ $(TR $(TD $(LREF reverseArgs))
$(TD Predicate that reverses the order of its arguments.
))
$(TR $(TD $(LREF toDelegate))
@@ -46,12 +48,12 @@ $(TR $(TH Function Name) $(TH Description)
$(TD Create a unary or binary function from a string. Most often
used when defining algorithms on ranges.
))
-)
+))
Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.org, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/_functional.d)
+Source: $(PHOBOSSRC std/functional.d)
*/
/*
Copyright Andrei Alexandrescu 2008 - 2009.
@@ -61,9 +63,10 @@ Distributed under the Boost Software License, Version 1.0.
*/
module std.functional;
-import std.meta; // AliasSeq, Reverse
-import std.traits; // isCallable, Parameters
+import std.meta : AliasSeq, Reverse;
+import std.traits : isCallable, Parameters;
+import std.internal.attributes : betterC;
private template needOpCallAlias(alias fun)
{
@@ -93,12 +96,19 @@ private template needOpCallAlias(alias fun)
}
/**
-Transforms a string representing an expression into a unary
-function. The string must either use symbol name $(D a) as
-the parameter or provide the symbol via the $(D parmName) argument.
-If $(D fun) is not a string, $(D unaryFun) aliases itself away to $(D fun).
+Transforms a `string` representing an expression into a unary
+function. The `string` must either use symbol name `a` as
+the parameter or provide the symbol via the `parmName` argument.
+
+Params:
+ fun = a `string` or a callable
+ parmName = the name of the parameter if `fun` is a string. Defaults
+ to `"a"`.
+Returns:
+ If `fun` is a `string`, a new single parameter function
+
+ If `fun` is not a `string`, an alias to `fun`.
*/
-
template unaryFun(alias fun, string parmName = "a")
{
static if (is(typeof(fun) : string))
@@ -116,7 +126,7 @@ template unaryFun(alias fun, string parmName = "a")
}
else static if (needOpCallAlias!fun)
{
- // Issue 9906
+ // https://issues.dlang.org/show_bug.cgi?id=9906
alias unaryFun = fun.opCall;
}
else
@@ -147,7 +157,7 @@ template unaryFun(alias fun, string parmName = "a")
int num = 41;
assert(unaryFun!"a + 1"(num) == 42);
- // Issue 9906
+ // https://issues.dlang.org/show_bug.cgi?id=9906
struct Seen
{
static bool opCall(int n) { return true; }
@@ -176,13 +186,20 @@ template unaryFun(alias fun, string parmName = "a")
}
/**
-Transforms a string representing an expression into a binary function. The
-string must either use symbol names $(D a) and $(D b) as the parameters or
-provide the symbols via the $(D parm1Name) and $(D parm2Name) arguments.
-If $(D fun) is not a string, $(D binaryFun) aliases itself away to
-$(D fun).
+Transforms a `string` representing an expression into a binary function. The
+`string` must either use symbol names `a` and `b` as the parameters or
+provide the symbols via the `parm1Name` and `parm2Name` arguments.
+
+Params:
+ fun = a `string` or a callable
+ parm1Name = the name of the first parameter if `fun` is a string.
+ Defaults to `"a"`.
+ parm2Name = the name of the second parameter if `fun` is a string.
+ Defaults to `"b"`.
+Returns:
+ If `fun` is not a string, `binaryFun` aliases itself away to
+ `fun`.
*/
-
template binaryFun(alias fun, string parm1Name = "a",
string parm2Name = "b")
{
@@ -203,7 +220,7 @@ template binaryFun(alias fun, string parm1Name = "a",
}
else static if (needOpCallAlias!fun)
{
- // Issue 9906
+ // https://issues.dlang.org/show_bug.cgi?id=9906
alias binaryFun = fun.opCall;
}
else
@@ -233,7 +250,7 @@ template binaryFun(alias fun, string parm1Name = "a",
//@@BUG
//assert(binaryFun!("return a + b;")(41, 1) == 42);
- // Issue 9906
+ // https://issues.dlang.org/show_bug.cgi?id=9906
struct Seen
{
static bool opCall(int x, int y) { return true; }
@@ -301,7 +318,7 @@ private uint _ctfeSkipName(ref string op, string name)
return 0;
}
-// returns 1 if $(D fun) is trivial unary function
+// returns 1 if `fun` is trivial unary function
private uint _ctfeMatchUnary(string fun, string name)
{
if (!__ctfe) assert(false);
@@ -348,7 +365,7 @@ private uint _ctfeMatchUnary(string fun, string name)
static assert(_ctfeMatchUnary("Ñ‘[21]", "Ñ‘"));
}
-// returns 1 if $(D fun) is trivial binary function
+// returns 1 if `fun` is trivial binary function
private uint _ctfeMatchBinary(string fun, string name1, string name2)
{
if (!__ctfe) assert(false);
@@ -531,13 +548,18 @@ alias equalTo = safeOp!"==";
assert(!equalTo(-1, ~0U));
}
/**
- N-ary predicate that reverses the order of arguments, e.g., given
- $(D pred(a, b, c)), returns $(D pred(c, b, a)).
+N-ary predicate that reverses the order of arguments, e.g., given
+$(D pred(a, b, c)), returns $(D pred(c, b, a)).
+
+Params:
+ pred = A callable
+Returns:
+ A function which calls `pred` after reversing the given parameters
*/
template reverseArgs(alias pred)
{
auto reverseArgs(Args...)(auto ref Args args)
- if (is(typeof(pred(Reverse!args))))
+ if (is(typeof(pred(Reverse!args))))
{
return pred(Reverse!args);
}
@@ -548,6 +570,24 @@ template reverseArgs(alias pred)
{
alias gt = reverseArgs!(binaryFun!("a < b"));
assert(gt(2, 1) && !gt(1, 1));
+}
+
+///
+@safe unittest
+{
+ int x = 42;
+ bool xyz(int a, int b) { return a * x < b / x; }
+ auto foo = &xyz;
+ foo(4, 5);
+ alias zyx = reverseArgs!(foo);
+ assert(zyx(5, 4) == foo(4, 5));
+}
+
+///
+@safe unittest
+{
+ alias gt = reverseArgs!(binaryFun!("a < b"));
+ assert(gt(2, 1) && !gt(1, 1));
int x = 42;
bool xyz(int a, int b) { return a * x < b / x; }
auto foo = &xyz;
@@ -581,38 +621,13 @@ template reverseArgs(alias pred)
}
/**
- Binary predicate that reverses the order of arguments, e.g., given
- $(D pred(a, b)), returns $(D pred(b, a)).
-*/
-template binaryReverseArgs(alias pred)
-{
- auto binaryReverseArgs(ElementType1, ElementType2)
- (auto ref ElementType1 a, auto ref ElementType2 b)
- {
- return pred(b, a);
- }
-}
-
-///
-@safe unittest
-{
- alias gt = binaryReverseArgs!(binaryFun!("a < b"));
- assert(gt(2, 1) && !gt(1, 1));
-}
-
-///
-@safe unittest
-{
- int x = 42;
- bool xyz(int a, int b) { return a * x < b / x; }
- auto foo = &xyz;
- foo(4, 5);
- alias zyx = binaryReverseArgs!(foo);
- assert(zyx(5, 4) == foo(4, 5));
-}
+Negates predicate `pred`.
-/**
-Negates predicate $(D pred).
+Params:
+ pred = A string or a callable
+Returns:
+ A function which calls `pred` and returns the logical negation of its
+ return value.
*/
template not(alias pred)
{
@@ -633,7 +648,6 @@ template not(alias pred)
@safe unittest
{
import std.algorithm.searching : find;
- import std.functional;
import std.uni : isWhite;
string a = " Hello, world!";
assert(find!(not!isWhite)(a) == "Hello, world!");
@@ -653,18 +667,20 @@ template not(alias pred)
/**
$(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially
applies) $(D_PARAM fun) by tying its first argument to $(D_PARAM arg).
+
+Params:
+ fun = A callable
+ arg = The first argument to apply to `fun`
+Returns:
+ A new function which calls `fun` with `arg` plus the passed parameters.
*/
template partial(alias fun, alias arg)
{
- static if (is(typeof(fun) == delegate) || is(typeof(fun) == function))
- {
- import std.traits : ReturnType;
- ReturnType!fun partial(Parameters!fun[1..$] args2)
- {
- return fun(arg, args2);
- }
- }
- else
+ import std.traits : isCallable;
+ // Check whether fun is a user defined type which implements opCall or a template.
+ // As opCall itself can be templated, std.traits.isCallable does not work here.
+ enum isSomeFunctor = (is(typeof(fun) == struct) || is(typeof(fun) == class)) && __traits(hasMember, fun, "opCall");
+ static if (isSomeFunctor || __traits(isTemplate, fun))
{
auto partial(Ts...)(Ts args2)
{
@@ -687,6 +703,36 @@ template partial(alias fun, alias arg)
}
}
}
+ else static if (!isCallable!fun)
+ {
+ static assert(false, "Cannot apply partial to a non-callable '" ~ fun.stringof ~ "'.");
+ }
+ else // Assume fun is callable and uniquely defined.
+ {
+ static if (Parameters!fun.length == 0)
+ {
+ static assert(0, "Cannot partially apply '" ~ fun.stringof ~ "'." ~
+ "'" ~ fun.stringof ~ "' has 0 arguments.");
+ }
+ else static if (!is(typeof(arg) : Parameters!fun[0]))
+ {
+ string errorMsg()
+ {
+ string msg = "Argument mismatch for '" ~ fun.stringof ~ "': expected " ~
+ Parameters!fun[0].stringof ~ ", but got " ~ typeof(arg).stringof ~ ".";
+ return msg;
+ }
+ static assert(0, errorMsg());
+ }
+ else
+ {
+ import std.traits : ReturnType;
+ ReturnType!fun partial(Parameters!fun[1..$] args2)
+ {
+ return fun(arg, args2);
+ }
+ }
+ }
}
///
@@ -773,6 +819,11 @@ template partial(alias fun, alias arg)
assert(partial!(tcallable, 5)(6) == 11);
static assert(!is(typeof(partial!(tcallable, "5")(6))));
+ static struct NonCallable{}
+ static assert(!__traits(compiles, partial!(NonCallable, 5)), "Partial should not work on non-callable structs.");
+ static assert(!__traits(compiles, partial!(NonCallable.init, 5)),
+ "Partial should not work on instances of non-callable structs.");
+
static A funOneArg(A)(A a) { return a; }
alias funOneArg1 = partial!(funOneArg, 1);
assert(funOneArg1() == 1);
@@ -786,15 +837,223 @@ template partial(alias fun, alias arg)
assert(dg2() == 1);
}
+// Fix https://issues.dlang.org/show_bug.cgi?id=15732
+@safe unittest
+{
+ // Test whether it works with functions.
+ auto partialFunction(){
+ auto fullFunction = (float a, float b, float c) => a + b / c;
+ alias apply1 = partial!(fullFunction, 1);
+ return &apply1;
+ }
+ auto result = partialFunction()(2, 4);
+ assert(result == 1.5f);
+
+ // And with delegates.
+ auto partialDelegate(float c){
+ auto fullDelegate = (float a, float b) => a + b / c;
+ alias apply1 = partial!(fullDelegate, 1);
+ return &apply1;
+ }
+ auto result2 = partialDelegate(4)(2);
+ assert(result2 == 1.5f);
+}
+
/**
-Takes multiple functions and adjoins them together. The result is a
-$(REF Tuple, std,typecons) with one element per passed-in function. Upon
-invocation, the returned tuple is the adjoined results of all
-functions.
+Takes a function of (potentially) many arguments, and returns a function taking
+one argument and returns a callable taking the rest. f(x, y) == curry(f)(x)(y)
+
+Params:
+ F = a function taking at least one argument
+ t = a callable object whose opCall takes at least 1 object
+Returns:
+ A single parameter callable object
+*/
+template curry(alias F)
+if (isCallable!F && Parameters!F.length)
+{
+ //inspired from the implementation from Artur Skawina here:
+ //https://forum.dlang.org/post/mailman.1626.1340110492.24740.digitalmars-d@puremagic.com
+ //This implementation stores a copy of all filled in arguments with each curried result
+ //this way, the curried functions are independent and don't share any references
+ //eg: auto fc = curry!f; auto fc1 = fc(1); auto fc2 = fc(2); fc1(3) != fc2(3)
+ struct CurryImpl(size_t N)
+ {
+ alias FParams = Parameters!F;
+ FParams[0 .. N] storedArguments;
+ static if (N > 0)
+ {
+ this(U : FParams[N - 1])(ref CurryImpl!(N - 1) prev, ref U arg)
+ {
+ storedArguments[0 .. N - 1] = prev.storedArguments[];
+ storedArguments[N-1] = arg;
+ }
+ }
+
+ auto opCall(U : FParams[N])(auto ref U arg) return scope
+ {
+ static if (N == FParams.length - 1)
+ {
+ return F(storedArguments, arg);
+ }
+ else
+ {
+ return CurryImpl!(N + 1)(this, arg);
+ }
+ }
+ }
+
+ auto curry()
+ {
+ CurryImpl!0 res;
+ return res; // return CurryImpl!0.init segfaults for delegates on Windows
+ }
+}
+
+///
+pure @safe @nogc nothrow unittest
+{
+ int f(int x, int y, int z)
+ {
+ return x + y + z;
+ }
+ auto cf = curry!f;
+ auto cf1 = cf(1);
+ auto cf2 = cf(2);
+
+ assert(cf1(2)(3) == f(1, 2, 3));
+ assert(cf2(2)(3) == f(2, 2, 3));
+}
+
+///ditto
+auto curry(T)(T t)
+if (isCallable!T && Parameters!T.length)
+{
+ static auto fun(ref T inst, ref Parameters!T args)
+ {
+ return inst(args);
+ }
+
+ return curry!fun()(t);
+}
+
+///
+pure @safe @nogc nothrow unittest
+{
+ //works with callable structs too
+ struct S
+ {
+ int w;
+ int opCall(int x, int y, int z)
+ {
+ return w + x + y + z;
+ }
+ }
+
+ S s;
+ s.w = 5;
+
+ auto cs = curry(s);
+ auto cs1 = cs(1);
+ auto cs2 = cs(2);
+
+ assert(cs1(2)(3) == s(1, 2, 3));
+ assert(cs1(2)(3) == (1 + 2 + 3 + 5));
+ assert(cs2(2)(3) ==s(2, 2, 3));
+}
+
+
+@safe pure @nogc nothrow unittest
+{
+ //currying a single argument function does nothing
+ int pork(int a){ return a*2;}
+ auto curryPork = curry!pork;
+ assert(curryPork(0) == pork(0));
+ assert(curryPork(1) == pork(1));
+ assert(curryPork(-1) == pork(-1));
+ assert(curryPork(1000) == pork(1000));
+
+ //test 2 argument function
+ double mixedVeggies(double a, int b, bool)
+ {
+ return a + b;
+ }
+
+ auto mixedCurry = curry!mixedVeggies;
+ assert(mixedCurry(10)(20)(false) == mixedVeggies(10, 20, false));
+ assert(mixedCurry(100)(200)(true) == mixedVeggies(100, 200, true));
+
+ // struct with opCall
+ struct S
+ {
+ double opCall(int x, double y, short z) const pure nothrow @nogc
+ {
+ return x*y*z;
+ }
+ }
+
+ S s;
+ auto curriedStruct = curry(s);
+ assert(curriedStruct(1)(2)(short(3)) == s(1, 2, short(3)));
+ assert(curriedStruct(300)(20)(short(10)) == s(300, 20, short(10)));
+}
+
+pure @safe nothrow unittest
+{
+ auto cfl = curry!((double a, int b) => a + b);
+ assert(cfl(13)(2) == 15);
+
+ int c = 42;
+ auto cdg = curry!((double a, int b) => a + b + c);
+ assert(cdg(13)(2) == 57);
+
+ static class C
+ {
+ int opCall(int mult, int add) pure @safe nothrow @nogc scope
+ {
+ return mult * 42 + add;
+ }
+ }
+
+ scope C ci = new C();
+ scope cc = curry(ci);
+ assert(cc(2)(4) == ci(2, 4));
+}
+
+// Disallows callables without parameters
+pure @safe @nogc nothrow unittest
+{
+ static void noargs() {}
+ static assert(!__traits(compiles, curry!noargs()));
+
+ static struct NoArgs
+ {
+ void opCall() {}
+ }
+
+ static assert(!__traits(compiles, curry(NoArgs.init)));
+}
+
+private template Iota(size_t n)
+{
+ static if (n == 0)
+ alias Iota = AliasSeq!();
+ else
+ alias Iota = AliasSeq!(Iota!(n - 1), n - 1);
+}
+
+/**
+Takes multiple functions and adjoins them together.
+
+Params:
+ F = the call-able(s) to adjoin
+Returns:
+ A new function which returns a $(REF Tuple, std,typecons). Each of the
+ elements of the tuple will be the return values of `F`.
Note: In the special case where only a single function is provided
($(D F.length == 1)), adjoin simply aliases to the single passed function
-($(D F[0])).
+(`F[0]`).
*/
template adjoin(F...)
if (F.length == 1)
@@ -808,27 +1067,21 @@ if (F.length > 1)
auto adjoin(V...)(auto ref V a)
{
import std.typecons : tuple;
- static if (F.length == 2)
- {
- return tuple(F[0](a), F[1](a));
- }
- else static if (F.length == 3)
- {
- return tuple(F[0](a), F[1](a), F[2](a));
- }
- else
+ import std.meta : staticMap;
+
+ auto resultElement(size_t i)()
{
- import std.format : format;
- import std.range : iota;
- return mixin (q{tuple(%(F[%s](a)%|, %))}.format(iota(0, F.length)));
+ return F[i](a);
}
+
+ return tuple(staticMap!(resultElement, Iota!(F.length)));
}
}
///
@safe unittest
{
- import std.functional, std.typecons : Tuple;
+ import std.typecons : Tuple;
static bool f1(int a) { return a != 0; }
static int f2(int a) { return a / 2; }
auto x = adjoin!(f1, f2)(5);
@@ -881,37 +1134,49 @@ if (F.length > 1)
enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)();
}
+// https://issues.dlang.org/show_bug.cgi?id=21347
+@safe @betterC unittest
+{
+ alias f = (int n) => n + 1;
+ alias g = (int n) => n + 2;
+ alias h = (int n) => n + 3;
+ alias i = (int n) => n + 4;
+
+ auto result = adjoin!(f, g, h, i)(0);
+
+ assert(result[0] == 1);
+ assert(result[1] == 2);
+ assert(result[2] == 3);
+ assert(result[3] == 4);
+}
+
/**
- Composes passed-in functions $(D fun[0], fun[1], ...) returning a
- function $(D f(x)) that in turn returns $(D
- fun[0](fun[1](...(x)))...). Each function can be a regular
- functions, a delegate, or a string.
+ Composes passed-in functions $(D fun[0], fun[1], ...).
+
+ Params:
+ fun = the call-able(s) or `string`(s) to compose into one function
+ Returns:
+ A new function `f(x)` that in turn returns `fun[0](fun[1](...(x)))...`.
See_Also: $(LREF pipe)
*/
template compose(fun...)
+if (fun.length > 0)
{
static if (fun.length == 1)
{
alias compose = unaryFun!(fun[0]);
}
- else static if (fun.length == 2)
+ else
{
- // starch
alias fun0 = unaryFun!(fun[0]);
- alias fun1 = unaryFun!(fun[1]);
+ alias rest = compose!(fun[1 .. $]);
- // protein: the core composition operation
- typeof({ E a; return fun0(fun1(a)); }()) compose(E)(E a)
+ auto compose(Args...)(Args args)
{
- return fun0(fun1(a));
+ return fun0(rest(args));
}
}
- else
- {
- // protein: assembling operations
- alias compose = compose!(fun[0], compose!(fun[1 .. $]));
- }
}
///
@@ -927,12 +1192,28 @@ template compose(fun...)
assert(compose!(map!(to!(int)), split)("1 2 3").equal([1, 2, 3]));
}
+// https://issues.dlang.org/show_bug.cgi?id=6484
+@safe unittest
+{
+ int f(int a) { return a; }
+ int g(int a) { return a; }
+ int h(int a,int b,int c) { return a * b * c; }
+
+ alias F = compose!(f,g,h);
+ assert(F(1,2,3) == f(g(h(1,2,3))));
+}
+
/**
Pipes functions in sequence. Offers the same functionality as $(D
compose), but with functions specified in reverse order. This may
lead to more readable code in some situation because the order of
execution is the same as lexical order.
+ Params:
+ fun = the call-able(s) or `string`(s) to compose into one function
+ Returns:
+ A new function `f(x)` that in turn returns `fun[$-1](...fun[1](fun[0](x)))...`.
+
Example:
----
@@ -946,6 +1227,7 @@ int[] a = pipe!(readText, split, map!(to!(int)))("file.txt");
*/
alias pipe(fun...) = compose!(Reverse!(fun));
+///
@safe unittest
{
import std.conv : to;
@@ -985,26 +1267,36 @@ unittest
}
----
-Technically the memoized function should be pure because $(D memoize) assumes it will
-always return the same result for a given tuple of arguments. However, $(D memoize) does not
-enforce that because sometimes it
-is useful to memoize an impure function, too.
+Params:
+ fun = the call-able to memozie
+ maxSize = The maximum size of the GC buffer to hold the return values
+Returns:
+ A new function which calls `fun` and caches its return values.
+
+Note:
+ Technically the memoized function should be pure because `memoize` assumes it will
+ always return the same result for a given tuple of arguments. However, `memoize` does not
+ enforce that because sometimes it is useful to memoize an impure function, too.
*/
template memoize(alias fun)
{
import std.traits : ReturnType;
- // alias Args = Parameters!fun; // Bugzilla 13580
+ // https://issues.dlang.org/show_bug.cgi?id=13580
+ // alias Args = Parameters!fun;
ReturnType!fun memoize(Parameters!fun args)
{
alias Args = Parameters!fun;
import std.typecons : Tuple;
+ import std.traits : Unqual;
- static ReturnType!fun[Tuple!Args] memo;
+ static Unqual!(ReturnType!fun)[Tuple!Args] memo;
auto t = Tuple!Args(args);
if (auto p = t in memo)
return *p;
- return memo[t] = fun(args);
+ auto r = fun(args);
+ memo[t] = r;
+ return r;
}
}
@@ -1012,12 +1304,14 @@ template memoize(alias fun)
template memoize(alias fun, uint maxSize)
{
import std.traits : ReturnType;
- // alias Args = Parameters!fun; // Bugzilla 13580
+ // https://issues.dlang.org/show_bug.cgi?id=13580
+ // alias Args = Parameters!fun;
ReturnType!fun memoize(Parameters!fun args)
{
- import std.traits : hasIndirections;
+ import std.meta : staticMap;
+ import std.traits : hasIndirections, Unqual;
import std.typecons : tuple;
- static struct Value { Parameters!fun args; ReturnType!fun res; }
+ static struct Value { staticMap!(Unqual, Parameters!fun) args; Unqual!(ReturnType!fun) res; }
static Value[] memo;
static size_t[] initialized;
@@ -1036,7 +1330,7 @@ template memoize(alias fun, uint maxSize)
}
import core.bitop : bt, bts;
- import std.conv : emplace;
+ import core.lifetime : emplace;
size_t hash;
foreach (ref arg; args)
@@ -1046,7 +1340,9 @@ template memoize(alias fun, uint maxSize)
if (!bt(initialized.ptr, idx1))
{
emplace(&memo[idx1], args, fun(args));
- bts(initialized.ptr, idx1); // only set to initialized after setting args and value (bugzilla 14025)
+ // only set to initialized after setting args and value
+ // https://issues.dlang.org/show_bug.cgi?id=14025
+ bts(initialized.ptr, idx1);
return memo[idx1].res;
}
else if (memo[idx1].args == args)
@@ -1056,7 +1352,7 @@ template memoize(alias fun, uint maxSize)
if (!bt(initialized.ptr, idx2))
{
emplace(&memo[idx2], memo[idx1]);
- bts(initialized.ptr, idx2); // only set to initialized after setting args and value (bugzilla 14025)
+ bts(initialized.ptr, idx2);
}
else if (memo[idx2].args == args)
return memo[idx2].res;
@@ -1072,9 +1368,10 @@ template memoize(alias fun, uint maxSize)
* To _memoize a recursive function, simply insert the memoized call in lieu of the plain recursive call.
* For example, to transform the exponential-time Fibonacci implementation into a linear-time computation:
*/
-@safe unittest
+@safe nothrow
+unittest
{
- ulong fib(ulong n) @safe
+ ulong fib(ulong n) @safe nothrow
{
return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1);
}
@@ -1094,8 +1391,8 @@ template memoize(alias fun, uint maxSize)
}
/**
- * This memoizes all values of $(D fact) up to the largest argument. To only cache the final
- * result, move $(D memoize) outside the function as shown below.
+ * This memoizes all values of `fact` up to the largest argument. To only cache the final
+ * result, move `memoize` outside the function as shown below.
*/
@safe unittest
{
@@ -1108,7 +1405,7 @@ template memoize(alias fun, uint maxSize)
}
/**
- * When the $(D maxSize) parameter is specified, memoize will used
+ * When the `maxSize` parameter is specified, memoize will used
* a fixed size hash table to limit the number of cached entries.
*/
@system unittest // not @safe due to memoize
@@ -1154,7 +1451,7 @@ template memoize(alias fun, uint maxSize)
}
assert(fact(10) == 3628800);
- // Issue 12568
+ // https://issues.dlang.org/show_bug.cgi?id=12568
static uint len2(const string s) { // Error
alias mLen2 = memoize!len2;
if (s.length == 0)
@@ -1169,8 +1466,9 @@ template memoize(alias fun, uint maxSize)
assert(func(int.init) == 1);
}
-// 16079: memoize should work with arrays
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=16079
+// memoize should work with arrays
+@system unittest // not @safe with -dip1000 due to memoize
{
int executed = 0;
T median(T)(const T[] nums) {
@@ -1193,7 +1491,7 @@ template memoize(alias fun, uint maxSize)
assert(executed == 1);
}
-// 16079: memoize should work with structs
+// https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with structs
@safe unittest
{
int executed = 0;
@@ -1212,9 +1510,20 @@ template memoize(alias fun, uint maxSize)
assert(executed == 1);
}
-// 16079: memoize should work with classes
+// https://issues.dlang.org/show_bug.cgi?id=20439 memoize should work with void opAssign
@safe unittest
{
+ static struct S
+ {
+ void opAssign(S) {}
+ }
+
+ assert(memoize!(() => S()) == S());
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with classes
+@system unittest // not @safe with -dip1000 due to memoize
+{
int executed = 0;
T pickFirst(T)(T first)
{
@@ -1246,6 +1555,33 @@ template memoize(alias fun, uint maxSize)
assert(executed == 1);
}
+// https://issues.dlang.org/show_bug.cgi?id=20302
+@system unittest
+{
+ version (none) // TODO change `none` to `all` and fix remaining limitations
+ struct S { const int len; }
+ else
+ struct S { int len; }
+
+ static string fun000( string str, S s) { return str[0 .. s.len] ~ "123"; }
+ static string fun001( string str, const S s) { return str[0 .. s.len] ~ "123"; }
+ static string fun010(const string str, S s) { return str[0 .. s.len] ~ "123"; }
+ static string fun011(const string str, const S s) { return str[0 .. s.len] ~ "123"; }
+ static const(string) fun100( string str, S s) { return str[0 .. s.len] ~ "123"; }
+ static const(string) fun101( string str, const S s) { return str[0 .. s.len] ~ "123"; }
+ static const(string) fun110(const string str, S s) { return str[0 .. s.len] ~ "123"; }
+ static const(string) fun111(const string str, const S s) { return str[0 .. s.len] ~ "123"; }
+
+ static foreach (fun; AliasSeq!(fun000, fun001, fun010, fun011, fun100, fun101, fun110, fun111))
+ {{
+ alias mfun = memoize!fun;
+ assert(mfun("abcdefgh", S(3)) == "abc123");
+
+ alias mfun2 = memoize!(fun, 42);
+ assert(mfun2("asd", S(3)) == "asd123");
+ }}
+}
+
private struct DelegateFaker(F)
{
import std.typecons : FuncInfo, MemberFunctionGenerator;
@@ -1305,6 +1641,11 @@ private struct DelegateFaker(F)
* Convert a callable to a delegate with the same parameter list and
* return type, avoiding heap allocations and use of auxiliary storage.
*
+ * Params:
+ * fp = a function pointer or an aggregate type with `opCall` defined.
+ * Returns:
+ * A delegate with the context pointer pointing to nothing.
+ *
* Example:
* ----
* void doStuff() {
@@ -1321,7 +1662,7 @@ private struct DelegateFaker(F)
*
* BUGS:
* $(UL
- * $(LI Does not work with $(D @safe) functions.)
+ * $(LI Does not work with `@safe` functions.)
* $(LI Ignores C-style / D-style variadic arguments.)
* )
*/
@@ -1467,98 +1808,9 @@ if (isCallable!(F))
}
}
-/**
-Forwards function arguments with saving ref-ness.
-*/
+// forward used to be here but was moved to druntime
template forward(args...)
{
- static if (args.length)
- {
- import std.algorithm.mutation : move;
-
- alias arg = args[0];
- static if (__traits(isRef, arg))
- alias fwd = arg;
- else
- @property fwd()(){ return move(arg); }
- alias forward = AliasSeq!(fwd, forward!(args[1..$]));
- }
- else
- alias forward = AliasSeq!();
-}
-
-///
-@safe unittest
-{
- class C
- {
- static int foo(int n) { return 1; }
- static int foo(ref int n) { return 2; }
- }
- int bar()(auto ref int x) { return C.foo(forward!x); }
-
- assert(bar(1) == 1);
- int i;
- assert(bar(i) == 2);
-}
-
-///
-@safe unittest
-{
- void foo(int n, ref string s) { s = null; foreach (i; 0 .. n) s ~= "Hello"; }
-
- // forwards all arguments which are bound to parameter tuple
- void bar(Args...)(auto ref Args args) { return foo(forward!args); }
-
- // forwards all arguments with swapping order
- void baz(Args...)(auto ref Args args) { return foo(forward!args[$/2..$], forward!args[0..$/2]); }
-
- string s;
- bar(1, s);
- assert(s == "Hello");
- baz(s, 2);
- assert(s == "HelloHello");
-}
-
-@safe unittest
-{
- auto foo(TL...)(auto ref TL args)
- {
- string result = "";
- foreach (i, _; args)
- {
- //pragma(msg, "[",i,"] ", __traits(isRef, args[i]) ? "L" : "R");
- result ~= __traits(isRef, args[i]) ? "L" : "R";
- }
- return result;
- }
-
- string bar(TL...)(auto ref TL args)
- {
- return foo(forward!args);
- }
- string baz(TL...)(auto ref TL args)
- {
- int x;
- return foo(forward!args[3], forward!args[2], 1, forward!args[1], forward!args[0], x);
- }
-
- struct S {}
- S makeS(){ return S(); }
- int n;
- string s;
- assert(bar(S(), makeS(), n, s) == "RRLL");
- assert(baz(S(), makeS(), n, s) == "LLRRRL");
-}
-
-@safe unittest
-{
- ref int foo(return ref int a) { return a; }
- ref int bar(Args)(auto ref Args args)
- {
- return foo(forward!args);
- }
- static assert(!__traits(compiles, { auto x1 = bar(3); })); // case of NG
- int value = 3;
- auto x2 = bar(value); // case of OK
+ import core.lifetime : fun = forward;
+ alias forward = fun!args;
}
diff --git a/libphobos/src/std/getopt.d b/libphobos/src/std/getopt.d
index 5beddcc23b4..d516012ac4a 100644
--- a/libphobos/src/std/getopt.d
+++ b/libphobos/src/std/getopt.d
@@ -3,7 +3,7 @@
/**
Processing of command line options.
-The getopt module implements a $(D getopt) function, which adheres to
+The getopt module implements a `getopt` function, which adheres to
the POSIX syntax for command line options. GNU extensions are
supported in the form of long options introduced by a double dash
("--"). Support for bundling of command line options, as was the case
@@ -13,12 +13,12 @@ enabled by default.
Copyright: Copyright Andrei Alexandrescu 2008 - 2015.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.org, Andrei Alexandrescu)
-Credits: This module and its documentation are inspired by Perl's $(HTTP
- perldoc.perl.org/Getopt/Long.html, Getopt::Long) module. The syntax of
- D's $(D getopt) is simpler than its Perl counterpart because $(D
+Credits: This module and its documentation are inspired by Perl's
+ $(HTTPS perldoc.perl.org/Getopt/Long.html, Getopt::Long) module. The syntax of
+ D's `getopt` is simpler than its Perl counterpart because $(D
getopt) infers the expected parameter types from the static types of
the passed-in pointers.
-Source: $(PHOBOSSRC std/_getopt.d)
+Source: $(PHOBOSSRC std/getopt.d)
*/
/*
Copyright Andrei Alexandrescu 2008 - 2015.
@@ -28,16 +28,17 @@ Distributed under the Boost Software License, Version 1.0.
*/
module std.getopt;
-import std.exception; // basicExceptionCtors
+import std.exception : basicExceptionCtors;
import std.traits;
/**
Thrown on one of the following conditions:
$(UL
$(LI An unrecognized command-line argument is passed, and
- $(D std.getopt.config.passThrough) was not present.)
+ `std.getopt.config.passThrough` was not present.)
$(LI A command-line option was not found, and
- $(D std.getopt.config.required) was present.)
+ `std.getopt.config.required` was present.)
+ $(LI A callback option is missing a value.)
)
*/
class GetOptException : Exception
@@ -80,31 +81,31 @@ void main(string[] args)
}
---------
- The $(D getopt) function takes a reference to the command line
- (as received by $(D main)) as its first argument, and an
+ The `getopt` function takes a reference to the command line
+ (as received by `main`) as its first argument, and an
unbounded number of pairs of strings and pointers. Each string is an
option meant to "fill" the value referenced by the pointer to its
right (the "bound" pointer). The option string in the call to
- $(D getopt) should not start with a dash.
+ `getopt` should not start with a dash.
In all cases, the command-line options that were parsed and used by
- $(D getopt) are removed from $(D args). Whatever in the
- arguments did not look like an option is left in $(D args) for
+ `getopt` are removed from `args`. Whatever in the
+ arguments did not look like an option is left in `args` for
further processing by the program. Values that were unaffected by the
options are not touched, so a common idiom is to initialize options
- to their defaults and then invoke $(D getopt). If a
+ to their defaults and then invoke `getopt`. If a
command-line argument is recognized as an option with a parameter and
the parameter cannot be parsed properly (e.g., a number is expected
- but not present), a $(D ConvException) exception is thrown.
- If $(D std.getopt.config.passThrough) was not passed to $(D getopt)
- and an unrecognized command-line argument is found, a $(D GetOptException)
- is thrown.
+ but not present), a `ConvException` exception is thrown.
+ If `std.getopt.config.passThrough` was not passed to `getopt`
+ and an unrecognized command-line argument is found, or if a required
+ argument is missing a `GetOptException` is thrown.
- Depending on the type of the pointer being bound, $(D getopt)
+ Depending on the type of the pointer being bound, `getopt`
recognizes the following kinds of options:
$(OL
- $(LI $(I Boolean options). A lone argument sets the option to $(D true).
+ $(LI $(I Boolean options). A lone argument sets the option to `true`.
Additionally $(B true) or $(B false) can be set within the option separated
with an "=" sign:
@@ -113,11 +114,11 @@ void main(string[] args)
getopt(args, "verbose", &verbose, "debug", &debugging);
---------
- To set $(D verbose) to $(D true), invoke the program with either
- $(D --verbose) or $(D --verbose=true).
+ To set `verbose` to `true`, invoke the program with either
+ `--verbose` or `--verbose=true`.
- To set $(D debugging) to $(D false), invoke the program with
- $(D --debugging=false).
+ To set `debugging` to `false`, invoke the program with
+ `--debugging=false`.
)
$(LI $(I Numeric options.) If an option is bound to a numeric type, a
@@ -129,8 +130,8 @@ void main(string[] args)
getopt(args, "timeout", &timeout);
---------
- To set $(D timeout) to $(D 5), invoke the program with either
- $(D --timeout=5) or $(D --timeout 5).
+ To set `timeout` to `5`, invoke the program with either
+ `--timeout=5` or $(D --timeout 5).
)
$(LI $(I Incremental options.) If an option name has a "+" suffix and is
@@ -145,7 +146,7 @@ void main(string[] args)
Invoking the program with "--paranoid --paranoid --paranoid" will set $(D
paranoid) to 3. Note that an incremental option never expects a parameter,
e.g., in the command line "--paranoid 42 --paranoid", the "42" does not set
- $(D paranoid) to 42; instead, $(D paranoid) is set to 2 and "42" is not
+ `paranoid` to 42; instead, `paranoid` is set to 2 and "42" is not
considered as part of the normal program arguments.
)
@@ -159,8 +160,8 @@ void main(string[] args)
getopt(args, "color", &color);
---------
- To set $(D color) to $(D Color.yes), invoke the program with either
- $(D --color=yes) or $(D --color yes).
+ To set `color` to `Color.yes`, invoke the program with either
+ `--color=yes` or $(D --color yes).
)
$(LI $(I String options.) If an option is bound to a string, a string is
@@ -173,7 +174,7 @@ getopt(args, "output", &outputFile);
---------
Invoking the program with "--output=myfile.txt" or "--output myfile.txt"
- will set $(D outputFile) to "myfile.txt". If you want to pass a string
+ will set `outputFile` to "myfile.txt". If you want to pass a string
containing spaces, you need to use the quoting that is appropriate to your
shell, e.g. --output='my file.txt'.
)
@@ -187,14 +188,15 @@ getopt(args, "output", &outputFiles);
---------
Invoking the program with "--output=myfile.txt --output=yourfile.txt" or
- "--output myfile.txt --output yourfile.txt" will set $(D outputFiles) to
+ "--output myfile.txt --output yourfile.txt" will set `outputFiles` to
$(D [ "myfile.txt", "yourfile.txt" ]).
- Alternatively you can set $(LREF arraySep) as the element separator:
+ Alternatively you can set $(LREF arraySep) to allow multiple elements in
+ one parameter.
---------
string[] outputFiles;
-arraySep = ","; // defaults to "", separation by whitespace
+arraySep = ","; // defaults to "", meaning one element per parameter
getopt(args, "output", &outputFiles);
---------
@@ -211,13 +213,13 @@ getopt(args, "tune", &tuningParms);
---------
Invoking the program with e.g. "--tune=alpha=0.5 --tune beta=0.6" will set
- $(D tuningParms) to [ "alpha" : 0.5, "beta" : 0.6 ].
+ `tuningParms` to [ "alpha" : 0.5, "beta" : 0.6 ].
Alternatively you can set $(LREF arraySep) as the element separator:
---------
double[string] tuningParms;
-arraySep = ","; // defaults to "", separation by whitespace
+arraySep = ","; // defaults to "", meaning one element per parameter
getopt(args, "tune", &tuningParms);
---------
@@ -309,7 +311,7 @@ getopt(args, "verbose|loquacious|garrulous", &verbose);
Case:
By default options are case-insensitive. You can change that behavior
-by passing $(D getopt) the $(D caseSensitive) directive like this:
+by passing `getopt` the `caseSensitive` directive like this:
---------
bool foo, bar;
@@ -321,8 +323,8 @@ getopt(args,
In the example above, "--foo" and "--bar" are recognized, but "--Foo", "--Bar",
"--FOo", "--bAr", etc. are rejected.
-The directive is active until the end of $(D getopt), or until the
-converse directive $(D caseInsensitive) is encountered:
+The directive is active until the end of `getopt`, or until the
+converse directive `caseInsensitive` is encountered:
---------
bool foo, bar;
@@ -341,28 +343,21 @@ option "bar" was parsed.
Short_versus_long_options:
Traditionally, programs accepted single-letter options preceded by
-only one dash (e.g. $(D -t)). $(D getopt) accepts such parameters
-seamlessly. When used with a double-dash (e.g. $(D --t)), a
+only one dash (e.g. `-t`). `getopt` accepts such parameters
+seamlessly. When used with a double-dash (e.g. `--t`), a
single-letter option behaves the same as a multi-letter option. When
-used with a single dash, a single-letter option is accepted. If the
-option has a parameter, that must be "stuck" to the option without
-any intervening space or "=":
-
----------
-uint timeout;
-getopt(args, "timeout|t", &timeout);
----------
+used with a single dash, a single-letter option is accepted.
-To set $(D timeout) to $(D 5), use either of the following: $(D --timeout=5),
-$(D --timeout 5), $(D --t=5), $(D --t 5), or $(D -t5). Forms such as $(D -t 5)
-and $(D -timeout=5) will be not accepted.
+To set `timeout` to `5`, use either of the following: `--timeout=5`,
+`--timeout 5`, `--t=5`, `--t 5`, `-t5`, or `-t 5`. Forms such as
+`-timeout=5` will be not accepted.
For more details about short options, refer also to the next section.
Bundling:
Single-letter options can be bundled together, i.e. "-abc" is the same as
$(D "-a -b -c"). By default, this option is turned off. You can turn it on
-with the $(D std.getopt.config.bundling) directive:
+with the `std.getopt.config.bundling` directive:
---------
bool foo, bar;
@@ -373,7 +368,7 @@ getopt(args,
---------
In case you want to only enable bundling for some of the parameters,
-bundling can be turned off with $(D std.getopt.config.noBundling).
+bundling can be turned off with `std.getopt.config.noBundling`.
Required:
An option can be marked as required. If that option is not present in the
@@ -387,13 +382,13 @@ getopt(args,
"bar|b", &bar);
---------
-Only the option directly following $(D std.getopt.config.required) is
+Only the option directly following `std.getopt.config.required` is
required.
Passing_unrecognized_options_through:
If an application needs to do its own processing of whichever arguments
-$(D getopt) did not understand, it can pass the
-$(D std.getopt.config.passThrough) directive to $(D getopt):
+`getopt` did not understand, it can pass the
+`std.getopt.config.passThrough` directive to `getopt`:
---------
bool foo, bar;
@@ -404,22 +399,22 @@ getopt(args,
---------
An unrecognized option such as "--baz" will be found untouched in
-$(D args) after $(D getopt) returns.
+`args` after `getopt` returns.
Help_Information_Generation:
If an option string is followed by another string, this string serves as a
-description for this option. The $(D getopt) function returns a struct of type
-$(D GetoptResult). This return value contains information about all passed options
+description for this option. The `getopt` function returns a struct of type
+`GetoptResult`. This return value contains information about all passed options
as well a $(D bool GetoptResult.helpWanted) flag indicating whether information
-about these options was requested. The $(D getopt) function always adds an option for
+about these options was requested. The `getopt` function always adds an option for
`--help|-h` to set the flag if the option is seen on the command line.
Options_Terminator:
-A lone double-dash terminates $(D getopt) gathering. It is used to
+A lone double-dash terminates `getopt` gathering. It is used to
separate program options from other parameters (e.g., options to be passed
to another program). Invoking the example above with $(D "--foo -- --bar")
-parses foo but leaves "--bar" in $(D args). The double-dash itself is
-removed from the argument array unless the $(D std.getopt.config.keepEndOfOptions)
+parses foo but leaves "--bar" in `args`. The double-dash itself is
+removed from the argument array unless the `std.getopt.config.keepEndOfOptions`
directive is given.
*/
GetoptResult getopt(T...)(ref string[] args, T opts)
@@ -460,9 +455,9 @@ GetoptResult getopt(T...)(ref string[] args, T opts)
}
/**
- Configuration options for $(D getopt).
+ Configuration options for `getopt`.
- You can pass them to $(D getopt) in any position, except in between an option
+ You can pass them to `getopt` in any position, except in between an option
string and its bound pointer.
*/
enum config {
@@ -486,9 +481,9 @@ enum config {
required
}
-/** The result of the $(D getopt) function.
+/** The result of the `getopt` function.
-$(D helpWanted) is set if the option `--help` or `-h` was passed to the option parser.
+`helpWanted` is set if the option `--help` or `-h` was passed to the option parser.
*/
struct GetoptResult {
bool helpWanted; /// Flag indicating if help was requested
@@ -561,7 +556,6 @@ follow this pattern:
private template optionValidator(A...)
{
import std.format : format;
- import std.typecons : staticIota;
enum fmt = "getopt validator: %s (at position %d)";
enum isReceiver(T) = isPointer!T || (is(T == function)) || (is(T == delegate));
@@ -580,24 +574,27 @@ private template optionValidator(A...)
{
msg = format(fmt, "invalid argument type: " ~ A[0].stringof, 0);
}
- else foreach (i; staticIota!(1, A.length))
+ else
{
- static if (!isReceiver!(A[i]) && !isOptionStr!(A[i]) &&
- !(is(A[i] == config)))
- {
- msg = format(fmt, "invalid argument type: " ~ A[i].stringof, i);
- break;
- }
- else static if (isReceiver!(A[i]) && !isOptionStr!(A[i-1]))
- {
- msg = format(fmt, "a receiver can not be preceeded by a receiver", i);
- break;
- }
- else static if (i > 1 && isOptionStr!(A[i]) && isOptionStr!(A[i-1])
- && isSomeString!(A[i-2]))
+ static foreach (i; 1 .. A.length)
{
- msg = format(fmt, "a string can not be preceeded by two strings", i);
- break;
+ static if (!isReceiver!(A[i]) && !isOptionStr!(A[i]) &&
+ !(is(A[i] == config)))
+ {
+ msg = format(fmt, "invalid argument type: " ~ A[i].stringof, i);
+ goto end;
+ }
+ else static if (isReceiver!(A[i]) && !isOptionStr!(A[i-1]))
+ {
+ msg = format(fmt, "a receiver can not be preceeded by a receiver", i);
+ goto end;
+ }
+ else static if (i > 1 && isOptionStr!(A[i]) && isOptionStr!(A[i-1])
+ && isSomeString!(A[i-2]))
+ {
+ msg = format(fmt, "a string can not be preceeded by two strings", i);
+ goto end;
+ }
}
}
static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config))
@@ -606,6 +603,7 @@ private template optionValidator(A...)
A.length -1);
}
}
+ end:
return msg;
}
enum message = validator;
@@ -654,8 +652,10 @@ private template optionValidator(A...)
static assert(optionValidator!(C,A,P,C,A,S,F) == "");
}
-@system unittest // bugzilla 15914
+// https://issues.dlang.org/show_bug.cgi?id=15914
+@safe unittest
{
+ import std.exception : assertThrown;
bool opt;
string[] args = ["program", "-a"];
getopt(args, config.passThrough, 'a', &opt);
@@ -725,13 +725,13 @@ private void getoptImpl(T...)(ref string[] args, ref configuration cfg,
static if (is(typeof(opts[1]) : string))
{
- auto receiver = opts[2];
+ alias receiver = opts[2];
optionHelp.help = opts[1];
immutable lowSliceIdx = 3;
}
else
{
- auto receiver = opts[1];
+ alias receiver = opts[1];
immutable lowSliceIdx = 2;
}
@@ -869,7 +869,6 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
{
import std.exception : enforce;
// non-boolean option, which might include an argument
- //enum isCallbackWithOneParameter = is(typeof(receiver("")) : void);
enum isCallbackWithLessThanTwoParameters =
(is(typeof(receiver) == delegate) || is(typeof(*receiver) == function)) &&
!is(typeof(receiver("", "")));
@@ -877,7 +876,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
{
// Eat the next argument too. Check to make sure there's one
// to be eaten first, though.
- enforce(i < args.length,
+ enforce!GetOptException(i < args.length,
"Missing value for argument " ~ a ~ ".");
val = args[i];
args = args[0 .. i] ~ args[i + 1 .. $];
@@ -907,13 +906,17 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
}
else static if (is(typeof(receiver("")) : void))
{
- static assert(is(typeof(receiver("")) : void));
+ alias RType = typeof(receiver(""));
+ static assert(is(RType : void),
+ "Invalid receiver return type " ~ RType.stringof);
// boolean-style receiver
receiver(option);
}
else
{
- static assert(is(typeof(receiver()) : void));
+ alias RType = typeof(receiver());
+ static assert(is(RType : void),
+ "Invalid receiver return type " ~ RType.stringof);
// boolean-style receiver without argument
receiver();
}
@@ -973,8 +976,8 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
return ret;
}
-// 17574
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=17574
+@safe unittest
{
import std.algorithm.searching : startsWith;
@@ -993,8 +996,8 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
assert(goe.msg.startsWith("Could not find"));
}
-// 5316 - arrays with arraySep
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=5316 - arrays with arraySep
+@safe unittest
{
import std.conv;
@@ -1022,8 +1025,8 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
assert(names == ["foo", "bar", "baz"], to!string(names));
}
-// 5316 - associative arrays with arraySep
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=5316 - associative arrays with arraySep
+@safe unittest
{
import std.conv;
@@ -1055,30 +1058,33 @@ private bool handleOption(R)(string option, R receiver, ref string[] args,
/**
The option character (default '-').
- Defaults to '-' but it can be assigned to prior to calling $(D getopt).
+ Defaults to '-' but it can be assigned to prior to calling `getopt`.
*/
dchar optionChar = '-';
/**
The string that conventionally marks the end of all options (default '--').
- Defaults to "--" but can be assigned to prior to calling $(D getopt). Assigning an
- empty string to $(D endOfOptions) effectively disables it.
+ Defaults to "--" but can be assigned to prior to calling `getopt`. Assigning an
+ empty string to `endOfOptions` effectively disables it.
*/
string endOfOptions = "--";
/**
The assignment character used in options with parameters (default '=').
- Defaults to '=' but can be assigned to prior to calling $(D getopt).
+ Defaults to '=' but can be assigned to prior to calling `getopt`.
*/
dchar assignChar = '=';
/**
- The string used to separate the elements of an array or associative array
- (default is "" which means the elements are separated by whitespace).
+ When set to "", parameters to array and associative array receivers are
+ treated as an individual argument. That is, only one argument is appended or
+ inserted per appearance of the option switch. If `arraySep` is set to
+ something else, then each parameter is first split by the separator, and the
+ individual pieces are treated as arguments to the same option.
- Defaults to "" but can be assigned to prior to calling $(D getopt).
+ Defaults to "" but can be assigned to prior to calling `getopt`.
*/
string arraySep = "";
@@ -1097,12 +1103,12 @@ private struct configuration
ubyte, "", 2));
}
-private bool optMatch(string arg, string optPattern, ref string value,
+private bool optMatch(string arg, scope string optPattern, ref string value,
configuration cfg) @safe
{
- import std.array : split;
+ import std.algorithm.iteration : splitter;
import std.string : indexOf;
- import std.uni : toUpper;
+ import std.uni : icmp;
//writeln("optMatch:\n ", arg, "\n ", optPattern, "\n ", value);
//scope(success) writeln("optMatch result: ", value);
if (arg.length < 2 || arg[0] != optionChar) return false;
@@ -1142,11 +1148,10 @@ private bool optMatch(string arg, string optPattern, ref string value,
}
//writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value);
// Split the option
- const variants = split(optPattern, "|");
- foreach (v ; variants)
+ foreach (v; splitter(optPattern, "|"))
{
//writeln("Trying variant: ", v, " against ", arg);
- if (arg == v || !cfg.caseSensitive && toUpper(arg) == toUpper(v))
+ if (arg == v || (!cfg.caseSensitive && icmp(arg, v) == 0))
return true;
if (cfg.bundling && !isLong && v.length == 1
&& indexOf(arg, v) >= 0)
@@ -1176,10 +1181,10 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
}
}
-@system unittest
+@safe unittest
{
import std.conv;
- import std.math;
+ import std.math.operations : isClose;
uint paranoid = 2;
string[] args = ["program.name", "--paranoid", "--paranoid", "--paranoid"];
@@ -1236,8 +1241,8 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
getopt(testArgs, "tune", &tuningParms);
assert(testArgs.length == 1);
assert(tuningParms.length == 2);
- assert(approxEqual(tuningParms["alpha"], 0.5));
- assert(approxEqual(tuningParms["beta"], 0.6));
+ assert(isClose(tuningParms["alpha"], 0.5));
+ assert(isClose(tuningParms["beta"], 0.6));
arraySep = "";
}
@@ -1356,6 +1361,12 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
args = ["program.name", "--verbose", "2"];
try { getopt(args, "verbose", &myStaticHandler3); assert(0); }
catch (MyEx ex) { assert(ex.option == "verbose" && ex.value == "2"); }
+
+ // check that GetOptException is thrown if the value is missing
+ args = ["program.name", "--verbose"];
+ try { getopt(args, "verbose", &myStaticHandler3); assert(0); }
+ catch (GetOptException e) {}
+ catch (Exception e) { assert(0); }
}
@safe unittest // @safe std.getopt.config option use
@@ -1368,9 +1379,9 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(x == 2);
}
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=2142
+@safe unittest
{
- // From bugzilla 2142
bool f_linenum, f_filename;
string[] args = [ "", "-nl" ];
getopt
@@ -1385,9 +1396,9 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(f_filename);
}
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=6887
+@safe unittest
{
- // From bugzilla 6887
string[] p;
string[] args = ["", "-pa"];
getopt(args, "p", &p);
@@ -1395,25 +1406,25 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(p[0] == "a");
}
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=6888
+@safe unittest
{
- // From bugzilla 6888
int[string] foo;
auto args = ["", "-t", "a=1"];
getopt(args, "t", &foo);
assert(foo == ["a":1]);
}
-@system unittest
+// https://issues.dlang.org/show_bug.cgi?id=9583
+@safe unittest
{
- // From bugzilla 9583
int opt;
auto args = ["prog", "--opt=123", "--", "--a", "--b", "--c"];
getopt(args, "opt", &opt);
assert(args == ["prog", "--a", "--b", "--c"]);
}
-@system unittest
+@safe unittest
{
string foo, bar;
auto args = ["prog", "-thello", "-dbar=baz"];
@@ -1421,7 +1432,7 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(foo == "hello");
assert(bar == "bar=baz");
- // From bugzilla 5762
+ // From https://issues.dlang.org/show_bug.cgi?id=5762
string a;
args = ["prog", "-a-0x12"];
getopt(args, config.bundling, "a|addr", &a);
@@ -1430,7 +1441,7 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
getopt(args, config.bundling, "a|addr", &a);
assert(a == "-0x12");
- // From https://d.puremagic.com/issues/show_bug.cgi?id=11764
+ // From https://issues.dlang.org/show_bug.cgi?id=11764
args = ["main", "-test"];
bool opt;
args.getopt(config.passThrough, "opt", &opt);
@@ -1448,7 +1459,8 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(o == "str");
}
-@system unittest // 5228
+// https://issues.dlang.org/show_bug.cgi?id=5228
+@safe unittest
{
import std.conv;
import std.exception;
@@ -1461,7 +1473,8 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assertThrown!ConvException(getopt(args, "abc", &abc));
}
-@system unittest // From bugzilla 7693
+// https://issues.dlang.org/show_bug.cgi?id=7693
+@safe unittest
{
import std.exception;
@@ -1481,7 +1494,8 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assertNotThrown(getopt(args, "foo", &foo));
}
-@system unittest // same bug as 7693 only for bool
+// Same as https://issues.dlang.org/show_bug.cgi?id=7693 only for `bool`
+@safe unittest
{
import std.exception;
@@ -1493,7 +1507,7 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(foo);
}
-@system unittest
+@safe unittest
{
bool foo;
auto args = ["prog", "--foo"];
@@ -1501,7 +1515,7 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(foo);
}
-@system unittest
+@safe unittest
{
bool foo;
bool bar;
@@ -1512,7 +1526,7 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(bar);
}
-@system unittest
+@safe unittest
{
bool foo;
bool bar;
@@ -1524,7 +1538,7 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(bar);
}
-@system unittest
+@safe unittest
{
import std.exception;
@@ -1536,7 +1550,7 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
config.passThrough));
}
-@system unittest
+@safe unittest
{
import std.exception;
@@ -1550,7 +1564,7 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(!bar);
}
-@system unittest
+@safe unittest
{
bool foo;
auto args = ["prog", "-f"];
@@ -1566,8 +1580,9 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(r.helpWanted);
}
-// Issue 13316 - std.getopt: implicit help option breaks the next argument
-@system unittest
+// std.getopt: implicit help option breaks the next argument
+// https://issues.dlang.org/show_bug.cgi?id=13316
+@safe unittest
{
string[] args = ["program", "--help", "--", "something"];
getopt(args);
@@ -1583,8 +1598,9 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(args == ["program", "nonoption", "--option"]);
}
-// Issue 13317 - std.getopt: endOfOptions broken when it doesn't look like an option
-@system unittest
+// std.getopt: endOfOptions broken when it doesn't look like an option
+// https://issues.dlang.org/show_bug.cgi?id=13317
+@safe unittest
{
auto endOfOptionsBackup = endOfOptions;
scope(exit) endOfOptions = endOfOptionsBackup;
@@ -1596,13 +1612,25 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow
assert(args == ["program", "--option"]);
}
-/** This function prints the passed $(D Option)s and text in an aligned manner on $(D stdout).
+// make std.getopt ready for DIP 1000
+// https://issues.dlang.org/show_bug.cgi?id=20480
+@safe unittest
+{
+ string[] args = ["test", "--foo", "42", "--bar", "BAR"];
+ int foo;
+ string bar;
+ getopt(args, "foo", &foo, "bar", "bar help", &bar);
+ assert(foo == 42);
+ assert(bar == "BAR");
+}
+
+/** This function prints the passed `Option`s and text in an aligned manner on `stdout`.
The passed text will be printed first, followed by a newline, then the short
and long version of every option will be printed. The short and long version
-will be aligned to the longest option of every $(D Option) passed. If the option
+will be aligned to the longest option of every `Option` passed. If the option
is required, then "Required:" will be printed after the long version of the
-$(D Option). If a help message is present it will be printed next. The format is
+`Option`. If a help message is present it will be printed next. The format is
illustrated by this code:
------------
@@ -1616,7 +1644,7 @@ foreach (it; opt)
Params:
text = The text to printed at the beginning of the help output.
- opt = The $(D Option) extracted from the $(D getopt) parameter.
+ opt = The `Option` extracted from the `getopt` parameter.
*/
void defaultGetoptPrinter(string text, Option[] opt)
{
@@ -1625,19 +1653,20 @@ void defaultGetoptPrinter(string text, Option[] opt)
defaultGetoptFormatter(stdout.lockingTextWriter(), text, opt);
}
-/** This function writes the passed text and $(D Option) into an output range
+/** This function writes the passed text and `Option` into an output range
in the manner described in the documentation of function
-$(D defaultGetoptPrinter).
+`defaultGetoptPrinter`, unless the style option is used.
Params:
output = The output range used to write the help information.
text = The text to print at the beginning of the help output.
- opt = The $(D Option) extracted from the $(D getopt) parameter.
+ opt = The `Option` extracted from the `getopt` parameter.
+ style = The manner in which to display the output of each `Option.`
*/
-void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt)
+void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt, string style = "%*s %*s%*s%s\n")
{
import std.algorithm.comparison : min, max;
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
output.formattedWrite("%s\n", text);
@@ -1655,12 +1684,12 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt)
foreach (it; opt)
{
- output.formattedWrite("%*s %*s%*s%s\n", ls, it.optShort, ll, it.optLong,
+ output.formattedWrite(style, ls, it.optShort, ll, it.optLong,
hasRequired ? re.length : 1, it.required ? re : " ", it.help);
}
}
-@system unittest
+@safe unittest
{
import std.conv;
@@ -1689,7 +1718,7 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt)
assert(wanted == helpMsg);
}
-@system unittest
+@safe unittest
{
import std.array ;
import std.conv;
@@ -1718,7 +1747,8 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt)
assert(wanted == helpMsg, helpMsg ~ wanted);
}
-@system unittest // Issue 14724
+// https://issues.dlang.org/show_bug.cgi?id=14724
+@safe unittest
{
bool a;
auto args = ["prog", "--help"];
@@ -1740,7 +1770,8 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt)
// throw on duplicate options
@system unittest
{
- import core.exception;
+ import core.exception : AssertError;
+ import std.exception : assertNotThrown, assertThrown;
auto args = ["prog", "--abc", "1"];
int abc, def;
assertThrown!AssertError(getopt(args, "abc", &abc, "abc", &abc));
@@ -1748,7 +1779,8 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt)
assertNotThrown!AssertError(getopt(args, "abc", &abc, "def", &def));
}
-@system unittest // Issue 17327 repeated option use
+// https://issues.dlang.org/show_bug.cgi?id=17327 repeated option use
+@safe unittest
{
long num = 0;
@@ -1835,7 +1867,9 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt)
assert(y == 50);
}
-@system unittest // Hyphens at the start of option values; Issue 17650
+// Hyphens at the start of option values;
+// https://issues.dlang.org/show_bug.cgi?id=17650
+@safe unittest
{
auto args = ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"];
@@ -1855,3 +1889,32 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt)
assert(c == '-');
assert(f == "-");
}
+
+@safe unittest
+{
+ import std.conv;
+
+ import std.array;
+ import std.string;
+ bool a;
+ auto args = ["prog", "--foo"];
+ auto t = getopt(args, "foo|f", "Help", &a);
+ string s;
+ auto app = appender!string();
+ defaultGetoptFormatter(app, "Some Text", t.options, "\t\t%*s %*s%*s\n%s\n");
+
+ string helpMsg = app.data;
+ //writeln(helpMsg);
+ assert(helpMsg.length);
+ assert(helpMsg.count("\n") == 5, to!string(helpMsg.count("\n")) ~ " "
+ ~ helpMsg);
+ assert(helpMsg.indexOf("--foo") != -1);
+ assert(helpMsg.indexOf("-f") != -1);
+ assert(helpMsg.indexOf("-h") != -1);
+ assert(helpMsg.indexOf("--help") != -1);
+ assert(helpMsg.indexOf("Help") != -1);
+
+ string wanted = "Some Text\n\t\t-f --foo \nHelp\n\t\t-h --help \nThis help "
+ ~ "information.\n";
+ assert(wanted == helpMsg);
+}
diff --git a/libphobos/src/std/internal/attributes.d b/libphobos/src/std/internal/attributes.d
new file mode 100644
index 00000000000..2405326b97f
--- /dev/null
+++ b/libphobos/src/std/internal/attributes.d
@@ -0,0 +1,11 @@
+module std.internal.attributes;
+
+/**
+Used to annotate `unittest`s which need to be tested in a `-betterC` environment.
+
+Such `unittest`s will be compiled and executed without linking druntime in, with
+a `__traits(getUnitTests, mixin(__MODULE__))` style test runner.
+Note that just like any other `unittest` in phobos, they will also be compiled
+and executed without `-betterC`.
+*/
+package(std) enum betterC = 1;
diff --git a/libphobos/src/std/internal/cstring.d b/libphobos/src/std/internal/cstring.d
index e5bc7f744bc..a61ee81cc45 100644
--- a/libphobos/src/std/internal/cstring.d
+++ b/libphobos/src/std/internal/cstring.d
@@ -11,7 +11,7 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Denis Shelomovskij
Macros:
-COREREF = $(HTTP dlang.org/phobos/core_$1.html#$2, $(D core.$1.$2))
+COREREF = $(HTTP dlang.org/phobos/core_$1.html#$2, `core.$1.$2`)
*/
module std.internal.cstring;
@@ -24,16 +24,16 @@ module std.internal.cstring;
import core.sys.posix.stdlib : setenv;
import std.exception : enforce;
- void setEnvironment(in char[] name, in char[] value)
+ void setEnvironment(scope const(char)[] name, scope const(char)[] value)
{ enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); }
}
version (Windows)
{
- import core.sys.windows.windows : SetEnvironmentVariableW;
+ import core.sys.windows.winbase : SetEnvironmentVariableW;
import std.exception : enforce;
- void setEnvironment(in char[] name, in char[] value)
+ void setEnvironment(scope const(char)[] name, scope const(char)[] value)
{ enforce(SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW())); }
}
}
@@ -41,18 +41,6 @@ module std.internal.cstring;
import std.range;
import std.traits;
-version (unittest)
-@property inout(C)[] asArray(C)(inout C* cstr) pure nothrow @nogc @trusted
-if (isSomeChar!C)
-in { assert(cstr); }
-body
-{
- size_t length = 0;
- while (cstr[length])
- ++length;
- return cstr[0 .. length];
-}
-
/**
Creates temporary 0-terminated $(I C string) with copy of passed text.
@@ -63,8 +51,8 @@ Params:
Returns:
The value returned is implicitly convertible to $(D const To*) and
-has two properties: $(D ptr) to access $(I C string) as $(D const To*)
-and $(D buffPtr) to access it as $(D To*).
+has two properties: `ptr` to access $(I C string) as $(D const To*)
+and `buffPtr` to access it as `To*`.
The value returned can be indexed by [] to access it as an array.
@@ -77,149 +65,99 @@ primary expression.
Implementation_note:
For small strings tempCString will use stack allocated buffer,
for large strings (approximately 250 characters and more) it will
-allocate temporary one using C's $(D malloc).
+allocate temporary one using C's `malloc`.
Note:
This function is intended to be used in function call expression (like
-$(D strlen(str.tempCString()))). Incorrect usage of this function may
+`strlen(str.tempCString())`). Incorrect usage of this function may
lead to memory corruption.
See $(RED WARNING) in $(B Examples) section.
*/
-auto tempCString(To = char, From)(From str)
+auto tempCString(To = char, From)(scope From str)
if (isSomeChar!To && (isInputRange!From || isSomeString!From) &&
isSomeChar!(ElementEncodingType!From))
{
-
alias CF = Unqual!(ElementEncodingType!From);
- enum To* useStack = () @trusted { return cast(To*) size_t.max; }();
-
- static struct Res
- {
- @trusted:
- nothrow @nogc:
+ auto res = TempCStringBuffer!To.trustedVoidInit(); // expensive to fill _buff[]
- @disable this();
- @disable this(this);
- alias ptr this;
-
- @property inout(To)* buffPtr() inout pure
- {
- return _ptr == useStack ? _buff.ptr : _ptr;
- }
+ // Note: res._ptr can't point to res._buff as structs are movable.
- @property const(To)* ptr() const pure
+ // https://issues.dlang.org/show_bug.cgi?id=14980
+ static if (isSomeString!From)
+ {
+ if (str is null)
{
- return buffPtr;
+ res._length = 0;
+ res._ptr = null;
+ return res;
}
+ }
- const(To)[] opIndex() const pure
+ // Use slice assignment if available.
+ static if (To.sizeof == CF.sizeof && is(typeof(res._buff[0 .. str.length] = str[])))
+ {
+ if (str.length < res._buff.length)
{
- return buffPtr[0 .. _length];
+ res._buff[0 .. str.length] = str[];
+ res._buff[str.length] = 0;
+ res._ptr = res.useStack;
}
-
- ~this()
+ else
{
- if (_ptr != useStack)
+ import std.internal.memory : enforceMalloc;
+ if (false)
{
- import core.stdc.stdlib : free;
- free(_ptr);
+ // This code is removed by the compiler but causes `@safe`ty
+ // to be inferred correctly.
+ CF[0] x;
+ x[] = str[0 .. 0];
}
+ res._ptr = () @trusted {
+ auto p = cast(CF*) enforceMalloc((str.length + 1) * CF.sizeof);
+ p[0 .. str.length] = str[];
+ p[str.length] = 0;
+ return cast(To*) p;
+ }();
}
-
- private:
- To* _ptr;
- size_t _length; // length of the string
-
- // the 'small string optimization'
- version (unittest)
- {
- // smaller size to trigger reallocations. Padding is to account for
- // unittest/non-unittest cross-compilation (to avoid corruption)
- To[16 / To.sizeof] _buff;
- To[(256 - 16) / To.sizeof] _unittest_pad;
- }
- else
- {
- To[256 / To.sizeof] _buff; // production size
- }
-
- static Res trustedVoidInit() { Res res = void; return res; }
+ res._length = str.length;
+ return res;
}
-
- Res res = Res.trustedVoidInit(); // expensive to fill _buff[]
-
- // Note: res._ptr can't point to res._buff as structs are movable.
-
- To[] p;
- bool p_is_onstack = true;
- size_t i;
-
- static To[] trustedRealloc(To[] buf, size_t i, To[] res, size_t strLength, bool res_is_onstack)
- @trusted @nogc nothrow
+ else
{
- pragma(inline, false); // because it's rarely called
-
- import core.exception : onOutOfMemoryError;
- import core.stdc.stdlib : malloc, realloc;
- import core.stdc.string : memcpy;
+ static assert(!(isSomeString!From && CF.sizeof == To.sizeof), "Should be using slice assignment.");
+ To[] p = res._buff;
+ size_t i;
- if (res_is_onstack)
+ size_t strLength;
+ static if (hasLength!From)
{
- size_t newlen = res.length * 3 / 2;
- if (newlen <= strLength)
- newlen = strLength + 1; // +1 for terminating 0
- auto ptr = cast(To*) malloc(newlen * To.sizeof);
- if (!ptr)
- onOutOfMemoryError();
- memcpy(ptr, res.ptr, i * To.sizeof);
- return ptr[0 .. newlen];
+ strLength = str.length;
}
+ import std.utf : byUTF;
+ static if (isSomeString!From)
+ auto r = cast(const(CF)[])str; // because inout(CF) causes problems with byUTF
else
+ alias r = str;
+ To[] heapBuffer;
+ foreach (const c; byUTF!(Unqual!To)(r))
{
- if (buf.length >= size_t.max / (2 * To.sizeof))
- onOutOfMemoryError();
- const newlen = buf.length * 3 / 2;
- auto ptr = cast(To*) realloc(buf.ptr, newlen * To.sizeof);
- if (!ptr)
- onOutOfMemoryError();
- return ptr[0 .. newlen];
- }
- }
-
- size_t strLength;
- static if (hasLength!From)
- {
- strLength = str.length;
- }
- import std.utf : byUTF;
- static if (isSomeString!From)
- {
- auto r = cast(const(CF)[])str; // because inout(CF) causes problems with byUTF
- if (r is null) // Bugzilla 14980
- {
- res._ptr = null;
- return res;
- }
- }
- else
- alias r = str;
- To[] q = res._buff;
- foreach (const c; byUTF!(Unqual!To)(r))
- {
- if (i + 1 == q.length)
- {
- p = trustedRealloc(p, i, res._buff, strLength, p_is_onstack);
- p_is_onstack = false;
- q = p;
+ if (i + 1 == p.length)
+ {
+ if (heapBuffer is null)
+ heapBuffer = trustedReallocStack(p, strLength);
+ else
+ heapBuffer = trustedRealloc(heapBuffer);
+ p = heapBuffer;
+ }
+ p[i++] = c;
}
- q[i++] = c;
+ p[i] = 0;
+ res._length = i;
+ res._ptr = (heapBuffer is null ? res.useStack : &heapBuffer[0]);
+ return res;
}
- q[i] = 0;
- res._length = i;
- res._ptr = p_is_onstack ? useStack : &p[0];
- return res;
}
///
@@ -244,21 +182,30 @@ nothrow @nogc @system unittest
// both primary expressions are ended.
}
-@safe nothrow @nogc unittest
+@safe pure nothrow @nogc unittest
{
- assert("abc".tempCString().asArray == "abc");
- assert("abc"d.tempCString().ptr.asArray == "abc");
- assert("abc".tempCString!wchar().buffPtr.asArray == "abc"w);
+ static inout(C)[] arrayFor(C)(inout(C)* cstr) pure nothrow @nogc @trusted
+ {
+ assert(cstr);
+ size_t length = 0;
+ while (cstr[length])
+ ++length;
+ return cstr[0 .. length];
+ }
+
+ assert(arrayFor("abc".tempCString()) == "abc");
+ assert(arrayFor("abc"d.tempCString().ptr) == "abc");
+ assert(arrayFor("abc".tempCString!wchar().buffPtr) == "abc"w);
import std.utf : byChar, byWchar;
char[300] abc = 'a';
- assert(tempCString(abc[].byChar).buffPtr.asArray == abc);
- assert(tempCString(abc[].byWchar).buffPtr.asArray == abc);
+ assert(arrayFor(tempCString(abc[].byChar).buffPtr) == abc);
+ assert(arrayFor(tempCString(abc[].byWchar).buffPtr) == abc);
assert(tempCString(abc[].byChar)[] == abc);
}
-// Bugzilla 14980
-nothrow @nogc @safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=14980
+pure nothrow @nogc @safe unittest
{
const(char[]) str = null;
auto res = tempCString(str);
@@ -267,4 +214,99 @@ nothrow @nogc @safe unittest
}
version (Windows)
- alias tempCStringW = tempCString!(wchar, const(char)[]);
+{
+ import core.sys.windows.winnt : WCHAR;
+ alias tempCStringW = tempCString!(WCHAR, const(char)[]);
+}
+
+private struct TempCStringBuffer(To = char)
+{
+@trusted pure nothrow @nogc:
+
+ @disable this();
+ @disable this(this);
+ alias ptr this; /// implicitly covert to raw pointer
+
+ @property inout(To)* buffPtr() inout
+ {
+ return _ptr == useStack ? _buff.ptr : _ptr;
+ }
+
+ @property const(To)* ptr() const
+ {
+ return buffPtr;
+ }
+
+ const(To)[] opIndex() const pure
+ {
+ return buffPtr[0 .. _length];
+ }
+
+ ~this()
+ {
+ if (_ptr != useStack)
+ {
+ import core.memory : pureFree;
+ pureFree(_ptr);
+ }
+ }
+
+private:
+ enum To* useStack = () @trusted { return cast(To*) size_t.max; }();
+
+ To* _ptr;
+ size_t _length; // length of the string
+ version (StdUnittest)
+ // the 'small string optimization'
+ {
+ // smaller size to trigger reallocations. Padding is to account for
+ // unittest/non-unittest cross-compilation (to avoid corruption)
+ To[16 / To.sizeof] _buff;
+ To[(256 - 16) / To.sizeof] _unittest_pad;
+ }
+ else
+ {
+ To[256 / To.sizeof] _buff; // production size
+ }
+
+ static TempCStringBuffer trustedVoidInit() { TempCStringBuffer res = void; return res; }
+}
+
+private To[] trustedRealloc(To)(return scope To[] buf)
+ @trusted @nogc pure nothrow
+{
+ pragma(inline, false); // because it's rarely called
+ import std.internal.memory : enforceRealloc;
+
+ const size_t newlen = buf.length * 3 / 2;
+ if (buf.length >= size_t.max / (2 * To.sizeof))
+ {
+ version (D_Exceptions)
+ {
+ import core.exception : onOutOfMemoryError;
+ onOutOfMemoryError();
+ }
+ else
+ {
+ assert(0, "Memory allocation failed");
+ }
+ }
+ auto ptr = cast(To*) enforceRealloc(buf.ptr, newlen * To.sizeof);
+ return ptr[0 .. newlen];
+
+}
+
+private To[] trustedReallocStack(To)(scope To[] buf, size_t strLength)
+ @trusted @nogc pure nothrow
+{
+ pragma(inline, false); // because it's rarely called
+
+ import std.internal.memory : enforceMalloc;
+
+ size_t newlen = buf.length * 3 / 2;
+ if (newlen <= strLength)
+ newlen = strLength + 1; // +1 for terminating 0
+ auto ptr = cast(To*) enforceMalloc(newlen * To.sizeof);
+ ptr[0 .. buf.length] = buf[];
+ return ptr[0 .. newlen];
+}
diff --git a/libphobos/src/std/internal/math/biguintcore.d b/libphobos/src/std/internal/math/biguintcore.d
index 6fc2d16734f..59d784265c1 100644
--- a/libphobos/src/std/internal/math/biguintcore.d
+++ b/libphobos/src/std/internal/math/biguintcore.d
@@ -35,43 +35,184 @@ module std.internal.math.biguintcore;
version (D_InlineAsm_X86)
{
- import std.internal.math.biguintx86;
-}
-else
-{
- import std.internal.math.biguintnoasm;
+ static import std.internal.math.biguintx86;
}
+static import std.internal.math.biguintnoasm;
+
+import std.internal.math.biguintnoasm : BigDigit, KARATSUBALIMIT,
+ KARATSUBASQUARELIMIT;
alias multibyteAdd = multibyteAddSub!('+');
alias multibyteSub = multibyteAddSub!('-');
-
-import core.cpuid;
+private import std.traits;
+private import std.range.primitives;
public import std.ascii : LetterCase;
import std.range.primitives;
import std.traits;
-shared static this()
+private:
+
+// dipatchers to the right low-level primitives. Added to allow BigInt CTFE for
+// 32 bit systems (https://issues.dlang.org/show_bug.cgi?id=14767) although it's
+// used by the other architectures too.
+// See comments below in case it has to be refactored.
+version (X86)
+uint multibyteAddSub(char op)(uint[] dest, const(uint)[] src1, const (uint)[] src2, uint carry)
{
- CACHELIMIT = core.cpuid.datacache[0].size*1024/2;
+ // must be checked before, otherwise D_InlineAsm_X86 is true.
+ if (__ctfe)
+ return std.internal.math.biguintnoasm.multibyteAddSub!op(dest, src1, src2, carry);
+ // Runtime.
+ else version (D_InlineAsm_X86)
+ return std.internal.math.biguintx86.multibyteAddSub!op(dest, src1, src2, carry);
+ // Runtime if no asm available.
+ else
+ return std.internal.math.biguintnoasm.multibyteAddSub!op(dest, src1, src2, carry);
}
+// Any other architecture
+else alias multibyteAddSub = std.internal.math.biguintnoasm.multibyteAddSub;
+
+version (X86)
+uint multibyteIncrementAssign(char op)(uint[] dest, uint carry)
+{
+ if (__ctfe)
+ return std.internal.math.biguintnoasm.multibyteIncrementAssign!op(dest, carry);
+ else version (D_InlineAsm_X86)
+ return std.internal.math.biguintx86.multibyteIncrementAssign!op(dest, carry);
+ else
+ return std.internal.math.biguintnoasm.multibyteIncrementAssign!op(dest, carry);
+}
+else alias multibyteIncrementAssign = std.internal.math.biguintnoasm.multibyteIncrementAssign;
+
+version (X86)
+uint multibyteShl()(uint[] dest, const(uint)[] src, uint numbits)
+{
+ if (__ctfe)
+ return std.internal.math.biguintnoasm.multibyteShl(dest, src, numbits);
+ else version (D_InlineAsm_X86)
+ return std.internal.math.biguintx86.multibyteShl(dest, src, numbits);
+ else
+ return std.internal.math.biguintnoasm.multibyteShl(dest, src, numbits);
+}
+else alias multibyteShl = std.internal.math.biguintnoasm.multibyteShl;
+
+version (X86)
+void multibyteShr()(uint[] dest, const(uint)[] src, uint numbits)
+{
+ if (__ctfe)
+ std.internal.math.biguintnoasm.multibyteShr(dest, src, numbits);
+ else version (D_InlineAsm_X86)
+ std.internal.math.biguintx86.multibyteShr(dest, src, numbits);
+ else
+ std.internal.math.biguintnoasm.multibyteShr(dest, src, numbits);
+}
+else alias multibyteShr = std.internal.math.biguintnoasm.multibyteShr;
+
+version (X86)
+uint multibyteMul()(uint[] dest, const(uint)[] src, uint multiplier, uint carry)
+{
+ if (__ctfe)
+ return std.internal.math.biguintnoasm.multibyteMul(dest, src, multiplier, carry);
+ else version (D_InlineAsm_X86)
+ return std.internal.math.biguintx86.multibyteMul(dest, src, multiplier, carry);
+ else
+ return std.internal.math.biguintnoasm.multibyteMul(dest, src, multiplier, carry);
+}
+else alias multibyteMul = std.internal.math.biguintnoasm.multibyteMul;
+
+version (X86)
+uint multibyteMulAdd(char op)(uint[] dest, const(uint)[] src, uint multiplier, uint carry)
+{
+ if (__ctfe)
+ return std.internal.math.biguintnoasm.multibyteMulAdd!op(dest, src, multiplier, carry);
+ else version (D_InlineAsm_X86)
+ return std.internal.math.biguintx86.multibyteMulAdd!op(dest, src, multiplier, carry);
+ else
+ return std.internal.math.biguintnoasm.multibyteMulAdd!op(dest, src, multiplier, carry);
+}
+else alias multibyteMulAdd = std.internal.math.biguintnoasm.multibyteMulAdd;
+
+version (X86)
+void multibyteMultiplyAccumulate()(uint[] dest, const(uint)[] left, const(uint)[] right)
+{
+ if (__ctfe)
+ std.internal.math.biguintnoasm.multibyteMultiplyAccumulate(dest, left, right);
+ else version (D_InlineAsm_X86)
+ std.internal.math.biguintx86.multibyteMultiplyAccumulate(dest, left, right);
+ else
+ std.internal.math.biguintnoasm.multibyteMultiplyAccumulate(dest, left, right);
+}
+else alias multibyteMultiplyAccumulate = std.internal.math.biguintnoasm.multibyteMultiplyAccumulate;
+
+version (X86)
+uint multibyteDivAssign()(uint[] dest, uint divisor, uint overflow)
+{
+ if (__ctfe)
+ return std.internal.math.biguintnoasm.multibyteDivAssign(dest, divisor, overflow);
+ else version (D_InlineAsm_X86)
+ return std.internal.math.biguintx86.multibyteDivAssign(dest, divisor, overflow);
+ else
+ return std.internal.math.biguintnoasm.multibyteDivAssign(dest, divisor, overflow);
+}
+else alias multibyteDivAssign = std.internal.math.biguintnoasm.multibyteDivAssign;
+
+version (X86)
+void multibyteAddDiagonalSquares()(uint[] dest, const(uint)[] src)
+{
+ if (__ctfe)
+ std.internal.math.biguintnoasm.multibyteAddDiagonalSquares(dest, src);
+ else version (D_InlineAsm_X86)
+ std.internal.math.biguintx86.multibyteAddDiagonalSquares(dest, src);
+ else
+ std.internal.math.biguintnoasm.multibyteAddDiagonalSquares(dest, src);
+}
+else alias multibyteAddDiagonalSquares = std.internal.math.biguintnoasm.multibyteAddDiagonalSquares;
+
+version (X86)
+void multibyteTriangleAccumulate()(uint[] dest, const(uint)[] x)
+{
+ if (__ctfe)
+ std.internal.math.biguintnoasm.multibyteTriangleAccumulate(dest, x);
+ else version (D_InlineAsm_X86)
+ std.internal.math.biguintx86.multibyteTriangleAccumulate(dest, x);
+ else
+ std.internal.math.biguintnoasm.multibyteTriangleAccumulate(dest, x);
+}
+else alias multibyteTriangleAccumulate = std.internal.math.biguintnoasm.multibyteTriangleAccumulate;
+
+version (X86)
+void multibyteSquare()(BigDigit[] result, const(BigDigit)[] x)
+{
+ if (__ctfe)
+ std.internal.math.biguintnoasm.multibyteSquare(result, x);
+ else version (D_InlineAsm_X86)
+ std.internal.math.biguintx86.multibyteSquare(result, x);
+ else
+ std.internal.math.biguintnoasm.multibyteSquare(result, x);
+}
+else alias multibyteSquare = std.internal.math.biguintnoasm.multibyteSquare;
-private:
// Limits for when to switch between algorithms.
-immutable size_t CACHELIMIT; // Half the size of the data cache.
+// Half the size of the data cache.
+@nogc nothrow pure @safe size_t getCacheLimit()
+{
+ import core.cpuid : dataCaches;
+ return dataCaches[0].size * 1024 / 2;
+}
enum size_t FASTDIVLIMIT = 100; // crossover to recursive division
// These constants are used by shift operations
static if (BigDigit.sizeof == int.sizeof)
{
- enum { LG2BIGDIGITBITS = 5, BIGDIGITSHIFTMASK = 31 };
+ enum { LG2BIGDIGITBITS = 5, BIGDIGITSHIFTMASK = 31 }
alias BIGHALFDIGIT = ushort;
}
else static if (BigDigit.sizeof == long.sizeof)
{
alias BIGHALFDIGIT = uint;
- enum { LG2BIGDIGITBITS = 6, BIGDIGITSHIFTMASK = 63 };
+ enum { LG2BIGDIGITBITS = 6, BIGDIGITSHIFTMASK = 63 }
}
else static assert(0, "Unsupported BigDigit size");
@@ -98,17 +239,18 @@ struct BigUint
private:
pure invariant()
{
- assert( data.length >= 1 && (data.length == 1 || data[$-1] != 0 ));
+ assert( data.length >= 1 && (data.length == 1 || data[$-1] != 0 ),
+ "Invariant requires data to not empty or zero");
}
immutable(BigDigit) [] data = ZERO;
- this(immutable(BigDigit) [] x) pure nothrow @nogc @safe
+ this(return scope immutable(BigDigit) [] x) pure nothrow @nogc @safe
{
data = x;
}
package(std) // used from: std.bigint
- this(T)(T x) pure nothrow @safe if (isIntegral!T)
+ this(T)(T x) pure nothrow @safe scope if (isIntegral!T)
{
opAssign(x);
}
@@ -118,7 +260,7 @@ private:
};
public:
// Length in uints
- @property size_t uintLength() pure nothrow const @safe @nogc
+ @property size_t uintLength() pure nothrow const @safe @nogc scope
{
static if (BigDigit.sizeof == uint.sizeof)
{
@@ -130,7 +272,7 @@ public:
((data[$-1] & 0xFFFF_FFFF_0000_0000L) ? 1 : 0);
}
}
- @property size_t ulongLength() pure nothrow const @safe @nogc
+ @property size_t ulongLength() pure nothrow const @safe @nogc scope
{
static if (BigDigit.sizeof == uint.sizeof)
{
@@ -143,7 +285,7 @@ public:
}
// The value at (cast(ulong[]) data)[n]
- ulong peekUlong(int n) pure nothrow const @safe @nogc
+ ulong peekUlong(size_t n) pure nothrow const @safe @nogc scope
{
static if (BigDigit.sizeof == int.sizeof)
{
@@ -155,7 +297,8 @@ public:
return data[n];
}
}
- uint peekUint(int n) pure nothrow const @safe @nogc
+
+ uint peekUint(size_t n) pure nothrow const @safe @nogc scope
{
static if (BigDigit.sizeof == int.sizeof)
{
@@ -167,9 +310,9 @@ public:
return (n & 1) ? cast(uint)(x >> 32) : cast(uint) x;
}
}
-public:
+
///
- void opAssign(Tulong)(Tulong u) pure nothrow @safe if (is (Tulong == ulong))
+ void opAssign(Tulong)(Tulong u) pure nothrow @safe scope if (is (Tulong == ulong))
{
if (u == 0) data = ZERO;
else if (u == 1) data = ONE;
@@ -196,13 +339,13 @@ public:
}
}
}
- void opAssign(Tdummy = void)(BigUint y) pure nothrow @nogc @safe
+ void opAssign(Tdummy = void)(BigUint y) pure nothrow @nogc @safe scope
{
this.data = y.data;
}
///
- int opCmp(Tdummy = void)(const BigUint y) pure nothrow @nogc const @safe
+ int opCmp(Tdummy = void)(const BigUint y) pure nothrow @nogc const @safe scope
{
if (data.length != y.data.length)
return (data.length > y.data.length) ? 1 : -1;
@@ -213,7 +356,7 @@ public:
}
///
- int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe if (is (Tulong == ulong))
+ int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe scope if (is (Tulong == ulong))
{
if (data.length > maxBigDigits!Tulong)
return 1;
@@ -239,12 +382,12 @@ public:
return 0;
}
- bool opEquals(Tdummy = void)(ref const BigUint y) pure nothrow @nogc const @safe
+ bool opEquals(Tdummy = void)(ref const BigUint y) pure nothrow @nogc const @safe scope
{
return y.data[] == data[];
}
- bool opEquals(Tdummy = void)(ulong y) pure nothrow @nogc const @safe
+ bool opEquals(Tdummy = void)(ulong y) pure nothrow @nogc const @safe scope
{
if (data.length > 2)
return false;
@@ -257,18 +400,18 @@ public:
return (data[0] == ylo);
}
- bool isZero() pure const nothrow @safe @nogc
+ bool isZero() pure const nothrow @safe @nogc scope
{
return data.length == 1 && data[0] == 0;
}
- size_t numBytes() pure nothrow const @safe @nogc
+ size_t numBytes() pure nothrow const @safe @nogc scope
{
return data.length * BigDigit.sizeof;
}
// the extra bytes are added to the start of the string
- char [] toDecimalString(int frontExtraBytes) const pure nothrow
+ char [] toDecimalString(int frontExtraBytes) const pure nothrow @safe scope
{
immutable predictlength = 20+20*(data.length/2); // just over 19
char [] buff = new char[frontExtraBytes + predictlength];
@@ -285,7 +428,7 @@ public:
*/
char [] toHexString(int frontExtraBytes, char separator = 0,
int minPadding=0, char padChar = '0',
- LetterCase letterCase = LetterCase.upper) const pure nothrow @safe
+ LetterCase letterCase = LetterCase.upper) const pure nothrow @safe scope
{
// Calculate number of extra padding bytes
size_t extraPad = (minPadding > data.length * 2 * BigDigit.sizeof)
@@ -349,7 +492,7 @@ public:
/**
* Convert to an octal string.
*/
- char[] toOctalString() const
+ char[] toOctalString() pure nothrow @safe const scope
{
auto predictLength = 1 + data.length*BigDigitBits / 3;
char[] buff = new char[predictLength];
@@ -358,7 +501,7 @@ public:
}
// return false if invalid character found
- bool fromHexString(Range)(Range s) if (
+ bool fromHexString(Range)(Range s) scope if (
isBidirectionalRange!Range && isSomeChar!(ElementType!Range))
{
import std.range : walkLength;
@@ -427,7 +570,7 @@ public:
}
// return true if OK; false if erroneous characters found
- bool fromDecimalString(Range)(Range s) if (
+ bool fromDecimalString(Range)(Range s) scope if (
isForwardRange!Range && isSomeChar!(ElementType!Range))
{
import std.range : walkLength;
@@ -452,14 +595,125 @@ public:
return true;
}
+ void fromMagnitude(Range)(Range magnitude) scope
+ if (isInputRange!Range
+ && (isForwardRange!Range || hasLength!Range)
+ && isUnsigned!(ElementType!Range))
+ {
+ while (!magnitude.empty && magnitude.front == 0)
+ magnitude.popFront;
+ static if (hasLength!Range)
+ immutable inputLen = magnitude.length;
+ else
+ immutable inputLen = magnitude.save.walkLength;
+ if (!inputLen)
+ {
+ this.data = ZERO;
+ return;
+ }
+ // `magnitude` has its most significant element first but BigUint.data
+ // stores the most significant last.
+ BigDigit[] newDigits;
+ alias E = ElementType!Range;
+ static if (E.sizeof == BigDigit.sizeof)
+ {
+ newDigits = new BigDigit[inputLen];
+ foreach_reverse (ref digit; newDigits)
+ {
+ digit = magnitude.front;
+ magnitude.popFront();
+ }
+ }
+ else static if (E.sizeof < BigDigit.sizeof)
+ {
+ enum elementsPerDigit = BigDigit.sizeof / E.sizeof;
+ newDigits = new BigDigit[(inputLen + elementsPerDigit - 1) / elementsPerDigit];
+ immutable remainder = inputLen % elementsPerDigit;
+ // If there is a remainder specially assemble the most significant digit.
+ if (remainder)
+ {
+ BigDigit tmp = magnitude.front;
+ magnitude.popFront();
+ foreach (_; 1 .. remainder)
+ {
+ tmp = (tmp << (E.sizeof * 8)) | magnitude.front;
+ magnitude.popFront();
+ }
+ newDigits[$-1] = tmp;
+ }
+ // Assemble full digits from most to least significant.
+ foreach_reverse (ref digit; newDigits[0 .. $ - int(remainder != 0)])
+ {
+ BigDigit tmp;
+ static foreach (n; 0 .. elementsPerDigit)
+ {
+ tmp |= cast(BigDigit) magnitude.front <<
+ ((BigDigit.sizeof - (E.sizeof * (n + 1))) * 8);
+ magnitude.popFront();
+ }
+ digit = tmp;
+ }
+ }
+ else static if (E.sizeof > BigDigit.sizeof)
+ {
+ enum digitsPerElement = E.sizeof / BigDigit.sizeof;
+ newDigits = new BigDigit[inputLen * digitsPerElement];
+ size_t i = newDigits.length - 1;
+ foreach (element; magnitude)
+ {
+ static foreach (n; 0 .. digitsPerElement)
+ newDigits[i - n] =
+ cast(BigDigit) (element >> ((E.sizeof - (BigDigit.sizeof * (n + 1))) * 8));
+ i -= digitsPerElement;
+ }
+ while (newDigits[$-1] == 0)
+ newDigits = newDigits[0 .. $-1];
+ }
+ else
+ static assert(0);
+ this.data = trustedAssumeUnique(newDigits);
+ return;
+ }
+
+ nothrow pure @safe unittest
+ {
+ immutable BigDigit[] referenceData = [BigDigit(0x2003_4005), 0x6007_8009, 0xABCD];
+ // Internal representation is most-significant-last but `fromMagnitude`
+ // argument is most-significant-first.
+ immutable BigDigit[] referenceMagnitude = [BigDigit(0xABCD), 0x6007_8009, 0x2003_4005];
+ BigUint b;
+ // Test with reference magnitude.
+ b.fromMagnitude(referenceMagnitude);
+ assert(b.data == referenceData);
+ // Test ubyte array.
+ import std.bitmanip : nativeToBigEndian;
+ ubyte[] ubyteMagnitude = nativeToBigEndian(referenceMagnitude[0]) ~
+ nativeToBigEndian(referenceMagnitude[1]) ~
+ nativeToBigEndian(referenceMagnitude[2]);
+ b.data = ZERO;
+ b.fromMagnitude(ubyteMagnitude);
+ assert(b.data == referenceData);
+ // Test ulong array.
+ static if (BigDigit.sizeof == uint.sizeof)
+ immutable(ulong)[] ulongMagnitude = [ulong(referenceMagnitude[0]),
+ ((cast(ulong) referenceMagnitude[1]) << 32) | referenceMagnitude[2],
+ ];
+ else static if (BigDigit.sizeof == ulong.sizeof)
+ alias ulongMagnitude = referenceMagnitude;
+ b.data = ZERO;
+ b.fromMagnitude(ulongMagnitude);
+ assert(b.data == referenceData);
+ }
+
////////////////////////
//
// All of these member functions create a new BigUint.
// return x >> y
- BigUint opShr(Tulong)(Tulong y) pure nothrow const if (is (Tulong == ulong))
+ BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const return scope
+ if (op == ">>" && is (Tulong == ulong))
{
- assert(y>0);
+ assert(y > 0, "Can not right shift BigUint by 0");
uint bits = cast(uint) y & BIGDIGITSHIFTMASK;
if ((y >> LG2BIGDIGITBITS) >= data.length) return BigUint(ZERO);
uint words = cast(uint)(y >> LG2BIGDIGITBITS);
@@ -480,12 +734,14 @@ public:
}
// return x << y
- BigUint opShl(Tulong)(Tulong y) pure nothrow const if (is (Tulong == ulong))
+ BigUint opBinary(string op, Tulong)(Tulong y) pure nothrow @safe const scope
+ if (op == "<<" && is (Tulong == ulong))
{
- assert(y>0);
+ assert(y > 0, "Can not left shift BigUint by 0");
if (isZero()) return this;
uint bits = cast(uint) y & BIGDIGITSHIFTMASK;
- assert((y >> LG2BIGDIGITBITS) < cast(ulong)(uint.max));
+ assert((y >> LG2BIGDIGITBITS) < cast(ulong)(uint.max),
+ "Shift result exceeds temporary store");
uint words = cast(uint)(y >> LG2BIGDIGITBITS);
BigDigit [] result = new BigDigit[data.length + words+1];
result[0 .. words] = 0;
@@ -505,15 +761,16 @@ public:
// If wantSub is false, return x + y, leaving sign unchanged
// If wantSub is true, return abs(x - y), negating sign if x < y
- static BigUint addOrSubInt(Tulong)(const BigUint x, Tulong y,
- bool wantSub, ref bool sign) pure nothrow if (is(Tulong == ulong))
+ static BigUint addOrSubInt(Tulong)(const scope BigUint x, Tulong y,
+ bool wantSub, ref bool sign) pure nothrow @safe if (is(Tulong == ulong))
{
BigUint r;
if (wantSub)
{ // perform a subtraction
if (x.data.length > 2)
{
- r.data = subInt(x.data, y);
+ // subInt returns GC allocated array, can be safely cast to immutable
+ r.data = (() @trusted => cast(immutable) subInt(x.data, y))();
}
else
{ // could change sign!
@@ -548,21 +805,23 @@ public:
}
else
{
- r.data = addInt(x.data, y);
+ // addInt returns GC allocated array, can be safely cast to immutable
+ r.data = (() @trusted => cast(immutable) addInt(x.data, y))();
}
return r;
}
// If wantSub is false, return x + y, leaving sign unchanged.
// If wantSub is true, return abs(x - y), negating sign if x<y
- static BigUint addOrSub(BigUint x, BigUint y, bool wantSub, bool *sign)
- pure nothrow
+ static BigUint addOrSub(scope BigUint x, scope BigUint y, bool wantSub, bool *sign)
+ pure nothrow @safe
{
BigUint r;
if (wantSub)
{ // perform a subtraction
bool negative;
- r.data = sub(x.data, y.data, &negative);
+ // sub returns GC allocated array, can be safely cast to immutable
+ r.data = (() @trusted => cast(immutable) sub(x.data, y.data, &negative))();
*sign ^= negative;
if (r.isZero())
{
@@ -571,19 +830,22 @@ public:
}
else
{
- r.data = add(x.data, y.data);
+ // add returns GC allocated array, can be safely cast to immutable
+ r.data = (() @trusted => cast(immutable) add(x.data, y.data))();
}
return r;
}
// return x*y.
- // y must not be zero.
- static BigUint mulInt(T = ulong)(BigUint x, T y) pure nothrow
+ static BigUint mulInt(T = ulong)(BigUint x, T y) pure nothrow @safe
{
if (y == 0 || x == 0) return BigUint(ZERO);
- uint hi = cast(uint)(y >>> 32);
- uint lo = cast(uint)(y & 0xFFFF_FFFF);
+ static if (T.sizeof * 8 <= 32)
+ uint hi = 0;
+ else
+ uint hi = cast(uint) (y >>> 32);
+ uint lo = cast(uint) (y & 0xFFFF_FFFF);
uint [] result = new BigDigit[x.data.length+1+(hi != 0)];
result[x.data.length] = multibyteMul(result[0 .. x.data.length], x.data, lo, 0);
if (hi != 0)
@@ -596,7 +858,7 @@ public:
/* return x * y.
*/
- static BigUint mul(BigUint x, BigUint y) pure nothrow
+ static BigUint mul(scope BigUint x, scope BigUint y) pure nothrow @safe
{
if (y == 0 || x == 0)
return BigUint(ZERO);
@@ -617,8 +879,8 @@ public:
}
// return x / y
- static BigUint divInt(T)(BigUint x, T y_) pure nothrow
- if ( is(Unqual!T == uint) )
+ static BigUint divInt(T)(scope return BigUint x, T y_) pure nothrow @safe
+ if ( is(immutable T == immutable uint) )
{
uint y = y_;
if (y == 1)
@@ -643,8 +905,8 @@ public:
return BigUint(removeLeadingZeros(trustedAssumeUnique(result)));
}
- static BigUint divInt(T)(BigUint x, T y) pure nothrow
- if ( is(Unqual!T == ulong) )
+ static BigUint divInt(T)(scope BigUint x, T y) pure nothrow @safe
+ if ( is(immutable T == immutable ulong) )
{
if (y <= uint.max)
return divInt!uint(x, cast(uint) y);
@@ -659,11 +921,11 @@ public:
}
// return x % y
- static uint modInt(T)(BigUint x, T y_) pure if ( is(Unqual!T == uint) )
+ static uint modInt(T)(scope BigUint x, T y_) pure if ( is(immutable T == immutable uint) )
{
import core.memory : GC;
uint y = y_;
- assert(y != 0);
+ assert(y != 0, "% 0 not allowed");
if ((y&(-y)) == y)
{ // perfect power of 2
return x.data[0] & (y-1);
@@ -680,7 +942,7 @@ public:
}
// return x / y
- static BigUint div(BigUint x, BigUint y) pure nothrow
+ static BigUint div(scope return BigUint x, scope BigUint y) pure nothrow @safe
{
if (y.data.length > x.data.length)
return BigUint(ZERO);
@@ -692,7 +954,7 @@ public:
}
// return x % y
- static BigUint mod(BigUint x, BigUint y) pure nothrow
+ static BigUint mod(scope return BigUint x, scope BigUint y) pure nothrow @safe
{
if (y.data.length > x.data.length) return x;
if (y.data.length == 1)
@@ -705,8 +967,33 @@ public:
return BigUint(removeLeadingZeros(trustedAssumeUnique(rem)));
}
+ // Return x / y in quotient, x % y in remainder
+ static void divMod(BigUint x, scope BigUint y,
+ out BigUint quotient, out BigUint remainder) pure nothrow @safe
+ {
+ /* TODO Qualify parameter `x` as `return` when it applies to `out` parameters */
+ if (y.data.length > x.data.length)
+ {
+ quotient = 0uL;
+ remainder = x;
+ }
+ else if (y.data.length == 1)
+ {
+ quotient = divInt(x, y.data[0]);
+ remainder = BigUint([modInt(x, y.data[0])]);
+ }
+ else
+ {
+ BigDigit[] quot = new BigDigit[x.data.length - y.data.length + 1];
+ BigDigit[] rem = new BigDigit[y.data.length];
+ divModInternal(quot, rem, x.data, y.data);
+ quotient = BigUint(removeLeadingZeros(trustedAssumeUnique(quot)));
+ remainder = BigUint(removeLeadingZeros(trustedAssumeUnique(rem)));
+ }
+ }
+
// return x op y
- static BigUint bitwiseOp(string op)(BigUint x, BigUint y, bool xSign, bool ySign, ref bool resultSign)
+ static BigUint bitwiseOp(string op)(scope BigUint x, scope BigUint y, bool xSign, bool ySign, ref bool resultSign)
pure nothrow @safe if (op == "|" || op == "^" || op == "&")
{
auto d1 = includeSign(x.data, y.uintLength, xSign);
@@ -733,7 +1020,7 @@ public:
* exponentiation is used.
* Memory allocation is minimized: at most one temporary BigUint is used.
*/
- static BigUint pow(BigUint x, ulong y) pure nothrow
+ static BigUint pow(scope return BigUint x, ulong y) pure nothrow @safe
{
// Deal with the degenerate cases first.
if (y == 0) return BigUint(ONE);
@@ -752,7 +1039,7 @@ public:
// If true, then x0 is that digit
// and the result will be (x0 ^^ y) * (2^^(firstnonzero*y*BigDigitBits))
BigDigit x0 = x.data[firstnonzero];
- assert(x0 != 0);
+ assert(x0 != 0, "pow(0, y) not allowed");
// Length of the non-zero portion
size_t nonzerolength = x.data.length - firstnonzero;
ulong y0;
@@ -941,9 +1228,9 @@ public:
}
// Implement toHash so that BigUint works properly as an AA key.
- size_t toHash() const @trusted nothrow
+ size_t toHash() const @nogc nothrow pure @safe scope
{
- return typeid(data).getHash(&data);
+ return .hashOf(data);
}
} // end BigUint
@@ -953,24 +1240,33 @@ public:
// ulong comparison test
BigUint a = [1];
assert(a == 1);
- assert(a < 0x8000_0000_0000_0000UL); // bug 9548
+ // https://issues.dlang.org/show_bug.cgi?id=9548
+ assert(a < 0x8000_0000_0000_0000UL);
- // bug 12234
+ // https://issues.dlang.org/show_bug.cgi?id=12234
BigUint z = [0];
assert(z == 0UL);
assert(!(z > 0UL));
assert(!(z < 0UL));
}
+// https://issues.dlang.org/show_bug.cgi?id=16223
+@system pure nothrow unittest
+{
+ BigUint a = [3];
+ int b = 5;
+ assert(BigUint.mulInt(a,b) == 15);
+}
+
// Remove leading zeros from x, to restore the BigUint invariant
-inout(BigDigit) [] removeLeadingZeros(inout(BigDigit) [] x) pure nothrow @safe
+inout(BigDigit) [] removeLeadingZeros(scope return inout(BigDigit) [] x) pure nothrow @safe
{
size_t k = x.length;
while (k>1 && x[k - 1]==0) --k;
return x[0 .. k];
}
-pure @system unittest
+pure @safe unittest
{
BigUint r = BigUint([5]);
BigUint t = BigUint([7]);
@@ -992,7 +1288,7 @@ pure @system unittest
// Pow tests
-pure @system unittest
+pure @safe unittest
{
BigUint r, s;
r.fromHexString("80000000_00000001");
@@ -1084,7 +1380,7 @@ pure nothrow @safe
}
// Encode BigInt as BigDigit array (sign and 2's complement)
-BigDigit[] includeSign(const(BigDigit) [] x, size_t minSize, bool sign)
+BigDigit[] includeSign(scope const(BigDigit) [] x, size_t minSize, bool sign)
pure nothrow @safe
{
size_t length = (x.length > minSize) ? x.length : minSize;
@@ -1139,7 +1435,7 @@ T intpow(T)(T x, ulong n) pure nothrow @safe
// returns the maximum power of x that will fit in a uint.
int highestPowerBelowUintMax(uint x) pure nothrow @safe
{
- assert(x>1);
+ assert(x > 1, "x must be greater than 1");
static immutable ubyte [22] maxpwr = [ 31, 20, 15, 13, 12, 11, 10, 10, 9, 9,
8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7];
if (x<24) return maxpwr[x-2];
@@ -1154,7 +1450,7 @@ int highestPowerBelowUintMax(uint x) pure nothrow @safe
// returns the maximum power of x that will fit in a ulong.
int highestPowerBelowUlongMax(uint x) pure nothrow @safe
{
- assert(x>1);
+ assert(x > 1, "x must be greater than 1");
static immutable ubyte [39] maxpwr = [ 63, 40, 31, 27, 24, 22, 21, 20, 19, 18,
17, 17, 16, 16, 15, 15, 15, 15, 14, 14,
14, 14, 13, 13, 13, 13, 13, 13, 13, 12,
@@ -1172,10 +1468,10 @@ int highestPowerBelowUlongMax(uint x) pure nothrow @safe
return 2;
}
-version (unittest)
+version (StdUnittest)
{
-int slowHighestPowerBelowUintMax(uint x) pure nothrow @safe
+private int slowHighestPowerBelowUintMax(uint x) pure nothrow @safe
{
int pwr = 1;
for (ulong q = x;x*q < cast(ulong) uint.max; )
@@ -1199,9 +1495,11 @@ int slowHighestPowerBelowUintMax(uint x) pure nothrow @safe
/* General unsigned subtraction routine for bigints.
* Sets result = x - y. If the result is negative, negative will be true.
+ * Returns:
+ * unique memory
*/
-BigDigit [] sub(const BigDigit [] x, const BigDigit [] y, bool *negative)
-pure nothrow
+BigDigit [] sub(const scope BigDigit [] x, const scope BigDigit [] y, bool *negative)
+pure nothrow @safe
{
if (x.length == y.length)
{
@@ -1255,8 +1553,12 @@ pure nothrow
}
-// return a + b
-BigDigit [] add(const BigDigit [] a, const BigDigit [] b) pure nothrow
+/*
+ * return a + b
+ * Returns:
+ * unique memory
+ */
+BigDigit [] add(const scope BigDigit [] a, const scope BigDigit [] b) pure nothrow @safe
{
const(BigDigit) [] x, y;
if (a.length < b.length)
@@ -1288,7 +1590,7 @@ BigDigit [] add(const BigDigit [] a, const BigDigit [] b) pure nothrow
/** return x + y
*/
-BigDigit [] addInt(const BigDigit[] x, ulong y) pure nothrow
+BigDigit [] addInt(const BigDigit[] x, ulong y) @safe pure nothrow
{
uint hi = cast(uint)(y >>> 32);
uint lo = cast(uint)(y& 0xFFFF_FFFF);
@@ -1315,7 +1617,7 @@ BigDigit [] addInt(const BigDigit[] x, ulong y) pure nothrow
/** Return x - y.
* x must be greater than y.
*/
-BigDigit [] subInt(const BigDigit[] x, ulong y) pure nothrow
+BigDigit [] subInt(const BigDigit[] x, ulong y) pure nothrow @safe
{
uint hi = cast(uint)(y >>> 32);
uint lo = cast(uint)(y & 0xFFFF_FFFF);
@@ -1340,12 +1642,13 @@ BigDigit [] subInt(const BigDigit[] x, ulong y) pure nothrow
*
*/
void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y)
- pure nothrow
+ pure nothrow @safe
{
import core.memory : GC;
- assert( result.length == x.length + y.length );
- assert( y.length > 0 );
- assert( x.length >= y.length);
+ assert( result.length == x.length + y.length,
+ "result array must have enough space to store computed result");
+ assert( y.length > 0, "y must not be empty");
+ assert( x.length >= y.length, "x must be greater or equal than y");
if (y.length <= KARATSUBALIMIT)
{
// Small multiplier, we'll just use the asm classic multiply.
@@ -1355,6 +1658,7 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y)
return;
}
+ immutable CACHELIMIT = getCacheLimit;
if (x.length + y.length < CACHELIMIT)
return mulSimple(result, x, y);
@@ -1402,59 +1706,81 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y)
auto extra = x.length % y.length;
auto maxchunk = chunksize + extra;
bool paddingY; // true = we're padding Y, false = we're padding X.
- if (extra * extra * 2 < y.length*y.length)
- {
- // The leftover bit is small enough that it should be incorporated
- // in the existing chunks.
- // Make all the chunks a tiny bit bigger
- // (We're padding y with zeros)
- chunksize += extra / numchunks;
- extra = x.length - chunksize*numchunks;
- // there will probably be a few left over.
- // Every chunk will either have size chunksize, or chunksize+1.
- maxchunk = chunksize + 1;
- paddingY = true;
- assert(chunksize + extra + chunksize *(numchunks-1) == x.length );
+ bool isExtraSmall = extra * extra * 2 < y.length * y.length;
+ if (numchunks == 1 && isExtraSmall)
+ {
+ // We divide (x_first_half * y) and (x_last_half * y)
+ // between 1.414:1 and 1.707:1 (1.707 = 1+1/sqrt(2)).
+ // (1.414 ~ 1.707)/2:1 is balanced.
+ BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(y.length) + y.length];
+ BigDigit [] partial = scratchbuff[$ - y.length .. $];
+ scratchbuff = scratchbuff[0 .. $ - y.length];
+ mulKaratsuba(result[0 .. half + y.length], y, x[0 .. half], scratchbuff);
+ partial[] = result[half .. half + y.length];
+ mulKaratsuba(result[half .. $], y, x[half .. $], scratchbuff);
+ BigDigit c = addAssignSimple(result[half .. half + y.length], partial);
+ if (c) multibyteIncrementAssign!('+')(result[half + y.length..$], c);
+ () @trusted { GC.free(scratchbuff.ptr); } ();
}
else
{
- // the extra bit is large enough that it's worth making a new chunk.
- // (This means we're padding x with zeros, when doing the first one).
- maxchunk = chunksize;
- ++numchunks;
- paddingY = false;
- assert(extra + chunksize *(numchunks-1) == x.length );
- }
- // We make the buffer a bit bigger so we have space for the partial sums.
- BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(maxchunk) + y.length];
- BigDigit [] partial = scratchbuff[$ - y.length .. $];
- size_t done; // how much of X have we done so far?
- if (paddingY)
- {
- // If the first chunk is bigger, do it first. We're padding y.
- mulKaratsuba(result[0 .. y.length + chunksize + (extra > 0 ? 1 : 0 )],
- x[0 .. chunksize + (extra>0?1:0)], y, scratchbuff);
- done = chunksize + (extra > 0 ? 1 : 0);
- if (extra) --extra;
- }
- else
- { // We're padding X. Begin with the extra bit.
- mulKaratsuba(result[0 .. y.length + extra], y, x[0 .. extra], scratchbuff);
- done = extra;
- extra = 0;
- }
- immutable basechunksize = chunksize;
- while (done < x.length)
- {
- chunksize = basechunksize + (extra > 0 ? 1 : 0);
- if (extra) --extra;
- partial[] = result[done .. done+y.length];
- mulKaratsuba(result[done .. done + y.length + chunksize],
- x[done .. done+chunksize], y, scratchbuff);
- addAssignSimple(result[done .. done + y.length + chunksize], partial);
- done += chunksize;
+ if (isExtraSmall)
+ {
+ // The leftover bit is small enough that it should be incorporated
+ // in the existing chunks.
+ // Make all the chunks a tiny bit bigger
+ // (We're padding y with zeros)
+ chunksize += extra / numchunks;
+ extra = x.length - chunksize*numchunks;
+ // there will probably be a few left over.
+ // Every chunk will either have size chunksize, or chunksize+1.
+ maxchunk = chunksize + 1;
+ paddingY = true;
+ assert(chunksize + extra + chunksize *(numchunks-1) == x.length,
+ "Unexpected size");
+ }
+ else
+ {
+ // the extra bit is large enough that it's worth making a new chunk.
+ // (This means we're padding x with zeros, when doing the first one).
+ maxchunk = chunksize;
+ ++numchunks;
+ paddingY = false;
+ assert(extra + chunksize *(numchunks-1) == x.length,
+ "Unexpected size");
+ }
+ // We make the buffer a bit bigger so we have space for the partial sums.
+ BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(maxchunk) + y.length];
+ BigDigit [] partial = scratchbuff[$ - y.length .. $];
+ scratchbuff = scratchbuff[0 .. $ - y.length];
+ size_t done; // how much of X have we done so far?
+ if (paddingY)
+ {
+ // If the first chunk is bigger, do it first. We're padding y.
+ mulKaratsuba(result[0 .. y.length + chunksize + (extra > 0 ? 1 : 0 )],
+ x[0 .. chunksize + (extra>0?1:0)], y, scratchbuff);
+ done = chunksize + (extra > 0 ? 1 : 0);
+ if (extra) --extra;
+ }
+ else
+ { // We're padding X. Begin with the extra bit.
+ mulKaratsuba(result[0 .. y.length + extra], y, x[0 .. extra], scratchbuff);
+ done = extra;
+ extra = 0;
+ }
+ immutable basechunksize = chunksize;
+ while (done < x.length)
+ {
+ chunksize = basechunksize + (extra > 0 ? 1 : 0);
+ if (extra) --extra;
+ partial[] = result[done .. done+y.length];
+ mulKaratsuba(result[done .. done + y.length + chunksize],
+ x[done .. done+chunksize], y, scratchbuff);
+ addAssignSimple(result[done .. done + y.length + chunksize], partial);
+ done += chunksize;
+ }
+ () @trusted { GC.free(scratchbuff.ptr); } ();
}
- () @trusted { GC.free(scratchbuff.ptr); } ();
}
else
{
@@ -1465,17 +1791,43 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y)
}
}
+// https://issues.dlang.org/show_bug.cgi?id=20493
+@safe unittest
+{
+ // the bug report has a testcase with very large numbers (~10^3800 and ~10^2300)
+ // the number itself isn't important, only the amount of digits, so we do a simpler
+ // multiplication of the same size, analogous to:
+ // 11111111 * 11111111 = 0123456787654321
+ // but instead of base 10, it's in base `BigDigit`
+
+ BigDigit[398] x = 1;
+ BigDigit[236] y = 1;
+ BigDigit[x.length + y.length] result;
+ mulInternal(result[], x[], y[]);
+
+ // create an array of the form [1, 2, ..., y.length, ..., y.length, y.length-1, ..., 1, 0]
+ BigDigit[x.length + y.length] expected = y.length;
+ foreach (BigDigit i; 0 .. y.length)
+ {
+ expected[i] = i+1;
+ expected[$-1-i] = i;
+ }
+
+ assert(result == expected);
+}
+
/** General unsigned squaring routine for BigInts.
* Sets result = x*x.
* NOTE: If the highest half-digit of x is zero, the highest digit of result will
* also be zero.
*/
-void squareInternal(BigDigit[] result, const BigDigit[] x) pure nothrow
+void squareInternal(BigDigit[] result, const BigDigit[] x) pure nothrow @safe
{
import core.memory : GC;
// Squaring is potentially half a multiply, plus add the squares of
// the diagonal elements.
- assert(result.length == 2*x.length);
+ assert(result.length == 2*x.length,
+ "result needs to have twice the capacity of x");
if (x.length <= KARATSUBASQUARELIMIT)
{
if (x.length == 1)
@@ -1496,13 +1848,15 @@ import core.bitop : bsr;
/// if remainder is null, only calculate quotient.
void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [] u,
- const BigDigit [] v) pure nothrow
+ const BigDigit [] v) pure nothrow @safe
{
import core.memory : GC;
- assert(quotient.length == u.length - v.length + 1);
- assert(remainder == null || remainder.length == v.length);
- assert(v.length > 1);
- assert(u.length >= v.length);
+ assert(quotient.length == u.length - v.length + 1,
+ "Invalid quotient length");
+ assert(remainder == null || remainder.length == v.length,
+ "Invalid remainder");
+ assert(v.length > 1, "v must have more than 1 element");
+ assert(u.length >= v.length, "u must be as longer or longer than v");
// Normalize by shifting v left just enough so that
// its high-order bit is on, and shift u left the
@@ -1541,7 +1895,7 @@ void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [
() @trusted { GC.free(un.ptr); GC.free(vn.ptr); } ();
}
-pure @system unittest
+pure @safe unittest
{
immutable(uint) [] u = [0, 0xFFFF_FFFE, 0x8000_0000];
immutable(uint) [] v = [0xFFFF_FFFF, 0x8000_0000];
@@ -1563,7 +1917,7 @@ private:
// every 8 digits.
// buff.length must be data.length*8 if separator is zero,
// or data.length*9 if separator is non-zero. It will be completely filled.
-char [] biguintToHex(char [] buff, const BigDigit [] data, char separator=0,
+char [] biguintToHex(scope return char [] buff, const scope BigDigit [] data, char separator=0,
LetterCase letterCase = LetterCase.upper) pure nothrow @safe
{
int x=0;
@@ -1613,10 +1967,10 @@ size_t biguintToOctal(char[] buff, const(BigDigit)[] data)
if (shift < 0)
{
// Some bits were carried over from previous word.
- assert(shift > -3);
+ assert(shift > -3, "shift must be greater than -3");
output(((bigdigit << -shift) | carry) & 0b111);
shift += 3;
- assert(shift > 0);
+ assert(shift > 0, "shift must be 1 or greater");
}
while (shift <= BigDigitBits - 3)
@@ -1631,13 +1985,13 @@ size_t biguintToOctal(char[] buff, const(BigDigit)[] data)
carry = (bigdigit >>> shift) & 0b11;
}
shift -= BigDigitBits;
- assert(shift >= -2 && shift <= 0);
+ assert(shift >= -2 && shift <= 0, "shift must in [-2,0]");
}
if (shift < 0)
{
// Last word had bits that haven't been output yet.
- assert(shift > -3);
+ assert(shift > -3, "Shift must be greater than -3");
output(carry);
}
@@ -1656,7 +2010,7 @@ size_t biguintToOctal(char[] buff, const(BigDigit)[] data)
* Returns:
* the lowest index of buff which was used.
*/
-size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow
+size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow @safe
{
ptrdiff_t sofar = buff.length;
// Might be better to divide by (10^38/2^32) since that gives 38 digits for
@@ -1701,9 +2055,10 @@ if (
in
{
static if (hasLength!Range)
- assert((data.length >= 2) || (data.length == 1 && s.length == 1));
+ assert((data.length >= 2) || (data.length == 1 && s.length == 1),
+ "data has a invalid length");
}
-body
+do
{
import std.conv : ConvException;
@@ -1837,26 +2192,27 @@ private:
// Classic 'schoolbook' multiplication.
void mulSimple(BigDigit[] result, const(BigDigit) [] left,
- const(BigDigit)[] right) pure nothrow
+ const(BigDigit)[] right) pure nothrow @safe
in
{
- assert(result.length == left.length + right.length);
- assert(right.length>1);
+ assert(result.length == left.length + right.length,
+ "Result must be able to store left + right");
+ assert(right.length>1, "right must not be empty");
}
-body
+do
{
result[left.length] = multibyteMul(result[0 .. left.length], left, right[0], 0);
multibyteMultiplyAccumulate(result[1..$], left, right[1..$]);
}
// Classic 'schoolbook' squaring
-void squareSimple(BigDigit[] result, const(BigDigit) [] x) pure nothrow
+void squareSimple(BigDigit[] result, const(BigDigit) [] x) pure nothrow @safe
in
{
- assert(result.length == 2*x.length);
- assert(x.length>1);
+ assert(result.length == 2*x.length, "result must be twice as long as x");
+ assert(x.length>1, "x must not be empty");
}
-body
+do
{
multibyteSquare(result, x);
}
@@ -1866,14 +2222,16 @@ body
// as the larger length.
// Returns carry (0 or 1).
uint addSimple(BigDigit[] result, const BigDigit [] left, const BigDigit [] right)
-pure nothrow
+pure nothrow @safe
in
{
- assert(result.length == left.length);
- assert(left.length >= right.length);
- assert(right.length>0);
+ assert(result.length == left.length,
+ "result and left must be of the same length");
+ assert(left.length >= right.length,
+ "left must be longer or of equal length to right");
+ assert(right.length > 0, "right must not be empty");
}
-body
+do
{
uint carry = multibyteAdd(result[0 .. right.length],
left[0 .. right.length], right, 0);
@@ -1891,11 +2249,13 @@ BigDigit subSimple(BigDigit [] result,const(BigDigit) [] left,
const(BigDigit) [] right) pure nothrow
in
{
- assert(result.length == left.length);
- assert(left.length >= right.length);
- assert(right.length>0);
+ assert(result.length == left.length,
+ "result and left must be of the same length");
+ assert(left.length >= right.length,
+ "left must be longer or of equal length to right");
+ assert(right.length > 0, "right must not be empty");
}
-body
+do
{
BigDigit carry = multibyteSub(result[0 .. right.length],
left[0 .. right.length], right, 0);
@@ -1912,9 +2272,10 @@ body
* Returns carry = 1 if result was less than right.
*/
BigDigit subAssignSimple(BigDigit [] result, const(BigDigit) [] right)
-pure nothrow
+pure nothrow @safe
{
- assert(result.length >= right.length);
+ assert(result.length >= right.length,
+ "result must be longer or of equal length to right");
uint c = multibyteSub(result[0 .. right.length], result[0 .. right.length], right, 0);
if (c && result.length > right.length)
c = multibyteIncrementAssign!('-')(result[right.length .. $], c);
@@ -1924,9 +2285,10 @@ pure nothrow
/* result = result + right
*/
BigDigit addAssignSimple(BigDigit [] result, const(BigDigit) [] right)
-pure nothrow
+pure nothrow @safe
{
- assert(result.length >= right.length);
+ assert(result.length >= right.length,
+ "result must be longer or of equal length to right");
uint c = multibyteAdd(result[0 .. right.length], result[0 .. right.length], right, 0);
if (c && result.length > right.length)
c = multibyteIncrementAssign!('+')(result[right.length .. $], c);
@@ -1936,7 +2298,7 @@ pure nothrow
/* performs result += wantSub? - right : right;
*/
BigDigit addOrSubAssignSimple(BigDigit [] result, const(BigDigit) [] right,
- bool wantSub) pure nothrow
+ bool wantSub) pure nothrow @safe
{
if (wantSub)
return subAssignSimple(result, right);
@@ -1946,9 +2308,10 @@ BigDigit addOrSubAssignSimple(BigDigit [] result, const(BigDigit) [] right,
// return true if x<y, considering leading zeros
-bool less(const(BigDigit)[] x, const(BigDigit)[] y) pure nothrow
+bool less(const(BigDigit)[] x, const(BigDigit)[] y) pure nothrow @safe
{
- assert(x.length >= y.length);
+ assert(x.length >= y.length,
+ "x must be longer or of equal length to y");
auto k = x.length-1;
while (x[k]==0 && k >= y.length)
--k;
@@ -1961,9 +2324,10 @@ bool less(const(BigDigit)[] x, const(BigDigit)[] y) pure nothrow
// Set result = abs(x-y), return true if result is negative(x<y), false if x <= y.
bool inplaceSub(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y)
- pure nothrow
+ pure nothrow @safe
{
- assert(result.length == (x.length >= y.length) ? x.length : y.length);
+ assert(result.length == ((x.length >= y.length) ? x.length : y.length),
+ "result must capable to store the maximum of x and y");
size_t minlen;
bool negative;
@@ -2000,10 +2364,12 @@ bool inplaceSub(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y)
/* Determine how much space is required for the temporaries
* when performing a Karatsuba multiplication.
+ * TODO: determining a tight bound is non-trivial and depends on KARATSUBALIMIT, see:
+ * https://issues.dlang.org/show_bug.cgi?id=20493
*/
size_t karatsubaRequiredBuffSize(size_t xlen) pure nothrow @safe
{
- return xlen <= KARATSUBALIMIT ? 0 : 2*xlen; // - KARATSUBALIMIT+2;
+ return xlen <= KARATSUBALIMIT ? 0 : (xlen * 9) / 4;
}
/* Sets result = x*y, using Karatsuba multiplication.
@@ -2018,11 +2384,12 @@ size_t karatsubaRequiredBuffSize(size_t xlen) pure nothrow @safe
* scratchbuff An array long enough to store all the temporaries. Will be destroyed.
*/
void mulKaratsuba(BigDigit [] result, const(BigDigit) [] x,
- const(BigDigit)[] y, BigDigit [] scratchbuff) pure nothrow
+ const(BigDigit)[] y, BigDigit [] scratchbuff) pure nothrow @safe
{
- assert(x.length >= y.length);
- assert(result.length < uint.max, "Operands too large");
- assert(result.length == x.length + y.length);
+ assert(x.length >= y.length, "x must be greater or equal to y");
+ assert(result.length < uint.max, "Operands too large");
+ assert(result.length == x.length + y.length,
+ "result must be as large as x + y");
if (x.length <= KARATSUBALIMIT)
{
return mulSimple(result, x, y);
@@ -2123,12 +2490,13 @@ void mulKaratsuba(BigDigit [] result, const(BigDigit) [] x,
}
void squareKaratsuba(BigDigit [] result, const BigDigit [] x,
- BigDigit [] scratchbuff) pure nothrow
+ BigDigit [] scratchbuff) pure nothrow @safe
{
// See mulKaratsuba for implementation comments.
// Squaring is simpler, since it never gets asymmetric.
assert(result.length < uint.max, "Operands too large");
- assert(result.length == 2*x.length);
+ assert(result.length == 2*x.length,
+ "result must be twice the length of x");
if (x.length <= KARATSUBASQUARELIMIT)
{
return squareSimple(result, x);
@@ -2180,13 +2548,14 @@ void squareKaratsuba(BigDigit [] result, const BigDigit [] x,
* u[0 .. v.length] holds the remainder.
*/
void schoolbookDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v)
- pure nothrow
+ pure nothrow @safe
{
- assert(quotient.length == u.length - v.length);
- assert(v.length > 1);
- assert(u.length >= v.length);
- assert((v[$-1]&0x8000_0000)!=0);
- assert(u[$-1] < v[$-1]);
+ assert(quotient.length == u.length - v.length,
+ "quotient has wrong length");
+ assert(v.length > 1, "v must not be empty");
+ assert(u.length >= v.length, "u must be larger or equal to v");
+ assert((v[$ - 1] & 0x8000_0000) != 0, "Invalid value at v[$ - 1]");
+ assert(u[$ - 1] < v[$ - 1], "u[$ - 1] must be less than v[$ - 1]");
// BUG: This code only works if BigDigit is uint.
uint vhi = v[$-1];
uint vlo = v[$-2];
@@ -2208,7 +2577,7 @@ void schoolbookDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v)
{
// Note: On DMD, this is only ~10% faster than the non-asm code.
uint *p = &u[j + v.length - 1];
- asm pure nothrow
+ asm pure nothrow @trusted
{
mov EAX, p;
mov EDX, [EAX+4];
@@ -2307,7 +2676,8 @@ private:
size_t highestDifferentDigit(const BigDigit [] left, const BigDigit [] right)
pure nothrow @nogc @safe
{
- assert(left.length == right.length);
+ assert(left.length == right.length,
+ "left have a length equal to that of right");
for (ptrdiff_t i = left.length - 1; i>0; --i)
{
if (left[i] != right[i])
@@ -2323,7 +2693,7 @@ int firstNonZeroDigit(const BigDigit [] x) pure nothrow @nogc @safe
while (x[k]==0)
{
++k;
- assert(k<x.length);
+ assert(k < x.length, "k must be less than x.length");
}
return k;
}
@@ -2354,26 +2724,30 @@ Returns:
*/
void recursiveDivMod(BigDigit[] quotient, BigDigit[] u, const(BigDigit)[] v,
BigDigit[] scratch, bool mayOverflow = false)
- pure nothrow
+ pure nothrow @safe
in
{
// v must be normalized
- assert(v.length > 1);
- assert((v[$ - 1] & 0x8000_0000) != 0);
- assert(!(u[$ - 1] & 0x8000_0000));
- assert(quotient.length == u.length - v.length);
+ assert(v.length > 1, "v must not be empty");
+ assert((v[$ - 1] & 0x8000_0000) != 0, "Invalid value at v[$ - 1]");
+ assert(!(u[$ - 1] & 0x8000_0000), "Invalid value at u[$ - 1]");
+ assert(quotient.length == u.length - v.length,
+ "quotient must be of equal length of u - v");
if (mayOverflow)
{
- assert(u[$-1] == 0);
- assert(u[$-2] & 0x8000_0000);
+ assert(u[$-1] == 0, "Invalid value at u[$ - 1]");
+ assert(u[$-2] & 0x8000_0000, "Invalid value at u[$ - 2]");
}
// Must be symmetric. Use block schoolbook division if not.
- assert((mayOverflow ? u.length-1 : u.length) <= 2 * v.length);
- assert((mayOverflow ? u.length-1 : u.length) >= v.length);
- assert(scratch.length >= quotient.length + (mayOverflow ? 0 : 1));
+ assert((mayOverflow ? u.length-1 : u.length) <= 2 * v.length,
+ "Invalid length of u");
+ assert((mayOverflow ? u.length-1 : u.length) >= v.length,
+ "Invalid length of u");
+ assert(scratch.length >= quotient.length + (mayOverflow ? 0 : 1),
+ "Invalid quotient length");
}
-body
+do
{
if (quotient.length < FASTDIVLIMIT)
{
@@ -2444,9 +2818,9 @@ body
// Needs (quot.length * k) scratch space to store the result of the multiply.
void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v,
ptrdiff_t k,
- BigDigit[] scratch, bool mayOverflow = false) pure nothrow
+ BigDigit[] scratch, bool mayOverflow = false) pure nothrow @safe
{
- assert(rem.length == v.length);
+ assert(rem.length == v.length, "rem must be as long as v");
mulInternal(scratch, quot, v[0 .. k]);
uint carry = 0;
if (mayOverflow)
@@ -2462,14 +2836,15 @@ void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v,
// Cope with unbalanced division by performing block schoolbook division.
void blockDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v)
-pure nothrow
+pure nothrow @safe
{
import core.memory : GC;
- assert(quotient.length == u.length - v.length);
- assert(v.length > 1);
- assert(u.length >= v.length);
- assert((v[$-1] & 0x8000_0000)!=0);
- assert((u[$-1] & 0x8000_0000)==0);
+ assert(quotient.length == u.length - v.length,
+ "quotient must be of equal length of u - v");
+ assert(v.length > 1, "v must not be empty");
+ assert(u.length >= v.length, "u must be longer or of equal length as v");
+ assert((v[$-1] & 0x8000_0000)!=0, "Invalid value at v[$ - 1]");
+ assert((u[$-1] & 0x8000_0000)==0, "Invalid value at u[$ - 1]");
BigDigit [] scratch = new BigDigit[v.length + 1];
// Perform block schoolbook division, with 'v.length' blocks.
@@ -2487,7 +2862,7 @@ pure nothrow
u[m - v.length .. m + v.length + (mayOverflow? 1: 0)], v, scratch, mayOverflow);
if (mayOverflow)
{
- assert(quotient[m] == 0);
+ assert(quotient[m] == 0, "quotient must not be 0");
quotient[m] = saveq;
}
m -= v.length;
@@ -2498,18 +2873,21 @@ pure nothrow
@system unittest
{
- import core.stdc.stdio;
-
- void printBiguint(const uint [] data)
+ version (none)
{
- char [] buff = biguintToHex(new char[data.length*9], data, '_');
- printf("%.*s\n", cast(int) buff.length, buff.ptr);
- }
+ import core.stdc.stdio;
- void printDecimalBigUint(BigUint data)
- {
- auto str = data.toDecimalString(0);
- printf("%.*s\n", cast(int) str.length, str.ptr);
+ void printBiguint(const uint [] data)
+ {
+ char [] buff = biguintToHex(new char[data.length*9], data, '_');
+ printf("%.*s\n", cast(int) buff.length, buff.ptr);
+ }
+
+ void printDecimalBigUint(BigUint data)
+ {
+ auto str = data.toDecimalString(0);
+ printf("%.*s\n", cast(int) str.length, str.ptr);
+ }
}
uint [] a, b;
diff --git a/libphobos/src/std/internal/math/biguintnoasm.d b/libphobos/src/std/internal/math/biguintnoasm.d
index aea1d50c2d6..2b2b5f18361 100644
--- a/libphobos/src/std/internal/math/biguintnoasm.d
+++ b/libphobos/src/std/internal/math/biguintnoasm.d
@@ -61,10 +61,10 @@ uint multibyteAddSub(char op)(uint[] dest, const(uint) [] src1,
}
c[19]=0x3333_3333;
uint carry = multibyteAddSub!('+')(c[0 .. 18], b[0 .. 18], a[0 .. 18], 0);
- assert(c[0]==0x8000_0003);
- assert(c[1]==4);
- assert(c[19]==0x3333_3333); // check for overrun
- assert(carry == 1);
+ assert(c[0]==0x8000_0003, "c[0] has invalid value");
+ assert(c[1]==4, "c[1] must be for");
+ assert(c[19]==0x3333_3333, "c[19] has invalid value"); // check for overrun
+ assert(carry == 1, "carry must be 1");
for (size_t i = 0; i < a.length; ++i)
{
a[i] = b[i] = c[i] = 0;
@@ -75,10 +75,10 @@ uint multibyteAddSub(char op)(uint[] dest, const(uint) [] src1,
b[10]=0x1D950C84;
a[5] =0x44444444;
carry = multibyteAddSub!('-')(a[0 .. 12], a[0 .. 12], b[0 .. 12], 0);
- assert(a[11] == 0);
+ assert(a[11] == 0, "a[11] must be 0");
for (size_t i = 0; i < 10; ++i)
if (i != 5)
- assert(a[i] == 0);
+ assert(a[i] == 0, "a[1] must be 0");
for (size_t q = 3; q < 36; ++q)
{
@@ -89,7 +89,7 @@ uint multibyteAddSub(char op)(uint[] dest, const(uint) [] src1,
a[q-2]=0x040000;
b[q-2]=0x040000;
carry = multibyteAddSub!('-')(a[0 .. q], a[0 .. q], b[0 .. q], 0);
- assert(a[q-2]==0);
+ assert(a[q-2]==0, "a[q-2] must be 0");
}
}
@@ -193,7 +193,7 @@ void multibyteShr(uint [] dest, const(uint) [] src, uint numbits)
uint multibyteMul(uint[] dest, const(uint)[] src, uint multiplier, uint carry)
pure @nogc @safe
{
- assert(dest.length == src.length);
+ assert(dest.length == src.length, "dest and src must have the same length");
ulong c = carry;
for (size_t i = 0; i < src.length; ++i)
{
@@ -220,7 +220,7 @@ uint multibyteMul(uint[] dest, const(uint)[] src, uint multiplier, uint carry)
uint multibyteMulAdd(char op)(uint [] dest, const(uint)[] src,
uint multiplier, uint carry) pure @nogc @safe
{
- assert(dest.length == src.length);
+ assert(dest.length == src.length, "dest and src must have the same length");
ulong c = carry;
for (size_t i = 0; i < src.length; ++i)
{
diff --git a/libphobos/src/std/internal/math/errorfunction.d b/libphobos/src/std/internal/math/errorfunction.d
index 4012e64181f..8894ffb92ea 100644
--- a/libphobos/src/std/internal/math/errorfunction.d
+++ b/libphobos/src/std/internal/math/errorfunction.d
@@ -24,6 +24,7 @@
*/
module std.internal.math.errorfunction;
import std.math;
+import core.math : fabs, sqrt;
pure:
nothrow:
@@ -50,16 +51,16 @@ private {
/* erfc(x) = exp(-x^2) P(1/x)/Q(1/x)
1/8 <= 1/x <= 1
Peak relative error 5.8e-21 */
-immutable real[10] P = [ -0x1.30dfa809b3cc6676p-17, 0x1.38637cd0913c0288p+18,
- 0x1.2f015e047b4476bp+22, 0x1.24726f46aa9ab08p+25, 0x1.64b13c6395dc9c26p+27,
- 0x1.294c93046ad55b5p+29, 0x1.5962a82f92576dap+30, 0x1.11a709299faba04ap+31,
- 0x1.11028065b087be46p+31, 0x1.0d8ef40735b097ep+30
+immutable real[10] P = [ -0x1.30dfa809b3cc6676p-17L, 0x1.38637cd0913c0288p+18L,
+ 0x1.2f015e047b4476bp+22L, 0x1.24726f46aa9ab08p+25L, 0x1.64b13c6395dc9c26p+27L,
+ 0x1.294c93046ad55b5p+29L, 0x1.5962a82f92576dap+30L, 0x1.11a709299faba04ap+31L,
+ 0x1.11028065b087be46p+31L, 0x1.0d8ef40735b097ep+30L
];
-immutable real[11] Q = [ 0x1.14d8e2a72dec49f4p+19, 0x1.0c880ff467626e1p+23,
- 0x1.04417ef060b58996p+26, 0x1.404e61ba86df4ebap+28, 0x1.0f81887bc82b873ap+30,
- 0x1.4552a5e39fb49322p+31, 0x1.11779a0ceb2a01cep+32, 0x1.3544dd691b5b1d5cp+32,
- 0x1.a91781f12251f02ep+31, 0x1.0d8ef3da605a1c86p+30, 1.0
+immutable real[11] Q = [ 0x1.14d8e2a72dec49f4p+19L, 0x1.0c880ff467626e1p+23L,
+ 0x1.04417ef060b58996p+26L, 0x1.404e61ba86df4ebap+28L, 0x1.0f81887bc82b873ap+30L,
+ 0x1.4552a5e39fb49322p+31L, 0x1.11779a0ceb2a01cep+32L, 0x1.3544dd691b5b1d5cp+32L,
+ 0x1.a91781f12251f02ep+31L, 0x1.0d8ef3da605a1c86p+30L, 1.0L
];
// For 128 bit quadruple-precision floats, we use a higher-precision implementation
@@ -120,7 +121,7 @@ static if (isIEEEQuadruple)
1.367697521219069280358984081407807931847E1L,
2.276988395995528495055594829206582732682E1L,
7.647745753648996559837591812375456641163E-1L,
- 1.0
+ 1.0L
];
// erfc(0.375) = C14a + C14b to extra precision.
@@ -150,7 +151,7 @@ static if (isIEEEQuadruple)
2.628752920321455606558942309396855629459E1L,
2.455649035885114308978333741080991380610E1L,
1.378826653595128464383127836412100939126E0L,
- 1.0
+ 1.0L
];
// erfc(0.5) = C15a + C15b to extra precision.
immutable real C15a = 0.4794921875L;
@@ -179,7 +180,7 @@ static if (isIEEEQuadruple)
4.465334221323222943418085830026979293091E1L,
2.612723259683205928103787842214809134746E1L,
2.341526751185244109722204018543276124997E0L,
- 1.0
+ 1.0L
];
// erfc(0.625) = C16a + C16b to extra precision.
immutable real C16a = 0.3767547607421875L;
@@ -208,7 +209,7 @@ static if (isIEEEQuadruple)
5.555800830216764702779238020065345401144E1L,
2.646215470959050279430447295801291168941E1L,
2.984905282103517497081766758550112011265E0L,
- 1.0
+ 1.0L
];
// erfc(0.75) = C17a + C17b to extra precision.
immutable real C17a = 0.2888336181640625L;
@@ -238,7 +239,7 @@ static if (isIEEEQuadruple)
5.998153487868943708236273854747564557632E1L,
2.657695108438628847733050476209037025318E1L,
3.252140524394421868923289114410336976512E0L,
- 1.0
+ 1.0L
];
// erfc(0.875) = C18a + C18b to extra precision.
@@ -268,7 +269,7 @@ static if (isIEEEQuadruple)
6.868976819510254139741559102693828237440E1L,
2.801505816247677193480190483913753613630E1L,
3.604439909194350263552750347742663954481E0L,
- 1.0
+ 1.0L
];
// erfc(1.0) = C19a + C19b to extra precision.
@@ -298,7 +299,7 @@ static if (isIEEEQuadruple)
8.848641738570783406484348434387611713070E1L,
3.132269062552392974833215844236160958502E1L,
4.430131663290563523933419966185230513168E0L,
- 1.0
+ 1.0L
];
// erfc(1.125) = C20a + C20b to extra precision.
@@ -604,26 +605,26 @@ else
/* erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
1/128 <= 1/x < 1/8
Peak relative error 1.9e-21 */
- immutable real[5] R = [ 0x1.b9f6d8b78e22459ep-6, 0x1.1b84686b0a4ea43ap-1,
- 0x1.b8f6aebe96000c2ap+1, 0x1.cb1dbedac27c8ec2p+2, 0x1.cf885f8f572a4c14p+1
+ immutable real[5] R = [ 0x1.b9f6d8b78e22459ep-6L, 0x1.1b84686b0a4ea43ap-1L,
+ 0x1.b8f6aebe96000c2ap+1L, 0x1.cb1dbedac27c8ec2p+2L, 0x1.cf885f8f572a4c14p+1L
];
immutable real[6] S = [
- 0x1.87ae3cae5f65eb5ep-5, 0x1.01616f266f306d08p+0, 0x1.a4abe0411eed6c22p+2,
- 0x1.eac9ce3da600abaap+3, 0x1.5752a9ac2faebbccp+3, 1.0
+ 0x1.87ae3cae5f65eb5ep-5L, 0x1.01616f266f306d08p+0L, 0x1.a4abe0411eed6c22p+2L,
+ 0x1.eac9ce3da600abaap+3L, 0x1.5752a9ac2faebbccp+3L, 1.0L
];
/* erf(x) = x P(x^2)/Q(x^2)
0 <= x <= 1
Peak relative error 7.6e-23 */
- immutable real[7] T = [ 0x1.0da01654d757888cp+20, 0x1.2eb7497bc8b4f4acp+17,
- 0x1.79078c19530f72a8p+15, 0x1.4eaf2126c0b2c23p+11, 0x1.1f2ea81c9d272a2ep+8,
- 0x1.59ca6e2d866e625p+2, 0x1.c188e0b67435faf4p-4
+ immutable real[7] T = [ 0x1.0da01654d757888cp+20L, 0x1.2eb7497bc8b4f4acp+17L,
+ 0x1.79078c19530f72a8p+15L, 0x1.4eaf2126c0b2c23p+11L, 0x1.1f2ea81c9d272a2ep+8L,
+ 0x1.59ca6e2d866e625p+2L, 0x1.c188e0b67435faf4p-4L
];
- immutable real[7] U = [ 0x1.dde6025c395ae34ep+19, 0x1.c4bc8b6235df35aap+18,
- 0x1.8465900e88b6903ap+16, 0x1.855877093959ffdp+13, 0x1.e5c44395625ee358p+9,
- 0x1.6a0fed103f1c68a6p+5, 1.0
+ immutable real[7] U = [ 0x1.dde6025c395ae34ep+19L, 0x1.c4bc8b6235df35aap+18L,
+ 0x1.8465900e88b6903ap+16L, 0x1.855877093959ffdp+13L, 0x1.e5c44395625ee358p+9L,
+ 0x1.6a0fed103f1c68a6p+5L, 1.0L
];
}
}
@@ -835,7 +836,7 @@ real erf(real x)
return -1.0;
if (x == real.infinity)
return 1.0;
- immutable ax = abs(x);
+ immutable ax = fabs(x);
if (ax > 1.0L)
return 1.0L - erfc(x);
@@ -867,16 +868,16 @@ real erf(real x)
@safe unittest
{
// High resolution test points.
- enum real erfc0_250 = 0.723663330078125 + 1.0279753638067014931732235184287934646022E-5;
- enum real erfc0_375 = 0.5958709716796875 + 1.2118885490201676174914080878232469565953E-5;
- enum real erfc0_500 = 0.4794921875 + 7.9346869534623172533461080354712635484242E-6;
- enum real erfc0_625 = 0.3767547607421875 + 4.3570693945275513594941232097252997287766E-6;
- enum real erfc0_750 = 0.2888336181640625 + 1.0748182422368401062165408589222625794046E-5;
- enum real erfc0_875 = 0.215911865234375 + 1.3073705765341685464282101150637224028267E-5;
- enum real erfc1_000 = 0.15728759765625 + 1.1609394035130658779364917390740703933002E-5;
- enum real erfc1_125 = 0.111602783203125 + 8.9850951672359304215530728365232161564636E-6;
+ enum real erfc0_250 = 0.723663330078125L + 1.0279753638067014931732235184287934646022E-5L;
+ enum real erfc0_375 = 0.5958709716796875L + 1.2118885490201676174914080878232469565953E-5L;
+ enum real erfc0_500 = 0.4794921875L + 7.9346869534623172533461080354712635484242E-6L;
+ enum real erfc0_625 = 0.3767547607421875L + 4.3570693945275513594941232097252997287766E-6L;
+ enum real erfc0_750 = 0.2888336181640625L + 1.0748182422368401062165408589222625794046E-5L;
+ enum real erfc0_875 = 0.215911865234375L + 1.3073705765341685464282101150637224028267E-5L;
+ enum real erfc1_000 = 0.15728759765625L + 1.1609394035130658779364917390740703933002E-5L;
+ enum real erfc1_125 = 0.111602783203125L + 8.9850951672359304215530728365232161564636E-6L;
- enum real erf0_875 = (1-0.215911865234375) - 1.3073705765341685464282101150637224028267E-5;
+ enum real erf0_875 = (1-0.215911865234375L) - 1.3073705765341685464282101150637224028267E-5L;
static bool isNaNWithPayload(real x, ulong payload) @safe pure nothrow @nogc
{
@@ -931,7 +932,7 @@ real expx2(real x, int sign)
const real M = 32_768.0;
const real MINV = 3.0517578125e-5L;
- x = abs(x);
+ x = fabs(x);
if (sign < 0)
x = -x;
@@ -984,7 +985,7 @@ Journal of Statistical Software <b>11</b>, (July 2004).
real normalDistributionImpl(real a)
{
real x = a * SQRT1_2;
- real z = abs(x);
+ real z = fabs(x);
if ( z < 1.0 )
return 0.5L + 0.5L * erf(x);
@@ -1002,7 +1003,7 @@ real normalDistributionImpl(real a)
@safe unittest
{
-assert(fabs(normalDistributionImpl(1L) - (0.841344746068543))< 0.0000000000000005);
+assert(fabs(normalDistributionImpl(1L) - (0.841344746068543L)) < 0.0000000000000005L);
assert(isIdentical(normalDistributionImpl(NaN(0x325)), NaN(0x325)));
}
@@ -1024,56 +1025,56 @@ real normalDistributionInvImpl(real p)
in {
assert(p >= 0.0L && p <= 1.0L, "Domain error");
}
-body
+do
{
static immutable real[8] P0 =
-[ -0x1.758f4d969484bfdcp-7, 0x1.53cee17a59259dd2p-3,
- -0x1.ea01e4400a9427a2p-1, 0x1.61f7504a0105341ap+1, -0x1.09475a594d0399f6p+2,
- 0x1.7c59e7a0df99e3e2p+1, -0x1.87a81da52edcdf14p-1, 0x1.1fb149fd3f83600cp-7
+[ -0x1.758f4d969484bfdcp-7L, 0x1.53cee17a59259dd2p-3L,
+ -0x1.ea01e4400a9427a2p-1L, 0x1.61f7504a0105341ap+1L, -0x1.09475a594d0399f6p+2L,
+ 0x1.7c59e7a0df99e3e2p+1L, -0x1.87a81da52edcdf14p-1L, 0x1.1fb149fd3f83600cp-7L
];
static immutable real[8] Q0 =
-[ -0x1.64b92ae791e64bb2p-7, 0x1.7585c7d597298286p-3,
- -0x1.40011be4f7591ce6p+0, 0x1.1fc067d8430a425ep+2, -0x1.21008ffb1e7ccdf2p+3,
- 0x1.3d1581cf9bc12fccp+3, -0x1.53723a89fd8f083cp+2, 1.0
+[ -0x1.64b92ae791e64bb2p-7L, 0x1.7585c7d597298286p-3L,
+ -0x1.40011be4f7591ce6p+0L, 0x1.1fc067d8430a425ep+2L, -0x1.21008ffb1e7ccdf2p+3L,
+ 0x1.3d1581cf9bc12fccp+3L, -0x1.53723a89fd8f083cp+2L, 1.0L
];
static immutable real[10] P1 =
-[ 0x1.20ceea49ea142f12p-13, 0x1.cbe8a7267aea80bp-7,
- 0x1.79fea765aa787c48p-2, 0x1.d1f59faa1f4c4864p+1, 0x1.1c22e426a013bb96p+4,
- 0x1.a8675a0c51ef3202p+5, 0x1.75782c4f83614164p+6, 0x1.7a2f3d90948f1666p+6,
- 0x1.5cd116ee4c088c3ap+5, 0x1.1361e3eb6e3cc20ap+2
+[ 0x1.20ceea49ea142f12p-13L, 0x1.cbe8a7267aea80bp-7L,
+ 0x1.79fea765aa787c48p-2L, 0x1.d1f59faa1f4c4864p+1L, 0x1.1c22e426a013bb96p+4L,
+ 0x1.a8675a0c51ef3202p+5L, 0x1.75782c4f83614164p+6L, 0x1.7a2f3d90948f1666p+6L,
+ 0x1.5cd116ee4c088c3ap+5L, 0x1.1361e3eb6e3cc20ap+2L
];
static immutable real[10] Q1 =
-[ 0x1.3a4ce1406cea98fap-13, 0x1.f45332623335cda2p-7,
- 0x1.98f28bbd4b98db1p-2, 0x1.ec3b24f9c698091cp+1, 0x1.1cc56ecda7cf58e4p+4,
- 0x1.92c6f7376bf8c058p+5, 0x1.4154c25aa47519b4p+6, 0x1.1b321d3b927849eap+6,
- 0x1.403a5f5a4ce7b202p+4, 1.0
+[ 0x1.3a4ce1406cea98fap-13L, 0x1.f45332623335cda2p-7L,
+ 0x1.98f28bbd4b98db1p-2L, 0x1.ec3b24f9c698091cp+1L, 0x1.1cc56ecda7cf58e4p+4L,
+ 0x1.92c6f7376bf8c058p+5L, 0x1.4154c25aa47519b4p+6L, 0x1.1b321d3b927849eap+6L,
+ 0x1.403a5f5a4ce7b202p+4L, 1.0L
];
static immutable real[8] P2 =
-[ 0x1.8c124a850116a6d8p-21, 0x1.534abda3c2fb90bap-13,
- 0x1.29a055ec93a4718cp-7, 0x1.6468e98aad6dd474p-3, 0x1.3dab2ef4c67a601cp+0,
- 0x1.e1fb3a1e70c67464p+1, 0x1.b6cce8035ff57b02p+2, 0x1.9f4c9e749ff35f62p+1
+[ 0x1.8c124a850116a6d8p-21L, 0x1.534abda3c2fb90bap-13L,
+ 0x1.29a055ec93a4718cp-7L, 0x1.6468e98aad6dd474p-3L, 0x1.3dab2ef4c67a601cp+0L,
+ 0x1.e1fb3a1e70c67464p+1L, 0x1.b6cce8035ff57b02p+2L, 0x1.9f4c9e749ff35f62p+1L
];
static immutable real[8] Q2 =
-[ 0x1.af03f4fc0655e006p-21, 0x1.713192048d11fb2p-13,
- 0x1.4357e5bbf5fef536p-7, 0x1.7fdac8749985d43cp-3, 0x1.4a080c813a2d8e84p+0,
- 0x1.c3a4b423cdb41bdap+1, 0x1.8160694e24b5557ap+2, 1.0
+[ 0x1.af03f4fc0655e006p-21L, 0x1.713192048d11fb2p-13L,
+ 0x1.4357e5bbf5fef536p-7L, 0x1.7fdac8749985d43cp-3L, 0x1.4a080c813a2d8e84p+0L,
+ 0x1.c3a4b423cdb41bdap+1L, 0x1.8160694e24b5557ap+2L, 1.0L
];
static immutable real[8] P3 =
-[ -0x1.55da447ae3806168p-34, -0x1.145635641f8778a6p-24,
- -0x1.abf46d6b48040128p-17, -0x1.7da550945da790fcp-11, -0x1.aa0b2a31157775fap-8,
- 0x1.b11d97522eed26bcp-3, 0x1.1106d22f9ae89238p+1, 0x1.029a358e1e630f64p+1
+[ -0x1.55da447ae3806168p-34L, -0x1.145635641f8778a6p-24L,
+ -0x1.abf46d6b48040128p-17L, -0x1.7da550945da790fcp-11L, -0x1.aa0b2a31157775fap-8L,
+ 0x1.b11d97522eed26bcp-3L, 0x1.1106d22f9ae89238p+1L, 0x1.029a358e1e630f64p+1L
];
static immutable real[8] Q3 =
-[ -0x1.74022dd5523e6f84p-34, -0x1.2cb60d61e29ee836p-24,
- -0x1.d19e6ec03a85e556p-17, -0x1.9ea2a7b4422f6502p-11, -0x1.c54b1e852f107162p-8,
- 0x1.e05268dd3c07989ep-3, 0x1.239c6aff14afbf82p+1, 1.0
+[ -0x1.74022dd5523e6f84p-34L, -0x1.2cb60d61e29ee836p-24L,
+ -0x1.d19e6ec03a85e556p-17L, -0x1.9ea2a7b4422f6502p-11L, -0x1.c54b1e852f107162p-8L,
+ 0x1.e05268dd3c07989ep-3L, 0x1.239c6aff14afbf82p+1L, 1.0L
];
if (p <= 0.0L || p >= 1.0L)
@@ -1130,9 +1131,9 @@ static immutable real[8] Q3 =
{
// TODO: Use verified test points.
// The values below are from Excel 2003.
- assert(fabs(normalDistributionInvImpl(0.001) - (-3.09023230616779))< 0.00000000000005);
- assert(fabs(normalDistributionInvImpl(1e-50) - (-14.9333375347885))< 0.00000000000005);
- assert(feqrel(normalDistributionInvImpl(0.999), -normalDistributionInvImpl(0.001)) > real.mant_dig-6);
+ assert(fabs(normalDistributionInvImpl(0.001) - (-3.09023230616779L)) < 0.00000000000005L);
+ assert(fabs(normalDistributionInvImpl(1e-50) - (-14.9333375347885L)) < 0.00000000000005L);
+ assert(feqrel(normalDistributionInvImpl(0.999L), -normalDistributionInvImpl(0.001L)) > real.mant_dig-6);
// Excel 2003 gets all the following values wrong!
assert(normalDistributionInvImpl(0.0) == -real.infinity);
@@ -1141,5 +1142,5 @@ static immutable real[8] Q3 =
// (Excel 2003 returns norminv(p) = -30 for all p < 1e-200).
// The value tested here is the one the function returned in Jan 2006.
real unknown1 = normalDistributionInvImpl(1e-250L);
- assert( fabs(unknown1 -(-33.79958617269L) ) < 0.00000005);
+ assert( fabs(unknown1 -(-33.79958617269L) ) < 0.00000005L);
}
diff --git a/libphobos/src/std/internal/math/gammafunction.d b/libphobos/src/std/internal/math/gammafunction.d
index c9677c72463..7f72234d640 100644
--- a/libphobos/src/std/internal/math/gammafunction.d
+++ b/libphobos/src/std/internal/math/gammafunction.d
@@ -21,6 +21,7 @@ Macros:
module std.internal.math.gammafunction;
import std.internal.math.errorfunction;
import std.math;
+import core.math : fabs, sin, sqrt;
pure:
nothrow:
@@ -34,46 +35,46 @@ immutable real EULERGAMMA = 0.57721_56649_01532_86060_65120_90082_40243_10421_59
// Polynomial approximations for gamma and loggamma.
-immutable real[8] GammaNumeratorCoeffs = [ 1.0,
- 0x1.acf42d903366539ep-1, 0x1.73a991c8475f1aeap-2, 0x1.c7e918751d6b2a92p-4,
- 0x1.86d162cca32cfe86p-6, 0x1.0c378e2e6eaf7cd8p-8, 0x1.dc5c66b7d05feb54p-12,
- 0x1.616457b47e448694p-15
+immutable real[8] GammaNumeratorCoeffs = [ 1.0L,
+ 0x1.acf42d903366539ep-1L, 0x1.73a991c8475f1aeap-2L, 0x1.c7e918751d6b2a92p-4L,
+ 0x1.86d162cca32cfe86p-6L, 0x1.0c378e2e6eaf7cd8p-8L, 0x1.dc5c66b7d05feb54p-12L,
+ 0x1.616457b47e448694p-15L
];
-immutable real[9] GammaDenominatorCoeffs = [ 1.0,
- 0x1.a8f9faae5d8fc8bp-2, -0x1.cb7895a6756eebdep-3, -0x1.7b9bab006d30652ap-5,
- 0x1.c671af78f312082ep-6, -0x1.a11ebbfaf96252dcp-11, -0x1.447b4d2230a77ddap-10,
- 0x1.ec1d45bb85e06696p-13,-0x1.d4ce24d05bd0a8e6p-17
+immutable real[9] GammaDenominatorCoeffs = [ 1.0L,
+ 0x1.a8f9faae5d8fc8bp-2L, -0x1.cb7895a6756eebdep-3L, -0x1.7b9bab006d30652ap-5L,
+ 0x1.c671af78f312082ep-6L, -0x1.a11ebbfaf96252dcp-11L, -0x1.447b4d2230a77ddap-10L,
+ 0x1.ec1d45bb85e06696p-13L,-0x1.d4ce24d05bd0a8e6p-17L
];
-immutable real[9] GammaSmallCoeffs = [ 1.0,
- 0x1.2788cfc6fb618f52p-1, -0x1.4fcf4026afa2f7ecp-1, -0x1.5815e8fa24d7e306p-5,
- 0x1.5512320aea2ad71ap-3, -0x1.59af0fb9d82e216p-5, -0x1.3b4b61d3bfdf244ap-7,
- 0x1.d9358e9d9d69fd34p-8, -0x1.38fc4bcbada775d6p-10
+immutable real[9] GammaSmallCoeffs = [ 1.0L,
+ 0x1.2788cfc6fb618f52p-1L, -0x1.4fcf4026afa2f7ecp-1L, -0x1.5815e8fa24d7e306p-5L,
+ 0x1.5512320aea2ad71ap-3L, -0x1.59af0fb9d82e216p-5L, -0x1.3b4b61d3bfdf244ap-7L,
+ 0x1.d9358e9d9d69fd34p-8L, -0x1.38fc4bcbada775d6p-10L
];
-immutable real[9] GammaSmallNegCoeffs = [ -1.0,
- 0x1.2788cfc6fb618f54p-1, 0x1.4fcf4026afa2bc4cp-1, -0x1.5815e8fa2468fec8p-5,
- -0x1.5512320baedaf4b6p-3, -0x1.59af0fa283baf07ep-5, 0x1.3b4a70de31e05942p-7,
- 0x1.d9398be3bad13136p-8, 0x1.291b73ee05bcbba2p-10
+immutable real[9] GammaSmallNegCoeffs = [ -1.0L,
+ 0x1.2788cfc6fb618f54p-1L, 0x1.4fcf4026afa2bc4cp-1L, -0x1.5815e8fa2468fec8p-5L,
+ -0x1.5512320baedaf4b6p-3L, -0x1.59af0fa283baf07ep-5L, 0x1.3b4a70de31e05942p-7L,
+ 0x1.d9398be3bad13136p-8L, 0x1.291b73ee05bcbba2p-10L
];
immutable real[7] logGammaStirlingCoeffs = [
- 0x1.5555555555553f98p-4, -0x1.6c16c16c07509b1p-9, 0x1.a01a012461cbf1e4p-11,
- -0x1.3813089d3f9d164p-11, 0x1.b911a92555a277b8p-11, -0x1.ed0a7b4206087b22p-10,
- 0x1.402523859811b308p-8
+ 0x1.5555555555553f98p-4L, -0x1.6c16c16c07509b1p-9L, 0x1.a01a012461cbf1e4p-11L,
+ -0x1.3813089d3f9d164p-11L, 0x1.b911a92555a277b8p-11L, -0x1.ed0a7b4206087b22p-10L,
+ 0x1.402523859811b308p-8L
];
immutable real[7] logGammaNumerator = [
- -0x1.0edd25913aaa40a2p+23, -0x1.31c6ce2e58842d1ep+24, -0x1.f015814039477c3p+23,
- -0x1.74ffe40c4b184b34p+22, -0x1.0d9c6d08f9eab55p+20, -0x1.54c6b71935f1fc88p+16,
- -0x1.0e761b42932b2aaep+11
+ -0x1.0edd25913aaa40a2p+23L, -0x1.31c6ce2e58842d1ep+24L, -0x1.f015814039477c3p+23L,
+ -0x1.74ffe40c4b184b34p+22L, -0x1.0d9c6d08f9eab55p+20L, -0x1.54c6b71935f1fc88p+16L,
+ -0x1.0e761b42932b2aaep+11L
];
immutable real[8] logGammaDenominator = [
- -0x1.4055572d75d08c56p+24, -0x1.deeb6013998e4d76p+24, -0x1.106f7cded5dcc79ep+24,
- -0x1.25e17184848c66d2p+22, -0x1.301303b99a614a0ap+19, -0x1.09e76ab41ae965p+15,
- -0x1.00f95ced9e5f54eep+9, 1.0
+ -0x1.4055572d75d08c56p+24L, -0x1.deeb6013998e4d76p+24L, -0x1.106f7cded5dcc79ep+24L,
+ -0x1.25e17184848c66d2p+22L, -0x1.301303b99a614a0ap+19L, -0x1.09e76ab41ae965p+15L,
+ -0x1.00f95ced9e5f54eep+9L, 1.0L
];
/*
@@ -89,9 +90,9 @@ real gammaStirling(real x)
// CEPHES code Copyright 1994 by Stephen L. Moshier
static immutable real[9] SmallStirlingCoeffs = [
- 0x1.55555555555543aap-4, 0x1.c71c71c720dd8792p-9, -0x1.5f7268f0b5907438p-9,
- -0x1.e13cd410e0477de6p-13, 0x1.9b0f31643442616ep-11, 0x1.2527623a3472ae08p-14,
- -0x1.37f6bc8ef8b374dep-11,-0x1.8c968886052b872ap-16, 0x1.76baa9c6d3eeddbcp-11
+ 0x1.55555555555543aap-4L, 0x1.c71c71c720dd8792p-9L, -0x1.5f7268f0b5907438p-9L,
+ -0x1.e13cd410e0477de6p-13L, 0x1.9b0f31643442616ep-11L, 0x1.2527623a3472ae08p-14L,
+ -0x1.37f6bc8ef8b374dep-11L,-0x1.8c968886052b872ap-16L, 0x1.76baa9c6d3eeddbcp-11L
];
static immutable real[7] LargeStirlingCoeffs = [ 1.0L,
@@ -144,76 +145,76 @@ real gammaStirling(real x)
real igammaTemmeLarge(real a, real x)
{
static immutable real[][13] coef = [
- [ -0.333333333333333333333, 0.0833333333333333333333,
- -0.0148148148148148148148, 0.00115740740740740740741,
- 0.000352733686067019400353, -0.0001787551440329218107,
- 0.39192631785224377817e-4, -0.218544851067999216147e-5,
- -0.18540622107151599607e-5, 0.829671134095308600502e-6,
- -0.176659527368260793044e-6, 0.670785354340149858037e-8,
- 0.102618097842403080426e-7, -0.438203601845335318655e-8,
- 0.914769958223679023418e-9, -0.255141939949462497669e-10,
- -0.583077213255042506746e-10, 0.243619480206674162437e-10,
- -0.502766928011417558909e-11 ],
- [ -0.00185185185185185185185, -0.00347222222222222222222,
- 0.00264550264550264550265, -0.000990226337448559670782,
- 0.000205761316872427983539, -0.40187757201646090535e-6,
- -0.18098550334489977837e-4, 0.764916091608111008464e-5,
- -0.161209008945634460038e-5, 0.464712780280743434226e-8,
- 0.137863344691572095931e-6, -0.575254560351770496402e-7,
- 0.119516285997781473243e-7, -0.175432417197476476238e-10,
- -0.100915437106004126275e-8, 0.416279299184258263623e-9,
- -0.856390702649298063807e-10 ],
- [ 0.00413359788359788359788, -0.00268132716049382716049,
- 0.000771604938271604938272, 0.200938786008230452675e-5,
- -0.000107366532263651605215, 0.529234488291201254164e-4,
- -0.127606351886187277134e-4, 0.342357873409613807419e-7,
- 0.137219573090629332056e-5, -0.629899213838005502291e-6,
- 0.142806142060642417916e-6, -0.204770984219908660149e-9,
- -0.140925299108675210533e-7, 0.622897408492202203356e-8,
- -0.136704883966171134993e-8 ],
- [ 0.000649434156378600823045, 0.000229472093621399176955,
- -0.000469189494395255712128, 0.000267720632062838852962,
- -0.756180167188397641073e-4, -0.239650511386729665193e-6,
- 0.110826541153473023615e-4, -0.56749528269915965675e-5,
- 0.142309007324358839146e-5, -0.278610802915281422406e-10,
- -0.169584040919302772899e-6, 0.809946490538808236335e-7,
- -0.191111684859736540607e-7 ],
- [ -0.000861888290916711698605, 0.000784039221720066627474,
- -0.000299072480303190179733, -0.146384525788434181781e-5,
- 0.664149821546512218666e-4, -0.396836504717943466443e-4,
- 0.113757269706784190981e-4, 0.250749722623753280165e-9,
- -0.169541495365583060147e-5, 0.890750753220530968883e-6,
- -0.229293483400080487057e-6],
- [ -0.000336798553366358150309, -0.697281375836585777429e-4,
- 0.000277275324495939207873, -0.000199325705161888477003,
- 0.679778047793720783882e-4, 0.141906292064396701483e-6,
- -0.135940481897686932785e-4, 0.801847025633420153972e-5,
- -0.229148117650809517038e-5 ],
- [ 0.000531307936463992223166, -0.000592166437353693882865,
- 0.000270878209671804482771, 0.790235323266032787212e-6,
- -0.815396936756196875093e-4, 0.561168275310624965004e-4,
- -0.183291165828433755673e-4, -0.307961345060330478256e-8,
- 0.346515536880360908674e-5, -0.20291327396058603727e-5,
- 0.57887928631490037089e-6 ],
- [ 0.000344367606892377671254, 0.517179090826059219337e-4,
- -0.000334931610811422363117, 0.000281269515476323702274,
- -0.000109765822446847310235, -0.127410090954844853795e-6,
- 0.277444515115636441571e-4, -0.182634888057113326614e-4,
- 0.578769494973505239894e-5 ],
- [ -0.000652623918595309418922, 0.000839498720672087279993,
- -0.000438297098541721005061, -0.696909145842055197137e-6,
- 0.000166448466420675478374, -0.000127835176797692185853,
- 0.462995326369130429061e-4 ],
- [ -0.000596761290192746250124, -0.720489541602001055909e-4,
- 0.000678230883766732836162, -0.0006401475260262758451,
- 0.000277501076343287044992 ],
- [ 0.00133244544948006563713, -0.0019144384985654775265,
- 0.00110893691345966373396 ],
- [ 0.00157972766073083495909, 0.000162516262783915816899,
- -0.00206334210355432762645, 0.00213896861856890981541,
- -0.00101085593912630031708 ],
- [ -0.00407251211951401664727, 0.00640336283380806979482,
- -0.00404101610816766177474 ]
+ [ -0.333333333333333333333L, 0.0833333333333333333333L,
+ -0.0148148148148148148148L, 0.00115740740740740740741L,
+ 0.000352733686067019400353L, -0.0001787551440329218107L,
+ 0.39192631785224377817e-4L, -0.218544851067999216147e-5L,
+ -0.18540622107151599607e-5L, 0.829671134095308600502e-6L,
+ -0.176659527368260793044e-6L, 0.670785354340149858037e-8L,
+ 0.102618097842403080426e-7L, -0.438203601845335318655e-8L,
+ 0.914769958223679023418e-9L, -0.255141939949462497669e-10L,
+ -0.583077213255042506746e-10L, 0.243619480206674162437e-10L,
+ -0.502766928011417558909e-11L ],
+ [ -0.00185185185185185185185L, -0.00347222222222222222222L,
+ 0.00264550264550264550265L, -0.000990226337448559670782L,
+ 0.000205761316872427983539L, -0.40187757201646090535e-6L,
+ -0.18098550334489977837e-4L, 0.764916091608111008464e-5L,
+ -0.161209008945634460038e-5L, 0.464712780280743434226e-8L,
+ 0.137863344691572095931e-6L, -0.575254560351770496402e-7L,
+ 0.119516285997781473243e-7L, -0.175432417197476476238e-10L,
+ -0.100915437106004126275e-8L, 0.416279299184258263623e-9L,
+ -0.856390702649298063807e-10L ],
+ [ 0.00413359788359788359788L, -0.00268132716049382716049L,
+ 0.000771604938271604938272L, 0.200938786008230452675e-5L,
+ -0.000107366532263651605215L, 0.529234488291201254164e-4L,
+ -0.127606351886187277134e-4L, 0.342357873409613807419e-7L,
+ 0.137219573090629332056e-5L, -0.629899213838005502291e-6L,
+ 0.142806142060642417916e-6L, -0.204770984219908660149e-9L,
+ -0.140925299108675210533e-7L, 0.622897408492202203356e-8L,
+ -0.136704883966171134993e-8L ],
+ [ 0.000649434156378600823045L, 0.000229472093621399176955L,
+ -0.000469189494395255712128L, 0.000267720632062838852962L,
+ -0.756180167188397641073e-4L, -0.239650511386729665193e-6L,
+ 0.110826541153473023615e-4L, -0.56749528269915965675e-5L,
+ 0.142309007324358839146e-5L, -0.278610802915281422406e-10L,
+ -0.169584040919302772899e-6L, 0.809946490538808236335e-7L,
+ -0.191111684859736540607e-7L ],
+ [ -0.000861888290916711698605L, 0.000784039221720066627474L,
+ -0.000299072480303190179733L, -0.146384525788434181781e-5L,
+ 0.664149821546512218666e-4L, -0.396836504717943466443e-4L,
+ 0.113757269706784190981e-4L, 0.250749722623753280165e-9L,
+ -0.169541495365583060147e-5L, 0.890750753220530968883e-6L,
+ -0.229293483400080487057e-6L ],
+ [ -0.000336798553366358150309L, -0.697281375836585777429e-4L,
+ 0.000277275324495939207873L, -0.000199325705161888477003L,
+ 0.679778047793720783882e-4L, 0.141906292064396701483e-6L,
+ -0.135940481897686932785e-4L, 0.801847025633420153972e-5L,
+ -0.229148117650809517038e-5L ],
+ [ 0.000531307936463992223166L, -0.000592166437353693882865L,
+ 0.000270878209671804482771L, 0.790235323266032787212e-6L,
+ -0.815396936756196875093e-4L, 0.561168275310624965004e-4L,
+ -0.183291165828433755673e-4L, -0.307961345060330478256e-8L,
+ 0.346515536880360908674e-5L, -0.20291327396058603727e-5L,
+ 0.57887928631490037089e-6L ],
+ [ 0.000344367606892377671254L, 0.517179090826059219337e-4L,
+ -0.000334931610811422363117L, 0.000281269515476323702274L,
+ -0.000109765822446847310235L, -0.127410090954844853795e-6L,
+ 0.277444515115636441571e-4L, -0.182634888057113326614e-4L,
+ 0.578769494973505239894e-5L ],
+ [ -0.000652623918595309418922L, 0.000839498720672087279993L,
+ -0.000438297098541721005061L, -0.696909145842055197137e-6L,
+ 0.000166448466420675478374L, -0.000127835176797692185853L,
+ 0.462995326369130429061e-4L ],
+ [ -0.000596761290192746250124L, -0.720489541602001055909e-4L,
+ 0.000678230883766732836162L, -0.0006401475260262758451L,
+ 0.000277501076343287044992L ],
+ [ 0.00133244544948006563713L, -0.0019144384985654775265L,
+ 0.00110893691345966373396L ],
+ [ 0.00157972766073083495909L, 0.000162516262783915816899L,
+ -0.00206334210355432762645L, 0.00213896861856890981541L,
+ -0.00101085593912630031708L ],
+ [ -0.00407251211951401664727L, 0.00640336283380806979482L,
+ -0.00404101610816766177474L ]
];
// avoid nans when one of the arguments is inf:
@@ -492,7 +493,7 @@ real logGamma(real x)
}
while ( x < 2.0L )
{
- if ( fabs(x) <= 0.03125 )
+ if ( fabs(x) <= 0.03125L )
{
if ( x == 0.0L )
return real.infinity;
@@ -568,9 +569,9 @@ real logGamma(real x)
assert( feqrel(log(fabs(gamma(testpoints[i]))), testpoints[i+1]) > real.mant_dig-5);
}
}
- assert(logGamma(-50.2) == log(fabs(gamma(-50.2))));
- assert(logGamma(-0.008) == log(fabs(gamma(-0.008))));
- assert(feqrel(logGamma(-38.8),log(fabs(gamma(-38.8)))) > real.mant_dig-4);
+ assert(feqrel(logGamma(-50.2L),log(fabs(gamma(-50.2L)))) > real.mant_dig-2);
+ assert(feqrel(logGamma(-0.008L),log(fabs(gamma(-0.008L)))) > real.mant_dig-2);
+ assert(feqrel(logGamma(-38.8L),log(fabs(gamma(-38.8L)))) > real.mant_dig-4);
static if (real.mant_dig >= 64) // incl. 80-bit reals
assert(feqrel(logGamma(1500.0L),log(gamma(1500.0L))) > real.mant_dig-2);
else static if (real.mant_dig >= 53) // incl. 64-bit reals
@@ -597,8 +598,8 @@ private {
*/
static if (floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
{
- enum real MAXLOG = 0x1.62e42fefa39ef35793c7673007e6p+13; // log(real.max)
- enum real MINLOG = -0x1.6546282207802c89d24d65e96274p+13; // log(real.min_normal*real.epsilon) = log(smallest denormal)
+ enum real MAXLOG = 0x1.62e42fefa39ef35793c7673007e6p+13L; // log(real.max)
+ enum real MINLOG = -0x1.6546282207802c89d24d65e96274p+13L; // log(real.min_normal*real.epsilon) = log(smallest denormal)
}
else static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
{
@@ -1039,17 +1040,17 @@ done:
// Test against Mathematica betaRegularized[z,a,b]
// These arbitrary points are chosen to give good code coverage.
- assert(feqrel(betaIncomplete(8, 10, 0.2), 0.010_934_315_234_099_2L) >= real.mant_dig - 5);
- assert(feqrel(betaIncomplete(2, 2.5, 0.9), 0.989_722_597_604_452_767_171_003_59L) >= real.mant_dig - 1);
+ assert(feqrel(betaIncomplete(8, 10, 0.2L), 0.010_934_315_234_099_2L) >= real.mant_dig - 5);
+ assert(feqrel(betaIncomplete(2, 2.5L, 0.9L), 0.989_722_597_604_452_767_171_003_59L) >= real.mant_dig - 1);
static if (real.mant_dig >= 64) // incl. 80-bit reals
- assert(feqrel(betaIncomplete(1000, 800, 0.5), 1.179140859734704555102808541457164E-06L) >= real.mant_dig - 13);
+ assert(feqrel(betaIncomplete(1000, 800, 0.5L), 1.179140859734704555102808541457164E-06L) >= real.mant_dig - 13);
else
- assert(feqrel(betaIncomplete(1000, 800, 0.5), 1.179140859734704555102808541457164E-06L) >= real.mant_dig - 14);
- assert(feqrel(betaIncomplete(0.0001, 10000, 0.0001), 0.999978059362107134278786L) >= real.mant_dig - 18);
- assert(betaIncomplete(0.01, 327726.7, 0.545113) == 1.0);
+ assert(feqrel(betaIncomplete(1000, 800, 0.5L), 1.179140859734704555102808541457164E-06L) >= real.mant_dig - 14);
+ assert(feqrel(betaIncomplete(0.0001, 10000, 0.0001L), 0.999978059362107134278786L) >= real.mant_dig - 18);
+ assert(betaIncomplete(0.01L, 327726.7L, 0.545113L) == 1.0);
assert(feqrel(betaIncompleteInv(8, 10, 0.010_934_315_234_099_2L), 0.2L) >= real.mant_dig - 2);
- assert(feqrel(betaIncomplete(0.01, 498.437, 0.0121433), 0.99999664562033077636065L) >= real.mant_dig - 1);
- assert(feqrel(betaIncompleteInv(5, 10, 0.2000002972865658842), 0.229121208190918L) >= real.mant_dig - 3);
+ assert(feqrel(betaIncomplete(0.01L, 498.437L, 0.0121433L), 0.99999664562033077636065L) >= real.mant_dig - 1);
+ assert(feqrel(betaIncompleteInv(5, 10, 0.2000002972865658842L), 0.229121208190918L) >= real.mant_dig - 3);
assert(feqrel(betaIncompleteInv(4, 7, 0.8000002209179505L), 0.483657360076904L) >= real.mant_dig - 3);
// Coverage tests. I don't have correct values for these tests, but
@@ -1060,32 +1061,32 @@ done:
// significant improvement over the original CEPHES code.
static if (real.mant_dig == 64) // 80-bit reals
{
- assert(betaIncompleteInv(0.01, 8e-48, 5.45464e-20) == 1-real.epsilon);
- assert(betaIncompleteInv(0.01, 8e-48, 9e-26) == 1-real.epsilon);
+ assert(betaIncompleteInv(0.01L, 8e-48L, 5.45464e-20L) == 1-real.epsilon);
+ assert(betaIncompleteInv(0.01L, 8e-48L, 9e-26L) == 1-real.epsilon);
// Beware: a one-bit change in pow() changes almost all digits in the result!
assert(feqrel(
- betaIncompleteInv(0x1.b3d151fbba0eb18p+1, 1.2265e-19, 2.44859e-18),
+ betaIncompleteInv(0x1.b3d151fbba0eb18p+1L, 1.2265e-19L, 2.44859e-18L),
0x1.c0110c8531d0952cp-1L
) > 10);
// This next case uncovered a one-bit difference in the FYL2X instruction
// between Intel and AMD processors. This difference gets magnified by 2^^38.
// WolframAlpha crashes attempting to calculate this.
- assert(feqrel(betaIncompleteInv(0x1.ff1275ae5b939bcap-41, 4.6713e18, 0.0813601),
+ assert(feqrel(betaIncompleteInv(0x1.ff1275ae5b939bcap-41L, 4.6713e18L, 0.0813601L),
0x1.f97749d90c7adba8p-63L) >= real.mant_dig - 39);
- real a1 = 3.40483;
- assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113) == 0x1.ba8c08108aaf5d14p-109);
- real b1 = 2.82847e-25;
- assert(feqrel(betaIncompleteInv(0.01, b1, 9e-26), 0x1.549696104490aa9p-830L) >= real.mant_dig-10);
+ real a1 = 3.40483L;
+ assert(betaIncompleteInv(a1, 4.0640301659679627772e19L, 0.545113L) == 0x1.ba8c08108aaf5d14p-109L);
+ real b1 = 2.82847e-25L;
+ assert(feqrel(betaIncompleteInv(0.01L, b1, 9e-26L), 0x1.549696104490aa9p-830L) >= real.mant_dig-10);
// --- Problematic cases ---
// This is a situation where the series expansion fails to converge
- assert( isNaN(betaIncompleteInv(0.12167, 4.0640301659679627772e19L, 0.0813601)));
+ assert( isNaN(betaIncompleteInv(0.12167L, 4.0640301659679627772e19L, 0.0813601L)));
// This next result is almost certainly erroneous.
// Mathematica states: "(cannot be determined by current methods)"
- assert(betaIncomplete(1.16251e20, 2.18e39, 5.45e-20) == -real.infinity);
+ assert(betaIncomplete(1.16251e20L, 2.18e39L, 5.45e-20L) == -real.infinity);
// WolframAlpha gives no result for this, though indicates that it approximately 1.0 - 1.3e-9
- assert(1 - betaIncomplete(0.01, 328222, 4.0375e-5) == 0x1.5f62926b4p-30);
+ assert(1 - betaIncomplete(0.01L, 328222, 4.0375e-5L) == 0x1.5f62926b4p-30L);
}
}
@@ -1326,11 +1327,13 @@ real betaDistPowerSeries(real a, real b, real x )
* values of a and x.
*/
real gammaIncomplete(real a, real x )
-in {
+in
+{
assert(x >= 0);
assert(a > 0);
}
-body {
+do
+{
/* left tail of incomplete gamma function:
*
* inf. k
@@ -1370,11 +1373,13 @@ body {
/** ditto */
real gammaIncompleteCompl(real a, real x )
-in {
+in
+{
assert(x >= 0);
assert(a > 0);
}
-body {
+do
+{
if (x == 0)
return 1.0L;
if ( (x < 1.0L) || (x < a) )
@@ -1456,11 +1461,13 @@ body {
* root of incompleteGammaCompl(a,x) - p = 0.
*/
real gammaIncompleteComplInv(real a, real p)
-in {
+in
+{
assert(p >= 0 && p <= 1);
assert(a>0);
}
-body {
+do
+{
if (p == 0) return real.infinity;
real y0 = p;
@@ -1585,17 +1592,17 @@ ihalve:
@safe unittest
{
//Values from Excel's GammaInv(1-p, x, 1)
-assert(fabs(gammaIncompleteComplInv(1, 0.5) - 0.693147188044814) < 0.00000005);
-assert(fabs(gammaIncompleteComplInv(12, 0.99) - 5.42818075054289) < 0.00000005);
-assert(fabs(gammaIncompleteComplInv(100, 0.8) - 91.5013985848288L) < 0.000005);
+assert(fabs(gammaIncompleteComplInv(1, 0.5L) - 0.693147188044814L) < 0.00000005L);
+assert(fabs(gammaIncompleteComplInv(12, 0.99L) - 5.42818075054289L) < 0.00000005L);
+assert(fabs(gammaIncompleteComplInv(100, 0.8L) - 91.5013985848288L) < 0.000005L);
assert(gammaIncomplete(1, 0)==0);
assert(gammaIncompleteCompl(1, 0)==1);
assert(gammaIncomplete(4545, real.infinity)==1);
// Values from Excel's (1-GammaDist(x, alpha, 1, TRUE))
-assert(fabs(1.0L-gammaIncompleteCompl(0.5, 2) - 0.954499729507309L) < 0.00000005);
-assert(fabs(gammaIncomplete(0.5, 2) - 0.954499729507309L) < 0.00000005);
+assert(fabs(1.0L-gammaIncompleteCompl(0.5L, 2) - 0.954499729507309L) < 0.00000005L);
+assert(fabs(gammaIncomplete(0.5L, 2) - 0.954499729507309L) < 0.00000005L);
// Fixed Cephes bug:
assert(gammaIncompleteCompl(384, real.infinity)==0);
assert(gammaIncompleteComplInv(3, 0)==real.infinity);
@@ -1603,9 +1610,9 @@ assert(gammaIncompleteComplInv(3, 0)==real.infinity);
// x was larger than a, but not by much, and both were large:
// The value is from WolframAlpha (Gamma[100000, 100001, inf] / Gamma[100000])
static if (real.mant_dig >= 64) // incl. 80-bit reals
- assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109) < 0.000000000005);
+ assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109L) < 0.000000000005L);
else
- assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109) < 0.00000005);
+ assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109L) < 0.00000005L);
}
@@ -1688,7 +1695,7 @@ real digamma(real x)
s += 1.0;
}
- if ( s < 1.0e17 )
+ if ( s < 1.0e17L )
{
z = 1.0/(s * s);
y = z * poly(z, Bn_n);
@@ -1755,7 +1762,7 @@ real logmdigamma(real x)
}
real y;
- if ( s < 1.0e17 )
+ if ( s < 1.0e17L )
{
immutable real z = 1.0/(s * s);
y = z * poly(z, Bn_n);
@@ -1771,9 +1778,9 @@ real logmdigamma(real x)
assert(isIdentical(logmdigamma(NaN(0xABC)), NaN(0xABC)));
assert(logmdigamma(0.0) == real.infinity);
for (auto x = 0.01; x < 1.0; x += 0.1)
- assert(approxEqual(digamma(x), log(x) - logmdigamma(x)));
+ assert(isClose(digamma(x), log(x) - logmdigamma(x)));
for (auto x = 1.0; x < 15.0; x += 1.0)
- assert(approxEqual(digamma(x), log(x) - logmdigamma(x)));
+ assert(isClose(digamma(x), log(x) - logmdigamma(x)));
}
/** Inverse of the Log Minus Digamma function
@@ -1830,12 +1837,12 @@ real logmdigammaInverse(real y)
tuple(1017.644138623741168814449776695062817947092468536L, 1.0L/1024),
];
foreach (test; testData)
- assert(approxEqual(logmdigammaInverse(test[0]), test[1], 2e-15, 0));
-
- assert(approxEqual(logmdigamma(logmdigammaInverse(1)), 1, 1e-15, 0));
- assert(approxEqual(logmdigamma(logmdigammaInverse(real.min_normal)), real.min_normal, 1e-15, 0));
- assert(approxEqual(logmdigamma(logmdigammaInverse(real.max/2)), real.max/2, 1e-15, 0));
- assert(approxEqual(logmdigammaInverse(logmdigamma(1)), 1, 1e-15, 0));
- assert(approxEqual(logmdigammaInverse(logmdigamma(real.min_normal)), real.min_normal, 1e-15, 0));
- assert(approxEqual(logmdigammaInverse(logmdigamma(real.max/2)), real.max/2, 1e-15, 0));
+ assert(isClose(logmdigammaInverse(test[0]), test[1], 2e-15L));
+
+ assert(isClose(logmdigamma(logmdigammaInverse(1)), 1, 1e-15L));
+ assert(isClose(logmdigamma(logmdigammaInverse(real.min_normal)), real.min_normal, 1e-15L));
+ assert(isClose(logmdigamma(logmdigammaInverse(real.max/2)), real.max/2, 1e-15L));
+ assert(isClose(logmdigammaInverse(logmdigamma(1)), 1, 1e-15L));
+ assert(isClose(logmdigammaInverse(logmdigamma(real.min_normal)), real.min_normal, 1e-15L));
+ assert(isClose(logmdigammaInverse(logmdigamma(real.max/2)), real.max/2, 1e-15L));
}
diff --git a/libphobos/src/std/internal/memory.d b/libphobos/src/std/internal/memory.d
new file mode 100644
index 00000000000..991cd685b73
--- /dev/null
+++ b/libphobos/src/std/internal/memory.d
@@ -0,0 +1,58 @@
+module std.internal.memory;
+
+package(std):
+
+version (D_Exceptions)
+{
+ import core.exception : onOutOfMemoryError;
+ private enum allocationFailed = `onOutOfMemoryError();`;
+}
+else
+{
+ private enum allocationFailed = `assert(0, "Memory allocation failed");`;
+}
+
+// (below comments are non-DDOC, but are written in similar style)
+
+/+
+Mnemonic for `enforce!OutOfMemoryError(malloc(size))` that (unlike malloc)
+can be considered pure because it causes the program to abort if the result
+of the allocation is null, with the consequence that errno will not be
+visibly changed by calling this function. Note that `malloc` can also
+return `null` in non-failure situations if given an argument of 0. Hence,
+it is a programmer error to use this function if the requested allocation
+size is logically permitted to be zero. `enforceCalloc` and `enforceRealloc`
+work analogously.
+
+All these functions are usable in `betterC`.
++/
+void* enforceMalloc()(size_t size) @nogc nothrow pure @safe
+{
+ auto result = fakePureMalloc(size);
+ if (!result) mixin(allocationFailed);
+ return result;
+}
+
+// ditto
+void* enforceCalloc()(size_t nmemb, size_t size) @nogc nothrow pure @safe
+{
+ auto result = fakePureCalloc(nmemb, size);
+ if (!result) mixin(allocationFailed);
+ return result;
+}
+
+// ditto
+void* enforceRealloc()(return scope void* ptr, size_t size) @nogc nothrow pure @system
+{
+ auto result = fakePureRealloc(ptr, size);
+ if (!result) mixin(allocationFailed);
+ return result;
+}
+
+// Purified for local use only.
+extern (C) @nogc nothrow pure private
+{
+ pragma(mangle, "malloc") void* fakePureMalloc(size_t) @safe;
+ pragma(mangle, "calloc") void* fakePureCalloc(size_t nmemb, size_t size) @safe;
+ pragma(mangle, "realloc") void* fakePureRealloc(return scope void* ptr, size_t size) @system;
+}
diff --git a/libphobos/src/std/internal/scopebuffer.d b/libphobos/src/std/internal/scopebuffer.d
index 70a7c8d1202..7c1f8c08d96 100644
--- a/libphobos/src/std/internal/scopebuffer.d
+++ b/libphobos/src/std/internal/scopebuffer.d
@@ -2,7 +2,7 @@
* Copyright: 2014 by Digital Mars
* License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright
- * Source: $(PHOBOSSRC std/internal/_scopebuffer.d)
+ * Source: $(PHOBOSSRC std/internal/scopebuffer.d)
*/
module std.internal.scopebuffer;
@@ -12,6 +12,7 @@ module std.internal.scopebuffer;
import core.stdc.stdlib : realloc;
import std.traits;
+import std.internal.attributes : betterC;
/**************************************
* ScopeBuffer encapsulates using a local array as a temporary buffer.
@@ -74,11 +75,11 @@ string cat(string s1, string s2)
return textbuf[].idup;
}
---
- * ScopeBuffer is intended for high performance usages in $(D @system) and $(D @trusted) code.
+ * ScopeBuffer is intended for high performance usages in `@system` and `@trusted` code.
* It is designed to fit into two 64 bit registers, again for high performance use.
* If used incorrectly, memory leaks and corruption can result. Be sure to use
* $(D scope(exit) textbuf.free();) for proper cleanup, and do not refer to a ScopeBuffer
- * instance's contents after $(D ScopeBuffer.free()) has been called.
+ * instance's contents after `ScopeBuffer.free()` has been called.
*
* The `realloc` parameter defaults to C's `realloc()`. Another can be supplied to override it.
*
@@ -122,13 +123,13 @@ if (isAssignable!T &&
assert(!(buf.length & wasResized)); // assure even length of scratch buffer space
assert(buf.length <= uint.max); // because we cast to uint later
}
- body
+ do
{
this.buf = buf.ptr;
this.bufLen = cast(uint) buf.length;
}
- @system unittest
+ @system @betterC unittest
{
ubyte[10] tmpbuf = void;
auto sbuf = ScopeBuffer!ubyte(tmpbuf);
@@ -171,8 +172,8 @@ if (isAssignable!T &&
/************************
* Append array s to the buffer.
*
- * If $(D const(T)) can be converted to $(D T), then put will accept
- * $(D const(T)[]) as input. It will accept a $(D T[]) otherwise.
+ * If `const(T)` can be converted to `T`, then put will accept
+ * `const(T)[]` as input. It will accept a `T[]` otherwise.
*/
package alias CT = Select!(is(const(T) : T), const(T), T);
/// ditto
@@ -203,7 +204,7 @@ if (isAssignable!T &&
assert(upper <= bufLen);
assert(lower <= upper);
}
- body
+ do
{
return buf[lower .. upper];
}
@@ -244,7 +245,7 @@ if (isAssignable!T &&
{
assert(i <= this.used);
}
- body
+ do
{
this.used = cast(uint) i;
}
@@ -263,7 +264,7 @@ if (isAssignable!T &&
{
assert(newsize <= uint.max);
}
- body
+ do
{
//writefln("%s: oldsize %s newsize %s", id, buf.length, newsize);
newsize |= wasResized;
@@ -289,7 +290,7 @@ if (isAssignable!T &&
}
}
-@system unittest
+@system @betterC unittest
{
import core.stdc.stdio;
import std.range;
@@ -350,7 +351,7 @@ if (isAssignable!T &&
}
// const
-@system unittest
+@system @betterC unittest
{
char[10] tmpbuf = void;
auto textbuf = ScopeBuffer!char(tmpbuf);
@@ -377,14 +378,14 @@ auto scopeBuffer(T)(T[] tmpbuf)
}
///
-@system unittest
+@system @betterC unittest
{
ubyte[10] tmpbuf = void;
auto sb = scopeBuffer(tmpbuf);
scope(exit) sb.free();
}
-@system unittest
+@system @betterC unittest
{
ScopeBuffer!(int*) b;
int*[] s;
diff --git a/libphobos/src/std/internal/test/dummyrange.d b/libphobos/src/std/internal/test/dummyrange.d
index a6bce0ada23..e07e2751021 100644
--- a/libphobos/src/std/internal/test/dummyrange.d
+++ b/libphobos/src/std/internal/test/dummyrange.d
@@ -340,7 +340,7 @@ if (is(T == uint))
pure struct Cmp(T)
if (is(T == double))
{
- import std.math : approxEqual;
+ import std.math.operations : isClose;
static auto iota(size_t low = 1, size_t high = 11)
{
@@ -354,7 +354,7 @@ if (is(T == double))
arr = iota().array;
}
- alias cmp = approxEqual!(double,double);
+ alias cmp = isClose!(double,double,double);
enum dummyValue = 1337.0;
enum dummyValueRslt = 1337.0 * 2.0;
@@ -386,8 +386,6 @@ struct TestFoo
pure struct Cmp(T)
if (is(T == TestFoo))
{
- import std.math : approxEqual;
-
static auto iota(size_t low = 1, size_t high = 11)
{
import std.algorithm.iteration : map;
@@ -421,7 +419,6 @@ if (is(T == TestFoo))
{
import std.algorithm.comparison : equal;
import std.range : iota, retro, repeat;
- import std.traits : Unqual;
static void testInputRange(T,Cmp)()
{
@@ -431,7 +428,7 @@ if (is(T == TestFoo))
{
if (numRuns == 1)
{
- static if (is(Unqual!(ElementType!(T)) == uint))
+ static if (is(immutable ElementType!(T) == immutable uint))
{
it.reinit();
}
@@ -540,7 +537,7 @@ if (is(T == TestFoo))
import std.meta : AliasSeq;
- foreach (S; AliasSeq!(uint, double, TestFoo))
+ static foreach (S; AliasSeq!(uint, double, TestFoo))
{
foreach (T; AllDummyRangesType!(S[]))
{
diff --git a/libphobos/src/std/internal/windows/advapi32.d b/libphobos/src/std/internal/windows/advapi32.d
index 9ed67629df0..2220eec63ec 100644
--- a/libphobos/src/std/internal/windows/advapi32.d
+++ b/libphobos/src/std/internal/windows/advapi32.d
@@ -6,13 +6,13 @@
*
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Kenji Hara
- * Source: $(PHOBOSSRC std/internal/windows/_advapi32.d)
+ * Source: $(PHOBOSSRC std/internal/windows/advapi32.d)
*/
module std.internal.windows.advapi32;
version (Windows):
-import core.sys.windows.windows;
+import core.sys.windows.winbase, core.sys.windows.winnt, core.sys.windows.winreg;
pragma(lib, "advapi32.lib");
diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d
index 8ba0f05f782..39f89a60c04 100644
--- a/libphobos/src/std/json.d
+++ b/libphobos/src/std/json.d
@@ -6,8 +6,8 @@ JavaScript Object Notation
Copyright: Copyright Jeremie Pelletier 2008 - 2009.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Jeremie Pelletier, David Herberth
-References: $(LINK http://json.org/)
-Source: $(PHOBOSSRC std/_json.d)
+References: $(LINK http://json.org/), $(LINK http://seriot.ch/parsing_json.html)
+Source: $(PHOBOSSRC std/json.d)
*/
/*
Copyright Jeremie Pelletier 2008 - 2009.
@@ -39,7 +39,7 @@ import std.traits;
long x;
if (const(JSONValue)* code = "code" in j)
{
- if (code.type() == JSON_TYPE.INTEGER)
+ if (code.type() == JSONType.integer)
x = code.integer;
else
x = to!int(code.str);
@@ -77,31 +77,47 @@ enum JSONOptions
specialFloatLiterals = 0x1, /// encode NaN and Inf float values as strings
escapeNonAsciiChars = 0x2, /// encode non ascii characters with an unicode escape sequence
doNotEscapeSlashes = 0x4, /// do not escape slashes ('/')
+ strictParsing = 0x8, /// Strictly follow RFC-8259 grammar when parsing
}
/**
JSON type enumeration
*/
-enum JSON_TYPE : byte
+enum JSONType : byte
{
- /// Indicates the type of a $(D JSONValue).
- NULL,
- STRING, /// ditto
- INTEGER, /// ditto
- UINTEGER,/// ditto
- FLOAT, /// ditto
- OBJECT, /// ditto
- ARRAY, /// ditto
- TRUE, /// ditto
- FALSE /// ditto
+ /// Indicates the type of a `JSONValue`.
+ null_,
+ string, /// ditto
+ integer, /// ditto
+ uinteger, /// ditto
+ float_, /// ditto
+ array, /// ditto
+ object, /// ditto
+ true_, /// ditto
+ false_, /// ditto
+ // FIXME: Find some way to deprecate the enum members below, which does NOT
+ // create lots of spam-like deprecation warnings, which can't be fixed
+ // by the user. See discussion on this issue at
+ // https://forum.dlang.org/post/feudrhtxkaxxscwhhhff@forum.dlang.org
+ /* deprecated("Use .null_") */ NULL = null_,
+ /* deprecated("Use .string") */ STRING = string,
+ /* deprecated("Use .integer") */ INTEGER = integer,
+ /* deprecated("Use .uinteger") */ UINTEGER = uinteger,
+ /* deprecated("Use .float_") */ FLOAT = float_,
+ /* deprecated("Use .array") */ ARRAY = array,
+ /* deprecated("Use .object") */ OBJECT = object,
+ /* deprecated("Use .true_") */ TRUE = true_,
+ /* deprecated("Use .false_") */ FALSE = false_,
}
+deprecated("Use JSONType and the new enum member names") alias JSON_TYPE = JSONType;
+
/**
JSON value node
*/
struct JSONValue
{
- import std.exception : enforceEx, enforce;
+ import std.exception : enforce;
union Store
{
@@ -113,12 +129,12 @@ struct JSONValue
JSONValue[] array;
}
private Store store;
- private JSON_TYPE type_tag;
+ private JSONType type_tag;
/**
- Returns the JSON_TYPE of the value stored in this structure.
+ Returns the JSONType of the value stored in this structure.
*/
- @property JSON_TYPE type() const pure nothrow @safe @nogc
+ @property JSONType type() const pure nothrow @safe @nogc
{
return type_tag;
}
@@ -127,23 +143,23 @@ struct JSONValue
{
string s = "{ \"language\": \"D\" }";
JSONValue j = parseJSON(s);
- assert(j.type == JSON_TYPE.OBJECT);
- assert(j["language"].type == JSON_TYPE.STRING);
+ assert(j.type == JSONType.object);
+ assert(j["language"].type == JSONType.string);
}
/***
- * Value getter/setter for $(D JSON_TYPE.STRING).
- * Throws: $(D JSONException) for read access if $(D type) is not
- * $(D JSON_TYPE.STRING).
+ * Value getter/setter for `JSONType.string`.
+ * Throws: `JSONException` for read access if `type` is not
+ * `JSONType.string`.
*/
- @property string str() const pure @trusted
+ @property string str() const pure @trusted return scope
{
- enforce!JSONException(type == JSON_TYPE.STRING,
+ enforce!JSONException(type == JSONType.string,
"JSONValue is not a string");
return store.str;
}
/// ditto
- @property string str(string v) pure nothrow @nogc @safe
+ @property string str(return string v) pure nothrow @nogc @trusted return // TODO make @safe
{
assign(v);
return v;
@@ -162,13 +178,13 @@ struct JSONValue
}
/***
- * Value getter/setter for $(D JSON_TYPE.INTEGER).
- * Throws: $(D JSONException) for read access if $(D type) is not
- * $(D JSON_TYPE.INTEGER).
+ * Value getter/setter for `JSONType.integer`.
+ * Throws: `JSONException` for read access if `type` is not
+ * `JSONType.integer`.
*/
- @property inout(long) integer() inout pure @safe
+ @property long integer() const pure @safe
{
- enforce!JSONException(type == JSON_TYPE.INTEGER,
+ enforce!JSONException(type == JSONType.integer,
"JSONValue is not an integer");
return store.integer;
}
@@ -180,13 +196,13 @@ struct JSONValue
}
/***
- * Value getter/setter for $(D JSON_TYPE.UINTEGER).
- * Throws: $(D JSONException) for read access if $(D type) is not
- * $(D JSON_TYPE.UINTEGER).
+ * Value getter/setter for `JSONType.uinteger`.
+ * Throws: `JSONException` for read access if `type` is not
+ * `JSONType.uinteger`.
*/
- @property inout(ulong) uinteger() inout pure @safe
+ @property ulong uinteger() const pure @safe
{
- enforce!JSONException(type == JSON_TYPE.UINTEGER,
+ enforce!JSONException(type == JSONType.uinteger,
"JSONValue is not an unsigned integer");
return store.uinteger;
}
@@ -198,14 +214,14 @@ struct JSONValue
}
/***
- * Value getter/setter for $(D JSON_TYPE.FLOAT). Note that despite
+ * Value getter/setter for `JSONType.float_`. Note that despite
* the name, this is a $(B 64)-bit `double`, not a 32-bit `float`.
- * Throws: $(D JSONException) for read access if $(D type) is not
- * $(D JSON_TYPE.FLOAT).
+ * Throws: `JSONException` for read access if `type` is not
+ * `JSONType.float_`.
*/
- @property inout(double) floating() inout pure @safe
+ @property double floating() const pure @safe
{
- enforce!JSONException(type == JSON_TYPE.FLOAT,
+ enforce!JSONException(type == JSONType.float_,
"JSONValue is not a floating type");
return store.floating;
}
@@ -217,9 +233,41 @@ struct JSONValue
}
/***
- * Value getter/setter for $(D JSON_TYPE.OBJECT).
- * Throws: $(D JSONException) for read access if $(D type) is not
- * $(D JSON_TYPE.OBJECT).
+ * Value getter/setter for boolean stored in JSON.
+ * Throws: `JSONException` for read access if `this.type` is not
+ * `JSONType.true_` or `JSONType.false_`.
+ */
+ @property bool boolean() const pure @safe
+ {
+ if (type == JSONType.true_) return true;
+ if (type == JSONType.false_) return false;
+
+ throw new JSONException("JSONValue is not a boolean type");
+ }
+ /// ditto
+ @property bool boolean(bool v) pure nothrow @safe @nogc
+ {
+ assign(v);
+ return v;
+ }
+ ///
+ @safe unittest
+ {
+ JSONValue j = true;
+ assert(j.boolean == true);
+
+ j.boolean = false;
+ assert(j.boolean == false);
+
+ j.integer = 12;
+ import std.exception : assertThrown;
+ assertThrown!JSONException(j.boolean);
+ }
+
+ /***
+ * Value getter/setter for `JSONType.object`.
+ * Throws: `JSONException` for read access if `type` is not
+ * `JSONType.object`.
* Note: this is @system because of the following pattern:
---
auto a = &(json.object());
@@ -227,22 +275,22 @@ struct JSONValue
(*a)["hello"] = "world"; // segmentation fault
---
*/
- @property ref inout(JSONValue[string]) object() inout pure @system
+ @property ref inout(JSONValue[string]) object() inout pure @system return
{
- enforce!JSONException(type == JSON_TYPE.OBJECT,
+ enforce!JSONException(type == JSONType.object,
"JSONValue is not an object");
return store.object;
}
/// ditto
- @property JSONValue[string] object(JSONValue[string] v) pure nothrow @nogc @safe
+ @property JSONValue[string] object(return JSONValue[string] v) pure nothrow @nogc @trusted // TODO make @safe
{
assign(v);
return v;
}
/***
- * Value getter for $(D JSON_TYPE.OBJECT).
- * Unlike $(D object), this retrieves the object by value and can be used in @safe code.
+ * Value getter for `JSONType.object`.
+ * Unlike `object`, this retrieves the object by value and can be used in @safe code.
*
* A caveat is that, if the returned value is null, modifications will not be visible:
* ---
@@ -252,20 +300,20 @@ struct JSONValue
* assert("hello" !in json.object);
* ---
*
- * Throws: $(D JSONException) for read access if $(D type) is not
- * $(D JSON_TYPE.OBJECT).
+ * Throws: `JSONException` for read access if `type` is not
+ * `JSONType.object`.
*/
@property inout(JSONValue[string]) objectNoRef() inout pure @trusted
{
- enforce!JSONException(type == JSON_TYPE.OBJECT,
+ enforce!JSONException(type == JSONType.object,
"JSONValue is not an object");
return store.object;
}
/***
- * Value getter/setter for $(D JSON_TYPE.ARRAY).
- * Throws: $(D JSONException) for read access if $(D type) is not
- * $(D JSON_TYPE.ARRAY).
+ * Value getter/setter for `JSONType.array`.
+ * Throws: `JSONException` for read access if `type` is not
+ * `JSONType.array`.
* Note: this is @system because of the following pattern:
---
auto a = &(json.array());
@@ -275,20 +323,20 @@ struct JSONValue
*/
@property ref inout(JSONValue[]) array() inout pure @system
{
- enforce!JSONException(type == JSON_TYPE.ARRAY,
+ enforce!JSONException(type == JSONType.array,
"JSONValue is not an array");
return store.array;
}
/// ditto
- @property JSONValue[] array(JSONValue[] v) pure nothrow @nogc @safe
+ @property JSONValue[] array(return JSONValue[] v) pure nothrow @nogc @trusted scope // TODO make @safe
{
assign(v);
return v;
}
/***
- * Value getter for $(D JSON_TYPE.ARRAY).
- * Unlike $(D array), this retrieves the array by value and can be used in @safe code.
+ * Value getter for `JSONType.array`.
+ * Unlike `array`, this retrieves the array by value and can be used in @safe code.
*
* A caveat is that, if you append to the returned array, the new values aren't visible in the
* JSONValue:
@@ -299,38 +347,137 @@ struct JSONValue
* assert(json.array.length == 1);
* ---
*
- * Throws: $(D JSONException) for read access if $(D type) is not
- * $(D JSON_TYPE.ARRAY).
+ * Throws: `JSONException` for read access if `type` is not
+ * `JSONType.array`.
*/
@property inout(JSONValue[]) arrayNoRef() inout pure @trusted
{
- enforce!JSONException(type == JSON_TYPE.ARRAY,
+ enforce!JSONException(type == JSONType.array,
"JSONValue is not an array");
return store.array;
}
- /// Test whether the type is $(D JSON_TYPE.NULL)
+ /// Test whether the type is `JSONType.null_`
@property bool isNull() const pure nothrow @safe @nogc
{
- return type == JSON_TYPE.NULL;
+ return type == JSONType.null_;
}
- private void assign(T)(T arg) @safe
+ /***
+ * Generic type value getter
+ * A convenience getter that returns this `JSONValue` as the specified D type.
+ * Note: only numeric, `bool`, `string`, `JSONValue[string]` and `JSONValue[]` types are accepted
+ * Throws: `JSONException` if `T` cannot hold the contents of this `JSONValue`
+ * `ConvException` in case of integer overflow when converting to `T`
+ */
+ @property inout(T) get(T)() inout const pure @safe
+ {
+ static if (is(immutable T == immutable string))
+ {
+ return str;
+ }
+ else static if (is(immutable T == immutable bool))
+ {
+ return boolean;
+ }
+ else static if (isFloatingPoint!T)
+ {
+ switch (type)
+ {
+ case JSONType.float_:
+ return cast(T) floating;
+ case JSONType.uinteger:
+ return cast(T) uinteger;
+ case JSONType.integer:
+ return cast(T) integer;
+ default:
+ throw new JSONException("JSONValue is not a number type");
+ }
+ }
+ else static if (isIntegral!T)
+ {
+ switch (type)
+ {
+ case JSONType.uinteger:
+ return uinteger.to!T;
+ case JSONType.integer:
+ return integer.to!T;
+ default:
+ throw new JSONException("JSONValue is not a an integral type");
+ }
+ }
+ else
+ {
+ static assert(false, "Unsupported type");
+ }
+ }
+ // This specialization is needed because arrayNoRef requires inout
+ @property inout(T) get(T : JSONValue[])() inout pure @trusted /// ditto
+ {
+ return arrayNoRef;
+ }
+ /// ditto
+ @property inout(T) get(T : JSONValue[string])() inout pure @trusted
+ {
+ return object;
+ }
+ ///
+ @safe unittest
+ {
+ import std.exception;
+ import std.conv;
+ string s =
+ `{
+ "a": 123,
+ "b": 3.1415,
+ "c": "text",
+ "d": true,
+ "e": [1, 2, 3],
+ "f": { "a": 1 },
+ "g": -45,
+ "h": ` ~ ulong.max.to!string ~ `,
+ }`;
+
+ struct a { }
+
+ immutable json = parseJSON(s);
+ assert(json["a"].get!double == 123.0);
+ assert(json["a"].get!int == 123);
+ assert(json["a"].get!uint == 123);
+ assert(json["b"].get!double == 3.1415);
+ assertThrown!JSONException(json["b"].get!int);
+ assert(json["c"].get!string == "text");
+ assert(json["d"].get!bool == true);
+ assertNotThrown(json["e"].get!(JSONValue[]));
+ assertNotThrown(json["f"].get!(JSONValue[string]));
+ static assert(!__traits(compiles, json["a"].get!a));
+ assertThrown!JSONException(json["e"].get!float);
+ assertThrown!JSONException(json["d"].get!(JSONValue[string]));
+ assertThrown!JSONException(json["f"].get!(JSONValue[]));
+ assert(json["g"].get!int == -45);
+ assertThrown!ConvException(json["g"].get!uint);
+ assert(json["h"].get!ulong == ulong.max);
+ assertThrown!ConvException(json["h"].get!uint);
+ assertNotThrown(json["h"].get!float);
+ }
+
+ private void assign(T)(T arg)
{
static if (is(T : typeof(null)))
{
- type_tag = JSON_TYPE.NULL;
+ type_tag = JSONType.null_;
}
else static if (is(T : string))
{
- type_tag = JSON_TYPE.STRING;
+ type_tag = JSONType.string;
string t = arg;
() @trusted { store.str = t; }();
}
- else static if (isSomeString!T) // issue 15884
+ // https://issues.dlang.org/show_bug.cgi?id=15884
+ else static if (isSomeString!T)
{
- type_tag = JSON_TYPE.STRING;
- // FIXME: std.array.array(Range) is not deduced as 'pure'
+ type_tag = JSONType.string;
+ // FIXME: std.Array.Array(Range) is not deduced as 'pure'
() @trusted {
import std.utf : byUTF;
store.str = cast(immutable)(arg.byUTF!char.array);
@@ -338,27 +485,27 @@ struct JSONValue
}
else static if (is(T : bool))
{
- type_tag = arg ? JSON_TYPE.TRUE : JSON_TYPE.FALSE;
+ type_tag = arg ? JSONType.true_ : JSONType.false_;
}
else static if (is(T : ulong) && isUnsigned!T)
{
- type_tag = JSON_TYPE.UINTEGER;
+ type_tag = JSONType.uinteger;
store.uinteger = arg;
}
else static if (is(T : long))
{
- type_tag = JSON_TYPE.INTEGER;
+ type_tag = JSONType.integer;
store.integer = arg;
}
else static if (isFloatingPoint!T)
{
- type_tag = JSON_TYPE.FLOAT;
+ type_tag = JSONType.float_;
store.floating = arg;
}
else static if (is(T : Value[Key], Key, Value))
{
static assert(is(Key : string), "AA key must be string");
- type_tag = JSON_TYPE.OBJECT;
+ type_tag = JSONType.object;
static if (is(Value : JSONValue))
{
JSONValue[string] t = arg;
@@ -374,7 +521,7 @@ struct JSONValue
}
else static if (isArray!T)
{
- type_tag = JSON_TYPE.ARRAY;
+ type_tag = JSONType.array;
static if (is(ElementEncodingType!T : JSONValue))
{
JSONValue[] t = arg;
@@ -401,7 +548,7 @@ struct JSONValue
private void assignRef(T)(ref T arg) if (isStaticArray!T)
{
- type_tag = JSON_TYPE.ARRAY;
+ type_tag = JSONType.array;
static if (is(ElementEncodingType!T : JSONValue))
{
store.array = arg;
@@ -416,15 +563,15 @@ struct JSONValue
}
/**
- * Constructor for $(D JSONValue). If $(D arg) is a $(D JSONValue)
- * its value and type will be copied to the new $(D JSONValue).
- * Note that this is a shallow copy: if type is $(D JSON_TYPE.OBJECT)
- * or $(D JSON_TYPE.ARRAY) then only the reference to the data will
+ * Constructor for `JSONValue`. If `arg` is a `JSONValue`
+ * its value and type will be copied to the new `JSONValue`.
+ * Note that this is a shallow copy: if type is `JSONType.object`
+ * or `JSONType.array` then only the reference to the data will
* be copied.
- * Otherwise, $(D arg) must be implicitly convertible to one of the
- * following types: $(D typeof(null)), $(D string), $(D ulong),
- * $(D long), $(D double), an associative array $(D V[K]) for any $(D V)
- * and $(D K) i.e. a JSON object, any array or $(D bool). The type will
+ * Otherwise, `arg` must be implicitly convertible to one of the
+ * following types: `typeof(null)`, `string`, `ulong`,
+ * `long`, `double`, an associative array `V[K]` for any `V`
+ * and `K` i.e. a JSON object, any array or `bool`. The type will
* be set accordingly.
*/
this(T)(T arg) if (!isStaticArray!T)
@@ -449,10 +596,10 @@ struct JSONValue
j = JSONValue(42);
j = JSONValue( [1, 2, 3] );
- assert(j.type == JSON_TYPE.ARRAY);
+ assert(j.type == JSONType.array);
j = JSONValue( ["language": "D"] );
- assert(j.type == JSON_TYPE.OBJECT);
+ assert(j.type == JSONType.object);
}
void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue))
@@ -467,12 +614,12 @@ struct JSONValue
/***
* Array syntax for json arrays.
- * Throws: $(D JSONException) if $(D type) is not $(D JSON_TYPE.ARRAY).
+ * Throws: `JSONException` if `type` is not `JSONType.array`.
*/
ref inout(JSONValue) opIndex(size_t i) inout pure @safe
{
auto a = this.arrayNoRef;
- enforceEx!JSONException(i < a.length,
+ enforce!JSONException(i < a.length,
"JSONValue array index is out of range");
return a[i];
}
@@ -486,9 +633,9 @@ struct JSONValue
/***
* Hash syntax for json objects.
- * Throws: $(D JSONException) if $(D type) is not $(D JSON_TYPE.OBJECT).
+ * Throws: `JSONException` if `type` is not `JSONType.object`.
*/
- ref inout(JSONValue) opIndex(string k) inout pure @safe
+ ref inout(JSONValue) opIndex(return string k) inout pure @safe
{
auto o = this.objectNoRef;
return *enforce!JSONException(k in o,
@@ -502,20 +649,20 @@ struct JSONValue
}
/***
- * Operator sets $(D value) for element of JSON object by $(D key).
+ * Operator sets `value` for element of JSON object by `key`.
*
* If JSON value is null, then operator initializes it with object and then
- * sets $(D value) for it.
+ * sets `value` for it.
*
- * Throws: $(D JSONException) if $(D type) is not $(D JSON_TYPE.OBJECT)
- * or $(D JSON_TYPE.NULL).
+ * Throws: `JSONException` if `type` is not `JSONType.object`
+ * or `JSONType.null_`.
*/
- void opIndexAssign(T)(auto ref T value, string key) pure
+ void opIndexAssign(T)(auto ref T value, string key)
{
- enforceEx!JSONException(type == JSON_TYPE.OBJECT || type == JSON_TYPE.NULL,
+ enforce!JSONException(type == JSONType.object || type == JSONType.null_,
"JSONValue must be object or null");
JSONValue[string] aa = null;
- if (type == JSON_TYPE.OBJECT)
+ if (type == JSONType.object)
{
aa = this.objectNoRef;
}
@@ -531,10 +678,10 @@ struct JSONValue
assert( j["language"].str == "Perl" );
}
- void opIndexAssign(T)(T arg, size_t i) pure
+ void opIndexAssign(T)(T arg, size_t i)
{
auto a = this.arrayNoRef;
- enforceEx!JSONException(i < a.length,
+ enforce!JSONException(i < a.length,
"JSONValue array index is out of range");
a[i] = arg;
this.array = a;
@@ -547,7 +694,7 @@ struct JSONValue
assert( j[1].str == "D" );
}
- JSONValue opBinary(string op : "~", T)(T arg) @safe
+ JSONValue opBinary(string op : "~", T)(T arg)
{
auto a = this.arrayNoRef;
static if (isArray!T)
@@ -564,7 +711,7 @@ struct JSONValue
}
}
- void opOpAssign(string op : "~", T)(T arg) @safe
+ void opOpAssign(string op : "~", T)(T arg)
{
auto a = this.arrayNoRef;
static if (isArray!T)
@@ -583,16 +730,16 @@ struct JSONValue
}
/**
- * Support for the $(D in) operator.
+ * Support for the `in` operator.
*
* Tests wether a key can be found in an object.
*
* Returns:
- * when found, the $(D const(JSONValue)*) that matches to the key,
- * otherwise $(D null).
+ * when found, the `const(JSONValue)*` that matches to the key,
+ * otherwise `null`.
*
- * Throws: $(D JSONException) if the right hand side argument $(D JSON_TYPE)
- * is not $(D OBJECT).
+ * Throws: `JSONException` if the right hand side argument `JSONType`
+ * is not `object`.
*/
auto opBinaryRight(string op : "in")(string k) const @safe
{
@@ -615,30 +762,67 @@ struct JSONValue
// Default doesn't work well since store is a union. Compare only
// what should be in store.
// This is @trusted to remain nogc, nothrow, fast, and usable from @safe code.
- if (type_tag != rhs.type_tag) return false;
final switch (type_tag)
{
- case JSON_TYPE.STRING:
- return store.str == rhs.store.str;
- case JSON_TYPE.INTEGER:
- return store.integer == rhs.store.integer;
- case JSON_TYPE.UINTEGER:
- return store.uinteger == rhs.store.uinteger;
- case JSON_TYPE.FLOAT:
- return store.floating == rhs.store.floating;
- case JSON_TYPE.OBJECT:
- return store.object == rhs.store.object;
- case JSON_TYPE.ARRAY:
- return store.array == rhs.store.array;
- case JSON_TYPE.TRUE:
- case JSON_TYPE.FALSE:
- case JSON_TYPE.NULL:
- return true;
+ case JSONType.integer:
+ switch (rhs.type_tag)
+ {
+ case JSONType.integer:
+ return store.integer == rhs.store.integer;
+ case JSONType.uinteger:
+ return store.integer == rhs.store.uinteger;
+ case JSONType.float_:
+ return store.integer == rhs.store.floating;
+ default:
+ return false;
+ }
+ case JSONType.uinteger:
+ switch (rhs.type_tag)
+ {
+ case JSONType.integer:
+ return store.uinteger == rhs.store.integer;
+ case JSONType.uinteger:
+ return store.uinteger == rhs.store.uinteger;
+ case JSONType.float_:
+ return store.uinteger == rhs.store.floating;
+ default:
+ return false;
+ }
+ case JSONType.float_:
+ switch (rhs.type_tag)
+ {
+ case JSONType.integer:
+ return store.floating == rhs.store.integer;
+ case JSONType.uinteger:
+ return store.floating == rhs.store.uinteger;
+ case JSONType.float_:
+ return store.floating == rhs.store.floating;
+ default:
+ return false;
+ }
+ case JSONType.string:
+ return type_tag == rhs.type_tag && store.str == rhs.store.str;
+ case JSONType.object:
+ return type_tag == rhs.type_tag && store.object == rhs.store.object;
+ case JSONType.array:
+ return type_tag == rhs.type_tag && store.array == rhs.store.array;
+ case JSONType.true_:
+ case JSONType.false_:
+ case JSONType.null_:
+ return type_tag == rhs.type_tag;
}
}
- /// Implements the foreach $(D opApply) interface for json arrays.
+ ///
+ @safe unittest
+ {
+ assert(JSONValue(0u) == JSONValue(0));
+ assert(JSONValue(0u) == JSONValue(0.0));
+ assert(JSONValue(0) == JSONValue(0.0));
+ }
+
+ /// Implements the foreach `opApply` interface for json arrays.
int opApply(scope int delegate(size_t index, ref JSONValue) dg) @system
{
int result;
@@ -653,10 +837,10 @@ struct JSONValue
return result;
}
- /// Implements the foreach $(D opApply) interface for json objects.
+ /// Implements the foreach `opApply` interface for json objects.
int opApply(scope int delegate(string key, ref JSONValue) dg) @system
{
- enforce!JSONException(type == JSON_TYPE.OBJECT,
+ enforce!JSONException(type == JSONType.object,
"JSONValue is not an object");
int result;
@@ -671,7 +855,7 @@ struct JSONValue
}
/***
- * Implicitly calls $(D toJSON) on this JSONValue.
+ * Implicitly calls `toJSON` on this JSONValue.
*
* $(I options) can be used to tweak the conversion behavior.
*/
@@ -680,8 +864,14 @@ struct JSONValue
return toJSON(this, false, options);
}
+ ///
+ void toString(Out)(Out sink, in JSONOptions options = JSONOptions.none) const
+ {
+ toJSON(sink, this, false, options);
+ }
+
/***
- * Implicitly calls $(D toJSON) on this JSONValue, like $(D toString), but
+ * Implicitly calls `toJSON` on this JSONValue, like `toString`, but
* also passes $(I true) as $(I pretty) argument.
*
* $(I options) can be used to tweak the conversion behavior
@@ -690,11 +880,47 @@ struct JSONValue
{
return toJSON(this, true, options);
}
+
+ ///
+ void toPrettyString(Out)(Out sink, in JSONOptions options = JSONOptions.none) const
+ {
+ toJSON(sink, this, true, options);
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20874
+@system unittest
+{
+ static struct MyCustomType
+ {
+ public string toString () const @system { return null; }
+ alias toString this;
+ }
+
+ static struct B
+ {
+ public JSONValue asJSON() const @system { return JSONValue.init; }
+ alias asJSON this;
+ }
+
+ if (false) // Just checking attributes
+ {
+ JSONValue json;
+ MyCustomType ilovedlang;
+ json = ilovedlang;
+ json["foo"] = ilovedlang;
+ auto s = ilovedlang in json;
+
+ B b;
+ json ~= b;
+ json ~ b;
+ }
}
/**
Parses a serialized string and returns a tree of JSON values.
-Throws: $(LREF JSONException) if the depth exceeds the max depth.
+Throws: $(LREF JSONException) if string does not follow the JSON grammar or the depth exceeds the max depth,
+ $(LREF ConvException) if a number in the input cannot be represented by a native D type.
Params:
json = json-formatted string to parse
maxDepth = maximum depth of nesting allowed, -1 disables depth checking
@@ -703,10 +929,10 @@ Params:
JSONValue parseJSON(T)(T json, int maxDepth = -1, JSONOptions options = JSONOptions.none)
if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
{
- import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower;
- import std.typecons : Yes;
+ import std.ascii : isDigit, isHexDigit, toUpper, toLower;
+ import std.typecons : Nullable, Yes;
JSONValue root;
- root.type_tag = JSON_TYPE.NULL;
+ root.type_tag = JSONType.null_;
// Avoid UTF decoding when possible, as it is unnecessary when
// processing JSON.
@@ -715,17 +941,37 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
else
alias Char = Unqual!(ElementType!T);
- if (json.empty) return root;
-
int depth = -1;
- Char next = 0;
+ Nullable!Char next;
int line = 1, pos = 0;
+ immutable bool strict = (options & JSONOptions.strictParsing) != 0;
void error(string msg)
{
throw new JSONException(msg, line, pos);
}
+ if (json.empty)
+ {
+ if (strict)
+ {
+ error("Empty JSON body");
+ }
+ return root;
+ }
+
+ bool isWhite(dchar c)
+ {
+ if (strict)
+ {
+ // RFC 7159 has a stricter definition of whitespace than general ASCII.
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+ }
+ import std.ascii : isWhite;
+ // Accept ASCII NUL as whitespace in non-strict mode.
+ return c == 0 || isWhite(c);
+ }
+
Char popChar()
{
if (json.empty) error("Unexpected end of data.");
@@ -755,17 +1001,35 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
Char peekChar()
{
- if (!next)
+ if (next.isNull)
{
if (json.empty) return '\0';
next = popChar();
}
+ return next.get;
+ }
+
+ Nullable!Char peekCharNullable()
+ {
+ if (next.isNull && !json.empty)
+ {
+ next = popChar();
+ }
return next;
}
void skipWhitespace()
{
- while (isWhite(peekChar())) next = 0;
+ while (true)
+ {
+ auto c = peekCharNullable();
+ if (c.isNull ||
+ !isWhite(c.get))
+ {
+ return;
+ }
+ next.nullify();
+ }
}
Char getChar(bool SkipWhitespace = false)()
@@ -773,10 +1037,10 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
static if (SkipWhitespace) skipWhitespace();
Char c;
- if (next)
+ if (!next.isNull)
{
- c = next;
- next = 0;
+ c = next.get;
+ next.nullify();
}
else
c = popChar();
@@ -784,11 +1048,11 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
return c;
}
- void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c)
+ void checkChar(bool SkipWhitespace = true)(char c, bool caseSensitive = true)
{
static if (SkipWhitespace) skipWhitespace();
auto c2 = getChar();
- static if (!CaseSensitive) c2 = toLower(c2);
+ if (!caseSensitive) c2 = toLower(c2);
if (c2 != c) error(text("Found '", c2, "' when expecting '", c, "'."));
}
@@ -819,7 +1083,6 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
string parseString()
{
- import std.ascii : isControl;
import std.uni : isSurrogateHi, isSurrogateLo;
import std.utf : encode, decode;
@@ -880,8 +1143,12 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
default:
// RFC 7159 states that control characters U+0000 through
// U+001F must not appear unescaped in a JSON string.
+ // Note: std.ascii.isControl can't be used for this test
+ // because it considers ASCII DEL (0x7f) to be a control
+ // character but RFC 7159 does not.
+ // Accept unescaped ASCII NULs in non-strict mode.
auto c = getChar();
- if (isControl(c))
+ if (c < 0x20 && (strict || c != 0))
error("Illegal control character.");
str.put(c);
goto Next;
@@ -927,6 +1194,11 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
JSONValue[string] obj;
do
{
+ skipWhitespace();
+ if (!strict && peekChar() == '}')
+ {
+ break;
+ }
checkChar('"');
string name = parseString();
checkChar(':');
@@ -943,13 +1215,18 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
case '[':
if (testChar(']'))
{
- value.type_tag = JSON_TYPE.ARRAY;
+ value.type_tag = JSONType.array;
break;
}
JSONValue[] arr;
do
{
+ skipWhitespace();
+ if (!strict && peekChar() == ']')
+ {
+ break;
+ }
JSONValue element;
parseValue(element);
arr ~= element;
@@ -968,12 +1245,11 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
tryGetSpecialFloat(str, value.store.floating))
{
// found a special float, its value was placed in value.store.floating
- value.type_tag = JSON_TYPE.FLOAT;
+ value.type_tag = JSONType.float_;
break;
}
- value.type_tag = JSON_TYPE.STRING;
- value.store.str = str;
+ value.assign(str);
break;
case '0': .. case '9':
@@ -1001,7 +1277,18 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
isNegative = true;
}
- readInteger();
+ if (strict && c == '0')
+ {
+ number.put('0');
+ if (isDigit(peekChar()))
+ {
+ error("Additional digits not allowed after initial zero digit");
+ }
+ }
+ else
+ {
+ readInteger();
+ }
if (testChar('.'))
{
@@ -1023,44 +1310,63 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
string data = number.data;
if (isFloat)
{
- value.type_tag = JSON_TYPE.FLOAT;
+ value.type_tag = JSONType.float_;
value.store.floating = parse!double(data);
}
else
{
if (isNegative)
+ {
value.store.integer = parse!long(data);
+ value.type_tag = JSONType.integer;
+ }
else
- value.store.uinteger = parse!ulong(data);
-
- value.type_tag = !isNegative && value.store.uinteger & (1UL << 63) ?
- JSON_TYPE.UINTEGER : JSON_TYPE.INTEGER;
+ {
+ // only set the correct union member to not confuse CTFE
+ ulong u = parse!ulong(data);
+ if (u & (1UL << 63))
+ {
+ value.store.uinteger = u;
+ value.type_tag = JSONType.uinteger;
+ }
+ else
+ {
+ value.store.integer = u;
+ value.type_tag = JSONType.integer;
+ }
+ }
}
break;
- case 't':
case 'T':
- value.type_tag = JSON_TYPE.TRUE;
- checkChar!(false, false)('r');
- checkChar!(false, false)('u');
- checkChar!(false, false)('e');
+ if (strict) goto default;
+ goto case;
+ case 't':
+ value.type_tag = JSONType.true_;
+ checkChar!false('r', strict);
+ checkChar!false('u', strict);
+ checkChar!false('e', strict);
break;
- case 'f':
case 'F':
- value.type_tag = JSON_TYPE.FALSE;
- checkChar!(false, false)('a');
- checkChar!(false, false)('l');
- checkChar!(false, false)('s');
- checkChar!(false, false)('e');
+ if (strict) goto default;
+ goto case;
+ case 'f':
+ value.type_tag = JSONType.false_;
+ checkChar!false('a', strict);
+ checkChar!false('l', strict);
+ checkChar!false('s', strict);
+ checkChar!false('e', strict);
break;
- case 'n':
case 'N':
- value.type_tag = JSON_TYPE.NULL;
- checkChar!(false, false)('u');
- checkChar!(false, false)('l');
- checkChar!(false, false)('l');
+ if (strict) goto default;
+ goto case;
+ case 'n':
+ value.type_tag = JSONType.null_;
+ checkChar!false('u', strict);
+ checkChar!false('l', strict);
+ checkChar!false('l', strict);
break;
default:
@@ -1071,16 +1377,21 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
}
parseValue(root);
+ if (strict)
+ {
+ skipWhitespace();
+ if (!peekCharNullable().isNull) error("Trailing non-whitespace characters");
+ }
return root;
}
@safe unittest
{
enum issue15742objectOfObject = `{ "key1": { "key2": 1 }}`;
- static assert(parseJSON(issue15742objectOfObject).type == JSON_TYPE.OBJECT);
+ static assert(parseJSON(issue15742objectOfObject).type == JSONType.object);
enum issue15742arrayOfArray = `[[1]]`;
- static assert(parseJSON(issue15742arrayOfArray).type == JSON_TYPE.ARRAY);
+ static assert(parseJSON(issue15742arrayOfArray).type == JSONType.array);
}
@safe unittest
@@ -1110,9 +1421,15 @@ if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
assert(json["key1"]["key2"].integer == 1);
}
+// https://issues.dlang.org/show_bug.cgi?id=20527
+@safe unittest
+{
+ static assert(parseJSON(`{"a" : 2}`)["a"].integer == 2);
+}
+
/**
Parses a serialized string and returns a tree of JSON values.
-Throws: $(REF JSONException, std,json) if the depth exceeds the max depth.
+Throws: $(LREF JSONException) if the depth exceeds the max depth.
Params:
json = json-formatted string to parse
options = enable decoding string representations of NaN/Inf as float values
@@ -1128,15 +1445,26 @@ Takes a tree of JSON values and returns the serialized string.
Any Object types will be serialized in a key-sorted order.
-If $(D pretty) is false no whitespaces are generated.
-If $(D pretty) is true serialized string is formatted to be human-readable.
-Set the $(LREF JSONOptions.specialFloatLiterals) flag is set in $(D options) to encode NaN/Infinity as strings.
+If `pretty` is false no whitespaces are generated.
+If `pretty` is true serialized string is formatted to be human-readable.
+Set the $(LREF JSONOptions.specialFloatLiterals) flag is set in `options` to encode NaN/Infinity as strings.
*/
string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions options = JSONOptions.none) @safe
{
auto json = appender!string();
+ toJSON(json, root, pretty, options);
+ return json.data;
+}
- void toStringImpl(Char)(string str) @safe
+///
+void toJSON(Out)(
+ auto ref Out json,
+ const ref JSONValue root,
+ in bool pretty = false,
+ in JSONOptions options = JSONOptions.none)
+if (isOutputRange!(Out,char))
+{
+ void toStringImpl(Char)(string str)
{
json.put('"');
@@ -1166,7 +1494,7 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
// Make sure we do UTF decoding iff we want to
// escape Unicode characters.
assert(((options & JSONOptions.escapeNonAsciiChars) != 0)
- == is(Char == dchar));
+ == is(Char == dchar), "JSONOptions.escapeNonAsciiChars needs dchar strings");
with (JSONOptions) if (isControl(c) ||
((options & escapeNonAsciiChars) >= escapeNonAsciiChars && c >= 0x80))
@@ -1197,7 +1525,7 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
json.put('"');
}
- void toString(string str) @safe
+ void toString(string str)
{
// Avoid UTF decoding when possible, as it is unnecessary when
// processing JSON.
@@ -1207,7 +1535,19 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
toStringImpl!char(str);
}
- void toValue(ref in JSONValue value, ulong indentLevel) @safe
+ // recursive @safe inference is broken here
+ // workaround: if json.put is @safe, we should be too,
+ // so annotate the recursion as @safe manually
+ static if (isSafe!({ json.put(""); }))
+ {
+ void delegate(ref const JSONValue, ulong) @safe toValue;
+ }
+ else
+ {
+ void delegate(ref const JSONValue, ulong) @system toValue;
+ }
+
+ void toValueImpl(ref const JSONValue value, ulong indentLevel)
{
void putTabs(ulong additionalIndent = 0)
{
@@ -1228,7 +1568,7 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
final switch (value.type)
{
- case JSON_TYPE.OBJECT:
+ case JSONType.object:
auto obj = value.objectNoRef;
if (!obj.length)
{
@@ -1257,7 +1597,7 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
}
import std.algorithm.sorting : sort;
- // @@@BUG@@@ 14439
+ // https://issues.dlang.org/show_bug.cgi?id=14439
// auto names = obj.keys; // aa.keys can't be called in @safe code
auto names = new string[obj.length];
size_t i = 0;
@@ -1275,7 +1615,7 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
}
break;
- case JSON_TYPE.ARRAY:
+ case JSONType.array:
auto arr = value.arrayNoRef;
if (arr.empty)
{
@@ -1297,20 +1637,20 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
}
break;
- case JSON_TYPE.STRING:
+ case JSONType.string:
toString(value.str);
break;
- case JSON_TYPE.INTEGER:
+ case JSONType.integer:
json.put(to!string(value.store.integer));
break;
- case JSON_TYPE.UINTEGER:
+ case JSONType.uinteger:
json.put(to!string(value.store.uinteger));
break;
- case JSON_TYPE.FLOAT:
- import std.math : isNaN, isInfinity;
+ case JSONType.float_:
+ import std.math.traits : isNaN, isInfinity;
auto val = value.store.floating;
@@ -1340,34 +1680,41 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
}
else
{
- import std.format : format;
+ import std.algorithm.searching : canFind;
+ import std.format : sformat;
// The correct formula for the number of decimal digits needed for lossless round
// trips is actually:
// ceil(log(pow(2.0, double.mant_dig - 1)) / log(10.0) + 1) == (double.dig + 2)
// Anything less will round off (1 + double.epsilon)
- json.put("%.18g".format(val));
+ char[25] buf;
+ auto result = buf[].sformat!"%.18g"(val);
+ json.put(result);
+ if (!result.canFind('e') && !result.canFind('.'))
+ json.put(".0");
}
break;
- case JSON_TYPE.TRUE:
+ case JSONType.true_:
json.put("true");
break;
- case JSON_TYPE.FALSE:
+ case JSONType.false_:
json.put("false");
break;
- case JSON_TYPE.NULL:
+ case JSONType.null_:
json.put("null");
break;
}
}
+ toValue = &toValueImpl;
+
toValue(root, 0);
- return json.data;
}
-@safe unittest // bugzilla 12897
+ // https://issues.dlang.org/show_bug.cgi?id=12897
+@safe unittest
{
JSONValue jv0 = JSONValue("test测试");
assert(toJSON(jv0, false, JSONOptions.escapeNonAsciiChars) == `"test\u6D4B\u8BD5"`);
@@ -1381,6 +1728,71 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o
assert(toJSON(jv1, false, JSONOptions.none) == `"été"`);
}
+// https://issues.dlang.org/show_bug.cgi?id=20511
+@system unittest
+{
+ import std.format.write : formattedWrite;
+ import std.range : nullSink, outputRangeObject;
+
+ outputRangeObject!(const(char)[])(nullSink)
+ .formattedWrite!"%s"(JSONValue.init);
+}
+
+// Issue 16432 - JSON incorrectly parses to string
+@safe unittest
+{
+ // Floating points numbers are rounded to the nearest integer and thus get
+ // incorrectly parsed
+
+ import std.math.operations : isClose;
+
+ string s = "{\"rating\": 3.0 }";
+ JSONValue j = parseJSON(s);
+ assert(j["rating"].type == JSONType.float_);
+ j = j.toString.parseJSON;
+ assert(j["rating"].type == JSONType.float_);
+ assert(isClose(j["rating"].floating, 3.0));
+
+ s = "{\"rating\": -3.0 }";
+ j = parseJSON(s);
+ assert(j["rating"].type == JSONType.float_);
+ j = j.toString.parseJSON;
+ assert(j["rating"].type == JSONType.float_);
+ assert(isClose(j["rating"].floating, -3.0));
+
+ // https://issues.dlang.org/show_bug.cgi?id=13660
+ auto jv1 = JSONValue(4.0);
+ auto textual = jv1.toString();
+ auto jv2 = parseJSON(textual);
+ assert(jv1.type == JSONType.float_);
+ assert(textual == "4.0");
+ assert(jv2.type == JSONType.float_);
+}
+
+@safe unittest
+{
+ // Adapted from https://github.com/dlang/phobos/pull/5005
+ // Result from toString is not checked here, because this
+ // might differ (%e-like or %f-like output) depending
+ // on OS and compiler optimization.
+ import std.math.operations : isClose;
+
+ // test positive extreme values
+ JSONValue j;
+ j["rating"] = 1e18 - 65;
+ assert(isClose(j.toString.parseJSON["rating"].floating, 1e18 - 65));
+
+ j["rating"] = 1e18 - 64;
+ assert(isClose(j.toString.parseJSON["rating"].floating, 1e18 - 64));
+
+ // negative extreme values
+ j["rating"] = -1e18 + 65;
+ assert(isClose(j.toString.parseJSON["rating"].floating, -1e18 + 65));
+
+ j["rating"] = -1e18 + 64;
+ assert(isClose(j.toString.parseJSON["rating"].floating, -1e18 + 64));
+}
+
/**
Exception thrown on JSON errors
*/
@@ -1405,7 +1817,7 @@ class JSONException : Exception
{
import std.exception;
JSONValue jv = "123";
- assert(jv.type == JSON_TYPE.STRING);
+ assert(jv.type == JSONType.string);
assertNotThrown(jv.str);
assertThrown!JSONException(jv.integer);
assertThrown!JSONException(jv.uinteger);
@@ -1416,19 +1828,19 @@ class JSONException : Exception
assertThrown!JSONException(jv[2]);
jv = -3;
- assert(jv.type == JSON_TYPE.INTEGER);
+ assert(jv.type == JSONType.integer);
assertNotThrown(jv.integer);
jv = cast(uint) 3;
- assert(jv.type == JSON_TYPE.UINTEGER);
+ assert(jv.type == JSONType.uinteger);
assertNotThrown(jv.uinteger);
jv = 3.0;
- assert(jv.type == JSON_TYPE.FLOAT);
+ assert(jv.type == JSONType.float_);
assertNotThrown(jv.floating);
jv = ["key" : "value"];
- assert(jv.type == JSON_TYPE.OBJECT);
+ assert(jv.type == JSONType.object);
assertNotThrown(jv.object);
assertNotThrown(jv["key"]);
assert("key" in jv);
@@ -1442,82 +1854,81 @@ class JSONException : Exception
{
static assert(is(typeof(value) == JSONValue));
assert(key == "key");
- assert(value.type == JSON_TYPE.STRING);
+ assert(value.type == JSONType.string);
assertNotThrown(value.str);
assert(value.str == "value");
}
jv = [3, 4, 5];
- assert(jv.type == JSON_TYPE.ARRAY);
+ assert(jv.type == JSONType.array);
assertNotThrown(jv.array);
assertNotThrown(jv[2]);
foreach (size_t index, value; jv)
{
static assert(is(typeof(value) == JSONValue));
- assert(value.type == JSON_TYPE.INTEGER);
+ assert(value.type == JSONType.integer);
assertNotThrown(value.integer);
assert(index == (value.integer-3));
}
jv = null;
- assert(jv.type == JSON_TYPE.NULL);
+ assert(jv.type == JSONType.null_);
assert(jv.isNull);
jv = "foo";
assert(!jv.isNull);
jv = JSONValue("value");
- assert(jv.type == JSON_TYPE.STRING);
+ assert(jv.type == JSONType.string);
assert(jv.str == "value");
JSONValue jv2 = JSONValue("value");
- assert(jv2.type == JSON_TYPE.STRING);
+ assert(jv2.type == JSONType.string);
assert(jv2.str == "value");
JSONValue jv3 = JSONValue("\u001c");
- assert(jv3.type == JSON_TYPE.STRING);
+ assert(jv3.type == JSONType.string);
assert(jv3.str == "\u001C");
}
+// https://issues.dlang.org/show_bug.cgi?id=11504
@system unittest
{
- // Bugzilla 11504
-
JSONValue jv = 1;
- assert(jv.type == JSON_TYPE.INTEGER);
+ assert(jv.type == JSONType.integer);
jv.str = "123";
- assert(jv.type == JSON_TYPE.STRING);
+ assert(jv.type == JSONType.string);
assert(jv.str == "123");
jv.integer = 1;
- assert(jv.type == JSON_TYPE.INTEGER);
+ assert(jv.type == JSONType.integer);
assert(jv.integer == 1);
jv.uinteger = 2u;
- assert(jv.type == JSON_TYPE.UINTEGER);
+ assert(jv.type == JSONType.uinteger);
assert(jv.uinteger == 2u);
jv.floating = 1.5;
- assert(jv.type == JSON_TYPE.FLOAT);
+ assert(jv.type == JSONType.float_);
assert(jv.floating == 1.5);
jv.object = ["key" : JSONValue("value")];
- assert(jv.type == JSON_TYPE.OBJECT);
+ assert(jv.type == JSONType.object);
assert(jv.object == ["key" : JSONValue("value")]);
jv.array = [JSONValue(1), JSONValue(2), JSONValue(3)];
- assert(jv.type == JSON_TYPE.ARRAY);
+ assert(jv.type == JSONType.array);
assert(jv.array == [JSONValue(1), JSONValue(2), JSONValue(3)]);
jv = true;
- assert(jv.type == JSON_TYPE.TRUE);
+ assert(jv.type == JSONType.true_);
jv = false;
- assert(jv.type == JSON_TYPE.FALSE);
+ assert(jv.type == JSONType.false_);
enum E{True = true}
jv = E.True;
- assert(jv.type == JSON_TYPE.TRUE);
+ assert(jv.type == JSONType.true_);
}
@system pure unittest
@@ -1653,32 +2064,32 @@ class JSONException : Exception
@system pure unittest
{
- // Bugzilla 12969
+ // https://issues.dlang.org/show_bug.cgi?id=12969
JSONValue jv;
jv["int"] = 123;
- assert(jv.type == JSON_TYPE.OBJECT);
+ assert(jv.type == JSONType.object);
assert("int" in jv);
assert(jv["int"].integer == 123);
jv["array"] = [1, 2, 3, 4, 5];
- assert(jv["array"].type == JSON_TYPE.ARRAY);
+ assert(jv["array"].type == JSONType.array);
assert(jv["array"][2].integer == 3);
jv["str"] = "D language";
- assert(jv["str"].type == JSON_TYPE.STRING);
+ assert(jv["str"].type == JSONType.string);
assert(jv["str"].str == "D language");
jv["bool"] = false;
- assert(jv["bool"].type == JSON_TYPE.FALSE);
+ assert(jv["bool"].type == JSONType.false_);
assert(jv.object.length == 4);
jv = [5, 4, 3, 2, 1];
- assert( jv.type == JSON_TYPE.ARRAY );
- assert( jv[3].integer == 2 );
+ assert(jv.type == JSONType.array);
+ assert(jv[3].integer == 2);
}
@safe unittest
@@ -1702,7 +2113,7 @@ EOF";
@safe unittest
{
import std.exception : assertThrown;
- import std.math : isNaN, isInfinity;
+ import std.math.traits : isNaN, isInfinity;
// expected representations of NaN and Inf
enum {
@@ -1753,28 +2164,30 @@ pure nothrow @safe @nogc unittest
assert(testVal.isNull);
}
-pure nothrow @safe unittest // issue 15884
+// https://issues.dlang.org/show_bug.cgi?id=15884
+pure nothrow @safe unittest
{
import std.typecons;
void Test(C)() {
C[] a = ['x'];
JSONValue testVal = a;
- assert(testVal.type == JSON_TYPE.STRING);
+ assert(testVal.type == JSONType.string);
testVal = a.idup;
- assert(testVal.type == JSON_TYPE.STRING);
+ assert(testVal.type == JSONType.string);
}
Test!char();
Test!wchar();
Test!dchar();
}
-@safe unittest // issue 15885
+// https://issues.dlang.org/show_bug.cgi?id=15885
+@safe unittest
{
enum bool realInDoublePrecision = real.mant_dig == double.mant_dig;
static bool test(const double num0)
{
- import std.math : feqrel;
+ import std.math.operations : feqrel;
const json0 = JSONValue(num0);
const num1 = to!double(toJSON(json0));
static if (realInDoublePrecision)
@@ -1802,34 +2215,39 @@ pure nothrow @safe unittest // issue 15884
assert(test(3*minSub));
}
-@safe unittest // issue 17555
+// https://issues.dlang.org/show_bug.cgi?id=17555
+@safe unittest
{
import std.exception : assertThrown;
assertThrown!JSONException(parseJSON("\"a\nb\""));
}
-@safe unittest // issue 17556
+// https://issues.dlang.org/show_bug.cgi?id=17556
+@safe unittest
{
auto v = JSONValue("\U0001D11E");
auto j = toJSON(v, false, JSONOptions.escapeNonAsciiChars);
assert(j == `"\uD834\uDD1E"`);
}
-@safe unittest // issue 5904
+// https://issues.dlang.org/show_bug.cgi?id=5904
+@safe unittest
{
string s = `"\uD834\uDD1E"`;
auto j = parseJSON(s);
assert(j.str == "\U0001D11E");
}
-@safe unittest // issue 17557
+// https://issues.dlang.org/show_bug.cgi?id=17557
+@safe unittest
{
assert(parseJSON("\"\xFF\"").str == "\xFF");
assert(parseJSON("\"\U0001D11E\"").str == "\U0001D11E");
}
-@safe unittest // issue 17553
+// https://issues.dlang.org/show_bug.cgi?id=17553
+@safe unittest
{
auto v = JSONValue("\xFF");
assert(toJSON(v) == "\"\xFF\"");
@@ -1842,10 +2260,145 @@ pure nothrow @safe unittest // issue 15884
assert(parseJSON("\"\U0001D11E\"".byChar).str == "\U0001D11E");
}
-@safe unittest // JSONOptions.doNotEscapeSlashes (issue 17587)
+// JSONOptions.doNotEscapeSlashes (https://issues.dlang.org/show_bug.cgi?id=17587)
+@safe unittest
{
assert(parseJSON(`"/"`).toString == `"\/"`);
assert(parseJSON(`"\/"`).toString == `"\/"`);
assert(parseJSON(`"/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`);
assert(parseJSON(`"\/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`);
}
+
+// JSONOptions.strictParsing (https://issues.dlang.org/show_bug.cgi?id=16639)
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ // Unescaped ASCII NULs
+ assert(parseJSON("[\0]").type == JSONType.array);
+ assertThrown!JSONException(parseJSON("[\0]", JSONOptions.strictParsing));
+ assert(parseJSON("\"\0\"").str == "\0");
+ assertThrown!JSONException(parseJSON("\"\0\"", JSONOptions.strictParsing));
+
+ // Unescaped ASCII DEL (0x7f) in strings
+ assert(parseJSON("\"\x7f\"").str == "\x7f");
+ assert(parseJSON("\"\x7f\"", JSONOptions.strictParsing).str == "\x7f");
+
+ // "true", "false", "null" case sensitivity
+ assert(parseJSON("true").type == JSONType.true_);
+ assert(parseJSON("true", JSONOptions.strictParsing).type == JSONType.true_);
+ assert(parseJSON("True").type == JSONType.true_);
+ assertThrown!JSONException(parseJSON("True", JSONOptions.strictParsing));
+ assert(parseJSON("tRUE").type == JSONType.true_);
+ assertThrown!JSONException(parseJSON("tRUE", JSONOptions.strictParsing));
+
+ assert(parseJSON("false").type == JSONType.false_);
+ assert(parseJSON("false", JSONOptions.strictParsing).type == JSONType.false_);
+ assert(parseJSON("False").type == JSONType.false_);
+ assertThrown!JSONException(parseJSON("False", JSONOptions.strictParsing));
+ assert(parseJSON("fALSE").type == JSONType.false_);
+ assertThrown!JSONException(parseJSON("fALSE", JSONOptions.strictParsing));
+
+ assert(parseJSON("null").type == JSONType.null_);
+ assert(parseJSON("null", JSONOptions.strictParsing).type == JSONType.null_);
+ assert(parseJSON("Null").type == JSONType.null_);
+ assertThrown!JSONException(parseJSON("Null", JSONOptions.strictParsing));
+ assert(parseJSON("nULL").type == JSONType.null_);
+ assertThrown!JSONException(parseJSON("nULL", JSONOptions.strictParsing));
+
+ // Whitespace characters
+ assert(parseJSON("[\f\v]").type == JSONType.array);
+ assertThrown!JSONException(parseJSON("[\f\v]", JSONOptions.strictParsing));
+ assert(parseJSON("[ \t\r\n]").type == JSONType.array);
+ assert(parseJSON("[ \t\r\n]", JSONOptions.strictParsing).type == JSONType.array);
+
+ // Empty input
+ assert(parseJSON("").type == JSONType.null_);
+ assertThrown!JSONException(parseJSON("", JSONOptions.strictParsing));
+
+ // Numbers with leading '0's
+ assert(parseJSON("01").integer == 1);
+ assertThrown!JSONException(parseJSON("01", JSONOptions.strictParsing));
+ assert(parseJSON("-01").integer == -1);
+ assertThrown!JSONException(parseJSON("-01", JSONOptions.strictParsing));
+ assert(parseJSON("0.01").floating == 0.01);
+ assert(parseJSON("0.01", JSONOptions.strictParsing).floating == 0.01);
+ assert(parseJSON("0e1").floating == 0);
+ assert(parseJSON("0e1", JSONOptions.strictParsing).floating == 0);
+
+ // Trailing characters after JSON value
+ assert(parseJSON(`""asdf`).str == "");
+ assertThrown!JSONException(parseJSON(`""asdf`, JSONOptions.strictParsing));
+ assert(parseJSON("987\0").integer == 987);
+ assertThrown!JSONException(parseJSON("987\0", JSONOptions.strictParsing));
+ assert(parseJSON("987\0\0").integer == 987);
+ assertThrown!JSONException(parseJSON("987\0\0", JSONOptions.strictParsing));
+ assert(parseJSON("[]]").type == JSONType.array);
+ assertThrown!JSONException(parseJSON("[]]", JSONOptions.strictParsing));
+ assert(parseJSON("123 \t\r\n").integer == 123); // Trailing whitespace is OK
+ assert(parseJSON("123 \t\r\n", JSONOptions.strictParsing).integer == 123);
+}
+
+@system unittest
+{
+ import std.algorithm.iteration : map;
+ import std.array : array;
+ import std.exception : assertThrown;
+
+ string s = `{ "a" : [1,2,3,], }`;
+ JSONValue j = parseJSON(s);
+ assert(j["a"].array().map!(i => i.integer()).array == [1,2,3]);
+
+ assertThrown(parseJSON(s, -1, JSONOptions.strictParsing));
+}
+
+@system unittest
+{
+ import std.algorithm.iteration : map;
+ import std.array : array;
+ import std.exception : assertThrown;
+
+ string s = `{ "a" : { } , }`;
+ JSONValue j = parseJSON(s);
+ assert("a" in j);
+ auto t = j["a"].object();
+ assert(t.empty);
+
+ assertThrown(parseJSON(s, -1, JSONOptions.strictParsing));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20330
+@safe unittest
+{
+ import std.array : appender;
+
+ string s = `{"a":[1,2,3]}`;
+ JSONValue j = parseJSON(s);
+
+ auto app = appender!string();
+ j.toString(app);
+
+ assert(app.data == s, app.data);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20330
+@safe unittest
+{
+ import std.array : appender;
+ import std.format.write : formattedWrite;
+
+ string s =
+`{
+ "a": [
+ 1,
+ 2,
+ 3
+ ]
+}`;
+ JSONValue j = parseJSON(s);
+
+ auto app = appender!string();
+ j.toPrettyString(app);
+
+ assert(app.data == s, app.data);
+}
diff --git a/libphobos/src/std/math.d b/libphobos/src/std/math.d
deleted file mode 100644
index 336c11a55e2..00000000000
--- a/libphobos/src/std/math.d
+++ /dev/null
@@ -1,8586 +0,0 @@
-// Written in the D programming language.
-
-/**
- * Contains the elementary mathematical functions (powers, roots,
- * and trigonometric functions), and low-level floating-point operations.
- * Mathematical special functions are available in $(D std.mathspecial).
- *
-$(SCRIPT inhibitQuickIndex = 1;)
-
-$(DIVC quickindex,
-$(BOOKTABLE ,
-$(TR $(TH Category) $(TH Members) )
-$(TR $(TDNW Constants) $(TD
- $(MYREF E) $(MYREF PI) $(MYREF PI_2) $(MYREF PI_4) $(MYREF M_1_PI)
- $(MYREF M_2_PI) $(MYREF M_2_SQRTPI) $(MYREF LN10) $(MYREF LN2)
- $(MYREF LOG2) $(MYREF LOG2E) $(MYREF LOG2T) $(MYREF LOG10E)
- $(MYREF SQRT2) $(MYREF SQRT1_2)
-))
-$(TR $(TDNW Classics) $(TD
- $(MYREF abs) $(MYREF fabs) $(MYREF sqrt) $(MYREF cbrt) $(MYREF hypot)
- $(MYREF poly) $(MYREF nextPow2) $(MYREF truncPow2)
-))
-$(TR $(TDNW Trigonometry) $(TD
- $(MYREF sin) $(MYREF cos) $(MYREF tan) $(MYREF asin) $(MYREF acos)
- $(MYREF atan) $(MYREF atan2) $(MYREF sinh) $(MYREF cosh) $(MYREF tanh)
- $(MYREF asinh) $(MYREF acosh) $(MYREF atanh) $(MYREF expi)
-))
-$(TR $(TDNW Rounding) $(TD
- $(MYREF ceil) $(MYREF floor) $(MYREF round) $(MYREF lround)
- $(MYREF trunc) $(MYREF rint) $(MYREF lrint) $(MYREF nearbyint)
- $(MYREF rndtol) $(MYREF quantize)
-))
-$(TR $(TDNW Exponentiation & Logarithms) $(TD
- $(MYREF pow) $(MYREF exp) $(MYREF exp2) $(MYREF expm1) $(MYREF ldexp)
- $(MYREF frexp) $(MYREF log) $(MYREF log2) $(MYREF log10) $(MYREF logb)
- $(MYREF ilogb) $(MYREF log1p) $(MYREF scalbn)
-))
-$(TR $(TDNW Modulus) $(TD
- $(MYREF fmod) $(MYREF modf) $(MYREF remainder)
-))
-$(TR $(TDNW Floating-point operations) $(TD
- $(MYREF approxEqual) $(MYREF feqrel) $(MYREF fdim) $(MYREF fmax)
- $(MYREF fmin) $(MYREF fma) $(MYREF nextDown) $(MYREF nextUp)
- $(MYREF nextafter) $(MYREF NaN) $(MYREF getNaNPayload)
- $(MYREF cmp)
-))
-$(TR $(TDNW Introspection) $(TD
- $(MYREF isFinite) $(MYREF isIdentical) $(MYREF isInfinity) $(MYREF isNaN)
- $(MYREF isNormal) $(MYREF isSubnormal) $(MYREF signbit) $(MYREF sgn)
- $(MYREF copysign) $(MYREF isPowerOf2)
-))
-$(TR $(TDNW Complex Numbers) $(TD
- $(MYREF abs) $(MYREF conj) $(MYREF sin) $(MYREF cos) $(MYREF expi)
-))
-$(TR $(TDNW Hardware Control) $(TD
- $(MYREF IeeeFlags) $(MYREF FloatingPointControl)
-))
-)
-)
-
- * The functionality closely follows the IEEE754-2008 standard for
- * floating-point arithmetic, including the use of camelCase names rather
- * than C99-style lower case names. All of these functions behave correctly
- * when presented with an infinity or NaN.
- *
- * The following IEEE 'real' formats are currently supported:
- * $(UL
- * $(LI 64 bit Big-endian 'double' (eg PowerPC))
- * $(LI 128 bit Big-endian 'quadruple' (eg SPARC))
- * $(LI 64 bit Little-endian 'double' (eg x86-SSE2))
- * $(LI 80 bit Little-endian, with implied bit 'real80' (eg x87, Itanium))
- * $(LI 128 bit Little-endian 'quadruple' (not implemented on any known processor!))
- * $(LI Non-IEEE 128 bit Big-endian 'doubledouble' (eg PowerPC) has partial support)
- * )
- * Unlike C, there is no global 'errno' variable. Consequently, almost all of
- * these functions are pure nothrow.
- *
- * Status:
- * The semantics and names of feqrel and approxEqual will be revised.
- *
- * Macros:
- * TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
- * <caption>Special Values</caption>
- * $0</table>
- * SVH = $(TR $(TH $1) $(TH $2))
- * SV = $(TR $(TD $1) $(TD $2))
- * TH3 = $(TR $(TH $1) $(TH $2) $(TH $3))
- * TD3 = $(TR $(TD $1) $(TD $2) $(TD $3))
- * TABLE_DOMRG = <table border="1" cellpadding="4" cellspacing="0">
- * $(SVH Domain X, Range Y)
- $(SV $1, $2)
- * </table>
- * DOMAIN=$1
- * RANGE=$1
-
- * NAN = $(RED NAN)
- * SUP = <span style="vertical-align:super;font-size:smaller">$0</span>
- * GAMMA = &#915;
- * THETA = &theta;
- * INTEGRAL = &#8747;
- * INTEGRATE = $(BIG &#8747;<sub>$(SMALL $1)</sub><sup>$2</sup>)
- * POWER = $1<sup>$2</sup>
- * SUB = $1<sub>$2</sub>
- * BIGSUM = $(BIG &Sigma; <sup>$2</sup><sub>$(SMALL $1)</sub>)
- * CHOOSE = $(BIG &#40;) <sup>$(SMALL $1)</sup><sub>$(SMALL $2)</sub> $(BIG &#41;)
- * PLUSMN = &plusmn;
- * INFIN = &infin;
- * PLUSMNINF = &plusmn;&infin;
- * PI = &pi;
- * LT = &lt;
- * GT = &gt;
- * SQRT = &radic;
- * HALF = &frac12;
- *
- * Copyright: Copyright Digital Mars 2000 - 2011.
- * D implementations of tan, atan, atan2, exp, expm1, exp2, log, log10, log1p,
- * log2, floor, ceil and lrint functions are based on the CEPHES math library,
- * which is Copyright (C) 2001 Stephen L. Moshier $(LT)steve@moshier.net$(GT)
- * and are incorporated herein by permission of the author. The author
- * reserves the right to distribute this material elsewhere under different
- * copying permissions. These modifications are distributed here under
- * the following terms:
- * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
- * Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
- * Source: $(PHOBOSSRC std/_math.d)
- */
-
-/* NOTE: This file has been patched from the original DMD distribution to
- * work with the GDC compiler.
- */
-module std.math;
-
-version (Win64)
-{
- version (D_InlineAsm_X86_64)
- version = Win64_DMD_InlineAsm;
-}
-
-static import core.math;
-static import core.stdc.math;
-static import core.stdc.fenv;
-import std.traits; // CommonType, isFloatingPoint, isIntegral, isSigned, isUnsigned, Largest, Unqual
-
-version (LDC)
-{
- import ldc.intrinsics;
-}
-
-version (DigitalMars)
-{
- version = INLINE_YL2X; // x87 has opcodes for these
-}
-
-version (X86) version = X86_Any;
-version (X86_64) version = X86_Any;
-version (PPC) version = PPC_Any;
-version (PPC64) version = PPC_Any;
-version (MIPS32) version = MIPS_Any;
-version (MIPS64) version = MIPS_Any;
-version (AArch64) version = ARM_Any;
-version (ARM) version = ARM_Any;
-version (S390) version = IBMZ_Any;
-version (SPARC) version = SPARC_Any;
-version (SPARC64) version = SPARC_Any;
-version (SystemZ) version = IBMZ_Any;
-version (RISCV32) version = RISCV_Any;
-version (RISCV64) version = RISCV_Any;
-
-version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
-version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
-
-version (InlineAsm_X86_Any) version = InlineAsm_X87;
-version (InlineAsm_X87)
-{
- static assert(real.mant_dig == 64);
- version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC;
-}
-
-version (X86_64) version = StaticallyHaveSSE;
-version (X86) version (OSX) version = StaticallyHaveSSE;
-
-version (StaticallyHaveSSE)
-{
- private enum bool haveSSE = true;
-}
-else version (X86)
-{
- static import core.cpuid;
- private alias haveSSE = core.cpuid.sse;
-}
-
-version (D_SoftFloat)
-{
- // Some soft float implementations may support IEEE floating flags.
- // The implementation here supports hardware flags only and is so currently
- // only available for supported targets.
-}
-else version (X86_Any) version = IeeeFlagsSupport;
-else version (PPC_Any) version = IeeeFlagsSupport;
-else version (RISCV_Any) version = IeeeFlagsSupport;
-else version (MIPS_Any) version = IeeeFlagsSupport;
-else version (ARM_Any) version = IeeeFlagsSupport;
-
-// Struct FloatingPointControl is only available if hardware FP units are available.
-version (D_HardFloat)
-{
- // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport
- version (IeeeFlagsSupport) version = FloatingPointControlSupport;
-}
-
-version (GNU)
-{
- // The compiler can unexpectedly rearrange floating point operations and
- // access to the floating point status flags when optimizing. This means
- // ieeeFlags tests cannot be reliably checked in optimized code.
- // See https://github.com/ldc-developers/ldc/issues/888
-}
-else
-{
- version = IeeeFlagsUnittest;
- version = FloatingPointControlUnittest;
-}
-
-version (unittest)
-{
- import core.stdc.stdio; // : sprintf;
-
- static if (real.sizeof > double.sizeof)
- enum uint useDigits = 16;
- else
- enum uint useDigits = 15;
-
- /******************************************
- * Compare floating point numbers to n decimal digits of precision.
- * Returns:
- * 1 match
- * 0 nomatch
- */
-
- private bool equalsDigit(real x, real y, uint ndigits)
- {
- if (signbit(x) != signbit(y))
- return 0;
-
- if (isInfinity(x) && isInfinity(y))
- return 1;
- if (isInfinity(x) || isInfinity(y))
- return 0;
-
- if (isNaN(x) && isNaN(y))
- return 1;
- if (isNaN(x) || isNaN(y))
- return 0;
-
- char[30] bufx;
- char[30] bufy;
- assert(ndigits < bufx.length);
-
- int ix;
- int iy;
- version (CRuntime_Microsoft)
- alias real_t = double;
- else
- alias real_t = real;
- ix = sprintf(bufx.ptr, is(real_t == real) ? "%.*Lg" : "%.*g", ndigits, cast(real_t) x);
- iy = sprintf(bufy.ptr, is(real_t == real) ? "%.*Lg" : "%.*g", ndigits, cast(real_t) y);
- assert(ix < bufx.length && ix > 0);
- assert(ix < bufy.length && ix > 0);
-
- return bufx[0 .. ix] == bufy[0 .. iy];
- }
-}
-
-
-
-package:
-// The following IEEE 'real' formats are currently supported.
-version (LittleEndian)
-{
- static assert(real.mant_dig == 53 || real.mant_dig == 64
- || real.mant_dig == 113,
- "Only 64-bit, 80-bit, and 128-bit reals"~
- " are supported for LittleEndian CPUs");
-}
-else
-{
- static assert(real.mant_dig == 53 || real.mant_dig == 106
- || real.mant_dig == 113,
- "Only 64-bit and 128-bit reals are supported for BigEndian CPUs."~
- " double-double reals have partial support");
-}
-
-// Underlying format exposed through floatTraits
-enum RealFormat
-{
- ieeeHalf,
- ieeeSingle,
- ieeeDouble,
- ieeeExtended, // x87 80-bit real
- ieeeExtended53, // x87 real rounded to precision of double.
- ibmExtended, // IBM 128-bit extended
- ieeeQuadruple,
-}
-
-// Constants used for extracting the components of the representation.
-// They supplement the built-in floating point properties.
-template floatTraits(T)
-{
- // EXPMASK is a ushort mask to select the exponent portion (without sign)
- // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort
- // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1).
- // EXPPOS_SHORT is the index of the exponent when represented as a ushort array.
- // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array.
- // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal
- enum T RECIP_EPSILON = (1/T.epsilon);
- static if (T.mant_dig == 24)
- {
- // Single precision float
- enum ushort EXPMASK = 0x7F80;
- enum ushort EXPSHIFT = 7;
- enum ushort EXPBIAS = 0x3F00;
- enum uint EXPMASK_INT = 0x7F80_0000;
- enum uint MANTISSAMASK_INT = 0x007F_FFFF;
- enum realFormat = RealFormat.ieeeSingle;
- version (LittleEndian)
- {
- enum EXPPOS_SHORT = 1;
- enum SIGNPOS_BYTE = 3;
- }
- else
- {
- enum EXPPOS_SHORT = 0;
- enum SIGNPOS_BYTE = 0;
- }
- }
- else static if (T.mant_dig == 53)
- {
- static if (T.sizeof == 8)
- {
- // Double precision float, or real == double
- enum ushort EXPMASK = 0x7FF0;
- enum ushort EXPSHIFT = 4;
- enum ushort EXPBIAS = 0x3FE0;
- enum uint EXPMASK_INT = 0x7FF0_0000;
- enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only
- enum realFormat = RealFormat.ieeeDouble;
- version (LittleEndian)
- {
- enum EXPPOS_SHORT = 3;
- enum SIGNPOS_BYTE = 7;
- }
- else
- {
- enum EXPPOS_SHORT = 0;
- enum SIGNPOS_BYTE = 0;
- }
- }
- else static if (T.sizeof == 12)
- {
- // Intel extended real80 rounded to double
- enum ushort EXPMASK = 0x7FFF;
- enum ushort EXPSHIFT = 0;
- enum ushort EXPBIAS = 0x3FFE;
- enum realFormat = RealFormat.ieeeExtended53;
- version (LittleEndian)
- {
- enum EXPPOS_SHORT = 4;
- enum SIGNPOS_BYTE = 9;
- }
- else
- {
- enum EXPPOS_SHORT = 0;
- enum SIGNPOS_BYTE = 0;
- }
- }
- else
- static assert(false, "No traits support for " ~ T.stringof);
- }
- else static if (T.mant_dig == 64)
- {
- // Intel extended real80
- enum ushort EXPMASK = 0x7FFF;
- enum ushort EXPSHIFT = 0;
- enum ushort EXPBIAS = 0x3FFE;
- enum realFormat = RealFormat.ieeeExtended;
- version (LittleEndian)
- {
- enum EXPPOS_SHORT = 4;
- enum SIGNPOS_BYTE = 9;
- }
- else
- {
- enum EXPPOS_SHORT = 0;
- enum SIGNPOS_BYTE = 0;
- }
- }
- else static if (T.mant_dig == 113)
- {
- // Quadruple precision float
- enum ushort EXPMASK = 0x7FFF;
- enum ushort EXPSHIFT = 0;
- enum ushort EXPBIAS = 0x3FFE;
- enum realFormat = RealFormat.ieeeQuadruple;
- version (LittleEndian)
- {
- enum EXPPOS_SHORT = 7;
- enum SIGNPOS_BYTE = 15;
- }
- else
- {
- enum EXPPOS_SHORT = 0;
- enum SIGNPOS_BYTE = 0;
- }
- }
- else static if (T.mant_dig == 106)
- {
- // IBM Extended doubledouble
- enum ushort EXPMASK = 0x7FF0;
- enum ushort EXPSHIFT = 4;
- enum realFormat = RealFormat.ibmExtended;
-
- // For IBM doubledouble the larger magnitude double comes first.
- // It's really a double[2] and arrays don't index differently
- // between little and big-endian targets.
- enum DOUBLEPAIR_MSB = 0;
- enum DOUBLEPAIR_LSB = 1;
-
- // The exponent/sign byte is for most significant part.
- version (LittleEndian)
- {
- enum EXPPOS_SHORT = 3;
- enum SIGNPOS_BYTE = 7;
- }
- else
- {
- enum EXPPOS_SHORT = 0;
- enum SIGNPOS_BYTE = 0;
- }
- }
- else
- static assert(false, "No traits support for " ~ T.stringof);
-}
-
-// These apply to all floating-point types
-version (LittleEndian)
-{
- enum MANTISSA_LSB = 0;
- enum MANTISSA_MSB = 1;
-}
-else
-{
- enum MANTISSA_LSB = 1;
- enum MANTISSA_MSB = 0;
-}
-
-// Common code for math implementations.
-
-// Helper for floor/ceil
-T floorImpl(T)(const T x) @trusted pure nothrow @nogc
-{
- alias F = floatTraits!(T);
- // Take care not to trigger library calls from the compiler,
- // while ensuring that we don't get defeated by some optimizers.
- union floatBits
- {
- T rv;
- ushort[T.sizeof/2] vu;
-
- // Other kinds of extractors for real formats.
- static if (F.realFormat == RealFormat.ieeeSingle)
- int vi;
- }
- floatBits y = void;
- y.rv = x;
-
- // Find the exponent (power of 2)
- // Do this by shifting the raw value so that the exponent lies in the low bits,
- // then mask out the sign bit, and subtract the bias.
- static if (F.realFormat == RealFormat.ieeeSingle)
- {
- int exp = ((y.vi >> (T.mant_dig - 1)) & 0xff) - 0x7f;
- }
- else static if (F.realFormat == RealFormat.ieeeDouble)
- {
- int exp = ((y.vu[F.EXPPOS_SHORT] >> 4) & 0x7ff) - 0x3ff;
-
- version (LittleEndian)
- int pos = 0;
- else
- int pos = 3;
- }
- else static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
-
- version (LittleEndian)
- int pos = 0;
- else
- int pos = 4;
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
-
- version (LittleEndian)
- int pos = 0;
- else
- int pos = 7;
- }
- else
- static assert(false, "Not implemented for this architecture");
-
- if (exp < 0)
- {
- if (x < 0.0)
- return -1.0;
- else
- return 0.0;
- }
-
- static if (F.realFormat == RealFormat.ieeeSingle)
- {
- if (exp < (T.mant_dig - 1))
- {
- // Clear all bits representing the fraction part.
- const uint fraction_mask = F.MANTISSAMASK_INT >> exp;
-
- if ((y.vi & fraction_mask) != 0)
- {
- // If 'x' is negative, then first substract 1.0 from the value.
- if (y.vi < 0)
- y.vi += 0x00800000 >> exp;
- y.vi &= ~fraction_mask;
- }
- }
- }
- else
- {
- static if (F.realFormat == RealFormat.ieeeExtended53)
- exp = (T.mant_dig + 11 - 1) - exp; // mant_dig is really 64
- else
- exp = (T.mant_dig - 1) - exp;
-
- // Zero 16 bits at a time.
- while (exp >= 16)
- {
- version (LittleEndian)
- y.vu[pos++] = 0;
- else
- y.vu[pos--] = 0;
- exp -= 16;
- }
-
- // Clear the remaining bits.
- if (exp > 0)
- y.vu[pos] &= 0xffff ^ ((1 << exp) - 1);
-
- if ((x < 0.0) && (x != y.rv))
- y.rv -= 1.0;
- }
-
- return y.rv;
-}
-
-public:
-
-// Values obtained from Wolfram Alpha. 116 bits ought to be enough for anybody.
-// Wolfram Alpha LLC. 2011. Wolfram|Alpha. http://www.wolframalpha.com/input/?i=e+in+base+16 (access July 6, 2011).
-enum real E = 0x1.5bf0a8b1457695355fb8ac404e7a8p+1L; /** e = 2.718281... */
-enum real LOG2T = 0x1.a934f0979a3715fc9257edfe9b5fbp+1L; /** $(SUB log, 2)10 = 3.321928... */
-enum real LOG2E = 0x1.71547652b82fe1777d0ffda0d23a8p+0L; /** $(SUB log, 2)e = 1.442695... */
-enum real LOG2 = 0x1.34413509f79fef311f12b35816f92p-2L; /** $(SUB log, 10)2 = 0.301029... */
-enum real LOG10E = 0x1.bcb7b1526e50e32a6ab7555f5a67cp-2L; /** $(SUB log, 10)e = 0.434294... */
-enum real LN2 = 0x1.62e42fefa39ef35793c7673007e5fp-1L; /** ln 2 = 0.693147... */
-enum real LN10 = 0x1.26bb1bbb5551582dd4adac5705a61p+1L; /** ln 10 = 2.302585... */
-enum real PI = 0x1.921fb54442d18469898cc51701b84p+1L; /** $(_PI) = 3.141592... */
-enum real PI_2 = PI/2; /** $(PI) / 2 = 1.570796... */
-enum real PI_4 = PI/4; /** $(PI) / 4 = 0.785398... */
-enum real M_1_PI = 0x1.45f306dc9c882a53f84eafa3ea69cp-2L; /** 1 / $(PI) = 0.318309... */
-enum real M_2_PI = 2*M_1_PI; /** 2 / $(PI) = 0.636619... */
-enum real M_2_SQRTPI = 0x1.20dd750429b6d11ae3a914fed7fd8p+0L; /** 2 / $(SQRT)$(PI) = 1.128379... */
-enum real SQRT2 = 0x1.6a09e667f3bcc908b2fb1366ea958p+0L; /** $(SQRT)2 = 1.414213... */
-enum real SQRT1_2 = SQRT2/2; /** $(SQRT)$(HALF) = 0.707106... */
-// Note: Make sure the magic numbers in compiler backend for x87 match these.
-
-
-/***********************************
- * Calculates the absolute value of a number
- *
- * Params:
- * Num = (template parameter) type of number
- * x = real number value
- * z = complex number value
- * y = imaginary number value
- *
- * Returns:
- * The absolute value of the number. If floating-point or integral,
- * the return type will be the same as the input; if complex or
- * imaginary, the returned value will be the corresponding floating
- * point type.
- *
- * For complex numbers, abs(z) = sqrt( $(POWER z.re, 2) + $(POWER z.im, 2) )
- * = hypot(z.re, z.im).
- */
-Num abs(Num)(Num x) @safe pure nothrow
-if (is(typeof(Num.init >= 0)) && is(typeof(-Num.init)) &&
- !(is(Num* : const(ifloat*)) || is(Num* : const(idouble*))
- || is(Num* : const(ireal*))))
-{
- static if (isFloatingPoint!(Num))
- return fabs(x);
- else
- return x >= 0 ? x : -x;
-}
-
-/// ditto
-auto abs(Num)(Num z) @safe pure nothrow @nogc
-if (is(Num* : const(cfloat*)) || is(Num* : const(cdouble*))
- || is(Num* : const(creal*)))
-{
- return hypot(z.re, z.im);
-}
-
-/// ditto
-auto abs(Num)(Num y) @safe pure nothrow @nogc
-if (is(Num* : const(ifloat*)) || is(Num* : const(idouble*))
- || is(Num* : const(ireal*)))
-{
- return fabs(y.im);
-}
-
-/// ditto
-@safe pure nothrow @nogc unittest
-{
- assert(isIdentical(abs(-0.0L), 0.0L));
- assert(isNaN(abs(real.nan)));
- assert(abs(-real.infinity) == real.infinity);
- assert(abs(-3.2Li) == 3.2L);
- assert(abs(71.6Li) == 71.6L);
- assert(abs(-56) == 56);
- assert(abs(2321312L) == 2321312L);
- assert(abs(-1L+1i) == sqrt(2.0L));
-}
-
-@safe pure nothrow @nogc unittest
-{
- import std.meta : AliasSeq;
- foreach (T; AliasSeq!(float, double, real))
- {
- T f = 3;
- assert(abs(f) == f);
- assert(abs(-f) == f);
- }
- foreach (T; AliasSeq!(cfloat, cdouble, creal))
- {
- T f = -12+3i;
- assert(abs(f) == hypot(f.re, f.im));
- assert(abs(-f) == hypot(f.re, f.im));
- }
-}
-
-/***********************************
- * Complex conjugate
- *
- * conj(x + iy) = x - iy
- *
- * Note that z * conj(z) = $(POWER z.re, 2) - $(POWER z.im, 2)
- * is always a real number
- */
-auto conj(Num)(Num z) @safe pure nothrow @nogc
-if (is(Num* : const(cfloat*)) || is(Num* : const(cdouble*))
- || is(Num* : const(creal*)))
-{
- //FIXME
- //Issue 14206
- static if (is(Num* : const(cdouble*)))
- return cast(cdouble) conj(cast(creal) z);
- else
- return z.re - z.im*1fi;
-}
-
-/** ditto */
-auto conj(Num)(Num y) @safe pure nothrow @nogc
-if (is(Num* : const(ifloat*)) || is(Num* : const(idouble*))
- || is(Num* : const(ireal*)))
-{
- return -y;
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- creal c = 7 + 3Li;
- assert(conj(c) == 7-3Li);
- ireal z = -3.2Li;
- assert(conj(z) == -z);
-}
-//Issue 14206
-@safe pure nothrow @nogc unittest
-{
- cdouble c = 7 + 3i;
- assert(conj(c) == 7-3i);
- idouble z = -3.2i;
- assert(conj(z) == -z);
-}
-//Issue 14206
-@safe pure nothrow @nogc unittest
-{
- cfloat c = 7f + 3fi;
- assert(conj(c) == 7f-3fi);
- ifloat z = -3.2fi;
- assert(conj(z) == -z);
-}
-
-/***********************************
- * Returns cosine of x. x is in radians.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH cos(x)) $(TH invalid?))
- * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) )
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes) )
- * )
- * Bugs:
- * Results are undefined if |x| >= $(POWER 2,64).
- */
-
-real cos(real x) @safe pure nothrow @nogc { pragma(inline, true); return core.math.cos(x); }
-//FIXME
-///ditto
-double cos(double x) @safe pure nothrow @nogc { return cos(cast(real) x); }
-//FIXME
-///ditto
-float cos(float x) @safe pure nothrow @nogc { return cos(cast(real) x); }
-
-@safe unittest
-{
- real function(real) pcos = &cos;
- assert(pcos != null);
-}
-
-/***********************************
- * Returns $(HTTP en.wikipedia.org/wiki/Sine, sine) of x. x is in $(HTTP en.wikipedia.org/wiki/Radian, radians).
- *
- * $(TABLE_SV
- * $(TH3 x , sin(x) , invalid?)
- * $(TD3 $(NAN) , $(NAN) , yes )
- * $(TD3 $(PLUSMN)0.0, $(PLUSMN)0.0, no )
- * $(TD3 $(PLUSMNINF), $(NAN) , yes )
- * )
- *
- * Params:
- * x = angle in radians (not degrees)
- * Returns:
- * sine of x
- * See_Also:
- * $(MYREF cos), $(MYREF tan), $(MYREF asin)
- * Bugs:
- * Results are undefined if |x| >= $(POWER 2,64).
- */
-
-real sin(real x) @safe pure nothrow @nogc { pragma(inline, true); return core.math.sin(x); }
-//FIXME
-///ditto
-double sin(double x) @safe pure nothrow @nogc { return sin(cast(real) x); }
-//FIXME
-///ditto
-float sin(float x) @safe pure nothrow @nogc { return sin(cast(real) x); }
-
-///
-@safe unittest
-{
- import std.math : sin, PI;
- import std.stdio : writefln;
-
- void someFunc()
- {
- real x = 30.0;
- auto result = sin(x * (PI / 180)); // convert degrees to radians
- writefln("The sine of %s degrees is %s", x, result);
- }
-}
-
-@safe unittest
-{
- real function(real) psin = &sin;
- assert(psin != null);
-}
-
-/***********************************
- * Returns sine for complex and imaginary arguments.
- *
- * sin(z) = sin(z.re)*cosh(z.im) + cos(z.re)*sinh(z.im)i
- *
- * If both sin($(THETA)) and cos($(THETA)) are required,
- * it is most efficient to use expi($(THETA)).
- */
-creal sin(creal z) @safe pure nothrow @nogc
-{
- const creal cs = expi(z.re);
- const creal csh = coshisinh(z.im);
- return cs.im * csh.re + cs.re * csh.im * 1i;
-}
-
-/** ditto */
-ireal sin(ireal y) @safe pure nothrow @nogc
-{
- return cosh(y.im)*1i;
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(sin(0.0+0.0i) == 0.0);
- assert(sin(2.0+0.0i) == sin(2.0L) );
-}
-
-/***********************************
- * cosine, complex and imaginary
- *
- * cos(z) = cos(z.re)*cosh(z.im) - sin(z.re)*sinh(z.im)i
- */
-creal cos(creal z) @safe pure nothrow @nogc
-{
- const creal cs = expi(z.re);
- const creal csh = coshisinh(z.im);
- return cs.re * csh.re - cs.im * csh.im * 1i;
-}
-
-/** ditto */
-real cos(ireal y) @safe pure nothrow @nogc
-{
- return cosh(y.im);
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(cos(0.0+0.0i)==1.0);
- assert(cos(1.3L+0.0i)==cos(1.3L));
- assert(cos(5.2Li)== cosh(5.2L));
-}
-
-/****************************************************************************
- * Returns tangent of x. x is in radians.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH tan(x)) $(TH invalid?))
- * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes))
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
- * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD yes))
- * )
- */
-
-real tan(real x) @trusted pure nothrow @nogc
-{
- version (D_InlineAsm_X86)
- {
- asm pure nothrow @nogc
- {
- fld x[EBP] ; // load theta
- fxam ; // test for oddball values
- fstsw AX ;
- sahf ;
- jc trigerr ; // x is NAN, infinity, or empty
- // 387's can handle subnormals
-SC18: fptan ;
- fstsw AX ;
- sahf ;
- jnp Clear1 ; // C2 = 1 (x is out of range)
-
- // Do argument reduction to bring x into range
- fldpi ;
- fxch ;
-SC17: fprem1 ;
- fstsw AX ;
- sahf ;
- jp SC17 ;
- fstp ST(1) ; // remove pi from stack
- jmp SC18 ;
-
-trigerr:
- jnp Lret ; // if theta is NAN, return theta
- fstp ST(0) ; // dump theta
- }
- return real.nan;
-
-Clear1: asm pure nothrow @nogc{
- fstp ST(0) ; // dump X, which is always 1
- }
-
-Lret: {}
- }
- else version (D_InlineAsm_X86_64)
- {
- version (Win64)
- {
- asm pure nothrow @nogc
- {
- fld real ptr [RCX] ; // load theta
- }
- }
- else
- {
- asm pure nothrow @nogc
- {
- fld x[RBP] ; // load theta
- }
- }
- asm pure nothrow @nogc
- {
- fxam ; // test for oddball values
- fstsw AX ;
- test AH,1 ;
- jnz trigerr ; // x is NAN, infinity, or empty
- // 387's can handle subnormals
-SC18: fptan ;
- fstsw AX ;
- test AH,4 ;
- jz Clear1 ; // C2 = 1 (x is out of range)
-
- // Do argument reduction to bring x into range
- fldpi ;
- fxch ;
-SC17: fprem1 ;
- fstsw AX ;
- test AH,4 ;
- jnz SC17 ;
- fstp ST(1) ; // remove pi from stack
- jmp SC18 ;
-
-trigerr:
- test AH,4 ;
- jz Lret ; // if theta is NAN, return theta
- fstp ST(0) ; // dump theta
- }
- return real.nan;
-
-Clear1: asm pure nothrow @nogc{
- fstp ST(0) ; // dump X, which is always 1
- }
-
-Lret: {}
- }
- else
- {
- // Coefficients for tan(x) and PI/4 split into three parts.
- static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
- {
- static immutable real[6] P = [
- 2.883414728874239697964612246732416606301E10L,
- -2.307030822693734879744223131873392503321E9L,
- 5.160188250214037865511600561074819366815E7L,
- -4.249691853501233575668486667664718192660E5L,
- 1.272297782199996882828849455156962260810E3L,
- -9.889929415807650724957118893791829849557E-1L
- ];
- static immutable real[7] Q = [
- 8.650244186622719093893836740197250197602E10L,
- -4.152206921457208101480801635640958361612E10L,
- 2.758476078803232151774723646710890525496E9L,
- -5.733709132766856723608447733926138506824E7L,
- 4.529422062441341616231663543669583527923E5L,
- -1.317243702830553658702531997959756728291E3L,
- 1.0
- ];
-
- enum real P1 =
- 7.853981633974483067550664827649598009884357452392578125E-1L;
- enum real P2 =
- 2.8605943630549158983813312792950660807511260829685741796657E-18L;
- enum real P3 =
- 2.1679525325309452561992610065108379921905808E-35L;
- }
- else
- {
- static immutable real[3] P = [
- -1.7956525197648487798769E7L,
- 1.1535166483858741613983E6L,
- -1.3093693918138377764608E4L,
- ];
- static immutable real[5] Q = [
- -5.3869575592945462988123E7L,
- 2.5008380182335791583922E7L,
- -1.3208923444021096744731E6L,
- 1.3681296347069295467845E4L,
- 1.0000000000000000000000E0L,
- ];
-
- enum real P1 = 7.853981554508209228515625E-1L;
- enum real P2 = 7.946627356147928367136046290398E-9L;
- enum real P3 = 3.061616997868382943065164830688E-17L;
- }
-
- // Special cases.
- if (x == 0.0 || isNaN(x))
- return x;
- if (isInfinity(x))
- return real.nan;
-
- // Make argument positive but save the sign.
- bool sign = false;
- if (signbit(x))
- {
- sign = true;
- x = -x;
- }
-
- // Compute x mod PI/4.
- real y = floor(x / PI_4);
- // Strip high bits of integer part.
- real z = ldexp(y, -4);
- // Compute y - 16 * (y / 16).
- z = y - ldexp(floor(z), 4);
-
- // Integer and fraction part modulo one octant.
- int j = cast(int)(z);
-
- // Map zeros and singularities to origin.
- if (j & 1)
- {
- j += 1;
- y += 1.0;
- }
-
- z = ((x - y * P1) - y * P2) - y * P3;
- const real zz = z * z;
-
- if (zz > 1.0e-20L)
- y = z + z * (zz * poly(zz, P) / poly(zz, Q));
- else
- y = z;
-
- if (j & 2)
- y = -1.0 / y;
-
- return (sign) ? -y : y;
- }
-}
-
-@safe nothrow @nogc unittest
-{
- static real[2][] vals = // angle,tan
- [
- [ 0, 0],
- [ .5, .5463024898],
- [ 1, 1.557407725],
- [ 1.5, 14.10141995],
- [ 2, -2.185039863],
- [ 2.5,-.7470222972],
- [ 3, -.1425465431],
- [ 3.5, .3745856402],
- [ 4, 1.157821282],
- [ 4.5, 4.637332055],
- [ 5, -3.380515006],
- [ 5.5,-.9955840522],
- [ 6, -.2910061914],
- [ 6.5, .2202772003],
- [ 10, .6483608275],
-
- // special angles
- [ PI_4, 1],
- //[ PI_2, real.infinity], // PI_2 is not _exactly_ pi/2.
- [ 3*PI_4, -1],
- [ PI, 0],
- [ 5*PI_4, 1],
- //[ 3*PI_2, -real.infinity],
- [ 7*PI_4, -1],
- [ 2*PI, 0],
- ];
- int i;
-
- for (i = 0; i < vals.length; i++)
- {
- real x = vals[i][0];
- real r = vals[i][1];
- real t = tan(x);
-
- //printf("tan(%Lg) = %Lg, should be %Lg\n", x, t, r);
- assert(approxEqual(r, t));
-
- x = -x;
- r = -r;
- t = tan(x);
- //printf("tan(%Lg) = %Lg, should be %Lg\n", x, t, r);
- assert(approxEqual(r, t));
- }
- // overflow
- assert(isNaN(tan(real.infinity)));
- assert(isNaN(tan(-real.infinity)));
- // NaN propagation
- assert(isIdentical( tan(NaN(0x0123L)), NaN(0x0123L) ));
-}
-
-@system unittest
-{
- assert(equalsDigit(tan(PI / 3), std.math.sqrt(3.0), useDigits));
-}
-
-/***************
- * Calculates the arc cosine of x,
- * returning a value ranging from 0 to $(PI).
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH acos(x)) $(TH invalid?))
- * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes))
- * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes))
- * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes))
- * )
- */
-real acos(real x) @safe pure nothrow @nogc
-{
- return atan2(sqrt(1-x*x), x);
-}
-
-/// ditto
-double acos(double x) @safe pure nothrow @nogc { return acos(cast(real) x); }
-
-/// ditto
-float acos(float x) @safe pure nothrow @nogc { return acos(cast(real) x); }
-
-@system unittest
-{
- assert(equalsDigit(acos(0.5), std.math.PI / 3, useDigits));
-}
-
-/***************
- * Calculates the arc sine of x,
- * returning a value ranging from -$(PI)/2 to $(PI)/2.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH asin(x)) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
- * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes))
- * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes))
- * )
- */
-real asin(real x) @safe pure nothrow @nogc
-{
- return atan2(x, sqrt(1-x*x));
-}
-
-/// ditto
-double asin(double x) @safe pure nothrow @nogc { return asin(cast(real) x); }
-
-/// ditto
-float asin(float x) @safe pure nothrow @nogc { return asin(cast(real) x); }
-
-@system unittest
-{
- assert(asin(0.5).approxEqual(PI / 6));
-}
-
-/***************
- * Calculates the arc tangent of x,
- * returning a value ranging from -$(PI)/2 to $(PI)/2.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH atan(x)) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes))
- * )
- */
-real atan(real x) @safe pure nothrow @nogc
-{
- version (InlineAsm_X86_Any)
- {
- return atan2(x, 1.0L);
- }
- else
- {
- // Coefficients for atan(x)
- static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
- {
- static immutable real[9] P = [
- -6.880597774405940432145577545328795037141E2L,
- -2.514829758941713674909996882101723647996E3L,
- -3.696264445691821235400930243493001671932E3L,
- -2.792272753241044941703278827346430350236E3L,
- -1.148164399808514330375280133523543970854E3L,
- -2.497759878476618348858065206895055957104E2L,
- -2.548067867495502632615671450650071218995E1L,
- -8.768423468036849091777415076702113400070E-1L,
- -6.635810778635296712545011270011752799963E-4L
- ];
- static immutable real[9] Q = [
- 2.064179332321782129643673263598686441900E3L,
- 8.782996876218210302516194604424986107121E3L,
- 1.547394317752562611786521896296215170819E4L,
- 1.458510242529987155225086911411015961174E4L,
- 7.928572347062145288093560392463784743935E3L,
- 2.494680540950601626662048893678584497900E3L,
- 4.308348370818927353321556740027020068897E2L,
- 3.566239794444800849656497338030115886153E1L,
- 1.0
- ];
- }
- else
- {
- static immutable real[5] P = [
- -5.0894116899623603312185E1L,
- -9.9988763777265819915721E1L,
- -6.3976888655834347413154E1L,
- -1.4683508633175792446076E1L,
- -8.6863818178092187535440E-1L,
- ];
- static immutable real[6] Q = [
- 1.5268235069887081006606E2L,
- 3.9157570175111990631099E2L,
- 3.6144079386152023162701E2L,
- 1.4399096122250781605352E2L,
- 2.2981886733594175366172E1L,
- 1.0000000000000000000000E0L,
- ];
- }
-
- // tan(PI/8)
- enum real TAN_PI_8 = 0.414213562373095048801688724209698078569672L;
- // tan(3 * PI/8)
- enum real TAN3_PI_8 = 2.414213562373095048801688724209698078569672L;
-
- // Special cases.
- if (x == 0.0)
- return x;
- if (isInfinity(x))
- return copysign(PI_2, x);
-
- // Make argument positive but save the sign.
- bool sign = false;
- if (signbit(x))
- {
- sign = true;
- x = -x;
- }
-
- // Range reduction.
- real y;
- if (x > TAN3_PI_8)
- {
- y = PI_2;
- x = -(1.0 / x);
- }
- else if (x > TAN_PI_8)
- {
- y = PI_4;
- x = (x - 1.0)/(x + 1.0);
- }
- else
- y = 0.0;
-
- // Rational form in x^^2.
- const real z = x * x;
- y = y + (poly(z, P) / poly(z, Q)) * z * x + x;
-
- return (sign) ? -y : y;
- }
-}
-
-/// ditto
-double atan(double x) @safe pure nothrow @nogc { return atan(cast(real) x); }
-
-/// ditto
-float atan(float x) @safe pure nothrow @nogc { return atan(cast(real) x); }
-
-@system unittest
-{
- assert(equalsDigit(atan(std.math.sqrt(3.0)), PI / 3, useDigits));
-}
-
-/***************
- * Calculates the arc tangent of y / x,
- * returning a value ranging from -$(PI) to $(PI).
- *
- * $(TABLE_SV
- * $(TR $(TH y) $(TH x) $(TH atan(y, x)))
- * $(TR $(TD $(NAN)) $(TD anything) $(TD $(NAN)) )
- * $(TR $(TD anything) $(TD $(NAN)) $(TD $(NAN)) )
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) )
- * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) $(TD $(PLUSMN)0.0) )
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT)0.0) $(TD $(PLUSMN)$(PI)))
- * $(TR $(TD $(PLUSMN)0.0) $(TD -0.0) $(TD $(PLUSMN)$(PI)))
- * $(TR $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) $(TD $(PI)/2) )
- * $(TR $(TD $(LT)0.0) $(TD $(PLUSMN)0.0) $(TD -$(PI)/2) )
- * $(TR $(TD $(GT)0.0) $(TD $(INFIN)) $(TD $(PLUSMN)0.0) )
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD anything) $(TD $(PLUSMN)$(PI)/2))
- * $(TR $(TD $(GT)0.0) $(TD -$(INFIN)) $(TD $(PLUSMN)$(PI)) )
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(INFIN)) $(TD $(PLUSMN)$(PI)/4))
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD -$(INFIN)) $(TD $(PLUSMN)3$(PI)/4))
- * )
- */
-real atan2(real y, real x) @trusted pure nothrow @nogc
-{
- version (InlineAsm_X86_Any)
- {
- version (Win64)
- {
- asm pure nothrow @nogc {
- naked;
- fld real ptr [RDX]; // y
- fld real ptr [RCX]; // x
- fpatan;
- ret;
- }
- }
- else
- {
- asm pure nothrow @nogc {
- fld y;
- fld x;
- fpatan;
- }
- }
- }
- else
- {
- // Special cases.
- if (isNaN(x) || isNaN(y))
- return real.nan;
- if (y == 0.0)
- {
- if (x >= 0 && !signbit(x))
- return copysign(0, y);
- else
- return copysign(PI, y);
- }
- if (x == 0.0)
- return copysign(PI_2, y);
- if (isInfinity(x))
- {
- if (signbit(x))
- {
- if (isInfinity(y))
- return copysign(3*PI_4, y);
- else
- return copysign(PI, y);
- }
- else
- {
- if (isInfinity(y))
- return copysign(PI_4, y);
- else
- return copysign(0.0, y);
- }
- }
- if (isInfinity(y))
- return copysign(PI_2, y);
-
- // Call atan and determine the quadrant.
- real z = atan(y / x);
-
- if (signbit(x))
- {
- if (signbit(y))
- z = z - PI;
- else
- z = z + PI;
- }
-
- if (z == 0.0)
- return copysign(z, y);
-
- return z;
- }
-}
-
-/// ditto
-double atan2(double y, double x) @safe pure nothrow @nogc
-{
- return atan2(cast(real) y, cast(real) x);
-}
-
-/// ditto
-float atan2(float y, float x) @safe pure nothrow @nogc
-{
- return atan2(cast(real) y, cast(real) x);
-}
-
-@system unittest
-{
- assert(atan2(1.0, sqrt(3.0)).approxEqual(PI / 6));
-}
-
-/***********************************
- * Calculates the hyperbolic cosine of x.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH cosh(x)) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)0.0) $(TD no) )
- * )
- */
-real cosh(real x) @safe pure nothrow @nogc
-{
- // cosh = (exp(x)+exp(-x))/2.
- // The naive implementation works correctly.
- const real y = exp(x);
- return (y + 1.0/y) * 0.5;
-}
-
-/// ditto
-double cosh(double x) @safe pure nothrow @nogc { return cosh(cast(real) x); }
-
-/// ditto
-float cosh(float x) @safe pure nothrow @nogc { return cosh(cast(real) x); }
-
-@system unittest
-{
- assert(equalsDigit(cosh(1.0), (E + 1.0 / E) / 2, useDigits));
-}
-
-/***********************************
- * Calculates the hyperbolic sine of x.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH sinh(x)) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no))
- * )
- */
-real sinh(real x) @safe pure nothrow @nogc
-{
- // sinh(x) = (exp(x)-exp(-x))/2;
- // Very large arguments could cause an overflow, but
- // the maximum value of x for which exp(x) + exp(-x)) != exp(x)
- // is x = 0.5 * (real.mant_dig) * LN2. // = 22.1807 for real80.
- if (fabs(x) > real.mant_dig * LN2)
- {
- return copysign(0.5 * exp(fabs(x)), x);
- }
-
- const real y = expm1(x);
- return 0.5 * y / (y+1) * (y+2);
-}
-
-/// ditto
-double sinh(double x) @safe pure nothrow @nogc { return sinh(cast(real) x); }
-
-/// ditto
-float sinh(float x) @safe pure nothrow @nogc { return sinh(cast(real) x); }
-
-@system unittest
-{
- assert(sinh(1.0).approxEqual((E - 1.0 / E) / 2));
-}
-
-/***********************************
- * Calculates the hyperbolic tangent of x.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH tanh(x)) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) )
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)1.0) $(TD no))
- * )
- */
-real tanh(real x) @safe pure nothrow @nogc
-{
- // tanh(x) = (exp(x) - exp(-x))/(exp(x)+exp(-x))
- if (fabs(x) > real.mant_dig * LN2)
- {
- return copysign(1, x);
- }
-
- const real y = expm1(2*x);
- return y / (y + 2);
-}
-
-/// ditto
-double tanh(double x) @safe pure nothrow @nogc { return tanh(cast(real) x); }
-
-/// ditto
-float tanh(float x) @safe pure nothrow @nogc { return tanh(cast(real) x); }
-
-@system unittest
-{
- assert(equalsDigit(tanh(1.0), sinh(1.0) / cosh(1.0), 15));
-}
-
-package:
-
-/* Returns cosh(x) + I * sinh(x)
- * Only one call to exp() is performed.
- */
-creal coshisinh(real x) @safe pure nothrow @nogc
-{
- // See comments for cosh, sinh.
- if (fabs(x) > real.mant_dig * LN2)
- {
- const real y = exp(fabs(x));
- return y * 0.5 + 0.5i * copysign(y, x);
- }
- else
- {
- const real y = expm1(x);
- return (y + 1.0 + 1.0/(y + 1.0)) * 0.5 + 0.5i * y / (y+1) * (y+2);
- }
-}
-
-@safe pure nothrow @nogc unittest
-{
- creal c = coshisinh(3.0L);
- assert(c.re == cosh(3.0L));
- assert(c.im == sinh(3.0L));
-}
-
-public:
-
-/***********************************
- * Calculates the inverse hyperbolic cosine of x.
- *
- * Mathematically, acosh(x) = log(x + sqrt( x*x - 1))
- *
- * $(TABLE_DOMRG
- * $(DOMAIN 1..$(INFIN)),
- * $(RANGE 0..$(INFIN))
- * )
- *
- * $(TABLE_SV
- * $(SVH x, acosh(x) )
- * $(SV $(NAN), $(NAN) )
- * $(SV $(LT)1, $(NAN) )
- * $(SV 1, 0 )
- * $(SV +$(INFIN),+$(INFIN))
- * )
- */
-real acosh(real x) @safe pure nothrow @nogc
-{
- if (x > 1/real.epsilon)
- return LN2 + log(x);
- else
- return log(x + sqrt(x*x - 1));
-}
-
-/// ditto
-double acosh(double x) @safe pure nothrow @nogc { return acosh(cast(real) x); }
-
-/// ditto
-float acosh(float x) @safe pure nothrow @nogc { return acosh(cast(real) x); }
-
-
-@system unittest
-{
- assert(isNaN(acosh(0.9)));
- assert(isNaN(acosh(real.nan)));
- assert(acosh(1.0)==0.0);
- assert(acosh(real.infinity) == real.infinity);
- assert(isNaN(acosh(0.5)));
- assert(equalsDigit(acosh(cosh(3.0)), 3, useDigits));
-}
-
-/***********************************
- * Calculates the inverse hyperbolic sine of x.
- *
- * Mathematically,
- * ---------------
- * asinh(x) = log( x + sqrt( x*x + 1 )) // if x >= +0
- * asinh(x) = -log(-x + sqrt( x*x + 1 )) // if x <= -0
- * -------------
- *
- * $(TABLE_SV
- * $(SVH x, asinh(x) )
- * $(SV $(NAN), $(NAN) )
- * $(SV $(PLUSMN)0, $(PLUSMN)0 )
- * $(SV $(PLUSMN)$(INFIN),$(PLUSMN)$(INFIN))
- * )
- */
-real asinh(real x) @safe pure nothrow @nogc
-{
- return (fabs(x) > 1 / real.epsilon)
- // beyond this point, x*x + 1 == x*x
- ? copysign(LN2 + log(fabs(x)), x)
- // sqrt(x*x + 1) == 1 + x * x / ( 1 + sqrt(x*x + 1) )
- : copysign(log1p(fabs(x) + x*x / (1 + sqrt(x*x + 1)) ), x);
-}
-
-/// ditto
-double asinh(double x) @safe pure nothrow @nogc { return asinh(cast(real) x); }
-
-/// ditto
-float asinh(float x) @safe pure nothrow @nogc { return asinh(cast(real) x); }
-
-@system unittest
-{
- assert(isIdentical(asinh(0.0), 0.0));
- assert(isIdentical(asinh(-0.0), -0.0));
- assert(asinh(real.infinity) == real.infinity);
- assert(asinh(-real.infinity) == -real.infinity);
- assert(isNaN(asinh(real.nan)));
- assert(equalsDigit(asinh(sinh(3.0)), 3, useDigits));
-}
-
-/***********************************
- * Calculates the inverse hyperbolic tangent of x,
- * returning a value from ranging from -1 to 1.
- *
- * Mathematically, atanh(x) = log( (1+x)/(1-x) ) / 2
- *
- * $(TABLE_DOMRG
- * $(DOMAIN -$(INFIN)..$(INFIN)),
- * $(RANGE -1 .. 1)
- * )
- * $(BR)
- * $(TABLE_SV
- * $(SVH x, acosh(x) )
- * $(SV $(NAN), $(NAN) )
- * $(SV $(PLUSMN)0, $(PLUSMN)0)
- * $(SV -$(INFIN), -0)
- * )
- */
-real atanh(real x) @safe pure nothrow @nogc
-{
- // log( (1+x)/(1-x) ) == log ( 1 + (2*x)/(1-x) )
- return 0.5 * log1p( 2 * x / (1 - x) );
-}
-
-/// ditto
-double atanh(double x) @safe pure nothrow @nogc { return atanh(cast(real) x); }
-
-/// ditto
-float atanh(float x) @safe pure nothrow @nogc { return atanh(cast(real) x); }
-
-
-@system unittest
-{
- assert(isIdentical(atanh(0.0), 0.0));
- assert(isIdentical(atanh(-0.0),-0.0));
- assert(isNaN(atanh(real.nan)));
- assert(isNaN(atanh(-real.infinity)));
- assert(atanh(0.0) == 0);
- assert(equalsDigit(atanh(tanh(0.5L)), 0.5, useDigits));
-}
-
-/*****************************************
- * Returns x rounded to a long value using the current rounding mode.
- * If the integer value of x is
- * greater than long.max, the result is
- * indeterminate.
- */
-long rndtol(real x) @nogc @safe pure nothrow { pragma(inline, true); return core.math.rndtol(x); }
-//FIXME
-///ditto
-long rndtol(double x) @safe pure nothrow @nogc { return rndtol(cast(real) x); }
-//FIXME
-///ditto
-long rndtol(float x) @safe pure nothrow @nogc { return rndtol(cast(real) x); }
-
-@safe unittest
-{
- long function(real) prndtol = &rndtol;
- assert(prndtol != null);
-}
-
-/*****************************************
- * Returns x rounded to a long value using the FE_TONEAREST rounding mode.
- * If the integer value of x is
- * greater than long.max, the result is
- * indeterminate.
- */
-extern (C) real rndtonl(real x);
-
-/***************************************
- * Compute square root of x.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH sqrt(x)) $(TH invalid?))
- * $(TR $(TD -0.0) $(TD -0.0) $(TD no))
- * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD yes))
- * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no))
- * )
- */
-float sqrt(float x) @nogc @safe pure nothrow { pragma(inline, true); return core.math.sqrt(x); }
-
-/// ditto
-double sqrt(double x) @nogc @safe pure nothrow { pragma(inline, true); return core.math.sqrt(x); }
-
-/// ditto
-real sqrt(real x) @nogc @safe pure nothrow { pragma(inline, true); return core.math.sqrt(x); }
-
-@safe pure nothrow @nogc unittest
-{
- //ctfe
- enum ZX80 = sqrt(7.0f);
- enum ZX81 = sqrt(7.0);
- enum ZX82 = sqrt(7.0L);
-
- assert(isNaN(sqrt(-1.0f)));
- assert(isNaN(sqrt(-1.0)));
- assert(isNaN(sqrt(-1.0L)));
-}
-
-@safe unittest
-{
- float function(float) psqrtf = &sqrt;
- assert(psqrtf != null);
- double function(double) psqrtd = &sqrt;
- assert(psqrtd != null);
- real function(real) psqrtr = &sqrt;
- assert(psqrtr != null);
-}
-
-creal sqrt(creal z) @nogc @safe pure nothrow
-{
- creal c;
- real x,y,w,r;
-
- if (z == 0)
- {
- c = 0 + 0i;
- }
- else
- {
- const real z_re = z.re;
- const real z_im = z.im;
-
- x = fabs(z_re);
- y = fabs(z_im);
- if (x >= y)
- {
- r = y / x;
- w = sqrt(x) * sqrt(0.5 * (1 + sqrt(1 + r * r)));
- }
- else
- {
- r = x / y;
- w = sqrt(y) * sqrt(0.5 * (r + sqrt(1 + r * r)));
- }
-
- if (z_re >= 0)
- {
- c = w + (z_im / (w + w)) * 1.0i;
- }
- else
- {
- if (z_im < 0)
- w = -w;
- c = z_im / (w + w) + w * 1.0i;
- }
- }
- return c;
-}
-
-/**
- * Calculates e$(SUPERSCRIPT x).
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH e$(SUPERSCRIPT x)) )
- * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) )
- * $(TR $(TD -$(INFIN)) $(TD +0.0) )
- * $(TR $(TD $(NAN)) $(TD $(NAN)) )
- * )
- */
-real exp(real x) @trusted pure nothrow @nogc
-{
- version (D_InlineAsm_X86)
- {
- // e^^x = 2^^(LOG2E*x)
- // (This is valid because the overflow & underflow limits for exp
- // and exp2 are so similar).
- return exp2(LOG2E*x);
- }
- else version (D_InlineAsm_X86_64)
- {
- // e^^x = 2^^(LOG2E*x)
- // (This is valid because the overflow & underflow limits for exp
- // and exp2 are so similar).
- return exp2(LOG2E*x);
- }
- else
- {
- alias F = floatTraits!real;
- static if (F.realFormat == RealFormat.ieeeDouble)
- {
- // Coefficients for exp(x)
- static immutable real[3] P = [
- 9.99999999999999999910E-1L,
- 3.02994407707441961300E-2L,
- 1.26177193074810590878E-4L,
- ];
- static immutable real[4] Q = [
- 2.00000000000000000009E0L,
- 2.27265548208155028766E-1L,
- 2.52448340349684104192E-3L,
- 3.00198505138664455042E-6L,
- ];
-
- // C1 + C2 = LN2.
- enum real C1 = 6.93145751953125E-1;
- enum real C2 = 1.42860682030941723212E-6;
-
- // Overflow and Underflow limits.
- enum real OF = 7.09782712893383996732E2; // ln((1-2^-53) * 2^1024)
- enum real UF = -7.451332191019412076235E2; // ln(2^-1075)
- }
- else static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- // Coefficients for exp(x)
- static immutable real[3] P = [
- 9.9999999999999999991025E-1L,
- 3.0299440770744196129956E-2L,
- 1.2617719307481059087798E-4L,
- ];
- static immutable real[4] Q = [
- 2.0000000000000000000897E0L,
- 2.2726554820815502876593E-1L,
- 2.5244834034968410419224E-3L,
- 3.0019850513866445504159E-6L,
- ];
-
- // C1 + C2 = LN2.
- enum real C1 = 6.9314575195312500000000E-1L;
- enum real C2 = 1.4286068203094172321215E-6L;
-
- // Overflow and Underflow limits.
- enum real OF = 1.1356523406294143949492E4L; // ln((1-2^-64) * 2^16384)
- enum real UF = -1.13994985314888605586758E4L; // ln(2^-16446)
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- // Coefficients for exp(x) - 1
- static immutable real[5] P = [
- 9.999999999999999999999999999999999998502E-1L,
- 3.508710990737834361215404761139478627390E-2L,
- 2.708775201978218837374512615596512792224E-4L,
- 6.141506007208645008909088812338454698548E-7L,
- 3.279723985560247033712687707263393506266E-10L
- ];
- static immutable real[6] Q = [
- 2.000000000000000000000000000000000000150E0,
- 2.368408864814233538909747618894558968880E-1L,
- 3.611828913847589925056132680618007270344E-3L,
- 1.504792651814944826817779302637284053660E-5L,
- 1.771372078166251484503904874657985291164E-8L,
- 2.980756652081995192255342779918052538681E-12L
- ];
-
- // C1 + C2 = LN2.
- enum real C1 = 6.93145751953125E-1L;
- enum real C2 = 1.428606820309417232121458176568075500134E-6L;
-
- // Overflow and Underflow limits.
- enum real OF = 1.135583025911358400418251384584930671458833e4L;
- enum real UF = -1.143276959615573793352782661133116431383730e4L;
- }
- else
- static assert(0, "Not implemented for this architecture");
-
- // Special cases. Raises an overflow or underflow flag accordingly,
- // except in the case for CTFE, where there are no hardware controls.
- if (isNaN(x))
- return x;
- if (x > OF)
- return real.infinity;
- if (x < UF)
- return 0.0;
-
- // Express: e^^x = e^^g * 2^^n
- // = e^^g * e^^(n * LOG2E)
- // = e^^(g + n * LOG2E)
- int n = cast(int) floor(LOG2E * x + 0.5);
- x -= n * C1;
- x -= n * C2;
-
- // Rational approximation for exponential of the fractional part:
- // e^^x = 1 + 2x P(x^^2) / (Q(x^^2) - P(x^^2))
- const real xx = x * x;
- const real px = x * poly(xx, P);
- x = px / (poly(xx, Q) - px);
- x = 1.0 + ldexp(x, 1);
-
- // Scale by power of 2.
- x = ldexp(x, n);
-
- return x;
- }
-}
-
-/// ditto
-double exp(double x) @safe pure nothrow @nogc { return exp(cast(real) x); }
-
-/// ditto
-float exp(float x) @safe pure nothrow @nogc { return exp(cast(real) x); }
-
-@system unittest
-{
- assert(exp(3.0).feqrel(E * E * E) > 16);
-}
-
-/**
- * Calculates the value of the natural logarithm base (e)
- * raised to the power of x, minus 1.
- *
- * For very small x, expm1(x) is more accurate
- * than exp(x)-1.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH e$(SUPERSCRIPT x)-1) )
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) )
- * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) )
- * $(TR $(TD -$(INFIN)) $(TD -1.0) )
- * $(TR $(TD $(NAN)) $(TD $(NAN)) )
- * )
- */
-real expm1(real x) @trusted pure nothrow @nogc
-{
- version (D_InlineAsm_X86)
- {
- enum PARAMSIZE = (real.sizeof+3)&(0xFFFF_FFFC); // always a multiple of 4
- asm pure nothrow @nogc
- {
- /* expm1() for x87 80-bit reals, IEEE754-2008 conformant.
- * Author: Don Clugston.
- *
- * expm1(x) = 2^^(rndint(y))* 2^^(y-rndint(y)) - 1 where y = LN2*x.
- * = 2rndy * 2ym1 + 2rndy - 1, where 2rndy = 2^^(rndint(y))
- * and 2ym1 = (2^^(y-rndint(y))-1).
- * If 2rndy < 0.5*real.epsilon, result is -1.
- * Implementation is otherwise the same as for exp2()
- */
- naked;
- fld real ptr [ESP+4] ; // x
- mov AX, [ESP+4+8]; // AX = exponent and sign
- sub ESP, 12+8; // Create scratch space on the stack
- // [ESP,ESP+2] = scratchint
- // [ESP+4..+6, +8..+10, +10] = scratchreal
- // set scratchreal mantissa = 1.0
- mov dword ptr [ESP+8], 0;
- mov dword ptr [ESP+8+4], 0x80000000;
- and AX, 0x7FFF; // drop sign bit
- cmp AX, 0x401D; // avoid InvalidException in fist
- jae L_extreme;
- fldl2e;
- fmulp ST(1), ST; // y = x*log2(e)
- fist dword ptr [ESP]; // scratchint = rndint(y)
- fisub dword ptr [ESP]; // y - rndint(y)
- // and now set scratchreal exponent
- mov EAX, [ESP];
- add EAX, 0x3fff;
- jle short L_largenegative;
- cmp EAX,0x8000;
- jge short L_largepositive;
- mov [ESP+8+8],AX;
- f2xm1; // 2ym1 = 2^^(y-rndint(y)) -1
- fld real ptr [ESP+8] ; // 2rndy = 2^^rndint(y)
- fmul ST(1), ST; // ST=2rndy, ST(1)=2rndy*2ym1
- fld1;
- fsubp ST(1), ST; // ST = 2rndy-1, ST(1) = 2rndy * 2ym1 - 1
- faddp ST(1), ST; // ST = 2rndy * 2ym1 + 2rndy - 1
- add ESP,12+8;
- ret PARAMSIZE;
-
-L_extreme: // Extreme exponent. X is very large positive, very
- // large negative, infinity, or NaN.
- fxam;
- fstsw AX;
- test AX, 0x0400; // NaN_or_zero, but we already know x != 0
- jz L_was_nan; // if x is NaN, returns x
- test AX, 0x0200;
- jnz L_largenegative;
-L_largepositive:
- // Set scratchreal = real.max.
- // squaring it will create infinity, and set overflow flag.
- mov word ptr [ESP+8+8], 0x7FFE;
- fstp ST(0);
- fld real ptr [ESP+8]; // load scratchreal
- fmul ST(0), ST; // square it, to create havoc!
-L_was_nan:
- add ESP,12+8;
- ret PARAMSIZE;
-L_largenegative:
- fstp ST(0);
- fld1;
- fchs; // return -1. Underflow flag is not set.
- add ESP,12+8;
- ret PARAMSIZE;
- }
- }
- else version (D_InlineAsm_X86_64)
- {
- asm pure nothrow @nogc
- {
- naked;
- }
- version (Win64)
- {
- asm pure nothrow @nogc
- {
- fld real ptr [RCX]; // x
- mov AX,[RCX+8]; // AX = exponent and sign
- }
- }
- else
- {
- asm pure nothrow @nogc
- {
- fld real ptr [RSP+8]; // x
- mov AX,[RSP+8+8]; // AX = exponent and sign
- }
- }
- asm pure nothrow @nogc
- {
- /* expm1() for x87 80-bit reals, IEEE754-2008 conformant.
- * Author: Don Clugston.
- *
- * expm1(x) = 2^(rndint(y))* 2^(y-rndint(y)) - 1 where y = LN2*x.
- * = 2rndy * 2ym1 + 2rndy - 1, where 2rndy = 2^(rndint(y))
- * and 2ym1 = (2^(y-rndint(y))-1).
- * If 2rndy < 0.5*real.epsilon, result is -1.
- * Implementation is otherwise the same as for exp2()
- */
- sub RSP, 24; // Create scratch space on the stack
- // [RSP,RSP+2] = scratchint
- // [RSP+4..+6, +8..+10, +10] = scratchreal
- // set scratchreal mantissa = 1.0
- mov dword ptr [RSP+8], 0;
- mov dword ptr [RSP+8+4], 0x80000000;
- and AX, 0x7FFF; // drop sign bit
- cmp AX, 0x401D; // avoid InvalidException in fist
- jae L_extreme;
- fldl2e;
- fmul ; // y = x*log2(e)
- fist dword ptr [RSP]; // scratchint = rndint(y)
- fisub dword ptr [RSP]; // y - rndint(y)
- // and now set scratchreal exponent
- mov EAX, [RSP];
- add EAX, 0x3fff;
- jle short L_largenegative;
- cmp EAX,0x8000;
- jge short L_largepositive;
- mov [RSP+8+8],AX;
- f2xm1; // 2^(y-rndint(y)) -1
- fld real ptr [RSP+8] ; // 2^rndint(y)
- fmul ST(1), ST;
- fld1;
- fsubp ST(1), ST;
- fadd;
- add RSP,24;
- ret;
-
-L_extreme: // Extreme exponent. X is very large positive, very
- // large negative, infinity, or NaN.
- fxam;
- fstsw AX;
- test AX, 0x0400; // NaN_or_zero, but we already know x != 0
- jz L_was_nan; // if x is NaN, returns x
- test AX, 0x0200;
- jnz L_largenegative;
-L_largepositive:
- // Set scratchreal = real.max.
- // squaring it will create infinity, and set overflow flag.
- mov word ptr [RSP+8+8], 0x7FFE;
- fstp ST(0);
- fld real ptr [RSP+8]; // load scratchreal
- fmul ST(0), ST; // square it, to create havoc!
-L_was_nan:
- add RSP,24;
- ret;
-
-L_largenegative:
- fstp ST(0);
- fld1;
- fchs; // return -1. Underflow flag is not set.
- add RSP,24;
- ret;
- }
- }
- else
- {
- // Coefficients for exp(x) - 1 and overflow/underflow limits.
- static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
- {
- static immutable real[8] P = [
- 2.943520915569954073888921213330863757240E8L,
- -5.722847283900608941516165725053359168840E7L,
- 8.944630806357575461578107295909719817253E6L,
- -7.212432713558031519943281748462837065308E5L,
- 4.578962475841642634225390068461943438441E4L,
- -1.716772506388927649032068540558788106762E3L,
- 4.401308817383362136048032038528753151144E1L,
- -4.888737542888633647784737721812546636240E-1L
- ];
-
- static immutable real[9] Q = [
- 1.766112549341972444333352727998584753865E9L,
- -7.848989743695296475743081255027098295771E8L,
- 1.615869009634292424463780387327037251069E8L,
- -2.019684072836541751428967854947019415698E7L,
- 1.682912729190313538934190635536631941751E6L,
- -9.615511549171441430850103489315371768998E4L,
- 3.697714952261803935521187272204485251835E3L,
- -8.802340681794263968892934703309274564037E1L,
- 1.0
- ];
-
- enum real OF = 1.1356523406294143949491931077970764891253E4L;
- enum real UF = -1.143276959615573793352782661133116431383730e4L;
- }
- else
- {
- static immutable real[5] P = [
- -1.586135578666346600772998894928250240826E4L,
- 2.642771505685952966904660652518429479531E3L,
- -3.423199068835684263987132888286791620673E2L,
- 1.800826371455042224581246202420972737840E1L,
- -5.238523121205561042771939008061958820811E-1L,
- ];
- static immutable real[6] Q = [
- -9.516813471998079611319047060563358064497E4L,
- 3.964866271411091674556850458227710004570E4L,
- -7.207678383830091850230366618190187434796E3L,
- 7.206038318724600171970199625081491823079E2L,
- -4.002027679107076077238836622982900945173E1L,
- 1.0
- ];
-
- enum real OF = 1.1356523406294143949492E4L;
- enum real UF = -4.5054566736396445112120088E1L;
- }
-
-
- // C1 + C2 = LN2.
- enum real C1 = 6.9314575195312500000000E-1L;
- enum real C2 = 1.428606820309417232121458176568075500134E-6L;
-
- // Special cases. Raises an overflow flag, except in the case
- // for CTFE, where there are no hardware controls.
- if (x > OF)
- return real.infinity;
- if (x == 0.0)
- return x;
- if (x < UF)
- return -1.0;
-
- // Express x = LN2 (n + remainder), remainder not exceeding 1/2.
- int n = cast(int) floor(0.5 + x / LN2);
- x -= n * C1;
- x -= n * C2;
-
- // Rational approximation:
- // exp(x) - 1 = x + 0.5 x^^2 + x^^3 P(x) / Q(x)
- real px = x * poly(x, P);
- real qx = poly(x, Q);
- const real xx = x * x;
- qx = x + (0.5 * xx + xx * px / qx);
-
- // We have qx = exp(remainder LN2) - 1, so:
- // exp(x) - 1 = 2^^n (qx + 1) - 1 = 2^^n qx + 2^^n - 1.
- px = ldexp(1.0, n);
- x = px * qx + (px - 1.0);
-
- return x;
- }
-}
-
-
-
-/**
- * Calculates 2$(SUPERSCRIPT x).
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH exp2(x)) )
- * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) )
- * $(TR $(TD -$(INFIN)) $(TD +0.0) )
- * $(TR $(TD $(NAN)) $(TD $(NAN)) )
- * )
- */
-pragma(inline, true)
-real exp2(real x) @nogc @trusted pure nothrow
-{
- version (InlineAsm_X86_Any)
- {
- if (!__ctfe)
- return exp2Asm(x);
- else
- return exp2Impl(x);
- }
- else
- {
- return exp2Impl(x);
- }
-}
-
-version (InlineAsm_X86_Any)
-private real exp2Asm(real x) @nogc @trusted pure nothrow
-{
- version (D_InlineAsm_X86)
- {
- enum PARAMSIZE = (real.sizeof+3)&(0xFFFF_FFFC); // always a multiple of 4
-
- asm pure nothrow @nogc
- {
- /* exp2() for x87 80-bit reals, IEEE754-2008 conformant.
- * Author: Don Clugston.
- *
- * exp2(x) = 2^^(rndint(x))* 2^^(y-rndint(x))
- * The trick for high performance is to avoid the fscale(28cycles on core2),
- * frndint(19 cycles), leaving f2xm1(19 cycles) as the only slow instruction.
- *
- * We can do frndint by using fist. BUT we can't use it for huge numbers,
- * because it will set the Invalid Operation flag if overflow or NaN occurs.
- * Fortunately, whenever this happens the result would be zero or infinity.
- *
- * We can perform fscale by directly poking into the exponent. BUT this doesn't
- * work for the (very rare) cases where the result is subnormal. So we fall back
- * to the slow method in that case.
- */
- naked;
- fld real ptr [ESP+4] ; // x
- mov AX, [ESP+4+8]; // AX = exponent and sign
- sub ESP, 12+8; // Create scratch space on the stack
- // [ESP,ESP+2] = scratchint
- // [ESP+4..+6, +8..+10, +10] = scratchreal
- // set scratchreal mantissa = 1.0
- mov dword ptr [ESP+8], 0;
- mov dword ptr [ESP+8+4], 0x80000000;
- and AX, 0x7FFF; // drop sign bit
- cmp AX, 0x401D; // avoid InvalidException in fist
- jae L_extreme;
- fist dword ptr [ESP]; // scratchint = rndint(x)
- fisub dword ptr [ESP]; // x - rndint(x)
- // and now set scratchreal exponent
- mov EAX, [ESP];
- add EAX, 0x3fff;
- jle short L_subnormal;
- cmp EAX,0x8000;
- jge short L_overflow;
- mov [ESP+8+8],AX;
-L_normal:
- f2xm1;
- fld1;
- faddp ST(1), ST; // 2^^(x-rndint(x))
- fld real ptr [ESP+8] ; // 2^^rndint(x)
- add ESP,12+8;
- fmulp ST(1), ST;
- ret PARAMSIZE;
-
-L_subnormal:
- // Result will be subnormal.
- // In this rare case, the simple poking method doesn't work.
- // The speed doesn't matter, so use the slow fscale method.
- fild dword ptr [ESP]; // scratchint
- fld1;
- fscale;
- fstp real ptr [ESP+8]; // scratchreal = 2^^scratchint
- fstp ST(0); // drop scratchint
- jmp L_normal;
-
-L_extreme: // Extreme exponent. X is very large positive, very
- // large negative, infinity, or NaN.
- fxam;
- fstsw AX;
- test AX, 0x0400; // NaN_or_zero, but we already know x != 0
- jz L_was_nan; // if x is NaN, returns x
- // set scratchreal = real.min_normal
- // squaring it will return 0, setting underflow flag
- mov word ptr [ESP+8+8], 1;
- test AX, 0x0200;
- jnz L_waslargenegative;
-L_overflow:
- // Set scratchreal = real.max.
- // squaring it will create infinity, and set overflow flag.
- mov word ptr [ESP+8+8], 0x7FFE;
-L_waslargenegative:
- fstp ST(0);
- fld real ptr [ESP+8]; // load scratchreal
- fmul ST(0), ST; // square it, to create havoc!
-L_was_nan:
- add ESP,12+8;
- ret PARAMSIZE;
- }
- }
- else version (D_InlineAsm_X86_64)
- {
- asm pure nothrow @nogc
- {
- naked;
- }
- version (Win64)
- {
- asm pure nothrow @nogc
- {
- fld real ptr [RCX]; // x
- mov AX,[RCX+8]; // AX = exponent and sign
- }
- }
- else
- {
- asm pure nothrow @nogc
- {
- fld real ptr [RSP+8]; // x
- mov AX,[RSP+8+8]; // AX = exponent and sign
- }
- }
- asm pure nothrow @nogc
- {
- /* exp2() for x87 80-bit reals, IEEE754-2008 conformant.
- * Author: Don Clugston.
- *
- * exp2(x) = 2^(rndint(x))* 2^(y-rndint(x))
- * The trick for high performance is to avoid the fscale(28cycles on core2),
- * frndint(19 cycles), leaving f2xm1(19 cycles) as the only slow instruction.
- *
- * We can do frndint by using fist. BUT we can't use it for huge numbers,
- * because it will set the Invalid Operation flag is overflow or NaN occurs.
- * Fortunately, whenever this happens the result would be zero or infinity.
- *
- * We can perform fscale by directly poking into the exponent. BUT this doesn't
- * work for the (very rare) cases where the result is subnormal. So we fall back
- * to the slow method in that case.
- */
- sub RSP, 24; // Create scratch space on the stack
- // [RSP,RSP+2] = scratchint
- // [RSP+4..+6, +8..+10, +10] = scratchreal
- // set scratchreal mantissa = 1.0
- mov dword ptr [RSP+8], 0;
- mov dword ptr [RSP+8+4], 0x80000000;
- and AX, 0x7FFF; // drop sign bit
- cmp AX, 0x401D; // avoid InvalidException in fist
- jae L_extreme;
- fist dword ptr [RSP]; // scratchint = rndint(x)
- fisub dword ptr [RSP]; // x - rndint(x)
- // and now set scratchreal exponent
- mov EAX, [RSP];
- add EAX, 0x3fff;
- jle short L_subnormal;
- cmp EAX,0x8000;
- jge short L_overflow;
- mov [RSP+8+8],AX;
-L_normal:
- f2xm1;
- fld1;
- fadd; // 2^(x-rndint(x))
- fld real ptr [RSP+8] ; // 2^rndint(x)
- add RSP,24;
- fmulp ST(1), ST;
- ret;
-
-L_subnormal:
- // Result will be subnormal.
- // In this rare case, the simple poking method doesn't work.
- // The speed doesn't matter, so use the slow fscale method.
- fild dword ptr [RSP]; // scratchint
- fld1;
- fscale;
- fstp real ptr [RSP+8]; // scratchreal = 2^scratchint
- fstp ST(0); // drop scratchint
- jmp L_normal;
-
-L_extreme: // Extreme exponent. X is very large positive, very
- // large negative, infinity, or NaN.
- fxam;
- fstsw AX;
- test AX, 0x0400; // NaN_or_zero, but we already know x != 0
- jz L_was_nan; // if x is NaN, returns x
- // set scratchreal = real.min
- // squaring it will return 0, setting underflow flag
- mov word ptr [RSP+8+8], 1;
- test AX, 0x0200;
- jnz L_waslargenegative;
-L_overflow:
- // Set scratchreal = real.max.
- // squaring it will create infinity, and set overflow flag.
- mov word ptr [RSP+8+8], 0x7FFE;
-L_waslargenegative:
- fstp ST(0);
- fld real ptr [RSP+8]; // load scratchreal
- fmul ST(0), ST; // square it, to create havoc!
-L_was_nan:
- add RSP,24;
- ret;
- }
- }
- else
- static assert(0);
-}
-
-private real exp2Impl(real x) @nogc @trusted pure nothrow
-{
- // Coefficients for exp2(x)
- static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
- {
- static immutable real[5] P = [
- 9.079594442980146270952372234833529694788E12L,
- 1.530625323728429161131811299626419117557E11L,
- 5.677513871931844661829755443994214173883E8L,
- 6.185032670011643762127954396427045467506E5L,
- 1.587171580015525194694938306936721666031E2L
- ];
-
- static immutable real[6] Q = [
- 2.619817175234089411411070339065679229869E13L,
- 1.490560994263653042761789432690793026977E12L,
- 1.092141473886177435056423606755843616331E10L,
- 2.186249607051644894762167991800811827835E7L,
- 1.236602014442099053716561665053645270207E4L,
- 1.0
- ];
- }
- else
- {
- static immutable real[3] P = [
- 2.0803843631901852422887E6L,
- 3.0286971917562792508623E4L,
- 6.0614853552242266094567E1L,
- ];
- static immutable real[4] Q = [
- 6.0027204078348487957118E6L,
- 3.2772515434906797273099E5L,
- 1.7492876999891839021063E3L,
- 1.0000000000000000000000E0L,
- ];
- }
-
- // Overflow and Underflow limits.
- enum real OF = 16_384.0L;
- enum real UF = -16_382.0L;
-
- // Special cases. Raises an overflow or underflow flag accordingly,
- // except in the case for CTFE, where there are no hardware controls.
- if (isNaN(x))
- return x;
- if (x > OF)
- return real.infinity;
- if (x < UF)
- return 0.0;
-
- // Separate into integer and fractional parts.
- int n = cast(int) floor(x + 0.5);
- x -= n;
-
- // Rational approximation:
- // exp2(x) = 1.0 + 2x P(x^^2) / (Q(x^^2) - P(x^^2))
- const real xx = x * x;
- const real px = x * poly(xx, P);
- x = px / (poly(xx, Q) - px);
- x = 1.0 + ldexp(x, 1);
-
- // Scale by power of 2.
- x = ldexp(x, n);
-
- return x;
-}
-
-///
-@safe unittest
-{
- assert(feqrel(exp2(0.5L), SQRT2) >= real.mant_dig -1);
- assert(exp2(8.0L) == 256.0);
- assert(exp2(-9.0L)== 1.0L/512.0);
-}
-
-@safe unittest
-{
- version (CRuntime_Microsoft) {} else // aexp2/exp2f/exp2l not implemented
- {
- assert( core.stdc.math.exp2f(0.0f) == 1 );
- assert( core.stdc.math.exp2 (0.0) == 1 );
- assert( core.stdc.math.exp2l(0.0L) == 1 );
- }
-}
-
-@system unittest
-{
- version (FloatingPointControlSupport)
- {
- FloatingPointControl ctrl;
- if (FloatingPointControl.hasExceptionTraps)
- ctrl.disableExceptions(FloatingPointControl.allExceptions);
- ctrl.rounding = FloatingPointControl.roundToNearest;
- }
-
- enum realFormat = floatTraits!real.realFormat;
- static if (realFormat == RealFormat.ieeeQuadruple)
- {
- static immutable real[2][] exptestpoints =
- [ // x exp(x)
- [ 1.0L, E ],
- [ 0.5L, 0x1.a61298e1e069bc972dfefab6df34p+0L ],
- [ 3.0L, E*E*E ],
- [ 0x1.6p+13L, 0x1.6e509d45728655cdb4840542acb5p+16250L ], // near overflow
- [ 0x1.7p+13L, real.infinity ], // close overflow
- [ 0x1p+80L, real.infinity ], // far overflow
- [ real.infinity, real.infinity ],
- [-0x1.18p+13L, 0x1.5e4bf54b4807034ea97fef0059a6p-12927L ], // near underflow
- [-0x1.625p+13L, 0x1.a6bd68a39d11fec3a250cd97f524p-16358L ], // ditto
- [-0x1.62dafp+13L, 0x0.cb629e9813b80ed4d639e875be6cp-16382L ], // near underflow - subnormal
- [-0x1.6549p+13L, 0x0.0000000000000000000000000001p-16382L ], // ditto
- [-0x1.655p+13L, 0 ], // close underflow
- [-0x1p+30L, 0 ], // far underflow
- ];
- }
- else static if (realFormat == RealFormat.ieeeExtended ||
- realFormat == RealFormat.ieeeExtended53)
- {
- static immutable real[2][] exptestpoints =
- [ // x exp(x)
- [ 1.0L, E ],
- [ 0.5L, 0x1.a61298e1e069bc97p+0L ],
- [ 3.0L, E*E*E ],
- [ 0x1.1p+13L, 0x1.29aeffefc8ec645p+12557L ], // near overflow
- [ 0x1.7p+13L, real.infinity ], // close overflow
- [ 0x1p+80L, real.infinity ], // far overflow
- [ real.infinity, real.infinity ],
- [-0x1.18p+13L, 0x1.5e4bf54b4806db9p-12927L ], // near underflow
- [-0x1.625p+13L, 0x1.a6bd68a39d11f35cp-16358L ], // ditto
- [-0x1.62dafp+13L, 0x1.96c53d30277021dp-16383L ], // near underflow - subnormal
- [-0x1.643p+13L, 0x1p-16444L ], // ditto
- [-0x1.645p+13L, 0 ], // close underflow
- [-0x1p+30L, 0 ], // far underflow
- ];
- }
- else static if (realFormat == RealFormat.ieeeDouble)
- {
- static immutable real[2][] exptestpoints =
- [ // x, exp(x)
- [ 1.0L, E ],
- [ 0.5L, 0x1.a61298e1e069cp+0L ],
- [ 3.0L, E*E*E ],
- [ 0x1.6p+9L, 0x1.93bf4ec282efbp+1015L ], // near overflow
- [ 0x1.7p+9L, real.infinity ], // close overflow
- [ 0x1p+80L, real.infinity ], // far overflow
- [ real.infinity, real.infinity ],
- [-0x1.6p+9L, 0x1.44a3824e5285fp-1016L ], // near underflow
- [-0x1.64p+9L, 0x0.06f84920bb2d3p-1022L ], // near underflow - subnormal
- [-0x1.743p+9L, 0x0.0000000000001p-1022L ], // ditto
- [-0x1.8p+9L, 0 ], // close underflow
- [-0x1p30L, 0 ], // far underflow
- ];
- }
- else
- static assert(0, "No exp() tests for real type!");
-
- const minEqualMantissaBits = real.mant_dig - 13;
- real x;
- version (IeeeFlagsSupport) IeeeFlags f;
- foreach (ref pair; exptestpoints)
- {
- version (IeeeFlagsSupport) resetIeeeFlags();
- x = exp(pair[0]);
- assert(feqrel(x, pair[1]) >= minEqualMantissaBits);
- }
-
- // Ideally, exp(0) would not set the inexact flag.
- // Unfortunately, fldl2e sets it!
- // So it's not realistic to avoid setting it.
- assert(exp(0.0L) == 1.0);
-
- // NaN propagation. Doesn't set flags, bcos was already NaN.
- version (IeeeFlagsSupport)
- {
- resetIeeeFlags();
- x = exp(real.nan);
- f = ieeeFlags;
- assert(isIdentical(abs(x), real.nan));
- assert(f.flags == 0);
-
- resetIeeeFlags();
- x = exp(-real.nan);
- f = ieeeFlags;
- assert(isIdentical(abs(x), real.nan));
- assert(f.flags == 0);
- }
- else
- {
- x = exp(real.nan);
- assert(isIdentical(abs(x), real.nan));
-
- x = exp(-real.nan);
- assert(isIdentical(abs(x), real.nan));
- }
-
- x = exp(NaN(0x123));
- assert(isIdentical(x, NaN(0x123)));
-
- // High resolution test (verified against GNU MPFR/Mathematica).
- assert(exp(0.5L) == 0x1.A612_98E1_E069_BC97_2DFE_FAB6_DF34p+0L);
-}
-
-
-/**
- * Calculate cos(y) + i sin(y).
- *
- * On many CPUs (such as x86), this is a very efficient operation;
- * almost twice as fast as calculating sin(y) and cos(y) separately,
- * and is the preferred method when both are required.
- */
-creal expi(real y) @trusted pure nothrow @nogc
-{
- version (InlineAsm_X86_Any)
- {
- version (Win64)
- {
- asm pure nothrow @nogc
- {
- naked;
- fld real ptr [ECX];
- fsincos;
- fxch ST(1), ST(0);
- ret;
- }
- }
- else
- {
- asm pure nothrow @nogc
- {
- fld y;
- fsincos;
- fxch ST(1), ST(0);
- }
- }
- }
- else
- {
- return cos(y) + sin(y)*1i;
- }
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(expi(1.3e5L) == cos(1.3e5L) + sin(1.3e5L) * 1i);
- assert(expi(0.0L) == 1L + 0.0Li);
-}
-
-/*********************************************************************
- * Separate floating point value into significand and exponent.
- *
- * Returns:
- * Calculate and return $(I x) and $(I exp) such that
- * value =$(I x)*2$(SUPERSCRIPT exp) and
- * .5 $(LT)= |$(I x)| $(LT) 1.0
- *
- * $(I x) has same sign as value.
- *
- * $(TABLE_SV
- * $(TR $(TH value) $(TH returns) $(TH exp))
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD 0))
- * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD int.max))
- * $(TR $(TD -$(INFIN)) $(TD -$(INFIN)) $(TD int.min))
- * $(TR $(TD $(PLUSMN)$(NAN)) $(TD $(PLUSMN)$(NAN)) $(TD int.min))
- * )
- */
-T frexp(T)(const T value, out int exp) @trusted pure nothrow @nogc
-if (isFloatingPoint!T)
-{
- Unqual!T vf = value;
- ushort* vu = cast(ushort*)&vf;
- static if (is(Unqual!T == float))
- int* vi = cast(int*)&vf;
- else
- long* vl = cast(long*)&vf;
- int ex;
- alias F = floatTraits!T;
-
- ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
- static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- if (ex)
- { // If exponent is non-zero
- if (ex == F.EXPMASK) // infinity or NaN
- {
- if (*vl & 0x7FFF_FFFF_FFFF_FFFF) // NaN
- {
- *vl |= 0xC000_0000_0000_0000; // convert NaNS to NaNQ
- exp = int.min;
- }
- else if (vu[F.EXPPOS_SHORT] & 0x8000) // negative infinity
- exp = int.min;
- else // positive infinity
- exp = int.max;
-
- }
- else
- {
- exp = ex - F.EXPBIAS;
- vu[F.EXPPOS_SHORT] = (0x8000 & vu[F.EXPPOS_SHORT]) | 0x3FFE;
- }
- }
- else if (!*vl)
- {
- // vf is +-0.0
- exp = 0;
- }
- else
- {
- // subnormal
-
- vf *= F.RECIP_EPSILON;
- ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
- exp = ex - F.EXPBIAS - T.mant_dig + 1;
- vu[F.EXPPOS_SHORT] = ((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3FFE;
- }
- return vf;
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- if (ex) // If exponent is non-zero
- {
- if (ex == F.EXPMASK)
- {
- // infinity or NaN
- if (vl[MANTISSA_LSB] |
- (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN
- {
- // convert NaNS to NaNQ
- vl[MANTISSA_MSB] |= 0x0000_8000_0000_0000;
- exp = int.min;
- }
- else if (vu[F.EXPPOS_SHORT] & 0x8000) // negative infinity
- exp = int.min;
- else // positive infinity
- exp = int.max;
- }
- else
- {
- exp = ex - F.EXPBIAS;
- vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]);
- }
- }
- else if ((vl[MANTISSA_LSB] |
- (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0)
- {
- // vf is +-0.0
- exp = 0;
- }
- else
- {
- // subnormal
- vf *= F.RECIP_EPSILON;
- ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
- exp = ex - F.EXPBIAS - T.mant_dig + 1;
- vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]);
- }
- return vf;
- }
- else static if (F.realFormat == RealFormat.ieeeDouble)
- {
- if (ex) // If exponent is non-zero
- {
- if (ex == F.EXPMASK) // infinity or NaN
- {
- if (*vl == 0x7FF0_0000_0000_0000) // positive infinity
- {
- exp = int.max;
- }
- else if (*vl == 0xFFF0_0000_0000_0000) // negative infinity
- exp = int.min;
- else
- { // NaN
- *vl |= 0x0008_0000_0000_0000; // convert NaNS to NaNQ
- exp = int.min;
- }
- }
- else
- {
- exp = (ex - F.EXPBIAS) >> 4;
- vu[F.EXPPOS_SHORT] = cast(ushort)((0x800F & vu[F.EXPPOS_SHORT]) | 0x3FE0);
- }
- }
- else if (!(*vl & 0x7FFF_FFFF_FFFF_FFFF))
- {
- // vf is +-0.0
- exp = 0;
- }
- else
- {
- // subnormal
- vf *= F.RECIP_EPSILON;
- ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
- exp = ((ex - F.EXPBIAS) >> 4) - T.mant_dig + 1;
- vu[F.EXPPOS_SHORT] =
- cast(ushort)(((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3FE0);
- }
- return vf;
- }
- else static if (F.realFormat == RealFormat.ieeeSingle)
- {
- if (ex) // If exponent is non-zero
- {
- if (ex == F.EXPMASK) // infinity or NaN
- {
- if (*vi == 0x7F80_0000) // positive infinity
- {
- exp = int.max;
- }
- else if (*vi == 0xFF80_0000) // negative infinity
- exp = int.min;
- else
- { // NaN
- *vi |= 0x0040_0000; // convert NaNS to NaNQ
- exp = int.min;
- }
- }
- else
- {
- exp = (ex - F.EXPBIAS) >> 7;
- vu[F.EXPPOS_SHORT] = cast(ushort)((0x807F & vu[F.EXPPOS_SHORT]) | 0x3F00);
- }
- }
- else if (!(*vi & 0x7FFF_FFFF))
- {
- // vf is +-0.0
- exp = 0;
- }
- else
- {
- // subnormal
- vf *= F.RECIP_EPSILON;
- ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
- exp = ((ex - F.EXPBIAS) >> 7) - T.mant_dig + 1;
- vu[F.EXPPOS_SHORT] =
- cast(ushort)(((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3F00);
- }
- return vf;
- }
- else // static if (F.realFormat == RealFormat.ibmExtended)
- {
- assert(0, "frexp not implemented");
- }
-}
-
-///
-@system unittest
-{
- int exp;
- real mantissa = frexp(123.456L, exp);
-
- // check if values are equal to 19 decimal digits of precision
- assert(equalsDigit(mantissa * pow(2.0L, cast(real) exp), 123.456L, 19));
-
- assert(frexp(-real.nan, exp) && exp == int.min);
- assert(frexp(real.nan, exp) && exp == int.min);
- assert(frexp(-real.infinity, exp) == -real.infinity && exp == int.min);
- assert(frexp(real.infinity, exp) == real.infinity && exp == int.max);
- assert(frexp(-0.0, exp) == -0.0 && exp == 0);
- assert(frexp(0.0, exp) == 0.0 && exp == 0);
-}
-
-@safe unittest
-{
- import std.meta : AliasSeq;
- import std.typecons : tuple, Tuple;
-
- foreach (T; AliasSeq!(real, double, float))
- {
- Tuple!(T, T, int)[] vals = // x,frexp,exp
- [
- tuple(T(0.0), T( 0.0 ), 0),
- tuple(T(-0.0), T( -0.0), 0),
- tuple(T(1.0), T( .5 ), 1),
- tuple(T(-1.0), T( -.5 ), 1),
- tuple(T(2.0), T( .5 ), 2),
- tuple(T(float.min_normal/2.0f), T(.5), -126),
- tuple(T.infinity, T.infinity, int.max),
- tuple(-T.infinity, -T.infinity, int.min),
- tuple(T.nan, T.nan, int.min),
- tuple(-T.nan, -T.nan, int.min),
-
- // Phobos issue #16026:
- tuple(3 * (T.min_normal * T.epsilon), T( .75), (T.min_exp - T.mant_dig) + 2)
- ];
-
- foreach (elem; vals)
- {
- T x = elem[0];
- T e = elem[1];
- int exp = elem[2];
- int eptr;
- T v = frexp(x, eptr);
- assert(isIdentical(e, v));
- assert(exp == eptr);
-
- }
-
- static if (floatTraits!(T).realFormat == RealFormat.ieeeExtended)
- {
- static T[3][] extendedvals = [ // x,frexp,exp
- [0x1.a5f1c2eb3fe4efp+73L, 0x1.A5F1C2EB3FE4EFp-1L, 74], // normal
- [0x1.fa01712e8f0471ap-1064L, 0x1.fa01712e8f0471ap-1L, -1063],
- [T.min_normal, .5, -16381],
- [T.min_normal/2.0L, .5, -16382] // subnormal
- ];
- foreach (elem; extendedvals)
- {
- T x = elem[0];
- T e = elem[1];
- int exp = cast(int) elem[2];
- int eptr;
- T v = frexp(x, eptr);
- assert(isIdentical(e, v));
- assert(exp == eptr);
-
- }
- }
- }
-}
-
-@safe unittest
-{
- import std.meta : AliasSeq;
- void foo() {
- foreach (T; AliasSeq!(real, double, float))
- {
- int exp;
- const T a = 1;
- immutable T b = 2;
- auto c = frexp(a, exp);
- auto d = frexp(b, exp);
- }
- }
-}
-
-/******************************************
- * Extracts the exponent of x as a signed integral value.
- *
- * If x is not a special value, the result is the same as
- * $(D cast(int) logb(x)).
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH ilogb(x)) $(TH Range error?))
- * $(TR $(TD 0) $(TD FP_ILOGB0) $(TD yes))
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD int.max) $(TD no))
- * $(TR $(TD $(NAN)) $(TD FP_ILOGBNAN) $(TD no))
- * )
- */
-int ilogb(T)(const T x) @trusted pure nothrow @nogc
-if (isFloatingPoint!T)
-{
- import core.bitop : bsr;
- alias F = floatTraits!T;
-
- union floatBits
- {
- T rv;
- ushort[T.sizeof/2] vu;
- uint[T.sizeof/4] vui;
- static if (T.sizeof >= 8)
- ulong[T.sizeof/8] vul;
- }
- floatBits y = void;
- y.rv = x;
-
- int ex = y.vu[F.EXPPOS_SHORT] & F.EXPMASK;
- static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- if (ex)
- {
- // If exponent is non-zero
- if (ex == F.EXPMASK) // infinity or NaN
- {
- if (y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF) // NaN
- return FP_ILOGBNAN;
- else // +-infinity
- return int.max;
- }
- else
- {
- return ex - F.EXPBIAS - 1;
- }
- }
- else if (!y.vul[0])
- {
- // vf is +-0.0
- return FP_ILOGB0;
- }
- else
- {
- // subnormal
- return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(y.vul[0]);
- }
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- if (ex) // If exponent is non-zero
- {
- if (ex == F.EXPMASK)
- {
- // infinity or NaN
- if (y.vul[MANTISSA_LSB] | ( y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN
- return FP_ILOGBNAN;
- else // +- infinity
- return int.max;
- }
- else
- {
- return ex - F.EXPBIAS - 1;
- }
- }
- else if ((y.vul[MANTISSA_LSB] | (y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0)
- {
- // vf is +-0.0
- return FP_ILOGB0;
- }
- else
- {
- // subnormal
- const ulong msb = y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF;
- const ulong lsb = y.vul[MANTISSA_LSB];
- if (msb)
- return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(msb) + 64;
- else
- return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(lsb);
- }
- }
- else static if (F.realFormat == RealFormat.ieeeDouble)
- {
- if (ex) // If exponent is non-zero
- {
- if (ex == F.EXPMASK) // infinity or NaN
- {
- if ((y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FF0_0000_0000_0000) // +- infinity
- return int.max;
- else // NaN
- return FP_ILOGBNAN;
- }
- else
- {
- return ((ex - F.EXPBIAS) >> 4) - 1;
- }
- }
- else if (!(y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF))
- {
- // vf is +-0.0
- return FP_ILOGB0;
- }
- else
- {
- // subnormal
- enum MANTISSAMASK_64 = ((cast(ulong) F.MANTISSAMASK_INT) << 32) | 0xFFFF_FFFF;
- return ((ex - F.EXPBIAS) >> 4) - T.mant_dig + 1 + bsr(y.vul[0] & MANTISSAMASK_64);
- }
- }
- else static if (F.realFormat == RealFormat.ieeeSingle)
- {
- if (ex) // If exponent is non-zero
- {
- if (ex == F.EXPMASK) // infinity or NaN
- {
- if ((y.vui[0] & 0x7FFF_FFFF) == 0x7F80_0000) // +- infinity
- return int.max;
- else // NaN
- return FP_ILOGBNAN;
- }
- else
- {
- return ((ex - F.EXPBIAS) >> 7) - 1;
- }
- }
- else if (!(y.vui[0] & 0x7FFF_FFFF))
- {
- // vf is +-0.0
- return FP_ILOGB0;
- }
- else
- {
- // subnormal
- const uint mantissa = y.vui[0] & F.MANTISSAMASK_INT;
- return ((ex - F.EXPBIAS) >> 7) - T.mant_dig + 1 + bsr(mantissa);
- }
- }
- else // static if (F.realFormat == RealFormat.ibmExtended)
- {
- core.stdc.math.ilogbl(x);
- }
-}
-/// ditto
-int ilogb(T)(const T x) @safe pure nothrow @nogc
-if (isIntegral!T && isUnsigned!T)
-{
- import core.bitop : bsr;
- if (x == 0)
- return FP_ILOGB0;
- else
- {
- static assert(T.sizeof <= ulong.sizeof, "integer size too large for the current ilogb implementation");
- return bsr(x);
- }
-}
-/// ditto
-int ilogb(T)(const T x) @safe pure nothrow @nogc
-if (isIntegral!T && isSigned!T)
-{
- import std.traits : Unsigned;
- // Note: abs(x) can not be used because the return type is not Unsigned and
- // the return value would be wrong for x == int.min
- Unsigned!T absx = x >= 0 ? x : -x;
- return ilogb(absx);
-}
-
-alias FP_ILOGB0 = core.stdc.math.FP_ILOGB0;
-alias FP_ILOGBNAN = core.stdc.math.FP_ILOGBNAN;
-
-@system nothrow @nogc unittest
-{
- import std.meta : AliasSeq;
- import std.typecons : Tuple;
- foreach (F; AliasSeq!(float, double, real))
- {
- alias T = Tuple!(F, int);
- T[13] vals = // x, ilogb(x)
- [
- T( F.nan , FP_ILOGBNAN ),
- T( -F.nan , FP_ILOGBNAN ),
- T( F.infinity, int.max ),
- T( -F.infinity, int.max ),
- T( 0.0 , FP_ILOGB0 ),
- T( -0.0 , FP_ILOGB0 ),
- T( 2.0 , 1 ),
- T( 2.0001 , 1 ),
- T( 1.9999 , 0 ),
- T( 0.5 , -1 ),
- T( 123.123 , 6 ),
- T( -123.123 , 6 ),
- T( 0.123 , -4 ),
- ];
-
- foreach (elem; vals)
- {
- assert(ilogb(elem[0]) == elem[1]);
- }
- }
-
- // min_normal and subnormals
- assert(ilogb(-float.min_normal) == -126);
- assert(ilogb(nextUp(-float.min_normal)) == -127);
- assert(ilogb(nextUp(-float(0.0))) == -149);
- assert(ilogb(-double.min_normal) == -1022);
- assert(ilogb(nextUp(-double.min_normal)) == -1023);
- assert(ilogb(nextUp(-double(0.0))) == -1074);
- static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
- {
- assert(ilogb(-real.min_normal) == -16382);
- assert(ilogb(nextUp(-real.min_normal)) == -16383);
- assert(ilogb(nextUp(-real(0.0))) == -16445);
- }
- else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble)
- {
- assert(ilogb(-real.min_normal) == -1022);
- assert(ilogb(nextUp(-real.min_normal)) == -1023);
- assert(ilogb(nextUp(-real(0.0))) == -1074);
- }
-
- // test integer types
- assert(ilogb(0) == FP_ILOGB0);
- assert(ilogb(int.max) == 30);
- assert(ilogb(int.min) == 31);
- assert(ilogb(uint.max) == 31);
- assert(ilogb(long.max) == 62);
- assert(ilogb(long.min) == 63);
- assert(ilogb(ulong.max) == 63);
-}
-
-/*******************************************
- * Compute n * 2$(SUPERSCRIPT exp)
- * References: frexp
- */
-
-real ldexp(real n, int exp) @nogc @safe pure nothrow { pragma(inline, true); return core.math.ldexp(n, exp); }
-//FIXME
-///ditto
-double ldexp(double n, int exp) @safe pure nothrow @nogc { return ldexp(cast(real) n, exp); }
-//FIXME
-///ditto
-float ldexp(float n, int exp) @safe pure nothrow @nogc { return ldexp(cast(real) n, exp); }
-
-///
-@nogc @safe pure nothrow unittest
-{
- import std.meta : AliasSeq;
- foreach (T; AliasSeq!(float, double, real))
- {
- T r;
-
- r = ldexp(3.0L, 3);
- assert(r == 24);
-
- r = ldexp(cast(T) 3.0, cast(int) 3);
- assert(r == 24);
-
- T n = 3.0;
- int exp = 3;
- r = ldexp(n, exp);
- assert(r == 24);
- }
-}
-
-@safe pure nothrow @nogc unittest
-{
- static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended ||
- floatTraits!(real).realFormat == RealFormat.ieeeExtended53 ||
- floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
- {
- assert(ldexp(1.0L, -16384) == 0x1p-16384L);
- assert(ldexp(1.0L, -16382) == 0x1p-16382L);
- int x;
- real n = frexp(0x1p-16384L, x);
- assert(n == 0.5L);
- assert(x==-16383);
- assert(ldexp(n, x)==0x1p-16384L);
- }
- else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble)
- {
- assert(ldexp(1.0L, -1024) == 0x1p-1024L);
- assert(ldexp(1.0L, -1022) == 0x1p-1022L);
- int x;
- real n = frexp(0x1p-1024L, x);
- assert(n == 0.5L);
- assert(x==-1023);
- assert(ldexp(n, x)==0x1p-1024L);
- }
- else static assert(false, "Floating point type real not supported");
-}
-
-/* workaround Issue 14718, float parsing depends on platform strtold
-@safe pure nothrow @nogc unittest
-{
- assert(ldexp(1.0, -1024) == 0x1p-1024);
- assert(ldexp(1.0, -1022) == 0x1p-1022);
- int x;
- double n = frexp(0x1p-1024, x);
- assert(n == 0.5);
- assert(x==-1023);
- assert(ldexp(n, x)==0x1p-1024);
-}
-
-@safe pure nothrow @nogc unittest
-{
- assert(ldexp(1.0f, -128) == 0x1p-128f);
- assert(ldexp(1.0f, -126) == 0x1p-126f);
- int x;
- float n = frexp(0x1p-128f, x);
- assert(n == 0.5f);
- assert(x==-127);
- assert(ldexp(n, x)==0x1p-128f);
-}
-*/
-
-@system unittest
-{
- static real[3][] vals = // value,exp,ldexp
- [
- [ 0, 0, 0],
- [ 1, 0, 1],
- [ -1, 0, -1],
- [ 1, 1, 2],
- [ 123, 10, 125952],
- [ real.max, int.max, real.infinity],
- [ real.max, -int.max, 0],
- [ real.min_normal, -int.max, 0],
- ];
- int i;
-
- for (i = 0; i < vals.length; i++)
- {
- real x = vals[i][0];
- int exp = cast(int) vals[i][1];
- real z = vals[i][2];
- real l = ldexp(x, exp);
-
- assert(equalsDigit(z, l, 7));
- }
-
- real function(real, int) pldexp = &ldexp;
- assert(pldexp != null);
-}
-
-private
-{
- version (INLINE_YL2X) {} else
- {
- static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
- {
- // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x)
- static immutable real[13] logCoeffsP = [
- 1.313572404063446165910279910527789794488E4L,
- 7.771154681358524243729929227226708890930E4L,
- 2.014652742082537582487669938141683759923E5L,
- 3.007007295140399532324943111654767187848E5L,
- 2.854829159639697837788887080758954924001E5L,
- 1.797628303815655343403735250238293741397E5L,
- 7.594356839258970405033155585486712125861E4L,
- 2.128857716871515081352991964243375186031E4L,
- 3.824952356185897735160588078446136783779E3L,
- 4.114517881637811823002128927449878962058E2L,
- 2.321125933898420063925789532045674660756E1L,
- 4.998469661968096229986658302195402690910E-1L,
- 1.538612243596254322971797716843006400388E-6L
- ];
- static immutable real[13] logCoeffsQ = [
- 3.940717212190338497730839731583397586124E4L,
- 2.626900195321832660448791748036714883242E5L,
- 7.777690340007566932935753241556479363645E5L,
- 1.347518538384329112529391120390701166528E6L,
- 1.514882452993549494932585972882995548426E6L,
- 1.158019977462989115839826904108208787040E6L,
- 6.132189329546557743179177159925690841200E5L,
- 2.248234257620569139969141618556349415120E5L,
- 5.605842085972455027590989944010492125825E4L,
- 9.147150349299596453976674231612674085381E3L,
- 9.104928120962988414618126155557301584078E2L,
- 4.839208193348159620282142911143429644326E1L,
- 1.0
- ];
-
- // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2)
- // where z = 2(x-1)/(x+1)
- static immutable real[6] logCoeffsR = [
- -8.828896441624934385266096344596648080902E-1L,
- 8.057002716646055371965756206836056074715E1L,
- -2.024301798136027039250415126250455056397E3L,
- 2.048819892795278657810231591630928516206E4L,
- -8.977257995689735303686582344659576526998E4L,
- 1.418134209872192732479751274970992665513E5L
- ];
- static immutable real[6] logCoeffsS = [
- 1.701761051846631278975701529965589676574E6L
- -1.332535117259762928288745111081235577029E6L,
- 4.001557694070773974936904547424676279307E5L,
- -5.748542087379434595104154610899551484314E4L,
- 3.998526750980007367835804959888064681098E3L,
- -1.186359407982897997337150403816839480438E2L,
- 1.0
- ];
- }
- else
- {
- // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x)
- static immutable real[7] logCoeffsP = [
- 2.0039553499201281259648E1L,
- 5.7112963590585538103336E1L,
- 6.0949667980987787057556E1L,
- 2.9911919328553073277375E1L,
- 6.5787325942061044846969E0L,
- 4.9854102823193375972212E-1L,
- 4.5270000862445199635215E-5L,
- ];
- static immutable real[7] logCoeffsQ = [
- 6.0118660497603843919306E1L,
- 2.1642788614495947685003E2L,
- 3.0909872225312059774938E2L,
- 2.2176239823732856465394E2L,
- 8.3047565967967209469434E1L,
- 1.5062909083469192043167E1L,
- 1.0000000000000000000000E0L,
- ];
-
- // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2)
- // where z = 2(x-1)/(x+1)
- static immutable real[4] logCoeffsR = [
- -3.5717684488096787370998E1L,
- 1.0777257190312272158094E1L,
- -7.1990767473014147232598E-1L,
- 1.9757429581415468984296E-3L,
- ];
- static immutable real[4] logCoeffsS = [
- -4.2861221385716144629696E2L,
- 1.9361891836232102174846E2L,
- -2.6201045551331104417768E1L,
- 1.0000000000000000000000E0L,
- ];
- }
- }
-}
-
-/**************************************
- * Calculate the natural logarithm of x.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH log(x)) $(TH divide by 0?) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no))
- * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes))
- * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no))
- * )
- */
-real log(real x) @safe pure nothrow @nogc
-{
- version (INLINE_YL2X)
- return core.math.yl2x(x, LN2);
- else
- {
- // C1 + C2 = LN2.
- enum real C1 = 6.93145751953125E-1L;
- enum real C2 = 1.428606820309417232121458176568075500134E-6L;
-
- // Special cases.
- if (isNaN(x))
- return x;
- if (isInfinity(x) && !signbit(x))
- return x;
- if (x == 0.0)
- return -real.infinity;
- if (x < 0.0)
- return real.nan;
-
- // Separate mantissa from exponent.
- // Note, frexp is used so that denormal numbers will be handled properly.
- real y, z;
- int exp;
-
- x = frexp(x, exp);
-
- // Logarithm using log(x) = z + z^^3 R(z) / S(z),
- // where z = 2(x - 1)/(x + 1)
- if ((exp > 2) || (exp < -2))
- {
- if (x < SQRT1_2)
- { // 2(2x - 1)/(2x + 1)
- exp -= 1;
- z = x - 0.5;
- y = 0.5 * z + 0.5;
- }
- else
- { // 2(x - 1)/(x + 1)
- z = x - 0.5;
- z -= 0.5;
- y = 0.5 * x + 0.5;
- }
- x = z / y;
- z = x * x;
- z = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
- z += exp * C2;
- z += x;
- z += exp * C1;
-
- return z;
- }
-
- // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
- if (x < SQRT1_2)
- { // 2x - 1
- exp -= 1;
- x = ldexp(x, 1) - 1.0;
- }
- else
- {
- x = x - 1.0;
- }
- z = x * x;
- y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
- y += exp * C2;
- z = y - ldexp(z, -1);
-
- // Note, the sum of above terms does not exceed x/4,
- // so it contributes at most about 1/4 lsb to the error.
- z += x;
- z += exp * C1;
-
- return z;
- }
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(log(E) == 1);
-}
-
-/**************************************
- * Calculate the base-10 logarithm of x.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH log10(x)) $(TH divide by 0?) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no))
- * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes))
- * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no))
- * )
- */
-real log10(real x) @safe pure nothrow @nogc
-{
- version (INLINE_YL2X)
- return core.math.yl2x(x, LOG2);
- else
- {
- // log10(2) split into two parts.
- enum real L102A = 0.3125L;
- enum real L102B = -1.14700043360188047862611052755069732318101185E-2L;
-
- // log10(e) split into two parts.
- enum real L10EA = 0.5L;
- enum real L10EB = -6.570551809674817234887108108339491770560299E-2L;
-
- // Special cases are the same as for log.
- if (isNaN(x))
- return x;
- if (isInfinity(x) && !signbit(x))
- return x;
- if (x == 0.0)
- return -real.infinity;
- if (x < 0.0)
- return real.nan;
-
- // Separate mantissa from exponent.
- // Note, frexp is used so that denormal numbers will be handled properly.
- real y, z;
- int exp;
-
- x = frexp(x, exp);
-
- // Logarithm using log(x) = z + z^^3 R(z) / S(z),
- // where z = 2(x - 1)/(x + 1)
- if ((exp > 2) || (exp < -2))
- {
- if (x < SQRT1_2)
- { // 2(2x - 1)/(2x + 1)
- exp -= 1;
- z = x - 0.5;
- y = 0.5 * z + 0.5;
- }
- else
- { // 2(x - 1)/(x + 1)
- z = x - 0.5;
- z -= 0.5;
- y = 0.5 * x + 0.5;
- }
- x = z / y;
- z = x * x;
- y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
- goto Ldone;
- }
-
- // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
- if (x < SQRT1_2)
- { // 2x - 1
- exp -= 1;
- x = ldexp(x, 1) - 1.0;
- }
- else
- x = x - 1.0;
-
- z = x * x;
- y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
- y = y - ldexp(z, -1);
-
- // Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
- // This sequence of operations is critical and it may be horribly
- // defeated by some compiler optimizers.
- Ldone:
- z = y * L10EB;
- z += x * L10EB;
- z += exp * L102B;
- z += y * L10EA;
- z += x * L10EA;
- z += exp * L102A;
-
- return z;
- }
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(fabs(log10(1000) - 3) < .000001);
-}
-
-/******************************************
- * Calculates the natural logarithm of 1 + x.
- *
- * For very small x, log1p(x) will be more accurate than
- * log(1 + x).
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH log1p(x)) $(TH divide by 0?) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) $(TD no))
- * $(TR $(TD -1.0) $(TD -$(INFIN)) $(TD yes) $(TD no))
- * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD no) $(TD yes))
- * $(TR $(TD +$(INFIN)) $(TD -$(INFIN)) $(TD no) $(TD no))
- * )
- */
-real log1p(real x) @safe pure nothrow @nogc
-{
- version (INLINE_YL2X)
- {
- // On x87, yl2xp1 is valid if and only if -0.5 <= lg(x) <= 0.5,
- // ie if -0.29 <= x <= 0.414
- return (fabs(x) <= 0.25) ? core.math.yl2xp1(x, LN2) : core.math.yl2x(x+1, LN2);
- }
- else
- {
- // Special cases.
- if (isNaN(x) || x == 0.0)
- return x;
- if (isInfinity(x) && !signbit(x))
- return x;
- if (x == -1.0)
- return -real.infinity;
- if (x < -1.0)
- return real.nan;
-
- return log(x + 1.0);
- }
-}
-
-/***************************************
- * Calculates the base-2 logarithm of x:
- * $(SUB log, 2)x
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH log2(x)) $(TH divide by 0?) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no) )
- * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes) )
- * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no) )
- * )
- */
-real log2(real x) @safe pure nothrow @nogc
-{
- version (INLINE_YL2X)
- return core.math.yl2x(x, 1.0L);
- else
- {
- // Special cases are the same as for log.
- if (isNaN(x))
- return x;
- if (isInfinity(x) && !signbit(x))
- return x;
- if (x == 0.0)
- return -real.infinity;
- if (x < 0.0)
- return real.nan;
-
- // Separate mantissa from exponent.
- // Note, frexp is used so that denormal numbers will be handled properly.
- real y, z;
- int exp;
-
- x = frexp(x, exp);
-
- // Logarithm using log(x) = z + z^^3 R(z) / S(z),
- // where z = 2(x - 1)/(x + 1)
- if ((exp > 2) || (exp < -2))
- {
- if (x < SQRT1_2)
- { // 2(2x - 1)/(2x + 1)
- exp -= 1;
- z = x - 0.5;
- y = 0.5 * z + 0.5;
- }
- else
- { // 2(x - 1)/(x + 1)
- z = x - 0.5;
- z -= 0.5;
- y = 0.5 * x + 0.5;
- }
- x = z / y;
- z = x * x;
- y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
- goto Ldone;
- }
-
- // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
- if (x < SQRT1_2)
- { // 2x - 1
- exp -= 1;
- x = ldexp(x, 1) - 1.0;
- }
- else
- x = x - 1.0;
-
- z = x * x;
- y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
- y = y - ldexp(z, -1);
-
- // Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
- // This sequence of operations is critical and it may be horribly
- // defeated by some compiler optimizers.
- Ldone:
- z = y * (LOG2E - 1.0);
- z += x * (LOG2E - 1.0);
- z += y;
- z += x;
- z += exp;
-
- return z;
- }
-}
-
-///
-@system unittest
-{
- // check if values are equal to 19 decimal digits of precision
- assert(equalsDigit(log2(1024.0L), 10, 19));
-}
-
-/*****************************************
- * Extracts the exponent of x as a signed integral value.
- *
- * If x is subnormal, it is treated as if it were normalized.
- * For a positive, finite x:
- *
- * 1 $(LT)= $(I x) * FLT_RADIX$(SUPERSCRIPT -logb(x)) $(LT) FLT_RADIX
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH logb(x)) $(TH divide by 0?) )
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) $(TD no))
- * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) )
- * )
- */
-real logb(real x) @trusted nothrow @nogc
-{
- version (Win64_DMD_InlineAsm)
- {
- asm pure nothrow @nogc
- {
- naked ;
- fld real ptr [RCX] ;
- fxtract ;
- fstp ST(0) ;
- ret ;
- }
- }
- else version (MSVC_InlineAsm)
- {
- asm pure nothrow @nogc
- {
- fld x ;
- fxtract ;
- fstp ST(0) ;
- }
- }
- else
- return core.stdc.math.logbl(x);
-}
-
-/************************************
- * Calculates the remainder from the calculation x/y.
- * Returns:
- * The value of x - i * y, where i is the number of times that y can
- * be completely subtracted from x. The result has the same sign as x.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH y) $(TH fmod(x, y)) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD not 0.0) $(TD $(PLUSMN)0.0) $(TD no))
- * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(NAN)) $(TD yes))
- * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(NAN)) $(TD yes))
- * $(TR $(TD !=$(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD no))
- * )
- */
-real fmod(real x, real y) @trusted nothrow @nogc
-{
- version (CRuntime_Microsoft)
- {
- return x % y;
- }
- else
- return core.stdc.math.fmodl(x, y);
-}
-
-/************************************
- * Breaks x into an integral part and a fractional part, each of which has
- * the same sign as x. The integral part is stored in i.
- * Returns:
- * The fractional part of x.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH i (on input)) $(TH modf(x, i)) $(TH i (on return)))
- * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(PLUSMNINF)))
- * )
- */
-real modf(real x, ref real i) @trusted nothrow @nogc
-{
- version (CRuntime_Microsoft)
- {
- i = trunc(x);
- return copysign(isInfinity(x) ? 0.0 : x - i, x);
- }
- else
- return core.stdc.math.modfl(x,&i);
-}
-
-/*************************************
- * Efficiently calculates x * 2$(SUPERSCRIPT n).
- *
- * scalbn handles underflow and overflow in
- * the same fashion as the basic arithmetic operators.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH scalb(x)))
- * $(TR $(TD $(PLUSMNINF)) $(TD $(PLUSMNINF)) )
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) )
- * )
- */
-real scalbn(real x, int n) @trusted nothrow @nogc
-{
- version (InlineAsm_X86_Any)
- {
- // scalbnl is not supported on DMD-Windows, so use asm pure nothrow @nogc.
- version (Win64)
- {
- asm pure nothrow @nogc {
- naked ;
- mov 16[RSP],RCX ;
- fild word ptr 16[RSP] ;
- fld real ptr [RDX] ;
- fscale ;
- fstp ST(1) ;
- ret ;
- }
- }
- else
- {
- asm pure nothrow @nogc {
- fild n;
- fld x;
- fscale;
- fstp ST(1);
- }
- }
- }
- else
- {
- return core.stdc.math.scalbnl(x, n);
- }
-}
-
-///
-@safe nothrow @nogc unittest
-{
- assert(scalbn(-real.infinity, 5) == -real.infinity);
-}
-
-/***************
- * Calculates the cube root of x.
- *
- * $(TABLE_SV
- * $(TR $(TH $(I x)) $(TH cbrt(x)) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) )
- * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) )
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no) )
- * )
- */
-real cbrt(real x) @trusted nothrow @nogc
-{
- version (CRuntime_Microsoft)
- {
- version (INLINE_YL2X)
- return copysign(exp2(core.math.yl2x(fabs(x), 1.0L/3.0L)), x);
- else
- return core.stdc.math.cbrtl(x);
- }
- else
- return core.stdc.math.cbrtl(x);
-}
-
-
-/*******************************
- * Returns |x|
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH fabs(x)))
- * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) )
- * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) )
- * )
- */
-real fabs(real x) @safe pure nothrow @nogc { pragma(inline, true); return core.math.fabs(x); }
-//FIXME
-///ditto
-double fabs(double x) @safe pure nothrow @nogc { return fabs(cast(real) x); }
-//FIXME
-///ditto
-float fabs(float x) @safe pure nothrow @nogc { return fabs(cast(real) x); }
-
-@safe unittest
-{
- real function(real) pfabs = &fabs;
- assert(pfabs != null);
-}
-
-/***********************************************************************
- * Calculates the length of the
- * hypotenuse of a right-angled triangle with sides of length x and y.
- * The hypotenuse is the value of the square root of
- * the sums of the squares of x and y:
- *
- * sqrt($(POWER x, 2) + $(POWER y, 2))
- *
- * Note that hypot(x, y), hypot(y, x) and
- * hypot(x, -y) are equivalent.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH y) $(TH hypot(x, y)) $(TH invalid?))
- * $(TR $(TD x) $(TD $(PLUSMN)0.0) $(TD |x|) $(TD no))
- * $(TR $(TD $(PLUSMNINF)) $(TD y) $(TD +$(INFIN)) $(TD no))
- * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD +$(INFIN)) $(TD no))
- * )
- */
-
-real hypot(real x, real y) @safe pure nothrow @nogc
-{
- // Scale x and y to avoid underflow and overflow.
- // If one is huge and the other tiny, return the larger.
- // If both are huge, avoid overflow by scaling by 1/sqrt(real.max/2).
- // If both are tiny, avoid underflow by scaling by sqrt(real.min_normal*real.epsilon).
-
- enum real SQRTMIN = 0.5 * sqrt(real.min_normal); // This is a power of 2.
- enum real SQRTMAX = 1.0L / SQRTMIN; // 2^^((max_exp)/2) = nextUp(sqrt(real.max))
-
- static assert(2*(SQRTMAX/2)*(SQRTMAX/2) <= real.max);
-
- // Proves that sqrt(real.max) ~~ 0.5/sqrt(real.min_normal)
- static assert(real.min_normal*real.max > 2 && real.min_normal*real.max <= 4);
-
- real u = fabs(x);
- real v = fabs(y);
- if (!(u >= v)) // check for NaN as well.
- {
- v = u;
- u = fabs(y);
- if (u == real.infinity) return u; // hypot(inf, nan) == inf
- if (v == real.infinity) return v; // hypot(nan, inf) == inf
- }
-
- // Now u >= v, or else one is NaN.
- if (v >= SQRTMAX*0.5)
- {
- // hypot(huge, huge) -- avoid overflow
- u *= SQRTMIN*0.5;
- v *= SQRTMIN*0.5;
- return sqrt(u*u + v*v) * SQRTMAX * 2.0;
- }
-
- if (u <= SQRTMIN)
- {
- // hypot (tiny, tiny) -- avoid underflow
- // This is only necessary to avoid setting the underflow
- // flag.
- u *= SQRTMAX / real.epsilon;
- v *= SQRTMAX / real.epsilon;
- return sqrt(u*u + v*v) * SQRTMIN * real.epsilon;
- }
-
- if (u * real.epsilon > v)
- {
- // hypot (huge, tiny) = huge
- return u;
- }
-
- // both are in the normal range
- return sqrt(u*u + v*v);
-}
-
-@safe unittest
-{
- static real[3][] vals = // x,y,hypot
- [
- [ 0.0, 0.0, 0.0],
- [ 0.0, -0.0, 0.0],
- [ -0.0, -0.0, 0.0],
- [ 3.0, 4.0, 5.0],
- [ -300, -400, 500],
- [0.0, 7.0, 7.0],
- [9.0, 9*real.epsilon, 9.0],
- [88/(64*sqrt(real.min_normal)), 105/(64*sqrt(real.min_normal)), 137/(64*sqrt(real.min_normal))],
- [88/(128*sqrt(real.min_normal)), 105/(128*sqrt(real.min_normal)), 137/(128*sqrt(real.min_normal))],
- [3*real.min_normal*real.epsilon, 4*real.min_normal*real.epsilon, 5*real.min_normal*real.epsilon],
- [ real.min_normal, real.min_normal, sqrt(2.0L)*real.min_normal],
- [ real.max/sqrt(2.0L), real.max/sqrt(2.0L), real.max],
- [ real.infinity, real.nan, real.infinity],
- [ real.nan, real.infinity, real.infinity],
- [ real.nan, real.nan, real.nan],
- [ real.nan, real.max, real.nan],
- [ real.max, real.nan, real.nan],
- ];
- for (int i = 0; i < vals.length; i++)
- {
- real x = vals[i][0];
- real y = vals[i][1];
- real z = vals[i][2];
- real h = hypot(x, y);
- assert(isIdentical(z,h) || feqrel(z, h) >= real.mant_dig - 1);
- }
-}
-
-/**************************************
- * Returns the value of x rounded upward to the next integer
- * (toward positive infinity).
- */
-real ceil(real x) @trusted pure nothrow @nogc
-{
- version (Win64_DMD_InlineAsm)
- {
- asm pure nothrow @nogc
- {
- naked ;
- fld real ptr [RCX] ;
- fstcw 8[RSP] ;
- mov AL,9[RSP] ;
- mov DL,AL ;
- and AL,0xC3 ;
- or AL,0x08 ; // round to +infinity
- mov 9[RSP],AL ;
- fldcw 8[RSP] ;
- frndint ;
- mov 9[RSP],DL ;
- fldcw 8[RSP] ;
- ret ;
- }
- }
- else version (MSVC_InlineAsm)
- {
- short cw;
- asm pure nothrow @nogc
- {
- fld x ;
- fstcw cw ;
- mov AL,byte ptr cw+1 ;
- mov DL,AL ;
- and AL,0xC3 ;
- or AL,0x08 ; // round to +infinity
- mov byte ptr cw+1,AL ;
- fldcw cw ;
- frndint ;
- mov byte ptr cw+1,DL ;
- fldcw cw ;
- }
- }
- else
- {
- // Special cases.
- if (isNaN(x) || isInfinity(x))
- return x;
-
- real y = floorImpl(x);
- if (y < x)
- y += 1.0;
-
- return y;
- }
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(ceil(+123.456L) == +124);
- assert(ceil(-123.456L) == -123);
- assert(ceil(-1.234L) == -1);
- assert(ceil(-0.123L) == 0);
- assert(ceil(0.0L) == 0);
- assert(ceil(+0.123L) == 1);
- assert(ceil(+1.234L) == 2);
- assert(ceil(real.infinity) == real.infinity);
- assert(isNaN(ceil(real.nan)));
- assert(isNaN(ceil(real.init)));
-}
-
-// ditto
-double ceil(double x) @trusted pure nothrow @nogc
-{
- // Special cases.
- if (isNaN(x) || isInfinity(x))
- return x;
-
- double y = floorImpl(x);
- if (y < x)
- y += 1.0;
-
- return y;
-}
-
-@safe pure nothrow @nogc unittest
-{
- assert(ceil(+123.456) == +124);
- assert(ceil(-123.456) == -123);
- assert(ceil(-1.234) == -1);
- assert(ceil(-0.123) == 0);
- assert(ceil(0.0) == 0);
- assert(ceil(+0.123) == 1);
- assert(ceil(+1.234) == 2);
- assert(ceil(double.infinity) == double.infinity);
- assert(isNaN(ceil(double.nan)));
- assert(isNaN(ceil(double.init)));
-}
-
-// ditto
-float ceil(float x) @trusted pure nothrow @nogc
-{
- // Special cases.
- if (isNaN(x) || isInfinity(x))
- return x;
-
- float y = floorImpl(x);
- if (y < x)
- y += 1.0;
-
- return y;
-}
-
-@safe pure nothrow @nogc unittest
-{
- assert(ceil(+123.456f) == +124);
- assert(ceil(-123.456f) == -123);
- assert(ceil(-1.234f) == -1);
- assert(ceil(-0.123f) == 0);
- assert(ceil(0.0f) == 0);
- assert(ceil(+0.123f) == 1);
- assert(ceil(+1.234f) == 2);
- assert(ceil(float.infinity) == float.infinity);
- assert(isNaN(ceil(float.nan)));
- assert(isNaN(ceil(float.init)));
-}
-
-/**************************************
- * Returns the value of x rounded downward to the next integer
- * (toward negative infinity).
- */
-real floor(real x) @trusted pure nothrow @nogc
-{
- version (Win64_DMD_InlineAsm)
- {
- asm pure nothrow @nogc
- {
- naked ;
- fld real ptr [RCX] ;
- fstcw 8[RSP] ;
- mov AL,9[RSP] ;
- mov DL,AL ;
- and AL,0xC3 ;
- or AL,0x04 ; // round to -infinity
- mov 9[RSP],AL ;
- fldcw 8[RSP] ;
- frndint ;
- mov 9[RSP],DL ;
- fldcw 8[RSP] ;
- ret ;
- }
- }
- else version (MSVC_InlineAsm)
- {
- short cw;
- asm pure nothrow @nogc
- {
- fld x ;
- fstcw cw ;
- mov AL,byte ptr cw+1 ;
- mov DL,AL ;
- and AL,0xC3 ;
- or AL,0x04 ; // round to -infinity
- mov byte ptr cw+1,AL ;
- fldcw cw ;
- frndint ;
- mov byte ptr cw+1,DL ;
- fldcw cw ;
- }
- }
- else
- {
- // Special cases.
- if (isNaN(x) || isInfinity(x) || x == 0.0)
- return x;
-
- return floorImpl(x);
- }
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(floor(+123.456L) == +123);
- assert(floor(-123.456L) == -124);
- assert(floor(-1.234L) == -2);
- assert(floor(-0.123L) == -1);
- assert(floor(0.0L) == 0);
- assert(floor(+0.123L) == 0);
- assert(floor(+1.234L) == 1);
- assert(floor(real.infinity) == real.infinity);
- assert(isNaN(floor(real.nan)));
- assert(isNaN(floor(real.init)));
-}
-
-// ditto
-double floor(double x) @trusted pure nothrow @nogc
-{
- // Special cases.
- if (isNaN(x) || isInfinity(x) || x == 0.0)
- return x;
-
- return floorImpl(x);
-}
-
-@safe pure nothrow @nogc unittest
-{
- assert(floor(+123.456) == +123);
- assert(floor(-123.456) == -124);
- assert(floor(-1.234) == -2);
- assert(floor(-0.123) == -1);
- assert(floor(0.0) == 0);
- assert(floor(+0.123) == 0);
- assert(floor(+1.234) == 1);
- assert(floor(double.infinity) == double.infinity);
- assert(isNaN(floor(double.nan)));
- assert(isNaN(floor(double.init)));
-}
-
-// ditto
-float floor(float x) @trusted pure nothrow @nogc
-{
- // Special cases.
- if (isNaN(x) || isInfinity(x) || x == 0.0)
- return x;
-
- return floorImpl(x);
-}
-
-@safe pure nothrow @nogc unittest
-{
- assert(floor(+123.456f) == +123);
- assert(floor(-123.456f) == -124);
- assert(floor(-1.234f) == -2);
- assert(floor(-0.123f) == -1);
- assert(floor(0.0f) == 0);
- assert(floor(+0.123f) == 0);
- assert(floor(+1.234f) == 1);
- assert(floor(float.infinity) == float.infinity);
- assert(isNaN(floor(float.nan)));
- assert(isNaN(floor(float.init)));
-}
-
-/**
- * Round `val` to a multiple of `unit`. `rfunc` specifies the rounding
- * function to use; by default this is `rint`, which uses the current
- * rounding mode.
- */
-Unqual!F quantize(alias rfunc = rint, F)(const F val, const F unit)
-if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F)
-{
- typeof(return) ret = val;
- if (unit != 0)
- {
- const scaled = val / unit;
- if (!scaled.isInfinity)
- ret = rfunc(scaled) * unit;
- }
- return ret;
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(12345.6789L.quantize(0.01L) == 12345.68L);
- assert(12345.6789L.quantize!floor(0.01L) == 12345.67L);
- assert(12345.6789L.quantize(22.0L) == 12342.0L);
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(12345.6789L.quantize(0) == 12345.6789L);
- assert(12345.6789L.quantize(real.infinity).isNaN);
- assert(12345.6789L.quantize(real.nan).isNaN);
- assert(real.infinity.quantize(0.01L) == real.infinity);
- assert(real.infinity.quantize(real.nan).isNaN);
- assert(real.nan.quantize(0.01L).isNaN);
- assert(real.nan.quantize(real.infinity).isNaN);
- assert(real.nan.quantize(real.nan).isNaN);
-}
-
-/**
- * Round `val` to a multiple of `pow(base, exp)`. `rfunc` specifies the
- * rounding function to use; by default this is `rint`, which uses the
- * current rounding mode.
- */
-Unqual!F quantize(real base, alias rfunc = rint, F, E)(const F val, const E exp)
-if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F && isIntegral!E)
-{
- // TODO: Compile-time optimization for power-of-two bases?
- return quantize!rfunc(val, pow(cast(F) base, exp));
-}
-
-/// ditto
-Unqual!F quantize(real base, long exp = 1, alias rfunc = rint, F)(const F val)
-if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F)
-{
- enum unit = cast(F) pow(base, exp);
- return quantize!rfunc(val, unit);
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(12345.6789L.quantize!10(-2) == 12345.68L);
- assert(12345.6789L.quantize!(10, -2) == 12345.68L);
- assert(12345.6789L.quantize!(10, floor)(-2) == 12345.67L);
- assert(12345.6789L.quantize!(10, -2, floor) == 12345.67L);
-
- assert(12345.6789L.quantize!22(1) == 12342.0L);
- assert(12345.6789L.quantize!22 == 12342.0L);
-}
-
-@safe pure nothrow @nogc unittest
-{
- import std.meta : AliasSeq;
-
- foreach (F; AliasSeq!(real, double, float))
- {
- const maxL10 = cast(int) F.max.log10.floor;
- const maxR10 = pow(cast(F) 10, maxL10);
- assert((cast(F) 0.9L * maxR10).quantize!10(maxL10) == maxR10);
- assert((cast(F)-0.9L * maxR10).quantize!10(maxL10) == -maxR10);
-
- assert(F.max.quantize(F.min_normal) == F.max);
- assert((-F.max).quantize(F.min_normal) == -F.max);
- assert(F.min_normal.quantize(F.max) == 0);
- assert((-F.min_normal).quantize(F.max) == 0);
- assert(F.min_normal.quantize(F.min_normal) == F.min_normal);
- assert((-F.min_normal).quantize(F.min_normal) == -F.min_normal);
- }
-}
-
-/******************************************
- * Rounds x to the nearest integer value, using the current rounding
- * mode.
- *
- * Unlike the rint functions, nearbyint does not raise the
- * FE_INEXACT exception.
- */
-real nearbyint(real x) @trusted nothrow @nogc
-{
- version (CRuntime_Microsoft)
- {
- assert(0); // not implemented in C library
- }
- else
- return core.stdc.math.nearbyintl(x);
-}
-
-/**********************************
- * Rounds x to the nearest integer value, using the current rounding
- * mode.
- * If the return value is not equal to x, the FE_INEXACT
- * exception is raised.
- * $(B nearbyint) performs
- * the same operation, but does not set the FE_INEXACT exception.
- */
-real rint(real x) @safe pure nothrow @nogc { pragma(inline, true); return core.math.rint(x); }
-//FIXME
-///ditto
-double rint(double x) @safe pure nothrow @nogc { return rint(cast(real) x); }
-//FIXME
-///ditto
-float rint(float x) @safe pure nothrow @nogc { return rint(cast(real) x); }
-
-@safe unittest
-{
- real function(real) print = &rint;
- assert(print != null);
-}
-
-/***************************************
- * Rounds x to the nearest integer value, using the current rounding
- * mode.
- *
- * This is generally the fastest method to convert a floating-point number
- * to an integer. Note that the results from this function
- * depend on the rounding mode, if the fractional part of x is exactly 0.5.
- * If using the default rounding mode (ties round to even integers)
- * lrint(4.5) == 4, lrint(5.5)==6.
- */
-long lrint(real x) @trusted pure nothrow @nogc
-{
- version (InlineAsm_X86_Any)
- {
- version (Win64)
- {
- asm pure nothrow @nogc
- {
- naked;
- fld real ptr [RCX];
- fistp qword ptr 8[RSP];
- mov RAX,8[RSP];
- ret;
- }
- }
- else
- {
- long n;
- asm pure nothrow @nogc
- {
- fld x;
- fistp n;
- }
- return n;
- }
- }
- else
- {
- alias F = floatTraits!(real);
- static if (F.realFormat == RealFormat.ieeeDouble)
- {
- long result;
-
- // Rounding limit when casting from real(double) to ulong.
- enum real OF = 4.50359962737049600000E15L;
-
- uint* vi = cast(uint*)(&x);
-
- // Find the exponent and sign
- uint msb = vi[MANTISSA_MSB];
- uint lsb = vi[MANTISSA_LSB];
- int exp = ((msb >> 20) & 0x7ff) - 0x3ff;
- const int sign = msb >> 31;
- msb &= 0xfffff;
- msb |= 0x100000;
-
- if (exp < 63)
- {
- if (exp >= 52)
- result = (cast(long) msb << (exp - 20)) | (lsb << (exp - 52));
- else
- {
- // Adjust x and check result.
- const real j = sign ? -OF : OF;
- x = (j + x) - j;
- msb = vi[MANTISSA_MSB];
- lsb = vi[MANTISSA_LSB];
- exp = ((msb >> 20) & 0x7ff) - 0x3ff;
- msb &= 0xfffff;
- msb |= 0x100000;
-
- if (exp < 0)
- result = 0;
- else if (exp < 20)
- result = cast(long) msb >> (20 - exp);
- else if (exp == 20)
- result = cast(long) msb;
- else
- result = (cast(long) msb << (exp - 20)) | (lsb >> (52 - exp));
- }
- }
- else
- {
- // It is left implementation defined when the number is too large.
- return cast(long) x;
- }
-
- return sign ? -result : result;
- }
- else static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- long result;
-
- // Rounding limit when casting from real(80-bit) to ulong.
- static if (F.realFormat == RealFormat.ieeeExtended)
- enum real OF = 9.22337203685477580800E18L;
- else
- enum real OF = 4.50359962737049600000E15L;
-
- ushort* vu = cast(ushort*)(&x);
- uint* vi = cast(uint*)(&x);
-
- // Find the exponent and sign
- int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
- const int sign = (vu[F.EXPPOS_SHORT] >> 15) & 1;
-
- if (exp < 63)
- {
- // Adjust x and check result.
- const real j = sign ? -OF : OF;
- x = (j + x) - j;
- exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
-
- version (LittleEndian)
- {
- if (exp < 0)
- result = 0;
- else if (exp <= 31)
- result = vi[1] >> (31 - exp);
- else
- result = (cast(long) vi[1] << (exp - 31)) | (vi[0] >> (63 - exp));
- }
- else
- {
- if (exp < 0)
- result = 0;
- else if (exp <= 31)
- result = vi[1] >> (31 - exp);
- else
- result = (cast(long) vi[1] << (exp - 31)) | (vi[2] >> (63 - exp));
- }
- }
- else
- {
- // It is left implementation defined when the number is too large
- // to fit in a 64bit long.
- return cast(long) x;
- }
-
- return sign ? -result : result;
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- const vu = cast(ushort*)(&x);
-
- // Find the exponent and sign
- const sign = (vu[F.EXPPOS_SHORT] >> 15) & 1;
- if ((vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1) > 63)
- {
- // The result is left implementation defined when the number is
- // too large to fit in a 64 bit long.
- return cast(long) x;
- }
-
- // Force rounding of lower bits according to current rounding
- // mode by adding ±2^-112 and subtracting it again.
- enum OF = 5.19229685853482762853049632922009600E33L;
- const j = sign ? -OF : OF;
- x = (j + x) - j;
-
- const exp = (vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1);
- const implicitOne = 1UL << 48;
- auto vl = cast(ulong*)(&x);
- vl[MANTISSA_MSB] &= implicitOne - 1;
- vl[MANTISSA_MSB] |= implicitOne;
-
- long result;
-
- if (exp < 0)
- result = 0;
- else if (exp <= 48)
- result = vl[MANTISSA_MSB] >> (48 - exp);
- else
- result = (vl[MANTISSA_MSB] << (exp - 48)) | (vl[MANTISSA_LSB] >> (112 - exp));
-
- return sign ? -result : result;
- }
- else
- {
- static assert(false, "real type not supported by lrint()");
- }
- }
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(lrint(4.5) == 4);
- assert(lrint(5.5) == 6);
- assert(lrint(-4.5) == -4);
- assert(lrint(-5.5) == -6);
-
- assert(lrint(int.max - 0.5) == 2147483646L);
- assert(lrint(int.max + 0.5) == 2147483648L);
- assert(lrint(int.min - 0.5) == -2147483648L);
- assert(lrint(int.min + 0.5) == -2147483648L);
-}
-
-static if (real.mant_dig >= long.sizeof * 8)
-{
- @safe pure nothrow @nogc unittest
- {
- assert(lrint(long.max - 1.5L) == long.max - 1);
- assert(lrint(long.max - 0.5L) == long.max - 1);
- assert(lrint(long.min + 0.5L) == long.min);
- assert(lrint(long.min + 1.5L) == long.min + 2);
- }
-}
-
-/*******************************************
- * Return the value of x rounded to the nearest integer.
- * If the fractional part of x is exactly 0.5, the return value is
- * rounded away from zero.
- */
-real round(real x) @trusted nothrow @nogc
-{
- version (CRuntime_Microsoft)
- {
- auto old = FloatingPointControl.getControlState();
- FloatingPointControl.setControlState(
- (old & ~FloatingPointControl.roundingMask) | FloatingPointControl.roundToZero
- );
- x = rint((x >= 0) ? x + 0.5 : x - 0.5);
- FloatingPointControl.setControlState(old);
- return x;
- }
- else
- return core.stdc.math.roundl(x);
-}
-
-/**********************************************
- * Return the value of x rounded to the nearest integer.
- *
- * If the fractional part of x is exactly 0.5, the return value is rounded
- * away from zero.
- *
- * $(BLUE This function is not implemented for Digital Mars C runtime.)
- */
-long lround(real x) @trusted nothrow @nogc
-{
- version (CRuntime_DigitalMars)
- assert(0, "lround not implemented");
- else
- return core.stdc.math.llroundl(x);
-}
-
-///
-@safe nothrow @nogc unittest
-{
- version (CRuntime_DigitalMars) {}
- else
- {
- assert(lround(0.49) == 0);
- assert(lround(0.5) == 1);
- assert(lround(1.5) == 2);
- }
-}
-
-/****************************************************
- * Returns the integer portion of x, dropping the fractional portion.
- *
- * This is also known as "chop" rounding.
- */
-real trunc(real x) @trusted nothrow @nogc
-{
- version (Win64_DMD_InlineAsm)
- {
- asm pure nothrow @nogc
- {
- naked ;
- fld real ptr [RCX] ;
- fstcw 8[RSP] ;
- mov AL,9[RSP] ;
- mov DL,AL ;
- and AL,0xC3 ;
- or AL,0x0C ; // round to 0
- mov 9[RSP],AL ;
- fldcw 8[RSP] ;
- frndint ;
- mov 9[RSP],DL ;
- fldcw 8[RSP] ;
- ret ;
- }
- }
- else version (MSVC_InlineAsm)
- {
- short cw;
- asm pure nothrow @nogc
- {
- fld x ;
- fstcw cw ;
- mov AL,byte ptr cw+1 ;
- mov DL,AL ;
- and AL,0xC3 ;
- or AL,0x0C ; // round to 0
- mov byte ptr cw+1,AL ;
- fldcw cw ;
- frndint ;
- mov byte ptr cw+1,DL ;
- fldcw cw ;
- }
- }
- else
- return core.stdc.math.truncl(x);
-}
-
-/****************************************************
- * Calculate the remainder x REM y, following IEC 60559.
- *
- * REM is the value of x - y * n, where n is the integer nearest the exact
- * value of x / y.
- * If |n - x / y| == 0.5, n is even.
- * If the result is zero, it has the same sign as x.
- * Otherwise, the sign of the result is the sign of x / y.
- * Precision mode has no effect on the remainder functions.
- *
- * remquo returns n in the parameter n.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH y) $(TH remainder(x, y)) $(TH n) $(TH invalid?))
- * $(TR $(TD $(PLUSMN)0.0) $(TD not 0.0) $(TD $(PLUSMN)0.0) $(TD 0.0) $(TD no))
- * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(NAN)) $(TD ?) $(TD yes))
- * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(NAN)) $(TD ?) $(TD yes))
- * $(TR $(TD != $(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD ?) $(TD no))
- * )
- *
- * $(BLUE `remquo` and `remainder` not supported on Windows.)
- */
-real remainder(real x, real y) @trusted nothrow @nogc
-{
- version (CRuntime_Microsoft)
- {
- int n;
- return remquo(x, y, n);
- }
- else
- return core.stdc.math.remainderl(x, y);
-}
-
-real remquo(real x, real y, out int n) @trusted nothrow @nogc /// ditto
-{
- version (Posix)
- return core.stdc.math.remquol(x, y, &n);
- else
- assert(0, "remquo not implemented");
-}
-
-
-version (IeeeFlagsSupport)
-{
-
-/** IEEE exception status flags ('sticky bits')
-
- These flags indicate that an exceptional floating-point condition has occurred.
- They indicate that a NaN or an infinity has been generated, that a result
- is inexact, or that a signalling NaN has been encountered. If floating-point
- exceptions are enabled (unmasked), a hardware exception will be generated
- instead of setting these flags.
- */
-struct IeeeFlags
-{
-private:
- // The x87 FPU status register is 16 bits.
- // The Pentium SSE2 status register is 32 bits.
- // The ARM and PowerPC FPSCR is a 32-bit register.
- // The SPARC FSR is a 32bit register (64 bits for SPARC 7 & 8, but high bits are uninteresting).
- // The RISC-V (32 & 64 bit) fcsr is 32-bit register.
- uint flags;
-
- version (CRuntime_Microsoft)
- {
- // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
- // Applies to both x87 status word (16 bits) and SSE2 status word(32 bits).
- enum : int
- {
- INEXACT_MASK = 0x20,
- UNDERFLOW_MASK = 0x10,
- OVERFLOW_MASK = 0x08,
- DIVBYZERO_MASK = 0x04,
- INVALID_MASK = 0x01,
-
- EXCEPTIONS_MASK = 0b11_1111
- }
- // Don't bother about subnormals, they are not supported on most CPUs.
- // SUBNORMAL_MASK = 0x02;
- }
- else
- {
- enum : int
- {
- INEXACT_MASK = core.stdc.fenv.FE_INEXACT,
- UNDERFLOW_MASK = core.stdc.fenv.FE_UNDERFLOW,
- OVERFLOW_MASK = core.stdc.fenv.FE_OVERFLOW,
- DIVBYZERO_MASK = core.stdc.fenv.FE_DIVBYZERO,
- INVALID_MASK = core.stdc.fenv.FE_INVALID,
- EXCEPTIONS_MASK = core.stdc.fenv.FE_ALL_EXCEPT,
- }
- }
-
-private:
- static uint getIeeeFlags()
- {
- version (GNU)
- {
- version (X86_Any)
- {
- ushort sw;
- asm pure nothrow @nogc
- {
- "fstsw %0" : "=a" (sw);
- }
- // OR the result with the SSE2 status register (MXCSR).
- if (haveSSE)
- {
- uint mxcsr;
- asm pure nothrow @nogc
- {
- "stmxcsr %0" : "=m" (mxcsr);
- }
- return (sw | mxcsr) & EXCEPTIONS_MASK;
- }
- else
- return sw & EXCEPTIONS_MASK;
- }
- else version (ARM)
- {
- version (ARM_SoftFloat)
- return 0;
- else
- {
- uint result = void;
- asm pure nothrow @nogc
- {
- "vmrs %0, FPSCR; and %0, %0, #0x1F;" : "=r" (result);
- }
- return result;
- }
- }
- else version (RISCV_Any)
- {
- version (D_SoftFloat)
- return 0;
- else
- {
- uint result = void;
- asm pure nothrow @nogc
- {
- "frflags %0" : "=r" (result);
- }
- return result;
- }
- }
- else
- assert(0, "Not yet supported");
- }
- else
- version (InlineAsm_X86_Any)
- {
- ushort sw;
- asm pure nothrow @nogc { fstsw sw; }
-
- // OR the result with the SSE2 status register (MXCSR).
- if (haveSSE)
- {
- uint mxcsr;
- asm pure nothrow @nogc { stmxcsr mxcsr; }
- return (sw | mxcsr) & EXCEPTIONS_MASK;
- }
- else return sw & EXCEPTIONS_MASK;
- }
- else version (SPARC)
- {
- /*
- int retval;
- asm pure nothrow @nogc { st %fsr, retval; }
- return retval;
- */
- assert(0, "Not yet supported");
- }
- else version (ARM)
- {
- assert(false, "Not yet supported.");
- }
- else
- assert(0, "Not yet supported");
- }
-
- static void resetIeeeFlags() @nogc
- {
- version (GNU)
- {
- version (X86_Any)
- {
- asm nothrow @nogc
- {
- "fnclex";
- }
-
- // Also clear exception flags in MXCSR, SSE's control register.
- if (haveSSE)
- {
- uint mxcsr;
- asm nothrow @nogc
- {
- "stmxcsr %0" : "=m" (mxcsr);
- }
- mxcsr &= ~EXCEPTIONS_MASK;
- asm nothrow @nogc
- {
- "ldmxcsr %0" : : "m" (mxcsr);
- }
- }
- }
- else version (ARM)
- {
- version (ARM_SoftFloat)
- return;
- else
- {
- uint old = FloatingPointControl.getControlState();
- old &= ~0b11111; // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0408i/Chdfifdc.html
- asm nothrow @nogc
- {
- "vmsr FPSCR, %0" : : "r" (old);
- }
- }
- }
- else version (RISCV_Any)
- {
- version (D_SoftFloat)
- return;
- else
- {
- uint newValues = 0x0;
- asm nothrow @nogc
- {
- "fsflags %0" : : "r" (newValues);
- }
- }
- }
- else
- assert(0, "Not yet supported");
- }
- else
- version (InlineAsm_X86_Any)
- {
- asm nothrow @nogc
- {
- fnclex;
- }
-
- // Also clear exception flags in MXCSR, SSE's control register.
- if (haveSSE)
- {
- uint mxcsr;
- asm nothrow @nogc { stmxcsr mxcsr; }
- mxcsr &= ~EXCEPTIONS_MASK;
- asm nothrow @nogc { ldmxcsr mxcsr; }
- }
- }
- else
- {
- /* SPARC:
- int tmpval;
- asm pure nothrow @nogc { st %fsr, tmpval; }
- tmpval &=0xFFFF_FC00;
- asm pure nothrow @nogc { ld tmpval, %fsr; }
- */
- assert(0, "Not yet supported");
- }
- }
-public:
- version (IeeeFlagsSupport)
- {
-
- /**
- * The result cannot be represented exactly, so rounding occurred.
- * Example: `x = sin(0.1);`
- */
- @property bool inexact() const { return (flags & INEXACT_MASK) != 0; }
-
- /**
- * A zero was generated by underflow
- * Example: `x = real.min*real.epsilon/2;`
- */
- @property bool underflow() const { return (flags & UNDERFLOW_MASK) != 0; }
-
- /**
- * An infinity was generated by overflow
- * Example: `x = real.max*2;`
- */
- @property bool overflow() const { return (flags & OVERFLOW_MASK) != 0; }
-
- /**
- * An infinity was generated by division by zero
- * Example: `x = 3/0.0;`
- */
- @property bool divByZero() const { return (flags & DIVBYZERO_MASK) != 0; }
-
- /**
- * A machine NaN was generated.
- * Example: `x = real.infinity * 0.0;`
- */
- @property bool invalid() const { return (flags & INVALID_MASK) != 0; }
-
- }
-}
-
-///
-version (IeeeFlagsUnittest)
-@system unittest
-{
- static void func() {
- int a = 10 * 10;
- }
- pragma(inline, false) static void blockopt(ref real x) {}
- real a = 3.5;
- // Set all the flags to zero
- resetIeeeFlags();
- assert(!ieeeFlags.divByZero);
- blockopt(a); // avoid constant propagation by the optimizer
- // Perform a division by zero.
- a /= 0.0L;
- assert(a == real.infinity);
- assert(ieeeFlags.divByZero);
- blockopt(a); // avoid constant propagation by the optimizer
- // Create a NaN
- a *= 0.0L;
- assert(ieeeFlags.invalid);
- assert(isNaN(a));
-
- // Check that calling func() has no effect on the
- // status flags.
- IeeeFlags f = ieeeFlags;
- func();
- assert(ieeeFlags == f);
-}
-
-version (IeeeFlagsUnittest)
-@system unittest
-{
- import std.meta : AliasSeq;
-
- static struct Test
- {
- void delegate() action;
- bool function() ieeeCheck;
- }
-
- foreach (T; AliasSeq!(float, double, real))
- {
- T x; /* Needs to be here to trick -O. It would optimize away the
- calculations if x were local to the function literals. */
- auto tests = [
- Test(
- () { x = 1; x += 0.1; },
- () => ieeeFlags.inexact
- ),
- Test(
- () { x = T.min_normal; x /= T.max; },
- () => ieeeFlags.underflow
- ),
- Test(
- () { x = T.max; x += T.max; },
- () => ieeeFlags.overflow
- ),
- Test(
- () { x = 1; x /= 0; },
- () => ieeeFlags.divByZero
- ),
- Test(
- () { x = 0; x /= 0; },
- () => ieeeFlags.invalid
- )
- ];
- foreach (test; tests)
- {
- resetIeeeFlags();
- assert(!test.ieeeCheck());
- test.action();
- assert(test.ieeeCheck());
- }
- }
-}
-
-/// Set all of the floating-point status flags to false.
-void resetIeeeFlags() @nogc { IeeeFlags.resetIeeeFlags(); }
-
-/// Returns: snapshot of the current state of the floating-point status flags
-@property IeeeFlags ieeeFlags()
-{
- return IeeeFlags(IeeeFlags.getIeeeFlags());
-}
-
-} // IeeeFlagsSupport
-
-
-version (FloatingPointControlSupport)
-{
-
-/** Control the Floating point hardware
-
- Change the IEEE754 floating-point rounding mode and the floating-point
- hardware exceptions.
-
- By default, the rounding mode is roundToNearest and all hardware exceptions
- are disabled. For most applications, debugging is easier if the $(I division
- by zero), $(I overflow), and $(I invalid operation) exceptions are enabled.
- These three are combined into a $(I severeExceptions) value for convenience.
- Note in particular that if $(I invalidException) is enabled, a hardware trap
- will be generated whenever an uninitialized floating-point variable is used.
-
- All changes are temporary. The previous state is restored at the
- end of the scope.
-
-
-Example:
-----
-{
- FloatingPointControl fpctrl;
-
- // Enable hardware exceptions for division by zero, overflow to infinity,
- // invalid operations, and uninitialized floating-point variables.
- fpctrl.enableExceptions(FloatingPointControl.severeExceptions);
-
- // This will generate a hardware exception, if x is a
- // default-initialized floating point variable:
- real x; // Add `= 0` or even `= real.nan` to not throw the exception.
- real y = x * 3.0;
-
- // The exception is only thrown for default-uninitialized NaN-s.
- // NaN-s with other payload are valid:
- real z = y * real.nan; // ok
-
- // Changing the rounding mode:
- fpctrl.rounding = FloatingPointControl.roundUp;
- assert(rint(1.1) == 2);
-
- // The set hardware exceptions will be disabled when leaving this scope.
- // The original rounding mode will also be restored.
-}
-
-// Ensure previous values are returned:
-assert(!FloatingPointControl.enabledExceptions);
-assert(FloatingPointControl.rounding == FloatingPointControl.roundToNearest);
-assert(rint(1.1) == 1);
-----
-
- */
-struct FloatingPointControl
-{
- alias RoundingMode = uint; ///
-
- version (StdDdoc)
- {
- enum : RoundingMode
- {
- /** IEEE rounding modes.
- * The default mode is roundToNearest.
- *
- * roundingMask = A mask of all rounding modes.
- */
- roundToNearest,
- roundDown, /// ditto
- roundUp, /// ditto
- roundToZero, /// ditto
- roundingMask, /// ditto
- }
- }
- else version (CRuntime_Microsoft)
- {
- // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
- enum : RoundingMode
- {
- roundToNearest = 0x0000,
- roundDown = 0x0400,
- roundUp = 0x0800,
- roundToZero = 0x0C00,
- roundingMask = roundToNearest | roundDown
- | roundUp | roundToZero,
- }
- }
- else
- {
- enum : RoundingMode
- {
- roundToNearest = core.stdc.fenv.FE_TONEAREST,
- roundDown = core.stdc.fenv.FE_DOWNWARD,
- roundUp = core.stdc.fenv.FE_UPWARD,
- roundToZero = core.stdc.fenv.FE_TOWARDZERO,
- roundingMask = roundToNearest | roundDown
- | roundUp | roundToZero,
- }
- }
-
- //// Change the floating-point hardware rounding mode
- @property void rounding(RoundingMode newMode) @nogc
- {
- initialize();
- setControlState(cast(ushort)((getControlState() & (-1 - roundingMask)) | (newMode & roundingMask)));
- }
-
- /// Returns: the currently active rounding mode
- @property static RoundingMode rounding() @nogc
- {
- return cast(RoundingMode)(getControlState() & roundingMask);
- }
-
- alias ExceptionMask = uint; ///
-
- version (StdDdoc)
- {
- enum : ExceptionMask
- {
- /** IEEE hardware exceptions.
- * By default, all exceptions are masked (disabled).
- *
- * severeExceptions = The overflow, division by zero, and invalid
- * exceptions.
- */
- subnormalException,
- inexactException, /// ditto
- underflowException, /// ditto
- overflowException, /// ditto
- divByZeroException, /// ditto
- invalidException, /// ditto
- severeExceptions, /// ditto
- allExceptions, /// ditto
- }
- }
- else version (ARM_Any)
- {
- enum : ExceptionMask
- {
- subnormalException = 0x8000,
- inexactException = 0x1000,
- underflowException = 0x0800,
- overflowException = 0x0400,
- divByZeroException = 0x0200,
- invalidException = 0x0100,
- severeExceptions = overflowException | divByZeroException
- | invalidException,
- allExceptions = severeExceptions | underflowException
- | inexactException | subnormalException,
- }
- }
- else version (PPC_Any)
- {
- enum : ExceptionMask
- {
- inexactException = 0x0008,
- divByZeroException = 0x0010,
- underflowException = 0x0020,
- overflowException = 0x0040,
- invalidException = 0x0080,
- severeExceptions = overflowException | divByZeroException
- | invalidException,
- allExceptions = severeExceptions | underflowException
- | inexactException,
- }
- }
- else version (HPPA)
- {
- enum : ExceptionMask
- {
- inexactException = 0x01,
- underflowException = 0x02,
- overflowException = 0x04,
- divByZeroException = 0x08,
- invalidException = 0x10,
- severeExceptions = overflowException | divByZeroException
- | invalidException,
- allExceptions = severeExceptions | underflowException
- | inexactException,
- }
- }
- else version (MIPS_Any)
- {
- enum : ExceptionMask
- {
- inexactException = 0x0080,
- divByZeroException = 0x0400,
- overflowException = 0x0200,
- underflowException = 0x0100,
- invalidException = 0x0800,
- severeExceptions = overflowException | divByZeroException
- | invalidException,
- allExceptions = severeExceptions | underflowException
- | inexactException,
- }
- }
- else version (SPARC_Any)
- {
- enum : ExceptionMask
- {
- inexactException = 0x0800000,
- divByZeroException = 0x1000000,
- overflowException = 0x4000000,
- underflowException = 0x2000000,
- invalidException = 0x8000000,
- severeExceptions = overflowException | divByZeroException
- | invalidException,
- allExceptions = severeExceptions | underflowException
- | inexactException,
- }
- }
- else version (IBMZ_Any)
- {
- enum : ExceptionMask
- {
- inexactException = 0x08000000,
- divByZeroException = 0x40000000,
- overflowException = 0x20000000,
- underflowException = 0x10000000,
- invalidException = 0x80000000,
- severeExceptions = overflowException | divByZeroException
- | invalidException,
- allExceptions = severeExceptions | underflowException
- | inexactException,
- }
- }
- else version (RISCV_Any)
- {
- enum : ExceptionMask
- {
- inexactException = 0x01,
- divByZeroException = 0x02,
- underflowException = 0x04,
- overflowException = 0x08,
- invalidException = 0x10,
- severeExceptions = overflowException | divByZeroException
- | invalidException,
- allExceptions = severeExceptions | underflowException
- | inexactException,
- }
- }
- else version (X86_Any)
- {
- enum : ExceptionMask
- {
- inexactException = 0x20,
- underflowException = 0x10,
- overflowException = 0x08,
- divByZeroException = 0x04,
- subnormalException = 0x02,
- invalidException = 0x01,
- severeExceptions = overflowException | divByZeroException
- | invalidException,
- allExceptions = severeExceptions | underflowException
- | inexactException | subnormalException,
- }
- }
- else
- static assert(false, "Not implemented for this architecture");
-
-public:
- /// Returns: true if the current FPU supports exception trapping
- @property static bool hasExceptionTraps() @safe nothrow @nogc
- {
- version (X86_Any)
- return true;
- else version (PPC_Any)
- return true;
- else version (MIPS_Any)
- return true;
- else version (ARM_Any)
- {
- auto oldState = getControlState();
- // If exceptions are not supported, we set the bit but read it back as zero
- // https://sourceware.org/ml/libc-ports/2012-06/msg00091.html
- setControlState(oldState | divByZeroException);
- immutable result = (getControlState() & allExceptions) != 0;
- setControlState(oldState);
- return result;
- }
- else
- assert(0, "Not yet supported");
- }
-
- /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together.
- void enableExceptions(ExceptionMask exceptions) @nogc
- {
- assert(hasExceptionTraps);
- initialize();
- version (X86_Any)
- setControlState(getControlState() & ~(exceptions & allExceptions));
- else
- setControlState(getControlState() | (exceptions & allExceptions));
- }
-
- /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together.
- void disableExceptions(ExceptionMask exceptions) @nogc
- {
- assert(hasExceptionTraps);
- initialize();
- version (X86_Any)
- setControlState(getControlState() | (exceptions & allExceptions));
- else
- setControlState(getControlState() & ~(exceptions & allExceptions));
- }
-
- /// Returns: the exceptions which are currently enabled (unmasked)
- @property static ExceptionMask enabledExceptions() @nogc
- {
- assert(hasExceptionTraps);
- version (X86_Any)
- return (getControlState() & allExceptions) ^ allExceptions;
- else
- return (getControlState() & allExceptions);
- }
-
- /// Clear all pending exceptions, then restore the original exception state and rounding mode.
- ~this() @nogc
- {
- clearExceptions();
- if (initialized)
- setControlState(savedState);
- }
-
-private:
- ControlState savedState;
-
- bool initialized = false;
-
- version (ARM_Any)
- {
- alias ControlState = uint;
- }
- else version (HPPA)
- {
- alias ControlState = uint;
- }
- else version (PPC_Any)
- {
- alias ControlState = uint;
- }
- else version (MIPS_Any)
- {
- alias ControlState = uint;
- }
- else version (SPARC_Any)
- {
- alias ControlState = ulong;
- }
- else version (IBMZ_Any)
- {
- alias ControlState = uint;
- }
- else version (RISCV_Any)
- {
- alias ControlState = uint;
- }
- else version (X86_Any)
- {
- alias ControlState = ushort;
- }
- else
- static assert(false, "Not implemented for this architecture");
-
- void initialize() @nogc
- {
- // BUG: This works around the absence of this() constructors.
- if (initialized) return;
- clearExceptions();
- savedState = getControlState();
- initialized = true;
- }
-
- // Clear all pending exceptions
- static void clearExceptions() @nogc
- {
- version (IeeeFlagsSupport)
- resetIeeeFlags();
- else
- static assert(false, "Not implemented for this architecture");
- }
-
- // Read from the control register
- static ControlState getControlState() @trusted nothrow @nogc
- {
- version (GNU)
- {
- version (X86_Any)
- {
- ControlState cont;
- asm pure nothrow @nogc
- {
- "fstcw %0" : "=m" (cont);
- }
- return cont;
- }
- else version (AArch64)
- {
- ControlState cont;
- asm pure nothrow @nogc
- {
- "mrs %0, FPCR;" : "=r" (cont);
- }
- return cont;
- }
- else version (ARM)
- {
- ControlState cont;
- version (ARM_SoftFloat)
- cont = 0;
- else
- {
- asm pure nothrow @nogc
- {
- "vmrs %0, FPSCR" : "=r" (cont);
- }
- }
- return cont;
- }
- else version (RISCV_Any)
- {
- version (D_SoftFloat)
- return 0;
- else
- {
- ControlState cont;
- asm pure nothrow @nogc
- {
- "frcsr %0" : "=r" (cont);
- }
- return cont;
- }
- }
- else
- assert(0, "Not yet supported");
- }
- else
- version (D_InlineAsm_X86)
- {
- short cont;
- asm pure nothrow @nogc
- {
- xor EAX, EAX;
- fstcw cont;
- }
- return cont;
- }
- else
- version (D_InlineAsm_X86_64)
- {
- short cont;
- asm pure nothrow @nogc
- {
- xor RAX, RAX;
- fstcw cont;
- }
- return cont;
- }
- else
- assert(0, "Not yet supported");
- }
-
- // Set the control register
- static void setControlState(ControlState newState) @trusted nothrow @nogc
- {
- version (GNU)
- {
- version (X86_Any)
- {
- asm nothrow @nogc
- {
- "fclex; fldcw %0" : : "m" (newState);
- }
-
- // Also update MXCSR, SSE's control register.
- if (haveSSE)
- {
- uint mxcsr;
- asm nothrow @nogc
- {
- "stmxcsr %0" : "=m" (mxcsr);
- }
-
- /* In the FPU control register, rounding mode is in bits 10 and
- 11. In MXCSR it's in bits 13 and 14. */
- mxcsr &= ~(roundingMask << 3); // delete old rounding mode
- mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
-
- /* In the FPU control register, masks are bits 0 through 5.
- In MXCSR they're 7 through 12. */
- mxcsr &= ~(allExceptions << 7); // delete old masks
- mxcsr |= (newState & allExceptions) << 7; // write new exception masks
-
- asm nothrow @nogc
- {
- "ldmxcsr %0" : : "m" (mxcsr);
- }
- }
- }
- else version (AArch64)
- {
- asm nothrow @nogc
- {
- "msr FPCR, %0;" : : "r" (newState);
- }
- }
- else version (ARM)
- {
- version (ARM_SoftFloat)
- return;
- else
- {
- asm nothrow @nogc
- {
- "vmsr FPSCR, %0" : : "r" (newState);
- }
- }
- }
- else version (RISCV_Any)
- {
- version (D_SoftFloat)
- return;
- else
- {
- asm nothrow @nogc
- {
- "fscsr %0" : : "r" (newState);
- }
- }
- }
- else
- assert(0, "Not yet supported");
- }
- else
- version (InlineAsm_X86_Any)
- {
- asm nothrow @nogc
- {
- fclex;
- fldcw newState;
- }
-
- // Also update MXCSR, SSE's control register.
- if (haveSSE)
- {
- uint mxcsr;
- asm nothrow @nogc { stmxcsr mxcsr; }
-
- /* In the FPU control register, rounding mode is in bits 10 and
- 11. In MXCSR it's in bits 13 and 14. */
- mxcsr &= ~(roundingMask << 3); // delete old rounding mode
- mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
-
- /* In the FPU control register, masks are bits 0 through 5.
- In MXCSR they're 7 through 12. */
- mxcsr &= ~(allExceptions << 7); // delete old masks
- mxcsr |= (newState & allExceptions) << 7; // write new exception masks
-
- asm nothrow @nogc { ldmxcsr mxcsr; }
- }
- }
- else
- assert(0, "Not yet supported");
- }
-}
-
-@system unittest
-{
- void ensureDefaults()
- {
- assert(FloatingPointControl.rounding
- == FloatingPointControl.roundToNearest);
- if (FloatingPointControl.hasExceptionTraps)
- assert(FloatingPointControl.enabledExceptions == 0);
- }
-
- {
- FloatingPointControl ctrl;
- }
- ensureDefaults();
-
- {
- FloatingPointControl ctrl;
- ctrl.rounding = FloatingPointControl.roundDown;
- assert(FloatingPointControl.rounding == FloatingPointControl.roundDown);
- }
- ensureDefaults();
-
- if (FloatingPointControl.hasExceptionTraps)
- {
- FloatingPointControl ctrl;
- ctrl.enableExceptions(FloatingPointControl.divByZeroException
- | FloatingPointControl.overflowException);
- assert(ctrl.enabledExceptions ==
- (FloatingPointControl.divByZeroException
- | FloatingPointControl.overflowException));
-
- ctrl.rounding = FloatingPointControl.roundUp;
- assert(FloatingPointControl.rounding == FloatingPointControl.roundUp);
- }
- ensureDefaults();
-}
-
-version (FloatingPointControlUnittest)
-@system unittest // rounding
-{
- import std.meta : AliasSeq;
-
- foreach (T; AliasSeq!(float, double, real))
- {
- /* Be careful with changing the rounding mode, it interferes
- * with common subexpressions. Changing rounding modes should
- * be done with separate functions that are not inlined.
- */
-
- {
- static T addRound(T)(uint rm)
- {
- pragma(inline, false) static void blockopt(ref T x) {}
- pragma(inline, false);
- FloatingPointControl fpctrl;
- fpctrl.rounding = rm;
- T x = 1;
- blockopt(x); // avoid constant propagation by the optimizer
- x += 0.1;
- return x;
- }
-
- T u = addRound!(T)(FloatingPointControl.roundUp);
- T d = addRound!(T)(FloatingPointControl.roundDown);
- T z = addRound!(T)(FloatingPointControl.roundToZero);
-
- assert(u > d);
- assert(z == d);
- }
-
- {
- static T subRound(T)(uint rm)
- {
- pragma(inline, false) static void blockopt(ref T x) {}
- pragma(inline, false);
- FloatingPointControl fpctrl;
- fpctrl.rounding = rm;
- T x = -1;
- blockopt(x); // avoid constant propagation by the optimizer
- x -= 0.1;
- return x;
- }
-
- T u = subRound!(T)(FloatingPointControl.roundUp);
- T d = subRound!(T)(FloatingPointControl.roundDown);
- T z = subRound!(T)(FloatingPointControl.roundToZero);
-
- assert(u > d);
- assert(z == u);
- }
- }
-}
-
-} // FloatingPointControlSupport
-
-
-/*********************************
- * Determines if $(D_PARAM x) is NaN.
- * Params:
- * x = a floating point number.
- * Returns:
- * $(D true) if $(D_PARAM x) is Nan.
- */
-bool isNaN(X)(X x) @nogc @trusted pure nothrow
-if (isFloatingPoint!(X))
-{
- alias F = floatTraits!(X);
- static if (F.realFormat == RealFormat.ieeeSingle)
- {
- const uint p = *cast(uint *)&x;
- return ((p & 0x7F80_0000) == 0x7F80_0000)
- && p & 0x007F_FFFF; // not infinity
- }
- else static if (F.realFormat == RealFormat.ieeeDouble)
- {
- const ulong p = *cast(ulong *)&x;
- return ((p & 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000)
- && p & 0x000F_FFFF_FFFF_FFFF; // not infinity
- }
- else static if (F.realFormat == RealFormat.ieeeExtended)
- {
- const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
- const ulong ps = *cast(ulong *)&x;
- return e == F.EXPMASK &&
- ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
- const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB];
- const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB];
- return e == F.EXPMASK &&
- (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0;
- }
- else
- {
- return x != x;
- }
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert( isNaN(float.init));
- assert( isNaN(-double.init));
- assert( isNaN(real.nan));
- assert( isNaN(-real.nan));
- assert(!isNaN(cast(float) 53.6));
- assert(!isNaN(cast(real)-53.6));
-}
-
-@safe pure nothrow @nogc unittest
-{
- import std.meta : AliasSeq;
-
- foreach (T; AliasSeq!(float, double, real))
- {
- // CTFE-able tests
- assert(isNaN(T.init));
- assert(isNaN(-T.init));
- assert(isNaN(T.nan));
- assert(isNaN(-T.nan));
- assert(!isNaN(T.infinity));
- assert(!isNaN(-T.infinity));
- assert(!isNaN(cast(T) 53.6));
- assert(!isNaN(cast(T)-53.6));
-
- // Runtime tests
- shared T f;
- f = T.init;
- assert(isNaN(f));
- assert(isNaN(-f));
- f = T.nan;
- assert(isNaN(f));
- assert(isNaN(-f));
- f = T.infinity;
- assert(!isNaN(f));
- assert(!isNaN(-f));
- f = cast(T) 53.6;
- assert(!isNaN(f));
- assert(!isNaN(-f));
- }
-}
-
-/*********************************
- * Determines if $(D_PARAM x) is finite.
- * Params:
- * x = a floating point number.
- * Returns:
- * $(D true) if $(D_PARAM x) is finite.
- */
-bool isFinite(X)(X x) @trusted pure nothrow @nogc
-{
- alias F = floatTraits!(X);
- ushort* pe = cast(ushort *)&x;
- return (pe[F.EXPPOS_SHORT] & F.EXPMASK) != F.EXPMASK;
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert( isFinite(1.23f));
- assert( isFinite(float.max));
- assert( isFinite(float.min_normal));
- assert(!isFinite(float.nan));
- assert(!isFinite(float.infinity));
-}
-
-@safe pure nothrow @nogc unittest
-{
- assert(isFinite(1.23));
- assert(isFinite(double.max));
- assert(isFinite(double.min_normal));
- assert(!isFinite(double.nan));
- assert(!isFinite(double.infinity));
-
- assert(isFinite(1.23L));
- assert(isFinite(real.max));
- assert(isFinite(real.min_normal));
- assert(!isFinite(real.nan));
- assert(!isFinite(real.infinity));
-}
-
-
-/*********************************
- * Determines if $(D_PARAM x) is normalized.
- *
- * A normalized number must not be zero, subnormal, infinite nor $(NAN).
- *
- * Params:
- * x = a floating point number.
- * Returns:
- * $(D true) if $(D_PARAM x) is normalized.
- */
-
-/* Need one for each format because subnormal floats might
- * be converted to normal reals.
- */
-bool isNormal(X)(X x) @trusted pure nothrow @nogc
-{
- alias F = floatTraits!(X);
- static if (F.realFormat == RealFormat.ibmExtended)
- {
- // doubledouble is normal if the least significant part is normal.
- return isNormal((cast(double*)&x)[MANTISSA_LSB]);
- }
- else
- {
- ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
- return (e != F.EXPMASK && e != 0);
- }
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- float f = 3;
- double d = 500;
- real e = 10e+48;
-
- assert(isNormal(f));
- assert(isNormal(d));
- assert(isNormal(e));
- f = d = e = 0;
- assert(!isNormal(f));
- assert(!isNormal(d));
- assert(!isNormal(e));
- assert(!isNormal(real.infinity));
- assert(isNormal(-real.max));
- assert(!isNormal(real.min_normal/4));
-
-}
-
-/*********************************
- * Determines if $(D_PARAM x) is subnormal.
- *
- * Subnormals (also known as "denormal number"), have a 0 exponent
- * and a 0 most significant mantissa bit.
- *
- * Params:
- * x = a floating point number.
- * Returns:
- * $(D true) if $(D_PARAM x) is a denormal number.
- */
-bool isSubnormal(X)(X x) @trusted pure nothrow @nogc
-{
- /*
- Need one for each format because subnormal floats might
- be converted to normal reals.
- */
- alias F = floatTraits!(X);
- static if (F.realFormat == RealFormat.ieeeSingle)
- {
- uint *p = cast(uint *)&x;
- return (*p & F.EXPMASK_INT) == 0 && *p & F.MANTISSAMASK_INT;
- }
- else static if (F.realFormat == RealFormat.ieeeDouble)
- {
- uint *p = cast(uint *)&x;
- return (p[MANTISSA_MSB] & F.EXPMASK_INT) == 0
- && (p[MANTISSA_LSB] || p[MANTISSA_MSB] & F.MANTISSAMASK_INT);
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
- long* ps = cast(long *)&x;
- return (e == 0 &&
- ((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF)) != 0));
- }
- else static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- ushort* pe = cast(ushort *)&x;
- long* ps = cast(long *)&x;
-
- return (pe[F.EXPPOS_SHORT] & F.EXPMASK) == 0 && *ps > 0;
- }
- else static if (F.realFormat == RealFormat.ibmExtended)
- {
- return isSubnormal((cast(double*)&x)[MANTISSA_MSB]);
- }
- else
- {
- static assert(false, "Not implemented for this architecture");
- }
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- import std.meta : AliasSeq;
-
- foreach (T; AliasSeq!(float, double, real))
- {
- T f;
- for (f = 1.0; !isSubnormal(f); f /= 2)
- assert(f != 0);
- }
-}
-
-/*********************************
- * Determines if $(D_PARAM x) is $(PLUSMN)$(INFIN).
- * Params:
- * x = a floating point number.
- * Returns:
- * $(D true) if $(D_PARAM x) is $(PLUSMN)$(INFIN).
- */
-bool isInfinity(X)(X x) @nogc @trusted pure nothrow
-if (isFloatingPoint!(X))
-{
- alias F = floatTraits!(X);
- static if (F.realFormat == RealFormat.ieeeSingle)
- {
- return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000;
- }
- else static if (F.realFormat == RealFormat.ieeeDouble)
- {
- return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF)
- == 0x7FF0_0000_0000_0000;
- }
- else static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]);
- const ulong ps = *cast(ulong *)&x;
-
- // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1.
- return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0;
- }
- else static if (F.realFormat == RealFormat.ibmExtended)
- {
- return (((cast(ulong *)&x)[MANTISSA_MSB]) & 0x7FFF_FFFF_FFFF_FFFF)
- == 0x7FF8_0000_0000_0000;
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- const long psLsb = (cast(long *)&x)[MANTISSA_LSB];
- const long psMsb = (cast(long *)&x)[MANTISSA_MSB];
- return (psLsb == 0)
- && (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000;
- }
- else
- {
- return (x < -X.max) || (X.max < x);
- }
-}
-
-///
-@nogc @safe pure nothrow unittest
-{
- assert(!isInfinity(float.init));
- assert(!isInfinity(-float.init));
- assert(!isInfinity(float.nan));
- assert(!isInfinity(-float.nan));
- assert(isInfinity(float.infinity));
- assert(isInfinity(-float.infinity));
- assert(isInfinity(-1.0f / 0.0f));
-}
-
-@safe pure nothrow @nogc unittest
-{
- // CTFE-able tests
- assert(!isInfinity(double.init));
- assert(!isInfinity(-double.init));
- assert(!isInfinity(double.nan));
- assert(!isInfinity(-double.nan));
- assert(isInfinity(double.infinity));
- assert(isInfinity(-double.infinity));
- assert(isInfinity(-1.0 / 0.0));
-
- assert(!isInfinity(real.init));
- assert(!isInfinity(-real.init));
- assert(!isInfinity(real.nan));
- assert(!isInfinity(-real.nan));
- assert(isInfinity(real.infinity));
- assert(isInfinity(-real.infinity));
- assert(isInfinity(-1.0L / 0.0L));
-
- // Runtime tests
- shared float f;
- f = float.init;
- assert(!isInfinity(f));
- assert(!isInfinity(-f));
- f = float.nan;
- assert(!isInfinity(f));
- assert(!isInfinity(-f));
- f = float.infinity;
- assert(isInfinity(f));
- assert(isInfinity(-f));
- f = (-1.0f / 0.0f);
- assert(isInfinity(f));
-
- shared double d;
- d = double.init;
- assert(!isInfinity(d));
- assert(!isInfinity(-d));
- d = double.nan;
- assert(!isInfinity(d));
- assert(!isInfinity(-d));
- d = double.infinity;
- assert(isInfinity(d));
- assert(isInfinity(-d));
- d = (-1.0 / 0.0);
- assert(isInfinity(d));
-
- shared real e;
- e = real.init;
- assert(!isInfinity(e));
- assert(!isInfinity(-e));
- e = real.nan;
- assert(!isInfinity(e));
- assert(!isInfinity(-e));
- e = real.infinity;
- assert(isInfinity(e));
- assert(isInfinity(-e));
- e = (-1.0L / 0.0L);
- assert(isInfinity(e));
-}
-
-/*********************************
- * Is the binary representation of x identical to y?
- *
- * Same as ==, except that positive and negative zero are not identical,
- * and two $(NAN)s are identical if they have the same 'payload'.
- */
-bool isIdentical(real x, real y) @trusted pure nothrow @nogc
-{
- // We're doing a bitwise comparison so the endianness is irrelevant.
- long* pxs = cast(long *)&x;
- long* pys = cast(long *)&y;
- alias F = floatTraits!(real);
- static if (F.realFormat == RealFormat.ieeeDouble)
- {
- return pxs[0] == pys[0];
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple
- || F.realFormat == RealFormat.ibmExtended)
- {
- return pxs[0] == pys[0] && pxs[1] == pys[1];
- }
- else
- {
- ushort* pxe = cast(ushort *)&x;
- ushort* pye = cast(ushort *)&y;
- return pxe[4] == pye[4] && pxs[0] == pys[0];
- }
-}
-
-/*********************************
- * Return 1 if sign bit of e is set, 0 if not.
- */
-int signbit(X)(X x) @nogc @trusted pure nothrow
-{
- alias F = floatTraits!(X);
- return ((cast(ubyte *)&x)[F.SIGNPOS_BYTE] & 0x80) != 0;
-}
-
-///
-@nogc @safe pure nothrow unittest
-{
- assert(!signbit(float.nan));
- assert(signbit(-float.nan));
- assert(!signbit(168.1234f));
- assert(signbit(-168.1234f));
- assert(!signbit(0.0f));
- assert(signbit(-0.0f));
- assert(signbit(-float.max));
- assert(!signbit(float.max));
-
- assert(!signbit(double.nan));
- assert(signbit(-double.nan));
- assert(!signbit(168.1234));
- assert(signbit(-168.1234));
- assert(!signbit(0.0));
- assert(signbit(-0.0));
- assert(signbit(-double.max));
- assert(!signbit(double.max));
-
- assert(!signbit(real.nan));
- assert(signbit(-real.nan));
- assert(!signbit(168.1234L));
- assert(signbit(-168.1234L));
- assert(!signbit(0.0L));
- assert(signbit(-0.0L));
- assert(signbit(-real.max));
- assert(!signbit(real.max));
-}
-
-
-/*********************************
- * Return a value composed of to with from's sign bit.
- */
-R copysign(R, X)(R to, X from) @trusted pure nothrow @nogc
-if (isFloatingPoint!(R) && isFloatingPoint!(X))
-{
- ubyte* pto = cast(ubyte *)&to;
- const ubyte* pfrom = cast(ubyte *)&from;
-
- alias T = floatTraits!(R);
- alias F = floatTraits!(X);
- pto[T.SIGNPOS_BYTE] &= 0x7F;
- pto[T.SIGNPOS_BYTE] |= pfrom[F.SIGNPOS_BYTE] & 0x80;
- return to;
-}
-
-// ditto
-R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc
-if (isIntegral!(X) && isFloatingPoint!(R))
-{
- return copysign(cast(R) to, from);
-}
-
-@safe pure nothrow @nogc unittest
-{
- import std.meta : AliasSeq;
-
- foreach (X; AliasSeq!(float, double, real, int, long))
- {
- foreach (Y; AliasSeq!(float, double, real))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
- X x = 21;
- Y y = 23.8;
- Y e = void;
-
- e = copysign(x, y);
- assert(e == 21.0);
-
- e = copysign(-x, y);
- assert(e == 21.0);
-
- e = copysign(x, -y);
- assert(e == -21.0);
-
- e = copysign(-x, -y);
- assert(e == -21.0);
-
- static if (isFloatingPoint!X)
- {
- e = copysign(X.nan, y);
- assert(isNaN(e) && !signbit(e));
-
- e = copysign(X.nan, -y);
- assert(isNaN(e) && signbit(e));
- }
- }();
- }
-}
-
-/*********************************
-Returns $(D -1) if $(D x < 0), $(D x) if $(D x == 0), $(D 1) if
-$(D x > 0), and $(NAN) if x==$(NAN).
- */
-F sgn(F)(F x) @safe pure nothrow @nogc
-{
- // @@@TODO@@@: make this faster
- return x > 0 ? 1 : x < 0 ? -1 : x;
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert(sgn(168.1234) == 1);
- assert(sgn(-168.1234) == -1);
- assert(sgn(0.0) == 0);
- assert(sgn(-0.0) == 0);
-}
-
-// Functions for NaN payloads
-/*
- * A 'payload' can be stored in the significand of a $(NAN). One bit is required
- * to distinguish between a quiet and a signalling $(NAN). This leaves 22 bits
- * of payload for a float; 51 bits for a double; 62 bits for an 80-bit real;
- * and 111 bits for a 128-bit quad.
-*/
-/**
- * Create a quiet $(NAN), storing an integer inside the payload.
- *
- * For floats, the largest possible payload is 0x3F_FFFF.
- * For doubles, it is 0x3_FFFF_FFFF_FFFF.
- * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF.
- */
-real NaN(ulong payload) @trusted pure nothrow @nogc
-{
- alias F = floatTraits!(real);
- static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- // real80 (in x86 real format, the implied bit is actually
- // not implied but a real bit which is stored in the real)
- ulong v = 3; // implied bit = 1, quiet bit = 1
- }
- else
- {
- ulong v = 1; // no implied bit. quiet bit = 1
- }
-
- ulong a = payload;
-
- // 22 Float bits
- ulong w = a & 0x3F_FFFF;
- a -= w;
-
- v <<=22;
- v |= w;
- a >>=22;
-
- // 29 Double bits
- v <<=29;
- w = a & 0xFFF_FFFF;
- v |= w;
- a -= w;
- a >>=29;
-
- static if (F.realFormat == RealFormat.ieeeDouble)
- {
- v |= 0x7FF0_0000_0000_0000;
- real x;
- * cast(ulong *)(&x) = v;
- return x;
- }
- else
- {
- v <<=11;
- a &= 0x7FF;
- v |= a;
- real x = real.nan;
-
- // Extended real bits
- static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- v <<= 1; // there's no implicit bit
-
- version (LittleEndian)
- {
- *cast(ulong*)(6+cast(ubyte*)(&x)) = v;
- }
- else
- {
- *cast(ulong*)(2+cast(ubyte*)(&x)) = v;
- }
- }
- else
- {
- *cast(ulong *)(&x) = v;
- }
- return x;
- }
-}
-
-@system pure nothrow @nogc unittest // not @safe because taking address of local.
-{
- static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble)
- {
- auto x = NaN(1);
- auto xl = *cast(ulong*)&x;
- assert(xl & 0x8_0000_0000_0000UL); //non-signaling bit, bit 52
- assert((xl & 0x7FF0_0000_0000_0000UL) == 0x7FF0_0000_0000_0000UL); //all exp bits set
- }
-}
-
-/**
- * Extract an integral payload from a $(NAN).
- *
- * Returns:
- * the integer payload as a ulong.
- *
- * For floats, the largest possible payload is 0x3F_FFFF.
- * For doubles, it is 0x3_FFFF_FFFF_FFFF.
- * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF.
- */
-ulong getNaNPayload(real x) @trusted pure nothrow @nogc
-{
- // assert(isNaN(x));
- alias F = floatTraits!(real);
- static if (F.realFormat == RealFormat.ieeeDouble)
- {
- ulong m = *cast(ulong *)(&x);
- // Make it look like an 80-bit significand.
- // Skip exponent, and quiet bit
- m &= 0x0007_FFFF_FFFF_FFFF;
- m <<= 11;
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- version (LittleEndian)
- {
- ulong m = *cast(ulong*)(6+cast(ubyte*)(&x));
- }
- else
- {
- ulong m = *cast(ulong*)(2+cast(ubyte*)(&x));
- }
-
- m >>= 1; // there's no implicit bit
- }
- else
- {
- ulong m = *cast(ulong *)(&x);
- }
-
- // ignore implicit bit and quiet bit
-
- const ulong f = m & 0x3FFF_FF00_0000_0000L;
-
- ulong w = f >>> 40;
- w |= (m & 0x00FF_FFFF_F800L) << (22 - 11);
- w |= (m & 0x7FF) << 51;
- return w;
-}
-
-debug(UnitTest)
-{
- @safe pure nothrow @nogc unittest
- {
- real nan4 = NaN(0x789_ABCD_EF12_3456);
- static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended
- || floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
- {
- assert(getNaNPayload(nan4) == 0x789_ABCD_EF12_3456);
- }
- else
- {
- assert(getNaNPayload(nan4) == 0x1_ABCD_EF12_3456);
- }
- double nan5 = nan4;
- assert(getNaNPayload(nan5) == 0x1_ABCD_EF12_3456);
- float nan6 = nan4;
- assert(getNaNPayload(nan6) == 0x12_3456);
- nan4 = NaN(0xFABCD);
- assert(getNaNPayload(nan4) == 0xFABCD);
- nan6 = nan4;
- assert(getNaNPayload(nan6) == 0xFABCD);
- nan5 = NaN(0x100_0000_0000_3456);
- assert(getNaNPayload(nan5) == 0x0000_0000_3456);
- }
-}
-
-/**
- * Calculate the next largest floating point value after x.
- *
- * Return the least number greater than x that is representable as a real;
- * thus, it gives the next point on the IEEE number line.
- *
- * $(TABLE_SV
- * $(SVH x, nextUp(x) )
- * $(SV -$(INFIN), -real.max )
- * $(SV $(PLUSMN)0.0, real.min_normal*real.epsilon )
- * $(SV real.max, $(INFIN) )
- * $(SV $(INFIN), $(INFIN) )
- * $(SV $(NAN), $(NAN) )
- * )
- */
-real nextUp(real x) @trusted pure nothrow @nogc
-{
- alias F = floatTraits!(real);
- static if (F.realFormat == RealFormat.ieeeDouble)
- {
- return nextUp(cast(double) x);
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
- if (e == F.EXPMASK)
- {
- // NaN or Infinity
- if (x == -real.infinity) return -real.max;
- return x; // +Inf and NaN are unchanged.
- }
-
- auto ps = cast(ulong *)&x;
- if (ps[MANTISSA_MSB] & 0x8000_0000_0000_0000)
- {
- // Negative number
- if (ps[MANTISSA_LSB] == 0 && ps[MANTISSA_MSB] == 0x8000_0000_0000_0000)
- {
- // it was negative zero, change to smallest subnormal
- ps[MANTISSA_LSB] = 1;
- ps[MANTISSA_MSB] = 0;
- return x;
- }
- if (ps[MANTISSA_LSB] == 0) --ps[MANTISSA_MSB];
- --ps[MANTISSA_LSB];
- }
- else
- {
- // Positive number
- ++ps[MANTISSA_LSB];
- if (ps[MANTISSA_LSB] == 0) ++ps[MANTISSA_MSB];
- }
- return x;
- }
- else static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- // For 80-bit reals, the "implied bit" is a nuisance...
- ushort *pe = cast(ushort *)&x;
- ulong *ps = cast(ulong *)&x;
- // EPSILON is 1 for 64-bit, and 2048 for 53-bit precision reals.
- enum ulong EPSILON = 2UL ^^ (64 - real.mant_dig);
-
- if ((pe[F.EXPPOS_SHORT] & F.EXPMASK) == F.EXPMASK)
- {
- // First, deal with NANs and infinity
- if (x == -real.infinity) return -real.max;
- return x; // +Inf and NaN are unchanged.
- }
- if (pe[F.EXPPOS_SHORT] & 0x8000)
- {
- // Negative number -- need to decrease the significand
- *ps -= EPSILON;
- // Need to mask with 0x7FFF... so subnormals are treated correctly.
- if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_FFFF_FFFF_FFFF)
- {
- if (pe[F.EXPPOS_SHORT] == 0x8000) // it was negative zero
- {
- *ps = 1;
- pe[F.EXPPOS_SHORT] = 0; // smallest subnormal.
- return x;
- }
-
- --pe[F.EXPPOS_SHORT];
-
- if (pe[F.EXPPOS_SHORT] == 0x8000)
- return x; // it's become a subnormal, implied bit stays low.
-
- *ps = 0xFFFF_FFFF_FFFF_FFFF; // set the implied bit
- return x;
- }
- return x;
- }
- else
- {
- // Positive number -- need to increase the significand.
- // Works automatically for positive zero.
- *ps += EPSILON;
- if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0)
- {
- // change in exponent
- ++pe[F.EXPPOS_SHORT];
- *ps = 0x8000_0000_0000_0000; // set the high bit
- }
- }
- return x;
- }
- else // static if (F.realFormat == RealFormat.ibmExtended)
- {
- assert(0, "nextUp not implemented");
- }
-}
-
-/** ditto */
-double nextUp(double x) @trusted pure nothrow @nogc
-{
- ulong *ps = cast(ulong *)&x;
-
- if ((*ps & 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000)
- {
- // First, deal with NANs and infinity
- if (x == -x.infinity) return -x.max;
- return x; // +INF and NAN are unchanged.
- }
- if (*ps & 0x8000_0000_0000_0000) // Negative number
- {
- if (*ps == 0x8000_0000_0000_0000) // it was negative zero
- {
- *ps = 0x0000_0000_0000_0001; // change to smallest subnormal
- return x;
- }
- --*ps;
- }
- else
- { // Positive number
- ++*ps;
- }
- return x;
-}
-
-/** ditto */
-float nextUp(float x) @trusted pure nothrow @nogc
-{
- uint *ps = cast(uint *)&x;
-
- if ((*ps & 0x7F80_0000) == 0x7F80_0000)
- {
- // First, deal with NANs and infinity
- if (x == -x.infinity) return -x.max;
-
- return x; // +INF and NAN are unchanged.
- }
- if (*ps & 0x8000_0000) // Negative number
- {
- if (*ps == 0x8000_0000) // it was negative zero
- {
- *ps = 0x0000_0001; // change to smallest subnormal
- return x;
- }
-
- --*ps;
- }
- else
- {
- // Positive number
- ++*ps;
- }
- return x;
-}
-
-/**
- * Calculate the next smallest floating point value before x.
- *
- * Return the greatest number less than x that is representable as a real;
- * thus, it gives the previous point on the IEEE number line.
- *
- * $(TABLE_SV
- * $(SVH x, nextDown(x) )
- * $(SV $(INFIN), real.max )
- * $(SV $(PLUSMN)0.0, -real.min_normal*real.epsilon )
- * $(SV -real.max, -$(INFIN) )
- * $(SV -$(INFIN), -$(INFIN) )
- * $(SV $(NAN), $(NAN) )
- * )
- */
-real nextDown(real x) @safe pure nothrow @nogc
-{
- return -nextUp(-x);
-}
-
-/** ditto */
-double nextDown(double x) @safe pure nothrow @nogc
-{
- return -nextUp(-x);
-}
-
-/** ditto */
-float nextDown(float x) @safe pure nothrow @nogc
-{
- return -nextUp(-x);
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- assert( nextDown(1.0 + real.epsilon) == 1.0);
-}
-
-@safe pure nothrow @nogc unittest
-{
- static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
- {
-
- // Tests for 80-bit reals
- assert(isIdentical(nextUp(NaN(0xABC)), NaN(0xABC)));
- // negative numbers
- assert( nextUp(-real.infinity) == -real.max );
- assert( nextUp(-1.0L-real.epsilon) == -1.0 );
- assert( nextUp(-2.0L) == -2.0 + real.epsilon);
- // subnormals and zero
- assert( nextUp(-real.min_normal) == -real.min_normal*(1-real.epsilon) );
- assert( nextUp(-real.min_normal*(1-real.epsilon)) == -real.min_normal*(1-2*real.epsilon) );
- assert( isIdentical(-0.0L, nextUp(-real.min_normal*real.epsilon)) );
- assert( nextUp(-0.0L) == real.min_normal*real.epsilon );
- assert( nextUp(0.0L) == real.min_normal*real.epsilon );
- assert( nextUp(real.min_normal*(1-real.epsilon)) == real.min_normal );
- assert( nextUp(real.min_normal) == real.min_normal*(1+real.epsilon) );
- // positive numbers
- assert( nextUp(1.0L) == 1.0 + real.epsilon );
- assert( nextUp(2.0L-real.epsilon) == 2.0 );
- assert( nextUp(real.max) == real.infinity );
- assert( nextUp(real.infinity)==real.infinity );
- }
-
- double n = NaN(0xABC);
- assert(isIdentical(nextUp(n), n));
- // negative numbers
- assert( nextUp(-double.infinity) == -double.max );
- assert( nextUp(-1-double.epsilon) == -1.0 );
- assert( nextUp(-2.0) == -2.0 + double.epsilon);
- // subnormals and zero
-
- assert( nextUp(-double.min_normal) == -double.min_normal*(1-double.epsilon) );
- assert( nextUp(-double.min_normal*(1-double.epsilon)) == -double.min_normal*(1-2*double.epsilon) );
- assert( isIdentical(-0.0, nextUp(-double.min_normal*double.epsilon)) );
- assert( nextUp(0.0) == double.min_normal*double.epsilon );
- assert( nextUp(-0.0) == double.min_normal*double.epsilon );
- assert( nextUp(double.min_normal*(1-double.epsilon)) == double.min_normal );
- assert( nextUp(double.min_normal) == double.min_normal*(1+double.epsilon) );
- // positive numbers
- assert( nextUp(1.0) == 1.0 + double.epsilon );
- assert( nextUp(2.0-double.epsilon) == 2.0 );
- assert( nextUp(double.max) == double.infinity );
-
- float fn = NaN(0xABC);
- assert(isIdentical(nextUp(fn), fn));
- float f = -float.min_normal*(1-float.epsilon);
- float f1 = -float.min_normal;
- assert( nextUp(f1) == f);
- f = 1.0f+float.epsilon;
- f1 = 1.0f;
- assert( nextUp(f1) == f );
- f1 = -0.0f;
- assert( nextUp(f1) == float.min_normal*float.epsilon);
- assert( nextUp(float.infinity)==float.infinity );
-
- assert(nextDown(1.0L+real.epsilon)==1.0);
- assert(nextDown(1.0+double.epsilon)==1.0);
- f = 1.0f+float.epsilon;
- assert(nextDown(f)==1.0);
- assert(nextafter(1.0+real.epsilon, -real.infinity)==1.0);
-}
-
-
-
-/******************************************
- * Calculates the next representable value after x in the direction of y.
- *
- * If y > x, the result will be the next largest floating-point value;
- * if y < x, the result will be the next smallest value.
- * If x == y, the result is y.
- *
- * Remarks:
- * This function is not generally very useful; it's almost always better to use
- * the faster functions nextUp() or nextDown() instead.
- *
- * The FE_INEXACT and FE_OVERFLOW exceptions will be raised if x is finite and
- * the function result is infinite. The FE_INEXACT and FE_UNDERFLOW
- * exceptions will be raised if the function value is subnormal, and x is
- * not equal to y.
- */
-T nextafter(T)(const T x, const T y) @safe pure nothrow @nogc
-{
- if (x == y) return y;
- return ((y>x) ? nextUp(x) : nextDown(x));
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- float a = 1;
- assert(is(typeof(nextafter(a, a)) == float));
- assert(nextafter(a, a.infinity) > a);
-
- double b = 2;
- assert(is(typeof(nextafter(b, b)) == double));
- assert(nextafter(b, b.infinity) > b);
-
- real c = 3;
- assert(is(typeof(nextafter(c, c)) == real));
- assert(nextafter(c, c.infinity) > c);
-}
-
-//real nexttoward(real x, real y) { return core.stdc.math.nexttowardl(x, y); }
-
-/*******************************************
- * Returns the positive difference between x and y.
- * Returns:
- * $(TABLE_SV
- * $(TR $(TH x, y) $(TH fdim(x, y)))
- * $(TR $(TD x $(GT) y) $(TD x - y))
- * $(TR $(TD x $(LT)= y) $(TD +0.0))
- * )
- */
-real fdim(real x, real y) @safe pure nothrow @nogc { return (x > y) ? x - y : +0.0; }
-
-/****************************************
- * Returns the larger of x and y.
- */
-real fmax(real x, real y) @safe pure nothrow @nogc { return x > y ? x : y; }
-
-/****************************************
- * Returns the smaller of x and y.
- */
-real fmin(real x, real y) @safe pure nothrow @nogc { return x < y ? x : y; }
-
-/**************************************
- * Returns (x * y) + z, rounding only once according to the
- * current rounding mode.
- *
- * BUGS: Not currently implemented - rounds twice.
- */
-real fma(real x, real y, real z) @safe pure nothrow @nogc { return (x * y) + z; }
-
-/*******************************************************************
- * Compute the value of x $(SUPERSCRIPT n), where n is an integer
- */
-Unqual!F pow(F, G)(F x, G n) @nogc @trusted pure nothrow
-if (isFloatingPoint!(F) && isIntegral!(G))
-{
- import std.traits : Unsigned;
- real p = 1.0, v = void;
- Unsigned!(Unqual!G) m = n;
- if (n < 0)
- {
- switch (n)
- {
- case -1:
- return 1 / x;
- case -2:
- return 1 / (x * x);
- default:
- }
-
- m = cast(typeof(m))(0 - n);
- v = p / x;
- }
- else
- {
- switch (n)
- {
- case 0:
- return 1.0;
- case 1:
- return x;
- case 2:
- return x * x;
- default:
- }
-
- v = x;
- }
-
- while (1)
- {
- if (m & 1)
- p *= v;
- m >>= 1;
- if (!m)
- break;
- v *= v;
- }
- return p;
-}
-
-@safe pure nothrow @nogc unittest
-{
- // Make sure it instantiates and works properly on immutable values and
- // with various integer and float types.
- immutable real x = 46;
- immutable float xf = x;
- immutable double xd = x;
- immutable uint one = 1;
- immutable ushort two = 2;
- immutable ubyte three = 3;
- immutable ulong eight = 8;
-
- immutable int neg1 = -1;
- immutable short neg2 = -2;
- immutable byte neg3 = -3;
- immutable long neg8 = -8;
-
-
- assert(pow(x,0) == 1.0);
- assert(pow(xd,one) == x);
- assert(pow(xf,two) == x * x);
- assert(pow(x,three) == x * x * x);
- assert(pow(x,eight) == (x * x) * (x * x) * (x * x) * (x * x));
-
- assert(pow(x, neg1) == 1 / x);
-
- // Test disabled on most targets.
- // See https://issues.dlang.org/show_bug.cgi?id=5628
- version (X86_64) enum BUG5628 = false;
- else version (ARM) enum BUG5628 = false;
- else version (GNU) enum BUG5628 = false;
- else enum BUG5628 = true;
-
- static if (BUG5628)
- {
- assert(pow(xd, neg2) == 1 / (x * x));
- assert(pow(xf, neg8) == 1 / ((x * x) * (x * x) * (x * x) * (x * x)));
- }
-
- assert(feqrel(pow(x, neg3), 1 / (x * x * x)) >= real.mant_dig - 1);
-}
-
-@system unittest
-{
- assert(equalsDigit(pow(2.0L, 10.0L), 1024, 19));
-}
-
-/** Compute the value of an integer x, raised to the power of a positive
- * integer n.
- *
- * If both x and n are 0, the result is 1.
- * If n is negative, an integer divide error will occur at runtime,
- * regardless of the value of x.
- */
-typeof(Unqual!(F).init * Unqual!(G).init) pow(F, G)(F x, G n) @nogc @trusted pure nothrow
-if (isIntegral!(F) && isIntegral!(G))
-{
- if (n<0) return x/0; // Only support positive powers
- typeof(return) p, v = void;
- Unqual!G m = n;
-
- switch (m)
- {
- case 0:
- p = 1;
- break;
-
- case 1:
- p = x;
- break;
-
- case 2:
- p = x * x;
- break;
-
- default:
- v = x;
- p = 1;
- while (1)
- {
- if (m & 1)
- p *= v;
- m >>= 1;
- if (!m)
- break;
- v *= v;
- }
- break;
- }
- return p;
-}
-
-///
-@safe pure nothrow @nogc unittest
-{
- immutable int one = 1;
- immutable byte two = 2;
- immutable ubyte three = 3;
- immutable short four = 4;
- immutable long ten = 10;
-
- assert(pow(two, three) == 8);
- assert(pow(two, ten) == 1024);
- assert(pow(one, ten) == 1);
- assert(pow(ten, four) == 10_000);
- assert(pow(four, 10) == 1_048_576);
- assert(pow(three, four) == 81);
-
-}
-
-/**Computes integer to floating point powers.*/
-real pow(I, F)(I x, F y) @nogc @trusted pure nothrow
-if (isIntegral!I && isFloatingPoint!F)
-{
- return pow(cast(real) x, cast(Unqual!F) y);
-}
-
-/*********************************************
- * Calculates x$(SUPERSCRIPT y).
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH y) $(TH pow(x, y))
- * $(TH div 0) $(TH invalid?))
- * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD 1.0)
- * $(TD no) $(TD no) )
- * $(TR $(TD |x| $(GT) 1) $(TD +$(INFIN)) $(TD +$(INFIN))
- * $(TD no) $(TD no) )
- * $(TR $(TD |x| $(LT) 1) $(TD +$(INFIN)) $(TD +0.0)
- * $(TD no) $(TD no) )
- * $(TR $(TD |x| $(GT) 1) $(TD -$(INFIN)) $(TD +0.0)
- * $(TD no) $(TD no) )
- * $(TR $(TD |x| $(LT) 1) $(TD -$(INFIN)) $(TD +$(INFIN))
- * $(TD no) $(TD no) )
- * $(TR $(TD +$(INFIN)) $(TD $(GT) 0.0) $(TD +$(INFIN))
- * $(TD no) $(TD no) )
- * $(TR $(TD +$(INFIN)) $(TD $(LT) 0.0) $(TD +0.0)
- * $(TD no) $(TD no) )
- * $(TR $(TD -$(INFIN)) $(TD odd integer $(GT) 0.0) $(TD -$(INFIN))
- * $(TD no) $(TD no) )
- * $(TR $(TD -$(INFIN)) $(TD $(GT) 0.0, not odd integer) $(TD +$(INFIN))
- * $(TD no) $(TD no))
- * $(TR $(TD -$(INFIN)) $(TD odd integer $(LT) 0.0) $(TD -0.0)
- * $(TD no) $(TD no) )
- * $(TR $(TD -$(INFIN)) $(TD $(LT) 0.0, not odd integer) $(TD +0.0)
- * $(TD no) $(TD no) )
- * $(TR $(TD $(PLUSMN)1.0) $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN))
- * $(TD no) $(TD yes) )
- * $(TR $(TD $(LT) 0.0) $(TD finite, nonintegral) $(TD $(NAN))
- * $(TD no) $(TD yes))
- * $(TR $(TD $(PLUSMN)0.0) $(TD odd integer $(LT) 0.0) $(TD $(PLUSMNINF))
- * $(TD yes) $(TD no) )
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT) 0.0, not odd integer) $(TD +$(INFIN))
- * $(TD yes) $(TD no))
- * $(TR $(TD $(PLUSMN)0.0) $(TD odd integer $(GT) 0.0) $(TD $(PLUSMN)0.0)
- * $(TD no) $(TD no) )
- * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT) 0.0, not odd integer) $(TD +0.0)
- * $(TD no) $(TD no) )
- * )
- */
-Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow
-if (isFloatingPoint!(F) && isFloatingPoint!(G))
-{
- alias Float = typeof(return);
-
- static real impl(real x, real y) @nogc pure nothrow
- {
- // Special cases.
- if (isNaN(y))
- return y;
- if (isNaN(x) && y != 0.0)
- return x;
-
- // Even if x is NaN.
- if (y == 0.0)
- return 1.0;
- if (y == 1.0)
- return x;
-
- if (isInfinity(y))
- {
- if (fabs(x) > 1)
- {
- if (signbit(y))
- return +0.0;
- else
- return F.infinity;
- }
- else if (fabs(x) == 1)
- {
- return y * 0; // generate NaN.
- }
- else // < 1
- {
- if (signbit(y))
- return F.infinity;
- else
- return +0.0;
- }
- }
- if (isInfinity(x))
- {
- if (signbit(x))
- {
- long i = cast(long) y;
- if (y > 0.0)
- {
- if (i == y && i & 1)
- return -F.infinity;
- else
- return F.infinity;
- }
- else if (y < 0.0)
- {
- if (i == y && i & 1)
- return -0.0;
- else
- return +0.0;
- }
- }
- else
- {
- if (y > 0.0)
- return F.infinity;
- else if (y < 0.0)
- return +0.0;
- }
- }
-
- if (x == 0.0)
- {
- if (signbit(x))
- {
- long i = cast(long) y;
- if (y > 0.0)
- {
- if (i == y && i & 1)
- return -0.0;
- else
- return +0.0;
- }
- else if (y < 0.0)
- {
- if (i == y && i & 1)
- return -F.infinity;
- else
- return F.infinity;
- }
- }
- else
- {
- if (y > 0.0)
- return +0.0;
- else if (y < 0.0)
- return F.infinity;
- }
- }
- if (x == 1.0)
- return 1.0;
-
- if (y >= F.max)
- {
- if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
- return 0.0;
- if (x > 1.0 || x < -1.0)
- return F.infinity;
- }
- if (y <= -F.max)
- {
- if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
- return F.infinity;
- if (x > 1.0 || x < -1.0)
- return 0.0;
- }
-
- if (x >= F.max)
- {
- if (y > 0.0)
- return F.infinity;
- else
- return 0.0;
- }
- if (x <= -F.max)
- {
- long i = cast(long) y;
- if (y > 0.0)
- {
- if (i == y && i & 1)
- return -F.infinity;
- else
- return F.infinity;
- }
- else if (y < 0.0)
- {
- if (i == y && i & 1)
- return -0.0;
- else
- return +0.0;
- }
- }
-
- // Integer power of x.
- long iy = cast(long) y;
- if (iy == y && fabs(y) < 32_768.0)
- return pow(x, iy);
-
- real sign = 1.0;
- if (x < 0)
- {
- // Result is real only if y is an integer
- // Check for a non-zero fractional part
- enum maxOdd = pow(2.0L, real.mant_dig) - 1.0L;
- static if (maxOdd > ulong.max)
- {
- // Generic method, for any FP type
- if (floor(y) != y)
- return sqrt(x); // Complex result -- create a NaN
-
- const hy = ldexp(y, -1);
- if (floor(hy) != hy)
- sign = -1.0;
- }
- else
- {
- // Much faster, if ulong has enough precision
- const absY = fabs(y);
- if (absY <= maxOdd)
- {
- const uy = cast(ulong) absY;
- if (uy != absY)
- return sqrt(x); // Complex result -- create a NaN
-
- if (uy & 1)
- sign = -1.0;
- }
- }
- x = -x;
- }
- version (INLINE_YL2X)
- {
- // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
- // TODO: This is not accurate in practice. A fast and accurate
- // (though complicated) method is described in:
- // "An efficient rounding boundary test for pow(x, y)
- // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007).
- return sign * exp2( core.math.yl2x(x, y) );
- }
- else
- {
- // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
- // TODO: This is not accurate in practice. A fast and accurate
- // (though complicated) method is described in:
- // "An efficient rounding boundary test for pow(x, y)
- // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007).
- Float w = exp2(y * log2(x));
- return sign * w;
- }
- }
- return impl(x, y);
-}
-
-@safe pure nothrow @nogc unittest
-{
- // Test all the special values. These unittests can be run on Windows
- // by temporarily changing the version (linux) to version (all).
- immutable float zero = 0;
- immutable real one = 1;
- immutable double two = 2;
- immutable float three = 3;
- immutable float fnan = float.nan;
- immutable double dnan = double.nan;
- immutable real rnan = real.nan;
- immutable dinf = double.infinity;
- immutable rninf = -real.infinity;
-
- assert(pow(fnan, zero) == 1);
- assert(pow(dnan, zero) == 1);
- assert(pow(rnan, zero) == 1);
-
- assert(pow(two, dinf) == double.infinity);
- assert(isIdentical(pow(0.2f, dinf), +0.0));
- assert(pow(0.99999999L, rninf) == real.infinity);
- assert(isIdentical(pow(1.000000001, rninf), +0.0));
- assert(pow(dinf, 0.001) == dinf);
- assert(isIdentical(pow(dinf, -0.001), +0.0));
- assert(pow(rninf, 3.0L) == rninf);
- assert(pow(rninf, 2.0L) == real.infinity);
- assert(isIdentical(pow(rninf, -3.0), -0.0));
- assert(isIdentical(pow(rninf, -2.0), +0.0));
-
- // @@@BUG@@@ somewhere
- version (OSX) {} else assert(isNaN(pow(one, dinf)));
- version (OSX) {} else assert(isNaN(pow(-one, dinf)));
- assert(isNaN(pow(-0.2, PI)));
- // boundary cases. Note that epsilon == 2^^-n for some n,
- // so 1/epsilon == 2^^n is always even.
- assert(pow(-1.0L, 1/real.epsilon - 1.0L) == -1.0L);
- assert(pow(-1.0L, 1/real.epsilon) == 1.0L);
- assert(isNaN(pow(-1.0L, 1/real.epsilon-0.5L)));
- assert(isNaN(pow(-1.0L, -1/real.epsilon+0.5L)));
-
- assert(pow(0.0, -3.0) == double.infinity);
- assert(pow(-0.0, -3.0) == -double.infinity);
- assert(pow(0.0, -PI) == double.infinity);
- assert(pow(-0.0, -PI) == double.infinity);
- assert(isIdentical(pow(0.0, 5.0), 0.0));
- assert(isIdentical(pow(-0.0, 5.0), -0.0));
- assert(isIdentical(pow(0.0, 6.0), 0.0));
- assert(isIdentical(pow(-0.0, 6.0), 0.0));
-
- // Issue #14786 fixed
- immutable real maxOdd = pow(2.0L, real.mant_dig) - 1.0L;
- assert(pow(-1.0L, maxOdd) == -1.0L);
- assert(pow(-1.0L, -maxOdd) == -1.0L);
- assert(pow(-1.0L, maxOdd + 1.0L) == 1.0L);
- assert(pow(-1.0L, -maxOdd + 1.0L) == 1.0L);
- assert(pow(-1.0L, maxOdd - 1.0L) == 1.0L);
- assert(pow(-1.0L, -maxOdd - 1.0L) == 1.0L);
-
- // Now, actual numbers.
- assert(approxEqual(pow(two, three), 8.0));
- assert(approxEqual(pow(two, -2.5), 0.1767767));
-
- // Test integer to float power.
- immutable uint twoI = 2;
- assert(approxEqual(pow(twoI, three), 8.0));
-}
-
-/**************************************
- * To what precision is x equal to y?
- *
- * Returns: the number of mantissa bits which are equal in x and y.
- * eg, 0x1.F8p+60 and 0x1.F1p+60 are equal to 5 bits of precision.
- *
- * $(TABLE_SV
- * $(TR $(TH x) $(TH y) $(TH feqrel(x, y)))
- * $(TR $(TD x) $(TD x) $(TD real.mant_dig))
- * $(TR $(TD x) $(TD $(GT)= 2*x) $(TD 0))
- * $(TR $(TD x) $(TD $(LT)= x/2) $(TD 0))
- * $(TR $(TD $(NAN)) $(TD any) $(TD 0))
- * $(TR $(TD any) $(TD $(NAN)) $(TD 0))
- * )
- */
-int feqrel(X)(const X x, const X y) @trusted pure nothrow @nogc
-if (isFloatingPoint!(X))
-{
- /* Public Domain. Author: Don Clugston, 18 Aug 2005.
- */
- alias F = floatTraits!(X);
- static if (F.realFormat == RealFormat.ibmExtended)
- {
- if (cast(double*)(&x)[MANTISSA_MSB] == cast(double*)(&y)[MANTISSA_MSB])
- {
- return double.mant_dig
- + feqrel(cast(double*)(&x)[MANTISSA_LSB],
- cast(double*)(&y)[MANTISSA_LSB]);
- }
- else
- {
- return feqrel(cast(double*)(&x)[MANTISSA_MSB],
- cast(double*)(&y)[MANTISSA_MSB]);
- }
- }
- else
- {
- static assert(F.realFormat == RealFormat.ieeeSingle
- || F.realFormat == RealFormat.ieeeDouble
- || F.realFormat == RealFormat.ieeeExtended
- || F.realFormat == RealFormat.ieeeExtended53
- || F.realFormat == RealFormat.ieeeQuadruple);
-
- if (x == y)
- return X.mant_dig; // ensure diff != 0, cope with INF.
-
- Unqual!X diff = fabs(x - y);
-
- ushort *pa = cast(ushort *)(&x);
- ushort *pb = cast(ushort *)(&y);
- ushort *pd = cast(ushort *)(&diff);
-
-
- // The difference in abs(exponent) between x or y and abs(x-y)
- // is equal to the number of significand bits of x which are
- // equal to y. If negative, x and y have different exponents.
- // If positive, x and y are equal to 'bitsdiff' bits.
- // AND with 0x7FFF to form the absolute value.
- // To avoid out-by-1 errors, we subtract 1 so it rounds down
- // if the exponents were different. This means 'bitsdiff' is
- // always 1 lower than we want, except that if bitsdiff == 0,
- // they could have 0 or 1 bits in common.
-
- int bitsdiff = ((( (pa[F.EXPPOS_SHORT] & F.EXPMASK)
- + (pb[F.EXPPOS_SHORT] & F.EXPMASK)
- - (1 << F.EXPSHIFT)) >> 1)
- - (pd[F.EXPPOS_SHORT] & F.EXPMASK)) >> F.EXPSHIFT;
- if ( (pd[F.EXPPOS_SHORT] & F.EXPMASK) == 0)
- { // Difference is subnormal
- // For subnormals, we need to add the number of zeros that
- // lie at the start of diff's significand.
- // We do this by multiplying by 2^^real.mant_dig
- diff *= F.RECIP_EPSILON;
- return bitsdiff + X.mant_dig - ((pd[F.EXPPOS_SHORT] & F.EXPMASK) >> F.EXPSHIFT);
- }
-
- if (bitsdiff > 0)
- return bitsdiff + 1; // add the 1 we subtracted before
-
- // Avoid out-by-1 errors when factor is almost 2.
- if (bitsdiff == 0
- && ((pa[F.EXPPOS_SHORT] ^ pb[F.EXPPOS_SHORT]) & F.EXPMASK) == 0)
- {
- return 1;
- } else return 0;
- }
-}
-
-@safe pure nothrow @nogc unittest
-{
- void testFeqrel(F)()
- {
- // Exact equality
- assert(feqrel(F.max, F.max) == F.mant_dig);
- assert(feqrel!(F)(0.0, 0.0) == F.mant_dig);
- assert(feqrel(F.infinity, F.infinity) == F.mant_dig);
-
- // a few bits away from exact equality
- F w=1;
- for (int i = 1; i < F.mant_dig - 1; ++i)
- {
- assert(feqrel!(F)(1.0 + w * F.epsilon, 1.0) == F.mant_dig-i);
- assert(feqrel!(F)(1.0 - w * F.epsilon, 1.0) == F.mant_dig-i);
- assert(feqrel!(F)(1.0, 1 + (w-1) * F.epsilon) == F.mant_dig - i + 1);
- w*=2;
- }
-
- assert(feqrel!(F)(1.5+F.epsilon, 1.5) == F.mant_dig-1);
- assert(feqrel!(F)(1.5-F.epsilon, 1.5) == F.mant_dig-1);
- assert(feqrel!(F)(1.5-F.epsilon, 1.5+F.epsilon) == F.mant_dig-2);
-
-
- // Numbers that are close
- assert(feqrel!(F)(0x1.Bp+84, 0x1.B8p+84) == 5);
- assert(feqrel!(F)(0x1.8p+10, 0x1.Cp+10) == 2);
- assert(feqrel!(F)(1.5 * (1 - F.epsilon), 1.0L) == 2);
- assert(feqrel!(F)(1.5, 1.0) == 1);
- assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1);
-
- // Factors of 2
- assert(feqrel(F.max, F.infinity) == 0);
- assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1);
- assert(feqrel!(F)(1.0, 2.0) == 0);
- assert(feqrel!(F)(4.0, 1.0) == 0);
-
- // Extreme inequality
- assert(feqrel(F.nan, F.nan) == 0);
- assert(feqrel!(F)(0.0L, -F.nan) == 0);
- assert(feqrel(F.nan, F.infinity) == 0);
- assert(feqrel(F.infinity, -F.infinity) == 0);
- assert(feqrel(F.max, -F.max) == 0);
-
- assert(feqrel(F.min_normal / 8, F.min_normal / 17) == 3);
-
- const F Const = 2;
- immutable F Immutable = 2;
- auto Compiles = feqrel(Const, Immutable);
- }
-
- assert(feqrel(7.1824L, 7.1824L) == real.mant_dig);
-
- testFeqrel!(real)();
- testFeqrel!(double)();
- testFeqrel!(float)();
-}
-
-package: // Not public yet
-/* Return the value that lies halfway between x and y on the IEEE number line.
- *
- * Formally, the result is the arithmetic mean of the binary significands of x
- * and y, multiplied by the geometric mean of the binary exponents of x and y.
- * x and y must have the same sign, and must not be NaN.
- * Note: this function is useful for ensuring O(log n) behaviour in algorithms
- * involving a 'binary chop'.
- *
- * Special cases:
- * If x and y are within a factor of 2, (ie, feqrel(x, y) > 0), the return value
- * is the arithmetic mean (x + y) / 2.
- * If x and y are even powers of 2, the return value is the geometric mean,
- * ieeeMean(x, y) = sqrt(x * y).
- *
- */
-T ieeeMean(T)(const T x, const T y) @trusted pure nothrow @nogc
-in
-{
- // both x and y must have the same sign, and must not be NaN.
- assert(signbit(x) == signbit(y));
- assert(x == x && y == y);
-}
-body
-{
- // Runtime behaviour for contract violation:
- // If signs are opposite, or one is a NaN, return 0.
- if (!((x >= 0 && y >= 0) || (x <= 0 && y <= 0))) return 0.0;
-
- // The implementation is simple: cast x and y to integers,
- // average them (avoiding overflow), and cast the result back to a floating-point number.
-
- alias F = floatTraits!(T);
- T u;
- static if (F.realFormat == RealFormat.ieeeExtended ||
- F.realFormat == RealFormat.ieeeExtended53)
- {
- // There's slight additional complexity because they are actually
- // 79-bit reals...
- ushort *ue = cast(ushort *)&u;
- ulong *ul = cast(ulong *)&u;
- ushort *xe = cast(ushort *)&x;
- ulong *xl = cast(ulong *)&x;
- ushort *ye = cast(ushort *)&y;
- ulong *yl = cast(ulong *)&y;
-
- // Ignore the useless implicit bit. (Bonus: this prevents overflows)
- ulong m = ((*xl) & 0x7FFF_FFFF_FFFF_FFFFL) + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL);
-
- // @@@ BUG? @@@
- // Cast shouldn't be here
- ushort e = cast(ushort) ((xe[F.EXPPOS_SHORT] & F.EXPMASK)
- + (ye[F.EXPPOS_SHORT] & F.EXPMASK));
- if (m & 0x8000_0000_0000_0000L)
- {
- ++e;
- m &= 0x7FFF_FFFF_FFFF_FFFFL;
- }
- // Now do a multi-byte right shift
- const uint c = e & 1; // carry
- e >>= 1;
- m >>>= 1;
- if (c)
- m |= 0x4000_0000_0000_0000L; // shift carry into significand
- if (e)
- *ul = m | 0x8000_0000_0000_0000L; // set implicit bit...
- else
- *ul = m; // ... unless exponent is 0 (subnormal or zero).
-
- ue[4]= e | (xe[F.EXPPOS_SHORT]& 0x8000); // restore sign bit
- }
- else static if (F.realFormat == RealFormat.ieeeQuadruple)
- {
- // This would be trivial if 'ucent' were implemented...
- ulong *ul = cast(ulong *)&u;
- ulong *xl = cast(ulong *)&x;
- ulong *yl = cast(ulong *)&y;
-
- // Multi-byte add, then multi-byte right shift.
- import core.checkedint : addu;
- bool carry;
- ulong ml = addu(xl[MANTISSA_LSB], yl[MANTISSA_LSB], carry);
-
- ulong mh = carry + (xl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL) +
- (yl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL);
-
- ul[MANTISSA_MSB] = (mh >>> 1) | (xl[MANTISSA_MSB] & 0x8000_0000_0000_0000);
- ul[MANTISSA_LSB] = (ml >>> 1) | (mh & 1) << 63;
- }
- else static if (F.realFormat == RealFormat.ieeeDouble)
- {
- ulong *ul = cast(ulong *)&u;
- ulong *xl = cast(ulong *)&x;
- ulong *yl = cast(ulong *)&y;
- ulong m = (((*xl) & 0x7FFF_FFFF_FFFF_FFFFL)
- + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL)) >>> 1;
- m |= ((*xl) & 0x8000_0000_0000_0000L);
- *ul = m;
- }
- else static if (F.realFormat == RealFormat.ieeeSingle)
- {
- uint *ul = cast(uint *)&u;
- uint *xl = cast(uint *)&x;
- uint *yl = cast(uint *)&y;
- uint m = (((*xl) & 0x7FFF_FFFF) + ((*yl) & 0x7FFF_FFFF)) >>> 1;
- m |= ((*xl) & 0x8000_0000);
- *ul = m;
- }
- else
- {
- assert(0, "Not implemented");
- }
- return u;
-}
-
-@safe pure nothrow @nogc unittest
-{
- assert(ieeeMean(-0.0,-1e-20)<0);
- assert(ieeeMean(0.0,1e-20)>0);
-
- assert(ieeeMean(1.0L,4.0L)==2L);
- assert(ieeeMean(2.0*1.013,8.0*1.013)==4*1.013);
- assert(ieeeMean(-1.0L,-4.0L)==-2L);
- assert(ieeeMean(-1.0,-4.0)==-2);
- assert(ieeeMean(-1.0f,-4.0f)==-2f);
- assert(ieeeMean(-1.0,-2.0)==-1.5);
- assert(ieeeMean(-1*(1+8*real.epsilon),-2*(1+8*real.epsilon))
- ==-1.5*(1+5*real.epsilon));
- assert(ieeeMean(0x1p60,0x1p-10)==0x1p25);
-
- static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
- {
- assert(ieeeMean(1.0L,real.infinity)==0x1p8192L);
- assert(ieeeMean(0.0L,real.infinity)==1.5);
- }
- assert(ieeeMean(0.5*real.min_normal*(1-4*real.epsilon),0.5*real.min_normal)
- == 0.5*real.min_normal*(1-2*real.epsilon));
-}
-
-public:
-
-
-/***********************************
- * Evaluate polynomial A(x) = $(SUB a, 0) + $(SUB a, 1)x + $(SUB a, 2)$(POWER x,2)
- * + $(SUB a,3)$(POWER x,3); ...
- *
- * Uses Horner's rule A(x) = $(SUB a, 0) + x($(SUB a, 1) + x($(SUB a, 2)
- * + x($(SUB a, 3) + ...)))
- * Params:
- * x = the value to evaluate.
- * A = array of coefficients $(SUB a, 0), $(SUB a, 1), etc.
- */
-Unqual!(CommonType!(T1, T2)) poly(T1, T2)(T1 x, in T2[] A) @trusted pure nothrow @nogc
-if (isFloatingPoint!T1 && isFloatingPoint!T2)
-in
-{
- assert(A.length > 0);
-}
-body
-{
- static if (is(Unqual!T2 == real))
- {
- return polyImpl(x, A);
- }
- else
- {
- return polyImplBase(x, A);
- }
-}
-
-///
-@safe nothrow @nogc unittest
-{
- real x = 3.1;
- static real[] pp = [56.1, 32.7, 6];
-
- assert(poly(x, pp) == (56.1L + (32.7L + 6.0L * x) * x));
-}
-
-@safe nothrow @nogc unittest
-{
- double x = 3.1;
- static double[] pp = [56.1, 32.7, 6];
- double y = x;
- y *= 6.0;
- y += 32.7;
- y *= x;
- y += 56.1;
- assert(poly(x, pp) == y);
-}
-
-@safe unittest
-{
- static assert(poly(3.0, [1.0, 2.0, 3.0]) == 34);
-}
-
-private Unqual!(CommonType!(T1, T2)) polyImplBase(T1, T2)(T1 x, in T2[] A) @trusted pure nothrow @nogc
-if (isFloatingPoint!T1 && isFloatingPoint!T2)
-{
- ptrdiff_t i = A.length - 1;
- typeof(return) r = A[i];
- while (--i >= 0)
- {
- r *= x;
- r += A[i];
- }
- return r;
-}
-
-private real polyImpl(real x, in real[] A) @trusted pure nothrow @nogc
-{
- version (D_InlineAsm_X86)
- {
- if (__ctfe)
- {
- return polyImplBase(x, A);
- }
- version (Windows)
- {
- // BUG: This code assumes a frame pointer in EBP.
- asm pure nothrow @nogc // assembler by W. Bright
- {
- // EDX = (A.length - 1) * real.sizeof
- mov ECX,A[EBP] ; // ECX = A.length
- dec ECX ;
- lea EDX,[ECX][ECX*8] ;
- add EDX,ECX ;
- add EDX,A+4[EBP] ;
- fld real ptr [EDX] ; // ST0 = coeff[ECX]
- jecxz return_ST ;
- fld x[EBP] ; // ST0 = x
- fxch ST(1) ; // ST1 = x, ST0 = r
- align 4 ;
- L2: fmul ST,ST(1) ; // r *= x
- fld real ptr -10[EDX] ;
- sub EDX,10 ; // deg--
- faddp ST(1),ST ;
- dec ECX ;
- jne L2 ;
- fxch ST(1) ; // ST1 = r, ST0 = x
- fstp ST(0) ; // dump x
- align 4 ;
- return_ST: ;
- ;
- }
- }
- else version (linux)
- {
- asm pure nothrow @nogc // assembler by W. Bright
- {
- // EDX = (A.length - 1) * real.sizeof
- mov ECX,A[EBP] ; // ECX = A.length
- dec ECX ;
- lea EDX,[ECX*8] ;
- lea EDX,[EDX][ECX*4] ;
- add EDX,A+4[EBP] ;
- fld real ptr [EDX] ; // ST0 = coeff[ECX]
- jecxz return_ST ;
- fld x[EBP] ; // ST0 = x
- fxch ST(1) ; // ST1 = x, ST0 = r
- align 4 ;
- L2: fmul ST,ST(1) ; // r *= x
- fld real ptr -12[EDX] ;
- sub EDX,12 ; // deg--
- faddp ST(1),ST ;
- dec ECX ;
- jne L2 ;
- fxch ST(1) ; // ST1 = r, ST0 = x
- fstp ST(0) ; // dump x
- align 4 ;
- return_ST: ;
- ;
- }
- }
- else version (OSX)
- {
- asm pure nothrow @nogc // assembler by W. Bright
- {
- // EDX = (A.length - 1) * real.sizeof
- mov ECX,A[EBP] ; // ECX = A.length
- dec ECX ;
- lea EDX,[ECX*8] ;
- add EDX,EDX ;
- add EDX,A+4[EBP] ;
- fld real ptr [EDX] ; // ST0 = coeff[ECX]
- jecxz return_ST ;
- fld x[EBP] ; // ST0 = x
- fxch ST(1) ; // ST1 = x, ST0 = r
- align 4 ;
- L2: fmul ST,ST(1) ; // r *= x
- fld real ptr -16[EDX] ;
- sub EDX,16 ; // deg--
- faddp ST(1),ST ;
- dec ECX ;
- jne L2 ;
- fxch ST(1) ; // ST1 = r, ST0 = x
- fstp ST(0) ; // dump x
- align 4 ;
- return_ST: ;
- ;
- }
- }
- else version (FreeBSD)
- {
- asm pure nothrow @nogc // assembler by W. Bright
- {
- // EDX = (A.length - 1) * real.sizeof
- mov ECX,A[EBP] ; // ECX = A.length
- dec ECX ;
- lea EDX,[ECX*8] ;
- lea EDX,[EDX][ECX*4] ;
- add EDX,A+4[EBP] ;
- fld real ptr [EDX] ; // ST0 = coeff[ECX]
- jecxz return_ST ;
- fld x[EBP] ; // ST0 = x
- fxch ST(1) ; // ST1 = x, ST0 = r
- align 4 ;
- L2: fmul ST,ST(1) ; // r *= x
- fld real ptr -12[EDX] ;
- sub EDX,12 ; // deg--
- faddp ST(1),ST ;
- dec ECX ;
- jne L2 ;
- fxch ST(1) ; // ST1 = r, ST0 = x
- fstp ST(0) ; // dump x
- align 4 ;
- return_ST: ;
- ;
- }
- }
- else version (Solaris)
- {
- asm pure nothrow @nogc // assembler by W. Bright
- {
- // EDX = (A.length - 1) * real.sizeof
- mov ECX,A[EBP] ; // ECX = A.length
- dec ECX ;
- lea EDX,[ECX*8] ;
- lea EDX,[EDX][ECX*4] ;
- add EDX,A+4[EBP] ;
- fld real ptr [EDX] ; // ST0 = coeff[ECX]
- jecxz return_ST ;
- fld x[EBP] ; // ST0 = x
- fxch ST(1) ; // ST1 = x, ST0 = r
- align 4 ;
- L2: fmul ST,ST(1) ; // r *= x
- fld real ptr -12[EDX] ;
- sub EDX,12 ; // deg--
- faddp ST(1),ST ;
- dec ECX ;
- jne L2 ;
- fxch ST(1) ; // ST1 = r, ST0 = x
- fstp ST(0) ; // dump x
- align 4 ;
- return_ST: ;
- ;
- }
- }
- else version (DragonFlyBSD)
- {
- asm pure nothrow @nogc // assembler by W. Bright
- {
- // EDX = (A.length - 1) * real.sizeof
- mov ECX,A[EBP] ; // ECX = A.length
- dec ECX ;
- lea EDX,[ECX*8] ;
- lea EDX,[EDX][ECX*4] ;
- add EDX,A+4[EBP] ;
- fld real ptr [EDX] ; // ST0 = coeff[ECX]
- jecxz return_ST ;
- fld x[EBP] ; // ST0 = x
- fxch ST(1) ; // ST1 = x, ST0 = r
- align 4 ;
- L2: fmul ST,ST(1) ; // r *= x
- fld real ptr -12[EDX] ;
- sub EDX,12 ; // deg--
- faddp ST(1),ST ;
- dec ECX ;
- jne L2 ;
- fxch ST(1) ; // ST1 = r, ST0 = x
- fstp ST(0) ; // dump x
- align 4 ;
- return_ST: ;
- ;
- }
- }
- else
- {
- static assert(0);
- }
- }
- else
- {
- return polyImplBase(x, A);
- }
-}
-
-
-/**
- Computes whether two values are approximately equal, admitting a maximum
- relative difference, and a maximum absolute difference.
-
- Params:
- lhs = First item to compare.
- rhs = Second item to compare.
- maxRelDiff = Maximum allowable difference relative to `rhs`.
- maxAbsDiff = Maximum absolute difference.
-
- Returns:
- `true` if the two items are approximately equal under either criterium.
- If one item is a range, and the other is a single value, then the result
- is the logical and-ing of calling `approxEqual` on each element of the
- ranged item against the single item. If both items are ranges, then
- `approxEqual` returns `true` if and only if the ranges have the same
- number of elements and if `approxEqual` evaluates to `true` for each
- pair of elements.
- */
-bool approxEqual(T, U, V)(T lhs, U rhs, V maxRelDiff, V maxAbsDiff = 1e-5)
-{
- import std.range.primitives : empty, front, isInputRange, popFront;
- static if (isInputRange!T)
- {
- static if (isInputRange!U)
- {
- // Two ranges
- for (;; lhs.popFront(), rhs.popFront())
- {
- if (lhs.empty) return rhs.empty;
- if (rhs.empty) return lhs.empty;
- if (!approxEqual(lhs.front, rhs.front, maxRelDiff, maxAbsDiff))
- return false;
- }
- }
- else static if (isIntegral!U)
- {
- // convert rhs to real
- return approxEqual(lhs, real(rhs), maxRelDiff, maxAbsDiff);
- }
- else
- {
- // lhs is range, rhs is number
- for (; !lhs.empty; lhs.popFront())
- {
- if (!approxEqual(lhs.front, rhs, maxRelDiff, maxAbsDiff))
- return false;
- }
- return true;
- }
- }
- else
- {
- static if (isInputRange!U)
- {
- // lhs is number, rhs is range
- for (; !rhs.empty; rhs.popFront())
- {
- if (!approxEqual(lhs, rhs.front, maxRelDiff, maxAbsDiff))
- return false;
- }
- return true;
- }
- else static if (isIntegral!T || isIntegral!U)
- {
- // convert both lhs and rhs to real
- return approxEqual(real(lhs), real(rhs), maxRelDiff, maxAbsDiff);
- }
- else
- {
- // two numbers
- //static assert(is(T : real) && is(U : real));
- if (rhs == 0)
- {
- return fabs(lhs) <= maxAbsDiff;
- }
- static if (is(typeof(lhs.infinity)) && is(typeof(rhs.infinity)))
- {
- if (lhs == lhs.infinity && rhs == rhs.infinity ||
- lhs == -lhs.infinity && rhs == -rhs.infinity) return true;
- }
- return fabs((lhs - rhs) / rhs) <= maxRelDiff
- || maxAbsDiff != 0 && fabs(lhs - rhs) <= maxAbsDiff;
- }
- }
-}
-
-/**
- Returns $(D approxEqual(lhs, rhs, 1e-2, 1e-5)).
- */
-bool approxEqual(T, U)(T lhs, U rhs)
-{
- return approxEqual(lhs, rhs, 1e-2, 1e-5);
-}
-
-///
-@safe pure nothrow unittest
-{
- assert(approxEqual(1.0, 1.0099));
- assert(!approxEqual(1.0, 1.011));
- float[] arr1 = [ 1.0, 2.0, 3.0 ];
- double[] arr2 = [ 1.001, 1.999, 3 ];
- assert(approxEqual(arr1, arr2));
-
- real num = real.infinity;
- assert(num == real.infinity); // Passes.
- assert(approxEqual(num, real.infinity)); // Fails.
- num = -real.infinity;
- assert(num == -real.infinity); // Passes.
- assert(approxEqual(num, -real.infinity)); // Fails.
-
- assert(!approxEqual(3, 0));
- assert(approxEqual(3, 3));
- assert(approxEqual(3.0, 3));
- assert(approxEqual([3, 3, 3], 3.0));
- assert(approxEqual([3.0, 3.0, 3.0], 3));
- int a = 10;
- assert(approxEqual(10, a));
-}
-
-@safe pure nothrow @nogc unittest
-{
- real num = real.infinity;
- assert(num == real.infinity); // Passes.
- assert(approxEqual(num, real.infinity)); // Fails.
-}
-
-
-@safe pure nothrow @nogc unittest
-{
- float f = sqrt(2.0f);
- assert(fabs(f * f - 2.0f) < .00001);
-
- double d = sqrt(2.0);
- assert(fabs(d * d - 2.0) < .00001);
-
- real r = sqrt(2.0L);
- assert(fabs(r * r - 2.0) < .00001);
-}
-
-@safe pure nothrow @nogc unittest
-{
- float f = fabs(-2.0f);
- assert(f == 2);
-
- double d = fabs(-2.0);
- assert(d == 2);
-
- real r = fabs(-2.0L);
- assert(r == 2);
-}
-
-@safe pure nothrow @nogc unittest
-{
- float f = sin(-2.0f);
- assert(fabs(f - -0.909297f) < .00001);
-
- double d = sin(-2.0);
- assert(fabs(d - -0.909297f) < .00001);
-
- real r = sin(-2.0L);
- assert(fabs(r - -0.909297f) < .00001);
-}
-
-@safe pure nothrow @nogc unittest
-{
- float f = cos(-2.0f);
- assert(fabs(f - -0.416147f) < .00001);
-
- double d = cos(-2.0);
- assert(fabs(d - -0.416147f) < .00001);
-
- real r = cos(-2.0L);
- assert(fabs(r - -0.416147f) < .00001);
-}
-
-@safe pure nothrow @nogc unittest
-{
- float f = tan(-2.0f);
- assert(fabs(f - 2.18504f) < .00001);
-
- double d = tan(-2.0);
- assert(fabs(d - 2.18504f) < .00001);
-
- real r = tan(-2.0L);
- assert(fabs(r - 2.18504f) < .00001);
-
- // Verify correct behavior for large inputs
- assert(!isNaN(tan(0x1p63)));
- assert(!isNaN(tan(0x1p300L)));
- assert(!isNaN(tan(-0x1p63)));
- assert(!isNaN(tan(-0x1p300L)));
-}
-
-@safe pure nothrow unittest
-{
- // issue 6381: floor/ceil should be usable in pure function.
- auto x = floor(1.2);
- auto y = ceil(1.2);
-}
-
-@safe pure nothrow unittest
-{
- // relative comparison depends on rhs, make sure proper side is used when
- // comparing range to single value. Based on bugzilla issue 15763
- auto a = [2e-3 - 1e-5];
- auto b = 2e-3 + 1e-5;
- assert(a[0].approxEqual(b));
- assert(!b.approxEqual(a[0]));
- assert(a.approxEqual(b));
- assert(!b.approxEqual(a));
-}
-
-/***********************************
- * Defines a total order on all floating-point numbers.
- *
- * The order is defined as follows:
- * $(UL
- * $(LI All numbers in [-$(INFIN), +$(INFIN)] are ordered
- * the same way as by built-in comparison, with the exception of
- * -0.0, which is less than +0.0;)
- * $(LI If the sign bit is set (that is, it's 'negative'), $(NAN) is less
- * than any number; if the sign bit is not set (it is 'positive'),
- * $(NAN) is greater than any number;)
- * $(LI $(NAN)s of the same sign are ordered by the payload ('negative'
- * ones - in reverse order).)
- * )
- *
- * Returns:
- * negative value if $(D x) precedes $(D y) in the order specified above;
- * 0 if $(D x) and $(D y) are identical, and positive value otherwise.
- *
- * See_Also:
- * $(MYREF isIdentical)
- * Standards: Conforms to IEEE 754-2008
- */
-int cmp(T)(const(T) x, const(T) y) @nogc @trusted pure nothrow
-if (isFloatingPoint!T)
-{
- alias F = floatTraits!T;
-
- static if (F.realFormat == RealFormat.ieeeSingle
- || F.realFormat == RealFormat.ieeeDouble)
- {
- static if (T.sizeof == 4)
- alias UInt = uint;
- else
- alias UInt = ulong;
-
- union Repainter
- {
- T number;
- UInt bits;
- }
-
- enum msb = ~(UInt.max >>> 1);
-
- import std.typecons : Tuple;
- Tuple!(Repainter, Repainter) vars = void;
- vars[0].number = x;
- vars[1].number = y;
-
- foreach (ref var; vars)
- if (var.bits & msb)
- var.bits = ~var.bits;
- else
- var.bits |= msb;
-
- if (vars[0].bits < vars[1].bits)
- return -1;
- else if (vars[0].bits > vars[1].bits)
- return 1;
- else
- return 0;
- }
- else static if (F.realFormat == RealFormat.ieeeExtended53
- || F.realFormat == RealFormat.ieeeExtended
- || F.realFormat == RealFormat.ieeeQuadruple)
- {
- static if (F.realFormat == RealFormat.ieeeQuadruple)
- alias RemT = ulong;
- else
- alias RemT = ushort;
-
- struct Bits
- {
- ulong bulk;
- RemT rem;
- }
-
- union Repainter
- {
- T number;
- Bits bits;
- ubyte[T.sizeof] bytes;
- }
-
- import std.typecons : Tuple;
- Tuple!(Repainter, Repainter) vars = void;
- vars[0].number = x;
- vars[1].number = y;
-
- foreach (ref var; vars)
- if (var.bytes[F.SIGNPOS_BYTE] & 0x80)
- {
- var.bits.bulk = ~var.bits.bulk;
- var.bits.rem = cast(typeof(var.bits.rem))(-1 - var.bits.rem); // ~var.bits.rem
- }
- else
- {
- var.bytes[F.SIGNPOS_BYTE] |= 0x80;
- }
-
- version (LittleEndian)
- {
- if (vars[0].bits.rem < vars[1].bits.rem)
- return -1;
- else if (vars[0].bits.rem > vars[1].bits.rem)
- return 1;
- else if (vars[0].bits.bulk < vars[1].bits.bulk)
- return -1;
- else if (vars[0].bits.bulk > vars[1].bits.bulk)
- return 1;
- else
- return 0;
- }
- else
- {
- if (vars[0].bits.bulk < vars[1].bits.bulk)
- return -1;
- else if (vars[0].bits.bulk > vars[1].bits.bulk)
- return 1;
- else if (vars[0].bits.rem < vars[1].bits.rem)
- return -1;
- else if (vars[0].bits.rem > vars[1].bits.rem)
- return 1;
- else
- return 0;
- }
- }
- else
- {
- // IBM Extended doubledouble does not follow the general
- // sign-exponent-significand layout, so has to be handled generically
-
- const int xSign = signbit(x),
- ySign = signbit(y);
-
- if (xSign == 1 && ySign == 1)
- return cmp(-y, -x);
- else if (xSign == 1)
- return -1;
- else if (ySign == 1)
- return 1;
- else if (x < y)
- return -1;
- else if (x == y)
- return 0;
- else if (x > y)
- return 1;
- else if (isNaN(x) && !isNaN(y))
- return 1;
- else if (isNaN(y) && !isNaN(x))
- return -1;
- else if (getNaNPayload(x) < getNaNPayload(y))
- return -1;
- else if (getNaNPayload(x) > getNaNPayload(y))
- return 1;
- else
- return 0;
- }
-}
-
-/// Most numbers are ordered naturally.
-@safe unittest
-{
- assert(cmp(-double.infinity, -double.max) < 0);
- assert(cmp(-double.max, -100.0) < 0);
- assert(cmp(-100.0, -0.5) < 0);
- assert(cmp(-0.5, 0.0) < 0);
- assert(cmp(0.0, 0.5) < 0);
- assert(cmp(0.5, 100.0) < 0);
- assert(cmp(100.0, double.max) < 0);
- assert(cmp(double.max, double.infinity) < 0);
-
- assert(cmp(1.0, 1.0) == 0);
-}
-
-/// Positive and negative zeroes are distinct.
-@safe unittest
-{
- assert(cmp(-0.0, +0.0) < 0);
- assert(cmp(+0.0, -0.0) > 0);
-}
-
-/// Depending on the sign, $(NAN)s go to either end of the spectrum.
-@safe unittest
-{
- assert(cmp(-double.nan, -double.infinity) < 0);
- assert(cmp(double.infinity, double.nan) < 0);
- assert(cmp(-double.nan, double.nan) < 0);
-}
-
-/// $(NAN)s of the same sign are ordered by the payload.
-@safe unittest
-{
- assert(cmp(NaN(10), NaN(20)) < 0);
- assert(cmp(-NaN(20), -NaN(10)) < 0);
-}
-
-@safe unittest
-{
- import std.meta : AliasSeq;
- foreach (T; AliasSeq!(float, double, real))
- {
- T[] values = [-cast(T) NaN(20), -cast(T) NaN(10), -T.nan, -T.infinity,
- -T.max, -T.max / 2, T(-16.0), T(-1.0).nextDown,
- T(-1.0), T(-1.0).nextUp,
- T(-0.5), -T.min_normal, (-T.min_normal).nextUp,
- -2 * T.min_normal * T.epsilon,
- -T.min_normal * T.epsilon,
- T(-0.0), T(0.0),
- T.min_normal * T.epsilon,
- 2 * T.min_normal * T.epsilon,
- T.min_normal.nextDown, T.min_normal, T(0.5),
- T(1.0).nextDown, T(1.0),
- T(1.0).nextUp, T(16.0), T.max / 2, T.max,
- T.infinity, T.nan, cast(T) NaN(10), cast(T) NaN(20)];
-
- foreach (i, x; values)
- {
- foreach (y; values[i + 1 .. $])
- {
- assert(cmp(x, y) < 0);
- assert(cmp(y, x) > 0);
- }
- assert(cmp(x, x) == 0);
- }
- }
-}
-
-private enum PowType
-{
- floor,
- ceil
-}
-
-pragma(inline, true)
-private T powIntegralImpl(PowType type, T)(T val)
-{
- import core.bitop : bsr;
-
- if (val == 0 || (type == PowType.ceil && (val > T.max / 2 || val == T.min)))
- return 0;
- else
- {
- static if (isSigned!T)
- return cast(Unqual!T) (val < 0 ? -(T(1) << bsr(0 - val) + type) : T(1) << bsr(val) + type);
- else
- return cast(Unqual!T) (T(1) << bsr(val) + type);
- }
-}
-
-private T powFloatingPointImpl(PowType type, T)(T x)
-{
- if (!x.isFinite)
- return x;
-
- if (!x)
- return x;
-
- int exp;
- auto y = frexp(x, exp);
-
- static if (type == PowType.ceil)
- y = ldexp(cast(T) 0.5, exp + 1);
- else
- y = ldexp(cast(T) 0.5, exp);
-
- if (!y.isFinite)
- return cast(T) 0.0;
-
- y = copysign(y, x);
-
- return y;
-}
-
-/**
- * Gives the next power of two after $(D val). `T` can be any built-in
- * numerical type.
- *
- * If the operation would lead to an over/underflow, this function will
- * return `0`.
- *
- * Params:
- * val = any number
- *
- * Returns:
- * the next power of two after $(D val)
- */
-T nextPow2(T)(const T val)
-if (isIntegral!T)
-{
- return powIntegralImpl!(PowType.ceil)(val);
-}
-
-/// ditto
-T nextPow2(T)(const T val)
-if (isFloatingPoint!T)
-{
- return powFloatingPointImpl!(PowType.ceil)(val);
-}
-
-///
-@safe @nogc pure nothrow unittest
-{
- assert(nextPow2(2) == 4);
- assert(nextPow2(10) == 16);
- assert(nextPow2(4000) == 4096);
-
- assert(nextPow2(-2) == -4);
- assert(nextPow2(-10) == -16);
-
- assert(nextPow2(uint.max) == 0);
- assert(nextPow2(uint.min) == 0);
- assert(nextPow2(size_t.max) == 0);
- assert(nextPow2(size_t.min) == 0);
-
- assert(nextPow2(int.max) == 0);
- assert(nextPow2(int.min) == 0);
- assert(nextPow2(long.max) == 0);
- assert(nextPow2(long.min) == 0);
-}
-
-///
-@safe @nogc pure nothrow unittest
-{
- assert(nextPow2(2.1) == 4.0);
- assert(nextPow2(-2.0) == -4.0);
- assert(nextPow2(0.25) == 0.5);
- assert(nextPow2(-4.0) == -8.0);
-
- assert(nextPow2(double.max) == 0.0);
- assert(nextPow2(double.infinity) == double.infinity);
-}
-
-@safe @nogc pure nothrow unittest
-{
- assert(nextPow2(ubyte(2)) == 4);
- assert(nextPow2(ubyte(10)) == 16);
-
- assert(nextPow2(byte(2)) == 4);
- assert(nextPow2(byte(10)) == 16);
-
- assert(nextPow2(short(2)) == 4);
- assert(nextPow2(short(10)) == 16);
- assert(nextPow2(short(4000)) == 4096);
-
- assert(nextPow2(ushort(2)) == 4);
- assert(nextPow2(ushort(10)) == 16);
- assert(nextPow2(ushort(4000)) == 4096);
-}
-
-@safe @nogc pure nothrow unittest
-{
- foreach (ulong i; 1 .. 62)
- {
- assert(nextPow2(1UL << i) == 2UL << i);
- assert(nextPow2((1UL << i) - 1) == 1UL << i);
- assert(nextPow2((1UL << i) + 1) == 2UL << i);
- assert(nextPow2((1UL << i) + (1UL<<(i-1))) == 2UL << i);
- }
-}
-
-@safe @nogc pure nothrow unittest
-{
- import std.meta : AliasSeq;
-
- foreach (T; AliasSeq!(float, double, real))
- {
- enum T subNormal = T.min_normal / 2;
-
- static if (subNormal) assert(nextPow2(subNormal) == T.min_normal);
-
- assert(nextPow2(T(0.0)) == 0.0);
-
- assert(nextPow2(T(2.0)) == 4.0);
- assert(nextPow2(T(2.1)) == 4.0);
- assert(nextPow2(T(3.1)) == 4.0);
- assert(nextPow2(T(4.0)) == 8.0);
- assert(nextPow2(T(0.25)) == 0.5);
-
- assert(nextPow2(T(-2.0)) == -4.0);
- assert(nextPow2(T(-2.1)) == -4.0);
- assert(nextPow2(T(-3.1)) == -4.0);
- assert(nextPow2(T(-4.0)) == -8.0);
- assert(nextPow2(T(-0.25)) == -0.5);
-
- assert(nextPow2(T.max) == 0);
- assert(nextPow2(-T.max) == 0);
-
- assert(nextPow2(T.infinity) == T.infinity);
- assert(nextPow2(T.init).isNaN);
- }
-}
-
-@safe @nogc pure nothrow unittest // Issue 15973
-{
- assert(nextPow2(uint.max / 2) == uint.max / 2 + 1);
- assert(nextPow2(uint.max / 2 + 2) == 0);
- assert(nextPow2(int.max / 2) == int.max / 2 + 1);
- assert(nextPow2(int.max / 2 + 2) == 0);
- assert(nextPow2(int.min + 1) == int.min);
-}
-
-/**
- * Gives the last power of two before $(D val). $(T) can be any built-in
- * numerical type.
- *
- * Params:
- * val = any number
- *
- * Returns:
- * the last power of two before $(D val)
- */
-T truncPow2(T)(const T val)
-if (isIntegral!T)
-{
- return powIntegralImpl!(PowType.floor)(val);
-}
-
-/// ditto
-T truncPow2(T)(const T val)
-if (isFloatingPoint!T)
-{
- return powFloatingPointImpl!(PowType.floor)(val);
-}
-
-///
-@safe @nogc pure nothrow unittest
-{
- assert(truncPow2(3) == 2);
- assert(truncPow2(4) == 4);
- assert(truncPow2(10) == 8);
- assert(truncPow2(4000) == 2048);
-
- assert(truncPow2(-5) == -4);
- assert(truncPow2(-20) == -16);
-
- assert(truncPow2(uint.max) == int.max + 1);
- assert(truncPow2(uint.min) == 0);
- assert(truncPow2(ulong.max) == long.max + 1);
- assert(truncPow2(ulong.min) == 0);
-
- assert(truncPow2(int.max) == (int.max / 2) + 1);
- assert(truncPow2(int.min) == int.min);
- assert(truncPow2(long.max) == (long.max / 2) + 1);
- assert(truncPow2(long.min) == long.min);
-}
-
-///
-@safe @nogc pure nothrow unittest
-{
- assert(truncPow2(2.1) == 2.0);
- assert(truncPow2(7.0) == 4.0);
- assert(truncPow2(-1.9) == -1.0);
- assert(truncPow2(0.24) == 0.125);
- assert(truncPow2(-7.0) == -4.0);
-
- assert(truncPow2(double.infinity) == double.infinity);
-}
-
-@safe @nogc pure nothrow unittest
-{
- assert(truncPow2(ubyte(3)) == 2);
- assert(truncPow2(ubyte(4)) == 4);
- assert(truncPow2(ubyte(10)) == 8);
-
- assert(truncPow2(byte(3)) == 2);
- assert(truncPow2(byte(4)) == 4);
- assert(truncPow2(byte(10)) == 8);
-
- assert(truncPow2(ushort(3)) == 2);
- assert(truncPow2(ushort(4)) == 4);
- assert(truncPow2(ushort(10)) == 8);
- assert(truncPow2(ushort(4000)) == 2048);
-
- assert(truncPow2(short(3)) == 2);
- assert(truncPow2(short(4)) == 4);
- assert(truncPow2(short(10)) == 8);
- assert(truncPow2(short(4000)) == 2048);
-}
-
-@safe @nogc pure nothrow unittest
-{
- foreach (ulong i; 1 .. 62)
- {
- assert(truncPow2(2UL << i) == 2UL << i);
- assert(truncPow2((2UL << i) + 1) == 2UL << i);
- assert(truncPow2((2UL << i) - 1) == 1UL << i);
- assert(truncPow2((2UL << i) - (2UL<<(i-1))) == 1UL << i);
- }
-}
-
-@safe @nogc pure nothrow unittest
-{
- import std.meta : AliasSeq;
-
- foreach (T; AliasSeq!(float, double, real))
- {
- assert(truncPow2(T(0.0)) == 0.0);
-
- assert(truncPow2(T(4.0)) == 4.0);
- assert(truncPow2(T(2.1)) == 2.0);
- assert(truncPow2(T(3.5)) == 2.0);
- assert(truncPow2(T(7.0)) == 4.0);
- assert(truncPow2(T(0.24)) == 0.125);
-
- assert(truncPow2(T(-2.0)) == -2.0);
- assert(truncPow2(T(-2.1)) == -2.0);
- assert(truncPow2(T(-3.1)) == -2.0);
- assert(truncPow2(T(-7.0)) == -4.0);
- assert(truncPow2(T(-0.24)) == -0.125);
-
- assert(truncPow2(T.infinity) == T.infinity);
- assert(truncPow2(T.init).isNaN);
- }
-}
-
-/**
-Check whether a number is an integer power of two.
-
-Note that only positive numbers can be integer powers of two. This
-function always return `false` if `x` is negative or zero.
-
-Params:
- x = the number to test
-
-Returns:
- `true` if `x` is an integer power of two.
-*/
-bool isPowerOf2(X)(const X x) pure @safe nothrow @nogc
-if (isNumeric!X)
-{
- static if (isFloatingPoint!X)
- {
- int exp;
- const X sig = frexp(x, exp);
-
- return (exp != int.min) && (sig is cast(X) 0.5L);
- }
- else
- {
- static if (isSigned!X)
- {
- auto y = cast(typeof(x + 0))x;
- return y > 0 && !(y & (y - 1));
- }
- else
- {
- auto y = cast(typeof(x + 0u))x;
- return (y & -y) > (y - 1);
- }
- }
-}
-///
-@safe unittest
-{
- assert( isPowerOf2(1.0L));
- assert( isPowerOf2(2.0L));
- assert( isPowerOf2(0.5L));
- assert( isPowerOf2(pow(2.0L, 96)));
- assert( isPowerOf2(pow(2.0L, -77)));
-
- assert(!isPowerOf2(-2.0L));
- assert(!isPowerOf2(-0.5L));
- assert(!isPowerOf2(0.0L));
- assert(!isPowerOf2(4.315));
- assert(!isPowerOf2(1.0L / 3.0L));
-
- assert(!isPowerOf2(real.nan));
- assert(!isPowerOf2(real.infinity));
-}
-///
-@safe unittest
-{
- assert( isPowerOf2(1));
- assert( isPowerOf2(2));
- assert( isPowerOf2(1uL << 63));
-
- assert(!isPowerOf2(-4));
- assert(!isPowerOf2(0));
- assert(!isPowerOf2(1337u));
-}
-
-@safe unittest
-{
- import std.meta : AliasSeq;
-
- immutable smallP2 = pow(2.0L, -62);
- immutable bigP2 = pow(2.0L, 50);
- immutable smallP7 = pow(7.0L, -35);
- immutable bigP7 = pow(7.0L, 30);
-
- foreach (X; AliasSeq!(float, double, real))
- {
- immutable min_sub = X.min_normal * X.epsilon;
-
- foreach (x; AliasSeq!(smallP2, min_sub, X.min_normal, .25L, 0.5L, 1.0L,
- 2.0L, 8.0L, pow(2.0L, X.max_exp - 1), bigP2))
- {
- assert( isPowerOf2(cast(X) x));
- assert(!isPowerOf2(cast(X)-x));
- }
-
- foreach (x; AliasSeq!(0.0L, 3 * min_sub, smallP7, 0.1L, 1337.0L, bigP7, X.max, real.nan, real.infinity))
- {
- assert(!isPowerOf2(cast(X) x));
- assert(!isPowerOf2(cast(X)-x));
- }
- }
-
- foreach (X; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
- {
- foreach (x; [1, 2, 4, 8, (X.max >>> 1) + 1])
- {
- assert( isPowerOf2(cast(X) x));
- static if (isSigned!X)
- assert(!isPowerOf2(cast(X)-x));
- }
-
- foreach (x; [0, 3, 5, 13, 77, X.min, X.max])
- assert(!isPowerOf2(cast(X) x));
- }
-}
diff --git a/libphobos/src/std/math/algebraic.d b/libphobos/src/std/math/algebraic.d
new file mode 100644
index 00000000000..ae7cbf941b4
--- /dev/null
+++ b/libphobos/src/std/math/algebraic.d
@@ -0,0 +1,1072 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, math).
+
+It contains classical algebraic functions like `abs`, `sqrt`, and `poly`.
+
+Copyright: Copyright The D Language Foundation 2000 - 2011.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
+ Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
+Source: $(PHOBOSSRC std/math/algebraic.d)
+
+Macros:
+ TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
+ <caption>Special Values</caption>
+ $0</table>
+ NAN = $(RED NAN)
+ POWER = $1<sup>$2</sup>
+ SUB = $1<sub>$2</sub>
+ PLUSMN = &plusmn;
+ INFIN = &infin;
+ PLUSMNINF = &plusmn;&infin;
+ LT = &lt;
+
+ */
+
+module std.math.algebraic;
+
+static import core.math;
+static import core.stdc.math;
+import std.traits : CommonType, isFloatingPoint, isIntegral, isSigned, Unqual;
+
+/***********************************
+ * Calculates the absolute value of a number.
+ *
+ * Params:
+ * Num = (template parameter) type of number
+ * x = real number value
+ *
+ * Returns:
+ * The absolute value of the number. If floating-point or integral,
+ * the return type will be the same as the input.
+ *
+ * Limitations:
+ * Does not work correctly for signed intergal types and value `Num`.min.
+ */
+auto abs(Num)(Num x) @nogc pure nothrow
+if ((is(immutable Num == immutable short) || is(immutable Num == immutable byte)) ||
+ (is(typeof(Num.init >= 0)) && is(typeof(-Num.init))))
+{
+ static if (isFloatingPoint!(Num))
+ return fabs(x);
+ else
+ {
+ static if (is(immutable Num == immutable short) || is(immutable Num == immutable byte))
+ return x >= 0 ? x : cast(Num) -int(x);
+ else
+ return x >= 0 ? x : -x;
+ }
+}
+
+/// ditto
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isIdentical, isNaN;
+
+ assert(isIdentical(abs(-0.0L), 0.0L));
+ assert(isNaN(abs(real.nan)));
+ assert(abs(-real.infinity) == real.infinity);
+ assert(abs(-56) == 56);
+ assert(abs(2321312L) == 2321312L);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ short s = -8;
+ byte b = -8;
+ assert(abs(s) == 8);
+ assert(abs(b) == 8);
+ immutable(byte) c = -8;
+ assert(abs(c) == 8);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ T f = 3;
+ assert(abs(f) == f);
+ assert(abs(-f) == f);
+ }}
+}
+
+// see https://issues.dlang.org/show_bug.cgi?id=20205
+// to avoid falling into the trap again
+@safe pure nothrow @nogc unittest
+{
+ assert(50 - abs(-100) == -50);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19162
+@safe unittest
+{
+ struct Vector(T, int size)
+ {
+ T x, y, z;
+ }
+
+ static auto abs(T, int size)(auto ref const Vector!(T, size) v)
+ {
+ return v;
+ }
+ Vector!(int, 3) v;
+ assert(abs(v) == v);
+}
+
+/*******************************
+ * Returns |x|
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH fabs(x)))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) )
+ * )
+ */
+pragma(inline, true)
+real fabs(real x) @safe pure nothrow @nogc { return core.math.fabs(x); }
+
+///ditto
+pragma(inline, true)
+double fabs(double x) @safe pure nothrow @nogc { return core.math.fabs(x); }
+
+///ditto
+pragma(inline, true)
+float fabs(float x) @safe pure nothrow @nogc { return core.math.fabs(x); }
+
+///
+@safe unittest
+{
+ import std.math.traits : isIdentical;
+
+ assert(isIdentical(fabs(0.0f), 0.0f));
+ assert(isIdentical(fabs(-0.0f), 0.0f));
+ assert(fabs(-10.0f) == 10.0f);
+
+ assert(isIdentical(fabs(0.0), 0.0));
+ assert(isIdentical(fabs(-0.0), 0.0));
+ assert(fabs(-10.0) == 10.0);
+
+ assert(isIdentical(fabs(0.0L), 0.0L));
+ assert(isIdentical(fabs(-0.0L), 0.0L));
+ assert(fabs(-10.0L) == 10.0L);
+}
+
+@safe unittest
+{
+ real function(real) pfabs = &fabs;
+ assert(pfabs != null);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ float f = fabs(-2.0f);
+ assert(f == 2);
+
+ double d = fabs(-2.0);
+ assert(d == 2);
+
+ real r = fabs(-2.0L);
+ assert(r == 2);
+}
+
+/***************************************
+ * Compute square root of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH sqrt(x)) $(TH invalid?))
+ * $(TR $(TD -0.0) $(TD -0.0) $(TD no))
+ * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no))
+ * )
+ */
+pragma(inline, true)
+float sqrt(float x) @nogc @safe pure nothrow { return core.math.sqrt(x); }
+
+/// ditto
+pragma(inline, true)
+double sqrt(double x) @nogc @safe pure nothrow { return core.math.sqrt(x); }
+
+/// ditto
+pragma(inline, true)
+real sqrt(real x) @nogc @safe pure nothrow { return core.math.sqrt(x); }
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : feqrel;
+ import std.math.traits : isNaN;
+
+ assert(sqrt(2.0).feqrel(1.4142) > 16);
+ assert(sqrt(9.0).feqrel(3.0) > 16);
+
+ assert(isNaN(sqrt(-1.0f)));
+ assert(isNaN(sqrt(-1.0)));
+ assert(isNaN(sqrt(-1.0L)));
+}
+
+@safe unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=5305
+ float function(float) psqrtf = &sqrt;
+ assert(psqrtf != null);
+ double function(double) psqrtd = &sqrt;
+ assert(psqrtd != null);
+ real function(real) psqrtr = &sqrt;
+ assert(psqrtr != null);
+
+ //ctfe
+ enum ZX80 = sqrt(7.0f);
+ enum ZX81 = sqrt(7.0);
+ enum ZX82 = sqrt(7.0L);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ float f = sqrt(2.0f);
+ assert(fabs(f * f - 2.0f) < .00001);
+
+ double d = sqrt(2.0);
+ assert(fabs(d * d - 2.0) < .00001);
+
+ real r = sqrt(2.0L);
+ assert(fabs(r * r - 2.0) < .00001);
+}
+
+/***************
+ * Calculates the cube root of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH $(I x)) $(TH cbrt(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) )
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no) )
+ * )
+ */
+real cbrt(real x) @trusted nothrow @nogc
+{
+ version (CRuntime_Microsoft)
+ {
+ import std.math.traits : copysign;
+ import std.math.exponential : exp2;
+
+ version (INLINE_YL2X)
+ return copysign(exp2(core.math.yl2x(fabs(x), 1.0L/3.0L)), x);
+ else
+ return core.stdc.math.cbrtl(x);
+ }
+ else
+ return core.stdc.math.cbrtl(x);
+}
+
+///
+@safe unittest
+{
+ import std.math.operations : feqrel;
+
+ assert(cbrt(1.0).feqrel(1.0) > 16);
+ assert(cbrt(27.0).feqrel(3.0) > 16);
+ assert(cbrt(15.625).feqrel(2.5) > 16);
+}
+
+/***********************************************************************
+ * Calculates the length of the
+ * hypotenuse of a right-angled triangle with sides of length x and y.
+ * The hypotenuse is the value of the square root of
+ * the sums of the squares of x and y:
+ *
+ * sqrt($(POWER x, 2) + $(POWER y, 2))
+ *
+ * Note that hypot(x, y), hypot(y, x) and
+ * hypot(x, -y) are equivalent.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH hypot(x, y)) $(TH invalid?))
+ * $(TR $(TD x) $(TD $(PLUSMN)0.0) $(TD |x|) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD y) $(TD +$(INFIN)) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD +$(INFIN)) $(TD no))
+ * )
+ */
+T hypot(T)(const T x, const T y) @safe pure nothrow @nogc
+if (isFloatingPoint!T)
+{
+ // Scale x and y to avoid underflow and overflow.
+ // If one is huge and the other tiny, return the larger.
+ // If both are huge, avoid overflow by scaling by 2^^-N.
+ // If both are tiny, avoid underflow by scaling by 2^^N.
+ import core.math : fabs, sqrt;
+ import std.math : floatTraits, RealFormat;
+
+ alias F = floatTraits!T;
+
+ T u = fabs(x);
+ T v = fabs(y);
+ if (!(u >= v)) // check for NaN as well.
+ {
+ v = u;
+ u = fabs(y);
+ if (u == T.infinity) return u; // hypot(inf, nan) == inf
+ if (v == T.infinity) return v; // hypot(nan, inf) == inf
+ }
+
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ enum SQRTMIN = 0x1p-60f;
+ enum SQRTMAX = 0x1p+60f;
+ enum SCALE_UNDERFLOW = 0x1p+90f;
+ enum SCALE_OVERFLOW = 0x1p-90f;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble ||
+ F.realFormat == RealFormat.ieeeExtended53 ||
+ F.realFormat == RealFormat.ibmExtended)
+ {
+ enum SQRTMIN = 0x1p-450L;
+ enum SQRTMAX = 0x1p+500L;
+ enum SCALE_UNDERFLOW = 0x1p+600L;
+ enum SCALE_OVERFLOW = 0x1p-600L;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ enum SQRTMIN = 0x1p-8000L;
+ enum SQRTMAX = 0x1p+8000L;
+ enum SCALE_UNDERFLOW = 0x1p+10000L;
+ enum SCALE_OVERFLOW = 0x1p-10000L;
+ }
+ else
+ assert(0, "hypot not implemented");
+
+ // Now u >= v, or else one is NaN.
+ T ratio = 1.0;
+ if (v >= SQRTMAX)
+ {
+ // hypot(huge, huge) -- avoid overflow
+ ratio = SCALE_UNDERFLOW;
+ u *= SCALE_OVERFLOW;
+ v *= SCALE_OVERFLOW;
+ }
+ else if (u <= SQRTMIN)
+ {
+ // hypot (tiny, tiny) -- avoid underflow
+ // This is only necessary to avoid setting the underflow
+ // flag.
+ ratio = SCALE_OVERFLOW;
+ u *= SCALE_UNDERFLOW;
+ v *= SCALE_UNDERFLOW;
+ }
+
+ if (u * T.epsilon > v)
+ {
+ // hypot (huge, tiny) = huge
+ return u;
+ }
+
+ // both are in the normal range
+ return ratio * sqrt(u*u + v*v);
+}
+
+///
+@safe unittest
+{
+ import std.math.operations : feqrel;
+
+ assert(hypot(1.0, 1.0).feqrel(1.4142) > 16);
+ assert(hypot(3.0, 4.0).feqrel(5.0) > 16);
+ assert(hypot(real.infinity, 1.0L) == real.infinity);
+ assert(hypot(real.infinity, real.nan) == real.infinity);
+}
+
+@safe unittest
+{
+ import std.math.operations : feqrel;
+
+ assert(hypot(1.0f, 1.0f).feqrel(1.4142f) > 16);
+ assert(hypot(3.0f, 4.0f).feqrel(5.0f) > 16);
+ assert(hypot(float.infinity, 1.0f) == float.infinity);
+ assert(hypot(float.infinity, float.nan) == float.infinity);
+
+ assert(hypot(1.0L, 1.0L).feqrel(1.4142L) > 16);
+ assert(hypot(3.0L, 4.0L).feqrel(5.0L) > 16);
+ assert(hypot(double.infinity, 1.0) == double.infinity);
+ assert(hypot(double.infinity, double.nan) == double.infinity);
+}
+
+@safe unittest
+{
+ import std.math.operations : feqrel;
+ import std.math.traits : isIdentical;
+ import std.meta : AliasSeq;
+
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ static T[3][] vals = // x,y,hypot
+ [
+ [ 0.0, 0.0, 0.0],
+ [ 0.0, -0.0, 0.0],
+ [ -0.0, -0.0, 0.0],
+ [ 3.0, 4.0, 5.0],
+ [ -300, -400, 500],
+ [0.0, 7.0, 7.0],
+ [9.0, 9*T.epsilon, 9.0],
+ [88/(64*sqrt(T.min_normal)), 105/(64*sqrt(T.min_normal)), 137/(64*sqrt(T.min_normal))],
+ [88/(128*sqrt(T.min_normal)), 105/(128*sqrt(T.min_normal)), 137/(128*sqrt(T.min_normal))],
+ [3*T.min_normal*T.epsilon, 4*T.min_normal*T.epsilon, 5*T.min_normal*T.epsilon],
+ [ T.min_normal, T.min_normal, sqrt(2.0L)*T.min_normal],
+ [ T.max/sqrt(2.0L), T.max/sqrt(2.0L), T.max],
+ [ T.infinity, T.nan, T.infinity],
+ [ T.nan, T.infinity, T.infinity],
+ [ T.nan, T.nan, T.nan],
+ [ T.nan, T.max, T.nan],
+ [ T.max, T.nan, T.nan],
+ ];
+ for (int i = 0; i < vals.length; i++)
+ {
+ T x = vals[i][0];
+ T y = vals[i][1];
+ T z = vals[i][2];
+ T h = hypot(x, y);
+ assert(isIdentical(z,h) || feqrel(z, h) >= T.mant_dig - 1);
+ }
+ }}
+}
+
+/***********************************************************************
+ * Calculates the distance of the point (x, y, z) from the origin (0, 0, 0)
+ * in three-dimensional space.
+ * The distance is the value of the square root of the sums of the squares
+ * of x, y, and z:
+ *
+ * sqrt($(POWER x, 2) + $(POWER y, 2) + $(POWER z, 2))
+ *
+ * Note that the distance between two points (x1, y1, z1) and (x2, y2, z2)
+ * in three-dimensional space can be calculated as hypot(x2-x1, y2-y1, z2-z1).
+ *
+ * Params:
+ * x = floating point value
+ * y = floating point value
+ * z = floating point value
+ *
+ * Returns:
+ * The square root of the sum of the squares of the given arguments.
+ */
+T hypot(T)(const T x, const T y, const T z) @safe pure nothrow @nogc
+if (isFloatingPoint!T)
+{
+ import core.math : fabs, sqrt;
+ import std.math.operations : fmax;
+ const absx = fabs(x);
+ const absy = fabs(y);
+ const absz = fabs(z);
+
+ // Scale all parameters to avoid overflow.
+ const ratio = fmax(absx, fmax(absy, absz));
+ if (ratio == 0.0)
+ return ratio;
+
+ return ratio * sqrt((absx / ratio) * (absx / ratio)
+ + (absy / ratio) * (absy / ratio)
+ + (absz / ratio) * (absz / ratio));
+}
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(hypot(1.0, 2.0, 2.0), 3.0));
+ assert(isClose(hypot(2.0, 3.0, 6.0), 7.0));
+ assert(isClose(hypot(1.0, 4.0, 8.0), 9.0));
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ import std.math.traits : isIdentical;
+ import std.math.operations : isClose;
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ static T[4][] vals = [
+ [ 0.0L, 0.0L, 0.0L, 0.0L ],
+ [ 0.0L, 1.0L, 1.0L, sqrt(2.0L) ],
+ [ 1.0L, 1.0L, 1.0L, sqrt(3.0L) ],
+ [ 1.0L, 2.0L, 2.0L, 3.0L ],
+ [ 2.0L, 3.0L, 6.0L, 7.0L ],
+ [ 1.0L, 4.0L, 8.0L, 9.0L ],
+ [ 4.0L, 4.0L, 7.0L, 9.0L ],
+ [ 12.0L, 16.0L, 21.0L, 29.0L ],
+ [ 1e+8L, 1.0L, 1e-8L, 1e+8L ],
+ [ 1.0L, 1e+8L, 1e-8L, 1e+8L ],
+ [ 1e-8L, 1.0L, 1e+8L, 1e+8L ],
+ [ 1e-2L, 1e-4L, 1e-4L, 0.010000999950004999375L ],
+ [ 2147483647.0L, 2147483647.0L, 2147483647.0L, 3719550785.027307813987L ]
+ ];
+ for (int i = 0; i < vals.length; i++)
+ {
+ T x = vals[i][0];
+ T y = vals[i][1];
+ T z = vals[i][2];
+ T r = vals[i][3];
+ T a = hypot(x, y, z);
+ assert(isIdentical(r, a) || isClose(r, a));
+ }
+ }}
+}
+
+/***********************************
+ * Evaluate polynomial A(x) = $(SUB a, 0) + $(SUB a, 1)x + $(SUB a, 2)$(POWER x,2) +
+ * $(SUB a,3)$(POWER x,3); ...
+ *
+ * Uses Horner's rule A(x) = $(SUB a, 0) + x($(SUB a, 1) + x($(SUB a, 2) +
+ * x($(SUB a, 3) + ...)))
+ * Params:
+ * x = the value to evaluate.
+ * A = array of coefficients $(SUB a, 0), $(SUB a, 1), etc.
+ */
+Unqual!(CommonType!(T1, T2)) poly(T1, T2)(T1 x, in T2[] A) @trusted pure nothrow @nogc
+if (isFloatingPoint!T1 && isFloatingPoint!T2)
+in
+{
+ assert(A.length > 0);
+}
+do
+{
+ static if (is(immutable T2 == immutable real))
+ {
+ return polyImpl(x, A);
+ }
+ else
+ {
+ return polyImplBase(x, A);
+ }
+}
+
+/// ditto
+Unqual!(CommonType!(T1, T2)) poly(T1, T2, int N)(T1 x, ref const T2[N] A) @safe pure nothrow @nogc
+if (isFloatingPoint!T1 && isFloatingPoint!T2 && N > 0 && N <= 10)
+{
+ // statically unrolled version for up to 10 coefficients
+ typeof(return) r = A[N - 1];
+ static foreach (i; 1 .. N)
+ {
+ r *= x;
+ r += A[N - 1 - i];
+ }
+ return r;
+}
+
+///
+@safe nothrow @nogc unittest
+{
+ real x = 3.1L;
+ static real[] pp = [56.1L, 32.7L, 6];
+
+ assert(poly(x, pp) == (56.1L + (32.7L + 6.0L * x) * x));
+}
+
+@safe nothrow @nogc unittest
+{
+ double x = 3.1;
+ static double[] pp = [56.1, 32.7, 6];
+ double y = x;
+ y *= 6.0;
+ y += 32.7;
+ y *= x;
+ y += 56.1;
+ assert(poly(x, pp) == y);
+}
+
+@safe unittest
+{
+ static assert(poly(3.0, [1.0, 2.0, 3.0]) == 34);
+}
+
+private Unqual!(CommonType!(T1, T2)) polyImplBase(T1, T2)(T1 x, in T2[] A) @trusted pure nothrow @nogc
+if (isFloatingPoint!T1 && isFloatingPoint!T2)
+{
+ ptrdiff_t i = A.length - 1;
+ typeof(return) r = A[i];
+ while (--i >= 0)
+ {
+ r *= x;
+ r += A[i];
+ }
+ return r;
+}
+
+private real polyImpl(real x, in real[] A) @trusted pure nothrow @nogc
+{
+ version (D_InlineAsm_X86)
+ {
+ if (__ctfe)
+ {
+ return polyImplBase(x, A);
+ }
+ version (Windows)
+ {
+ // BUG: This code assumes a frame pointer in EBP.
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX][ECX*8] ;
+ add EDX,ECX ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -10[EDX] ;
+ sub EDX,10 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ }
+ }
+ else version (linux)
+ {
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX*8] ;
+ lea EDX,[EDX][ECX*4] ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -12[EDX] ;
+ sub EDX,12 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ }
+ }
+ else version (OSX)
+ {
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX*8] ;
+ add EDX,EDX ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -16[EDX] ;
+ sub EDX,16 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ }
+ }
+ else version (FreeBSD)
+ {
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX*8] ;
+ lea EDX,[EDX][ECX*4] ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -12[EDX] ;
+ sub EDX,12 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ }
+ }
+ else version (Solaris)
+ {
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX*8] ;
+ lea EDX,[EDX][ECX*4] ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -12[EDX] ;
+ sub EDX,12 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ }
+ }
+ else version (DragonFlyBSD)
+ {
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX*8] ;
+ lea EDX,[EDX][ECX*4] ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -12[EDX] ;
+ sub EDX,12 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ }
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+ else
+ {
+ return polyImplBase(x, A);
+ }
+}
+
+/**
+ * Gives the next power of two after `val`. `T` can be any built-in
+ * numerical type.
+ *
+ * If the operation would lead to an over/underflow, this function will
+ * return `0`.
+ *
+ * Params:
+ * val = any number
+ *
+ * Returns:
+ * the next power of two after `val`
+ */
+T nextPow2(T)(const T val)
+if (isIntegral!T)
+{
+ return powIntegralImpl!(PowType.ceil)(val);
+}
+
+/// ditto
+T nextPow2(T)(const T val)
+if (isFloatingPoint!T)
+{
+ return powFloatingPointImpl!(PowType.ceil)(val);
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ assert(nextPow2(2) == 4);
+ assert(nextPow2(10) == 16);
+ assert(nextPow2(4000) == 4096);
+
+ assert(nextPow2(-2) == -4);
+ assert(nextPow2(-10) == -16);
+
+ assert(nextPow2(uint.max) == 0);
+ assert(nextPow2(uint.min) == 0);
+ assert(nextPow2(size_t.max) == 0);
+ assert(nextPow2(size_t.min) == 0);
+
+ assert(nextPow2(int.max) == 0);
+ assert(nextPow2(int.min) == 0);
+ assert(nextPow2(long.max) == 0);
+ assert(nextPow2(long.min) == 0);
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ assert(nextPow2(2.1) == 4.0);
+ assert(nextPow2(-2.0) == -4.0);
+ assert(nextPow2(0.25) == 0.5);
+ assert(nextPow2(-4.0) == -8.0);
+
+ assert(nextPow2(double.max) == 0.0);
+ assert(nextPow2(double.infinity) == double.infinity);
+}
+
+@safe @nogc pure nothrow unittest
+{
+ assert(nextPow2(ubyte(2)) == 4);
+ assert(nextPow2(ubyte(10)) == 16);
+
+ assert(nextPow2(byte(2)) == 4);
+ assert(nextPow2(byte(10)) == 16);
+
+ assert(nextPow2(short(2)) == 4);
+ assert(nextPow2(short(10)) == 16);
+ assert(nextPow2(short(4000)) == 4096);
+
+ assert(nextPow2(ushort(2)) == 4);
+ assert(nextPow2(ushort(10)) == 16);
+ assert(nextPow2(ushort(4000)) == 4096);
+}
+
+@safe @nogc pure nothrow unittest
+{
+ foreach (ulong i; 1 .. 62)
+ {
+ assert(nextPow2(1UL << i) == 2UL << i);
+ assert(nextPow2((1UL << i) - 1) == 1UL << i);
+ assert(nextPow2((1UL << i) + 1) == 2UL << i);
+ assert(nextPow2((1UL << i) + (1UL<<(i-1))) == 2UL << i);
+ }
+}
+
+@safe @nogc pure nothrow unittest
+{
+ import std.math.traits : isNaN;
+ import std.meta : AliasSeq;
+
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ enum T subNormal = T.min_normal / 2;
+
+ static if (subNormal) assert(nextPow2(subNormal) == T.min_normal);
+
+ assert(nextPow2(T(0.0)) == 0.0);
+
+ assert(nextPow2(T(2.0)) == 4.0);
+ assert(nextPow2(T(2.1)) == 4.0);
+ assert(nextPow2(T(3.1)) == 4.0);
+ assert(nextPow2(T(4.0)) == 8.0);
+ assert(nextPow2(T(0.25)) == 0.5);
+
+ assert(nextPow2(T(-2.0)) == -4.0);
+ assert(nextPow2(T(-2.1)) == -4.0);
+ assert(nextPow2(T(-3.1)) == -4.0);
+ assert(nextPow2(T(-4.0)) == -8.0);
+ assert(nextPow2(T(-0.25)) == -0.5);
+
+ assert(nextPow2(T.max) == 0);
+ assert(nextPow2(-T.max) == 0);
+
+ assert(nextPow2(T.infinity) == T.infinity);
+ assert(nextPow2(T.init).isNaN);
+ }}
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15973
+@safe @nogc pure nothrow unittest
+{
+ assert(nextPow2(uint.max / 2) == uint.max / 2 + 1);
+ assert(nextPow2(uint.max / 2 + 2) == 0);
+ assert(nextPow2(int.max / 2) == int.max / 2 + 1);
+ assert(nextPow2(int.max / 2 + 2) == 0);
+ assert(nextPow2(int.min + 1) == int.min);
+}
+
+/**
+ * Gives the last power of two before `val`. $(T) can be any built-in
+ * numerical type.
+ *
+ * Params:
+ * val = any number
+ *
+ * Returns:
+ * the last power of two before `val`
+ */
+T truncPow2(T)(const T val)
+if (isIntegral!T)
+{
+ return powIntegralImpl!(PowType.floor)(val);
+}
+
+/// ditto
+T truncPow2(T)(const T val)
+if (isFloatingPoint!T)
+{
+ return powFloatingPointImpl!(PowType.floor)(val);
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ assert(truncPow2(3) == 2);
+ assert(truncPow2(4) == 4);
+ assert(truncPow2(10) == 8);
+ assert(truncPow2(4000) == 2048);
+
+ assert(truncPow2(-5) == -4);
+ assert(truncPow2(-20) == -16);
+
+ assert(truncPow2(uint.max) == int.max + 1);
+ assert(truncPow2(uint.min) == 0);
+ assert(truncPow2(ulong.max) == long.max + 1);
+ assert(truncPow2(ulong.min) == 0);
+
+ assert(truncPow2(int.max) == (int.max / 2) + 1);
+ assert(truncPow2(int.min) == int.min);
+ assert(truncPow2(long.max) == (long.max / 2) + 1);
+ assert(truncPow2(long.min) == long.min);
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ assert(truncPow2(2.1) == 2.0);
+ assert(truncPow2(7.0) == 4.0);
+ assert(truncPow2(-1.9) == -1.0);
+ assert(truncPow2(0.24) == 0.125);
+ assert(truncPow2(-7.0) == -4.0);
+
+ assert(truncPow2(double.infinity) == double.infinity);
+}
+
+@safe @nogc pure nothrow unittest
+{
+ assert(truncPow2(ubyte(3)) == 2);
+ assert(truncPow2(ubyte(4)) == 4);
+ assert(truncPow2(ubyte(10)) == 8);
+
+ assert(truncPow2(byte(3)) == 2);
+ assert(truncPow2(byte(4)) == 4);
+ assert(truncPow2(byte(10)) == 8);
+
+ assert(truncPow2(ushort(3)) == 2);
+ assert(truncPow2(ushort(4)) == 4);
+ assert(truncPow2(ushort(10)) == 8);
+ assert(truncPow2(ushort(4000)) == 2048);
+
+ assert(truncPow2(short(3)) == 2);
+ assert(truncPow2(short(4)) == 4);
+ assert(truncPow2(short(10)) == 8);
+ assert(truncPow2(short(4000)) == 2048);
+}
+
+@safe @nogc pure nothrow unittest
+{
+ foreach (ulong i; 1 .. 62)
+ {
+ assert(truncPow2(2UL << i) == 2UL << i);
+ assert(truncPow2((2UL << i) + 1) == 2UL << i);
+ assert(truncPow2((2UL << i) - 1) == 1UL << i);
+ assert(truncPow2((2UL << i) - (2UL<<(i-1))) == 1UL << i);
+ }
+}
+
+@safe @nogc pure nothrow unittest
+{
+ import std.math.traits : isNaN;
+ import std.meta : AliasSeq;
+
+ static foreach (T; AliasSeq!(float, double, real))
+ {
+ assert(truncPow2(T(0.0)) == 0.0);
+
+ assert(truncPow2(T(4.0)) == 4.0);
+ assert(truncPow2(T(2.1)) == 2.0);
+ assert(truncPow2(T(3.5)) == 2.0);
+ assert(truncPow2(T(7.0)) == 4.0);
+ assert(truncPow2(T(0.24)) == 0.125);
+
+ assert(truncPow2(T(-2.0)) == -2.0);
+ assert(truncPow2(T(-2.1)) == -2.0);
+ assert(truncPow2(T(-3.1)) == -2.0);
+ assert(truncPow2(T(-7.0)) == -4.0);
+ assert(truncPow2(T(-0.24)) == -0.125);
+
+ assert(truncPow2(T.infinity) == T.infinity);
+ assert(truncPow2(T.init).isNaN);
+ }
+}
+
+private enum PowType
+{
+ floor,
+ ceil
+}
+
+pragma(inline, true)
+private T powIntegralImpl(PowType type, T)(T val)
+{
+ import core.bitop : bsr;
+
+ if (val == 0 || (type == PowType.ceil && (val > T.max / 2 || val == T.min)))
+ return 0;
+ else
+ {
+ static if (isSigned!T)
+ return cast(Unqual!T) (val < 0 ? -(T(1) << bsr(0 - val) + type) : T(1) << bsr(val) + type);
+ else
+ return cast(Unqual!T) (T(1) << bsr(val) + type);
+ }
+}
+
+private T powFloatingPointImpl(PowType type, T)(T x)
+{
+ import std.math.traits : copysign, isFinite;
+ import std.math.exponential : frexp;
+
+ if (!x.isFinite)
+ return x;
+
+ if (!x)
+ return x;
+
+ int exp;
+ auto y = frexp(x, exp);
+
+ static if (type == PowType.ceil)
+ y = core.math.ldexp(cast(T) 0.5, exp + 1);
+ else
+ y = core.math.ldexp(cast(T) 0.5, exp);
+
+ if (!y.isFinite)
+ return cast(T) 0.0;
+
+ y = copysign(y, x);
+
+ return y;
+}
diff --git a/libphobos/src/std/math/constants.d b/libphobos/src/std/math/constants.d
new file mode 100644
index 00000000000..0c0da0db55f
--- /dev/null
+++ b/libphobos/src/std/math/constants.d
@@ -0,0 +1,38 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, math).
+
+It contains several useful mathematical constants.
+
+Copyright: Copyright The D Language Foundation 2000 - 2011.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston
+Source: $(PHOBOSSRC std/math/constants.d)
+
+Macros:
+ SUB = $1<sub>$2</sub>
+ PI = &pi;
+ SQRT = &radic;
+ HALF = &frac12;
+ */
+module std.math.constants;
+
+// Values obtained from Wolfram Alpha. 116 bits ought to be enough for anybody.
+// Wolfram Alpha LLC. 2011. Wolfram|Alpha. http://www.wolframalpha.com/input/?i=e+in+base+16 (access July 6, 2011).
+enum real E = 0x1.5bf0a8b1457695355fb8ac404e7a8p+1L; /** e = 2.718281... */
+enum real LOG2T = 0x1.a934f0979a3715fc9257edfe9b5fbp+1L; /** $(SUB log, 2)10 = 3.321928... */
+enum real LOG2E = 0x1.71547652b82fe1777d0ffda0d23a8p+0L; /** $(SUB log, 2)e = 1.442695... */
+enum real LOG2 = 0x1.34413509f79fef311f12b35816f92p-2L; /** $(SUB log, 10)2 = 0.301029... */
+enum real LOG10E = 0x1.bcb7b1526e50e32a6ab7555f5a67cp-2L; /** $(SUB log, 10)e = 0.434294... */
+enum real LN2 = 0x1.62e42fefa39ef35793c7673007e5fp-1L; /** ln 2 = 0.693147... */
+enum real LN10 = 0x1.26bb1bbb5551582dd4adac5705a61p+1L; /** ln 10 = 2.302585... */
+enum real PI = 0x1.921fb54442d18469898cc51701b84p+1L; /** &pi; = 3.141592... */
+enum real PI_2 = PI/2; /** $(PI) / 2 = 1.570796... */
+enum real PI_4 = PI/4; /** $(PI) / 4 = 0.785398... */
+enum real M_1_PI = 0x1.45f306dc9c882a53f84eafa3ea69cp-2L; /** 1 / $(PI) = 0.318309... */
+enum real M_2_PI = 2*M_1_PI; /** 2 / $(PI) = 0.636619... */
+enum real M_2_SQRTPI = 0x1.20dd750429b6d11ae3a914fed7fd8p+0L; /** 2 / $(SQRT)$(PI) = 1.128379... */
+enum real SQRT2 = 0x1.6a09e667f3bcc908b2fb1366ea958p+0L; /** $(SQRT)2 = 1.414213... */
+enum real SQRT1_2 = SQRT2/2; /** $(SQRT)$(HALF) = 0.707106... */
+// Note: Make sure the magic numbers in compiler backend for x87 match these.
diff --git a/libphobos/src/std/math/exponential.d b/libphobos/src/std/math/exponential.d
new file mode 100644
index 00000000000..a9ec930d90c
--- /dev/null
+++ b/libphobos/src/std/math/exponential.d
@@ -0,0 +1,3439 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, math).
+
+It contains several exponential and logarithm functions.
+
+Copyright: Copyright The D Language Foundation 2000 - 2011.
+ D implementations of exp, expm1, exp2, log, log10, log1p, and log2
+ functions are based on the CEPHES math library, which is Copyright
+ (C) 2001 Stephen L. Moshier $(LT)steve@moshier.net$(GT) and are
+ incorporated herein by permission of the author. The author reserves
+ the right to distribute this material elsewhere under different
+ copying permissions. These modifications are distributed here under
+ the following terms:
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
+ Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
+Source: $(PHOBOSSRC std/math/exponential.d)
+
+Macros:
+ TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
+ <caption>Special Values</caption>
+ $0</table>
+ NAN = $(RED NAN)
+ PLUSMN = &plusmn;
+ INFIN = &infin;
+ PLUSMNINF = &plusmn;&infin;
+ LT = &lt;
+ GT = &gt;
+ */
+
+module std.math.exponential;
+
+import std.traits : isFloatingPoint, isIntegral, isSigned, isUnsigned, Largest, Unqual;
+
+static import core.math;
+static import core.stdc.math;
+
+version (DigitalMars)
+{
+ version = INLINE_YL2X; // x87 has opcodes for these
+}
+
+version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
+version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
+
+version (InlineAsm_X86_Any) version = InlineAsm_X87;
+version (InlineAsm_X87)
+{
+ static assert(real.mant_dig == 64);
+ version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC;
+}
+
+version (D_HardFloat)
+{
+ // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport
+ version (IeeeFlagsSupport) version = FloatingPointControlSupport;
+}
+
+/**
+ * Compute the value of x $(SUPERSCRIPT n), where n is an integer
+ */
+Unqual!F pow(F, G)(F x, G n) @nogc @trusted pure nothrow
+if (isFloatingPoint!(F) && isIntegral!(G))
+{
+ import std.traits : Unsigned;
+
+ real p = 1.0, v = void;
+ Unsigned!(Unqual!G) m = n;
+
+ if (n < 0)
+ {
+ if (n == -1) return 1 / x;
+
+ m = cast(typeof(m))(0 - n);
+ v = p / x;
+ }
+ else
+ {
+ switch (n)
+ {
+ case 0:
+ return 1.0;
+ case 1:
+ return x;
+ case 2:
+ return x * x;
+ default:
+ }
+
+ v = x;
+ }
+
+ while (1)
+ {
+ if (m & 1)
+ p *= v;
+ m >>= 1;
+ if (!m)
+ break;
+ v *= v;
+ }
+ return p;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : feqrel;
+
+ assert(pow(2.0, 5) == 32.0);
+ assert(pow(1.5, 9).feqrel(38.4433) > 16);
+ assert(pow(real.nan, 2) is real.nan);
+ assert(pow(real.infinity, 2) == real.infinity);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose, feqrel;
+
+ // Make sure it instantiates and works properly on immutable values and
+ // with various integer and float types.
+ immutable real x = 46;
+ immutable float xf = x;
+ immutable double xd = x;
+ immutable uint one = 1;
+ immutable ushort two = 2;
+ immutable ubyte three = 3;
+ immutable ulong eight = 8;
+
+ immutable int neg1 = -1;
+ immutable short neg2 = -2;
+ immutable byte neg3 = -3;
+ immutable long neg8 = -8;
+
+
+ assert(pow(x,0) == 1.0);
+ assert(pow(xd,one) == x);
+ assert(pow(xf,two) == x * x);
+ assert(pow(x,three) == x * x * x);
+ assert(pow(x,eight) == (x * x) * (x * x) * (x * x) * (x * x));
+
+ assert(pow(x, neg1) == 1 / x);
+
+ assert(isClose(pow(xd, neg2), cast(double) (1 / (x * x)), 1e-15));
+ assert(isClose(pow(xf, neg8), cast(float) (1 / ((x * x) * (x * x) * (x * x) * (x * x))), 1e-15));
+
+ assert(feqrel(pow(x, neg3), 1 / (x * x * x)) >= real.mant_dig - 1);
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(pow(2.0L, 10L), 1024, 1e-18));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21601
+@safe @nogc nothrow pure unittest
+{
+ // When reals are large enough the results of pow(b, e) can be
+ // calculated correctly, if b is of type float or double and e is
+ // not too large.
+ static if (real.mant_dig >= 64)
+ {
+ // expected result: 3.790e-42
+ assert(pow(-513645318757045764096.0f, -2) > 0.0);
+
+ // expected result: 3.763915357831797e-309
+ assert(pow(-1.6299717435255677e+154, -2) > 0.0);
+ }
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isInfinity;
+
+ static float f1 = 19100.0f;
+ static float f2 = 0.000012f;
+
+ assert(isClose(pow(f1,9), 3.3829868e+38f));
+ assert(isInfinity(pow(f1,10)));
+ assert(pow(f2,9) > 0.0f);
+ assert(isClose(pow(f2,10), 0.0f, 0.0, float.min_normal));
+
+ static double d1 = 21800.0;
+ static double d2 = 0.000012;
+
+ assert(isClose(pow(d1,71), 1.0725339442974e+308));
+ assert(isInfinity(pow(d1,72)));
+ assert(pow(d2,65) > 0.0f);
+ assert(isClose(pow(d2,66), 0.0, 0.0, double.min_normal));
+
+ static if (real.mant_dig == 64) // x87
+ {
+ static real r1 = 21950.0L;
+ static real r2 = 0.000011L;
+
+ assert(isClose(pow(r1,1136), 7.4066175654969242752260330529e+4931L));
+ assert(isInfinity(pow(r1,1137)));
+ assert(pow(r2,998) > 0.0L);
+ assert(isClose(pow(r2,999), 0.0L, 0.0, real.min_normal));
+ }
+}
+
+@safe @nogc nothrow pure unittest
+{
+ import std.math.operations : isClose;
+
+ enum f1 = 19100.0f;
+ enum f2 = 0.000012f;
+
+ static assert(isClose(pow(f1,9), 3.3829868e+38f));
+ static assert(pow(f1,10) > float.max);
+ static assert(pow(f2,9) > 0.0f);
+ static assert(isClose(pow(f2,10), 0.0f, 0.0, float.min_normal));
+
+ enum d1 = 21800.0;
+ enum d2 = 0.000012;
+
+ static assert(isClose(pow(d1,71), 1.0725339442974e+308));
+ static assert(pow(d1,72) > double.max);
+ static assert(pow(d2,65) > 0.0f);
+ static assert(isClose(pow(d2,66), 0.0, 0.0, double.min_normal));
+
+ static if (real.mant_dig == 64) // x87
+ {
+ enum r1 = 21950.0L;
+ enum r2 = 0.000011L;
+
+ static assert(isClose(pow(r1,1136), 7.4066175654969242752260330529e+4931L));
+ static assert(pow(r1,1137) > real.max);
+ static assert(pow(r2,998) > 0.0L);
+ static assert(isClose(pow(r2,999), 0.0L, 0.0, real.min_normal));
+ }
+}
+
+/**
+ * Compute the power of two integral numbers.
+ *
+ * Params:
+ * x = base
+ * n = exponent
+ *
+ * Returns:
+ * x raised to the power of n. If n is negative the result is 1 / pow(x, -n),
+ * which is calculated as integer division with remainder. This may result in
+ * a division by zero error.
+ *
+ * If both x and n are 0, the result is 1.
+ *
+ * Throws:
+ * If x is 0 and n is negative, the result is the same as the result of a
+ * division by zero.
+ */
+typeof(Unqual!(F).init * Unqual!(G).init) pow(F, G)(F x, G n) @nogc @trusted pure nothrow
+if (isIntegral!(F) && isIntegral!(G))
+{
+ import std.traits : isSigned;
+
+ typeof(return) p, v = void;
+ Unqual!G m = n;
+
+ static if (isSigned!(F))
+ {
+ if (x == -1) return cast(typeof(return)) (m & 1 ? -1 : 1);
+ }
+ static if (isSigned!(G))
+ {
+ if (x == 0 && m <= -1) return x / 0;
+ }
+ if (x == 1) return 1;
+ static if (isSigned!(G))
+ {
+ if (m < 0) return 0;
+ }
+
+ switch (m)
+ {
+ case 0:
+ p = 1;
+ break;
+
+ case 1:
+ p = x;
+ break;
+
+ case 2:
+ p = x * x;
+ break;
+
+ default:
+ v = x;
+ p = 1;
+ while (1)
+ {
+ if (m & 1)
+ p *= v;
+ m >>= 1;
+ if (!m)
+ break;
+ v *= v;
+ }
+ break;
+ }
+ return p;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(pow(2, 3) == 8);
+ assert(pow(3, 2) == 9);
+
+ assert(pow(2, 10) == 1_024);
+ assert(pow(2, 20) == 1_048_576);
+ assert(pow(2, 30) == 1_073_741_824);
+
+ assert(pow(0, 0) == 1);
+
+ assert(pow(1, -5) == 1);
+ assert(pow(1, -6) == 1);
+ assert(pow(-1, -5) == -1);
+ assert(pow(-1, -6) == 1);
+
+ assert(pow(-2, 5) == -32);
+ assert(pow(-2, -5) == 0);
+ assert(pow(cast(double) -2, -5) == -0.03125);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ immutable int one = 1;
+ immutable byte two = 2;
+ immutable ubyte three = 3;
+ immutable short four = 4;
+ immutable long ten = 10;
+
+ assert(pow(two, three) == 8);
+ assert(pow(two, ten) == 1024);
+ assert(pow(one, ten) == 1);
+ assert(pow(ten, four) == 10_000);
+ assert(pow(four, 10) == 1_048_576);
+ assert(pow(three, four) == 81);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=7006
+@safe pure nothrow @nogc unittest
+{
+ assert(pow(5, -1) == 0);
+ assert(pow(-5, -1) == 0);
+ assert(pow(5, -2) == 0);
+ assert(pow(-5, -2) == 0);
+ assert(pow(-1, int.min) == 1);
+ assert(pow(-2, int.min) == 0);
+
+ assert(pow(4294967290UL,2) == 18446744022169944100UL);
+ assert(pow(0,uint.max) == 0);
+}
+
+/**Computes integer to floating point powers.*/
+real pow(I, F)(I x, F y) @nogc @trusted pure nothrow
+if (isIntegral!I && isFloatingPoint!F)
+{
+ return pow(cast(real) x, cast(Unqual!F) y);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(pow(2, 5.0) == 32.0);
+ assert(pow(7, 3.0) == 343.0);
+ assert(pow(2, real.nan) is real.nan);
+ assert(pow(2, real.infinity) == real.infinity);
+}
+
+/**
+ * Calculates x$(SUPERSCRIPT y).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH pow(x, y))
+ * $(TH div 0) $(TH invalid?))
+ * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD 1.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD |x| $(GT) 1) $(TD +$(INFIN)) $(TD +$(INFIN))
+ * $(TD no) $(TD no) )
+ * $(TR $(TD |x| $(LT) 1) $(TD +$(INFIN)) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD |x| $(GT) 1) $(TD -$(INFIN)) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD |x| $(LT) 1) $(TD -$(INFIN)) $(TD +$(INFIN))
+ * $(TD no) $(TD no) )
+ * $(TR $(TD +$(INFIN)) $(TD $(GT) 0.0) $(TD +$(INFIN))
+ * $(TD no) $(TD no) )
+ * $(TR $(TD +$(INFIN)) $(TD $(LT) 0.0) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD -$(INFIN)) $(TD odd integer $(GT) 0.0) $(TD -$(INFIN))
+ * $(TD no) $(TD no) )
+ * $(TR $(TD -$(INFIN)) $(TD $(GT) 0.0, not odd integer) $(TD +$(INFIN))
+ * $(TD no) $(TD no))
+ * $(TR $(TD -$(INFIN)) $(TD odd integer $(LT) 0.0) $(TD -0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD -$(INFIN)) $(TD $(LT) 0.0, not odd integer) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD $(PLUSMN)1.0) $(TD $(PLUSMN)$(INFIN)) $(TD -$(NAN))
+ * $(TD no) $(TD yes) )
+ * $(TR $(TD $(LT) 0.0) $(TD finite, nonintegral) $(TD $(NAN))
+ * $(TD no) $(TD yes))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD odd integer $(LT) 0.0) $(TD $(PLUSMNINF))
+ * $(TD yes) $(TD no) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT) 0.0, not odd integer) $(TD +$(INFIN))
+ * $(TD yes) $(TD no))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD odd integer $(GT) 0.0) $(TD $(PLUSMN)0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT) 0.0, not odd integer) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * )
+ */
+Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow
+if (isFloatingPoint!(F) && isFloatingPoint!(G))
+{
+ import core.math : fabs, sqrt;
+ import std.math.traits : isInfinity, isNaN, signbit;
+
+ alias Float = typeof(return);
+
+ static real impl(real x, real y) @nogc pure nothrow
+ {
+ // Special cases.
+ if (isNaN(y))
+ return y;
+ if (isNaN(x) && y != 0.0)
+ return x;
+
+ // Even if x is NaN.
+ if (y == 0.0)
+ return 1.0;
+ if (y == 1.0)
+ return x;
+
+ if (isInfinity(y))
+ {
+ if (isInfinity(x))
+ {
+ if (!signbit(y) && !signbit(x))
+ return F.infinity;
+ else
+ return F.nan;
+ }
+ else if (fabs(x) > 1)
+ {
+ if (signbit(y))
+ return +0.0;
+ else
+ return F.infinity;
+ }
+ else if (fabs(x) == 1)
+ {
+ return F.nan;
+ }
+ else // < 1
+ {
+ if (signbit(y))
+ return F.infinity;
+ else
+ return +0.0;
+ }
+ }
+ if (isInfinity(x))
+ {
+ if (signbit(x))
+ {
+ long i = cast(long) y;
+ if (y > 0.0)
+ {
+ if (i == y && i & 1)
+ return -F.infinity;
+ else if (i == y)
+ return F.infinity;
+ else
+ return -F.nan;
+ }
+ else if (y < 0.0)
+ {
+ if (i == y && i & 1)
+ return -0.0;
+ else if (i == y)
+ return +0.0;
+ else
+ return F.nan;
+ }
+ }
+ else
+ {
+ if (y > 0.0)
+ return F.infinity;
+ else if (y < 0.0)
+ return +0.0;
+ }
+ }
+
+ if (x == 0.0)
+ {
+ if (signbit(x))
+ {
+ long i = cast(long) y;
+ if (y > 0.0)
+ {
+ if (i == y && i & 1)
+ return -0.0;
+ else
+ return +0.0;
+ }
+ else if (y < 0.0)
+ {
+ if (i == y && i & 1)
+ return -F.infinity;
+ else
+ return F.infinity;
+ }
+ }
+ else
+ {
+ if (y > 0.0)
+ return +0.0;
+ else if (y < 0.0)
+ return F.infinity;
+ }
+ }
+ if (x == 1.0)
+ return 1.0;
+
+ if (y >= F.max)
+ {
+ if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
+ return 0.0;
+ if (x > 1.0 || x < -1.0)
+ return F.infinity;
+ }
+ if (y <= -F.max)
+ {
+ if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
+ return F.infinity;
+ if (x > 1.0 || x < -1.0)
+ return 0.0;
+ }
+
+ if (x >= F.max)
+ {
+ if (y > 0.0)
+ return F.infinity;
+ else
+ return 0.0;
+ }
+ if (x <= -F.max)
+ {
+ long i = cast(long) y;
+ if (y > 0.0)
+ {
+ if (i == y && i & 1)
+ return -F.infinity;
+ else
+ return F.infinity;
+ }
+ else if (y < 0.0)
+ {
+ if (i == y && i & 1)
+ return -0.0;
+ else
+ return +0.0;
+ }
+ }
+
+ // Integer power of x.
+ long iy = cast(long) y;
+ if (iy == y && fabs(y) < 32_768.0)
+ return pow(x, iy);
+
+ real sign = 1.0;
+ if (x < 0)
+ {
+ // Result is real only if y is an integer
+ // Check for a non-zero fractional part
+ enum maxOdd = pow(2.0L, real.mant_dig) - 1.0L;
+ static if (maxOdd > ulong.max)
+ {
+ // Generic method, for any FP type
+ import std.math.rounding : floor;
+ if (floor(y) != y)
+ return sqrt(x); // Complex result -- create a NaN
+
+ const hy = 0.5 * y;
+ if (floor(hy) != hy)
+ sign = -1.0;
+ }
+ else
+ {
+ // Much faster, if ulong has enough precision
+ const absY = fabs(y);
+ if (absY <= maxOdd)
+ {
+ const uy = cast(ulong) absY;
+ if (uy != absY)
+ return sqrt(x); // Complex result -- create a NaN
+
+ if (uy & 1)
+ sign = -1.0;
+ }
+ }
+ x = -x;
+ }
+ version (INLINE_YL2X)
+ {
+ // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
+ // TODO: This is not accurate in practice. A fast and accurate
+ // (though complicated) method is described in:
+ // "An efficient rounding boundary test for pow(x, y)
+ // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007).
+ return sign * exp2( core.math.yl2x(x, y) );
+ }
+ else
+ {
+ // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
+ // TODO: This is not accurate in practice. A fast and accurate
+ // (though complicated) method is described in:
+ // "An efficient rounding boundary test for pow(x, y)
+ // in double precision", C.Q. Lauter and V. Lefèvre, INRIA (2007).
+ Float w = exp2(y * log2(x));
+ return sign * w;
+ }
+ }
+ return impl(x, y);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(pow(2.0, 3.0), 8.0));
+ assert(isClose(pow(1.5, 10.0), 57.6650390625));
+
+ // square root of 9
+ assert(isClose(pow(9.0, 0.5), 3.0));
+ // 10th root of 1024
+ assert(isClose(pow(1024.0, 0.1), 2.0));
+
+ assert(isClose(pow(-4.0, 3.0), -64.0));
+
+ // reciprocal of 4 ^^ 2
+ assert(isClose(pow(4.0, -2.0), 0.0625));
+ // reciprocal of (-2) ^^ 3
+ assert(isClose(pow(-2.0, -3.0), -0.125));
+
+ assert(isClose(pow(-2.5, 3.0), -15.625));
+ // reciprocal of 2.5 ^^ 3
+ assert(isClose(pow(2.5, -3.0), 0.064));
+ // reciprocal of (-2.5) ^^ 3
+ assert(isClose(pow(-2.5, -3.0), -0.064));
+
+ // reciprocal of square root of 4
+ assert(isClose(pow(4.0, -0.5), 0.5));
+
+ // per definition
+ assert(isClose(pow(0.0, 0.0), 1.0));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+
+ // the result is a complex number
+ // which cannot be represented as floating point number
+ import std.math.traits : isNaN;
+ assert(isNaN(pow(-2.5, -1.5)));
+
+ // use the ^^-operator of std.complex instead
+ import std.complex : complex;
+ auto c1 = complex(-2.5, 0.0);
+ auto c2 = complex(-1.5, 0.0);
+ auto result = c1 ^^ c2;
+ // exact result apparently depends on `real` precision => increased tolerance
+ assert(isClose(result.re, -4.64705438e-17, 2e-4));
+ assert(isClose(result.im, 2.52982e-1, 2e-4));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(pow(1.5, real.infinity) == real.infinity);
+ assert(pow(0.5, real.infinity) == 0.0);
+ assert(pow(1.5, -real.infinity) == 0.0);
+ assert(pow(0.5, -real.infinity) == real.infinity);
+ assert(pow(real.infinity, 1.0) == real.infinity);
+ assert(pow(real.infinity, -1.0) == 0.0);
+ assert(pow(real.infinity, real.infinity) == real.infinity);
+ assert(pow(-real.infinity, 1.0) == -real.infinity);
+ assert(pow(-real.infinity, 2.0) == real.infinity);
+ assert(pow(-real.infinity, -1.0) == -0.0);
+ assert(pow(-real.infinity, -2.0) == 0.0);
+ assert(isNaN(pow(1.0, real.infinity)));
+ assert(pow(0.0, -1.0) == real.infinity);
+ assert(pow(real.nan, 0.0) == 1.0);
+ assert(isNaN(pow(real.nan, 3.0)));
+ assert(isNaN(pow(3.0, real.nan)));
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(pow(2.0L, 10.0L), 1024, 1e-18));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isIdentical, isNaN;
+ import std.math.constants : PI;
+
+ // Test all the special values. These unittests can be run on Windows
+ // by temporarily changing the version (linux) to version (all).
+ immutable float zero = 0;
+ immutable real one = 1;
+ immutable double two = 2;
+ immutable float three = 3;
+ immutable float fnan = float.nan;
+ immutable double dnan = double.nan;
+ immutable real rnan = real.nan;
+ immutable dinf = double.infinity;
+ immutable rninf = -real.infinity;
+
+ assert(pow(fnan, zero) == 1);
+ assert(pow(dnan, zero) == 1);
+ assert(pow(rnan, zero) == 1);
+
+ assert(pow(two, dinf) == double.infinity);
+ assert(isIdentical(pow(0.2f, dinf), +0.0));
+ assert(pow(0.99999999L, rninf) == real.infinity);
+ assert(isIdentical(pow(1.000000001, rninf), +0.0));
+ assert(pow(dinf, 0.001) == dinf);
+ assert(isIdentical(pow(dinf, -0.001), +0.0));
+ assert(pow(rninf, 3.0L) == rninf);
+ assert(pow(rninf, 2.0L) == real.infinity);
+ assert(isIdentical(pow(rninf, -3.0), -0.0));
+ assert(isIdentical(pow(rninf, -2.0), +0.0));
+
+ // @@@BUG@@@ somewhere
+ version (OSX) {} else assert(isNaN(pow(one, dinf)));
+ version (OSX) {} else assert(isNaN(pow(-one, dinf)));
+ assert(isNaN(pow(-0.2, PI)));
+ // boundary cases. Note that epsilon == 2^^-n for some n,
+ // so 1/epsilon == 2^^n is always even.
+ assert(pow(-1.0L, 1/real.epsilon - 1.0L) == -1.0L);
+ assert(pow(-1.0L, 1/real.epsilon) == 1.0L);
+ assert(isNaN(pow(-1.0L, 1/real.epsilon-0.5L)));
+ assert(isNaN(pow(-1.0L, -1/real.epsilon+0.5L)));
+
+ assert(pow(0.0, -3.0) == double.infinity);
+ assert(pow(-0.0, -3.0) == -double.infinity);
+ assert(pow(0.0, -PI) == double.infinity);
+ assert(pow(-0.0, -PI) == double.infinity);
+ assert(isIdentical(pow(0.0, 5.0), 0.0));
+ assert(isIdentical(pow(-0.0, 5.0), -0.0));
+ assert(isIdentical(pow(0.0, 6.0), 0.0));
+ assert(isIdentical(pow(-0.0, 6.0), 0.0));
+
+ // https://issues.dlang.org/show_bug.cgi?id=14786 fixed
+ immutable real maxOdd = pow(2.0L, real.mant_dig) - 1.0L;
+ assert(pow(-1.0L, maxOdd) == -1.0L);
+ assert(pow(-1.0L, -maxOdd) == -1.0L);
+ assert(pow(-1.0L, maxOdd + 1.0L) == 1.0L);
+ assert(pow(-1.0L, -maxOdd + 1.0L) == 1.0L);
+ assert(pow(-1.0L, maxOdd - 1.0L) == 1.0L);
+ assert(pow(-1.0L, -maxOdd - 1.0L) == 1.0L);
+
+ // Now, actual numbers.
+ assert(isClose(pow(two, three), 8.0));
+ assert(isClose(pow(two, -2.5), 0.1767766953));
+
+ // Test integer to float power.
+ immutable uint twoI = 2;
+ assert(isClose(pow(twoI, three), 8.0));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20508
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(isNaN(pow(-double.infinity, 0.5)));
+
+ assert(isNaN(pow(-real.infinity, real.infinity)));
+ assert(isNaN(pow(-real.infinity, -real.infinity)));
+ assert(isNaN(pow(-real.infinity, 1.234)));
+ assert(isNaN(pow(-real.infinity, -0.751)));
+ assert(pow(-real.infinity, 0.0) == 1.0);
+}
+
+/** Computes the value of a positive integer `x`, raised to the power `n`, modulo `m`.
+ *
+ * Params:
+ * x = base
+ * n = exponent
+ * m = modulus
+ *
+ * Returns:
+ * `x` to the power `n`, modulo `m`.
+ * The return type is the largest of `x`'s and `m`'s type.
+ *
+ * The function requires that all values have unsigned types.
+ */
+Unqual!(Largest!(F, H)) powmod(F, G, H)(F x, G n, H m)
+if (isUnsigned!F && isUnsigned!G && isUnsigned!H)
+{
+ import std.meta : AliasSeq;
+
+ alias T = Unqual!(Largest!(F, H));
+ static if (T.sizeof <= 4)
+ {
+ alias DoubleT = AliasSeq!(void, ushort, uint, void, ulong)[T.sizeof];
+ }
+
+ static T mulmod(T a, T b, T c)
+ {
+ static if (T.sizeof == 8)
+ {
+ static T addmod(T a, T b, T c)
+ {
+ b = c - b;
+ if (a >= b)
+ return a - b;
+ else
+ return c - b + a;
+ }
+
+ T result = 0, tmp;
+
+ b %= c;
+ while (a > 0)
+ {
+ if (a & 1)
+ result = addmod(result, b, c);
+
+ a >>= 1;
+ b = addmod(b, b, c);
+ }
+
+ return result;
+ }
+ else
+ {
+ DoubleT result = cast(DoubleT) (cast(DoubleT) a * cast(DoubleT) b);
+ return result % c;
+ }
+ }
+
+ T base = x, result = 1, modulus = m;
+ Unqual!G exponent = n;
+
+ while (exponent > 0)
+ {
+ if (exponent & 1)
+ result = mulmod(result, base, modulus);
+
+ base = mulmod(base, base, modulus);
+ exponent >>= 1;
+ }
+
+ return result;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(powmod(1U, 10U, 3U) == 1);
+ assert(powmod(3U, 2U, 6U) == 3);
+ assert(powmod(5U, 5U, 15U) == 5);
+ assert(powmod(2U, 3U, 5U) == 3);
+ assert(powmod(2U, 4U, 5U) == 1);
+ assert(powmod(2U, 5U, 5U) == 2);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ ulong a = 18446744073709551615u, b = 20u, c = 18446744073709551610u;
+ assert(powmod(a, b, c) == 95367431640625u);
+ a = 100; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 18223853583554725198u);
+ a = 117; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 11493139548346411394u);
+ a = 134; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 10979163786734356774u);
+ a = 151; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 7023018419737782840u);
+ a = 168; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 58082701842386811u);
+ a = 185; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 17423478386299876798u);
+ a = 202; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 5522733478579799075u);
+ a = 219; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 15230218982491623487u);
+ a = 236; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 5198328724976436000u);
+
+ a = 0; b = 7919; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 0);
+ a = 123; b = 0; c = 18446744073709551557u;
+ assert(powmod(a, b, c) == 1);
+
+ immutable ulong a1 = 253, b1 = 7919, c1 = 18446744073709551557u;
+ assert(powmod(a1, b1, c1) == 3883707345459248860u);
+
+ uint x = 100 ,y = 7919, z = 1844674407u;
+ assert(powmod(x, y, z) == 1613100340u);
+ x = 134; y = 7919; z = 1844674407u;
+ assert(powmod(x, y, z) == 734956622u);
+ x = 151; y = 7919; z = 1844674407u;
+ assert(powmod(x, y, z) == 1738696945u);
+ x = 168; y = 7919; z = 1844674407u;
+ assert(powmod(x, y, z) == 1247580927u);
+ x = 185; y = 7919; z = 1844674407u;
+ assert(powmod(x, y, z) == 1293855176u);
+ x = 202; y = 7919; z = 1844674407u;
+ assert(powmod(x, y, z) == 1566963682u);
+ x = 219; y = 7919; z = 1844674407u;
+ assert(powmod(x, y, z) == 181227807u);
+ x = 236; y = 7919; z = 1844674407u;
+ assert(powmod(x, y, z) == 217988321u);
+ x = 253; y = 7919; z = 1844674407u;
+ assert(powmod(x, y, z) == 1588843243u);
+
+ x = 0; y = 7919; z = 184467u;
+ assert(powmod(x, y, z) == 0);
+ x = 123; y = 0; z = 1844674u;
+ assert(powmod(x, y, z) == 1);
+
+ immutable ubyte x1 = 117;
+ immutable uint y1 = 7919;
+ immutable uint z1 = 1844674407u;
+ auto res = powmod(x1, y1, z1);
+ assert(is(typeof(res) == uint));
+ assert(res == 9479781u);
+
+ immutable ushort x2 = 123;
+ immutable uint y2 = 203;
+ immutable ubyte z2 = 113;
+ auto res2 = powmod(x2, y2, z2);
+ assert(is(typeof(res2) == ushort));
+ assert(res2 == 42u);
+}
+
+/**
+ * Calculates e$(SUPERSCRIPT x).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH e$(SUPERSCRIPT x)) )
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) )
+ * $(TR $(TD -$(INFIN)) $(TD +0.0) )
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) )
+ * )
+ */
+pragma(inline, true)
+real exp(real x) @trusted pure nothrow @nogc // TODO: @safe
+{
+ import std.math.constants : LOG2E;
+
+ version (InlineAsm_X87)
+ {
+ // e^^x = 2^^(LOG2E*x)
+ // (This is valid because the overflow & underflow limits for exp
+ // and exp2 are so similar).
+ if (!__ctfe)
+ return exp2Asm(LOG2E*x);
+ }
+ return expImpl(x);
+}
+
+/// ditto
+pragma(inline, true)
+double exp(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) exp(cast(real) x) : expImpl(x); }
+
+/// ditto
+pragma(inline, true)
+float exp(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) exp(cast(real) x) : expImpl(x); }
+
+///
+@safe unittest
+{
+ import std.math.operations : feqrel;
+ import std.math.constants : E;
+
+ assert(exp(0.0) == 1.0);
+ assert(exp(3.0).feqrel(E * E * E) > 16);
+}
+
+private T expImpl(T)(T x) @safe pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.traits : isNaN;
+ import std.math.rounding : floor;
+ import std.math.algebraic : poly;
+ import std.math.constants : LOG2E;
+
+ alias F = floatTraits!T;
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ static immutable T[6] P = [
+ 5.0000001201E-1,
+ 1.6666665459E-1,
+ 4.1665795894E-2,
+ 8.3334519073E-3,
+ 1.3981999507E-3,
+ 1.9875691500E-4,
+ ];
+
+ enum T C1 = 0.693359375;
+ enum T C2 = -2.12194440e-4;
+
+ // Overflow and Underflow limits.
+ enum T OF = 88.72283905206835;
+ enum T UF = -103.278929903431851103; // ln(2^-149)
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ // Coefficients for exp(x)
+ static immutable T[3] P = [
+ 9.99999999999999999910E-1L,
+ 3.02994407707441961300E-2L,
+ 1.26177193074810590878E-4L,
+ ];
+ static immutable T[4] Q = [
+ 2.00000000000000000009E0L,
+ 2.27265548208155028766E-1L,
+ 2.52448340349684104192E-3L,
+ 3.00198505138664455042E-6L,
+ ];
+
+ // C1 + C2 = LN2.
+ enum T C1 = 6.93145751953125E-1;
+ enum T C2 = 1.42860682030941723212E-6;
+
+ // Overflow and Underflow limits.
+ enum T OF = 7.09782712893383996732E2; // ln((1-2^-53) * 2^1024)
+ enum T UF = -7.451332191019412076235E2; // ln(2^-1075)
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ // Coefficients for exp(x)
+ static immutable T[3] P = [
+ 9.9999999999999999991025E-1L,
+ 3.0299440770744196129956E-2L,
+ 1.2617719307481059087798E-4L,
+ ];
+ static immutable T[4] Q = [
+ 2.0000000000000000000897E0L,
+ 2.2726554820815502876593E-1L,
+ 2.5244834034968410419224E-3L,
+ 3.0019850513866445504159E-6L,
+ ];
+
+ // C1 + C2 = LN2.
+ enum T C1 = 6.9314575195312500000000E-1L;
+ enum T C2 = 1.4286068203094172321215E-6L;
+
+ // Overflow and Underflow limits.
+ enum T OF = 1.1356523406294143949492E4L; // ln((1-2^-64) * 2^16384)
+ enum T UF = -1.13994985314888605586758E4L; // ln(2^-16446)
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ // Coefficients for exp(x) - 1
+ static immutable T[5] P = [
+ 9.999999999999999999999999999999999998502E-1L,
+ 3.508710990737834361215404761139478627390E-2L,
+ 2.708775201978218837374512615596512792224E-4L,
+ 6.141506007208645008909088812338454698548E-7L,
+ 3.279723985560247033712687707263393506266E-10L
+ ];
+ static immutable T[6] Q = [
+ 2.000000000000000000000000000000000000150E0,
+ 2.368408864814233538909747618894558968880E-1L,
+ 3.611828913847589925056132680618007270344E-3L,
+ 1.504792651814944826817779302637284053660E-5L,
+ 1.771372078166251484503904874657985291164E-8L,
+ 2.980756652081995192255342779918052538681E-12L
+ ];
+
+ // C1 + C2 = LN2.
+ enum T C1 = 6.93145751953125E-1L;
+ enum T C2 = 1.428606820309417232121458176568075500134E-6L;
+
+ // Overflow and Underflow limits.
+ enum T OF = 1.135583025911358400418251384584930671458833e4L;
+ enum T UF = -1.143276959615573793352782661133116431383730e4L;
+ }
+ else
+ static assert(0, "Not implemented for this architecture");
+
+ // Special cases.
+ if (isNaN(x))
+ return x;
+ if (x > OF)
+ return real.infinity;
+ if (x < UF)
+ return 0.0;
+
+ // Express: e^^x = e^^g * 2^^n
+ // = e^^g * e^^(n * LOG2E)
+ // = e^^(g + n * LOG2E)
+ T xx = floor((cast(T) LOG2E) * x + cast(T) 0.5);
+ const int n = cast(int) xx;
+ x -= xx * C1;
+ x -= xx * C2;
+
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ xx = x * x;
+ x = poly(x, P) * xx + x + 1.0f;
+ }
+ else
+ {
+ // Rational approximation for exponential of the fractional part:
+ // e^^x = 1 + 2x P(x^^2) / (Q(x^^2) - P(x^^2))
+ xx = x * x;
+ const T px = x * poly(xx, P);
+ x = px / (poly(xx, Q) - px);
+ x = (cast(T) 1.0) + (cast(T) 2.0) * x;
+ }
+
+ // Scale by power of 2.
+ x = core.math.ldexp(x, n);
+
+ return x;
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.operations : NaN, feqrel, isClose;
+ import std.math.constants : E;
+ import std.math.traits : isIdentical;
+ import std.math.algebraic : abs;
+
+ version (IeeeFlagsSupport) import std.math.hardware : IeeeFlags, resetIeeeFlags, ieeeFlags;
+ version (FloatingPointControlSupport)
+ {
+ import std.math.hardware : FloatingPointControl;
+
+ FloatingPointControl ctrl;
+ if (FloatingPointControl.hasExceptionTraps)
+ ctrl.disableExceptions(FloatingPointControl.allExceptions);
+ ctrl.rounding = FloatingPointControl.roundToNearest;
+ }
+
+ static void testExp(T)()
+ {
+ enum realFormat = floatTraits!T.realFormat;
+ static if (realFormat == RealFormat.ieeeQuadruple)
+ {
+ static immutable T[2][] exptestpoints =
+ [ // x exp(x)
+ [ 1.0L, E ],
+ [ 0.5L, 0x1.a61298e1e069bc972dfefab6df34p+0L ],
+ [ 3.0L, E*E*E ],
+ [ 0x1.6p+13L, 0x1.6e509d45728655cdb4840542acb5p+16250L ], // near overflow
+ [ 0x1.7p+13L, T.infinity ], // close overflow
+ [ 0x1p+80L, T.infinity ], // far overflow
+ [ T.infinity, T.infinity ],
+ [-0x1.18p+13L, 0x1.5e4bf54b4807034ea97fef0059a6p-12927L ], // near underflow
+ [-0x1.625p+13L, 0x1.a6bd68a39d11fec3a250cd97f524p-16358L ], // ditto
+ [-0x1.62dafp+13L, 0x0.cb629e9813b80ed4d639e875be6cp-16382L ], // near underflow - subnormal
+ [-0x1.6549p+13L, 0x0.0000000000000000000000000001p-16382L ], // ditto
+ [-0x1.655p+13L, 0 ], // close underflow
+ [-0x1p+30L, 0 ], // far underflow
+ ];
+ }
+ else static if (realFormat == RealFormat.ieeeExtended ||
+ realFormat == RealFormat.ieeeExtended53)
+ {
+ static immutable T[2][] exptestpoints =
+ [ // x exp(x)
+ [ 1.0L, E ],
+ [ 0.5L, 0x1.a61298e1e069bc97p+0L ],
+ [ 3.0L, E*E*E ],
+ [ 0x1.1p+13L, 0x1.29aeffefc8ec645p+12557L ], // near overflow
+ [ 0x1.7p+13L, T.infinity ], // close overflow
+ [ 0x1p+80L, T.infinity ], // far overflow
+ [ T.infinity, T.infinity ],
+ [-0x1.18p+13L, 0x1.5e4bf54b4806db9p-12927L ], // near underflow
+ [-0x1.625p+13L, 0x1.a6bd68a39d11f35cp-16358L ], // ditto
+ [-0x1.62dafp+13L, 0x1.96c53d30277021dp-16383L ], // near underflow - subnormal
+ [-0x1.643p+13L, 0x1p-16444L ], // ditto
+ [-0x1.645p+13L, 0 ], // close underflow
+ [-0x1p+30L, 0 ], // far underflow
+ ];
+ }
+ else static if (realFormat == RealFormat.ieeeDouble)
+ {
+ static immutable T[2][] exptestpoints =
+ [ // x, exp(x)
+ [ 1.0L, E ],
+ [ 0.5L, 0x1.a61298e1e069cp+0L ],
+ [ 3.0L, E*E*E ],
+ [ 0x1.6p+9L, 0x1.93bf4ec282efbp+1015L ], // near overflow
+ [ 0x1.7p+9L, T.infinity ], // close overflow
+ [ 0x1p+80L, T.infinity ], // far overflow
+ [ T.infinity, T.infinity ],
+ [-0x1.6p+9L, 0x1.44a3824e5285fp-1016L ], // near underflow
+ [-0x1.64p+9L, 0x0.06f84920bb2d4p-1022L ], // near underflow - subnormal
+ [-0x1.743p+9L, 0x0.0000000000001p-1022L ], // ditto
+ [-0x1.8p+9L, 0 ], // close underflow
+ [-0x1p+30L, 0 ], // far underflow
+ ];
+ }
+ else static if (realFormat == RealFormat.ieeeSingle)
+ {
+ static immutable T[2][] exptestpoints =
+ [ // x, exp(x)
+ [ 1.0L, E ],
+ [ 0.5L, 0x1.a61299p+0L ],
+ [ 3.0L, E*E*E ],
+ [ 0x1.62p+6L, 0x1.99b988p+127L ], // near overflow
+ [ 0x1.7p+6L, T.infinity ], // close overflow
+ [ 0x1p+80L, T.infinity ], // far overflow
+ [ T.infinity, T.infinity ],
+ [-0x1.5cp+6L, 0x1.666d0ep-126L ], // near underflow
+ [-0x1.7p+6L, 0x0.026a42p-126L ], // near underflow - subnormal
+ [-0x1.9cp+6L, 0x0.000002p-126L ], // ditto
+ [-0x1.ap+6L, 0 ], // close underflow
+ [-0x1p+30L, 0 ], // far underflow
+ ];
+ }
+ else
+ static assert(0, "No exp() tests for real type!");
+
+ const minEqualMantissaBits = T.mant_dig - 13;
+ T x;
+ version (IeeeFlagsSupport) IeeeFlags f;
+ foreach (ref pair; exptestpoints)
+ {
+ version (IeeeFlagsSupport) resetIeeeFlags();
+ x = exp(pair[0]);
+ //printf("exp(%La) = %La, should be %La\n", cast(real) pair[0], cast(real) x, cast(real) pair[1]);
+ assert(feqrel(x, pair[1]) >= minEqualMantissaBits);
+ }
+
+ // Ideally, exp(0) would not set the inexact flag.
+ // Unfortunately, fldl2e sets it!
+ // So it's not realistic to avoid setting it.
+ assert(exp(cast(T) 0.0) == 1.0);
+
+ // NaN propagation. Doesn't set flags, bcos was already NaN.
+ version (IeeeFlagsSupport)
+ {
+ resetIeeeFlags();
+ x = exp(T.nan);
+ f = ieeeFlags;
+ assert(isIdentical(abs(x), T.nan));
+ assert(f.flags == 0);
+
+ resetIeeeFlags();
+ x = exp(-T.nan);
+ f = ieeeFlags;
+ assert(isIdentical(abs(x), T.nan));
+ assert(f.flags == 0);
+ }
+ else
+ {
+ x = exp(T.nan);
+ assert(isIdentical(abs(x), T.nan));
+
+ x = exp(-T.nan);
+ assert(isIdentical(abs(x), T.nan));
+ }
+
+ x = exp(NaN(0x123));
+ assert(isIdentical(x, NaN(0x123)));
+ }
+
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(real, double, float))
+ testExp!T();
+
+ // High resolution test (verified against GNU MPFR/Mathematica).
+ assert(exp(0.5L) == 0x1.A612_98E1_E069_BC97_2DFE_FAB6_DF34p+0L);
+
+ assert(isClose(exp(3.0L), E * E * E, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+/**
+ * Calculates the value of the natural logarithm base (e)
+ * raised to the power of x, minus 1.
+ *
+ * For very small x, expm1(x) is more accurate
+ * than exp(x)-1.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH e$(SUPERSCRIPT x)-1) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) )
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) )
+ * $(TR $(TD -$(INFIN)) $(TD -1.0) )
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) )
+ * )
+ */
+pragma(inline, true)
+real expm1(real x) @trusted pure nothrow @nogc // TODO: @safe
+{
+ version (InlineAsm_X87)
+ {
+ if (!__ctfe)
+ return expm1Asm(x);
+ }
+ return expm1Impl(x);
+}
+
+/// ditto
+pragma(inline, true)
+double expm1(double x) @safe pure nothrow @nogc
+{
+ return __ctfe ? cast(double) expm1(cast(real) x) : expm1Impl(x);
+}
+
+/// ditto
+pragma(inline, true)
+float expm1(float x) @safe pure nothrow @nogc
+{
+ // no single-precision version in Cephes => use double precision
+ return __ctfe ? cast(float) expm1(cast(real) x) : cast(float) expm1Impl(cast(double) x);
+}
+
+///
+@safe unittest
+{
+ import std.math.traits : isIdentical;
+ import std.math.operations : feqrel;
+
+ assert(isIdentical(expm1(0.0), 0.0));
+ assert(expm1(1.0).feqrel(1.71828) > 16);
+ assert(expm1(2.0).feqrel(6.3890) > 16);
+}
+
+version (InlineAsm_X87)
+private real expm1Asm(real x) @trusted pure nothrow @nogc
+{
+ version (X86)
+ {
+ enum PARAMSIZE = (real.sizeof+3)&(0xFFFF_FFFC); // always a multiple of 4
+ asm pure nothrow @nogc
+ {
+ /* expm1() for x87 80-bit reals, IEEE754-2008 conformant.
+ * Author: Don Clugston.
+ *
+ * expm1(x) = 2^^(rndint(y))* 2^^(y-rndint(y)) - 1 where y = LN2*x.
+ * = 2rndy * 2ym1 + 2rndy - 1, where 2rndy = 2^^(rndint(y))
+ * and 2ym1 = (2^^(y-rndint(y))-1).
+ * If 2rndy < 0.5*real.epsilon, result is -1.
+ * Implementation is otherwise the same as for exp2()
+ */
+ naked;
+ fld real ptr [ESP+4] ; // x
+ mov AX, [ESP+4+8]; // AX = exponent and sign
+ sub ESP, 12+8; // Create scratch space on the stack
+ // [ESP,ESP+2] = scratchint
+ // [ESP+4..+6, +8..+10, +10] = scratchreal
+ // set scratchreal mantissa = 1.0
+ mov dword ptr [ESP+8], 0;
+ mov dword ptr [ESP+8+4], 0x80000000;
+ and AX, 0x7FFF; // drop sign bit
+ cmp AX, 0x401D; // avoid InvalidException in fist
+ jae L_extreme;
+ fldl2e;
+ fmulp ST(1), ST; // y = x*log2(e)
+ fist dword ptr [ESP]; // scratchint = rndint(y)
+ fisub dword ptr [ESP]; // y - rndint(y)
+ // and now set scratchreal exponent
+ mov EAX, [ESP];
+ add EAX, 0x3fff;
+ jle short L_largenegative;
+ cmp EAX,0x8000;
+ jge short L_largepositive;
+ mov [ESP+8+8],AX;
+ f2xm1; // 2ym1 = 2^^(y-rndint(y)) -1
+ fld real ptr [ESP+8] ; // 2rndy = 2^^rndint(y)
+ fmul ST(1), ST; // ST=2rndy, ST(1)=2rndy*2ym1
+ fld1;
+ fsubp ST(1), ST; // ST = 2rndy-1, ST(1) = 2rndy * 2ym1 - 1
+ faddp ST(1), ST; // ST = 2rndy * 2ym1 + 2rndy - 1
+ add ESP,12+8;
+ ret PARAMSIZE;
+
+L_extreme: // Extreme exponent. X is very large positive, very
+ // large negative, infinity, or NaN.
+ fxam;
+ fstsw AX;
+ test AX, 0x0400; // NaN_or_zero, but we already know x != 0
+ jz L_was_nan; // if x is NaN, returns x
+ test AX, 0x0200;
+ jnz L_largenegative;
+L_largepositive:
+ // Set scratchreal = real.max.
+ // squaring it will create infinity, and set overflow flag.
+ mov word ptr [ESP+8+8], 0x7FFE;
+ fstp ST(0);
+ fld real ptr [ESP+8]; // load scratchreal
+ fmul ST(0), ST; // square it, to create havoc!
+L_was_nan:
+ add ESP,12+8;
+ ret PARAMSIZE;
+L_largenegative:
+ fstp ST(0);
+ fld1;
+ fchs; // return -1. Underflow flag is not set.
+ add ESP,12+8;
+ ret PARAMSIZE;
+ }
+ }
+ else version (X86_64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked;
+ }
+ version (Win64)
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RCX]; // x
+ mov AX,[RCX+8]; // AX = exponent and sign
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RSP+8]; // x
+ mov AX,[RSP+8+8]; // AX = exponent and sign
+ }
+ }
+ asm pure nothrow @nogc
+ {
+ /* expm1() for x87 80-bit reals, IEEE754-2008 conformant.
+ * Author: Don Clugston.
+ *
+ * expm1(x) = 2^(rndint(y))* 2^(y-rndint(y)) - 1 where y = LN2*x.
+ * = 2rndy * 2ym1 + 2rndy - 1, where 2rndy = 2^(rndint(y))
+ * and 2ym1 = (2^(y-rndint(y))-1).
+ * If 2rndy < 0.5*real.epsilon, result is -1.
+ * Implementation is otherwise the same as for exp2()
+ */
+ sub RSP, 24; // Create scratch space on the stack
+ // [RSP,RSP+2] = scratchint
+ // [RSP+4..+6, +8..+10, +10] = scratchreal
+ // set scratchreal mantissa = 1.0
+ mov dword ptr [RSP+8], 0;
+ mov dword ptr [RSP+8+4], 0x80000000;
+ and AX, 0x7FFF; // drop sign bit
+ cmp AX, 0x401D; // avoid InvalidException in fist
+ jae L_extreme;
+ fldl2e;
+ fmul ; // y = x*log2(e)
+ fist dword ptr [RSP]; // scratchint = rndint(y)
+ fisub dword ptr [RSP]; // y - rndint(y)
+ // and now set scratchreal exponent
+ mov EAX, [RSP];
+ add EAX, 0x3fff;
+ jle short L_largenegative;
+ cmp EAX,0x8000;
+ jge short L_largepositive;
+ mov [RSP+8+8],AX;
+ f2xm1; // 2^(y-rndint(y)) -1
+ fld real ptr [RSP+8] ; // 2^rndint(y)
+ fmul ST(1), ST;
+ fld1;
+ fsubp ST(1), ST;
+ fadd;
+ add RSP,24;
+ ret;
+
+L_extreme: // Extreme exponent. X is very large positive, very
+ // large negative, infinity, or NaN.
+ fxam;
+ fstsw AX;
+ test AX, 0x0400; // NaN_or_zero, but we already know x != 0
+ jz L_was_nan; // if x is NaN, returns x
+ test AX, 0x0200;
+ jnz L_largenegative;
+L_largepositive:
+ // Set scratchreal = real.max.
+ // squaring it will create infinity, and set overflow flag.
+ mov word ptr [RSP+8+8], 0x7FFE;
+ fstp ST(0);
+ fld real ptr [RSP+8]; // load scratchreal
+ fmul ST(0), ST; // square it, to create havoc!
+L_was_nan:
+ add RSP,24;
+ ret;
+
+L_largenegative:
+ fstp ST(0);
+ fld1;
+ fchs; // return -1. Underflow flag is not set.
+ add RSP,24;
+ ret;
+ }
+ }
+ else
+ static assert(0);
+}
+
+private T expm1Impl(T)(T x) @safe pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.rounding : floor;
+ import std.math.algebraic : poly;
+ import std.math.constants : LN2;
+
+ // Coefficients for exp(x) - 1 and overflow/underflow limits.
+ enum realFormat = floatTraits!T.realFormat;
+ static if (realFormat == RealFormat.ieeeQuadruple)
+ {
+ static immutable T[8] P = [
+ 2.943520915569954073888921213330863757240E8L,
+ -5.722847283900608941516165725053359168840E7L,
+ 8.944630806357575461578107295909719817253E6L,
+ -7.212432713558031519943281748462837065308E5L,
+ 4.578962475841642634225390068461943438441E4L,
+ -1.716772506388927649032068540558788106762E3L,
+ 4.401308817383362136048032038528753151144E1L,
+ -4.888737542888633647784737721812546636240E-1L
+ ];
+
+ static immutable T[9] Q = [
+ 1.766112549341972444333352727998584753865E9L,
+ -7.848989743695296475743081255027098295771E8L,
+ 1.615869009634292424463780387327037251069E8L,
+ -2.019684072836541751428967854947019415698E7L,
+ 1.682912729190313538934190635536631941751E6L,
+ -9.615511549171441430850103489315371768998E4L,
+ 3.697714952261803935521187272204485251835E3L,
+ -8.802340681794263968892934703309274564037E1L,
+ 1.0
+ ];
+
+ enum T OF = 1.1356523406294143949491931077970764891253E4L;
+ enum T UF = -1.143276959615573793352782661133116431383730e4L;
+ }
+ else static if (realFormat == RealFormat.ieeeExtended)
+ {
+ static immutable T[5] P = [
+ -1.586135578666346600772998894928250240826E4L,
+ 2.642771505685952966904660652518429479531E3L,
+ -3.423199068835684263987132888286791620673E2L,
+ 1.800826371455042224581246202420972737840E1L,
+ -5.238523121205561042771939008061958820811E-1L,
+ ];
+ static immutable T[6] Q = [
+ -9.516813471998079611319047060563358064497E4L,
+ 3.964866271411091674556850458227710004570E4L,
+ -7.207678383830091850230366618190187434796E3L,
+ 7.206038318724600171970199625081491823079E2L,
+ -4.002027679107076077238836622982900945173E1L,
+ 1.0
+ ];
+
+ enum T OF = 1.1356523406294143949492E4L;
+ enum T UF = -4.5054566736396445112120088E1L;
+ }
+ else static if (realFormat == RealFormat.ieeeDouble)
+ {
+ static immutable T[3] P = [
+ 9.9999999999999999991025E-1,
+ 3.0299440770744196129956E-2,
+ 1.2617719307481059087798E-4,
+ ];
+ static immutable T[4] Q = [
+ 2.0000000000000000000897E0,
+ 2.2726554820815502876593E-1,
+ 2.5244834034968410419224E-3,
+ 3.0019850513866445504159E-6,
+ ];
+ }
+ else
+ static assert(0, "no coefficients for expm1()");
+
+ static if (realFormat == RealFormat.ieeeDouble) // special case for double precision
+ {
+ if (x < -0.5 || x > 0.5)
+ return exp(x) - 1.0;
+ if (x == 0.0)
+ return x;
+
+ const T xx = x * x;
+ x = x * poly(xx, P);
+ x = x / (poly(xx, Q) - x);
+ return x + x;
+ }
+ else
+ {
+ // C1 + C2 = LN2.
+ enum T C1 = 6.9314575195312500000000E-1L;
+ enum T C2 = 1.428606820309417232121458176568075500134E-6L;
+
+ // Special cases.
+ if (x > OF)
+ return real.infinity;
+ if (x == cast(T) 0.0)
+ return x;
+ if (x < UF)
+ return -1.0;
+
+ // Express x = LN2 (n + remainder), remainder not exceeding 1/2.
+ int n = cast(int) floor((cast(T) 0.5) + x / cast(T) LN2);
+ x -= n * C1;
+ x -= n * C2;
+
+ // Rational approximation:
+ // exp(x) - 1 = x + 0.5 x^^2 + x^^3 P(x) / Q(x)
+ T px = x * poly(x, P);
+ T qx = poly(x, Q);
+ const T xx = x * x;
+ qx = x + ((cast(T) 0.5) * xx + xx * px / qx);
+
+ // We have qx = exp(remainder LN2) - 1, so:
+ // exp(x) - 1 = 2^^n (qx + 1) - 1 = 2^^n qx + 2^^n - 1.
+ px = core.math.ldexp(cast(T) 1.0, n);
+ x = px * qx + (px - cast(T) 1.0);
+
+ return x;
+ }
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.traits : isNaN;
+ import std.math.operations : isClose, CommonDefaultFor;
+
+ static void testExpm1(T)()
+ {
+ // NaN
+ assert(isNaN(expm1(cast(T) T.nan)));
+
+ static immutable T[] xs = [ -2, -0.75, -0.3, 0.0, 0.1, 0.2, 0.5, 1.0 ];
+ foreach (x; xs)
+ {
+ const T e = expm1(x);
+ const T r = exp(x) - 1;
+
+ //printf("expm1(%Lg) = %Lg, should approximately be %Lg\n", cast(real) x, cast(real) e, cast(real) r);
+ assert(isClose(r, e, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T)));
+ }
+ }
+
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(real, double))
+ testExpm1!T();
+}
+
+/**
+ * Calculates 2$(SUPERSCRIPT x).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH exp2(x)) )
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) )
+ * $(TR $(TD -$(INFIN)) $(TD +0.0) )
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) )
+ * )
+ */
+pragma(inline, true)
+real exp2(real x) @nogc @trusted pure nothrow // TODO: @safe
+{
+ version (InlineAsm_X87)
+ {
+ if (!__ctfe)
+ return exp2Asm(x);
+ }
+ return exp2Impl(x);
+}
+
+/// ditto
+pragma(inline, true)
+double exp2(double x) @nogc @safe pure nothrow { return __ctfe ? cast(double) exp2(cast(real) x) : exp2Impl(x); }
+
+/// ditto
+pragma(inline, true)
+float exp2(float x) @nogc @safe pure nothrow { return __ctfe ? cast(float) exp2(cast(real) x) : exp2Impl(x); }
+
+///
+@safe unittest
+{
+ import std.math.traits : isIdentical;
+ import std.math.operations : feqrel;
+
+ assert(isIdentical(exp2(0.0), 1.0));
+ assert(exp2(2.0).feqrel(4.0) > 16);
+ assert(exp2(8.0).feqrel(256.0) > 16);
+}
+
+@safe unittest
+{
+ version (CRuntime_Microsoft) {} else // aexp2/exp2f/exp2l not implemented
+ {
+ assert( core.stdc.math.exp2f(0.0f) == 1 );
+ assert( core.stdc.math.exp2 (0.0) == 1 );
+ assert( core.stdc.math.exp2l(0.0L) == 1 );
+ }
+}
+
+version (InlineAsm_X87)
+private real exp2Asm(real x) @nogc @trusted pure nothrow
+{
+ version (X86)
+ {
+ enum PARAMSIZE = (real.sizeof+3)&(0xFFFF_FFFC); // always a multiple of 4
+
+ asm pure nothrow @nogc
+ {
+ /* exp2() for x87 80-bit reals, IEEE754-2008 conformant.
+ * Author: Don Clugston.
+ *
+ * exp2(x) = 2^^(rndint(x))* 2^^(y-rndint(x))
+ * The trick for high performance is to avoid the fscale(28cycles on core2),
+ * frndint(19 cycles), leaving f2xm1(19 cycles) as the only slow instruction.
+ *
+ * We can do frndint by using fist. BUT we can't use it for huge numbers,
+ * because it will set the Invalid Operation flag if overflow or NaN occurs.
+ * Fortunately, whenever this happens the result would be zero or infinity.
+ *
+ * We can perform fscale by directly poking into the exponent. BUT this doesn't
+ * work for the (very rare) cases where the result is subnormal. So we fall back
+ * to the slow method in that case.
+ */
+ naked;
+ fld real ptr [ESP+4] ; // x
+ mov AX, [ESP+4+8]; // AX = exponent and sign
+ sub ESP, 12+8; // Create scratch space on the stack
+ // [ESP,ESP+2] = scratchint
+ // [ESP+4..+6, +8..+10, +10] = scratchreal
+ // set scratchreal mantissa = 1.0
+ mov dword ptr [ESP+8], 0;
+ mov dword ptr [ESP+8+4], 0x80000000;
+ and AX, 0x7FFF; // drop sign bit
+ cmp AX, 0x401D; // avoid InvalidException in fist
+ jae L_extreme;
+ fist dword ptr [ESP]; // scratchint = rndint(x)
+ fisub dword ptr [ESP]; // x - rndint(x)
+ // and now set scratchreal exponent
+ mov EAX, [ESP];
+ add EAX, 0x3fff;
+ jle short L_subnormal;
+ cmp EAX,0x8000;
+ jge short L_overflow;
+ mov [ESP+8+8],AX;
+L_normal:
+ f2xm1;
+ fld1;
+ faddp ST(1), ST; // 2^^(x-rndint(x))
+ fld real ptr [ESP+8] ; // 2^^rndint(x)
+ add ESP,12+8;
+ fmulp ST(1), ST;
+ ret PARAMSIZE;
+
+L_subnormal:
+ // Result will be subnormal.
+ // In this rare case, the simple poking method doesn't work.
+ // The speed doesn't matter, so use the slow fscale method.
+ fild dword ptr [ESP]; // scratchint
+ fld1;
+ fscale;
+ fstp real ptr [ESP+8]; // scratchreal = 2^^scratchint
+ fstp ST(0); // drop scratchint
+ jmp L_normal;
+
+L_extreme: // Extreme exponent. X is very large positive, very
+ // large negative, infinity, or NaN.
+ fxam;
+ fstsw AX;
+ test AX, 0x0400; // NaN_or_zero, but we already know x != 0
+ jz L_was_nan; // if x is NaN, returns x
+ // set scratchreal = real.min_normal
+ // squaring it will return 0, setting underflow flag
+ mov word ptr [ESP+8+8], 1;
+ test AX, 0x0200;
+ jnz L_waslargenegative;
+L_overflow:
+ // Set scratchreal = real.max.
+ // squaring it will create infinity, and set overflow flag.
+ mov word ptr [ESP+8+8], 0x7FFE;
+L_waslargenegative:
+ fstp ST(0);
+ fld real ptr [ESP+8]; // load scratchreal
+ fmul ST(0), ST; // square it, to create havoc!
+L_was_nan:
+ add ESP,12+8;
+ ret PARAMSIZE;
+ }
+ }
+ else version (X86_64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked;
+ }
+ version (Win64)
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RCX]; // x
+ mov AX,[RCX+8]; // AX = exponent and sign
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RSP+8]; // x
+ mov AX,[RSP+8+8]; // AX = exponent and sign
+ }
+ }
+ asm pure nothrow @nogc
+ {
+ /* exp2() for x87 80-bit reals, IEEE754-2008 conformant.
+ * Author: Don Clugston.
+ *
+ * exp2(x) = 2^(rndint(x))* 2^(y-rndint(x))
+ * The trick for high performance is to avoid the fscale(28cycles on core2),
+ * frndint(19 cycles), leaving f2xm1(19 cycles) as the only slow instruction.
+ *
+ * We can do frndint by using fist. BUT we can't use it for huge numbers,
+ * because it will set the Invalid Operation flag is overflow or NaN occurs.
+ * Fortunately, whenever this happens the result would be zero or infinity.
+ *
+ * We can perform fscale by directly poking into the exponent. BUT this doesn't
+ * work for the (very rare) cases where the result is subnormal. So we fall back
+ * to the slow method in that case.
+ */
+ sub RSP, 24; // Create scratch space on the stack
+ // [RSP,RSP+2] = scratchint
+ // [RSP+4..+6, +8..+10, +10] = scratchreal
+ // set scratchreal mantissa = 1.0
+ mov dword ptr [RSP+8], 0;
+ mov dword ptr [RSP+8+4], 0x80000000;
+ and AX, 0x7FFF; // drop sign bit
+ cmp AX, 0x401D; // avoid InvalidException in fist
+ jae L_extreme;
+ fist dword ptr [RSP]; // scratchint = rndint(x)
+ fisub dword ptr [RSP]; // x - rndint(x)
+ // and now set scratchreal exponent
+ mov EAX, [RSP];
+ add EAX, 0x3fff;
+ jle short L_subnormal;
+ cmp EAX,0x8000;
+ jge short L_overflow;
+ mov [RSP+8+8],AX;
+L_normal:
+ f2xm1;
+ fld1;
+ fadd; // 2^(x-rndint(x))
+ fld real ptr [RSP+8] ; // 2^rndint(x)
+ add RSP,24;
+ fmulp ST(1), ST;
+ ret;
+
+L_subnormal:
+ // Result will be subnormal.
+ // In this rare case, the simple poking method doesn't work.
+ // The speed doesn't matter, so use the slow fscale method.
+ fild dword ptr [RSP]; // scratchint
+ fld1;
+ fscale;
+ fstp real ptr [RSP+8]; // scratchreal = 2^scratchint
+ fstp ST(0); // drop scratchint
+ jmp L_normal;
+
+L_extreme: // Extreme exponent. X is very large positive, very
+ // large negative, infinity, or NaN.
+ fxam;
+ fstsw AX;
+ test AX, 0x0400; // NaN_or_zero, but we already know x != 0
+ jz L_was_nan; // if x is NaN, returns x
+ // set scratchreal = real.min
+ // squaring it will return 0, setting underflow flag
+ mov word ptr [RSP+8+8], 1;
+ test AX, 0x0200;
+ jnz L_waslargenegative;
+L_overflow:
+ // Set scratchreal = real.max.
+ // squaring it will create infinity, and set overflow flag.
+ mov word ptr [RSP+8+8], 0x7FFE;
+L_waslargenegative:
+ fstp ST(0);
+ fld real ptr [RSP+8]; // load scratchreal
+ fmul ST(0), ST; // square it, to create havoc!
+L_was_nan:
+ add RSP,24;
+ ret;
+ }
+ }
+ else
+ static assert(0);
+}
+
+private T exp2Impl(T)(T x) @nogc @safe pure nothrow
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.traits : isNaN;
+ import std.math.rounding : floor;
+ import std.math.algebraic : poly;
+
+ // Coefficients for exp2(x)
+ enum realFormat = floatTraits!T.realFormat;
+ static if (realFormat == RealFormat.ieeeQuadruple)
+ {
+ static immutable T[5] P = [
+ 9.079594442980146270952372234833529694788E12L,
+ 1.530625323728429161131811299626419117557E11L,
+ 5.677513871931844661829755443994214173883E8L,
+ 6.185032670011643762127954396427045467506E5L,
+ 1.587171580015525194694938306936721666031E2L
+ ];
+
+ static immutable T[6] Q = [
+ 2.619817175234089411411070339065679229869E13L,
+ 1.490560994263653042761789432690793026977E12L,
+ 1.092141473886177435056423606755843616331E10L,
+ 2.186249607051644894762167991800811827835E7L,
+ 1.236602014442099053716561665053645270207E4L,
+ 1.0
+ ];
+ }
+ else static if (realFormat == RealFormat.ieeeExtended)
+ {
+ static immutable T[3] P = [
+ 2.0803843631901852422887E6L,
+ 3.0286971917562792508623E4L,
+ 6.0614853552242266094567E1L,
+ ];
+ static immutable T[4] Q = [
+ 6.0027204078348487957118E6L,
+ 3.2772515434906797273099E5L,
+ 1.7492876999891839021063E3L,
+ 1.0000000000000000000000E0L,
+ ];
+ }
+ else static if (realFormat == RealFormat.ieeeDouble)
+ {
+ static immutable T[3] P = [
+ 1.51390680115615096133E3L,
+ 2.02020656693165307700E1L,
+ 2.30933477057345225087E-2L,
+ ];
+ static immutable T[3] Q = [
+ 4.36821166879210612817E3L,
+ 2.33184211722314911771E2L,
+ 1.00000000000000000000E0L,
+ ];
+ }
+ else static if (realFormat == RealFormat.ieeeSingle)
+ {
+ static immutable T[6] P = [
+ 6.931472028550421E-001L,
+ 2.402264791363012E-001L,
+ 5.550332471162809E-002L,
+ 9.618437357674640E-003L,
+ 1.339887440266574E-003L,
+ 1.535336188319500E-004L,
+ ];
+ }
+ else
+ static assert(0, "no coefficients for exp2()");
+
+ // Overflow and Underflow limits.
+ enum T OF = T.max_exp;
+ enum T UF = T.min_exp - 1;
+
+ // Special cases.
+ if (isNaN(x))
+ return x;
+ if (x > OF)
+ return real.infinity;
+ if (x < UF)
+ return 0.0;
+
+ static if (realFormat == RealFormat.ieeeSingle) // special case for single precision
+ {
+ // The following is necessary because range reduction blows up.
+ if (x == 0.0f)
+ return 1.0f;
+
+ // Separate into integer and fractional parts.
+ const T i = floor(x);
+ int n = cast(int) i;
+ x -= i;
+ if (x > 0.5f)
+ {
+ n += 1;
+ x -= 1.0f;
+ }
+
+ // Rational approximation:
+ // exp2(x) = 1.0 + x P(x)
+ x = 1.0f + x * poly(x, P);
+ }
+ else
+ {
+ // Separate into integer and fractional parts.
+ const T i = floor(x + cast(T) 0.5);
+ int n = cast(int) i;
+ x -= i;
+
+ // Rational approximation:
+ // exp2(x) = 1.0 + 2x P(x^^2) / (Q(x^^2) - P(x^^2))
+ const T xx = x * x;
+ const T px = x * poly(xx, P);
+ x = px / (poly(xx, Q) - px);
+ x = (cast(T) 1.0) + (cast(T) 2.0) * x;
+ }
+
+ // Scale by power of 2.
+ x = core.math.ldexp(x, n);
+
+ return x;
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : feqrel, NaN, isClose;
+ import std.math.traits : isIdentical;
+ import std.math.constants : SQRT2;
+
+ assert(feqrel(exp2(0.5L), SQRT2) >= real.mant_dig -1);
+ assert(exp2(8.0L) == 256.0);
+ assert(exp2(-9.0L)== 1.0L/512.0);
+
+ static void testExp2(T)()
+ {
+ // NaN
+ const T specialNaN = NaN(0x0123L);
+ assert(isIdentical(exp2(specialNaN), specialNaN));
+
+ // over-/underflow
+ enum T OF = T.max_exp;
+ enum T UF = T.min_exp - T.mant_dig;
+ assert(isIdentical(exp2(OF + 1), cast(T) T.infinity));
+ assert(isIdentical(exp2(UF - 1), cast(T) 0.0));
+
+ static immutable T[2][] vals =
+ [
+ // x, exp2(x)
+ [ 0.0, 1.0 ],
+ [ -0.0, 1.0 ],
+ [ 0.5, SQRT2 ],
+ [ 8.0, 256.0 ],
+ [ -9.0, 1.0 / 512 ],
+ ];
+
+ foreach (ref val; vals)
+ {
+ const T x = val[0];
+ const T r = val[1];
+ const T e = exp2(x);
+
+ //printf("exp2(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) e, cast(real) r);
+ assert(isClose(r, e));
+ }
+ }
+
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(real, double, float))
+ testExp2!T();
+}
+
+/*********************************************************************
+ * Separate floating point value into significand and exponent.
+ *
+ * Returns:
+ * Calculate and return $(I x) and $(I exp) such that
+ * value =$(I x)*2$(SUPERSCRIPT exp) and
+ * .5 $(LT)= |$(I x)| $(LT) 1.0
+ *
+ * $(I x) has same sign as value.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH value) $(TH returns) $(TH exp))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD 0))
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD int.max))
+ * $(TR $(TD -$(INFIN)) $(TD -$(INFIN)) $(TD int.min))
+ * $(TR $(TD $(PLUSMN)$(NAN)) $(TD $(PLUSMN)$(NAN)) $(TD int.min))
+ * )
+ */
+T frexp(T)(const T value, out int exp) @trusted pure nothrow @nogc
+if (isFloatingPoint!T)
+{
+ import std.math : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB;
+ import std.math.traits : isSubnormal;
+
+ if (__ctfe)
+ {
+ // Handle special cases.
+ if (value == 0) { exp = 0; return value; }
+ if (value == T.infinity) { exp = int.max; return value; }
+ if (value == -T.infinity || value != value) { exp = int.min; return value; }
+ // Handle ordinary cases.
+ // In CTFE there is no performance advantage for having separate
+ // paths for different floating point types.
+ T absValue = value < 0 ? -value : value;
+ int expCount;
+ static if (T.mant_dig > double.mant_dig)
+ {
+ for (; absValue >= 0x1.0p+1024L; absValue *= 0x1.0p-1024L)
+ expCount += 1024;
+ for (; absValue < 0x1.0p-1021L; absValue *= 0x1.0p+1021L)
+ expCount -= 1021;
+ }
+ const double dval = cast(double) absValue;
+ int dexp = cast(int) (((*cast(const long*) &dval) >>> 52) & 0x7FF) + double.min_exp - 2;
+ dexp++;
+ expCount += dexp;
+ absValue *= 2.0 ^^ -dexp;
+ // If the original value was subnormal or if it was a real
+ // then absValue can still be outside the [0.5, 1.0) range.
+ if (absValue < 0.5)
+ {
+ assert(T.mant_dig > double.mant_dig || isSubnormal(value));
+ do
+ {
+ absValue += absValue;
+ expCount--;
+ } while (absValue < 0.5);
+ }
+ else
+ {
+ assert(absValue < 1 || T.mant_dig > double.mant_dig);
+ for (; absValue >= 1; absValue *= T(0.5))
+ expCount++;
+ }
+ exp = expCount;
+ return value < 0 ? -absValue : absValue;
+ }
+
+ Unqual!T vf = value;
+ ushort* vu = cast(ushort*)&vf;
+ static if (is(immutable T == immutable float))
+ int* vi = cast(int*)&vf;
+ else
+ long* vl = cast(long*)&vf;
+ int ex;
+ alias F = floatTraits!T;
+
+ ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ if (ex)
+ { // If exponent is non-zero
+ if (ex == F.EXPMASK) // infinity or NaN
+ {
+ if (*vl & 0x7FFF_FFFF_FFFF_FFFF) // NaN
+ {
+ *vl |= 0xC000_0000_0000_0000; // convert NaNS to NaNQ
+ exp = int.min;
+ }
+ else if (vu[F.EXPPOS_SHORT] & 0x8000) // negative infinity
+ exp = int.min;
+ else // positive infinity
+ exp = int.max;
+
+ }
+ else
+ {
+ exp = ex - F.EXPBIAS;
+ vu[F.EXPPOS_SHORT] = (0x8000 & vu[F.EXPPOS_SHORT]) | 0x3FFE;
+ }
+ }
+ else if (!*vl)
+ {
+ // vf is +-0.0
+ exp = 0;
+ }
+ else
+ {
+ // subnormal
+
+ vf *= F.RECIP_EPSILON;
+ ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ exp = ex - F.EXPBIAS - T.mant_dig + 1;
+ vu[F.EXPPOS_SHORT] = ((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3FFE;
+ }
+ return vf;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ if (ex == F.EXPMASK)
+ {
+ // infinity or NaN
+ if (vl[MANTISSA_LSB] |
+ (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN
+ {
+ // convert NaNS to NaNQ
+ vl[MANTISSA_MSB] |= 0x0000_8000_0000_0000;
+ exp = int.min;
+ }
+ else if (vu[F.EXPPOS_SHORT] & 0x8000) // negative infinity
+ exp = int.min;
+ else // positive infinity
+ exp = int.max;
+ }
+ else
+ {
+ exp = ex - F.EXPBIAS;
+ vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]);
+ }
+ }
+ else if ((vl[MANTISSA_LSB] |
+ (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0)
+ {
+ // vf is +-0.0
+ exp = 0;
+ }
+ else
+ {
+ // subnormal
+ vf *= F.RECIP_EPSILON;
+ ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ exp = ex - F.EXPBIAS - T.mant_dig + 1;
+ vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]);
+ }
+ return vf;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ if (ex == F.EXPMASK) // infinity or NaN
+ {
+ if (*vl == 0x7FF0_0000_0000_0000) // positive infinity
+ {
+ exp = int.max;
+ }
+ else if (*vl == 0xFFF0_0000_0000_0000) // negative infinity
+ exp = int.min;
+ else
+ { // NaN
+ *vl |= 0x0008_0000_0000_0000; // convert NaNS to NaNQ
+ exp = int.min;
+ }
+ }
+ else
+ {
+ exp = (ex - F.EXPBIAS) >> 4;
+ vu[F.EXPPOS_SHORT] = cast(ushort)((0x800F & vu[F.EXPPOS_SHORT]) | 0x3FE0);
+ }
+ }
+ else if (!(*vl & 0x7FFF_FFFF_FFFF_FFFF))
+ {
+ // vf is +-0.0
+ exp = 0;
+ }
+ else
+ {
+ // subnormal
+ vf *= F.RECIP_EPSILON;
+ ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ exp = ((ex - F.EXPBIAS) >> 4) - T.mant_dig + 1;
+ vu[F.EXPPOS_SHORT] =
+ cast(ushort)(((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3FE0);
+ }
+ return vf;
+ }
+ else static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ if (ex == F.EXPMASK) // infinity or NaN
+ {
+ if (*vi == 0x7F80_0000) // positive infinity
+ {
+ exp = int.max;
+ }
+ else if (*vi == 0xFF80_0000) // negative infinity
+ exp = int.min;
+ else
+ { // NaN
+ *vi |= 0x0040_0000; // convert NaNS to NaNQ
+ exp = int.min;
+ }
+ }
+ else
+ {
+ exp = (ex - F.EXPBIAS) >> 7;
+ vu[F.EXPPOS_SHORT] = cast(ushort)((0x807F & vu[F.EXPPOS_SHORT]) | 0x3F00);
+ }
+ }
+ else if (!(*vi & 0x7FFF_FFFF))
+ {
+ // vf is +-0.0
+ exp = 0;
+ }
+ else
+ {
+ // subnormal
+ vf *= F.RECIP_EPSILON;
+ ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ exp = ((ex - F.EXPBIAS) >> 7) - T.mant_dig + 1;
+ vu[F.EXPPOS_SHORT] =
+ cast(ushort)(((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3F00);
+ }
+ return vf;
+ }
+ else // static if (F.realFormat == RealFormat.ibmExtended)
+ {
+ assert(0, "frexp not implemented");
+ }
+}
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+
+ int exp;
+ real mantissa = frexp(123.456L, exp);
+
+ assert(isClose(mantissa * pow(2.0L, cast(real) exp), 123.456L));
+
+ assert(frexp(-real.nan, exp) && exp == int.min);
+ assert(frexp(real.nan, exp) && exp == int.min);
+ assert(frexp(-real.infinity, exp) == -real.infinity && exp == int.min);
+ assert(frexp(real.infinity, exp) == real.infinity && exp == int.max);
+ assert(frexp(-0.0, exp) == -0.0 && exp == 0);
+ assert(frexp(0.0, exp) == 0.0 && exp == 0);
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+
+ int exp;
+ real mantissa = frexp(123.456L, exp);
+
+ // check if values are equal to 19 decimal digits of precision
+ assert(isClose(mantissa * pow(2.0L, cast(real) exp), 123.456L, 1e-18));
+}
+
+@safe unittest
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.traits : isIdentical;
+ import std.meta : AliasSeq;
+ import std.typecons : tuple, Tuple;
+
+ static foreach (T; AliasSeq!(real, double, float))
+ {{
+ Tuple!(T, T, int)[] vals = [ // x,frexp,exp
+ tuple(T(0.0), T( 0.0 ), 0),
+ tuple(T(-0.0), T( -0.0), 0),
+ tuple(T(1.0), T( .5 ), 1),
+ tuple(T(-1.0), T( -.5 ), 1),
+ tuple(T(2.0), T( .5 ), 2),
+ tuple(T(float.min_normal/2.0f), T(.5), -126),
+ tuple(T.infinity, T.infinity, int.max),
+ tuple(-T.infinity, -T.infinity, int.min),
+ tuple(T.nan, T.nan, int.min),
+ tuple(-T.nan, -T.nan, int.min),
+
+ // https://issues.dlang.org/show_bug.cgi?id=16026:
+ tuple(3 * (T.min_normal * T.epsilon), T( .75), (T.min_exp - T.mant_dig) + 2)
+ ];
+
+ foreach (elem; vals)
+ {
+ T x = elem[0];
+ T e = elem[1];
+ int exp = elem[2];
+ int eptr;
+ T v = frexp(x, eptr);
+ assert(isIdentical(e, v));
+ assert(exp == eptr);
+ }
+
+ static if (floatTraits!(T).realFormat == RealFormat.ieeeExtended)
+ {
+ static T[3][] extendedvals = [ // x,frexp,exp
+ [0x1.a5f1c2eb3fe4efp+73L, 0x1.A5F1C2EB3FE4EFp-1L, 74], // normal
+ [0x1.fa01712e8f0471ap-1064L, 0x1.fa01712e8f0471ap-1L, -1063],
+ [T.min_normal, .5, -16381],
+ [T.min_normal/2.0L, .5, -16382] // subnormal
+ ];
+ foreach (elem; extendedvals)
+ {
+ T x = elem[0];
+ T e = elem[1];
+ int exp = cast(int) elem[2];
+ int eptr;
+ T v = frexp(x, eptr);
+ assert(isIdentical(e, v));
+ assert(exp == eptr);
+ }
+ }
+ }}
+
+ // CTFE
+ alias CtfeFrexpResult= Tuple!(real, int);
+ static CtfeFrexpResult ctfeFrexp(T)(const T value)
+ {
+ int exp;
+ auto significand = frexp(value, exp);
+ return CtfeFrexpResult(significand, exp);
+ }
+ static foreach (T; AliasSeq!(real, double, float))
+ {{
+ enum Tuple!(T, T, int)[] vals = [ // x,frexp,exp
+ tuple(T(0.0), T( 0.0 ), 0),
+ tuple(T(-0.0), T( -0.0), 0),
+ tuple(T(1.0), T( .5 ), 1),
+ tuple(T(-1.0), T( -.5 ), 1),
+ tuple(T(2.0), T( .5 ), 2),
+ tuple(T(float.min_normal/2.0f), T(.5), -126),
+ tuple(T.infinity, T.infinity, int.max),
+ tuple(-T.infinity, -T.infinity, int.min),
+ tuple(T.nan, T.nan, int.min),
+ tuple(-T.nan, -T.nan, int.min),
+
+ // https://issues.dlang.org/show_bug.cgi?id=16026:
+ tuple(3 * (T.min_normal * T.epsilon), T( .75), (T.min_exp - T.mant_dig) + 2)
+ ];
+
+ static foreach (elem; vals)
+ {
+ static assert(ctfeFrexp(elem[0]) is CtfeFrexpResult(elem[1], elem[2]));
+ }
+
+ static if (floatTraits!(T).realFormat == RealFormat.ieeeExtended)
+ {
+ enum T[3][] extendedvals = [ // x,frexp,exp
+ [0x1.a5f1c2eb3fe4efp+73L, 0x1.A5F1C2EB3FE4EFp-1L, 74], // normal
+ [0x1.fa01712e8f0471ap-1064L, 0x1.fa01712e8f0471ap-1L, -1063],
+ [T.min_normal, .5, -16381],
+ [T.min_normal/2.0L, .5, -16382] // subnormal
+ ];
+ static foreach (elem; extendedvals)
+ {
+ static assert(ctfeFrexp(elem[0]) is CtfeFrexpResult(elem[1], cast(int) elem[2]));
+ }
+ }
+ }}
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ void foo() {
+ static foreach (T; AliasSeq!(real, double, float))
+ {{
+ int exp;
+ const T a = 1;
+ immutable T b = 2;
+ auto c = frexp(a, exp);
+ auto d = frexp(b, exp);
+ }}
+ }
+}
+
+/******************************************
+ * Extracts the exponent of x as a signed integral value.
+ *
+ * If x is not a special value, the result is the same as
+ * $(D cast(int) logb(x)).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH ilogb(x)) $(TH Range error?))
+ * $(TR $(TD 0) $(TD FP_ILOGB0) $(TD yes))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD int.max) $(TD no))
+ * $(TR $(TD $(NAN)) $(TD FP_ILOGBNAN) $(TD no))
+ * )
+ */
+int ilogb(T)(const T x) @trusted pure nothrow @nogc
+if (isFloatingPoint!T)
+{
+ import std.math : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB;
+
+ import core.bitop : bsr;
+ alias F = floatTraits!T;
+
+ union floatBits
+ {
+ T rv;
+ ushort[T.sizeof/2] vu;
+ uint[T.sizeof/4] vui;
+ static if (T.sizeof >= 8)
+ ulong[T.sizeof/8] vul;
+ }
+ floatBits y = void;
+ y.rv = x;
+
+ int ex = y.vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ if (ex)
+ {
+ // If exponent is non-zero
+ if (ex == F.EXPMASK) // infinity or NaN
+ {
+ if (y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF) // NaN
+ return FP_ILOGBNAN;
+ else // +-infinity
+ return int.max;
+ }
+ else
+ {
+ return ex - F.EXPBIAS - 1;
+ }
+ }
+ else if (!y.vul[0])
+ {
+ // vf is +-0.0
+ return FP_ILOGB0;
+ }
+ else
+ {
+ // subnormal
+ return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(y.vul[0]);
+ }
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ if (ex == F.EXPMASK)
+ {
+ // infinity or NaN
+ if (y.vul[MANTISSA_LSB] | ( y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN
+ return FP_ILOGBNAN;
+ else // +- infinity
+ return int.max;
+ }
+ else
+ {
+ return ex - F.EXPBIAS - 1;
+ }
+ }
+ else if ((y.vul[MANTISSA_LSB] | (y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0)
+ {
+ // vf is +-0.0
+ return FP_ILOGB0;
+ }
+ else
+ {
+ // subnormal
+ const ulong msb = y.vul[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF;
+ const ulong lsb = y.vul[MANTISSA_LSB];
+ if (msb)
+ return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(msb) + 64;
+ else
+ return ex - F.EXPBIAS - T.mant_dig + 1 + bsr(lsb);
+ }
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ if (ex == F.EXPMASK) // infinity or NaN
+ {
+ if ((y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FF0_0000_0000_0000) // +- infinity
+ return int.max;
+ else // NaN
+ return FP_ILOGBNAN;
+ }
+ else
+ {
+ return ((ex - F.EXPBIAS) >> 4) - 1;
+ }
+ }
+ else if (!(y.vul[0] & 0x7FFF_FFFF_FFFF_FFFF))
+ {
+ // vf is +-0.0
+ return FP_ILOGB0;
+ }
+ else
+ {
+ // subnormal
+ enum MANTISSAMASK_64 = ((cast(ulong) F.MANTISSAMASK_INT) << 32) | 0xFFFF_FFFF;
+ return ((ex - F.EXPBIAS) >> 4) - T.mant_dig + 1 + bsr(y.vul[0] & MANTISSAMASK_64);
+ }
+ }
+ else static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ if (ex == F.EXPMASK) // infinity or NaN
+ {
+ if ((y.vui[0] & 0x7FFF_FFFF) == 0x7F80_0000) // +- infinity
+ return int.max;
+ else // NaN
+ return FP_ILOGBNAN;
+ }
+ else
+ {
+ return ((ex - F.EXPBIAS) >> 7) - 1;
+ }
+ }
+ else if (!(y.vui[0] & 0x7FFF_FFFF))
+ {
+ // vf is +-0.0
+ return FP_ILOGB0;
+ }
+ else
+ {
+ // subnormal
+ const uint mantissa = y.vui[0] & F.MANTISSAMASK_INT;
+ return ((ex - F.EXPBIAS) >> 7) - T.mant_dig + 1 + bsr(mantissa);
+ }
+ }
+ else // static if (F.realFormat == RealFormat.ibmExtended)
+ {
+ assert(0, "ilogb not implemented");
+ }
+}
+/// ditto
+int ilogb(T)(const T x) @safe pure nothrow @nogc
+if (isIntegral!T && isUnsigned!T)
+{
+ import core.bitop : bsr;
+ if (x == 0)
+ return FP_ILOGB0;
+ else
+ {
+ static assert(T.sizeof <= ulong.sizeof, "integer size too large for the current ilogb implementation");
+ return bsr(x);
+ }
+}
+/// ditto
+int ilogb(T)(const T x) @safe pure nothrow @nogc
+if (isIntegral!T && isSigned!T)
+{
+ import std.traits : Unsigned;
+ // Note: abs(x) can not be used because the return type is not Unsigned and
+ // the return value would be wrong for x == int.min
+ Unsigned!T absx = x >= 0 ? x : -x;
+ return ilogb(absx);
+}
+
+///
+@safe pure unittest
+{
+ assert(ilogb(1) == 0);
+ assert(ilogb(3) == 1);
+ assert(ilogb(3.0) == 1);
+ assert(ilogb(100_000_000) == 26);
+
+ assert(ilogb(0) == FP_ILOGB0);
+ assert(ilogb(0.0) == FP_ILOGB0);
+ assert(ilogb(double.nan) == FP_ILOGBNAN);
+ assert(ilogb(double.infinity) == int.max);
+}
+
+/**
+Special return values of $(LREF ilogb).
+ */
+alias FP_ILOGB0 = core.stdc.math.FP_ILOGB0;
+/// ditto
+alias FP_ILOGBNAN = core.stdc.math.FP_ILOGBNAN;
+
+///
+@safe pure unittest
+{
+ assert(ilogb(0) == FP_ILOGB0);
+ assert(ilogb(0.0) == FP_ILOGB0);
+ assert(ilogb(double.nan) == FP_ILOGBNAN);
+}
+
+@safe nothrow @nogc unittest
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.operations : nextUp;
+ import std.meta : AliasSeq;
+ import std.typecons : Tuple;
+ static foreach (F; AliasSeq!(float, double, real))
+ {{
+ alias T = Tuple!(F, int);
+ T[13] vals = // x, ilogb(x)
+ [
+ T( F.nan , FP_ILOGBNAN ),
+ T( -F.nan , FP_ILOGBNAN ),
+ T( F.infinity, int.max ),
+ T( -F.infinity, int.max ),
+ T( 0.0 , FP_ILOGB0 ),
+ T( -0.0 , FP_ILOGB0 ),
+ T( 2.0 , 1 ),
+ T( 2.0001 , 1 ),
+ T( 1.9999 , 0 ),
+ T( 0.5 , -1 ),
+ T( 123.123 , 6 ),
+ T( -123.123 , 6 ),
+ T( 0.123 , -4 ),
+ ];
+
+ foreach (elem; vals)
+ {
+ assert(ilogb(elem[0]) == elem[1]);
+ }
+ }}
+
+ // min_normal and subnormals
+ assert(ilogb(-float.min_normal) == -126);
+ assert(ilogb(nextUp(-float.min_normal)) == -127);
+ assert(ilogb(nextUp(-float(0.0))) == -149);
+ assert(ilogb(-double.min_normal) == -1022);
+ assert(ilogb(nextUp(-double.min_normal)) == -1023);
+ assert(ilogb(nextUp(-double(0.0))) == -1074);
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
+ {
+ assert(ilogb(-real.min_normal) == -16382);
+ assert(ilogb(nextUp(-real.min_normal)) == -16383);
+ assert(ilogb(nextUp(-real(0.0))) == -16445);
+ }
+ else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble)
+ {
+ assert(ilogb(-real.min_normal) == -1022);
+ assert(ilogb(nextUp(-real.min_normal)) == -1023);
+ assert(ilogb(nextUp(-real(0.0))) == -1074);
+ }
+
+ // test integer types
+ assert(ilogb(0) == FP_ILOGB0);
+ assert(ilogb(int.max) == 30);
+ assert(ilogb(int.min) == 31);
+ assert(ilogb(uint.max) == 31);
+ assert(ilogb(long.max) == 62);
+ assert(ilogb(long.min) == 63);
+ assert(ilogb(ulong.max) == 63);
+}
+
+/*******************************************
+ * Compute n * 2$(SUPERSCRIPT exp)
+ * References: frexp
+ */
+
+pragma(inline, true)
+real ldexp(real n, int exp) @safe pure nothrow @nogc { return core.math.ldexp(n, exp); }
+///ditto
+pragma(inline, true)
+double ldexp(double n, int exp) @safe pure nothrow @nogc { return core.math.ldexp(n, exp); }
+///ditto
+pragma(inline, true)
+float ldexp(float n, int exp) @safe pure nothrow @nogc { return core.math.ldexp(n, exp); }
+
+///
+@nogc @safe pure nothrow unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ T r;
+
+ r = ldexp(3.0L, 3);
+ assert(r == 24);
+
+ r = ldexp(cast(T) 3.0, cast(int) 3);
+ assert(r == 24);
+
+ T n = 3.0;
+ int exp = 3;
+ r = ldexp(n, exp);
+ assert(r == 24);
+ }}
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math : floatTraits, RealFormat;
+
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended ||
+ floatTraits!(real).realFormat == RealFormat.ieeeExtended53 ||
+ floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
+ {
+ assert(ldexp(1.0L, -16384) == 0x1p-16384L);
+ assert(ldexp(1.0L, -16382) == 0x1p-16382L);
+ int x;
+ real n = frexp(0x1p-16384L, x);
+ assert(n == 0.5L);
+ assert(x==-16383);
+ assert(ldexp(n, x)==0x1p-16384L);
+ }
+ else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble)
+ {
+ assert(ldexp(1.0L, -1024) == 0x1p-1024L);
+ assert(ldexp(1.0L, -1022) == 0x1p-1022L);
+ int x;
+ real n = frexp(0x1p-1024L, x);
+ assert(n == 0.5L);
+ assert(x==-1023);
+ assert(ldexp(n, x)==0x1p-1024L);
+ }
+ else static assert(false, "Floating point type real not supported");
+}
+
+/* workaround https://issues.dlang.org/show_bug.cgi?id=14718
+ float parsing depends on platform strtold
+@safe pure nothrow @nogc unittest
+{
+ assert(ldexp(1.0, -1024) == 0x1p-1024);
+ assert(ldexp(1.0, -1022) == 0x1p-1022);
+ int x;
+ double n = frexp(0x1p-1024, x);
+ assert(n == 0.5);
+ assert(x==-1023);
+ assert(ldexp(n, x)==0x1p-1024);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(ldexp(1.0f, -128) == 0x1p-128f);
+ assert(ldexp(1.0f, -126) == 0x1p-126f);
+ int x;
+ float n = frexp(0x1p-128f, x);
+ assert(n == 0.5f);
+ assert(x==-127);
+ assert(ldexp(n, x)==0x1p-128f);
+}
+*/
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+
+ static real[3][] vals = // value,exp,ldexp
+ [
+ [ 0, 0, 0],
+ [ 1, 0, 1],
+ [ -1, 0, -1],
+ [ 1, 1, 2],
+ [ 123, 10, 125952],
+ [ real.max, int.max, real.infinity],
+ [ real.max, -int.max, 0],
+ [ real.min_normal, -int.max, 0],
+ ];
+ int i;
+
+ for (i = 0; i < vals.length; i++)
+ {
+ real x = vals[i][0];
+ int exp = cast(int) vals[i][1];
+ real z = vals[i][2];
+ real l = ldexp(x, exp);
+
+ assert(isClose(z, l, 1e-6));
+ }
+
+ real function(real, int) pldexp = &ldexp;
+ assert(pldexp != null);
+}
+
+private
+{
+ import std.math : floatTraits, RealFormat;
+
+ version (INLINE_YL2X) {} else
+ {
+ static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
+ {
+ // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x)
+ static immutable real[13] logCoeffsP = [
+ 1.313572404063446165910279910527789794488E4L,
+ 7.771154681358524243729929227226708890930E4L,
+ 2.014652742082537582487669938141683759923E5L,
+ 3.007007295140399532324943111654767187848E5L,
+ 2.854829159639697837788887080758954924001E5L,
+ 1.797628303815655343403735250238293741397E5L,
+ 7.594356839258970405033155585486712125861E4L,
+ 2.128857716871515081352991964243375186031E4L,
+ 3.824952356185897735160588078446136783779E3L,
+ 4.114517881637811823002128927449878962058E2L,
+ 2.321125933898420063925789532045674660756E1L,
+ 4.998469661968096229986658302195402690910E-1L,
+ 1.538612243596254322971797716843006400388E-6L
+ ];
+ static immutable real[13] logCoeffsQ = [
+ 3.940717212190338497730839731583397586124E4L,
+ 2.626900195321832660448791748036714883242E5L,
+ 7.777690340007566932935753241556479363645E5L,
+ 1.347518538384329112529391120390701166528E6L,
+ 1.514882452993549494932585972882995548426E6L,
+ 1.158019977462989115839826904108208787040E6L,
+ 6.132189329546557743179177159925690841200E5L,
+ 2.248234257620569139969141618556349415120E5L,
+ 5.605842085972455027590989944010492125825E4L,
+ 9.147150349299596453976674231612674085381E3L,
+ 9.104928120962988414618126155557301584078E2L,
+ 4.839208193348159620282142911143429644326E1L,
+ 1.0
+ ];
+
+ // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2)
+ // where z = 2(x-1)/(x+1)
+ static immutable real[6] logCoeffsR = [
+ 1.418134209872192732479751274970992665513E5L,
+ -8.977257995689735303686582344659576526998E4L,
+ 2.048819892795278657810231591630928516206E4L,
+ -2.024301798136027039250415126250455056397E3L,
+ 8.057002716646055371965756206836056074715E1L,
+ -8.828896441624934385266096344596648080902E-1L
+ ];
+ static immutable real[7] logCoeffsS = [
+ 1.701761051846631278975701529965589676574E6L,
+ -1.332535117259762928288745111081235577029E6L,
+ 4.001557694070773974936904547424676279307E5L,
+ -5.748542087379434595104154610899551484314E4L,
+ 3.998526750980007367835804959888064681098E3L,
+ -1.186359407982897997337150403816839480438E2L,
+ 1.0
+ ];
+ }
+ else
+ {
+ // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x)
+ static immutable real[7] logCoeffsP = [
+ 2.0039553499201281259648E1L,
+ 5.7112963590585538103336E1L,
+ 6.0949667980987787057556E1L,
+ 2.9911919328553073277375E1L,
+ 6.5787325942061044846969E0L,
+ 4.9854102823193375972212E-1L,
+ 4.5270000862445199635215E-5L,
+ ];
+ static immutable real[7] logCoeffsQ = [
+ 6.0118660497603843919306E1L,
+ 2.1642788614495947685003E2L,
+ 3.0909872225312059774938E2L,
+ 2.2176239823732856465394E2L,
+ 8.3047565967967209469434E1L,
+ 1.5062909083469192043167E1L,
+ 1.0000000000000000000000E0L,
+ ];
+
+ // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2)
+ // where z = 2(x-1)/(x+1)
+ static immutable real[4] logCoeffsR = [
+ -3.5717684488096787370998E1L,
+ 1.0777257190312272158094E1L,
+ -7.1990767473014147232598E-1L,
+ 1.9757429581415468984296E-3L,
+ ];
+ static immutable real[4] logCoeffsS = [
+ -4.2861221385716144629696E2L,
+ 1.9361891836232102174846E2L,
+ -2.6201045551331104417768E1L,
+ 1.0000000000000000000000E0L,
+ ];
+ }
+ }
+}
+
+/**************************************
+ * Calculate the natural logarithm of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH log(x)) $(TH divide by 0?) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no))
+ * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes))
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no))
+ * )
+ */
+real log(real x) @safe pure nothrow @nogc
+{
+ import std.math.constants : LN2, LOG2, SQRT1_2;
+ import std.math.traits : isInfinity, isNaN, signbit;
+ import std.math.algebraic : poly;
+
+ version (INLINE_YL2X)
+ return core.math.yl2x(x, LN2);
+ else
+ {
+ // C1 + C2 = LN2.
+ enum real C1 = 6.93145751953125E-1L;
+ enum real C2 = 1.428606820309417232121458176568075500134E-6L;
+
+ // Special cases.
+ if (isNaN(x))
+ return x;
+ if (isInfinity(x) && !signbit(x))
+ return x;
+ if (x == 0.0)
+ return -real.infinity;
+ if (x < 0.0)
+ return real.nan;
+
+ // Separate mantissa from exponent.
+ // Note, frexp is used so that denormal numbers will be handled properly.
+ real y, z;
+ int exp;
+
+ x = frexp(x, exp);
+
+ // Logarithm using log(x) = z + z^^3 R(z) / S(z),
+ // where z = 2(x - 1)/(x + 1)
+ if ((exp > 2) || (exp < -2))
+ {
+ if (x < SQRT1_2)
+ { // 2(2x - 1)/(2x + 1)
+ exp -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ }
+ else
+ { // 2(x - 1)/(x + 1)
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x * x;
+ z = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
+ z += exp * C2;
+ z += x;
+ z += exp * C1;
+
+ return z;
+ }
+
+ // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
+ if (x < SQRT1_2)
+ {
+ exp -= 1;
+ x = 2.0 * x - 1.0;
+ }
+ else
+ {
+ x = x - 1.0;
+ }
+ z = x * x;
+ y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
+ y += exp * C2;
+ z = y - 0.5 * z;
+
+ // Note, the sum of above terms does not exceed x/4,
+ // so it contributes at most about 1/4 lsb to the error.
+ z += x;
+ z += exp * C1;
+
+ return z;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : feqrel;
+ import std.math.constants : E;
+
+ assert(feqrel(log(E), 1) >= real.mant_dig - 1);
+}
+
+/**************************************
+ * Calculate the base-10 logarithm of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH log10(x)) $(TH divide by 0?) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no))
+ * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes))
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no))
+ * )
+ */
+real log10(real x) @safe pure nothrow @nogc
+{
+ import std.math.constants : LOG2, LN2, SQRT1_2;
+ import std.math.algebraic : poly;
+ import std.math.traits : isNaN, isInfinity, signbit;
+
+ version (INLINE_YL2X)
+ return core.math.yl2x(x, LOG2);
+ else
+ {
+ // log10(2) split into two parts.
+ enum real L102A = 0.3125L;
+ enum real L102B = -1.14700043360188047862611052755069732318101185E-2L;
+
+ // log10(e) split into two parts.
+ enum real L10EA = 0.5L;
+ enum real L10EB = -6.570551809674817234887108108339491770560299E-2L;
+
+ // Special cases are the same as for log.
+ if (isNaN(x))
+ return x;
+ if (isInfinity(x) && !signbit(x))
+ return x;
+ if (x == 0.0)
+ return -real.infinity;
+ if (x < 0.0)
+ return real.nan;
+
+ // Separate mantissa from exponent.
+ // Note, frexp is used so that denormal numbers will be handled properly.
+ real y, z;
+ int exp;
+
+ x = frexp(x, exp);
+
+ // Logarithm using log(x) = z + z^^3 R(z) / S(z),
+ // where z = 2(x - 1)/(x + 1)
+ if ((exp > 2) || (exp < -2))
+ {
+ if (x < SQRT1_2)
+ { // 2(2x - 1)/(2x + 1)
+ exp -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ }
+ else
+ { // 2(x - 1)/(x + 1)
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x * x;
+ y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
+ goto Ldone;
+ }
+
+ // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
+ if (x < SQRT1_2)
+ {
+ exp -= 1;
+ x = 2.0 * x - 1.0;
+ }
+ else
+ x = x - 1.0;
+
+ z = x * x;
+ y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
+ y = y - 0.5 * z;
+
+ // Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
+ // This sequence of operations is critical and it may be horribly
+ // defeated by some compiler optimizers.
+ Ldone:
+ z = y * L10EB;
+ z += x * L10EB;
+ z += exp * L102B;
+ z += y * L10EA;
+ z += x * L10EA;
+ z += exp * L102A;
+
+ return z;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.algebraic : fabs;
+
+ assert(fabs(log10(1000) - 3) < .000001);
+}
+
+/**
+ * Calculates the natural logarithm of 1 + x.
+ *
+ * For very small x, log1p(x) will be more accurate than
+ * log(1 + x).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH log1p(x)) $(TH divide by 0?) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) $(TD no))
+ * $(TR $(TD -1.0) $(TD -$(INFIN)) $(TD yes) $(TD no))
+ * $(TR $(TD $(LT)-1.0) $(TD -$(NAN)) $(TD no) $(TD yes))
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no))
+ * )
+ */
+real log1p(real x) @safe pure nothrow @nogc
+{
+ import std.math.traits : isNaN, isInfinity, signbit;
+ import std.math.constants : LN2;
+
+ version (INLINE_YL2X)
+ {
+ // On x87, yl2xp1 is valid if and only if -0.5 <= lg(x) <= 0.5,
+ // ie if -0.29 <= x <= 0.414
+ return (core.math.fabs(x) <= 0.25) ? core.math.yl2xp1(x, LN2) : core.math.yl2x(x+1, LN2);
+ }
+ else
+ {
+ // Special cases.
+ if (isNaN(x) || x == 0.0)
+ return x;
+ if (isInfinity(x) && !signbit(x))
+ return x;
+ if (x == -1.0)
+ return -real.infinity;
+ if (x < -1.0)
+ return real.nan;
+
+ return log(x + 1.0);
+ }
+}
+
+///
+@safe pure unittest
+{
+ import std.math.traits : isIdentical, isNaN;
+ import std.math.operations : feqrel;
+
+ assert(isIdentical(log1p(0.0), 0.0));
+ assert(log1p(1.0).feqrel(0.69314) > 16);
+
+ assert(log1p(-1.0) == -real.infinity);
+ assert(isNaN(log1p(-2.0)));
+ assert(log1p(real.nan) is real.nan);
+ assert(log1p(-real.nan) is -real.nan);
+ assert(log1p(real.infinity) == real.infinity);
+}
+
+/***************************************
+ * Calculates the base-2 logarithm of x:
+ * $(SUB log, 2)x
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH log2(x)) $(TH divide by 0?) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no) )
+ * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes) )
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no) )
+ * )
+ */
+real log2(real x) @safe pure nothrow @nogc
+{
+ import std.math.traits : isNaN, isInfinity, signbit;
+ import std.math.constants : SQRT1_2, LOG2E;
+ import std.math.algebraic : poly;
+
+ version (INLINE_YL2X)
+ return core.math.yl2x(x, 1.0L);
+ else
+ {
+ // Special cases are the same as for log.
+ if (isNaN(x))
+ return x;
+ if (isInfinity(x) && !signbit(x))
+ return x;
+ if (x == 0.0)
+ return -real.infinity;
+ if (x < 0.0)
+ return real.nan;
+
+ // Separate mantissa from exponent.
+ // Note, frexp is used so that denormal numbers will be handled properly.
+ real y, z;
+ int exp;
+
+ x = frexp(x, exp);
+
+ // Logarithm using log(x) = z + z^^3 R(z) / S(z),
+ // where z = 2(x - 1)/(x + 1)
+ if ((exp > 2) || (exp < -2))
+ {
+ if (x < SQRT1_2)
+ { // 2(2x - 1)/(2x + 1)
+ exp -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ }
+ else
+ { // 2(x - 1)/(x + 1)
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x * x;
+ y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
+ goto Ldone;
+ }
+
+ // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
+ if (x < SQRT1_2)
+ {
+ exp -= 1;
+ x = 2.0 * x - 1.0;
+ }
+ else
+ x = x - 1.0;
+
+ z = x * x;
+ y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
+ y = y - 0.5 * z;
+
+ // Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
+ // This sequence of operations is critical and it may be horribly
+ // defeated by some compiler optimizers.
+ Ldone:
+ z = y * (LOG2E - 1.0);
+ z += x * (LOG2E - 1.0);
+ z += y;
+ z += x;
+ z += exp;
+
+ return z;
+ }
+}
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(log2(1024.0L), 10));
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+
+ // check if values are equal to 19 decimal digits of precision
+ assert(isClose(log2(1024.0L), 10, 1e-18));
+}
+
+/*****************************************
+ * Extracts the exponent of x as a signed integral value.
+ *
+ * If x is subnormal, it is treated as if it were normalized.
+ * For a positive, finite x:
+ *
+ * 1 $(LT)= $(I x) * FLT_RADIX$(SUPERSCRIPT -logb(x)) $(LT) FLT_RADIX
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH logb(x)) $(TH divide by 0?) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) $(TD no))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) )
+ * )
+ */
+real logb(real x) @trusted nothrow @nogc
+{
+ version (InlineAsm_X87_MSVC)
+ {
+ version (X86_64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked ;
+ fld real ptr [RCX] ;
+ fxtract ;
+ fstp ST(0) ;
+ ret ;
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ fld x ;
+ fxtract ;
+ fstp ST(0) ;
+ }
+ }
+ }
+ else
+ return core.stdc.math.logbl(x);
+}
+
+///
+@safe @nogc nothrow unittest
+{
+ assert(logb(1.0) == 0);
+ assert(logb(100.0) == 6);
+
+ assert(logb(0.0) == -real.infinity);
+ assert(logb(real.infinity) == real.infinity);
+ assert(logb(-real.infinity) == real.infinity);
+}
+
+/*************************************
+ * Efficiently calculates x * 2$(SUPERSCRIPT n).
+ *
+ * scalbn handles underflow and overflow in
+ * the same fashion as the basic arithmetic operators.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH scalb(x)))
+ * $(TR $(TD $(PLUSMNINF)) $(TD $(PLUSMNINF)) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) )
+ * )
+ */
+pragma(inline, true)
+real scalbn(real x, int n) @safe pure nothrow @nogc { return _scalbn(x,n); }
+
+/// ditto
+pragma(inline, true)
+double scalbn(double x, int n) @safe pure nothrow @nogc { return _scalbn(x,n); }
+
+/// ditto
+pragma(inline, true)
+float scalbn(float x, int n) @safe pure nothrow @nogc { return _scalbn(x,n); }
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(scalbn(0x1.2345678abcdefp0L, 999) == 0x1.2345678abcdefp999L);
+ assert(scalbn(-real.infinity, 5) == -real.infinity);
+ assert(scalbn(2.0,10) == 2048.0);
+ assert(scalbn(2048.0f,-10) == 2.0f);
+}
+
+pragma(inline, true)
+private F _scalbn(F)(F x, int n)
+{
+ import std.math.traits : isInfinity;
+
+ if (__ctfe)
+ {
+ // Handle special cases.
+ if (x == F(0.0) || isInfinity(x))
+ return x;
+ }
+ return core.math.ldexp(x, n);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ // CTFE-able test
+ static assert(scalbn(0x1.2345678abcdefp0L, 999) == 0x1.2345678abcdefp999L);
+ static assert(scalbn(-real.infinity, 5) == -real.infinity);
+ // Test with large exponent delta n where the result is in bounds but 2.0L ^^ n is not.
+ enum initialExponent = real.min_exp + 2, resultExponent = real.max_exp - 2;
+ enum int n = resultExponent - initialExponent;
+ enum real x = 0x1.2345678abcdefp0L * (2.0L ^^ initialExponent);
+ enum staticResult = scalbn(x, n);
+ static assert(staticResult == 0x1.2345678abcdefp0L * (2.0L ^^ resultExponent));
+ assert(scalbn(x, n) == staticResult);
+}
+
diff --git a/libphobos/src/std/math/hardware.d b/libphobos/src/std/math/hardware.d
new file mode 100644
index 00000000000..90bc96df148
--- /dev/null
+++ b/libphobos/src/std/math/hardware.d
@@ -0,0 +1,1212 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, math).
+
+It contains hardware support for floating point numbers.
+
+Copyright: Copyright The D Language Foundation 2000 - 2011.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
+ Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
+Source: $(PHOBOSSRC std/math/hardware.d)
+ */
+
+/* NOTE: This file has been patched from the original DMD distribution to
+ * work with the GDC compiler.
+ */
+module std.math.hardware;
+
+static import core.stdc.fenv;
+
+version (X86) version = X86_Any;
+version (X86_64) version = X86_Any;
+version (PPC) version = PPC_Any;
+version (PPC64) version = PPC_Any;
+version (MIPS32) version = MIPS_Any;
+version (MIPS64) version = MIPS_Any;
+version (AArch64) version = ARM_Any;
+version (ARM) version = ARM_Any;
+version (S390) version = IBMZ_Any;
+version (SPARC) version = SPARC_Any;
+version (SPARC64) version = SPARC_Any;
+version (SystemZ) version = IBMZ_Any;
+version (RISCV32) version = RISCV_Any;
+version (RISCV64) version = RISCV_Any;
+
+version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
+version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
+
+version (InlineAsm_X86_Any) version = InlineAsm_X87;
+version (InlineAsm_X87)
+{
+ static assert(real.mant_dig == 64);
+ version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC;
+}
+
+version (X86_64) version = StaticallyHaveSSE;
+version (X86) version (OSX) version = StaticallyHaveSSE;
+
+version (StaticallyHaveSSE)
+{
+ private enum bool haveSSE = true;
+}
+else version (X86)
+{
+ static import core.cpuid;
+ private alias haveSSE = core.cpuid.sse;
+}
+
+version (D_SoftFloat)
+{
+ // Some soft float implementations may support IEEE floating flags.
+ // The implementation here supports hardware flags only and is so currently
+ // only available for supported targets.
+}
+else version (X86_Any) version = IeeeFlagsSupport;
+else version (PPC_Any) version = IeeeFlagsSupport;
+else version (RISCV_Any) version = IeeeFlagsSupport;
+else version (MIPS_Any) version = IeeeFlagsSupport;
+else version (ARM_Any) version = IeeeFlagsSupport;
+
+// Struct FloatingPointControl is only available if hardware FP units are available.
+version (D_HardFloat)
+{
+ // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport
+ version (IeeeFlagsSupport) version = FloatingPointControlSupport;
+}
+
+version (GNU)
+{
+ // The compiler can unexpectedly rearrange floating point operations and
+ // access to the floating point status flags when optimizing. This means
+ // ieeeFlags tests cannot be reliably checked in optimized code.
+ // See https://github.com/ldc-developers/ldc/issues/888
+}
+else
+{
+ version = IeeeFlagsUnittest;
+ version = FloatingPointControlUnittest;
+}
+
+version (IeeeFlagsSupport)
+{
+
+/** IEEE exception status flags ('sticky bits')
+
+ These flags indicate that an exceptional floating-point condition has occurred.
+ They indicate that a NaN or an infinity has been generated, that a result
+ is inexact, or that a signalling NaN has been encountered. If floating-point
+ exceptions are enabled (unmasked), a hardware exception will be generated
+ instead of setting these flags.
+ */
+struct IeeeFlags
+{
+nothrow @nogc:
+
+private:
+ // The x87 FPU status register is 16 bits.
+ // The Pentium SSE2 status register is 32 bits.
+ // The ARM and PowerPC FPSCR is a 32-bit register.
+ // The SPARC FSR is a 32bit register (64 bits for SPARC 7 & 8, but high bits are uninteresting).
+ // The RISC-V (32 & 64 bit) fcsr is 32-bit register.
+ uint flags;
+
+ version (CRuntime_Microsoft)
+ {
+ // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
+ // Applies to both x87 status word (16 bits) and SSE2 status word(32 bits).
+ enum : int
+ {
+ INEXACT_MASK = 0x20,
+ UNDERFLOW_MASK = 0x10,
+ OVERFLOW_MASK = 0x08,
+ DIVBYZERO_MASK = 0x04,
+ INVALID_MASK = 0x01,
+
+ EXCEPTIONS_MASK = 0b11_1111
+ }
+ // Don't bother about subnormals, they are not supported on most CPUs.
+ // SUBNORMAL_MASK = 0x02;
+ }
+ else
+ {
+ enum : int
+ {
+ INEXACT_MASK = core.stdc.fenv.FE_INEXACT,
+ UNDERFLOW_MASK = core.stdc.fenv.FE_UNDERFLOW,
+ OVERFLOW_MASK = core.stdc.fenv.FE_OVERFLOW,
+ DIVBYZERO_MASK = core.stdc.fenv.FE_DIVBYZERO,
+ INVALID_MASK = core.stdc.fenv.FE_INVALID,
+ EXCEPTIONS_MASK = core.stdc.fenv.FE_ALL_EXCEPT,
+ }
+ }
+
+ static uint getIeeeFlags() @trusted pure
+ {
+ version (GNU)
+ {
+ version (X86_Any)
+ {
+ ushort sw;
+ asm pure nothrow @nogc
+ {
+ "fstsw %0" : "=a" (sw);
+ }
+ // OR the result with the SSE2 status register (MXCSR).
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm pure nothrow @nogc
+ {
+ "stmxcsr %0" : "=m" (mxcsr);
+ }
+ return (sw | mxcsr) & EXCEPTIONS_MASK;
+ }
+ else
+ return sw & EXCEPTIONS_MASK;
+ }
+ else version (ARM)
+ {
+ version (ARM_SoftFloat)
+ return 0;
+ else
+ {
+ uint result = void;
+ asm pure nothrow @nogc
+ {
+ "vmrs %0, FPSCR; and %0, %0, #0x1F;" : "=r" (result);
+ }
+ return result;
+ }
+ }
+ else version (RISCV_Any)
+ {
+ version (D_SoftFloat)
+ return 0;
+ else
+ {
+ uint result = void;
+ asm pure nothrow @nogc
+ {
+ "frflags %0" : "=r" (result);
+ }
+ return result;
+ }
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+ else
+ version (InlineAsm_X86_Any)
+ {
+ ushort sw;
+ asm pure nothrow @nogc { fstsw sw; }
+
+ // OR the result with the SSE2 status register (MXCSR).
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm pure nothrow @nogc { stmxcsr mxcsr; }
+ return (sw | mxcsr) & EXCEPTIONS_MASK;
+ }
+ else return sw & EXCEPTIONS_MASK;
+ }
+ else version (SPARC)
+ {
+ /*
+ int retval;
+ asm pure nothrow @nogc { st %fsr, retval; }
+ return retval;
+ */
+ assert(0, "Not yet supported");
+ }
+ else version (ARM)
+ {
+ assert(false, "Not yet supported.");
+ }
+ else version (RISCV_Any)
+ {
+ mixin(`
+ uint result = void;
+ asm pure nothrow @nogc
+ {
+ "frflags %0" : "=r" (result);
+ }
+ return result;
+ `);
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+
+ static void resetIeeeFlags() @trusted
+ {
+ version (GNU)
+ {
+ version (X86_Any)
+ {
+ asm nothrow @nogc
+ {
+ "fnclex";
+ }
+
+ // Also clear exception flags in MXCSR, SSE's control register.
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm nothrow @nogc
+ {
+ "stmxcsr %0" : "=m" (mxcsr);
+ }
+ mxcsr &= ~EXCEPTIONS_MASK;
+ asm nothrow @nogc
+ {
+ "ldmxcsr %0" : : "m" (mxcsr);
+ }
+ }
+ }
+ else version (ARM)
+ {
+ version (ARM_SoftFloat)
+ return;
+ else
+ {
+ uint old = FloatingPointControl.getControlState();
+ old &= ~0b11111; // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0408i/Chdfifdc.html
+ asm nothrow @nogc
+ {
+ "vmsr FPSCR, %0" : : "r" (old);
+ }
+ }
+ }
+ else version (RISCV_Any)
+ {
+ version (D_SoftFloat)
+ return;
+ else
+ {
+ uint newValues = 0x0;
+ asm nothrow @nogc
+ {
+ "fsflags %0" : : "r" (newValues);
+ }
+ }
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+ else
+ version (InlineAsm_X86_Any)
+ {
+ asm nothrow @nogc
+ {
+ fnclex;
+ }
+
+ // Also clear exception flags in MXCSR, SSE's control register.
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm nothrow @nogc { stmxcsr mxcsr; }
+ mxcsr &= ~EXCEPTIONS_MASK;
+ asm nothrow @nogc { ldmxcsr mxcsr; }
+ }
+ }
+ else version (RISCV_Any)
+ {
+ mixin(`
+ uint newValues = 0x0;
+ asm pure nothrow @nogc
+ {
+ "fsflags %0" : : "r" (newValues);
+ }
+ `);
+ }
+ else
+ {
+ /* SPARC:
+ int tmpval;
+ asm pure nothrow @nogc { st %fsr, tmpval; }
+ tmpval &=0xFFFF_FC00;
+ asm pure nothrow @nogc { ld tmpval, %fsr; }
+ */
+ assert(0, "Not yet supported");
+ }
+ }
+
+public:
+ /**
+ * The result cannot be represented exactly, so rounding occurred.
+ * Example: `x = sin(0.1);`
+ */
+ @property bool inexact() @safe const { return (flags & INEXACT_MASK) != 0; }
+
+ /**
+ * A zero was generated by underflow
+ * Example: `x = real.min*real.epsilon/2;`
+ */
+ @property bool underflow() @safe const { return (flags & UNDERFLOW_MASK) != 0; }
+
+ /**
+ * An infinity was generated by overflow
+ * Example: `x = real.max*2;`
+ */
+ @property bool overflow() @safe const { return (flags & OVERFLOW_MASK) != 0; }
+
+ /**
+ * An infinity was generated by division by zero
+ * Example: `x = 3/0.0;`
+ */
+ @property bool divByZero() @safe const { return (flags & DIVBYZERO_MASK) != 0; }
+
+ /**
+ * A machine NaN was generated.
+ * Example: `x = real.infinity * 0.0;`
+ */
+ @property bool invalid() @safe const { return (flags & INVALID_MASK) != 0; }
+}
+
+///
+version (IeeeFlagsUnittest)
+@safe unittest
+{
+ import std.math.traits : isNaN;
+
+ static void func() {
+ int a = 10 * 10;
+ }
+ pragma(inline, false) static void blockopt(ref real x) {}
+ real a = 3.5;
+ // Set all the flags to zero
+ resetIeeeFlags();
+ assert(!ieeeFlags.divByZero);
+ blockopt(a); // avoid constant propagation by the optimizer
+ // Perform a division by zero.
+ a /= 0.0L;
+ assert(a == real.infinity);
+ assert(ieeeFlags.divByZero);
+ blockopt(a); // avoid constant propagation by the optimizer
+ // Create a NaN
+ a *= 0.0L;
+ assert(ieeeFlags.invalid);
+ assert(isNaN(a));
+
+ // Check that calling func() has no effect on the
+ // status flags.
+ IeeeFlags f = ieeeFlags;
+ func();
+ assert(ieeeFlags == f);
+}
+
+version (IeeeFlagsUnittest)
+@safe unittest
+{
+ import std.meta : AliasSeq;
+
+ static struct Test
+ {
+ void delegate() @trusted action;
+ bool function() @trusted ieeeCheck;
+ }
+
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ T x; /* Needs to be here to trick -O. It would optimize away the
+ calculations if x were local to the function literals. */
+ auto tests = [
+ Test(
+ () { x = 1; x += 0.1L; },
+ () => ieeeFlags.inexact
+ ),
+ Test(
+ () { x = T.min_normal; x /= T.max; },
+ () => ieeeFlags.underflow
+ ),
+ Test(
+ () { x = T.max; x += T.max; },
+ () => ieeeFlags.overflow
+ ),
+ Test(
+ () { x = 1; x /= 0; },
+ () => ieeeFlags.divByZero
+ ),
+ Test(
+ () { x = 0; x /= 0; },
+ () => ieeeFlags.invalid
+ )
+ ];
+ foreach (test; tests)
+ {
+ resetIeeeFlags();
+ assert(!test.ieeeCheck());
+ test.action();
+ assert(test.ieeeCheck());
+ }
+ }}
+}
+
+/// Set all of the floating-point status flags to false.
+void resetIeeeFlags() @trusted nothrow @nogc
+{
+ IeeeFlags.resetIeeeFlags();
+}
+
+///
+@safe unittest
+{
+ pragma(inline, false) static void blockopt(ref real x) {}
+ resetIeeeFlags();
+ real a = 3.5;
+ blockopt(a); // avoid constant propagation by the optimizer
+ a /= 0.0L;
+ blockopt(a); // avoid constant propagation by the optimizer
+ assert(a == real.infinity);
+ assert(ieeeFlags.divByZero);
+
+ resetIeeeFlags();
+ assert(!ieeeFlags.divByZero);
+}
+
+/// Returns: snapshot of the current state of the floating-point status flags
+@property IeeeFlags ieeeFlags() @trusted pure nothrow @nogc
+{
+ return IeeeFlags(IeeeFlags.getIeeeFlags());
+}
+
+///
+@safe nothrow unittest
+{
+ import std.math.traits : isNaN;
+
+ pragma(inline, false) static void blockopt(ref real x) {}
+ resetIeeeFlags();
+ real a = 3.5;
+ blockopt(a); // avoid constant propagation by the optimizer
+
+ a /= 0.0L;
+ assert(a == real.infinity);
+ assert(ieeeFlags.divByZero);
+ blockopt(a); // avoid constant propagation by the optimizer
+
+ a *= 0.0L;
+ assert(isNaN(a));
+ assert(ieeeFlags.invalid);
+}
+
+} // IeeeFlagsSupport
+
+
+version (FloatingPointControlSupport)
+{
+
+/** Control the Floating point hardware
+
+ Change the IEEE754 floating-point rounding mode and the floating-point
+ hardware exceptions.
+
+ By default, the rounding mode is roundToNearest and all hardware exceptions
+ are disabled. For most applications, debugging is easier if the $(I division
+ by zero), $(I overflow), and $(I invalid operation) exceptions are enabled.
+ These three are combined into a $(I severeExceptions) value for convenience.
+ Note in particular that if $(I invalidException) is enabled, a hardware trap
+ will be generated whenever an uninitialized floating-point variable is used.
+
+ All changes are temporary. The previous state is restored at the
+ end of the scope.
+
+
+Example:
+----
+{
+ FloatingPointControl fpctrl;
+
+ // Enable hardware exceptions for division by zero, overflow to infinity,
+ // invalid operations, and uninitialized floating-point variables.
+ fpctrl.enableExceptions(FloatingPointControl.severeExceptions);
+
+ // This will generate a hardware exception, if x is a
+ // default-initialized floating point variable:
+ real x; // Add `= 0` or even `= real.nan` to not throw the exception.
+ real y = x * 3.0;
+
+ // The exception is only thrown for default-uninitialized NaN-s.
+ // NaN-s with other payload are valid:
+ real z = y * real.nan; // ok
+
+ // The set hardware exceptions and rounding modes will be disabled when
+ // leaving this scope.
+}
+----
+
+ */
+struct FloatingPointControl
+{
+nothrow @nogc:
+
+ alias RoundingMode = uint; ///
+
+ version (StdDdoc)
+ {
+ enum : RoundingMode
+ {
+ /** IEEE rounding modes.
+ * The default mode is roundToNearest.
+ *
+ * roundingMask = A mask of all rounding modes.
+ */
+ roundToNearest,
+ roundDown, /// ditto
+ roundUp, /// ditto
+ roundToZero, /// ditto
+ roundingMask, /// ditto
+ }
+ }
+ else version (CRuntime_Microsoft)
+ {
+ // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
+ enum : RoundingMode
+ {
+ roundToNearest = 0x0000,
+ roundDown = 0x0400,
+ roundUp = 0x0800,
+ roundToZero = 0x0C00,
+ roundingMask = roundToNearest | roundDown
+ | roundUp | roundToZero,
+ }
+ }
+ else
+ {
+ enum : RoundingMode
+ {
+ roundToNearest = core.stdc.fenv.FE_TONEAREST,
+ roundDown = core.stdc.fenv.FE_DOWNWARD,
+ roundUp = core.stdc.fenv.FE_UPWARD,
+ roundToZero = core.stdc.fenv.FE_TOWARDZERO,
+ roundingMask = roundToNearest | roundDown
+ | roundUp | roundToZero,
+ }
+ }
+
+ /***
+ * Change the floating-point hardware rounding mode
+ *
+ * Changing the rounding mode in the middle of a function can interfere
+ * with optimizations of floating point expressions, as the optimizer assumes
+ * that the rounding mode does not change.
+ * It is best to change the rounding mode only at the
+ * beginning of the function, and keep it until the function returns.
+ * It is also best to add the line:
+ * ---
+ * pragma(inline, false);
+ * ---
+ * as the first line of the function so it will not get inlined.
+ * Params:
+ * newMode = the new rounding mode
+ */
+ @property void rounding(RoundingMode newMode) @trusted
+ {
+ initialize();
+ setControlState((getControlState() & (-1 - roundingMask)) | (newMode & roundingMask));
+ }
+
+ /// Returns: the currently active rounding mode
+ @property static RoundingMode rounding() @trusted pure
+ {
+ return cast(RoundingMode)(getControlState() & roundingMask);
+ }
+
+ alias ExceptionMask = uint; ///
+
+ version (StdDdoc)
+ {
+ enum : ExceptionMask
+ {
+ /** IEEE hardware exceptions.
+ * By default, all exceptions are masked (disabled).
+ *
+ * severeExceptions = The overflow, division by zero, and invalid
+ * exceptions.
+ */
+ subnormalException,
+ inexactException, /// ditto
+ underflowException, /// ditto
+ overflowException, /// ditto
+ divByZeroException, /// ditto
+ invalidException, /// ditto
+ severeExceptions, /// ditto
+ allExceptions, /// ditto
+ }
+ }
+ else version (ARM_Any)
+ {
+ enum : ExceptionMask
+ {
+ subnormalException = 0x8000,
+ inexactException = 0x1000,
+ underflowException = 0x0800,
+ overflowException = 0x0400,
+ divByZeroException = 0x0200,
+ invalidException = 0x0100,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException | subnormalException,
+ }
+ }
+ else version (PPC_Any)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x0008,
+ divByZeroException = 0x0010,
+ underflowException = 0x0020,
+ overflowException = 0x0040,
+ invalidException = 0x0080,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (RISCV_Any)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x01,
+ divByZeroException = 0x02,
+ underflowException = 0x04,
+ overflowException = 0x08,
+ invalidException = 0x10,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (HPPA)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x01,
+ underflowException = 0x02,
+ overflowException = 0x04,
+ divByZeroException = 0x08,
+ invalidException = 0x10,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (MIPS_Any)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x0080,
+ divByZeroException = 0x0400,
+ overflowException = 0x0200,
+ underflowException = 0x0100,
+ invalidException = 0x0800,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (SPARC_Any)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x0800000,
+ divByZeroException = 0x1000000,
+ overflowException = 0x4000000,
+ underflowException = 0x2000000,
+ invalidException = 0x8000000,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (IBMZ_Any)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x08000000,
+ divByZeroException = 0x40000000,
+ overflowException = 0x20000000,
+ underflowException = 0x10000000,
+ invalidException = 0x80000000,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (X86_Any)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x20,
+ underflowException = 0x10,
+ overflowException = 0x08,
+ divByZeroException = 0x04,
+ subnormalException = 0x02,
+ invalidException = 0x01,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException | subnormalException,
+ }
+ }
+ else
+ static assert(false, "Not implemented for this architecture");
+
+ version (ARM_Any)
+ {
+ static bool hasExceptionTraps_impl() @safe
+ {
+ auto oldState = getControlState();
+ // If exceptions are not supported, we set the bit but read it back as zero
+ // https://sourceware.org/ml/libc-ports/2012-06/msg00091.html
+ setControlState(oldState | divByZeroException);
+ immutable result = (getControlState() & allExceptions) != 0;
+ setControlState(oldState);
+ return result;
+ }
+ }
+
+ /// Returns: true if the current FPU supports exception trapping
+ @property static bool hasExceptionTraps() @safe pure
+ {
+ version (X86_Any)
+ return true;
+ else version (PPC_Any)
+ return true;
+ else version (MIPS_Any)
+ return true;
+ else version (ARM_Any)
+ {
+ // The hasExceptionTraps_impl function is basically pure,
+ // as it restores all global state
+ auto fptr = ( () @trusted => cast(bool function() @safe
+ pure nothrow @nogc)&hasExceptionTraps_impl)();
+ return fptr();
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+
+ /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together.
+ void enableExceptions(ExceptionMask exceptions) @trusted
+ {
+ assert(hasExceptionTraps);
+ initialize();
+ version (X86_Any)
+ setControlState(getControlState() & ~(exceptions & allExceptions));
+ else
+ setControlState(getControlState() | (exceptions & allExceptions));
+ }
+
+ /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together.
+ void disableExceptions(ExceptionMask exceptions) @trusted
+ {
+ assert(hasExceptionTraps);
+ initialize();
+ version (X86_Any)
+ setControlState(getControlState() | (exceptions & allExceptions));
+ else
+ setControlState(getControlState() & ~(exceptions & allExceptions));
+ }
+
+ /// Returns: the exceptions which are currently enabled (unmasked)
+ @property static ExceptionMask enabledExceptions() @trusted pure
+ {
+ assert(hasExceptionTraps);
+ version (X86_Any)
+ return (getControlState() & allExceptions) ^ allExceptions;
+ else
+ return (getControlState() & allExceptions);
+ }
+
+ /// Clear all pending exceptions, then restore the original exception state and rounding mode.
+ ~this() @trusted
+ {
+ clearExceptions();
+ if (initialized)
+ setControlState(savedState);
+ }
+
+private:
+ ControlState savedState;
+
+ bool initialized = false;
+
+ version (ARM_Any)
+ {
+ alias ControlState = uint;
+ }
+ else version (HPPA)
+ {
+ alias ControlState = uint;
+ }
+ else version (PPC_Any)
+ {
+ alias ControlState = uint;
+ }
+ else version (RISCV_Any)
+ {
+ alias ControlState = uint;
+ }
+ else version (MIPS_Any)
+ {
+ alias ControlState = uint;
+ }
+ else version (SPARC_Any)
+ {
+ alias ControlState = ulong;
+ }
+ else version (IBMZ_Any)
+ {
+ alias ControlState = uint;
+ }
+ else version (X86_Any)
+ {
+ alias ControlState = ushort;
+ }
+ else
+ static assert(false, "Not implemented for this architecture");
+
+ void initialize() @safe
+ {
+ // BUG: This works around the absence of this() constructors.
+ if (initialized) return;
+ clearExceptions();
+ savedState = getControlState();
+ initialized = true;
+ }
+
+ // Clear all pending exceptions
+ static void clearExceptions() @safe
+ {
+ version (IeeeFlagsSupport)
+ resetIeeeFlags();
+ else
+ static assert(false, "Not implemented for this architecture");
+ }
+
+ // Read from the control register
+ package(std.math) static ControlState getControlState() @trusted pure
+ {
+ version (GNU)
+ {
+ version (X86_Any)
+ {
+ ControlState cont;
+ asm pure nothrow @nogc
+ {
+ "fstcw %0" : "=m" (cont);
+ }
+ return cont;
+ }
+ else version (AArch64)
+ {
+ asm pure nothrow @nogc
+ {
+ "mrs %0, FPCR;" : "=r" (cont);
+ }
+ return cont;
+ }
+ else version (ARM)
+ {
+ ControlState cont;
+ version (ARM_SoftFloat)
+ cont = 0;
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ "vmrs %0, FPSCR" : "=r" (cont);
+ }
+ }
+ return cont;
+ }
+ else version (RISCV_Any)
+ {
+ version (D_SoftFloat)
+ return 0;
+ else
+ {
+ ControlState cont;
+ asm pure nothrow @nogc
+ {
+ "frcsr %0" : "=r" (cont);
+ }
+ return cont;
+ }
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+ else
+ version (D_InlineAsm_X86)
+ {
+ short cont;
+ asm pure nothrow @nogc
+ {
+ xor EAX, EAX;
+ fstcw cont;
+ }
+ return cont;
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ short cont;
+ asm pure nothrow @nogc
+ {
+ xor RAX, RAX;
+ fstcw cont;
+ }
+ return cont;
+ }
+ else version (RISCV_Any)
+ {
+ mixin(`
+ ControlState cont;
+ asm pure nothrow @nogc
+ {
+ "frcsr %0" : "=r" (cont);
+ }
+ return cont;
+ `);
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+
+ // Set the control register
+ package(std.math) static void setControlState(ControlState newState) @trusted
+ {
+ version (GNU)
+ {
+ version (X86_Any)
+ {
+ asm nothrow @nogc
+ {
+ "fclex; fldcw %0" : : "m" (newState);
+ }
+
+ // Also update MXCSR, SSE's control register.
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm nothrow @nogc
+ {
+ "stmxcsr %0" : "=m" (mxcsr);
+ }
+
+ /* In the FPU control register, rounding mode is in bits 10 and
+ 11. In MXCSR it's in bits 13 and 14. */
+ mxcsr &= ~(roundingMask << 3); // delete old rounding mode
+ mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
+
+ /* In the FPU control register, masks are bits 0 through 5.
+ In MXCSR they're 7 through 12. */
+ mxcsr &= ~(allExceptions << 7); // delete old masks
+ mxcsr |= (newState & allExceptions) << 7; // write new exception masks
+
+ asm nothrow @nogc
+ {
+ "ldmxcsr %0" : : "m" (mxcsr);
+ }
+ }
+ }
+ else version (AArch64)
+ {
+ asm nothrow @nogc
+ {
+ "msr FPCR, %0;" : : "r" (newState);
+ }
+ }
+ else version (ARM)
+ {
+ version (ARM_SoftFloat)
+ return;
+ else
+ {
+ asm nothrow @nogc
+ {
+ "vmsr FPSCR, %0" : : "r" (newState);
+ }
+ }
+ }
+ else version (RISCV_Any)
+ {
+ version (D_SoftFloat)
+ return;
+ else
+ {
+ asm nothrow @nogc
+ {
+ "fscsr %0" : : "r" (newState);
+ }
+ }
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+ else
+ version (InlineAsm_X86_Any)
+ {
+ asm nothrow @nogc
+ {
+ fclex;
+ fldcw newState;
+ }
+
+ // Also update MXCSR, SSE's control register.
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm nothrow @nogc { stmxcsr mxcsr; }
+
+ /* In the FPU control register, rounding mode is in bits 10 and
+ 11. In MXCSR it's in bits 13 and 14. */
+ mxcsr &= ~(roundingMask << 3); // delete old rounding mode
+ mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
+
+ /* In the FPU control register, masks are bits 0 through 5.
+ In MXCSR they're 7 through 12. */
+ mxcsr &= ~(allExceptions << 7); // delete old masks
+ mxcsr |= (newState & allExceptions) << 7; // write new exception masks
+
+ asm nothrow @nogc { ldmxcsr mxcsr; }
+ }
+ }
+ else version (RISCV_Any)
+ {
+ mixin(`
+ asm pure nothrow @nogc
+ {
+ "fscsr %0" : : "r" (newState);
+ }
+ `);
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+}
+
+///
+version (FloatingPointControlUnittest)
+@safe unittest
+{
+ import std.math.rounding : lrint;
+
+ FloatingPointControl fpctrl;
+
+ fpctrl.rounding = FloatingPointControl.roundDown;
+ assert(lrint(1.5) == 1.0);
+
+ fpctrl.rounding = FloatingPointControl.roundUp;
+ assert(lrint(1.4) == 2.0);
+
+ fpctrl.rounding = FloatingPointControl.roundToNearest;
+ assert(lrint(1.5) == 2.0);
+}
+
+@safe unittest
+{
+ void ensureDefaults()
+ {
+ assert(FloatingPointControl.rounding
+ == FloatingPointControl.roundToNearest);
+ if (FloatingPointControl.hasExceptionTraps)
+ assert(FloatingPointControl.enabledExceptions == 0);
+ }
+
+ {
+ FloatingPointControl ctrl;
+ }
+ ensureDefaults();
+
+ {
+ FloatingPointControl ctrl;
+ ctrl.rounding = FloatingPointControl.roundDown;
+ assert(FloatingPointControl.rounding == FloatingPointControl.roundDown);
+ }
+ ensureDefaults();
+
+ if (FloatingPointControl.hasExceptionTraps)
+ {
+ FloatingPointControl ctrl;
+ ctrl.enableExceptions(FloatingPointControl.divByZeroException
+ | FloatingPointControl.overflowException);
+ assert(ctrl.enabledExceptions ==
+ (FloatingPointControl.divByZeroException
+ | FloatingPointControl.overflowException));
+
+ ctrl.rounding = FloatingPointControl.roundUp;
+ assert(FloatingPointControl.rounding == FloatingPointControl.roundUp);
+ }
+ ensureDefaults();
+}
+
+version (FloatingPointControlUnittest)
+@safe unittest // rounding
+{
+ import std.meta : AliasSeq;
+
+ static T addRound(T)(uint rm)
+ {
+ pragma(inline, false) static void blockopt(ref T x) {}
+ pragma(inline, false);
+ FloatingPointControl fpctrl;
+ fpctrl.rounding = rm;
+ T x = 1;
+ blockopt(x); // avoid constant propagation by the optimizer
+ x += 0.1L;
+ return x;
+ }
+
+ static T subRound(T)(uint rm)
+ {
+ pragma(inline, false) static void blockopt(ref T x) {}
+ pragma(inline, false);
+ FloatingPointControl fpctrl;
+ fpctrl.rounding = rm;
+ T x = -1;
+ blockopt(x); // avoid constant propagation by the optimizer
+ x -= 0.1L;
+ return x;
+ }
+
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ /* Be careful with changing the rounding mode, it interferes
+ * with common subexpressions. Changing rounding modes should
+ * be done with separate functions that are not inlined.
+ */
+
+ {
+ T u = addRound!(T)(FloatingPointControl.roundUp);
+ T d = addRound!(T)(FloatingPointControl.roundDown);
+ T z = addRound!(T)(FloatingPointControl.roundToZero);
+
+ assert(u > d);
+ assert(z == d);
+ }
+
+ {
+ T u = subRound!(T)(FloatingPointControl.roundUp);
+ T d = subRound!(T)(FloatingPointControl.roundDown);
+ T z = subRound!(T)(FloatingPointControl.roundToZero);
+
+ assert(u > d);
+ assert(z == u);
+ }
+ }}
+}
+
+}
diff --git a/libphobos/src/std/math/operations.d b/libphobos/src/std/math/operations.d
new file mode 100644
index 00000000000..dfb1aeea587
--- /dev/null
+++ b/libphobos/src/std/math/operations.d
@@ -0,0 +1,1998 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, math).
+
+It contains several functions for work with floating point numbers.
+
+Copyright: Copyright The D Language Foundation 2000 - 2011.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
+ Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
+Source: $(PHOBOSSRC std/math/operations.d)
+
+Macros:
+ TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
+ <caption>Special Values</caption>
+ $0</table>
+ SVH = $(TR $(TH $1) $(TH $2))
+ SV = $(TR $(TD $1) $(TD $2))
+ NAN = $(RED NAN)
+ PLUSMN = &plusmn;
+ INFIN = &infin;
+ LT = &lt;
+ GT = &gt;
+ */
+
+module std.math.operations;
+
+import std.traits : CommonType, isFloatingPoint, isIntegral, Unqual;
+
+// Functions for NaN payloads
+/*
+ * A 'payload' can be stored in the significand of a $(NAN). One bit is required
+ * to distinguish between a quiet and a signalling $(NAN). This leaves 22 bits
+ * of payload for a float; 51 bits for a double; 62 bits for an 80-bit real;
+ * and 111 bits for a 128-bit quad.
+*/
+/**
+ * Create a quiet $(NAN), storing an integer inside the payload.
+ *
+ * For floats, the largest possible payload is 0x3F_FFFF.
+ * For doubles, it is 0x3_FFFF_FFFF_FFFF.
+ * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF.
+ */
+real NaN(ulong payload) @trusted pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+
+ alias F = floatTraits!(real);
+ static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ // real80 (in x86 real format, the implied bit is actually
+ // not implied but a real bit which is stored in the real)
+ ulong v = 3; // implied bit = 1, quiet bit = 1
+ }
+ else
+ {
+ ulong v = 1; // no implied bit. quiet bit = 1
+ }
+ if (__ctfe)
+ {
+ v = 1; // We use a double in CTFE.
+ assert(payload >>> 51 == 0,
+ "Cannot set more than 51 bits of NaN payload in CTFE.");
+ }
+
+
+ ulong a = payload;
+
+ // 22 Float bits
+ ulong w = a & 0x3F_FFFF;
+ a -= w;
+
+ v <<=22;
+ v |= w;
+ a >>=22;
+
+ // 29 Double bits
+ v <<=29;
+ w = a & 0xFFF_FFFF;
+ v |= w;
+ a -= w;
+ a >>=29;
+
+ if (__ctfe)
+ {
+ v |= 0x7FF0_0000_0000_0000;
+ return *cast(double*) &v;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ v |= 0x7FF0_0000_0000_0000;
+ real x;
+ * cast(ulong *)(&x) = v;
+ return x;
+ }
+ else
+ {
+ v <<=11;
+ a &= 0x7FF;
+ v |= a;
+ real x = real.nan;
+
+ // Extended real bits
+ static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ v <<= 1; // there's no implicit bit
+
+ version (LittleEndian)
+ {
+ *cast(ulong*)(6+cast(ubyte*)(&x)) = v;
+ }
+ else
+ {
+ *cast(ulong*)(2+cast(ubyte*)(&x)) = v;
+ }
+ }
+ else
+ {
+ *cast(ulong *)(&x) = v;
+ }
+ return x;
+ }
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ import std.math.traits : isNaN;
+
+ real a = NaN(1_000_000);
+ assert(isNaN(a));
+ assert(getNaNPayload(a) == 1_000_000);
+}
+
+@system pure nothrow @nogc unittest // not @safe because taking address of local.
+{
+ import std.math : floatTraits, RealFormat;
+
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble)
+ {
+ auto x = NaN(1);
+ auto xl = *cast(ulong*)&x;
+ assert(xl & 0x8_0000_0000_0000UL); //non-signaling bit, bit 52
+ assert((xl & 0x7FF0_0000_0000_0000UL) == 0x7FF0_0000_0000_0000UL); //all exp bits set
+ }
+}
+
+/**
+ * Extract an integral payload from a $(NAN).
+ *
+ * Returns:
+ * the integer payload as a ulong.
+ *
+ * For floats, the largest possible payload is 0x3F_FFFF.
+ * For doubles, it is 0x3_FFFF_FFFF_FFFF.
+ * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF.
+ */
+ulong getNaNPayload(real x) @trusted pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+
+ // assert(isNaN(x));
+ alias F = floatTraits!(real);
+ ulong m = void;
+ if (__ctfe)
+ {
+ double y = x;
+ m = *cast(ulong*) &y;
+ // Make it look like an 80-bit significand.
+ // Skip exponent, and quiet bit
+ m &= 0x0007_FFFF_FFFF_FFFF;
+ m <<= 11;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ m = *cast(ulong*)(&x);
+ // Make it look like an 80-bit significand.
+ // Skip exponent, and quiet bit
+ m &= 0x0007_FFFF_FFFF_FFFF;
+ m <<= 11;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ version (LittleEndian)
+ {
+ m = *cast(ulong*)(6+cast(ubyte*)(&x));
+ }
+ else
+ {
+ m = *cast(ulong*)(2+cast(ubyte*)(&x));
+ }
+
+ m >>= 1; // there's no implicit bit
+ }
+ else
+ {
+ m = *cast(ulong*)(&x);
+ }
+
+ // ignore implicit bit and quiet bit
+
+ const ulong f = m & 0x3FFF_FF00_0000_0000L;
+
+ ulong w = f >>> 40;
+ w |= (m & 0x00FF_FFFF_F800L) << (22 - 11);
+ w |= (m & 0x7FF) << 51;
+ return w;
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ import std.math.traits : isNaN;
+
+ real a = NaN(1_000_000);
+ assert(isNaN(a));
+ assert(getNaNPayload(a) == 1_000_000);
+}
+
+@safe @nogc pure nothrow unittest
+{
+ import std.math.traits : isIdentical, isNaN;
+
+ enum real a = NaN(1_000_000);
+ static assert(isNaN(a));
+ static assert(getNaNPayload(a) == 1_000_000);
+ real b = NaN(1_000_000);
+ assert(isIdentical(b, a));
+ // The CTFE version of getNaNPayload relies on it being impossible
+ // for a CTFE-constructed NaN to have more than 51 bits of payload.
+ enum nanNaN = NaN(getNaNPayload(real.nan));
+ assert(isIdentical(real.nan, nanNaN));
+ static if (real.init != real.init)
+ {
+ enum initNaN = NaN(getNaNPayload(real.init));
+ assert(isIdentical(real.init, initNaN));
+ }
+}
+
+debug(UnitTest)
+{
+ @safe pure nothrow @nogc unittest
+ {
+ real nan4 = NaN(0x789_ABCD_EF12_3456);
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended
+ || floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
+ {
+ assert(getNaNPayload(nan4) == 0x789_ABCD_EF12_3456);
+ }
+ else
+ {
+ assert(getNaNPayload(nan4) == 0x1_ABCD_EF12_3456);
+ }
+ double nan5 = nan4;
+ assert(getNaNPayload(nan5) == 0x1_ABCD_EF12_3456);
+ float nan6 = nan4;
+ assert(getNaNPayload(nan6) == 0x12_3456);
+ nan4 = NaN(0xFABCD);
+ assert(getNaNPayload(nan4) == 0xFABCD);
+ nan6 = nan4;
+ assert(getNaNPayload(nan6) == 0xFABCD);
+ nan5 = NaN(0x100_0000_0000_3456);
+ assert(getNaNPayload(nan5) == 0x0000_0000_3456);
+ }
+}
+
+/**
+ * Calculate the next largest floating point value after x.
+ *
+ * Return the least number greater than x that is representable as a real;
+ * thus, it gives the next point on the IEEE number line.
+ *
+ * $(TABLE_SV
+ * $(SVH x, nextUp(x) )
+ * $(SV -$(INFIN), -real.max )
+ * $(SV $(PLUSMN)0.0, real.min_normal*real.epsilon )
+ * $(SV real.max, $(INFIN) )
+ * $(SV $(INFIN), $(INFIN) )
+ * $(SV $(NAN), $(NAN) )
+ * )
+ */
+real nextUp(real x) @trusted pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB;
+
+ alias F = floatTraits!(real);
+ static if (F.realFormat != RealFormat.ieeeDouble)
+ {
+ if (__ctfe)
+ {
+ if (x == -real.infinity)
+ return -real.max;
+ if (!(x < real.infinity)) // Infinity or NaN.
+ return x;
+ real delta;
+ // Start with a decent estimate of delta.
+ if (x <= 0x1.ffffffffffffep+1023 && x >= -double.max)
+ {
+ const double d = cast(double) x;
+ delta = (cast(real) nextUp(d) - cast(real) d) * 0x1p-11L;
+ while (x + (delta * 0x1p-100L) > x)
+ delta *= 0x1p-100L;
+ }
+ else
+ {
+ delta = 0x1p960L;
+ while (!(x + delta > x) && delta < real.max * 0x1p-100L)
+ delta *= 0x1p100L;
+ }
+ if (x + delta > x)
+ {
+ while (x + (delta / 2) > x)
+ delta /= 2;
+ }
+ else
+ {
+ do { delta += delta; } while (!(x + delta > x));
+ }
+ if (x < 0 && x + delta == 0)
+ return -0.0L;
+ return x + delta;
+ }
+ }
+ static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ return nextUp(cast(double) x);
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
+ if (e == F.EXPMASK)
+ {
+ // NaN or Infinity
+ if (x == -real.infinity) return -real.max;
+ return x; // +Inf and NaN are unchanged.
+ }
+
+ auto ps = cast(ulong *)&x;
+ if (ps[MANTISSA_MSB] & 0x8000_0000_0000_0000)
+ {
+ // Negative number
+ if (ps[MANTISSA_LSB] == 0 && ps[MANTISSA_MSB] == 0x8000_0000_0000_0000)
+ {
+ // it was negative zero, change to smallest subnormal
+ ps[MANTISSA_LSB] = 1;
+ ps[MANTISSA_MSB] = 0;
+ return x;
+ }
+ if (ps[MANTISSA_LSB] == 0) --ps[MANTISSA_MSB];
+ --ps[MANTISSA_LSB];
+ }
+ else
+ {
+ // Positive number
+ ++ps[MANTISSA_LSB];
+ if (ps[MANTISSA_LSB] == 0) ++ps[MANTISSA_MSB];
+ }
+ return x;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ // For 80-bit reals, the "implied bit" is a nuisance...
+ ushort *pe = cast(ushort *)&x;
+ ulong *ps = cast(ulong *)&x;
+ // EPSILON is 1 for 64-bit, and 2048 for 53-bit precision reals.
+ enum ulong EPSILON = 2UL ^^ (64 - real.mant_dig);
+
+ if ((pe[F.EXPPOS_SHORT] & F.EXPMASK) == F.EXPMASK)
+ {
+ // First, deal with NANs and infinity
+ if (x == -real.infinity) return -real.max;
+ return x; // +Inf and NaN are unchanged.
+ }
+ if (pe[F.EXPPOS_SHORT] & 0x8000)
+ {
+ // Negative number -- need to decrease the significand
+ *ps -= EPSILON;
+ // Need to mask with 0x7FFF... so subnormals are treated correctly.
+ if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_FFFF_FFFF_FFFF)
+ {
+ if (pe[F.EXPPOS_SHORT] == 0x8000) // it was negative zero
+ {
+ *ps = 1;
+ pe[F.EXPPOS_SHORT] = 0; // smallest subnormal.
+ return x;
+ }
+
+ --pe[F.EXPPOS_SHORT];
+
+ if (pe[F.EXPPOS_SHORT] == 0x8000)
+ return x; // it's become a subnormal, implied bit stays low.
+
+ *ps = 0xFFFF_FFFF_FFFF_FFFF; // set the implied bit
+ return x;
+ }
+ return x;
+ }
+ else
+ {
+ // Positive number -- need to increase the significand.
+ // Works automatically for positive zero.
+ *ps += EPSILON;
+ if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0)
+ {
+ // change in exponent
+ ++pe[F.EXPPOS_SHORT];
+ *ps = 0x8000_0000_0000_0000; // set the high bit
+ }
+ }
+ return x;
+ }
+ else // static if (F.realFormat == RealFormat.ibmExtended)
+ {
+ assert(0, "nextUp not implemented");
+ }
+}
+
+/** ditto */
+double nextUp(double x) @trusted pure nothrow @nogc
+{
+ ulong s = *cast(ulong *)&x;
+
+ if ((s & 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000)
+ {
+ // First, deal with NANs and infinity
+ if (x == -x.infinity) return -x.max;
+ return x; // +INF and NAN are unchanged.
+ }
+ if (s & 0x8000_0000_0000_0000) // Negative number
+ {
+ if (s == 0x8000_0000_0000_0000) // it was negative zero
+ {
+ s = 0x0000_0000_0000_0001; // change to smallest subnormal
+ return *cast(double*) &s;
+ }
+ --s;
+ }
+ else
+ { // Positive number
+ ++s;
+ }
+ return *cast(double*) &s;
+}
+
+/** ditto */
+float nextUp(float x) @trusted pure nothrow @nogc
+{
+ uint s = *cast(uint *)&x;
+
+ if ((s & 0x7F80_0000) == 0x7F80_0000)
+ {
+ // First, deal with NANs and infinity
+ if (x == -x.infinity) return -x.max;
+
+ return x; // +INF and NAN are unchanged.
+ }
+ if (s & 0x8000_0000) // Negative number
+ {
+ if (s == 0x8000_0000) // it was negative zero
+ {
+ s = 0x0000_0001; // change to smallest subnormal
+ return *cast(float*) &s;
+ }
+
+ --s;
+ }
+ else
+ {
+ // Positive number
+ ++s;
+ }
+ return *cast(float*) &s;
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ assert(nextUp(1.0 - 1.0e-6).feqrel(0.999999) > 16);
+ assert(nextUp(1.0 - real.epsilon).feqrel(1.0) > 16);
+}
+
+/**
+ * Calculate the next smallest floating point value before x.
+ *
+ * Return the greatest number less than x that is representable as a real;
+ * thus, it gives the previous point on the IEEE number line.
+ *
+ * $(TABLE_SV
+ * $(SVH x, nextDown(x) )
+ * $(SV $(INFIN), real.max )
+ * $(SV $(PLUSMN)0.0, -real.min_normal*real.epsilon )
+ * $(SV -real.max, -$(INFIN) )
+ * $(SV -$(INFIN), -$(INFIN) )
+ * $(SV $(NAN), $(NAN) )
+ * )
+ */
+real nextDown(real x) @safe pure nothrow @nogc
+{
+ return -nextUp(-x);
+}
+
+/** ditto */
+double nextDown(double x) @safe pure nothrow @nogc
+{
+ return -nextUp(-x);
+}
+
+/** ditto */
+float nextDown(float x) @safe pure nothrow @nogc
+{
+ return -nextUp(-x);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( nextDown(1.0 + real.epsilon) == 1.0);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.traits : isIdentical;
+
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended ||
+ floatTraits!(real).realFormat == RealFormat.ieeeDouble ||
+ floatTraits!(real).realFormat == RealFormat.ieeeExtended53 ||
+ floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
+ {
+ // Tests for reals
+ assert(isIdentical(nextUp(NaN(0xABC)), NaN(0xABC)));
+ //static assert(isIdentical(nextUp(NaN(0xABC)), NaN(0xABC)));
+ // negative numbers
+ assert( nextUp(-real.infinity) == -real.max );
+ assert( nextUp(-1.0L-real.epsilon) == -1.0 );
+ assert( nextUp(-2.0L) == -2.0 + real.epsilon);
+ static assert( nextUp(-real.infinity) == -real.max );
+ static assert( nextUp(-1.0L-real.epsilon) == -1.0 );
+ static assert( nextUp(-2.0L) == -2.0 + real.epsilon);
+ // subnormals and zero
+ assert( nextUp(-real.min_normal) == -real.min_normal*(1-real.epsilon) );
+ assert( nextUp(-real.min_normal*(1-real.epsilon)) == -real.min_normal*(1-2*real.epsilon) );
+ assert( isIdentical(-0.0L, nextUp(-real.min_normal*real.epsilon)) );
+ assert( nextUp(-0.0L) == real.min_normal*real.epsilon );
+ assert( nextUp(0.0L) == real.min_normal*real.epsilon );
+ assert( nextUp(real.min_normal*(1-real.epsilon)) == real.min_normal );
+ assert( nextUp(real.min_normal) == real.min_normal*(1+real.epsilon) );
+ static assert( nextUp(-real.min_normal) == -real.min_normal*(1-real.epsilon) );
+ static assert( nextUp(-real.min_normal*(1-real.epsilon)) == -real.min_normal*(1-2*real.epsilon) );
+ static assert( -0.0L is nextUp(-real.min_normal*real.epsilon) );
+ static assert( nextUp(-0.0L) == real.min_normal*real.epsilon );
+ static assert( nextUp(0.0L) == real.min_normal*real.epsilon );
+ static assert( nextUp(real.min_normal*(1-real.epsilon)) == real.min_normal );
+ static assert( nextUp(real.min_normal) == real.min_normal*(1+real.epsilon) );
+ // positive numbers
+ assert( nextUp(1.0L) == 1.0 + real.epsilon );
+ assert( nextUp(2.0L-real.epsilon) == 2.0 );
+ assert( nextUp(real.max) == real.infinity );
+ assert( nextUp(real.infinity)==real.infinity );
+ static assert( nextUp(1.0L) == 1.0 + real.epsilon );
+ static assert( nextUp(2.0L-real.epsilon) == 2.0 );
+ static assert( nextUp(real.max) == real.infinity );
+ static assert( nextUp(real.infinity)==real.infinity );
+ // ctfe near double.max boundary
+ static assert(nextUp(nextDown(cast(real) double.max)) == cast(real) double.max);
+ }
+
+ double n = NaN(0xABC);
+ assert(isIdentical(nextUp(n), n));
+ // negative numbers
+ assert( nextUp(-double.infinity) == -double.max );
+ assert( nextUp(-1-double.epsilon) == -1.0 );
+ assert( nextUp(-2.0) == -2.0 + double.epsilon);
+ // subnormals and zero
+
+ assert( nextUp(-double.min_normal) == -double.min_normal*(1-double.epsilon) );
+ assert( nextUp(-double.min_normal*(1-double.epsilon)) == -double.min_normal*(1-2*double.epsilon) );
+ assert( isIdentical(-0.0, nextUp(-double.min_normal*double.epsilon)) );
+ assert( nextUp(0.0) == double.min_normal*double.epsilon );
+ assert( nextUp(-0.0) == double.min_normal*double.epsilon );
+ assert( nextUp(double.min_normal*(1-double.epsilon)) == double.min_normal );
+ assert( nextUp(double.min_normal) == double.min_normal*(1+double.epsilon) );
+ // positive numbers
+ assert( nextUp(1.0) == 1.0 + double.epsilon );
+ assert( nextUp(2.0-double.epsilon) == 2.0 );
+ assert( nextUp(double.max) == double.infinity );
+
+ float fn = NaN(0xABC);
+ assert(isIdentical(nextUp(fn), fn));
+ float f = -float.min_normal*(1-float.epsilon);
+ float f1 = -float.min_normal;
+ assert( nextUp(f1) == f);
+ f = 1.0f+float.epsilon;
+ f1 = 1.0f;
+ assert( nextUp(f1) == f );
+ f1 = -0.0f;
+ assert( nextUp(f1) == float.min_normal*float.epsilon);
+ assert( nextUp(float.infinity)==float.infinity );
+
+ assert(nextDown(1.0L+real.epsilon)==1.0);
+ assert(nextDown(1.0+double.epsilon)==1.0);
+ f = 1.0f+float.epsilon;
+ assert(nextDown(f)==1.0);
+ assert(nextafter(1.0+real.epsilon, -real.infinity)==1.0);
+
+ // CTFE
+
+ enum double ctfe_n = NaN(0xABC);
+ //static assert(isIdentical(nextUp(ctfe_n), ctfe_n)); // FIXME: https://issues.dlang.org/show_bug.cgi?id=20197
+ static assert(nextUp(double.nan) is double.nan);
+ // negative numbers
+ static assert( nextUp(-double.infinity) == -double.max );
+ static assert( nextUp(-1-double.epsilon) == -1.0 );
+ static assert( nextUp(-2.0) == -2.0 + double.epsilon);
+ // subnormals and zero
+
+ static assert( nextUp(-double.min_normal) == -double.min_normal*(1-double.epsilon) );
+ static assert( nextUp(-double.min_normal*(1-double.epsilon)) == -double.min_normal*(1-2*double.epsilon) );
+ static assert( -0.0 is nextUp(-double.min_normal*double.epsilon) );
+ static assert( nextUp(0.0) == double.min_normal*double.epsilon );
+ static assert( nextUp(-0.0) == double.min_normal*double.epsilon );
+ static assert( nextUp(double.min_normal*(1-double.epsilon)) == double.min_normal );
+ static assert( nextUp(double.min_normal) == double.min_normal*(1+double.epsilon) );
+ // positive numbers
+ static assert( nextUp(1.0) == 1.0 + double.epsilon );
+ static assert( nextUp(2.0-double.epsilon) == 2.0 );
+ static assert( nextUp(double.max) == double.infinity );
+
+ enum float ctfe_fn = NaN(0xABC);
+ //static assert(isIdentical(nextUp(ctfe_fn), ctfe_fn)); // FIXME: https://issues.dlang.org/show_bug.cgi?id=20197
+ static assert(nextUp(float.nan) is float.nan);
+ static assert(nextUp(-float.min_normal) == -float.min_normal*(1-float.epsilon));
+ static assert(nextUp(1.0f) == 1.0f+float.epsilon);
+ static assert(nextUp(-0.0f) == float.min_normal*float.epsilon);
+ static assert(nextUp(float.infinity)==float.infinity);
+ static assert(nextDown(1.0L+real.epsilon)==1.0);
+ static assert(nextDown(1.0+double.epsilon)==1.0);
+ static assert(nextDown(1.0f+float.epsilon)==1.0);
+ static assert(nextafter(1.0+real.epsilon, -real.infinity)==1.0);
+}
+
+
+
+/******************************************
+ * Calculates the next representable value after x in the direction of y.
+ *
+ * If y > x, the result will be the next largest floating-point value;
+ * if y < x, the result will be the next smallest value.
+ * If x == y, the result is y.
+ * If x or y is a NaN, the result is a NaN.
+ *
+ * Remarks:
+ * This function is not generally very useful; it's almost always better to use
+ * the faster functions nextUp() or nextDown() instead.
+ *
+ * The FE_INEXACT and FE_OVERFLOW exceptions will be raised if x is finite and
+ * the function result is infinite. The FE_INEXACT and FE_UNDERFLOW
+ * exceptions will be raised if the function value is subnormal, and x is
+ * not equal to y.
+ */
+T nextafter(T)(const T x, const T y) @safe pure nothrow @nogc
+{
+ import std.math.traits : isNaN;
+
+ if (x == y || isNaN(y))
+ {
+ return y;
+ }
+
+ if (isNaN(x))
+ {
+ return x;
+ }
+
+ return ((y>x) ? nextUp(x) : nextDown(x));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ float a = 1;
+ assert(is(typeof(nextafter(a, a)) == float));
+ assert(nextafter(a, a.infinity) > a);
+ assert(isNaN(nextafter(a, a.nan)));
+ assert(isNaN(nextafter(a.nan, a)));
+
+ double b = 2;
+ assert(is(typeof(nextafter(b, b)) == double));
+ assert(nextafter(b, b.infinity) > b);
+ assert(isNaN(nextafter(b, b.nan)));
+ assert(isNaN(nextafter(b.nan, b)));
+
+ real c = 3;
+ assert(is(typeof(nextafter(c, c)) == real));
+ assert(nextafter(c, c.infinity) > c);
+ assert(isNaN(nextafter(c, c.nan)));
+ assert(isNaN(nextafter(c.nan, c)));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN, signbit;
+
+ // CTFE
+ enum float a = 1;
+ static assert(is(typeof(nextafter(a, a)) == float));
+ static assert(nextafter(a, a.infinity) > a);
+ static assert(isNaN(nextafter(a, a.nan)));
+ static assert(isNaN(nextafter(a.nan, a)));
+
+ enum double b = 2;
+ static assert(is(typeof(nextafter(b, b)) == double));
+ static assert(nextafter(b, b.infinity) > b);
+ static assert(isNaN(nextafter(b, b.nan)));
+ static assert(isNaN(nextafter(b.nan, b)));
+
+ enum real c = 3;
+ static assert(is(typeof(nextafter(c, c)) == real));
+ static assert(nextafter(c, c.infinity) > c);
+ static assert(isNaN(nextafter(c, c.nan)));
+ static assert(isNaN(nextafter(c.nan, c)));
+
+ enum real negZero = nextafter(+0.0L, -0.0L);
+ static assert(negZero == -0.0L);
+ static assert(signbit(negZero));
+
+ static assert(nextafter(c, c) == c);
+}
+
+//real nexttoward(real x, real y) { return core.stdc.math.nexttowardl(x, y); }
+
+/**
+ * Returns the positive difference between x and y.
+ *
+ * Equivalent to `fmax(x-y, 0)`.
+ *
+ * Returns:
+ * $(TABLE_SV
+ * $(TR $(TH x, y) $(TH fdim(x, y)))
+ * $(TR $(TD x $(GT) y) $(TD x - y))
+ * $(TR $(TD x $(LT)= y) $(TD +0.0))
+ * )
+ */
+real fdim(real x, real y) @safe pure nothrow @nogc
+{
+ return (x < y) ? +0.0 : x - y;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(fdim(2.0, 0.0) == 2.0);
+ assert(fdim(-2.0, 0.0) == 0.0);
+ assert(fdim(real.infinity, 2.0) == real.infinity);
+ assert(isNaN(fdim(real.nan, 2.0)));
+ assert(isNaN(fdim(2.0, real.nan)));
+ assert(isNaN(fdim(real.nan, real.nan)));
+}
+
+/**
+ * Returns the larger of `x` and `y`.
+ *
+ * If one of the arguments is a `NaN`, the other is returned.
+ *
+ * See_Also: $(REF max, std,algorithm,comparison) is faster because it does not perform the `isNaN` test.
+ */
+F fmax(F)(const F x, const F y) @safe pure nothrow @nogc
+if (__traits(isFloating, F))
+{
+ import std.math.traits : isNaN;
+
+ // Do the more predictable test first. Generates 0 branches with ldc and 1 branch with gdc.
+ // See https://godbolt.org/z/erxrW9
+ if (isNaN(x)) return y;
+ return y > x ? y : x;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (F; AliasSeq!(float, double, real))
+ {
+ assert(fmax(F(0.0), F(2.0)) == 2.0);
+ assert(fmax(F(-2.0), 0.0) == F(0.0));
+ assert(fmax(F.infinity, F(2.0)) == F.infinity);
+ assert(fmax(F.nan, F(2.0)) == F(2.0));
+ assert(fmax(F(2.0), F.nan) == F(2.0));
+ }
+}
+
+/**
+ * Returns the smaller of `x` and `y`.
+ *
+ * If one of the arguments is a `NaN`, the other is returned.
+ *
+ * See_Also: $(REF min, std,algorithm,comparison) is faster because it does not perform the `isNaN` test.
+ */
+F fmin(F)(const F x, const F y) @safe pure nothrow @nogc
+if (__traits(isFloating, F))
+{
+ import std.math.traits : isNaN;
+
+ // Do the more predictable test first. Generates 0 branches with ldc and 1 branch with gdc.
+ // See https://godbolt.org/z/erxrW9
+ if (isNaN(x)) return y;
+ return y < x ? y : x;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (F; AliasSeq!(float, double, real))
+ {
+ assert(fmin(F(0.0), F(2.0)) == 0.0);
+ assert(fmin(F(-2.0), F(0.0)) == -2.0);
+ assert(fmin(F.infinity, F(2.0)) == 2.0);
+ assert(fmin(F.nan, F(2.0)) == 2.0);
+ assert(fmin(F(2.0), F.nan) == 2.0);
+ }
+}
+
+/**************************************
+ * Returns (x * y) + z, rounding only once according to the
+ * current rounding mode.
+ *
+ * BUGS: Not currently implemented - rounds twice.
+ */
+pragma(inline, true)
+real fma(real x, real y, real z) @safe pure nothrow @nogc { return (x * y) + z; }
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(fma(0.0, 2.0, 2.0) == 2.0);
+ assert(fma(2.0, 2.0, 2.0) == 6.0);
+ assert(fma(real.infinity, 2.0, 2.0) == real.infinity);
+ assert(fma(real.nan, 2.0, 2.0) is real.nan);
+ assert(fma(2.0, 2.0, real.nan) is real.nan);
+}
+
+/**************************************
+ * To what precision is x equal to y?
+ *
+ * Returns: the number of mantissa bits which are equal in x and y.
+ * eg, 0x1.F8p+60 and 0x1.F1p+60 are equal to 5 bits of precision.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH feqrel(x, y)))
+ * $(TR $(TD x) $(TD x) $(TD real.mant_dig))
+ * $(TR $(TD x) $(TD $(GT)= 2*x) $(TD 0))
+ * $(TR $(TD x) $(TD $(LT)= x/2) $(TD 0))
+ * $(TR $(TD $(NAN)) $(TD any) $(TD 0))
+ * $(TR $(TD any) $(TD $(NAN)) $(TD 0))
+ * )
+ */
+int feqrel(X)(const X x, const X y) @trusted pure nothrow @nogc
+if (isFloatingPoint!(X))
+{
+ import std.math : floatTraits, RealFormat;
+ import core.math : fabs;
+
+ /* Public Domain. Author: Don Clugston, 18 Aug 2005.
+ */
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ieeeSingle
+ || F.realFormat == RealFormat.ieeeDouble
+ || F.realFormat == RealFormat.ieeeExtended
+ || F.realFormat == RealFormat.ieeeExtended53
+ || F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ if (x == y)
+ return X.mant_dig; // ensure diff != 0, cope with INF.
+
+ Unqual!X diff = fabs(x - y);
+
+ ushort *pa = cast(ushort *)(&x);
+ ushort *pb = cast(ushort *)(&y);
+ ushort *pd = cast(ushort *)(&diff);
+
+
+ // The difference in abs(exponent) between x or y and abs(x-y)
+ // is equal to the number of significand bits of x which are
+ // equal to y. If negative, x and y have different exponents.
+ // If positive, x and y are equal to 'bitsdiff' bits.
+ // AND with 0x7FFF to form the absolute value.
+ // To avoid out-by-1 errors, we subtract 1 so it rounds down
+ // if the exponents were different. This means 'bitsdiff' is
+ // always 1 lower than we want, except that if bitsdiff == 0,
+ // they could have 0 or 1 bits in common.
+
+ int bitsdiff = ((( (pa[F.EXPPOS_SHORT] & F.EXPMASK)
+ + (pb[F.EXPPOS_SHORT] & F.EXPMASK)
+ - (1 << F.EXPSHIFT)) >> 1)
+ - (pd[F.EXPPOS_SHORT] & F.EXPMASK)) >> F.EXPSHIFT;
+ if ( (pd[F.EXPPOS_SHORT] & F.EXPMASK) == 0)
+ { // Difference is subnormal
+ // For subnormals, we need to add the number of zeros that
+ // lie at the start of diff's significand.
+ // We do this by multiplying by 2^^real.mant_dig
+ diff *= F.RECIP_EPSILON;
+ return bitsdiff + X.mant_dig - ((pd[F.EXPPOS_SHORT] & F.EXPMASK) >> F.EXPSHIFT);
+ }
+
+ if (bitsdiff > 0)
+ return bitsdiff + 1; // add the 1 we subtracted before
+
+ // Avoid out-by-1 errors when factor is almost 2.
+ if (bitsdiff == 0
+ && ((pa[F.EXPPOS_SHORT] ^ pb[F.EXPPOS_SHORT]) & F.EXPMASK) == 0)
+ {
+ return 1;
+ } else return 0;
+ }
+ else
+ {
+ static assert(false, "Not implemented for this architecture");
+ }
+}
+
+///
+@safe pure unittest
+{
+ assert(feqrel(2.0, 2.0) == 53);
+ assert(feqrel(2.0f, 2.0f) == 24);
+ assert(feqrel(2.0, double.nan) == 0);
+
+ // Test that numbers are within n digits of each
+ // other by testing if feqrel > n * log2(10)
+
+ // five digits
+ assert(feqrel(2.0, 2.00001) > 16);
+ // ten digits
+ assert(feqrel(2.0, 2.00000000001) > 33);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ void testFeqrel(F)()
+ {
+ // Exact equality
+ assert(feqrel(F.max, F.max) == F.mant_dig);
+ assert(feqrel!(F)(0.0, 0.0) == F.mant_dig);
+ assert(feqrel(F.infinity, F.infinity) == F.mant_dig);
+
+ // a few bits away from exact equality
+ F w=1;
+ for (int i = 1; i < F.mant_dig - 1; ++i)
+ {
+ assert(feqrel!(F)(1.0 + w * F.epsilon, 1.0) == F.mant_dig-i);
+ assert(feqrel!(F)(1.0 - w * F.epsilon, 1.0) == F.mant_dig-i);
+ assert(feqrel!(F)(1.0, 1 + (w-1) * F.epsilon) == F.mant_dig - i + 1);
+ w*=2;
+ }
+
+ assert(feqrel!(F)(1.5+F.epsilon, 1.5) == F.mant_dig-1);
+ assert(feqrel!(F)(1.5-F.epsilon, 1.5) == F.mant_dig-1);
+ assert(feqrel!(F)(1.5-F.epsilon, 1.5+F.epsilon) == F.mant_dig-2);
+
+
+ // Numbers that are close
+ assert(feqrel!(F)(0x1.Bp+84, 0x1.B8p+84) == 5);
+ assert(feqrel!(F)(0x1.8p+10, 0x1.Cp+10) == 2);
+ assert(feqrel!(F)(1.5 * (1 - F.epsilon), 1.0L) == 2);
+ assert(feqrel!(F)(1.5, 1.0) == 1);
+ assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1);
+
+ // Factors of 2
+ assert(feqrel(F.max, F.infinity) == 0);
+ assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1);
+ assert(feqrel!(F)(1.0, 2.0) == 0);
+ assert(feqrel!(F)(4.0, 1.0) == 0);
+
+ // Extreme inequality
+ assert(feqrel(F.nan, F.nan) == 0);
+ assert(feqrel!(F)(0.0L, -F.nan) == 0);
+ assert(feqrel(F.nan, F.infinity) == 0);
+ assert(feqrel(F.infinity, -F.infinity) == 0);
+ assert(feqrel(F.max, -F.max) == 0);
+
+ assert(feqrel(F.min_normal / 8, F.min_normal / 17) == 3);
+
+ const F Const = 2;
+ immutable F Immutable = 2;
+ auto Compiles = feqrel(Const, Immutable);
+ }
+
+ assert(feqrel(7.1824L, 7.1824L) == real.mant_dig);
+
+ testFeqrel!(real)();
+ testFeqrel!(double)();
+ testFeqrel!(float)();
+}
+
+/**
+ Computes whether a values is approximately equal to a reference value,
+ admitting a maximum relative difference, and a maximum absolute difference.
+
+ Warning:
+ This template is considered out-dated. It will be removed from
+ Phobos in 2.106.0. Please use $(LREF isClose) instead. To achieve
+ a similar behaviour to `approxEqual(a, b)` use
+ `isClose(a, b, 1e-2, 1e-5)`. In case of comparing to 0.0,
+ `isClose(a, b, 0.0, eps)` should be used, where `eps`
+ represents the accepted deviation from 0.0."
+
+ Params:
+ value = Value to compare.
+ reference = Reference value.
+ maxRelDiff = Maximum allowable difference relative to `reference`.
+ Setting to 0.0 disables this check. Defaults to `1e-2`.
+ maxAbsDiff = Maximum absolute difference. This is mainly usefull
+ for comparing values to zero. Setting to 0.0 disables this check.
+ Defaults to `1e-5`.
+
+ Returns:
+ `true` if `value` is approximately equal to `reference` under
+ either criterium. It is sufficient, when `value ` satisfies
+ one of the two criteria.
+
+ If one item is a range, and the other is a single value, then
+ the result is the logical and-ing of calling `approxEqual` on
+ each element of the ranged item against the single item. If
+ both items are ranges, then `approxEqual` returns `true` if
+ and only if the ranges have the same number of elements and if
+ `approxEqual` evaluates to `true` for each pair of elements.
+
+ See_Also:
+ Use $(LREF feqrel) to get the number of equal bits in the mantissa.
+ */
+deprecated("approxEqual will be removed in 2.106.0. Please use isClose instead.")
+bool approxEqual(T, U, V)(T value, U reference, V maxRelDiff = 1e-2, V maxAbsDiff = 1e-5)
+{
+ import core.math : fabs;
+ import std.range.primitives : empty, front, isInputRange, popFront;
+ static if (isInputRange!T)
+ {
+ static if (isInputRange!U)
+ {
+ // Two ranges
+ for (;; value.popFront(), reference.popFront())
+ {
+ if (value.empty) return reference.empty;
+ if (reference.empty) return value.empty;
+ if (!approxEqual(value.front, reference.front, maxRelDiff, maxAbsDiff))
+ return false;
+ }
+ }
+ else static if (isIntegral!U)
+ {
+ // convert reference to real
+ return approxEqual(value, real(reference), maxRelDiff, maxAbsDiff);
+ }
+ else
+ {
+ // value is range, reference is number
+ for (; !value.empty; value.popFront())
+ {
+ if (!approxEqual(value.front, reference, maxRelDiff, maxAbsDiff))
+ return false;
+ }
+ return true;
+ }
+ }
+ else
+ {
+ static if (isInputRange!U)
+ {
+ // value is number, reference is range
+ for (; !reference.empty; reference.popFront())
+ {
+ if (!approxEqual(value, reference.front, maxRelDiff, maxAbsDiff))
+ return false;
+ }
+ return true;
+ }
+ else static if (isIntegral!T || isIntegral!U)
+ {
+ // convert both value and reference to real
+ return approxEqual(real(value), real(reference), maxRelDiff, maxAbsDiff);
+ }
+ else
+ {
+ // two numbers
+ //static assert(is(T : real) && is(U : real));
+ if (reference == 0)
+ {
+ return fabs(value) <= maxAbsDiff;
+ }
+ static if (is(typeof(value.infinity)) && is(typeof(reference.infinity)))
+ {
+ if (value == value.infinity && reference == reference.infinity ||
+ value == -value.infinity && reference == -reference.infinity) return true;
+ }
+ return fabs((value - reference) / reference) <= maxRelDiff
+ || maxAbsDiff != 0 && fabs(value - reference) <= maxAbsDiff;
+ }
+ }
+}
+
+deprecated @safe pure nothrow unittest
+{
+ assert(approxEqual(1.0, 1.0099));
+ assert(!approxEqual(1.0, 1.011));
+ assert(approxEqual(0.00001, 0.0));
+ assert(!approxEqual(0.00002, 0.0));
+
+ assert(approxEqual(3.0, [3, 3.01, 2.99])); // several reference values is strange
+ assert(approxEqual([3, 3.01, 2.99], 3.0)); // better
+
+ float[] arr1 = [ 1.0, 2.0, 3.0 ];
+ double[] arr2 = [ 1.001, 1.999, 3 ];
+ assert(approxEqual(arr1, arr2));
+}
+
+deprecated @safe pure nothrow unittest
+{
+ // relative comparison depends on reference, make sure proper
+ // side is used when comparing range to single value. Based on
+ // https://issues.dlang.org/show_bug.cgi?id=15763
+ auto a = [2e-3 - 1e-5];
+ auto b = 2e-3 + 1e-5;
+ assert(a[0].approxEqual(b));
+ assert(!b.approxEqual(a[0]));
+ assert(a.approxEqual(b));
+ assert(!b.approxEqual(a));
+}
+
+deprecated @safe pure nothrow @nogc unittest
+{
+ assert(!approxEqual(0.0,1e-15,1e-9,0.0));
+ assert(approxEqual(0.0,1e-15,1e-9,1e-9));
+ assert(!approxEqual(1.0,3.0,0.0,1.0));
+
+ assert(approxEqual(1.00000000099,1.0,1e-9,0.0));
+ assert(!approxEqual(1.0000000011,1.0,1e-9,0.0));
+}
+
+deprecated @safe pure nothrow @nogc unittest
+{
+ // maybe unintuitive behavior
+ assert(approxEqual(1000.0,1010.0));
+ assert(approxEqual(9_090_000_000.0,9_000_000_000.0));
+ assert(approxEqual(0.0,1e30,1.0));
+ assert(approxEqual(0.00001,1e-30));
+ assert(!approxEqual(-1e-30,1e-30,1e-2,0.0));
+}
+
+deprecated @safe pure nothrow @nogc unittest
+{
+ int a = 10;
+ assert(approxEqual(10, a));
+
+ assert(!approxEqual(3, 0));
+ assert(approxEqual(3, 3));
+ assert(approxEqual(3.0, 3));
+ assert(approxEqual(3, 3.0));
+
+ assert(approxEqual(0.0,0.0));
+ assert(approxEqual(-0.0,0.0));
+ assert(approxEqual(0.0f,0.0));
+}
+
+deprecated @safe pure nothrow @nogc unittest
+{
+ real num = real.infinity;
+ assert(num == real.infinity);
+ assert(approxEqual(num, real.infinity));
+ num = -real.infinity;
+ assert(num == -real.infinity);
+ assert(approxEqual(num, -real.infinity));
+
+ assert(!approxEqual(1,real.nan));
+ assert(!approxEqual(real.nan,real.max));
+ assert(!approxEqual(real.nan,real.nan));
+}
+
+deprecated @safe pure nothrow unittest
+{
+ assert(!approxEqual([1.0,2.0,3.0],[1.0,2.0]));
+ assert(!approxEqual([1.0,2.0],[1.0,2.0,3.0]));
+
+ assert(approxEqual!(real[],real[])([],[]));
+ assert(approxEqual(cast(real[])[],cast(real[])[]));
+}
+
+
+/**
+ Computes whether two values are approximately equal, admitting a maximum
+ relative difference, and a maximum absolute difference.
+
+ Params:
+ lhs = First item to compare.
+ rhs = Second item to compare.
+ maxRelDiff = Maximum allowable relative difference.
+ Setting to 0.0 disables this check. Default depends on the type of
+ `lhs` and `rhs`: It is approximately half the number of decimal digits of
+ precision of the smaller type.
+ maxAbsDiff = Maximum absolute difference. This is mainly usefull
+ for comparing values to zero. Setting to 0.0 disables this check.
+ Defaults to `0.0`.
+
+ Returns:
+ `true` if the two items are approximately equal under either criterium.
+ It is sufficient, when `value ` satisfies one of the two criteria.
+
+ If one item is a range, and the other is a single value, then
+ the result is the logical and-ing of calling `isClose` on
+ each element of the ranged item against the single item. If
+ both items are ranges, then `isClose` returns `true` if
+ and only if the ranges have the same number of elements and if
+ `isClose` evaluates to `true` for each pair of elements.
+
+ See_Also:
+ Use $(LREF feqrel) to get the number of equal bits in the mantissa.
+ */
+bool isClose(T, U, V = CommonType!(FloatingPointBaseType!T,FloatingPointBaseType!U))
+ (T lhs, U rhs, V maxRelDiff = CommonDefaultFor!(T,U), V maxAbsDiff = 0.0)
+{
+ import std.range.primitives : empty, front, isInputRange, popFront;
+ import std.complex : Complex;
+ static if (isInputRange!T)
+ {
+ static if (isInputRange!U)
+ {
+ // Two ranges
+ for (;; lhs.popFront(), rhs.popFront())
+ {
+ if (lhs.empty) return rhs.empty;
+ if (rhs.empty) return lhs.empty;
+ if (!isClose(lhs.front, rhs.front, maxRelDiff, maxAbsDiff))
+ return false;
+ }
+ }
+ else
+ {
+ // lhs is range, rhs is number
+ for (; !lhs.empty; lhs.popFront())
+ {
+ if (!isClose(lhs.front, rhs, maxRelDiff, maxAbsDiff))
+ return false;
+ }
+ return true;
+ }
+ }
+ else static if (isInputRange!U)
+ {
+ // lhs is number, rhs is range
+ for (; !rhs.empty; rhs.popFront())
+ {
+ if (!isClose(lhs, rhs.front, maxRelDiff, maxAbsDiff))
+ return false;
+ }
+ return true;
+ }
+ else static if (is(T TE == Complex!TE))
+ {
+ static if (is(U UE == Complex!UE))
+ {
+ // Two complex numbers
+ return isClose(lhs.re, rhs.re, maxRelDiff, maxAbsDiff)
+ && isClose(lhs.im, rhs.im, maxRelDiff, maxAbsDiff);
+ }
+ else
+ {
+ // lhs is complex, rhs is number
+ return isClose(lhs.re, rhs, maxRelDiff, maxAbsDiff)
+ && isClose(lhs.im, 0.0, maxRelDiff, maxAbsDiff);
+ }
+ }
+ else static if (is(U UE == Complex!UE))
+ {
+ // lhs is number, rhs is complex
+ return isClose(lhs, rhs.re, maxRelDiff, maxAbsDiff)
+ && isClose(0.0, rhs.im, maxRelDiff, maxAbsDiff);
+ }
+ else
+ {
+ // two numbers
+ if (lhs == rhs) return true;
+
+ static if (is(typeof(lhs.infinity)) && is(typeof(rhs.infinity)))
+ {
+ if (lhs == lhs.infinity || rhs == rhs.infinity ||
+ lhs == -lhs.infinity || rhs == -rhs.infinity) return false;
+ }
+
+ import std.math.algebraic : abs;
+
+ auto diff = abs(lhs - rhs);
+
+ return diff <= maxRelDiff*abs(lhs)
+ || diff <= maxRelDiff*abs(rhs)
+ || diff <= maxAbsDiff;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(isClose(1.0,0.999_999_999));
+ assert(isClose(0.001, 0.000_999_999_999));
+ assert(isClose(1_000_000_000.0,999_999_999.0));
+
+ assert(isClose(17.123_456_789, 17.123_456_78));
+ assert(!isClose(17.123_456_789, 17.123_45));
+
+ // use explicit 3rd parameter for less (or more) accuracy
+ assert(isClose(17.123_456_789, 17.123_45, 1e-6));
+ assert(!isClose(17.123_456_789, 17.123_45, 1e-7));
+
+ // use 4th parameter when comparing close to zero
+ assert(!isClose(1e-100, 0.0));
+ assert(isClose(1e-100, 0.0, 0.0, 1e-90));
+ assert(!isClose(1e-10, -1e-10));
+ assert(isClose(1e-10, -1e-10, 0.0, 1e-9));
+ assert(!isClose(1e-300, 1e-298));
+ assert(isClose(1e-300, 1e-298, 0.0, 1e-200));
+
+ // different default limits for different floating point types
+ assert(isClose(1.0f, 0.999_99f));
+ assert(!isClose(1.0, 0.999_99));
+ static if (real.sizeof > double.sizeof)
+ assert(!isClose(1.0L, 0.999_999_999L));
+}
+
+///
+@safe pure nothrow unittest
+{
+ assert(isClose([1.0, 2.0, 3.0], [0.999_999_999, 2.000_000_001, 3.0]));
+ assert(!isClose([1.0, 2.0], [0.999_999_999, 2.000_000_001, 3.0]));
+ assert(!isClose([1.0, 2.0, 3.0], [0.999_999_999, 2.000_000_001]));
+
+ assert(isClose([2.0, 1.999_999_999, 2.000_000_001], 2.0));
+ assert(isClose(2.0, [2.0, 1.999_999_999, 2.000_000_001]));
+}
+
+@safe pure nothrow unittest
+{
+ assert(!isClose([1.0, 2.0, 3.0], [0.999_999_999, 3.0, 3.0]));
+ assert(!isClose([2.0, 1.999_999, 2.000_000_001], 2.0));
+ assert(!isClose(2.0, [2.0, 1.999_999_999, 2.000_000_999]));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ immutable a = 1.00001f;
+ const b = 1.000019;
+ assert(isClose(a,b));
+
+ assert(isClose(1.00001f,1.000019f));
+ assert(isClose(1.00001f,1.000019));
+ assert(isClose(1.00001,1.000019f));
+ assert(!isClose(1.00001,1.000019));
+
+ real a1 = 1e-300L;
+ real a2 = a1.nextUp;
+ assert(isClose(a1,a2));
+}
+
+@safe pure nothrow unittest
+{
+ float[] arr1 = [ 1.0, 2.0, 3.0 ];
+ double[] arr2 = [ 1.00001, 1.99999, 3 ];
+ assert(isClose(arr1, arr2));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(!isClose(1000.0,1010.0));
+ assert(!isClose(9_090_000_000.0,9_000_000_000.0));
+ assert(isClose(0.0,1e30,1.0));
+ assert(!isClose(0.00001,1e-30));
+ assert(!isClose(-1e-30,1e-30,1e-2,0.0));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(!isClose(3, 0));
+ assert(isClose(3, 3));
+ assert(isClose(3.0, 3));
+ assert(isClose(3, 3.0));
+
+ assert(isClose(0.0,0.0));
+ assert(isClose(-0.0,0.0));
+ assert(isClose(0.0f,0.0));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ real num = real.infinity;
+ assert(num == real.infinity);
+ assert(isClose(num, real.infinity));
+ num = -real.infinity;
+ assert(num == -real.infinity);
+ assert(isClose(num, -real.infinity));
+
+ assert(!isClose(1,real.nan));
+ assert(!isClose(real.nan,real.max));
+ assert(!isClose(real.nan,real.nan));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(isClose!(real[],real[],real)([],[]));
+ assert(isClose(cast(real[])[],cast(real[])[]));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.conv : to;
+
+ float f = 31.79f;
+ double d = 31.79;
+ double f2d = f.to!double;
+
+ assert(isClose(f,f2d));
+ assert(!isClose(d,f2d));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.conv : to;
+
+ double d = 31.79;
+ float f = d.to!float;
+ double f2d = f.to!double;
+
+ assert(isClose(f,f2d));
+ assert(!isClose(d,f2d));
+ assert(isClose(d,f2d,1e-4));
+}
+
+package(std.math) template CommonDefaultFor(T,U)
+{
+ import std.algorithm.comparison : min;
+
+ alias baseT = FloatingPointBaseType!T;
+ alias baseU = FloatingPointBaseType!U;
+
+ enum CommonType!(baseT, baseU) CommonDefaultFor = 10.0L ^^ -((min(baseT.dig, baseU.dig) + 1) / 2 + 1);
+}
+
+private template FloatingPointBaseType(T)
+{
+ import std.range.primitives : ElementType;
+ static if (isFloatingPoint!T)
+ {
+ alias FloatingPointBaseType = Unqual!T;
+ }
+ else static if (isFloatingPoint!(ElementType!(Unqual!T)))
+ {
+ alias FloatingPointBaseType = Unqual!(ElementType!(Unqual!T));
+ }
+ else
+ {
+ alias FloatingPointBaseType = real;
+ }
+}
+
+/***********************************
+ * Defines a total order on all floating-point numbers.
+ *
+ * The order is defined as follows:
+ * $(UL
+ * $(LI All numbers in [-$(INFIN), +$(INFIN)] are ordered
+ * the same way as by built-in comparison, with the exception of
+ * -0.0, which is less than +0.0;)
+ * $(LI If the sign bit is set (that is, it's 'negative'), $(NAN) is less
+ * than any number; if the sign bit is not set (it is 'positive'),
+ * $(NAN) is greater than any number;)
+ * $(LI $(NAN)s of the same sign are ordered by the payload ('negative'
+ * ones - in reverse order).)
+ * )
+ *
+ * Returns:
+ * negative value if `x` precedes `y` in the order specified above;
+ * 0 if `x` and `y` are identical, and positive value otherwise.
+ *
+ * See_Also:
+ * $(MYREF isIdentical)
+ * Standards: Conforms to IEEE 754-2008
+ */
+int cmp(T)(const(T) x, const(T) y) @nogc @trusted pure nothrow
+if (isFloatingPoint!T)
+{
+ import std.math : floatTraits, RealFormat;
+
+ alias F = floatTraits!T;
+
+ static if (F.realFormat == RealFormat.ieeeSingle
+ || F.realFormat == RealFormat.ieeeDouble)
+ {
+ static if (T.sizeof == 4)
+ alias UInt = uint;
+ else
+ alias UInt = ulong;
+
+ union Repainter
+ {
+ T number;
+ UInt bits;
+ }
+
+ enum msb = ~(UInt.max >>> 1);
+
+ import std.typecons : Tuple;
+ Tuple!(Repainter, Repainter) vars = void;
+ vars[0].number = x;
+ vars[1].number = y;
+
+ foreach (ref var; vars)
+ if (var.bits & msb)
+ var.bits = ~var.bits;
+ else
+ var.bits |= msb;
+
+ if (vars[0].bits < vars[1].bits)
+ return -1;
+ else if (vars[0].bits > vars[1].bits)
+ return 1;
+ else
+ return 0;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended53
+ || F.realFormat == RealFormat.ieeeExtended
+ || F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ static if (F.realFormat == RealFormat.ieeeQuadruple)
+ alias RemT = ulong;
+ else
+ alias RemT = ushort;
+
+ struct Bits
+ {
+ ulong bulk;
+ RemT rem;
+ }
+
+ union Repainter
+ {
+ T number;
+ Bits bits;
+ ubyte[T.sizeof] bytes;
+ }
+
+ import std.typecons : Tuple;
+ Tuple!(Repainter, Repainter) vars = void;
+ vars[0].number = x;
+ vars[1].number = y;
+
+ foreach (ref var; vars)
+ if (var.bytes[F.SIGNPOS_BYTE] & 0x80)
+ {
+ var.bits.bulk = ~var.bits.bulk;
+ var.bits.rem = cast(typeof(var.bits.rem))(-1 - var.bits.rem); // ~var.bits.rem
+ }
+ else
+ {
+ var.bytes[F.SIGNPOS_BYTE] |= 0x80;
+ }
+
+ version (LittleEndian)
+ {
+ if (vars[0].bits.rem < vars[1].bits.rem)
+ return -1;
+ else if (vars[0].bits.rem > vars[1].bits.rem)
+ return 1;
+ else if (vars[0].bits.bulk < vars[1].bits.bulk)
+ return -1;
+ else if (vars[0].bits.bulk > vars[1].bits.bulk)
+ return 1;
+ else
+ return 0;
+ }
+ else
+ {
+ if (vars[0].bits.bulk < vars[1].bits.bulk)
+ return -1;
+ else if (vars[0].bits.bulk > vars[1].bits.bulk)
+ return 1;
+ else if (vars[0].bits.rem < vars[1].bits.rem)
+ return -1;
+ else if (vars[0].bits.rem > vars[1].bits.rem)
+ return 1;
+ else
+ return 0;
+ }
+ }
+ else
+ {
+ // IBM Extended doubledouble does not follow the general
+ // sign-exponent-significand layout, so has to be handled generically
+
+ import std.math.traits : signbit, isNaN;
+
+ const int xSign = signbit(x),
+ ySign = signbit(y);
+
+ if (xSign == 1 && ySign == 1)
+ return cmp(-y, -x);
+ else if (xSign == 1)
+ return -1;
+ else if (ySign == 1)
+ return 1;
+ else if (x < y)
+ return -1;
+ else if (x == y)
+ return 0;
+ else if (x > y)
+ return 1;
+ else if (isNaN(x) && !isNaN(y))
+ return 1;
+ else if (isNaN(y) && !isNaN(x))
+ return -1;
+ else if (getNaNPayload(x) < getNaNPayload(y))
+ return -1;
+ else if (getNaNPayload(x) > getNaNPayload(y))
+ return 1;
+ else
+ return 0;
+ }
+}
+
+/// Most numbers are ordered naturally.
+@safe unittest
+{
+ assert(cmp(-double.infinity, -double.max) < 0);
+ assert(cmp(-double.max, -100.0) < 0);
+ assert(cmp(-100.0, -0.5) < 0);
+ assert(cmp(-0.5, 0.0) < 0);
+ assert(cmp(0.0, 0.5) < 0);
+ assert(cmp(0.5, 100.0) < 0);
+ assert(cmp(100.0, double.max) < 0);
+ assert(cmp(double.max, double.infinity) < 0);
+
+ assert(cmp(1.0, 1.0) == 0);
+}
+
+/// Positive and negative zeroes are distinct.
+@safe unittest
+{
+ assert(cmp(-0.0, +0.0) < 0);
+ assert(cmp(+0.0, -0.0) > 0);
+}
+
+/// Depending on the sign, $(NAN)s go to either end of the spectrum.
+@safe unittest
+{
+ assert(cmp(-double.nan, -double.infinity) < 0);
+ assert(cmp(double.infinity, double.nan) < 0);
+ assert(cmp(-double.nan, double.nan) < 0);
+}
+
+/// $(NAN)s of the same sign are ordered by the payload.
+@safe unittest
+{
+ assert(cmp(NaN(10), NaN(20)) < 0);
+ assert(cmp(-NaN(20), -NaN(10)) < 0);
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ T[] values = [-cast(T) NaN(20), -cast(T) NaN(10), -T.nan, -T.infinity,
+ -T.max, -T.max / 2, T(-16.0), T(-1.0).nextDown,
+ T(-1.0), T(-1.0).nextUp,
+ T(-0.5), -T.min_normal, (-T.min_normal).nextUp,
+ -2 * T.min_normal * T.epsilon,
+ -T.min_normal * T.epsilon,
+ T(-0.0), T(0.0),
+ T.min_normal * T.epsilon,
+ 2 * T.min_normal * T.epsilon,
+ T.min_normal.nextDown, T.min_normal, T(0.5),
+ T(1.0).nextDown, T(1.0),
+ T(1.0).nextUp, T(16.0), T.max / 2, T.max,
+ T.infinity, T.nan, cast(T) NaN(10), cast(T) NaN(20)];
+
+ foreach (i, x; values)
+ {
+ foreach (y; values[i + 1 .. $])
+ {
+ assert(cmp(x, y) < 0);
+ assert(cmp(y, x) > 0);
+ }
+ assert(cmp(x, x) == 0);
+ }
+ }}
+}
+
+package(std): // not yet public
+
+struct FloatingPointBitpattern(T)
+if (isFloatingPoint!T)
+{
+ static if (T.mant_dig <= 64)
+ {
+ ulong mantissa;
+ }
+ else
+ {
+ ulong mantissa_lsb;
+ ulong mantissa_msb;
+ }
+
+ int exponent;
+ bool negative;
+}
+
+FloatingPointBitpattern!T extractBitpattern(T)(T val) @trusted
+if (isFloatingPoint!T)
+{
+ import std.math : floatTraits, RealFormat;
+
+ FloatingPointBitpattern!T ret;
+
+ alias F = floatTraits!T;
+ static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ if (__ctfe)
+ {
+ import core.math : fabs, ldexp;
+ import std.math.rounding : floor;
+ import std.math.traits : isInfinity, isNaN, signbit;
+ import std.math.exponential : log2;
+
+ if (isNaN(val) || isInfinity(val))
+ ret.exponent = 32767;
+ else if (fabs(val) < real.min_normal)
+ ret.exponent = 0;
+ else if (fabs(val) >= nextUp(real.max / 2))
+ ret.exponent = 32766;
+ else
+ ret.exponent = cast(int) (val.fabs.log2.floor() + 16383);
+
+ if (ret.exponent == 32767)
+ {
+ // NaN or infinity
+ ret.mantissa = isNaN(val) ? ((1L << 63) - 1) : 0;
+ }
+ else
+ {
+ auto delta = 16382 + 64 // bias + bits of ulong
+ - (ret.exponent == 0 ? 1 : ret.exponent); // -1 in case of subnormals
+ val = ldexp(val, delta); // val *= 2^^delta
+
+ ulong tmp = cast(ulong) fabs(val);
+ if (ret.exponent != 32767 && ret.exponent > 0 && tmp <= ulong.max / 2)
+ {
+ // correction, due to log2(val) being rounded up:
+ ret.exponent--;
+ val *= 2;
+ tmp = cast(ulong) fabs(val);
+ }
+
+ ret.mantissa = tmp & long.max;
+ }
+
+ ret.negative = (signbit(val) == 1);
+ }
+ else
+ {
+ ushort* vs = cast(ushort*) &val;
+ ret.mantissa = (cast(ulong*) vs)[0] & long.max;
+ ret.exponent = vs[4] & short.max;
+ ret.negative = (vs[4] >> 15) & 1;
+ }
+ }
+ else
+ {
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ ulong ival = *cast(uint*) &val;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ ulong ival = *cast(ulong*) &val;
+ }
+ else
+ {
+ static assert(false, "Floating point type `" ~ F.realFormat ~ "` not supported.");
+ }
+
+ import std.math.exponential : log2;
+ enum log2_max_exp = cast(int) log2(T.max_exp);
+
+ ret.mantissa = ival & ((1L << (T.mant_dig - 1)) - 1);
+ ret.exponent = (ival >> (T.mant_dig - 1)) & ((1L << (log2_max_exp + 1)) - 1);
+ ret.negative = (ival >> (T.mant_dig + log2_max_exp)) & 1;
+ }
+
+ // add leading 1 for normalized values and correct exponent for denormalied values
+ if (ret.exponent != 0 && ret.exponent != 2 * T.max_exp - 1)
+ ret.mantissa |= 1L << (T.mant_dig - 1);
+ else if (ret.exponent == 0)
+ ret.exponent = 1;
+
+ ret.exponent -= T.max_exp - 1;
+
+ return ret;
+}
+
+@safe pure unittest
+{
+ float f = 1.0f;
+ auto bp = extractBitpattern(f);
+ assert(bp.mantissa == 0x80_0000);
+ assert(bp.exponent == 0);
+ assert(bp.negative == false);
+
+ f = float.max;
+ bp = extractBitpattern(f);
+ assert(bp.mantissa == 0xff_ffff);
+ assert(bp.exponent == 127);
+ assert(bp.negative == false);
+
+ f = -1.5432e-17f;
+ bp = extractBitpattern(f);
+ assert(bp.mantissa == 0x8e_55c8);
+ assert(bp.exponent == -56);
+ assert(bp.negative == true);
+
+ // using double literal due to https://issues.dlang.org/show_bug.cgi?id=20361
+ f = 2.3822073893521890206e-44;
+ bp = extractBitpattern(f);
+ assert(bp.mantissa == 0x00_0011);
+ assert(bp.exponent == -126);
+ assert(bp.negative == false);
+
+ f = -float.infinity;
+ bp = extractBitpattern(f);
+ assert(bp.mantissa == 0);
+ assert(bp.exponent == 128);
+ assert(bp.negative == true);
+
+ f = float.nan;
+ bp = extractBitpattern(f);
+ assert(bp.mantissa != 0); // we don't guarantee payloads
+ assert(bp.exponent == 128);
+ assert(bp.negative == false);
+}
+
+@safe pure unittest
+{
+ double d = 1.0;
+ auto bp = extractBitpattern(d);
+ assert(bp.mantissa == 0x10_0000_0000_0000L);
+ assert(bp.exponent == 0);
+ assert(bp.negative == false);
+
+ d = double.max;
+ bp = extractBitpattern(d);
+ assert(bp.mantissa == 0x1f_ffff_ffff_ffffL);
+ assert(bp.exponent == 1023);
+ assert(bp.negative == false);
+
+ d = -1.5432e-222;
+ bp = extractBitpattern(d);
+ assert(bp.mantissa == 0x11_d9b6_a401_3b04L);
+ assert(bp.exponent == -737);
+ assert(bp.negative == true);
+
+ d = 0.0.nextUp;
+ bp = extractBitpattern(d);
+ assert(bp.mantissa == 0x00_0000_0000_0001L);
+ assert(bp.exponent == -1022);
+ assert(bp.negative == false);
+
+ d = -double.infinity;
+ bp = extractBitpattern(d);
+ assert(bp.mantissa == 0);
+ assert(bp.exponent == 1024);
+ assert(bp.negative == true);
+
+ d = double.nan;
+ bp = extractBitpattern(d);
+ assert(bp.mantissa != 0); // we don't guarantee payloads
+ assert(bp.exponent == 1024);
+ assert(bp.negative == false);
+}
+
+@safe pure unittest
+{
+ import std.math : floatTraits, RealFormat;
+
+ alias F = floatTraits!real;
+ static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ real r = 1.0L;
+ auto bp = extractBitpattern(r);
+ assert(bp.mantissa == 0x8000_0000_0000_0000L);
+ assert(bp.exponent == 0);
+ assert(bp.negative == false);
+
+ r = real.max;
+ bp = extractBitpattern(r);
+ assert(bp.mantissa == 0xffff_ffff_ffff_ffffL);
+ assert(bp.exponent == 16383);
+ assert(bp.negative == false);
+
+ r = -1.5432e-3333L;
+ bp = extractBitpattern(r);
+ assert(bp.mantissa == 0xc768_a2c7_a616_cc22L);
+ assert(bp.exponent == -11072);
+ assert(bp.negative == true);
+
+ r = 0.0L.nextUp;
+ bp = extractBitpattern(r);
+ assert(bp.mantissa == 0x0000_0000_0000_0001L);
+ assert(bp.exponent == -16382);
+ assert(bp.negative == false);
+
+ r = -float.infinity;
+ bp = extractBitpattern(r);
+ assert(bp.mantissa == 0);
+ assert(bp.exponent == 16384);
+ assert(bp.negative == true);
+
+ r = float.nan;
+ bp = extractBitpattern(r);
+ assert(bp.mantissa != 0); // we don't guarantee payloads
+ assert(bp.exponent == 16384);
+ assert(bp.negative == false);
+
+ r = nextDown(0x1p+16383L);
+ bp = extractBitpattern(r);
+ assert(bp.mantissa == 0xffff_ffff_ffff_ffffL);
+ assert(bp.exponent == 16382);
+ assert(bp.negative == false);
+ }
+}
+
+@safe pure unittest
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.exponential : log2;
+
+ alias F = floatTraits!real;
+
+ // log2 is broken for x87-reals on some computers in CTFE
+ // the following test excludes these computers from the test
+ // (issue 21757)
+ enum test = cast(int) log2(3.05e2312L);
+ static if (F.realFormat == RealFormat.ieeeExtended && test == 7681)
+ {
+ enum r1 = 1.0L;
+ enum bp1 = extractBitpattern(r1);
+ static assert(bp1.mantissa == 0x8000_0000_0000_0000L);
+ static assert(bp1.exponent == 0);
+ static assert(bp1.negative == false);
+
+ enum r2 = real.max;
+ enum bp2 = extractBitpattern(r2);
+ static assert(bp2.mantissa == 0xffff_ffff_ffff_ffffL);
+ static assert(bp2.exponent == 16383);
+ static assert(bp2.negative == false);
+
+ enum r3 = -1.5432e-3333L;
+ enum bp3 = extractBitpattern(r3);
+ static assert(bp3.mantissa == 0xc768_a2c7_a616_cc22L);
+ static assert(bp3.exponent == -11072);
+ static assert(bp3.negative == true);
+
+ enum r4 = 0.0L.nextUp;
+ enum bp4 = extractBitpattern(r4);
+ static assert(bp4.mantissa == 0x0000_0000_0000_0001L);
+ static assert(bp4.exponent == -16382);
+ static assert(bp4.negative == false);
+
+ enum r5 = -real.infinity;
+ enum bp5 = extractBitpattern(r5);
+ static assert(bp5.mantissa == 0);
+ static assert(bp5.exponent == 16384);
+ static assert(bp5.negative == true);
+
+ enum r6 = real.nan;
+ enum bp6 = extractBitpattern(r6);
+ static assert(bp6.mantissa != 0); // we don't guarantee payloads
+ static assert(bp6.exponent == 16384);
+ static assert(bp6.negative == false);
+
+ enum r7 = nextDown(0x1p+16383L);
+ enum bp7 = extractBitpattern(r7);
+ static assert(bp7.mantissa == 0xffff_ffff_ffff_ffffL);
+ static assert(bp7.exponent == 16382);
+ static assert(bp7.negative == false);
+ }
+}
diff --git a/libphobos/src/std/math/package.d b/libphobos/src/std/math/package.d
new file mode 100644
index 00000000000..7443b0dea2e
--- /dev/null
+++ b/libphobos/src/std/math/package.d
@@ -0,0 +1,494 @@
+// Written in the D programming language.
+
+/**
+ * Contains the elementary mathematical functions (powers, roots,
+ * and trigonometric functions), and low-level floating-point operations.
+ * Mathematical special functions are available in `std.mathspecial`.
+ *
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Members) )
+$(TR $(TDNW $(SUBMODULE Constants, constants)) $(TD
+ $(SUBREF constants, E)
+ $(SUBREF constants, PI)
+ $(SUBREF constants, PI_2)
+ $(SUBREF constants, PI_4)
+ $(SUBREF constants, M_1_PI)
+ $(SUBREF constants, M_2_PI)
+ $(SUBREF constants, M_2_SQRTPI)
+ $(SUBREF constants, LN10)
+ $(SUBREF constants, LN2)
+ $(SUBREF constants, LOG2)
+ $(SUBREF constants, LOG2E)
+ $(SUBREF constants, LOG2T)
+ $(SUBREF constants, LOG10E)
+ $(SUBREF constants, SQRT2)
+ $(SUBREF constants, SQRT1_2)
+))
+$(TR $(TDNW $(SUBMODULE Algebraic, algebraic)) $(TD
+ $(SUBREF algebraic, abs)
+ $(SUBREF algebraic, fabs)
+ $(SUBREF algebraic, sqrt)
+ $(SUBREF algebraic, cbrt)
+ $(SUBREF algebraic, hypot)
+ $(SUBREF algebraic, poly)
+ $(SUBREF algebraic, nextPow2)
+ $(SUBREF algebraic, truncPow2)
+))
+$(TR $(TDNW $(SUBMODULE Trigonometry, trigonometry)) $(TD
+ $(SUBREF trigonometry, sin)
+ $(SUBREF trigonometry, cos)
+ $(SUBREF trigonometry, tan)
+ $(SUBREF trigonometry, asin)
+ $(SUBREF trigonometry, acos)
+ $(SUBREF trigonometry, atan)
+ $(SUBREF trigonometry, atan2)
+ $(SUBREF trigonometry, sinh)
+ $(SUBREF trigonometry, cosh)
+ $(SUBREF trigonometry, tanh)
+ $(SUBREF trigonometry, asinh)
+ $(SUBREF trigonometry, acosh)
+ $(SUBREF trigonometry, atanh)
+))
+$(TR $(TDNW $(SUBMODULE Rounding, rounding)) $(TD
+ $(SUBREF rounding, ceil)
+ $(SUBREF rounding, floor)
+ $(SUBREF rounding, round)
+ $(SUBREF rounding, lround)
+ $(SUBREF rounding, trunc)
+ $(SUBREF rounding, rint)
+ $(SUBREF rounding, lrint)
+ $(SUBREF rounding, nearbyint)
+ $(SUBREF rounding, rndtol)
+ $(SUBREF rounding, quantize)
+))
+$(TR $(TDNW $(SUBMODULE Exponentiation & Logarithms, exponential)) $(TD
+ $(SUBREF exponential, pow)
+ $(SUBREF exponential, powmod)
+ $(SUBREF exponential, exp)
+ $(SUBREF exponential, exp2)
+ $(SUBREF exponential, expm1)
+ $(SUBREF exponential, ldexp)
+ $(SUBREF exponential, frexp)
+ $(SUBREF exponential, log)
+ $(SUBREF exponential, log2)
+ $(SUBREF exponential, log10)
+ $(SUBREF exponential, logb)
+ $(SUBREF exponential, ilogb)
+ $(SUBREF exponential, log1p)
+ $(SUBREF exponential, scalbn)
+))
+$(TR $(TDNW $(SUBMODULE Remainder, remainder)) $(TD
+ $(SUBREF remainder, fmod)
+ $(SUBREF remainder, modf)
+ $(SUBREF remainder, remainder)
+ $(SUBREF remainder, remquo)
+))
+$(TR $(TDNW $(SUBMODULE Floating-point operations, operations)) $(TD
+ $(SUBREF operations, approxEqual)
+ $(SUBREF operations, feqrel)
+ $(SUBREF operations, fdim)
+ $(SUBREF operations, fmax)
+ $(SUBREF operations, fmin)
+ $(SUBREF operations, fma)
+ $(SUBREF operations, isClose)
+ $(SUBREF operations, nextDown)
+ $(SUBREF operations, nextUp)
+ $(SUBREF operations, nextafter)
+ $(SUBREF operations, NaN)
+ $(SUBREF operations, getNaNPayload)
+ $(SUBREF operations, cmp)
+))
+$(TR $(TDNW $(SUBMODULE Introspection, traits)) $(TD
+ $(SUBREF traits, isFinite)
+ $(SUBREF traits, isIdentical)
+ $(SUBREF traits, isInfinity)
+ $(SUBREF traits, isNaN)
+ $(SUBREF traits, isNormal)
+ $(SUBREF traits, isSubnormal)
+ $(SUBREF traits, signbit)
+ $(SUBREF traits, sgn)
+ $(SUBREF traits, copysign)
+ $(SUBREF traits, isPowerOf2)
+))
+$(TR $(TDNW $(SUBMODULE Hardware Control, hardware)) $(TD
+ $(SUBREF hardware, IeeeFlags)
+ $(SUBREF hardware, ieeeFlags)
+ $(SUBREF hardware, resetIeeeFlags)
+ $(SUBREF hardware, FloatingPointControl)
+))
+)
+)
+
+ * The functionality closely follows the IEEE754-2008 standard for
+ * floating-point arithmetic, including the use of camelCase names rather
+ * than C99-style lower case names. All of these functions behave correctly
+ * when presented with an infinity or NaN.
+ *
+ * The following IEEE 'real' formats are currently supported:
+ * $(UL
+ * $(LI 64 bit Big-endian 'double' (eg PowerPC))
+ * $(LI 128 bit Big-endian 'quadruple' (eg SPARC))
+ * $(LI 64 bit Little-endian 'double' (eg x86-SSE2))
+ * $(LI 80 bit Little-endian, with implied bit 'real80' (eg x87, Itanium))
+ * $(LI 128 bit Little-endian 'quadruple' (not implemented on any known processor!))
+ * $(LI Non-IEEE 128 bit Big-endian 'doubledouble' (eg PowerPC) has partial support)
+ * )
+ * Unlike C, there is no global 'errno' variable. Consequently, almost all of
+ * these functions are pure nothrow.
+ *
+ * Macros:
+ * SUBMODULE = $(MREF_ALTTEXT $1, std, math, $2)
+ * SUBREF = $(REF_ALTTEXT $(TT $2), $2, std, math, $1)$(NBSP)
+ *
+ * Copyright: Copyright The D Language Foundation 2000 - 2011.
+ * D implementations of tan, atan, atan2, exp, expm1, exp2, log, log10, log1p,
+ * log2, floor, ceil and lrint functions are based on the CEPHES math library,
+ * which is Copyright (C) 2001 Stephen L. Moshier $(LT)steve@moshier.net$(GT)
+ * and are incorporated herein by permission of the author. The author
+ * reserves the right to distribute this material elsewhere under different
+ * copying permissions. These modifications are distributed here under
+ * the following terms:
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
+ * Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
+ * Source: $(PHOBOSSRC std/math/package.d)
+ */
+module std.math;
+
+public import std.math.algebraic;
+public import std.math.constants;
+public import std.math.exponential;
+public import std.math.operations;
+public import std.math.hardware;
+public import std.math.remainder;
+public import std.math.rounding;
+public import std.math.traits;
+public import std.math.trigonometry;
+
+// @@@DEPRECATED_2.102@@@
+// Note: Exposed accidentally, should be deprecated / removed
+deprecated("std.meta.AliasSeq was unintentionally available from std.math "
+ ~ "and will be removed after 2.102. Please import std.meta instead")
+public import std.meta : AliasSeq;
+
+package(std): // Not public yet
+/* Return the value that lies halfway between x and y on the IEEE number line.
+ *
+ * Formally, the result is the arithmetic mean of the binary significands of x
+ * and y, multiplied by the geometric mean of the binary exponents of x and y.
+ * x and y must have the same sign, and must not be NaN.
+ * Note: this function is useful for ensuring O(log n) behaviour in algorithms
+ * involving a 'binary chop'.
+ *
+ * Special cases:
+ * If x and y are within a factor of 2, (ie, feqrel(x, y) > 0), the return value
+ * is the arithmetic mean (x + y) / 2.
+ * If x and y are even powers of 2, the return value is the geometric mean,
+ * ieeeMean(x, y) = sqrt(x * y).
+ *
+ */
+T ieeeMean(T)(const T x, const T y) @trusted pure nothrow @nogc
+in
+{
+ // both x and y must have the same sign, and must not be NaN.
+ assert(signbit(x) == signbit(y));
+ assert(x == x && y == y);
+}
+do
+{
+ // Runtime behaviour for contract violation:
+ // If signs are opposite, or one is a NaN, return 0.
+ if (!((x >= 0 && y >= 0) || (x <= 0 && y <= 0))) return 0.0;
+
+ // The implementation is simple: cast x and y to integers,
+ // average them (avoiding overflow), and cast the result back to a floating-point number.
+
+ alias F = floatTraits!(T);
+ T u;
+ static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ // There's slight additional complexity because they are actually
+ // 79-bit reals...
+ ushort *ue = cast(ushort *)&u;
+ ulong *ul = cast(ulong *)&u;
+ ushort *xe = cast(ushort *)&x;
+ ulong *xl = cast(ulong *)&x;
+ ushort *ye = cast(ushort *)&y;
+ ulong *yl = cast(ulong *)&y;
+
+ // Ignore the useless implicit bit. (Bonus: this prevents overflows)
+ ulong m = ((*xl) & 0x7FFF_FFFF_FFFF_FFFFL) + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL);
+
+ // @@@ BUG? @@@
+ // Cast shouldn't be here
+ ushort e = cast(ushort) ((xe[F.EXPPOS_SHORT] & F.EXPMASK)
+ + (ye[F.EXPPOS_SHORT] & F.EXPMASK));
+ if (m & 0x8000_0000_0000_0000L)
+ {
+ ++e;
+ m &= 0x7FFF_FFFF_FFFF_FFFFL;
+ }
+ // Now do a multi-byte right shift
+ const uint c = e & 1; // carry
+ e >>= 1;
+ m >>>= 1;
+ if (c)
+ m |= 0x4000_0000_0000_0000L; // shift carry into significand
+ if (e)
+ *ul = m | 0x8000_0000_0000_0000L; // set implicit bit...
+ else
+ *ul = m; // ... unless exponent is 0 (subnormal or zero).
+
+ ue[4]= e | (xe[F.EXPPOS_SHORT]& 0x8000); // restore sign bit
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ // This would be trivial if 'ucent' were implemented...
+ ulong *ul = cast(ulong *)&u;
+ ulong *xl = cast(ulong *)&x;
+ ulong *yl = cast(ulong *)&y;
+
+ // Multi-byte add, then multi-byte right shift.
+ import core.checkedint : addu;
+ bool carry;
+ ulong ml = addu(xl[MANTISSA_LSB], yl[MANTISSA_LSB], carry);
+
+ ulong mh = carry + (xl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL) +
+ (yl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL);
+
+ ul[MANTISSA_MSB] = (mh >>> 1) | (xl[MANTISSA_MSB] & 0x8000_0000_0000_0000);
+ ul[MANTISSA_LSB] = (ml >>> 1) | (mh & 1) << 63;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ ulong *ul = cast(ulong *)&u;
+ ulong *xl = cast(ulong *)&x;
+ ulong *yl = cast(ulong *)&y;
+ ulong m = (((*xl) & 0x7FFF_FFFF_FFFF_FFFFL)
+ + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL)) >>> 1;
+ m |= ((*xl) & 0x8000_0000_0000_0000L);
+ *ul = m;
+ }
+ else static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ uint *ul = cast(uint *)&u;
+ uint *xl = cast(uint *)&x;
+ uint *yl = cast(uint *)&y;
+ uint m = (((*xl) & 0x7FFF_FFFF) + ((*yl) & 0x7FFF_FFFF)) >>> 1;
+ m |= ((*xl) & 0x8000_0000);
+ *ul = m;
+ }
+ else
+ {
+ assert(0, "Not implemented");
+ }
+ return u;
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(ieeeMean(-0.0,-1e-20)<0);
+ assert(ieeeMean(0.0,1e-20)>0);
+
+ assert(ieeeMean(1.0L,4.0L)==2L);
+ assert(ieeeMean(2.0*1.013,8.0*1.013)==4*1.013);
+ assert(ieeeMean(-1.0L,-4.0L)==-2L);
+ assert(ieeeMean(-1.0,-4.0)==-2);
+ assert(ieeeMean(-1.0f,-4.0f)==-2f);
+ assert(ieeeMean(-1.0,-2.0)==-1.5);
+ assert(ieeeMean(-1*(1+8*real.epsilon),-2*(1+8*real.epsilon))
+ ==-1.5*(1+5*real.epsilon));
+ assert(ieeeMean(0x1p60,0x1p-10)==0x1p25);
+
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
+ {
+ assert(ieeeMean(1.0L,real.infinity)==0x1p8192L);
+ assert(ieeeMean(0.0L,real.infinity)==1.5);
+ }
+ assert(ieeeMean(0.5*real.min_normal*(1-4*real.epsilon),0.5*real.min_normal)
+ == 0.5*real.min_normal*(1-2*real.epsilon));
+}
+
+
+// The following IEEE 'real' formats are currently supported.
+version (LittleEndian)
+{
+ static assert(real.mant_dig == 53 || real.mant_dig == 64
+ || real.mant_dig == 113,
+ "Only 64-bit, 80-bit, and 128-bit reals"~
+ " are supported for LittleEndian CPUs");
+}
+else
+{
+ static assert(real.mant_dig == 53 || real.mant_dig == 113,
+ "Only 64-bit and 128-bit reals are supported for BigEndian CPUs.");
+}
+
+// Underlying format exposed through floatTraits
+enum RealFormat
+{
+ ieeeHalf,
+ ieeeSingle,
+ ieeeDouble,
+ ieeeExtended, // x87 80-bit real
+ ieeeExtended53, // x87 real rounded to precision of double.
+ ibmExtended, // IBM 128-bit extended
+ ieeeQuadruple,
+}
+
+// Constants used for extracting the components of the representation.
+// They supplement the built-in floating point properties.
+template floatTraits(T)
+{
+ import std.traits : Unqual;
+
+ // EXPMASK is a ushort mask to select the exponent portion (without sign)
+ // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort
+ // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1).
+ // EXPPOS_SHORT is the index of the exponent when represented as a ushort array.
+ // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array.
+ // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal
+ enum Unqual!T RECIP_EPSILON = (1/T.epsilon);
+ static if (T.mant_dig == 24)
+ {
+ // Single precision float
+ enum ushort EXPMASK = 0x7F80;
+ enum ushort EXPSHIFT = 7;
+ enum ushort EXPBIAS = 0x3F00;
+ enum uint EXPMASK_INT = 0x7F80_0000;
+ enum uint MANTISSAMASK_INT = 0x007F_FFFF;
+ enum realFormat = RealFormat.ieeeSingle;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 1;
+ enum SIGNPOS_BYTE = 3;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.mant_dig == 53)
+ {
+ static if (T.sizeof == 8)
+ {
+ // Double precision float, or real == double
+ enum ushort EXPMASK = 0x7FF0;
+ enum ushort EXPSHIFT = 4;
+ enum ushort EXPBIAS = 0x3FE0;
+ enum uint EXPMASK_INT = 0x7FF0_0000;
+ enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only
+ enum realFormat = RealFormat.ieeeDouble;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 3;
+ enum SIGNPOS_BYTE = 7;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.sizeof == 12)
+ {
+ // Intel extended real80 rounded to double
+ enum ushort EXPMASK = 0x7FFF;
+ enum ushort EXPSHIFT = 0;
+ enum ushort EXPBIAS = 0x3FFE;
+ enum realFormat = RealFormat.ieeeExtended53;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 4;
+ enum SIGNPOS_BYTE = 9;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else
+ static assert(false, "No traits support for " ~ T.stringof);
+ }
+ else static if (T.mant_dig == 64)
+ {
+ // Intel extended real80
+ enum ushort EXPMASK = 0x7FFF;
+ enum ushort EXPSHIFT = 0;
+ enum ushort EXPBIAS = 0x3FFE;
+ enum realFormat = RealFormat.ieeeExtended;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 4;
+ enum SIGNPOS_BYTE = 9;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.mant_dig == 113)
+ {
+ // Quadruple precision float
+ enum ushort EXPMASK = 0x7FFF;
+ enum ushort EXPSHIFT = 0;
+ enum ushort EXPBIAS = 0x3FFE;
+ enum realFormat = RealFormat.ieeeQuadruple;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 7;
+ enum SIGNPOS_BYTE = 15;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.mant_dig == 106)
+ {
+ // IBM Extended doubledouble
+ enum ushort EXPMASK = 0x7FF0;
+ enum ushort EXPSHIFT = 4;
+ enum realFormat = RealFormat.ibmExtended;
+
+ // For IBM doubledouble the larger magnitude double comes first.
+ // It's really a double[2] and arrays don't index differently
+ // between little and big-endian targets.
+ enum DOUBLEPAIR_MSB = 0;
+ enum DOUBLEPAIR_LSB = 1;
+
+ // The exponent/sign byte is for most significant part.
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 3;
+ enum SIGNPOS_BYTE = 7;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else
+ static assert(false, "No traits support for " ~ T.stringof);
+}
+
+// These apply to all floating-point types
+version (LittleEndian)
+{
+ enum MANTISSA_LSB = 0;
+ enum MANTISSA_MSB = 1;
+}
+else
+{
+ enum MANTISSA_LSB = 1;
+ enum MANTISSA_MSB = 0;
+}
diff --git a/libphobos/src/std/math/remainder.d b/libphobos/src/std/math/remainder.d
new file mode 100644
index 00000000000..8766713916e
--- /dev/null
+++ b/libphobos/src/std/math/remainder.d
@@ -0,0 +1,155 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, math).
+
+It contains several versions of remainder calculation.
+
+Copyright: Copyright The D Language Foundation 2000 - 2011.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
+ Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
+Source: $(PHOBOSSRC std/math/remainder.d)
+
+Macros:
+ TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
+ <caption>Special Values</caption>
+ $0</table>
+ NAN = $(RED NAN)
+ PLUSMN = &plusmn;
+ PLUSMNINF = &plusmn;&infin;
+ */
+
+module std.math.remainder;
+
+static import core.stdc.math;
+
+/************************************
+ * Calculates the remainder from the calculation x/y.
+ * Returns:
+ * The value of x - i * y, where i is the number of times that y can
+ * be completely subtracted from x. The result has the same sign as x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH fmod(x, y)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD not 0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD !=$(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD no))
+ * )
+ */
+real fmod(real x, real y) @trusted nothrow @nogc
+{
+ version (CRuntime_Microsoft)
+ {
+ return x % y;
+ }
+ else
+ return core.stdc.math.fmodl(x, y);
+}
+
+///
+@safe unittest
+{
+ import std.math.operations : feqrel;
+ import std.math.traits : isIdentical, isNaN;
+
+ assert(isIdentical(fmod(0.0, 1.0), 0.0));
+ assert(fmod(5.0, 3.0).feqrel(2.0) > 16);
+ assert(isNaN(fmod(5.0, 0.0)));
+}
+
+/************************************
+ * Breaks x into an integral part and a fractional part, each of which has
+ * the same sign as x. The integral part is stored in i.
+ * Returns:
+ * The fractional part of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH i (on input)) $(TH modf(x, i)) $(TH i (on return)))
+ * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(PLUSMNINF)))
+ * )
+ */
+real modf(real x, ref real i) @trusted nothrow @nogc
+{
+ version (CRuntime_Microsoft)
+ {
+ import std.math.traits : copysign, isInfinity;
+ import std.math.rounding : trunc;
+
+ i = trunc(x);
+ return copysign(isInfinity(x) ? 0.0 : x - i, x);
+ }
+ else
+ return core.stdc.math.modfl(x,&i);
+}
+
+///
+@safe unittest
+{
+ import std.math.operations : feqrel;
+
+ real frac;
+ real intpart;
+
+ frac = modf(3.14159, intpart);
+ assert(intpart.feqrel(3.0) > 16);
+ assert(frac.feqrel(0.14159) > 16);
+}
+
+/****************************************************
+ * Calculate the remainder x REM y, following IEC 60559.
+ *
+ * REM is the value of x - y * n, where n is the integer nearest the exact
+ * value of x / y.
+ * If |n - x / y| == 0.5, n is even.
+ * If the result is zero, it has the same sign as x.
+ * Otherwise, the sign of the result is the sign of x / y.
+ * Precision mode has no effect on the remainder functions.
+ *
+ * remquo returns `n` in the parameter `n`.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH remainder(x, y)) $(TH n) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD not 0.0) $(TD $(PLUSMN)0.0) $(TD 0.0) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD -$(NAN)) $(TD ?) $(TD yes))
+ * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)$(NAN)) $(TD ?) $(TD yes))
+ * $(TR $(TD != $(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD ?) $(TD no))
+ * )
+ */
+real remainder(real x, real y) @trusted nothrow @nogc
+{
+ return core.stdc.math.remainderl(x, y);
+}
+
+/// ditto
+real remquo(real x, real y, out int n) @trusted nothrow @nogc /// ditto
+{
+ return core.stdc.math.remquol(x, y, &n);
+}
+
+///
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : feqrel;
+ import std.math.traits : isNaN;
+
+ assert(remainder(5.1, 3.0).feqrel(-0.9) > 16);
+ assert(remainder(-5.1, 3.0).feqrel(0.9) > 16);
+ assert(remainder(0.0, 3.0) == 0.0);
+
+ assert(isNaN(remainder(1.0, 0.0)));
+ assert(isNaN(remainder(-1.0, 0.0)));
+}
+
+///
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : feqrel;
+
+ int n;
+
+ assert(remquo(5.1, 3.0, n).feqrel(-0.9) > 16 && n == 2);
+ assert(remquo(-5.1, 3.0, n).feqrel(0.9) > 16 && n == -2);
+ assert(remquo(0.0, 3.0, n) == 0.0 && n == 0);
+}
diff --git a/libphobos/src/std/math/rounding.d b/libphobos/src/std/math/rounding.d
new file mode 100644
index 00000000000..5c8d708c489
--- /dev/null
+++ b/libphobos/src/std/math/rounding.d
@@ -0,0 +1,1004 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, math).
+
+It contains several functions for rounding floating point numbers.
+
+Copyright: Copyright The D Language Foundation 2000 - 2011.
+ D implementations of floor, ceil, and lrint functions are based on the
+ CEPHES math library, which is Copyright (C) 2001 Stephen L. Moshier
+ $(LT)steve@moshier.net$(GT) and are incorporated herein by permission
+ of the author. The author reserves the right to distribute this
+ material elsewhere under different copying permissions.
+ These modifications are distributed here under the following terms:
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
+ Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
+Source: $(PHOBOSSRC std/math/rounding.d)
+ */
+
+/* NOTE: This file has been patched from the original DMD distribution to
+ * work with the GDC compiler.
+ */
+module std.math.rounding;
+
+static import core.math;
+static import core.stdc.math;
+
+import std.traits : isFloatingPoint, isIntegral, Unqual;
+
+version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
+version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
+
+version (InlineAsm_X86_Any) version = InlineAsm_X87;
+version (InlineAsm_X87)
+{
+ static assert(real.mant_dig == 64);
+ version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC;
+}
+
+/**************************************
+ * Returns the value of x rounded upward to the next integer
+ * (toward positive infinity).
+ */
+real ceil(real x) @trusted pure nothrow @nogc
+{
+ version (InlineAsm_X87_MSVC)
+ {
+ version (X86_64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked ;
+ fld real ptr [RCX] ;
+ fstcw 8[RSP] ;
+ mov AL,9[RSP] ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x08 ; // round to +infinity
+ mov 9[RSP],AL ;
+ fldcw 8[RSP] ;
+ frndint ;
+ mov 9[RSP],DL ;
+ fldcw 8[RSP] ;
+ ret ;
+ }
+ }
+ else
+ {
+ short cw;
+ asm pure nothrow @nogc
+ {
+ fld x ;
+ fstcw cw ;
+ mov AL,byte ptr cw+1 ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x08 ; // round to +infinity
+ mov byte ptr cw+1,AL ;
+ fldcw cw ;
+ frndint ;
+ mov byte ptr cw+1,DL ;
+ fldcw cw ;
+ }
+ }
+ }
+ else
+ {
+ import std.math.traits : isInfinity, isNaN;
+
+ // Special cases.
+ if (isNaN(x) || isInfinity(x))
+ return x;
+
+ real y = floorImpl(x);
+ if (y < x)
+ y += 1.0;
+
+ return y;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(ceil(+123.456L) == +124);
+ assert(ceil(-123.456L) == -123);
+ assert(ceil(-1.234L) == -1);
+ assert(ceil(-0.123L) == 0);
+ assert(ceil(0.0L) == 0);
+ assert(ceil(+0.123L) == 1);
+ assert(ceil(+1.234L) == 2);
+ assert(ceil(real.infinity) == real.infinity);
+ assert(isNaN(ceil(real.nan)));
+ assert(isNaN(ceil(real.init)));
+}
+
+/// ditto
+double ceil(double x) @trusted pure nothrow @nogc
+{
+ import std.math.traits : isInfinity, isNaN;
+
+ // Special cases.
+ if (isNaN(x) || isInfinity(x))
+ return x;
+
+ double y = floorImpl(x);
+ if (y < x)
+ y += 1.0;
+
+ return y;
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(ceil(+123.456) == +124);
+ assert(ceil(-123.456) == -123);
+ assert(ceil(-1.234) == -1);
+ assert(ceil(-0.123) == 0);
+ assert(ceil(0.0) == 0);
+ assert(ceil(+0.123) == 1);
+ assert(ceil(+1.234) == 2);
+ assert(ceil(double.infinity) == double.infinity);
+ assert(isNaN(ceil(double.nan)));
+ assert(isNaN(ceil(double.init)));
+}
+
+/// ditto
+float ceil(float x) @trusted pure nothrow @nogc
+{
+ import std.math.traits : isInfinity, isNaN;
+
+ // Special cases.
+ if (isNaN(x) || isInfinity(x))
+ return x;
+
+ float y = floorImpl(x);
+ if (y < x)
+ y += 1.0;
+
+ return y;
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(ceil(+123.456f) == +124);
+ assert(ceil(-123.456f) == -123);
+ assert(ceil(-1.234f) == -1);
+ assert(ceil(-0.123f) == 0);
+ assert(ceil(0.0f) == 0);
+ assert(ceil(+0.123f) == 1);
+ assert(ceil(+1.234f) == 2);
+ assert(ceil(float.infinity) == float.infinity);
+ assert(isNaN(ceil(float.nan)));
+ assert(isNaN(ceil(float.init)));
+}
+
+/**************************************
+ * Returns the value of x rounded downward to the next integer
+ * (toward negative infinity).
+ */
+real floor(real x) @trusted pure nothrow @nogc
+{
+ version (InlineAsm_X87_MSVC)
+ {
+ version (X86_64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked ;
+ fld real ptr [RCX] ;
+ fstcw 8[RSP] ;
+ mov AL,9[RSP] ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x04 ; // round to -infinity
+ mov 9[RSP],AL ;
+ fldcw 8[RSP] ;
+ frndint ;
+ mov 9[RSP],DL ;
+ fldcw 8[RSP] ;
+ ret ;
+ }
+ }
+ else
+ {
+ short cw;
+ asm pure nothrow @nogc
+ {
+ fld x ;
+ fstcw cw ;
+ mov AL,byte ptr cw+1 ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x04 ; // round to -infinity
+ mov byte ptr cw+1,AL ;
+ fldcw cw ;
+ frndint ;
+ mov byte ptr cw+1,DL ;
+ fldcw cw ;
+ }
+ }
+ }
+ else
+ {
+ import std.math.traits : isInfinity, isNaN;
+
+ // Special cases.
+ if (isNaN(x) || isInfinity(x) || x == 0.0)
+ return x;
+
+ return floorImpl(x);
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(floor(+123.456L) == +123);
+ assert(floor(-123.456L) == -124);
+ assert(floor(+123.0L) == +123);
+ assert(floor(-124.0L) == -124);
+ assert(floor(-1.234L) == -2);
+ assert(floor(-0.123L) == -1);
+ assert(floor(0.0L) == 0);
+ assert(floor(+0.123L) == 0);
+ assert(floor(+1.234L) == 1);
+ assert(floor(real.infinity) == real.infinity);
+ assert(isNaN(floor(real.nan)));
+ assert(isNaN(floor(real.init)));
+}
+
+/// ditto
+double floor(double x) @trusted pure nothrow @nogc
+{
+ import std.math.traits : isInfinity, isNaN;
+
+ // Special cases.
+ if (isNaN(x) || isInfinity(x) || x == 0.0)
+ return x;
+
+ return floorImpl(x);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(floor(+123.456) == +123);
+ assert(floor(-123.456) == -124);
+ assert(floor(+123.0) == +123);
+ assert(floor(-124.0) == -124);
+ assert(floor(-1.234) == -2);
+ assert(floor(-0.123) == -1);
+ assert(floor(0.0) == 0);
+ assert(floor(+0.123) == 0);
+ assert(floor(+1.234) == 1);
+ assert(floor(double.infinity) == double.infinity);
+ assert(isNaN(floor(double.nan)));
+ assert(isNaN(floor(double.init)));
+}
+
+/// ditto
+float floor(float x) @trusted pure nothrow @nogc
+{
+ import std.math.traits : isInfinity, isNaN;
+
+ // Special cases.
+ if (isNaN(x) || isInfinity(x) || x == 0.0)
+ return x;
+
+ return floorImpl(x);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(floor(+123.456f) == +123);
+ assert(floor(-123.456f) == -124);
+ assert(floor(+123.0f) == +123);
+ assert(floor(-124.0f) == -124);
+ assert(floor(-1.234f) == -2);
+ assert(floor(-0.123f) == -1);
+ assert(floor(0.0f) == 0);
+ assert(floor(+0.123f) == 0);
+ assert(floor(+1.234f) == 1);
+ assert(floor(float.infinity) == float.infinity);
+ assert(isNaN(floor(float.nan)));
+ assert(isNaN(floor(float.init)));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=6381
+// floor/ceil should be usable in pure function.
+@safe pure nothrow unittest
+{
+ auto x = floor(1.2);
+ auto y = ceil(1.2);
+}
+
+/**
+ * Round `val` to a multiple of `unit`. `rfunc` specifies the rounding
+ * function to use; by default this is `rint`, which uses the current
+ * rounding mode.
+ */
+Unqual!F quantize(alias rfunc = rint, F)(const F val, const F unit)
+if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F)
+{
+ import std.math.traits : isInfinity;
+
+ typeof(return) ret = val;
+ if (unit != 0)
+ {
+ const scaled = val / unit;
+ if (!scaled.isInfinity)
+ ret = rfunc(scaled) * unit;
+ }
+ return ret;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(12345.6789L.quantize(0.01L), 12345.68L));
+ assert(isClose(12345.6789L.quantize!floor(0.01L), 12345.67L));
+ assert(isClose(12345.6789L.quantize(22.0L), 12342.0L));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isNaN;
+
+ assert(isClose(12345.6789L.quantize(0), 12345.6789L));
+ assert(12345.6789L.quantize(real.infinity).isNaN);
+ assert(12345.6789L.quantize(real.nan).isNaN);
+ assert(real.infinity.quantize(0.01L) == real.infinity);
+ assert(real.infinity.quantize(real.nan).isNaN);
+ assert(real.nan.quantize(0.01L).isNaN);
+ assert(real.nan.quantize(real.infinity).isNaN);
+ assert(real.nan.quantize(real.nan).isNaN);
+}
+
+/**
+ * Round `val` to a multiple of `pow(base, exp)`. `rfunc` specifies the
+ * rounding function to use; by default this is `rint`, which uses the
+ * current rounding mode.
+ */
+Unqual!F quantize(real base, alias rfunc = rint, F, E)(const F val, const E exp)
+if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F && isIntegral!E)
+{
+ import std.math.exponential : pow;
+
+ // TODO: Compile-time optimization for power-of-two bases?
+ return quantize!rfunc(val, pow(cast(F) base, exp));
+}
+
+/// ditto
+Unqual!F quantize(real base, long exp = 1, alias rfunc = rint, F)(const F val)
+if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F)
+{
+ import std.math.exponential : pow;
+
+ enum unit = cast(F) pow(base, exp);
+ return quantize!rfunc(val, unit);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(12345.6789L.quantize!10(-2), 12345.68L));
+ assert(isClose(12345.6789L.quantize!(10, -2), 12345.68L));
+ assert(isClose(12345.6789L.quantize!(10, floor)(-2), 12345.67L));
+ assert(isClose(12345.6789L.quantize!(10, -2, floor), 12345.67L));
+
+ assert(isClose(12345.6789L.quantize!22(1), 12342.0L));
+ assert(isClose(12345.6789L.quantize!22, 12342.0L));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.exponential : log10, pow;
+ import std.math.operations : isClose;
+ import std.meta : AliasSeq;
+
+ static foreach (F; AliasSeq!(real, double, float))
+ {{
+ const maxL10 = cast(int) F.max.log10.floor;
+ const maxR10 = pow(cast(F) 10, maxL10);
+ assert(isClose((cast(F) 0.9L * maxR10).quantize!10(maxL10), maxR10));
+ assert(isClose((cast(F)-0.9L * maxR10).quantize!10(maxL10), -maxR10));
+
+ assert(F.max.quantize(F.min_normal) == F.max);
+ assert((-F.max).quantize(F.min_normal) == -F.max);
+ assert(F.min_normal.quantize(F.max) == 0);
+ assert((-F.min_normal).quantize(F.max) == 0);
+ assert(F.min_normal.quantize(F.min_normal) == F.min_normal);
+ assert((-F.min_normal).quantize(F.min_normal) == -F.min_normal);
+ }}
+}
+
+/******************************************
+ * Rounds x to the nearest integer value, using the current rounding
+ * mode.
+ *
+ * Unlike the rint functions, nearbyint does not raise the
+ * FE_INEXACT exception.
+ */
+pragma(inline, true)
+real nearbyint(real x) @safe pure nothrow @nogc
+{
+ return core.stdc.math.nearbyintl(x);
+}
+
+///
+@safe pure unittest
+{
+ import std.math.traits : isNaN;
+
+ assert(nearbyint(0.4) == 0);
+ assert(nearbyint(0.5) == 0);
+ assert(nearbyint(0.6) == 1);
+ assert(nearbyint(100.0) == 100);
+
+ assert(isNaN(nearbyint(real.nan)));
+ assert(nearbyint(real.infinity) == real.infinity);
+ assert(nearbyint(-real.infinity) == -real.infinity);
+}
+
+/**********************************
+ * Rounds x to the nearest integer value, using the current rounding
+ * mode.
+ *
+ * If the return value is not equal to x, the FE_INEXACT
+ * exception is raised.
+ *
+ * $(LREF nearbyint) performs the same operation, but does
+ * not set the FE_INEXACT exception.
+ */
+pragma(inline, true)
+real rint(real x) @safe pure nothrow @nogc
+{
+ return core.math.rint(x);
+}
+///ditto
+pragma(inline, true)
+double rint(double x) @safe pure nothrow @nogc
+{
+ return core.math.rint(x);
+}
+///ditto
+pragma(inline, true)
+float rint(float x) @safe pure nothrow @nogc
+{
+ return core.math.rint(x);
+}
+
+///
+@safe unittest
+{
+ import std.math.traits : isNaN;
+
+ version (IeeeFlagsSupport) resetIeeeFlags();
+ assert(rint(0.4) == 0);
+ version (GNU) { /* inexact bit not set with enabled optimizations */ } else
+ version (IeeeFlagsSupport) assert(ieeeFlags.inexact);
+
+ assert(rint(0.5) == 0);
+ assert(rint(0.6) == 1);
+ assert(rint(100.0) == 100);
+
+ assert(isNaN(rint(real.nan)));
+ assert(rint(real.infinity) == real.infinity);
+ assert(rint(-real.infinity) == -real.infinity);
+}
+
+@safe unittest
+{
+ real function(real) print = &rint;
+ assert(print != null);
+}
+
+/***************************************
+ * Rounds x to the nearest integer value, using the current rounding
+ * mode.
+ *
+ * This is generally the fastest method to convert a floating-point number
+ * to an integer. Note that the results from this function
+ * depend on the rounding mode, if the fractional part of x is exactly 0.5.
+ * If using the default rounding mode (ties round to even integers)
+ * lrint(4.5) == 4, lrint(5.5)==6.
+ */
+long lrint(real x) @trusted pure nothrow @nogc
+{
+ version (InlineAsm_X87)
+ {
+ version (Win64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked;
+ fld real ptr [RCX];
+ fistp qword ptr 8[RSP];
+ mov RAX,8[RSP];
+ ret;
+ }
+ }
+ else
+ {
+ long n;
+ asm pure nothrow @nogc
+ {
+ fld x;
+ fistp n;
+ }
+ return n;
+ }
+ }
+ else
+ {
+ import std.math : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB;
+
+ alias F = floatTraits!(real);
+ static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ long result;
+
+ // Rounding limit when casting from real(double) to ulong.
+ enum real OF = 4.50359962737049600000E15L;
+
+ uint* vi = cast(uint*)(&x);
+
+ // Find the exponent and sign
+ uint msb = vi[MANTISSA_MSB];
+ uint lsb = vi[MANTISSA_LSB];
+ int exp = ((msb >> 20) & 0x7ff) - 0x3ff;
+ const int sign = msb >> 31;
+ msb &= 0xfffff;
+ msb |= 0x100000;
+
+ if (exp < 63)
+ {
+ if (exp >= 52)
+ result = (cast(long) msb << (exp - 20)) | (lsb << (exp - 52));
+ else
+ {
+ // Adjust x and check result.
+ const real j = sign ? -OF : OF;
+ x = (j + x) - j;
+ msb = vi[MANTISSA_MSB];
+ lsb = vi[MANTISSA_LSB];
+ exp = ((msb >> 20) & 0x7ff) - 0x3ff;
+ msb &= 0xfffff;
+ msb |= 0x100000;
+
+ if (exp < 0)
+ result = 0;
+ else if (exp < 20)
+ result = cast(long) msb >> (20 - exp);
+ else if (exp == 20)
+ result = cast(long) msb;
+ else
+ result = (cast(long) msb << (exp - 20)) | (lsb >> (52 - exp));
+ }
+ }
+ else
+ {
+ // It is left implementation defined when the number is too large.
+ return cast(long) x;
+ }
+
+ return sign ? -result : result;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ long result;
+
+ // Rounding limit when casting from real(80-bit) to ulong.
+ static if (F.realFormat == RealFormat.ieeeExtended)
+ enum real OF = 9.22337203685477580800E18L;
+ else
+ enum real OF = 4.50359962737049600000E15L;
+
+ ushort* vu = cast(ushort*)(&x);
+ uint* vi = cast(uint*)(&x);
+
+ // Find the exponent and sign
+ int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+ const int sign = (vu[F.EXPPOS_SHORT] >> 15) & 1;
+
+ if (exp < 63)
+ {
+ // Adjust x and check result.
+ const real j = sign ? -OF : OF;
+ x = (j + x) - j;
+ exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+
+ version (LittleEndian)
+ {
+ if (exp < 0)
+ result = 0;
+ else if (exp <= 31)
+ result = vi[1] >> (31 - exp);
+ else
+ result = (cast(long) vi[1] << (exp - 31)) | (vi[0] >> (63 - exp));
+ }
+ else
+ {
+ if (exp < 0)
+ result = 0;
+ else if (exp <= 31)
+ result = vi[1] >> (31 - exp);
+ else
+ result = (cast(long) vi[1] << (exp - 31)) | (vi[2] >> (63 - exp));
+ }
+ }
+ else
+ {
+ // It is left implementation defined when the number is too large
+ // to fit in a 64bit long.
+ return cast(long) x;
+ }
+
+ return sign ? -result : result;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ const vu = cast(ushort*)(&x);
+
+ // Find the exponent and sign
+ const sign = (vu[F.EXPPOS_SHORT] >> 15) & 1;
+ if ((vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1) > 63)
+ {
+ // The result is left implementation defined when the number is
+ // too large to fit in a 64 bit long.
+ return cast(long) x;
+ }
+
+ // Force rounding of lower bits according to current rounding
+ // mode by adding ±2^-112 and subtracting it again.
+ enum OF = 5.19229685853482762853049632922009600E33L;
+ const j = sign ? -OF : OF;
+ x = (j + x) - j;
+
+ const exp = (vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1);
+ const implicitOne = 1UL << 48;
+ auto vl = cast(ulong*)(&x);
+ vl[MANTISSA_MSB] &= implicitOne - 1;
+ vl[MANTISSA_MSB] |= implicitOne;
+
+ long result;
+
+ if (exp < 0)
+ result = 0;
+ else if (exp <= 48)
+ result = vl[MANTISSA_MSB] >> (48 - exp);
+ else
+ result = (vl[MANTISSA_MSB] << (exp - 48)) | (vl[MANTISSA_LSB] >> (112 - exp));
+
+ return sign ? -result : result;
+ }
+ else
+ {
+ static assert(false, "real type not supported by lrint()");
+ }
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(lrint(4.5) == 4);
+ assert(lrint(5.5) == 6);
+ assert(lrint(-4.5) == -4);
+ assert(lrint(-5.5) == -6);
+
+ assert(lrint(int.max - 0.5) == 2147483646L);
+ assert(lrint(int.max + 0.5) == 2147483648L);
+ assert(lrint(int.min - 0.5) == -2147483648L);
+ assert(lrint(int.min + 0.5) == -2147483648L);
+}
+
+static if (real.mant_dig >= long.sizeof * 8)
+{
+ @safe pure nothrow @nogc unittest
+ {
+ assert(lrint(long.max - 1.5L) == long.max - 1);
+ assert(lrint(long.max - 0.5L) == long.max - 1);
+ assert(lrint(long.min + 0.5L) == long.min);
+ assert(lrint(long.min + 1.5L) == long.min + 2);
+ }
+}
+
+/*******************************************
+ * Return the value of x rounded to the nearest integer.
+ * If the fractional part of x is exactly 0.5, the return value is
+ * rounded away from zero.
+ *
+ * Returns:
+ * A `real`.
+ */
+auto round(real x) @trusted nothrow @nogc
+{
+ version (CRuntime_Microsoft)
+ {
+ import std.math.hardware : FloatingPointControl;
+
+ auto old = FloatingPointControl.getControlState();
+ FloatingPointControl.setControlState(
+ (old & (-1 - FloatingPointControl.roundingMask)) | FloatingPointControl.roundToZero
+ );
+ x = core.math.rint((x >= 0) ? x + 0.5 : x - 0.5);
+ FloatingPointControl.setControlState(old);
+ return x;
+ }
+ else
+ {
+ return core.stdc.math.roundl(x);
+ }
+}
+
+///
+@safe nothrow @nogc unittest
+{
+ assert(round(4.5) == 5);
+ assert(round(5.4) == 5);
+ assert(round(-4.5) == -5);
+ assert(round(-5.1) == -5);
+}
+
+// assure purity on Posix
+version (Posix)
+{
+ @safe pure nothrow @nogc unittest
+ {
+ assert(round(4.5) == 5);
+ }
+}
+
+/**********************************************
+ * Return the value of x rounded to the nearest integer.
+ *
+ * If the fractional part of x is exactly 0.5, the return value is rounded
+ * away from zero.
+ *
+ * $(BLUE This function is not implemented for Digital Mars C runtime.)
+ */
+long lround(real x) @trusted nothrow @nogc
+{
+ version (CRuntime_DigitalMars)
+ assert(0, "lround not implemented");
+ else
+ return core.stdc.math.llroundl(x);
+}
+
+///
+@safe nothrow @nogc unittest
+{
+ version (CRuntime_DigitalMars) {}
+ else
+ {
+ assert(lround(0.49) == 0);
+ assert(lround(0.5) == 1);
+ assert(lround(1.5) == 2);
+ }
+}
+
+/**
+ Returns the integer portion of x, dropping the fractional portion.
+ This is also known as "chop" rounding.
+ `pure` on all platforms.
+ */
+real trunc(real x) @trusted nothrow @nogc pure
+{
+ version (InlineAsm_X87_MSVC)
+ {
+ version (X86_64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked ;
+ fld real ptr [RCX] ;
+ fstcw 8[RSP] ;
+ mov AL,9[RSP] ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x0C ; // round to 0
+ mov 9[RSP],AL ;
+ fldcw 8[RSP] ;
+ frndint ;
+ mov 9[RSP],DL ;
+ fldcw 8[RSP] ;
+ ret ;
+ }
+ }
+ else
+ {
+ short cw;
+ asm pure nothrow @nogc
+ {
+ fld x ;
+ fstcw cw ;
+ mov AL,byte ptr cw+1 ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x0C ; // round to 0
+ mov byte ptr cw+1,AL ;
+ fldcw cw ;
+ frndint ;
+ mov byte ptr cw+1,DL ;
+ fldcw cw ;
+ }
+ }
+ }
+ else
+ {
+ return core.stdc.math.truncl(x);
+ }
+}
+
+///
+@safe pure unittest
+{
+ assert(trunc(0.01) == 0);
+ assert(trunc(0.49) == 0);
+ assert(trunc(0.5) == 0);
+ assert(trunc(1.5) == 1);
+}
+
+/*****************************************
+ * Returns x rounded to a long value using the current rounding mode.
+ * If the integer value of x is
+ * greater than long.max, the result is
+ * indeterminate.
+ */
+pragma(inline, true)
+long rndtol(real x) @nogc @safe pure nothrow { return core.math.rndtol(x); }
+//FIXME
+///ditto
+pragma(inline, true)
+long rndtol(double x) @safe pure nothrow @nogc { return rndtol(cast(real) x); }
+//FIXME
+///ditto
+pragma(inline, true)
+long rndtol(float x) @safe pure nothrow @nogc { return rndtol(cast(real) x); }
+
+///
+@safe unittest
+{
+ assert(rndtol(1.0) == 1L);
+ assert(rndtol(1.2) == 1L);
+ assert(rndtol(1.7) == 2L);
+ assert(rndtol(1.0001) == 1L);
+}
+
+@safe unittest
+{
+ long function(real) prndtol = &rndtol;
+ assert(prndtol != null);
+}
+
+// Helper for floor/ceil
+T floorImpl(T)(const T x) @trusted pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+
+ alias F = floatTraits!(T);
+ // Take care not to trigger library calls from the compiler,
+ // while ensuring that we don't get defeated by some optimizers.
+ union floatBits
+ {
+ T rv;
+ ushort[T.sizeof/2] vu;
+
+ // Other kinds of extractors for real formats.
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ int vi;
+ }
+ floatBits y = void;
+ y.rv = x;
+
+ // Find the exponent (power of 2)
+ // Do this by shifting the raw value so that the exponent lies in the low bits,
+ // then mask out the sign bit, and subtract the bias.
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ int exp = ((y.vi >> (T.mant_dig - 1)) & 0xff) - 0x7f;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ int exp = ((y.vu[F.EXPPOS_SHORT] >> 4) & 0x7ff) - 0x3ff;
+
+ version (LittleEndian)
+ int pos = 0;
+ else
+ int pos = 3;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+
+ version (LittleEndian)
+ int pos = 0;
+ else
+ int pos = 4;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+
+ version (LittleEndian)
+ int pos = 0;
+ else
+ int pos = 7;
+ }
+ else
+ static assert(false, "Not implemented for this architecture");
+
+ if (exp < 0)
+ {
+ if (x < 0.0)
+ return -1.0;
+ else
+ return 0.0;
+ }
+
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ if (exp < (T.mant_dig - 1))
+ {
+ // Clear all bits representing the fraction part.
+ const uint fraction_mask = F.MANTISSAMASK_INT >> exp;
+
+ if ((y.vi & fraction_mask) != 0)
+ {
+ // If 'x' is negative, then first substract 1.0 from the value.
+ if (y.vi < 0)
+ y.vi += 0x00800000 >> exp;
+ y.vi &= ~fraction_mask;
+ }
+ }
+ }
+ else
+ {
+ static if (F.realFormat == RealFormat.ieeeExtended53)
+ exp = (T.mant_dig + 11 - 1) - exp; // mant_dig is really 64
+ else
+ exp = (T.mant_dig - 1) - exp;
+
+ // Zero 16 bits at a time.
+ while (exp >= 16)
+ {
+ version (LittleEndian)
+ y.vu[pos++] = 0;
+ else
+ y.vu[pos--] = 0;
+ exp -= 16;
+ }
+
+ // Clear the remaining bits.
+ if (exp > 0)
+ y.vu[pos] &= 0xffff ^ ((1 << exp) - 1);
+
+ if ((x < 0.0) && (x != y.rv))
+ y.rv -= 1.0;
+ }
+
+ return y.rv;
+}
diff --git a/libphobos/src/std/math/traits.d b/libphobos/src/std/math/traits.d
new file mode 100644
index 00000000000..2841bad219f
--- /dev/null
+++ b/libphobos/src/std/math/traits.d
@@ -0,0 +1,853 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, math).
+
+It contains several functions for introspection on numerical values.
+
+Copyright: Copyright The D Language Foundation 2000 - 2011.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
+ Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
+Source: $(PHOBOSSRC std/math/traits.d)
+
+Macros:
+ NAN = $(RED NAN)
+ PLUSMN = &plusmn;
+ INFIN = &infin;
+ */
+
+module std.math.traits;
+
+import std.traits : isFloatingPoint, isIntegral, isNumeric, isSigned;
+
+/*********************************
+ * Determines if $(D_PARAM x) is NaN.
+ * Params:
+ * x = a floating point number.
+ * Returns:
+ * `true` if $(D_PARAM x) is Nan.
+ */
+bool isNaN(X)(X x) @nogc @trusted pure nothrow
+if (isFloatingPoint!(X))
+{
+ version (all)
+ {
+ return x != x;
+ }
+ else
+ {
+ /*
+ Code kept for historical context. At least on Intel, the simple test
+ x != x uses one dedicated instruction (ucomiss/ucomisd) that runs in one
+ cycle. Code for 80- and 128-bits is larger but still smaller than the
+ integrals-based solutions below. Future revisions may enable the code
+ below conditionally depending on hardware.
+ */
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ const uint p = *cast(uint *)&x;
+ // Sign bit (MSB) is irrelevant so mask it out.
+ // Next 8 bits should be all set.
+ // At least one bit among the least significant 23 bits should be set.
+ return (p & 0x7FFF_FFFF) > 0x7F80_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ const ulong p = *cast(ulong *)&x;
+ // Sign bit (MSB) is irrelevant so mask it out.
+ // Next 11 bits should be all set.
+ // At least one bit among the least significant 52 bits should be set.
+ return (p & 0x7FFF_FFFF_FFFF_FFFF) > 0x7FF0_0000_0000_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
+ const ulong ps = *cast(ulong *)&x;
+ return e == F.EXPMASK &&
+ ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
+ const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB];
+ const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB];
+ return e == F.EXPMASK &&
+ (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0;
+ }
+ else
+ {
+ return x != x;
+ }
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isNaN(float.init));
+ assert( isNaN(-double.init));
+ assert( isNaN(real.nan));
+ assert( isNaN(-real.nan));
+ assert(!isNaN(cast(float) 53.6));
+ assert(!isNaN(cast(real)-53.6));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ // CTFE-able tests
+ assert(isNaN(T.init));
+ assert(isNaN(-T.init));
+ assert(isNaN(T.nan));
+ assert(isNaN(-T.nan));
+ assert(!isNaN(T.infinity));
+ assert(!isNaN(-T.infinity));
+ assert(!isNaN(cast(T) 53.6));
+ assert(!isNaN(cast(T)-53.6));
+
+ // Runtime tests
+ shared T f;
+ f = T.init;
+ assert(isNaN(f));
+ assert(isNaN(-f));
+ f = T.nan;
+ assert(isNaN(f));
+ assert(isNaN(-f));
+ f = T.infinity;
+ assert(!isNaN(f));
+ assert(!isNaN(-f));
+ f = cast(T) 53.6;
+ assert(!isNaN(f));
+ assert(!isNaN(-f));
+ }}
+}
+
+/*********************************
+ * Determines if $(D_PARAM x) is finite.
+ * Params:
+ * x = a floating point number.
+ * Returns:
+ * `true` if $(D_PARAM x) is finite.
+ */
+bool isFinite(X)(X x) @trusted pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+
+ static if (__traits(isFloating, X))
+ if (__ctfe)
+ return x == x && x != X.infinity && x != -X.infinity;
+ alias F = floatTraits!(X);
+ ushort* pe = cast(ushort *)&x;
+ return (pe[F.EXPPOS_SHORT] & F.EXPMASK) != F.EXPMASK;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isFinite(1.23f));
+ assert( isFinite(float.max));
+ assert( isFinite(float.min_normal));
+ assert(!isFinite(float.nan));
+ assert(!isFinite(float.infinity));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(isFinite(1.23));
+ assert(isFinite(double.max));
+ assert(isFinite(double.min_normal));
+ assert(!isFinite(double.nan));
+ assert(!isFinite(double.infinity));
+
+ assert(isFinite(1.23L));
+ assert(isFinite(real.max));
+ assert(isFinite(real.min_normal));
+ assert(!isFinite(real.nan));
+ assert(!isFinite(real.infinity));
+
+ //CTFE
+ static assert(isFinite(1.23));
+ static assert(isFinite(double.max));
+ static assert(isFinite(double.min_normal));
+ static assert(!isFinite(double.nan));
+ static assert(!isFinite(double.infinity));
+
+ static assert(isFinite(1.23L));
+ static assert(isFinite(real.max));
+ static assert(isFinite(real.min_normal));
+ static assert(!isFinite(real.nan));
+ static assert(!isFinite(real.infinity));
+}
+
+
+/*********************************
+ * Determines if $(D_PARAM x) is normalized.
+ *
+ * A normalized number must not be zero, subnormal, infinite nor $(NAN).
+ *
+ * Params:
+ * x = a floating point number.
+ * Returns:
+ * `true` if $(D_PARAM x) is normalized.
+ */
+
+/* Need one for each format because subnormal floats might
+ * be converted to normal reals.
+ */
+bool isNormal(X)(X x) @trusted pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+
+ static if (__traits(isFloating, X))
+ if (__ctfe)
+ return (x <= -X.min_normal && x != -X.infinity) || (x >= X.min_normal && x != X.infinity);
+ alias F = floatTraits!(X);
+ ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
+ return (e != F.EXPMASK && e != 0);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ float f = 3;
+ double d = 500;
+ real e = 10e+48;
+
+ assert(isNormal(f));
+ assert(isNormal(d));
+ assert(isNormal(e));
+ f = d = e = 0;
+ assert(!isNormal(f));
+ assert(!isNormal(d));
+ assert(!isNormal(e));
+ assert(!isNormal(real.infinity));
+ assert(isNormal(-real.max));
+ assert(!isNormal(real.min_normal/4));
+
+}
+
+@safe pure nothrow @nogc unittest
+{
+ // CTFE
+ enum float f = 3;
+ enum double d = 500;
+ enum real e = 10e+48;
+
+ static assert(isNormal(f));
+ static assert(isNormal(d));
+ static assert(isNormal(e));
+
+ static assert(!isNormal(0.0f));
+ static assert(!isNormal(0.0));
+ static assert(!isNormal(0.0L));
+ static assert(!isNormal(real.infinity));
+ static assert(isNormal(-real.max));
+ static assert(!isNormal(real.min_normal/4));
+}
+
+/*********************************
+ * Determines if $(D_PARAM x) is subnormal.
+ *
+ * Subnormals (also known as "denormal number"), have a 0 exponent
+ * and a 0 most significant mantissa bit.
+ *
+ * Params:
+ * x = a floating point number.
+ * Returns:
+ * `true` if $(D_PARAM x) is a denormal number.
+ */
+bool isSubnormal(X)(X x) @trusted pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB;
+
+ static if (__traits(isFloating, X))
+ if (__ctfe)
+ return -X.min_normal < x && x < X.min_normal;
+ /*
+ Need one for each format because subnormal floats might
+ be converted to normal reals.
+ */
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ uint *p = cast(uint *)&x;
+ return (*p & F.EXPMASK_INT) == 0 && *p & F.MANTISSAMASK_INT;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ uint *p = cast(uint *)&x;
+ return (p[MANTISSA_MSB] & F.EXPMASK_INT) == 0
+ && (p[MANTISSA_LSB] || p[MANTISSA_MSB] & F.MANTISSAMASK_INT);
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
+ long* ps = cast(long *)&x;
+ return (e == 0 &&
+ ((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF)) != 0));
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ ushort* pe = cast(ushort *)&x;
+ long* ps = cast(long *)&x;
+
+ return (pe[F.EXPPOS_SHORT] & F.EXPMASK) == 0 && *ps > 0;
+ }
+ else
+ {
+ static assert(false, "Not implemented for this architecture");
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ T f;
+ for (f = 1.0; !isSubnormal(f); f /= 2)
+ assert(f != 0);
+ }}
+}
+
+@safe pure nothrow @nogc unittest
+{
+ static bool subnormalTest(T)()
+ {
+ T f;
+ for (f = 1.0; !isSubnormal(f); f /= 2)
+ if (f == 0)
+ return false;
+ return true;
+ }
+ static assert(subnormalTest!float());
+ static assert(subnormalTest!double());
+ static assert(subnormalTest!real());
+}
+
+/*********************************
+ * Determines if $(D_PARAM x) is $(PLUSMN)$(INFIN).
+ * Params:
+ * x = a floating point number.
+ * Returns:
+ * `true` if $(D_PARAM x) is $(PLUSMN)$(INFIN).
+ */
+bool isInfinity(X)(X x) @nogc @trusted pure nothrow
+if (isFloatingPoint!(X))
+{
+ import std.math : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB;
+
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF)
+ == 0x7FF0_0000_0000_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended ||
+ F.realFormat == RealFormat.ieeeExtended53)
+ {
+ const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]);
+ const ulong ps = *cast(ulong *)&x;
+
+ // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1.
+ return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ const long psLsb = (cast(long *)&x)[MANTISSA_LSB];
+ const long psMsb = (cast(long *)&x)[MANTISSA_MSB];
+ return (psLsb == 0)
+ && (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000;
+ }
+ else
+ {
+ return (x < -X.max) || (X.max < x);
+ }
+}
+
+///
+@nogc @safe pure nothrow unittest
+{
+ assert(!isInfinity(float.init));
+ assert(!isInfinity(-float.init));
+ assert(!isInfinity(float.nan));
+ assert(!isInfinity(-float.nan));
+ assert(isInfinity(float.infinity));
+ assert(isInfinity(-float.infinity));
+ assert(isInfinity(-1.0f / 0.0f));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ // CTFE-able tests
+ assert(!isInfinity(double.init));
+ assert(!isInfinity(-double.init));
+ assert(!isInfinity(double.nan));
+ assert(!isInfinity(-double.nan));
+ assert(isInfinity(double.infinity));
+ assert(isInfinity(-double.infinity));
+ assert(isInfinity(-1.0 / 0.0));
+
+ assert(!isInfinity(real.init));
+ assert(!isInfinity(-real.init));
+ assert(!isInfinity(real.nan));
+ assert(!isInfinity(-real.nan));
+ assert(isInfinity(real.infinity));
+ assert(isInfinity(-real.infinity));
+ assert(isInfinity(-1.0L / 0.0L));
+
+ // Runtime tests
+ shared float f;
+ f = float.init;
+ assert(!isInfinity(f));
+ assert(!isInfinity(-f));
+ f = float.nan;
+ assert(!isInfinity(f));
+ assert(!isInfinity(-f));
+ f = float.infinity;
+ assert(isInfinity(f));
+ assert(isInfinity(-f));
+ f = (-1.0f / 0.0f);
+ assert(isInfinity(f));
+
+ shared double d;
+ d = double.init;
+ assert(!isInfinity(d));
+ assert(!isInfinity(-d));
+ d = double.nan;
+ assert(!isInfinity(d));
+ assert(!isInfinity(-d));
+ d = double.infinity;
+ assert(isInfinity(d));
+ assert(isInfinity(-d));
+ d = (-1.0 / 0.0);
+ assert(isInfinity(d));
+
+ shared real e;
+ e = real.init;
+ assert(!isInfinity(e));
+ assert(!isInfinity(-e));
+ e = real.nan;
+ assert(!isInfinity(e));
+ assert(!isInfinity(-e));
+ e = real.infinity;
+ assert(isInfinity(e));
+ assert(isInfinity(-e));
+ e = (-1.0L / 0.0L);
+ assert(isInfinity(e));
+}
+
+@nogc @safe pure nothrow unittest
+{
+ import std.meta : AliasSeq;
+ static bool foo(T)(inout T x) { return isInfinity(x); }
+ foreach (T; AliasSeq!(float, double, real))
+ {
+ assert(!foo(T(3.14f)));
+ assert(foo(T.infinity));
+ }
+}
+
+/*********************************
+ * Is the binary representation of x identical to y?
+ */
+bool isIdentical(real x, real y) @trusted pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+
+ // We're doing a bitwise comparison so the endianness is irrelevant.
+ long* pxs = cast(long *)&x;
+ long* pys = cast(long *)&y;
+ alias F = floatTraits!(real);
+ static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ return pxs[0] == pys[0];
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ return pxs[0] == pys[0] && pxs[1] == pys[1];
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ ushort* pxe = cast(ushort *)&x;
+ ushort* pye = cast(ushort *)&y;
+ return pxe[4] == pye[4] && pxs[0] == pys[0];
+ }
+ else
+ {
+ assert(0, "isIdentical not implemented");
+ }
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ assert( isIdentical(0.0, 0.0));
+ assert( isIdentical(1.0, 1.0));
+ assert( isIdentical(real.infinity, real.infinity));
+ assert( isIdentical(-real.infinity, -real.infinity));
+
+ assert(!isIdentical(0.0, -0.0));
+ assert(!isIdentical(real.nan, -real.nan));
+ assert(!isIdentical(real.infinity, -real.infinity));
+}
+
+/*********************************
+ * Return 1 if sign bit of e is set, 0 if not.
+ */
+int signbit(X)(X x) @nogc @trusted pure nothrow
+{
+ import std.math : floatTraits, RealFormat;
+
+ if (__ctfe)
+ {
+ double dval = cast(double) x; // Precision can increase or decrease but sign won't change (even NaN).
+ return 0 > *cast(long*) &dval;
+ }
+
+ alias F = floatTraits!(X);
+ return ((cast(ubyte *)&x)[F.SIGNPOS_BYTE] & 0x80) != 0;
+}
+
+///
+@nogc @safe pure nothrow unittest
+{
+ assert(!signbit(float.nan));
+ assert(signbit(-float.nan));
+ assert(!signbit(168.1234f));
+ assert(signbit(-168.1234f));
+ assert(!signbit(0.0f));
+ assert(signbit(-0.0f));
+ assert(signbit(-float.max));
+ assert(!signbit(float.max));
+
+ assert(!signbit(double.nan));
+ assert(signbit(-double.nan));
+ assert(!signbit(168.1234));
+ assert(signbit(-168.1234));
+ assert(!signbit(0.0));
+ assert(signbit(-0.0));
+ assert(signbit(-double.max));
+ assert(!signbit(double.max));
+
+ assert(!signbit(real.nan));
+ assert(signbit(-real.nan));
+ assert(!signbit(168.1234L));
+ assert(signbit(-168.1234L));
+ assert(!signbit(0.0L));
+ assert(signbit(-0.0L));
+ assert(signbit(-real.max));
+ assert(!signbit(real.max));
+}
+
+@nogc @safe pure nothrow unittest
+{
+ // CTFE
+ static assert(!signbit(float.nan));
+ static assert(signbit(-float.nan));
+ static assert(!signbit(168.1234f));
+ static assert(signbit(-168.1234f));
+ static assert(!signbit(0.0f));
+ static assert(signbit(-0.0f));
+ static assert(signbit(-float.max));
+ static assert(!signbit(float.max));
+
+ static assert(!signbit(double.nan));
+ static assert(signbit(-double.nan));
+ static assert(!signbit(168.1234));
+ static assert(signbit(-168.1234));
+ static assert(!signbit(0.0));
+ static assert(signbit(-0.0));
+ static assert(signbit(-double.max));
+ static assert(!signbit(double.max));
+
+ static assert(!signbit(real.nan));
+ static assert(signbit(-real.nan));
+ static assert(!signbit(168.1234L));
+ static assert(signbit(-168.1234L));
+ static assert(!signbit(0.0L));
+ static assert(signbit(-0.0L));
+ static assert(signbit(-real.max));
+ static assert(!signbit(real.max));
+}
+
+/**
+Params:
+ to = the numeric value to use
+ from = the sign value to use
+Returns:
+ a value composed of to with from's sign bit.
+ */
+R copysign(R, X)(R to, X from) @trusted pure nothrow @nogc
+if (isFloatingPoint!(R) && isFloatingPoint!(X))
+{
+ import std.math : floatTraits, RealFormat;
+
+ if (__ctfe)
+ {
+ return signbit(to) == signbit(from) ? to : -to;
+ }
+ ubyte* pto = cast(ubyte *)&to;
+ const ubyte* pfrom = cast(ubyte *)&from;
+
+ alias T = floatTraits!(R);
+ alias F = floatTraits!(X);
+ pto[T.SIGNPOS_BYTE] &= 0x7F;
+ pto[T.SIGNPOS_BYTE] |= pfrom[F.SIGNPOS_BYTE] & 0x80;
+ return to;
+}
+
+/// ditto
+R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc
+if (isIntegral!(X) && isFloatingPoint!(R))
+{
+ return copysign(cast(R) to, from);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(copysign(1.0, 1.0) == 1.0);
+ assert(copysign(1.0, -0.0) == -1.0);
+ assert(copysign(1UL, -1.0) == -1.0);
+ assert(copysign(-1.0, -1.0) == -1.0);
+
+ assert(copysign(real.infinity, -1.0) == -real.infinity);
+ assert(copysign(real.nan, 1.0) is real.nan);
+ assert(copysign(-real.nan, 1.0) is real.nan);
+ assert(copysign(real.nan, -1.0) is -real.nan);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+
+ static foreach (X; AliasSeq!(float, double, real, int, long))
+ {
+ static foreach (Y; AliasSeq!(float, double, real))
+ {{
+ X x = 21;
+ Y y = 23.8;
+ Y e = void;
+
+ e = copysign(x, y);
+ assert(e == 21.0);
+
+ e = copysign(-x, y);
+ assert(e == 21.0);
+
+ e = copysign(x, -y);
+ assert(e == -21.0);
+
+ e = copysign(-x, -y);
+ assert(e == -21.0);
+
+ static if (isFloatingPoint!X)
+ {
+ e = copysign(X.nan, y);
+ assert(isNaN(e) && !signbit(e));
+
+ e = copysign(X.nan, -y);
+ assert(isNaN(e) && signbit(e));
+ }
+ }}
+ }
+ // CTFE
+ static foreach (X; AliasSeq!(float, double, real, int, long))
+ {
+ static foreach (Y; AliasSeq!(float, double, real))
+ {{
+ enum X x = 21;
+ enum Y y = 23.8;
+
+ assert(21.0 == copysign(x, y));
+ assert(21.0 == copysign(-x, y));
+ assert(-21.0 == copysign(x, -y));
+ assert(-21.0 == copysign(-x, -y));
+
+ static if (isFloatingPoint!X)
+ {
+ static assert(isNaN(copysign(X.nan, y)) && !signbit(copysign(X.nan, y)));
+ assert(isNaN(copysign(X.nan, -y)) && signbit(copysign(X.nan, -y)));
+ }
+ }}
+ }
+}
+
+/*********************************
+Returns `-1` if $(D x < 0), `x` if $(D x == 0), `1` if
+$(D x > 0), and $(NAN) if x==$(NAN).
+ */
+F sgn(F)(F x) @safe pure nothrow @nogc
+if (isFloatingPoint!F || isIntegral!F)
+{
+ // @@@TODO@@@: make this faster
+ return x > 0 ? 1 : x < 0 ? -1 : x;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(sgn(168.1234) == 1);
+ assert(sgn(-168.1234) == -1);
+ assert(sgn(0.0) == 0);
+ assert(sgn(-0.0) == 0);
+}
+
+/**
+Check whether a number is an integer power of two.
+
+Note that only positive numbers can be integer powers of two. This
+function always return `false` if `x` is negative or zero.
+
+Params:
+ x = the number to test
+
+Returns:
+ `true` if `x` is an integer power of two.
+*/
+bool isPowerOf2(X)(const X x) pure @safe nothrow @nogc
+if (isNumeric!X)
+{
+ import std.math.exponential : frexp;
+
+ static if (isFloatingPoint!X)
+ {
+ int exp;
+ const X sig = frexp(x, exp);
+
+ return (exp != int.min) && (sig is cast(X) 0.5L);
+ }
+ else
+ {
+ static if (isSigned!X)
+ {
+ auto y = cast(typeof(x + 0))x;
+ return y > 0 && !(y & (y - 1));
+ }
+ else
+ {
+ auto y = cast(typeof(x + 0u))x;
+ return (y & -y) > (y - 1);
+ }
+ }
+}
+///
+@safe unittest
+{
+ import std.math.exponential : pow;
+
+ assert( isPowerOf2(1.0L));
+ assert( isPowerOf2(2.0L));
+ assert( isPowerOf2(0.5L));
+ assert( isPowerOf2(pow(2.0L, 96)));
+ assert( isPowerOf2(pow(2.0L, -77)));
+
+ assert(!isPowerOf2(-2.0L));
+ assert(!isPowerOf2(-0.5L));
+ assert(!isPowerOf2(0.0L));
+ assert(!isPowerOf2(4.315));
+ assert(!isPowerOf2(1.0L / 3.0L));
+
+ assert(!isPowerOf2(real.nan));
+ assert(!isPowerOf2(real.infinity));
+}
+///
+@safe unittest
+{
+ assert( isPowerOf2(1));
+ assert( isPowerOf2(2));
+ assert( isPowerOf2(1uL << 63));
+
+ assert(!isPowerOf2(-4));
+ assert(!isPowerOf2(0));
+ assert(!isPowerOf2(1337u));
+}
+
+@safe unittest
+{
+ import std.math.exponential : pow;
+ import std.meta : AliasSeq;
+
+ enum smallP2 = pow(2.0L, -62);
+ enum bigP2 = pow(2.0L, 50);
+ enum smallP7 = pow(7.0L, -35);
+ enum bigP7 = pow(7.0L, 30);
+
+ static foreach (X; AliasSeq!(float, double, real))
+ {{
+ immutable min_sub = X.min_normal * X.epsilon;
+
+ foreach (x; [smallP2, min_sub, X.min_normal, .25L, 0.5L, 1.0L,
+ 2.0L, 8.0L, pow(2.0L, X.max_exp - 1), bigP2])
+ {
+ assert( isPowerOf2(cast(X) x));
+ assert(!isPowerOf2(cast(X)-x));
+ }
+
+ foreach (x; [0.0L, 3 * min_sub, smallP7, 0.1L, 1337.0L, bigP7, X.max, real.nan, real.infinity])
+ {
+ assert(!isPowerOf2(cast(X) x));
+ assert(!isPowerOf2(cast(X)-x));
+ }
+ }}
+
+ static foreach (X; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ {{
+ foreach (x; [1, 2, 4, 8, (X.max >>> 1) + 1])
+ {
+ assert( isPowerOf2(cast(X) x));
+ static if (isSigned!X)
+ assert(!isPowerOf2(cast(X)-x));
+ }
+
+ foreach (x; [0, 3, 5, 13, 77, X.min, X.max])
+ assert(!isPowerOf2(cast(X) x));
+ }}
+
+ // CTFE
+ static foreach (X; AliasSeq!(float, double, real))
+ {{
+ enum min_sub = X.min_normal * X.epsilon;
+
+ static foreach (x; [smallP2, min_sub, X.min_normal, .25L, 0.5L, 1.0L,
+ 2.0L, 8.0L, pow(2.0L, X.max_exp - 1), bigP2])
+ {
+ static assert( isPowerOf2(cast(X) x));
+ static assert(!isPowerOf2(cast(X)-x));
+ }
+
+ static foreach (x; [0.0L, 3 * min_sub, smallP7, 0.1L, 1337.0L, bigP7, X.max, real.nan, real.infinity])
+ {
+ static assert(!isPowerOf2(cast(X) x));
+ static assert(!isPowerOf2(cast(X)-x));
+ }
+ }}
+
+ static foreach (X; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ {{
+ static foreach (x; [1, 2, 4, 8, (X.max >>> 1) + 1])
+ {
+ static assert( isPowerOf2(cast(X) x));
+ static if (isSigned!X)
+ static assert(!isPowerOf2(cast(X)-x));
+ }
+
+ static foreach (x; [0, 3, 5, 13, 77, X.min, X.max])
+ static assert(!isPowerOf2(cast(X) x));
+ }}
+}
+
diff --git a/libphobos/src/std/math/trigonometry.d b/libphobos/src/std/math/trigonometry.d
new file mode 100644
index 00000000000..06a7cb10b35
--- /dev/null
+++ b/libphobos/src/std/math/trigonometry.d
@@ -0,0 +1,1425 @@
+// Written in the D programming language.
+
+/**
+This is a submodule of $(MREF std, math).
+
+It contains several trigonometric functions.
+
+Copyright: Copyright The D Language Foundation 2000 - 2011.
+ D implementations of tan, atan, and atan2 functions are based on the
+ CEPHES math library, which is Copyright (C) 2001 Stephen L. Moshier
+ $(LT)steve@moshier.net$(GT) and are incorporated herein by permission
+ of the author. The author reserves the right to distribute this
+ material elsewhere under different copying permissions.
+ These modifications are distributed here under the following terms:
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
+ Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
+Source: $(PHOBOSSRC std/math/trigonometry.d)
+
+Macros:
+ TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
+ <caption>Special Values</caption>
+ $0</table>
+ SVH = $(TR $(TH $1) $(TH $2))
+ SV = $(TR $(TD $1) $(TD $2))
+ TH3 = $(TR $(TH $1) $(TH $2) $(TH $3))
+ TD3 = $(TR $(TD $1) $(TD $2) $(TD $3))
+ TABLE_DOMRG = <table border="1" cellpadding="4" cellspacing="0">
+ $(SVH Domain X, Range Y)
+ $(SV $1, $2)
+ </table>
+ DOMAIN=$1
+ RANGE=$1
+ POWER = $1<sup>$2</sup>
+ NAN = $(RED NAN)
+ PLUSMN = &plusmn;
+ INFIN = &infin;
+ PLUSMNINF = &plusmn;&infin;
+ */
+
+module std.math.trigonometry;
+
+static import core.math;
+
+version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
+version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
+
+version (InlineAsm_X86_Any) version = InlineAsm_X87;
+version (InlineAsm_X87)
+{
+ static assert(real.mant_dig == 64);
+ version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC;
+}
+
+/***********************************
+ * Returns cosine of x. x is in radians.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH cos(x)) $(TH invalid?))
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes) )
+ * )
+ * Bugs:
+ * Results are undefined if |x| >= $(POWER 2,64).
+ */
+pragma(inline, true)
+real cos(real x) @safe pure nothrow @nogc { return core.math.cos(x); }
+///ditto
+pragma(inline, true)
+double cos(double x) @safe pure nothrow @nogc { return core.math.cos(x); }
+///ditto
+pragma(inline, true)
+float cos(float x) @safe pure nothrow @nogc { return core.math.cos(x); }
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+
+ assert(cos(0.0) == 1.0);
+ assert(cos(1.0).isClose(0.5403023059));
+ assert(cos(3.0).isClose(-0.9899924966));
+}
+
+@safe unittest
+{
+ real function(real) pcos = &cos;
+ assert(pcos != null);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.algebraic : fabs;
+
+ float f = cos(-2.0f);
+ assert(fabs(f - -0.416147f) < .00001);
+
+ double d = cos(-2.0);
+ assert(fabs(d - -0.416147f) < .00001);
+
+ real r = cos(-2.0L);
+ assert(fabs(r - -0.416147f) < .00001);
+}
+
+/***********************************
+ * Returns $(HTTP en.wikipedia.org/wiki/Sine, sine) of x. x is in $(HTTP en.wikipedia.org/wiki/Radian, radians).
+ *
+ * $(TABLE_SV
+ * $(TH3 x , sin(x) , invalid?)
+ * $(TD3 $(NAN) , $(NAN) , yes )
+ * $(TD3 $(PLUSMN)0.0, $(PLUSMN)0.0, no )
+ * $(TD3 $(PLUSMNINF), $(NAN) , yes )
+ * )
+ *
+ * Params:
+ * x = angle in radians (not degrees)
+ * Returns:
+ * sine of x
+ * See_Also:
+ * $(MYREF cos), $(MYREF tan), $(MYREF asin)
+ * Bugs:
+ * Results are undefined if |x| >= $(POWER 2,64).
+ */
+pragma(inline, true)
+real sin(real x) @safe pure nothrow @nogc { return core.math.sin(x); }
+///ditto
+pragma(inline, true)
+double sin(double x) @safe pure nothrow @nogc { return core.math.sin(x); }
+///ditto
+pragma(inline, true)
+float sin(float x) @safe pure nothrow @nogc { return core.math.sin(x); }
+
+///
+@safe unittest
+{
+ import std.math.constants : PI;
+ import std.stdio : writefln;
+
+ void someFunc()
+ {
+ real x = 30.0;
+ auto result = sin(x * (PI / 180)); // convert degrees to radians
+ writefln("The sine of %s degrees is %s", x, result);
+ }
+}
+
+@safe unittest
+{
+ real function(real) psin = &sin;
+ assert(psin != null);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.algebraic : fabs;
+
+ float f = sin(-2.0f);
+ assert(fabs(f - -0.909297f) < .00001);
+
+ double d = sin(-2.0);
+ assert(fabs(d - -0.909297f) < .00001);
+
+ real r = sin(-2.0L);
+ assert(fabs(r - -0.909297f) < .00001);
+}
+
+/****************************************************************************
+ * Returns tangent of x. x is in radians.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH tan(x)) $(TH invalid?))
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD yes))
+ * )
+ */
+pragma(inline, true)
+real tan(real x) @safe pure nothrow @nogc
+{
+ version (InlineAsm_X87)
+ {
+ if (!__ctfe)
+ return tanAsm(x);
+ }
+ return tanImpl(x);
+}
+
+/// ditto
+pragma(inline, true)
+double tan(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) tan(cast(real) x) : tanImpl(x); }
+
+/// ditto
+pragma(inline, true)
+float tan(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) tan(cast(real) x) : tanImpl(x); }
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isIdentical;
+ import std.math.constants : PI;
+ import std.math.algebraic : sqrt;
+
+ assert(isIdentical(tan(0.0), 0.0));
+ assert(tan(PI).isClose(0, 0.0, 1e-10));
+ assert(tan(PI / 3).isClose(sqrt(3.0)));
+}
+
+version (InlineAsm_X87)
+private real tanAsm(real x) @trusted pure nothrow @nogc
+{
+ // Separating `return real.nan` from the asm block on LDC produces unintended
+ // behaviour as additional instructions are generated, invalidating the asm
+ // logic inside the previous block. To circumvent this, we can push rnan
+ // manually by creating an immutable variable in the stack.
+ immutable rnan = real.nan;
+
+ version (X86)
+ {
+ asm pure nothrow @nogc
+ {
+ fld x[EBP] ; // load theta
+ fxam ; // test for oddball values
+ fstsw AX ;
+ sahf ;
+ jc trigerr ; // x is NAN, infinity, or empty
+ // 387's can handle subnormals
+SC18: fptan ;
+ fstsw AX ;
+ sahf ;
+ jnp Clear1 ; // C2 = 1 (x is out of range)
+
+ // Do argument reduction to bring x into range
+ fldpi ;
+ fxch ;
+SC17: fprem1 ;
+ fstsw AX ;
+ sahf ;
+ jp SC17 ;
+ fstp ST(1) ; // remove pi from stack
+ jmp SC18 ;
+
+trigerr:
+ jnp Lret ; // if theta is NAN, return theta
+ fstp ST(0) ; // dump theta
+ fld rnan ; // return rnan
+ jmp Lret ;
+Clear1:
+ fstp ST(0) ; // dump X, which is always 1
+Lret:
+ ;
+ }
+ }
+ else version (X86_64)
+ {
+ version (Win64)
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RCX] ; // load theta
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ fld x[RBP] ; // load theta
+ }
+ }
+ asm pure nothrow @nogc
+ {
+ fxam ; // test for oddball values
+ fstsw AX ;
+ test AH,1 ;
+ jnz trigerr ; // x is NAN, infinity, or empty
+ // 387's can handle subnormals
+SC18: fptan ;
+ fstsw AX ;
+ test AH,4 ;
+ jz Clear1 ; // C2 = 1 (x is out of range)
+
+ // Do argument reduction to bring x into range
+ fldpi ;
+ fxch ;
+SC17: fprem1 ;
+ fstsw AX ;
+ test AH,4 ;
+ jnz SC17 ;
+ fstp ST(1) ; // remove pi from stack
+ jmp SC18 ;
+
+trigerr:
+ test AH,4 ;
+ jz Lret ; // if theta is NAN, return theta
+ fstp ST(0) ; // dump theta
+ fld rnan ; // return rnan
+ jmp Lret ;
+Clear1:
+ fstp ST(0) ; // dump X, which is always 1
+Lret:
+ ;
+ }
+ }
+ else
+ static assert(0);
+}
+
+private T tanImpl(T)(T x) @safe pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.constants : PI, PI_4;
+ import std.math.rounding : floor;
+ import std.math.algebraic : poly;
+ import std.math.traits : isInfinity, isNaN, signbit;
+
+ // Coefficients for tan(x) and PI/4 split into three parts.
+ enum realFormat = floatTraits!T.realFormat;
+ static if (realFormat == RealFormat.ieeeQuadruple)
+ {
+ static immutable T[6] P = [
+ 2.883414728874239697964612246732416606301E10L,
+ -2.307030822693734879744223131873392503321E9L,
+ 5.160188250214037865511600561074819366815E7L,
+ -4.249691853501233575668486667664718192660E5L,
+ 1.272297782199996882828849455156962260810E3L,
+ -9.889929415807650724957118893791829849557E-1L
+ ];
+ static immutable T[7] Q = [
+ 8.650244186622719093893836740197250197602E10L,
+ -4.152206921457208101480801635640958361612E10L,
+ 2.758476078803232151774723646710890525496E9L,
+ -5.733709132766856723608447733926138506824E7L,
+ 4.529422062441341616231663543669583527923E5L,
+ -1.317243702830553658702531997959756728291E3L,
+ 1.0
+ ];
+
+ enum T P1 =
+ 7.853981633974483067550664827649598009884357452392578125E-1L;
+ enum T P2 =
+ 2.8605943630549158983813312792950660807511260829685741796657E-18L;
+ enum T P3 =
+ 2.1679525325309452561992610065108379921905808E-35L;
+ }
+ else static if (realFormat == RealFormat.ieeeExtended ||
+ realFormat == RealFormat.ieeeDouble)
+ {
+ static immutable T[3] P = [
+ -1.7956525197648487798769E7L,
+ 1.1535166483858741613983E6L,
+ -1.3093693918138377764608E4L,
+ ];
+ static immutable T[5] Q = [
+ -5.3869575592945462988123E7L,
+ 2.5008380182335791583922E7L,
+ -1.3208923444021096744731E6L,
+ 1.3681296347069295467845E4L,
+ 1.0000000000000000000000E0L,
+ ];
+
+ enum T P1 = 7.853981554508209228515625E-1L;
+ enum T P2 = 7.946627356147928367136046290398E-9L;
+ enum T P3 = 3.061616997868382943065164830688E-17L;
+ }
+ else static if (realFormat == RealFormat.ieeeSingle)
+ {
+ static immutable T[6] P = [
+ 3.33331568548E-1,
+ 1.33387994085E-1,
+ 5.34112807005E-2,
+ 2.44301354525E-2,
+ 3.11992232697E-3,
+ 9.38540185543E-3,
+ ];
+
+ enum T P1 = 0.78515625;
+ enum T P2 = 2.4187564849853515625E-4;
+ enum T P3 = 3.77489497744594108E-8;
+ }
+ else
+ static assert(0, "no coefficients for tan()");
+
+ // Special cases.
+ if (x == cast(T) 0.0 || isNaN(x))
+ return x;
+ if (isInfinity(x))
+ return T.nan;
+
+ // Make argument positive but save the sign.
+ bool sign = false;
+ if (signbit(x))
+ {
+ sign = true;
+ x = -x;
+ }
+
+ // Compute x mod PI/4.
+ static if (realFormat == RealFormat.ieeeSingle)
+ {
+ enum T FOPI = 4 / PI;
+ int j = cast(int) (FOPI * x);
+ T y = j;
+ T z;
+ }
+ else
+ {
+ T y = floor(x / cast(T) PI_4);
+ // Strip high bits of integer part.
+ enum T highBitsFactor = (realFormat == RealFormat.ieeeDouble ? 0x1p3 : 0x1p4);
+ enum T highBitsInv = 1.0 / highBitsFactor;
+ T z = y * highBitsInv;
+ // Compute y - 2^numHighBits * (y / 2^numHighBits).
+ z = y - highBitsFactor * floor(z);
+
+ // Integer and fraction part modulo one octant.
+ int j = cast(int)(z);
+ }
+
+ // Map zeros and singularities to origin.
+ if (j & 1)
+ {
+ j += 1;
+ y += cast(T) 1.0;
+ }
+
+ z = ((x - y * P1) - y * P2) - y * P3;
+ const T zz = z * z;
+
+ enum T zzThreshold = (realFormat == RealFormat.ieeeSingle ? 1.0e-4L :
+ realFormat == RealFormat.ieeeDouble ? 1.0e-14L : 1.0e-20L);
+ if (zz > zzThreshold)
+ {
+ static if (realFormat == RealFormat.ieeeSingle)
+ y = z + z * (zz * poly(zz, P));
+ else
+ y = z + z * (zz * poly(zz, P) / poly(zz, Q));
+ }
+ else
+ y = z;
+
+ if (j & 2)
+ y = (cast(T) -1.0) / y;
+
+ return (sign) ? -y : y;
+}
+
+@safe @nogc nothrow unittest
+{
+ static void testTan(T)()
+ {
+ import std.math.operations : CommonDefaultFor, isClose, NaN;
+ import std.math.traits : isIdentical, isNaN;
+ import std.math.constants : PI, PI_4;
+
+ // ±0
+ const T zero = 0.0;
+ assert(isIdentical(tan(zero), zero));
+ assert(isIdentical(tan(-zero), -zero));
+ // ±∞
+ const T inf = T.infinity;
+ assert(isNaN(tan(inf)));
+ assert(isNaN(tan(-inf)));
+ // NaN
+ const T specialNaN = NaN(0x0123L);
+ assert(isIdentical(tan(specialNaN), specialNaN));
+
+ static immutable T[2][] vals =
+ [
+ // angle, tan
+ [ .5, .54630248984],
+ [ 1, 1.5574077247],
+ [ 1.5, 14.101419947],
+ [ 2, -2.1850398633],
+ [ 2.5,-.74702229724],
+ [ 3, -.14254654307],
+ [ 3.5, .37458564016],
+ [ 4, 1.1578212823],
+ [ 4.5, 4.6373320546],
+ [ 5, -3.3805150062],
+ [ 5.5,-.99558405221],
+ [ 6, -.29100619138],
+ [ 6.5, .22027720035],
+ [ 10, .64836082746],
+
+ // special angles
+ [ PI_4, 1],
+ //[ PI_2, T.infinity], // PI_2 is not _exactly_ pi/2.
+ [ 3*PI_4, -1],
+ [ PI, 0],
+ [ 5*PI_4, 1],
+ //[ 3*PI_2, -T.infinity],
+ [ 7*PI_4, -1],
+ [ 2*PI, 0],
+ ];
+
+ foreach (ref val; vals)
+ {
+ T x = val[0];
+ T r = val[1];
+ T t = tan(x);
+
+ //printf("tan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) t, cast(real) r);
+ assert(isClose(r, t, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T)));
+
+ x = -x;
+ r = -r;
+ t = tan(x);
+ //printf("tan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) t, cast(real) r);
+ assert(isClose(r, t, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T)));
+ }
+ }
+
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(real, double, float))
+ testTan!T();
+
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+ import std.math.algebraic : sqrt;
+ assert(isClose(tan(PI / 3), sqrt(3.0L), real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.math.algebraic : fabs;
+ import std.math.traits : isNaN;
+
+ float f = tan(-2.0f);
+ assert(fabs(f - 2.18504f) < .00001);
+
+ double d = tan(-2.0);
+ assert(fabs(d - 2.18504f) < .00001);
+
+ real r = tan(-2.0L);
+ assert(fabs(r - 2.18504f) < .00001);
+
+ // Verify correct behavior for large inputs
+ assert(!isNaN(tan(0x1p63)));
+ assert(!isNaN(tan(-0x1p63)));
+ static if (real.mant_dig >= 64)
+ {
+ assert(!isNaN(tan(0x1p300L)));
+ assert(!isNaN(tan(-0x1p300L)));
+ }
+}
+
+/***************
+ * Calculates the arc cosine of x,
+ * returning a value ranging from 0 to $(PI).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH acos(x)) $(TH invalid?))
+ * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes))
+ * )
+ */
+real acos(real x) @safe pure nothrow @nogc
+{
+ import core.math : sqrt;
+
+ return atan2(sqrt(1-x*x), x);
+}
+
+/// ditto
+double acos(double x) @safe pure nothrow @nogc { return acos(cast(real) x); }
+
+/// ditto
+float acos(float x) @safe pure nothrow @nogc { return acos(cast(real) x); }
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isNaN;
+ import std.math.constants : PI;
+
+ assert(acos(0.0).isClose(1.570796327));
+ assert(acos(0.5).isClose(PI / 3));
+ assert(acos(PI).isNaN);
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+
+ assert(isClose(acos(0.5), PI / 3, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+/***************
+ * Calculates the arc sine of x,
+ * returning a value ranging from -$(PI)/2 to $(PI)/2.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH asin(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes))
+ * )
+ */
+real asin(real x) @safe pure nothrow @nogc
+{
+ import core.math : sqrt;
+
+ return atan2(x, sqrt(1-x*x));
+}
+
+/// ditto
+double asin(double x) @safe pure nothrow @nogc { return asin(cast(real) x); }
+
+/// ditto
+float asin(float x) @safe pure nothrow @nogc { return asin(cast(real) x); }
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isIdentical, isNaN;
+ import std.math.constants : PI;
+
+ assert(isIdentical(asin(0.0), 0.0));
+ assert(asin(0.5).isClose(PI / 6));
+ assert(asin(PI).isNaN);
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+
+ assert(isClose(asin(0.5), PI / 6, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+/***************
+ * Calculates the arc tangent of x,
+ * returning a value ranging from -$(PI)/2 to $(PI)/2.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH atan(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes))
+ * )
+ */
+pragma(inline, true)
+real atan(real x) @safe pure nothrow @nogc
+{
+ version (InlineAsm_X87)
+ {
+ if (!__ctfe)
+ return atan2Asm(x, 1.0L);
+ }
+ return atanImpl(x);
+}
+
+/// ditto
+pragma(inline, true)
+double atan(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) atan(cast(real) x) : atanImpl(x); }
+
+/// ditto
+pragma(inline, true)
+float atan(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) atan(cast(real) x) : atanImpl(x); }
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isIdentical;
+ import std.math.constants : PI;
+ import std.math.algebraic : sqrt;
+
+ assert(isIdentical(atan(0.0), 0.0));
+ assert(atan(sqrt(3.0)).isClose(PI / 3));
+}
+
+private T atanImpl(T)(T x) @safe pure nothrow @nogc
+{
+ import std.math : floatTraits, RealFormat;
+ import std.math.traits : copysign, isInfinity, signbit;
+ import std.math.constants : PI_2, PI_4;
+ import std.math.algebraic : poly;
+
+ // Coefficients for atan(x)
+ enum realFormat = floatTraits!T.realFormat;
+ static if (realFormat == RealFormat.ieeeQuadruple)
+ {
+ static immutable T[9] P = [
+ -6.880597774405940432145577545328795037141E2L,
+ -2.514829758941713674909996882101723647996E3L,
+ -3.696264445691821235400930243493001671932E3L,
+ -2.792272753241044941703278827346430350236E3L,
+ -1.148164399808514330375280133523543970854E3L,
+ -2.497759878476618348858065206895055957104E2L,
+ -2.548067867495502632615671450650071218995E1L,
+ -8.768423468036849091777415076702113400070E-1L,
+ -6.635810778635296712545011270011752799963E-4L
+ ];
+ static immutable T[9] Q = [
+ 2.064179332321782129643673263598686441900E3L,
+ 8.782996876218210302516194604424986107121E3L,
+ 1.547394317752562611786521896296215170819E4L,
+ 1.458510242529987155225086911411015961174E4L,
+ 7.928572347062145288093560392463784743935E3L,
+ 2.494680540950601626662048893678584497900E3L,
+ 4.308348370818927353321556740027020068897E2L,
+ 3.566239794444800849656497338030115886153E1L,
+ 1.0
+ ];
+ }
+ else static if (realFormat == RealFormat.ieeeExtended)
+ {
+ static immutable T[5] P = [
+ -5.0894116899623603312185E1L,
+ -9.9988763777265819915721E1L,
+ -6.3976888655834347413154E1L,
+ -1.4683508633175792446076E1L,
+ -8.6863818178092187535440E-1L,
+ ];
+ static immutable T[6] Q = [
+ 1.5268235069887081006606E2L,
+ 3.9157570175111990631099E2L,
+ 3.6144079386152023162701E2L,
+ 1.4399096122250781605352E2L,
+ 2.2981886733594175366172E1L,
+ 1.0000000000000000000000E0L,
+ ];
+ }
+ else static if (realFormat == RealFormat.ieeeDouble)
+ {
+ static immutable T[5] P = [
+ -6.485021904942025371773E1L,
+ -1.228866684490136173410E2L,
+ -7.500855792314704667340E1L,
+ -1.615753718733365076637E1L,
+ -8.750608600031904122785E-1L,
+ ];
+ static immutable T[6] Q = [
+ 1.945506571482613964425E2L,
+ 4.853903996359136964868E2L,
+ 4.328810604912902668951E2L,
+ 1.650270098316988542046E2L,
+ 2.485846490142306297962E1L,
+ 1.000000000000000000000E0L,
+ ];
+
+ enum T MOREBITS = 6.123233995736765886130E-17L;
+ }
+ else static if (realFormat == RealFormat.ieeeSingle)
+ {
+ static immutable T[4] P = [
+ -3.33329491539E-1,
+ 1.99777106478E-1,
+ -1.38776856032E-1,
+ 8.05374449538E-2,
+ ];
+ }
+ else
+ static assert(0, "no coefficients for atan()");
+
+ // tan(PI/8)
+ enum T TAN_PI_8 = 0.414213562373095048801688724209698078569672L;
+ // tan(3 * PI/8)
+ enum T TAN3_PI_8 = 2.414213562373095048801688724209698078569672L;
+
+ // Special cases.
+ if (x == cast(T) 0.0)
+ return x;
+ if (isInfinity(x))
+ return copysign(cast(T) PI_2, x);
+
+ // Make argument positive but save the sign.
+ bool sign = false;
+ if (signbit(x))
+ {
+ sign = true;
+ x = -x;
+ }
+
+ static if (realFormat == RealFormat.ieeeDouble) // special case for double precision
+ {
+ short flag = 0;
+ T y;
+ if (x > TAN3_PI_8)
+ {
+ y = PI_2;
+ flag = 1;
+ x = -(1.0 / x);
+ }
+ else if (x <= 0.66)
+ {
+ y = 0.0;
+ }
+ else
+ {
+ y = PI_4;
+ flag = 2;
+ x = (x - 1.0)/(x + 1.0);
+ }
+
+ T z = x * x;
+ z = z * poly(z, P) / poly(z, Q);
+ z = x * z + x;
+ if (flag == 2)
+ z += 0.5 * MOREBITS;
+ else if (flag == 1)
+ z += MOREBITS;
+ y = y + z;
+ }
+ else
+ {
+ // Range reduction.
+ T y;
+ if (x > TAN3_PI_8)
+ {
+ y = PI_2;
+ x = -((cast(T) 1.0) / x);
+ }
+ else if (x > TAN_PI_8)
+ {
+ y = PI_4;
+ x = (x - cast(T) 1.0)/(x + cast(T) 1.0);
+ }
+ else
+ y = 0.0;
+
+ // Rational form in x^^2.
+ const T z = x * x;
+ static if (realFormat == RealFormat.ieeeSingle)
+ y += poly(z, P) * z * x + x;
+ else
+ y = y + (poly(z, P) / poly(z, Q)) * z * x + x;
+ }
+
+ return (sign) ? -y : y;
+}
+
+@safe @nogc nothrow unittest
+{
+ static void testAtan(T)()
+ {
+ import std.math.operations : CommonDefaultFor, isClose, NaN;
+ import std.math.traits : isIdentical;
+ import std.math.constants : PI_2, PI_4;
+
+ // ±0
+ const T zero = 0.0;
+ assert(isIdentical(atan(zero), zero));
+ assert(isIdentical(atan(-zero), -zero));
+ // ±∞
+ const T inf = T.infinity;
+ assert(isClose(atan(inf), cast(T) PI_2));
+ assert(isClose(atan(-inf), cast(T) -PI_2));
+ // NaN
+ const T specialNaN = NaN(0x0123L);
+ assert(isIdentical(atan(specialNaN), specialNaN));
+
+ static immutable T[2][] vals =
+ [
+ // x, atan(x)
+ [ 0.25, 0.24497866313 ],
+ [ 0.5, 0.46364760900 ],
+ [ 1, PI_4 ],
+ [ 1.5, 0.98279372325 ],
+ [ 10, 1.47112767430 ],
+ ];
+
+ foreach (ref val; vals)
+ {
+ T x = val[0];
+ T r = val[1];
+ T a = atan(x);
+
+ //printf("atan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) a, cast(real) r);
+ assert(isClose(r, a, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T)));
+
+ x = -x;
+ r = -r;
+ a = atan(x);
+ //printf("atan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) a, cast(real) r);
+ assert(isClose(r, a, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T)));
+ }
+ }
+
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(real, double, float))
+ testAtan!T();
+
+ import std.math.operations : isClose;
+ import std.math.algebraic : sqrt;
+ import std.math.constants : PI;
+ assert(isClose(atan(sqrt(3.0L)), PI / 3, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+/***************
+ * Calculates the arc tangent of y / x,
+ * returning a value ranging from -$(PI) to $(PI).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH y) $(TH x) $(TH atan(y, x)))
+ * $(TR $(TD $(NAN)) $(TD anything) $(TD $(NAN)) )
+ * $(TR $(TD anything) $(TD $(NAN)) $(TD $(NAN)) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) $(TD $(PLUSMN)0.0) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT)0.0) $(TD $(PLUSMN)$(PI)))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -0.0) $(TD $(PLUSMN)$(PI)))
+ * $(TR $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) $(TD $(PI)/2) )
+ * $(TR $(TD $(LT)0.0) $(TD $(PLUSMN)0.0) $(TD -$(PI)/2) )
+ * $(TR $(TD $(GT)0.0) $(TD $(INFIN)) $(TD $(PLUSMN)0.0) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD anything) $(TD $(PLUSMN)$(PI)/2))
+ * $(TR $(TD $(GT)0.0) $(TD -$(INFIN)) $(TD $(PLUSMN)$(PI)) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(INFIN)) $(TD $(PLUSMN)$(PI)/4))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD -$(INFIN)) $(TD $(PLUSMN)3$(PI)/4))
+ * )
+ */
+pragma(inline, true)
+real atan2(real y, real x) @trusted pure nothrow @nogc // TODO: @safe
+{
+ version (InlineAsm_X87)
+ {
+ if (!__ctfe)
+ return atan2Asm(y, x);
+ }
+ return atan2Impl(y, x);
+}
+
+/// ditto
+pragma(inline, true)
+double atan2(double y, double x) @safe pure nothrow @nogc
+{
+ return __ctfe ? cast(double) atan2(cast(real) y, cast(real) x) : atan2Impl(y, x);
+}
+
+/// ditto
+pragma(inline, true)
+float atan2(float y, float x) @safe pure nothrow @nogc
+{
+ return __ctfe ? cast(float) atan2(cast(real) y, cast(real) x) : atan2Impl(y, x);
+}
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+ import std.math.constants : PI;
+ import std.math.algebraic : sqrt;
+
+ assert(atan2(1.0, sqrt(3.0)).isClose(PI / 6));
+}
+
+version (InlineAsm_X87)
+private real atan2Asm(real y, real x) @trusted pure nothrow @nogc
+{
+ version (Win64)
+ {
+ asm pure nothrow @nogc {
+ naked;
+ fld real ptr [RDX]; // y
+ fld real ptr [RCX]; // x
+ fpatan;
+ ret;
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc {
+ fld y;
+ fld x;
+ fpatan;
+ }
+ }
+}
+
+private T atan2Impl(T)(T y, T x) @safe pure nothrow @nogc
+{
+ import std.math.traits : copysign, isInfinity, isNaN, signbit;
+ import std.math.constants : PI, PI_2, PI_4;
+
+ // Special cases.
+ if (isNaN(x) || isNaN(y))
+ return T.nan;
+ if (y == cast(T) 0.0)
+ {
+ if (x >= 0 && !signbit(x))
+ return copysign(0, y);
+ else
+ return copysign(cast(T) PI, y);
+ }
+ if (x == cast(T) 0.0)
+ return copysign(cast(T) PI_2, y);
+ if (isInfinity(x))
+ {
+ if (signbit(x))
+ {
+ if (isInfinity(y))
+ return copysign(3 * cast(T) PI_4, y);
+ else
+ return copysign(cast(T) PI, y);
+ }
+ else
+ {
+ if (isInfinity(y))
+ return copysign(cast(T) PI_4, y);
+ else
+ return copysign(cast(T) 0.0, y);
+ }
+ }
+ if (isInfinity(y))
+ return copysign(cast(T) PI_2, y);
+
+ // Call atan and determine the quadrant.
+ T z = atan(y / x);
+
+ if (signbit(x))
+ {
+ if (signbit(y))
+ z = z - cast(T) PI;
+ else
+ z = z + cast(T) PI;
+ }
+
+ if (z == cast(T) 0.0)
+ return copysign(z, y);
+
+ return z;
+}
+
+@safe @nogc nothrow unittest
+{
+ static void testAtan2(T)()
+ {
+ import std.math.operations : isClose;
+ import std.math.traits : isIdentical, isNaN;
+ import std.math.constants : PI, PI_2, PI_4;
+
+ // NaN
+ const T nan = T.nan;
+ assert(isNaN(atan2(nan, cast(T) 1)));
+ assert(isNaN(atan2(cast(T) 1, nan)));
+
+ const T inf = T.infinity;
+ static immutable T[3][] vals =
+ [
+ // y, x, atan2(y, x)
+
+ // ±0
+ [ 0.0, 1.0, 0.0 ],
+ [ -0.0, 1.0, -0.0 ],
+ [ 0.0, 0.0, 0.0 ],
+ [ -0.0, 0.0, -0.0 ],
+ [ 0.0, -1.0, PI ],
+ [ -0.0, -1.0, -PI ],
+ [ 0.0, -0.0, PI ],
+ [ -0.0, -0.0, -PI ],
+ [ 1.0, 0.0, PI_2 ],
+ [ 1.0, -0.0, PI_2 ],
+ [ -1.0, 0.0, -PI_2 ],
+ [ -1.0, -0.0, -PI_2 ],
+
+ // ±∞
+ [ 1.0, inf, 0.0 ],
+ [ -1.0, inf, -0.0 ],
+ [ 1.0, -inf, PI ],
+ [ -1.0, -inf, -PI ],
+ [ inf, 1.0, PI_2 ],
+ [ inf, -1.0, PI_2 ],
+ [ -inf, 1.0, -PI_2 ],
+ [ -inf, -1.0, -PI_2 ],
+ [ inf, inf, PI_4 ],
+ [ -inf, inf, -PI_4 ],
+ [ inf, -inf, 3 * PI_4 ],
+ [ -inf, -inf, -3 * PI_4 ],
+
+ [ 1.0, 1.0, PI_4 ],
+ [ -2.0, 2.0, -PI_4 ],
+ [ 3.0, -3.0, 3 * PI_4 ],
+ [ -4.0, -4.0, -3 * PI_4 ],
+
+ [ 0.75, 0.25, 1.249045772398 ],
+ [ -0.5, 0.375, -0.927295218002 ],
+ [ 0.5, -0.125, 1.815774989922 ],
+ [ -0.75, -0.5, -2.158798930342 ],
+ ];
+
+ foreach (ref val; vals)
+ {
+ const T y = val[0];
+ const T x = val[1];
+ const T r = val[2];
+ const T a = atan2(y, x);
+
+ //printf("atan2(%Lg, %Lg) = %Lg, should be %Lg\n", cast(real) y, cast(real) x, cast(real) a, cast(real) r);
+ if (r == 0)
+ assert(isIdentical(r, a)); // check sign
+ else
+ assert(isClose(r, a));
+ }
+ }
+
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(real, double, float))
+ testAtan2!T();
+
+ import std.math.operations : isClose;
+ import std.math.algebraic : sqrt;
+ import std.math.constants : PI;
+ assert(isClose(atan2(1.0L, sqrt(3.0L)), PI / 6, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+/***********************************
+ * Calculates the hyperbolic cosine of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH cosh(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)0.0) $(TD no) )
+ * )
+ */
+real cosh(real x) @safe pure nothrow @nogc
+{
+ import std.math.exponential : exp;
+
+ // cosh = (exp(x)+exp(-x))/2.
+ // The naive implementation works correctly.
+ const real y = exp(x);
+ return (y + 1.0/y) * 0.5;
+}
+
+/// ditto
+double cosh(double x) @safe pure nothrow @nogc { return cosh(cast(real) x); }
+
+/// ditto
+float cosh(float x) @safe pure nothrow @nogc { return cosh(cast(real) x); }
+
+///
+@safe unittest
+{
+ import std.math.constants : E;
+ import std.math.operations : isClose;
+
+ assert(cosh(0.0) == 1.0);
+ assert(cosh(1.0).isClose((E + 1.0 / E) / 2));
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.constants : E;
+ import std.math.operations : isClose;
+
+ assert(isClose(cosh(1.0), (E + 1.0 / E) / 2, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+/***********************************
+ * Calculates the hyperbolic sine of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH sinh(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no))
+ * )
+ */
+real sinh(real x) @safe pure nothrow @nogc { return _sinh(x); }
+
+/// ditto
+double sinh(double x) @safe pure nothrow @nogc { return _sinh(x); }
+
+/// ditto
+float sinh(float x) @safe pure nothrow @nogc { return _sinh(x); }
+
+///
+@safe unittest
+{
+ import std.math.constants : E;
+ import std.math.operations : isClose;
+ import std.math.traits : isIdentical;
+
+ enum sinh1 = (E - 1.0 / E) / 2;
+ import std.meta : AliasSeq;
+ static foreach (F; AliasSeq!(float, double, real))
+ {
+ assert(isIdentical(sinh(F(0.0)), F(0.0)));
+ assert(sinh(F(1.0)).isClose(F(sinh1)));
+ }
+}
+
+private F _sinh(F)(F x)
+{
+ import std.math.traits : copysign;
+ import std.math.exponential : exp, expm1;
+ import core.math : fabs;
+ import std.math.constants : LN2;
+
+ // sinh(x) = (exp(x)-exp(-x))/2;
+ // Very large arguments could cause an overflow, but
+ // the maximum value of x for which exp(x) + exp(-x)) != exp(x)
+ // is x = 0.5 * (real.mant_dig) * LN2. // = 22.1807 for real80.
+ if (fabs(x) > F.mant_dig * F(LN2))
+ {
+ return copysign(F(0.5) * exp(fabs(x)), x);
+ }
+
+ const y = expm1(x);
+ return F(0.5) * y / (y+1) * (y+2);
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.constants : E;
+ import std.math.operations : isClose;
+
+ assert(isClose(sinh(1.0L), real((E - 1.0 / E) / 2), real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+/***********************************
+ * Calculates the hyperbolic tangent of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH tanh(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)1.0) $(TD no))
+ * )
+ */
+real tanh(real x) @safe pure nothrow @nogc { return _tanh(x); }
+
+/// ditto
+double tanh(double x) @safe pure nothrow @nogc { return _tanh(x); }
+
+/// ditto
+float tanh(float x) @safe pure nothrow @nogc { return _tanh(x); }
+
+///
+@safe unittest
+{
+ import std.math.operations : isClose;
+ import std.math.traits : isIdentical;
+
+ assert(isIdentical(tanh(0.0), 0.0));
+ assert(tanh(1.0).isClose(sinh(1.0) / cosh(1.0)));
+}
+
+private F _tanh(F)(F x)
+{
+ import std.math.traits : copysign;
+ import std.math.exponential : expm1;
+ import core.math : fabs;
+ import std.math.constants : LN2;
+
+ // tanh(x) = (exp(x) - exp(-x))/(exp(x)+exp(-x))
+ if (fabs(x) > F.mant_dig * F(LN2))
+ {
+ return copysign(1, x);
+ }
+
+ const y = expm1(2*x);
+ return y / (y + 2);
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(tanh(1.0L), sinh(1.0L) / cosh(1.0L), real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+/***********************************
+ * Calculates the inverse hyperbolic cosine of x.
+ *
+ * Mathematically, acosh(x) = log(x + sqrt( x*x - 1))
+ *
+ * $(TABLE_DOMRG
+ * $(DOMAIN 1..$(INFIN)),
+ * $(RANGE 0..$(INFIN))
+ * )
+ *
+ * $(TABLE_SV
+ * $(SVH x, acosh(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV $(LT)1, $(NAN) )
+ * $(SV 1, 0 )
+ * $(SV +$(INFIN),+$(INFIN))
+ * )
+ */
+real acosh(real x) @safe pure nothrow @nogc { return _acosh(x); }
+
+/// ditto
+double acosh(double x) @safe pure nothrow @nogc { return _acosh(x); }
+
+/// ditto
+float acosh(float x) @safe pure nothrow @nogc { return _acosh(x); }
+
+///
+@safe @nogc nothrow unittest
+{
+ import std.math.traits : isIdentical, isNaN;
+
+ assert(isNaN(acosh(0.9)));
+ assert(isNaN(acosh(real.nan)));
+ assert(isIdentical(acosh(1.0), 0.0));
+ assert(acosh(real.infinity) == real.infinity);
+ assert(isNaN(acosh(0.5)));
+}
+
+private F _acosh(F)(F x) @safe pure nothrow @nogc
+{
+ import std.math.constants : LN2;
+ import std.math.exponential : log;
+ import core.math : sqrt;
+
+ if (x > 1/F.epsilon)
+ return F(LN2) + log(x);
+ else
+ return log(x + sqrt(x*x - 1));
+}
+
+@safe @nogc nothrow unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(acosh(cosh(3.0L)), 3.0L, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+/***********************************
+ * Calculates the inverse hyperbolic sine of x.
+ *
+ * Mathematically,
+ * ---------------
+ * asinh(x) = log( x + sqrt( x*x + 1 )) // if x >= +0
+ * asinh(x) = -log(-x + sqrt( x*x + 1 )) // if x <= -0
+ * -------------
+ *
+ * $(TABLE_SV
+ * $(SVH x, asinh(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV $(PLUSMN)0, $(PLUSMN)0 )
+ * $(SV $(PLUSMN)$(INFIN),$(PLUSMN)$(INFIN))
+ * )
+ */
+real asinh(real x) @safe pure nothrow @nogc { return _asinh(x); }
+
+/// ditto
+double asinh(double x) @safe pure nothrow @nogc { return _asinh(x); }
+
+/// ditto
+float asinh(float x) @safe pure nothrow @nogc { return _asinh(x); }
+
+///
+@safe @nogc nothrow unittest
+{
+ import std.math.traits : isIdentical, isNaN;
+
+ assert(isIdentical(asinh(0.0), 0.0));
+ assert(isIdentical(asinh(-0.0), -0.0));
+ assert(asinh(real.infinity) == real.infinity);
+ assert(asinh(-real.infinity) == -real.infinity);
+ assert(isNaN(asinh(real.nan)));
+}
+
+private F _asinh(F)(F x)
+{
+ import std.math.traits : copysign;
+ import core.math : fabs, sqrt;
+ import std.math.exponential : log, log1p;
+ import std.math.constants : LN2;
+
+ return (fabs(x) > 1 / F.epsilon)
+ // beyond this point, x*x + 1 == x*x
+ ? copysign(F(LN2) + log(fabs(x)), x)
+ // sqrt(x*x + 1) == 1 + x * x / ( 1 + sqrt(x*x + 1) )
+ : copysign(log1p(fabs(x) + x*x / (1 + sqrt(x*x + 1)) ), x);
+}
+
+@safe unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(asinh(sinh(3.0L)), 3.0L, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
+
+/***********************************
+ * Calculates the inverse hyperbolic tangent of x,
+ * returning a value from ranging from -1 to 1.
+ *
+ * Mathematically, atanh(x) = log( (1+x)/(1-x) ) / 2
+ *
+ * $(TABLE_DOMRG
+ * $(DOMAIN -$(INFIN)..$(INFIN)),
+ * $(RANGE -1 .. 1)
+ * )
+ * $(BR)
+ * $(TABLE_SV
+ * $(SVH x, acosh(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV $(PLUSMN)0, $(PLUSMN)0)
+ * $(SV -$(INFIN), -0)
+ * )
+ */
+real atanh(real x) @safe pure nothrow @nogc
+{
+ import std.math.exponential : log1p;
+
+ // log( (1+x)/(1-x) ) == log ( 1 + (2*x)/(1-x) )
+ return 0.5 * log1p( 2 * x / (1 - x) );
+}
+
+/// ditto
+double atanh(double x) @safe pure nothrow @nogc { return atanh(cast(real) x); }
+
+/// ditto
+float atanh(float x) @safe pure nothrow @nogc { return atanh(cast(real) x); }
+
+///
+@safe @nogc nothrow unittest
+{
+ import std.math.traits : isIdentical, isNaN;
+
+ assert(isIdentical(atanh(0.0), 0.0));
+ assert(isIdentical(atanh(-0.0),-0.0));
+ assert(isNaN(atanh(real.nan)));
+ assert(isNaN(atanh(-real.infinity)));
+ assert(atanh(0.0) == 0);
+}
+
+@safe unittest
+{
+ import std.math.operations : isClose;
+
+ assert(isClose(atanh(tanh(0.5L)), 0.5, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
+}
diff --git a/libphobos/src/std/mathspecial.d b/libphobos/src/std/mathspecial.d
index e35c74cc5aa..64ab9bf3714 100644
--- a/libphobos/src/std/mathspecial.d
+++ b/libphobos/src/std/mathspecial.d
@@ -50,7 +50,7 @@
* Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com).
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Stephen L. Moshier (original C code). Conversion to D by Don Clugston
- * Source: $(PHOBOSSRC std/_mathspecial.d)
+ * Source: $(PHOBOSSRC std/mathspecial.d)
*/
module std.mathspecial;
import std.internal.math.errorfunction;
@@ -119,6 +119,7 @@ real logGamma(real x)
*/
real sgnGamma(real x)
{
+ import core.math : rndtol;
/* Author: Don Clugston. */
if (isNaN(x)) return x;
if (x > 0) return 1.0;
@@ -202,13 +203,14 @@ real logmdigammaInverse(real x)
/** Incomplete beta integral
*
- * Returns incomplete beta integral of the arguments, evaluated
+ * Returns regularized incomplete beta integral of the arguments, evaluated
* from zero to x. The regularized incomplete beta function is defined as
*
* betaIncomplete(a, b, x) = $(GAMMA)(a + b) / ( $(GAMMA)(a) $(GAMMA)(b) ) *
* $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t), b-1) dt
*
- * and is the same as the the cumulative distribution function.
+ * and is the same as the cumulative distribution function of the Beta
+ * distribution.
*
* The domain of definition is 0 <= x <= 1. In this
* implementation a and b are restricted to positive values.
@@ -253,21 +255,25 @@ real betaIncompleteInverse(real a, real b, real y )
* values of a and x.
*/
real gammaIncomplete(real a, real x )
-in {
+in
+{
assert(x >= 0);
assert(a > 0);
}
-body {
+do
+{
return std.internal.math.gammafunction.gammaIncomplete(a, x);
}
/** ditto */
real gammaIncompleteCompl(real a, real x )
-in {
+in
+{
assert(x >= 0);
assert(a > 0);
}
-body {
+do
+{
return std.internal.math.gammafunction.gammaIncompleteCompl(a, x);
}
@@ -278,11 +284,13 @@ body {
* gammaIncompleteCompl( a, x ) = p.
*/
real gammaIncompleteComplInverse(real a, real p)
-in {
+in
+{
assert(p >= 0 && p <= 1);
assert(a > 0);
}
-body {
+do
+{
return std.internal.math.gammafunction.gammaIncompleteComplInv(a, p);
}
@@ -321,7 +329,7 @@ real erfc(real x)
}
-/** Normal distribution function.
+/** Standard normal distribution function.
*
* The normal (or Gaussian, or bell-shaped) distribution is
* defined as:
@@ -343,7 +351,7 @@ real normalDistribution(real x)
return std.internal.math.errorfunction.normalDistributionImpl(x);
}
-/** Inverse of Normal distribution function
+/** Inverse of Standard normal distribution function
*
* Returns the argument, x, for which the area under the
* Normal probability density function (integrated from
@@ -352,10 +360,11 @@ real normalDistribution(real x)
* Note: This function is only implemented to 80 bit precision.
*/
real normalDistributionInverse(real p)
-in {
+in
+{
assert(p >= 0.0L && p <= 1.0L, "Domain error");
}
-body
+do
{
return std.internal.math.errorfunction.normalDistributionInvImpl(p);
}
diff --git a/libphobos/src/std/meta.d b/libphobos/src/std/meta.d
index 308e50f041f..1209987eabd 100644
--- a/libphobos/src/std/meta.d
+++ b/libphobos/src/std/meta.d
@@ -1,14 +1,18 @@
// Written in the D programming language.
/**
- * Templates to manipulate template argument lists (also known as type lists).
+ * Templates to manipulate
+ * $(DDSUBLINK spec/template, variadic-templates, template parameter sequences)
+ * (also known as $(I alias sequences)).
*
- * Some operations on alias sequences are built in to the language,
- * such as TL[$(I n)] which gets the $(I n)th type from the
- * alias sequence. TL[$(I lwr) .. $(I upr)] returns a new type
- * list that is a slice of the old one.
+ * Some operations on alias sequences are built into the language,
+ * such as `S[i]`, which accesses the element at index `i` in the
+ * sequence. `S[low .. high]` returns a new alias
+ * sequence that is a slice of the old one.
*
- * Several templates in this module use or operate on eponymous templates that
+ * For more information, see $(DDLINK ctarguments, Compile-time Sequences, Compile-time Sequences).
+ *
+ * $(B Note:) Several templates in this module use or operate on eponymous templates that
* take a single argument and evaluate to a boolean constant. Such templates
* are referred to as $(I template predicates).
*
@@ -54,6 +58,7 @@
* $(TR $(TD Template instantiation) $(TD
* $(LREF ApplyLeft)
* $(LREF ApplyRight)
+ * $(LREF Instantiate)
* ))
* ))
*
@@ -62,26 +67,26 @@
* $(LINK2 http://amazon.com/exec/obidos/ASIN/0201704315/ref=ase_classicempire/102-2957199-2585768,
* Modern C++ Design),
* Andrei Alexandrescu (Addison-Wesley Professional, 2001)
- * Copyright: Copyright Digital Mars 2005 - 2015.
+ * Copyright: Copyright The D Language Foundation 2005 - 2015.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors:
* $(HTTP digitalmars.com, Walter Bright),
* $(HTTP klickverbot.at, David Nadlinger)
- * Source: $(PHOBOSSRC std/_meta.d)
+ * Source: $(PHOBOSSRC std/meta.d)
*/
module std.meta;
+import std.traits : isAggregateType, Unqual, isIterable;
+import std.range.primitives : isInfinite;
+
/**
* Creates a sequence of zero or more aliases. This is most commonly
* used as template parameters or arguments.
*
* In previous versions of Phobos, this was known as `TypeTuple`.
*/
-template AliasSeq(TList...)
-{
- alias AliasSeq = TList;
-}
+alias AliasSeq(TList...) = TList;
///
@safe unittest
@@ -105,70 +110,50 @@ template AliasSeq(TList...)
}
-/**
- Returns an `AliasSeq` expression of `Func` being
- applied to every variadic template argument.
- */
-
///
@safe unittest
{
- auto ref ArgCall(alias Func, alias arg)()
+ // Creates a compile-time sequence of function call expressions
+ // that each call `func` with the next variadic template argument
+ template Map(alias func, args...)
{
- return Func(arg);
- }
+ auto ref lazyItem() {return func(args[0]);}
- template Map(alias Func, args...)
- {
- static if (args.length > 1)
+ static if (args.length == 1)
{
- alias Map = AliasSeq!(ArgCall!(Func, args[0]), Map!(Func, args[1 .. $]));
+ alias Map = lazyItem;
}
else
{
- alias Map = ArgCall!(Func, args[0]);
+ // recurse
+ alias Map = AliasSeq!(lazyItem, Map!(func, args[1 .. $]));
}
}
- static int square(int arg)
- {
- return arg * arg;
- }
-
- static int refSquare(ref int arg)
- {
- arg *= arg;
- return arg;
- }
-
- static ref int refRetSquare(ref int arg)
- {
- arg *= arg;
- return arg;
- }
-
static void test(int a, int b)
{
assert(a == 4);
assert(b == 16);
}
- static void testRef(ref int a, ref int b)
- {
- assert(a++ == 16);
- assert(b++ == 256);
- }
-
static int a = 2;
static int b = 4;
- test(Map!(square, a, b));
+ test(Map!(i => i ^^ 2, a, b));
+ assert(a == 2);
+ assert(b == 4);
- test(Map!(refSquare, a, b));
+ test(Map!((ref i) => i *= i, a, b));
assert(a == 4);
assert(b == 16);
- testRef(Map!(refRetSquare, a, b));
+ static void testRef(ref int a, ref int b)
+ {
+ assert(a++ == 16);
+ assert(b++ == 256);
+ }
+
+ testRef(Map!(function ref(ref i) => i *= i, a, b));
assert(a == 17);
assert(b == 257);
}
@@ -178,15 +163,15 @@ template AliasSeq(TList...)
*
* Not everything can be directly aliased. An alias cannot be declared
* of - for example - a literal:
- *
- * `alias a = 4; //Error`
- *
+ * ---
+ * alias a = 4; //Error
+ * ---
* With this template any single entity can be aliased:
- *
- * `alias b = Alias!4; //OK`
- *
+ * ---
+ * alias b = Alias!4; //OK
+ * ---
* See_Also:
- * To alias more than one thing at once, use $(LREF AliasSeq)
+ * To alias more than one thing at once, use $(LREF AliasSeq).
*/
alias Alias(alias a) = a;
@@ -198,8 +183,8 @@ alias Alias(T) = T;
{
// Without Alias this would fail if Args[0] was e.g. a value and
// some logic would be needed to detect when to use enum instead
- alias Head(Args ...) = Alias!(Args[0]);
- alias Tail(Args ...) = Args[1 .. $];
+ alias Head(Args...) = Alias!(Args[0]);
+ alias Tail(Args...) = Args[1 .. $];
alias Blah = AliasSeq!(3, int, "hello");
static assert(Head!Blah == 3);
@@ -247,8 +232,6 @@ package template OldAlias(alias a)
static assert(0, "Cannot alias " ~ a.stringof);
}
-import std.traits : isAggregateType, Unqual;
-
package template OldAlias(T)
if (!isAggregateType!T || is(Unqual!T == T))
{
@@ -258,7 +241,7 @@ if (!isAggregateType!T || is(Unqual!T == T))
@safe unittest
{
static struct Foo {}
- static assert(is(OldAlias!(const(Foo)) == Foo));
+ //static assert(is(OldAlias!(const(Foo)) == const Foo));
static assert(is(OldAlias!(const(int)) == const(int)));
static assert(OldAlias!123 == 123);
enum abc = 123;
@@ -266,19 +249,21 @@ if (!isAggregateType!T || is(Unqual!T == T))
}
/**
- * Returns the index of the first occurrence of type T in the
- * sequence of zero or more types TList.
- * If not found, -1 is returned.
+ * Returns the index of the first occurrence of `args[0]` in the
+ * sequence `args[1 .. $]`. `args` may be types or compile-time values.
+ * If not found, `-1` is returned.
*/
-template staticIndexOf(T, TList...)
-{
- enum staticIndexOf = genericIndexOf!(T, TList).index;
-}
-
-/// Ditto
-template staticIndexOf(alias T, TList...)
+template staticIndexOf(args...)
+if (args.length >= 1)
{
- enum staticIndexOf = genericIndexOf!(T, TList).index;
+ enum staticIndexOf =
+ {
+ static foreach (idx, arg; args[1 .. $])
+ static if (isSame!(args[0], arg))
+ // `if (__ctfe)` is redundant here but avoids the "Unreachable code" warning.
+ if (__ctfe) return idx;
+ return -1;
+ }();
}
///
@@ -294,34 +279,6 @@ template staticIndexOf(alias T, TList...)
}
}
-// [internal]
-private template genericIndexOf(args...)
-if (args.length >= 1)
-{
- alias e = OldAlias!(args[0]);
- alias tuple = args[1 .. $];
-
- static if (tuple.length)
- {
- alias head = OldAlias!(tuple[0]);
- alias tail = tuple[1 .. $];
-
- static if (isSame!(e, head))
- {
- enum index = 0;
- }
- else
- {
- enum next = genericIndexOf!(e, tail).index;
- enum index = (next == -1) ? -1 : 1 + next;
- }
- }
- else
- {
- enum index = -1;
- }
-}
-
@safe unittest
{
static assert(staticIndexOf!( byte, byte, short, int, long) == 0);
@@ -345,18 +302,17 @@ if (args.length >= 1)
}
/**
- * Returns an `AliasSeq` created from TList with the first occurrence,
- * if any, of T removed.
+ * Returns an `AliasSeq` created from `args[1 .. $]` with the first occurrence,
+ * if any, of `args[0]` removed.
*/
-template Erase(T, TList...)
-{
- alias Erase = GenericErase!(T, TList).result;
-}
-
-/// Ditto
-template Erase(alias T, TList...)
+template Erase(args...)
+if (args.length >= 1)
{
- alias Erase = GenericErase!(T, TList).result;
+ private enum pos = staticIndexOf!(args[0], args[1 .. $]);
+ static if (pos < 0)
+ alias Erase = args[1 .. $];
+ else
+ alias Erase = AliasSeq!(args[1 .. pos + 1], args[pos + 2 .. $]);
}
///
@@ -367,29 +323,6 @@ template Erase(alias T, TList...)
static assert(is(TL == AliasSeq!(int, double, char)));
}
-// [internal]
-private template GenericErase(args...)
-if (args.length >= 1)
-{
- alias e = OldAlias!(args[0]);
- alias tuple = args[1 .. $] ;
-
- static if (tuple.length)
- {
- alias head = OldAlias!(tuple[0]);
- alias tail = tuple[1 .. $];
-
- static if (isSame!(e, head))
- alias result = tail;
- else
- alias result = AliasSeq!(head, GenericErase!(e, tail).result);
- }
- else
- {
- alias result = AliasSeq!();
- }
-}
-
@safe unittest
{
static assert(Pack!(Erase!(int,
@@ -403,54 +336,23 @@ if (args.length >= 1)
/**
- * Returns an `AliasSeq` created from TList with the all occurrences,
- * if any, of T removed.
+ * Returns an `AliasSeq` created from `args[1 .. $]` with all occurrences,
+ * if any, of `args[0]` removed.
*/
-template EraseAll(T, TList...)
-{
- alias EraseAll = GenericEraseAll!(T, TList).result;
-}
-
-/// Ditto
-template EraseAll(alias T, TList...)
+template EraseAll(args...)
+if (args.length >= 1)
{
- alias EraseAll = GenericEraseAll!(T, TList).result;
+ alias EraseAll = AliasSeq!();
+ static foreach (arg; args[1 .. $])
+ static if (!isSame!(args[0], arg))
+ EraseAll = AliasSeq!(EraseAll, arg);
}
///
@safe unittest
{
alias Types = AliasSeq!(int, long, long, int);
-
- alias TL = EraseAll!(long, Types);
- static assert(is(TL == AliasSeq!(int, int)));
-}
-
-// [internal]
-private template GenericEraseAll(args...)
-if (args.length >= 1)
-{
- alias e = OldAlias!(args[0]);
- alias tuple = args[1 .. $];
-
- static if (tuple.length)
- {
- alias head = OldAlias!(tuple[0]);
- alias tail = tuple[1 .. $];
- alias next = AliasSeq!(
- GenericEraseAll!(e, tail[0..$/2]).result,
- GenericEraseAll!(e, tail[$/2..$]).result
- );
-
- static if (isSame!(e, head))
- alias result = next;
- else
- alias result = AliasSeq!(head, next);
- }
- else
- {
- alias result = AliasSeq!();
- }
+ static assert(is(EraseAll!(long, Types) == AliasSeq!(int, int)));
}
@safe unittest
@@ -464,39 +366,33 @@ if (args.length >= 1)
equals!(real, 3, 4, 5, 9));
}
+/*
+ * Returns `items[0 .. $ - 1]` if `item[$ - 1]` found in `items[0 .. $ - 1]`, and
+ * `items` otherwise.
+ *
+ * Params:
+ * items = list to be processed
+ *
+ * See_Also: $(LREF NoDuplicates)
+ */
+private template AppendUnique(items...)
+{
+ alias head = items[0 .. $ - 1];
+ static if (staticIndexOf!(items[$ - 1], head) >= 0)
+ alias AppendUnique = head;
+ else
+ alias AppendUnique = items;
+}
/**
- * Returns an `AliasSeq` created from TList with the all duplicate
+ * Returns an `AliasSeq` created from `args` with all duplicate
* types removed.
*/
-template NoDuplicates(TList...)
+template NoDuplicates(args...)
{
- template EraseAllN(uint N, T...)
- {
- static if (N <= 1)
- {
- alias EraseAllN = T;
- }
- else
- {
- alias EraseAllN = EraseAllN!(N-1, T[1 .. N], EraseAll!(T[0], T[N..$]));
- }
- }
- static if (TList.length > 500)
- {
- enum steps = 16;
- alias first = NoDuplicates!(TList[0 .. steps]);
- alias NoDuplicates = NoDuplicates!(EraseAllN!(first.length, first, TList[steps..$]));
- }
- else static if (TList.length == 0)
- {
- alias NoDuplicates = TList;
- }
- else
- {
- alias NoDuplicates =
- AliasSeq!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $])));
- }
+ alias NoDuplicates = AliasSeq!();
+ static foreach (arg; args)
+ NoDuplicates = AppendUnique!(NoDuplicates, arg);
}
///
@@ -510,9 +406,19 @@ template NoDuplicates(TList...)
@safe unittest
{
- // Bugzilla 14561: huge enums
+ import std.range : iota;
+
+ // https://issues.dlang.org/show_bug.cgi?id=14561: huge enums
alias LongList = Repeat!(1500, int);
static assert(NoDuplicates!LongList.length == 1);
+ // https://issues.dlang.org/show_bug.cgi?id=17995: huge enums, revisited
+
+ alias a = NoDuplicates!(AliasSeq!(1, Repeat!(1000, 3)));
+ alias b = NoDuplicates!(AliasSeq!(1, Repeat!(10, 3)));
+ static assert(a.length == b.length);
+
+ static assert(NoDuplicates!(aliasSeqOf!(iota(7)), aliasSeqOf!(iota(7))) == aliasSeqOf!(iota(7)));
+ static assert(NoDuplicates!(aliasSeqOf!(iota(8)), aliasSeqOf!(iota(8))) == aliasSeqOf!(iota(8)));
}
@safe unittest
@@ -526,7 +432,7 @@ template NoDuplicates(TList...)
/**
* Returns an `AliasSeq` created from TList with the first occurrence
- * of type T, if found, replaced with type U.
+ * of T, if found, replaced with U.
*/
template Replace(T, U, TList...)
{
@@ -606,7 +512,7 @@ if (args.length >= 2)
/**
* Returns an `AliasSeq` created from TList with all occurrences
- * of type T, if found, replaced with type U.
+ * of T, if found, replaced with U.
*/
template ReplaceAll(T, U, TList...)
{
@@ -685,44 +591,34 @@ if (args.length >= 2)
}
/**
- * Returns an `AliasSeq` created from TList with the order reversed.
+ * Returns an `AliasSeq` created from `args` with the order reversed.
*/
-template Reverse(TList...)
+template Reverse(args...)
{
- static if (TList.length <= 1)
- {
- alias Reverse = TList;
- }
- else
- {
- alias Reverse =
- AliasSeq!(
- Reverse!(TList[$/2 .. $ ]),
- Reverse!(TList[ 0 .. $/2]));
- }
+ alias Reverse = AliasSeq!();
+ static foreach_reverse (arg; args)
+ Reverse = AliasSeq!(Reverse, arg);
}
///
@safe unittest
{
- alias Types = AliasSeq!(int, long, long, int, float);
+ alias Types = AliasSeq!(int, long, long, int, float, byte, ubyte, short, ushort, uint);
alias TL = Reverse!(Types);
- static assert(is(TL == AliasSeq!(float, int, long, long, int)));
+ static assert(is(TL == AliasSeq!(uint, ushort, short, ubyte, byte, float, int, long, long, int)));
}
/**
- * Returns the type from TList that is the most derived from type T.
- * If none are found, T is returned.
+ * Returns the type from `TList` that is the most derived from type `T`.
+ * If no such type is found, `T` is returned.
*/
template MostDerived(T, TList...)
{
- static if (TList.length == 0)
- alias MostDerived = T;
- else static if (is(TList[0] : T))
- alias MostDerived = MostDerived!(TList[0], TList[1 .. $]);
- else
- alias MostDerived = MostDerived!(T, TList[1 .. $]);
+ import std.traits : Select;
+ alias MostDerived = T;
+ static foreach (U; TList)
+ MostDerived = Select!(is(U : MostDerived), U, MostDerived);
}
///
@@ -738,19 +634,13 @@ template MostDerived(T, TList...)
}
/**
- * Returns the `AliasSeq` TList with the types sorted so that the most
+ * Returns an `AliasSeq` with the elements of TList sorted so that the most
* derived types come first.
*/
template DerivedToFront(TList...)
{
- static if (TList.length == 0)
- alias DerivedToFront = TList;
- else
- alias DerivedToFront =
- AliasSeq!(MostDerived!(TList[0], TList[1 .. $]),
- DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. $]),
- TList[0],
- TList[1 .. $])));
+ private enum cmp(T, U) = is(T : U);
+ alias DerivedToFront = staticSort!(cmp, TList);
}
///
@@ -763,36 +653,42 @@ template DerivedToFront(TList...)
alias TL = DerivedToFront!(Types);
static assert(is(TL == AliasSeq!(C, B, A)));
+
+ alias TL2 = DerivedToFront!(A, A, A, B, B, B, C, C, C);
+ static assert(is(TL2 == AliasSeq!(C, C, C, B, B, B, A, A, A)));
}
+private enum staticMapExpandFactor = 150;
+private string generateCases()
+{
+ string[staticMapExpandFactor] chunks;
+ chunks[0] = q{};
+ static foreach (enum i; 0 .. staticMapExpandFactor - 1)
+ chunks[i + 1] = chunks[i] ~ `F!(Args[` ~ i.stringof ~ `]),`;
+ string ret = `AliasSeq!(`;
+ foreach (chunk; chunks)
+ ret ~= `q{alias staticMap = AliasSeq!(` ~ chunk ~ `);},`;
+ return ret ~ `)`;
+}
+private alias staticMapBasicCases = AliasSeq!(mixin(generateCases()));
+
/**
Evaluates to $(D AliasSeq!(F!(T[0]), F!(T[1]), ..., F!(T[$ - 1]))).
*/
-template staticMap(alias F, T...)
+template staticMap(alias F, Args ...)
{
- static if (T.length == 0)
- {
- alias staticMap = AliasSeq!();
- }
- else static if (T.length == 1)
- {
- alias staticMap = AliasSeq!(F!(T[0]));
- }
+ static if (Args.length < staticMapExpandFactor)
+ mixin(staticMapBasicCases[Args.length]);
else
- {
- alias staticMap =
- AliasSeq!(
- staticMap!(F, T[ 0 .. $/2]),
- staticMap!(F, T[$/2 .. $ ]));
- }
+ alias staticMap = AliasSeq!(staticMap!(F, Args[0 .. $/2]), staticMap!(F, Args[$/2 .. $]));
}
///
@safe unittest
{
import std.traits : Unqual;
- alias TL = staticMap!(Unqual, int, const int, immutable int);
- static assert(is(TL == AliasSeq!(int, int, int)));
+ alias TL = staticMap!(Unqual, int, const int, immutable int, uint, ubyte, byte, short, ushort);
+ static assert(is(TL == AliasSeq!(int, int, int, uint, ubyte, byte, short, ushort)));
}
@safe unittest
@@ -807,8 +703,17 @@ template staticMap(alias F, T...)
alias Single = staticMap!(Unqual, const int);
static assert(is(Single == AliasSeq!int));
- alias T = staticMap!(Unqual, int, const int, immutable int);
- static assert(is(T == AliasSeq!(int, int, int)));
+ alias T = staticMap!(Unqual, int, const int, immutable int, uint, ubyte, byte, short, ushort, long);
+ static assert(is(T == AliasSeq!(int, int, int, uint, ubyte, byte, short, ushort, long)));
+}
+
+// regression test for https://issues.dlang.org/show_bug.cgi?id=21088
+@system unittest // typeid opEquals is @system
+{
+ enum getTypeId(T) = typeid(T);
+ alias A = staticMap!(getTypeId, int);
+
+ assert(A == typeid(int));
}
/**
@@ -820,20 +725,8 @@ template predicate must be instantiable with all the given items.
*/
template allSatisfy(alias F, T...)
{
- static if (T.length == 0)
- {
- enum allSatisfy = true;
- }
- else static if (T.length == 1)
- {
- enum allSatisfy = F!(T[0]);
- }
- else
- {
- enum allSatisfy =
- allSatisfy!(F, T[ 0 .. $/2]) &&
- allSatisfy!(F, T[$/2 .. $ ]);
- }
+ import core.internal.traits : allSat = allSatisfy;
+ alias allSatisfy = allSat!(F, T);
}
///
@@ -854,20 +747,8 @@ template predicate must be instantiable with one of the given items.
*/
template anySatisfy(alias F, T...)
{
- static if (T.length == 0)
- {
- enum anySatisfy = false;
- }
- else static if (T.length == 1)
- {
- enum anySatisfy = F!(T[0]);
- }
- else
- {
- enum anySatisfy =
- anySatisfy!(F, T[ 0 .. $/2]) ||
- anySatisfy!(F, T[$/2 .. $ ]);
- }
+ import core.internal.traits : anySat = anySatisfy;
+ alias anySatisfy = anySat!(F, T);
}
///
@@ -879,30 +760,169 @@ template anySatisfy(alias F, T...)
static assert( anySatisfy!(isIntegral, int, double));
}
+private alias FilterShortCode = AliasSeq!(
+ q{
+ alias Filter = Nothing;
+ },
+ q{
+ static if (pred!(TList[0]))
+ alias Filter = AliasSeq!(TList[0]);
+ else
+ alias Filter = Nothing;
+ },
+ q{
+ static if (pred!(TList[0]))
+ {
+ static if (pred!(TList[1]))
+ alias Filter = AliasSeq!(TList[0], TList[1]);
+ else
+ alias Filter = AliasSeq!(TList[0]);
+ }
+ else
+ {
+ static if (pred!(TList[1]))
+ alias Filter = AliasSeq!(TList[1]);
+ else
+ alias Filter = Nothing;
+ }
+ },
+ q{
+ static if (pred!(TList[0]))
+ {
+ static if (pred!(TList[1]))
+ {
+ static if (pred!(TList[2]))
+ alias Filter = AliasSeq!(TList[0], TList[1], TList[2]);
+ else
+ alias Filter = AliasSeq!(TList[0], TList[1]);
+ }
+ else
+ {
+ static if (pred!(TList[2]))
+ alias Filter = AliasSeq!(TList[0], TList[2]);
+ else
+ alias Filter = AliasSeq!(TList[0]);
+ }
+ }
+ else
+ {
+ static if (pred!(TList[1]))
+ {
+ static if (pred!(TList[2]))
+ alias Filter = AliasSeq!(TList[1], TList[2]);
+ else
+ alias Filter = AliasSeq!(TList[1]);
+ }
+ else
+ {
+ static if (pred!(TList[2]))
+ alias Filter = AliasSeq!(TList[2]);
+ else
+ alias Filter = Nothing;
+ }
+ }
+ },
+ q{
+ static if (pred!(TList[0]))
+ {
+ static if (pred!(TList[1]))
+ {
+ static if (pred!(TList[2]))
+ {
+ static if (pred!(TList[3]))
+ alias Filter = AliasSeq!(TList[0], TList[1], TList[2], TList[3]);
+ else
+ alias Filter = AliasSeq!(TList[0], TList[1], TList[2]);
+ }
+ else
+ {
+ static if (pred!(TList[3]))
+ alias Filter = AliasSeq!(TList[0], TList[1], TList[3]);
+ else
+ alias Filter = AliasSeq!(TList[0], TList[1]);
+ }
+ }
+ else
+ {
+ static if (pred!(TList[2]))
+ {
+ static if (pred!(TList[3]))
+ alias Filter = AliasSeq!(TList[0], TList[2], TList[3]);
+ else
+ alias Filter = AliasSeq!(TList[0], TList[2]);
+ }
+ else
+ {
+ static if (pred!(TList[3]))
+ alias Filter = AliasSeq!(TList[0], TList[3]);
+ else
+ alias Filter = AliasSeq!(TList[0]);
+ }
+ }
+ }
+ else
+ {
+ static if (pred!(TList[1]))
+ {
+ static if (pred!(TList[2]))
+ {
+ static if (pred!(TList[3]))
+ alias Filter = AliasSeq!(TList[1], TList[2], TList[3]);
+ else
+ alias Filter = AliasSeq!(TList[1], TList[2]);
+ }
+ else
+ {
+ static if (pred!(TList[3]))
+ alias Filter = AliasSeq!(TList[1], TList[3]);
+ else
+ alias Filter = AliasSeq!(TList[1]);
+ }
+ }
+ else
+ {
+ static if (pred!(TList[2]))
+ {
+ static if (pred!(TList[3]))
+ alias Filter = AliasSeq!(TList[2], TList[3]);
+ else
+ alias Filter = AliasSeq!(TList[2]);
+ }
+ else
+ {
+ static if (pred!(TList[3]))
+ alias Filter = AliasSeq!(TList[3]);
+ else
+ alias Filter = Nothing;
+ }
+ }
+ }
+ }
+);
+
+private enum filterExpandFactor = FilterShortCode.length;
+package alias Nothing = AliasSeq!(); // yes, this really does speed up compilation!
/**
- * Filters an $(D AliasSeq) using a template predicate. Returns a
- * $(D AliasSeq) of the elements which satisfy the predicate.
+ * Filters an `AliasSeq` using a template predicate. Returns an
+ * `AliasSeq` of the elements which satisfy the predicate.
*/
-template Filter(alias pred, TList...)
+template Filter(alias pred, TList ...)
{
- static if (TList.length == 0)
+ static if (TList.length < filterExpandFactor)
{
- alias Filter = AliasSeq!();
- }
- else static if (TList.length == 1)
- {
- static if (pred!(TList[0]))
- alias Filter = AliasSeq!(TList[0]);
- else
- alias Filter = AliasSeq!();
+ mixin(FilterShortCode[TList.length]);
}
else
{
- alias Filter =
- AliasSeq!(
- Filter!(pred, TList[ 0 .. $/2]),
- Filter!(pred, TList[$/2 .. $ ]));
+ template MaybeNothing(Q ...)
+ {
+ static if (pred!(Q[0]))
+ alias MaybeNothing = AliasSeq!(Q[0]);
+ else
+ alias MaybeNothing = Nothing;
+ }
+ alias Filter = staticMap!(MaybeNothing, TList);
}
}
@@ -928,9 +948,15 @@ template Filter(alias pred, TList...)
static assert(is(Filter!isPointer == AliasSeq!()));
}
+@safe unittest
+{
+ enum Yes(T) = true;
+ static struct S {}
+ static assert(is(Filter!(Yes, const(int), const(S)) == AliasSeq!(const(int), const(S))));
+}
// Used in template predicate unit tests below.
-private version (unittest)
+private version (StdUnittest)
{
template testAlways(T...)
{
@@ -969,7 +995,7 @@ template templateNot(alias pred)
@safe unittest
{
- foreach (T; AliasSeq!(int, staticMap, 42))
+ static foreach (T; AliasSeq!(int, staticMap, 42))
{
static assert(!Instantiate!(templateNot!testAlways, T));
static assert(Instantiate!(templateNot!testNever, T));
@@ -1013,14 +1039,14 @@ template templateAnd(Preds...)
static assert(storesNegativeNumbers!int);
static assert(!storesNegativeNumbers!string && !storesNegativeNumbers!uint);
- // An empty list of predicates always yields true.
+ // An empty sequence of predicates always yields true.
alias alwaysTrue = templateAnd!();
static assert(alwaysTrue!int);
}
@safe unittest
{
- foreach (T; AliasSeq!(int, staticMap, 42))
+ static foreach (T; AliasSeq!(int, staticMap, 42))
{
static assert( Instantiate!(templateAnd!(), T));
static assert( Instantiate!(templateAnd!(testAlways), T));
@@ -1071,14 +1097,14 @@ template templateOr(Preds...)
static assert( isPtrOrUnsigned!uint && isPtrOrUnsigned!(short*));
static assert(!isPtrOrUnsigned!int && !isPtrOrUnsigned!(string));
- // An empty list of predicates never yields true.
+ // An empty sequence of predicates never yields true.
alias alwaysFalse = templateOr!();
static assert(!alwaysFalse!int);
}
@safe unittest
{
- foreach (T; AliasSeq!(int, staticMap, 42))
+ static foreach (T; AliasSeq!(int, staticMap, 42))
{
static assert( Instantiate!(templateOr!(testAlways), T));
static assert( Instantiate!(templateOr!(testAlways, testAlways), T));
@@ -1097,41 +1123,26 @@ template templateOr(Preds...)
}
/**
- * Converts an input range $(D range) to an alias sequence.
+ * Converts any foreach-iterable entity (e.g. an input range) to an alias sequence.
+ *
+ * Params:
+ * iter = the entity to convert into an `AliasSeq`. It must be able to be able to be iterated over using
+ * a $(LINK2 https://dlang.org/spec/statement.html#foreach-statement, foreach-statement).
+ *
+ * Returns:
+ * An `AliasSeq` containing the values produced by iterating over `iter`.
*/
-template aliasSeqOf(alias range)
+template aliasSeqOf(alias iter)
+if (isIterable!(typeof(iter)) && !isInfinite!(typeof(iter)))
{
- import std.traits : isArray, isNarrowString;
+ import std.array : array;
- alias ArrT = typeof(range);
- static if (isArray!ArrT && !isNarrowString!ArrT)
- {
- static if (range.length == 0)
- {
- alias aliasSeqOf = AliasSeq!();
- }
- else static if (range.length == 1)
- {
- alias aliasSeqOf = AliasSeq!(range[0]);
- }
- else
- {
- alias aliasSeqOf = AliasSeq!(aliasSeqOf!(range[0 .. $/2]), aliasSeqOf!(range[$/2 .. $]));
- }
- }
- else
+ struct Impl
{
- import std.range.primitives : isInputRange;
- static if (isInputRange!ArrT)
- {
- import std.array : array;
- alias aliasSeqOf = aliasSeqOf!(array(range));
- }
- else
- {
- static assert(false, "Cannot transform range of type " ~ ArrT.stringof ~ " into a AliasSeq.");
- }
+ static foreach (size_t i, el; iter.array)
+ mixin(`auto e` ~ i.stringof ~ ` = el;`);
}
+ enum aliasSeqOf = Impl.init.tupleof;
}
///
@@ -1190,6 +1201,34 @@ template aliasSeqOf(alias range)
}
}
+@safe unittest
+{
+ struct S
+ {
+ int opApply(scope int delegate(ref int) dg)
+ {
+ foreach (int i; 3 .. 5)
+ if (auto r = dg(i))
+ return r;
+ return 0;
+ }
+ }
+ static assert(aliasSeqOf!(S.init) == AliasSeq!(3, 4));
+}
+
+@safe unittest
+{
+ struct Infinite
+ {
+ int front();
+ void popFront();
+ enum empty = false;
+ }
+ enum infinite = Infinite();
+ static assert(isInfinite!Infinite);
+ static assert(!__traits(compiles, aliasSeqOf!infinite));
+}
+
/**
* $(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially applies)
* $(D_PARAM Template) by binding its first (left) or last (right) arguments
@@ -1289,7 +1328,7 @@ private template SmartAlias(T...)
}
else
{
- alias SmartAlias = AliasSeq!T;
+ alias SmartAlias = T;
}
}
@@ -1313,36 +1352,39 @@ private template SmartAlias(T...)
}
/**
- * Creates an `AliasSeq` which repeats a type or an `AliasSeq` exactly `n` times.
+ * Creates an `AliasSeq` which repeats `items` exactly `n` times.
*/
-template Repeat(size_t n, TList...)
-if (n > 0)
+template Repeat(size_t n, items...)
{
- static if (n == 1)
+ static if (n == 0)
{
- alias Repeat = AliasSeq!TList;
- }
- else static if (n == 2)
- {
- alias Repeat = AliasSeq!(TList, TList);
+ alias Repeat = AliasSeq!();
}
else
{
- alias R = Repeat!((n - 1) / 2, TList);
- static if ((n - 1) % 2 == 0)
+ alias Repeat = items;
+ enum log2n =
{
- alias Repeat = AliasSeq!(TList, R, R);
- }
- else
+ uint result = 0;
+ auto x = n;
+ while (x >>= 1)
+ ++result;
+ return result;
+ }();
+ static foreach (i; 0 .. log2n)
{
- alias Repeat = AliasSeq!(TList, TList, R, R);
+ Repeat = AliasSeq!(Repeat, Repeat);
}
+ Repeat = AliasSeq!(Repeat, Repeat!(n - (1u << log2n), items));
}
}
///
@safe unittest
{
+ alias ImInt0 = Repeat!(0, int);
+ static assert(is(ImInt0 == AliasSeq!()));
+
alias ImInt1 = Repeat!(1, immutable(int));
static assert(is(ImInt1 == AliasSeq!(immutable(int))));
@@ -1356,6 +1398,11 @@ if (n > 0)
alias Composite = AliasSeq!(uint, int);
alias Composite2 = Repeat!(2, Composite);
static assert(is(Composite2 == AliasSeq!(uint, int, uint, int)));
+
+ alias ImInt10 = Repeat!(10, int);
+ static assert(is(ImInt10 == AliasSeq!(int, int, int, int, int, int, int, int, int, int)));
+
+ alias Big = Repeat!(1_000_000, int);
}
@@ -1374,11 +1421,11 @@ if (n > 0)
}
/**
- * Sorts a $(LREF AliasSeq) using $(D cmp).
+ * Sorts an $(LREF AliasSeq) using `cmp`.
*
* Parameters:
- * cmp = A template that returns a $(D bool) (if its first argument is less than the second one)
- * or an $(D int) (-1 means less than, 0 means equal, 1 means greater than)
+ * cmp = A template that returns a `bool` (if its first argument is less than the second one)
+ * or an `int` (-1 means less than, 0 means equal, 1 means greater than)
*
* Seq = The $(LREF AliasSeq) to sort
*
@@ -1455,30 +1502,24 @@ if (Seq.length == 2)
}
/**
- * Checks if an $(LREF AliasSeq) is sorted according to $(D cmp).
+ * Checks if an $(LREF AliasSeq) is sorted according to `cmp`.
*
* Parameters:
- * cmp = A template that returns a $(D bool) (if its first argument is less than the second one)
- * or an $(D int) (-1 means less than, 0 means equal, 1 means greater than)
+ * cmp = A template that returns a `bool` (if its first argument is less than the second one)
+ * or an `int` (-1 means less than, 0 means equal, 1 means greater than)
*
* Seq = The $(LREF AliasSeq) to check
*
* Returns: `true` if `Seq` is sorted; otherwise `false`
*/
-template staticIsSorted(alias cmp, Seq...)
-{
- static if (Seq.length <= 1)
- enum staticIsSorted = true;
- else static if (Seq.length == 2)
- enum staticIsSorted = isLessEq!(cmp, Seq[0], Seq[1]);
- else
+enum staticIsSorted(alias cmp, items...) =
{
- enum staticIsSorted =
- isLessEq!(cmp, Seq[($ / 2) - 1], Seq[$ / 2]) &&
- staticIsSorted!(cmp, Seq[0 .. $ / 2]) &&
- staticIsSorted!(cmp, Seq[$ / 2 .. $]);
- }
-}
+ static if (items.length > 1)
+ static foreach (i, item; items[1 .. $])
+ static if (!isLessEq!(cmp, items[i], item))
+ if (__ctfe) return false;
+ return true;
+ }();
///
@safe unittest
@@ -1498,35 +1539,28 @@ template staticIsSorted(alias cmp, Seq...)
}
/**
-Selects a subset of the argument list by stepping with fixed `stepSize` over the list.
-A negative `stepSize` starts iteration with the last list element.
+Selects a subset of `Args` by stepping with fixed `stepSize` over the sequence.
+A negative `stepSize` starts iteration with the last element.
Params:
stepSize = Number of elements to increment on each iteration. Can't be `0`.
- Args = Template arguments
+ Args = Template arguments.
-Returns: A template argument list filtered by the selected stride.
+Returns: An `AliasSeq` filtered by the selected stride.
*/
template Stride(int stepSize, Args...)
if (stepSize != 0)
{
- static if (Args.length == 0)
+ alias Stride = AliasSeq!();
+ static if (stepSize > 0)
{
- alias Stride = AliasSeq!();
- }
- else static if (stepSize > 0)
- {
- static if (stepSize >= Args.length)
- alias Stride = AliasSeq!(Args[0]);
- else
- alias Stride = AliasSeq!(Args[0], Stride!(stepSize, Args[stepSize .. $]));
+ static foreach (i; 0 .. (Args.length + stepSize - 1) / stepSize)
+ Stride = AliasSeq!(Stride, Args[i * stepSize]);
}
else
{
- static if (-stepSize >= Args.length)
- alias Stride = AliasSeq!(Args[$ - 1]);
- else
- alias Stride = AliasSeq!(Args[$ - 1], Stride!(stepSize, Args[0 .. $ + stepSize]));
+ static foreach (i; 0 .. (Args.length - stepSize - 1) / -stepSize)
+ Stride = AliasSeq!(Stride, Args[$ - 1 + i * stepSize]);
}
}
@@ -1551,6 +1585,42 @@ if (stepSize != 0)
static assert(!__traits(compiles, Stride!(0, int)));
}
+/**
+ * Instantiates the given template with the given parameters.
+ *
+ * Used to work around syntactic limitations of D with regard to instantiating
+ * a template from an alias sequence (e.g. `T[0]!(...)` is not valid) or a
+ * template returning another template (e.g. `Foo!(Bar)!(Baz)` is not allowed).
+ *
+ * Params:
+ * Template = The template to instantiate.
+ * Params = The parameters with which to instantiate the template.
+ * Returns:
+ * The instantiated template.
+ */
+alias Instantiate(alias Template, Params...) = Template!Params;
+
+///
+@safe unittest
+{
+ // ApplyRight combined with Instantiate can be used to apply various
+ // templates to the same parameters.
+ import std.string : leftJustify, center, rightJustify;
+ alias functions = staticMap!(ApplyRight!(Instantiate, string),
+ leftJustify, center, rightJustify);
+ string result = "";
+ static foreach (f; functions)
+ {
+ {
+ auto x = &f; // not a template, but a function instantiation
+ result ~= x("hello", 7);
+ result ~= ";";
+ }
+ }
+
+ assert(result == "hello ; hello ; hello;");
+}
+
// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : //
private:
@@ -1559,40 +1629,42 @@ private:
* not. Both a and b can be types, literals, or symbols.
*
* How: When:
- * is(a == b) - both are types
- * a == b - both are literals (true literals, enums)
- * __traits(isSame, a, b) - other cases (variables, functions,
- * templates, etc.)
+ * a == b - at least one rvalue (literals, enums, function calls)
+ * __traits(isSame, a, b) - other cases (types, variables, functions, templates, etc.)
*/
-private template isSame(ab...)
-if (ab.length == 2)
+private template isSame(alias a, alias b)
{
- static if (__traits(compiles, expectType!(ab[0]),
- expectType!(ab[1])))
+ static if (!is(typeof(&a && &b)) // at least one is an rvalue
+ && __traits(compiles, { enum isSame = a == b; })) // c-t comparable
{
- enum isSame = is(ab[0] == ab[1]);
- }
- else static if (!__traits(compiles, expectType!(ab[0])) &&
- !__traits(compiles, expectType!(ab[1])) &&
- __traits(compiles, expectBool!(ab[0] == ab[1])))
- {
- static if (!__traits(compiles, &ab[0]) ||
- !__traits(compiles, &ab[1]))
- enum isSame = (ab[0] == ab[1]);
- else
- enum isSame = __traits(isSame, ab[0], ab[1]);
+ enum isSame = a == b;
}
else
{
- enum isSame = __traits(isSame, ab[0], ab[1]);
+ enum isSame = __traits(isSame, a, b);
}
}
-private template expectType(T) {}
-private template expectBool(bool b) {}
+// TODO: remove after https://github.com/dlang/dmd/pull/11320 and https://issues.dlang.org/show_bug.cgi?id=21889 are fixed
+private template isSame(A, B)
+{
+ enum isSame = is(A == B);
+}
@safe unittest
{
+ static assert(!isSame!(Object, const Object));
+ static assert(!isSame!(Object, immutable Object));
+
+ static struct S {}
+ static assert(!isSame!(S, const S));
+ static assert( isSame!(S(), S()));
+
+ static class C {}
+ static assert(!isSame!(C, const C));
+
static assert( isSame!(int, int));
+ static assert(!isSame!(int, const int));
+ static assert(!isSame!(const int, immutable int));
static assert(!isSame!(int, short));
enum a = 1, b = 1, c = 2, s = "a", t = "a";
@@ -1617,6 +1689,8 @@ private template expectBool(bool b) {}
static assert( isSame!(X, X));
static assert(!isSame!(X, Y));
static assert(!isSame!(Y, Z));
+ static assert( isSame!(X, 1));
+ static assert( isSame!(1, X));
int foo();
int bar();
@@ -1637,28 +1711,12 @@ private template expectBool(bool b) {}
}
/*
- * [internal] Confines a tuple within a template.
+ * [internal] Wraps a sequence in a template. Used only in unittests.
*/
private template Pack(T...)
{
- alias tuple = T;
-
- // For convenience
- template equals(U...)
- {
- static if (T.length == U.length)
- {
- static if (T.length == 0)
- enum equals = true;
- else
- enum equals = isSame!(T[0], U[0]) &&
- Pack!(T[1 .. $]).equals!(U[1 .. $]);
- }
- else
- {
- enum equals = false;
- }
- }
+ alias Expand = T;
+ enum equals(U...) = isSame!(Pack!T, Pack!U);
}
@safe unittest
@@ -1666,14 +1724,3 @@ private template Pack(T...)
static assert( Pack!(1, int, "abc").equals!(1, int, "abc"));
static assert(!Pack!(1, int, "abc").equals!(1, int, "cba"));
}
-
-/*
- * Instantiates the given template with the given list of parameters.
- *
- * Used to work around syntactic limitations of D with regard to instantiating
- * a template from an alias sequence (e.g. T[0]!(...) is not valid) or a template
- * returning another template (e.g. Foo!(Bar)!(Baz) is not allowed).
- */
-// TODO: Consider publicly exposing this, maybe even if only for better
-// understandability of error messages.
-alias Instantiate(alias Template, Params...) = Template!Params;
diff --git a/libphobos/src/std/mmfile.d b/libphobos/src/std/mmfile.d
index 453e2ec74e2..e4000d4db27 100644
--- a/libphobos/src/std/mmfile.d
+++ b/libphobos/src/std/mmfile.d
@@ -2,15 +2,15 @@
/**
* Read and write memory mapped files.
- * Copyright: Copyright Digital Mars 2004 - 2009.
+ * Copyright: Copyright The D Language Foundation 2004 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright),
* Matthew Wilson
- * Source: $(PHOBOSSRC std/_mmfile.d)
+ * Source: $(PHOBOSSRC std/mmfile.d)
*
* $(SCRIPT inhibitQuickIndex = 1;)
*/
-/* Copyright Digital Mars 2004 - 2009.
+/* Copyright The D Language Foundation 2004 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -31,7 +31,8 @@ import std.internal.cstring;
version (Windows)
{
- import core.sys.windows.windows;
+ import core.sys.windows.winbase;
+ import core.sys.windows.winnt;
import std.utf;
import std.windows.syserror;
}
@@ -67,7 +68,8 @@ class MmFile
* Open memory mapped file filename for reading.
* File is closed when the object instance is deleted.
* Throws:
- * std.file.FileException
+ * - On POSIX, $(REF ErrnoException, std, exception).
+ * - On Windows, $(REF WindowsException, std, windows, syserror).
*/
this(string filename)
{
@@ -88,7 +90,7 @@ class MmFile
int oflag;
int fmode;
- switch (mode)
+ final switch (mode)
{
case Mode.read:
flags = MAP_SHARED;
@@ -118,9 +120,6 @@ class MmFile
oflag = O_RDWR;
fmode = 0;
break;
-
- default:
- assert(0);
}
fd = fildes;
@@ -166,7 +165,8 @@ class MmFile
* with 0 meaning map the entire file. The window size must be a
* multiple of the memory allocation page size.
* Throws:
- * std.file.FileException
+ * - On POSIX, $(REF ErrnoException, std, exception).
+ * - On Windows, $(REF WindowsException, std, windows, syserror).
*/
this(string filename, Mode mode, ulong size, void* address,
size_t window = 0)
@@ -184,7 +184,7 @@ class MmFile
uint dwCreationDisposition;
uint flProtect;
- switch (mode)
+ final switch (mode)
{
case Mode.read:
dwDesiredAccess2 = GENERIC_READ;
@@ -218,9 +218,6 @@ class MmFile
flProtect = PAGE_WRITECOPY;
dwDesiredAccess = FILE_MAP_COPY;
break;
-
- default:
- assert(0);
}
if (filename != null)
@@ -281,7 +278,7 @@ class MmFile
int oflag;
int fmode;
- switch (mode)
+ final switch (mode)
{
case Mode.read:
flags = MAP_SHARED;
@@ -311,9 +308,6 @@ class MmFile
oflag = O_RDWR;
fmode = 0;
break;
-
- default:
- assert(0);
}
if (filename.length)
@@ -343,7 +337,6 @@ class MmFile
else
{
fd = -1;
- version (CRuntime_Glibc) import core.sys.linux.sys.mman : MAP_ANON;
flags |= MAP_ANON;
}
this.size = size;
@@ -440,6 +433,11 @@ class MmFile
}
/**
+ * Forwards `length`.
+ */
+ alias opDollar = length;
+
+ /**
* Read-only property returning the file mode.
*/
Mode mode()
@@ -648,9 +646,10 @@ private:
win = sysinfo.dwAllocationGranularity;
+/
}
- else version (linux)
+ else version (Posix)
{
- // getpagesize() is not defined in the unix D headers so use the guess
+ import core.sys.posix.unistd;
+ win = cast(size_t) sysconf(_SC_PAGESIZE);
}
string test_file = std.file.deleteme ~ "-testing.txt";
MmFile mf = new MmFile(test_file,MmFile.Mode.readWriteNew,
@@ -671,7 +670,6 @@ private:
assert( data2[$-1] == 'b' );
destroy(mf);
- GC.free(&mf);
std.file.remove(test_file);
// Create anonymous mapping
@@ -679,7 +677,7 @@ private:
}
version (linux)
-@system unittest // Issue 14868
+@system unittest // https://issues.dlang.org/show_bug.cgi?id=14868
{
import std.file : deleteme;
import std.typecons : scoped;
@@ -702,7 +700,9 @@ version (linux)
assert(.close(fd) == -1);
}
-@system unittest // Issue 14994, 14995
+// https://issues.dlang.org/show_bug.cgi?id=14994
+// https://issues.dlang.org/show_bug.cgi?id=14995
+@system unittest
{
import std.file : deleteme;
import std.typecons : scoped;
@@ -719,3 +719,101 @@ version (linux)
scope(exit) std.file.remove(fn);
verifyThrown(scoped!MmFile(fn, MmFile.Mode.readWrite, 0, null));
}
+
+@system unittest
+{
+ MmFile shar = new MmFile(null, MmFile.Mode.readWrite, 10, null, 0);
+ void[] output = shar[0 .. $];
+}
+
+@system unittest
+{
+ import std.file : deleteme;
+ auto name = std.file.deleteme ~ "-test.tmp";
+ scope(exit) std.file.remove(name);
+
+ std.file.write(name, "abcd");
+ {
+ scope MmFile mmf = new MmFile(name);
+ string p;
+
+ assert(mmf[0] == 'a');
+ p = cast(string) mmf[];
+ assert(p[1] == 'b');
+ p = cast(string) mmf[0 .. 4];
+ assert(p[2] == 'c');
+ }
+ {
+ scope MmFile mmf = new MmFile(name, MmFile.Mode.read, 0, null);
+ string p;
+
+ assert(mmf[0] == 'a');
+ p = cast(string) mmf[];
+ assert(mmf.length == 4);
+ assert(p[1] == 'b');
+ p = cast(string) mmf[0 .. 4];
+ assert(p[2] == 'c');
+ }
+ std.file.remove(name);
+ {
+ scope MmFile mmf = new MmFile(name, MmFile.Mode.readWriteNew, 4, null);
+ char[] p = cast(char[]) mmf[];
+ p[] = "1234";
+ mmf[3] = '5';
+ assert(mmf[2] == '3');
+ assert(mmf[3] == '5');
+ }
+ {
+ string p = cast(string) std.file.read(name);
+ assert(p[] == "1235");
+ }
+ {
+ scope MmFile mmf = new MmFile(name, MmFile.Mode.readWriteNew, 4, null);
+ char[] p = cast(char[]) mmf[];
+ p[] = "5678";
+ mmf[3] = '5';
+ assert(mmf[2] == '7');
+ assert(mmf[3] == '5');
+ assert(cast(string) mmf[] == "5675");
+ }
+ {
+ string p = cast(string) std.file.read(name);
+ assert(p[] == "5675");
+ }
+ {
+ scope MmFile mmf = new MmFile(name, MmFile.Mode.readWrite, 4, null);
+ char[] p = cast(char[]) mmf[];
+ assert(cast(char[]) mmf[] == "5675");
+ p[] = "9102";
+ mmf[2] = '5';
+ assert(cast(string) mmf[] == "9152");
+ }
+ {
+ string p = cast(string) std.file.read(name);
+ assert(p[] == "9152");
+ }
+ std.file.remove(name);
+ {
+ scope MmFile mmf = new MmFile(name, MmFile.Mode.readWrite, 4, null);
+ char[] p = cast(char[]) mmf[];
+ p[] = "abcd";
+ mmf[2] = '5';
+ assert(cast(string) mmf[] == "ab5d");
+ }
+ {
+ string p = cast(string) std.file.read(name);
+ assert(p[] == "ab5d");
+ }
+ {
+ scope MmFile mmf = new MmFile(name, MmFile.Mode.readCopyOnWrite, 4, null);
+ char[] p = cast(char[]) mmf[];
+ assert(cast(string) mmf[] == "ab5d");
+ p[] = "9102";
+ mmf[2] = '5';
+ assert(cast(string) mmf[] == "9152");
+ }
+ {
+ string p = cast(string) std.file.read(name);
+ assert(p[] == "ab5d");
+ }
+}
diff --git a/libphobos/src/std/net/curl.d b/libphobos/src/std/net/curl.d
index 445f996ea08..7ea2cebb30f 100644
--- a/libphobos/src/std/net/curl.d
+++ b/libphobos/src/std/net/curl.d
@@ -1,7 +1,7 @@
// Written in the D programming language.
/**
-Networking client functionality as provided by $(HTTP _curl.haxx.se/libcurl,
+Networking client functionality as provided by $(HTTP curl.haxx.se/libcurl,
libcurl). The libcurl library must be installed on the system in order to use
this module.
@@ -28,19 +28,21 @@ to your $(B dub.json) file if you are using $(LINK2 http://code.dlang.org, DUB).
Windows x86 note:
A DMD compatible libcurl static library can be downloaded from the dlang.org
-$(LINK2 http://dlang.org/download.html, download page).
+$(LINK2 http://downloads.dlang.org/other/index.html, download archive page).
+
+This module is not available for iOS, tvOS or watchOS.
Compared to using libcurl directly this module allows simpler client code for
common uses, requires no unsafe operations, and integrates better with the rest
-of the language. Futhermore it provides <a href="std_range.html">$(D range)</a>
+of the language. Futhermore it provides $(MREF_ALTTEXT range, std,range)
access to protocols supported by libcurl both synchronously and asynchronously.
A high level and a low level API are available. The high level API is built
entirely on top of the low level one.
The high level API is for commonly used functionality such as HTTP/FTP get. The
-$(LREF byLineAsync) and $(LREF byChunkAsync) provides asynchronous <a
-href="std_range.html">$(D ranges)</a> that performs the request in another
+$(LREF byLineAsync) and $(LREF byChunkAsync) provides asynchronous
+$(MREF_ALTTEXT range, std,range) that performs the request in another
thread while handling a line/chunk in the current thread.
The low level API allows for streaming and other advanced features.
@@ -86,9 +88,9 @@ dlang.org web page asynchronously.)
)
$(LEADINGROW Low level
)
-$(TR $(TDNW $(LREF HTTP)) $(TD $(D HTTP) struct for advanced usage))
-$(TR $(TDNW $(LREF FTP)) $(TD $(D FTP) struct for advanced usage))
-$(TR $(TDNW $(LREF SMTP)) $(TD $(D SMTP) struct for advanced usage))
+$(TR $(TDNW $(LREF HTTP)) $(TD `HTTP` struct for advanced usage))
+$(TR $(TDNW $(LREF FTP)) $(TD `FTP` struct for advanced usage))
+$(TR $(TDNW $(LREF SMTP)) $(TD `SMTP` struct for advanced usage))
)
@@ -139,13 +141,13 @@ the onReceive callback. See $(LREF onReceiveHeader)/$(LREF onReceive) for more
information. Finally the HTTP request is effected by calling perform(), which is
synchronous.
-Source: $(PHOBOSSRC std/net/_curl.d)
+Source: $(PHOBOSSRC std/net/curl.d)
Copyright: Copyright Jonas Drewsen 2011-2012
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Jonas Drewsen. Some of the SMTP code contributed by Jimmy Cao.
-Credits: The functionally is based on $(HTTP _curl.haxx.se/libcurl, libcurl).
+Credits: The functionally is based on $(HTTP curl.haxx.se/libcurl, libcurl).
LibCurl is licensed under an MIT/X derivative license.
*/
/*
@@ -156,36 +158,39 @@ Distributed under the Boost Software License, Version 1.0.
*/
module std.net.curl;
-import core.thread;
-import etc.c.curl;
-import std.concurrency;
-import std.encoding;
-import std.exception;
-import std.meta;
+public import etc.c.curl : CurlOption;
+import core.time : dur;
+import etc.c.curl : CURLcode;
import std.range.primitives;
-import std.socket : InternetAddress;
-import std.traits;
-import std.typecons;
+import std.encoding : EncodingScheme;
+import std.traits : isSomeChar;
+import std.typecons : Flag, Yes, No, Tuple;
-import std.internal.cstring;
+version (iOS)
+ version = iOSDerived;
+else version (TVOS)
+ version = iOSDerived;
+else version (WatchOS)
+ version = iOSDerived;
-public import etc.c.curl : CurlOption;
+version (iOSDerived) {}
+else:
-version (unittest)
+version (StdUnittest)
{
- // Run unit test with the PHOBOS_TEST_ALLOW_NET=1 set in order to
- // allow net traffic
- import std.range;
- import std.stdio;
-
- import std.socket : Address, INADDR_LOOPBACK, Socket, SocketShutdown, TcpSocket;
+ import std.socket : Socket, SocketShutdown;
private struct TestServer
{
+ import std.concurrency : Tid;
+
+ import std.socket : Socket, TcpSocket;
+
string addr() { return _addr; }
void handle(void function(Socket s) dg)
{
+ import std.concurrency : send;
tid.send(dg);
}
@@ -196,6 +201,9 @@ version (unittest)
static void loop(shared TcpSocket listener)
{
+ import std.concurrency : OwnerTerminated, receiveOnly;
+ import std.stdio : stderr;
+
try while (true)
{
void function(Socket) handler = void;
@@ -207,13 +215,17 @@ version (unittest)
}
catch (Throwable e)
{
- stderr.writeln(e); // Bugzilla 7018
+ // https://issues.dlang.org/show_bug.cgi?id=7018
+ stderr.writeln(e);
}
}
}
private TestServer startServer()
{
+ import std.concurrency : spawn;
+ import std.socket : INADDR_LOOPBACK, InternetAddress, TcpSocket;
+
tlsInit = true;
auto sock = new TcpSocket;
sock.bind(new InternetAddress(INADDR_LOOPBACK, InternetAddress.PORT_ANY));
@@ -223,11 +235,14 @@ version (unittest)
return TestServer(addr, tid, sock);
}
+ /** Test server */
__gshared TestServer server;
+ /** Thread-local storage init */
bool tlsInit;
private ref TestServer testServer()
{
+ import std.concurrency : initOnce;
return initOnce!server(startServer());
}
@@ -365,7 +380,7 @@ CALLBACK_PARAMS = $(TABLE ,
* ---
* import std.net.curl;
* // Two requests below will do the same.
- * string content;
+ * char[] content;
*
* // Explicit connection provided
* content = get!HTTP("dlang.org");
@@ -407,7 +422,7 @@ private template isCurlConn(Conn)
* Example:
* ----
* import std.net.curl;
- * download("d-lang.appspot.com/testUrl2", "/tmp/downloaded-http-file");
+ * download("https://httpbin.org/get", "/tmp/downloaded-http-file");
* ----
*/
void download(Conn = AutoProtocol)(const(char)[] url, string saveToPath, Conn conn = Conn())
@@ -464,7 +479,7 @@ if (isCurlConn!Conn)
* ----
* import std.net.curl;
* upload("/tmp/downloaded-ftp-file", "ftp.digitalmars.com/sieve.ds");
- * upload("/tmp/downloaded-http-file", "d-lang.appspot.com/testUrl2");
+ * upload("/tmp/downloaded-http-file", "https://httpbin.org/post");
* ----
*/
void upload(Conn = AutoProtocol)(string loadFromPath, const(char)[] url, Conn conn = Conn())
@@ -531,16 +546,16 @@ if (isCurlConn!Conn)
* conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will
* guess connection type and create a new instance for this call only.
*
- * The template parameter $(D T) specifies the type to return. Possible values
- * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]). If asking
- * for $(D char), content will be converted from the connection character set
+ * The template parameter `T` specifies the type to return. Possible values
+ * are `char` and `ubyte` to return `char[]` or `ubyte[]`. If asking
+ * for `char`, content will be converted from the connection character set
* (specified in HTTP response headers or FTP connection properties, both ISO-8859-1
* by default) to UTF-8.
*
* Example:
* ----
* import std.net.curl;
- * auto content = get("d-lang.appspot.com/testUrl2");
+ * auto content = get("https://httpbin.org/get");
* ----
*
* Returns:
@@ -548,7 +563,7 @@ if (isCurlConn!Conn)
*
* Throws:
*
- * $(D CurlException) on error.
+ * `CurlException` on error.
*
* See_Also: $(LREF HTTP.Method)
*/
@@ -595,15 +610,15 @@ if ( isCurlConn!Conn && (is(T == char) || is(T == ubyte)) )
* Params:
* url = resource to post to
* postDict = data to send as the body of the request. An associative array
- * of $(D string) is accepted and will be encoded using
+ * of `string` is accepted and will be encoded using
* www-form-urlencoding
* postData = data to send as the body of the request. An array
* of an arbitrary type is accepted and will be cast to ubyte[]
* before sending it.
* conn = HTTP connection to use
- * T = The template parameter $(D T) specifies the type to return. Possible values
- * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]). If asking
- * for $(D char), content will be converted from the connection character set
+ * T = The template parameter `T` specifies the type to return. Possible values
+ * are `char` and `ubyte` to return `char[]` or `ubyte[]`. If asking
+ * for `char`, content will be converted from the connection character set
* (specified in HTTP response headers or FTP connection properties, both ISO-8859-1
* by default) to UTF-8.
*
@@ -611,8 +626,8 @@ if ( isCurlConn!Conn && (is(T == char) || is(T == ubyte)) )
* ----
* import std.net.curl;
*
- * auto content1 = post("d-lang.appspot.com/testUrl2", ["name1" : "value1", "name2" : "value2"]);
- * auto content2 = post("d-lang.appspot.com/testUrl2", [1,2,3,4]);
+ * auto content1 = post("https://httpbin.org/post", ["name1" : "value1", "name2" : "value2"]);
+ * auto content2 = post("https://httpbin.org/post", [1,2,3,4]);
* ----
*
* Returns:
@@ -668,19 +683,27 @@ if (is(T == char) || is(T == ubyte))
{
import std.uri : urlEncode;
- return post(url, urlEncode(postDict), conn);
+ return post!T(url, urlEncode(postDict), conn);
}
@system unittest
{
+ import std.algorithm.searching : canFind;
+ import std.meta : AliasSeq;
+
+ static immutable expected = ["name1=value1&name2=value2", "name2=value2&name1=value1"];
+
foreach (host; [testServer.addr, "http://" ~ testServer.addr])
{
- testServer.handle((s) {
- auto req = s.recvReq!char;
- s.send(httpOK(req.bdy));
- });
- auto res = post(host ~ "/path", ["name1" : "value1", "name2" : "value2"]);
- assert(res == "name1=value1&name2=value2" || res == "name2=value2&name1=value1");
+ foreach (T; AliasSeq!(char, ubyte))
+ {
+ testServer.handle((s) {
+ auto req = s.recvReq!char;
+ s.send(httpOK(req.bdy));
+ });
+ auto res = post!T(host ~ "/path", ["name1" : "value1", "name2" : "value2"]);
+ assert(canFind(expected, res));
+ }
}
}
@@ -694,16 +717,16 @@ if (is(T == char) || is(T == ubyte))
* conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will
* guess connection type and create a new instance for this call only.
*
- * The template parameter $(D T) specifies the type to return. Possible values
- * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]). If asking
- * for $(D char), content will be converted from the connection character set
+ * The template parameter `T` specifies the type to return. Possible values
+ * are `char` and `ubyte` to return `char[]` or `ubyte[]`. If asking
+ * for `char`, content will be converted from the connection character set
* (specified in HTTP response headers or FTP connection properties, both ISO-8859-1
* by default) to UTF-8.
*
* Example:
* ----
* import std.net.curl;
- * auto content = put("d-lang.appspot.com/testUrl2",
+ * auto content = put("https://httpbin.org/put",
* "Putting this data");
* ----
*
@@ -762,7 +785,7 @@ if ( isCurlConn!Conn && (is(T == char) || is(T == ubyte)) )
* Example:
* ----
* import std.net.curl;
- * del("d-lang.appspot.com/testUrl2");
+ * del("https://httpbin.org/delete");
* ----
*
* See_Also: $(LREF HTTP.Method)
@@ -779,6 +802,7 @@ if (isCurlConn!Conn)
{
import std.algorithm.searching : findSplitAfter;
import std.conv : text;
+ import std.exception : enforce;
auto trimmed = url.findSplitAfter("ftp://")[1];
auto t = trimmed.findSplitAfter("/");
@@ -824,14 +848,14 @@ if (isCurlConn!Conn)
* conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will
* guess connection type and create a new instance for this call only.
*
- * The template parameter $(D T) specifies the type to return. Possible values
- * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]).
+ * The template parameter `T` specifies the type to return. Possible values
+ * are `char` and `ubyte` to return `char[]` or `ubyte[]`.
*
* Example:
* ----
* import std.net.curl;
* auto http = HTTP();
- * options("d-lang.appspot.com/testUrl2", http);
+ * options("https://httpbin.org/headers", http);
* writeln("Allow set to " ~ http.responseHeaders["Allow"]);
* ----
*
@@ -868,13 +892,13 @@ if (is(T == char) || is(T == ubyte))
* conn = connection to use e.g. FTP or HTTP. The default AutoProtocol will
* guess connection type and create a new instance for this call only.
*
- * The template parameter $(D T) specifies the type to return. Possible values
- * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]).
+ * The template parameter `T` specifies the type to return. Possible values
+ * are `char` and `ubyte` to return `char[]` or `ubyte[]`.
*
* Example:
* ----
* import std.net.curl;
- * trace("d-lang.appspot.com/testUrl1");
+ * trace("https://httpbin.org/headers");
* ----
*
* Returns:
@@ -909,13 +933,13 @@ if (is(T == char) || is(T == ubyte))
* url = resource make a connect to
* conn = HTTP connection to use
*
- * The template parameter $(D T) specifies the type to return. Possible values
- * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]).
+ * The template parameter `T` specifies the type to return. Possible values
+ * are `char` and `ubyte` to return `char[]` or `ubyte[]`.
*
* Example:
* ----
* import std.net.curl;
- * connect("d-lang.appspot.com/testUrl1");
+ * connect("https://httpbin.org/headers");
* ----
*
* Returns:
@@ -953,14 +977,14 @@ if (is(T == char) || is(T == ubyte))
* before sending it.
* conn = HTTP connection to use
*
- * The template parameter $(D T) specifies the type to return. Possible values
- * are $(D char) and $(D ubyte) to return $(D char[]) or $(D ubyte[]).
+ * The template parameter `T` specifies the type to return. Possible values
+ * are `char` and `ubyte` to return `char[]` or `ubyte[]`.
*
* Example:
* ----
* auto http = HTTP();
* http.addRequestHeader("Content-Type", "application/json");
- * auto content = patch("d-lang.appspot.com/testUrl2", `{"title": "Patched Title"}`, http);
+ * auto content = patch("https://httpbin.org/patch", `{"title": "Patched Title"}`, http);
* ----
*
* Returns:
@@ -1001,6 +1025,8 @@ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP clien
{
import std.algorithm.comparison : min;
import std.format : format;
+ import std.exception : enforce;
+ import etc.c.curl : CurlSeek, CurlSeekPos;
immutable doSend = sendData !is null &&
(client.method == HTTP.Method.post ||
@@ -1077,6 +1103,7 @@ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP clien
@system unittest
{
import std.algorithm.searching : canFind;
+ import std.exception : collectException;
testServer.handle((s) {
auto req = s.recvReq;
@@ -1088,7 +1115,8 @@ private auto _basicHTTP(T)(const(char)[] url, const(void)[] sendData, HTTP clien
assert(e.status == 404);
}
-// Bugzilla 14760 - content length must be reset after post
+// Content length must be reset after post
+// https://issues.dlang.org/show_bug.cgi?id=14760
@system unittest
{
import std.algorithm.searching : canFind;
@@ -1197,6 +1225,7 @@ private auto _decodeContent(T)(ubyte[] content, string encoding)
}
else
{
+ import std.exception : enforce;
import std.format : format;
// Optimally just return the utf8 encoded content
@@ -1252,7 +1281,7 @@ struct ByLineBuffer(Char)
/** HTTP/FTP fetch content as a range of lines.
*
* A range of lines is returned when the request is complete. If the method or
- * other request properties is to be customized then set the $(D conn) parameter
+ * other request properties is to be customized then set the `conn` parameter
* with a HTTP/FTP instance that has these properties set.
*
* Example:
@@ -1264,7 +1293,7 @@ struct ByLineBuffer(Char)
*
* Params:
* url = The url to receive content from
- * keepTerminator = $(D Yes.keepTerminator) signals that the line terminator should be
+ * keepTerminator = `Yes.keepTerminator` signals that the line terminator should be
* returned as part of the lines in the range.
* terminator = The character that terminates a line
* conn = The connection to use e.g. HTTP or FTP.
@@ -1302,6 +1331,7 @@ if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator)
@property @safe Char[] front()
{
+ import std.exception : enforce;
enforce!CurlException(currentValid, "Cannot call front() on empty range");
return current;
}
@@ -1309,6 +1339,7 @@ if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator)
void popFront()
{
import std.algorithm.searching : findSplitAfter, findSplit;
+ import std.exception : enforce;
enforce!CurlException(currentValid, "Cannot call popFront() on empty range");
if (lines.empty)
@@ -1361,7 +1392,7 @@ if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator)
/** HTTP/FTP fetch content as a range of chunks.
*
* A range of chunks is returned when the request is complete. If the method or
- * other request properties is to be customized then set the $(D conn) parameter
+ * other request properties is to be customized then set the `conn` parameter
* with a HTTP/FTP instance that has these properties set.
*
* Example:
@@ -1458,6 +1489,8 @@ private T[] _getForRange(T,Conn)(const(char)[] url, Conn conn)
*/
private mixin template WorkerThreadProtocol(Unit, alias units)
{
+ import core.time : Duration;
+
@property bool empty()
{
tryEnsureUnits();
@@ -1476,7 +1509,9 @@ private mixin template WorkerThreadProtocol(Unit, alias units)
void popFront()
{
+ import std.concurrency : send;
import std.format : format;
+
tryEnsureUnits();
assert(state == State.gotUnits,
format("Expected %s but got $s",
@@ -1492,7 +1527,9 @@ private mixin template WorkerThreadProtocol(Unit, alias units)
*/
bool wait(Duration d)
{
+ import core.time : dur;
import std.datetime.stopwatch : StopWatch;
+ import std.concurrency : receiveTimeout;
if (state == State.gotUnits)
return true;
@@ -1543,6 +1580,7 @@ private mixin template WorkerThreadProtocol(Unit, alias units)
void tryEnsureUnits()
{
+ import std.concurrency : receive;
while (true)
{
final switch (state)
@@ -1574,41 +1612,14 @@ private mixin template WorkerThreadProtocol(Unit, alias units)
}
}
-// @@@@BUG 15831@@@@
-// this should be inside byLineAsync
-// Range that reads one line at a time asynchronously.
-private static struct AsyncLineInputRange(Char)
-{
- private Char[] line;
- mixin WorkerThreadProtocol!(Char, line);
-
- private Tid workerTid;
- private State running;
-
- private this(Tid tid, size_t transmitBuffers, size_t bufferSize)
- {
- workerTid = tid;
- state = State.needUnits;
-
- // Send buffers to other thread for it to use. Since no mechanism is in
- // place for moving ownership a cast to shared is done here and casted
- // back to non-shared in the receiving end.
- foreach (i ; 0 .. transmitBuffers)
- {
- auto arr = new Char[](bufferSize);
- workerTid.send(cast(immutable(Char[]))arr);
- }
- }
-}
-
/** HTTP/FTP fetch content as a range of lines asynchronously.
*
* A range of lines is returned immediately and the request that fetches the
* lines is performed in another thread. If the method or other request
- * properties is to be customized then set the $(D conn) parameter with a
+ * properties is to be customized then set the `conn` parameter with a
* HTTP/FTP instance that has these properties set.
*
- * If $(D postData) is non-_null the method will be set to $(D post) for HTTP
+ * If `postData` is non-_null the method will be set to `post` for HTTP
* requests.
*
* The background thread will buffer up to transmitBuffers number of lines
@@ -1617,8 +1628,8 @@ private static struct AsyncLineInputRange(Char)
* to receive more data from the network.
*
* If no data is available and the main thread accesses the range it will block
- * until data becomes available. An exception to this is the $(D wait(Duration)) method on
- * the $(LREF AsyncLineInputRange). This method will wait at maximum for the
+ * until data becomes available. An exception to this is the `wait(Duration)` method on
+ * the $(LREF LineInputRange). This method will wait at maximum for the
* specified duration and return true if data is available.
*
* Example:
@@ -1649,7 +1660,7 @@ private static struct AsyncLineInputRange(Char)
* Params:
* url = The url to receive content from
* postData = Data to HTTP Post
- * keepTerminator = $(D Yes.keepTerminator) signals that the line terminator should be
+ * keepTerminator = `Yes.keepTerminator` signals that the line terminator should be
* returned as part of the lines in the range.
* terminator = The character that terminates a line
* transmitBuffers = The number of lines buffered asynchronously
@@ -1677,17 +1688,18 @@ if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator)
}
else
{
+ import std.concurrency : OnCrowding, send, setMaxMailboxSize, spawn, thisTid, Tid;
// 50 is just an arbitrary number for now
setMaxMailboxSize(thisTid, 50, OnCrowding.block);
- auto tid = spawn(&_spawnAsync!(Conn, Char, Terminator));
+ auto tid = spawn(&_async!().spawn!(Conn, Char, Terminator));
tid.send(thisTid);
tid.send(terminator);
tid.send(keepTerminator == Yes.keepTerminator);
- _asyncDuplicateConnection(url, conn, postData, tid);
+ _async!().duplicateConnection(url, conn, postData, tid);
- return AsyncLineInputRange!Char(tid, transmitBuffers,
- Conn.defaultAsyncStringBufferSize);
+ return _async!().LineInputRange!Char(tid, transmitBuffers,
+ Conn.defaultAsyncStringBufferSize);
}
}
@@ -1727,41 +1739,14 @@ auto byLineAsync(Conn = AutoProtocol, Terminator = char, Char = char)
}
}
-// @@@@BUG 15831@@@@
-// this should be inside byLineAsync
-// Range that reads one chunk at a time asynchronously.
-private static struct AsyncChunkInputRange
-{
- private ubyte[] chunk;
- mixin WorkerThreadProtocol!(ubyte, chunk);
-
- private Tid workerTid;
- private State running;
-
- private this(Tid tid, size_t transmitBuffers, size_t chunkSize)
- {
- workerTid = tid;
- state = State.needUnits;
-
- // Send buffers to other thread for it to use. Since no mechanism is in
- // place for moving ownership a cast to shared is done here and a cast
- // back to non-shared in the receiving end.
- foreach (i ; 0 .. transmitBuffers)
- {
- ubyte[] arr = new ubyte[](chunkSize);
- workerTid.send(cast(immutable(ubyte[]))arr);
- }
- }
-}
-
/** HTTP/FTP fetch content as a range of chunks asynchronously.
*
* A range of chunks is returned immediately and the request that fetches the
* chunks is performed in another thread. If the method or other request
- * properties is to be customized then set the $(D conn) parameter with a
+ * properties is to be customized then set the `conn` parameter with a
* HTTP/FTP instance that has these properties set.
*
- * If $(D postData) is non-_null the method will be set to $(D post) for HTTP
+ * If `postData` is non-_null the method will be set to `post` for HTTP
* requests.
*
* The background thread will buffer up to transmitBuffers number of chunks
@@ -1770,8 +1755,8 @@ private static struct AsyncChunkInputRange
* thread to receive more data from the network.
*
* If no data is available and the main thread access the range it will block
- * until data becomes available. An exception to this is the $(D wait(Duration))
- * method on the $(LREF AsyncChunkInputRange). This method will wait at maximum for the specified
+ * until data becomes available. An exception to this is the `wait(Duration)`
+ * method on the $(LREF ChunkInputRange). This method will wait at maximum for the specified
* duration and return true if data is available.
*
* Example:
@@ -1827,14 +1812,15 @@ if (isCurlConn!(Conn))
}
else
{
+ import std.concurrency : OnCrowding, send, setMaxMailboxSize, spawn, thisTid, Tid;
// 50 is just an arbitrary number for now
setMaxMailboxSize(thisTid, 50, OnCrowding.block);
- auto tid = spawn(&_spawnAsync!(Conn, ubyte));
+ auto tid = spawn(&_async!().spawn!(Conn, ubyte));
tid.send(thisTid);
- _asyncDuplicateConnection(url, conn, postData, tid);
+ _async!().duplicateConnection(url, conn, postData, tid);
- return AsyncChunkInputRange(tid, transmitBuffers, chunkSize);
+ return _async!().ChunkInputRange(tid, transmitBuffers, chunkSize);
}
}
@@ -1876,51 +1862,6 @@ if (isCurlConn!(Conn))
}
-/* Used by byLineAsync/byChunkAsync to duplicate an existing connection
- * that can be used exclusively in a spawned thread.
- */
-private void _asyncDuplicateConnection(Conn, PostData)
- (const(char)[] url, Conn conn, PostData postData, Tid tid)
-{
- // no move semantic available in std.concurrency ie. must use casting.
- auto connDup = conn.dup();
- connDup.url = url;
-
- static if ( is(Conn : HTTP) )
- {
- connDup.p.headersOut = null;
- connDup.method = conn.method == HTTP.Method.undefined ?
- HTTP.Method.get : conn.method;
- if (postData !is null)
- {
- if (connDup.method == HTTP.Method.put)
- {
- connDup.handle.set(CurlOption.infilesize_large,
- postData.length);
- }
- else
- {
- // post
- connDup.method = HTTP.Method.post;
- connDup.handle.set(CurlOption.postfieldsize_large,
- postData.length);
- }
- connDup.handle.set(CurlOption.copypostfields,
- cast(void*) postData.ptr);
- }
- tid.send(cast(ulong) connDup.handle.handle);
- tid.send(connDup.method);
- }
- else
- {
- enforce!CurlException(postData is null,
- "Cannot put ftp data using byLineAsync()");
- tid.send(cast(ulong) connDup.handle.handle);
- tid.send(HTTP.Method.undefined);
- }
- connDup.p.curl.handle = null; // make sure handle is not freed
-}
-
/*
Mixin template for all supported curl protocols. This is the commom
functionallity such as timeouts and network interface settings. This should
@@ -1930,8 +1871,11 @@ private void _asyncDuplicateConnection(Conn, PostData)
*/
private mixin template Protocol()
{
+ import etc.c.curl : CurlReadFunc, RawCurlProxy = CurlProxy;
+ import core.time : Duration;
+ import std.socket : InternetAddress;
- /// Value to return from $(D onSend)/$(D onReceive) delegates in order to
+ /// Value to return from `onSend`/`onReceive` delegates in order to
/// pause a request
alias requestPause = CurlReadFunc.pause;
@@ -2012,7 +1956,7 @@ private mixin template Protocol()
}
/// Type of proxy
- alias CurlProxy = etc.c.curl.CurlProxy;
+ alias CurlProxy = RawCurlProxy;
/** Proxy type
* See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type)
@@ -2155,7 +2099,7 @@ private mixin template Protocol()
http.setAuthentication("user", "pass");
http.perform();
- // Bugzilla 17540
+ // https://issues.dlang.org/show_bug.cgi?id=17540
http.setNoProxy("www.example.com");
}
@@ -2180,15 +2124,15 @@ private mixin template Protocol()
/**
* The event handler that gets called when data is needed for sending. The
- * length of the $(D void[]) specifies the maximum number of bytes that can
+ * length of the `void[]` specifies the maximum number of bytes that can
* be sent.
*
* Returns:
* The callback returns the number of elements in the buffer that have been
* filled and are ready to send.
- * The special value $(D .abortRequest) can be returned in order to abort the
+ * The special value `.abortRequest` can be returned in order to abort the
* current request.
- * The special value $(D .pauseRequest) can be returned in order to pause the
+ * The special value `.pauseRequest` can be returned in order to pause the
* current request.
*
* Example:
@@ -2259,10 +2203,11 @@ private mixin template Protocol()
* ----
* import std.net.curl, std.stdio;
* auto client = HTTP("dlang.org");
- * client.onProgress = delegate int(size_t dl, size_t dln, size_t ul, size_t ult)
+ * client.onProgress = delegate int(size_t dl, size_t dln, size_t ul, size_t uln)
* {
* writeln("Progress: downloaded ", dln, " of ", dl);
* writeln("Progress: uploaded ", uln, " of ", ul);
+ * return 0;
* };
* client.perform();
* ----
@@ -2275,8 +2220,8 @@ private mixin template Protocol()
}
/*
- Decode $(D ubyte[]) array using the provided EncodingScheme up to maxChars
- Returns: Tuple of ubytes read and the $(D Char[]) characters decoded.
+ Decode `ubyte[]` array using the provided EncodingScheme up to maxChars
+ Returns: Tuple of ubytes read and the `Char[]` characters decoded.
Not all ubytes are guaranteed to be read in case of decoding error.
*/
private Tuple!(size_t,Char[])
@@ -2284,6 +2229,7 @@ decodeString(Char = char)(const(ubyte)[] data,
EncodingScheme scheme,
size_t maxChars = size_t.max)
{
+ import std.encoding : INVALID_SEQUENCE;
Char[] res;
immutable startLen = data.length;
size_t charsDecoded = 0;
@@ -2301,7 +2247,7 @@ decodeString(Char = char)(const(ubyte)[] data,
}
/*
- Decode $(D ubyte[]) array using the provided $(D EncodingScheme) until a the
+ Decode `ubyte[]` array using the provided `EncodingScheme` until a the
line terminator specified is found. The basesrc parameter is effectively
prepended to src as the first thing.
@@ -2324,6 +2270,8 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc,
Terminator terminator)
{
import std.algorithm.searching : endsWith;
+ import std.encoding : INVALID_SEQUENCE;
+ import std.exception : enforce;
// if there is anything in the basesrc then try to decode that
// first.
@@ -2374,17 +2322,28 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc,
* HTTP client functionality.
*
* Example:
+ *
+ * Get with custom data receivers:
+ *
* ---
* import std.net.curl, std.stdio;
*
- * // Get with custom data receivers
- * auto http = HTTP("dlang.org");
+ * auto http = HTTP("https://dlang.org");
* http.onReceiveHeader =
* (in char[] key, in char[] value) { writeln(key ~ ": " ~ value); };
* http.onReceive = (ubyte[] data) { /+ drop +/ return data.length; };
* http.perform();
+ * ---
*
- * // Put with data senders
+ */
+
+/**
+ * Put with data senders:
+ *
+ * ---
+ * import std.net.curl, std.stdio;
+ *
+ * auto http = HTTP("https://dlang.org");
* auto msg = "Hello world";
* http.contentLength = msg.length;
* http.onSend = (void[] data)
@@ -2397,10 +2356,19 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc,
* return len;
* };
* http.perform();
+ * ---
*
- * // Track progress
+ */
+
+/**
+ * Tracking progress:
+ *
+ * ---
+ * import std.net.curl, std.stdio;
+ *
+ * auto http = HTTP();
* http.method = HTTP.Method.get;
- * http.url = "http://upload.wikimedia.org/wikipedia/commons/"
+ * http.url = "http://upload.wikimedia.org/wikipedia/commons/" ~
* "5/53/Wikipedia-logo-en-big.png";
* http.onReceive = (ubyte[] data) { return data.length; };
* http.onProgress = (size_t dltotal, size_t dlnow,
@@ -2412,7 +2380,7 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc,
* http.perform();
* ---
*
- * See_Also: $(_HTTP www.ietf.org/rfc/rfc2616.txt, RFC2616)
+ * See_Also: $(LINK2 http://www.ietf.org/rfc/rfc2616.txt, RFC2616)
*
*/
struct HTTP
@@ -2420,6 +2388,8 @@ struct HTTP
mixin Protocol;
import std.datetime.systime : SysTime;
+ import std.typecons : RefCounted;
+ import etc.c.curl : CurlAuth, CurlInfo, curl_slist, CURLVERSION_NOW, curl_off_t;
/// Authentication method equal to $(REF CurlAuth, etc,c,curl)
alias AuthMethod = CurlAuth;
@@ -2505,9 +2475,10 @@ struct HTTP
}
private RefCounted!Impl p;
+ import etc.c.curl : CurlTimeCond;
/// Parse status line, as received from / generated by cURL.
- private static bool parseStatusLine(in char[] header, out StatusLine status) @safe
+ private static bool parseStatusLine(const char[] header, out StatusLine status) @safe
{
import std.conv : to;
import std.regex : regex, match;
@@ -2600,7 +2571,7 @@ struct HTTP
Perform a http request.
After the HTTP client has been setup and possibly assigned callbacks the
- $(D perform()) method will start performing the request towards the
+ `perform()` method will start performing the request towards the
specified server.
Params:
@@ -2676,7 +2647,9 @@ struct HTTP
// docs mixed in.
version (StdDdoc)
{
- /// Value to return from $(D onSend)/$(D onReceive) delegates in order to
+ static import etc.c.curl;
+
+ /// Value to return from `onSend`/`onReceive` delegates in order to
/// pause a request
alias requestPause = CurlReadFunc.pause;
@@ -2801,15 +2774,15 @@ struct HTTP
/**
* The event handler that gets called when data is needed for sending. The
- * length of the $(D void[]) specifies the maximum number of bytes that can
+ * length of the `void[]` specifies the maximum number of bytes that can
* be sent.
*
* Returns:
* The callback returns the number of elements in the buffer that have been
* filled and are ready to send.
- * The special value $(D .abortRequest) can be returned in order to abort the
+ * The special value `.abortRequest` can be returned in order to abort the
* current request.
- * The special value $(D .pauseRequest) can be returned in order to pause the
+ * The special value `.pauseRequest` can be returned in order to pause the
* current request.
*
* Example:
@@ -2870,10 +2843,11 @@ struct HTTP
* ----
* import std.net.curl, std.stdio;
* auto client = HTTP("dlang.org");
- * client.onProgress = delegate int(size_t dl, size_t dln, size_t ul, size_t ult)
+ * client.onProgress = delegate int(size_t dl, size_t dln, size_t ul, size_t uln)
* {
* writeln("Progress: downloaded ", dln, " of ", dl);
* writeln("Progress: uploaded ", uln, " of ", ul);
+ * return 0;
* };
* client.perform();
* ----
@@ -2908,6 +2882,7 @@ struct HTTP
void addRequestHeader(const(char)[] name, const(char)[] value)
{
import std.format : format;
+ import std.internal.cstring : tempCString;
import std.uni : icmp;
if (icmp(name, "User-Agent") == 0)
@@ -2947,7 +2922,7 @@ struct HTTP
/** Set the value of the user agent request header field.
*
* By default a request has it's "User-Agent" field set to $(LREF
- * defaultUserAgent) even if $(D setUserAgent) was never called. Pass
+ * defaultUserAgent) even if `setUserAgent` was never called. Pass
* an empty string to suppress the "User-Agent" field altogether.
*/
void setUserAgent(const(char)[] userAgent)
@@ -2957,23 +2932,23 @@ struct HTTP
/**
* Get various timings defined in $(REF CurlInfo, etc, c, curl).
- * The value is usable only if the return value is equal to $(D etc.c.curl.CurlError.ok).
+ * The value is usable only if the return value is equal to `etc.c.curl.CurlError.ok`.
*
* Params:
* timing = one of the timings defined in $(REF CurlInfo, etc, c, curl).
* The values are:
- * $(D etc.c.curl.CurlInfo.namelookup_time),
- * $(D etc.c.curl.CurlInfo.connect_time),
- * $(D etc.c.curl.CurlInfo.pretransfer_time),
- * $(D etc.c.curl.CurlInfo.starttransfer_time),
- * $(D etc.c.curl.CurlInfo.redirect_time),
- * $(D etc.c.curl.CurlInfo.appconnect_time),
- * $(D etc.c.curl.CurlInfo.total_time).
+ * `etc.c.curl.CurlInfo.namelookup_time`,
+ * `etc.c.curl.CurlInfo.connect_time`,
+ * `etc.c.curl.CurlInfo.pretransfer_time`,
+ * `etc.c.curl.CurlInfo.starttransfer_time`,
+ * `etc.c.curl.CurlInfo.redirect_time`,
+ * `etc.c.curl.CurlInfo.appconnect_time`,
+ * `etc.c.curl.CurlInfo.total_time`.
* val = the actual value of the inquired timing.
*
* Returns:
* The return code of the operation. The value stored in val
- * should be used only if the return value is $(D etc.c.curl.CurlInfo.ok).
+ * should be used only if the return value is `etc.c.curl.CurlInfo.ok`.
*
* Example:
* ---
@@ -2986,7 +2961,7 @@ struct HTTP
* double val;
* CurlCode code;
*
- * code = http.getTiming(CurlInfo.namelookup_time, val);
+ * code = client.getTiming(CurlInfo.namelookup_time, val);
* assert(code == CurlError.ok);
* ---
*/
@@ -3060,7 +3035,7 @@ struct HTTP
Set time condition on the request.
Params:
- cond = $(D CurlTimeCond.{none,ifmodsince,ifunmodsince,lastmod})
+ cond = `CurlTimeCond.{none,ifmodsince,ifunmodsince,lastmod}`
timestamp = Timestamp for the condition
$(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25)
@@ -3166,7 +3141,7 @@ struct HTTP
* Set the event handler that receives incoming headers.
*
* The callback will receive a header field key, value as parameter. The
- * $(D const(char)[]) arrays are not valid after the delegate has returned.
+ * `const(char)[]` arrays are not valid after the delegate has returned.
*
* Example:
* ----
@@ -3187,7 +3162,7 @@ struct HTTP
Callback for each received StatusLine.
Notice that several callbacks can be done for each call to
- $(D perform()) due to redirections.
+ `perform()` due to redirections.
See_Also: $(LREF StatusLine)
*/
@@ -3310,11 +3285,11 @@ struct HTTP
@system unittest // charset/Charset/CHARSET/...
{
- import std.meta : AliasSeq;
+ import etc.c.curl;
- foreach (c; AliasSeq!("charset", "Charset", "CHARSET", "CharSet", "charSet",
- "ChArSeT", "cHaRsEt"))
- {
+ static foreach (c; ["charset", "Charset", "CHARSET", "CharSet", "charSet",
+ "ChArSeT", "cHaRsEt"])
+ {{
testServer.handle((s) {
s.send("HTTP/1.1 200 OK\r\n"~
"Content-Length: 0\r\n"~
@@ -3326,7 +3301,7 @@ struct HTTP
http.perform();
assert(http.p.charset == "foo");
- // Bugzilla 16736
+ // https://issues.dlang.org/show_bug.cgi?id=16736
double val;
CurlCode code;
@@ -3344,7 +3319,7 @@ struct HTTP
assert(code == CurlError.ok);
code = http.getTiming(CurlInfo.appconnect_time, val);
assert(code == CurlError.ok);
- }
+ }}
}
/**
@@ -3357,6 +3332,9 @@ struct FTP
mixin Protocol;
+ import std.typecons : RefCounted;
+ import etc.c.curl : CurlError, CurlInfo, curl_off_t, curl_slist;
+
private struct Impl
{
~this()
@@ -3449,7 +3427,9 @@ struct FTP
// docs mixed in.
version (StdDdoc)
{
- /// Value to return from $(D onSend)/$(D onReceive) delegates in order to
+ static import etc.c.curl;
+
+ /// Value to return from `onSend`/`onReceive` delegates in order to
/// pause a request
alias requestPause = CurlReadFunc.pause;
@@ -3574,15 +3554,15 @@ struct FTP
/**
* The event handler that gets called when data is needed for sending. The
- * length of the $(D void[]) specifies the maximum number of bytes that can
+ * length of the `void[]` specifies the maximum number of bytes that can
* be sent.
*
* Returns:
* The callback returns the number of elements in the buffer that have been
* filled and are ready to send.
- * The special value $(D .abortRequest) can be returned in order to abort the
+ * The special value `.abortRequest` can be returned in order to abort the
* current request.
- * The special value $(D .pauseRequest) can be returned in order to pause the
+ * The special value `.pauseRequest` can be returned in order to pause the
* current request.
*
*/
@@ -3642,6 +3622,7 @@ struct FTP
*/
void addCommand(const(char)[] command)
{
+ import std.internal.cstring : tempCString;
p.commands = Curl.curl.slist_append(p.commands,
command.tempCString().buffPtr);
p.curl.set(CurlOption.postquote, p.commands);
@@ -3670,23 +3651,23 @@ struct FTP
/**
* Get various timings defined in $(REF CurlInfo, etc, c, curl).
- * The value is usable only if the return value is equal to $(D etc.c.curl.CurlError.ok).
+ * The value is usable only if the return value is equal to `etc.c.curl.CurlError.ok`.
*
* Params:
* timing = one of the timings defined in $(REF CurlInfo, etc, c, curl).
* The values are:
- * $(D etc.c.curl.CurlInfo.namelookup_time),
- * $(D etc.c.curl.CurlInfo.connect_time),
- * $(D etc.c.curl.CurlInfo.pretransfer_time),
- * $(D etc.c.curl.CurlInfo.starttransfer_time),
- * $(D etc.c.curl.CurlInfo.redirect_time),
- * $(D etc.c.curl.CurlInfo.appconnect_time),
- * $(D etc.c.curl.CurlInfo.total_time).
+ * `etc.c.curl.CurlInfo.namelookup_time`,
+ * `etc.c.curl.CurlInfo.connect_time`,
+ * `etc.c.curl.CurlInfo.pretransfer_time`,
+ * `etc.c.curl.CurlInfo.starttransfer_time`,
+ * `etc.c.curl.CurlInfo.redirect_time`,
+ * `etc.c.curl.CurlInfo.appconnect_time`,
+ * `etc.c.curl.CurlInfo.total_time`.
* val = the actual value of the inquired timing.
*
* Returns:
* The return code of the operation. The value stored in val
- * should be used only if the return value is $(D etc.c.curl.CurlInfo.ok).
+ * should be used only if the return value is `etc.c.curl.CurlInfo.ok`.
*
* Example:
* ---
@@ -3701,7 +3682,7 @@ struct FTP
* double val;
* CurlCode code;
*
- * code = http.getTiming(CurlInfo.namelookup_time, val);
+ * code = client.getTiming(CurlInfo.namelookup_time, val);
* assert(code == CurlError.ok);
* ---
*/
@@ -3755,6 +3736,8 @@ struct FTP
struct SMTP
{
mixin Protocol;
+ import std.typecons : RefCounted;
+ import etc.c.curl : CurlUseSSL, curl_slist;
private struct Impl
{
@@ -3840,6 +3823,7 @@ struct SMTP
@property void url(const(char)[] url)
{
import std.algorithm.searching : startsWith;
+ import std.exception : enforce;
import std.uni : toLower;
auto lowered = url.toLower();
@@ -3869,7 +3853,9 @@ struct SMTP
// docs mixed in.
version (StdDdoc)
{
- /// Value to return from $(D onSend)/$(D onReceive) delegates in order to
+ static import etc.c.curl;
+
+ /// Value to return from `onSend`/`onReceive` delegates in order to
/// pause a request
alias requestPause = CurlReadFunc.pause;
@@ -3994,15 +3980,15 @@ struct SMTP
/**
* The event handler that gets called when data is needed for sending. The
- * length of the $(D void[]) specifies the maximum number of bytes that can
+ * length of the `void[]` specifies the maximum number of bytes that can
* be sent.
*
* Returns:
* The callback returns the number of elements in the buffer that have been
* filled and are ready to send.
- * The special value $(D .abortRequest) can be returned in order to abort the
+ * The special value `.abortRequest` can be returned in order to abort the
* current request.
- * The special value $(D .pauseRequest) can be returned in order to pause the
+ * The special value `.pauseRequest` can be returned in order to pause the
* current request.
*/
@property void onSend(size_t delegate(void[]) callback);
@@ -4048,6 +4034,7 @@ struct SMTP
*/
void mailTo()(const(char)[][] recipients...)
{
+ import std.internal.cstring : tempCString;
assert(!recipients.empty, "Recipient must not be empty");
curl_slist* recipients_list = null;
foreach (recipient; recipients)
@@ -4069,6 +4056,20 @@ struct SMTP
}
}
+@system unittest
+{
+ import std.net.curl;
+
+ // Send an email with SMTPS
+ auto smtp = SMTP("smtps://smtp.gmail.com");
+ smtp.setAuthentication("from.addr@gmail.com", "password");
+ smtp.mailTo = ["<to.addr@gmail.com>"];
+ smtp.mailFrom = "<from.addr@gmail.com>";
+ smtp.message = "Example Message";
+ //smtp.perform();
+}
+
+
/++
Exception thrown on errors in std.net.curl functions.
+/
@@ -4143,14 +4144,16 @@ class HTTPStatusException : CurlException
/// Equal to $(REF CURLcode, etc,c,curl)
alias CurlCode = CURLcode;
-import std.typecons : Flag, Yes, No;
/// Flag to specify whether or not an exception is thrown on error.
alias ThrowOnError = Flag!"throwOnError";
private struct CurlAPI
{
+ import etc.c.curl : CurlGlobal;
static struct API
{
+ import etc.c.curl : curl_version_info, curl_version_info_data,
+ CURL, CURLcode, CURLINFO, CURLoption, CURLversion, curl_slist;
extern(C):
import core.stdc.config : c_long;
CURLcode function(c_long flags) global_init;
@@ -4179,6 +4182,8 @@ private struct CurlAPI
static void* loadAPI()
{
+ import std.exception : enforce;
+
version (Posix)
{
import core.sys.posix.dlfcn : dlsym, dlopen, dlclose, RTLD_LAZY;
@@ -4186,7 +4191,7 @@ private struct CurlAPI
}
else version (Windows)
{
- import core.sys.windows.windows : GetProcAddress, GetModuleHandleA,
+ import core.sys.windows.winbase : GetProcAddress, GetModuleHandleA,
LoadLibraryA;
alias loadSym = GetProcAddress;
}
@@ -4207,7 +4212,12 @@ private struct CurlAPI
version (Posix)
dlclose(handle);
- version (OSX)
+ version (LibcurlPath)
+ {
+ import std.string : strip;
+ static immutable names = [strip(import("LibcurlPathFile"))];
+ }
+ else version (OSX)
static immutable names = ["libcurl.4.dylib"];
else version (Posix)
{
@@ -4251,7 +4261,7 @@ private struct CurlAPI
}
else version (Windows)
{
- import core.sys.windows.windows : FreeLibrary;
+ import core.sys.windows.winbase : FreeLibrary;
FreeLibrary(_handle);
}
else
@@ -4269,7 +4279,7 @@ private struct CurlAPI
/**
Wrapper to provide a better interface to libcurl than using the plain C API.
- It is recommended to use the $(D HTTP)/$(D FTP) etc. structs instead unless
+ It is recommended to use the `HTTP`/`FTP` etc. structs instead unless
raw access to libcurl is needed.
Warning: This struct uses interior pointers for callbacks. Only allocate it
@@ -4279,6 +4289,11 @@ private struct CurlAPI
*/
struct Curl
{
+ import etc.c.curl : CURL, CurlError, CurlPause, CurlSeek, CurlSeekPos,
+ curl_socket_t, CurlSockType,
+ CurlReadFunc, CurlInfo, curlsocktype, curl_off_t,
+ LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR, LIBCURL_VERSION_PATCH;
+
alias OutData = void[];
alias InData = ubyte[];
private bool _stopped;
@@ -4288,7 +4303,7 @@ struct Curl
// A handle should not be used by two threads simultaneously
private CURL* handle;
- // May also return $(D CURL_READFUNC_ABORT) or $(D CURL_READFUNC_PAUSE)
+ // May also return `CURL_READFUNC_ABORT` or `CURL_READFUNC_PAUSE`
private size_t delegate(OutData) _onSend;
private size_t delegate(InData) _onReceive;
private void delegate(in char[]) _onReceiveHeader;
@@ -4305,6 +4320,7 @@ struct Curl
*/
void initialize()
{
+ import std.exception : enforce;
enforce!CurlException(!handle, "Curl instance already initialized");
handle = curl.easy_init();
enforce!CurlException(handle, "Curl instance couldn't be initialized");
@@ -4328,6 +4344,7 @@ struct Curl
*/
Curl dup()
{
+ import std.meta : AliasSeq;
Curl copy;
copy.handle = curl.easy_duphandle(handle);
copy._stopped = false;
@@ -4378,6 +4395,7 @@ struct Curl
private void _check(CurlCode code)
{
+ import std.exception : enforce;
enforce!CurlTimeoutException(code != CurlError.operation_timedout,
errorString(code));
@@ -4397,6 +4415,7 @@ struct Curl
private void throwOnStopped(string message = null)
{
+ import std.exception : enforce;
auto def = "Curl instance called after being cleaned up";
enforce!CurlException(!stopped,
message == null ? def : message);
@@ -4404,7 +4423,7 @@ struct Curl
/**
Stop and invalidate this curl instance.
- Warning: Do not call this from inside a callback handler e.g. $(D onReceive).
+ Warning: Do not call this from inside a callback handler e.g. `onReceive`.
*/
void shutdown()
{
@@ -4433,6 +4452,7 @@ struct Curl
*/
void set(CurlOption option, const(char)[] value)
{
+ import std.internal.cstring : tempCString;
throwOnStopped();
_check(curl.easy_setopt(this.handle, option, value.tempCString().buffPtr));
}
@@ -4506,7 +4526,7 @@ struct Curl
Get the various timings like name lookup time, total time, connect time etc.
The timed category is passed through the timing parameter while the timing
value is stored at val. The value is usable only if res is equal to
- $(D etc.c.curl.CurlError.ok).
+ `etc.c.curl.CurlError.ok`.
*/
CurlCode getTiming(CurlInfo timing, ref double val)
{
@@ -4519,7 +4539,7 @@ struct Curl
* The event handler that receives incoming data.
*
* Params:
- * callback = the callback that receives the $(D ubyte[]) data.
+ * callback = the callback that receives the `ubyte[]` data.
* Be sure to copy the incoming data and not store
* a slice.
*
@@ -4587,14 +4607,14 @@ struct Curl
* The event handler that gets called when data is needed for sending.
*
* Params:
- * callback = the callback that has a $(D void[]) buffer to be filled
+ * callback = the callback that has a `void[]` buffer to be filled
*
* Returns:
* The callback returns the number of elements in the buffer that have been
* filled and are ready to send.
- * The special value $(D Curl.abortRequest) can be returned in
+ * The special value `Curl.abortRequest` can be returned in
* order to abort the current request.
- * The special value $(D Curl.pauseRequest) can be returned in order to
+ * The special value `Curl.pauseRequest` can be returned in order to
* pause the current request.
*
* Example:
@@ -4666,7 +4686,7 @@ struct Curl
/**
* The event handler that gets called when the net socket has been created
- * but a $(D connect()) call has not yet been done. This makes it possible to set
+ * but a `connect()` call has not yet been done. This makes it possible to set
* misc. socket options.
*
* Params:
@@ -4715,16 +4735,17 @@ struct Curl
*
* Example:
* ----
- * import std.net.curl;
+ * import std.net.curl, std.stdio;
* Curl curl;
* curl.initialize();
* curl.set(CurlOption.url, "http://dlang.org");
- * curl.onProgress = delegate int(size_t dltotal, size_t dlnow, size_t ultotal, size_t uln)
+ * curl.onProgress = delegate int(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow)
* {
* writeln("Progress: downloaded bytes ", dlnow, " of ", dltotal);
* writeln("Progress: uploaded bytes ", ulnow, " of ", ultotal);
- * curl.perform();
+ * return 0;
* };
+ * curl.perform();
* ----
*/
@property void onProgress(int delegate(size_t dlTotal,
@@ -4865,6 +4886,7 @@ private struct Pool(Data)
@safe Data pop()
{
+ import std.exception : enforce;
enforce!Exception(root != null, "pop() called on empty pool");
auto d = root.data;
auto n = root.next;
@@ -4875,276 +4897,397 @@ private struct Pool(Data)
}
}
-// Shared function for reading incoming chunks of data and
-// sending the to a parent thread
-private static size_t _receiveAsyncChunks(ubyte[] data, ref ubyte[] outdata,
- Pool!(ubyte[]) freeBuffers,
- ref ubyte[] buffer, Tid fromTid,
- ref bool aborted)
+// Lazily-instantiated namespace to avoid importing std.concurrency until needed.
+private struct _async()
{
- immutable datalen = data.length;
-
- // Copy data to fill active buffer
- while (!data.empty)
+static:
+ // https://issues.dlang.org/show_bug.cgi?id=15831
+ // this should be inside byLineAsync
+ // Range that reads one chunk at a time asynchronously.
+ private struct ChunkInputRange
{
+ import std.concurrency : Tid, send;
- // Make sure a buffer is present
- while ( outdata.empty && freeBuffers.empty)
- {
- // Active buffer is invalid and there are no
- // available buffers in the pool. Wait for buffers
- // to return from main thread in order to reuse
- // them.
- receive((immutable(ubyte)[] buf)
- {
- buffer = cast(ubyte[]) buf;
- outdata = buffer[];
- },
- (bool flag) { aborted = true; }
- );
- if (aborted) return cast(size_t) 0;
- }
- if (outdata.empty)
- {
- buffer = freeBuffers.pop();
- outdata = buffer[];
- }
+ private ubyte[] chunk;
+ mixin WorkerThreadProtocol!(ubyte, chunk);
- // Copy data
- auto copyBytes = outdata.length < data.length ?
- outdata.length : data.length;
+ private Tid workerTid;
+ private State running;
- outdata[0 .. copyBytes] = data[0 .. copyBytes];
- outdata = outdata[copyBytes..$];
- data = data[copyBytes..$];
+ private this(Tid tid, size_t transmitBuffers, size_t chunkSize)
+ {
+ workerTid = tid;
+ state = State.needUnits;
- if (outdata.empty)
- fromTid.send(thisTid, curlMessage(cast(immutable(ubyte)[])buffer));
+ // Send buffers to other thread for it to use. Since no mechanism is in
+ // place for moving ownership a cast to shared is done here and a cast
+ // back to non-shared in the receiving end.
+ foreach (i ; 0 .. transmitBuffers)
+ {
+ ubyte[] arr = new ubyte[](chunkSize);
+ workerTid.send(cast(immutable(ubyte[]))arr);
+ }
+ }
}
- return datalen;
-}
-
-// ditto
-private static void _finalizeAsyncChunks(ubyte[] outdata, ref ubyte[] buffer,
- Tid fromTid)
-{
- if (!outdata.empty)
+ // https://issues.dlang.org/show_bug.cgi?id=15831
+ // this should be inside byLineAsync
+ // Range that reads one line at a time asynchronously.
+ private static struct LineInputRange(Char)
{
- // Resize the last buffer
- buffer.length = buffer.length - outdata.length;
- fromTid.send(thisTid, curlMessage(cast(immutable(ubyte)[])buffer));
- }
-}
+ private Char[] line;
+ mixin WorkerThreadProtocol!(Char, line);
+ private Tid workerTid;
+ private State running;
-// Shared function for reading incoming lines of data and sending the to a
-// parent thread
-private static size_t _receiveAsyncLines(Terminator, Unit)
- (const(ubyte)[] data, ref EncodingScheme encodingScheme,
- bool keepTerminator, Terminator terminator,
- ref const(ubyte)[] leftOverBytes, ref bool bufferValid,
- ref Pool!(Unit[]) freeBuffers, ref Unit[] buffer,
- Tid fromTid, ref bool aborted)
-{
- import std.format : format;
+ private this(Tid tid, size_t transmitBuffers, size_t bufferSize)
+ {
+ import std.concurrency : send;
- immutable datalen = data.length;
+ workerTid = tid;
+ state = State.needUnits;
- // Terminator is specified and buffers should be resized as determined by
- // the terminator
+ // Send buffers to other thread for it to use. Since no mechanism is in
+ // place for moving ownership a cast to shared is done here and casted
+ // back to non-shared in the receiving end.
+ foreach (i ; 0 .. transmitBuffers)
+ {
+ auto arr = new Char[](bufferSize);
+ workerTid.send(cast(immutable(Char[]))arr);
+ }
+ }
+ }
- // Copy data to active buffer until terminator is found.
+ import std.concurrency : Tid;
- // Decode as many lines as possible
- while (true)
+ // Shared function for reading incoming chunks of data and
+ // sending the to a parent thread
+ private size_t receiveChunks(ubyte[] data, ref ubyte[] outdata,
+ Pool!(ubyte[]) freeBuffers,
+ ref ubyte[] buffer, Tid fromTid,
+ ref bool aborted)
{
+ import std.concurrency : receive, send, thisTid;
+
+ immutable datalen = data.length;
- // Make sure a buffer is present
- while (!bufferValid && freeBuffers.empty)
+ // Copy data to fill active buffer
+ while (!data.empty)
{
- // Active buffer is invalid and there are no available buffers in
- // the pool. Wait for buffers to return from main thread in order to
- // reuse them.
- receive((immutable(Unit)[] buf)
- {
- buffer = cast(Unit[]) buf;
- buffer.length = 0;
- buffer.assumeSafeAppend();
- bufferValid = true;
- },
- (bool flag) { aborted = true; }
- );
- if (aborted) return cast(size_t) 0;
+
+ // Make sure a buffer is present
+ while ( outdata.empty && freeBuffers.empty)
+ {
+ // Active buffer is invalid and there are no
+ // available buffers in the pool. Wait for buffers
+ // to return from main thread in order to reuse
+ // them.
+ receive((immutable(ubyte)[] buf)
+ {
+ buffer = cast(ubyte[]) buf;
+ outdata = buffer[];
+ },
+ (bool flag) { aborted = true; }
+ );
+ if (aborted) return cast(size_t) 0;
+ }
+ if (outdata.empty)
+ {
+ buffer = freeBuffers.pop();
+ outdata = buffer[];
+ }
+
+ // Copy data
+ auto copyBytes = outdata.length < data.length ?
+ outdata.length : data.length;
+
+ outdata[0 .. copyBytes] = data[0 .. copyBytes];
+ outdata = outdata[copyBytes..$];
+ data = data[copyBytes..$];
+
+ if (outdata.empty)
+ fromTid.send(thisTid, curlMessage(cast(immutable(ubyte)[])buffer));
}
- if (!bufferValid)
+
+ return datalen;
+ }
+
+ // ditto
+ private void finalizeChunks(ubyte[] outdata, ref ubyte[] buffer,
+ Tid fromTid)
+ {
+ import std.concurrency : send, thisTid;
+ if (!outdata.empty)
{
- buffer = freeBuffers.pop();
- bufferValid = true;
+ // Resize the last buffer
+ buffer.length = buffer.length - outdata.length;
+ fromTid.send(thisTid, curlMessage(cast(immutable(ubyte)[])buffer));
}
+ }
- // Try to read a line from left over bytes from last onReceive plus the
- // newly received bytes.
- try
+
+ // Shared function for reading incoming lines of data and sending the to a
+ // parent thread
+ private static size_t receiveLines(Terminator, Unit)
+ (const(ubyte)[] data, ref EncodingScheme encodingScheme,
+ bool keepTerminator, Terminator terminator,
+ ref const(ubyte)[] leftOverBytes, ref bool bufferValid,
+ ref Pool!(Unit[]) freeBuffers, ref Unit[] buffer,
+ Tid fromTid, ref bool aborted)
+ {
+ import std.concurrency : prioritySend, receive, send, thisTid;
+ import std.exception : enforce;
+ import std.format : format;
+ import std.traits : isArray;
+
+ immutable datalen = data.length;
+
+ // Terminator is specified and buffers should be resized as determined by
+ // the terminator
+
+ // Copy data to active buffer until terminator is found.
+
+ // Decode as many lines as possible
+ while (true)
{
- if (decodeLineInto(leftOverBytes, data, buffer,
- encodingScheme, terminator))
+
+ // Make sure a buffer is present
+ while (!bufferValid && freeBuffers.empty)
{
- if (keepTerminator)
+ // Active buffer is invalid and there are no available buffers in
+ // the pool. Wait for buffers to return from main thread in order to
+ // reuse them.
+ receive((immutable(Unit)[] buf)
+ {
+ buffer = cast(Unit[]) buf;
+ buffer.length = 0;
+ buffer.assumeSafeAppend();
+ bufferValid = true;
+ },
+ (bool flag) { aborted = true; }
+ );
+ if (aborted) return cast(size_t) 0;
+ }
+ if (!bufferValid)
+ {
+ buffer = freeBuffers.pop();
+ bufferValid = true;
+ }
+
+ // Try to read a line from left over bytes from last onReceive plus the
+ // newly received bytes.
+ try
+ {
+ if (decodeLineInto(leftOverBytes, data, buffer,
+ encodingScheme, terminator))
{
- fromTid.send(thisTid,
- curlMessage(cast(immutable(Unit)[])buffer));
+ if (keepTerminator)
+ {
+ fromTid.send(thisTid,
+ curlMessage(cast(immutable(Unit)[])buffer));
+ }
+ else
+ {
+ static if (isArray!Terminator)
+ fromTid.send(thisTid,
+ curlMessage(cast(immutable(Unit)[])
+ buffer[0..$-terminator.length]));
+ else
+ fromTid.send(thisTid,
+ curlMessage(cast(immutable(Unit)[])
+ buffer[0..$-1]));
+ }
+ bufferValid = false;
}
else
{
- static if (isArray!Terminator)
- fromTid.send(thisTid,
- curlMessage(cast(immutable(Unit)[])
- buffer[0..$-terminator.length]));
- else
- fromTid.send(thisTid,
- curlMessage(cast(immutable(Unit)[])
- buffer[0..$-1]));
+ // Could not decode an entire line. Save
+ // bytes left in data for next call to
+ // onReceive. Can be up to a max of 4 bytes.
+ enforce!CurlException(data.length <= 4,
+ format(
+ "Too many bytes left not decoded %s"~
+ " > 4. Maybe the charset specified in"~
+ " headers does not match "~
+ "the actual content downloaded?",
+ data.length));
+ leftOverBytes ~= data;
+ break;
}
- bufferValid = false;
}
- else
+ catch (CurlException ex)
{
- // Could not decode an entire line. Save
- // bytes left in data for next call to
- // onReceive. Can be up to a max of 4 bytes.
- enforce!CurlException(data.length <= 4,
- format(
- "Too many bytes left not decoded %s"~
- " > 4. Maybe the charset specified in"~
- " headers does not match "~
- "the actual content downloaded?",
- data.length));
- leftOverBytes ~= data;
- break;
+ prioritySend(fromTid, cast(immutable(CurlException))ex);
+ return cast(size_t) 0;
}
}
- catch (CurlException ex)
- {
- prioritySend(fromTid, cast(immutable(CurlException))ex);
- return cast(size_t) 0;
- }
+ return datalen;
}
- return datalen;
-}
-
-// ditto
-private static
-void _finalizeAsyncLines(Unit)(bool bufferValid, Unit[] buffer, Tid fromTid)
-{
- if (bufferValid && buffer.length != 0)
- fromTid.send(thisTid, curlMessage(cast(immutable(Unit)[])buffer[0..$]));
-}
-
-
-// Spawn a thread for handling the reading of incoming data in the
-// background while the delegate is executing. This will optimize
-// throughput by allowing simultaneous input (this struct) and
-// output (e.g. AsyncHTTPLineOutputRange).
-private static void _spawnAsync(Conn, Unit, Terminator = void)()
-{
- Tid fromTid = receiveOnly!Tid();
-
- // Get buffer to read into
- Pool!(Unit[]) freeBuffers; // Free list of buffer objects
- // Number of bytes filled into active buffer
- Unit[] buffer;
- bool aborted = false;
+ // ditto
+ private static
+ void finalizeLines(Unit)(bool bufferValid, Unit[] buffer, Tid fromTid)
+ {
+ import std.concurrency : send, thisTid;
+ if (bufferValid && buffer.length != 0)
+ fromTid.send(thisTid, curlMessage(cast(immutable(Unit)[])buffer[0..$]));
+ }
- EncodingScheme encodingScheme;
- static if ( !is(Terminator == void))
+ /* Used by byLineAsync/byChunkAsync to duplicate an existing connection
+ * that can be used exclusively in a spawned thread.
+ */
+ private void duplicateConnection(Conn, PostData)
+ (const(char)[] url, Conn conn, PostData postData, Tid tid)
{
- // Only lines reading will receive a terminator
- const terminator = receiveOnly!Terminator();
- const keepTerminator = receiveOnly!bool();
+ import std.concurrency : send;
+ import std.exception : enforce;
+
+ // no move semantic available in std.concurrency ie. must use casting.
+ auto connDup = conn.dup();
+ connDup.url = url;
- // max number of bytes to carry over from an onReceive
- // callback. This is 4 because it is the max code units to
- // decode a code point in the supported encodings.
- auto leftOverBytes = new const(ubyte)[4];
- leftOverBytes.length = 0;
- auto bufferValid = false;
+ static if ( is(Conn : HTTP) )
+ {
+ connDup.p.headersOut = null;
+ connDup.method = conn.method == HTTP.Method.undefined ?
+ HTTP.Method.get : conn.method;
+ if (postData !is null)
+ {
+ if (connDup.method == HTTP.Method.put)
+ {
+ connDup.handle.set(CurlOption.infilesize_large,
+ postData.length);
+ }
+ else
+ {
+ // post
+ connDup.method = HTTP.Method.post;
+ connDup.handle.set(CurlOption.postfieldsize_large,
+ postData.length);
+ }
+ connDup.handle.set(CurlOption.copypostfields,
+ cast(void*) postData.ptr);
+ }
+ tid.send(cast(ulong) connDup.handle.handle);
+ tid.send(connDup.method);
+ }
+ else
+ {
+ enforce!CurlException(postData is null,
+ "Cannot put ftp data using byLineAsync()");
+ tid.send(cast(ulong) connDup.handle.handle);
+ tid.send(HTTP.Method.undefined);
+ }
+ connDup.p.curl.handle = null; // make sure handle is not freed
}
- else
+
+ // Spawn a thread for handling the reading of incoming data in the
+ // background while the delegate is executing. This will optimize
+ // throughput by allowing simultaneous input (this struct) and
+ // output (e.g. AsyncHTTPLineOutputRange).
+ private static void spawn(Conn, Unit, Terminator = void)()
{
- Unit[] outdata;
- }
+ import std.concurrency : Tid, prioritySend, receiveOnly, send, thisTid;
+ import etc.c.curl : CURL, CurlError;
+ Tid fromTid = receiveOnly!Tid();
- // no move semantic available in std.concurrency ie. must use casting.
- auto connDup = cast(CURL*) receiveOnly!ulong();
- auto client = Conn();
- client.p.curl.handle = connDup;
+ // Get buffer to read into
+ Pool!(Unit[]) freeBuffers; // Free list of buffer objects
- // receive a method for both ftp and http but just use it for http
- auto method = receiveOnly!(HTTP.Method)();
+ // Number of bytes filled into active buffer
+ Unit[] buffer;
+ bool aborted = false;
- client.onReceive = (ubyte[] data)
- {
- // If no terminator is specified the chunk size is fixed.
- static if ( is(Terminator == void) )
- return _receiveAsyncChunks(data, outdata, freeBuffers, buffer,
- fromTid, aborted);
+ EncodingScheme encodingScheme;
+ static if ( !is(Terminator == void))
+ {
+ // Only lines reading will receive a terminator
+ const terminator = receiveOnly!Terminator();
+ const keepTerminator = receiveOnly!bool();
+
+ // max number of bytes to carry over from an onReceive
+ // callback. This is 4 because it is the max code units to
+ // decode a code point in the supported encodings.
+ auto leftOverBytes = new const(ubyte)[4];
+ leftOverBytes.length = 0;
+ auto bufferValid = false;
+ }
else
- return _receiveAsyncLines(data, encodingScheme,
- keepTerminator, terminator, leftOverBytes,
- bufferValid, freeBuffers, buffer,
- fromTid, aborted);
- };
+ {
+ Unit[] outdata;
+ }
- static if ( is(Conn == HTTP) )
- {
- client.method = method;
- // register dummy header handler
- client.onReceiveHeader = (in char[] key, in char[] value)
+ // no move semantic available in std.concurrency ie. must use casting.
+ auto connDup = cast(CURL*) receiveOnly!ulong();
+ auto client = Conn();
+ client.p.curl.handle = connDup;
+
+ // receive a method for both ftp and http but just use it for http
+ auto method = receiveOnly!(HTTP.Method)();
+
+ client.onReceive = (ubyte[] data)
{
- if (key == "content-type")
- encodingScheme = EncodingScheme.create(client.p.charset);
+ // If no terminator is specified the chunk size is fixed.
+ static if ( is(Terminator == void) )
+ return receiveChunks(data, outdata, freeBuffers, buffer,
+ fromTid, aborted);
+ else
+ return receiveLines(data, encodingScheme,
+ keepTerminator, terminator, leftOverBytes,
+ bufferValid, freeBuffers, buffer,
+ fromTid, aborted);
};
- }
- else
- {
- encodingScheme = EncodingScheme.create(client.encoding);
- }
- // Start the request
- CurlCode code;
- try
- {
- code = client.perform(No.throwOnError);
- }
- catch (Exception ex)
- {
- prioritySend(fromTid, cast(immutable(Exception)) ex);
- fromTid.send(thisTid, curlMessage(true)); // signal done
- return;
- }
+ static if ( is(Conn == HTTP) )
+ {
+ client.method = method;
+ // register dummy header handler
+ client.onReceiveHeader = (in char[] key, in char[] value)
+ {
+ if (key == "content-type")
+ encodingScheme = EncodingScheme.create(client.p.charset);
+ };
+ }
+ else
+ {
+ encodingScheme = EncodingScheme.create(client.encoding);
+ }
- if (code != CurlError.ok)
- {
- if (aborted && (code == CurlError.aborted_by_callback ||
- code == CurlError.write_error))
+ // Start the request
+ CurlCode code;
+ try
+ {
+ code = client.perform(No.throwOnError);
+ }
+ catch (Exception ex)
{
+ prioritySend(fromTid, cast(immutable(Exception)) ex);
fromTid.send(thisTid, curlMessage(true)); // signal done
return;
}
- prioritySend(fromTid, cast(immutable(CurlException))
- new CurlException(client.p.curl.errorString(code)));
- fromTid.send(thisTid, curlMessage(true)); // signal done
- return;
- }
+ if (code != CurlError.ok)
+ {
+ if (aborted && (code == CurlError.aborted_by_callback ||
+ code == CurlError.write_error))
+ {
+ fromTid.send(thisTid, curlMessage(true)); // signal done
+ return;
+ }
+ prioritySend(fromTid, cast(immutable(CurlException))
+ new CurlException(client.p.curl.errorString(code)));
- // Send remaining data that is not a full chunk size
- static if ( is(Terminator == void) )
- _finalizeAsyncChunks(outdata, buffer, fromTid);
- else
- _finalizeAsyncLines(bufferValid, buffer, fromTid);
+ fromTid.send(thisTid, curlMessage(true)); // signal done
+ return;
+ }
- fromTid.send(thisTid, curlMessage(true)); // signal done
+ // Send remaining data that is not a full chunk size
+ static if ( is(Terminator == void) )
+ finalizeChunks(outdata, buffer, fromTid);
+ else
+ finalizeLines(bufferValid, buffer, fromTid);
+
+ fromTid.send(thisTid, curlMessage(true)); // signal done
+ }
}
diff --git a/libphobos/src/std/net/isemail.d b/libphobos/src/std/net/isemail.d
index cd6aa419701..f2a8ff3025d 100644
--- a/libphobos/src/std/net/isemail.d
+++ b/libphobos/src/std/net/isemail.d
@@ -5,6 +5,7 @@
* Copyright: Dominic Sayers, Jacob Carlborg 2008-.
* Test schema documentation: Copyright © 2011, Daniel Marschall
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
+ * Dominic Sayers graciously granted permission to use the Boost license via email on Feb 22, 2011.
* Version: 3.0.13 - Version 3.0 of the original PHP implementation: $(LINK http://www.dominicsayers.com/isemail)
*
* Standards:
@@ -20,13 +21,11 @@
* $(LI $(LINK http://tools.ietf.org/html/rfc5322))
* )
*
- * Source: $(PHOBOSSRC std/net/_isemail.d)
+ * Source: $(PHOBOSSRC std/net/isemail.d)
*/
module std.net.isemail;
-// FIXME
-import std.range.primitives; // : ElementType;
-import std.regex;
+import std.range.primitives : back, front, ElementType, popFront, popBack;
import std.traits;
import std.typecons : Flag, Yes, No;
@@ -41,7 +40,7 @@ import std.typecons : Flag, Yes, No;
*
* Params:
* email = The email address to check
- * checkDNS = If $(D Yes.checkDns) then a DNS check for MX records will be made
+ * checkDNS = If `Yes.checkDns` then a DNS check for MX records will be made
* errorLevel = Determines the boundary between valid and invalid addresses.
* Status codes above this number will be returned as-is,
* status codes below will be returned as EmailStatusCode.valid.
@@ -73,10 +72,6 @@ if (isSomeChar!(Char))
alias tstring = const(Char)[];
alias Token = TokenImpl!(Char);
- static ipRegex = ctRegex!(`\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}`~
- `(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`.to!(const(Char)[]));
- static fourChars = ctRegex!(`^[0-9A-Fa-f]{0,4}$`.to!(const(Char)[]));
-
enum defaultThreshold = 16;
int threshold;
bool diagnose;
@@ -398,12 +393,11 @@ if (isSomeChar!(Char))
auto maxGroups = 8;
size_t index = -1;
auto addressLiteral = parseData[EmailPart.componentLiteral];
- auto matchesIp = addressLiteral.matchAll(ipRegex).map!(a => a.hit).array;
+ const(Char)[] ipSuffix = matchIPSuffix(addressLiteral);
- if (!matchesIp.empty)
+ if (ipSuffix.length)
{
- index = addressLiteral.lastIndexOf(matchesIp.front);
-
+ index = addressLiteral.length - ipSuffix.length;
if (index != 0)
addressLiteral = addressLiteral[0 .. index] ~ "0:0";
}
@@ -417,7 +411,7 @@ if (isSomeChar!(Char))
else
{
auto ipV6 = addressLiteral[5 .. $];
- matchesIp = ipV6.split(Token.colon);
+ auto matchesIp = ipV6.split(Token.colon);
immutable groupCount = matchesIp.length;
index = ipV6.indexOf(Token.doubleColon);
@@ -452,7 +446,7 @@ if (isSomeChar!(Char))
returnStatus ~= EmailStatusCode.rfc5322IpV6ColonEnd;
else if (!matchesIp
- .filter!(a => a.matchFirst(fourChars).empty)
+ .filter!(a => !isUpToFourHexChars(a))
.empty)
returnStatus ~= EmailStatusCode.rfc5322IpV6BadChar;
@@ -1273,9 +1267,9 @@ if (isSomeChar!(Char))
/**
* Flag for indicating if the isEmail function should perform a DNS check or not.
*
- * If set to $(D CheckDns.no), isEmail does not perform DNS checking.
+ * If set to `CheckDns.no`, isEmail does not perform DNS checking.
*
- * Otherwise if set to $(D CheckDns.yes), isEmail performs DNS checking.
+ * Otherwise if set to `CheckDns.yes`, isEmail performs DNS checking.
*/
alias CheckDns = Flag!"checkDns";
@@ -1309,37 +1303,37 @@ struct EmailStatus
}
/// Returns: If the email address is valid or not.
- @property bool valid() const @safe @nogc pure nothrow
+ @property bool valid() const @safe @nogc pure nothrow scope
{
return valid_;
}
/// Returns: The local part of the email address, that is, the part before the @ sign.
- @property string localPart() const @safe @nogc pure nothrow
+ @property string localPart() const @safe @nogc pure nothrow return scope
{
return localPart_;
}
/// Returns: The domain part of the email address, that is, the part after the @ sign.
- @property string domainPart() const @safe @nogc pure nothrow
+ @property string domainPart() const @safe @nogc pure nothrow return scope
{
return domainPart_;
}
/// Returns: The email status code
- @property EmailStatusCode statusCode() const @safe @nogc pure nothrow
+ @property EmailStatusCode statusCode() const @safe @nogc pure nothrow scope
{
return statusCode_;
}
/// Returns: A describing string of the status code
- @property string status() const @safe @nogc pure nothrow
+ @property string status() const @safe @nogc pure nothrow scope
{
return statusCodeDescription(statusCode_);
}
/// Returns: A textual representation of the email status
- string toString() const @safe pure
+ string toString() const @safe pure scope
{
import std.format : format;
return format("EmailStatus\n{\n\tvalid: %s\n\tlocalPart: %s\n\tdomainPart: %s\n\tstatusCode: %s\n}", valid,
@@ -1789,7 +1783,7 @@ enum AsciiToken
* )
*/
int compareFirstN(alias pred = "a < b", S1, S2) (S1 s1, S2 s2, size_t length)
-if (is(Unqual!(ElementType!(S1)) == dchar) && is(Unqual!(ElementType!(S2)) == dchar))
+if (is(immutable ElementType!(S1) == immutable dchar) && is(immutable ElementType!(S2) == immutable dchar))
{
import std.uni : icmp;
auto s1End = length <= s1.length ? length : s1.length;
@@ -1862,3 +1856,96 @@ const(T)[] get (T) (const(T)[] str, size_t index, dchar c)
assert("abc".get(1, 'b') == "b");
assert("löv".get(1, 'ö') == "ö");
}
+
+/+
+Replacement for:
+---
+static fourChars = ctRegex!(`^[0-9A-Fa-f]{0,4}$`.to!(const(Char)[]));
+...
+a => a.matchFirst(fourChars).empty
+---
++/
+bool isUpToFourHexChars(Char)(scope const(Char)[] s)
+{
+ import std.ascii : isHexDigit;
+ if (s.length > 4) return false;
+ foreach (c; s)
+ if (!isHexDigit(c)) return false;
+ return true;
+}
+
+@nogc nothrow pure @safe unittest
+{
+ assert(!isUpToFourHexChars("12345"));
+ assert(!isUpToFourHexChars("defg"));
+ assert(isUpToFourHexChars("1A0a"));
+}
+
+/+
+Replacement for:
+---
+static ipRegex = ctRegex!(`\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}`~
+ `(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`.to!(const(Char)[]));
+...
+auto matchesIp = addressLiteral.matchAll(ipRegex).map!(a => a.hit).array;
+----
+Note that only the first item of "matchAll" was ever used in practice
+so we can return `const(Char)[]` instead of `const(Char)[][]` using a
+zero-length string to indicate no match.
++/
+const(Char)[] matchIPSuffix(Char)(return const(Char)[] s) @nogc nothrow pure @safe
+{
+ size_t end = s.length;
+ if (end < 7) return null;
+ // Check the first three `[.]\d{1,3}`
+ foreach (_; 0 .. 3)
+ {
+ size_t start = void;
+ if (end >= 2 && s[end-2] == '.')
+ start = end - 2;
+ else if (end >= 3 && s[end-3] == '.')
+ start = end - 3;
+ else if (end >= 4 && s[end-4] == '.')
+ start = end - 4;
+ else
+ return null;
+ uint x = 0;
+ foreach (i; start + 1 .. end)
+ {
+ uint c = cast(uint) s[i] - '0';
+ if (c > 9) return null;
+ x = x * 10 + c;
+ }
+ if (x > 255) return null;
+ end = start;
+ }
+ // Check the final `\d{1,3}`.
+ if (end < 1) return null;
+ size_t start = end - 1;
+ uint x = cast(uint) s[start] - '0';
+ if (x > 9) return null;
+ if (start > 0 && cast(uint) s[start-1] - '0' <= 9)
+ {
+ --start;
+ x += 10 * (cast(uint) s[start] - '0');
+ if (start > 0 && cast(uint) s[start-1] - '0' <= 9)
+ {
+ --start;
+ x += 100 * (cast(uint) s[start] - '0');
+ }
+ }
+ if (x > 255) return null;
+ // Must either be at start of string or preceded by a non-word character.
+ // (TO DETERMINE: is the definition of "word character" ASCII only?)
+ if (start == 0) return s;
+ const b = s[start - 1];
+ import std.ascii : isAlphaNum;
+ if (isAlphaNum(b) || b == '_') return null;
+ return s[start .. $];
+}
+
+@nogc nothrow pure @safe unittest
+{
+ assert(matchIPSuffix("255.255.255.255") == "255.255.255.255");
+ assert(matchIPSuffix("babaev 176.16.0.1") == "176.16.0.1");
+}
diff --git a/libphobos/src/std/numeric.d b/libphobos/src/std/numeric.d
index 307406e50a0..fd532b2ce40 100644
--- a/libphobos/src/std/numeric.d
+++ b/libphobos/src/std/numeric.d
@@ -2,7 +2,7 @@
/**
This module is a port of a growing fragment of the $(D_PARAM numeric)
-header in Alexander Stepanov's $(LINK2 http://sgi.com/tech/stl,
+header in Alexander Stepanov's $(LINK2 https://en.wikipedia.org/wiki/Standard_Template_Library,
Standard Template Library), with a few additions.
Macros:
@@ -10,7 +10,7 @@ Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.org, Andrei Alexandrescu),
Don Clugston, Robert Jacques, Ilya Yaroshenko
-Source: $(PHOBOSSRC std/_numeric.d)
+Source: $(PHOBOSSRC std/numeric.d)
*/
/*
Copyright Andrei Alexandrescu 2008 - 2009.
@@ -22,14 +22,11 @@ module std.numeric;
import std.complex;
import std.math;
+import core.math : fabs, ldexp, sin, sqrt;
import std.range.primitives;
import std.traits;
import std.typecons;
-version (unittest)
-{
- import std.stdio;
-}
/// Format flags for CustomFloat.
public enum CustomFloatFlags
{
@@ -39,7 +36,7 @@ public enum CustomFloatFlags
/**
* Store values in normalized form by default. The actual precision of the
* significand is extended by 1 bit by assuming an implicit leading bit of 1
- * instead of 0. i.e. $(D 1.nnnn) instead of $(D 0.nnnn).
+ * instead of 0. i.e. `1.nnnn` instead of `0.nnnn`.
* True for all $(LINK2 https://en.wikipedia.org/wiki/IEEE_floating_point, IEE754) types
*/
storeNormalized = 2,
@@ -109,7 +106,7 @@ private template CustomFloatParams(uint precision, uint exponentWidth, CustomFlo
/**
* Allows user code to define custom floating-point formats. These formats are
* for storage only; all operations on them are performed by first implicitly
- * extracting them to $(D real) first. After the operation is completed the
+ * extracting them to `real` first. After the operation is completed the
* result can be stored in a custom floating-point value via assignment.
*/
template CustomFloat(uint bits)
@@ -128,7 +125,7 @@ if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && precision +
///
@safe unittest
{
- import std.math : sin, cos;
+ import std.math.trigonometry : sin, cos;
// Define a 16-bit floating point values
CustomFloat!16 x; // Using the number of bits
@@ -149,13 +146,33 @@ if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && precision +
auto p = Probability(0.5);
}
+// Facilitate converting numeric types to custom float
+private union ToBinary(F)
+if (is(typeof(CustomFloatParams!(F.sizeof*8))) || is(F == real))
+{
+ F set;
+
+ // If on Linux or Mac, where 80-bit reals are padded, ignore the
+ // padding.
+ import std.algorithm.comparison : min;
+ CustomFloat!(CustomFloatParams!(min(F.sizeof*8, 80))) get;
+
+ // Convert F to the correct binary type.
+ static typeof(get) opCall(F value)
+ {
+ ToBinary r;
+ r.set = value;
+ return r.get;
+ }
+ alias get this;
+}
+
/// ditto
struct CustomFloat(uint precision, // fraction bits (23 for float)
uint exponentWidth, // exponent bits (8 for float) Exponent width
CustomFloatFlags flags,
uint bias)
-if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 &&
- precision + exponentWidth > 0)
+if (isCorrectCustomFloat(precision, exponentWidth, flags))
{
import std.bitmanip : bitfields;
import std.meta : staticIndexOf;
@@ -180,31 +197,15 @@ private:
alias Flags = CustomFloatFlags;
- // Facilitate converting numeric types to custom float
- union ToBinary(F)
- if (is(typeof(CustomFloatParams!(F.sizeof*8))) || is(F == real))
- {
- F set;
-
- // If on Linux or Mac, where 80-bit reals are padded, ignore the
- // padding.
- import std.algorithm.comparison : min;
- CustomFloat!(CustomFloatParams!(min(F.sizeof*8, 80))) get;
-
- // Convert F to the correct binary type.
- static typeof(get) opCall(F value)
- {
- ToBinary r;
- r.set = value;
- return r.get;
- }
- alias get this;
- }
-
// Perform IEEE rounding with round to nearest detection
void roundedShift(T,U)(ref T sig, U shift)
{
- if (sig << (T.sizeof*8 - shift) == cast(T) 1uL << (T.sizeof*8 - 1))
+ if (shift >= T.sizeof*8)
+ {
+ // avoid illegal shift
+ sig = 0;
+ }
+ else if (sig << (T.sizeof*8 - shift) == cast(T) 1uL << (T.sizeof*8 - 1))
{
// round to even
sig >>= shift;
@@ -386,7 +387,7 @@ public:
{
CustomFloat value;
static if (flags & Flags.signed)
- value.sign = 0;
+ value.sign = 0;
value.significand = 0;
value.exponent = exponent_max;
return value;
@@ -398,7 +399,7 @@ public:
{
CustomFloat value;
static if (flags & Flags.signed)
- value.sign = 0;
+ value.sign = 0;
value.significand = cast(typeof(significand_max)) 1L << (precision-1);
value.exponent = exponent_max;
return value;
@@ -407,32 +408,18 @@ public:
/// Returns: number of decimal digits of precision
static @property size_t dig()
{
- auto shiftcnt = precision - ((flags&Flags.storeNormalized) != 0);
- immutable x = (shiftcnt == 64) ? 0 : 1uL << shiftcnt;
- return cast(size_t) log10(x);
+ auto shiftcnt = precision - ((flags&Flags.storeNormalized) == 0);
+ return shiftcnt == 64 ? 19 : cast(size_t) log10(1uL << shiftcnt);
}
/// Returns: smallest increment to the value 1
static @property CustomFloat epsilon()
{
- CustomFloat value;
- static if (flags & Flags.signed)
- value.sign = 0;
- T_signed_exp exp = -precision;
- T_sig sig = 0;
+ CustomFloat one = CustomFloat(1);
+ CustomFloat onePlusEpsilon = one;
+ onePlusEpsilon.significand = onePlusEpsilon.significand | 1; // |= does not work here
- value.fromNormalized(sig,exp);
- if (exp == 0 && sig == 0) // underflowed to zero
- {
- static if ((flags&Flags.allowDenorm) ||
- (~flags&Flags.storeNormalized))
- sig = 1;
- else
- sig = cast(T) 1uL << (precision - 1);
- }
- value.exponent = cast(value.T_exp) exp;
- value.significand = cast(value.T_sig) sig;
- return value;
+ return CustomFloat(onePlusEpsilon - one);
}
/// the number of bits in mantissa
@@ -442,32 +429,33 @@ public:
static @property int max_10_exp(){ return cast(int) log10( +max ); }
/// maximum int value such that 2<sup>max_exp-1</sup> is representable
- enum max_exp = exponent_max-bias+((~flags&(Flags.infinity|flags.nan))!=0);
+ enum max_exp = exponent_max - bias - ((flags & (Flags.infinity | Flags.nan)) != 0) + 1;
/// Returns: minimum int value such that 10<sup>min_10_exp</sup> is representable
static @property int min_10_exp(){ return cast(int) log10( +min_normal ); }
/// minimum int value such that 2<sup>min_exp-1</sup> is representable as a normalized value
- enum min_exp = cast(T_signed_exp)-bias +1+ ((flags&Flags.allowDenorm)!=0);
+ enum min_exp = cast(T_signed_exp) -(cast(long) bias) + 1 + ((flags & Flags.allowDenorm) != 0);
/// Returns: largest representable value that's not infinity
static @property CustomFloat max()
{
CustomFloat value;
static if (flags & Flags.signed)
- value.sign = 0;
+ value.sign = 0;
value.exponent = exponent_max - ((flags&(flags.infinity|flags.nan)) != 0);
value.significand = significand_max;
return value;
}
/// Returns: smallest representable normalized value that's not 0
- static @property CustomFloat min_normal() {
+ static @property CustomFloat min_normal()
+ {
CustomFloat value;
static if (flags & Flags.signed)
- value.sign = 0;
- value.exponent = 1;
- static if (flags&Flags.storeNormalized)
+ value.sign = 0;
+ value.exponent = (flags & Flags.allowDenorm) != 0;
+ static if (flags & Flags.storeNormalized)
value.significand = 0;
else
value.significand = cast(T_sig) 1uL << (precision - 1);
@@ -480,7 +468,7 @@ public:
/// Returns: imaginary part
static @property CustomFloat im() { return CustomFloat(0.0f); }
- /// Initialize from any $(D real) compatible type.
+ /// Initialize from any `real` compatible type.
this(F)(F input) if (__traits(compiles, cast(real) input ))
{
this = input;
@@ -490,18 +478,18 @@ public:
void opAssign(F:CustomFloat)(F input)
{
static if (flags & Flags.signed)
- sign = input.sign;
+ sign = input.sign;
exponent = input.exponent;
significand = input.significand;
}
- /// Assigns from any $(D real) compatible type.
+ /// Assigns from any `real` compatible type.
void opAssign(F)(F input)
if (__traits(compiles, cast(real) input))
{
import std.conv : text;
- static if (staticIndexOf!(Unqual!F, float, double, real) >= 0)
+ static if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0)
auto value = ToBinary!(Unqual!F)(input);
else
auto value = ToBinary!(real )(input);
@@ -529,9 +517,9 @@ public:
significand = cast(T_sig) sig;
}
- /// Fetches the stored value either as a $(D float), $(D double) or $(D real).
+ /// Fetches the stored value either as a `float`, `double` or `real`.
@property F get(F)()
- if (staticIndexOf!(Unqual!F, float, double, real) >= 0)
+ if (staticIndexOf!(immutable F, immutable float, immutable double, immutable real) >= 0)
{
import std.conv : text;
@@ -555,9 +543,9 @@ public:
}
///ditto
- T opCast(T)() if (__traits(compiles, get!T )) { return get!T; }
+ alias opCast = get;
- /// Convert the CustomFloat to a real and perform the relavent operator on the result
+ /// Convert the CustomFloat to a real and perform the relevant operator on the result
real opUnary(string op)()
if (__traits(compiles, mixin(op~`(get!real)`)) || op=="++" || op=="--")
{
@@ -572,8 +560,19 @@ public:
}
/// ditto
+ // Define an opBinary `CustomFloat op CustomFloat` so that those below
+ // do not match equally, which is disallowed by the spec:
+ // https://dlang.org/spec/operatoroverloading.html#binary
real opBinary(string op,T)(T b)
- if (__traits(compiles, mixin(`get!real`~op~`b`)))
+ if (__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
+ {
+ return mixin(`get!real`~op~`b.get!real`);
+ }
+
+ /// ditto
+ real opBinary(string op,T)(T b)
+ if ( __traits(compiles, mixin(`get!real`~op~`b`)) &&
+ !__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
{
return mixin(`get!real`~op~`b`);
}
@@ -581,7 +580,8 @@ public:
/// ditto
real opBinaryRight(string op,T)(T a)
if ( __traits(compiles, mixin(`a`~op~`get!real`)) &&
- !__traits(compiles, mixin(`get!real`~op~`b`)))
+ !__traits(compiles, mixin(`get!real`~op~`b`)) &&
+ !__traits(compiles, mixin(`get!real`~op~`b.get!real`)))
{
return mixin(`a`~op~`get!real`);
}
@@ -605,9 +605,10 @@ public:
/// ditto
template toString()
{
- import std.format : FormatSpec, formatValue;
- // Needs to be a template because of DMD @@BUG@@ 13737.
- void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
+ import std.format.spec : FormatSpec;
+ import std.format.write : formatValue;
+ // Needs to be a template because of https://issues.dlang.org/show_bug.cgi?id=13737.
+ void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt)
{
sink.formatValue(get!real, fmt);
}
@@ -621,7 +622,7 @@ public:
AliasSeq!(
CustomFloat!(5, 10),
CustomFloat!(5, 11, CustomFloatFlags.ieee ^ CustomFloatFlags.signed),
- CustomFloat!(1, 15, CustomFloatFlags.ieee ^ CustomFloatFlags.signed),
+ CustomFloat!(1, 7, CustomFloatFlags.ieee ^ CustomFloatFlags.signed),
CustomFloat!(4, 3, CustomFloatFlags.ieee | CustomFloatFlags.probability ^ CustomFloatFlags.signed)
);
@@ -658,26 +659,320 @@ public:
assert(y.to!string == "0.125");
}
+@safe unittest
+{
+ alias cf = CustomFloat!(5, 2);
+
+ auto a = cf.infinity;
+ assert(a.sign == 0);
+ assert(a.exponent == 3);
+ assert(a.significand == 0);
+
+ auto b = cf.nan;
+ assert(b.exponent == 3);
+ assert(b.significand != 0);
+
+ assert(cf.dig == 1);
+
+ auto c = cf.epsilon;
+ assert(c.sign == 0);
+ assert(c.exponent == 0);
+ assert(c.significand == 1);
+
+ assert(cf.mant_dig == 6);
+
+ assert(cf.max_10_exp == 0);
+ assert(cf.max_exp == 2);
+ assert(cf.min_10_exp == 0);
+ assert(cf.min_exp == 1);
+
+ auto d = cf.max;
+ assert(d.sign == 0);
+ assert(d.exponent == 2);
+ assert(d.significand == 31);
+
+ auto e = cf.min_normal;
+ assert(e.sign == 0);
+ assert(e.exponent == 1);
+ assert(e.significand == 0);
+
+ assert(e.re == e);
+ assert(e.im == cf(0.0));
+}
+
+// check whether CustomFloats identical to float/double behave like float/double
+@safe unittest
+{
+ import std.conv : to;
+
+ alias myFloat = CustomFloat!(23, 8);
+
+ static assert(myFloat.dig == float.dig);
+ static assert(myFloat.mant_dig == float.mant_dig);
+ assert(myFloat.max_10_exp == float.max_10_exp);
+ static assert(myFloat.max_exp == float.max_exp);
+ assert(myFloat.min_10_exp == float.min_10_exp);
+ static assert(myFloat.min_exp == float.min_exp);
+ assert(to!float(myFloat.epsilon) == float.epsilon);
+ assert(to!float(myFloat.max) == float.max);
+ assert(to!float(myFloat.min_normal) == float.min_normal);
+
+ alias myDouble = CustomFloat!(52, 11);
+
+ static assert(myDouble.dig == double.dig);
+ static assert(myDouble.mant_dig == double.mant_dig);
+ assert(myDouble.max_10_exp == double.max_10_exp);
+ static assert(myDouble.max_exp == double.max_exp);
+ assert(myDouble.min_10_exp == double.min_10_exp);
+ static assert(myDouble.min_exp == double.min_exp);
+ assert(to!double(myDouble.epsilon) == double.epsilon);
+ assert(to!double(myDouble.max) == double.max);
+ assert(to!double(myDouble.min_normal) == double.min_normal);
+}
+
+// testing .dig
+@safe unittest
+{
+ static assert(CustomFloat!(1, 6).dig == 0);
+ static assert(CustomFloat!(9, 6).dig == 2);
+ static assert(CustomFloat!(10, 5).dig == 3);
+ static assert(CustomFloat!(10, 6, CustomFloatFlags.none).dig == 2);
+ static assert(CustomFloat!(11, 5, CustomFloatFlags.none).dig == 3);
+ static assert(CustomFloat!(64, 7).dig == 19);
+}
+
+// testing .mant_dig
+@safe unittest
+{
+ static assert(CustomFloat!(10, 5).mant_dig == 11);
+ static assert(CustomFloat!(10, 6, CustomFloatFlags.none).mant_dig == 10);
+}
+
+// testing .max_exp
+@safe unittest
+{
+ static assert(CustomFloat!(1, 6).max_exp == 2^^5);
+ static assert(CustomFloat!(2, 6, CustomFloatFlags.none).max_exp == 2^^5);
+ static assert(CustomFloat!(5, 10).max_exp == 2^^9);
+ static assert(CustomFloat!(6, 10, CustomFloatFlags.none).max_exp == 2^^9);
+ static assert(CustomFloat!(2, 6, CustomFloatFlags.nan).max_exp == 2^^5);
+ static assert(CustomFloat!(6, 10, CustomFloatFlags.nan).max_exp == 2^^9);
+}
+
+// testing .min_exp
+@safe unittest
+{
+ static assert(CustomFloat!(1, 6).min_exp == -2^^5+3);
+ static assert(CustomFloat!(5, 10).min_exp == -2^^9+3);
+ static assert(CustomFloat!(2, 6, CustomFloatFlags.none).min_exp == -2^^5+1);
+ static assert(CustomFloat!(6, 10, CustomFloatFlags.none).min_exp == -2^^9+1);
+ static assert(CustomFloat!(2, 6, CustomFloatFlags.nan).min_exp == -2^^5+2);
+ static assert(CustomFloat!(6, 10, CustomFloatFlags.nan).min_exp == -2^^9+2);
+ static assert(CustomFloat!(2, 6, CustomFloatFlags.allowDenorm).min_exp == -2^^5+2);
+ static assert(CustomFloat!(6, 10, CustomFloatFlags.allowDenorm).min_exp == -2^^9+2);
+}
+
+// testing .max_10_exp
+@safe unittest
+{
+ assert(CustomFloat!(1, 6).max_10_exp == 9);
+ assert(CustomFloat!(5, 10).max_10_exp == 154);
+ assert(CustomFloat!(2, 6, CustomFloatFlags.none).max_10_exp == 9);
+ assert(CustomFloat!(6, 10, CustomFloatFlags.none).max_10_exp == 154);
+ assert(CustomFloat!(2, 6, CustomFloatFlags.nan).max_10_exp == 9);
+ assert(CustomFloat!(6, 10, CustomFloatFlags.nan).max_10_exp == 154);
+}
+
+// testing .min_10_exp
+@safe unittest
+{
+ assert(CustomFloat!(1, 6).min_10_exp == -9);
+ assert(CustomFloat!(5, 10).min_10_exp == -153);
+ assert(CustomFloat!(2, 6, CustomFloatFlags.none).min_10_exp == -9);
+ assert(CustomFloat!(6, 10, CustomFloatFlags.none).min_10_exp == -154);
+ assert(CustomFloat!(2, 6, CustomFloatFlags.nan).min_10_exp == -9);
+ assert(CustomFloat!(6, 10, CustomFloatFlags.nan).min_10_exp == -153);
+ assert(CustomFloat!(2, 6, CustomFloatFlags.allowDenorm).min_10_exp == -9);
+ assert(CustomFloat!(6, 10, CustomFloatFlags.allowDenorm).min_10_exp == -153);
+}
+
+// testing .epsilon
+@safe unittest
+{
+ assert(CustomFloat!(1,6).epsilon.sign == 0);
+ assert(CustomFloat!(1,6).epsilon.exponent == 30);
+ assert(CustomFloat!(1,6).epsilon.significand == 0);
+ assert(CustomFloat!(2,5).epsilon.sign == 0);
+ assert(CustomFloat!(2,5).epsilon.exponent == 13);
+ assert(CustomFloat!(2,5).epsilon.significand == 0);
+ assert(CustomFloat!(3,4).epsilon.sign == 0);
+ assert(CustomFloat!(3,4).epsilon.exponent == 4);
+ assert(CustomFloat!(3,4).epsilon.significand == 0);
+ // the following epsilons are only available, when denormalized numbers are allowed:
+ assert(CustomFloat!(4,3).epsilon.sign == 0);
+ assert(CustomFloat!(4,3).epsilon.exponent == 0);
+ assert(CustomFloat!(4,3).epsilon.significand == 4);
+ assert(CustomFloat!(5,2).epsilon.sign == 0);
+ assert(CustomFloat!(5,2).epsilon.exponent == 0);
+ assert(CustomFloat!(5,2).epsilon.significand == 1);
+}
+
+// testing .max
+@safe unittest
+{
+ static assert(CustomFloat!(5,2).max.sign == 0);
+ static assert(CustomFloat!(5,2).max.exponent == 2);
+ static assert(CustomFloat!(5,2).max.significand == 31);
+ static assert(CustomFloat!(4,3).max.sign == 0);
+ static assert(CustomFloat!(4,3).max.exponent == 6);
+ static assert(CustomFloat!(4,3).max.significand == 15);
+ static assert(CustomFloat!(3,4).max.sign == 0);
+ static assert(CustomFloat!(3,4).max.exponent == 14);
+ static assert(CustomFloat!(3,4).max.significand == 7);
+ static assert(CustomFloat!(2,5).max.sign == 0);
+ static assert(CustomFloat!(2,5).max.exponent == 30);
+ static assert(CustomFloat!(2,5).max.significand == 3);
+ static assert(CustomFloat!(1,6).max.sign == 0);
+ static assert(CustomFloat!(1,6).max.exponent == 62);
+ static assert(CustomFloat!(1,6).max.significand == 1);
+ static assert(CustomFloat!(3,5, CustomFloatFlags.none).max.exponent == 31);
+ static assert(CustomFloat!(3,5, CustomFloatFlags.none).max.significand == 7);
+}
+
+// testing .min_normal
+@safe unittest
+{
+ static assert(CustomFloat!(5,2).min_normal.sign == 0);
+ static assert(CustomFloat!(5,2).min_normal.exponent == 1);
+ static assert(CustomFloat!(5,2).min_normal.significand == 0);
+ static assert(CustomFloat!(4,3).min_normal.sign == 0);
+ static assert(CustomFloat!(4,3).min_normal.exponent == 1);
+ static assert(CustomFloat!(4,3).min_normal.significand == 0);
+ static assert(CustomFloat!(3,4).min_normal.sign == 0);
+ static assert(CustomFloat!(3,4).min_normal.exponent == 1);
+ static assert(CustomFloat!(3,4).min_normal.significand == 0);
+ static assert(CustomFloat!(2,5).min_normal.sign == 0);
+ static assert(CustomFloat!(2,5).min_normal.exponent == 1);
+ static assert(CustomFloat!(2,5).min_normal.significand == 0);
+ static assert(CustomFloat!(1,6).min_normal.sign == 0);
+ static assert(CustomFloat!(1,6).min_normal.exponent == 1);
+ static assert(CustomFloat!(1,6).min_normal.significand == 0);
+ static assert(CustomFloat!(3,5, CustomFloatFlags.none).min_normal.exponent == 0);
+ static assert(CustomFloat!(3,5, CustomFloatFlags.none).min_normal.significand == 4);
+}
+
+@safe unittest
+{
+ import std.math.traits : isNaN;
+
+ alias cf = CustomFloat!(5, 2);
+
+ auto f = cf.nan.get!float();
+ assert(isNaN(f));
+
+ cf a;
+ a = real.max;
+ assert(a == cf.infinity);
+
+ a = 0.015625;
+ assert(a.exponent == 0);
+ assert(a.significand == 0);
+
+ a = 0.984375;
+ assert(a.exponent == 1);
+ assert(a.significand == 0);
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+ import core.exception : AssertError;
+
+ alias cf = CustomFloat!(3, 5, CustomFloatFlags.none);
+
+ cf a;
+ assertThrown!AssertError(a = real.max);
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+ import core.exception : AssertError;
+
+ alias cf = CustomFloat!(3, 5, CustomFloatFlags.nan);
+
+ cf a;
+ assertThrown!AssertError(a = real.max);
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+ import core.exception : AssertError;
+
+ alias cf = CustomFloat!(24, 8, CustomFloatFlags.none);
+
+ cf a;
+ assertThrown!AssertError(a = float.infinity);
+}
+
+private bool isCorrectCustomFloat(uint precision, uint exponentWidth, CustomFloatFlags flags) @safe pure nothrow @nogc
+{
+ // Restrictions from bitfield
+ // due to CustomFloat!80 support hack precision with 64 bits is handled specially
+ auto length = (flags & flags.signed) + exponentWidth + ((precision == 64) ? 0 : precision);
+ if (length != 8 && length != 16 && length != 32 && length != 64) return false;
+
+ // mantissa needs to fit into real mantissa
+ if (precision > real.mant_dig - 1 && precision != 64) return false;
+
+ // exponent needs to fit into real exponent
+ if (1L << exponentWidth - 1 > real.max_exp) return false;
+
+ // mantissa should have at least one bit
+ if (precision == 0) return false;
+
+ // exponent should have at least one bit, in some cases two
+ if (exponentWidth <= ((flags & (flags.allowDenorm | flags.infinity | flags.nan)) != 0)) return false;
+
+ return true;
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(isCorrectCustomFloat(3,4,CustomFloatFlags.ieee));
+ assert(isCorrectCustomFloat(3,5,CustomFloatFlags.none));
+ assert(!isCorrectCustomFloat(3,3,CustomFloatFlags.ieee));
+ assert(isCorrectCustomFloat(64,7,CustomFloatFlags.ieee));
+ assert(!isCorrectCustomFloat(64,4,CustomFloatFlags.ieee));
+ assert(!isCorrectCustomFloat(508,3,CustomFloatFlags.ieee));
+ assert(!isCorrectCustomFloat(3,100,CustomFloatFlags.ieee));
+ assert(!isCorrectCustomFloat(0,7,CustomFloatFlags.ieee));
+ assert(!isCorrectCustomFloat(6,1,CustomFloatFlags.ieee));
+ assert(isCorrectCustomFloat(7,1,CustomFloatFlags.none));
+ assert(!isCorrectCustomFloat(8,0,CustomFloatFlags.none));
+}
+
/**
Defines the fastest type to use when storing temporaries of a
-calculation intended to ultimately yield a result of type $(D F)
-(where $(D F) must be one of $(D float), $(D double), or $(D
+calculation intended to ultimately yield a result of type `F`
+(where `F` must be one of `float`, `double`, or $(D
real)). When doing a multi-step computation, you may want to store
-intermediate results as $(D FPTemporary!F).
+intermediate results as `FPTemporary!F`.
-The necessity of $(D FPTemporary) stems from the optimized
+The necessity of `FPTemporary` stems from the optimized
floating-point operations and registers present in virtually all
processors. When adding numbers in the example above, the addition may
-in fact be done in $(D real) precision internally. In that case,
-storing the intermediate $(D result) in $(D double format) is not only
+in fact be done in `real` precision internally. In that case,
+storing the intermediate `result` in $(D double format) is not only
less precise, it is also (surprisingly) slower, because a conversion
-from $(D real) to $(D double) is performed every pass through the
-loop. This being a lose-lose situation, $(D FPTemporary!F) has been
+from `real` to `double` is performed every pass through the
+loop. This being a lose-lose situation, `FPTemporary!F` has been
defined as the $(I fastest) type to use for calculations at precision
-$(D F). There is no need to define a type for the $(I most accurate)
-calculations, as that is always $(D real).
+`F`. There is no need to define a type for the $(I most accurate)
+calculations, as that is always `real`.
-Finally, there is no guarantee that using $(D FPTemporary!F) will
+Finally, there is no guarantee that using `FPTemporary!F` will
always be fastest, as the speed of floating-point calculations depends
on very many factors.
*/
@@ -693,7 +988,7 @@ if (isFloatingPoint!F)
///
@safe unittest
{
- import std.math : approxEqual;
+ import std.math.operations : isClose;
// Average numbers in an array
double avg(in double[] a)
@@ -705,14 +1000,14 @@ if (isFloatingPoint!F)
}
auto a = [1.0, 2.0, 3.0];
- assert(approxEqual(avg(a), 2));
+ assert(isClose(avg(a), 2));
}
/**
Implements the $(HTTP tinyurl.com/2zb9yr, secant method) for finding a
-root of the function $(D fun) starting from points $(D [xn_1, x_n])
-(ideally close to the root). $(D Num) may be $(D float), $(D double),
-or $(D real).
+root of the function `fun` starting from points $(D [xn_1, x_n])
+(ideally close to the root). `Num` may be `float`, `double`,
+or `real`.
*/
template secantMethod(alias fun)
{
@@ -723,7 +1018,7 @@ template secantMethod(alias fun)
typeof(fxn) fxn_1;
xn = xn_1;
- while (!approxEqual(d, 0) && isFinite(d))
+ while (!isClose(d, 0, 0.0, 1e-5) && isFinite(d))
{
xn_1 = xn;
xn -= d;
@@ -738,29 +1033,31 @@ template secantMethod(alias fun)
///
@safe unittest
{
- import std.math : approxEqual, cos;
+ import std.math.operations : isClose;
+ import std.math.trigonometry : cos;
float f(float x)
{
return cos(x) - x*x*x;
}
auto x = secantMethod!(f)(0f, 1f);
- assert(approxEqual(x, 0.865474));
+ assert(isClose(x, 0.865474));
}
@system unittest
{
// @system because of __gshared stderr
+ import std.stdio;
scope(failure) stderr.writeln("Failure testing secantMethod");
float f(float x)
{
return cos(x) - x*x*x;
}
immutable x = secantMethod!(f)(0f, 1f);
- assert(approxEqual(x, 0.865474));
+ assert(isClose(x, 0.865474));
auto d = &f;
immutable y = secantMethod!(d)(0f, 1f);
- assert(approxEqual(y, 0.865474));
+ assert(isClose(y, 0.865474));
}
@@ -799,7 +1096,7 @@ public:
* www.netlib.org,www.netlib.org) as algorithm TOMS478.
*
*/
-T findRoot(T, DF, DT)(scope DF f, in T a, in T b,
+T findRoot(T, DF, DT)(scope DF f, const T a, const T b,
scope DT tolerance) //= (T a, T b) => false)
if (
isFloatingPoint!T &&
@@ -819,7 +1116,7 @@ if (
}
///ditto
-T findRoot(T, DF)(scope DF f, in T a, in T b)
+T findRoot(T, DF)(scope DF f, const T a, const T b)
{
return findRoot(f, a, b, (T a, T b) => false);
}
@@ -837,16 +1134,16 @@ T findRoot(T, DF)(scope DF f, in T a, in T b)
* bx = Right bound of initial range of `f` known to contain the
* root.
*
- * fax = Value of $(D f(ax)).
+ * fax = Value of `f(ax)`.
*
- * fbx = Value of $(D f(bx)). $(D fax) and $(D fbx) should have opposite signs.
- * ($(D f(ax)) and $(D f(bx)) are commonly known in advance.)
+ * fbx = Value of `f(bx)`. `fax` and `fbx` should have opposite signs.
+ * (`f(ax)` and `f(bx)` are commonly known in advance.)
*
*
* tolerance = Defines an early termination condition. Receives the
* current upper and lower bounds on the root. The
- * delegate must return $(D true) when these bounds are
- * acceptable. If this function always returns $(D false),
+ * delegate must return `true` when these bounds are
+ * acceptable. If this function always returns `false`,
* full machine precision will be achieved.
*
* Returns:
@@ -857,7 +1154,8 @@ T findRoot(T, DF)(scope DF f, in T a, in T b)
* root was found, both of the first two elements will contain the
* root, and the second pair of elements will be 0.
*/
-Tuple!(T, T, R, R) findRoot(T, R, DF, DT)(scope DF f, in T ax, in T bx, in R fax, in R fbx,
+Tuple!(T, T, R, R) findRoot(T, R, DF, DT)(scope DF f,
+ const T ax, const T bx, const R fax, const R fbx,
scope DT tolerance) // = (T a, T b) => false)
if (
isFloatingPoint!T &&
@@ -869,7 +1167,7 @@ in
assert(!ax.isNaN() && !bx.isNaN(), "Limits must not be NaN");
assert(signbit(fax) != signbit(fbx), "Parameters must bracket the root.");
}
-body
+do
{
// Author: Don Clugston. This code is (heavily) modified from TOMS748
// (www.netlib.org). The changes to improve the worst-cast performance are
@@ -1164,13 +1462,14 @@ whileloop:
}
///ditto
-Tuple!(T, T, R, R) findRoot(T, R, DF)(scope DF f, in T ax, in T bx, in R fax, in R fbx)
+Tuple!(T, T, R, R) findRoot(T, R, DF)(scope DF f,
+ const T ax, const T bx, const R fax, const R fbx)
{
return findRoot(f, ax, bx, fax, fbx, (T a, T b) => false);
}
///ditto
-T findRoot(T, R)(scope R delegate(T) f, in T a, in T b,
+T findRoot(T, R)(scope R delegate(T) f, const T a, const T b,
scope bool delegate(T lo, T hi) tolerance = (T a, T b) => false)
{
return findRoot!(T, R delegate(T), bool delegate(T lo, T hi))(f, a, b, tolerance);
@@ -1186,7 +1485,7 @@ T findRoot(T, R)(scope R delegate(T) f, in T a, in T b,
//numCalls=0;
//++numProblems;
assert(!x1.isNaN() && !x2.isNaN());
- assert(signbit(x1) != signbit(x2));
+ assert(signbit(f(x1)) != signbit(f(x2)));
auto result = findRoot(f, x1, x2, f(x1), f(x2),
(real lo, real hi) { return false; });
@@ -1204,16 +1503,16 @@ T findRoot(T, R)(scope R delegate(T) f, in T a, in T b,
//++numCalls;
if (x>float.max)
x = float.max;
- if (x<-double.max)
- x = -double.max;
+ if (x<-float.max)
+ x = -float.max;
// This has a single real root at -59.286543284815
return 0.386*x*x*x + 23*x*x + 15.7*x + 525.2;
}
// Test a function with more than one root.
real multisine(real x) { ++numCalls; return sin(x); }
- //testFindRoot( &multisine, 6, 90);
- //testFindRoot(&cubicfn, -100, 100);
- //testFindRoot( &cubicfn, -double.max, real.max);
+ testFindRoot( &multisine, 6, 90);
+ testFindRoot(&cubicfn, -100, 100);
+ testFindRoot( &cubicfn, -double.max, real.max);
/* Tests from the paper:
@@ -1246,7 +1545,7 @@ T findRoot(T, R)(scope R delegate(T) f, in T a, in T b,
foreach (k; power_nvals)
{
n = k;
- //testFindRoot(&power, -1, 10);
+ testFindRoot(&power, -1, 10);
}
int powerProblems = numProblems;
@@ -1307,17 +1606,17 @@ T findRoot(T, R)(scope R delegate(T) f, in T a, in T b,
}
numProblems=0;
- //testFindRoot(&alefeld0, PI_2, PI);
+ testFindRoot(&alefeld0, PI_2, PI);
for (n=1; n <= 10; ++n)
{
- //testFindRoot(&alefeld0, n*n+1e-9L, (n+1)*(n+1)-1e-9L);
+ testFindRoot(&alefeld0, n*n+1e-9L, (n+1)*(n+1)-1e-9L);
}
ale_a = -40; ale_b = -1;
- //testFindRoot(&alefeld1, -9, 31);
+ testFindRoot(&alefeld1, -9, 31);
ale_a = -100; ale_b = -2;
- //testFindRoot(&alefeld1, -9, 31);
+ testFindRoot(&alefeld1, -9, 31);
ale_a = -200; ale_b = -3;
- //testFindRoot(&alefeld1, -9, 31);
+ testFindRoot(&alefeld1, -9, 31);
int [] nvals_3 = [1, 2, 5, 10, 15, 20];
int [] nvals_5 = [1, 2, 4, 5, 8, 15, 20];
int [] nvals_6 = [1, 5, 10, 15, 20];
@@ -1325,48 +1624,48 @@ T findRoot(T, R)(scope R delegate(T) f, in T a, in T b,
for (int i=4; i<12; i+=2)
{
- n = i;
- ale_a = 0.2;
- //testFindRoot(&alefeld2, 0, 5);
- ale_a=1;
- //testFindRoot(&alefeld2, 0.95, 4.05);
- //testFindRoot(&alefeld2, 0, 1.5);
+ n = i;
+ ale_a = 0.2;
+ testFindRoot(&alefeld2, 0, 5);
+ ale_a=1;
+ testFindRoot(&alefeld2, 0.95, 4.05);
+ testFindRoot(&alefeld2, 0, 1.5);
}
foreach (i; nvals_3)
{
n=i;
- //testFindRoot(&alefeld3, 0, 1);
+ testFindRoot(&alefeld3, 0, 1);
}
foreach (i; nvals_3)
{
n=i;
- //testFindRoot(&alefeld4, 0, 1);
+ testFindRoot(&alefeld4, 0, 1);
}
foreach (i; nvals_5)
{
n=i;
- //testFindRoot(&alefeld5, 0, 1);
+ testFindRoot(&alefeld5, 0, 1);
}
foreach (i; nvals_6)
{
n=i;
- //testFindRoot(&alefeld6, 0, 1);
+ testFindRoot(&alefeld6, 0, 1);
}
foreach (i; nvals_7)
{
n=i;
- //testFindRoot(&alefeld7, 0.01L, 1);
+ testFindRoot(&alefeld7, 0.01L, 1);
}
real worstcase(real x)
{
++numCalls;
return x<0.3*real.max? -0.999e-3 : 1.0;
}
- //testFindRoot(&worstcase, -real.max, real.max);
+ testFindRoot(&worstcase, -real.max, real.max);
// just check that the double + float cases compile
- //findRoot((double x){ return 0.0; }, -double.max, double.max);
- //findRoot((float x){ return 0.0f; }, -float.max, float.max);
+ findRoot((double x){ return 0.0; }, -double.max, double.max);
+ findRoot((float x){ return 0.0f; }, -float.max, float.max);
/*
int grandtotal=0;
@@ -1381,7 +1680,7 @@ T findRoot(T, R)(scope R delegate(T) f, in T a, in T b,
printf("POWER TOTAL = %d avg = %f ", powercalls,
(1.0*powercalls)/powerProblems);
*/
- //Issue 14231
+ // https://issues.dlang.org/show_bug.cgi?id=14231
auto xp = findRoot((float x) => x, 0f, 1f);
auto xn = findRoot((float x) => x, -1f, -0f);
}
@@ -1413,8 +1712,8 @@ Params:
Preconditions:
`ax` and `bx` shall be finite reals. $(BR)
- $(D relTolerance) shall be normal positive real. $(BR)
- $(D absTolerance) shall be normal positive real no less then $(D T.epsilon*2).
+ `relTolerance` shall be normal positive real. $(BR)
+ `absTolerance` shall be normal positive real no less then `T.epsilon*2`.
Returns:
A tuple consisting of `x`, `y = f(x)` and `error = 3 * (absTolerance * fabs(x) + relTolerance)`.
@@ -1431,10 +1730,10 @@ See_Also: $(LREF findRoot), $(REF isNormal, std,math)
Tuple!(T, "x", Unqual!(ReturnType!DF), "y", T, "error")
findLocalMin(T, DF)(
scope DF f,
- in T ax,
- in T bx,
- in T relTolerance = sqrt(T.epsilon),
- in T absTolerance = sqrt(T.epsilon),
+ const T ax,
+ const T bx,
+ const T relTolerance = sqrt(T.epsilon),
+ const T absTolerance = sqrt(T.epsilon),
)
if (isFloatingPoint!T
&& __traits(compiles, {T _ = DF.init(T.init);}))
@@ -1451,7 +1750,7 @@ out (result)
{
assert(isFinite(result.x));
}
-body
+do
{
alias R = Unqual!(CommonType!(ReturnType!DF, T));
// c is the squared inverse of the golden ratio
@@ -1553,14 +1852,14 @@ body
// update a, b, v, w, and x
if (fu <= fx)
{
- u < x ? b : a = x;
+ (u < x ? b : a) = x;
v = w; fv = fw;
w = x; fw = fx;
x = u; fx = fu;
}
else
{
- u < x ? a : b = u;
+ (u < x ? a : b) = u;
if (fu <= fw || w == x)
{
v = w; fv = fw;
@@ -1578,27 +1877,27 @@ body
///
@safe unittest
{
- import std.math : approxEqual;
+ import std.math.operations : isClose;
auto ret = findLocalMin((double x) => (x-4)^^2, -1e7, 1e7);
- assert(ret.x.approxEqual(4.0));
- assert(ret.y.approxEqual(0.0));
+ assert(ret.x.isClose(4.0));
+ assert(ret.y.isClose(0.0, 0.0, 1e-10));
}
@safe unittest
{
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(double, float, real))
+ static foreach (T; AliasSeq!(double, float, real))
{
{
auto ret = findLocalMin!T((T x) => (x-4)^^2, T.min_normal, 1e7);
- assert(ret.x.approxEqual(T(4)));
- assert(ret.y.approxEqual(T(0)));
+ assert(ret.x.isClose(T(4)));
+ assert(ret.y.isClose(T(0), 0.0, T.epsilon));
}
{
auto ret = findLocalMin!T((T x) => fabs(x-1), -T.max/4, T.max/4, T.min_normal, 2*T.epsilon);
- assert(approxEqual(ret.x, T(1)));
- assert(approxEqual(ret.y, T(0)));
+ assert(isClose(ret.x, T(1)));
+ assert(isClose(ret.y, T(0), 0.0, T.epsilon));
assert(ret.error <= 10 * T.epsilon);
}
{
@@ -1620,19 +1919,19 @@ body
}
{
auto ret = findLocalMin!T((T x) => -fabs(x), -1, 1, T.min_normal, 2*T.epsilon);
- assert(ret.x.fabs.approxEqual(T(1)));
- assert(ret.y.fabs.approxEqual(T(1)));
- assert(ret.error.approxEqual(T(0)));
+ assert(ret.x.fabs.isClose(T(1)));
+ assert(ret.y.fabs.isClose(T(1)));
+ assert(ret.error.isClose(T(0), 0.0, 100*T.epsilon));
}
}
}
/**
Computes $(LINK2 https://en.wikipedia.org/wiki/Euclidean_distance,
-Euclidean distance) between input ranges $(D a) and
-$(D b). The two ranges must have the same length. The three-parameter
+Euclidean distance) between input ranges `a` and
+`b`. The two ranges must have the same length. The three-parameter
version stops computation as soon as the distance is greater than or
-equal to $(D limit) (this is useful to save computation if a small
+equal to `limit` (this is useful to save computation if a small
distance is sought).
*/
CommonType!(ElementType!(Range1), ElementType!(Range2))
@@ -1677,20 +1976,21 @@ if (isInputRange!(Range1) && isInputRange!(Range2))
@safe unittest
{
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(double, const double, immutable double))
- {
+ static foreach (T; AliasSeq!(double, const double, immutable double))
+ {{
T[] a = [ 1.0, 2.0, ];
T[] b = [ 4.0, 6.0, ];
assert(euclideanDistance(a, b) == 5);
+ assert(euclideanDistance(a, b, 6) == 5);
assert(euclideanDistance(a, b, 5) == 5);
assert(euclideanDistance(a, b, 4) == 5);
assert(euclideanDistance(a, b, 2) == 3);
- }
+ }}
}
/**
Computes the $(LINK2 https://en.wikipedia.org/wiki/Dot_product,
-dot product) of input ranges $(D a) and $(D
+dot product) of input ranges `a` and $(D
b). The two ranges must have the same length. If both ranges define
length, the check is done once; otherwise, it is done at each
iteration.
@@ -1765,30 +2065,55 @@ dotProduct(F1, F2)(in F1[] avector, in F2[] bvector)
return sum0;
}
+/// ditto
+F dotProduct(F, uint N)(const ref scope F[N] a, const ref scope F[N] b)
+if (N <= 16)
+{
+ F sum0 = 0;
+ F sum1 = 0;
+ static foreach (i; 0 .. N / 2)
+ {
+ sum0 += a[i*2] * b[i*2];
+ sum1 += a[i*2+1] * b[i*2+1];
+ }
+ static if (N % 2 == 1)
+ {
+ sum0 += a[N-1] * b[N-1];
+ }
+ return sum0 + sum1;
+}
+
@system unittest
{
// @system due to dotProduct and assertCTFEable
import std.exception : assertCTFEable;
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(double, const double, immutable double))
- {
+ static foreach (T; AliasSeq!(double, const double, immutable double))
+ {{
T[] a = [ 1.0, 2.0, ];
T[] b = [ 4.0, 6.0, ];
assert(dotProduct(a, b) == 16);
assert(dotProduct([1, 3, -5], [4, -2, -1]) == 3);
- }
+ // Test with fixed-length arrays.
+ T[2] c = [ 1.0, 2.0, ];
+ T[2] d = [ 4.0, 6.0, ];
+ assert(dotProduct(c, d) == 16);
+ T[3] e = [1, 3, -5];
+ T[3] f = [4, -2, -1];
+ assert(dotProduct(e, f) == 3);
+ }}
// Make sure the unrolled loop codepath gets tested.
static const x =
- [1.0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
+ [1.0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22];
static const y =
- [2.0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
- assertCTFEable!({ assert(dotProduct(x, y) == 2280); });
+ [2.0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23];
+ assertCTFEable!({ assert(dotProduct(x, y) == 4048); });
}
/**
Computes the $(LINK2 https://en.wikipedia.org/wiki/Cosine_similarity,
-cosine similarity) of input ranges $(D a) and $(D
+cosine similarity) of input ranges `a` and $(D
b). The two ranges must have the same length. If both ranges define
length, the check is done once; otherwise, it is done at each
iteration. If either range has all-zero elements, return 0.
@@ -1815,25 +2140,25 @@ if (isInputRange!(Range1) && isInputRange!(Range2))
@safe unittest
{
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(double, const double, immutable double))
- {
+ static foreach (T; AliasSeq!(double, const double, immutable double))
+ {{
T[] a = [ 1.0, 2.0, ];
T[] b = [ 4.0, 3.0, ];
- assert(approxEqual(
+ assert(isClose(
cosineSimilarity(a, b), 10.0 / sqrt(5.0 * 25),
0.01));
- }
+ }}
}
/**
-Normalizes values in $(D range) by multiplying each element with a
-number chosen such that values sum up to $(D sum). If elements in $(D
+Normalizes values in `range` by multiplying each element with a
+number chosen such that values sum up to `sum`. If elements in $(D
range) sum to zero, assigns $(D sum / range.length) to
-all. Normalization makes sense only if all elements in $(D range) are
-positive. $(D normalize) assumes that is the case without checking it.
+all. Normalization makes sense only if all elements in `range` are
+positive. `normalize` assumes that is the case without checking it.
-Returns: $(D true) if normalization completed normally, $(D false) if
-all elements in $(D range) were zero or if $(D range) is empty.
+Returns: `true` if normalization completed normally, `false` if
+all elements in `range` were zero or if `range` is empty.
*/
bool normalize(R)(R range, ElementType!(R) sum = 1)
if (isForwardRange!(R))
@@ -1883,13 +2208,14 @@ if (isForwardRange!(R))
a = [ 1.0, 3.0 ];
assert(normalize(a));
assert(a == [ 0.25, 0.75 ]);
+ assert(normalize!(typeof(a))(a, 50)); // a = [12.5, 37.5]
a = [ 0.0, 0.0 ];
assert(!normalize(a));
assert(a == [ 0.5, 0.5 ]);
}
/**
-Compute the sum of binary logarithms of the input range $(D r).
+Compute the sum of binary logarithms of the input range `r`.
The error of this method is much smaller than with a naive sum of log2.
*/
ElementType!Range sumOfLog2s(Range)(Range r)
@@ -1916,7 +2242,7 @@ if (isInputRange!Range && isFloatingPoint!(ElementType!Range))
///
@safe unittest
{
- import std.math : isNaN;
+ import std.math.traits : isNaN;
assert(sumOfLog2s(new double[0]) == 0);
assert(sumOfLog2s([0.0L]) == -real.infinity);
@@ -1932,12 +2258,12 @@ if (isInputRange!Range && isFloatingPoint!(ElementType!Range))
/**
Computes $(LINK2 https://en.wikipedia.org/wiki/Entropy_(information_theory),
-_entropy) of input range $(D r) in bits. This
-function assumes (without checking) that the values in $(D r) are all
-in $(D [0, 1]). For the entropy to be meaningful, often $(D r) should
+_entropy) of input range `r` in bits. This
+function assumes (without checking) that the values in `r` are all
+in $(D [0, 1]). For the entropy to be meaningful, often `r` should
be normalized too (i.e., its values should sum to 1). The
two-parameter version stops evaluating as soon as the intermediate
-result is greater than or equal to $(D max).
+result is greater than or equal to `max`.
*/
ElementType!Range entropy(Range)(Range r)
if (isInputRange!Range)
@@ -1969,25 +2295,25 @@ if (isInputRange!Range &&
@safe unittest
{
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(double, const double, immutable double))
- {
+ static foreach (T; AliasSeq!(double, const double, immutable double))
+ {{
T[] p = [ 0.0, 0, 0, 1 ];
assert(entropy(p) == 0);
p = [ 0.25, 0.25, 0.25, 0.25 ];
assert(entropy(p) == 2);
assert(entropy(p, 1) == 1);
- }
+ }}
}
/**
Computes the $(LINK2 https://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence,
Kullback-Leibler divergence) between input ranges
-$(D a) and $(D b), which is the sum $(D ai * log(ai / bi)). The base
+`a` and `b`, which is the sum $(D ai * log(ai / bi)). The base
of logarithm is 2. The ranges are assumed to contain elements in $(D
[0, 1]). Usually the ranges are normalized probability distributions,
but this is not required or checked by $(D
-kullbackLeiblerDivergence). If any element $(D bi) is zero and the
-corresponding element $(D ai) nonzero, returns infinity. (Otherwise,
+kullbackLeiblerDivergence). If any element `bi` is zero and the
+corresponding element `ai` nonzero, returns infinity. (Otherwise,
if $(D ai == 0 && bi == 0), the term $(D ai * log(ai / bi)) is
considered zero.) If the inputs are normalized, the result is
positive.
@@ -2015,7 +2341,7 @@ if (isInputRange!(Range1) && isInputRange!(Range2))
///
@safe unittest
{
- import std.math : approxEqual;
+ import std.math.operations : isClose;
double[] p = [ 0.0, 0, 0, 1 ];
assert(kullbackLeiblerDivergence(p, p) == 0);
@@ -2024,21 +2350,21 @@ if (isInputRange!(Range1) && isInputRange!(Range2))
assert(kullbackLeiblerDivergence(p, p1) == 2);
assert(kullbackLeiblerDivergence(p1, p) == double.infinity);
double[] p2 = [ 0.2, 0.2, 0.2, 0.4 ];
- assert(approxEqual(kullbackLeiblerDivergence(p1, p2), 0.0719281));
- assert(approxEqual(kullbackLeiblerDivergence(p2, p1), 0.0780719));
+ assert(isClose(kullbackLeiblerDivergence(p1, p2), 0.0719281, 1e-5));
+ assert(isClose(kullbackLeiblerDivergence(p2, p1), 0.0780719, 1e-5));
}
/**
Computes the $(LINK2 https://en.wikipedia.org/wiki/Jensen%E2%80%93Shannon_divergence,
-Jensen-Shannon divergence) between $(D a) and $(D
+Jensen-Shannon divergence) between `a` and $(D
b), which is the sum $(D (ai * log(2 * ai / (ai + bi)) + bi * log(2 *
bi / (ai + bi))) / 2). The base of logarithm is 2. The ranges are
assumed to contain elements in $(D [0, 1]). Usually the ranges are
normalized probability distributions, but this is not required or
-checked by $(D jensenShannonDivergence). If the inputs are normalized,
+checked by `jensenShannonDivergence`. If the inputs are normalized,
the result is bounded within $(D [0, 1]). The three-parameter version
stops evaluations as soon as the intermediate result is greater than
-or equal to $(D limit).
+or equal to `limit`.
*/
CommonType!(ElementType!Range1, ElementType!Range2)
jensenShannonDivergence(Range1, Range2)(Range1 a, Range2 b)
@@ -2099,33 +2425,33 @@ if (isInputRange!Range1 && isInputRange!Range2 &&
///
@safe unittest
{
- import std.math : approxEqual;
+ import std.math.operations : isClose;
double[] p = [ 0.0, 0, 0, 1 ];
assert(jensenShannonDivergence(p, p) == 0);
double[] p1 = [ 0.25, 0.25, 0.25, 0.25 ];
assert(jensenShannonDivergence(p1, p1) == 0);
- assert(approxEqual(jensenShannonDivergence(p1, p), 0.548795));
+ assert(isClose(jensenShannonDivergence(p1, p), 0.548795, 1e-5));
double[] p2 = [ 0.2, 0.2, 0.2, 0.4 ];
- assert(approxEqual(jensenShannonDivergence(p1, p2), 0.0186218));
- assert(approxEqual(jensenShannonDivergence(p2, p1), 0.0186218));
- assert(approxEqual(jensenShannonDivergence(p2, p1, 0.005), 0.00602366));
+ assert(isClose(jensenShannonDivergence(p1, p2), 0.0186218, 1e-5));
+ assert(isClose(jensenShannonDivergence(p2, p1), 0.0186218, 1e-5));
+ assert(isClose(jensenShannonDivergence(p2, p1, 0.005), 0.00602366, 1e-5));
}
/**
The so-called "all-lengths gap-weighted string kernel" computes a
-similarity measure between $(D s) and $(D t) based on all of their
+similarity measure between `s` and `t` based on all of their
common subsequences of all lengths. Gapped subsequences are also
included.
To understand what $(D gapWeightedSimilarity(s, t, lambda)) computes,
consider first the case $(D lambda = 1) and the strings $(D s =
["Hello", "brave", "new", "world"]) and $(D t = ["Hello", "new",
-"world"]). In that case, $(D gapWeightedSimilarity) counts the
+"world"]). In that case, `gapWeightedSimilarity` counts the
following matches:
-$(OL $(LI three matches of length 1, namely $(D "Hello"), $(D "new"),
-and $(D "world");) $(LI three matches of length 2, namely ($(D
+$(OL $(LI three matches of length 1, namely `"Hello"`, `"new"`,
+and `"world"`;) $(LI three matches of length 2, namely ($(D
"Hello", "new")), ($(D "Hello", "world")), and ($(D "new", "world"));)
$(LI one match of length 3, namely ($(D "Hello", "new", "world")).))
@@ -2156,12 +2482,12 @@ tally. That leaves only 4 matches.
The most interesting case is when gapped matches still participate in
the result, but not as strongly as ungapped matches. The result will
be a smooth, fine-grained similarity measure between the input
-strings. This is where values of $(D lambda) between 0 and 1 enter
+strings. This is where values of `lambda` between 0 and 1 enter
into play: gapped matches are $(I exponentially penalized with the
-number of gaps) with base $(D lambda). This means that an ungapped
+number of gaps) with base `lambda`. This means that an ungapped
match adds 1 to the return value; a match with one gap in either
-string adds $(D lambda) to the return value; ...; a match with a total
-of $(D n) gaps in both strings adds $(D pow(lambda, n)) to the return
+string adds `lambda` to the return value; ...; a match with a total
+of `n` gaps in both strings adds $(D pow(lambda, n)) to the return
value. In the example above, we have 4 matches without gaps, 2 matches
with one gap, and 1 match with three gaps. The latter match is ($(D
"Hello", "world")), which has two gaps in the first string and one gap
@@ -2174,11 +2500,11 @@ string[] t = ["Hello", "new", "world"];
assert(gapWeightedSimilarity(s, t, 0.5) == 4 + 0.5 * 2 + 0.125);
----
-$(D gapWeightedSimilarity) is useful wherever a smooth similarity
+`gapWeightedSimilarity` is useful wherever a smooth similarity
measure between sequences allowing for approximate matches is
needed. The examples above are given with words, but any sequences
with elements comparable for equality are allowed, e.g. characters or
-numbers. $(D gapWeightedSimilarity) uses a highly optimized dynamic
+numbers. `gapWeightedSimilarity` uses a highly optimized dynamic
programming implementation that needs $(D 16 * min(s.length,
t.length)) extra bytes of memory and $(BIGOH s.length * t.length) time
to complete.
@@ -2242,25 +2568,25 @@ if (isRandomAccessRange!(R1) && hasLength!(R1) &&
}
/**
-The similarity per $(D gapWeightedSimilarity) has an issue in that it
+The similarity per `gapWeightedSimilarity` has an issue in that it
grows with the lengths of the two strings, even though the strings are
not actually very similar. For example, the range $(D ["Hello",
"world"]) is increasingly similar with the range $(D ["Hello",
-"world", "world", "world",...]) as more instances of $(D "world") are
-appended. To prevent that, $(D gapWeightedSimilarityNormalized)
+"world", "world", "world",...]) as more instances of `"world"` are
+appended. To prevent that, `gapWeightedSimilarityNormalized`
computes a normalized version of the similarity that is computed as
$(D gapWeightedSimilarity(s, t, lambda) /
sqrt(gapWeightedSimilarity(s, t, lambda) * gapWeightedSimilarity(s, t,
-lambda))). The function $(D gapWeightedSimilarityNormalized) (a
-so-called normalized kernel) is bounded in $(D [0, 1]), reaches $(D 0)
-only for ranges that don't match in any position, and $(D 1) only for
+lambda))). The function `gapWeightedSimilarityNormalized` (a
+so-called normalized kernel) is bounded in $(D [0, 1]), reaches `0`
+only for ranges that don't match in any position, and `1` only for
identical ranges.
-The optional parameters $(D sSelfSim) and $(D tSelfSim) are meant for
+The optional parameters `sSelfSim` and `tSelfSim` are meant for
avoiding duplicate computation. Many applications may have already
computed $(D gapWeightedSimilarity(s, s, lambda)) and/or $(D
gapWeightedSimilarity(t, t, lambda)). In that case, they can be passed
-as $(D sSelfSim) and $(D tSelfSim), respectively.
+as `sSelfSim` and `tSelfSim`, respectively.
*/
Select!(isFloatingPoint!(F), F, double)
gapWeightedSimilarityNormalized(alias comp = "a == b", R1, R2, F)
@@ -2289,19 +2615,20 @@ if (isRandomAccessRange!(R1) && hasLength!(R1) &&
///
@system unittest
{
- import std.math : approxEqual, sqrt;
+ import std.math.operations : isClose;
+ import std.math.algebraic : sqrt;
string[] s = ["Hello", "brave", "new", "world"];
string[] t = ["Hello", "new", "world"];
assert(gapWeightedSimilarity(s, s, 1) == 15);
assert(gapWeightedSimilarity(t, t, 1) == 7);
assert(gapWeightedSimilarity(s, t, 1) == 7);
- assert(approxEqual(gapWeightedSimilarityNormalized(s, t, 1),
+ assert(isClose(gapWeightedSimilarityNormalized(s, t, 1),
7.0 / sqrt(15.0 * 7), 0.01));
}
/**
-Similar to $(D gapWeightedSimilarity), just works in an incremental
+Similar to `gapWeightedSimilarity`, just works in an incremental
manner by first revealing the matches of length 1, then gapped matches
of length 2, and so on. The memory requirement is $(BIGOH s.length *
t.length). The time complexity is $(BIGOH s.length * t.length) time
@@ -2327,8 +2654,8 @@ private:
public:
/**
-Constructs an object given two ranges $(D s) and $(D t) and a penalty
-$(D lambda). Constructor completes in $(BIGOH s.length * t.length)
+Constructs an object given two ranges `s` and `t` and a penalty
+`lambda`. Constructor completes in $(BIGOH s.length * t.length)
time and computes all matches of length 1.
*/
this(Range s, Range t, F lambda)
@@ -2391,7 +2718,7 @@ time and computes all matches of length 1.
}
/**
- Returns: $(D this).
+ Returns: `this`.
*/
ref GapWeightedSimilarityIncremental opSlice()
{
@@ -2484,7 +2811,7 @@ time and computes all matches of length 1.
/**
Returns: The gapped similarity at the current match length (initially
- 1, grows with each call to $(D popFront)).
+ 1, grows with each call to `popFront`).
*/
@property F front() { return currentValue; }
@@ -2598,61 +2925,63 @@ GapWeightedSimilarityIncremental!(R, F) gapWeightedSimilarityIncremental(R, F)
}
/**
-Computes the greatest common divisor of $(D a) and $(D b) by using
+Computes the greatest common divisor of `a` and `b` by using
an efficient algorithm such as $(HTTPS en.wikipedia.org/wiki/Euclidean_algorithm, Euclid's)
or $(HTTPS en.wikipedia.org/wiki/Binary_GCD_algorithm, Stein's) algorithm.
Params:
- T = Any numerical type that supports the modulo operator `%`. If
- bit-shifting `<<` and `>>` are also supported, Stein's algorithm will
+ a = Integer value of any numerical type that supports the modulo operator `%`.
+ If bit-shifting `<<` and `>>` are also supported, Stein's algorithm will
be used; otherwise, Euclid's algorithm is used as _a fallback.
+ b = Integer value of any equivalent numerical type.
+
Returns:
The greatest common divisor of the given arguments.
*/
-T gcd(T)(T a, T b)
- if (isIntegral!T)
+typeof(Unqual!(T).init % Unqual!(U).init) gcd(T, U)(T a, U b)
+if (isIntegral!T && isIntegral!U)
{
- static if (is(T == const) || is(T == immutable))
- {
- return gcd!(Unqual!T)(a, b);
- }
- else version (DigitalMars)
- {
- static if (T.min < 0)
- {
- assert(a >= 0 && b >= 0);
- }
- while (b)
- {
- immutable t = b;
- b = a % b;
- a = t;
- }
- return a;
- }
+ // Operate on a common type between the two arguments.
+ alias UCT = Unsigned!(CommonType!(Unqual!T, Unqual!U));
+
+ // `std.math.abs` doesn't support unsigned integers, and `T.min` is undefined.
+ static if (is(T : immutable short) || is(T : immutable byte))
+ UCT ax = (isUnsigned!T || a >= 0) ? a : cast(UCT) -int(a);
else
- {
- if (a == 0)
- return b;
- if (b == 0)
- return a;
+ UCT ax = (isUnsigned!T || a >= 0) ? a : -UCT(a);
- import core.bitop : bsf;
- import std.algorithm.mutation : swap;
+ static if (is(U : immutable short) || is(U : immutable byte))
+ UCT bx = (isUnsigned!U || b >= 0) ? b : cast(UCT) -int(b);
+ else
+ UCT bx = (isUnsigned!U || b >= 0) ? b : -UCT(b);
- immutable uint shift = bsf(a | b);
- a >>= a.bsf;
+ // Special cases.
+ if (ax == 0)
+ return bx;
+ if (bx == 0)
+ return ax;
- do
- {
- b >>= b.bsf;
- if (a > b)
- swap(a, b);
- b -= a;
- } while (b);
+ return gcdImpl(ax, bx);
+}
- return a << shift;
- }
+private typeof(T.init % T.init) gcdImpl(T)(T a, T b)
+if (isIntegral!T)
+{
+ pragma(inline, true);
+ import core.bitop : bsf;
+ import std.algorithm.mutation : swap;
+
+ immutable uint shift = bsf(a | b);
+ a >>= a.bsf;
+ do
+ {
+ b >>= b.bsf;
+ if (a > b)
+ swap(a, b);
+ b -= a;
+ } while (b);
+
+ return a << shift;
}
///
@@ -2663,16 +2992,114 @@ T gcd(T)(T a, T b)
assert(gcd(a, b) == 13);
}
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong,
+ const byte, const short, const int, const long,
+ immutable ubyte, immutable ushort, immutable uint, immutable ulong))
+ {
+ static foreach (U; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong,
+ const ubyte, const ushort, const uint, const ulong,
+ immutable byte, immutable short, immutable int, immutable long))
+ {
+ // Signed and unsigned tests.
+ static if (T.max > byte.max && U.max > byte.max)
+ assert(gcd(T(200), U(200)) == 200);
+ static if (T.max > ubyte.max)
+ {
+ assert(gcd(T(2000), U(20)) == 20);
+ assert(gcd(T(2011), U(17)) == 1);
+ }
+ static if (T.max > ubyte.max && U.max > ubyte.max)
+ assert(gcd(T(1071), U(462)) == 21);
+
+ assert(gcd(T(0), U(13)) == 13);
+ assert(gcd(T(29), U(0)) == 29);
+ assert(gcd(T(0), U(0)) == 0);
+ assert(gcd(T(1), U(2)) == 1);
+ assert(gcd(T(9), U(6)) == 3);
+ assert(gcd(T(3), U(4)) == 1);
+ assert(gcd(T(32), U(24)) == 8);
+ assert(gcd(T(5), U(6)) == 1);
+ assert(gcd(T(54), U(36)) == 18);
+
+ // Int and Long tests.
+ static if (T.max > short.max && U.max > short.max)
+ assert(gcd(T(46391), U(62527)) == 2017);
+ static if (T.max > ushort.max && U.max > ushort.max)
+ assert(gcd(T(63245986), U(39088169)) == 1);
+ static if (T.max > uint.max && U.max > uint.max)
+ {
+ assert(gcd(T(77160074263), U(47687519812)) == 1);
+ assert(gcd(T(77160074264), U(47687519812)) == 4);
+ }
+
+ // Negative tests.
+ static if (T.min < 0)
+ {
+ assert(gcd(T(-21), U(28)) == 7);
+ assert(gcd(T(-3), U(4)) == 1);
+ }
+ static if (U.min < 0)
+ {
+ assert(gcd(T(1), U(-2)) == 1);
+ assert(gcd(T(33), U(-44)) == 11);
+ }
+ static if (T.min < 0 && U.min < 0)
+ {
+ assert(gcd(T(-5), U(-6)) == 1);
+ assert(gcd(T(-50), U(-60)) == 10);
+ }
+ }
+ }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21834
+@safe unittest
+{
+ assert(gcd(-120, 10U) == 10);
+ assert(gcd(120U, -10) == 10);
+ assert(gcd(int.min, 0L) == 1L + int.max);
+ assert(gcd(0L, int.min) == 1L + int.max);
+ assert(gcd(int.min, 0L + int.min) == 1L + int.max);
+ assert(gcd(int.min, 1L + int.max) == 1L + int.max);
+ assert(gcd(short.min, 1U + short.max) == 1U + short.max);
+}
+
// This overload is for non-builtin numerical types like BigInt or
// user-defined types.
/// ditto
-T gcd(T)(T a, T b)
- if (!isIntegral!T &&
+auto gcd(T)(T a, T b)
+if (!isIntegral!T &&
is(typeof(T.init % T.init)) &&
is(typeof(T.init == 0 || T.init > 0)))
{
- import std.algorithm.mutation : swap;
+ static if (!is(T == Unqual!T))
+ {
+ return gcd!(Unqual!T)(a, b);
+ }
+ else
+ {
+ // Ensure arguments are unsigned.
+ a = a >= 0 ? a : -a;
+ b = b >= 0 ? b : -b;
+
+ // Special cases.
+ if (a == 0)
+ return b;
+ if (b == 0)
+ return a;
+
+ return gcdImpl(a, b);
+ }
+}
+private auto gcdImpl(T)(T a, T b)
+if (!isIntegral!T)
+{
+ pragma(inline, true);
+ import std.algorithm.mutation : swap;
enum canUseBinaryGcd = is(typeof(() {
T t, u;
t <<= 1;
@@ -2682,8 +3109,6 @@ T gcd(T)(T a, T b)
swap(t, u);
}));
- assert(a >= 0 && b >= 0);
-
static if (canUseBinaryGcd)
{
uint shift = 0;
@@ -2694,6 +3119,8 @@ T gcd(T)(T a, T b)
shift++;
}
+ if ((a & 1) == 0) swap(a, b);
+
do
{
assert((a & 1) != 0);
@@ -2719,13 +3146,16 @@ T gcd(T)(T a, T b)
}
}
-// Issue 7102
+// https://issues.dlang.org/show_bug.cgi?id=7102
@system pure unittest
{
import std.bigint : BigInt;
assert(gcd(BigInt("71_000_000_000_000_000_000"),
BigInt("31_000_000_000_000_000_000")) ==
BigInt("1_000_000_000_000_000_000"));
+
+ assert(gcd(BigInt(0), BigInt(1234567)) == BigInt(1234567));
+ assert(gcd(BigInt(1234567), BigInt(0)) == BigInt(1234567));
}
@safe pure nothrow unittest
@@ -2739,16 +3169,149 @@ T gcd(T)(T a, T b)
{
return CrippledInt(impl % i.impl);
}
+ CrippledInt opUnary(string op : "-")()
+ {
+ return CrippledInt(-impl);
+ }
int opEquals(CrippledInt i) { return impl == i.impl; }
int opEquals(int i) { return impl == i; }
int opCmp(int i) { return (impl < i) ? -1 : (impl > i) ? 1 : 0; }
}
assert(gcd(CrippledInt(2310), CrippledInt(1309)) == CrippledInt(77));
+ assert(gcd(CrippledInt(-120), CrippledInt(10U)) == CrippledInt(10));
+ assert(gcd(CrippledInt(120U), CrippledInt(-10)) == CrippledInt(10));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19514
+@system pure unittest
+{
+ import std.bigint : BigInt;
+ assert(gcd(BigInt(2), BigInt(1)) == BigInt(1));
+}
+
+// Issue 20924
+@safe unittest
+{
+ import std.bigint : BigInt;
+ const a = BigInt("123143238472389492934020");
+ const b = BigInt("902380489324729338420924");
+ assert(__traits(compiles, gcd(a, b)));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21834
+@safe unittest
+{
+ import std.bigint : BigInt;
+ assert(gcd(BigInt(-120), BigInt(10U)) == BigInt(10));
+ assert(gcd(BigInt(120U), BigInt(-10)) == BigInt(10));
+ assert(gcd(BigInt(int.min), BigInt(0L)) == BigInt(1L + int.max));
+ assert(gcd(BigInt(0L), BigInt(int.min)) == BigInt(1L + int.max));
+ assert(gcd(BigInt(int.min), BigInt(0L + int.min)) == BigInt(1L + int.max));
+ assert(gcd(BigInt(int.min), BigInt(1L + int.max)) == BigInt(1L + int.max));
+ assert(gcd(BigInt(short.min), BigInt(1U + short.max)) == BigInt(1U + short.max));
+}
+
+
+/**
+Computes the least common multiple of `a` and `b`.
+Arguments are the same as $(MYREF gcd).
+
+Returns:
+ The least common multiple of the given arguments.
+ */
+typeof(Unqual!(T).init % Unqual!(U).init) lcm(T, U)(T a, U b)
+if (isIntegral!T && isIntegral!U)
+{
+ // Operate on a common type between the two arguments.
+ alias UCT = Unsigned!(CommonType!(Unqual!T, Unqual!U));
+
+ // `std.math.abs` doesn't support unsigned integers, and `T.min` is undefined.
+ static if (is(T : immutable short) || is(T : immutable byte))
+ UCT ax = (isUnsigned!T || a >= 0) ? a : cast(UCT) -int(a);
+ else
+ UCT ax = (isUnsigned!T || a >= 0) ? a : -UCT(a);
+
+ static if (is(U : immutable short) || is(U : immutable byte))
+ UCT bx = (isUnsigned!U || b >= 0) ? b : cast(UCT) -int(b);
+ else
+ UCT bx = (isUnsigned!U || b >= 0) ? b : -UCT(b);
+
+ // Special cases.
+ if (ax == 0)
+ return ax;
+ if (bx == 0)
+ return bx;
+
+ return (ax / gcdImpl(ax, bx)) * bx;
+}
+
+///
+@safe unittest
+{
+ assert(lcm(1, 2) == 2);
+ assert(lcm(3, 4) == 12);
+ assert(lcm(5, 6) == 30);
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong,
+ const byte, const short, const int, const long,
+ immutable ubyte, immutable ushort, immutable uint, immutable ulong))
+ {
+ static foreach (U; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong,
+ const ubyte, const ushort, const uint, const ulong,
+ immutable byte, immutable short, immutable int, immutable long))
+ {
+ assert(lcm(T(21), U(6)) == 42);
+ assert(lcm(T(41), U(0)) == 0);
+ assert(lcm(T(0), U(7)) == 0);
+ assert(lcm(T(0), U(0)) == 0);
+ assert(lcm(T(1U), U(2)) == 2);
+ assert(lcm(T(3), U(4U)) == 12);
+ assert(lcm(T(5U), U(6U)) == 30);
+ static if (T.min < 0)
+ assert(lcm(T(-42), U(21U)) == 42);
+ }
+ }
+}
+
+/// ditto
+auto lcm(T)(T a, T b)
+if (!isIntegral!T &&
+ is(typeof(T.init % T.init)) &&
+ is(typeof(T.init == 0 || T.init > 0)))
+{
+ // Ensure arguments are unsigned.
+ a = a >= 0 ? a : -a;
+ b = b >= 0 ? b : -b;
+
+ // Special cases.
+ if (a == 0)
+ return a;
+ if (b == 0)
+ return b;
+
+ return (a / gcdImpl(a, b)) * b;
+}
+
+@safe unittest
+{
+ import std.bigint : BigInt;
+ assert(lcm(BigInt(21), BigInt(6)) == BigInt(42));
+ assert(lcm(BigInt(41), BigInt(0)) == BigInt(0));
+ assert(lcm(BigInt(0), BigInt(7)) == BigInt(0));
+ assert(lcm(BigInt(0), BigInt(0)) == BigInt(0));
+ assert(lcm(BigInt(1U), BigInt(2)) == BigInt(2));
+ assert(lcm(BigInt(3), BigInt(4U)) == BigInt(12));
+ assert(lcm(BigInt(5U), BigInt(6U)) == BigInt(30));
+ assert(lcm(BigInt(-42), BigInt(21U)) == BigInt(42));
}
// This is to make tweaking the speed/size vs. accuracy tradeoff easy,
// though floats seem accurate enough for all practical purposes, since
-// they pass the "approxEqual(inverseFft(fft(arr)), arr)" test even for
+// they pass the "isClose(inverseFft(fft(arr)), arr)" test even for
// size 2 ^^ 22.
private alias lookup_t = float;
@@ -2785,7 +3348,7 @@ private:
assert(range.length >= 4);
assert(isPowerOf2(range.length));
}
- body
+ do
{
auto recurseRange = range;
recurseRange.doubleSteps();
@@ -2819,7 +3382,7 @@ private:
assert(range.length >= 4);
assert(isPowerOf2(range.length));
}
- body
+ do
{
alias E = ElementType!R;
@@ -2927,7 +3490,7 @@ private:
{
assert(isPowerOf2(buf.length));
}
- body
+ do
{
immutable n = buf.length;
immutable localLookup = negSinLookup[bsf(n)];
@@ -2998,7 +3561,9 @@ private:
//
// Also, this is unsafe because the memSpace buffer will be cast
// to immutable.
- public this(lookup_t[] memSpace) // Public b/c of bug 4636.
+ //
+ // Public b/c of https://issues.dlang.org/show_bug.cgi?id=4636.
+ public this(lookup_t[] memSpace)
{
immutable size = memSpace.length / 2;
@@ -3056,8 +3621,8 @@ private:
}
public:
- /**Create an $(D Fft) object for computing fast Fourier transforms of
- * power of two sizes of $(D size) or smaller. $(D size) must be a
+ /**Create an `Fft` object for computing fast Fourier transforms of
+ * power of two sizes of `size` or smaller. `size` must be a
* power of two.
*/
this(size_t size)
@@ -3074,11 +3639,11 @@ public:
}
/**Compute the Fourier transform of range using the $(BIGOH N log N)
- * Cooley-Tukey Algorithm. $(D range) must be a random-access range with
- * slicing and a length equal to $(D size) as provided at the construction of
+ * Cooley-Tukey Algorithm. `range` must be a random-access range with
+ * slicing and a length equal to `size` as provided at the construction of
* this object. The contents of range can be either numeric types,
* which will be interpreted as pure real values, or complex types with
- * properties or members $(D .re) and $(D .im) that can be read.
+ * properties or members `.re` and `.im` that can be read.
*
* Note: Pure real FFTs are automatically detected and the relevant
* optimizations are performed.
@@ -3217,7 +3782,7 @@ private enum string MakeLocalFft = q{
auto fftObj = scoped!Fft(lookupBuf);
};
-/**Convenience functions that create an $(D Fft) object, run the FFT or inverse
+/**Convenience functions that create an `Fft` object, run the FFT or inverse
* FFT and return the result. Useful for one-off FFTs.
*
* Note: In addition to convenience, these functions are slightly more
@@ -3260,37 +3825,37 @@ void inverseFft(Ret, R)(R range, Ret buf)
// Test values from R and Octave.
auto arr = [1,2,3,4,5,6,7,8];
auto fft1 = fft(arr);
- assert(approxEqual(map!"a.re"(fft1),
- [36.0, -4, -4, -4, -4, -4, -4, -4]));
- assert(approxEqual(map!"a.im"(fft1),
- [0, 9.6568, 4, 1.6568, 0, -1.6568, -4, -9.6568]));
+ assert(isClose(map!"a.re"(fft1),
+ [36.0, -4, -4, -4, -4, -4, -4, -4], 1e-4));
+ assert(isClose(map!"a.im"(fft1),
+ [0, 9.6568, 4, 1.6568, 0, -1.6568, -4, -9.6568], 1e-4));
auto fft1Retro = fft(retro(arr));
- assert(approxEqual(map!"a.re"(fft1Retro),
- [36.0, 4, 4, 4, 4, 4, 4, 4]));
- assert(approxEqual(map!"a.im"(fft1Retro),
- [0, -9.6568, -4, -1.6568, 0, 1.6568, 4, 9.6568]));
+ assert(isClose(map!"a.re"(fft1Retro),
+ [36.0, 4, 4, 4, 4, 4, 4, 4], 1e-4));
+ assert(isClose(map!"a.im"(fft1Retro),
+ [0, -9.6568, -4, -1.6568, 0, 1.6568, 4, 9.6568], 1e-4));
auto fft1Float = fft(to!(float[])(arr));
- assert(approxEqual(map!"a.re"(fft1), map!"a.re"(fft1Float)));
- assert(approxEqual(map!"a.im"(fft1), map!"a.im"(fft1Float)));
+ assert(isClose(map!"a.re"(fft1), map!"a.re"(fft1Float)));
+ assert(isClose(map!"a.im"(fft1), map!"a.im"(fft1Float)));
alias C = Complex!float;
auto arr2 = [C(1,2), C(3,4), C(5,6), C(7,8), C(9,10),
C(11,12), C(13,14), C(15,16)];
auto fft2 = fft(arr2);
- assert(approxEqual(map!"a.re"(fft2),
- [64.0, -27.3137, -16, -11.3137, -8, -4.6862, 0, 11.3137]));
- assert(approxEqual(map!"a.im"(fft2),
- [72, 11.3137, 0, -4.686, -8, -11.3137, -16, -27.3137]));
+ assert(isClose(map!"a.re"(fft2),
+ [64.0, -27.3137, -16, -11.3137, -8, -4.6862, 0, 11.3137], 1e-4));
+ assert(isClose(map!"a.im"(fft2),
+ [72, 11.3137, 0, -4.686, -8, -11.3137, -16, -27.3137], 1e-4));
auto inv1 = inverseFft(fft1);
- assert(approxEqual(map!"a.re"(inv1), arr));
+ assert(isClose(map!"a.re"(inv1), arr, 1e-6));
assert(reduce!max(map!"a.im"(inv1)) < 1e-10);
auto inv2 = inverseFft(fft2);
- assert(approxEqual(map!"a.re"(inv2), map!"a.re"(arr2)));
- assert(approxEqual(map!"a.im"(inv2), map!"a.im"(arr2)));
+ assert(isClose(map!"a.re"(inv2), map!"a.re"(arr2)));
+ assert(isClose(map!"a.im"(inv2), map!"a.im"(arr2)));
// FFTs of size 0, 1 and 2 are handled as special cases. Test them here.
ushort[] empty;
@@ -3305,21 +3870,21 @@ void inverseFft(Ret, R)(R range, Ret buf)
auto oneInv = inverseFft(oneFft);
assert(oneInv.length == 1);
- assert(approxEqual(oneInv[0].re, 4.5));
- assert(approxEqual(oneInv[0].im, 0));
+ assert(isClose(oneInv[0].re, 4.5));
+ assert(isClose(oneInv[0].im, 0, 0.0, 1e-10));
long[2] twoElems = [8, 4];
auto twoFft = fft(twoElems[]);
assert(twoFft.length == 2);
- assert(approxEqual(twoFft[0].re, 12));
- assert(approxEqual(twoFft[0].im, 0));
- assert(approxEqual(twoFft[1].re, 4));
- assert(approxEqual(twoFft[1].im, 0));
+ assert(isClose(twoFft[0].re, 12));
+ assert(isClose(twoFft[0].im, 0, 0.0, 1e-10));
+ assert(isClose(twoFft[1].re, 4));
+ assert(isClose(twoFft[1].im, 0, 0.0, 1e-10));
auto twoInv = inverseFft(twoFft);
- assert(approxEqual(twoInv[0].re, 8));
- assert(approxEqual(twoInv[0].im, 0));
- assert(approxEqual(twoInv[1].re, 4));
- assert(approxEqual(twoInv[1].im, 0));
+ assert(isClose(twoInv[0].re, 8));
+ assert(isClose(twoInv[0].im, 0, 0.0, 1e-10));
+ assert(isClose(twoInv[1].re, 4));
+ assert(isClose(twoInv[1].im, 0, 0.0, 1e-10));
}
// Swaps the real and imaginary parts of a complex number. This is useful
@@ -3329,6 +3894,89 @@ C swapRealImag(C)(C input)
return C(input.im, input.re);
}
+/** This function transforms `decimal` value into a value in the factorial number
+system stored in `fac`.
+
+A factorial number is constructed as:
+$(D fac[0] * 0! + fac[1] * 1! + ... fac[20] * 20!)
+
+Params:
+ decimal = The decimal value to convert into the factorial number system.
+ fac = The array to store the factorial number. The array is of size 21 as
+ `ulong.max` requires 21 digits in the factorial number system.
+Returns:
+ A variable storing the number of digits of the factorial number stored in
+ `fac`.
+*/
+size_t decimalToFactorial(ulong decimal, ref ubyte[21] fac)
+ @safe pure nothrow @nogc
+{
+ import std.algorithm.mutation : reverse;
+ size_t idx;
+
+ for (ulong i = 1; decimal != 0; ++i)
+ {
+ auto temp = decimal % i;
+ decimal /= i;
+ fac[idx++] = cast(ubyte)(temp);
+ }
+
+ if (idx == 0)
+ {
+ fac[idx++] = cast(ubyte) 0;
+ }
+
+ reverse(fac[0 .. idx]);
+
+ // first digit of the number in factorial will always be zero
+ assert(fac[idx - 1] == 0);
+
+ return idx;
+}
+
+///
+@safe pure @nogc unittest
+{
+ ubyte[21] fac;
+ size_t idx = decimalToFactorial(2982, fac);
+
+ assert(fac[0] == 4);
+ assert(fac[1] == 0);
+ assert(fac[2] == 4);
+ assert(fac[3] == 1);
+ assert(fac[4] == 0);
+ assert(fac[5] == 0);
+ assert(fac[6] == 0);
+}
+
+@safe pure unittest
+{
+ ubyte[21] fac;
+ size_t idx = decimalToFactorial(0UL, fac);
+ assert(idx == 1);
+ assert(fac[0] == 0);
+
+ fac[] = 0;
+ idx = 0;
+ idx = decimalToFactorial(ulong.max, fac);
+ assert(idx == 21);
+ auto t = [7, 11, 12, 4, 3, 15, 3, 5, 3, 5, 0, 8, 3, 5, 0, 0, 0, 2, 1, 1, 0];
+ foreach (i, it; fac[0 .. 21])
+ {
+ assert(it == t[i]);
+ }
+
+ fac[] = 0;
+ idx = decimalToFactorial(2982, fac);
+
+ assert(idx == 7);
+ t = [4, 0, 4, 1, 0, 0, 0];
+ foreach (i, it; fac[0 .. idx])
+ {
+ assert(it == t[i]);
+ }
+}
+
private:
// The reasons I couldn't use std.algorithm were b/c its stride length isn't
// modifiable on the fly and because range has grown some performance hacks
diff --git a/libphobos/src/std/outbuffer.d b/libphobos/src/std/outbuffer.d
index d76ead2ed49..9590238c7f9 100644
--- a/libphobos/src/std/outbuffer.d
+++ b/libphobos/src/std/outbuffer.d
@@ -1,18 +1,19 @@
// Written in the D programming language.
/**
-Serialize data to $(D ubyte) arrays.
+Serialize data to `ubyte` arrays.
- * Copyright: Copyright Digital Mars 2000 - 2015.
+ * Copyright: Copyright The D Language Foundation 2000 - 2015.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright)
- * Source: $(PHOBOSSRC std/_outbuffer.d)
+ * Source: $(PHOBOSSRC std/outbuffer.d)
*
* $(SCRIPT inhibitQuickIndex = 1;)
*/
module std.outbuffer;
-import core.stdc.stdarg; // : va_list;
+import core.stdc.stdarg;
+import std.traits : isSomeString;
/*********************************************
* OutBuffer provides a way to build up an array of bytes out
@@ -41,7 +42,7 @@ class OutBuffer
/*********************************
* Convert to array of bytes.
*/
- ubyte[] toBytes() { return data[0 .. offset]; }
+ inout(ubyte)[] toBytes() scope inout { return data[0 .. offset]; }
/***********************************
* Preallocate nbytes more to the size of the internal buffer.
@@ -59,7 +60,7 @@ class OutBuffer
{
assert(offset + nbytes <= data.length);
}
- body
+ do
{
if (data.length < offset + nbytes)
{
@@ -78,19 +79,19 @@ class OutBuffer
* Append data to the internal buffer.
*/
- void write(const(ubyte)[] bytes)
+ void write(scope const(ubyte)[] bytes)
{
reserve(bytes.length);
data[offset .. offset + bytes.length] = bytes[];
offset += bytes.length;
}
- void write(in wchar[] chars) @trusted
+ void write(scope const(wchar)[] chars) @trusted
{
write(cast(ubyte[]) chars);
}
- void write(const(dchar)[] chars) @trusted
+ void write(scope const(dchar)[] chars) @trusted
{
write(cast(ubyte[]) chars);
}
@@ -161,12 +162,12 @@ class OutBuffer
offset += real.sizeof;
}
- void write(in char[] s) @trusted /// ditto
+ void write(scope const(char)[] s) @trusted /// ditto
{
write(cast(ubyte[]) s);
}
- void write(OutBuffer buf) /// ditto
+ void write(scope const OutBuffer buf) /// ditto
{
write(buf.toBytes());
}
@@ -195,7 +196,7 @@ class OutBuffer
{
assert((offset & (alignsize - 1)) == 0);
}
- body
+ do
{
auto nbytes = offset & (alignsize - 1);
if (nbytes)
@@ -245,13 +246,13 @@ class OutBuffer
* Append output of C's vprintf() to internal buffer.
*/
- void vprintf(string format, va_list args) @trusted nothrow
+ void vprintf(scope string format, va_list args) @trusted nothrow
{
import core.stdc.stdio : vsnprintf;
import core.stdc.stdlib : alloca;
import std.string : toStringz;
- version (unittest)
+ version (StdUnittest)
char[3] buffer = void; // trigger reallocation
else
char[128] buffer = void;
@@ -290,7 +291,7 @@ class OutBuffer
* Append output of C's printf() to internal buffer.
*/
- void printf(string format, ...) @trusted
+ void printf(scope string format, ...) @trusted
{
va_list ap;
va_start(ap, format);
@@ -309,9 +310,9 @@ class OutBuffer
* $(REF _writef, std,stdio);
* $(REF formattedWrite, std,format);
*/
- void writef(Char, A...)(in Char[] fmt, A args)
+ void writef(Char, A...)(scope const(Char)[] fmt, A args)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
formattedWrite(this, fmt, args);
}
@@ -323,6 +324,25 @@ class OutBuffer
assert(b.toString() == "a16b");
}
+ /// ditto
+ void writef(alias fmt, A...)(A args)
+ if (isSomeString!(typeof(fmt)))
+ {
+ import std.format : checkFormatException;
+
+ alias e = checkFormatException!(fmt, A);
+ static assert(!e, e.msg);
+ return this.writef(fmt, args);
+ }
+
+ ///
+ @safe unittest
+ {
+ OutBuffer b = new OutBuffer();
+ b.writef!"a%sb"(16);
+ assert(b.toString() == "a16b");
+ }
+
/**
* Formats and writes its arguments in text format to the OutBuffer,
* followed by a newline.
@@ -335,9 +355,9 @@ class OutBuffer
* $(REF _writefln, std,stdio);
* $(REF formattedWrite, std,format);
*/
- void writefln(Char, A...)(in Char[] fmt, A args)
+ void writefln(Char, A...)(scope const(Char)[] fmt, A args)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
formattedWrite(this, fmt, args);
put('\n');
}
@@ -350,6 +370,25 @@ class OutBuffer
assert(b.toString() == "a16b\n");
}
+ /// ditto
+ void writefln(alias fmt, A...)(A args)
+ if (isSomeString!(typeof(fmt)))
+ {
+ import std.format : checkFormatException;
+
+ alias e = checkFormatException!(fmt, A);
+ static assert(!e, e.msg);
+ return this.writefln(fmt, args);
+ }
+
+ ///
+ @safe unittest
+ {
+ OutBuffer b = new OutBuffer();
+ b.writefln!"a%sb"(16);
+ assert(b.toString() == "a16b\n");
+ }
+
/*****************************************
* At offset index into buffer, create nbytes of space by shifting upwards
* all data past index.
@@ -360,7 +399,7 @@ class OutBuffer
{
assert(index <= offset);
}
- body
+ do
{
reserve(nbytes);
diff --git a/libphobos/src/std/package.d b/libphobos/src/std/package.d
new file mode 100644
index 00000000000..a1d04444d62
--- /dev/null
+++ b/libphobos/src/std/package.d
@@ -0,0 +1,82 @@
+/++
+Convenience file that allows to import entire Phobos in one import.
++/
+module std;
+
+///
+@safe unittest
+{
+ import std;
+
+ int len;
+ const r = 6.iota
+ .filter!(a => a % 2) // 1 3 5
+ .map!(a => a * 2) // 2 6 10
+ .tee!(_ => len++)
+ .substitute(6, -6) // 2 -6 10
+ .sum
+ .reverseArgs!format("Sum: %d");
+
+ assert(len == 3);
+ assert(r == "Sum: 6");
+}
+
+///
+@safe unittest
+{
+ import std;
+ assert(10.iota.map!(a => pow(2, a)).sum == 1023);
+}
+
+public import
+ std.algorithm,
+ std.array,
+ std.ascii,
+ std.base64,
+ std.bigint,
+ std.bitmanip,
+ std.compiler,
+ std.complex,
+ std.concurrency,
+ std.container,
+ std.conv,
+ std.csv,
+ std.datetime,
+ std.demangle,
+ std.digest,
+ std.encoding,
+ std.exception,
+ std.file,
+ std.format,
+ std.functional,
+ std.getopt,
+ std.json,
+ std.math,
+ std.mathspecial,
+ std.meta,
+ std.mmfile,
+ std.net.curl,
+ std.net.isemail,
+ std.numeric,
+ std.parallelism,
+ std.path,
+ std.process,
+ std.random,
+ std.range,
+ std.regex,
+ std.signals,
+ std.socket,
+ std.stdint,
+ std.stdio,
+ std.string,
+ std.sumtype,
+ std.system,
+ std.traits,
+ std.typecons,
+ std.uni,
+ std.uri,
+ std.utf,
+ std.uuid,
+ std.variant,
+ std.zip,
+ std.zlib;
diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d
index 61d5cea55f9..664330a3c2b 100644
--- a/libphobos/src/std/parallelism.d
+++ b/libphobos/src/std/parallelism.d
@@ -1,39 +1,39 @@
/**
-$(D std._parallelism) implements high-level primitives for SMP _parallelism.
+`std.parallelism` implements high-level primitives for SMP parallelism.
These include parallel foreach, parallel reduce, parallel eager map, pipelining
-and future/promise _parallelism. $(D std._parallelism) is recommended when the
+and future/promise parallelism. `std.parallelism` is recommended when the
same operation is to be executed in parallel on different data, or when a
function is to be executed in a background thread and its result returned to a
well-defined main thread. For communication between arbitrary threads, see
-$(D std.concurrency).
+`std.concurrency`.
-$(D std._parallelism) is based on the concept of a $(D Task). A $(D Task) is an
+`std.parallelism` is based on the concept of a `Task`. A `Task` is an
object that represents the fundamental unit of work in this library and may be
-executed in parallel with any other $(D Task). Using $(D Task)
+executed in parallel with any other `Task`. Using `Task`
directly allows programming with a future/promise paradigm. All other
-supported _parallelism paradigms (parallel foreach, map, reduce, pipelining)
-represent an additional level of abstraction over $(D Task). They
-automatically create one or more $(D Task) objects, or closely related types
+supported parallelism paradigms (parallel foreach, map, reduce, pipelining)
+represent an additional level of abstraction over `Task`. They
+automatically create one or more `Task` objects, or closely related types
that are conceptually identical but not part of the public API.
-After creation, a $(D Task) may be executed in a new thread, or submitted
-to a $(D TaskPool) for execution. A $(D TaskPool) encapsulates a task queue
+After creation, a `Task` may be executed in a new thread, or submitted
+to a `TaskPool` for execution. A `TaskPool` encapsulates a task queue
and its worker threads. Its purpose is to efficiently map a large
-number of $(D Task)s onto a smaller number of threads. A task queue is a
-FIFO queue of $(D Task) objects that have been submitted to the
-$(D TaskPool) and are awaiting execution. A worker thread is a thread that
-is associated with exactly one task queue. It executes the $(D Task) at the
+number of `Task`s onto a smaller number of threads. A task queue is a
+FIFO queue of `Task` objects that have been submitted to the
+`TaskPool` and are awaiting execution. A worker thread is a thread that
+is associated with exactly one task queue. It executes the `Task` at the
front of its queue when the queue has work available, or sleeps when
no work is available. Each task queue is associated with zero or
-more worker threads. If the result of a $(D Task) is needed before execution
-by a worker thread has begun, the $(D Task) can be removed from the task queue
+more worker threads. If the result of a `Task` is needed before execution
+by a worker thread has begun, the `Task` can be removed from the task queue
and executed immediately in the thread where the result is needed.
-Warning: Unless marked as $(D @trusted) or $(D @safe), artifacts in
+Warning: Unless marked as `@trusted` or `@safe`, artifacts in
this module allow implicit data sharing between threads and cannot
guarantee that client code is free from low level data races.
-Source: $(PHOBOSSRC std/_parallelism.d)
+Source: $(PHOBOSSRC std/parallelism.d)
Author: David Simcha
Copyright: Copyright (c) 2009-2011, David Simcha.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0)
@@ -53,7 +53,7 @@ else version (WatchOS)
@system unittest
{
import std.algorithm.iteration : map;
- import std.math : approxEqual;
+ import std.math.operations : isClose;
import std.parallelism : taskPool;
import std.range : iota;
@@ -82,7 +82,7 @@ else version (WatchOS)
immutable pi = 4.0 * taskPool.reduce!"a + b"(n.iota.map!getTerm);
- assert(pi.approxEqual(3.1415926));
+ assert(pi.isClose(3.14159, 1e-5));
}
import core.atomic;
@@ -389,32 +389,32 @@ private struct AbstractTask
}
/**
-$(D Task) represents the fundamental unit of work. A $(D Task) may be
-executed in parallel with any other $(D Task). Using this struct directly
-allows future/promise _parallelism. In this paradigm, a function (or delegate
+`Task` represents the fundamental unit of work. A `Task` may be
+executed in parallel with any other `Task`. Using this struct directly
+allows future/promise parallelism. In this paradigm, a function (or delegate
or other callable) is executed in a thread other than the one it was called
from. The calling thread does not block while the function is being executed.
-A call to $(D workForce), $(D yieldForce), or $(D spinForce) is used to
-ensure that the $(D Task) has finished executing and to obtain the return
-value, if any. These functions and $(D done) also act as full memory barriers,
-meaning that any memory writes made in the thread that executed the $(D Task)
+A call to `workForce`, `yieldForce`, or `spinForce` is used to
+ensure that the `Task` has finished executing and to obtain the return
+value, if any. These functions and `done` also act as full memory barriers,
+meaning that any memory writes made in the thread that executed the `Task`
are guaranteed to be visible in the calling thread after one of these functions
returns.
The $(REF task, std,parallelism) and $(REF scopedTask, std,parallelism) functions can
-be used to create an instance of this struct. See $(D task) for usage examples.
+be used to create an instance of this struct. See `task` for usage examples.
-Function results are returned from $(D yieldForce), $(D spinForce) and
-$(D workForce) by ref. If $(D fun) returns by ref, the reference will point
-to the returned reference of $(D fun). Otherwise it will point to a
+Function results are returned from `yieldForce`, `spinForce` and
+`workForce` by ref. If `fun` returns by ref, the reference will point
+to the returned reference of `fun`. Otherwise it will point to a
field in this struct.
Copying of this struct is disabled, since it would provide no useful semantics.
If you want to pass this struct around, you should do so by reference or
pointer.
-Bugs: Changes to $(D ref) and $(D out) arguments are not propagated to the
- call site, only to $(D args) in this struct.
+Bugs: Changes to `ref` and `out` arguments are not propagated to the
+ call site, only to `args` in this struct.
*/
struct Task(alias fun, Args...)
{
@@ -435,7 +435,7 @@ struct Task(alias fun, Args...)
{
fun(myCastedTask._args);
}
- else static if (is(typeof(addressOf(fun(myCastedTask._args)))))
+ else static if (is(typeof(&(fun(myCastedTask._args)))))
{
myCastedTask.returnVal = addressOf(fun(myCastedTask._args));
}
@@ -451,8 +451,8 @@ struct Task(alias fun, Args...)
Args _args;
/**
- The arguments the function was called with. Changes to $(D out) and
- $(D ref) arguments will be visible here.
+ The arguments the function was called with. Changes to `out` and
+ `ref` arguments will be visible here.
*/
static if (__traits(isSame, fun, run))
{
@@ -472,7 +472,7 @@ struct Task(alias fun, Args...)
static if (isFunctionPointer!(_args[0]))
{
private enum bool isPure =
- functionAttributes!(Args[0]) & FunctionAttribute.pure_;
+ (functionAttributes!(Args[0]) & FunctionAttribute.pure_) != 0;
}
else
{
@@ -493,8 +493,8 @@ struct Task(alias fun, Args...)
/**
- The return type of the function called by this $(D Task). This can be
- $(D void).
+ The return type of the function called by this `Task`. This can be
+ `void`.
*/
alias ReturnType = typeof(fun(_args));
@@ -536,7 +536,8 @@ struct Task(alias fun, Args...)
}
}
- // Work around DMD bug 6588, allow immutable elements.
+ // Work around DMD bug https://issues.dlang.org/show_bug.cgi?id=6588,
+ // allow immutable elements.
static if (allSatisfy!(isAssignable, Args))
{
typeof(this) opAssign(typeof(this) rhs)
@@ -550,20 +551,17 @@ struct Task(alias fun, Args...)
}
else
{
- @disable typeof(this) opAssign(typeof(this) rhs)
- {
- assert(0);
- }
+ @disable typeof(this) opAssign(typeof(this) rhs);
}
/**
- If the $(D Task) isn't started yet, execute it in the current thread.
+ If the `Task` isn't started yet, execute it in the current thread.
If it's done, return its return value, if any. If it's in progress,
busy spin until it's done, then return the return value. If it threw
an exception, rethrow that exception.
This function should be used when you expect the result of the
- $(D Task) to be available on a timescale shorter than that of an OS
+ `Task` to be available on a timescale shorter than that of an OS
context switch.
*/
@property ref ReturnType spinForce() @trusted
@@ -586,7 +584,7 @@ struct Task(alias fun, Args...)
}
/**
- If the $(D Task) isn't started yet, execute it in the current thread.
+ If the `Task` isn't started yet, execute it in the current thread.
If it's done, return its return value, if any. If it's in progress,
wait on a condition variable. If it threw an exception, rethrow that
exception.
@@ -631,13 +629,13 @@ struct Task(alias fun, Args...)
}
/**
- If this $(D Task) was not started yet, execute it in the current
+ If this `Task` was not started yet, execute it in the current
thread. If it is finished, return its result. If it is in progress,
- execute any other $(D Task) from the $(D TaskPool) instance that
- this $(D Task) was submitted to until this one
+ execute any other `Task` from the `TaskPool` instance that
+ this `Task` was submitted to until this one
is finished. If it threw an exception, rethrow that exception.
- If no other tasks are available or this $(D Task) was executed using
- $(D executeInNewThread), wait on a condition variable.
+ If no other tasks are available or this `Task` was executed using
+ `executeInNewThread`, wait on a condition variable.
*/
@property ref ReturnType workForce() @trusted
{
@@ -705,10 +703,10 @@ struct Task(alias fun, Args...)
}
/**
- Returns $(D true) if the $(D Task) is finished executing.
+ Returns `true` if the `Task` is finished executing.
Throws: Rethrows any exception thrown during the execution of the
- $(D Task).
+ `Task`.
*/
@property bool done() @trusted
{
@@ -717,11 +715,11 @@ struct Task(alias fun, Args...)
}
/**
- Create a new thread for executing this $(D Task), execute it in the
+ Create a new thread for executing this `Task`, execute it in the
newly created thread, then terminate the thread. This can be used for
future/promise parallelism. An explicit priority may be given
- to the $(D Task). If one is provided, its value is forwarded to
- $(D core.thread.Thread.priority). See $(REF task, std,parallelism) for
+ to the `Task`. If one is provided, its value is forwarded to
+ `core.thread.Thread.priority`. See $(REF task, std,parallelism) for
usage example.
*/
void executeInNewThread() @trusted
@@ -748,8 +746,8 @@ struct Task(alias fun, Args...)
//@disable this(this) {}
}
-// Calls $(D fpOrDelegate) with $(D args). This is an
-// adapter that makes $(D Task) work with delegates, function pointers and
+// Calls `fpOrDelegate` with `args`. This is an
+// adapter that makes `Task` work with delegates, function pointers and
// functors instead of just aliases.
ReturnType!F run(F, Args...)(F fpOrDelegate, ref Args args)
{
@@ -757,12 +755,12 @@ ReturnType!F run(F, Args...)(F fpOrDelegate, ref Args args)
}
/**
-Creates a $(D Task) on the GC heap that calls an alias. This may be executed
-via $(D Task.executeInNewThread) or by submitting to a
+Creates a `Task` on the GC heap that calls an alias. This may be executed
+via `Task.executeInNewThread` or by submitting to a
$(REF TaskPool, std,parallelism). A globally accessible instance of
-$(D TaskPool) is provided by $(REF taskPool, std,parallelism).
+`TaskPool` is provided by $(REF taskPool, std,parallelism).
-Returns: A pointer to the $(D Task).
+Returns: A pointer to the `Task`.
Example:
---
@@ -828,7 +826,7 @@ auto task(alias fun, Args...)(Args args)
}
/**
-Creates a $(D Task) on the GC heap that calls a function pointer, delegate, or
+Creates a `Task` on the GC heap that calls a function pointer, delegate, or
class/struct with overloaded opCall.
Example:
@@ -842,7 +840,7 @@ void main()
{
// Create and execute a Task for reading
// foo.txt.
- auto file1Task = task(&read, "foo.txt");
+ auto file1Task = task(&read!string, "foo.txt", size_t.max);
file1Task.executeInNewThread();
// Read bar.txt in parallel.
@@ -855,7 +853,7 @@ void main()
Notes: This function takes a non-scope delegate, meaning it can be
used with closures. If you can't allocate a closure due to objects
- on the stack that have scoped destruction, see $(D scopedTask), which
+ on the stack that have scoped destruction, see `scopedTask`, which
takes a scope delegate.
*/
auto task(F, Args...)(F delegateOrFp, Args args)
@@ -865,24 +863,24 @@ if (is(typeof(delegateOrFp(args))) && !isSafeTask!F)
}
/**
-Version of $(D task) usable from $(D @safe) code. Usage mechanics are
+Version of `task` usable from `@safe` code. Usage mechanics are
identical to the non-@safe case, but safety introduces some restrictions:
-1. $(D fun) must be @safe or @trusted.
+1. `fun` must be @safe or @trusted.
-2. $(D F) must not have any unshared aliasing as defined by
+2. `F` must not have any unshared aliasing as defined by
$(REF hasUnsharedAliasing, std,traits). This means it
may not be an unshared delegate or a non-shared class or struct
- with overloaded $(D opCall). This also precludes accepting template
+ with overloaded `opCall`. This also precludes accepting template
alias parameters.
-3. $(D Args) must not have unshared aliasing.
+3. `Args` must not have unshared aliasing.
-4. $(D fun) must not return by reference.
+4. `fun` must not return by reference.
-5. The return type must not have unshared aliasing unless $(D fun) is
- $(D pure) or the $(D Task) is executed via $(D executeInNewThread) instead
- of using a $(D TaskPool).
+5. The return type must not have unshared aliasing unless `fun` is
+ `pure` or the `Task` is executed via `executeInNewThread` instead
+ of using a `TaskPool`.
*/
@trusted auto task(F, Args...)(F fun, Args args)
@@ -892,25 +890,25 @@ if (is(typeof(fun(args))) && isSafeTask!F)
}
/**
-These functions allow the creation of $(D Task) objects on the stack rather
-than the GC heap. The lifetime of a $(D Task) created by $(D scopedTask)
+These functions allow the creation of `Task` objects on the stack rather
+than the GC heap. The lifetime of a `Task` created by `scopedTask`
cannot exceed the lifetime of the scope it was created in.
-$(D scopedTask) might be preferred over $(D task):
+`scopedTask` might be preferred over `task`:
-1. When a $(D Task) that calls a delegate is being created and a closure
+1. When a `Task` that calls a delegate is being created and a closure
cannot be allocated due to objects on the stack that have scoped
- destruction. The delegate overload of $(D scopedTask) takes a $(D scope)
+ destruction. The delegate overload of `scopedTask` takes a `scope`
delegate.
2. As a micro-optimization, to avoid the heap allocation associated with
- $(D task) or with the creation of a closure.
+ `task` or with the creation of a closure.
-Usage is otherwise identical to $(D task).
+Usage is otherwise identical to `task`.
-Notes: $(D Task) objects created using $(D scopedTask) will automatically
-call $(D Task.yieldForce) in their destructor if necessary to ensure
-the $(D Task) is complete before the stack frame they reside on is destroyed.
+Notes: `Task` objects created using `scopedTask` will automatically
+call `Task.yieldForce` in their destructor if necessary to ensure
+the `Task` is complete before the stack frame they reside on is destroyed.
*/
auto scopedTask(alias fun, Args...)(Args args)
{
@@ -1053,22 +1051,28 @@ shared static ~this()
/**
This class encapsulates a task queue and a set of worker threads. Its purpose
-is to efficiently map a large number of $(D Task)s onto a smaller number of
-threads. A task queue is a FIFO queue of $(D Task) objects that have been
-submitted to the $(D TaskPool) and are awaiting execution. A worker thread is a
-thread that executes the $(D Task) at the front of the queue when one is
+is to efficiently map a large number of `Task`s onto a smaller number of
+threads. A task queue is a FIFO queue of `Task` objects that have been
+submitted to the `TaskPool` and are awaiting execution. A worker thread is a
+thread that executes the `Task` at the front of the queue when one is
available and sleeps when the queue is empty.
This class should usually be used via the global instantiation
available via the $(REF taskPool, std,parallelism) property.
-Occasionally it is useful to explicitly instantiate a $(D TaskPool):
+Occasionally it is useful to explicitly instantiate a `TaskPool`:
-1. When you want $(D TaskPool) instances with multiple priorities, for example
+1. When you want `TaskPool` instances with multiple priorities, for example
a low priority pool and a high priority pool.
2. When the threads in the global task pool are waiting on a synchronization
primitive (for example a mutex), and you want to parallelize the code that
needs to run before these threads can be resumed.
+
+Note: The worker threads in this pool will not stop until
+ `stop` or `finish` is called, even if the main thread
+ has finished already. This may lead to programs that
+ never end. If you do not want this behaviour, you can set `isDaemon`
+ to true.
*/
final class TaskPool
{
@@ -1091,7 +1095,7 @@ private:
Mutex waiterMutex; // For waiterCondition
// The instanceStartIndex of the next instance that will be created.
- __gshared static size_t nextInstanceIndex = 1;
+ __gshared size_t nextInstanceIndex = 1;
// The index of the current thread.
static size_t threadIndex;
@@ -1214,7 +1218,7 @@ private:
assert(returned.prev is null);
}
}
- body
+ do
{
if (isSingleTask) return null;
@@ -1258,7 +1262,7 @@ private:
assert(tail.prev.next is tail, text(tail.prev, '\t', tail.next));
}
}
- body
+ do
{
// Not using enforce() to save on function call overhead since this
// is a performance critical function.
@@ -1452,7 +1456,7 @@ private:
// Disabled until writing code to support
// running thread with specified priority
- // See https://d.puremagic.com/issues/show_bug.cgi?id=8960
+ // See https://issues.dlang.org/show_bug.cgi?id=8960
/*if (priority != int.max)
{
@@ -1478,11 +1482,11 @@ public:
}
/**
- Default constructor that initializes a $(D TaskPool) with
- $(D totalCPUs) - 1 worker threads. The minus 1 is included because the
+ Default constructor that initializes a `TaskPool` with
+ `totalCPUs` - 1 worker threads. The minus 1 is included because the
main thread will also be available to do work.
- Note: On single-core machines, the primitives provided by $(D TaskPool)
+ Note: On single-core machines, the primitives provided by `TaskPool`
operate transparently in single-threaded mode.
*/
this() @trusted
@@ -1522,17 +1526,17 @@ public:
/**
Implements a parallel foreach loop over a range. This works by implicitly
- creating and submitting one $(D Task) to the $(D TaskPool) for each worker
- thread. A work unit is a set of consecutive elements of $(D range) to
+ creating and submitting one `Task` to the `TaskPool` for each worker
+ thread. A work unit is a set of consecutive elements of `range` to
be processed by a worker thread between communication with any other
thread. The number of elements processed per work unit is controlled by the
- $(D workUnitSize) parameter. Smaller work units provide better load
+ `workUnitSize` parameter. Smaller work units provide better load
balancing, but larger work units avoid the overhead of communicating
with other threads frequently to fetch the next work unit. Large work
units also avoid false sharing in cases where the range is being modified.
The less time a single iteration of the loop takes, the larger
- $(D workUnitSize) should be. For very expensive loop bodies,
- $(D workUnitSize) should be 1. An overload that chooses a default work
+ `workUnitSize` should be. For very expensive loop bodies,
+ `workUnitSize` should be 1. An overload that chooses a default work
unit size is also available.
Example:
@@ -1566,18 +1570,18 @@ public:
Notes:
The memory usage of this implementation is guaranteed to be constant
- in $(D range.length).
+ in `range.length`.
Breaking from a parallel foreach loop via a break, labeled break,
labeled continue, return or goto statement throws a
- $(D ParallelForeachError).
+ `ParallelForeachError`.
In the case of non-random access ranges, parallel foreach buffers lazily
- to an array of size $(D workUnitSize) before executing the parallel portion
+ to an array of size `workUnitSize` before executing the parallel portion
of the loop. The exception is that, if a parallel foreach is executed
- over a range returned by $(D asyncBuf) or $(D map), the copying is elided
- and the buffers are simply swapped. In this case $(D workUnitSize) is
- ignored and the work unit size is set to the buffer size of $(D range).
+ over a range returned by `asyncBuf` or `map`, the copying is elided
+ and the buffers are simply swapped. In this case `workUnitSize` is
+ ignored and the work unit size is set to the buffer size of `range`.
A memory barrier is guaranteed to be executed on exit from the loop,
so that results produced by all threads are visible in the calling thread.
@@ -1585,10 +1589,10 @@ public:
$(B Exception Handling):
When at least one exception is thrown from inside a parallel foreach loop,
- the submission of additional $(D Task) objects is terminated as soon as
+ the submission of additional `Task` objects is terminated as soon as
possible, in a non-deterministic manner. All executing or
enqueued work units are allowed to complete. Then, all exceptions that
- were thrown by any work unit are chained using $(D Throwable.next) and
+ were thrown by any work unit are chained using `Throwable.next` and
rethrown. The order of the exception chaining is non-deterministic.
*/
ParallelForeach!R parallel(R)(R range, size_t workUnitSize)
@@ -1623,9 +1627,9 @@ public:
{
/**
Eager parallel map. The eagerness of this function means it has less
- overhead than the lazily evaluated $(D TaskPool.map) and should be
+ overhead than the lazily evaluated `TaskPool.map` and should be
preferred where the memory requirements of eagerness are acceptable.
- $(D functions) are the functions to be evaluated, passed as template
+ `functions` are the functions to be evaluated, passed as template
alias parameters in a style similar to
$(REF map, std,algorithm,iteration).
The first argument must be a random access range. For performance
@@ -1633,7 +1637,7 @@ public:
initialized. Elements will be overwritten without calling a destructor
nor doing an assignment. As such, the range must not contain meaningful
data$(DDOC_COMMENT not a section): either un-initialized objects, or
- objects in their $(D .init) state.
+ objects in their `.init` state.
---
auto numbers = iota(100_000_000.0);
@@ -1648,7 +1652,7 @@ public:
---
Immediately after the range argument, an optional work unit size argument
- may be provided. Work units as used by $(D amap) are identical to those
+ may be provided. Work units as used by `amap` are identical to those
defined for parallel foreach. If no work unit size is provided, the
default work unit size is used.
@@ -1693,21 +1697,21 @@ public:
To parallelize the copying of a range with expensive to evaluate elements
to an array, pass an identity function (a function that just returns
- whatever argument is provided to it) to $(D amap).
+ whatever argument is provided to it) to `amap`.
$(B Exception Handling):
When at least one exception is thrown from inside the map functions,
- the submission of additional $(D Task) objects is terminated as soon as
+ the submission of additional `Task` objects is terminated as soon as
possible, in a non-deterministic manner. All currently executing or
enqueued work units are allowed to complete. Then, all exceptions that
- were thrown from any work unit are chained using $(D Throwable.next) and
+ were thrown from any work unit are chained using `Throwable.next` and
rethrown. The order of the exception chaining is non-deterministic.
*/
auto amap(Args...)(Args args)
if (isRandomAccessRange!(Args[0]))
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
alias fun = adjoin!(staticMap!(unaryFun, functions));
@@ -1829,8 +1833,8 @@ public:
{
/**
A semi-lazy parallel map that can be used for pipelining. The map
- functions are evaluated for the first $(D bufSize) elements and stored in a
- buffer and made available to $(D popFront). Meanwhile, in the
+ functions are evaluated for the first `bufSize` elements and stored in a
+ buffer and made available to `popFront`. Meanwhile, in the
background a second buffer of the same size is filled. When the first
buffer is exhausted, it is swapped with the second buffer and filled while
the values from what was originally the second buffer are read. This
@@ -1838,36 +1842,37 @@ public:
the need for atomic operations or synchronization for each write, and
enables the mapping function to be evaluated efficiently in parallel.
- $(D map) has more overhead than the simpler procedure used by $(D amap)
+ `map` has more overhead than the simpler procedure used by `amap`
but avoids the need to keep all results in memory simultaneously and works
with non-random access ranges.
Params:
- source = The input range to be mapped. If $(D source) is not random
- access it will be lazily buffered to an array of size $(D bufSize) before
+ source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ to be mapped. If `source` is not random
+ access it will be lazily buffered to an array of size `bufSize` before
the map function is evaluated. (For an exception to this rule, see Notes.)
bufSize = The size of the buffer to store the evaluated elements.
workUnitSize = The number of elements to evaluate in a single
- $(D Task). Must be less than or equal to $(D bufSize), and
- should be a fraction of $(D bufSize) such that all worker threads can be
+ `Task`. Must be less than or equal to `bufSize`, and
+ should be a fraction of `bufSize` such that all worker threads can be
used. If the default of size_t.max is used, workUnitSize will be set to
the pool-wide default.
Returns: An input range representing the results of the map. This range
- has a length iff $(D source) has a length.
+ has a length iff `source` has a length.
Notes:
- If a range returned by $(D map) or $(D asyncBuf) is used as an input to
- $(D map), then as an optimization the copying from the output buffer
+ If a range returned by `map` or `asyncBuf` is used as an input to
+ `map`, then as an optimization the copying from the output buffer
of the first range to the input buffer of the second range is elided, even
- though the ranges returned by $(D map) and $(D asyncBuf) are non-random
- access ranges. This means that the $(D bufSize) parameter passed to the
- current call to $(D map) will be ignored and the size of the buffer
- will be the buffer size of $(D source).
+ though the ranges returned by `map` and `asyncBuf` are non-random
+ access ranges. This means that the `bufSize` parameter passed to the
+ current call to `map` will be ignored and the size of the buffer
+ will be the buffer size of `source`.
Example:
---
@@ -1890,11 +1895,11 @@ public:
$(B Exception Handling):
- Any exceptions thrown while iterating over $(D source)
- or computing the map function are re-thrown on a call to $(D popFront) or,
+ Any exceptions thrown while iterating over `source`
+ or computing the map function are re-thrown on a call to `popFront` or,
if thrown during construction, are simply allowed to propagate to the
caller. In the case of exceptions thrown while computing the map function,
- the exceptions are chained as in $(D TaskPool.amap).
+ the exceptions are chained as in `TaskPool.amap`.
*/
auto
map(S)(S source, size_t bufSize = 100, size_t workUnitSize = size_t.max)
@@ -2092,7 +2097,8 @@ public:
{
assert(nextBufTask.prev is null);
assert(nextBufTask.next is null);
- } body
+ }
+ do
{
// Hack to reuse the task object.
@@ -2171,13 +2177,13 @@ public:
}
/**
- Given a $(D source) range that is expensive to iterate over, returns an
- input range that asynchronously buffers the contents of
- $(D source) into a buffer of $(D bufSize) elements in a worker thread,
+ Given a `source` range that is expensive to iterate over, returns an
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives) that
+ asynchronously buffers the contents of `source` into a buffer of `bufSize` elements in a worker thread,
while making previously buffered elements from a second buffer, also of size
- $(D bufSize), available via the range interface of the returned
- object. The returned range has a length iff $(D hasLength!S).
- $(D asyncBuf) is useful, for example, when performing expensive operations
+ `bufSize`, available via the range interface of the returned
+ object. The returned range has a length iff `hasLength!S`.
+ `asyncBuf` is useful, for example, when performing expensive operations
on the elements of ranges that represent data on a disk or network.
Example:
@@ -2209,8 +2215,8 @@ public:
$(B Exception Handling):
- Any exceptions thrown while iterating over $(D source) are re-thrown on a
- call to $(D popFront) or, if thrown during construction, simply
+ Any exceptions thrown while iterating over `source` are re-thrown on a
+ call to `popFront` or, if thrown during construction, simply
allowed to propagate to the caller.
*/
auto asyncBuf(S)(S source, size_t bufSize = 100) if (isInputRange!S)
@@ -2278,7 +2284,8 @@ public:
{
assert(nextBufTask.prev is null);
assert(nextBufTask.next is null);
- } body
+ }
+ do
{
// Hack to reuse the task object.
@@ -2351,30 +2358,30 @@ public:
}
/**
- Given a callable object $(D next) that writes to a user-provided buffer and
- a second callable object $(D empty) that determines whether more data is
- available to write via $(D next), returns an input range that
- asynchronously calls $(D next) with a set of size $(D nBuffers) of buffers
+ Given a callable object `next` that writes to a user-provided buffer and
+ a second callable object `empty` that determines whether more data is
+ available to write via `next`, returns an input range that
+ asynchronously calls `next` with a set of size `nBuffers` of buffers
and makes the results available in the order they were obtained via the
input range interface of the returned object. Similarly to the
- input range overload of $(D asyncBuf), the first half of the buffers
+ input range overload of `asyncBuf`, the first half of the buffers
are made available via the range interface while the second half are
filled and vice-versa.
Params:
next = A callable object that takes a single argument that must be an array
- with mutable elements. When called, $(D next) writes data to
+ with mutable elements. When called, `next` writes data to
the array provided by the caller.
empty = A callable object that takes no arguments and returns a type
- implicitly convertible to $(D bool). This is used to signify
- that no more data is available to be obtained by calling $(D next).
+ implicitly convertible to `bool`. This is used to signify
+ that no more data is available to be obtained by calling `next`.
- initialBufSize = The initial size of each buffer. If $(D next) takes its
+ initialBufSize = The initial size of each buffer. If `next` takes its
array by reference, it may resize the buffers.
- nBuffers = The number of buffers to cycle through when calling $(D next).
+ nBuffers = The number of buffers to cycle through when calling `next`.
Example:
---
@@ -2403,8 +2410,8 @@ public:
$(B Exception Handling):
- Any exceptions thrown while iterating over $(D range) are re-thrown on a
- call to $(D popFront).
+ Any exceptions thrown while iterating over `range` are re-thrown on a
+ call to `popFront`.
Warning:
@@ -2428,23 +2435,26 @@ public:
{
/**
Parallel reduce on a random access range. Except as otherwise noted,
- usage is similar to $(REF _reduce, std,algorithm,iteration). This
- function works by splitting the range to be reduced into work units,
- which are slices to be reduced in parallel. Once the results from all
- work units are computed, a final serial reduction is performed on these
- results to compute the final answer. Therefore, care must be taken to
- choose the seed value appropriately.
-
- Because the reduction is being performed in parallel, $(D functions)
+ usage is similar to $(REF _reduce, std,algorithm,iteration). There is
+ also $(LREF fold) which does the same thing with a different parameter
+ order.
+
+ This function works by splitting the range to be reduced into work
+ units, which are slices to be reduced in parallel. Once the results
+ from all work units are computed, a final serial reduction is performed
+ on these results to compute the final answer. Therefore, care must be
+ taken to choose the seed value appropriately.
+
+ Because the reduction is being performed in parallel, `functions`
must be associative. For notational simplicity, let # be an
- infix operator representing $(D functions). Then, (a # b) # c must equal
+ infix operator representing `functions`. Then, (a # b) # c must equal
a # (b # c). Floating point addition is not associative
even though addition in exact arithmetic is. Summing floating
point numbers using this function may give different results than summing
serially. However, for many practical purposes floating point addition
can be treated as associative.
- Note that, since $(D functions) are assumed to be associative,
+ Note that, since `functions` are assumed to be associative,
additional optimizations are made to the serial portion of the reduction
algorithm. These take advantage of the instruction level parallelism of
modern CPUs, in addition to the thread-level parallelism that the rest
@@ -2485,7 +2495,7 @@ public:
An explicit work unit size may be specified as the last argument.
Specifying too small a work unit size will effectively serialize the
reduction, as the final reduction of the result of each work unit will
- dominate computation time. If $(D TaskPool.size) for this instance
+ dominate computation time. If `TaskPool.size` for this instance
is zero, this parameter is ignored and one work unit is used.
---
// Use a work unit size of 100.
@@ -2496,7 +2506,7 @@ public:
---
Parallel reduce supports multiple functions, like
- $(D std.algorithm.reduce).
+ `std.algorithm.reduce`.
---
// Find both the min and max of nums.
auto minMax = taskPool.reduce!(min, max)(nums);
@@ -2507,13 +2517,19 @@ public:
$(B Exception Handling):
After this function is finished executing, any exceptions thrown
- are chained together via $(D Throwable.next) and rethrown. The chaining
+ are chained together via `Throwable.next` and rethrown. The chaining
order is non-deterministic.
+
+ See_Also:
+
+ $(LREF fold) is functionally equivalent to $(LREF _reduce) except the
+ range parameter comes first and there is no need to use
+ $(REF_ALTTEXT `tuple`,tuple,std,typecons) for multiple seeds.
*/
auto reduce(Args...)(Args args)
{
import core.exception : OutOfMemoryError;
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
import std.exception : enforce;
alias fun = reduceAdjoin!functions;
@@ -2685,8 +2701,8 @@ public:
alias RTask = Task!(run, typeof(&reduceOnRange), R, size_t, size_t);
RTask[] tasks;
- // Can't use alloca() due to Bug 3753. Use a fixed buffer
- // backed by malloc().
+ // Can't use alloca() due to https://issues.dlang.org/show_bug.cgi?id=3753
+ // Use a fixed buffer backed by malloc().
enum maxStack = 2_048;
byte[maxStack] buf = void;
immutable size_t nBytesNeeded = nWorkUnits * RTask.sizeof;
@@ -2787,7 +2803,7 @@ public:
// done or in progress. Force all of them.
E result = seed;
- Throwable firstException, lastException;
+ Throwable firstException;
foreach (ref task; tasks)
{
@@ -2797,7 +2813,10 @@ public:
}
catch (Throwable e)
{
- addToChain(e, firstException, lastException);
+ /* Chain e to front because order doesn't matter and because
+ * e is not likely to be a chain itself (so fewer traversals)
+ */
+ firstException = Throwable.chainTogether(e, firstException);
continue;
}
@@ -2810,10 +2829,132 @@ public:
}
}
+ ///
+ template fold(functions...)
+ {
+ /** Implements the homonym function (also known as `accumulate`, `compress`,
+ `inject`, or `foldl`) present in various programming languages of
+ functional flavor.
+
+ `fold` is functionally equivalent to $(LREF reduce) except the range
+ parameter comes first and there is no need to use $(REF_ALTTEXT
+ `tuple`,tuple,std,typecons) for multiple seeds.
+
+ There may be one or more callable entities (`functions` argument) to
+ apply.
+
+ Params:
+ args = Just the range to _fold over; or the range and one seed
+ per function; or the range, one seed per function, and
+ the work unit size
+
+ Returns:
+ The accumulated result as a single value for single function and
+ as a tuple of values for multiple functions
+
+ See_Also:
+ Similar to $(REF _fold, std,algorithm,iteration), `fold` is a wrapper around $(LREF reduce).
+
+ Example:
+ ---
+ static int adder(int a, int b)
+ {
+ return a + b;
+ }
+ static int multiplier(int a, int b)
+ {
+ return a * b;
+ }
+
+ // Just the range
+ auto x = taskPool.fold!adder([1, 2, 3, 4]);
+ assert(x == 10);
+
+ // The range and the seeds (0 and 1 below; also note multiple
+ // functions in this example)
+ auto y = taskPool.fold!(adder, multiplier)([1, 2, 3, 4], 0, 1);
+ assert(y[0] == 10);
+ assert(y[1] == 24);
+
+ // The range, the seed (0), and the work unit size (20)
+ auto z = taskPool.fold!adder([1, 2, 3, 4], 0, 20);
+ assert(z == 10);
+ ---
+ */
+ auto fold(Args...)(Args args)
+ {
+ static assert(isInputRange!(Args[0]), "First argument must be an InputRange");
+
+ alias range = args[0];
+
+ static if (Args.length == 1)
+ {
+ // Just the range
+ return reduce!functions(range);
+ }
+ else static if (Args.length == 1 + functions.length ||
+ Args.length == 1 + functions.length + 1)
+ {
+ static if (functions.length == 1)
+ {
+ alias seeds = args[1];
+ }
+ else
+ {
+ auto seeds()
+ {
+ import std.typecons : tuple;
+ return tuple(args[1 .. functions.length+1]);
+ }
+ }
+
+ static if (Args.length == 1 + functions.length)
+ {
+ // The range and the seeds
+ return reduce!functions(seeds, range);
+ }
+ else static if (Args.length == 1 + functions.length + 1)
+ {
+ // The range, the seeds, and the work unit size
+ static assert(isIntegral!(Args[$-1]), "Work unit size must be an integral type");
+ return reduce!functions(seeds, range, args[$-1]);
+ }
+ }
+ else
+ {
+ import std.conv : text;
+ static assert(0, "Invalid number of arguments (" ~ Args.length.text ~ "): Should be an input range, "
+ ~ functions.length.text ~ " optional seed(s), and an optional work unit size.");
+ }
+ }
+ }
+
+ // This test is not included in the documentation because even though these
+ // examples are for the inner fold() template, with their current location,
+ // they would appear under the outer one. (We can't move this inside the
+ // outer fold() template because then dmd runs out of memory possibly due to
+ // recursive template instantiation, which is surprisingly not caught.)
+ @system unittest
+ {
+ // Just the range
+ auto x = taskPool.fold!"a + b"([1, 2, 3, 4]);
+ assert(x == 10);
+
+ // The range and the seeds (0 and 1 below; also note multiple
+ // functions in this example)
+ auto y = taskPool.fold!("a + b", "a * b")([1, 2, 3, 4], 0, 1);
+ assert(y[0] == 10);
+ assert(y[1] == 24);
+
+ // The range, the seed (0), and the work unit size (20)
+ auto z = taskPool.fold!"a + b"([1, 2, 3, 4], 0, 20);
+ assert(z == 10);
+ }
+
/**
- Gets the index of the current thread relative to this $(D TaskPool). Any
+ Gets the index of the current thread relative to this `TaskPool`. Any
thread not in this pool will receive an index of 0. The worker threads in
- this pool receive unique indices of 1 through $(D this.size).
+ this pool receive unique indices of 1 through `this.size`.
This function is useful for maintaining worker-local resources.
@@ -2860,22 +3001,22 @@ public:
/**
Struct for creating worker-local storage. Worker-local storage is
thread-local storage that exists only for worker threads in a given
- $(D TaskPool) plus a single thread outside the pool. It is allocated on the
+ `TaskPool` plus a single thread outside the pool. It is allocated on the
garbage collected heap in a way that avoids _false sharing, and doesn't
necessarily have global scope within any thread. It can be accessed from
- any worker thread in the $(D TaskPool) that created it, and one thread
- outside this $(D TaskPool). All threads outside the pool that created a
+ any worker thread in the `TaskPool` that created it, and one thread
+ outside this `TaskPool`. All threads outside the pool that created a
given instance of worker-local storage share a single slot.
Since the underlying data for this struct is heap-allocated, this struct
has reference semantics when passed between functions.
- The main uses cases for $(D WorkerLocalStorageStorage) are:
+ The main uses cases for `WorkerLocalStorageStorage` are:
1. Performing parallel reductions with an imperative, as opposed to
- functional, programming style. In this case, it's useful to treat
- $(D WorkerLocalStorageStorage) as local to each thread for only the parallel
- portion of an algorithm.
+ functional, programming style. In this case, it's useful to treat
+ `WorkerLocalStorageStorage` as local to each thread for only the parallel
+ portion of an algorithm.
2. Recycling temporary buffers across iterations of a parallel foreach loop.
@@ -2971,13 +3112,13 @@ public:
public:
/**
Get the current thread's instance. Returns by ref.
- Note that calling $(D get) from any thread
- outside the $(D TaskPool) that created this instance will return the
+ Note that calling `get` from any thread
+ outside the `TaskPool` that created this instance will return the
same reference, so an instance of worker-local storage should only be
accessed from one thread outside the pool that created it. If this
rule is violated, undefined behavior will result.
- If assertions are enabled and $(D toRange) has been called, then this
+ If assertions are enabled and `toRange` has been called, then this
WorkerLocalStorage instance is no longer worker-local and an assertion
failure will result when calling this method. This is not checked
when assertions are disabled for performance reasons.
@@ -3012,7 +3153,7 @@ public:
of your algorithm.
Calling this function sets a flag indicating that this struct is no
- longer worker-local, and attempting to use the $(D get) method again
+ longer worker-local, and attempting to use the `get` method again
will result in an assertion failure if assertions are enabled.
*/
WorkerLocalStorageRange!T toRange() @property
@@ -3042,9 +3183,9 @@ public:
Do not use this struct in the parallel portion of your algorithm.
The proper way to instantiate this object is to call
- $(D WorkerLocalStorage.toRange). Once instantiated, this object behaves
+ `WorkerLocalStorage.toRange`. Once instantiated, this object behaves
as a finite random-access range with assignable, lvalue elements and
- a length equal to the number of worker threads in the $(D TaskPool) that
+ a length equal to the number of worker threads in the `TaskPool` that
created it plus 1.
*/
static struct WorkerLocalStorageRange(T)
@@ -3128,9 +3269,9 @@ public:
/**
Creates an instance of worker-local storage, initialized with a given
- value. The value is $(D lazy) so that you can, for example, easily
+ value. The value is `lazy` so that you can, for example, easily
create one instance of a class for each worker. For usage example,
- see the $(D WorkerLocalStorage) struct.
+ see the `WorkerLocalStorage` struct.
*/
WorkerLocalStorage!T workerLocalStorage(T)(lazy T initialVal = T.init)
{
@@ -3151,12 +3292,12 @@ public:
/**
Signals to all worker threads to terminate as soon as they are finished
- with their current $(D Task), or immediately if they are not executing a
- $(D Task). $(D Task)s that were in queue will not be executed unless
- a call to $(D Task.workForce), $(D Task.yieldForce) or $(D Task.spinForce)
+ with their current `Task`, or immediately if they are not executing a
+ `Task`. `Task`s that were in queue will not be executed unless
+ a call to `Task.workForce`, `Task.yieldForce` or `Task.spinForce`
causes them to be executed.
- Use only if you have waited on every $(D Task) and therefore know the
+ Use only if you have waited on every `Task` and therefore know the
queue is empty, or if you speculatively executed some tasks and no longer
need the results.
*/
@@ -3173,13 +3314,13 @@ public:
If blocking argument is true, wait for all worker threads to terminate
before returning. This option might be used in applications where
- task results are never consumed-- e.g. when $(D TaskPool) is employed as a
+ task results are never consumed-- e.g. when `TaskPool` is employed as a
rudimentary scheduler for tasks which communicate by means other than
return values.
Warning: Calling this function with $(D blocking = true) from a worker
- thread that is a member of the same $(D TaskPool) that
- $(D finish) is being called on will result in a deadlock.
+ thread that is a member of the same `TaskPool` that
+ `finish` is being called on will result in a deadlock.
*/
void finish(bool blocking = false) @trusted
{
@@ -3218,7 +3359,7 @@ public:
}
/**
- Put a $(D Task) object on the back of the task queue. The $(D Task)
+ Put a `Task` object on the back of the task queue. The `Task`
object may be passed by pointer or reference.
Example:
@@ -3234,20 +3375,20 @@ public:
Notes:
- @trusted overloads of this function are called for $(D Task)s if
- $(REF hasUnsharedAliasing, std,traits) is false for the $(D Task)'s
- return type or the function the $(D Task) executes is $(D pure).
- $(D Task) objects that meet all other requirements specified in the
- $(D @trusted) overloads of $(D task) and $(D scopedTask) may be created
- and executed from $(D @safe) code via $(D Task.executeInNewThread) but
- not via $(D TaskPool).
+ @trusted overloads of this function are called for `Task`s if
+ $(REF hasUnsharedAliasing, std,traits) is false for the `Task`'s
+ return type or the function the `Task` executes is `pure`.
+ `Task` objects that meet all other requirements specified in the
+ `@trusted` overloads of `task` and `scopedTask` may be created
+ and executed from `@safe` code via `Task.executeInNewThread` but
+ not via `TaskPool`.
While this function takes the address of variables that may
be on the stack, some overloads are marked as @trusted.
- $(D Task) includes a destructor that waits for the task to complete
+ `Task` includes a destructor that waits for the task to complete
before destroying the stack frame it is allocated on. Therefore,
it is impossible for the stack frame to be destroyed before the task is
- complete and no longer referenced by a $(D TaskPool).
+ complete and no longer referenced by a `TaskPool`.
*/
void put(alias fun, Args...)(ref Task!(fun, Args) task)
if (!isSafeReturn!(typeof(task)))
@@ -3286,11 +3427,11 @@ public:
have terminated. A non-daemon thread will prevent a program from
terminating as long as it has not terminated.
- If any $(D TaskPool) with non-daemon threads is active, either $(D stop)
- or $(D finish) must be called on it before the program can terminate.
+ If any `TaskPool` with non-daemon threads is active, either `stop`
+ or `finish` must be called on it before the program can terminate.
- The worker treads in the $(D TaskPool) instance returned by the
- $(D taskPool) property are daemon by default. The worker threads of
+ The worker treads in the `TaskPool` instance returned by the
+ `taskPool` property are daemon by default. The worker threads of
manually instantiated task pools are non-daemon by default.
Note: For a size zero pool, the getter arbitrarily returns true and the
@@ -3316,12 +3457,12 @@ public:
/**
These functions allow getting and setting the OS scheduling priority of
- the worker threads in this $(D TaskPool). They forward to
- $(D core.thread.Thread.priority), so a given priority value here means the
- same thing as an identical priority value in $(D core.thread).
+ the worker threads in this `TaskPool`. They forward to
+ `core.thread.Thread.priority`, so a given priority value here means the
+ same thing as an identical priority value in `core.thread`.
Note: For a size zero pool, the getter arbitrarily returns
- $(D core.thread.Thread.PRIORITY_MIN) and the setter has no effect.
+ `core.thread.Thread.PRIORITY_MIN` and the setter has no effect.
*/
int priority() @property @trusted
{
@@ -3342,11 +3483,33 @@ public:
}
}
+@system unittest
+{
+ import std.algorithm.iteration : sum;
+ import std.range : iota;
+ import std.typecons : tuple;
+
+ enum N = 100;
+ auto r = iota(1, N + 1);
+ const expected = r.sum();
+
+ // Just the range
+ assert(taskPool.fold!"a + b"(r) == expected);
+
+ // Range and seeds
+ assert(taskPool.fold!"a + b"(r, 0) == expected);
+ assert(taskPool.fold!("a + b", "a + b")(r, 0, 0) == tuple(expected, expected));
+
+ // Range, seeds, and work unit size
+ assert(taskPool.fold!"a + b"(r, 0, 42) == expected);
+ assert(taskPool.fold!("a + b", "a + b")(r, 0, 0, 42) == tuple(expected, expected));
+}
+
/**
-Returns a lazily initialized global instantiation of $(D TaskPool).
+Returns a lazily initialized global instantiation of `TaskPool`.
This function can safely be called concurrently from multiple non-worker
threads. The worker threads in this pool are daemon threads, meaning that it
-is not necessary to call $(D TaskPool.stop) or $(D TaskPool.finish) before
+is not necessary to call `TaskPool.stop` or `TaskPool.finish` before
terminating the main thread.
*/
@property TaskPool taskPool() @trusted
@@ -3363,10 +3526,10 @@ terminating the main thread.
private shared uint _defaultPoolThreads = uint.max;
/**
-These properties get and set the number of worker threads in the $(D TaskPool)
-instance returned by $(D taskPool). The default value is $(D totalCPUs) - 1.
-Calling the setter after the first call to $(D taskPool) does not changes
-number of worker threads in the instance returned by $(D taskPool).
+These properties get and set the number of worker threads in the `TaskPool`
+instance returned by `taskPool`. The default value is `totalCPUs` - 1.
+Calling the setter after the first call to `taskPool` does not changes
+number of worker threads in the instance returned by `taskPool`.
*/
@property uint defaultPoolThreads() @trusted
{
@@ -3381,7 +3544,7 @@ number of worker threads in the instance returned by $(D taskPool).
}
/**
-Convenience functions that forwards to $(D taskPool.parallel). The
+Convenience functions that forwards to `taskPool.parallel`. The
purpose of these is to make parallel foreach less verbose and more
readable.
@@ -3410,6 +3573,23 @@ ParallelForeach!R parallel(R)(R range, size_t workUnitSize)
return taskPool.parallel(range, workUnitSize);
}
+// `each` should be usable with parallel
+// https://issues.dlang.org/show_bug.cgi?id=17019
+@system unittest
+{
+ import std.algorithm.iteration : each, sum;
+ import std.range : iota;
+
+ // check behavior with parallel
+ auto arr = new int[10];
+ parallel(arr).each!((ref e) => e += 1);
+ assert(arr.sum == 10);
+
+ auto arrIndex = new int[10];
+ parallel(arrIndex).each!((i, ref e) => e += i);
+ assert(arrIndex.sum == 10.iota.sum);
+}
+
// Thrown when a parallel foreach loop is broken from.
class ParallelForeachError : Error
{
@@ -3441,9 +3621,10 @@ private void submitAndExecute(
// The logical thing to do would be to just use alloca() here, but that
// causes problems on Windows for reasons that I don't understand
// (tentatively a compiler bug) and definitely doesn't work on Posix due
- // to Bug 3753. Therefore, allocate a fixed buffer and fall back to
- // malloc() if someone's using a ridiculous amount of threads. Also,
- // the using a byte array instead of a PTask array as the fixed buffer
+ // to https://issues.dlang.org/show_bug.cgi?id=3753.
+ // Therefore, allocate a fixed buffer and fall back to `malloc()` if
+ // someone's using a ridiculous amount of threads.
+ // Also, the using a byte array instead of a PTask array as the fixed buffer
// is to prevent d'tors from being called on uninitialized excess PTask
// instances.
enum nBuf = 64;
@@ -3519,7 +3700,7 @@ private void submitAndExecute(
}
}
- Throwable firstException, lastException;
+ Throwable firstException;
foreach (i, ref task; tasks)
{
@@ -3529,7 +3710,10 @@ private void submitAndExecute(
}
catch (Throwable e)
{
- addToChain(e, firstException, lastException);
+ /* Chain e to front because order doesn't matter and because
+ * e is not likely to be a chain itself (so fewer traversals)
+ */
+ firstException = Throwable.chainTogether(e, firstException);
continue;
}
}
@@ -3829,38 +4013,6 @@ enum string parallelApplyMixinInputRange = q{
return 0;
};
-// Calls e.next until the end of the chain is found.
-private Throwable findLastException(Throwable e) pure nothrow
-{
- if (e is null) return null;
-
- while (e.next)
- {
- e = e.next;
- }
-
- return e;
-}
-
-// Adds e to the exception chain.
-private void addToChain(
- Throwable e,
- ref Throwable firstException,
- ref Throwable lastException
-) pure nothrow
-{
- if (firstException)
- {
- assert(lastException); // nocoverage
- lastException.next = e; // nocoverage
- lastException = findLastException(e); // nocoverage
- }
- else
- {
- firstException = e;
- lastException = findLastException(e);
- }
-}
private struct ParallelForeach(R)
{
@@ -3947,7 +4099,7 @@ private struct RoundRobinBuffer(C1, C2)
{
assert(!empty);
}
- body
+ do
{
scope(success) primed = true;
nextDel(bufs[index]);
@@ -3959,7 +4111,7 @@ private struct RoundRobinBuffer(C1, C2)
{
assert(!empty);
}
- body
+ do
{
if (!primed) prime();
return bufs[index];
@@ -3983,12 +4135,10 @@ private struct RoundRobinBuffer(C1, C2)
}
}
-version (unittest)
+version (StdUnittest)
{
// This was the only way I could get nested maps to work.
- __gshared TaskPool poolInstance;
-
- import std.stdio;
+ private __gshared TaskPool poolInstance;
}
// These test basic functionality but don't stress test for threading bugs.
@@ -4000,9 +4150,12 @@ version (unittest)
import std.array : split;
import std.conv : text;
import std.exception : assertThrown;
- import std.math : approxEqual, sqrt, log, abs;
+ import std.math.operations : isClose;
+ import std.math.algebraic : sqrt, abs;
+ import std.math.exponential : log;
import std.range : indexed, iota, join;
import std.typecons : Tuple, tuple;
+ import std.stdio;
poolInstance = new TaskPool(2);
scope(exit) poolInstance.stop();
@@ -4132,7 +4285,7 @@ version (unittest)
foreach (i, elem; logs)
{
- assert(approxEqual(elem, cast(double) log(i + 1)));
+ assert(isClose(elem, cast(double) log(i + 1)));
}
assert(poolInstance.amap!"a * a"([1,2,3,4,5]) == [1,4,9,16,25]);
@@ -4206,7 +4359,7 @@ version (unittest)
pool2.finish(true); // blocking
assert(tSlow2.done);
- // Test fix for Bug 8582 by making pool size zero.
+ // Test fix for https://issues.dlang.org/show_bug.cgi?id=8582 by making pool size zero.
auto pool3 = new TaskPool(0);
auto tSlow3 = task!slowFun();
pool3.put(tSlow3);
@@ -4230,25 +4383,25 @@ version (unittest)
assert(equal(nums, iota(1000)));
assert(equal(
- poolInstance.map!"a * a"(iota(30_000_001), 10_000),
- map!"a * a"(iota(30_000_001))
+ poolInstance.map!"a * a"(iota(3_000_001), 10_000),
+ map!"a * a"(iota(3_000_001))
));
// The filter is to kill random access and test the non-random access
// branch.
assert(equal(
poolInstance.map!"a * a"(
- filter!"a == a"(iota(30_000_001)
+ filter!"a == a"(iota(3_000_001)
), 10_000, 1000),
- map!"a * a"(iota(30_000_001))
+ map!"a * a"(iota(3_000_001))
));
assert(
reduce!"a + b"(0UL,
- poolInstance.map!"a * a"(iota(3_000_001), 10_000)
+ poolInstance.map!"a * a"(iota(300_001), 10_000)
) ==
reduce!"a + b"(0UL,
- map!"a * a"(iota(3_000_001))
+ map!"a * a"(iota(300_001))
)
);
@@ -4311,7 +4464,7 @@ version (unittest)
int ii;
foreach ( elem; (lmchain))
{
- if (!approxEqual(elem, ii))
+ if (!isClose(elem, ii))
{
stderr.writeln(ii, '\t', elem);
}
@@ -4405,8 +4558,12 @@ version (unittest)
// tons of stuff and should not be run every time make unittest is run.
version (parallelismStressTest)
{
- @safe unittest
+ @system unittest
{
+ import std.stdio : stderr, writeln, readln;
+ import std.range : iota;
+ import std.algorithm.iteration : filter, reduce;
+
size_t attempt;
for (; attempt < 10; attempt++)
foreach (poolSize; [0, 4])
@@ -4488,8 +4645,16 @@ version (parallelismStressTest)
// These unittests are intended more for actual testing and not so much
// as examples.
- @safe unittest
- {
+ @system unittest
+ {
+ import std.stdio : stderr;
+ import std.range : iota;
+ import std.algorithm.iteration : filter, reduce;
+ import std.math.algebraic : sqrt;
+ import std.math.operations : isClose;
+ import std.math.traits : isNaN;
+ import std.conv : text;
+
foreach (attempt; 0 .. 10)
foreach (poolSize; [0, 4])
{
@@ -4559,7 +4724,7 @@ version (parallelismStressTest)
foreach (j, elem; row)
{
real shouldBe = sqrt( cast(real) i * j);
- assert(approxEqual(shouldBe, elem));
+ assert(isClose(shouldBe, elem));
sqrtMatrix[i][j] = shouldBe;
}
}
@@ -4586,10 +4751,11 @@ version (parallelismStressTest)
)
);
- assert(approxEqual(sumSqrt, 4.437e8));
+ assert(isClose(sumSqrt, 4.437e8, 1e-2));
stderr.writeln("Done sum of square roots.");
// Test whether tasks work with function pointers.
+ /+ // This part is buggy and needs to be fixed...
auto nanTask = task(&isNaN, 1.0L);
poolInstance.put(nanTask);
assert(nanTask.spinForce == false);
@@ -4616,6 +4782,7 @@ version (parallelismStressTest)
uselessTask.workForce();
}
}
+ +/
// Test the case of non-random access + ref returns.
int[] nums = [1,2,3,4,5];
@@ -4650,9 +4817,9 @@ version (parallelismStressTest)
}
}
-version (unittest)
+@system unittest
{
- struct __S_12733
+ static struct __S_12733
{
invariant() { assert(checksum == 1_234_567_890); }
this(ulong u){n = u;}
@@ -4662,10 +4829,6 @@ version (unittest)
}
static auto __genPair_12733(ulong n) { return __S_12733(n); }
-}
-
-@system unittest
-{
immutable ulong[] data = [ 2UL^^59-1, 2UL^^59-1, 2UL^^59-1, 112_272_537_195_293UL ];
auto result = taskPool.amap!__genPair_12733(data);
diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d
index 4a435efba6c..2d64684b4a2 100644
--- a/libphobos/src/std/path.d
+++ b/libphobos/src/std/path.d
@@ -1,15 +1,15 @@
// Written in the D programming language.
-/** This module is used to manipulate _path strings.
+/** This module is used to manipulate path strings.
All functions, with the exception of $(LREF expandTilde) (and in some
cases $(LREF absolutePath) and $(LREF relativePath)), are pure
string manipulation functions; they don't depend on any state outside
the program, nor do they perform any actual file system actions.
This has the consequence that the module does not make any distinction
- between a _path that points to a directory and a _path that points to a
+ between a path that points to a directory and a path that points to a
file, and it does not know whether or not the object pointed to by the
- _path actually exists in the file system.
+ path actually exists in the file system.
To differentiate between these cases, use $(REF isDir, std,file) and
$(REF exists, std,file).
@@ -21,9 +21,9 @@
In general, the functions in this module assume that the input paths
are well-formed. (That is, they should not contain invalid characters,
- they should follow the file system's _path format, etc.) The result
- of calling a function on an ill-formed _path is undefined. When there
- is a chance that a _path or a file name is invalid (for instance, when it
+ they should follow the file system's path format, etc.) The result
+ of calling a function on an ill-formed path is undefined. When there
+ is a chance that a path or a file name is invalid (for instance, when it
has been input by the user), it may sometimes be desirable to use the
$(LREF isValidFilename) and $(LREF isValidPath) functions to check
this.
@@ -91,29 +91,37 @@ $(TR $(TD Other) $(TD
License:
$(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0)
Source:
- $(PHOBOSSRC std/_path.d)
+ $(PHOBOSSRC std/path.d)
*/
module std.path;
-// FIXME
-import std.file; //: getcwd;
+import std.file : getcwd;
static import std.meta;
import std.range.primitives;
import std.traits;
-version (unittest)
+version (OSX)
+ version = Darwin;
+else version (iOS)
+ version = Darwin;
+else version (TVOS)
+ version = Darwin;
+else version (WatchOS)
+ version = Darwin;
+
+version (StdUnittest)
{
private:
struct TestAliasedString
{
- string get() @safe @nogc pure nothrow { return _s; }
+ string get() @safe @nogc pure nothrow return scope { return _s; }
alias get this;
@disable this(this);
string _s;
}
- bool testAliasedString(alias func, Args...)(string s, Args args)
+ bool testAliasedString(alias func, Args...)(scope string s, scope Args args)
{
return func(TestAliasedString(s), args) == func(s, args);
}
@@ -151,6 +159,21 @@ bool isDirSeparator(dchar c) @safe pure nothrow @nogc
return false;
}
+///
+@safe pure nothrow @nogc unittest
+{
+ version (Windows)
+ {
+ assert( '/'.isDirSeparator);
+ assert( '\\'.isDirSeparator);
+ }
+ else
+ {
+ assert( '/'.isDirSeparator);
+ assert(!'\\'.isDirSeparator);
+ }
+}
+
/* Determines whether the given character is a drive separator.
@@ -201,7 +224,7 @@ version (Windows)
if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
isNarrowString!R)
in { assert(isUNC(path)); }
- body
+ do
{
ptrdiff_t i = 3;
while (i < path.length && !isDirSeparator(path[i])) ++i;
@@ -257,7 +280,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementType!R) ||
}
}
-@system unittest
+@safe unittest
{
import std.array;
import std.utf : byDchar;
@@ -286,7 +309,7 @@ if (isBidirectionalRange!R && isSomeChar!(ElementType!R) ||
}
}
-@system unittest
+@safe unittest
{
import std.array;
import std.utf : byDchar;
@@ -304,7 +327,7 @@ if (isBidirectionalRange!R && isSomeChar!(ElementType!R) ||
return ltrimDirSeparators(rtrimDirSeparators(path));
}
-@system unittest
+@safe unittest
{
import std.array;
import std.utf : byDchar;
@@ -315,10 +338,7 @@ if (isBidirectionalRange!R && isSomeChar!(ElementType!R) ||
assert(trimDirSeparators(MockBiRange!char("//abc//")).array == "abc");
}
-
-
-
-/** This $(D enum) is used as a template argument to functions which
+/** This `enum` is used as a template argument to functions which
compare file names, and determines whether the comparison is
case sensitive or not.
*/
@@ -331,18 +351,28 @@ enum CaseSensitive : bool
yes = true,
/** The default (or most common) setting for the current platform.
- That is, $(D no) on Windows and Mac OS X, and $(D yes) on all
- POSIX systems except OS X (Linux, *BSD, etc.).
+ That is, `no` on Windows and Mac OS X, and `yes` on all
+ POSIX systems except Darwin (Linux, *BSD, etc.).
*/
osDefault = osDefaultCaseSensitivity
}
-version (Windows) private enum osDefaultCaseSensitivity = false;
-else version (OSX) private enum osDefaultCaseSensitivity = false;
-else version (Posix) private enum osDefaultCaseSensitivity = true;
-else static assert(0);
+///
+@safe unittest
+{
+ assert(baseName!(CaseSensitive.no)("dir/file.EXT", ".ext") == "file");
+ assert(baseName!(CaseSensitive.yes)("dir/file.EXT", ".ext") != "file");
+ version (Posix)
+ assert(relativePath!(CaseSensitive.no)("/FOO/bar", "/foo/baz") == "../bar");
+ else
+ assert(relativePath!(CaseSensitive.no)(`c:\FOO\bar`, `c:\foo\baz`) == `..\bar`);
+}
+version (Windows) private enum osDefaultCaseSensitivity = false;
+else version (Darwin) private enum osDefaultCaseSensitivity = false;
+else version (Posix) private enum osDefaultCaseSensitivity = true;
+else static assert(0);
/**
Params:
@@ -353,27 +383,12 @@ else static assert(0);
Returns: The name of the file in the path name, without any leading
directory and with an optional suffix chopped off.
- If $(D suffix) is specified, it will be compared to $(D path)
- using $(D filenameCmp!cs),
- where $(D cs) is an optional template parameter determining whether
+ If `suffix` is specified, it will be compared to `path`
+ using `filenameCmp!cs`,
+ where `cs` is an optional template parameter determining whether
the comparison is case sensitive or not. See the
$(LREF filenameCmp) documentation for details.
- Example:
- ---
- assert(baseName("dir/file.ext") == "file.ext");
- assert(baseName("dir/file.ext", ".ext") == "file");
- assert(baseName("dir/file.ext", ".xyz") == "file.ext");
- assert(baseName("dir/filename", "name") == "file");
- assert(baseName("dir/subdir/") == "subdir");
-
- version (Windows)
- {
- assert(baseName(`d:file.ext`) == "file.ext");
- assert(baseName(`d:\dir\file.ext`) == "file.ext");
- }
- ---
-
Note:
This function $(I only) strips away the specified suffix, which
doesn't necessarily have to represent an extension.
@@ -391,42 +406,22 @@ else static assert(0);
the POSIX requirements for the 'basename' shell utility)
(with suitable adaptations for Windows paths).
*/
-auto baseName(R)(R path)
+auto baseName(R)(return scope R path)
if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) && !isSomeString!R)
{
return _baseName(path);
}
/// ditto
-auto baseName(C)(C[] path)
+auto baseName(C)(return scope C[] path)
if (isSomeChar!C)
{
return _baseName(path);
}
-private R _baseName(R)(R path)
-if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || isNarrowString!R)
-{
- auto p1 = stripDrive(path);
- if (p1.empty)
- {
- version (Windows) if (isUNC(path))
- return path[0 .. 1];
- static if (isSomeString!R)
- return null;
- else
- return p1; // which is empty
- }
-
- auto p2 = rtrimDirSeparators(p1);
- if (p2.empty) return p1[0 .. 1];
-
- return p2[lastSeparator(p2)+1 .. p2.length];
-}
-
/// ditto
inout(C)[] baseName(CaseSensitive cs = CaseSensitive.osDefault, C, C1)
- (inout(C)[] path, in C1[] suffix)
+ (return scope inout(C)[] path, in C1[] suffix)
@safe pure //TODO: nothrow (because of filenameCmp())
if (isSomeChar!C && isSomeChar!C1)
{
@@ -439,6 +434,22 @@ if (isSomeChar!C && isSomeChar!C1)
else return p;
}
+///
+@safe unittest
+{
+ assert(baseName("dir/file.ext") == "file.ext");
+ assert(baseName("dir/file.ext", ".ext") == "file");
+ assert(baseName("dir/file.ext", ".xyz") == "file.ext");
+ assert(baseName("dir/filename", "name") == "file");
+ assert(baseName("dir/subdir/") == "subdir");
+
+ version (Windows)
+ {
+ assert(baseName(`d:file.ext`) == "file.ext");
+ assert(baseName(`d:\dir\file.ext`) == "file.ext");
+ }
+}
+
@safe unittest
{
assert(baseName("").empty);
@@ -511,14 +522,35 @@ if (isSomeChar!C && isSomeChar!C1)
assert(sa.baseName == "test");
}
-/** Returns the directory part of a path. On Windows, this
- includes the drive letter if present.
+private R _baseName(R)(return scope R path)
+if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || isNarrowString!R)
+{
+ auto p1 = stripDrive(path);
+ if (p1.empty)
+ {
+ version (Windows) if (isUNC(path))
+ return path[0 .. 1];
+ static if (isSomeString!R)
+ return null;
+ else
+ return p1; // which is empty
+ }
+
+ auto p2 = rtrimDirSeparators(p1);
+ if (p2.empty) return p1[0 .. 1];
+
+ return p2[lastSeparator(p2)+1 .. p2.length];
+}
+
+/** Returns the parent directory of `path`. On Windows, this
+ includes the drive letter if present. If `path` is a relative path and
+ the parent directory is the current working directory, returns `"."`.
Params:
path = A path name.
Returns:
- A slice of $(D path) or ".".
+ A slice of `path` or `"."`.
Standards:
This function complies with
@@ -526,67 +558,19 @@ if (isSomeChar!C && isSomeChar!C1)
the POSIX requirements for the 'dirname' shell utility)
(with suitable adaptations for Windows paths).
*/
-auto dirName(R)(R path)
+auto dirName(R)(return scope R path)
if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R)
{
return _dirName(path);
}
/// ditto
-auto dirName(C)(C[] path)
+auto dirName(C)(return scope C[] path)
if (isSomeChar!C)
{
return _dirName(path);
}
-private auto _dirName(R)(R path)
-if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
- isNarrowString!R)
-{
- static auto result(bool dot, typeof(path[0 .. 1]) p)
- {
- static if (isSomeString!R)
- return dot ? "." : p;
- else
- {
- import std.range : choose, only;
- return choose(dot, only(cast(ElementEncodingType!R)'.'), p);
- }
- }
-
- if (path.empty)
- return result(true, path[0 .. 0]);
-
- auto p = rtrimDirSeparators(path);
- if (p.empty)
- return result(false, path[0 .. 1]);
-
- version (Windows)
- {
- if (isUNC(p) && uncRootLength(p) == p.length)
- return result(false, p);
-
- if (p.length == 2 && isDriveSeparator(p[1]) && path.length > 2)
- return result(false, path[0 .. 3]);
- }
-
- auto i = lastSeparator(p);
- if (i == -1)
- return result(true, p);
- if (i == 0)
- return result(false, p[0 .. 1]);
-
- version (Windows)
- {
- // If the directory part is either d: or d:\
- // do not chop off the last symbol.
- if (isDriveSeparator(p[i]) || isDriveSeparator(p[i-1]))
- return result(false, p[0 .. i+1]);
- }
- // Remove any remaining trailing (back)slashes.
- return result(false, rtrimDirSeparators(p[0 .. i]));
-}
-
///
@safe unittest
{
@@ -636,7 +620,7 @@ if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementT
assert(sa.dirName == "file/path/to");
}
-@system unittest
+@safe unittest
{
static assert(dirName("dir/file") == "dir");
@@ -678,51 +662,72 @@ if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementT
//static assert(dirName("dir/file".byChar).array == "dir");
}
+private auto _dirName(R)(return scope R path)
+{
+ static auto result(bool dot, typeof(path[0 .. 1]) p)
+ {
+ static if (isSomeString!R)
+ return dot ? "." : p;
+ else
+ {
+ import std.range : choose, only;
+ return choose(dot, only(cast(ElementEncodingType!R)'.'), p);
+ }
+ }
+ if (path.empty)
+ return result(true, path[0 .. 0]);
+
+ auto p = rtrimDirSeparators(path);
+ if (p.empty)
+ return result(false, path[0 .. 1]);
+ version (Windows)
+ {
+ if (isUNC(p) && uncRootLength(p) == p.length)
+ return result(false, p);
-/** Returns the root directory of the specified path, or $(D null) if the
+ if (p.length == 2 && isDriveSeparator(p[1]) && path.length > 2)
+ return result(false, path[0 .. 3]);
+ }
+
+ auto i = lastSeparator(p);
+ if (i == -1)
+ return result(true, p);
+ if (i == 0)
+ return result(false, p[0 .. 1]);
+
+ version (Windows)
+ {
+ // If the directory part is either d: or d:\
+ // do not chop off the last symbol.
+ if (isDriveSeparator(p[i]) || isDriveSeparator(p[i-1]))
+ return result(false, p[0 .. i+1]);
+ }
+ // Remove any remaining trailing (back)slashes.
+ return result(false, rtrimDirSeparators(p[0 .. i]));
+}
+
+/** Returns the root directory of the specified path, or `null` if the
path is not rooted.
Params:
path = A path name.
Returns:
- A slice of $(D path).
+ A slice of `path`.
*/
auto rootName(R)(R path)
-if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
- isNarrowString!R) &&
- !isConvertibleToString!R)
+if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R)
{
- if (path.empty)
- goto Lnull;
-
- version (Posix)
- {
- if (isDirSeparator(path[0])) return path[0 .. 1];
- }
- else version (Windows)
- {
- if (isDirSeparator(path[0]))
- {
- if (isUNC(path)) return path[0 .. uncRootLength(path)];
- else return path[0 .. 1];
- }
- else if (path.length >= 3 && isDriveSeparator(path[1]) &&
- isDirSeparator(path[2]))
- {
- return path[0 .. 3];
- }
- }
- else static assert(0, "unsupported platform");
+ return _rootName(path);
+}
- assert(!isRooted(path));
-Lnull:
- static if (is(StringTypeOf!R))
- return null; // legacy code may rely on null return rather than slice
- else
- return path[0 .. 0];
+/// ditto
+auto rootName(C)(C[] path)
+if (isSomeChar!C)
+{
+ return _rootName(path);
}
///
@@ -745,6 +750,12 @@ Lnull:
@safe unittest
{
assert(testAliasedString!rootName("/foo/bar"));
+
+ enum S : string { a = "/foo/bar" }
+ assert(S.a.rootName == "/");
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.rootName == "/");
}
@safe unittest
@@ -766,12 +777,37 @@ Lnull:
}
}
-auto rootName(R)(R path)
-if (isConvertibleToString!R)
+private auto _rootName(R)(R path)
{
- return rootName!(StringTypeOf!R)(path);
-}
+ if (path.empty)
+ goto Lnull;
+
+ version (Posix)
+ {
+ if (isDirSeparator(path[0])) return path[0 .. 1];
+ }
+ else version (Windows)
+ {
+ if (isDirSeparator(path[0]))
+ {
+ if (isUNC(path)) return path[0 .. uncRootLength(path)];
+ else return path[0 .. 1];
+ }
+ else if (path.length >= 3 && isDriveSeparator(path[1]) &&
+ isDirSeparator(path[2]))
+ {
+ return path[0 .. 3];
+ }
+ }
+ else static assert(0, "unsupported platform");
+ assert(!isRooted(path));
+Lnull:
+ static if (is(StringTypeOf!R))
+ return null; // legacy code may rely on null return rather than slice
+ else
+ return path[0 .. 0];
+}
/**
Get the drive portion of a path.
@@ -780,28 +816,23 @@ if (isConvertibleToString!R)
path = string or range of characters
Returns:
- A slice of $(D _path) that is the drive, or an empty range if the drive
+ A slice of `path` that is the drive, or an empty range if the drive
is not specified. In the case of UNC paths, the network share
is returned.
Always returns an empty range on POSIX.
*/
auto driveName(R)(R path)
-if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
- isNarrowString!R) &&
- !isConvertibleToString!R)
+if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R)
{
- version (Windows)
- {
- if (hasDrive(path))
- return path[0 .. 2];
- else if (isUNC(path))
- return path[0 .. uncRootLength(path)];
- }
- static if (isSomeString!R)
- return cast(ElementEncodingType!R[]) null; // legacy code may rely on null return rather than slice
- else
- return path[0 .. 0];
+ return _driveName(path);
+}
+
+/// ditto
+auto driveName(C)(C[] path)
+if (isSomeChar!C)
+{
+ return _driveName(path);
}
///
@@ -823,15 +854,20 @@ if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(Element
}
}
-auto driveName(R)(auto ref R path)
-if (isConvertibleToString!R)
-{
- return driveName!(StringTypeOf!R)(path);
-}
-
@safe unittest
{
- assert(testAliasedString!driveName(`d:\file`));
+ assert(testAliasedString!driveName("d:/file"));
+
+ version (Posix)
+ immutable result = "";
+ else version (Windows)
+ immutable result = "d:";
+
+ enum S : string { a = "d:/file" }
+ assert(S.a.driveName == result);
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.driveName == result);
}
@safe unittest
@@ -854,6 +890,20 @@ if (isConvertibleToString!R)
}
}
+private auto _driveName(R)(R path)
+{
+ version (Windows)
+ {
+ if (hasDrive(path))
+ return path[0 .. 2];
+ else if (isUNC(path))
+ return path[0 .. uncRootLength(path)];
+ }
+ static if (isSomeString!R)
+ return cast(ElementEncodingType!R[]) null; // legacy code may rely on null return rather than slice
+ else
+ return path[0 .. 0];
+}
/** Strips the drive from a Windows path. On POSIX, the path is returned
unaltered.
@@ -864,16 +914,16 @@ if (isConvertibleToString!R)
Returns: A slice of path without the drive component.
*/
auto stripDrive(R)(R path)
-if ((isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) ||
- isNarrowString!R) &&
- !isConvertibleToString!R)
+if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) && !isSomeString!R)
{
- version (Windows)
- {
- if (hasDrive!(BaseOf!R)(path)) return path[2 .. path.length];
- else if (isUNC!(BaseOf!R)(path)) return path[uncRootLength!(BaseOf!R)(path) .. path.length];
- }
- return path;
+ return _stripDrive(path);
+}
+
+/// ditto
+auto stripDrive(C)(C[] path)
+if (isSomeChar!C)
+{
+ return _stripDrive(path);
}
///
@@ -886,15 +936,20 @@ if ((isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) ||
}
}
-auto stripDrive(R)(auto ref R path)
-if (isConvertibleToString!R)
-{
- return stripDrive!(StringTypeOf!R)(path);
-}
-
@safe unittest
{
- assert(testAliasedString!stripDrive(`d:\dir\file`));
+ assert(testAliasedString!stripDrive("d:/dir/file"));
+
+ version (Posix)
+ immutable result = "d:/dir/file";
+ else version (Windows)
+ immutable result = "/dir/file";
+
+ enum S : string { a = "d:/dir/file" }
+ assert(S.a.stripDrive == result);
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.stripDrive == result);
}
@safe unittest
@@ -921,6 +976,16 @@ if (isConvertibleToString!R)
}
}
+private auto _stripDrive(R)(R path)
+{
+ version (Windows)
+ {
+ if (hasDrive!(BaseOf!R)(path)) return path[2 .. path.length];
+ else if (isUNC!(BaseOf!R)(path)) return path[uncRootLength!(BaseOf!R)(path) .. path.length];
+ }
+ return path;
+}
+
/* Helper function that returns the position of the filename/extension
separator dot in path.
@@ -983,7 +1048,7 @@ if (isRandomAccessRange!R && hasLength!R && isSomeChar!(ElementType!R) ||
Params: path = A path name.
Returns: The _extension part of a file name, including the dot.
- If there is no _extension, $(D null) is returned.
+ If there is no _extension, `null` is returned.
*/
auto extension(R)(R path)
if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) ||
@@ -1038,12 +1103,16 @@ if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) ||
slice of path with the extension (if any) stripped off
*/
auto stripExtension(R)(R path)
-if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
- isNarrowString!R) &&
- !isConvertibleToString!R)
+if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R)
+{
+ return _stripExtension(path);
+}
+
+/// Ditto
+auto stripExtension(C)(C[] path)
+if (isSomeChar!C)
{
- auto i = extSeparatorPos(path);
- return (i == -1) ? path : path[0 .. i];
+ return _stripExtension(path);
}
///
@@ -1058,15 +1127,15 @@ if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(Element
assert(stripExtension("dir/file.ext") == "dir/file");
}
-auto stripExtension(R)(auto ref R path)
-if (isConvertibleToString!R)
-{
- return stripExtension!(StringTypeOf!R)(path);
-}
-
@safe unittest
{
assert(testAliasedString!stripExtension("file"));
+
+ enum S : string { a = "foo.bar" }
+ assert(S.a.stripExtension == "foo");
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.stripExtension == "foo");
}
@safe unittest
@@ -1082,12 +1151,17 @@ if (isConvertibleToString!R)
assert(stripExtension("file.ext1.ext2"d.byDchar).array == "file.ext1");
}
+private auto _stripExtension(R)(R path)
+{
+ immutable i = extSeparatorPos(path);
+ return i == -1 ? path : path[0 .. i];
+}
/** Sets or replaces an extension.
If the filename already has an extension, it is replaced. If not, the
extension is simply appended to the filename. Including a leading dot
- in $(D ext) is optional.
+ in `ext` is optional.
If the extension is empty, this function is equivalent to
$(LREF stripExtension).
@@ -1100,14 +1174,14 @@ if (isConvertibleToString!R)
path = A path name
ext = The new extension
- Returns: A string containing the _path given by $(D path), but where
- the extension has been set to $(D ext).
+ Returns: A string containing the path given by `path`, but where
+ the extension has been set to `ext`.
See_Also:
$(LREF withExtension) which does not allocate and returns a lazy range.
*/
-immutable(Unqual!C1)[] setExtension(C1, C2)(in C1[] path, in C2[] ext)
-if (isSomeChar!C1 && !is(C1 == immutable) && is(Unqual!C1 == Unqual!C2))
+immutable(C1)[] setExtension(C1, C2)(in C1[] path, in C2[] ext)
+if (isSomeChar!C1 && !is(C1 == immutable) && is(immutable C1 == immutable C2))
{
try
{
@@ -1122,7 +1196,7 @@ if (isSomeChar!C1 && !is(C1 == immutable) && is(Unqual!C1 == Unqual!C2))
///ditto
immutable(C1)[] setExtension(C1, C2)(immutable(C1)[] path, const(C2)[] ext)
-if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2))
+if (isSomeChar!C1 && is(immutable C1 == immutable C2))
{
if (ext.length == 0)
return stripExtension(path);
@@ -1164,7 +1238,7 @@ if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2))
static assert(setExtension("file"w.dup, "ext"w) == "file.ext");
static assert(setExtension("file.old"d.dup, "new"d) == "file.new");
- // Issue 10601
+ // https://issues.dlang.org/show_bug.cgi?id=10601
assert(setExtension("file", "") == "file");
assert(setExtension("file.ext", "") == "file");
}
@@ -1176,25 +1250,23 @@ if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2))
* path = string or random access range representing a filespec
* ext = the new extension
* Returns:
- * Range with $(D path)'s extension (if any) replaced with $(D ext).
- * The element encoding type of the returned range will be the same as $(D path)'s.
+ * Range with `path`'s extension (if any) replaced with `ext`.
+ * The element encoding type of the returned range will be the same as `path`'s.
* See_Also:
* $(LREF setExtension)
*/
auto withExtension(R, C)(R path, C[] ext)
-if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
- isNarrowString!R) &&
- !isConvertibleToString!R &&
- isSomeChar!C)
+if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) &&
+ !isSomeString!R && isSomeChar!C)
{
- import std.range : only, chain;
- import std.utf : byUTF;
+ return _withExtension(path, ext);
+}
- alias CR = Unqual!(ElementEncodingType!R);
- auto dot = only(CR('.'));
- if (ext.length == 0 || ext[0] == '.')
- dot.popFront(); // so dot is an empty range, too
- return chain(stripExtension(path).byUTF!CR, dot, ext.byUTF!CR);
+/// Ditto
+auto withExtension(C1, C2)(C1[] path, C2[] ext)
+if (isSomeChar!C1 && isSomeChar!C2)
+{
+ return _withExtension(path, ext);
}
///
@@ -1211,22 +1283,36 @@ if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(Element
assert(withExtension("file.ext"w.byWchar, ".").array == "file."w);
}
-auto withExtension(R, C)(auto ref R path, C[] ext)
-if (isConvertibleToString!R)
+@safe unittest
{
- return withExtension!(StringTypeOf!R)(path, ext);
+ import std.algorithm.comparison : equal;
+
+ assert(testAliasedString!withExtension("file", "ext"));
+
+ enum S : string { a = "foo.bar" }
+ assert(equal(S.a.withExtension(".txt"), "foo.txt"));
+
+ char[S.a.length] sa = S.a[];
+ assert(equal(sa.withExtension(".txt"), "foo.txt"));
}
-@safe unittest
+private auto _withExtension(R, C)(R path, C[] ext)
{
- assert(testAliasedString!withExtension("file", "ext"));
+ import std.range : only, chain;
+ import std.utf : byUTF;
+
+ alias CR = Unqual!(ElementEncodingType!R);
+ auto dot = only(CR('.'));
+ if (ext.length == 0 || ext[0] == '.')
+ dot.popFront(); // so dot is an empty range, too
+ return chain(stripExtension(path).byUTF!CR, dot, ext.byUTF!CR);
}
/** Params:
path = A path name.
ext = The default extension to use.
- Returns: The _path given by $(D path), with the extension given by $(D ext)
+ Returns: The path given by `path`, with the extension given by `ext`
appended if the path doesn't already have one.
Including the dot in the extension is optional.
@@ -1234,8 +1320,8 @@ if (isConvertibleToString!R)
This function always allocates a new string, except in the case when
path is immutable and already has an extension.
*/
-immutable(Unqual!C1)[] defaultExtension(C1, C2)(in C1[] path, in C2[] ext)
-if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2))
+immutable(C1)[] defaultExtension(C1, C2)(in C1[] path, in C2[] ext)
+if (isSomeChar!C1 && is(immutable C1 == immutable C2))
{
import std.conv : to;
return withDefaultExtension(path, ext).to!(typeof(return));
@@ -1265,7 +1351,7 @@ if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2))
/********************************
- * Set the extension of $(D path) to $(D ext) if $(D path) doesn't have one.
+ * Set the extension of `path` to `ext` if `path` doesn't have one.
*
* Params:
* path = filespec as string or range
@@ -1274,29 +1360,17 @@ if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2))
* range with the result
*/
auto withDefaultExtension(R, C)(R path, C[] ext)
-if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
- isNarrowString!R) &&
- !isConvertibleToString!R &&
- isSomeChar!C)
+if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) &&
+ !isSomeString!R && isSomeChar!C)
{
- import std.range : only, chain;
- import std.utf : byUTF;
+ return _withDefaultExtension(path, ext);
+}
- alias CR = Unqual!(ElementEncodingType!R);
- auto dot = only(CR('.'));
- auto i = extSeparatorPos(path);
- if (i == -1)
- {
- if (ext.length > 0 && ext[0] == '.')
- ext = ext[1 .. $]; // remove any leading . from ext[]
- }
- else
- {
- // path already has an extension, so make these empty
- ext = ext[0 .. 0];
- dot.popFront();
- }
- return chain(path.byUTF!CR, dot, ext.byUTF!CR);
+/// Ditto
+auto withDefaultExtension(C1, C2)(C1[] path, C2[] ext)
+if (isSomeChar!C1 && isSomeChar!C2)
+{
+ return _withDefaultExtension(path, ext);
}
///
@@ -1315,15 +1389,39 @@ if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(Element
assert(withDefaultExtension("file".byChar, "").array == "file.");
}
-auto withDefaultExtension(R, C)(auto ref R path, C[] ext)
-if (isConvertibleToString!R)
+@safe unittest
{
- return withDefaultExtension!(StringTypeOf!R, C)(path, ext);
+ import std.algorithm.comparison : equal;
+
+ assert(testAliasedString!withDefaultExtension("file", "ext"));
+
+ enum S : string { a = "foo" }
+ assert(equal(S.a.withDefaultExtension(".txt"), "foo.txt"));
+
+ char[S.a.length] sa = S.a[];
+ assert(equal(sa.withDefaultExtension(".txt"), "foo.txt"));
}
-@safe unittest
+private auto _withDefaultExtension(R, C)(R path, C[] ext)
{
- assert(testAliasedString!withDefaultExtension("file", "ext"));
+ import std.range : only, chain;
+ import std.utf : byUTF;
+
+ alias CR = Unqual!(ElementEncodingType!R);
+ auto dot = only(CR('.'));
+ immutable i = extSeparatorPos(path);
+ if (i == -1)
+ {
+ if (ext.length > 0 && ext[0] == '.')
+ ext = ext[1 .. $]; // remove any leading . from ext[]
+ }
+ else
+ {
+ // path already has an extension, so make these empty
+ ext = ext[0 .. 0];
+ dot.popFront();
+ }
+ return chain(path.byUTF!CR, dot, ext.byUTF!CR);
}
/** Combines one or more path segments.
@@ -1341,15 +1439,16 @@ if (isConvertibleToString!R)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
- allocation, as is the range version if $(D paths) is a forward
+ allocation, as is the range version if `paths` is a forward
range.
Params:
- segments = An input range of segments to assemble the path from.
+ segments = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ of segments to assemble the path from.
Returns: The assembled path.
*/
immutable(ElementEncodingType!(ElementType!Range))[]
- buildPath(Range)(Range segments)
+ buildPath(Range)(scope Range segments)
if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range))
{
if (segments.empty) return null;
@@ -1638,7 +1737,7 @@ if (Ranges.length >= 2 &&
/** Performs the same task as $(LREF buildPath),
while at the same time resolving current/parent directory
- symbols ($(D ".") and $(D "..")) and removing superfluous
+ symbols (`"."` and `".."`) and removing superfluous
directory separators.
It will return "." if the path leads to the starting directory.
On Windows, slashes are replaced with backslashes.
@@ -1656,21 +1755,23 @@ if (Ranges.length >= 2 &&
Returns: The assembled path.
*/
immutable(C)[] buildNormalizedPath(C)(const(C[])[] paths...)
- @trusted pure nothrow
+ @safe pure nothrow
if (isSomeChar!C)
{
import std.array : array;
+ import std.exception : assumeUnique;
- const(C)[] result;
+ const(C)[] chained;
foreach (path; paths)
{
- if (result)
- result = chainPath(result, path).array;
+ if (chained)
+ chained = chainPath(chained, path).array;
else
- result = path;
+ chained = path;
}
- result = asNormalizedPath(result).array;
- return cast(typeof(return)) result;
+ auto result = asNormalizedPath(chained);
+ // .array returns a copy, so it is unique
+ return () @trusted { return assumeUnique(result.array); } ();
}
///
@@ -1790,7 +1891,7 @@ if (isSomeChar!C)
/** Normalize a path by resolving current/parent directory
- symbols ($(D ".") and $(D "..")) and removing superfluous
+ symbols (`"."` and `".."`) and removing superfluous
directory separators.
It will return "." if the path leads to the starting directory.
On Windows, slashes are replaced with backslashes.
@@ -1803,13 +1904,13 @@ if (isSomeChar!C)
Use $(LREF buildNormalizedPath) to allocate memory and return a string.
Params:
- path = string or random access range representing the _path to normalize
+ path = string or random access range representing the path to normalize
Returns:
normalized path as a forward range
*/
-auto asNormalizedPath(R)(R path)
+auto asNormalizedPath(R)(return scope R path)
if (isSomeChar!(ElementEncodingType!R) &&
(isRandomAccessRange!R && hasSlicing!R && hasLength!R || isNarrowString!R) &&
!isConvertibleToString!R)
@@ -1976,7 +2077,7 @@ if (isSomeChar!(ElementEncodingType!R) &&
}
}
-auto asNormalizedPath(R)(auto ref R path)
+auto asNormalizedPath(R)(return scope auto ref R path)
if (isConvertibleToString!R)
{
return asNormalizedPath!(StringTypeOf!R)(path);
@@ -2460,35 +2561,17 @@ if (isConvertibleToString!R)
/** Determines whether a path starts at a root directory.
- Params: path = A path name.
- Returns: Whether a path starts at a root directory.
+Params:
+ path = A path name.
+Returns:
+ Whether a path starts at a root directory.
On POSIX, this function returns true if and only if the path starts
with a slash (/).
- ---
- version (Posix)
- {
- assert(isRooted("/"));
- assert(isRooted("/foo"));
- assert(!isRooted("foo"));
- assert(!isRooted("../foo"));
- }
- ---
On Windows, this function returns true if the path starts at
the root directory of the current drive, of some other drive,
or of a network drive.
- ---
- version (Windows)
- {
- assert(isRooted(`\`));
- assert(isRooted(`\foo`));
- assert(isRooted(`d:\foo`));
- assert(isRooted(`\\foo\bar`));
- assert(!isRooted("foo"));
- assert(!isRooted("d:foo"));
- }
- ---
*/
bool isRooted(R)(R path)
if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
@@ -2499,6 +2582,27 @@ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
else version (Windows) return isAbsolute!(BaseOf!R)(path);
}
+///
+@safe unittest
+{
+ version (Posix)
+ {
+ assert( isRooted("/"));
+ assert( isRooted("/foo"));
+ assert(!isRooted("foo"));
+ assert(!isRooted("../foo"));
+ }
+
+ version (Windows)
+ {
+ assert( isRooted(`\`));
+ assert( isRooted(`\foo`));
+ assert( isRooted(`d:\foo`));
+ assert( isRooted(`\\foo\bar`));
+ assert(!isRooted("foo"));
+ assert(!isRooted("d:foo"));
+ }
+}
@safe unittest
{
@@ -2524,9 +2628,6 @@ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
assert(!isRooted(DirEntry("foo")));
}
-
-
-
/** Determines whether a path is absolute or not.
Params: path = A path name.
@@ -2535,7 +2636,7 @@ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
Example:
On POSIX, an absolute path starts at the root directory.
- (In fact, $(D _isAbsolute) is just an alias for $(LREF isRooted).)
+ (In fact, `_isAbsolute` is just an alias for $(LREF isRooted).)
---
version (Posix)
{
@@ -2548,7 +2649,7 @@ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
On Windows, an absolute path starts at the root directory of
a specific drive. Hence, it must start with $(D `d:\`) or $(D `d:/`),
- where $(D d) is the drive letter. Alternatively, it may be a
+ where `d` is the drive letter. Alternatively, it may be a
network path, i.e. a path starting with a double (back)slash.
---
version (Windows)
@@ -2620,14 +2721,14 @@ else version (Posix)
-/** Transforms $(D path) into an absolute _path.
+/** Transforms `path` into an absolute path.
The following algorithm is used:
$(OL
- $(LI If $(D path) is empty, return $(D null).)
- $(LI If $(D path) is already absolute, return it.)
- $(LI Otherwise, append $(D path) to $(D base) and return
- the result. If $(D base) is not specified, the current
+ $(LI If `path` is empty, return `null`.)
+ $(LI If `path` is already absolute, return it.)
+ $(LI Otherwise, append `path` to `base` and return
+ the result. If `base` is not specified, the current
working directory is used.)
)
The function allocates memory if and only if it gets to the third stage
@@ -2641,7 +2742,7 @@ else version (Posix)
string of transformed path
Throws:
- $(D Exception) if the specified _base directory is not absolute.
+ `Exception` if the specified _base directory is not absolute.
See_Also:
$(LREF asAbsolutePath) which does not allocate
@@ -2693,13 +2794,13 @@ string absolutePath(string path, lazy string base = getcwd())
assertThrown(absolutePath("bar", "foo"));
}
-/** Transforms $(D path) into an absolute _path.
+/** Transforms `path` into an absolute path.
The following algorithm is used:
$(OL
- $(LI If $(D path) is empty, return $(D null).)
- $(LI If $(D path) is already absolute, return it.)
- $(LI Otherwise, append $(D path) to the current working directory,
+ $(LI If `path` is empty, return `null`.)
+ $(LI If `path` is already absolute, return it.)
+ $(LI Otherwise, append `path` to the current working directory,
which allocates memory.)
)
@@ -2751,27 +2852,27 @@ if (isConvertibleToString!R)
assert(testAliasedString!asAbsolutePath(null));
}
-/** Translates $(D path) into a relative _path.
+/** Translates `path` into a relative path.
- The returned _path is relative to $(D base), which is by default
+ The returned path is relative to `base`, which is by default
taken to be the current working directory. If specified,
- $(D base) must be an absolute _path, and it is always assumed
- to refer to a directory. If $(D path) and $(D base) refer to
+ `base` must be an absolute path, and it is always assumed
+ to refer to a directory. If `path` and `base` refer to
the same directory, the function returns $(D `.`).
The following algorithm is used:
$(OL
- $(LI If $(D path) is a relative directory, return it unaltered.)
- $(LI Find a common root between $(D path) and $(D base).
- If there is no common root, return $(D path) unaltered.)
+ $(LI If `path` is a relative directory, return it unaltered.)
+ $(LI Find a common root between `path` and `base`.
+ If there is no common root, return `path` unaltered.)
$(LI Prepare a string with as many $(D `../`) or $(D `..\`) as
necessary to reach the common root from base path.)
- $(LI Append the remaining segments of $(D path) to the string
+ $(LI Append the remaining segments of `path` to the string
and return.)
)
- In the second step, path components are compared using $(D filenameCmp!cs),
- where $(D cs) is an optional template parameter determining whether
+ In the second step, path components are compared using `filenameCmp!cs`,
+ where `cs` is an optional template parameter determining whether
the comparison is case sensitive or not. See the
$(LREF filenameCmp) documentation for details.
@@ -2789,7 +2890,7 @@ if (isConvertibleToString!R)
$(LREF asRelativePath) which does not allocate memory
Throws:
- $(D Exception) if the specified _base directory is not absolute.
+ `Exception` if the specified _base directory is not absolute.
*/
string relativePath(CaseSensitive cs = CaseSensitive.osDefault)
(string path, lazy string base = getcwd())
@@ -2805,7 +2906,7 @@ string relativePath(CaseSensitive cs = CaseSensitive.osDefault)
}
///
-@system unittest
+@safe unittest
{
assert(relativePath("foo") == "foo");
@@ -2828,7 +2929,7 @@ string relativePath(CaseSensitive cs = CaseSensitive.osDefault)
}
}
-@system unittest
+@safe unittest
{
import std.exception;
assert(relativePath("foo") == "foo");
@@ -2847,11 +2948,11 @@ string relativePath(CaseSensitive cs = CaseSensitive.osDefault)
else static assert(0);
}
-/** Transforms `path` into a _path relative to `base`.
+/** Transforms `path` into a path relative to `base`.
- The returned _path is relative to `base`, which is usually
+ The returned path is relative to `base`, which is usually
the current working directory.
- `base` must be an absolute _path, and it is always assumed
+ `base` must be an absolute path, and it is always assumed
to refer to a directory. If `path` and `base` refer to
the same directory, the function returns `'.'`.
@@ -2872,13 +2973,13 @@ string relativePath(CaseSensitive cs = CaseSensitive.osDefault)
$(LREF filenameCmp) documentation for details.
Params:
- path = _path to transform
+ path = path to transform
base = absolute path
cs = whether filespec comparisons are sensitive or not; defaults to
`CaseSensitive.osDefault`
Returns:
- a random access range of the transformed _path
+ a random access range of the transformed path
See_Also:
$(LREF relativePath)
@@ -2936,7 +3037,7 @@ if ((isNarrowString!R1 ||
}
///
-@system unittest
+@safe unittest
{
import std.array;
version (Posix)
@@ -2962,6 +3063,19 @@ if ((isNarrowString!R1 ||
static assert(0);
}
+@safe unittest
+{
+ version (Posix)
+ {
+ assert(isBidirectionalRange!(typeof(asRelativePath("foo/bar/baz", "/foo/woo/wee"))));
+ }
+
+ version (Windows)
+ {
+ assert(isBidirectionalRange!(typeof(asRelativePath(`c:\foo\bar`, `c:\foo\baz`))));
+ }
+}
+
auto asRelativePath(CaseSensitive cs = CaseSensitive.osDefault, R1, R2)
(auto ref R1 path, auto ref R2 base)
if (isConvertibleToString!R1 || isConvertibleToString!R2)
@@ -2971,7 +3085,7 @@ if (isConvertibleToString!R1 || isConvertibleToString!R2)
return asRelativePath!(cs, Types)(path, base);
}
-@system unittest
+@safe unittest
{
import std.array;
version (Posix)
@@ -2985,7 +3099,7 @@ if (isConvertibleToString!R1 || isConvertibleToString!R2)
assert(asRelativePath("foo"d.byDchar, TestAliasedString("bar")).array == "foo");
}
-@system unittest
+@safe unittest
{
import std.array, std.utf : bCU=byCodeUnit;
version (Posix)
@@ -3005,8 +3119,8 @@ if (isConvertibleToString!R1 || isConvertibleToString!R2)
/** Compares filename characters.
This function can perform a case-sensitive or a case-insensitive
- comparison. This is controlled through the $(D cs) template parameter
- which, if not specified, is given by $(LREF CaseSensitive)$(D .osDefault).
+ comparison. This is controlled through the `cs` template parameter
+ which, if not specified, is given by $(LREF CaseSensitive)`.osDefault`.
On Windows, the backslash and slash characters ($(D `\`) and $(D `/`))
are considered equal.
@@ -3018,7 +3132,7 @@ if (isConvertibleToString!R1 || isConvertibleToString!R2)
Returns:
$(D < 0) if $(D a < b),
- $(D 0) if $(D a == b), and
+ `0` if $(D a == b), and
$(D > 0) if $(D a > b).
*/
int filenameCharCmp(CaseSensitive cs = CaseSensitive.osDefault)(dchar a, dchar b)
@@ -3078,8 +3192,8 @@ int filenameCharCmp(CaseSensitive cs = CaseSensitive.osDefault)(dchar a, dchar b
/** Compares file names and returns
- Individual characters are compared using $(D filenameCharCmp!cs),
- where $(D cs) is an optional template parameter determining whether
+ Individual characters are compared using `filenameCharCmp!cs`,
+ where `cs` is an optional template parameter determining whether
the comparison is case sensitive or not.
Treatment of invalid UTF encodings is implementation defined.
@@ -3091,7 +3205,7 @@ int filenameCharCmp(CaseSensitive cs = CaseSensitive.osDefault)(dchar a, dchar b
Returns:
$(D < 0) if $(D filename1 < filename2),
- $(D 0) if $(D filename1 == filename2) and
+ `0` if $(D filename1 == filename2) and
$(D > 0) if $(D filename1 > filename2).
See_Also:
@@ -3204,22 +3318,22 @@ if (isConvertibleToString!Range1 || isConvertibleToString!Range2)
$(I meta-characters)) and can't be escaped. These are:
$(BOOKTABLE,
- $(TR $(TD $(D *))
+ $(TR $(TD `*`)
$(TD Matches 0 or more instances of any character.))
- $(TR $(TD $(D ?))
+ $(TR $(TD `?`)
$(TD Matches exactly one instance of any character.))
- $(TR $(TD $(D [)$(I chars)$(D ]))
+ $(TR $(TD `[`$(I chars)`]`)
$(TD Matches one instance of any character that appears
between the brackets.))
- $(TR $(TD $(D [!)$(I chars)$(D ]))
+ $(TR $(TD `[!`$(I chars)`]`)
$(TD Matches one instance of any character that does not
appear between the brackets after the exclamation mark.))
- $(TR $(TD $(D {)$(I string1)$(D ,)$(I string2)$(D ,)&hellip;$(D }))
+ $(TR $(TD `{`$(I string1)`,`$(I string2)`,`&hellip;`}`)
$(TD Matches either of the specified strings.))
)
- Individual characters are compared using $(D filenameCharCmp!cs),
- where $(D cs) is an optional template parameter determining whether
+ Individual characters are compared using `filenameCharCmp!cs`,
+ where `cs` is an optional template parameter determining whether
the comparison is case sensitive or not. See the
$(LREF filenameCharCmp) documentation for details.
@@ -3233,7 +3347,7 @@ if (isConvertibleToString!Range1 || isConvertibleToString!Range2)
pattern = The glob pattern
Returns:
- $(D true) if pattern matches path, $(D false) otherwise.
+ `true` if pattern matches path, `false` otherwise.
See_also:
$(LINK2 http://en.wikipedia.org/wiki/Glob_%28programming%29,Wikipedia: _glob (programming))
@@ -3243,7 +3357,7 @@ bool globMatch(CaseSensitive cs = CaseSensitive.osDefault, C, Range)
@safe pure nothrow
if (isForwardRange!Range && !isInfinite!Range &&
isSomeChar!(ElementEncodingType!Range) && !isConvertibleToString!Range &&
- isSomeChar!C && is(Unqual!C == Unqual!(ElementEncodingType!Range)))
+ isSomeChar!C && is(immutable C == immutable ElementEncodingType!Range))
in
{
// Verify that pattern[] is valid
@@ -3251,7 +3365,7 @@ in
assert(balancedParens(pattern, '[', ']', 0));
assert(balancedParens(pattern, '{', '}', 0));
}
-body
+do
{
alias RC = Unqual!(ElementEncodingType!Range);
@@ -3483,31 +3597,31 @@ if (isConvertibleToString!Range)
/** Checks that the given file or directory name is valid.
- The maximum length of $(D filename) is given by the constant
- $(D core.stdc.stdio.FILENAME_MAX). (On Windows, this number is
+ The maximum length of `filename` is given by the constant
+ `core.stdc.stdio.FILENAME_MAX`. (On Windows, this number is
defined as the maximum number of UTF-16 code points, and the
test will therefore only yield strictly correct results when
- $(D filename) is a string of $(D wchar)s.)
+ `filename` is a string of `wchar`s.)
On Windows, the following criteria must be satisfied
($(LINK2 http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx,source)):
$(UL
- $(LI $(D filename) must not contain any characters whose integer
+ $(LI `filename` must not contain any characters whose integer
representation is in the range 0-31.)
- $(LI $(D filename) must not contain any of the following $(I reserved
- characters): <>:"/\|?*)
- $(LI $(D filename) may not end with a space ($(D ' ')) or a period
- ($(D '.')).)
+ $(LI `filename` must not contain any of the following $(I reserved
+ characters): `<>:"/\|?*`)
+ $(LI `filename` may not end with a space ($(D ' ')) or a period
+ (`'.'`).)
)
- On POSIX, $(D filename) may not contain a forward slash ($(D '/')) or
- the null character ($(D '\0')).
+ On POSIX, `filename` may not contain a forward slash (`'/'`) or
+ the null character (`'\0'`).
Params:
filename = string to check
Returns:
- $(D true) if and only if $(D filename) is not
+ `true` if and only if `filename` is not
empty, not too long, and does not contain invalid characters.
*/
@@ -3590,7 +3704,7 @@ unittest
else static assert(0);
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(char[], const(char)[], string, wchar[],
+ static foreach (T; AliasSeq!(char[], const(char)[], string, wchar[],
const(wchar)[], wstring, dchar[], const(dchar)[], dstring))
{
foreach (fn; valid)
@@ -3623,28 +3737,28 @@ unittest
-/** Checks whether $(D path) is a valid _path.
+/** Checks whether `path` is a valid path.
- Generally, this function checks that $(D path) is not empty, and that
+ Generally, this function checks that `path` is not empty, and that
each component of the path either satisfies $(LREF isValidFilename)
- or is equal to $(D ".") or $(D "..").
+ or is equal to `"."` or `".."`.
- $(B It does $(I not) check whether the _path points to an existing file
+ $(B It does $(I not) check whether the path points to an existing file
or directory; use $(REF exists, std,file) for this purpose.)
On Windows, some special rules apply:
$(UL
- $(LI If the second character of $(D path) is a colon ($(D ':')),
+ $(LI If the second character of `path` is a colon (`':'`),
the first character is interpreted as a drive letter, and
must be in the range A-Z (case insensitive).)
- $(LI If $(D path) is on the form $(D `\\$(I server)\$(I share)\...`)
+ $(LI If `path` is on the form $(D `\\$(I server)\$(I share)\...`)
(UNC path), $(LREF isValidFilename) is applied to $(I server)
and $(I share) as well.)
- $(LI If $(D path) starts with $(D `\\?\`) (long UNC path), the
+ $(LI If `path` starts with $(D `\\?\`) (long UNC path), the
only requirement for the rest of the string is that it does
not contain the null character.)
- $(LI If $(D path) starts with $(D `\\.\`) (Win32 device namespace)
- this function returns $(D false); such paths are beyond the scope
+ $(LI If `path` starts with $(D `\\.\`) (Win32 device namespace)
+ this function returns `false`; such paths are beyond the scope
of this module.)
)
@@ -3652,7 +3766,7 @@ unittest
path = string or Range of characters to check
Returns:
- true if $(D path) is a valid _path.
+ true if `path` is a valid path.
*/
bool isValidPath(Range)(Range path)
if ((isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && isSomeChar!(ElementEncodingType!Range) ||
@@ -3807,16 +3921,16 @@ if (isConvertibleToString!Range)
There are two ways of using tilde expansion in a path. One
involves using the tilde alone or followed by a path separator. In
this case, the tilde will be expanded with the value of the
- environment variable $(D HOME). The second way is putting
- a username after the tilde (i.e. $(D ~john/Mail)). Here,
+ environment variable `HOME`. The second way is putting
+ a username after the tilde (i.e. `~john/Mail`). Here,
the username will be searched for in the user database
- (i.e. $(D /etc/passwd) on Unix systems) and will expand to
+ (i.e. `/etc/passwd` on Unix systems) and will expand to
whatever path is stored there. The username is considered the
string after the tilde ending at the first instance of a path
separator.
- Note that using the $(D ~user) syntax may give different
- values from just $(D ~) if the environment variable doesn't
+ Note that using the `~user` syntax may give different
+ values from just `~` if the environment variable doesn't
match the value stored in the user database.
When the environment variable version is used, the path won't
@@ -3831,9 +3945,9 @@ if (isConvertibleToString!Range)
inputPath = The path name to expand.
Returns:
- $(D inputPath) with the tilde expanded, or just $(D inputPath)
+ `inputPath` with the tilde expanded, or just `inputPath`
if it could not be expanded.
- For Windows, $(D expandTilde) merely returns its argument $(D inputPath).
+ For Windows, `expandTilde` merely returns its argument `inputPath`.
Example:
-----
@@ -3845,7 +3959,7 @@ if (isConvertibleToString!Range)
}
-----
*/
-string expandTilde(string inputPath) nothrow
+string expandTilde(string inputPath) @safe nothrow
{
version (Posix)
{
@@ -3859,9 +3973,10 @@ string expandTilde(string inputPath) nothrow
is joined to path[char_pos .. length] if char_pos is smaller
than length, otherwise path is not appended to c_path.
*/
- static string combineCPathWithDPath(char* c_path, string path, size_t char_pos) nothrow
+ static string combineCPathWithDPath(char* c_path, string path, size_t char_pos) @trusted nothrow
{
import core.stdc.string : strlen;
+ import std.exception : assumeUnique;
assert(c_path != null);
assert(path.length > 0);
@@ -3870,24 +3985,33 @@ string expandTilde(string inputPath) nothrow
// Search end of C string
size_t end = strlen(c_path);
- // Remove trailing path separator, if any
- if (end && isDirSeparator(c_path[end - 1]))
- end--;
+ const cPathEndsWithDirSep = end && isDirSeparator(c_path[end - 1]);
- // (this is the only GC allocation done in expandTilde())
string cp;
if (char_pos < path.length)
+ {
+ // Remove trailing path separator, if any (with special care for root /)
+ if (cPathEndsWithDirSep && (end > 1 || isDirSeparator(path[char_pos])))
+ end--;
+
// Append something from path
- cp = cast(string)(c_path[0 .. end] ~ path[char_pos .. $]);
+ cp = assumeUnique(c_path[0 .. end] ~ path[char_pos .. $]);
+ }
else
+ {
+ // Remove trailing path separator, if any (except for root /)
+ if (cPathEndsWithDirSep && end > 1)
+ end--;
+
// Create our own copy, as lifetime of c_path is undocumented
cp = c_path[0 .. end].idup;
+ }
return cp;
}
// Replaces the tilde from path with the environment variable HOME.
- static string expandFromEnvironment(string path) nothrow
+ static string expandFromEnvironment(string path) @safe nothrow
{
import core.stdc.stdlib : getenv;
@@ -3895,7 +4019,7 @@ string expandTilde(string inputPath) nothrow
assert(path[0] == '~');
// Get HOME and use that to replace the tilde.
- auto home = getenv("HOME");
+ auto home = () @trusted { return getenv("HOME"); } ();
if (home == null)
return path;
@@ -3903,7 +4027,7 @@ string expandTilde(string inputPath) nothrow
}
// Replaces the tilde from path with the path from the user database.
- static string expandFromDatabase(string path) nothrow
+ static string expandFromDatabase(string path) @safe nothrow
{
// bionic doesn't really support this, as getpwnam_r
// isn't provided and getpwnam is basically just a stub
@@ -3923,10 +4047,7 @@ string expandTilde(string inputPath) nothrow
auto last_char = indexOf(path, dirSeparator[0]);
size_t username_len = (last_char == -1) ? path.length : last_char;
- char* username = cast(char*) malloc(username_len * char.sizeof);
- if (!username)
- onOutOfMemoryError();
- scope(exit) free(username);
+ char[] username = new char[username_len * char.sizeof];
if (last_char == -1)
{
@@ -3942,28 +4063,31 @@ string expandTilde(string inputPath) nothrow
assert(last_char > 1);
// Reserve C memory for the getpwnam_r() function.
- version (unittest)
+ version (StdUnittest)
uint extra_memory_size = 2;
else
uint extra_memory_size = 5 * 1024;
- char* extra_memory;
- scope(exit) free(extra_memory);
+ char[] extra_memory;
passwd result;
while (1)
{
- extra_memory = cast(char*) realloc(extra_memory, extra_memory_size * char.sizeof);
- if (extra_memory == null)
- onOutOfMemoryError();
+ extra_memory.length += extra_memory_size;
// Obtain info from database.
passwd *verify;
errno = 0;
- if (getpwnam_r(username, &result, extra_memory, extra_memory_size,
- &verify) == 0)
+ auto passResult = () @trusted { return getpwnam_r(
+ &username[0],
+ &result,
+ &extra_memory[0],
+ extra_memory.length,
+ &verify
+ ); } ();
+ if (passResult == 0)
{
// Succeeded if verify points at result
- if (verify == &result)
+ if (verify == () @trusted { return &result; } ())
// username is found
path = combineCPathWithDPath(result.pw_dir, path, last_char);
break;
@@ -4004,12 +4128,32 @@ string expandTilde(string inputPath) nothrow
}
}
+///
+@system unittest
+{
+ version (Posix)
+ {
+ import std.process : environment;
+
+ auto oldHome = environment["HOME"];
+ scope(exit) environment["HOME"] = oldHome;
+
+ environment["HOME"] = "dmd/test";
+ assert(expandTilde("~/") == "dmd/test/");
+ assert(expandTilde("~") == "dmd/test");
+ }
+}
-version (unittest) import std.process : environment;
@system unittest
{
version (Posix)
{
+ static if (__traits(compiles, { import std.process : executeShell; }))
+ import std.process : executeShell;
+
+ import std.process : environment;
+ import std.string : strip;
+
// Retrieve the current home variable.
auto oldHome = environment.get("HOME");
@@ -4028,30 +4172,33 @@ version (unittest) import std.process : environment;
assert(expandTilde("~/") == "dmd/test/");
assert(expandTilde("~") == "dmd/test");
+ // The same, but with a variable set to root.
+ environment["HOME"] = "/";
+ assert(expandTilde("~/") == "/");
+ assert(expandTilde("~") == "/");
+
// Recover original HOME variable before continuing.
if (oldHome !is null) environment["HOME"] = oldHome;
else environment.remove("HOME");
- // Test user expansion for root, no /root on Android
- version (OSX)
+ static if (is(typeof(executeShell)))
{
- assert(expandTilde("~root") == "/var/root", expandTilde("~root"));
- assert(expandTilde("~root/") == "/var/root/", expandTilde("~root/"));
- }
- else version (Android)
- {
- }
- else
- {
- assert(expandTilde("~root") == "/root", expandTilde("~root"));
- assert(expandTilde("~root/") == "/root/", expandTilde("~root/"));
+ immutable tildeUser = "~" ~ environment.get("USER");
+ immutable path = executeShell("echo " ~ tildeUser).output.strip();
+ immutable expTildeUser = expandTilde(tildeUser);
+ assert(expTildeUser == path, expTildeUser);
+ immutable expTildeUserSlash = expandTilde(tildeUser ~ "/");
+ immutable pathSlash = path[$-1] == '/' ? path : path ~ "/";
+ assert(expTildeUserSlash == pathSlash, expTildeUserSlash);
}
+
assert(expandTilde("~Idontexist/hey") == "~Idontexist/hey");
}
}
-version (unittest)
+version (StdUnittest)
{
+private:
/* Define a mock RandomAccessRange to use for unittesting.
*/
@@ -4064,7 +4211,7 @@ version (unittest)
@property bool empty() { return array.length == 0; }
@property C front() { return array[0]; }
@property C back() { return array[$ - 1]; }
- @property size_t opDollar() { return length; }
+ alias opDollar = length;
C opIndex(size_t i) { return array[i]; }
}
void popFront() { array = array[1 .. $]; }
@@ -4078,11 +4225,6 @@ version (unittest)
C[] array;
}
- static assert( isRandomAccessRange!(MockRange!(const(char))) );
-}
-
-version (unittest)
-{
/* Define a mock BidirectionalRange to use for unittesting.
*/
@@ -4103,6 +4245,11 @@ version (unittest)
const(C)[] array;
}
+}
+
+@safe unittest
+{
+ static assert( isRandomAccessRange!(MockRange!(const(char))) );
static assert( isBidirectionalRange!(MockBiRange!(const(char))) );
}
diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d
index 1e977aa34f5..7ec8fa1dc59 100644
--- a/libphobos/src/std/process.d
+++ b/libphobos/src/std/process.d
@@ -2,51 +2,51 @@
/**
Functions for starting and interacting with other processes, and for
-working with the current _process' execution environment.
+working with the current process' execution environment.
Process_handling:
$(UL $(LI
- $(LREF spawnProcess) spawns a new _process, optionally assigning it an
+ $(LREF spawnProcess) spawns a new process, optionally assigning it an
arbitrary set of standard input, output, and error streams.
- The function returns immediately, leaving the child _process to execute
+ The function returns immediately, leaving the child process to execute
in parallel with its parent. All other functions in this module that
- spawn processes are built around $(D spawnProcess).)
+ spawn processes are built around `spawnProcess`.)
$(LI
- $(LREF wait) makes the parent _process wait for a child _process to
+ $(LREF wait) makes the parent process wait for a child process to
terminate. In general one should always do this, to avoid
- child processes becoming "zombies" when the parent _process exits.
+ child processes becoming "zombies" when the parent process exits.
Scope guards are perfect for this – see the $(LREF spawnProcess)
- documentation for examples. $(LREF tryWait) is similar to $(D wait),
- but does not block if the _process has not yet terminated.)
+ documentation for examples. $(LREF tryWait) is similar to `wait`,
+ but does not block if the process has not yet terminated.)
$(LI
- $(LREF pipeProcess) also spawns a child _process which runs
+ $(LREF pipeProcess) also spawns a child process which runs
in parallel with its parent. However, instead of taking
arbitrary streams, it automatically creates a set of
pipes that allow the parent to communicate with the child
through the child's standard input, output, and/or error streams.
- This function corresponds roughly to C's $(D popen) function.)
+ This function corresponds roughly to C's `popen` function.)
$(LI
- $(LREF execute) starts a new _process and waits for it
+ $(LREF execute) starts a new process and waits for it
to complete before returning. Additionally, it captures
- the _process' standard output and error streams and returns
+ the process' standard output and error streams and returns
the output of these as a string.)
$(LI
$(LREF spawnShell), $(LREF pipeShell) and $(LREF executeShell) work like
- $(D spawnProcess), $(D pipeProcess) and $(D execute), respectively,
+ `spawnProcess`, `pipeProcess` and `execute`, respectively,
except that they take a single command string and run it through
the current user's default command interpreter.
- $(D executeShell) corresponds roughly to C's $(D system) function.)
+ `executeShell` corresponds roughly to C's `system` function.)
$(LI
- $(LREF kill) attempts to terminate a running _process.)
+ $(LREF kill) attempts to terminate a running process.)
)
-The following table compactly summarises the different _process creation
+The following table compactly summarises the different process creation
functions and how they relate to each other:
$(BOOKTABLE,
$(TR $(TH )
$(TH Runs program directly)
$(TH Runs shell command))
- $(TR $(TD Low-level _process creation)
+ $(TR $(TD Low-level process creation)
$(TD $(LREF spawnProcess))
$(TD $(LREF spawnShell)))
$(TR $(TD Automatic input/output redirection using pipes)
@@ -62,7 +62,7 @@ $(UL
$(LI
$(LREF pipe) is used to create unidirectional pipes.)
$(LI
- $(LREF environment) is an interface through which the current _process'
+ $(LREF environment) is an interface through which the current process'
environment variables can be read and manipulated.)
$(LI
$(LREF escapeShellCommand) and $(LREF escapeShellFileName) are useful
@@ -78,13 +78,19 @@ Copyright:
License:
$(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Source:
- $(PHOBOSSRC std/_process.d)
+ $(PHOBOSSRC std/process.d)
Macros:
- OBJECTREF=$(D $(LINK2 object.html#$0,$0))
- LREF=$(D $(LINK2 #.$0,$0))
+ OBJECTREF=$(REF1 $0, object)
+
+Note:
+Most of the functionality in this module is not available on iOS, tvOS
+and watchOS. The only functions available on those platforms are:
+$(LREF environment), $(LREF thisProcessID) and $(LREF thisThreadID).
*/
module std.process;
+import core.thread : ThreadID;
+
version (Posix)
{
import core.sys.posix.sys.wait;
@@ -93,7 +99,8 @@ version (Posix)
version (Windows)
{
import core.stdc.stdio;
- import core.sys.windows.windows;
+ import core.sys.windows.winbase;
+ import core.sys.windows.winnt;
import std.utf;
import std.windows.syserror;
}
@@ -101,7 +108,25 @@ version (Windows)
import std.internal.cstring;
import std.range.primitives;
import std.stdio;
+import std.traits : isSomeChar;
+version (OSX)
+ version = Darwin;
+else version (iOS)
+{
+ version = Darwin;
+ version = iOSDerived;
+}
+else version (TVOS)
+{
+ version = Darwin;
+ version = iOSDerived;
+}
+else version (WatchOS)
+{
+ version = Darwin;
+ version = iOSDerived;
+}
// When the DMC runtime is used, we have to use some custom functions
// to convert between Windows file handles and FILE*s.
@@ -129,7 +154,7 @@ private
// POSIX API declarations.
version (Posix)
{
- version (OSX)
+ version (Darwin)
{
extern(C) char*** _NSGetEnviron() nothrow;
const(char**) getEnvironPtr() @trusted
@@ -149,36 +174,532 @@ private
@system unittest
{
+ import core.thread : Thread;
new Thread({assert(getEnvironPtr !is null);}).start();
}
}
} // private
+// =============================================================================
+// Environment variable manipulation.
+// =============================================================================
+
+/**
+Manipulates _environment variables using an associative-array-like
+interface.
+
+This class contains only static methods, and cannot be instantiated.
+See below for examples of use.
+*/
+abstract final class environment
+{
+ static import core.sys.posix.stdlib;
+ import core.stdc.errno : errno, EINVAL;
+
+static:
+ /**
+ Retrieves the value of the environment variable with the given `name`.
+ ---
+ auto path = environment["PATH"];
+ ---
+
+ Throws:
+ $(OBJECTREF Exception) if the environment variable does not exist,
+ or $(REF UTFException, std,utf) if the variable contains invalid UTF-16
+ characters (Windows only).
+
+ See_also:
+ $(LREF environment.get), which doesn't throw on failure.
+ */
+ string opIndex(scope const(char)[] name) @safe
+ {
+ import std.exception : enforce;
+ return get(name, null).enforce("Environment variable not found: "~name);
+ }
+
+ /**
+ Retrieves the value of the environment variable with the given `name`,
+ or a default value if the variable doesn't exist.
+
+ Unlike $(LREF environment.opIndex), this function never throws on Posix.
+ ---
+ auto sh = environment.get("SHELL", "/bin/sh");
+ ---
+ This function is also useful in checking for the existence of an
+ environment variable.
+ ---
+ auto myVar = environment.get("MYVAR");
+ if (myVar is null)
+ {
+ // Environment variable doesn't exist.
+ // Note that we have to use 'is' for the comparison, since
+ // myVar == null is also true if the variable exists but is
+ // empty.
+ }
+ ---
+ Params:
+ name = name of the environment variable to retrieve
+ defaultValue = default value to return if the environment variable doesn't exist.
+
+ Returns:
+ the value of the environment variable if found, otherwise
+ `null` if the environment doesn't exist.
+
+ Throws:
+ $(REF UTFException, std,utf) if the variable contains invalid UTF-16
+ characters (Windows only).
+ */
+ string get(scope const(char)[] name, string defaultValue = null) @safe
+ {
+ string value;
+ getImpl(name, (result) { value = result ? cachedToString(result) : defaultValue; });
+ return value;
+ }
+
+ /**
+ Assigns the given `value` to the environment variable with the given
+ `name`.
+ If `value` is null the variable is removed from environment.
+
+ If the variable does not exist, it will be created. If it already exists,
+ it will be overwritten.
+ ---
+ environment["foo"] = "bar";
+ ---
+
+ Throws:
+ $(OBJECTREF Exception) if the environment variable could not be added
+ (e.g. if the name is invalid).
+
+ Note:
+ On some platforms, modifying environment variables may not be allowed in
+ multi-threaded programs. See e.g.
+ $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).
+ */
+ inout(char)[] opIndexAssign(return inout char[] value, scope const(char)[] name) @trusted
+ {
+ version (Posix)
+ {
+ import std.exception : enforce, errnoEnforce;
+ if (value is null)
+ {
+ remove(name);
+ return value;
+ }
+ if (core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), 1) != -1)
+ {
+ return value;
+ }
+ // The default errno error message is very uninformative
+ // in the most common case, so we handle it manually.
+ enforce(errno != EINVAL,
+ "Invalid environment variable name: '"~name~"'");
+ errnoEnforce(false,
+ "Failed to add environment variable");
+ assert(0);
+ }
+ else version (Windows)
+ {
+ import std.exception : enforce;
+ enforce(
+ SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()),
+ sysErrorString(GetLastError())
+ );
+ return value;
+ }
+ else static assert(0);
+ }
+
+ /**
+ Removes the environment variable with the given `name`.
+
+ If the variable isn't in the environment, this function returns
+ successfully without doing anything.
+
+ Note:
+ On some platforms, modifying environment variables may not be allowed in
+ multi-threaded programs. See e.g.
+ $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).
+ */
+ void remove(scope const(char)[] name) @trusted nothrow @nogc
+ {
+ version (Windows) SetEnvironmentVariableW(name.tempCStringW(), null);
+ else version (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString());
+ else static assert(0);
+ }
+
+ /**
+ Identify whether a variable is defined in the environment.
+
+ Because it doesn't return the value, this function is cheaper than `get`.
+ However, if you do need the value as well, you should just check the
+ return of `get` for `null` instead of using this function first.
+
+ Example:
+ -------------
+ // good usage
+ if ("MY_ENV_FLAG" in environment)
+ doSomething();
+
+ // bad usage
+ if ("MY_ENV_VAR" in environment)
+ doSomething(environment["MY_ENV_VAR"]);
+
+ // do this instead
+ if (auto var = environment.get("MY_ENV_VAR"))
+ doSomething(var);
+ -------------
+ */
+ bool opBinaryRight(string op : "in")(scope const(char)[] name) @trusted
+ {
+ version (Posix)
+ return core.sys.posix.stdlib.getenv(name.tempCString()) !is null;
+ else version (Windows)
+ {
+ SetLastError(NO_ERROR);
+ if (GetEnvironmentVariableW(name.tempCStringW, null, 0) > 0)
+ return true;
+ immutable err = GetLastError();
+ if (err == NO_ERROR)
+ return true; // zero-length environment variable on Wine / XP
+ if (err == ERROR_ENVVAR_NOT_FOUND)
+ return false;
+ // Some other Windows error, throw.
+ throw new WindowsException(err);
+ }
+ else static assert(0);
+ }
+
+ /**
+ Copies all environment variables into an associative array.
+
+ Windows_specific:
+ While Windows environment variable names are case insensitive, D's
+ built-in associative arrays are not. This function will store all
+ variable names in uppercase (e.g. `PATH`).
+
+ Throws:
+ $(OBJECTREF Exception) if the environment variables could not
+ be retrieved (Windows only).
+ */
+ string[string] toAA() @trusted
+ {
+ import std.conv : to;
+ string[string] aa;
+ version (Posix)
+ {
+ auto environ = getEnvironPtr;
+ for (int i=0; environ[i] != null; ++i)
+ {
+ import std.string : indexOf;
+
+ immutable varDef = to!string(environ[i]);
+ immutable eq = indexOf(varDef, '=');
+ assert(eq >= 0);
+
+ immutable name = varDef[0 .. eq];
+ immutable value = varDef[eq+1 .. $];
+
+ // In POSIX, environment variables may be defined more
+ // than once. This is a security issue, which we avoid
+ // by checking whether the key already exists in the array.
+ // For more info:
+ // http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html
+ if (name !in aa) aa[name] = value;
+ }
+ }
+ else version (Windows)
+ {
+ import std.exception : enforce;
+ import std.uni : toUpper;
+ auto envBlock = GetEnvironmentStringsW();
+ enforce(envBlock, "Failed to retrieve environment variables.");
+ scope(exit) FreeEnvironmentStringsW(envBlock);
+
+ for (int i=0; envBlock[i] != '\0'; ++i)
+ {
+ auto start = i;
+ while (envBlock[i] != '=') ++i;
+ immutable name = toUTF8(toUpper(envBlock[start .. i]));
+
+ start = i+1;
+ while (envBlock[i] != '\0') ++i;
+
+ // Ignore variables with empty names. These are used internally
+ // by Windows to keep track of each drive's individual current
+ // directory.
+ if (!name.length)
+ continue;
+
+ // Just like in POSIX systems, environment variables may be
+ // defined more than once in an environment block on Windows,
+ // and it is just as much of a security issue there. Moreso,
+ // in fact, due to the case insensensitivity of variable names,
+ // which is not handled correctly by all programs.
+ auto val = toUTF8(envBlock[start .. i]);
+ if (name !in aa) aa[name] = val is null ? "" : val;
+ }
+ }
+ else static assert(0);
+ return aa;
+ }
+
+private:
+ version (Windows) alias OSChar = WCHAR;
+ else version (Posix) alias OSChar = char;
+
+ // Retrieves the environment variable. Calls `sink` with a
+ // temporary buffer of OS characters, or `null` if the variable
+ // doesn't exist.
+ void getImpl(scope const(char)[] name, scope void delegate(const(OSChar)[]) @safe sink) @trusted
+ {
+ version (Windows)
+ {
+ // first we ask windows how long the environment variable is,
+ // then we try to read it in to a buffer of that length. Lots
+ // of error conditions because the windows API is nasty.
+
+ import std.conv : to;
+ const namezTmp = name.tempCStringW();
+ WCHAR[] buf;
+
+ // clear error because GetEnvironmentVariable only says it sets it
+ // if the environment variable is missing, not on other errors.
+ SetLastError(NO_ERROR);
+ // len includes terminating null
+ immutable len = GetEnvironmentVariableW(namezTmp, null, 0);
+ if (len == 0)
+ {
+ immutable err = GetLastError();
+ if (err == ERROR_ENVVAR_NOT_FOUND)
+ return sink(null);
+ if (err != NO_ERROR) // Some other Windows error, throw.
+ throw new WindowsException(err);
+ }
+ if (len <= 1)
+ return sink("");
+ buf.length = len;
+
+ while (true)
+ {
+ // lenRead is either the number of bytes read w/o null - if buf was long enough - or
+ // the number of bytes necessary *including* null if buf wasn't long enough
+ immutable lenRead = GetEnvironmentVariableW(namezTmp, buf.ptr, to!DWORD(buf.length));
+ if (lenRead == 0)
+ {
+ immutable err = GetLastError();
+ if (err == NO_ERROR) // sucessfully read a 0-length variable
+ return sink("");
+ if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist
+ return sink(null);
+ // some other windows error
+ throw new WindowsException(err);
+ }
+ assert(lenRead != buf.length, "impossible according to msft docs");
+ if (lenRead < buf.length) // the buffer was long enough
+ return sink(buf[0 .. lenRead]);
+ // resize and go around again, because the environment variable grew
+ buf.length = lenRead;
+ }
+ }
+ else version (Posix)
+ {
+ import core.stdc.string : strlen;
+
+ const vz = core.sys.posix.stdlib.getenv(name.tempCString());
+ if (vz == null) return sink(null);
+ return sink(vz[0 .. strlen(vz)]);
+ }
+ else static assert(0);
+ }
+
+ string cachedToString(C)(scope const(C)[] v) @safe
+ {
+ import std.algorithm.comparison : equal;
+
+ // Cache the last call's result.
+ static string lastResult;
+ if (v.empty)
+ {
+ // Return non-null array for blank result to distinguish from
+ // not-present result.
+ lastResult = "";
+ }
+ else if (!v.equal(lastResult))
+ {
+ import std.conv : to;
+ lastResult = v.to!string;
+ }
+ return lastResult;
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+ // New variable
+ environment["std_process"] = "foo";
+ assert(environment["std_process"] == "foo");
+ assert("std_process" in environment);
+
+ // Set variable again (also tests length 1 case)
+ environment["std_process"] = "b";
+ assert(environment["std_process"] == "b");
+ assert("std_process" in environment);
+
+ // Remove variable
+ environment.remove("std_process");
+ assert("std_process" !in environment);
+
+ // Remove again, should succeed
+ environment.remove("std_process");
+ assert("std_process" !in environment);
+
+ // Throw on not found.
+ assertThrown(environment["std_process"]);
+
+ // get() without default value
+ assert(environment.get("std_process") is null);
+
+ // get() with default value
+ assert(environment.get("std_process", "baz") == "baz");
+
+ // get() on an empty (but present) value
+ environment["std_process"] = "";
+ auto res = environment.get("std_process");
+ assert(res !is null);
+ assert(res == "");
+ assert("std_process" in environment);
+
+ // Important to do the following round-trip after the previous test
+ // because it tests toAA with an empty var
+
+ // Convert to associative array
+ auto aa = environment.toAA();
+ assert(aa.length > 0);
+ foreach (n, v; aa)
+ {
+ // Wine has some bugs related to environment variables:
+ // - Wine allows the existence of an env. variable with the name
+ // "\0", but GetEnvironmentVariable refuses to retrieve it.
+ // As of 2.067 we filter these out anyway (see comment in toAA).
+
+ assert(v == environment[n]);
+ }
+
+ // ... and back again.
+ foreach (n, v; aa)
+ environment[n] = v;
+
+ // Complete the roundtrip
+ auto aa2 = environment.toAA();
+ import std.conv : text;
+ assert(aa == aa2, text(aa, " != ", aa2));
+ assert("std_process" in environment);
+
+ // Setting null must have the same effect as remove
+ environment["std_process"] = null;
+ assert("std_process" !in environment);
+}
// =============================================================================
// Functions and classes for process management.
// =============================================================================
+/**
+ * Returns the process ID of the current process,
+ * which is guaranteed to be unique on the system.
+ *
+ * Example:
+ * ---
+ * writefln("Current process ID: %d", thisProcessID);
+ * ---
+ */
+@property int thisProcessID() @trusted nothrow //TODO: @safe
+{
+ version (Windows) return GetCurrentProcessId();
+ else version (Posix) return core.sys.posix.unistd.getpid();
+}
+
/**
-Spawns a new _process, optionally assigning it an arbitrary set of standard
+ * Returns the process ID of the current thread,
+ * which is guaranteed to be unique within the current process.
+ *
+ * Returns:
+ * A $(REF ThreadID, core,thread) value for the calling thread.
+ *
+ * Example:
+ * ---
+ * writefln("Current thread ID: %s", thisThreadID);
+ * ---
+ */
+@property ThreadID thisThreadID() @trusted nothrow //TODO: @safe
+{
+ version (Windows)
+ return GetCurrentThreadId();
+ else
+ version (Posix)
+ {
+ import core.sys.posix.pthread : pthread_self;
+ return pthread_self();
+ }
+}
+
+
+@system unittest
+{
+ int pidA, pidB;
+ ThreadID tidA, tidB;
+ pidA = thisProcessID;
+ tidA = thisThreadID;
+
+ import core.thread;
+ auto t = new Thread({
+ pidB = thisProcessID;
+ tidB = thisThreadID;
+ });
+ t.start();
+ t.join();
+
+ assert(pidA == pidB);
+ assert(tidA != tidB);
+}
+
+
+package(std) string uniqueTempPath() @safe
+{
+ import std.file : tempDir;
+ import std.path : buildPath;
+ import std.uuid : randomUUID;
+ // Path should contain spaces to test escaping whitespace
+ return buildPath(tempDir(), "std.process temporary file " ~
+ randomUUID().toString());
+}
+
+
+version (iOSDerived) {}
+else:
+
+/**
+Spawns a new process, optionally assigning it an arbitrary set of standard
input, output, and error streams.
-The function returns immediately, leaving the child _process to execute
+The function returns immediately, leaving the child process to execute
in parallel with its parent. It is recommended to always call $(LREF wait)
on the returned $(LREF Pid) unless the process was spawned with
-$(D Config.detached) flag, as detailed in the documentation for $(D wait).
+`Config.detached` flag, as detailed in the documentation for `wait`.
Command_line:
There are four overloads of this function. The first two take an array
-of strings, $(D args), which should contain the program name as the
+of strings, `args`, which should contain the program name as the
zeroth element and any command-line arguments in subsequent elements.
The third and fourth versions are included for convenience, and may be
used when there are no command-line arguments. They take a single string,
-$(D program), which specifies the program name.
+`program`, which specifies the program name.
-Unless a directory is specified in $(D args[0]) or $(D program),
-$(D spawnProcess) will search for the program in a platform-dependent
+Unless a directory is specified in `args[0]` or `program`,
+`spawnProcess` will search for the program in a platform-dependent
manner. On POSIX systems, it will look for the executable in the
directories listed in the PATH environment variable, in the order
they are listed. On Windows, it will search for the executable in
@@ -208,19 +729,19 @@ if (wait(dmdPid) != 0)
Environment_variables:
By default, the child process inherits the environment of the parent
-process, along with any additional variables specified in the $(D env)
+process, along with any additional variables specified in the `env`
parameter. If the same variable exists in both the parent's environment
-and in $(D env), the latter takes precedence.
+and in `env`, the latter takes precedence.
-If the $(LREF Config.newEnv) flag is set in $(D config), the child
+If the $(LREF Config.newEnv) flag is set in `config`, the child
process will $(I not) inherit the parent's environment. Its entire
-environment will then be determined by $(D env).
+environment will then be determined by `env`.
---
wait(spawnProcess("myapp", ["foo" : "bar"], Config.newEnv));
---
Standard_streams:
-The optional arguments $(D stdin), $(D stdout) and $(D stderr) may
+The optional arguments `stdin`, `stdout` and `stderr` may
be used to assign arbitrary $(REF File, std,stdio) objects as the standard
input, output and error streams, respectively, of the child process. The
former must be opened for reading, while the latter two must be opened for
@@ -238,14 +759,14 @@ if (wait(pid) != 0)
writeln("Compilation failed. See errors.log for details.");
---
-Note that if you pass a $(D File) object that is $(I not)
+Note that if you pass a `File` object that is $(I not)
one of the standard input/output/error streams of the parent process,
that stream will by default be $(I closed) in the parent process when
this function returns. See the $(LREF Config) documentation below for
information about how to disable this behaviour.
-Beware of buffering issues when passing $(D File) objects to
-$(D spawnProcess). The child process will inherit the low-level raw
+Beware of buffering issues when passing `File` objects to
+`spawnProcess`. The child process will inherit the low-level raw
read/write offset associated with the underlying file descriptor, but
it will not be aware of any buffered data. In cases where this matters
(e.g. when a file should be aligned before being passed on to the
@@ -279,27 +800,36 @@ Throws:
$(LREF ProcessException) on failure to start the process.$(BR)
$(REF StdioException, std,stdio) on failure to pass one of the streams
to the child process (Windows only).$(BR)
-$(REF RangeError, core,exception) if $(D args) is empty.
+$(REF RangeError, core,exception) if `args` is empty.
*/
-Pid spawnProcess(in char[][] args,
+Pid spawnProcess(scope const(char[])[] args,
File stdin = std.stdio.stdin,
File stdout = std.stdio.stdout,
File stderr = std.stdio.stderr,
const string[string] env = null,
Config config = Config.none,
- in char[] workDir = null)
- @trusted // TODO: Should be @safe
+ scope const char[] workDir = null)
+ @safe
{
- version (Windows) auto args2 = escapeShellArguments(args);
- else version (Posix) alias args2 = args;
- return spawnProcessImpl(args2, stdin, stdout, stderr, env, config, workDir);
+ version (Windows)
+ {
+ const commandLine = escapeShellArguments(args);
+ const program = args.length ? args[0] : null;
+ return spawnProcessWin(commandLine, program, stdin, stdout, stderr, env, config, workDir);
+ }
+ else version (Posix)
+ {
+ return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir);
+ }
+ else
+ static assert(0);
}
/// ditto
-Pid spawnProcess(in char[][] args,
+Pid spawnProcess(scope const(char[])[] args,
const string[string] env,
Config config = Config.none,
- in char[] workDir = null)
+ scope const(char)[] workDir = null)
@trusted // TODO: Should be @safe
{
return spawnProcess(args,
@@ -312,13 +842,13 @@ Pid spawnProcess(in char[][] args,
}
/// ditto
-Pid spawnProcess(in char[] program,
+Pid spawnProcess(scope const(char)[] program,
File stdin = std.stdio.stdin,
File stdout = std.stdio.stdout,
File stderr = std.stdio.stderr,
const string[string] env = null,
Config config = Config.none,
- in char[] workDir = null)
+ scope const(char)[] workDir = null)
@trusted
{
return spawnProcess((&program)[0 .. 1],
@@ -326,10 +856,10 @@ Pid spawnProcess(in char[] program,
}
/// ditto
-Pid spawnProcess(in char[] program,
+Pid spawnProcess(scope const(char)[] program,
const string[string] env,
Config config = Config.none,
- in char[] workDir = null)
+ scope const(char)[] workDir = null)
@trusted
{
return spawnProcess((&program)[0 .. 1], env, config, workDir);
@@ -342,6 +872,8 @@ version (Posix) private enum InternalError : ubyte
chdir,
getrlimit,
doubleFork,
+ malloc,
+ preExec,
}
/*
@@ -351,13 +883,13 @@ envz should be a zero-terminated array of zero-terminated strings
on the form "var=value".
*/
version (Posix)
-private Pid spawnProcessImpl(in char[][] args,
- File stdin,
- File stdout,
- File stderr,
- const string[string] env,
- Config config,
- in char[] workDir)
+private Pid spawnProcessPosix(scope const(char[])[] args,
+ File stdin,
+ File stdout,
+ File stderr,
+ scope const string[string] env,
+ Config config,
+ scope const(char)[] workDir)
@trusted // TODO: Should be @safe
{
import core.exception : RangeError;
@@ -368,12 +900,7 @@ private Pid spawnProcessImpl(in char[][] args,
if (args.empty) throw new RangeError();
const(char)[] name = args[0];
- if (any!isDirSeparator(name))
- {
- if (!isExecutable(name))
- throw new ProcessException(text("Not an executable file: ", name));
- }
- else
+ if (!any!isDirSeparator(name))
{
name = searchPathFor(name);
if (name is null)
@@ -387,7 +914,7 @@ private Pid spawnProcessImpl(in char[][] args,
argz[$-1] = null;
// Prepare environment.
- auto envz = createEnv(env, !(config & Config.newEnv));
+ auto envz = createEnv(env, !(config.flags & Config.Flags.newEnv));
// Open the working directory.
// We use open in the parent and fchdir in the child
@@ -434,13 +961,13 @@ private Pid spawnProcessImpl(in char[][] args,
since the first and the second forks will run in parallel.
*/
int[2] pidPipe;
- if (config & Config.detached)
+ if (config.flags & Config.Flags.detached)
{
if (core.sys.posix.unistd.pipe(pidPipe) != 0)
throw ProcessException.newFromErrno("Could not create pipe to get process pid");
setCLOEXEC(pidPipe[1], true);
}
- scope(exit) if (config & Config.detached) close(pidPipe[0]);
+ scope(exit) if (config.flags & Config.Flags.detached) close(pidPipe[0]);
static void abortOnError(int forkPipeOut, InternalError errorType, int error) nothrow
{
@@ -454,7 +981,7 @@ private Pid spawnProcessImpl(in char[][] args,
void closePipeWriteEnds()
{
close(forkPipe[1]);
- if (config & Config.detached)
+ if (config.flags & Config.Flags.detached)
close(pidPipe[1]);
}
@@ -468,12 +995,11 @@ private Pid spawnProcessImpl(in char[][] args,
void forkChild() nothrow @nogc
{
static import core.sys.posix.stdio;
- pragma(inline, true);
// Child process
// no need for the read end of pipe on child side
- if (config & Config.detached)
+ if (config.flags & Config.Flags.detached)
close(pidPipe[0]);
close(forkPipe[0]);
immutable forkPipeOut = forkPipe[1];
@@ -508,8 +1034,12 @@ private Pid spawnProcessImpl(in char[][] args,
setCLOEXEC(STDOUT_FILENO, false);
setCLOEXEC(STDERR_FILENO, false);
- if (!(config & Config.inheritFDs))
+ if (!(config.flags & Config.Flags.inheritFDs))
{
+ // NOTE: malloc() and getrlimit() are not on the POSIX async
+ // signal safe functions list, but practically this should
+ // not be a problem. Java VM and CPython also use malloc()
+ // in its own implementation via opendir().
import core.stdc.stdlib : malloc;
import core.sys.posix.poll : pollfd, poll, POLLNVAL;
import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE;
@@ -527,6 +1057,10 @@ private Pid spawnProcessImpl(in char[][] args,
// Call poll() to see which ones are actually open:
auto pfds = cast(pollfd*) malloc(pollfd.sizeof * maxToClose);
+ if (pfds is null)
+ {
+ abortOnError(forkPipeOut, InternalError.malloc, .errno);
+ }
foreach (i; 0 .. maxToClose)
{
pfds[i].fd = i + 3;
@@ -562,6 +1096,14 @@ private Pid spawnProcessImpl(in char[][] args,
if (stderrFD > STDERR_FILENO) close(stderrFD);
}
+ if (config.preExecFunction !is null)
+ {
+ if (config.preExecFunction() != true)
+ {
+ abortOnError(forkPipeOut, InternalError.preExec, .errno);
+ }
+ }
+
// Execute program.
core.sys.posix.unistd.execve(argz[0], argz.ptr, envz);
@@ -569,7 +1111,7 @@ private Pid spawnProcessImpl(in char[][] args,
abortOnError(forkPipeOut, InternalError.exec, .errno);
}
- if (config & Config.detached)
+ if (config.flags & Config.Flags.detached)
{
auto secondFork = core.sys.posix.unistd.fork();
if (secondFork == 0)
@@ -610,7 +1152,7 @@ private Pid spawnProcessImpl(in char[][] args,
// Save error number just in case if subsequent "waitpid" fails and overrides errno
immutable lastError = .errno;
- if (config & Config.detached)
+ if (config.flags & Config.Flags.detached)
{
// Forked child exits right after creating second fork. So it should be safe to wait here.
import core.sys.posix.sys.wait : waitpid;
@@ -636,13 +1178,19 @@ private Pid spawnProcessImpl(in char[][] args,
errorMsg = "getrlimit failed";
break;
case InternalError.exec:
- errorMsg = "Failed to execute program";
+ errorMsg = "Failed to execute '" ~ cast(string) name ~ "'";
break;
case InternalError.doubleFork:
// Can happen only when starting detached process
- assert(config & Config.detached);
+ assert(config.flags & Config.Flags.detached);
errorMsg = "Failed to fork twice";
break;
+ case InternalError.malloc:
+ errorMsg = "Failed to allocate memory";
+ break;
+ case InternalError.preExec:
+ errorMsg = "Failed to execute preExecFunction";
+ break;
case InternalError.noerror:
assert(false);
}
@@ -650,7 +1198,7 @@ private Pid spawnProcessImpl(in char[][] args,
throw ProcessException.newFromErrno(error, errorMsg);
throw new ProcessException(errorMsg);
}
- else if (config & Config.detached)
+ else if (config.flags & Config.Flags.detached)
{
owned = false;
if (read(pidPipe[0], &id, id.sizeof) != id.sizeof)
@@ -658,19 +1206,73 @@ private Pid spawnProcessImpl(in char[][] args,
}
// Parent process: Close streams and return.
- if (!(config & Config.retainStdin ) && stdinFD > STDERR_FILENO
+ if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO
&& stdinFD != getFD(std.stdio.stdin ))
stdin.close();
- if (!(config & Config.retainStdout) && stdoutFD > STDERR_FILENO
+ if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO
&& stdoutFD != getFD(std.stdio.stdout))
stdout.close();
- if (!(config & Config.retainStderr) && stderrFD > STDERR_FILENO
+ if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO
&& stderrFD != getFD(std.stdio.stderr))
stderr.close();
return new Pid(id, owned);
}
}
+version (Posix)
+@system unittest
+{
+ import std.concurrency : ownerTid, receiveTimeout, send, spawn;
+ import std.datetime : seconds;
+
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGINT);
+ pthread_sigmask(SIG_BLOCK, &ss, null);
+
+ Config config = {
+ preExecFunction: () @trusted @nogc nothrow {
+ // Reset signal handlers
+ sigset_t ss;
+ if (sigfillset(&ss) != 0)
+ {
+ return false;
+ }
+ if (sigprocmask(SIG_UNBLOCK, &ss, null) != 0)
+ {
+ return false;
+ }
+ return true;
+ },
+ };
+
+ auto pid = spawnProcess(["sleep", "10000"],
+ std.stdio.stdin,
+ std.stdio.stdout,
+ std.stdio.stderr,
+ null,
+ config,
+ null);
+ scope(failure)
+ {
+ kill(pid, SIGKILL);
+ wait(pid);
+ }
+
+ // kill the spawned process with SIGINT
+ // and send its return code
+ spawn((shared Pid pid) {
+ auto p = cast() pid;
+ kill(p, SIGINT);
+ auto code = wait(p);
+ assert(code < 0);
+ send(ownerTid, code);
+ }, cast(shared) pid);
+
+ auto received = receiveTimeout(3.seconds, (int) {});
+ assert(received);
+}
+
/*
Implementation of spawnProcess() for Windows.
@@ -681,21 +1283,23 @@ envz must be a pointer to a block of UTF-16 characters on the form
"var1=value1\0var2=value2\0...varN=valueN\0\0".
*/
version (Windows)
-private Pid spawnProcessImpl(in char[] commandLine,
- File stdin,
- File stdout,
- File stderr,
- const string[string] env,
- Config config,
- in char[] workDir)
+private Pid spawnProcessWin(scope const(char)[] commandLine,
+ scope const(char)[] program,
+ File stdin,
+ File stdout,
+ File stderr,
+ scope const string[string] env,
+ Config config,
+ scope const(char)[] workDir)
@trusted
{
import core.exception : RangeError;
+ import std.conv : text;
if (commandLine.empty) throw new RangeError("Command line is empty");
// Prepare environment.
- auto envz = createEnv(env, !(config & Config.newEnv));
+ auto envz = createEnv(env, !(config.flags & Config.Flags.newEnv));
// Startup info for CreateProcessW().
STARTUPINFO_W startinfo;
@@ -707,12 +1311,13 @@ private Pid spawnProcessImpl(in char[] commandLine,
static void prepareStream(ref File file, DWORD stdHandle, string which,
out int fileDescriptor, out HANDLE handle)
{
+ enum _NO_CONSOLE_FILENO = cast(HANDLE)-2;
fileDescriptor = getFD(file);
handle = null;
if (fileDescriptor >= 0)
handle = file.windowsHandle;
// Windows GUI applications have a fd but not a valid Windows HANDLE.
- if (handle is null || handle == INVALID_HANDLE_VALUE)
+ if (handle is null || handle == INVALID_HANDLE_VALUE || handle == _NO_CONSOLE_FILENO)
handle = GetStdHandle(stdHandle);
DWORD dwFlags;
@@ -746,26 +1351,28 @@ private Pid spawnProcessImpl(in char[] commandLine,
PROCESS_INFORMATION pi;
DWORD dwCreationFlags =
CREATE_UNICODE_ENVIRONMENT |
- ((config & Config.suppressConsole) ? CREATE_NO_WINDOW : 0);
- auto pworkDir = workDir.tempCStringW(); // workaround until Bugzilla 14696 is fixed
- if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr, null, null, true, dwCreationFlags,
+ ((config.flags & Config.Flags.suppressConsole) ? CREATE_NO_WINDOW : 0);
+ // workaround until https://issues.dlang.org/show_bug.cgi?id=14696 is fixed
+ auto pworkDir = workDir.tempCStringW();
+ if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr,
+ null, null, true, dwCreationFlags,
envz, workDir.length ? pworkDir : null, &startinfo, &pi))
- throw ProcessException.newFromLastError("Failed to spawn new process");
+ throw ProcessException.newFromLastError("Failed to spawn process \"" ~ cast(string) program ~ '"');
// figure out if we should close any of the streams
- if (!(config & Config.retainStdin ) && stdinFD > STDERR_FILENO
+ if (!(config.flags & Config.Flags.retainStdin ) && stdinFD > STDERR_FILENO
&& stdinFD != getFD(std.stdio.stdin ))
stdin.close();
- if (!(config & Config.retainStdout) && stdoutFD > STDERR_FILENO
+ if (!(config.flags & Config.Flags.retainStdout) && stdoutFD > STDERR_FILENO
&& stdoutFD != getFD(std.stdio.stdout))
stdout.close();
- if (!(config & Config.retainStderr) && stderrFD > STDERR_FILENO
+ if (!(config.flags & Config.Flags.retainStderr) && stderrFD > STDERR_FILENO
&& stderrFD != getFD(std.stdio.stderr))
stderr.close();
// close the thread handle in the process info structure
CloseHandle(pi.hThread);
- if (config & Config.detached)
+ if (config.flags & Config.Flags.detached)
{
CloseHandle(pi.hProcess);
return new Pid(pi.dwProcessId);
@@ -887,29 +1494,40 @@ version (Windows) @system unittest
// Searches the PATH variable for the given executable file,
// (checking that it is in fact executable).
version (Posix)
-package(std) string searchPathFor(in char[] executable)
- @trusted //TODO: @safe nothrow
+package(std) string searchPathFor(scope const(char)[] executable)
+ @safe
{
import std.algorithm.iteration : splitter;
- import std.conv : to;
- import std.path : buildPath;
+ import std.conv : text;
+ import std.path : chainPath;
- auto pathz = core.stdc.stdlib.getenv("PATH");
- if (pathz == null) return null;
+ string result;
- foreach (dir; splitter(to!string(pathz), ':'))
- {
- auto execPath = buildPath(dir, executable);
- if (isExecutable(execPath)) return execPath;
- }
+ environment.getImpl("PATH",
+ (scope const(char)[] path)
+ {
+ if (!path)
+ return;
+
+ foreach (dir; splitter(path, ":"))
+ {
+ auto execPath = chainPath(dir, executable);
+ if (isExecutable(execPath))
+ {
+ result = text(execPath);
+ return;
+ }
+ }
+ });
- return null;
+ return result;
}
// Checks whether the file exists and can be executed by the
// current user.
version (Posix)
-private bool isExecutable(in char[] path) @trusted nothrow @nogc //TODO: @safe
+private bool isExecutable(R)(R path) @trusted nothrow @nogc
+if (isInputRange!R && isSomeChar!(ElementEncodingType!R))
{
return (access(path.tempCString(), X_OK) == 0);
}
@@ -987,47 +1605,51 @@ version (Posix) @system unittest
assert(execute(testDefaults.path).status == 0);
assert(execute(testDefaults.path, null, Config.inheritFDs).status == 0);
- // try /proc/<pid>/fd/ on linux
- version (linux)
+ // Try a few different methods to check whether there are any
+ // incorrectly-open files.
+ void testFDs()
{
- TestScript proc = "ls /proc/$$/fd";
- auto procRes = execute(proc.path, null);
- if (procRes.status == 0)
+ // try /proc/<pid>/fd/ on linux
+ version (linux)
+ {
+ TestScript proc = "ls /proc/$$/fd";
+ auto procRes = execute(proc.path, null);
+ if (procRes.status == 0)
+ {
+ auto fdStr = fd.to!string;
+ assert(!procRes.output.split.canFind(fdStr));
+ assert(execute(proc.path, null, Config.inheritFDs)
+ .output.split.canFind(fdStr));
+ return;
+ }
+ }
+
+ // try fuser (might sometimes need permissions)
+ TestScript fuser = "echo $$ && fuser -f " ~ path;
+ auto fuserRes = execute(fuser.path, null);
+ if (fuserRes.status == 0)
{
- auto fdStr = fd.to!string;
- assert(!procRes.output.split.canFind(fdStr));
- assert(execute(proc.path, null, Config.inheritFDs)
- .output.split.canFind(fdStr));
+ assert(!reverseArgs!canFind(fuserRes
+ .output.findSplitBefore("\n").expand));
+ assert(reverseArgs!canFind(execute(fuser.path, null, Config.inheritFDs)
+ .output.findSplitBefore("\n").expand));
return;
}
- }
- // try fuser (might sometimes need permissions)
- TestScript fuser = "echo $$ && fuser -f " ~ path;
- auto fuserRes = execute(fuser.path, null);
- if (fuserRes.status == 0)
- {
- assert(!reverseArgs!canFind(fuserRes
- .output.findSplitBefore("\n").expand));
- assert(reverseArgs!canFind(execute(fuser.path, null, Config.inheritFDs)
- .output.findSplitBefore("\n").expand));
- return;
- }
+ // last resort, try lsof (not available on all Posix)
+ TestScript lsof = "lsof -p$$";
+ auto lsofRes = execute(lsof.path, null);
+ if (lsofRes.status == 0)
+ {
+ assert(!lsofRes.output.canFind(path));
+ assert(execute(lsof.path, null, Config.inheritFDs).output.canFind(path));
+ return;
+ }
- // last resort, try lsof (not available on all Posix)
- TestScript lsof = "lsof -p$$";
- auto lsofRes = execute(lsof.path, null);
- if (lsofRes.status == 0)
- {
- assert(!lsofRes.output.canFind(path));
- assert(execute(lsof.path, null, Config.inheritFDs).output.canFind(path));
- return;
+ std.stdio.stderr.writeln(__FILE__, ':', __LINE__,
+ ": Warning: Couldn't find any way to check open files");
}
-
- std.stdio.stderr.writeln(__FILE__, ':', __LINE__,
- ": Warning: Couldn't find any way to check open files");
- // DON'T DO ANY MORE TESTS BELOW HERE IN THIS UNITTEST BLOCK, THE ABOVE
- // TESTS RETURN ON SUCCESS
+ testFDs();
}
@system unittest // Environment variables in spawnProcess().
@@ -1082,32 +1704,39 @@ version (Posix) @system unittest
version (Windows) TestScript prog =
"set /p INPUT=
echo %INPUT% output %~1
- echo %INPUT% error %~2 1>&2";
+ echo %INPUT% error %~2 1>&2
+ echo done > %3";
else version (Posix) TestScript prog =
"read INPUT
echo $INPUT output $1
- echo $INPUT error $2 >&2";
+ echo $INPUT error $2 >&2
+ echo done > \"$3\"";
// Pipes
void testPipes(Config config)
{
+ import std.file, std.uuid, core.thread, std.exception;
auto pipei = pipe();
auto pipeo = pipe();
auto pipee = pipe();
- auto pid = spawnProcess([prog.path, "foo", "bar"],
+ auto done = buildPath(tempDir(), randomUUID().toString());
+ auto pid = spawnProcess([prog.path, "foo", "bar", done],
pipei.readEnd, pipeo.writeEnd, pipee.writeEnd, null, config);
pipei.writeEnd.writeln("input");
pipei.writeEnd.flush();
assert(pipeo.readEnd.readln().chomp() == "input output foo");
assert(pipee.readEnd.readln().chomp().stripRight() == "input error bar");
- if (!(config & Config.detached))
+ if (config.flags & Config.Flags.detached)
+ while (!done.exists) Thread.sleep(10.msecs);
+ else
wait(pid);
+ while (remove(done).collectException) Thread.sleep(10.msecs);
}
// Files
void testFiles(Config config)
{
- import std.ascii, std.file, std.uuid, core.thread;
+ import std.ascii, std.file, std.uuid, core.thread, std.exception;
auto pathi = buildPath(tempDir(), randomUUID().toString());
auto patho = buildPath(tempDir(), randomUUID().toString());
auto pathe = buildPath(tempDir(), randomUUID().toString());
@@ -1115,17 +1744,18 @@ version (Posix) @system unittest
auto filei = File(pathi, "r");
auto fileo = File(patho, "w");
auto filee = File(pathe, "w");
- auto pid = spawnProcess([prog.path, "bar", "baz" ], filei, fileo, filee, null, config);
- if (!(config & Config.detached))
- wait(pid);
+ auto done = buildPath(tempDir(), randomUUID().toString());
+ auto pid = spawnProcess([prog.path, "bar", "baz", done], filei, fileo, filee, null, config);
+ if (config.flags & Config.Flags.detached)
+ while (!done.exists) Thread.sleep(10.msecs);
else
- // We need to wait a little to ensure that the process has finished and data was written to files
- Thread.sleep(2.seconds);
+ wait(pid);
assert(readText(patho).chomp() == "INPUT output bar");
assert(readText(pathe).chomp().stripRight() == "INPUT error baz");
- remove(pathi);
- remove(patho);
- remove(pathe);
+ while (remove(pathi).collectException) Thread.sleep(10.msecs);
+ while (remove(patho).collectException) Thread.sleep(10.msecs);
+ while (remove(pathe).collectException) Thread.sleep(10.msecs);
+ while (remove(done).collectException) Thread.sleep(10.msecs);
}
testPipes(Config.none);
@@ -1136,18 +1766,24 @@ version (Posix) @system unittest
@system unittest // Error handling in spawnProcess()
{
- import std.exception : assertThrown;
- assertThrown!ProcessException(spawnProcess("ewrgiuhrifuheiohnmnvqweoijwf"));
- assertThrown!ProcessException(spawnProcess("./rgiuhrifuheiohnmnvqweoijwf"));
- assertThrown!ProcessException(spawnProcess("ewrgiuhrifuheiohnmnvqweoijwf", null, Config.detached));
- assertThrown!ProcessException(spawnProcess("./rgiuhrifuheiohnmnvqweoijwf", null, Config.detached));
+ import std.algorithm.searching : canFind;
+ import std.exception : assertThrown, collectExceptionMsg;
+
+ static void testNotFoundException(string program)
+ {
+ assert(collectExceptionMsg!ProcessException(spawnProcess(program)).canFind(program));
+ assert(collectExceptionMsg!ProcessException(spawnProcess(program, null, Config.detached)).canFind(program));
+ }
+ testNotFoundException("ewrgiuhrifuheiohnmnvqweoijwf");
+ testNotFoundException("./rgiuhrifuheiohnmnvqweoijwf");
// can't execute malformed file with executable permissions
version (Posix)
{
import std.path : buildPath;
- import std.file : remove, write, setAttributes;
+ import std.file : remove, write, setAttributes, tempDir;
import core.sys.posix.sys.stat : S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH;
+ import std.conv : to;
string deleteme = buildPath(tempDir(), "deleteme.std.process.unittest.pid") ~ to!string(thisProcessID);
write(deleteme, "");
scope(exit) remove(deleteme);
@@ -1160,6 +1796,7 @@ version (Posix) @system unittest
@system unittest // Specifying a working directory.
{
import std.path;
+ import std.file;
TestScript prog = "echo foo>bar";
auto directory = uniqueTempPath();
@@ -1174,6 +1811,7 @@ version (Posix) @system unittest
@system unittest // Specifying a bad working directory.
{
import std.exception : assertThrown;
+ import std.file;
TestScript prog = "echo";
auto directory = uniqueTempPath();
@@ -1210,9 +1848,11 @@ version (Posix) @system unittest
spawnProcess([prog.path], null, Config.none, directory).wait();
}
-@system unittest // Reopening the standard streams (issue 13258)
+// Reopening the standard streams (https://issues.dlang.org/show_bug.cgi?id=13258)
+@system unittest
{
import std.string;
+ import std.file;
void fun()
{
spawnShell("echo foo").wait();
@@ -1235,10 +1875,12 @@ version (Posix) @system unittest
assert(lines == ["foo", "bar"]);
}
+// MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422)
version (Windows)
-@system unittest // MSVCRT workaround (issue 14422)
+@system unittest
{
auto fn = uniqueTempPath();
+ scope(exit) if (exists(fn)) remove(fn);
std.file.write(fn, "AAAAAAAAAA");
auto f = File(fn, "a");
@@ -1248,16 +1890,33 @@ version (Windows)
assert(data == "AAAAAAAAAABBBBB\r\n", data);
}
+// https://issues.dlang.org/show_bug.cgi?id=20765
+// Test that running processes with relative path works in conjunction
+// with indicating a workDir.
+version (Posix) @system unittest
+{
+ import std.file : mkdir, write, setAttributes, rmdirRecurse;
+ import std.conv : octal;
+
+ auto dir = uniqueTempPath();
+ mkdir(dir);
+ scope(exit) rmdirRecurse(dir);
+ write(dir ~ "/program", "#!/bin/sh\necho Hello");
+ setAttributes(dir ~ "/program", octal!700);
+
+ assert(execute(["./program"], null, Config.none, size_t.max, dir).output == "Hello\n");
+}
+
/**
A variation on $(LREF spawnProcess) that runs the given _command through
the current user's preferred _command interpreter (aka. shell).
-The string $(D command) is passed verbatim to the shell, and is therefore
+The string `command` is passed verbatim to the shell, and is therefore
subject to its rules about _command structure, argument/filename quoting
and escaping of special characters.
The path to the shell executable defaults to $(LREF nativeShell).
-In all other respects this function works just like $(D spawnProcess).
+In all other respects this function works just like `spawnProcess`.
Please refer to the $(LREF spawnProcess) documentation for descriptions
of the other function parameters, the return value and any exceptions
that may be thrown.
@@ -1272,15 +1931,15 @@ See_also:
$(LREF escapeShellCommand), which may be helpful in constructing a
properly quoted and escaped shell _command line for the current platform.
*/
-Pid spawnShell(in char[] command,
+Pid spawnShell(scope const(char)[] command,
File stdin = std.stdio.stdin,
File stdout = std.stdio.stdout,
File stderr = std.stdio.stderr,
- const string[string] env = null,
+ scope const string[string] env = null,
Config config = Config.none,
- in char[] workDir = null,
- string shellPath = nativeShell)
- @trusted // TODO: Should be @safe
+ scope const(char)[] workDir = null,
+ scope string shellPath = nativeShell)
+ @trusted // See reason below
{
version (Windows)
{
@@ -1288,8 +1947,9 @@ Pid spawnShell(in char[] command,
// It does not use CommandLineToArgvW.
// Instead, it treats the first and last quote specially.
// See CMD.EXE /? for details.
- auto args = escapeShellFileName(shellPath)
- ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`;
+ const commandLine = escapeShellFileName(shellPath)
+ ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`;
+ return spawnProcessWin(commandLine, shellPath, stdin, stdout, stderr, env, config, workDir);
}
else version (Posix)
{
@@ -1297,16 +1957,23 @@ Pid spawnShell(in char[] command,
args[0] = shellPath;
args[1] = shellSwitch;
args[2] = command;
+ /* The passing of args converts the static array, which is initialized with `scope` pointers,
+ * to a dynamic array, which is also a scope parameter. So, it is a scope pointer to a
+ * scope pointer, which although is safely used here, D doesn't allow transitive scope.
+ * See https://github.com/dlang/dmd/pull/10951
+ */
+ return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir);
}
- return spawnProcessImpl(args, stdin, stdout, stderr, env, config, workDir);
+ else
+ static assert(0);
}
/// ditto
-Pid spawnShell(in char[] command,
- const string[string] env,
+Pid spawnShell(scope const(char)[] command,
+ scope const string[string] env,
Config config = Config.none,
- in char[] workDir = null,
- string shellPath = nativeShell)
+ scope const(char)[] workDir = null,
+ scope string shellPath = nativeShell)
@trusted // TODO: Should be @safe
{
return spawnShell(command,
@@ -1343,6 +2010,7 @@ version (Windows)
@system unittest
{
import std.string;
+ import std.conv : text;
TestScript prog = "echo %0 %*";
auto outputFn = uniqueTempPath();
scope(exit) if (exists(outputFn)) remove(outputFn);
@@ -1358,12 +2026,10 @@ version (Windows)
/**
-Flags that control the behaviour of process creation functions in this
-module. Most flags only apply to $(LREF spawnProcess) and
+Options that control the behaviour of process creation functions in this
+module. Most options only apply to $(LREF spawnProcess) and
$(LREF spawnShell).
-Use bitwise OR to combine flags.
-
Example:
---
auto logFile = File("myapp_error.log", "w");
@@ -1381,76 +2047,142 @@ scope(exit)
}
---
*/
-enum Config
+struct Config
{
- none = 0,
-
- /**
- By default, the child process inherits the parent's environment,
- and any environment variables passed to $(LREF spawnProcess) will
- be added to it. If this flag is set, the only variables in the
- child process' environment will be those given to spawnProcess.
- */
- newEnv = 1,
-
- /**
- Unless the child process inherits the standard input/output/error
- streams of its parent, one almost always wants the streams closed
- in the parent when $(LREF spawnProcess) returns. Therefore, by
- default, this is done. If this is not desirable, pass any of these
- options to spawnProcess.
- */
- retainStdin = 2,
- retainStdout = 4, /// ditto
- retainStderr = 8, /// ditto
-
/**
- On Windows, if the child process is a console application, this
- flag will prevent the creation of a console window. Otherwise,
- it will be ignored. On POSIX, $(D suppressConsole) has no effect.
- */
- suppressConsole = 16,
-
- /**
- On POSIX, open $(LINK2 http://en.wikipedia.org/wiki/File_descriptor,file descriptors)
- are by default inherited by the child process. As this may lead
- to subtle bugs when pipes or multiple threads are involved,
- $(LREF spawnProcess) ensures that all file descriptors except the
- ones that correspond to standard input/output/error are closed
- in the child process when it starts. Use $(D inheritFDs) to prevent
- this.
-
- On Windows, this option has no effect, and any handles which have been
- explicitly marked as inheritable will always be inherited by the child
- process.
- */
- inheritFDs = 32,
+ Flag options.
+ Use bitwise OR to combine flags.
+ **/
+ enum Flags
+ {
+ none = 0,
+
+ /**
+ By default, the child process inherits the parent's environment,
+ and any environment variables passed to $(LREF spawnProcess) will
+ be added to it. If this flag is set, the only variables in the
+ child process' environment will be those given to spawnProcess.
+ */
+ newEnv = 1,
+
+ /**
+ Unless the child process inherits the standard input/output/error
+ streams of its parent, one almost always wants the streams closed
+ in the parent when $(LREF spawnProcess) returns. Therefore, by
+ default, this is done. If this is not desirable, pass any of these
+ options to spawnProcess.
+ */
+ retainStdin = 2,
+ retainStdout = 4, /// ditto
+ retainStderr = 8, /// ditto
+
+ /**
+ On Windows, if the child process is a console application, this
+ flag will prevent the creation of a console window. Otherwise,
+ it will be ignored. On POSIX, `suppressConsole` has no effect.
+ */
+ suppressConsole = 16,
+
+ /**
+ On POSIX, open $(LINK2 http://en.wikipedia.org/wiki/File_descriptor,file descriptors)
+ are by default inherited by the child process. As this may lead
+ to subtle bugs when pipes or multiple threads are involved,
+ $(LREF spawnProcess) ensures that all file descriptors except the
+ ones that correspond to standard input/output/error are closed
+ in the child process when it starts. Use `inheritFDs` to prevent
+ this.
+
+ On Windows, this option has no effect, and any handles which have been
+ explicitly marked as inheritable will always be inherited by the child
+ process.
+ */
+ inheritFDs = 32,
+
+ /**
+ Spawn process in detached state. This removes the need in calling
+ $(LREF wait) to clean up the process resources.
+
+ Note:
+ Calling $(LREF wait) or $(LREF kill) with the resulting `Pid` is invalid.
+ */
+ detached = 64,
+
+ /**
+ By default, the $(LREF execute) and $(LREF executeShell) functions
+ will capture child processes' both stdout and stderr. This can be
+ undesirable if the standard output is to be processed or otherwise
+ used by the invoking program, as `execute`'s result would then
+ contain a mix of output and warning/error messages.
+
+ Specify this flag when calling `execute` or `executeShell` to
+ cause invoked processes' stderr stream to be sent to $(REF stderr,
+ std,stdio), and only capture and return standard output.
+
+ This flag has no effect on $(LREF spawnProcess) or $(LREF spawnShell).
+ */
+ stderrPassThrough = 128,
+ }
+ Flags flags; /// ditto
/**
- Spawn process in detached state. This removes the need in calling
- $(LREF wait) to clean up the process resources.
-
- Note:
- Calling $(LREF wait) or $(LREF kill) with the resulting $(D Pid) is invalid.
+ For backwards compatibility, and cases when only flags need to
+ be specified in the `Config`, these allow building `Config`
+ instances using flag names only.
*/
- detached = 64,
+ enum Config none = Config.init;
+ enum Config newEnv = Config(Flags.newEnv); /// ditto
+ enum Config retainStdin = Config(Flags.retainStdin); /// ditto
+ enum Config retainStdout = Config(Flags.retainStdout); /// ditto
+ enum Config retainStderr = Config(Flags.retainStderr); /// ditto
+ enum Config suppressConsole = Config(Flags.suppressConsole); /// ditto
+ enum Config inheritFDs = Config(Flags.inheritFDs); /// ditto
+ enum Config detached = Config(Flags.detached); /// ditto
+ enum Config stderrPassThrough = Config(Flags.stderrPassThrough); /// ditto
+ Config opUnary(string op)()
+ if (is(typeof(mixin(op ~ q{flags}))))
+ {
+ return Config(mixin(op ~ q{flags}));
+ } /// ditto
+ Config opBinary(string op)(Config other)
+ if (is(typeof(mixin(q{flags} ~ op ~ q{other.flags}))))
+ {
+ return Config(mixin(q{flags} ~ op ~ q{other.flags}));
+ } /// ditto
+ Config opOpAssign(string op)(Config other)
+ if (is(typeof(mixin(q{flags} ~ op ~ q{=other.flags}))))
+ {
+ return Config(mixin(q{flags} ~ op ~ q{=other.flags}));
+ } /// ditto
- /**
- By default, the $(LREF execute) and $(LREF executeShell) functions
- will capture child processes' both stdout and stderr. This can be
- undesirable if the standard output is to be processed or otherwise
- used by the invoking program, as `execute`'s result would then
- contain a mix of output and warning/error messages.
+ version (StdDdoc)
+ {
+ /**
+ A function that is called before `exec` in $(LREF spawnProcess).
+ It returns `true` if succeeded and otherwise returns `false`.
- Specify this flag when calling `execute` or `executeShell` to
- cause invoked processes' stderr stream to be sent to $(REF stderr,
- std,stdio), and only capture and return standard output.
+ $(RED Warning:
+ Please note that the code in this function must only use
+ async-signal-safe functions.)
- This flag has no effect on $(LREF spawnProcess) or $(LREF spawnShell).
- */
- stderrPassThrough = 128,
+ On Windows, this member is not available.
+ */
+ bool function() nothrow @nogc @safe preExecFunction;
+ }
+ else version (Posix)
+ {
+ bool function() nothrow @nogc @safe preExecFunction;
+ }
}
+// https://issues.dlang.org/show_bug.cgi?id=22125
+@safe unittest
+{
+ Config c = Config.retainStdin;
+ c |= Config.retainStdout;
+ c |= Config.retainStderr;
+ c &= ~Config.retainStderr;
+ assert(c == (Config.retainStdin | Config.retainStdout));
+}
/// A handle that corresponds to a spawned process.
final class Pid
@@ -1472,21 +2204,21 @@ final class Pid
An operating system handle to the process.
This handle is used to specify the process in OS-specific APIs.
- On POSIX, this function returns a $(D core.sys.posix.sys.types.pid_t)
+ On POSIX, this function returns a `core.sys.posix.sys.types.pid_t`
with the same value as $(LREF Pid.processID), while on Windows it returns
- a $(D core.sys.windows.windows.HANDLE).
+ a `core.sys.windows.windows.HANDLE`.
Once $(LREF wait) has been called on the $(LREF Pid), this method
will return an invalid handle.
*/
// Note: Since HANDLE is a reference, this function cannot be const.
version (Windows)
- @property HANDLE osHandle() @safe pure nothrow
+ @property HANDLE osHandle() @nogc @safe pure nothrow
{
return _handle;
}
else version (Posix)
- @property pid_t osHandle() @safe pure nothrow
+ @property pid_t osHandle() @nogc @safe pure nothrow
{
return _processID;
}
@@ -1507,8 +2239,8 @@ private:
version (Posix)
int performWait(bool block) @trusted
{
- import std.exception : enforceEx;
- enforceEx!ProcessException(owned, "Can't wait on a detached process");
+ import std.exception : enforce;
+ enforce!ProcessException(owned, "Can't wait on a detached process");
if (_processID == terminated) return _exitCode;
int exitCode;
while (true)
@@ -1555,17 +2287,23 @@ private:
}
else version (Windows)
{
- int performWait(bool block) @trusted
+ int performWait(const bool block, const DWORD timeout = INFINITE) @trusted
{
- import std.exception : enforceEx;
- enforceEx!ProcessException(owned, "Can't wait on a detached process");
+ import std.exception : enforce;
+ enforce!ProcessException(owned, "Can't wait on a detached process");
if (_processID == terminated) return _exitCode;
assert(_handle != INVALID_HANDLE_VALUE);
if (block)
{
- auto result = WaitForSingleObject(_handle, INFINITE);
+ auto result = WaitForSingleObject(_handle, timeout);
if (result != WAIT_OBJECT_0)
+ {
+ // Wait time exceeded `timeout` milliseconds?
+ if (result == WAIT_TIMEOUT && timeout != INFINITE)
+ return 0;
+
throw ProcessException.newFromLastError("Wait failed.");
+ }
}
if (!GetExitCodeProcess(_handle, cast(LPDWORD)&_exitCode))
throw ProcessException.newFromLastError();
@@ -1576,6 +2314,20 @@ private:
return _exitCode;
}
+ int performWait(Duration timeout) @safe
+ {
+ import std.exception : enforce;
+ const msecs = timeout.total!"msecs";
+
+ // Limit this implementation the maximum wait time offered by
+ // WaitForSingleObject. One could theoretically break up larger
+ // durations into multiple waits but (DWORD.max - 1).msecs
+ // (> 7 weeks, 17 hours) should be enough for the usual case.
+ // DWORD.max is reserved for INFINITE
+ enforce!ProcessException(msecs < DWORD.max, "Timeout exceeds maximum wait time!");
+ return performWait(true, cast(DWORD) msecs);
+ }
+
~this()
{
if (_handle != INVALID_HANDLE_VALUE)
@@ -1630,12 +2382,12 @@ private:
/**
-Waits for the process associated with $(D pid) to terminate, and returns
+Waits for the process associated with `pid` to terminate, and returns
its exit status.
In general one should always _wait for child processes to terminate
before exiting the parent process unless the process was spawned as detached
-(that was spawned with $(D Config.detached) flag).
+(that was spawned with `Config.detached` flag).
Otherwise, they may become "$(HTTP en.wikipedia.org/wiki/Zombie_process,zombies)"
– processes that are defunct, yet still occupy a slot in the OS process table.
You should not and must not wait for detached processes, since you don't own them.
@@ -1649,8 +2401,8 @@ If the process is terminated by a signal, this function returns a
negative number whose absolute value is the signal number.
Since POSIX restricts normal exit codes to the range 0-255, a
negative return value will always indicate termination by signal.
-Signal codes are defined in the $(D core.sys.posix.signal) module
-(which corresponds to the $(D signal.h) POSIX header).
+Signal codes are defined in the `core.sys.posix.signal` module
+(which corresponds to the `signal.h` POSIX header).
Throws:
$(LREF ProcessException) on failure or on attempt to wait for detached process.
@@ -1685,22 +2437,83 @@ int wait(Pid pid) @safe
else version (Posix) assert(pid.osHandle < 0);
}
+private import std.typecons : Tuple;
+
+/**
+Waits until either the process associated with `pid` terminates or the
+elapsed time exceeds the given timeout.
+
+If the process terminates within the given duration it behaves exactly like
+`wait`, except that it returns a tuple `(true, exit code)`.
+
+If the process does not terminate within the given duration it will stop
+waiting and return `(false, 0).`
+
+The timeout may not exceed `(uint.max - 1).msecs` (~ 7 weeks, 17 hours).
+
+$(BLUE This function is Windows-Only.)
+
+Returns:
+An $(D std.typecons.Tuple!(bool, "terminated", int, "status")).
+
+Throws:
+$(LREF ProcessException) on failure or on attempt to wait for detached process.
+
+Example:
+See the $(LREF spawnProcess) documentation.
+
+See_also:
+$(LREF wait), for a blocking function without timeout.
+$(LREF tryWait), for a non-blocking function without timeout.
+*/
+version (StdDdoc)
+Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe;
+
+else version (Windows)
+Tuple!(bool, "terminated", int, "status") waitTimeout(Pid pid, Duration timeout) @safe
+{
+ assert(pid !is null, "Called wait on a null Pid.");
+ auto code = pid.performWait(timeout);
+ return typeof(return)(pid._processID == Pid.terminated, code);
+}
+
+version (Windows)
+@system unittest // Pid and waitTimeout()
+{
+ import std.exception : collectException;
+ import std.typecons : tuple;
+
+ TestScript prog = ":Loop\ngoto Loop;";
+ auto pid = spawnProcess(prog.path);
+
+ // Doesn't block longer than one second
+ assert(waitTimeout(pid, 1.seconds) == tuple(false, 0));
+
+ kill(pid);
+ assert(waitTimeout(pid, 1.seconds) == tuple(true, 1)); // exit 1 because the process is killed
+
+ // Rejects timeouts exceeding the Windows API capabilities
+ const dur = DWORD.max.msecs;
+ const ex = collectException!ProcessException(waitTimeout(pid, dur));
+ assert(ex);
+ assert(ex.msg == "Timeout exceeds maximum wait time!");
+}
/**
A non-blocking version of $(LREF wait).
-If the process associated with $(D pid) has already terminated,
-$(D tryWait) has the exact same effect as $(D wait).
-In this case, it returns a tuple where the $(D terminated) field
-is set to $(D true) and the $(D status) field has the same
-interpretation as the return value of $(D wait).
+If the process associated with `pid` has already terminated,
+`tryWait` has the exact same effect as `wait`.
+In this case, it returns a tuple where the `terminated` field
+is set to `true` and the `status` field has the same
+interpretation as the return value of `wait`.
If the process has $(I not) yet terminated, this function differs
-from $(D wait) in that does not wait for this to happen, but instead
-returns immediately. The $(D terminated) field of the returned
-tuple will then be set to $(D false), while the $(D status) field
-will always be 0 (zero). $(D wait) or $(D tryWait) should then be
-called again on the same $(D Pid) at some later time; not only to
+from `wait` in that does not wait for this to happen, but instead
+returns immediately. The `terminated` field of the returned
+tuple will then be set to `false`, while the `status` field
+will always be 0 (zero). `wait` or `tryWait` should then be
+called again on the same `Pid` at some later time; not only to
get the exit code, but also to avoid the process becoming a "zombie"
when it finally terminates. (See $(LREF wait) for details).
@@ -1724,9 +2537,9 @@ if (dmd.terminated)
else writeln("Still compiling...");
...
---
-Note that in this example, the first $(D wait) call will have no
-effect if the process has already terminated by the time $(D tryWait)
-is called. In the opposite case, however, the $(D scope) statement
+Note that in this example, the first `wait` call will have no
+effect if the process has already terminated by the time `tryWait`
+is called. In the opposite case, however, the `scope` statement
ensures that we always wait for the process if it hasn't terminated
by the time we reach the end of the scope.
*/
@@ -1741,21 +2554,21 @@ auto tryWait(Pid pid) @safe
/**
-Attempts to terminate the process associated with $(D pid).
+Attempts to terminate the process associated with `pid`.
-The effect of this function, as well as the meaning of $(D codeOrSignal),
+The effect of this function, as well as the meaning of `codeOrSignal`,
is highly platform dependent. Details are given below. Common to all
platforms is that this function only $(I initiates) termination of the process,
and returns immediately. It does not wait for the process to end,
nor does it guarantee that the process does in fact get terminated.
-Always call $(LREF wait) to wait for a process to complete, even if $(D kill)
+Always call $(LREF wait) to wait for a process to complete, even if `kill`
has been called on it.
Windows_specific:
The process will be
$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.100%29.aspx,
-forcefully and abruptly terminated). If $(D codeOrSignal) is specified, it
+forcefully and abruptly terminated). If `codeOrSignal` is specified, it
must be a nonnegative number which will be used as the exit code of the process.
If not, the process wil exit with code 1. Do not use $(D codeOrSignal = 259),
as this is a special value (aka. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683189.aspx,STILL_ACTIVE))
@@ -1768,15 +2581,15 @@ assert(wait(pid) == 10);
POSIX_specific:
A $(LINK2 http://en.wikipedia.org/wiki/Unix_signal,signal) will be sent to
-the process, whose value is given by $(D codeOrSignal). Depending on the
+the process, whose value is given by `codeOrSignal`. Depending on the
signal sent, this may or may not terminate the process. Symbolic constants
for various $(LINK2 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals,
-POSIX signals) are defined in $(D core.sys.posix.signal), which corresponds to the
+POSIX signals) are defined in `core.sys.posix.signal`, which corresponds to the
$(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html,
-$(D signal.h) POSIX header). If $(D codeOrSignal) is omitted, the
-$(D SIGTERM) signal will be sent. (This matches the behaviour of the
+`signal.h` POSIX header). If `codeOrSignal` is omitted, the
+`SIGTERM` signal will be sent. (This matches the behaviour of the
$(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html,
-$(D _kill)) shell command.)
+`_kill`) shell command.)
---
import core.sys.posix.signal : SIGKILL;
auto pid = spawnProcess("some_app");
@@ -1803,8 +2616,8 @@ void kill(Pid pid)
/// ditto
void kill(Pid pid, int codeOrSignal)
{
- import std.exception : enforceEx;
- enforceEx!ProcessException(pid.owned, "Can't kill detached process");
+ import std.exception : enforce;
+ enforce!ProcessException(pid.owned, "Can't kill detached process");
version (Windows)
{
if (codeOrSignal < 0) throw new ProcessException("Invalid exit code");
@@ -1844,13 +2657,13 @@ void kill(Pid pid, int codeOrSignal)
version (Android)
Thread.sleep(dur!"msecs"(5));
else
- Thread.sleep(dur!"seconds"(1));
+ Thread.sleep(dur!"msecs"(500));
kill(pid);
version (Windows) assert(wait(pid) == 1);
else version (Posix) assert(wait(pid) == -SIGTERM);
pid = spawnProcess(prog.path);
- Thread.sleep(dur!"seconds"(1));
+ Thread.sleep(dur!"msecs"(500));
auto s = tryWait(pid);
assert(!s.terminated && s.status == 0);
assertThrown!ProcessException(kill(pid, -123)); // Negative code not allowed.
@@ -1874,7 +2687,7 @@ void kill(Pid pid, int codeOrSignal)
This leads to the annoying message like "/bin/sh: 0: Can't open /tmp/std.process temporary file" to appear when running tests.
It does not happen in unittests with non-detached processes because we always wait() for them to finish.
*/
- Thread.sleep(1.seconds);
+ Thread.sleep(500.msecs);
assert(!pid.owned);
version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE);
assertThrown!ProcessException(wait(pid));
@@ -2021,16 +2834,16 @@ private:
Starts a new process, creating pipes to redirect its standard
input, output and/or error streams.
-$(D pipeProcess) and $(D pipeShell) are convenient wrappers around
+`pipeProcess` and `pipeShell` are convenient wrappers around
$(LREF spawnProcess) and $(LREF spawnShell), respectively, and
automate the task of redirecting one or more of the child process'
standard streams through pipes. Like the functions they wrap,
these functions return immediately, leaving the child process to
execute in parallel with the invoking process. It is recommended
to always call $(LREF wait) on the returned $(LREF ProcessPipes.pid),
-as detailed in the documentation for $(D wait).
+as detailed in the documentation for `wait`.
-The $(D args)/$(D program)/$(D command), $(D env) and $(D config)
+The `args`/`program`/`command`, `env` and `config`
parameters are forwarded straight to the underlying spawn functions,
and we refer to their documentation for details.
@@ -2049,7 +2862,7 @@ env = Additional environment variables for the child process.
(See $(LREF spawnProcess) for details.)
config = Flags that control process creation. See $(LREF Config)
for an overview of available flags, and note that the
- $(D retainStd...) flags have no effect in this function.
+ `retainStd...` flags have no effect in this function.
workDir = The working directory for the new process.
By default the child process inherits the parent's working
directory.
@@ -2103,33 +2916,33 @@ wait(pipes.pid);
---
*/
-ProcessPipes pipeProcess(in char[][] args,
+ProcessPipes pipeProcess(scope const(char[])[] args,
Redirect redirect = Redirect.all,
const string[string] env = null,
Config config = Config.none,
- in char[] workDir = null)
+ scope const(char)[] workDir = null)
@safe
{
return pipeProcessImpl!spawnProcess(args, redirect, env, config, workDir);
}
/// ditto
-ProcessPipes pipeProcess(in char[] program,
+ProcessPipes pipeProcess(scope const(char)[] program,
Redirect redirect = Redirect.all,
const string[string] env = null,
Config config = Config.none,
- in char[] workDir = null)
+ scope const(char)[] workDir = null)
@safe
{
return pipeProcessImpl!spawnProcess(program, redirect, env, config, workDir);
}
/// ditto
-ProcessPipes pipeShell(in char[] command,
+ProcessPipes pipeShell(scope const(char)[] command,
Redirect redirect = Redirect.all,
const string[string] env = null,
Config config = Config.none,
- in char[] workDir = null,
+ scope const(char)[] workDir = null,
string shellPath = nativeShell)
@safe
{
@@ -2143,11 +2956,11 @@ ProcessPipes pipeShell(in char[] command,
// Implementation of the pipeProcess() family of functions.
private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd, ExtraSpawnFuncArgs...)
- (Cmd command,
+ (scope Cmd command,
Redirect redirectFlags,
const string[string] env = null,
Config config = Config.none,
- in char[] workDir = null,
+ scope const(char)[] workDir = null,
ExtraSpawnFuncArgs extraArgs = ExtraSpawnFuncArgs.init)
@trusted //TODO: @safe
{
@@ -2213,7 +3026,7 @@ private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd, ExtraSpawnFuncArgs...
childStderr = childStdout;
}
- config &= ~(Config.retainStdin | Config.retainStdout | Config.retainStderr);
+ config.flags &= ~(Config.Flags.retainStdin | Config.Flags.retainStdout | Config.Flags.retainStderr);
pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr,
env, config, workDir, extraArgs);
return pipes;
@@ -2240,13 +3053,13 @@ enum Redirect
/**
Redirect the standard error stream into the standard output stream.
- This can not be combined with $(D Redirect.stderr).
+ This can not be combined with `Redirect.stderr`.
*/
stderrToStdout = 8,
/**
Redirect the standard output stream into the standard error stream.
- This can not be combined with $(D Redirect.stdout).
+ This can not be combined with `Redirect.stdout`.
*/
stdoutToStderr = 16,
}
@@ -2400,7 +3213,7 @@ private:
Executes the given program or shell command and returns its exit
code and output.
-$(D execute) and $(D executeShell) start a new process using
+`execute` and `executeShell` start a new process using
$(LREF spawnProcess) and $(LREF spawnShell), respectively, and wait
for the process to complete before returning. The functions capture
what the child process prints to both its standard output and
@@ -2414,7 +3227,7 @@ if (ls.status != 0) writeln("Failed to retrieve file listing");
else writeln(ls.output);
---
-The $(D args)/$(D program)/$(D command), $(D env) and $(D config)
+The `args`/`program`/`command`, `env` and `config`
parameters are forwarded straight to the underlying spawn functions,
and we refer to their documentation for details.
@@ -2430,7 +3243,7 @@ env = Additional environment variables for the child process.
(See $(LREF spawnProcess) for details.)
config = Flags that control process creation. See $(LREF Config)
for an overview of available flags, and note that the
- $(D retainStd...) flags have no effect in this function.
+ `retainStd...` flags have no effect in this function.
maxOutput = The maximum number of bytes of output that should be
captured.
workDir = The working directory for the new process.
@@ -2444,7 +3257,7 @@ Returns:
An $(D std.typecons.Tuple!(int, "status", string, "output")).
POSIX_specific:
-If the process is terminated by a signal, the $(D status) field of
+If the process is terminated by a signal, the `status` field of
the return value will contain a negative number whose absolute
value is the signal number. (See $(LREF wait) for details.)
@@ -2452,35 +3265,35 @@ Throws:
$(LREF ProcessException) on failure to start the process.$(BR)
$(REF StdioException, std,stdio) on failure to capture output.
*/
-auto execute(in char[][] args,
+auto execute(scope const(char[])[] args,
const string[string] env = null,
Config config = Config.none,
size_t maxOutput = size_t.max,
- in char[] workDir = null)
- @trusted //TODO: @safe
+ scope const(char)[] workDir = null)
+ @safe
{
return executeImpl!pipeProcess(args, env, config, maxOutput, workDir);
}
/// ditto
-auto execute(in char[] program,
+auto execute(scope const(char)[] program,
const string[string] env = null,
Config config = Config.none,
size_t maxOutput = size_t.max,
- in char[] workDir = null)
- @trusted //TODO: @safe
+ scope const(char)[] workDir = null)
+ @safe
{
return executeImpl!pipeProcess(program, env, config, maxOutput, workDir);
}
/// ditto
-auto executeShell(in char[] command,
+auto executeShell(scope const(char)[] command,
const string[string] env = null,
Config config = Config.none,
size_t maxOutput = size_t.max,
- in char[] workDir = null,
+ scope const(char)[] workDir = null,
string shellPath = nativeShell)
- @trusted //TODO: @safe
+ @safe
{
return executeImpl!pipeShell(command,
env,
@@ -2496,21 +3309,22 @@ private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)(
const string[string] env = null,
Config config = Config.none,
size_t maxOutput = size_t.max,
- in char[] workDir = null,
+ scope const(char)[] workDir = null,
ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init)
+ @trusted //TODO: @safe
{
import std.algorithm.comparison : min;
import std.array : appender;
import std.typecons : Tuple;
- auto redirect = (config & Config.stderrPassThrough)
+ auto redirect = (config.flags & Config.Flags.stderrPassThrough)
? Redirect.stdout
: Redirect.stdout | Redirect.stderrToStdout;
auto p = pipeFunc(commandLine, redirect,
env, config, workDir, extraArgs);
- auto a = appender!(ubyte[])();
+ auto a = appender!string;
enum size_t defaultChunkSize = 4096;
immutable chunkSize = min(maxOutput, defaultChunkSize);
@@ -2529,7 +3343,7 @@ private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)(
// Exhaust the stream, if necessary.
foreach (ubyte[] chunk; p.stdout.byChunk(defaultChunkSize)) { }
- return Tuple!(int, "status", string, "output")(wait(p.pid), cast(string) a.data);
+ return Tuple!(int, "status", string, "output")(wait(p.pid), a.data);
}
@system unittest
@@ -2576,7 +3390,7 @@ private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)(
// Temporarily disable output to stderr so as to not spam the build log.
import std.stdio : stderr;
import std.typecons : Tuple;
- import std.file : readText;
+ import std.file : readText, exists, remove;
import std.traits : ReturnType;
ReturnType!executeShell r;
@@ -2657,7 +3471,7 @@ Determines the path to the current user's preferred command interpreter.
On Windows, this function returns the contents of the COMSPEC environment
variable, if it exists. Otherwise, it returns the result of $(LREF nativeShell).
-On POSIX, $(D userShell) returns the contents of the SHELL environment
+On POSIX, `userShell` returns the contents of the SHELL environment
variable, if it exists and is non-empty. Otherwise, it returns the result of
$(LREF nativeShell).
*/
@@ -2670,8 +3484,8 @@ $(LREF nativeShell).
/**
The platform-specific native shell path.
-This function returns $(D "cmd.exe") on Windows, $(D "/bin/sh") on POSIX, and
-$(D "/system/bin/sh") on Android.
+This function returns `"cmd.exe"` on Windows, `"/bin/sh"` on POSIX, and
+`"/system/bin/sh"` on Android.
*/
@property string nativeShell() @safe @nogc pure nothrow
{
@@ -2685,74 +3499,12 @@ $(D "/system/bin/sh") on Android.
version (Posix) private immutable string shellSwitch = "-c";
version (Windows) private immutable string shellSwitch = "/C";
-
-/**
- * Returns the process ID of the current process,
- * which is guaranteed to be unique on the system.
- *
- * Example:
- * ---
- * writefln("Current process ID: %d", thisProcessID);
- * ---
- */
-@property int thisProcessID() @trusted nothrow //TODO: @safe
-{
- version (Windows) return GetCurrentProcessId();
- else version (Posix) return core.sys.posix.unistd.getpid();
-}
-
-
-/**
- * Returns the process ID of the current thread,
- * which is guaranteed to be unique within the current process.
- *
- * Returns:
- * A $(REF ThreadID, core,thread) value for the calling thread.
- *
- * Example:
- * ---
- * writefln("Current thread ID: %s", thisThreadID);
- * ---
- */
-@property ThreadID thisThreadID() @trusted nothrow //TODO: @safe
-{
- version (Windows)
- return GetCurrentThreadId();
- else
- version (Posix)
- {
- import core.sys.posix.pthread : pthread_self;
- return pthread_self();
- }
-}
-
-
-@system unittest
-{
- int pidA, pidB;
- ThreadID tidA, tidB;
- pidA = thisProcessID;
- tidA = thisThreadID;
-
- import core.thread;
- auto t = new Thread({
- pidB = thisProcessID;
- tidB = thisThreadID;
- });
- t.start();
- t.join();
-
- assert(pidA == pidB);
- assert(tidA != tidB);
-}
-
-
// Unittest support code: TestScript takes a string that contains a
// shell script for the current platform, and writes it to a temporary
// file. On Windows the file name gets a .cmd extension, while on
// POSIX its executable permission bit is set. The file is
// automatically deleted when the object goes out of scope.
-version (unittest)
+version (StdUnittest)
private struct TestScript
{
this(string code) @system
@@ -2775,6 +3527,7 @@ private struct TestScript
version (Posix)
{
import core.sys.posix.sys.stat : chmod;
+ import std.conv : octal;
chmod(path.tempCString(), octal!777);
}
}
@@ -2795,16 +3548,6 @@ private struct TestScript
string path;
}
-package(std) string uniqueTempPath() @safe
-{
- import std.file : tempDir;
- import std.path : buildPath;
- import std.uuid : randomUUID;
- // Path should contain spaces to test escaping whitespace
- return buildPath(tempDir(), "std.process temporary file " ~
- randomUUID().toString());
-}
-
// =============================================================================
// Functions for shell command quoting/escaping.
@@ -2837,7 +3580,7 @@ string url = "http://dlang.org/";
executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html"));
---
-Concatenate multiple $(D escapeShellCommand) and
+Concatenate multiple `escapeShellCommand` and
$(LREF escapeShellFileName) results to use shell redirection or
piping operators.
---
@@ -2853,7 +3596,7 @@ Throws:
$(OBJECTREF Exception) if any part of the command line contains unescapable
characters (NUL on all platforms, as well as CR and LF on Windows).
*/
-string escapeShellCommand(in char[][] args...) @safe pure
+string escapeShellCommand(scope const(char[])[] args...) @safe pure
{
if (args.empty)
return null;
@@ -2919,7 +3662,7 @@ string escapeShellCommand(in char[][] args...) @safe pure
assert(escapeShellCommand(test.args) == test.posix );
}
-private string escapeShellCommandString(string command) @safe pure
+private string escapeShellCommandString(return scope string command) @safe pure
{
version (Windows)
return escapeWindowsShellCommand(command);
@@ -2927,7 +3670,7 @@ private string escapeShellCommandString(string command) @safe pure
return command;
}
-private string escapeWindowsShellCommand(in char[] command) @safe pure
+private string escapeWindowsShellCommand(scope const(char)[] command) @safe pure
{
import std.array : appender;
auto result = appender!string();
@@ -2958,7 +3701,7 @@ private string escapeWindowsShellCommand(in char[] command) @safe pure
return result.data;
}
-private string escapeShellArguments(in char[][] args...)
+private string escapeShellArguments(scope const(char[])[] args...)
@trusted pure nothrow
{
import std.exception : assumeUnique;
@@ -2983,7 +3726,7 @@ private string escapeShellArguments(in char[][] args...)
return assumeUnique(buf);
}
-private auto escapeShellArgument(alias allocator)(in char[] arg) @safe nothrow
+private auto escapeShellArgument(alias allocator)(scope const(char)[] arg) @safe nothrow
{
// The unittest for this function requires special
// preparation - see below.
@@ -2999,7 +3742,7 @@ Quotes a command-line argument in a manner conforming to the behavior of
$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx,
CommandLineToArgvW).
*/
-string escapeWindowsArgument(in char[] arg) @trusted pure nothrow
+string escapeWindowsArgument(scope const(char)[] arg) @trusted pure nothrow
{
// Rationale for leaving this function as public:
// this algorithm of escaping paths is also used in other software,
@@ -3016,7 +3759,7 @@ private char[] charAllocator(size_t size) @safe pure nothrow
}
-private char[] escapeWindowsArgumentImpl(alias allocator)(in char[] arg)
+private char[] escapeWindowsArgumentImpl(alias allocator)(scope const(char)[] arg)
@safe nothrow
if (is(typeof(allocator(size_t.init)[0] = char.init)))
{
@@ -3093,21 +3836,24 @@ if (is(typeof(allocator(size_t.init)[0] = char.init)))
return buf;
}
-version (Windows) version (unittest)
+version (Windows) version (StdUnittest)
{
+private:
import core.stdc.stddef;
import core.stdc.wchar_ : wcslen;
import core.sys.windows.shellapi : CommandLineToArgvW;
- import core.sys.windows.windows;
+ import core.sys.windows.winbase;
+ import core.sys.windows.winnt;
import std.array;
string[] parseCommandLine(string line)
{
import std.algorithm.iteration : map;
import std.array : array;
- LPWSTR lpCommandLine = (to!(wchar[])(line) ~ "\0"w).ptr;
+ import std.conv : to;
+ auto lpCommandLine = (to!(WCHAR[])(line) ~ '\0').ptr;
int numArgs;
- LPWSTR* args = CommandLineToArgvW(lpCommandLine, &numArgs);
+ auto args = CommandLineToArgvW(lpCommandLine, &numArgs);
scope(exit) LocalFree(args);
return args[0 .. numArgs]
.map!(arg => to!string(arg[0 .. wcslen(arg)]))
@@ -3116,6 +3862,7 @@ version (Windows) version (unittest)
@system unittest
{
+ import std.conv : text;
string[] testStrings = [
`Hello`,
`Hello, world`,
@@ -3142,14 +3889,14 @@ version (Windows) version (unittest)
}
}
-private string escapePosixArgument(in char[] arg) @trusted pure nothrow
+private string escapePosixArgument(scope const(char)[] arg) @trusted pure nothrow
{
import std.exception : assumeUnique;
auto buf = escapePosixArgumentImpl!charAllocator(arg);
return assumeUnique(buf);
}
-private char[] escapePosixArgumentImpl(alias allocator)(in char[] arg)
+private char[] escapePosixArgumentImpl(alias allocator)(scope const(char)[] arg)
@safe nothrow
if (is(typeof(allocator(size_t.init)[0] = char.init)))
{
@@ -3185,7 +3932,7 @@ if (is(typeof(allocator(size_t.init)[0] = char.init)))
Escapes a filename to be used for shell redirection with $(LREF spawnShell),
$(LREF pipeShell) or $(LREF executeShell).
*/
-string escapeShellFileName(in char[] fileName) @trusted pure nothrow
+string escapeShellFileName(scope const(char)[] fileName) @trusted pure nothrow
{
// The unittest for this function requires special
// preparation - see below.
@@ -3319,424 +4066,6 @@ version (unittest_burnin)
}
}
-
-// =============================================================================
-// Environment variable manipulation.
-// =============================================================================
-
-
-/**
-Manipulates _environment variables using an associative-array-like
-interface.
-
-This class contains only static methods, and cannot be instantiated.
-See below for examples of use.
-*/
-abstract final class environment
-{
-static:
- /**
- Retrieves the value of the environment variable with the given $(D name).
- ---
- auto path = environment["PATH"];
- ---
-
- Throws:
- $(OBJECTREF Exception) if the environment variable does not exist,
- or $(REF UTFException, std,utf) if the variable contains invalid UTF-16
- characters (Windows only).
-
- See_also:
- $(LREF environment.get), which doesn't throw on failure.
- */
- string opIndex(in char[] name) @safe
- {
- import std.exception : enforce;
- string value;
- enforce(getImpl(name, value), "Environment variable not found: "~name);
- return value;
- }
-
- /**
- Retrieves the value of the environment variable with the given $(D name),
- or a default value if the variable doesn't exist.
-
- Unlike $(LREF environment.opIndex), this function never throws.
- ---
- auto sh = environment.get("SHELL", "/bin/sh");
- ---
- This function is also useful in checking for the existence of an
- environment variable.
- ---
- auto myVar = environment.get("MYVAR");
- if (myVar is null)
- {
- // Environment variable doesn't exist.
- // Note that we have to use 'is' for the comparison, since
- // myVar == null is also true if the variable exists but is
- // empty.
- }
- ---
-
- Throws:
- $(REF UTFException, std,utf) if the variable contains invalid UTF-16
- characters (Windows only).
- */
- string get(in char[] name, string defaultValue = null) @safe
- {
- string value;
- auto found = getImpl(name, value);
- return found ? value : defaultValue;
- }
-
- /**
- Assigns the given $(D value) to the environment variable with the given
- $(D name).
- If $(D value) is null the variable is removed from environment.
-
- If the variable does not exist, it will be created. If it already exists,
- it will be overwritten.
- ---
- environment["foo"] = "bar";
- ---
-
- Throws:
- $(OBJECTREF Exception) if the environment variable could not be added
- (e.g. if the name is invalid).
-
- Note:
- On some platforms, modifying environment variables may not be allowed in
- multi-threaded programs. See e.g.
- $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).
- */
- inout(char)[] opIndexAssign(inout char[] value, in char[] name) @trusted
- {
- version (Posix)
- {
- import std.exception : enforce, errnoEnforce;
- if (value is null)
- {
- remove(name);
- return value;
- }
- if (core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), 1) != -1)
- {
- return value;
- }
- // The default errno error message is very uninformative
- // in the most common case, so we handle it manually.
- enforce(errno != EINVAL,
- "Invalid environment variable name: '"~name~"'");
- errnoEnforce(false,
- "Failed to add environment variable");
- assert(0);
- }
- else version (Windows)
- {
- import std.exception : enforce;
- enforce(
- SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()),
- sysErrorString(GetLastError())
- );
- return value;
- }
- else static assert(0);
- }
-
- /**
- Removes the environment variable with the given $(D name).
-
- If the variable isn't in the environment, this function returns
- successfully without doing anything.
-
- Note:
- On some platforms, modifying environment variables may not be allowed in
- multi-threaded programs. See e.g.
- $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).
- */
- void remove(in char[] name) @trusted nothrow @nogc // TODO: @safe
- {
- version (Windows) SetEnvironmentVariableW(name.tempCStringW(), null);
- else version (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString());
- else static assert(0);
- }
-
- /**
- Identify whether a variable is defined in the environment.
-
- Because it doesn't return the value, this function is cheaper than `get`.
- However, if you do need the value as well, you should just check the
- return of `get` for `null` instead of using this function first.
-
- Example:
- -------------
- // good usage
- if ("MY_ENV_FLAG" in environment)
- doSomething();
-
- // bad usage
- if ("MY_ENV_VAR" in environment)
- doSomething(environment["MY_ENV_VAR"]);
-
- // do this instead
- if (auto var = environment.get("MY_ENV_VAR"))
- doSomething(var);
- -------------
- */
- bool opBinaryRight(string op : "in")(in char[] name) @trusted
- {
- version (Posix)
- return core.sys.posix.stdlib.getenv(name.tempCString()) !is null;
- else version (Windows)
- {
- SetLastError(NO_ERROR);
- if (GetEnvironmentVariableW(name.tempCStringW, null, 0) > 0)
- return true;
- immutable err = GetLastError();
- if (err == ERROR_ENVVAR_NOT_FOUND)
- return false;
- // some other windows error. Might actually be NO_ERROR, because
- // GetEnvironmentVariable doesn't specify whether it sets on all
- // failures
- throw new WindowsException(err);
- }
- else static assert(0);
- }
-
- /**
- Copies all environment variables into an associative array.
-
- Windows_specific:
- While Windows environment variable names are case insensitive, D's
- built-in associative arrays are not. This function will store all
- variable names in uppercase (e.g. $(D PATH)).
-
- Throws:
- $(OBJECTREF Exception) if the environment variables could not
- be retrieved (Windows only).
- */
- string[string] toAA() @trusted
- {
- import std.conv : to;
- string[string] aa;
- version (Posix)
- {
- auto environ = getEnvironPtr;
- for (int i=0; environ[i] != null; ++i)
- {
- import std.string : indexOf;
-
- immutable varDef = to!string(environ[i]);
- immutable eq = indexOf(varDef, '=');
- assert(eq >= 0);
-
- immutable name = varDef[0 .. eq];
- immutable value = varDef[eq+1 .. $];
-
- // In POSIX, environment variables may be defined more
- // than once. This is a security issue, which we avoid
- // by checking whether the key already exists in the array.
- // For more info:
- // http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html
- if (name !in aa) aa[name] = value;
- }
- }
- else version (Windows)
- {
- import std.exception : enforce;
- import std.uni : toUpper;
- auto envBlock = GetEnvironmentStringsW();
- enforce(envBlock, "Failed to retrieve environment variables.");
- scope(exit) FreeEnvironmentStringsW(envBlock);
-
- for (int i=0; envBlock[i] != '\0'; ++i)
- {
- auto start = i;
- while (envBlock[i] != '=') ++i;
- immutable name = toUTF8(toUpper(envBlock[start .. i]));
-
- start = i+1;
- while (envBlock[i] != '\0') ++i;
-
- // Ignore variables with empty names. These are used internally
- // by Windows to keep track of each drive's individual current
- // directory.
- if (!name.length)
- continue;
-
- // Just like in POSIX systems, environment variables may be
- // defined more than once in an environment block on Windows,
- // and it is just as much of a security issue there. Moreso,
- // in fact, due to the case insensensitivity of variable names,
- // which is not handled correctly by all programs.
- auto val = toUTF8(envBlock[start .. i]);
- if (name !in aa) aa[name] = val is null ? "" : val;
- }
- }
- else static assert(0);
- return aa;
- }
-
-private:
- // Retrieves the environment variable, returns false on failure.
- bool getImpl(in char[] name, out string value) @trusted
- {
- version (Windows)
- {
- // first we ask windows how long the environment variable is,
- // then we try to read it in to a buffer of that length. Lots
- // of error conditions because the windows API is nasty.
-
- import std.conv : to;
- const namezTmp = name.tempCStringW();
- WCHAR[] buf;
-
- // clear error because GetEnvironmentVariable only says it sets it
- // if the environment variable is missing, not on other errors.
- SetLastError(NO_ERROR);
- // len includes terminating null
- immutable len = GetEnvironmentVariableW(namezTmp, null, 0);
- if (len == 0)
- {
- immutable err = GetLastError();
- if (err == ERROR_ENVVAR_NOT_FOUND)
- return false;
- // some other windows error. Might actually be NO_ERROR, because
- // GetEnvironmentVariable doesn't specify whether it sets on all
- // failures
- throw new WindowsException(err);
- }
- if (len == 1)
- {
- value = "";
- return true;
- }
- buf.length = len;
-
- while (true)
- {
- // lenRead is either the number of bytes read w/o null - if buf was long enough - or
- // the number of bytes necessary *including* null if buf wasn't long enough
- immutable lenRead = GetEnvironmentVariableW(namezTmp, buf.ptr, to!DWORD(buf.length));
- if (lenRead == 0)
- {
- immutable err = GetLastError();
- if (err == NO_ERROR) // sucessfully read a 0-length variable
- {
- value = "";
- return true;
- }
- if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist
- return false;
- // some other windows error
- throw new WindowsException(err);
- }
- assert(lenRead != buf.length, "impossible according to msft docs");
- if (lenRead < buf.length) // the buffer was long enough
- {
- value = toUTF8(buf[0 .. lenRead]);
- return true;
- }
- // resize and go around again, because the environment variable grew
- buf.length = lenRead;
- }
- }
- else version (Posix)
- {
- const vz = core.sys.posix.stdlib.getenv(name.tempCString());
- if (vz == null) return false;
- auto v = vz[0 .. strlen(vz)];
-
- // Cache the last call's result.
- static string lastResult;
- if (v.empty)
- {
- // Return non-null array for blank result to distinguish from
- // not-present result.
- lastResult = "";
- }
- else if (v != lastResult)
- {
- lastResult = v.idup;
- }
- value = lastResult;
- return true;
- }
- else static assert(0);
- }
-}
-
-@safe unittest
-{
- import std.exception : assertThrown;
- // New variable
- environment["std_process"] = "foo";
- assert(environment["std_process"] == "foo");
- assert("std_process" in environment);
-
- // Set variable again (also tests length 1 case)
- environment["std_process"] = "b";
- assert(environment["std_process"] == "b");
- assert("std_process" in environment);
-
- // Remove variable
- environment.remove("std_process");
- assert("std_process" !in environment);
-
- // Remove again, should succeed
- environment.remove("std_process");
- assert("std_process" !in environment);
-
- // Throw on not found.
- assertThrown(environment["std_process"]);
-
- // get() without default value
- assert(environment.get("std_process") is null);
-
- // get() with default value
- assert(environment.get("std_process", "baz") == "baz");
-
- // get() on an empty (but present) value
- environment["std_process"] = "";
- auto res = environment.get("std_process");
- assert(res !is null);
- assert(res == "");
- assert("std_process" in environment);
-
- // Important to do the following round-trip after the previous test
- // because it tests toAA with an empty var
-
- // Convert to associative array
- auto aa = environment.toAA();
- assert(aa.length > 0);
- foreach (n, v; aa)
- {
- // Wine has some bugs related to environment variables:
- // - Wine allows the existence of an env. variable with the name
- // "\0", but GetEnvironmentVariable refuses to retrieve it.
- // As of 2.067 we filter these out anyway (see comment in toAA).
-
- assert(v == environment[n]);
- }
-
- // ... and back again.
- foreach (n, v; aa)
- environment[n] = v;
-
- // Complete the roundtrip
- auto aa2 = environment.toAA();
- import std.conv : text;
- assert(aa == aa2, text(aa, " != ", aa2));
- assert("std_process" in environment);
-
- // Setting null must have the same effect as remove
- environment["std_process"] = null;
- assert("std_process" !in environment);
-}
-
-
-
-
// =============================================================================
// Everything below this line was part of the old std.process, and most of
// it will be deprecated and removed.
@@ -3744,7 +4073,7 @@ private:
/*
-Copyright: Copyright Digital Mars 2007 - 2009.
+Copyright: Copyright The D Language Foundation 2007 - 2009.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP digitalmars.com, Walter Bright),
$(HTTP erdani.org, Andrei Alexandrescu),
@@ -3752,7 +4081,7 @@ Authors: $(HTTP digitalmars.com, Walter Bright),
Source: $(PHOBOSSRC std/_process.d)
*/
/*
- Copyright Digital Mars 2007 - 2009.
+ Copyright The D Language Foundation 2007 - 2009.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
@@ -3772,11 +4101,6 @@ version (Posix)
{
import core.sys.posix.stdlib;
}
-version (unittest)
-{
- import std.conv, std.file, std.random;
-}
-
private void toAStringz(in string[] a, const(char)**az)
{
@@ -3807,7 +4131,7 @@ private void toAStringz(in string[] a, const(char)**az)
// Incorporating idea (for spawnvp() on Posix) from Dave Fladebo
enum { _P_WAIT, _P_NOWAIT, _P_OVERLAY }
-version (Windows) extern(C) int spawnvp(int, in char *, in char **);
+version (Windows) extern(C) int spawnvp(int, scope const(char) *, scope const(char*)*);
alias P_WAIT = _P_WAIT;
alias P_NOWAIT = _P_NOWAIT;
@@ -3816,14 +4140,14 @@ alias P_NOWAIT = _P_NOWAIT;
version (StdDdoc)
{
/**
- Replaces the current process by executing a command, $(D pathname), with
- the arguments in $(D argv).
+ Replaces the current process by executing a command, `pathname`, with
+ the arguments in `argv`.
- $(BLUE This functions is Posix-Only.)
+ $(BLUE This function is Posix-Only.)
- Typically, the first element of $(D argv) is
+ Typically, the first element of `argv` is
the command being executed, i.e. $(D argv[0] == pathname). The 'p'
- versions of $(D exec) search the PATH environment variable for $(D
+ versions of `exec` search the PATH environment variable for $(D
pathname). The 'e' versions additionally take the new process'
environment variables as an array of strings of the form key=value.
@@ -3835,7 +4159,7 @@ version (StdDdoc)
These functions are only supported on POSIX platforms, as the Windows
operating systems do not provide the ability to overwrite the current
process image with another. In single-threaded programs it is possible
- to approximate the effect of $(D execv*) by using $(LREF spawnProcess)
+ to approximate the effect of `execv*` by using $(LREF spawnProcess)
and terminating the current process once the child process has returned.
For example:
---
@@ -3847,20 +4171,20 @@ version (StdDdoc)
}
else version (Windows)
{
- import core.stdc.stdlib : _exit;
- _exit(wait(spawnProcess(commandLine)));
+ import core.stdc.stdlib : _Exit;
+ _Exit(wait(spawnProcess(commandLine)));
}
---
- This is, however, NOT equivalent to POSIX' $(D execv*). For one thing, the
+ This is, however, NOT equivalent to POSIX' `execv*`. For one thing, the
executed program is started as a separate process, with all this entails.
Secondly, in a multithreaded program, other threads will continue to do
work while the current thread is waiting for the child process to complete.
A better option may sometimes be to terminate the current program immediately
after spawning the child process. This is the behaviour exhibited by the
- $(LINK2 http://msdn.microsoft.com/en-us/library/431x4c1w.aspx,$(D __exec))
+ $(LINK2 http://msdn.microsoft.com/en-us/library/431x4c1w.aspx,`__exec`)
functions in Microsoft's C runtime library, and it is how D's now-deprecated
- Windows $(D execv*) functions work. Example:
+ Windows `execv*` functions work. Example:
---
auto commandLine = [ "program", "arg1", "arg2" ];
version (Posix)
@@ -3907,15 +4231,18 @@ else version (Posix)
// Move these C declarations to druntime if we decide to keep the D wrappers
extern(C)
{
- int execv(in char *, in char **);
- int execve(in char *, in char **, in char **);
- int execvp(in char *, in char **);
- version (Windows) int execvpe(in char *, in char **, in char **);
+ int execv(scope const(char) *, scope const(char *)*);
+ int execve(scope const(char)*, scope const(char*)*, scope const(char*)*);
+ int execvp(scope const(char)*, scope const(char*)*);
+ version (Windows) int execvpe(scope const(char)*, scope const(char*)*, scope const(char*)*);
}
private int execv_(in string pathname, in string[] argv)
{
+ import core.exception : OutOfMemoryError;
+ import std.exception : enforce;
auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
+ enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process.");
scope(exit) core.stdc.stdlib.free(argv_);
toAStringz(argv, argv_);
@@ -3925,9 +4252,13 @@ private int execv_(in string pathname, in string[] argv)
private int execve_(in string pathname, in string[] argv, in string[] envp)
{
+ import core.exception : OutOfMemoryError;
+ import std.exception : enforce;
auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
+ enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process.");
scope(exit) core.stdc.stdlib.free(argv_);
auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length));
+ enforce!OutOfMemoryError(envp_ !is null, "Out of memory in std.process.");
scope(exit) core.stdc.stdlib.free(envp_);
toAStringz(argv, argv_);
@@ -3938,7 +4269,10 @@ private int execve_(in string pathname, in string[] argv, in string[] envp)
private int execvp_(in string pathname, in string[] argv)
{
+ import core.exception : OutOfMemoryError;
+ import std.exception : enforce;
auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
+ enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process.");
scope(exit) core.stdc.stdlib.free(argv_);
toAStringz(argv, argv_);
@@ -3985,9 +4319,13 @@ version (Posix)
}
else version (Windows)
{
+ import core.exception : OutOfMemoryError;
+ import std.exception : enforce;
auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
+ enforce!OutOfMemoryError(argv_ !is null, "Out of memory in std.process.");
scope(exit) core.stdc.stdlib.free(argv_);
auto envp_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + envp.length));
+ enforce!OutOfMemoryError(envp_ !is null, "Out of memory in std.process.");
scope(exit) core.stdc.stdlib.free(envp_);
toAStringz(argv, argv_);
@@ -4006,88 +4344,98 @@ version (StdDdoc)
/****************************************
* Start up the browser and set it to viewing the page at url.
*/
- void browse(const(char)[] url);
+ void browse(scope const(char)[] url);
}
else
version (Windows)
{
- import core.sys.windows.windows;
+ import core.sys.windows.shellapi, core.sys.windows.winuser;
pragma(lib,"shell32.lib");
- void browse(const(char)[] url)
+ void browse(scope const(char)[] url) nothrow @nogc @trusted
{
ShellExecuteW(null, "open", url.tempCStringW(), null, null, SW_SHOWNORMAL);
}
}
-else version (OSX)
+else version (Posix)
{
import core.stdc.stdio;
import core.stdc.string;
import core.sys.posix.unistd;
- void browse(const(char)[] url) nothrow @nogc
+ void browse(scope const(char)[] url) nothrow @nogc @safe
{
- const(char)*[5] args;
+ const(char)*[3] args;
- auto curl = url.tempCString();
- const(char)* browser = core.stdc.stdlib.getenv("BROWSER");
+ // Trusted because it's called with a zero-terminated literal
+ const(char)* browser = (() @trusted => core.stdc.stdlib.getenv("BROWSER"))();
if (browser)
- { browser = strdup(browser);
+ {
+ // String already zero-terminated
+ browser = (() @trusted => strdup(browser))();
args[0] = browser;
- args[1] = curl;
- args[2] = null;
}
else
{
- args[0] = "open".ptr;
- args[1] = curl;
- args[2] = null;
+ version (OSX)
+ {
+ args[0] = "open";
+ }
+ else
+ {
+ //args[0] = "x-www-browser"; // doesn't work on some systems
+ args[0] = "xdg-open";
+ }
}
+ const buffer = url.tempCString(); // Retain buffer until end of scope
+ args[1] = buffer;
+ args[2] = null;
+
auto childpid = core.sys.posix.unistd.fork();
if (childpid == 0)
{
- core.sys.posix.unistd.execvp(args[0], cast(char**) args.ptr);
- perror(args[0]); // failed to execute
+ // Trusted because args and all entries are always zero-terminated
+ (() @trusted =>
+ core.sys.posix.unistd.execvp(args[0], &args[0]) ||
+ perror(args[0]) // failed to execute
+ )();
return;
}
if (browser)
- free(cast(void*) browser);
- }
-}
-else version (Posix)
-{
- import core.stdc.stdio;
- import core.stdc.string;
- import core.sys.posix.unistd;
-
- void browse(const(char)[] url) nothrow @nogc
- {
- const(char)*[3] args;
-
- const(char)* browser = core.stdc.stdlib.getenv("BROWSER");
- if (browser)
- { browser = strdup(browser);
- args[0] = browser;
- }
- else
- //args[0] = "x-www-browser".ptr; // doesn't work on some systems
- args[0] = "xdg-open".ptr;
+ // Trusted because it's allocated via strdup above
+ (() @trusted => free(cast(void*) browser))();
- args[1] = url.tempCString();
- args[2] = null;
-
- auto childpid = core.sys.posix.unistd.fork();
- if (childpid == 0)
+ version (StdUnittest)
{
- core.sys.posix.unistd.execvp(args[0], cast(char**) args.ptr);
- perror(args[0]); // failed to execute
- return;
+ // Verify that the test script actually suceeds
+ int status;
+ const check = (() @trusted => waitpid(childpid, &status, 0))();
+ assert(check != -1);
+ assert(status == 0);
}
- if (browser)
- free(cast(void*) browser);
}
}
else
static assert(0, "os not supported");
+
+// Verify attributes are consistent between all implementations
+@safe @nogc nothrow unittest
+{
+ if (false)
+ browse("");
+}
+
+version (Windows) { /* Doesn't use BROWSER */ }
+else
+@system unittest
+{
+ import std.conv : text;
+ import std.range : repeat;
+ immutable string url = text("http://", repeat('x', 249));
+
+ TestScript prog = `if [ "$1" != "` ~ url ~ `" ]; then exit 1; fi`;
+ environment["BROWSER"] = prog.path;
+ browse(url);
+}
diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d
index f4c64d4a36d..441bc5d5533 100644
--- a/libphobos/src/std/random.d
+++ b/libphobos/src/std/random.d
@@ -3,7 +3,59 @@
/**
Facilities for random number generation.
-$(RED Disclaimer:) The _random number generators and API provided in this
+$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Uniform sampling) $(TD
+ $(LREF uniform)
+ $(LREF uniform01)
+ $(LREF uniformDistribution)
+))
+$(TR $(TD Element sampling) $(TD
+ $(LREF choice)
+ $(LREF dice)
+))
+$(TR $(TD Range sampling) $(TD
+ $(LREF randomCover)
+ $(LREF randomSample)
+))
+$(TR $(TD Default Random Engines) $(TD
+ $(LREF rndGen)
+ $(LREF Random)
+ $(LREF unpredictableSeed)
+))
+$(TR $(TD Linear Congruential Engines) $(TD
+ $(LREF MinstdRand)
+ $(LREF MinstdRand0)
+ $(LREF LinearCongruentialEngine)
+))
+$(TR $(TD Mersenne Twister Engines) $(TD
+ $(LREF Mt19937)
+ $(LREF Mt19937_64)
+ $(LREF MersenneTwisterEngine)
+))
+$(TR $(TD Xorshift Engines) $(TD
+ $(LREF Xorshift)
+ $(LREF XorshiftEngine)
+ $(LREF Xorshift32)
+ $(LREF Xorshift64)
+ $(LREF Xorshift96)
+ $(LREF Xorshift128)
+ $(LREF Xorshift160)
+ $(LREF Xorshift192)
+))
+$(TR $(TD Shuffle) $(TD
+ $(LREF partialShuffle)
+ $(LREF randomShuffle)
+))
+$(TR $(TD Traits) $(TD
+ $(LREF isSeedable)
+ $(LREF isUniformRNG)
+))
+))
+
+$(RED Disclaimer:) The random number generators and API provided in this
module are not designed to be cryptographically secure, and are therefore
unsuitable for cryptographic or security-related purposes such as generating
authentication tokens or network sequence numbers. For such needs, please use a
@@ -18,7 +70,7 @@ is the $(D_PARAM Mt19937) generator, which derives its name from
with a period of 2 to the power of
19937". In memory-constrained situations,
$(LINK2 https://en.wikipedia.org/wiki/Linear_congruential_generator,
-linear congruential generators) such as $(D MinstdRand0) and $(D MinstdRand) might be
+linear congruential generators) such as `MinstdRand0` and `MinstdRand` might be
useful. The standard library provides an alias $(D_PARAM Random) for
whichever generator it considers the most fit for the target
environment.
@@ -28,7 +80,7 @@ distributions, which skew a generator's output statistical
distribution in various ways. So far the uniform distribution for
integers and real numbers have been implemented.
-Source: $(PHOBOSSRC std/_random.d)
+Source: $(PHOBOSSRC std/random.d)
Macros:
@@ -37,7 +89,7 @@ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.org, Andrei Alexandrescu)
Masahiro Nakagawa (Xorshift random generator)
$(HTTP braingam.es, Joseph Rushton Wakeling) (Algorithm D for random sampling)
- Ilya Yaroshenko (Mersenne Twister implementation, adapted from $(HTTPS github.com/libmir/mir-_random, mir-_random))
+ Ilya Yaroshenko (Mersenne Twister implementation, adapted from $(HTTPS github.com/libmir/mir-random, mir-random))
Credits: The entire random number library architecture is derived from the
excellent $(HTTP open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf, C++0X)
random number facility proposed by Jens Maurer and contributed to by
@@ -55,9 +107,24 @@ module std.random;
import std.range.primitives;
import std.traits;
+version (OSX)
+ version = Darwin;
+else version (iOS)
+ version = Darwin;
+else version (TVOS)
+ version = Darwin;
+else version (WatchOS)
+ version = Darwin;
+
+version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
+version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
+
///
@safe unittest
{
+ import std.algorithm.comparison : among, equal;
+ import std.range : iota;
+
// seed a random generator with a constant
auto rnd = Random(42);
@@ -70,16 +137,51 @@ import std.traits;
auto r = uniform(0.0L, 100.0L, rnd);
assert(r >= 0 && r < 100);
+ // Sample from a custom type
+ enum Fruit { apple, mango, pear }
+ auto f = rnd.uniform!Fruit;
+ with(Fruit)
+ assert(f.among(apple, mango, pear));
+
// Generate a 32-bit random number
auto u = uniform!uint(rnd);
static assert(is(typeof(u) == uint));
+
+ // Generate a random number in the range in the range [0, 1)
+ auto u2 = uniform01(rnd);
+ assert(u2 >= 0 && u2 < 1);
+
+ // Select an element randomly
+ auto el = 10.iota.choice(rnd);
+ assert(0 <= el && el < 10);
+
+ // Throw a dice with custom proportions
+ // 0: 20%, 1: 10%, 2: 60%
+ auto val = rnd.dice(0.2, 0.1, 0.6);
+ assert(0 <= val && val <= 2);
+
+ auto rnd2 = MinstdRand0(42);
+
+ // Select a random subsample from a range
+ assert(10.iota.randomSample(3, rnd2).equal([7, 8, 9]));
+
+ // Cover all elements in an array in random order
+ version (X86_64) // https://issues.dlang.org/show_bug.cgi?id=15147
+ assert(10.iota.randomCover(rnd2).equal([7, 4, 2, 0, 1, 6, 8, 3, 9, 5]));
+
+ // Shuffle an array
+ version (X86_64) // https://issues.dlang.org/show_bug.cgi?id=15147
+ assert([0, 1, 2, 4, 5].randomShuffle(rnd2).equal([2, 0, 4, 5, 1]));
}
-version (unittest)
+version (StdUnittest)
{
static import std.meta;
+ package alias Xorshift64_64 = XorshiftEngine!(ulong, 64, -12, 25, -27);
+ package alias Xorshift128_64 = XorshiftEngine!(ulong, 128, 23, -18, -5);
package alias PseudoRngTypes = std.meta.AliasSeq!(MinstdRand0, MinstdRand, Mt19937, Xorshift32, Xorshift64,
- Xorshift96, Xorshift128, Xorshift160, Xorshift192);
+ Xorshift96, Xorshift128, Xorshift160, Xorshift192,
+ Xorshift64_64, Xorshift128_64);
}
// Segments of the code in this file Copyright (c) 1997 by Rick Booth
@@ -143,12 +245,8 @@ version (unittest)
*/
template isUniformRNG(Rng, ElementType)
{
- enum bool isUniformRNG = isInputRange!Rng &&
- is(typeof(Rng.front) == ElementType) &&
- is(typeof(
- {
- static assert(Rng.isUniformRandom); //tag
- }));
+ enum bool isUniformRNG = .isUniformRNG!Rng &&
+ is(std.range.primitives.ElementType!Rng == ElementType);
}
/**
@@ -163,6 +261,43 @@ template isUniformRNG(Rng)
}));
}
+///
+@safe unittest
+{
+ struct NoRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+ }
+ static assert(!isUniformRNG!(NoRng));
+
+ struct validRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+
+ enum isUniformRandom = true;
+ }
+ static assert(isUniformRNG!(validRng, uint));
+ static assert(isUniformRNG!(validRng));
+}
+
+@safe unittest
+{
+ // two-argument predicate should not require @property on `front`
+ // https://issues.dlang.org/show_bug.cgi?id=19837
+ struct Rng
+ {
+ float front() {return 0;}
+ void popFront() {}
+ enum empty = false;
+ enum isUniformRandom = true;
+ }
+ static assert(isUniformRNG!(Rng, float));
+}
+
/**
* Test if Rng is seedable. The overload
* taking a SeedType also makes sure that the Rng can be seeded with SeedType.
@@ -178,7 +313,8 @@ template isSeedable(Rng, SeedType)
is(typeof(
{
Rng r = void; // can define a Rng object
- r.seed(SeedType.init); // can seed a Rng
+ SeedType s = void;
+ r.seed(s); // can seed a Rng
}));
}
@@ -189,11 +325,40 @@ template isSeedable(Rng)
is(typeof(
{
Rng r = void; // can define a Rng object
- r.seed(typeof(r.front).init); // can seed a Rng
+ alias SeedType = typeof(r.front);
+ SeedType s = void;
+ r.seed(s); // can seed a Rng
}));
}
-@safe pure nothrow unittest
+///
+@safe unittest
+{
+ struct validRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+
+ enum isUniformRandom = true;
+ }
+ static assert(!isSeedable!(validRng, uint));
+ static assert(!isSeedable!(validRng));
+
+ struct seedRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+ void seed(uint val){}
+ enum isUniformRandom = true;
+ }
+ static assert(isSeedable!(seedRng, uint));
+ static assert(!isSeedable!(seedRng, ulong));
+ static assert(isSeedable!(seedRng));
+}
+
+@safe @nogc pure nothrow unittest
{
struct NoRng
{
@@ -255,11 +420,12 @@ template isSeedable(Rng)
static assert(isUniformRNG!(seedRng, uint));
static assert(isUniformRNG!(seedRng));
static assert(isSeedable!(seedRng, uint));
+ static assert(!isSeedable!(seedRng, ulong));
static assert(isSeedable!(seedRng));
}
/**
-Linear Congruential generator.
+Linear Congruential generator. When m = 0, no modulus is used.
*/
struct LinearCongruentialEngine(UIntType, UIntType a, UIntType c, UIntType m)
if (isUnsigned!UIntType)
@@ -268,7 +434,7 @@ if (isUnsigned!UIntType)
enum bool isUniformRandom = true;
/// Does this generator have a fixed range? ($(D_PARAM true)).
enum bool hasFixedRange = true;
- /// Lowest generated value ($(D 1) if $(D c == 0), $(D 0) otherwise).
+ /// Lowest generated value (`1` if $(D c == 0), `0` otherwise).
enum UIntType min = ( c == 0 ? 1 : 0 );
/// Highest generated value ($(D modulus - 1)).
enum UIntType max = m - 1;
@@ -360,9 +526,9 @@ The parameters of this distribution. The random number is $(D_PARAM x
/**
Constructs a $(D_PARAM LinearCongruentialEngine) generator seeded with
-$(D x0).
+`x0`.
*/
- this(UIntType x0) @safe pure
+ this(UIntType x0) @safe pure nothrow @nogc
{
seed(x0);
}
@@ -370,15 +536,15 @@ $(D x0).
/**
(Re)seeds the generator.
*/
- void seed(UIntType x0 = 1) @safe pure
+ void seed(UIntType x0 = 1) @safe pure nothrow @nogc
{
+ _x = modulus ? (x0 % modulus) : x0;
static if (c == 0)
{
- import std.exception : enforce;
- enforce(x0, "Invalid (zero) seed for "
- ~ LinearCongruentialEngine.stringof);
+ //Necessary to prevent generator from outputting an endless series of zeroes.
+ if (_x == 0)
+ _x = max;
}
- _x = modulus ? (x0 % modulus) : x0;
popFront();
}
@@ -427,33 +593,76 @@ $(D x0).
}
///
- @property typeof(this) save() @safe pure nothrow @nogc
+ @property typeof(this) save() const @safe pure nothrow @nogc
{
return this;
}
/**
-Always $(D false) (random generators are infinite ranges).
+Always `false` (random generators are infinite ranges).
*/
enum bool empty = false;
-/**
- Compares against $(D_PARAM rhs) for equality.
- */
- bool opEquals(ref const LinearCongruentialEngine rhs) const @safe pure nothrow @nogc
+ // https://issues.dlang.org/show_bug.cgi?id=21610
+ static if (m)
+ {
+ private UIntType _x = (a + c) % m;
+ }
+ else
{
- return _x == rhs._x;
+ private UIntType _x = a + c;
}
+}
+
+/// Declare your own linear congruential engine
+@safe unittest
+{
+ alias CPP11LCG = LinearCongruentialEngine!(uint, 48271, 0, 2_147_483_647);
+
+ // seed with a constant
+ auto rnd = CPP11LCG(42);
+ auto n = rnd.front; // same for each run
+ assert(n == 2027382);
+}
+
+/// Declare your own linear congruential engine
+@safe unittest
+{
+ // glibc's LCG
+ alias GLibcLCG = LinearCongruentialEngine!(uint, 1103515245, 12345, 2_147_483_648);
+
+ // Seed with an unpredictable value
+ auto rnd = GLibcLCG(unpredictableSeed);
+ auto n = rnd.front; // different across runs
+}
+
+/// Declare your own linear congruential engine
+@safe unittest
+{
+ // Visual C++'s LCG
+ alias MSVCLCG = LinearCongruentialEngine!(uint, 214013, 2531011, 0);
+
+ // seed with a constant
+ auto rnd = MSVCLCG(1);
+ auto n = rnd.front; // same for each run
+ assert(n == 2745024);
+}
+
+// Ensure that unseeded LCGs produce correct values
+@safe unittest
+{
+ alias LGE = LinearCongruentialEngine!(uint, 10000, 19682, 19683);
- private UIntType _x = m ? (a + c) % m : (a + c);
+ LGE rnd;
+ assert(rnd.front == 9999);
}
/**
Define $(D_PARAM LinearCongruentialEngine) generators with well-chosen
-parameters. $(D MinstdRand0) implements Park and Miller's "minimal
+parameters. `MinstdRand0` implements Park and Miller's "minimal
standard" $(HTTP
wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator,
-generator) that uses 16807 for the multiplier. $(D MinstdRand)
+generator) that uses 16807 for the multiplier. `MinstdRand`
implements a variant that has slightly better spectral behavior by
using the multiplier 48271. Both generators are rather simplistic.
*/
@@ -462,17 +671,20 @@ alias MinstdRand0 = LinearCongruentialEngine!(uint, 16_807, 0, 2_147_483_647);
alias MinstdRand = LinearCongruentialEngine!(uint, 48_271, 0, 2_147_483_647);
///
-@safe unittest
+@safe @nogc unittest
{
// seed with a constant
auto rnd0 = MinstdRand0(1);
- auto n = rnd0.front; // same for each run
+ auto n = rnd0.front;
+ // same for each run
+ assert(n == 16807);
+
// Seed with an unpredictable value
rnd0.seed(unpredictableSeed);
n = rnd0.front; // different across runs
}
-@safe unittest
+@safe @nogc unittest
{
import std.range;
static assert(isForwardRange!MinstdRand);
@@ -487,7 +699,7 @@ alias MinstdRand = LinearCongruentialEngine!(uint, 48_271, 0, 2_147_483_647);
// The correct numbers are taken from The Database of Integer Sequences
// http://www.research.att.com/~njas/sequences/eisBTfry00128.txt
- auto checking0 = [
+ enum ulong[20] checking0 = [
16807UL,282475249,1622650073,984943658,1144108930,470211272,
101027544,1457850878,1458777923,2007237709,823564440,1115438165,
1784484492,74243042,114807987,1137522503,1441282327,16531729,
@@ -508,7 +720,7 @@ alias MinstdRand = LinearCongruentialEngine!(uint, 48_271, 0, 2_147_483_647);
assert(rnd0.front == 1043618065);
// Test MinstdRand
- auto checking = [48271UL,182605794,1291394886,1914720637,2078669041,
+ enum ulong[6] checking = [48271UL,182605794,1291394886,1914720637,2078669041,
407355683];
//auto rnd = MinstdRand(1);
MinstdRand rnd;
@@ -526,15 +738,26 @@ alias MinstdRand = LinearCongruentialEngine!(uint, 48_271, 0, 2_147_483_647);
assert(rnd.front == 399268537);
// Check .save works
- foreach (Type; std.meta.AliasSeq!(MinstdRand0, MinstdRand))
- {
- auto rnd1 = Type(unpredictableSeed);
- auto rnd2 = rnd1.save;
+ static foreach (Type; std.meta.AliasSeq!(MinstdRand0, MinstdRand))
+ {{
+ auto rnd1 = Type(123_456_789);
+ rnd1.popFront();
+ // https://issues.dlang.org/show_bug.cgi?id=15853
+ auto rnd2 = ((const ref Type a) => a.save())(rnd1);
assert(rnd1 == rnd2);
// Enable next test when RNGs are reference types
version (none) { assert(rnd1 !is rnd2); }
- assert(rnd1.take(100).array() == rnd2.take(100).array());
- }
+ for (auto i = 0; i < 3; i++, rnd1.popFront, rnd2.popFront)
+ assert(rnd1.front() == rnd2.front());
+ }}
+}
+
+@safe @nogc unittest
+{
+ auto rnd0 = MinstdRand0(MinstdRand0.modulus);
+ auto n = rnd0.front;
+ rnd0.popFront();
+ assert(n != rnd0.front);
}
/**
@@ -551,7 +774,7 @@ if (isUnsigned!UIntType)
static assert(0 <= r && 0 <= u && 0 <= s && 0 <= t && 0 <= l);
static assert(r <= w && u <= w && s <= w && t <= w && l <= w);
static assert(0 <= a && 0 <= b && 0 <= c);
- static assert(n <= sizediff_t.max);
+ static assert(n <= ptrdiff_t.max);
///Mark this as a Rng
enum bool isUniformRandom = true;
@@ -675,7 +898,7 @@ Parameters for the generator.
Implementation of the seeding mechanism, which
can be used with an arbitrary `State` instance
*/
- private static void seedImpl(UIntType value, ref State mtState)
+ private static void seedImpl(UIntType value, ref State mtState) @nogc
{
mtState.data[$ - 1] = value;
static if (this.max != UIntType.max)
@@ -705,10 +928,10 @@ Parameters for the generator.
Seeds a MersenneTwisterEngine object using an InputRange.
Throws:
- $(D Exception) if the InputRange didn't provide enough elements to seed the generator.
+ `Exception` if the InputRange didn't provide enough elements to seed the generator.
The number of elements required is the 'n' template parameter of the MersenneTwisterEngine struct.
*/
- void seed(T)(T range) if (isInputRange!T && is(Unqual!(ElementType!T) == UIntType))
+ void seed(T)(T range) if (isInputRange!T && is(immutable ElementType!T == immutable UIntType))
{
this.seedImpl(range, this.state);
}
@@ -718,12 +941,12 @@ Parameters for the generator.
which can be used with an arbitrary `State` instance
*/
private static void seedImpl(T)(T range, ref State mtState)
- if (isInputRange!T && is(Unqual!(ElementType!T) == UIntType))
+ if (isInputRange!T && is(immutable ElementType!T == immutable UIntType))
{
size_t j;
for (j = 0; j < n && !range.empty; ++j, range.popFront())
{
- sizediff_t idx = n - j - 1;
+ ptrdiff_t idx = n - j - 1;
mtState.data[idx] = range.front;
}
@@ -735,7 +958,7 @@ Parameters for the generator.
UnsignedStringBuf buf = void;
string s = "MersenneTwisterEngine.seed: Input range didn't provide enough elements: Need ";
- s ~= unsignedToTempString(n, buf, 10) ~ " elements.";
+ s ~= unsignedToTempString(n, buf) ~ " elements.";
throw new Exception(s);
}
@@ -758,7 +981,7 @@ Parameters for the generator.
Internal implementation of `popFront()`, which
can be used with an arbitrary `State` instance
*/
- private static void popFrontImpl(ref State mtState)
+ private static void popFrontImpl(ref State mtState) @nogc
{
/* This function blends two nominally independent
processes: (i) calculation of the next random
@@ -772,12 +995,12 @@ Parameters for the generator.
them separately in sequence, the variables
are kept 'hot' in CPU registers, allowing
for significantly faster performance. */
- sizediff_t index = mtState.index;
- sizediff_t next = index - 1;
+ ptrdiff_t index = mtState.index;
+ ptrdiff_t next = index - 1;
if (next < 0)
next = n - 1;
auto z = mtState.z;
- sizediff_t conj = index - m;
+ ptrdiff_t conj = index - m;
if (conj < 0)
conj = index - m + n;
@@ -819,23 +1042,36 @@ Parameters for the generator.
}
///
- @property typeof(this) save() @safe pure nothrow @nogc
+ @property typeof(this) save() @safe const pure nothrow @nogc
{
return this;
}
/**
-Always $(D false).
+Always `false`.
*/
enum bool empty = false;
}
+///
+@safe unittest
+{
+ // seed with a constant
+ Mt19937 gen;
+ auto n = gen.front; // same for each run
+ assert(n == 3499211612);
+
+ // Seed with an unpredictable value
+ gen.seed(unpredictableSeed);
+ n = gen.front; // different across runs
+}
+
/**
-A $(D MersenneTwisterEngine) instantiated with the parameters of the
+A `MersenneTwisterEngine` instantiated with the parameters of the
original engine $(HTTP math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html,
MT19937), generating uniformly-distributed 32-bit numbers with a
period of 2 to the power of 19937. Recommended for random number
-generation unless memory is severely restricted, in which case a $(D
+generation unless memory is severely restricted, in which case a $(LREF
LinearCongruentialEngine) would be the generator of choice.
*/
alias Mt19937 = MersenneTwisterEngine!(uint, 32, 624, 397, 31,
@@ -844,11 +1080,13 @@ alias Mt19937 = MersenneTwisterEngine!(uint, 32, 624, 397, 31,
0xefc60000, 18, 1_812_433_253);
///
-@safe unittest
+@safe @nogc unittest
{
// seed with a constant
Mt19937 gen;
auto n = gen.front; // same for each run
+ assert(n == 3499211612);
+
// Seed with an unpredictable value
gen.seed(unpredictableSeed);
n = gen.front; // different across runs
@@ -874,7 +1112,7 @@ alias Mt19937 = MersenneTwisterEngine!(uint, 32, 624, 397, 31,
}
/**
-A $(D MersenneTwisterEngine) instantiated with the parameters of the
+A `MersenneTwisterEngine` instantiated with the parameters of the
original engine $(HTTP en.wikipedia.org/wiki/Mersenne_Twister,
MT19937-64), generating uniformly-distributed 64-bit numbers with a
period of 2 to the power of 19937.
@@ -885,13 +1123,15 @@ alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31,
0xfff7eee000000000, 43, 6_364_136_223_846_793_005);
///
-@safe unittest
+@safe @nogc unittest
{
// Seed with a constant
auto gen = Mt19937_64(12345);
auto n = gen.front; // same for each run
+ assert(n == 6597103971274460346);
+
// Seed with an unpredictable value
- gen.seed(unpredictableSeed);
+ gen.seed(unpredictableSeed!ulong);
n = gen.front; // different across runs
}
@@ -903,13 +1143,7 @@ alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31,
static assert(isUniformRNG!(Mt19937_64, ulong));
static assert(isSeedable!Mt19937_64);
static assert(isSeedable!(Mt19937_64, ulong));
- // FIXME: this test demonstrates viably that Mt19937_64
- // is seedable with an infinite range of `ulong` values
- // but it's a poor example of how to actually seed the
- // generator, since it can't cover the full range of
- // possible seed values. Ideally we need a 64-bit
- // unpredictable seed to complement the 32-bit one!
- static assert(isSeedable!(Mt19937_64, typeof(map!((a) => (cast(ulong) unpredictableSeed))(repeat(0)))));
+ static assert(isSeedable!(Mt19937_64, typeof(map!((a) => unpredictableSeed!ulong)(repeat(0)))));
Mt19937_64 gen;
assert(gen.front == 14514284786278117030uL);
popFrontN(gen, 9999);
@@ -928,14 +1162,14 @@ alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31,
Mt19937 gen;
- assertThrown(gen.seed(map!((a) => unpredictableSeed)(repeat(0, 623))));
+ assertThrown(gen.seed(map!((a) => 123_456_789U)(repeat(0, 623))));
- gen.seed(map!((a) => unpredictableSeed)(repeat(0, 624)));
+ gen.seed(123_456_789U.repeat(624));
//infinite Range
- gen.seed(map!((a) => unpredictableSeed)(repeat(0)));
+ gen.seed(123_456_789U.repeat);
}
-@safe pure nothrow unittest
+@safe @nogc pure nothrow unittest
{
uint a, b;
{
@@ -951,125 +1185,271 @@ alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31,
assert(a != b);
}
-@safe unittest
+@safe @nogc unittest
{
- import std.range;
// Check .save works
- foreach (Type; std.meta.AliasSeq!(Mt19937, Mt19937_64))
- {
- auto gen1 = Type(unpredictableSeed);
- auto gen2 = gen1.save;
+ static foreach (Type; std.meta.AliasSeq!(Mt19937, Mt19937_64))
+ {{
+ auto gen1 = Type(123_456_789);
+ gen1.popFront();
+ // https://issues.dlang.org/show_bug.cgi?id=15853
+ auto gen2 = ((const ref Type a) => a.save())(gen1);
assert(gen1 == gen2); // Danger, Will Robinson -- no opEquals for MT
// Enable next test when RNGs are reference types
version (none) { assert(gen1 !is gen2); }
- assert(gen1.take(100).array() == gen2.take(100).array());
- }
+ for (auto i = 0; i < 100; i++, gen1.popFront, gen2.popFront)
+ assert(gen1.front() == gen2.front());
+ }}
}
-@safe pure nothrow unittest //11690
+// https://issues.dlang.org/show_bug.cgi?id=11690
+@safe @nogc pure nothrow unittest
{
alias MT(UIntType, uint w) = MersenneTwisterEngine!(UIntType, w, 624, 397, 31,
0x9908b0df, 11, 0xffffffff, 7,
0x9d2c5680, 15,
0xefc60000, 18, 1812433253);
- ulong[] expectedFirstValue = [3499211612uL, 3499211612uL,
+ static immutable ulong[] expectedFirstValue = [3499211612uL, 3499211612uL,
171143175841277uL, 1145028863177033374uL];
- ulong[] expected10kValue = [4123659995uL, 4123659995uL,
+ static immutable ulong[] expected10kValue = [4123659995uL, 4123659995uL,
51991688252792uL, 3031481165133029945uL];
- foreach (i, R; std.meta.AliasSeq!(MT!(uint, 32), MT!(ulong, 32), MT!(ulong, 48), MT!(ulong, 64)))
- {
+ static foreach (i, R; std.meta.AliasSeq!(MT!(uint, 32), MT!(ulong, 32), MT!(ulong, 48), MT!(ulong, 64)))
+ {{
auto a = R();
a.seed(a.defaultSeed); // checks that some alternative paths in `seed` are utilized
assert(a.front == expectedFirstValue[i]);
a.popFrontN(9999);
assert(a.front == expected10kValue[i]);
- }
+ }}
}
+/++
+Xorshift generator.
+Implemented according to $(HTTP www.jstatsoft.org/v08/i14/paper, Xorshift RNGs)
+(Marsaglia, 2003) when the size is small. For larger sizes the generator
+uses Sebastino Vigna's optimization of using an index to avoid needing
+to rotate the internal array.
-/**
- * Xorshift generator using 32bit algorithm.
- *
- * Implemented according to $(HTTP www.jstatsoft.org/v08/i14/paper, Xorshift RNGs).
- * Supporting bits are below, $(D bits) means second parameter of XorshiftEngine.
- *
- * $(BOOKTABLE ,
- * $(TR $(TH bits) $(TH period))
- * $(TR $(TD 32) $(TD 2^32 - 1))
- * $(TR $(TD 64) $(TD 2^64 - 1))
- * $(TR $(TD 96) $(TD 2^96 - 1))
- * $(TR $(TD 128) $(TD 2^128 - 1))
- * $(TR $(TD 160) $(TD 2^160 - 1))
- * $(TR $(TD 192) $(TD 2^192 - 2^32))
- * )
- */
-struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType c)
-if (isUnsigned!UIntType)
+Period is `2 ^^ nbits - 1` except for a legacy 192-bit uint version (see
+note below).
+
+Params:
+ UIntType = Word size of this xorshift generator and the return type
+ of `opCall`.
+ nbits = The number of bits of state of this generator. This must be
+ a positive multiple of the size in bits of UIntType. If
+ nbits is large this struct may occupy slightly more memory
+ than this so it can use a circular counter instead of
+ shifting the entire array.
+ sa = The direction and magnitude of the 1st shift. Positive
+ means left, negative means right.
+ sb = The direction and magnitude of the 2nd shift. Positive
+ means left, negative means right.
+ sc = The direction and magnitude of the 3rd shift. Positive
+ means left, negative means right.
+
+Note:
+For historical compatibility when `nbits == 192` and `UIntType` is `uint`
+a legacy hybrid PRNG is used consisting of a 160-bit xorshift combined
+with a 32-bit counter. This combined generator has period equal to the
+least common multiple of `2^^160 - 1` and `2^^32`.
+
+Previous versions of `XorshiftEngine` did not provide any mechanism to specify
+the directions of the shifts, taking each shift as an unsigned magnitude.
+For backwards compatibility, because three shifts in the same direction
+cannot result in a full-period XorshiftEngine, when all three of `sa`, `sb`,
+`sc, are positive `XorshiftEngine` treats them as unsigned magnitudes and
+uses shift directions to match the old behavior of `XorshiftEngine`.
+
+Not every set of shifts results in a full-period xorshift generator.
+The template does not currently at compile-time perform a full check
+for maximum period but in a future version might reject parameters
+resulting in shorter periods.
++/
+struct XorshiftEngine(UIntType, uint nbits, int sa, int sb, int sc)
+if (isUnsigned!UIntType && !(sa > 0 && sb > 0 && sc > 0))
{
- static assert(bits == 32 || bits == 64 || bits == 96 || bits == 128 || bits == 160 || bits == 192,
- "Xorshift supports only 32, 64, 96, 128, 160 and 192 bit versions. "
- ~ to!string(bits) ~ " is not supported.");
+ static assert(nbits > 0 && nbits % (UIntType.sizeof * 8) == 0,
+ "nbits must be an even multiple of "~UIntType.stringof
+ ~".sizeof * 8, not "~nbits.stringof~".");
+
+ static assert(!((sa >= 0) == (sb >= 0) && (sa >= 0) == (sc >= 0))
+ && (sa * sb * sc != 0),
+ "shifts cannot be zero and cannot all be in same direction: cannot be ["
+ ~sa.stringof~", "~sb.stringof~", "~sc.stringof~"].");
+
+ static assert(sa != sb && sb != sc,
+ "consecutive shifts with the same magnitude and direction would partially or completely cancel!");
+
+ static assert(UIntType.sizeof == uint.sizeof || UIntType.sizeof == ulong.sizeof,
+ "XorshiftEngine currently does not support type " ~ UIntType.sizeof
+ ~ " because it does not have a `seed` implementation for arrays "
+ ~ " of element types other than uint and ulong.");
public:
///Mark this as a Rng
enum bool isUniformRandom = true;
- /// Always $(D false) (random generators are infinite ranges).
+ /// Always `false` (random generators are infinite ranges).
enum empty = false;
/// Smallest generated value.
- enum UIntType min = 0;
+ enum UIntType min = _state.length == 1 ? 1 : 0;
/// Largest generated value.
enum UIntType max = UIntType.max;
private:
- enum size = bits / 32;
-
- static if (bits == 32)
- UIntType[size] seeds_ = [2_463_534_242];
- else static if (bits == 64)
- UIntType[size] seeds_ = [123_456_789, 362_436_069];
- else static if (bits == 96)
- UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629];
- else static if (bits == 128)
- UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123];
- else static if (bits == 160)
- UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321];
- else static if (bits == 192)
- {
- UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321, 6_615_241];
+ // Legacy 192 bit uint hybrid counter/xorshift.
+ enum bool isLegacy192Bit = UIntType.sizeof == uint.sizeof && nbits == 192;
+
+ // Shift magnitudes.
+ enum a = (sa < 0 ? -sa : sa);
+ enum b = (sb < 0 ? -sb : sb);
+ enum c = (sc < 0 ? -sc : sc);
+
+ // Shift expressions to mix in.
+ enum shiftA(string expr) = `((`~expr~`) `~(sa > 0 ? `<< a)` : ` >>> a)`);
+ enum shiftB(string expr) = `((`~expr~`) `~(sb > 0 ? `<< b)` : ` >>> b)`);
+ enum shiftC(string expr) = `((`~expr~`) `~(sc > 0 ? `<< c)` : ` >>> c)`);
+
+ enum N = nbits / (UIntType.sizeof * 8);
+
+ // For N <= 2 it is strictly worse to use an index.
+ // Informal third-party benchmarks suggest that for `ulong` it is
+ // faster to use an index when N == 4. For `uint` we err on the side
+ // of not increasing the struct's size and only switch to the other
+ // implementation when N > 4.
+ enum useIndex = !isLegacy192Bit && (UIntType.sizeof >= ulong.sizeof ? N > 3 : N > 4);
+ static if (useIndex)
+ {
+ enum initialIndex = N - 1;
+ uint _index = initialIndex;
+ }
+
+ static if (N == 1 && UIntType.sizeof <= uint.sizeof)
+ {
+ UIntType[N] _state = [cast(UIntType) 2_463_534_242];
+ }
+ else static if (isLegacy192Bit)
+ {
+ UIntType[N] _state = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321, 6_615_241];
UIntType value_;
}
+ else static if (N <= 5 && UIntType.sizeof <= uint.sizeof)
+ {
+ UIntType[N] _state = [
+ cast(UIntType) 123_456_789,
+ cast(UIntType) 362_436_069,
+ cast(UIntType) 521_288_629,
+ cast(UIntType) 88_675_123,
+ cast(UIntType) 5_783_321][0 .. N];
+ }
else
{
- static assert(false, "Phobos Error: Xorshift has no instantiation rule for "
- ~ to!string(bits) ~ " bits.");
+ UIntType[N] _state = ()
+ {
+ static if (UIntType.sizeof < ulong.sizeof)
+ {
+ uint x0 = 123_456_789;
+ enum uint m = 1_812_433_253U;
+ }
+ else static if (UIntType.sizeof <= ulong.sizeof)
+ {
+ ulong x0 = 123_456_789;
+ enum ulong m = 6_364_136_223_846_793_005UL;
+ }
+ else
+ {
+ static assert(0, "Phobos Error: Xorshift has no instantiation rule for "
+ ~ UIntType.stringof);
+ }
+ enum uint rshift = typeof(x0).sizeof * 8 - 2;
+ UIntType[N] result = void;
+ foreach (i, ref e; result)
+ {
+ e = cast(UIntType) (x0 = (m * (x0 ^ (x0 >>> rshift)) + i + 1));
+ if (e == 0)
+ e = cast(UIntType) (i + 1);
+ }
+ return result;
+ }();
}
public:
- /**
- * Constructs a $(D XorshiftEngine) generator seeded with $(D_PARAM x0).
- */
- this(UIntType x0) @safe pure nothrow @nogc
+ /++
+ Constructs a `XorshiftEngine` generator seeded with $(D_PARAM x0).
+
+ Params:
+ x0 = value used to deterministically initialize internal state
+ +/
+ this()(UIntType x0) @safe pure nothrow @nogc
{
seed(x0);
}
- /**
- * (Re)seeds the generator.
- */
- void seed(UIntType x0) @safe pure nothrow @nogc
+ /++
+ (Re)seeds the generator.
+
+ Params:
+ x0 = value used to deterministically initialize internal state
+ +/
+ void seed()(UIntType x0) @safe pure nothrow @nogc
{
- // Initialization routine from MersenneTwisterEngine.
- foreach (i, e; seeds_)
- seeds_[i] = x0 = cast(UIntType)(1_812_433_253U * (x0 ^ (x0 >> 30)) + i + 1);
+ static if (useIndex)
+ _index = initialIndex;
- // All seeds must not be 0.
- sanitizeSeeds(seeds_);
+ static if (UIntType.sizeof == uint.sizeof)
+ {
+ // Initialization routine from MersenneTwisterEngine.
+ foreach (uint i, ref e; _state)
+ {
+ e = (x0 = (1_812_433_253U * (x0 ^ (x0 >> 30)) + i + 1));
+ // Xorshift requires merely that not every word of the internal
+ // array is 0. For historical compatibility the 32-bit word version
+ // has the stronger requirement that not any word of the state
+ // array is 0 after initial seeding.
+ if (e == 0)
+ e = (i + 1);
+ }
+ }
+ else static if (UIntType.sizeof == ulong.sizeof)
+ {
+ static if (N > 1)
+ {
+ // Initialize array using splitmix64 as recommended by Sebastino Vigna.
+ // By construction the array will not be all zeroes.
+ // http://xoroshiro.di.unimi.it/splitmix64.c
+ foreach (ref e; _state)
+ {
+ ulong z = (x0 += 0x9e37_79b9_7f4a_7c15UL);
+ z = (z ^ (z >>> 30)) * 0xbf58_476d_1ce4_e5b9UL;
+ z = (z ^ (z >>> 27)) * 0x94d0_49bb_1331_11ebUL;
+ e = z ^ (z >>> 31);
+ }
+ }
+ else
+ {
+ // Apply a transformation when N == 1 instead of just copying x0
+ // directly because it's not unlikely that a user might initialize
+ // a PRNG with small counting numbers (e.g. 1, 2, 3) that have the
+ // statistically rare property of having only 1 or 2 non-zero bits.
+ // Additionally we need to ensure that the internal state is not
+ // entirely zero.
+ if (x0 != 0)
+ _state[0] = x0 * 6_364_136_223_846_793_005UL;
+ else
+ _state[0] = typeof(this).init._state[0];
+ }
+ }
+ else
+ {
+ static assert(0, "Phobos Error: Xorshift has no `seed` implementation for "
+ ~ UIntType.stringof);
+ }
popFront();
}
@@ -1081,10 +1461,12 @@ if (isUnsigned!UIntType)
@property
UIntType front() const @safe pure nothrow @nogc
{
- static if (bits == 192)
+ static if (isLegacy192Bit)
return value_;
+ else static if (!useIndex)
+ return _state[N-1];
else
- return seeds_[size - 1];
+ return _state[_index];
}
@@ -1093,58 +1475,42 @@ if (isUnsigned!UIntType)
*/
void popFront() @safe pure nothrow @nogc
{
- UIntType temp;
-
- static if (bits == 32)
- {
- temp = seeds_[0] ^ (seeds_[0] << a);
- temp = temp ^ (temp >> b);
- seeds_[0] = temp ^ (temp << c);
- }
- else static if (bits == 64)
- {
- temp = seeds_[0] ^ (seeds_[0] << a);
- seeds_[0] = seeds_[1];
- seeds_[1] = seeds_[1] ^ (seeds_[1] >> c) ^ temp ^ (temp >> b);
- }
- else static if (bits == 96)
+ alias s = _state;
+ static if (isLegacy192Bit)
{
- temp = seeds_[0] ^ (seeds_[0] << a);
- seeds_[0] = seeds_[1];
- seeds_[1] = seeds_[2];
- seeds_[2] = seeds_[2] ^ (seeds_[2] >> c) ^ temp ^ (temp >> b);
+ auto x = _state[0] ^ mixin(shiftA!`s[0]`);
+ static foreach (i; 0 .. N-2)
+ s[i] = s[i + 1];
+ s[N-2] = s[N-2] ^ mixin(shiftC!`s[N-2]`) ^ x ^ mixin(shiftB!`x`);
+ value_ = s[N-2] + (s[N-1] += 362_437);
}
- else static if (bits == 128)
+ else static if (N == 1)
{
- temp = seeds_[0] ^ (seeds_[0] << a);
- seeds_[0] = seeds_[1];
- seeds_[1] = seeds_[2];
- seeds_[2] = seeds_[3];
- seeds_[3] = seeds_[3] ^ (seeds_[3] >> c) ^ temp ^ (temp >> b);
+ s[0] ^= mixin(shiftA!`s[0]`);
+ s[0] ^= mixin(shiftB!`s[0]`);
+ s[0] ^= mixin(shiftC!`s[0]`);
}
- else static if (bits == 160)
+ else static if (!useIndex)
{
- temp = seeds_[0] ^ (seeds_[0] << a);
- seeds_[0] = seeds_[1];
- seeds_[1] = seeds_[2];
- seeds_[2] = seeds_[3];
- seeds_[3] = seeds_[4];
- seeds_[4] = seeds_[4] ^ (seeds_[4] >> c) ^ temp ^ (temp >> b);
- }
- else static if (bits == 192)
- {
- temp = seeds_[0] ^ (seeds_[0] >> a);
- seeds_[0] = seeds_[1];
- seeds_[1] = seeds_[2];
- seeds_[2] = seeds_[3];
- seeds_[3] = seeds_[4];
- seeds_[4] = seeds_[4] ^ (seeds_[4] << c) ^ temp ^ (temp << b);
- value_ = seeds_[4] + (seeds_[5] += 362_437);
+ auto x = s[0] ^ mixin(shiftA!`s[0]`);
+ static foreach (i; 0 .. N-1)
+ s[i] = s[i + 1];
+ s[N-1] = s[N-1] ^ mixin(shiftC!`s[N-1]`) ^ x ^ mixin(shiftB!`x`);
}
else
{
- static assert(false, "Phobos Error: Xorshift has no popFront() update for "
- ~ to!string(bits) ~ " bits.");
+ assert(_index < N); // Invariant.
+ const sIndexMinus1 = s[_index];
+ static if ((N & (N - 1)) == 0)
+ _index = (_index + 1) & typeof(_index)(N - 1);
+ else
+ {
+ if (++_index >= N)
+ _index = 0;
+ }
+ auto x = s[_index];
+ x ^= mixin(shiftA!`x`);
+ s[_index] = sIndexMinus1 ^ mixin(shiftC!`sIndexMinus1`) ^ x ^ mixin(shiftB!`x`);
}
}
@@ -1153,49 +1519,43 @@ if (isUnsigned!UIntType)
* Captures a range state.
*/
@property
- typeof(this) save() @safe pure nothrow @nogc
+ typeof(this) save() const @safe pure nothrow @nogc
{
return this;
}
+private:
+ // Workaround for a DScanner bug. If we remove this `private:` DScanner
+ // gives erroneous warnings about missing documentation for public symbols
+ // later in the module.
+}
- /**
- * Compares against $(D_PARAM rhs) for equality.
- */
- bool opEquals(ref const XorshiftEngine rhs) const @safe pure nothrow @nogc
- {
- return seeds_ == rhs.seeds_;
- }
-
-
- private:
- static void sanitizeSeeds(ref UIntType[size] seeds) @safe pure nothrow @nogc
- {
- for (uint i; i < seeds.length; i++)
- {
- if (seeds[i] == 0)
- seeds[i] = i + 1;
- }
- }
-
-
- @safe pure nothrow unittest
- {
- static if (size == 4) // Other bits too
- {
- UIntType[size] seeds = [1, 0, 0, 4];
-
- sanitizeSeeds(seeds);
+/// ditto
+template XorshiftEngine(UIntType, int bits, int a, int b, int c)
+if (isUnsigned!UIntType && a > 0 && b > 0 && c > 0)
+{
+ // Compatibility with old parameterizations without explicit shift directions.
+ static if (bits == UIntType.sizeof * 8)
+ alias XorshiftEngine = .XorshiftEngine!(UIntType, bits, a, -b, c);//left, right, left
+ else static if (bits == 192 && UIntType.sizeof == uint.sizeof)
+ alias XorshiftEngine = .XorshiftEngine!(UIntType, bits, -a, b, c);//right, left, left
+ else
+ alias XorshiftEngine = .XorshiftEngine!(UIntType, bits, a, -b, -c);//left, right, right
+}
- assert(seeds == [1, 2, 3, 4]);
- }
- }
+///
+@safe unittest
+{
+ alias Xorshift96 = XorshiftEngine!(uint, 96, 10, 5, 26);
+ auto rnd = Xorshift96(42);
+ auto num = rnd.front; // same for each run
+ assert(num == 2704588748);
}
/**
- * Define $(D XorshiftEngine) generators with well-chosen parameters. See each bits examples of "Xorshift RNGs".
- * $(D Xorshift) is a Xorshift128's alias because 128bits implementation is mostly used.
+ * Define `XorshiftEngine` generators with well-chosen parameters. See each bits examples of "Xorshift RNGs".
+ * `Xorshift` is a Xorshift128's alias because 128bits implementation is mostly used.
*/
alias Xorshift32 = XorshiftEngine!(uint, 32, 13, 17, 15) ;
alias Xorshift64 = XorshiftEngine!(uint, 64, 10, 13, 10); /// ditto
@@ -1206,18 +1566,19 @@ alias Xorshift192 = XorshiftEngine!(uint, 192, 2, 1, 4); /// ditto
alias Xorshift = Xorshift128; /// ditto
///
-@safe unittest
+@safe @nogc unittest
{
// Seed with a constant
auto rnd = Xorshift(1);
auto num = rnd.front; // same for each run
+ assert(num == 1405313047);
// Seed with an unpredictable value
rnd.seed(unpredictableSeed);
num = rnd.front; // different across rnd
}
-@safe unittest
+@safe @nogc unittest
{
import std.range;
static assert(isForwardRange!Xorshift);
@@ -1226,8 +1587,10 @@ alias Xorshift = Xorshift128; /// ditto
static assert(isSeedable!Xorshift);
static assert(isSeedable!(Xorshift, uint));
+ static assert(Xorshift32.min == 1);
+
// Result from reference implementation.
- auto checking = [
+ static ulong[][] checking = [
[2463534242UL, 901999875, 3371835698, 2675058524, 1053936272, 3811264849,
472493137, 3856898176, 2131710969, 2312157505],
[362436069UL, 2113136921, 19051112, 3010520417, 951284840, 1213972223,
@@ -1239,10 +1602,19 @@ alias Xorshift = Xorshift128; /// ditto
[5783321UL, 393427209, 1947109840, 565829276, 1006220149, 971147905,
1436324242, 2800460115, 1484058076, 3823330032],
[0UL, 246875399, 3690007200, 1264581005, 3906711041, 1866187943, 2481925219,
- 2464530826, 1604040631, 3653403911]
+ 2464530826, 1604040631, 3653403911],
+ [16749904790159980466UL, 14489774923612894650UL, 148813570191443371UL,
+ 6529783008780612886UL, 10182425759614080046UL, 16549659571055687844UL,
+ 542957868271744939UL, 9459127085596028450UL, 16001049981702441780UL,
+ 7351634712593111741],
+ [14750058843113249055UL, 17731577314455387619UL, 1314705253499959044UL,
+ 3113030620614841056UL, 9468075444678629182UL, 13962152036600088141UL,
+ 9030205609946043947UL, 1856726150434672917UL, 8098922200110395314UL,
+ 2772699174618556175UL],
];
- alias XorshiftTypes = std.meta.AliasSeq!(Xorshift32, Xorshift64, Xorshift96, Xorshift128, Xorshift160, Xorshift192);
+ alias XorshiftTypes = std.meta.AliasSeq!(Xorshift32, Xorshift64, Xorshift96,
+ Xorshift128, Xorshift160, Xorshift192, Xorshift64_64, Xorshift128_64);
foreach (I, Type; XorshiftTypes)
{
@@ -1258,12 +1630,15 @@ alias Xorshift = Xorshift128; /// ditto
// Check .save works
foreach (Type; XorshiftTypes)
{
- auto rnd1 = Type(unpredictableSeed);
- auto rnd2 = rnd1.save;
+ auto rnd1 = Type(123_456_789);
+ rnd1.popFront();
+ // https://issues.dlang.org/show_bug.cgi?id=15853
+ auto rnd2 = ((const ref Type a) => a.save())(rnd1);
assert(rnd1 == rnd2);
// Enable next test when RNGs are reference types
version (none) { assert(rnd1 !is rnd2); }
- assert(rnd1.take(100).array() == rnd2.take(100).array());
+ for (auto i = 0; i <= Type.sizeof / 4; i++, rnd1.popFront, rnd2.popFront)
+ assert(rnd1.front() == rnd2.front());
}
}
@@ -1273,15 +1648,124 @@ alias Xorshift = Xorshift128; /// ditto
* object is compatible with all the pseudo-random number generators
* available. It is enabled only in unittest mode.
*/
-@safe unittest
+@safe @nogc unittest
{
foreach (Rng; PseudoRngTypes)
{
static assert(isUniformRNG!Rng);
- auto rng = Rng(unpredictableSeed);
+ auto rng = Rng(123_456_789);
+ }
+}
+
+version (CRuntime_Bionic)
+ version = SecureARC4Random; // ChaCha20
+version (Darwin)
+ version = SecureARC4Random; // AES
+version (OpenBSD)
+ version = SecureARC4Random; // ChaCha20
+version (NetBSD)
+ version = SecureARC4Random; // ChaCha20
+
+version (CRuntime_UClibc)
+ version = LegacyARC4Random; // ARC4
+version (FreeBSD)
+ version = LegacyARC4Random; // ARC4
+version (DragonFlyBSD)
+ version = LegacyARC4Random; // ARC4
+version (BSD)
+ version = LegacyARC4Random; // Unknown implementation
+
+// For the current purpose of unpredictableSeed the difference between
+// a secure arc4random implementation and a legacy implementation is
+// unimportant. The source code documents this distinction in case in the
+// future Phobos is altered to require cryptographically secure sources
+// of randomness, and also so other people reading this source code (as
+// Phobos is often looked to as an example of good D programming practices)
+// do not mistakenly use insecure versions of arc4random in contexts where
+// cryptographically secure sources of randomness are needed.
+
+// Performance note: ChaCha20 is about 70% faster than ARC4, contrary to
+// what one might assume from it being more secure.
+
+version (SecureARC4Random)
+ version = AnyARC4Random;
+version (LegacyARC4Random)
+ version = AnyARC4Random;
+
+version (AnyARC4Random)
+{
+ extern(C) private @nogc nothrow
+ {
+ uint arc4random() @safe;
+ void arc4random_buf(scope void* buf, size_t nbytes) @system;
}
}
+else
+{
+ private ulong bootstrapSeed() @nogc nothrow
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=19580
+ // previously used `ulong result = void` to start with an arbitary value
+ // but using an uninitialized variable's value is undefined behavior
+ // and enabled unwanted optimizations on non-DMD compilers.
+ ulong result;
+ enum ulong m = 0xc6a4_a793_5bd1_e995UL; // MurmurHash2_64A constant.
+ void updateResult(ulong x)
+ {
+ x *= m;
+ x = (x ^ (x >>> 47)) * m;
+ result = (result ^ x) * m;
+ }
+ import core.thread : getpid, Thread;
+ import core.time : MonoTime;
+
+ updateResult(cast(ulong) cast(void*) Thread.getThis());
+ updateResult(cast(ulong) getpid());
+ updateResult(cast(ulong) MonoTime.currTime.ticks);
+ result = (result ^ (result >>> 47)) * m;
+ return result ^ (result >>> 47);
+ }
+ // If we don't have arc4random and we don't have RDRAND fall back to this.
+ private ulong fallbackSeed() @nogc nothrow
+ {
+ // Bit avalanche function from MurmurHash3.
+ static ulong fmix64(ulong k) @nogc nothrow pure @safe
+ {
+ k = (k ^ (k >>> 33)) * 0xff51afd7ed558ccd;
+ k = (k ^ (k >>> 33)) * 0xc4ceb9fe1a85ec53;
+ return k ^ (k >>> 33);
+ }
+ // Using SplitMix algorithm with constant gamma.
+ // Chosen gamma is the odd number closest to 2^^64
+ // divided by the silver ratio (1.0L + sqrt(2.0L)).
+ enum gamma = 0x6a09e667f3bcc909UL;
+ import core.atomic : has64BitCAS;
+ static if (has64BitCAS)
+ {
+ import core.atomic : MemoryOrder, atomicLoad, atomicOp, atomicStore, cas;
+ shared static ulong seed;
+ shared static bool initialized;
+ if (0 == atomicLoad!(MemoryOrder.raw)(initialized))
+ {
+ cas(&seed, 0UL, fmix64(bootstrapSeed()));
+ atomicStore!(MemoryOrder.rel)(initialized, true);
+ }
+ return fmix64(atomicOp!"+="(seed, gamma));
+ }
+ else
+ {
+ static ulong seed;
+ static bool initialized;
+ if (!initialized)
+ {
+ seed = fmix64(bootstrapSeed());
+ initialized = true;
+ }
+ return fmix64(seed += gamma);
+ }
+ }
+}
/**
A "good" seed for initializing random number engines. Initializing
@@ -1290,24 +1774,135 @@ random number sequences every run.
Returns:
A single unsigned integer seed value, different on each successive call
+Note:
+In general periodically 'reseeding' a PRNG does not improve its quality
+and in some cases may harm it. For an extreme example the Mersenne
+Twister has `2 ^^ 19937 - 1` distinct states but after `seed(uint)` is
+called it can only be in one of `2 ^^ 32` distinct states regardless of
+how excellent the source of entropy is.
*/
-@property uint unpredictableSeed() @trusted
+@property uint unpredictableSeed() @trusted nothrow @nogc
{
- import core.thread : Thread, getpid, MonoTime;
- static bool seeded;
- static MinstdRand0 rand;
- if (!seeded)
+ version (AnyARC4Random)
{
- uint threadID = cast(uint) cast(void*) Thread.getThis();
- rand.seed((getpid() + threadID) ^ cast(uint) MonoTime.currTime.ticks);
- seeded = true;
+ return arc4random();
+ }
+ else
+ {
+ version (InlineAsm_X86_Any)
+ {
+ import core.cpuid : hasRdrand;
+ if (hasRdrand)
+ {
+ uint result;
+ asm @nogc nothrow
+ {
+ db 0x0f, 0xc7, 0xf0; // rdrand EAX
+ jnc LnotUsingRdrand;
+ // Some AMD CPUs shipped with bugs where RDRAND could fail
+ // but still set the carry flag to 1. In those cases the
+ // output will be -1.
+ cmp EAX, 0xffff_ffff;
+ je LnotUsingRdrand;
+ mov result, EAX;
+ }
+ return result;
+ }
+ LnotUsingRdrand:
+ }
+ return cast(uint) fallbackSeed();
}
- rand.popFront();
- return cast(uint) (MonoTime.currTime.ticks ^ rand.front);
+}
+
+/// ditto
+template unpredictableSeed(UIntType)
+if (isUnsigned!UIntType)
+{
+ static if (is(UIntType == uint))
+ alias unpredictableSeed = .unpredictableSeed;
+ else static if (!is(Unqual!UIntType == UIntType))
+ alias unpredictableSeed = .unpredictableSeed!(Unqual!UIntType);
+ else
+ /// ditto
+ @property UIntType unpredictableSeed() @nogc nothrow @trusted
+ {
+ version (AnyARC4Random)
+ {
+ static if (UIntType.sizeof <= uint.sizeof)
+ {
+ return cast(UIntType) arc4random();
+ }
+ else
+ {
+ UIntType result = void;
+ arc4random_buf(&result, UIntType.sizeof);
+ return result;
+ }
+ }
+ else
+ {
+ version (InlineAsm_X86_Any)
+ {
+ import core.cpuid : hasRdrand;
+ if (hasRdrand)
+ {
+ static if (UIntType.sizeof <= uint.sizeof)
+ {
+ uint result;
+ asm @nogc nothrow
+ {
+ db 0x0f, 0xc7, 0xf0; // rdrand EAX
+ jnc LnotUsingRdrand;
+ // Some AMD CPUs shipped with bugs where RDRAND could fail
+ // but still set the carry flag to 1. In those cases the
+ // output will be -1.
+ cmp EAX, 0xffff_ffff;
+ je LnotUsingRdrand;
+ mov result, EAX;
+ }
+ return cast(UIntType) result;
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ ulong result;
+ asm @nogc nothrow
+ {
+ db 0x48, 0x0f, 0xc7, 0xf0; // rdrand RAX
+ jnc LnotUsingRdrand;
+ // Some AMD CPUs shipped with bugs where RDRAND could fail
+ // but still set the carry flag to 1. In those cases the
+ // output will be -1.
+ cmp RAX, 0xffff_ffff_ffff_ffff;
+ je LnotUsingRdrand;
+ mov result, RAX;
+ }
+ return result;
+ }
+ else
+ {
+ uint resultLow, resultHigh;
+ asm @nogc nothrow
+ {
+ db 0x0f, 0xc7, 0xf0; // rdrand EAX
+ jnc LnotUsingRdrand;
+ mov resultLow, EAX;
+ db 0x0f, 0xc7, 0xf0; // rdrand EAX
+ jnc LnotUsingRdrand;
+ mov resultHigh, EAX;
+ }
+ if (resultLow != uint.max || resultHigh != uint.max) // Protect against AMD RDRAND bug.
+ return ((cast(ulong) resultHigh) << 32) ^ resultLow;
+ }
+ }
+ LnotUsingRdrand:
+ }
+ return cast(UIntType) fallbackSeed();
+ }
+ }
}
///
-@safe unittest
+@safe @nogc unittest
{
auto rnd = Random(unpredictableSeed);
auto n = rnd.front;
@@ -1324,7 +1919,7 @@ method being used.
alias Random = Mt19937;
-@safe unittest
+@safe @nogc unittest
{
static assert(isUniformRNG!Random);
static assert(isUniformRNG!(Random, uint));
@@ -1340,17 +1935,18 @@ and initialized to an unpredictable value for each thread.
Returns:
A singleton instance of the default random number generator
*/
-@property ref Random rndGen() @safe
+@property ref Random rndGen() @safe nothrow @nogc
{
- import std.algorithm.iteration : map;
- import std.range : repeat;
-
static Random result;
static bool initialized;
if (!initialized)
{
- static if (isSeedable!(Random, typeof(map!((a) => unpredictableSeed)(repeat(0)))))
- result.seed(map!((a) => unpredictableSeed)(repeat(0)));
+ static if (isSeedable!(Random, ulong))
+ result.seed(unpredictableSeed!ulong); // Avoid unnecessary copy.
+ else static if (is(Random : MersenneTwisterEngine!Params, Params...))
+ initMTEngine(result);
+ else static if (isSeedable!(Random, uint))
+ result.seed(unpredictableSeed!uint); // Avoid unnecessary copy.
else
result = Random(unpredictableSeed);
initialized = true;
@@ -1358,23 +1954,73 @@ A singleton instance of the default random number generator
return result;
}
+///
+@safe nothrow @nogc unittest
+{
+ import std.algorithm.iteration : sum;
+ import std.range : take;
+ auto rnd = rndGen;
+ assert(rnd.take(3).sum > 0);
+}
+
+/+
+Initialize a 32-bit MersenneTwisterEngine from 64 bits of entropy.
+This is private and accepts no seed as a parameter, freeing the internal
+implementaton from any need for stability across releases.
++/
+private void initMTEngine(MTEngine)(scope ref MTEngine mt)
+if (is(MTEngine : MersenneTwisterEngine!Params, Params...))
+{
+ pragma(inline, false); // Called no more than once per thread by rndGen.
+ ulong seed = unpredictableSeed!ulong;
+ static if (is(typeof(mt.seed(seed))))
+ {
+ mt.seed(seed);
+ }
+ else
+ {
+ alias UIntType = typeof(mt.front());
+ if (seed == 0) seed = -1; // Any number but 0 is fine.
+ uint s0 = cast(uint) seed;
+ uint s1 = cast(uint) (seed >> 32);
+ foreach (ref e; mt.state.data)
+ {
+ //http://xoshiro.di.unimi.it/xoroshiro64starstar.c
+ const tmp = s0 * 0x9E3779BB;
+ e = ((tmp << 5) | (tmp >> (32 - 5))) * 5;
+ static if (MTEngine.max != UIntType.max) { e &= MTEngine.max; }
+
+ const tmp1 = s0 ^ s1;
+ s0 = ((s0 << 26) | (s0 >> (32 - 26))) ^ tmp1 ^ (tmp1 << 9);
+ s1 = (tmp1 << 13) | (tmp1 >> (32 - 13));
+ }
+
+ mt.state.index = mt.state.data.length - 1;
+ // double popFront() to guarantee both `mt.state.z`
+ // and `mt.state.front` are derived from the newly
+ // set values in `mt.state.data`.
+ mt.popFront();
+ mt.popFront();
+ }
+}
+
/**
-Generates a number between $(D a) and $(D b). The $(D boundaries)
+Generates a number between `a` and `b`. The `boundaries`
parameter controls the shape of the interval (open vs. closed on
-either side). Valid values for $(D boundaries) are $(D "[]"), $(D
-"$(LPAREN)]"), $(D "[$(RPAREN)"), and $(D "()"). The default interval
+either side). Valid values for `boundaries` are `"[]"`, $(D
+"$(LPAREN)]"), `"[$(RPAREN)"`, and `"()"`. The default interval
is closed to the left and open to the right. The version that does not
-take $(D urng) uses the default generator $(D rndGen).
+take `urng` uses the default generator `rndGen`.
Params:
a = lower bound of the _uniform distribution
b = upper bound of the _uniform distribution
urng = (optional) random number generator to use;
- if not specified, defaults to $(D rndGen)
+ if not specified, defaults to `rndGen`
Returns:
A single random variate drawn from the _uniform distribution
- between $(D a) and $(D b), whose type is the common type of
+ between `a` and `b`, whose type is the common type of
these parameters
*/
auto uniform(string boundaries = "[)", T1, T2)
@@ -1387,11 +2033,34 @@ if (!is(CommonType!(T1, T2) == void))
///
@safe unittest
{
- auto gen = Random(unpredictableSeed);
+ auto rnd = Random(unpredictableSeed);
+
// Generate an integer in [0, 1023]
- auto a = uniform(0, 1024, gen);
+ auto a = uniform(0, 1024, rnd);
+ assert(0 <= a && a < 1024);
+
// Generate a float in [0, 1)
- auto b = uniform(0.0f, 1.0f, gen);
+ auto b = uniform(0.0f, 1.0f, rnd);
+ assert(0 <= b && b < 1);
+
+ // Generate a float in [0, 1]
+ b = uniform!"[]"(0.0f, 1.0f, rnd);
+ assert(0 <= b && b <= 1);
+
+ // Generate a float in (0, 1)
+ b = uniform!"()"(0.0f, 1.0f, rnd);
+ assert(0 < b && b < 1);
+}
+
+/// Create an array of random numbers using range functions and UFCS
+@safe unittest
+{
+ import std.array : array;
+ import std.range : generate, takeExactly;
+
+ int[] arr = generate!(() => uniform(0, 100)).takeExactly(10).array;
+ assert(arr.length == 10);
+ assert(arr[0] >= 0 && arr[0] < 100);
}
@safe unittest
@@ -1435,7 +2104,7 @@ if (isFloatingPoint!(CommonType!(T1, T2)) && isUniformRNG!UniformRandomNumberGen
alias NumberType = Unqual!(CommonType!(T1, T2));
static if (boundaries[0] == '(')
{
- import std.math : nextafter;
+ import std.math.operations : nextafter;
NumberType _a = nextafter(cast(NumberType) a, NumberType.infinity);
}
else
@@ -1444,7 +2113,7 @@ if (isFloatingPoint!(CommonType!(T1, T2)) && isUniformRNG!UniformRandomNumberGen
}
static if (boundaries[1] == ')')
{
- import std.math : nextafter;
+ import std.math.operations : nextafter;
NumberType _b = nextafter(cast(NumberType) b, -NumberType.infinity);
}
else
@@ -1504,22 +2173,22 @@ If we start at `UpperType.max` and walk backwards `upperDist - 1` spaces, then
the space we land on is the last acceptable position where a full bucket can
fit:
-```
+---
bucketFront UpperType.max
v v
[..., 0, 1, 2, ..., upperDist - 1]
^~~ upperDist - 1 ~~^
-```
+---
If the bucket starts any later, then it must have lost at least one number and
at least that number won't be represented fairly.
-```
+---
bucketFront UpperType.max
v v
[..., upperDist - 1, 0, 1, 2, ..., upperDist - 2]
^~~~~~~~ upperDist - 1 ~~~~~~~^
-```
+---
Hence, our condition to reroll is
`bucketFront > (UpperType.max - (upperDist - 1))`
@@ -1589,7 +2258,7 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) &&
@safe unittest
{
import std.conv : to;
- auto gen = Mt19937(unpredictableSeed);
+ auto gen = Mt19937(123_456_789);
static assert(isForwardRange!(typeof(gen)));
auto a = uniform(0, 1024, gen);
@@ -1599,9 +2268,9 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) &&
auto c = uniform(0.0, 1.0);
assert(0 <= c && c < 1);
- foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
+ static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
int, uint, long, ulong, float, double, real))
- {
+ {{
T lo = 0, hi = 100;
// Try tests with each of the possible bounds
@@ -1649,19 +2318,19 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) &&
assert(u <= T.max, "Upper bound violation for uniform!\"[]\" with " ~ T.stringof);
}
}
- }
+ }}
auto reproRng = Xorshift(239842);
- foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short,
+ static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short,
ushort, int, uint, long, ulong))
- {
+ {{
T lo = T.min + 10, hi = T.max - 10;
T init = uniform(lo, hi, reproRng);
size_t i = 50;
while (--i && uniform(lo, hi, reproRng) == init) {}
assert(i > 0);
- }
+ }}
{
bool sawLB = false, sawUB = false;
@@ -1721,18 +2390,64 @@ if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) &&
}
}
+/+
+Generates an unsigned integer in the half-open range `[0, k)`.
+Non-public because we locally guarantee `k > 0`.
+
+Params:
+ k = unsigned exclusive upper bound; caller guarantees this is non-zero
+ rng = random number generator to use
+
+Returns:
+ Pseudo-random unsigned integer strictly less than `k`.
++/
+private UInt _uniformIndex(UniformRNG, UInt = size_t)(const UInt k, ref UniformRNG rng)
+if (isUnsigned!UInt && isUniformRNG!UniformRNG)
+{
+ alias ResultType = UInt;
+ alias UpperType = Unsigned!(typeof(k - 0));
+ alias upperDist = k;
+
+ assert(upperDist != 0);
+
+ // For backwards compatibility use same algorithm as uniform(0, k, rng).
+ UpperType offset, rnum, bucketFront;
+ do
+ {
+ rnum = uniform!UpperType(rng);
+ offset = rnum % upperDist;
+ bucketFront = rnum - offset;
+ } // while we're in an unfair bucket...
+ while (bucketFront > (UpperType.max - (upperDist - 1)));
+
+ return cast(ResultType) offset;
+}
+
+pure @safe unittest
+{
+ // For backwards compatibility check that _uniformIndex(k, rng)
+ // has the same result as uniform(0, k, rng).
+ auto rng1 = Xorshift(123_456_789);
+ auto rng2 = rng1.save();
+ const size_t k = (1U << 31) - 1;
+ assert(_uniformIndex(k, rng1) == uniform(0, k, rng2));
+}
+
/**
Generates a uniformly-distributed number in the range $(D [T.min,
-T.max]) for any integral or character type $(D T). If no random
-number generator is passed, uses the default $(D rndGen).
+T.max]) for any integral or character type `T`. If no random
+number generator is passed, uses the default `rndGen`.
+
+If an `enum` is used as type, the random variate is drawn with
+equal probability from any of the possible values of the enum `E`.
Params:
urng = (optional) random number generator to use;
- if not specified, defaults to $(D rndGen)
+ if not specified, defaults to `rndGen`
Returns:
Random variate drawn from the _uniform distribution across all
- possible values of the integral or character type $(D T).
+ possible values of the integral, character or enum type `T`.
*/
auto uniform(T, UniformRandomNumberGenerator)
(ref UniformRandomNumberGenerator urng)
@@ -1741,9 +2456,9 @@ if (!is(T == enum) && (isIntegral!T || isSomeChar!T) && isUniformRNG!UniformRand
/* dchar does not use its full bit range, so we must
* revert to the uniform with specified bounds
*/
- static if (is(T == dchar))
+ static if (is(immutable T == immutable dchar))
{
- return uniform!"[]"(T.min, T.max);
+ return uniform!"[]"(T.min, T.max, urng);
}
else
{
@@ -1770,11 +2485,36 @@ if (!is(T == enum) && (isIntegral!T || isSomeChar!T))
return uniform!T(rndGen);
}
+///
+@safe unittest
+{
+ auto rnd = MinstdRand0(42);
+
+ assert(rnd.uniform!ubyte == 102);
+ assert(rnd.uniform!ulong == 4838462006927449017);
+
+ enum Fruit { apple, mango, pear }
+ version (X86_64) // https://issues.dlang.org/show_bug.cgi?id=15147
+ assert(rnd.uniform!Fruit == Fruit.mango);
+}
+
+@safe unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=21383
+ auto rng1 = Xorshift32(123456789);
+ auto rng2 = rng1.save;
+ assert(rng1.uniform!dchar == rng2.uniform!dchar);
+ // https://issues.dlang.org/show_bug.cgi?id=21384
+ assert(rng1.uniform!(const shared dchar) <= dchar.max);
+ // https://issues.dlang.org/show_bug.cgi?id=8671
+ double t8671 = 1.0 - uniform(0.0, 1.0);
+}
+
@safe unittest
{
- foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
+ static foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
int, uint, long, ulong))
- {
+ {{
T init = uniform!T();
size_t i = 50;
while (--i && uniform!T() == init) {}
@@ -1787,21 +2527,10 @@ if (!is(T == enum) && (isIntegral!T || isSomeChar!T))
assert(T.min <= u, "Lower bound violation for uniform!" ~ T.stringof);
assert(u <= T.max, "Upper bound violation for uniform!" ~ T.stringof);
}
- }
+ }}
}
-/**
-Returns a uniformly selected member of enum $(D E). If no random number
-generator is passed, uses the default $(D rndGen).
-
-Params:
- urng = (optional) random number generator to use;
- if not specified, defaults to $(D rndGen)
-
-Returns:
- Random variate drawn with equal probability from any
- of the possible values of the enum $(D E).
- */
+/// ditto
auto uniform(E, UniformRandomNumberGenerator)
(ref UniformRandomNumberGenerator urng)
if (is(E == enum) && isUniformRNG!UniformRandomNumberGenerator)
@@ -1817,13 +2546,6 @@ if (is(E == enum))
return uniform!E(rndGen);
}
-///
-@safe unittest
-{
- enum Fruit { apple, mango, pear }
- auto randFruit = uniform!Fruit();
-}
-
@safe unittest
{
enum Fruit { Apple = 12, Mango = 29, Pear = 72 }
@@ -1838,20 +2560,20 @@ if (is(E == enum))
/**
* Generates a uniformly-distributed floating point number of type
- * $(D T) in the range [0, 1$(RPAREN). If no random number generator is
- * specified, the default RNG $(D rndGen) will be used as the source
+ * `T` in the range [0, 1$(RPAREN). If no random number generator is
+ * specified, the default RNG `rndGen` will be used as the source
* of randomness.
*
- * $(D uniform01) offers a faster generation of random variates than
+ * `uniform01` offers a faster generation of random variates than
* the equivalent $(D uniform!"[$(RPAREN)"(0.0, 1.0)) and so may be preferred
* for some applications.
*
* Params:
* rng = (optional) random number generator to use;
- * if not specified, defaults to $(D rndGen)
+ * if not specified, defaults to `rndGen`
*
* Returns:
- * Floating-point random variate of type $(D T) drawn from the _uniform
+ * Floating-point random variate of type `T` drawn from the _uniform
* distribution across the half-open interval [0, 1$(RPAREN).
*
*/
@@ -1869,7 +2591,7 @@ out (result)
assert(0 <= result);
assert(result < 1);
}
-body
+do
{
alias R = typeof(rng.front);
static if (isIntegral!R)
@@ -1890,8 +2612,7 @@ body
immutable T u = (rng.front - rng.min) * factor;
rng.popFront();
- import core.stdc.limits : CHAR_BIT; // CHAR_BIT is always 8
- static if (isIntegral!R && T.mant_dig >= (CHAR_BIT * R.sizeof))
+ static if (isIntegral!R && T.mant_dig >= (8 * R.sizeof))
{
/* If RNG variates are integral and T has enough precision to hold
* R without loss, we're guaranteed by the definition of factor
@@ -1917,15 +2638,34 @@ body
assert(false);
}
-@safe unittest
+///
+@safe @nogc unittest
+{
+ import std.math.operations : feqrel;
+
+ auto rnd = MinstdRand0(42);
+
+ // Generate random numbers in the range in the range [0, 1)
+ auto u1 = uniform01(rnd);
+ assert(u1 >= 0 && u1 < 1);
+
+ auto u2 = rnd.uniform01!float;
+ assert(u2 >= 0 && u2 < 1);
+
+ // Confirm that the random values with the initial seed 42 are 0.000328707 and 0.524587
+ assert(u1.feqrel(0.000328707) > 20);
+ assert(u2.feqrel(0.524587) > 20);
+}
+
+@safe @nogc unittest
{
import std.meta;
- foreach (UniformRNG; PseudoRngTypes)
- {
+ static foreach (UniformRNG; PseudoRngTypes)
+ {{
- foreach (T; std.meta.AliasSeq!(float, double, real))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
- UniformRNG rng = UniformRNG(unpredictableSeed);
+ static foreach (T; std.meta.AliasSeq!(float, double, real))
+ {{
+ UniformRNG rng = UniformRNG(123_456_789);
auto a = uniform01();
assert(is(typeof(a) == double));
@@ -1948,14 +2688,14 @@ body
while (--i && uniform01!T(rng) == init) {}
assert(i > 0);
assert(i < 50);
- }();
- }
+ }}
+ }}
}
/**
-Generates a uniform probability distribution of size $(D n), i.e., an
-array of size $(D n) of positive numbers of type $(D F) that sum to
-$(D 1). If $(D useThis) is provided, it is used as storage.
+Generates a uniform probability distribution of size `n`, i.e., an
+array of size `n` of positive numbers of type `F` that sum to
+`1`. If `useThis` is provided, it is used as storage.
*/
F[] uniformDistribution(F = double)(size_t n, F[] useThis = null)
if (isFloatingPoint!F)
@@ -1970,17 +2710,19 @@ if (isFloatingPoint!F)
return useThis;
}
+///
@safe unittest
{
- import std.algorithm;
- import std.math;
- static assert(is(CommonType!(double, int) == double));
+ import std.algorithm.iteration : reduce;
+ import std.math.operations : isClose;
+
auto a = uniformDistribution(5);
assert(a.length == 5);
- assert(approxEqual(reduce!"a + b"(a), 1));
+ assert(isClose(reduce!"a + b"(a), 1));
+
a = uniformDistribution(10, a);
assert(a.length == 10);
- assert(approxEqual(reduce!"a + b"(a), 1));
+ assert(isClose(reduce!"a + b"(a), 1));
}
/**
@@ -1998,8 +2740,7 @@ Returns:
return a `ref` to the $(D range element), otherwise it will return
a copy.
*/
-auto ref choice(Range, RandomGen = Random)(auto ref Range range,
- ref RandomGen urng = rndGen)
+auto ref choice(Range, RandomGen = Random)(auto ref Range range, ref RandomGen urng)
if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen)
{
assert(range.length > 0,
@@ -2008,22 +2749,20 @@ if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen)
return range[uniform(size_t(0), $, urng)];
}
+/// ditto
+auto ref choice(Range)(auto ref Range range)
+{
+ return choice(range, rndGen);
+}
+
///
@safe unittest
{
- import std.algorithm.searching : canFind;
-
- auto array = [1, 2, 3, 4, 5];
- auto elem = choice(array);
-
- assert(canFind(array, elem),
- "Choice did not return a valid element from the given Range");
-
- auto urng = Random(unpredictableSeed);
- elem = choice(array, urng);
+ auto rnd = MinstdRand0(42);
- assert(canFind(array, elem),
- "Choice did not return a valid element from the given Range");
+ auto elem = [1, 2, 3, 4, 5].choice(rnd);
+ version (X86_64) // https://issues.dlang.org/show_bug.cgi?id=15147
+ assert(elem == 3);
}
@safe unittest
@@ -2067,38 +2806,58 @@ if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen)
}
/**
-Shuffles elements of $(D r) using $(D gen) as a shuffler. $(D r) must be
-a random-access range with length. If no RNG is specified, $(D rndGen)
+Shuffles elements of `r` using `gen` as a shuffler. `r` must be
+a random-access range with length. If no RNG is specified, `rndGen`
will be used.
Params:
r = random-access range whose elements are to be shuffled
gen = (optional) random number generator to use; if not
- specified, defaults to $(D rndGen)
- */
+ specified, defaults to `rndGen`
+Returns:
+ The shuffled random-access range.
+*/
-void randomShuffle(Range, RandomGen)(Range r, ref RandomGen gen)
+Range randomShuffle(Range, RandomGen)(Range r, ref RandomGen gen)
if (isRandomAccessRange!Range && isUniformRNG!RandomGen)
{
- return partialShuffle!(Range, RandomGen)(r, r.length, gen);
+ import std.algorithm.mutation : swapAt;
+ const n = r.length;
+ foreach (i; 0 .. n)
+ {
+ r.swapAt(i, i + _uniformIndex(n - i, gen));
+ }
+ return r;
}
/// ditto
-void randomShuffle(Range)(Range r)
+Range randomShuffle(Range)(Range r)
if (isRandomAccessRange!Range)
{
return randomShuffle(r, rndGen);
}
+///
@safe unittest
{
+ auto rnd = MinstdRand0(42);
+
+ auto arr = [1, 2, 3, 4, 5].randomShuffle(rnd);
+ version (X86_64) // https://issues.dlang.org/show_bug.cgi?id=15147
+ assert(arr == [3, 5, 2, 4, 1]);
+}
+
+@safe unittest
+{
+ int[10] sa = void;
+ int[10] sb = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
import std.algorithm.sorting : sort;
foreach (RandomGen; PseudoRngTypes)
{
- // Also tests partialShuffle indirectly.
- auto a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
- auto b = a.dup;
- auto gen = RandomGen(unpredictableSeed);
+ sa[] = sb[];
+ auto a = sa[];
+ auto b = sb[];
+ auto gen = RandomGen(123_456_789);
randomShuffle(a, gen);
sort(a);
assert(a == b);
@@ -2106,27 +2865,47 @@ if (isRandomAccessRange!Range)
sort(a);
assert(a == b);
}
+ // For backwards compatibility verify randomShuffle(r, gen)
+ // is equivalent to partialShuffle(r, 0, r.length, gen).
+ auto gen1 = Xorshift(123_456_789);
+ auto gen2 = gen1.save();
+ sa[] = sb[];
+ // @nogc std.random.randomShuffle.
+ // https://issues.dlang.org/show_bug.cgi?id=19156
+ () @nogc nothrow pure { randomShuffle(sa[], gen1); }();
+ partialShuffle(sb[], sb.length, gen2);
+ assert(sa[] == sb[]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18501
+@safe unittest
+{
+ import std.algorithm.comparison : among;
+ auto r = randomShuffle([0,1]);
+ assert(r.among([0,1],[1,0]));
}
/**
-Partially shuffles the elements of $(D r) such that upon returning $(D r[0 .. n])
-is a random subset of $(D r) and is randomly ordered. $(D r[n .. r.length])
+Partially shuffles the elements of `r` such that upon returning $(D r[0 .. n])
+is a random subset of `r` and is randomly ordered. $(D r[n .. r.length])
will contain the elements not in $(D r[0 .. n]). These will be in an undefined
order, but will not be random in the sense that their order after
-$(D partialShuffle) returns will not be independent of their order before
-$(D partialShuffle) was called.
+`partialShuffle` returns will not be independent of their order before
+`partialShuffle` was called.
-$(D r) must be a random-access range with length. $(D n) must be less than
-or equal to $(D r.length). If no RNG is specified, $(D rndGen) will be used.
+`r` must be a random-access range with length. `n` must be less than
+or equal to `r.length`. If no RNG is specified, `rndGen` will be used.
Params:
r = random-access range whose elements are to be shuffled
- n = number of elements of $(D r) to shuffle (counting from the beginning);
- must be less than $(D r.length)
+ n = number of elements of `r` to shuffle (counting from the beginning);
+ must be less than `r.length`
gen = (optional) random number generator to use; if not
- specified, defaults to $(D rndGen)
+ specified, defaults to `rndGen`
+Returns:
+ The shuffled random-access range.
*/
-void partialShuffle(Range, RandomGen)(Range r, in size_t n, ref RandomGen gen)
+Range partialShuffle(Range, RandomGen)(Range r, in size_t n, ref RandomGen gen)
if (isRandomAccessRange!Range && isUniformRNG!RandomGen)
{
import std.algorithm.mutation : swapAt;
@@ -2136,15 +2915,36 @@ if (isRandomAccessRange!Range && isUniformRNG!RandomGen)
{
r.swapAt(i, uniform(i, r.length, gen));
}
+ return r;
}
/// ditto
-void partialShuffle(Range)(Range r, in size_t n)
+Range partialShuffle(Range)(Range r, in size_t n)
if (isRandomAccessRange!Range)
{
return partialShuffle(r, n, rndGen);
}
+///
+@safe unittest
+{
+ auto rnd = MinstdRand0(42);
+
+ auto arr = [1, 2, 3, 4, 5, 6];
+ arr = arr.dup.partialShuffle(1, rnd);
+
+ version (X86_64) // https://issues.dlang.org/show_bug.cgi?id=15147
+ assert(arr == [2, 1, 3, 4, 5, 6]); // 1<->2
+
+ arr = arr.dup.partialShuffle(2, rnd);
+ version (X86_64) // https://issues.dlang.org/show_bug.cgi?id=15147
+ assert(arr == [1, 4, 3, 2, 5, 6]); // 1<->2, 2<->4
+
+ arr = arr.dup.partialShuffle(3, rnd);
+ version (X86_64) // https://issues.dlang.org/show_bug.cgi?id=15147
+ assert(arr == [5, 4, 6, 2, 1, 3]); // 1<->5, 2<->4, 3<->6
+}
+
@safe unittest
{
import std.algorithm;
@@ -2187,11 +2987,11 @@ if (isRandomAccessRange!Range)
/**
Rolls a dice with relative probabilities stored in $(D
-proportions). Returns the index in $(D proportions) that was chosen.
+proportions). Returns the index in `proportions` that was chosen.
Params:
rnd = (optional) random number generator to use; if not
- specified, defaults to $(D rndGen)
+ specified, defaults to `rndGen`
proportions = forward range or list of individual values
whose elements correspond to the probabilities
with which to choose the corresponding index
@@ -2199,9 +2999,9 @@ Params:
Returns:
Random variate drawn from the index values
- [0, ... $(D proportions.length) - 1], with the probability
- of getting an individual index value $(D i) being proportional to
- $(D proportions[i]).
+ [0, ... `proportions.length` - 1], with the probability
+ of getting an individual index value `i` being proportional to
+ `proportions[i]`.
*/
size_t dice(Rng, Num)(ref Rng rnd, Num[] proportions...)
if (isNumeric!Num && isForwardRange!Rng)
@@ -2239,6 +3039,16 @@ if (isNumeric!Num)
// and 2 10% of the time
}
+///
+@safe unittest
+{
+ auto rnd = MinstdRand0(42);
+ auto z = rnd.dice(70, 20, 10);
+ assert(z == 0);
+ z = rnd.dice(30, 20, 40, 10);
+ assert(z == 2);
+}
+
private size_t diceImpl(Rng, Range)(ref Rng rng, scope Range proportions)
if (isForwardRange!Range && isNumeric!(ElementType!Range) && isForwardRange!Rng)
in
@@ -2246,7 +3056,7 @@ in
import std.algorithm.searching : all;
assert(proportions.save.all!"a >= 0");
}
-body
+do
{
import std.algorithm.iteration : reduce;
import std.exception : enforce;
@@ -2267,9 +3077,10 @@ body
assert(false);
}
+///
@safe unittest
{
- auto rnd = Random(unpredictableSeed);
+ auto rnd = Xorshift(123_456_789);
auto i = dice(rnd, 0.0, 100.0);
assert(i == 1);
i = dice(rnd, 100.0, 0.0);
@@ -2279,62 +3090,131 @@ body
assert(i == 0);
}
-/**
-Covers a given range $(D r) in a random manner, i.e. goes through each
-element of $(D r) once and only once, just in a random order. $(D r)
-must be a random-access range with length.
+/+ @nogc bool array designed for RandomCover.
+- constructed with an invariable length
+- small length means 0 alloc and bit field (if up to 32(x86) or 64(x64) choices to cover)
+- bigger length means non-GC heap allocation(s) and dealloc. +/
+private struct RandomCoverChoices
+{
+ private size_t* buffer;
+ private immutable size_t _length;
+ private immutable bool hasPackedBits;
+ private enum BITS_PER_WORD = typeof(buffer[0]).sizeof * 8;
-If no random number generator is passed to $(D randomCover), the
-thread-global RNG rndGen will be used internally.
+ void opAssign(T)(T) @disable;
-Params:
- r = random-access range to cover
- rng = (optional) random number generator to use;
- if not specified, defaults to $(D rndGen)
+ this(this) pure nothrow @nogc @trusted
+ {
+ import core.stdc.string : memcpy;
+ import std.internal.memory : enforceMalloc;
-Returns:
- Range whose elements consist of the elements of $(D r),
- in random order. Will be a forward range if both $(D r) and
- $(D rng) are forward ranges, an input range otherwise.
+ if (!hasPackedBits && buffer !is null)
+ {
+ const nBytesToAlloc = size_t.sizeof * (_length / BITS_PER_WORD + int(_length % BITS_PER_WORD != 0));
+ void* nbuffer = enforceMalloc(nBytesToAlloc);
+ buffer = cast(size_t*) memcpy(nbuffer, buffer, nBytesToAlloc);
+ }
+ }
-Example:
-----
-int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
-foreach (e; randomCover(a))
-{
- writeln(e);
-}
-----
+ this(size_t numChoices) pure nothrow @nogc @trusted
+ {
+ import std.internal.memory : enforceCalloc;
+
+ _length = numChoices;
+ hasPackedBits = _length <= size_t.sizeof * 8;
+ if (!hasPackedBits)
+ {
+ const nWordsToAlloc = _length / BITS_PER_WORD + int(_length % BITS_PER_WORD != 0);
+ buffer = cast(size_t*) enforceCalloc(nWordsToAlloc, BITS_PER_WORD / 8);
+ }
+ }
-$(B WARNING:) If an alternative RNG is desired, it is essential for this
-to be a $(I new) RNG seeded in an unpredictable manner. Passing it a RNG
-used elsewhere in the program will result in unintended correlations,
-due to the current implementation of RNGs as value types.
+ size_t length() const pure nothrow @nogc @safe @property {return _length;}
-Example:
-----
-int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
-foreach (e; randomCover(a, Random(unpredictableSeed))) // correct!
-{
- writeln(e);
+ ~this() pure nothrow @nogc @trusted
+ {
+ import core.memory : pureFree;
+
+ if (!hasPackedBits && buffer !is null)
+ pureFree(buffer);
+ }
+
+ bool opIndex(size_t index) const pure nothrow @nogc @trusted
+ {
+ assert(index < _length);
+ import core.bitop : bt;
+ if (!hasPackedBits)
+ return cast(bool) bt(buffer, index);
+ else
+ return ((cast(size_t) buffer) >> index) & size_t(1);
+ }
+
+ void opIndexAssign(bool value, size_t index) pure nothrow @nogc @trusted
+ {
+ assert(index < _length);
+ if (!hasPackedBits)
+ {
+ import core.bitop : btr, bts;
+ if (value)
+ bts(buffer, index);
+ else
+ btr(buffer, index);
+ }
+ else
+ {
+ if (value)
+ (*cast(size_t*) &buffer) |= size_t(1) << index;
+ else
+ (*cast(size_t*) &buffer) &= ~(size_t(1) << index);
+ }
+ }
}
-foreach (e; randomCover(a, rndGen)) // DANGEROUS!! rndGen gets copied by value
+@safe @nogc nothrow unittest
{
- writeln(e);
+ static immutable lengths = [3, 32, 65, 256];
+ foreach (length; lengths)
+ {
+ RandomCoverChoices c = RandomCoverChoices(length);
+ assert(c.hasPackedBits == (length <= size_t.sizeof * 8));
+ c[0] = true;
+ c[2] = true;
+ assert(c[0]);
+ assert(!c[1]);
+ assert(c[2]);
+ c[0] = false;
+ c[1] = true;
+ c[2] = false;
+ assert(!c[0]);
+ assert(c[1]);
+ assert(!c[2]);
+ }
}
-foreach (e; randomCover(a, rndGen)) // ... so this second random cover
-{ // will output the same sequence as
- writeln(e); // the previous one.
-}
-----
- */
+/**
+Covers a given range `r` in a random manner, i.e. goes through each
+element of `r` once and only once, just in a random order. `r`
+must be a random-access range with length.
+
+If no random number generator is passed to `randomCover`, the
+thread-global RNG rndGen will be used internally.
+
+Params:
+ r = random-access range to cover
+ rng = (optional) random number generator to use;
+ if not specified, defaults to `rndGen`
+
+Returns:
+ Range whose elements consist of the elements of `r`,
+ in random order. Will be a forward range if both `r` and
+ `rng` are forward ranges, an
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives) otherwise.
+*/
struct RandomCover(Range, UniformRNG = void)
if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void)))
{
private Range _input;
- private bool[] _chosen;
+ private RandomCoverChoices _chosen;
private size_t _current;
private size_t _alreadyChosen = 0;
private bool _isEmpty = false;
@@ -2344,14 +3224,14 @@ if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == vo
this(Range input)
{
_input = input;
- _chosen.length = _input.length;
+ _chosen = RandomCoverChoices(_input.length);
if (_input.empty)
{
_isEmpty = true;
}
else
{
- _current = uniform(0, _chosen.length);
+ _current = _uniformIndex(_chosen.length, rndGen);
}
}
}
@@ -2363,14 +3243,14 @@ if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == vo
{
_input = input;
_rng = rng;
- _chosen.length = _input.length;
+ _chosen = RandomCoverChoices(_input.length);
if (_input.empty)
{
_isEmpty = true;
}
else
{
- _current = uniform(0, _chosen.length, rng);
+ _current = _uniformIndex(_chosen.length, rng);
}
}
@@ -2413,11 +3293,11 @@ if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == vo
// Roll a dice with k faces
static if (is(UniformRNG == void))
{
- auto chooseMe = uniform(0, k) == 0;
+ auto chooseMe = _uniformIndex(k, rndGen) == 0;
}
else
{
- auto chooseMe = uniform(0, k, _rng) == 0;
+ auto chooseMe = _uniformIndex(k, _rng) == 0;
}
assert(k > 1 || chooseMe);
if (chooseMe)
@@ -2443,7 +3323,7 @@ if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == vo
}
}
- @property bool empty() { return _isEmpty; }
+ @property bool empty() const { return _isEmpty; }
}
/// Ditto
@@ -2460,14 +3340,48 @@ if (isRandomAccessRange!Range)
return RandomCover!(Range, void)(r);
}
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+ auto rnd = MinstdRand0(42);
+
+ version (X86_64) // https://issues.dlang.org/show_bug.cgi?id=15147
+ assert(10.iota.randomCover(rnd).equal([7, 4, 2, 0, 1, 6, 8, 3, 9, 5]));
+}
+
+@safe unittest // cover RandomCoverChoices postblit for heap storage
+{
+ import std.array : array;
+ import std.range : iota;
+ auto a = 1337.iota.randomCover().array;
+ assert(a.length == 1337);
+}
+
+@nogc nothrow pure @safe unittest
+{
+ // Optionally @nogc std.random.randomCover
+ // https://issues.dlang.org/show_bug.cgi?id=14001
+ auto rng = Xorshift(123_456_789);
+ static immutable int[] sa = [1, 2, 3, 4, 5];
+ auto r = randomCover(sa, rng);
+ assert(!r.empty);
+ const x = r.front;
+ r.popFront();
+ assert(!r.empty);
+ const y = r.front;
+ assert(x != y);
+}
+
@safe unittest
{
import std.algorithm;
import std.conv;
int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
int[] c;
- foreach (UniformRNG; std.meta.AliasSeq!(void, PseudoRngTypes))
- {
+ static foreach (UniformRNG; std.meta.AliasSeq!(void, PseudoRngTypes))
+ {{
static if (is(UniformRNG == void))
{
auto rc = randomCover(a);
@@ -2476,11 +3390,11 @@ if (isRandomAccessRange!Range)
}
else
{
- auto rng = UniformRNG(unpredictableSeed);
+ auto rng = UniformRNG(123_456_789);
auto rc = randomCover(a, rng);
static assert(isForwardRange!(typeof(rc)));
// check for constructor passed a value-type RNG
- auto rc2 = RandomCover!(int[], UniformRNG)(a, UniformRNG(unpredictableSeed));
+ auto rc2 = RandomCover!(int[], UniformRNG)(a, UniformRNG(987_654_321));
static assert(isForwardRange!(typeof(rc2)));
auto rcEmpty = randomCover(c, rng);
assert(rcEmpty.length == 0);
@@ -2495,18 +3409,18 @@ if (isRandomAccessRange!Range)
}
sort(b);
assert(a == b, text(b));
- }
+ }}
}
@safe unittest
{
- // Bugzilla 12589
+ // https://issues.dlang.org/show_bug.cgi?id=12589
int[] r = [];
auto rc = randomCover(r);
assert(rc.length == 0);
assert(rc.empty);
- // Bugzilla 16724
+ // https://issues.dlang.org/show_bug.cgi?id=16724
import std.range : iota;
auto range = iota(10);
auto randy = range.randomCover;
@@ -2520,84 +3434,50 @@ if (isRandomAccessRange!Range)
// RandomSample
/**
-Selects a random subsample out of $(D r), containing exactly $(D n)
+Selects a random subsample out of `r`, containing exactly `n`
elements. The order of elements is the same as in the original
-range. The total length of $(D r) must be known. If $(D total) is
+range. The total length of `r` must be known. If `total` is
passed in, the total number of sample is considered to be $(D
-total). Otherwise, $(D RandomSample) uses $(D r.length).
+total). Otherwise, `RandomSample` uses `r.length`.
Params:
r = range to sample from
n = number of elements to include in the sample;
must be less than or equal to the total number
- of elements in $(D r) and/or the parameter
- $(D total) (if provided)
- total = (semi-optional) number of elements of $(D r)
+ of elements in `r` and/or the parameter
+ `total` (if provided)
+ total = (semi-optional) number of elements of `r`
from which to select the sample (counting from
the beginning); must be less than or equal to
- the total number of elements in $(D r) itself.
- May be omitted if $(D r) has the $(D .length)
+ the total number of elements in `r` itself.
+ May be omitted if `r` has the `.length`
property and the sample is to be drawn from
- all elements of $(D r).
+ all elements of `r`.
rng = (optional) random number generator to use;
- if not specified, defaults to $(D rndGen)
+ if not specified, defaults to `rndGen`
Returns:
Range whose elements consist of a randomly selected subset of
- the elements of $(D r), in the same order as these elements
- appear in $(D r) itself. Will be a forward range if both $(D r)
- and $(D rng) are forward ranges, an input range otherwise.
+ the elements of `r`, in the same order as these elements
+ appear in `r` itself. Will be a forward range if both `r`
+ and `rng` are forward ranges, an input range otherwise.
-$(D RandomSample) implements Jeffrey Scott Vitter's Algorithm D
+`RandomSample` implements Jeffrey Scott Vitter's Algorithm D
(see Vitter $(HTTP dx.doi.org/10.1145/358105.893, 1984), $(HTTP
dx.doi.org/10.1145/23002.23003, 1987)), which selects a sample
-of size $(D n) in O(n) steps and requiring O(n) random variates,
+of size `n` in O(n) steps and requiring O(n) random variates,
regardless of the size of the data being sampled. The exception
to this is if traversing k elements on the input range is itself
an O(k) operation (e.g. when sampling lines from an input file),
in which case the sampling calculation will inevitably be of
O(total).
-RandomSample will throw an exception if $(D total) is verifiably
+RandomSample will throw an exception if `total` is verifiably
less than the total number of elements available in the input,
or if $(D n > total).
-If no random number generator is passed to $(D randomSample), the
+If no random number generator is passed to `randomSample`, the
thread-global RNG rndGen will be used internally.
-
-Example:
-----
-int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
-// Print 5 random elements picked off from a
-foreach (e; randomSample(a, 5))
-{
- writeln(e);
-}
-----
-
-$(B WARNING:) If an alternative RNG is desired, it is essential for this
-to be a $(I new) RNG seeded in an unpredictable manner. Passing it a RNG
-used elsewhere in the program will result in unintended correlations,
-due to the current implementation of RNGs as value types.
-
-Example:
-----
-int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
-foreach (e; randomSample(a, 5, Random(unpredictableSeed))) // correct!
-{
- writeln(e);
-}
-
-foreach (e; randomSample(a, 5, rndGen)) // DANGEROUS!! rndGen gets
-{ // copied by value
- writeln(e);
-}
-
-foreach (e; randomSample(a, 5, rndGen)) // ... so this second random
-{ // sample will select the same
- writeln(e); // values as the previous one.
-}
-----
*/
struct RandomSample(Range, UniformRNG = void)
if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void)))
@@ -2637,7 +3517,7 @@ if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void)))
static if (hasLength!Range)
{
- this(Range input, size_t howMany, ref UniformRNG rng)
+ this(Range input, size_t howMany, ref scope UniformRNG rng)
{
_rng = rng;
_input = input;
@@ -2650,7 +3530,7 @@ if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void)))
}
}
- this(Range input, size_t howMany, size_t total, ref UniformRNG rng)
+ this(Range input, size_t howMany, size_t total, ref scope UniformRNG rng)
{
_rng = rng;
_input = input;
@@ -2741,17 +3621,36 @@ if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void)))
/// Ditto
static if (isForwardRange!Range && isForwardRange!UniformRNG)
{
- @property typeof(this) save()
+ static if (is(typeof(((const UniformRNG* p) => (*p).save)(null)) : UniformRNG)
+ && is(typeof(((const Range* p) => (*p).save)(null)) : Range))
{
- auto ret = this;
- ret._input = _input.save;
- ret._rng = _rng.save;
- return ret;
+ @property typeof(this) save() const
+ {
+ auto ret = RandomSample.init;
+ foreach (fieldIndex, ref val; this.tupleof)
+ {
+ static if (is(typeof(val) == const(Range)) || is(typeof(val) == const(UniformRNG)))
+ ret.tupleof[fieldIndex] = val.save;
+ else
+ ret.tupleof[fieldIndex] = val;
+ }
+ return ret;
+ }
+ }
+ else
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ ret._rng = _rng.save;
+ return ret;
+ }
}
}
/// Ditto
- @property size_t length()
+ @property size_t length() const
{
return _toSelect;
}
@@ -2871,7 +3770,8 @@ Variable names are chosen to match those in Vitter's paper.
*/
private size_t skipD()
{
- import std.math : isNaN, trunc;
+ import std.math.traits : isNaN;
+ import std.math.rounding : trunc;
// Confirm that the check in Step D1 is valid and we
// haven't been sent here by mistake
assert((_alphaInverse * _toSelect) <= _available);
@@ -3014,6 +3914,15 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
return RandomSample!(Range, UniformRNG)(r, n, r.length, rng);
}
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+ auto rnd = MinstdRand0(42);
+ assert(10.iota.randomSample(3, rnd).equal([7, 8, 9]));
+}
+
@system unittest
{
// @system because it takes the address of a local
@@ -3031,10 +3940,11 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
static assert(isInputRange!TestInputRange);
static assert(!isForwardRange!TestInputRange);
- int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
+ const(int)[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
foreach (UniformRNG; PseudoRngTypes)
- {
+ (){ // avoid workaround optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
auto rng = UniformRNG(1234);
/* First test the most general case: randomSample of input range, with and
* without a specified random number generator.
@@ -3047,7 +3957,7 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
{
auto sample =
RandomSample!(TestInputRange, UniformRNG)
- (TestInputRange(), 5, 10, UniformRNG(unpredictableSeed));
+ (TestInputRange(), 5, 10, UniformRNG(987_654_321));
static assert(isInputRange!(typeof(sample)));
static assert(!isForwardRange!(typeof(sample)));
}
@@ -3063,7 +3973,7 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
{
auto sample =
RandomSample!(typeof(TestInputRange().takeExactly(10)), UniformRNG)
- (TestInputRange().takeExactly(10), 5, 10, UniformRNG(unpredictableSeed));
+ (TestInputRange().takeExactly(10), 5, 10, UniformRNG(654_321_987));
static assert(isInputRange!(typeof(sample)));
static assert(!isForwardRange!(typeof(sample)));
}
@@ -3076,8 +3986,8 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
// ... and test with range initialized directly
{
auto sample =
- RandomSample!(int[], UniformRNG)
- (a, 5, UniformRNG(unpredictableSeed));
+ RandomSample!(const(int)[], UniformRNG)
+ (a, 5, UniformRNG(321_987_654));
static assert(isForwardRange!(typeof(sample)));
}
}
@@ -3088,8 +3998,8 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
// ... and test with range initialized directly
{
auto sample =
- RandomSample!(int[], UniformRNG)
- (a, 5, UniformRNG(unpredictableSeed));
+ RandomSample!(const(int)[], UniformRNG)
+ (a, 5, UniformRNG(789_123_456));
static assert(isInputRange!(typeof(sample)));
static assert(!isForwardRange!(typeof(sample)));
}
@@ -3218,7 +4128,7 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
}
/* Check that it also works if .index is called before .front.
- * See: http://d.puremagic.com/issues/show_bug.cgi?id=10322
+ * See: https://issues.dlang.org/show_bug.cgi?id=10322
*/
auto sample3 = randomSample(TestInputRange(), 654, 654_321);
for (; !sample3.empty; sample3.popFront())
@@ -3242,7 +4152,7 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
*/
{
size_t count0, count1, count99;
- foreach (_; 0 .. 100_000)
+ foreach (_; 0 .. 50_000)
{
auto sample = randomSample(iota(100), 5, &rng);
sample.popFront();
@@ -3277,9 +4187,9 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
* the variance can be quite high.
*/
assert(count0 == 0);
- assert(count1 < 300, text("1: ", count1, " > 300."));
- assert(4_700 < count99, text("99: ", count99, " < 4700."));
- assert(count99 < 5_300, text("99: ", count99, " > 5300."));
+ assert(count1 < 150, text("1: ", count1, " > 150."));
+ assert(2_200 < count99, text("99: ", count99, " < 2200."));
+ assert(count99 < 2_800, text("99: ", count99, " > 2800."));
}
/* Odd corner-cases: RandomSample has 2 constructors that are not called
@@ -3326,11 +4236,12 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
static if (isForwardRange!UniformRNG)
{
auto sample1 = randomSample(a, 5, rng);
- auto sample2 = sample1.save;
+ // https://issues.dlang.org/show_bug.cgi?id=15853
+ auto sample2 = ((const ref typeof(sample1) a) => a.save)(sample1);
assert(sample1.array() == sample2.array());
}
- // Bugzilla 8314
+ // https://issues.dlang.org/show_bug.cgi?id=8314
{
auto sample(RandomGen)(uint seed) { return randomSample(a, 1, RandomGen(seed)).front; }
@@ -3340,5 +4251,5 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
while (sample!UniformRNG(++n) == fst && n < n.max) {}
assert(n < n.max);
}
- }
+ }();
}
diff --git a/libphobos/src/std/range/interfaces.d b/libphobos/src/std/range/interfaces.d
index 7207776cabc..4f8eba73278 100644
--- a/libphobos/src/std/range/interfaces.d
+++ b/libphobos/src/std/range/interfaces.d
@@ -4,10 +4,11 @@ This module is a submodule of $(MREF std, range).
The main $(MREF std, range) module provides template-based tools for working with
ranges, but sometimes an object-based interface for ranges is needed, such as
when runtime polymorphism is required. For this purpose, this submodule
-provides a number of object and $(D interface) definitions that can be used to
-wrap around _range objects created by the $(MREF std, range) templates.
+provides a number of object and `interface` definitions that can be used to
+wrap around range objects created by the $(MREF std, range) templates.
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE ,
$(TR $(TD $(LREF InputRange))
$(TD Wrapper for input ranges.
@@ -40,33 +41,35 @@ $(BOOKTABLE ,
$(TD Wrapper for output ranges.
))
$(TR $(TD $(LREF OutputRangeObject))
- $(TD Class that implements the $(D OutputRange) interface and wraps the
- $(D put) methods in virtual functions.
+ $(TD Class that implements the `OutputRange` interface and wraps the
+ `put` methods in virtual functions.
+ ))
$(TR $(TD $(LREF outputRangeObject))
- Convenience function for creating an $(D OutputRangeObject) with a base
+ $(TD Convenience function for creating an `OutputRangeObject` with a base
range of type R that accepts types E.
))
$(TR $(TD $(LREF InputRangeObject))
- $(TD Class that implements the $(D InputRange) interface and wraps the
- input _range methods in virtual functions.
+ $(TD Class that implements the `InputRange` interface and wraps the
+ input range methods in virtual functions.
))
$(TR $(TD $(LREF inputRangeObject))
- $(TD Convenience function for creating an $(D InputRangeObject)
+ $(TD Convenience function for creating an `InputRangeObject`
of the proper type.
))
$(TR $(TD $(LREF MostDerivedInputRange))
- $(TD Returns the interface type that best matches the range.)
+ $(TD Returns the interface type that best matches the range.
))
-)
+))
-Source: $(PHOBOSSRC std/range/_interfaces.d)
+Source: $(PHOBOSSRC std/range/interfaces.d)
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha,
-and Jonathan M Davis. Credit for some of the ideas in building this module goes
-to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
+Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and
+ $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas
+ in building this module goes to
+ $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
*/
module std.range.interfaces;
@@ -80,11 +83,11 @@ import std.traits;
* needs to accept a generic range as a parameter. Note that
* $(REF_ALTTEXT isInputRange, isInputRange, std, range, primitives)
* and friends check for conformance to structural interfaces
- * not for implementation of these $(D interface) types.
+ * not for implementation of these `interface` types.
*
* Limitations:
*
- * These interfaces are not capable of forwarding $(D ref) access to elements.
+ * These interfaces are not capable of forwarding `ref` access to elements.
*
* Infiniteness of the wrapped range is not propagated.
*
@@ -115,7 +118,7 @@ interface InputRange(E) {
* InputRangeObject, range primitives: 877 milliseconds (3.15x penalty)
*/
- /**$(D foreach) iteration uses opApply, since one delegate call per loop
+ /**`foreach` iteration uses opApply, since one delegate call per loop
* iteration is faster than three virtual function calls.
*/
int opApply(scope int delegate(E));
@@ -145,13 +148,13 @@ interface InputRange(E) {
useRange(squaresWrapped);
}
-/**Interface for a forward range of type $(D E).*/
+/**Interface for a forward range of type `E`.*/
interface ForwardRange(E) : InputRange!E {
///
@property ForwardRange!E save();
}
-/**Interface for a bidirectional range of type $(D E).*/
+/**Interface for a bidirectional range of type `E`.*/
interface BidirectionalRange(E) : ForwardRange!(E) {
///
@property BidirectionalRange!E save();
@@ -166,7 +169,7 @@ interface BidirectionalRange(E) : ForwardRange!(E) {
void popBack();
}
-/**Interface for a finite random access range of type $(D E).*/
+/**Interface for a finite random access range of type `E`.*/
interface RandomAccessFinite(E) : BidirectionalRange!(E) {
///
@property RandomAccessFinite!E save();
@@ -192,7 +195,7 @@ interface RandomAccessFinite(E) : BidirectionalRange!(E) {
}
}
-/**Interface for an infinite random access range of type $(D E).*/
+/**Interface for an infinite random access range of type `E`.*/
interface RandomAccessInfinite(E) : ForwardRange!E {
///
E moveAt(size_t);
@@ -241,16 +244,16 @@ interface RandomFiniteAssignable(E) : RandomAccessFinite!E, BidirectionalAssigna
void opIndexAssign(E val, size_t index);
}
-/**Interface for an output range of type $(D E). Usage is similar to the
- * $(D InputRange) interface and descendants.*/
+/**Interface for an output range of type `E`. Usage is similar to the
+ * `InputRange` interface and descendants.*/
interface OutputRange(E) {
///
void put(E);
}
+// https://issues.dlang.org/show_bug.cgi?id=6973
@safe unittest
{
- // 6973
static assert(isOutputRange!(OutputRange!int, int));
}
@@ -271,8 +274,8 @@ private string putMethods(E...)()
return ret;
}
-/**Implements the $(D OutputRange) interface for all types E and wraps the
- * $(D put) method for each type $(D E) in a virtual function.
+/**Implements the `OutputRange` interface for all types E and wraps the
+ * `put` method for each type `E` in a virtual function.
*/
class OutputRangeObject(R, E...) : staticMap!(OutputRange, E) {
// @BUG 4689: There should be constraints on this template class, but
@@ -288,7 +291,7 @@ class OutputRangeObject(R, E...) : staticMap!(OutputRange, E) {
}
-/**Returns the interface type that best matches $(D R).*/
+/**Returns the interface type that best matches `R`.*/
template MostDerivedInputRange(R)
if (isInputRange!(Unqual!R))
{
@@ -344,9 +347,9 @@ if (isInputRange!(Unqual!R))
}
}
-/**Implements the most derived interface that $(D R) works with and wraps
- * all relevant range primitives in virtual functions. If $(D R) is already
- * derived from the $(D InputRange) interface, aliases itself away.
+/**Implements the most derived interface that `R` works with and wraps
+ * all relevant range primitives in virtual functions. If `R` is already
+ * derived from the `InputRange` interface, aliases itself away.
*/
template InputRangeObject(R)
if (isInputRange!(Unqual!R))
@@ -480,7 +483,7 @@ if (isInputRange!(Unqual!R))
}
}
-/**Convenience function for creating an $(D InputRangeObject) of the proper type.
+/**Convenience function for creating an `InputRangeObject` of the proper type.
* See $(LREF InputRange) for an example.
*/
InputRangeObject!R inputRangeObject(R)(R range)
@@ -496,8 +499,8 @@ if (isInputRange!R)
}
}
-/**Convenience function for creating an $(D OutputRangeObject) with a base range
- * of type $(D R) that accepts types $(D E).
+/**Convenience function for creating an `OutputRangeObject` with a base range
+ * of type `R` that accepts types `E`.
*/
template outputRangeObject(E...) {
diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d
index deedb689974..86bd4a1dd19 100644
--- a/libphobos/src/std/range/package.d
+++ b/libphobos/src/std/range/package.d
@@ -14,7 +14,7 @@ Guides:
There are many articles available that can bolster understanding ranges:
$(UL
- $(LI Ali Çehreli's $(HTTP ddili.org/ders/d.en/ranges.html, tutorial on _ranges)
+ $(LI Ali Çehreli's $(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges)
for the basics of working with and creating range-based code.)
$(LI Jonathan M. Davis $(LINK2 http://dconf.org/2015/talks/davis.html, $(I Introduction to Ranges))
talk at DConf 2015 a vivid introduction from its core constructs to practical advice.)
@@ -22,7 +22,7 @@ $(UL
for an interactive introduction.)
$(LI H. S. Teoh's $(LINK2 http://wiki.dlang.org/Component_programming_with_ranges, tutorial on
component programming with ranges) for a real-world showcase of the influence
- of _range-based programming on complex algorithms.)
+ of range-based programming on complex algorithms.)
$(LI Andrei Alexandrescu's article
$(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357$(AMP)rll=1,
$(I On Iteration)) for conceptual aspect of ranges and the motivation
@@ -33,23 +33,24 @@ Submodules:
This module has two submodules:
-The $(MREF std, _range, primitives) submodule
-provides basic _range functionality. It defines several templates for testing
-whether a given object is a _range, what kind of _range it is, and provides
-some common _range operations.
+The $(MREF std, range, primitives) submodule
+provides basic range functionality. It defines several templates for testing
+whether a given object is a range, what kind of range it is, and provides
+some common range operations.
-The $(MREF std, _range, interfaces) submodule
+The $(MREF std, range, interfaces) submodule
provides object-based interfaces for working with ranges via runtime
polymorphism.
-The remainder of this module provides a rich set of _range creation and
+The remainder of this module provides a rich set of range creation and
composition templates that let you construct new ranges out of existing ranges:
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE ,
$(TR $(TD $(LREF chain))
- $(TD Concatenates several ranges into a single _range.
+ $(TD Concatenates several ranges into a single range.
))
$(TR $(TD $(LREF choose))
$(TD Chooses one of two ranges at runtime based on a boolean condition.
@@ -58,174 +59,173 @@ $(BOOKTABLE ,
$(TD Chooses one of several ranges at runtime based on an index.
))
$(TR $(TD $(LREF chunks))
- $(TD Creates a _range that returns fixed-size chunks of the original
- _range.
+ $(TD Creates a range that returns fixed-size chunks of the original
+ range.
))
$(TR $(TD $(LREF cycle))
- $(TD Creates an infinite _range that repeats the given forward _range
+ $(TD Creates an infinite range that repeats the given forward range
indefinitely. Good for implementing circular buffers.
))
$(TR $(TD $(LREF drop))
- $(TD Creates the _range that results from discarding the first $(I n)
- elements from the given _range.
+ $(TD Creates the range that results from discarding the first $(I n)
+ elements from the given range.
))
$(TR $(TD $(LREF dropBack))
- $(TD Creates the _range that results from discarding the last $(I n)
- elements from the given _range.
+ $(TD Creates the range that results from discarding the last $(I n)
+ elements from the given range.
))
$(TR $(TD $(LREF dropExactly))
- $(TD Creates the _range that results from discarding exactly $(I n)
- of the first elements from the given _range.
+ $(TD Creates the range that results from discarding exactly $(I n)
+ of the first elements from the given range.
))
$(TR $(TD $(LREF dropBackExactly))
- $(TD Creates the _range that results from discarding exactly $(I n)
- of the last elements from the given _range.
+ $(TD Creates the range that results from discarding exactly $(I n)
+ of the last elements from the given range.
))
$(TR $(TD $(LREF dropOne))
- $(TD Creates the _range that results from discarding
- the first element from the given _range.
+ $(TD Creates the range that results from discarding
+ the first element from the given range.
))
$(TR $(TD $(D $(LREF dropBackOne)))
- $(TD Creates the _range that results from discarding
- the last element from the given _range.
+ $(TD Creates the range that results from discarding
+ the last element from the given range.
))
$(TR $(TD $(LREF enumerate))
- $(TD Iterates a _range with an attached index variable.
+ $(TD Iterates a range with an attached index variable.
))
$(TR $(TD $(LREF evenChunks))
- $(TD Creates a _range that returns a number of chunks of
- approximately equal length from the original _range.
+ $(TD Creates a range that returns a number of chunks of
+ approximately equal length from the original range.
))
$(TR $(TD $(LREF frontTransversal))
- $(TD Creates a _range that iterates over the first elements of the
+ $(TD Creates a range that iterates over the first elements of the
given ranges.
))
$(TR $(TD $(LREF generate))
- $(TD Creates a _range by successive calls to a given function. This
+ $(TD Creates a range by successive calls to a given function. This
allows to create ranges as a single delegate.
))
$(TR $(TD $(LREF indexed))
- $(TD Creates a _range that offers a view of a given _range as though
- its elements were reordered according to a given _range of indices.
+ $(TD Creates a range that offers a view of a given range as though
+ its elements were reordered according to a given range of indices.
))
$(TR $(TD $(LREF iota))
- $(TD Creates a _range consisting of numbers between a starting point
+ $(TD Creates a range consisting of numbers between a starting point
and ending point, spaced apart by a given interval.
))
$(TR $(TD $(LREF lockstep))
- $(TD Iterates $(I n) _ranges in lockstep, for use in a $(D foreach)
- loop. Similar to $(D zip), except that $(D lockstep) is designed
- especially for $(D foreach) loops.
+ $(TD Iterates $(I n) ranges in lockstep, for use in a `foreach`
+ loop. Similar to `zip`, except that `lockstep` is designed
+ especially for `foreach` loops.
))
- $(TR $(TD $(LREF NullSink))
- $(TD An output _range that discards the data it receives.
+ $(TR $(TD $(LREF nullSink))
+ $(TD An output range that discards the data it receives.
))
$(TR $(TD $(LREF only))
- $(TD Creates a _range that iterates over the given arguments.
+ $(TD Creates a range that iterates over the given arguments.
))
$(TR $(TD $(LREF padLeft))
- $(TD Pads a _range to a specified length by adding a given element to
- the front of the _range. Is lazy if the _range has a known length.
+ $(TD Pads a range to a specified length by adding a given element to
+ the front of the range. Is lazy if the range has a known length.
))
$(TR $(TD $(LREF padRight))
- $(TD Lazily pads a _range to a specified length by adding a given element to
- the back of the _range.
+ $(TD Lazily pads a range to a specified length by adding a given element to
+ the back of the range.
))
$(TR $(TD $(LREF radial))
- $(TD Given a random-access _range and a starting point, creates a
- _range that alternately returns the next left and next right element to
+ $(TD Given a random-access range and a starting point, creates a
+ range that alternately returns the next left and next right element to
the starting point.
))
$(TR $(TD $(LREF recurrence))
- $(TD Creates a forward _range whose values are defined by a
+ $(TD Creates a forward range whose values are defined by a
mathematical recurrence relation.
))
$(TR $(TD $(LREF refRange))
- $(TD Pass a _range by reference. Both the original _range and the RefRange
+ $(TD Pass a range by reference. Both the original range and the RefRange
will always have the exact same elements.
Any operation done on one will affect the other.
))
$(TR $(TD $(LREF repeat))
- $(TD Creates a _range that consists of a single element repeated $(I n)
- times, or an infinite _range repeating that element indefinitely.
+ $(TD Creates a range that consists of a single element repeated $(I n)
+ times, or an infinite range repeating that element indefinitely.
))
$(TR $(TD $(LREF retro))
- $(TD Iterates a bidirectional _range backwards.
+ $(TD Iterates a bidirectional range backwards.
))
$(TR $(TD $(LREF roundRobin))
- $(TD Given $(I n) ranges, creates a new _range that return the $(I n)
- first elements of each _range, in turn, then the second element of each
- _range, and so on, in a round-robin fashion.
+ $(TD Given $(I n) ranges, creates a new range that return the $(I n)
+ first elements of each range, in turn, then the second element of each
+ range, and so on, in a round-robin fashion.
))
$(TR $(TD $(LREF sequence))
- $(TD Similar to $(D recurrence), except that a random-access _range is
+ $(TD Similar to `recurrence`, except that a random-access range is
created.
))
- $(COMMENT Explicitly undocumented to delay the release until 2.076
$(TR $(TD $(D $(LREF slide)))
- $(TD Creates a _range that returns a fixed-size sliding window
- over the original _range. Unlike chunks,
+ $(TD Creates a range that returns a fixed-size sliding window
+ over the original range. Unlike chunks,
it advances a configurable number of items at a time,
not one chunk at a time.
))
- )
$(TR $(TD $(LREF stride))
- $(TD Iterates a _range with stride $(I n).
+ $(TD Iterates a range with stride $(I n).
))
$(TR $(TD $(LREF tail))
- $(TD Return a _range advanced to within $(D n) elements of the end of
- the given _range.
+ $(TD Return a range advanced to within `n` elements of the end of
+ the given range.
))
$(TR $(TD $(LREF take))
- $(TD Creates a sub-_range consisting of only up to the first $(I n)
- elements of the given _range.
+ $(TD Creates a sub-range consisting of only up to the first $(I n)
+ elements of the given range.
))
$(TR $(TD $(LREF takeExactly))
- $(TD Like $(D take), but assumes the given _range actually has $(I n)
- elements, and therefore also defines the $(D length) property.
+ $(TD Like `take`, but assumes the given range actually has $(I n)
+ elements, and therefore also defines the `length` property.
))
$(TR $(TD $(LREF takeNone))
- $(TD Creates a random-access _range consisting of zero elements of the
- given _range.
+ $(TD Creates a random-access range consisting of zero elements of the
+ given range.
))
$(TR $(TD $(LREF takeOne))
- $(TD Creates a random-access _range consisting of exactly the first
- element of the given _range.
+ $(TD Creates a random-access range consisting of exactly the first
+ element of the given range.
))
$(TR $(TD $(LREF tee))
- $(TD Creates a _range that wraps a given _range, forwarding along
+ $(TD Creates a range that wraps a given range, forwarding along
its elements while also calling a provided function with each element.
))
$(TR $(TD $(LREF transposed))
- $(TD Transposes a _range of ranges.
+ $(TD Transposes a range of ranges.
))
$(TR $(TD $(LREF transversal))
- $(TD Creates a _range that iterates over the $(I n)'th elements of the
+ $(TD Creates a range that iterates over the $(I n)'th elements of the
given random-access ranges.
))
$(TR $(TD $(LREF zip))
- $(TD Given $(I n) _ranges, creates a _range that successively returns a
+ $(TD Given $(I n) ranges, creates a range that successively returns a
tuple of all the first elements, a tuple of all the second elements,
etc.
))
-)
+))
Sortedness:
Ranges whose elements are sorted afford better efficiency with certain
operations. For this, the $(LREF assumeSorted) function can be used to
-construct a $(LREF SortedRange) from a pre-sorted _range. The $(REF
+construct a $(LREF SortedRange) from a pre-sorted range. The $(REF
sort, std, algorithm, sorting) function also conveniently
returns a $(LREF SortedRange). $(LREF SortedRange) objects provide some additional
-_range operations that take advantage of the fact that the _range is sorted.
+range operations that take advantage of the fact that the range is sorted.
-Source: $(PHOBOSSRC std/_range/_package.d)
+Source: $(PHOBOSSRC std/range/package.d)
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, Jonathan M Davis,
-and Jack Stouffer. Credit for some of the ideas in building this module goes
-to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
+Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha,
+ $(HTTP jmdavisprog.com, Jonathan M Davis), and Jack Stouffer. Credit
+ for some of the ideas in building this module goes to
+ $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
*/
module std.range;
@@ -234,14 +234,15 @@ public import std.range.interfaces;
public import std.range.primitives;
public import std.typecons : Flag, Yes, No;
-import std.meta; // allSatisfy, staticMap
-import std.traits; // CommonType, isCallable, isFloatingPoint, isIntegral,
- // isPointer, isSomeFunction, isStaticArray, Unqual
+import std.internal.attributes : betterC;
+import std.meta : allSatisfy, staticMap;
+import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral,
+ isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf;
/**
Iterates a bidirectional range backwards. The original range can be
-accessed by using the $(D source) property. Applying retro twice to
+accessed by using the `source` property. Applying retro twice to
the same range yields the original range.
Params:
@@ -348,15 +349,7 @@ if (isBidirectionalRange!(Unqual!Range))
}
}
- static if (hasLength!R)
- {
- @property auto length()
- {
- return source.length;
- }
-
- alias opDollar = length;
- }
+ mixin ImplementLength!source;
}
return Result!()(r);
@@ -472,7 +465,7 @@ pure @safe nothrow @nogc unittest
assert(equal(r, excepted[]));
}
-// Issue 12662
+// https://issues.dlang.org/show_bug.cgi?id=12662
pure @safe nothrow @nogc unittest
{
int[3] src = [1,2,3];
@@ -483,14 +476,14 @@ pure @safe nothrow @nogc unittest
/**
-Iterates range $(D r) with stride $(D n). If the range is a
+Iterates range `r` with stride `n`. If the range is a
random-access range, moves by indexing into the range; otherwise,
-moves by successive calls to $(D popFront). Applying stride twice to
+moves by successive calls to `popFront`. Applying stride twice to
the same range results in a stride with a step that is the
-product of the two applications. It is an error for $(D n) to be 0.
+product of the two applications. It is an error for `n` to be 0.
Params:
- r = the input range to stride over
+ r = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to stride over
n = the number of elements to skip over
Returns:
@@ -504,7 +497,7 @@ in
{
assert(n != 0, "stride cannot have step zero.");
}
-body
+do
{
import std.algorithm.comparison : min;
@@ -663,12 +656,14 @@ body
static if (hasSlicing!R && hasLength!R)
typeof(this) opSlice(size_t lower, size_t upper)
{
- assert(upper >= lower && upper <= length);
+ assert(upper >= lower && upper <= length,
+ "Attempt to get out-of-bounds slice of `stride` range");
immutable translatedUpper = (upper == 0) ? 0 :
(upper * _n - (_n - 1));
immutable translatedLower = min(lower * _n, translatedUpper);
- assert(translatedLower <= translatedUpper);
+ assert(translatedLower <= translatedUpper,
+ "Overflow when calculating slice of `stride` range");
return typeof(this)(source[translatedLower .. translatedUpper], _n);
}
@@ -713,7 +708,7 @@ debug pure nothrow @system unittest
scope (success) assert(passed);
import core.exception : AssertError;
//std.exception.assertThrown won't do because it can't infer nothrow
- // @@@BUG@@@ 12647
+ // https://issues.dlang.org/show_bug.cgi?id=12647
try
{
auto unused = testArr[].stride(0);
@@ -764,7 +759,7 @@ pure @safe nothrow unittest
// assert(s2[$ .. $].empty);
assert(s2[s2.opDollar .. s2.opDollar].empty);
- // Test fix for Bug 5035
+ // Test fix for https://issues.dlang.org/show_bug.cgi?id=5035
auto m = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]; // 3 rows, 4 columns
auto col = stride(m, 4);
assert(equal(col, [1, 1, 1]));
@@ -870,19 +865,19 @@ pure @safe nothrow unittest
}
/**
-Spans multiple ranges in sequence. The function $(D chain) takes any
+Spans multiple ranges in sequence. The function `chain` takes any
number of ranges and returns a $(D Chain!(R1, R2,...)) object. The
ranges may be different, but they must have the same element type. The
-result is a range that offers the $(D front), $(D popFront), and $(D
+result is a range that offers the `front`, `popFront`, and $(D
empty) primitives. If all input ranges offer random access and $(D
-length), $(D Chain) offers them as well.
+length), `Chain` offers them as well.
-If only one range is offered to $(D Chain) or $(D chain), the $(D
+If only one range is offered to `Chain` or `chain`, the $(D
Chain) type exits the picture by aliasing itself directly to that
range's type.
Params:
- rs = the input ranges to chain together
+ rs = the $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) to chain together
Returns:
An input range at minimum. If all of the ranges in `rs` provide
@@ -913,16 +908,8 @@ if (Ranges.length > 0 &&
}
enum bool allSameType = allSatisfy!(sameET, R);
+ alias ElementType = RvalueElementType;
- // This doesn't work yet
- static if (allSameType)
- {
- alias ElementType = ref RvalueElementType;
- }
- else
- {
- alias ElementType = RvalueElementType;
- }
static if (allSameType && allSatisfy!(hasLvalueElements, R))
{
static ref RvalueElementType fixRef(ref RvalueElementType val)
@@ -945,7 +932,8 @@ if (Ranges.length > 0 &&
public:
this(R input)
{
- foreach (i, v; input)
+ // Must be static foreach because of https://issues.dlang.org/show_bug.cgi?id=21209
+ static foreach (i, v; input)
{
source[i] = v;
}
@@ -973,12 +961,21 @@ if (Ranges.length > 0 &&
static if (allSatisfy!(isForwardRange, R))
@property auto save()
{
- typeof(this) result = this;
- foreach (i, Unused; R)
+ auto saveSource(size_t len)()
{
- result.source[i] = result.source[i].save;
+ import std.typecons : tuple;
+ static assert(len > 0);
+ static if (len == 1)
+ {
+ return tuple(source[0].save);
+ }
+ else
+ {
+ return saveSource!(len - 1)() ~
+ tuple(source[len - 1].save);
+ }
}
- return result;
+ return Result(saveSource!(R.length).expand);
}
void popFront()
@@ -989,6 +986,7 @@ if (Ranges.length > 0 &&
source[i].popFront();
return;
}
+ assert(false, "Attempt to `popFront` of empty `chain` range");
}
@property auto ref front()
@@ -998,7 +996,7 @@ if (Ranges.length > 0 &&
if (source[i].empty) continue;
return fixRef(source[i].front);
}
- assert(false);
+ assert(false, "Attempt to get `front` of empty `chain` range");
}
static if (allSameType && allSatisfy!(hasAssignableElements, R))
@@ -1014,7 +1012,7 @@ if (Ranges.length > 0 &&
source[i].front = v;
return;
}
- assert(false);
+ assert(false, "Attempt to set `front` of empty `chain` range");
}
}
@@ -1027,7 +1025,7 @@ if (Ranges.length > 0 &&
if (source[i].empty) continue;
return source[i].moveFront();
}
- assert(false);
+ assert(false, "Attempt to `moveFront` of empty `chain` range");
}
}
@@ -1040,7 +1038,7 @@ if (Ranges.length > 0 &&
if (source[i].empty) continue;
return fixRef(source[i].back);
}
- assert(false);
+ assert(false, "Attempt to get `back` of empty `chain` range");
}
void popBack()
@@ -1051,6 +1049,7 @@ if (Ranges.length > 0 &&
source[i].popBack();
return;
}
+ assert(false, "Attempt to `popBack` of empty `chain` range");
}
static if (allSatisfy!(hasMobileElements, R))
@@ -1062,7 +1061,7 @@ if (Ranges.length > 0 &&
if (source[i].empty) continue;
return source[i].moveBack();
}
- assert(false);
+ assert(false, "Attempt to `moveBack` of empty `chain` range");
}
}
@@ -1076,7 +1075,7 @@ if (Ranges.length > 0 &&
source[i].back = v;
return;
}
- assert(false);
+ assert(false, "Attempt to set `back` of empty `chain` range");
}
}
}
@@ -1113,7 +1112,7 @@ if (Ranges.length > 0 &&
index -= length;
}
}
- assert(false);
+ assert(false, "Attempt to access out-of-bounds index of `chain` range");
}
static if (allSatisfy!(hasMobileElements, R))
@@ -1133,7 +1132,7 @@ if (Ranges.length > 0 &&
index -= length;
}
}
- assert(false);
+ assert(false, "Attempt to move out-of-bounds index of `chain` range");
}
}
@@ -1157,12 +1156,12 @@ if (Ranges.length > 0 &&
index -= length;
}
}
- assert(false);
+ assert(false, "Attempt to write out-of-bounds index of `chain` range");
}
}
static if (allSatisfy!(hasLength, R) && allSatisfy!(hasSlicing, R))
- auto opSlice(size_t begin, size_t end)
+ auto opSlice(size_t begin, size_t end) return scope
{
auto result = this;
foreach (i, Unused; R)
@@ -1242,6 +1241,38 @@ pure @safe nothrow unittest
assert(arr3.equal([7, 8, 9]));
}
+/**
+Due to safe type promotion in D, chaining together different
+character ranges results in a `uint` range.
+
+Use $(REF_ALTTEXT byChar, byChar,std,utf), $(REF_ALTTEXT byWchar, byWchar,std,utf),
+and $(REF_ALTTEXT byDchar, byDchar,std,utf) on the ranges
+to get the type you need.
+ */
+pure @safe nothrow unittest
+{
+ import std.utf : byChar, byCodeUnit;
+
+ auto s1 = "string one";
+ auto s2 = "string two";
+ // s1 and s2 front is dchar because of auto-decoding
+ static assert(is(typeof(s1.front) == dchar) && is(typeof(s2.front) == dchar));
+
+ auto r1 = s1.chain(s2);
+ // chains of ranges of the same character type give that same type
+ static assert(is(typeof(r1.front) == dchar));
+
+ auto s3 = "string three".byCodeUnit;
+ static assert(is(typeof(s3.front) == immutable char));
+ auto r2 = s1.chain(s3);
+ // chaining ranges of mixed character types gives `dchar`
+ static assert(is(typeof(r2.front) == dchar));
+
+ // use byChar on character ranges to correctly convert them to UTF-8
+ auto r3 = s1.byChar.chain(s3);
+ static assert(is(typeof(r3.front) == immutable char));
+}
+
pure @safe nothrow unittest
{
import std.algorithm.comparison : equal;
@@ -1281,7 +1312,8 @@ pure @safe nothrow unittest
assert(c.moveAt(5) == 1);
}
- // Make sure bug 3311 is fixed. ChainImpl should compile even if not all
+
+ // Make sure https://issues.dlang.org/show_bug.cgi?id=3311 is fixed.
// elements are mutable.
assert(equal(chain(iota(0, 3), iota(0, 3)), [0, 1, 2, 0, 1, 2]));
@@ -1301,7 +1333,8 @@ pure @safe nothrow unittest
// pair of DummyRange types, in either order.
foreach (DummyType1; AllDummyRanges)
- {
+ (){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
DummyType1 dummy1;
foreach (DummyType2; AllDummyRanges)
{
@@ -1338,7 +1371,7 @@ pure @safe nothrow unittest
static assert(!hasLvalueElements!(typeof(myChain)));
}
}
- }
+ }();
}
pure @safe nothrow @nogc unittest
@@ -1349,226 +1382,411 @@ pure @safe nothrow @nogc unittest
assert(chain(a, b).empty);
}
+// https://issues.dlang.org/show_bug.cgi?id=18657
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ string s = "foo";
+ auto r = refRange(&s).chain("bar");
+ assert(equal(r.save, "foobar"));
+ assert(equal(r, "foobar"));
+}
+
/**
Choose one of two ranges at runtime depending on a Boolean condition.
The ranges may be different, but they must have compatible element types (i.e.
-$(D CommonType) must exist for the two element types). The result is a range
-that offers the weakest capabilities of the two (e.g. $(D ForwardRange) if $(D
-R1) is a random-access range and $(D R2) is a forward range).
+`CommonType` must exist for the two element types). The result is a range
+that offers the weakest capabilities of the two (e.g. `ForwardRange` if $(D
+R1) is a random-access range and `R2` is a forward range).
Params:
- condition = which range to choose: $(D r1) if $(D true), $(D r2) otherwise
+ condition = which range to choose: `r1` if `true`, `r2` otherwise
r1 = the "true" range
r2 = the "false" range
Returns:
- A range type dependent on $(D R1) and $(D R2).
-
-Bugs:
- $(BUGZILLA 14660)
+ A range type dependent on `R1` and `R2`.
*/
-auto choose(R1, R2)(bool condition, R1 r1, R2 r2)
+auto choose(R1, R2)(bool condition, return scope R1 r1, return scope R2 r2)
if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) &&
!is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void))
{
- static struct Result
+ return ChooseResult!(R1, R2)(condition, r1, r2);
+}
+
+///
+@safe nothrow pure @nogc unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter, map;
+
+ auto data1 = only(1, 2, 3, 4).filter!(a => a != 3);
+ auto data2 = only(5, 6, 7, 8).map!(a => a + 1);
+
+ // choose() is primarily useful when you need to select one of two ranges
+ // with different types at runtime.
+ static assert(!is(typeof(data1) == typeof(data2)));
+
+ auto chooseRange(bool pickFirst)
+ {
+ // The returned range is a common wrapper type that can be used for
+ // returning or storing either range without running into a type error.
+ return choose(pickFirst, data1, data2);
+
+ // Simply returning the chosen range without using choose() does not
+ // work, because map() and filter() return different types.
+ //return pickFirst ? data1 : data2; // does not compile
+ }
+
+ auto result = chooseRange(true);
+ assert(result.equal(only(1, 2, 4)));
+
+ result = chooseRange(false);
+ assert(result.equal(only(6, 7, 8, 9)));
+}
+
+
+private struct ChooseResult(R1, R2)
+{
+ import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor;
+
+ private union
{
- import std.algorithm.comparison : max;
- import std.algorithm.internal : addressOf;
- import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor;
+ R1 r1;
+ R2 r2;
+ }
+ private bool r1Chosen;
- private union
+ private static auto ref actOnChosen(alias foo, ExtraArgs ...)(ref ChooseResult r,
+ auto ref ExtraArgs extraArgs)
+ {
+ if (r.r1Chosen)
{
- void[max(R1.sizeof, R2.sizeof)] buffer = void;
- void* forAlignmentOnly = void;
+ ref get1(return ref ChooseResult r) @trusted { return r.r1; }
+ return foo(get1(r), extraArgs);
}
- private bool condition;
- private @property ref R1 r1()
+ else
{
- assert(condition);
- return *cast(R1*) buffer.ptr;
+ ref get2(return ref ChooseResult r) @trusted { return r.r2; }
+ return foo(get2(r), extraArgs);
}
- private @property ref R2 r2()
+ }
+
+ this(bool r1Chosen, return scope R1 r1, return scope R2 r2) @trusted
+ {
+ // @trusted because of assignment of r1 and r2 which overlap each other
+ import core.lifetime : emplace;
+
+ // This should be the only place r1Chosen is ever assigned
+ // independently
+ this.r1Chosen = r1Chosen;
+ if (r1Chosen)
{
- assert(!condition);
- return *cast(R2*) buffer.ptr;
+ this.r2 = R2.init;
+ emplace(&this.r1, r1);
}
-
- this(bool condition, R1 r1, R2 r2)
+ else
{
- this.condition = condition;
- import std.conv : emplace;
- if (condition) emplace(addressOf(this.r1), r1);
- else emplace(addressOf(this.r2), r2);
+ this.r1 = R1.init;
+ emplace(&this.r2, r2);
}
+ }
- // Carefully defined postblit to postblit the appropriate range
- static if (hasElaborateCopyConstructor!R1
- || hasElaborateCopyConstructor!R2)
- this(this)
- {
- if (condition)
- {
- static if (hasElaborateCopyConstructor!R1) r1.__postblit();
- }
- else
+ void opAssign(ChooseResult r)
+ {
+ static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2)
+ if (r1Chosen != r.r1Chosen)
{
- static if (hasElaborateCopyConstructor!R2) r2.__postblit();
+ // destroy the current item
+ actOnChosen!((ref r) => destroy(r))(this);
}
+ r1Chosen = r.r1Chosen;
+ if (r1Chosen)
+ {
+ ref get1(return ref ChooseResult r) @trusted { return r.r1; }
+ get1(this) = get1(r);
}
+ else
+ {
+ ref get2(return ref ChooseResult r) @trusted { return r.r2; }
+ get2(this) = get2(r);
+ }
+ }
- static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2)
- ~this()
+ // Carefully defined postblit to postblit the appropriate range
+ static if (hasElaborateCopyConstructor!R1
+ || hasElaborateCopyConstructor!R2)
+ this(this)
+ {
+ actOnChosen!((ref r) {
+ static if (hasElaborateCopyConstructor!(typeof(r))) r.__postblit();
+ })(this);
+ }
+
+ static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2)
+ ~this()
+ {
+ actOnChosen!((ref r) => destroy(r))(this);
+ }
+
+ static if (isInfinite!R1 && isInfinite!R2)
+ // Propagate infiniteness.
+ enum bool empty = false;
+ else
+ @property bool empty()
{
- if (condition) destroy(r1);
- else destroy(r2);
+ return actOnChosen!(r => r.empty)(this);
}
- static if (isInfinite!R1 && isInfinite!R2)
- // Propagate infiniteness.
- enum bool empty = false;
- else
- @property bool empty()
- {
- return condition ? r1.empty : r2.empty;
- }
+ @property auto ref front()
+ {
+ static auto ref getFront(R)(ref R r) { return r.front; }
+ return actOnChosen!getFront(this);
+ }
- @property auto ref front()
+ void popFront()
+ {
+ return actOnChosen!((ref r) { r.popFront; })(this);
+ }
+
+ static if (isForwardRange!R1 && isForwardRange!R2)
+ @property auto save() return scope
+ {
+ if (r1Chosen)
+ {
+ ref R1 getR1() @trusted { return r1; }
+ return ChooseResult(r1Chosen, getR1.save, R2.init);
+ }
+ else
{
- return condition ? r1.front : r2.front;
+ ref R2 getR2() @trusted { return r2; }
+ return ChooseResult(r1Chosen, R1.init, getR2.save);
}
+ }
- void popFront()
+ @property void front(T)(T v)
+ if (is(typeof({ r1.front = v; r2.front = v; })))
+ {
+ actOnChosen!((ref r, T v) { r.front = v; })(this, v);
+ }
+
+ static if (hasMobileElements!R1 && hasMobileElements!R2)
+ auto moveFront()
{
- return condition ? r1.popFront : r2.popFront;
+ return actOnChosen!((ref r) => r.moveFront)(this);
}
- static if (isForwardRange!R1 && isForwardRange!R2)
- @property auto save()
- {
- auto result = this;
- if (condition) r1 = r1.save;
- else r2 = r2.save;
- return result;
- }
+ static if (isBidirectionalRange!R1 && isBidirectionalRange!R2)
+ {
+ @property auto ref back()
+ {
+ static auto ref getBack(R)(ref R r) { return r.back; }
+ return actOnChosen!getBack(this);
+ }
- @property void front(T)(T v)
- if (is(typeof({ r1.front = v; r2.front = v; })))
+ void popBack()
{
- if (condition) r1.front = v; else r2.front = v;
+ actOnChosen!((ref r) { r.popBack; })(this);
}
static if (hasMobileElements!R1 && hasMobileElements!R2)
- auto moveFront()
+ auto moveBack()
{
- return condition ? r1.moveFront : r2.moveFront;
+ return actOnChosen!((ref r) => r.moveBack)(this);
}
- static if (isBidirectionalRange!R1 && isBidirectionalRange!R2)
+ @property void back(T)(T v)
+ if (is(typeof({ r1.back = v; r2.back = v; })))
{
- @property auto ref back()
- {
- return condition ? r1.back : r2.back;
- }
+ actOnChosen!((ref r, T v) { r.back = v; })(this, v);
+ }
+ }
- void popBack()
- {
- return condition ? r1.popBack : r2.popBack;
- }
+ static if (hasLength!R1 && hasLength!R2)
+ {
+ @property size_t length()
+ {
+ return actOnChosen!(r => r.length)(this);
+ }
+ alias opDollar = length;
+ }
- static if (hasMobileElements!R1 && hasMobileElements!R2)
- auto moveBack()
- {
- return condition ? r1.moveBack : r2.moveBack;
- }
+ static if (isRandomAccessRange!R1 && isRandomAccessRange!R2)
+ {
+ auto ref opIndex(size_t index)
+ {
+ static auto ref get(R)(ref R r, size_t index) { return r[index]; }
+ return actOnChosen!get(this, index);
+ }
- @property void back(T)(T v)
- if (is(typeof({ r1.back = v; r2.back = v; })))
+ static if (hasMobileElements!R1 && hasMobileElements!R2)
+ auto moveAt(size_t index)
{
- if (condition) r1.back = v; else r2.back = v;
+ return actOnChosen!((ref r, size_t index) => r.moveAt(index))
+ (this, index);
}
- }
- static if (hasLength!R1 && hasLength!R2)
+ void opIndexAssign(T)(T v, size_t index)
+ if (is(typeof({ r1[1] = v; r2[1] = v; })))
{
- @property size_t length()
- {
- return condition ? r1.length : r2.length;
- }
- alias opDollar = length;
+ return actOnChosen!((ref r, size_t index, T v) { r[index] = v; })
+ (this, index, v);
}
+ }
- static if (isRandomAccessRange!R1 && isRandomAccessRange!R2)
+ static if (hasSlicing!R1 && hasSlicing!R2)
+ auto opSlice(size_t begin, size_t end)
{
- auto ref opIndex(size_t index)
- {
- return condition ? r1[index] : r2[index];
- }
+ alias Slice1 = typeof(R1.init[0 .. 1]);
+ alias Slice2 = typeof(R2.init[0 .. 1]);
+ return actOnChosen!((r, size_t begin, size_t end) {
+ static if (is(typeof(r) == Slice1))
+ return choose(true, r[begin .. end], Slice2.init);
+ else
+ return choose(false, Slice1.init, r[begin .. end]);
+ })(this, begin, end);
+ }
+}
- static if (hasMobileElements!R1 && hasMobileElements!R2)
- auto moveAt(size_t index)
- {
- return condition ? r1.moveAt(index) : r2.moveAt(index);
- }
+// https://issues.dlang.org/show_bug.cgi?id=18657
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ string s = "foo";
+ auto r = choose(true, refRange(&s), "bar");
+ assert(equal(r.save, "foo"));
+ assert(equal(r, "foo"));
+}
- void opIndexAssign(T)(T v, size_t index)
- if (is(typeof({ r1[1] = v; r2[1] = v; })))
- {
- if (condition) r1[index] = v; else r2[index] = v;
- }
- }
+@safe unittest
+{
+ static void* p;
+ static struct R
+ {
+ void* q;
+ int front;
+ bool empty;
+ void popFront() {}
+ @property R save() { p = q; return this; }
+ // `p = q;` is only there to prevent inference of `scope return`.
+ }
+ R r;
+ choose(true, r, r).save;
+}
- // BUG: this should work for infinite ranges, too
- static if (hasSlicing!R1 && hasSlicing!R2 &&
- !isInfinite!R2 && !isInfinite!R2)
- auto opSlice(size_t begin, size_t end)
- {
- auto result = this;
- if (condition) result.r1 = result.r1[begin .. end];
- else result.r2 = result.r2[begin .. end];
- return result;
- }
+// Make sure ChooseResult.save doesn't trust @system user code.
+@system unittest // copy is @system
+{
+ static struct R
+ {
+ int front;
+ bool empty;
+ void popFront() {}
+ this(this) @system {}
+ @property R save() { return R(front, empty); }
}
- return Result(condition, r1, r2);
+ choose(true, R(), R()).save;
+ choose(true, [0], R()).save;
+ choose(true, R(), [0]).save;
}
-///
-@system unittest
+@safe unittest // copy is @system
{
- import std.algorithm.comparison : equal;
- import std.algorithm.iteration : filter, map;
+ static struct R
+ {
+ int front;
+ bool empty;
+ void popFront() {}
+ this(this) @system {}
+ @property R save() { return R(front, empty); }
+ }
+ static assert(!__traits(compiles, choose(true, R(), R()).save));
+ static assert(!__traits(compiles, choose(true, [0], R()).save));
+ static assert(!__traits(compiles, choose(true, R(), [0]).save));
+}
- auto data1 = [ 1, 2, 3, 4 ].filter!(a => a != 3);
- auto data2 = [ 5, 6, 7, 8 ].map!(a => a + 1);
+@system unittest // .save is @system
+{
+ static struct R
+ {
+ int front;
+ bool empty;
+ void popFront() {}
+ @property R save() @system { return this; }
+ }
+ choose(true, R(), R()).save;
+ choose(true, [0], R()).save;
+ choose(true, R(), [0]).save;
+}
- // choose() is primarily useful when you need to select one of two ranges
- // with different types at runtime.
- static assert(!is(typeof(data1) == typeof(data2)));
+@safe unittest // .save is @system
+{
+ static struct R
+ {
+ int front;
+ bool empty;
+ void popFront() {}
+ @property R save() @system { return this; }
+ }
+ static assert(!__traits(compiles, choose(true, R(), R()).save));
+ static assert(!__traits(compiles, choose(true, [0], R()).save));
+ static assert(!__traits(compiles, choose(true, R(), [0]).save));
+}
- auto chooseRange(bool pickFirst)
+//https://issues.dlang.org/show_bug.cgi?id=19738
+@safe nothrow pure @nogc unittest
+{
+ static struct EvilRange
{
- // The returned range is a common wrapper type that can be used for
- // returning or storing either range without running into a type error.
- return choose(pickFirst, data1, data2);
+ enum empty = true;
+ int front;
+ void popFront() @safe {}
+ auto opAssign(const ref EvilRange other)
+ {
+ *(cast(uint*) 0xcafebabe) = 0xdeadbeef;
+ return this;
+ }
+ }
- // Simply returning the chosen range without using choose() does not
- // work, because map() and filter() return different types.
- //return pickFirst ? data1 : data2; // does not compile
+ static assert(!__traits(compiles, () @safe
+ {
+ auto c1 = choose(true, EvilRange(), EvilRange());
+ auto c2 = c1;
+ c1 = c2;
+ }));
+}
+
+
+// https://issues.dlang.org/show_bug.cgi?id=20495
+@safe unittest
+{
+ static struct KillableRange
+ {
+ int *item;
+ ref int front() { return *item; }
+ bool empty() { return *item > 10; }
+ void popFront() { ++(*item); }
+ this(this)
+ {
+ assert(item is null || cast(size_t) item > 1000);
+ item = new int(*item);
+ }
+ KillableRange save() { return this; }
}
- auto result = chooseRange(true);
- assert(result.equal([ 1, 2, 4 ]));
+ auto kr = KillableRange(new int(1));
+ int[] x = [1,2,3,4,5]; // length is first
- result = chooseRange(false);
- assert(result.equal([ 6, 7, 8, 9 ]));
+ auto chosen = choose(true, x, kr);
+ auto chosen2 = chosen.save;
}
/**
Choose one of multiple ranges at runtime.
The ranges may be different, but they must have compatible element types. The
-result is a range that offers the weakest capabilities of all $(D Ranges).
+result is a range that offers the weakest capabilities of all `Ranges`.
Params:
index = which range to choose, must be less than the number of ranges
@@ -1578,7 +1796,7 @@ Returns:
The indexed range. If rs consists of only one range, the return type is an
alias of that range's type.
*/
-auto chooseAmong(Ranges...)(size_t index, Ranges rs)
+auto chooseAmong(Ranges...)(size_t index, return scope Ranges rs)
if (Ranges.length >= 2
&& allSatisfy!(isInputRange, staticMap!(Unqual, Ranges))
&& !is(CommonType!(staticMap!(ElementType, Ranges)) == void))
@@ -1590,84 +1808,125 @@ if (Ranges.length >= 2
}
///
-@system unittest
+@safe nothrow pure @nogc unittest
{
- import std.algorithm.comparison : equal;
+ auto test()
+ {
+ import std.algorithm.comparison : equal;
- int[] arr1 = [ 1, 2, 3, 4 ];
- int[] arr2 = [ 5, 6 ];
- int[] arr3 = [ 7 ];
+ int[4] sarr1 = [1, 2, 3, 4];
+ int[2] sarr2 = [5, 6];
+ int[1] sarr3 = [7];
+ auto arr1 = sarr1[];
+ auto arr2 = sarr2[];
+ auto arr3 = sarr3[];
- {
- auto s = chooseAmong(0, arr1, arr2, arr3);
- auto t = s.save;
- assert(s.length == 4);
- assert(s[2] == 3);
- s.popFront();
- assert(equal(t, [1, 2, 3, 4][]));
- }
- {
- auto s = chooseAmong(1, arr1, arr2, arr3);
- assert(s.length == 2);
- s.front = 8;
- assert(equal(s, [8, 6][]));
- }
- {
- auto s = chooseAmong(1, arr1, arr2, arr3);
- assert(s.length == 2);
- s[1] = 9;
- assert(equal(s, [8, 9][]));
- }
- {
- auto s = chooseAmong(1, arr2, arr1, arr3)[1 .. 3];
- assert(s.length == 2);
- assert(equal(s, [2, 3][]));
- }
- {
- auto s = chooseAmong(0, arr1, arr2, arr3);
- assert(s.length == 4);
- assert(s.back == 4);
- s.popBack();
- s.back = 5;
- assert(equal(s, [1, 2, 5][]));
- s.back = 3;
- assert(equal(s, [1, 2, 3][]));
- }
- {
- uint[] foo = [1,2,3,4,5];
- uint[] bar = [6,7,8,9,10];
- auto c = chooseAmong(1,foo, bar);
- assert(c[3] == 9);
- c[3] = 42;
- assert(c[3] == 42);
- assert(c.moveFront() == 6);
- assert(c.moveBack() == 10);
- assert(c.moveAt(4) == 10);
- }
- {
- import std.range : cycle;
- auto s = chooseAmong(1, cycle(arr2), cycle(arr3));
- assert(isInfinite!(typeof(s)));
- assert(!s.empty);
- assert(s[100] == 7);
+ {
+ auto s = chooseAmong(0, arr1, arr2, arr3);
+ auto t = s.save;
+ assert(s.length == 4);
+ assert(s[2] == 3);
+ s.popFront();
+ assert(equal(t, only(1, 2, 3, 4)));
+ }
+ {
+ auto s = chooseAmong(1, arr1, arr2, arr3);
+ assert(s.length == 2);
+ s.front = 8;
+ assert(equal(s, only(8, 6)));
+ }
+ {
+ auto s = chooseAmong(1, arr1, arr2, arr3);
+ assert(s.length == 2);
+ s[1] = 9;
+ assert(equal(s, only(8, 9)));
+ }
+ {
+ auto s = chooseAmong(1, arr2, arr1, arr3)[1 .. 3];
+ assert(s.length == 2);
+ assert(equal(s, only(2, 3)));
+ }
+ {
+ auto s = chooseAmong(0, arr1, arr2, arr3);
+ assert(s.length == 4);
+ assert(s.back == 4);
+ s.popBack();
+ s.back = 5;
+ assert(equal(s, only(1, 2, 5)));
+ s.back = 3;
+ assert(equal(s, only(1, 2, 3)));
+ }
+ {
+ uint[5] foo = [1, 2, 3, 4, 5];
+ uint[5] bar = [6, 7, 8, 9, 10];
+ auto c = chooseAmong(1, foo[], bar[]);
+ assert(c[3] == 9);
+ c[3] = 42;
+ assert(c[3] == 42);
+ assert(c.moveFront() == 6);
+ assert(c.moveBack() == 10);
+ assert(c.moveAt(4) == 10);
+ }
+ {
+ import std.range : cycle;
+ auto s = chooseAmong(0, cycle(arr2), cycle(arr3));
+ assert(isInfinite!(typeof(s)));
+ assert(!s.empty);
+ assert(s[100] == 8);
+ assert(s[101] == 9);
+ assert(s[0 .. 3].equal(only(8, 9, 8)));
+ }
+ return 0;
}
+ // works at runtime
+ auto a = test();
+ // and at compile time
+ static b = test();
}
-@system unittest
+@safe nothrow pure @nogc unittest
{
- int[] a = [1, 2, 3];
- long[] b = [4, 5, 6];
- auto c = chooseAmong(0, a, b);
+ int[3] a = [1, 2, 3];
+ long[3] b = [4, 5, 6];
+ auto c = chooseAmong(0, a[], b[]);
c[0] = 42;
assert(c[0] == 42);
}
+@safe nothrow pure @nogc unittest
+{
+ static struct RefAccessRange
+ {
+ int[] r;
+ ref front() @property { return r[0]; }
+ ref back() @property { return r[$ - 1]; }
+ void popFront() { r = r[1 .. $]; }
+ void popBack() { r = r[0 .. $ - 1]; }
+ auto empty() @property { return r.empty; }
+ ref opIndex(size_t i) { return r[i]; }
+ auto length() @property { return r.length; }
+ alias opDollar = length;
+ auto save() { return this; }
+ }
+ static assert(isRandomAccessRange!RefAccessRange);
+ static assert(isRandomAccessRange!RefAccessRange);
+ int[4] a = [4, 3, 2, 1];
+ int[2] b = [6, 5];
+ auto c = chooseAmong(0, RefAccessRange(a[]), RefAccessRange(b[]));
+
+ void refFunc(ref int a, int target) { assert(a == target); }
+
+ refFunc(c[2], 2);
+ refFunc(c.front, 4);
+ refFunc(c.back, 1);
+}
+
/**
-$(D roundRobin(r1, r2, r3)) yields $(D r1.front), then $(D r2.front),
-then $(D r3.front), after which it pops off one element from each and
-continues again from $(D r1). For example, if two ranges are involved,
-it alternately yields elements off the two ranges. $(D roundRobin)
+$(D roundRobin(r1, r2, r3)) yields `r1.front`, then `r2.front`,
+then `r3.front`, after which it pops off one element from each and
+continues again from `r1`. For example, if two ranges are involved,
+it alternately yields elements off the two ranges. `roundRobin`
stops after it has consumed all ranges (skipping over the ones that
finish early).
*/
@@ -1743,12 +2002,21 @@ if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs)))
static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs)))
@property auto save()
{
- Result result = this;
- foreach (i, Unused; Rs)
+ auto saveSource(size_t len)()
{
- result.source[i] = result.source[i].save;
+ import std.typecons : tuple;
+ static assert(len > 0);
+ static if (len == 1)
+ {
+ return tuple(source[0].save);
+ }
+ else
+ {
+ return saveSource!(len - 1)() ~
+ tuple(source[len - 1].save);
+ }
}
- return result;
+ return Result(saveSource!(Rs.length).expand, _current);
}
static if (allSatisfy!(hasLength, Rs))
@@ -1806,6 +2074,15 @@ if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs)))
assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3]));
}
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ string f = "foo", b = "bar";
+ auto r = roundRobin(refRange(&f), refRange(&b));
+ assert(equal(r.save, "fboaor"));
+ assert(equal(r.save, "fboaor"));
+}
+
/**
Iterates a random-access range starting from a given point and
progressively extending left and right from that point. If no initial
@@ -1916,7 +2193,8 @@ information is not applied to the result unless `input` also has
length information.
Params:
- input = an input range to iterate over up to `n` times
+ input = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ to iterate over up to `n` times
n = the number of elements to take
Returns:
@@ -1998,7 +2276,8 @@ if (isInputRange!(Unqual!Range) &&
assert(!empty,
"Attempting to assign to the front of an empty "
~ Take.stringof);
- // This has to return auto instead of void because of Bug 4706.
+ // This has to return auto instead of void because of
+ // https://issues.dlang.org/show_bug.cgi?id=4706
source.front = v;
}
@@ -2083,7 +2362,8 @@ if (isInputRange!(Unqual!Range) &&
/// ditto
@property void back(ElementType!R v)
{
- // This has to return auto instead of void because of Bug 4706.
+ // This has to return auto instead of void because of
+ // https://issues.dlang.org/show_bug.cgi?id=4706
assert(!empty,
"Attempting to assign to the back of an empty "
~ Take.stringof);
@@ -2133,10 +2413,7 @@ if (isInputRange!(Unqual!Range) &&
}
}
-/**
-This template simply aliases itself to R and is useful for consistency in
-generic code.
-*/
+/// ditto
template Take(R)
if (isInputRange!(Unqual!R) &&
((!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R)) || is(R T == Take!T)))
@@ -2251,7 +2528,8 @@ pure @safe nothrow @nogc unittest
{
// Check that one can declare variables of all Take types,
// and that they match the return type of the corresponding
- // take(). (See issue 4464.)
+ // take().
+ // See https://issues.dlang.org/show_bug.cgi?id=4464
int[] r1;
Take!(int[]) t1;
t1 = take(r1, 1);
@@ -2277,7 +2555,8 @@ pure @safe nothrow @nogc unittest
static assert(isBidirectionalRange!TR2);
}
-pure @safe nothrow @nogc unittest //12731
+// https://issues.dlang.org/show_bug.cgi?id=12731
+pure @safe nothrow @nogc unittest
{
auto a = repeat(1);
auto s = a[1 .. 5];
@@ -2287,7 +2566,8 @@ pure @safe nothrow @nogc unittest //12731
assert(s[1] == 1);
}
-pure @safe nothrow @nogc unittest //13151
+// https://issues.dlang.org/show_bug.cgi?id=13151
+pure @safe nothrow @nogc unittest
{
import std.algorithm.comparison : equal;
@@ -2297,16 +2577,16 @@ pure @safe nothrow @nogc unittest //13151
/**
-Similar to $(LREF take), but assumes that $(D range) has at least $(D
+Similar to $(LREF take), but assumes that `range` has at least $(D
n) elements. Consequently, the result of $(D takeExactly(range, n))
-always defines the $(D length) property (and initializes it to $(D n))
-even when $(D range) itself does not define $(D length).
+always defines the `length` property (and initializes it to `n`)
+even when `range` itself does not define `length`.
-The result of $(D takeExactly) is identical to that of $(LREF take) in
-cases where the original range defines $(D length) or is infinite.
+The result of `takeExactly` is identical to that of $(LREF take) in
+cases where the original range defines `length` or is infinite.
Unlike $(LREF take), however, it is illegal to pass a range with less than
-$(D n) elements to $(D takeExactly); this will cause an assertion failure.
+`n` elements to `takeExactly`; this will cause an assertion failure.
*/
auto takeExactly(R)(R range, size_t n)
if (isInputRange!R)
@@ -2346,9 +2626,9 @@ if (isInputRange!R)
@property size_t length() const { return _n; }
alias opDollar = length;
- @property Take!R _takeExactly_Result_asTake()
+ @property auto _takeExactly_Result_asTake()
{
- return typeof(return)(_input, _n);
+ return take(_input, _n);
}
alias _takeExactly_Result_asTake this;
@@ -2498,18 +2778,34 @@ pure @safe nothrow unittest
assert(equal(t, te));
}
+// https://issues.dlang.org/show_bug.cgi?id=18092
+// can't combine take and takeExactly
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : AllDummyRanges;
+
+ static foreach (Range; AllDummyRanges)
+ {{
+ Range r;
+ assert(r.take(6).takeExactly(2).equal([1, 2]));
+ assert(r.takeExactly(6).takeExactly(2).equal([1, 2]));
+ assert(r.takeExactly(6).take(2).equal([1, 2]));
+ }}
+}
+
/**
Returns a range with at most one element; for example, $(D
takeOne([42, 43, 44])) returns a range consisting of the integer $(D
-42). Calling $(D popFront()) off that range renders it empty.
+42). Calling `popFront()` off that range renders it empty.
-In effect $(D takeOne(r)) is somewhat equivalent to $(D take(r, 1)) but in
+In effect `takeOne(r)` is somewhat equivalent to $(D take(r, 1)) but in
certain interfaces it is important to know statically that the range may only
have at most one element.
-The type returned by $(D takeOne) is a random-access range with length
-regardless of $(D R)'s capabilities, as long as it is a forward range.
-(another feature that distinguishes $(D takeOne) from $(D take)). If
+The type returned by `takeOne` is a random-access range with length
+regardless of `R`'s capabilities, as long as it is a forward range.
+(another feature that distinguishes `takeOne` from `take`). If
(D R) is an input range but not a forward range, return type is an input
range with all random-access capabilities except save.
*/
@@ -2562,8 +2858,15 @@ if (isInputRange!R)
}
auto opSlice(size_t m, size_t n)
{
- assert(m <= n && n < length, "Attempting to index a takeOne out of bounds");
- return n > m ? this : Result(_source, false);
+ assert(
+ m <= n,
+ "Attempting to slice a takeOne range with a larger first argument than the second."
+ );
+ assert(
+ n <= length,
+ "Attempting to slice using an out of bounds index on a takeOne range."
+ );
+ return n > m ? this : Result(_source, true);
}
// Non-standard property
@property R source() { return _source; }
@@ -2602,10 +2905,92 @@ pure @safe nothrow @nogc unittest
static assert(!isForwardRange!NonForwardRange);
auto s = takeOne(NonForwardRange());
+ assert(s.length == 1);
+ assert(!s.empty);
+ assert(s.front == 42);
+ assert(s.back == 42);
+ assert(s[0] == 42);
+
+ auto t = s[0 .. 0];
+ assert(t.empty);
+ assert(t.length == 0);
+
+ auto u = s[1 .. 1];
+ assert(u.empty);
+ assert(u.length == 0);
+
+ auto v = s[0 .. 1];
+ s.popFront();
+ assert(s.length == 0);
+ assert(s.empty);
+ assert(!v.empty);
+ assert(v.front == 42);
+ v.popBack();
+ assert(v.empty);
+ assert(v.length == 0);
+}
+
+pure @safe nothrow @nogc unittest
+{
+ struct NonSlicingForwardRange
+ {
+ enum empty = false;
+ int front() { return 42; }
+ void popFront() {}
+ @property auto save() { return this; }
+ }
+
+ static assert(isForwardRange!NonSlicingForwardRange);
+ static assert(!hasSlicing!NonSlicingForwardRange);
+
+ auto s = takeOne(NonSlicingForwardRange());
+ assert(s.length == 1);
+ assert(!s.empty);
assert(s.front == 42);
+ assert(s.back == 42);
+ assert(s[0] == 42);
+ auto t = s.save;
+ s.popFront();
+ assert(s.length == 0);
+ assert(s.empty);
+ assert(!t.empty);
+ assert(t.front == 42);
+ t.popBack();
+ assert(t.empty);
+ assert(t.length == 0);
+}
+
+// Test that asserts trigger correctly
+@system unittest
+{
+ import std.exception : assertThrown;
+ import core.exception : AssertError;
+
+ struct NonForwardRange
+ {
+ enum empty = false;
+ int front() { return 42; }
+ void popFront() {}
+ }
+
+ auto s = takeOne(NonForwardRange());
+
+ assertThrown!AssertError(s[1]);
+ assertThrown!AssertError(s[0 .. 2]);
+
+ size_t one = 1; // Avoid style warnings triggered by literals
+ size_t zero = 0;
+ assertThrown!AssertError(s[one .. zero]);
+
+ s.popFront;
+ assert(s.empty);
+ assertThrown!AssertError(s.front);
+ assertThrown!AssertError(s.back);
+ assertThrown!AssertError(s.popFront);
+ assertThrown!AssertError(s.popBack);
}
-//guards against issue 16999
+// https://issues.dlang.org/show_bug.cgi?id=16999
pure @safe unittest
{
auto myIota = new class
@@ -2626,7 +3011,7 @@ pure @safe unittest
/++
Returns an empty range which is statically known to be empty and is
- guaranteed to have $(D length) and be random access regardless of $(D R)'s
+ guaranteed to have `length` and be random access regardless of `R`'s
capabilities.
+/
auto takeNone(R)()
@@ -2664,7 +3049,7 @@ if (isInputRange!R)
//member version if it's defined.
static if (is(typeof(R.takeNone)))
auto retval = range.takeNone();
- //@@@BUG@@@ 8339
+ // https://issues.dlang.org/show_bug.cgi?id=8339
else static if (isDynamicArray!R)/+ ||
(is(R == struct) && __traits(compiles, {auto r = R.init;}) && R.init.empty))+/
{
@@ -2676,7 +3061,8 @@ if (isInputRange!R)
else
auto retval = takeExactly(range, 0);
- //@@@BUG@@@ 7892 prevents this from being done in an out block.
+ // https://issues.dlang.org/show_bug.cgi?id=7892 prevents this from being
+ // done in an out block.
assert(retval.empty);
return retval;
}
@@ -2772,13 +3158,13 @@ pure @safe nothrow unittest
import std.format : format;
- foreach (range; AliasSeq!([1, 2, 3, 4, 5],
+ static foreach (range; AliasSeq!([1, 2, 3, 4, 5],
"hello world",
"hello world"w,
"hello world"d,
SliceStruct([1, 2, 3]),
- //@@@BUG@@@ 8339 forces this to be takeExactly
- //`InitStruct([1, 2, 3]),
+ // https://issues.dlang.org/show_bug.cgi?id=8339
+ // forces this to be takeExactly `InitStruct([1, 2, 3]),
TakeNoneStruct([1, 2, 3])))
{
static assert(takeNone(range).empty, typeof(range).stringof);
@@ -2786,7 +3172,7 @@ pure @safe nothrow unittest
static assert(is(typeof(range) == typeof(takeNone(range))), typeof(range).stringof);
}
- foreach (range; AliasSeq!(NormalStruct([1, 2, 3]),
+ static foreach (range; AliasSeq!(NormalStruct([1, 2, 3]),
InitStruct([1, 2, 3])))
{
static assert(takeNone(range).empty, typeof(range).stringof);
@@ -2809,28 +3195,29 @@ pure @safe nothrow unittest
auto filtered = filter!"true"([1, 2, 3, 4, 5]);
assert(takeNone(filtered).empty);
- //@@@BUG@@@ 8339 and 5941 force this to be takeExactly
+ // https://issues.dlang.org/show_bug.cgi?id=8339 and
+ // https://issues.dlang.org/show_bug.cgi?id=5941 force this to be takeExactly
//static assert(is(typeof(filtered) == typeof(takeNone(filtered))), typeof(filtered).stringof);
}
/++
- + Return a _range advanced to within $(D _n) elements of the end of
- + $(D _range).
+ + Return a range advanced to within `_n` elements of the end of
+ + `range`.
+
- + Intended as the _range equivalent of the Unix
+ + Intended as the range equivalent of the Unix
+ $(HTTP en.wikipedia.org/wiki/Tail_%28Unix%29, _tail) utility. When the length
- + of $(D _range) is less than or equal to $(D _n), $(D _range) is returned
+ + of `range` is less than or equal to `_n`, `range` is returned
+ as-is.
+
+ Completes in $(BIGOH 1) steps for ranges that support slicing and have
- + length. Completes in $(BIGOH _range.length) time for all other ranges.
+ + length. Completes in $(BIGOH range.length) time for all other ranges.
+
+ Params:
- + range = _range to get _tail of
+ + range = range to get _tail of
+ n = maximum number of elements to include in _tail
+
+ Returns:
- + Returns the _tail of $(D _range) augmented with length information
+ + Returns the _tail of `range` augmented with length information
+/
auto tail(Range)(Range range, size_t n)
if (isInputRange!Range && !isInfinite!Range &&
@@ -2889,7 +3276,7 @@ pure @safe nothrow unittest
.assumeWontThrow);
}
-// @nogc prevented by @@@BUG@@@ 15408
+// @nogc prevented by https://issues.dlang.org/show_bug.cgi?id=15408
pure nothrow @safe /+@nogc+/ unittest
{
import std.algorithm.comparison : equal;
@@ -2929,27 +3316,27 @@ pure @safe nothrow @nogc unittest
/++
Convenience function which calls
- $(REF popFrontN, std, _range, primitives)`(range, n)` and returns `range`.
+ $(REF popFrontN, std, range, primitives)`(range, n)` and returns `range`.
`drop` makes it easier to pop elements from a range
and then pass it to another function within a single expression,
whereas `popFrontN` would require multiple statements.
`dropBack` provides the same functionality but instead calls
- $(REF popBackN, std, _range, primitives)`(range, n)`
+ $(REF popBackN, std, range, primitives)`(range, n)`
Note: `drop` and `dropBack` will only pop $(I up to)
`n` elements but will stop if the range is empty first.
In other languages this is sometimes called `skip`.
Params:
- range = the input range to drop from
+ range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from
n = the number of elements to drop
Returns:
`range` with up to `n` elements dropped
See_Also:
- $(REF popFront, std, _range, primitives), $(REF popBackN, std, _range, primitives)
+ $(REF popFront, std, range, primitives), $(REF popBackN, std, range, primitives)
+/
R drop(R)(R range, size_t n)
if (isInputRange!R)
@@ -2957,13 +3344,6 @@ if (isInputRange!R)
range.popFrontN(n);
return range;
}
-/// ditto
-R dropBack(R)(R range, size_t n)
-if (isBidirectionalRange!R)
-{
- range.popBackN(n);
- return range;
-}
///
@safe unittest
@@ -2976,6 +3356,15 @@ if (isBidirectionalRange!R)
assert("hello world".take(6).drop(3).equal("lo "));
}
+/// ditto
+R dropBack(R)(R range, size_t n)
+if (isBidirectionalRange!R)
+{
+ range.popBackN(n);
+ return range;
+}
+
+///
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -3018,28 +3407,28 @@ if (isBidirectionalRange!R)
}
/++
- Similar to $(LREF drop) and $(D dropBack) but they call
- $(D range.$(LREF popFrontExactly)(n)) and $(D range.popBackExactly(n))
+ Similar to $(LREF drop) and `dropBack` but they call
+ $(D range.$(LREF popFrontExactly)(n)) and `range.popBackExactly(n)`
instead.
- Note: Unlike $(D drop), $(D dropExactly) will assume that the
- range holds at least $(D n) elements. This makes $(D dropExactly)
- faster than $(D drop), but it also means that if $(D range) does
- not contain at least $(D n) elements, it will attempt to call $(D popFront)
+ Note: Unlike `drop`, `dropExactly` will assume that the
+ range holds at least `n` elements. This makes `dropExactly`
+ faster than `drop`, but it also means that if `range` does
+ not contain at least `n` elements, it will attempt to call `popFront`
on an empty range, which is undefined behavior. So, only use
- $(D popFrontExactly) when it is guaranteed that $(D range) holds at least
- $(D n) elements.
+ `popFrontExactly` when it is guaranteed that `range` holds at least
+ `n` elements.
Params:
- range = the input range to drop from
+ range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from
n = the number of elements to drop
Returns:
`range` with `n` elements dropped
See_Also:
- $(REF popFrontExcatly, std, _range, primitives),
- $(REF popBackExcatly, std, _range, primitives)
+ $(REF popFrontExcatly, std, range, primitives),
+ $(REF popBackExcatly, std, range, primitives)
+/
R dropExactly(R)(R range, size_t n)
if (isInputRange!R)
@@ -3076,13 +3465,13 @@ if (isBidirectionalRange!R)
/++
Convenience function which calls
- $(D range.popFront()) and returns $(D range). $(D dropOne)
+ `range.popFront()` and returns `range`. `dropOne`
makes it easier to pop an element from a range
and then pass it to another function within a single expression,
- whereas $(D popFront) would require multiple statements.
+ whereas `popFront` would require multiple statements.
- $(D dropBackOne) provides the same functionality but instead calls
- $(D range.popBack()).
+ `dropBackOne` provides the same functionality but instead calls
+ `range.popBack()`.
+/
R dropOne(R)(R range)
if (isInputRange!R)
@@ -3123,13 +3512,17 @@ pure @safe nothrow unittest
}
/**
-Create a range which repeats one value forever.
+Create a range which repeats one value.
Params:
- value = the value to repeat
+ value = the _value to repeat
+ n = the number of times to repeat `value`
Returns:
- An infinite random access range with slicing.
+ If `n` is not defined, an infinite random access range
+ with slicing.
+
+ If `n` is defined, a random access range with slicing.
*/
struct Repeat(T)
{
@@ -3177,7 +3570,7 @@ public:
"Attempting to slice a Repeat with a larger first argument than the second."
);
}
- body
+ do
{
return this.takeExactly(j - i);
}
@@ -3198,7 +3591,7 @@ pure @safe nothrow unittest
{
import std.algorithm.comparison : equal;
- assert(equal(5.repeat().take(4), [ 5, 5, 5, 5 ]));
+ assert(5.repeat().take(4).equal([5, 5, 5, 5]));
}
pure @safe nothrow unittest
@@ -3222,24 +3615,22 @@ pure @safe nothrow unittest
assert(r2.front == 5);
}
-/**
- Repeats $(D value) exactly $(D n) times. Equivalent to $(D
- take(repeat(value), n)).
-*/
+/// ditto
Take!(Repeat!T) repeat(T)(T value, size_t n)
{
return take(repeat(value), n);
}
///
-pure @safe nothrow @nogc unittest
+pure @safe nothrow unittest
{
import std.algorithm.comparison : equal;
- assert(equal(5.repeat(4), 5.repeat().take(4)));
+ assert(5.repeat(4).equal([5, 5, 5, 5]));
}
-pure @safe nothrow unittest //12007
+// https://issues.dlang.org/show_bug.cgi?id=12007
+pure @safe nothrow unittest
{
static class C{}
Repeat!(immutable int) ri;
@@ -3295,7 +3686,7 @@ if (isCallable!fun)
}
///
-@safe pure unittest
+@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;
@@ -3306,7 +3697,7 @@ if (isCallable!fun)
}
///
-@safe pure unittest
+@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
@@ -3334,6 +3725,7 @@ private struct Generator(Fun...)
{
static assert(Fun.length == 1);
static assert(isInputRange!Generator);
+ import std.traits : FunctionAttribute, functionAttributes, ReturnType;
private:
static if (is(Fun[0]))
@@ -3378,7 +3770,7 @@ public:
}
}
-@safe unittest
+@safe nothrow unittest
{
import std.algorithm.comparison : equal;
@@ -3404,7 +3796,7 @@ public:
}
// verify ref mechanism works
-@system unittest
+@system nothrow unittest
{
int[10] arr;
int idx;
@@ -3436,12 +3828,12 @@ public:
/**
Repeats the given forward range ad infinitum. If the original range is
-infinite (fact that would make $(D Cycle) the identity application),
-$(D Cycle) detects that and aliases itself to the range type
+infinite (fact that would make `Cycle` the identity application),
+`Cycle` detects that and aliases itself to the range type
itself. That works for non-forward ranges too.
-If the original range has random access, $(D Cycle) offers
+If the original range has random access, `Cycle` offers
random access and also offers a constructor taking an initial position
-$(D index). $(D Cycle) works with static arrays in addition to ranges,
+`index`. `Cycle` works with static arrays in addition to ranges,
mostly for performance reasons.
Note: The input range must not be empty.
@@ -3543,7 +3935,7 @@ if (isForwardRange!R && !isInfinite!R)
{
assert(i <= j);
}
- body
+ do
{
return this[i .. $].takeExactly(j - i);
}
@@ -3567,6 +3959,12 @@ if (isForwardRange!R && !isInfinite!R)
_current = input.save;
}
+ private this(R original, R current)
+ {
+ _original = original;
+ _current = current;
+ }
+
/// ditto
@property auto ref front()
{
@@ -3606,10 +4004,7 @@ if (isForwardRange!R && !isInfinite!R)
@property Cycle save()
{
//No need to call _original.save, because Cycle never actually modifies _original
- Cycle ret = this;
- ret._original = _original;
- ret._current = _current.save;
- return ret;
+ return Cycle(_original, _current.save);
}
}
}
@@ -3621,7 +4016,7 @@ if (isInfinite!R)
alias Cycle = R;
}
-///
+/// ditto
struct Cycle(R)
if (isStaticArray!R)
{
@@ -3688,7 +4083,7 @@ nothrow:
"Attempting to slice a Repeat with a larger first argument than the second."
);
}
- body
+ do
{
return this[i .. $].takeExactly(j - i);
}
@@ -3744,7 +4139,7 @@ if (isStaticArray!R)
return Cycle!R(input, index);
}
-@safe unittest
+@safe nothrow unittest
{
import std.algorithm.comparison : equal;
import std.internal.test.dummyrange : AllDummyRanges;
@@ -3806,7 +4201,7 @@ if (isStaticArray!R)
}
}
-@system unittest // For static arrays.
+@system nothrow unittest // For static arrays.
{
import std.algorithm.comparison : equal;
@@ -3824,7 +4219,7 @@ if (isStaticArray!R)
static assert(is(typeof(cConst[1 .. $]) == const(C)));
}
-@safe unittest // For infinite ranges
+@safe nothrow unittest // For infinite ranges
{
struct InfRange
{
@@ -3868,7 +4263,7 @@ if (isStaticArray!R)
}
}
-@system unittest
+@system @nogc nothrow unittest
{
import std.algorithm.comparison : equal;
@@ -3899,7 +4294,8 @@ if (isStaticArray!R)
assert(cleS.front == 0);
}
-@system unittest //10845
+// https://issues.dlang.org/show_bug.cgi?id=10845
+@system unittest
{
import std.algorithm.comparison : equal;
import std.algorithm.iteration : filter;
@@ -3908,12 +4304,13 @@ if (isStaticArray!R)
assert(equal(cycle(a).take(10), [0, 1, 2, 0, 1, 2, 0, 1, 2, 0]));
}
-@safe unittest // 12177
+// https://issues.dlang.org/show_bug.cgi?id=12177
+@safe unittest
{
static assert(__traits(compiles, recurrence!q{a[n - 1] ~ a[n - 2]}("1", "0")));
}
-// Issue 13390
+// https://issues.dlang.org/show_bug.cgi?id=13390
@system unittest
{
import core.exception : AssertError;
@@ -3921,12 +4318,22 @@ if (isStaticArray!R)
assertThrown!AssertError(cycle([0, 1, 2][0 .. 0]));
}
+// https://issues.dlang.org/show_bug.cgi?id=18657
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ string s = "foo";
+ auto r = refRange(&s).cycle.take(4);
+ assert(equal(r.save, "foof"));
+ assert(equal(r.save, "foof"));
+}
+
private alias lengthType(R) = typeof(R.init.length.init);
/**
Iterate several ranges in lockstep. The element type is a proxy tuple
- that allows accessing the current element in the $(D n)th range by
- using $(D e[n]).
+ that allows accessing the current element in the `n`th range by
+ using `e[n]`.
`zip` is similar to $(LREF lockstep), but `lockstep` doesn't
bundle its elements and uses the `opApply` protocol.
@@ -3934,7 +4341,7 @@ private alias lengthType(R) = typeof(R.init.length.init);
`foreach` iterations.
Params:
- sp = controls what `zip` will do if the _ranges are different lengths
+ sp = controls what `zip` will do if the ranges are different lengths
ranges = the ranges to zip together
Returns:
At minimum, an input range. `Zip` offers the lowest range facilities
@@ -3943,8 +4350,13 @@ private alias lengthType(R) = typeof(R.init.length.init);
it. Due to this, `Zip` is extremely powerful because it allows manipulating
several ranges in lockstep.
Throws:
- An `Exception` if all of the _ranges are not the same length and
+ An `Exception` if all of the ranges are not the same length and
`sp` is set to `StoppingPolicy.requireSameLength`.
+
+ Limitations: The `@nogc` and `nothrow` attributes cannot be inferred for
+ the `Zip` struct because $(LREF StoppingPolicy) can vary at runtime. This
+ limitation is not shared by the anonymous range returned by the `zip`
+ function when not given an explicit `StoppingPolicy` as an argument.
*/
struct Zip(Ranges...)
if (Ranges.length && allSatisfy!(isInputRange, Ranges))
@@ -3968,7 +4380,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges))
}
/**
- Returns $(D true) if the range is at end. The test depends on the
+ Returns `true` if the range is at end. The test depends on the
stopping policy.
*/
static if (allSatisfy!(isInfinite, R))
@@ -4102,7 +4514,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges))
{
//TODO: Fixme! BackElement != back of all ranges in case of jagged-ness
- @property tryMoveBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveFront();}
+ @property tryMoveBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveBack();}
//ElementType(tryMoveBack!0, tryMoveBack!1, ...)
return mixin(q{ElementType(%(tryMoveBack!%s, %))}.format(iota(0, R.length)));
}
@@ -4162,7 +4574,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges))
}
/**
- Calls $(D popBack) for all controlled ranges.
+ Calls `popBack` for all controlled ranges.
*/
static if (allSatisfy!(isBidirectionalRange, R))
{
@@ -4199,7 +4611,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges))
/**
Returns the length of this range. Defined only if all ranges define
- $(D length).
+ `length`.
*/
static if (allSatisfy!(hasLength, R))
{
@@ -4242,7 +4654,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges))
}
/**
- Returns the $(D n)th element in the composite range. Defined if all
+ Returns the `n`th element in the composite range. Defined if all
ranges offer random access.
*/
static if (allSatisfy!(isRandomAccessRange, R))
@@ -4257,7 +4669,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges))
}
/**
- Assigns to the $(D n)th element in the composite range. Defined if
+ Assigns to the `n`th element in the composite range. Defined if
all ranges offer random access.
*/
static if (allSatisfy!(hasAssignableElements, R))
@@ -4273,7 +4685,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges))
}
/**
- Destructively reads the $(D n)th element in the composite
+ Destructively reads the `n`th element in the composite
range. Defined if all ranges offer random access.
*/
static if (allSatisfy!(hasMobileElements, R))
@@ -4294,22 +4706,75 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges))
auto zip(Ranges...)(Ranges ranges)
if (Ranges.length && allSatisfy!(isInputRange, Ranges))
{
- return Zip!Ranges(ranges);
+ import std.meta : anySatisfy, templateOr;
+ static if (allSatisfy!(isInfinite, Ranges) || Ranges.length == 1)
+ {
+ return ZipShortest!(Ranges)(ranges);
+ }
+ else static if (allSatisfy!(isBidirectionalRange, Ranges))
+ {
+ static if (allSatisfy!(templateOr!(isInfinite, hasLength), Ranges)
+ && allSatisfy!(templateOr!(isInfinite, hasSlicing), Ranges)
+ && allSatisfy!(isBidirectionalRange, staticMap!(Take, Ranges)))
+ {
+ // If all the ranges are bidirectional, if possible slice them to
+ // the same length to simplify the implementation.
+ static assert(anySatisfy!(hasLength, Ranges));
+ static foreach (i, Range; Ranges)
+ static if (hasLength!Range)
+ {
+ static if (!is(typeof(minLen) == size_t))
+ size_t minLen = ranges[i].length;
+ else
+ {{
+ const x = ranges[i].length;
+ if (x < minLen) minLen = x;
+ }}
+ }
+ import std.format : format;
+ static if (!anySatisfy!(isInfinite, Ranges))
+ return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~
+ `(%(ranges[%s][0 .. minLen]%|, %))`.format(iota(0, Ranges.length)));
+ else
+ return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~
+ `(%(take(ranges[%s], minLen)%|, %))`.format(iota(0, Ranges.length)));
+ }
+ else static if (allSatisfy!(isRandomAccessRange, Ranges))
+ {
+ // We can't slice but we can still use random access to ensure
+ // "back" is retrieving the same index for each range.
+ return ZipShortest!(Ranges)(ranges);
+ }
+ else
+ {
+ // If bidirectional range operations would not be supported by
+ // ZipShortest that might have actually been a bug since Zip
+ // supported `back` without verifying that each range had the
+ // same length, but for the sake of backwards compatibility
+ // use the old Zip to continue supporting them.
+ return Zip!Ranges(ranges);
+ }
+ }
+ else
+ {
+ return ZipShortest!(Ranges)(ranges);
+ }
}
///
-pure @safe unittest
+@nogc nothrow pure @safe unittest
{
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;
// pairwise sum
- auto arr = [0, 1, 2];
- assert(zip(arr, arr.dropOne).map!"a[0] + a[1]".equal([1, 3]));
+ auto arr = only(0, 1, 2);
+ auto part1 = zip(arr, arr.dropOne).map!"a[0] + a[1]";
+ assert(part1.equal(only(1, 3)));
}
///
-pure @safe unittest
+nothrow pure @safe unittest
{
import std.conv : to;
@@ -4334,8 +4799,8 @@ pure @safe unittest
}
}
-/// $(D zip) is powerful - the following code sorts two arrays in parallel:
-pure @safe unittest
+/// `zip` is powerful - the following code sorts two arrays in parallel:
+nothrow pure @safe unittest
{
import std.algorithm.sorting : sort;
@@ -4356,8 +4821,8 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges))
}
/**
- Dictates how iteration in a $(D Zip) should stop. By default stop at
- the end of the shortest of all ranges.
+ Dictates how iteration in a $(LREF zip) and $(LREF lockstep) should stop.
+ By default stop at the end of the shortest of all ranges.
*/
enum StoppingPolicy
{
@@ -4369,7 +4834,333 @@ enum StoppingPolicy
requireSameLength,
}
-@system unittest
+///
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.exception : assertThrown;
+ import std.range.primitives;
+ import std.typecons : tuple;
+
+ auto a = [1, 2, 3];
+ auto b = [4, 5, 6, 7];
+
+ auto shortest = zip(StoppingPolicy.shortest, a, b);
+ assert(shortest.equal([
+ tuple(1, 4),
+ tuple(2, 5),
+ tuple(3, 6)
+ ]));
+
+ auto longest = zip(StoppingPolicy.longest, a, b);
+ assert(longest.equal([
+ tuple(1, 4),
+ tuple(2, 5),
+ tuple(3, 6),
+ tuple(0, 7)
+ ]));
+
+ auto same = zip(StoppingPolicy.requireSameLength, a, b);
+ same.popFrontN(3);
+ assertThrown!Exception(same.popFront);
+}
+
+/+
+Non-public. Like $(LREF Zip) with `StoppingPolicy.shortest`
+except it properly implements `back` and `popBack` in the
+case of uneven ranges or disables those operations when
+it is not possible to guarantee they are correct.
++/
+package template ZipShortest(Ranges...)
+if (Ranges.length && __traits(compiles,
+ {
+ static assert(allSatisfy!(isInputRange, Ranges));
+ }))
+{
+ alias ZipShortest = .ZipShortest!(
+ Ranges.length == 1 || allSatisfy!(isInfinite, Ranges)
+ ? Yes.allKnownSameLength
+ : No.allKnownSameLength,
+ Ranges);
+}
+/+ non-public, ditto +/
+package struct ZipShortest(Flag!"allKnownSameLength" allKnownSameLength, Ranges...)
+if (Ranges.length && allSatisfy!(isInputRange, Ranges))
+{
+ import std.format : format; //for generic mixins
+ import std.meta : anySatisfy, templateOr;
+ import std.typecons : Tuple;
+
+ deprecated("Use of an undocumented alias R.")
+ alias R = Ranges; // Unused here but defined in case library users rely on it.
+ private Ranges ranges;
+ alias ElementType = Tuple!(staticMap!(.ElementType, Ranges));
+
+ /+
+ Builds an object. Usually this is invoked indirectly by using the
+ $(LREF zip) function.
+ +/
+ this(Ranges rs)
+ {
+ ranges[] = rs[];
+ }
+
+ /+
+ Returns `true` if the range is at end.
+ +/
+ static if (allKnownSameLength ? anySatisfy!(isInfinite, Ranges)
+ : allSatisfy!(isInfinite, Ranges))
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ static if (allKnownSameLength)
+ {
+ return ranges[0].empty;
+ }
+ else
+ {
+ static foreach (i; 0 .. Ranges.length)
+ {
+ if (ranges[i].empty)
+ return true;
+ }
+ return false;
+ }
+ }
+ }
+
+ /+
+ Forward range primitive. Only present if each constituent range is a
+ forward range.
+ +/
+ static if (allSatisfy!(isForwardRange, Ranges))
+ @property typeof(this) save()
+ {
+ return mixin(`typeof(return)(%(ranges[%s].save%|, %))`.format(iota(0, Ranges.length)));
+ }
+
+ /+
+ Returns the current iterated element.
+ +/
+ @property ElementType front()
+ {
+ return mixin(`typeof(return)(%(ranges[%s].front%|, %))`.format(iota(0, Ranges.length)));
+ }
+
+ /+
+ Sets the front of all iterated ranges. Only present if each constituent
+ range has assignable elements.
+ +/
+ static if (allSatisfy!(hasAssignableElements, Ranges))
+ @property void front()(ElementType v)
+ {
+ static foreach (i; 0 .. Ranges.length)
+ ranges[i].front = v[i];
+ }
+
+ /+
+ Moves out the front. Present if each constituent range has mobile elements.
+ +/
+ static if (allSatisfy!(hasMobileElements, Ranges))
+ ElementType moveFront()()
+ {
+ return mixin(`typeof(return)(%(ranges[%s].moveFront()%|, %))`.format(iota(0, Ranges.length)));
+ }
+
+ private enum bool isBackWellDefined = allSatisfy!(isBidirectionalRange, Ranges)
+ && (allKnownSameLength
+ || allSatisfy!(isRandomAccessRange, Ranges)
+ // Could also add the case where there is one non-infinite bidirectional
+ // range that defines `length` and all others are infinite random access
+ // ranges. Adding this would require appropriate branches in
+ // back/moveBack/popBack.
+ );
+
+ /+
+ Returns the rightmost element. Present if all constituent ranges are
+ bidirectional and either there is a compile-time guarantee that all
+ ranges have the same length (in `allKnownSameLength`) or all ranges
+ provide random access to elements.
+ +/
+ static if (isBackWellDefined)
+ @property ElementType back()
+ {
+ static if (allKnownSameLength)
+ {
+ return mixin(`typeof(return)(%(ranges[%s].back()%|, %))`.format(iota(0, Ranges.length)));
+ }
+ else
+ {
+ const backIndex = length - 1;
+ return mixin(`typeof(return)(%(ranges[%s][backIndex]%|, %))`.format(iota(0, Ranges.length)));
+ }
+ }
+
+ /+
+ Moves out the back. Present if `back` is defined and
+ each constituent range has mobile elements.
+ +/
+ static if (isBackWellDefined && allSatisfy!(hasMobileElements, Ranges))
+ ElementType moveBack()()
+ {
+ static if (allKnownSameLength)
+ {
+ return mixin(`typeof(return)(%(ranges[%s].moveBack()%|, %))`.format(iota(0, Ranges.length)));
+ }
+ else
+ {
+ const backIndex = length - 1;
+ return mixin(`typeof(return)(%(ranges[%s].moveAt(backIndex)%|, %))`.format(iota(0, Ranges.length)));
+ }
+ }
+
+ /+
+ Sets the rightmost element. Only present if `back` is defined and
+ each constituent range has assignable elements.
+ +/
+ static if (isBackWellDefined && allSatisfy!(hasAssignableElements, Ranges))
+ @property void back()(ElementType v)
+ {
+ static if (allKnownSameLength)
+ {
+ static foreach (i; 0 .. Ranges.length)
+ ranges[i].back = v[i];
+ }
+ else
+ {
+ const backIndex = length - 1;
+ static foreach (i; 0 .. Ranges.length)
+ ranges[i][backIndex] = v[i];
+ }
+ }
+
+ /+
+ Calls `popFront` on each constituent range.
+ +/
+ void popFront()
+ {
+ static foreach (i; 0 .. Ranges.length)
+ ranges[i].popFront();
+ }
+
+ /+
+ Pops the rightmost element. Present if `back` is defined.
+ +/
+ static if (isBackWellDefined)
+ void popBack()
+ {
+ static if (allKnownSameLength)
+ {
+ static foreach (i; 0 .. Ranges.length)
+ ranges[i].popBack;
+ }
+ else
+ {
+ const len = length;
+ static foreach (i; 0 .. Ranges.length)
+ static if (!isInfinite!(Ranges[i]))
+ if (ranges[i].length == len)
+ ranges[i].popBack();
+ }
+ }
+
+ /+
+ Returns the length of this range. Defined if at least one
+ constituent range defines `length` and the other ranges all also
+ define `length` or are infinite, or if at least one constituent
+ range defines `length` and there is a compile-time guarantee that
+ all ranges have the same length (in `allKnownSameLength`).
+ +/
+ static if (allKnownSameLength
+ ? anySatisfy!(hasLength, Ranges)
+ : (anySatisfy!(hasLength, Ranges)
+ && allSatisfy!(templateOr!(isInfinite, hasLength), Ranges)))
+ {
+ @property size_t length()
+ {
+ static foreach (i, Range; Ranges)
+ {
+ static if (hasLength!Range)
+ {
+ static if (!is(typeof(minLen) == size_t))
+ size_t minLen = ranges[i].length;
+ else static if (!allKnownSameLength)
+ {{
+ const x = ranges[i].length;
+ if (x < minLen) minLen = x;
+ }}
+ }
+ }
+ return minLen;
+ }
+
+ alias opDollar = length;
+ }
+
+ /+
+ Returns a slice of the range. Defined if all constituent ranges
+ support slicing.
+ +/
+ static if (allSatisfy!(hasSlicing, Ranges))
+ {
+ // Note: we will know that all elements of the resultant range
+ // will have the same length but we cannot change `allKnownSameLength`
+ // because the `hasSlicing` predicate tests that the result returned
+ // by `opSlice` has the same type as the receiver.
+ auto opSlice()(size_t from, size_t to)
+ {
+ //(ranges[0][from .. to], ranges[1][from .. to], ...)
+ enum sliceArgs = `(%(ranges[%s][from .. to]%|, %))`.format(iota(0, Ranges.length));
+ static if (__traits(compiles, mixin(`typeof(this)`~sliceArgs)))
+ return mixin(`typeof(this)`~sliceArgs);
+ else
+ // The type is different anyway so we might as well
+ // explicitly set allKnownSameLength.
+ return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`
+ ~sliceArgs);
+ }
+ }
+
+ /+
+ Returns the `n`th element in the composite range. Defined if all
+ constituent ranges offer random access.
+ +/
+ static if (allSatisfy!(isRandomAccessRange, Ranges))
+ ElementType opIndex()(size_t n)
+ {
+ return mixin(`typeof(return)(%(ranges[%s][n]%|, %))`.format(iota(0, Ranges.length)));
+ }
+
+ /+
+ Sets the `n`th element in the composite range. Defined if all
+ constituent ranges offer random access and have assignable elements.
+ +/
+ static if (allSatisfy!(isRandomAccessRange, Ranges)
+ && allSatisfy!(hasAssignableElements, Ranges))
+ void opIndexAssign()(ElementType v, size_t n)
+ {
+ static foreach (i; 0 .. Ranges.length)
+ ranges[i][n] = v[i];
+ }
+
+ /+
+ Destructively reads the `n`th element in the composite
+ range. Defined if all constituent ranges offer random
+ access and have mobile elements.
+ +/
+ static if (allSatisfy!(isRandomAccessRange, Ranges)
+ && allSatisfy!(hasMobileElements, Ranges))
+ ElementType moveAt()(size_t n)
+ {
+ return mixin(`typeof(return)(%(ranges[%s].moveAt(n)%|, %))`.format(iota(0, Ranges.length)));
+ }
+}
+
+pure @system unittest
{
import std.algorithm.comparison : equal;
import std.algorithm.iteration : filter, map;
@@ -4387,13 +5178,15 @@ enum StoppingPolicy
}
swap(a[0], a[1]);
- auto z = zip(a, b);
+ {
+ auto z = zip(a, b);
+ }
//swap(z.front(), z.back());
sort!("a[0] < b[0]")(zip(a, b));
assert(a == [1, 2, 3]);
assert(b == [2.0, 1.0, 3.0]);
- z = zip(StoppingPolicy.requireSameLength, a, b);
+ auto z = zip(StoppingPolicy.requireSameLength, a, b);
assertNotThrown(z.popBack());
assertNotThrown(z.popBack());
assertNotThrown(z.popBack());
@@ -4449,10 +5242,18 @@ enum StoppingPolicy
assert(zLongest.empty);
}
- // BUG 8900
+ // https://issues.dlang.org/show_bug.cgi?id=8900
assert(zip([1, 2], repeat('a')).array == [tuple(1, 'a'), tuple(2, 'a')]);
assert(zip(repeat('a'), [1, 2]).array == [tuple('a', 1), tuple('a', 2)]);
+ // https://issues.dlang.org/show_bug.cgi?id=18524
+ // moveBack instead performs moveFront
+ {
+ auto r = zip([1,2,3]);
+ assert(r.moveBack()[0] == 3);
+ assert(r.moveFront()[0] == 1);
+ }
+
// Doesn't work yet. Issues w/ emplace.
// static assert(is(Zip!(immutable int[], immutable float[])));
@@ -4491,7 +5292,7 @@ enum StoppingPolicy
+/
}
-pure @safe unittest
+nothrow pure @safe unittest
{
import std.algorithm.sorting : sort;
@@ -4505,7 +5306,7 @@ pure @safe unittest
assert(b == [6, 5, 2, 1, 3]);
}
-@safe pure unittest
+nothrow pure @safe unittest
{
import std.algorithm.comparison : equal;
import std.typecons : tuple;
@@ -4520,7 +5321,7 @@ pure @safe unittest
assert(equal(z2, [tuple(7, 0L)]));
}
-// Text for Issue 11196
+// Test for https://issues.dlang.org/show_bug.cgi?id=11196
@safe pure unittest
{
import std.exception : assertThrown;
@@ -4531,7 +5332,8 @@ pure @safe unittest
assertThrown(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).front);
}
-@safe pure unittest //12007
+// https://issues.dlang.org/show_bug.cgi?id=12007
+@nogc nothrow @safe pure unittest
{
static struct R
{
@@ -4546,7 +5348,7 @@ pure @safe unittest
assert(z.save == z);
}
-pure @system unittest
+nothrow pure @system unittest
{
import std.typecons : tuple;
@@ -4559,6 +5361,98 @@ pure @system unittest
assert(z2.front == tuple(0,1));
}
+@nogc nothrow pure @safe unittest
+{
+ // Test zip's `back` and `length` with non-equal ranges.
+ static struct NonSliceableRandomAccess
+ {
+ private int[] a;
+ @property ref front()
+ {
+ return a.front;
+ }
+ @property ref back()
+ {
+ return a.back;
+ }
+ ref opIndex(size_t i)
+ {
+ return a[i];
+ }
+ void popFront()
+ {
+ a.popFront();
+ }
+ void popBack()
+ {
+ a.popBack();
+ }
+ auto moveFront()
+ {
+ return a.moveFront();
+ }
+ auto moveBack()
+ {
+ return a.moveBack();
+ }
+ auto moveAt(size_t i)
+ {
+ return a.moveAt(i);
+ }
+ bool empty() const
+ {
+ return a.empty;
+ }
+ size_t length() const
+ {
+ return a.length;
+ }
+ typeof(this) save()
+ {
+ return this;
+ }
+ }
+ static assert(isRandomAccessRange!NonSliceableRandomAccess);
+ static assert(!hasSlicing!NonSliceableRandomAccess);
+ static foreach (iteration; 0 .. 2)
+ {{
+ int[5] data = [101, 102, 103, 201, 202];
+ static if (iteration == 0)
+ {
+ auto r1 = NonSliceableRandomAccess(data[0 .. 3]);
+ auto r2 = NonSliceableRandomAccess(data[3 .. 5]);
+ }
+ else
+ {
+ auto r1 = data[0 .. 3];
+ auto r2 = data[3 .. 5];
+ }
+ auto z = zip(r1, r2);
+ static assert(isRandomAccessRange!(typeof(z)));
+ assert(z.length == 2);
+ assert(z.back[0] == 102 && z.back[1] == 202);
+ z.back = typeof(z.back)(-102, -202);// Assign to back.
+ assert(z.back[0] == -102 && z.back[1] == -202);
+ z.popBack();
+ assert(z.length == 1);
+ assert(z.back[0] == 101 && z.back[1] == 201);
+ z.front = typeof(z.front)(-101, -201);
+ assert(z.moveBack() == typeof(z.back)(-101, -201));
+ z.popBack();
+ assert(z.empty);
+ }}
+}
+
+@nogc nothrow pure @safe unittest
+{
+ // Test opSlice on infinite `zip`.
+ auto z = zip(repeat(1), repeat(2));
+ assert(hasSlicing!(typeof(z)));
+ auto slice = z[10 .. 20];
+ assert(slice.length == 10);
+ static assert(!is(typeof(z) == typeof(slice)));
+}
+
/*
Generate lockstep's opApply function as a mixin string.
If withIndex is true prepend a size_t index to the delegate.
@@ -4647,21 +5541,25 @@ private string lockstepMixin(Ranges...)(bool withIndex, bool reverse)
}
/**
- Iterate multiple ranges in lockstep using a $(D foreach) loop. In contrast to
+ Iterate multiple ranges in lockstep using a `foreach` loop. In contrast to
$(LREF zip) it allows reference access to its elements. If only a single
- range is passed in, the $(D Lockstep) aliases itself away. If the
- ranges are of different lengths and $(D s) == $(D StoppingPolicy.shortest)
+ range is passed in, the `Lockstep` aliases itself away. If the
+ ranges are of different lengths and `s` == `StoppingPolicy.shortest`
stop after the shortest range is empty. If the ranges are of different
- lengths and $(D s) == $(D StoppingPolicy.requireSameLength), throw an
- exception. $(D s) may not be $(D StoppingPolicy.longest), and passing this
+ lengths and `s` == `StoppingPolicy.requireSameLength`, throw an
+ exception. `s` may not be `StoppingPolicy.longest`, and passing this
will throw an exception.
- Iterating over $(D Lockstep) in reverse and with an index is only possible
- when $(D s) == $(D StoppingPolicy.requireSameLength), in order to preserve
- indexes. If an attempt is made at iterating in reverse when $(D s) ==
- $(D StoppingPolicy.shortest), an exception will be thrown.
+ Iterating over `Lockstep` in reverse and with an index is only possible
+ when `s` == `StoppingPolicy.requireSameLength`, in order to preserve
+ indexes. If an attempt is made at iterating in reverse when `s` ==
+ `StoppingPolicy.shortest`, an exception will be thrown.
+
+ By default `StoppingPolicy` is set to `StoppingPolicy.shortest`.
- By default $(D StoppingPolicy) is set to $(D StoppingPolicy.shortest).
+ Limitations: The `pure`, `@safe`, `@nogc`, or `nothrow` attributes cannot be
+ inferred for `lockstep` iteration. $(LREF zip) can infer the first two due to
+ a different implementation.
See_Also: $(LREF zip)
@@ -4710,48 +5608,6 @@ private:
StoppingPolicy _stoppingPolicy;
}
-string lockstepReverseFailMixin(Ranges...)(bool withIndex)
-{
- import std.format : format;
- string[] params;
- string message;
-
- if (withIndex)
- {
- message = "Indexed reverse iteration with lockstep is only supported"
- ~"if all ranges are bidirectional and have a length.\n";
- }
- else
- {
- message = "Reverse iteration with lockstep is only supported if all ranges are bidirectional.\n";
- }
-
- if (withIndex)
- {
- params ~= "size_t";
- }
-
- foreach (idx, Range; Ranges)
- {
- params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx);
- }
-
- return format(
- q{
- int opApplyReverse()(scope int delegate(%s) dg)
- {
- static assert(false, "%s");
- }
- }, params.join(", "), message);
-}
-
-// For generic programming, make sure Lockstep!(Range) is well defined for a
-// single range.
-template Lockstep(Range)
-{
- alias Lockstep = Range;
-}
-
/// Ditto
Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges)
if (allSatisfy!(isInputRange, Ranges))
@@ -4789,7 +5645,8 @@ if (allSatisfy!(isInputRange, Ranges))
}
}
-@system unittest // Bugzilla 15860: foreach_reverse on lockstep
+// https://issues.dlang.org/show_bug.cgi?id=15860: foreach_reverse on lockstep
+@system unittest
{
auto arr1 = [0, 1, 2, 3];
auto arr2 = [4, 5, 6, 7];
@@ -4871,8 +5728,9 @@ if (allSatisfy!(isInputRange, Ranges))
assert(0);
} catch (Exception) {}
- // Just make sure 1-range case instantiates. This hangs the compiler
- // when no explicit stopping policy is specified due to Bug 4652.
+ // Just make sure 1-range case instantiates. This hangs the compiler
+ // when no explicit stopping policy is specified due to
+ // https://issues.dlang.org/show_bug.cgi?id=4652
auto stuff = lockstep([1,2,3,4,5], StoppingPolicy.shortest);
foreach (i, a; stuff)
{
@@ -4937,15 +5795,57 @@ if (allSatisfy!(isInputRange, Ranges))
}));
}
+private string lockstepReverseFailMixin(Ranges...)(bool withIndex)
+{
+ import std.format : format;
+ string[] params;
+ string message;
+
+ if (withIndex)
+ {
+ message = "Indexed reverse iteration with lockstep is only supported"
+ ~"if all ranges are bidirectional and have a length.\n";
+ }
+ else
+ {
+ message = "Reverse iteration with lockstep is only supported if all ranges are bidirectional.\n";
+ }
+
+ if (withIndex)
+ {
+ params ~= "size_t";
+ }
+
+ foreach (idx, Range; Ranges)
+ {
+ params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx);
+ }
+
+ return format(
+ q{
+ int opApplyReverse()(scope int delegate(%s) dg)
+ {
+ static assert(false, "%s");
+ }
+ }, params.join(", "), message);
+}
+
+// For generic programming, make sure Lockstep!(Range) is well defined for a
+// single range.
+template Lockstep(Range)
+{
+ alias Lockstep = Range;
+}
+
/**
Creates a mathematical sequence given the initial values and a
recurrence function that computes the next value from the existing
values. The sequence comes in the form of an infinite forward
-range. The type $(D Recurrence) itself is seldom used directly; most
+range. The type `Recurrence` itself is seldom used directly; most
often, recurrences are obtained by calling the function $(D
recurrence).
-When calling $(D recurrence), the function that computes the next
+When calling `recurrence`, the function that computes the next
value is specified as a template argument, and the initial values in
the recurrence are passed as regular arguments. For example, in a
Fibonacci sequence, there are two initial values (and therefore a
@@ -4956,17 +5856,17 @@ The signature of this function should be:
----
auto fun(R)(R state, size_t n)
----
-where $(D n) will be the index of the current value, and $(D state) will be an
+where `n` will be the index of the current value, and `state` will be an
opaque state vector that can be indexed with array-indexing notation
-$(D state[i]), where valid values of $(D i) range from $(D (n - 1)) to
+`state[i]`, where valid values of `i` range from $(D (n - 1)) to
$(D (n - State.length)).
-If the function is passed in string form, the state has name $(D "a")
-and the zero-based index in the recurrence has name $(D "n"). The
-given string must return the desired value for $(D a[n]) given $(D a[n
-- 1]), $(D a[n - 2]), $(D a[n - 3]),..., $(D a[n - stateSize]). The
+If the function is passed in string form, the state has name `"a"`
+and the zero-based index in the recurrence has name `"n"`. The
+given string must return the desired value for `a[n]` given
+`a[n - 1]`, `a[n - 2]`, `a[n - 3]`,..., `a[n - stateSize]`. The
state size is dictated by the number of arguments passed to the call
-to $(D recurrence). The $(D Recurrence) struct itself takes care of
+to `recurrence`. The `Recurrence` struct itself takes care of
managing the recurrence's state and shifting it appropriately.
*/
struct Recurrence(alias fun, StateType, size_t stateSize)
@@ -5006,7 +5906,7 @@ struct Recurrence(alias fun, StateType, size_t stateSize)
}
///
-@safe unittest
+pure @safe nothrow unittest
{
import std.algorithm.comparison : equal;
@@ -5042,7 +5942,7 @@ recurrence(alias fun, State...)(State initial)
return typeof(return)(state);
}
-@safe unittest
+pure @safe nothrow unittest
{
import std.algorithm.comparison : equal;
@@ -5064,15 +5964,15 @@ recurrence(alias fun, State...)(State initial)
}
/**
- $(D Sequence) is similar to $(D Recurrence) except that iteration is
+ `Sequence` is similar to `Recurrence` except that iteration is
presented in the so-called $(HTTP en.wikipedia.org/wiki/Closed_form,
- closed form). This means that the $(D n)th element in the series is
- computable directly from the initial values and $(D n) itself. This
- implies that the interface offered by $(D Sequence) is a random-access
- range, as opposed to the regular $(D Recurrence), which only offers
+ closed form). This means that the `n`th element in the series is
+ computable directly from the initial values and `n` itself. This
+ implies that the interface offered by `Sequence` is a random-access
+ range, as opposed to the regular `Recurrence`, which only offers
forward iteration.
- The state of the sequence is stored as a $(D Tuple) so it can be
+ The state of the sequence is stored as a `Tuple` so it can be
heterogeneous.
*/
struct Sequence(alias fun, State)
@@ -5114,7 +6014,7 @@ public:
"Attempting to slice a Sequence with a larger first argument than the second."
);
}
- body
+ do
{
return typeof(this)(_state, _n + lower).take(upper - lower);
}
@@ -5143,7 +6043,7 @@ auto sequence(alias fun, State...)(State args)
}
/// Odd numbers, using function in string form:
-@safe unittest
+pure @safe nothrow @nogc unittest
{
auto odds = sequence!("a[0] + n * a[1]")(1, 2);
assert(odds.front == 1);
@@ -5154,7 +6054,7 @@ auto sequence(alias fun, State...)(State args)
}
/// Triangular numbers, using function in lambda form:
-@safe unittest
+pure @safe nothrow @nogc unittest
{
auto tri = sequence!((a,n) => n*(n+1)/2)();
@@ -5167,9 +6067,11 @@ auto sequence(alias fun, State...)(State args)
}
/// Fibonacci numbers, using function in explicit form:
-@safe unittest
+@safe nothrow @nogc unittest
{
- import std.math : pow, round, sqrt;
+ import std.math.exponential : pow;
+ import std.math.rounding : round;
+ import std.math.algebraic : sqrt;
static ulong computeFib(S)(S state, size_t n)
{
// Binet's formula
@@ -5189,7 +6091,7 @@ auto sequence(alias fun, State...)(State args)
assert(fib[9] == 55);
}
-@safe unittest
+pure @safe nothrow @nogc unittest
{
import std.typecons : Tuple, tuple;
auto y = Sequence!("a[0] + n * a[1]", Tuple!(int, int))(tuple(0, 4));
@@ -5210,7 +6112,7 @@ auto sequence(alias fun, State...)(State args)
}
}
-@safe unittest
+pure @safe nothrow @nogc unittest
{
import std.algorithm.comparison : equal;
@@ -5221,21 +6123,21 @@ auto sequence(alias fun, State...)(State args)
//since they'll both just forward to opSlice, making the tests irrelevant
// static slicing tests
- assert(equal(odds[0 .. 5], [1, 3, 5, 7, 9]));
- assert(equal(odds[3 .. 7], [7, 9, 11, 13]));
+ assert(equal(odds[0 .. 5], only(1, 3, 5, 7, 9)));
+ assert(equal(odds[3 .. 7], only(7, 9, 11, 13)));
// relative slicing test, testing slicing is NOT agnostic of state
auto odds_less5 = odds.drop(5); //this should actually call odds[5 .. $]
- assert(equal(odds_less5[0 .. 3], [11, 13, 15]));
+ assert(equal(odds_less5[0 .. 3], only(11, 13, 15)));
assert(equal(odds_less5[0 .. 10], odds[5 .. 15]));
//Infinite slicing tests
odds = odds[10 .. $];
- assert(equal(odds.take(3), [21, 23, 25]));
+ assert(equal(odds.take(3), only(21, 23, 25)));
}
-// Issue 5036
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=5036
+pure @safe nothrow unittest
{
auto s = sequence!((a, n) => new int)(0);
assert(s.front != s.front); // no caching
@@ -5253,21 +6155,21 @@ auto sequence(alias fun, State...)(State args)
step = The value to add to the current value at each iteration.
Returns:
- A range that goes through the numbers $(D begin), $(D begin + step),
- $(D begin + 2 * step), $(D ...), up to and excluding $(D end).
+ A range that goes through the numbers `begin`, $(D begin + step),
+ $(D begin + 2 * step), `...`, up to and excluding `end`.
The two-argument overloads have $(D step = 1). If $(D begin < end && step <
0) or $(D begin > end && step > 0) or $(D begin == end), then an empty range
is returned. If $(D step == 0) then $(D begin == end) is an error.
For built-in types, the range returned is a random access range. For
- user-defined types that support $(D ++), the range is an input
+ user-defined types that support `++`, the range is an input
range.
- An integral iota also supports $(D in) operator from the right. It takes
+ An integral iota also supports `in` operator from the right. It takes
the stepping into account, the integral won't be considered
contained if it falls between two consecutive values of the range.
- $(D contains) does the same as in, but from lefthand side.
+ `contains` does the same as in, but from lefthand side.
Example:
---
@@ -5429,7 +6331,8 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
{
if (current < pastLast)
{
- assert(unsigned(pastLast - current) <= size_t.max);
+ assert(unsigned(pastLast - current) <= size_t.max,
+ "`iota` range is too long");
this.current = current;
this.pastLast = pastLast;
@@ -5442,17 +6345,34 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
}
@property bool empty() const { return current == pastLast; }
- @property inout(Value) front() inout { assert(!empty); return current; }
- void popFront() { assert(!empty); ++current; }
+ @property inout(Value) front() inout
+ {
+ assert(!empty, "Attempt to access `front` of empty `iota` range");
+ return current;
+ }
+ void popFront()
+ {
+ assert(!empty, "Attempt to `popFront` of empty `iota` range");
+ ++current;
+ }
- @property inout(Value) back() inout { assert(!empty); return cast(inout(Value))(pastLast - 1); }
- void popBack() { assert(!empty); --pastLast; }
+ @property inout(Value) back() inout
+ {
+ assert(!empty, "Attempt to access `back` of empty `iota` range");
+ return cast(inout(Value))(pastLast - 1);
+ }
+ void popBack()
+ {
+ assert(!empty, "Attempt to `popBack` of empty `iota` range");
+ --pastLast;
+ }
@property auto save() { return this; }
inout(Value) opIndex(size_t n) inout
{
- assert(n < this.length);
+ assert(n < this.length,
+ "Attempt to read out-of-bounds index of `iota` range");
// Just cast to Value here because doing so gives overflow behavior
// consistent with calling popFront() n times.
@@ -5467,7 +6387,8 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
inout(Result) opSlice() inout { return this; }
inout(Result) opSlice(ulong lower, ulong upper) inout
{
- assert(upper >= lower && upper <= this.length);
+ assert(upper >= lower && upper <= this.length,
+ "Attempt to get out-of-bounds slice of `iota` range");
return cast(inout Result) Result(cast(Value)(current + lower),
cast(Value)(pastLast - (length - upper)));
@@ -5500,7 +6421,7 @@ in
assert(step != 0, "iota: step must not be 0");
assert((end - begin) / step >= 0, "iota: incorrect startup parameters");
}
-body
+do
{
alias Value = Unqual!(CommonType!(B, E, S));
static struct Result
@@ -5579,10 +6500,10 @@ body
}
///
-@safe unittest
+pure @safe unittest
{
import std.algorithm.comparison : equal;
- import std.math : approxEqual;
+ import std.math.operations : isClose;
auto r = iota(0, 10, 1);
assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
@@ -5596,11 +6517,12 @@ body
assert(r[2] == 6);
assert(!(2 in r));
auto rf = iota(0.0, 0.5, 0.1);
- assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
+ assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
}
-nothrow @nogc @safe unittest
+pure nothrow @nogc @safe unittest
{
+ import std.traits : Signed;
//float overloads use std.conv.to so can't be @nogc or nothrow
alias ssize_t = Signed!size_t;
assert(iota(ssize_t.max, 0, -1).length == ssize_t.max);
@@ -5620,7 +6542,7 @@ debug @system unittest
assertThrown!AssertError(iota(0f,1f,-0.1f));
}
-@system unittest
+pure @system nothrow unittest
{
int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
auto r1 = iota(a.ptr, a.ptr + a.length, 1);
@@ -5629,7 +6551,7 @@ debug @system unittest
assert(&a[4] in r1);
}
-@safe unittest
+pure @safe nothrow @nogc unittest
{
assert(iota(1UL, 0UL).length == 0);
assert(iota(1UL, 0UL, 1).length == 0);
@@ -5639,11 +6561,11 @@ debug @system unittest
assert(iota(ulong.max, 0).length == 0);
}
-@safe unittest
+pure @safe unittest
{
import std.algorithm.comparison : equal;
import std.algorithm.searching : count;
- import std.math : approxEqual, nextUp, nextDown;
+ import std.math.operations : isClose, nextUp, nextDown;
import std.meta : AliasSeq;
static assert(is(ElementType!(typeof(iota(0f))) == float));
@@ -5701,7 +6623,7 @@ debug @system unittest
assert(equal(rSlice, [3, 6]));
auto rf = iota(0.0, 0.5, 0.1);
- assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4][]));
+ assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4][]));
assert(rf.length == 5);
rf.popFront();
@@ -5709,35 +6631,35 @@ debug @system unittest
auto rfSlice = rf[1 .. 4];
assert(rfSlice.length == 3);
- assert(approxEqual(rfSlice, [0.2, 0.3, 0.4]));
+ assert(isClose(rfSlice, [0.2, 0.3, 0.4]));
rfSlice.popFront();
- assert(approxEqual(rfSlice[0], 0.3));
+ assert(isClose(rfSlice[0], 0.3));
rf.popFront();
assert(rf.length == 3);
rfSlice = rf[1 .. 3];
assert(rfSlice.length == 2);
- assert(approxEqual(rfSlice, [0.3, 0.4]));
- assert(approxEqual(rfSlice[0], 0.3));
+ assert(isClose(rfSlice, [0.3, 0.4]));
+ assert(isClose(rfSlice[0], 0.3));
// With something just above 0.5
rf = iota(0.0, nextUp(0.5), 0.1);
- assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4, 0.5][]));
+ assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4, 0.5][]));
rf.popBack();
assert(rf[rf.length - 1] == rf.back);
- assert(approxEqual(rf.back, 0.4));
+ assert(isClose(rf.back, 0.4));
assert(rf.length == 5);
// going down
rf = iota(0.0, -0.5, -0.1);
- assert(approxEqual(rf, [0.0, -0.1, -0.2, -0.3, -0.4][]));
+ assert(isClose(rf, [0.0, -0.1, -0.2, -0.3, -0.4][]));
rfSlice = rf[2 .. 5];
- assert(approxEqual(rfSlice, [-0.2, -0.3, -0.4]));
+ assert(isClose(rfSlice, [-0.2, -0.3, -0.4]));
rf = iota(0.0, nextDown(-0.5), -0.1);
- assert(approxEqual(rf, [0.0, -0.1, -0.2, -0.3, -0.4, -0.5][]));
+ assert(isClose(rf, [0.0, -0.1, -0.2, -0.3, -0.4, -0.5][]));
// iota of longs
auto rl = iota(5_000_000L);
@@ -5752,14 +6674,15 @@ debug @system unittest
assert(iota_of_longs_with_steps.length == 6);
assert(equal(iota_of_longs_with_steps, [50L, 60L, 70L, 80L, 90L, 100L]));
- // iota of unsigned zero length (issue 6222, actually trying to consume it
- // is the only way to find something is wrong because the public
- // properties are all correct)
+ // iota of unsigned zero length (https://issues.dlang.org/show_bug.cgi?id=6222)
+ // Actually trying to consume it is the only way to find something is wrong
+ // because the public properties are all correct.
auto iota_zero_unsigned = iota(0, 0u, 3);
assert(count(iota_zero_unsigned) == 0);
- // unsigned reverse iota can be buggy if .length doesn't take them into
- // account (issue 7982).
+ // https://issues.dlang.org/show_bug.cgi?id=7982
+ // unsigned reverse iota can be buggy if `.length` doesn't
+ // take them into account
assert(iota(10u, 0u, -1).length == 10);
assert(iota(10u, 0u, -2).length == 5);
assert(iota(uint.max, uint.max-10, -1).length == 10);
@@ -5775,17 +6698,17 @@ debug @system unittest
assert(!(int.max in iota(20u, 10u, -1)));
- // Issue 8920
- foreach (Type; AliasSeq!(byte, ubyte, short, ushort,
+ // https://issues.dlang.org/show_bug.cgi?id=8920
+ static foreach (Type; AliasSeq!(byte, ubyte, short, ushort,
int, uint, long, ulong))
- {
+ {{
Type val;
foreach (i; iota(cast(Type) 0, cast(Type) 10)) { val++; }
assert(val == 10);
- }
+ }}
}
-@safe unittest
+pure @safe nothrow unittest
{
import std.algorithm.mutation : copy;
auto idx = new size_t[100];
@@ -5795,11 +6718,11 @@ debug @system unittest
@safe unittest
{
import std.meta : AliasSeq;
- foreach (range; AliasSeq!(iota(2, 27, 4),
+ static foreach (range; AliasSeq!(iota(2, 27, 4),
iota(3, 9),
iota(2.7, 12.3, .1),
iota(3.2, 9.7)))
- {
+ {{
const cRange = range;
const e = cRange.empty;
const f = cRange.front;
@@ -5808,7 +6731,7 @@ debug @system unittest
const s1 = cRange[];
const s2 = cRange[0 .. 3];
const l = cRange.length;
- }
+ }}
}
@system unittest
@@ -5840,8 +6763,7 @@ debug @system unittest
}
}
-@nogc nothrow pure @safe
-unittest
+@nogc nothrow pure @safe unittest
{
{
ushort start = 0, end = 10, step = 2;
@@ -5864,7 +6786,7 @@ unittest
* operations.
*
* User-defined types such as $(REF BigInt, std,bigint) are also supported, as long
- * as they can be incremented with $(D ++) and compared with $(D <) or $(D ==).
+ * as they can be incremented with `++` and compared with `<` or `==`.
*/
/// ditto
auto iota(B, E)(B begin, E end)
@@ -5952,7 +6874,30 @@ enum TransverseOptions
checking was already done from the outside of the range.
*/
assumeNotJagged,
- }
+}
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.exception : assertThrown;
+
+ auto arr = [[1, 2], [3, 4, 5]];
+
+ auto r1 = arr.frontTransversal!(TransverseOptions.assumeJagged);
+ assert(r1.equal([1, 3]));
+
+ // throws on construction
+ assertThrown!Exception(arr.frontTransversal!(TransverseOptions.enforceNotJagged));
+
+ auto r2 = arr.frontTransversal!(TransverseOptions.assumeNotJagged);
+ assert(r2.equal([1, 3]));
+
+ // either assuming or checking for equal lengths makes
+ // the result a random access range
+ assert(r2[0] == 1);
+ static assert(!__traits(compiles, r1[0]));
+}
/**
Given a range of ranges, iterate transversally through the first
@@ -6059,7 +7004,7 @@ struct FrontTransversal(Ror,
}
/**
- Duplicates this $(D frontTransversal). Note that only the encapsulating
+ Duplicates this `frontTransversal`. Note that only the encapsulating
range of range will be duplicated. Underlying ranges will not be
duplicated.
*/
@@ -6139,19 +7084,10 @@ struct FrontTransversal(Ror,
_input[n].front = val;
}
}
- /// Ditto
- static if (hasLength!RangeOfRanges)
- {
- @property size_t length()
- {
- return _input.length;
- }
-
- alias opDollar = length;
- }
+ mixin ImplementLength!_input;
/**
- Slicing if offered if $(D RangeOfRanges) supports slicing and all the
+ Slicing if offered if `RangeOfRanges` supports slicing and all the
conditions for supporting indexing are met.
*/
static if (hasSlicing!RangeOfRanges)
@@ -6179,7 +7115,7 @@ FrontTransversal!(RangeOfRanges, opt) frontTransversal(
}
///
-@safe unittest
+pure @safe nothrow unittest
{
import std.algorithm.comparison : equal;
int[][] x = new int[][2];
@@ -6256,8 +7192,8 @@ FrontTransversal!(RangeOfRanges, opt) frontTransversal(
}
}
-// Issue 16363
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=16363
+pure @safe nothrow unittest
{
import std.algorithm.comparison : equal;
@@ -6268,21 +7204,28 @@ FrontTransversal!(RangeOfRanges, opt) frontTransversal(
static assert(isRandomAccessRange!(typeof(ft)));
}
-// Bugzilla 16442
-@safe unittest
+// https://issues.dlang.org/show_bug.cgi?id=16442
+pure @safe nothrow unittest
{
int[][] arr = [[], []];
- auto ft1 = frontTransversal!(TransverseOptions.assumeNotJagged)(arr);
- assert(ft1.empty);
+ auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(arr);
+ assert(ft.empty);
+}
+
+// ditto
+pure @safe unittest
+{
+ int[][] arr = [[], []];
- auto ft2 = frontTransversal!(TransverseOptions.enforceNotJagged)(arr);
- assert(ft2.empty);
+ auto ft = frontTransversal!(TransverseOptions.enforceNotJagged)(arr);
+ assert(ft.empty);
}
/**
Given a range of ranges, iterate transversally through the
- `n`th element of each of the enclosed ranges.
+ `n`th element of each of the enclosed ranges. This function
+ is similar to `unzip` in other languages.
Params:
opt = Controls the assumptions the function makes about the lengths
@@ -6471,19 +7414,10 @@ struct Transversal(Ror,
}
}
- /// Ditto
- static if (hasLength!RangeOfRanges)
- {
- @property size_t length()
- {
- return _input.length;
- }
-
- alias opDollar = length;
- }
+ mixin ImplementLength!_input;
/**
- Slicing if offered if $(D RangeOfRanges) supports slicing and all the
+ Slicing if offered if `RangeOfRanges` supports slicing and all the
conditions for supporting indexing are met.
*/
static if (hasSlicing!RangeOfRanges)
@@ -6518,7 +7452,17 @@ Transversal!(RangeOfRanges, opt) transversal
x[0] = [1, 2];
x[1] = [3, 4];
auto ror = transversal(x, 1);
- assert(equal(ror, [ 2, 4 ][]));
+ assert(equal(ror, [ 2, 4 ]));
+}
+
+/// The following code does a full unzip
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ int[][] y = [[1, 2, 3], [4, 5, 6]];
+ auto z = y.front.walkLength.iota.map!(i => transversal(y, i));
+ assert(equal!equal(z, [[1, 4], [2, 5], [3, 6]]));
}
@safe unittest
@@ -6581,16 +7525,26 @@ Transversal!(RangeOfRanges, opt) transversal
assert(mat1[1] == 10);
}
-struct Transposed(RangeOfRanges)
+struct Transposed(RangeOfRanges,
+ TransverseOptions opt = TransverseOptions.assumeJagged)
if (isForwardRange!RangeOfRanges &&
isInputRange!(ElementType!RangeOfRanges) &&
hasAssignableElements!RangeOfRanges)
{
- //alias ElementType = typeof(map!"a.front"(RangeOfRanges.init));
-
this(RangeOfRanges input)
{
this._input = input;
+ static if (opt == TransverseOptions.enforceNotJagged)
+ {
+ import std.exception : enforce;
+
+ if (empty) return;
+ immutable commonLength = _input.front.length;
+ foreach (e; _input)
+ {
+ enforce(e.length == commonLength);
+ }
+ }
}
@property auto front()
@@ -6618,10 +7572,13 @@ if (isForwardRange!RangeOfRanges &&
}
}
- // ElementType opIndex(size_t n)
- // {
- // return _input[n].front;
- // }
+ static if (isRandomAccessRange!(ElementType!RangeOfRanges))
+ {
+ auto ref opIndex(size_t n)
+ {
+ return transversal!opt(_input, n);
+ }
+ }
@property bool empty()
{
@@ -6633,11 +7590,6 @@ if (isForwardRange!RangeOfRanges &&
return true;
}
- @property Transposed save()
- {
- return Transposed(_input.save);
- }
-
auto opSlice() { return this; }
private:
@@ -6651,7 +7603,7 @@ private:
assert(transposed(ror).empty);
}
-// Issue 9507
+// https://issues.dlang.org/show_bug.cgi?id=9507
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -6663,16 +7615,61 @@ private:
]));
}
+// https://issues.dlang.org/show_bug.cgi?id=17742
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.algorithm.comparison : equal;
+ auto ror = 5.iota.map!(y => 5.iota.map!(x => x * y).array).array;
+ assert(ror[3][2] == 6);
+ auto result = transposed!(TransverseOptions.assumeNotJagged)(ror);
+ assert(result[2][3] == 6);
+
+ auto x = [[1,2,3],[4,5,6]];
+ auto y = transposed!(TransverseOptions.assumeNotJagged)(x);
+ assert(y.front.equal([1,4]));
+ assert(y[0].equal([1,4]));
+ assert(y[0][0] == 1);
+ assert(y[1].equal([2,5]));
+ assert(y[1][1] == 5);
+
+ auto yy = transposed!(TransverseOptions.enforceNotJagged)(x);
+ assert(yy.front.equal([1,4]));
+ assert(yy[0].equal([1,4]));
+ assert(yy[0][0] == 1);
+ assert(yy[1].equal([2,5]));
+ assert(yy[1][1] == 5);
+
+ auto z = x.transposed; // assumeJagged
+ assert(z.front.equal([1,4]));
+ assert(z[0].equal([1,4]));
+ assert(!is(typeof(z[0][0])));
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ auto r = [[1,2], [3], [4,5], [], [6]];
+ assertThrown(r.transposed!(TransverseOptions.enforceNotJagged));
+}
+
/**
Given a range of ranges, returns a range of ranges where the $(I i)'th subrange
contains the $(I i)'th elements of the original subranges.
+
+Params:
+ opt = Controls the assumptions the function makes about the lengths of the ranges (i.e. jagged or not)
+ rr = Range of ranges
*/
-Transposed!RangeOfRanges transposed(RangeOfRanges)(RangeOfRanges rr)
+Transposed!(RangeOfRanges, opt) transposed
+(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)
+(RangeOfRanges rr)
if (isForwardRange!RangeOfRanges &&
isInputRange!(ElementType!RangeOfRanges) &&
hasAssignableElements!RangeOfRanges)
{
- return Transposed!RangeOfRanges(rr);
+ return Transposed!(RangeOfRanges, opt)(rr);
}
///
@@ -6707,11 +7704,11 @@ if (isForwardRange!RangeOfRanges &&
}
}
-// Issue 8764
+// https://issues.dlang.org/show_bug.cgi?id=8764
@safe unittest
{
import std.algorithm.comparison : equal;
- ulong[1] t0 = [ 123 ];
+ ulong[] t0 = [ 123 ];
assert(!hasAssignableElements!(typeof(t0[].chunks(1))));
assert(!is(typeof(transposed(t0[].chunks(1)))));
@@ -6722,13 +7719,13 @@ if (isForwardRange!RangeOfRanges &&
}
/**
-This struct takes two ranges, $(D source) and $(D indices), and creates a view
-of $(D source) as if its elements were reordered according to $(D indices).
-$(D indices) may include only a subset of the elements of $(D source) and
+This struct takes two ranges, `source` and `indices`, and creates a view
+of `source` as if its elements were reordered according to `indices`.
+`indices` may include only a subset of the elements of `source` and
may also repeat elements.
-$(D Source) must be a random access range. The returned range will be
-bidirectional or random-access if $(D Indices) is bidirectional or
+`Source` must be a random access range. The returned range will be
+bidirectional or random-access if `Indices` is bidirectional or
random-access, respectively.
*/
struct Indexed(Source, Indices)
@@ -6837,16 +7834,7 @@ if (isRandomAccessRange!Source && isInputRange!Indices &&
}
}
- static if (hasLength!Indices)
- {
- /// Ditto
- @property size_t length()
- {
- return _indices.length;
- }
-
- alias opDollar = length;
- }
+ mixin ImplementLength!_indices;
static if (isRandomAccessRange!Indices)
{
@@ -6910,7 +7898,7 @@ if (isRandomAccessRange!Source && isInputRange!Indices &&
/**
Returns the physical index into the source range corresponding to a
given logical index. This is useful, for example, when indexing
- an $(D Indexed) without adding another layer of indirection.
+ an `Indexed` without adding another layer of indirection.
*/
size_t physicalIndex(size_t logicalIndex)
{
@@ -6981,13 +7969,13 @@ Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indice
}
/**
-This range iterates over fixed-sized chunks of size $(D chunkSize) of a
-$(D source) range. $(D Source) must be an input range. $(D chunkSize) must be
-greater than zero.
+This range iterates over fixed-sized chunks of size `chunkSize` of a
+`source` range. `Source` must be an $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
+`chunkSize` must be greater than zero.
-If $(D !isInfinite!Source) and $(D source.walkLength) is not evenly
-divisible by $(D chunkSize), the back element of this range will contain
-fewer than $(D chunkSize) elements.
+If `!isInfinite!Source` and `source.walkLength` is not evenly
+divisible by `chunkSize`, the back element of this range will contain
+fewer than `chunkSize` elements.
If `Source` is a forward range, the resulting range will be forward ranges as
well. Otherwise, the resulting chunks will be input ranges consuming the same
@@ -7049,7 +8037,7 @@ if (isInputRange!Source)
static if (hasLength!Source)
{
- /// Length. Only if $(D hasLength!Source) is $(D true)
+ /// Length. Only if `hasLength!Source` is `true`
@property size_t length()
{
// Note: _source.length + _chunkSize may actually overflow.
@@ -7070,7 +8058,7 @@ if (isInputRange!Source)
/**
Indexing and slicing operations. Provided only if
- $(D hasSlicing!Source) is $(D true).
+ `hasSlicing!Source` is `true`.
*/
auto opIndex(size_t index)
{
@@ -7180,7 +8168,7 @@ if (isInputRange!Source)
{
/**
Bidirectional range primitives. Provided only if both
- $(D hasSlicing!Source) and $(D hasLength!Source) are $(D true).
+ `hasSlicing!Source` and `hasLength!Source` are `true`.
*/
@property auto back()
{
@@ -7399,16 +8387,16 @@ if (isInputRange!Source)
/**
-This range splits a $(D source) range into $(D chunkCount) chunks of
-approximately equal length. $(D Source) must be a forward range with
+This range splits a `source` range into `chunkCount` chunks of
+approximately equal length. `Source` must be a forward range with
known length.
-Unlike $(LREF chunks), $(D evenChunks) takes a chunk count (not size).
+Unlike $(LREF chunks), `evenChunks` takes a chunk count (not size).
The returned range will contain zero or more $(D source.length /
chunkCount + 1) elements followed by $(D source.length / chunkCount)
elements. If $(D source.length < chunkCount), some chunks will be empty.
-$(D chunkCount) must not be zero, unless $(D source) is also empty.
+`chunkCount` must not be zero, unless `source` is also empty.
*/
struct EvenChunks(Source)
if (isForwardRange!Source && hasLength!Source)
@@ -7460,7 +8448,7 @@ if (isForwardRange!Source && hasLength!Source)
{
/**
Indexing, slicing and bidirectional operations and range primitives.
- Provided only if $(D hasSlicing!Source) is $(D true).
+ Provided only if `hasSlicing!Source` is `true`.
*/
auto opIndex(size_t index)
{
@@ -7590,46 +8578,117 @@ if (isForwardRange!Source && hasLength!Source)
assert(equal(chunks, [[1], [2], [3], [], []]));
}
-/*
+/**
A fixed-sized sliding window iteration
of size `windowSize` over a `source` range by a custom `stepSize`.
-The `Source` range must be at least an `ForwardRange` and
-the `windowSize` must be greater than zero.
+The `Source` range must be at least a $(REF_ALTTEXT ForwardRange, isForwardRange, std,range,primitives)
+and the `windowSize` must be greater than zero.
For `windowSize = 1` it splits the range into single element groups (aka `unflatten`)
For `windowSize = 2` it is similar to `zip(source, source.save.dropOne)`.
Params:
- f = If `Yes.withFewerElements` slide with fewer
- elements than `windowSize`. This can only happen if the initial range
- contains less elements than `windowSize`. In this case
- if `No.withFewerElements` an empty range will be returned.
+ f = Whether the last element has fewer elements than `windowSize`
+ it should be be ignored (`No.withPartial`) or added (`Yes.withPartial`)
source = Range from which the slide will be selected
windowSize = Sliding window size
stepSize = Steps between the windows (by default 1)
Returns: Range of all sliding windows with propagated bi-directionality,
- forwarding, conditional random access, and slicing.
+ forwarding, random access, and slicing.
+
+Note: To avoid performance overhead, $(REF_ALTTEXT bi-directionality, isBidirectionalRange, std,range,primitives)
+ is only available when $(REF hasSlicing, std,range,primitives)
+ and $(REF hasLength, std,range,primitives) are true.
See_Also: $(LREF chunks)
*/
-// Explicitly set to private to delay the release until 2.076
-private
-auto slide(Flag!"withFewerElements" f = Yes.withFewerElements,
+auto slide(Flag!"withPartial" f = Yes.withPartial,
Source)(Source source, size_t windowSize, size_t stepSize = 1)
- if (isForwardRange!Source)
+if (isForwardRange!Source)
{
return Slides!(f, Source)(source, windowSize, stepSize);
}
-private struct Slides(Flag!"withFewerElements" withFewerElements = Yes.withFewerElements, Source)
- if (isForwardRange!Source)
+/// Iterate over ranges with windows
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert([0, 1, 2, 3].slide(2).equal!equal(
+ [[0, 1], [1, 2], [2, 3]]
+ ));
+
+ assert(5.iota.slide(3).equal!equal(
+ [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
+ ));
+}
+
+/// set a custom stepsize (default 1)
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(6.iota.slide(1, 2).equal!equal(
+ [[0], [2], [4]]
+ ));
+
+ assert(6.iota.slide(2, 4).equal!equal(
+ [[0, 1], [4, 5]]
+ ));
+
+ assert(iota(7).slide(2, 2).equal!equal(
+ [[0, 1], [2, 3], [4, 5], [6]]
+ ));
+
+ assert(iota(12).slide(2, 4).equal!equal(
+ [[0, 1], [4, 5], [8, 9]]
+ ));
+}
+
+/// Allow the last slide to have fewer elements than windowSize
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(3.iota.slide!(No.withPartial)(4).empty);
+ assert(3.iota.slide!(Yes.withPartial)(4).equal!equal(
+ [[0, 1, 2]]
+ ));
+}
+
+/// Count all the possible substrings of length 2
+@safe pure nothrow unittest
+{
+ import std.algorithm.iteration : each;
+
+ int[dstring] d;
+ "AGAGA"d.slide!(Yes.withPartial)(2).each!(a => d[a]++);
+ assert(d == ["AG"d: 2, "GA"d: 2]);
+}
+
+/// withPartial only has an effect if last element in the range doesn't have the full size
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(5.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4]]));
+ assert(6.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5]]));
+ assert(7.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]]));
+
+ assert(5.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]]));
+ assert(6.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]]));
+ assert(7.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]]));
+}
+
+private struct Slides(Flag!"withPartial" withPartial = Yes.withPartial, Source)
+if (isForwardRange!Source)
{
private:
- Source _source;
- size_t _windowSize;
- size_t _stepSize;
+ Source source;
+ size_t windowSize;
+ size_t stepSize;
static if (hasLength!Source)
{
@@ -7637,17 +8696,18 @@ private:
}
else
{
- // if there's no information about the length, track needs to be kept manually
- Source _nextSource;
+ // If there's no information about the length, track needs to be kept manually
+ Source nextSource;
enum needsEndTracker = true;
}
bool _empty;
static if (hasSlicing!Source)
- {
enum hasSliceToEnd = hasSlicing!Source && is(typeof(Source.init[0 .. $]) == Source);
- }
+
+ static if (withPartial)
+ bool hasShownPartialBefore;
public:
/// Standard constructor
@@ -7655,82 +8715,124 @@ public:
{
assert(windowSize > 0, "windowSize must be greater than zero");
assert(stepSize > 0, "stepSize must be greater than zero");
- _source = source;
- _windowSize = windowSize;
- _stepSize = stepSize;
+ this.source = source;
+ this.windowSize = windowSize;
+ this.stepSize = stepSize;
static if (needsEndTracker)
{
- // _nextSource is used to "look into the future" and check for the end
- _nextSource = source.save;
- _nextSource.popFrontN(windowSize);
+ // `nextSource` is used to "look one step into the future" and check for the end
+ // this means `nextSource` is advanced by `stepSize` on every `popFront`
+ nextSource = source.save.drop(windowSize);
}
- static if (!withFewerElements)
+ if (source.empty)
+ {
+ _empty = true;
+ return;
+ }
+
+ static if (withPartial)
+ {
+ static if (needsEndTracker)
+ {
+ if (nextSource.empty)
+ hasShownPartialBefore = true;
+ }
+ else
+ {
+ if (source.length <= windowSize)
+ hasShownPartialBefore = true;
+ }
+
+ }
+ else
{
// empty source range is needed, s.t. length, slicing etc. works properly
static if (needsEndTracker)
{
- if (_nextSource.empty)
- _source = _nextSource;
+ if (nextSource.empty)
+ _empty = true;
}
else
{
- if (_source.length < windowSize)
- {
- static if (hasSlicing!Source)
- {
- // if possible use the faster opDollar overload
- static if (hasSliceToEnd)
- _source = _source[$ .. $];
- else
- _source = _source[_source.length .. _source.length];
- }
- else
- {
- _source.popFrontN(_source.length);
- }
- }
+ if (source.length < windowSize)
+ _empty = true;
}
}
-
- _empty = _source.empty;
}
/// Forward range primitives. Always present.
@property auto front()
{
- assert(!empty, "Attempting to access front on an empty slide");
+ assert(!empty, "Attempting to access front on an empty slide.");
static if (hasSlicing!Source && hasLength!Source)
{
- import std.algorithm.comparison : min;
- return _source[0 .. min(_windowSize, _source.length)];
+ static if (withPartial)
+ {
+ import std.algorithm.comparison : min;
+ return source[0 .. min(windowSize, source.length)];
+ }
+ else
+ {
+ assert(windowSize <= source.length, "The last element is smaller than the current windowSize.");
+ return source[0 .. windowSize];
+ }
}
else
{
- return _source.save.take(_windowSize);
+ static if (withPartial)
+ return source.save.take(windowSize);
+ else
+ return source.save.takeExactly(windowSize);
}
}
/// Ditto
void popFront()
{
- assert(!empty, "Attempting to call popFront() on an empty slide");
- _source.popFrontN(_stepSize);
+ assert(!empty, "Attempting to call popFront() on an empty slide.");
+ source.popFrontN(stepSize);
- // if the range has less elements than its window size,
- // we have seen the last full window (i.e. its empty)
- static if (needsEndTracker)
+ if (source.empty)
{
- if (_nextSource.empty)
+ _empty = true;
+ return;
+ }
+
+ static if (withPartial)
+ {
+ if (hasShownPartialBefore)
_empty = true;
+ }
+
+ static if (needsEndTracker)
+ {
+ // Check the upcoming slide
+ auto poppedElements = nextSource.popFrontN(stepSize);
+ static if (withPartial)
+ {
+ if (poppedElements < stepSize || nextSource.empty)
+ hasShownPartialBefore = true;
+ }
else
- _nextSource.popFrontN(_stepSize);
+ {
+ if (poppedElements < stepSize)
+ _empty = true;
+ }
}
else
{
- if (_source.length < _windowSize)
- _empty = true;
+ static if (withPartial)
+ {
+ if (source.length <= windowSize)
+ hasShownPartialBefore = true;
+ }
+ else
+ {
+ if (source.length < windowSize)
+ _empty = true;
+ }
}
}
@@ -7751,24 +8853,109 @@ public:
/// Ditto
@property typeof(this) save()
{
- return typeof(this)(_source.save, _windowSize, _stepSize);
+ return typeof(this)(source.save, windowSize, stepSize);
}
static if (hasLength!Source)
{
- /// Length. Only if $(D hasLength!Source) is $(D true)
+ // gaps between the last element and the end of the range
+ private size_t gap()
+ {
+ /*
+ * Note:
+ * - In the following `end` is the exclusive end as used in opSlice
+ * - For the trivial case with `stepSize = 1` `end` is at `len`:
+ *
+ * iota(4).slide(2) = [[0, 1], [1, 2], [2, 3]] (end = 4)
+ * iota(4).slide(3) = [[0, 1, 2], [1, 2, 3]] (end = 4)
+ *
+ * - For the non-trivial cases, we need to calculate the gap
+ * between `len` and `end` - this is the number of missing elements
+ * from the input range:
+ *
+ * iota(7).slide(2, 3) = [[0, 1], [3, 4]] || <gap: 2> 6
+ * iota(7).slide(2, 4) = [[0, 1], [4, 5]] || <gap: 1> 6
+ * iota(7).slide(1, 5) = [[0], [5]] || <gap: 1> 6
+ *
+ * As it can be seen `gap` can be at most `stepSize - 1`
+ * More generally the elements of the sliding window with
+ * `w = windowSize` and `s = stepSize` are:
+ *
+ * [0, w], [s, s + w], [2 * s, 2 * s + w], ... [n * s, n * s + w]
+ *
+ * We can thus calculate the gap between the `end` and `len` as:
+ *
+ * gap = len - (n * s + w) = len - w - (n * s)
+ *
+ * As we aren't interested in exact value of `n`, but the best
+ * minimal `gap` value, we can use modulo to "cut" `len - w` optimally:
+ *
+ * gap = len - w - (s - s ... - s) = (len - w) % s
+ *
+ * So for example:
+ *
+ * iota(7).slide(2, 3) = [[0, 1], [3, 4]]
+ * gap: (7 - 2) % 3 = 5 % 3 = 2
+ * end: 7 - 2 = 5
+ *
+ * iota(7).slide(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]]
+ * gap: (7 - 4) % 2 = 3 % 2 = 1
+ * end: 7 - 1 = 6
+ */
+ return (source.length - windowSize) % stepSize;
+ }
+
+ private size_t numberOfFullFrames()
+ {
+ /**
+ 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4)
+ 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (3)
+ 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (2)
+ 6.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5] (2)
+ 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (2)
+
+ As the last window is only added iff its complete,
+ we don't count the last window except if it's full due to integer rounding.
+ */
+ return 1 + (source.length - windowSize) / stepSize;
+ }
+
+ // Whether the last slide frame size is less than windowSize
+ private bool hasPartialElements()
+ {
+ static if (withPartial)
+ return gap != 0 && source.length > numberOfFullFrames * stepSize;
+ else
+ return 0;
+ }
+
+ /// Length. Only if `hasLength!Source` is `true`
@property size_t length()
{
- if (_source.length < _windowSize)
+ if (source.length < windowSize)
{
- static if (withFewerElements)
- return 1;
+ static if (withPartial)
+ return source.length > 0;
else
return 0;
}
else
{
- return (_source.length - _windowSize + _stepSize) / _stepSize;
+ /***
+ We bump the pointer by stepSize for every element.
+ If withPartial, we don't count the last element if its size
+ isn't windowSize
+
+ At most:
+ [p, p + stepSize, ..., p + stepSize * n]
+
+ 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4)
+ 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (4)
+ 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (3)
+ 7.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5, 6] (3)
+ 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (3)
+ */
+ return numberOfFullFrames + hasPartialElements;
}
}
}
@@ -7781,22 +8968,22 @@ public:
*/
auto opIndex(size_t index)
{
- immutable start = index * _stepSize;
+ immutable start = index * stepSize;
static if (isInfinite!Source)
{
- immutable end = start + _windowSize;
+ immutable end = start + windowSize;
}
else
{
import std.algorithm.comparison : min;
- immutable len = _source.length;
+ immutable len = source.length;
assert(start < len, "slide index out of bounds");
- immutable end = min(start + _windowSize, len);
+ immutable end = min(start + windowSize, len);
}
- return _source[start .. end];
+ return source[start .. end];
}
static if (!isInfinite!Source)
@@ -7805,39 +8992,83 @@ public:
typeof(this) opSlice(size_t lower, size_t upper)
{
import std.algorithm.comparison : min;
- assert(lower <= upper && upper <= length, "slide slicing index out of bounds");
- lower *= _stepSize;
- upper *= _stepSize;
+ assert(upper <= length, "slide slicing index out of bounds");
+ assert(lower <= upper, "slide slicing index out of bounds");
- immutable len = _source.length;
+ lower *= stepSize;
+ upper *= stepSize;
- /*
- * Notice that we only need to move for windowSize - 1 to the right:
- * source = [0, 1, 2, 3] (length: 4)
- * - source.slide(2) -> s = [[0, 1], [1, 2], [2, 3]]
- * right pos for s[0 .. 3]: 3 (upper) + 2 (windowSize) - 1 = 4
- *
- * - source.slide(3) -> s = [[0, 1, 2], [1, 2, 3]]
- * right pos for s[0 .. 2]: 2 (upper) + 3 (windowSize) - 1 = 4
- *
- * source = [0, 1, 2, 3, 4] (length: 5)
- * - source.slide(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]]
- * right pos for s[0 .. 2]: 2 (upper) + 4 (windowSize) - 1 = 5
- */
- return typeof(this)
- (_source[min(lower, len) .. min(upper + _windowSize - 1, len)],
- _windowSize, _stepSize);
+ immutable len = source.length;
+
+ static if (withPartial)
+ {
+ import std.algorithm.comparison : max;
+
+ if (lower == upper)
+ return this[$ .. $];
+
+ /*
+ A) If `stepSize` >= `windowSize` => `rightPos = upper`
+
+ [0, 1, 2, 3, 4, 5, 6].slide(2, 3) -> s = [[0, 1], [3, 4], [6]]
+ rightPos for s[0 .. 2]: (upper=2) * (stepSize=3) = 6
+ 6.iota.slide(2, 3) = [[0, 1], [3, 4]]
+
+ B) If `stepSize` < `windowSize` => add `windowSize - stepSize` to `upper`
+
+ [0, 1, 2, 3].slide(2) = [[0, 1], [1, 2], [2, 3]]
+ rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) = 1
+ 1.iota.slide(2) = [[0]]
+
+ rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) + (windowSize-stepSize=1) = 2
+ 1.iota.slide(2) = [[0, 1]]
+
+ More complex:
+
+ 20.iota.slide(7, 6)[0 .. 2]
+ rightPos: (upper=2) * (stepSize=6) = 12.iota
+ 12.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11]]
+
+ Now we add up for the difference between `windowSize` and `stepSize`:
+
+ rightPos: (upper=2) * (stepSize=6) + (windowSize-stepSize=1) = 13.iota
+ 13.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11, 12]]
+ */
+ immutable rightPos = min(len, upper + max(0, windowSize - stepSize));
+ }
+ else
+ {
+ /*
+ After we have normalized `lower` and `upper` by `stepSize`,
+ we only need to look at the case of `stepSize=1`.
+ As `leftPos`, is equal to `lower`, we will only look `rightPos`.
+ Notice that starting from `upper`,
+ we only need to move for `windowSize - 1` to the right:
+
+ - [0, 1, 2, 3].slide(2) -> s = [[0, 1], [1, 2], [2, 3]]
+ rightPos for s[0 .. 3]: (upper=3) + (windowSize=2) - 1 = 4
+
+ - [0, 1, 2, 3].slide(3) -> s = [[0, 1, 2], [1, 2, 3]]
+ rightPos for s[0 .. 2]: (upper=2) + (windowSize=3) - 1 = 4
+
+ - [0, 1, 2, 3, 4].slide(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]]
+ rightPos for s[0 .. 2]: (upper=2) + (windowSize=4) - 1 = 5
+ */
+ immutable rightPos = min(upper + windowSize - 1, len);
+ }
+
+ return typeof(this)(source[min(lower, len) .. rightPos], windowSize, stepSize);
}
}
else static if (hasSliceToEnd)
{
- //For slicing an infinite chunk, we need to slice the source to the infinite end.
+ // For slicing an infinite chunk, we need to slice the source to the infinite end.
auto opSlice(size_t lower, size_t upper)
{
assert(lower <= upper, "slide slicing index out of bounds");
- return typeof(this)(_source[lower * _stepSize .. $],
- _windowSize, _stepSize).takeExactly(upper - lower);
+ return typeof(this)(source[lower * stepSize .. $], windowSize, stepSize)
+ .takeExactly(upper - lower);
}
}
@@ -7853,15 +9084,15 @@ public:
//Slice to dollar
typeof(this) opSlice(size_t lower, DollarToken)
{
- return typeof(this)(_source[lower * _stepSize .. $], _windowSize, _stepSize);
+ return typeof(this)(source[lower * stepSize .. $], windowSize, stepSize);
}
}
}
else
{
- //Dollar token carries a static type, with no extra information.
- //It can lazily transform into _source.length on algorithmic
- //operations such as : slide[$/2, $-1];
+ // Dollar token carries a static type, with no extra information.
+ // It can lazily transform into source.length on algorithmic
+ // operations such as : slide[$/2, $-1];
private static struct DollarToken
{
private size_t _length;
@@ -7878,12 +9109,12 @@ public:
{
static if (hasSliceToEnd)
{
- return typeof(this)(_source[$ .. $], _windowSize, _stepSize);
+ return typeof(this)(source[$ .. $], windowSize, stepSize);
}
else
{
- immutable len = _source.length;
- return typeof(this)(_source[len .. len], _windowSize, _stepSize);
+ immutable len = source.length;
+ return typeof(this)(source[len .. len], windowSize, stepSize);
}
}
@@ -7892,15 +9123,15 @@ public:
{
import std.algorithm.comparison : min;
assert(lower <= length, "slide slicing index out of bounds");
- lower *= _stepSize;
+ lower *= stepSize;
static if (hasSliceToEnd)
{
- return typeof(this)(_source[min(lower, _source.length) .. $], _windowSize, _stepSize);
+ return typeof(this)(source[min(lower, source.length) .. $], windowSize, stepSize);
}
else
{
- immutable len = _source.length;
- return typeof(this)(_source[min(lower, len) .. len], _windowSize, _stepSize);
+ immutable len = source.length;
+ return typeof(this)(source[min(lower, len) .. len], windowSize, stepSize);
}
}
@@ -7925,54 +9156,20 @@ public:
assert(!empty, "Attempting to access front on an empty slide");
- immutable len = _source.length;
- /*
- * Note:
- * - `end` in the following is the exclusive end as used in opSlice
- * - For the trivial case with `stepSize = 1` `end` is at `len`:
- *
- * iota(4).slide(2) = [[0, 1], [1, 2], [2, 3] (end = 4)
- * iota(4).slide(3) = [[0, 1, 2], [1, 2, 3]] (end = 4)
- *
- * - For the non-trivial cases, we need to calculate the gap
- * between `len` and `end` - this is the number of missing elements
- * from the input range:
- *
- * iota(7).slide(2, 3) = [[0, 1], [3, 4]] || <gap: 2> 6
- * iota(7).slide(2, 4) = [[0, 1], [4, 5]] || <gap: 1> 6
- * iota(7).slide(1, 5) = [[0], [5]] || <gap: 1> 6
- *
- * As it can be seen `gap` can be at most `stepSize - 1`
- * More generally the elements of the sliding window with
- * `w = windowSize` and `s = stepSize` are:
- *
- * [0, w], [s, s + w], [2 * s, 2 * s + w], ... [n * s, n * s + w]
- *
- * We can thus calculate the gap between the `end` and `len` as:
- *
- * gap = len - (n * s + w) = len - w - (n * s)
- *
- * As we aren't interested in exact value of `n`, but the best
- * minimal `gap` value, we can use modulo to "cut" `len - w` optimally:
- *
- * gap = len - w - (s - s ... - s) = (len - w) % s
- *
- * So for example:
- *
- * iota(7).slide(2, 3) = [[0, 1], [3, 4]]
- * gap: (7 - 2) % 3 = 5 % 3 = 2
- * end: 7 - 2 = 5
- *
- * iota(7).slide(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]]
- * gap: (7 - 4) % 2 = 3 % 2 = 1
- * end: 7 - 1 = 6
- */
- size_t gap = (len - _windowSize) % _stepSize;
+ immutable len = source.length;
- // check for underflow
- immutable start = (len > _windowSize + gap) ? len - _windowSize - gap : 0;
+ static if (withPartial)
+ {
+ if (source.length <= windowSize)
+ return source[0 .. source.length];
- return _source[start .. len - gap];
+ if (hasPartialElements)
+ return source[numberOfFullFrames * stepSize .. len];
+ }
+
+ // check for underflow
+ immutable start = (len > windowSize + gap) ? len - windowSize - gap : 0;
+ return source[start .. len - gap];
}
/// Ditto
@@ -7980,258 +9177,236 @@ public:
{
assert(!empty, "Attempting to call popBack() on an empty slide");
- immutable end = _source.length > _stepSize ? _source.length - _stepSize : 0;
- _source = _source[0 .. end];
+ // Move by stepSize
+ immutable end = source.length > stepSize ? source.length - stepSize : 0;
- if (_source.length < _windowSize)
+ static if (withPartial)
+ {
+ if (hasShownPartialBefore || source.empty)
+ {
+ _empty = true;
+ return;
+ }
+
+ // pop by stepSize, except for the partial frame at the end
+ if (hasPartialElements)
+ source = source[0 .. source.length - gap];
+ else
+ source = source[0 .. end];
+ }
+ else
+ {
+ source = source[0 .. end];
+ }
+
+ if (source.length < windowSize)
_empty = true;
}
}
}
}
-//
-@safe pure nothrow unittest
-{
- import std.algorithm.comparison : equal;
- import std.array : array;
-
- assert([0, 1, 2, 3].slide(2).equal!equal(
- [[0, 1], [1, 2], [2, 3]]
- ));
- assert(5.iota.slide(3).equal!equal(
- [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
- ));
-
- assert(iota(7).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]]));
- assert(iota(12).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]]));
-
- // set a custom stepsize (default 1)
- assert(6.iota.slide(1, 2).equal!equal(
- [[0], [2], [4]]
- ));
-
- assert(6.iota.slide(2, 4).equal!equal(
- [[0, 1], [4, 5]]
- ));
-
- // allow slide with less elements than the window size
- assert(3.iota.slide!(No.withFewerElements)(4).empty);
- assert(3.iota.slide!(Yes.withFewerElements)(4).equal!equal(
- [[0, 1, 2]]
- ));
-}
-
-// count k-mers
-@safe pure nothrow unittest
-{
- import std.algorithm.comparison : equal;
- import std.algorithm.iteration : each;
-
- int[dstring] d;
- "AGAGA"d.slide(2).each!(a => d[a]++);
- assert(d == ["AG"d: 2, "GA"d: 2]);
-}
-
-// @nogc
+// test @nogc
@safe pure nothrow @nogc unittest
{
import std.algorithm.comparison : equal;
static immutable res1 = [[0], [1], [2], [3]];
- assert(4.iota.slide(1).equal!equal(res1));
+ assert(4.iota.slide!(Yes.withPartial)(1).equal!equal(res1));
static immutable res2 = [[0, 1], [1, 2], [2, 3]];
- assert(4.iota.slide(2).equal!equal(res2));
+ assert(4.iota.slide!(Yes.withPartial)(2).equal!equal(res2));
}
-// different window sizes
+// test different window sizes
@safe pure nothrow unittest
{
- import std.algorithm.comparison : equal;
import std.array : array;
+ import std.algorithm.comparison : equal;
- assert([0, 1, 2, 3].slide(1).array == [[0], [1], [2], [3]]);
- assert([0, 1, 2, 3].slide(2).array == [[0, 1], [1, 2], [2, 3]]);
- assert([0, 1, 2, 3].slide(3).array == [[0, 1, 2], [1, 2, 3]]);
- assert([0, 1, 2, 3].slide(4).array == [[0, 1, 2, 3]]);
- assert([0, 1, 2, 3].slide(5).array == [[0, 1, 2, 3]]);
-
+ assert([0, 1, 2, 3].slide!(Yes.withPartial)(1).array == [[0], [1], [2], [3]]);
+ assert([0, 1, 2, 3].slide!(Yes.withPartial)(2).array == [[0, 1], [1, 2], [2, 3]]);
+ assert([0, 1, 2, 3].slide!(Yes.withPartial)(3).array == [[0, 1, 2], [1, 2, 3]]);
+ assert([0, 1, 2, 3].slide!(Yes.withPartial)(4).array == [[0, 1, 2, 3]]);
+ assert([0, 1, 2, 3].slide!(No.withPartial)(5).walkLength == 0);
+ assert([0, 1, 2, 3].slide!(Yes.withPartial)(5).array == [[0, 1, 2, 3]]);
- assert(iota(2).slide(2).front.equal([0, 1]));
- assert(iota(3).slide(2).equal!equal([[0, 1],[1, 2]]));
- assert(iota(3).slide(3).equal!equal([[0, 1, 2]]));
- assert(iota(3).slide(4).equal!equal([[0, 1, 2]]));
- assert(iota(1, 4).slide(1).equal!equal([[1], [2], [3]]));
- assert(iota(1, 4).slide(3).equal!equal([[1, 2, 3]]));
+ assert(iota(2).slide!(Yes.withPartial)(2).front.equal([0, 1]));
+ assert(iota(3).slide!(Yes.withPartial)(2).equal!equal([[0, 1],[1, 2]]));
+ assert(iota(3).slide!(Yes.withPartial)(3).equal!equal([[0, 1, 2]]));
+ assert(iota(3).slide!(No.withPartial)(4).walkLength == 0);
+ assert(iota(3).slide!(Yes.withPartial)(4).equal!equal([[0, 1, 2]]));
+ assert(iota(1, 4).slide!(Yes.withPartial)(1).equal!equal([[1], [2], [3]]));
+ assert(iota(1, 4).slide!(Yes.withPartial)(3).equal!equal([[1, 2, 3]]));
}
-@safe unittest
+// test combinations
+@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
- assert(6.iota.slide(1, 1).equal!equal(
- [[0], [1], [2], [3], [4], [5]]
- ));
- assert(6.iota.slide(1, 2).equal!equal(
- [[0], [2], [4]]
- ));
- assert(6.iota.slide(1, 3).equal!equal(
- [[0], [3]]
- ));
- assert(6.iota.slide(1, 4).equal!equal(
- [[0], [4]]
- ));
- assert(6.iota.slide(1, 5).equal!equal(
- [[0], [5]]
- ));
- assert(6.iota.slide(2, 1).equal!equal(
- [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]]
- ));
- assert(6.iota.slide(2, 2).equal!equal(
- [[0, 1], [2, 3], [4, 5]]
- ));
- assert(6.iota.slide(2, 3).equal!equal(
- [[0, 1], [3, 4]]
- ));
- assert(6.iota.slide(2, 4).equal!equal(
- [[0, 1], [4, 5]]
- ));
- assert(6.iota.slide(2, 5).equal!equal(
- [[0, 1]]
- ));
- assert(6.iota.slide(3, 1).equal!equal(
- [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
- ));
- assert(6.iota.slide(3, 2).equal!equal(
- [[0, 1, 2], [2, 3, 4]]
- ));
- assert(6.iota.slide(3, 3).equal!equal(
- [[0, 1, 2], [3, 4, 5]]
- ));
- assert(6.iota.slide(3, 4).equal!equal(
- [[0, 1, 2]]
- ));
- assert(6.iota.slide(4, 1).equal!equal(
- [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
- ));
- assert(6.iota.slide(4, 2).equal!equal(
- [[0, 1, 2, 3], [2, 3, 4, 5]]
- ));
- assert(6.iota.slide(4, 3).equal!equal(
- [[0, 1, 2, 3]]
- ));
- assert(6.iota.slide(5, 1).equal!equal(
- [[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]]
- ));
- assert(6.iota.slide(5, 2).equal!equal(
- [[0, 1, 2, 3, 4]]
- ));
- assert(6.iota.slide(5, 3).equal!equal(
- [[0, 1, 2, 3, 4]]
- ));
+ alias t = tuple;
+ auto list = [
+ t(t(1, 1), [[0], [1], [2], [3], [4], [5]]),
+ t(t(1, 2), [[0], [2], [4]]),
+ t(t(1, 3), [[0], [3]]),
+ t(t(1, 4), [[0], [4]]),
+ t(t(1, 5), [[0], [5]]),
+ t(t(2, 1), [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]]),
+ t(t(2, 2), [[0, 1], [2, 3], [4, 5]]),
+ t(t(2, 3), [[0, 1], [3, 4]]),
+ t(t(2, 4), [[0, 1], [4, 5]]),
+ t(t(3, 1), [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]),
+ t(t(3, 3), [[0, 1, 2], [3, 4, 5]]),
+ t(t(4, 1), [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]),
+ t(t(4, 2), [[0, 1, 2, 3], [2, 3, 4, 5]]),
+ t(t(5, 1), [[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]]),
+ ];
+
+ static foreach (Partial; [Yes.withPartial, No.withPartial])
+ foreach (e; list)
+ assert(6.iota.slide!Partial(e[0].expand).equal!equal(e[1]));
+
+ auto listSpecial = [
+ t(t(2, 5), [[0, 1], [5]]),
+ t(t(3, 2), [[0, 1, 2], [2, 3, 4], [4, 5]]),
+ t(t(3, 4), [[0, 1, 2], [4, 5]]),
+ t(t(4, 3), [[0, 1, 2, 3], [3, 4, 5]]),
+ t(t(5, 2), [[0, 1, 2, 3, 4], [2, 3, 4, 5]]),
+ t(t(5, 3), [[0, 1, 2, 3, 4], [3, 4, 5]]),
+ ];
+ foreach (e; listSpecial)
+ {
+ assert(6.iota.slide!(Yes.withPartial)(e[0].expand).equal!equal(e[1]));
+ assert(6.iota.slide!(No.withPartial)(e[0].expand).equal!equal(e[1].dropBackOne));
+ }
}
-// emptyness, copyability, strings
+// test emptiness and copyability
@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
- import std.algorithm.iteration : each, map;
+ import std.algorithm.iteration : map;
// check with empty input
int[] d;
- assert(d.slide(2).empty);
- assert(d.slide(2, 2).empty);
+ assert(d.slide!(Yes.withPartial)(2).empty);
+ assert(d.slide!(Yes.withPartial)(2, 2).empty);
// is copyable?
- auto e = iota(5).slide(2);
+ auto e = iota(5).slide!(Yes.withPartial)(2);
e.popFront;
assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]]));
assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]]));
assert(e.map!"a.array".array == [[1, 2], [2, 3], [3, 4]]);
+}
+
+// test with strings
+@safe pure nothrow unittest
+{
+ import std.algorithm.iteration : each;
- // test with strings
int[dstring] f;
- "AGAGA"d.slide(3).each!(a => f[a]++);
+ "AGAGA"d.slide!(Yes.withPartial)(3).each!(a => f[a]++);
assert(f == ["AGA"d: 2, "GAG"d: 1]);
int[dstring] g;
- "ABCDEFG"d.slide(3, 3).each!(a => g[a]++);
+ "ABCDEFG"d.slide!(Yes.withPartial)(3, 3).each!(a => g[a]++);
+ assert(g == ["ABC"d:1, "DEF"d:1, "G": 1]);
+ g = null;
+ "ABCDEFG"d.slide!(No.withPartial)(3, 3).each!(a => g[a]++);
assert(g == ["ABC"d:1, "DEF"d:1]);
}
-// test slicing, length
-@safe pure nothrow unittest
+// test with utf8 strings
+@safe unittest
{
+ import std.stdio;
import std.algorithm.comparison : equal;
- import std.array : array;
- // test index
- assert(iota(3).slide(4)[0].equal([0, 1, 2]));
- assert(iota(5).slide(4)[1].equal([1, 2, 3, 4]));
- assert(iota(3).slide(4, 2)[0].equal([0, 1, 2]));
- assert(iota(5).slide(4, 2)[1].equal([2, 3, 4]));
- assert(iota(3).slide(4, 3)[0].equal([0, 1, 2]));
- assert(iota(5).slide(4, 3)[1].equal([3, 4,]));
-
- // test slicing
- assert(iota(3).slide(4)[0 .. $].equal!equal([[0, 1, 2]]));
- assert(iota(3).slide(2)[1 .. $].equal!equal([[1, 2]]));
- assert(iota(1, 5).slide(2)[0 .. 1].equal!equal([[1, 2]]));
- assert(iota(1, 5).slide(2)[0 .. 2].equal!equal([[1, 2], [2, 3]]));
- assert(iota(1, 5).slide(3)[0 .. 1].equal!equal([[1, 2, 3]]));
- assert(iota(1, 5).slide(3)[0 .. 2].equal!equal([[1, 2, 3], [2, 3, 4]]));
- assert(iota(1, 6).slide(3)[2 .. 3].equal!equal([[3, 4, 5]]));
- assert(iota(1, 5).slide(4)[0 .. 1].equal!equal([[1, 2, 3, 4]]));
-
- // length
- assert(iota(3).slide(1).length == 3);
- assert(iota(3).slide(1, 2).length == 2);
- assert(iota(3).slide(1, 3).length == 1);
- assert(iota(3).slide(1, 4).length == 1);
- assert(iota(3).slide(2).length == 2);
- assert(iota(3).slide(2, 2).length == 1);
- assert(iota(3).slide(2, 3).length == 1);
- assert(iota(3).slide(3).length == 1);
- assert(iota(3).slide(3, 2).length == 1);
-
- // opDollar
- assert(iota(3).slide(4)[$/2 .. $].equal!equal([[0, 1, 2]]));
- assert(iota(3).slide(4)[$ .. $].empty);
- assert(iota(3).slide(4)[$ .. 1].empty);
-
- assert(iota(5).slide(3, 1)[$/2 .. $].equal!equal([[1, 2, 3], [2, 3, 4]]));
- assert(iota(5).slide(3, 2)[$/2 .. $].equal!equal([[2, 3, 4]]));
- assert(iota(5).slide(3, 3)[$/2 .. $].equal!equal([[0, 1, 2]]));
- assert(iota(3).slide(4, 3)[$ .. $].empty);
- assert(iota(3).slide(4, 3)[$ .. 1].empty);
-}
-
-// test No.withFewerElements
+ assert("ä.ö.ü.".slide!(Yes.withPartial)(3, 2).equal!equal(["ä.ö", "ö.ü", "ü."]));
+ assert("ä.ö.ü.".slide!(No.withPartial)(3, 2).equal!equal(["ä.ö", "ö.ü"]));
+
+ "😄😅😆😇😈😄😅😆😇😈".slide!(Yes.withPartial)(2, 4).equal!equal(["😄😅", "😈😄", "😇😈"]);
+ "😄😅😆😇😈😄😅😆😇😈".slide!(No.withPartial)(2, 4).equal!equal(["😄😅", "😈😄", "😇😈"]);
+ "😄😅😆😇😈😄😅😆😇😈".slide!(Yes.withPartial)(3, 3).equal!equal(["😄😅😆", "😇😈😄", "😅😆😇", "😈"]);
+ "😄😅😆😇😈😄😅😆😇😈".slide!(No.withPartial)(3, 3).equal!equal(["😄😅😆", "😇😈😄", "😅😆😇"]);
+}
+
+// test length
+@safe pure nothrow unittest
+{
+ // Slides with fewer elements are empty or 1 for Yes.withPartial
+ static foreach (expectedLength, Partial; [No.withPartial, Yes.withPartial])
+ {{
+ assert(3.iota.slide!(Partial)(4, 2).walkLength == expectedLength);
+ assert(3.iota.slide!(Partial)(4).walkLength == expectedLength);
+ assert(3.iota.slide!(Partial)(4, 3).walkLength == expectedLength);
+ }}
+
+ static immutable list = [
+ // iota slide expected
+ [4, 2, 1, 3, 3],
+ [5, 3, 1, 3, 3],
+ [7, 2, 2, 4, 3],
+ [12, 2, 4, 3, 3],
+ [6, 1, 2, 3, 3],
+ [6, 2, 4, 2, 2],
+ [3, 2, 4, 1, 1],
+ [5, 2, 1, 4, 4],
+ [7, 2, 2, 4, 3],
+ [7, 2, 3, 3, 2],
+ [7, 3, 2, 3, 3],
+ [7, 3, 3, 3, 2],
+ ];
+ foreach (e; list)
+ {
+ assert(e[0].iota.slide!(Yes.withPartial)(e[1], e[2]).length == e[3]);
+ assert(e[0].iota.slide!(No.withPartial)(e[1], e[2]).length == e[4]);
+ }
+}
+
+// test index and slicing
@safe pure nothrow unittest
{
- assert(iota(3).slide(4).length == 1);
- assert(iota(3).slide(4, 4).length == 1);
+ import std.algorithm.comparison : equal;
+ import std.array : array;
+
+ static foreach (Partial; [Yes.withPartial, No.withPartial])
+ {
+ foreach (s; [5, 7, 10, 15, 20])
+ foreach (windowSize; 1 .. 10)
+ foreach (stepSize; 1 .. 10)
+ {
+ auto r = s.iota.slide!Partial(windowSize, stepSize);
+ auto arr = r.array;
+ assert(r.length == arr.length);
+
+ // test indexing
+ foreach (i; 0 .. arr.length)
+ assert(r[i] == arr[i]);
- assert(iota(3).slide!(No.withFewerElements)(4).empty);
- assert(iota(3, 3).slide!(No.withFewerElements)(4).empty);
- assert(iota(3).slide!(No.withFewerElements)(4).length == 0);
- assert(iota(3).slide!(No.withFewerElements)(4, 4).length == 0);
+ // test slicing
+ foreach (i; 0 .. arr.length)
+ {
+ foreach (j; i .. arr.length)
+ assert(r[i .. j].equal(arr[i .. j]));
- assert(iota(3).slide!(No.withFewerElements)(400).empty);
- assert(iota(3).slide!(No.withFewerElements)(400).length == 0);
- assert(iota(3).slide!(No.withFewerElements)(400, 10).length == 0);
+ assert(r[i .. $].equal(arr[i .. $]));
+ }
- assert(iota(3).slide!(No.withFewerElements)(4)[0 .. $].empty);
- assert(iota(3).slide!(No.withFewerElements)(4)[$ .. $].empty);
- assert(iota(3).slide!(No.withFewerElements)(4)[$ .. 0].empty);
- assert(iota(3).slide!(No.withFewerElements)(4)[$/2 .. $].empty);
+ // test opDollar slicing
+ assert(r[$/2 .. $].equal(arr[$/2 .. $]));
+ assert(r[$ .. $].empty);
+ if (arr.empty)
+ {
+ assert(r[$ .. 0].empty);
+ assert(r[$/2 .. $].empty);
- // with different step sizes
- assert(iota(3).slide!(No.withFewerElements)(4, 5)[0 .. $].empty);
- assert(iota(3).slide!(No.withFewerElements)(4, 6)[$ .. $].empty);
- assert(iota(3).slide!(No.withFewerElements)(4, 7)[$ .. 0].empty);
- assert(iota(3).slide!(No.withFewerElements)(4, 8)[$/2 .. $].empty);
+ }
+ }
+ }
}
// test with infinite ranges
@@ -8239,27 +9414,30 @@ public:
{
import std.algorithm.comparison : equal;
- // InfiniteRange without RandomAccess
- auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1);
- assert(fibs.slide(2).take(2).equal!equal([[1, 1], [1, 2]]));
- assert(fibs.slide(2, 3).take(2).equal!equal([[1, 1], [3, 5]]));
+ static foreach (Partial; [Yes.withPartial, No.withPartial])
+ {{
+ // InfiniteRange without RandomAccess
+ auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1);
+ assert(fibs.slide!Partial(2).take(2).equal!equal([[1, 1], [1, 2]]));
+ assert(fibs.slide!Partial(2, 3).take(2).equal!equal([[1, 1], [3, 5]]));
- // InfiniteRange with RandomAccess and slicing
- auto odds = sequence!("a[0] + n * a[1]")(1, 2);
- auto oddsByPairs = odds.slide(2);
- assert(oddsByPairs.take(2).equal!equal([[ 1, 3], [ 3, 5]]));
- assert(oddsByPairs[1].equal([3, 5]));
- assert(oddsByPairs[4].equal([9, 11]));
+ // InfiniteRange with RandomAccess and slicing
+ auto odds = sequence!("a[0] + n * a[1]")(1, 2);
+ auto oddsByPairs = odds.slide!Partial(2);
+ assert(oddsByPairs.take(2).equal!equal([[ 1, 3], [ 3, 5]]));
+ assert(oddsByPairs[1].equal([3, 5]));
+ assert(oddsByPairs[4].equal([9, 11]));
- static assert(hasSlicing!(typeof(odds)));
- assert(oddsByPairs[3 .. 5].equal!equal([[7, 9], [9, 11]]));
- assert(oddsByPairs[3 .. $].take(2).equal!equal([[7, 9], [9, 11]]));
+ static assert(hasSlicing!(typeof(odds)));
+ assert(oddsByPairs[3 .. 5].equal!equal([[7, 9], [9, 11]]));
+ assert(oddsByPairs[3 .. $].take(2).equal!equal([[7, 9], [9, 11]]));
- auto oddsWithGaps = odds.slide(2, 4);
- assert(oddsWithGaps.take(3).equal!equal([[1, 3], [9, 11], [17, 19]]));
- assert(oddsWithGaps[2].equal([17, 19]));
- assert(oddsWithGaps[1 .. 3].equal!equal([[9, 11], [17, 19]]));
- assert(oddsWithGaps[1 .. $].take(2).equal!equal([[9, 11], [17, 19]]));
+ auto oddsWithGaps = odds.slide!Partial(2, 4);
+ assert(oddsWithGaps.take(3).equal!equal([[1, 3], [9, 11], [17, 19]]));
+ assert(oddsWithGaps[2].equal([17, 19]));
+ assert(oddsWithGaps[1 .. 3].equal!equal([[9, 11], [17, 19]]));
+ assert(oddsWithGaps[1 .. $].take(2).equal!equal([[9, 11], [17, 19]]));
+ }}
}
// test reverse
@@ -8267,188 +9445,157 @@ public:
{
import std.algorithm.comparison : equal;
- auto e = iota(3).slide(2);
- assert(e.retro.equal!equal([[1, 2], [0, 1]]));
- assert(e.retro.array.equal(e.array.retro));
-
- auto e2 = iota(5).slide(3);
- assert(e2.retro.equal!equal([[2, 3, 4], [1, 2, 3], [0, 1, 2]]));
- assert(e2.retro.array.equal(e2.array.retro));
+ static foreach (Partial; [Yes.withPartial, No.withPartial])
+ {{
+ foreach (windowSize; 1 .. 15)
+ foreach (stepSize; 1 .. 15)
+ {
+ auto r = 20.iota.slide!Partial(windowSize, stepSize);
+ auto rArr = r.array.retro;
+ auto rRetro = r.retro;
- auto e3 = iota(3).slide(4);
- assert(e3.retro.equal!equal([[0, 1, 2]]));
- assert(e3.retro.array.equal(e3.array.retro));
+ assert(rRetro.length == rArr.length);
+ assert(rRetro.equal(rArr));
+ assert(rRetro.array.retro.equal(r));
+ }
+ }}
}
-// test reverse with different steps
+// test with dummy ranges
@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : AllDummyRanges;
+ import std.meta : Filter;
- assert(iota(7).slide(2, 1).retro.equal!equal(
- [[5, 6], [4, 5], [3, 4], [2, 3], [1, 2], [0, 1]]
- ));
- assert(iota(7).slide(2, 2).retro.equal!equal(
- [[4, 5], [2, 3], [0, 1]]
- ));
- assert(iota(7).slide(2, 3).retro.equal!equal(
- [[3, 4], [0, 1]]
- ));
- assert(iota(7).slide(2, 4).retro.equal!equal(
- [[4, 5], [0, 1]]
- ));
- assert(iota(7).slide(2, 5).retro.equal!equal(
- [[5, 6], [0, 1]]
- ));
- assert(iota(7).slide(3, 1).retro.equal!equal(
- [[4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]]
- ));
- assert(iota(7).slide(3, 2).retro.equal!equal(
- [[4, 5, 6], [2, 3, 4], [0, 1, 2]]
- ));
- assert(iota(7).slide(4, 1).retro.equal!equal(
- [[3, 4, 5, 6], [2, 3, 4, 5], [1, 2, 3, 4], [0, 1, 2, 3]]
- ));
- assert(iota(7).slide(4, 2).retro.equal!equal(
- [[2, 3, 4, 5], [0, 1, 2, 3]]
- ));
- assert(iota(7).slide(4, 3).retro.equal!equal(
- [[3, 4, 5, 6], [0, 1, 2, 3]]
- ));
- assert(iota(7).slide(4, 4).retro.equal!equal(
- [[0, 1, 2, 3]]
- ));
- assert(iota(7).slide(5, 1).retro.equal!equal(
- [[2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [0, 1, 2, 3, 4]]
- ));
- assert(iota(7).slide(5, 2).retro.equal!equal(
- [[2, 3, 4, 5, 6], [0, 1, 2, 3, 4]]
- ));
- assert(iota(7).slide(5, 3).retro.equal!equal(
- [[0, 1, 2, 3, 4]]
- ));
- assert(iota(7).slide(5, 4).retro.equal!equal(
- [[0, 1, 2, 3, 4]]
- ));
-}
+ static foreach (Range; Filter!(isForwardRange, AllDummyRanges))
+ {{
+ Range r;
-// step size
-@safe pure nothrow unittest
-{
- import std.algorithm.comparison : equal;
+ static foreach (Partial; [Yes.withPartial, No.withPartial])
+ {
+ assert(r.slide!Partial(1).equal!equal(
+ [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]
+ ));
+ assert(r.slide!Partial(2).equal!equal(
+ [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]]
+ ));
+ assert(r.slide!Partial(3).equal!equal(
+ [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6],
+ [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]
+ ));
+ assert(r.slide!Partial(6).equal!equal(
+ [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8],
+ [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9, 10]]
+ ));
+ }
- assert(iota(7).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]]));
- assert(iota(8).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]]));
- assert(iota(9).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]]));
- assert(iota(12).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]]));
- assert(iota(13).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]]));
+ // special cases
+ assert(r.slide!(Yes.withPartial)(15).equal!equal(iota(1, 11).only));
+ assert(r.slide!(Yes.withPartial)(15).walkLength == 1);
+ assert(r.slide!(No.withPartial)(15).empty);
+ assert(r.slide!(No.withPartial)(15).walkLength == 0);
+ }}
}
// test with dummy ranges
@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
- import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy, AllDummyRanges;
- import std.meta : AliasSeq;
+ import std.internal.test.dummyrange : AllDummyRanges;
+ import std.meta : Filter;
+ import std.typecons : tuple;
- alias AllForwardDummyRanges = AliasSeq!(
- DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward),
- DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional),
- DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random),
- DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward),
- DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional),
- //DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input),
- DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward),
- DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional),
- DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random),
- //DummyRange!(ReturnBy.Value, Length.No, RangeType.Input),
- DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward),
- DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional)
- );
+ alias t = tuple;
+ static immutable list = [
+ // iota slide expected
+ t(6, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6]]),
+ t(6, t(4, 6), [[1, 2, 3, 4]]),
+ t(6, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]),
+ t(7, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]),
+ t(7, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7]]),
+ t(8, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8]]),
+ t(8, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8]]),
+ t(8, t(3, 4), [[1, 2, 3], [5, 6, 7]]),
+ t(10, t(3, 7), [[1, 2, 3], [8, 9, 10]]),
+ ];
- foreach (Range; AliasSeq!AllForwardDummyRanges)
+ static foreach (Range; Filter!(isForwardRange, AllDummyRanges))
+ static foreach (Partial; [Yes.withPartial, No.withPartial])
+ foreach (e; list)
+ assert(Range().take(e[0]).slide!Partial(e[1].expand).equal!equal(e[2]));
+
+ static immutable listSpecial = [
+ // iota slide expected
+ t(6, t(4, 3), [[1, 2, 3, 4], [4, 5, 6]]),
+ t(7, t(4, 5), [[1, 2, 3, 4], [6, 7]]),
+ t(7, t(4, 4), [[1, 2, 3, 4], [5, 6, 7]]),
+ t(7, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7]]),
+ t(8, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7], [7, 8]]),
+ t(8, t(3, 3), [[1, 2, 3], [4, 5, 6], [7, 8]]),
+ t(8, t(3, 6), [[1, 2, 3], [7, 8]]),
+ t(10, t(7, 6), [[1, 2, 3, 4, 5, 6, 7], [7, 8, 9, 10]]),
+ t(10, t(3, 8), [[1, 2, 3], [9, 10]]),
+ ];
+ static foreach (Range; Filter!(isForwardRange, AllDummyRanges))
+ static foreach (Partial; [Yes.withPartial, No.withPartial])
+ foreach (e; listSpecial)
{
Range r;
- assert(r.slide(1).equal!equal(
- [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]
- ));
- assert(r.slide(2).equal!equal(
- [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]]
- ));
- assert(r.slide(3).equal!equal(
- [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6],
- [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]
- ));
- assert(r.slide(6).equal!equal(
- [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8],
- [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9, 10]]
- ));
- assert(r.slide(15).equal!equal(
- [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
- ));
-
- assert(r.slide!(No.withFewerElements)(15).empty);
- }
-
- alias BackwardsDummyRanges = AliasSeq!(
- DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random),
- DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random),
- );
+ assert(r.take(e[0]).slide!(Yes.withPartial)(e[1].expand).equal!equal(e[2]));
+ assert(r.take(e[0]).slide!(No.withPartial)(e[1].expand).equal!equal(e[2].dropBackOne));
+ }
+}
- foreach (Range; AliasSeq!BackwardsDummyRanges)
- {
+// test reverse with dummy ranges
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : AllDummyRanges;
+ import std.meta : Filter, templateAnd;
+ import std.typecons : tuple;
+ alias t = tuple;
+
+ static immutable list = [
+ // slide expected
+ t(1, 1, [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]]),
+ t(2, 1, [[9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]]),
+ t(5, 1, [[6, 7, 8, 9, 10], [5, 6, 7, 8, 9], [4, 5, 6, 7, 8],
+ [3, 4, 5, 6, 7], [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]]),
+ t(2, 2, [[9, 10], [7, 8], [5, 6], [3, 4], [1, 2]]),
+ t(2, 4, [[9, 10], [5, 6], [1, 2]]),
+ ];
+
+ static foreach (Range; Filter!(templateAnd!(hasSlicing, hasLength, isBidirectionalRange), AllDummyRanges))
+ {{
Range r;
- assert(r.slide(1).retro.equal!equal(
- [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]]
- ));
- assert(r.slide(2).retro.equal!equal(
- [[9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]]
- ));
- assert(r.slide(5).retro.equal!equal(
- [[6, 7, 8, 9, 10], [5, 6, 7, 8, 9], [4, 5, 6, 7, 8],
- [3, 4, 5, 6, 7], [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]]
- ));
- assert(r.slide(15).retro.equal!equal(
- [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
- ));
-
- // different step sizes
- assert(r.slide(2, 4)[2].equal([9, 10]));
- assert(r.slide(2, 1).equal!equal(
- [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]]
- ));
- assert(r.slide(2, 2).equal!equal(
- [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
- ));
- assert(r.slide(2, 3).equal!equal(
- [[1, 2], [4, 5], [7, 8]]
- ));
- assert(r.slide(2, 4).equal!equal(
- [[1, 2], [5, 6], [9, 10]]
- ));
-
- // front = back
- foreach (windowSize; 1 .. 10)
+ static foreach (Partial; [Yes.withPartial, No.withPartial])
+ {
+ foreach (e; list)
+ assert(r.slide!Partial(e[0], e[1]).retro.equal!equal(e[2]));
+
+ // front = back
+ foreach (windowSize; 1 .. 10)
foreach (stepSize; 1 .. 10)
{
- auto slider = r.slide(windowSize, stepSize);
- assert(slider.retro.retro.equal!equal(slider));
+ auto slider = r.slide!Partial(windowSize, stepSize);
+ auto sliderRetro = slider.retro.array;
+ assert(slider.length == sliderRetro.length);
+ assert(sliderRetro.retro.equal!equal(slider));
}
- }
-
- assert(iota(1, 12).slide(2, 4)[0 .. 3].equal!equal([[1, 2], [5, 6], [9, 10]]));
- assert(iota(1, 12).slide(2, 4)[0 .. $].equal!equal([[1, 2], [5, 6], [9, 10]]));
- assert(iota(1, 12).slide(2, 4)[$/2 .. $].equal!equal([[5, 6], [9, 10]]));
+ }
- // reverse
- assert(iota(1, 12).slide(2, 4).retro.equal!equal([[9, 10], [5, 6], [1, 2]]));
+ // special cases
+ assert(r.slide!(No.withPartial)(15).retro.walkLength == 0);
+ assert(r.slide!(Yes.withPartial)(15).retro.equal!equal(iota(1, 11).only));
+ }}
}
// test different sliceable ranges
@safe pure nothrow unittest
{
import std.algorithm.comparison : equal;
- import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy;
+ import std.internal.test.dummyrange : AllDummyRanges;
import std.meta : AliasSeq;
struct SliceableRange(Range, Flag!"withOpDollar" withOpDollar = No.withOpDollar,
@@ -8481,7 +9628,7 @@ public:
struct Dollar {}
Dollar opDollar() const { return Dollar.init; }
- //Slice to dollar
+ // Slice to dollar
typeof(this) opSlice(size_t lower, Dollar)
{
return typeof(this)(arr[lower .. $]);
@@ -8495,82 +9642,80 @@ public:
}
}
- alias T = int[];
+ import std.meta : Filter, templateNot;
+ alias SliceableDummyRanges = Filter!(hasSlicing, AllDummyRanges);
- alias SliceableDummyRanges = AliasSeq!(
- DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T),
- DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T),
- SliceableRange!(T, No.withOpDollar, No.withInfiniteness),
- SliceableRange!(T, Yes.withOpDollar, No.withInfiniteness),
- SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness),
- );
+ static foreach (Partial; [Yes.withPartial, No.withPartial])
+ {{
+ static foreach (Range; SliceableDummyRanges)
+ {{
+ Range r;
+ r.reinit;
+ r.arr[] -= 1; // use a 0-based array (for clarity)
- foreach (Range; AliasSeq!SliceableDummyRanges)
- {
- Range r;
- r.arr = 10.iota.array; // for clarity
+ assert(r.slide!Partial(2)[0].equal([0, 1]));
+ assert(r.slide!Partial(2)[1].equal([1, 2]));
- static assert(isForwardRange!Range);
- enum hasSliceToEnd = hasSlicing!Range && is(typeof(Range.init[0 .. $]) == Range);
+ // saveable
+ auto s = r.slide!Partial(2);
+ assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]]));
+ s.save.popFront;
+ assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]]));
- assert(r.slide(2)[0].equal([0, 1]));
- assert(r.slide(2)[1].equal([1, 2]));
+ assert(r.slide!Partial(3)[1 .. 3].equal!equal([[1, 2, 3], [2, 3, 4]]));
+ }}
- // saveable
- auto s = r.slide(2);
- assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]]));
- s.save.popFront;
- assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]]));
+ static foreach (Range; Filter!(templateNot!isInfinite, SliceableDummyRanges))
+ {{
+ Range r;
+ r.reinit;
+ r.arr[] -= 1; // use a 0-based array (for clarity)
- assert(r.slide(3)[1 .. 3].equal!equal([[1, 2, 3], [2, 3, 4]]));
- }
+ assert(r.slide!(No.withPartial)(6).equal!equal(
+ [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7],
+ [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]]
+ ));
+ assert(r.slide!(No.withPartial)(16).empty);
- alias SliceableDummyRangesWithoutInfinity = AliasSeq!(
- DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T),
- DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T),
- SliceableRange!(T, No.withOpDollar, No.withInfiniteness),
- SliceableRange!(T, Yes.withOpDollar, No.withInfiniteness),
- );
+ assert(r.slide!Partial(4)[0 .. $].equal(r.slide!Partial(4)));
+ assert(r.slide!Partial(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]]));
+ assert(r.slide!Partial(2)[$ .. $].empty);
- foreach (Range; AliasSeq!SliceableDummyRangesWithoutInfinity)
- {
- static assert(hasSlicing!Range);
- static assert(hasLength!Range);
-
- Range r;
- r.arr = 10.iota.array; // for clarity
+ assert(r.slide!Partial(3).retro.equal!equal(
+ [[7, 8, 9], [6, 7, 8], [5, 6, 7], [4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]]
+ ));
+ }}
- assert(r.slide!(No.withFewerElements)(6).equal!equal(
- [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7],
- [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]]
- ));
- assert(r.slide!(No.withFewerElements)(16).empty);
+ alias T = int[];
- assert(r.slide(4)[0 .. $].equal(r.slide(4)));
- assert(r.slide(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]]));
- assert(r.slide(2)[$ .. $].empty);
+ // separate checks for infinity
+ auto infIndex = SliceableRange!(T, No.withOpDollar, Yes.withInfiniteness)([0, 1, 2, 3]);
+ assert(infIndex.slide!Partial(2)[0].equal([0, 1]));
+ assert(infIndex.slide!Partial(2)[1].equal([1, 2]));
- assert(r.slide(3).retro.equal!equal(
- [[7, 8, 9], [6, 7, 8], [5, 6, 7], [4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]]
- ));
- }
-
- // separate checks for infinity
- auto infIndex = SliceableRange!(T, No.withOpDollar, Yes.withInfiniteness)([0, 1, 2, 3]);
- assert(infIndex.slide(2)[0].equal([0, 1]));
- assert(infIndex.slide(2)[1].equal([1, 2]));
+ auto infDollar = SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness)();
+ assert(infDollar.slide!Partial(2)[1 .. $].front.equal([1, 2]));
+ assert(infDollar.slide!Partial(4)[0 .. $].front.equal([0, 1, 2, 3]));
+ assert(infDollar.slide!Partial(4)[2 .. $].front.equal([2, 3, 4, 5]));
+ }}
+}
- auto infDollar = SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness)();
- assert(infDollar.slide(2)[1 .. $].front.equal([1, 2]));
- assert(infDollar.slide(4)[0 .. $].front.equal([0, 1, 2, 3]));
- assert(infDollar.slide(4)[2 .. $].front.equal([2, 3, 4, 5]));
+// https://issues.dlang.org/show_bug.cgi?id=19082
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ assert([1].map!(x => x).slide(2).equal!equal([[1]]));
}
-private struct OnlyResult(T, size_t arity)
+private struct OnlyResult(Values...)
+if (Values.length > 1)
{
- private this(Values...)(auto ref Values values)
+ private enum arity = Values.length;
+
+ private this(return scope ref Values values)
{
- this.data = [values];
+ this.values = values;
this.backIndex = arity;
}
@@ -8579,10 +9724,10 @@ private struct OnlyResult(T, size_t arity)
return frontIndex >= backIndex;
}
- T front() @property
+ CommonType!Values front() @property
{
assert(!empty, "Attempting to fetch the front of an empty Only range");
- return data[frontIndex];
+ return this[0];
}
void popFront()
@@ -8591,10 +9736,10 @@ private struct OnlyResult(T, size_t arity)
++frontIndex;
}
- T back() @property
+ CommonType!Values back() @property
{
assert(!empty, "Attempting to fetch the back of an empty Only range");
- return data[backIndex - 1];
+ return this[$ - 1];
}
void popBack()
@@ -8615,12 +9760,15 @@ private struct OnlyResult(T, size_t arity)
alias opDollar = length;
- T opIndex(size_t idx)
+ CommonType!Values opIndex(size_t idx)
{
// when i + idx points to elements popped
// with popBack
assert(idx < length, "Attempting to fetch an out of bounds index from an Only range");
- return data[frontIndex + idx];
+ final switch (frontIndex + idx)
+ static foreach (i, T; Values)
+ case i:
+ return values[i];
}
OnlyResult opSlice()
@@ -8647,21 +9795,21 @@ private struct OnlyResult(T, size_t arity)
private size_t frontIndex = 0;
private size_t backIndex = 0;
- // @@@BUG@@@ 10643
+ // https://issues.dlang.org/show_bug.cgi?id=10643
version (none)
{
import std.traits : hasElaborateAssign;
static if (hasElaborateAssign!T)
- private T[arity] data;
+ private Values values;
else
- private T[arity] data = void;
+ private Values values = void;
}
else
- private T[arity] data;
+ private Values values;
}
// Specialize for single-element results
-private struct OnlyResult(T, size_t arity : 1)
+private struct OnlyResult(T)
{
@property T front()
{
@@ -8688,7 +9836,7 @@ private struct OnlyResult(T, size_t arity : 1)
}
alias opDollar = length;
- private this()(auto ref T value)
+ private this()(return scope auto ref T value)
{
this._value = value;
this._empty = false;
@@ -8725,7 +9873,7 @@ private struct OnlyResult(T, size_t arity : 1)
}
// Specialize for the empty range
-private struct OnlyResult(T, size_t arity : 0)
+private struct OnlyResult()
{
private static struct EmptyElementType {}
@@ -8753,7 +9901,7 @@ private struct OnlyResult(T, size_t arity : 0)
}
/**
-Assemble $(D values) into a range that carries all its
+Assemble `values` into a range that carries all its
elements in-situ.
Useful when a single value or multiple disconnected values
@@ -8772,10 +9920,10 @@ Returns:
See_Also: $(LREF chain) to chain ranges
*/
-auto only(Values...)(auto ref Values values)
+auto only(Values...)(return scope Values values)
if (!is(CommonType!Values == void) || Values.length == 0)
{
- return OnlyResult!(CommonType!Values, Values.length)(values);
+ return OnlyResult!Values(values);
}
///
@@ -8799,15 +9947,14 @@ if (!is(CommonType!Values == void) || Values.length == 0)
.equal("T.D.P.L"));
}
+// https://issues.dlang.org/show_bug.cgi?id=20314
@safe unittest
{
- // Verify that the same common type and same arity
- // results in the same template instantiation
- static assert(is(typeof(only(byte.init, int.init)) ==
- typeof(only(int.init, byte.init))));
+ import std.algorithm.iteration : joiner;
- static assert(is(typeof(only((const(char)[]).init, string.init)) ==
- typeof(only((const(char)[]).init, (const(char)[]).init))));
+ const string s = "foo", t = "bar";
+
+ assert([only(s, t), only(t, s)].joiner(only(", ")).join == "foobar, barfoo");
}
// Tests the zero-element result
@@ -8872,7 +10019,7 @@ if (!is(CommonType!Values == void) || Values.length == 0)
assert(imm.front == 1);
assert(imm.back == 1);
assert(!imm.empty);
- assert(imm.init.empty); // Issue 13441
+ assert(imm.init.empty); // https://issues.dlang.org/show_bug.cgi?id=13441
assert(imm.length == 1);
assert(equal(imm, imm[]));
assert(equal(imm, imm[0 .. 1]));
@@ -8923,8 +10070,8 @@ if (!is(CommonType!Values == void) || Values.length == 0)
["one two", "one two three", "one two three four"];
string[] joinedRange = joined;
- foreach (argCount; AliasSeq!(2, 3, 4))
- {
+ static foreach (argCount; 2 .. 5)
+ {{
auto values = only(data[0 .. argCount]);
alias Values = typeof(values);
static assert(is(ElementType!Values == string));
@@ -8939,7 +10086,7 @@ if (!is(CommonType!Values == void) || Values.length == 0)
assert(values[0 .. $].equal(values[0 .. values.length]));
assert(values.joiner(" ").equal(joinedRange.front));
joinedRange.popFront();
- }
+ }}
assert(saved.retro.equal(only(3, 2, 1)));
assert(saved.length == 3);
@@ -8961,7 +10108,7 @@ if (!is(CommonType!Values == void) || Values.length == 0)
alias Imm = typeof(imm);
static assert(is(ElementType!Imm == immutable(int)));
assert(!imm.empty);
- assert(imm.init.empty); // Issue 13441
+ assert(imm.init.empty); // https://issues.dlang.org/show_bug.cgi?id=13441
assert(imm.front == 42);
imm.popFront();
assert(imm.front == 24);
@@ -8973,11 +10120,48 @@ if (!is(CommonType!Values == void) || Values.length == 0)
cast(void) only(test, test); // Works with mutable indirection
}
+// https://issues.dlang.org/show_bug.cgi?id=21129
+@safe unittest
+{
+ auto range = () @safe {
+ const(char)[5] staticStr = "Hello";
+
+ // `only` must store a char[5] - not a char[]!
+ return only(staticStr, " World");
+ } ();
+
+ assert(range.join == "Hello World");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21129
+@safe unittest
+{
+ struct AliasedString
+ {
+ const(char)[5] staticStr = "Hello";
+
+ @property const(char)[] slice() const
+ {
+ return staticStr[];
+ }
+ alias slice this;
+ }
+
+ auto range = () @safe {
+ auto hello = AliasedString();
+
+ // a copy of AliasedString is stored in the range.
+ return only(hello, " World");
+ } ();
+
+ assert(range.join == "Hello World");
+}
+
/**
Iterate over `range` with an attached index variable.
Each element is a $(REF Tuple, std,typecons) containing the index
-and the element, in that order, where the index member is named $(D index)
+and the element, in that order, where the index member is named `index`
and the element member is named `value`.
The index starts at `start` and is incremented by one on every iteration.
@@ -8992,7 +10176,7 @@ Overflow:
continue from `Enumerator.min`.
Params:
- range = the input range to attach indexes to
+ range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to attach indexes to
start = the number to start the index counter from
Returns:
@@ -9001,7 +10185,7 @@ Returns:
primitives, which are propagated only if `range` has length.
Example:
-Useful for using $(D foreach) with an index loop variable:
+Useful for using `foreach` with an index loop variable:
----
import std.stdio : stdin, stdout;
import std.range : enumerate;
@@ -9027,13 +10211,18 @@ in
auto result = adds(start, range.length, overflow);
else static if (isSigned!Enumerator)
{
- Largest!(Enumerator, Signed!LengthType) signedLength;
- try signedLength = to!(typeof(signedLength))(range.length);
- catch (ConvException)
- overflow = true;
- catch (Exception)
- assert(false);
-
+ alias signed_t = Largest!(Enumerator, Signed!LengthType);
+ signed_t signedLength;
+ //This is to trick the compiler because if length is enum
+ //the compiler complains about unreachable code.
+ auto getLength()
+ {
+ return range.length;
+ }
+ //Can length fit in the signed type
+ assert(getLength() < signed_t.max,
+ "a signed length type is required but the range's length() is too great");
+ signedLength = range.length;
auto result = adds(start, signedLength, overflow);
}
else
@@ -9046,7 +10235,7 @@ in
assert(!overflow && result <= Enumerator.max);
}
}
-body
+do
{
// TODO: Relax isIntegral!Enumerator to allow user-defined integral types
static struct Result
@@ -9056,7 +10245,7 @@ body
private:
alias ElemType = Tuple!(Enumerator, "index", ElementType!Range, "value");
Range range;
- Enumerator index;
+ Unqual!Enumerator index;
public:
ElemType front() @property
@@ -9092,12 +10281,7 @@ body
static if (hasLength!Range)
{
- size_t length() @property
- {
- return range.length;
- }
-
- alias opDollar = length;
+ mixin ImplementLength!range;
static if (isBidirectionalRange!Range)
{
@@ -9165,6 +10349,14 @@ pure @safe nothrow unittest
assert(aa[1]);
}
+// Make sure passing qualified types works
+pure @safe nothrow unittest
+{
+ char[4] v;
+ immutable start = 2;
+ v[2 .. $].enumerate(start);
+}
+
pure @safe nothrow unittest
{
import std.internal.test.dummyrange : AllDummyRanges;
@@ -9183,8 +10375,8 @@ pure @safe nothrow unittest
}
}
- foreach (DummyType; AliasSeq!(AllDummyRanges, HasSlicing))
- {
+ static foreach (DummyType; AliasSeq!(AllDummyRanges, HasSlicing))
+ {{
alias R = typeof(enumerate(DummyType.init));
static assert(isInputRange!R);
static assert(isForwardRange!R == isForwardRange!DummyType);
@@ -9199,7 +10391,7 @@ pure @safe nothrow unittest
}
static assert(hasSlicing!R == hasSlicing!DummyType);
- }
+ }}
static immutable values = ["zero", "one", "two", "three"];
auto enumerated = values[].enumerate();
@@ -9249,8 +10441,8 @@ pure @safe nothrow unittest
assert(shifted.empty);
}
- foreach (T; AliasSeq!(ubyte, byte, uint, int))
- {
+ static foreach (T; AliasSeq!(ubyte, byte, uint, int))
+ {{
auto inf = 42.repeat().enumerate(T.max);
alias Inf = typeof(inf);
static assert(isInfinite!Inf);
@@ -9269,7 +10461,7 @@ pure @safe nothrow unittest
assert(window.front == inf.front);
window.popFront();
assert(window.empty);
- }
+ }}
}
pure @safe unittest
@@ -9277,8 +10469,8 @@ pure @safe unittest
import std.algorithm.comparison : equal;
import std.meta : AliasSeq;
static immutable int[] values = [0, 1, 2, 3, 4];
- foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
- {
+ static foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
+ {{
auto enumerated = values.enumerate!T();
static assert(is(typeof(enumerated.front.index) == T));
assert(enumerated.equal(values[].zip(values)));
@@ -9290,10 +10482,28 @@ pure @safe unittest
static assert(is(typeof(enumerated.front.index) == T));
assert(offsetEnumerated.equal(subset.zip(subset)));
}
+ }}
+}
+@nogc @safe unittest
+{
+ const val = iota(1, 100).enumerate(1);
+}
+@nogc @safe unittest
+{
+ import core.exception : AssertError;
+ import std.exception : assertThrown;
+ struct RangePayload {
+ enum length = size_t.max;
+ void popFront() {}
+ int front() { return 0; }
+ bool empty() { return true; }
}
+ RangePayload thePayload;
+ //Assertion won't happen when contracts are disabled for -release.
+ debug assertThrown!AssertError(enumerate(thePayload, -10));
}
-
-version (none) // @@@BUG@@@ 10939
+// https://issues.dlang.org/show_bug.cgi?id=10939
+version (none)
{
// Re-enable (or remove) if 10939 is resolved.
/+pure+/ @safe unittest // Impure because of std.conv.to
@@ -9319,7 +10529,7 @@ version (none) // @@@BUG@@@ 10939
}
SignedLengthRange svalues;
- foreach (Enumerator; AliasSeq!(ubyte, byte, ushort, short, uint, int, ulong, long))
+ static foreach (Enumerator; AliasSeq!(ubyte, byte, ushort, short, uint, int, ulong, long))
{
assertThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max));
assertNotThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max - values.length));
@@ -9330,7 +10540,7 @@ version (none) // @@@BUG@@@ 10939
assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length + 1));
}
- foreach (Enumerator; AliasSeq!(byte, short, int))
+ static foreach (Enumerator; AliasSeq!(byte, short, int))
{
assertThrown!RangeError(repeat(0, uint.max).enumerate!Enumerator());
}
@@ -9340,32 +10550,45 @@ version (none) // @@@BUG@@@ 10939
}
/**
- Returns true if $(D fn) accepts variables of type T1 and T2 in any order.
+ Returns true if `fn` accepts variables of type T1 and T2 in any order.
The following code should compile:
---
- T1 foo();
- T2 bar();
-
- fn(foo(), bar());
- fn(bar(), foo());
+ (ref T1 a, ref T2 b)
+ {
+ fn(a, b);
+ fn(b, a);
+ }
---
*/
template isTwoWayCompatible(alias fn, T1, T2)
{
- enum isTwoWayCompatible = is(typeof( (){
- T1 foo();
- T2 bar();
-
- fn(foo(), bar());
- fn(bar(), foo());
+ enum isTwoWayCompatible = is(typeof((ref T1 a, ref T2 b)
+ {
+ cast(void) fn(a, b);
+ cast(void) fn(b, a);
}
));
}
+///
+@safe unittest
+{
+ void func1(int a, int b);
+ void func2(int a, float b);
+
+ static assert(isTwoWayCompatible!(func1, int, int));
+ static assert(isTwoWayCompatible!(func1, short, int));
+ static assert(!isTwoWayCompatible!(func2, int, float));
+
+ void func3(ref int a, ref int b);
+ static assert( isTwoWayCompatible!(func3, int, int));
+ static assert(!isTwoWayCompatible!(func3, short, int));
+}
+
/**
- Policy used with the searching primitives $(D lowerBound), $(D
- upperBound), and $(D equalRange) of $(LREF SortedRange) below.
+ Policy used with the searching primitives `lowerBound`, $(D
+ upperBound), and `equalRange` of $(LREF SortedRange) below.
*/
enum SearchPolicy
{
@@ -9394,44 +10617,97 @@ enum SearchPolicy
remaining interval is searched using binary search. A value is
found in $(BIGOH log(n)) time.
*/
- gallop,
+ gallop,
/**
Searches using a classic interval halving policy. The search
starts in the middle of the range, and each search step cuts
the range in half. This policy finds a value in $(BIGOH log(n))
- time but is less cache friendly than $(D gallop) for large
- ranges. The $(D binarySearch) policy is used as the last step
- of $(D trot), $(D gallop), $(D trotBackwards), and $(D
+ time but is less cache friendly than `gallop` for large
+ ranges. The `binarySearch` policy is used as the last step
+ of `trot`, `gallop`, `trotBackwards`, and $(D
gallopBackwards) strategies.
*/
- binarySearch,
+ binarySearch,
/**
- Similar to $(D trot) but starts backwards. Use it when
+ Similar to `trot` but starts backwards. Use it when
confident that the value is around the end of the range.
*/
- trotBackwards,
+ trotBackwards,
/**
- Similar to $(D gallop) but starts backwards. Use it when
+ Similar to `gallop` but starts backwards. Use it when
confident that the value is around the end of the range.
*/
- gallopBackwards
- }
+ gallopBackwards
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto a = assumeSorted([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ auto p1 = a.upperBound!(SearchPolicy.binarySearch)(3);
+ assert(p1.equal([4, 5, 6, 7, 8, 9]));
+
+ auto p2 = a.lowerBound!(SearchPolicy.gallop)(4);
+ assert(p2.equal([0, 1, 2, 3]));
+}
/**
-Represents a sorted range. In addition to the regular range
-primitives, supports additional operations that take advantage of the
-ordering, such as merge and binary search. To obtain a $(D
-SortedRange) from an unsorted range $(D r), use
-$(REF sort, std,algorithm,sorting) which sorts $(D r) in place and returns the
-corresponding $(D SortedRange). To construct a $(D SortedRange) from a range
-$(D r) that is known to be already sorted, use $(LREF assumeSorted) described
-below.
+ Options for $(LREF SortedRange) ranges (below).
*/
-struct SortedRange(Range, alias pred = "a < b")
-if (isInputRange!Range)
+enum SortedRangeOptions
+{
+ /**
+ Assume, that the range is sorted without checking.
+ */
+ assumeSorted,
+
+ /**
+ All elements of the range are checked to be sorted.
+ The check is performed in O(n) time.
+ */
+ checkStrictly,
+
+ /**
+ Some elements of the range are checked to be sorted.
+ For ranges with random order, this will almost surely
+ detect, that it is not sorted. For almost sorted ranges
+ it's more likely to fail. The checked elements are choosen
+ in a deterministic manner, which makes this check reproducable.
+ The check is performed in O(log(n)) time.
+ */
+ checkRoughly,
+}
+
+///
+@safe pure unittest
+{
+ // create a SortedRange, that's checked strictly
+ SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 5, 7, 9 ]);
+}
+
+/**
+ Represents a sorted range. In addition to the regular range
+ primitives, supports additional operations that take advantage of the
+ ordering, such as merge and binary search. To obtain a $(D
+ SortedRange) from an unsorted range `r`, use
+ $(REF sort, std,algorithm,sorting) which sorts `r` in place and returns the
+ corresponding `SortedRange`. To construct a `SortedRange` from a range
+ `r` that is known to be already sorted, use $(LREF assumeSorted).
+
+ Params:
+ pred: The predicate used to define the sortedness
+ opt: Controls how strongly the range is checked for sortedness.
+ Will only be used for `RandomAccessRanges`.
+ Will not be used in CTFE.
+*/
+struct SortedRange(Range, alias pred = "a < b",
+ SortedRangeOptions opt = SortedRangeOptions.assumeSorted)
+if (isInputRange!Range && !isInstanceOf!(SortedRange, Range))
{
import std.functional : binaryFun;
@@ -9449,36 +10725,55 @@ if (isInputRange!Range)
// Undocummented because a clearer way to invoke is by calling
// assumeSorted.
this(Range input)
- out
- {
- // moved out of the body as a workaround for Issue 12661
- dbgVerifySorted();
- }
- body
{
+ static if (opt == SortedRangeOptions.checkRoughly)
+ {
+ roughlyVerifySorted(input);
+ }
+ static if (opt == SortedRangeOptions.checkStrictly)
+ {
+ strictlyVerifySorted(input);
+ }
this._input = input;
}
// Assertion only.
- private void dbgVerifySorted()
+ static if (opt == SortedRangeOptions.checkRoughly)
+ private void roughlyVerifySorted(Range r)
{
if (!__ctfe)
- debug
{
static if (isRandomAccessRange!Range && hasLength!Range)
{
import core.bitop : bsr;
import std.algorithm.sorting : isSorted;
+ import std.exception : enforce;
// Check the sortedness of the input
- if (this._input.length < 2) return;
+ if (r.length < 2) return;
- immutable size_t msb = bsr(this._input.length) + 1;
- assert(msb > 0 && msb <= this._input.length);
- immutable step = this._input.length / msb;
- auto st = stride(this._input, step);
+ immutable size_t msb = bsr(r.length) + 1;
+ assert(msb > 0 && msb <= r.length);
+ immutable step = r.length / msb;
+ auto st = stride(r, step);
- assert(isSorted!pred(st), "Range is not sorted");
+ enforce(isSorted!pred(st), "Range is not sorted");
+ }
+ }
+ }
+
+ // Assertion only.
+ static if (opt == SortedRangeOptions.checkStrictly)
+ private void strictlyVerifySorted(Range r)
+ {
+ if (!__ctfe)
+ {
+ static if (isRandomAccessRange!Range && hasLength!Range)
+ {
+ import std.algorithm.sorting : isSorted;
+ import std.exception : enforce;
+
+ enforce(isSorted!pred(r), "Range is not sorted");
}
}
}
@@ -9535,7 +10830,7 @@ if (isInputRange!Range)
/// Ditto
static if (hasSlicing!Range)
- auto opSlice(size_t a, size_t b)
+ auto opSlice(size_t a, size_t b) return scope
{
assert(
a <= b,
@@ -9546,25 +10841,33 @@ if (isInputRange!Range)
return result;
}
- /// Ditto
- static if (hasLength!Range)
- {
- @property size_t length() //const
- {
- return _input.length;
- }
- alias opDollar = length;
- }
+ mixin ImplementLength!_input;
/**
- Releases the controlled range and returns it.
+ Releases the controlled range and returns it.
+
+ This does the opposite of $(LREF assumeSorted): instead of turning a range
+ into a `SortedRange`, it extracts the original range back out of the `SortedRange`
+ using $(REF, move, std,algorithm,mutation).
*/
- auto release()
+ auto release() return scope
{
import std.algorithm.mutation : move;
return move(_input);
}
+ ///
+ static if (is(Range : int[]))
+ @safe unittest
+ {
+ import std.algorithm.sorting : sort;
+ int[3] data = [ 1, 2, 3 ];
+ auto a = assumeSorted(data[]);
+ assert(a == sort!"a < b"(data[]));
+ int[] p = a.release();
+ assert(p == [ 1, 2, 3 ]);
+ }
+
// Assuming a predicate "test" that returns 0 for a left portion
// of the range and then 1 for the rest, returns the index at
// which the first 1 appears. Used internally by the search routines.
@@ -9655,13 +10958,12 @@ if (isInputRange!Range)
// lowerBound
/**
- This function uses a search with policy $(D sp) to find the
- largest left subrange on which $(D pred(x, value)) is $(D true) for
- all $(D x) (e.g., if $(D pred) is "less than", returns the portion of
- the range with elements strictly smaller than $(D value)). The search
+ This function uses a search with policy `sp` to find the
+ largest left subrange on which $(D pred(x, value)) is `true` for
+ all `x` (e.g., if `pred` is "less than", returns the portion of
+ the range with elements strictly smaller than `value`). The search
schedule and its complexity are documented in
- $(LREF SearchPolicy). See also STL's
- $(HTTP sgi.com/tech/stl/lower_bound.html, lower_bound).
+ $(LREF SearchPolicy).
*/
auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value)
if (isTwoWayCompatible!(predFun, ElementType!Range, V)
@@ -9671,6 +10973,7 @@ if (isInputRange!Range)
}
///
+ static if (is(Range : int[]))
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -9681,18 +10984,16 @@ if (isInputRange!Range)
// upperBound
/**
-This function searches with policy $(D sp) to find the largest right
-subrange on which $(D pred(value, x)) is $(D true) for all $(D x)
-(e.g., if $(D pred) is "less than", returns the portion of the range
-with elements strictly greater than $(D value)). The search schedule
+This function searches with policy `sp` to find the largest right
+subrange on which $(D pred(value, x)) is `true` for all `x`
+(e.g., if `pred` is "less than", returns the portion of the range
+with elements strictly greater than `value`). The search schedule
and its complexity are documented in $(LREF SearchPolicy).
-For ranges that do not offer random access, $(D SearchPolicy.linear)
+For ranges that do not offer random access, `SearchPolicy.linear`
is the only policy allowed (and it must be specified explicitly lest it exposes
user code to unexpected inefficiencies). For random-access searches, all
-policies are allowed, and $(D SearchPolicy.binarySearch) is the default.
-
-See_Also: STL's $(HTTP sgi.com/tech/stl/lower_bound.html,upper_bound).
+policies are allowed, and `SearchPolicy.binarySearch` is the default.
*/
auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value)
if (isTwoWayCompatible!(predFun, ElementType!Range, V))
@@ -9715,6 +11016,7 @@ See_Also: STL's $(HTTP sgi.com/tech/stl/lower_bound.html,upper_bound).
}
///
+ static if (is(Range : int[]))
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -9726,17 +11028,16 @@ See_Also: STL's $(HTTP sgi.com/tech/stl/lower_bound.html,upper_bound).
// equalRange
/**
- Returns the subrange containing all elements $(D e) for which both $(D
- pred(e, value)) and $(D pred(value, e)) evaluate to $(D false) (e.g.,
- if $(D pred) is "less than", returns the portion of the range with
- elements equal to $(D value)). Uses a classic binary search with
+ Returns the subrange containing all elements `e` for which both $(D
+ pred(e, value)) and $(D pred(value, e)) evaluate to `false` (e.g.,
+ if `pred` is "less than", returns the portion of the range with
+ elements equal to `value`). Uses a classic binary search with
interval halving until it finds a value that satisfies the condition,
- then uses $(D SearchPolicy.gallopBackwards) to find the left boundary
- and $(D SearchPolicy.gallop) to find the right boundary. These
+ then uses `SearchPolicy.gallopBackwards` to find the left boundary
+ and `SearchPolicy.gallop` to find the right boundary. These
policies are justified by the fact that the two boundaries are likely
to be near the first found value (i.e., equal ranges are relatively
- small). Completes the entire search in $(BIGOH log(n)) time. See also
- STL's $(HTTP sgi.com/tech/stl/equal_range.html, equal_range).
+ small). Completes the entire search in $(BIGOH log(n)) time.
*/
auto equalRange(V)(V value)
if (isTwoWayCompatible!(predFun, ElementType!Range, V)
@@ -9778,6 +11079,7 @@ See_Also: STL's $(HTTP sgi.com/tech/stl/lower_bound.html,upper_bound).
}
///
+ static if (is(Range : int[]))
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -9788,9 +11090,9 @@ See_Also: STL's $(HTTP sgi.com/tech/stl/lower_bound.html,upper_bound).
// trisect
/**
-Returns a tuple $(D r) such that $(D r[0]) is the same as the result
-of $(D lowerBound(value)), $(D r[1]) is the same as the result of $(D
-equalRange(value)), and $(D r[2]) is the same as the result of $(D
+Returns a tuple `r` such that `r[0]` is the same as the result
+of `lowerBound(value)`, `r[1]` is the same as the result of $(D
+equalRange(value)), and `r[2]` is the same as the result of $(D
upperBound(value)). The call is faster than computing all three
separately. Uses a search schedule similar to $(D
equalRange). Completes the entire search in $(BIGOH log(n)) time.
@@ -9838,6 +11140,7 @@ equalRange). Completes the entire search in $(BIGOH log(n)) time.
}
///
+ static if (is(Range : int[]))
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -9850,10 +11153,9 @@ equalRange). Completes the entire search in $(BIGOH log(n)) time.
// contains
/**
-Returns $(D true) if and only if $(D value) can be found in $(D
+Returns `true` if and only if `value` can be found in $(D
range), which is assumed to be sorted. Performs $(BIGOH log(r.length))
-evaluations of $(D pred). See also STL's $(HTTP
-sgi.com/tech/stl/binary_search.html, binary_search).
+evaluations of `pred`.
*/
bool contains(V)(V value)
@@ -9865,6 +11167,15 @@ sgi.com/tech/stl/binary_search.html, binary_search).
return !predFun(value, _input[i]);
}
+/**
+Like `contains`, but the value is specified before the range.
+*/
+ bool opBinaryRight(string op, V)(V value)
+ if (op == "in" && isRandomAccessRange!Range)
+ {
+ return contains(value);
+ }
+
// groupBy
/**
Returns a range of subranges of elements that are equivalent according to the
@@ -9877,6 +11188,15 @@ sorting relation.
}
}
+/// ditto
+template SortedRange(Range, alias pred = "a < b",
+ SortedRangeOptions opt = SortedRangeOptions.assumeSorted)
+if (isInstanceOf!(SortedRange, Range))
+{
+ // Avoid nesting SortedRange types (see https://issues.dlang.org/show_bug.cgi?id=18933);
+ alias SortedRange = SortedRange!(Unqual!(typeof(Range._input)), pred, opt);
+}
+
///
@safe unittest
{
@@ -9884,21 +11204,21 @@ sorting relation.
auto a = [ 1, 2, 3, 42, 52, 64 ];
auto r = assumeSorted(a);
assert(r.contains(3));
- assert(!r.contains(32));
+ assert(!(32 in r));
auto r1 = sort!"a > b"(a);
- assert(r1.contains(3));
+ assert(3 in r1);
assert(!r1.contains(32));
assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]);
}
/**
-$(D SortedRange) could accept ranges weaker than random-access, but it
+`SortedRange` could accept ranges weaker than random-access, but it
is unable to provide interesting functionality for them. Therefore,
-$(D SortedRange) is currently restricted to random-access ranges.
+`SortedRange` is currently restricted to random-access ranges.
No copy of the original range is ever made. If the underlying range is
-changed concurrently with its corresponding $(D SortedRange) in ways
-that break its sorted-ness, $(D SortedRange) will work erratically.
+changed concurrently with its corresponding `SortedRange` in ways
+that break its sorted-ness, `SortedRange` will work erratically.
*/
@safe unittest
{
@@ -9910,6 +11230,42 @@ that break its sorted-ness, $(D SortedRange) will work erratically.
assert(!r.contains(42)); // passes although it shouldn't
}
+/**
+`SortedRange` can be searched with predicates that do not take
+two elements of the underlying range as arguments.
+
+This is useful, if a range of structs is sorted by a member and you
+want to search in that range by only providing a value for that member.
+
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ static struct S { int i; }
+ static bool byI(A, B)(A a, B b)
+ {
+ static if (is(A == S))
+ return a.i < b;
+ else
+ return a < b.i;
+ }
+ auto r = assumeSorted!byI([S(1), S(2), S(3)]);
+ auto lessThanTwo = r.lowerBound(2);
+ assert(equal(lessThanTwo, [S(1)]));
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown, assertNotThrown;
+
+ assertNotThrown(SortedRange!(int[])([ 1, 3, 10, 5, 7 ]));
+ assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 10, 5, 7 ]));
+
+ // these two checks are implementation depended
+ assertNotThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 12, 2 ]));
+ assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 2, 12 ]));
+}
+
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -9998,9 +11354,9 @@ that break its sorted-ness, $(D SortedRange) will work erratically.
assert(!r.contains(42)); // passes although it shouldn't
}
-@safe unittest
+@betterC @nogc nothrow @safe unittest
{
- immutable(int)[] arr = [ 1, 2, 3 ];
+ static immutable(int)[] arr = [ 1, 2, 3 ];
auto s = assumeSorted(arr);
}
@@ -10034,48 +11390,67 @@ that break its sorted-ness, $(D SortedRange) will work erratically.
f.close();
}
+// https://issues.dlang.org/show_bug.cgi?id=19337
+@safe unittest
+{
+ import std.algorithm.sorting : sort;
+ auto a = [ 1, 2, 3, 42, 52, 64 ];
+ a.sort.sort!"a > b";
+}
+
/**
-Assumes $(D r) is sorted by predicate $(D pred) and returns the
-corresponding $(D SortedRange!(pred, R)) having $(D r) as support. To
-keep the checking costs low, the cost is $(BIGOH 1) in release mode
-(no checks for sorted-ness are performed). In debug mode, a few random
-elements of $(D r) are checked for sorted-ness. The size of the sample
-is proportional $(BIGOH log(r.length)). That way, checking has no
-effect on the complexity of subsequent operations specific to sorted
-ranges (such as binary search). The probability of an arbitrary
-unsorted range failing the test is very high (however, an
-almost-sorted range is likely to pass it). To check for sorted-ness at
+Assumes `r` is sorted by predicate `pred` and returns the
+corresponding $(D SortedRange!(pred, R)) having `r` as support.
+To check for sorted-ness at
cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting).
*/
auto assumeSorted(alias pred = "a < b", R)(R r)
if (isInputRange!(Unqual!R))
{
- return SortedRange!(Unqual!R, pred)(r);
+ // Avoid senseless `SortedRange!(SortedRange!(...), pred)` nesting.
+ static if (is(R == SortedRange!(RRange, RPred), RRange, alias RPred))
+ {
+ static if (isInputRange!R && __traits(isSame, pred, RPred))
+ // If the predicate is the same and we don't need to cast away
+ // constness for the result to be an input range.
+ return r;
+ else
+ return SortedRange!(Unqual!(typeof(r._input)), pred)(r._input);
+ }
+ else
+ {
+ return SortedRange!(Unqual!R, pred)(r);
+ }
}
+///
@safe unittest
{
import std.algorithm.comparison : equal;
- static assert(isRandomAccessRange!(SortedRange!(int[])));
- int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
- auto p = assumeSorted(a).lowerBound(4);
- assert(equal(p, [0, 1, 2, 3]));
- p = assumeSorted(a).lowerBound(5);
- assert(equal(p, [0, 1, 2, 3, 4]));
- p = assumeSorted(a).lowerBound(6);
- assert(equal(p, [ 0, 1, 2, 3, 4, 5]));
- p = assumeSorted(a).lowerBound(6.9);
- assert(equal(p, [ 0, 1, 2, 3, 4, 5, 6]));
+
+ int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+ auto p = assumeSorted(a);
+
+ assert(equal(p.lowerBound(4), [0, 1, 2, 3]));
+ assert(equal(p.lowerBound(5), [0, 1, 2, 3, 4]));
+ assert(equal(p.lowerBound(6), [0, 1, 2, 3, 4, 5]));
+ assert(equal(p.lowerBound(6.9), [0, 1, 2, 3, 4, 5, 6]));
}
@safe unittest
{
import std.algorithm.comparison : equal;
+ static assert(isRandomAccessRange!(SortedRange!(int[])));
int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
auto p = assumeSorted(a).upperBound(3);
assert(equal(p, [4, 4, 5, 6 ]));
p = assumeSorted(a).upperBound(4.2);
assert(equal(p, [ 5, 6 ]));
+
+ // https://issues.dlang.org/show_bug.cgi?id=18933
+ // don't create senselessly nested SortedRange types.
+ assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted(a)))));
+ assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted!"a > b"(a)))));
}
@safe unittest
@@ -10119,21 +11494,7 @@ if (isInputRange!(Unqual!R))
r = assumeSorted(a);
}
-@system unittest
-{
- bool ok = true;
- try
- {
- auto r2 = assumeSorted([ 677, 345, 34, 7, 5 ]);
- debug ok = false;
- }
- catch (Throwable)
- {
- }
- assert(ok);
-}
-
-// issue 15003
+// https://issues.dlang.org/show_bug.cgi?id=15003
@nogc @safe unittest
{
static immutable a = [1, 2, 3, 4];
@@ -10149,15 +11510,15 @@ if (isInputRange!(Unqual!R))
consumed as if it were a reference type.
Note:
- `save` works as normal and operates on a new _range, so if
+ `save` works as normal and operates on a new range, so if
`save` is ever called on the `RefRange`, then no operations on the
- saved _range will affect the original.
+ saved range will affect the original.
Params:
range = the range to construct the `RefRange` from
Returns:
- A `RefRange`. If the given _range is a class type
+ A `RefRange`. If the given range is a class type
(and thus is already a reference type), then the original
range is returned rather than a `RefRange`.
+/
@@ -10174,13 +11535,13 @@ public:
/++
- This does not assign the pointer of $(D rhs) to this $(D RefRange).
- Rather it assigns the range pointed to by $(D rhs) to the range pointed
- to by this $(D RefRange). This is because $(I any) operation on a
- $(D RefRange) is the same is if it occurred to the original range. The
- one exception is when a $(D RefRange) is assigned $(D null) either
- directly or because $(D rhs) is $(D null). In that case, $(D RefRange)
- no longer refers to the original range but is $(D null).
+ This does not assign the pointer of `rhs` to this `RefRange`.
+ Rather it assigns the range pointed to by `rhs` to the range pointed
+ to by this `RefRange`. This is because $(I any) operation on a
+ `RefRange` is the same is if it occurred to the original range. The
+ one exception is when a `RefRange` is assigned `null` either
+ directly or because `rhs` is `null`. In that case, `RefRange`
+ no longer refers to the original range but is `null`.
+/
auto opAssign(RefRange rhs)
{
@@ -10267,7 +11628,7 @@ public:
version (StdDdoc)
{
/++
- Only defined if $(D isForwardRange!R) is $(D true).
+ Only defined if `isForwardRange!R` is `true`.
+/
@property auto save() {assert(0);}
/++ Ditto +/
@@ -10322,7 +11683,7 @@ public:
private static string _genSave() @safe pure nothrow
{
- return `import std.conv : emplace;` ~
+ return `import core.lifetime : emplace;` ~
`alias S = typeof((*_range).save);` ~
`static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~
`auto mem = new void[S.sizeof];` ~
@@ -10337,7 +11698,7 @@ public:
version (StdDdoc)
{
/++
- Only defined if $(D isBidirectionalRange!R) is $(D true).
+ Only defined if `isBidirectionalRange!R` is `true`.
+/
@property auto back() {assert(0);}
/++ Ditto +/
@@ -10374,7 +11735,7 @@ public:
version (StdDdoc)
{
/++
- Only defined if $(D isRandomAccesRange!R) is $(D true).
+ Only defined if `isRandomAccessRange!R` is `true`.
+/
auto ref opIndex(IndexType)(IndexType index) {assert(0);}
@@ -10398,8 +11759,8 @@ public:
/++
- Only defined if $(D hasMobileElements!R) and $(D isForwardRange!R) are
- $(D true).
+ Only defined if `hasMobileElements!R` and `isForwardRange!R` are
+ `true`.
+/
static if (hasMobileElements!R && isForwardRange!R) auto moveFront()
{
@@ -10408,8 +11769,8 @@ public:
/++
- Only defined if $(D hasMobileElements!R) and $(D isBidirectionalRange!R)
- are $(D true).
+ Only defined if `hasMobileElements!R` and `isBidirectionalRange!R`
+ are `true`.
+/
static if (hasMobileElements!R && isBidirectionalRange!R) auto moveBack()
{
@@ -10418,8 +11779,8 @@ public:
/++
- Only defined if $(D hasMobileElements!R) and $(D isRandomAccessRange!R)
- are $(D true).
+ Only defined if `hasMobileElements!R` and `isRandomAccessRange!R`
+ are `true`.
+/
static if (hasMobileElements!R && isRandomAccessRange!R) auto moveAt(size_t index)
{
@@ -10429,15 +11790,11 @@ public:
version (StdDdoc)
{
- /++
- Only defined if $(D hasLength!R) is $(D true).
- +/
- @property auto length() {assert(0);}
-
- /++ Ditto +/
- @property auto length() const {assert(0);}
-
- /++ Ditto +/
+ /// Only defined if `hasLength!R` is `true`.
+ @property size_t length();
+ /// ditto
+ @property size_t length() const;
+ /// Ditto
alias opDollar = length;
}
else static if (hasLength!R)
@@ -10446,12 +11803,10 @@ public:
{
return (*_range).length;
}
-
static if (is(typeof((*cast(const R*)_range).length))) @property auto length() const
{
return (*_range).length;
}
-
alias opDollar = length;
}
@@ -10459,7 +11814,7 @@ public:
version (StdDdoc)
{
/++
- Only defined if $(D hasSlicing!R) is $(D true).
+ Only defined if `hasSlicing!R` is `true`.
+/
auto opSlice(IndexType1, IndexType2)
(IndexType1 begin, IndexType2 end) {assert(0);}
@@ -10492,7 +11847,7 @@ public:
private static string _genOpSlice() @safe pure nothrow
{
- return `import std.conv : emplace;` ~
+ return `import core.lifetime : emplace;` ~
`alias S = typeof((*_range)[begin .. end]);` ~
`static assert(hasSlicing!S, S.stringof ~ " is not sliceable.");` ~
`auto mem = new void[S.sizeof];` ~
@@ -10657,8 +12012,8 @@ private:
}
{
- // Issue 16534 - opDollar should be defined if the
- // wrapped range defines length.
+ // https://issues.dlang.org/show_bug.cgi?id=16534
+ // opDollar should be defined if the wrapped range defines length.
auto range = 10.iota.takeExactly(5);
auto wrapper = refRange(&range);
assert(wrapper.length == 5);
@@ -10821,7 +12176,7 @@ private:
@property int front() @safe const pure nothrow { return 0; }
enum bool empty = false;
void popFront() @safe pure nothrow { }
- @property auto save() @safe pure nothrow { return this; }
+ @property auto save() @safe pure nothrow return scope { return this; }
}
S s;
@@ -10836,7 +12191,7 @@ private:
@property int front() @safe const pure nothrow { return 0; }
@property bool empty() @safe const pure nothrow { return false; }
void popFront() @safe pure nothrow { }
- @property auto save() @safe pure nothrow { return this; }
+ @property auto save() @safe pure nothrow return scope { return this; }
}
static assert(isForwardRange!C);
@@ -10846,7 +12201,8 @@ private:
assert(cWrapper is c);
}
-@system unittest // issue 14373
+// https://issues.dlang.org/show_bug.cgi?id=14373
+@system unittest
{
static struct R
{
@@ -10859,7 +12215,8 @@ private:
assert(r.empty);
}
-@system unittest // issue 14575
+// https://issues.dlang.org/show_bug.cgi?id=14575
+@system unittest
{
struct R
{
@@ -10899,9 +12256,8 @@ if (isInputRange!R)
return *range;
}
-/*****************************************************************************/
-
-@safe unittest // bug 9060
+// https://issues.dlang.org/show_bug.cgi?id=9060
+@safe unittest
{
import std.algorithm.iteration : map, joiner, group;
import std.algorithm.searching : until;
@@ -10946,6 +12302,7 @@ if (isInputRange!R)
private struct Bitwise(R)
if (isInputRange!R && isIntegral!(ElementType!R))
{
+ import std.traits : Unsigned;
private:
alias ElemType = ElementType!R;
alias UnsignedElemType = Unsigned!ElemType;
@@ -11087,11 +12444,11 @@ public:
assert(n < length, "Index out of bounds");
}
}
- body
+ do
{
immutable size_t remainingBits = bitsNum - maskPos + 1;
// If n >= maskPos, then the bit sign will be 1, otherwise 0
- immutable sizediff_t sign = (remainingBits - n - 1) >> (sizediff_t.sizeof * 8 - 1);
+ immutable ptrdiff_t sign = (remainingBits - n - 1) >> (ptrdiff_t.sizeof * 8 - 1);
/*
By truncating n with remainingBits bits we have skipped the
remaining bits in parent[0], so we need to add 1 to elemIndex.
@@ -11126,12 +12483,12 @@ public:
assert(n < length, "Index out of bounds");
}
}
- body
+ do
{
import core.bitop : bsf;
immutable size_t remainingBits = bitsNum - maskPos + 1;
- immutable sizediff_t sign = (remainingBits - n - 1) >> (sizediff_t.sizeof * 8 - 1);
+ immutable ptrdiff_t sign = (remainingBits - n - 1) >> (ptrdiff_t.sizeof * 8 - 1);
immutable size_t elemIndex = sign * (((n - remainingBits) >> bitsNum.bsf) + 1);
immutable size_t elemMaskPos = (sign ^ 1) * (maskPos + n)
+ sign * (1 + ((n - remainingBits) & (bitsNum - 1)));
@@ -11153,19 +12510,19 @@ public:
{
assert(start < end, "Invalid bounds: end <= start");
}
- body
+ do
{
import core.bitop : bsf;
size_t remainingBits = bitsNum - maskPos + 1;
- sizediff_t sign = (remainingBits - start - 1) >> (sizediff_t.sizeof * 8 - 1);
+ ptrdiff_t sign = (remainingBits - start - 1) >> (ptrdiff_t.sizeof * 8 - 1);
immutable size_t startElemIndex = sign * (((start - remainingBits) >> bitsNum.bsf) + 1);
immutable size_t startElemMaskPos = (sign ^ 1) * (maskPos + start)
+ sign * (1 + ((start - remainingBits) & (bitsNum - 1)));
immutable size_t sliceLen = end - start - 1;
remainingBits = bitsNum - startElemMaskPos + 1;
- sign = (remainingBits - sliceLen - 1) >> (sizediff_t.sizeof * 8 - 1);
+ sign = (remainingBits - sliceLen - 1) >> (ptrdiff_t.sizeof * 8 - 1);
immutable size_t endElemIndex = startElemIndex
+ sign * (((sliceLen - remainingBits) >> bitsNum.bsf) + 1);
immutable size_t endElemMaskPos = (sign ^ 1) * (startElemMaskPos + sliceLen)
@@ -11195,7 +12552,7 @@ Bitwise adapter over an integral type range. Consumes the range elements bit by
bit, from the least significant bit to the most significant bit.
Params:
- R = an integral input range to iterate over
+ R = an integral $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to iterate over
range = range to consume bit by by
Returns:
@@ -11261,6 +12618,7 @@ if (isInputRange!R && isIntegral!(ElementType!R))
// Test all range types over all integral types
@safe pure nothrow unittest
{
+ import std.meta : AliasSeq;
import std.internal.test.dummyrange;
alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint,
@@ -11357,6 +12715,7 @@ if (isInputRange!R && isIntegral!(ElementType!R))
// Test opIndex and opSlice
@system unittest
{
+ import std.meta : AliasSeq;
alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint,
long, ulong);
foreach (IntegralType; IntegralTypes)
@@ -11403,17 +12762,49 @@ if (isInputRange!R && isIntegral!(ElementType!R))
*/
struct NullSink
{
- void put(E)(E){}
+ void put(E)(scope const E) pure @safe @nogc nothrow {}
+}
+
+/// ditto
+auto ref nullSink()
+{
+ static NullSink sink;
+ return sink;
}
///
-@safe unittest
+@safe nothrow unittest
{
import std.algorithm.iteration : map;
import std.algorithm.mutation : copy;
- [4, 5, 6].map!(x => x * 2).copy(NullSink()); // data is discarded
+ [4, 5, 6].map!(x => x * 2).copy(nullSink); // data is discarded
+}
+
+///
+@safe unittest
+{
+ import std.csv : csvNextToken;
+
+ string line = "a,b,c";
+
+ // ignore the first column
+ line.csvNextToken(nullSink, ',', '"');
+ line.popFront;
+
+ // look at the second column
+ Appender!string app;
+ line.csvNextToken(app, ',', '"');
+ assert(app.data == "b");
}
+@safe unittest
+{
+ auto r = 10.iota
+ .tee(nullSink)
+ .dropOne;
+
+ assert(r.front == 1);
+}
/++
@@ -11421,11 +12812,11 @@ struct NullSink
range can be passed to a provided function or $(LREF OutputRange) as they are
iterated over. This is useful for printing out intermediate values in a long
chain of range code, performing some operation with side-effects on each call
- to $(D front) or $(D popFront), or diverting the elements of a range into an
+ to `front` or `popFront`, or diverting the elements of a range into an
auxiliary $(LREF OutputRange).
It is important to note that as the resultant range is evaluated lazily,
- in the case of the version of $(D tee) that takes a function, the function
+ in the case of the version of `tee` that takes a function, the function
will not actually be executed until the range is "walked" using functions
that evaluate ranges, such as $(REF array, std,array) or
$(REF fold, std,algorithm,iteration).
@@ -11433,8 +12824,13 @@ struct NullSink
Params:
pipeOnPop = If `Yes.pipeOnPop`, simply iterating the range without ever
calling `front` is enough to have `tee` mirror elements to `outputRange` (or,
- respectively, `fun`). If `No.pipeOnPop`, only elements for which `front` does
- get called will be also sent to `outputRange`/`fun`.
+ respectively, `fun`). Note that each `popFront()` call will mirror the
+ old `front` value, not the new one. This means that the last value will
+ not be forwarded if the range isn't iterated until empty. If
+ `No.pipeOnPop`, only elements for which `front` does get called will be
+ also sent to `outputRange`/`fun`. If `front` is called twice for the same
+ element, it will still be sent only once. If this caching is undesired,
+ consider using $(REF map, std,algorithm,iteration) instead.
inputRange = The input range being passed through.
outputRange = This range will receive elements of `inputRange` progressively
as iteration proceeds.
@@ -11462,13 +12858,7 @@ if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1))
private bool _frontAccessed;
}
- static if (hasLength!R1)
- {
- @property auto length()
- {
- return _input.length;
- }
- }
+ mixin ImplementLength!_input;
static if (isInfinite!R1)
{
@@ -11521,7 +12911,7 @@ if (is(typeof(fun) == void) || isSomeFunction!fun)
when using either as an $(LREF OutputRange). Since a template
has no type, typeof(template) will always return void.
If it's a template lambda, it's first necessary to instantiate
- it with $(D ElementType!R1).
+ it with `ElementType!R1`.
*/
static if (is(typeof(fun) == void))
alias _fun = fun!(ElementType!R1);
@@ -11690,24 +13080,24 @@ if (is(typeof(fun) == void) || isSomeFunction!fun)
auto result3 = txt.tee(asink3).array;
assert(equal(txt, result3) && equal(result3, asink3));
- foreach (CharType; AliasSeq!(char, wchar, dchar))
- {
+ static foreach (CharType; AliasSeq!(char, wchar, dchar))
+ {{
auto appSink = appender!(CharType[])();
auto appResult = txt.tee(appSink).array;
assert(equal(txt, appResult) && equal(appResult, appSink.data));
- }
+ }}
- foreach (StringType; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (StringType; AliasSeq!(string, wstring, dstring))
+ {{
auto appSink = appender!StringType();
auto appResult = txt.tee(appSink).array;
assert(equal(txt, appResult) && equal(appResult, appSink.data));
- }
+ }}
}
+// https://issues.dlang.org/show_bug.cgi?id=13483
@safe unittest
{
- // Issue 13483
static void func1(T)(T x) {}
void func2(int x) {}
@@ -11729,7 +13119,7 @@ $(REF byGrapheme, std, uni) before calling this function.
If `r` has a length, then this is $(BIGOH 1). Otherwise, it's $(BIGOH r.length).
Params:
- r = an input range with a length, or a forward range
+ r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length, or a forward range
e = element to pad the range with
n = the length to pad to
@@ -11815,7 +13205,7 @@ provides them. Except the functions `back` and `popBack`, which also require
the range to have a length as well as `back` and `popBack`
Params:
- r = an input range with a length
+ r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length
e = element to pad the range with
n = the length to pad to
@@ -11836,14 +13226,27 @@ if (
private:
R data;
E element;
- size_t counter;
- static if (isBidirectionalRange!R && hasLength!R) size_t backPosition;
- size_t maxSize;
+ static if (hasLength!R)
+ {
+ size_t padLength;
+ }
+ else
+ {
+ size_t minLength;
+ size_t consumed;
+ }
public:
bool empty() @property
{
- return data.empty && counter >= maxSize;
+ static if (hasLength!R)
+ {
+ return data.empty && padLength == 0;
+ }
+ else
+ {
+ return data.empty && consumed >= minLength;
+ }
}
auto front() @property
@@ -11855,11 +13258,25 @@ if (
void popFront()
{
assert(!empty, "Attempting to popFront an empty padRight");
- ++counter;
- if (!data.empty)
+ static if (hasLength!R)
{
- data.popFront;
+ if (!data.empty)
+ {
+ data.popFront;
+ }
+ else
+ {
+ --padLength;
+ }
+ }
+ else
+ {
+ ++consumed;
+ if (!data.empty)
+ {
+ data.popFront;
+ }
}
}
@@ -11867,8 +13284,7 @@ if (
{
size_t length() @property
{
- import std.algorithm.comparison : max;
- return max(data.length, maxSize);
+ return data.length + padLength;
}
}
@@ -11887,16 +13303,15 @@ if (
auto back() @property
{
assert(!empty, "Attempting to fetch the back of an empty padRight");
- return backPosition > data.length ? element : data.back;
+ return padLength > 0 ? element : data.back;
}
void popBack()
{
assert(!empty, "Attempting to popBack an empty padRight");
- if (backPosition > data.length)
+ if (padLength > 0)
{
- --backPosition;
- --maxSize;
+ --padLength;
}
else
{
@@ -11910,8 +13325,7 @@ if (
E opIndex(size_t index)
{
assert(index <= this.length, "Index out of bounds");
- return (index > data.length && index <= maxSize) ? element :
- data[index];
+ return index >= data.length ? element : data[index];
}
}
@@ -11927,20 +13341,26 @@ if (
b <= length,
"Attempting to slice using an out of bounds index on a padRight"
);
- return Result((b <= data.length) ? data[a .. b] : data[a .. data.length],
+ return Result(
+ a >= data.length ? data[0 .. 0] : b <= data.length ? data[a .. b] : data[a .. data.length],
element, b - a);
}
alias opDollar = length;
}
- this(R r, E e, size_t max)
+ this(R r, E e, size_t n)
{
data = r;
element = e;
- maxSize = max;
- static if (isBidirectionalRange!R && hasLength!R)
- backPosition = max;
+ static if (hasLength!R)
+ {
+ padLength = n > data.length ? n - data.length : 0;
+ }
+ else
+ {
+ minLength = n;
+ }
}
@disable this();
@@ -11984,6 +13404,8 @@ pure @safe unittest
RangeType r3;
assert(r3.padRight(0, 12)[0] == 1);
assert(r3.padRight(0, 12)[2] == 3);
+ assert(r3.padRight(0, 12)[9] == 10);
+ assert(r3.padRight(0, 12)[10] == 0);
assert(r3.padRight(0, 12)[11] == 0);
}
@@ -11996,6 +13418,14 @@ pure @safe unittest
.equal([1, 2, 3])
);
assert(r4
+ .padRight(0, 12)[0 .. 10]
+ .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U])
+ );
+ assert(r4
+ .padRight(0, 12)[0 .. 11]
+ .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0])
+ );
+ assert(r4
.padRight(0, 12)[2 .. $]
.equal([3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0])
);
@@ -12004,6 +13434,10 @@ pure @safe unittest
.equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0])
);
}
+
+ // drop & dropBack test opslice ranges when available, popFront/popBack otherwise
+ RangeType r5;
+ foreach (i; 1 .. 13) assert(r5.padRight(0, 12).drop(i).walkLength == 12 - i);
}
}
@@ -12016,3 +13450,54 @@ pure @safe unittest
static immutable r2 = [1, 2, 3, 4, 0, 0];
assert(r1.padRight(0, 6).equal(r2));
}
+
+// Test back, popBack, and save
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto r1 = [1, 2, 3, 4].padRight(0, 6);
+ assert(r1.back == 0);
+
+ r1.popBack;
+ auto r2 = r1.save;
+ assert(r1.equal([1, 2, 3, 4, 0]));
+ assert(r2.equal([1, 2, 3, 4, 0]));
+
+ r1.popBackN(2);
+ assert(r1.back == 3);
+ assert(r1.length == 3);
+ assert(r2.length == 5);
+ assert(r2.equal([1, 2, 3, 4, 0]));
+
+ r2.popFront;
+ assert(r2.length == 4);
+ assert(r2[0] == 2);
+ assert(r2[1] == 3);
+ assert(r2[2] == 4);
+ assert(r2[3] == 0);
+ assert(r2.equal([2, 3, 4, 0]));
+
+ r2.popBack;
+ assert(r2.equal([2, 3, 4]));
+
+ auto r3 = [1, 2, 3, 4].padRight(0, 6);
+ size_t len = 0;
+ while (!r3.empty)
+ {
+ ++len;
+ r3.popBack;
+ }
+ assert(len == 6);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19042
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert([2, 5, 13].padRight(42, 10).chunks(5)
+ .equal!equal([[2, 5, 13, 42, 42], [42, 42, 42, 42, 42]]));
+
+ assert([1, 2, 3, 4].padRight(0, 10)[7 .. 9].equal([0, 0]));
+}
diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d
index 193ee952cf3..9c61ff5253a 100644
--- a/libphobos/src/std/range/primitives.d
+++ b/libphobos/src/std/range/primitives.d
@@ -1,70 +1,74 @@
/**
This module is a submodule of $(MREF std, range).
+It defines the bidirectional and forward range primitives for arrays:
+$(LREF empty), $(LREF front), $(LREF back), $(LREF popFront), $(LREF popBack) and $(LREF save).
+
It provides basic range functionality by defining several templates for testing
-whether a given object is a _range, and what kind of _range it is:
+whether a given object is a range, and what kind of range it is:
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE ,
$(TR $(TD $(LREF isInputRange))
- $(TD Tests if something is an $(I input _range), defined to be
+ $(TD Tests if something is an $(I input range), defined to be
something from which one can sequentially read data using the
- primitives $(D front), $(D popFront), and $(D empty).
+ primitives `front`, `popFront`, and `empty`.
))
$(TR $(TD $(LREF isOutputRange))
- $(TD Tests if something is an $(I output _range), defined to be
+ $(TD Tests if something is an $(I output range), defined to be
something to which one can sequentially write data using the
$(LREF put) primitive.
))
$(TR $(TD $(LREF isForwardRange))
- $(TD Tests if something is a $(I forward _range), defined to be an
- input _range with the additional capability that one can save one's
- current position with the $(D save) primitive, thus allowing one to
- iterate over the same _range multiple times.
+ $(TD Tests if something is a $(I forward range), defined to be an
+ input range with the additional capability that one can save one's
+ current position with the `save` primitive, thus allowing one to
+ iterate over the same range multiple times.
))
$(TR $(TD $(LREF isBidirectionalRange))
- $(TD Tests if something is a $(I bidirectional _range), that is, a
- forward _range that allows reverse traversal using the primitives $(D
- back) and $(D popBack).
+ $(TD Tests if something is a $(I bidirectional range), that is, a
+ forward range that allows reverse traversal using the primitives $(D
+ back) and `popBack`.
))
$(TR $(TD $(LREF isRandomAccessRange))
- $(TD Tests if something is a $(I random access _range), which is a
- bidirectional _range that also supports the array subscripting
- operation via the primitive $(D opIndex).
+ $(TD Tests if something is a $(I random access range), which is a
+ bidirectional range that also supports the array subscripting
+ operation via the primitive `opIndex`.
))
-)
+))
-It also provides number of templates that test for various _range capabilities:
+It also provides number of templates that test for various range capabilities:
$(BOOKTABLE ,
$(TR $(TD $(LREF hasMobileElements))
- $(TD Tests if a given _range's elements can be moved around using the
- primitives $(D moveFront), $(D moveBack), or $(D moveAt).
+ $(TD Tests if a given range's elements can be moved around using the
+ primitives `moveFront`, `moveBack`, or `moveAt`.
))
$(TR $(TD $(LREF ElementType))
- $(TD Returns the element type of a given _range.
+ $(TD Returns the element type of a given range.
))
$(TR $(TD $(LREF ElementEncodingType))
- $(TD Returns the encoding element type of a given _range.
+ $(TD Returns the encoding element type of a given range.
))
$(TR $(TD $(LREF hasSwappableElements))
- $(TD Tests if a _range is a forward _range with swappable elements.
+ $(TD Tests if a range is a forward range with swappable elements.
))
$(TR $(TD $(LREF hasAssignableElements))
- $(TD Tests if a _range is a forward _range with mutable elements.
+ $(TD Tests if a range is a forward range with mutable elements.
))
$(TR $(TD $(LREF hasLvalueElements))
- $(TD Tests if a _range is a forward _range with elements that can be
+ $(TD Tests if a range is a forward range with elements that can be
passed by reference and have their address taken.
))
$(TR $(TD $(LREF hasLength))
- $(TD Tests if a given _range has the $(D length) attribute.
+ $(TD Tests if a given range has the `length` attribute.
))
$(TR $(TD $(LREF isInfinite))
- $(TD Tests if a given _range is an $(I infinite _range).
+ $(TD Tests if a given range is an $(I infinite range).
))
$(TR $(TD $(LREF hasSlicing))
- $(TD Tests if a given _range supports the array slicing operation $(D
+ $(TD Tests if a given range supports the array slicing operation $(D
R[x .. y]).
))
)
@@ -73,51 +77,52 @@ Finally, it includes some convenience functions for manipulating ranges:
$(BOOKTABLE ,
$(TR $(TD $(LREF popFrontN))
- $(TD Advances a given _range by up to $(I n) elements.
+ $(TD Advances a given range by up to $(I n) elements.
))
$(TR $(TD $(LREF popBackN))
- $(TD Advances a given bidirectional _range from the right by up to
+ $(TD Advances a given bidirectional range from the right by up to
$(I n) elements.
))
$(TR $(TD $(LREF popFrontExactly))
- $(TD Advances a given _range by up exactly $(I n) elements.
+ $(TD Advances a given range by up exactly $(I n) elements.
))
$(TR $(TD $(LREF popBackExactly))
- $(TD Advances a given bidirectional _range from the right by exactly
+ $(TD Advances a given bidirectional range from the right by exactly
$(I n) elements.
))
$(TR $(TD $(LREF moveFront))
- $(TD Removes the front element of a _range.
+ $(TD Removes the front element of a range.
))
$(TR $(TD $(LREF moveBack))
- $(TD Removes the back element of a bidirectional _range.
+ $(TD Removes the back element of a bidirectional range.
))
$(TR $(TD $(LREF moveAt))
- $(TD Removes the $(I i)'th element of a random-access _range.
+ $(TD Removes the $(I i)'th element of a random-access range.
))
$(TR $(TD $(LREF walkLength))
- $(TD Computes the length of any _range in O(n) time.
+ $(TD Computes the length of any range in O(n) time.
))
$(TR $(TD $(LREF put))
- $(TD Outputs element $(D e) to a _range.
+ $(TD Outputs element `e` to a range.
))
)
-Source: $(PHOBOSSRC std/range/_primitives.d)
+Source: $(PHOBOSSRC std/range/primitives.d)
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha,
-and Jonathan M Davis. Credit for some of the ideas in building this module goes
-to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
+Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and
+ $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas
+ in building this module goes to
+ $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
*/
module std.range.primitives;
import std.traits;
/**
-Returns $(D true) if $(D R) is an input range. An input range must
-define the primitives $(D empty), $(D popFront), and $(D front). The
+Returns `true` if `R` is an input range. An input range must
+define the primitives `empty`, `popFront`, and `front`. The
following code should compile for any input range.
----
@@ -155,11 +160,14 @@ Also, note that Phobos code assumes that the primitives `r.front` and
running time. $(BIGOH) statements in the documentation of range functions
are made with this assumption.
+See_Also:
+ The header of $(MREF std,range) for tutorials on ranges.
+
Params:
R = type to be tested
Returns:
- true if R is an InputRange, false if not
+ `true` if R is an input range, `false` if not
*/
enum bool isInputRange(R) =
is(typeof(R.init) == R)
@@ -246,13 +254,13 @@ enum bool isInputRange(R) =
}
/+
-puts the whole raw element $(D e) into $(D r). doPut will not attempt to
-iterate, slice or transcode $(D e) in any way shape or form. It will $(B only)
-call the correct primitive ($(D r.put(e)), $(D r.front = e) or
-$(D r(0)) once.
+puts the whole raw element `e` into `r`. doPut will not attempt to
+iterate, slice or transcode `e` in any way shape or form. It will $(B only)
+call the correct primitive (`r.put(e)`, $(D r.front = e) or
+`r(e)` once.
-This can be important when $(D e) needs to be placed in $(D r) unchanged.
-Furthermore, it can be useful when working with $(D InputRange)s, as doPut
+This can be important when `e` needs to be placed in `r` unchanged.
+Furthermore, it can be useful when working with `InputRange`s, as doPut
guarantees that no more than a single element will be placed.
+/
private void doPut(R, E)(ref R r, auto ref E e)
@@ -268,6 +276,20 @@ private void doPut(R, E)(ref R r, auto ref E e)
"Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ ".");
r.put(e);
}
+ else static if (isNarrowString!R && is(const(E) == const(typeof(r[0]))))
+ {
+ // one character, we can put it
+ r[0] = e;
+ r = r[1 .. $];
+ }
+ else static if (isNarrowString!R && isNarrowString!E && is(typeof(r[] = e)))
+ {
+ // slice assign. Note that this is a duplicate from put, but because
+ // putChar uses doPut exclusively, we have to copy it here.
+ immutable len = e.length;
+ r[0 .. len] = e;
+ r = r[len .. $];
+ }
else static if (isInputRange!R)
{
static assert(is(typeof(r.front = e)),
@@ -304,7 +326,7 @@ private void doPut(R, E)(ref R r, auto ref E e)
static assert( isNativeOutputRange!(int[4][], int)); //Scary!
static assert( isNativeOutputRange!(int[4][], int[4]));
- static assert(!isNativeOutputRange!( char[], char));
+ static assert( isNativeOutputRange!( char[], char));
static assert(!isNativeOutputRange!( char[], dchar));
static assert( isNativeOutputRange!(dchar[], char));
static assert( isNativeOutputRange!(dchar[], dchar));
@@ -312,14 +334,14 @@ private void doPut(R, E)(ref R r, auto ref E e)
}
/++
-Outputs $(D e) to $(D r). The exact effect is dependent upon the two
+Outputs `e` to `r`. The exact effect is dependent upon the two
types. Several cases are accepted, as described below. The code snippets
are attempted in order, and the first to compile "wins" and gets
evaluated.
-In this table "doPut" is a method that places $(D e) into $(D r), using the
-correct primitive: $(D r.put(e)) if $(D R) defines $(D put), $(D r.front = e)
-if $(D r) is an input range (followed by $(D r.popFront())), or $(D r(e))
+In this table "doPut" is a method that places `e` into `r`, using the
+correct primitive: `r.put(e)` if `R` defines `put`, $(D r.front = e)
+if `r` is an input range (followed by `r.popFront()`), or `r(e)`
otherwise.
$(BOOKTABLE ,
@@ -328,27 +350,27 @@ $(BOOKTABLE ,
$(TH Scenario)
)
$(TR
- $(TD $(D r.doPut(e);))
- $(TD $(D R) specifically accepts an $(D E).)
+ $(TD `r.doPut(e);`)
+ $(TD `R` specifically accepts an `E`.)
)
$(TR
$(TD $(D r.doPut([ e ]);))
- $(TD $(D R) specifically accepts an $(D E[]).)
+ $(TD `R` specifically accepts an `E[]`.)
)
$(TR
- $(TD $(D r.putChar(e);))
- $(TD $(D R) accepts some form of string or character. put will
- transcode the character $(D e) accordingly.)
+ $(TD `r.putChar(e);`)
+ $(TD `R` accepts some form of string or character. put will
+ transcode the character `e` accordingly.)
)
$(TR
$(TD $(D for (; !e.empty; e.popFront()) put(r, e.front);))
- $(TD Copying range $(D E) into $(D R).)
+ $(TD Copying range `E` into `R`.)
)
)
-Tip: $(D put) should $(I not) be used "UFCS-style", e.g. $(D r.put(e)).
-Doing this may call $(D R.put) directly, by-passing any transformation
-feature provided by $(D Range.put). $(D put(r, e)) is prefered.
+Tip: `put` should $(I not) be used "UFCS-style", e.g. `r.put(e)`.
+Doing this may call `R.put` directly, by-passing any transformation
+feature provided by `Range.put`. $(D put(r, e)) is prefered.
+/
void put(R, E)(ref R r, E e)
{
@@ -358,7 +380,10 @@ void put(R, E)(ref R r, E e)
doPut(r, e);
}
//Optional optimization block for straight up array to array copy.
- else static if (isDynamicArray!R && !isNarrowString!R && isDynamicArray!E && is(typeof(r[] = e[])))
+ else static if (isDynamicArray!R &&
+ !isAutodecodableString!R &&
+ isDynamicArray!E &&
+ is(typeof(r[] = e[])))
{
immutable len = e.length;
r[0 .. len] = e[];
@@ -386,7 +411,7 @@ void put(R, E)(ref R r, E e)
{
//Special optimization: If E is a narrow string, and r accepts characters no-wider than the string's
//Then simply feed the characters 1 by 1.
- static if (isNarrowString!E && (
+ static if (isAutodecodableString!E && !isAggregateType!E && (
(is(E : const char[]) && is(typeof(doPut(r, char.max))) && !is(typeof(doPut(r, dchar.max))) &&
!is(typeof(doPut(r, wchar.max)))) ||
(is(E : const wchar[]) && is(typeof(doPut(r, wchar.max))) && !is(typeof(doPut(r, dchar.max)))) ) )
@@ -406,9 +431,78 @@ void put(R, E)(ref R r, E e)
}
}
+/**
+ * When an output range's `put` method only accepts elements of type
+ * `T`, use the global `put` to handle outputting a `T[]` to the range
+ * or vice-versa.
+ */
+@safe pure unittest
+{
+ import std.traits : isSomeChar;
+
+ static struct A
+ {
+ string data;
+
+ void put(C)(C c) if (isSomeChar!C)
+ {
+ data ~= c;
+ }
+ }
+ static assert(isOutputRange!(A, char));
+
+ auto a = A();
+ put(a, "Hello");
+ assert(a.data == "Hello");
+}
+
+/**
+ * `put` treats dynamic arrays as array slices, and will call `popFront`
+ * on the slice after an element has been copied.
+ *
+ * Be sure to save the position of the array before calling `put`.
+ */
+@safe pure nothrow unittest
+{
+ int[] a = [1, 2, 3], b = [10, 20];
+ auto c = a;
+ put(a, b);
+ assert(c == [10, 20, 3]);
+ // at this point, a was advanced twice, so it only contains
+ // its last element while c represents the whole array
+ assert(a == [3]);
+}
+
+/**
+ * It's also possible to `put` any width strings or characters into narrow
+ * strings -- put does the conversion for you.
+ *
+ * Note that putting the same width character as the target buffer type is
+ * `nothrow`, but transcoding can throw a $(REF UTFException, std, utf).
+ */
+@safe pure unittest
+{
+ // the elements must be mutable, so using string or const(char)[]
+ // won't compile
+ char[] s1 = new char[13];
+ auto r1 = s1;
+ put(r1, "Hello, World!"w);
+ assert(s1 == "Hello, World!");
+}
+
+@safe pure nothrow unittest
+{
+ // same thing, just using same character width.
+ char[] s1 = new char[13];
+ auto r1 = s1;
+ put(r1, "Hello, World!");
+ assert(s1 == "Hello, World!");
+}
+
+
@safe pure nothrow @nogc unittest
{
- static struct R() { void put(in char[]) {} }
+ static struct R() { void put(scope const(char)[]) {} }
R!() r;
put(r, 'a');
}
@@ -418,14 +512,14 @@ void put(R, E)(ref R r, E e)
private void putChar(R, E)(ref R r, E e)
if (isSomeChar!E)
{
- ////@@@9186@@@: Can't use (E[]).init
+ // https://issues.dlang.org/show_bug.cgi?id=9186: Can't use (E[]).init
ref const( char)[] cstringInit();
ref const(wchar)[] wstringInit();
ref const(dchar)[] dstringInit();
- enum csCond = !isDynamicArray!R && is(typeof(doPut(r, cstringInit())));
- enum wsCond = !isDynamicArray!R && is(typeof(doPut(r, wstringInit())));
- enum dsCond = !isDynamicArray!R && is(typeof(doPut(r, dstringInit())));
+ enum csCond = is(typeof(doPut(r, cstringInit())));
+ enum wsCond = is(typeof(doPut(r, wstringInit())));
+ enum dsCond = is(typeof(doPut(r, dstringInit())));
//Use "max" to avoid static type demotion
enum ccCond = is(typeof(doPut(r, char.max)));
@@ -469,7 +563,7 @@ pure @safe unittest
@safe pure unittest
{
- static struct R() { void put(in char[]) {} }
+ static struct R() { void put(scope const(char)[]) {} }
R!() r;
putChar(r, 'a');
}
@@ -488,15 +582,6 @@ pure @safe unittest
@safe unittest
{
- int[] a = [1, 2, 3], b = [10, 20];
- auto c = a;
- put(a, b);
- assert(c == [10, 20, 3]);
- assert(a == [3]);
-}
-
-@safe unittest
-{
int[] a = new int[10];
int b;
static assert(isInputRange!(typeof(a)));
@@ -505,7 +590,7 @@ pure @safe unittest
@safe unittest
{
- void myprint(in char[] s) { }
+ void myprint(scope const(char)[] s) { }
auto r = &myprint;
put(r, 'a');
}
@@ -531,9 +616,64 @@ pure @safe unittest
char[] a = new char[10];
static assert(!__traits(compiles, put(a, 1.0L)));
static assert(!__traits(compiles, put(a, 1)));
- // char[] is NOT output range.
- static assert(!__traits(compiles, put(a, 'a')));
- static assert(!__traits(compiles, put(a, "ABC")));
+ //char[] is now an output range for char, wchar, dchar, and ranges of such.
+ static assert(__traits(compiles, putChar(a, 'a')));
+ static assert(__traits(compiles, put(a, wchar('a'))));
+ static assert(__traits(compiles, put(a, dchar('a'))));
+ static assert(__traits(compiles, put(a, "ABC")));
+ static assert(__traits(compiles, put(a, "ABC"w)));
+ static assert(__traits(compiles, put(a, "ABC"d)));
+}
+
+@safe unittest
+{
+ // attempt putting into narrow strings by transcoding
+ char[] a = new char[10];
+ auto b = a;
+ put(a, "ABC"w);
+ assert(b[0 .. 3] == "ABC");
+ assert(a.length == 7);
+
+ a = b; // reset
+ put(a, 'λ');
+ assert(b[0 .. 2] == "λ");
+ assert(a.length == 8);
+
+ a = b; // reset
+ put(a, "ABC"d);
+ assert(b[0 .. 3] == "ABC");
+ assert(a.length == 7);
+
+ a = b; // reset
+ put(a, 'ð·');
+ assert(b[0 .. 4] == "ð·");
+ assert(a.length == 6);
+
+ wchar[] aw = new wchar[10];
+ auto bw = aw;
+ put(aw, "ABC");
+ assert(bw[0 .. 3] == "ABC"w);
+ assert(aw.length == 7);
+
+ aw = bw; // reset
+ put(aw, 'λ');
+ assert(bw[0 .. 1] == "λ"w);
+ assert(aw.length == 9);
+
+ aw = bw; // reset
+ put(aw, "ABC"d);
+ assert(bw[0 .. 3] == "ABC"w);
+ assert(aw.length == 7);
+
+ aw = bw; // reset
+ put(aw, 'ð·');
+ assert(bw[0 .. 2] == "ð·"w);
+ assert(aw.length == 8);
+
+ aw = bw; // reset
+ put(aw, "ð·"); // try transcoding from char[]
+ assert(bw[0 .. 2] == "ð·"w);
+ assert(aw.length == 8);
}
@safe unittest
@@ -578,8 +718,8 @@ pure @safe unittest
void popFront(){ end = true; }
}
LockingTextWriter w;
- RetroResult r;
- put(w, r);
+ RetroResult re;
+ put(w, re);
}
@system unittest
@@ -612,19 +752,19 @@ pure @safe unittest
putChar(p, cast(dchar)'a');
//Source Char
- foreach (SC; AliasSeq!(char, wchar, dchar))
- {
+ static foreach (SC; AliasSeq!(char, wchar, dchar))
+ {{
SC ch = 'I';
dchar dh = '♥';
immutable(SC)[] s = "日本語ï¼";
immutable(SC)[][] ss = ["日本語", "ãŒ", "好ã", "ã§ã™ã‹", "?"];
//Target Char
- foreach (TC; AliasSeq!(char, wchar, dchar))
+ static foreach (TC; AliasSeq!(char, wchar, dchar))
{
//Testing PutC and PutS
- foreach (Type; AliasSeq!(PutC!TC, PutS!TC))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (Type; AliasSeq!(PutC!TC, PutS!TC))
+ {{
Type type;
auto sink = new Type();
@@ -640,9 +780,9 @@ pure @safe unittest
put(value, ss);
assert(value.result == "I♥日本語ï¼æ—¥æœ¬èªžãŒå¥½ãã§ã™ã‹ï¼Ÿ");
}
- }();
+ }}
}
- }
+ }}
}
@safe unittest
@@ -662,27 +802,27 @@ pure @safe unittest
put(c, "hello"d);
}
+// https://issues.dlang.org/show_bug.cgi?id=9823
@system unittest
{
- // issue 9823
const(char)[] r;
void delegate(const(char)[]) dg = (s) { r = s; };
put(dg, ["ABC"]);
assert(r == "ABC");
}
+// https://issues.dlang.org/show_bug.cgi?id=10571
@safe unittest
{
- // issue 10571
- import std.format;
+ import std.format.write : formattedWrite;
string buf;
- formattedWrite((in char[] s) { buf ~= s; }, "%s", "hello");
+ formattedWrite((scope const(char)[] s) { buf ~= s; }, "%s", "hello");
assert(buf == "hello");
}
@safe unittest
{
- import std.format;
+ import std.format.write : formattedWrite;
import std.meta : AliasSeq;
struct PutC(C)
{
@@ -716,8 +856,8 @@ pure @safe unittest
}
void foo()
{
- foreach (C; AliasSeq!(char, wchar, dchar))
- {
+ static foreach (C; AliasSeq!(char, wchar, dchar))
+ {{
formattedWrite((C c){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
formattedWrite((const(C)[]){}, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
formattedWrite(PutC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
@@ -728,21 +868,21 @@ pure @safe unittest
formattedWrite(callS, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
formattedWrite(FrontC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
formattedWrite(FrontS!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
- }
+ }}
formattedWrite((dchar[]).init, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
}
}
/+
-Returns $(D true) if $(D R) is a native output range for elements of type
-$(D E). An output range is defined functionally as a range that
+Returns `true` if `R` is a native output range for elements of type
+`E`. An output range is defined functionally as a range that
supports the operation $(D doPut(r, e)) as defined above. if $(D doPut(r, e))
-is valid, then $(D put(r,e)) will have the same behavior.
+is valid, then `put(r,e)` will have the same behavior.
-The two guarantees isNativeOutputRange gives over the larger $(D isOutputRange)
+The two guarantees isNativeOutputRange gives over the larger `isOutputRange`
are:
-1: $(D e) is $(B exactly) what will be placed (not $(D [e]), for example).
-2: if $(D E) is a non $(empty) $(D InputRange), then placing $(D e) is
+1: `e` is $(B exactly) what will be placed (not `[e]`, for example).
+2: if `E` is a non $(empty) `InputRange`, then placing `e` is
guaranteed to not overflow the range.
+/
package(std) enum bool isNativeOutputRange(R, E) =
@@ -763,9 +903,12 @@ package(std) enum bool isNativeOutputRange(R, E) =
}
/++
-Returns $(D true) if $(D R) is an output range for elements of type
-$(D E). An output range is defined functionally as a range that
+Returns `true` if `R` is an output range for elements of type
+`E`. An output range is defined functionally as a range that
supports the operation $(D put(r, e)) as defined above.
+
+See_Also:
+ The header of $(MREF std,range) for tutorials on ranges.
+/
enum bool isOutputRange(R, E) =
is(typeof(put(lvalueOf!R, lvalueOf!E)));
@@ -773,10 +916,10 @@ enum bool isOutputRange(R, E) =
///
@safe unittest
{
- void myprint(in char[] s) { }
+ void myprint(scope const(char)[] s) { }
static assert(isOutputRange!(typeof(&myprint), char));
- static assert(!isOutputRange!(char[], char));
+ static assert( isOutputRange!(char[], char));
static assert( isOutputRange!(dchar[], wchar));
static assert( isOutputRange!(dchar[], dchar));
}
@@ -791,7 +934,7 @@ enum bool isOutputRange(R, E) =
static assert( isOutputRange!(Appender!string, string));
static assert( isOutputRange!(Appender!string*, string));
static assert(!isOutputRange!(Appender!string, int));
- static assert(!isOutputRange!(wchar[], wchar));
+ static assert( isOutputRange!(wchar[], wchar));
static assert( isOutputRange!(dchar[], char));
static assert( isOutputRange!(dchar[], string));
static assert( isOutputRange!(dchar[], wstring));
@@ -803,9 +946,9 @@ enum bool isOutputRange(R, E) =
/**
-Returns $(D true) if $(D R) is a forward range. A forward range is an
-input range $(D r) that can save "checkpoints" by saving $(D r.save)
-to another value of type $(D R). Notable examples of input ranges that
+Returns `true` if `R` is a forward range. A forward range is an
+input range `r` that can save "checkpoints" by saving `r.save`
+to another value of type `R`. Notable examples of input ranges that
are $(I not) forward ranges are file/socket ranges; copying such a
range will not save the position in the stream, and they most likely
reuse an internal buffer as the entire stream does not sit in
@@ -821,14 +964,27 @@ auto s1 = r1.save;
static assert(is(typeof(s1) == R));
----
-Saving a range is not duplicating it; in the example above, $(D r1)
-and $(D r2) still refer to the same underlying data. They just
+Saving a range is not duplicating it; in the example above, `r1`
+and `r2` still refer to the same underlying data. They just
navigate that data independently.
The semantics of a forward range (not checkable during compilation)
are the same as for an input range, with the additional requirement
that backtracking must be possible by saving a copy of the range
-object with $(D save) and using it later.
+object with `save` and using it later.
+
+`save` behaves in many ways like a copy constructor, and its
+implementation typically is done using copy construction.
+
+The existence of a copy constructor, however, does not imply
+the range is a forward range. For example, a range that reads
+from a TTY consumes its input and cannot save its place and
+read it again, and so cannot be a forward range and cannot
+have a `save` function.
+
+
+See_Also:
+ The header of $(MREF std,range) for tutorials on ranges.
*/
enum bool isForwardRange(R) = isInputRange!R
&& is(ReturnType!((R r) => r.save) == R);
@@ -856,18 +1012,21 @@ enum bool isForwardRange(R) = isInputRange!R
}
/**
-Returns $(D true) if $(D R) is a bidirectional range. A bidirectional
-range is a forward range that also offers the primitives $(D back) and
-$(D popBack). The following code should compile for any bidirectional
+Returns `true` if `R` is a bidirectional range. A bidirectional
+range is a forward range that also offers the primitives `back` and
+`popBack`. The following code should compile for any bidirectional
range.
The semantics of a bidirectional range (not checkable during
-compilation) are assumed to be the following ($(D r) is an object of
-type $(D R)):
+compilation) are assumed to be the following (`r` is an object of
+type `R`):
+
+$(UL $(LI `r.back` returns (possibly a reference to) the last
+element in the range. Calling `r.back` is allowed only if calling
+`r.empty` has, or would have, returned `false`.))
-$(UL $(LI $(D r.back) returns (possibly a reference to) the last
-element in the range. Calling $(D r.back) is allowed only if calling
-$(D r.empty) has, or would have, returned $(D false).))
+See_Also:
+ The header of $(MREF std,range) for tutorials on ranges.
*/
enum bool isBidirectionalRange(R) = isForwardRange!R
&& is(typeof((R r) => r.popBack))
@@ -912,27 +1071,30 @@ enum bool isBidirectionalRange(R) = isForwardRange!R
}
/**
-Returns $(D true) if $(D R) is a random-access range. A random-access
+Returns `true` if `R` is a random-access range. A random-access
range is a bidirectional range that also offers the primitive $(D
-opIndex), OR an infinite forward range that offers $(D opIndex). In
-either case, the range must either offer $(D length) or be
+opIndex), OR an infinite forward range that offers `opIndex`. In
+either case, the range must either offer `length` or be
infinite. The following code should compile for any random-access
range.
The semantics of a random-access range (not checkable during
-compilation) are assumed to be the following ($(D r) is an object of
-type $(D R)): $(UL $(LI $(D r.opIndex(n)) returns a reference to the
-$(D n)th element in the range.))
+compilation) are assumed to be the following (`r` is an object of
+type `R`): $(UL $(LI `r.opIndex(n)` returns a reference to the
+`n`th element in the range.))
-Although $(D char[]) and $(D wchar[]) (as well as their qualified
-versions including $(D string) and $(D wstring)) are arrays, $(D
-isRandomAccessRange) yields $(D false) for them because they use
+Although `char[]` and `wchar[]` (as well as their qualified
+versions including `string` and `wstring`) are arrays, $(D
+isRandomAccessRange) yields `false` for them because they use
variable-length encodings (UTF-8 and UTF-16 respectively). These types
are bidirectional ranges only.
+
+See_Also:
+ The header of $(MREF std,range) for tutorials on ranges.
*/
enum bool isRandomAccessRange(R) =
is(typeof(lvalueOf!R[1]) == ElementType!R)
- && !isNarrowString!R
+ && !(isAutodecodableString!R && !isAggregateType!R)
&& isForwardRange!R
&& (isBidirectionalRange!R || isInfinite!R)
&& (hasLength!R || isInfinite!R)
@@ -942,7 +1104,7 @@ enum bool isRandomAccessRange(R) =
///
@safe unittest
{
- import std.traits : isNarrowString;
+ import std.traits : isAggregateType, isAutodecodableString;
alias R = int[];
@@ -954,7 +1116,7 @@ enum bool isRandomAccessRange(R) =
auto e = r[1]; // can index
auto f = r.front;
static assert(is(typeof(e) == typeof(f))); // same type for indexed and front
- static assert(!isNarrowString!R); // narrow strings cannot be indexed as ranges
+ static assert(!(isAutodecodableString!R && !isAggregateType!R)); // narrow strings cannot be indexed as ranges
static assert(hasLength!R || isInfinite!R); // must have length or be infinite
// $ must work as it does with arrays if opIndex works with $
@@ -1051,10 +1213,10 @@ enum bool isRandomAccessRange(R) =
}
/**
-Returns $(D true) iff $(D R) is an input range that supports the
-$(D moveFront) primitive, as well as $(D moveBack) and $(D moveAt) if it's a
+Returns `true` iff `R` is an input range that supports the
+`moveFront` primitive, as well as `moveBack` and `moveAt` if it's a
bidirectional or random access range. These may be explicitly implemented, or
-may work via the default behavior of the module level functions $(D moveFront)
+may work via the default behavior of the module level functions `moveFront`
and friends. The following code should compile for any range
with mobile elements.
@@ -1101,12 +1263,12 @@ enum bool hasMobileElements(R) =
}
/**
-The element type of $(D R). $(D R) does not have to be a range. The
-element type is determined as the type yielded by $(D r.front) for an
-object $(D r) of type $(D R). For example, $(D ElementType!(T[])) is
-$(D T) if $(D T[]) isn't a narrow string; if it is, the element type is
-$(D dchar). If $(D R) doesn't have $(D front), $(D ElementType!R) is
-$(D void).
+The element type of `R`. `R` does not have to be a range. The
+element type is determined as the type yielded by `r.front` for an
+object `r` of type `R`. For example, `ElementType!(T[])` is
+`T` if `T[]` isn't a narrow string; if it is, the element type is
+`dchar`. If `R` doesn't have `front`, `ElementType!R` is
+`void`.
*/
template ElementType(R)
{
@@ -1167,7 +1329,8 @@ template ElementType(R)
static assert(is(ElementType!(char[0]) == dchar));
}
-@safe unittest //11336
+// https://issues.dlang.org/show_bug.cgi?id=11336
+@safe unittest
{
static struct S
{
@@ -1176,7 +1339,8 @@ template ElementType(R)
static assert(is(ElementType!(S[]) == S));
}
-@safe unittest // 11401
+// https://issues.dlang.org/show_bug.cgi?id=11401
+@safe unittest
{
// ElementType should also work for non-@propety 'front'
struct E { ushort id; }
@@ -1188,11 +1352,11 @@ template ElementType(R)
}
/**
-The encoding element type of $(D R). For narrow strings ($(D char[]),
-$(D wchar[]) and their qualified variants including $(D string) and
-$(D wstring)), $(D ElementEncodingType) is the character type of the
-string. For all other types, $(D ElementEncodingType) is the same as
-$(D ElementType).
+The encoding element type of `R`. For narrow strings (`char[]`,
+`wchar[]` and their qualified variants including `string` and
+`wstring`), `ElementEncodingType` is the character type of the
+string. For all other types, `ElementEncodingType` is the same as
+`ElementType`.
*/
template ElementEncodingType(R)
{
@@ -1253,7 +1417,7 @@ template ElementEncodingType(R)
}
/**
-Returns $(D true) if $(D R) is an input range and has swappable
+Returns `true` if `R` is an input range and has swappable
elements. The following code should compile for any range
with swappable elements.
@@ -1291,7 +1455,7 @@ template hasSwappableElements(R)
}
/**
-Returns $(D true) if $(D R) is an input range and has mutable
+Returns `true` if `R` is an input range and has mutable
elements. The following code should compile for any range
with assignable elements.
@@ -1325,7 +1489,7 @@ enum bool hasAssignableElements(R) = isInputRange!R
}
/**
-Tests whether the range $(D R) has lvalue elements. These are defined as
+Tests whether the range `R` has lvalue elements. These are defined as
elements that can be passed by reference and have their address taken.
The following code should compile for any range with lvalue elements.
----
@@ -1338,11 +1502,19 @@ static if (isRandomAccessRange!R) passByRef(r[0]);
----
*/
enum bool hasLvalueElements(R) = isInputRange!R
- && is(typeof(((ref x) => x)(lvalueOf!R.front)))
+ && is(typeof(isLvalue(lvalueOf!R.front)))
&& (!isBidirectionalRange!R
- || is(typeof(((ref x) => x)(lvalueOf!R.back))))
+ || is(typeof(isLvalue(lvalueOf!R.back))))
&& (!isRandomAccessRange!R
- || is(typeof(((ref x) => x)(lvalueOf!R[0]))));
+ || is(typeof(isLvalue(lvalueOf!R[0]))));
+
+/* Compile successfully if argument of type T is an lvalue
+ */
+private void isLvalue(T)(T)
+if (0);
+
+private void isLvalue(T)(ref T)
+if (1);
///
@safe unittest
@@ -1392,7 +1564,8 @@ length, use $(REF representation, std, string) or $(REF byCodeUnit, std, utf).
template hasLength(R)
{
static if (is(typeof(((R* r) => r.length)(null)) Length))
- enum bool hasLength = is(Length == size_t) && !isNarrowString!R;
+ enum bool hasLength = is(Length == size_t) &&
+ !(isAutodecodableString!R && !isAggregateType!R);
else
enum bool hasLength = false;
}
@@ -1411,7 +1584,7 @@ template hasLength(R)
}
// test combinations which are invalid on some platforms
-unittest
+@safe unittest
{
struct A { ulong length; }
struct B { @property uint length() { return 0; } }
@@ -1429,7 +1602,7 @@ unittest
}
// test combinations which are invalid on all platforms
-unittest
+@safe unittest
{
struct A { long length; }
struct B { int length; }
@@ -1442,9 +1615,9 @@ unittest
}
/**
-Returns $(D true) if $(D R) is an infinite input range. An
+Returns `true` if `R` is an infinite input range. An
infinite input range is an input range that has a statically-defined
-enumerated member called $(D empty) that is always $(D false),
+enumerated member called `empty` that is always `false`,
for example:
----
@@ -1473,17 +1646,17 @@ template isInfinite(R)
}
/**
-Returns $(D true) if $(D R) offers a slicing operator with integral boundaries
+Returns `true` if `R` offers a slicing operator with integral boundaries
that returns a forward range type.
-For finite ranges, the result of $(D opSlice) must be of the same type as the
-original range type. If the range defines $(D opDollar), then it must support
+For finite ranges, the result of `opSlice` must be of the same type as the
+original range type. If the range defines `opDollar`, then it must support
subtraction.
-For infinite ranges, when $(I not) using $(D opDollar), the result of
-$(D opSlice) must be the result of $(LREF take) or $(LREF takeExactly) on the
+For infinite ranges, when $(I not) using `opDollar`, the result of
+`opSlice` must be the result of $(LREF take) or $(LREF takeExactly) on the
original range (they both return the same type for infinite ranges). However,
-when using $(D opDollar), the result of $(D opSlice) must be that of the
+when using `opDollar`, the result of `opSlice` must be that of the
original range type.
The following expression must be true for `hasSlicing` to be `true`:
@@ -1503,7 +1676,7 @@ The following expression must be true for `hasSlicing` to be `true`:
----
*/
enum bool hasSlicing(R) = isForwardRange!R
- && !isNarrowString!R
+ && !(isAutodecodableString!R && !isAggregateType!R)
&& is(ReturnType!((R r) => r[1 .. 1].length) == size_t)
&& (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R)
&& (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R))
@@ -1560,23 +1733,23 @@ enum bool hasSlicing(R) = isForwardRange!R
}
/**
-This is a best-effort implementation of $(D length) for any kind of
+This is a best-effort implementation of `length` for any kind of
range.
-If $(D hasLength!Range), simply returns $(D range.length) without
-checking $(D upTo) (when specified).
+If `hasLength!Range`, simply returns `range.length` without
+checking `upTo` (when specified).
Otherwise, walks the range through its length and returns the number
-of elements seen. Performes $(BIGOH n) evaluations of $(D range.empty)
-and $(D range.popFront()), where $(D n) is the effective length of $(D
+of elements seen. Performes $(BIGOH n) evaluations of `range.empty`
+and `range.popFront()`, where `n` is the effective length of $(D
range).
-The $(D upTo) parameter is useful to "cut the losses" in case
+The `upTo` parameter is useful to "cut the losses" in case
the interest is in seeing whether the range has at least some number
-of elements. If the parameter $(D upTo) is specified, stops if $(D
-upTo) steps have been taken and returns $(D upTo).
+of elements. If the parameter `upTo` is specified, stops if $(D
+upTo) steps have been taken and returns `upTo`.
-Infinite ranges are compatible, provided the parameter $(D upTo) is
+Infinite ranges are compatible, provided the parameter `upTo` is
specified, in which case the implementation simply returns upTo.
*/
auto walkLength(Range)(Range range)
@@ -1587,6 +1760,20 @@ if (isInputRange!Range && !isInfinite!Range)
else
{
size_t result;
+ static if (autodecodeStrings && isNarrowString!Range)
+ {
+ import std.utf : codeUnitLimit;
+ result = range.length;
+ foreach (const i, const c; range)
+ {
+ if (c >= codeUnitLimit!Range)
+ {
+ result = i;
+ break;
+ }
+ }
+ range = range[result .. $];
+ }
for ( ; !range.empty ; range.popFront() )
++result;
return result;
@@ -1603,12 +1790,38 @@ if (isInputRange!Range)
else
{
size_t result;
+ static if (autodecodeStrings && isNarrowString!Range)
+ {
+ import std.utf : codeUnitLimit;
+ result = upTo > range.length ? range.length : upTo;
+ foreach (const i, const c; range[0 .. result])
+ {
+ if (c >= codeUnitLimit!Range)
+ {
+ result = i;
+ break;
+ }
+ }
+ range = range[result .. $];
+ }
for ( ; result < upTo && !range.empty ; range.popFront() )
++result;
return result;
}
}
+///
+@safe unittest
+{
+ import std.range : iota;
+
+ assert(10.iota.walkLength == 10);
+ // iota has a length function, and therefore the
+ // doesn't have to be walked, and the upTo
+ // parameter is ignored
+ assert(10.iota.walkLength(5) == 10);
+}
+
@safe unittest
{
import std.algorithm.iteration : filter;
@@ -1637,18 +1850,18 @@ if (isInputRange!Range)
}
/**
- Eagerly advances $(D r) itself (not a copy) up to $(D n) times (by
- calling $(D r.popFront)). $(D popFrontN) takes $(D r) by $(D ref),
+ `popFrontN` eagerly advances `r` itself (not a copy) up to `n` times
+ (by calling `r.popFront`). `popFrontN` takes `r` by `ref`,
so it mutates the original range. Completes in $(BIGOH 1) steps for ranges
that support slicing and have length.
Completes in $(BIGOH n) time for all other ranges.
- Returns:
- How much $(D r) was actually advanced, which may be less than $(D n) if
- $(D r) did not have at least $(D n) elements.
+ `popBackN` behaves the same as `popFrontN` but instead removes
+ elements from the back of the (bidirectional) range instead of the front.
- $(D popBackN) will behave the same but instead removes elements from
- the back of the (bidirectional) range instead of the front.
+ Returns:
+ How much `r` was actually advanced, which may be less than `n` if
+ `r` did not have at least `n` elements.
See_Also: $(REF drop, std, range), $(REF dropBack, std, range)
*/
@@ -1766,24 +1979,24 @@ if (isBidirectionalRange!Range)
}
/**
- Eagerly advances $(D r) itself (not a copy) exactly $(D n) times (by
- calling $(D r.popFront)). $(D popFrontExactly) takes $(D r) by $(D ref),
+ Eagerly advances `r` itself (not a copy) exactly `n` times (by
+ calling `r.popFront`). `popFrontExactly` takes `r` by `ref`,
so it mutates the original range. Completes in $(BIGOH 1) steps for ranges
that support slicing, and have either length or are infinite.
Completes in $(BIGOH n) time for all other ranges.
- Note: Unlike $(LREF popFrontN), $(D popFrontExactly) will assume that the
- range holds at least $(D n) elements. This makes $(D popFrontExactly)
- faster than $(D popFrontN), but it also means that if $(D range) does
- not contain at least $(D n) elements, it will attempt to call $(D popFront)
+ Note: Unlike $(LREF popFrontN), `popFrontExactly` will assume that the
+ range holds at least `n` elements. This makes `popFrontExactly`
+ faster than `popFrontN`, but it also means that if `range` does
+ not contain at least `n` elements, it will attempt to call `popFront`
on an empty range, which is undefined behavior. So, only use
- $(D popFrontExactly) when it is guaranteed that $(D range) holds at least
- $(D n) elements.
+ `popFrontExactly` when it is guaranteed that `range` holds at least
+ `n` elements.
- $(D popBackExactly) will behave the same but instead removes elements from
+ `popBackExactly` will behave the same but instead removes elements from
the back of the (bidirectional) range instead of the front.
- See_Also: $(REF dropExcatly, std, range), $(REF dropBackExactly, std, range)
+ See_Also: $(REF dropExactly, std, range), $(REF dropBackExactly, std, range)
*/
void popFrontExactly(Range)(ref Range r, size_t n)
if (isInputRange!Range)
@@ -1842,9 +2055,9 @@ if (isBidirectionalRange!Range)
}
/**
- Moves the front of $(D r) out and returns it. Leaves $(D r.front) in a
+ Moves the front of `r` out and returns it. Leaves `r.front` in a
destroyable state that does not allocate any resources (usually equal
- to its $(D .init) value).
+ to its `.init` value).
*/
ElementType!R moveFront(R)(R r)
{
@@ -1900,9 +2113,9 @@ ElementType!R moveFront(R)(R r)
}
/**
- Moves the back of $(D r) out and returns it. Leaves $(D r.back) in a
+ Moves the back of `r` out and returns it. Leaves `r.back` in a
destroyable state that does not allocate any resources (usually equal
- to its $(D .init) value).
+ to its `.init` value).
*/
ElementType!R moveBack(R)(R r)
{
@@ -1946,9 +2159,9 @@ ElementType!R moveBack(R)(R r)
}
/**
- Moves element at index $(D i) of $(D r) out and returns it. Leaves $(D
+ Moves element at index `i` of `r` out and returns it. Leaves $(D
r[i]) in a destroyable state that does not allocate any resources
- (usually equal to its $(D .init) value).
+ (usually equal to its `.init` value).
*/
ElementType!R moveAt(R)(R r, size_t i)
{
@@ -2004,12 +2217,13 @@ ElementType!R moveAt(R)(R r, size_t i)
}
/**
-Implements the range interface primitive $(D empty) for built-in
-arrays. Due to the fact that nonmember functions can be called with
-the first argument using the dot notation, $(D array.empty) is
-equivalent to $(D empty(array)).
+Implements the range interface primitive `empty` for types that
+obey $(LREF hasLength) property and for narrow strings. Due to the
+fact that nonmember functions can be called with the first argument
+using the dot notation, `a.empty` is equivalent to `empty(a)`.
*/
-@property bool empty(T)(in T[] a) @safe pure nothrow @nogc
+@property bool empty(T)(auto ref scope T a)
+if (is(typeof(a.length) : size_t))
{
return !a.length;
}
@@ -2020,16 +2234,21 @@ equivalent to $(D empty(array)).
auto a = [ 1, 2, 3 ];
assert(!a.empty);
assert(a[3 .. $].empty);
+
+ int[string] b;
+ assert(b.empty);
+ b["zero"] = 0;
+ assert(!b.empty);
}
/**
-Implements the range interface primitive $(D save) for built-in
+Implements the range interface primitive `save` for built-in
arrays. Due to the fact that nonmember functions can be called with
-the first argument using the dot notation, $(D array.save) is
-equivalent to $(D save(array)). The function does not duplicate the
+the first argument using the dot notation, `array.save` is
+equivalent to `save(array)`. The function does not duplicate the
content of the array, it simply returns its argument.
*/
-@property T[] save(T)(T[] a) @safe pure nothrow @nogc
+@property inout(T)[] save(T)(return scope inout(T)[] a) @safe pure nothrow @nogc
{
return a;
}
@@ -2043,15 +2262,15 @@ content of the array, it simply returns its argument.
}
/**
-Implements the range interface primitive $(D popFront) for built-in
+Implements the range interface primitive `popFront` for built-in
arrays. Due to the fact that nonmember functions can be called with
-the first argument using the dot notation, $(D array.popFront) is
-equivalent to $(D popFront(array)). For $(GLOSSARY narrow strings),
-$(D popFront) automatically advances to the next $(GLOSSARY code
+the first argument using the dot notation, `array.popFront` is
+equivalent to `popFront(array)`. For $(GLOSSARY narrow strings),
+`popFront` automatically advances to the next $(GLOSSARY code
point).
*/
-void popFront(T)(ref T[] a) @safe pure nothrow @nogc
-if (!isNarrowString!(T[]) && !is(T[] == void[]))
+void popFront(T)(scope ref inout(T)[] a) @safe pure nothrow @nogc
+if (!isAutodecodableString!(T[]) && !is(T[] == void[]))
{
assert(a.length, "Attempting to popFront() past the end of an array of " ~ T.stringof);
a = a[1 .. $];
@@ -2065,7 +2284,7 @@ if (!isNarrowString!(T[]) && !is(T[] == void[]))
assert(a == [ 2, 3 ]);
}
-version (unittest)
+@safe unittest
{
static assert(!is(typeof({ int[4] a; popFront(a); })));
static assert(!is(typeof({ immutable int[] a; popFront(a); })));
@@ -2073,14 +2292,14 @@ version (unittest)
}
/// ditto
-void popFront(C)(ref C[] str) @trusted pure nothrow
-if (isNarrowString!(C[]))
+void popFront(C)(scope ref inout(C)[] str) @trusted pure nothrow
+if (isAutodecodableString!(C[]))
{
import std.algorithm.comparison : min;
assert(str.length, "Attempting to popFront() past the end of an array of " ~ C.stringof);
- static if (is(Unqual!C == char))
+ static if (is(immutable C == immutable char))
{
static immutable ubyte[] charWidthTab = [
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -2090,17 +2309,10 @@ if (isNarrowString!(C[]))
];
immutable c = str[0];
- if (c < 192)
- {
- str = str.ptr[1 .. str.length];
- }
- else
- {
- str = str.ptr[min(str.length, charWidthTab.ptr[c - 192]) .. str.length];
- }
-
+ immutable charWidth = c < 192 ? 1 : charWidthTab.ptr[c - 192];
+ str = str.ptr[min(str.length, charWidth) .. str.length];
}
- else static if (is(Unqual!C == wchar))
+ else static if (is(immutable C == immutable wchar))
{
immutable u = str[0];
immutable seqLen = 1 + (u >= 0xD800 && u <= 0xDBFF);
@@ -2113,8 +2325,8 @@ if (isNarrowString!(C[]))
{
import std.meta : AliasSeq;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
S s = "\xC2\xA9hello";
s.popFront();
assert(s == "hello");
@@ -2129,7 +2341,7 @@ if (isNarrowString!(C[]))
static assert(!is(typeof({ immutable S a; popFront(a); })));
static assert(!is(typeof({ typeof(S.init[0])[4] a; popFront(a); })));
- }
+ }}
C[] _eatString(C)(C[] str)
{
@@ -2144,7 +2356,8 @@ if (isNarrowString!(C[]))
static assert(checkCTFEW.empty);
}
-@safe unittest // issue 16090
+// https://issues.dlang.org/show_bug.cgi?id=16090
+@safe unittest
{
string s = "\u00E4";
assert(s.length == 2);
@@ -2165,14 +2378,14 @@ if (isNarrowString!(C[]))
}
/**
-Implements the range interface primitive $(D popBack) for built-in
+Implements the range interface primitive `popBack` for built-in
arrays. Due to the fact that nonmember functions can be called with
-the first argument using the dot notation, $(D array.popBack) is
-equivalent to $(D popBack(array)). For $(GLOSSARY narrow strings), $(D
+the first argument using the dot notation, `array.popBack` is
+equivalent to `popBack(array)`. For $(GLOSSARY narrow strings), $(D
popFront) automatically eliminates the last $(GLOSSARY code point).
*/
-void popBack(T)(ref T[] a) @safe pure nothrow @nogc
-if (!isNarrowString!(T[]) && !is(T[] == void[]))
+void popBack(T)(scope ref inout(T)[] a) @safe pure nothrow @nogc
+if (!isAutodecodableString!(T[]) && !is(T[] == void[]))
{
assert(a.length);
a = a[0 .. $ - 1];
@@ -2186,7 +2399,7 @@ if (!isNarrowString!(T[]) && !is(T[] == void[]))
assert(a == [ 1, 2 ]);
}
-version (unittest)
+@safe unittest
{
static assert(!is(typeof({ immutable int[] a; popBack(a); })));
static assert(!is(typeof({ int[4] a; popBack(a); })));
@@ -2194,8 +2407,8 @@ version (unittest)
}
/// ditto
-void popBack(T)(ref T[] a) @safe pure
-if (isNarrowString!(T[]))
+void popBack(T)(scope ref inout(T)[] a) @safe pure
+if (isAutodecodableString!(T[]))
{
import std.utf : strideBack;
assert(a.length, "Attempting to popBack() past the front of an array of " ~ T.stringof);
@@ -2206,8 +2419,8 @@ if (isNarrowString!(T[]))
{
import std.meta : AliasSeq;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
S s = "hello\xE2\x89\xA0";
s.popBack();
assert(s == "hello");
@@ -2227,19 +2440,34 @@ if (isNarrowString!(T[]))
static assert(!is(typeof({ immutable S a; popBack(a); })));
static assert(!is(typeof({ typeof(S.init[0])[4] a; popBack(a); })));
- }
+ }}
}
/**
-Implements the range interface primitive $(D front) for built-in
+EXPERIMENTAL: to try out removing autodecoding, set the version
+`NoAutodecodeStrings`. Most things are expected to fail with this version
+currently.
+*/
+version (NoAutodecodeStrings)
+{
+ enum autodecodeStrings = false;
+}
+else
+{
+ ///
+ enum autodecodeStrings = true;
+}
+
+/**
+Implements the range interface primitive `front` for built-in
arrays. Due to the fact that nonmember functions can be called with
-the first argument using the dot notation, $(D array.front) is
-equivalent to $(D front(array)). For $(GLOSSARY narrow strings), $(D
+the first argument using the dot notation, `array.front` is
+equivalent to `front(array)`. For $(GLOSSARY narrow strings), $(D
front) automatically returns the first $(GLOSSARY code point) as _a $(D
dchar).
*/
-@property ref T front(T)(T[] a) @safe pure nothrow @nogc
-if (!isNarrowString!(T[]) && !is(T[] == void[]))
+@property ref inout(T) front(T)(return scope inout(T)[] a) @safe pure nothrow @nogc
+if (!isAutodecodableString!(T[]) && !is(T[] == void[]))
{
assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof);
return a[0];
@@ -2267,8 +2495,8 @@ if (!isNarrowString!(T[]) && !is(T[] == void[]))
}
/// ditto
-@property dchar front(T)(T[] a) @safe pure
-if (isNarrowString!(T[]))
+@property dchar front(T)(scope const(T)[] a) @safe pure
+if (isAutodecodableString!(T[]))
{
import std.utf : decode;
assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof);
@@ -2277,15 +2505,15 @@ if (isNarrowString!(T[]))
}
/**
-Implements the range interface primitive $(D back) for built-in
+Implements the range interface primitive `back` for built-in
arrays. Due to the fact that nonmember functions can be called with
-the first argument using the dot notation, $(D array.back) is
-equivalent to $(D back(array)). For $(GLOSSARY narrow strings), $(D
+the first argument using the dot notation, `array.back` is
+equivalent to `back(array)`. For $(GLOSSARY narrow strings), $(D
back) automatically returns the last $(GLOSSARY code point) as _a $(D
dchar).
*/
-@property ref T back(T)(T[] a) @safe pure nothrow @nogc
-if (!isNarrowString!(T[]) && !is(T[] == void[]))
+@property ref inout(T) back(T)(return scope inout(T)[] a) @safe pure nothrow @nogc
+if (!isAutodecodableString!(T[]) && !is(T[] == void[]))
{
assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof);
return a[$ - 1];
@@ -2311,11 +2539,26 @@ if (!isNarrowString!(T[]) && !is(T[] == void[]))
/// ditto
// Specialization for strings
-@property dchar back(T)(T[] a) @safe pure
-if (isNarrowString!(T[]))
+@property dchar back(T)(scope const(T)[] a) @safe pure
+if (isAutodecodableString!(T[]))
{
import std.utf : decode, strideBack;
assert(a.length, "Attempting to fetch the back of an empty array of " ~ T.stringof);
size_t i = a.length - strideBack(a, a.length);
return decode(a, i);
}
+
+/*
+Implements `length` for a range by forwarding it to `member`.
+*/
+package(std) mixin template ImplementLength(alias member)
+{
+ static if (hasLength!(typeof(member)))
+ {
+ @property auto length()
+ {
+ return member.length;
+ }
+ alias opDollar = length;
+ }
+}
diff --git a/libphobos/src/std/regex/internal/backtracking.d b/libphobos/src/std/regex/internal/backtracking.d
index ffc9779923a..21766fc2d0c 100644
--- a/libphobos/src/std/regex/internal/backtracking.d
+++ b/libphobos/src/std/regex/internal/backtracking.d
@@ -9,760 +9,783 @@ package(std.regex):
import core.stdc.stdlib, std.range.primitives, std.traits, std.typecons;
import std.regex.internal.ir;
+import core.memory : pureMalloc, pureFree;
+
/+
BacktrackingMatcher implements backtracking scheme of matching
regular expressions.
+/
-template BacktrackingMatcher(bool CTregex)
+@trusted class BacktrackingMatcher(Char, Stream = Input!Char) : Matcher!Char
+if (is(Char : dchar))
{
- @trusted struct BacktrackingMatcher(Char, Stream = Input!Char)
- if (is(Char : dchar))
- {
- alias DataIndex = Stream.DataIndex;
- struct State
- {//top bit in pc is set if saved along with matches
- DataIndex index;
- uint pc, counter, infiniteNesting;
- }
- static assert(State.sizeof % size_t.sizeof == 0);
- enum stateSize = State.sizeof / size_t.sizeof;
- enum initialStack = 1 << 11; // items in a block of segmented stack
- alias String = const(Char)[];
- alias RegEx = Regex!Char;
- alias MatchFn = bool function (ref BacktrackingMatcher!(Char, Stream));
- RegEx re; //regex program
- static if (CTregex)
- MatchFn nativeFn; //native code for that program
- //Stream state
- Stream s;
+ alias DataIndex = Stream.DataIndex;
+ struct State
+ {//top bit in pc is set if saved along with matches
DataIndex index;
- dchar front;
- bool exhausted;
- //backtracking machine state
- uint pc, counter;
- DataIndex lastState = 0; //top of state stack
- static if (!CTregex)
- uint infiniteNesting;
- size_t[] memory;
- Trace[] merge;
- static struct Trace
- {
- ulong mask;
- size_t offset;
+ uint pc, counter, infiniteNesting;
+ }
+ static assert(State.sizeof % size_t.sizeof == 0);
+ enum stateSize = State.sizeof / size_t.sizeof;
+ enum initialStack = 1 << 11; // items in a block of segmented stack
+ alias String = const(Char)[];
+ alias RegEx = Regex!Char;
+ alias MatchFn = bool function(BacktrackingMatcher) pure;
+ const RegEx re; // regex program
+ MatchFn nativeFn; // native code for that program
+ // Stream state
+ Stream s;
+ DataIndex index;
+ dchar front;
+ bool exhausted;
+ // Backtracking machine state
+ uint pc, counter;
+ DataIndex lastState = 0; // Top of state stack
+ uint infiniteNesting;
+ size_t[] memory;
+ Trace[] merge;
+ static struct Trace
+ {
+ ulong mask;
+ size_t offset;
- bool mark(size_t idx)
+ bool mark(size_t idx)
+ {
+ immutable d = idx - offset;
+ if (d < 64) // including overflow
{
- immutable d = idx - offset;
- if (d < 64) // including overflow
- {
- immutable p = mask & (1UL << d);
- mask |= 1UL << d;
- return p != 0;
- }
- else
- {
- offset = idx;
- mask = 1;
- return false;
- }
+ immutable p = mask & (1UL << d);
+ mask |= 1UL << d;
+ return p != 0;
+ }
+ else
+ {
+ offset = idx;
+ mask = 1;
+ return false;
}
}
- //local slice of matches, global for backref
- Group!DataIndex[] matches, backrefed;
+ }
+ //local slice of matches, global for backref
+ Group!DataIndex[] matches, backrefed;
+ size_t _refCount;
+final:
- static if (__traits(hasMember,Stream, "search"))
- {
- enum kicked = true;
- }
- else
- enum kicked = false;
+ override @property ref size_t refCount() { return _refCount; }
+ override @property ref const(RegEx) pattern(){ return re; }
- static size_t initialMemory(const ref RegEx re)
- {
- return stackSize(re)*size_t.sizeof + re.hotspotTableSize*Trace.sizeof;
- }
+ static if (__traits(hasMember,Stream, "search"))
+ {
+ enum kicked = true;
+ }
+ else
+ enum kicked = false;
- static size_t stackSize(const ref RegEx re)
- {
- size_t itemSize = stateSize
- + re.ngroup * (Group!DataIndex).sizeof / size_t.sizeof;
- return initialStack * itemSize + 2;
- }
+ static size_t initialMemory(const ref RegEx re)
+ {
+ return stackSize(re)*size_t.sizeof + re.hotspotTableSize*Trace.sizeof;
+ }
+
+ static size_t stackSize(const ref RegEx re)
+ {
+ size_t itemSize = stateSize
+ + re.ngroup * (Group!DataIndex).sizeof / size_t.sizeof;
+ return initialStack * itemSize + 2;
+ }
- @property bool atStart(){ return index == 0; }
+ @property bool atStart(){ return index == 0; }
- @property bool atEnd(){ return index == s.lastIndex && s.atEnd; }
+ @property bool atEnd(){ return index == s.lastIndex && s.atEnd; }
- void next()
- {
- if (!s.nextChar(front, index))
- index = s.lastIndex;
- }
+ void next()
+ {
+ if (!s.nextChar(front, index))
+ index = s.lastIndex;
+ }
- void search()
+ void search()
+ {
+ static if (kicked)
{
- static if (kicked)
+ if (!s.search(re.kickstart, front, index))
{
- if (!s.search(re.kickstart, front, index))
- {
- index = s.lastIndex;
- }
+ index = s.lastIndex;
}
- else
- next();
}
+ else
+ next();
+ }
- //
- void newStack()
- {
- auto chunk = mallocArray!(size_t)(stackSize(re));
- chunk[0] = cast(size_t)(memory.ptr);
- chunk[1] = lastState;
- memory = chunk[2..$];
- lastState = 0;
- }
+ //
+ void newStack()
+ {
+ auto chunk = mallocArray!(size_t)(stackSize(re));
+ chunk[0] = cast(size_t)(memory.ptr);
+ chunk[1] = lastState;
+ memory = chunk[2..$];
+ lastState = 0;
+ }
- bool prevStack()
+ bool prevStack()
+ {
+ // pointer to previous block
+ size_t* prev = cast(size_t*) memory.ptr[-2];
+ if (!prev)
{
- // pointer to previous block
- size_t* prev = cast(size_t*) memory.ptr[-2];
- if (!prev)
- {
- // The last segment is freed in RegexMatch
- return false;
- }
- else
- {
- import core.stdc.stdlib : free;
- // memory used in previous block
- size_t size = memory.ptr[-1];
- free(memory.ptr-2);
- memory = prev[0 .. size];
- lastState = size;
- return true;
- }
+ // The last segment is freed in RegexMatch
+ return false;
}
-
- void initExternalMemory(void[] memBlock)
+ else
{
- merge = arrayInChunk!(Trace)(re.hotspotTableSize, memBlock);
- merge[] = Trace.init;
- memory = cast(size_t[]) memBlock;
- memory[0] = 0; // hidden pointer
- memory[1] = 0; // used size
- memory = memory[2..$];
+ import core.memory : pureFree;
+ // memory used in previous block
+ size_t size = memory.ptr[-1];
+ pureFree(memory.ptr-2);
+ memory = prev[0 .. size];
+ lastState = size;
+ return true;
}
+ }
- void initialize(ref RegEx program, Stream stream, void[] memBlock)
- {
- re = program;
- s = stream;
- exhausted = false;
- initExternalMemory(memBlock);
- backrefed = null;
- }
+ void initExternalMemory(void[] memBlock)
+ {
+ merge = arrayInChunk!(Trace)(re.hotspotTableSize, memBlock);
+ merge[] = Trace.init;
+ memory = cast(size_t[]) memBlock;
+ memory[0] = 0; // hidden pointer
+ memory[1] = 0; // used size
+ memory = memory[2..$];
+ }
- auto dupTo(void[] memory)
- {
- typeof(this) tmp = this;
- tmp.initExternalMemory(memory);
- return tmp;
- }
+ void initialize(ref const RegEx program, Stream stream, void[] memBlock)
+ {
+ s = stream;
+ exhausted = false;
+ initExternalMemory(memBlock);
+ backrefed = null;
+ }
- this(ref RegEx program, Stream stream, void[] memBlock, dchar ch, DataIndex idx)
- {
- initialize(program, stream, memBlock);
- front = ch;
- index = idx;
- }
+ override void dupTo(Matcher!Char m, void[] memBlock)
+ {
+ auto backtracking = cast(BacktrackingMatcher) m;
+ backtracking.s = s;
+ backtracking.front = front;
+ backtracking.index = index;
+ backtracking.exhausted = exhausted;
+ backtracking.initExternalMemory(memBlock);
+ }
- this(ref RegEx program, Stream stream, void[] memBlock)
- {
- initialize(program, stream, memBlock);
- next();
- }
+ override Matcher!Char rearm(in Char[] data)
+ {
+ merge[] = Trace.init;
+ exhausted = false;
+ s = Stream(data);
+ next();
+ return this;
+ }
- auto fwdMatcher(ref BacktrackingMatcher matcher, void[] memBlock)
- {
- alias BackMatcherTempl = .BacktrackingMatcher!(CTregex);
- alias BackMatcher = BackMatcherTempl!(Char, Stream);
- auto fwdMatcher = BackMatcher(matcher.re, s, memBlock, front, index);
- return fwdMatcher;
+ this(ref const RegEx program, Stream stream, void[] memBlock, dchar ch, DataIndex idx)
+ {
+ _refCount = 1;
+ re = program;
+ nativeFn = null;
+ initialize(program, stream, memBlock);
+ front = ch;
+ index = idx;
+ }
+
+ this(ref const RegEx program, MatchFn func, Stream stream, void[] memBlock)
+ {
+ _refCount = 1;
+ re = program;
+ initialize(program, stream, memBlock);
+ nativeFn = func;
+ next();
+ }
+
+ this(ref const RegEx program, Stream stream, void[] memBlock)
+ {
+ _refCount = 1;
+ re = program;
+ nativeFn = null;
+ initialize(program, stream, memBlock);
+ next();
+ }
+
+ auto fwdMatcher(ref const RegEx re, void[] memBlock)
+ {
+ alias BackMatcher = BacktrackingMatcher!(Char, Stream);
+ auto fwdMatcher = new BackMatcher(re, s, memBlock, front, index);
+ return fwdMatcher;
+ }
+
+ auto bwdMatcher(ref const RegEx re, void[] memBlock)
+ {
+ alias BackMatcher = BacktrackingMatcher!(Char, typeof(s.loopBack(index)));
+ auto fwdMatcher =
+ new BackMatcher(re, s.loopBack(index), memBlock);
+ return fwdMatcher;
+ }
+
+ //
+ int matchFinalize()
+ {
+ immutable start = index;
+ immutable val = matchImpl();
+ if (val)
+ {//stream is updated here
+ matches[0].begin = start;
+ matches[0].end = index;
+ if (!(re.flags & RegexOption.global) || atEnd)
+ exhausted = true;
+ if (start == index)//empty match advances input
+ next();
+ return val;
}
+ else
+ return 0;
+ }
- auto bwdMatcher(ref BacktrackingMatcher matcher, void[] memBlock)
+ //lookup next match, fill matches with indices into input
+ override int match(Group!DataIndex[] matches)
+ {
+ debug(std_regex_matcher)
{
- alias BackMatcherTempl = .BacktrackingMatcher!(CTregex);
- alias BackMatcher = BackMatcherTempl!(Char, typeof(s.loopBack(index)));
- auto fwdMatcher =
- BackMatcher(matcher.re, s.loopBack(index), memBlock);
- return fwdMatcher;
+ writeln("------------------------------------------");
}
-
- //
- int matchFinalize()
+ if (exhausted) //all matches collected
+ return false;
+ this.matches = matches;
+ if (re.flags & RegexInfo.oneShot)
{
- immutable start = index;
- immutable val = matchImpl();
- if (val)
- {//stream is updated here
+ exhausted = true;
+ const DataIndex start = index;
+ immutable m = matchImpl();
+ if (m)
+ {
matches[0].begin = start;
matches[0].end = index;
- if (!(re.flags & RegexOption.global) || atEnd)
- exhausted = true;
- if (start == index)//empty match advances input
- next();
- return val;
}
- else
- return 0;
+ return m;
}
-
- //lookup next match, fill matches with indices into input
- int match(Group!DataIndex[] matches)
+ static if (kicked)
{
- debug(std_regex_matcher)
- {
- writeln("------------------------------------------");
- }
- if (exhausted) //all matches collected
- return false;
- this.matches = matches;
- if (re.flags & RegexInfo.oneShot)
- {
- exhausted = true;
- const DataIndex start = index;
- immutable m = matchImpl();
- if (m)
- {
- matches[0].begin = start;
- matches[0].end = index;
- }
- return m;
- }
- static if (kicked)
+ if (!re.kickstart.empty)
{
- if (!re.kickstart.empty)
+ for (;;)
{
- for (;;)
+ immutable val = matchFinalize();
+ if (val)
+ return val;
+ else
{
- immutable val = matchFinalize();
- if (val)
- return val;
- else
+ if (atEnd)
+ break;
+ search();
+ if (atEnd)
{
- if (atEnd)
- break;
- search();
- if (atEnd)
- {
- exhausted = true;
- return matchFinalize();
- }
+ exhausted = true;
+ return matchFinalize();
}
}
- exhausted = true;
- return 0; //early return
}
+ exhausted = true;
+ return 0; //early return
}
- //no search available - skip a char at a time
- for (;;)
+ }
+ //no search available - skip a char at a time
+ for (;;)
+ {
+ immutable val = matchFinalize();
+ if (val)
+ return val;
+ else
{
- immutable val = matchFinalize();
- if (val)
- return val;
- else
+ if (atEnd)
+ break;
+ next();
+ if (atEnd)
{
- if (atEnd)
- break;
- next();
- if (atEnd)
- {
- exhausted = true;
- return matchFinalize();
- }
+ exhausted = true;
+ return matchFinalize();
}
}
- exhausted = true;
- return 0;
}
+ exhausted = true;
+ return 0;
+ }
- /+
- match subexpression against input,
- results are stored in matches
- +/
- int matchImpl()
+ /+
+ match subexpression against input,
+ results are stored in matches
+ +/
+ int matchImpl() pure
+ {
+ if (nativeFn)
{
- static if (CTregex && is(typeof(nativeFn(this))))
- {
- debug(std_regex_ctr) writeln("using C-T matcher");
- return nativeFn(this);
- }
- else
+ debug(std_regex_ctr) writeln("using C-T matcher");
+ return nativeFn(this);
+ }
+ else
+ {
+ pc = 0;
+ counter = 0;
+ lastState = 0;
+ infiniteNesting = 0;
+ matches[] = Group!DataIndex.init;
+ auto start = s._index;
+ debug(std_regex_matcher)
+ writeln("Try match starting at ", s[index .. s.lastIndex]);
+ for (;;)
{
- pc = 0;
- counter = 0;
- lastState = 0;
- matches[] = Group!DataIndex.init;
- auto start = s._index;
debug(std_regex_matcher)
- writeln("Try match starting at ", s[index .. s.lastIndex]);
- for (;;)
+ writefln("PC: %s\tCNT: %s\t%s \tfront: %s src: %s",
+ pc, counter, disassemble(re.ir, pc, re.dict),
+ front, s._index);
+ switch (re.ir[pc].code)
{
- debug(std_regex_matcher)
- writefln("PC: %s\tCNT: %s\t%s \tfront: %s src: %s",
- pc, counter, disassemble(re.ir, pc, re.dict),
- front, s._index);
- switch (re.ir[pc].code)
+ case IR.OrChar://assumes IRL!(OrChar) == 1
+ if (atEnd)
+ goto L_backtrack;
+ uint len = re.ir[pc].sequence;
+ uint end = pc + len;
+ if (re.ir[pc].data != front && re.ir[pc+1].data != front)
{
- case IR.OrChar://assumes IRL!(OrChar) == 1
- if (atEnd)
- goto L_backtrack;
- uint len = re.ir[pc].sequence;
- uint end = pc + len;
- if (re.ir[pc].data != front && re.ir[pc+1].data != front)
- {
- for (pc = pc+2; pc < end; pc++)
- if (re.ir[pc].data == front)
- break;
- if (pc == end)
- goto L_backtrack;
- }
- pc = end;
- next();
- break;
- case IR.Char:
- if (atEnd || front != re.ir[pc].data)
+ for (pc = pc+2; pc < end; pc++)
+ if (re.ir[pc].data == front)
+ break;
+ if (pc == end)
goto L_backtrack;
- pc += IRL!(IR.Char);
- next();
+ }
+ pc = end;
+ next();
break;
- case IR.Any:
- if (atEnd)
- goto L_backtrack;
- pc += IRL!(IR.Any);
- next();
- break;
- case IR.CodepointSet:
- if (atEnd || !re.charsets[re.ir[pc].data].scanFor(front))
- goto L_backtrack;
- next();
- pc += IRL!(IR.CodepointSet);
+ case IR.Char:
+ if (atEnd || front != re.ir[pc].data)
+ goto L_backtrack;
+ pc += IRL!(IR.Char);
+ next();
+ break;
+ case IR.Any:
+ if (atEnd)
+ goto L_backtrack;
+ pc += IRL!(IR.Any);
+ next();
+ break;
+ case IR.CodepointSet:
+ if (atEnd || !re.charsets[re.ir[pc].data].scanFor(front))
+ goto L_backtrack;
+ next();
+ pc += IRL!(IR.CodepointSet);
+ break;
+ case IR.Trie:
+ if (atEnd || !re.matchers[re.ir[pc].data][front])
+ goto L_backtrack;
+ next();
+ pc += IRL!(IR.Trie);
+ break;
+ case IR.Wordboundary:
+ dchar back;
+ DataIndex bi;
+ //at start & end of input
+ if (atStart && wordMatcher[front])
+ {
+ pc += IRL!(IR.Wordboundary);
break;
- case IR.Trie:
- if (atEnd || !re.matchers[re.ir[pc].data][front])
- goto L_backtrack;
- next();
- pc += IRL!(IR.Trie);
+ }
+ else if (atEnd && s.loopBack(index).nextChar(back, bi)
+ && wordMatcher[back])
+ {
+ pc += IRL!(IR.Wordboundary);
break;
- case IR.Wordboundary:
- dchar back;
- DataIndex bi;
- //at start & end of input
- if (atStart && wordMatcher[front])
- {
- pc += IRL!(IR.Wordboundary);
- break;
- }
- else if (atEnd && s.loopBack(index).nextChar(back, bi)
- && wordMatcher[back])
+ }
+ else if (s.loopBack(index).nextChar(back, bi))
+ {
+ immutable af = wordMatcher[front];
+ immutable ab = wordMatcher[back];
+ if (af ^ ab)
{
pc += IRL!(IR.Wordboundary);
break;
}
- else if (s.loopBack(index).nextChar(back, bi))
- {
- immutable af = wordMatcher[front];
- immutable ab = wordMatcher[back];
- if (af ^ ab)
- {
- pc += IRL!(IR.Wordboundary);
- break;
- }
- }
+ }
+ goto L_backtrack;
+ case IR.Notwordboundary:
+ dchar back;
+ DataIndex bi;
+ //at start & end of input
+ if (atStart && wordMatcher[front])
goto L_backtrack;
- case IR.Notwordboundary:
- dchar back;
- DataIndex bi;
- //at start & end of input
- if (atStart && wordMatcher[front])
- goto L_backtrack;
- else if (atEnd && s.loopBack(index).nextChar(back, bi)
- && wordMatcher[back])
- goto L_backtrack;
- else if (s.loopBack(index).nextChar(back, bi))
- {
- immutable af = wordMatcher[front];
- immutable ab = wordMatcher[back];
- if (af ^ ab)
- goto L_backtrack;
- }
- pc += IRL!(IR.Wordboundary);
- break;
- case IR.Bof:
- if (atStart)
- pc += IRL!(IR.Bol);
- else
- goto L_backtrack;
- break;
- case IR.Bol:
- dchar back;
- DataIndex bi;
- if (atStart)
- pc += IRL!(IR.Bol);
- else if (s.loopBack(index).nextChar(back,bi)
- && endOfLine(back, front == '\n'))
- {
- pc += IRL!(IR.Bol);
- }
- else
- goto L_backtrack;
- break;
- case IR.Eof:
- if (atEnd)
- pc += IRL!(IR.Eol);
- else
- goto L_backtrack;
- break;
- case IR.Eol:
- dchar back;
- DataIndex bi;
- debug(std_regex_matcher) writefln("EOL (front 0x%x) %s", front, s[index .. s.lastIndex]);
- //no matching inside \r\n
- if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back,bi)
- && back == '\r')))
- {
- pc += IRL!(IR.Eol);
- }
- else
+ else if (atEnd && s.loopBack(index).nextChar(back, bi)
+ && wordMatcher[back])
+ goto L_backtrack;
+ else if (s.loopBack(index).nextChar(back, bi))
+ {
+ immutable af = wordMatcher[front];
+ immutable ab = wordMatcher[back];
+ if (af ^ ab)
goto L_backtrack;
- break;
- case IR.InfiniteStart, IR.InfiniteQStart:
- pc += re.ir[pc].data + IRL!(IR.InfiniteStart);
- //now pc is at end IR.Infinite(Q)End
- uint len = re.ir[pc].data;
- if (re.ir[pc].code == IR.InfiniteEnd)
- {
- pushState(pc+IRL!(IR.InfiniteEnd), counter);
- pc -= len;
- }
- else
- {
- pushState(pc - len, counter);
- pc += IRL!(IR.InfiniteEnd);
- }
- break;
- case IR.InfiniteBloomStart:
- pc += re.ir[pc].data + IRL!(IR.InfiniteBloomStart);
- //now pc is at end IR.InfiniteBloomEnd
- immutable len = re.ir[pc].data;
- immutable filterIdx = re.ir[pc+2].raw;
- if (re.filters[filterIdx][front])
- pushState(pc+IRL!(IR.InfiniteBloomEnd), counter);
+ }
+ pc += IRL!(IR.Wordboundary);
+ break;
+ case IR.Bof:
+ if (atStart)
+ pc += IRL!(IR.Bol);
+ else
+ goto L_backtrack;
+ break;
+ case IR.Bol:
+ dchar back;
+ DataIndex bi;
+ if (atStart)
+ pc += IRL!(IR.Bol);
+ else if (s.loopBack(index).nextChar(back,bi)
+ && endOfLine(back, front == '\n'))
+ {
+ pc += IRL!(IR.Bol);
+ }
+ else
+ goto L_backtrack;
+ break;
+ case IR.Eof:
+ if (atEnd)
+ pc += IRL!(IR.Eol);
+ else
+ goto L_backtrack;
+ break;
+ case IR.Eol:
+ dchar back;
+ DataIndex bi;
+ debug(std_regex_matcher) writefln("EOL (front 0x%x) %s", front, s[index .. s.lastIndex]);
+ //no matching inside \r\n
+ if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back,bi)
+ && back == '\r')))
+ {
+ pc += IRL!(IR.Eol);
+ }
+ else
+ goto L_backtrack;
+ break;
+ case IR.InfiniteStart, IR.InfiniteQStart:
+ pc += re.ir[pc].data + IRL!(IR.InfiniteStart);
+ //now pc is at end IR.Infinite(Q)End
+ uint len = re.ir[pc].data;
+ if (re.ir[pc].code == IR.InfiniteEnd)
+ {
+ pushState(pc+IRL!(IR.InfiniteEnd), counter);
pc -= len;
- break;
- case IR.RepeatStart, IR.RepeatQStart:
- pc += re.ir[pc].data + IRL!(IR.RepeatStart);
- break;
- case IR.RepeatEnd:
- case IR.RepeatQEnd:
- if (merge[re.ir[pc + 1].raw+counter].mark(index))
- {
- // merged!
- goto L_backtrack;
- }
- //len, step, min, max
- immutable len = re.ir[pc].data;
- immutable step = re.ir[pc+2].raw;
- immutable min = re.ir[pc+3].raw;
- immutable max = re.ir[pc+4].raw;
- if (counter < min)
+ }
+ else
+ {
+ pushState(pc - len, counter);
+ pc += IRL!(IR.InfiniteEnd);
+ }
+ break;
+ case IR.InfiniteBloomStart:
+ pc += re.ir[pc].data + IRL!(IR.InfiniteBloomStart);
+ //now pc is at end IR.InfiniteBloomEnd
+ immutable len = re.ir[pc].data;
+ immutable filterIdx = re.ir[pc+2].raw;
+ if (re.filters[filterIdx][front])
+ pushState(pc+IRL!(IR.InfiniteBloomEnd), counter);
+ pc -= len;
+ break;
+ case IR.RepeatStart, IR.RepeatQStart:
+ pc += re.ir[pc].data + IRL!(IR.RepeatStart);
+ break;
+ case IR.RepeatEnd:
+ case IR.RepeatQEnd:
+ if (merge[re.ir[pc + 1].raw+counter].mark(index))
+ {
+ // merged!
+ goto L_backtrack;
+ }
+ //len, step, min, max
+ immutable len = re.ir[pc].data;
+ immutable step = re.ir[pc+2].raw;
+ immutable min = re.ir[pc+3].raw;
+ immutable max = re.ir[pc+4].raw;
+ if (counter < min)
+ {
+ counter += step;
+ pc -= len;
+ }
+ else if (counter < max)
+ {
+ if (re.ir[pc].code == IR.RepeatEnd)
{
+ pushState(pc + IRL!(IR.RepeatEnd), counter%step);
counter += step;
pc -= len;
}
- else if (counter < max)
- {
- if (re.ir[pc].code == IR.RepeatEnd)
- {
- pushState(pc + IRL!(IR.RepeatEnd), counter%step);
- counter += step;
- pc -= len;
- }
- else
- {
- pushState(pc - len, counter + step);
- counter = counter%step;
- pc += IRL!(IR.RepeatEnd);
- }
- }
else
{
+ pushState(pc - len, counter + step);
counter = counter%step;
pc += IRL!(IR.RepeatEnd);
}
- break;
- case IR.InfiniteEnd:
- case IR.InfiniteQEnd:
- debug(std_regex_matcher) writeln("Infinited nesting:", infiniteNesting);
- if (merge[re.ir[pc + 1].raw+counter].mark(index))
- {
- // merged!
- goto L_backtrack;
- }
- immutable len = re.ir[pc].data;
- if (re.ir[pc].code == IR.InfiniteEnd)
- {
- pushState(pc + IRL!(IR.InfiniteEnd), counter);
- pc -= len;
- }
- else
- {
- pushState(pc-len, counter);
- pc += IRL!(IR.InfiniteEnd);
- }
- break;
- case IR.InfiniteBloomEnd:
- debug(std_regex_matcher) writeln("Infinited nesting:", infiniteNesting);
- if (merge[re.ir[pc + 1].raw+counter].mark(index))
- {
- // merged!
- goto L_backtrack;
- }
- immutable len = re.ir[pc].data;
- immutable filterIdx = re.ir[pc+2].raw;
- if (re.filters[filterIdx][front])
- {
- infiniteNesting--;
- pushState(pc + IRL!(IR.InfiniteBloomEnd), counter);
- infiniteNesting++;
- }
+ }
+ else
+ {
+ counter = counter%step;
+ pc += IRL!(IR.RepeatEnd);
+ }
+ break;
+ case IR.InfiniteEnd:
+ case IR.InfiniteQEnd:
+ debug(std_regex_matcher) writeln("Infinited nesting:", infiniteNesting);
+ if (merge[re.ir[pc + 1].raw+counter].mark(index))
+ {
+ // merged!
+ goto L_backtrack;
+ }
+ immutable len = re.ir[pc].data;
+ if (re.ir[pc].code == IR.InfiniteEnd)
+ {
+ pushState(pc + IRL!(IR.InfiniteEnd), counter);
pc -= len;
- break;
- case IR.OrEnd:
- if (merge[re.ir[pc + 1].raw+counter].mark(index))
- {
- // merged!
- goto L_backtrack;
- }
- pc += IRL!(IR.OrEnd);
- break;
- case IR.OrStart:
- pc += IRL!(IR.OrStart);
- goto case;
- case IR.Option:
- immutable len = re.ir[pc].data;
- if (re.ir[pc+len].code == IR.GotoEndOr)//not a last one
- {
- pushState(pc + len + IRL!(IR.Option), counter); //remember 2nd branch
- }
- pc += IRL!(IR.Option);
- break;
- case IR.GotoEndOr:
- pc = pc + re.ir[pc].data + IRL!(IR.GotoEndOr);
- break;
- case IR.GroupStart:
- immutable n = re.ir[pc].data;
- matches[n].begin = index;
- debug(std_regex_matcher) writefln("IR group #%u starts at %u", n, index);
- pc += IRL!(IR.GroupStart);
- break;
- case IR.GroupEnd:
- immutable n = re.ir[pc].data;
- matches[n].end = index;
- debug(std_regex_matcher) writefln("IR group #%u ends at %u", n, index);
- pc += IRL!(IR.GroupEnd);
- break;
- case IR.LookaheadStart:
- case IR.NeglookaheadStart:
- immutable len = re.ir[pc].data;
- auto save = index;
- immutable ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw;
- auto mem = malloc(initialMemory(re))[0 .. initialMemory(re)];
- scope(exit) free(mem.ptr);
- static if (Stream.isLoopback)
- {
- auto matcher = bwdMatcher(this, mem);
- }
- else
- {
- auto matcher = fwdMatcher(this, mem);
- }
- matcher.matches = matches[ms .. me];
- matcher.backrefed = backrefed.empty ? matches : backrefed;
- matcher.re.ir = re.ir[
- pc+IRL!(IR.LookaheadStart) .. pc+IRL!(IR.LookaheadStart)+len+IRL!(IR.LookaheadEnd)
- ];
- immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookaheadStart);
- s.reset(save);
+ }
+ else
+ {
+ pushState(pc-len, counter);
+ pc += IRL!(IR.InfiniteEnd);
+ }
+ break;
+ case IR.InfiniteBloomEnd:
+ debug(std_regex_matcher) writeln("Infinited nesting:", infiniteNesting);
+ if (merge[re.ir[pc + 1].raw+counter].mark(index))
+ {
+ // merged!
+ goto L_backtrack;
+ }
+ immutable len = re.ir[pc].data;
+ immutable filterIdx = re.ir[pc+2].raw;
+ if (re.filters[filterIdx][front])
+ {
+ infiniteNesting--;
+ pushState(pc + IRL!(IR.InfiniteBloomEnd), counter);
+ infiniteNesting++;
+ }
+ pc -= len;
+ break;
+ case IR.OrEnd:
+ if (merge[re.ir[pc + 1].raw+counter].mark(index))
+ {
+ // merged!
+ goto L_backtrack;
+ }
+ pc += IRL!(IR.OrEnd);
+ break;
+ case IR.OrStart:
+ pc += IRL!(IR.OrStart);
+ goto case;
+ case IR.Option:
+ immutable len = re.ir[pc].data;
+ if (re.ir[pc+len].code == IR.GotoEndOr)//not a last one
+ {
+ pushState(pc + len + IRL!(IR.Option), counter); //remember 2nd branch
+ }
+ pc += IRL!(IR.Option);
+ break;
+ case IR.GotoEndOr:
+ pc = pc + re.ir[pc].data + IRL!(IR.GotoEndOr);
+ break;
+ case IR.GroupStart:
+ immutable n = re.ir[pc].data;
+ matches[n].begin = index;
+ debug(std_regex_matcher) writefln("IR group #%u starts at %u", n, index);
+ pc += IRL!(IR.GroupStart);
+ break;
+ case IR.GroupEnd:
+ immutable n = re.ir[pc].data;
+ matches[n].end = index;
+ debug(std_regex_matcher) writefln("IR group #%u ends at %u", n, index);
+ pc += IRL!(IR.GroupEnd);
+ break;
+ case IR.LookaheadStart:
+ case IR.NeglookaheadStart:
+ immutable len = re.ir[pc].data;
+ auto save = index;
+ immutable ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw;
+ auto mem = pureMalloc(initialMemory(re))[0 .. initialMemory(re)];
+ scope(exit) pureFree(mem.ptr);
+ auto slicedRe = re.withCode(re.ir[
+ pc+IRL!(IR.LookaheadStart) .. pc+IRL!(IR.LookaheadStart)+len+IRL!(IR.LookaheadEnd)
+ ]);
+ static if (Stream.isLoopback)
+ {
+ auto matcher = bwdMatcher(slicedRe, mem);
+ }
+ else
+ {
+ auto matcher = fwdMatcher(slicedRe, mem);
+ }
+ matcher.matches = matches[ms .. me];
+ matcher.backrefed = backrefed.empty ? matches : backrefed;
+ immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookaheadStart);
+ s.reset(save);
+ next();
+ if (!match)
+ goto L_backtrack;
+ else
+ {
+ pc += IRL!(IR.LookaheadStart)+len+IRL!(IR.LookaheadEnd);
+ }
+ break;
+ case IR.LookbehindStart:
+ case IR.NeglookbehindStart:
+ immutable len = re.ir[pc].data;
+ immutable ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw;
+ auto mem = pureMalloc(initialMemory(re))[0 .. initialMemory(re)];
+ scope(exit) pureFree(mem.ptr);
+ auto slicedRe = re.withCode(re.ir[
+ pc + IRL!(IR.LookbehindStart) .. pc + IRL!(IR.LookbehindStart) + len + IRL!(IR.LookbehindEnd)
+ ]);
+ static if (Stream.isLoopback)
+ {
+ alias Matcher = BacktrackingMatcher!(Char, Stream);
+ auto matcher = new Matcher(slicedRe, s, mem, front, index);
+ }
+ else
+ {
+ alias Matcher = BacktrackingMatcher!(Char, typeof(s.loopBack(index)));
+ auto matcher = new Matcher(slicedRe, s.loopBack(index), mem);
+ }
+ matcher.matches = matches[ms .. me];
+ matcher.backrefed = backrefed.empty ? matches : backrefed;
+ immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookbehindStart);
+ if (!match)
+ goto L_backtrack;
+ else
+ {
+ pc += IRL!(IR.LookbehindStart)+len+IRL!(IR.LookbehindEnd);
+ }
+ break;
+ case IR.Backref:
+ immutable n = re.ir[pc].data;
+ auto referenced = re.ir[pc].localRef
+ ? s[matches[n].begin .. matches[n].end]
+ : s[backrefed[n].begin .. backrefed[n].end];
+ while (!atEnd && !referenced.empty && front == referenced.front)
+ {
next();
- if (!match)
- goto L_backtrack;
- else
- {
- pc += IRL!(IR.LookaheadStart)+len+IRL!(IR.LookaheadEnd);
- }
- break;
- case IR.LookbehindStart:
- case IR.NeglookbehindStart:
- immutable len = re.ir[pc].data;
- immutable ms = re.ir[pc+1].raw, me = re.ir[pc+2].raw;
- auto mem = malloc(initialMemory(re))[0 .. initialMemory(re)];
- scope(exit) free(mem.ptr);
- static if (Stream.isLoopback)
- {
- alias Matcher = BacktrackingMatcher!(Char, Stream);
- auto matcher = Matcher(re, s, mem, front, index);
- }
- else
- {
- alias Matcher = BacktrackingMatcher!(Char, typeof(s.loopBack(index)));
- auto matcher = Matcher(re, s.loopBack(index), mem);
- }
- matcher.matches = matches[ms .. me];
- matcher.re.ir = re.ir[
- pc + IRL!(IR.LookbehindStart) .. pc + IRL!(IR.LookbehindStart) + len + IRL!(IR.LookbehindEnd)
- ];
- matcher.backrefed = backrefed.empty ? matches : backrefed;
- immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookbehindStart);
- if (!match)
- goto L_backtrack;
- else
- {
- pc += IRL!(IR.LookbehindStart)+len+IRL!(IR.LookbehindEnd);
- }
- break;
- case IR.Backref:
- immutable n = re.ir[pc].data;
- auto referenced = re.ir[pc].localRef
- ? s[matches[n].begin .. matches[n].end]
- : s[backrefed[n].begin .. backrefed[n].end];
- while (!atEnd && !referenced.empty && front == referenced.front)
- {
- next();
- referenced.popFront();
- }
- if (referenced.empty)
- pc++;
- else
- goto L_backtrack;
- break;
- case IR.Nop:
- pc += IRL!(IR.Nop);
- break;
- case IR.LookaheadEnd:
- case IR.NeglookaheadEnd:
- case IR.LookbehindEnd:
- case IR.NeglookbehindEnd:
- case IR.End:
- // cleanup stale stack blocks if any
- while (prevStack()) {}
- return re.ir[pc].data;
- default:
- debug printBytecode(re.ir[0..$]);
- assert(0);
- L_backtrack:
- if (!popState())
- {
- s.reset(start);
- return 0;
- }
+ referenced.popFront();
+ }
+ if (referenced.empty)
+ pc++;
+ else
+ goto L_backtrack;
+ break;
+ case IR.Nop:
+ pc += IRL!(IR.Nop);
+ break;
+ case IR.LookaheadEnd:
+ case IR.NeglookaheadEnd:
+ case IR.LookbehindEnd:
+ case IR.NeglookbehindEnd:
+ case IR.End:
+ // cleanup stale stack blocks if any
+ while (prevStack()) {}
+ return re.ir[pc].data;
+ default:
+ debug printBytecode(re.ir[0..$]);
+ assert(0);
+ L_backtrack:
+ if (!popState())
+ {
+ s.reset(start);
+ return 0;
}
}
}
- assert(0);
}
+ assert(0);
+ }
- @property size_t stackAvail()
- {
- return memory.length - lastState;
- }
+ @property size_t stackAvail()
+ {
+ return memory.length - lastState;
+ }
- void stackPush(T)(T val)
- if (!isDynamicArray!T)
- {
- *cast(T*)&memory[lastState] = val;
- enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof;
- lastState += delta;
- debug(std_regex_matcher) writeln("push element SP= ", lastState);
- }
+ void stackPush(T)(T val)
+ if (!isDynamicArray!T)
+ {
+ *cast(T*)&memory[lastState] = val;
+ enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof;
+ lastState += delta;
+ debug(std_regex_matcher) writeln("push element SP= ", lastState);
+ }
- void stackPush(T)(T[] val)
- {
- static assert(T.sizeof % size_t.sizeof == 0);
- (cast(T*)&memory[lastState])[0 .. val.length]
- = val[0..$];
- lastState += val.length*(T.sizeof/size_t.sizeof);
- debug(std_regex_matcher) writeln("push array SP= ", lastState);
- }
+ void stackPush(T)(T[] val)
+ {
+ static assert(T.sizeof % size_t.sizeof == 0);
+ (cast(T*)&memory[lastState])[0 .. val.length]
+ = val[0..$];
+ lastState += val.length*(T.sizeof/size_t.sizeof);
+ debug(std_regex_matcher) writeln("push array SP= ", lastState);
+ }
- void stackPop(T)(ref T val)
- if (!isDynamicArray!T)
- {
- enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof;
- lastState -= delta;
- val = *cast(T*)&memory[lastState];
- debug(std_regex_matcher) writeln("pop element SP= ", lastState);
- }
+ void stackPop(T)(ref T val)
+ if (!isDynamicArray!T)
+ {
+ enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof;
+ lastState -= delta;
+ val = *cast(T*)&memory[lastState];
+ debug(std_regex_matcher) writeln("pop element SP= ", lastState);
+ }
- void stackPop(T)(T[] val)
- {
- stackPop(val); // call ref version
- }
- void stackPop(T)(ref T[] val)
+ void stackPop(T)(T[] val)
+ {
+ stackPop(val); // call ref version
+ }
+ void stackPop(T)(ref T[] val)
+ {
+ lastState -= val.length*(T.sizeof/size_t.sizeof);
+ val[0..$] = (cast(T*)&memory[lastState])[0 .. val.length];
+ debug(std_regex_matcher) writeln("pop array SP= ", lastState);
+ }
+ //helper function, saves engine state
+ void pushState(uint pc, uint counter)
+ {
+ if (stateSize + 2 * matches.length > stackAvail)
{
- lastState -= val.length*(T.sizeof/size_t.sizeof);
- val[0..$] = (cast(T*)&memory[lastState])[0 .. val.length];
- debug(std_regex_matcher) writeln("pop array SP= ", lastState);
+ newStack();
}
+ *cast(State*)&memory[lastState] =
+ State(index, pc, counter, infiniteNesting);
+ lastState += stateSize;
+ memory[lastState .. lastState + 2 * matches.length] = (cast(size_t[]) matches)[];
+ lastState += 2*matches.length;
+ debug(std_regex_matcher)
+ writefln("Saved(pc=%s) front: %s src: %s",
+ pc, front, s[index .. s.lastIndex]);
+ }
- static if (!CTregex)
+ //helper function, restores engine state
+ bool popState()
+ {
+ if (!lastState && !prevStack())
+ return false;
+ lastState -= 2*matches.length;
+ auto pm = cast(size_t[]) matches;
+ pm[] = memory[lastState .. lastState + 2 * matches.length];
+ lastState -= stateSize;
+ State* state = cast(State*)&memory[lastState];
+ index = state.index;
+ pc = state.pc;
+ counter = state.counter;
+ infiniteNesting = state.infiniteNesting;
+ debug(std_regex_matcher)
{
- //helper function, saves engine state
- void pushState(uint pc, uint counter)
- {
- if (stateSize + 2 * matches.length > stackAvail)
- {
- newStack();
- }
- *cast(State*)&memory[lastState] =
- State(index, pc, counter, infiniteNesting);
- lastState += stateSize;
- memory[lastState .. lastState + 2 * matches.length] = (cast(size_t[]) matches)[];
- lastState += 2*matches.length;
- debug(std_regex_matcher)
- writefln("Saved(pc=%s) front: %s src: %s",
- pc, front, s[index .. s.lastIndex]);
- }
-
- //helper function, restores engine state
- bool popState()
- {
- if (!lastState && !prevStack())
- return false;
- lastState -= 2*matches.length;
- auto pm = cast(size_t[]) matches;
- pm[] = memory[lastState .. lastState + 2 * matches.length];
- lastState -= stateSize;
- State* state = cast(State*)&memory[lastState];
- index = state.index;
- pc = state.pc;
- counter = state.counter;
- infiniteNesting = state.infiniteNesting;
- debug(std_regex_matcher)
- {
- writefln("Restored matches", front, s[index .. s.lastIndex]);
- foreach (i, m; matches)
- writefln("Sub(%d) : %s..%s", i, m.begin, m.end);
- }
- s.reset(index);
- next();
- debug(std_regex_matcher)
- writefln("Backtracked (pc=%s) front: %s src: %s",
- pc, front, s[index .. s.lastIndex]);
- return true;
- }
+ writefln("Restored matches", front, s[index .. s.lastIndex]);
+ foreach (i, m; matches)
+ writefln("Sub(%d) : %s..%s", i, m.begin, m.end);
}
+ s.reset(index);
+ next();
+ debug(std_regex_matcher)
+ writefln("Backtracked (pc=%s) front: %s src: %s",
+ pc, front, s[index .. s.lastIndex]);
+ return true;
}
}
@@ -795,8 +818,6 @@ template BacktrackingMatcher(bool CTregex)
return format;
}
-alias Sequence(int B, int E) = staticIota!(B, E);
-
struct CtContext
{
import std.conv : to, text;
@@ -805,7 +826,7 @@ struct CtContext
//to mark the portion of matches to save
int match, total_matches;
int reserved;
- CodepointSet[] charsets;
+ const(CodepointInterval)[][] charsets;
//state of codegenerator
@@ -815,12 +836,15 @@ struct CtContext
int addr;
}
- this(Char)(Regex!Char re)
+ this(Char)(ref const Regex!Char re)
{
match = 1;
reserved = 1; //first match is skipped
total_matches = re.ngroup;
- charsets = re.charsets;
+ foreach (ref set; re.charsets)
+ {
+ charsets ~= set.intervals;
+ }
}
CtContext lookaround(uint s, uint e)
@@ -876,7 +900,7 @@ struct CtContext
}
//
- CtState ctGenBlock(Bytecode[] ir, int addr)
+ CtState ctGenBlock(const(Bytecode)[] ir, int addr)
{
CtState result;
result.addr = addr;
@@ -890,7 +914,7 @@ struct CtContext
}
//
- CtState ctGenGroup(ref Bytecode[] ir, int addr)
+ CtState ctGenGroup(ref const(Bytecode)[] ir, int addr)
{
import std.algorithm.comparison : max;
auto bailOut = "goto L_backtrack;";
@@ -932,10 +956,10 @@ struct CtContext
immutable len = ir[0].data;
immutable behind = ir[0].code == IR.LookbehindStart || ir[0].code == IR.NeglookbehindStart;
immutable negative = ir[0].code == IR.NeglookaheadStart || ir[0].code == IR.NeglookbehindStart;
- string fwdType = "typeof(fwdMatcher(matcher, []))";
- string bwdType = "typeof(bwdMatcher(matcher, []))";
- string fwdCreate = "fwdMatcher(matcher, mem)";
- string bwdCreate = "bwdMatcher(matcher, mem)";
+ string fwdType = "typeof(fwdMatcher(re, []))";
+ string bwdType = "typeof(bwdMatcher(re, []))";
+ string fwdCreate = "fwdMatcher(re, mem)";
+ string bwdCreate = "bwdMatcher(re, mem)";
immutable start = IRL!(IR.LookbehindStart);
immutable end = IRL!(IR.LookbehindStart)+len+IRL!(IR.LookaheadEnd);
CtContext context = lookaround(ir[1].raw, ir[2].raw); //split off new context
@@ -946,15 +970,15 @@ struct CtContext
alias Lookaround = $$;
else
alias Lookaround = $$;
- static bool matcher_$$(ref Lookaround matcher) @trusted
+ static bool matcher_$$(Lookaround matcher) @trusted
{
//(neg)lookaround piece start
$$
//(neg)lookaround piece ends
}
auto save = index;
- auto mem = malloc(initialMemory(re))[0 .. initialMemory(re)];
- scope(exit) free(mem.ptr);
+ auto mem = pureMalloc(initialMemory(re))[0 .. initialMemory(re)];
+ scope(exit) pureFree(mem.ptr);
static if (typeof(matcher.s).isLoopback)
auto lookaround = $$;
else
@@ -992,7 +1016,7 @@ struct CtContext
}
//generate source for bytecode contained in OrStart ... OrEnd
- CtState ctGenAlternation(Bytecode[] ir, int addr)
+ CtState ctGenAlternation(const(Bytecode)[] ir, int addr)
{
CtState[] pieces;
CtState r;
@@ -1032,11 +1056,11 @@ struct CtContext
// generate fixup code for instruction in ir,
// fixup means it has an alternative way for control flow
- string ctGenFixupCode(Bytecode[] ir, int addr, int fixup)
+ string ctGenFixupCode(const(Bytecode)[] ir, int addr, int fixup)
{
return ctGenFixupCode(ir, addr, fixup); // call ref Bytecode[] version
}
- string ctGenFixupCode(ref Bytecode[] ir, int addr, int fixup)
+ string ctGenFixupCode(ref const(Bytecode)[] ir, int addr, int fixup)
{
string r;
string testCode;
@@ -1190,7 +1214,7 @@ struct CtContext
}
- string ctQuickTest(Bytecode[] ir, int id)
+ string ctQuickTest(const(Bytecode)[] ir, int id)
{
uint pc = 0;
while (pc < ir.length && ir[pc].isAtom)
@@ -1217,7 +1241,7 @@ struct CtContext
}
//process & generate source for simple bytecodes at front of ir using address addr
- CtState ctGenAtom(ref Bytecode[] ir, int addr)
+ CtState ctGenAtom(ref const(Bytecode)[] ir, int addr)
{
CtState result;
result.code = ctAtomCode(ir, addr);
@@ -1227,7 +1251,7 @@ struct CtContext
}
//D code for atom at ir using address addr, addr < 0 means quickTest
- string ctAtomCode(Bytecode[] ir, int addr)
+ string ctAtomCode(const(Bytecode)[] ir, int addr)
{
string code;
string bailOut, nextInstr;
@@ -1282,7 +1306,7 @@ struct CtContext
if (charsets.length)
{
string name = `func_`~to!string(addr+1);
- string funcCode = charsets[ir[0].data].toSourceCode(name);
+ string funcCode = CodepointSet.toSourceCode(charsets[ir[0].data], name);
code ~= ctSub( `
static $$
if (atEnd || !$$(front))
@@ -1298,7 +1322,7 @@ struct CtContext
$$`, ir[0].data, bailOut, addr >= 0 ? "next();" :"", nextInstr);
break;
case IR.Trie:
- if (charsets.length && charsets[ir[0].data].byInterval.length <= 8)
+ if (charsets.length && charsets[ir[0].data].length <= 8)
goto case IR.CodepointSet;
code ~= ctSub( `
if (atEnd || !re.matchers[$$][front])
@@ -1439,11 +1463,11 @@ struct CtContext
}
//generate D code for the whole regex
- public string ctGenRegEx(Bytecode[] ir)
+ public string ctGenRegEx(const(Bytecode)[] ir)
{
auto bdy = ctGenBlock(ir, 0);
auto r = `
- import core.stdc.stdlib;
+ import core.memory : pureMalloc, pureFree;
with(matcher)
{
pc = 0;
@@ -1488,7 +1512,7 @@ struct CtContext
}
-string ctGenRegExCode(Char)(Regex!Char re)
+string ctGenRegExCode(Char)(const Regex!Char re)
{
auto context = CtContext(re);
return context.ctGenRegEx(re.ir);
diff --git a/libphobos/src/std/regex/internal/generator.d b/libphobos/src/std/regex/internal/generator.d
index 6831e59ed2e..c1fe4cde63d 100644
--- a/libphobos/src/std/regex/internal/generator.d
+++ b/libphobos/src/std/regex/internal/generator.d
@@ -13,7 +13,7 @@ module std.regex.internal.generator;
@trusted private struct SampleGenerator(Char)
{
import std.array : appender, Appender;
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
import std.random : Xorshift;
import std.regex.internal.ir : Regex, IR, IRL;
import std.utf : isValidDchar, byChar;
diff --git a/libphobos/src/std/regex/internal/ir.d b/libphobos/src/std/regex/internal/ir.d
index 28b199895d9..ec0cb66631e 100644
--- a/libphobos/src/std/regex/internal/ir.d
+++ b/libphobos/src/std/regex/internal/ir.d
@@ -30,7 +30,9 @@ CharMatcher[CodepointSet] matcherCache;
//accessor with caching
@trusted CharMatcher getMatcher(CodepointSet set)
-{// @@@BUG@@@ 6357 almost all properties of AA are not @safe
+{
+ // almost all properties of AA are not @safe
+ // https://issues.dlang.org/show_bug.cgi?id=6357
if (__ctfe || maxCachedMatchers == 0)
return CharMatcher(set);
else
@@ -47,40 +49,15 @@ CharMatcher[CodepointSet] matcherCache;
}
}
-@trusted auto memoizeExpr(string expr)()
-{
- if (__ctfe)
- return mixin(expr);
- alias T = typeof(mixin(expr));
- static T slot;
- static bool initialized;
- if (!initialized)
- {
- slot = mixin(expr);
- initialized = true;
- }
- return slot;
-}
-
-//property for \w character class
-@property CodepointSet wordCharacter()
+@property ref wordMatcher()()
{
- return memoizeExpr!("unicode.Alphabetic | unicode.Mn | unicode.Mc
- | unicode.Me | unicode.Nd | unicode.Pc")();
-}
-
-@property CharMatcher wordMatcher()
-{
- return memoizeExpr!("CharMatcher(wordCharacter)")();
+ static immutable CharMatcher matcher = CharMatcher(wordCharacter);
+ return matcher;
}
// some special Unicode white space characters
private enum NEL = '\u0085', LS = '\u2028', PS = '\u2029';
-// Characters that need escaping in string posed as regular expressions
-alias Escapables = AliasSeq!('[', ']', '\\', '^', '$', '.', '|', '?', ',', '-',
- ';', ':', '#', '&', '%', '/', '<', '>', '`', '*', '+', '(', ')', '{', '}', '~');
-
//Regular expression engine/parser options:
// global - search all nonoverlapping matches in input
// casefold - case insensitive matching, do casefolding on match in unicode mode
@@ -97,6 +74,20 @@ enum RegexOption: uint {
//do not reorder this list
alias RegexOptionNames = AliasSeq!('g', 'i', 'x', 'U', 'm', 's');
static assert( RegexOption.max < 0x80);
+
+package(std) string regexOptionsToString()(uint flags) nothrow pure @safe
+{
+ flags &= (RegexOption.max << 1) - 1;
+ if (!flags)
+ return "";
+ char[RegexOptionNames.length] buffer = void;
+ size_t pos = 0;
+ foreach (i, flag; __traits(allMembers, RegexOption))
+ if (flags & __traits(getMember, RegexOption, flag))
+ buffer[pos++] = RegexOptionNames[i];
+ return buffer[0 .. pos].idup;
+}
+
// flags that allow guide execution of engine
enum RegexInfo : uint { oneShot = 0x80 }
@@ -173,7 +164,8 @@ template IRL(IR code)
static assert(IRL!(IR.LookaheadStart) == 3);
//how many parameters follow the IR, should be optimized fixing some IR bits
-int immediateParamsIR(IR i){
+int immediateParamsIR(IR i) @safe pure nothrow @nogc
+{
switch (i)
{
case IR.OrEnd,IR.InfiniteEnd,IR.InfiniteQEnd:
@@ -190,49 +182,50 @@ int immediateParamsIR(IR i){
}
//full length of IR instruction inlcuding all parameters that might follow it
-int lengthOfIR(IR i)
+int lengthOfIR(IR i) @safe pure nothrow @nogc
{
return 1 + immediateParamsIR(i);
}
//full length of the paired IR instruction inlcuding all parameters that might follow it
-int lengthOfPairedIR(IR i)
+int lengthOfPairedIR(IR i) @safe pure nothrow @nogc
{
return 1 + immediateParamsIR(pairedIR(i));
}
//if the operation has a merge point (this relies on the order of the ops)
-bool hasMerge(IR i)
+bool hasMerge(IR i) @safe pure nothrow @nogc
{
return (i&0b11)==0b10 && i <= IR.RepeatQEnd;
}
//is an IR that opens a "group"
-bool isStartIR(IR i)
+bool isStartIR(IR i) @safe pure nothrow @nogc
{
return (i&0b11)==0b01;
}
//is an IR that ends a "group"
-bool isEndIR(IR i)
+bool isEndIR(IR i) @safe pure nothrow @nogc
{
return (i&0b11)==0b10;
}
//is a standalone IR
-bool isAtomIR(IR i)
+bool isAtomIR(IR i) @safe pure nothrow @nogc
{
return (i&0b11)==0b00;
}
//makes respective pair out of IR i, swapping start/end bits of instruction
-IR pairedIR(IR i)
+IR pairedIR(IR i) @safe pure nothrow @nogc
{
assert(isStartIR(i) || isEndIR(i));
- return cast(IR)(i ^ 0b11);
+ return cast(IR) (i ^ 0b11);
}
//encoded IR instruction
+@safe pure
struct Bytecode
{
uint raw;
@@ -241,6 +234,7 @@ struct Bytecode
enum maxData = 1 << 22;
enum maxRaw = 1 << 31;
+@safe pure:
this(IR code, uint data)
{
assert(data < (1 << 22) && code < 256);
@@ -262,8 +256,8 @@ struct Bytecode
return t;
}
- //bit twiddling helpers
- //0-arg template due to @@@BUG@@@ 10985
+ // bit twiddling helpers
+ // 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985
@property uint data()() const { return raw & 0x003f_ffff; }
@property void data()(uint val)
@@ -271,12 +265,12 @@ struct Bytecode
raw = (raw & ~0x003f_ffff) | (val & 0x003f_ffff);
}
- //ditto
- //0-arg template due to @@@BUG@@@ 10985
+ // ditto
+ // 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985
@property uint sequence()() const { return 2 + (raw >> 22 & 0x3); }
- //ditto
- //0-arg template due to @@@BUG@@@ 10985
+ // ditto
+ // 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985
@property IR code()() const { return cast(IR)(raw >> 24); }
//ditto
@@ -369,11 +363,20 @@ struct NamedGroup
//holds pair of start-end markers for a submatch
struct Group(DataIndex)
{
- DataIndex begin, end;
+ DataIndex begin = DataIndex.max;
+ DataIndex end = DataIndex.min;
+
+ bool opCast(T : bool)() const
+ {
+ return begin <= end;
+ }
+
@trusted string toString()() const
{
+ if (begin < end)
+ return "(unmatched)";
import std.array : appender;
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
auto a = appender!string();
formattedWrite(a, "%s..%s", begin, end);
return a.data;
@@ -384,7 +387,7 @@ struct Group(DataIndex)
@trusted string disassemble(in Bytecode[] irb, uint pc, in NamedGroup[] dict=[])
{
import std.array : appender;
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
auto output = appender!string();
formattedWrite(output,"%s", irb[pc].mnemonic);
switch (irb[pc].code)
@@ -452,9 +455,169 @@ struct Group(DataIndex)
writeln("\t", disassemble(slice, pc, dict));
}
+// Encapsulates memory management, explicit ref counting
+// and the exact type of engine created
+// there is a single instance per engine combination type x Char
+// In future may also maintain a (TLS?) cache of memory
+interface MatcherFactory(Char)
+{
+@safe:
+ Matcher!Char create(const ref Regex!Char, in Char[] input) const;
+ Matcher!Char dup(Matcher!Char m, in Char[] input) const;
+ size_t incRef(Matcher!Char m) const;
+ size_t decRef(Matcher!Char m) const;
+}
+
+// Only memory management, no compile-time vs run-time specialities
+abstract class GenericFactory(alias EngineType, Char) : MatcherFactory!Char
+{
+ import core.memory : pureFree;
+ import std.internal.memory : enforceMalloc;
+ import core.memory : GC;
+ // round up to next multiple of size_t for alignment purposes
+ enum classSize = (__traits(classInstanceSize, EngineType!Char) + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
+
+ EngineType!Char construct(const ref Regex!Char re, in Char[] input, void[] memory) const;
+
+ override EngineType!Char create(const ref Regex!Char re, in Char[] input) const @trusted
+ {
+ immutable size = EngineType!Char.initialMemory(re) + classSize;
+ auto memory = enforceMalloc(size)[0 .. size];
+ scope(failure) pureFree(memory.ptr);
+ GC.addRange(memory.ptr, classSize);
+ auto engine = construct(re, input, memory);
+ assert(engine.refCount == 1);
+ assert(cast(void*) engine == memory.ptr);
+ return engine;
+ }
+
+ override EngineType!Char dup(Matcher!Char engine, in Char[] input) const @trusted
+ {
+ immutable size = EngineType!Char.initialMemory(engine.pattern) + classSize;
+ auto memory = enforceMalloc(size)[0 .. size];
+ scope(failure) pureFree(memory.ptr);
+ auto copy = construct(engine.pattern, input, memory);
+ GC.addRange(memory.ptr, classSize);
+ engine.dupTo(copy, memory[classSize .. size]);
+ assert(copy.refCount == 1);
+ return copy;
+ }
+
+ override size_t incRef(Matcher!Char m) const
+ {
+ return ++m.refCount;
+ }
+
+ override size_t decRef(Matcher!Char m) const @trusted
+ {
+ assert(m.refCount != 0);
+ auto cnt = --m.refCount;
+ if (cnt == 0)
+ {
+ void* ptr = cast(void*) m;
+ GC.removeRange(ptr);
+ pureFree(ptr);
+ }
+ return cnt;
+ }
+}
+
+// A factory for run-time engines
+class RuntimeFactory(alias EngineType, Char) : GenericFactory!(EngineType, Char)
+{
+ override EngineType!Char construct(const ref Regex!Char re, in Char[] input, void[] memory) const
+ {
+ import core.lifetime : emplace;
+ return emplace!(EngineType!Char)(memory[0 .. classSize],
+ re, Input!Char(input), memory[classSize .. $]);
+ }
+}
+
+// A factory for compile-time engine
+class CtfeFactory(alias EngineType, Char, alias func) : GenericFactory!(EngineType, Char)
+{
+ override EngineType!Char construct(const ref Regex!Char re, in Char[] input, void[] memory) const
+ {
+ import core.lifetime : emplace;
+ return emplace!(EngineType!Char)(memory[0 .. classSize],
+ re, &func, Input!Char(input), memory[classSize .. $]);
+ }
+}
+
+private auto defaultFactoryImpl(Char)(const ref Regex!Char re)
+{
+ import std.regex.internal.backtracking : BacktrackingMatcher;
+ import std.regex.internal.thompson : ThompsonMatcher;
+ import std.algorithm.searching : canFind;
+ static MatcherFactory!Char backtrackingFactory;
+ static MatcherFactory!Char thompsonFactory;
+ if (re.backrefed.canFind!"a != 0")
+ {
+ if (backtrackingFactory is null)
+ backtrackingFactory = new RuntimeFactory!(BacktrackingMatcher, Char);
+ return backtrackingFactory;
+ }
+ else
+ {
+ if (thompsonFactory is null)
+ thompsonFactory = new RuntimeFactory!(ThompsonMatcher, Char);
+ return thompsonFactory;
+ }
+}
+
+// Used to generate a pure wrapper for defaultFactoryImpl. Based on the example in
+// the std.traits.SetFunctionAttributes documentation.
+auto assumePureFunction(T)(T t)
+if (isFunctionPointer!T)
+{
+ enum attrs = functionAttributes!T | FunctionAttribute.pure_;
+ return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
+}
+
+// A workaround for R-T enum re = regex(...)
+template defaultFactory(Char)
+{
+ // defaultFactory is constructed as a safe, pure wrapper over defaultFactoryImpl.
+ // It can be faked as pure because the static mutable variables are used to cache
+ // the key and character matcher. The technique used avoids delegates and GC.
+ @property MatcherFactory!Char defaultFactory(const ref Regex!Char re) @safe pure
+ {
+ static auto impl(const ref Regex!Char re)
+ {
+ return defaultFactoryImpl(re);
+ }
+
+ static auto pureImpl(const ref Regex!Char re) @trusted
+ {
+ auto p = assumePureFunction(&impl);
+ return p(re);
+ }
+
+ return pureImpl(re);
+ }
+}
+
+// Defining it as an interface has the undesired side-effect:
+// casting any class to an interface silently adjusts pointer to point to a nested vtbl
+abstract class Matcher(Char)
+{
+abstract:
+ // Get a (next) match
+ int match(Group!size_t[] matches) pure;
+ // This only maintains internal ref-count,
+ // deallocation happens inside MatcherFactory
+ @property ref size_t refCount() @safe;
+ // Copy internal state to another engine, using memory arena 'memory'
+ void dupTo(Matcher!Char m, void[] memory);
+ // The pattern loaded
+ @property ref const(Regex!Char) pattern() @safe;
+ // Re-arm the engine with new Input
+ Matcher rearm(in Char[] stream);
+}
+
/++
- $(D Regex) object holds regular expression pattern in compiled form.
- Instances of this object are constructed via calls to $(D regex).
+ `Regex` object holds regular expression pattern in compiled form.
+ Instances of this object are constructed via calls to `regex`.
This is an intended form for caching and storage of frequently
used regular expressions.
+/
@@ -466,17 +629,19 @@ struct Regex(Char)
@safe @property bool empty() const nothrow { return ir is null; }
-
+ /++
+ `namedCaptures` returns a range of all named captures in a given regular expression.
+ +/
@safe @property auto namedCaptures()
{
static struct NamedGroupRange
{
private:
- NamedGroup[] groups;
+ const(NamedGroup)[] groups;
size_t start;
size_t end;
public:
- this(NamedGroup[] g, size_t s, size_t e)
+ this(const(NamedGroup)[] g, size_t s, size_t e)
{
assert(s <= e);
assert(e <= g.length);
@@ -514,7 +679,7 @@ struct Regex(Char)
package(std.regex):
import std.regex.internal.kickstart : Kickstart; //TODO: get rid of this dependency
- NamedGroup[] dict; // maps name -> user group number
+ const(NamedGroup)[] dict; // maps name -> user group number
uint ngroup; // number of internal groups
uint maxCounterDepth; // max depth of nested {n,m} repetitions
uint hotspotTableSize; // number of entries in merge table
@@ -524,6 +689,36 @@ package(std.regex):
public const(BitTable)[] filters; // bloom filters for conditional loops
uint[] backrefed; // bit array of backreferenced submatches
Kickstart!Char kickstart;
+ MatcherFactory!Char factory; // produces optimal matcher for this pattern
+ immutable(Char)[] pattern; // copy of pattern to serve as cache key
+
+ const(Regex) withFactory(MatcherFactory!Char factory) pure const @trusted
+ {
+ auto r = cast() this;
+ r.factory = factory;
+ return r;
+ }
+
+ const(Regex) withFlags(uint newFlags) pure const @trusted
+ {
+ auto r = cast() this;
+ r.flags = newFlags;
+ return r;
+ }
+
+ const(Regex) withCode(const(Bytecode)[] code) pure const @trusted
+ {
+ auto r = cast() this;
+ r.ir = code.dup; // TODO: sidestep const instead?
+ return r;
+ }
+
+ const(Regex) withNGroup(uint nGroup) pure const @trusted
+ {
+ auto r = cast() this;
+ r.ngroup = nGroup;
+ return r;
+ }
//bit access helper
uint isBackref(uint n)
@@ -564,26 +759,20 @@ package(std.regex):
writeln("Max counter nesting depth: ", maxCounterDepth);
}
-}
-
-//@@@BUG@@@ (unreduced) - public makes it inaccessible in std.regex.package (!)
-/*public*/ struct StaticRegex(Char)
-{
-package(std.regex):
- import std.regex.internal.backtracking : BacktrackingMatcher;
- alias Matcher = BacktrackingMatcher!(true);
- alias MatchFn = bool function(ref Matcher!Char) @trusted;
- MatchFn nativeFn;
-public:
- Regex!Char _regex;
- alias _regex this;
- this(Regex!Char re, MatchFn fn)
+ public string toString()() const
{
- _regex = re;
- nativeFn = fn;
-
+ import std.format : format;
+ static if (is(typeof(pattern) : string))
+ alias patternString = pattern;
+ else
+ {
+ import std.conv : to;
+ auto patternString = conv.to!string(pattern);
+ }
+ auto quotedEscapedPattern = format("%(%s %)", [patternString]);
+ auto flagString = regexOptionsToString(flags);
+ return "Regex!" ~ Char.stringof ~ "(" ~ quotedEscapedPattern ~ ", \"" ~ flagString ~ "\")";
}
-
}
// The stuff below this point is temporarrily part of IR module
@@ -622,7 +811,7 @@ if (is(Char :dchar))
@property bool atEnd(){
return _index == _origin.length;
}
- bool search(Kickstart)(ref Kickstart kick, ref dchar res, ref size_t pos)
+ bool search(Kickstart)(ref const Kickstart kick, ref dchar res, ref size_t pos)
{
size_t idx = kick.search(_origin, _index);
_index = idx;
@@ -653,6 +842,11 @@ struct BackLooperImpl(Input)
_origin = input._origin;
_index = index;
}
+ this(String input)
+ {
+ _origin = input;
+ _index = input.length;
+ }
@trusted bool nextChar(ref dchar res,ref size_t pos)
{
pos = _index;
@@ -690,10 +884,10 @@ template BackLooper(E)
//both helpers below are internal, on its own are quite "explosive"
//unsafe, no initialization of elements
-@system T[] mallocArray(T)(size_t len)
+@system pure T[] mallocArray(T)(size_t len)
{
- import core.stdc.stdlib : malloc;
- return (cast(T*) malloc(len * T.sizeof))[0 .. len];
+ import core.memory : pureMalloc;
+ return (cast(T*) pureMalloc(len * T.sizeof))[0 .. len];
}
//very unsafe, no initialization
@@ -705,7 +899,7 @@ template BackLooper(E)
}
//
-@trusted uint lookupNamedGroup(String)(NamedGroup[] dict, String name)
+@trusted uint lookupNamedGroup(String)(const(NamedGroup)[] dict, String name)
{//equal is @system?
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;
@@ -718,16 +912,15 @@ template BackLooper(E)
return dict[fnd].group;
}
-//whether ch is one of unicode newline sequences
-//0-arg template due to @@@BUG@@@ 10985
+// whether ch is one of unicode newline sequences
+// 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985
bool endOfLine()(dchar front, bool seenCr)
{
return ((front == '\n') ^ seenCr) || front == '\r'
|| front == NEL || front == LS || front == PS;
}
-//
-//0-arg template due to @@@BUG@@@ 10985
+// 0-arg template due to https://issues.dlang.org/show_bug.cgi?id=10985
bool startOfLine()(dchar back, bool seenNl)
{
return ((back == '\r') ^ seenNl) || back == '\n'
@@ -786,3 +979,215 @@ struct CharMatcher {
return trie[ch];
}
}
+
+// Internal non-resizeble array, switches between inline storage and CoW
+// POD-only
+struct SmallFixedArray(T, uint SMALL=3)
+if (!hasElaborateDestructor!T)
+{
+ import std.internal.memory : enforceMalloc;
+ import core.memory : pureFree;
+ static struct Payload
+ {
+ size_t refcount;
+ T[0] placeholder;
+ inout(T)* ptr() inout { return placeholder.ptr; }
+ }
+ static assert(Payload.sizeof == size_t.sizeof);
+ union
+ {
+ Payload* big;
+ T[SMALL] small;
+ }
+ size_t _sizeMask;
+ enum BIG_MASK = size_t(1)<<(8*size_t.sizeof-1);
+ enum SIZE_MASK = ~BIG_MASK;
+
+ @property bool isBig() const { return (_sizeMask & BIG_MASK) != 0; }
+ @property size_t length() const { return _sizeMask & SIZE_MASK; }
+
+ this(size_t size)
+ {
+ if (size <= SMALL)
+ {
+ small[] = T.init;
+ _sizeMask = size;
+ }
+ else
+ {
+ big = cast(Payload*) enforceMalloc(Payload.sizeof + T.sizeof*size);
+ big.refcount = 1;
+ _sizeMask = size | BIG_MASK;
+ }
+ }
+
+ private @trusted @property inout(T)[] internalSlice() inout
+ {
+ return isBig ? big.ptr[0 .. length] : small[0 .. length];
+ }
+
+ this(this)
+ {
+ if (isBig)
+ {
+ big.refcount++;
+ }
+ }
+
+ bool opEquals(SmallFixedArray a)
+ {
+ return internalSlice[] == a.internalSlice[];
+ }
+
+ size_t toHash() const
+ {
+ return hashOf(internalSlice[]);
+ }
+
+ ref inout(T) opIndex(size_t idx) inout
+ {
+ return internalSlice[idx];
+ }
+
+ // accesses big to test self-referencing so not @safe
+ @trusted ref opAssign(SmallFixedArray arr)
+ {
+ if (isBig)
+ {
+ if (arr.isBig)
+ {
+ if (big is arr.big) return this; // self-assign
+ else
+ {
+ abandonRef();
+ _sizeMask = arr._sizeMask;
+ big = arr.big;
+ big.refcount++;
+ }
+ }
+ else
+ {
+ abandonRef();
+ _sizeMask = arr._sizeMask;
+ small = arr.small;
+ }
+ }
+ else
+ {
+ if (arr.isBig)
+ {
+ _sizeMask = arr._sizeMask;
+ big = arr.big;
+ big.refcount++;
+ }
+ else
+ {
+ _sizeMask = arr._sizeMask;
+ small = arr.small;
+ }
+ }
+ return this;
+ }
+
+ void mutate(scope void delegate(T[]) pure filler)
+ {
+ if (isBig && big.refcount != 1) // copy on write
+ {
+ auto oldSizeMask = _sizeMask;
+ auto newbig = cast(Payload*) enforceMalloc(Payload.sizeof + T.sizeof*length);
+ newbig.refcount = 1;
+ abandonRef();
+ big = newbig;
+ _sizeMask = oldSizeMask;
+ }
+ filler(internalSlice);
+ }
+
+ ~this()
+ {
+ if (isBig)
+ {
+ abandonRef();
+ }
+ }
+
+ @trusted private void abandonRef()
+ {
+ assert(isBig);
+ if (--big.refcount == 0)
+ {
+ pureFree(big);
+ _sizeMask = 0;
+ assert(!isBig);
+ }
+ }
+}
+
+@system unittest
+{
+ alias SA = SmallFixedArray!(int, 2);
+ SA create(int[] data)
+ {
+ SA a = SA(data.length);
+ a.mutate((slice) { slice[] = data[]; });
+ assert(a.internalSlice == data);
+ return a;
+ }
+
+ {
+ SA a;
+ a = SA(1);
+ assert(a.length == 1);
+ a = SA.init;
+ assert(a.length == 0);
+ }
+
+ {
+ SA a, b, c, d;
+ assert(a.length == 0);
+ assert(a.internalSlice == b.internalSlice);
+ a = create([1]);
+ assert(a.internalSlice == [1]);
+ b = create([2, 3]);
+ assert(b.internalSlice == [2, 3]);
+ c = create([3, 4, 5]);
+ d = create([5, 6, 7, 8]);
+ assert(c.isBig);
+ a = c;
+ assert(a.isBig);
+ assert(a.big is c.big);
+ assert(a.big.refcount == 2);
+ assert(a.internalSlice == [3, 4, 5]);
+ assert(c.internalSlice == [3, 4, 5]);
+ a = b;
+ assert(!a.isBig);
+ assert(a.internalSlice == [2, 3]);
+ assert(c.big.refcount == 1);
+ a = c;
+ assert(c.big.refcount == 2);
+
+ // mutate copies on write if ref-count is not 1
+ a.mutate((slice){ slice[] = 1; });
+ assert(a.internalSlice == [1, 1, 1]);
+ assert(c.internalSlice == [3, 4, 5]);
+ assert(a.isBig && c.isBig);
+ assert(a.big.refcount == 1);
+ assert(c.big.refcount == 1);
+
+ auto e = d;
+ assert(e.big.refcount == 2);
+ auto f = d;
+ f = a;
+ assert(f.isBig);
+ assert(f.internalSlice == [1, 1, 1]);
+ assert(f.big.refcount == 2); // a & f
+ assert(e.big.refcount == 2); // d & e
+ a = c;
+ assert(f.big.refcount == 1); // f
+ assert(e.big.refcount == 2); // d & e
+ a = a;
+ a = a;
+ a = a;
+ assert(a.big.refcount == 2); // a & c
+ }
+}
diff --git a/libphobos/src/std/regex/internal/kickstart.d b/libphobos/src/std/regex/internal/kickstart.d
index f303b43b6ba..452920514e1 100644
--- a/libphobos/src/std/regex/internal/kickstart.d
+++ b/libphobos/src/std/regex/internal/kickstart.d
@@ -393,7 +393,7 @@ public:
// has a useful trait: if supplied with valid UTF indexes,
// returns only valid UTF indexes
// (that given the haystack in question is valid UTF string)
- @trusted size_t search(const(Char)[] haystack, size_t idx)
+ @trusted size_t search(const(Char)[] haystack, size_t idx) const
{//@BUG: apparently assumes little endian machines
import core.stdc.string : memchr;
import std.conv : text;
@@ -517,8 +517,8 @@ public:
import std.conv, std.regex;
@trusted void test_fixed(alias Kick)()
{
- foreach (i, v; AliasSeq!(char, wchar, dchar))
- {
+ static foreach (i, v; AliasSeq!(char, wchar, dchar))
+ {{
alias Char = v;
alias String = immutable(v)[];
auto r = regex(to!String(`abc$`));
@@ -539,12 +539,12 @@ public:
assert(x == 3, text(Kick.stringof,v.stringof," == ", kick.length));
x = kick.search("aabaacaa", x+1);
assert(x == 8, text(Kick.stringof,v.stringof," == ", kick.length));
- }
+ }}
}
@trusted void test_flex(alias Kick)()
{
- foreach (i, v; AliasSeq!(char, wchar, dchar))
- {
+ static foreach (i, v; AliasSeq!(char, wchar, dchar))
+ {{
alias Char = v;
alias String = immutable(v)[];
auto r = regex(to!String(`abc[a-z]`));
@@ -570,7 +570,7 @@ public:
assert(kick.search("ababx",0) == 2);
assert(kick.search("abaacba",0) == 3);//expected inexact
- }
+ }}
}
test_fixed!(ShiftOr)();
test_flex!(ShiftOr)();
diff --git a/libphobos/src/std/regex/internal/parser.d b/libphobos/src/std/regex/internal/parser.d
index 8cabae5d0b5..41ca6872a7a 100644
--- a/libphobos/src/std/regex/internal/parser.d
+++ b/libphobos/src/std/regex/internal/parser.d
@@ -4,15 +4,19 @@
*/
module std.regex.internal.parser;
-static import std.ascii;
+import std.regex.internal.ir;
import std.range.primitives, std.uni, std.meta,
std.traits, std.typecons, std.exception;
-import std.regex.internal.ir;
+static import std.ascii;
// package relevant info from parser into a regex object
auto makeRegex(S, CG)(Parser!(S, CG) p)
{
- Regex!(BasicElementOf!S) re;
+ import std.regex.internal.backtracking : BacktrackingMatcher;
+ import std.regex.internal.thompson : ThompsonMatcher;
+ import std.algorithm.searching : canFind;
+ alias Char = BasicElementOf!S;
+ Regex!Char re;
auto g = p.g;
with(re)
{
@@ -24,7 +28,14 @@ auto makeRegex(S, CG)(Parser!(S, CG) p)
charsets = g.charsets;
matchers = g.matchers;
backrefed = g.backrefed;
+ re.pattern = p.origin.idup;
re.postprocess();
+ // check if we have backreferences, if so - use backtracking
+ if (__ctfe) factory = null; // allows us to use the awful enum re = regex(...);
+ else if (re.backrefed.canFind!"a != 0")
+ factory = new RuntimeFactory!(BacktrackingMatcher, Char);
+ else
+ factory = new RuntimeFactory!(ThompsonMatcher, Char);
debug(std_regex_parser)
{
__ctfe || print();
@@ -157,98 +168,6 @@ if (isSomeString!S)
code[] = rev[];
}
-//test if a given string starts with hex number of maxDigit that's a valid codepoint
-//returns it's value and skips these maxDigit chars on success, throws on failure
-dchar parseUniHex(Char)(ref Char[] str, size_t maxDigit)
-{
- //std.conv.parse is both @system and bogus
- enforce(str.length >= maxDigit,"incomplete escape sequence");
- uint val;
- for (int k = 0; k < maxDigit; k++)
- {
- immutable current = str[k];//accepts ascii only, so it's OK to index directly
- if ('0' <= current && current <= '9')
- val = val * 16 + current - '0';
- else if ('a' <= current && current <= 'f')
- val = val * 16 + current -'a' + 10;
- else if ('A' <= current && current <= 'F')
- val = val * 16 + current - 'A' + 10;
- else
- throw new Exception("invalid escape sequence");
- }
- enforce(val <= 0x10FFFF, "invalid codepoint");
- str = str[maxDigit..$];
- return val;
-}
-
-@system unittest //BUG canFind is system
-{
- import std.algorithm.searching : canFind;
- string[] non_hex = [ "000j", "000z", "FffG", "0Z"];
- string[] hex = [ "01", "ff", "00af", "10FFFF" ];
- int[] value = [ 1, 0xFF, 0xAF, 0x10FFFF ];
- foreach (v; non_hex)
- assert(collectException(parseUniHex(v, v.length)).msg
- .canFind("invalid escape sequence"));
- foreach (i, v; hex)
- assert(parseUniHex(v, v.length) == value[i]);
- string over = "0011FFFF";
- assert(collectException(parseUniHex(over, over.length)).msg
- .canFind("invalid codepoint"));
-}
-
-auto caseEnclose(CodepointSet set)
-{
- auto cased = set & unicode.LC;
- foreach (dchar ch; cased.byCodepoint)
- {
- foreach (c; simpleCaseFoldings(ch))
- set |= c;
- }
- return set;
-}
-
-/+
- fetch codepoint set corresponding to a name (InBlock or binary property)
-+/
-@trusted CodepointSet getUnicodeSet(in char[] name, bool negated, bool casefold)
-{
- CodepointSet s = unicode(name);
- //FIXME: caseEnclose for new uni as Set | CaseEnclose(SET && LC)
- if (casefold)
- s = caseEnclose(s);
- if (negated)
- s = s.inverted;
- return s;
-}
-
-//basic stack, just in case it gets used anywhere else then Parser
-@trusted struct Stack(T)
-{
- T[] data;
- @property bool empty(){ return data.empty; }
-
- @property size_t length(){ return data.length; }
-
- void push(T val){ data ~= val; }
-
- T pop()
- {
- assert(!empty);
- auto val = data[$ - 1];
- data = data[0 .. $ - 1];
- if (!__ctfe)
- cast(void) data.assumeSafeAppend();
- return val;
- }
-
- @property ref T top()
- {
- assert(!empty);
- return data[$ - 1];
- }
-}
-
struct CodeGen
{
Bytecode[] ir; // resulting bytecode
@@ -616,7 +535,7 @@ enum infinite = ~0u;
struct Parser(R, Generator)
if (isForwardRange!R && is(ElementType!R : dchar))
{
- dchar _current;
+ dchar front;
bool empty;
R pat, origin; //keep full pattern for pretty printing error messages
uint re_flags = 0; //global flags e.g. multiline + internal ones
@@ -628,8 +547,8 @@ if (isForwardRange!R && is(ElementType!R : dchar))
pat = origin = pattern;
//reserve slightly more then avg as sampled from unittests
parseFlags(flags);
- _current = ' ';//a safe default for freeform parsing
- next();
+ front = ' ';//a safe default for freeform parsing
+ popFront();
g.start(cast(uint) pat.length);
try
{
@@ -642,61 +561,47 @@ if (isForwardRange!R && is(ElementType!R : dchar))
g.endPattern(1);
}
- @property dchar current(){ return _current; }
-
- bool _next()
+ void _popFront()
{
if (pat.empty)
{
empty = true;
- return false;
}
- _current = pat.front;
- pat.popFront();
- return true;
+ else
+ {
+ front = pat.front;
+ pat.popFront();
+ }
}
void skipSpace()
{
- while (isWhite(current) && _next()){ }
+ while (!empty && isWhite(front)) _popFront();
}
- bool next()
+ void popFront()
{
- if (re_flags & RegexOption.freeform)
- {
- immutable r = _next();
- skipSpace();
- return r;
- }
- else
- return _next();
+ _popFront();
+ if (re_flags & RegexOption.freeform) skipSpace();
}
+ auto save(){ return this; }
+
//parsing number with basic overflow check
uint parseDecimal()
{
uint r = 0;
- while (std.ascii.isDigit(current))
+ while (std.ascii.isDigit(front))
{
if (r >= (uint.max/10))
error("Overflow in decimal number");
- r = 10*r + cast(uint)(current-'0');
- if (!next())
- break;
+ r = 10*r + cast(uint)(front-'0');
+ popFront();
+ if (empty) break;
}
return r;
}
- //parse control code of form \cXXX, c assumed to be the current symbol
- dchar parseControlCode()
- {
- enforce(next(), "Unfinished escape sequence");
- enforce(('a' <= current && current <= 'z') || ('A' <= current && current <= 'Z'),
- "Only letters are allowed after \\c");
- return current & 0x1f;
- }
-
//
@trusted void parseFlags(S)(S flags)
{//@@@BUG@@@ text is @system
@@ -730,73 +635,74 @@ if (isForwardRange!R && is(ElementType!R : dchar))
{
debug(std_regex_parser)
__ctfe || writeln("*LR*\nSource: ", pat, "\nStack: ",fixupStack.data);
- switch (current)
+ switch (front)
{
case '(':
- next();
- if (current == '?')
+ popFront();
+ if (front == '?')
{
- next();
- switch (current)
+ popFront();
+ switch (front)
{
case '#':
for (;;)
{
- if (!next())
- error("Unexpected end of pattern");
- if (current == ')')
+ popFront();
+ enforce(!empty, "Unexpected end of pattern");
+ if (front == ')')
{
- next();
+ popFront();
break;
}
}
break;
case ':':
g.genLogicGroup();
- next();
+ popFront();
break;
case '=':
g.genLookaround(IR.LookaheadStart);
- next();
+ popFront();
break;
case '!':
g.genLookaround(IR.NeglookaheadStart);
- next();
+ popFront();
break;
case 'P':
- next();
- if (current != '<')
- error("Expected '<' in named group");
+ popFront();
+ enforce(front == '<', "Expected '<' in named group");
string name;
- if (!next() || !(isAlpha(current) || current == '_'))
+ popFront();
+ if (empty || !(isAlpha(front) || front == '_'))
error("Expected alpha starting a named group");
- name ~= current;
- while (next() && (isAlpha(current) ||
- current == '_' || std.ascii.isDigit(current)))
+ name ~= front;
+ popFront();
+ while (!empty && (isAlpha(front) ||
+ front == '_' || std.ascii.isDigit(front)))
{
- name ~= current;
+ name ~= front;
+ popFront();
}
- if (current != '>')
- error("Expected '>' closing named group");
- next();
+ enforce(front == '>', "Expected '>' closing named group");
+ popFront();
g.genNamedGroup(name);
break;
case '<':
- next();
- if (current == '=')
+ popFront();
+ if (front == '=')
g.genLookaround(IR.LookbehindStart);
- else if (current == '!')
+ else if (front == '!')
g.genLookaround(IR.NeglookbehindStart);
else
error("'!' or '=' expected after '<'");
- next();
+ popFront();
break;
default:
uint enableFlags, disableFlags;
bool enable = true;
do
{
- switch (current)
+ switch (front)
{
case 's':
if (enable)
@@ -830,9 +736,9 @@ if (isForwardRange!R && is(ElementType!R : dchar))
default:
error(" 's', 'x', 'i', 'm' or '-' expected after '(?' ");
}
- next();
- }while (current != ')');
- next();
+ popFront();
+ }while (front != ')');
+ popFront();
re_flags |= enableFlags;
re_flags &= ~disableFlags;
}
@@ -844,13 +750,13 @@ if (isForwardRange!R && is(ElementType!R : dchar))
break;
case ')':
enforce(g.nesting, "Unmatched ')'");
- next();
+ popFront();
auto pair = g.onClose();
if (pair[0])
parseQuantifier(pair[1]);
break;
case '|':
- next();
+ popFront();
g.fixAlternation();
break;
default://no groups or whatever
@@ -875,7 +781,7 @@ if (isForwardRange!R && is(ElementType!R : dchar))
if (empty)
return g.fixRepetition(offset);
uint min, max;
- switch (current)
+ switch (front)
{
case '*':
min = 0;
@@ -890,28 +796,27 @@ if (isForwardRange!R && is(ElementType!R : dchar))
max = infinite;
break;
case '{':
- enforce(next(), "Unexpected end of regex pattern");
- enforce(std.ascii.isDigit(current), "First number required in repetition");
+ popFront();
+ enforce(!empty, "Unexpected end of regex pattern");
+ enforce(std.ascii.isDigit(front), "First number required in repetition");
min = parseDecimal();
- if (current == '}')
+ if (front == '}')
max = min;
- else if (current == ',')
+ else if (front == ',')
{
- next();
- if (std.ascii.isDigit(current))
+ popFront();
+ if (std.ascii.isDigit(front))
max = parseDecimal();
- else if (current == '}')
+ else if (front == '}')
max = infinite;
else
error("Unexpected symbol in regex pattern");
skipSpace();
- if (current != '}')
- error("Unmatched '{' in regex pattern");
+ enforce(front == '}', "Unmatched '{' in regex pattern");
}
else
error("Unexpected symbol in regex pattern");
- if (min > max)
- error("Illegal {n,m} quantifier");
+ enforce(min <= max, "Illegal {n,m} quantifier");
break;
default:
g.fixRepetition(offset);
@@ -919,10 +824,11 @@ if (isForwardRange!R && is(ElementType!R : dchar))
}
bool greedy = true;
//check only if we managed to get new symbol
- if (next() && current == '?')
+ popFront();
+ if (!empty && front == '?')
{
greedy = false;
- next();
+ popFront();
}
g.fixRepetition(offset, min, max, greedy);
}
@@ -932,11 +838,10 @@ if (isForwardRange!R && is(ElementType!R : dchar))
{
if (empty)
return;
- switch (current)
+ switch (front)
{
case '*', '?', '+', '|', '{', '}':
error("'*', '+', '?', '{', '}' not allowed in atom");
- break;
case '.':
if (re_flags & RegexOption.singleline)
g.put(Bytecode(IR.Any, 0));
@@ -945,13 +850,14 @@ if (isForwardRange!R && is(ElementType!R : dchar))
CodepointSet set;
g.charsetToIr(set.add('\n','\n'+1).add('\r', '\r'+1).inverted);
}
- next();
+ popFront();
break;
case '[':
parseCharset();
break;
case '\\':
- enforce(_next(), "Unfinished escape sequence");
+ _popFront();
+ enforce(!empty, "Unfinished escape sequence");
parseEscape();
break;
case '^':
@@ -959,20 +865,19 @@ if (isForwardRange!R && is(ElementType!R : dchar))
g.put(Bytecode(IR.Bol, 0));
else
g.put(Bytecode(IR.Bof, 0));
- next();
+ popFront();
break;
case '$':
if (re_flags & RegexOption.multiline)
g.put(Bytecode(IR.Eol, 0));
else
g.put(Bytecode(IR.Eof, 0));
- next();
+ popFront();
break;
default:
- //FIXME: getCommonCasing in new std uni
if (re_flags & RegexOption.casefold)
{
- auto range = simpleCaseFoldings(current);
+ auto range = simpleCaseFoldings(front);
assert(range.length <= 5);
if (range.length == 1)
g.put(Bytecode(IR.Char, range.front));
@@ -981,507 +886,96 @@ if (isForwardRange!R && is(ElementType!R : dchar))
g.put(Bytecode(IR.OrChar, v, cast(uint) range.length));
}
else
- g.put(Bytecode(IR.Char, current));
- next();
+ g.put(Bytecode(IR.Char, front));
+ popFront();
}
}
-
-
- //CodepointSet operations relatively in order of priority
- enum Operator:uint {
- Open = 0, Negate, Difference, SymDifference, Intersection, Union, None
- }
-
- //parse unit of CodepointSet spec, most notably escape sequences and char ranges
- //also fetches next set operation
- Tuple!(CodepointSet,Operator) parseCharTerm()
- {
- enum State{ Start, Char, Escape, CharDash, CharDashEscape,
- PotentialTwinSymbolOperator }
- Operator op = Operator.None;
- dchar last;
- CodepointSet set;
- State state = State.Start;
-
- static void addWithFlags(ref CodepointSet set, uint ch, uint re_flags)
- {
- if (re_flags & RegexOption.casefold)
- {
- auto range = simpleCaseFoldings(ch);
- foreach (v; range)
- set |= v;
- }
- else
- set |= ch;
- }
-
- static Operator twinSymbolOperator(dchar symbol)
- {
- switch (symbol)
- {
- case '|':
- return Operator.Union;
- case '-':
- return Operator.Difference;
- case '~':
- return Operator.SymDifference;
- case '&':
- return Operator.Intersection;
- default:
- assert(false);
- }
- }
-
- L_CharTermLoop:
- for (;;)
- {
- final switch (state)
- {
- case State.Start:
- switch (current)
- {
- case '|':
- case '-':
- case '~':
- case '&':
- state = State.PotentialTwinSymbolOperator;
- last = current;
- break;
- case '[':
- op = Operator.Union;
- goto case;
- case ']':
- break L_CharTermLoop;
- case '\\':
- state = State.Escape;
- break;
- default:
- state = State.Char;
- last = current;
- }
- break;
- case State.Char:
- // xxx last current xxx
- switch (current)
- {
- case '|':
- case '~':
- case '&':
- // then last is treated as normal char and added as implicit union
- state = State.PotentialTwinSymbolOperator;
- addWithFlags(set, last, re_flags);
- last = current;
- break;
- case '-': // still need more info
- state = State.CharDash;
- break;
- case '\\':
- set |= last;
- state = State.Escape;
- break;
- case '[':
- op = Operator.Union;
- goto case;
- case ']':
- addWithFlags(set, last, re_flags);
- break L_CharTermLoop;
- default:
- state = State.Char;
- addWithFlags(set, last, re_flags);
- last = current;
- }
- break;
- case State.PotentialTwinSymbolOperator:
- // xxx last current xxxx
- // where last = [|-&~]
- if (current == last)
- {
- op = twinSymbolOperator(last);
- next();//skip second twin char
- break L_CharTermLoop;
- }
- goto case State.Char;
- case State.Escape:
- // xxx \ current xxx
- switch (current)
- {
- case 'f':
- last = '\f';
- state = State.Char;
- break;
- case 'n':
- last = '\n';
- state = State.Char;
- break;
- case 'r':
- last = '\r';
- state = State.Char;
- break;
- case 't':
- last = '\t';
- state = State.Char;
- break;
- case 'v':
- last = '\v';
- state = State.Char;
- break;
- case 'c':
- last = parseControlCode();
- state = State.Char;
- break;
- foreach (val; Escapables)
- {
- case val:
- }
- last = current;
- state = State.Char;
- break;
- case 'p':
- set.add(parseUnicodePropertySpec(false));
- state = State.Start;
- continue L_CharTermLoop; //next char already fetched
- case 'P':
- set.add(parseUnicodePropertySpec(true));
- state = State.Start;
- continue L_CharTermLoop; //next char already fetched
- case 'x':
- last = parseUniHex(pat, 2);
- state = State.Char;
- break;
- case 'u':
- last = parseUniHex(pat, 4);
- state = State.Char;
- break;
- case 'U':
- last = parseUniHex(pat, 8);
- state = State.Char;
- break;
- case 'd':
- set.add(unicode.Nd);
- state = State.Start;
- break;
- case 'D':
- set.add(unicode.Nd.inverted);
- state = State.Start;
- break;
- case 's':
- set.add(unicode.White_Space);
- state = State.Start;
- break;
- case 'S':
- set.add(unicode.White_Space.inverted);
- state = State.Start;
- break;
- case 'w':
- set.add(wordCharacter);
- state = State.Start;
- break;
- case 'W':
- set.add(wordCharacter.inverted);
- state = State.Start;
- break;
- default:
- if (current >= privateUseStart && current <= privateUseEnd)
- enforce(false, "no matching ']' found while parsing character class");
- enforce(false, "invalid escape sequence");
- }
- break;
- case State.CharDash:
- // xxx last - current xxx
- switch (current)
- {
- case '[':
- op = Operator.Union;
- goto case;
- case ']':
- //means dash is a single char not an interval specifier
- addWithFlags(set, last, re_flags);
- addWithFlags(set, '-', re_flags);
- break L_CharTermLoop;
- case '-'://set Difference again
- addWithFlags(set, last, re_flags);
- op = Operator.Difference;
- next();//skip '-'
- break L_CharTermLoop;
- case '\\':
- state = State.CharDashEscape;
- break;
- default:
- enforce(last <= current, "inverted range");
- if (re_flags & RegexOption.casefold)
- {
- for (uint ch = last; ch <= current; ch++)
- addWithFlags(set, ch, re_flags);
- }
- else
- set.add(last, current + 1);
- state = State.Start;
- }
- break;
- case State.CharDashEscape:
- //xxx last - \ current xxx
- uint end;
- switch (current)
- {
- case 'f':
- end = '\f';
- break;
- case 'n':
- end = '\n';
- break;
- case 'r':
- end = '\r';
- break;
- case 't':
- end = '\t';
- break;
- case 'v':
- end = '\v';
- break;
- foreach (val; Escapables)
- {
- case val:
- }
- end = current;
- break;
- case 'c':
- end = parseControlCode();
- break;
- case 'x':
- end = parseUniHex(pat, 2);
- break;
- case 'u':
- end = parseUniHex(pat, 4);
- break;
- case 'U':
- end = parseUniHex(pat, 8);
- break;
- default:
- if (current >= privateUseStart && current <= privateUseEnd)
- enforce(false, "no matching ']' found while parsing character class");
- error("invalid escape sequence");
- }
- // Lookahead to check if it's a \T
- // where T is sub-pattern terminator in multi-pattern scheme
- if (end == '\\' && !pat.empty)
- {
- if (pat.front >= privateUseStart && pat.front <= privateUseEnd)
- enforce(false, "invalid escape sequence");
- }
- enforce(last <= end,"inverted range");
- set.add(last, end + 1);
- state = State.Start;
- break;
- }
- enforce(next(), "unexpected end of CodepointSet");
- }
- return tuple(set, op);
- }
-
- alias ValStack = Stack!(CodepointSet);
- alias OpStack = Stack!(Operator);
-
//parse and store IR for CodepointSet
void parseCharset()
{
const save = re_flags;
re_flags &= ~RegexOption.freeform; // stop ignoring whitespace if we did
- parseCharsetImpl();
+ bool casefold = cast(bool)(re_flags & RegexOption.casefold);
+ g.charsetToIr(unicode.parseSet(this, casefold));
re_flags = save;
- // Last next() in parseCharsetImp is executed w/o freeform flag
+ // Last next() in parseCharset is executed w/o freeform flag
if (re_flags & RegexOption.freeform) skipSpace();
}
- void parseCharsetImpl()
- {
- ValStack vstack;
- OpStack opstack;
- import std.functional : unaryFun;
- //
- static bool apply(Operator op, ref ValStack stack)
- {
- switch (op)
- {
- case Operator.Negate:
- enforce(!stack.empty, "no operand for '^'");
- stack.top = stack.top.inverted;
- break;
- case Operator.Union:
- auto s = stack.pop();//2nd operand
- enforce(!stack.empty, "no operand for '||'");
- stack.top.add(s);
- break;
- case Operator.Difference:
- auto s = stack.pop();//2nd operand
- enforce(!stack.empty, "no operand for '--'");
- stack.top.sub(s);
- break;
- case Operator.SymDifference:
- auto s = stack.pop();//2nd operand
- enforce(!stack.empty, "no operand for '~~'");
- stack.top ~= s;
- break;
- case Operator.Intersection:
- auto s = stack.pop();//2nd operand
- enforce(!stack.empty, "no operand for '&&'");
- stack.top.intersect(s);
- break;
- default:
- return false;
- }
- return true;
- }
- static bool unrollWhile(alias cond)(ref ValStack vstack, ref OpStack opstack)
- {
- while (cond(opstack.top))
- {
- if (!apply(opstack.pop(),vstack))
- return false;//syntax error
- if (opstack.empty)
- return false;
- }
- return true;
- }
-
- L_CharsetLoop:
- do
- {
- switch (current)
- {
- case '[':
- opstack.push(Operator.Open);
- enforce(next(), "unexpected end of character class");
- if (current == '^')
- {
- opstack.push(Operator.Negate);
- enforce(next(), "unexpected end of character class");
- }
- else if (current == ']') // []...] is special cased
- {
- enforce(next(), "wrong character set");
- auto pair = parseCharTerm();
- pair[0].add(']', ']'+1);
- if (pair[1] != Operator.None)
- {
- if (opstack.top == Operator.Union)
- unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack);
- opstack.push(pair[1]);
- }
- vstack.push(pair[0]);
- }
- break;
- case ']':
- enforce(unrollWhile!(unaryFun!"a != a.Open")(vstack, opstack),
- "character class syntax error");
- enforce(!opstack.empty, "unmatched ']'");
- opstack.pop();
- if (!next() || opstack.empty)
- break L_CharsetLoop;
- auto pair = parseCharTerm();
- if (!pair[0].empty)//not only operator e.g. -- or ~~
- {
- vstack.top.add(pair[0]);//apply union
- }
- if (pair[1] != Operator.None)
- {
- if (opstack.top == Operator.Union)
- unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack);
- opstack.push(pair[1]);
- }
- break;
- //
- default://yet another pair of term(op)?
- auto pair = parseCharTerm();
- if (pair[1] != Operator.None)
- {
- if (opstack.top == Operator.Union)
- unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack);
- opstack.push(pair[1]);
- }
- vstack.push(pair[0]);
- }
- }while (!empty || !opstack.empty);
- while (!opstack.empty)
- {
- enforce(opstack.top != Operator.Open,
- "no matching ']' found while parsing character class");
- apply(opstack.pop(), vstack);
- }
- assert(vstack.length == 1);
- g.charsetToIr(vstack.top);
- }
-
//parse and generate IR for escape stand alone escape sequence
@trusted void parseEscape()
{//accesses array of appender
import std.algorithm.iteration : sum;
- switch (current)
+ switch (front)
{
- case 'f': next(); g.put(Bytecode(IR.Char, '\f')); break;
- case 'n': next(); g.put(Bytecode(IR.Char, '\n')); break;
- case 'r': next(); g.put(Bytecode(IR.Char, '\r')); break;
- case 't': next(); g.put(Bytecode(IR.Char, '\t')); break;
- case 'v': next(); g.put(Bytecode(IR.Char, '\v')); break;
+ case 'f': popFront(); g.put(Bytecode(IR.Char, '\f')); break;
+ case 'n': popFront(); g.put(Bytecode(IR.Char, '\n')); break;
+ case 'r': popFront(); g.put(Bytecode(IR.Char, '\r')); break;
+ case 't': popFront(); g.put(Bytecode(IR.Char, '\t')); break;
+ case 'v': popFront(); g.put(Bytecode(IR.Char, '\v')); break;
case 'd':
- next();
+ popFront();
g.charsetToIr(unicode.Nd);
break;
case 'D':
- next();
+ popFront();
g.charsetToIr(unicode.Nd.inverted);
break;
- case 'b': next(); g.put(Bytecode(IR.Wordboundary, 0)); break;
- case 'B': next(); g.put(Bytecode(IR.Notwordboundary, 0)); break;
+ case 'b': popFront(); g.put(Bytecode(IR.Wordboundary, 0)); break;
+ case 'B': popFront(); g.put(Bytecode(IR.Notwordboundary, 0)); break;
case 's':
- next();
+ popFront();
g.charsetToIr(unicode.White_Space);
break;
case 'S':
- next();
+ popFront();
g.charsetToIr(unicode.White_Space.inverted);
break;
case 'w':
- next();
+ popFront();
g.charsetToIr(wordCharacter);
break;
case 'W':
- next();
+ popFront();
g.charsetToIr(wordCharacter.inverted);
break;
case 'p': case 'P':
- auto CodepointSet = parseUnicodePropertySpec(current == 'P');
- g.charsetToIr(CodepointSet);
+ bool casefold = cast(bool)(re_flags & RegexOption.casefold);
+ auto set = unicode.parsePropertySpec(this, front == 'P', casefold);
+ g.charsetToIr(set);
break;
case 'x':
immutable code = parseUniHex(pat, 2);
- next();
+ popFront();
g.put(Bytecode(IR.Char,code));
break;
case 'u': case 'U':
- immutable code = parseUniHex(pat, current == 'u' ? 4 : 8);
- next();
+ immutable code = parseUniHex(pat, front == 'u' ? 4 : 8);
+ popFront();
g.put(Bytecode(IR.Char, code));
break;
case 'c': //control codes
- Bytecode code = Bytecode(IR.Char, parseControlCode());
- next();
+ Bytecode code = Bytecode(IR.Char, unicode.parseControlCode(this));
+ popFront();
g.put(code);
break;
case '0':
- next();
+ popFront();
g.put(Bytecode(IR.Char, 0));//NUL character
break;
case '1': .. case '9':
- uint nref = cast(uint) current - '0';
+ uint nref = cast(uint) front - '0';
immutable maxBackref = sum(g.groupStack.data);
enforce(nref < maxBackref, "Backref to unseen group");
//perl's disambiguation rule i.e.
//get next digit only if there is such group number
- while (nref < maxBackref && next() && std.ascii.isDigit(current))
+ popFront();
+ while (nref < maxBackref && !empty && std.ascii.isDigit(front))
{
- nref = nref * 10 + current - '0';
+ nref = nref * 10 + front - '0';
+ popFront();
}
if (nref >= maxBackref)
nref /= 10;
@@ -1497,57 +991,27 @@ if (isForwardRange!R && is(ElementType!R : dchar))
g.markBackref(nref);
break;
default:
- // Lookahead to check if it's a \T
- // where T is sub-pattern terminator in multi-pattern scheme
- if (current == '\\' && !pat.empty)
+ if (front == '\\' && !pat.empty)
{
- if (pat.front >= privateUseStart && current <= privateUseEnd)
+ if (pat.front >= privateUseStart && pat.front <= privateUseEnd)
enforce(false, "invalid escape sequence");
}
- if (current >= privateUseStart && current <= privateUseEnd)
+ if (front >= privateUseStart && front <= privateUseEnd)
{
- g.endPattern(current - privateUseStart + 1);
+ g.endPattern(front - privateUseStart + 1);
break;
}
- auto op = Bytecode(IR.Char, current);
- next();
+ auto op = Bytecode(IR.Char, front);
+ popFront();
g.put(op);
}
}
- //parse and return a CodepointSet for \p{...Property...} and \P{...Property..},
- //\ - assumed to be processed, p - is current
- CodepointSet parseUnicodePropertySpec(bool negated)
- {
- enum MAX_PROPERTY = 128;
- char[MAX_PROPERTY] result;
- uint k = 0;
- enforce(next(), "eof parsing unicode property spec");
- if (current == '{')
- {
- while (k < MAX_PROPERTY && next() && current !='}' && current !=':')
- if (current != '-' && current != ' ' && current != '_')
- result[k++] = cast(char) std.ascii.toLower(current);
- enforce(k != MAX_PROPERTY, "invalid property name");
- enforce(current == '}', "} expected ");
- }
- else
- {//single char properties e.g.: \pL, \pN ...
- enforce(current < 0x80, "invalid property name");
- result[k++] = cast(char) current;
- }
- auto s = getUnicodeSet(result[0 .. k], negated,
- cast(bool)(re_flags & RegexOption.casefold));
- enforce(!s.empty, "unrecognized unicode property spec");
- next();
- return s;
- }
-
//
@trusted void error(string msg)
{
import std.array : appender;
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
auto app = appender!string();
formattedWrite(app, "%s\nPattern with error: `%s` <--HERE-- `%s`",
msg, origin[0..$-pat.length], pat);
diff --git a/libphobos/src/std/regex/internal/tests.d b/libphobos/src/std/regex/internal/tests.d
index fe75ce03c0a..8a0fec16a1b 100644
--- a/libphobos/src/std/regex/internal/tests.d
+++ b/libphobos/src/std/regex/internal/tests.d
@@ -8,9 +8,9 @@ package(std.regex):
import std.conv, std.exception, std.meta, std.range,
std.typecons, std.regex;
-import std.regex.internal.ir : Escapables; // characters that need escaping
+import std.uni : Escapables; // characters that need escaping
-alias Sequence(int B, int E) = staticIota!(B, E);
+debug(std_regex_test) import std.stdio;
@safe unittest
{//sanity checks
@@ -353,8 +353,8 @@ alias Sequence(int B, int E) = staticIota!(B, E);
void run_tests(alias matchFn)()
{
int i;
- foreach (Char; AliasSeq!( char, wchar, dchar))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (Char; AliasSeq!( char, wchar, dchar))
+ {{
alias String = immutable(Char)[];
String produceExpected(M,Range)(auto ref M m, Range fmt)
{
@@ -397,7 +397,7 @@ alias Sequence(int B, int E) = staticIota!(B, E);
}
}
}
- }();
+ }}
debug(std_regex_test) writeln("!!! FReD bulk test done "~matchFn.stringof~" !!!");
}
@@ -408,28 +408,28 @@ alias Sequence(int B, int E) = staticIota!(B, E);
version (std_regex_ct1)
{
pragma(msg, "Testing 1st part of ctRegex");
- alias Tests = Sequence!(0, 155);
+ enum Tests = iota(0, 155);
}
else version (std_regex_ct2)
{
pragma(msg, "Testing 2nd part of ctRegex");
- alias Tests = Sequence!(155, 174);
+ enum Tests = iota(155, 174);
}
//FIXME: #174-178 contains CTFE parser bug
else version (std_regex_ct3)
{
pragma(msg, "Testing 3rd part of ctRegex");
- alias Tests = Sequence!(178, 220);
+ enum Tests = iota(178, 220);
}
else version (std_regex_ct4)
{
pragma(msg, "Testing 4th part of ctRegex");
- alias Tests = Sequence!(220, tv.length);
+ enum Tests = iota(220, tv.length);
}
else
- alias Tests = AliasSeq!(Sequence!(0, 30), Sequence!(235, tv.length-5));
- foreach (a, v; Tests)
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ enum Tests = chain(iota(0, 30), iota(235, tv.length-5));
+ static foreach (v; Tests)
+ {{
enum tvd = tv[v];
static if (tvd.result == "c")
{
@@ -443,22 +443,20 @@ alias Sequence(int B, int E) = staticIota!(B, E);
auto r = ctRegex!(tv[v].pattern, tv[v].flags);
auto nr = regex(tvd.pattern, tvd.flags);
assert(equal(r.ir, nr.ir),
- text("!C-T regex! failed to compile pattern #", a ,": ", tvd.pattern));
+ text("!C-T regex! failed to compile pattern #", v ,": ", tvd.pattern));
auto m = match(tvd.input, r);
auto c = tvd.result[0];
bool ok = (c == 'y') ^ m.empty;
assert(ok, text("ctRegex: failed to match pattern #",
- a ,": ", tvd.pattern));
+ v ,": ", tvd.pattern));
if (c == 'y')
{
- import std.stdio;
auto result = produceExpected(m, tvd.format);
- if (result != tvd.replace)
- writeln("ctRegex mismatch pattern #", a, ": ", tvd.pattern," expected: ",
- tvd.replace, " vs ", result);
+ assert(result == tvd.replace, text("ctRegex mismatch pattern #", v,
+ ": ", tvd.pattern," expected: ", tvd.replace, " vs ", result));
}
}
- }();
+ }}
debug(std_regex_test) writeln("!!! FReD C-T test done !!!");
}
diff --git a/libphobos/src/std/regex/internal/tests2.d b/libphobos/src/std/regex/internal/tests2.d
index 420f8d3d6cc..0c9f23d7941 100644
--- a/libphobos/src/std/regex/internal/tests2.d
+++ b/libphobos/src/std/regex/internal/tests2.d
@@ -7,7 +7,7 @@ package(std.regex):
import std.conv, std.exception, std.meta, std.range,
std.typecons, std.regex;
-import std.regex.internal.ir : Escapables; // characters that need escaping
+import std.uni : Escapables; // characters that need escaping
@safe unittest
{
@@ -60,11 +60,11 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
{
import std.algorithm.comparison : equal;
auto rtr = regex("a|b|c");
- enum ctr = regex("a|b|c");
+ static ctr = regex("a|b|c");
assert(equal(rtr.ir,ctr.ir));
//CTFE parser BUG is triggered by group
//in the middle of alternation (at least not first and not last)
- enum testCT = regex(`abc|(edf)|xyz`);
+ static testCT = regex(`abc|(edf)|xyz`);
auto testRT = regex(`abc|(edf)|xyz`);
assert(equal(testCT.ir,testRT.ir));
}
@@ -149,7 +149,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
import std.algorithm.iteration : map;
void test_body(alias matchFn)()
{
- //issue 5857
+ // https://issues.dlang.org/show_bug.cgi?id=5857
//matching goes out of control if ... in (...){x} has .*/.+
auto c = matchFn("axxxzayyyyyzd",regex("(a.*z){2}d")).captures;
assert(c[0] == "axxxzayyyyyzd");
@@ -157,7 +157,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
auto c2 = matchFn("axxxayyyyyd",regex("(a.*){2}d")).captures;
assert(c2[0] == "axxxayyyyyd");
assert(c2[1] == "ayyyyy");
- //issue 2108
+ // https://issues.dlang.org/show_bug.cgi?id=2108
//greedy vs non-greedy
auto nogreed = regex("<packet.*?/packet>");
assert(matchFn("<packet>text</packet><packet>text</packet>", nogreed).hit
@@ -165,7 +165,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
auto greed = regex("<packet.*/packet>");
assert(matchFn("<packet>text</packet><packet>text</packet>", greed).hit
== "<packet>text</packet><packet>text</packet>");
- //issue 4574
+ // https://issues.dlang.org/show_bug.cgi?id=4574
//empty successful match still advances the input
string[] pres, posts, hits;
foreach (m; matchFn("abcabc", regex("","g")))
@@ -195,7 +195,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
];
assert(pres == array(retro(heads)));
assert(posts == tails);
- //issue 6076
+ // https://issues.dlang.org/show_bug.cgi?id=6076
//regression on .*
auto re = regex("c.*|d");
auto m = matchFn("mm", re);
@@ -214,7 +214,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(!match(to!string(ch),regex(`[^\`~ch~`]`)));
assert(match(to!string(ch),regex(`[\`~ch~`-\`~ch~`]`)));
}
- //bugzilla 7718
+ // https://issues.dlang.org/show_bug.cgi?id=7718
string strcmd = "./myApp.rb -os OSX -path \"/GIT/Ruby Apps/sec\" -conf 'notimer'";
auto reStrCmd = regex (`(".*")|('.*')`, "g");
assert(equal(map!"a[0]"(matchFn(strcmd, reStrCmd)),
@@ -231,8 +231,8 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
{
import std.uni : toUpper;
- foreach (i, v; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (i, v; AliasSeq!(string, wstring, dstring))
+ {{
auto baz(Cap)(Cap m)
if (is(Cap == Captures!(Cap.String)))
{
@@ -251,7 +251,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
auto s = std.regex.replace!(baz!(Captures!(String)))(to!String("Strap a rocket engine on a chicken."),
regex(to!String("[ar]"), "g"));
assert(s == "StRAp A Rocket engine on A chicken.");
- }
+ }}
debug(std_regex_test) writeln("!!! Replace test done "~matchFn.stringof~" !!!");
}
test!(bmatch)();
@@ -293,24 +293,29 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(equal(split(s1, regex(", *")), w1[]));
}
+// https://issues.dlang.org/show_bug.cgi?id=7141
@safe unittest
-{ // bugzilla 7141
+{
string pattern = `[a\--b]`;
assert(match("-", pattern));
assert(match("b", pattern));
string pattern2 = `[&-z]`;
assert(match("b", pattern2));
}
+
+// https://issues.dlang.org/show_bug.cgi?id=7111
@safe unittest
-{//bugzilla 7111
+{
assert(match("", regex("^")));
}
+
+// https://issues.dlang.org/show_bug.cgi?id=7300
@safe unittest
-{//bugzilla 7300
+{
assert(!match("a"d, "aa"d));
}
-// bugzilla 7551
+// https://issues.dlang.org/show_bug.cgi?id=7551
@safe unittest
{
auto r = regex("[]abc]*");
@@ -320,25 +325,30 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert("]ac".matchFirst(r2).hit == "]");
}
+// https://issues.dlang.org/show_bug.cgi?id=7674
@safe unittest
-{//bugzilla 7674
+{
assert("1234".replace(regex("^"), "$$") == "$1234");
assert("hello?".replace(regex(r"\?", "g"), r"\?") == r"hello\?");
assert("hello?".replace(regex(r"\?", "g"), r"\\?") != r"hello\?");
}
+
+// https://issues.dlang.org/show_bug.cgi?id=7679
@safe unittest
-{// bugzilla 7679
+{
import std.algorithm.comparison : equal;
- foreach (S; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
enum re = ctRegex!(to!S(r"\."));
auto str = to!S("a.b");
assert(equal(std.regex.splitter(str, re), [to!S("a"), to!S("b")]));
assert(split(str, re) == [to!S("a"), to!S("b")]);
- }();
+ }}
}
+
+// https://issues.dlang.org/show_bug.cgi?id=8203
@safe unittest
-{//bugzilla 8203
+{
string data = "
NAME = XPAW01_STA:STATION
NAME = XPAW01_STA
@@ -353,13 +363,15 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
auto r2 = regex(`([а-ÑÐ-Я\-_]+\s*)+(?<=[\s\.,\^])`);
match("Ð°Ð»Ð»ÐµÑ Ð¢ÐµÐ°Ñ‚Ñ€Ð°Ð»ÑŒÐ½Ð°Ñ", r2);
}
+
+// https://issues.dlang.org/show_bug.cgi?id=8637 purity of enforce
@safe unittest
-{// bugzilla 8637 purity of enforce
+{
auto m = match("hello world", regex("world"));
enforce(m);
}
-// bugzilla 8725
+// https://issues.dlang.org/show_bug.cgi?id=8725
@safe unittest
{
static italic = regex( r"\*
@@ -372,7 +384,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
"this * is* interesting, <i>very</i> interesting");
}
-// bugzilla 8349
+// https://issues.dlang.org/show_bug.cgi?id=8349
@safe unittest
{
enum peakRegexStr = r"\>(wgEncode.*Tfbs.*\.(?:narrow)|(?:broad)Peak.gz)</a>";
@@ -381,7 +393,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(match(r"\>wgEncode-blah-Tfbs.narrow</a>", peakRegex));
}
-// bugzilla 9211
+// https://issues.dlang.org/show_bug.cgi?id=9211
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -393,7 +405,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(equal(m2.front, ["1234", "3", "4"]));
}
-// bugzilla 9280
+// https://issues.dlang.org/show_bug.cgi?id=9280
@safe unittest
{
string tomatch = "a!b@c";
@@ -406,7 +418,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
}
-// bugzilla 9579
+// https://issues.dlang.org/show_bug.cgi?id=9579
@safe unittest
{
char[] input = ['a', 'b', 'c'];
@@ -417,14 +429,14 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(r == "(a)bc");
}
-// bugzilla 9634
+// https://issues.dlang.org/show_bug.cgi?id=9634
@safe unittest
{
auto re = ctRegex!"(?:a+)";
assert(match("aaaa", re).hit == "aaaa");
}
-//bugzilla 10798
+// https://issues.dlang.org/show_bug.cgi?id=10798
@safe unittest
{
auto cr = ctRegex!("[abcd--c]*");
@@ -433,7 +445,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(m.hit == "ab");
}
-// bugzilla 10913
+// https://issues.dlang.org/show_bug.cgi?id=10913
@system unittest
{
@system static string foo(const(char)[] s)
@@ -452,7 +464,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
}();
}
-// bugzilla 11262
+// https://issues.dlang.org/show_bug.cgi?id=11262
@safe unittest
{
enum reg = ctRegex!(r",", "g");
@@ -461,13 +473,13 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(str == "This-List");
}
-// bugzilla 11775
+// https://issues.dlang.org/show_bug.cgi?id=11775
@safe unittest
{
assert(collectException(regex("a{1,0}")));
}
-// bugzilla 11839
+// https://issues.dlang.org/show_bug.cgi?id=11839
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -478,7 +490,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(regex(`(?P<Ñ>\w+)`).namedCaptures.equal(["Ñ"]));
}
-// bugzilla 12076
+// https://issues.dlang.org/show_bug.cgi?id=12076
@safe unittest
{
auto RE = ctRegex!(r"(?<!x[a-z]+)\s([a-z]+)");
@@ -486,7 +498,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
auto m = match(s, RE);
}
-// bugzilla 12105
+// https://issues.dlang.org/show_bug.cgi?id=12105
@safe unittest
{
auto r = ctRegex!`.*?(?!a)`;
@@ -495,14 +507,14 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert("aaab".matchFirst(r2).hit == "aaab");
}
-//bugzilla 11784
+// https://issues.dlang.org/show_bug.cgi?id=11784
@safe unittest
{
assert("abcdefghijklmnopqrstuvwxyz"
.matchFirst("[a-z&&[^aeiuo]]").hit == "b");
}
-//bugzilla 12366
+// https://issues.dlang.org/show_bug.cgi?id=12366
@safe unittest
{
auto re = ctRegex!(`^((?=(xx+?)\2+$)((?=\2+$)(?=(x+)(\4+$))\5){2})*x?$`);
@@ -510,27 +522,27 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(!"xxxx".match(re).empty);
}
-// bugzilla 12582
+// https://issues.dlang.org/show_bug.cgi?id=12582
@safe unittest
{
auto r = regex(`(?P<a>abc)`);
assert(collectException("abc".matchFirst(r)["b"]));
}
-// bugzilla 12691
+// https://issues.dlang.org/show_bug.cgi?id=12691
@safe unittest
{
assert(bmatch("e@", "^([a-z]|)*$").empty);
assert(bmatch("e@", ctRegex!`^([a-z]|)*$`).empty);
}
-//bugzilla 12713
+// https://issues.dlang.org/show_bug.cgi?id=12713
@safe unittest
{
assertThrown(regex("[[a-z]([a-z]|(([[a-z])))"));
}
-//bugzilla 12747
+// https://issues.dlang.org/show_bug.cgi?id=12747
@safe unittest
{
assertThrown(regex(`^x(\1)`));
@@ -538,14 +550,44 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assertThrown(regex(`^((x)(?=\1))`));
}
-// bugzilla 14504
+// https://issues.dlang.org/show_bug.cgi?id=13532
+version (none) // TODO: revist once we have proper benchmark framework
+@safe unittest
+{
+ import std.datetime.stopwatch : StopWatch, AutoStart;
+ import std.math.algebraic : abs;
+ import std.conv : to;
+ enum re1 = ctRegex!`[0-9][0-9]`;
+ immutable static re2 = ctRegex!`[0-9][0-9]`;
+ immutable iterations = 1_000_000;
+ size_t result1 = 0, result2 = 0;
+ auto sw = StopWatch(AutoStart.yes);
+ foreach (_; 0 .. iterations)
+ {
+ result1 += matchFirst("12345678", re1).length;
+ }
+ const staticTime = sw.peek();
+ sw.reset();
+ foreach (_; 0 .. iterations)
+ {
+ result2 += matchFirst("12345678", re2).length;
+ }
+ const enumTime = sw.peek();
+ assert(result1 == result2);
+ auto ratio = 1.0 * enumTime.total!"usecs" / staticTime.total!"usecs";
+ // enum is faster or the diff is less < 30%
+ assert(ratio < 1.0 || abs(ratio - 1.0) < 0.75,
+ "enum regex to static regex ratio "~to!string(ratio));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=14504
@safe unittest
{
auto p = ctRegex!("a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?" ~
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
}
-// bugzilla 14529
+// https://issues.dlang.org/show_bug.cgi?id=14529
@safe unittest
{
auto ctPat2 = regex(r"^[CDF]$", "i");
@@ -553,7 +595,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(matchAll(v, ctPat2).front.hit == v);
}
-// bugzilla 14615
+// https://issues.dlang.org/show_bug.cgi?id=14615
@safe unittest
{
import std.array : appender;
@@ -572,14 +614,14 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(sink.data == "Hello, world!Hello, world!");
}
-// bugzilla 15573
+// https://issues.dlang.org/show_bug.cgi?id=15573
@safe unittest
{
auto rx = regex("[c d]", "x");
assert("a b".matchFirst(rx));
}
-// bugzilla 15864
+// https://issues.dlang.org/show_bug.cgi?id=15864
@safe unittest
{
regex(`(<a (?:(?:\w+=\"[^"]*\")?\s*)*href="\.\.?)"`);
@@ -592,7 +634,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assertThrown(regex("(?#..."));
}
-// bugzilla 17075
+// https://issues.dlang.org/show_bug.cgi?id=17075
@safe unittest
{
enum titlePattern = `<title>(.+)</title>`;
@@ -601,14 +643,14 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(input.matchFirst(titleRegex).empty);
}
-// bugzilla 17212
+// https://issues.dlang.org/show_bug.cgi?id=17212
@safe unittest
{
auto r = regex(" [a] ", "x");
assert("a".matchFirst(r));
}
-// bugzilla 17157
+// https://issues.dlang.org/show_bug.cgi?id=17157
@safe unittest
{
import std.algorithm.comparison : equal;
@@ -625,7 +667,7 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(equal!equal(s.bmatch(r), outcomes));
}
-// bugzilla 17667
+// https://issues.dlang.org/show_bug.cgi?id=17667
@safe unittest
{
import std.algorithm.searching : canFind;
@@ -637,19 +679,19 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
willThrow([r".", r"[\(\{[\]\}\)]"], "no matching ']' found while parsing character class");
willThrow([r"[\", r"123"], "no matching ']' found while parsing character class");
willThrow([r"[a-", r"123"], "no matching ']' found while parsing character class");
- willThrow([r"[a-\", r"123"], "invalid escape sequence");
+ willThrow([r"[a-\", r"123"], "no matching ']' found while parsing character class");
willThrow([r"\", r"123"], "invalid escape sequence");
}
-// bugzilla 17668
+// https://issues.dlang.org/show_bug.cgi?id=17668
@safe unittest
{
import std.algorithm.searching;
auto e = collectException!RegexException(regex(q"<[^]>"));
- assert(e.msg.canFind("no operand for '^'"));
+ assert(e.msg.canFind("no operand for '^'"), e.msg);
}
-// bugzilla 17673
+// https://issues.dlang.org/show_bug.cgi?id=17673
@safe unittest
{
string str = `<">`;
@@ -660,3 +702,12 @@ import std.regex.internal.ir : Escapables; // characters that need escaping
assert(c.whichPattern == 2);
}
+// https://issues.dlang.org/show_bug.cgi?id=18692
+@safe unittest
+{
+ auto rx = regex("()()()");
+ auto ma = "".matchFirst(rx);
+ auto ma2 = ma;
+ ma = ma2;
+ assert(ma[1] == "");
+}
diff --git a/libphobos/src/std/regex/internal/thompson.d b/libphobos/src/std/regex/internal/thompson.d
index 4d7deaa1f88..ab28d37ecb7 100644
--- a/libphobos/src/std/regex/internal/thompson.d
+++ b/libphobos/src/std/regex/internal/thompson.d
@@ -70,7 +70,7 @@ struct ThreadList(DataIndex)
this(ThreadList tlist){ ct = tlist.tip; }
@property bool empty(){ return ct is null; }
@property const(Thread!DataIndex)* front(){ return ct; }
- @property popFront()
+ void popFront()
{
assert(ct);
ct = ct.next;
@@ -89,7 +89,7 @@ struct ThreadList(DataIndex)
template ThompsonOps(E, S, bool withInput:true)
{
@trusted:
- static bool op(IR code:IR.End)(E* e, S* state)
+ static bool op(IR code:IR.End)(E e, S* state)
{
with(e) with(state)
{
@@ -105,7 +105,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Wordboundary)(E* e, S* state)
+ static bool op(IR code:IR.Wordboundary)(E e, S* state)
{
with(e) with(state)
{
@@ -137,7 +137,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Notwordboundary)(E* e, S* state)
+ static bool op(IR code:IR.Notwordboundary)(E e, S* state)
{
with(e) with(state)
{
@@ -167,7 +167,7 @@ template ThompsonOps(E, S, bool withInput:true)
return true;
}
- static bool op(IR code:IR.Bof)(E* e, S* state)
+ static bool op(IR code:IR.Bof)(E e, S* state)
{
with(e) with(state)
{
@@ -183,7 +183,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Bol)(E* e, S* state)
+ static bool op(IR code:IR.Bol)(E e, S* state)
{
with(e) with(state)
{
@@ -203,7 +203,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Eof)(E* e, S* state)
+ static bool op(IR code:IR.Eof)(E e, S* state)
{
with(e) with(state)
{
@@ -219,7 +219,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Eol)(E* e, S* state)
+ static bool op(IR code:IR.Eol)(E e, S* state)
{
with(e) with(state)
{
@@ -240,42 +240,42 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.InfiniteStart)(E* e, S* state)
+ static bool op(IR code:IR.InfiniteStart)(E e, S* state)
{
with(e) with(state)
t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteStart);
return op!(IR.InfiniteEnd)(e,state);
}
- static bool op(IR code:IR.InfiniteBloomStart)(E* e, S* state)
+ static bool op(IR code:IR.InfiniteBloomStart)(E e, S* state)
{
with(e) with(state)
t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteBloomStart);
return op!(IR.InfiniteBloomEnd)(e,state);
}
- static bool op(IR code:IR.InfiniteQStart)(E* e, S* state)
+ static bool op(IR code:IR.InfiniteQStart)(E e, S* state)
{
with(e) with(state)
t.pc += re.ir[t.pc].data + IRL!(IR.InfiniteQStart);
return op!(IR.InfiniteQEnd)(e,state);
}
- static bool op(IR code:IR.RepeatStart)(E* e, S* state)
+ static bool op(IR code:IR.RepeatStart)(E e, S* state)
{
with(e) with(state)
t.pc += re.ir[t.pc].data + IRL!(IR.RepeatStart);
return op!(IR.RepeatEnd)(e,state);
}
- static bool op(IR code:IR.RepeatQStart)(E* e, S* state)
+ static bool op(IR code:IR.RepeatQStart)(E e, S* state)
{
with(e) with(state)
t.pc += re.ir[t.pc].data + IRL!(IR.RepeatQStart);
return op!(IR.RepeatQEnd)(e,state);
}
- static bool op(IR code)(E* e, S* state)
+ static bool op(IR code)(E e, S* state)
if (code == IR.RepeatEnd || code == IR.RepeatQEnd)
{
with(e) with(state)
@@ -330,7 +330,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code)(E* e, S* state)
+ static bool op(IR code)(E e, S* state)
if (code == IR.InfiniteEnd || code == IR.InfiniteQEnd)
{
with(e) with(state)
@@ -365,7 +365,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code)(E* e, S* state)
+ static bool op(IR code)(E e, S* state)
if (code == IR.InfiniteBloomEnd)
{
with(e) with(state)
@@ -394,7 +394,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.OrEnd)(E* e, S* state)
+ static bool op(IR code:IR.OrEnd)(E e, S* state)
{
with(e) with(state)
{
@@ -415,7 +415,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.OrStart)(E* e, S* state)
+ static bool op(IR code:IR.OrStart)(E e, S* state)
{
with(e) with(state)
{
@@ -424,7 +424,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Option)(E* e, S* state)
+ static bool op(IR code:IR.Option)(E e, S* state)
{
with(e) with(state)
{
@@ -439,7 +439,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.GotoEndOr)(E* e, S* state)
+ static bool op(IR code:IR.GotoEndOr)(E e, S* state)
{
with(e) with(state)
{
@@ -448,7 +448,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.GroupStart)(E* e, S* state)
+ static bool op(IR code:IR.GroupStart)(E e, S* state)
{
with(e) with(state)
{
@@ -458,7 +458,7 @@ template ThompsonOps(E, S, bool withInput:true)
return true;
}
}
- static bool op(IR code:IR.GroupEnd)(E* e, S* state)
+ static bool op(IR code:IR.GroupEnd)(E e, S* state)
{
with(e) with(state)
{
@@ -469,7 +469,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Backref)(E* e, S* state)
+ static bool op(IR code:IR.Backref)(E e, S* state)
{
with(e) with(state)
{
@@ -506,7 +506,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
- static bool op(IR code)(E* e, S* state)
+ static bool op(IR code)(E e, S* state)
if (code == IR.LookbehindStart || code == IR.NeglookbehindStart)
{
with(e) with(state)
@@ -516,10 +516,9 @@ template ThompsonOps(E, S, bool withInput:true)
uint end = t.pc + len + IRL!(IR.LookbehindEnd) + IRL!(IR.LookbehindStart);
bool positive = re.ir[t.pc].code == IR.LookbehindStart;
static if (Stream.isLoopback)
- auto matcher = fwdMatcher(t.pc, end, subCounters.get(t.pc, 0));
+ auto matcher = fwdMatcher(t.pc, end, me - ms, subCounters.get(t.pc, 0));
else
- auto matcher = bwdMatcher(t.pc, end, subCounters.get(t.pc, 0));
- matcher.re.ngroup = me - ms;
+ auto matcher = bwdMatcher(t.pc, end, me - ms, subCounters.get(t.pc, 0));
matcher.backrefed = backrefed.empty ? t.matches : backrefed;
//backMatch
auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookbehindStart));
@@ -534,7 +533,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code)(E* e, S* state)
+ static bool op(IR code)(E e, S* state)
if (code == IR.LookaheadStart || code == IR.NeglookaheadStart)
{
with(e) with(state)
@@ -545,10 +544,9 @@ template ThompsonOps(E, S, bool withInput:true)
uint end = t.pc+len+IRL!(IR.LookaheadEnd)+IRL!(IR.LookaheadStart);
bool positive = re.ir[t.pc].code == IR.LookaheadStart;
static if (Stream.isLoopback)
- auto matcher = bwdMatcher(t.pc, end, subCounters.get(t.pc, 0));
+ auto matcher = bwdMatcher(t.pc, end, me - ms, subCounters.get(t.pc, 0));
else
- auto matcher = fwdMatcher(t.pc, end, subCounters.get(t.pc, 0));
- matcher.re.ngroup = me - ms;
+ auto matcher = fwdMatcher(t.pc, end, me - ms, subCounters.get(t.pc, 0));
matcher.backrefed = backrefed.empty ? t.matches : backrefed;
auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookaheadStart));
freelist = matcher.freelist;
@@ -564,7 +562,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code)(E* e, S* state)
+ static bool op(IR code)(E e, S* state)
if (code == IR.LookaheadEnd || code == IR.NeglookaheadEnd ||
code == IR.LookbehindEnd || code == IR.NeglookbehindEnd)
{
@@ -579,13 +577,13 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Nop)(E* e, S* state)
+ static bool op(IR code:IR.Nop)(E e, S* state)
{
with(state) t.pc += IRL!(IR.Nop);
return true;
}
- static bool op(IR code:IR.OrChar)(E* e, S* state)
+ static bool op(IR code:IR.OrChar)(E e, S* state)
{
with(e) with(state)
{
@@ -607,7 +605,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Char)(E* e, S* state)
+ static bool op(IR code:IR.Char)(E e, S* state)
{
with(e) with(state)
{
@@ -623,7 +621,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Any)(E* e, S* state)
+ static bool op(IR code:IR.Any)(E e, S* state)
{
with(e) with(state)
{
@@ -634,7 +632,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.CodepointSet)(E* e, S* state)
+ static bool op(IR code:IR.CodepointSet)(E e, S* state)
{
with(e) with(state)
{
@@ -652,7 +650,7 @@ template ThompsonOps(E, S, bool withInput:true)
}
}
- static bool op(IR code:IR.Trie)(E* e, S* state)
+ static bool op(IR code:IR.Trie)(E e, S* state)
{
with(e) with(state)
{
@@ -676,7 +674,7 @@ template ThompsonOps(E,S, bool withInput:false)
{
@trusted:
// can't match these without input
- static bool op(IR code)(E* e, S* state)
+ static bool op(IR code)(E e, S* state)
if (code == IR.Char || code == IR.OrChar || code == IR.CodepointSet
|| code == IR.Trie || code == IR.Char || code == IR.Any)
{
@@ -684,7 +682,7 @@ template ThompsonOps(E,S, bool withInput:false)
}
// special case of zero-width backref
- static bool op(IR code:IR.Backref)(E* e, S* state)
+ static bool op(IR code:IR.Backref)(E e, S* state)
{
with(e) with(state)
{
@@ -702,7 +700,7 @@ template ThompsonOps(E,S, bool withInput:false)
}
// forward all control flow to normal versions
- static bool op(IR code)(E* e, S* state)
+ static bool op(IR code)(E e, S* state)
if (code != IR.Char && code != IR.OrChar && code != IR.CodepointSet
&& code != IR.Trie && code != IR.Char && code != IR.Any && code != IR.Backref)
{
@@ -714,19 +712,19 @@ template ThompsonOps(E,S, bool withInput:false)
Thomspon matcher does all matching in lockstep,
never looking at the same char twice
+/
-@trusted struct ThompsonMatcher(Char, StreamType = Input!Char)
+@trusted class ThompsonMatcher(Char, StreamType = Input!Char): Matcher!Char
if (is(Char : dchar))
{
alias DataIndex = Stream.DataIndex;
alias Stream = StreamType;
- alias OpFunc = bool function(ThompsonMatcher*, State*);
+ alias OpFunc = bool function(ThompsonMatcher, State*) pure;
alias BackMatcher = ThompsonMatcher!(Char, BackLooper!(Stream));
- alias OpBackFunc = bool function(BackMatcher*, BackMatcher.State*);
+ alias OpBackFunc = bool function(BackMatcher, BackMatcher.State*) pure;
Thread!DataIndex* freelist;
ThreadList!DataIndex clist, nlist;
DataIndex[] merge;
Group!DataIndex[] backrefed;
- Regex!Char re; //regex program
+ const Regex!Char re; //regex program
Stream s;
dchar front;
DataIndex index;
@@ -737,16 +735,19 @@ if (is(Char : dchar))
OpBackFunc[] opCacheBackTrue; // ditto
OpBackFunc[] opCacheBackFalse; // ditto
size_t threadSize;
+ size_t _refCount;
int matched;
bool exhausted;
+final:
+ pure
static struct State
{
Thread!DataIndex* t;
ThreadList!DataIndex worklist;
Group!DataIndex[] matches;
- bool popState(E)(E* e)
+ bool popState(E)(E e)
{
with(e)
{
@@ -784,6 +785,10 @@ if (is(Char : dchar))
//true if it's end of input
@property bool atEnd(){ return index == s.lastIndex && s.atEnd; }
+ override @property ref size_t refCount() @safe { return _refCount; }
+
+ override @property ref const(Regex!Char) pattern() @safe { return re; }
+
bool next()
{
if (!s.nextChar(front, index))
@@ -843,19 +848,36 @@ if (is(Char : dchar))
}
}
- this()(Regex!Char program, Stream stream, void[] memory)
+ override Matcher!Char rearm(in Char[] data)
+ {
+ exhausted = false;
+ matched = 0;
+ s = Stream(data);
+ return this;
+ }
+
+ this()(ref const Regex!Char program, Stream stream, void[] memory)
{
+ // We are emplace'd to malloced memory w/o blitting T.init over it\
+ // make sure we initialize all fields explicitly
+ _refCount = 1;
+ subCounters = null;
+ backrefed = null;
+ exhausted = false;
+ matched = 0;
re = program;
s = stream;
initExternalMemory(memory);
genCounter = 0;
}
- this(ref ThompsonMatcher matcher, size_t lo, size_t hi, Stream stream)
+ this(ThompsonMatcher matcher, size_t lo, size_t hi, uint nGroup, Stream stream)
{
+ _refCount = 1;
+ subCounters = matcher.subCounters;
s = stream;
- re = matcher.re;
- re.ir = re.ir[lo .. hi];
+ auto code = matcher.re.ir[lo .. hi];
+ re = matcher.re.withCode(code).withNGroup(nGroup);
threadSize = matcher.threadSize;
merge = matcher.merge;
freelist = matcher.freelist;
@@ -867,11 +889,13 @@ if (is(Char : dchar))
index = matcher.index;
}
- this(ref BackMatcher matcher, size_t lo, size_t hi, Stream stream)
+ this(BackMatcher matcher, size_t lo, size_t hi, uint nGroup, Stream stream)
{
+ _refCount = 1;
+ subCounters = matcher.subCounters;
s = stream;
- re = matcher.re;
- re.ir = re.ir[lo .. hi];
+ auto code = matcher.re.ir[lo .. hi];
+ re = matcher.re.withCode(code).withNGroup(nGroup);
threadSize = matcher.threadSize;
merge = matcher.merge;
freelist = matcher.freelist;
@@ -883,31 +907,35 @@ if (is(Char : dchar))
index = matcher.index;
}
- auto fwdMatcher()(size_t lo, size_t hi, size_t counter)
+ auto fwdMatcher()(size_t lo, size_t hi, uint nGroup, size_t counter)
{
- auto m = ThompsonMatcher!(Char, Stream)(this, lo, hi, s);
+ auto m = new ThompsonMatcher!(Char, Stream)(this, lo, hi, nGroup, s);
m.genCounter = counter;
return m;
}
- auto bwdMatcher()(size_t lo, size_t hi, size_t counter)
+ auto bwdMatcher()(size_t lo, size_t hi, uint nGroup, size_t counter)
{
alias BackLooper = typeof(s.loopBack(index));
- auto m = ThompsonMatcher!(Char, BackLooper)(this, lo, hi, s.loopBack(index));
+ auto m = new ThompsonMatcher!(Char, BackLooper)(this, lo, hi, nGroup, s.loopBack(index));
m.genCounter = counter;
m.next();
return m;
}
- auto dupTo(void[] memory)
+ override void dupTo(Matcher!Char engine, void[] memory)
{
- typeof(this) tmp = this;//bitblit
- tmp.initExternalMemory(memory);
- tmp.genCounter = 0;
- return tmp;
+ auto thompson = cast(ThompsonMatcher) engine;
+ thompson.s = s;
+ thompson.subCounters = null;
+ thompson.front = front;
+ thompson.index = index;
+ thompson.matched = matched;
+ thompson.exhausted = exhausted;
+ thompson.initExternalMemory(memory);
}
- int match(Group!DataIndex[] matches)
+ override int match(Group!DataIndex[] matches)
{
debug(std_regex_matcher)
writeln("------------------------------------------");
@@ -1052,9 +1080,9 @@ if (is(Char : dchar))
{
debug(std_regex_matcher) writeln("---- Evaluating thread");
static if (withInput)
- while (opCacheTrue.ptr[state.t.pc](&this, state)){}
+ while (opCacheTrue.ptr[state.t.pc](this, state)){}
else
- while (opCacheFalse.ptr[state.t.pc](&this, state)){}
+ while (opCacheFalse.ptr[state.t.pc](this, state)){}
}
enum uint RestartPc = uint.max;
//match the input, evaluating IR without searching
diff --git a/libphobos/src/std/regex/package.d b/libphobos/src/std/regex/package.d
index bfc7d7ff30b..82207b6cc68 100644
--- a/libphobos/src/std/regex/package.d
+++ b/libphobos/src/std/regex/package.d
@@ -7,6 +7,7 @@
in text processing utilities.
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE,
$(TR $(TH Category) $(TH Functions))
$(TR $(TD Matching) $(TD
@@ -18,7 +19,7 @@ $(TR $(TD Matching) $(TD
$(TR $(TD Building) $(TD
$(LREF ctRegex)
$(LREF escaper)
- $(LREF _regex)
+ $(LREF regex)
))
$(TR $(TD Replace) $(TD
$(LREF replace)
@@ -39,7 +40,7 @@ $(TR $(TD Objects) $(TD
$(LREF Splitter)
$(LREF StaticRegex)
))
-)
+))
$(SECTION Synopsis)
---
@@ -92,7 +93,7 @@ $(TR $(TD Objects) $(TD
Checks of this sort of are better addressed by additional post-processing.
The basic syntax shouldn't surprise experienced users of regular expressions.
- For an introduction to $(D std.regex) see a
+ For an introduction to `std.regex` see a
$(HTTP dlang.org/regular-expression.html, short tour) of the module API
and its abilities.
@@ -136,7 +137,7 @@ $(TR $(TD Objects) $(TD
$(REG_ROW \W, Matches any non-word character.)
$(REG_ROW \s, Matches whitespace, same as \p{White_Space}.)
$(REG_ROW \S, Matches any character except those recognized as $(I \s ). )
- $(REG_ROW \\, Matches \ character. )
+ $(REG_ROW \\\\, Matches \ character. )
$(REG_ROW \c where c is one of [|*+?(), Matches the character c itself. )
$(REG_ROW \p{PropertyName}, Matches a character that belongs
to the Unicode PropertyName set.
@@ -208,7 +209,8 @@ $(TR $(TD Objects) $(TD
$(REG_START Character classes )
$(REG_TABLE
$(REG_TITLE Pattern element, Semantics )
- $(REG_ROW Any atom, Has the same meaning as outside of a character class.)
+ $(REG_ROW Any atom, Has the same meaning as outside of a character class,
+ except for ] which must be written as \\])
$(REG_ROW a-z, Includes characters a, b, c, ..., z. )
$(REG_ROW [a||b]$(COMMA) [a--b]$(COMMA) [a~~b]$(COMMA) [a$(AMP)$(AMP)b],
Where a, b are arbitrary classes, means union, set difference,
@@ -250,25 +252,25 @@ $(TR $(TD Objects) $(TD
A set of functions in this module that do the substitution rely
on a simple format to guide the process. In particular the table below
- applies to the $(D format) argument of
+ applies to the `format` argument of
$(LREF replaceFirst) and $(LREF replaceAll).
The format string can reference parts of match using the following notation.
$(REG_TABLE
$(REG_TITLE Format specifier, Replaced by )
- $(REG_ROW $$(AMP), the whole match. )
+ $(REG_ROW $(DOLLAR)$(AMP), the whole match. )
$(REG_ROW $(DOLLAR)$(BACKTICK), part of input $(I preceding) the match. )
$(REG_ROW $', part of input $(I following) the match. )
$(REG_ROW $$, '$' character. )
$(REG_ROW \c $(COMMA) where c is any character, the character c itself. )
- $(REG_ROW \\, '\' character. )
+ $(REG_ROW \\\\, '\\' character. )
$(REG_ROW $(DOLLAR)1 .. $(DOLLAR)99, submatch number 1 to 99 respectively. )
)
$(SECTION Slicing and zero memory allocations orientation)
All matches returned by pattern matching functionality in this library
- are slices of the original input. The notable exception is the $(D replace)
+ are slices of the original input. The notable exception is the `replace`
family of functions that generate a new string from the input.
In cases where producing the replacement is the ultimate goal
@@ -281,10 +283,10 @@ $(TR $(TD Objects) $(TD
Authors: Dmitry Olshansky,
- API and utility constructs are modeled after the original $(D std.regex)
+ API and utility constructs are modeled after the original `std.regex`
by Walter Bright and Andrei Alexandrescu.
- Source: $(PHOBOSSRC std/_regex/_package.d)
+ Source: $(PHOBOSSRC std/regex/package.d)
Macros:
REG_ROW = $(TR $(TD $(I $1 )) $(TD $+) )
@@ -298,13 +300,12 @@ module std.regex;
import std.range.primitives, std.traits;
import std.regex.internal.ir;
-import std.regex.internal.thompson; //TODO: get rid of this dependency
-import std.typecons; // : Flag, Yes, No;
+import std.typecons : Flag, Yes, No;
/++
- $(D Regex) object holds regular expression pattern in compiled form.
+ `Regex` object holds regular expression pattern in compiled form.
- Instances of this object are constructed via calls to $(D regex).
+ Instances of this object are constructed via calls to `regex`.
This is an intended form for caching and storage of frequently
used regular expressions.
@@ -336,35 +337,34 @@ import std.typecons; // : Flag, Yes, No;
public alias Regex(Char) = std.regex.internal.ir.Regex!(Char);
/++
- A $(D StaticRegex) is $(D Regex) object that contains D code specially
+ A `StaticRegex` is `Regex` object that contains D code specially
generated at compile-time to speed up matching.
- Implicitly convertible to normal $(D Regex),
- however doing so will result in losing this additional capability.
+ No longer used, kept as alias to Regex for backwards compatibility.
+/
-public alias StaticRegex(Char) = std.regex.internal.ir.StaticRegex!(Char);
+public alias StaticRegex = Regex;
/++
Compile regular expression pattern for the later execution.
- Returns: $(D Regex) object that works on inputs having
- the same character width as $(D pattern).
+ Returns: `Regex` object that works on inputs having
+ the same character width as `pattern`.
Params:
pattern = A single regular expression to match.
patterns = An array of regular expression strings.
The resulting `Regex` object will match any expression;
use $(LREF whichPattern) to know which.
- flags = The _attributes (g, i, m and x accepted)
+ flags = The _attributes (g, i, m, s and x accepted)
- Throws: $(D RegexException) if there were any errors during compilation.
+ Throws: `RegexException` if there were any errors during compilation.
+/
-@trusted public auto regex(S)(S[] patterns, const(char)[] flags="")
+@trusted public auto regex(S : C[], C)(const S[] patterns, const(char)[] flags="")
if (isSomeString!(S))
{
import std.array : appender;
import std.functional : memoize;
enum cacheSize = 8; //TODO: invent nice interface to control regex caching
- S pat;
+ const(C)[] pat;
if (patterns.length > 1)
{
auto app = appender!S();
@@ -404,19 +404,42 @@ if (isSomeString!(S))
///
@system unittest
{
- // multi-pattern regex example
- auto multi = regex([`([a-z]+):(\d+)`, `(\d+),\d+`]); // multi regex
- auto m = "abc:43 12,34".matchAll(multi);
- assert(m.front.whichPattern == 1);
- assert(m.front[1] == "abc");
- assert(m.front[2] == "43");
- m.popFront();
- assert(m.front.whichPattern == 2);
- assert(m.front[1] == "12");
+ void test(S)()
+ {
+ // multi-pattern regex example
+ S[] arr = [`([a-z]+):(\d+)`, `(\d+),\d+`];
+ auto multi = regex(arr); // multi regex
+ S str = "abc:43 12,34";
+ auto m = str.matchAll(multi);
+ assert(m.front.whichPattern == 1);
+ assert(m.front[1] == "abc");
+ assert(m.front[2] == "43");
+ m.popFront();
+ assert(m.front.whichPattern == 2);
+ assert(m.front[1] == "12");
+ }
+
+ import std.meta : AliasSeq;
+ static foreach (C; AliasSeq!(string, wstring, dstring))
+ // Test with const array of patterns - see https://issues.dlang.org/show_bug.cgi?id=20301
+ static foreach (S; AliasSeq!(C, const C, immutable C))
+ test!S();
}
-public auto regexImpl(S)(S pattern, const(char)[] flags="")
-if (isSomeString!(S))
+@system unittest
+{
+ import std.conv : to;
+ import std.string : indexOf;
+
+ immutable pattern = "s+";
+ auto regexString = to!string(regex(pattern, "U"));
+ assert(regexString.length <= pattern.length + 100, "String representation shouldn't be unreasonably bloated.");
+ assert(indexOf(regexString, "s+") >= 0, "String representation should include pattern.");
+ assert(indexOf(regexString, 'U') >= 0, "String representation should include flags.");
+}
+
+public auto regexImpl(S)(const S pattern, const(char)[] flags="")
+if (isSomeString!(typeof(pattern)))
{
import std.regex.internal.parser : Parser, CodeGen;
auto parser = Parser!(Unqual!(typeof(pattern)), CodeGen)(pattern, flags);
@@ -425,19 +448,52 @@ if (isSomeString!(S))
}
+private struct CTRegexWrapper(Char)
+{
+ private immutable(Regex!Char)* re;
+
+ // allow code that expects mutable Regex to still work
+ // we stay "logically const"
+ @property @trusted ref getRe() const { return *cast(Regex!Char*) re; }
+ alias getRe this;
+}
+
template ctRegexImpl(alias pattern, string flags=[])
{
import std.regex.internal.backtracking, std.regex.internal.parser;
- enum r = regex(pattern, flags);
+ static immutable r = cast(immutable) regex(pattern, flags);
alias Char = BasicElementOf!(typeof(pattern));
enum source = ctGenRegExCode(r);
- alias Matcher = BacktrackingMatcher!(true);
- @trusted bool func(ref Matcher!Char matcher)
+ @trusted pure bool func(BacktrackingMatcher!Char matcher)
{
debug(std_regex_ctr) pragma(msg, source);
+ cast(void) matcher;
mixin(source);
}
- enum nr = StaticRegex!Char(r, &func);
+ static immutable staticRe =
+ cast(immutable) r.withFactory(new CtfeFactory!(BacktrackingMatcher, Char, func));
+ enum wrapper = CTRegexWrapper!Char(&staticRe);
+}
+
+@safe pure unittest
+{
+ // test compat for logical const workaround
+ static void test(StaticRegex!char)
+ {
+ }
+ enum re = ctRegex!``;
+ test(re);
+}
+
+@safe pure unittest
+{
+ auto re = ctRegex!`foo`;
+ assert(matchFirst("foo", re));
+
+ // test reassignment
+ re = ctRegex!`bar`;
+ assert(matchFirst("bar", re));
+ assert(!matchFirst("bar", ctRegex!`foo`));
}
/++
@@ -448,108 +504,61 @@ template ctRegexImpl(alias pattern, string flags=[])
Params:
pattern = Regular expression
- flags = The _attributes (g, i, m and x accepted)
+ flags = The _attributes (g, i, m, s and x accepted)
+/
-public enum ctRegex(alias pattern, alias flags=[]) = ctRegexImpl!(pattern, flags).nr;
+public enum ctRegex(alias pattern, alias flags=[]) = ctRegexImpl!(pattern, flags).wrapper;
-enum isRegexFor(RegEx, R) = is(RegEx == Regex!(BasicElementOf!R))
- || is(RegEx == StaticRegex!(BasicElementOf!R));
+enum isRegexFor(RegEx, R) = is(immutable RegEx == immutable Regex!(BasicElementOf!R))
+ || is(RegEx : const(Regex!(BasicElementOf!R)))
+ || is(immutable RegEx == immutable StaticRegex!(BasicElementOf!R));
/++
- $(D Captures) object contains submatches captured during a call
- to $(D match) or iteration over $(D RegexMatch) range.
+ `Captures` object contains submatches captured during a call
+ to `match` or iteration over `RegexMatch` range.
First element of range is the whole match.
+/
-@trusted public struct Captures(R, DIndex = size_t)
+@trusted public struct Captures(R)
if (isSomeString!R)
{//@trusted because of union inside
- alias DataIndex = DIndex;
+ alias DataIndex = size_t;
alias String = R;
+ alias Store = SmallFixedArray!(Group!DataIndex, 3);
private:
import std.conv : text;
+ Store matches;
+ const(NamedGroup)[] _names;
R _input;
int _nMatch;
- enum smallString = 3;
- enum SMALL_MASK = 0x8000_0000, REF_MASK= 0x1FFF_FFFF;
- union
- {
- Group!DataIndex[] big_matches;
- Group!DataIndex[smallString] small_matches;
- }
uint _f, _b;
- uint _refcount; // ref count or SMALL MASK + num groups
- NamedGroup[] _names;
- this()(R input, uint n, NamedGroup[] named)
+ this(R input, uint n, const(NamedGroup)[] named)
{
_input = input;
_names = named;
- newMatches(n);
+ matches = Store(n);
_b = n;
_f = 0;
}
- this(alias Engine)(ref RegexMatch!(R,Engine) rmatch)
+ this(ref RegexMatch!R rmatch)
{
_input = rmatch._input;
- _names = rmatch._engine.re.dict;
- immutable n = rmatch._engine.re.ngroup;
- newMatches(n);
+ _names = rmatch._engine.pattern.dict;
+ immutable n = rmatch._engine.pattern.ngroup;
+ matches = Store(n);
_b = n;
_f = 0;
}
- @property inout(Group!DataIndex[]) matches() inout
+ inout(R) getMatch(size_t index) inout
{
- return (_refcount & SMALL_MASK) ? small_matches[0 .. _refcount & 0xFF] : big_matches;
- }
-
- void newMatches(uint n)
- {
- import core.stdc.stdlib : calloc;
- import std.exception : enforce;
- if (n > smallString)
- {
- auto p = cast(Group!DataIndex*) enforce(
- calloc(Group!DataIndex.sizeof,n),
- "Failed to allocate Captures struct"
- );
- big_matches = p[0 .. n];
- _refcount = 1;
- }
- else
- {
- _refcount = SMALL_MASK | n;
- }
- }
-
- bool unique()
- {
- return (_refcount & SMALL_MASK) || _refcount == 1;
+ auto m = &matches[index];
+ return *m ? _input[m.begin .. m.end] : null;
}
public:
- this(this)
- {
- if (!(_refcount & SMALL_MASK))
- {
- _refcount++;
- }
- }
- ~this()
- {
- import core.stdc.stdlib : free;
- if (!(_refcount & SMALL_MASK))
- {
- if (--_refcount == 0)
- {
- free(big_matches.ptr);
- big_matches = null;
- }
- }
- }
///Slice of input prior to the match.
@property R pre()
{
@@ -573,14 +582,14 @@ public:
@property R front()
{
assert(_nMatch, "attempted to get front of an empty match");
- return _input[matches[_f].begin .. matches[_f].end];
+ return getMatch(_f);
}
///ditto
@property R back()
{
assert(_nMatch, "attempted to get back of an empty match");
- return _input[matches[_b - 1].begin .. matches[_b - 1].end];
+ return getMatch(_b - 1);
}
///ditto
@@ -604,9 +613,7 @@ public:
inout(R) opIndex()(size_t i) inout
{
assert(_f + i < _b,text("requested submatch number ", i," is out of range"));
- assert(matches[_f + i].begin <= matches[_f + i].end,
- text("wrong match: ", matches[_f + i].begin, "..", matches[_f + i].end));
- return _input[matches[_f + i].begin .. matches[_f + i].end];
+ return getMatch(_f + i);
}
/++
@@ -656,7 +663,7 @@ public:
if (isSomeString!String)
{
size_t index = lookupNamedGroup(_names, i);
- return _input[matches[index].begin .. matches[index].end];
+ return getMatch(index);
}
///Number of matches in this object.
@@ -686,65 +693,69 @@ public:
assert(c.empty);
assert(!matchFirst("nothing", "something"));
+
+ // Captures that are not matched will be null.
+ c = matchFirst("ac", regex(`a(b)?c`));
+ assert(c);
+ assert(!c[1]);
+}
+
+@system unittest
+{
+ Captures!string c;
+ string s = "abc";
+ assert(cast(bool)(c = matchFirst(s, regex("d")))
+ || cast(bool)(c = matchFirst(s, regex("a"))));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19979
+@system unittest
+{
+ auto c = matchFirst("bad", regex(`(^)(not )?bad($)`));
+ assert(c[0] && c[0].length == "bad".length);
+ assert(c[1] && !c[1].length);
+ assert(!c[2]);
+ assert(c[3] && !c[3].length);
}
/++
- A regex engine state, as returned by $(D match) family of functions.
+ A regex engine state, as returned by `match` family of functions.
Effectively it's a forward range of Captures!R, produced
by lazily searching for matches in a given input.
-
- $(D alias Engine) specifies an engine type to use during matching,
- and is automatically deduced in a call to $(D match)/$(D bmatch).
+/
-@trusted public struct RegexMatch(R, alias Engine = ThompsonMatcher)
+@trusted public struct RegexMatch(R)
if (isSomeString!R)
{
+ import std.typecons : Rebindable;
private:
- import core.stdc.stdlib : malloc, free;
alias Char = BasicElementOf!R;
- alias EngineType = Engine!Char;
- EngineType _engine;
+ Matcher!Char _engine;
+ Rebindable!(const MatcherFactory!Char) _factory;
R _input;
- Captures!(R,EngineType.DataIndex) _captures;
- void[] _memory;//is ref-counted
+ Captures!R _captures;
this(RegEx)(R input, RegEx prog)
{
import std.exception : enforce;
_input = input;
- immutable size = EngineType.initialMemory(prog)+size_t.sizeof;
- _memory = (enforce(malloc(size), "malloc failed")[0 .. size]);
- scope(failure) free(_memory.ptr);
- *cast(size_t*)_memory.ptr = 1;
- _engine = EngineType(prog, Input!Char(input), _memory[size_t.sizeof..$]);
- static if (is(RegEx == StaticRegex!(BasicElementOf!R)))
- _engine.nativeFn = prog.nativeFn;
- _captures = Captures!(R,EngineType.DataIndex)(this);
- _captures._nMatch = _engine.match(_captures.matches);
- debug(std_regex_allocation) writefln("RefCount (ctor): %x %d", _memory.ptr, counter);
+ if (prog.factory is null) _factory = defaultFactory!Char(prog);
+ else _factory = prog.factory;
+ _engine = _factory.create(prog, input);
+ assert(_engine.refCount == 1);
+ _captures = Captures!R(this);
+ _captures.matches.mutate((slice) pure { _captures._nMatch = _engine.match(slice); });
}
- @property ref size_t counter(){ return *cast(size_t*)_memory.ptr; }
public:
this(this)
{
- if (_memory.ptr)
- {
- ++counter;
- debug(std_regex_allocation) writefln("RefCount (postblit): %x %d",
- _memory.ptr, *cast(size_t*)_memory.ptr);
- }
+ if (_engine) _factory.incRef(_engine);
}
~this()
{
- if (_memory.ptr && --*cast(size_t*)_memory.ptr == 0)
- {
- debug(std_regex_allocation) writefln("RefCount (dtor): %x %d",
- _memory.ptr, *cast(size_t*)_memory.ptr);
- free(cast(void*)_memory.ptr);
- }
+ if (_engine) _factory.decRef(_engine);
}
///Shorthands for front.pre, front.post, front.hit.
@@ -777,7 +788,7 @@ public:
assert(m.empty);
---
+/
- @property auto front()
+ @property inout(Captures!R) front() inout
{
return _captures;
}
@@ -786,21 +797,15 @@ public:
void popFront()
{
import std.exception : enforce;
- if (counter != 1)
- {//do cow magic first
- counter--;//we abandon this reference
- immutable size = EngineType.initialMemory(_engine.re)+size_t.sizeof;
- _memory = (enforce(malloc(size), "malloc failed")[0 .. size]);
- _engine = _engine.dupTo(_memory[size_t.sizeof .. size]);
- counter = 1;//points to new chunk
- }
-
- if (!_captures.unique)
+ // CoW - if refCount is not 1, we are aliased by somebody else
+ if (_engine.refCount != 1)
{
- // has external references - allocate new space
- _captures.newMatches(_engine.re.ngroup);
+ // we create a new engine & abandon this reference
+ auto old = _engine;
+ _engine = _factory.dup(old, _input);
+ _factory.decRef(old);
}
- _captures._nMatch = _engine.match(_captures.matches);
+ _captures.matches.mutate((slice) { _captures._nMatch = _engine.match(slice); });
}
///ditto
@@ -813,42 +818,79 @@ public:
T opCast(T:bool)(){ return !empty; }
/// Same as .front, provided for compatibility with original std.regex.
- @property auto captures() inout { return _captures; }
-
+ @property inout(Captures!R) captures() inout { return _captures; }
}
-private @trusted auto matchOnce(alias Engine, RegEx, R)(R input, RegEx re)
+private auto matchOnceImpl(RegEx, R)(R input, const auto ref RegEx prog) @trusted
{
- import core.stdc.stdlib : malloc, free;
- import std.exception : enforce;
alias Char = BasicElementOf!R;
- alias EngineType = Engine!Char;
-
- size_t size = EngineType.initialMemory(re);
- void[] memory = enforce(malloc(size), "malloc failed")[0 .. size];
- scope(exit) free(memory.ptr);
- auto captures = Captures!(R, EngineType.DataIndex)(input, re.ngroup, re.dict);
- auto engine = EngineType(re, Input!Char(input), memory);
- static if (is(RegEx == StaticRegex!(BasicElementOf!R)))
- engine.nativeFn = re.nativeFn;
- captures._nMatch = engine.match(captures.matches);
+ static struct Key
+ {
+ immutable(Char)[] pattern;
+ uint flags;
+ }
+ static Key cacheKey = Key("", -1);
+ static Matcher!Char cache;
+ auto factory = prog.factory is null ? defaultFactory!Char(prog) : prog.factory;
+ auto key = Key(prog.pattern, prog.flags);
+ Matcher!Char engine;
+ if (cacheKey == key)
+ {
+ engine = cache;
+ engine.rearm(input);
+ }
+ else
+ {
+ engine = factory.create(prog, input);
+ if (cache) factory.decRef(cache); // destroy cached engine *after* building a new one
+ cache = engine;
+ cacheKey = key;
+ }
+ auto captures = Captures!R(input, prog.ngroup, prog.dict);
+ captures.matches.mutate((slice) pure { captures._nMatch = engine.match(slice); });
return captures;
}
-private auto matchMany(alias Engine, RegEx, R)(R input, RegEx re)
+// matchOnce is constructed as a safe, pure wrapper over matchOnceImpl. It can be
+// faked as pure because the static mutable variables are used to cache the key and
+// character matcher. The technique used avoids delegates and GC.
+private @safe auto matchOnce(RegEx, R)(R input, const auto ref RegEx prog) pure
+{
+ static auto impl(R input, const ref RegEx prog)
+ {
+ return matchOnceImpl(input, prog);
+ }
+
+ static @trusted auto pureImpl(R input, const ref RegEx prog)
+ {
+ auto p = assumePureFunction(&impl);
+ return p(input, prog);
+ }
+
+ return pureImpl(input, prog);
+}
+
+private auto matchMany(RegEx, R)(R input, auto ref RegEx re) @safe
{
- re.flags |= RegexOption.global;
- return RegexMatch!(R, Engine)(input, re);
+ return RegexMatch!R(input, re.withFlags(re.flags | RegexOption.global));
}
@system unittest
{
//sanity checks for new API
auto re = regex("abc");
- assert(!"abc".matchOnce!(ThompsonMatcher)(re).empty);
- assert("abc".matchOnce!(ThompsonMatcher)(re)[0] == "abc");
+ assert(!"abc".matchOnce(re).empty);
+ assert("abc".matchOnce(re)[0] == "abc");
}
+// https://issues.dlang.org/show_bug.cgi?id=18135
+@system unittest
+{
+ static struct MapResult { RegexMatch!string m; }
+ MapResult m;
+ m = MapResult();
+ assert(m == m);
+}
private enum isReplaceFunctor(alias fun, R) =
__traits(compiles, (Captures!R c) { fun(c); });
@@ -922,7 +964,7 @@ if (isSomeString!R && isRegexFor!(RegEx, R))
/++
- Start matching $(D input) to regex pattern $(D re),
+ Start matching `input` to regex pattern `re`,
using Thompson NFA matching scheme.
The use of this function is $(RED discouraged) - use either of
@@ -934,37 +976,28 @@ if (isSomeString!R && isRegexFor!(RegEx, R))
matching scheme to use depends highly on the pattern kind and
can done automatically on case by case basis.
- Returns: a $(D RegexMatch) object holding engine state after first match.
+ Returns: a `RegexMatch` object holding engine state after first match.
+/
public auto match(R, RegEx)(R input, RegEx re)
-if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R)))
+if (isSomeString!R && isRegexFor!(RegEx,R))
{
- import std.regex.internal.thompson : ThompsonMatcher;
- return RegexMatch!(Unqual!(typeof(input)),ThompsonMatcher)(input, re);
+ return RegexMatch!(Unqual!(typeof(input)))(input, re);
}
///ditto
public auto match(R, String)(R input, String re)
if (isSomeString!R && isSomeString!String)
{
- import std.regex.internal.thompson : ThompsonMatcher;
- return RegexMatch!(Unqual!(typeof(input)),ThompsonMatcher)(input, regex(re));
-}
-
-public auto match(R, RegEx)(R input, RegEx re)
-if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
-{
- import std.regex.internal.backtracking : BacktrackingMatcher;
- return RegexMatch!(Unqual!(typeof(input)),BacktrackingMatcher!true)(input, re);
+ return RegexMatch!(Unqual!(typeof(input)))(input, regex(re));
}
/++
- Find the first (leftmost) slice of the $(D input) that
- matches the pattern $(D re). This function picks the most suitable
+ Find the first (leftmost) slice of the `input` that
+ matches the pattern `re`. This function picks the most suitable
regular expression engine depending on the pattern properties.
- $(D re) parameter can be one of three types:
+ `re` parameter can be one of three types:
$(UL
$(LI Plain string(s), in which case it's compiled to bytecode before matching. )
$(LI Regex!char (wchar/dchar) that contains a pattern in the form of
@@ -978,44 +1011,34 @@ if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
if there was a match, otherwise an empty $(LREF Captures) object.
+/
public auto matchFirst(R, RegEx)(R input, RegEx re)
-if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R)))
+if (isSomeString!R && isRegexFor!(RegEx, R))
{
- import std.regex.internal.thompson : ThompsonMatcher;
- return matchOnce!ThompsonMatcher(input, re);
+ return matchOnce(input, re);
}
///ditto
public auto matchFirst(R, String)(R input, String re)
if (isSomeString!R && isSomeString!String)
{
- import std.regex.internal.thompson : ThompsonMatcher;
- return matchOnce!ThompsonMatcher(input, regex(re));
+ return matchOnce(input, regex(re));
}
///ditto
public auto matchFirst(R, String)(R input, String[] re...)
if (isSomeString!R && isSomeString!String)
{
- import std.regex.internal.thompson : ThompsonMatcher;
- return matchOnce!ThompsonMatcher(input, regex(re));
-}
-
-public auto matchFirst(R, RegEx)(R input, RegEx re)
-if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
-{
- import std.regex.internal.backtracking : BacktrackingMatcher;
- return matchOnce!(BacktrackingMatcher!true)(input, re);
+ return matchOnce(input, regex(re));
}
/++
- Initiate a search for all non-overlapping matches to the pattern $(D re)
- in the given $(D input). The result is a lazy range of matches generated
+ Initiate a search for all non-overlapping matches to the pattern `re`
+ in the given `input`. The result is a lazy range of matches generated
as they are encountered in the input going left to right.
This function picks the most suitable regular expression engine
depending on the pattern properties.
- $(D re) parameter can be one of three types:
+ `re` parameter can be one of three types:
$(UL
$(LI Plain string(s), in which case it's compiled to bytecode before matching. )
$(LI Regex!char (wchar/dchar) that contains a pattern in the form of
@@ -1029,33 +1052,23 @@ if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
after the first match was found or an empty one if not present.
+/
public auto matchAll(R, RegEx)(R input, RegEx re)
-if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R)))
+if (isSomeString!R && isRegexFor!(RegEx, R))
{
- import std.regex.internal.thompson : ThompsonMatcher;
- return matchMany!ThompsonMatcher(input, re);
+ return matchMany(input, re);
}
///ditto
public auto matchAll(R, String)(R input, String re)
if (isSomeString!R && isSomeString!String)
{
- import std.regex.internal.thompson : ThompsonMatcher;
- return matchMany!ThompsonMatcher(input, regex(re));
+ return matchMany(input, regex(re));
}
///ditto
public auto matchAll(R, String)(R input, String[] re...)
if (isSomeString!R && isSomeString!String)
{
- import std.regex.internal.thompson : ThompsonMatcher;
- return matchMany!ThompsonMatcher(input, regex(re));
-}
-
-public auto matchAll(R, RegEx)(R input, RegEx re)
-if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
-{
- import std.regex.internal.backtracking : BacktrackingMatcher;
- return matchMany!(BacktrackingMatcher!true)(input, re);
+ return matchMany(input, regex(re));
}
// another set of tests just to cover the new API
@@ -1065,8 +1078,8 @@ if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
import std.algorithm.iteration : map;
import std.conv : to;
- foreach (String; AliasSeq!(string, wstring, const(dchar)[]))
- {
+ static foreach (String; AliasSeq!(string, wstring, const(dchar)[]))
+ {{
auto str1 = "blah-bleh".to!String();
auto pat1 = "bl[ae]h".to!String();
auto mf = matchFirst(str1, pat1);
@@ -1097,11 +1110,11 @@ if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
assert(cmAll.front.equal(cmf));
cmAll.popFront();
assert(cmAll.front.equal(["6/1", "6", "1"].map!(to!String)()));
- }
+ }}
}
/++
- Start matching of $(D input) to regex pattern $(D re),
+ Start matching of `input` to regex pattern `re`,
using traditional $(LINK2 https://en.wikipedia.org/wiki/Backtracking,
backtracking) matching scheme.
@@ -1114,30 +1127,21 @@ if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
matching scheme to use depends highly on the pattern kind and
can done automatically on case by case basis.
- Returns: a $(D RegexMatch) object holding engine
+ Returns: a `RegexMatch` object holding engine
state after first match.
+/
public auto bmatch(R, RegEx)(R input, RegEx re)
-if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R)))
+if (isSomeString!R && isRegexFor!(RegEx, R))
{
- import std.regex.internal.backtracking : BacktrackingMatcher;
- return RegexMatch!(Unqual!(typeof(input)), BacktrackingMatcher!false)(input, re);
+ return RegexMatch!(Unqual!(typeof(input)))(input, re);
}
///ditto
public auto bmatch(R, String)(R input, String re)
if (isSomeString!R && isSomeString!String)
{
- import std.regex.internal.backtracking : BacktrackingMatcher;
- return RegexMatch!(Unqual!(typeof(input)), BacktrackingMatcher!false)(input, regex(re));
-}
-
-public auto bmatch(R, RegEx)(R input, RegEx re)
-if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
-{
- import std.regex.internal.backtracking : BacktrackingMatcher;
- return RegexMatch!(Unqual!(typeof(input)),BacktrackingMatcher!true)(input, re);
+ return RegexMatch!(Unqual!(typeof(input)))(input, regex(re));
}
// produces replacement string from format using captures for substitution
@@ -1215,8 +1219,8 @@ L_Replace_Loop:
}
/++
- Construct a new string from $(D input) by replacing the first match with
- a string generated from it according to the $(D format) specifier.
+ Construct a new string from `input` by replacing the first match with
+ a string generated from it according to the `format` specifier.
To replace all matches use $(LREF replaceAll).
@@ -1244,18 +1248,18 @@ if (isSomeString!R && is(C : dchar) && isRegexFor!(RegEx, R))
/++
This is a general replacement tool that construct a new string by replacing
- matches of pattern $(D re) in the $(D input). Unlike the other overload
+ matches of pattern `re` in the `input`. Unlike the other overload
there is no format string instead captures are passed to
- to a user-defined functor $(D fun) that returns a new string
+ to a user-defined functor `fun` that returns a new string
to use as replacement.
- This version replaces the first match in $(D input),
+ This version replaces the first match in `input`,
see $(LREF replaceAll) to replace the all of the matches.
Returns:
- A new string of the same type as $(D input) with all matches
- replaced by return values of $(D fun). If no matches found
- returns the $(D input) itself.
+ A new string of the same type as `input` with all matches
+ replaced by return values of `fun`. If no matches found
+ returns the `input` itself.
+/
public R replaceFirst(alias fun, R, RegEx)(R input, RegEx re)
if (isSomeString!R && isRegexFor!(RegEx, R))
@@ -1275,11 +1279,11 @@ if (isSomeString!R && isRegexFor!(RegEx, R))
/++
A variation on $(LREF replaceFirst) that instead of allocating a new string
- on each call outputs the result piece-wise to the $(D sink). In particular
+ on each call outputs the result piece-wise to the `sink`. In particular
this enables efficient construction of a final output incrementally.
Like in $(LREF replaceFirst) family of functions there is an overload
- for the substitution guided by the $(D format) string
+ for the substitution guided by the `format` string
and the one with the user defined callback.
+/
public @trusted void replaceFirstInto(Sink, R, C, RegEx)
@@ -1331,9 +1335,9 @@ if (isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R))
}
/++
- Construct a new string from $(D input) by replacing all of the
- fragments that match a pattern $(D re) with a string generated
- from the match according to the $(D format) specifier.
+ Construct a new string from `input` by replacing all of the
+ fragments that match a pattern `re` with a string generated
+ from the match according to the `format` specifier.
To replace only the first match use $(LREF replaceFirst).
@@ -1344,7 +1348,7 @@ if (isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R))
see $(S_LINK Replace _format string, the _format string).
Returns:
- A string of the same type as $(D input) with the all
+ A string of the same type as `input` with the all
of the matches (if any) replaced.
If no match is found returns the input string itself.
+/
@@ -1364,18 +1368,18 @@ if (isSomeString!R && is(C : dchar) && isRegexFor!(RegEx, R))
/++
This is a general replacement tool that construct a new string by replacing
- matches of pattern $(D re) in the $(D input). Unlike the other overload
+ matches of pattern `re` in the `input`. Unlike the other overload
there is no format string instead captures are passed to
- to a user-defined functor $(D fun) that returns a new string
+ to a user-defined functor `fun` that returns a new string
to use as replacement.
- This version replaces all of the matches found in $(D input),
+ This version replaces all of the matches found in `input`,
see $(LREF replaceFirst) to replace the first match only.
Returns:
- A new string of the same type as $(D input) with all matches
- replaced by return values of $(D fun). If no matches found
- returns the $(D input) itself.
+ A new string of the same type as `input` with all matches
+ replaced by return values of `fun`. If no matches found
+ returns the `input` itself.
Params:
input = string to search
@@ -1404,7 +1408,7 @@ if (isSomeString!R && isRegexFor!(RegEx, R))
/++
A variation on $(LREF replaceAll) that instead of allocating a new string
- on each call outputs the result piece-wise to the $(D sink). In particular
+ on each call outputs the result piece-wise to the `sink`. In particular
this enables efficient construction of a final output incrementally.
As with $(LREF replaceAll) there are 2 overloads - one with a format string,
@@ -1450,8 +1454,8 @@ if (isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R))
import std.array : appender;
import std.conv;
// try and check first/all simple substitution
- foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {{
S s1 = "curt trial".to!S();
S s2 = "round dome".to!S();
S t1F = "court trial".to!S();
@@ -1482,11 +1486,11 @@ if (isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R))
assert(sink.data == t1F~t2F~t1A);
replaceAllInto(sink, s2, re2, "ho");
assert(sink.data == t1F~t2F~t1A~t2A);
- }
+ }}
}
/++
- Old API for replacement, operation depends on flags of pattern $(D re).
+ Old API for replacement, operation depends on flags of pattern `re`.
With "g" flag it performs the equivalent of $(LREF replaceAll) otherwise it
works the same as $(LREF replaceFirst).
@@ -1530,7 +1534,7 @@ private:
@trusted this(Range input, RegEx separator)
{//@@@BUG@@@ generated opAssign of RegexMatch is not @trusted
_input = input;
- separator.flags |= RegexOption.global;
+ const re = separator.withFlags(separator.flags | RegexOption.global);
if (_input.empty)
{
//there is nothing to match at all, make _offset > 0
@@ -1538,7 +1542,7 @@ private:
}
else
{
- _match = Rx(_input, separator);
+ _match = Rx(_input, re);
static if (keepSeparators)
if (_match.pre.empty)
@@ -1659,7 +1663,7 @@ if (
.equal([",", "1", ",", "2", ",", "3"]));
}
-///An eager version of $(D splitter) that creates an array with splitted slices of $(D input).
+///An eager version of `splitter` that creates an array with splitted slices of `input`.
public @trusted String[] split(String, RegEx)(String input, RegEx rx)
if (isSomeString!String && isRegexFor!(RegEx, String))
{
@@ -1725,11 +1729,11 @@ auto escaper(Range)(Range r)
{
import std.algorithm.comparison;
import std.conv;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto s = "^".to!S;
assert(s.escaper.equal(`\^`));
auto s2 = "";
assert(s2.escaper.equal(""));
- }
+ }}
}
diff --git a/libphobos/src/std/signals.d b/libphobos/src/std/signals.d
index 071adcabb1a..e5dc67eb83d 100644
--- a/libphobos/src/std/signals.d
+++ b/libphobos/src/std/signals.d
@@ -37,25 +37,25 @@
* $(LINK2 http://www.digitalmars.com/d/archives/16368.html, signals and slots)$(BR)
*
* Bugs:
- * Slots can only be delegates formed from class objects or
- * interfaces to class objects. If a delegate to something else
+ * $(RED Slots can only be delegates referring directly to
+ * class or interface member functions. If a delegate to something else
* is passed to connect(), such as a struct member function,
- * a nested function or a COM interface, undefined behavior
- * will result.
+ * a nested function, a COM interface, a closure, undefined behavior
+ * will result.)
*
* Not safe for multiple threads operating on the same signals
* or slots.
* Macros:
* SIGNALS=signals
*
- * Copyright: Copyright Digital Mars 2000 - 2009.
+ * Copyright: Copyright The D Language Foundation 2000 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright)
- * Source: $(PHOBOSSRC std/_signals.d)
+ * Source: $(PHOBOSSRC std/signals.d)
*
* $(SCRIPT inhibitQuickIndex = 1;)
*/
-/* Copyright Digital Mars 2000 - 2009.
+/* Copyright The D Language Foundation 2000 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -93,7 +93,8 @@ mixin template Signal(T1...)
* The delegate must be to an instance of a class or an interface
* to a class instance.
* Delegates to struct instances or nested functions must not be
- * used as slots.
+ * used as slots. This applies even if the nested function does not access
+ * it's parent function variables.
*/
alias slot_t = void delegate(T1);
@@ -221,6 +222,17 @@ mixin template Signal(T1...)
}
}
+ /***
+ * Disconnect all the slots.
+ */
+ final void disconnectAll()
+ {
+ debug (signal) writefln("Signal.disconnectAll");
+ __dtor();
+ slots_idx = 0;
+ status = ST.idle;
+ }
+
/* **
* Special function called when o is destroyed.
* It causes any slots dependent on o to be removed from the list
@@ -228,7 +240,8 @@ mixin template Signal(T1...)
*/
final void unhook(Object o)
in { assert( status == ST.idle ); }
- body {
+ do
+ {
debug (signal) writefln("Signal.unhook(o = %s)", cast(void*) o);
for (size_t i = 0; i < slots_idx; )
{
@@ -639,7 +652,7 @@ void linkin() { }
a.emit(); // should not raise segfault since &o.watch2 is no longer connected
}
-version (none) // Disabled because of dmd @@@BUG5028@@@
+version (none) // Disabled because of https://issues.dlang.org/show_bug.cgi?id=5028
@system unittest
{
class A
@@ -706,3 +719,58 @@ version (none) // Disabled because of dmd @@@BUG5028@@@
assert( dot2.value == -22 );
}
+@system unittest
+{
+ import std.signals;
+
+ class Observer
+ { // our slot
+ void watch(string msg, int value)
+ {
+ if (value != 0)
+ {
+ assert(msg == "setting new value");
+ assert(value == 1);
+ }
+ }
+ }
+
+ class Foo
+ {
+ int value() { return _value; }
+
+ int value(int v)
+ {
+ if (v != _value)
+ {
+ _value = v;
+ // call all the connected slots with the parameters
+ emit("setting new value", v);
+ }
+ return v;
+ }
+
+ // Mix in all the code we need to make Foo into a signal
+ mixin Signal!(string, int);
+
+ private :
+ int _value;
+ }
+
+ Foo a = new Foo;
+ Observer o = new Observer;
+ auto o2 = new Observer;
+
+ a.value = 3; // should not call o.watch()
+ a.connect(&o.watch); // o.watch is the slot
+ a.connect(&o2.watch);
+ a.value = 1; // should call o.watch()
+ a.disconnectAll();
+ a.value = 5; // so should not call o.watch()
+ a.connect(&o.watch); // connect again
+ a.connect(&o2.watch);
+ a.value = 1; // should call o.watch()
+ destroy(o); // destroying o should automatically disconnect it
+ destroy(o2);
+ a.value = 7; // should not call o.watch()
+}
diff --git a/libphobos/src/std/socket.d b/libphobos/src/std/socket.d
index d7de153063f..be0aeba5ca8 100644
--- a/libphobos/src/std/socket.d
+++ b/libphobos/src/std/socket.d
@@ -1,32 +1,12 @@
// Written in the D programming language
+// NOTE: When working on this module, be sure to run tests with -debug=std_socket
+// E.g.: dmd -version=StdUnittest -debug=std_socket -unittest -main -run socket
+// This will enable some tests which are too slow or flaky to run as part of CI.
+
/*
Copyright (C) 2004-2011 Christopher E. Miller
- Boost Software License - Version 1.0 - August 17th, 2003
-
- Permission is hereby granted, free of charge, to any person or organization
- obtaining a copy of the software and accompanying documentation covered by
- this license (the "Software") to use, reproduce, display, distribute,
- execute, and transmit the Software, and to prepare derivative works of the
- Software, and to permit third-parties to whom the Software is furnished to
- do so, all subject to the following:
-
- The copyright notices in the Software and this entire statement, including
- the above license grant, this restriction and the following disclaimer,
- must be included in all copies of the Software, in whole or in part, and
- all derivative works of the Software, unless such copies or derivative
- works are solely in the form of machine-executable object code generated by
- a source language processor.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
- SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
- FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
socket.d 1.4
Jan 2011
@@ -39,7 +19,7 @@
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Christopher E. Miller, $(HTTP klickverbot.at, David Nadlinger),
* $(HTTP thecybershadow.net, Vladimir Panteleev)
- * Source: $(PHOBOSSRC std/_socket.d)
+ * Source: $(PHOBOSSRC std/socket.d)
*/
module std.socket;
@@ -52,6 +32,12 @@ import std.exception;
import std.internal.cstring;
+version (iOS)
+ version = iOSDerived;
+else version (TVOS)
+ version = iOSDerived;
+else version (WatchOS)
+ version = iOSDerived;
@safe:
@@ -60,7 +46,7 @@ version (Windows)
pragma (lib, "ws2_32.lib");
pragma (lib, "wsock32.lib");
- import core.sys.windows.windows, std.windows.syserror;
+ import core.sys.windows.winbase, std.windows.syserror;
public import core.sys.windows.winsock2;
private alias _ctimeval = core.sys.windows.winsock2.timeval;
private alias _clinger = core.sys.windows.winsock2.linger;
@@ -100,7 +86,7 @@ else version (Posix)
import core.stdc.errno;
- enum socket_t : int32_t { init = -1 }
+ enum socket_t : int32_t { _init = -1 }
private const int _SOCKET_ERROR = -1;
private enum : int
@@ -117,30 +103,28 @@ else version (Posix)
}
else
{
- static assert(0); // No socket support yet.
+ static assert(0, "No socket support for this platform yet.");
}
-version (unittest)
+version (StdUnittest)
{
- static assert(is(uint32_t == uint));
- static assert(is(uint16_t == ushort));
-
- import std.stdio : writefln;
-
// Print a message on exception instead of failing the unittest.
private void softUnittest(void delegate() @safe test, int line = __LINE__) @trusted
{
- try
+ debug (std_socket)
test();
- catch (Throwable e)
+ else
{
- writefln(" --- std.socket(%d) test fails depending on environment ---", line);
- writefln(" (%s)", e);
+ import std.stdio : writefln;
+ try
+ test();
+ catch (Throwable e)
+ writefln("Ignoring std.socket(%d) test failure (likely caused by flaky environment): %s", line, e.msg);
}
}
}
-/// Base exception thrown by $(D std.socket).
+/// Base exception thrown by `std.socket`.
class SocketException: Exception
{
mixin basicExceptionCtors;
@@ -262,17 +246,29 @@ class SocketFeatureException: SocketException
/**
* Returns:
- * $(D true) if the last socket operation failed because the socket
- * was in non-blocking mode and the operation would have blocked.
+ * `true` if the last socket operation failed because the socket
+ * was in non-blocking mode and the operation would have blocked,
+ * or if the socket is in blocking mode and set a SNDTIMEO or RCVTIMEO,
+ * and the operation timed out.
*/
bool wouldHaveBlocked() nothrow @nogc
{
version (Windows)
- return _lasterr() == WSAEWOULDBLOCK;
+ return _lasterr() == WSAEWOULDBLOCK || _lasterr() == WSAETIMEDOUT;
else version (Posix)
return _lasterr() == EAGAIN;
else
- static assert(0);
+ static assert(0, "No socket support for this platform yet.");
+}
+
+@safe unittest
+{
+ auto sockets = socketPair();
+ auto s = sockets[0];
+ s.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"msecs"(10));
+ ubyte[] buffer = new ubyte[](16);
+ auto rec = s.receive(buffer);
+ assert(rec == -1 && wouldHaveBlocked());
}
@@ -329,7 +325,7 @@ shared static ~this() @system nothrow @nogc
/**
* The communication domain used to resolve an address.
*/
-enum AddressFamily: int
+enum AddressFamily: ushort
{
UNSPEC = AF_UNSPEC, /// Unspecified address family
UNIX = AF_UNIX, /// Local communication
@@ -372,7 +368,7 @@ enum ProtocolType: int
/**
- * $(D Protocol) is a class for retrieving protocol information.
+ * `Protocol` is a class for retrieving protocol information.
*
* Example:
* ---
@@ -424,7 +420,7 @@ class Protocol
}
/** Returns: false on failure */
- bool getProtocolByName(in char[] name) @trusted nothrow
+ bool getProtocolByName(scope const(char)[] name) @trusted nothrow
{
protoent* proto;
proto = getprotobyname(name.tempCString());
@@ -454,6 +450,7 @@ class Protocol
version (CRuntime_Bionic) {} else
@safe unittest
{
+ // import std.stdio : writefln;
softUnittest({
Protocol proto = new Protocol;
assert(proto.getProtocolByType(ProtocolType.TCP));
@@ -470,7 +467,7 @@ version (CRuntime_Bionic) {} else
/**
- * $(D Service) is a class for retrieving service information.
+ * `Service` is a class for retrieving service information.
*
* Example:
* ---
@@ -529,7 +526,7 @@ class Service
* If a protocol name is omitted, any protocol will be matched.
* Returns: false on failure.
*/
- bool getServiceByName(in char[] name, in char[] protocolName = null) @trusted nothrow
+ bool getServiceByName(scope const(char)[] name, scope const(char)[] protocolName = null) @trusted nothrow
{
servent* serv;
serv = getservbyname(name.tempCString(), protocolName.tempCString());
@@ -541,7 +538,7 @@ class Service
/// ditto
- bool getServiceByPort(ushort port, in char[] protocolName = null) @trusted nothrow
+ bool getServiceByPort(ushort port, scope const(char)[] protocolName = null) @trusted nothrow
{
servent* serv;
serv = getservbyport(port, protocolName.tempCString());
@@ -555,6 +552,7 @@ class Service
@safe unittest
{
+ import std.stdio : writefln;
softUnittest({
Service serv = new Service;
if (serv.getServiceByName("epmap", "tcp"))
@@ -710,7 +708,7 @@ class InternetHost
* Resolve host name.
* Returns: false if unable to resolve.
*/
- bool getHostByName(in char[] name) @trusted
+ bool getHostByName(scope const(char)[] name) @trusted
{
static if (is(typeof(gethostbyname_r)))
{
@@ -760,7 +758,7 @@ class InternetHost
* dotted-decimal form $(I a.b.c.d).
* Returns: false if unable to resolve.
*/
- bool getHostByAddr(in char[] addr) @trusted
+ bool getHostByAddr(scope const(char)[] addr) @trusted
{
return getHost!q{
auto x = inet_addr(param.tempCString());
@@ -799,30 +797,30 @@ class InternetHost
}
-/// Holds information about a socket _address retrieved by $(D getAddressInfo).
+/// Holds information about a socket _address retrieved by `getAddressInfo`.
struct AddressInfo
{
AddressFamily family; /// Address _family
SocketType type; /// Socket _type
ProtocolType protocol; /// Protocol
Address address; /// Socket _address
- string canonicalName; /// Canonical name, when $(D AddressInfoFlags.CANONNAME) is used.
+ string canonicalName; /// Canonical name, when `AddressInfoFlags.CANONNAME` is used.
}
/**
* A subset of flags supported on all platforms with getaddrinfo.
- * Specifies option flags for $(D getAddressInfo).
+ * Specifies option flags for `getAddressInfo`.
*/
enum AddressInfoFlags: int
{
- /// The resulting addresses will be used in a call to $(D Socket.bind).
+ /// The resulting addresses will be used in a call to `Socket.bind`.
PASSIVE = AI_PASSIVE,
- /// The canonical name is returned in $(D canonicalName) member in the first $(D AddressInfo).
+ /// The canonical name is returned in `canonicalName` member in the first `AddressInfo`.
CANONNAME = AI_CANONNAME,
/**
- * The $(D node) parameter passed to $(D getAddressInfo) must be a numeric string.
+ * The `node` parameter passed to `getAddressInfo` must be a numeric string.
* This will suppress any potentially lengthy network host address lookups.
*/
NUMERICHOST = AI_NUMERICHOST,
@@ -849,21 +847,21 @@ private string formatGaiError(int err) @trusted
/**
* Provides _protocol-independent translation from host names to socket
* addresses. If advanced functionality is not required, consider using
- * $(D getAddress) for compatibility with older systems.
+ * `getAddress` for compatibility with older systems.
*
- * Returns: Array with one $(D AddressInfo) per socket address.
+ * Returns: Array with one `AddressInfo` per socket address.
*
- * Throws: $(D SocketOSException) on failure, or $(D SocketFeatureException)
+ * Throws: `SocketOSException` on failure, or `SocketFeatureException`
* if this functionality is not available on the current system.
*
* Params:
* node = string containing host name or numeric address
* options = optional additional parameters, identified by type:
- * $(UL $(LI $(D string) - service name or port number)
- * $(LI $(D AddressInfoFlags) - option flags)
- * $(LI $(D AddressFamily) - address family to filter by)
- * $(LI $(D SocketType) - socket type to filter by)
- * $(LI $(D ProtocolType) - protocol to filter by))
+ * $(UL $(LI `string` - service name or port number)
+ * $(LI `AddressInfoFlags` - option flags)
+ * $(LI `AddressFamily` - address family to filter by)
+ * $(LI `SocketType` - socket type to filter by)
+ * $(LI `ProtocolType` - protocol to filter by))
*
* Example:
* ---
@@ -898,16 +896,16 @@ private string formatGaiError(int err) @trusted
* AddressFamily.INET6);
* ---
*/
-AddressInfo[] getAddressInfo(T...)(in char[] node, T options)
+AddressInfo[] getAddressInfo(T...)(scope const(char)[] node, scope T options)
{
const(char)[] service = null;
addrinfo hints;
hints.ai_family = AF_UNSPEC;
- foreach (option; options)
+ foreach (i, option; options)
{
static if (is(typeof(option) : const(char)[]))
- service = option;
+ service = options[i];
else
static if (is(typeof(option) == AddressInfoFlags))
hints.ai_flags |= option;
@@ -943,7 +941,7 @@ AddressInfo[] getAddressInfo(T...)(in char[] node, T options)
}), "getAddressInfo breaks @safe");
}
-private AddressInfo[] getAddressInfoImpl(in char[] node, in char[] service, addrinfo* hints) @system
+private AddressInfo[] getAddressInfoImpl(scope const(char)[] node, scope const(char)[] service, addrinfo* hints) @system
{
import std.array : appender;
@@ -1019,7 +1017,7 @@ private AddressInfo[] getAddressInfoImpl(in char[] node, in char[] service, addr
}
-private ushort serviceToPort(in char[] service)
+private ushort serviceToPort(scope const(char)[] service)
{
if (service == "")
return InternetAddress.PORT_ANY;
@@ -1036,12 +1034,12 @@ private ushort serviceToPort(in char[] service)
/**
* Provides _protocol-independent translation from host names to socket
- * addresses. Uses $(D getAddressInfo) if the current system supports it,
- * and $(D InternetHost) otherwise.
+ * addresses. Uses `getAddressInfo` if the current system supports it,
+ * and `InternetHost` otherwise.
*
- * Returns: Array with one $(D Address) instance per socket address.
+ * Returns: Array with one `Address` instance per socket address.
*
- * Throws: $(D SocketOSException) on failure.
+ * Throws: `SocketOSException` on failure.
*
* Example:
* ---
@@ -1056,7 +1054,7 @@ private ushort serviceToPort(in char[] service)
* writefln(" Lookup failed: %s", e.msg);
* ---
*/
-Address[] getAddress(in char[] hostname, in char[] service = null)
+Address[] getAddress(scope const(char)[] hostname, scope const(char)[] service = null)
{
if (getaddrinfoPointer && freeaddrinfoPointer)
{
@@ -1073,7 +1071,7 @@ Address[] getAddress(in char[] hostname, in char[] service = null)
}
/// ditto
-Address[] getAddress(in char[] hostname, ushort port)
+Address[] getAddress(scope const(char)[] hostname, ushort port)
{
if (getaddrinfoPointer && freeaddrinfoPointer)
return getAddress(hostname, to!string(port));
@@ -1115,13 +1113,13 @@ Address[] getAddress(in char[] hostname, ushort port)
/**
* Provides _protocol-independent parsing of network addresses. Does not
- * attempt name resolution. Uses $(D getAddressInfo) with
- * $(D AddressInfoFlags.NUMERICHOST) if the current system supports it, and
- * $(D InternetAddress) otherwise.
+ * attempt name resolution. Uses `getAddressInfo` with
+ * `AddressInfoFlags.NUMERICHOST` if the current system supports it, and
+ * `InternetAddress` otherwise.
*
- * Returns: An $(D Address) instance representing specified address.
+ * Returns: An `Address` instance representing specified address.
*
- * Throws: $(D SocketException) on failure.
+ * Throws: `SocketException` on failure.
*
* Example:
* ---
@@ -1150,7 +1148,7 @@ Address[] getAddress(in char[] hostname, ushort port)
* }
* ---
*/
-Address parseAddress(in char[] hostaddr, in char[] service = null)
+Address parseAddress(scope const(char)[] hostaddr, scope const(char)[] service = null)
{
if (getaddrinfoPointer && freeaddrinfoPointer)
return getAddressInfo(hostaddr, service, AddressInfoFlags.NUMERICHOST)[0].address;
@@ -1159,7 +1157,7 @@ Address parseAddress(in char[] hostaddr, in char[] service = null)
}
/// ditto
-Address parseAddress(in char[] hostaddr, ushort port)
+Address parseAddress(scope const(char)[] hostaddr, ushort port)
{
if (getaddrinfoPointer && freeaddrinfoPointer)
return parseAddress(hostaddr, to!string(port));
@@ -1196,7 +1194,7 @@ Address parseAddress(in char[] hostaddr, ushort port)
/**
- * Class for exceptions thrown from an $(D Address).
+ * Class for exceptions thrown from an `Address`.
*/
class AddressException: SocketOSException
{
@@ -1205,7 +1203,7 @@ class AddressException: SocketOSException
/**
- * $(D Address) is an abstract class for representing a socket addresses.
+ * `Address` is an abstract class for representing a socket addresses.
*
* Example:
* ---
@@ -1230,11 +1228,11 @@ class AddressException: SocketOSException
*/
abstract class Address
{
- /// Returns pointer to underlying $(D sockaddr) structure.
+ /// Returns pointer to underlying `sockaddr` structure.
abstract @property sockaddr* name() pure nothrow @nogc;
abstract @property const(sockaddr)* name() const pure nothrow @nogc; /// ditto
- /// Returns actual size of underlying $(D sockaddr) structure.
+ /// Returns actual size of underlying `sockaddr` structure.
abstract @property socklen_t nameLen() const pure nothrow @nogc;
// Socket.remoteAddress, Socket.localAddress, and Socket.receiveFrom
@@ -1321,7 +1319,7 @@ abstract class Address
/**
* Attempts to retrieve the host address as a human-readable string.
*
- * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
+ * Throws: `AddressException` on failure, or `SocketFeatureException`
* if address retrieval for this address family is not available on the
* current system.
*/
@@ -1333,10 +1331,10 @@ abstract class Address
/**
* Attempts to retrieve the host name as a fully qualified domain name.
*
- * Returns: The FQDN corresponding to this $(D Address), or $(D null) if
+ * Returns: The FQDN corresponding to this `Address`, or `null` if
* the host name did not resolve.
*
- * Throws: $(D AddressException) on error, or $(D SocketFeatureException)
+ * Throws: `AddressException` on error, or `SocketFeatureException`
* if host name lookup for this address family is not available on the
* current system.
*/
@@ -1348,7 +1346,7 @@ abstract class Address
/**
* Attempts to retrieve the numeric port number as a string.
*
- * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
+ * Throws: `AddressException` on failure, or `SocketFeatureException`
* if port number retrieval for this address family is not available on the
* current system.
*/
@@ -1360,7 +1358,7 @@ abstract class Address
/**
* Attempts to retrieve the service name as a string.
*
- * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
+ * Throws: `AddressException` on failure, or `SocketFeatureException`
* if service name lookup for this address family is not available on the
* current system.
*/
@@ -1387,7 +1385,7 @@ abstract class Address
}
/**
- * $(D UnknownAddress) encapsulates an unknown socket address.
+ * `UnknownAddress` encapsulates an unknown socket address.
*/
class UnknownAddress: Address
{
@@ -1396,12 +1394,12 @@ protected:
public:
- override @property sockaddr* name()
+ override @property sockaddr* name() return
{
return &sa;
}
- override @property const(sockaddr)* name() const
+ override @property const(sockaddr)* name() const return
{
return &sa;
}
@@ -1416,7 +1414,7 @@ public:
/**
- * $(D UnknownAddressReference) encapsulates a reference to an arbitrary
+ * `UnknownAddressReference` encapsulates a reference to an arbitrary
* socket address.
*/
class UnknownAddressReference: Address
@@ -1426,14 +1424,14 @@ protected:
socklen_t len;
public:
- /// Constructs an $(D Address) with a reference to the specified $(D sockaddr).
+ /// Constructs an `Address` with a reference to the specified `sockaddr`.
this(sockaddr* sa, socklen_t len) pure nothrow @nogc
{
this.sa = sa;
this.len = len;
}
- /// Constructs an $(D Address) with a copy of the specified $(D sockaddr).
+ /// Constructs an `Address` with a copy of the specified `sockaddr`.
this(const(sockaddr)* sa, socklen_t len) @system pure nothrow
{
this.sa = cast(sockaddr*) (cast(ubyte*) sa)[0 .. len].dup.ptr;
@@ -1459,10 +1457,10 @@ public:
/**
- * $(D InternetAddress) encapsulates an IPv4 (Internet Protocol version 4)
+ * `InternetAddress` encapsulates an IPv4 (Internet Protocol version 4)
* socket address.
*
- * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods
+ * Consider using `getAddress`, `parseAddress` and `Address` methods
* instead of using this class directly.
*/
class InternetAddress: Address
@@ -1477,12 +1475,12 @@ protected:
public:
- override @property sockaddr* name()
+ override @property sockaddr* name() return
{
return cast(sockaddr*)&sin;
}
- override @property const(sockaddr)* name() const
+ override @property const(sockaddr)* name() const return
{
return cast(const(sockaddr)*)&sin;
}
@@ -1511,14 +1509,14 @@ public:
}
/**
- * Construct a new $(D InternetAddress).
+ * Construct a new `InternetAddress`.
* Params:
* addr = an IPv4 address string in the dotted-decimal form a.b.c.d,
- * or a host name which will be resolved using an $(D InternetHost)
+ * or a host name which will be resolved using an `InternetHost`
* object.
- * port = port number, may be $(D PORT_ANY).
+ * port = port number, may be `PORT_ANY`.
*/
- this(in char[] addr, ushort port)
+ this(scope const(char)[] addr, ushort port)
{
uint uiaddr = parse(addr);
if (ADDR_NONE == uiaddr)
@@ -1536,10 +1534,10 @@ public:
}
/**
- * Construct a new $(D InternetAddress).
+ * Construct a new `InternetAddress`.
* Params:
- * addr = (optional) an IPv4 address in host byte order, may be $(D ADDR_ANY).
- * port = port number, may be $(D PORT_ANY).
+ * addr = (optional) an IPv4 address in host byte order, may be `ADDR_ANY`.
+ * port = port number, may be `PORT_ANY`.
*/
this(uint addr, ushort port) pure nothrow @nogc
{
@@ -1557,13 +1555,13 @@ public:
}
/**
- * Construct a new $(D InternetAddress).
+ * Construct a new `InternetAddress`.
* Params:
* addr = A sockaddr_in as obtained from lower-level API calls such as getifaddrs.
*/
this(sockaddr_in addr) pure nothrow @nogc
{
- assert(addr.sin_family == AddressFamily.INET);
+ assert(addr.sin_family == AddressFamily.INET, "Socket address is not of INET family.");
sin = addr;
}
@@ -1582,10 +1580,10 @@ public:
/**
* Attempts to retrieve the host name as a fully qualified domain name.
*
- * Returns: The FQDN corresponding to this $(D InternetAddress), or
- * $(D null) if the host name did not resolve.
+ * Returns: The FQDN corresponding to this `InternetAddress`, or
+ * `null` if the host name did not resolve.
*
- * Throws: $(D AddressException) on error.
+ * Throws: `AddressException` on error.
*/
override string toHostNameString() const
{
@@ -1634,9 +1632,9 @@ public:
* Parse an IPv4 address string in the dotted-decimal form $(I a.b.c.d)
* and return the number.
* Returns: If the string is not a legitimate IPv4 address,
- * $(D ADDR_NONE) is returned.
+ * `ADDR_NONE` is returned.
*/
- static uint parse(in char[] addr) @trusted nothrow
+ static uint parse(scope const(char)[] addr) @trusted nothrow
{
return ntohl(inet_addr(addr.tempCString()));
}
@@ -1693,18 +1691,18 @@ public:
}
});
- version (SlowTests)
+ debug (std_socket)
softUnittest({
// test failing reverse lookup
- const InternetAddress ia = new InternetAddress("127.114.111.120", 80);
+ const InternetAddress ia = new InternetAddress("255.255.255.255", 80);
assert(ia.toHostNameString() is null);
if (getnameinfoPointer)
{
// test failing reverse lookup, via gethostbyaddr
auto getnameinfoPointerBackup = getnameinfoPointer;
- getnameinfoPointer = null;
- scope(exit) getnameinfoPointer = getnameinfoPointerBackup;
+ cast() getnameinfoPointer = null;
+ scope(exit) cast() getnameinfoPointer = getnameinfoPointerBackup;
assert(ia.toHostNameString() is null);
}
@@ -1713,10 +1711,10 @@ public:
/**
- * $(D Internet6Address) encapsulates an IPv6 (Internet Protocol version 6)
+ * `Internet6Address` encapsulates an IPv6 (Internet Protocol version 6)
* socket address.
*
- * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods
+ * Consider using `getAddress`, `parseAddress` and `Address` methods
* instead of using this class directly.
*/
class Internet6Address: Address
@@ -1731,12 +1729,12 @@ protected:
public:
- override @property sockaddr* name()
+ override @property sockaddr* name() return
{
return cast(sockaddr*)&sin6;
}
- override @property const(sockaddr)* name() const
+ override @property const(sockaddr)* name() const return
{
return cast(const(sockaddr)*)&sin6;
}
@@ -1751,16 +1749,19 @@ public:
/// Any IPv6 host address.
static @property ref const(ubyte)[16] ADDR_ANY() pure nothrow @nogc
{
- const(ubyte)[16]* addr;
static if (is(typeof(IN6ADDR_ANY)))
{
- addr = &IN6ADDR_ANY.s6_addr;
- return *addr;
+ version (Windows)
+ {
+ static immutable addr = IN6ADDR_ANY.s6_addr;
+ return addr;
+ }
+ else
+ return IN6ADDR_ANY.s6_addr;
}
else static if (is(typeof(in6addr_any)))
{
- addr = &in6addr_any.s6_addr;
- return *addr;
+ return in6addr_any.s6_addr;
}
else
static assert(0);
@@ -1782,13 +1783,13 @@ public:
}
/**
- * Construct a new $(D Internet6Address).
+ * Construct a new `Internet6Address`.
* Params:
* addr = an IPv6 host address string in the form described in RFC 2373,
- * or a host name which will be resolved using $(D getAddressInfo).
+ * or a host name which will be resolved using `getAddressInfo`.
* service = (optional) service name.
*/
- this(in char[] addr, in char[] service = null) @trusted
+ this(scope const(char)[] addr, scope const(char)[] service = null) @trusted
{
auto results = getAddressInfo(addr, service, AddressFamily.INET6);
assert(results.length && results[0].family == AddressFamily.INET6);
@@ -1796,13 +1797,13 @@ public:
}
/**
- * Construct a new $(D Internet6Address).
+ * Construct a new `Internet6Address`.
* Params:
* addr = an IPv6 host address string in the form described in RFC 2373,
- * or a host name which will be resolved using $(D getAddressInfo).
- * port = port number, may be $(D PORT_ANY).
+ * or a host name which will be resolved using `getAddressInfo`.
+ * port = port number, may be `PORT_ANY`.
*/
- this(in char[] addr, ushort port)
+ this(scope const(char)[] addr, ushort port)
{
if (port == PORT_ANY)
this(addr);
@@ -1811,11 +1812,11 @@ public:
}
/**
- * Construct a new $(D Internet6Address).
+ * Construct a new `Internet6Address`.
* Params:
* addr = (optional) an IPv6 host address in host byte order, or
- * $(D ADDR_ANY).
- * port = port number, may be $(D PORT_ANY).
+ * `ADDR_ANY`.
+ * port = port number, may be `PORT_ANY`.
*/
this(ubyte[16] addr, ushort port) pure nothrow @nogc
{
@@ -1833,7 +1834,7 @@ public:
}
/**
- * Construct a new $(D Internet6Address).
+ * Construct a new `Internet6Address`.
* Params:
* addr = A sockaddr_in6 as obtained from lower-level API calls such as getifaddrs.
*/
@@ -1846,9 +1847,9 @@ public:
/**
* Parse an IPv6 host address string as described in RFC 2373, and return the
* address.
- * Throws: $(D SocketException) on error.
+ * Throws: `SocketException` on error.
*/
- static ubyte[16] parse(in char[] addr) @trusted
+ static ubyte[16] parse(scope const(char)[] addr) @trusted
{
// Although we could use inet_pton here, it's only available on Windows
// versions starting with Vista, so use getAddressInfo with NUMERICHOST
@@ -1895,8 +1896,8 @@ version (StdDdoc)
}
/**
- * $(D UnixAddress) encapsulates an address for a Unix domain socket
- * ($(D AF_UNIX)), i.e. a socket bound to a path name in the file system.
+ * `UnixAddress` encapsulates an address for a Unix domain socket
+ * (`AF_UNIX`), i.e. a socket bound to a path name in the file system.
* Available only on supported systems.
*
* Linux also supports an abstract address namespace, in which addresses
@@ -1917,11 +1918,11 @@ version (StdDdoc)
{
private this() pure nothrow @nogc {}
- /// Construct a new $(D UnixAddress) from the specified path.
- this(in char[] path) { }
+ /// Construct a new `UnixAddress` from the specified path.
+ this(scope const(char)[] path) { }
/**
- * Construct a new $(D UnixAddress).
+ * Construct a new `UnixAddress`.
* Params:
* addr = A sockaddr_un as obtained from lower-level API calls.
*/
@@ -1968,12 +1969,12 @@ static if (is(sockaddr_un))
}
public:
- override @property sockaddr* name()
+ override @property sockaddr* name() return
{
return cast(sockaddr*)&sun;
}
- override @property const(sockaddr)* name() const
+ override @property const(sockaddr)* name() const return
{
return cast(const(sockaddr)*)&sun;
}
@@ -1983,7 +1984,7 @@ static if (is(sockaddr_un))
return _nameLen;
}
- this(in char[] path) @trusted pure
+ this(scope const(char)[] path) @trusted pure
{
enforce(path.length <= sun.sun_path.sizeof, new SocketParameterException("Path too long"));
sun.sun_family = AddressFamily.UNIX;
@@ -2011,6 +2012,8 @@ static if (is(sockaddr_un))
@property string path() @trusted const pure
{
auto len = _nameLen - sockaddr_un.init.sun_path.offsetof;
+ if (len == 0)
+ return null; // An empty path may be returned from getpeername
// For pathname socket address we need to strip off the terminating '\0'
if (sun.sun_path.ptr[0])
--len;
@@ -2026,14 +2029,33 @@ static if (is(sockaddr_un))
@safe unittest
{
import core.stdc.stdio : remove;
- import std.file : deleteme;
+
+ version (iOSDerived)
+ {
+ // Slightly different version of `std.file.deleteme` to reduce the path
+ // length on iOS derived platforms. Due to the sandbox, the length
+ // of paths can quickly become too long.
+ static string deleteme()
+ {
+ import std.conv : text;
+ import std.process : thisProcessID;
+ import std.file : tempDir;
+
+ return text(tempDir, thisProcessID);
+ }
+ }
+
+ else
+ import std.file : deleteme;
immutable ubyte[] data = [1, 2, 3, 4];
Socket[2] pair;
- auto names = [ deleteme ~ "-unix-socket" ];
+ const basePath = deleteme;
+ auto names = [ basePath ~ "-socket" ];
version (linux)
- names ~= "\0" ~ deleteme ~ "-abstract\0unix\0socket";
+ names ~= "\0" ~ basePath ~ "-abstract\0unix\0socket";
+
foreach (name; names)
{
auto address = new UnixAddress(name);
@@ -2060,13 +2082,19 @@ static if (is(sockaddr_un))
auto buf = new ubyte[data.length];
pair[1].receive(buf);
assert(buf == data);
+
+ // getpeername is free to return an empty name for a unix
+ // domain socket pair or unbound socket. Let's confirm it
+ // returns successfully and doesn't throw anything.
+ // See https://issues.dlang.org/show_bug.cgi?id=20544
+ assertNotThrown(pair[1].remoteAddress().toString());
}
}
}
/**
- * Class for exceptions thrown by $(D Socket.accept).
+ * Class for exceptions thrown by `Socket.accept`.
*/
class SocketAcceptException: SocketOSException
{
@@ -2132,10 +2160,10 @@ struct TimeVal
/**
- * A collection of sockets for use with $(D Socket.select).
+ * A collection of sockets for use with `Socket.select`.
*
- * $(D SocketSet) wraps the platform $(D fd_set) type. However, unlike
- * $(D fd_set), $(D SocketSet) is not statically limited to $(D FD_SETSIZE)
+ * `SocketSet` wraps the platform `fd_set` type. However, unlike
+ * `fd_set`, `SocketSet` is not statically limited to `FD_SETSIZE`
* or any other limit, and grows as needed.
*/
class SocketSet
@@ -2243,7 +2271,7 @@ public:
/**
* Create a SocketSet with a specific initial capacity (defaults to
- * $(D FD_SETSIZE), the system's default capacity).
+ * `FD_SETSIZE`, the system's default capacity).
*/
this(size_t size = FD_SETSIZE) pure nothrow
{
@@ -2251,7 +2279,7 @@ public:
reset();
}
- /// Reset the $(D SocketSet) so that there are 0 $(D Socket)s in the collection.
+ /// Reset the `SocketSet` so that there are 0 `Socket`s in the collection.
void reset() pure nothrow @nogc
{
version (Windows)
@@ -2294,7 +2322,7 @@ public:
}
/**
- * Add a $(D Socket) to the collection.
+ * Add a `Socket` to the collection.
* The socket must not already be in the collection.
*/
void add(Socket s) pure nothrow
@@ -2324,7 +2352,7 @@ public:
/**
- * Remove this $(D Socket) from the collection.
+ * Remove this `Socket` from the collection.
* Does nothing if the socket is not in the collection already.
*/
void remove(Socket s) pure nothrow
@@ -2349,7 +2377,7 @@ public:
}
- /// Return nonzero if this $(D Socket) is in the collection.
+ /// Return nonzero if this `Socket` is in the collection.
int isSet(Socket s) const pure nothrow @nogc
{
return isSet(s.sock);
@@ -2358,12 +2386,12 @@ public:
/**
* Returns:
- * The current capacity of this $(D SocketSet). The exact
+ * The current capacity of this `SocketSet`. The exact
* meaning of the return value varies from platform to platform.
*
* Note:
* Since D 2.065, this value does not indicate a
- * restriction, and $(D SocketSet) will grow its capacity as
+ * restriction, and `SocketSet` will grow its capacity as
* needed automatically.
*/
@property uint max() const pure nothrow @nogc
@@ -2415,12 +2443,21 @@ public:
@safe unittest
{
- softUnittest({
+ version (iOSDerived)
+ {
+ enum PAIRS = 256;
+ enum LIMIT = 1024;
+ }
+ else
+ {
enum PAIRS = 768;
+ enum LIMIT = 2048;
+ }
+
+ softUnittest({
version (Posix)
() @trusted
{
- enum LIMIT = 2048;
static assert(LIMIT > PAIRS*2);
import core.sys.posix.sys.resource;
rlimit fileLimit;
@@ -2477,18 +2514,28 @@ public:
assert(!errorSet.isSet(testPair[1]));
ubyte[1] b;
- testPair[0].send(b[]);
+ // Socket.send can't be marked with `scope`
+ // -> @safe DIP1000 code can't use it - see https://github.com/dlang/phobos/pull/6204
+ () @trusted {
+ testPair[0].send(b[]);
+ }();
fillSets();
n = Socket.select(readSet, null, null);
assert(n == 1); // testPair[1]
assert(readSet.isSet(testPair[1]));
assert(!readSet.isSet(testPair[0]));
- testPair[1].receive(b[]);
+ // Socket.receive can't be marked with `scope`
+ // -> @safe DIP1000 code can't use it - see https://github.com/dlang/phobos/pull/6204
+ () @trusted {
+ testPair[1].receive(b[]);
+ }();
}
});
}
-@safe unittest // Issue 14012, 14013
+// https://issues.dlang.org/show_bug.cgi?id=14012
+// https://issues.dlang.org/show_bug.cgi?id=14013
+@safe unittest
{
auto set = new SocketSet(1);
assert(set.max >= 0);
@@ -2570,7 +2617,7 @@ enum SocketOption: int
/**
- * $(D Socket) is a class that creates a network communication endpoint using
+ * `Socket` is a class that creates a network communication endpoint using
* the Berkeley sockets interface.
*/
class Socket
@@ -2591,9 +2638,9 @@ private:
@safe unittest
{
- version (SlowTests)
+ debug (std_socket)
softUnittest({
- import std.datetime;
+ import std.datetime.stopwatch;
import std.typecons;
enum msecs = 1000;
@@ -2611,7 +2658,7 @@ private:
sock.getOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, readBack);
assert(readBack.total!"msecs" == msecs);
- assert(sw.peek().msecs > msecs-100 && sw.peek().msecs < msecs+100);
+ assert(sw.peek().total!"msecs" > msecs - 100 && sw.peek().total!"msecs" < msecs + 100);
});
}
@@ -2639,7 +2686,7 @@ public:
/**
* Create a blocking socket. If a single protocol type exists to support
- * this socket type within the address family, the $(D ProtocolType) may be
+ * this socket type within the address family, the `ProtocolType` may be
* omitted.
*/
this(AddressFamily af, SocketType type, ProtocolType protocol) @trusted
@@ -2662,7 +2709,7 @@ public:
/// ditto
- this(AddressFamily af, SocketType type, in char[] protocolName) @trusted
+ this(AddressFamily af, SocketType type, scope const(char)[] protocolName) @trusted
{
protoent* proto;
proto = getprotobyname(protocolName.tempCString());
@@ -2674,9 +2721,9 @@ public:
/**
* Create a blocking socket using the parameters from the specified
- * $(D AddressInfo) structure.
+ * `AddressInfo` structure.
*/
- this(in AddressInfo info)
+ this(const scope AddressInfo info)
{
this(info.family, info.type, info.protocol);
}
@@ -2764,7 +2811,14 @@ public:
return !getsockopt(sock, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize);
}
- /// Associate a local address with this socket.
+ /**
+ * Associate a local address with this socket.
+ *
+ * Params:
+ * addr = The $(LREF Address) to associate this socket with.
+ *
+ * Throws: $(LREF SocketOSException) when unable to bind the socket.
+ */
void bind(Address addr) @trusted
{
if (_SOCKET_ERROR == .bind(sock, addr.name, addr.nameLen))
@@ -2805,9 +2859,9 @@ public:
}
/**
- * Listen for an incoming connection. $(D bind) must be called before you
- * can $(D listen). The $(D backlog) is a request of how many pending
- * incoming connections are queued until $(D accept)ed.
+ * Listen for an incoming connection. `bind` must be called before you
+ * can `listen`. The `backlog` is a request of how many pending
+ * incoming connections are queued until `accept`ed.
*/
void listen(int backlog) @trusted
{
@@ -2816,10 +2870,10 @@ public:
}
/**
- * Called by $(D accept) when a new $(D Socket) must be created for a new
+ * Called by `accept` when a new `Socket` must be created for a new
* connection. To use a derived class, override this method and return an
- * instance of your class. The returned $(D Socket)'s handle must not be
- * set; $(D Socket) has a protected constructor $(D this()) to use in this
+ * instance of your class. The returned `Socket`'s handle must not be
+ * set; `Socket` has a protected constructor `this()` to use in this
* situation.
*
* Override to use a derived class.
@@ -2831,9 +2885,9 @@ public:
}
/**
- * Accept an incoming connection. If the socket is blocking, $(D accept)
- * waits for a connection request. Throws $(D SocketAcceptException) if
- * unable to _accept. See $(D accepting) for use with derived classes.
+ * Accept an incoming connection. If the socket is blocking, `accept`
+ * waits for a connection request. Throws `SocketAcceptException` if
+ * unable to _accept. See `accepting` for use with derived classes.
*/
Socket accept() @trusted
{
@@ -2883,10 +2937,8 @@ public:
/**
* Immediately drop any connections and release socket resources.
- * Calling $(D shutdown) before $(D close) is recommended for
- * connection-oriented sockets. The $(D Socket) object is no longer
- * usable after $(D close).
- * Calling shutdown() before this is recommended
+ * The `Socket` object is no longer usable after `close`.
+ * Calling `shutdown` before `close` is recommended
* for connection-oriented sockets.
*/
void close() @trusted nothrow @nogc
@@ -2907,7 +2959,7 @@ public:
return to!string(result.ptr);
}
- /// Remote endpoint $(D Address).
+ /// Remote endpoint `Address`.
@property Address remoteAddress() @trusted
{
Address addr = createAddress();
@@ -2919,7 +2971,7 @@ public:
return addr;
}
- /// Local endpoint $(D Address).
+ /// Local endpoint `Address`.
@property Address localAddress() @trusted
{
Address addr = createAddress();
@@ -2932,8 +2984,8 @@ public:
}
/**
- * Send or receive error code. See $(D wouldHaveBlocked),
- * $(D lastSocketError) and $(D Socket.getErrorText) for obtaining more
+ * Send or receive error code. See `wouldHaveBlocked`,
+ * `lastSocketError` and `Socket.getErrorText` for obtaining more
* information about the error.
*/
enum int ERROR = _SOCKET_ERROR;
@@ -2949,8 +3001,8 @@ public:
/**
* Send data on the connection. If the socket is blocking and there is no
- * buffer space left, $(D send) waits.
- * Returns: The number of bytes actually sent, or $(D Socket.ERROR) on
+ * buffer space left, `send` waits.
+ * Returns: The number of bytes actually sent, or `Socket.ERROR` on
* failure.
*/
ptrdiff_t send(const(void)[] buf, SocketFlags flags) @trusted
@@ -2975,8 +3027,8 @@ public:
/**
* Send data to a specific destination Address. If the destination address is
* not specified, a connection must have been made and that address is used.
- * If the socket is blocking and there is no buffer space left, $(D sendTo) waits.
- * Returns: The number of bytes actually sent, or $(D Socket.ERROR) on
+ * If the socket is blocking and there is no buffer space left, `sendTo` waits.
+ * Returns: The number of bytes actually sent, or `Socket.ERROR` on
* failure.
*/
ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags, Address to) @trusted
@@ -3025,10 +3077,10 @@ public:
/**
- * Receive data on the connection. If the socket is blocking, $(D receive)
+ * Receive data on the connection. If the socket is blocking, `receive`
* waits until there is data to be received.
- * Returns: The number of bytes actually received, $(D 0) if the remote side
- * has closed the connection, or $(D Socket.ERROR) on failure.
+ * Returns: The number of bytes actually received, `0` if the remote side
+ * has closed the connection, or `Socket.ERROR` on failure.
*/
ptrdiff_t receive(void[] buf, SocketFlags flags) @trusted
{
@@ -3053,11 +3105,11 @@ public:
}
/**
- * Receive data and get the remote endpoint $(D Address).
- * If the socket is blocking, $(D receiveFrom) waits until there is data to
+ * Receive data and get the remote endpoint `Address`.
+ * If the socket is blocking, `receiveFrom` waits until there is data to
* be received.
- * Returns: The number of bytes actually received, $(D 0) if the remote side
- * has closed the connection, or $(D Socket.ERROR) on failure.
+ * Returns: The number of bytes actually received, `0` if the remote side
+ * has closed the connection, or `Socket.ERROR` on failure.
*/
ptrdiff_t receiveFrom(void[] buf, SocketFlags flags, ref Address from) @trusted
{
@@ -3123,7 +3175,7 @@ public:
/**
* Get a socket option.
- * Returns: The number of bytes written to $(D result).
+ * Returns: The number of bytes written to `result`.
* The length, in bytes, of the actual result - very different from getsockopt()
*/
int getOption(SocketOptionLevel level, SocketOption option, void[] result) @trusted
@@ -3197,8 +3249,8 @@ public:
}
/**
- * Sets a timeout (duration) option, i.e. $(D SocketOption.SNDTIMEO) or
- * $(D RCVTIMEO). Zero indicates no timeout.
+ * Sets a timeout (duration) option, i.e. `SocketOption.SNDTIMEO` or
+ * `RCVTIMEO`. Zero indicates no timeout.
*
* In a typical application, you might also want to consider using
* a non-blocking socket instead of setting a timeout on a blocking one.
@@ -3207,17 +3259,17 @@ public:
* on *nix systems even for smaller durations, there are two issues to
* be aware of on Windows: First, although undocumented, the effective
* timeout duration seems to be the one set on the socket plus half
- * a second. $(D setOption()) tries to compensate for that, but still,
+ * a second. `setOption()` tries to compensate for that, but still,
* timeouts under 500ms are not possible on Windows. Second, be aware
* that the actual amount of time spent until a blocking call returns
* randomly varies on the order of 10ms.
*
* Params:
* level = The level at which a socket option is defined.
- * option = Either $(D SocketOption.SNDTIMEO) or $(D SocketOption.RCVTIMEO).
+ * option = Either `SocketOption.SNDTIMEO` or `SocketOption.RCVTIMEO`.
* value = The timeout duration to set. Must not be negative.
*
- * Throws: $(D SocketException) if setting the options fails.
+ * Throws: `SocketException` if setting the options fails.
*
* Example:
* ---
@@ -3284,8 +3336,8 @@ public:
* interval = Number of seconds between when successive keep-alive
* packets are sent if no acknowledgement is received.
*
- * Throws: $(D SocketOSException) if setting the options fails, or
- * $(D SocketFeatureException) if setting keep-alive parameters is
+ * Throws: `SocketOSException` if setting the options fails, or
+ * `SocketFeatureException` if setting keep-alive parameters is
* unsupported on the current platform.
*/
void setKeepAlive(int time, int interval) @trusted
@@ -3317,12 +3369,12 @@ public:
/**
* Wait for a socket to change status. A wait timeout of $(REF Duration, core, time) or
- * $(D TimeVal), may be specified; if a timeout is not specified or the
- * $(D TimeVal) is $(D null), the maximum timeout is used. The $(D TimeVal)
- * timeout has an unspecified value when $(D select) returns.
- * Returns: The number of sockets with status changes, $(D 0) on timeout,
- * or $(D -1) on interruption. If the return value is greater than $(D 0),
- * the $(D SocketSets) are updated to only contain the sockets having status
+ * `TimeVal`, may be specified; if a timeout is not specified or the
+ * `TimeVal` is `null`, the maximum timeout is used. The `TimeVal`
+ * timeout has an unspecified value when `select` returns.
+ * Returns: The number of sockets with status changes, `0` on timeout,
+ * or `-1` on interruption. If the return value is greater than `0`,
+ * the `SocketSets` are updated to only contain the sockets having status
* changes. For a connecting socket, a write status change means the
* connection is established and it's able to send. For a listening socket,
* a read status change means there is an incoming connection request and
@@ -3367,7 +3419,7 @@ public:
assert(checkWrite !is checkError);
}
}
- body
+ do
{
fd_set* fr, fw, fe;
int n = 0;
@@ -3481,7 +3533,7 @@ public:
}
-/// $(D TcpSocket) is a shortcut class for a TCP Socket.
+/// `TcpSocket` is a shortcut class for a TCP Socket.
class TcpSocket: Socket
{
/// Constructs a blocking TCP Socket.
@@ -3498,7 +3550,7 @@ class TcpSocket: Socket
//shortcut
- /// Constructs a blocking TCP Socket and connects to an $(D Address).
+ /// Constructs a blocking TCP Socket and connects to an `Address`.
this(Address connectTo)
{
this(connectTo.addressFamily);
@@ -3507,7 +3559,7 @@ class TcpSocket: Socket
}
-/// $(D UdpSocket) is a shortcut class for a UDP Socket.
+/// `UdpSocket` is a shortcut class for a UDP Socket.
class UdpSocket: Socket
{
/// Constructs a blocking UDP Socket.
@@ -3524,50 +3576,167 @@ class UdpSocket: Socket
}
}
-// Issue 16514
+// https://issues.dlang.org/show_bug.cgi?id=16514
@safe unittest
{
+ void checkAttributes(string attributes)()
+ {
+ mixin(attributes ~ q{ void function() fun = {};});
+ fun();
+ }
+
class TestSocket : Socket
{
override
{
- const pure nothrow @nogc @property @safe socket_t handle() { assert(0); }
- const nothrow @nogc @property @trusted bool blocking() { assert(0); }
- @property @trusted void blocking(bool byes) { assert(0); }
- @property @safe AddressFamily addressFamily() { assert(0); }
- const @property @trusted bool isAlive() { assert(0); }
- @trusted void bind(Address addr) { assert(0); }
- @trusted void connect(Address to) { assert(0); }
- @trusted void listen(int backlog) { assert(0); }
- protected pure nothrow @safe Socket accepting() { assert(0); }
- @trusted Socket accept() { assert(0); }
- nothrow @nogc @trusted void shutdown(SocketShutdown how) { assert(0); }
- nothrow @nogc @trusted void close() { assert(0); }
- @property @trusted Address remoteAddress() { assert(0); }
- @property @trusted Address localAddress() { assert(0); }
- @trusted ptrdiff_t send(const(void)[] buf, SocketFlags flags) { assert(0); }
- @safe ptrdiff_t send(const(void)[] buf) { assert(0); }
- @trusted ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags, Address to) { assert(0); }
- @safe ptrdiff_t sendTo(const(void)[] buf, Address to) { assert(0); }
- @trusted ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags) { assert(0); }
- @safe ptrdiff_t sendTo(const(void)[] buf) { assert(0); }
- @trusted ptrdiff_t receive(void[] buf, SocketFlags flags) { assert(0); }
- @safe ptrdiff_t receive(void[] buf) { assert(0); }
- @trusted ptrdiff_t receiveFrom(void[] buf, SocketFlags flags, ref Address from) { assert(0); }
- @safe ptrdiff_t receiveFrom(void[] buf, ref Address from) { assert(0); }
- @trusted ptrdiff_t receiveFrom(void[] buf, SocketFlags flags) { assert(0); }
- @safe ptrdiff_t receiveFrom(void[] buf) { assert(0); }
- @trusted int getOption(SocketOptionLevel level, SocketOption option, void[] result) { assert(0); }
- @trusted int getOption(SocketOptionLevel level, SocketOption option, out int32_t result) { assert(0); }
- @trusted int getOption(SocketOptionLevel level, SocketOption option, out Linger result) { assert(0); }
- @trusted void getOption(SocketOptionLevel level, SocketOption option, out Duration result) { assert(0); }
- @trusted void setOption(SocketOptionLevel level, SocketOption option, void[] value) { assert(0); }
- @trusted void setOption(SocketOptionLevel level, SocketOption option, int32_t value) { assert(0); }
- @trusted void setOption(SocketOptionLevel level, SocketOption option, Linger value) { assert(0); }
- @trusted void setOption(SocketOptionLevel level, SocketOption option, Duration value) { assert(0); }
- @safe string getErrorText() { assert(0); }
- @trusted void setKeepAlive(int time, int interval) { assert(0); }
- protected pure nothrow @safe Address createAddress() { assert(0); }
+ @property pure nothrow @nogc @safe socket_t handle() const
+ {
+ checkAttributes!q{pure nothrow @nogc @safe}; assert(0);
+ }
+ @property nothrow @nogc @trusted bool blocking() const
+ {
+ checkAttributes!q{nothrow @nogc @trusted}; assert(0);
+ }
+ @property @trusted void blocking(bool byes)
+ {
+ checkAttributes!q{@trusted};
+ }
+ @property @safe AddressFamily addressFamily()
+ {
+ checkAttributes!q{@safe}; assert(0);
+ }
+ @property @trusted bool isAlive() const
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @trusted void bind(Address addr)
+ {
+ checkAttributes!q{@trusted};
+ }
+ @trusted void connect(Address to)
+ {
+ checkAttributes!q{@trusted};
+ }
+ @trusted void listen(int backlog)
+ {
+ checkAttributes!q{@trusted};
+ }
+ protected pure nothrow @safe Socket accepting()
+ {
+ checkAttributes!q{pure nothrow @safe}; assert(0);
+ }
+ @trusted Socket accept()
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ nothrow @nogc @trusted void shutdown(SocketShutdown how)
+ {
+ checkAttributes!q{nothrow @nogc @trusted};
+ }
+ nothrow @nogc @trusted void close()
+ {
+ checkAttributes!q{nothrow @nogc @trusted};
+ }
+ @property @trusted Address remoteAddress()
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @property @trusted Address localAddress()
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @trusted ptrdiff_t send(const(void)[] buf, SocketFlags flags)
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @safe ptrdiff_t send(const(void)[] buf)
+ {
+ checkAttributes!q{@safe}; assert(0);
+ }
+ @trusted ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags, Address to)
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @safe ptrdiff_t sendTo(const(void)[] buf, Address to)
+ {
+ checkAttributes!q{@safe}; assert(0);
+ }
+ @trusted ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags)
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @safe ptrdiff_t sendTo(const(void)[] buf)
+ {
+ checkAttributes!q{@safe}; assert(0);
+ }
+ @trusted ptrdiff_t receive(void[] buf, SocketFlags flags)
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @safe ptrdiff_t receive(void[] buf)
+ {
+ checkAttributes!q{@safe}; assert(0);
+ }
+ @trusted ptrdiff_t receiveFrom(void[] buf, SocketFlags flags, ref Address from)
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @safe ptrdiff_t receiveFrom(void[] buf, ref Address from)
+ {
+ checkAttributes!q{@safe}; assert(0);
+ }
+ @trusted ptrdiff_t receiveFrom(void[] buf, SocketFlags flags)
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @safe ptrdiff_t receiveFrom(void[] buf)
+ {
+ checkAttributes!q{@safe}; assert(0);
+ }
+ @trusted int getOption(SocketOptionLevel level, SocketOption option, void[] result)
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @trusted int getOption(SocketOptionLevel level, SocketOption option, out int32_t result)
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @trusted int getOption(SocketOptionLevel level, SocketOption option, out Linger result)
+ {
+ checkAttributes!q{@trusted}; assert(0);
+ }
+ @trusted void getOption(SocketOptionLevel level, SocketOption option, out Duration result)
+ {
+ checkAttributes!q{@trusted};
+ }
+ @trusted void setOption(SocketOptionLevel level, SocketOption option, void[] value)
+ {
+ checkAttributes!q{@trusted};
+ }
+ @trusted void setOption(SocketOptionLevel level, SocketOption option, int32_t value)
+ {
+ checkAttributes!q{@trusted};
+ }
+ @trusted void setOption(SocketOptionLevel level, SocketOption option, Linger value)
+ {
+ checkAttributes!q{@trusted};
+ }
+ @trusted void setOption(SocketOptionLevel level, SocketOption option, Duration value)
+ {
+ checkAttributes!q{@trusted};
+ }
+ @safe string getErrorText()
+ {
+ checkAttributes!q{@safe}; assert(0);
+ }
+ @trusted void setKeepAlive(int time, int interval)
+ {
+ checkAttributes!q{@trusted};
+ }
+ protected pure nothrow @safe Address createAddress()
+ {
+ checkAttributes!q{pure nothrow @safe}; assert(0);
+ }
}
}
}
@@ -3577,7 +3746,7 @@ class UdpSocket: Socket
*
* The two sockets are indistinguishable.
*
- * Throws: $(D SocketException) if creation of the sockets fails.
+ * Throws: `SocketException` if creation of the sockets fails.
*/
Socket[2] socketPair() @trusted
{
diff --git a/libphobos/src/std/stdint.d b/libphobos/src/std/stdint.d
index b4a5ff918a5..88f4a225602 100644
--- a/libphobos/src/std/stdint.d
+++ b/libphobos/src/std/stdint.d
@@ -116,12 +116,12 @@
* Macros:
* ATABLE=<table border="1" cellspacing="0" cellpadding="5">$0</table>
*
- * Copyright: Copyright Digital Mars 2000 - 2009.
+ * Copyright: Copyright The D Language Foundation 2000 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright)
- * Source: $(PHOBOSSRC std/_stdint.d)
+ * Source: $(PHOBOSSRC std/stdint.d)
*/
-/* Copyright Digital Mars 2000 - 2009.
+/* Copyright The D Language Foundation 2000 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d
index bbf785773d4..a88beb8ff32 100644
--- a/libphobos/src/std/stdio.d
+++ b/libphobos/src/std/stdio.d
@@ -1,11 +1,44 @@
// Written in the D programming language.
/**
+$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Symbols))
+$(TR $(TD File handles) $(TD
+ $(MYREF __popen)
+ $(MYREF File)
+ $(MYREF isFileHandle)
+ $(MYREF openNetwork)
+ $(MYREF stderr)
+ $(MYREF stdin)
+ $(MYREF stdout)
+))
+$(TR $(TD Reading) $(TD
+ $(MYREF chunks)
+ $(MYREF lines)
+ $(MYREF readf)
+ $(MYREF readln)
+))
+$(TR $(TD Writing) $(TD
+ $(MYREF toFile)
+ $(MYREF write)
+ $(MYREF writef)
+ $(MYREF writefln)
+ $(MYREF writeln)
+))
+$(TR $(TD Misc) $(TD
+ $(MYREF KeepTerminator)
+ $(MYREF LockType)
+ $(MYREF StdioException)
+))
+))
+
Standard I/O functions that extend $(B core.stdc.stdio). $(B core.stdc.stdio)
is $(D_PARAM public)ally imported when importing $(B std.stdio).
-Source: $(PHOBOSSRC std/_stdio.d)
-Copyright: Copyright Digital Mars 2007-.
+Source: $(PHOBOSSRC std/stdio.d)
+Copyright: Copyright The D Language Foundation 2007-.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP digitalmars.com, Walter Bright),
$(HTTP erdani.org, Andrei Alexandrescu),
@@ -13,17 +46,17 @@ Authors: $(HTTP digitalmars.com, Walter Bright),
*/
module std.stdio;
-import core.stdc.stddef; // wchar_t
+import core.stdc.stddef : wchar_t;
public import core.stdc.stdio;
-import std.algorithm.mutation; // copy
-import std.meta; // allSatisfy
-import std.range.primitives; // ElementEncodingType, empty, front,
- // isBidirectionalRange, isInputRange, put
-import std.traits; // isSomeChar, isSomeString, Unqual, isPointer
-import std.typecons; // Flag
+import std.algorithm.mutation : copy;
+import std.meta : allSatisfy;
+import std.range.primitives : ElementEncodingType, empty, front,
+ isBidirectionalRange, isInputRange, put;
+import std.traits : isSomeChar, isSomeString, Unqual, isPointer;
+import std.typecons : Flag, No, Yes;
/++
-If flag $(D KeepTerminator) is set to $(D KeepTerminator.yes), then the delimiter
+If flag `KeepTerminator` is set to `KeepTerminator.yes`, then the delimiter
is included in the strings returned.
+/
alias KeepTerminator = Flag!"keepTerminator";
@@ -58,18 +91,22 @@ else version (CRuntime_UClibc)
else version (OSX)
{
version = GENERIC_IO;
+ version = Darwin;
}
else version (iOS)
{
version = GENERIC_IO;
+ version = Darwin;
}
else version (TVOS)
{
version = GENERIC_IO;
+ version = Darwin;
}
else version (WatchOS)
{
version = GENERIC_IO;
+ version = Darwin;
}
else version (FreeBSD)
{
@@ -112,147 +149,214 @@ version (Windows)
extern (C) nothrow @nogc FILE* _wfopen(in wchar* filename, in wchar* mode);
extern (C) nothrow @nogc FILE* _wfreopen(in wchar* filename, in wchar* mode, FILE* fp);
- import core.sys.windows.windows : HANDLE;
+ import core.sys.windows.basetsd : HANDLE;
}
version (Posix)
{
- static import core.sys.posix.stdio; // getdelim
+ static import core.sys.posix.stdio; // getdelim, flockfile
}
version (DIGITAL_MARS_STDIO)
{
- extern (C)
- {
- /* **
- * Digital Mars under-the-hood C I/O functions.
- * Use _iobuf* for the unshared version of FILE*,
- * usable when the FILE is locked.
- */
- nothrow:
- @nogc:
- int _fputc_nlock(int, _iobuf*);
- int _fputwc_nlock(int, _iobuf*);
- int _fgetc_nlock(_iobuf*);
- int _fgetwc_nlock(_iobuf*);
- int __fp_lock(FILE*);
- void __fp_unlock(FILE*);
-
- int setmode(int, int);
- }
+ private alias _FPUTC = _fputc_nlock;
+ private alias _FPUTWC = _fputwc_nlock;
+ private alias _FGETC = _fgetc_nlock;
+ private alias _FGETWC = _fgetwc_nlock;
+ private alias _FLOCK = __fp_lock;
+ private alias _FUNLOCK = __fp_unlock;
+
+ // Alias for MICROSOFT_STDIO compatibility.
+ // @@@DEPRECATED_2.107@@@
+ // Rename this back to _setmode once the deprecation phase has ended.
+ private alias __setmode = setmode;
+
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FPUTC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FPUTC = _fputc_nlock;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FPUTWC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FPUTWC = _fputwc_nlock;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FGETC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FGETC = _fgetc_nlock;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FGETWC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FGETWC = _fgetwc_nlock;
-
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FLOCK was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FLOCK = __fp_lock;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FUNLOCK was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FUNLOCK = __fp_unlock;
-
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias _setmode was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias _setmode = setmode;
- enum _O_BINARY = 0x8000;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal function _fileno was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
int _fileno(FILE* f) { return f._file; }
- alias fileno = _fileno;
}
else version (MICROSOFT_STDIO)
{
- extern (C)
- {
- /* **
- * Microsoft under-the-hood C I/O functions
- */
- nothrow:
- @nogc:
- int _fputc_nolock(int, _iobuf*);
- int _fputwc_nolock(int, _iobuf*);
- int _fgetc_nolock(_iobuf*);
- int _fgetwc_nolock(_iobuf*);
- void _lock_file(FILE*);
- void _unlock_file(FILE*);
- int _setmode(int, int);
- int _fileno(FILE*);
- FILE* _fdopen(int, const (char)*);
- int _fseeki64(FILE*, long, int);
- long _ftelli64(FILE*);
- }
+ private alias _FPUTC = _fputc_nolock;
+ private alias _FPUTWC = _fputwc_nolock;
+ private alias _FGETC = _fgetc_nolock;
+ private alias _FGETWC = _fgetwc_nolock;
+ private alias _FLOCK = _lock_file;
+ private alias _FUNLOCK = _unlock_file;
+
+ // @@@DEPRECATED_2.107@@@
+ // Remove this once the deprecation phase for DIGITAL_MARS_STDIO has ended.
+ private alias __setmode = _setmode;
+
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FPUTC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FPUTC = _fputc_nolock;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FPUTWC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FPUTWC = _fputwc_nolock;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FGETC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FGETC = _fgetc_nolock;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FGETWC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FGETWC = _fgetwc_nolock;
-
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FLOCK was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FLOCK = _lock_file;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FUNLOCK was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FUNLOCK = _unlock_file;
-
- alias setmode = _setmode;
- alias fileno = _fileno;
-
- enum
- {
- _O_RDONLY = 0x0000,
- _O_APPEND = 0x0004,
- _O_TEXT = 0x4000,
- _O_BINARY = 0x8000,
- }
}
else version (GCC_IO)
{
- /* **
- * Gnu under-the-hood C I/O functions; see
- * http://gnu.org/software/libc/manual/html_node/I_002fO-on-Streams.html
- */
- extern (C)
- {
- nothrow:
- @nogc:
- int fputc_unlocked(int, _iobuf*);
- int fputwc_unlocked(wchar_t, _iobuf*);
- int fgetc_unlocked(_iobuf*);
- int fgetwc_unlocked(_iobuf*);
- void flockfile(FILE*);
- void funlockfile(FILE*);
-
- private size_t fwrite_unlocked(const(void)* ptr,
- size_t size, size_t n, _iobuf *stream);
- }
-
+ private alias _FPUTC = fputc_unlocked;
+ private alias _FPUTWC = fputwc_unlocked;
+ private alias _FGETC = fgetc_unlocked;
+ private alias _FGETWC = fgetwc_unlocked;
+ private alias _FLOCK = core.sys.posix.stdio.flockfile;
+ private alias _FUNLOCK = core.sys.posix.stdio.funlockfile;
+
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FPUTC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FPUTC = fputc_unlocked;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FPUTWC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FPUTWC = fputwc_unlocked;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FGETC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FGETC = fgetc_unlocked;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FGETWC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FGETWC = fgetwc_unlocked;
-
- alias FLOCK = flockfile;
- alias FUNLOCK = funlockfile;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FLOCK was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
+ alias FLOCK = core.sys.posix.stdio.flockfile;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FUNLOCK was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
+ alias FUNLOCK = core.sys.posix.stdio.funlockfile;
}
else version (GENERIC_IO)
{
nothrow:
@nogc:
- extern (C)
+ private int _FPUTC(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); }
+ private int _FPUTWC(wchar_t c, _iobuf* fp)
{
- void flockfile(FILE*);
- void funlockfile(FILE*);
+ import core.stdc.wchar_ : fputwc;
+ return fputwc(c, cast(shared) fp);
+ }
+ private int _FGETC(_iobuf* fp) { return fgetc(cast(shared) fp); }
+ private int _FGETWC(_iobuf* fp)
+ {
+ import core.stdc.wchar_ : fgetwc;
+ return fgetwc(cast(shared) fp);
}
+ version (Posix)
+ {
+ private alias _FLOCK = core.sys.posix.stdio.flockfile;
+ private alias _FUNLOCK = core.sys.posix.stdio.funlockfile;
+ }
+ else
+ {
+ static assert(0, "don't know how to lock files on GENERIC_IO");
+ }
+
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal function fputc_unlocked was unintentionally available "
+ ~ "from std.stdio and will be removed afer 2.107")
int fputc_unlocked(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); }
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal function fputwc_unlocked was unintentionally available "
+ ~ "from std.stdio and will be removed afer 2.107")
int fputwc_unlocked(wchar_t c, _iobuf* fp)
{
import core.stdc.wchar_ : fputwc;
return fputwc(c, cast(shared) fp);
}
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal function fgetc_unlocked was unintentionally available "
+ ~ "from std.stdio and will be removed afer 2.107")
int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); }
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal function fgetwc_unlocked was unintentionally available "
+ ~ "from std.stdio and will be removed afer 2.107")
int fgetwc_unlocked(_iobuf* fp)
{
import core.stdc.wchar_ : fgetwc;
return fgetwc(cast(shared) fp);
}
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FPUTC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FPUTC = fputc_unlocked;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FPUTWC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FPUTWC = fputwc_unlocked;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FGETC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FGETC = fgetc_unlocked;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FGETWC was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
alias FGETWC = fgetwc_unlocked;
- alias FLOCK = flockfile;
- alias FUNLOCK = funlockfile;
+ version (Posix)
+ {
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FLOCK was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
+ alias FLOCK = core.sys.posix.stdio.flockfile;
+ // @@@DEPRECATED_2.107@@@
+ deprecated("internal alias FUNLOCK was unintentionally available from "
+ ~ "std.stdio and will be removed afer 2.107")
+ alias FUNLOCK = core.sys.posix.stdio.funlockfile;
+ }
}
else
{
@@ -275,7 +379,7 @@ static if (__traits(compiles, core.sys.posix.stdio.getdelim))
}
//------------------------------------------------------------------------------
-struct ByRecord(Fields...)
+private struct ByRecordImpl(Fields...)
{
private:
import std.typecons : Tuple;
@@ -311,7 +415,7 @@ public:
{
import std.conv : text;
import std.exception : enforce;
- import std.format : formattedRead;
+ import std.format.read : formattedRead;
import std.string : chomp;
enforce(file.isOpen, "ByRecord: File must be open");
@@ -332,27 +436,29 @@ public:
template byRecord(Fields...)
{
- ByRecord!(Fields) byRecord(File f, string format)
+ auto byRecord(File f, string format)
{
return typeof(return)(f, format);
}
}
/**
-Encapsulates a $(D FILE*). Generally D does not attempt to provide
+Encapsulates a `FILE*`. Generally D does not attempt to provide
thin wrappers over equivalent functions in the C standard library, but
-manipulating $(D FILE*) values directly is unsafe and error-prone in
-many ways. The $(D File) type ensures safe manipulation, automatic
+manipulating `FILE*` values directly is unsafe and error-prone in
+many ways. The `File` type ensures safe manipulation, automatic
file closing, and a lot of convenience.
-The underlying $(D FILE*) handle is maintained in a reference-counted
-manner, such that as soon as the last $(D File) variable bound to a
-given $(D FILE*) goes out of scope, the underlying $(D FILE*) is
+The underlying `FILE*` handle is maintained in a reference-counted
+manner, such that as soon as the last `File` variable bound to a
+given `FILE*` goes out of scope, the underlying `FILE*` is
automatically closed.
Example:
----
// test.d
+import std.stdio;
+
void main(string[] args)
{
auto f = File("test.txt", "w"); // open for writing
@@ -378,6 +484,7 @@ Hello, Jimmy!
*/
struct File
{
+ import core.atomic : atomicOp, atomicStore, atomicLoad;
import std.range.primitives : ElementEncodingType;
import std.traits : isScalarType, isArray;
enum Orientation { unknown, narrow, wide }
@@ -385,7 +492,7 @@ struct File
private struct Impl
{
FILE * handle = null; // Is null iff this Impl is closed by another File
- uint refs = uint.max / 2;
+ shared uint refs = uint.max / 2;
bool isPopened; // true iff the stream has been created by popen()
Orientation orientation;
}
@@ -399,8 +506,14 @@ struct File
assert(!_p);
_p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
+ initImpl(handle, name, refs, isPopened);
+ }
+
+ private void initImpl(FILE* handle, string name, uint refs = 1, bool isPopened = false)
+ {
+ assert(_p);
_p.handle = handle;
- _p.refs = refs;
+ atomicStore(_p.refs, refs);
_p.isPopened = isPopened;
_p.orientation = Orientation.unknown;
_name = name;
@@ -409,10 +522,10 @@ struct File
/**
Constructor taking the name of the file to open and the open mode.
-Copying one $(D File) object to another results in the two $(D File)
+Copying one `File` object to another results in the two `File`
objects referring to the same underlying file.
-The destructor automatically closes the file as soon as no $(D File)
+The destructor automatically closes the file as soon as no `File`
object refers to it anymore.
Params:
@@ -422,14 +535,14 @@ Params:
$(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen)
function)
-Throws: $(D ErrnoException) if the file could not be opened.
+Throws: `ErrnoException` if the file could not be opened.
*/
- this(string name, in char[] stdioOpenmode = "rb") @safe
+ this(string name, scope const(char)[] stdioOpenmode = "rb") @safe
{
import std.conv : text;
import std.exception : errnoEnforce;
- this(errnoEnforce(.fopen(name, stdioOpenmode),
+ this(errnoEnforce(_fopen(name, stdioOpenmode),
text("Cannot open file `", name, "' in mode `",
stdioOpenmode, "'")),
name);
@@ -437,15 +550,7 @@ Throws: $(D ErrnoException) if the file could not be opened.
// MSVCRT workaround (issue 14422)
version (MICROSOFT_STDIO)
{
- bool append, update;
- foreach (c; stdioOpenmode)
- if (c == 'a')
- append = true;
- else
- if (c == '+')
- update = true;
- if (append && !update)
- seek(size);
+ setAppendWin(stdioOpenmode);
}
}
@@ -484,8 +589,8 @@ Throws: $(D ErrnoException) if the file could not be opened.
this(this) @safe nothrow
{
if (!_p) return;
- assert(_p.refs);
- ++_p.refs;
+ assert(atomicLoad(_p.refs));
+ atomicOp!"+="(_p.refs, 1);
}
/**
@@ -493,25 +598,134 @@ Assigns a file to another. The target of the assignment gets detached
from whatever file it was attached to, and attaches itself to the new
file.
*/
- void opAssign(File rhs) @safe
+ ref File opAssign(File rhs) @safe return
{
import std.algorithm.mutation : swap;
swap(this, rhs);
+ return this;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=20129
+ @safe unittest
+ {
+ File[int] aa;
+ aa.require(0, File.init);
}
/**
-First calls $(D detach) (throwing on failure), and then attempts to
-_open file $(D name) with mode $(D stdioOpenmode). The mode has the
+Detaches from the current file (throwing on failure), and then attempts to
+_open file `name` with mode `stdioOpenmode`. The mode has the
same semantics as in the C standard library $(HTTP
cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function.
-Throws: $(D ErrnoException) in case of error.
+Throws: `ErrnoException` in case of error.
*/
- void open(string name, in char[] stdioOpenmode = "rb") @safe
+ void open(string name, scope const(char)[] stdioOpenmode = "rb") @trusted
{
- detach();
- this = File(name, stdioOpenmode);
+ resetFile(name, stdioOpenmode, false);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=20585
+ @system unittest
+ {
+ File f;
+ try
+ f.open("doesn't exist");
+ catch (Exception _e)
+ {
+ }
+
+ assert(!f.isOpen);
+
+ f.close(); // to check not crash here
+ }
+
+ private void resetFile(string name, scope const(char)[] stdioOpenmode, bool isPopened) @trusted
+ {
+ import core.stdc.stdlib : malloc;
+ import std.exception : enforce;
+ import std.conv : text;
+ import std.exception : errnoEnforce;
+
+ if (_p !is null)
+ {
+ detach();
+ }
+
+ FILE* handle;
+ version (Posix)
+ {
+ if (isPopened)
+ {
+ errnoEnforce(handle = _popen(name, stdioOpenmode),
+ "Cannot run command `"~name~"'");
+ }
+ else
+ {
+ errnoEnforce(handle = _fopen(name, stdioOpenmode),
+ text("Cannot open file `", name, "' in mode `",
+ stdioOpenmode, "'"));
+ }
+ }
+ else
+ {
+ assert(isPopened == false);
+ errnoEnforce(handle = _fopen(name, stdioOpenmode),
+ text("Cannot open file `", name, "' in mode `",
+ stdioOpenmode, "'"));
+ }
+ _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
+ initImpl(handle, name, 1, isPopened);
+ version (MICROSOFT_STDIO)
+ {
+ setAppendWin(stdioOpenmode);
+ }
+ }
+
+ private void closeHandles() @trusted
+ {
+ assert(_p);
+ import std.exception : errnoEnforce;
+
+ version (Posix)
+ {
+ import core.sys.posix.stdio : pclose;
+ import std.format : format;
+
+ if (_p.isPopened)
+ {
+ auto res = pclose(_p.handle);
+ errnoEnforce(res != -1,
+ "Could not close pipe `"~_name~"'");
+ _p.handle = null;
+ return;
+ }
+ }
+ if (_p.handle)
+ {
+ auto handle = _p.handle;
+ _p.handle = null;
+ // fclose disassociates the FILE* even in case of error (issue 19751)
+ errnoEnforce(.fclose(handle) == 0,
+ "Could not close file `"~_name~"'");
+ }
+ }
+
+ version (MICROSOFT_STDIO)
+ {
+ private void setAppendWin(scope const(char)[] stdioOpenmode) @safe
+ {
+ bool append, update;
+ foreach (c; stdioOpenmode)
+ if (c == 'a')
+ append = true;
+ else
+ if (c == '+')
+ update = true;
+ if (append && !update)
+ seek(size);
+ }
}
/**
@@ -525,9 +739,9 @@ function.
Note: Calling `reopen` with a `null` `name` is not implemented
in all C runtimes.
-Throws: $(D ErrnoException) in case of error.
+Throws: `ErrnoException` in case of error.
*/
- void reopen(string name, in char[] stdioOpenmode = "rb") @trusted
+ void reopen(string name, scope const(char)[] stdioOpenmode = "rb") @trusted
{
import std.conv : text;
import std.exception : enforce, errnoEnforce;
@@ -595,35 +809,36 @@ Throws: $(D ErrnoException) in case of error.
}
/**
-First calls $(D detach) (throwing on failure), and then runs a command
+Detaches from the current file (throwing on failure), and then runs a command
by calling the C standard library function $(HTTP
opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen).
-Throws: $(D ErrnoException) in case of error.
+Throws: `ErrnoException` in case of error.
*/
- version (Posix) void popen(string command, in char[] stdioOpenmode = "r") @safe
+ version (Posix) void popen(string command, scope const(char)[] stdioOpenmode = "r") @safe
{
- import std.exception : errnoEnforce;
-
- detach();
- this = File(errnoEnforce(.popen(command, stdioOpenmode),
- "Cannot run command `"~command~"'"),
- command, 1, true);
+ resetFile(command, stdioOpenmode ,true);
}
/**
-First calls $(D detach) (throwing on failure), and then attempts to
-associate the given file descriptor with the $(D File). The mode must
-be compatible with the mode of the file descriptor.
+First calls `detach` (throwing on failure), then attempts to
+associate the given file descriptor with the `File`, and sets the file's name to `null`.
+
+The mode must be compatible with the mode of the file descriptor.
-Throws: $(D ErrnoException) in case of error.
+Throws: `ErrnoException` in case of error.
+Params:
+ fd = File descriptor to associate with this `File`.
+ stdioOpenmode = Mode to associate with this File. The mode has the same semantics
+ semantics as in the C standard library
+ $(HTTP cplusplus.com/reference/cstdio/fopen/, fdopen) function, and must be compatible with `fd`.
*/
- void fdopen(int fd, in char[] stdioOpenmode = "rb") @safe
+ void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe
{
fdopen(fd, stdioOpenmode, null);
}
- package void fdopen(int fd, in char[] stdioOpenmode, string name) @trusted
+ package void fdopen(int fd, scope const(char)[] stdioOpenmode, string name) @trusted
{
import std.exception : errnoEnforce;
import std.internal.cstring : tempCString;
@@ -637,15 +852,14 @@ Throws: $(D ErrnoException) in case of error.
// mucking with the file descriptor. POSIX standard requires the
// new fdopen'd file to retain the given file descriptor's
// position.
- import core.stdc.stdio : fopen;
auto fp = fopen("NUL", modez);
errnoEnforce(fp, "Cannot open placeholder NUL stream");
- FLOCK(fp);
+ _FLOCK(fp);
auto iob = cast(_iobuf*) fp;
.close(iob._file);
iob._file = fd;
iob._flag &= ~_IOTRAN;
- FUNLOCK(fp);
+ _FUNLOCK(fp);
}
else
{
@@ -666,17 +880,17 @@ Throws: $(D ErrnoException) in case of error.
version (StdDdoc) { version (Windows) {} else alias HANDLE = int; }
/**
-First calls $(D detach) (throwing on failure), and then attempts to
-associate the given Windows $(D HANDLE) with the $(D File). The mode must
+First calls `detach` (throwing on failure), and then attempts to
+associate the given Windows `HANDLE` with the `File`. The mode must
be compatible with the access attributes of the handle. Windows only.
-Throws: $(D ErrnoException) in case of error.
+Throws: `ErrnoException` in case of error.
*/
version (StdDdoc)
- void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode);
+ void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode);
version (Windows)
- void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode)
+ void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode)
{
import core.stdc.stdint : intptr_t;
import std.exception : errnoEnforce;
@@ -709,17 +923,17 @@ Throws: $(D ErrnoException) in case of error.
}
-/** Returns $(D true) if the file is opened. */
+/** Returns `true` if the file is opened. */
@property bool isOpen() const @safe pure nothrow
{
return _p !is null && _p.handle;
}
/**
-Returns $(D true) if the file is at end (see $(HTTP
+Returns `true` if the file is at end (see $(HTTP
cplusplus.com/reference/clibrary/cstdio/feof.html, feof)).
-Throws: $(D Exception) if the file is not opened.
+Throws: `Exception` if the file is not opened.
*/
@property bool eof() const @trusted pure
{
@@ -729,16 +943,22 @@ Throws: $(D Exception) if the file is not opened.
return .feof(cast(FILE*) _p.handle) != 0;
}
-/** Returns the name of the last opened file, if any.
-If a $(D File) was created with $(LREF tmpfile) and $(LREF wrapFile)
-it has no name.*/
- @property string name() const @safe pure nothrow
+/**
+ Returns the name last used to initialize this `File`, if any.
+
+ Some functions that create or initialize the `File` set the name field to `null`.
+ Examples include $(LREF tmpfile), $(LREF wrapFile), and $(LREF fdopen). See the
+ documentation of those functions for details.
+
+ Returns: The name last used to initialize this this file, or `null` otherwise.
+ */
+ @property string name() const @safe pure nothrow return
{
return _name;
}
/**
-If the file is not opened, returns $(D true). Otherwise, returns
+If the file is not opened, returns `true`. Otherwise, returns
$(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for
the file handle.
*/
@@ -749,7 +969,7 @@ the file handle.
@safe unittest
{
- // Issue 12349
+ // https://issues.dlang.org/show_bug.cgi?id=12349
static import std.file;
auto deleteme = testFilename();
auto f = File(deleteme, "w");
@@ -760,20 +980,21 @@ the file handle.
}
/**
-Detaches from the underlying file. If the sole owner, calls $(D close).
+Detaches from the underlying file. If the sole owner, calls `close`.
-Throws: $(D ErrnoException) on failure if closing the file.
+Throws: `ErrnoException` on failure if closing the file.
*/
- void detach() @safe
+ void detach() @trusted
{
+ import core.stdc.stdlib : free;
+
if (!_p) return;
- if (_p.refs == 1)
- close();
- else
+ scope(exit) _p = null;
+
+ if (atomicOp!"-="(_p.refs, 1) == 0)
{
- assert(_p.refs);
- --_p.refs;
- _p = null;
+ scope(exit) free(_p);
+ closeHandles();
}
}
@@ -797,11 +1018,11 @@ If the file was unopened, succeeds vacuously. Otherwise closes the
file (by calling $(HTTP
cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)),
throwing on error. Even if an exception is thrown, afterwards the $(D
-File) object is empty. This is different from $(D detach) in that it
-always closes the file; consequently, all other $(D File) objects
+File) object is empty. This is different from `detach` in that it
+always closes the file; consequently, all other `File` objects
referring to the same handle will see a closed file henceforth.
-Throws: $(D ErrnoException) on error.
+Throws: `ErrnoException` on error.
*/
void close() @trusted
{
@@ -811,30 +1032,14 @@ Throws: $(D ErrnoException) on error.
if (!_p) return; // succeed vacuously
scope(exit)
{
- assert(_p.refs);
- if (!--_p.refs)
+ if (atomicOp!"-="(_p.refs, 1) == 0)
free(_p);
_p = null; // start a new life
}
if (!_p.handle) return; // Impl is closed by another File
scope(exit) _p.handle = null; // nullify the handle anyway
- version (Posix)
- {
- import core.sys.posix.stdio : pclose;
- import std.format : format;
-
- if (_p.isPopened)
- {
- auto res = pclose(_p.handle);
- errnoEnforce(res != -1,
- "Could not close pipe `"~_name~"'");
- errnoEnforce(res == 0, format("Command returned %d", res));
- return;
- }
- }
- errnoEnforce(.fclose(_p.handle) == 0,
- "Could not close file `"~_name~"'");
+ closeHandles();
}
/**
@@ -849,12 +1054,12 @@ _clearerr) for the file handle.
}
/**
-Flushes the C $(D FILE) buffers.
+Flushes the C `FILE` buffers.
Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush)
for the file handle.
-Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) fails.
+Throws: `Exception` if the file is not opened or if the call to `fflush` fails.
*/
void flush() @trusted
{
@@ -866,7 +1071,7 @@ Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) f
@safe unittest
{
- // Issue 12349
+ // https://issues.dlang.org/show_bug.cgi?id=12349
import std.exception : assertThrown;
static import std.file;
@@ -880,15 +1085,17 @@ Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) f
/**
Forces any data buffered by the OS to be written to disk.
-Call $(LREF flush) before calling this function to flush the C $(D FILE) buffers first.
+Call $(LREF flush) before calling this function to flush the C `FILE` buffers first.
This function calls
$(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx,
-$(D FlushFileBuffers)) on Windows and
+`FlushFileBuffers`) on Windows,
+$(HTTP developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html,
+`F_FULLFSYNC fcntl`) on Darwin and
$(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html,
-$(D fsync)) on POSIX for the file handle.
+`fsync`) on POSIX for the file handle.
-Throws: $(D Exception) if the file is not opened or if the OS call fails.
+Throws: `Exception` if the file is not opened or if the OS call fails.
*/
void sync() @trusted
{
@@ -898,9 +1105,15 @@ Throws: $(D Exception) if the file is not opened or if the OS call fails.
version (Windows)
{
- import core.sys.windows.windows : FlushFileBuffers;
+ import core.sys.windows.winbase : FlushFileBuffers;
wenforce(FlushFileBuffers(windowsHandle), "FlushFileBuffers failed");
}
+ else version (Darwin)
+ {
+ import core.sys.darwin.fcntl : fcntl, F_FULLFSYNC;
+ import std.exception : errnoEnforce;
+ errnoEnforce(fcntl(fileno, F_FULLFSYNC, 0) != -1, "fcntl failed");
+ }
else
{
import core.sys.posix.unistd : fsync;
@@ -914,31 +1127,32 @@ Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the
file handle. The number of items to read and the size of
each item is inferred from the size and type of the input array, respectively.
-Returns: The slice of $(D buffer) containing the data that was actually read.
-This will be shorter than $(D buffer) if EOF was reached before the buffer
+Returns: The slice of `buffer` containing the data that was actually read.
+This will be shorter than `buffer` if EOF was reached before the buffer
could be filled.
-Throws: $(D Exception) if $(D buffer) is empty.
- $(D ErrnoException) if the file is not opened or the call to $(D fread) fails.
+Throws: `Exception` if `buffer` is empty.
+ `ErrnoException` if the file is not opened or the call to `fread` fails.
-$(D rawRead) always reads in binary mode on Windows.
+`rawRead` always reads in binary mode on Windows.
*/
T[] rawRead(T)(T[] buffer)
{
- import std.exception : errnoEnforce;
+ import std.exception : enforce, errnoEnforce;
if (!buffer.length)
throw new Exception("rawRead must take a non-empty buffer");
+ enforce(isOpen, "Attempting to read from an unopened file");
version (Windows)
{
- immutable fd = ._fileno(_p.handle);
- immutable mode = ._setmode(fd, _O_BINARY);
- scope(exit) ._setmode(fd, mode);
+ immutable fd = .fileno(_p.handle);
+ immutable mode = .__setmode(fd, _O_BINARY);
+ scope(exit) .__setmode(fd, mode);
version (DIGITAL_MARS_STDIO)
{
import core.atomic : atomicOp;
- // @@@BUG@@@ 4243
+ // https://issues.dlang.org/show_bug.cgi?id=4243
immutable info = __fhnd_info[fd];
atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
scope(exit) __fhnd_info[fd] = info;
@@ -959,7 +1173,7 @@ $(D rawRead) always reads in binary mode on Windows.
{
static import std.file;
- auto testFile = testFilename();
+ auto testFile = std.file.deleteme();
std.file.write(testFile, "\r\n\n\r\n");
scope(exit) std.file.remove(testFile);
@@ -969,15 +1183,40 @@ $(D rawRead) always reads in binary mode on Windows.
assert(buf == "\r\n\n\r\n");
}
+ // https://issues.dlang.org/show_bug.cgi?id=21729
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ File f;
+ ubyte[1] u;
+ assertThrown(f.rawRead(u));
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=21728
+ @system unittest
+ {
+ static if (__traits(compiles, { import std.process : pipe; })) // not available for iOS
+ {
+ import std.process : pipe;
+ import std.exception : assertThrown;
+
+ auto p = pipe();
+ p.readEnd.close;
+ ubyte[1] u;
+ assertThrown(p.readEnd.rawRead(u));
+ }
+ }
+
/**
Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file
handle. The number of items to write and the size of each
item is inferred from the size and type of the input array, respectively. An
error is thrown if the buffer could not be written in its entirety.
-$(D rawWrite) always writes in binary mode on Windows.
+`rawWrite` always writes in binary mode on Windows.
-Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwrite) fails.
+Throws: `ErrnoException` if the file is not opened or if the call to `fwrite` fails.
*/
void rawWrite(T)(in T[] buffer)
{
@@ -986,21 +1225,37 @@ Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwri
version (Windows)
{
- flush(); // before changing translation mode
- immutable fd = ._fileno(_p.handle);
- immutable mode = ._setmode(fd, _O_BINARY);
- scope(exit) ._setmode(fd, mode);
+ immutable fd = .fileno(_p.handle);
+ immutable oldMode = .__setmode(fd, _O_BINARY);
+
+ if (oldMode != _O_BINARY)
+ {
+ // need to flush the data that was written with the original mode
+ .__setmode(fd, oldMode);
+ flush(); // before changing translation mode .__setmode(fd, _O_BINARY);
+ .__setmode(fd, _O_BINARY);
+ }
+
version (DIGITAL_MARS_STDIO)
{
import core.atomic : atomicOp;
- // @@@BUG@@@ 4243
+ // https://issues.dlang.org/show_bug.cgi?id=4243
immutable info = __fhnd_info[fd];
atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
- scope(exit) __fhnd_info[fd] = info;
+ scope (exit) __fhnd_info[fd] = info;
+ }
+
+ scope (exit)
+ {
+ if (oldMode != _O_BINARY)
+ {
+ flush();
+ .__setmode(fd, oldMode);
+ }
}
- scope(exit) flush(); // before restoring translation mode
}
+
auto result = trustedFwrite(_p.handle, buffer);
if (result == result.max) result = 0;
errnoEnforce(result == buffer.length,
@@ -1014,7 +1269,7 @@ Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwri
{
static import std.file;
- auto testFile = testFilename();
+ auto testFile = std.file.deleteme();
auto f = File(testFile, "w");
scope(exit) std.file.remove(testFile);
@@ -1025,16 +1280,34 @@ Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwri
/**
Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek)
-for the file handle.
+for the file handle to move its position indicator.
-Throws: $(D Exception) if the file is not opened.
- $(D ErrnoException) if the call to $(D fseek) fails.
+Params:
+ offset = Binary files: Number of bytes to offset from origin.$(BR)
+ Text files: Either zero, or a value returned by $(LREF tell).
+ origin = Binary files: Position used as reference for the offset, must be
+ one of $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio),
+ $(REF_ALTTEXT SEEK_CUR, SEEK_CUR, core,stdc,stdio) or
+ $(REF_ALTTEXT SEEK_END, SEEK_END, core,stdc,stdio).$(BR)
+ Text files: Shall necessarily be
+ $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio).
+
+Throws: `Exception` if the file is not opened.
+ `ErrnoException` if the call to `fseek` fails.
*/
void seek(long offset, int origin = SEEK_SET) @trusted
{
import std.conv : to, text;
import std.exception : enforce, errnoEnforce;
+ // Some libc sanitize the whence input (e.g. glibc), but some don't,
+ // e.g. Microsoft runtime crashes on an invalid origin,
+ // and Musl additionally accept SEEK_DATA & SEEK_HOLE (Linux extension).
+ // To provide a consistent behavior cross platform, we use the glibc check
+ // See also https://issues.dlang.org/show_bug.cgi?id=19797
+ enforce(origin == SEEK_SET || origin == SEEK_CUR || origin == SEEK_END,
+ "Invalid `origin` argument passed to `seek`, must be one of: SEEK_SET, SEEK_CUR, SEEK_END");
+
enforce(isOpen, "Attempting to seek() in an unopened file");
version (Windows)
{
@@ -1062,6 +1335,7 @@ Throws: $(D Exception) if the file is not opened.
{
import std.conv : text;
static import std.file;
+ import std.exception;
auto deleteme = testFilename();
auto f = File(deleteme, "w+");
@@ -1084,14 +1358,16 @@ Throws: $(D Exception) if the file is not opened.
// f.rawWrite("abcdefghijklmnopqrstuvwxyz");
// f.seek(-3, SEEK_END);
// assert(f.readln() == "xyz");
+
+ assertThrown(f.seek(0, ushort.max));
}
/**
Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the
managed file handle.
-Throws: $(D Exception) if the file is not opened.
- $(D ErrnoException) if the call to $(D ftell) fails.
+Throws: `Exception` if the file is not opened.
+ `ErrnoException` if the call to `ftell` fails.
*/
@property ulong tell() const @trusted
{
@@ -1121,7 +1397,7 @@ Throws: $(D Exception) if the file is not opened.
import std.conv : text;
static import std.file;
- auto testFile = testFilename();
+ auto testFile = std.file.deleteme();
std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz");
scope(exit) { std.file.remove(testFile); }
@@ -1135,7 +1411,7 @@ Throws: $(D Exception) if the file is not opened.
Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind)
for the file handle.
-Throws: $(D Exception) if the file is not opened.
+Throws: `Exception` if the file is not opened.
*/
void rewind() @safe
{
@@ -1149,8 +1425,8 @@ Throws: $(D Exception) if the file is not opened.
Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for
the file handle.
-Throws: $(D Exception) if the file is not opened.
- $(D ErrnoException) if the call to $(D setvbuf) fails.
+Throws: `Exception` if the file is not opened.
+ `ErrnoException` if the call to `setvbuf` fails.
*/
void setvbuf(size_t size, int mode = _IOFBF) @trusted
{
@@ -1165,8 +1441,8 @@ Throws: $(D Exception) if the file is not opened.
Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html,
_setvbuf) for the file handle.
-Throws: $(D Exception) if the file is not opened.
- $(D ErrnoException) if the call to $(D setvbuf) fails.
+Throws: `Exception` if the file is not opened.
+ `ErrnoException` if the call to `setvbuf` fails.
*/
void setvbuf(void[] buf, int mode = _IOFBF) @trusted
{
@@ -1181,7 +1457,8 @@ Throws: $(D Exception) if the file is not opened.
version (Windows)
{
- import core.sys.windows.windows : ULARGE_INTEGER, OVERLAPPED, BOOL;
+ import core.sys.windows.winbase : OVERLAPPED;
+ import core.sys.windows.winnt : BOOL, ULARGE_INTEGER;
private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
Flags flags)
@@ -1199,9 +1476,9 @@ Throws: $(D Exception) if the file is not opened.
liLength.HighPart, &overlapped);
}
- private static T wenforce(T)(T cond, string str)
+ private static T wenforce(T)(T cond, lazy string str)
{
- import core.sys.windows.windows : GetLastError;
+ import core.sys.windows.winbase : GetLastError;
import std.windows.syserror : sysErrorString;
if (cond) return cond;
@@ -1230,14 +1507,14 @@ Throws: $(D Exception) if the file is not opened.
/**
Locks the specified file segment. If the file segment is already locked
by another process, waits until the existing lock is released.
-If both $(D start) and $(D length) are zero, the entire file is locked.
+If both `start` and `length` are zero, the entire file is locked.
-Locks created using $(D lock) and $(D tryLock) have the following properties:
+Locks created using `lock` and `tryLock` have the following properties:
$(UL
$(LI All locks are automatically released when the process terminates.)
$(LI Locks are not inherited by child processes.)
$(LI Closing a file will release all locks associated with the file. On POSIX,
- even locks acquired via a different $(D File) will be released as well.)
+ even locks acquired via a different `File` will be released as well.)
$(LI Not all NFS implementations correctly implement file locking.)
)
*/
@@ -1259,7 +1536,7 @@ $(UL
else
version (Windows)
{
- import core.sys.windows.windows : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK;
+ import core.sys.windows.winbase : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK;
immutable type = lockType == LockType.readWrite ?
LOCKFILE_EXCLUSIVE_LOCK : 0;
wenforce(lockImpl!LockFileEx(start, length, type),
@@ -1271,8 +1548,8 @@ $(UL
/**
Attempts to lock the specified file segment.
-If both $(D start) and $(D length) are zero, the entire file is locked.
-Returns: $(D true) if the lock was successful, and $(D false) if the
+If both `start` and `length` are zero, the entire file is locked.
+Returns: `true` if the lock was successful, and `false` if the
specified file segment was already locked.
*/
bool tryLock(LockType lockType = LockType.readWrite,
@@ -1297,8 +1574,9 @@ specified file segment was already locked.
else
version (Windows)
{
- import core.sys.windows.windows : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK,
- ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, LOCKFILE_FAIL_IMMEDIATELY;
+ import core.sys.windows.winbase : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK,
+ LOCKFILE_FAIL_IMMEDIATELY;
+ import core.sys.windows.winerror : ERROR_IO_PENDING, ERROR_LOCK_VIOLATION;
immutable type = lockType == LockType.readWrite
? LOCKFILE_EXCLUSIVE_LOCK : 0;
immutable res = lockImpl!LockFileEx(start, length,
@@ -1331,7 +1609,7 @@ Removes the lock over the specified file segment.
else
version (Windows)
{
- import core.sys.windows.windows : UnlockFileEx;
+ import core.sys.windows.winbase : UnlockFileEx;
wenforce(lockImpl!UnlockFileEx(start, length),
"Could not remove lock for file `"~_name~"'");
}
@@ -1361,6 +1639,8 @@ Removes the lock over the specified file segment.
version (Posix)
@system unittest
{
+ static if (__traits(compiles, { import std.process : spawnProcess; }))
+ {
static import std.file;
auto deleteme = testFilename();
scope(exit) std.file.remove(deleteme);
@@ -1369,18 +1649,17 @@ Removes the lock over the specified file segment.
// the same process. fork() is used to create a second process.
static void runForked(void delegate() code)
{
- import core.stdc.stdlib : exit;
- import core.sys.posix.sys.wait : wait;
- import core.sys.posix.unistd : fork;
+ import core.sys.posix.sys.wait : waitpid;
+ import core.sys.posix.unistd : fork, _exit;
int child, status;
if ((child = fork()) == 0)
{
code();
- exit(0);
+ _exit(0);
}
else
{
- assert(wait(&status) != -1);
+ assert(waitpid(child, &status, 0) != -1);
assert(status == 0, "Fork crashed");
}
}
@@ -1413,52 +1692,64 @@ Removes the lock over the specified file segment.
g.unlock();
});
f.unlock();
- }
+ } // static if
+ } // unittest
/**
Writes its arguments in text format to the file.
-Throws: $(D Exception) if the file is not opened.
- $(D ErrnoException) on an error writing to the file.
+Throws: `Exception` if the file is not opened.
+ `ErrnoException` on an error writing to the file.
*/
void write(S...)(S args)
{
import std.traits : isBoolean, isIntegral, isAggregateType;
+ import std.utf : UTFException;
auto w = lockingTextWriter();
foreach (arg; args)
{
- alias A = typeof(arg);
- static if (isAggregateType!A || is(A == enum))
+ try
{
- import std.format : formattedWrite;
+ alias A = typeof(arg);
+ static if (isAggregateType!A || is(A == enum))
+ {
+ import std.format.write : formattedWrite;
- formattedWrite(w, "%s", arg);
- }
- else static if (isSomeString!A)
- {
- put(w, arg);
- }
- else static if (isIntegral!A)
- {
- import std.conv : toTextRange;
+ formattedWrite(w, "%s", arg);
+ }
+ else static if (isSomeString!A)
+ {
+ put(w, arg);
+ }
+ else static if (isIntegral!A)
+ {
+ import std.conv : toTextRange;
- toTextRange(arg, w);
- }
- else static if (isBoolean!A)
- {
- put(w, arg ? "true" : "false");
- }
- else static if (isSomeChar!A)
- {
- put(w, arg);
+ toTextRange(arg, w);
+ }
+ else static if (isBoolean!A)
+ {
+ put(w, arg ? "true" : "false");
+ }
+ else static if (isSomeChar!A)
+ {
+ put(w, arg);
+ }
+ else
+ {
+ import std.format.write : formattedWrite;
+
+ // Most general case
+ formattedWrite(w, "%s", arg);
+ }
}
- else
+ catch (UTFException e)
{
- import std.format : formattedWrite;
-
- // Most general case
- formattedWrite(w, "%s", arg);
+ /* Reset the writer so that it doesn't throw another
+ UTFException on destruction. */
+ w.highSurrogate = '\0';
+ throw e;
}
}
}
@@ -1466,8 +1757,8 @@ Throws: $(D Exception) if the file is not opened.
/**
Writes its arguments in text format to the file, followed by a newline.
-Throws: $(D Exception) if the file is not opened.
- $(D ErrnoException) on an error writing to the file.
+Throws: `Exception` if the file is not opened.
+ `ErrnoException` on an error writing to the file.
*/
void writeln(S...)(S args)
{
@@ -1479,13 +1770,13 @@ Writes its arguments in text format to the file, according to the
format string fmt.
Params:
-fmt = The $(LINK2 std_format.html#format-string, format string).
+fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
When passed as a compile-time argument, the string will be statically checked
against the argument types passed.
args = Items to write.
-Throws: $(D Exception) if the file is not opened.
- $(D ErrnoException) on an error writing to the file.
+Throws: `Exception` if the file is not opened.
+ `ErrnoException` on an error writing to the file.
*/
void writef(alias fmt, A...)(A args)
if (isSomeString!(typeof(fmt)))
@@ -1500,7 +1791,7 @@ Throws: $(D Exception) if the file is not opened.
/// ditto
void writef(Char, A...)(in Char[] fmt, A args)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
formattedWrite(lockingTextWriter(), fmt, args);
}
@@ -1519,7 +1810,7 @@ Throws: $(D Exception) if the file is not opened.
/// ditto
void writefln(Char, A...)(in Char[] fmt, A args)
{
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
auto w = lockingTextWriter();
formattedWrite(w, fmt, args);
@@ -1530,12 +1821,12 @@ Throws: $(D Exception) if the file is not opened.
Read line from the file handle and return it as a specified type.
This version manages its own read buffer, which means one memory allocation per call. If you are not
-retaining a reference to the read data, consider the $(D File.readln(buf)) version, which may offer
+retaining a reference to the read data, consider the `File.readln(buf)` version, which may offer
better performance as it can reuse its read buffer.
Params:
- S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
- terminator = Line terminator (by default, $(D '\n')).
+ S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
+ terminator = Line terminator (by default, `'\n'`).
Note:
String terminators are not supported due to ambiguity with readln(buf) below.
@@ -1544,7 +1835,7 @@ Returns:
The line that was read, including the line terminator character.
Throws:
- $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
+ `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
Example:
---
@@ -1576,8 +1867,8 @@ void main()
auto deleteme = testFilename();
std.file.write(deleteme, "hello\nworld\n");
scope(exit) std.file.remove(deleteme);
- foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
- {
+ static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
+ {{
auto witness = [ "hello\n", "world\n" ];
auto f = File(deleteme);
uint i = 0;
@@ -1588,7 +1879,7 @@ void main()
assert(equal(buf, witness[i++]));
}
assert(i == witness.length);
- }
+ }}
}
@system unittest
@@ -1600,17 +1891,17 @@ void main()
std.file.write(deleteme, "cześć \U0002000D");
scope(exit) std.file.remove(deleteme);
uint[] lengths = [12,8,7];
- foreach (uint i, C; Tuple!(char, wchar, dchar).Types)
- {
+ static foreach (uint i, C; Tuple!(char, wchar, dchar).Types)
+ {{
immutable(C)[] witness = "cześć \U0002000D";
auto buf = File(deleteme).readln!(immutable(C)[])();
assert(buf.length == lengths[i]);
assert(buf == witness);
- }
+ }}
}
/**
-Read line from the file handle and write it to $(D buf[]), including
+Read line from the file handle and write it to `buf[]`, including
terminating character.
This can be faster than $(D line = File.readln()) because you can reuse
@@ -1619,15 +1910,16 @@ must copy the previous contents if you wish to retain them.
Params:
buf = Buffer used to store the resulting line data. buf is
-resized as necessary.
-terminator = Line terminator (by default, $(D '\n')). Use
+enlarged if necessary, then set to the slice exactly containing the line.
+terminator = Line terminator (by default, `'\n'`). Use
$(REF newline, std,ascii) for portability (unless the file was opened in
text mode).
Returns:
-0 for end of file, otherwise number of characters read
+0 for end of file, otherwise number of characters read.
+The return value will always be equal to `buf.length`.
-Throws: $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode
+Throws: `StdioException` on I/O error, or `UnicodeException` on Unicode
conversion error.
Example:
@@ -1635,6 +1927,7 @@ Example:
// Read lines from `stdin` into a string
// Ignore lines starting with '#'
// Write the string to `stdout`
+import std.stdio;
void main()
{
@@ -1654,17 +1947,18 @@ void main()
---
This method can be more efficient than the one in the previous example
-because $(D stdin.readln(buf)) reuses (if possible) memory allocated
-for $(D buf), whereas $(D line = stdin.readln()) makes a new memory allocation
+because `stdin.readln(buf)` reuses (if possible) memory allocated
+for `buf`, whereas $(D line = stdin.readln()) makes a new memory allocation
for every line.
-For even better performance you can help $(D readln) by passing in a
+For even better performance you can help `readln` by passing in a
large buffer to avoid memory reallocations. This can be done by reusing the
-largest buffer returned by $(D readln):
+largest buffer returned by `readln`:
Example:
---
// Read lines from `stdin` and count words
+import std.array, std.stdio;
void main()
{
@@ -1706,14 +2000,19 @@ is recommended if you want to process a complete file.
}
else
{
- // TODO: optimize this
string s = readln(terminator);
- buf.length = 0;
- if (!s.length) return 0;
- foreach (C c; s)
+ if (!s.length)
{
- buf ~= c;
+ buf = buf[0 .. 0];
+ return 0;
}
+
+ import std.utf : codeLength;
+ buf.length = codeLength!C(s);
+ size_t idx;
+ foreach (C c; s)
+ buf[idx++] = c;
+
return buf.length;
}
}
@@ -1736,7 +2035,8 @@ is recommended if you want to process a complete file.
assert(buffer[beyond] == 'a');
}
- @system unittest // bugzilla 15293
+ // https://issues.dlang.org/show_bug.cgi?id=15293
+ @system unittest
{
// @system due to readln
static import std.file;
@@ -1816,10 +2116,13 @@ is recommended if you want to process a complete file.
/**
* Reads formatted _data from the file using $(REF formattedRead, std,_format).
* Params:
- * format = The $(LINK2 std_format.html#_format-string, _format string).
+ * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
* When passed as a compile-time argument, the string will be statically checked
* against the argument types passed.
* data = Items to be read.
+ * Returns:
+ * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
+ * this number will be less than the number of variables provided.
* Example:
----
// test.d
@@ -1854,9 +2157,9 @@ $(CONSOLE
}
/// ditto
- uint readf(Data...)(in char[] format, auto ref Data data)
+ uint readf(Data...)(scope const(char)[] format, auto ref Data data)
{
- import std.format : formattedRead;
+ import std.format.read : formattedRead;
assert(isOpen);
auto input = LockingTextReader(this);
@@ -1868,7 +2171,7 @@ $(CONSOLE
{
static import std.file;
- auto deleteme = testFilename();
+ auto deleteme = std.file.deleteme();
std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
scope(exit) std.file.remove(deleteme);
string s;
@@ -1899,7 +2202,7 @@ $(CONSOLE
f.readf("%s\n", &s);
assert(s == "world", "["~s~"]");
- // Issue 11698
+ // https://issues.dlang.org/show_bug.cgi?id=11698
bool b1, b2;
f.readf("%s\n%s\n", &b1, &b2);
assert(b1 == true && b2 == false);
@@ -1920,13 +2223,14 @@ $(CONSOLE
assert(s1 == "hello");
assert(s2 == "world");
- // Issue 11698
+ // https://issues.dlang.org/show_bug.cgi?id=11698
bool b1, b2;
f.readf("%s\n%s\n", &b1, b2);
assert(b1 == true && b2 == false);
}
- // Issue 12260 - Nice error of std.stdio.readf with newlines
+ // Nice error of std.stdio.readf with newlines
+ // https://issues.dlang.org/show_bug.cgi?id=12260
@system unittest
{
static import std.file;
@@ -1958,7 +2262,7 @@ $(CONSOLE
}
/**
-Unsafe function that wraps an existing $(D FILE*). The resulting $(D
+Unsafe function that wraps an existing `FILE*`. The resulting $(D
File) never takes the initiative in closing the file.
Note that the created file has no $(LREF name)*/
/*private*/ static File wrapFile(FILE* f) @safe
@@ -1970,7 +2274,7 @@ Note that the created file has no $(LREF name)*/
}
/**
-Returns the $(D FILE*) corresponding to this object.
+Returns the `FILE*` corresponding to this object.
*/
FILE* getFP() @safe pure
{
@@ -1999,7 +2303,7 @@ Returns the file number corresponding to this object.
}
/**
-Returns the underlying operating system $(D HANDLE) (Windows only).
+Returns the underlying operating system `HANDLE` (Windows only).
*/
version (StdDdoc)
@property HANDLE windowsHandle();
@@ -2020,7 +2324,7 @@ Range that reads one line at a time. Returned by $(LREF byLine).
Allows to directly use range operations on lines of a file.
*/
- struct ByLine(Char, Terminator)
+ private struct ByLineImpl(Char, Terminator)
{
private:
import std.typecons : RefCounted, RefCountedAutoInitialize;
@@ -2068,6 +2372,7 @@ Allows to directly use range operations on lines of a file.
Char[] buffer;
Terminator terminator;
KeepTerminator keepTerminator;
+ bool haveLine;
public:
this(File f, KeepTerminator kt, Terminator terminator)
@@ -2075,22 +2380,32 @@ Allows to directly use range operations on lines of a file.
file = f;
this.terminator = terminator;
keepTerminator = kt;
- popFront();
}
// Range primitive implementations.
@property bool empty()
{
+ needLine();
return line is null;
}
@property Char[] front()
{
+ needLine();
return line;
}
void popFront()
{
+ needLine();
+ haveLine = false;
+ }
+
+ private:
+ void needLine()
+ {
+ if (haveLine)
+ return;
import std.algorithm.searching : endsWith;
assert(file.isOpen);
line = buffer;
@@ -2112,36 +2427,37 @@ Allows to directly use range operations on lines of a file.
else static if (isArray!Terminator)
{
static assert(
- is(Unqual!(ElementEncodingType!Terminator) == Char));
+ is(immutable ElementEncodingType!Terminator == immutable Char));
const tlen = terminator.length;
}
else
static assert(false);
line = line[0 .. line.length - tlen];
}
+ haveLine = true;
}
}
}
/**
-Returns an input range set up to read from the file handle one line
-at a time.
+Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+set up to read from the file handle one line at a time.
-The element type for the range will be $(D Char[]). Range primitives
-may throw $(D StdioException) on I/O error.
+The element type for the range will be `Char[]`. Range primitives
+may throw `StdioException` on I/O error.
Note:
-Each $(D front) will not persist after $(D
+Each `front` will not persist after $(D
popFront) is called, so the caller must copy its contents (e.g. by
-calling $(D to!string)) when retention is needed. If the caller needs
+calling `to!string`) when retention is needed. If the caller needs
to retain a copy of every line, use the $(LREF byLineCopy) function
instead.
Params:
-Char = Character type for each line, defaulting to $(D char).
-keepTerminator = Use $(D Yes.keepTerminator) to include the
+Char = Character type for each line, defaulting to `char`.
+keepTerminator = Use `Yes.keepTerminator` to include the
terminator at the end of each line.
-terminator = Line separator ($(D '\n') by default). Use
+terminator = Line separator (`'\n'` by default). Use
$(REF newline, std,ascii) for portability (unless the file was opened in
text mode).
@@ -2180,7 +2496,7 @@ void main()
}
----
Notice that neither example accesses the line data returned by
-$(D front) after the corresponding $(D popFront) call is made (because
+`front` after the corresponding `popFront` call is made (because
the contents may well have changed).
*/
auto byLine(Terminator = char, Char = char)
@@ -2188,15 +2504,15 @@ the contents may well have changed).
Terminator terminator = '\n')
if (isScalarType!Terminator)
{
- return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
+ return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
}
/// ditto
auto byLine(Terminator, Char = char)
(KeepTerminator keepTerminator, Terminator terminator)
- if (is(Unqual!(ElementEncodingType!Terminator) == Char))
+ if (is(immutable ElementEncodingType!Terminator == immutable Char))
{
- return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
+ return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
}
@system unittest
@@ -2207,13 +2523,27 @@ the contents may well have changed).
scope(success) std.file.remove(deleteme);
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(char, wchar, dchar))
- {
+ static foreach (T; AliasSeq!(char, wchar, dchar))
+ {{
auto blc = File(deleteme).byLine!(T, T);
assert(blc.front == "hi");
// check front is cached
assert(blc.front is blc.front);
- }
+ }}
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=19980
+ @system unittest
+ {
+ static import std.file;
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "Line 1\nLine 2\nLine 3\n");
+ scope(success) std.file.remove(deleteme);
+
+ auto f = File(deleteme);
+ f.byLine();
+ f.byLine();
+ assert(f.byLine().front == "Line 1");
}
private struct ByLineCopy(Char, Terminator)
@@ -2253,14 +2583,14 @@ the contents may well have changed).
private struct ByLineCopyImpl(Char, Terminator)
{
- ByLine!(Unqual!Char, Terminator).Impl impl;
+ ByLineImpl!(Unqual!Char, Terminator).Impl impl;
bool gotFront;
Char[] line;
public:
this(File f, KeepTerminator kt, Terminator terminator)
{
- impl = ByLine!(Unqual!Char, Terminator).Impl(f, kt, terminator);
+ impl = ByLineImpl!(Unqual!Char, Terminator).Impl(f, kt, terminator);
}
@property bool empty()
@@ -2286,21 +2616,22 @@ the contents may well have changed).
}
/**
-Returns an input range set up to read from the file handle one line
-at a time. Each line will be newly allocated. $(D front) will cache
+Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+set up to read from the file handle one line
+at a time. Each line will be newly allocated. `front` will cache
its value to allow repeated calls without unnecessary allocations.
Note: Due to caching byLineCopy can be more memory-efficient than
-$(D File.byLine.map!idup).
+`File.byLine.map!idup`.
-The element type for the range will be $(D Char[]). Range
-primitives may throw $(D StdioException) on I/O error.
+The element type for the range will be `Char[]`. Range
+primitives may throw `StdioException` on I/O error.
Params:
Char = Character type for each line, defaulting to $(D immutable char).
-keepTerminator = Use $(D Yes.keepTerminator) to include the
+keepTerminator = Use `Yes.keepTerminator` to include the
terminator at the end of each line.
-terminator = Line separator ($(D '\n') by default). Use
+terminator = Line separator (`'\n'` by default). Use
$(REF newline, std,ascii) for portability (unless the file was opened in
text mode).
@@ -2332,7 +2663,7 @@ $(REF readText, std,file)
/// ditto
auto byLineCopy(Terminator, Char = immutable char)
(KeepTerminator keepTerminator, Terminator terminator)
- if (is(Unqual!(ElementEncodingType!Terminator) == Unqual!Char))
+ if (is(immutable ElementEncodingType!Terminator == immutable Char))
{
return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
}
@@ -2393,7 +2724,7 @@ $(REF readText, std,file)
}
assert(i == witness.length, text(i, " != ", witness.length));
- // Issue 11830
+ // https://issues.dlang.org/show_bug.cgi?id=11830
auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));
@@ -2453,9 +2784,9 @@ $(REF readText, std,file)
auto file = File.tmpfile();
file.write("1\n2\n3\n");
- // bug 9599
+ // https://issues.dlang.org/show_bug.cgi?id=9599
file.rewind();
- File.ByLine!(char, char) fbl = file.byLine();
+ File.ByLineImpl!(char, char) fbl = file.byLine();
auto fbl2 = fbl;
assert(fbl.front == "1");
assert(fbl.front is fbl2.front);
@@ -2489,10 +2820,10 @@ $(REF readText, std,file)
}
/**
- Creates an input range set up to parse one line at a time from the file
- into a tuple.
+ Creates an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ set up to parse one line at a time from the file into a tuple.
- Range primitives may throw $(D StdioException) on I/O error.
+ Range primitives may throw `StdioException` on I/O error.
Params:
format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format)
@@ -2507,9 +2838,9 @@ $(REF readText, std,file)
*/
template byRecord(Fields...)
{
- ByRecord!(Fields) byRecord(string format)
+ auto byRecord(string format)
{
- return typeof(return)(this, format);
+ return ByRecordImpl!(Fields)(this, format);
}
}
@@ -2520,7 +2851,7 @@ $(REF readText, std,file)
import std.typecons : tuple;
// prepare test file
- auto testFile = testFilename();
+ auto testFile = std.file.deleteme();
scope(failure) printf("Failed test at line %d\n", __LINE__);
std.file.write(testFile, "1 2\n4 1\n5 100");
scope(exit) std.file.remove(testFile);
@@ -2540,7 +2871,7 @@ $(REF readText, std,file)
/*
* Range that reads a chunk at a time.
*/
- struct ByChunk
+ private struct ByChunkImpl
{
private:
File file_;
@@ -2568,7 +2899,7 @@ $(REF readText, std,file)
prime();
}
- // $(D ByChunk)'s input range primitive operations.
+ // `ByChunk`'s input range primitive operations.
@property nothrow
bool empty() const
{
@@ -2602,11 +2933,11 @@ $(REF readText, std,file)
}
/**
-Returns an input range set up to read from the file handle a chunk at a
-time.
+Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+set up to read from the file handle a chunk at a time.
-The element type for the range will be $(D ubyte[]). Range primitives
-may throw $(D StdioException) on I/O error.
+The element type for the range will be `ubyte[]`. Range primitives
+may throw `StdioException` on I/O error.
Example:
---------
@@ -2621,7 +2952,7 @@ void main()
---------
The parameter may be a number (as shown in the example above) dictating the
-size of each chunk. Alternatively, $(D byChunk) accepts a
+size of each chunk. Alternatively, `byChunk` accepts a
user-provided buffer that it uses directly.
Example:
@@ -2637,14 +2968,14 @@ void main()
---------
In either case, the content of the buffer is reused across calls. That means
-$(D front) will not persist after $(D popFront) is called, so if retention is
-needed, the caller must copy its contents (e.g. by calling $(D buffer.dup)).
+`front` will not persist after `popFront` is called, so if retention is
+needed, the caller must copy its contents (e.g. by calling `buffer.dup`).
-In the example above, $(D buffer.length) is 4096 for all iterations, except
-for the last one, in which case $(D buffer.length) may be less than 4096 (but
+In the example above, `buffer.length` is 4096 for all iterations, except
+for the last one, in which case `buffer.length` may be less than 4096 (but
always greater than zero).
-With the mentioned limitations, $(D byChunk) works with any algorithm
+With the mentioned limitations, `byChunk` works with any algorithm
compatible with input ranges.
Example:
@@ -2671,21 +3002,21 @@ void main()
}
---
-Returns: A call to $(D byChunk) returns a range initialized with the $(D File)
+Returns: A call to `byChunk` returns a range initialized with the `File`
object and the appropriate buffer.
Throws: If the user-provided size is zero or the user-provided buffer
-is empty, throws an $(D Exception). In case of an I/O error throws
-$(D StdioException).
+is empty, throws an `Exception`. In case of an I/O error throws
+`StdioException`.
*/
auto byChunk(size_t chunkSize)
{
- return ByChunk(this, chunkSize);
+ return ByChunkImpl(this, chunkSize);
}
/// Ditto
- ByChunk byChunk(ubyte[] buffer)
+ auto byChunk(ubyte[] buffer)
{
- return ByChunk(this, buffer);
+ return ByChunkImpl(this, buffer);
}
@system unittest
@@ -2740,53 +3071,88 @@ $(D StdioException).
// Note: This was documented until 2013/08
/*
-$(D Range) that locks the file and allows fast writing to it.
+`Range` that locks the file and allows fast writing to it.
*/
struct LockingTextWriter
{
private:
import std.range.primitives : ElementType, isInfinite, isInputRange;
- // the shared file handle
- FILE* fps_;
+ // Access the FILE* handle through the 'file_' member
+ // to keep the object alive through refcounting
+ File file_;
- // the unshared version of fps
- @property _iobuf* handle_() @trusted { return cast(_iobuf*) fps_; }
+ // the unshared version of FILE* handle, extracted from the File object
+ @property _iobuf* handle_() @trusted { return cast(_iobuf*) file_._p.handle; }
// the file's orientation (byte- or wide-oriented)
int orientation_;
+
+ // Buffers for when we need to transcode.
+ wchar highSurrogate = '\0'; // '\0' indicates empty
+ void highSurrogateShouldBeEmpty() @safe
+ {
+ import std.utf : UTFException;
+ if (highSurrogate != '\0')
+ throw new UTFException("unpaired surrogate UTF-16 value");
+ }
+ char[4] rbuf8;
+ size_t rbuf8Filled = 0;
public:
this(ref File f) @trusted
{
- import core.stdc.wchar_ : fwide;
import std.exception : enforce;
enforce(f._p && f._p.handle, "Attempting to write to closed File");
- fps_ = f._p.handle;
- orientation_ = fwide(fps_, 0);
- FLOCK(fps_);
+ file_ = f;
+ FILE* fps = f._p.handle;
+
+ version (MICROSOFT_STDIO)
+ {
+ // Microsoft doesn't implement fwide. Instead, there's the
+ // concept of ANSI/UNICODE mode. fputc doesn't work in UNICODE
+ // mode; fputwc has to be used. So that essentially means
+ // "wide-oriented" for us.
+ immutable int mode = __setmode(f.fileno, _O_TEXT);
+ // Set some arbitrary mode to obtain the previous one.
+ __setmode(f.fileno, mode); // Restore previous mode.
+ if (mode & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT))
+ {
+ orientation_ = 1; // wide
+ }
+ }
+ else
+ {
+ import core.stdc.wchar_ : fwide;
+ orientation_ = fwide(fps, 0);
+ }
+
+ _FLOCK(fps);
}
~this() @trusted
{
- if (fps_)
+ if (auto p = file_._p)
{
- FUNLOCK(fps_);
- fps_ = null;
+ if (p.handle) _FUNLOCK(p.handle);
}
+ file_ = File.init;
+ /* Destroy file_ before possibly throwing. Else it wouldn't be
+ destroyed, and its reference count would be wrong. */
+ highSurrogateShouldBeEmpty();
}
this(this) @trusted
{
- if (fps_)
+ if (auto p = file_._p)
{
- FLOCK(fps_);
+ if (p.handle) _FLOCK(p.handle);
}
}
/// Range primitive implementations.
- void put(A)(A writeme)
- if ((isSomeChar!(Unqual!(ElementType!A)) ||
+ void put(A)(scope A writeme)
+ if ((isSomeChar!(ElementType!A) ||
is(ElementType!A : const(ubyte))) &&
isInputRange!A &&
!isInfinite!A)
@@ -2801,66 +3167,106 @@ $(D Range) that locks the file and allows fast writing to it.
{
//file.write(writeme); causes infinite recursion!!!
//file.rawWrite(writeme);
- auto result = trustedFwrite(fps_, writeme);
+ auto result = trustedFwrite(file_._p.handle, writeme);
if (result != writeme.length) errnoEnforce(0);
return;
}
}
// put each element in turn.
- alias Elem = Unqual!(ElementType!A);
- foreach (Elem c; writeme)
+ foreach (c; writeme)
{
put(c);
}
}
/// ditto
- void put(C)(C c) @safe if (isSomeChar!C || is(C : const(ubyte)))
+ void put(C)(scope C c) @safe if (isSomeChar!C || is(C : const(ubyte)))
{
import std.traits : Parameters;
+ import std.utf : decodeFront, encode, stride;
static auto trustedFPUTC(int ch, _iobuf* h) @trusted
{
- return FPUTC(ch, h);
+ return _FPUTC(ch, h);
}
- static auto trustedFPUTWC(Parameters!FPUTWC[0] ch, _iobuf* h) @trusted
+ static auto trustedFPUTWC(Parameters!_FPUTWC[0] ch, _iobuf* h) @trusted
{
- return FPUTWC(ch, h);
+ return _FPUTWC(ch, h);
}
static if (c.sizeof == 1)
{
- // simple char
+ highSurrogateShouldBeEmpty();
if (orientation_ <= 0) trustedFPUTC(c, handle_);
- else trustedFPUTWC(c, handle_);
+ else if (c <= 0x7F) trustedFPUTWC(c, handle_);
+ else if (c >= 0b1100_0000) // start byte of multibyte sequence
+ {
+ rbuf8[0] = c;
+ rbuf8Filled = 1;
+ }
+ else // continuation byte of multibyte sequence
+ {
+ rbuf8[rbuf8Filled] = c;
+ ++rbuf8Filled;
+ if (stride(rbuf8[]) == rbuf8Filled) // sequence is complete
+ {
+ char[] str = rbuf8[0 .. rbuf8Filled];
+ immutable dchar d = decodeFront(str);
+ wchar_t[4 / wchar_t.sizeof] wbuf;
+ immutable size = encode(wbuf, d);
+ foreach (i; 0 .. size)
+ trustedFPUTWC(wbuf[i], handle_);
+ rbuf8Filled = 0;
+ }
+ }
}
else static if (c.sizeof == 2)
{
- import std.utf : encode, UseReplacementDchar;
+ import std.utf : decode;
- if (orientation_ <= 0)
+ if (c <= 0x7F)
{
- if (c <= 0x7F)
+ highSurrogateShouldBeEmpty();
+ if (orientation_ <= 0) trustedFPUTC(c, handle_);
+ else trustedFPUTWC(c, handle_);
+ }
+ else if (0xD800 <= c && c <= 0xDBFF) // high surrogate
+ {
+ highSurrogateShouldBeEmpty();
+ highSurrogate = c;
+ }
+ else // standalone or low surrogate
+ {
+ dchar d = c;
+ if (highSurrogate != '\0')
{
- trustedFPUTC(c, handle_);
+ immutable wchar[2] rbuf = [highSurrogate, c];
+ size_t index = 0;
+ d = decode(rbuf[], index);
+ highSurrogate = 0;
+ }
+ if (orientation_ <= 0)
+ {
+ char[4] wbuf;
+ immutable size = encode(wbuf, d);
+ foreach (i; 0 .. size)
+ trustedFPUTC(wbuf[i], handle_);
}
else
{
- char[4] buf;
- immutable size = encode!(UseReplacementDchar.yes)(buf, c);
- foreach (i ; 0 .. size)
- trustedFPUTC(buf[i], handle_);
+ wchar_t[4 / wchar_t.sizeof] wbuf;
+ immutable size = encode(wbuf, d);
+ foreach (i; 0 .. size)
+ trustedFPUTWC(wbuf[i], handle_);
}
- }
- else
- {
- trustedFPUTWC(c, handle_);
+ rbuf8Filled = 0;
}
}
else // 32-bit characters
{
import std.utf : encode;
+ highSurrogateShouldBeEmpty();
if (orientation_ <= 0)
{
if (c <= 0x7F)
@@ -2884,21 +3290,21 @@ $(D Range) that locks the file and allows fast writing to it.
assert(isValidDchar(c));
if (c <= 0xFFFF)
{
- trustedFPUTWC(c, handle_);
+ trustedFPUTWC(cast(wchar_t) c, handle_);
}
else
{
- trustedFPUTWC(cast(wchar)
+ trustedFPUTWC(cast(wchar_t)
((((c - 0x10000) >> 10) & 0x3FF)
+ 0xD800), handle_);
- trustedFPUTWC(cast(wchar)
+ trustedFPUTWC(cast(wchar_t)
(((c - 0x10000) & 0x3FF) + 0xDC00),
handle_);
}
}
else version (Posix)
{
- trustedFPUTWC(c, handle_);
+ trustedFPUTWC(cast(wchar_t) c, handle_);
}
else
{
@@ -2909,10 +3315,24 @@ $(D Range) that locks the file and allows fast writing to it.
}
}
-/** Returns an output range that locks the file and allows fast writing to it.
-
-See $(LREF byChunk) for an example.
-*/
+ /**
+ * Output range which locks the file when created, and unlocks the file when it goes
+ * out of scope.
+ *
+ * Returns: An $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ * which accepts string types, `ubyte[]`, individual character types, and
+ * individual `ubyte`s.
+ *
+ * Note: Writing either arrays of `char`s or `ubyte`s is faster than
+ * writing each character individually from a range. For large amounts of data,
+ * writing the contents in chunks using an intermediary array can result
+ * in a speed increase.
+ *
+ * Throws: $(REF UTFException, std, utf) if the data given is a `char` range
+ * and it contains malformed UTF data.
+ *
+ * See_Also: $(LREF byChunk) for an example.
+ */
auto lockingTextWriter() @safe
{
return LockingTextWriter(this);
@@ -2925,7 +3345,9 @@ See $(LREF byChunk) for an example.
{
import std.traits : hasIndirections;
private:
- FILE* fps;
+ // Access the FILE* handle through the 'file_' member
+ // to keep the object alive through refcounting
+ File file_;
string name;
version (Windows)
@@ -2935,52 +3357,54 @@ See $(LREF byChunk) for an example.
ubyte oldInfo;
}
- package:
- this(ref File f)
+ public:
+ // Don't use this, but `File.lockingBinaryWriter()` instead.
+ // Must be public for RefCounted and emplace() in druntime.
+ this(scope ref File f)
{
import std.exception : enforce;
-
+ file_ = f;
enforce(f._p && f._p.handle);
name = f._name;
- fps = f._p.handle;
+ FILE* fps = f._p.handle;
static if (locking)
- FLOCK(fps);
+ _FLOCK(fps);
version (Windows)
{
.fflush(fps); // before changing translation mode
- fd = ._fileno(fps);
- oldMode = ._setmode(fd, _O_BINARY);
+ fd = .fileno(fps);
+ oldMode = .__setmode(fd, _O_BINARY);
version (DIGITAL_MARS_STDIO)
{
import core.atomic : atomicOp;
- // @@@BUG@@@ 4243
+ // https://issues.dlang.org/show_bug.cgi?id=4243
oldInfo = __fhnd_info[fd];
atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
}
}
}
- public:
~this()
{
- if (!fps)
+ if (!file_._p || !file_._p.handle)
return;
+ FILE* fps = file_._p.handle;
+
version (Windows)
{
.fflush(fps); // before restoring translation mode
version (DIGITAL_MARS_STDIO)
{
- // @@@BUG@@@ 4243
+ // https://issues.dlang.org/show_bug.cgi?id=4243
__fhnd_info[fd] = oldInfo;
}
- ._setmode(fd, oldMode);
+ .__setmode(fd, oldMode);
}
- FUNLOCK(fps);
- fps = null;
+ _FUNLOCK(fps);
}
void rawWrite(T)(in T[] buffer)
@@ -2988,7 +3412,7 @@ See $(LREF byChunk) for an example.
import std.conv : text;
import std.exception : errnoEnforce;
- auto result = trustedFwrite(fps, buffer);
+ auto result = trustedFwrite(file_._p.handle, buffer);
if (result == result.max) result = 0;
errnoEnforce(result == buffer.length,
text("Wrote ", result, " instead of ", buffer.length,
@@ -3004,21 +3428,21 @@ See $(LREF byChunk) for an example.
{
this(this)
{
- if (fps)
+ if (auto p = file_._p)
{
- FLOCK(fps);
+ if (p.handle) _FLOCK(p.handle);
}
}
}
- void put(T)(auto ref in T value)
+ void put(T)(auto ref scope const T value)
if (!hasIndirections!T &&
!isInputRange!T)
{
rawWrite((&value)[0 .. 1]);
}
- void put(T)(in T[] array)
+ void put(T)(scope const(T)[] array)
if (!hasIndirections!T &&
!isInputRange!T)
{
@@ -3032,7 +3456,7 @@ Example:
Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set)
in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output.
---
-import std.algorithm, std.range, std.stdio;
+import std.algorithm, std.complex, std.range, std.stdio;
void main()
{
@@ -3042,7 +3466,7 @@ void main()
iota(-1, 3, 2.0/size).map!(y =>
iota(-1.5, 0.5, 2.0/size).map!(x =>
cast(ubyte)(1+
- recurrence!((a, n) => x + y*1i + a[n-1]^^2)(0+0i)
+ recurrence!((a, n) => x + y * complex(0, 1) + a[n-1]^^2)(complex(0))
.take(ubyte.max)
.countUntil!(z => z.re^^2 + z.im^^2 > 4))
)
@@ -3076,6 +3500,19 @@ void main()
auto deleteme = testFilename();
scope(exit) collectException(std.file.remove(deleteme));
+
+ {
+ auto writer = File(deleteme, "wb").lockingBinaryWriter();
+ auto input = File(deleteme, "rb");
+
+ ubyte[1] byteIn = [42];
+ writer.rawWrite(byteIn);
+ destroy(writer);
+
+ ubyte[1] byteOut = input.rawRead(new ubyte[1]);
+ assert(byteIn[0] == byteOut[0]);
+ }
+
auto output = File(deleteme, "wb");
auto writer = output.lockingBinaryWriter();
auto input = File(deleteme, "rb");
@@ -3125,7 +3562,22 @@ void main()
assert(dcharsOut == "foo");
}
-/// Get the size of the file, ulong.max if file is not searchable, but still throws if an actual error occurs.
+/** Returns the size of the file in bytes, ulong.max if file is not searchable or throws if the operation fails.
+Example:
+---
+import std.stdio, std.file;
+
+void main()
+{
+ string deleteme = "delete.me";
+ auto file_handle = File(deleteme, "w");
+ file_handle.write("abc"); //create temporary file
+ scope(exit) deleteme.remove; //remove temporary file at scope exit
+
+ assert(file_handle.size() == 3); //check if file size is 3 bytes
+}
+---
+*/
@property ulong size() @safe
{
import std.exception : collectException;
@@ -3300,20 +3752,155 @@ void main()
scope(exit) std.file.remove(deleteme);
{
- File f = File(deleteme, "w");
- auto writer = f.lockingTextWriter();
+ auto writer = File(deleteme, "w").lockingTextWriter();
static assert(isOutputRange!(typeof(writer), dchar));
writer.put("日本語");
writer.put("日本語"w);
writer.put("日本語"d);
writer.put('æ—¥');
writer.put(chain(only('本'), only('語')));
- writer.put(repeat('#', 12)); // BUG 11945
- writer.put(cast(immutable(ubyte)[])"日本語"); // Bug 17229
+ // https://issues.dlang.org/show_bug.cgi?id=11945
+ writer.put(repeat('#', 12));
+ // https://issues.dlang.org/show_bug.cgi?id=17229
+ writer.put(cast(immutable(ubyte)[])"日本語");
}
assert(File(deleteme).readln() == "日本語日本語日本語日本語############日本語");
}
+@safe unittest // wchar -> char
+{
+ static import std.file;
+ import std.exception : assertThrown;
+ import std.utf : UTFException;
+
+ auto deleteme = testFilename();
+ scope(exit) std.file.remove(deleteme);
+
+ {
+ auto writer = File(deleteme, "w").lockingTextWriter();
+ writer.put("\U0001F608"w);
+ }
+ assert(std.file.readText!string(deleteme) == "\U0001F608");
+
+ // Test invalid input: unpaired high surrogate
+ {
+ immutable wchar surr = "\U0001F608"w[0];
+ auto f = File(deleteme, "w");
+ assertThrown!UTFException(() {
+ auto writer = f.lockingTextWriter();
+ writer.put('x');
+ writer.put(surr);
+ assertThrown!UTFException(writer.put(char('y')));
+ assertThrown!UTFException(writer.put(wchar('y')));
+ assertThrown!UTFException(writer.put(dchar('y')));
+ assertThrown!UTFException(writer.put(surr));
+ // First `surr` is still unpaired at this point. `writer` gets
+ // destroyed now, and the destructor throws a UTFException for
+ // the unpaired surrogate.
+ } ());
+ }
+ assert(std.file.readText!string(deleteme) == "x");
+
+ // Test invalid input: unpaired low surrogate
+ {
+ immutable wchar surr = "\U0001F608"w[1];
+ auto writer = File(deleteme, "w").lockingTextWriter();
+ assertThrown!UTFException(writer.put(surr));
+ writer.put('y');
+ assertThrown!UTFException(writer.put(surr));
+ }
+ assert(std.file.readText!string(deleteme) == "y");
+}
+
+@safe unittest // issue 18801
+{
+ static import std.file;
+ import std.string : stripLeft;
+
+ auto deleteme = testFilename();
+ scope(exit) std.file.remove(deleteme);
+
+ {
+ auto writer = File(deleteme, "w,ccs=UTF-8").lockingTextWriter();
+ writer.put("foo");
+ }
+ assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foo");
+
+ {
+ auto writer = File(deleteme, "a,ccs=UTF-8").lockingTextWriter();
+ writer.put("bar");
+ }
+ assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foobar");
+}
+@safe unittest // char/wchar -> wchar_t
+{
+ import core.stdc.locale : LC_CTYPE, setlocale;
+ import core.stdc.wchar_ : fwide;
+ import core.stdc.string : strlen;
+ import std.algorithm.searching : any, endsWith;
+ import std.conv : text;
+ import std.meta : AliasSeq;
+ import std.string : fromStringz, stripLeft;
+ static import std.file;
+ auto deleteme = testFilename();
+ scope(exit) std.file.remove(deleteme);
+ const char* oldCt = () @trusted {
+ const(char)* p = setlocale(LC_CTYPE, null);
+ // Subsequent calls to `setlocale` might invalidate this return value,
+ // so duplicate it.
+ // See: https://github.com/dlang/phobos/pull/7660
+ return p ? p[0 .. strlen(p) + 1].idup.ptr : null;
+ }();
+ const utf8 = ["en_US.UTF-8", "C.UTF-8", ".65001"].any!((loc) @trusted {
+ return setlocale(LC_CTYPE, loc.ptr).fromStringz.endsWith(loc);
+ });
+ scope(exit) () @trusted { setlocale(LC_CTYPE, oldCt); } ();
+ version (DIGITAL_MARS_STDIO) // DM can't handle Unicode above U+07FF.
+ {
+ alias strs = AliasSeq!("xä\u07FE", "yö\u07FF"w);
+ }
+ else
+ {
+ alias strs = AliasSeq!("xä\U0001F607", "yö\U0001F608"w);
+ }
+ {
+ auto f = File(deleteme, "w");
+ version (MICROSOFT_STDIO)
+ {
+ () @trusted { __setmode(fileno(f.getFP()), _O_U8TEXT); } ();
+ }
+ else
+ {
+ assert(fwide(f.getFP(), 1) == 1);
+ }
+ auto writer = f.lockingTextWriter();
+ assert(writer.orientation_ == 1);
+ static foreach (s; strs) writer.put(s);
+ }
+ assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") ==
+ text(strs));
+}
+@safe unittest // https://issues.dlang.org/show_bug.cgi?id=18789
+{
+ static import std.file;
+ auto deleteme = testFilename();
+ scope(exit) std.file.remove(deleteme);
+ // converting to char
+ {
+ auto f = File(deleteme, "w");
+ f.writeln("\U0001F608"w); // UTFException
+ }
+ // converting to wchar_t
+ {
+ auto f = File(deleteme, "w,ccs=UTF-16LE");
+ // from char
+ f.writeln("ö"); // writes garbage
+ f.writeln("\U0001F608"); // ditto
+ // from wchar
+ f.writeln("\U0001F608"w); // leads to ErrnoException
+ }
+}
+
@safe unittest
{
import std.exception : collectException;
@@ -3321,7 +3908,37 @@ void main()
assert(e && e.msg == "Attempting to write to closed File");
}
-/// Used to specify the lock type for $(D File.lock) and $(D File.tryLock).
+@safe unittest // https://issues.dlang.org/show_bug.cgi?id=21592
+{
+ import std.exception : collectException;
+ import std.utf : UTFException;
+ static import std.file;
+ auto deleteme = testFilename();
+ scope(exit) std.file.remove(deleteme);
+ auto f = File(deleteme, "w");
+ auto e = collectException!UTFException(f.writeln(wchar(0xD801)));
+ assert(e.next is null);
+}
+
+version (StdStressTest)
+{
+ // https://issues.dlang.org/show_bug.cgi?id=15768
+ @system unittest
+ {
+ import std.parallelism : parallel;
+ import std.range : iota;
+
+ auto deleteme = testFilename();
+ stderr = File(deleteme, "w");
+
+ foreach (t; 1_000_000.iota.parallel)
+ {
+ stderr.write("aaa");
+ }
+ }
+}
+
+/// Used to specify the lock type for `File.lock` and `File.tryLock`.
enum LockType
{
/**
@@ -3353,12 +3970,12 @@ struct LockingTextReader
import std.exception : enforce;
enforce(f.isOpen, "LockingTextReader: File must be open");
_f = f;
- FLOCK(_f._p.handle);
+ _FLOCK(_f._p.handle);
}
this(this)
{
- FLOCK(_f._p.handle);
+ _FLOCK(_f._p.handle);
}
~this()
@@ -3367,7 +3984,7 @@ struct LockingTextReader
ungetc(_front, cast(FILE*)_f._p.handle);
// File locking has its own reference count
- if (_f.isOpen) FUNLOCK(_f._p.handle);
+ if (_f.isOpen) _FUNLOCK(_f._p.handle);
}
void opAssign(LockingTextReader r)
@@ -3382,7 +3999,7 @@ struct LockingTextReader
{
if (!_f.isOpen || _f.eof)
return true;
- immutable int c = FGETC(cast(_iobuf*) _f._p.handle);
+ immutable int c = _FGETC(cast(_iobuf*) _f._p.handle);
if (c == EOF)
{
.destroy(_f);
@@ -3430,7 +4047,7 @@ struct LockingTextReader
auto deleteme = testFilename();
std.file.write(deleteme, "1 2 3");
scope(exit) std.file.remove(deleteme);
- int x, y;
+ int x;
auto f = File(deleteme);
f.readf("%s ", &x);
assert(x == 1);
@@ -3440,7 +4057,8 @@ struct LockingTextReader
assert(x == 3);
}
-@system unittest // bugzilla 13686
+// https://issues.dlang.org/show_bug.cgi?id=13686
+@system unittest
{
import std.algorithm.comparison : equal;
static import std.file;
@@ -3458,7 +4076,8 @@ struct LockingTextReader
assert(equal(ltr, "ТеÑÑ‚".byDchar));
}
-@system unittest // bugzilla 12320
+// https://issues.dlang.org/show_bug.cgi?id=12320
+@system unittest
{
static import std.file;
auto deleteme = testFilename();
@@ -3472,7 +4091,8 @@ struct LockingTextReader
assert(ltr.empty);
}
-@system unittest // bugzilla 14861
+// https://issues.dlang.org/show_bug.cgi?id=14861
+@system unittest
{
// @system due to readf
static import std.file;
@@ -3492,7 +4112,7 @@ struct LockingTextReader
}
/**
- * Indicates whether $(D T) is a file handle, i.e. the type
+ * Indicates whether `T` is a file handle, i.e. the type
* is implicitly convertable to $(LREF File) or a pointer to a
* $(REF FILE, core,stdc,stdio).
*
@@ -3521,15 +4141,12 @@ private @property File trustedStdout() @trusted
}
/***********************************
-For each argument $(D arg) in $(D args), format the argument (using
-$(REF to, std,conv)) and write the resulting
-string to $(D args[0]). A call without any arguments will fail to
-compile.
+Writes its arguments in text format to standard output (without a trailing newline).
Params:
args = the items to write to `stdout`
-Throws: In case of an I/O error, throws an $(D StdioException).
+Throws: In case of an I/O error, throws an `StdioException`.
Example:
Reads `stdin` and writes it to `stdout` with an argument
@@ -3581,7 +4198,7 @@ if (!is(T[0] : File))
* Throws:
* In case of an I/O error, throws an $(LREF StdioException).
* Example:
- * Reads $(D stdin) and writes it to $(D stdout) with a argument
+ * Reads `stdin` and writes it to `stdout` with an argument
* counter.
---
import std.stdio;
@@ -3599,7 +4216,6 @@ void main()
*/
void writeln(T...)(T args)
{
- import std.traits : isAggregateType;
static if (T.length == 0)
{
import std.exception : enforce;
@@ -3607,17 +4223,13 @@ void writeln(T...)(T args)
enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed");
}
else static if (T.length == 1 &&
- is(typeof(args[0]) : const(char)[]) &&
- !is(typeof(args[0]) == enum) &&
- !is(Unqual!(typeof(args[0])) == typeof(null)) &&
- !isAggregateType!(typeof(args[0])))
+ is(T[0] : const(char)[]) &&
+ (is(T[0] == U[], U) || __traits(isStaticArray, T[0])))
{
- import std.traits : isStaticArray;
-
// Specialization for strings - a very frequent case
auto w = .trustedStdout.lockingTextWriter();
- static if (isStaticArray!(typeof(args[0])))
+ static if (__traits(isStaticArray, T[0]))
{
w.put(args[0][]);
}
@@ -3641,15 +4253,19 @@ void writeln(T...)(T args)
if (false) writeln("wyda");
- // bug 8040
+ // https://issues.dlang.org/show_bug.cgi?id=8040
if (false) writeln(null);
if (false) writeln(">", null, "<");
- // Bugzilla 14041
+ // https://issues.dlang.org/show_bug.cgi?id=14041
if (false)
{
char[8] a;
writeln(a);
+ immutable b = a;
+ b.writeln;
+ const c = a[];
+ c.writeln;
}
}
@@ -3687,9 +4303,9 @@ void writeln(T...)(T args)
stdout.open(deleteme, "w");
writeln("Hello!"c);
- writeln("Hello!"w); // bug 8386
- writeln("Hello!"d); // bug 8386
- writeln("embedded\0null"c); // bug 8730
+ writeln("Hello!"w); // https://issues.dlang.org/show_bug.cgi?id=8386
+ writeln("Hello!"d); // https://issues.dlang.org/show_bug.cgi?id=8386
+ writeln("embedded\0null"c); // https://issues.dlang.org/show_bug.cgi?id=8730
stdout.close();
version (Windows)
assert(cast(char[]) std.file.read(deleteme) ==
@@ -3708,8 +4324,8 @@ void writeln(T...)(T args)
scope(exit) { std.file.remove(deleteme); }
enum EI : int { A, B }
- enum ED : double { A, B }
- enum EC : char { A, B }
+ enum ED : double { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
+ enum EC : char { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
enum ES : string { A = "aaa", B = "bbb" }
f.writeln(EI.A); // false, but A on 2.058
@@ -3745,12 +4361,19 @@ void writeln(T...)(T args)
useInit(stdout.lockingTextWriter());
}
+@system unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=21920
+ void function(string) printer = &writeln!string;
+ if (false) printer("Hello");
+}
+
/***********************************
Writes formatted data to standard output (without a trailing newline).
Params:
-fmt = The $(LINK2 std_format.html#format-string, format string).
+fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
When passed as a compile-time argument, the string will be statically checked
against the argument types passed.
args = Items to write.
@@ -3761,7 +4384,7 @@ Note: In older versions of Phobos, it used to be possible to write:
writef(stderr, "%s", "message");
------
-to print a message to $(D stderr). This syntax is no longer supported, and has
+to print a message to `stderr`. This syntax is no longer supported, and has
been superceded by:
------
@@ -3861,12 +4484,15 @@ void writefln(Char, A...)(in Char[] fmt, A args)
}
/**
- * Reads formatted data from $(D stdin) using $(REF formattedRead, std,_format).
+ * Reads formatted data from `stdin` using $(REF formattedRead, std,_format).
* Params:
- * format = The $(LINK2 std_format.html#_format-string, _format string).
+ * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
* When passed as a compile-time argument, the string will be statically checked
* against the argument types passed.
* args = Items to be read.
+ * Returns:
+ * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
+ * this number will be less than the number of variables provided.
* Example:
----
// test.d
@@ -3899,7 +4525,7 @@ if (isSomeString!(typeof(format)))
}
/// ditto
-uint readf(A...)(in char[] format, auto ref A args)
+uint readf(A...)(scope const(char)[] format, auto ref A args)
{
return stdin.readf(format, args);
}
@@ -3907,7 +4533,7 @@ uint readf(A...)(in char[] format, auto ref A args)
@system unittest
{
float f;
- if (false) uint x = readf("%s", &f);
+ if (false) readf("%s", &f);
char a;
wchar b;
@@ -3919,23 +4545,23 @@ uint readf(A...)(in char[] format, auto ref A args)
}
/**********************************
- * Read line from $(D stdin).
+ * Read line from `stdin`.
*
* This version manages its own read buffer, which means one memory allocation per call. If you are not
- * retaining a reference to the read data, consider the $(D readln(buf)) version, which may offer
+ * retaining a reference to the read data, consider the `readln(buf)` version, which may offer
* better performance as it can reuse its read buffer.
*
* Returns:
* The line that was read, including the line terminator character.
* Params:
- * S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
- * terminator = Line terminator (by default, $(D '\n')).
+ * S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
+ * terminator = Line terminator (by default, `'\n'`).
* Note:
* String terminators are not supported due to ambiguity with readln(buf) below.
* Throws:
- * $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
+ * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
* Example:
- * Reads $(D stdin) and writes it to $(D stdout).
+ * Reads `stdin` and writes it to `stdout`.
---
import std.stdio;
@@ -3954,22 +4580,22 @@ if (isSomeString!S)
}
/**********************************
- * Read line from $(D stdin) and write it to buf[], including terminating character.
+ * Read line from `stdin` and write it to buf[], including terminating character.
*
* This can be faster than $(D line = readln()) because you can reuse
* the buffer for each call. Note that reusing the buffer means that you
* must copy the previous contents if you wish to retain them.
*
* Returns:
- * $(D size_t) 0 for end of file, otherwise number of characters read
+ * `size_t` 0 for end of file, otherwise number of characters read
* Params:
* buf = Buffer used to store the resulting line data. buf is resized as necessary.
- * terminator = Line terminator (by default, $(D '\n')). Use $(REF newline, std,ascii)
+ * terminator = Line terminator (by default, `'\n'`). Use $(REF newline, std,ascii)
* for portability (unless the file was opened in text mode).
* Throws:
- * $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
+ * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
* Example:
- * Reads $(D stdin) and writes it to $(D stdout).
+ * Reads `stdin` and writes it to `stdout`.
---
import std.stdio;
@@ -4005,27 +4631,27 @@ if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
{
readln();
readln('\t');
- foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
+ static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
{
readln!String();
readln!String('\t');
}
- foreach (String; AliasSeq!(char[], wchar[], dchar[]))
- {
+ static foreach (String; AliasSeq!(char[], wchar[], dchar[]))
+ {{
String buf;
readln(buf);
readln(buf, '\t');
readln(buf, "<br />");
- }
+ }}
}
}
/*
- * Convenience function that forwards to $(D core.sys.posix.stdio.fopen)
- * (to $(D _wfopen) on Windows)
+ * Convenience function that forwards to `core.sys.posix.stdio.fopen`
+ * (to `_wfopen` on Windows)
* with appropriately-constructed C-style strings.
*/
-private FILE* fopen(R1, R2)(R1 name, R2 mode = "r")
+private FILE* _fopen(R1, R2)(R1 name, R2 mode = "r")
if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) &&
(isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2))
{
@@ -4034,7 +4660,7 @@ if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1)
auto namez = name.tempCString!FSChar();
auto modez = mode.tempCString!FSChar();
- static fopenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
+ static _fopenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
{
version (Windows)
{
@@ -4055,19 +4681,19 @@ if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1)
}
else
{
- return .fopen(namez, modez);
+ return fopen(namez, modez);
}
}
- return fopenImpl(namez, modez);
+ return _fopenImpl(namez, modez);
}
version (Posix)
{
/***********************************
- * Convenience function that forwards to $(D core.sys.posix.stdio.popen)
+ * Convenience function that forwards to `core.sys.posix.stdio.popen`
* with appropriately-constructed C-style strings.
*/
- FILE* popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc
+ FILE* _popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc
if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) &&
(isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2))
{
@@ -4086,7 +4712,7 @@ version (Posix)
}
/*
- * Convenience function that forwards to $(D core.stdc.stdio.fwrite)
+ * Convenience function that forwards to `core.stdc.stdio.fwrite`
*/
private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
{
@@ -4094,7 +4720,7 @@ private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
}
/*
- * Convenience function that forwards to $(D core.stdc.stdio.fread)
+ * Convenience function that forwards to `core.stdc.stdio.fread`
*/
private auto trustedFread(T)(FILE* f, T[] obj) @trusted
{
@@ -4102,7 +4728,7 @@ private auto trustedFread(T)(FILE* f, T[] obj) @trusted
}
/**
- * Iterates through the lines of a file by using $(D foreach).
+ * Iterates through the lines of a file by using `foreach`.
*
* Example:
*
@@ -4115,24 +4741,24 @@ void main()
}
}
---------
-The line terminator ($(D '\n') by default) is part of the string read (it
+The line terminator (`'\n'` by default) is part of the string read (it
could be missing in the last line of the file). Several types are
-supported for $(D line), and the behavior of $(D lines)
+supported for `line`, and the behavior of `lines`
changes accordingly:
-$(OL $(LI If $(D line) has type $(D string), $(D
-wstring), or $(D dstring), a new string of the respective type
-is allocated every read.) $(LI If $(D line) has type $(D
-char[]), $(D wchar[]), $(D dchar[]), the line's content
-will be reused (overwritten) across reads.) $(LI If $(D line)
-has type $(D immutable(ubyte)[]), the behavior is similar to
+$(OL $(LI If `line` has type `string`, $(D
+wstring), or `dstring`, a new string of the respective type
+is allocated every read.) $(LI If `line` has type $(D
+char[]), `wchar[]`, `dchar[]`, the line's content
+will be reused (overwritten) across reads.) $(LI If `line`
+has type `immutable(ubyte)[]`, the behavior is similar to
case (1), except that no UTF checking is attempted upon input.) $(LI
-If $(D line) has type $(D ubyte[]), the behavior is
+If `line` has type `ubyte[]`, the behavior is
similar to case (2), except that no UTF checking is attempted upon
input.))
In all cases, a two-symbols versions is also accepted, in which case
-the first symbol (of integral type, e.g. $(D ulong) or $(D
+the first symbol (of integral type, e.g. `ulong` or $(D
uint)) tracks the zero-based number of the current line.
Example:
@@ -4143,7 +4769,7 @@ Example:
}
----
- In case of an I/O error, an $(D StdioException) is thrown.
+ In case of an I/O error, an `StdioException` is thrown.
See_Also:
$(LREF byLine)
@@ -4158,7 +4784,7 @@ struct lines
Constructor.
Params:
f = File to read lines from.
- terminator = Line separator ($(D '\n') by default).
+ terminator = Line separator (`'\n'` by default).
*/
this(File f, dchar terminator = '\n')
{
@@ -4172,8 +4798,6 @@ struct lines
alias Parms = Parameters!(dg);
static if (isSomeString!(Parms[$ - 1]))
{
- enum bool duplicate = is(Parms[$ - 1] == string)
- || is(Parms[$ - 1] == wstring) || is(Parms[$ - 1] == dstring);
int result = 0;
static if (is(Parms[$ - 1] : const(char)[]))
alias C = char;
@@ -4220,12 +4844,12 @@ struct lines
enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
int result = 1;
int c = void;
- FLOCK(f._p.handle);
- scope(exit) FUNLOCK(f._p.handle);
+ _FLOCK(f._p.handle);
+ scope(exit) _FUNLOCK(f._p.handle);
ubyte[] buffer;
static if (Parms.length == 2)
Parms[0] line = 0;
- while ((c = FGETC(cast(_iobuf*) f._p.handle)) != -1)
+ while ((c = _FGETC(cast(_iobuf*) f._p.handle)) != -1)
{
buffer ~= to!(ubyte)(c);
if (c == terminator)
@@ -4235,8 +4859,8 @@ struct lines
else
alias arg = buffer;
// unlock the file while calling the delegate
- FUNLOCK(f._p.handle);
- scope(exit) FLOCK(f._p.handle);
+ _FUNLOCK(f._p.handle);
+ scope(exit) _FLOCK(f._p.handle);
static if (Parms.length == 1)
{
result = dg(arg);
@@ -4251,7 +4875,7 @@ struct lines
buffer.length = 0;
}
}
- // can only reach when FGETC returned -1
+ // can only reach when _FGETC returned -1
if (!f.eof) throw new StdioException("Error in reading file"); // error occured
return result;
}
@@ -4354,7 +4978,7 @@ struct lines
}
- foreach (T; AliasSeq!(ubyte[]))
+ static foreach (T; AliasSeq!(ubyte[]))
{
// test looping with a file with three lines, last without a newline
// using a counter too this time
@@ -4374,7 +4998,7 @@ struct lines
}
/**
-Iterates through a file a chunk at a time by using $(D foreach).
+Iterates through a file a chunk at a time by using `foreach`.
Example:
@@ -4388,12 +5012,12 @@ void main()
}
---------
-The content of $(D buffer) is reused across calls. In the
- example above, $(D buffer.length) is 4096 for all iterations,
- except for the last one, in which case $(D buffer.length) may
+The content of `buffer` is reused across calls. In the
+ example above, `buffer.length` is 4096 for all iterations,
+ except for the last one, in which case `buffer.length` may
be less than 4096 (but always greater than zero).
- In case of an I/O error, an $(D StdioException) is thrown.
+ In case of an I/O error, an `StdioException` is thrown.
*/
auto chunks(File f, size_t size)
{
@@ -4410,7 +5034,7 @@ private struct ChunksImpl
{
assert(size, "size must be larger than 0");
}
- body
+ do
{
this.f = f;
this.size = size;
@@ -4419,6 +5043,9 @@ private struct ChunksImpl
int opApply(D)(scope D dg)
{
import core.stdc.stdlib : alloca;
+ import std.exception : enforce;
+
+ enforce(f.isOpen, "Attempting to read from an unopened file");
enum maxStackSize = 1024 * 16;
ubyte[] buffer = void;
if (size < maxStackSize)
@@ -4484,12 +5111,26 @@ private struct ChunksImpl
f.close();
}
+// Issue 21730 - null ptr dereferenced in ChunksImpl.opApply (SIGSEGV)
+@system unittest
+{
+ import std.exception : assertThrown;
+ static import std.file;
+
+ auto deleteme = testFilename();
+ scope(exit) { if (std.file.exists(deleteme)) std.file.remove(deleteme); }
+
+ auto err1 = File(deleteme, "w+x");
+ err1.close;
+ std.file.remove(deleteme);
+ assertThrown(() {foreach (ubyte[] buf; chunks(err1, 4096)) {}}());
+}
/**
Writes an array or range to a file.
Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)).
Similar to $(REF write, std,file), strings are written as-is,
-rather than encoded according to the $(D File)'s $(HTTP
+rather than encoded according to the `File`'s $(HTTP
en.cppreference.com/w/c/io#Narrow_and_wide_orientation,
orientation).
*/
@@ -4533,7 +5174,7 @@ Initialize with a message and an error code.
: (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg));
}
-/** Convenience functions that throw an $(D StdioException). */
+/** Convenience functions that throw an `StdioException`. */
static void opCall(string msg)
{
throw new StdioException(msg);
@@ -4587,10 +5228,19 @@ enum StdFileHandle: string
}
/** The standard input stream.
- Bugs:
- Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
- it is thread un-safe to reassign `stdin` to a different `File` instance
- than the default.
+
+ Returns:
+ stdin as a $(LREF File).
+
+ Note:
+ The returned $(LREF File) wraps $(REF stdin,core,stdc,stdio), and
+ is therefore thread global. Reassigning `stdin` to a different
+ `File` must be done in a single-threaded or locked context in
+ order to avoid race conditions.
+
+ All reading from `stdin` automatically locks the file globally,
+ and will cause all other threads calling `read` to wait until
+ the lock is released.
*/
alias stdin = makeGlobal!(StdFileHandle.stdin);
@@ -4603,7 +5253,8 @@ alias stdin = makeGlobal!(StdFileHandle.stdin);
import std.array : array;
import std.typecons : Yes;
- void main() {
+ void main()
+ {
stdin // read from stdin
.byLineCopy(Yes.keepTerminator) // copying each line
.array() // convert to array of lines
@@ -4615,22 +5266,94 @@ alias stdin = makeGlobal!(StdFileHandle.stdin);
/**
The standard output stream.
- Bugs:
- Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
- it is thread un-safe to reassign `stdout` to a different `File` instance
- than the default.
+
+ Returns:
+ stdout as a $(LREF File).
+
+ Note:
+ The returned $(LREF File) wraps $(REF stdout,core,stdc,stdio), and
+ is therefore thread global. Reassigning `stdout` to a different
+ `File` must be done in a single-threaded or locked context in
+ order to avoid race conditions.
+
+ All writing to `stdout` automatically locks the file globally,
+ and will cause all other threads calling `write` to wait until
+ the lock is released.
*/
alias stdout = makeGlobal!(StdFileHandle.stdout);
+///
+@safe unittest
+{
+ void main()
+ {
+ stdout.writeln("Write a message to stdout.");
+ }
+}
+
+///
+@safe unittest
+{
+ void main()
+ {
+ import std.algorithm.iteration : filter, map, sum;
+ import std.format : format;
+ import std.range : iota, tee;
+
+ int len;
+ const r = 6.iota
+ .filter!(a => a % 2) // 1 3 5
+ .map!(a => a * 2) // 2 6 10
+ .tee!(_ => stdout.writefln("len: %d", len++))
+ .sum;
+
+ assert(r == 18);
+ }
+}
+
+///
+@safe unittest
+{
+ void main()
+ {
+ import std.algorithm.mutation : copy;
+ import std.algorithm.iteration : map;
+ import std.format : format;
+ import std.range : iota;
+
+ 10.iota
+ .map!(e => "N: %d".format(e))
+ .copy(stdout.lockingTextWriter()); // the OutputRange
+ }
+}
+
/**
The standard error stream.
- Bugs:
- Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
- it is thread un-safe to reassign `stderr` to a different `File` instance
- than the default.
+
+ Returns:
+ stderr as a $(LREF File).
+
+ Note:
+ The returned $(LREF File) wraps $(REF stderr,core,stdc,stdio), and
+ is therefore thread global. Reassigning `stderr` to a different
+ `File` must be done in a single-threaded or locked context in
+ order to avoid race conditions.
+
+ All writing to `stderr` automatically locks the file globally,
+ and will cause all other threads calling `write` to wait until
+ the lock is released.
*/
alias stderr = makeGlobal!(StdFileHandle.stderr);
+///
+@safe unittest
+{
+ void main()
+ {
+ stderr.writeln("Write a message to stderr.");
+ }
+}
+
@system unittest
{
static import std.file;
@@ -4745,8 +5468,8 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
{
version (DIGITAL_MARS_STDIO)
{
- FLOCK(fps);
- scope(exit) FUNLOCK(fps);
+ _FLOCK(fps);
+ scope(exit) _FUNLOCK(fps);
/* Since fps is now locked, we can create an "unshared" version
* of fp.
@@ -4761,7 +5484,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
* Read them and convert to chars.
*/
static assert(wchar_t.sizeof == 2);
- for (int c = void; (c = FGETWC(fp)) != -1; )
+ for (int c = void; (c = _FGETWC(fp)) != -1; )
{
if ((c & ~0x7F) == 0)
{
@@ -4774,7 +5497,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
if (c >= 0xD800 && c <= 0xDBFF)
{
int c2 = void;
- if ((c2 = FGETWC(fp)) != -1 ||
+ if ((c2 = _FGETWC(fp)) != -1 ||
c2 < 0xDC00 && c2 > 0xDFFF)
{
StdioException("unpaired UTF-16 surrogate");
@@ -4796,7 +5519,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
*/
L1:
int c;
- while ((c = FGETC(fp)) != -1)
+ while ((c = _FGETC(fp)) != -1)
{
app.putchar(cast(char) c);
if (c == terminator)
@@ -4866,8 +5589,8 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
}
else version (MICROSOFT_STDIO)
{
- FLOCK(fps);
- scope(exit) FUNLOCK(fps);
+ _FLOCK(fps);
+ scope(exit) _FUNLOCK(fps);
/* Since fps is now locked, we can create an "unshared" version
* of fp.
@@ -4878,7 +5601,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
app.initialize(buf);
int c;
- while ((c = FGETC(fp)) != -1)
+ while ((c = _FGETC(fp)) != -1)
{
app.putchar(cast(char) c);
if (c == terminator)
@@ -4904,13 +5627,13 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
/* Stream is in wide characters.
* Read them and convert to chars.
*/
- FLOCK(fps);
- scope(exit) FUNLOCK(fps);
+ _FLOCK(fps);
+ scope(exit) _FUNLOCK(fps);
auto fp = cast(_iobuf*) fps;
version (Windows)
{
buf.length = 0;
- for (int c = void; (c = FGETWC(fp)) != -1; )
+ for (int c = void; (c = _FGETWC(fp)) != -1; )
{
if ((c & ~0x7F) == 0)
{ buf ~= c;
@@ -4922,7 +5645,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
if (c >= 0xD800 && c <= 0xDBFF)
{
int c2 = void;
- if ((c2 = FGETWC(fp)) != -1 ||
+ if ((c2 = _FGETWC(fp)) != -1 ||
c2 < 0xDC00 && c2 > 0xDFFF)
{
StdioException("unpaired UTF-16 surrogate");
@@ -4940,7 +5663,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
else version (Posix)
{
buf.length = 0;
- for (int c; (c = FGETWC(fp)) != -1; )
+ for (int c; (c = _FGETWC(fp)) != -1; )
{
import std.utf : encode;
@@ -4998,8 +5721,8 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
{
import core.stdc.wchar_ : fwide;
- FLOCK(fps);
- scope(exit) FUNLOCK(fps);
+ _FLOCK(fps);
+ scope(exit) _FUNLOCK(fps);
auto fp = cast(_iobuf*) fps;
if (orientation == File.Orientation.wide)
{
@@ -5009,7 +5732,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
version (Windows)
{
buf.length = 0;
- for (int c; (c = FGETWC(fp)) != -1; )
+ for (int c; (c = _FGETWC(fp)) != -1; )
{
if ((c & ~0x7F) == 0)
{ buf ~= c;
@@ -5021,7 +5744,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
if (c >= 0xD800 && c <= 0xDBFF)
{
int c2 = void;
- if ((c2 = FGETWC(fp)) != -1 ||
+ if ((c2 = _FGETWC(fp)) != -1 ||
c2 < 0xDC00 && c2 > 0xDFFF)
{
StdioException("unpaired UTF-16 surrogate");
@@ -5040,7 +5763,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
{
import std.utf : encode;
buf.length = 0;
- for (int c; (c = FGETWC(fp)) != -1; )
+ for (int c; (c = _FGETWC(fp)) != -1; )
{
if ((c & ~0x7F) == 0)
buf ~= cast(char) c;
@@ -5063,7 +5786,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
// First, fill the existing buffer
for (size_t bufPos = 0; bufPos < buf.length; )
{
- immutable c = FGETC(fp);
+ immutable c = _FGETC(fp);
if (c == -1)
{
buf.length = bufPos;
@@ -5078,7 +5801,7 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
}
}
// Then, append to it
- for (int c; (c = FGETC(fp)) != -1; )
+ for (int c; (c = _FGETC(fp)) != -1; )
{
buf ~= cast(char) c;
if (c == terminator)
@@ -5105,18 +5828,17 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orie
File f = File(deleteme, "rb");
char[] ln = new char[2];
- char* lnptr = ln.ptr;
f.readln(ln);
assert(ln == "abcd\n");
char[] t = ln[0 .. 2];
t ~= 't';
assert(t == "abt");
- assert(ln == "abcd\n"); // bug 13856: ln stomped to "abtd"
+ // https://issues.dlang.org/show_bug.cgi?id=13856: ln stomped to "abtd"
+ assert(ln == "abcd\n");
// it can also stomp the array length
ln = new char[4];
- lnptr = ln.ptr;
f.readln(ln);
assert(ln == "0123456789abcde\n");
@@ -5182,12 +5904,13 @@ version (linux)
}
}
-version (unittest) string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
+version (StdUnittest) private string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
{
import std.conv : text;
import std.file : deleteme;
import std.path : baseName;
- // filename intentionally contains non-ASCII (Russian) characters for test Issue 7648
+ // filename intentionally contains non-ASCII (Russian) characters for
+ // https://issues.dlang.org/show_bug.cgi?id=7648
return text(deleteme, "-детка.", baseName(file), ".", line);
}
diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d
index 1128a090304..420b68abe6a 100644
--- a/libphobos/src/std/string.d
+++ b/libphobos/src/std/string.d
@@ -69,9 +69,9 @@ $(TR $(TDNW Miscellaneous)
)
)))
-Objects of types $(D _string), $(D wstring), and $(D dstring) are value types
+Objects of types `string`, `wstring`, and `dstring` are value types
and cannot be mutated element-by-element. For using mutation during building
-strings, use $(D char[]), $(D wchar[]), or $(D dchar[]). The $(D xxxstring)
+strings, use `char[]`, `wchar[]`, or `dchar[]`. The `xxxstring`
types are preferable because they don't exhibit undesired aliasing, thus
making code more robust.
@@ -110,7 +110,7 @@ $(LEADINGROW Publicly imported functions)
))
)
-There is a rich set of functions for _string handling defined in other modules.
+There is a rich set of functions for string handling defined in other modules.
Functions related to Unicode and ASCII are found in $(MREF std, uni)
and $(MREF std, ascii), respectively. Other functions that have a
wider generality than just strings can be found in $(MREF std, algorithm)
@@ -129,26 +129,26 @@ See_Also:
for functions that work with unicode strings
)
-Copyright: Copyright Digital Mars 2007-.
+Copyright: Copyright The D Language Foundation 2007-.
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP digitalmars.com, Walter Bright),
$(HTTP erdani.org, Andrei Alexandrescu),
- Jonathan M Davis,
- and David L. 'SpottedTiger' Davis
+ $(HTTP jmdavisprog.com, Jonathan M Davis),
+ and David L. 'SpottedTiger' Davis
-Source: $(PHOBOSSRC std/_string.d)
+Source: $(PHOBOSSRC std/string.d)
*/
module std.string;
-version (unittest)
+version (StdUnittest)
{
private:
struct TestAliasedString
{
- string get() @safe @nogc pure nothrow { return _s; }
+ string get() @safe @nogc pure nothrow return scope { return _s; }
alias get this;
@disable this(this);
string _s;
@@ -175,13 +175,13 @@ public import std.format : format, sformat;
import std.typecons : Flag, Yes, No;
public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace;
-import std.meta; // AliasSeq, staticIndexOf
-import std.range.primitives; // back, ElementEncodingType, ElementType, front,
- // hasLength, hasSlicing, isBidirectionalRange, isForwardRange, isInfinite,
- // isInputRange, isOutputRange, isRandomAccessRange, popBack, popFront, put,
- // save;
-import std.traits; // isConvertibleToString, isNarrowString, isSomeChar,
- // isSomeString, StringTypeOf, Unqual
+import std.meta : AliasSeq, staticIndexOf;
+import std.range.primitives : back, ElementEncodingType, ElementType, front,
+ hasLength, hasSlicing, isBidirectionalRange, isForwardRange, isInfinite,
+ isInputRange, isOutputRange, isRandomAccessRange, popBack, popFront, put,
+ save;
+import std.traits : isConvertibleToString, isNarrowString, isSomeChar,
+ isSomeString, StringTypeOf, Unqual;
//public imports for backward compatibility
public import std.algorithm.comparison : cmp;
@@ -201,46 +201,163 @@ class StringException : Exception
mixin basicExceptionCtors;
}
+///
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+ auto bad = " a\n\tb\n c";
+ assertThrown!StringException(bad.outdent);
+}
/++
Params:
cString = A null-terminated c-style string.
- Returns: A D-style array of $(D char) referencing the same string. The
- returned array will retain the same type qualifiers as the input.
+ Returns: A D-style array of `char`, `wchar` or `dchar` referencing the same
+ string. The returned array will retain the same type qualifiers as the input.
$(RED Important Note:) The returned array is a slice of the original buffer.
The original data is not changed and not copied.
+/
+inout(Char)[] fromStringz(Char)(return scope inout(Char)* cString) @nogc @system pure nothrow
+if (isSomeChar!Char)
+{
+ import core.stdc.stddef : wchar_t;
-inout(char)[] fromStringz(inout(char)* cString) @nogc @system pure nothrow {
- import core.stdc.string : strlen;
- return cString ? cString[0 .. strlen(cString)] : null;
+ static if (is(immutable Char == immutable char))
+ import core.stdc.string : cstrlen = strlen;
+ else static if (is(immutable Char == immutable wchar_t))
+ import core.stdc.wchar_ : cstrlen = wcslen;
+ else
+ static size_t cstrlen(scope const Char* s)
+ {
+ const(Char)* p = s;
+ while (*p)
+ ++p;
+ return p - s;
+ }
+
+ return cString ? cString[0 .. cstrlen(cString)] : null;
+}
+
+/// ditto
+inout(Char)[] fromStringz(Char)(return scope inout(Char)[] cString) @nogc @safe pure nothrow
+if (isSomeChar!Char)
+{
+ foreach (i; 0 .. cString.length)
+ if (cString[i] == '\0')
+ return cString[0 .. i];
+
+ return cString;
}
///
@system pure unittest
{
- assert(fromStringz(null) == null);
- assert(fromStringz("foo") == "foo");
+ assert(fromStringz("foo\0"c.ptr) == "foo"c);
+ assert(fromStringz("foo\0"w.ptr) == "foo"w);
+ assert(fromStringz("foo\0"d.ptr) == "foo"d);
+
+ assert(fromStringz("ç¦\0"c.ptr) == "ç¦"c);
+ assert(fromStringz("ç¦\0"w.ptr) == "ç¦"w);
+ assert(fromStringz("ç¦\0"d.ptr) == "ç¦"d);
+}
+
+///
+@nogc @safe pure nothrow unittest
+{
+ struct C
+ {
+ char[32] name;
+ }
+ assert(C("foo\0"c).name.fromStringz() == "foo"c);
+
+ struct W
+ {
+ wchar[32] name;
+ }
+ assert(W("foo\0"w).name.fromStringz() == "foo"w);
+
+ struct D
+ {
+ dchar[32] name;
+ }
+ assert(D("foo\0"d).name.fromStringz() == "foo"d);
+}
+
+@nogc @safe pure nothrow unittest
+{
+ assert( string.init.fromStringz() == ""c);
+ assert(wstring.init.fromStringz() == ""w);
+ assert(dstring.init.fromStringz() == ""d);
+
+ immutable char[3] a = "foo"c;
+ assert(a.fromStringz() == "foo"c);
+
+ immutable wchar[3] b = "foo"w;
+ assert(b.fromStringz() == "foo"w);
+
+ immutable dchar[3] c = "foo"d;
+ assert(c.fromStringz() == "foo"d);
+}
+
+@system pure unittest
+{
+ char* a = null;
+ assert(fromStringz(a) == null);
+ wchar* b = null;
+ assert(fromStringz(b) == null);
+ dchar* c = null;
+ assert(fromStringz(c) == null);
+
+ const char* d = "foo\0";
+ assert(fromStringz(d) == "foo");
+
+ immutable char* e = "foo\0";
+ assert(fromStringz(e) == "foo");
+
+ const wchar* f = "foo\0";
+ assert(fromStringz(f) == "foo");
+
+ immutable wchar* g = "foo\0";
+ assert(fromStringz(g) == "foo");
+
+ const dchar* h = "foo\0";
+ assert(fromStringz(h) == "foo");
+
+ immutable dchar* i = "foo\0";
+ assert(fromStringz(i) == "foo");
+
+ immutable wchar z = 0x0000;
+ // Test some surrogate pairs
+ // high surrogates are in the range 0xD800 .. 0xDC00
+ // low surrogates are in the range 0xDC00 .. 0xE000
+ // since UTF16 doesn't specify endianness we test both.
+ foreach (wchar[] t; [[0xD800, 0xDC00], [0xD800, 0xE000], [0xDC00, 0xDC00],
+ [0xDC00, 0xE000], [0xDA00, 0xDE00]])
+ {
+ immutable hi = t[0], lo = t[1];
+ assert(fromStringz([hi, lo, z].ptr) == [hi, lo]);
+ assert(fromStringz([lo, hi, z].ptr) == [lo, hi]);
+ }
}
/++
Params:
s = A D-style string.
- Returns: A C-style null-terminated string equivalent to $(D s). $(D s)
- must not contain embedded $(D '\0')'s as any C function will treat the
- first $(D '\0') that it sees as the end of the string. If $(D s.empty) is
- $(D true), then a string containing only $(D '\0') is returned.
+ Returns: A C-style null-terminated string equivalent to `s`. `s`
+ must not contain embedded `'\0'`'s as any C function will treat the
+ first `'\0'` that it sees as the end of the string. If `s.empty` is
+ `true`, then a string containing only `'\0'` is returned.
- $(RED Important Note:) When passing a $(D char*) to a C function, and the C
+ $(RED Important Note:) When passing a `char*` to a C function, and the C
function keeps it around for any reason, make sure that you keep a
reference to it in your D code. Otherwise, it may become invalid during a
garbage collection cycle and cause a nasty bug when the C code tries to use
it.
+/
-immutable(char)* toStringz(const(char)[] s) @trusted pure nothrow
+immutable(char)* toStringz(scope const(char)[] s) @trusted pure nothrow
out (result)
{
import core.stdc.string : strlen, memcmp;
@@ -248,13 +365,18 @@ out (result)
{
auto slen = s.length;
while (slen > 0 && s[slen-1] == 0) --slen;
- assert(strlen(result) == slen);
- assert(result[0 .. slen] == s[0 .. slen]);
+ assert(strlen(result) == slen,
+ "The result c string is shorter than the in input string");
+ assert(result[0 .. slen] == s[0 .. slen],
+ "The input and result string are not equal");
}
}
-body
+do
{
import std.exception : assumeUnique;
+
+ if (s.empty) return "".ptr;
+
/+ Unfortunately, this isn't reliable.
We could make this work if string literals are put
in read-only memory and we test if s[] is pointing into
@@ -278,26 +400,6 @@ body
return &assumeUnique(copy)[0];
}
-/++ Ditto +/
-immutable(char)* toStringz(in string s) @trusted pure nothrow
-{
- if (s.empty) return "".ptr;
- /* Peek past end of s[], if it's 0, no conversion necessary.
- * Note that the compiler will put a 0 past the end of static
- * strings, and the storage allocator will put a 0 past the end
- * of newly allocated char[]'s.
- */
- immutable p = s.ptr + s.length;
- // Is p dereferenceable? A simple test: if the p points to an
- // address multiple of 4, then conservatively assume the pointer
- // might be pointing to a new block of memory, which might be
- // unreadable. Otherwise, it's definitely pointing to valid
- // memory.
- if ((cast(size_t) p & 3) && *p == 0)
- return &s[0];
- return toStringz(cast(const char[]) s);
-}
-
///
pure nothrow @system unittest
{
@@ -325,6 +427,27 @@ pure nothrow @system unittest
const string test2 = "";
p = toStringz(test2);
assert(*p == 0);
+
+ assert(toStringz([]) is toStringz(""));
+}
+
+pure nothrow @system unittest // https://issues.dlang.org/show_bug.cgi?id=15136
+{
+ static struct S
+ {
+ immutable char[5] str;
+ ubyte foo;
+ this(char[5] str) pure nothrow
+ {
+ this.str = str;
+ }
+ }
+ auto s = S("01234");
+ const str = s.str.toStringz;
+ assert(str !is s.str.ptr);
+ assert(*(str + 5) == 0); // Null terminated.
+ s.foo = 42;
+ assert(*(str + 5) == 0); // Still null terminated.
}
@@ -340,27 +463,199 @@ alias CaseSensitive = Flag!"caseSensitive";
s = string or InputRange of characters to search in correct UTF format
c = character to search for
startIdx = starting index to a well-formed code point
- cs = $(D Yes.caseSensitive) or $(D No.caseSensitive)
+ cs = `Yes.caseSensitive` or `No.caseSensitive`
Returns:
- the index of the first occurrence of $(D c) in $(D s) with
- respect to the start index $(D startIdx). If $(D c)
- is not found, then $(D -1) is returned.
- If $(D c) is found the value of the returned index is at least
- $(D startIdx).
+ the index of the first occurrence of `c` in `s` with
+ respect to the start index `startIdx`. If `c`
+ is not found, then `-1` is returned.
+ If `c` is found the value of the returned index is at least
+ `startIdx`.
If the parameters are not valid UTF, the result will still
be in the range [-1 .. s.length], but will not be reliable otherwise.
Throws:
- If the sequence starting at $(D startIdx) does not represent a well
+ If the sequence starting at `startIdx` does not represent a well
formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
See_Also: $(REF countUntil, std,algorithm,searching)
+/
-ptrdiff_t indexOf(Range)(Range s, in dchar c,
- in CaseSensitive cs = Yes.caseSensitive)
-if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
- !isConvertibleToString!Range)
+ptrdiff_t indexOf(Range)(Range s, dchar c, CaseSensitive cs = Yes.caseSensitive)
+if (isInputRange!Range && isSomeChar!(ElementType!Range) && !isSomeString!Range)
+{
+ return _indexOf(s, c, cs);
+}
+
+/// Ditto
+ptrdiff_t indexOf(C)(scope const(C)[] s, dchar c, CaseSensitive cs = Yes.caseSensitive)
+if (isSomeChar!C)
+{
+ return _indexOf(s, c, cs);
+}
+
+/// Ditto
+ptrdiff_t indexOf(Range)(Range s, dchar c, size_t startIdx, CaseSensitive cs = Yes.caseSensitive)
+if (isInputRange!Range && isSomeChar!(ElementType!Range) && !isSomeString!Range)
+{
+ return _indexOf(s, c, startIdx, cs);
+}
+
+/// Ditto
+ptrdiff_t indexOf(C)(scope const(C)[] s, dchar c, size_t startIdx, CaseSensitive cs = Yes.caseSensitive)
+if (isSomeChar!C)
+{
+ return _indexOf(s, c, startIdx, cs);
+}
+
+///
+@safe pure unittest
+{
+ import std.typecons : No;
+
+ string s = "Hello World";
+ assert(indexOf(s, 'W') == 6);
+ assert(indexOf(s, 'Z') == -1);
+ assert(indexOf(s, 'w', No.caseSensitive) == 6);
+}
+
+///
+@safe pure unittest
+{
+ import std.typecons : No;
+
+ string s = "Hello World";
+ assert(indexOf(s, 'W', 4) == 6);
+ assert(indexOf(s, 'Z', 100) == -1);
+ assert(indexOf(s, 'w', 3, No.caseSensitive) == 6);
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!indexOf("std/string.d", '/'));
+
+ enum S : string { a = "std/string.d" }
+ assert(S.a.indexOf('/') == 3);
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.indexOf('/') == 3);
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+ import std.traits : EnumMembers;
+ import std.utf : byChar, byWchar, byDchar;
+
+ assertCTFEable!(
+ {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
+ assert(indexOf(cast(S) null, cast(dchar)'a') == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a') == -1);
+ assert(indexOf(to!S("abba"), cast(dchar)'a') == 0);
+ assert(indexOf(to!S("def"), cast(dchar)'f') == 2);
+
+ assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1);
+ assert(indexOf(to!S("Abba"), cast(dchar)'a', No.caseSensitive) == 0);
+ assert(indexOf(to!S("def"), cast(dchar)'F', No.caseSensitive) == 2);
+ assert(indexOf(to!S("ödef"), 'ö', No.caseSensitive) == 0);
+
+ S sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
+ assert(indexOf("def", cast(char)'f', No.caseSensitive) == 2);
+ assert(indexOf(sPlts, cast(char)'P', No.caseSensitive) == 23);
+ assert(indexOf(sPlts, cast(char)'R', No.caseSensitive) == 2);
+ }}
+
+ foreach (cs; EnumMembers!CaseSensitive)
+ {
+ assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', cs) == 9);
+ assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', cs) == 7);
+ assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', cs) == 6);
+
+ assert(indexOf("hello\U00010143\u0100\U00010143".byChar, '\u0100', cs) == 9);
+ assert(indexOf("hello\U00010143\u0100\U00010143".byWchar, '\u0100', cs) == 7);
+ assert(indexOf("hello\U00010143\u0100\U00010143".byDchar, '\u0100', cs) == 6);
+
+ assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, 'l', cs) == 2);
+ assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, '\u0100', cs) == 7);
+ assert(indexOf("hello\U0000EFFF\u0100\U00010143".byChar, '\u0100', cs) == 8);
+
+ assert(indexOf("hello\U00010100".byWchar, '\U00010100', cs) == 5);
+ assert(indexOf("hello\U00010100".byWchar, '\U00010101', cs) == -1);
+ }
+
+ char[10] fixedSizeArray = "0123456789";
+ assert(indexOf(fixedSizeArray, '2') == 2);
+ });
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!indexOf("std/string.d", '/', 0));
+ assert(testAliasedString!indexOf("std/string.d", '/', 1));
+ assert(testAliasedString!indexOf("std/string.d", '/', 4));
+
+ enum S : string { a = "std/string.d" }
+ assert(S.a.indexOf('/', 0) == 3);
+ assert(S.a.indexOf('/', 1) == 3);
+ assert(S.a.indexOf('/', 4) == -1);
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.indexOf('/', 0) == 3);
+ assert(sa.indexOf('/', 1) == 3);
+ assert(sa.indexOf('/', 4) == -1);
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.traits : EnumMembers;
+ import std.utf : byCodeUnit, byChar, byWchar;
+
+ assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2);
+ assert("hello".byWchar.indexOf(cast(dchar)'l', 1) == 2);
+ assert("hello".byWchar.indexOf(cast(dchar)'l', 6) == -1);
+
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
+ assert(indexOf(cast(S) null, cast(dchar)'a', 1) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', 1) == -1);
+ assert(indexOf(to!S("abba"), cast(dchar)'a', 1) == 3);
+ assert(indexOf(to!S("def"), cast(dchar)'f', 1) == 2);
+
+ assert((to!S("def")).indexOf(cast(dchar)'a', 1,
+ No.caseSensitive) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', 1,
+ No.caseSensitive) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', 12,
+ No.caseSensitive) == -1);
+ assert(indexOf(to!S("AbbA"), cast(dchar)'a', 2,
+ No.caseSensitive) == 3);
+ assert(indexOf(to!S("def"), cast(dchar)'F', 2, No.caseSensitive) == 2);
+
+ S sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
+ assert(indexOf("def", cast(char)'f', cast(uint) 2,
+ No.caseSensitive) == 2);
+ assert(indexOf(sPlts, cast(char)'P', 12, No.caseSensitive) == 23);
+ assert(indexOf(sPlts, cast(char)'R', cast(ulong) 1,
+ No.caseSensitive) == 2);
+ }}
+
+ foreach (cs; EnumMembers!CaseSensitive)
+ {
+ assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', 2, cs)
+ == 9);
+ assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', 3, cs)
+ == 7);
+ assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', 6, cs)
+ == 6);
+ }
+}
+
+private ptrdiff_t _indexOf(Range)(Range s, dchar c, CaseSensitive cs = Yes.caseSensitive)
+if (isInputRange!Range && isSomeChar!(ElementType!Range))
{
static import std.ascii;
static import std.uni;
@@ -484,11 +779,8 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return -1;
}
-/// Ditto
-ptrdiff_t indexOf(Range)(Range s, in dchar c, in size_t startIdx,
- in CaseSensitive cs = Yes.caseSensitive)
-if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
- !isConvertibleToString!Range)
+private ptrdiff_t _indexOf(Range)(Range s, dchar c, size_t startIdx, CaseSensitive cs = Yes.caseSensitive)
+if (isInputRange!Range && isSomeChar!(ElementType!Range))
{
static if (isSomeString!(typeof(s)) ||
(hasSlicing!(typeof(s)) && hasLength!(typeof(s))))
@@ -519,263 +811,165 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return -1;
}
-///
-@safe pure unittest
+private template _indexOfStr(CaseSensitive cs)
{
- import std.typecons : No;
-
- string s = "Hello World";
- assert(indexOf(s, 'W') == 6);
- assert(indexOf(s, 'Z') == -1);
- assert(indexOf(s, 'w', No.caseSensitive) == 6);
-}
-
-///
-@safe pure unittest
-{
- import std.typecons : No;
-
- string s = "Hello World";
- assert(indexOf(s, 'W', 4) == 6);
- assert(indexOf(s, 'Z', 100) == -1);
- assert(indexOf(s, 'w', 3, No.caseSensitive) == 6);
-}
-
-ptrdiff_t indexOf(Range)(auto ref Range s, in dchar c,
- in CaseSensitive cs = Yes.caseSensitive)
-if (isConvertibleToString!Range)
-{
- return indexOf!(StringTypeOf!Range)(s, c, cs);
-}
-
-ptrdiff_t indexOf(Range)(auto ref Range s, in dchar c, in size_t startIdx,
- in CaseSensitive cs = Yes.caseSensitive)
-if (isConvertibleToString!Range)
-{
- return indexOf!(StringTypeOf!Range)(s, c, startIdx, cs);
-}
-
-@safe pure unittest
-{
- assert(testAliasedString!indexOf("std/string.d", '/'));
-}
-
-@safe pure unittest
-{
- import std.conv : to;
- import std.exception : assertCTFEable;
- import std.traits : EnumMembers;
- import std.utf : byChar, byWchar, byDchar;
-
- assertCTFEable!(
- {
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
- assert(indexOf(cast(S) null, cast(dchar)'a') == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a') == -1);
- assert(indexOf(to!S("abba"), cast(dchar)'a') == 0);
- assert(indexOf(to!S("def"), cast(dchar)'f') == 2);
-
- assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a', No.caseSensitive) == -1);
- assert(indexOf(to!S("Abba"), cast(dchar)'a', No.caseSensitive) == 0);
- assert(indexOf(to!S("def"), cast(dchar)'F', No.caseSensitive) == 2);
- assert(indexOf(to!S("ödef"), 'ö', No.caseSensitive) == 0);
-
- S sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
- assert(indexOf("def", cast(char)'f', No.caseSensitive) == 2);
- assert(indexOf(sPlts, cast(char)'P', No.caseSensitive) == 23);
- assert(indexOf(sPlts, cast(char)'R', No.caseSensitive) == 2);
- }
-
- foreach (cs; EnumMembers!CaseSensitive)
+ private ptrdiff_t _indexOfStr(Range, Char)(Range s, const(Char)[] sub)
+ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ isSomeChar!Char)
{
- assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', cs) == 9);
- assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', cs) == 7);
- assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', cs) == 6);
+ alias Char1 = Unqual!(ElementEncodingType!Range);
- assert(indexOf("hello\U00010143\u0100\U00010143".byChar, '\u0100', cs) == 9);
- assert(indexOf("hello\U00010143\u0100\U00010143".byWchar, '\u0100', cs) == 7);
- assert(indexOf("hello\U00010143\u0100\U00010143".byDchar, '\u0100', cs) == 6);
-
- assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, 'l', cs) == 2);
- assert(indexOf("hello\U000007FF\u0100\U00010143".byChar, '\u0100', cs) == 7);
- assert(indexOf("hello\U0000EFFF\u0100\U00010143".byChar, '\u0100', cs) == 8);
-
- assert(indexOf("hello\U00010100".byWchar, '\U00010100', cs) == 5);
- assert(indexOf("hello\U00010100".byWchar, '\U00010101', cs) == -1);
- }
-
- char[10] fixedSizeArray = "0123456789";
- assert(indexOf(fixedSizeArray, '2') == 2);
- });
-}
-
-@safe pure unittest
-{
- assert(testAliasedString!indexOf("std/string.d", '/', 3));
-}
-
-@safe pure unittest
-{
- import std.conv : to;
- import std.traits : EnumMembers;
- import std.utf : byCodeUnit, byChar, byWchar;
+ static if (isSomeString!Range)
+ {
+ static if (is(Char1 == Char) && cs == Yes.caseSensitive)
+ {
+ import std.algorithm.searching : countUntil;
+ return s.representation.countUntil(sub.representation);
+ }
+ else
+ {
+ import std.algorithm.searching : find;
- assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2);
- assert("hello".byWchar.indexOf(cast(dchar)'l', 1) == 2);
- assert("hello".byWchar.indexOf(cast(dchar)'l', 6) == -1);
+ const(Char1)[] balance;
+ static if (cs == Yes.caseSensitive)
+ {
+ balance = find(s, sub);
+ }
+ else
+ {
+ balance = find!
+ ((a, b) => toLower(a) == toLower(b))
+ (s, sub);
+ }
+ return () @trusted { return balance.empty ? -1 : balance.ptr - s.ptr; } ();
+ }
+ }
+ else
+ {
+ if (s.empty)
+ return -1;
+ if (sub.empty)
+ return 0; // degenerate case
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
- assert(indexOf(cast(S) null, cast(dchar)'a', 1) == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a', 1) == -1);
- assert(indexOf(to!S("abba"), cast(dchar)'a', 1) == 3);
- assert(indexOf(to!S("def"), cast(dchar)'f', 1) == 2);
+ import std.utf : byDchar, codeLength;
+ auto subr = sub.byDchar; // decode sub[] by dchar's
+ dchar sub0 = subr.front; // cache first character of sub[]
+ subr.popFront();
- assert((to!S("def")).indexOf(cast(dchar)'a', 1,
- No.caseSensitive) == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a', 1,
- No.caseSensitive) == -1);
- assert(indexOf(to!S("def"), cast(dchar)'a', 12,
- No.caseSensitive) == -1);
- assert(indexOf(to!S("AbbA"), cast(dchar)'a', 2,
- No.caseSensitive) == 3);
- assert(indexOf(to!S("def"), cast(dchar)'F', 2, No.caseSensitive) == 2);
+ // Special case for single character search
+ if (subr.empty)
+ return indexOf(s, sub0, cs);
- S sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
- assert(indexOf("def", cast(char)'f', cast(uint) 2,
- No.caseSensitive) == 2);
- assert(indexOf(sPlts, cast(char)'P', 12, No.caseSensitive) == 23);
- assert(indexOf(sPlts, cast(char)'R', cast(ulong) 1,
- No.caseSensitive) == 2);
- }
+ static if (cs == No.caseSensitive)
+ sub0 = toLower(sub0);
- foreach (cs; EnumMembers!CaseSensitive)
- {
- assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', 2, cs)
- == 9);
- assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', 3, cs)
- == 7);
- assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', 6, cs)
- == 6);
+ /* Classic double nested loop search algorithm
+ */
+ ptrdiff_t index = 0; // count code unit index into s
+ for (auto sbydchar = s.byDchar(); !sbydchar.empty; sbydchar.popFront())
+ {
+ dchar c2 = sbydchar.front;
+ static if (cs == No.caseSensitive)
+ c2 = toLower(c2);
+ if (c2 == sub0)
+ {
+ auto s2 = sbydchar.save; // why s must be a forward range
+ foreach (c; subr.save)
+ {
+ s2.popFront();
+ if (s2.empty)
+ return -1;
+ static if (cs == Yes.caseSensitive)
+ {
+ if (c != s2.front)
+ goto Lnext;
+ }
+ else
+ {
+ if (toLower(c) != toLower(s2.front))
+ goto Lnext;
+ }
+ }
+ return index;
+ }
+ Lnext:
+ index += codeLength!Char1(c2);
+ }
+ return -1;
+ }
}
}
/++
- Searches for substring in $(D s).
+ Searches for substring in `s`.
Params:
s = string or ForwardRange of characters to search in correct UTF format
sub = substring to search for
startIdx = the index into s to start searching from
- cs = $(D Yes.caseSensitive) or $(D No.caseSensitive)
+ cs = `Yes.caseSensitive` (default) or `No.caseSensitive`
Returns:
- the index of the first occurrence of $(D sub) in $(D s) with
- respect to the start index $(D startIdx). If $(D sub) is not found,
- then $(D -1) is returned.
+ the index of the first occurrence of `sub` in `s` with
+ respect to the start index `startIdx`. If `sub` is not found,
+ then `-1` is returned.
If the arguments are not valid UTF, the result will still
be in the range [-1 .. s.length], but will not be reliable otherwise.
- If $(D sub) is found the value of the returned index is at least
- $(D startIdx).
+ If `sub` is found the value of the returned index is at least
+ `startIdx`.
Throws:
- If the sequence starting at $(D startIdx) does not represent a well
+ If the sequence starting at `startIdx` does not represent a well
formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
Bugs:
Does not work with case insensitive strings where the mapping of
tolower and toupper is not 1:1.
+/
-ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub,
- in CaseSensitive cs = Yes.caseSensitive)
+ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub)
if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
isSomeChar!Char)
{
- alias Char1 = Unqual!(ElementEncodingType!Range);
-
- static if (isSomeString!Range)
- {
- import std.algorithm.searching : find;
+ return _indexOfStr!(Yes.caseSensitive)(s, sub);
+}
- const(Char1)[] balance;
- if (cs == Yes.caseSensitive)
- {
- balance = find(s, sub);
- }
- else
- {
- balance = find!
- ((a, b) => toLower(a) == toLower(b))
- (s, sub);
- }
- return () @trusted { return balance.empty ? -1 : balance.ptr - s.ptr; } ();
- }
+/// Ditto
+ptrdiff_t indexOf(Range, Char)(Range s, const(Char)[] sub, in CaseSensitive cs)
+if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ isSomeChar!Char)
+{
+ if (cs == Yes.caseSensitive)
+ return indexOf(s, sub);
else
- {
- if (s.empty)
- return -1;
- if (sub.empty)
- return 0; // degenerate case
-
- import std.utf : byDchar, codeLength;
- auto subr = sub.byDchar; // decode sub[] by dchar's
- dchar sub0 = subr.front; // cache first character of sub[]
- subr.popFront();
-
- // Special case for single character search
- if (subr.empty)
- return indexOf(s, sub0, cs);
-
- if (cs == No.caseSensitive)
- sub0 = toLower(sub0);
+ return _indexOfStr!(No.caseSensitive)(s, sub);
+}
- /* Classic double nested loop search algorithm
- */
- ptrdiff_t index = 0; // count code unit index into s
- for (auto sbydchar = s.byDchar(); !sbydchar.empty; sbydchar.popFront())
- {
- dchar c2 = sbydchar.front;
- if (cs == No.caseSensitive)
- c2 = toLower(c2);
- if (c2 == sub0)
- {
- auto s2 = sbydchar.save; // why s must be a forward range
- foreach (c; subr.save)
- {
- s2.popFront();
- if (s2.empty)
- return -1;
- if (cs == Yes.caseSensitive ? c != s2.front
- : toLower(c) != toLower(s2.front)
- )
- goto Lnext;
- }
- return index;
- }
- Lnext:
- index += codeLength!Char1(c2);
- }
+/// Ditto
+ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
+ in size_t startIdx)
+@safe
+if (isSomeChar!Char1 && isSomeChar!Char2)
+{
+ if (startIdx >= s.length)
return -1;
- }
+ ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub);
+ if (foundIdx == -1)
+ return -1;
+ return foundIdx + cast(ptrdiff_t) startIdx;
}
/// Ditto
ptrdiff_t indexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
- in size_t startIdx, in CaseSensitive cs = Yes.caseSensitive)
+ in size_t startIdx, in CaseSensitive cs)
@safe
if (isSomeChar!Char1 && isSomeChar!Char2)
{
- if (startIdx < s.length)
- {
- ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub, cs);
- if (foundIdx != -1)
- {
- return foundIdx + cast(ptrdiff_t) startIdx;
- }
- }
- return -1;
+ if (startIdx >= s.length)
+ return -1;
+ ptrdiff_t foundIdx = indexOf(s[startIdx .. $], sub, cs);
+ if (foundIdx == -1)
+ return -1;
+ return foundIdx + cast(ptrdiff_t) startIdx;
}
///
@@ -800,8 +994,25 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
assert(indexOf(s, "wO", No.caseSensitive) == 6);
}
+@safe pure nothrow @nogc unittest
+{
+ string s = "Hello World";
+ assert(indexOf(s, "Wo", 4) == 6);
+ assert(indexOf(s, "Zo", 100) == -1);
+ assert(indexOf(s, "Wo") == 6);
+ assert(indexOf(s, "Zo") == -1);
+}
+
+ptrdiff_t indexOf(Range, Char)(auto ref Range s, const(Char)[] sub)
+if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ isSomeChar!Char) &&
+ is(StringTypeOf!Range))
+{
+ return indexOf!(StringTypeOf!Range)(s, sub);
+}
+
ptrdiff_t indexOf(Range, Char)(auto ref Range s, const(Char)[] sub,
- in CaseSensitive cs = Yes.caseSensitive)
+ in CaseSensitive cs)
if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
isSomeChar!Char) &&
is(StringTypeOf!Range))
@@ -809,7 +1020,7 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return indexOf!(StringTypeOf!Range)(s, sub, cs);
}
-@safe pure unittest
+@safe pure nothrow @nogc unittest
{
assert(testAliasedString!indexOf("std/string.d", "string"));
}
@@ -822,10 +1033,10 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(indexOf(cast(S) null, to!T("a")) == -1);
assert(indexOf(to!S("def"), to!T("a")) == -1);
assert(indexOf(to!S("abba"), to!T("a")) == 0);
@@ -855,7 +1066,7 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
// Thanks to Carlos Santander B. and zwang
assert(indexOf("sus mejores cortesanos. Se embarcaron en el puerto de Dubai y",
to!T("page-break-before"), No.caseSensitive) == -1);
- }();
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -890,10 +1101,10 @@ unittest
import std.conv : to;
import std.traits : EnumMembers;
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(indexOf(cast(S) null, to!T("a"), 1337) == -1);
assert(indexOf(to!S("def"), to!T("a"), 0) == -1);
assert(indexOf(to!S("abba"), to!T("a"), 2) == 3);
@@ -930,7 +1141,7 @@ unittest
// In order for indexOf with and without index to be consistent
assert(indexOf(to!S(""), to!T("")) == indexOf(to!S(""), to!T(""), 0));
- }();
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -949,19 +1160,19 @@ unittest
s = string to search
c = character to search for
startIdx = the index into s to start searching from
- cs = $(D Yes.caseSensitive) or $(D No.caseSensitive)
+ cs = `Yes.caseSensitive` or `No.caseSensitive`
Returns:
- The index of the last occurrence of $(D c) in $(D s). If $(D c) is not
- found, then $(D -1) is returned. The $(D startIdx) slices $(D s) in
- the following way $(D s[0 .. startIdx]). $(D startIdx) represents a
- codeunit index in $(D s).
+ The index of the last occurrence of `c` in `s`. If `c` is not
+ found, then `-1` is returned. The `startIdx` slices `s` in
+ the following way $(D s[0 .. startIdx]). `startIdx` represents a
+ codeunit index in `s`.
Throws:
- If the sequence ending at $(D startIdx) does not represent a well
+ If the sequence ending at `startIdx` does not represent a well
formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
- $(D cs) indicates whether the comparisons are case sensitive.
+ `cs` indicates whether the comparisons are case sensitive.
+/
ptrdiff_t lastIndexOf(Char)(const(Char)[] s, in dchar c,
in CaseSensitive cs = Yes.caseSensitive) @safe pure
@@ -1068,8 +1279,8 @@ if (isSomeChar!Char)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOf(cast(S) null, 'a') == -1);
assert(lastIndexOf(to!S("def"), 'a') == -1);
assert(lastIndexOf(to!S("abba"), 'a') == 3);
@@ -1089,7 +1300,7 @@ if (isSomeChar!Char)
assert(lastIndexOf(to!S("def"), 'f', No.caseSensitive) == 2);
assert(lastIndexOf(sPlts, 'M', No.caseSensitive) == 34);
assert(lastIndexOf(sPlts, 'S', No.caseSensitive) == 40);
- }
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1105,8 +1316,8 @@ if (isSomeChar!Char)
import std.conv : to;
import std.traits : EnumMembers;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOf(cast(S) null, 'a') == -1);
assert(lastIndexOf(to!S("def"), 'a') == -1);
assert(lastIndexOf(to!S("abba"), 'a', 3) == 0);
@@ -1123,7 +1334,7 @@ if (isSomeChar!Char)
assert(lastIndexOf(to!S("def"), 'f', 4, No.caseSensitive) == -1);
assert(lastIndexOf(sPlts, 'M', sPlts.length -2, No.caseSensitive) == 34);
assert(lastIndexOf(sPlts, 'S', sPlts.length -2, No.caseSensitive) == 40);
- }
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1138,19 +1349,19 @@ if (isSomeChar!Char)
s = string to search
sub = substring to search for
startIdx = the index into s to start searching from
- cs = $(D Yes.caseSensitive) or $(D No.caseSensitive)
+ cs = `Yes.caseSensitive` or `No.caseSensitive`
Returns:
- the index of the last occurrence of $(D sub) in $(D s). If $(D sub) is
- not found, then $(D -1) is returned. The $(D startIdx) slices $(D s)
- in the following way $(D s[0 .. startIdx]). $(D startIdx) represents a
- codeunit index in $(D s).
+ the index of the last occurrence of `sub` in `s`. If `sub` is
+ not found, then `-1` is returned. The `startIdx` slices `s`
+ in the following way $(D s[0 .. startIdx]). `startIdx` represents a
+ codeunit index in `s`.
Throws:
- If the sequence ending at $(D startIdx) does not represent a well
+ If the sequence ending at `startIdx` does not represent a well
formed codepoint, then a $(REF UTFException, std,utf) may be thrown.
- $(D cs) indicates whether the comparisons are case sensitive.
+ `cs` indicates whether the comparisons are case sensitive.
+/
ptrdiff_t lastIndexOf(Char1, Char2)(const(Char1)[] s, const(Char2)[] sub,
in CaseSensitive cs = Yes.caseSensitive) @safe pure
@@ -1169,7 +1380,7 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
if (cs == Yes.caseSensitive)
{
- static if (is(Unqual!Char1 == Unqual!Char2))
+ static if (is(immutable Char1 == immutable Char2))
{
import core.stdc.string : memcmp;
@@ -1181,12 +1392,8 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
{
if (__ctfe)
{
- foreach (j; 1 .. sub.length)
- {
- if (s[i + j] != sub[j])
- continue;
- }
- return i;
+ if (s[i + 1 .. i + sub.length] == sub[1 .. $])
+ return i;
}
else
{
@@ -1270,8 +1477,8 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").lastIndexOf("hello");
assert(r == -1, to!string(r));
@@ -1280,7 +1487,7 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
r = to!S("").lastIndexOf("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -1291,10 +1498,10 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
enum typeStr = S.stringof ~ " " ~ T.stringof;
assert(lastIndexOf(cast(S) null, to!T("a")) == -1, typeStr);
@@ -1329,7 +1536,7 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
assert(lastIndexOf(sPlts, to!T("FOuRTh"), No.caseSensitive) == 10, typeStr);
assert(lastIndexOf(sMars, to!T("whO\'s \'MY"), No.caseSensitive) == 0, typeStr);
assert(lastIndexOf(sMars, to!T(sMars), No.caseSensitive) == 0, typeStr);
- }();
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1343,20 +1550,21 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
});
}
-@safe pure unittest // issue13529
+// https://issues.dlang.org/show_bug.cgi?id=13529
+@safe pure unittest
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
enum typeStr = S.stringof ~ " " ~ T.stringof;
auto idx = lastIndexOf(to!T("Hällö Wörldö ö"),to!S("ö ö"));
assert(idx != -1, to!string(idx) ~ " " ~ typeStr);
idx = lastIndexOf(to!T("Hällö Wörldö ö"),to!S("ö öd"));
assert(idx == -1, to!string(idx) ~ " " ~ typeStr);
- }
+ }}
}
}
@@ -1365,10 +1573,10 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
import std.conv : to;
import std.traits : EnumMembers;
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
enum typeStr = S.stringof ~ " " ~ T.stringof;
assert(lastIndexOf(cast(S) null, to!T("a")) == -1, typeStr);
@@ -1396,7 +1604,7 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
assert(lastIndexOf(to!S("abcdefcdef"), to!T("cd"), 4, No.caseSensitive) == 2, typeStr);
assert(lastIndexOf(to!S("abcdefcdef"), to!T("def"), 6, No.caseSensitive) == 3, typeStr);
assert(lastIndexOf(to!S(""), to!T(""), 0) == lastIndexOf(to!S(""), to!T("")), typeStr);
- }();
+ }}
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1409,6 +1617,19 @@ if (isSomeChar!Char1 && isSomeChar!Char2)
}
}
+// https://issues.dlang.org/show_bug.cgi?id=20783
+@safe pure @nogc unittest
+{
+ enum lastIndex = "aa".lastIndexOf("ab");
+ assert(lastIndex == -1);
+}
+
+@safe pure @nogc unittest
+{
+ enum lastIndex = "hello hello hell h".lastIndexOf("hello");
+ assert(lastIndex == 6);
+}
+
private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)(
const(Char)[] haystack, const(Char2)[] needles,
in CaseSensitive cs = Yes.caseSensitive) @safe pure
@@ -1527,10 +1748,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
Returns the index of the first occurrence of any of the elements in $(D
- needles) in $(D haystack). If no element of $(D needles) is found,
- then $(D -1) is returned. The $(D startIdx) slices $(D haystack) in the
- following way $(D haystack[startIdx .. $]). $(D startIdx) represents a
- codeunit index in $(D haystack). If the sequence ending at $(D startIdx)
+ needles) in `haystack`. If no element of `needles` is found,
+ then `-1` is returned. The `startIdx` slices `haystack` in the
+ following way $(D haystack[startIdx .. $]). `startIdx` represents a
+ codeunit index in `haystack`. If the sequence ending at `startIdx`
does not represent a well formed codepoint, then a $(REF UTFException, std,utf)
may be thrown.
@@ -1539,7 +1760,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
needles = Strings to search for in haystack.
startIdx = slices haystack like this $(D haystack[startIdx .. $]). If
the startIdx is greater equal the length of haystack the functions
- returns $(D -1).
+ returns `-1`.
cs = Indicates whether the comparisons are case sensitive.
*/
ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles,
@@ -1593,8 +1814,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").indexOfAny("hello");
assert(r == -1, to!string(r));
@@ -1603,7 +1824,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
r = to!S("").indexOfAny("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -1613,10 +1834,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
assert(indexOfAny(cast(S) null, to!T("a")) == -1);
assert(indexOfAny(to!S("def"), to!T("rsa")) == -1);
assert(indexOfAny(to!S("abba"), to!T("a")) == 0);
@@ -1639,7 +1860,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
No.caseSensitive) == 0);
assert(indexOfAny("\u0100", to!T("\u0100"), No.caseSensitive) == 0);
- }();
+ }
}
}
);
@@ -1650,10 +1871,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
import std.conv : to;
import std.traits : EnumMembers;
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
assert(indexOfAny(cast(S) null, to!T("a"), 1337) == -1);
assert(indexOfAny(to!S("def"), to!T("AaF"), 0) == -1);
assert(indexOfAny(to!S("abba"), to!T("NSa"), 2) == 3);
@@ -1678,7 +1899,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
assert(indexOfAny("\u0100", to!T("\u0100"), 0,
No.caseSensitive) == 0);
- }();
+ }
foreach (cs; EnumMembers!CaseSensitive)
{
@@ -1694,10 +1915,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
Returns the index of the last occurrence of any of the elements in $(D
- needles) in $(D haystack). If no element of $(D needles) is found,
- then $(D -1) is returned. The $(D stopIdx) slices $(D haystack) in the
- following way $(D s[0 .. stopIdx]). $(D stopIdx) represents a codeunit
- index in $(D haystack). If the sequence ending at $(D startIdx) does not
+ needles) in `haystack`. If no element of `needles` is found,
+ then `-1` is returned. The `stopIdx` slices `haystack` in the
+ following way $(D s[0 .. stopIdx]). `stopIdx` represents a codeunit
+ index in `haystack`. If the sequence ending at `startIdx` does not
represent a well formed codepoint, then a $(REF UTFException, std,utf) may be
thrown.
@@ -1706,7 +1927,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
needles = Strings to search for in haystack.
stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]). If
the stopIdx is greater equal the length of haystack the functions
- returns $(D -1).
+ returns `-1`.
cs = Indicates whether the comparisons are case sensitive.
*/
ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack,
@@ -1757,8 +1978,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").lastIndexOfAny("hello");
assert(r == -1, to!string(r));
@@ -1767,7 +1988,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
r = to!S("").lastIndexOfAny("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -1777,10 +1998,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOfAny(cast(S) null, to!T("a")) == -1);
assert(lastIndexOfAny(to!S("def"), to!T("rsa")) == -1);
assert(lastIndexOfAny(to!S("abba"), to!T("a")) == 3);
@@ -1817,7 +2038,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
assert(lastIndexOfAny("\u0100", to!T("\u0100"),
No.caseSensitive) == 0);
- }();
+ }}
}
}
);
@@ -1830,10 +2051,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
enum typeStr = S.stringof ~ " " ~ T.stringof;
assert(lastIndexOfAny(cast(S) null, to!T("a"), 1337) == -1,
@@ -1869,7 +2090,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
No.caseSensitive) == -1, typeStr);
assert(lastIndexOfAny(to!S("ÖABCDEFCDEF"), to!T("ö"), 2,
No.caseSensitive) == 0, typeStr);
- }();
+ }}
}
}
);
@@ -1877,15 +2098,15 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
Returns the index of the first occurrence of any character not an elements
- in $(D needles) in $(D haystack). If all element of $(D haystack) are
- element of $(D needles) $(D -1) is returned.
+ in `needles` in `haystack`. If all element of `haystack` are
+ element of `needles` `-1` is returned.
Params:
haystack = String to search for needles in.
needles = Strings to search for in haystack.
startIdx = slices haystack like this $(D haystack[startIdx .. $]). If
the startIdx is greater equal the length of haystack the functions
- returns $(D -1).
+ returns `-1`.
cs = Indicates whether the comparisons are case sensitive.
*/
ptrdiff_t indexOfNeither(Char,Char2)(const(Char)[] haystack,
@@ -1935,8 +2156,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").indexOfNeither("hello");
assert(r == -1, to!string(r));
@@ -1945,7 +2166,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
r = to!S("").indexOfNeither("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -1955,10 +2176,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
assert(indexOfNeither(cast(S) null, to!T("a")) == -1);
assert(indexOfNeither("abba", "a") == 1);
@@ -1986,7 +2207,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
to!string(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"),
No.caseSensitive)));
}
- }();
+ }
}
}
);
@@ -1999,10 +2220,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {
assert(indexOfNeither(cast(S) null, to!T("a"), 1) == -1);
assert(indexOfNeither(to!S("def"), to!T("a"), 1) == 1,
to!string(indexOfNeither(to!S("def"), to!T("a"), 1)));
@@ -2029,7 +2250,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
No.caseSensitive) == 2, to!string(indexOfNeither(
to!S("öDfEfffg"), to!T("äDi"), 2, No.caseSensitive)));
}
- }();
+ }
}
}
);
@@ -2037,15 +2258,15 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
Returns the last index of the first occurence of any character that is not
- an elements in $(D needles) in $(D haystack). If all element of
- $(D haystack) are element of $(D needles) $(D -1) is returned.
+ an elements in `needles` in `haystack`. If all element of
+ `haystack` are element of `needles` `-1` is returned.
Params:
haystack = String to search for needles in.
needles = Strings to search for in haystack.
stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]) If
the stopIdx is greater equal the length of haystack the functions
- returns $(D -1).
+ returns `-1`.
cs = Indicates whether the comparisons are case sensitive.
*/
ptrdiff_t lastIndexOfNeither(Char,Char2)(const(Char)[] haystack,
@@ -2089,8 +2310,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
{
import std.conv : to;
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
auto r = to!S("").lastIndexOfNeither("hello");
assert(r == -1, to!string(r));
@@ -2099,7 +2320,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
r = to!S("").lastIndexOfNeither("");
assert(r == -1, to!string(r));
- }
+ }}
}
@safe pure unittest
@@ -2109,10 +2330,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOfNeither(cast(S) null, to!T("a")) == -1);
assert(lastIndexOfNeither(to!S("def"), to!T("rsa")) == 2);
assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2);
@@ -2141,7 +2362,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
assert(lastIndexOfNeither(to!S("dfeffgfffö"), to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"),
No.caseSensitive) == 8, to!string(lastIndexOfNeither(to!S("dfeffgfffö"),
to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), No.caseSensitive)));
- }();
+ }}
}
}
);
@@ -2154,10 +2375,10 @@ if (isSomeChar!Char && isSomeChar!Char2)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
+ static foreach (S; AliasSeq!(string, wstring, dstring))
{
- foreach (T; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(string, wstring, dstring))
+ {{
assert(lastIndexOfNeither(cast(S) null, to!T("a"), 1337) == -1);
assert(lastIndexOfNeither(to!S("def"), to!T("f")) == 1);
assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2);
@@ -2185,7 +2406,7 @@ if (isSomeChar!Char && isSomeChar!Char2)
assert(lastIndexOfNeither(to!S("dfefffg"), to!T("NSA"), 2,
No.caseSensitive) == 1, to!string(lastIndexOfNeither(
to!S("dfefffg"), to!T("NSA"), 2, No.caseSensitive)));
- }();
+ }}
}
}
);
@@ -2193,8 +2414,8 @@ if (isSomeChar!Char && isSomeChar!Char2)
/**
* Returns the _representation of a string, which has the same type
- * as the string except the character type is replaced by $(D ubyte),
- * $(D ushort), or $(D uint) depending on the character width.
+ * as the string except the character type is replaced by `ubyte`,
+ * `ushort`, or `uint` depending on the character width.
*
* Params:
* s = The string to return the _representation of.
@@ -2233,10 +2454,10 @@ if (isSomeChar!Char)
assert(representation(str) is cast(T[]) str);
}
- foreach (Type; AliasSeq!(Tuple!(char , ubyte ),
+ static foreach (Type; AliasSeq!(Tuple!(char , ubyte ),
Tuple!(wchar, ushort),
Tuple!(dchar, uint )))
- {
+ {{
alias Char = Fields!Type[0];
alias Int = Fields!Type[1];
enum immutable(Char)[] hello = "hello";
@@ -2246,13 +2467,13 @@ if (isSomeChar!Char)
test!( Char, Int)(hello.dup);
test!( shared Char, shared Int)(cast(shared) hello.dup);
test!(const shared Char, const shared Int)(hello);
- }
+ }}
});
}
/**
- * Capitalize the first character of $(D s) and convert the rest of $(D s) to
+ * Capitalize the first character of `s` and convert the rest of `s` to
* lowercase.
*
* Params:
@@ -2300,8 +2521,8 @@ if (!isSomeString!S && is(StringTypeOf!S))
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {{
S s1 = to!S("FoL");
S s2;
@@ -2325,15 +2546,15 @@ if (!isSomeString!S && is(StringTypeOf!S))
s2 = capitalize(s1);
assert(cmp(s2, "\u0053 \u0069") == 0);
assert(s2 !is s1);
- }
+ }}
});
}
/++
- Split $(D s) into an array of lines according to the unicode standard using
- $(D '\r'), $(D '\n'), $(D "\r\n"), $(REF lineSep, std,uni),
- $(REF paraSep, std,uni), $(D U+0085) (NEL), $(D '\v') and $(D '\f')
- as delimiters. If $(D keepTerm) is set to $(D KeepTerminator.yes), then the
+ Split `s` into an array of lines according to the unicode standard using
+ `'\r'`, `'\n'`, `"\r\n"`, $(REF lineSep, std,uni),
+ $(REF paraSep, std,uni), `U+0085` (NEL), `'\v'` and `'\f'`
+ as delimiters. If `keepTerm` is set to `KeepTerminator.yes`, then the
delimiter is included in the strings returned.
Does not throw on invalid UTF; such is simply passed unchanged
@@ -2345,11 +2566,11 @@ if (!isSomeString!S && is(StringTypeOf!S))
Adheres to $(HTTP www.unicode.org/versions/Unicode7.0.0/ch05.pdf, Unicode 7.0).
Params:
- s = a string of $(D chars), $(D wchars), or $(D dchars), or any custom
- type that casts to a $(D string) type
+ s = a string of `chars`, `wchars`, or `dchars`, or any custom
+ type that casts to a `string` type
keepTerm = whether delimiter is included or not in the results
Returns:
- array of strings, each element is a line that is a slice of $(D s)
+ array of strings, each element is a line that is a slice of `s`
See_Also:
$(LREF lineSplitter)
$(REF splitter, std,algorithm)
@@ -2358,14 +2579,14 @@ if (!isSomeString!S && is(StringTypeOf!S))
alias KeepTerminator = Flag!"keepTerminator";
/// ditto
-S[] splitLines(S)(S s, in KeepTerminator keepTerm = No.keepTerminator) @safe pure
-if (isSomeString!S)
+C[][] splitLines(C)(C[] s, KeepTerminator keepTerm = No.keepTerminator) @safe pure
+if (isSomeChar!C)
{
import std.array : appender;
import std.uni : lineSep, paraSep;
size_t iStart = 0;
- auto retval = appender!(S[])();
+ auto retval = appender!(C[][])();
for (size_t i; i < s.length; ++i)
{
@@ -2454,15 +2675,19 @@ if (isSomeString!S)
assert(splitLines(s) == [s]);
}
-auto splitLines(S)(auto ref S s, in KeepTerminator keepTerm = No.keepTerminator)
-if (!isSomeString!S && is(StringTypeOf!S))
+@safe pure nothrow unittest
{
- return splitLines!(StringTypeOf!S)(s, keepTerm);
+ assert(testAliasedString!splitLines("hello\nworld"));
+
+ enum S : string { a = "hello\nworld" }
+ assert(S.a.splitLines() == ["hello", "world"]);
}
-@safe pure nothrow unittest
+@system pure nothrow unittest
{
- assert(testAliasedString!splitLines("hello\nworld"));
+ // dip1000 cannot express an array of scope arrays, so this is not @safe
+ char[11] sa = "hello\nworld";
+ assert(sa.splitLines() == ["hello", "world"]);
}
@safe pure unittest
@@ -2472,8 +2697,8 @@ if (!isSomeString!S && is(StringTypeOf!S))
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {{
auto s = to!S(
"\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\nsunday\n" ~
"mon\u2030day\nschadenfreude\vkindergarten\f\vcookies\u0085"
@@ -2525,7 +2750,7 @@ if (!isSomeString!S && is(StringTypeOf!S))
lines = splitLines(s, Yes.keepTerminator);
assert(lines.length == 14);
assert(lines[13] == "cookies");
- }
+ }}
});
}
@@ -2644,7 +2869,7 @@ public:
{
if (iStart == _unComputed)
{
- assert(!empty);
+ assert(!empty, "Can not popFront an empty range");
front;
}
iStart = _unComputed;
@@ -2663,9 +2888,9 @@ public:
/***********************************
* Split an array or slicable range of characters into a range of lines
- using $(D '\r'), $(D '\n'), $(D '\v'), $(D '\f'), $(D "\r\n"),
- $(REF lineSep, std,uni), $(REF paraSep, std,uni) and $(D '\u0085') (NEL)
- as delimiters. If $(D keepTerm) is set to $(D Yes.keepTerminator), then the
+ using `'\r'`, `'\n'`, `'\v'`, `'\f'`, `"\r\n"`,
+ $(REF lineSep, std,uni), $(REF paraSep, std,uni) and `'\u0085'` (NEL)
+ as delimiters. If `keepTerm` is set to `Yes.keepTerminator`, then the
delimiter is included in the slices returned.
Does not throw on invalid UTF; such is simply passed unchanged
@@ -2676,10 +2901,10 @@ public:
Does not allocate memory.
Params:
- r = array of $(D chars), $(D wchars), or $(D dchars) or a slicable range
+ r = array of `chars`, `wchars`, or `dchars` or a slicable range
keepTerm = whether delimiter is included or not in the results
Returns:
- range of slices of the input range $(D r)
+ range of slices of the input range `r`
See_Also:
$(LREF splitLines)
@@ -2687,13 +2912,18 @@ public:
$(REF splitter, std,regex)
*/
auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range)(Range r)
-if ((hasSlicing!Range && hasLength!Range && isSomeChar!(ElementType!Range) ||
- isSomeString!Range) &&
- !isConvertibleToString!Range)
+if (hasSlicing!Range && hasLength!Range && isSomeChar!(ElementType!Range) && !isSomeString!Range)
{
return LineSplitter!(keepTerm, Range)(r);
}
+/// Ditto
+auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, C)(C[] r)
+if (isSomeChar!C)
+{
+ return LineSplitter!(keepTerm, C[])(r);
+}
+
///
@safe pure unittest
{
@@ -2707,12 +2937,6 @@ if ((hasSlicing!Range && hasLength!Range && isSomeChar!(ElementType!Range) ||
assert(lineSplitter(s).array == splitLines(s));
}
-auto lineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range)(auto ref Range r)
-if (isConvertibleToString!Range)
-{
- return LineSplitter!(keepTerm, StringTypeOf!Range)(r);
-}
-
@safe pure unittest
{
import std.array : array;
@@ -2721,8 +2945,8 @@ if (isConvertibleToString!Range)
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {{
auto s = to!S(
"\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\n" ~
"sunday\nmon\u2030day\nschadenfreude\vkindergarten\f\vcookies\u0085"
@@ -2775,7 +2999,7 @@ if (isConvertibleToString!Range)
lines = lineSplitter!(Yes.keepTerminator)(s).array;
assert(lines.length == 14);
assert(lines[13] == "cookies");
- }
+ }}
});
}
@@ -2796,9 +3020,17 @@ if (isConvertibleToString!Range)
@nogc @safe pure unittest
{
import std.algorithm.comparison : equal;
+ import std.range : only;
+
auto s = "std/string.d";
auto as = TestAliasedString(s);
assert(equal(s.lineSplitter(), as.lineSplitter()));
+
+ enum S : string { a = "hello\nworld" }
+ assert(equal(S.a.lineSplitter(), only("hello", "world")));
+
+ char[S.a.length] sa = S.a[];
+ assert(equal(sa.lineSplitter(), only("hello", "world")));
}
@safe pure unittest
@@ -2813,15 +3045,18 @@ if (isConvertibleToString!Range)
}
/++
- Strips leading whitespace (as defined by $(REF isWhite, std,uni)).
+ Strips leading whitespace (as defined by $(REF isWhite, std,uni)) or
+ as specified in the second argument.
Params:
input = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
of characters
+ chars = string of characters to be stripped
- Returns: $(D input) stripped of leading whitespace.
+ Returns: `input` stripped of leading whitespace or characters
+ specified in the second argument.
- Postconditions: $(D input) and the returned value
+ Postconditions: `input` and the returned value
will share the same tail (see $(REF sameTail, std,array)).
See_Also:
@@ -2831,38 +3066,104 @@ auto stripLeft(Range)(Range input)
if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
!isInfinite!Range && !isConvertibleToString!Range)
{
+ import std.traits : isDynamicArray;
static import std.ascii;
static import std.uni;
- import std.utf : decodeFront;
- while (!input.empty)
+ static if (is(immutable ElementEncodingType!Range == immutable dchar)
+ || is(immutable ElementEncodingType!Range == immutable wchar))
{
- auto c = input.front;
- if (std.ascii.isASCII(c))
+ // Decoding is never needed for dchar. It happens not to be needed
+ // here for wchar because no whitepace is outside the basic
+ // multilingual plane meaning every whitespace character is encoded
+ // with a single wchar and due to the design of UTF-16 those wchars
+ // will not occur as part of the encoding of multi-wchar codepoints.
+ static if (isDynamicArray!Range)
{
- if (!std.ascii.isWhite(c))
- break;
- input.popFront();
+ foreach (i; 0 .. input.length)
+ {
+ if (!std.uni.isWhite(input[i]))
+ return input[i .. $];
+ }
+ return input[$ .. $];
+ }
+ else
+ {
+ while (!input.empty)
+ {
+ if (!std.uni.isWhite(input.front))
+ break;
+ input.popFront();
+ }
+ return input;
+ }
+ }
+ else
+ {
+ static if (isDynamicArray!Range)
+ {
+ // ASCII optimization for dynamic arrays.
+ size_t i = 0;
+ for (const size_t end = input.length; i < end; ++i)
+ {
+ auto c = input[i];
+ if (c >= 0x80) goto NonAsciiPath;
+ if (!std.ascii.isWhite(c)) break;
+ }
+ input = input[i .. $];
+ return input;
+
+ NonAsciiPath:
+ input = input[i .. $];
+ // Fall through to standard case.
+ }
+
+ import std.utf : decode, decodeFront, UseReplacementDchar;
+
+ static if (isNarrowString!Range)
+ {
+ for (size_t index = 0; index < input.length;)
+ {
+ const saveIndex = index;
+ if (!std.uni.isWhite(decode!(UseReplacementDchar.yes)(input, index)))
+ return input[saveIndex .. $];
+ }
+ return input[$ .. $];
}
else
{
- auto save = input.save;
- auto dc = decodeFront(input);
- if (!std.uni.isWhite(dc))
- return save;
+ while (!input.empty)
+ {
+ auto c = input.front;
+ if (std.ascii.isASCII(c))
+ {
+ if (!std.ascii.isWhite(c))
+ break;
+ input.popFront();
+ }
+ else
+ {
+ auto save = input.save;
+ auto dc = decodeFront!(UseReplacementDchar.yes)(input);
+ if (!std.uni.isWhite(dc))
+ return save;
+ }
+ }
+ return input;
}
}
- return input;
}
///
-@safe pure unittest
+nothrow @safe pure unittest
{
import std.uni : lineSep, paraSep;
assert(stripLeft(" hello world ") ==
"hello world ");
assert(stripLeft("\n\t\v\rhello world\n\t\v\r") ==
"hello world\n\t\v\r");
+ assert(stripLeft(" \u2028hello world") ==
+ "hello world");
assert(stripLeft("hello world") ==
"hello world");
assert(stripLeft([lineSep] ~ "hello world" ~ lineSep) ==
@@ -2874,6 +3175,8 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
import std.utf : byChar;
assert(stripLeft(" hello world "w.byChar).array ==
"hello world ");
+ assert(stripLeft(" \u2022hello world ".byChar).array ==
+ "\u2022hello world ");
}
auto stripLeft(Range)(auto ref Range str)
@@ -2882,19 +3185,77 @@ if (isConvertibleToString!Range)
return stripLeft!(StringTypeOf!Range)(str);
}
-@safe pure unittest
+@nogc nothrow @safe pure unittest
{
assert(testAliasedString!stripLeft(" hello"));
}
+/// Ditto
+auto stripLeft(Range, Char)(Range input, const(Char)[] chars)
+if (((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) ||
+ isConvertibleToString!Range) && isSomeChar!Char)
+{
+ static if (isConvertibleToString!Range)
+ return stripLeft!(StringTypeOf!Range)(input, chars);
+ else
+ {
+ for (; !input.empty; input.popFront)
+ {
+ if (chars.indexOf(input.front) == -1)
+ break;
+ }
+ return input;
+ }
+}
+
+///
+@safe pure unittest
+{
+ assert(stripLeft(" hello world ", " ") ==
+ "hello world ");
+ assert(stripLeft("xxxxxhello world ", "x") ==
+ "hello world ");
+ assert(stripLeft("xxxyy hello world ", "xy ") ==
+ "hello world ");
+}
+
+///
+@safe pure unittest
+{
+ import std.array : array;
+ import std.utf : byChar, byWchar, byDchar;
+
+ assert(stripLeft(" xxxyy hello world "w.byChar, "xy ").array ==
+ "hello world ");
+
+ assert(stripLeft("\u2028\u2020hello world\u2028"w.byWchar,
+ "\u2028").array == "\u2020hello world\u2028");
+ assert(stripLeft("\U00010001hello world"w.byWchar, " ").array ==
+ "\U00010001hello world"w);
+ assert(stripLeft("\U00010001 xyhello world"d.byDchar,
+ "\U00010001 xy").array == "hello world"d);
+
+ assert(stripLeft("\u2020hello"w, "\u2020"w) == "hello"w);
+ assert(stripLeft("\U00010001hello"d, "\U00010001"d) == "hello"d);
+ assert(stripLeft(" hello ", "") == " hello ");
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!stripLeft(" xyz hello", "xyz "));
+}
+
/++
- Strips trailing whitespace (as defined by $(REF isWhite, std,uni)).
+ Strips trailing whitespace (as defined by $(REF isWhite, std,uni)) or
+ as specified in the second argument.
Params:
str = string or random access range of characters
+ chars = string of characters to be stripped
Returns:
- slice of $(D str) stripped of trailing whitespace.
+ slice of `str` stripped of trailing whitespace or characters
+ specified in the second argument.
See_Also:
Generic stripping on ranges: $(REF _stripRight, std, algorithm, mutation)
@@ -2905,63 +3266,60 @@ if (isSomeString!Range ||
!isConvertibleToString!Range &&
isSomeChar!(ElementEncodingType!Range))
{
+ import std.traits : isDynamicArray;
import std.uni : isWhite;
alias C = Unqual!(ElementEncodingType!(typeof(str)));
- static if (isSomeString!(typeof(str)))
+ static if (isSomeString!(typeof(str)) && C.sizeof >= 2)
{
- import std.utf : codeLength;
-
- foreach_reverse (i, dchar c; str)
+ // No whitespace takes multiple wchars to encode and due to
+ // the design of UTF-16 those wchars will not occur as part
+ // of the encoding of multi-wchar codepoints.
+ foreach_reverse (i, C c; str)
{
if (!isWhite(c))
- return str[0 .. i + codeLength!C(c)];
+ return str[0 .. i + 1];
}
-
return str[0 .. 0];
}
else
{
- size_t i = str.length;
- while (i--)
+ // ASCII optimization for dynamic arrays.
+ static if (isDynamicArray!(typeof(str)))
{
- static if (C.sizeof == 4)
+ static import std.ascii;
+ foreach_reverse (i, C c; str)
{
- if (isWhite(str[i]))
- continue;
- break;
- }
- else static if (C.sizeof == 2)
- {
- auto c2 = str[i];
- if (c2 < 0xD800 || c2 >= 0xE000)
+ if (c >= 0x80)
{
- if (isWhite(c2))
- continue;
+ str = str[0 .. i + 1];
+ goto NonAsciiPath;
}
- else if (c2 >= 0xDC00)
+ if (!std.ascii.isWhite(c))
{
- if (i)
- {
- immutable c1 = str[i - 1];
- if (c1 >= 0xD800 && c1 < 0xDC00)
- {
- immutable dchar c = ((c1 - 0xD7C0) << 10) + (c2 - 0xDC00);
- if (isWhite(c))
- {
- --i;
- continue;
- }
- }
- }
+ return str[0 .. i + 1];
}
+ }
+ return str[0 .. 0];
+ }
+
+ NonAsciiPath:
+
+ size_t i = str.length;
+ while (i--)
+ {
+ static if (C.sizeof >= 2)
+ {
+ // No whitespace takes multiple wchars to encode and due to
+ // the design of UTF-16 those wchars will not occur as part
+ // of the encoding of multi-wchar codepoints.
+ if (isWhite(str[i]))
+ continue;
break;
}
else static if (C.sizeof == 1)
{
- import std.utf : byDchar;
-
- char cx = str[i];
+ const cx = str[i];
if (cx <= 0x7F)
{
if (isWhite(cx))
@@ -2970,21 +3328,30 @@ if (isSomeString!Range ||
}
else
{
- size_t stride = 0;
-
- while (1)
+ if (i == 0 || (0b1100_0000 & cx) != 0b1000_0000)
+ break;
+ const uint d = 0b0011_1111 & cx;
+ const c2 = str[i - 1];
+ if ((c2 & 0b1110_0000) == 0b1100_0000) // 2 byte encoding.
{
- ++stride;
- if (!i || (cx & 0xC0) == 0xC0 || stride == 4)
- break;
- cx = str[i - 1];
- if (!(cx & 0x80))
- break;
- --i;
+ if (isWhite(d + (uint(c2 & 0b0001_1111) << 6)))
+ {
+ i--;
+ continue;
+ }
+ break;
}
-
- if (!str[i .. i + stride].byDchar.front.isWhite)
- return str[0 .. i + stride];
+ if (i == 1 || (c2 & 0b1100_0000) != 0b1000_0000)
+ break;
+ const c3 = str[i - 2];
+ // In UTF-8 all whitespace is encoded in 3 bytes or fewer.
+ if ((c3 & 0b1111_0000) == 0b1110_0000 &&
+ isWhite(d + (uint(c2 & 0b0011_1111) << 6) + (uint(c3 & 0b0000_1111) << 12)))
+ {
+ i -= 2;
+ continue;
+ }
+ break;
}
}
else
@@ -2996,7 +3363,7 @@ if (isSomeString!Range ||
}
///
-@safe pure
+nothrow @safe pure
unittest
{
import std.uni : lineSep, paraSep;
@@ -3018,7 +3385,7 @@ if (isConvertibleToString!Range)
return stripRight!(StringTypeOf!Range)(str);
}
-@safe pure unittest
+@nogc nothrow @safe pure unittest
{
assert(testAliasedString!stripRight("hello "));
}
@@ -3034,7 +3401,7 @@ if (isConvertibleToString!Range)
assert(stripRight("\u2028hello world\u2020\u2028".byChar).array == "\u2028hello world\u2020");
assert(stripRight("hello world\U00010001"w.byWchar).array == "hello world\U00010001"w);
- foreach (C; AliasSeq!(char, wchar, dchar))
+ static foreach (C; AliasSeq!(char, wchar, dchar))
{
foreach (s; invalidUTFstrings!C())
{
@@ -3047,16 +3414,73 @@ if (isConvertibleToString!Range)
cast(void) stripRight(ws.byUTF!wchar).array;
}
+/// Ditto
+auto stripRight(Range, Char)(Range str, const(Char)[] chars)
+if (((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range)) ||
+ isConvertibleToString!Range) && isSomeChar!Char)
+{
+ static if (isConvertibleToString!Range)
+ return stripRight!(StringTypeOf!Range)(str, chars);
+ else
+ {
+ for (; !str.empty; str.popBack)
+ {
+ if (chars.indexOf(str.back) == -1)
+ break;
+ }
+ return str;
+ }
+}
+
+///
+@safe pure
+unittest
+{
+ assert(stripRight(" hello world ", "x") ==
+ " hello world ");
+ assert(stripRight(" hello world ", " ") ==
+ " hello world");
+ assert(stripRight(" hello worldxy ", "xy ") ==
+ " hello world");
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!stripRight("hello xyz ", "xyz "));
+}
+
+@safe pure unittest
+{
+ import std.array : array;
+ import std.utf : byChar, byDchar, byUTF, byWchar;
+
+ assert(stripRight(" hello world xyz ".byChar,
+ "xyz ").array == " hello world");
+ assert(stripRight("\u2028hello world\u2020\u2028"w.byWchar,
+ "\u2028").array == "\u2028hello world\u2020");
+ assert(stripRight("hello world\U00010001"w.byWchar,
+ " ").array == "hello world\U00010001"w);
+ assert(stripRight("hello world\U00010001 xy"d.byDchar,
+ "\U00010001 xy").array == "hello world"d);
+ assert(stripRight("hello\u2020"w, "\u2020"w) == "hello"w);
+ assert(stripRight("hello\U00010001"d, "\U00010001"d) == "hello"d);
+ assert(stripRight(" hello ", "") == " hello ");
+}
+
/++
Strips both leading and trailing whitespace (as defined by
- $(REF isWhite, std,uni)).
+ $(REF isWhite, std,uni)) or as specified in the second argument.
Params:
str = string or random access range of characters
+ chars = string of characters to be stripped
+ leftChars = string of leading characters to be stripped
+ rightChars = string of trailing characters to be stripped
Returns:
- slice of $(D str) stripped of leading and trailing whitespace.
+ slice of `str` stripped of leading and trailing whitespace
+ or characters as specified in the second argument.
See_Also:
Generic stripping on ranges: $(REF _strip, std, algorithm, mutation)
@@ -3105,7 +3529,7 @@ if (isConvertibleToString!Range)
assertCTFEable!(
{
- foreach (S; AliasSeq!( char[], const char[], string,
+ static foreach (S; AliasSeq!( char[], const char[], string,
wchar[], const wchar[], wstring,
dchar[], const dchar[], dstring))
{
@@ -3144,15 +3568,134 @@ if (isConvertibleToString!Range)
});
}
+/// Ditto
+auto strip(Range, Char)(Range str, const(Char)[] chars)
+if (((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range)) ||
+ isConvertibleToString!Range) && isSomeChar!Char)
+{
+ static if (isConvertibleToString!Range)
+ return strip!(StringTypeOf!Range)(str, chars);
+ else
+ return stripRight(stripLeft(str, chars), chars);
+}
+
+///
+@safe pure unittest
+{
+ assert(strip(" hello world ", "x") ==
+ " hello world ");
+ assert(strip(" hello world ", " ") ==
+ "hello world");
+ assert(strip(" xyxyhello worldxyxy ", "xy ") ==
+ "hello world");
+ assert(strip("\u2020hello\u2020"w, "\u2020"w) == "hello"w);
+ assert(strip("\U00010001hello\U00010001"d, "\U00010001"d) == "hello"d);
+ assert(strip(" hello ", "") == " hello ");
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!strip(" xyz hello world xyz ", "xyz "));
+}
+
+/// Ditto
+auto strip(Range, Char)(Range str, const(Char)[] leftChars, const(Char)[] rightChars)
+if (((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range)) ||
+ isConvertibleToString!Range) && isSomeChar!Char)
+{
+ static if (isConvertibleToString!Range)
+ return strip!(StringTypeOf!Range)(str, leftChars, rightChars);
+ else
+ return stripRight(stripLeft(str, leftChars), rightChars);
+}
+
+///
+@safe pure unittest
+{
+ assert(strip("xxhelloyy", "x", "y") == "hello");
+ assert(strip(" xyxyhello worldxyxyzz ", "xy ", "xyz ") ==
+ "hello world");
+ assert(strip("\u2020hello\u2028"w, "\u2020"w, "\u2028"w) == "hello"w);
+ assert(strip("\U00010001hello\U00010002"d, "\U00010001"d, "\U00010002"d) ==
+ "hello"d);
+ assert(strip(" hello ", "", "") == " hello ");
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!strip(" xy hello world pq ", "xy ", "pq "));
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ static foreach (S; AliasSeq!( char[], const char[], string,
+ wchar[], const wchar[], wstring,
+ dchar[], const dchar[], dstring))
+ {
+ assert(equal(stripLeft(to!S(" \tfoo\t "), "\t "), "foo\t "));
+ assert(equal(stripLeft(to!S("\u2008 foo\t \u2007"), "\u2008 "),
+ "foo\t \u2007"));
+ assert(equal(stripLeft(to!S("\u0085 μ \u0085 \u00BB \r"), "\u0085 "),
+ "μ \u0085 \u00BB \r"));
+ assert(equal(stripLeft(to!S("1"), " "), "1"));
+ assert(equal(stripLeft(to!S("\U0010FFFE"), " "), "\U0010FFFE"));
+ assert(equal(stripLeft(to!S(""), " "), ""));
+
+ assert(equal(stripRight(to!S(" foo\t "), "\t "), " foo"));
+ assert(equal(stripRight(to!S("\u2008 foo\t \u2007"), "\u2007\t "),
+ "\u2008 foo"));
+ assert(equal(stripRight(to!S("\u0085 μ \u0085 \u00BB \r"), "\r "),
+ "\u0085 μ \u0085 \u00BB"));
+ assert(equal(stripRight(to!S("1"), " "), "1"));
+ assert(equal(stripRight(to!S("\U0010FFFE"), " "), "\U0010FFFE"));
+ assert(equal(stripRight(to!S(""), " "), ""));
+
+ assert(equal(strip(to!S(" foo\t "), "\t "), "foo"));
+ assert(equal(strip(to!S("\u2008 foo\t \u2007"), "\u2008\u2007\t "),
+ "foo"));
+ assert(equal(strip(to!S("\u0085 μ \u0085 \u00BB \r"), "\u0085\r "),
+ "μ \u0085 \u00BB"));
+ assert(equal(strip(to!S("\U0010FFFE"), " "), "\U0010FFFE"));
+ assert(equal(strip(to!S(""), " "), ""));
+
+ assert(equal(strip(to!S(" \nfoo\t "), "\n ", "\t "), "foo"));
+ assert(equal(strip(to!S("\u2008\n foo\t \u2007"),
+ "\u2008\n ", "\u2007\t "), "foo"));
+ assert(equal(strip(to!S("\u0085 μ \u0085 \u00BB μ \u00BB\r"),
+ "\u0085 ", "\u00BB\r "), "μ \u0085 \u00BB μ"));
+ assert(equal(strip(to!S("\U0010FFFE"), " ", " "), "\U0010FFFE"));
+ assert(equal(strip(to!S(""), " ", " "), ""));
+ }
+ });
+}
+
+@safe pure unittest
+{
+ import std.array : sameHead, sameTail;
+ import std.exception : assertCTFEable;
+ assertCTFEable!(
+ {
+ wstring s = " xyz ";
+ assert(s.sameTail(s.stripLeft(" ")));
+ assert(s.sameHead(s.stripRight(" ")));
+ });
+}
+
/++
- If $(D str) ends with $(D delimiter), then $(D str) is returned without
- $(D delimiter) on its end. If it $(D str) does $(I not) end with
- $(D delimiter), then it is returned unchanged.
+ If `str` ends with `delimiter`, then `str` is returned without
+ `delimiter` on its end. If it `str` does $(I not) end with
+ `delimiter`, then it is returned unchanged.
- If no $(D delimiter) is given, then one trailing $(D '\r'), $(D '\n'),
- $(D "\r\n"), $(D '\f'), $(D '\v'), $(REF lineSep, std,uni), $(REF paraSep, std,uni), or $(REF nelSep, std,uni)
- is removed from the end of $(D str). If $(D str) does not end with any of those characters,
+ If no `delimiter` is given, then one trailing `'\r'`, `'\n'`,
+ `"\r\n"`, `'\f'`, `'\v'`, $(REF lineSep, std,uni), $(REF paraSep, std,uni), or $(REF nelSep, std,uni)
+ is removed from the end of `str`. If `str` does not end with any of those characters,
then it is returned unchanged.
Params:
@@ -3229,7 +3772,7 @@ if ((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range) ||
alias C1 = ElementEncodingType!Range;
- static if (is(Unqual!C1 == Unqual!C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
+ static if (is(immutable C1 == immutable C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
{
import std.algorithm.searching : endsWith;
if (str.endsWith(delimiter))
@@ -3306,11 +3849,9 @@ if (isConvertibleToString!Range)
import std.conv : to;
import std.exception : assertCTFEable;
- string s;
-
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
{
// @@@ BUG IN COMPILER, MUST INSERT CAST
assert(chomp(cast(S) null) is null);
@@ -3330,8 +3871,8 @@ if (isConvertibleToString!Range)
assert(chomp(to!S("hello\u2029\u2129")) == "hello\u2029\u2129");
assert(chomp(to!S("hello\u2029\u0185")) == "hello\u2029\u0185");
- foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
// @@@ BUG IN COMPILER, MUST INSERT CAST
assert(chomp(cast(S) null, cast(T) null) is null);
assert(chomp(to!S("hello\n"), cast(T) null) == "hello");
@@ -3342,7 +3883,7 @@ if (isConvertibleToString!Range)
assert(chomp(to!S("hello"), to!T("llo")) == "he");
assert(chomp(to!S("\uFF28ello"), to!T("llo")) == "\uFF28e");
assert(chomp(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co")) == "\uFF28e");
- }();
+ }
}
});
@@ -3361,10 +3902,10 @@ if (isConvertibleToString!Range)
/++
- If $(D str) starts with $(D delimiter), then the part of $(D str) following
- $(D delimiter) is returned. If $(D str) does $(I not) start with
+ If `str` starts with `delimiter`, then the part of `str` following
+ `delimiter` is returned. If `str` does $(I not) start with
- $(D delimiter), then it is returned unchanged.
+ `delimiter`, then it is returned unchanged.
Params:
str = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
@@ -3382,7 +3923,7 @@ if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) ||
{
alias C1 = ElementEncodingType!Range;
- static if (is(Unqual!C1 == Unqual!C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
+ static if (is(immutable C1 == immutable C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
{
import std.algorithm.searching : startsWith;
if (str.startsWith(delimiter))
@@ -3433,16 +3974,16 @@ unittest
import std.exception : assertCTFEable;
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
{
- foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
assert(equal(chompPrefix(to!S("abcdefgh"), to!T("abcde")), "fgh"));
assert(equal(chompPrefix(to!S("abcde"), to!T("abcdefgh")), "abcde"));
assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el\uFF4co")), ""));
assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el")), "\uFF4co"));
assert(equal(chompPrefix(to!S("\uFF28el"), to!T("\uFF28el\uFF4co")), "\uFF28el"));
- }();
+ }
}
});
@@ -3467,9 +4008,9 @@ unittest
}
/++
- Returns $(D str) without its last character, if there is one. If $(D str)
- ends with $(D "\r\n"), then both are removed. If $(D str) is empty, then
- then it is returned unchanged.
+ Returns `str` without its last character, if there is one. If `str`
+ ends with `"\r\n"`, then both are removed. If `str` is empty, then
+ it is returned unchanged.
Params:
str = string (must be valid UTF)
@@ -3599,7 +4140,7 @@ if (isConvertibleToString!Range)
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
{
assert(chop(cast(S) null) is null);
assert(equal(chop(to!S("hello")), "hell"));
@@ -3614,14 +4155,14 @@ if (isConvertibleToString!Range)
/++
- Left justify $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Left justify `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
s = string
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
GC allocated string
@@ -3645,14 +4186,14 @@ if (isSomeString!S)
}
/++
- Left justify $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Left justify `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
r = string or range of characters
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
a lazy range of the left justified result
@@ -3756,14 +4297,14 @@ if (isConvertibleToString!Range)
}
/++
- Right justify $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Right justify `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
s = string
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
GC allocated string
@@ -3787,15 +4328,15 @@ if (isSomeString!S)
}
/++
- Right justify $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Right justify `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
of characters
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
a lazy range of the right justified result
@@ -3836,7 +4377,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
{
// Replace _width with nfill
// (use alias instead of union because CTFE cannot deal with unions)
- assert(_width);
+ assert(_width, "width of 0 not allowed");
static if (hasLength!Range)
{
immutable len = _input.length;
@@ -3900,7 +4441,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return Result(r, width, fillChar);
}
else
- static assert(0);
+ static assert(0, "Invalid character type of " ~ C.stringof);
}
///
@@ -3947,9 +4488,9 @@ if (isConvertibleToString!Range)
}
/++
- Center $(D s) in a field $(D width) characters wide. $(D fillChar)
+ Center `s` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D s) doesn't fill.
+ `s` doesn't fill.
Params:
s = The string to center
@@ -3984,8 +4525,8 @@ unittest
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {{
S s = to!S("hello");
assert(leftJustify(s, 2) == "hello");
@@ -4007,20 +4548,20 @@ unittest
assert(leftJustify(s, 8, 'ö') == "helloööö");
assert(rightJustify(s, 8, 'ö') == "öööhello");
assert(center(s, 8, 'ö') == "öhelloöö");
- }
+ }}
});
}
/++
- Center justify $(D r) in a field $(D width) characters wide. $(D fillChar)
+ Center justify `r` in a field `width` characters wide. `fillChar`
is the character that will be used to fill up the space in the field that
- $(D r) doesn't fill.
+ `r` doesn't fill.
Params:
r = string or $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
of characters
width = minimum field width
- fillChar = used to pad end up to $(D width) characters
+ fillChar = used to pad end up to `width` characters
Returns:
a lazy range of the center justified result
@@ -4084,12 +4625,13 @@ if (isConvertibleToString!Range)
assert(testAliasedString!centerJustifier("hello", 8));
}
-@system unittest
+@safe unittest
{
static auto byFwdRange(dstring s)
{
static struct FRange
{
+ @safe:
dstring str;
this(dstring s) { str = s; }
@property bool empty() { return str.length == 0; }
@@ -4124,7 +4666,7 @@ if (isConvertibleToString!Range)
/++
- Replace each tab character in $(D s) with the number of spaces necessary
+ Replace each tab character in `s` with the number of spaces necessary
to align the following character at the next tab stop.
Params:
@@ -4143,7 +4685,7 @@ if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range))
}
///
-@system pure unittest
+@safe pure unittest
{
assert(detab(" \n\tx", 9) == " \n x");
}
@@ -4174,7 +4716,7 @@ if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range))
}
/++
- Replace each tab character in $(D r) with the number of spaces
+ Replace each tab character in `r` with the number of spaces
necessary to align the following character at the next tab stop.
Params:
@@ -4296,7 +4838,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
}
///
-@system pure unittest
+@safe pure unittest
{
import std.array : array;
@@ -4314,7 +4856,7 @@ if (isConvertibleToString!Range)
assert(testAliasedString!detabber( " ab\t asdf ", 8));
}
-@system pure unittest
+@safe pure unittest
{
import std.algorithm.comparison : cmp;
import std.conv : to;
@@ -4322,8 +4864,8 @@ if (isConvertibleToString!Range)
assertCTFEable!(
{
- foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {{
S s = to!S("This \tis\t a fofof\tof list");
assert(cmp(detab(s), "This is a fofof of list") == 0);
@@ -4340,12 +4882,12 @@ if (isConvertibleToString!Range)
assert(detab("\u0085\t", 9) == "\u0085 ");
assert(detab("\u2028\t", 9) == "\u2028 ");
assert(detab(" \u2029\t", 9) == " \u2029 ");
- }
+ }}
});
}
///
-@system pure unittest
+@safe pure unittest
{
import std.array : array;
import std.utf : byChar, byWchar;
@@ -4361,12 +4903,12 @@ if (isConvertibleToString!Range)
}
/++
- Replaces spaces in $(D s) with the optimal number of tabs.
+ Replaces spaces in `s` with the optimal number of tabs.
All spaces and tabs at the end of a line are removed.
Params:
s = String to convert.
- tabSize = Tab columns are $(D tabSize) spaces apart.
+ tabSize = Tab columns are `tabSize` spaces apart.
Returns:
GC allocated string with spaces replaced with tabs;
@@ -4401,7 +4943,7 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) &&
}
/++
- Replaces spaces in range $(D r) with the optimal number of tabs.
+ Replaces spaces in range `r` with the optimal number of tabs.
All spaces and tabs at the end of a line are removed.
Params:
@@ -4420,7 +4962,7 @@ if (isForwardRange!Range && !isConvertibleToString!Range)
import std.uni : lineSep, paraSep, nelSep;
import std.utf : codeUnitLimit, decodeFront;
- assert(tabSize > 0);
+ assert(tabSize > 0, "tabSize must be greater than 0");
alias C = Unqual!(ElementEncodingType!Range);
static struct Result
@@ -4508,7 +5050,8 @@ if (isForwardRange!Range && !isConvertibleToString!Range)
{
while (1)
{
- assert(_input.length);
+ assert(_input.length, "input did not contain non "
+ ~ "whitespace character");
cx = _input[0];
if (cx == ' ')
++column;
@@ -4523,7 +5066,8 @@ if (isForwardRange!Range && !isConvertibleToString!Range)
{
while (1)
{
- assert(!_input.empty);
+ assert(_input.length, "input did not contain non "
+ ~ "whitespace character");
cx = _input.front;
if (cx == ' ')
++column;
@@ -4716,16 +5260,17 @@ unittest
/++
- Replaces the characters in $(D str) which are keys in $(D transTable) with
- their corresponding values in $(D transTable). $(D transTable) is an AA
- where its keys are $(D dchar) and its values are either $(D dchar) or some
- type of string. Also, if $(D toRemove) is given, the characters in it are
- removed from $(D str) prior to translation. $(D str) itself is unaltered.
+ Replaces the characters in `str` which are keys in `transTable` with
+ their corresponding values in `transTable`. `transTable` is an AA
+ where its keys are `dchar` and its values are either `dchar` or some
+ type of string. Also, if `toRemove` is given, the characters in it are
+ removed from `str` prior to translation. `str` itself is unaltered.
A copy with the changes is returned.
See_Also:
- $(LREF tr)
- $(REF replace, std,array)
+ $(LREF tr),
+ $(REF replace, std,array),
+ $(REF substitute, std,algorithm,iteration)
Params:
str = The original string.
@@ -4756,7 +5301,8 @@ if (isSomeChar!C1 && isSomeChar!C2)
assert(translate("hello world", transTable2) == "h5llorange worangerld");
}
-@safe pure unittest // issue 13018
+// https://issues.dlang.org/show_bug.cgi?id=13018
+@safe pure unittest
{
immutable dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
assert(translate("hello world", transTable1) == "h5ll7 w7rld");
@@ -4774,10 +5320,11 @@ if (isSomeChar!C1 && isSomeChar!C2)
assertCTFEable!(
{
- foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
+ static foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[]))
- {
+ {(){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
assert(translate(to!S("hello world"), cast(dchar[dchar])['h' : 'q', 'l' : '5']) ==
to!S("qe55o wor5d"));
assert(translate(to!S("hello world"), cast(dchar[dchar])['o' : 'l', 'l' : '\U00010143']) ==
@@ -4788,13 +5335,14 @@ if (isSomeChar!C1 && isSomeChar!C2)
to!S("hell0 o w0rld"));
assert(translate(to!S("hello world"), cast(dchar[dchar]) null) == to!S("hello world"));
- foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[],
+ static foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[]))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
- foreach (R; AliasSeq!(dchar[dchar], const dchar[dchar],
+ (){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
+ static foreach (R; AliasSeq!(dchar[dchar], const dchar[dchar],
immutable dchar[dchar]))
- {
+ {{
R tt = ['h' : 'q', 'l' : '5'];
assert(translate(to!S("hello world"), tt, to!T("r"))
== to!S("qe55o wo5d"));
@@ -4802,13 +5350,14 @@ if (isSomeChar!C1 && isSomeChar!C2)
== to!S(" wrd"));
assert(translate(to!S("hello world"), tt, to!T("q5"))
== to!S("qe55o wor5d"));
- }
+ }}
}();
auto s = to!S("hello world");
dchar[dchar] transTable = ['h' : 'q', 'l' : '5'];
static assert(is(typeof(s) == typeof(translate(s, transTable))));
- }
+ assert(translate(s, transTable) == "qe55o wor5d");
+ }();}
});
}
@@ -4831,10 +5380,11 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2)
assertCTFEable!(
{
- foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
+ static foreach (S; AliasSeq!( char[], const( char)[], immutable( char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[]))
- {
+ {(){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
assert(translate(to!S("hello world"), ['h' : "yellow", 'l' : "42"]) ==
to!S("yellowe4242o wor42d"));
assert(translate(to!S("hello world"), ['o' : "owl", 'l' : "\U00010143\U00010143"]) ==
@@ -4849,14 +5399,14 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2)
to!S("hello world"));
assert(translate(to!S("hello world"), cast(string[dchar]) null) == to!S("hello world"));
- foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[],
+ static foreach (T; AliasSeq!( char[], const( char)[], immutable( char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[]))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
-
- foreach (R; AliasSeq!(string[dchar], const string[dchar],
+ (){ // workaround slow optimizations for large functions
+ // https://issues.dlang.org/show_bug.cgi?id=2396
+ static foreach (R; AliasSeq!(string[dchar], const string[dchar],
immutable string[dchar]))
- {
+ {{
R tt = ['h' : "yellow", 'l' : "42"];
assert(translate(to!S("hello world"), tt, to!T("r")) ==
to!S("yellowe4242o wo42d"));
@@ -4868,18 +5418,19 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2)
to!S(""));
assert(translate(to!S("hello world"), tt, to!T("42")) ==
to!S("yellowe4242o wor42d"));
- }
+ }}
}();
auto s = to!S("hello world");
string[dchar] transTable = ['h' : "silly", 'l' : "putty"];
static assert(is(typeof(s) == typeof(translate(s, transTable))));
- }
+ assert(translate(s, transTable) == "sillyeputtyputtyo worputtyd");
+ }();}
});
}
/++
- This is an overload of $(D translate) which takes an existing buffer to write the contents to.
+ This is an overload of `translate` which takes an existing buffer to write the contents to.
Params:
str = The original string.
@@ -4888,7 +5439,7 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2)
toRemove = The characters to remove from the string.
buffer = An output range to write the contents to.
+/
-void translate(C1, C2 = immutable char, Buffer)(C1[] str,
+void translate(C1, C2 = immutable char, Buffer)(const(C1)[] str,
in dchar[dchar] transTable,
const(C2)[] toRemove,
Buffer buffer)
@@ -4916,7 +5467,8 @@ if (isSomeChar!C1 && isSomeChar!C2 && isOutputRange!(Buffer, C1))
assert(buffer.data == "h5llorange worangerld");
}
-@safe pure unittest // issue 13018
+// https://issues.dlang.org/show_bug.cgi?id=13018
+@safe pure unittest
{
import std.array : appender;
immutable dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
@@ -4944,8 +5496,8 @@ if (isSomeChar!C1 && isSomeString!S && isSomeChar!C2 && isOutputRange!(Buffer, S
translateImpl(str, transTable, toRemove, buffer);
}
-private void translateImpl(C1, T, C2, Buffer)(C1[] str,
- T transTable,
+private void translateImpl(C1, T, C2, Buffer)(const(C1)[] str,
+ scope T transTable,
const(C2)[] toRemove,
Buffer buffer)
{
@@ -4974,26 +5526,27 @@ private void translateImpl(C1, T, C2, Buffer)(C1[] str,
cases where Unicode processing is not necessary.
Unlike the other overloads of $(LREF _translate), this one does not take
- an AA. Rather, it takes a $(D string) generated by $(LREF makeTransTable).
+ an AA. Rather, it takes a `string` generated by $(LREF makeTransTable).
- The array generated by $(D makeTransTable) is $(D 256) elements long such that
+ The array generated by `makeTransTable` is `256` elements long such that
the index is equal to the ASCII character being replaced and the value is
equal to the character that it's being replaced with. Note that translate
does not decode any of the characters, so you can actually pass it Extended
- ASCII characters if you want to (ASCII only actually uses $(D 128)
+ ASCII characters if you want to (ASCII only actually uses `128`
characters), but be warned that Extended ASCII characters are not valid
- Unicode and therefore will result in a $(D UTFException) being thrown from
+ Unicode and therefore will result in a `UTFException` being thrown from
most other Phobos functions.
Also, because no decoding occurs, it is possible to use this overload to
translate ASCII characters within a proper UTF-8 string without altering the
other, non-ASCII characters. It's replacing any code unit greater than
- $(D 127) with another code unit or replacing any code unit with another code
- unit greater than $(D 127) which will cause UTF validation issues.
+ `127` with another code unit or replacing any code unit with another code
+ unit greater than `127` which will cause UTF validation issues.
See_Also:
- $(LREF tr)
- $(REF replace, std,array)
+ $(LREF tr),
+ $(REF replace, std,array),
+ $(REF substitute, std,algorithm,iteration)
Params:
str = The original string.
@@ -5001,13 +5554,16 @@ private void translateImpl(C1, T, C2, Buffer)(C1[] str,
to replace them with. It is generated by $(LREF makeTransTable).
toRemove = The characters to remove from the string.
+/
-C[] translate(C = immutable char)(in char[] str, in char[] transTable, in char[] toRemove = null) @trusted pure nothrow
-if (is(Unqual!C == char))
+C[] translate(C = immutable char)(scope const(char)[] str, scope const(char)[] transTable,
+ scope const(char)[] toRemove = null) @trusted pure nothrow
+if (is(immutable C == immutable char))
in
{
- assert(transTable.length == 256);
+ import std.conv : to;
+ assert(transTable.length == 256, "transTable had invalid length of " ~
+ to!string(transTable.length));
}
-body
+do
{
bool[256] remTable = false;
@@ -5033,6 +5589,14 @@ body
return cast(C[])(buffer);
}
+///
+@safe pure nothrow unittest
+{
+ auto transTable1 = makeTrans("eo5", "57q");
+ assert(translate("hello world", transTable1) == "h5ll7 w7rld");
+
+ assert(translate("hello world", transTable1, "low") == "h5 rd");
+}
/**
* Do same thing as $(LREF makeTransTable) but allocate the translation table
@@ -5040,7 +5604,7 @@ body
*
* Use $(LREF makeTransTable) instead.
*/
-string makeTrans(in char[] from, in char[] to) @trusted pure nothrow
+string makeTrans(scope const(char)[] from, scope const(char)[] to) @trusted pure nothrow
{
return makeTransTable(from, to)[].idup;
}
@@ -5064,19 +5628,20 @@ string makeTrans(in char[] from, in char[] to) @trusted pure nothrow
* Returns:
* translation array
*/
-
-char[256] makeTransTable(in char[] from, in char[] to) @safe pure nothrow @nogc
+char[256] makeTransTable(scope const(char)[] from, scope const(char)[] to) @safe pure nothrow @nogc
in
{
import std.ascii : isASCII;
- assert(from.length == to.length);
- assert(from.length <= 256);
+ assert(from.length == to.length, "from.length must match to.length");
+ assert(from.length <= 256, "from.length must be <= 256");
foreach (char c; from)
- assert(isASCII(c));
+ assert(isASCII(c),
+ "all characters in from must be valid ascii character");
foreach (char c; to)
- assert(isASCII(c));
+ assert(isASCII(c),
+ "all characters in to must be valid ascii character");
}
-body
+do
{
char[256] result = void;
@@ -5087,6 +5652,13 @@ body
return result;
}
+///
+@safe pure unittest
+{
+ assert(translate("hello world", makeTransTable("hl", "q5")) == "qe55o wor5d");
+ assert(translate("hello world", makeTransTable("12345", "67890")) == "hello world");
+}
+
@safe pure unittest
{
import std.conv : to;
@@ -5094,16 +5666,17 @@ body
assertCTFEable!(
{
- foreach (C; AliasSeq!(char, const char, immutable char))
- {
+ static foreach (C; AliasSeq!(char, const char, immutable char))
+ {{
assert(translate!C("hello world", makeTransTable("hl", "q5")) == to!(C[])("qe55o wor5d"));
auto s = to!(C[])("hello world");
auto transTable = makeTransTable("hl", "q5");
static assert(is(typeof(s) == typeof(translate!C(s, transTable))));
- }
+ assert(translate(s, transTable) == "qe55o wor5d");
+ }}
- foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[]))
+ static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[]))
{
assert(translate(to!S("hello world"), makeTransTable("hl", "q5")) == to!S("qe55o wor5d"));
assert(translate(to!S("hello \U00010143 world"), makeTransTable("hl", "q5")) ==
@@ -5114,8 +5687,8 @@ body
assert(translate(to!S("hello \U00010143 world"), makeTransTable("12345", "67890")) ==
to!S("hello \U00010143 world"));
- foreach (T; AliasSeq!(char[], const(char)[], immutable(char)[]))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(char[], const(char)[], immutable(char)[]))
+ {
assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("r")) ==
to!S("qe55o wo5d"));
assert(translate(to!S("hello \U00010143 world"), makeTransTable("hl", "q5"), to!T("r")) ==
@@ -5124,13 +5697,13 @@ body
to!S(" wrd"));
assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("q5")) ==
to!S("qe55o wor5d"));
- }();
+ }
}
});
}
/++
- This is an $(I $(RED ASCII-only)) overload of $(D translate) which takes an existing buffer to write the contents to.
+ This is an $(I $(RED ASCII-only)) overload of `translate` which takes an existing buffer to write the contents to.
Params:
str = The original string.
@@ -5139,14 +5712,15 @@ body
toRemove = The characters to remove from the string.
buffer = An output range to write the contents to.
+/
-void translate(C = immutable char, Buffer)(in char[] str, in char[] transTable,
- in char[] toRemove, Buffer buffer) @trusted pure
-if (is(Unqual!C == char) && isOutputRange!(Buffer, char))
+void translate(C = immutable char, Buffer)(scope const(char)[] str, scope const(char)[] transTable,
+ scope const(char)[] toRemove, Buffer buffer) @trusted pure
+if (is(immutable C == immutable char) && isOutputRange!(Buffer, char))
in
{
- assert(transTable.length == 256);
+ assert(transTable.length == 256, format!
+ "transTable.length %s must equal 256"(transTable.length));
}
-body
+do
{
bool[256] remTable = false;
@@ -5255,10 +5829,10 @@ if (isSomeString!S)
/++
- Replaces the characters in $(D str) which are in $(D from) with the
- the corresponding characters in $(D to) and returns the resulting string.
+ Replaces the characters in `str` which are in `from` with the
+ the corresponding characters in `to` and returns the resulting string.
- $(D tr) is based on
+ `tr` is based on
$(HTTP pubs.opengroup.org/onlinepubs/9699919799/utilities/_tr.html, Posix's tr),
though it doesn't do everything that the Posix utility does.
@@ -5271,27 +5845,32 @@ if (isSomeString!S)
Modifiers:
$(BOOKTABLE,
$(TR $(TD Modifier) $(TD Description))
- $(TR $(TD $(D 'c')) $(TD Complement the list of characters in $(D from)))
- $(TR $(TD $(D 'd')) $(TD Removes matching characters with no corresponding
- replacement in $(D to)))
- $(TR $(TD $(D 's')) $(TD Removes adjacent duplicates in the replaced
+ $(TR $(TD `'c'`) $(TD Complement the list of characters in `from`))
+ $(TR $(TD `'d'`) $(TD Removes matching characters with no corresponding
+ replacement in `to`))
+ $(TR $(TD `'s'`) $(TD Removes adjacent duplicates in the replaced
characters))
)
- If the modifier $(D 'd') is present, then the number of characters in
- $(D to) may be only $(D 0) or $(D 1).
+ If the modifier `'d'` is present, then the number of characters in
+ `to` may be only `0` or `1`.
- If the modifier $(D 'd') is $(I not) present, and $(D to) is empty, then
- $(D to) is taken to be the same as $(D from).
+ If the modifier `'d'` is $(I not) present, and `to` is empty, then
+ `to` is taken to be the same as `from`.
- If the modifier $(D 'd') is $(I not) present, and $(D to) is shorter than
- $(D from), then $(D to) is extended by replicating the last character in
- $(D to).
+ If the modifier `'d'` is $(I not) present, and `to` is shorter than
+ `from`, then `to` is extended by replicating the last character in
+ `to`.
- Both $(D from) and $(D to) may contain ranges using the $(D '-') character
- (e.g. $(D "a-d") is synonymous with $(D "abcd").) Neither accept a leading
- $(D '^') as meaning the complement of the string (use the $(D 'c') modifier
+ Both `from` and `to` may contain ranges using the `'-'` character
+ (e.g. `"a-d"` is synonymous with `"abcd"`.) Neither accept a leading
+ `'^'` as meaning the complement of the string (use the `'c'` modifier
for that).
+
+ See_Also:
+ $(LREF translate),
+ $(REF replace, std,array),
+ $(REF substitute, std,algorithm,iteration)
+/
C1[] tr(C1, C2, C3, C4 = immutable char)
(C1[] str, const(C2)[] from, const(C3)[] to, const(C4)[] modifiers = null)
@@ -5311,7 +5890,8 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
case 'c': mod_c = 1; break; // complement
case 'd': mod_d = 1; break; // delete unreplaced chars
case 's': mod_s = 1; break; // squeeze duplicated replaced chars
- default: assert(0);
+ default: assert(false, "modifier must be one of ['c', 'd', 's'] not "
+ ~ c);
}
}
@@ -5394,7 +5974,7 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
if (mod_s && modified && newc == lastc)
continue;
result.put(newc);
- assert(newc != dchar.init);
+ assert(newc != dchar.init, "character must not be dchar.init");
modified = true;
lastc = newc;
continue;
@@ -5408,6 +5988,15 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
return result.data;
}
+///
+@safe pure unittest
+{
+ assert(tr("abcdef", "cd", "CD") == "abCDef");
+ assert(tr("1st March, 2018", "March", "MAR", "s") == "1st MAR, 2018");
+ assert(tr("abcdef", "ef", "", "d") == "abcd");
+ assert(tr("14-Jul-87", "a-zA-Z", " ", "cs") == " Jul ");
+}
+
@safe pure unittest
{
import std.algorithm.comparison : equal;
@@ -5447,6 +6036,7 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
auto s = to!S("hello world");
static assert(is(typeof(s) == typeof(tr(s, "he", "if"))));
+ assert(tr(s, "he", "if") == "ifllo world");
}
});
}
@@ -5459,11 +6049,11 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
}
/**
- * Takes a string $(D s) and determines if it represents a number. This function
- * also takes an optional parameter, $(D bAllowSep), which will accept the
- * separator characters $(D ',') and $(D '__') within the string. But these
+ * Takes a string `s` and determines if it represents a number. This function
+ * also takes an optional parameter, `bAllowSep`, which will accept the
+ * separator characters `','` and `'__'` within the string. But these
* characters should be stripped from the string before using any
- * of the conversion functions like $(D to!int()), $(D to!float()), and etc
+ * of the conversion functions like `to!int()`, `to!float()`, and etc
* else an error will occur.
*
* Also please note, that no spaces are allowed within the string
@@ -5476,7 +6066,7 @@ C1[] tr(C1, C2, C3, C4 = immutable char)
* bAllowSep = accept separator characters or not
*
* Returns:
- * $(D bool)
+ * `bool`
*/
bool isNumeric(S)(S s, bool bAllowSep = false)
if (isSomeString!S ||
@@ -5692,7 +6282,7 @@ if (isSomeString!S ||
{
import std.conv : to;
- foreach (T; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
+ static foreach (T; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
{
assert("123".to!T.isNumeric());
assert("123UL".to!T.isNumeric());
@@ -5769,7 +6359,6 @@ if (isSomeString!S ||
{
assert(isNumeric(to!string(real.nan)) == true);
assert(isNumeric(to!string(-real.infinity)) == true);
- assert(isNumeric(to!string(123e+2+1234.78Li)) == true);
}
string s = "$250.99-";
@@ -5803,10 +6392,8 @@ if (isSomeString!S ||
* $(LUCKY The Soundex Indexing System)
* $(LREF soundex)
*
- * Bugs:
+ * Note:
* Only works well with English names.
- * There are other arguably better Soundex algorithms,
- * but this one is the standard one.
*/
char[4] soundexer(Range)(Range str)
if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
@@ -5864,12 +6451,25 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
return result;
}
+/// ditto
char[4] soundexer(Range)(auto ref Range str)
if (isConvertibleToString!Range)
{
return soundexer!(StringTypeOf!Range)(str);
}
+///
+@safe unittest
+{
+ assert(soundexer("Gauss") == "G200");
+ assert(soundexer("Ghosh") == "G200");
+
+ assert(soundexer("Robert") == "R163");
+ assert(soundexer("Rupert") == "R163");
+
+ assert(soundexer("0123^&^^**&^") == ['\0', '\0', '\0', '\0']);
+}
+
/*****************************
* Like $(LREF soundexer), but with different parameters
* and return value.
@@ -5885,7 +6485,7 @@ if (isConvertibleToString!Range)
* See_Also:
* $(LREF soundexer)
*/
-char[] soundex(const(char)[] str, char[] buffer = null)
+char[] soundex(scope const(char)[] str, return scope char[] buffer = null)
@safe pure nothrow
in
{
@@ -5895,13 +6495,15 @@ out (result)
{
if (result !is null)
{
- assert(result.length == 4);
- assert(result[0] >= 'A' && result[0] <= 'Z');
+ assert(result.length == 4, "Result must have length of 4");
+ assert(result[0] >= 'A' && result[0] <= 'Z', "The first character of "
+ ~ " the result must be an upper character not " ~ result);
foreach (char c; result[1 .. 4])
- assert(c >= '0' && c <= '6');
+ assert(c >= '0' && c <= '6', "the last three character of the"
+ ~ " result must be number between 0 and 6 not " ~ result);
}
}
-body
+do
{
char[4] result = soundexer(str);
if (result[0] == 0)
@@ -5912,6 +6514,17 @@ body
return buffer;
}
+///
+@safe unittest
+{
+ assert(soundex("Gauss") == "G200");
+ assert(soundex("Ghosh") == "G200");
+
+ assert(soundex("Robert") == "R163");
+ assert(soundex("Rupert") == "R163");
+
+ assert(soundex("0123^&^^**&^") == null);
+}
@safe pure nothrow unittest
{
@@ -5981,7 +6594,6 @@ body
* auto-complete the string once sufficient characters have been
* entered that uniquely identify it.
*/
-
string[string] abbrev(string[] values) @safe pure
{
import std.algorithm.sorting : sort;
@@ -6095,11 +6707,11 @@ string[string] abbrev(string[] values) @safe pure
*/
size_t column(Range)(Range str, in size_t tabsize = 8)
-if ((isInputRange!Range && isSomeChar!(Unqual!(ElementEncodingType!Range)) ||
+if ((isInputRange!Range && isSomeChar!(ElementEncodingType!Range) ||
isNarrowString!Range) &&
!isConvertibleToString!Range)
{
- static if (is(Unqual!(ElementEncodingType!Range) == char))
+ static if (is(immutable ElementEncodingType!Range == immutable char))
{
// decoding needed for chars
import std.utf : byDchar;
@@ -6378,7 +6990,7 @@ void main() {
* StringException if indentation is done with different sequences
* of whitespace characters.
*/
-S[] outdent(S)(S[] lines) @safe pure
+S[] outdent(S)(return scope S[] lines) @safe pure
if (isSomeString!S)
{
import std.algorithm.searching : startsWith;
@@ -6438,6 +7050,32 @@ if (isSomeString!S)
return lines;
}
+///
+@safe pure unittest
+{
+ auto str1 = [
+ " void main()\n",
+ " {\n",
+ " test();\n",
+ " }\n"
+ ];
+ auto str1Expected = [
+ "void main()\n",
+ "{\n",
+ " test();\n",
+ "}\n"
+ ];
+ assert(str1.outdent == str1Expected);
+
+ auto str2 = [
+ "void main()\n",
+ " {\n",
+ " test();\n",
+ " }\n"
+ ];
+ assert(str2.outdent == str2);
+}
+
@safe pure unittest
{
import std.conv : to;
@@ -6470,8 +7108,8 @@ if (isSomeString!S)
assertCTFEable!(
{
- foreach (S; AliasSeq!(string, wstring, dstring))
- {
+ static foreach (S; AliasSeq!(string, wstring, dstring))
+ {{
enum S blank = "";
assert(blank.outdent() == blank);
static assert(blank.outdent() == blank);
@@ -6527,7 +7165,7 @@ if (isSomeString!S)
enum expected7 = "a \nb ";
assert(testStr7.outdent() == expected7);
static assert(testStr7.outdent() == expected7);
- }
+ }}
});
}
@@ -6538,11 +7176,11 @@ if (isSomeString!S)
assertThrown!StringException(bad.outdent);
}
-/** Assume the given array of integers $(D arr) is a well-formed UTF string and
+/** Assume the given array of integers `arr` is a well-formed UTF string and
return it typed as a UTF string.
-$(D ubyte) becomes $(D char), $(D ushort) becomes $(D wchar) and $(D uint)
-becomes $(D dchar). Type qualifiers are preserved.
+`ubyte` becomes `char`, `ushort` becomes `wchar` and `uint`
+becomes `dchar`. Type qualifiers are preserved.
When compiled with debug mode, this function performs an extra check to make
sure the return value is a valid Unicode string.
@@ -6553,16 +7191,27 @@ Params:
Returns:
arr retyped as an array of chars, wchars, or dchars
+Throws:
+ In debug mode `AssertError`, when the result is not a well-formed UTF string.
+
See_Also: $(LREF representation)
*/
-auto assumeUTF(T)(T[] arr) pure
-if (staticIndexOf!(Unqual!T, ubyte, ushort, uint) != -1)
+auto assumeUTF(T)(T[] arr)
+if (staticIndexOf!(immutable T, immutable ubyte, immutable ushort, immutable uint) != -1)
{
import std.traits : ModifyTypePreservingTQ;
+ import std.exception : collectException;
import std.utf : validate;
+
alias ToUTFType(U) = AliasSeq!(char, wchar, dchar)[U.sizeof / 2];
- auto asUTF = cast(ModifyTypePreservingTQ!(ToUTFType, T)[])arr;
- debug validate(asUTF);
+ auto asUTF = cast(ModifyTypePreservingTQ!(ToUTFType, T)[]) arr;
+
+ debug
+ {
+ scope ex = collectException(validate(asUTF));
+ assert(!ex, ex.msg);
+ }
+
return asUTF;
}
@@ -6573,14 +7222,14 @@ if (staticIndexOf!(Unqual!T, ubyte, ushort, uint) != -1)
immutable(ubyte)[] b = a.representation;
string c = b.assumeUTF;
- assert(a == c);
+ assert(c == "Hölo World");
}
pure @system unittest
{
import std.algorithm.comparison : equal;
- foreach (T; AliasSeq!(char[], wchar[], dchar[]))
- {
+ static foreach (T; AliasSeq!(char[], wchar[], dchar[]))
+ {{
immutable T jti = "Hello World";
T jt = jti.dup;
@@ -6609,5 +7258,18 @@ pure @system unittest
assert(equal(jt, ht));
assert(equal(jt, htc));
assert(equal(jt, hti));
- }
+ }}
+}
+
+pure @system unittest
+{
+ import core.exception : AssertError;
+ import std.exception : assertThrown, assertNotThrown;
+
+ immutable(ubyte)[] a = [ 0xC0 ];
+
+ debug
+ assertThrown!AssertError( () nothrow @nogc @safe {cast(void) a.assumeUTF;} () );
+ else
+ assertNotThrown!AssertError( () nothrow @nogc @safe {cast(void) a.assumeUTF;} () );
}
diff --git a/libphobos/src/std/sumtype.d b/libphobos/src/std/sumtype.d
new file mode 100644
index 00000000000..8242f1e3ee5
--- /dev/null
+++ b/libphobos/src/std/sumtype.d
@@ -0,0 +1,2500 @@
+/++
+[SumType] is a generic discriminated union implementation that uses
+design-by-introspection to generate safe and efficient code. Its features
+include:
+
+* [Pattern matching.][match]
+* Support for self-referential types.
+* Full attribute correctness (`pure`, `@safe`, `@nogc`, and `nothrow` are
+ inferred whenever possible).
+* A type-safe and memory-safe API compatible with DIP 1000 (`scope`).
+* No dependency on runtime type information (`TypeInfo`).
+* Compatibility with BetterC.
+
+License: Boost License 1.0
+Authors: Paul Backus
++/
+module std.sumtype;
+
+/// $(DIVID basic-usage,$(H3 Basic usage))
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.math.operations : isClose;
+
+ struct Fahrenheit { double degrees; }
+ struct Celsius { double degrees; }
+ struct Kelvin { double degrees; }
+
+ alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin);
+
+ // Construct from any of the member types.
+ Temperature t1 = Fahrenheit(98.6);
+ Temperature t2 = Celsius(100);
+ Temperature t3 = Kelvin(273);
+
+ // Use pattern matching to access the value.
+ Fahrenheit toFahrenheit(Temperature t)
+ {
+ return Fahrenheit(
+ t.match!(
+ (Fahrenheit f) => f.degrees,
+ (Celsius c) => c.degrees * 9.0/5 + 32,
+ (Kelvin k) => k.degrees * 9.0/5 - 459.4
+ )
+ );
+ }
+
+ assert(toFahrenheit(t1).degrees.isClose(98.6));
+ assert(toFahrenheit(t2).degrees.isClose(212));
+ assert(toFahrenheit(t3).degrees.isClose(32));
+
+ // Use ref to modify the value in place.
+ void freeze(ref Temperature t)
+ {
+ t.match!(
+ (ref Fahrenheit f) => f.degrees = 32,
+ (ref Celsius c) => c.degrees = 0,
+ (ref Kelvin k) => k.degrees = 273
+ );
+ }
+
+ freeze(t1);
+ assert(toFahrenheit(t1).degrees.isClose(32));
+
+ // Use a catch-all handler to give a default result.
+ bool isFahrenheit(Temperature t)
+ {
+ return t.match!(
+ (Fahrenheit f) => true,
+ _ => false
+ );
+ }
+
+ assert(isFahrenheit(t1));
+ assert(!isFahrenheit(t2));
+ assert(!isFahrenheit(t3));
+}
+
+/** $(DIVID introspection-based-matching, $(H3 Introspection-based matching))
+ *
+ * In the `length` and `horiz` functions below, the handlers for `match` do not
+ * specify the types of their arguments. Instead, matching is done based on how
+ * the argument is used in the body of the handler: any type with `x` and `y`
+ * properties will be matched by the `rect` handlers, and any type with `r` and
+ * `theta` properties will be matched by the `polar` handlers.
+ */
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.math.operations : isClose;
+ import std.math.trigonometry : cos;
+ import std.math.constants : PI;
+ import std.math.algebraic : sqrt;
+
+ struct Rectangular { double x, y; }
+ struct Polar { double r, theta; }
+ alias Vector = SumType!(Rectangular, Polar);
+
+ double length(Vector v)
+ {
+ return v.match!(
+ rect => sqrt(rect.x^^2 + rect.y^^2),
+ polar => polar.r
+ );
+ }
+
+ double horiz(Vector v)
+ {
+ return v.match!(
+ rect => rect.x,
+ polar => polar.r * cos(polar.theta)
+ );
+ }
+
+ Vector u = Rectangular(1, 1);
+ Vector v = Polar(1, PI/4);
+
+ assert(length(u).isClose(sqrt(2.0)));
+ assert(length(v).isClose(1));
+ assert(horiz(u).isClose(1));
+ assert(horiz(v).isClose(sqrt(0.5)));
+}
+
+/** $(DIVID arithmetic-expression-evaluator, $(H3 Arithmetic expression evaluator))
+ *
+ * This example makes use of the special placeholder type `This` to define a
+ * [recursive data type](https://en.wikipedia.org/wiki/Recursive_data_type): an
+ * [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) for
+ * representing simple arithmetic expressions.
+ */
+version (D_BetterC) {} else
+@system unittest
+{
+ import std.functional : partial;
+ import std.traits : EnumMembers;
+ import std.typecons : Tuple;
+
+ enum Op : string
+ {
+ Plus = "+",
+ Minus = "-",
+ Times = "*",
+ Div = "/"
+ }
+
+ // An expression is either
+ // - a number,
+ // - a variable, or
+ // - a binary operation combining two sub-expressions.
+ alias Expr = SumType!(
+ double,
+ string,
+ Tuple!(Op, "op", This*, "lhs", This*, "rhs")
+ );
+
+ // Shorthand for Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs"),
+ // the Tuple type above with Expr substituted for This.
+ alias BinOp = Expr.Types[2];
+
+ // Factory function for number expressions
+ Expr* num(double value)
+ {
+ return new Expr(value);
+ }
+
+ // Factory function for variable expressions
+ Expr* var(string name)
+ {
+ return new Expr(name);
+ }
+
+ // Factory function for binary operation expressions
+ Expr* binOp(Op op, Expr* lhs, Expr* rhs)
+ {
+ return new Expr(BinOp(op, lhs, rhs));
+ }
+
+ // Convenience wrappers for creating BinOp expressions
+ alias sum = partial!(binOp, Op.Plus);
+ alias diff = partial!(binOp, Op.Minus);
+ alias prod = partial!(binOp, Op.Times);
+ alias quot = partial!(binOp, Op.Div);
+
+ // Evaluate expr, looking up variables in env
+ double eval(Expr expr, double[string] env)
+ {
+ return expr.match!(
+ (double num) => num,
+ (string var) => env[var],
+ (BinOp bop)
+ {
+ double lhs = eval(*bop.lhs, env);
+ double rhs = eval(*bop.rhs, env);
+ final switch (bop.op)
+ {
+ static foreach (op; EnumMembers!Op)
+ {
+ case op:
+ return mixin("lhs" ~ op ~ "rhs");
+ }
+ }
+ }
+ );
+ }
+
+ // Return a "pretty-printed" representation of expr
+ string pprint(Expr expr)
+ {
+ import std.format : format;
+
+ return expr.match!(
+ (double num) => "%g".format(num),
+ (string var) => var,
+ (BinOp bop) => "(%s %s %s)".format(
+ pprint(*bop.lhs),
+ cast(string) bop.op,
+ pprint(*bop.rhs)
+ )
+ );
+ }
+
+ Expr* myExpr = sum(var("a"), prod(num(2), var("b")));
+ double[string] myEnv = ["a":3, "b":4, "c":7];
+
+ assert(eval(*myExpr, myEnv) == 11);
+ assert(pprint(*myExpr) == "(a + (2 * b))");
+}
+
+import std.format.spec : FormatSpec, singleSpec;
+import std.meta : AliasSeq, Filter, IndexOf = staticIndexOf, Map = staticMap;
+import std.meta : NoDuplicates;
+import std.meta : anySatisfy, allSatisfy;
+import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor;
+import std.traits : isAssignable, isCopyable, isStaticArray, isRvalueAssignable;
+import std.traits : ConstOf, ImmutableOf, InoutOf, TemplateArgsOf;
+import std.traits : CommonType;
+import std.typecons : ReplaceTypeUnless;
+import std.typecons : Flag;
+
+/// Placeholder used to refer to the enclosing [SumType].
+struct This {}
+
+// Converts an unsigned integer to a compile-time string constant.
+private enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length];
+
+// Check that .stringof does what we expect, since it's not guaranteed by the
+// language spec.
+@safe unittest
+{
+ assert(toCtString!0 == "0");
+ assert(toCtString!123456 == "123456");
+}
+
+// True if a variable of type T can appear on the lhs of an assignment
+private enum isAssignableTo(T) =
+ isAssignable!T || (!isCopyable!T && isRvalueAssignable!T);
+
+// toHash is required by the language spec to be nothrow and @safe
+private enum isHashable(T) = __traits(compiles,
+ () nothrow @safe { hashOf(T.init); }
+);
+
+private enum hasPostblit(T) = __traits(hasPostblit, T);
+
+/**
+ * A [tagged union](https://en.wikipedia.org/wiki/Tagged_union) that can hold a
+ * single value from any of a specified set of types.
+ *
+ * The value in a `SumType` can be operated on using [pattern matching][match].
+ *
+ * To avoid ambiguity, duplicate types are not allowed (but see the
+ * ["basic usage" example](#basic-usage) for a workaround).
+ *
+ * The special type `This` can be used as a placeholder to create
+ * self-referential types, just like with `Algebraic`. See the
+ * ["Arithmetic expression evaluator" example](#arithmetic-expression-evaluator) for
+ * usage.
+ *
+ * A `SumType` is initialized by default to hold the `.init` value of its
+ * first member type, just like a regular union. The version identifier
+ * `SumTypeNoDefaultCtor` can be used to disable this behavior.
+ *
+ * See_Also: $(REF Algebraic, std,variant)
+ */
+struct SumType(Types...)
+if (is(NoDuplicates!Types == Types) && Types.length > 0)
+{
+ /// The types a `SumType` can hold.
+ alias Types = AliasSeq!(
+ ReplaceTypeUnless!(isSumTypeInstance, This, typeof(this), TemplateArgsOf!SumType)
+ );
+
+private:
+
+ enum bool canHoldTag(T) = Types.length <= T.max;
+ alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong);
+
+ alias Tag = Filter!(canHoldTag, unsignedInts)[0];
+
+ union Storage
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=20068
+ template memberName(T)
+ if (IndexOf!(T, Types) >= 0)
+ {
+ enum tid = IndexOf!(T, Types);
+ mixin("enum memberName = `values_", toCtString!tid, "`;");
+ }
+
+ static foreach (T; Types)
+ {
+ mixin("T ", memberName!T, ";");
+ }
+ }
+
+ Storage storage;
+ Tag tag;
+
+ /* Accesses the value stored in a SumType.
+ *
+ * This method is memory-safe, provided that:
+ *
+ * 1. A SumType's tag is always accurate.
+ * 2. A SumType cannot be assigned to in @safe code if that assignment
+ * could cause unsafe aliasing.
+ *
+ * All code that accesses a SumType's tag or storage directly, including
+ * @safe code in this module, must be manually checked to ensure that it
+ * does not violate either of the above requirements.
+ */
+ @trusted
+ ref inout(T) get(T)() inout
+ if (IndexOf!(T, Types) >= 0)
+ {
+ enum tid = IndexOf!(T, Types);
+ assert(tag == tid,
+ "This `" ~ SumType.stringof ~
+ "` does not contain a(n) `" ~ T.stringof ~ "`"
+ );
+ return __traits(getMember, storage, Storage.memberName!T);
+ }
+
+public:
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21399
+ version (StdDdoc)
+ {
+ // Dummy type to stand in for loop variable
+ private struct T;
+
+ /// Constructs a `SumType` holding a specific value.
+ this(T value);
+
+ /// ditto
+ this(const(T) value) const;
+
+ /// ditto
+ this(immutable(T) value) immutable;
+ }
+
+ static foreach (tid, T; Types)
+ {
+ /// Constructs a `SumType` holding a specific value.
+ this(T value)
+ {
+ import core.lifetime : forward;
+
+ static if (isCopyable!T)
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
+ __traits(getMember, storage, Storage.memberName!T) = __ctfe ? value : forward!value;
+ }
+ else
+ {
+ __traits(getMember, storage, Storage.memberName!T) = forward!value;
+ }
+
+ tag = tid;
+ }
+
+ static if (isCopyable!(const(T)))
+ {
+ static if (IndexOf!(const(T), Map!(ConstOf, Types)) == tid)
+ {
+ /// ditto
+ this(const(T) value) const
+ {
+ __traits(getMember, storage, Storage.memberName!T) = value;
+ tag = tid;
+ }
+ }
+ }
+ else
+ {
+ @disable this(const(T) value) const;
+ }
+
+ static if (isCopyable!(immutable(T)))
+ {
+ static if (IndexOf!(immutable(T), Map!(ImmutableOf, Types)) == tid)
+ {
+ /// ditto
+ this(immutable(T) value) immutable
+ {
+ __traits(getMember, storage, Storage.memberName!T) = value;
+ tag = tid;
+ }
+ }
+ }
+ else
+ {
+ @disable this(immutable(T) value) immutable;
+ }
+ }
+
+ static if (anySatisfy!(hasElaborateCopyConstructor, Types))
+ {
+ static if
+ (
+ allSatisfy!(isCopyable, Map!(InoutOf, Types))
+ && !anySatisfy!(hasPostblit, Map!(InoutOf, Types))
+ )
+ {
+ /// Constructs a `SumType` that's a copy of another `SumType`.
+ this(ref inout(SumType) other) inout
+ {
+ storage = other.match!((ref value) {
+ alias OtherTypes = Map!(InoutOf, Types);
+ enum tid = IndexOf!(typeof(value), OtherTypes);
+ alias T = Types[tid];
+
+ mixin("inout(Storage) newStorage = { ",
+ Storage.memberName!T, ": value",
+ " };");
+
+ return newStorage;
+ });
+
+ tag = other.tag;
+ }
+ }
+ else
+ {
+ static if (allSatisfy!(isCopyable, Types))
+ {
+ /// ditto
+ this(ref SumType other)
+ {
+ storage = other.match!((ref value) {
+ alias T = typeof(value);
+
+ mixin("Storage newStorage = { ",
+ Storage.memberName!T, ": value",
+ " };");
+
+ return newStorage;
+ });
+
+ tag = other.tag;
+ }
+ }
+ else
+ {
+ @disable this(ref SumType other);
+ }
+
+ static if (allSatisfy!(isCopyable, Map!(ConstOf, Types)))
+ {
+ /// ditto
+ this(ref const(SumType) other) const
+ {
+ storage = other.match!((ref value) {
+ alias OtherTypes = Map!(ConstOf, Types);
+ enum tid = IndexOf!(typeof(value), OtherTypes);
+ alias T = Types[tid];
+
+ mixin("const(Storage) newStorage = { ",
+ Storage.memberName!T, ": value",
+ " };");
+
+ return newStorage;
+ });
+
+ tag = other.tag;
+ }
+ }
+ else
+ {
+ @disable this(ref const(SumType) other) const;
+ }
+
+ static if (allSatisfy!(isCopyable, Map!(ImmutableOf, Types)))
+ {
+ /// ditto
+ this(ref immutable(SumType) other) immutable
+ {
+ storage = other.match!((ref value) {
+ alias OtherTypes = Map!(ImmutableOf, Types);
+ enum tid = IndexOf!(typeof(value), OtherTypes);
+ alias T = Types[tid];
+
+ mixin("immutable(Storage) newStorage = { ",
+ Storage.memberName!T, ": value",
+ " };");
+
+ return newStorage;
+ });
+
+ tag = other.tag;
+ }
+ }
+ else
+ {
+ @disable this(ref immutable(SumType) other) immutable;
+ }
+ }
+ }
+
+ version (SumTypeNoDefaultCtor)
+ {
+ @disable this();
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21399
+ version (StdDdoc)
+ {
+ // Dummy type to stand in for loop variable
+ private struct T;
+
+ /**
+ * Assigns a value to a `SumType`.
+ *
+ * Assigning to a `SumType` is `@system` if any of the
+ * `SumType`'s members contain pointers or references, since
+ * those members may be reachable through external references,
+ * and overwriting them could therefore lead to memory
+ * corruption.
+ *
+ * An individual assignment can be `@trusted` if the caller can
+ * guarantee that there are no outstanding references to $(I any)
+ * of the `SumType`'s members when the assignment occurs.
+ */
+ ref SumType opAssign(T rhs);
+ }
+
+ static foreach (tid, T; Types)
+ {
+ static if (isAssignableTo!T)
+ {
+ /**
+ * Assigns a value to a `SumType`.
+ *
+ * Assigning to a `SumType` is `@system` if any of the `SumType`'s
+ * $(I other) members contain pointers or references, since those
+ * members may be reachable through external references, and
+ * overwriting them could therefore lead to memory corruption.
+ *
+ * An individual assignment can be `@trusted` if the caller can
+ * guarantee that, when the assignment occurs, there are no
+ * outstanding references to any such members.
+ */
+ ref SumType opAssign(T rhs)
+ {
+ import core.lifetime : forward;
+ import std.traits : hasIndirections, hasNested;
+ import std.meta : AliasSeq, Or = templateOr;
+
+ alias OtherTypes =
+ AliasSeq!(Types[0 .. tid], Types[tid + 1 .. $]);
+ enum unsafeToOverwrite =
+ anySatisfy!(Or!(hasIndirections, hasNested), OtherTypes);
+
+ static if (unsafeToOverwrite)
+ {
+ cast(void) () @system {}();
+ }
+
+ this.match!destroyIfOwner;
+
+ mixin("Storage newStorage = { ",
+ Storage.memberName!T, ": forward!rhs",
+ " };");
+
+ storage = newStorage;
+ tag = tid;
+
+ return this;
+ }
+ }
+ }
+
+ static if (allSatisfy!(isAssignableTo, Types))
+ {
+ static if (allSatisfy!(isCopyable, Types))
+ {
+ /**
+ * Copies the value from another `SumType` into this one.
+ *
+ * See the value-assignment overload for details on `@safe`ty.
+ *
+ * Copy assignment is `@disable`d if any of `Types` is non-copyable.
+ */
+ ref SumType opAssign(ref SumType rhs)
+ {
+ rhs.match!((ref value) { this = value; });
+ return this;
+ }
+ }
+ else
+ {
+ @disable ref SumType opAssign(ref SumType rhs);
+ }
+
+ /**
+ * Moves the value from another `SumType` into this one.
+ *
+ * See the value-assignment overload for details on `@safe`ty.
+ */
+ ref SumType opAssign(SumType rhs)
+ {
+ import core.lifetime : move;
+
+ rhs.match!((ref value) { this = move(value); });
+ return this;
+ }
+ }
+
+ /**
+ * Compares two `SumType`s for equality.
+ *
+ * Two `SumType`s are equal if they are the same kind of `SumType`, they
+ * contain values of the same type, and those values are equal.
+ */
+ bool opEquals(this This, Rhs)(auto ref Rhs rhs)
+ if (!is(CommonType!(This, Rhs) == void))
+ {
+ static if (is(This == Rhs))
+ {
+ return AliasSeq!(this, rhs).match!((ref value, ref rhsValue) {
+ static if (is(typeof(value) == typeof(rhsValue)))
+ {
+ return value == rhsValue;
+ }
+ else
+ {
+ return false;
+ }
+ });
+ }
+ else
+ {
+ alias CommonSumType = CommonType!(This, Rhs);
+ return cast(CommonSumType) this == cast(CommonSumType) rhs;
+ }
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=19407
+ static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types)))
+ {
+ // If possible, include the destructor only when it's needed
+ private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types);
+ }
+ else
+ {
+ // If we can't tell, always include it, even when it does nothing
+ private enum includeDtor = true;
+ }
+
+ static if (includeDtor)
+ {
+ /// Calls the destructor of the `SumType`'s current value.
+ ~this()
+ {
+ this.match!destroyIfOwner;
+ }
+ }
+
+ invariant
+ {
+ this.match!((ref value) {
+ static if (is(typeof(value) == class))
+ {
+ if (value !is null)
+ {
+ assert(value);
+ }
+ }
+ else static if (is(typeof(value) == struct))
+ {
+ assert(&value);
+ }
+ });
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21400
+ version (StdDdoc)
+ {
+ /**
+ * Returns a string representation of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+ string toString(this This)();
+
+ /**
+ * Handles formatted writing of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ *
+ * Params:
+ * sink = Output range to write to.
+ * fmt = Format specifier to use.
+ *
+ * See_Also: $(REF formatValue, std,format)
+ */
+ void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt);
+ }
+
+ version (D_BetterC) {} else
+ /**
+ * Returns a string representation of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+ string toString(this This)()
+ {
+ import std.conv : to;
+
+ return this.match!(to!string);
+ }
+
+ version (D_BetterC) {} else
+ /**
+ * Handles formatted writing of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ *
+ * Params:
+ * sink = Output range to write to.
+ * fmt = Format specifier to use.
+ *
+ * See_Also: $(REF formatValue, std,format)
+ */
+ void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt)
+ {
+ import std.format.write : formatValue;
+
+ this.match!((ref value) {
+ formatValue(sink, value, fmt);
+ });
+ }
+
+ static if (allSatisfy!(isHashable, Map!(ConstOf, Types)))
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21400
+ version (StdDdoc)
+ {
+ /**
+ * Returns the hash of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+ size_t toHash() const;
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=20095
+ version (D_BetterC) {} else
+ /**
+ * Returns the hash of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+ size_t toHash() const
+ {
+ return this.match!hashOf;
+ }
+ }
+}
+
+// Construction
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+}
+
+// Assignment
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ x = 3.14;
+}
+
+// Self assignment
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+ y = x;
+}
+
+// Equality
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ assert(MySum(123) == MySum(123));
+ assert(MySum(123) != MySum(456));
+ assert(MySum(123) != MySum(123.0));
+ assert(MySum(123) != MySum(456.0));
+
+}
+
+// Equality of differently-qualified SumTypes
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias SumA = SumType!(int, float);
+ alias SumB = SumType!(const(int[]), int[]);
+ alias SumC = SumType!(int[], const(int[]));
+
+ int[] ma = [1, 2, 3];
+ const(int[]) ca = [1, 2, 3];
+
+ assert(const(SumA)(123) == SumA(123));
+ assert(const(SumB)(ma[]) == SumB(ca[]));
+ assert(const(SumC)(ma[]) == SumC(ca[]));
+}
+
+// Imported types
+@safe unittest
+{
+ import std.typecons : Tuple;
+
+ alias MySum = SumType!(Tuple!(int, int));
+}
+
+// const and immutable types
+@safe unittest
+{
+ alias MySum = SumType!(const(int[]), immutable(float[]));
+}
+
+// Recursive types
+@safe unittest
+{
+ alias MySum = SumType!(This*);
+ assert(is(MySum.Types[0] == MySum*));
+}
+
+// Allowed types
+@safe unittest
+{
+ import std.meta : AliasSeq;
+
+ alias MySum = SumType!(int, float, This*);
+
+ assert(is(MySum.Types == AliasSeq!(int, float, MySum*)));
+}
+
+// Types with destructors and postblits
+@system unittest
+{
+ int copies;
+
+ static struct Test
+ {
+ bool initialized = false;
+ int* copiesPtr;
+
+ this(this) { (*copiesPtr)++; }
+ ~this() { if (initialized) (*copiesPtr)--; }
+ }
+
+ alias MySum = SumType!(int, Test);
+
+ Test t = Test(true, &copies);
+
+ {
+ MySum x = t;
+ assert(copies == 1);
+ }
+ assert(copies == 0);
+
+ {
+ MySum x = 456;
+ assert(copies == 0);
+ }
+ assert(copies == 0);
+
+ {
+ MySum x = t;
+ assert(copies == 1);
+ x = 456;
+ assert(copies == 0);
+ }
+
+ {
+ MySum x = 456;
+ assert(copies == 0);
+ x = t;
+ assert(copies == 1);
+ }
+
+ {
+ MySum x = t;
+ MySum y = x;
+ assert(copies == 2);
+ }
+
+ {
+ MySum x = t;
+ MySum y;
+ y = x;
+ assert(copies == 2);
+ }
+}
+
+// Doesn't destroy reference types
+// Disabled in BetterC due to use of classes
+version (D_BetterC) {} else
+@system unittest
+{
+ bool destroyed;
+
+ class C
+ {
+ ~this()
+ {
+ destroyed = true;
+ }
+ }
+
+ struct S
+ {
+ ~this() {}
+ }
+
+ alias MySum = SumType!(S, C);
+
+ C c = new C();
+ {
+ MySum x = c;
+ destroyed = false;
+ }
+ assert(!destroyed);
+
+ {
+ MySum x = c;
+ destroyed = false;
+ x = S();
+ assert(!destroyed);
+ }
+}
+
+// Types with @disable this()
+@safe unittest
+{
+ static struct NoInit
+ {
+ @disable this();
+ }
+
+ alias MySum = SumType!(NoInit, int);
+
+ assert(!__traits(compiles, MySum()));
+ auto _ = MySum(42);
+}
+
+// const SumTypes
+version (D_BetterC) {} else // not @nogc, https://issues.dlang.org/show_bug.cgi?id=22117
+@safe unittest
+{
+ auto _ = const(SumType!(int[]))([1, 2, 3]);
+}
+
+// Equality of const SumTypes
+@safe unittest
+{
+ alias MySum = SumType!int;
+
+ auto _ = const(MySum)(123) == const(MySum)(456);
+}
+
+// Compares reference types using value equality
+@safe unittest
+{
+ import std.array : staticArray;
+
+ static struct Field {}
+ static struct Struct { Field[] fields; }
+ alias MySum = SumType!Struct;
+
+ static arr1 = staticArray([Field()]);
+ static arr2 = staticArray([Field()]);
+
+ auto a = MySum(Struct(arr1[]));
+ auto b = MySum(Struct(arr2[]));
+
+ assert(a == b);
+}
+
+// toString
+// Disabled in BetterC due to use of std.conv.text
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.conv : text;
+
+ static struct Int { int i; }
+ static struct Double { double d; }
+ alias Sum = SumType!(Int, Double);
+
+ assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text);
+ assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text);
+ assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text);
+}
+
+// string formatting
+// Disabled in BetterC due to use of std.format.format
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.format : format;
+
+ SumType!int x = 123;
+
+ assert(format!"%s"(x) == format!"%s"(123));
+ assert(format!"%x"(x) == format!"%x"(123));
+}
+
+// string formatting of qualified SumTypes
+// Disabled in BetterC due to use of std.format.format and dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.format : format;
+
+ int[] a = [1, 2, 3];
+ const(SumType!(int[])) x = a;
+
+ assert(format!"%(%d, %)"(x) == format!"%(%s, %)"(a));
+}
+
+// Github issue #16
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias Node = SumType!(This[], string);
+
+ // override inference of @system attribute for cyclic functions
+ assert((() @trusted =>
+ Node([Node([Node("x")])])
+ ==
+ Node([Node([Node("x")])])
+ )());
+}
+
+// Github issue #16 with const
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias Node = SumType!(const(This)[], string);
+
+ // override inference of @system attribute for cyclic functions
+ assert((() @trusted =>
+ Node([Node([Node("x")])])
+ ==
+ Node([Node([Node("x")])])
+ )());
+}
+
+// Stale pointers
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@system unittest
+{
+ alias MySum = SumType!(ubyte, void*[2]);
+
+ MySum x = [null, cast(void*) 0x12345678];
+ void** p = &x.get!(void*[2])[1];
+ x = ubyte(123);
+
+ assert(*p != cast(void*) 0x12345678);
+}
+
+// Exception-safe assignment
+// Disabled in BetterC due to use of exceptions
+version (D_BetterC) {} else
+@safe unittest
+{
+ static struct A
+ {
+ int value = 123;
+ }
+
+ static struct B
+ {
+ int value = 456;
+ this(this) { throw new Exception("oops"); }
+ }
+
+ alias MySum = SumType!(A, B);
+
+ MySum x;
+ try
+ {
+ x = B();
+ }
+ catch (Exception e) {}
+
+ assert(
+ (x.tag == 0 && x.get!A.value == 123) ||
+ (x.tag == 1 && x.get!B.value == 456)
+ );
+}
+
+// Types with @disable this(this)
+@safe unittest
+{
+ import core.lifetime : move;
+
+ static struct NoCopy
+ {
+ @disable this(this);
+ }
+
+ alias MySum = SumType!NoCopy;
+
+ NoCopy lval = NoCopy();
+
+ MySum x = NoCopy();
+ MySum y = NoCopy();
+
+
+ assert(!__traits(compiles, SumType!NoCopy(lval)));
+
+ y = NoCopy();
+ y = move(x);
+ assert(!__traits(compiles, y = lval));
+ assert(!__traits(compiles, y = x));
+
+ bool b = x == y;
+}
+
+// Github issue #22
+// Disabled in BetterC due to use of std.typecons.Nullable
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.typecons;
+
+ static struct A
+ {
+ SumType!(Nullable!int) a = Nullable!int.init;
+ }
+}
+
+// Static arrays of structs with postblits
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ static struct S
+ {
+ int n;
+ this(this) { n++; }
+ }
+
+ SumType!(S[1]) x = [S(0)];
+ SumType!(S[1]) y = x;
+
+ auto xval = x.get!(S[1])[0].n;
+ auto yval = y.get!(S[1])[0].n;
+
+ assert(xval != yval);
+}
+
+// Replacement does not happen inside SumType
+// Disabled in BetterC due to use of associative arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.typecons : Tuple, ReplaceTypeUnless;
+ alias A = Tuple!(This*,SumType!(This*))[SumType!(This*,string)[This]];
+ alias TR = ReplaceTypeUnless!(isSumTypeInstance, This, int, A);
+ static assert(is(TR == Tuple!(int*,SumType!(This*))[SumType!(This*, string)[int]]));
+}
+
+// Supports nested self-referential SumTypes
+@safe unittest
+{
+ import std.typecons : Tuple, Flag;
+ alias Nat = SumType!(Flag!"0", Tuple!(This*));
+ alias Inner = SumType!Nat;
+ alias Outer = SumType!(Nat*, Tuple!(This*, This*));
+}
+
+// Self-referential SumTypes inside Algebraic
+// Disabled in BetterC due to use of std.variant.Algebraic
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.variant : Algebraic;
+
+ alias T = Algebraic!(SumType!(This*));
+
+ assert(is(T.AllowedTypes[0].Types[0] == T.AllowedTypes[0]*));
+}
+
+// Doesn't call @system postblits in @safe code
+@safe unittest
+{
+ static struct SystemCopy { @system this(this) {} }
+ SystemCopy original;
+
+ assert(!__traits(compiles, () @safe
+ {
+ SumType!SystemCopy copy = original;
+ }));
+
+ assert(!__traits(compiles, () @safe
+ {
+ SumType!SystemCopy copy; copy = original;
+ }));
+}
+
+// Doesn't overwrite pointers in @safe code
+@safe unittest
+{
+ alias MySum = SumType!(int*, int);
+
+ MySum x;
+
+ assert(!__traits(compiles, () @safe
+ {
+ x = 123;
+ }));
+
+ assert(!__traits(compiles, () @safe
+ {
+ x = MySum(123);
+ }));
+}
+
+// Types with invariants
+// Disabled in BetterC due to use of exceptions
+version (D_BetterC) {} else
+@system unittest
+{
+ import std.exception : assertThrown;
+ import core.exception : AssertError;
+
+ struct S
+ {
+ int i;
+ invariant { assert(i >= 0); }
+ }
+
+ class C
+ {
+ int i;
+ invariant { assert(i >= 0); }
+ }
+
+ // Only run test if contract checking is enabled
+ try
+ {
+ S probe = S(-1);
+ assert(&probe);
+ }
+ catch (AssertError _)
+ {
+ SumType!S x;
+ x.match!((ref v) { v.i = -1; });
+ assertThrown!AssertError(assert(&x));
+
+ SumType!C y = new C();
+ y.match!((ref v) { v.i = -1; });
+ assertThrown!AssertError(assert(&y));
+ }
+}
+
+// Calls value postblit on self-assignment
+@safe unittest
+{
+ static struct S
+ {
+ int n;
+ this(this) { n++; }
+ }
+
+ SumType!S x = S();
+ SumType!S y;
+ y = x;
+
+ auto xval = x.get!S.n;
+ auto yval = y.get!S.n;
+
+ assert(xval != yval);
+}
+
+// Github issue #29
+@safe unittest
+{
+ alias A = SumType!string;
+
+ @safe A createA(string arg)
+ {
+ return A(arg);
+ }
+
+ @safe void test()
+ {
+ A a = createA("");
+ }
+}
+
+// SumTypes as associative array keys
+// Disabled in BetterC due to use of associative arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ int[SumType!(int, string)] aa;
+}
+
+// toString with non-copyable types
+// Disabled in BetterC due to use of std.conv.to (in toString)
+version (D_BetterC) {} else
+@safe unittest
+{
+ struct NoCopy
+ {
+ @disable this(this);
+ }
+
+ SumType!NoCopy x;
+
+ auto _ = x.toString();
+}
+
+// Can use the result of assignment
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum a = MySum(123);
+ MySum b = MySum(3.14);
+
+ assert((a = b) == b);
+ assert((a = MySum(123)) == MySum(123));
+ assert((a = 3.14) == MySum(3.14));
+ assert(((a = b) = MySum(123)) == MySum(123));
+}
+
+// Types with copy constructors
+@safe unittest
+{
+ static struct S
+ {
+ int n;
+
+ this(ref return scope inout S other) inout
+ {
+ n = other.n + 1;
+ }
+ }
+
+ SumType!S x = S();
+ SumType!S y = x;
+
+ auto xval = x.get!S.n;
+ auto yval = y.get!S.n;
+
+ assert(xval != yval);
+}
+
+// Copyable by generated copy constructors
+@safe unittest
+{
+ static struct Inner
+ {
+ ref this(ref inout Inner other) {}
+ }
+
+ static struct Outer
+ {
+ SumType!Inner inner;
+ }
+
+ Outer x;
+ Outer y = x;
+}
+
+// Types with qualified copy constructors
+@safe unittest
+{
+ static struct ConstCopy
+ {
+ int n;
+ this(inout int n) inout { this.n = n; }
+ this(ref const typeof(this) other) const { this.n = other.n; }
+ }
+
+ static struct ImmutableCopy
+ {
+ int n;
+ this(inout int n) inout { this.n = n; }
+ this(ref immutable typeof(this) other) immutable { this.n = other.n; }
+ }
+
+ const SumType!ConstCopy x = const(ConstCopy)(1);
+ immutable SumType!ImmutableCopy y = immutable(ImmutableCopy)(1);
+}
+
+// Types with disabled opEquals
+@safe unittest
+{
+ static struct S
+ {
+ @disable bool opEquals(const S rhs) const;
+ }
+
+ auto _ = SumType!S(S());
+}
+
+// Types with non-const opEquals
+@safe unittest
+{
+ static struct S
+ {
+ int i;
+ bool opEquals(S rhs) { return i == rhs.i; }
+ }
+
+ auto _ = SumType!S(S(123));
+}
+
+// Incomparability of different SumTypes
+@safe unittest
+{
+ SumType!(int, string) x = 123;
+ SumType!(string, int) y = 123;
+
+ assert(!__traits(compiles, x != y));
+}
+
+// Self-reference in return/parameter type of function pointer member
+// Disabled in BetterC due to use of delegates
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias T = SumType!(int, This delegate(This));
+}
+
+// Construction and assignment from implicitly-convertible lvalue
+@safe unittest
+{
+ alias MySum = SumType!bool;
+
+ const(bool) b = true;
+
+ MySum x = b;
+ MySum y; y = b;
+}
+
+// @safe assignment to the only pointer type in a SumType
+@safe unittest
+{
+ SumType!(string, int) sm = 123;
+ sm = "this should be @safe";
+}
+
+// Pointers to local variables
+// https://issues.dlang.org/show_bug.cgi?id=22117
+@safe unittest
+{
+ int n = 123;
+ immutable int ni = 456;
+
+ SumType!(int*) s = &n;
+ const SumType!(int*) sc = &n;
+ immutable SumType!(int*) si = &ni;
+}
+
+/// True if `T` is an instance of the `SumType` template, otherwise false.
+private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...);
+
+@safe unittest
+{
+ static struct Wrapper
+ {
+ SumType!int s;
+ alias s this;
+ }
+
+ assert(isSumTypeInstance!(SumType!int));
+ assert(!isSumTypeInstance!Wrapper);
+}
+
+/// True if `T` is a [SumType] or implicitly converts to one, otherwise false.
+enum bool isSumType(T) = is(T : SumType!Args, Args...);
+
+///
+@safe unittest
+{
+ static struct ConvertsToSumType
+ {
+ SumType!int payload;
+ alias payload this;
+ }
+
+ static struct ContainsSumType
+ {
+ SumType!int payload;
+ }
+
+ assert(isSumType!(SumType!int));
+ assert(isSumType!ConvertsToSumType);
+ assert(!isSumType!ContainsSumType);
+}
+
+/**
+ * Calls a type-appropriate function with the value held in a [SumType].
+ *
+ * For each possible type the [SumType] can hold, the given handlers are
+ * checked, in order, to see whether they accept a single argument of that type.
+ * The first one that does is chosen as the match for that type. (Note that the
+ * first match may not always be the most exact match.
+ * See ["Avoiding unintentional matches"](#avoiding-unintentional-matches) for
+ * one common pitfall.)
+ *
+ * Every type must have a matching handler, and every handler must match at
+ * least one type. This is enforced at compile time.
+ *
+ * Handlers may be functions, delegates, or objects with `opCall` overloads. If
+ * a function with more than one overload is given as a handler, all of the
+ * overloads are considered as potential matches.
+ *
+ * Templated handlers are also accepted, and will match any type for which they
+ * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See
+ * ["Introspection-based matching"](#introspection-based-matching) for an
+ * example of templated handler usage.
+ *
+ * If multiple [SumType]s are passed to match, their values are passed to the
+ * handlers as separate arguments, and matching is done for each possible
+ * combination of value types. See ["Multiple dispatch"](#multiple-dispatch) for
+ * an example.
+ *
+ * Returns:
+ * The value returned from the handler that matches the currently-held type.
+ *
+ * See_Also: $(REF visit, std,variant)
+ */
+template match(handlers...)
+{
+ import std.typecons : Yes;
+
+ /**
+ * The actual `match` function.
+ *
+ * Params:
+ * args = One or more [SumType] objects.
+ */
+ auto ref match(SumTypes...)(auto ref SumTypes args)
+ if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
+ {
+ return matchImpl!(Yes.exhaustive, handlers)(args);
+ }
+}
+
+/** $(DIVID avoiding-unintentional-matches, $(H3 Avoiding unintentional matches))
+ *
+ * Sometimes, implicit conversions may cause a handler to match more types than
+ * intended. The example below shows two solutions to this problem.
+ */
+@safe unittest
+{
+ alias Number = SumType!(double, int);
+
+ Number x;
+
+ // Problem: because int implicitly converts to double, the double
+ // handler is used for both types, and the int handler never matches.
+ assert(!__traits(compiles,
+ x.match!(
+ (double d) => "got double",
+ (int n) => "got int"
+ )
+ ));
+
+ // Solution 1: put the handler for the "more specialized" type (in this
+ // case, int) before the handler for the type it converts to.
+ assert(__traits(compiles,
+ x.match!(
+ (int n) => "got int",
+ (double d) => "got double"
+ )
+ ));
+
+ // Solution 2: use a template that only accepts the exact type it's
+ // supposed to match, instead of any type that implicitly converts to it.
+ alias exactly(T, alias fun) = function (arg)
+ {
+ static assert(is(typeof(arg) == T));
+ return fun(arg);
+ };
+
+ // Now, even if we put the double handler first, it will only be used for
+ // doubles, not ints.
+ assert(__traits(compiles,
+ x.match!(
+ exactly!(double, d => "got double"),
+ exactly!(int, n => "got int")
+ )
+ ));
+}
+
+/** $(DIVID multiple-dispatch, $(H3 Multiple dispatch))
+ *
+ * Pattern matching can be performed on multiple `SumType`s at once by passing
+ * handlers with multiple arguments. This usually leads to more concise code
+ * than using nested calls to `match`, as show below.
+ */
+@safe unittest
+{
+ struct Point2D { double x, y; }
+ struct Point3D { double x, y, z; }
+
+ alias Point = SumType!(Point2D, Point3D);
+
+ version (none)
+ {
+ // This function works, but the code is ugly and repetitive.
+ // It uses three separate calls to match!
+ @safe pure nothrow @nogc
+ bool sameDimensions(Point p1, Point p2)
+ {
+ return p1.match!(
+ (Point2D _) => p2.match!(
+ (Point2D _) => true,
+ _ => false
+ ),
+ (Point3D _) => p2.match!(
+ (Point3D _) => true,
+ _ => false
+ )
+ );
+ }
+ }
+
+ // This version is much nicer.
+ @safe pure nothrow @nogc
+ bool sameDimensions(Point p1, Point p2)
+ {
+ alias doMatch = match!(
+ (Point2D _1, Point2D _2) => true,
+ (Point3D _1, Point3D _2) => true,
+ (_1, _2) => false
+ );
+
+ return doMatch(p1, p2);
+ }
+
+ Point a = Point2D(1, 2);
+ Point b = Point2D(3, 4);
+ Point c = Point3D(5, 6, 7);
+ Point d = Point3D(8, 9, 0);
+
+ assert( sameDimensions(a, b));
+ assert( sameDimensions(c, d));
+ assert(!sameDimensions(a, c));
+ assert(!sameDimensions(d, b));
+}
+
+/**
+ * Attempts to call a type-appropriate function with the value held in a
+ * [SumType], and throws on failure.
+ *
+ * Matches are chosen using the same rules as [match], but are not required to
+ * be exhaustive—in other words, a type (or combination of types) is allowed to
+ * have no matching handler. If a type without a handler is encountered at
+ * runtime, a [MatchException] is thrown.
+ *
+ * Not available when compiled with `-betterC`.
+ *
+ * Returns:
+ * The value returned from the handler that matches the currently-held type,
+ * if a handler was given for that type.
+ *
+ * Throws:
+ * [MatchException], if the currently-held type has no matching handler.
+ *
+ * See_Also: `std.variant.tryVisit`
+ * See_Also: $(REF tryVisit, std,variant)
+ */
+version (D_Exceptions)
+template tryMatch(handlers...)
+{
+ import std.typecons : No;
+
+ /**
+ * The actual `tryMatch` function.
+ *
+ * Params:
+ * args = One or more [SumType] objects.
+ */
+ auto ref tryMatch(SumTypes...)(auto ref SumTypes args)
+ if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
+ {
+ return matchImpl!(No.exhaustive, handlers)(args);
+ }
+}
+
+/**
+ * Thrown by [tryMatch] when an unhandled type is encountered.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+version (D_Exceptions)
+class MatchException : Exception
+{
+ ///
+ pure @safe @nogc nothrow
+ this(string msg, string file = __FILE__, size_t line = __LINE__)
+ {
+ super(msg, file, line);
+ }
+}
+
+/**
+ * True if `handler` is a potential match for `Ts`, otherwise false.
+ *
+ * See the documentation for [match] for a full explanation of how matches are
+ * chosen.
+ */
+template canMatch(alias handler, Ts...)
+if (Ts.length > 0)
+{
+ enum canMatch = is(typeof((Ts args) => handler(args)));
+}
+
+///
+@safe unittest
+{
+ alias handleInt = (int i) => "got an int";
+
+ assert( canMatch!(handleInt, int));
+ assert(!canMatch!(handleInt, string));
+}
+
+// Includes all overloads of the given handler
+@safe unittest
+{
+ static struct OverloadSet
+ {
+ static void fun(int n) {}
+ static void fun(double d) {}
+ }
+
+ assert(canMatch!(OverloadSet.fun, int));
+ assert(canMatch!(OverloadSet.fun, double));
+}
+
+// Like aliasSeqOf!(iota(n)), but works in BetterC
+private template Iota(size_t n)
+{
+ static if (n == 0)
+ {
+ alias Iota = AliasSeq!();
+ }
+ else
+ {
+ alias Iota = AliasSeq!(Iota!(n - 1), n - 1);
+ }
+}
+
+@safe unittest
+{
+ assert(is(Iota!0 == AliasSeq!()));
+ assert(Iota!1 == AliasSeq!(0));
+ assert(Iota!3 == AliasSeq!(0, 1, 2));
+}
+
+/* The number that the dim-th argument's tag is multiplied by when
+ * converting TagTuples to and from case indices ("caseIds").
+ *
+ * Named by analogy to the stride that the dim-th index into a
+ * multidimensional static array is multiplied by to calculate the
+ * offset of a specific element.
+ */
+private size_t stride(size_t dim, lengths...)()
+{
+ import core.checkedint : mulu;
+
+ size_t result = 1;
+ bool overflow = false;
+
+ static foreach (i; 0 .. dim)
+ {
+ result = mulu(result, lengths[i], overflow);
+ }
+
+ /* The largest number matchImpl uses, numCases, is calculated with
+ * stride!(SumTypes.length), so as long as this overflow check
+ * passes, we don't need to check for overflow anywhere else.
+ */
+ assert(!overflow, "Integer overflow");
+ return result;
+}
+
+private template matchImpl(Flag!"exhaustive" exhaustive, handlers...)
+{
+ auto ref matchImpl(SumTypes...)(auto ref SumTypes args)
+ if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
+ {
+ enum typeCount(SumType) = SumType.Types.length;
+ alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes));
+
+ /* A TagTuple represents a single possible set of tags that `args`
+ * could have at runtime.
+ *
+ * Because D does not allow a struct to be the controlling expression
+ * of a switch statement, we cannot dispatch on the TagTuple directly.
+ * Instead, we must map each TagTuple to a unique integer and generate
+ * a case label for each of those integers.
+ *
+ * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses
+ * the same technique that's used to map index tuples to memory offsets
+ * in a multidimensional static array.
+ *
+ * For example, when `args` consists of two SumTypes with two member
+ * types each, the TagTuples corresponding to each case label are:
+ *
+ * case 0: TagTuple([0, 0])
+ * case 1: TagTuple([1, 0])
+ * case 2: TagTuple([0, 1])
+ * case 3: TagTuple([1, 1])
+ *
+ * When there is only one argument, the caseId is equal to that
+ * argument's tag.
+ */
+ static struct TagTuple
+ {
+ size_t[SumTypes.length] tags;
+ alias tags this;
+
+ invariant
+ {
+ static foreach (i; 0 .. tags.length)
+ {
+ assert(tags[i] < SumTypes[i].Types.length, "Invalid tag");
+ }
+ }
+
+ this(ref const(SumTypes) args)
+ {
+ static foreach (i; 0 .. tags.length)
+ {
+ tags[i] = args[i].tag;
+ }
+ }
+
+ static TagTuple fromCaseId(size_t caseId)
+ {
+ TagTuple result;
+
+ // Most-significant to least-significant
+ static foreach_reverse (i; 0 .. result.length)
+ {
+ result[i] = caseId / stride!i;
+ caseId %= stride!i;
+ }
+
+ return result;
+ }
+
+ size_t toCaseId()
+ {
+ size_t result;
+
+ static foreach (i; 0 .. tags.length)
+ {
+ result += tags[i] * stride!i;
+ }
+
+ return result;
+ }
+ }
+
+ /*
+ * A list of arguments to be passed to a handler needed for the case
+ * labeled with `caseId`.
+ */
+ template handlerArgs(size_t caseId)
+ {
+ enum tags = TagTuple.fromCaseId(caseId);
+ enum argsFrom(size_t i : tags.length) = "";
+ enum argsFrom(size_t i) = "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~
+ ".Types[" ~ toCtString!(tags[i]) ~ "])(), " ~ argsFrom!(i + 1);
+ enum handlerArgs = argsFrom!0;
+ }
+
+ /* An AliasSeq of the types of the member values in the argument list
+ * returned by `handlerArgs!caseId`.
+ *
+ * Note that these are the actual (that is, qualified) types of the
+ * member values, which may not be the same as the types listed in
+ * the arguments' `.Types` properties.
+ */
+ template valueTypes(size_t caseId)
+ {
+ enum tags = TagTuple.fromCaseId(caseId);
+
+ template getType(size_t i)
+ {
+ enum tid = tags[i];
+ alias T = SumTypes[i].Types[tid];
+ alias getType = typeof(args[i].get!T());
+ }
+
+ alias valueTypes = Map!(getType, Iota!(tags.length));
+ }
+
+ /* The total number of cases is
+ *
+ * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length
+ *
+ * Or, equivalently,
+ *
+ * ubyte[SumTypes[0].Types.length]...[SumTypes[$-1].Types.length].sizeof
+ *
+ * Conveniently, this is equal to stride!(SumTypes.length), so we can
+ * use that function to compute it.
+ */
+ enum numCases = stride!(SumTypes.length);
+
+ /* Guaranteed to never be a valid handler index, since
+ * handlers.length <= size_t.max.
+ */
+ enum noMatch = size_t.max;
+
+ // An array that maps caseIds to handler indices ("hids").
+ enum matches = ()
+ {
+ size_t[numCases] matches;
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=19561
+ foreach (ref match; matches)
+ {
+ match = noMatch;
+ }
+
+ static foreach (caseId; 0 .. numCases)
+ {
+ static foreach (hid, handler; handlers)
+ {
+ static if (canMatch!(handler, valueTypes!caseId))
+ {
+ if (matches[caseId] == noMatch)
+ {
+ matches[caseId] = hid;
+ }
+ }
+ }
+ }
+
+ return matches;
+ }();
+
+ import std.algorithm.searching : canFind;
+
+ // Check for unreachable handlers
+ static foreach (hid, handler; handlers)
+ {
+ static assert(matches[].canFind(hid),
+ "`handlers[" ~ toCtString!hid ~ "]` " ~
+ "of type `" ~ ( __traits(isTemplate, handler)
+ ? "template"
+ : typeof(handler).stringof
+ ) ~ "` " ~
+ "never matches"
+ );
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=19993
+ enum handlerName(size_t hid) = "handler" ~ toCtString!hid;
+
+ static foreach (size_t hid, handler; handlers)
+ {
+ mixin("alias ", handlerName!hid, " = handler;");
+ }
+
+ immutable argsId = TagTuple(args).toCaseId;
+
+ final switch (argsId)
+ {
+ static foreach (caseId; 0 .. numCases)
+ {
+ case caseId:
+ static if (matches[caseId] != noMatch)
+ {
+ return mixin(handlerName!(matches[caseId]), "(", handlerArgs!caseId, ")");
+ }
+ else
+ {
+ static if (exhaustive)
+ {
+ static assert(false,
+ "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
+ }
+ else
+ {
+ throw new MatchException(
+ "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
+ }
+ }
+ }
+ }
+
+ assert(false, "unreachable");
+ }
+}
+
+// Matching
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!((int v) => true, (float v) => false));
+ assert(y.match!((int v) => false, (float v) => true));
+}
+
+// Missing handlers
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+
+ assert(!__traits(compiles, x.match!((int x) => true)));
+ assert(!__traits(compiles, x.match!()));
+}
+
+// Handlers with qualified parameters
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias MySum = SumType!(int[], float[]);
+
+ MySum x = MySum([1, 2, 3]);
+ MySum y = MySum([1.0, 2.0, 3.0]);
+
+ assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
+ assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true));
+}
+
+// Handlers for qualified types
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias MySum = SumType!(immutable(int[]), immutable(float[]));
+
+ MySum x = MySum([1, 2, 3]);
+
+ assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false));
+ assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
+ // Tail-qualified parameters
+ assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false));
+ assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false));
+ // Generic parameters
+ assert(x.match!((immutable v) => true));
+ assert(x.match!((const v) => true));
+ // Unqualified parameters
+ assert(!__traits(compiles,
+ x.match!((int[] v) => true, (float[] v) => false)
+ ));
+}
+
+// Delegate handlers
+// Disabled in BetterC due to use of closures
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ int answer = 42;
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!((int v) => v == answer, (float v) => v == answer));
+ assert(!y.match!((int v) => v == answer, (float v) => v == answer));
+}
+
+version (unittest)
+{
+ version (D_BetterC)
+ {
+ // std.math.isClose depends on core.runtime.math, so use a
+ // libc-based version for testing with -betterC
+ @safe pure @nogc nothrow
+ private bool isClose(double lhs, double rhs)
+ {
+ import core.stdc.math : fabs;
+
+ return fabs(lhs - rhs) < 1e-5;
+ }
+ }
+ else
+ {
+ import std.math.operations : isClose;
+ }
+}
+
+// Generic handler
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!(v => v*2) == 84);
+ assert(y.match!(v => v*2).isClose(6.28));
+}
+
+// Fallback to generic handler
+// Disabled in BetterC due to use of std.conv.to
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.conv : to;
+
+ alias MySum = SumType!(int, float, string);
+
+ MySum x = MySum(42);
+ MySum y = MySum("42");
+
+ assert(x.match!((string v) => v.to!int, v => v*2) == 84);
+ assert(y.match!((string v) => v.to!int, v => v*2) == 42);
+}
+
+// Multiple non-overlapping generic handlers
+@safe unittest
+{
+ import std.array : staticArray;
+
+ alias MySum = SumType!(int, float, int[], char[]);
+
+ static ints = staticArray([1, 2, 3]);
+ static chars = staticArray(['a', 'b', 'c']);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+ MySum z = MySum(ints[]);
+ MySum w = MySum(chars[]);
+
+ assert(x.match!(v => v*2, v => v.length) == 84);
+ assert(y.match!(v => v*2, v => v.length).isClose(6.28));
+ assert(w.match!(v => v*2, v => v.length) == 3);
+ assert(z.match!(v => v*2, v => v.length) == 3);
+}
+
+// Structural matching
+@safe unittest
+{
+ static struct S1 { int x; }
+ static struct S2 { int y; }
+ alias MySum = SumType!(S1, S2);
+
+ MySum a = MySum(S1(0));
+ MySum b = MySum(S2(0));
+
+ assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1);
+ assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1);
+}
+
+// Separate opCall handlers
+@safe unittest
+{
+ static struct IntHandler
+ {
+ bool opCall(int arg)
+ {
+ return true;
+ }
+ }
+
+ static struct FloatHandler
+ {
+ bool opCall(float arg)
+ {
+ return false;
+ }
+ }
+
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!(IntHandler.init, FloatHandler.init));
+ assert(!y.match!(IntHandler.init, FloatHandler.init));
+}
+
+// Compound opCall handler
+@safe unittest
+{
+ static struct CompoundHandler
+ {
+ bool opCall(int arg)
+ {
+ return true;
+ }
+
+ bool opCall(float arg)
+ {
+ return false;
+ }
+ }
+
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!(CompoundHandler.init));
+ assert(!y.match!(CompoundHandler.init));
+}
+
+// Ordered matching
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+
+ assert(x.match!((int v) => true, v => false));
+}
+
+// Non-exhaustive matching
+version (D_Exceptions)
+@system unittest
+{
+ import std.exception : assertThrown, assertNotThrown;
+
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assertNotThrown!MatchException(x.tryMatch!((int n) => true));
+ assertThrown!MatchException(y.tryMatch!((int n) => true));
+}
+
+// Non-exhaustive matching in @safe code
+version (D_Exceptions)
+@safe unittest
+{
+ SumType!(int, float) x;
+
+ auto _ = x.tryMatch!(
+ (int n) => n + 1,
+ );
+}
+
+// Handlers with ref parameters
+@safe unittest
+{
+ alias Value = SumType!(long, double);
+
+ auto value = Value(3.14);
+
+ value.match!(
+ (long) {},
+ (ref double d) { d *= 2; }
+ );
+
+ assert(value.get!double.isClose(6.28));
+}
+
+// Unreachable handlers
+@safe unittest
+{
+ alias MySum = SumType!(int, string);
+
+ MySum s;
+
+ assert(!__traits(compiles,
+ s.match!(
+ (int _) => 0,
+ (string _) => 1,
+ (double _) => 2
+ )
+ ));
+
+ assert(!__traits(compiles,
+ s.match!(
+ _ => 0,
+ (int _) => 1
+ )
+ ));
+}
+
+// Unsafe handlers
+@system unittest
+{
+ SumType!int x;
+ alias unsafeHandler = (int x) @system { return; };
+
+ assert(!__traits(compiles, () @safe
+ {
+ x.match!unsafeHandler;
+ }));
+
+ auto test() @system
+ {
+ return x.match!unsafeHandler;
+ }
+}
+
+// Overloaded handlers
+@safe unittest
+{
+ static struct OverloadSet
+ {
+ static string fun(int i) { return "int"; }
+ static string fun(double d) { return "double"; }
+ }
+
+ alias MySum = SumType!(int, double);
+
+ MySum a = 42;
+ MySum b = 3.14;
+
+ assert(a.match!(OverloadSet.fun) == "int");
+ assert(b.match!(OverloadSet.fun) == "double");
+}
+
+// Overload sets that include SumType arguments
+@safe unittest
+{
+ alias Inner = SumType!(int, double);
+ alias Outer = SumType!(Inner, string);
+
+ static struct OverloadSet
+ {
+ @safe:
+ static string fun(int i) { return "int"; }
+ static string fun(double d) { return "double"; }
+ static string fun(string s) { return "string"; }
+ static string fun(Inner i) { return i.match!fun; }
+ static string fun(Outer o) { return o.match!fun; }
+ }
+
+ Outer a = Inner(42);
+ Outer b = Inner(3.14);
+ Outer c = "foo";
+
+ assert(OverloadSet.fun(a) == "int");
+ assert(OverloadSet.fun(b) == "double");
+ assert(OverloadSet.fun(c) == "string");
+}
+
+// Overload sets with ref arguments
+@safe unittest
+{
+ static struct OverloadSet
+ {
+ static void fun(ref int i) { i = 42; }
+ static void fun(ref double d) { d = 3.14; }
+ }
+
+ alias MySum = SumType!(int, double);
+
+ MySum x = 0;
+ MySum y = 0.0;
+
+ x.match!(OverloadSet.fun);
+ y.match!(OverloadSet.fun);
+
+ assert(x.match!((value) => is(typeof(value) == int) && value == 42));
+ assert(y.match!((value) => is(typeof(value) == double) && value == 3.14));
+}
+
+// Overload sets with templates
+@safe unittest
+{
+ import std.traits : isNumeric;
+
+ static struct OverloadSet
+ {
+ static string fun(string arg)
+ {
+ return "string";
+ }
+
+ static string fun(T)(T arg)
+ if (isNumeric!T)
+ {
+ return "numeric";
+ }
+ }
+
+ alias MySum = SumType!(int, string);
+
+ MySum x = 123;
+ MySum y = "hello";
+
+ assert(x.match!(OverloadSet.fun) == "numeric");
+ assert(y.match!(OverloadSet.fun) == "string");
+}
+
+// Github issue #24
+@safe unittest
+{
+ void test() @nogc
+ {
+ int acc = 0;
+ SumType!int(1).match!((int x) => acc += x);
+ }
+}
+
+// Github issue #31
+@safe unittest
+{
+ void test() @nogc
+ {
+ int acc = 0;
+
+ SumType!(int, string)(1).match!(
+ (int x) => acc += x,
+ (string _) => 0,
+ );
+ }
+}
+
+// Types that `alias this` a SumType
+@safe unittest
+{
+ static struct A {}
+ static struct B {}
+ static struct D { SumType!(A, B) value; alias value this; }
+
+ auto _ = D().match!(_ => true);
+}
+
+// Multiple dispatch
+@safe unittest
+{
+ alias MySum = SumType!(int, string);
+
+ static int fun(MySum x, MySum y)
+ {
+ import std.meta : Args = AliasSeq;
+
+ return Args!(x, y).match!(
+ (int xv, int yv) => 0,
+ (string xv, int yv) => 1,
+ (int xv, string yv) => 2,
+ (string xv, string yv) => 3
+ );
+ }
+
+ assert(fun(MySum(0), MySum(0)) == 0);
+ assert(fun(MySum(""), MySum(0)) == 1);
+ assert(fun(MySum(0), MySum("")) == 2);
+ assert(fun(MySum(""), MySum("")) == 3);
+}
+
+// inout SumTypes
+@safe unittest
+{
+ inout(int[]) fun(inout(SumType!(int[])) x)
+ {
+ return x.match!((inout(int[]) a) => a);
+ }
+}
+
+private void destroyIfOwner(T)(ref T value)
+{
+ static if (hasElaborateDestructor!T)
+ {
+ destroy(value);
+ }
+}
diff --git a/libphobos/src/std/system.d b/libphobos/src/std/system.d
index 353d692848a..7a115da5409 100644
--- a/libphobos/src/std/system.d
+++ b/libphobos/src/std/system.d
@@ -3,10 +3,11 @@
/**
* Information about the target operating system, environment, and CPU.
*
- * Copyright: Copyright Digital Mars 2000 - 2011
+ * Copyright: Copyright The D Language Foundation 2000 - 2011
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: $(HTTP digitalmars.com, Walter Bright) and Jonathan M Davis
- * Source: $(PHOBOSSRC std/_system.d)
+ * Authors: $(HTTP digitalmars.com, Walter Bright) and
+ $(HTTP jmdavisprog.com, Jonathan M Davis)
+ * Source: $(PHOBOSSRC std/system.d)
*/
module std.system;
@@ -18,8 +19,8 @@ immutable
Note:
This is for cases where you need a value representing the OS at
runtime. If you're doing something which should compile differently
- on different OSes, then please use $(D version (Windows)),
- $(D version (linux)), etc.
+ on different OSes, then please use `version (Windows)`,
+ `version (linux)`, etc.
See_Also:
$(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions)
@@ -38,7 +39,8 @@ immutable
dragonFlyBSD, /// DragonFlyBSD
solaris, /// Solaris
android, /// Android
- otherPosix /// Other Posix Systems
+ otherPosix, /// Other Posix Systems
+ unknown, /// Unknown
}
/// The OS that the program was compiled for.
@@ -54,7 +56,7 @@ immutable
else version (NetBSD) OS os = OS.netBSD;
else version (DragonFlyBSD) OS os = OS.dragonFlyBSD;
else version (Posix) OS os = OS.otherPosix;
- else static assert(0, "Unknown OS.");
+ else OS os = OS.unknown;
/++
Byte order endianness.
@@ -63,8 +65,8 @@ immutable
This is intended for cases where you need to deal with endianness at
runtime. If you're doing something which should compile differently
depending on whether you're compiling on a big endian or little
- endian machine, then please use $(D version (BigEndian)) and
- $(D version (LittleEndian)).
+ endian machine, then please use `version (BigEndian)` and
+ `version (LittleEndian)`.
See_Also:
$(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions)
diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d
index 7badab4280b..230a7c677cf 100644
--- a/libphobos/src/std/traits.d
+++ b/libphobos/src/std/traits.d
@@ -8,12 +8,12 @@
* $(DIVC quickindex,
* $(BOOKTABLE ,
* $(TR $(TH Category) $(TH Templates))
- * $(TR $(TD Symbol Name _traits) $(TD
+ * $(TR $(TD Symbol Name traits) $(TD
* $(LREF fullyQualifiedName)
* $(LREF moduleName)
* $(LREF packageName)
* ))
- * $(TR $(TD Function _traits) $(TD
+ * $(TR $(TD Function traits) $(TD
* $(LREF isFunction)
* $(LREF arity)
* $(LREF functionAttributes)
@@ -31,7 +31,7 @@
* $(LREF SetFunctionAttributes)
* $(LREF variadicFunctionStyle)
* ))
- * $(TR $(TD Aggregate Type _traits) $(TD
+ * $(TR $(TD Aggregate Type traits) $(TD
* $(LREF BaseClassesTuple)
* $(LREF BaseTypeTuple)
* $(LREF classInstanceAlignment)
@@ -42,6 +42,7 @@
* $(LREF hasElaborateAssign)
* $(LREF hasElaborateCopyConstructor)
* $(LREF hasElaborateDestructor)
+ * $(LREF hasElaborateMove)
* $(LREF hasIndirections)
* $(LREF hasMember)
* $(LREF hasStaticMember)
@@ -58,6 +59,7 @@
* ))
* $(TR $(TD Type Conversion) $(TD
* $(LREF CommonType)
+ * $(LREF AllImplicitConversionTargets)
* $(LREF ImplicitConversionTargets)
* $(LREF CopyTypeQualifiers)
* $(LREF CopyConstness)
@@ -73,6 +75,7 @@
* $(LREF SharedOf)
* $(LREF SharedInoutOf)
* $(LREF SharedConstOf)
+ * $(LREF SharedConstInoutOf)
* $(LREF ImmutableOf)
* $(LREF QualifierOf)
* ))
@@ -128,6 +131,7 @@
* $(LREF OriginalType)
* $(LREF PointerTarget)
* $(LREF Signed)
+ * $(LREF Unconst)
* $(LREF Unqual)
* $(LREF Unsigned)
* $(LREF ValueType)
@@ -146,27 +150,26 @@
* )
* )
*
- * Copyright: Copyright Digital Mars 2005 - 2009.
+ * Copyright: Copyright The D Language Foundation 2005 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright),
- * Tomasz Stachowiak ($(D isExpressions)),
+ * Tomasz Stachowiak (`isExpressions`),
* $(HTTP erdani.org, Andrei Alexandrescu),
* Shin Fujishiro,
* $(HTTP octarineparrot.com, Robert Clipsham),
* $(HTTP klickverbot.at, David Nadlinger),
* Kenji Hara,
* Shoichi Kato
- * Source: $(PHOBOSSRC std/_traits.d)
+ * Source: $(PHOBOSSRC std/traits.d)
*/
-/* Copyright Digital Mars 2005 - 2009.
+/* Copyright The D Language Foundation 2005 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
module std.traits;
-import std.meta : AliasSeq, allSatisfy;
-import std.functional : unaryFun;
+import std.meta : AliasSeq, allSatisfy, anySatisfy, ApplyLeft;
// Legacy inheritance from std.typetuple
// See also: https://github.com/dlang/phobos/pull/5484#discussion_r122602797
@@ -176,88 +179,11 @@ import std.meta : staticMapMeta = staticMap;
alias staticMap = staticMapMeta;
///////////////////////////////////////////////////////////////////////////////
-// Functions
+// Type lists
///////////////////////////////////////////////////////////////////////////////
-// Petit demangler
-// (this or similar thing will eventually go to std.demangle if necessary
-// ctfe stuffs are available)
private
{
- struct Demangle(T)
- {
- T value; // extracted information
- string rest;
- }
-
- /* Demangles mstr as the storage class part of Argument. */
- Demangle!uint demangleParameterStorageClass(string mstr)
- {
- uint pstc = 0; // parameter storage class
-
- // Argument --> Argument2 | M Argument2
- if (mstr.length > 0 && mstr[0] == 'M')
- {
- pstc |= ParameterStorageClass.scope_;
- mstr = mstr[1 .. $];
- }
-
- // Argument2 --> Type | J Type | K Type | L Type
- ParameterStorageClass stc2;
-
- switch (mstr.length ? mstr[0] : char.init)
- {
- case 'J': stc2 = ParameterStorageClass.out_; break;
- case 'K': stc2 = ParameterStorageClass.ref_; break;
- case 'L': stc2 = ParameterStorageClass.lazy_; break;
- case 'N': if (mstr.length >= 2 && mstr[1] == 'k')
- stc2 = ParameterStorageClass.return_;
- break;
- default : break;
- }
- if (stc2 != ParameterStorageClass.init)
- {
- pstc |= stc2;
- mstr = mstr[1 .. $];
- if (stc2 & ParameterStorageClass.return_)
- mstr = mstr[1 .. $];
- }
-
- return Demangle!uint(pstc, mstr);
- }
-
- /* Demangles mstr as FuncAttrs. */
- Demangle!uint demangleFunctionAttributes(string mstr)
- {
- immutable LOOKUP_ATTRIBUTE =
- [
- 'a': FunctionAttribute.pure_,
- 'b': FunctionAttribute.nothrow_,
- 'c': FunctionAttribute.ref_,
- 'd': FunctionAttribute.property,
- 'e': FunctionAttribute.trusted,
- 'f': FunctionAttribute.safe,
- 'i': FunctionAttribute.nogc,
- 'j': FunctionAttribute.return_,
- 'l': FunctionAttribute.scope_
- ];
- uint atts = 0;
-
- // FuncAttrs --> FuncAttr | FuncAttr FuncAttrs
- // FuncAttr --> empty | Na | Nb | Nc | Nd | Ne | Nf | Ni | Nj
- // except 'Ng' == inout, because it is a qualifier of function type
- while (mstr.length >= 2 && mstr[0] == 'N' && mstr[1] != 'g' && mstr[1] != 'k')
- {
- if (FunctionAttribute att = LOOKUP_ATTRIBUTE[ mstr[1] ])
- {
- atts |= att;
- mstr = mstr[2 .. $];
- }
- else assert(0);
- }
- return Demangle!uint(atts, mstr);
- }
-
static if (is(ucent))
{
alias CentTypeList = AliasSeq!(cent, ucent);
@@ -281,28 +207,131 @@ private
alias CharTypeList = AliasSeq!(char, wchar, dchar);
}
-package
+/**
+ * Params:
+ * T = The type to qualify
+ * Returns:
+ * `T` with the `inout` qualifier added.
+ */
+alias InoutOf(T) = inout(T);
+
+///
+@safe unittest
{
- // Add the mutable qualifier to the given type T.
- template MutableOf(T) { alias MutableOf = T ; }
+ static assert(is(InoutOf!(int) == inout int));
+ static assert(is(InoutOf!(inout int) == inout int));
+ static assert(is(InoutOf!(const int) == inout const int));
+ static assert(is(InoutOf!(shared int) == inout shared int));
}
-/// Add the inout qualifier to the given type T.
-template InoutOf(T) { alias InoutOf = inout(T) ; }
-/// Add the const qualifier to the given type T.
-template ConstOf(T) { alias ConstOf = const(T) ; }
-/// Add the shared qualifier to the given type T.
-template SharedOf(T) { alias SharedOf = shared(T) ; }
-/// Add the shared and inout qualifiers to the given type T.
-template SharedInoutOf(T) { alias SharedInoutOf = shared(inout(T)); }
-/// Add the shared and const qualifiers to the given type T.
-template SharedConstOf(T) { alias SharedConstOf = shared(const(T)); }
-/// Add the immutable qualifier to the given type T.
-template ImmutableOf(T) { alias ImmutableOf = immutable(T) ; }
+/**
+ * Params:
+ * T = The type to qualify
+ * Returns:
+ * `T` with the `const` qualifier added.
+ */
+alias ConstOf(T) = const(T);
+
+///
+@safe unittest
+{
+ static assert(is(ConstOf!(int) == const int));
+ static assert(is(ConstOf!(const int) == const int));
+ static assert(is(ConstOf!(inout int) == const inout int));
+ static assert(is(ConstOf!(shared int) == const shared int));
+}
+
+/**
+ * Params:
+ * T = The type to qualify
+ * Returns:
+ * `T` with the `shared` qualifier added.
+ */
+alias SharedOf(T) = shared(T);
+
+///
+@safe unittest
+{
+ static assert(is(SharedOf!(int) == shared int));
+ static assert(is(SharedOf!(shared int) == shared int));
+ static assert(is(SharedOf!(inout int) == shared inout int));
+ static assert(is(SharedOf!(immutable int) == shared immutable int));
+}
+
+/**
+ * Params:
+ * T = The type to qualify
+ * Returns:
+ * `T` with the `inout` and `shared` qualifiers added.
+ */
+alias SharedInoutOf(T) = shared(inout(T));
+
+///
+@safe unittest
+{
+ static assert(is(SharedInoutOf!(int) == shared inout int));
+ static assert(is(SharedInoutOf!(int) == inout shared int));
+
+ static assert(is(SharedInoutOf!(const int) == shared inout const int));
+ static assert(is(SharedInoutOf!(immutable int) == shared inout immutable int));
+}
+
+/**
+ * Params:
+ * T = The type to qualify
+ * Returns:
+ * `T` with the `const` and `shared` qualifiers added.
+ */
+alias SharedConstOf(T) = shared(const(T));
+
+///
+@safe unittest
+{
+ static assert(is(SharedConstOf!(int) == shared const int));
+ static assert(is(SharedConstOf!(int) == const shared int));
+
+ static assert(is(SharedConstOf!(inout int) == shared inout const int));
+ // immutable variables are implicitly shared and const
+ static assert(is(SharedConstOf!(immutable int) == immutable int));
+}
+
+/**
+ * Params:
+ * T = The type to qualify
+ * Returns:
+ * `T` with the `const`, `shared`, and `inout` qualifiers added.
+ */
+alias SharedConstInoutOf(T) = shared(const(inout(T)));
+
+///
+@safe unittest
+{
+ static assert(is(SharedConstInoutOf!(int) == shared const inout int));
+ static assert(is(SharedConstInoutOf!(int) == const shared inout int));
+ static assert(is(SharedConstInoutOf!(inout int) == shared inout const int));
+ // immutable variables are implicitly shared and const
+ static assert(is(SharedConstInoutOf!(immutable int) == immutable int));
+}
+
+/**
+ * Params:
+ * T = The type to qualify
+ * Returns:
+ * `T` with the `immutable` qualifier added.
+ */
+alias ImmutableOf(T) = immutable(T);
+
+///
+@safe unittest
+{
+ static assert(is(ImmutableOf!(int) == immutable int));
+ static assert(is(ImmutableOf!(const int) == immutable int));
+ static assert(is(ImmutableOf!(inout int) == immutable int));
+ static assert(is(ImmutableOf!(shared int) == immutable int));
+}
@safe unittest
{
- static assert(is( MutableOf!int == int));
static assert(is( InoutOf!int == inout int));
static assert(is( ConstOf!int == const int));
static assert(is( SharedOf!int == shared int));
@@ -311,16 +340,45 @@ template ImmutableOf(T) { alias ImmutableOf = immutable(T) ; }
static assert(is( ImmutableOf!int == immutable int));
}
-/// Get qualifier template from the given type T
+/**
+ * Gives a template that can be used to apply the same
+ * attributes that are on the given type `T`. E.g. passing
+ * `inout shared int` will return `SharedInoutOf`.
+ *
+ * Params:
+ * T = the type to check qualifiers from
+ * Returns:
+ * The qualifier template from the given type `T`
+ */
template QualifierOf(T)
{
- static if (is(T == shared(const U), U)) alias QualifierOf = SharedConstOf;
- else static if (is(T == const U , U)) alias QualifierOf = ConstOf;
- else static if (is(T == shared(inout U), U)) alias QualifierOf = SharedInoutOf;
- else static if (is(T == inout U , U)) alias QualifierOf = InoutOf;
- else static if (is(T == immutable U , U)) alias QualifierOf = ImmutableOf;
- else static if (is(T == shared U , U)) alias QualifierOf = SharedOf;
- else alias QualifierOf = MutableOf;
+ static if (is(immutable T == T))
+ {
+ alias QualifierOf = ImmutableOf;
+ }
+ else
+ {
+ private enum quals = is(const T == T) | (is(inout T == T) << 1) | (is(shared T == T) << 2);
+ static if (quals == 0) { import std.meta : Alias; alias QualifierOf = Alias; }
+ else static if (quals == 1) alias QualifierOf = ConstOf;
+ else static if (quals == 2) alias QualifierOf = InoutOf;
+ else static if (quals == 3) alias QualifierOf = ConstInoutOf;
+ else static if (quals == 4) alias QualifierOf = SharedOf;
+ else static if (quals == 5) alias QualifierOf = SharedConstOf;
+ else static if (quals == 6) alias QualifierOf = SharedInoutOf;
+ else alias QualifierOf = SharedConstInoutOf;
+ }
+}
+
+///
+@safe unittest
+{
+ static assert(__traits(isSame, QualifierOf!(shared const inout int), SharedConstInoutOf));
+ static assert(__traits(isSame, QualifierOf!(immutable int), ImmutableOf));
+ static assert(__traits(isSame, QualifierOf!(shared int), SharedOf));
+ static assert(__traits(isSame, QualifierOf!(shared inout int), SharedInoutOf));
+ import std.meta : Alias;
+ static assert(__traits(isSame, QualifierOf!(int), Alias));
}
@safe unittest
@@ -334,9 +392,10 @@ template QualifierOf(T)
alias Qual7 = QualifierOf!( immutable int); static assert(is(Qual7!long == immutable long));
}
-version (unittest)
+version (StdUnittest)
{
- alias TypeQualifierList = AliasSeq!(MutableOf, ConstOf, SharedOf, SharedConstOf, ImmutableOf);
+ import std.meta : Alias;
+ alias TypeQualifierList = AliasSeq!(Alias, ConstOf, SharedOf, SharedConstOf, ImmutableOf);
struct SubTypeOf(T)
{
@@ -355,12 +414,14 @@ template packageName(alias T)
{
import std.algorithm.searching : startsWith;
+ enum bool isNotFunc = !isSomeFunction!(T);
+
static if (__traits(compiles, parentOf!T))
enum parent = packageName!(parentOf!T);
else
enum string parent = null;
- static if (T.stringof.startsWith("package "))
+ static if (isNotFunc && T.stringof.startsWith("package "))
enum packageName = (parent.length ? parent ~ '.' : "") ~ T.stringof[8 .. $];
else static if (parent)
enum packageName = parent;
@@ -371,7 +432,6 @@ template packageName(alias T)
///
@safe unittest
{
- import std.traits;
static assert(packageName!packageName == "std");
}
@@ -379,8 +439,7 @@ template packageName(alias T)
{
import std.array;
- // Commented out because of dmd @@@BUG8922@@@
- // static assert(packageName!std == "std"); // this package (currently: "std.std")
+ static assert(packageName!std == "std");
static assert(packageName!(std.traits) == "std"); // this module
static assert(packageName!packageName == "std"); // symbol in this module
static assert(packageName!(std.array) == "std"); // other module from same package
@@ -394,7 +453,7 @@ template packageName(alias T)
static assert(packageName!(X12287!int.i) == "std");
}
-version (none) version (unittest) //Please uncomment me when changing packageName to test global imports
+version (none) @safe unittest //Please uncomment me when changing packageName to test global imports
{
import core.sync.barrier; // global import
static assert(packageName!core == "core");
@@ -402,6 +461,25 @@ version (none) version (unittest) //Please uncomment me when changing packageNam
static assert(packageName!Barrier == "core.sync");
}
+///
+@safe unittest
+{
+ static assert(packageName!moduleName == "std");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=13741
+@safe unittest
+{
+ import std.ascii : isWhite;
+ static assert(packageName!(isWhite) == "std");
+
+ struct Foo{void opCall(int){}}
+ static assert(packageName!(Foo.opCall) == "std");
+
+ @property void function(int) vf;
+ static assert(packageName!(vf) == "std");
+}
+
/**
* Get the module name (including package) for the given symbol.
*/
@@ -409,9 +487,13 @@ template moduleName(alias T)
{
import std.algorithm.searching : startsWith;
- static assert(!T.stringof.startsWith("package "), "cannot get the module name for a package");
+ enum bool isNotFunc = !isSomeFunction!(T);
- static if (T.stringof.startsWith("module "))
+ static if (isNotFunc)
+ static assert(!T.stringof.startsWith("package "),
+ "cannot get the module name for a package");
+
+ static if (isNotFunc && T.stringof.startsWith("module "))
{
static if (__traits(compiles, packageName!T))
enum packagePrefix = packageName!T ~ '.';
@@ -427,7 +509,6 @@ template moduleName(alias T)
///
@safe unittest
{
- import std.traits;
static assert(moduleName!moduleName == "std.traits");
}
@@ -450,7 +531,20 @@ template moduleName(alias T)
static assert(moduleName!(X12287!int.i) == "std.traits");
}
-version (none) version (unittest) //Please uncomment me when changing moduleName to test global imports
+// https://issues.dlang.org/show_bug.cgi?id=13741
+@safe unittest
+{
+ import std.ascii : isWhite;
+ static assert(moduleName!(isWhite) == "std.ascii");
+
+ struct Foo{void opCall(int){}}
+ static assert(moduleName!(Foo.opCall) == "std.traits");
+
+ @property void function(int) vf;
+ static assert(moduleName!(vf) == "std.traits");
+}
+
+version (none) @safe unittest //Please uncomment me when changing moduleName to test global imports
{
import core.sync.barrier; // global import
static assert(!__traits(compiles, moduleName!(core.sync)));
@@ -469,7 +563,7 @@ static assert(fullyQualifiedName!(const MyStruct[]) == "const(myModule.MyStruct[
-----------------
*/
template fullyQualifiedName(T...)
- if (T.length == 1)
+if (T.length == 1)
{
static if (is(T))
@@ -484,13 +578,14 @@ template fullyQualifiedName(T...)
static assert(fullyQualifiedName!fullyQualifiedName == "std.traits.fullyQualifiedName");
}
-version (unittest)
+version (StdUnittest)
{
// Used for both fqnType and fqnSym unittests
private struct QualifiedNameTests
{
struct Inner
{
+ bool value;
}
ref const(Inner[string]) func( ref Inner var1, lazy scope string var2 );
@@ -607,8 +702,6 @@ private template fqnSym(alias T)
private template fqnType(T,
bool alreadyConst, bool alreadyImmutable, bool alreadyShared, bool alreadyInout)
{
- import std.format : format;
-
// Convenience tags
enum {
_const = 0,
@@ -622,14 +715,17 @@ private template fqnType(T,
string storageClassesString(uint psc)() @property
{
+ import std.conv : text;
+
alias PSC = ParameterStorageClass;
- return format("%s%s%s%s%s",
+ return text(
psc & PSC.scope_ ? "scope " : "",
psc & PSC.return_ ? "return " : "",
+ psc & PSC.in_ ? "in " : "",
psc & PSC.out_ ? "out " : "",
psc & PSC.ref_ ? "ref " : "",
- psc & PSC.lazy_ ? "lazy " : ""
+ psc & PSC.lazy_ ? "lazy " : "",
);
}
@@ -658,7 +754,7 @@ private template fqnType(T,
import std.range : zip;
string result = join(
- map!(a => format("%s%s", a[0], a[1]))(
+ map!(a => (a[0] ~ a[1]))(
zip([staticMap!(storageClassesString, parameterStC)],
[staticMap!(fullyQualifiedName, parameters)])
),
@@ -676,7 +772,7 @@ private template fqnType(T,
enum linkage = functionLinkage!T;
if (linkage != "D")
- return format("extern(%s) ", linkage);
+ return "extern(" ~ linkage ~ ") ";
else
return "";
}
@@ -689,16 +785,15 @@ private template fqnType(T,
static if (attrs == FA.none)
return "";
else
- return format("%s%s%s%s%s%s%s%s",
- attrs & FA.pure_ ? " pure" : "",
- attrs & FA.nothrow_ ? " nothrow" : "",
- attrs & FA.ref_ ? " ref" : "",
- attrs & FA.property ? " @property" : "",
- attrs & FA.trusted ? " @trusted" : "",
- attrs & FA.safe ? " @safe" : "",
- attrs & FA.nogc ? " @nogc" : "",
- attrs & FA.return_ ? " return" : ""
- );
+ return
+ (attrs & FA.pure_ ? " pure" : "")
+ ~ (attrs & FA.nothrow_ ? " nothrow" : "")
+ ~ (attrs & FA.ref_ ? " ref" : "")
+ ~ (attrs & FA.property ? " @property" : "")
+ ~ (attrs & FA.trusted ? " @trusted" : "")
+ ~ (attrs & FA.safe ? " @safe" : "")
+ ~ (attrs & FA.nogc ? " @nogc" : "")
+ ~ (attrs & FA.return_ ? " return" : "");
}
string addQualifiers(string typeString,
@@ -707,15 +802,12 @@ private template fqnType(T,
auto result = typeString;
if (addShared)
{
- result = format("shared(%s)", result);
+ result = "shared(" ~ result ~")";
}
if (addConst || addImmutable || addInout)
{
- result = format("%s(%s)",
- addConst ? "const" :
- addImmutable ? "immutable" : "inout",
- result
- );
+ result = (addConst ? "const" : addImmutable ? "immutable" : "inout")
+ ~ "(" ~ result ~ ")";
}
return result;
}
@@ -752,61 +844,62 @@ private template fqnType(T,
}
else static if (isStaticArray!T)
{
+ import std.conv : to;
enum fqnType = chain!(
- format("%s[%s]", fqnType!(typeof(T.init[0]), qualifiers), T.length)
+ fqnType!(typeof(T.init[0]), qualifiers) ~ "[" ~ to!string(T.length) ~ "]"
);
}
else static if (isArray!T)
{
enum fqnType = chain!(
- format("%s[]", fqnType!(typeof(T.init[0]), qualifiers))
+ fqnType!(typeof(T.init[0]), qualifiers) ~ "[]"
);
}
else static if (isAssociativeArray!T)
{
enum fqnType = chain!(
- format("%s[%s]", fqnType!(ValueType!T, qualifiers), fqnType!(KeyType!T, noQualifiers))
+ fqnType!(ValueType!T, qualifiers) ~ '[' ~ fqnType!(KeyType!T, noQualifiers) ~ ']'
);
}
else static if (isSomeFunction!T)
{
static if (is(T F == delegate))
{
- enum qualifierString = format("%s%s",
- is(F == shared) ? " shared" : "",
- is(F == inout) ? " inout" :
- is(F == immutable) ? " immutable" :
- is(F == const) ? " const" : ""
- );
- enum formatStr = "%s%s delegate(%s)%s%s";
+ enum qualifierString =
+ (is(F == shared) ? " shared" : "")
+ ~ (is(F == inout) ? " inout" :
+ is(F == immutable) ? " immutable" :
+ is(F == const) ? " const" : "");
enum fqnType = chain!(
- format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers),
- parametersTypeString!(T), functionAttributeString!T, qualifierString)
+ linkageString!T
+ ~ fqnType!(ReturnType!T, noQualifiers)
+ ~ " delegate(" ~ parametersTypeString!(T) ~ ")"
+ ~ functionAttributeString!T
+ ~ qualifierString
);
}
else
{
- static if (isFunctionPointer!T)
- enum formatStr = "%s%s function(%s)%s";
- else
- enum formatStr = "%s%s(%s)%s";
-
enum fqnType = chain!(
- format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers),
- parametersTypeString!(T), functionAttributeString!T)
+ linkageString!T
+ ~ fqnType!(ReturnType!T, noQualifiers)
+ ~ (isFunctionPointer!T ? " function(" : "(")
+ ~ parametersTypeString!(T) ~ ")"
+ ~ functionAttributeString!T
);
}
}
else static if (isPointer!T)
{
enum fqnType = chain!(
- format("%s*", fqnType!(PointerTarget!T, qualifiers))
+ fqnType!(PointerTarget!T, qualifiers) ~ "*"
);
}
else static if (is(T : __vector(V[N]), V, size_t N))
{
+ import std.conv : to;
enum fqnType = chain!(
- format("__vector(%s[%s])", fqnType!(V, qualifiers), N)
+ "__vector(" ~ fqnType!(V, qualifiers) ~ "[" ~ N.to!string ~ "])"
);
}
else
@@ -889,12 +982,12 @@ private template fqnType(T,
* Get the type of the return value from a function,
* a pointer to function, a delegate, a struct
* with an opCall, a pointer to a struct with an opCall,
- * or a class with an $(D opCall). Please note that $(D_KEYWORD ref)
+ * or a class with an `opCall`. Please note that $(D_KEYWORD ref)
* is not part of a type, but the attribute of the function
* (see template $(LREF functionAttributes)).
*/
template ReturnType(func...)
- if (func.length == 1 && isCallable!func)
+if (func.length == 1 && isCallable!func)
{
static if (is(FunctionTypeOf!func R == return))
alias ReturnType = R;
@@ -949,11 +1042,11 @@ template ReturnType(func...)
/***
Get, as a tuple, the types of the parameters to a function, a pointer
-to function, a delegate, a struct with an $(D opCall), a pointer to a
-struct with an $(D opCall), or a class with an $(D opCall).
+to function, a delegate, a struct with an `opCall`, a pointer to a
+struct with an `opCall`, or a class with an `opCall`.
*/
template Parameters(func...)
- if (func.length == 1 && isCallable!func)
+if (func.length == 1 && isCallable!func)
{
static if (is(FunctionTypeOf!func P == function))
alias Parameters = P;
@@ -999,11 +1092,12 @@ alias ParameterTypeTuple = Parameters;
}
/**
-Returns the number of arguments of function $(D func).
+Returns the number of arguments of function `func`.
arity is undefined for variadic functions.
*/
-template arity(alias func)
- if ( isCallable!func && variadicFunctionStyle!func == Variadic.no )
+template arity(func...)
+if (func.length == 1 && isCallable!func &&
+ variadicFunctionStyle!func == Variadic.no)
{
enum size_t arity = Parameters!func.length;
}
@@ -1019,6 +1113,13 @@ template arity(alias func)
static assert(!__traits(compiles, arity!variadicFoo));
}
+// https://issues.dlang.org/show_bug.cgi?id=11389
+@safe unittest
+{
+ alias TheType = size_t function( string[] );
+ static assert(arity!TheType == 1);
+}
+
/**
Get tuple, one per function parameter, of the storage classes of the parameters.
Params:
@@ -1032,17 +1133,18 @@ enum ParameterStorageClass : uint
* These flags can be bitwise OR-ed together to represent complex storage
* class.
*/
- none = 0,
- scope_ = 1, /// ditto
- out_ = 2, /// ditto
- ref_ = 4, /// ditto
- lazy_ = 8, /// ditto
- return_ = 0x10, /// ditto
+ none = 0x00,
+ in_ = 0x01, /// ditto
+ ref_ = 0x02, /// ditto
+ out_ = 0x04, /// ditto
+ lazy_ = 0x08, /// ditto
+ scope_ = 0x10, /// ditto
+ return_ = 0x20, /// ditto
}
/// ditto
template ParameterStorageClassTuple(func...)
- if (func.length == 1 && isCallable!func)
+if (func.length == 1 && isCallable!func)
{
alias Func = FunctionTypeOf!func;
@@ -1073,22 +1175,32 @@ template ParameterStorageClassTuple(func...)
{
alias STC = ParameterStorageClass; // shorten the enum name
- void func(ref int ctx, out real result, real param)
+ void func(ref int ctx, out real result, in real param, void* ptr)
{
}
alias pstc = ParameterStorageClassTuple!func;
- static assert(pstc.length == 3); // three parameters
+ static assert(pstc.length == 4); // number of parameters
static assert(pstc[0] == STC.ref_);
static assert(pstc[1] == STC.out_);
- static assert(pstc[2] == STC.none);
+ version (none)
+ {
+ // TODO: When the DMD PR (dlang/dmd#11474) gets merged,
+ // remove the versioning and the second test
+ static assert(pstc[2] == STC.in_);
+ // This is the current behavior, before `in` is fixed to not be an alias
+ static assert(pstc[2] == STC.scope_);
+ }
+ static assert(pstc[3] == STC.none);
}
-/*****************
- * Convert string tuple Attribs to ParameterStorageClass bits
- * Params:
- * Attribs = string tuple
- * Returns:
- * ParameterStorageClass bits
+/**
+Convert the result of `__traits(getParameterStorageClasses)`
+to $(LREF ParameterStorageClass) `enum`s.
+
+Params:
+ Attribs = The return value of `__traits(getParameterStorageClasses)`
+Returns:
+ The bitwise OR of the equivalent $(LREF ParameterStorageClass) `enum`s.
*/
template extractParameterStorageClassFlags(Attribs...)
{
@@ -1097,11 +1209,12 @@ template extractParameterStorageClassFlags(Attribs...)
auto result = ParameterStorageClass.none;
static if (Attribs.length > 0)
{
- foreach (attrib; [Attribs])
+ static foreach (attrib; Attribs)
{
final switch (attrib) with (ParameterStorageClass)
{
case "scope": result |= scope_; break;
+ case "in": result |= in_; break;
case "out": result |= out_; break;
case "ref": result |= ref_; break;
case "lazy": result |= lazy_; break;
@@ -1118,6 +1231,28 @@ template extractParameterStorageClassFlags(Attribs...)
}();
}
+///
+@safe unittest
+{
+ static void func(ref int ctx, out real result);
+
+ enum param1 = extractParameterStorageClassFlags!(
+ __traits(getParameterStorageClasses, func, 0)
+ );
+ static assert(param1 == ParameterStorageClass.ref_);
+
+ enum param2 = extractParameterStorageClassFlags!(
+ __traits(getParameterStorageClasses, func, 1)
+ );
+ static assert(param2 == ParameterStorageClass.out_);
+
+ enum param3 = extractParameterStorageClassFlags!(
+ __traits(getParameterStorageClasses, func, 0),
+ __traits(getParameterStorageClasses, func, 1)
+ );
+ static assert(param3 == (ParameterStorageClass.ref_ | ParameterStorageClass.out_));
+}
+
@safe unittest
{
alias STC = ParameterStorageClass;
@@ -1154,14 +1289,14 @@ template extractParameterStorageClassFlags(Attribs...)
static assert(dglit_pstc.length == 1);
static assert(dglit_pstc[0] == STC.ref_);
- // Bugzilla 9317
+ // https://issues.dlang.org/show_bug.cgi?id=9317
static inout(int) func(inout int param) { return param; }
static assert(ParameterStorageClassTuple!(typeof(func))[0] == STC.none);
}
@safe unittest
{
- // Bugzilla 14253
+ // https://issues.dlang.org/show_bug.cgi?id=14253
static struct Foo {
ref Foo opAssign(ref Foo rhs) return { return this; }
}
@@ -1174,7 +1309,7 @@ template extractParameterStorageClassFlags(Attribs...)
Get, as a tuple, the identifiers of the parameters to a function symbol.
*/
template ParameterIdentifierTuple(func...)
- if (func.length == 1 && isCallable!func)
+if (func.length == 1 && isCallable!func)
{
static if (is(FunctionTypeOf!func PT == __parameters))
{
@@ -1182,7 +1317,9 @@ template ParameterIdentifierTuple(func...)
{
static if (!isFunctionPointer!func && !isDelegate!func
// Unnamed parameters yield CT error.
- && is(typeof(__traits(identifier, PT[i .. i+1]))))
+ && is(typeof(__traits(identifier, PT[i .. i+1])))
+ // Filter out unnamed args, which look like (Type) instead of (Type name).
+ && PT[i].stringof != PT[i .. i+1].stringof[1..$-1])
{
enum Get = __traits(identifier, PT[i .. i+1]);
}
@@ -1194,7 +1331,7 @@ template ParameterIdentifierTuple(func...)
}
else
{
- static assert(0, func[0].stringof ~ "is not a function");
+ static assert(0, func[0].stringof ~ " is not a function");
// Define dummy entities to avoid pointless errors
template Get(size_t i) { enum Get = ""; }
@@ -1219,6 +1356,16 @@ template ParameterIdentifierTuple(func...)
static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]);
}
+// https://issues.dlang.org/show_bug.cgi?id=19456
+@safe unittest
+{
+ struct SomeType {}
+ void foo(SomeType);
+ void bar(int);
+ static assert([ParameterIdentifierTuple!foo] == [""]);
+ static assert([ParameterIdentifierTuple!bar] == [""]);
+}
+
@safe unittest
{
alias PIT = ParameterIdentifierTuple;
@@ -1258,10 +1405,10 @@ template ParameterIdentifierTuple(func...)
/**
Get, as a tuple, the default value of the parameters to a function symbol.
-If a parameter doesn't have the default value, $(D void) is returned instead.
+If a parameter doesn't have the default value, `void` is returned instead.
*/
template ParameterDefaults(func...)
- if (func.length == 1 && isCallable!func)
+if (func.length == 1 && isCallable!func)
{
alias param_names = ParameterIdentifierTuple!func;
static if (is(FunctionTypeOf!(func[0]) PT == __parameters))
@@ -1285,7 +1432,6 @@ template ParameterDefaults(func...)
// like this.
auto " ~ val ~ " = " ~ args ~ "[0];
auto " ~ ptr ~ " = &" ~ val ~ ";
- // workaround Bugzilla 16582
return *" ~ ptr ~ ";
};
");
@@ -1298,7 +1444,7 @@ template ParameterDefaults(func...)
}
else
{
- static assert(0, func[0].stringof ~ "is not a function");
+ static assert(0, func[0].stringof ~ " is not a function");
// Define dummy entities to avoid pointless errors
template Get(size_t i) { enum Get = ""; }
@@ -1326,7 +1472,8 @@ template ParameterDefaults(func...)
static assert( ParameterDefaults!foo[3] == 0);
}
-@safe unittest // issue 17192
+// https://issues.dlang.org/show_bug.cgi?id=17192
+@safe unittest
{
static void func(int i, int PT, int __pd_value, int __pd_val, int __args,
int name, int args, int val, int ptr, int args_, int val_, int ptr_)
@@ -1334,7 +1481,7 @@ template ParameterDefaults(func...)
}
alias Voids = ParameterDefaults!func;
static assert(Voids.length == 12);
- foreach (V; Voids) static assert(is(V == void));
+ static foreach (V; Voids) static assert(is(V == void));
}
/**
@@ -1359,7 +1506,8 @@ alias ParameterDefaultValueTuple = ParameterDefaults;
static assert( PDVT!baz[2] == "hello");
static assert(is(typeof(PDVT!baz) == typeof(AliasSeq!(void, 1, "hello"))));
- // bug 10800 - property functions return empty string
+ // property functions return empty string
+ // https://issues.dlang.org/show_bug.cgi?id=10800
@property void foo(int x = 3) { }
static assert(PDVT!foo.length == 1);
static assert(PDVT!foo[0] == 3);
@@ -1371,16 +1519,18 @@ alias ParameterDefaultValueTuple = ParameterDefaults;
static immutable Colour white = Colour(255,255,255,255);
}
+ // https://issues.dlang.org/show_bug.cgi?id=8106
void bug8106(Colour c = Colour.white) {}
//pragma(msg, PDVT!bug8106);
static assert(PDVT!bug8106[0] == Colour.white);
+ // https://issues.dlang.org/show_bug.cgi?id=16582
void bug16582(scope int* val = null) {}
static assert(PDVT!bug16582[0] is null);
}
/**
-Returns the FunctionAttribute mask for function $(D func).
+Returns the FunctionAttribute mask for function `func`.
See_Also:
$(LREF hasFunctionAttributes)
@@ -1405,11 +1555,12 @@ enum FunctionAttribute : uint
shared_ = 1 << 11, /// ditto
return_ = 1 << 12, /// ditto
scope_ = 1 << 13, /// ditto
+ live = 1 << 14, /// ditto
}
/// ditto
template functionAttributes(func...)
- if (func.length == 1 && isCallable!func)
+if (func.length == 1 && isCallable!func)
{
// @bug: workaround for opCall
alias FuncSym = Select!(is(typeof(__traits(getFunctionAttributes, func))),
@@ -1422,8 +1573,6 @@ template functionAttributes(func...)
///
@safe unittest
{
- import std.traits : functionAttributes, FunctionAttribute;
-
alias FA = FunctionAttribute; // shorten the enum name
real func(real x) pure nothrow @safe
@@ -1458,6 +1607,8 @@ template functionAttributes(func...)
int safeF() @safe { return 0; }
int pureF() pure { return 0; }
+
+ int liveF() @live { return 0; }
}
static assert(functionAttributes!(S.noF) == FA.system);
@@ -1499,6 +1650,9 @@ template functionAttributes(func...)
static assert(functionAttributes!(S.pureF) == (FA.pure_ | FA.system));
static assert(functionAttributes!(typeof(S.pureF)) == (FA.pure_ | FA.system));
+ static assert(functionAttributes!(S.liveF) == (FA.live | FA.system));
+ static assert(functionAttributes!(typeof(S.liveF)) == (FA.live | FA.system));
+
int pure_nothrow() nothrow pure;
void safe_nothrow() @safe nothrow;
static ref int static_ref_property() @property;
@@ -1548,7 +1702,7 @@ private FunctionAttribute extractAttribFlags(Attribs...)()
{
auto res = FunctionAttribute.none;
- foreach (attrib; Attribs)
+ static foreach (attrib; Attribs)
{
switch (attrib) with (FunctionAttribute)
{
@@ -1566,6 +1720,7 @@ private FunctionAttribute extractAttribFlags(Attribs...)()
case "shared": res |= shared_; break;
case "return": res |= return_; break;
case "scope": res |= scope_; break;
+ case "@live": res |= live; break;
default: assert(0, attrib);
}
}
@@ -1587,14 +1742,14 @@ See_Also:
$(LREF functionAttributes)
*/
template hasFunctionAttributes(args...)
- if (args.length > 0 && isCallable!(args[0])
- && allSatisfy!(isSomeString, typeof(args[1 .. $])))
+if (args.length > 0 && isCallable!(args[0])
+ && allSatisfy!(isSomeString, typeof(args[1 .. $])))
{
enum bool hasFunctionAttributes = {
import std.algorithm.searching : canFind;
import std.range : only;
enum funcAttribs = only(__traits(getFunctionAttributes, args[0]));
- foreach (attribute; args[1 .. $])
+ static foreach (attribute; args[1 .. $])
{
if (!funcAttribs.canFind(attribute))
return false;
@@ -1639,6 +1794,8 @@ template hasFunctionAttributes(args...)
int safeF() @safe;
int pureF() pure;
+
+ int liveF() @live;
}
// true if no args passed
@@ -1696,6 +1853,10 @@ template hasFunctionAttributes(args...)
static assert(hasFunctionAttributes!(typeof(S.pureF), "pure", "@system"));
static assert(!hasFunctionAttributes!(S.pureF, "pure", "@system", "ref"));
+ static assert(hasFunctionAttributes!(S.liveF, "@live", "@system"));
+ static assert(hasFunctionAttributes!(typeof(S.liveF), "@live", "@system"));
+ static assert(!hasFunctionAttributes!(S.liveF, "@live", "@system", "ref"));
+
int pure_nothrow() nothrow pure { return 0; }
void safe_nothrow() @safe nothrow { }
static ref int static_ref_property() @property { return *(new int); }
@@ -1768,10 +1929,10 @@ template hasFunctionAttributes(args...)
}
/**
-$(D true) if $(D func) is $(D @safe) or $(D @trusted).
+`true` if `func` is `@safe` or `@trusted`.
*/
template isSafe(alias func)
- if (isCallable!func)
+if (isCallable!func)
{
enum isSafe = (functionAttributes!func & FunctionAttribute.safe) != 0 ||
(functionAttributes!func & FunctionAttribute.trusted) != 0;
@@ -1804,9 +1965,9 @@ template isSafe(alias func)
static assert(!isSafe!(Set.systemF));
//Functions
- @safe static safeFunc() {}
- @trusted static trustedFunc() {}
- @system static systemFunc() {}
+ @safe static void safeFunc() {}
+ @trusted static void trustedFunc() {}
+ @system static void systemFunc() {}
static assert( isSafe!safeFunc);
static assert( isSafe!trustedFunc);
@@ -1847,7 +2008,7 @@ template isSafe(alias func)
/**
-$(D true) if $(D func) is $(D @system).
+`true` if `func` is `@system`.
*/
template isUnsafe(alias func)
{
@@ -1880,9 +2041,9 @@ template isUnsafe(alias func)
static assert( isUnsafe!(Set.systemF));
//Functions
- @safe static safeFunc() {}
- @trusted static trustedFunc() {}
- @system static systemFunc() {}
+ @safe static void safeFunc() {}
+ @trusted static void trustedFunc() {}
+ @system static void systemFunc() {}
static assert(!isUnsafe!safeFunc);
static assert(!isUnsafe!trustedFunc);
@@ -1927,10 +2088,10 @@ Determine the linkage attribute of the function.
Params:
func = the function symbol, or the type of a function, delegate, or pointer to function
Returns:
- one of the strings "D", "C", "Windows", or "Objective-C"
+ one of the strings "D", "C", "C++", "Windows", "Objective-C", or "System".
*/
template functionLinkage(func...)
- if (func.length == 1 && isCallable!func)
+if (func.length == 1 && isCallable!func)
{
enum string functionLinkage = __traits(getLinkage, FunctionTypeOf!func);
}
@@ -1974,17 +2135,21 @@ Returns:
*/
enum Variadic
{
- no, /// Function is not variadic.
- c, /// Function is a _C-style variadic function, which uses
- /// core.stdc.stdarg
- /// Function is a _D-style variadic function, which uses
- d, /// __argptr and __arguments.
- typesafe, /// Function is a typesafe variadic function.
+ /// Function is not variadic.
+ no,
+ /// Function is a _C-style variadic function, which uses
+ /// `core.stdc.stdarg`
+ c,
+ /// Function is a _D-style variadic function, which uses
+ /// `__argptr` and `__arguments`.
+ d,
+ /// Function is a typesafe variadic function.
+ typesafe,
}
/// ditto
template variadicFunctionStyle(func...)
- if (func.length == 1 && isCallable!func)
+if (func.length == 1 && isCallable!func)
{
enum string varargs = __traits(getFunctionVariadicStyle, FunctionTypeOf!func);
enum Variadic variadicFunctionStyle =
@@ -2023,28 +2188,31 @@ template variadicFunctionStyle(func...)
/**
-Get the function type from a callable object $(D func).
+Get the function type from a callable object `func`.
-Using builtin $(D typeof) on a property function yields the types of the
+Using builtin `typeof` on a property function yields the types of the
property value, not of the property function itself. Still,
-$(D FunctionTypeOf) is able to obtain function types of properties.
+`FunctionTypeOf` is able to obtain function types of properties.
Note:
Do not confuse function types with function pointer types; function types are
usually used for compile-time reflection purposes.
*/
template FunctionTypeOf(func...)
- if (func.length == 1 && isCallable!func)
+if (func.length == 1 && isCallable!func)
{
- static if (is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function) || is(typeof(& func[0]) Fsym == delegate))
+ static if ((is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function)) || is(typeof(& func[0]) Fsym == delegate))
{
alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol
}
- else static if (is(typeof(& func[0].opCall) Fobj == delegate))
+ else static if (is(typeof(& func[0].opCall) Fobj == delegate) || is(typeof(& func[0].opCall!()) Fobj == delegate))
{
alias FunctionTypeOf = Fobj; // HIT: callable object
}
- else static if (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function))
+ else static if (
+ (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function)) ||
+ (is(typeof(& func[0].opCall!()) Ftyp : Ftyp*) && is(Ftyp == function))
+ )
{
alias FunctionTypeOf = Ftyp; // HIT: callable type
}
@@ -2105,6 +2273,13 @@ template FunctionTypeOf(func...)
static assert(is( FunctionTypeOf!stcall_val == typeof(test) ));
static assert(is( FunctionTypeOf!stcall_ptr == typeof(test) ));
+ struct TemplatedOpCallF { int opCall()(int) { return 0; } }
+ static assert(is( FunctionTypeOf!TemplatedOpCallF == typeof(TemplatedOpCallF.opCall!()) ));
+
+ int foovar;
+ struct TemplatedOpCallDg { int opCall()() { return foovar; } }
+ static assert(is( FunctionTypeOf!TemplatedOpCallDg == typeof(TemplatedOpCallDg.opCall!()) ));
+
interface Overloads
{
void test(string);
@@ -2112,7 +2287,7 @@ template FunctionTypeOf(func...)
int test(int);
int test() @property;
}
- alias ov = AliasSeq!(__traits(getVirtualFunctions, Overloads, "test"));
+ alias ov = __traits(getVirtualFunctions, Overloads, "test");
alias F_ov0 = FunctionTypeOf!(ov[0]);
alias F_ov1 = FunctionTypeOf!(ov[1]);
alias F_ov2 = FunctionTypeOf!(ov[2]);
@@ -2139,7 +2314,7 @@ template FunctionTypeOf(func...)
* attrs = The desired $(LREF FunctionAttribute)s of the result type.
*/
template SetFunctionAttributes(T, string linkage, uint attrs)
- if (isFunctionPointer!T || isDelegate!T)
+if (isFunctionPointer!T || isDelegate!T)
{
mixin({
import std.algorithm.searching : canFind;
@@ -2206,6 +2381,8 @@ template SetFunctionAttributes(T, string linkage, uint attrs)
result ~= " shared";
static if (attrs & FunctionAttribute.return_)
result ~= " return";
+ static if (attrs & FunctionAttribute.live)
+ result ~= " @live";
result ~= " SetFunctionAttributes;";
return result;
@@ -2214,7 +2391,7 @@ template SetFunctionAttributes(T, string linkage, uint attrs)
/// Ditto
template SetFunctionAttributes(T, string linkage, uint attrs)
- if (is(T == function))
+if (is(T == function))
{
// To avoid a lot of syntactic headaches, we just use the above version to
// operate on the corresponding function pointer type and then remove the
@@ -2228,15 +2405,29 @@ template SetFunctionAttributes(T, string linkage, uint attrs)
alias ExternC(T) = SetFunctionAttributes!(T, "C", functionAttributes!T);
auto assumePure(T)(T t)
- if (isFunctionPointer!T || isDelegate!T)
+ if (isFunctionPointer!T || isDelegate!T)
{
enum attrs = functionAttributes!T | FunctionAttribute.pure_;
return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
}
+
+ int f()
+ {
+ import core.thread : getpid;
+ return getpid();
+ }
+
+ int g() pure @trusted
+ {
+ auto pureF = assumePure(&f);
+ return pureF();
+ }
+ assert(g() > 0);
}
-version (unittest)
+version (StdUnittest)
{
+private:
// Some function types to test.
int sc(scope int, ref int, out int, lazy int, int);
extern(System) int novar();
@@ -2249,11 +2440,11 @@ version (unittest)
import std.algorithm.iteration : reduce;
alias FA = FunctionAttribute;
- foreach (BaseT; AliasSeq!(typeof(&sc), typeof(&novar), typeof(&cstyle),
+ static foreach (BaseT; AliasSeq!(typeof(&sc), typeof(&novar), typeof(&cstyle),
typeof(&dstyle), typeof(&typesafe)))
{
- foreach (T; AliasSeq!(BaseT, FunctionTypeOf!BaseT))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ static foreach (T; AliasSeq!(BaseT, FunctionTypeOf!BaseT))
+ {{
enum linkage = functionLinkage!T;
enum attrs = functionAttributes!T;
@@ -2263,13 +2454,13 @@ version (unittest)
// Check that all linkage types work (D-style variadics require D linkage).
static if (variadicFunctionStyle!T != Variadic.d)
{
- foreach (newLinkage; AliasSeq!("D", "C", "Windows", "C++"))
- {
+ static foreach (newLinkage; AliasSeq!("D", "C", "Windows", "C++"))
+ {{
alias New = SetFunctionAttributes!(T, newLinkage, attrs);
static assert(functionLinkage!New == newLinkage,
"Linkage test failed for: " ~ T.stringof ~ ", " ~ newLinkage ~
" (got " ~ New.stringof ~ ")");
- }
+ }}
}
// Add @safe.
@@ -2287,7 +2478,7 @@ version (unittest)
// Strip all attributes again.
alias T3 = SetFunctionAttributes!(T2, functionLinkage!T, FA.none);
static assert(is(T3 == T));
- }();
+ }}
}
}
@@ -2309,13 +2500,22 @@ Returns:
`false` otherwise
*/
template isInnerClass(T)
- if (is(T == class))
+if (is(T == class))
{
- import std.meta : staticIndexOf;
-
static if (is(typeof(T.outer)))
- enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T))
- && (staticIndexOf!(__traits(allMembers, T), "outer") == -1);
+ {
+ bool hasOuterMember(string[] members...)
+ {
+ foreach (m; members)
+ {
+ if (m == "outer")
+ return true;
+ }
+ return false;
+ }
+ enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) &&
+ !hasOuterMember(__traits(allMembers, T));
+ }
else
enum isInnerClass = false;
}
@@ -2351,11 +2551,11 @@ template isInnerClass(T)
}
/**
-Determines whether $(D T) has its own context pointer.
-$(D T) must be either $(D class), $(D struct), or $(D union).
+Determines whether `T` has its own context pointer.
+`T` must be either `class`, `struct`, or `union`.
*/
template isNested(T)
- if (is(T == class) || is(T == struct) || is(T == union))
+if (is(T == class) || is(T == struct) || is(T == union))
{
enum isNested = __traits(isNested, T);
}
@@ -2372,19 +2572,19 @@ template isNested(T)
}
/**
-Determines whether $(D T) or any of its representation types
+Determines whether `T` or any of its representation types
have a context pointer.
*/
template hasNested(T)
{
- import std.meta : anySatisfy, Filter;
+ import std.meta : Filter;
static if (isStaticArray!T && T.length)
enum hasNested = hasNested!(typeof(T.init[0]));
else static if (is(T == class) || is(T == struct) || is(T == union))
{
// prevent infinite recursion for class with member of same type
- enum notSame(U) = !is(Unqual!T == Unqual!U);
+ enum notSame(U) = !is(immutable T == immutable U);
enum hasNested = isNested!T ||
anySatisfy!(.hasNested, Filter!(notSame, Fields!T));
}
@@ -2467,14 +2667,17 @@ template hasNested(T)
* This consists of the fields that take up memory space,
* excluding the hidden fields like the virtual function
* table pointer or a context pointer for nested types.
- * If $(D T) isn't a struct, class, or union returns a tuple
- * with one element $(D T).
+ * If `T` isn't a struct, class, interface or union returns a tuple
+ * with one element `T`.
+ *
+ * History:
+ * - Returned `AliasSeq!(Interface)` for interfaces prior to 2.097
*/
template Fields(T)
{
static if (is(T == struct) || is(T == union))
alias Fields = typeof(T.tupleof[0 .. $ - isNested!T]);
- else static if (is(T == class))
+ else static if (is(T == class) || is(T == interface))
alias Fields = typeof(T.tupleof);
else
alias Fields = AliasSeq!T;
@@ -2483,6 +2686,7 @@ template Fields(T)
///
@safe unittest
{
+ import std.meta : AliasSeq;
struct S { int x; float y; }
static assert(is(Fields!S == AliasSeq!(int, float)));
}
@@ -2512,8 +2716,10 @@ alias FieldTypeTuple = Fields;
class NestedClass { int a; void f() { ++i; } }
static assert(is(FieldTypeTuple!NestedClass == AliasSeq!int));
-}
+ static interface I {}
+ static assert(is(Fields!I == AliasSeq!()));
+}
//Required for FieldNameTuple
private enum NameOf(alias T) = T.stringof;
@@ -2522,15 +2728,20 @@ private enum NameOf(alias T) = T.stringof;
* Get as an expression tuple the names of the fields of a struct, class, or
* union. This consists of the fields that take up memory space, excluding the
* hidden fields like the virtual function table pointer or a context pointer
- * for nested types. If $(D T) isn't a struct, class, or union returns an
- * expression tuple with an empty string.
+ * for nested types.
+ * Inherited fields (for classes) are not included.
+ * If `T` isn't a struct, class, interface or union, an
+ * expression tuple with an empty string is returned.
+ *
+ * History:
+ * - Returned `AliasSeq!""` for interfaces prior to 2.097
*/
template FieldNameTuple(T)
{
import std.meta : staticMap;
static if (is(T == struct) || is(T == union))
alias FieldNameTuple = staticMap!(NameOf, T.tupleof[0 .. $ - isNested!T]);
- else static if (is(T == class))
+ else static if (is(T == class) || is(T == interface))
alias FieldNameTuple = staticMap!(NameOf, T.tupleof);
else
alias FieldNameTuple = AliasSeq!"";
@@ -2539,6 +2750,7 @@ template FieldNameTuple(T)
///
@safe unittest
{
+ import std.meta : AliasSeq;
struct S { int x; float y; }
static assert(FieldNameTuple!S == AliasSeq!("x", "y"));
static assert(FieldNameTuple!int == AliasSeq!"");
@@ -2554,6 +2766,15 @@ template FieldNameTuple(T)
static struct StaticStruct2 { int a, b; }
static assert(FieldNameTuple!StaticStruct2 == AliasSeq!("a", "b"));
+ static class StaticClass1 { }
+ static assert(is(FieldNameTuple!StaticClass1 == AliasSeq!()));
+
+ static class StaticClass2 : StaticClass1 { int a, b; }
+ static assert(FieldNameTuple!StaticClass2 == AliasSeq!("a", "b"));
+
+ static class StaticClass3 : StaticClass2 { int c; }
+ static assert(FieldNameTuple!StaticClass3 == AliasSeq!("c"));
+
int i;
struct NestedStruct1 { void f() { ++i; } }
@@ -2564,6 +2785,9 @@ template FieldNameTuple(T)
class NestedClass { int a; void f() { ++i; } }
static assert(FieldNameTuple!NestedClass == AliasSeq!"a");
+
+ interface I {}
+ static assert(FieldNameTuple!I == AliasSeq!());
}
@@ -2573,41 +2797,13 @@ topological order.
*/
template RepresentationTypeTuple(T)
{
- template Impl(T...)
- {
- static if (T.length == 0)
- {
- alias Impl = AliasSeq!();
- }
- else
- {
- import std.typecons : Rebindable;
-
- static if (is(T[0] R: Rebindable!R))
- {
- alias Impl = Impl!(Impl!R, T[1 .. $]);
- }
- else static if (is(T[0] == struct) || is(T[0] == union))
- {
- // @@@BUG@@@ this should work
- //alias .RepresentationTypes!(T[0].tupleof)
- // RepresentationTypes;
- alias Impl = Impl!(FieldTypeTuple!(T[0]), T[1 .. $]);
- }
- else
- {
- alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $]));
- }
- }
- }
-
static if (is(T == struct) || is(T == union) || is(T == class))
{
- alias RepresentationTypeTuple = Impl!(FieldTypeTuple!T);
+ alias RepresentationTypeTuple = staticMapMeta!(RepresentationTypeTupleImpl, FieldTypeTuple!T);
}
else
{
- alias RepresentationTypeTuple = Impl!T;
+ alias RepresentationTypeTuple = RepresentationTypeTupleImpl!T;
}
}
@@ -2645,7 +2841,7 @@ template RepresentationTypeTuple(T)
alias R1 = RepresentationTypeTuple!C;
static assert(R1.length == 2 && is(R1[0] == int) && is(R1[1] == float));
- /* Issue 6642 */
+ /* https://issues.dlang.org/show_bug.cgi?id=6642 */
import std.typecons : Rebindable;
struct S5 { int a; Rebindable!(immutable Object) b; }
@@ -2653,39 +2849,58 @@ template RepresentationTypeTuple(T)
static assert(R2.length == 2 && is(R2[0] == int) && is(R2[1] == immutable(Object)));
}
-/*
-Statically evaluates to $(D true) if and only if $(D T)'s
-representation contains at least one field of pointer or array type.
-Members of class types are not considered raw pointers. Pointers to
-immutable objects are not considered raw aliasing.
-*/
-private template hasRawAliasing(T...)
+@safe unittest
{
- template Impl(T...)
+ struct VeryLargeType
{
- static if (T.length == 0)
+ import std.format : format;
+ import std.range : iota;
+
+ static foreach (i; 500.iota)
{
- enum Impl = false;
+ mixin(format!"int v%s;"(i));
}
- else
- {
- static if (is(T[0] foo : U*, U) && !isFunctionPointer!(T[0]))
- enum has = !is(U == immutable);
- else static if (is(T[0] foo : U[], U) && !isStaticArray!(T[0]))
- enum has = !is(U == immutable);
- else static if (isAssociativeArray!(T[0]))
- enum has = !is(T[0] == immutable);
- else
- enum has = false;
+ }
- enum Impl = has || Impl!(T[1 .. $]);
- }
+ alias BigList = RepresentationTypeTuple!VeryLargeType;
+}
+
+private template RepresentationTypeTupleImpl(T)
+{
+ import std.typecons : Rebindable;
+
+ static if (is(T R: Rebindable!R))
+ {
+ alias RepresentationTypeTupleImpl
+ = staticMapMeta!(.RepresentationTypeTupleImpl, RepresentationTypeTupleImpl!R);
+ }
+ else static if (is(T == struct) || is(T == union))
+ {
+ // @@@BUG@@@ this should work
+ //alias .RepresentationTypes!(T[0].tupleof)
+ // RepresentationTypes;
+ alias RepresentationTypeTupleImpl
+ = staticMapMeta!(.RepresentationTypeTupleImpl, FieldTypeTuple!(T));
+ }
+ else
+ {
+ alias RepresentationTypeTupleImpl
+ = AliasSeq!T;
}
+}
- enum hasRawAliasing = Impl!(RepresentationTypeTuple!T);
+/*
+Statically evaluates to `true` if and only if `T`'s
+representation contains at least one field of pointer or array type.
+Members of class types are not considered raw pointers. Pointers to
+immutable objects are not considered raw aliasing.
+*/
+private template hasRawAliasing(T)
+{
+ enum hasRawAliasing = anySatisfy!(hasRawAliasingImpl, RepresentationTypeTuple!T);
}
-///
+//
@safe unittest
{
// simple types
@@ -2703,6 +2918,16 @@ private template hasRawAliasing(T...)
static assert(!hasRawAliasing!S2);
}
+// https://issues.dlang.org/show_bug.cgi?id=19228
+@safe unittest
+{
+ static struct C
+ {
+ int*[1] a;
+ }
+ static assert(hasRawAliasing!C);
+}
+
@safe unittest
{
// struct with a pointer member
@@ -2749,39 +2974,36 @@ private template hasRawAliasing(T...)
static assert(!hasRawAliasing!(immutable(int[string])));
}
+private template hasRawAliasingImpl(T)
+{
+ static if (is(T foo : U*, U) && !isFunctionPointer!T)
+ enum hasRawAliasingImpl = !is(U == immutable);
+ else static if (is(T foo : U[N], U, size_t N))
+ // separate static ifs to avoid forward reference
+ static if (is(U == class) || is(U == interface))
+ enum hasRawAliasingImpl = false;
+ else
+ enum hasRawAliasingImpl = hasRawAliasingImpl!U;
+ else static if (is(T foo : U[], U) && !isStaticArray!(T))
+ enum hasRawAliasingImpl = !is(U == immutable);
+ else static if (isAssociativeArray!(T))
+ enum hasRawAliasingImpl = !is(T == immutable);
+ else
+ enum hasRawAliasingImpl = false;
+}
+
/*
-Statically evaluates to $(D true) if and only if $(D T)'s
+Statically evaluates to `true` if and only if `T`'s
representation contains at least one non-shared field of pointer or
array type. Members of class types are not considered raw pointers.
Pointers to immutable objects are not considered raw aliasing.
*/
-private template hasRawUnsharedAliasing(T...)
+private template hasRawUnsharedAliasing(T)
{
- template Impl(T...)
- {
- static if (T.length == 0)
- {
- enum Impl = false;
- }
- else
- {
- static if (is(T[0] foo : U*, U) && !isFunctionPointer!(T[0]))
- enum has = !is(U == immutable) && !is(U == shared);
- else static if (is(T[0] foo : U[], U) && !isStaticArray!(T[0]))
- enum has = !is(U == immutable) && !is(U == shared);
- else static if (isAssociativeArray!(T[0]))
- enum has = !is(T[0] == immutable) && !is(T[0] == shared);
- else
- enum has = false;
-
- enum Impl = has || Impl!(T[1 .. $]);
- }
- }
-
- enum hasRawUnsharedAliasing = Impl!(RepresentationTypeTuple!T);
+ enum hasRawUnsharedAliasing = anySatisfy!(hasRawUnsharedAliasingImpl, RepresentationTypeTuple!T);
}
-///
+//
@safe unittest
{
// simple types
@@ -2931,81 +3153,64 @@ private template hasRawUnsharedAliasing(T...)
static assert(!hasRawUnsharedAliasing!S28);
}
+private template hasRawUnsharedAliasingImpl(T)
+{
+ static if (is(T foo : U*, U) && !isFunctionPointer!T)
+ enum hasRawUnsharedAliasingImpl = !is(U == immutable) && !is(U == shared);
+ else static if (is(T foo : U[], U) && !isStaticArray!T)
+ enum hasRawUnsharedAliasingImpl = !is(U == immutable) && !is(U == shared);
+ else static if (isAssociativeArray!T)
+ enum hasRawUnsharedAliasingImpl = !is(T == immutable) && !is(T == shared);
+ else
+ enum hasRawUnsharedAliasingImpl = false;
+}
+
/*
-Statically evaluates to $(D true) if and only if $(D T)'s
+Statically evaluates to `true` if and only if `T`'s
representation includes at least one non-immutable object reference.
*/
-private template hasObjects(T...)
+private template hasObjects(T)
{
- static if (T.length == 0)
- {
- enum hasObjects = false;
- }
- else static if (is(T[0] == struct))
+ static if (is(T == struct))
{
- enum hasObjects = hasObjects!(
- RepresentationTypeTuple!(T[0]), T[1 .. $]);
+ enum hasObjects = anySatisfy!(.hasObjects, RepresentationTypeTuple!T);
}
else
{
- enum hasObjects = ((is(T[0] == class) || is(T[0] == interface))
- && !is(T[0] == immutable)) || hasObjects!(T[1 .. $]);
+ enum hasObjects = (is(T == class) || is(T == interface)) && !is(T == immutable);
}
}
/*
-Statically evaluates to $(D true) if and only if $(D T)'s
+Statically evaluates to `true` if and only if `T`'s
representation includes at least one non-immutable non-shared object
reference.
*/
-private template hasUnsharedObjects(T...)
+private template hasUnsharedObjects(T)
{
- static if (T.length == 0)
- {
- enum hasUnsharedObjects = false;
- }
- else static if (is(T[0] == struct))
+ static if (is(T == struct))
{
- enum hasUnsharedObjects = hasUnsharedObjects!(
- RepresentationTypeTuple!(T[0]), T[1 .. $]);
+ enum hasUnsharedObjects = anySatisfy!(.hasUnsharedObjects, RepresentationTypeTuple!T);
}
else
{
- enum hasUnsharedObjects = ((is(T[0] == class) || is(T[0] == interface)) &&
- !is(T[0] == immutable) && !is(T[0] == shared)) ||
- hasUnsharedObjects!(T[1 .. $]);
+ enum hasUnsharedObjects = (is(T == class) || is(T == interface)) &&
+ !is(T == immutable) && !is(T == shared);
}
}
/**
-Returns $(D true) if and only if $(D T)'s representation includes at
-least one of the following: $(OL $(LI a raw pointer $(D U*) and $(D U)
-is not immutable;) $(LI an array $(D U[]) and $(D U) is not
-immutable;) $(LI a reference to a class or interface type $(D C) and $(D C) is
+Returns `true` if and only if `T`'s representation includes at
+least one of the following: $(OL $(LI a raw pointer `U*` and `U`
+is not immutable;) $(LI an array `U[]` and `U` is not
+immutable;) $(LI a reference to a class or interface type `C` and `C` is
not immutable.) $(LI an associative array that is not immutable.)
$(LI a delegate.))
*/
template hasAliasing(T...)
{
- import std.meta : anySatisfy;
- import std.typecons : Rebindable;
-
- static if (T.length && is(T[0] : Rebindable!R, R))
- {
- enum hasAliasing = hasAliasing!(R, T[1 .. $]);
- }
- else
- {
- template isAliasingDelegate(T)
- {
- enum isAliasingDelegate = isDelegate!T
- && !is(T == immutable)
- && !is(FunctionTypeOf!T == immutable);
- }
- enum hasAliasing = hasRawAliasing!T || hasObjects!T ||
- anySatisfy!(isAliasingDelegate, T, RepresentationTypeTuple!T);
- }
+ enum hasAliasing = anySatisfy!(hasAliasingImpl, T);
}
///
@@ -3083,18 +3288,44 @@ template hasAliasing(T...)
static assert( hasAliasing!S12);
static assert( hasAliasing!S13);
static assert(!hasAliasing!S14);
+
+ class S15 { S15[1] a; }
+ static assert( hasAliasing!S15);
+ static assert(!hasAliasing!(immutable(S15)));
+}
+
+private template hasAliasingImpl(T)
+{
+ import std.typecons : Rebindable;
+
+ static if (is(T : Rebindable!R, R))
+ {
+ enum hasAliasingImpl = hasAliasingImpl!R;
+ }
+ else
+ {
+ template isAliasingDelegate(T)
+ {
+ enum isAliasingDelegate = isDelegate!T
+ && !is(T == immutable)
+ && !is(FunctionTypeOf!T == immutable);
+ }
+ enum hasAliasingImpl = hasRawAliasing!T || hasObjects!T ||
+ anySatisfy!(isAliasingDelegate, T, RepresentationTypeTuple!T);
+ }
}
+
/**
-Returns $(D true) if and only if $(D T)'s representation includes at
-least one of the following: $(OL $(LI a raw pointer $(D U*);) $(LI an
-array $(D U[]);) $(LI a reference to a class type $(D C).)
-$(LI an associative array.) $(LI a delegate.))
+Returns `true` if and only if `T`'s representation includes at
+least one of the following: $(OL $(LI a raw pointer `U*`;) $(LI an
+array `U[]`;) $(LI a reference to a class type `C`;)
+$(LI an associative array;) $(LI a delegate;)
+$(LI a [context pointer][isNested].))
*/
template hasIndirections(T)
{
- import std.meta : anySatisfy;
static if (is(T == struct) || is(T == union))
- enum hasIndirections = anySatisfy!(.hasIndirections, FieldTypeTuple!T);
+ enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof));
else static if (isStaticArray!T && is(T : E[N], E, size_t N))
enum hasIndirections = is(E == void) ? true : hasIndirections!E;
else static if (isFunctionPointer!T)
@@ -3175,9 +3406,13 @@ template hasIndirections(T)
static assert( hasIndirections!S24);
static assert( hasIndirections!S25);
static assert( hasIndirections!S26);
+ int local;
+ struct HasContextPointer { int opCall() { return ++local; } }
+ static assert(hasIndirections!HasContextPointer);
}
-@safe unittest //12000
+// https://issues.dlang.org/show_bug.cgi?id=12000
+@safe unittest
{
static struct S(T)
{
@@ -3193,45 +3428,17 @@ template hasIndirections(T)
}
/**
-Returns $(D true) if and only if $(D T)'s representation includes at
-least one of the following: $(OL $(LI a raw pointer $(D U*) and $(D U)
-is not immutable or shared;) $(LI an array $(D U[]) and $(D U) is not
-immutable or shared;) $(LI a reference to a class type $(D C) and
-$(D C) is not immutable or shared.) $(LI an associative array that is not
+Returns `true` if and only if `T`'s representation includes at
+least one of the following: $(OL $(LI a raw pointer `U*` and `U`
+is not immutable or shared;) $(LI an array `U[]` and `U` is not
+immutable or shared;) $(LI a reference to a class type `C` and
+`C` is not immutable or shared.) $(LI an associative array that is not
immutable or shared.) $(LI a delegate that is not shared.))
*/
template hasUnsharedAliasing(T...)
{
- import std.meta : anySatisfy;
- import std.typecons : Rebindable;
-
- static if (!T.length)
- {
- enum hasUnsharedAliasing = false;
- }
- else static if (is(T[0] R: Rebindable!R))
- {
- enum hasUnsharedAliasing = hasUnsharedAliasing!R;
- }
- else
- {
- template unsharedDelegate(T)
- {
- enum bool unsharedDelegate = isDelegate!T
- && !is(T == shared)
- && !is(T == shared)
- && !is(T == immutable)
- && !is(FunctionTypeOf!T == shared)
- && !is(FunctionTypeOf!T == immutable);
- }
-
- enum hasUnsharedAliasing =
- hasRawUnsharedAliasing!(T[0]) ||
- anySatisfy!(unsharedDelegate, RepresentationTypeTuple!(T[0])) ||
- hasUnsharedObjects!(T[0]) ||
- hasUnsharedAliasing!(T[1..$]);
- }
+ enum hasUnsharedAliasing = anySatisfy!(hasUnsharedAliasingImpl, T);
}
///
@@ -3256,7 +3463,7 @@ template hasUnsharedAliasing(T...)
@safe unittest
{
- /* Issue 6642 */
+ /* https://issues.dlang.org/show_bug.cgi?id=6642 */
import std.typecons : Rebindable;
struct S8 { int a; Rebindable!(immutable Object) b; }
static assert(!hasUnsharedAliasing!S8);
@@ -3303,7 +3510,7 @@ template hasUnsharedAliasing(T...)
static assert(!hasUnsharedAliasing!(Rebindable!(shared Object)));
static assert( hasUnsharedAliasing!(Rebindable!Object));
- /* Issue 6979 */
+ /* https://issues.dlang.org/show_bug.cgi?id=6979 */
static assert(!hasUnsharedAliasing!(int, shared(int)*));
static assert( hasUnsharedAliasing!(int, int*));
static assert( hasUnsharedAliasing!(int, const(int)[]));
@@ -3370,29 +3577,43 @@ template hasUnsharedAliasing(T...)
static assert(!hasUnsharedAliasing!S20);
}
+private template hasUnsharedAliasingImpl(T)
+{
+ import std.typecons : Rebindable;
+
+ static if (is(T R: Rebindable!R))
+ {
+ enum hasUnsharedAliasingImpl = hasUnsharedAliasingImpl!R;
+ }
+ else
+ {
+ template unsharedDelegate(T)
+ {
+ enum bool unsharedDelegate = isDelegate!T
+ && !is(T == shared)
+ && !is(T == immutable)
+ && !is(FunctionTypeOf!T == shared)
+ && !is(FunctionTypeOf!T == immutable);
+ }
+
+ enum hasUnsharedAliasingImpl =
+ hasRawUnsharedAliasing!T ||
+ anySatisfy!(unsharedDelegate, RepresentationTypeTuple!T) ||
+ hasUnsharedObjects!T;
+ }
+}
+
/**
- True if $(D S) or any type embedded directly in the representation of $(D S)
+ True if `S` or any type embedded directly in the representation of `S`
defines an elaborate copy constructor. Elaborate copy constructors are
- introduced by defining $(D this(this)) for a $(D struct).
+ introduced by defining `this(this)` for a `struct`.
Classes and unions never have elaborate copy constructors.
*/
template hasElaborateCopyConstructor(S)
{
- import std.meta : anySatisfy;
- static if (isStaticArray!S && S.length)
- {
- enum bool hasElaborateCopyConstructor = hasElaborateCopyConstructor!(typeof(S.init[0]));
- }
- else static if (is(S == struct))
- {
- enum hasElaborateCopyConstructor = hasMember!(S, "__postblit")
- || anySatisfy!(.hasElaborateCopyConstructor, FieldTypeTuple!S);
- }
- else
- {
- enum bool hasElaborateCopyConstructor = false;
- }
+ import core.internal.traits : hasElabCCtor = hasElaborateCopyConstructor;
+ alias hasElaborateCopyConstructor = hasElabCCtor!(S);
}
///
@@ -3420,13 +3641,13 @@ template hasElaborateCopyConstructor(S)
}
/**
- True if $(D S) or any type directly embedded in the representation of $(D S)
+ True if `S` or any type directly embedded in the representation of `S`
defines an elaborate assignment. Elaborate assignments are introduced by
- defining $(D opAssign(typeof(this))) or $(D opAssign(ref typeof(this)))
- for a $(D struct) or when there is a compiler-generated $(D opAssign).
+ defining `opAssign(typeof(this))` or $(D opAssign(ref typeof(this)))
+ for a `struct` or when there is a compiler-generated `opAssign`.
- A type $(D S) gets compiler-generated $(D opAssign) in case it has
- an elaborate copy constructor or elaborate destructor.
+ A type `S` gets compiler-generated `opAssign` if it has
+ an elaborate destructor.
Classes and unions never have elaborate assignments.
@@ -3436,7 +3657,6 @@ template hasElaborateCopyConstructor(S)
*/
template hasElaborateAssign(S)
{
- import std.meta : anySatisfy;
static if (isStaticArray!S && S.length)
{
enum bool hasElaborateAssign = hasElaborateAssign!(typeof(S.init[0]));
@@ -3513,30 +3733,18 @@ template hasElaborateAssign(S)
}
/**
- True if $(D S) or any type directly embedded in the representation
- of $(D S) defines an elaborate destructor. Elaborate destructors
- are introduced by defining $(D ~this()) for a $(D
+ True if `S` or any type directly embedded in the representation
+ of `S` defines an elaborate destructor. Elaborate destructors
+ are introduced by defining `~this()` for a $(D
struct).
Classes and unions never have elaborate destructors, even
- though classes may define $(D ~this()).
+ though classes may define `~this()`.
*/
template hasElaborateDestructor(S)
{
- import std.meta : anySatisfy;
- static if (isStaticArray!S && S.length)
- {
- enum bool hasElaborateDestructor = hasElaborateDestructor!(typeof(S.init[0]));
- }
- else static if (is(S == struct))
- {
- enum hasElaborateDestructor = hasMember!(S, "__dtor")
- || anySatisfy!(.hasElaborateDestructor, FieldTypeTuple!S);
- }
- else
- {
- enum bool hasElaborateDestructor = false;
- }
+ import core.internal.traits : hasElabDest = hasElaborateDestructor;
+ alias hasElaborateDestructor = hasElabDest!(S);
}
///
@@ -3563,11 +3771,52 @@ template hasElaborateDestructor(S)
static assert( hasElaborateDestructor!S7);
}
+/**
+ True if `S` or any type embedded directly in the representation of `S`
+ defines elaborate move semantics. Elaborate move semantics are
+ introduced by defining `opPostMove(ref typeof(this))` for a `struct`.
+
+ Classes and unions never have elaborate move semantics.
+ */
+template hasElaborateMove(S)
+{
+ import core.internal.traits : hasElabMove = hasElaborateMove;
+ alias hasElaborateMove = hasElabMove!(S);
+}
+
+///
+@safe unittest
+{
+ static assert(!hasElaborateMove!int);
+
+ static struct S1 { }
+ static struct S2 { void opPostMove(ref S2) {} }
+ static struct S3 { void opPostMove(inout ref S3) inout {} }
+ static struct S4 { void opPostMove(const ref S4) {} }
+ static struct S5 { void opPostMove(S5) {} }
+ static struct S6 { void opPostMove(int) {} }
+ static struct S7 { S3[1] field; }
+ static struct S8 { S3[] field; }
+ static struct S9 { S3[0] field; }
+ static struct S10 { @disable this(); S3 field; }
+ static assert(!hasElaborateMove!S1);
+ static assert( hasElaborateMove!S2);
+ static assert( hasElaborateMove!S3);
+ static assert( hasElaborateMove!(immutable S3));
+ static assert( hasElaborateMove!S4);
+ static assert(!hasElaborateMove!S5);
+ static assert(!hasElaborateMove!S6);
+ static assert( hasElaborateMove!S7);
+ static assert(!hasElaborateMove!S8);
+ static assert(!hasElaborateMove!S9);
+ static assert( hasElaborateMove!S10);
+}
+
package alias Identity(alias A) = A;
/**
- Yields $(D true) if and only if $(D T) is an aggregate that defines
- a symbol called $(D name).
+ Yields `true` if and only if `T` is an aggregate that defines
+ a symbol called `name`.
*/
enum hasMember(T, string name) = __traits(hasMember, T, name);
@@ -3587,7 +3836,7 @@ enum hasMember(T, string name) = __traits(hasMember, T, name);
@safe unittest
{
- // 8321
+ // https://issues.dlang.org/show_bug.cgi?id=8321
struct S {
int x;
void f(){}
@@ -3630,20 +3879,25 @@ enum hasMember(T, string name) = __traits(hasMember, T, name);
* Whether the symbol represented by the string, member, exists and is a static member of T.
*
* Params:
- * T = Type containing symbol $(D member).
- * member = Name of symbol to test that resides in $(D T).
+ * T = Type containing symbol `member`.
+ * member = Name of symbol to test that resides in `T`.
*
* Returns:
- * $(D true) iff $(D member) exists and is static.
+ * `true` iff `member` exists and is static.
*/
template hasStaticMember(T, string member)
{
static if (__traits(hasMember, T, member))
{
+ static if (isPointer!T)
+ alias U = PointerTarget!T;
+ else
+ alias U = T;
+
import std.meta : Alias;
- alias sym = Alias!(__traits(getMember, T, member));
+ alias sym = Alias!(__traits(getMember, U, member));
- static if (__traits(getOverloads, T, member).length == 0)
+ static if (__traits(getOverloads, U, member).length == 0)
enum bool hasStaticMember = __traits(compiles, &sym);
else
enum bool hasStaticMember = __traits(isStaticFunction, sym);
@@ -3696,7 +3950,7 @@ template hasStaticMember(T, string member)
static void f();
static void f2() pure nothrow @nogc @safe;
- shared void g();
+ void g() shared;
static void function() fp;
__gshared void function() gfp;
@@ -3734,7 +3988,7 @@ template hasStaticMember(T, string member)
static void f();
static void f2() pure nothrow @nogc @safe;
- shared void g() { }
+ void g() shared { }
static void function() fp;
__gshared void function() gfp;
@@ -3753,6 +4007,7 @@ template hasStaticMember(T, string member)
static @property int sp();
}
+ static assert(!hasStaticMember!(S, "na"));
static assert(!hasStaticMember!(S, "X"));
static assert(!hasStaticMember!(S, "Y"));
static assert(!hasStaticMember!(S, "Y.i"));
@@ -3777,6 +4032,7 @@ template hasStaticMember(T, string member)
static assert(!hasStaticMember!(S, "p"));
static assert( hasStaticMember!(S, "sp"));
+ static assert(!hasStaticMember!(C, "na"));
static assert(!hasStaticMember!(C, "X"));
static assert(!hasStaticMember!(C, "Y"));
static assert(!hasStaticMember!(C, "Y.i"));
@@ -3788,8 +4044,8 @@ template hasStaticMember(T, string member)
static assert( hasStaticMember!(C, "sy"));
static assert( hasStaticMember!(C, "f"));
static assert( hasStaticMember!(C, "f2"));
- static assert(!hasStaticMember!(S, "dm"));
- static assert( hasStaticMember!(S, "sd"));
+ static assert(!hasStaticMember!(C, "dm"));
+ static assert( hasStaticMember!(C, "sd"));
static assert(!hasStaticMember!(C, "g"));
static assert( hasStaticMember!(C, "fp"));
static assert( hasStaticMember!(C, "gfp"));
@@ -3800,75 +4056,65 @@ template hasStaticMember(T, string member)
static assert( hasStaticMember!(C, "iosf"));
static assert(!hasStaticMember!(C, "p"));
static assert( hasStaticMember!(C, "sp"));
+
+ alias P = S*;
+ static assert(!hasStaticMember!(P, "na"));
+ static assert(!hasStaticMember!(P, "X"));
+ static assert(!hasStaticMember!(P, "Y"));
+ static assert(!hasStaticMember!(P, "Y.i"));
+ static assert(!hasStaticMember!(P, "S"));
+ static assert(!hasStaticMember!(P, "C"));
+ static assert( hasStaticMember!(P, "sx"));
+ static assert( hasStaticMember!(P, "gx"));
+ static assert(!hasStaticMember!(P, "y"));
+ static assert( hasStaticMember!(P, "sy"));
+ static assert( hasStaticMember!(P, "f"));
+ static assert( hasStaticMember!(P, "f2"));
+ static assert(!hasStaticMember!(P, "dm"));
+ static assert( hasStaticMember!(P, "sd"));
+ static assert(!hasStaticMember!(P, "g"));
+ static assert( hasStaticMember!(P, "fp"));
+ static assert( hasStaticMember!(P, "gfp"));
+ static assert(!hasStaticMember!(P, "fpm"));
+ static assert(!hasStaticMember!(P, "m"));
+ static assert(!hasStaticMember!(P, "m2"));
+ static assert(!hasStaticMember!(P, "iom"));
+ static assert( hasStaticMember!(P, "iosf"));
+ static assert(!hasStaticMember!(P, "p"));
+ static assert( hasStaticMember!(P, "sp"));
}
/**
-Retrieves the members of an enumerated type $(D enum E).
+Retrieves the members of an enumerated type `enum E`.
Params:
- E = An enumerated type. $(D E) may have duplicated values.
+ E = An enumerated type. `E` may have duplicated values.
Returns:
- Static tuple composed of the members of the enumerated type $(D E).
- The members are arranged in the same order as declared in $(D E).
+ Static tuple composed of the members of the enumerated type `E`.
+ The members are arranged in the same order as declared in `E`.
+ The name of the enum can be found by querying the compiler for the
+ name of the identifier, i.e. `__traits(identifier, EnumMembers!MyEnum[i])`.
+ For enumerations with unique values, $(REF to, std,conv) can also be used.
Note:
- An enum can have multiple members which have the same value. If you want
- to use EnumMembers to e.g. generate switch cases at compile-time,
- you should use the $(REF NoDuplicates, std,meta) template to avoid
- generating duplicate switch cases.
+ An enum can have multiple members which have the same value. If you want
+ to use EnumMembers to e.g. generate switch cases at compile-time,
+ you should use the $(REF NoDuplicates, std,meta) template to avoid
+ generating duplicate switch cases.
Note:
- Returned values are strictly typed with $(D E). Thus, the following code
- does not work without the explicit cast:
+ Returned values are strictly typed with `E`. Thus, the following code
+ does not work without the explicit cast:
--------------------
enum E : int { a, b, c }
int[] abc = cast(int[]) [ EnumMembers!E ];
--------------------
- Cast is not necessary if the type of the variable is inferred. See the
- example below.
-
-Example:
- Creating an array of enumerated values:
---------------------
-enum Sqrts : real
-{
- one = 1,
- two = 1.41421,
- three = 1.73205,
-}
-auto sqrts = [ EnumMembers!Sqrts ];
-assert(sqrts == [ Sqrts.one, Sqrts.two, Sqrts.three ]);
---------------------
-
- A generic function $(D rank(v)) in the following example uses this
- template for finding a member $(D e) in an enumerated type $(D E).
---------------------
-// Returns i if e is the i-th enumerator of E.
-size_t rank(E)(E e)
- if (is(E == enum))
-{
- foreach (i, member; EnumMembers!E)
- {
- if (e == member)
- return i;
- }
- assert(0, "Not an enum member");
-}
-
-enum Mode
-{
- read = 1,
- write = 2,
- map = 4,
-}
-assert(rank(Mode.read ) == 0);
-assert(rank(Mode.write) == 1);
-assert(rank(Mode.map ) == 2);
---------------------
+ Cast is not necessary if the type of the variable is inferred. See the
+ example below.
*/
template EnumMembers(E)
- if (is(E == enum))
+if (is(E == enum))
{
import std.meta : AliasSeq;
// Supply the specified identifier to an constant value.
@@ -3916,6 +4162,81 @@ template EnumMembers(E)
alias EnumMembers = EnumSpecificMembers!(__traits(allMembers, E));
}
+/// Create an array of enumerated values
+@safe unittest
+{
+ enum Sqrts : real
+ {
+ one = 1,
+ two = 1.41421,
+ three = 1.73205
+ }
+ auto sqrts = [EnumMembers!Sqrts];
+ assert(sqrts == [Sqrts.one, Sqrts.two, Sqrts.three]);
+}
+
+/**
+A generic function `rank(v)` in the following example uses this
+template for finding a member `e` in an enumerated type `E`.
+ */
+@safe unittest
+{
+ // Returns i if e is the i-th enumerator of E.
+ static size_t rank(E)(E e)
+ if (is(E == enum))
+ {
+ static foreach (i, member; EnumMembers!E)
+ {
+ if (e == member)
+ return i;
+ }
+ assert(0, "Not an enum member");
+ }
+
+ enum Mode
+ {
+ read = 1,
+ write = 2,
+ map = 4
+ }
+ assert(rank(Mode.read) == 0);
+ assert(rank(Mode.write) == 1);
+ assert(rank(Mode.map) == 2);
+}
+
+/**
+Use EnumMembers to generate a switch statement using static foreach.
+*/
+
+@safe unittest
+{
+ import std.conv : to;
+ class FooClass
+ {
+ string calledMethod;
+ void foo() @safe { calledMethod = "foo"; }
+ void bar() @safe { calledMethod = "bar"; }
+ void baz() @safe { calledMethod = "baz"; }
+ }
+
+ enum FooEnum { foo, bar, baz }
+
+ auto var = FooEnum.bar;
+ auto fooObj = new FooClass();
+ s: final switch (var)
+ {
+ static foreach (member; EnumMembers!FooEnum)
+ {
+ case member: // Generate a case for each enum value.
+ // Call fooObj.{name of enum value}().
+ __traits(getMember, fooObj, to!string(member))();
+ break s;
+ }
+ }
+ // As we pass in FooEnum.bar, the bar() method gets called.
+ assert(fooObj.calledMethod == "bar");
+}
+
@safe unittest
{
enum A { a }
@@ -3948,7 +4269,8 @@ template EnumMembers(E)
static assert([ EnumMembers!A ] == [ A.a, A.b, A.c, A.d, A.e ]);
}
-@safe unittest // Bugzilla 14561: huge enums
+// https://issues.dlang.org/show_bug.cgi?id=14561: huge enums
+@safe unittest
{
string genEnum()
{
@@ -4002,6 +4324,8 @@ template BaseTypeTuple(A)
///
@safe unittest
{
+ import std.meta : AliasSeq;
+
interface I1 { }
interface I2 { }
interface I12 : I1, I2 { }
@@ -4034,7 +4358,7 @@ template BaseTypeTuple(A)
* BaseClassesTuple!Object) yields the empty type tuple.
*/
template BaseClassesTuple(T)
- if (is(T == class))
+if (is(T == class))
{
static if (is(T == Object))
{
@@ -4044,6 +4368,10 @@ template BaseClassesTuple(T)
{
alias BaseClassesTuple = AliasSeq!Object;
}
+ else static if (!is(BaseTypeTuple!T[0] == Object) && !is(BaseTypeTuple!T[0] == class))
+ {
+ alias BaseClassesTuple = AliasSeq!();
+ }
else
{
alias BaseClassesTuple =
@@ -4055,6 +4383,8 @@ template BaseClassesTuple(T)
///
@safe unittest
{
+ import std.meta : AliasSeq;
+
class C1 { }
class C2 : C1 { }
class C3 : C2 { }
@@ -4064,6 +4394,22 @@ template BaseClassesTuple(T)
static assert(is(BaseClassesTuple!C3 == AliasSeq!(C2, C1, Object)));
}
+// https://issues.dlang.org/show_bug.cgi?id=17276
+@safe unittest
+{
+ extern (C++) static interface Ext
+ {
+ void someext();
+ }
+
+ extern (C++) static class E : Ext
+ {
+ void someext() {}
+ }
+
+ alias BaseClassesWithNoObject = BaseClassesTuple!E;
+}
+
@safe unittest
{
struct S { }
@@ -4076,10 +4422,15 @@ template BaseClassesTuple(T)
}
/**
- * Get a $(D_PARAM AliasSeq) of $(I all) interfaces directly or
- * indirectly inherited by this class or interface. Interfaces do not
- * repeat if multiply implemented. $(D_PARAM InterfacesTuple!Object)
- * yields the empty type tuple.
+Params:
+ T = The `class` or `interface` to search.
+
+Returns:
+ $(REF AliasSeq,std,meta) of all interfaces directly or
+ indirectly inherited by this class or interface. Interfaces
+ do not repeat if multiply implemented.
+
+ `InterfacesTuple!Object` yields an empty `AliasSeq`.
*/
template InterfacesTuple(T)
{
@@ -4105,14 +4456,15 @@ template InterfacesTuple(T)
alias InterfacesTuple = AliasSeq!();
}
+///
@safe unittest
{
- // doc example
interface I1 {}
interface I2 {}
- class A : I1, I2 { }
- class B : A, I1 { }
- class C : B { }
+ class A : I1, I2 {}
+ class B : A, I1 {}
+ class C : B {}
+
alias TL = InterfacesTuple!C;
static assert(is(TL[0] == I1) && is(TL[1] == I2));
}
@@ -4172,12 +4524,12 @@ template TransitiveBaseTypeTuple(T)
/**
-Returns a tuple of non-static functions with the name $(D name) declared in the
-class or interface $(D C). Covariant duplicates are shrunk into the most
+Returns a tuple of non-static functions with the name `name` declared in the
+class or interface `C`. Covariant duplicates are shrunk into the most
derived one.
*/
template MemberFunctionsTuple(C, string name)
- if (is(C == class) || is(C == interface))
+if (is(C == class) || is(C == interface))
{
static if (__traits(hasMember, C, name))
{
@@ -4189,7 +4541,7 @@ template MemberFunctionsTuple(C, string name)
static if (__traits(hasMember, Node, name) && __traits(compiles, __traits(getMember, Node, name)))
{
// Get all overloads in sight (not hidden).
- alias inSight = AliasSeq!(__traits(getVirtualFunctions, Node, name));
+ alias inSight = __traits(getVirtualFunctions, Node, name);
// And collect all overloads in ancestor classes to reveal hidden
// methods. The result may contain duplicates.
@@ -4213,8 +4565,11 @@ template MemberFunctionsTuple(C, string name)
alias CollectOverloads = AliasSeq!(); // no overloads in this hierarchy
}
- // duplicates in this tuple will be removed by shrink()
- alias overloads = CollectOverloads!C;
+ static if (name == "__ctor" || name == "__dtor")
+ alias overloads = AliasSeq!(__traits(getOverloads, C, name));
+ else
+ // duplicates in this tuple will be removed by shrink()
+ alias overloads = CollectOverloads!C;
// shrinkOne!args[0] = the most derived one in the covariant siblings of target
// shrinkOne!args[1..$] = non-covariant others
@@ -4232,7 +4587,7 @@ template MemberFunctionsTuple(C, string name)
static if (isCovariantWith!(Target, Rest0) && isCovariantWith!(Rest0, Target))
{
// One of these overrides the other. Choose the one from the most derived parent.
- static if (is(AliasSeq!(__traits(parent, target))[0] : AliasSeq!(__traits(parent, rest[0]))[0]))
+ static if (is(__traits(parent, target) : __traits(parent, rest[0])))
alias shrinkOne = shrinkOne!(target, rest[1 .. $]);
else
alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]);
@@ -4293,7 +4648,8 @@ template MemberFunctionsTuple(C, string name)
static assert(__traits(isSame, foos[1], B.foo));
}
-@safe unittest // Issue 15920
+// https://issues.dlang.org/show_bug.cgi?id=15920
+@safe unittest
{
import std.meta : AliasSeq;
class A
@@ -4307,11 +4663,46 @@ template MemberFunctionsTuple(C, string name)
override void f(int){}
}
alias fs = MemberFunctionsTuple!(B, "f");
- alias bfs = AliasSeq!(__traits(getOverloads, B, "f"));
+ alias bfs = __traits(getOverloads, B, "f");
assert(__traits(isSame, fs[0], bfs[0]) || __traits(isSame, fs[0], bfs[1]));
assert(__traits(isSame, fs[1], bfs[0]) || __traits(isSame, fs[1], bfs[1]));
}
+// https://issues.dlang.org/show_bug.cgi?id=8388
+@safe unittest
+{
+ class C
+ {
+ this() {}
+ this(int i) {}
+ this(int i, float j) {}
+ this(string s) {}
+
+ /*
+ Commented out, because this causes a cyclic dependency
+ between module constructors/destructors error. Might
+ be caused by https://issues.dlang.org/show_bug.cgi?id=20529. */
+ // static this() {}
+
+ ~this() {}
+ }
+
+ class D : C
+ {
+ this() {}
+ ~this() {}
+ }
+
+ alias test_ctor = MemberFunctionsTuple!(C, "__ctor");
+ assert(test_ctor.length == 4);
+ alias test_dtor = MemberFunctionsTuple!(C, "__dtor");
+ assert(test_dtor.length == 1);
+ alias test2_ctor = MemberFunctionsTuple!(D, "__ctor");
+ assert(test2_ctor.length == 1);
+ alias test2_dtor = MemberFunctionsTuple!(D, "__dtor");
+ assert(test2_dtor.length == 1);
+}
+
@safe unittest
{
interface I { I test(); }
@@ -4354,7 +4745,8 @@ template MemberFunctionsTuple(C, string name)
/**
-Returns an alias to the template that $(D T) is an instance of.
+Returns an alias to the template that `T` is an instance of.
+It will return `void` if a symbol without a template is given.
*/
template TemplateOf(alias T : Base!Args, alias Base, Args...)
{
@@ -4367,6 +4759,12 @@ template TemplateOf(T : Base!Args, alias Base, Args...)
alias TemplateOf = Base;
}
+/// ditto
+template TemplateOf(T)
+{
+ alias TemplateOf = void;
+}
+
///
@safe unittest
{
@@ -4397,9 +4795,15 @@ template TemplateOf(T : Base!Args, alias Base, Args...)
static assert(__traits(isSame, TemplateOf!(Foo10!()), Foo10));
}
+// https://issues.dlang.org/show_bug.cgi?id=18214
+@safe unittest
+{
+ static assert(is(TemplateOf!(int[]) == void));
+ static assert(is(TemplateOf!bool == void));
+}
/**
-Returns a $(D AliasSeq) of the template arguments used to instantiate $(D T).
+Returns a `AliasSeq` of the template arguments used to instantiate `T`.
*/
template TemplateArgsOf(alias T : Base!Args, alias Base, Args...)
{
@@ -4415,6 +4819,8 @@ template TemplateArgsOf(T : Base!Args, alias Base, Args...)
///
@safe unittest
{
+ import std.meta : AliasSeq;
+
struct Foo(T, U) {}
static assert(is(TemplateArgsOf!(Foo!(int, real)) == AliasSeq!(int, real)));
}
@@ -4445,17 +4851,20 @@ template TemplateArgsOf(T : Base!Args, alias Base, Args...)
}
-private template maxAlignment(U...) if (isTypeTuple!U)
+package template maxAlignment(U...)
+if (isTypeTuple!U)
{
- import std.meta : staticMap;
static if (U.length == 0)
static assert(0);
else static if (U.length == 1)
enum maxAlignment = U[0].alignof;
+ else static if (U.length == 2)
+ enum maxAlignment = U[0].alignof > U[1].alignof ? U[0].alignof : U[1].alignof;
else
{
- import std.algorithm.comparison : max;
- enum maxAlignment = max(staticMap!(.maxAlignment, U));
+ enum a = maxAlignment!(U[0 .. ($+1)/2]);
+ enum b = maxAlignment!(U[($+1)/2 .. $]);
+ enum maxAlignment = a > b ? a : b;
}
}
@@ -4463,7 +4872,8 @@ private template maxAlignment(U...) if (isTypeTuple!U)
/**
Returns class instance alignment.
*/
-template classInstanceAlignment(T) if (is(T == class))
+template classInstanceAlignment(T)
+if (is(T == class))
{
alias classInstanceAlignment = maxAlignment!(void*, typeof(T.tupleof));
}
@@ -4523,26 +4933,186 @@ template CommonType(T...)
alias Y = CommonType!(int, char[], short);
assert(is(Y == void));
}
+
+///
@safe unittest
{
static assert(is(CommonType!(3) == int));
static assert(is(CommonType!(double, 4, float) == double));
static assert(is(CommonType!(string, char[]) == const(char)[]));
static assert(is(CommonType!(3, 3U) == uint));
+ static assert(is(CommonType!(double, int) == double));
}
/**
- * Returns a tuple with all possible target types of an implicit
- * conversion of a value of type $(D_PARAM T).
- *
- * Important note:
- *
- * The possible targets are computed more conservatively than the D
- * 2.005 compiler does, eliminating all dangerous conversions. For
- * example, $(D_PARAM ImplicitConversionTargets!double) does not
- * include $(D_PARAM float).
+Params:
+ T = The type to check
+
+Returns:
+ An $(REF AliasSeq,std,meta) with all possible target types of an implicit
+ conversion `T`.
+
+ If `T` is a class derived from `Object`, the result of
+ $(LREF TransitiveBaseTypeTuple) is returned.
+
+ If the type is not a built-in value type or a class derived from
+ `Object`, an empty $(REF AliasSeq,std,meta) is returned.
+
+See_Also:
+ $(LREF isImplicitlyConvertible)
+ */
+template AllImplicitConversionTargets(T)
+{
+ static if (is(T == bool))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList,
+ float, double, real, char, wchar, dchar);
+ else static if (is(T == byte))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(ubyte, short, ushort, int, uint, long, ulong, CentTypeList,
+ float, double, real, char, wchar, dchar);
+ else static if (is(T == ubyte))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(byte, short, ushort, int, uint, long, ulong, CentTypeList,
+ float, double, real, char, wchar, dchar);
+ else static if (is(T == short))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(ushort, int, uint, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == ushort))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(short, int, uint, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == int))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(uint, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == uint))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(int, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == long))
+ alias AllImplicitConversionTargets = AliasSeq!(ulong, float, double, real);
+ else static if (is(T == ulong))
+ alias AllImplicitConversionTargets = AliasSeq!(long, float, double, real);
+ else static if (is(cent) && is(T == cent))
+ alias AllImplicitConversionTargets = AliasSeq!(UnsignedCentTypeList, float, double, real);
+ else static if (is(ucent) && is(T == ucent))
+ alias AllImplicitConversionTargets = AliasSeq!(SignedCentTypeList, float, double, real);
+ else static if (is(T == float))
+ alias AllImplicitConversionTargets = AliasSeq!(double, real);
+ else static if (is(T == double))
+ alias AllImplicitConversionTargets = AliasSeq!(float, real);
+ else static if (is(T == real))
+ alias AllImplicitConversionTargets = AliasSeq!(float, double);
+ else static if (is(T == char))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(wchar, dchar, byte, ubyte, short, ushort,
+ int, uint, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == wchar))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(dchar, short, ushort, int, uint, long, ulong, CentTypeList,
+ float, double, real);
+ else static if (is(T == dchar))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T : typeof(null)))
+ alias AllImplicitConversionTargets = AliasSeq!(typeof(null));
+ else static if (is(T == class))
+ alias AllImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), TransitiveBaseTypeTuple!(T));
+ else static if (is(T == interface))
+ alias AllImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), InterfacesTuple!(T));
+ else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const))
+ {
+ static if (is(typeof(T.init[0]) == shared))
+ alias AllImplicitConversionTargets =
+ AliasSeq!(const(shared(Unqual!(typeof(T.init[0]))))[]);
+ else
+ alias AllImplicitConversionTargets =
+ AliasSeq!(const(Unqual!(typeof(T.init[0])))[]);
+ }
+ else static if (is(T : void*))
+ alias AllImplicitConversionTargets = AliasSeq!(void*);
+ else
+ alias AllImplicitConversionTargets = AliasSeq!();
+}
+
+///
+@safe unittest
+{
+ import std.meta : AliasSeq;
+
+ static assert(is(AllImplicitConversionTargets!(ulong) == AliasSeq!(long, float, double, real)));
+ static assert(is(AllImplicitConversionTargets!(int) == AliasSeq!(uint, long, ulong, float, double, real)));
+ static assert(is(AllImplicitConversionTargets!(float) == AliasSeq!(double, real)));
+ static assert(is(AllImplicitConversionTargets!(double) == AliasSeq!(float, real)));
+
+ static assert(is(AllImplicitConversionTargets!(char) == AliasSeq!(
+ wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real
+ )));
+ static assert(is(AllImplicitConversionTargets!(wchar) == AliasSeq!(
+ dchar, short, ushort, int, uint, long, ulong, float, double, real
+ )));
+ static assert(is(AllImplicitConversionTargets!(dchar) == AliasSeq!(
+ int, uint, long, ulong, float, double, real
+ )));
+
+ static assert(is(AllImplicitConversionTargets!(string) == AliasSeq!(const(char)[])));
+ static assert(is(AllImplicitConversionTargets!(void*) == AliasSeq!(void*)));
+
+ interface A {}
+ interface B {}
+ class C : A, B {}
+
+ static assert(is(AllImplicitConversionTargets!(C) == AliasSeq!(Object, A, B)));
+ static assert(is(AllImplicitConversionTargets!(const C) == AliasSeq!(const Object, const A, const B)));
+ static assert(is(AllImplicitConversionTargets!(immutable C) == AliasSeq!(
+ immutable Object, immutable A, immutable B
+ )));
+
+ interface I : A, B {}
+
+ static assert(is(AllImplicitConversionTargets!(I) == AliasSeq!(A, B)));
+ static assert(is(AllImplicitConversionTargets!(const I) == AliasSeq!(const A, const B)));
+ static assert(is(AllImplicitConversionTargets!(immutable I) == AliasSeq!(
+ immutable A, immutable B
+ )));
+}
+
+@safe unittest
+{
+ static assert(is(AllImplicitConversionTargets!(double)[0] == float));
+ static assert(is(AllImplicitConversionTargets!(double)[1] == real));
+ static assert(is(AllImplicitConversionTargets!(string)[0] == const(char)[]));
+}
+
+
+/**
+Params:
+ T = The type to check
+
+Warning:
+ This template is considered out-dated. It will be removed from
+ Phobos in 2.107.0. Please use $(LREF AllImplicitConversionTargets) instead.
+
+Returns:
+ An $(REF AliasSeq,std,meta) with all possible target types of an implicit
+ conversion `T`.
+
+ If `T` is a class derived from `Object`, the result of
+ $(LREF TransitiveBaseTypeTuple) is returned.
+
+ If the type is not a built-in value type or a class derived from
+ `Object`, an empty $(REF AliasSeq,std,meta) is returned.
+
+Note:
+ The possible targets are computed more conservatively than the
+ language allows, eliminating all dangerous conversions. For example,
+ `ImplicitConversionTargets!double` does not include `float`.
+
+See_Also:
+ $(LREF isImplicitlyConvertible)
*/
+// @@@DEPRECATED_[2.107.0]@@@
+deprecated("ImplicitConversionTargets has been deprecated in favour of AllImplicitConversionTargets "
+ ~ "and will be removed in 2.107.0")
template ImplicitConversionTargets(T)
{
static if (is(T == bool))
@@ -4594,36 +5164,66 @@ template ImplicitConversionTargets(T)
AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
else static if (is(T : typeof(null)))
alias ImplicitConversionTargets = AliasSeq!(typeof(null));
- else static if (is(T : Object))
- alias ImplicitConversionTargets = TransitiveBaseTypeTuple!(T);
+ else static if (is(T == class))
+ alias ImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), TransitiveBaseTypeTuple!(T));
else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const))
- alias ImplicitConversionTargets =
- AliasSeq!(const(Unqual!(typeof(T.init[0])))[]);
+ {
+ static if (is(typeof(T.init[0]) == shared))
+ alias ImplicitConversionTargets =
+ AliasSeq!(const(shared(Unqual!(typeof(T.init[0]))))[]);
+ else
+ alias ImplicitConversionTargets =
+ AliasSeq!(const(Unqual!(typeof(T.init[0])))[]);
+ }
else static if (is(T : void*))
alias ImplicitConversionTargets = AliasSeq!(void*);
else
alias ImplicitConversionTargets = AliasSeq!();
}
-@safe unittest
+deprecated @safe unittest
+{
+ import std.meta : AliasSeq;
+
+ static assert(is(ImplicitConversionTargets!(ulong) == AliasSeq!(float, double, real)));
+ static assert(is(ImplicitConversionTargets!(int) == AliasSeq!(long, ulong, float, double, real)));
+ static assert(is(ImplicitConversionTargets!(float) == AliasSeq!(double, real)));
+ static assert(is(ImplicitConversionTargets!(double) == AliasSeq!(real)));
+
+ static assert(is(ImplicitConversionTargets!(char) == AliasSeq!(
+ wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real
+ )));
+ static assert(is(ImplicitConversionTargets!(wchar) == AliasSeq!(
+ dchar, short, ushort, int, uint, long, ulong, float, double, real
+ )));
+ static assert(is(ImplicitConversionTargets!(dchar) == AliasSeq!(
+ int, uint, long, ulong, float, double, real
+ )));
+
+ static assert(is(ImplicitConversionTargets!(string) == AliasSeq!(const(char)[])));
+ static assert(is(ImplicitConversionTargets!(void*) == AliasSeq!(void*)));
+
+ interface A {}
+ interface B {}
+ class C : A, B {}
+
+ static assert(is(ImplicitConversionTargets!(C) == AliasSeq!(Object, A, B)));
+ static assert(is(ImplicitConversionTargets!(const C) == AliasSeq!(const Object, const A, const B)));
+ static assert(is(ImplicitConversionTargets!(immutable C) == AliasSeq!(
+ immutable Object, immutable A, immutable B
+ )));
+}
+
+deprecated @safe unittest
{
static assert(is(ImplicitConversionTargets!(double)[0] == real));
static assert(is(ImplicitConversionTargets!(string)[0] == const(char)[]));
}
/**
-Is $(D From) implicitly convertible to $(D To)?
+Is `From` implicitly convertible to `To`?
*/
-template isImplicitlyConvertible(From, To)
-{
- enum bool isImplicitlyConvertible = is(typeof({
- void fun(ref From v)
- {
- void gun(To) {}
- gun(v);
- }
- }));
-}
+enum bool isImplicitlyConvertible(From, To) = is(From : To);
///
@safe unittest
@@ -4642,12 +5242,12 @@ template isImplicitlyConvertible(From, To)
}
/**
-Returns $(D true) iff a value of type $(D Rhs) can be assigned to a variable of
-type $(D Lhs).
+Returns `true` iff a value of type `Rhs` can be assigned to a variable of
+type `Lhs`.
-$(D isAssignable) returns whether both an lvalue and rvalue can be assigned.
+`isAssignable` returns whether both an lvalue and rvalue can be assigned.
-If you omit $(D Rhs), $(D isAssignable) will check identity assignable of $(D Lhs).
+If you omit `Rhs`, `isAssignable` will check identity assignable of `Lhs`.
*/
enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs);
@@ -4666,11 +5266,17 @@ enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAss
static assert(!isAssignable!(immutable int));
}
-// ditto
-private enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lhs = rvalueOf!Rhs);
+/**
+Returns `true` iff an rvalue of type `Rhs` can be assigned to a variable of
+type `Lhs`
+*/
+enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; });
-// ditto
-private enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs);
+/**
+Returns `true` iff an lvalue of type `Rhs` can be assigned to a variable of
+type `Lhs`
+*/
+enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lvalueOf!Rhs; });
@safe unittest
{
@@ -4706,20 +5312,37 @@ private enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lh
static assert( isAssignable!(S4, immutable int));
struct S5 { @disable this(); @disable this(this); }
- struct S6 { void opAssign(in ref S5); }
- static assert(!isAssignable!(S6, S5));
- static assert(!isRvalueAssignable!(S6, S5));
- static assert( isLvalueAssignable!(S6, S5));
- static assert( isLvalueAssignable!(S6, immutable S5));
+ // https://issues.dlang.org/show_bug.cgi?id=21210
+ static assert(!isAssignable!S5);
+
+ // `-preview=in` is enabled
+ static if (!is(typeof(mixin(q{(in ref int a) => a}))))
+ {
+ struct S6 { void opAssign(in S5); }
+
+ static assert(isRvalueAssignable!(S6, S5));
+ static assert(isLvalueAssignable!(S6, S5));
+ static assert(isAssignable!(S6, S5));
+ static assert(isAssignable!(S6, immutable S5));
+ }
+ else
+ {
+ mixin(q{ struct S6 { void opAssign(in ref S5); } });
+
+ static assert(!isRvalueAssignable!(S6, S5));
+ static assert( isLvalueAssignable!(S6, S5));
+ static assert(!isAssignable!(S6, S5));
+ static assert( isLvalueAssignable!(S6, immutable S5));
+ }
}
// Equivalent with TypeStruct::isAssignable in compiler code.
package template isBlitAssignable(T)
{
- static if (is(OriginalType!T U) && !is(T == U))
+ static if (is(T == enum))
{
- enum isBlitAssignable = isBlitAssignable!U;
+ enum isBlitAssignable = isBlitAssignable!(OriginalType!T);
}
else static if (isStaticArray!T && is(T == E[n], E, size_t n))
// Workaround for issue 11499 : isStaticArray!T should not be necessary.
@@ -4835,7 +5458,7 @@ package template isBlitAssignable(T)
/*
-Works like $(D isImplicitlyConvertible), except this cares only about storage
+Works like `isImplicitlyConvertible`, except this cares only about storage
classes of the arguments.
*/
private template isStorageClassImplicitlyConvertible(From, To)
@@ -4860,11 +5483,13 @@ private template isStorageClassImplicitlyConvertible(From, To)
/**
-Determines whether the function type $(D F) is covariant with $(D G), i.e.,
-functions of the type $(D F) can override ones of the type $(D G).
+Determines whether the function type `F` is covariant with `G`, i.e.,
+functions of the type `F` can override ones of the type `G`.
*/
template isCovariantWith(F, G)
- if (is(F == function) && is(G == function))
+if (is(F == function) && is(G == function) ||
+ is(F == delegate) && is(G == delegate) ||
+ isFunctionPointer!F && isFunctionPointer!G)
{
static if (is(F : G))
enum isCovariantWith = true;
@@ -4926,7 +5551,8 @@ template isCovariantWith(F, G)
}
/*
* Check for parameters:
- * - require exact match for types (cf. bugzilla 3075)
+ * - require exact match for types
+ * (cf. https://issues.dlang.org/show_bug.cgi?id=3075)
* - require exact match for in, out, ref and lazy
* - overrider can add scope, but can't remove
*/
@@ -5010,6 +5636,15 @@ template isCovariantWith(F, G)
static assert( isCovariantWith!(DerivA_1.test, DerivA_1.test));
static assert( isCovariantWith!(DerivA_2.test, DerivA_2.test));
+ // function, function pointer and delegate
+ J function() derived_function;
+ I function() base_function;
+ J delegate() derived_delegate;
+ I delegate() base_delegate;
+ static assert(.isCovariantWith!(typeof(derived_function), typeof(base_function)));
+ static assert(.isCovariantWith!(typeof(*derived_function), typeof(*base_function)));
+ static assert(.isCovariantWith!(typeof(derived_delegate), typeof(base_delegate)));
+
// scope parameter
interface BaseB { void test( int*, int*); }
interface DerivB_1 : BaseB { override void test(scope int*, int*); }
@@ -5058,31 +5693,35 @@ template isCovariantWith(F, G)
private struct __InoutWorkaroundStruct{}
/**
-Creates an lvalue or rvalue of type $(D T) for $(D typeof(...)) and
-$(D __traits(compiles, ...)) purposes. No actual value is returned.
+Creates an lvalue or rvalue of type `T` for `typeof(...)` and
+`__traits(compiles, ...)` purposes. No actual value is returned.
+
+Params:
+ T = The type to transform
Note: Trying to use returned value will result in a
"Symbol Undefined" error at link time.
-
-Example:
----
-// Note that `f` doesn't have to be implemented
-// as is isn't called.
-int f(int);
-bool f(ref int);
-static assert(is(typeof(f(rvalueOf!int)) == int));
-static assert(is(typeof(f(lvalueOf!int)) == bool));
-
-int i = rvalueOf!int; // error, no actual value is returned
----
*/
@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);
/// ditto
@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);
-// Note: unittest can't be used as an example here as function overloads
+// Note: can't put these unittests together as function overloads
// aren't allowed inside functions.
+///
+@system unittest
+{
+ static int f(int);
+ static assert(is(typeof(f(rvalueOf!int)) == int));
+}
+
+///
+@system unittest
+{
+ static bool f(ref int);
+ static assert(is(typeof(f(lvalueOf!int)) == bool));
+}
@system unittest
{
@@ -5090,7 +5729,7 @@ int i = rvalueOf!int; // error, no actual value is returned
static struct S { }
int i;
struct Nested { void f() { ++i; } }
- foreach (T; AliasSeq!(int, immutable int, inout int, string, S, Nested, Object))
+ static foreach (T; AliasSeq!(int, immutable int, inout int, string, S, Nested, Object))
{
static assert(!__traits(compiles, needLvalue(rvalueOf!T)));
static assert( __traits(compiles, needLvalue(lvalueOf!T)));
@@ -5108,17 +5747,7 @@ int i = rvalueOf!int; // error, no actual value is returned
// SomethingTypeOf
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
-private template AliasThisTypeOf(T) if (isAggregateType!T)
-{
- alias members = AliasSeq!(__traits(getAliasThis, T));
-
- static if (members.length == 1)
- {
- alias AliasThisTypeOf = typeof(__traits(getMember, T.init, members[0]));
- }
- else
- static assert(0, T.stringof~" does not have alias this type");
-}
+private alias AliasThisTypeOf(T) = typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0]));
/*
*/
@@ -5129,7 +5758,7 @@ template BooleanTypeOf(T)
else
alias X = OriginalType!T;
- static if (is(Unqual!X == bool))
+ static if (is(immutable X == immutable bool))
{
alias BooleanTypeOf = X;
}
@@ -5140,15 +5769,15 @@ template BooleanTypeOf(T)
@safe unittest
{
// unexpected failure, maybe dmd type-merging bug
- foreach (T; AliasSeq!bool)
- foreach (Q; TypeQualifierList)
+ static foreach (T; AliasSeq!bool)
+ static foreach (Q; TypeQualifierList)
{
static assert( is(Q!T == BooleanTypeOf!( Q!T )));
static assert( is(Q!T == BooleanTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; AliasSeq!(void, NumericTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
- foreach (Q; TypeQualifierList)
+ static foreach (T; AliasSeq!(void, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList))
+ static foreach (Q; TypeQualifierList)
{
static assert(!is(BooleanTypeOf!( Q!T )), Q!T.stringof);
static assert(!is(BooleanTypeOf!( SubTypeOf!(Q!T) )));
@@ -5175,13 +5804,13 @@ template BooleanTypeOf(T)
*/
template IntegralTypeOf(T)
{
- import std.meta : staticIndexOf;
static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
alias X = IntegralTypeOf!AT;
else
alias X = OriginalType!T;
- static if (staticIndexOf!(Unqual!X, IntegralTypeList) >= 0)
+ static if (__traits(isIntegral, X) && __traits(isZeroInit, X) // Not char, wchar, or dchar.
+ && !is(immutable X == immutable bool) && !is(X == __vector))
{
alias IntegralTypeOf = X;
}
@@ -5191,15 +5820,16 @@ template IntegralTypeOf(T)
@safe unittest
{
- foreach (T; IntegralTypeList)
- foreach (Q; TypeQualifierList)
+ static foreach (T; IntegralTypeList)
+ static foreach (Q; TypeQualifierList)
{
static assert( is(Q!T == IntegralTypeOf!( Q!T )));
static assert( is(Q!T == IntegralTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; AliasSeq!(void, bool, FloatingPointTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
- foreach (Q; TypeQualifierList)
+ static foreach (T; AliasSeq!(void, bool, FloatingPointTypeList,
+ /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList))
+ static foreach (Q; TypeQualifierList)
{
static assert(!is(IntegralTypeOf!( Q!T )));
static assert(!is(IntegralTypeOf!( SubTypeOf!(Q!T) )));
@@ -5210,13 +5840,12 @@ template IntegralTypeOf(T)
*/
template FloatingPointTypeOf(T)
{
- import std.meta : staticIndexOf;
static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
alias X = FloatingPointTypeOf!AT;
else
alias X = OriginalType!T;
- static if (staticIndexOf!(Unqual!X, FloatingPointTypeList) >= 0)
+ static if (is(immutable X == immutable U, U) && is(U == float) || is(U == double) || is(U == real))
{
alias FloatingPointTypeOf = X;
}
@@ -5226,15 +5855,15 @@ template FloatingPointTypeOf(T)
@safe unittest
{
- foreach (T; FloatingPointTypeList)
- foreach (Q; TypeQualifierList)
+ static foreach (T; FloatingPointTypeList)
+ static foreach (Q; TypeQualifierList)
{
static assert( is(Q!T == FloatingPointTypeOf!( Q!T )));
static assert( is(Q!T == FloatingPointTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; AliasSeq!(void, bool, IntegralTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
- foreach (Q; TypeQualifierList)
+ static foreach (T; AliasSeq!(void, bool, IntegralTypeList, /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList))
+ static foreach (Q; TypeQualifierList)
{
static assert(!is(FloatingPointTypeOf!( Q!T )));
static assert(!is(FloatingPointTypeOf!( SubTypeOf!(Q!T) )));
@@ -5255,15 +5884,15 @@ template NumericTypeOf(T)
@safe unittest
{
- foreach (T; NumericTypeList)
- foreach (Q; TypeQualifierList)
+ static foreach (T; NumericTypeList)
+ static foreach (Q; TypeQualifierList)
{
static assert( is(Q!T == NumericTypeOf!( Q!T )));
static assert( is(Q!T == NumericTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; AliasSeq!(void, bool, CharTypeList, ImaginaryTypeList, ComplexTypeList))
- foreach (Q; TypeQualifierList)
+ static foreach (T; AliasSeq!(void, bool, CharTypeList, /*ImaginaryTypeList, ComplexTypeList*/))
+ static foreach (Q; TypeQualifierList)
{
static assert(!is(NumericTypeOf!( Q!T )));
static assert(!is(NumericTypeOf!( SubTypeOf!(Q!T) )));
@@ -5274,9 +5903,7 @@ template NumericTypeOf(T)
*/
template UnsignedTypeOf(T)
{
- import std.meta : staticIndexOf;
- static if (is(IntegralTypeOf!T X) &&
- staticIndexOf!(Unqual!X, UnsignedIntTypeList) >= 0)
+ static if (is(IntegralTypeOf!T X) && __traits(isUnsigned, X))
alias UnsignedTypeOf = X;
else
static assert(0, T.stringof~" is not an unsigned type.");
@@ -5286,9 +5913,7 @@ template UnsignedTypeOf(T)
*/
template SignedTypeOf(T)
{
- import std.meta : staticIndexOf;
- static if (is(IntegralTypeOf!T X) &&
- staticIndexOf!(Unqual!X, SignedIntTypeList) >= 0)
+ static if (is(IntegralTypeOf!T X) && !__traits(isUnsigned, X))
alias SignedTypeOf = X;
else static if (is(FloatingPointTypeOf!T X))
alias SignedTypeOf = X;
@@ -5300,13 +5925,12 @@ template SignedTypeOf(T)
*/
template CharTypeOf(T)
{
- import std.meta : staticIndexOf;
static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
alias X = CharTypeOf!AT;
else
alias X = OriginalType!T;
- static if (staticIndexOf!(Unqual!X, CharTypeList) >= 0)
+ static if (is(immutable X == immutable U, U) && is(U == char) || is(U == wchar) || is(U == dchar))
{
alias CharTypeOf = X;
}
@@ -5316,22 +5940,22 @@ template CharTypeOf(T)
@safe unittest
{
- foreach (T; CharTypeList)
- foreach (Q; TypeQualifierList)
+ static foreach (T; CharTypeList)
+ static foreach (Q; TypeQualifierList)
{
static assert( is(CharTypeOf!( Q!T )));
static assert( is(CharTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; AliasSeq!(void, bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
- foreach (Q; TypeQualifierList)
+ static foreach (T; AliasSeq!(void, bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/))
+ static foreach (Q; TypeQualifierList)
{
static assert(!is(CharTypeOf!( Q!T )));
static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) )));
}
- foreach (T; AliasSeq!(string, wstring, dstring, char[4]))
- foreach (Q; TypeQualifierList)
+ static foreach (T; AliasSeq!(string, wstring, dstring, char[4]))
+ static foreach (Q; TypeQualifierList)
{
static assert(!is(CharTypeOf!( Q!T )));
static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) )));
@@ -5347,7 +5971,7 @@ template StaticArrayTypeOf(T)
else
alias X = OriginalType!T;
- static if (is(X : E[n], E, size_t n))
+ static if (__traits(isStaticArray, X))
alias StaticArrayTypeOf = X;
else
static assert(0, T.stringof~" is not a static array type");
@@ -5355,19 +5979,19 @@ template StaticArrayTypeOf(T)
@safe unittest
{
- foreach (T; AliasSeq!(bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
- foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ static foreach (T; AliasSeq!(bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/))
+ static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
{
static assert(is( Q!( T[1] ) == StaticArrayTypeOf!( Q!( T[1] ) ) ));
- foreach (P; TypeQualifierList)
+ static foreach (P; TypeQualifierList)
{ // SubTypeOf cannot have inout type
static assert(is( Q!(P!(T[1])) == StaticArrayTypeOf!( Q!(SubTypeOf!(P!(T[1]))) ) ));
}
}
- foreach (T; AliasSeq!void)
- foreach (Q; AliasSeq!TypeQualifierList)
+ static foreach (T; AliasSeq!void)
+ static foreach (Q; AliasSeq!TypeQualifierList)
{
static assert(is( StaticArrayTypeOf!( Q!(void[1]) ) == Q!(void[1]) ));
}
@@ -5382,7 +6006,7 @@ template DynamicArrayTypeOf(T)
else
alias X = OriginalType!T;
- static if (is(Unqual!X : E[], E) && !is(typeof({ enum n = X.length; })))
+ static if (is(X == E[], E))
{
alias DynamicArrayTypeOf = X;
}
@@ -5392,13 +6016,14 @@ template DynamicArrayTypeOf(T)
@safe unittest
{
- foreach (T; AliasSeq!(/*void, */bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
- foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ import std.meta : Alias;
+ static foreach (T; AliasSeq!(/*void, */bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/))
+ static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
{
static assert(is( Q!T[] == DynamicArrayTypeOf!( Q!T[] ) ));
static assert(is( Q!(T[]) == DynamicArrayTypeOf!( Q!(T[]) ) ));
- foreach (P; AliasSeq!(MutableOf, ConstOf, ImmutableOf))
+ static foreach (P; AliasSeq!(Alias, ConstOf, ImmutableOf))
{
static assert(is( Q!(P!T[]) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!T[])) ) ));
static assert(is( Q!(P!(T[])) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!(T[]))) ) ));
@@ -5423,7 +6048,20 @@ template ArrayTypeOf(T)
}
/*
-Always returns the Dynamic Array version.
+ * Converts strings and string-like types to the corresponding dynamic array of characters.
+ * Params:
+ * T = one of the following:
+ * 1. dynamic arrays of `char`, `wchar`, or `dchar` that are implicitly convertible to `const`
+ * (`shared` is rejected)
+ * 2. static arrays of `char`, `wchar`, or `dchar` that are implicitly convertible to `const`
+ * (`shared` is rejected)
+ * 3. aggregates that use `alias this` to refer to a field that is (1), (2), or (3)
+ *
+ * Other cases are rejected with a compile time error.
+ * `typeof(null)` is rejected.
+ *
+ * Returns:
+ * The result of `[]` applied to the qualified character type.
*/
template StringTypeOf(T)
{
@@ -5447,23 +6085,24 @@ template StringTypeOf(T)
@safe unittest
{
- foreach (T; CharTypeList)
- foreach (Q; AliasSeq!(MutableOf, ConstOf, ImmutableOf, InoutOf))
+ import std.meta : Alias;
+ static foreach (T; CharTypeList)
+ static foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, InoutOf))
{
static assert(is(Q!T[] == StringTypeOf!( Q!T[] )));
static if (!__traits(isSame, Q, InoutOf))
- {
+ {{
static assert(is(Q!T[] == StringTypeOf!( SubTypeOf!(Q!T[]) )));
alias Str = Q!T[];
class C(S) { S val; alias val this; }
static assert(is(StringTypeOf!(C!Str) == Str));
- }
+ }}
}
- foreach (T; CharTypeList)
- foreach (Q; AliasSeq!(SharedOf, SharedConstOf, SharedInoutOf))
+ static foreach (T; CharTypeList)
+ static foreach (Q; AliasSeq!(SharedOf, SharedConstOf, SharedInoutOf))
{
static assert(!is(StringTypeOf!( Q!T[] )));
}
@@ -5472,6 +6111,21 @@ template StringTypeOf(T)
@safe unittest
{
static assert(is(StringTypeOf!(char[4]) == char[]));
+
+ struct S
+ {
+ string s;
+ alias s this;
+ }
+
+ struct T
+ {
+ S s;
+ alias s this;
+ }
+
+ static assert(is(StringTypeOf!S == string));
+ static assert(is(StringTypeOf!T == string));
}
/*
@@ -5483,7 +6137,7 @@ template AssocArrayTypeOf(T)
else
alias X = OriginalType!T;
- static if (is(Unqual!X : V[K], K, V))
+ static if (__traits(isAssociativeArray, X))
{
alias AssocArrayTypeOf = X;
}
@@ -5493,19 +6147,19 @@ template AssocArrayTypeOf(T)
@safe unittest
{
- foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
- foreach (P; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
- foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
- foreach (R; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ static foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
+ static foreach (P; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ static foreach (R; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
{
static assert(is( P!(Q!T[R!T]) == AssocArrayTypeOf!( P!(Q!T[R!T]) ) ));
}
- foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
- foreach (O; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
- foreach (P; AliasSeq!TypeQualifierList)
- foreach (Q; AliasSeq!TypeQualifierList)
- foreach (R; AliasSeq!TypeQualifierList)
+ static foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
+ static foreach (O; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ static foreach (P; AliasSeq!TypeQualifierList)
+ static foreach (Q; AliasSeq!TypeQualifierList)
+ static foreach (R; AliasSeq!TypeQualifierList)
{
static assert(is( O!(P!(Q!T[R!T])) == AssocArrayTypeOf!( O!(SubTypeOf!(P!(Q!T[R!T]))) ) ));
}
@@ -5515,16 +6169,21 @@ template AssocArrayTypeOf(T)
*/
template BuiltinTypeOf(T)
{
- static if (is(T : void)) alias BuiltinTypeOf = void;
- else static if (is(BooleanTypeOf!T X)) alias BuiltinTypeOf = X;
- else static if (is(IntegralTypeOf!T X)) alias BuiltinTypeOf = X;
- else static if (is(FloatingPointTypeOf!T X))alias BuiltinTypeOf = X;
- else static if (is(T : const(ireal))) alias BuiltinTypeOf = ireal; //TODO
- else static if (is(T : const(creal))) alias BuiltinTypeOf = creal; //TODO
- else static if (is(CharTypeOf!T X)) alias BuiltinTypeOf = X;
- else static if (is(ArrayTypeOf!T X)) alias BuiltinTypeOf = X;
- else static if (is(AssocArrayTypeOf!T X)) alias BuiltinTypeOf = X;
- else static assert(0);
+ static if (is(T : void))
+ alias BuiltinTypeOf = void;
+ else
+ {
+ static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
+ alias X = BuiltinTypeOf!AT;
+ else
+ alias X = OriginalType!T;
+ static if (__traits(isArithmetic, X) && !is(X == __vector) ||
+ __traits(isStaticArray, X) || is(X == E[], E) ||
+ __traits(isAssociativeArray, X))
+ alias BuiltinTypeOf = X;
+ else
+ static assert(0);
+ }
}
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
@@ -5532,9 +6191,9 @@ template BuiltinTypeOf(T)
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
/**
- * Detect whether $(D T) is a built-in boolean type.
+ * Detect whether `T` is a built-in boolean type.
*/
-enum bool isBoolean(T) = is(BooleanTypeOf!T) && !isAggregateType!T;
+enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool);
///
@safe unittest
@@ -5556,10 +6215,19 @@ enum bool isBoolean(T) = is(BooleanTypeOf!T) && !isAggregateType!T;
}
/**
- * Detect whether $(D T) is a built-in integral type. Types $(D bool),
- * $(D char), $(D wchar), and $(D dchar) are not considered integral.
+ * Detect whether `T` is a built-in integral type. Types `bool`,
+ * `char`, `wchar`, and `dchar` are not considered integral.
*/
-enum bool isIntegral(T) = is(IntegralTypeOf!T) && !isAggregateType!T;
+template isIntegral(T)
+{
+ static if (!__traits(isIntegral, T))
+ enum isIntegral = false;
+ else static if (is(T U == enum))
+ enum isIntegral = isIntegral!U;
+ else
+ enum isIntegral = __traits(isZeroInit, T) // Not char, wchar, or dchar.
+ && !is(immutable T == immutable bool) && !is(T == __vector);
+}
///
@safe unittest
@@ -5591,9 +6259,9 @@ enum bool isIntegral(T) = is(IntegralTypeOf!T) && !isAggregateType!T;
@safe unittest
{
- foreach (T; IntegralTypeList)
+ static foreach (T; IntegralTypeList)
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isIntegral!(Q!T));
static assert(!isIntegral!(SubTypeOf!(Q!T)));
@@ -5603,20 +6271,16 @@ enum bool isIntegral(T) = is(IntegralTypeOf!T) && !isAggregateType!T;
static assert(!isIntegral!float);
enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned
- enum EI : int { a = -1, b = 0, c = 1 } // base type is signed (bug 7909)
+ // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909)
+ enum EI : int { a = -1, b = 0, c = 1 }
static assert(isIntegral!EU && isUnsigned!EU && !isSigned!EU);
static assert(isIntegral!EI && !isUnsigned!EI && isSigned!EI);
}
/**
- * Detect whether $(D T) is a built-in floating point type.
+ * Detect whether `T` is a built-in floating point type.
*/
-enum bool isFloatingPoint(T) = __traits(isFloating, T) && !(is(Unqual!T == cfloat) ||
- is(Unqual!T == cdouble) ||
- is(Unqual!T == creal) ||
- is(Unqual!T == ifloat) ||
- is(Unqual!T == idouble) ||
- is(Unqual!T == ireal));
+enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real);
///
@safe unittest
@@ -5631,12 +6295,6 @@ enum bool isFloatingPoint(T) = __traits(isFloating, T) && !(is(Unqual!T == cfloa
static assert(!isFloatingPoint!int);
- // complex and imaginary numbers do not pass
- static assert(
- !isFloatingPoint!cfloat &&
- !isFloatingPoint!ifloat
- );
-
// types which act as floating point values do not pass
struct S
{
@@ -5651,43 +6309,43 @@ enum bool isFloatingPoint(T) = __traits(isFloating, T) && !(is(Unqual!T == cfloa
{
enum EF : real { a = 1.414, b = 1.732, c = 2.236 }
- foreach (T; AliasSeq!(FloatingPointTypeList, EF))
+ static foreach (T; AliasSeq!(FloatingPointTypeList, EF))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isFloatingPoint!(Q!T));
static assert(!isFloatingPoint!(SubTypeOf!(Q!T)));
}
}
- foreach (T; IntegralTypeList)
+ static foreach (T; IntegralTypeList)
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert(!isFloatingPoint!(Q!T));
}
}
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=17195
-@safe unittest
-{
- static assert(!isFloatingPoint!cfloat);
- static assert(!isFloatingPoint!cdouble);
- static assert(!isFloatingPoint!creal);
-
- static assert(!isFloatingPoint!ifloat);
- static assert(!isFloatingPoint!idouble);
- static assert(!isFloatingPoint!ireal);
+ static if (is(__vector(float[4])))
+ {
+ static assert(!isFloatingPoint!(__vector(float[4])));
+ }
}
/**
- * Detect whether $(D T) is a built-in numeric type (integral or floating
+ * Detect whether `T` is a built-in numeric type (integral or floating
* point).
*/
-enum bool isNumeric(T) = __traits(isArithmetic, T) && !(is(Unqual!T == bool) ||
- is(Unqual!T == char) ||
- is(Unqual!T == wchar) ||
- is(Unqual!T == dchar));
+template isNumeric(T)
+{
+ static if (!__traits(isArithmetic, T))
+ enum isNumeric = false;
+ else static if (__traits(isFloating, T))
+ enum isNumeric = is(T : real); // Not __vector, imaginary, or complex.
+ else static if (is(T U == enum))
+ enum isNumeric = isNumeric!U;
+ else
+ enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar.
+ && !is(immutable T == immutable bool) && !is(T == __vector);
+}
///
@safe unittest
@@ -5719,14 +6377,14 @@ enum bool isNumeric(T) = __traits(isArithmetic, T) && !(is(Unqual!T == bool) ||
alias val this;
}
- static assert(!isIntegral!S);
+ static assert(!isNumeric!S);
}
@safe unittest
{
- foreach (T; AliasSeq!(NumericTypeList))
+ static foreach (T; AliasSeq!(NumericTypeList))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isNumeric!(Q!T));
static assert(!isNumeric!(SubTypeOf!(Q!T)));
@@ -5739,13 +6397,28 @@ enum bool isNumeric(T) = __traits(isArithmetic, T) && !(is(Unqual!T == bool) ||
alias t this;
}
static assert(!isNumeric!(S!int));
+
+ enum EChar : char { a = 0, }
+ static assert(!isNumeric!EChar);
+
+ static if (is(__vector(float[4])))
+ {
+ static assert(!isNumeric!(__vector(float[4])));
+ }
+ static if (is(__vector(int[4])))
+ {
+ static assert(!isNumeric!(__vector(int[4])));
+ }
+
+ static assert(!isNumeric!ifloat);
+ static assert(!isNumeric!cfloat);
}
/**
- * Detect whether $(D T) is a scalar type (a built-in numeric, character or
+ * Detect whether `T` is a scalar type (a built-in numeric, character or
* boolean type).
*/
-enum bool isScalarType(T) = is(T : real) && !isAggregateType!T;
+enum bool isScalarType(T) = __traits(isScalar, T) && is(T : real);
///
@safe unittest
@@ -5775,9 +6448,9 @@ enum bool isScalarType(T) = is(T : real) && !isAggregateType!T;
}
/**
- * Detect whether $(D T) is a basic type (scalar type or void).
+ * Detect whether `T` is a basic type (scalar type or void).
*/
-enum bool isBasicType(T) = isScalarType!T || is(Unqual!T == void);
+enum bool isBasicType(T) = isScalarType!T || is(immutable T == immutable void);
///
@safe unittest
@@ -5798,12 +6471,18 @@ enum bool isBasicType(T) = isScalarType!T || is(Unqual!T == void);
}
/**
- * Detect whether $(D T) is a built-in unsigned numeric type.
+ * Detect whether `T` is a built-in unsigned numeric type.
*/
-enum bool isUnsigned(T) = __traits(isUnsigned, T) && !(is(Unqual!T == char) ||
- is(Unqual!T == wchar) ||
- is(Unqual!T == dchar) ||
- is(Unqual!T == bool));
+template isUnsigned(T)
+{
+ static if (!__traits(isUnsigned, T))
+ enum isUnsigned = false;
+ else static if (is(T U == enum))
+ enum isUnsigned = isUnsigned!U;
+ else
+ enum isUnsigned = __traits(isZeroInit, T) // Not char, wchar, or dchar.
+ && !is(immutable T == immutable bool) && !is(T == __vector);
+}
///
@safe unittest
@@ -5825,9 +6504,9 @@ enum bool isUnsigned(T) = __traits(isUnsigned, T) && !(is(Unqual!T == char) ||
@safe unittest
{
- foreach (T; AliasSeq!(UnsignedIntTypeList))
+ static foreach (T; AliasSeq!(UnsignedIntTypeList))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isUnsigned!(Q!T));
static assert(!isUnsigned!(SubTypeOf!(Q!T)));
@@ -5840,12 +6519,21 @@ enum bool isUnsigned(T) = __traits(isUnsigned, T) && !(is(Unqual!T == char) ||
alias t this;
}
static assert(!isUnsigned!(S!uint));
+
+ enum EChar : char { a = 0, }
+ static assert(!isUnsigned!EChar);
+
+ static if (is(__vector(uint[4])))
+ {
+ static assert(!isUnsigned!(__vector(uint[4])));
+ }
}
/**
- * Detect whether $(D T) is a built-in signed numeric type.
+ * Detect whether `T` is a built-in signed numeric type.
*/
-enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T);
+enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T)
+ && is(T : real);
///
@safe unittest
@@ -5869,9 +6557,9 @@ enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T);
enum Eubyte : ubyte { e1 = 0 }
static assert(!isSigned!Eubyte);
- foreach (T; AliasSeq!(SignedIntTypeList))
+ static foreach (T; AliasSeq!(SignedIntTypeList))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isSigned!(Q!T));
static assert(!isSigned!(SubTypeOf!(Q!T)));
@@ -5884,6 +6572,14 @@ enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T);
alias t this;
}
static assert(!isSigned!(S!uint));
+
+ static if (is(__vector(int[4])))
+ {
+ static assert(!isSigned!(__vector(int[4])));
+ }
+
+ static assert(!isSigned!ifloat);
+ static assert(!isSigned!cfloat);
}
// https://issues.dlang.org/show_bug.cgi?id=17196
@@ -5894,12 +6590,20 @@ enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T);
}
/**
- * Detect whether $(D T) is one of the built-in character types.
+ * Detect whether `T` is one of the built-in character types.
*
- * The built-in char types are any of $(D char), $(D wchar) or $(D dchar), with
+ * The built-in char types are any of `char`, `wchar` or `dchar`, with
* or without qualifiers.
*/
-enum bool isSomeChar(T) = is(CharTypeOf!T) && !isAggregateType!T;
+template isSomeChar(T)
+{
+ static if (!__traits(isUnsigned, T))
+ enum isSomeChar = false;
+ else static if (is(T U == enum))
+ enum isSomeChar = isSomeChar!U;
+ else
+ enum isSomeChar = !__traits(isZeroInit, T);
+}
///
@safe unittest
@@ -5925,9 +6629,9 @@ enum bool isSomeChar(T) = is(CharTypeOf!T) && !isAggregateType!T;
{
enum EC : char { a = 'x', b = 'y' }
- foreach (T; AliasSeq!(CharTypeList, EC))
+ static foreach (T; AliasSeq!(CharTypeList, EC))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isSomeChar!( Q!T ));
static assert(!isSomeChar!( SubTypeOf!(Q!T) ));
@@ -5944,15 +6648,15 @@ enum bool isSomeChar(T) = is(CharTypeOf!T) && !isAggregateType!T;
}
/**
-Detect whether $(D T) is one of the built-in string types.
+Detect whether `T` is one of the built-in string types.
-The built-in string types are $(D Char[]), where $(D Char) is any of $(D char),
-$(D wchar) or $(D dchar), with or without qualifiers.
+The built-in string types are `Char[]`, where `Char` is any of `char`,
+`wchar` or `dchar`, with or without qualifiers.
-Static arrays of characters (like $(D char[80])) are not considered
+Static arrays of characters (like `char[80]`) are not considered
built-in string types.
*/
-enum bool isSomeString(T) = is(StringTypeOf!T) && !isAggregateType!T && !isStaticArray!T;
+enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar));
///
@safe unittest
@@ -5964,33 +6668,42 @@ enum bool isSomeString(T) = is(StringTypeOf!T) && !isAggregateType!T && !isStati
static assert( isSomeString!(typeof("aaa")));
static assert( isSomeString!(const(char)[]));
- enum ES : string { a = "aaa", b = "bbb" }
- static assert( isSomeString!ES);
-
//Non string types
static assert(!isSomeString!int);
static assert(!isSomeString!(int[]));
static assert(!isSomeString!(byte[]));
static assert(!isSomeString!(typeof(null)));
static assert(!isSomeString!(char[4]));
+
+ enum ES : string { a = "aaa", b = "bbb" }
+ static assert(!isSomeString!ES);
+
+ static struct Stringish
+ {
+ string str;
+ alias str this;
+ }
+ static assert(!isSomeString!Stringish);
}
@safe unittest
{
- foreach (T; AliasSeq!(char[], dchar[], string, wstring, dstring))
+ static foreach (T; AliasSeq!(char[], dchar[], string, wstring, dstring))
{
static assert( isSomeString!( T ));
static assert(!isSomeString!(SubTypeOf!(T)));
}
+ enum C : char { _ = 0 }
+ static assert(!isSomeString!(C[]));
}
/**
- * Detect whether type $(D T) is a narrow string.
+ * Detect whether type `T` is a narrow string.
*
* All arrays that use char, wchar, and their qualified versions are narrow
* strings. (Those include string and wstring).
*/
-enum bool isNarrowString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isAggregateType!T && !isStaticArray!T;
+enum bool isNarrowString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar));
///
@safe unittest
@@ -6002,41 +6715,56 @@ enum bool isNarrowString(T) = (is(T : const char[]) || is(T : const wchar[])) &&
static assert(!isNarrowString!dstring);
static assert(!isNarrowString!(dchar[]));
+
+ static assert(!isNarrowString!(typeof(null)));
+ static assert(!isNarrowString!(char[4]));
+
+ enum ES : string { a = "aaa", b = "bbb" }
+ static assert(!isNarrowString!ES);
+
+ static struct Stringish
+ {
+ string str;
+ alias str this;
+ }
+ static assert(!isNarrowString!Stringish);
}
@safe unittest
{
- foreach (T; AliasSeq!(char[], string, wstring))
+ import std.meta : Alias;
+ static foreach (T; AliasSeq!(char[], string, wstring))
{
- foreach (Q; AliasSeq!(MutableOf, ConstOf, ImmutableOf)/*TypeQualifierList*/)
+ static foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf)/*TypeQualifierList*/)
{
static assert( isNarrowString!( Q!T ));
static assert(!isNarrowString!( SubTypeOf!(Q!T) ));
}
}
- foreach (T; AliasSeq!(int, int[], byte[], dchar[], dstring, char[4]))
+ static foreach (T; AliasSeq!(int, int[], byte[], dchar[], dstring, char[4]))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert(!isNarrowString!( Q!T ));
static assert(!isNarrowString!( SubTypeOf!(Q!T) ));
}
}
+ enum C : char { _ = 0 }
+ static assert(!isNarrowString!(C[]));
}
/**
* Detects whether `T` is a comparable type. Basic types and structs and
* classes that implement opCmp are ordering comparable.
*/
-enum bool isOrderingComparable(T) = ifTestable!(T, unaryFun!"a < a");
+enum bool isOrderingComparable(T) = is(typeof((ref T a) => a < a ? 1 : 0));
///
@safe unittest
{
static assert(isOrderingComparable!int);
static assert(isOrderingComparable!string);
- static assert(!isOrderingComparable!creal);
static struct Foo {}
static assert(!isOrderingComparable!Foo);
@@ -6053,13 +6781,12 @@ enum bool isOrderingComparable(T) = ifTestable!(T, unaryFun!"a < a");
}
/// ditto
-enum bool isEqualityComparable(T) = ifTestable!(T, unaryFun!"a == a");
+enum bool isEqualityComparable(T) = is(typeof((ref T a) => a == a ? 1 : 0));
@safe unittest
{
static assert(isEqualityComparable!int);
static assert(isEqualityComparable!string);
- static assert(isEqualityComparable!creal);
static assert(!isEqualityComparable!void);
struct Foo {}
@@ -6080,8 +6807,27 @@ enum bool isEqualityComparable(T) = ifTestable!(T, unaryFun!"a == a");
}
/**
- * Detect whether $(D T) is a struct, static array, or enum that is implicitly
- * convertible to a string.
+ $(RED Warning: This trait will be deprecated as soon as it is no longer used
+ in Phobos. For a function parameter to safely accept a type
+ that implicitly converts to string as a string, the conversion
+ needs to happen at the callsite; otherwise, the conversion is
+ done inside the function, and in many cases, that means that
+ local memory is sliced (e.g. if a static array is passed to
+ the function, then it's copied, and the resulting dynamic
+ array will be a slice of a local variable). So, if the
+ resulting string escapes the function, the string refers to
+ invalid memory, and accessing it would mean accessing invalid
+ memory. As such, the only safe way for a function to accept
+ types that implicitly convert to string is for the implicit
+ conversion to be done at the callsite, and that can only occur
+ if the parameter is explicitly typed as an array, whereas
+ using isConvertibleToString in a template constraint would
+ result in the conversion being done inside the function. As
+ such, isConvertibleToString is inherently unsafe and is going
+ to be deprecated.)
+
+ Detect whether `T` is a struct, static array, or enum that is implicitly
+ convertible to a string.
*/
template isConvertibleToString(T)
{
@@ -6108,7 +6854,8 @@ template isConvertibleToString(T)
assert(!isConvertibleToString!(char[]));
}
-@safe unittest // Bugzilla 16573
+// https://issues.dlang.org/show_bug.cgi?id=16573
+@safe unittest
{
enum I : int { foo = 1 }
enum S : string { foo = "foo" }
@@ -6125,11 +6872,21 @@ package template convertToString(T)
}
/**
- * Detect whether type $(D T) is a string that will be autodecoded.
+ * Detect whether type `T` is a string that will be autodecoded.
*
- * All arrays that use char, wchar, and their qualified versions are narrow
- * strings. (Those include string and wstring).
- * Aggregates that implicitly cast to narrow strings are included.
+ * Given a type `S` that is one of:
+ * $(OL
+ * $(LI `const(char)[]`)
+ * $(LI `const(wchar)[]`)
+ * )
+ * Type `T` can be one of:
+ * $(OL
+ * $(LI `S`)
+ * $(LI implicitly convertible to `T`)
+ * $(LI an enum with a base type `T`)
+ * $(LI an aggregate with a base type `T`)
+ * )
+ * with the proviso that `T` cannot be a static array.
*
* Params:
* T = type to be tested
@@ -6140,7 +6897,13 @@ package template convertToString(T)
* See Also:
* $(LREF isNarrowString)
*/
-enum bool isAutodecodableString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isStaticArray!T;
+template isAutodecodableString(T)
+{
+ import std.range.primitives : autodecodeStrings;
+
+ enum isAutodecodableString = autodecodeStrings &&
+ (is(T : const char[]) || is(T : const wchar[])) && !is(T : U[n], U, size_t n);
+}
///
@safe unittest
@@ -6150,13 +6913,34 @@ enum bool isAutodecodableString(T) = (is(T : const char[]) || is(T : const wchar
string s;
alias s this;
}
- assert(isAutodecodableString!wstring);
- assert(isAutodecodableString!Stringish);
- assert(!isAutodecodableString!dstring);
+ static assert(isAutodecodableString!wstring);
+ static assert(isAutodecodableString!Stringish);
+ static assert(!isAutodecodableString!dstring);
+
+ enum E : const(char)[3] { X = "abc" }
+ enum F : const(char)[] { X = "abc" }
+ enum G : F { X = F.init }
+
+ static assert(isAutodecodableString!(char[]));
+ static assert(!isAutodecodableString!(E));
+ static assert(isAutodecodableString!(F));
+ static assert(isAutodecodableString!(G));
+
+ struct Stringish2
+ {
+ Stringish s;
+ alias s this;
+ }
+
+ enum H : Stringish { X = Stringish() }
+ enum I : Stringish2 { X = Stringish2() }
+
+ static assert(isAutodecodableString!(H));
+ static assert(isAutodecodableString!(I));
}
/**
- * Detect whether type $(D T) is a static array.
+ * Detect whether type `T` is a static array.
*/
enum bool isStaticArray(T) = __traits(isStaticArray, T);
@@ -6179,11 +6963,11 @@ enum bool isStaticArray(T) = __traits(isStaticArray, T);
@safe unittest
{
- foreach (T; AliasSeq!(int[51], int[][2],
+ static foreach (T; AliasSeq!(int[51], int[][2],
char[][int][11], immutable char[13u],
const(real)[1], const(real)[1][1], void[0]))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isStaticArray!( Q!T ));
static assert(!isStaticArray!( SubTypeOf!(Q!T) ));
@@ -6195,9 +6979,21 @@ enum bool isStaticArray(T) = __traits(isStaticArray, T);
}
/**
- * Detect whether type $(D T) is a dynamic array.
+ * Detect whether type `T` is a dynamic array.
*/
-enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T;
+template isDynamicArray(T)
+{
+ static if (is(T == U[], U))
+ enum bool isDynamicArray = true;
+ else static if (is(T U == enum))
+ // BUG: isDynamicArray / isStaticArray considers enums
+ // with appropriate base types as dynamic/static arrays
+ // Retain old behaviour for now, see
+ // https://github.com/dlang/phobos/pull/7574
+ enum bool isDynamicArray = isDynamicArray!U;
+ else
+ enum bool isDynamicArray = false;
+}
///
@safe unittest
@@ -6213,18 +7009,36 @@ enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T;
@safe unittest
{
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][]))
+ static foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][]))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isDynamicArray!( Q!T ));
static assert(!isDynamicArray!( SubTypeOf!(Q!T) ));
}
}
+
+ static assert(!isDynamicArray!(int[5]));
+
+ static struct AliasThis
+ {
+ int[] values;
+ alias values this;
+ }
+
+ static assert(!isDynamicArray!AliasThis);
+
+ // https://github.com/dlang/phobos/pull/7574/files#r464115492
+ enum E : string
+ {
+ a = "a",
+ b = "b",
+ }
+ static assert( isDynamicArray!E);
}
/**
- * Detect whether type $(D T) is an array (static or dynamic; for associative
+ * Detect whether type `T` is an array (static or dynamic; for associative
* arrays see $(LREF isAssociativeArray)).
*/
enum bool isArray(T) = isStaticArray!T || isDynamicArray!T;
@@ -6244,9 +7058,9 @@ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T;
@safe unittest
{
import std.meta : AliasSeq;
- foreach (T; AliasSeq!(int[], int[5], void[]))
+ static foreach (T; AliasSeq!(int[], int[5], void[]))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isArray!(Q!T));
static assert(!isArray!(SubTypeOf!(Q!T)));
@@ -6255,7 +7069,7 @@ enum bool isArray(T) = isStaticArray!T || isDynamicArray!T;
}
/**
- * Detect whether $(D T) is an associative array type
+ * Detect whether `T` is an associative array type
*/
enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T);
@@ -6267,9 +7081,9 @@ enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T);
@property uint[] values() { return null; }
}
- foreach (T; AliasSeq!(int[int], int[string], immutable(char[5])[int]))
+ static foreach (T; AliasSeq!(int[int], int[string], immutable(char[5])[int]))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isAssociativeArray!(Q!T));
static assert(!isAssociativeArray!(SubTypeOf!(Q!T)));
@@ -6286,7 +7100,7 @@ enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T);
}
/**
- * Detect whether type $(D T) is a builtin type.
+ * Detect whether type `T` is a builtin type.
*/
enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T;
@@ -6310,7 +7124,7 @@ enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T;
}
/**
- * Detect whether type $(D T) is a SIMD vector type.
+ * Detect whether type `T` is a SIMD vector type.
*/
enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N);
@@ -6327,15 +7141,15 @@ enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N);
}
/**
- * Detect whether type $(D T) is a pointer.
+ * Detect whether type `T` is a pointer.
*/
-enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T;
+enum bool isPointer(T) = is(T == U*, U) && __traits(isScalar, T);
@safe unittest
{
- foreach (T; AliasSeq!(int*, void*, char[]*))
+ static foreach (T; AliasSeq!(int*, void*, char[]*))
{
- foreach (Q; TypeQualifierList)
+ static foreach (Q; TypeQualifierList)
{
static assert( isPointer!(Q!T));
static assert(!isPointer!(SubTypeOf!(Q!T)));
@@ -6361,7 +7175,7 @@ alias PointerTarget(T : T*) = T;
}
/**
- * Detect whether type $(D T) is an aggregate type.
+ * Detect whether type `T` is an aggregate type.
*/
enum bool isAggregateType(T) = is(T == struct) || is(T == union) ||
is(T == class) || is(T == interface);
@@ -6386,10 +7200,10 @@ enum bool isAggregateType(T) = is(T == struct) || is(T == union) ||
}
/**
- * Returns $(D true) if T can be iterated over using a $(D foreach) loop with
+ * Returns `true` if T can be iterated over using a `foreach` loop with
* a single loop variable of automatically inferred type, regardless of how
- * the $(D foreach) loop is implemented. This includes ranges, structs/classes
- * that define $(D opApply) with a single loop variable, and builtin dynamic,
+ * the `foreach` loop is implemented. This includes ranges, structs/classes
+ * that define `opApply` with a single loop variable, and builtin dynamic,
* static and associative arrays.
*/
enum bool isIterable(T) = is(typeof({ foreach (elem; T.init) {} }));
@@ -6470,6 +7284,30 @@ template isInstanceOf(alias S, alias T)
static assert(isInstanceOf!(templ, templ!int));
}
+/**
+ * To use `isInstanceOf` to check the identity of a template while inside of said
+ * template, use $(LREF TemplateOf).
+ */
+@safe unittest
+{
+ static struct A(T = void)
+ {
+ // doesn't work as expected, only accepts A when T = void
+ void func(B)(B b) if (isInstanceOf!(A, B)) {}
+
+ // correct behavior
+ void method(B)(B b) if (isInstanceOf!(TemplateOf!(A), B)) {}
+ }
+
+ A!(void) a1;
+ A!(void) a2;
+ A!(int) a3;
+
+ static assert(!__traits(compiles, a1.func(a3)));
+ static assert( __traits(compiles, a1.method(a2)));
+ static assert( __traits(compiles, a1.method(a3)));
+}
+
@safe unittest
{
static void fun1(T)() { }
@@ -6487,17 +7325,20 @@ template isInstanceOf(alias S, alias T)
*
* See_Also: $(LREF isTypeTuple).
*/
-template isExpressions(T ...)
+template isExpressions(T...)
{
- static if (T.length >= 2)
- enum bool isExpressions =
- isExpressions!(T[0 .. $/2]) &&
- isExpressions!(T[$/2 .. $]);
- else static if (T.length == 1)
- enum bool isExpressions =
- !is(T[0]) && __traits(compiles, { auto ex = T[0]; });
- else
- enum bool isExpressions = true; // default
+ static foreach (Ti; T)
+ {
+ static if (!is(typeof(isExpressions) == bool) && // not yet defined
+ (is(Ti) || !__traits(compiles, { auto ex = Ti; })))
+ {
+ enum isExpressions = false;
+ }
+ }
+ static if (!is(typeof(isExpressions) == bool)) // if not yet defined
+ {
+ enum isExpressions = true;
+ }
}
///
@@ -6535,7 +7376,7 @@ alias isExpressionTuple = isExpressions;
/**
- * Check whether the tuple $(D T) is a type tuple.
+ * Check whether the tuple `T` is a type tuple.
* A type tuple only contains types.
*
* See_Also: $(LREF isExpressions).
@@ -6578,21 +7419,9 @@ template isTypeTuple(T...)
/**
-Detect whether symbol or type $(D T) is a function pointer.
+Detect whether symbol or type `T` is a function pointer.
*/
-template isFunctionPointer(T...)
- if (T.length == 1)
-{
- static if (is(T[0] U) || is(typeof(T[0]) U))
- {
- static if (is(U F : F*) && is(F == function))
- enum bool isFunctionPointer = true;
- else
- enum bool isFunctionPointer = false;
- }
- else
- enum bool isFunctionPointer = false;
-}
+enum bool isFunctionPointer(alias T) = is(typeof(*T) == function);
///
@safe unittest
@@ -6614,24 +7443,9 @@ template isFunctionPointer(T...)
}
/**
-Detect whether symbol or type $(D T) is a delegate.
+Detect whether symbol or type `T` is a delegate.
*/
-template isDelegate(T...)
- if (T.length == 1)
-{
- static if (is(typeof(& T[0]) U : U*) && is(typeof(& T[0]) U == delegate))
- {
- // T is a (nested) function symbol.
- enum bool isDelegate = true;
- }
- else static if (is(T[0] W) || is(typeof(T[0]) W))
- {
- // T is an expression or a type. Take the type of it and examine.
- enum bool isDelegate = is(W == delegate);
- }
- else
- enum bool isDelegate = false;
-}
+enum bool isDelegate(alias T) = is(typeof(T) == delegate) || is(T == delegate);
///
@safe unittest
@@ -6652,10 +7466,15 @@ template isDelegate(T...)
}
/**
-Detect whether symbol or type $(D T) is a function, a function pointer or a delegate.
+Detect whether symbol or type `T` is a function, a function pointer or a delegate.
+
+Params:
+ T = The type to check
+Returns:
+ A `bool`
*/
template isSomeFunction(T...)
- if (T.length == 1)
+if (T.length == 1)
{
static if (is(typeof(& T[0]) U : U*) && is(U == function) || is(typeof(& T[0]) U == delegate))
{
@@ -6674,12 +7493,11 @@ template isSomeFunction(T...)
enum bool isSomeFunction = false;
}
+///
@safe unittest
{
static real func(ref int) { return 0; }
static void prop() @property { }
- void nestedFunc() { }
- void nestedProp() @property { }
class C
{
real method(ref int) { return 0; }
@@ -6692,69 +7510,126 @@ template isSomeFunction(T...)
static assert( isSomeFunction!func);
static assert( isSomeFunction!prop);
- static assert( isSomeFunction!nestedFunc);
- static assert( isSomeFunction!nestedProp);
static assert( isSomeFunction!(C.method));
static assert( isSomeFunction!(C.prop));
static assert( isSomeFunction!(c.prop));
static assert( isSomeFunction!(c.prop));
static assert( isSomeFunction!fp);
static assert( isSomeFunction!dg);
- static assert( isSomeFunction!(typeof(func)));
- static assert( isSomeFunction!(real function(ref int)));
- static assert( isSomeFunction!(real delegate(ref int)));
- static assert( isSomeFunction!((int a) { return a; }));
static assert(!isSomeFunction!int);
static assert(!isSomeFunction!val);
- static assert(!isSomeFunction!isSomeFunction);
}
+@safe unittest
+{
+ void nestedFunc() { }
+ void nestedProp() @property { }
+ static assert(isSomeFunction!nestedFunc);
+ static assert(isSomeFunction!nestedProp);
+ static assert(isSomeFunction!(real function(ref int)));
+ static assert(isSomeFunction!(real delegate(ref int)));
+ static assert(isSomeFunction!((int a) { return a; }));
+ static assert(!isSomeFunction!isSomeFunction);
+}
/**
-Detect whether $(D T) is a callable object, which can be called with the
-function call operator $(D $(LPAREN)...$(RPAREN)).
+Detect whether `T` is a callable object, which can be called with the
+function call operator `$(LPAREN)...$(RPAREN)`.
*/
-template isCallable(T...)
- if (T.length == 1)
+template isCallable(alias callable)
{
- static if (is(typeof(& T[0].opCall) == delegate))
+ static if (is(typeof(&callable.opCall) == delegate))
// T is a object which has a member function opCall().
enum bool isCallable = true;
- else static if (is(typeof(& T[0].opCall) V : V*) && is(V == function))
+ else static if (is(typeof(&callable.opCall) V : V*) && is(V == function))
// T is a type which has a static member function opCall().
enum bool isCallable = true;
+ else static if (is(typeof(&callable.opCall!())))
+ {
+ alias TemplateInstanceType = typeof(&callable.opCall!());
+ enum bool isCallable = isCallable!TemplateInstanceType;
+ }
+ else static if (is(typeof(&callable!())))
+ {
+ alias TemplateInstanceType = typeof(&callable!());
+ enum bool isCallable = isCallable!TemplateInstanceType;
+ }
else
- enum bool isCallable = isSomeFunction!T;
+ {
+ enum bool isCallable = isSomeFunction!callable;
+ }
}
-///
+/// Functions, lambdas, and aggregate types with (static) opCall.
@safe unittest
{
- interface I { real value() @property; }
- struct S { static int opCall(int) { return 0; } }
+ void f() { }
+ int g(int x) { return x; }
+
+ static assert( isCallable!f);
+ static assert( isCallable!g);
+
class C { int opCall(int) { return 0; } }
auto c = new C;
+ struct S { static int opCall(int) { return 0; } }
+ interface I { real value() @property; }
static assert( isCallable!c);
- static assert( isCallable!S);
static assert( isCallable!(c.opCall));
+ static assert( isCallable!S);
static assert( isCallable!(I.value));
static assert( isCallable!((int a) { return a; }));
static assert(!isCallable!I);
}
+/// Templates
+@safe unittest
+{
+ void f()() { }
+ T g(T = int)(T x) { return x; }
+ struct S1 { static void opCall()() { } }
+ struct S2 { static T opCall(T = int)(T x) {return x; } }
+
+ static assert( isCallable!f);
+ static assert( isCallable!g);
+ static assert( isCallable!S1);
+ static assert( isCallable!S2);
+}
+
+/// Overloaded functions and function templates.
+@safe unittest
+{
+ static struct Wrapper
+ {
+ void f() { }
+ int f(int x) { return x; }
+
+ void g()() { }
+ T g(T = int)(T x) { return x; }
+ }
+
+ static assert(isCallable!(Wrapper.f));
+ static assert(isCallable!(Wrapper.g));
+}
+
/**
- * Detect whether $(D T) is an abstract function.
+Detect whether `T` is an abstract function.
+
+Params:
+ T = The type to check
+Returns:
+ A `bool`
*/
template isAbstractFunction(T...)
- if (T.length == 1)
+if (T.length == 1)
{
enum bool isAbstractFunction = __traits(isAbstractFunction, T[0]);
}
+///
@safe unittest
{
struct S { void foo() { } }
@@ -6767,10 +7642,10 @@ template isAbstractFunction(T...)
}
/**
- * Detect whether $(D T) is a final function.
+ * Detect whether `T` is a final function.
*/
template isFinalFunction(T...)
- if (T.length == 1)
+if (T.length == 1)
{
enum bool isFinalFunction = __traits(isFinalFunction, T[0]);
}
@@ -6793,26 +7668,56 @@ template isFinalFunction(T...)
}
/**
-Determines whether function $(D f) requires a context pointer.
+Determines if `f` is a function that requires a context pointer.
+
+Params:
+ f = The type to check
+Returns
+ A `bool`
*/
template isNestedFunction(alias f)
{
- enum isNestedFunction = __traits(isNested, f);
+ enum isNestedFunction = __traits(isNested, f) && isSomeFunction!(f);
}
+///
@safe unittest
{
- static void f() { }
- void g() { }
+ static void f() {}
+ static void fun()
+ {
+ int i;
+ int f() { return i; }
+
+ static assert(isNestedFunction!(f));
+ }
+
static assert(!isNestedFunction!f);
- static assert( isNestedFunction!g);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18669
+@safe unittest
+{
+ static class Outer
+ {
+ class Inner
+ {
+ }
+ }
+ int i;
+ struct SS
+ {
+ int bar() { return i; }
+ }
+ static assert(!isNestedFunction!(Outer.Inner));
+ static assert(!isNestedFunction!(SS));
}
/**
- * Detect whether $(D T) is an abstract class.
+ * Detect whether `T` is an abstract class.
*/
template isAbstractClass(T...)
- if (T.length == 1)
+if (T.length == 1)
{
enum bool isAbstractClass = __traits(isAbstractClass, T[0]);
}
@@ -6833,10 +7738,10 @@ template isAbstractClass(T...)
}
/**
- * Detect whether $(D T) is a final class.
+ * Detect whether `T` is a final class.
*/
template isFinalClass(T...)
- if (T.length == 1)
+if (T.length == 1)
{
enum bool isFinalClass = __traits(isFinalClass, T[0]);
}
@@ -6863,30 +7768,47 @@ template isFinalClass(T...)
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
/**
-Removes all qualifiers, if any, from type $(D T).
+Removes `const`, `inout` and `immutable` qualifiers, if any, from type `T`.
+ */
+template Unconst(T)
+{
+ import core.internal.traits : CoreUnconst = Unconst;
+ alias Unconst = CoreUnconst!(T);
+}
+
+///
+@safe unittest
+{
+ static assert(is(Unconst!int == int));
+ static assert(is(Unconst!(const int) == int));
+ static assert(is(Unconst!(immutable int) == int));
+ static assert(is(Unconst!(shared int) == shared int));
+ static assert(is(Unconst!(shared(const int)) == shared int));
+}
+
+@safe unittest
+{
+ static assert(is(Unconst!( int) == int));
+ static assert(is(Unconst!( const int) == int));
+ static assert(is(Unconst!( inout int) == int));
+ static assert(is(Unconst!( inout const int) == int));
+ static assert(is(Unconst!(shared int) == shared int));
+ static assert(is(Unconst!(shared const int) == shared int));
+ static assert(is(Unconst!(shared inout int) == shared int));
+ static assert(is(Unconst!(shared inout const int) == shared int));
+ static assert(is(Unconst!( immutable int) == int));
+
+ alias ImmIntArr = immutable(int[]);
+ static assert(is(Unconst!ImmIntArr == immutable(int)[]));
+}
+
+/**
+Removes all qualifiers, if any, from type `T`.
*/
template Unqual(T)
{
- version (none) // Error: recursive alias declaration @@@BUG1308@@@
- {
- static if (is(T U == const U)) alias Unqual = Unqual!U;
- else static if (is(T U == immutable U)) alias Unqual = Unqual!U;
- else static if (is(T U == inout U)) alias Unqual = Unqual!U;
- else static if (is(T U == shared U)) alias Unqual = Unqual!U;
- else alias Unqual = T;
- }
- else // workaround
- {
- static if (is(T U == immutable U)) alias Unqual = U;
- else static if (is(T U == shared inout const U)) alias Unqual = U;
- else static if (is(T U == shared inout U)) alias Unqual = U;
- else static if (is(T U == shared const U)) alias Unqual = U;
- else static if (is(T U == shared U)) alias Unqual = U;
- else static if (is(T U == inout const U)) alias Unqual = U;
- else static if (is(T U == inout U)) alias Unqual = U;
- else static if (is(T U == const U)) alias Unqual = U;
- else alias Unqual = T;
- }
+ import core.internal.traits : CoreUnqual = Unqual;
+ alias Unqual = CoreUnqual!(T);
}
///
@@ -6944,14 +7866,14 @@ package template ModifyTypePreservingTQ(alias Modifier, T)
}
/**
- * Copies type qualifiers from $(D FromType) to $(D ToType).
+ * Copies type qualifiers from `FromType` to `ToType`.
*
* Supported type qualifiers:
* $(UL
- * $(LI $(D const))
- * $(LI $(D inout))
- * $(LI $(D immutable))
- * $(LI $(D shared))
+ * $(LI `const`)
+ * $(LI `inout`)
+ * $(LI `immutable`)
+ * $(LI `shared`)
* )
*/
template CopyTypeQualifiers(FromType, ToType)
@@ -6980,9 +7902,9 @@ template CopyTypeQualifiers(FromType, ToType)
}
/**
-Returns the type of `Target` with the "constness" of `Source`. A type's $(B constness)
-refers to whether it is `const`, `immutable`, or `inout`. If `source` has no constness, the
-returned type will be the same as `Target`.
+Returns the type of `ToType` with the "constness" of `FromType`. A type's $(B constness)
+refers to whether it is `const`, `immutable`, or `inout`. If `FromType` has no constness, the
+returned type will be the same as `ToType`.
*/
template CopyConstness(FromType, ToType)
{
@@ -7054,9 +7976,9 @@ template CopyConstness(FromType, ToType)
/**
Returns the inferred type of the loop variable when a variable of type T
-is iterated over using a $(D foreach) loop with a single loop variable and
+is iterated over using a `foreach` loop with a single loop variable and
automatically inferred return type. Note that this may not be the same as
-$(D std.range.ElementType!Range) in the case of narrow strings, or if T
+`std.range.ElementType!Range` in the case of narrow strings, or if T
has both opApply and a range interface.
*/
template ForeachType(T)
@@ -7083,23 +8005,30 @@ template ForeachType(T)
/**
- * Strips off all $(D enum)s from type $(D T).
+ * Strips off all `enum`s from type `T`.
*/
template OriginalType(T)
{
- template Impl(T)
+ static if (is(T == enum))
{
- static if (is(T U == enum)) alias Impl = OriginalType!U;
- else alias Impl = T;
- }
+ template Impl(T)
+ {
+ static if (is(T U == enum)) alias Impl = OriginalType!U;
+ else alias Impl = T;
+ }
- alias OriginalType = ModifyTypePreservingTQ!(Impl, T);
+ alias OriginalType = ModifyTypePreservingTQ!(Impl, T);
+ }
+ else
+ {
+ alias OriginalType = T;
+ }
}
///
@safe unittest
{
- enum E : real { a }
+ enum E : real { a = 0 } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
enum F : E { a = E.a }
alias G = const(F);
static assert(is(OriginalType!E == real));
@@ -7115,7 +8044,6 @@ alias KeyType(V : V[K], K) = K;
///
@safe unittest
{
- import std.traits;
alias Hash = int[string];
static assert(is(KeyType!Hash == string));
static assert(is(ValueType!Hash == int));
@@ -7131,7 +8059,6 @@ alias ValueType(V : V[K], K) = V;
///
@safe unittest
{
- import std.traits;
alias Hash = int[string];
static assert(is(KeyType!Hash == string));
static assert(is(ValueType!Hash == int));
@@ -7140,8 +8067,14 @@ alias ValueType(V : V[K], K) = V;
}
/**
- * Returns the corresponding unsigned type for T. T must be a numeric
- * integral type, otherwise a compile-time error occurs.
+Params:
+ T = A built in integral or vector type.
+
+Returns:
+ The corresponding unsigned numeric type for `T` with the
+ same type qualifiers.
+
+ If `T` is not a integral or vector, a compile-time error is given.
*/
template Unsigned(T)
{
@@ -7167,6 +8100,27 @@ template Unsigned(T)
alias Unsigned = ModifyTypePreservingTQ!(Impl, OriginalType!T);
}
+///
+@safe unittest
+{
+ static assert(is(Unsigned!(int) == uint));
+ static assert(is(Unsigned!(long) == ulong));
+ static assert(is(Unsigned!(const short) == const ushort));
+ static assert(is(Unsigned!(immutable byte) == immutable ubyte));
+ static assert(is(Unsigned!(inout int) == inout uint));
+}
+
+
+/// Unsigned types are forwarded
+@safe unittest
+{
+ static assert(is(Unsigned!(uint) == uint));
+ static assert(is(Unsigned!(const uint) == const uint));
+
+ static assert(is(Unsigned!(ubyte) == ubyte));
+ static assert(is(Unsigned!(immutable uint) == immutable uint));
+}
+
@safe unittest
{
alias U1 = Unsigned!int;
@@ -7201,7 +8155,8 @@ Returns the largest type, i.e. T such that T.sizeof is the largest. If more
than one type is of the same size, the leftmost argument of these in will be
returned.
*/
-template Largest(T...) if (T.length >= 1)
+template Largest(T...)
+if (T.length >= 1)
{
static if (T.length == 1)
{
@@ -7296,7 +8251,7 @@ template Signed(T)
Returns the most negative value of the numeric type T.
*/
template mostNegative(T)
- if (isNumeric!T || isSomeChar!T || isBoolean!T)
+if (isNumeric!T || isSomeChar!T || isBoolean!T)
{
static if (is(typeof(T.min_normal)))
enum mostNegative = -T.max;
@@ -7318,10 +8273,12 @@ template mostNegative(T)
///
@safe unittest
{
- foreach (T; AliasSeq!(bool, byte, short, int, long))
+ import std.meta : AliasSeq;
+
+ static foreach (T; AliasSeq!(bool, byte, short, int, long))
static assert(mostNegative!T == T.min);
- foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, char, wchar, dchar))
+ static foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, char, wchar, dchar))
static assert(mostNegative!T == 0);
}
@@ -7330,7 +8287,7 @@ Get the type that a scalar type `T` will $(LINK2 $(ROOT_DIR)spec/type.html#integ
to in multi-term arithmetic expressions.
*/
template Promoted(T)
- if (isScalarType!T)
+if (isScalarType!T)
{
alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init));
}
@@ -7350,14 +8307,14 @@ template Promoted(T)
@safe unittest
{
// promote to int:
- foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, char, wchar))
+ static foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, char, wchar))
{
static assert(is(Promoted!T == int));
static assert(is(Promoted!(shared(const T)) == shared(const int)));
}
// already promoted:
- foreach (T; AliasSeq!(int, uint, long, ulong, float, double, real))
+ static foreach (T; AliasSeq!(int, uint, long, ulong, float, double, real))
{
static assert(is(Promoted!T == T));
static assert(is(Promoted!(immutable(T)) == immutable(T)));
@@ -7369,14 +8326,14 @@ template Promoted(T)
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
/**
-Returns the mangled name of symbol or type $(D sth).
+Returns the mangled name of symbol or type `sth`.
-$(D mangledName) is the same as builtin $(D .mangleof) property, but
+`mangledName` is the same as builtin `.mangleof` property, but
might be more convenient in generic code, e.g. as a template argument
when invoking staticMap.
*/
template mangledName(sth...)
- if (sth.length == 1)
+if (sth.length == 1)
{
enum string mangledName = sth[0].mangleof;
}
@@ -7384,11 +8341,12 @@ template mangledName(sth...)
///
@safe unittest
{
+ import std.meta : AliasSeq;
alias TL = staticMap!(mangledName, int, const int, immutable int);
static assert(TL == AliasSeq!("i", "xi", "yi"));
}
-version (unittest) void freeFunc(string);
+version (StdUnittest) private void freeFunc(string);
@safe unittest
{
@@ -7400,7 +8358,8 @@ version (unittest) void freeFunc(string);
static assert(mangledName!mangledName == "3std6traits11mangledName");
static assert(mangledName!freeFunc == "_D3std6traits8freeFuncFAyaZv");
int x;
- static if (is(typeof({ return x; }) : int delegate() pure)) // issue 9148
+ // https://issues.dlang.org/show_bug.cgi?id=9148
+ static if (is(typeof({ return x; }) : int delegate() pure))
static assert(mangledName!((int a) { return a+x; }) == "DFNaNbNiNfiZi"); // pure nothrow @safe @nogc
else
static assert(mangledName!((int a) { return a+x; }) == "DFNbNiNfiZi"); // nothrow @safe @nnogc
@@ -7409,7 +8368,7 @@ version (unittest) void freeFunc(string);
@system unittest
{
// @system due to demangle
- // Test for bug 5718
+ // Test for https://issues.dlang.org/show_bug.cgi?id=5718
import std.demangle : demangle;
int foo;
auto foo_demangled = demangle(mangledName!foo);
@@ -7426,10 +8385,11 @@ version (unittest) void freeFunc(string);
// XXX Select & select should go to another module. (functional or algorithm?)
/**
-Aliases itself to $(D T[0]) if the boolean $(D condition) is $(D true)
-and to $(D T[1]) otherwise.
+Aliases itself to `T[0]` if the boolean `condition` is `true`
+and to `T[1]` otherwise.
*/
-template Select(bool condition, T...) if (T.length == 2)
+template Select(bool condition, T...)
+if (T.length == 2)
{
import std.meta : Alias;
alias Select = Alias!(T[!condition]);
@@ -7458,19 +8418,28 @@ template Select(bool condition, T...) if (T.length == 2)
}
/**
-If $(D cond) is $(D true), returns $(D a) without evaluating $(D
-b). Otherwise, returns $(D b) without evaluating $(D a).
+Select one of two functions to run via template parameter.
+
+Params:
+ cond = A `bool` which determines which function is run
+ a = The first function
+ b = The second function
+
+Returns:
+ `a` without evaluating `b` if `cond` is `true`.
+ Otherwise, returns `b` without evaluating `a`.
*/
A select(bool cond : true, A, B)(A a, lazy B b) { return a; }
/// Ditto
B select(bool cond : false, A, B)(lazy A a, B b) { return b; }
+///
@safe unittest
{
- real pleasecallme() { return 0; }
- int dontcallme() { assert(0); }
- auto a = select!true(pleasecallme(), dontcallme());
- auto b = select!false(dontcallme(), pleasecallme());
+ real run() { return 0; }
+ int fail() { assert(0); }
+ auto a = select!true(run(), fail());
+ auto b = select!false(fail(), run());
static assert(is(typeof(a) == real));
static assert(is(typeof(b) == real));
}
@@ -7591,28 +8560,7 @@ template getUDAs(alias symbol, alias attribute)
{
import std.meta : Filter;
- template isDesiredUDA(alias toCheck)
- {
- static if (is(typeof(attribute)) && !__traits(isTemplate, attribute))
- {
- static if (__traits(compiles, toCheck == attribute))
- enum isDesiredUDA = toCheck == attribute;
- else
- enum isDesiredUDA = false;
- }
- else static if (is(typeof(toCheck)))
- {
- static if (__traits(isTemplate, attribute))
- enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck));
- else
- enum isDesiredUDA = is(typeof(toCheck) == attribute);
- }
- else static if (__traits(isTemplate, attribute))
- enum isDesiredUDA = isInstanceOf!(attribute, toCheck);
- else
- enum isDesiredUDA = is(toCheck == attribute);
- }
- alias getUDAs = Filter!(isDesiredUDA, __traits(getAttributes, symbol));
+ alias getUDAs = Filter!(isDesiredUDA!attribute, __traits(getAttributes, symbol));
}
///
@@ -7717,37 +8665,46 @@ template getUDAs(alias symbol, alias attribute)
static assert(getUDAs!(i, 'c').length == 0);
}
-/**
- * Gets all symbols within `symbol` that have the given user-defined attribute.
- * This is not recursive; it will not search for symbols within symbols such as
- * nested structs or unions.
- */
-template getSymbolsByUDA(alias symbol, alias attribute)
+private template isDesiredUDA(alias attribute)
{
- import std.format : format;
- import std.meta : AliasSeq, Filter;
-
- // translate a list of strings into symbols. mixing in the entire alias
- // avoids trying to access the symbol, which could cause a privacy violation
- template toSymbols(names...)
+ template isDesiredUDA(alias toCheck)
{
- static if (names.length == 0)
- alias toSymbols = AliasSeq!();
+ static if (is(typeof(attribute)) && !__traits(isTemplate, attribute))
+ {
+ static if (__traits(compiles, toCheck == attribute))
+ enum isDesiredUDA = toCheck == attribute;
+ else
+ enum isDesiredUDA = false;
+ }
+ else static if (is(typeof(toCheck)))
+ {
+ static if (__traits(isTemplate, attribute))
+ enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck));
+ else
+ enum isDesiredUDA = is(typeof(toCheck) == attribute);
+ }
+ else static if (__traits(isTemplate, attribute))
+ enum isDesiredUDA = isInstanceOf!(attribute, toCheck);
else
- mixin("alias toSymbols = AliasSeq!(symbol.%s, toSymbols!(names[1..$]));"
- .format(names[0]));
+ enum isDesiredUDA = is(toCheck == attribute);
}
+}
- // filtering inaccessible members
- enum isAccessibleMember(string name) = __traits(compiles, __traits(getMember, symbol, name));
- alias accessibleMembers = Filter!(isAccessibleMember, __traits(allMembers, symbol));
+/**
+Params:
+ symbol = The aggregate type or module to search
+ attribute = The user-defined attribute to search for
- // filtering not compiled members such as alias of basic types
- enum hasSpecificUDA(string name) = mixin("hasUDA!(symbol." ~ name ~ ", attribute)");
- enum isCorrectMember(string name) = __traits(compiles, hasSpecificUDA!(name));
+Returns:
+ All symbols within `symbol` that have the given UDA `attribute`.
- alias correctMembers = Filter!(isCorrectMember, accessibleMembers);
- alias membersWithUDA = toSymbols!(Filter!(hasSpecificUDA, correctMembers));
+Note:
+ This is not recursive; it will not search for symbols within symbols such as
+ nested structs or unions.
+ */
+template getSymbolsByUDA(alias symbol, alias attribute)
+{
+ alias membersWithUDA = getSymbolsByUDAImpl!(symbol, attribute, __traits(allMembers, symbol));
// if the symbol itself has the UDA, tack it on to the front of the list
static if (hasUDA!(symbol, attribute))
@@ -7760,6 +8717,20 @@ template getSymbolsByUDA(alias symbol, alias attribute)
@safe unittest
{
enum Attr;
+ struct A
+ {
+ @Attr int a;
+ int b;
+ }
+
+ static assert(getSymbolsByUDA!(A, Attr).length == 1);
+ static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr));
+}
+
+///
+@safe unittest
+{
+ enum Attr;
static struct A
{
@@ -7780,7 +8751,11 @@ template getSymbolsByUDA(alias symbol, alias attribute)
// Can access attributes on the symbols returned by getSymbolsByUDA.
static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr));
static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr));
+}
+/// Finds multiple attributes
+@safe unittest
+{
static struct UDA { string name; }
static struct B
@@ -7799,6 +8774,12 @@ template getSymbolsByUDA(alias symbol, alias attribute)
static assert(getSymbolsByUDA!(B, 100).length == 1);
// Can get the value of the UDA from the return value
static assert(getUDAs!(getSymbolsByUDA!(B, UDA)[0], UDA)[0].name == "X");
+}
+
+/// Checks for UDAs on the aggregate symbol itself
+@safe unittest
+{
+ static struct UDA { string name; }
@UDA("A")
static struct C
@@ -7807,77 +8788,175 @@ template getSymbolsByUDA(alias symbol, alias attribute)
int d;
}
- // Also checks the symbol itself
static assert(getSymbolsByUDA!(C, UDA).length == 2);
static assert(getSymbolsByUDA!(C, UDA)[0].stringof == "C");
static assert(getSymbolsByUDA!(C, UDA)[1].stringof == "d");
+}
+
+/// Finds nothing if there is no member with specific UDA
+@safe unittest
+{
+ static struct UDA { string name; }
static struct D
{
int x;
}
- //Finds nothing if there is no member with specific UDA
- static assert(getSymbolsByUDA!(D,UDA).length == 0);
+ static assert(getSymbolsByUDA!(D, UDA).length == 0);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18314
+@safe unittest
+{
+ enum attr1;
+ enum attr2;
+
+ struct A
+ {
+ @attr1
+ int n;
+ // Removed due to https://issues.dlang.org/show_bug.cgi?id=16206
+ //@attr1
+ //void foo()(string){}
+ @attr1
+ void foo();
+ @attr2
+ void foo(int a);
+ }
+
+ static assert(getSymbolsByUDA!(A, attr1).length == 2);
+ static assert(getSymbolsByUDA!(A, attr2).length == 1);
}
-// #15335: getSymbolsByUDA fails if type has private members
+// getSymbolsByUDA fails if type has private members
+// https://issues.dlang.org/show_bug.cgi?id=15335
@safe unittest
{
// HasPrivateMembers has, well, private members, one of which has a UDA.
import std.internal.test.uda : Attr, HasPrivateMembers;
// Trying access to private member from another file therefore we do not have access
// for this otherwise we get deprecation warning - not visible from module
- static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 1);
+ // This line is commented because `__traits(getMember)` should also consider
+ // private members; this is not currently the case, but the PR that
+ // fixes `__traits(getMember)` is blocked by this specific test.
+ //static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 1);
static assert(hasUDA!(getSymbolsByUDA!(HasPrivateMembers, Attr)[0], Attr));
}
-///
+// getSymbolsByUDA works with structs but fails with classes
+// https://issues.dlang.org/show_bug.cgi?id=16387
@safe unittest
{
enum Attr;
- struct A
+ class A
{
- alias int INT;
- alias void function(INT) SomeFunction;
- @Attr int a;
- int b;
- @Attr private int c;
- private int d;
+ @Attr uint a;
}
- // Here everything is fine, we have access to private member c
- static assert(getSymbolsByUDA!(A, Attr).length == 2);
- static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr));
- static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr));
+ alias res = getSymbolsByUDA!(A, Attr);
+ static assert(res.length == 1);
+ static assert(res[0].stringof == "a");
+}
+
+// getSymbolsByUDA fails on AliasSeq members
+// https://issues.dlang.org/show_bug.cgi?id=18884
+@safe unittest
+{
+ struct X
+ {
+ alias A = AliasSeq!(ulong, uint);
+ }
+
+ static assert(is(getSymbolsByUDA!(X, X) == AliasSeq!()));
}
-// #16387: getSymbolsByUDA works with structs but fails with classes
+// getSymbolsByUDA produces wrong result if one of the symbols having the UDA is a function
+// https://issues.dlang.org/show_bug.cgi?id=18624
@safe unittest
{
enum Attr;
- class A
+ struct A
{
- @Attr uint a;
+ @Attr void a();
+ @Attr void a(int n);
+ void b();
+ @Attr void c();
}
- alias res = getSymbolsByUDA!(A, Attr);
- static assert(res.length == 1);
- static assert(res[0].stringof == "a");
+ static assert(getSymbolsByUDA!(A, Attr).stringof == "tuple(a, a, c)");
+}
+
+// getSymbolsByUDA no longer works on modules
+// https://issues.dlang.org/show_bug.cgi?id=20054
+version (StdUnittest)
+{
+ @("Issue20054")
+ void issue20054() {}
+ static assert(__traits(compiles, getSymbolsByUDA!(mixin(__MODULE__), "Issue20054")));
+}
+
+private template getSymbolsByUDAImpl(alias symbol, alias attribute, names...)
+{
+ import std.meta : Alias, AliasSeq, Filter;
+ static if (names.length == 0)
+ {
+ alias getSymbolsByUDAImpl = AliasSeq!();
+ }
+ else
+ {
+ alias tail = getSymbolsByUDAImpl!(symbol, attribute, names[1 .. $]);
+
+ // Filtering inaccessible members.
+ static if (!__traits(compiles, __traits(getMember, symbol, names[0])))
+ {
+ alias getSymbolsByUDAImpl = tail;
+ }
+ else
+ {
+ alias member = __traits(getMember, symbol, names[0]);
+
+ // Filtering not compiled members such as alias of basic types.
+ static if (!__traits(compiles, hasUDA!(member, attribute)))
+ {
+ alias getSymbolsByUDAImpl = tail;
+ }
+ // Get overloads for functions, in case different overloads have different sets of UDAs.
+ else static if (isFunction!member)
+ {
+ enum hasSpecificUDA(alias member) = hasUDA!(member, attribute);
+ alias overloadsWithUDA = Filter!(hasSpecificUDA, __traits(getOverloads, symbol, names[0]));
+ alias getSymbolsByUDAImpl = AliasSeq!(overloadsWithUDA, tail);
+ }
+ else static if (hasUDA!(member, attribute))
+ {
+ alias getSymbolsByUDAImpl = AliasSeq!(member, tail);
+ }
+ else
+ {
+ alias getSymbolsByUDAImpl = tail;
+ }
+ }
+ }
}
/**
- Returns: $(D true) iff all types $(D T) are the same.
+ Returns: `true` iff all types `T` are the same.
*/
template allSameType(T...)
{
- static if (T.length <= 1)
+ static foreach (idx, Ti; T)
{
- enum bool allSameType = true;
+ static if (idx + 1 < T.length &&
+ !is(typeof(allSameType) == bool) &&
+ !is(T[idx] == T[idx + 1]))
+ {
+ enum bool allSameType = false;
+ }
}
- else
+ static if (!is(typeof(allSameType) == bool))
{
- enum bool allSameType = is(T[0] == T[1]) && allSameType!(T[1..$]);
+ enum bool allSameType = true;
}
}
@@ -7894,7 +8973,7 @@ template allSameType(T...)
}
/**
- Returns: $(D true) iff the type $(D T) can be tested in an $(D
+ Returns: `true` iff the type `T` can be tested in an $(D
if)-expression, that is if $(D if (pred(T.init)) {}) is compilable.
*/
enum ifTestable(T, alias pred = a => a) = __traits(compiles, { if (pred(T.init)) {} });
@@ -7914,7 +8993,8 @@ enum ifTestable(T, alias pred = a => a) = __traits(compiles, { if (pred(T.init))
* Returns:
* `true` if `X` is a type, `false` otherwise
*/
-template isType(X...) if (X.length == 1)
+template isType(X...)
+if (X.length == 1)
{
enum isType = is(X[0]);
}
@@ -7957,7 +9037,8 @@ template isType(X...) if (X.length == 1)
* Use $(LREF isFunctionPointer) or $(LREF isDelegate) for detecting those types
* respectively.
*/
-template isFunction(X...) if (X.length == 1)
+template isFunction(X...)
+if (X.length == 1)
{
static if (is(typeof(&X[0]) U : U*) && is(U == function) ||
is(typeof(&X[0]) U == delegate))
@@ -7993,7 +9074,8 @@ template isFunction(X...) if (X.length == 1)
* Returns:
* `true` if `X` is final, `false` otherwise
*/
-template isFinal(X...) if (X.length == 1)
+template isFinal(X...)
+if (X.length == 1)
{
static if (is(X[0] == class))
enum isFinal = __traits(isFinalClass, X[0]);
@@ -8033,16 +9115,14 @@ template isFinal(X...) if (X.length == 1)
+ Returns:
+ `true` if `S` can be copied. `false` otherwise.
+ ++/
-enum isCopyable(S) = is(typeof(
- { S foo = S.init; S copy = foo; }
-));
+enum isCopyable(S) = __traits(isCopyable, S);
///
@safe unittest
{
struct S1 {} // Fine. Can be copied
struct S2 { this(this) {}} // Fine. Can be copied
- struct S3 {@disable this(this) {}} // Not fine. Copying is disabled.
+ struct S3 {@disable this(this); } // Not fine. Copying is disabled.
struct S4 {S3 s;} // Not fine. A field has copying disabled.
class C1 {}
diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d
index 84e876f3c59..feedf90e951 100644
--- a/libphobos/src/std/typecons.d
+++ b/libphobos/src/std/typecons.d
@@ -5,8 +5,9 @@ This module implements a variety of type constructors, i.e., templates
that allow construction of new, useful general-purpose types.
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE,
-$(TR $(TH Category) $(TH Functions))
+$(TR $(TH Category) $(TH Symbols))
$(TR $(TD Tuple) $(TD
$(LREF isTuple)
$(LREF Tuple)
@@ -55,11 +56,11 @@ $(TR $(TD Types) $(TD
$(LREF TypedefType)
$(LREF UnqualRef)
))
-)
+))
Copyright: Copyright the respective authors, 2008-
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Source: $(PHOBOSSRC std/_typecons.d)
+Source: $(PHOBOSSRC std/typecons.d)
Authors: $(HTTP erdani.org, Andrei Alexandrescu),
$(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski),
Don Clugston,
@@ -68,9 +69,12 @@ Authors: $(HTTP erdani.org, Andrei Alexandrescu),
*/
module std.typecons;
-import core.stdc.stdint : uintptr_t;
-import std.meta; // : AliasSeq, allSatisfy;
+import std.format.spec : singleSpec, FormatSpec;
+import std.format.write : formatValue;
+import std.meta : AliasSeq, allSatisfy;
+import std.range.primitives : isOutputRange;
import std.traits;
+import std.internal.attributes : betterC;
///
@safe unittest
@@ -104,33 +108,31 @@ import std.traits;
}
}
-debug(Unique) import std.stdio;
-
/**
Encapsulates unique ownership of a resource.
-When a $(D Unique!T) goes out of scope it will call $(D destroy)
-on the resource $(D T) that it manages, unless it is transferred.
-One important consequence of $(D destroy) is that it will call the
-destructor of the resource $(D T). GC-managed references are not
+When a `Unique!T` goes out of scope it will call `destroy`
+on the resource `T` that it manages, unless it is transferred.
+One important consequence of `destroy` is that it will call the
+destructor of the resource `T`. GC-managed references are not
guaranteed to be valid during a destructor call, but other members of
-$(D T), such as file handles or pointers to $(D malloc) memory, will
+`T`, such as file handles or pointers to `malloc` memory, will
still be valid during the destructor call. This allows the resource
-$(D T) to deallocate or clean up any non-GC resources.
+`T` to deallocate or clean up any non-GC resources.
-If it is desirable to persist a $(D Unique!T) outside of its original
+If it is desirable to persist a `Unique!T` outside of its original
scope, then it can be transferred. The transfer can be explicit, by
-calling $(D release), or implicit, when returning Unique from a
-function. The resource $(D T) can be a polymorphic class object or
+calling `release`, or implicit, when returning Unique from a
+function. The resource `T` can be a polymorphic class object or
instance of an interface, in which case Unique behaves polymorphically
too.
-If $(D T) is a value type, then $(D Unique!T) will be implemented
-as a reference to a $(D T).
+If `T` is a value type, then `Unique!T` will be implemented
+as a reference to a `T`.
*/
struct Unique(T)
{
-/** Represents a reference to $(D T). Resolves to $(D T*) if $(D T) is a value type. */
+/** Represents a reference to `T`. Resolves to `T*` if `T` is a value type. */
static if (is(T == class) || is(T == interface))
alias RefT = T;
else
@@ -140,12 +142,12 @@ public:
// Deferred in case we get some language support for checking uniqueness.
version (None)
/**
- Allows safe construction of $(D Unique). It creates the resource and
- guarantees unique ownership of it (unless $(D T) publishes aliases of
- $(D this)).
+ Allows safe construction of `Unique`. It creates the resource and
+ guarantees unique ownership of it (unless `T` publishes aliases of
+ `this`).
Note: Nested structs/classes cannot be created.
Params:
- args = Arguments to pass to $(D T)'s constructor.
+ args = Arguments to pass to `T`'s constructor.
---
static class C {}
auto u = Unique!(C).create();
@@ -154,7 +156,6 @@ public:
static Unique!T create(A...)(auto ref A args)
if (__traits(compiles, new T(args)))
{
- debug(Unique) writeln("Unique.create for ", T.stringof);
Unique!T u;
u._p = new T(args);
return u;
@@ -171,7 +172,6 @@ public:
*/
this(RefT p)
{
- debug(Unique) writeln("Unique constructor with rvalue");
_p = p;
}
/**
@@ -182,15 +182,14 @@ public:
this(ref RefT p)
{
_p = p;
- debug(Unique) writeln("Unique constructor nulling source");
p = null;
assert(p is null);
}
/**
- Constructor that takes a $(D Unique) of a type that is convertible to our type.
+ Constructor that takes a `Unique` of a type that is convertible to our type.
- Typically used to transfer a $(D Unique) rvalue of derived type to
- a $(D Unique) of base type.
+ Typically used to transfer a `Unique` rvalue of derived type to
+ a `Unique` of base type.
Example:
---
class C : Object {}
@@ -202,16 +201,14 @@ public:
this(U)(Unique!U u)
if (is(u.RefT:RefT))
{
- debug(Unique) writeln("Unique constructor converting from ", U.stringof);
_p = u._p;
u._p = null;
}
- /// Transfer ownership from a $(D Unique) of a type that is convertible to our type.
+ /// Transfer ownership from a `Unique` of a type that is convertible to our type.
void opAssign(U)(Unique!U u)
if (is(u.RefT:RefT))
{
- debug(Unique) writeln("Unique opAssign converting from ", U.stringof);
// first delete any resource we own
destroy(this);
_p = u._p;
@@ -220,7 +217,6 @@ public:
~this()
{
- debug(Unique) writeln("Unique destructor of ", (_p is null)? null: _p);
if (_p !is null)
{
destroy(_p);
@@ -233,12 +229,11 @@ public:
{
return _p is null;
}
- /** Transfer ownership to a $(D Unique) rvalue. Nullifies the current contents.
+ /** Transfer ownership to a `Unique` rvalue. Nullifies the current contents.
Same as calling std.algorithm.move on it.
*/
Unique release()
{
- debug(Unique) writeln("Unique Release");
import std.algorithm.mutation : move;
return this.move;
}
@@ -247,7 +242,7 @@ public:
mixin Proxy!_p;
/**
- Postblit operator is undefined to prevent the cloning of $(D Unique) objects.
+ Postblit operator is undefined to prevent the cloning of `Unique` objects.
*/
@disable this(this);
@@ -256,7 +251,7 @@ private:
}
///
-@system unittest
+@safe unittest
{
static struct S
{
@@ -318,7 +313,6 @@ private:
@system unittest
{
- debug(Unique) writeln("Unique class");
class Bar
{
~this() { debug(Unique) writeln(" Bar destructor"); }
@@ -334,16 +328,13 @@ private:
assert(!ub.isEmpty);
assert(ub.val == 4);
static assert(!__traits(compiles, {auto ub3 = g(ub);}));
- debug(Unique) writeln("Calling g");
auto ub2 = g(ub.release);
- debug(Unique) writeln("Returned from g");
assert(ub.isEmpty);
assert(!ub2.isEmpty);
}
@system unittest
{
- debug(Unique) writeln("Unique interface");
interface Bar
{
int val() const;
@@ -377,21 +368,18 @@ private:
assert(!ub.isEmpty);
assert(ub.val == 4);
static assert(!__traits(compiles, {auto ub3 = g(ub);}));
- debug(Unique) writeln("Calling g");
auto ub2 = g(ub.release);
- debug(Unique) writeln("Returned from g");
assert(ub.isEmpty);
assert(!ub2.isEmpty);
consume(ub2.release);
assert(BarImpl.count == 0);
}
-@system unittest
+@safe unittest
{
- debug(Unique) writeln("Unique struct");
struct Foo
{
- ~this() { debug(Unique) writeln(" Foo destructor"); }
+ ~this() { }
int val() const { return 3; }
@disable this(this);
}
@@ -399,7 +387,6 @@ private:
UFoo f(UFoo u)
{
- debug(Unique) writeln("inside f");
return u.release;
}
@@ -407,9 +394,7 @@ private:
assert(!uf.isEmpty);
assert(uf.val == 3);
static assert(!__traits(compiles, {auto uf3 = f(uf);}));
- debug(Unique) writeln("Unique struct: calling f");
auto uf2 = f(uf.release);
- debug(Unique) writeln("Unique struct: returned from f");
assert(uf.isEmpty);
assert(!uf2.isEmpty);
}
@@ -436,36 +421,48 @@ private:
// Used in Tuple.toString
private template sharedToString(alias field)
- if (is(typeof(field) == shared))
+if (is(typeof(field) == shared))
{
static immutable sharedToString = typeof(field).stringof;
}
private template sharedToString(alias field)
- if (!is(typeof(field) == shared))
+if (!is(typeof(field) == shared))
{
alias sharedToString = field;
}
+private enum bool distinctFieldNames(names...) = __traits(compiles,
+{
+ static foreach (__name; names)
+ static if (is(typeof(__name) : string))
+ mixin("enum int " ~ __name ~ " = 0;");
+});
+
+@safe unittest
+{
+ static assert(!distinctFieldNames!(string, "abc", string, "abc"));
+ static assert(distinctFieldNames!(string, "abc", int, "abd"));
+ static assert(!distinctFieldNames!(int, "abc", string, "abd", int, "abc"));
+ // https://issues.dlang.org/show_bug.cgi?id=19240
+ static assert(!distinctFieldNames!(int, "int"));
+}
+
/**
_Tuple of values, for example $(D Tuple!(int, string)) is a record that
-stores an $(D int) and a $(D string). $(D Tuple) can be used to bundle
+stores an `int` and a `string`. `Tuple` can be used to bundle
values together, notably when returning multiple values from a
-function. If $(D obj) is a `Tuple`, the individual members are
-accessible with the syntax $(D obj[0]) for the first field, $(D obj[1])
+function. If `obj` is a `Tuple`, the individual members are
+accessible with the syntax `obj[0]` for the first field, `obj[1]`
for the second, and so on.
-The choice of zero-based indexing instead of one-base indexing was
-motivated by the ability to use value tuples with various compile-time
-loop constructs (e.g. $(REF AliasSeq, std,meta) iteration), all of which use
-zero-based indexing.
-
See_Also: $(LREF tuple).
Params:
Specs = A list of types (and optionally, member names) that the `Tuple` contains.
*/
template Tuple(Specs...)
+if (distinctFieldNames!(Specs))
{
import std.meta : staticMap;
@@ -517,21 +514,20 @@ template Tuple(Specs...)
// :
// NOTE: field[k] is an expression (which yields a symbol of a
// variable) and can't be aliased directly.
- string injectNamedFields()
+ enum injectNamedFields = ()
{
string decl = "";
- foreach (i, name; staticMap!(extractName, fieldSpecs))
- {
- import std.format : format;
-
- decl ~= format("alias _%s = Identity!(field[%s]);", i, i);
- if (name.length != 0)
+ static foreach (i, val; fieldSpecs)
+ {{
+ immutable si = i.stringof;
+ decl ~= "alias _" ~ si ~ " = Identity!(field[" ~ si ~ "]);";
+ if (val.name.length != 0)
{
- decl ~= format("alias %s = _%s;", name, i);
+ decl ~= "alias " ~ val.name ~ " = _" ~ si ~ ";";
}
- }
+ }}
return decl;
- }
+ };
// Returns Specs for a subtuple this[from .. to] preserving field
// names if any.
@@ -550,35 +546,35 @@ template Tuple(Specs...)
}
}
- enum areCompatibleTuples(Tup1, Tup2, string op) = isTuple!Tup2 && is(typeof(
+ enum areCompatibleTuples(Tup1, Tup2, string op) = isTuple!(OriginalType!Tup2) && is(typeof(
(ref Tup1 tup1, ref Tup2 tup2)
{
static assert(tup1.field.length == tup2.field.length);
- foreach (i, _; Tup1.Types)
- {
+ static foreach (i; 0 .. Tup1.Types.length)
+ {{
auto lhs = typeof(tup1.field[i]).init;
auto rhs = typeof(tup2.field[i]).init;
static if (op == "=")
lhs = rhs;
else
auto result = mixin("lhs "~op~" rhs");
- }
+ }}
}));
enum areBuildCompatibleTuples(Tup1, Tup2) = isTuple!Tup2 && is(typeof(
{
static assert(Tup1.Types.length == Tup2.Types.length);
- foreach (i, _; Tup1.Types)
+ static foreach (i; 0 .. Tup1.Types.length)
static assert(isBuildable!(Tup1.Types[i], Tup2.Types[i]));
}));
- /+ Returns $(D true) iff a $(D T) can be initialized from a $(D U). +/
+ /+ Returns `true` iff a `T` can be initialized from a `U`. +/
enum isBuildable(T, U) = is(typeof(
{
U u = U.init;
T t = u;
}));
- /+ Helper for partial instanciation +/
+ /+ Helper for partial instantiation +/
template isBuildableFrom(U)
{
enum isBuildableFrom(T) = isBuildable!(T, U);
@@ -591,9 +587,12 @@ template Tuple(Specs...)
*/
alias Types = staticMap!(extractType, fieldSpecs);
+ private alias _Fields = Specs;
+
///
static if (Specs.length == 0) @safe unittest
{
+ import std.meta : AliasSeq;
alias Fields = Tuple!(int, "id", string, float);
static assert(is(Fields.Types == AliasSeq!(int, string, float)));
}
@@ -606,14 +605,15 @@ template Tuple(Specs...)
///
static if (Specs.length == 0) @safe unittest
{
+ import std.meta : AliasSeq;
alias Fields = Tuple!(int, "id", string, float);
static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
}
/**
- * Use $(D t.expand) for a `Tuple` $(D t) to expand it into its
- * components. The result of $(D expand) acts as if the `Tuple`'s components
- * were listed as a list of values. (Ordinarily, a $(D Tuple) acts as a
+ * Use `t.expand` for a `Tuple` `t` to expand it into its
+ * components. The result of `expand` acts as if the `Tuple`'s components
+ * were listed as a list of values. (Ordinarily, a `Tuple` acts as a
* single value.)
*/
Types expand;
@@ -622,8 +622,8 @@ template Tuple(Specs...)
///
static if (Specs.length == 0) @safe unittest
{
- auto t1 = tuple(1, " hello ", 2.3);
- assert(t1.toString() == `Tuple!(int, string, double)(1, " hello ", 2.3)`);
+ auto t1 = tuple(1, " hello ", 'a');
+ assert(t1.toString() == `Tuple!(int, string, char)(1, " hello ", 'a')`);
void takeSeveralTypes(int n, string s, bool b)
{
@@ -645,7 +645,7 @@ template Tuple(Specs...)
@property
ref inout(Tuple!Types) _Tuple_super() inout @trusted
{
- foreach (i, _; Types) // Rely on the field layout
+ static foreach (i; 0 .. Types.length) // Rely on the field layout
{
static assert(typeof(return).init.tupleof[i].offsetof ==
expand[i].offsetof);
@@ -695,7 +695,7 @@ template Tuple(Specs...)
this(U, size_t n)(U[n] values)
if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types))
{
- foreach (i, _; Types)
+ static foreach (i; 0 .. Types.length)
{
field[i] = values[i];
}
@@ -771,6 +771,17 @@ template Tuple(Specs...)
return field[] == rhs.field[];
}
+ /// ditto
+ bool opEquals(R...)(auto ref R rhs)
+ if (R.length > 1 && areCompatibleTuples!(typeof(this), Tuple!R, "=="))
+ {
+ static foreach (i; 0 .. Types.length)
+ if (field[i] != rhs[i])
+ return false;
+
+ return true;
+ }
+
///
static if (Specs.length == 0) @safe unittest
{
@@ -789,21 +800,42 @@ template Tuple(Specs...)
* for comparison between `Tuple`s.
*
* Returns:
- * For any values `v1` on the right-hand side and `v2` on the
- * left-hand side:
+ * For any values `v1` contained by the left-hand side tuple and any
+ * values `v2` contained by the right-hand side:
+ *
+ * 0 if `v1 == v2` for all members or the following value for the
+ * first position were the mentioned criteria is not satisfied:
*
* $(UL
- * $(LI A negative integer if the expression `v1 < v2` is true.)
- * $(LI A positive integer if the expression `v1 > v2` is true.)
- * $(LI 0 if the expression `v1 == v2` is true.))
+ * $(LI NaN, in case one of the operands is a NaN.)
+ * $(LI A negative number if the expression `v1 < v2` is true.)
+ * $(LI A positive number if the expression `v1 > v2` is true.))
*/
- int opCmp(R)(R rhs)
+ auto opCmp(R)(R rhs)
if (areCompatibleTuples!(typeof(this), R, "<"))
{
- foreach (i, Unused; Types)
+ static foreach (i; 0 .. Types.length)
{
if (field[i] != rhs.field[i])
{
+ import std.math.traits : isNaN;
+ static if (isFloatingPoint!(Types[i]))
+ {
+ if (isNaN(field[i]))
+ return float.nan;
+ }
+ static if (isFloatingPoint!(typeof(rhs.field[i])))
+ {
+ if (isNaN(rhs.field[i]))
+ return float.nan;
+ }
+ static if (is(typeof(field[i].opCmp(rhs.field[i]))) &&
+ isFloatingPoint!(typeof(field[i].opCmp(rhs.field[i]))))
+ {
+ if (isNaN(field[i].opCmp(rhs.field[i])))
+ return float.nan;
+ }
+
return field[i] < rhs.field[i] ? -1 : 1;
}
}
@@ -811,13 +843,31 @@ template Tuple(Specs...)
}
/// ditto
- int opCmp(R)(R rhs) const
+ auto opCmp(R)(R rhs) const
if (areCompatibleTuples!(typeof(this), R, "<"))
{
- foreach (i, Unused; Types)
+ static foreach (i; 0 .. Types.length)
{
if (field[i] != rhs.field[i])
{
+ import std.math.traits : isNaN;
+ static if (isFloatingPoint!(Types[i]))
+ {
+ if (isNaN(field[i]))
+ return float.nan;
+ }
+ static if (isFloatingPoint!(typeof(rhs.field[i])))
+ {
+ if (isNaN(rhs.field[i]))
+ return float.nan;
+ }
+ static if (is(typeof(field[i].opCmp(rhs.field[i]))) &&
+ isFloatingPoint!(typeof(field[i].opCmp(rhs.field[i]))))
+ {
+ if (isNaN(field[i].opCmp(rhs.field[i])))
+ return float.nan;
+ }
+
return field[i] < rhs.field[i] ? -1 : 1;
}
}
@@ -840,6 +890,49 @@ template Tuple(Specs...)
}
/**
+ Concatenate Tuples.
+ Tuple concatenation is only allowed if all named fields are distinct (no named field of this tuple occurs in `t`
+ and no named field of `t` occurs in this tuple).
+
+ Params:
+ t = The `Tuple` to concatenate with
+
+ Returns: A concatenation of this tuple and `t`
+ */
+ auto opBinary(string op, T)(auto ref T t)
+ if (op == "~" && !(is(T : U[], U) && isTuple!U))
+ {
+ static if (isTuple!T)
+ {
+ static assert(distinctFieldNames!(_Fields, T._Fields),
+ "Cannot concatenate tuples with duplicate fields: " ~ fieldNames.stringof ~
+ " - " ~ T.fieldNames.stringof);
+ return Tuple!(_Fields, T._Fields)(expand, t.expand);
+ }
+ else
+ {
+ return Tuple!(_Fields, T)(expand, t);
+ }
+ }
+
+ /// ditto
+ auto opBinaryRight(string op, T)(auto ref T t)
+ if (op == "~" && !(is(T : U[], U) && isTuple!U))
+ {
+ static if (isTuple!T)
+ {
+ static assert(distinctFieldNames!(_Fields, T._Fields),
+ "Cannot concatenate tuples with duplicate fields: " ~ T.stringof ~
+ " - " ~ fieldNames.fieldNames.stringof);
+ return Tuple!(T._Fields, _Fields)(t.expand, expand);
+ }
+ else
+ {
+ return Tuple!(T, _Fields)(t, expand);
+ }
+ }
+
+ /**
* Assignment from another `Tuple`.
*
* Params:
@@ -847,12 +940,12 @@ template Tuple(Specs...)
* source `Tuple` must be implicitly assignable to each
* respective element of the target `Tuple`.
*/
- void opAssign(R)(auto ref R rhs)
+ ref Tuple opAssign(R)(auto ref R rhs)
if (areCompatibleTuples!(typeof(this), R, "="))
{
import std.algorithm.mutation : swap;
- static if (is(R : Tuple!Types) && !__traits(isRef, rhs))
+ static if (is(R : Tuple!Types) && !__traits(isRef, rhs) && isTuple!R)
{
if (__ctfe)
{
@@ -870,6 +963,7 @@ template Tuple(Specs...)
// Do not swap; opAssign should be called on the fields.
field[] = rhs.field[];
}
+ return this;
}
/**
@@ -884,11 +978,11 @@ template Tuple(Specs...)
* It is an compile-time error to pass more names than
* there are members of the $(LREF Tuple).
*/
- ref rename(names...)() return
+ ref rename(names...)() inout return
if (names.length == 0 || allSatisfy!(isSomeString, typeof(names)))
{
import std.algorithm.comparison : equal;
- // to circumvent bug 16418
+ // to circumvent https://issues.dlang.org/show_bug.cgi?id=16418
static if (names.length == 0 || equal([names], [fieldNames]))
return this;
else
@@ -898,6 +992,8 @@ template Tuple(Specs...)
static assert(nN <= nT, "Cannot have more names than tuple members");
alias allNames = AliasSeq!(names, fieldNames[nN .. $]);
+ import std.meta : Alias, aliasSeqOf;
+
template GetItem(size_t idx)
{
import std.array : empty;
@@ -938,7 +1034,7 @@ template Tuple(Specs...)
t2 = tuple(3,4,5);
auto t2Named = t2.rename!("", "b");
// "a" no longer has a name
- static assert(!hasMember!(typeof(t2Named), "a"));
+ static assert(!__traits(hasMember, typeof(t2Named), "a"));
assert(t2Named[0] == 3);
assert(t2Named.b == 4);
assert(t2Named.c == 5);
@@ -954,6 +1050,10 @@ template Tuple(Specs...)
.map!(t => t.a * t.b)
.sum;
assert(res == 68);
+
+ const tup = Tuple!(int, "a", int, "b")(2, 3);
+ const renamed = tup.rename!("c", "d");
+ assert(renamed.c + renamed.d == 5);
}
/**
@@ -966,10 +1066,11 @@ template Tuple(Specs...)
* The same rules for empty strings apply as for the variadic
* template overload of $(LREF _rename).
*/
- ref rename(alias translate)()
+ ref rename(alias translate)() inout
if (is(typeof(translate) : V[K], V, K) && isSomeString!V &&
(isSomeString!K || is(K : size_t)))
{
+ import std.meta : aliasSeqOf;
import std.range : ElementType;
static if (isSomeString!(ElementType!(typeof(translate.keys))))
{
@@ -1033,6 +1134,11 @@ template Tuple(Specs...)
auto t2Named = t2.rename!(["a": "b", "b": "c"]);
assert(t2Named.b == 3);
assert(t2Named.c == 4);
+
+ const t3 = Tuple!(int, "a", int, "b")(3, 4);
+ const t3Named = t3.rename!(["a": "b", "b": "c"]);
+ assert(t3Named.b == 3);
+ assert(t3Named.c == 4);
}
///
@@ -1094,7 +1200,8 @@ template Tuple(Specs...)
static assert(
(typeof(this).alignof % typeof(return).alignof == 0) &&
(expand[from].offsetof % typeof(return).alignof == 0),
- "Slicing by reference is impossible because of an alignment mistmatch. (See Phobos issue #15645.)");
+ "Slicing by reference is impossible because of an alignment mistmatch" ~
+ " (See https://issues.dlang.org/show_bug.cgi?id=15645).");
return *cast(typeof(return)*) &(field[from]);
}
@@ -1109,7 +1216,7 @@ template Tuple(Specs...)
static assert(is(typeof(s) == Tuple!(string, float)));
assert(s[0] == "abc" && s[1] == 4.5);
- // Phobos issue #15645
+ // https://issues.dlang.org/show_bug.cgi?id=15645
Tuple!(int, short, bool, double) b;
static assert(!__traits(compiles, b.slice!(2, 4)));
}
@@ -1120,144 +1227,180 @@ template Tuple(Specs...)
Returns:
A `size_t` representing the hash of this `Tuple`.
*/
- size_t toHash() const nothrow @trusted
+ size_t toHash() const nothrow @safe
{
size_t h = 0;
- foreach (i, T; Types)
- h += typeid(T).getHash(cast(const void*)&field[i]);
+ static foreach (i, T; Types)
+ {{
+ static if (__traits(compiles, h = .hashOf(field[i])))
+ const k = .hashOf(field[i]);
+ else
+ {
+ // Workaround for when .hashOf is not both @safe and nothrow.
+ static if (is(T : shared U, U) && __traits(compiles, (U* a) nothrow @safe => .hashOf(*a))
+ && !__traits(hasMember, T, "toHash"))
+ // BUG: Improperly casts away `shared`!
+ const k = .hashOf(*(() @trusted => cast(U*) &field[i])());
+ else
+ // BUG: Improperly casts away `shared`!
+ const k = typeid(T).getHash((() @trusted => cast(const void*) &field[i])());
+ }
+ static if (i == 0)
+ h = k;
+ else
+ // As in boost::hash_combine
+ // https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine
+ h ^= k + 0x9e3779b9 + (h << 6) + (h >>> 2);
+ }}
return h;
}
- ///
- template toString()
- {
- /**
- * Converts to string.
- *
- * Returns:
- * The string representation of this `Tuple`.
- */
- string toString()() const
- {
- import std.array : appender;
- auto app = appender!string();
- this.toString((const(char)[] chunk) => app ~= chunk);
- return app.data;
- }
+ /**
+ * Converts to string.
+ *
+ * Returns:
+ * The string representation of this `Tuple`.
+ */
+ string toString()() const
+ {
+ import std.array : appender;
+ auto app = appender!string();
+ this.toString((const(char)[] chunk) => app ~= chunk);
+ return app.data;
+ }
- import std.format : FormatSpec;
-
- /**
- * Formats `Tuple` with either `%s`, `%(inner%)` or `%(inner%|sep%)`.
- *
- * $(TABLE2 Formats supported by Tuple,
- * $(THEAD Format, Description)
- * $(TROW $(P `%s`), $(P Format like `Tuple!(types)(elements formatted with %s each)`.))
- * $(TROW $(P `%(inner%)`), $(P The format `inner` is applied the expanded `Tuple`, so
- * it may contain as many formats as the `Tuple` has fields.))
- * $(TROW $(P `%(inner%|sep%)`), $(P The format `inner` is one format, that is applied
- * on all fields of the `Tuple`. The inner format must be compatible to all
- * of them.)))
- * ---
- * Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];
- *
- * // Default format
- * assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`);
- *
- * // One Format for each individual component
- * assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`);
- * assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`);
- *
- * // One Format for all components
- * assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`);
- *
- * // Array of Tuples
- * assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`);
- *
- *
- * // Error: %( %) missing.
- * assertThrown!FormatException(
- * format("%d, %f", tuple(1, 2.0)) == `1, 2.0`
- * );
- *
- * // Error: %( %| %) missing.
- * assertThrown!FormatException(
- * format("%d", tuple(1, 2)) == `1, 2`
- * );
- *
- * // Error: %d inadequate for double.
- * assertThrown!FormatException(
- * format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0`
- * );
- * ---
- */
- void toString(DG)(scope DG sink) const
- {
- toString(sink, FormatSpec!char());
- }
+ import std.format.spec : FormatSpec;
+
+ /**
+ * Formats `Tuple` with either `%s`, `%(inner%)` or `%(inner%|sep%)`.
+ *
+ * $(TABLE2 Formats supported by Tuple,
+ * $(THEAD Format, Description)
+ * $(TROW $(P `%s`), $(P Format like `Tuple!(types)(elements formatted with %s each)`.))
+ * $(TROW $(P `%(inner%)`), $(P The format `inner` is applied the expanded `Tuple`$(COMMA) so
+ * it may contain as many formats as the `Tuple` has fields.))
+ * $(TROW $(P `%(inner%|sep%)`), $(P The format `inner` is one format$(COMMA) that is applied
+ * on all fields of the `Tuple`. The inner format must be compatible to all
+ * of them.)))
+ *
+ * Params:
+ * sink = A `char` accepting delegate
+ * fmt = A $(REF FormatSpec, std,format)
+ */
+ void toString(DG)(scope DG sink) const
+ {
+ auto f = FormatSpec!char();
+ toString(sink, f);
+ }
- /// ditto
- void toString(DG, Char)(scope DG sink, FormatSpec!Char fmt) const
+ /// ditto
+ void toString(DG, Char)(scope DG sink, scope const ref FormatSpec!Char fmt) const
+ {
+ import std.format : format, FormatException;
+ import std.format.write : formattedWrite;
+ import std.range : only;
+ if (fmt.nested)
{
- import std.format : formatElement, formattedWrite, FormatException;
- if (fmt.nested)
+ if (fmt.sep)
{
- if (fmt.sep)
- {
- foreach (i, Type; Types)
- {
- static if (i > 0)
- {
- sink(fmt.sep);
- }
- // TODO: Change this once formattedWrite() works for shared objects.
- static if (is(Type == class) && is(Type == shared))
- {
- sink(Type.stringof);
- }
- else
- {
- formattedWrite(sink, fmt.nested, this.field[i]);
- }
- }
- }
- else
- {
- formattedWrite(sink, fmt.nested, staticMap!(sharedToString, this.expand));
- }
- }
- else if (fmt.spec == 's')
- {
- enum header = Unqual!(typeof(this)).stringof ~ "(",
- footer = ")",
- separator = ", ";
- sink(header);
foreach (i, Type; Types)
{
static if (i > 0)
{
- sink(separator);
+ sink(fmt.sep);
}
- // TODO: Change this once formatElement() works for shared objects.
+ // TODO: Change this once formattedWrite() works for shared objects.
static if (is(Type == class) && is(Type == shared))
{
sink(Type.stringof);
}
else
{
- FormatSpec!Char f;
- formatElement(sink, field[i], f);
+ formattedWrite(sink, fmt.nested, this.field[i]);
}
}
- sink(footer);
}
else
{
- throw new FormatException(
- "Expected '%s' or '%(...%)' or '%(...%|...%)' format specifier for type '" ~
- Unqual!(typeof(this)).stringof ~ "', not '%" ~ fmt.spec ~ "'.");
+ formattedWrite(sink, fmt.nested, staticMap!(sharedToString, this.expand));
}
}
+ else if (fmt.spec == 's')
+ {
+ enum header = Unqual!(typeof(this)).stringof ~ "(",
+ footer = ")",
+ separator = ", ";
+ sink(header);
+ foreach (i, Type; Types)
+ {
+ static if (i > 0)
+ {
+ sink(separator);
+ }
+ // TODO: Change this once format() works for shared objects.
+ static if (is(Type == class) && is(Type == shared))
+ {
+ sink(Type.stringof);
+ }
+ else
+ {
+ sink(format!("%(%s%)")(only(field[i])));
+ }
+ }
+ sink(footer);
+ }
+ else
+ {
+ const spec = fmt.spec;
+ throw new FormatException(
+ "Expected '%s' or '%(...%)' or '%(...%|...%)' format specifier for type '" ~
+ Unqual!(typeof(this)).stringof ~ "', not '%" ~ spec ~ "'.");
+ }
+ }
+
+ ///
+ static if (Types.length == 0)
+ @safe unittest
+ {
+ import std.format : format;
+
+ Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];
+
+ // Default format
+ assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`);
+
+ // One Format for each individual component
+ assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`);
+ assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`);
+
+ // One Format for all components
+ assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`);
+
+ // Array of Tuples
+ assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`);
+ }
+
+ ///
+ static if (Types.length == 0)
+ @safe unittest
+ {
+ import std.exception : assertThrown;
+ import std.format : format, FormatException;
+
+ // Error: %( %) missing.
+ assertThrown!FormatException(
+ format("%d, %f", tuple(1, 2.0)) == `1, 2.0`
+ );
+
+ // Error: %( %| %) missing.
+ assertThrown!FormatException(
+ format("%d", tuple(1, 2)) == `1, 2`
+ );
+
+ // Error: %d inadequate for double
+ assertThrown!FormatException(
+ format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0`
+ );
}
}
}
@@ -1301,6 +1444,193 @@ template Tuple(Specs...)
assert(!is(typeof(point1) == typeof(point2)));
}
+/// Use tuples as ranges
+@safe unittest
+{
+ import std.algorithm.iteration : sum;
+ import std.range : only;
+ auto t = tuple(1, 2);
+ assert(t.expand.only.sum == 3);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=4582
+@safe unittest
+{
+ static assert(!__traits(compiles, Tuple!(string, "id", int, "id")));
+ static assert(!__traits(compiles, Tuple!(string, "str", int, "i", string, "str", float)));
+}
+
+/// Concatenate tuples
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ auto t = tuple(1, "2") ~ tuple(ushort(42), true);
+ static assert(is(t.Types == AliasSeq!(int, string, ushort, bool)));
+ assert(t[1] == "2");
+ assert(t[2] == 42);
+ assert(t[3] == true);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=14637
+// tuple concat
+@safe unittest
+{
+ auto t = tuple!"foo"(1.0) ~ tuple!"bar"("3");
+ static assert(is(t.Types == AliasSeq!(double, string)));
+ static assert(t.fieldNames == tuple("foo", "bar"));
+ assert(t.foo == 1.0);
+ assert(t.bar == "3");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18824
+// tuple concat
+@safe unittest
+{
+ alias Type = Tuple!(int, string);
+ Type[] arr;
+ auto t = tuple(2, "s");
+ // Test opBinaryRight
+ arr = arr ~ t;
+ // Test opBinary
+ arr = t ~ arr;
+ static assert(is(typeof(arr) == Type[]));
+ immutable Type[] b;
+ auto c = b ~ t;
+ static assert(is(typeof(c) == immutable(Type)[]));
+}
+
+// tuple concat
+@safe unittest
+{
+ auto t = tuple!"foo"(1.0) ~ "3";
+ static assert(is(t.Types == AliasSeq!(double, string)));
+ assert(t.foo == 1.0);
+ assert(t[1]== "3");
+}
+
+// tuple concat
+@safe unittest
+{
+ auto t = "2" ~ tuple!"foo"(1.0);
+ static assert(is(t.Types == AliasSeq!(string, double)));
+ assert(t.foo == 1.0);
+ assert(t[0]== "2");
+}
+
+// tuple concat
+@safe unittest
+{
+ auto t = "2" ~ tuple!"foo"(1.0) ~ tuple(42, 3.0f) ~ real(1) ~ "a";
+ static assert(is(t.Types == AliasSeq!(string, double, int, float, real, string)));
+ assert(t.foo == 1.0);
+ assert(t[0] == "2");
+ assert(t[1] == 1.0);
+ assert(t[2] == 42);
+ assert(t[3] == 3.0f);
+ assert(t[4] == 1.0);
+ assert(t[5] == "a");
+}
+
+// ensure that concatenation of tuples with non-distinct fields is forbidden
+@safe unittest
+{
+ static assert(!__traits(compiles,
+ tuple!("a")(0) ~ tuple!("a")("1")));
+ static assert(!__traits(compiles,
+ tuple!("a", "b")(0, 1) ~ tuple!("b", "a")("3", 1)));
+ static assert(!__traits(compiles,
+ tuple!("a")(0) ~ tuple!("b", "a")("3", 1)));
+ static assert(!__traits(compiles,
+ tuple!("a1", "a")(1.0, 0) ~ tuple!("a2", "a")("3", 0)));
+}
+
+// Ensure that Tuple comparison with non-const opEquals works
+@safe unittest
+{
+ static struct Bad
+ {
+ int a;
+
+ bool opEquals(Bad b)
+ {
+ return a == b.a;
+ }
+ }
+
+ auto t = Tuple!(int, Bad, string)(1, Bad(1), "asdf");
+
+ //Error: mutable method Bad.opEquals is not callable using a const object
+ assert(t == AliasSeq!(1, Bad(1), "asdf"));
+}
+
+// Ensure Tuple.toHash works
+@safe unittest
+{
+ Tuple!(int, int) point;
+ assert(point.toHash == typeof(point).init.toHash);
+ assert(tuple(1, 2) != point);
+ assert(tuple(1, 2) == tuple(1, 2));
+ point[0] = 1;
+ assert(tuple(1, 2) != point);
+ point[1] = 2;
+ assert(tuple(1, 2) == point);
+}
+
+@safe @betterC unittest
+{
+ auto t = tuple(1, 2);
+ assert(t == tuple(1, 2));
+ auto t3 = tuple(1, 'd');
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20850
+// Assignment to enum tuple
+@safe unittest
+{
+ enum T : Tuple!(int*) { a = T(null) }
+ T t;
+ t = T.a;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=13663
+@safe unittest
+{
+ auto t = tuple(real.nan);
+ assert(!(t > t));
+ assert(!(t < t));
+ assert(!(t == t));
+}
+
+@safe unittest
+{
+ struct S
+ {
+ float opCmp(S s) { return float.nan; }
+ bool opEquals(S s) { return false; }
+ }
+
+ auto t = tuple(S());
+ assert(!(t > t));
+ assert(!(t < t));
+ assert(!(t == t));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=8015
+@safe unittest
+{
+ struct MyStruct
+ {
+ string str;
+ @property string toStr()
+ {
+ return str;
+ }
+ alias toStr this;
+ }
+
+ Tuple!(MyStruct) t;
+}
+
/**
Creates a copy of a $(LREF Tuple) with its fields in _reverse order.
@@ -1311,7 +1641,7 @@ template Tuple(Specs...)
A new `Tuple`.
*/
auto reverse(T)(T t)
- if (isTuple!T)
+if (isTuple!T)
{
import std.meta : Reverse;
// @@@BUG@@@ Cannot be an internal function due to forward reference issues.
@@ -1334,7 +1664,7 @@ auto reverse(T)(T t)
/* Get a Tuple type with the reverse specification of Tuple T. */
private template ReverseTupleType(T)
- if (isTuple!T)
+if (isTuple!T)
{
static if (is(T : Tuple!A, A...))
alias ReverseTupleType = Tuple!(ReverseTupleSpecs!A);
@@ -1479,7 +1809,7 @@ private template ReverseTupleSpecs(T...)
ssCopy[1] = ssCopy[0];
assert(ssCopy[1].count == 2);
}
- // bug 2800
+ // https://issues.dlang.org/show_bug.cgi?id=2800
{
static struct R
{
@@ -1499,7 +1829,7 @@ private template ReverseTupleSpecs(T...)
{
auto t1 = Tuple!(int, double)(1, 1);
- // 8702
+ // https://issues.dlang.org/show_bug.cgi?id=8702
auto t8702a = tuple(tuple(1));
auto t8702b = Tuple!(Tuple!(int))(Tuple!(int)(1));
}
@@ -1514,19 +1844,19 @@ private template ReverseTupleSpecs(T...)
// incompatible
static assert(!__traits(compiles, Tuple!(int, int)(y)));
}
- // 6275
+ // https://issues.dlang.org/show_bug.cgi?id=6275
{
const int x = 1;
auto t1 = tuple(x);
alias T = Tuple!(const(int));
auto t2 = T(1);
}
- // 9431
+ // https://issues.dlang.org/show_bug.cgi?id=9431
{
alias T = Tuple!(int[1][]);
auto t = T([[10]]);
}
- // 7666
+ // https://issues.dlang.org/show_bug.cgi?id=7666
{
auto tup = tuple(1, "2");
assert(tup.reverse == tuple("2", 1));
@@ -1565,8 +1895,9 @@ private template ReverseTupleSpecs(T...)
static assert( is(typeof(tc2 == tm2)));
static assert( is(typeof(tc2 == tc2)));
+ // https://issues.dlang.org/show_bug.cgi?id=8686
struct Equ3 { bool opEquals(T)(T) { return true; } }
- auto tm3 = tuple(Equ3.init); // bugzilla 8686
+ auto tm3 = tuple(Equ3.init);
const tc3 = tuple(Equ3.init);
static assert( is(typeof(tm3 == tm3)));
static assert( is(typeof(tm3 == tc3)));
@@ -1615,7 +1946,7 @@ private template ReverseTupleSpecs(T...)
static assert( is(typeof(tc4 < tm4)));
static assert( is(typeof(tc4 < tc4)));
}
- // Bugzilla 14890
+ // https://issues.dlang.org/show_bug.cgi?id=14890
static void test14890(inout int[] dummy)
{
alias V = Tuple!(int, int);
@@ -1626,8 +1957,8 @@ private template ReverseTupleSpecs(T...)
inout V wv; // OK <- NG
inout const V wcv; // OK <- NG
- foreach (v1; AliasSeq!(mv, cv, iv, wv, wcv))
- foreach (v2; AliasSeq!(mv, cv, iv, wv, wcv))
+ static foreach (v1; AliasSeq!(mv, cv, iv, wv, wcv))
+ static foreach (v2; AliasSeq!(mv, cv, iv, wv, wcv))
{
assert(!(v1 < v2));
}
@@ -1657,7 +1988,7 @@ private template ReverseTupleSpecs(T...)
}
@safe unittest
{
- // Bugzilla 10686
+ // https://issues.dlang.org/show_bug.cgi?id=10686
immutable Tuple!(int) t1;
auto r1 = t1[0]; // OK
immutable Tuple!(int, "x") t2;
@@ -1667,7 +1998,7 @@ private template ReverseTupleSpecs(T...)
{
import std.exception : assertCTFEable;
- // Bugzilla 10218
+ // https://issues.dlang.org/show_bug.cgi?id=10218
assertCTFEable!(
{
auto t = tuple(1);
@@ -1702,7 +2033,7 @@ private template ReverseTupleSpecs(T...)
TISIS e = TISIS(ss);
}
-// Bugzilla #9819
+// https://issues.dlang.org/show_bug.cgi?id=9819
@safe unittest
{
alias T = Tuple!(int, "x", double, "foo");
@@ -1713,7 +2044,7 @@ private template ReverseTupleSpecs(T...)
static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
}
-// Bugzilla 13837
+// https://issues.dlang.org/show_bug.cgi?id=13837
@safe unittest
{
// New behaviour, named arguments.
@@ -1749,7 +2080,7 @@ private template ReverseTupleSpecs(T...)
@safe unittest
{
- class C {}
+ class C { override size_t toHash() const nothrow @safe { return 0; } }
Tuple!(Rebindable!(const C)) a;
Tuple!(const C) b;
a = b;
@@ -1767,39 +2098,24 @@ private template ReverseTupleSpecs(T...)
import std.format : format, FormatException;
import std.exception : assertThrown;
- // enum tupStr = tuple(1, 1.0).toString; // toString is *impure*.
+ //enum tupStr = tuple(1, 1.0).toString; // toString is *impure*.
//static assert(tupStr == `Tuple!(int, double)(1, 1)`);
+}
- Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];
-
- // Default format
- assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`);
-
- // One Format for each individual component
- assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`);
- assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`);
-
- // One Format for all components
- assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`);
-
- // Array of Tuples
- assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`);
-
-
- // Error: %( %) missing.
- assertThrown!FormatException(
- format("%d, %f", tuple(1, 2.0)) == `1, 2.0`
- );
-
- // Error: %( %| %) missing.
- assertThrown!FormatException(
- format("%d", tuple(1, 2)) == `1, 2`
- );
-
- // Error: %d inadequate for double
- assertThrown!FormatException(
- format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0`
- );
+// https://issues.dlang.org/show_bug.cgi?id=17803, parte uno
+@safe unittest
+{
+ auto a = tuple(3, "foo");
+ assert(__traits(compiles, { a = (a = a); }));
+}
+// Ditto
+@safe unittest
+{
+ Tuple!(int[]) a, b, c;
+ a = tuple([0, 1, 2]);
+ c = b = a;
+ assert(a[0].length == b[0].length && b[0].length == c[0].length);
+ assert(a[0].ptr == b[0].ptr && b[0].ptr == c[0].ptr);
}
/**
@@ -1807,11 +2123,15 @@ private template ReverseTupleSpecs(T...)
the given arguments.
Params:
- Names = An optional list of strings naming each successive field of the `Tuple`.
- Each name matches up with the corresponding field given by `Args`.
+ Names = An optional list of strings naming each successive field of the `Tuple`
+ or a list of types that the elements are being casted to.
+ For a list of names,
+ each name matches up with the corresponding field given by `Args`.
A name does not have to be provided for every field, but as
the names must proceed in order, it is not possible to skip
one field and name the next after it.
+ For a list of types,
+ there must be exactly as many types as parameters.
*/
template tuple(Names...)
{
@@ -1877,7 +2197,7 @@ template tuple(Names...)
}
/**
- Returns $(D true) if and only if $(D T) is an instance of $(D std.typecons.Tuple).
+ Returns `true` if and only if `T` is an instance of `std.typecons.Tuple`.
Params:
T = The type to check.
@@ -1915,7 +2235,7 @@ enum isTuple(T) = __traits(compiles,
// used by both Rebindable and UnqualRef
private mixin template RebindableCommon(T, U, alias This)
- if (is(T == class) || is(T == interface) || isAssociativeArray!T)
+if (is(T == class) || is(T == interface) || isAssociativeArray!T)
{
private union
{
@@ -1923,58 +2243,75 @@ private mixin template RebindableCommon(T, U, alias This)
U stripped;
}
- @trusted pure nothrow @nogc
+ void opAssign(T another) pure nothrow @nogc
{
- void opAssign(T another)
+ // If `T` defines `opCast` we must infer the safety
+ static if (hasMember!(T, "opCast"))
{
- stripped = cast(U) another;
+ // This will allow the compiler to infer the safety of `T.opCast!U`
+ // without generating any runtime cost
+ if (false) { stripped = cast(U) another; }
}
+ () @trusted { stripped = cast(U) another; }();
+ }
- void opAssign(typeof(this) another)
+ void opAssign(typeof(this) another) @trusted pure nothrow @nogc
+ {
+ stripped = another.stripped;
+ }
+
+ static if (is(T == const U) && is(T == const shared U))
+ {
+ // safely assign immutable to const / const shared
+ void opAssign(This!(immutable U) another) @trusted pure nothrow @nogc
{
stripped = another.stripped;
}
+ }
- static if (is(T == const U) && is(T == const shared U))
- {
- // safely assign immutable to const / const shared
- void opAssign(This!(immutable U) another)
- {
- stripped = another.stripped;
- }
- }
+ this(T initializer) pure nothrow @nogc
+ {
+ // Infer safety from opAssign
+ opAssign(initializer);
+ }
- this(T initializer)
- {
- opAssign(initializer);
- }
+ @property inout(T) get() @trusted pure nothrow @nogc inout
+ {
+ return original;
+ }
- @property inout(T) get() inout
- {
- return original;
- }
+ bool opEquals()(auto ref const(typeof(this)) rhs) const
+ {
+ // Must forward explicitly because 'stripped' is part of a union.
+ // The necessary 'toHash' is forwarded to the class via alias this.
+ return stripped == rhs.stripped;
+ }
+
+ bool opEquals(const(U) rhs) const
+ {
+ return stripped == rhs;
}
alias get this;
}
/**
-$(D Rebindable!(T)) is a simple, efficient wrapper that behaves just
-like an object of type $(D T), except that you can reassign it to
-refer to another object. For completeness, $(D Rebindable!(T)) aliases
-itself away to $(D T) if $(D T) is a non-const object type.
-
-You may want to use $(D Rebindable) when you want to have mutable
-storage referring to $(D const) objects, for example an array of
-references that must be sorted in place. $(D Rebindable) does not
+`Rebindable!(T)` is a simple, efficient wrapper that behaves just
+like an object of type `T`, except that you can reassign it to
+refer to another object. For completeness, `Rebindable!(T)` aliases
+itself away to `T` if `T` is a non-const object type.
+
+You may want to use `Rebindable` when you want to have mutable
+storage referring to `const` objects, for example an array of
+references that must be sorted in place. `Rebindable` does not
break the soundness of D's type system and does not incur any of the
-risks usually associated with $(D cast).
+risks usually associated with `cast`.
Params:
T = An object, interface, array slice type, or associative array type.
*/
template Rebindable(T)
- if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
+if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
{
static if (is(T == const U, U) || is(T == immutable U, U))
{
@@ -1997,10 +2334,10 @@ template Rebindable(T)
}
}
-///Regular $(D const) object references cannot be reassigned.
-@system unittest
+///Regular `const` object references cannot be reassigned.
+@safe unittest
{
- class Widget { int x; int y() const { return x; } }
+ class Widget { int x; int y() @safe const { return x; } }
const a = new Widget;
// Fine
a.y();
@@ -2011,12 +2348,12 @@ template Rebindable(T)
}
/**
- However, $(D Rebindable!(Widget)) does allow reassignment,
+ However, `Rebindable!(Widget)` does allow reassignment,
while otherwise behaving exactly like a $(D const Widget).
*/
-@system unittest
+@safe unittest
{
- class Widget { int x; int y() const { return x; } }
+ class Widget { int x; int y() const @safe { return x; } }
auto a = Rebindable!(const Widget)(new Widget);
// Fine
a.y();
@@ -2026,15 +2363,94 @@ template Rebindable(T)
a = new Widget;
}
-@safe unittest // issue 16054
+// https://issues.dlang.org/show_bug.cgi?id=16054
+@safe unittest
{
Rebindable!(immutable Object) r;
static assert(__traits(compiles, r.get()));
static assert(!__traits(compiles, &r.get()));
}
+@safe unittest
+{
+ class CustomToHash
+ {
+ override size_t toHash() const nothrow @trusted { return 42; }
+ }
+ Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash();
+ assert(a.toHash() == 42, "Rebindable!A should offer toHash()"
+ ~ " by forwarding to A.toHash().");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18615
+// Rebindable!A should use A.opEqualsa
+@system unittest
+{
+ class CustomOpEq
+ {
+ int x;
+ override bool opEquals(Object rhsObj)
+ {
+ if (auto rhs = cast(const(CustomOpEq)) rhsObj)
+ return this.x == rhs.x;
+ else
+ return false;
+ }
+ }
+ CustomOpEq a = new CustomOpEq();
+ CustomOpEq b = new CustomOpEq();
+ assert(a !is b);
+ assert(a == b, "a.x == b.x should be true (0 == 0).");
+
+ Rebindable!(const(CustomOpEq)) ra = a;
+ Rebindable!(const(CustomOpEq)) rb = b;
+ assert(ra !is rb);
+ assert(ra == rb, "Rebindable should use CustomOpEq's opEquals, not 'is'.");
+ assert(ra == b, "Rebindable!(someQualifier(A)) should be comparable"
+ ~ " against const(A) via A.opEquals.");
+ assert(a == rb, "Rebindable!(someQualifier(A)) should be comparable"
+ ~ " against const(A) via A.opEquals.");
+
+ b.x = 1;
+ assert(a != b);
+ assert(ra != b, "Rebindable!(someQualifier(A)) should be comparable"
+ ~ " against const(A) via A.opEquals.");
+ assert(a != rb, "Rebindable!(someQualifier(A)) should be comparable"
+ ~ " against const(A) via A.opEquals.");
+
+ Rebindable!(const(Object)) o1 = new Object();
+ Rebindable!(const(Object)) o2 = new Object();
+ assert(o1 !is o2);
+ assert(o1 == o1, "When the class doesn't provide its own opEquals,"
+ ~ " Rebindable treats 'a == b' as 'a is b' like Object.opEquals.");
+ assert(o1 != o2, "When the class doesn't provide its own opEquals,"
+ ~ " Rebindable treats 'a == b' as 'a is b' like Object.opEquals.");
+ assert(o1 != new Object(), "Rebindable!(const(Object)) should be"
+ ~ " comparable against Object itself and use Object.opEquals.");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18755
+@safe unittest
+{
+ static class Foo
+ {
+ auto opCast(T)() @system immutable pure nothrow
+ {
+ *(cast(uint*) 0xdeadbeef) = 0xcafebabe;
+ return T.init;
+ }
+ }
+
+ static assert(!__traits(compiles, () @safe {
+ auto r = Rebindable!(immutable Foo)(new Foo);
+ }));
+ static assert(__traits(compiles, () @system {
+ auto r = Rebindable!(immutable Foo)(new Foo);
+ }));
+}
+
/**
-Convenience function for creating a $(D Rebindable) using automatic type
+Convenience function for creating a `Rebindable` using automatic type
inference.
Params:
@@ -2045,17 +2461,39 @@ Returns:
A newly constructed `Rebindable` initialized with the given reference.
*/
Rebindable!T rebindable(T)(T obj)
- if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
+if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
{
typeof(return) ret;
ret = obj;
return ret;
}
+///
+@system unittest
+{
+ class C
+ {
+ int payload;
+ this(int p) { payload = p; }
+ }
+ const c = new C(1);
+
+ auto c2 = c.rebindable;
+ assert(c2.payload == 1);
+ // passing Rebindable to rebindable
+ c2 = c2.rebindable;
+
+ c2 = new C(2);
+ assert(c2.payload == 2);
+
+ const c3 = c2.get;
+ assert(c3.payload == 2);
+}
+
/**
-This function simply returns the $(D Rebindable) object passed in. It's useful
+This function simply returns the `Rebindable` object passed in. It's useful
in generic programming cases when a given object may be either a regular
-$(D class) or a $(D Rebindable).
+`class` or a `Rebindable`.
Params:
obj = An instance of Rebindable!T.
@@ -2068,6 +2506,24 @@ Rebindable!T rebindable(T)(Rebindable!T obj)
return obj;
}
+// TODO: remove me once the rebindable overloads have been joined
+///
+@system unittest
+{
+ class C
+ {
+ int payload;
+ this(int p) { payload = p; }
+ }
+ const c = new C(1);
+
+ auto c2 = c.rebindable;
+ assert(c2.payload == 1);
+ // passing Rebindable to rebindable
+ c2 = c2.rebindable;
+ assert(c2.payload == 1);
+}
+
@system unittest
{
interface CI { int foo() const; }
@@ -2135,18 +2591,18 @@ Rebindable!T rebindable(T)(Rebindable!T obj)
assert(rebindable(arr) == arr);
assert(rebindable(arrConst) == arr);
- // Issue 7654
+ // https://issues.dlang.org/show_bug.cgi?id=7654
immutable(char[]) s7654;
Rebindable!(typeof(s7654)) r7654 = s7654;
- foreach (T; AliasSeq!(char, wchar, char, int))
+ static foreach (T; AliasSeq!(char, wchar, char, int))
{
static assert(is(Rebindable!(immutable(T[])) == immutable(T)[]));
static assert(is(Rebindable!(const(T[])) == const(T)[]));
static assert(is(Rebindable!(T[]) == T[]));
}
- // Issue 12046
+ // https://issues.dlang.org/show_bug.cgi?id=12046
static assert(!__traits(compiles, Rebindable!(int[1])));
static assert(!__traits(compiles, Rebindable!(const int[1])));
@@ -2160,7 +2616,7 @@ Rebindable!T rebindable(T)(Rebindable!T obj)
}
/**
- Similar to $(D Rebindable!(T)) but strips all qualifiers from the reference as
+ Similar to `Rebindable!(T)` but strips all qualifiers from the reference as
opposed to just constness / immutability. Primary intended use case is with
shared (having thread-local reference to shared class data)
@@ -2168,12 +2624,12 @@ Rebindable!T rebindable(T)(Rebindable!T obj)
T = A class or interface type.
*/
template UnqualRef(T)
- if (is(T == class) || is(T == interface))
+if (is(T == class) || is(T == interface))
{
- static if (is(T == const U, U)
- || is(T == immutable U, U)
- || is(T == shared U, U)
- || is(T == const shared U, U))
+ static if (is(T == immutable U, U)
+ || is(T == const shared U, U)
+ || is(T == const U, U)
+ || is(T == shared U, U))
{
struct UnqualRef
{
@@ -2271,20 +2727,20 @@ string alignForSize(E...)(const char[][] names...)
{
enum x = alignForSize!(int[], char[3], short, double[5])("x", "y","z", "w");
struct Foo { int x; }
- enum y = alignForSize!(ubyte, Foo, cdouble)("x", "y", "z");
+ enum y = alignForSize!(ubyte, Foo, double)("x", "y", "z");
enum passNormalX = x == "double[5] w;\nint[] x;\nshort z;\nchar[3] y;\n";
- enum passNormalY = y == "cdouble z;\nFoo y;\nubyte x;\n";
+ enum passNormalY = y == "double z;\nFoo y;\nubyte x;\n";
enum passAbnormalX = x == "int[] x;\ndouble[5] w;\nshort z;\nchar[3] y;\n";
- enum passAbnormalY = y == "Foo y;\ncdouble z;\nubyte x;\n";
- // ^ blame http://d.puremagic.com/issues/show_bug.cgi?id=231
+ enum passAbnormalY = y == "Foo y;\ndouble z;\nubyte x;\n";
+ // ^ blame https://issues.dlang.org/show_bug.cgi?id=231
static assert(passNormalX || passAbnormalX && double.alignof <= (int[]).alignof);
static assert(passNormalY || passAbnormalY && double.alignof <= int.alignof);
}
-// Issue 12914
+// https://issues.dlang.org/show_bug.cgi?id=12914
@safe unittest
{
immutable string[] fieldNames = ["x", "y"];
@@ -2298,46 +2754,72 @@ string alignForSize(E...)(const char[][] names...)
Defines a value paired with a distinctive "null" state that denotes
the absence of a value. If default constructed, a $(D
Nullable!T) object starts in the null state. Assigning it renders it
-non-null. Calling $(D nullify) can nullify it again.
+non-null. Calling `nullify` can nullify it again.
-Practically $(D Nullable!T) stores a $(D T) and a $(D bool).
+Practically `Nullable!T` stores a `T` and a `bool`.
*/
struct Nullable(T)
{
- private T _value;
- private bool _isNull = true;
+ private union DontCallDestructorT
+ {
+ T payload;
+ }
-/**
-Constructor initializing $(D this) with $(D value).
+ private DontCallDestructorT _value = DontCallDestructorT.init;
-Params:
- value = The value to initialize this `Nullable` with.
- */
+ private bool _isNull = true;
+
+ /**
+ * Constructor initializing `this` with `value`.
+ *
+ * Params:
+ * value = The value to initialize this `Nullable` with.
+ */
this(inout T value) inout
{
- _value = value;
+ _value.payload = value;
_isNull = false;
}
+ static if (hasElaborateDestructor!T)
+ {
+ ~this()
+ {
+ if (!_isNull)
+ {
+ destroy(_value.payload);
+ }
+ }
+ }
+
/**
- If they are both null, then they are equal. If one is null and the other
- is not, then they are not equal. If they are both non-null, then they are
- equal if their values are equal.
- */
- bool opEquals()(auto ref const(typeof(this)) rhs) const
+ * If they are both null, then they are equal. If one is null and the other
+ * is not, then they are not equal. If they are both non-null, then they are
+ * equal if their values are equal.
+ */
+ bool opEquals(this This, Rhs)(auto ref Rhs rhs)
+ if (!is(CommonType!(This, Rhs) == void))
{
- if (_isNull)
- return rhs._isNull;
- if (rhs._isNull)
- return false;
- return _value == rhs._value;
+ static if (is(This == Rhs))
+ {
+ if (_isNull)
+ return rhs._isNull;
+ if (rhs._isNull)
+ return false;
+ return _value.payload == rhs._value.payload;
+ }
+ else
+ {
+ alias Common = CommonType!(This, Rhs);
+ return cast(Common) this == cast(Common) rhs;
+ }
}
/// Ditto
- bool opEquals(U)(auto ref const(U) rhs) const
- if (is(typeof(this.get == rhs)))
+ bool opEquals(this This, Rhs)(auto ref Rhs rhs)
+ if (is(CommonType!(This, Rhs) == void) && is(typeof(this.get == rhs)))
{
- return _isNull ? false : rhs == _value;
+ return _isNull ? false : rhs == _value.payload;
}
///
@@ -2383,7 +2865,7 @@ Params:
assert(a != Nullable!int(29));
}
- // Issue 17482
+ // https://issues.dlang.org/show_bug.cgi?id=17482
@system unittest
{
import std.variant : Variant;
@@ -2393,158 +2875,214 @@ Params:
assert(e != 12);
}
- template toString()
+ size_t toHash() const @safe nothrow
{
- import std.format : FormatSpec, formatValue;
- // Needs to be a template because of DMD @@BUG@@ 13737.
- void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
- {
- if (isNull)
- {
- sink.formatValue("Nullable.null", fmt);
- }
- else
- {
- sink.formatValue(_value, fmt);
- }
- }
+ static if (__traits(compiles, .hashOf(_value.payload)))
+ return _isNull ? 0 : .hashOf(_value.payload);
+ else
+ // Workaround for when .hashOf is not both @safe and nothrow.
+ return _isNull ? 0 : typeid(T).getHash(&_value.payload);
+ }
- // Issue 14940
- void toString()(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt)
- {
- if (isNull)
- {
- sink.formatValue("Nullable.null", fmt);
- }
- else
- {
- sink.formatValue(_value, fmt);
- }
- }
+ /**
+ * Gives the string `"Nullable.null"` if `isNull` is `true`. Otherwise, the
+ * result is equivalent to calling $(REF formattedWrite, std,format) on the
+ * underlying value.
+ *
+ * Params:
+ * writer = A `char` accepting
+ * $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
+ * fmt = A $(REF FormatSpec, std,format) which is used to represent
+ * the value if this Nullable is not null
+ * Returns:
+ * A `string` if `writer` and `fmt` are not set; `void` otherwise.
+ */
+ string toString()
+ {
+ import std.array : appender;
+ auto app = appender!string();
+ auto spec = singleSpec("%s");
+ toString(app, spec);
+ return app.data;
}
-/**
-Check if `this` is in the null state.
+ /// ditto
+ string toString() const
+ {
+ import std.array : appender;
+ auto app = appender!string();
+ auto spec = singleSpec("%s");
+ toString(app, spec);
+ return app.data;
+ }
-Returns:
- true $(B iff) `this` is in the null state, otherwise false.
- */
+ /// ditto
+ void toString(W)(ref W writer, scope const ref FormatSpec!char fmt)
+ if (isOutputRange!(W, char))
+ {
+ import std.range.primitives : put;
+ if (isNull)
+ put(writer, "Nullable.null");
+ else
+ formatValue(writer, _value.payload, fmt);
+ }
+
+ /// ditto
+ void toString(W)(ref W writer, scope const ref FormatSpec!char fmt) const
+ if (isOutputRange!(W, char))
+ {
+ import std.range.primitives : put;
+ if (isNull)
+ put(writer, "Nullable.null");
+ else
+ formatValue(writer, _value.payload, fmt);
+ }
+
+ /**
+ * Check if `this` is in the null state.
+ *
+ * Returns:
+ * true $(B iff) `this` is in the null state, otherwise false.
+ */
@property bool isNull() const @safe pure nothrow
{
return _isNull;
}
-///
-@system unittest
-{
- Nullable!int ni;
- assert(ni.isNull);
+ ///
+ @safe unittest
+ {
+ Nullable!int ni;
+ assert(ni.isNull);
- ni = 0;
- assert(!ni.isNull);
-}
+ ni = 0;
+ assert(!ni.isNull);
+ }
-// Issue 14940
-@safe unittest
-{
- import std.array : appender;
- import std.format : formattedWrite;
+ // https://issues.dlang.org/show_bug.cgi?id=14940
+ @safe unittest
+ {
+ import std.array : appender;
+ import std.format.write : formattedWrite;
- auto app = appender!string();
- Nullable!int a = 1;
- formattedWrite(app, "%s", a);
- assert(app.data == "1");
-}
+ auto app = appender!string();
+ Nullable!int a = 1;
+ formattedWrite(app, "%s", a);
+ assert(app.data == "1");
+ }
-/**
-Forces $(D this) to the null state.
- */
+ // https://issues.dlang.org/show_bug.cgi?id=19799
+ @safe unittest
+ {
+ import std.format : format;
+
+ const Nullable!string a = const(Nullable!string)();
+
+ format!"%s"(a);
+ }
+
+ /**
+ * Forces `this` to the null state.
+ */
void nullify()()
{
- .destroy(_value);
+ static if (is(T == class) || is(T == interface))
+ _value.payload = null;
+ else
+ .destroy(_value.payload);
_isNull = true;
}
-///
-@safe unittest
-{
- Nullable!int ni = 0;
- assert(!ni.isNull);
-
- ni.nullify();
- assert(ni.isNull);
-}
+ ///
+ @safe unittest
+ {
+ Nullable!int ni = 0;
+ assert(!ni.isNull);
-/**
-Assigns $(D value) to the internally-held state. If the assignment
-succeeds, $(D this) becomes non-null.
+ ni.nullify();
+ assert(ni.isNull);
+ }
-Params:
- value = A value of type `T` to assign to this `Nullable`.
- */
- void opAssign()(T value)
+ /**
+ * Assigns `value` to the internally-held state. If the assignment
+ * succeeds, `this` becomes non-null.
+ *
+ * Params:
+ * value = A value of type `T` to assign to this `Nullable`.
+ */
+ Nullable opAssign()(T value)
{
- _value = value;
+ import std.algorithm.mutation : moveEmplace, move;
+
+ // the lifetime of the value in copy shall be managed by
+ // this Nullable, so we must avoid calling its destructor.
+ auto copy = DontCallDestructorT(value);
+
+ if (_isNull)
+ {
+ // trusted since payload is known to be T.init here.
+ () @trusted { moveEmplace(copy.payload, _value.payload); }();
+ }
+ else
+ {
+ move(copy.payload, _value.payload);
+ }
_isNull = false;
+ return this;
}
-/**
- If this `Nullable` wraps a type that already has a null value
- (such as a pointer), then assigning the null value to this
- `Nullable` is no different than assigning any other value of
- type `T`, and the resulting code will look very strange. It
- is strongly recommended that this be avoided by instead using
- the version of `Nullable` that takes an additional `nullValue`
- template argument.
- */
-@safe unittest
-{
- //Passes
- Nullable!(int*) npi;
- assert(npi.isNull);
-
- //Passes?!
- npi = null;
- assert(!npi.isNull);
-}
+ /**
+ * If this `Nullable` wraps a type that already has a null value
+ * (such as a pointer), then assigning the null value to this
+ * `Nullable` is no different than assigning any other value of
+ * type `T`, and the resulting code will look very strange. It
+ * is strongly recommended that this be avoided by instead using
+ * the version of `Nullable` that takes an additional `nullValue`
+ * template argument.
+ */
+ @safe unittest
+ {
+ //Passes
+ Nullable!(int*) npi;
+ assert(npi.isNull);
-/**
-Gets the value. $(D this) must not be in the null state.
-This function is also called for the implicit conversion to $(D T).
+ //Passes?!
+ npi = null;
+ assert(!npi.isNull);
+ }
-Returns:
- The value held internally by this `Nullable`.
- */
+ /**
+ * Gets the value if not null. If `this` is in the null state, and the optional
+ * parameter `fallback` was provided, it will be returned. Without `fallback`,
+ * calling `get` with a null state is invalid.
+ *
+ * When the fallback type is different from the Nullable type, `get(T)` returns
+ * the common type.
+ *
+ * Params:
+ * fallback = the value to return in case the `Nullable` is null.
+ *
+ * Returns:
+ * The value held internally by this `Nullable`.
+ */
@property ref inout(T) get() inout @safe pure nothrow
{
enum message = "Called `get' on null Nullable!" ~ T.stringof ~ ".";
assert(!isNull, message);
- return _value;
+ return _value.payload;
}
-///
-@system unittest
-{
- import core.exception : AssertError;
- import std.exception : assertThrown, assertNotThrown;
-
- Nullable!int ni;
- int i = 42;
- //`get` is implicitly called. Will throw
- //an AssertError in non-release mode
- assertThrown!AssertError(i = ni);
- assert(i == 42);
-
- ni = 5;
- assertNotThrown!AssertError(i = ni);
- assert(i == 5);
-}
+ /// ditto
+ @property inout(T) get()(inout(T) fallback) inout
+ {
+ return isNull ? fallback : _value.payload;
+ }
-/**
-Implicitly converts to $(D T).
-$(D this) must not be in the null state.
- */
- alias get this;
+ /// ditto
+ @property auto get(U)(inout(U) fallback) inout
+ {
+ return isNull ? fallback : _value.payload;
+ }
}
/// ditto
@@ -2574,8 +3112,8 @@ auto nullable(T)(T t)
if (!queryResult.isNull)
{
//Process Mr. Doe's customer record
- auto address = queryResult.address;
- auto customerNum = queryResult.customerNum;
+ auto address = queryResult.get.address;
+ auto customerNum = queryResult.get.customerNum;
//Do some things with this customer's info
}
@@ -2598,30 +3136,6 @@ auto nullable(T)(T t)
assert(a.isNull);
assertThrown!Throwable(a.get);
}
-
-@system unittest
-{
- import std.exception : assertThrown;
-
- Nullable!int a;
- assert(a.isNull);
- assertThrown!Throwable(a.get);
- a = 5;
- assert(!a.isNull);
- assert(a == 5);
- assert(a != 3);
- assert(a.get != 3);
- a.nullify();
- assert(a.isNull);
- a = 3;
- assert(a == 3);
- a *= 6;
- assert(a == 18);
- a = a;
- assert(a == 18);
- a.nullify();
- assertThrown!Throwable(a += 2);
-}
@safe unittest
{
auto k = Nullable!int(74);
@@ -2631,7 +3145,7 @@ auto nullable(T)(T t)
}
@safe unittest
{
- static int f(in Nullable!int x) {
+ static int f(scope const Nullable!int x) {
return x.isNull ? 42 : x.get;
}
Nullable!int a;
@@ -2652,10 +3166,10 @@ auto nullable(T)(T t)
assert(s == S(6));
assert(s != S(0));
assert(s.get != S(0));
- s.x = 9190;
- assert(s.x == 9190);
+ s.get.x = 9190;
+ assert(s.get.x == 9190);
s.nullify();
- assertThrown!Throwable(s.x = 9441);
+ assertThrown!Throwable(s.get.x = 9441);
}
@safe unittest
{
@@ -2684,13 +3198,14 @@ auto nullable(T)(T t)
assert(s.isNull);
s = S(5);
assert(!s.isNull);
- assert(s.x == 5);
+ assert(s.get.x == 5);
s.nullify();
assert(s.isNull);
}
+
+// https://issues.dlang.org/show_bug.cgi?id=9404
@safe unittest
{
- // Bugzilla 9404
alias N = Nullable!int;
void foo(N a)
@@ -2778,17 +3293,18 @@ auto nullable(T)(T t)
ni = other.ni;
}
}
- foreach (S; AliasSeq!(S1, S2))
- {
+ static foreach (S; AliasSeq!(S1, S2))
+ {{
S a;
S b = a;
S c;
c = a;
- }
+ }}
}
+
+// https://issues.dlang.org/show_bug.cgi?id=10268
@system unittest
{
- // Bugzilla 10268
import std.json;
JSONValue value = null;
auto na = Nullable!JSONValue(value);
@@ -2804,10 +3320,10 @@ auto nullable(T)(T t)
auto x2 = immutable Nullable!S1(sm);
auto x3 = Nullable!S1(si);
auto x4 = immutable Nullable!S1(si);
- assert(x1.val == 1);
- assert(x2.val == 1);
- assert(x3.val == 1);
- assert(x4.val == 1);
+ assert(x1.get.val == 1);
+ assert(x2.get.val == 1);
+ assert(x3.get.val == 1);
+ assert(x4.get.val == 1);
}
auto nm = 10;
@@ -2820,8 +3336,8 @@ auto nullable(T)(T t)
static assert(!__traits(compiles, { auto x2 = immutable Nullable!S2(sm); }));
static assert(!__traits(compiles, { auto x3 = Nullable!S2(si); }));
auto x4 = immutable Nullable!S2(si);
- assert(*x1.val == 10);
- assert(*x4.val == 10);
+ assert(*x1.get.val == 10);
+ assert(*x4.get.val == 10);
}
{
@@ -2831,34 +3347,42 @@ auto nullable(T)(T t)
auto x2 = immutable Nullable!S3(sm);
auto x3 = Nullable!S3(si);
auto x4 = immutable Nullable!S3(si);
- assert(*x1.val == 10);
- assert(*x2.val == 10);
- assert(*x3.val == 10);
- assert(*x4.val == 10);
+ assert(*x1.get.val == 10);
+ assert(*x2.get.val == 10);
+ assert(*x3.get.val == 10);
+ assert(*x4.get.val == 10);
}
}
+
+// https://issues.dlang.org/show_bug.cgi?id=10357
@safe unittest
{
- // Bugzila 10357
import std.datetime;
Nullable!SysTime time = SysTime(0);
}
+
+// https://issues.dlang.org/show_bug.cgi?id=10915
@system unittest
{
import std.conv : to;
import std.array;
- // Bugzilla 10915
Appender!string buffer;
Nullable!int ni;
assert(ni.to!string() == "Nullable.null");
+ assert((cast(const) ni).to!string() == "Nullable.null");
struct Test { string s; }
alias NullableTest = Nullable!Test;
NullableTest nt = Test("test");
+ // test output range version
assert(nt.to!string() == `Test("test")`);
+ // test appender version
+ assert(nt.toString() == `Test("test")`);
+ // test const version
+ assert((cast(const) nt).toString() == `const(Test)("test")`);
NullableTest ntn = Test("null");
assert(ntn.to!string() == `Test("null")`);
@@ -2881,12 +3405,219 @@ auto nullable(T)(T t)
assert(ntts.to!string() == "2.5");
}
+// https://issues.dlang.org/show_bug.cgi?id=14477
+@safe unittest
+{
+ static struct DisabledDefaultConstructor
+ {
+ @disable this();
+ this(int i) { }
+ }
+ Nullable!DisabledDefaultConstructor var;
+ var = DisabledDefaultConstructor(5);
+ var.nullify;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17440
+@system unittest
+{
+ static interface I { }
+
+ static class C : I
+ {
+ int canary;
+ ~this()
+ {
+ canary = 0x5050DEAD;
+ }
+ }
+ auto c = new C;
+ c.canary = 0xA71FE;
+ auto nc = nullable(c);
+ nc.nullify;
+ assert(c.canary == 0xA71FE);
+
+ I i = c;
+ auto ni = nullable(i);
+ ni.nullify;
+ assert(c.canary == 0xA71FE);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19037
+@safe unittest
+{
+ import std.datetime : SysTime;
+
+ struct Test
+ {
+ bool b;
+
+ nothrow invariant { assert(b == true); }
+
+ SysTime _st;
+
+ static bool destroyed;
+
+ @disable this();
+ this(bool b) { this.b = b; }
+ ~this() @safe { destroyed = true; }
+
+ // mustn't call opAssign on Test.init in Nullable!Test, because the invariant
+ // will be called before opAssign on the Test.init that is in Nullable
+ // and Test.init violates its invariant.
+ void opAssign(Test rhs) @safe { assert(false); }
+ }
+
+ {
+ Nullable!Test nt;
+
+ nt = Test(true);
+
+ // destroy value
+ Test.destroyed = false;
+
+ nt.nullify;
+
+ assert(Test.destroyed);
+
+ Test.destroyed = false;
+ }
+ // don't run destructor on T.init in Nullable on scope exit!
+ assert(!Test.destroyed);
+}
+// check that the contained type's destructor is called on assignment
+@system unittest
+{
+ struct S
+ {
+ // can't be static, since we need a specific value's pointer
+ bool* destroyedRef;
+
+ ~this()
+ {
+ if (this.destroyedRef)
+ {
+ *this.destroyedRef = true;
+ }
+ }
+ }
+
+ Nullable!S ns;
+
+ bool destroyed;
+
+ ns = S(&destroyed);
+
+ // reset from rvalue destruction in Nullable's opAssign
+ destroyed = false;
+
+ // overwrite Nullable
+ ns = S(null);
+
+ // the original S should be destroyed.
+ assert(destroyed == true);
+}
+// check that the contained type's destructor is still called when required
+@system unittest
+{
+ bool destructorCalled = false;
+
+ struct S
+ {
+ bool* destroyed;
+ ~this() { *this.destroyed = true; }
+ }
+
+ {
+ Nullable!S ns;
+ }
+ assert(!destructorCalled);
+ {
+ Nullable!S ns = Nullable!S(S(&destructorCalled));
+
+ destructorCalled = false; // reset after S was destroyed in the NS constructor
+ }
+ assert(destructorCalled);
+}
+
+// check that toHash on Nullable is forwarded to the contained type
+@system unittest
+{
+ struct S
+ {
+ size_t toHash() const @safe pure nothrow { return 5; }
+ }
+
+ Nullable!S s1 = S();
+ Nullable!S s2 = Nullable!S();
+
+ assert(typeid(Nullable!S).getHash(&s1) == 5);
+ assert(typeid(Nullable!S).getHash(&s2) == 0);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21704
+@safe unittest
+{
+ import std.array : staticArray;
+
+ bool destroyed;
+
+ struct Probe
+ {
+ ~this() { destroyed = true; }
+ }
+
+ {
+ Nullable!(Probe[1]) test = [Probe()].staticArray;
+ destroyed = false;
+ }
+ assert(destroyed);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21705
+@safe unittest
+{
+ static struct S
+ {
+ int n;
+ bool opEquals(S rhs) { return n == rhs.n; }
+ }
+
+ Nullable!S test1 = S(1), test2 = S(1);
+ S s = S(1);
+
+ assert(test1 == s);
+ assert(test1 == test2);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=22101
+@safe unittest
+{
+ static int impure;
+
+ struct S
+ {
+ ~this() { impure++; }
+ }
+
+ Nullable!S s;
+ s.get(S());
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=22100
+@safe unittest
+{
+ Nullable!int a, b, c;
+ a = b = c = 5;
+ a = b = c = nullable(5);
+}
+
/**
-Just like $(D Nullable!T), except that the null state is defined as a
+Just like `Nullable!T`, except that the null state is defined as a
particular value. For example, $(D Nullable!(uint, uint.max)) is an
-$(D uint) that sets aside the value $(D uint.max) to denote a null
+`uint` that sets aside the value `uint.max` to denote a null
state. $(D Nullable!(T, nullValue)) is more storage-efficient than $(D
-Nullable!T) because it does not need to store an extra $(D bool).
+Nullable!T) because it does not need to store an extra `bool`.
Params:
T = The wrapped type for which Nullable provides a null value.
@@ -2899,7 +3630,7 @@ struct Nullable(T, T nullValue)
private T _value = nullValue;
/**
-Constructor initializing $(D this) with $(D value).
+Constructor initializing `this` with `value`.
Params:
value = The value to initialize this `Nullable` with.
@@ -2911,9 +3642,10 @@ Params:
template toString()
{
- import std.format : FormatSpec, formatValue;
- // Needs to be a template because of DMD @@BUG@@ 13737.
- void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
+ import std.format.spec : FormatSpec;
+ import std.format.write : formatValue;
+ // Needs to be a template because of https://issues.dlang.org/show_bug.cgi?id=13737.
+ void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt)
{
if (isNull)
{
@@ -2942,7 +3674,7 @@ Returns:
}
//Need to use 'is' if T is a float type
//because NaN != NaN
- else static if (isFloatingPoint!T)
+ else static if (__traits(isFloating, T) || __traits(compiles, { static assert(!(nullValue == nullValue)); }))
{
return _value is nullValue;
}
@@ -2953,7 +3685,7 @@ Returns:
}
///
-@system unittest
+@safe unittest
{
Nullable!(int, -1) ni;
//Initialized to "null" state
@@ -2963,12 +3695,20 @@ Returns:
assert(!ni.isNull);
}
+@system unittest
+{
+ assert(typeof(this).init.isNull, typeof(this).stringof ~
+ ".isNull does not work correctly because " ~ T.stringof ~
+ " has an == operator that is non-reflexive and could not be" ~
+ " determined before runtime to be non-reflexive!");
+}
+
// https://issues.dlang.org/show_bug.cgi?id=11135
// disable test until https://issues.dlang.org/show_bug.cgi?id=15316 gets fixed
version (none) @system unittest
{
- foreach (T; AliasSeq!(float, double, real))
- {
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
Nullable!(T, T.init) nf;
//Initialized to "null" state
assert(nf.isNull);
@@ -2979,11 +3719,11 @@ version (none) @system unittest
nf.nullify();
assert(nf.isNull);
- }
+ }}
}
/**
-Forces $(D this) to the null state.
+Forces `this` to the null state.
*/
void nullify()()
{
@@ -2991,7 +3731,7 @@ Forces $(D this) to the null state.
}
///
-@system unittest
+@safe unittest
{
Nullable!(int, -1) ni = 0;
assert(!ni.isNull);
@@ -3001,9 +3741,9 @@ Forces $(D this) to the null state.
}
/**
-Assigns $(D value) to the internally-held state. If the assignment
-succeeds, $(D this) becomes non-null. No null checks are made. Note
-that the assignment may leave $(D this) in the null state.
+Assigns `value` to the internally-held state. If the assignment
+succeeds, `this` becomes non-null. No null checks are made. Note
+that the assignment may leave `this` in the null state.
Params:
value = A value of type `T` to assign to this `Nullable`.
@@ -3012,7 +3752,9 @@ Params:
*/
void opAssign()(T value)
{
- _value = value;
+ import std.algorithm.mutation : swap;
+
+ swap(value, _value);
}
/**
@@ -3037,9 +3779,10 @@ Params:
}
/**
-Gets the value. $(D this) must not be in the null state.
-This function is also called for the implicit conversion to $(D T).
+Gets the value. `this` must not be in the null state.
+This function is also called for the implicit conversion to `T`.
+Preconditions: `isNull` must be `false`.
Returns:
The value held internally by this `Nullable`.
*/
@@ -3067,15 +3810,15 @@ Returns:
}
/**
-Implicitly converts to $(D T).
-$(D this) must not be in the null state.
+Implicitly converts to `T`.
+`this` must not be in the null state.
*/
alias get this;
}
/// ditto
auto nullable(alias nullValue, T)(T t)
- if (is (typeof(nullValue) == T))
+if (is (typeof(nullValue) == T))
{
return Nullable!(T, nullValue)(t);
}
@@ -3134,9 +3877,29 @@ auto nullable(alias nullValue, T)(T t)
assert(a.isNull);
}
+@nogc nothrow pure @safe unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=19226
+ // fully handle non-self-equal nullValue
+ static struct Fraction
+ {
+ int denominator;
+ bool isNaN() const
+ {
+ return denominator == 0;
+ }
+ bool opEquals(const Fraction rhs) const
+ {
+ return !isNaN && denominator == rhs.denominator;
+ }
+ }
+ alias N = Nullable!(Fraction, Fraction.init);
+ assert(N.init.isNull);
+}
+
@safe unittest
{
- static int f(in Nullable!(int, int.min) x) {
+ static int f(scope const Nullable!(int, int.min) x) {
return x.isNull ? 42 : x.get;
}
Nullable!(int, int.min) a;
@@ -3196,19 +3959,19 @@ auto nullable(alias nullValue, T)(T t)
ni = other.ni;
}
}
- foreach (S; AliasSeq!(S1, S2))
- {
+ static foreach (S; AliasSeq!(S1, S2))
+ {{
S a;
S b = a;
S c;
c = a;
- }
+ }}
}
@system unittest
{
import std.conv : to;
- // Bugzilla 10915
+ // https://issues.dlang.org/show_bug.cgi?id=10915
Nullable!(int, 1) ni = 1;
assert(ni.to!string() == "Nullable.null");
@@ -3241,19 +4004,160 @@ auto nullable(alias nullValue, T)(T t)
assert(ntts.to!string() == "2.5");
}
+// apply
+/**
+Unpacks the content of a `Nullable`, performs an operation and packs it again. Does nothing if isNull.
+
+When called on a `Nullable`, `apply` will unpack the value contained in the `Nullable`,
+pass it to the function you provide and wrap the result in another `Nullable` (if necessary).
+If the `Nullable` is null, `apply` will return null itself.
+
+Params:
+ t = a `Nullable`
+ fun = a function operating on the content of the nullable
+
+Returns:
+ `fun(t.get).nullable` if `!t.isNull`, else `Nullable.init`.
+
+See also:
+ $(HTTPS en.wikipedia.org/wiki/Monad_(functional_programming)#The_Maybe_monad, The `Maybe` monad)
+ */
+template apply(alias fun)
+{
+ import std.functional : unaryFun;
+
+ auto apply(T)(auto ref T t)
+ if (isInstanceOf!(Nullable, T) && is(typeof(unaryFun!fun(T.init.get))))
+ {
+ alias FunType = typeof(unaryFun!fun(T.init.get));
+
+ enum MustWrapReturn = !isInstanceOf!(Nullable, FunType);
+
+ static if (MustWrapReturn)
+ {
+ alias ReturnType = Nullable!FunType;
+ }
+ else
+ {
+ alias ReturnType = FunType;
+ }
+
+ if (!t.isNull)
+ {
+ static if (MustWrapReturn)
+ {
+ return unaryFun!fun(t.get).nullable;
+ }
+ else
+ {
+ return unaryFun!fun(t.get);
+ }
+ }
+ else
+ {
+ return ReturnType.init;
+ }
+ }
+}
+
+///
+nothrow pure @nogc @safe unittest
+{
+ alias toFloat = i => cast(float) i;
+
+ Nullable!int sample;
+
+ // apply(null) results in a null `Nullable` of the function's return type.
+ Nullable!float f = sample.apply!toFloat;
+ assert(sample.isNull && f.isNull);
+
+ sample = 3;
+
+ // apply(non-null) calls the function and wraps the result in a `Nullable`.
+ f = sample.apply!toFloat;
+ assert(!sample.isNull && !f.isNull);
+ assert(f.get == 3.0f);
+}
+
+///
+nothrow pure @nogc @safe unittest
+{
+ alias greaterThree = i => (i > 3) ? i.nullable : Nullable!(typeof(i)).init;
+
+ Nullable!int sample;
+
+ // when the function already returns a `Nullable`, that `Nullable` is not wrapped.
+ auto result = sample.apply!greaterThree;
+ assert(sample.isNull && result.isNull);
+
+ // The function may decide to return a null `Nullable`.
+ sample = 3;
+ result = sample.apply!greaterThree;
+ assert(!sample.isNull && result.isNull);
+
+ // Or it may return a value already wrapped in a `Nullable`.
+ sample = 4;
+ result = sample.apply!greaterThree;
+ assert(!sample.isNull && !result.isNull);
+ assert(result.get == 4);
+}
+
+// test that Nullable.get(default) can merge types
+@safe @nogc nothrow pure
+unittest
+{
+ Nullable!ubyte sample = Nullable!ubyte();
+
+ // Test that get(U) returns the common type of the Nullable type and the parameter type.
+ assert(sample.get(1000) == 1000);
+}
+
+// Workaround for https://issues.dlang.org/show_bug.cgi?id=20670
+@safe @nogc nothrow pure
+unittest
+{
+ immutable struct S { }
+
+ S[] array = Nullable!(S[])().get(S[].init);
+}
+
+// regression test for https://issues.dlang.org/show_bug.cgi?id=21199
+@safe @nogc nothrow pure
+unittest
+{
+ struct S { int i; }
+ assert(S(5).nullable.apply!"a.i" == 5);
+}
+
+// regression test for https://issues.dlang.org/show_bug.cgi?id=22176
+@safe @nogc nothrow pure
+unittest
+{
+ struct S
+ {
+ int i;
+ invariant(i != 0);
+
+ // Nullable shouldn't cause S to generate an
+ // opAssign that would check the invariant.
+ Nullable!int j;
+ }
+ S s;
+ s = S(5);
+}
/**
-Just like $(D Nullable!T), except that the object refers to a value
+Just like `Nullable!T`, except that the object refers to a value
sitting elsewhere in memory. This makes assignments overwrite the
-initially assigned value. Internally $(D NullableRef!T) only stores a
-pointer to $(D T) (i.e., $(D Nullable!T.sizeof == (T*).sizeof)).
+initially assigned value. Internally `NullableRef!T` only stores a
+pointer to `T` (i.e., $(D Nullable!T.sizeof == (T*).sizeof)).
*/
struct NullableRef(T)
{
private T* _value;
/**
-Constructor binding $(D this) to $(D value).
+Constructor binding `this` to `value`.
Params:
value = The value to bind to.
@@ -3265,9 +4169,10 @@ Params:
template toString()
{
- import std.format : FormatSpec, formatValue;
- // Needs to be a template because of DMD @@BUG@@ 13737.
- void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
+ import std.format.spec : FormatSpec;
+ import std.format.write : formatValue;
+ // Needs to be a template because of https://issues.dlang.org/show_bug.cgi?id=13737.
+ void toString()(scope void delegate(const(char)[]) sink, scope const ref FormatSpec!char fmt)
{
if (isNull)
{
@@ -3281,7 +4186,7 @@ Params:
}
/**
-Binds the internal state to $(D value).
+Binds the internal state to `value`.
Params:
value = A pointer to a value of type `T` to bind this `NullableRef` to.
@@ -3303,7 +4208,7 @@ Params:
}
/**
-Returns $(D true) if and only if $(D this) is in the null state.
+Returns `true` if and only if `this` is in the null state.
Returns:
true if `this` is in the null state, otherwise false.
@@ -3325,7 +4230,7 @@ Returns:
}
/**
-Forces $(D this) to the null state.
+Forces `this` to the null state.
*/
void nullify() @safe pure nothrow
{
@@ -3343,7 +4248,7 @@ Forces $(D this) to the null state.
}
/**
-Assigns $(D value) to the internally-held state.
+Assigns `value` to the internally-held state.
Params:
value = A value of type `T` to assign to this `NullableRef`.
@@ -3375,8 +4280,8 @@ Params:
}
/**
-Gets the value. $(D this) must not be in the null state.
-This function is also called for the implicit conversion to $(D T).
+Gets the value. `this` must not be in the null state.
+This function is also called for the implicit conversion to `T`.
*/
@property ref inout(T) get() inout @safe pure nothrow
{
@@ -3400,8 +4305,8 @@ This function is also called for the implicit conversion to $(D T).
}
/**
-Implicitly converts to $(D T).
-$(D this) must not be in the null state.
+Implicitly converts to `T`.
+`this` must not be in the null state.
*/
alias get this;
}
@@ -3438,7 +4343,7 @@ auto nullableRef(T)(T* t)
}
@system unittest
{
- static int f(in NullableRef!int x) {
+ static int f(scope const NullableRef!int x) {
return x.isNull ? 42 : x.get;
}
int x = 5;
@@ -3505,19 +4410,20 @@ auto nullableRef(T)(T* t)
ni = other.ni;
}
}
- foreach (S; AliasSeq!(S1, S2))
- {
+ static foreach (S; AliasSeq!(S1, S2))
+ {{
S a;
S b = a;
S c;
c = a;
- }
+ }}
}
+
+// https://issues.dlang.org/show_bug.cgi?id=10915
@system unittest
{
import std.conv : to;
- // Bugzilla 10915
NullableRef!int nri;
assert(nri.to!string() == "Nullable.null");
@@ -3549,8 +4455,8 @@ auto nullableRef(T)(T* t)
/**
-$(D BlackHole!Base) is a subclass of $(D Base) which automatically implements
-all abstract member functions in $(D Base) as do-nothing functions. Each
+`BlackHole!Base` is a subclass of `Base` which automatically implements
+all abstract member functions in `Base` as do-nothing functions. Each
auto-implemented function just returns the default value of the return type
without doing anything.
@@ -3569,7 +4475,7 @@ alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFu
///
@system unittest
{
- import std.math : isNaN;
+ import std.math.traits : isNaN;
static abstract class C
{
@@ -3592,7 +4498,7 @@ alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFu
@system unittest
{
- import std.math : isNaN;
+ import std.math.traits : isNaN;
// return default
{
@@ -3619,7 +4525,7 @@ alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFu
c.doSomething();
}
- // Bugzilla 12058
+ // https://issues.dlang.org/show_bug.cgi?id=12058
interface Foo
{
inout(Object) foo() inout;
@@ -3627,11 +4533,22 @@ alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFu
BlackHole!Foo o;
}
+nothrow pure @nogc @safe unittest
+{
+ static interface I
+ {
+ I foo() nothrow pure @nogc @safe return scope;
+ }
+
+ scope cb = new BlackHole!I();
+ cb.foo();
+}
+
/**
-$(D WhiteHole!Base) is a subclass of $(D Base) which automatically implements
+`WhiteHole!Base` is a subclass of `Base` which automatically implements
all abstract member functions as functions that always fail. These functions
-simply throw an $(D Error) and never return. `Whitehole` is useful for
+simply throw an `Error` and never return. `Whitehole` is useful for
trapping the use of class member functions that haven't been implemented.
The name came from
@@ -3660,10 +4577,25 @@ alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap, isAbstractFunct
assertThrown!NotImplementedError(c.notYetImplemented()); // throws an Error
}
+// https://issues.dlang.org/show_bug.cgi?id=20232
+nothrow pure @safe unittest
+{
+ static interface I
+ {
+ I foo() nothrow pure @safe return scope;
+ }
+
+ if (0) // Just checking attribute interference
+ {
+ scope cw = new WhiteHole!I();
+ cw.foo();
+ }
+}
+
// / ditto
class NotImplementedError : Error
{
- this(string method)
+ this(string method) nothrow pure @safe
{
super(method ~ " is not implemented");
}
@@ -3702,25 +4634,25 @@ class NotImplementedError : Error
/**
-$(D AutoImplement) automatically implements (by default) all abstract member
-functions in the class or interface $(D Base) in specified way.
+`AutoImplement` automatically implements (by default) all abstract member
+functions in the class or interface `Base` in specified way.
-The second version of $(D AutoImplement) automatically implements
-$(D Interface), while deriving from $(D BaseClass).
+The second version of `AutoImplement` automatically implements
+`Interface`, while deriving from `BaseClass`.
Params:
how = template which specifies _how functions will be implemented/overridden.
- Two arguments are passed to $(D how): the type $(D Base) and an alias
- to an implemented function. Then $(D how) must return an implemented
+ Two arguments are passed to `how`: the type `Base` and an alias
+ to an implemented function. Then `how` must return an implemented
function body as a string.
The generated function body can use these keywords:
$(UL
- $(LI $(D a0), $(D a1), &hellip;: arguments passed to the function;)
- $(LI $(D args): a tuple of the arguments;)
- $(LI $(D self): an alias to the function itself;)
- $(LI $(D parent): an alias to the overridden function (if any).)
+ $(LI `a0`, `a1`, &hellip;: arguments passed to the function;)
+ $(LI `args`: a tuple of the arguments;)
+ $(LI `self`: an alias to the function itself;)
+ $(LI `parent`: an alias to the overridden function (if any).)
)
You may want to use templated property functions (instead of Implicit
@@ -3753,9 +4685,9 @@ string generateLogger(C, alias fun)() @property
what = template which determines _what functions should be
implemented/overridden.
- An argument is passed to $(D what): an alias to a non-final member
- function in $(D Base). Then $(D what) must return a boolean value.
- Return $(D true) to indicate that the passed function should be
+ An argument is passed to `what`: an alias to a non-final member
+ function in `Base`. Then `what` must return a boolean value.
+ Return `true` to indicate that the passed function should be
implemented/overridden.
--------------------
@@ -3766,10 +4698,10 @@ enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void);
Note:
-Generated code is inserted in the scope of $(D std.typecons) module. Thus,
-any useful functions outside $(D std.typecons) cannot be used in the generated
-code. To workaround this problem, you may $(D import) necessary things in a
-local struct, as done in the $(D generateLogger()) template in the above
+Generated code is inserted in the scope of `std.typecons` module. Thus,
+any useful functions outside `std.typecons` cannot be used in the generated
+code. To workaround this problem, you may `import` necessary things in a
+local struct, as done in the `generateLogger()` template in the above
example.
@@ -3779,16 +4711,16 @@ $(UL
$(LI Variadic arguments to constructors are not forwarded to super.)
$(LI Deep interface inheritance causes compile error with messages like
"Error: function std.typecons._AutoImplement!(Foo)._AutoImplement.bar
- does not override any function". [$(BUGZILLA 2525), $(BUGZILLA 3525)] )
- $(LI The $(D parent) keyword is actually a delegate to the super class'
+ does not override any function". [$(BUGZILLA 2525)] )
+ $(LI The `parent` keyword is actually a delegate to the super class'
corresponding member function. [$(BUGZILLA 2540)] )
- $(LI Using alias template parameter in $(D how) and/or $(D what) may cause
+ $(LI Using alias template parameter in `how` and/or `what` may cause
strange compile error. Use template tuple parameter instead to workaround
this problem. [$(BUGZILLA 4217)] )
)
*/
class AutoImplement(Base, alias how, alias what = isAbstractFunction) : Base
- if (!is(how == class))
+if (!is(how == class))
{
private alias autoImplement_helper_ =
AutoImplement_Helper!("autoImplement_helper_", "Base", Base, typeof(this), how, what);
@@ -3799,13 +4731,69 @@ class AutoImplement(Base, alias how, alias what = isAbstractFunction) : Base
class AutoImplement(
Interface, BaseClass, alias how,
alias what = isAbstractFunction) : BaseClass, Interface
- if (is(Interface == interface) && is(BaseClass == class))
+if (is(Interface == interface) && is(BaseClass == class))
{
private alias autoImplement_helper_ = AutoImplement_Helper!(
"autoImplement_helper_", "Interface", Interface, typeof(this), how, what);
mixin(autoImplement_helper_.code);
}
+///
+@system unittest
+{
+ interface PackageSupplier
+ {
+ int foo();
+ int bar();
+ }
+
+ static abstract class AbstractFallbackPackageSupplier : PackageSupplier
+ {
+ protected PackageSupplier default_, fallback;
+
+ this(PackageSupplier default_, PackageSupplier fallback)
+ {
+ this.default_ = default_;
+ this.fallback = fallback;
+ }
+
+ abstract int foo();
+ abstract int bar();
+ }
+
+ template fallback(T, alias func)
+ {
+ import std.format : format;
+ // for all implemented methods:
+ // - try default first
+ // - only on a failure run & return fallback
+ enum fallback = q{
+ scope (failure) return fallback.%1$s(args);
+ return default_.%1$s(args);
+ }.format(__traits(identifier, func));
+ }
+
+ // combines two classes and use the second one as fallback
+ alias FallbackPackageSupplier = AutoImplement!(AbstractFallbackPackageSupplier, fallback);
+
+ class FailingPackageSupplier : PackageSupplier
+ {
+ int foo(){ throw new Exception("failure"); }
+ int bar(){ return 2;}
+ }
+
+ class BackupPackageSupplier : PackageSupplier
+ {
+ int foo(){ return -1; }
+ int bar(){ return -1;}
+ }
+
+ auto registry = new FallbackPackageSupplier(new FailingPackageSupplier(), new BackupPackageSupplier());
+
+ assert(registry.foo() == -1);
+ assert(registry.bar() == 2);
+}
+
/*
* Code-generating stuffs are encupsulated in this helper template so that
* namespace pollution, which can cause name confliction with Base's public
@@ -3847,7 +4835,7 @@ private static:
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Add a non-final check to the cherrypickMethod.
- enum bool canonicalPicker(fun.../+[BUG 4217]+/) =
+ enum bool canonicalPicker(fun.../+[https://issues.dlang.org/show_bug.cgi?id=4217]+/) =
!__traits(isFinalFunction, fun[0]) && cherrypickMethod!(fun);
/*
@@ -3949,7 +4937,7 @@ private static:
mixin CommonGeneratingPolicy;
/* Generates constructor body. Just forward to the base class' one. */
- string generateFunctionBody(ctor.../+[BUG 4217]+/)() @property
+ string generateFunctionBody(ctor.../+[https://issues.dlang.org/show_bug.cgi?id=4217]+/)() @property
{
enum varstyle = variadicFunctionStyle!(typeof(&ctor[0]));
@@ -3970,7 +4958,7 @@ private static:
mixin CommonGeneratingPolicy;
/* Geneartes method body. */
- string generateFunctionBody(func.../+[BUG 4217]+/)() @property
+ string generateFunctionBody(func.../+[https://issues.dlang.org/show_bug.cgi?id=4217]+/)() @property
{
return generateMethodBody!(Base, func); // given
}
@@ -4101,7 +5089,8 @@ private static:
}
/+ // deep inheritance
{
- // XXX [BUG 2525,3525]
+ // https://issues.dlang.org/show_bug.cgi?id=2525
+ // https://issues.dlang.org/show_bug.cgi?id=3525
// NOTE: [r494] func.c(504-571) FuncDeclaration::semantic()
interface I { void foo(); }
interface J : I {}
@@ -4111,7 +5100,9 @@ private static:
}+/
}
-// Issue 17177 - AutoImplement fails on function overload sets with "cannot infer type from overloaded function symbol"
+// https://issues.dlang.org/show_bug.cgi?id=17177
+// AutoImplement fails on function overload sets with
+// "cannot infer type from overloaded function symbol"
@system unittest
{
static class Issue17177
@@ -4149,13 +5140,15 @@ private static:
}
}
+ import std.meta : templateNot;
alias Implementation = AutoImplement!(Issue17177, how, templateNot!isFinalFunction);
}
-version (unittest)
+version (StdUnittest)
{
- // Issue 10647
- // Add prefix "issue10647_" as a workaround for issue 1238
+ // https://issues.dlang.org/show_bug.cgi?id=10647
+ // Add prefix "issue10647_" as a workaround for
+ // https://issues.dlang.org/show_bug.cgi?id=1238
private string issue10647_generateDoNothing(C, alias fun)() @property
{
string stmt;
@@ -4307,7 +5300,8 @@ private static:
// declaration here to reveal possible hidden functions.
code ~= format("alias %s = %s.%s;\n",
oset.name,
- Policy.BASE_CLASS_ID, // [BUG 2540] super.
+ // super: https://issues.dlang.org/show_bug.cgi?id=2540
+ Policy.BASE_CLASS_ID,
oset.name);
}
}
@@ -4380,6 +5374,8 @@ private static:
if (atts & FA.property) poatts ~= " @property";
if (atts & FA.safe ) poatts ~= " @safe";
if (atts & FA.trusted ) poatts ~= " @trusted";
+ if (atts & FA.scope_ ) poatts ~= " scope";
+ if (atts & FA.return_ ) poatts ~= " return";
return poatts;
}
enum postAtts = make_postAtts();
@@ -4420,7 +5416,7 @@ private static:
{
preamble ~= "alias self = " ~ name ~ ";\n";
if (WITH_BASE_CLASS && !__traits(isAbstractFunction, func))
- preamble ~= "alias parent = AliasSeq!(__traits(getMember, super, \"" ~ name ~ "\"))[0];";
+ preamble ~= `alias parent = __traits(getMember, super, "` ~ name ~ `");`;
}
// Function body
@@ -4458,6 +5454,7 @@ private static:
// Parameter storage classes.
if (stc & STC.scope_) params ~= "scope ";
+ if (stc & STC.in_) params ~= "in ";
if (stc & STC.out_ ) params ~= "out ";
if (stc & STC.ref_ ) params ~= "ref ";
if (stc & STC.lazy_ ) params ~= "lazy ";
@@ -4508,10 +5505,10 @@ private static:
/**
-Predefined how-policies for $(D AutoImplement). These templates are also used by
-$(D BlackHole) and $(D WhiteHole), respectively.
+Predefined how-policies for `AutoImplement`. These templates are also used by
+`BlackHole` and `WhiteHole`, respectively.
*/
-template generateEmptyFunction(C, func.../+[BUG 4217]+/)
+template generateEmptyFunction(C, func.../+[https://issues.dlang.org/show_bug.cgi?id=4217]+/)
{
static if (is(ReturnType!(func) == void))
enum string generateEmptyFunction = q{
@@ -4527,6 +5524,23 @@ template generateEmptyFunction(C, func.../+[BUG 4217]+/)
};
}
+///
+@system unittest
+{
+ alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction);
+
+ interface I
+ {
+ int foo();
+ string bar();
+ }
+
+ auto i = new BlackHole!I();
+ // generateEmptyFunction returns the default value of the return type without doing anything
+ assert(i.foo == 0);
+ assert(i.bar is null);
+}
+
/// ditto
template generateAssertTrap(C, func...)
{
@@ -4535,6 +5549,25 @@ template generateAssertTrap(C, func...)
~ __traits(identifier, func) ~ `");`;
}
+///
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap);
+
+ interface I
+ {
+ int foo();
+ string bar();
+ }
+
+ auto i = new WhiteHole!I();
+ // generateAssertTrap throws an exception for every unimplemented function of the interface
+ assertThrown!NotImplementedError(i.foo);
+ assertThrown!NotImplementedError(i.bar);
+}
+
private
{
pragma(mangle, "_d_toObject")
@@ -4568,14 +5601,14 @@ if (is(T == class) || is(T == interface))
@system unittest
{
- class C { @disable opCast(T)() {} }
+ class C { @disable void opCast(T)(); }
auto c = new C;
static assert(!__traits(compiles, cast(Object) c));
auto o = dynamicCast!Object(c);
assert(c is o);
- interface I { @disable opCast(T)() {} Object instance(); }
- interface J { @disable opCast(T)() {} Object instance(); }
+ interface I { @disable void opCast(T)(); Object instance(); }
+ interface J { @disable void opCast(T)(); Object instance(); }
class D : I, J { Object instance() { return this; } }
I i = new D();
static assert(!__traits(compiles, cast(J) i));
@@ -4584,12 +5617,14 @@ if (is(T == class) || is(T == interface))
}
/**
- * Supports structural based typesafe conversion.
- *
- * If $(D Source) has structural conformance with the $(D interface) $(D Targets),
- * wrap creates internal wrapper class which inherits $(D Targets) and
- * wrap $(D src) object, then return it.
- */
+Supports structural based typesafe conversion.
+
+If `Source` has structural conformance with the `interface` `Targets`,
+wrap creates an internal wrapper class which inherits `Targets` and
+wraps the `src` object, then returns it.
+
+`unwrap` can be used to extract objects which have been wrapped by `wrap`.
+*/
template wrap(Targets...)
if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
{
@@ -4623,6 +5658,14 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
alias type = F;
}
+ // https://issues.dlang.org/show_bug.cgi?id=12064: Remove NVI members
+ template OnlyVirtual(members...)
+ {
+ enum notFinal(alias T) = !__traits(isFinalFunction, T);
+ import std.meta : Filter;
+ alias OnlyVirtual = Filter!(notFinal, members);
+ }
+
// Concat all Targets function members into one tuple
template Concat(size_t i = 0)
{
@@ -4630,9 +5673,10 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
alias Concat = AliasSeq!();
else
{
- alias Concat = AliasSeq!(GetOverloadedMethods!(Targets[i]), Concat!(i + 1));
+ alias Concat = AliasSeq!(OnlyVirtual!(GetOverloadedMethods!(Targets[i]), Concat!(i + 1)));
}
}
+
// Remove duplicated functions based on the identifier name and function type covariance
template Uniq(members...)
{
@@ -4708,7 +5752,7 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
}
import std.conv : to;
- import std.functional : forward;
+ import core.lifetime : forward;
template generateFun(size_t i)
{
enum name = TargetMembers[i].name;
@@ -4757,8 +5801,8 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
}
public:
- mixin mixinAll!(
- staticMap!(generateFun, staticIota!(0, TargetMembers.length)));
+ static foreach (i; 0 .. TargetMembers.length)
+ mixin(generateFun!i);
}
}
}
@@ -4771,15 +5815,7 @@ if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))
alias wrap = .wrap!(staticMap!(Unqual, Targets));
}
-// Internal class to support dynamic cross-casting
-private interface Structural
-{
- inout(Object) _wrap_getSource() inout @safe pure nothrow;
-}
-
-/**
- * Extract object which wrapped by $(D wrap).
- */
+/// ditto
template unwrap(Target)
if (isMutable!Target)
{
@@ -4811,6 +5847,7 @@ if (isMutable!Target)
return null;
}
}
+
/// ditto
template unwrap(Target)
if (!isMutable!Target)
@@ -4848,6 +5885,7 @@ if (!isMutable!Target)
{
int reflesh();
}
+
// does not have structural conformance
static assert(!__traits(compiles, d1.wrap!Refleshable));
static assert(!__traits(compiles, h1.wrap!Refleshable));
@@ -4871,14 +5909,15 @@ if (!isMutable!Target)
Quack qx = h1.wrap!Quack; // Human -> Quack
Flyer fx = qx.wrap!Flyer; // Quack -> Flyer
assert(fx.height == 20); // calls Human.height
- // strucural downcast (two steps)
+ // structural downcast (two steps)
Quack qy = fx.unwrap!Quack; // Flyer -> Quack
Human hy = qy.unwrap!Human; // Quack -> Human
assert(hy is h1);
- // strucural downcast (one step)
+ // structural downcast (one step)
Human hz = fx.unwrap!Human; // Flyer -> Human
assert(hz is h1);
}
+
///
@system unittest
{
@@ -4901,6 +5940,13 @@ if (!isMutable!Target)
assert(b.status == 3);
static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
}
+
+// Internal class to support dynamic cross-casting
+private interface Structural
+{
+ inout(Object) _wrap_getSource() inout @safe pure nothrow;
+}
+
@system unittest
{
class A
@@ -4947,9 +5993,10 @@ if (!isMutable!Target)
assert(d.draw(10) == 10);
}
}
+
+// https://issues.dlang.org/show_bug.cgi?id=10377
@system unittest
{
- // Bugzilla 10377
import std.range, std.algorithm;
interface MyInputRange(T)
@@ -4964,9 +6011,10 @@ if (!isMutable!Target)
auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)();
assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
}
+
+// https://issues.dlang.org/show_bug.cgi?id=10536
@system unittest
{
- // Bugzilla 10536
interface Interface
{
int foo();
@@ -4998,12 +6046,39 @@ if (!isMutable!Target)
assert(i.bar(10) == 100);
}
+// https://issues.dlang.org/show_bug.cgi?id=12064
+@system unittest
+{
+ interface I
+ {
+ int foo();
+ final int nvi1(){return foo();}
+ }
+
+ interface J
+ {
+ int bar();
+ final int nvi2(){return bar();}
+ }
+
+ class Baz
+ {
+ int foo() { return 42;}
+ int bar() { return 12064;}
+ }
+
+ auto baz = new Baz();
+ auto foobar = baz.wrap!(I, J)();
+ assert(foobar.nvi1 == 42);
+ assert(foobar.nvi2 == 12064);
+}
+
// Make a tuple of non-static function symbols
package template GetOverloadedMethods(T)
{
import std.meta : Filter;
- alias allMembers = AliasSeq!(__traits(allMembers, T));
+ alias allMembers = __traits(allMembers, T);
template follows(size_t i = 0)
{
static if (i >= allMembers.length)
@@ -5091,16 +6166,14 @@ private template TypeMod(T)
enum TypeMod = cast(TypeModifier)(mod1 | mod2);
}
-version (unittest)
+@system unittest
{
- private template UnittestFuncInfo(alias f)
+ template UnittestFuncInfo(alias f)
{
enum name = __traits(identifier, f);
alias type = FunctionTypeOf!f;
}
-}
-@system unittest
-{
+
class A
{
int draw() { return 1; }
@@ -5246,47 +6319,6 @@ package template DerivedFunctionType(T...)
static assert(is(DerivedFunctionType!(F17, F18) == void));
}
-package template staticIota(int beg, int end)
-{
- static if (beg + 1 >= end)
- {
- static if (beg >= end)
- {
- alias staticIota = AliasSeq!();
- }
- else
- {
- alias staticIota = AliasSeq!(+beg);
- }
- }
- else
- {
- enum mid = beg + (end - beg) / 2;
- alias staticIota = AliasSeq!(staticIota!(beg, mid), staticIota!(mid, end));
- }
-}
-
-package template mixinAll(mixins...)
-{
- static if (mixins.length == 1)
- {
- static if (is(typeof(mixins[0]) == string))
- {
- mixin(mixins[0]);
- }
- else
- {
- alias it = mixins[0];
- mixin it;
- }
- }
- else static if (mixins.length >= 2)
- {
- mixin mixinAll!(mixins[ 0 .. $/2]);
- mixin mixinAll!(mixins[$/2 .. $ ]);
- }
-}
-
package template Bind(alias Template, args1...)
{
alias Bind(args2...) = Template!(args1, args2);
@@ -5294,8 +6326,8 @@ package template Bind(alias Template, args1...)
/**
-Options regarding auto-initialization of a $(D RefCounted) object (see
-the definition of $(D RefCounted) below).
+Options regarding auto-initialization of a `RefCounted` object (see
+the definition of `RefCounted` below).
*/
enum RefCountedAutoInitialize
{
@@ -5305,58 +6337,94 @@ enum RefCountedAutoInitialize
yes,
}
+///
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.exception : assertThrown;
+
+ struct Foo
+ {
+ int a = 42;
+ }
+
+ RefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto;
+ RefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto;
+
+ assert(rcAuto.refCountedPayload.a == 42);
+
+ assertThrown!AssertError(rcNoAuto.refCountedPayload);
+ rcNoAuto.refCountedStore.ensureInitialized;
+ assert(rcNoAuto.refCountedPayload.a == 42);
+}
+
/**
-Defines a reference-counted object containing a $(D T) value as
+Defines a reference-counted object containing a `T` value as
payload.
-An instance of $(D RefCounted) is a reference to a structure,
+An instance of `RefCounted` is a reference to a structure,
which is referred to as the $(I store), or $(I storage implementation
struct) in this documentation. The store contains a reference count
-and the $(D T) payload. $(D RefCounted) uses $(D malloc) to allocate
-the store. As instances of $(D RefCounted) are copied or go out of
+and the `T` payload. `RefCounted` uses `malloc` to allocate
+the store. As instances of `RefCounted` are copied or go out of
scope, they will automatically increment or decrement the reference
-count. When the reference count goes down to zero, $(D RefCounted)
-will call $(D destroy) against the payload and call $(D free) to
-deallocate the store. If the $(D T) payload contains any references
+count. When the reference count goes down to zero, `RefCounted`
+will call `destroy` against the payload and call `free` to
+deallocate the store. If the `T` payload contains any references
to GC-allocated memory, then `RefCounted` will add it to the GC memory
that is scanned for pointers, and remove it from GC scanning before
-$(D free) is called on the store.
+`free` is called on the store.
-One important consequence of $(D destroy) is that it will call the
-destructor of the $(D T) payload. GC-managed references are not
+One important consequence of `destroy` is that it will call the
+destructor of the `T` payload. GC-managed references are not
guaranteed to be valid during a destructor call, but other members of
-$(D T), such as file handles or pointers to $(D malloc) memory, will
-still be valid during the destructor call. This allows the $(D T) to
+`T`, such as file handles or pointers to `malloc` memory, will
+still be valid during the destructor call. This allows the `T` to
deallocate or clean up any non-GC resources immediately after the
reference count has reached zero.
-$(D RefCounted) is unsafe and should be used with care. No references
-to the payload should be escaped outside the $(D RefCounted) object.
+`RefCounted` is unsafe and should be used with care. No references
+to the payload should be escaped outside the `RefCounted` object.
-The $(D autoInit) option makes the object ensure the store is
+The `autoInit` option makes the object ensure the store is
automatically initialized. Leaving $(D autoInit ==
RefCountedAutoInitialize.yes) (the default option) is convenient but
has the cost of a test whenever the payload is accessed. If $(D
autoInit == RefCountedAutoInitialize.no), user code must call either
-$(D refCountedStore.isInitialized) or $(D refCountedStore.ensureInitialized)
+`refCountedStore.isInitialized` or `refCountedStore.ensureInitialized`
before attempting to access the payload. Not doing so results in null
pointer dereference.
+
+If `T.this()` is annotated with `@disable` then `autoInit` must be
+`RefCountedAutoInitialize.no` in order to compile.
*/
struct RefCounted(T, RefCountedAutoInitialize autoInit =
RefCountedAutoInitialize.yes)
if (!is(T == class) && !(is(T == interface)))
{
- extern(C) private pure nothrow @nogc static // TODO remove pure when https://issues.dlang.org/show_bug.cgi?id=15862 has been fixed
+ version (D_BetterC)
+ {
+ private enum enableGCScan = false;
+ }
+ else
+ {
+ private enum enableGCScan = hasIndirections!T;
+ }
+
+ // TODO remove pure when https://issues.dlang.org/show_bug.cgi?id=15862 has been fixed
+ extern(C) private pure nothrow @nogc static
{
pragma(mangle, "free") void pureFree( void *ptr );
- pragma(mangle, "gc_addRange") void pureGcAddRange( in void* p, size_t sz, const TypeInfo ti = null );
- pragma(mangle, "gc_removeRange") void pureGcRemoveRange( in void* p );
+ static if (enableGCScan)
+ {
+ pragma(mangle, "gc_addRange") void pureGcAddRange( in void* p, size_t sz, const TypeInfo ti = null );
+ pragma(mangle, "gc_removeRange") void pureGcRemoveRange( in void* p );
+ }
}
- /// $(D RefCounted) storage implementation.
+ /// `RefCounted` storage implementation.
struct RefCountedStore
{
- import core.memory : pureMalloc;
private struct Impl
{
T _payload;
@@ -5367,61 +6435,51 @@ if (!is(T == class) && !(is(T == interface)))
private void initialize(A...)(auto ref A args)
{
- import core.exception : onOutOfMemoryError;
- import std.conv : emplace;
+ import core.lifetime : emplace, forward;
- _store = cast(Impl*) pureMalloc(Impl.sizeof);
- if (_store is null)
- onOutOfMemoryError();
- static if (hasIndirections!T)
- pureGcAddRange(&_store._payload, T.sizeof);
- emplace(&_store._payload, args);
+ allocateStore();
+ version (D_Exceptions) scope(failure) deallocateStore();
+ emplace(&_store._payload, forward!args);
_store._count = 1;
}
- private void move(ref T source)
+ private void move(ref T source) nothrow pure
{
- import core.exception : onOutOfMemoryError;
- import core.stdc.string : memcpy, memset;
-
- _store = cast(Impl*) pureMalloc(Impl.sizeof);
- if (_store is null)
- onOutOfMemoryError();
- static if (hasIndirections!T)
- pureGcAddRange(&_store._payload, T.sizeof);
+ import std.algorithm.mutation : moveEmplace;
- // Can't use std.algorithm.move(source, _store._payload)
- // here because it requires the target to be initialized.
- // Might be worth to add this as `moveEmplace`
+ allocateStore();
+ moveEmplace(source, _store._payload);
+ _store._count = 1;
+ }
- // Can avoid destructing result.
- static if (hasElaborateAssign!T || !isAssignable!T)
- memcpy(&_store._payload, &source, T.sizeof);
+ // 'nothrow': can only generate an Error
+ private void allocateStore() nothrow pure
+ {
+ static if (enableGCScan)
+ {
+ import std.internal.memory : enforceCalloc;
+ _store = cast(Impl*) enforceCalloc(1, Impl.sizeof);
+ pureGcAddRange(&_store._payload, T.sizeof);
+ }
else
- _store._payload = source;
-
- // If the source defines a destructor or a postblit hook, we must obliterate the
- // object in order to avoid double freeing and undue aliasing
- static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T)
{
- // If T is nested struct, keep original context pointer
- static if (__traits(isNested, T))
- enum sz = T.sizeof - (void*).sizeof;
- else
- enum sz = T.sizeof;
-
- auto init = typeid(T).initializer();
- if (init.ptr is null) // null ptr means initialize to 0s
- memset(&source, 0, sz);
- else
- memcpy(&source, init.ptr, sz);
+ import std.internal.memory : enforceMalloc;
+ _store = cast(Impl*) enforceMalloc(Impl.sizeof);
}
+ }
- _store._count = 1;
+ private void deallocateStore() nothrow pure
+ {
+ static if (enableGCScan)
+ {
+ pureGcRemoveRange(&this._store._payload);
+ }
+ pureFree(_store);
+ _store = null;
}
/**
- Returns $(D true) if and only if the underlying store has been
+ Returns `true` if and only if the underlying store has been
allocated and initialized.
*/
@property nothrow @safe pure @nogc
@@ -5432,7 +6490,7 @@ if (!is(T == class) && !(is(T == interface)))
/**
Returns underlying reference count if it is allocated and initialized
- (a positive integer), and $(D 0) otherwise.
+ (a positive integer), and `0` otherwise.
*/
@property nothrow @safe pure @nogc
size_t refCount() const
@@ -5443,9 +6501,18 @@ if (!is(T == class) && !(is(T == interface)))
/**
Makes sure the payload was properly initialized. Such a
call is typically inserted before using the payload.
+
+ This function is unavailable if `T.this()` is annotated with
+ `@disable`.
*/
- void ensureInitialized()
- {
+ void ensureInitialized()()
+ {
+ // By checking for `@disable this()` and failing early we can
+ // produce a clearer error message.
+ static assert(__traits(compiles, { static T t; }),
+ "Cannot automatically initialize `" ~ fullyQualifiedName!T ~
+ "` because `" ~ fullyQualifiedName!T ~
+ ".this()` is annotated with `@disable`.");
if (!isInitialized) initialize();
}
@@ -5462,11 +6529,17 @@ if (!is(T == class) && !(is(T == interface)))
/**
Constructor that initializes the payload.
-Postcondition: $(D refCountedStore.isInitialized)
+Postcondition: `refCountedStore.isInitialized`
*/
this(A...)(auto ref A args) if (A.length > 0)
+ out
+ {
+ assert(refCountedStore.isInitialized);
+ }
+ do
{
- _refCounted.initialize(args);
+ import core.lifetime : forward;
+ _refCounted.initialize(forward!args);
}
/// Ditto
@@ -5488,7 +6561,7 @@ Constructor that tracks the reference count appropriately. If $(D
/**
Destructor that tracks the reference count appropriately. If $(D
!refCountedStore.isInitialized), does nothing. When the reference count goes
-down to zero, calls $(D destroy) agaist the payload and calls $(D free)
+down to zero, calls `destroy` agaist the payload and calls `free`
to deallocate the corresponding resource.
*/
~this()
@@ -5497,15 +6570,9 @@ to deallocate the corresponding resource.
assert(_refCounted._store._count > 0);
if (--_refCounted._store._count)
return;
- // Done, deallocate
+ // Done, destroy and deallocate
.destroy(_refCounted._store._payload);
- static if (hasIndirections!T)
- {
- pureGcRemoveRange(&_refCounted._store._payload);
- }
-
- pureFree(_refCounted._store);
- _refCounted._store = null;
+ _refCounted.deallocateStore();
}
/**
@@ -5542,16 +6609,16 @@ Assignment operators
RefCountedAutoInitialize.yes), calls $(D
refCountedStore.ensureInitialized). Otherwise, just issues $(D
assert(refCountedStore.isInitialized)). Used with $(D alias
- refCountedPayload this;), so callers can just use the $(D RefCounted)
- object as a $(D T).
+ refCountedPayload this;), so callers can just use the `RefCounted`
+ object as a `T`.
$(BLUE The first overload exists only if $(D autoInit == RefCountedAutoInitialize.yes).)
So if $(D autoInit == RefCountedAutoInitialize.no)
or called for a constant or immutable object, then
- $(D refCountedPayload) will also be qualified as safe and nothrow
+ `refCountedPayload` will also be qualified as safe and nothrow
(but will still assert if not initialized).
*/
- @property
+ @property @trusted
ref T refCountedPayload() return;
/// ditto
@@ -5586,10 +6653,28 @@ refCountedStore.ensureInitialized). Otherwise, just issues $(D
assert(refCountedStore.isInitialized)).
*/
alias refCountedPayload this;
+
+ static if (is(T == struct) && !is(typeof((ref T t) => t.toString())))
+ {
+ string toString(this This)()
+ {
+ import std.conv : to;
+
+ static if (autoInit)
+ return to!string(refCountedPayload);
+ else
+ {
+ if (!_refCounted.isInitialized)
+ return This.stringof ~ "(RefCountedStore(null))";
+ else
+ return to!string(_refCounted._store._payload);
+ }
+ }
+ }
}
///
-pure @system nothrow @nogc unittest
+@betterC pure @system nothrow @nogc unittest
{
// A pair of an `int` and a `size_t` - the latter being the
// reference count - will be dynamically allocated
@@ -5639,10 +6724,11 @@ pure @system unittest
}
auto a = A(4);
auto b = a.copy();
- assert(a.x._refCounted._store._count == 2, "BUG 4356 still unfixed");
+ assert(a.x._refCounted._store._count == 2,
+ "https://issues.dlang.org/show_bug.cgi?id=4356 still unfixed");
}
-pure @system nothrow @nogc unittest
+@betterC pure @system nothrow @nogc unittest
{
import std.algorithm.mutation : swap;
@@ -5650,8 +6736,8 @@ pure @system nothrow @nogc unittest
swap(p1, p2);
}
-// 6606
-@safe pure nothrow @nogc unittest
+// https://issues.dlang.org/show_bug.cgi?id=6606
+@betterC @safe pure nothrow @nogc unittest
{
union U {
size_t i;
@@ -5665,25 +6751,30 @@ pure @system nothrow @nogc unittest
alias SRC = RefCounted!S;
}
-// 6436
-@system pure unittest
+// https://issues.dlang.org/show_bug.cgi?id=6436
+@betterC @system pure unittest
{
- struct S { this(ref int val) { assert(val == 3); ++val; } }
+ struct S
+ {
+ this(int rval) { assert(rval == 1); }
+ this(ref int lval) { assert(lval == 3); ++lval; }
+ }
- int val = 3;
- auto s = RefCounted!S(val);
- assert(val == 4);
+ auto s1 = RefCounted!S(1);
+ int lval = 3;
+ auto s2 = RefCounted!S(lval);
+ assert(lval == 4);
}
// gc_addRange coverage
-@system pure unittest
+@betterC @system pure unittest
{
struct S { int* p; }
auto s = RefCounted!S(null);
}
-@system pure nothrow @nogc unittest
+@betterC @system pure nothrow @nogc unittest
{
RefCounted!int a;
a = 5; //This should not assert
@@ -5696,6 +6787,44 @@ pure @system nothrow @nogc unittest
RefCounted!(int*) c;
}
+// https://issues.dlang.org/show_bug.cgi?id=21638
+@betterC @system pure nothrow @nogc unittest
+{
+ static struct NoDefaultCtor
+ {
+ @disable this();
+ this(int x) @nogc nothrow pure { this.x = x; }
+ int x;
+ }
+ auto rc = RefCounted!(NoDefaultCtor, RefCountedAutoInitialize.no)(5);
+ assert(rc.x == 5);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20502
+@system unittest
+{
+ import std.conv : to;
+ // Check that string conversion is transparent for refcounted
+ // structs that do not have either toString or alias this.
+ static struct A { Object a; }
+ auto a = A(new Object());
+ auto r = refCounted(a);
+ assert(to!string(r) == to!string(a));
+ assert(to!string(cast(const) r) == to!string(cast(const) a));
+ // Check that string conversion is still transparent for refcounted
+ // structs that have alias this.
+ static struct B { int b; alias b this; }
+ static struct C { B b; alias b this; }
+ assert(to!string(refCounted(C(B(123)))) == to!string(C(B(123))));
+ // https://issues.dlang.org/show_bug.cgi?id=22093
+ // Check that uninitialized refcounted structs that previously could be
+ // converted to strings still can be.
+ alias R = typeof(r);
+ R r2;
+ cast(void) (((const ref R a) => to!string(a))(r2));
+ cast(void) to!string(RefCounted!(A, RefCountedAutoInitialize.no).init);
+}
+
/**
* Initializes a `RefCounted` with `val`. The template parameter
* `T` of `RefCounted` is inferred from `val`.
@@ -5705,7 +6834,7 @@ pure @system nothrow @nogc unittest
* Params:
* val = The value to be reference counted
* Returns:
- * An initialized $(D RefCounted) containing $(D val).
+ * An initialized `RefCounted` containing `val`.
* See_Also:
* $(HTTP en.cppreference.com/w/cpp/memory/shared_ptr/make_shared, C++'s make_shared)
*/
@@ -5816,13 +6945,20 @@ mixin template Proxy(alias a)
static if (accessibleFrom!(const typeof(this)))
{
- override hash_t toHash() const nothrow @trusted
+ override size_t toHash() const nothrow @safe
{
- static if (is(typeof(&a) == ValueType*))
- alias v = a;
+ static if (__traits(compiles, .hashOf(a)))
+ return .hashOf(a);
else
- auto v = a; // if a is (property) function
- return typeid(ValueType).getHash(cast(const void*)&v);
+ // Workaround for when .hashOf is not both @safe and nothrow.
+ {
+ static if (is(typeof(&a) == ValueType*))
+ alias v = a;
+ else
+ auto v = a; // if a is (property) function
+ // BUG: Improperly casts away `shared`!
+ return typeid(ValueType).getHash((() @trusted => cast(const void*) &v)());
+ }
}
}
}
@@ -5839,7 +6975,6 @@ mixin template Proxy(alias a)
}
auto ref opCmp(this X, B)(auto ref B b)
- if (!is(typeof(a.opCmp(b))) || !is(typeof(b.opCmp(a))))
{
static if (is(typeof(a.opCmp(b))))
return a.opCmp(b);
@@ -5853,13 +6988,20 @@ mixin template Proxy(alias a)
static if (accessibleFrom!(const typeof(this)))
{
- hash_t toHash() const nothrow @trusted
+ size_t toHash() const nothrow @safe
{
- static if (is(typeof(&a) == ValueType*))
- alias v = a;
+ static if (__traits(compiles, .hashOf(a)))
+ return .hashOf(a);
else
- auto v = a; // if a is (property) function
- return typeid(ValueType).getHash(cast(const void*)&v);
+ // Workaround for when .hashOf is not both @safe and nothrow.
+ {
+ static if (is(typeof(&a) == ValueType*))
+ alias v = a;
+ else
+ auto v = a; // if a is (property) function
+ // BUG: Improperly casts away `shared`!
+ return typeid(ValueType).getHash((() @trusted => cast(const void*) &v)());
+ }
}
}
}
@@ -5908,7 +7050,7 @@ mixin template Proxy(alias a)
auto ref opOpAssign (string op, this X, V )(auto ref V v)
{
- return mixin("a " ~op~"= v");
+ return mixin("a = a "~op~" v");
}
auto ref opIndexOpAssign(string op, this X, V, D...)(auto ref V v, auto ref D i)
{
@@ -6040,7 +7182,7 @@ mixin template Proxy(alias a)
*/
@safe unittest
{
- import std.math;
+ import std.math.traits : isInfinity;
float f = 1.0;
assert(!f.isInfinity);
@@ -6069,8 +7211,8 @@ mixin template Proxy(alias a)
static immutable arr = [1,2,3];
}
- foreach (T; AliasSeq!(MyInt, const MyInt, immutable MyInt))
- {
+ static foreach (T; AliasSeq!(MyInt, const MyInt, immutable MyInt))
+ {{
T m = 10;
static assert(!__traits(compiles, { int x = m; }));
static assert(!__traits(compiles, { void func(int n){} func(m); }));
@@ -6102,7 +7244,7 @@ mixin template Proxy(alias a)
static assert(T.init == int.init);
static assert(T.str == "str");
static assert(T.arr == [1,2,3]);
- }
+ }}
}
@system unittest
{
@@ -6114,8 +7256,8 @@ mixin template Proxy(alias a)
this(immutable int[] arr) immutable { value = arr; }
}
- foreach (T; AliasSeq!(MyArray, const MyArray, immutable MyArray))
- {
+ static foreach (T; AliasSeq!(MyArray, const MyArray, immutable MyArray))
+ {{
static if (is(T == immutable) && !is(typeof({ T a = [1,2,3,4]; })))
T a = [1,2,3,4].idup; // workaround until qualified ctor is properly supported
else
@@ -6142,7 +7284,7 @@ mixin template Proxy(alias a)
a[] *= 2; assert(a == [8,4,4,2]);
a[0 .. 2] /= 2; assert(a == [4,2,4,2]);
}
- }
+ }}
}
@system unittest
{
@@ -6209,7 +7351,7 @@ mixin template Proxy(alias a)
h.ifti2(4);
h.ifti3!int(4, 3);
- // bug5896 test
+ // https://issues.dlang.org/show_bug.cgi?id=5896 test
assert(h.opCast!int() == 0);
assert(cast(int) h == 0);
const ih = new const Hoge(new Foo());
@@ -6370,9 +7512,10 @@ mixin template Proxy(alias a)
MyFoo2 f2;
f2 = f2;
}
+
+// https://issues.dlang.org/show_bug.cgi?id=8613
@safe unittest
{
- // bug8613
static struct Name
{
mixin Proxy!val;
@@ -6385,28 +7528,41 @@ mixin template Proxy(alias a)
bool* b = Name("a") in names;
}
+// workaround for https://issues.dlang.org/show_bug.cgi?id=19669
+private enum isDIP1000 = __traits(compiles, () @safe {
+ int x;
+ int* p;
+ p = &x;
+});
+// excludes struct S; it's 'mixin Proxy!foo' doesn't compile with -dip1000
+static if (isDIP1000) {} else
@system unittest
{
- // bug14213, using function for the payload
+ // https://issues.dlang.org/show_bug.cgi?id=14213
+ // using function for the payload
static struct S
{
int foo() { return 12; }
mixin Proxy!foo;
}
+ S s;
+ assert(s + 1 == 13);
+ assert(s * 2 == 24);
+}
+
+@system unittest
+{
static class C
{
int foo() { return 12; }
mixin Proxy!foo;
}
- S s;
- assert(s + 1 == 13);
C c = new C();
- assert(s * 2 == 24);
}
// Check all floating point comparisons for both Proxy and Typedef,
// also against int and a Typedef!int, to be as regression-proof
-// as possible. bug 15561
+// as possible. https://issues.dlang.org/show_bug.cgi?id=15561
@safe unittest
{
static struct MyFloatImpl
@@ -6422,11 +7578,11 @@ mixin template Proxy(alias a)
assert(!(a>b));
assert(!(a >= b));
}
- foreach (T1; AliasSeq!(MyFloatImpl, Typedef!float, Typedef!double,
+ static foreach (T1; AliasSeq!(MyFloatImpl, Typedef!float, Typedef!double,
float, real, Typedef!int, int))
{
- foreach (T2; AliasSeq!(MyFloatImpl, Typedef!float))
- {
+ static foreach (T2; AliasSeq!(MyFloatImpl, Typedef!float))
+ {{
T1 a;
T2 b;
@@ -6448,65 +7604,36 @@ mixin template Proxy(alias a)
assert(a <= b);
assert(!(a>b));
assert(a >= b);
- }
+ }}
}
}
/**
$(B Typedef) allows the creation of a unique type which is
-based on an existing type. Unlike the $(D alias) feature,
+based on an existing type. Unlike the `alias` feature,
$(B Typedef) ensures the two types are not considered as equals.
-Example:
-----
-alias MyInt = Typedef!int;
-static void takeInt(int) { }
-static void takeMyInt(MyInt) { }
-
-int i;
-takeInt(i); // ok
-takeMyInt(i); // fails
-
-MyInt myInt;
-takeInt(myInt); // fails
-takeMyInt(myInt); // ok
-----
-
Params:
-init = Optional initial value for the new type. For example:
-
-----
-alias MyInt = Typedef!(int, 10);
-MyInt myInt;
-assert(myInt == 10); // default-initialized to 10
-----
-
-cookie = Optional, used to create multiple unique types which are
-based on the same origin type $(D T). For example:
-
-----
-alias TypeInt1 = Typedef!int;
-alias TypeInt2 = Typedef!int;
-
-// The two Typedefs are the same type.
-static assert(is(TypeInt1 == TypeInt2));
-
-alias MoneyEuros = Typedef!(float, float.init, "euros");
-alias MoneyDollars = Typedef!(float, float.init, "dollars");
-
-// The two Typedefs are _not_ the same type.
-static assert(!is(MoneyEuros == MoneyDollars));
-----
+ init = Optional initial value for the new type.
+ cookie = Optional, used to create multiple unique types which are
+ based on the same origin type `T`
Note: If a library routine cannot handle the Typedef type,
-you can use the $(D TypedefType) template to extract the
+you can use the `TypedefType` template to extract the
type which the Typedef wraps.
*/
struct Typedef(T, T init = T.init, string cookie=null)
{
private T Typedef_payload = init;
+ // https://issues.dlang.org/show_bug.cgi?id=18415
+ // prevent default construction if original type does too.
+ static if ((is(T == struct) || is(T == union)) && !is(typeof({T t;})))
+ {
+ @disable this();
+ }
+
this(T init)
{
Typedef_payload = init;
@@ -6556,11 +7683,99 @@ struct Typedef(T, T init = T.init, string cookie=null)
TD im() {return TD(Typedef_payload.im);}
}
}
+
+ /**
+ * Convert wrapped value to a human readable string
+ */
+ string toString(this T)()
+ {
+ import std.array : appender;
+ auto app = appender!string();
+ auto spec = singleSpec("%s");
+ toString(app, spec);
+ return app.data;
+ }
+
+ /// ditto
+ void toString(this T, W)(ref W writer, scope const ref FormatSpec!char fmt)
+ if (isOutputRange!(W, char))
+ {
+ formatValue(writer, Typedef_payload, fmt);
+ }
+
+ ///
+ @safe unittest
+ {
+ import std.conv : to;
+
+ int i = 123;
+ auto td = Typedef!int(i);
+ assert(i.to!string == td.to!string);
+ }
+}
+
+///
+@safe unittest
+{
+ alias MyInt = Typedef!int;
+ MyInt foo = 10;
+ foo++;
+ assert(foo == 11);
+}
+
+/// custom initialization values
+@safe unittest
+{
+ alias MyIntInit = Typedef!(int, 42);
+ static assert(is(TypedefType!MyIntInit == int));
+ static assert(MyIntInit() == 42);
+}
+
+/// Typedef creates a new type
+@safe unittest
+{
+ alias MyInt = Typedef!int;
+ static void takeInt(int) {}
+ static void takeMyInt(MyInt) {}
+
+ int i;
+ takeInt(i); // ok
+ static assert(!__traits(compiles, takeMyInt(i)));
+
+ MyInt myInt;
+ static assert(!__traits(compiles, takeInt(myInt)));
+ takeMyInt(myInt); // ok
+}
+
+/// Use the optional `cookie` argument to create different types of the same base type
+@safe unittest
+{
+ alias TypeInt1 = Typedef!int;
+ alias TypeInt2 = Typedef!int;
+
+ // The two Typedefs are the same type.
+ static assert(is(TypeInt1 == TypeInt2));
+
+ alias MoneyEuros = Typedef!(float, float.init, "euros");
+ alias MoneyDollars = Typedef!(float, float.init, "dollars");
+
+ // The two Typedefs are _not_ the same type.
+ static assert(!is(MoneyEuros == MoneyDollars));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=12461
+@safe unittest
+{
+ alias Int = Typedef!int;
+
+ Int a, b;
+ a += b;
+ assert(a == 0);
}
/**
-Get the underlying type which a $(D Typedef) wraps.
-If $(D T) is not a $(D Typedef) it will alias itself to $(D T).
+Get the underlying type which a `Typedef` wraps.
+If `T` is not a `Typedef` it will alias itself to `T`.
*/
template TypedefType(T)
{
@@ -6573,7 +7788,6 @@ template TypedefType(T)
///
@safe unittest
{
- import std.typecons : Typedef, TypedefType;
import std.conv : to;
alias MyInt = Typedef!int;
@@ -6658,7 +7872,17 @@ template TypedefType(T)
assert(drange3[$] == 123);
}
-@safe @nogc pure nothrow unittest // Bugzilla 11703
+// https://issues.dlang.org/show_bug.cgi?id=18415
+@safe @nogc pure nothrow unittest
+{
+ struct NoDefCtorS{@disable this();}
+ union NoDefCtorU{@disable this();}
+ static assert(!is(typeof({Typedef!NoDefCtorS s;})));
+ static assert(!is(typeof({Typedef!NoDefCtorU u;})));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=11703
+@safe @nogc pure nothrow unittest
{
alias I = Typedef!int;
static assert(is(typeof(I.min) == I));
@@ -6675,7 +7899,7 @@ template TypedefType(T)
@safe unittest
{
- // bug8655
+ // https://issues.dlang.org/show_bug.cgi?id=8655
import std.typecons;
import std.bitmanip;
static import core.stdc.config;
@@ -6691,7 +7915,8 @@ template TypedefType(T)
}
}
-@safe unittest // Issue 12596
+// https://issues.dlang.org/show_bug.cgi?id=12596
+@safe unittest
{
import std.typecons;
alias TD = Typedef!int;
@@ -6770,9 +7995,56 @@ template TypedefType(T)
assert(s2 == cs2);
}
+@system unittest // toString
+{
+ import std.meta : AliasSeq;
+ import std.conv : to;
+
+ struct TestS {}
+ class TestC {}
+
+ static foreach (T; AliasSeq!(int, bool, float, double, real,
+ char, dchar, wchar,
+ TestS, TestC,
+ int*, int[], int[2], int[int]))
+ {{
+ T t;
+
+ Typedef!T td;
+ Typedef!(const T) ctd;
+ Typedef!(immutable T) itd;
+
+ assert(t.to!string() == td.to!string());
+
+ static if (!(is(T == TestS) || is(T == TestC)))
+ {
+ assert(t.to!string() == ctd.to!string());
+ assert(t.to!string() == itd.to!string());
+ }
+ }}
+}
+
+@safe @nogc unittest // typedef'ed type with custom operators
+{
+ static struct MyInt
+ {
+ int value;
+ int opCmp(MyInt other)
+ {
+ if (value < other.value)
+ return -1;
+ return !(value == other.value);
+ }
+ }
+
+ auto m1 = Typedef!MyInt(MyInt(1));
+ auto m2 = Typedef!MyInt(MyInt(2));
+ assert(m1 < m2);
+}
+
/**
-Allocates a $(D class) object right inside the current scope,
-therefore avoiding the overhead of $(D new). This facility is unsafe;
+Allocates a `class` object right inside the current scope,
+therefore avoiding the overhead of `new`. This facility is unsafe;
it is the responsibility of the user to not escape a reference to the
object outside the scope.
@@ -6789,7 +8061,7 @@ It's illegal to move a class instance even if you are sure there
are no pointers to it. As such, it is illegal to move a scoped object.
*/
template scoped(T)
- if (is(T == class))
+if (is(T == class))
{
// _d_newclass now use default GC alignment (looks like (void*).sizeof * 2 for
// small objects). We will just use the maximum of filed alignments.
@@ -6803,7 +8075,7 @@ template scoped(T)
@property inout(T) Scoped_payload() inout
{
- void* alignedStore = cast(void*) aligned(cast(uintptr_t) Scoped_store.ptr);
+ void* alignedStore = cast(void*) aligned(cast(size_t) Scoped_store.ptr);
// As `Scoped` can be unaligned moved in memory class instance should be moved accordingly.
immutable size_t d = alignedStore - Scoped_store.ptr;
size_t* currD = cast(size_t*) &Scoped_store[$ - size_t.sizeof];
@@ -6829,17 +8101,17 @@ template scoped(T)
}
/** Returns the _scoped object.
- Params: args = Arguments to pass to $(D T)'s constructor.
+ Params: args = Arguments to pass to `T`'s constructor.
*/
@system auto scoped(Args...)(auto ref Args args)
{
- import std.conv : emplace;
+ import core.lifetime : emplace, forward;
Scoped result = void;
- void* alignedStore = cast(void*) aligned(cast(uintptr_t) result.Scoped_store.ptr);
+ void* alignedStore = cast(void*) aligned(cast(size_t) result.Scoped_store.ptr);
immutable size_t d = alignedStore - result.Scoped_store.ptr;
*cast(size_t*) &result.Scoped_store[$ - size_t.sizeof] = d;
- emplace!(Unqual!T)(result.Scoped_store[d .. $ - size_t.sizeof], args);
+ emplace!(Unqual!T)(result.Scoped_store[d .. $ - size_t.sizeof], forward!args);
return result;
}
}
@@ -6926,14 +8198,15 @@ template scoped(T)
destroy(*b2); // calls A's destructor for b2.a
}
-private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n)
- if (alignment > 0 && !((alignment - 1) & alignment))
+private size_t _alignUp(size_t alignment)(size_t n)
+if (alignment > 0 && !((alignment - 1) & alignment))
{
enum badEnd = alignment - 1; // 0b11, 0b111, ...
return (n + badEnd) & ~badEnd;
}
-@system unittest // Issue 6580 testcase
+// https://issues.dlang.org/show_bug.cgi?id=6580 testcase
+@system unittest
{
enum alignment = (void*).alignof;
@@ -6978,7 +8251,7 @@ private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n)
void test(size_t size)
{
import core.stdc.stdlib;
- alloca(size);
+ cast(void) alloca(size);
alignmentTest();
}
foreach (i; 0 .. 10)
@@ -6991,12 +8264,13 @@ private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n)
byte[size] arr;
alignmentTest();
}
- foreach (i; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
+ static foreach (i; 0 .. 11)
test!i();
}
}
-@system unittest // Original Issue 6580 testcase
+// Original https://issues.dlang.org/show_bug.cgi?id=6580 testcase
+@system unittest
{
class C { int i; byte b; }
@@ -7049,7 +8323,8 @@ private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n)
assert(A.dead, "asdasd");
}
-@system unittest // Issue 8039 testcase
+// https://issues.dlang.org/show_bug.cgi?id=8039 testcase
+@system unittest
{
static int dels;
static struct S { ~this(){ ++dels; } }
@@ -7074,7 +8349,7 @@ private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n)
@system unittest
{
- // bug4500
+ // https://issues.dlang.org/show_bug.cgi?id=4500
class A
{
this() { a = this; }
@@ -7153,11 +8428,16 @@ private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n)
@system unittest
{
- class C { this(ref int val) { assert(val == 3); ++val; } }
+ class C
+ {
+ this(int rval) { assert(rval == 1); }
+ this(ref int lval) { assert(lval == 3); ++lval; }
+ }
- int val = 3;
- auto s = scoped!C(val);
- assert(val == 4);
+ auto c1 = scoped!C(1);
+ int lval = 3;
+ auto c2 = scoped!C(lval);
+ assert(lval == 4);
}
@system unittest
@@ -7182,13 +8462,13 @@ private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n)
Defines a simple, self-documenting yes/no flag. This makes it easy for
APIs to define functions accepting flags without resorting to $(D
bool), which is opaque in calls, and without needing to define an
-enumerated type separately. Using $(D Flag!"Name") instead of $(D
+enumerated type separately. Using `Flag!"Name"` instead of $(D
bool) makes the flag's meaning visible in calls. Each yes/no flag has
its own type, which makes confusions and mix-ups impossible.
Example:
-Code calling $(D getLine) (usually far away from its definition) can't be
+Code calling `getLine` (usually far away from its definition) can't be
understood without looking at the documentation, even by users familiar with
the API:
----
@@ -7205,8 +8485,8 @@ auto line = getLine(false);
Assuming the reverse meaning (i.e. "ignoreTerminator") and inserting the wrong
code compiles and runs with erroneous results.
-After replacing the boolean parameter with an instantiation of $(D Flag), code
-calling $(D getLine) can be easily read and understood even by people not
+After replacing the boolean parameter with an instantiation of `Flag`, code
+calling `getLine` can be easily read and understood even by people not
fluent with the API:
----
@@ -7220,17 +8500,17 @@ string getLine(Flag!"keepTerminator" keepTerminator)
auto line = getLine(Yes.keepTerminator);
----
-The structs $(D Yes) and $(D No) are provided as shorthand for
-$(D Flag!"Name".yes) and $(D Flag!"Name".no) and are preferred for brevity and
+The structs `Yes` and `No` are provided as shorthand for
+`Flag!"Name".yes` and `Flag!"Name".no` and are preferred for brevity and
readability. These convenience structs mean it is usually unnecessary and
-counterproductive to create an alias of a $(D Flag) as a way of avoiding typing
+counterproductive to create an alias of a `Flag` as a way of avoiding typing
out the full type while specifying the affirmative or negative options.
-Passing categorical data by means of unstructured $(D bool)
+Passing categorical data by means of unstructured `bool`
parameters is classified under "simple-data coupling" by Steve
McConnell in the $(LUCKY Code Complete) book, along with three other
kinds of coupling. The author argues citing several studies that
-coupling has a negative effect on code quality. $(D Flag) offers a
+coupling has a negative effect on code quality. `Flag` offers a
simple structuring method for passing yes/no flags to APIs.
*/
template Flag(string name) {
@@ -7238,24 +8518,46 @@ template Flag(string name) {
enum Flag : bool
{
/**
- When creating a value of type $(D Flag!"Name"), use $(D
+ When creating a value of type `Flag!"Name"`, use $(D
Flag!"Name".no) for the negative option. When using a value
- of type $(D Flag!"Name"), compare it against $(D
- Flag!"Name".no) or just $(D false) or $(D 0). */
+ of type `Flag!"Name"`, compare it against $(D
+ Flag!"Name".no) or just `false` or `0`. */
no = false,
- /** When creating a value of type $(D Flag!"Name"), use $(D
+ /** When creating a value of type `Flag!"Name"`, use $(D
Flag!"Name".yes) for the affirmative option. When using a
- value of type $(D Flag!"Name"), compare it against $(D
+ value of type `Flag!"Name"`, compare it against $(D
Flag!"Name".yes).
*/
yes = true
}
}
+///
+@safe unittest
+{
+ Flag!"abc" flag;
+
+ assert(flag == Flag!"abc".no);
+ assert(flag == No.abc);
+ assert(!flag);
+ if (flag) assert(0);
+}
+
+///
+@safe unittest
+{
+ auto flag = Yes.abc;
+
+ assert(flag);
+ assert(flag == Yes.abc);
+ if (!flag) assert(0);
+ if (flag) {} else assert(0);
+}
+
/**
-Convenience names that allow using e.g. $(D Yes.encryption) instead of
-$(D Flag!"encryption".yes) and $(D No.encryption) instead of $(D
+Convenience names that allow using e.g. `Yes.encryption` instead of
+`Flag!"encryption".yes` and `No.encryption` instead of $(D
Flag!"encryption".no).
*/
struct Yes
@@ -7279,16 +8581,23 @@ struct No
///
@safe unittest
{
- Flag!"abc" flag1;
- assert(flag1 == Flag!"abc".no);
- assert(flag1 == No.abc);
- assert(!flag1);
- if (flag1) assert(false);
- flag1 = Yes.abc;
- assert(flag1);
- if (!flag1) assert(false);
- if (flag1) {} else assert(false);
- assert(flag1 == Yes.abc);
+ Flag!"abc" flag;
+
+ assert(flag == Flag!"abc".no);
+ assert(flag == No.abc);
+ assert(!flag);
+ if (flag) assert(0);
+}
+
+///
+@safe unittest
+{
+ auto flag = Yes.abc;
+
+ assert(flag);
+ assert(flag == Yes.abc);
+ if (!flag) assert(0);
+ if (flag) {} else assert(0);
}
/**
@@ -7303,12 +8612,12 @@ template isBitFlagEnum(E)
{
enum isBitFlagEnum = (E.min >= 0) &&
{
- foreach (immutable flag; EnumMembers!E)
- {
+ static foreach (immutable flag; EnumMembers!E)
+ {{
Base value = flag;
value &= value - 1;
if (value != 0) return false;
- }
+ }}
return true;
}();
}
@@ -7331,7 +8640,11 @@ template isBitFlagEnum(E)
}
static assert(isBitFlagEnum!A);
+}
+/// Test an enum with default (consecutive) values
+@safe pure nothrow unittest
+{
enum B
{
A,
@@ -7341,7 +8654,11 @@ template isBitFlagEnum(E)
}
static assert(!isBitFlagEnum!B);
+}
+/// Test an enum with non-integral values
+@safe pure nothrow unittest
+{
enum C: double
{
A = 1 << 0,
@@ -7356,7 +8673,7 @@ A typesafe structure for storing combinations of enum values.
This template defines a simple struct to represent bitwise OR combinations of
enum values. It can be used if all the enum values are integral constants with
-a bit count of at most 1, or if the $(D unsafe) parameter is explicitly set to
+a bit count of at most 1, or if the `unsafe` parameter is explicitly set to
Yes.
This is much safer than using the enum itself to store
the OR combination, which can produce surprising effects like this:
@@ -7377,7 +8694,8 @@ final switch (e)
}
----
*/
-struct BitFlags(E, Flag!"unsafe" unsafe = No.unsafe) if (unsafe || isBitFlagEnum!(E))
+struct BitFlags(E, Flag!"unsafe" unsafe = No.unsafe)
+if (unsafe || isBitFlagEnum!(E))
{
@safe @nogc pure nothrow:
private:
@@ -7502,14 +8820,52 @@ public:
{
return opBinary!op(flag);
}
+
+ bool opDispatch(string name)() const
+ if (__traits(hasMember, E, name))
+ {
+ enum e = __traits(getMember, E, name);
+ return (mValue & e) == e;
+ }
+
+ void opDispatch(string name)(bool set)
+ if (__traits(hasMember, E, name))
+ {
+ enum e = __traits(getMember, E, name);
+ if (set)
+ mValue |= e;
+ else
+ mValue &= ~e;
+ }
}
-/// BitFlags can be manipulated with the usual operators
+/// Set values with the | operator and test with &
@safe @nogc pure nothrow unittest
{
- import std.traits : EnumMembers;
+ enum Enum
+ {
+ A = 1 << 0,
+ }
+
+ // A default constructed BitFlags has no value set
+ immutable BitFlags!Enum flags_empty;
+ assert(!flags_empty.A);
- // You can use such an enum with BitFlags straight away
+ // Value can be set with the | operator
+ immutable flags_A = flags_empty | Enum.A;
+
+ // and tested using property access
+ assert(flags_A.A);
+
+ // or the & operator
+ assert(flags_A & Enum.A);
+ // which commutes.
+ assert(Enum.A & flags_A);
+}
+
+/// A default constructed BitFlags has no value set
+@safe @nogc pure nothrow unittest
+{
enum Enum
{
None,
@@ -7517,82 +8873,153 @@ public:
B = 1 << 1,
C = 1 << 2
}
- BitFlags!Enum flags1;
- assert(!(flags1 & (Enum.A | Enum.B | Enum.C)));
-
- // You need to specify the `unsafe` parameter for enum with custom values
- enum UnsafeEnum
- {
- A,
- B,
- C,
- D = B|C
- }
- static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags2; }));
- BitFlags!(UnsafeEnum, Yes.unsafe) flags3;
immutable BitFlags!Enum flags_empty;
- // A default constructed BitFlags has no value set
+ assert(!(flags_empty & (Enum.A | Enum.B | Enum.C)));
assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C));
+}
- // Value can be set with the | operator
- immutable BitFlags!Enum flags_A = flags_empty | Enum.A;
+// BitFlags can be variadically initialized
+@safe @nogc pure nothrow unittest
+{
+ import std.traits : EnumMembers;
- // And tested with the & operator
- assert(flags_A & Enum.A);
+ enum Enum
+ {
+ A = 1 << 0,
+ B = 1 << 1,
+ C = 1 << 2
+ }
- // Which commutes
- assert(Enum.A & flags_A);
+ // Values can also be set using property access
+ BitFlags!Enum flags;
+ flags.A = true;
+ assert(flags & Enum.A);
+ flags.A = false;
+ assert(!(flags & Enum.A));
// BitFlags can be variadically initialized
immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B);
- assert((flags_AB & Enum.A) && (flags_AB & Enum.B) && !(flags_AB & Enum.C));
-
- // Use the ~ operator for subtracting flags
- immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A);
- assert(!(flags_B & Enum.A) && (flags_B & Enum.B) && !(flags_B & Enum.C));
+ assert(flags_AB.A && flags_AB.B && !flags_AB.C);
// You can use the EnumMembers template to set all flags
immutable BitFlags!Enum flags_all = EnumMembers!Enum;
+ assert(flags_all.A && flags_all.B && flags_all.C);
+}
- // use & between BitFlags for intersection
+/// Binary operations: subtracting and intersecting flags
+@safe @nogc pure nothrow unittest
+{
+ enum Enum
+ {
+ A = 1 << 0,
+ B = 1 << 1,
+ C = 1 << 2,
+ }
+ immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B);
immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C);
+
+ // Use the ~ operator for subtracting flags
+ immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A);
+ assert(!flags_B.A && flags_B.B && !flags_B.C);
+
+ // use & between BitFlags for intersection
assert(flags_B == (flags_BC & flags_AB));
+}
+
+/// All the binary operators work in their assignment version
+@safe @nogc pure nothrow unittest
+{
+ enum Enum
+ {
+ A = 1 << 0,
+ B = 1 << 1,
+ }
+
+ BitFlags!Enum flags_empty, temp, flags_AB;
+ flags_AB = Enum.A | Enum.B;
- // All the binary operators work in their assignment version
- BitFlags!Enum temp = flags_empty;
temp |= flags_AB;
assert(temp == (flags_empty | flags_AB));
+
temp = flags_empty;
temp |= Enum.B;
assert(temp == (flags_empty | Enum.B));
+
temp = flags_empty;
temp &= flags_AB;
assert(temp == (flags_empty & flags_AB));
+
temp = flags_empty;
temp &= Enum.A;
assert(temp == (flags_empty & Enum.A));
+}
+
+/// Conversion to bool and int
+@safe @nogc pure nothrow unittest
+{
+ enum Enum
+ {
+ A = 1 << 0,
+ B = 1 << 1,
+ }
+
+ BitFlags!Enum flags;
// BitFlags with no value set evaluate to false
- assert(!flags_empty);
+ assert(!flags);
// BitFlags with at least one value set evaluate to true
- assert(flags_A);
+ flags |= Enum.A;
+ assert(flags);
// This can be useful to check intersection between BitFlags
- assert(flags_A & flags_AB);
- assert(flags_AB & Enum.A);
+ BitFlags!Enum flags_AB = Enum.A | Enum.B;
+ assert(flags & flags_AB);
+ assert(flags & Enum.A);
- // Finally, you can of course get you raw value out of flags
- auto value = cast(int) flags_A;
+ // You can of course get you raw value out of flags
+ auto value = cast(int) flags;
assert(value == Enum.A);
}
+/// You need to specify the `unsafe` parameter for enums with custom values
+@safe @nogc pure nothrow unittest
+{
+ enum UnsafeEnum
+ {
+ A = 1,
+ B = 2,
+ C = 4,
+ BC = B|C
+ }
+ static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags; }));
+ BitFlags!(UnsafeEnum, Yes.unsafe) flags;
+
+ // property access tests for exact match of unsafe enums
+ flags.B = true;
+ assert(!flags.BC); // only B
+ flags.C = true;
+ assert(flags.BC); // both B and C
+ flags.B = false;
+ assert(!flags.BC); // only C
+
+ // property access sets all bits of unsafe enum group
+ flags = flags.init;
+ flags.BC = true;
+ assert(!flags.A && flags.B && flags.C);
+ flags.A = true;
+ flags.BC = false;
+ assert(flags.A && !flags.B && !flags.C);
+}
+
+private enum false_(T) = false;
+
// ReplaceType
/**
Replaces all occurrences of `From` into `To`, in one or more types `T`. For
-example, $(D ReplaceType!(int, uint, Tuple!(int, float)[string])) yields
-$(D Tuple!(uint, float)[string]). The types in which replacement is performed
+example, `ReplaceType!(int, uint, Tuple!(int, float)[string])` yields
+`Tuple!(uint, float)[string]`. The types in which replacement is performed
may be arbitrarily complex, including qualifiers, built-in type constructors
(pointers, arrays, associative arrays, functions, and delegates), and template
instantiations; replacement proceeds transitively through the type definition.
@@ -7605,85 +9032,109 @@ placeholder type `This` in $(REF Algebraic, std,variant).
Returns: `ReplaceType` aliases itself to the type(s) that result after
replacement.
*/
-template ReplaceType(From, To, T...)
+alias ReplaceType(From, To, T...) = ReplaceTypeUnless!(false_, From, To, T);
+
+///
+@safe unittest
{
+ static assert(
+ is(ReplaceType!(int, string, int[]) == string[]) &&
+ is(ReplaceType!(int, string, int[int]) == string[string]) &&
+ is(ReplaceType!(int, string, const(int)[]) == const(string)[]) &&
+ is(ReplaceType!(int, string, Tuple!(int[], float))
+ == Tuple!(string[], float))
+ );
+}
+
+/**
+Like $(LREF ReplaceType), but does not perform replacement in types for which
+`pred` evaluates to `true`.
+*/
+template ReplaceTypeUnless(alias pred, From, To, T...)
+{
+ import std.meta;
+
static if (T.length == 1)
{
- static if (is(T[0] == From))
- alias ReplaceType = To;
+ static if (pred!(T[0]))
+ alias ReplaceTypeUnless = T[0];
+ else static if (is(T[0] == From))
+ alias ReplaceTypeUnless = To;
else static if (is(T[0] == const(U), U))
- alias ReplaceType = const(ReplaceType!(From, To, U));
+ alias ReplaceTypeUnless = const(ReplaceTypeUnless!(pred, From, To, U));
else static if (is(T[0] == immutable(U), U))
- alias ReplaceType = immutable(ReplaceType!(From, To, U));
+ alias ReplaceTypeUnless = immutable(ReplaceTypeUnless!(pred, From, To, U));
else static if (is(T[0] == shared(U), U))
- alias ReplaceType = shared(ReplaceType!(From, To, U));
+ alias ReplaceTypeUnless = shared(ReplaceTypeUnless!(pred, From, To, U));
else static if (is(T[0] == U*, U))
{
static if (is(U == function))
- alias ReplaceType = replaceTypeInFunctionType!(From, To, T[0]);
+ alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]);
else
- alias ReplaceType = ReplaceType!(From, To, U)*;
+ alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)*;
}
else static if (is(T[0] == delegate))
{
- alias ReplaceType = replaceTypeInFunctionType!(From, To, T[0]);
+ alias ReplaceTypeUnless = replaceTypeInFunctionTypeUnless!(pred, From, To, T[0]);
}
else static if (is(T[0] == function))
{
static assert(0, "Function types not supported," ~
" use a function pointer type instead of " ~ T[0].stringof);
}
- else static if (is(T[0] : U!V, alias U, V...))
+ else static if (is(T[0] == U!V, alias U, V...))
{
template replaceTemplateArgs(T...)
{
static if (is(typeof(T[0]))) // template argument is value or symbol
enum replaceTemplateArgs = T[0];
else
- alias replaceTemplateArgs = ReplaceType!(From, To, T[0]);
+ alias replaceTemplateArgs = ReplaceTypeUnless!(pred, From, To, T[0]);
}
- alias ReplaceType = U!(staticMap!(replaceTemplateArgs, V));
+ alias ReplaceTypeUnless = U!(staticMap!(replaceTemplateArgs, V));
}
else static if (is(T[0] == struct))
- // don't match with alias this struct below (Issue 15168)
- alias ReplaceType = T[0];
+ // don't match with alias this struct below
+ // https://issues.dlang.org/show_bug.cgi?id=15168
+ alias ReplaceTypeUnless = T[0];
else static if (is(T[0] == U[], U))
- alias ReplaceType = ReplaceType!(From, To, U)[];
+ alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[];
else static if (is(T[0] == U[n], U, size_t n))
- alias ReplaceType = ReplaceType!(From, To, U)[n];
+ alias ReplaceTypeUnless = ReplaceTypeUnless!(pred, From, To, U)[n];
else static if (is(T[0] == U[V], U, V))
- alias ReplaceType =
- ReplaceType!(From, To, U)[ReplaceType!(From, To, V)];
+ alias ReplaceTypeUnless =
+ ReplaceTypeUnless!(pred, From, To, U)[ReplaceTypeUnless!(pred, From, To, V)];
else
- alias ReplaceType = T[0];
+ alias ReplaceTypeUnless = T[0];
}
else static if (T.length > 1)
{
- alias ReplaceType = AliasSeq!(ReplaceType!(From, To, T[0]),
- ReplaceType!(From, To, T[1 .. $]));
+ alias ReplaceTypeUnless = AliasSeq!(ReplaceTypeUnless!(pred, From, To, T[0]),
+ ReplaceTypeUnless!(pred, From, To, T[1 .. $]));
}
else
{
- alias ReplaceType = AliasSeq!();
+ alias ReplaceTypeUnless = AliasSeq!();
}
}
///
@safe unittest
{
+ import std.traits : isArray;
+
static assert(
- is(ReplaceType!(int, string, int[]) == string[]) &&
- is(ReplaceType!(int, string, int[int]) == string[string]) &&
- is(ReplaceType!(int, string, const(int)[]) == const(string)[]) &&
- is(ReplaceType!(int, string, Tuple!(int[], float))
- == Tuple!(string[], float))
- );
+ is(ReplaceTypeUnless!(isArray, int, string, int*) == string*) &&
+ is(ReplaceTypeUnless!(isArray, int, string, int[]) == int[]) &&
+ is(ReplaceTypeUnless!(isArray, int, string, Tuple!(int, int[]))
+ == Tuple!(string, int[]))
+ );
}
-private template replaceTypeInFunctionType(From, To, fun)
+private template replaceTypeInFunctionTypeUnless(alias pred, From, To, fun)
{
- alias RX = ReplaceType!(From, To, ReturnType!fun);
- alias PX = AliasSeq!(ReplaceType!(From, To, Parameters!fun));
+ alias RX = ReplaceTypeUnless!(pred, From, To, ReturnType!fun);
+ alias PX = AliasSeq!(ReplaceTypeUnless!(pred, From, To, Parameters!fun));
// Wrapping with AliasSeq is neccesary because ReplaceType doesn't return
// tuple if Parameters!fun.length == 1
@@ -7709,12 +9160,14 @@ private template replaceTypeInFunctionType(From, To, fun)
result ~= " function";
result ~= "(";
- foreach (i, _; PX)
+ static foreach (i; 0 .. PX.length)
{
if (i)
result ~= ", ";
if (storageClasses[i] & ParameterStorageClass.scope_)
result ~= "scope ";
+ if (storageClasses[i] & ParameterStorageClass.in_)
+ result ~= "in ";
if (storageClasses[i] & ParameterStorageClass.out_)
result ~= "out ";
if (storageClasses[i] & ParameterStorageClass.ref_)
@@ -7759,9 +9212,8 @@ private template replaceTypeInFunctionType(From, To, fun)
return result;
}
- //pragma(msg, "gen ==> ", gen());
- mixin("alias replaceTypeInFunctionType = " ~ gen() ~ ";");
+ mixin("alias replaceTypeInFunctionTypeUnless = " ~ gen() ~ ";");
}
@safe unittest
@@ -7807,6 +9259,10 @@ private template replaceTypeInFunctionType(From, To, fun)
float function(lazy float, long),
int, float, int function(out long, ref const int),
float function(out long, ref const float),
+ int, float, int function(in long, ref const int),
+ float function(in long, ref const float),
+ int, float, int function(long, in int),
+ float function(long, in float),
int, int, int, int,
int, float, int, float,
int, float, const int, const float,
@@ -7841,7 +9297,7 @@ private template replaceTypeInFunctionType(From, To, fun)
string[3] function(string[] arr, string[2] ...) pure @trusted,
);
- // Bugzilla 15168
+ // https://issues.dlang.org/show_bug.cgi?id=15168
static struct T1 { string s; alias s this; }
static struct T2 { char[10] s; alias s this; }
static struct T3 { string[string] s; alias s this; }
@@ -7852,7 +9308,8 @@ private template replaceTypeInFunctionType(From, To, fun)
);
}
-@safe unittest // Bugzilla 17116
+// https://issues.dlang.org/show_bug.cgi?id=17116
+@safe unittest
{
alias ConstDg = void delegate(float) const;
alias B = void delegate(int) const;
@@ -7860,6 +9317,30 @@ private template replaceTypeInFunctionType(From, To, fun)
static assert(is(B == A));
}
+ // https://issues.dlang.org/show_bug.cgi?id=19696
+@safe unittest
+{
+ static struct T(U) {}
+ static struct S { T!int t; alias t this; }
+ static assert(is(ReplaceType!(float, float, S) == S));
+}
+
+ // https://issues.dlang.org/show_bug.cgi?id=19697
+@safe unittest
+{
+ class D(T) {}
+ class C : D!C {}
+ static assert(is(ReplaceType!(float, float, C)));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=16132
+@safe unittest
+{
+ interface I(T) {}
+ class C : I!int {}
+ static assert(is(ReplaceType!(int, string, C) == C));
+}
+
/**
Ternary type with three truth values:
@@ -7946,6 +9427,13 @@ struct Ternary
{
return make((26_504 >> (value + rhs.value)) & 6);
}
+
+ /// ditto
+ Ternary opBinary(string s)(bool rhs)
+ if (s == "|" || s == "&" || s == "^")
+ {
+ return this.opBinary!s(Ternary(rhs));
+ }
}
///
@@ -8030,3 +9518,14 @@ unittest
assert(~Ternary.no == Ternary.yes);
assert(~Ternary.unknown == Ternary.unknown);
}
+
+@safe @nogc nothrow pure
+unittest
+{
+ Ternary a = Ternary(true);
+ assert(a == Ternary.yes);
+ assert((a & false) == Ternary.no);
+ assert((a | false) == Ternary.yes);
+ assert((a ^ true) == Ternary.no);
+ assert((a ^ false) == Ternary.yes);
+}
diff --git a/libphobos/src/std/typetuple.d b/libphobos/src/std/typetuple.d
index dedbdc21580..ecf2cc23db6 100644
--- a/libphobos/src/std/typetuple.d
+++ b/libphobos/src/std/typetuple.d
@@ -2,10 +2,10 @@
* This module was renamed to disambiguate the term tuple, use
* $(MREF std, meta) instead.
*
- * Copyright: Copyright Digital Mars 2005 - 2015.
+ * Copyright: Copyright The D Language Foundation 2005 - 2015.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors:
- * Source: $(PHOBOSSRC std/_typetuple.d)
+ * Source: $(PHOBOSSRC std/typetuple.d)
*
* $(SCRIPT inhibitQuickIndex = 1;)
*/
@@ -28,6 +28,7 @@ alias TypeTuple = AliasSeq;
{
return td[0] + cast(int) td[1];
}
+ assert(foo(1, 2.5) == 3);
}
///
diff --git a/libphobos/src/std/uni.d b/libphobos/src/std/uni.d
deleted file mode 100644
index 0b3da58286c..00000000000
--- a/libphobos/src/std/uni.d
+++ /dev/null
@@ -1,9768 +0,0 @@
-// Written in the D programming language.
-
-/++
- $(P The $(D std.uni) module provides an implementation
- of fundamental Unicode algorithms and data structures.
- This doesn't include UTF encoding and decoding primitives,
- see $(REF decode, std,_utf) and $(REF encode, std,_utf) in $(MREF std, utf)
- for this functionality. )
-
-$(SCRIPT inhibitQuickIndex = 1;)
-$(BOOKTABLE,
-$(TR $(TH Category) $(TH Functions))
-$(TR $(TD Decode) $(TD
- $(LREF byCodePoint)
- $(LREF byGrapheme)
- $(LREF decodeGrapheme)
- $(LREF graphemeStride)
-))
-$(TR $(TD Comparison) $(TD
- $(LREF icmp)
- $(LREF sicmp)
-))
-$(TR $(TD Classification) $(TD
- $(LREF isAlpha)
- $(LREF isAlphaNum)
- $(LREF isCodepointSet)
- $(LREF isControl)
- $(LREF isFormat)
- $(LREF isGraphical)
- $(LREF isIntegralPair)
- $(LREF isMark)
- $(LREF isNonCharacter)
- $(LREF isNumber)
- $(LREF isPrivateUse)
- $(LREF isPunctuation)
- $(LREF isSpace)
- $(LREF isSurrogate)
- $(LREF isSurrogateHi)
- $(LREF isSurrogateLo)
- $(LREF isSymbol)
- $(LREF isWhite)
-))
-$(TR $(TD Normalization) $(TD
- $(LREF NFC)
- $(LREF NFD)
- $(LREF NFKD)
- $(LREF NormalizationForm)
- $(LREF normalize)
-))
-$(TR $(TD Decompose) $(TD
- $(LREF decompose)
- $(LREF decomposeHangul)
- $(LREF UnicodeDecomposition)
-))
-$(TR $(TD Compose) $(TD
- $(LREF compose)
- $(LREF composeJamo)
-))
-$(TR $(TD Sets) $(TD
- $(LREF CodepointInterval)
- $(LREF CodepointSet)
- $(LREF InversionList)
- $(LREF unicode)
-))
-$(TR $(TD Trie) $(TD
- $(LREF codepointSetTrie)
- $(LREF CodepointSetTrie)
- $(LREF codepointTrie)
- $(LREF CodepointTrie)
- $(LREF toTrie)
- $(LREF toDelegate)
-))
-$(TR $(TD Casing) $(TD
- $(LREF asCapitalized)
- $(LREF asLowerCase)
- $(LREF asUpperCase)
- $(LREF isLower)
- $(LREF isUpper)
- $(LREF toLower)
- $(LREF toLowerInPlace)
- $(LREF toUpper)
- $(LREF toUpperInPlace)
-))
-$(TR $(TD Utf8Matcher) $(TD
- $(LREF isUtfMatcher)
- $(LREF MatcherConcept)
- $(LREF utfMatcher)
-))
-$(TR $(TD Separators) $(TD
- $(LREF lineSep)
- $(LREF nelSep)
- $(LREF paraSep)
-))
-$(TR $(TD Building blocks) $(TD
- $(LREF allowedIn)
- $(LREF combiningClass)
- $(LREF Grapheme)
-))
-)
-
- $(P All primitives listed operate on Unicode characters and
- sets of characters. For functions which operate on ASCII characters
- and ignore Unicode $(CHARACTERS), see $(MREF std, ascii).
- For definitions of Unicode $(CHARACTER), $(CODEPOINT) and other terms
- used throughout this module see the $(S_LINK Terminology, terminology) section
- below.
- )
- $(P The focus of this module is the core needs of developing Unicode-aware
- applications. To that effect it provides the following optimized primitives:
- )
- $(UL
- $(LI Character classification by category and common properties:
- $(LREF isAlpha), $(LREF isWhite) and others.
- )
- $(LI
- Case-insensitive string comparison ($(LREF sicmp), $(LREF icmp)).
- )
- $(LI
- Converting text to any of the four normalization forms via $(LREF normalize).
- )
- $(LI
- Decoding ($(LREF decodeGrapheme)) and iteration ($(LREF byGrapheme), $(LREF graphemeStride))
- by user-perceived characters, that is by $(LREF Grapheme) clusters.
- )
- $(LI
- Decomposing and composing of individual character(s) according to canonical
- or compatibility rules, see $(LREF compose) and $(LREF decompose),
- including the specific version for Hangul syllables $(LREF composeJamo)
- and $(LREF decomposeHangul).
- )
- )
- $(P It's recognized that an application may need further enhancements
- and extensions, such as less commonly known algorithms,
- or tailoring existing ones for region specific needs. To help users
- with building any extra functionality beyond the core primitives,
- the module provides:
- )
- $(UL
- $(LI
- $(LREF CodepointSet), a type for easy manipulation of sets of characters.
- Besides the typical set algebra it provides an unusual feature:
- a D source code generator for detection of $(CODEPOINTS) in this set.
- This is a boon for meta-programming parser frameworks,
- and is used internally to power classification in small
- sets like $(LREF isWhite).
- )
- $(LI
- A way to construct optimal packed multi-stage tables also known as a
- special case of $(LINK2 https://en.wikipedia.org/wiki/Trie, Trie).
- The functions $(LREF codepointTrie), $(LREF codepointSetTrie)
- construct custom tries that map dchar to value.
- The end result is a fast and predictable $(BIGOH 1) lookup that powers
- functions like $(LREF isAlpha) and $(LREF combiningClass),
- but for user-defined data sets.
- )
- $(LI
- A useful technique for Unicode-aware parsers that perform
- character classification of encoded $(CODEPOINTS)
- is to avoid unnecassary decoding at all costs.
- $(LREF utfMatcher) provides an improvement over the usual workflow
- of decode-classify-process, combining the decoding and classification
- steps. By extracting necessary bits directly from encoded
- $(S_LINK Code unit, code units) matchers achieve
- significant performance improvements. See $(LREF MatcherConcept) for
- the common interface of UTF matchers.
- )
- $(LI
- Generally useful building blocks for customized normalization:
- $(LREF combiningClass) for querying combining class
- and $(LREF allowedIn) for testing the Quick_Check
- property of a given normalization form.
- )
- $(LI
- Access to a large selection of commonly used sets of $(CODEPOINTS).
- $(S_LINK Unicode properties, Supported sets) include Script,
- Block and General Category. The exact contents of a set can be
- observed in the CLDR utility, on the
- $(HTTP www.unicode.org/cldr/utility/properties.jsp, property index) page
- of the Unicode website.
- See $(LREF unicode) for easy and (optionally) compile-time checked set
- queries.
- )
- )
- $(SECTION Synopsis)
- ---
- import std.uni;
- void main()
- {
- // initialize code point sets using script/block or property name
- // now 'set' contains code points from both scripts.
- auto set = unicode("Cyrillic") | unicode("Armenian");
- // same thing but simpler and checked at compile-time
- auto ascii = unicode.ASCII;
- auto currency = unicode.Currency_Symbol;
-
- // easy set ops
- auto a = set & ascii;
- assert(a.empty); // as it has no intersection with ascii
- a = set | ascii;
- auto b = currency - a; // subtract all ASCII, Cyrillic and Armenian
-
- // some properties of code point sets
- assert(b.length > 45); // 46 items in Unicode 6.1, even more in 6.2
- // testing presence of a code point in a set
- // is just fine, it is O(logN)
- assert(!b['$']);
- assert(!b['\u058F']); // Armenian dram sign
- assert(b['Â¥']);
-
- // building fast lookup tables, these guarantee O(1) complexity
- // 1-level Trie lookup table essentially a huge bit-set ~262Kb
- auto oneTrie = toTrie!1(b);
- // 2-level far more compact but typically slightly slower
- auto twoTrie = toTrie!2(b);
- // 3-level even smaller, and a bit slower yet
- auto threeTrie = toTrie!3(b);
- assert(oneTrie['£']);
- assert(twoTrie['£']);
- assert(threeTrie['£']);
-
- // build the trie with the most sensible trie level
- // and bind it as a functor
- auto cyrillicOrArmenian = toDelegate(set);
- auto balance = find!(cyrillicOrArmenian)("Hello Õ¨Õ¶Õ¯Õ¥Ö€!");
- assert(balance == "Õ¨Õ¶Õ¯Õ¥Ö€!");
- // compatible with bool delegate(dchar)
- bool delegate(dchar) bindIt = cyrillicOrArmenian;
-
- // Normalization
- string s = "Plain ascii (and not only), is always normalized!";
- assert(s is normalize(s));// is the same string
-
- string nonS = "A\u0308ffin"; // A ligature
- auto nS = normalize(nonS); // to NFC, the W3C endorsed standard
- assert(nS == "Äffin");
- assert(nS != nonS);
- string composed = "Äffin";
-
- assert(normalize!NFD(composed) == "A\u0308ffin");
- // to NFKD, compatibility decomposition useful for fuzzy matching/searching
- assert(normalize!NFKD("2¹â°") == "210");
- }
- ---
- $(SECTION Terminology
- )
- $(P The following is a list of important Unicode notions
- and definitions. Any conventions used specifically in this
- module alone are marked as such. The descriptions are based on the formal
- definition as found in $(HTTP www.unicode.org/versions/Unicode6.2.0/ch03.pdf,
- chapter three of The Unicode Standard Core Specification.)
- )
- $(P $(DEF Abstract character) A unit of information used for the organization,
- control, or representation of textual data.
- Note that:
- $(UL
- $(LI When representing data, the nature of that data
- is generally symbolic as opposed to some other
- kind of data (for example, visual).
- )
- $(LI An abstract character has no concrete form
- and should not be confused with a $(S_LINK Glyph, glyph).
- )
- $(LI An abstract character does not necessarily
- correspond to what a user thinks of as a “characterâ€
- and should not be confused with a $(LREF Grapheme).
- )
- $(LI The abstract characters encoded (see Encoded character)
- are known as Unicode abstract characters.
- )
- $(LI Abstract characters not directly
- encoded by the Unicode Standard can often be
- represented by the use of combining character sequences.
- )
- )
- )
- $(P $(DEF Canonical decomposition)
- The decomposition of a character or character sequence
- that results from recursively applying the canonical
- mappings found in the Unicode Character Database
- and these described in Conjoining Jamo Behavior
- (section 12 of
- $(HTTP www.unicode.org/uni2book/ch03.pdf, Unicode Conformance)).
- )
- $(P $(DEF Canonical composition)
- The precise definition of the Canonical composition
- is the algorithm as specified in $(HTTP www.unicode.org/uni2book/ch03.pdf,
- Unicode Conformance) section 11.
- Informally it's the process that does the reverse of the canonical
- decomposition with the addition of certain rules
- that e.g. prevent legacy characters from appearing in the composed result.
- )
- $(P $(DEF Canonical equivalent)
- Two character sequences are said to be canonical equivalents if
- their full canonical decompositions are identical.
- )
- $(P $(DEF Character) Typically differs by context.
- For the purpose of this documentation the term $(I character)
- implies $(I encoded character), that is, a code point having
- an assigned abstract character (a symbolic meaning).
- )
- $(P $(DEF Code point) Any value in the Unicode codespace;
- that is, the range of integers from 0 to 10FFFF (hex).
- Not all code points are assigned to encoded characters.
- )
- $(P $(DEF Code unit) The minimal bit combination that can represent
- a unit of encoded text for processing or interchange.
- Depending on the encoding this could be:
- 8-bit code units in the UTF-8 ($(D char)),
- 16-bit code units in the UTF-16 ($(D wchar)),
- and 32-bit code units in the UTF-32 ($(D dchar)).
- $(I Note that in UTF-32, a code unit is a code point
- and is represented by the D $(D dchar) type.)
- )
- $(P $(DEF Combining character) A character with the General Category
- of Combining Mark(M).
- $(UL
- $(LI All characters with non-zero canonical combining class
- are combining characters, but the reverse is not the case:
- there are combining characters with a zero combining class.
- )
- $(LI These characters are not normally used in isolation
- unless they are being described. They include such characters
- as accents, diacritics, Hebrew points, Arabic vowel signs,
- and Indic matras.
- )
- )
- )
- $(P $(DEF Combining class)
- A numerical value used by the Unicode Canonical Ordering Algorithm
- to determine which sequences of combining marks are to be
- considered canonically equivalent and which are not.
- )
- $(P $(DEF Compatibility decomposition)
- The decomposition of a character or character sequence that results
- from recursively applying both the compatibility mappings and
- the canonical mappings found in the Unicode Character Database, and those
- described in Conjoining Jamo Behavior no characters
- can be further decomposed.
- )
- $(P $(DEF Compatibility equivalent)
- Two character sequences are said to be compatibility
- equivalents if their full compatibility decompositions are identical.
- )
- $(P $(DEF Encoded character) An association (or mapping)
- between an abstract character and a code point.
- )
- $(P $(DEF Glyph) The actual, concrete image of a glyph representation
- having been rasterized or otherwise imaged onto some display surface.
- )
- $(P $(DEF Grapheme base) A character with the property
- Grapheme_Base, or any standard Korean syllable block.
- )
- $(P $(DEF Grapheme cluster) Defined as the text between
- grapheme boundaries as specified by Unicode Standard Annex #29,
- $(HTTP www.unicode.org/reports/tr29/, Unicode text segmentation).
- Important general properties of a grapheme:
- $(UL
- $(LI The grapheme cluster represents a horizontally segmentable
- unit of text, consisting of some grapheme base (which may
- consist of a Korean syllable) together with any number of
- nonspacing marks applied to it.
- )
- $(LI A grapheme cluster typically starts with a grapheme base
- and then extends across any subsequent sequence of nonspacing marks.
- A grapheme cluster is most directly relevant to text rendering and
- processes such as cursor placement and text selection in editing,
- but may also be relevant to comparison and searching.
- )
- $(LI For many processes, a grapheme cluster behaves as if it was a
- single character with the same properties as its grapheme base.
- Effectively, nonspacing marks apply $(I graphically) to the base,
- but do not change its properties.
- )
- )
- $(P This module defines a number of primitives that work with graphemes:
- $(LREF Grapheme), $(LREF decodeGrapheme) and $(LREF graphemeStride).
- All of them are using $(I extended grapheme) boundaries
- as defined in the aforementioned standard annex.
- )
- )
- $(P $(DEF Nonspacing mark) A combining character with the
- General Category of Nonspacing Mark (Mn) or Enclosing Mark (Me).
- )
- $(P $(DEF Spacing mark) A combining character that is not a nonspacing mark.
- )
- $(SECTION Normalization
- )
- $(P The concepts of $(S_LINK Canonical equivalent, canonical equivalent)
- or $(S_LINK Compatibility equivalent, compatibility equivalent)
- characters in the Unicode Standard make it necessary to have a full, formal
- definition of equivalence for Unicode strings.
- String equivalence is determined by a process called normalization,
- whereby strings are converted into forms which are compared
- directly for identity. This is the primary goal of the normalization process,
- see the function $(LREF normalize) to convert into any of
- the four defined forms.
- )
- $(P A very important attribute of the Unicode Normalization Forms
- is that they must remain stable between versions of the Unicode Standard.
- A Unicode string normalized to a particular Unicode Normalization Form
- in one version of the standard is guaranteed to remain in that Normalization
- Form for implementations of future versions of the standard.
- )
- $(P The Unicode Standard specifies four normalization forms.
- Informally, two of these forms are defined by maximal decomposition
- of equivalent sequences, and two of these forms are defined
- by maximal $(I composition) of equivalent sequences.
- $(UL
- $(LI Normalization Form D (NFD): The $(S_LINK Canonical decomposition,
- canonical decomposition) of a character sequence.)
- $(LI Normalization Form KD (NFKD): The $(S_LINK Compatibility decomposition,
- compatibility decomposition) of a character sequence.)
- $(LI Normalization Form C (NFC): The canonical composition of the
- $(S_LINK Canonical decomposition, canonical decomposition)
- of a coded character sequence.)
- $(LI Normalization Form KC (NFKC): The canonical composition
- of the $(S_LINK Compatibility decomposition,
- compatibility decomposition) of a character sequence)
- )
- )
- $(P The choice of the normalization form depends on the particular use case.
- NFC is the best form for general text, since it's more compatible with
- strings converted from legacy encodings. NFKC is the preferred form for
- identifiers, especially where there are security concerns. NFD and NFKD
- are the most useful for internal processing.
- )
- $(SECTION Construction of lookup tables
- )
- $(P The Unicode standard describes a set of algorithms that
- depend on having the ability to quickly look up various properties
- of a code point. Given the the codespace of about 1 million $(CODEPOINTS),
- it is not a trivial task to provide a space-efficient solution for
- the multitude of properties.
- )
- $(P Common approaches such as hash-tables or binary search over
- sorted code point intervals (as in $(LREF InversionList)) are insufficient.
- Hash-tables have enormous memory footprint and binary search
- over intervals is not fast enough for some heavy-duty algorithms.
- )
- $(P The recommended solution (see Unicode Implementation Guidelines)
- is using multi-stage tables that are an implementation of the
- $(HTTP en.wikipedia.org/wiki/Trie, Trie) data structure with integer
- keys and a fixed number of stages. For the remainder of the section
- this will be called a fixed trie. The following describes a particular
- implementation that is aimed for the speed of access at the expense
- of ideal size savings.
- )
- $(P Taking a 2-level Trie as an example the principle of operation is as follows.
- Split the number of bits in a key (code point, 21 bits) into 2 components
- (e.g. 15 and 8). The first is the number of bits in the index of the trie
- and the other is number of bits in each page of the trie.
- The layout of the trie is then an array of size 2^^bits-of-index followed
- an array of memory chunks of size 2^^bits-of-page/bits-per-element.
- )
- $(P The number of pages is variable (but not less then 1)
- unlike the number of entries in the index. The slots of the index
- all have to contain a number of a page that is present. The lookup is then
- just a couple of operations - slice the upper bits,
- lookup an index for these, take a page at this index and use
- the lower bits as an offset within this page.
-
- Assuming that pages are laid out consequently
- in one array at $(D pages), the pseudo-code is:
- )
- ---
- auto elemsPerPage = (2 ^^ bits_per_page) / Value.sizeOfInBits;
- pages[index[n >> bits_per_page]][n & (elemsPerPage - 1)];
- ---
- $(P Where if $(D elemsPerPage) is a power of 2 the whole process is
- a handful of simple instructions and 2 array reads. Subsequent levels
- of the trie are introduced by recursing on this notion - the index array
- is treated as values. The number of bits in index is then again
- split into 2 parts, with pages over 'current-index' and the new 'upper-index'.
- )
-
- $(P For completeness a level 1 trie is simply an array.
- The current implementation takes advantage of bit-packing values
- when the range is known to be limited in advance (such as $(D bool)).
- See also $(LREF BitPacked) for enforcing it manually.
- The major size advantage however comes from the fact
- that multiple $(B identical pages on every level are merged) by construction.
- )
- $(P The process of constructing a trie is more involved and is hidden from
- the user in a form of the convenience functions $(LREF codepointTrie),
- $(LREF codepointSetTrie) and the even more convenient $(LREF toTrie).
- In general a set or built-in AA with $(D dchar) type
- can be turned into a trie. The trie object in this module
- is read-only (immutable); it's effectively frozen after construction.
- )
- $(SECTION Unicode properties
- )
- $(P This is a full list of Unicode properties accessible through $(LREF unicode)
- with specific helpers per category nested within. Consult the
- $(HTTP www.unicode.org/cldr/utility/properties.jsp, CLDR utility)
- when in doubt about the contents of a particular set.
- )
- $(P General category sets listed below are only accessible with the
- $(LREF unicode) shorthand accessor.)
- $(BOOKTABLE $(B General category ),
- $(TR $(TH Abb.) $(TH Long form)
- $(TH Abb.) $(TH Long form)$(TH Abb.) $(TH Long form))
- $(TR $(TD L) $(TD Letter)
- $(TD Cn) $(TD Unassigned) $(TD Po) $(TD Other_Punctuation))
- $(TR $(TD Ll) $(TD Lowercase_Letter)
- $(TD Co) $(TD Private_Use) $(TD Ps) $(TD Open_Punctuation))
- $(TR $(TD Lm) $(TD Modifier_Letter)
- $(TD Cs) $(TD Surrogate) $(TD S) $(TD Symbol))
- $(TR $(TD Lo) $(TD Other_Letter)
- $(TD N) $(TD Number) $(TD Sc) $(TD Currency_Symbol))
- $(TR $(TD Lt) $(TD Titlecase_Letter)
- $(TD Nd) $(TD Decimal_Number) $(TD Sk) $(TD Modifier_Symbol))
- $(TR $(TD Lu) $(TD Uppercase_Letter)
- $(TD Nl) $(TD Letter_Number) $(TD Sm) $(TD Math_Symbol))
- $(TR $(TD M) $(TD Mark)
- $(TD No) $(TD Other_Number) $(TD So) $(TD Other_Symbol))
- $(TR $(TD Mc) $(TD Spacing_Mark)
- $(TD P) $(TD Punctuation) $(TD Z) $(TD Separator))
- $(TR $(TD Me) $(TD Enclosing_Mark)
- $(TD Pc) $(TD Connector_Punctuation) $(TD Zl) $(TD Line_Separator))
- $(TR $(TD Mn) $(TD Nonspacing_Mark)
- $(TD Pd) $(TD Dash_Punctuation) $(TD Zp) $(TD Paragraph_Separator))
- $(TR $(TD C) $(TD Other)
- $(TD Pe) $(TD Close_Punctuation) $(TD Zs) $(TD Space_Separator))
- $(TR $(TD Cc) $(TD Control) $(TD Pf)
- $(TD Final_Punctuation) $(TD -) $(TD Any))
- $(TR $(TD Cf) $(TD Format)
- $(TD Pi) $(TD Initial_Punctuation) $(TD -) $(TD ASCII))
- )
- $(P Sets for other commonly useful properties that are
- accessible with $(LREF unicode):)
- $(BOOKTABLE $(B Common binary properties),
- $(TR $(TH Name) $(TH Name) $(TH Name))
- $(TR $(TD Alphabetic) $(TD Ideographic) $(TD Other_Uppercase))
- $(TR $(TD ASCII_Hex_Digit) $(TD IDS_Binary_Operator) $(TD Pattern_Syntax))
- $(TR $(TD Bidi_Control) $(TD ID_Start) $(TD Pattern_White_Space))
- $(TR $(TD Cased) $(TD IDS_Trinary_Operator) $(TD Quotation_Mark))
- $(TR $(TD Case_Ignorable) $(TD Join_Control) $(TD Radical))
- $(TR $(TD Dash) $(TD Logical_Order_Exception) $(TD Soft_Dotted))
- $(TR $(TD Default_Ignorable_Code_Point) $(TD Lowercase) $(TD STerm))
- $(TR $(TD Deprecated) $(TD Math) $(TD Terminal_Punctuation))
- $(TR $(TD Diacritic) $(TD Noncharacter_Code_Point) $(TD Unified_Ideograph))
- $(TR $(TD Extender) $(TD Other_Alphabetic) $(TD Uppercase))
- $(TR $(TD Grapheme_Base) $(TD Other_Default_Ignorable_Code_Point) $(TD Variation_Selector))
- $(TR $(TD Grapheme_Extend) $(TD Other_Grapheme_Extend) $(TD White_Space))
- $(TR $(TD Grapheme_Link) $(TD Other_ID_Continue) $(TD XID_Continue))
- $(TR $(TD Hex_Digit) $(TD Other_ID_Start) $(TD XID_Start))
- $(TR $(TD Hyphen) $(TD Other_Lowercase) )
- $(TR $(TD ID_Continue) $(TD Other_Math) )
- )
- $(P Below is the table with block names accepted by $(LREF unicode.block).
- Note that the shorthand version $(LREF unicode) requires "In"
- to be prepended to the names of blocks so as to disambiguate
- scripts and blocks.
- )
- $(BOOKTABLE $(B Blocks),
- $(TR $(TD Aegean Numbers) $(TD Ethiopic Extended) $(TD Mongolian))
- $(TR $(TD Alchemical Symbols) $(TD Ethiopic Extended-A) $(TD Musical Symbols))
- $(TR $(TD Alphabetic Presentation Forms) $(TD Ethiopic Supplement) $(TD Myanmar))
- $(TR $(TD Ancient Greek Musical Notation) $(TD General Punctuation) $(TD Myanmar Extended-A))
- $(TR $(TD Ancient Greek Numbers) $(TD Geometric Shapes) $(TD New Tai Lue))
- $(TR $(TD Ancient Symbols) $(TD Georgian) $(TD NKo))
- $(TR $(TD Arabic) $(TD Georgian Supplement) $(TD Number Forms))
- $(TR $(TD Arabic Extended-A) $(TD Glagolitic) $(TD Ogham))
- $(TR $(TD Arabic Mathematical Alphabetic Symbols) $(TD Gothic) $(TD Ol Chiki))
- $(TR $(TD Arabic Presentation Forms-A) $(TD Greek and Coptic) $(TD Old Italic))
- $(TR $(TD Arabic Presentation Forms-B) $(TD Greek Extended) $(TD Old Persian))
- $(TR $(TD Arabic Supplement) $(TD Gujarati) $(TD Old South Arabian))
- $(TR $(TD Armenian) $(TD Gurmukhi) $(TD Old Turkic))
- $(TR $(TD Arrows) $(TD Halfwidth and Fullwidth Forms) $(TD Optical Character Recognition))
- $(TR $(TD Avestan) $(TD Hangul Compatibility Jamo) $(TD Oriya))
- $(TR $(TD Balinese) $(TD Hangul Jamo) $(TD Osmanya))
- $(TR $(TD Bamum) $(TD Hangul Jamo Extended-A) $(TD Phags-pa))
- $(TR $(TD Bamum Supplement) $(TD Hangul Jamo Extended-B) $(TD Phaistos Disc))
- $(TR $(TD Basic Latin) $(TD Hangul Syllables) $(TD Phoenician))
- $(TR $(TD Batak) $(TD Hanunoo) $(TD Phonetic Extensions))
- $(TR $(TD Bengali) $(TD Hebrew) $(TD Phonetic Extensions Supplement))
- $(TR $(TD Block Elements) $(TD High Private Use Surrogates) $(TD Playing Cards))
- $(TR $(TD Bopomofo) $(TD High Surrogates) $(TD Private Use Area))
- $(TR $(TD Bopomofo Extended) $(TD Hiragana) $(TD Rejang))
- $(TR $(TD Box Drawing) $(TD Ideographic Description Characters) $(TD Rumi Numeral Symbols))
- $(TR $(TD Brahmi) $(TD Imperial Aramaic) $(TD Runic))
- $(TR $(TD Braille Patterns) $(TD Inscriptional Pahlavi) $(TD Samaritan))
- $(TR $(TD Buginese) $(TD Inscriptional Parthian) $(TD Saurashtra))
- $(TR $(TD Buhid) $(TD IPA Extensions) $(TD Sharada))
- $(TR $(TD Byzantine Musical Symbols) $(TD Javanese) $(TD Shavian))
- $(TR $(TD Carian) $(TD Kaithi) $(TD Sinhala))
- $(TR $(TD Chakma) $(TD Kana Supplement) $(TD Small Form Variants))
- $(TR $(TD Cham) $(TD Kanbun) $(TD Sora Sompeng))
- $(TR $(TD Cherokee) $(TD Kangxi Radicals) $(TD Spacing Modifier Letters))
- $(TR $(TD CJK Compatibility) $(TD Kannada) $(TD Specials))
- $(TR $(TD CJK Compatibility Forms) $(TD Katakana) $(TD Sundanese))
- $(TR $(TD CJK Compatibility Ideographs) $(TD Katakana Phonetic Extensions) $(TD Sundanese Supplement))
- $(TR $(TD CJK Compatibility Ideographs Supplement) $(TD Kayah Li) $(TD Superscripts and Subscripts))
- $(TR $(TD CJK Radicals Supplement) $(TD Kharoshthi) $(TD Supplemental Arrows-A))
- $(TR $(TD CJK Strokes) $(TD Khmer) $(TD Supplemental Arrows-B))
- $(TR $(TD CJK Symbols and Punctuation) $(TD Khmer Symbols) $(TD Supplemental Mathematical Operators))
- $(TR $(TD CJK Unified Ideographs) $(TD Lao) $(TD Supplemental Punctuation))
- $(TR $(TD CJK Unified Ideographs Extension A) $(TD Latin-1 Supplement) $(TD Supplementary Private Use Area-A))
- $(TR $(TD CJK Unified Ideographs Extension B) $(TD Latin Extended-A) $(TD Supplementary Private Use Area-B))
- $(TR $(TD CJK Unified Ideographs Extension C) $(TD Latin Extended Additional) $(TD Syloti Nagri))
- $(TR $(TD CJK Unified Ideographs Extension D) $(TD Latin Extended-B) $(TD Syriac))
- $(TR $(TD Combining Diacritical Marks) $(TD Latin Extended-C) $(TD Tagalog))
- $(TR $(TD Combining Diacritical Marks for Symbols) $(TD Latin Extended-D) $(TD Tagbanwa))
- $(TR $(TD Combining Diacritical Marks Supplement) $(TD Lepcha) $(TD Tags))
- $(TR $(TD Combining Half Marks) $(TD Letterlike Symbols) $(TD Tai Le))
- $(TR $(TD Common Indic Number Forms) $(TD Limbu) $(TD Tai Tham))
- $(TR $(TD Control Pictures) $(TD Linear B Ideograms) $(TD Tai Viet))
- $(TR $(TD Coptic) $(TD Linear B Syllabary) $(TD Tai Xuan Jing Symbols))
- $(TR $(TD Counting Rod Numerals) $(TD Lisu) $(TD Takri))
- $(TR $(TD Cuneiform) $(TD Low Surrogates) $(TD Tamil))
- $(TR $(TD Cuneiform Numbers and Punctuation) $(TD Lycian) $(TD Telugu))
- $(TR $(TD Currency Symbols) $(TD Lydian) $(TD Thaana))
- $(TR $(TD Cypriot Syllabary) $(TD Mahjong Tiles) $(TD Thai))
- $(TR $(TD Cyrillic) $(TD Malayalam) $(TD Tibetan))
- $(TR $(TD Cyrillic Extended-A) $(TD Mandaic) $(TD Tifinagh))
- $(TR $(TD Cyrillic Extended-B) $(TD Mathematical Alphanumeric Symbols) $(TD Transport And Map Symbols))
- $(TR $(TD Cyrillic Supplement) $(TD Mathematical Operators) $(TD Ugaritic))
- $(TR $(TD Deseret) $(TD Meetei Mayek) $(TD Unified Canadian Aboriginal Syllabics))
- $(TR $(TD Devanagari) $(TD Meetei Mayek Extensions) $(TD Unified Canadian Aboriginal Syllabics Extended))
- $(TR $(TD Devanagari Extended) $(TD Meroitic Cursive) $(TD Vai))
- $(TR $(TD Dingbats) $(TD Meroitic Hieroglyphs) $(TD Variation Selectors))
- $(TR $(TD Domino Tiles) $(TD Miao) $(TD Variation Selectors Supplement))
- $(TR $(TD Egyptian Hieroglyphs) $(TD Miscellaneous Mathematical Symbols-A) $(TD Vedic Extensions))
- $(TR $(TD Emoticons) $(TD Miscellaneous Mathematical Symbols-B) $(TD Vertical Forms))
- $(TR $(TD Enclosed Alphanumerics) $(TD Miscellaneous Symbols) $(TD Yijing Hexagram Symbols))
- $(TR $(TD Enclosed Alphanumeric Supplement) $(TD Miscellaneous Symbols and Arrows) $(TD Yi Radicals))
- $(TR $(TD Enclosed CJK Letters and Months) $(TD Miscellaneous Symbols And Pictographs) $(TD Yi Syllables))
- $(TR $(TD Enclosed Ideographic Supplement) $(TD Miscellaneous Technical) )
- $(TR $(TD Ethiopic) $(TD Modifier Tone Letters) )
- )
- $(P Below is the table with script names accepted by $(LREF unicode.script)
- and by the shorthand version $(LREF unicode):)
- $(BOOKTABLE $(B Scripts),
- $(TR $(TD Arabic) $(TD Hanunoo) $(TD Old_Italic))
- $(TR $(TD Armenian) $(TD Hebrew) $(TD Old_Persian))
- $(TR $(TD Avestan) $(TD Hiragana) $(TD Old_South_Arabian))
- $(TR $(TD Balinese) $(TD Imperial_Aramaic) $(TD Old_Turkic))
- $(TR $(TD Bamum) $(TD Inherited) $(TD Oriya))
- $(TR $(TD Batak) $(TD Inscriptional_Pahlavi) $(TD Osmanya))
- $(TR $(TD Bengali) $(TD Inscriptional_Parthian) $(TD Phags_Pa))
- $(TR $(TD Bopomofo) $(TD Javanese) $(TD Phoenician))
- $(TR $(TD Brahmi) $(TD Kaithi) $(TD Rejang))
- $(TR $(TD Braille) $(TD Kannada) $(TD Runic))
- $(TR $(TD Buginese) $(TD Katakana) $(TD Samaritan))
- $(TR $(TD Buhid) $(TD Kayah_Li) $(TD Saurashtra))
- $(TR $(TD Canadian_Aboriginal) $(TD Kharoshthi) $(TD Sharada))
- $(TR $(TD Carian) $(TD Khmer) $(TD Shavian))
- $(TR $(TD Chakma) $(TD Lao) $(TD Sinhala))
- $(TR $(TD Cham) $(TD Latin) $(TD Sora_Sompeng))
- $(TR $(TD Cherokee) $(TD Lepcha) $(TD Sundanese))
- $(TR $(TD Common) $(TD Limbu) $(TD Syloti_Nagri))
- $(TR $(TD Coptic) $(TD Linear_B) $(TD Syriac))
- $(TR $(TD Cuneiform) $(TD Lisu) $(TD Tagalog))
- $(TR $(TD Cypriot) $(TD Lycian) $(TD Tagbanwa))
- $(TR $(TD Cyrillic) $(TD Lydian) $(TD Tai_Le))
- $(TR $(TD Deseret) $(TD Malayalam) $(TD Tai_Tham))
- $(TR $(TD Devanagari) $(TD Mandaic) $(TD Tai_Viet))
- $(TR $(TD Egyptian_Hieroglyphs) $(TD Meetei_Mayek) $(TD Takri))
- $(TR $(TD Ethiopic) $(TD Meroitic_Cursive) $(TD Tamil))
- $(TR $(TD Georgian) $(TD Meroitic_Hieroglyphs) $(TD Telugu))
- $(TR $(TD Glagolitic) $(TD Miao) $(TD Thaana))
- $(TR $(TD Gothic) $(TD Mongolian) $(TD Thai))
- $(TR $(TD Greek) $(TD Myanmar) $(TD Tibetan))
- $(TR $(TD Gujarati) $(TD New_Tai_Lue) $(TD Tifinagh))
- $(TR $(TD Gurmukhi) $(TD Nko) $(TD Ugaritic))
- $(TR $(TD Han) $(TD Ogham) $(TD Vai))
- $(TR $(TD Hangul) $(TD Ol_Chiki) $(TD Yi))
- )
- $(P Below is the table of names accepted by $(LREF unicode.hangulSyllableType).)
- $(BOOKTABLE $(B Hangul syllable type),
- $(TR $(TH Abb.) $(TH Long form))
- $(TR $(TD L) $(TD Leading_Jamo))
- $(TR $(TD LV) $(TD LV_Syllable))
- $(TR $(TD LVT) $(TD LVT_Syllable) )
- $(TR $(TD T) $(TD Trailing_Jamo))
- $(TR $(TD V) $(TD Vowel_Jamo))
- )
- References:
- $(HTTP www.digitalmars.com/d/ascii-table.html, ASCII Table),
- $(HTTP en.wikipedia.org/wiki/Unicode, Wikipedia),
- $(HTTP www.unicode.org, The Unicode Consortium),
- $(HTTP www.unicode.org/reports/tr15/, Unicode normalization forms),
- $(HTTP www.unicode.org/reports/tr29/, Unicode text segmentation)
- $(HTTP www.unicode.org/uni2book/ch05.pdf,
- Unicode Implementation Guidelines)
- $(HTTP www.unicode.org/uni2book/ch03.pdf,
- Unicode Conformance)
- Trademarks:
- Unicode(tm) is a trademark of Unicode, Inc.
-
- Copyright: Copyright 2013 -
- License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: Dmitry Olshansky
- Source: $(PHOBOSSRC std/_uni.d)
- Standards: $(HTTP www.unicode.org/versions/Unicode6.2.0/, Unicode v6.2)
-
-Macros:
-
-SECTION = <h3><a id="$1">$0</a></h3>
-DEF = <div><a id="$1"><i>$0</i></a></div>
-S_LINK = <a href="#$1">$+</a>
-CODEPOINT = $(S_LINK Code point, code point)
-CODEPOINTS = $(S_LINK Code point, code points)
-CHARACTER = $(S_LINK Character, character)
-CHARACTERS = $(S_LINK Character, characters)
-CLUSTER = $(S_LINK Grapheme cluster, grapheme cluster)
-+/
-module std.uni;
-
-import std.meta; // AliasSeq
-import std.range.primitives; // back, ElementEncodingType, ElementType, empty,
- // front, isForwardRange, isInputRange, isRandomAccessRange, popFront, put,
- // save
-import std.traits; // isConvertibleToString, isIntegral, isSomeChar,
- // isSomeString, Unqual
-import std.exception;// : enforce;
-import core.memory; //: pureMalloc, pureRealloc, pureFree;
-import core.exception; // : onOutOfMemoryError;
-static import std.ascii;
-// debug = std_uni;
-
-debug(std_uni) import std.stdio; // writefln, writeln
-
-private:
-
-version (unittest)
-{
-private:
- struct TestAliasedString
- {
- string get() @safe @nogc pure nothrow { return _s; }
- alias get this;
- @disable this(this);
- string _s;
- }
-
- bool testAliasedString(alias func, Args...)(string s, Args args)
- {
- import std.algorithm.comparison : equal;
- auto a = func(TestAliasedString(s), args);
- auto b = func(s, args);
- static if (is(typeof(equal(a, b))))
- {
- // For ranges, compare contents instead of object identity.
- return equal(a, b);
- }
- else
- {
- return a == b;
- }
- }
-}
-
-void copyBackwards(T,U)(T[] src, U[] dest)
-{
- assert(src.length == dest.length);
- for (size_t i=src.length; i-- > 0; )
- dest[i] = src[i];
-}
-
-void copyForward(T,U)(T[] src, U[] dest)
-{
- assert(src.length == dest.length);
- for (size_t i=0; i<src.length; i++)
- dest[i] = src[i];
-}
-
-// TODO: update to reflect all major CPUs supporting unaligned reads
-version (X86)
- enum hasUnalignedReads = true;
-else version (X86_64)
- enum hasUnalignedReads = true;
-else version (SystemZ)
- enum hasUnalignedReads = true;
-else
- enum hasUnalignedReads = false; // better be safe then sorry
-
-public enum dchar lineSep = '\u2028'; /// Constant $(CODEPOINT) (0x2028) - line separator.
-public enum dchar paraSep = '\u2029'; /// Constant $(CODEPOINT) (0x2029) - paragraph separator.
-public enum dchar nelSep = '\u0085'; /// Constant $(CODEPOINT) (0x0085) - next line.
-
-// test the intro example
-@safe unittest
-{
- import std.algorithm.searching : find;
- // initialize code point sets using script/block or property name
- // set contains code points from both scripts.
- auto set = unicode("Cyrillic") | unicode("Armenian");
- // or simpler and statically-checked look
- auto ascii = unicode.ASCII;
- auto currency = unicode.Currency_Symbol;
-
- // easy set ops
- auto a = set & ascii;
- assert(a.empty); // as it has no intersection with ascii
- a = set | ascii;
- auto b = currency - a; // subtract all ASCII, Cyrillic and Armenian
-
- // some properties of code point sets
- assert(b.length > 45); // 46 items in Unicode 6.1, even more in 6.2
- // testing presence of a code point in a set
- // is just fine, it is O(logN)
- assert(!b['$']);
- assert(!b['\u058F']); // Armenian dram sign
- assert(b['Â¥']);
-
- // building fast lookup tables, these guarantee O(1) complexity
- // 1-level Trie lookup table essentially a huge bit-set ~262Kb
- auto oneTrie = toTrie!1(b);
- // 2-level far more compact but typically slightly slower
- auto twoTrie = toTrie!2(b);
- // 3-level even smaller, and a bit slower yet
- auto threeTrie = toTrie!3(b);
- assert(oneTrie['£']);
- assert(twoTrie['£']);
- assert(threeTrie['£']);
-
- // build the trie with the most sensible trie level
- // and bind it as a functor
- auto cyrillicOrArmenian = toDelegate(set);
- auto balance = find!(cyrillicOrArmenian)("Hello Õ¨Õ¶Õ¯Õ¥Ö€!");
- assert(balance == "Õ¨Õ¶Õ¯Õ¥Ö€!");
- // compatible with bool delegate(dchar)
- bool delegate(dchar) bindIt = cyrillicOrArmenian;
-
- // Normalization
- string s = "Plain ascii (and not only), is always normalized!";
- assert(s is normalize(s));// is the same string
-
- string nonS = "A\u0308ffin"; // A ligature
- auto nS = normalize(nonS); // to NFC, the W3C endorsed standard
- assert(nS == "Äffin");
- assert(nS != nonS);
- string composed = "Äffin";
-
- assert(normalize!NFD(composed) == "A\u0308ffin");
- // to NFKD, compatibility decomposition useful for fuzzy matching/searching
- assert(normalize!NFKD("2¹â°") == "210");
-}
-
-enum lastDchar = 0x10FFFF;
-
-auto force(T, F)(F from)
-if (isIntegral!T && !is(T == F))
-{
- assert(from <= T.max && from >= T.min);
- return cast(T) from;
-}
-
-auto force(T, F)(F from)
-if (isBitPacked!T && !is(T == F))
-{
- assert(from <= 2^^bitSizeOf!T-1);
- return T(cast(TypeOfBitPacked!T) from);
-}
-
-auto force(T, F)(F from)
-if (is(T == F))
-{
- return from;
-}
-
-// repeat X times the bit-pattern in val assuming it's length is 'bits'
-size_t replicateBits(size_t times, size_t bits)(size_t val) @safe pure nothrow @nogc
-{
- static if (times == 1)
- return val;
- else static if (bits == 1)
- {
- static if (times == size_t.sizeof*8)
- return val ? size_t.max : 0;
- else
- return val ? (1 << times)-1 : 0;
- }
- else static if (times % 2)
- return (replicateBits!(times-1, bits)(val)<<bits) | val;
- else
- return replicateBits!(times/2, bits*2)((val << bits) | val);
-}
-
-@safe pure nothrow @nogc unittest // for replicate
-{
- import std.algorithm.iteration : sum, map;
- import std.range : iota;
- size_t m = 0b111;
- size_t m2 = 0b01;
- foreach (i; AliasSeq!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
- {
- assert(replicateBits!(i, 3)(m)+1 == (1<<(3*i)));
- assert(replicateBits!(i, 2)(m2) == iota(0, i).map!"2^^(2*a)"().sum());
- }
-}
-
-// multiple arrays squashed into one memory block
-struct MultiArray(Types...)
-{
- import std.range.primitives : isOutputRange;
- this(size_t[] sizes...) @safe pure nothrow
- {
- assert(dim == sizes.length);
- size_t full_size;
- foreach (i, v; Types)
- {
- full_size += spaceFor!(bitSizeOf!v)(sizes[i]);
- sz[i] = sizes[i];
- static if (i >= 1)
- offsets[i] = offsets[i-1] +
- spaceFor!(bitSizeOf!(Types[i-1]))(sizes[i-1]);
- }
-
- storage = new size_t[full_size];
- }
-
- this(const(size_t)[] raw_offsets,
- const(size_t)[] raw_sizes, const(size_t)[] data)const @safe pure nothrow @nogc
- {
- offsets[] = raw_offsets[];
- sz[] = raw_sizes[];
- storage = data;
- }
-
- @property auto slice(size_t n)()inout pure nothrow @nogc
- {
- auto ptr = raw_ptr!n;
- return packedArrayView!(Types[n])(ptr, sz[n]);
- }
-
- @property auto ptr(size_t n)()inout pure nothrow @nogc
- {
- auto ptr = raw_ptr!n;
- return inout(PackedPtr!(Types[n]))(ptr);
- }
-
- template length(size_t n)
- {
- @property size_t length()const @safe pure nothrow @nogc{ return sz[n]; }
-
- @property void length(size_t new_size)
- {
- if (new_size > sz[n])
- {// extend
- size_t delta = (new_size - sz[n]);
- sz[n] += delta;
- delta = spaceFor!(bitSizeOf!(Types[n]))(delta);
- storage.length += delta;// extend space at end
- // raw_slice!x must follow resize as it could be moved!
- // next stmts move all data past this array, last-one-goes-first
- static if (n != dim-1)
- {
- auto start = raw_ptr!(n+1);
- // len includes delta
- size_t len = (storage.ptr+storage.length-start);
-
- copyBackwards(start[0 .. len-delta], start[delta .. len]);
-
- start[0 .. delta] = 0;
- // offsets are used for raw_slice, ptr etc.
- foreach (i; n+1 .. dim)
- offsets[i] += delta;
- }
- }
- else if (new_size < sz[n])
- {// shrink
- size_t delta = (sz[n] - new_size);
- sz[n] -= delta;
- delta = spaceFor!(bitSizeOf!(Types[n]))(delta);
- // move all data past this array, forward direction
- static if (n != dim-1)
- {
- auto start = raw_ptr!(n+1);
- size_t len = (storage.ptr+storage.length-start);
- copyForward(start[0 .. len-delta], start[delta .. len]);
-
- // adjust offsets last, they affect raw_slice
- foreach (i; n+1 .. dim)
- offsets[i] -= delta;
- }
- storage.length -= delta;
- }
- // else - NOP
- }
- }
-
- @property size_t bytes(size_t n=size_t.max)() const @safe
- {
- static if (n == size_t.max)
- return storage.length*size_t.sizeof;
- else static if (n != Types.length-1)
- return (raw_ptr!(n+1)-raw_ptr!n)*size_t.sizeof;
- else
- return (storage.ptr+storage.length - raw_ptr!n)*size_t.sizeof;
- }
-
- void store(OutRange)(scope OutRange sink) const
- if (isOutputRange!(OutRange, char))
- {
- import std.format : formattedWrite;
- formattedWrite(sink, "[%( 0x%x, %)]", offsets[]);
- formattedWrite(sink, ", [%( 0x%x, %)]", sz[]);
- formattedWrite(sink, ", [%( 0x%x, %)]", storage);
- }
-
-private:
- import std.meta : staticMap;
- @property auto raw_ptr(size_t n)()inout pure nothrow @nogc
- {
- static if (n == 0)
- return storage.ptr;
- else
- {
- return storage.ptr+offsets[n];
- }
- }
- enum dim = Types.length;
- size_t[dim] offsets;// offset for level x
- size_t[dim] sz;// size of level x
- alias bitWidth = staticMap!(bitSizeOf, Types);
- size_t[] storage;
-}
-
-@system unittest
-{
- import std.conv : text;
- enum dg = (){
- // sizes are:
- // lvl0: 3, lvl1 : 2, lvl2: 1
- auto m = MultiArray!(int, ubyte, int)(3,2,1);
-
- static void check(size_t k, T)(ref T m, int n)
- {
- foreach (i; 0 .. n)
- assert(m.slice!(k)[i] == i+1, text("level:",i," : ",m.slice!(k)[0 .. n]));
- }
-
- static void checkB(size_t k, T)(ref T m, int n)
- {
- foreach (i; 0 .. n)
- assert(m.slice!(k)[i] == n-i, text("level:",i," : ",m.slice!(k)[0 .. n]));
- }
-
- static void fill(size_t k, T)(ref T m, int n)
- {
- foreach (i; 0 .. n)
- m.slice!(k)[i] = force!ubyte(i+1);
- }
-
- static void fillB(size_t k, T)(ref T m, int n)
- {
- foreach (i; 0 .. n)
- m.slice!(k)[i] = force!ubyte(n-i);
- }
-
- m.length!1 = 100;
- fill!1(m, 100);
- check!1(m, 100);
-
- m.length!0 = 220;
- fill!0(m, 220);
- check!1(m, 100);
- check!0(m, 220);
-
- m.length!2 = 17;
- fillB!2(m, 17);
- checkB!2(m, 17);
- check!0(m, 220);
- check!1(m, 100);
-
- m.length!2 = 33;
- checkB!2(m, 17);
- fillB!2(m, 33);
- checkB!2(m, 33);
- check!0(m, 220);
- check!1(m, 100);
-
- m.length!1 = 195;
- fillB!1(m, 195);
- checkB!1(m, 195);
- checkB!2(m, 33);
- check!0(m, 220);
-
- auto marr = MultiArray!(BitPacked!(uint, 4), BitPacked!(uint, 6))(20, 10);
- marr.length!0 = 15;
- marr.length!1 = 30;
- fill!1(marr, 30);
- fill!0(marr, 15);
- check!1(marr, 30);
- check!0(marr, 15);
- return 0;
- };
- enum ct = dg();
- auto rt = dg();
-}
-
-@system unittest
-{// more bitpacking tests
- import std.conv : text;
-
- alias Bitty =
- MultiArray!(BitPacked!(size_t, 3)
- , BitPacked!(size_t, 4)
- , BitPacked!(size_t, 3)
- , BitPacked!(size_t, 6)
- , bool);
- alias fn1 = sliceBits!(13, 16);
- alias fn2 = sliceBits!( 9, 13);
- alias fn3 = sliceBits!( 6, 9);
- alias fn4 = sliceBits!( 0, 6);
- static void check(size_t lvl, MA)(ref MA arr){
- for (size_t i = 0; i< arr.length!lvl; i++)
- assert(arr.slice!(lvl)[i] == i, text("Mismatch on lvl ", lvl, " idx ", i, " value: ", arr.slice!(lvl)[i]));
- }
-
- static void fillIdx(size_t lvl, MA)(ref MA arr){
- for (size_t i = 0; i< arr.length!lvl; i++)
- arr.slice!(lvl)[i] = i;
- }
- Bitty m1;
-
- m1.length!4 = 10;
- m1.length!3 = 2^^6;
- m1.length!2 = 2^^3;
- m1.length!1 = 2^^4;
- m1.length!0 = 2^^3;
-
- m1.length!4 = 2^^16;
-
- for (size_t i = 0; i< m1.length!4; i++)
- m1.slice!(4)[i] = i % 2;
-
- fillIdx!1(m1);
- check!1(m1);
- fillIdx!2(m1);
- check!2(m1);
- fillIdx!3(m1);
- check!3(m1);
- fillIdx!0(m1);
- check!0(m1);
- check!3(m1);
- check!2(m1);
- check!1(m1);
- for (size_t i=0; i < 2^^16; i++)
- {
- m1.slice!(4)[i] = i % 2;
- m1.slice!(0)[fn1(i)] = fn1(i);
- m1.slice!(1)[fn2(i)] = fn2(i);
- m1.slice!(2)[fn3(i)] = fn3(i);
- m1.slice!(3)[fn4(i)] = fn4(i);
- }
- for (size_t i=0; i < 2^^16; i++)
- {
- assert(m1.slice!(4)[i] == i % 2);
- assert(m1.slice!(0)[fn1(i)] == fn1(i));
- assert(m1.slice!(1)[fn2(i)] == fn2(i));
- assert(m1.slice!(2)[fn3(i)] == fn3(i));
- assert(m1.slice!(3)[fn4(i)] == fn4(i));
- }
-}
-
-size_t spaceFor(size_t _bits)(size_t new_len) @safe pure nothrow @nogc
-{
- import std.math : nextPow2;
- enum bits = _bits == 1 ? 1 : nextPow2(_bits - 1);// see PackedArrayView
- static if (bits > 8*size_t.sizeof)
- {
- static assert(bits % (size_t.sizeof*8) == 0);
- return new_len * bits/(8*size_t.sizeof);
- }
- else
- {
- enum factor = size_t.sizeof*8/bits;
- return (new_len+factor-1)/factor; // rounded up
- }
-}
-
-template isBitPackableType(T)
-{
- enum isBitPackableType = isBitPacked!T
- || isIntegral!T || is(T == bool) || isSomeChar!T;
-}
-
-//============================================================================
-template PackedArrayView(T)
-if ((is(T dummy == BitPacked!(U, sz), U, size_t sz)
- && isBitPackableType!U) || isBitPackableType!T)
-{
- import std.math : nextPow2;
- private enum bits = bitSizeOf!T;
- alias PackedArrayView = PackedArrayViewImpl!(T, bits > 1 ? nextPow2(bits - 1) : 1);
-}
-
-//unsafe and fast access to a chunk of RAM as if it contains packed values
-template PackedPtr(T)
-if ((is(T dummy == BitPacked!(U, sz), U, size_t sz)
- && isBitPackableType!U) || isBitPackableType!T)
-{
- import std.math : nextPow2;
- private enum bits = bitSizeOf!T;
- alias PackedPtr = PackedPtrImpl!(T, bits > 1 ? nextPow2(bits - 1) : 1);
-}
-
-struct PackedPtrImpl(T, size_t bits)
-{
-pure nothrow:
- static assert(isPow2OrZero(bits));
-
- this(inout(size_t)* ptr)inout @safe @nogc
- {
- origin = ptr;
- }
-
- private T simpleIndex(size_t n) inout
- {
- immutable q = n / factor;
- immutable r = n % factor;
- return cast(T)((origin[q] >> bits*r) & mask);
- }
-
- private void simpleWrite(TypeOfBitPacked!T val, size_t n)
- in
- {
- static if (isIntegral!T)
- assert(val <= mask);
- }
- body
- {
- immutable q = n / factor;
- immutable r = n % factor;
- immutable tgt_shift = bits*r;
- immutable word = origin[q];
- origin[q] = (word & ~(mask << tgt_shift))
- | (cast(size_t) val << tgt_shift);
- }
-
- static if (factor == bytesPerWord// can safely pack by byte
- || factor == 1 // a whole word at a time
- || ((factor == bytesPerWord/2 || factor == bytesPerWord/4)
- && hasUnalignedReads)) // this needs unaligned reads
- {
- static if (factor == bytesPerWord)
- alias U = ubyte;
- else static if (factor == bytesPerWord/2)
- alias U = ushort;
- else static if (factor == bytesPerWord/4)
- alias U = uint;
- else static if (size_t.sizeof == 8 && factor == bytesPerWord/8)
- alias U = ulong;
-
- T opIndex(size_t idx) inout
- {
- T ret;
- version (LittleEndian)
- ret = __ctfe ? simpleIndex(idx) :
- cast(inout(T))(cast(U*) origin)[idx];
- else
- ret = simpleIndex(idx);
- return ret;
- }
-
- static if (isBitPacked!T) // lack of user-defined implicit conversion
- {
- void opIndexAssign(T val, size_t idx)
- {
- return opIndexAssign(cast(TypeOfBitPacked!T) val, idx);
- }
- }
-
- void opIndexAssign(TypeOfBitPacked!T val, size_t idx)
- {
- version (LittleEndian)
- {
- if (__ctfe)
- simpleWrite(val, idx);
- else
- (cast(U*) origin)[idx] = cast(U) val;
- }
- else
- simpleWrite(val, idx);
- }
- }
- else
- {
- T opIndex(size_t n) inout
- {
- return simpleIndex(n);
- }
-
- static if (isBitPacked!T) // lack of user-defined implicit conversion
- {
- void opIndexAssign(T val, size_t idx)
- {
- return opIndexAssign(cast(TypeOfBitPacked!T) val, idx);
- }
- }
-
- void opIndexAssign(TypeOfBitPacked!T val, size_t n)
- {
- return simpleWrite(val, n);
- }
- }
-
-private:
- // factor - number of elements in one machine word
- enum factor = size_t.sizeof*8/bits, mask = 2^^bits-1;
- enum bytesPerWord = size_t.sizeof;
- size_t* origin;
-}
-
-// data is packed only by power of two sized packs per word,
-// thus avoiding mul/div overhead at the cost of ultimate packing
-// this construct doesn't own memory, only provides access, see MultiArray for usage
-struct PackedArrayViewImpl(T, size_t bits)
-{
-pure nothrow:
-
- this(inout(size_t)* origin, size_t offset, size_t items) inout @safe
- {
- ptr = inout(PackedPtr!(T))(origin);
- ofs = offset;
- limit = items;
- }
-
- bool zeros(size_t s, size_t e)
- in
- {
- assert(s <= e);
- }
- body
- {
- s += ofs;
- e += ofs;
- immutable pad_s = roundUp(s);
- if ( s >= e)
- {
- foreach (i; s .. e)
- if (ptr[i])
- return false;
- return true;
- }
- immutable pad_e = roundDown(e);
- size_t i;
- for (i=s; i<pad_s; i++)
- if (ptr[i])
- return false;
- // all in between is x*factor elements
- for (size_t j=i/factor; i<pad_e; i+=factor, j++)
- if (ptr.origin[j])
- return false;
- for (; i<e; i++)
- if (ptr[i])
- return false;
- return true;
- }
-
- T opIndex(size_t idx) inout
- in
- {
- assert(idx < limit);
- }
- body
- {
- return ptr[ofs + idx];
- }
-
- static if (isBitPacked!T) // lack of user-defined implicit conversion
- {
- void opIndexAssign(T val, size_t idx)
- {
- return opIndexAssign(cast(TypeOfBitPacked!T) val, idx);
- }
- }
-
- void opIndexAssign(TypeOfBitPacked!T val, size_t idx)
- in
- {
- assert(idx < limit);
- }
- body
- {
- ptr[ofs + idx] = val;
- }
-
- static if (isBitPacked!T) // lack of user-defined implicit conversions
- {
- void opSliceAssign(T val, size_t start, size_t end)
- {
- opSliceAssign(cast(TypeOfBitPacked!T) val, start, end);
- }
- }
-
- void opSliceAssign(TypeOfBitPacked!T val, size_t start, size_t end)
- in
- {
- assert(start <= end);
- assert(end <= limit);
- }
- body
- {
- // account for ofsetted view
- start += ofs;
- end += ofs;
- // rounded to factor granularity
- immutable pad_start = roundUp(start);// rounded up
- if (pad_start >= end) //rounded up >= then end of slice
- {
- //nothing to gain, use per element assignment
- foreach (i; start .. end)
- ptr[i] = val;
- return;
- }
- immutable pad_end = roundDown(end); // rounded down
- size_t i;
- for (i=start; i<pad_start; i++)
- ptr[i] = val;
- // all in between is x*factor elements
- if (pad_start != pad_end)
- {
- immutable repval = replicateBits!(factor, bits)(val);
- for (size_t j=i/factor; i<pad_end; i+=factor, j++)
- ptr.origin[j] = repval;// so speed it up by factor
- }
- for (; i<end; i++)
- ptr[i] = val;
- }
-
- auto opSlice(size_t from, size_t to)inout
- in
- {
- assert(from <= to);
- assert(ofs + to <= limit);
- }
- body
- {
- return typeof(this)(ptr.origin, ofs + from, to - from);
- }
-
- auto opSlice(){ return opSlice(0, length); }
-
- bool opEquals(T)(auto ref T arr) const
- {
- if (limit != arr.limit)
- return false;
- size_t s1 = ofs, s2 = arr.ofs;
- size_t e1 = s1 + limit, e2 = s2 + limit;
- if (s1 % factor == 0 && s2 % factor == 0 && length % factor == 0)
- {
- return ptr.origin[s1/factor .. e1/factor]
- == arr.ptr.origin[s2/factor .. e2/factor];
- }
- for (size_t i=0;i<limit; i++)
- if (this[i] != arr[i])
- return false;
- return true;
- }
-
- @property size_t length()const{ return limit; }
-
-private:
- auto roundUp()(size_t val){ return (val+factor-1)/factor*factor; }
- auto roundDown()(size_t val){ return val/factor*factor; }
- // factor - number of elements in one machine word
- enum factor = size_t.sizeof*8/bits;
- PackedPtr!(T) ptr;
- size_t ofs, limit;
-}
-
-
-private struct SliceOverIndexed(T)
-{
- enum assignableIndex = is(typeof((){ T.init[0] = Item.init; }));
- enum assignableSlice = is(typeof((){ T.init[0 .. 0] = Item.init; }));
- auto opIndex(size_t idx)const
- in
- {
- assert(idx < to - from);
- }
- body
- {
- return (*arr)[from+idx];
- }
-
- static if (assignableIndex)
- void opIndexAssign(Item val, size_t idx)
- in
- {
- assert(idx < to - from);
- }
- body
- {
- (*arr)[from+idx] = val;
- }
-
- auto opSlice(size_t a, size_t b)
- {
- return typeof(this)(from+a, from+b, arr);
- }
-
- // static if (assignableSlice)
- void opSliceAssign(T)(T val, size_t start, size_t end)
- {
- (*arr)[start+from .. end+from] = val;
- }
-
- auto opSlice()
- {
- return typeof(this)(from, to, arr);
- }
-
- @property size_t length()const { return to-from;}
-
- auto opDollar()const { return length; }
-
- @property bool empty()const { return from == to; }
-
- @property auto front()const { return (*arr)[from]; }
-
- static if (assignableIndex)
- @property void front(Item val) { (*arr)[from] = val; }
-
- @property auto back()const { return (*arr)[to-1]; }
-
- static if (assignableIndex)
- @property void back(Item val) { (*arr)[to-1] = val; }
-
- @property auto save() inout { return this; }
-
- void popFront() { from++; }
-
- void popBack() { to--; }
-
- bool opEquals(T)(auto ref T arr) const
- {
- if (arr.length != length)
- return false;
- for (size_t i=0; i <length; i++)
- if (this[i] != arr[i])
- return false;
- return true;
- }
-private:
- alias Item = typeof(T.init[0]);
- size_t from, to;
- T* arr;
-}
-
-static assert(isRandomAccessRange!(SliceOverIndexed!(int[])));
-
-SliceOverIndexed!(const(T)) sliceOverIndexed(T)(size_t a, size_t b, const(T)* x)
-if (is(Unqual!T == T))
-{
- return SliceOverIndexed!(const(T))(a, b, x);
-}
-
-// BUG? inout is out of reach
-//...SliceOverIndexed.arr only parameters or stack based variables can be inout
-SliceOverIndexed!T sliceOverIndexed(T)(size_t a, size_t b, T* x)
-if (is(Unqual!T == T))
-{
- return SliceOverIndexed!T(a, b, x);
-}
-
-@system unittest
-{
- int[] idxArray = [2, 3, 5, 8, 13];
- auto sliced = sliceOverIndexed(0, idxArray.length, &idxArray);
-
- assert(!sliced.empty);
- assert(sliced.front == 2);
- sliced.front = 1;
- assert(sliced.front == 1);
- assert(sliced.back == 13);
- sliced.popFront();
- assert(sliced.front == 3);
- assert(sliced.back == 13);
- sliced.back = 11;
- assert(sliced.back == 11);
- sliced.popBack();
-
- assert(sliced.front == 3);
- assert(sliced[$-1] == 8);
- sliced = sliced[];
- assert(sliced[0] == 3);
- assert(sliced.back == 8);
- sliced = sliced[1..$];
- assert(sliced.front == 5);
- sliced = sliced[0..$-1];
- assert(sliced[$-1] == 5);
-
- int[] other = [2, 5];
- assert(sliced[] == sliceOverIndexed(1, 2, &other));
- sliceOverIndexed(0, 2, &idxArray)[0 .. 2] = -1;
- assert(idxArray[0 .. 2] == [-1, -1]);
- uint[] nullArr = null;
- auto nullSlice = sliceOverIndexed(0, 0, &idxArray);
- assert(nullSlice.empty);
-}
-
-private auto packedArrayView(T)(inout(size_t)* ptr, size_t items) @trusted pure nothrow
-{
- return inout(PackedArrayView!T)(ptr, 0, items);
-}
-
-
-//============================================================================
-// Partially unrolled binary search using Shar's method
-//============================================================================
-
-string genUnrolledSwitchSearch(size_t size) @safe pure nothrow
-{
- import core.bitop : bsr;
- import std.array : replace;
- import std.conv : to;
- assert(isPow2OrZero(size));
- string code = `
- import core.bitop : bsr;
- auto power = bsr(m)+1;
- switch (power){`;
- size_t i = bsr(size);
- foreach_reverse (val; 0 .. bsr(size))
- {
- auto v = 2^^val;
- code ~= `
- case pow:
- if (pred(range[idx+m], needle))
- idx += m;
- goto case;
- `.replace("m", to!string(v))
- .replace("pow", to!string(i));
- i--;
- }
- code ~= `
- case 0:
- if (pred(range[idx], needle))
- idx += 1;
- goto default;
- `;
- code ~= `
- default:
- }`;
- return code;
-}
-
-bool isPow2OrZero(size_t sz) @safe pure nothrow @nogc
-{
- // See also: std.math.isPowerOf2()
- return (sz & (sz-1)) == 0;
-}
-
-size_t uniformLowerBound(alias pred, Range, T)(Range range, T needle)
-if (is(T : ElementType!Range))
-{
- assert(isPow2OrZero(range.length));
- size_t idx = 0, m = range.length/2;
- while (m != 0)
- {
- if (pred(range[idx+m], needle))
- idx += m;
- m /= 2;
- }
- if (pred(range[idx], needle))
- idx += 1;
- return idx;
-}
-
-size_t switchUniformLowerBound(alias pred, Range, T)(Range range, T needle)
-if (is(T : ElementType!Range))
-{
- assert(isPow2OrZero(range.length));
- size_t idx = 0, m = range.length/2;
- enum max = 1 << 10;
- while (m >= max)
- {
- if (pred(range[idx+m], needle))
- idx += m;
- m /= 2;
- }
- mixin(genUnrolledSwitchSearch(max));
- return idx;
-}
-
-template sharMethod(alias uniLowerBound)
-{
- size_t sharMethod(alias _pred="a<b", Range, T)(Range range, T needle)
- if (is(T : ElementType!Range))
- {
- import std.functional : binaryFun;
- import std.math : nextPow2, truncPow2;
- alias pred = binaryFun!_pred;
- if (range.length == 0)
- return 0;
- if (isPow2OrZero(range.length))
- return uniLowerBound!pred(range, needle);
- size_t n = truncPow2(range.length);
- if (pred(range[n-1], needle))
- {// search in another 2^^k area that fully covers the tail of range
- size_t k = nextPow2(range.length - n + 1);
- return range.length - k + uniLowerBound!pred(range[$-k..$], needle);
- }
- else
- return uniLowerBound!pred(range[0 .. n], needle);
- }
-}
-
-alias sharLowerBound = sharMethod!uniformLowerBound;
-alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound;
-
-@safe unittest
-{
- import std.array : array;
- import std.range : assumeSorted, iota;
-
- auto stdLowerBound(T)(T[] range, T needle)
- {
- return assumeSorted(range).lowerBound(needle).length;
- }
- immutable MAX = 5*1173;
- auto arr = array(iota(5, MAX, 5));
- assert(arr.length == MAX/5-1);
- foreach (i; 0 .. MAX+5)
- {
- auto st = stdLowerBound(arr, i);
- assert(st == sharLowerBound(arr, i));
- assert(st == sharSwitchLowerBound(arr, i));
- }
- arr = [];
- auto st = stdLowerBound(arr, 33);
- assert(st == sharLowerBound(arr, 33));
- assert(st == sharSwitchLowerBound(arr, 33));
-}
-//============================================================================
-
-@safe
-{
-// hope to see simillar stuff in public interface... once Allocators are out
-//@@@BUG moveFront and friends? dunno, for now it's POD-only
-
-@trusted size_t genericReplace(Policy=void, T, Range)
- (ref T dest, size_t from, size_t to, Range stuff)
-{
- import std.algorithm.mutation : copy;
- size_t delta = to - from;
- size_t stuff_end = from+stuff.length;
- if (stuff.length > delta)
- {// replace increases length
- delta = stuff.length - delta;// now, new is > old by delta
- static if (is(Policy == void))
- dest.length = dest.length+delta;//@@@BUG lame @property
- else
- dest = Policy.realloc(dest, dest.length+delta);
- copyBackwards(dest[to .. dest.length-delta],
- dest[to+delta .. dest.length]);
- copyForward(stuff, dest[from .. stuff_end]);
- }
- else if (stuff.length == delta)
- {
- copy(stuff, dest[from .. to]);
- }
- else
- {// replace decreases length by delta
- delta = delta - stuff.length;
- copy(stuff, dest[from .. stuff_end]);
- copyForward(dest[to .. dest.length],
- dest[stuff_end .. dest.length-delta]);
- static if (is(Policy == void))
- dest.length = dest.length - delta;//@@@BUG lame @property
- else
- dest = Policy.realloc(dest, dest.length-delta);
- }
- return stuff_end;
-}
-
-
-// Simple storage manipulation policy
-@trusted private struct GcPolicy
-{
- import std.traits : isDynamicArray;
-
- static T[] dup(T)(const T[] arr)
- {
- return arr.dup;
- }
-
- static T[] alloc(T)(size_t size)
- {
- return new T[size];
- }
-
- static T[] realloc(T)(T[] arr, size_t sz)
- {
- arr.length = sz;
- return arr;
- }
-
- static void replaceImpl(T, Range)(ref T[] dest, size_t from, size_t to, Range stuff)
- {
- replaceInPlace(dest, from, to, stuff);
- }
-
- static void append(T, V)(ref T[] arr, V value)
- if (!isInputRange!V)
- {
- arr ~= force!T(value);
- }
-
- static void append(T, V)(ref T[] arr, V value)
- if (isInputRange!V)
- {
- insertInPlace(arr, arr.length, value);
- }
-
- static void destroy(T)(ref T arr)
- if (isDynamicArray!T && is(Unqual!T == T))
- {
- debug
- {
- arr[] = cast(typeof(T.init[0]))(0xdead_beef);
- }
- arr = null;
- }
-
- static void destroy(T)(ref T arr)
- if (isDynamicArray!T && !is(Unqual!T == T))
- {
- arr = null;
- }
-}
-
-// ditto
-@trusted struct ReallocPolicy
-{
- import std.range.primitives : hasLength;
-
- static T[] dup(T)(const T[] arr)
- {
- auto result = alloc!T(arr.length);
- result[] = arr[];
- return result;
- }
-
- static T[] alloc(T)(size_t size)
- {
- import core.stdc.stdlib : malloc;
- import std.exception : enforce;
-
- import core.checkedint : mulu;
- bool overflow;
- size_t nbytes = mulu(size, T.sizeof, overflow);
- if (overflow) assert(0);
-
- auto ptr = cast(T*) enforce(malloc(nbytes), "out of memory on C heap");
- return ptr[0 .. size];
- }
-
- static T[] realloc(T)(T[] arr, size_t size)
- {
- import core.stdc.stdlib : realloc;
- import std.exception : enforce;
- if (!size)
- {
- destroy(arr);
- return null;
- }
-
- import core.checkedint : mulu;
- bool overflow;
- size_t nbytes = mulu(size, T.sizeof, overflow);
- if (overflow) assert(0);
-
- auto ptr = cast(T*) enforce(realloc(arr.ptr, nbytes), "out of memory on C heap");
- return ptr[0 .. size];
- }
-
- static void replaceImpl(T, Range)(ref T[] dest, size_t from, size_t to, Range stuff)
- {
- genericReplace!(ReallocPolicy)(dest, from, to, stuff);
- }
-
- static void append(T, V)(ref T[] arr, V value)
- if (!isInputRange!V)
- {
- if (arr.length == size_t.max) assert(0);
- arr = realloc(arr, arr.length+1);
- arr[$-1] = force!T(value);
- }
-
- @safe unittest
- {
- int[] arr;
- ReallocPolicy.append(arr, 3);
-
- import std.algorithm.comparison : equal;
- assert(equal(arr, [3]));
- }
-
- static void append(T, V)(ref T[] arr, V value)
- if (isInputRange!V && hasLength!V)
- {
- import core.checkedint : addu;
- bool overflow;
- size_t nelems = addu(arr.length, value.length, overflow);
- if (overflow) assert(0);
-
- arr = realloc(arr, nelems);
-
- import std.algorithm.mutation : copy;
- copy(value, arr[$-value.length..$]);
- }
-
- @safe unittest
- {
- int[] arr;
- ReallocPolicy.append(arr, [1,2,3]);
-
- import std.algorithm.comparison : equal;
- assert(equal(arr, [1,2,3]));
- }
-
- static void destroy(T)(ref T[] arr)
- {
- import core.stdc.stdlib : free;
- if (arr.ptr)
- free(arr.ptr);
- arr = null;
- }
-}
-
-//build hack
-alias _RealArray = CowArray!ReallocPolicy;
-
-@safe unittest
-{
- import std.algorithm.comparison : equal;
-
- with(ReallocPolicy)
- {
- bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result,
- string file = __FILE__, size_t line = __LINE__)
- {
- {
- replaceImpl(orig, from, to, toReplace);
- scope(exit) destroy(orig);
- if (!equal(orig, result))
- return false;
- }
- return true;
- }
- static T[] arr(T)(T[] args... )
- {
- return dup(args);
- }
-
- assert(test(arr([1, 2, 3, 4]), 0, 0, [5, 6, 7], [5, 6, 7, 1, 2, 3, 4]));
- assert(test(arr([1, 2, 3, 4]), 0, 2, cast(int[])[], [3, 4]));
- assert(test(arr([1, 2, 3, 4]), 0, 4, [5, 6, 7], [5, 6, 7]));
- assert(test(arr([1, 2, 3, 4]), 0, 2, [5, 6, 7], [5, 6, 7, 3, 4]));
- assert(test(arr([1, 2, 3, 4]), 2, 3, [5, 6, 7], [1, 2, 5, 6, 7, 4]));
- }
-}
-
-/**
- Tests if T is some kind a set of code points. Intended for template constraints.
-*/
-public template isCodepointSet(T)
-{
- static if (is(T dummy == InversionList!(Args), Args...))
- enum isCodepointSet = true;
- else
- enum isCodepointSet = false;
-}
-
-/**
- Tests if $(D T) is a pair of integers that implicitly convert to $(D V).
- The following code must compile for any pair $(D T):
- ---
- (T x){ V a = x[0]; V b = x[1];}
- ---
- The following must not compile:
- ---
- (T x){ V c = x[2];}
- ---
-*/
-public template isIntegralPair(T, V=uint)
-{
- enum isIntegralPair = is(typeof((T x){ V a = x[0]; V b = x[1];}))
- && !is(typeof((T x){ V c = x[2]; }));
-}
-
-
-/**
- The recommended default type for set of $(CODEPOINTS).
- For details, see the current implementation: $(LREF InversionList).
-*/
-public alias CodepointSet = InversionList!GcPolicy;
-
-
-//@@@BUG: std.typecons tuples depend on std.format to produce fields mixin
-// which relies on std.uni.isGraphical and this chain blows up with Forward reference error
-// hence below doesn't seem to work
-// public alias CodepointInterval = Tuple!(uint, "a", uint, "b");
-
-/**
- The recommended type of $(REF Tuple, std,_typecons)
- to represent [a, b$(RPAREN) intervals of $(CODEPOINTS). As used in $(LREF InversionList).
- Any interval type should pass $(LREF isIntegralPair) trait.
-*/
-public struct CodepointInterval
-{
-pure:
- uint[2] _tuple;
- alias _tuple this;
-
-@safe pure nothrow @nogc:
-
- this(uint low, uint high)
- {
- _tuple[0] = low;
- _tuple[1] = high;
- }
- bool opEquals(T)(T val) const
- {
- return this[0] == val[0] && this[1] == val[1];
- }
- @property ref inout(uint) a() inout { return _tuple[0]; }
- @property ref inout(uint) b() inout { return _tuple[1]; }
-}
-
-/**
- $(P
- $(D InversionList) is a set of $(CODEPOINTS)
- represented as an array of open-right [a, b$(RPAREN)
- intervals (see $(LREF CodepointInterval) above).
- The name comes from the way the representation reads left to right.
- For instance a set of all values [10, 50$(RPAREN), [80, 90$(RPAREN),
- plus a singular value 60 looks like this:
- )
- ---
- 10, 50, 60, 61, 80, 90
- ---
- $(P
- The way to read this is: start with negative meaning that all numbers
- smaller then the next one are not present in this set (and positive
- - the contrary). Then switch positive/negative after each
- number passed from left to right.
- )
- $(P This way negative spans until 10, then positive until 50,
- then negative until 60, then positive until 61, and so on.
- As seen this provides a space-efficient storage of highly redundant data
- that comes in long runs. A description which Unicode $(CHARACTER)
- properties fit nicely. The technique itself could be seen as a variation
- on $(LINK2 https://en.wikipedia.org/wiki/Run-length_encoding, RLE encoding).
- )
-
- $(P Sets are value types (just like $(D int) is) thus they
- are never aliased.
- )
- Example:
- ---
- auto a = CodepointSet('a', 'z'+1);
- auto b = CodepointSet('A', 'Z'+1);
- auto c = a;
- a = a | b;
- assert(a == CodepointSet('A', 'Z'+1, 'a', 'z'+1));
- assert(a != c);
- ---
- $(P See also $(LREF unicode) for simpler construction of sets
- from predefined ones.
- )
-
- $(P Memory usage is 8 bytes per each contiguous interval in a set.
- The value semantics are achieved by using the
- $(HTTP en.wikipedia.org/wiki/Copy-on-write, COW) technique
- and thus it's $(RED not) safe to cast this type to $(D_KEYWORD shared).
- )
-
- Note:
- $(P It's not recommended to rely on the template parameters
- or the exact type of a current $(CODEPOINT) set in $(D std.uni).
- The type and parameters may change when the standard
- allocators design is finalized.
- Use $(LREF isCodepointSet) with templates or just stick with the default
- alias $(LREF CodepointSet) throughout the whole code base.
- )
-*/
-@trusted public struct InversionList(SP=GcPolicy)
-{
- import std.range : assumeSorted;
-
- /**
- Construct from another code point set of any type.
- */
- this(Set)(Set set) pure
- if (isCodepointSet!Set)
- {
- uint[] arr;
- foreach (v; set.byInterval)
- {
- arr ~= v.a;
- arr ~= v.b;
- }
- data = CowArray!(SP).reuse(arr);
- }
-
- /**
- Construct a set from a forward range of code point intervals.
- */
- this(Range)(Range intervals) pure
- if (isForwardRange!Range && isIntegralPair!(ElementType!Range))
- {
- uint[] arr;
- foreach (v; intervals)
- {
- SP.append(arr, v.a);
- SP.append(arr, v.b);
- }
- data = CowArray!(SP).reuse(arr);
- sanitize(); //enforce invariant: sort intervals etc.
- }
-
- //helper function that avoids sanity check to be CTFE-friendly
- private static fromIntervals(Range)(Range intervals) pure
- {
- import std.algorithm.iteration : map;
- import std.range : roundRobin;
- auto flattened = roundRobin(intervals.save.map!"a[0]"(),
- intervals.save.map!"a[1]"());
- InversionList set;
- set.data = CowArray!(SP)(flattened);
- return set;
- }
- //ditto untill sort is CTFE-able
- private static fromIntervals()(uint[] intervals...) pure
- in
- {
- import std.conv : text;
- assert(intervals.length % 2 == 0, "Odd number of interval bounds [a, b)!");
- for (uint i = 0; i < intervals.length; i += 2)
- {
- auto a = intervals[i], b = intervals[i+1];
- assert(a < b, text("illegal interval [a, b): ", a, " > ", b));
- }
- }
- body
- {
- InversionList set;
- set.data = CowArray!(SP)(intervals);
- return set;
- }
-
- /**
- Construct a set from plain values of code point intervals.
- */
- this()(uint[] intervals...)
- in
- {
- import std.conv : text;
- assert(intervals.length % 2 == 0, "Odd number of interval bounds [a, b)!");
- for (uint i = 0; i < intervals.length; i += 2)
- {
- auto a = intervals[i], b = intervals[i+1];
- assert(a < b, text("illegal interval [a, b): ", a, " > ", b));
- }
- }
- body
- {
- data = CowArray!(SP)(intervals);
- sanitize(); //enforce invariant: sort intervals etc.
- }
-
- ///
- @safe unittest
- {
- import std.algorithm.comparison : equal;
-
- auto set = CodepointSet('a', 'z'+1, 'а', 'Ñ'+1);
- foreach (v; 'a'..'z'+1)
- assert(set[v]);
- // Cyrillic lowercase interval
- foreach (v; 'а'..'Ñ'+1)
- assert(set[v]);
- //specific order is not required, intervals may interesect
- auto set2 = CodepointSet('а', 'Ñ'+1, 'a', 'd', 'b', 'z'+1);
- //the same end result
- assert(set2.byInterval.equal(set.byInterval));
- }
-
- /**
- Get range that spans all of the $(CODEPOINT) intervals in this $(LREF InversionList).
-
- Example:
- -----------
- import std.algorithm.comparison : equal;
- import std.typecons : tuple;
-
- auto set = CodepointSet('A', 'D'+1, 'a', 'd'+1);
-
- assert(set.byInterval.equal([tuple('A','E'), tuple('a','e')]));
- -----------
- */
- @property auto byInterval()
- {
- return Intervals!(typeof(data))(data);
- }
-
- /**
- Tests the presence of code point $(D val) in this set.
- */
- bool opIndex(uint val) const
- {
- // the <= ensures that searching in interval of [a, b) for 'a' you get .length == 1
- // return assumeSorted!((a,b) => a <= b)(data[]).lowerBound(val).length & 1;
- return sharSwitchLowerBound!"a <= b"(data[], val) & 1;
- }
-
- ///
- @safe unittest
- {
- auto gothic = unicode.Gothic;
- // Gothic letter ahsa
- assert(gothic['\U00010330']);
- // no ascii in Gothic obviously
- assert(!gothic['$']);
- }
-
-
- // Linear scan for $(D ch). Useful only for small sets.
- // TODO:
- // used internally in std.regex
- // should be properly exposed in a public API ?
- package auto scanFor()(dchar ch) const
- {
- immutable len = data.length;
- for (size_t i = 0; i < len; i++)
- if (ch < data[i])
- return i & 1;
- return 0;
- }
-
- /// Number of $(CODEPOINTS) in this set
- @property size_t length()
- {
- size_t sum = 0;
- foreach (iv; byInterval)
- {
- sum += iv.b - iv.a;
- }
- return sum;
- }
-
-// bootstrap full set operations from 4 primitives (suitable as a template mixin):
-// addInterval, skipUpTo, dropUpTo & byInterval iteration
-//============================================================================
-public:
- /**
- $(P Sets support natural syntax for set algebra, namely: )
- $(BOOKTABLE ,
- $(TR $(TH Operator) $(TH Math notation) $(TH Description) )
- $(TR $(TD &) $(TD a ∩ b) $(TD intersection) )
- $(TR $(TD |) $(TD a ∪ b) $(TD union) )
- $(TR $(TD -) $(TD a ∖ b) $(TD subtraction) )
- $(TR $(TD ~) $(TD a ~ b) $(TD symmetric set difference i.e. (a ∪ b) \ (a ∩ b)) )
- )
- */
- This opBinary(string op, U)(U rhs)
- if (isCodepointSet!U || is(U:dchar))
- {
- static if (op == "&" || op == "|" || op == "~")
- {// symmetric ops thus can swap arguments to reuse r-value
- static if (is(U:dchar))
- {
- auto tmp = this;
- mixin("tmp "~op~"= rhs; ");
- return tmp;
- }
- else
- {
- static if (is(Unqual!U == U))
- {
- // try hard to reuse r-value
- mixin("rhs "~op~"= this;");
- return rhs;
- }
- else
- {
- auto tmp = this;
- mixin("tmp "~op~"= rhs;");
- return tmp;
- }
- }
- }
- else static if (op == "-") // anti-symmetric
- {
- auto tmp = this;
- tmp -= rhs;
- return tmp;
- }
- else
- static assert(0, "no operator "~op~" defined for Set");
- }
-
- ///
- @safe unittest
- {
- import std.algorithm.comparison : equal;
- import std.range : iota;
-
- auto lower = unicode.LowerCase;
- auto upper = unicode.UpperCase;
- auto ascii = unicode.ASCII;
-
- assert((lower & upper).empty); // no intersection
- auto lowerASCII = lower & ascii;
- assert(lowerASCII.byCodepoint.equal(iota('a', 'z'+1)));
- // throw away all of the lowercase ASCII
- assert((ascii - lower).length == 128 - 26);
-
- auto onlyOneOf = lower ~ ascii;
- assert(!onlyOneOf['Δ']); // not ASCII and not lowercase
- assert(onlyOneOf['$']); // ASCII and not lowercase
- assert(!onlyOneOf['a']); // ASCII and lowercase
- assert(onlyOneOf['Ñ']); // not ASCII but lowercase
-
- // throw away all cased letters from ASCII
- auto noLetters = ascii - (lower | upper);
- assert(noLetters.length == 128 - 26*2);
- }
-
- /// The 'op=' versions of the above overloaded operators.
- ref This opOpAssign(string op, U)(U rhs)
- if (isCodepointSet!U || is(U:dchar))
- {
- static if (op == "|") // union
- {
- static if (is(U:dchar))
- {
- this.addInterval(rhs, rhs+1);
- return this;
- }
- else
- return this.add(rhs);
- }
- else static if (op == "&") // intersection
- return this.intersect(rhs);// overloaded
- else static if (op == "-") // set difference
- return this.sub(rhs);// overloaded
- else static if (op == "~") // symmetric set difference
- {
- auto copy = this & rhs;
- this |= rhs;
- this -= copy;
- return this;
- }
- else
- static assert(0, "no operator "~op~" defined for Set");
- }
-
- /**
- Tests the presence of codepoint $(D ch) in this set,
- the same as $(LREF opIndex).
- */
- bool opBinaryRight(string op: "in", U)(U ch) const
- if (is(U : dchar))
- {
- return this[ch];
- }
-
- ///
- @safe unittest
- {
- assert('Ñ' in unicode.Cyrillic);
- assert(!('z' in unicode.Cyrillic));
- }
-
-
-
- /**
- * Obtains a set that is the inversion of this set.
- *
- * See_Also: $(LREF inverted)
- */
- auto opUnary(string op: "!")()
- {
- return this.inverted;
- }
-
- /**
- A range that spans each $(CODEPOINT) in this set.
- */
- @property auto byCodepoint()
- {
- @trusted static struct CodepointRange
- {
- this(This set)
- {
- r = set.byInterval;
- if (!r.empty)
- cur = r.front.a;
- }
-
- @property dchar front() const
- {
- return cast(dchar) cur;
- }
-
- @property bool empty() const
- {
- return r.empty;
- }
-
- void popFront()
- {
- cur++;
- while (cur >= r.front.b)
- {
- r.popFront();
- if (r.empty)
- break;
- cur = r.front.a;
- }
- }
- private:
- uint cur;
- typeof(This.init.byInterval) r;
- }
-
- return CodepointRange(this);
- }
-
- ///
- @safe unittest
- {
- import std.algorithm.comparison : equal;
- import std.range : iota;
-
- auto set = unicode.ASCII;
- set.byCodepoint.equal(iota(0, 0x80));
- }
-
- /**
- $(P Obtain textual representation of this set in from of
- open-right intervals and feed it to $(D sink).
- )
- $(P Used by various standard formatting facilities such as
- $(REF formattedWrite, std,_format), $(REF write, std,_stdio),
- $(REF writef, std,_stdio), $(REF to, std,_conv) and others.
- )
- Example:
- ---
- import std.conv;
- assert(unicode.ASCII.to!string == "[0..128$(RPAREN)");
- ---
- */
-
- private import std.format : FormatSpec;
-
- /***************************************
- * Obtain a textual representation of this InversionList
- * in form of open-right intervals.
- *
- * The formatting flag is applied individually to each value, for example:
- * $(LI $(B %s) and $(B %d) format the intervals as a [low .. high$(RPAREN) range of integrals)
- * $(LI $(B %x) formats the intervals as a [low .. high$(RPAREN) range of lowercase hex characters)
- * $(LI $(B %X) formats the intervals as a [low .. high$(RPAREN) range of uppercase hex characters)
- */
- void toString(Writer)(scope Writer sink,
- FormatSpec!char fmt) /* const */
- {
- import std.format : formatValue;
- auto range = byInterval;
- if (range.empty)
- return;
-
- while (1)
- {
- auto i = range.front;
- range.popFront();
-
- put(sink, "[");
- formatValue(sink, i.a, fmt);
- put(sink, "..");
- formatValue(sink, i.b, fmt);
- put(sink, ")");
- if (range.empty) return;
- put(sink, " ");
- }
- }
-
- ///
- @safe unittest
- {
- import std.conv : to;
- import std.format : format;
- import std.uni : unicode;
-
- assert(unicode.Cyrillic.to!string ==
- "[1024..1157) [1159..1320) [7467..7468) [7544..7545) [11744..11776) [42560..42648) [42655..42656)");
-
- // The specs '%s' and '%d' are equivalent to the to!string call above.
- assert(format("%d", unicode.Cyrillic) == unicode.Cyrillic.to!string);
-
- assert(format("%#x", unicode.Cyrillic) ==
- "[0x400..0x485) [0x487..0x528) [0x1d2b..0x1d2c) [0x1d78..0x1d79) [0x2de0..0x2e00) "
- ~"[0xa640..0xa698) [0xa69f..0xa6a0)");
-
- assert(format("%#X", unicode.Cyrillic) ==
- "[0X400..0X485) [0X487..0X528) [0X1D2B..0X1D2C) [0X1D78..0X1D79) [0X2DE0..0X2E00) "
- ~"[0XA640..0XA698) [0XA69F..0XA6A0)");
- }
-
- @safe unittest
- {
- import std.exception : assertThrown;
- import std.format : format, FormatException;
- assertThrown!FormatException(format("%a", unicode.ASCII));
- }
-
-
- /**
- Add an interval [a, b$(RPAREN) to this set.
- */
- ref add()(uint a, uint b)
- {
- addInterval(a, b);
- return this;
- }
-
- ///
- @safe unittest
- {
- CodepointSet someSet;
- someSet.add('0', '5').add('A','Z'+1);
- someSet.add('5', '9'+1);
- assert(someSet['0']);
- assert(someSet['5']);
- assert(someSet['9']);
- assert(someSet['Z']);
- }
-
-private:
-
- package(std) // used from: std.regex.internal.parser
- ref intersect(U)(U rhs)
- if (isCodepointSet!U)
- {
- Marker mark;
- foreach ( i; rhs.byInterval)
- {
- mark = this.dropUpTo(i.a, mark);
- mark = this.skipUpTo(i.b, mark);
- }
- this.dropUpTo(uint.max, mark);
- return this;
- }
-
- ref intersect()(dchar ch)
- {
- foreach (i; byInterval)
- if (i.a <= ch && ch < i.b)
- return this = This.init.add(ch, ch+1);
- this = This.init;
- return this;
- }
-
- @safe unittest
- {
- assert(unicode.Cyrillic.intersect('-').byInterval.empty);
- }
-
- ref sub()(dchar ch)
- {
- return subChar(ch);
- }
-
- // same as the above except that skip & drop parts are swapped
- package(std) // used from: std.regex.internal.parser
- ref sub(U)(U rhs)
- if (isCodepointSet!U)
- {
- Marker mark;
- foreach (i; rhs.byInterval)
- {
- mark = this.skipUpTo(i.a, mark);
- mark = this.dropUpTo(i.b, mark);
- }
- return this;
- }
-
- package(std) // used from: std.regex.internal.parse
- ref add(U)(U rhs)
- if (isCodepointSet!U)
- {
- Marker start;
- foreach (i; rhs.byInterval)
- {
- start = addInterval(i.a, i.b, start);
- }
- return this;
- }
-
-// end of mixin-able part
-//============================================================================
-public:
- /**
- Obtains a set that is the inversion of this set.
-
- See the '!' $(LREF opUnary) for the same but using operators.
- */
- @property auto inverted()
- {
- InversionList inversion = this;
- if (inversion.data.length == 0)
- {
- inversion.addInterval(0, lastDchar+1);
- return inversion;
- }
- if (inversion.data[0] != 0)
- genericReplace(inversion.data, 0, 0, [0]);
- else
- genericReplace(inversion.data, 0, 1, cast(uint[]) null);
- if (data[data.length-1] != lastDchar+1)
- genericReplace(inversion.data,
- inversion.data.length, inversion.data.length, [lastDchar+1]);
- else
- genericReplace(inversion.data,
- inversion.data.length-1, inversion.data.length, cast(uint[]) null);
-
- return inversion;
- }
-
- ///
- @safe unittest
- {
- auto set = unicode.ASCII;
- // union with the inverse gets all of the code points in the Unicode
- assert((set | set.inverted).length == 0x110000);
- // no intersection with the inverse
- assert((set & set.inverted).empty);
- }
-
- /**
- Generates string with D source code of unary function with name of
- $(D funcName) taking a single $(D dchar) argument. If $(D funcName) is empty
- the code is adjusted to be a lambda function.
-
- The function generated tests if the $(CODEPOINT) passed
- belongs to this set or not. The result is to be used with string mixin.
- The intended usage area is aggressive optimization via meta programming
- in parser generators and the like.
-
- Note: Use with care for relatively small or regular sets. It
- could end up being slower then just using multi-staged tables.
-
- Example:
- ---
- import std.stdio;
-
- // construct set directly from [a, b$RPAREN intervals
- auto set = CodepointSet(10, 12, 45, 65, 100, 200);
- writeln(set);
- writeln(set.toSourceCode("func"));
- ---
-
- The above outputs something along the lines of:
- ---
- bool func(dchar ch) @safe pure nothrow @nogc
- {
- if (ch < 45)
- {
- if (ch == 10 || ch == 11) return true;
- return false;
- }
- else if (ch < 65) return true;
- else
- {
- if (ch < 100) return false;
- if (ch < 200) return true;
- return false;
- }
- }
- ---
- */
- string toSourceCode(string funcName="")
- {
- import std.algorithm.searching : countUntil;
- import std.array : array;
- import std.format : format;
- enum maxBinary = 3;
- static string linearScope(R)(R ivals, string indent)
- {
- string result = indent~"{\n";
- string deeper = indent~" ";
- foreach (ival; ivals)
- {
- immutable span = ival[1] - ival[0];
- assert(span != 0);
- if (span == 1)
- {
- result ~= format("%sif (ch == %s) return true;\n", deeper, ival[0]);
- }
- else if (span == 2)
- {
- result ~= format("%sif (ch == %s || ch == %s) return true;\n",
- deeper, ival[0], ival[0]+1);
- }
- else
- {
- if (ival[0] != 0) // dchar is unsigned and < 0 is useless
- result ~= format("%sif (ch < %s) return false;\n", deeper, ival[0]);
- result ~= format("%sif (ch < %s) return true;\n", deeper, ival[1]);
- }
- }
- result ~= format("%sreturn false;\n%s}\n", deeper, indent); // including empty range of intervals
- return result;
- }
-
- static string binaryScope(R)(R ivals, string indent)
- {
- // time to do unrolled comparisons?
- if (ivals.length < maxBinary)
- return linearScope(ivals, indent);
- else
- return bisect(ivals, ivals.length/2, indent);
- }
-
- // not used yet if/elsebinary search is far better with DMD as of 2.061
- // and GDC is doing fine job either way
- static string switchScope(R)(R ivals, string indent)
- {
- string result = indent~"switch (ch){\n";
- string deeper = indent~" ";
- foreach (ival; ivals)
- {
- if (ival[0]+1 == ival[1])
- {
- result ~= format("%scase %s: return true;\n",
- deeper, ival[0]);
- }
- else
- {
- result ~= format("%scase %s: .. case %s: return true;\n",
- deeper, ival[0], ival[1]-1);
- }
- }
- result ~= deeper~"default: return false;\n"~indent~"}\n";
- return result;
- }
-
- static string bisect(R)(R range, size_t idx, string indent)
- {
- string deeper = indent ~ " ";
- // bisect on one [a, b) interval at idx
- string result = indent~"{\n";
- // less branch, < a
- result ~= format("%sif (ch < %s)\n%s",
- deeper, range[idx][0], binaryScope(range[0 .. idx], deeper));
- // middle point, >= a && < b
- result ~= format("%selse if (ch < %s) return true;\n",
- deeper, range[idx][1]);
- // greater or equal branch, >= b
- result ~= format("%selse\n%s",
- deeper, binaryScope(range[idx+1..$], deeper));
- return result~indent~"}\n";
- }
-
- string code = format("bool %s(dchar ch) @safe pure nothrow @nogc\n",
- funcName.empty ? "function" : funcName);
- auto range = byInterval.array();
- // special case first bisection to be on ASCII vs beyond
- auto tillAscii = countUntil!"a[0] > 0x80"(range);
- if (tillAscii <= 0) // everything is ASCII or nothing is ascii (-1 & 0)
- code ~= binaryScope(range, "");
- else
- code ~= bisect(range, tillAscii, "");
- return code;
- }
-
- /**
- True if this set doesn't contain any $(CODEPOINTS).
- */
- @property bool empty() const
- {
- return data.length == 0;
- }
-
- ///
- @safe unittest
- {
- CodepointSet emptySet;
- assert(emptySet.length == 0);
- assert(emptySet.empty);
- }
-
-private:
- alias This = typeof(this);
- alias Marker = size_t;
-
- // a random-access range of integral pairs
- static struct Intervals(Range)
- {
- this(Range sp)
- {
- slice = sp;
- start = 0;
- end = sp.length;
- }
-
- this(Range sp, size_t s, size_t e)
- {
- slice = sp;
- start = s;
- end = e;
- }
-
- @property auto front()const
- {
- immutable a = slice[start];
- immutable b = slice[start+1];
- return CodepointInterval(a, b);
- }
-
- //may break sorted property - but we need std.sort to access it
- //hence package protection attribute
- package @property void front(CodepointInterval val)
- {
- slice[start] = val.a;
- slice[start+1] = val.b;
- }
-
- @property auto back()const
- {
- immutable a = slice[end-2];
- immutable b = slice[end-1];
- return CodepointInterval(a, b);
- }
-
- //ditto about package
- package @property void back(CodepointInterval val)
- {
- slice[end-2] = val.a;
- slice[end-1] = val.b;
- }
-
- void popFront()
- {
- start += 2;
- }
-
- void popBack()
- {
- end -= 2;
- }
-
- auto opIndex(size_t idx) const
- {
- immutable a = slice[start+idx*2];
- immutable b = slice[start+idx*2+1];
- return CodepointInterval(a, b);
- }
-
- //ditto about package
- package void opIndexAssign(CodepointInterval val, size_t idx)
- {
- slice[start+idx*2] = val.a;
- slice[start+idx*2+1] = val.b;
- }
-
- auto opSlice(size_t s, size_t e)
- {
- return Intervals(slice, s*2+start, e*2+start);
- }
-
- @property size_t length()const { return slice.length/2; }
-
- @property bool empty()const { return start == end; }
-
- @property auto save(){ return this; }
- private:
- size_t start, end;
- Range slice;
- }
-
- // called after construction from intervals
- // to make sure invariants hold
- void sanitize()
- {
- import std.algorithm.comparison : max;
- import std.algorithm.mutation : SwapStrategy;
- import std.algorithm.sorting : sort;
- if (data.length == 0)
- return;
- alias Ival = CodepointInterval;
- //intervals wrapper for a _range_ over packed array
- auto ivals = Intervals!(typeof(data[]))(data[]);
- //@@@BUG@@@ can't use "a.a < b.a" see issue 12265
- sort!((a,b) => a.a < b.a, SwapStrategy.stable)(ivals);
- // what follows is a variation on stable remove
- // differences:
- // - predicate is binary, and is tested against
- // the last kept element (at 'i').
- // - predicate mutates lhs (merges rhs into lhs)
- size_t len = ivals.length;
- size_t i = 0;
- size_t j = 1;
- while (j < len)
- {
- if (ivals[i].b >= ivals[j].a)
- {
- ivals[i] = Ival(ivals[i].a, max(ivals[i].b, ivals[j].b));
- j++;
- }
- else //unmergable
- {
- // check if there is a hole after merges
- // (in the best case we do 0 writes to ivals)
- if (j != i+1)
- ivals[i+1] = ivals[j]; //copy over
- i++;
- j++;
- }
- }
- len = i + 1;
- for (size_t k=0; k + 1 < len; k++)
- {
- assert(ivals[k].a < ivals[k].b);
- assert(ivals[k].b < ivals[k+1].a);
- }
- data.length = len * 2;
- }
-
- // special case for normal InversionList
- ref subChar(dchar ch)
- {
- auto mark = skipUpTo(ch);
- if (mark != data.length
- && data[mark] == ch && data[mark-1] == ch)
- {
- // it has split, meaning that ch happens to be in one of intervals
- data[mark] = data[mark]+1;
- }
- return this;
- }
-
- //
- Marker addInterval(int a, int b, Marker hint=Marker.init)
- in
- {
- assert(a <= b);
- }
- body
- {
- import std.range : assumeSorted, SearchPolicy;
- auto range = assumeSorted(data[]);
- size_t pos;
- size_t a_idx = hint + range[hint..$].lowerBound!(SearchPolicy.gallop)(a).length;
- if (a_idx == range.length)
- {
- // [---+++----++++----++++++]
- // [ a b]
- data.append(a, b);
- return data.length-1;
- }
- size_t b_idx = range[a_idx .. range.length].lowerBound!(SearchPolicy.gallop)(b).length+a_idx;
- uint[3] buf = void;
- uint to_insert;
- debug(std_uni)
- {
- writefln("a_idx=%d; b_idx=%d;", a_idx, b_idx);
- }
- if (b_idx == range.length)
- {
- // [-------++++++++----++++++-]
- // [ s a b]
- if (a_idx & 1)// a in positive
- {
- buf[0] = b;
- to_insert = 1;
- }
- else// a in negative
- {
- buf[0] = a;
- buf[1] = b;
- to_insert = 2;
- }
- pos = genericReplace(data, a_idx, b_idx, buf[0 .. to_insert]);
- return pos - 1;
- }
-
- uint top = data[b_idx];
-
- debug(std_uni)
- {
- writefln("a_idx=%d; b_idx=%d;", a_idx, b_idx);
- writefln("a=%s; b=%s; top=%s;", a, b, top);
- }
- if (a_idx & 1)
- {// a in positive
- if (b_idx & 1)// b in positive
- {
- // [-------++++++++----++++++-]
- // [ s a b ]
- buf[0] = top;
- to_insert = 1;
- }
- else // b in negative
- {
- // [-------++++++++----++++++-]
- // [ s a b ]
- if (top == b)
- {
- assert(b_idx+1 < data.length);
- buf[0] = data[b_idx+1];
- pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 1]);
- return pos - 1;
- }
- buf[0] = b;
- buf[1] = top;
- to_insert = 2;
- }
- }
- else
- { // a in negative
- if (b_idx & 1) // b in positive
- {
- // [----------+++++----++++++-]
- // [ a b ]
- buf[0] = a;
- buf[1] = top;
- to_insert = 2;
- }
- else// b in negative
- {
- // [----------+++++----++++++-]
- // [ a s b ]
- if (top == b)
- {
- assert(b_idx+1 < data.length);
- buf[0] = a;
- buf[1] = data[b_idx+1];
- pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 2]);
- return pos - 1;
- }
- buf[0] = a;
- buf[1] = b;
- buf[2] = top;
- to_insert = 3;
- }
- }
- pos = genericReplace(data, a_idx, b_idx+1, buf[0 .. to_insert]);
- debug(std_uni)
- {
- writefln("marker idx: %d; length=%d", pos, data[pos], data.length);
- writeln("inserting ", buf[0 .. to_insert]);
- }
- return pos - 1;
- }
-
- //
- Marker dropUpTo(uint a, Marker pos=Marker.init)
- in
- {
- assert(pos % 2 == 0); // at start of interval
- }
- body
- {
- auto range = assumeSorted!"a <= b"(data[pos .. data.length]);
- if (range.empty)
- return pos;
- size_t idx = pos;
- idx += range.lowerBound(a).length;
-
- debug(std_uni)
- {
- writeln("dropUpTo full length=", data.length);
- writeln(pos,"~~~", idx);
- }
- if (idx == data.length)
- return genericReplace(data, pos, idx, cast(uint[])[]);
- if (idx & 1)
- { // a in positive
- //[--+++----++++++----+++++++------...]
- // |<---si s a t
- genericReplace(data, pos, idx, [a]);
- }
- else
- { // a in negative
- //[--+++----++++++----+++++++-------+++...]
- // |<---si s a t
- genericReplace(data, pos, idx, cast(uint[])[]);
- }
- return pos;
- }
-
- //
- Marker skipUpTo(uint a, Marker pos=Marker.init)
- out(result)
- {
- assert(result % 2 == 0);// always start of interval
- //(may be 0-width after-split)
- }
- body
- {
- assert(data.length % 2 == 0);
- auto range = assumeSorted!"a <= b"(data[pos .. data.length]);
- size_t idx = pos+range.lowerBound(a).length;
-
- if (idx >= data.length) // could have Marker point to recently removed stuff
- return data.length;
-
- if (idx & 1)// inside of interval, check for split
- {
-
- immutable top = data[idx];
- if (top == a)// no need to split, it's end
- return idx+1;
- immutable start = data[idx-1];
- if (a == start)
- return idx-1;
- // split it up
- genericReplace(data, idx, idx+1, [a, a, top]);
- return idx+1; // avoid odd index
- }
- return idx;
- }
-
- CowArray!SP data;
-}
-
-@system unittest
-{
- import std.conv : to;
- assert(unicode.ASCII.to!string() == "[0..128)");
-}
-
-// pedantic version for ctfe, and aligned-access only architectures
-@system private uint safeRead24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc
-{
- idx *= 3;
- version (LittleEndian)
- return ptr[idx] + (cast(uint) ptr[idx+1]<<8)
- + (cast(uint) ptr[idx+2]<<16);
- else
- return (cast(uint) ptr[idx]<<16) + (cast(uint) ptr[idx+1]<<8)
- + ptr[idx+2];
-}
-
-// ditto
-@system private void safeWrite24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc
-{
- idx *= 3;
- version (LittleEndian)
- {
- ptr[idx] = val & 0xFF;
- ptr[idx+1] = (val >> 8) & 0xFF;
- ptr[idx+2] = (val >> 16) & 0xFF;
- }
- else
- {
- ptr[idx] = (val >> 16) & 0xFF;
- ptr[idx+1] = (val >> 8) & 0xFF;
- ptr[idx+2] = val & 0xFF;
- }
-}
-
-// unaligned x86-like read/write functions
-@system private uint unalignedRead24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc
-{
- uint* src = cast(uint*)(ptr+3*idx);
- version (LittleEndian)
- return *src & 0xFF_FFFF;
- else
- return *src >> 8;
-}
-
-// ditto
-@system private void unalignedWrite24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc
-{
- uint* dest = cast(uint*)(cast(ubyte*) ptr + 3*idx);
- version (LittleEndian)
- *dest = val | (*dest & 0xFF00_0000);
- else
- *dest = (val << 8) | (*dest & 0xFF);
-}
-
-@system private uint read24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc
-{
- static if (hasUnalignedReads)
- return __ctfe ? safeRead24(ptr, idx) : unalignedRead24(ptr, idx);
- else
- return safeRead24(ptr, idx);
-}
-
-@system private void write24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc
-{
- static if (hasUnalignedReads)
- return __ctfe ? safeWrite24(ptr, val, idx) : unalignedWrite24(ptr, val, idx);
- else
- return safeWrite24(ptr, val, idx);
-}
-
-struct CowArray(SP=GcPolicy)
-{
- import std.range.primitives : hasLength;
-
- @safe:
- static auto reuse(uint[] arr)
- {
- CowArray cow;
- cow.data = arr;
- SP.append(cow.data, 1);
- assert(cow.refCount == 1);
- assert(cow.length == arr.length);
- return cow;
- }
-
- this(Range)(Range range)
- if (isInputRange!Range && hasLength!Range)
- {
- import std.algorithm.mutation : copy;
- length = range.length;
- copy(range, data[0..$-1]);
- }
-
- this(Range)(Range range)
- if (isForwardRange!Range && !hasLength!Range)
- {
- import std.algorithm.mutation : copy;
- import std.range.primitives : walkLength;
- immutable len = walkLength(range.save);
- length = len;
- copy(range, data[0..$-1]);
- }
-
- this(this)
- {
- if (!empty)
- {
- refCount = refCount + 1;
- }
- }
-
- ~this()
- {
- if (!empty)
- {
- immutable cnt = refCount;
- if (cnt == 1)
- SP.destroy(data);
- else
- refCount = cnt - 1;
- }
- }
-
- // no ref-count for empty U24 array
- @property bool empty() const { return data.length == 0; }
-
- // report one less then actual size
- @property size_t length() const
- {
- return data.length ? data.length - 1 : 0;
- }
-
- //+ an extra slot for ref-count
- @property void length(size_t len)
- {
- import std.algorithm.comparison : min;
- import std.algorithm.mutation : copy;
- if (len == 0)
- {
- if (!empty)
- freeThisReference();
- return;
- }
- immutable total = len + 1; // including ref-count
- if (empty)
- {
- data = SP.alloc!uint(total);
- refCount = 1;
- return;
- }
- immutable cur_cnt = refCount;
- if (cur_cnt != 1) // have more references to this memory
- {
- refCount = cur_cnt - 1;
- auto new_data = SP.alloc!uint(total);
- // take shrinking into account
- auto to_copy = min(total, data.length) - 1;
- copy(data[0 .. to_copy], new_data[0 .. to_copy]);
- data = new_data; // before setting refCount!
- refCount = 1;
- }
- else // 'this' is the only reference
- {
- // use the realloc (hopefully in-place operation)
- data = SP.realloc(data, total);
- refCount = 1; // setup a ref-count in the new end of the array
- }
- }
-
- alias opDollar = length;
-
- uint opIndex()(size_t idx)const
- {
- return data[idx];
- }
-
- void opIndexAssign(uint val, size_t idx)
- {
- auto cnt = refCount;
- if (cnt != 1)
- dupThisReference(cnt);
- data[idx] = val;
- }
-
- //
- auto opSlice(size_t from, size_t to)
- {
- if (!empty)
- {
- auto cnt = refCount;
- if (cnt != 1)
- dupThisReference(cnt);
- }
- return data[from .. to];
-
- }
-
- //
- auto opSlice(size_t from, size_t to) const
- {
- return data[from .. to];
- }
-
- // length slices before the ref count
- auto opSlice()
- {
- return opSlice(0, length);
- }
-
- // ditto
- auto opSlice() const
- {
- return opSlice(0, length);
- }
-
- void append(Range)(Range range)
- if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint))
- {
- size_t nl = length + range.length;
- length = nl;
- copy(range, this[nl-range.length .. nl]);
- }
-
- void append()(uint[] val...)
- {
- length = length + val.length;
- data[$-val.length-1 .. $-1] = val[];
- }
-
- bool opEquals()(auto const ref CowArray rhs)const
- {
- if (empty ^ rhs.empty)
- return false; // one is empty and the other isn't
- return empty || data[0..$-1] == rhs.data[0..$-1];
- }
-
-private:
- // ref-count is right after the data
- @property uint refCount() const
- {
- return data[$-1];
- }
-
- @property void refCount(uint cnt)
- {
- data[$-1] = cnt;
- }
-
- void freeThisReference()
- {
- immutable count = refCount;
- if (count != 1) // have more references to this memory
- {
- // dec shared ref-count
- refCount = count - 1;
- data = [];
- }
- else
- SP.destroy(data);
- assert(!data.ptr);
- }
-
- void dupThisReference(uint count)
- in
- {
- assert(!empty && count != 1 && count == refCount);
- }
- body
- {
- import std.algorithm.mutation : copy;
- // dec shared ref-count
- refCount = count - 1;
- // copy to the new chunk of RAM
- auto new_data = SP.alloc!uint(data.length);
- // bit-blit old stuff except the counter
- copy(data[0..$-1], new_data[0..$-1]);
- data = new_data; // before setting refCount!
- refCount = 1; // so that this updates the right one
- }
-
- uint[] data;
-}
-
-@safe unittest// Uint24 tests
-{
- import std.algorithm.comparison : equal;
- import std.algorithm.mutation : copy;
- import std.conv : text;
- import std.range : iota, chain;
- import std.range.primitives : isBidirectionalRange, isOutputRange;
- void funcRef(T)(ref T u24)
- {
- u24.length = 2;
- u24[1] = 1024;
- T u24_c = u24;
- assert(u24[1] == 1024);
- u24.length = 0;
- assert(u24.empty);
- u24.append([1, 2]);
- assert(equal(u24[], [1, 2]));
- u24.append(111);
- assert(equal(u24[], [1, 2, 111]));
- assert(!u24_c.empty && u24_c[1] == 1024);
- u24.length = 3;
- copy(iota(0, 3), u24[]);
- assert(equal(u24[], iota(0, 3)));
- assert(u24_c[1] == 1024);
- }
-
- void func2(T)(T u24)
- {
- T u24_2 = u24;
- T u24_3;
- u24_3 = u24_2;
- assert(u24_2 == u24_3);
- assert(equal(u24[], u24_2[]));
- assert(equal(u24_2[], u24_3[]));
- funcRef(u24_3);
-
- assert(equal(u24_3[], iota(0, 3)));
- assert(!equal(u24_2[], u24_3[]));
- assert(equal(u24_2[], u24[]));
- u24_2 = u24_3;
- assert(equal(u24_2[], iota(0, 3)));
- // to test that passed arg is intact outside
- // plus try out opEquals
- u24 = u24_3;
- u24 = T.init;
- u24_3 = T.init;
- assert(u24.empty);
- assert(u24 == u24_3);
- assert(u24 != u24_2);
- }
-
- foreach (Policy; AliasSeq!(GcPolicy, ReallocPolicy))
- {
- alias Range = typeof(CowArray!Policy.init[]);
- alias U24A = CowArray!Policy;
- static assert(isForwardRange!Range);
- static assert(isBidirectionalRange!Range);
- static assert(isOutputRange!(Range, uint));
- static assert(isRandomAccessRange!(Range));
-
- auto arr = U24A([42u, 36, 100]);
- assert(arr[0] == 42);
- assert(arr[1] == 36);
- arr[0] = 72;
- arr[1] = 0xFE_FEFE;
- assert(arr[0] == 72);
- assert(arr[1] == 0xFE_FEFE);
- assert(arr[2] == 100);
- U24A arr2 = arr;
- assert(arr2[0] == 72);
- arr2[0] = 11;
- // test COW-ness
- assert(arr[0] == 72);
- assert(arr2[0] == 11);
- // set this to about 100M to stress-test COW memory management
- foreach (v; 0 .. 10_000)
- func2(arr);
- assert(equal(arr[], [72, 0xFE_FEFE, 100]));
-
- auto r2 = U24A(iota(0, 100));
- assert(equal(r2[], iota(0, 100)), text(r2[]));
- copy(iota(10, 170, 2), r2[10 .. 90]);
- assert(equal(r2[], chain(iota(0, 10), iota(10, 170, 2), iota(90, 100)))
- , text(r2[]));
- }
-}
-
-version (unittest)
-{
- private alias AllSets = AliasSeq!(InversionList!GcPolicy, InversionList!ReallocPolicy);
-}
-
-@safe unittest// core set primitives test
-{
- import std.conv : text;
- foreach (CodeList; AllSets)
- {
- CodeList a;
- //"plug a hole" test
- a.add(10, 20).add(25, 30).add(15, 27);
- assert(a == CodeList(10, 30), text(a));
-
- auto x = CodeList.init;
- x.add(10, 20).add(30, 40).add(50, 60);
-
- a = x;
- a.add(20, 49);//[10, 49) [50, 60)
- assert(a == CodeList(10, 49, 50 ,60));
-
- a = x;
- a.add(20, 50);
- assert(a == CodeList(10, 60), text(a));
-
- // simple unions, mostly edge effects
- x = CodeList.init;
- x.add(10, 20).add(40, 60);
-
- a = x;
- a.add(10, 25); //[10, 25) [40, 60)
- assert(a == CodeList(10, 25, 40, 60));
-
- a = x;
- a.add(5, 15); //[5, 20) [40, 60)
- assert(a == CodeList(5, 20, 40, 60));
-
- a = x;
- a.add(0, 10); // [0, 20) [40, 60)
- assert(a == CodeList(0, 20, 40, 60));
-
- a = x;
- a.add(0, 5); // prepand
- assert(a == CodeList(0, 5, 10, 20, 40, 60), text(a));
-
- a = x;
- a.add(5, 20);
- assert(a == CodeList(5, 20, 40, 60));
-
- a = x;
- a.add(3, 37);
- assert(a == CodeList(3, 37, 40, 60));
-
- a = x;
- a.add(37, 65);
- assert(a == CodeList(10, 20, 37, 65));
-
- // some tests on helpers for set intersection
- x = CodeList.init.add(10, 20).add(40, 60).add(100, 120);
- a = x;
-
- auto m = a.skipUpTo(60);
- a.dropUpTo(110, m);
- assert(a == CodeList(10, 20, 40, 60, 110, 120), text(a.data[]));
-
- a = x;
- a.dropUpTo(100);
- assert(a == CodeList(100, 120), text(a.data[]));
-
- a = x;
- m = a.skipUpTo(50);
- a.dropUpTo(140, m);
- assert(a == CodeList(10, 20, 40, 50), text(a.data[]));
- a = x;
- a.dropUpTo(60);
- assert(a == CodeList(100, 120), text(a.data[]));
- }
-}
-
-
-//test constructor to work with any order of intervals
-@safe unittest
-{
- import std.algorithm.comparison : equal;
- import std.conv : text, to;
- import std.range : chain, iota;
- import std.typecons : tuple;
- //ensure constructor handles bad ordering and overlap
- auto c1 = CodepointSet('а', 'Ñ'+1, 'Ð','Я'+1);
- foreach (ch; chain(iota('а', 'Ñ'+1), iota('Ð','Я'+1)))
- assert(ch in c1, to!string(ch));
-
- //contiguos
- assert(CodepointSet(1000, 1006, 1006, 1009)
- .byInterval.equal([tuple(1000, 1009)]));
- //contains
- assert(CodepointSet(900, 1200, 1000, 1100)
- .byInterval.equal([tuple(900, 1200)]));
- //intersect left
- assert(CodepointSet(900, 1100, 1000, 1200)
- .byInterval.equal([tuple(900, 1200)]));
- //intersect right
- assert(CodepointSet(1000, 1200, 900, 1100)
- .byInterval.equal([tuple(900, 1200)]));
-
- //ditto with extra items at end
- assert(CodepointSet(1000, 1200, 900, 1100, 800, 850)
- .byInterval.equal([tuple(800, 850), tuple(900, 1200)]));
- assert(CodepointSet(900, 1100, 1000, 1200, 800, 850)
- .byInterval.equal([tuple(800, 850), tuple(900, 1200)]));
-
- //"plug a hole" test
- auto c2 = CodepointSet(20, 40,
- 60, 80, 100, 140, 150, 200,
- 40, 60, 80, 100, 140, 150
- );
- assert(c2.byInterval.equal([tuple(20, 200)]));
-
- auto c3 = CodepointSet(
- 20, 40, 60, 80, 100, 140, 150, 200,
- 0, 10, 15, 100, 10, 20, 200, 220);
- assert(c3.byInterval.equal([tuple(0, 140), tuple(150, 220)]));
-}
-
-
-@safe unittest
-{ // full set operations
- import std.conv : text;
- foreach (CodeList; AllSets)
- {
- CodeList a, b, c, d;
-
- //"plug a hole"
- a.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
- b.add(40, 60).add(80, 100).add(140, 150);
- c = a | b;
- d = b | a;
- assert(c == CodeList(20, 200), text(CodeList.stringof," ", c));
- assert(c == d, text(c," vs ", d));
-
- b = CodeList.init.add(25, 45).add(65, 85).add(95,110).add(150, 210);
- c = a | b; //[20,45) [60, 85) [95, 140) [150, 210)
- d = b | a;
- assert(c == CodeList(20, 45, 60, 85, 95, 140, 150, 210), text(c));
- assert(c == d, text(c," vs ", d));
-
- b = CodeList.init.add(10, 20).add(30,100).add(145,200);
- c = a | b;//[10, 140) [145, 200)
- d = b | a;
- assert(c == CodeList(10, 140, 145, 200));
- assert(c == d, text(c," vs ", d));
-
- b = CodeList.init.add(0, 10).add(15, 100).add(10, 20).add(200, 220);
- c = a | b;//[0, 140) [150, 220)
- d = b | a;
- assert(c == CodeList(0, 140, 150, 220));
- assert(c == d, text(c," vs ", d));
-
-
- a = CodeList.init.add(20, 40).add(60, 80);
- b = CodeList.init.add(25, 35).add(65, 75);
- c = a & b;
- d = b & a;
- assert(c == CodeList(25, 35, 65, 75), text(c));
- assert(c == d, text(c," vs ", d));
-
- a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
- b = CodeList.init.add(25, 35).add(65, 75).add(110, 130).add(160, 180);
- c = a & b;
- d = b & a;
- assert(c == CodeList(25, 35, 65, 75, 110, 130, 160, 180), text(c));
- assert(c == d, text(c," vs ", d));
-
- a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
- b = CodeList.init.add(10, 30).add(60, 120).add(135, 160);
- c = a & b;//[20, 30)[60, 80) [100, 120) [135, 140) [150, 160)
- d = b & a;
-
- assert(c == CodeList(20, 30, 60, 80, 100, 120, 135, 140, 150, 160),text(c));
- assert(c == d, text(c, " vs ",d));
- assert((c & a) == c);
- assert((d & b) == d);
- assert((c & d) == d);
-
- b = CodeList.init.add(40, 60).add(80, 100).add(140, 200);
- c = a & b;
- d = b & a;
- assert(c == CodeList(150, 200), text(c));
- assert(c == d, text(c, " vs ",d));
- assert((c & a) == c);
- assert((d & b) == d);
- assert((c & d) == d);
-
- assert((a & a) == a);
- assert((b & b) == b);
-
- a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
- b = CodeList.init.add(30, 60).add(75, 120).add(190, 300);
- c = a - b;// [30, 40) [60, 75) [120, 140) [150, 190)
- d = b - a;// [40, 60) [80, 100) [200, 300)
- assert(c == CodeList(20, 30, 60, 75, 120, 140, 150, 190), text(c));
- assert(d == CodeList(40, 60, 80, 100, 200, 300), text(d));
- assert(c - d == c, text(c-d, " vs ", c));
- assert(d - c == d, text(d-c, " vs ", d));
- assert(c - c == CodeList.init);
- assert(d - d == CodeList.init);
-
- a = CodeList.init.add(20, 40).add( 60, 80).add(100, 140).add(150, 200);
- b = CodeList.init.add(10, 50).add(60, 160).add(190, 300);
- c = a - b;// [160, 190)
- d = b - a;// [10, 20) [40, 50) [80, 100) [140, 150) [200, 300)
- assert(c == CodeList(160, 190), text(c));
- assert(d == CodeList(10, 20, 40, 50, 80, 100, 140, 150, 200, 300), text(d));
- assert(c - d == c, text(c-d, " vs ", c));
- assert(d - c == d, text(d-c, " vs ", d));
- assert(c - c == CodeList.init);
- assert(d - d == CodeList.init);
-
- a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
- b = CodeList.init.add(10, 30).add(45, 100).add(130, 190);
- c = a ~ b; // [10, 20) [30, 40) [45, 60) [80, 130) [140, 150) [190, 200)
- d = b ~ a;
- assert(c == CodeList(10, 20, 30, 40, 45, 60, 80, 130, 140, 150, 190, 200),
- text(c));
- assert(c == d, text(c, " vs ", d));
- }
-}
-
-}
-
-@safe unittest// vs single dchar
-{
- import std.conv : text;
- CodepointSet a = CodepointSet(10, 100, 120, 200);
- assert(a - 'A' == CodepointSet(10, 65, 66, 100, 120, 200), text(a - 'A'));
- assert((a & 'B') == CodepointSet(66, 67));
-}
-
-@safe unittest// iteration & opIndex
-{
- import std.algorithm.comparison : equal;
- import std.conv : text;
- import std.typecons : tuple, Tuple;
-
- foreach (CodeList; AliasSeq!(InversionList!(ReallocPolicy)))
- {
- auto arr = "ABCDEFGHIJKLMabcdefghijklm"d;
- auto a = CodeList('A','N','a', 'n');
- assert(equal(a.byInterval,
- [tuple(cast(uint)'A', cast(uint)'N'), tuple(cast(uint)'a', cast(uint)'n')]
- ), text(a.byInterval));
-
- // same @@@BUG as in issue 8949 ?
- version (bug8949)
- {
- import std.range : retro;
- assert(equal(retro(a.byInterval),
- [tuple(cast(uint)'a', cast(uint)'n'), tuple(cast(uint)'A', cast(uint)'N')]
- ), text(retro(a.byInterval)));
- }
- auto achr = a.byCodepoint;
- assert(equal(achr, arr), text(a.byCodepoint));
- foreach (ch; a.byCodepoint)
- assert(a[ch]);
- auto x = CodeList(100, 500, 600, 900, 1200, 1500);
- assert(equal(x.byInterval, [ tuple(100, 500), tuple(600, 900), tuple(1200, 1500)]), text(x.byInterval));
- foreach (ch; x.byCodepoint)
- assert(x[ch]);
- static if (is(CodeList == CodepointSet))
- {
- auto y = CodeList(x.byInterval);
- assert(equal(x.byInterval, y.byInterval));
- }
- assert(equal(CodepointSet.init.byInterval, cast(Tuple!(uint, uint)[])[]));
- assert(equal(CodepointSet.init.byCodepoint, cast(dchar[])[]));
- }
-}
-
-//============================================================================
-// Generic Trie template and various ways to build it
-//============================================================================
-
-// debug helper to get a shortened array dump
-auto arrayRepr(T)(T x)
-{
- import std.conv : text;
- if (x.length > 32)
- {
- return text(x[0 .. 16],"~...~", x[x.length-16 .. x.length]);
- }
- else
- return text(x);
-}
-
-/**
- Maps $(D Key) to a suitable integer index within the range of $(D size_t).
- The mapping is constructed by applying predicates from $(D Prefix) left to right
- and concatenating the resulting bits.
-
- The first (leftmost) predicate defines the most significant bits of
- the resulting index.
- */
-template mapTrieIndex(Prefix...)
-{
- size_t mapTrieIndex(Key)(Key key)
- if (isValidPrefixForTrie!(Key, Prefix))
- {
- alias p = Prefix;
- size_t idx;
- foreach (i, v; p[0..$-1])
- {
- idx |= p[i](key);
- idx <<= p[i+1].bitSize;
- }
- idx |= p[$-1](key);
- return idx;
- }
-}
-
-/*
- $(D TrieBuilder) is a type used for incremental construction
- of $(LREF Trie)s.
-
- See $(LREF buildTrie) for generic helpers built on top of it.
-*/
-@trusted private struct TrieBuilder(Value, Key, Args...)
-if (isBitPackableType!Value && isValidArgsForTrie!(Key, Args))
-{
- import std.exception : enforce;
-
-private:
- // last index is not stored in table, it is used as an offset to values in a block.
- static if (is(Value == bool))// always pack bool
- alias V = BitPacked!(Value, 1);
- else
- alias V = Value;
- static auto deduceMaxIndex(Preds...)()
- {
- size_t idx = 1;
- foreach (v; Preds)
- idx *= 2^^v.bitSize;
- return idx;
- }
-
- static if (is(typeof(Args[0]) : Key)) // Args start with upper bound on Key
- {
- alias Prefix = Args[1..$];
- enum lastPageSize = 2^^Prefix[$-1].bitSize;
- enum translatedMaxIndex = mapTrieIndex!(Prefix)(Args[0]);
- enum roughedMaxIndex =
- (translatedMaxIndex + lastPageSize-1)/lastPageSize*lastPageSize;
- // check warp around - if wrapped, use the default deduction rule
- enum maxIndex = roughedMaxIndex < translatedMaxIndex ?
- deduceMaxIndex!(Prefix)() : roughedMaxIndex;
- }
- else
- {
- alias Prefix = Args;
- enum maxIndex = deduceMaxIndex!(Prefix)();
- }
-
- alias getIndex = mapTrieIndex!(Prefix);
-
- enum lastLevel = Prefix.length-1;
- struct ConstructState
- {
- size_t idx_zeros, idx_ones;
- }
- // iteration over levels of Trie, each indexes its own level and thus a shortened domain
- size_t[Prefix.length] indices;
- // default filler value to use
- Value defValue;
- // this is a full-width index of next item
- size_t curIndex;
- // all-zeros page index, all-ones page index (+ indicator if there is such a page)
- ConstructState[Prefix.length] state;
- // the table being constructed
- MultiArray!(idxTypes!(Key, fullBitSize!(Prefix), Prefix[0..$]), V) table;
-
- @disable this();
-
- //shortcut for index variable at level 'level'
- @property ref idx(size_t level)(){ return indices[level]; }
-
- // this function assumes no holes in the input so
- // indices are going one by one
- void addValue(size_t level, T)(T val, size_t numVals)
- {
- alias j = idx!level;
- enum pageSize = 1 << Prefix[level].bitSize;
- if (numVals == 0)
- return;
- auto ptr = table.slice!(level);
- if (numVals == 1)
- {
- static if (level == Prefix.length-1)
- ptr[j] = val;
- else
- {// can incur narrowing conversion
- assert(j < ptr.length);
- ptr[j] = force!(typeof(ptr[j]))(val);
- }
- j++;
- if (j % pageSize == 0)
- spillToNextPage!level(ptr);
- return;
- }
- // longer row of values
- // get to the next page boundary
- immutable nextPB = (j + pageSize) & ~(pageSize-1);
- immutable n = nextPB - j;// can fill right in this page
- if (numVals < n) //fits in current page
- {
- ptr[j .. j+numVals] = val;
- j += numVals;
- return;
- }
- static if (level != 0)//on the first level it always fits
- {
- numVals -= n;
- //write till the end of current page
- ptr[j .. j+n] = val;
- j += n;
- //spill to the next page
- spillToNextPage!level(ptr);
- // page at once loop
- if (state[level].idx_zeros != size_t.max && val == T.init)
- {
- alias NextIdx = typeof(table.slice!(level-1)[0]);
- addValue!(level-1)(force!NextIdx(state[level].idx_zeros),
- numVals/pageSize);
- ptr = table.slice!level; //table structure might have changed
- numVals %= pageSize;
- }
- else
- {
- while (numVals >= pageSize)
- {
- numVals -= pageSize;
- ptr[j .. j+pageSize] = val;
- j += pageSize;
- spillToNextPage!level(ptr);
- }
- }
- if (numVals)
- {
- // the leftovers, an incomplete page
- ptr[j .. j+numVals] = val;
- j += numVals;
- }
- }
- }
-
- void spillToNextPage(size_t level, Slice)(ref Slice ptr)
- {
- // last level (i.e. topmost) has 1 "page"
- // thus it need not to add a new page on upper level
- static if (level != 0)
- spillToNextPageImpl!(level)(ptr);
- }
-
- // this can re-use the current page if duplicate or allocate a new one
- // it also makes sure that previous levels point to the correct page in this level
- void spillToNextPageImpl(size_t level, Slice)(ref Slice ptr)
- {
- alias NextIdx = typeof(table.slice!(level-1)[0]);
- NextIdx next_lvl_index;
- enum pageSize = 1 << Prefix[level].bitSize;
- assert(idx!level % pageSize == 0);
- immutable last = idx!level-pageSize;
- const slice = ptr[idx!level - pageSize .. idx!level];
- size_t j;
- for (j=0; j<last; j+=pageSize)
- {
- if (ptr[j .. j+pageSize] == slice)
- {
- // get index to it, reuse ptr space for the next block
- next_lvl_index = force!NextIdx(j/pageSize);
- version (none)
- {
- import std.stdio : writefln, writeln;
- writefln("LEVEL(%s) page mapped idx: %s: 0..%s ---> [%s..%s]"
- ,level
- ,indices[level-1], pageSize, j, j+pageSize);
- writeln("LEVEL(", level
- , ") mapped page is: ", slice, ": ", arrayRepr(ptr[j .. j+pageSize]));
- writeln("LEVEL(", level
- , ") src page is :", ptr, ": ", arrayRepr(slice[0 .. pageSize]));
- }
- idx!level -= pageSize; // reuse this page, it is duplicate
- break;
- }
- }
- if (j == last)
- {
- L_allocate_page:
- next_lvl_index = force!NextIdx(idx!level/pageSize - 1);
- if (state[level].idx_zeros == size_t.max && ptr.zeros(j, j+pageSize))
- {
- state[level].idx_zeros = next_lvl_index;
- }
- // allocate next page
- version (none)
- {
- import std.stdio : writefln;
- writefln("LEVEL(%s) page allocated: %s"
- , level, arrayRepr(slice[0 .. pageSize]));
- writefln("LEVEL(%s) index: %s ; page at this index %s"
- , level
- , next_lvl_index
- , arrayRepr(
- table.slice!(level)
- [pageSize*next_lvl_index..(next_lvl_index+1)*pageSize]
- ));
- }
- table.length!level = table.length!level + pageSize;
- }
- L_know_index:
- // for the previous level, values are indices to the pages in the current level
- addValue!(level-1)(next_lvl_index, 1);
- ptr = table.slice!level; //re-load the slice after moves
- }
-
- // idx - full-width index to fill with v (full-width index != key)
- // fills everything in the range of [curIndex, idx) with filler
- void putAt(size_t idx, Value v)
- {
- assert(idx >= curIndex);
- immutable numFillers = idx - curIndex;
- addValue!lastLevel(defValue, numFillers);
- addValue!lastLevel(v, 1);
- curIndex = idx + 1;
- }
-
- // ditto, but sets the range of [idxA, idxB) to v
- void putRangeAt(size_t idxA, size_t idxB, Value v)
- {
- assert(idxA >= curIndex);
- assert(idxB >= idxA);
- size_t numFillers = idxA - curIndex;
- addValue!lastLevel(defValue, numFillers);
- addValue!lastLevel(v, idxB - idxA);
- curIndex = idxB; // open-right
- }
-
- enum errMsg = "non-monotonic prefix function(s), an unsorted range or "~
- "duplicate key->value mapping";
-
-public:
- /**
- Construct a builder, where $(D filler) is a value
- to indicate empty slots (or "not found" condition).
- */
- this(Value filler)
- {
- curIndex = 0;
- defValue = filler;
- // zeros-page index, ones-page index
- foreach (ref v; state)
- v = ConstructState(size_t.max, size_t.max);
- table = typeof(table)(indices);
- // one page per level is a bootstrap minimum
- foreach (i, Pred; Prefix)
- table.length!i = (1 << Pred.bitSize);
- }
-
- /**
- Put a value $(D v) into interval as
- mapped by keys from $(D a) to $(D b).
- All slots prior to $(D a) are filled with
- the default filler.
- */
- void putRange(Key a, Key b, Value v)
- {
- auto idxA = getIndex(a), idxB = getIndex(b);
- // indexes of key should always grow
- enforce(idxB >= idxA && idxA >= curIndex, errMsg);
- putRangeAt(idxA, idxB, v);
- }
-
- /**
- Put a value $(D v) into slot mapped by $(D key).
- All slots prior to $(D key) are filled with the
- default filler.
- */
- void putValue(Key key, Value v)
- {
- import std.conv : text;
- auto idx = getIndex(key);
- enforce(idx >= curIndex, text(errMsg, " ", idx));
- putAt(idx, v);
- }
-
- /// Finishes construction of Trie, yielding an immutable Trie instance.
- auto build()
- {
- static if (maxIndex != 0) // doesn't cover full range of size_t
- {
- assert(curIndex <= maxIndex);
- addValue!lastLevel(defValue, maxIndex - curIndex);
- }
- else
- {
- if (curIndex != 0 // couldn't wrap around
- || (Prefix.length != 1 && indices[lastLevel] == 0)) // can be just empty
- {
- addValue!lastLevel(defValue, size_t.max - curIndex);
- addValue!lastLevel(defValue, 1);
- }
- // else curIndex already completed the full range of size_t by wrapping around
- }
- return Trie!(V, Key, maxIndex, Prefix)(table);
- }
-}
-
-/**
- $(P A generic Trie data-structure for a fixed number of stages.
- The design goal is optimal speed with smallest footprint size.
- )
- $(P It's intentionally read-only and doesn't provide constructors.
- To construct one use a special builder,
- see $(LREF TrieBuilder) and $(LREF buildTrie).
- )
-
-*/
-@trusted private struct Trie(Value, Key, Args...)
-if (isValidPrefixForTrie!(Key, Args)
- || (isValidPrefixForTrie!(Key, Args[1..$])
- && is(typeof(Args[0]) : size_t)))
-{
- import std.range.primitives : isOutputRange;
- static if (is(typeof(Args[0]) : size_t))
- {
- private enum maxIndex = Args[0];
- private enum hasBoundsCheck = true;
- private alias Prefix = Args[1..$];
- }
- else
- {
- private enum hasBoundsCheck = false;
- private alias Prefix = Args;
- }
-
- private this()(typeof(_table) table)
- {
- _table = table;
- }
-
- // only for constant Tries constructed from precompiled tables
- private this()(const(size_t)[] offsets, const(size_t)[] sizes,
- const(size_t)[] data) const
- {
- _table = typeof(_table)(offsets, sizes, data);
- }
-
- /**
- $(P Lookup the $(D key) in this $(D Trie). )
-
- $(P The lookup always succeeds if key fits the domain
- provided during construction. The whole domain defined
- is covered so instead of not found condition
- the sentinel (filler) value could be used. )
-
- $(P See $(LREF buildTrie), $(LREF TrieBuilder) for how to
- define a domain of $(D Trie) keys and the sentinel value. )
-
- Note:
- Domain range-checking is only enabled in debug builds
- and results in assertion failure.
- */
- TypeOfBitPacked!Value opIndex()(Key key) const
- {
- static if (hasBoundsCheck)
- assert(mapTrieIndex!Prefix(key) < maxIndex);
- size_t idx;
- alias p = Prefix;
- idx = cast(size_t) p[0](key);
- foreach (i, v; p[0..$-1])
- idx = cast(size_t)((_table.ptr!i[idx]<<p[i+1].bitSize) + p[i+1](key));
- return _table.ptr!(p.length-1)[idx];
- }
-
- ///
- @property size_t bytes(size_t n=size_t.max)() const
- {
- return _table.bytes!n;
- }
-
- ///
- @property size_t pages(size_t n)() const
- {
- return (bytes!n+2^^(Prefix[n].bitSize-1))
- /2^^Prefix[n].bitSize;
- }
-
- ///
- void store(OutRange)(scope OutRange sink) const
- if (isOutputRange!(OutRange, char))
- {
- _table.store(sink);
- }
-
-private:
- MultiArray!(idxTypes!(Key, fullBitSize!(Prefix), Prefix[0..$]), Value) _table;
-}
-
-// create a tuple of 'sliceBits' that slice the 'top' of bits into pieces of sizes 'sizes'
-// left-to-right, the most significant bits first
-template GetBitSlicing(size_t top, sizes...)
-{
- static if (sizes.length > 0)
- alias GetBitSlicing =
- AliasSeq!(sliceBits!(top - sizes[0], top),
- GetBitSlicing!(top - sizes[0], sizes[1..$]));
- else
- alias GetBitSlicing = AliasSeq!();
-}
-
-template callableWith(T)
-{
- template callableWith(alias Pred)
- {
- static if (!is(typeof(Pred(T.init))))
- enum callableWith = false;
- else
- {
- alias Result = typeof(Pred(T.init));
- enum callableWith = isBitPackableType!(TypeOfBitPacked!(Result));
- }
- }
-}
-
-/*
- Check if $(D Prefix) is a valid set of predicates
- for $(D Trie) template having $(D Key) as the type of keys.
- This requires all predicates to be callable, take
- single argument of type $(D Key) and return unsigned value.
-*/
-template isValidPrefixForTrie(Key, Prefix...)
-{
- import std.meta : allSatisfy;
- enum isValidPrefixForTrie = allSatisfy!(callableWith!Key, Prefix); // TODO: tighten the screws
-}
-
-/*
- Check if $(D Args) is a set of maximum key value followed by valid predicates
- for $(D Trie) template having $(D Key) as the type of keys.
-*/
-template isValidArgsForTrie(Key, Args...)
-{
- static if (Args.length > 1)
- {
- enum isValidArgsForTrie = isValidPrefixForTrie!(Key, Args)
- || (isValidPrefixForTrie!(Key, Args[1..$]) && is(typeof(Args[0]) : Key));
- }
- else
- enum isValidArgsForTrie = isValidPrefixForTrie!Args;
-}
-
-@property size_t sumOfIntegerTuple(ints...)()
-{
- size_t count=0;
- foreach (v; ints)
- count += v;
- return count;
-}
-
-/**
- A shorthand for creating a custom multi-level fixed Trie
- from a $(D CodepointSet). $(D sizes) are numbers of bits per level,
- with the most significant bits used first.
-
- Note: The sum of $(D sizes) must be equal 21.
-
- See_Also: $(LREF toTrie), which is even simpler.
-
- Example:
- ---
- {
- import std.stdio;
- auto set = unicode("Number");
- auto trie = codepointSetTrie!(8, 5, 8)(set);
- writeln("Input code points to test:");
- foreach (line; stdin.byLine)
- {
- int count=0;
- foreach (dchar ch; line)
- if (trie[ch])// is number
- count++;
- writefln("Contains %d number code points.", count);
- }
- }
- ---
-*/
-public template codepointSetTrie(sizes...)
-if (sumOfIntegerTuple!sizes == 21)
-{
- auto codepointSetTrie(Set)(Set set)
- if (isCodepointSet!Set)
- {
- auto builder = TrieBuilder!(bool, dchar, lastDchar+1, GetBitSlicing!(21, sizes))(false);
- foreach (ival; set.byInterval)
- builder.putRange(ival[0], ival[1], true);
- return builder.build();
- }
-}
-
-/// Type of Trie generated by codepointSetTrie function.
-public template CodepointSetTrie(sizes...)
-if (sumOfIntegerTuple!sizes == 21)
-{
- alias Prefix = GetBitSlicing!(21, sizes);
- alias CodepointSetTrie = typeof(TrieBuilder!(bool, dchar, lastDchar+1, Prefix)(false).build());
-}
-
-/**
- A slightly more general tool for building fixed $(D Trie)
- for the Unicode data.
-
- Specifically unlike $(D codepointSetTrie) it's allows creating mappings
- of $(D dchar) to an arbitrary type $(D T).
-
- Note: Overload taking $(D CodepointSet)s will naturally convert
- only to bool mapping $(D Trie)s.
-*/
-public template codepointTrie(T, sizes...)
-if (sumOfIntegerTuple!sizes == 21)
-{
- alias Prefix = GetBitSlicing!(21, sizes);
-
- static if (is(TypeOfBitPacked!T == bool))
- {
- auto codepointTrie(Set)(in Set set)
- if (isCodepointSet!Set)
- {
- return codepointSetTrie(set);
- }
- }
-
- auto codepointTrie()(T[dchar] map, T defValue=T.init)
- {
- return buildTrie!(T, dchar, Prefix)(map, defValue);
- }
-
- // unsorted range of pairs
- auto codepointTrie(R)(R range, T defValue=T.init)
- if (isInputRange!R
- && is(typeof(ElementType!R.init[0]) : T)
- && is(typeof(ElementType!R.init[1]) : dchar))
- {
- // build from unsorted array of pairs
- // TODO: expose index sorting functions for Trie
- return buildTrie!(T, dchar, Prefix)(range, defValue, true);
- }
-}
-
-@system pure unittest
-{
- import std.algorithm.comparison : max;
- import std.algorithm.searching : count;
-
- // pick characters from the Greek script
- auto set = unicode.Greek;
-
- // a user-defined property (or an expensive function)
- // that we want to look up
- static uint luckFactor(dchar ch)
- {
- // here we consider a character lucky
- // if its code point has a lot of identical hex-digits
- // e.g. arabic letter DDAL (\u0688) has a "luck factor" of 2
- ubyte[6] nibbles; // 6 4-bit chunks of code point
- uint value = ch;
- foreach (i; 0 .. 6)
- {
- nibbles[i] = value & 0xF;
- value >>= 4;
- }
- uint luck;
- foreach (n; nibbles)
- luck = cast(uint) max(luck, count(nibbles[], n));
- return luck;
- }
-
- // only unsigned built-ins are supported at the moment
- alias LuckFactor = BitPacked!(uint, 3);
-
- // create a temporary associative array (AA)
- LuckFactor[dchar] map;
- foreach (ch; set.byCodepoint)
- map[ch] = LuckFactor(luckFactor(ch));
-
- // bits per stage are chosen randomly, fell free to optimize
- auto trie = codepointTrie!(LuckFactor, 8, 5, 8)(map);
-
- // from now on the AA is not needed
- foreach (ch; set.byCodepoint)
- assert(trie[ch] == luckFactor(ch)); // verify
- // CJK is not Greek, thus it has the default value
- assert(trie['\u4444'] == 0);
- // and here is a couple of quite lucky Greek characters:
- // Greek small letter epsilon with dasia
- assert(trie['\u1F11'] == 3);
- // Ancient Greek metretes sign
- assert(trie['\U00010181'] == 3);
-
-}
-
-/// Type of Trie as generated by codepointTrie function.
-public template CodepointTrie(T, sizes...)
-if (sumOfIntegerTuple!sizes == 21)
-{
- alias Prefix = GetBitSlicing!(21, sizes);
- alias CodepointTrie = typeof(TrieBuilder!(T, dchar, lastDchar+1, Prefix)(T.init).build());
-}
-
-package template cmpK0(alias Pred)
-{
- import std.typecons : Tuple;
- static bool cmpK0(Value, Key)
- (Tuple!(Value, Key) a, Tuple!(Value, Key) b)
- {
- return Pred(a[1]) < Pred(b[1]);
- }
-}
-
-/**
- The most general utility for construction of $(D Trie)s
- short of using $(D TrieBuilder) directly.
-
- Provides a number of convenience overloads.
- $(D Args) is tuple of maximum key value followed by
- predicates to construct index from key.
-
- Alternatively if the first argument is not a value convertible to $(D Key)
- then the whole tuple of $(D Args) is treated as predicates
- and the maximum Key is deduced from predicates.
-*/
-private template buildTrie(Value, Key, Args...)
-if (isValidArgsForTrie!(Key, Args))
-{
- static if (is(typeof(Args[0]) : Key)) // prefix starts with upper bound on Key
- {
- alias Prefix = Args[1..$];
- }
- else
- alias Prefix = Args;
-
- alias getIndex = mapTrieIndex!(Prefix);
-
- // for multi-sort
- template GetComparators(size_t n)
- {
- static if (n > 0)
- alias GetComparators =
- AliasSeq!(GetComparators!(n-1), cmpK0!(Prefix[n-1]));
- else
- alias GetComparators = AliasSeq!();
- }
-
- /*
- Build $(D Trie) from a range of a Key-Value pairs,
- assuming it is sorted by Key as defined by the following lambda:
- ------
- (a, b) => mapTrieIndex!(Prefix)(a) < mapTrieIndex!(Prefix)(b)
- ------
- Exception is thrown if it's detected that the above order doesn't hold.
-
- In other words $(LREF mapTrieIndex) should be a
- monotonically increasing function that maps $(D Key) to an integer.
-
- See_Also: $(REF sort, std,_algorithm),
- $(REF SortedRange, std,_range),
- $(REF setUnion, std,_algorithm).
- */
- auto buildTrie(Range)(Range range, Value filler=Value.init)
- if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value)
- && is(typeof(Range.init.front[1]) : Key))
- {
- auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
- foreach (v; range)
- builder.putValue(v[1], v[0]);
- return builder.build();
- }
-
- /*
- If $(D Value) is bool (or BitPacked!(bool, x)) then it's possible
- to build $(D Trie) from a range of open-right intervals of $(D Key)s.
- The requirement on the ordering of keys (and the behavior on the
- violation of it) is the same as for Key-Value range overload.
-
- Intervals denote ranges of !$(D filler) i.e. the opposite of filler.
- If no filler provided keys inside of the intervals map to true,
- and $(D filler) is false.
- */
- auto buildTrie(Range)(Range range, Value filler=Value.init)
- if (is(TypeOfBitPacked!Value == bool)
- && isInputRange!Range && is(typeof(Range.init.front[0]) : Key)
- && is(typeof(Range.init.front[1]) : Key))
- {
- auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
- foreach (ival; range)
- builder.putRange(ival[0], ival[1], !filler);
- return builder.build();
- }
-
- auto buildTrie(Range)(Range range, Value filler, bool unsorted)
- if (isInputRange!Range
- && is(typeof(Range.init.front[0]) : Value)
- && is(typeof(Range.init.front[1]) : Key))
- {
- import std.algorithm.sorting : multiSort;
- alias Comps = GetComparators!(Prefix.length);
- if (unsorted)
- multiSort!(Comps)(range);
- return buildTrie(range, filler);
- }
-
- /*
- If $(D Value) is bool (or BitPacked!(bool, x)) then it's possible
- to build $(D Trie) simply from an input range of $(D Key)s.
- The requirement on the ordering of keys (and the behavior on the
- violation of it) is the same as for Key-Value range overload.
-
- Keys found in range denote !$(D filler) i.e. the opposite of filler.
- If no filler provided keys map to true, and $(D filler) is false.
- */
- auto buildTrie(Range)(Range range, Value filler=Value.init)
- if (is(TypeOfBitPacked!Value == bool)
- && isInputRange!Range && is(typeof(Range.init.front) : Key))
- {
- auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
- foreach (v; range)
- builder.putValue(v, !filler);
- return builder.build();
- }
-
- /*
- If $(D Key) is unsigned integer $(D Trie) could be constructed from array
- of values where array index serves as key.
- */
- auto buildTrie()(Value[] array, Value filler=Value.init)
- if (isUnsigned!Key)
- {
- auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
- foreach (idx, v; array)
- builder.putValue(idx, v);
- return builder.build();
- }
-
- /*
- Builds $(D Trie) from associative array.
- */
- auto buildTrie(Key, Value)(Value[Key] map, Value filler=Value.init)
- {
- import std.array : array;
- import std.range : zip;
- auto range = array(zip(map.values, map.keys));
- return buildTrie(range, filler, true); // sort it
- }
-}
-
-// helper in place of assumeSize to
-//reduce mangled name & help DMD inline Trie functors
-struct clamp(size_t bits)
-{
- static size_t opCall(T)(T arg){ return arg; }
- enum bitSize = bits;
-}
-
-struct clampIdx(size_t idx, size_t bits)
-{
- static size_t opCall(T)(T arg){ return arg[idx]; }
- enum bitSize = bits;
-}
-
-/**
- Conceptual type that outlines the common properties of all UTF Matchers.
-
- Note: For illustration purposes only, every method
- call results in assertion failure.
- Use $(LREF utfMatcher) to obtain a concrete matcher
- for UTF-8 or UTF-16 encodings.
-*/
-public struct MatcherConcept
-{
- /**
- $(P Perform a semantic equivalent 2 operations:
- decoding a $(CODEPOINT) at front of $(D inp) and testing if
- it belongs to the set of $(CODEPOINTS) of this matcher. )
-
- $(P The effect on $(D inp) depends on the kind of function called:)
-
- $(P Match. If the codepoint is found in the set then range $(D inp)
- is advanced by its size in $(S_LINK Code unit, code units),
- otherwise the range is not modifed.)
-
- $(P Skip. The range is always advanced by the size
- of the tested $(CODEPOINT) regardless of the result of test.)
-
- $(P Test. The range is left unaffected regardless
- of the result of test.)
- */
- public bool match(Range)(ref Range inp)
- if (isRandomAccessRange!Range && is(ElementType!Range : char))
- {
- assert(false);
- }
-
- ///ditto
- public bool skip(Range)(ref Range inp)
- if (isRandomAccessRange!Range && is(ElementType!Range : char))
- {
- assert(false);
- }
-
- ///ditto
- public bool test(Range)(ref Range inp)
- if (isRandomAccessRange!Range && is(ElementType!Range : char))
- {
- assert(false);
- }
- ///
- @safe unittest
- {
- string truth = "2² = 4";
- auto m = utfMatcher!char(unicode.Number);
- assert(m.match(truth)); // '2' is a number all right
- assert(truth == "² = 4"); // skips on match
- assert(m.match(truth)); // so is the superscript '2'
- assert(!m.match(truth)); // space is not a number
- assert(truth == " = 4"); // unaffected on no match
- assert(!m.skip(truth)); // same test ...
- assert(truth == "= 4"); // but skips a codepoint regardless
- assert(!m.test(truth)); // '=' is not a number
- assert(truth == "= 4"); // test never affects argument
- }
-
- /**
- Advanced feature - provide direct access to a subset of matcher based a
- set of known encoding lengths. Lengths are provided in
- $(S_LINK Code unit, code units). The sub-matcher then may do less
- operations per any $(D test)/$(D match).
-
- Use with care as the sub-matcher won't match
- any $(CODEPOINTS) that have encoded length that doesn't belong
- to the selected set of lengths. Also the sub-matcher object references
- the parent matcher and must not be used past the liftetime
- of the latter.
-
- Another caveat of using sub-matcher is that skip is not available
- preciesly because sub-matcher doesn't detect all lengths.
- */
- @property auto subMatcher(Lengths...)()
- {
- assert(0);
- return this;
- }
-
- @safe unittest
- {
- auto m = utfMatcher!char(unicode.Number);
- string square = "2²";
- // about sub-matchers
- assert(!m.subMatcher!(2,3,4).test(square)); // ASCII no covered
- assert(m.subMatcher!1.match(square)); // ASCII-only, works
- assert(!m.subMatcher!1.test(square)); // unicode '²'
- assert(m.subMatcher!(2,3,4).match(square)); //
- assert(square == "");
- wstring wsquare = "2²";
- auto m16 = utfMatcher!wchar(unicode.Number);
- // may keep ref, but the orignal (m16) must be kept alive
- auto bmp = m16.subMatcher!1;
- assert(bmp.match(wsquare)); // Okay, in basic multilingual plan
- assert(bmp.match(wsquare)); // And '²' too
- }
-}
-
-/**
- Test if $(D M) is an UTF Matcher for ranges of $(D Char).
-*/
-public enum isUtfMatcher(M, C) = __traits(compiles, (){
- C[] s;
- auto d = s.decoder;
- M m;
- assert(is(typeof(m.match(d)) == bool));
- assert(is(typeof(m.test(d)) == bool));
- static if (is(typeof(m.skip(d))))
- {
- assert(is(typeof(m.skip(d)) == bool));
- assert(is(typeof(m.skip(s)) == bool));
- }
- assert(is(typeof(m.match(s)) == bool));
- assert(is(typeof(m.test(s)) == bool));
-});
-
-@safe unittest
-{
- alias CharMatcher = typeof(utfMatcher!char(CodepointSet.init));
- alias WcharMatcher = typeof(utfMatcher!wchar(CodepointSet.init));
- static assert(isUtfMatcher!(CharMatcher, char));
- static assert(isUtfMatcher!(CharMatcher, immutable(char)));
- static assert(isUtfMatcher!(WcharMatcher, wchar));
- static assert(isUtfMatcher!(WcharMatcher, immutable(wchar)));
-}
-
-enum Mode {
- alwaysSkip,
- neverSkip,
- skipOnMatch
-}
-
-mixin template ForwardStrings()
-{
- private bool fwdStr(string fn, C)(ref C[] str) const pure
- {
- import std.utf : byCodeUnit;
- alias type = typeof(byCodeUnit(str));
- return mixin(fn~"(*cast(type*)&str)");
- }
-}
-
-template Utf8Matcher()
-{
- enum validSize(int sz) = sz >= 1 && sz <= 4;
-
- void badEncoding() pure @safe
- {
- import std.utf : UTFException;
- throw new UTFException("Invalid UTF-8 sequence");
- }
-
- //for 1-stage ASCII
- alias AsciiSpec = AliasSeq!(bool, char, clamp!7);
- //for 2-stage lookup of 2 byte UTF-8 sequences
- alias Utf8Spec2 = AliasSeq!(bool, char[2],
- clampIdx!(0, 5), clampIdx!(1, 6));
- //ditto for 3 byte
- alias Utf8Spec3 = AliasSeq!(bool, char[3],
- clampIdx!(0, 4),
- clampIdx!(1, 6),
- clampIdx!(2, 6)
- );
- //ditto for 4 byte
- alias Utf8Spec4 = AliasSeq!(bool, char[4],
- clampIdx!(0, 3), clampIdx!(1, 6),
- clampIdx!(2, 6), clampIdx!(3, 6)
- );
- alias Tables = AliasSeq!(
- typeof(TrieBuilder!(AsciiSpec)(false).build()),
- typeof(TrieBuilder!(Utf8Spec2)(false).build()),
- typeof(TrieBuilder!(Utf8Spec3)(false).build()),
- typeof(TrieBuilder!(Utf8Spec4)(false).build())
- );
- alias Table(int size) = Tables[size-1];
-
- enum leadMask(size_t size) = (cast(size_t) 1<<(7 - size))-1;
- enum encMask(size_t size) = ((1 << size)-1)<<(8-size);
-
- char truncate()(char ch) pure @safe
- {
- ch -= 0x80;
- if (ch < 0x40)
- {
- return ch;
- }
- else
- {
- badEncoding();
- return cast(char) 0;
- }
- }
-
- static auto encode(size_t sz)(dchar ch)
- if (sz > 1)
- {
- import std.utf : encodeUTF = encode;
- char[4] buf;
- encodeUTF(buf, ch);
- char[sz] ret;
- buf[0] &= leadMask!sz;
- foreach (n; 1 .. sz)
- buf[n] = buf[n] & 0x3f; //keep 6 lower bits
- ret[] = buf[0 .. sz];
- return ret;
- }
-
- auto build(Set)(Set set)
- {
- import std.algorithm.iteration : map;
- auto ascii = set & unicode.ASCII;
- auto utf8_2 = set & CodepointSet(0x80, 0x800);
- auto utf8_3 = set & CodepointSet(0x800, 0x1_0000);
- auto utf8_4 = set & CodepointSet(0x1_0000, lastDchar+1);
- auto asciiT = ascii.byCodepoint.map!(x=>cast(char) x).buildTrie!(AsciiSpec);
- auto utf8_2T = utf8_2.byCodepoint.map!(x=>encode!2(x)).buildTrie!(Utf8Spec2);
- auto utf8_3T = utf8_3.byCodepoint.map!(x=>encode!3(x)).buildTrie!(Utf8Spec3);
- auto utf8_4T = utf8_4.byCodepoint.map!(x=>encode!4(x)).buildTrie!(Utf8Spec4);
- alias Ret = Impl!(1,2,3,4);
- return Ret(asciiT, utf8_2T, utf8_3T, utf8_4T);
- }
-
- // Bootstrap UTF-8 static matcher interface
- // from 3 primitives: tab!(size), lookup and Sizes
- mixin template DefMatcher()
- {
- import std.format : format;
- import std.meta : Erase, staticIndexOf;
- enum hasASCII = staticIndexOf!(1, Sizes) >= 0;
- alias UniSizes = Erase!(1, Sizes);
-
- //generate dispatch code sequence for unicode parts
- static auto genDispatch()
- {
- string code;
- foreach (size; UniSizes)
- code ~= format(q{
- if ((ch & ~leadMask!%d) == encMask!(%d))
- return lookup!(%d, mode)(inp);
- else
- }, size, size, size);
- static if (Sizes.length == 4) //covers all code unit cases
- code ~= "{ badEncoding(); return false; }";
- else
- code ~= "return false;"; //may be just fine but not covered
- return code;
- }
- enum dispatch = genDispatch();
-
- public bool match(Range)(ref Range inp) const pure
- if (isRandomAccessRange!Range && is(ElementType!Range : char))
- {
- enum mode = Mode.skipOnMatch;
- assert(!inp.empty);
- immutable ch = inp[0];
- static if (hasASCII)
- {
- if (ch < 0x80)
- {
- immutable r = tab!1[ch];
- if (r)
- inp.popFront();
- return r;
- }
- else
- mixin(dispatch);
- }
- else
- mixin(dispatch);
- }
-
- static if (Sizes.length == 4) // can skip iff can detect all encodings
- {
- public bool skip(Range)(ref Range inp) const pure @trusted
- if (isRandomAccessRange!Range && is(ElementType!Range : char))
- {
- enum mode = Mode.alwaysSkip;
- assert(!inp.empty);
- auto ch = inp[0];
- static if (hasASCII)
- {
- if (ch < 0x80)
- {
- inp.popFront();
- return tab!1[ch];
- }
- else
- mixin(dispatch);
- }
- else
- mixin(dispatch);
- }
- }
-
- public bool test(Range)(ref Range inp) const pure @trusted
- if (isRandomAccessRange!Range && is(ElementType!Range : char))
- {
- enum mode = Mode.neverSkip;
- assert(!inp.empty);
- auto ch = inp[0];
- static if (hasASCII)
- {
- if (ch < 0x80)
- return tab!1[ch];
- else
- mixin(dispatch);
- }
- else
- mixin(dispatch);
- }
-
- bool match(C)(ref C[] str) const pure @trusted
- if (isSomeChar!C)
- {
- return fwdStr!"match"(str);
- }
-
- bool skip(C)(ref C[] str) const pure @trusted
- if (isSomeChar!C)
- {
- return fwdStr!"skip"(str);
- }
-
- bool test(C)(ref C[] str) const pure @trusted
- if (isSomeChar!C)
- {
- return fwdStr!"test"(str);
- }
-
- mixin ForwardStrings;
- }
-
- struct Impl(Sizes...)
- {
- import std.meta : allSatisfy, staticMap;
- static assert(allSatisfy!(validSize, Sizes),
- "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8");
- private:
- //pick tables for chosen sizes
- alias OurTabs = staticMap!(Table, Sizes);
- OurTabs tables;
- mixin DefMatcher;
- //static disptach helper UTF size ==> table
- alias tab(int i) = tables[i - 1];
-
- package @property auto subMatcher(SizesToPick...)() @trusted
- {
- return CherryPick!(Impl, SizesToPick)(&this);
- }
-
- bool lookup(int size, Mode mode, Range)(ref Range inp) const pure @trusted
- {
- import std.typecons : staticIota;
- if (inp.length < size)
- {
- badEncoding();
- return false;
- }
- char[size] needle = void;
- needle[0] = leadMask!size & inp[0];
- foreach (i; staticIota!(1, size))
- {
- needle[i] = truncate(inp[i]);
- }
- //overlong encoding checks
- static if (size == 2)
- {
- //0x80-0x7FF
- //got 6 bits in needle[1], must use at least 8 bits
- //must use at least 2 bits in needle[1]
- if (needle[0] < 2) badEncoding();
- }
- else static if (size == 3)
- {
- //0x800-0xFFFF
- //got 6 bits in needle[2], must use at least 12bits
- //must use 6 bits in needle[1] or anything in needle[0]
- if (needle[0] == 0 && needle[1] < 0x20) badEncoding();
- }
- else static if (size == 4)
- {
- //0x800-0xFFFF
- //got 2x6=12 bits in needle[2 .. 3] must use at least 17bits
- //must use 5 bits (or above) in needle[1] or anything in needle[0]
- if (needle[0] == 0 && needle[1] < 0x10) badEncoding();
- }
- static if (mode == Mode.alwaysSkip)
- {
- inp.popFrontN(size);
- return tab!size[needle];
- }
- else static if (mode == Mode.neverSkip)
- {
- return tab!size[needle];
- }
- else
- {
- static assert(mode == Mode.skipOnMatch);
- if (tab!size[needle])
- {
- inp.popFrontN(size);
- return true;
- }
- else
- return false;
- }
- }
- }
-
- struct CherryPick(I, Sizes...)
- {
- import std.meta : allSatisfy;
- static assert(allSatisfy!(validSize, Sizes),
- "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8");
- private:
- I* m;
- @property ref tab(int i)() const pure { return m.tables[i - 1]; }
- bool lookup(int size, Mode mode, Range)(ref Range inp) const pure
- {
- return m.lookup!(size, mode)(inp);
- }
- mixin DefMatcher;
- }
-}
-
-template Utf16Matcher()
-{
- enum validSize(int sz) = sz >= 1 && sz <= 2;
-
- void badEncoding() pure
- {
- import std.utf : UTFException;
- throw new UTFException("Invalid UTF-16 sequence");
- }
-
- // 1-stage ASCII
- alias AsciiSpec = AliasSeq!(bool, wchar, clamp!7);
- //2-stage BMP
- alias BmpSpec = AliasSeq!(bool, wchar, sliceBits!(7, 16), sliceBits!(0, 7));
- //4-stage - full Unicode
- //assume that 0xD800 & 0xDC00 bits are cleared
- //thus leaving 10 bit per wchar to worry about
- alias UniSpec = AliasSeq!(bool, wchar[2],
- assumeSize!(x=>x[0]>>4, 6), assumeSize!(x=>x[0]&0xf, 4),
- assumeSize!(x=>x[1]>>6, 4), assumeSize!(x=>x[1]&0x3f, 6),
- );
- alias Ascii = typeof(TrieBuilder!(AsciiSpec)(false).build());
- alias Bmp = typeof(TrieBuilder!(BmpSpec)(false).build());
- alias Uni = typeof(TrieBuilder!(UniSpec)(false).build());
-
- auto encode2(dchar ch)
- {
- ch -= 0x1_0000;
- assert(ch <= 0xF_FFFF);
- wchar[2] ret;
- //do not put surrogate bits, they are sliced off
- ret[0] = cast(wchar)(ch >> 10);
- ret[1] = (ch & 0xFFF);
- return ret;
- }
-
- auto build(Set)(Set set)
- {
- import std.algorithm.iteration : map;
- auto ascii = set & unicode.ASCII;
- auto bmp = (set & CodepointSet.fromIntervals(0x80, 0xFFFF+1))
- - CodepointSet.fromIntervals(0xD800, 0xDFFF+1);
- auto other = set - (bmp | ascii);
- auto asciiT = ascii.byCodepoint.map!(x=>cast(char) x).buildTrie!(AsciiSpec);
- auto bmpT = bmp.byCodepoint.map!(x=>cast(wchar) x).buildTrie!(BmpSpec);
- auto otherT = other.byCodepoint.map!(x=>encode2(x)).buildTrie!(UniSpec);
- alias Ret = Impl!(1,2);
- return Ret(asciiT, bmpT, otherT);
- }
-
- //bootstrap full UTF-16 matcher interace from
- //sizeFlags, lookupUni and ascii
- mixin template DefMatcher()
- {
- public bool match(Range)(ref Range inp) const pure @trusted
- if (isRandomAccessRange!Range && is(ElementType!Range : wchar))
- {
- enum mode = Mode.skipOnMatch;
- assert(!inp.empty);
- immutable ch = inp[0];
- static if (sizeFlags & 1)
- {
- if (ch < 0x80)
- {
- if (ascii[ch])
- {
- inp.popFront();
- return true;
- }
- else
- return false;
- }
- return lookupUni!mode(inp);
- }
- else
- return lookupUni!mode(inp);
- }
-
- static if (Sizes.length == 2)
- {
- public bool skip(Range)(ref Range inp) const pure @trusted
- if (isRandomAccessRange!Range && is(ElementType!Range : wchar))
- {
- enum mode = Mode.alwaysSkip;
- assert(!inp.empty);
- immutable ch = inp[0];
- static if (sizeFlags & 1)
- {
- if (ch < 0x80)
- {
- inp.popFront();
- return ascii[ch];
- }
- else
- return lookupUni!mode(inp);
- }
- else
- return lookupUni!mode(inp);
- }
- }
-
- public bool test(Range)(ref Range inp) const pure @trusted
- if (isRandomAccessRange!Range && is(ElementType!Range : wchar))
- {
- enum mode = Mode.neverSkip;
- assert(!inp.empty);
- auto ch = inp[0];
- static if (sizeFlags & 1)
- return ch < 0x80 ? ascii[ch] : lookupUni!mode(inp);
- else
- return lookupUni!mode(inp);
- }
-
- bool match(C)(ref C[] str) const pure @trusted
- if (isSomeChar!C)
- {
- return fwdStr!"match"(str);
- }
-
- bool skip(C)(ref C[] str) const pure @trusted
- if (isSomeChar!C)
- {
- return fwdStr!"skip"(str);
- }
-
- bool test(C)(ref C[] str) const pure @trusted
- if (isSomeChar!C)
- {
- return fwdStr!"test"(str);
- }
-
- mixin ForwardStrings; //dispatch strings to range versions
- }
-
- struct Impl(Sizes...)
- if (Sizes.length >= 1 && Sizes.length <= 2)
- {
- private:
- import std.meta : allSatisfy;
- static assert(allSatisfy!(validSize, Sizes),
- "Only lengths of 1 and 2 code units are possible in UTF-16");
- static if (Sizes.length > 1)
- enum sizeFlags = Sizes[0] | Sizes[1];
- else
- enum sizeFlags = Sizes[0];
-
- static if (sizeFlags & 1)
- {
- Ascii ascii;
- Bmp bmp;
- }
- static if (sizeFlags & 2)
- {
- Uni uni;
- }
- mixin DefMatcher;
-
- package @property auto subMatcher(SizesToPick...)() @trusted
- {
- return CherryPick!(Impl, SizesToPick)(&this);
- }
-
- bool lookupUni(Mode mode, Range)(ref Range inp) const pure
- {
- wchar x = cast(wchar)(inp[0] - 0xD800);
- //not a high surrogate
- if (x > 0x3FF)
- {
- //low surrogate
- if (x <= 0x7FF) badEncoding();
- static if (sizeFlags & 1)
- {
- auto ch = inp[0];
- static if (mode == Mode.alwaysSkip)
- inp.popFront();
- static if (mode == Mode.skipOnMatch)
- {
- if (bmp[ch])
- {
- inp.popFront();
- return true;
- }
- else
- return false;
- }
- else
- return bmp[ch];
- }
- else //skip is not available for sub-matchers, so just false
- return false;
- }
- else
- {
- static if (sizeFlags & 2)
- {
- if (inp.length < 2)
- badEncoding();
- wchar y = cast(wchar)(inp[1] - 0xDC00);
- //not a low surrogate
- if (y > 0x3FF)
- badEncoding();
- wchar[2] needle = [inp[0] & 0x3ff, inp[1] & 0x3ff];
- static if (mode == Mode.alwaysSkip)
- inp.popFrontN(2);
- static if (mode == Mode.skipOnMatch)
- {
- if (uni[needle])
- {
- inp.popFrontN(2);
- return true;
- }
- else
- return false;
- }
- else
- return uni[needle];
- }
- else //ditto
- return false;
- }
- }
- }
-
- struct CherryPick(I, Sizes...)
- if (Sizes.length >= 1 && Sizes.length <= 2)
- {
- private:
- import std.meta : allSatisfy;
- I* m;
- enum sizeFlags = I.sizeFlags;
-
- static if (sizeFlags & 1)
- {
- @property ref ascii()() const pure{ return m.ascii; }
- }
-
- bool lookupUni(Mode mode, Range)(ref Range inp) const pure
- {
- return m.lookupUni!mode(inp);
- }
- mixin DefMatcher;
- static assert(allSatisfy!(validSize, Sizes),
- "Only lengths of 1 and 2 code units are possible in UTF-16");
- }
-}
-
-private auto utf8Matcher(Set)(Set set) @trusted
-{
- return Utf8Matcher!().build(set);
-}
-
-private auto utf16Matcher(Set)(Set set) @trusted
-{
- return Utf16Matcher!().build(set);
-}
-
-/**
- Constructs a matcher object
- to classify $(CODEPOINTS) from the $(D set) for encoding
- that has $(D Char) as code unit.
-
- See $(LREF MatcherConcept) for API outline.
-*/
-public auto utfMatcher(Char, Set)(Set set) @trusted
-if (isCodepointSet!Set)
-{
- static if (is(Char : char))
- return utf8Matcher(set);
- else static if (is(Char : wchar))
- return utf16Matcher(set);
- else static if (is(Char : dchar))
- static assert(false, "UTF-32 needs no decoding,
- and thus not supported by utfMatcher");
- else
- static assert(false, "Only character types 'char' and 'wchar' are allowed");
-}
-
-
-//a range of code units, packed with index to speed up forward iteration
-package auto decoder(C)(C[] s, size_t offset=0) @safe pure nothrow @nogc
-if (is(C : wchar) || is(C : char))
-{
- static struct Decoder
- {
- pure nothrow:
- C[] str;
- size_t idx;
- @property C front(){ return str[idx]; }
- @property C back(){ return str[$-1]; }
- void popFront(){ idx++; }
- void popBack(){ str = str[0..$-1]; }
- void popFrontN(size_t n){ idx += n; }
- @property bool empty(){ return idx == str.length; }
- @property auto save(){ return this; }
- auto opIndex(size_t i){ return str[idx+i]; }
- @property size_t length(){ return str.length - idx; }
- alias opDollar = length;
- auto opSlice(size_t a, size_t b){ return Decoder(str[0 .. idx+b], idx+a); }
- }
- static assert(isRandomAccessRange!Decoder);
- static assert(is(ElementType!Decoder : C));
- return Decoder(s, offset);
-}
-
-@safe unittest
-{
- string rs = "hi! ネемног砀 текÑта";
- auto codec = rs.decoder;
- auto utf8 = utf8Matcher(unicode.Letter);
- auto asc = utf8.subMatcher!(1);
- auto uni = utf8.subMatcher!(2,3,4);
- assert(asc.test(codec));
- assert(!uni.match(codec));
- assert(utf8.skip(codec));
- assert(codec.idx == 1);
-
- assert(!uni.match(codec));
- assert(asc.test(codec));
- assert(utf8.skip(codec));
- assert(codec.idx == 2);
- assert(!asc.match(codec));
-
- assert(!utf8.test(codec));
- assert(!utf8.skip(codec));
-
- assert(!asc.test(codec));
- assert(!utf8.test(codec));
- assert(!utf8.skip(codec));
- assert(utf8.test(codec));
- foreach (i; 0 .. 7)
- {
- assert(!asc.test(codec));
- assert(uni.test(codec));
- assert(utf8.skip(codec));
- }
- assert(!utf8.test(codec));
- assert(!utf8.skip(codec));
- //the same with match where applicable
- codec = rs.decoder;
- assert(utf8.match(codec));
- assert(codec.idx == 1);
- assert(utf8.match(codec));
- assert(codec.idx == 2);
- assert(!utf8.match(codec));
- assert(codec.idx == 2);
- assert(!utf8.skip(codec));
- assert(!utf8.skip(codec));
-
- foreach (i; 0 .. 7)
- {
- assert(!asc.test(codec));
- assert(utf8.test(codec));
- assert(utf8.match(codec));
- }
- auto i = codec.idx;
- assert(!utf8.match(codec));
- assert(codec.idx == i);
-}
-
-@safe unittest
-{
- import std.range : stride;
- static bool testAll(Matcher, Range)(ref Matcher m, ref Range r)
- {
- bool t = m.test(r);
- auto save = r.idx;
- assert(t == m.match(r));
- assert(r.idx == save || t); //ether no change or was match
- r.idx = save;
- static if (is(typeof(m.skip(r))))
- {
- assert(t == m.skip(r));
- assert(r.idx != save); //always changed
- r.idx = save;
- }
- return t;
- }
- auto utf16 = utfMatcher!wchar(unicode.L);
- auto bmp = utf16.subMatcher!1;
- auto nonBmp = utf16.subMatcher!1;
- auto utf8 = utfMatcher!char(unicode.L);
- auto ascii = utf8.subMatcher!1;
- auto uni2 = utf8.subMatcher!2;
- auto uni3 = utf8.subMatcher!3;
- auto uni24 = utf8.subMatcher!(2,4);
- foreach (ch; unicode.L.byCodepoint.stride(3))
- {
- import std.utf : encode;
- char[4] buf;
- wchar[2] buf16;
- auto len = encode(buf, ch);
- auto len16 = encode(buf16, ch);
- auto c8 = buf[0 .. len].decoder;
- auto c16 = buf16[0 .. len16].decoder;
- assert(testAll(utf16, c16));
- assert(testAll(bmp, c16) || len16 != 1);
- assert(testAll(nonBmp, c16) || len16 != 2);
-
- assert(testAll(utf8, c8));
-
- //submatchers return false on out of their domain
- assert(testAll(ascii, c8) || len != 1);
- assert(testAll(uni2, c8) || len != 2);
- assert(testAll(uni3, c8) || len != 3);
- assert(testAll(uni24, c8) || (len != 2 && len != 4));
- }
-}
-
-// cover decode fail cases of Matcher
-@system unittest
-{
- import std.algorithm.iteration : map;
- import std.exception : collectException;
- import std.format : format;
- auto utf16 = utfMatcher!wchar(unicode.L);
- auto utf8 = utfMatcher!char(unicode.L);
- //decode failure cases UTF-8
- alias fails8 = AliasSeq!("\xC1", "\x80\x00","\xC0\x00", "\xCF\x79",
- "\xFF\x00\0x00\0x00\x00", "\xC0\0x80\0x80\x80", "\x80\0x00\0x00\x00",
- "\xCF\x00\0x00\0x00\x00");
- foreach (msg; fails8)
- {
- assert(collectException((){
- auto s = msg;
- size_t idx = 0;
- utf8.test(s);
- }()), format("%( %2x %)", cast(ubyte[]) msg));
- }
- //decode failure cases UTF-16
- alias fails16 = AliasSeq!([0xD811], [0xDC02]);
- foreach (msg; fails16)
- {
- assert(collectException((){
- auto s = msg.map!(x => cast(wchar) x);
- utf16.test(s);
- }()));
- }
-}
-
-/++
- Convenience function to construct optimal configurations for
- packed Trie from any $(D set) of $(CODEPOINTS).
-
- The parameter $(D level) indicates the number of trie levels to use,
- allowed values are: 1, 2, 3 or 4. Levels represent different trade-offs
- speed-size wise.
-
- $(P Level 1 is fastest and the most memory hungry (a bit array). )
- $(P Level 4 is the slowest and has the smallest footprint. )
-
- See the $(S_LINK Synopsis, Synopsis) section for example.
-
- Note:
- Level 4 stays very practical (being faster and more predictable)
- compared to using direct lookup on the $(D set) itself.
-
-
-+/
-public auto toTrie(size_t level, Set)(Set set)
-if (isCodepointSet!Set)
-{
- static if (level == 1)
- return codepointSetTrie!(21)(set);
- else static if (level == 2)
- return codepointSetTrie!(10, 11)(set);
- else static if (level == 3)
- return codepointSetTrie!(8, 5, 8)(set);
- else static if (level == 4)
- return codepointSetTrie!(6, 4, 4, 7)(set);
- else
- static assert(false,
- "Sorry, toTrie doesn't support levels > 4, use codepointSetTrie directly");
-}
-
-/**
- $(P Builds a $(D Trie) with typically optimal speed-size trade-off
- and wraps it into a delegate of the following type:
- $(D bool delegate(dchar ch)). )
-
- $(P Effectively this creates a 'tester' lambda suitable
- for algorithms like std.algorithm.find that take unary predicates. )
-
- See the $(S_LINK Synopsis, Synopsis) section for example.
-*/
-public auto toDelegate(Set)(Set set)
-if (isCodepointSet!Set)
-{
- // 3 is very small and is almost as fast as 2-level (due to CPU caches?)
- auto t = toTrie!3(set);
- return (dchar ch) => t[ch];
-}
-
-/**
- $(P Opaque wrapper around unsigned built-in integers and
- code unit (char/wchar/dchar) types.
- Parameter $(D sz) indicates that the value is confined
- to the range of [0, 2^^sz$(RPAREN). With this knowledge it can be
- packed more tightly when stored in certain
- data-structures like trie. )
-
- Note:
- $(P The $(D BitPacked!(T, sz)) is implicitly convertible to $(D T)
- but not vise-versa. Users have to ensure the value fits in
- the range required and use the $(D cast)
- operator to perform the conversion.)
-*/
-struct BitPacked(T, size_t sz)
-if (isIntegral!T || is(T:dchar))
-{
- enum bitSize = sz;
- T _value;
- alias _value this;
-}
-
-/*
- Depending on the form of the passed argument $(D bitSizeOf) returns
- the amount of bits required to represent a given type
- or a return type of a given functor.
-*/
-template bitSizeOf(Args...)
-if (Args.length == 1)
-{
- import std.traits : ReturnType;
- alias T = Args[0];
- static if (__traits(compiles, { size_t val = T.bitSize; })) //(is(typeof(T.bitSize) : size_t))
- {
- enum bitSizeOf = T.bitSize;
- }
- else static if (is(ReturnType!T dummy == BitPacked!(U, bits), U, size_t bits))
- {
- enum bitSizeOf = bitSizeOf!(ReturnType!T);
- }
- else
- {
- enum bitSizeOf = T.sizeof*8;
- }
-}
-
-/**
- Tests if $(D T) is some instantiation of $(LREF BitPacked)!(U, x)
- and thus suitable for packing.
-*/
-template isBitPacked(T)
-{
- static if (is(T dummy == BitPacked!(U, bits), U, size_t bits))
- enum isBitPacked = true;
- else
- enum isBitPacked = false;
-}
-
-/**
- Gives the type $(D U) from $(LREF BitPacked)!(U, x)
- or $(D T) itself for every other type.
-*/
-template TypeOfBitPacked(T)
-{
- static if (is(T dummy == BitPacked!(U, bits), U, size_t bits))
- alias TypeOfBitPacked = U;
- else
- alias TypeOfBitPacked = T;
-}
-
-/*
- Wrapper, used in definition of custom data structures from $(D Trie) template.
- Applying it to a unary lambda function indicates that the returned value always
- fits within $(D bits) of bits.
-*/
-struct assumeSize(alias Fn, size_t bits)
-{
- enum bitSize = bits;
- static auto ref opCall(T)(auto ref T arg)
- {
- return Fn(arg);
- }
-}
-
-/*
- A helper for defining lambda function that yields a slice
- of certain bits from an unsigned integral value.
- The resulting lambda is wrapped in assumeSize and can be used directly
- with $(D Trie) template.
-*/
-struct sliceBits(size_t from, size_t to)
-{
- //for now bypass assumeSize, DMD has trouble inlining it
- enum bitSize = to-from;
- static auto opCall(T)(T x)
- out(result)
- {
- assert(result < (1 << to-from));
- }
- body
- {
- static assert(from < to);
- static if (from == 0)
- return x & ((1 << to)-1);
- else
- return (x >> from) & ((1<<(to-from))-1);
- }
-}
-
-@safe pure nothrow @nogc uint low_8(uint x) { return x&0xFF; }
-@safe pure nothrow @nogc uint midlow_8(uint x){ return (x&0xFF00)>>8; }
-alias lo8 = assumeSize!(low_8, 8);
-alias mlo8 = assumeSize!(midlow_8, 8);
-
-static assert(bitSizeOf!lo8 == 8);
-static assert(bitSizeOf!(sliceBits!(4, 7)) == 3);
-static assert(bitSizeOf!(BitPacked!(uint, 2)) == 2);
-
-template Sequence(size_t start, size_t end)
-{
- static if (start < end)
- alias Sequence = AliasSeq!(start, Sequence!(start+1, end));
- else
- alias Sequence = AliasSeq!();
-}
-
-//---- TRIE TESTS ----
-@system unittest
-{
- import std.algorithm.iteration : map;
- import std.algorithm.sorting : sort;
- import std.array : array;
- import std.conv : text, to;
- import std.range : iota;
- static trieStats(TRIE)(TRIE t)
- {
- version (std_uni_stats)
- {
- import std.stdio : writefln, writeln;
- writeln("---TRIE FOOTPRINT STATS---");
- foreach (i; staticIota!(0, t.table.dim) )
- {
- writefln("lvl%s = %s bytes; %s pages"
- , i, t.bytes!i, t.pages!i);
- }
- writefln("TOTAL: %s bytes", t.bytes);
- version (none)
- {
- writeln("INDEX (excluding value level):");
- foreach (i; staticIota!(0, t.table.dim-1) )
- writeln(t.table.slice!(i)[0 .. t.table.length!i]);
- }
- writeln("---------------------------");
- }
- }
- //@@@BUG link failure, lambdas not found by linker somehow (in case of trie2)
- // alias lo8 = assumeSize!(8, function (uint x) { return x&0xFF; });
- // alias next8 = assumeSize!(7, function (uint x) { return (x&0x7F00)>>8; });
- alias Set = CodepointSet;
- auto set = Set('A','Z','a','z');
- auto trie = buildTrie!(bool, uint, 256, lo8)(set.byInterval);// simple bool array
- for (int a='a'; a<'z';a++)
- assert(trie[a]);
- for (int a='A'; a<'Z';a++)
- assert(trie[a]);
- for (int a=0; a<'A'; a++)
- assert(!trie[a]);
- for (int a ='Z'; a<'a'; a++)
- assert(!trie[a]);
- trieStats(trie);
-
- auto redundant2 = Set(
- 1, 18, 256+2, 256+111, 512+1, 512+18, 768+2, 768+111);
- auto trie2 = buildTrie!(bool, uint, 1024, mlo8, lo8)(redundant2.byInterval);
- trieStats(trie2);
- foreach (e; redundant2.byCodepoint)
- assert(trie2[e], text(cast(uint) e, " - ", trie2[e]));
- foreach (i; 0 .. 1024)
- {
- assert(trie2[i] == (i in redundant2));
- }
-
-
- auto redundant3 = Set(
- 2, 4, 6, 8, 16,
- 2+16, 4+16, 16+6, 16+8, 16+16,
- 2+32, 4+32, 32+6, 32+8,
- );
-
- enum max3 = 256;
- // sliceBits
- auto trie3 = buildTrie!(bool, uint, max3,
- sliceBits!(6,8), sliceBits!(4,6), sliceBits!(0,4)
- )(redundant3.byInterval);
- trieStats(trie3);
- foreach (i; 0 .. max3)
- assert(trie3[i] == (i in redundant3), text(cast(uint) i));
-
- auto redundant4 = Set(
- 10, 64, 64+10, 128, 128+10, 256, 256+10, 512,
- 1000, 2000, 3000, 4000, 5000, 6000
- );
- enum max4 = 2^^16;
- auto trie4 = buildTrie!(bool, size_t, max4,
- sliceBits!(13, 16), sliceBits!(9, 13), sliceBits!(6, 9) , sliceBits!(0, 6)
- )(redundant4.byInterval);
- foreach (i; 0 .. max4)
- {
- if (i in redundant4)
- assert(trie4[i], text(cast(uint) i));
- }
- trieStats(trie4);
-
- alias mapToS = mapTrieIndex!(useItemAt!(0, char));
- string[] redundantS = ["tea", "start", "orange"];
- redundantS.sort!((a,b) => mapToS(a) < mapToS(b))();
- auto strie = buildTrie!(bool, string, useItemAt!(0, char))(redundantS);
- // using first char only
- assert(redundantS == ["orange", "start", "tea"]);
- assert(strie["test"], text(strie["test"]));
- assert(!strie["aea"]);
- assert(strie["s"]);
-
- // a bit size test
- auto a = array(map!(x => to!ubyte(x))(iota(0, 256)));
- auto bt = buildTrie!(bool, ubyte, sliceBits!(7, 8), sliceBits!(5, 7), sliceBits!(0, 5))(a);
- trieStats(bt);
- foreach (i; 0 .. 256)
- assert(bt[cast(ubyte) i]);
-}
-
-template useItemAt(size_t idx, T)
-if (isIntegral!T || is(T: dchar))
-{
- size_t impl(in T[] arr){ return arr[idx]; }
- alias useItemAt = assumeSize!(impl, 8*T.sizeof);
-}
-
-template useLastItem(T)
-{
- size_t impl(in T[] arr){ return arr[$-1]; }
- alias useLastItem = assumeSize!(impl, 8*T.sizeof);
-}
-
-template fullBitSize(Prefix...)
-{
- static if (Prefix.length > 0)
- enum fullBitSize = bitSizeOf!(Prefix[0])+fullBitSize!(Prefix[1..$]);
- else
- enum fullBitSize = 0;
-}
-
-template idxTypes(Key, size_t fullBits, Prefix...)
-{
- static if (Prefix.length == 1)
- {// the last level is value level, so no index once reduced to 1-level
- alias idxTypes = AliasSeq!();
- }
- else
- {
- // Important note on bit packing
- // Each level has to hold enough of bits to address the next one
- // The bottom level is known to hold full bit width
- // thus it's size in pages is full_bit_width - size_of_last_prefix
- // Recourse on this notion
- alias idxTypes =
- AliasSeq!(
- idxTypes!(Key, fullBits - bitSizeOf!(Prefix[$-1]), Prefix[0..$-1]),
- BitPacked!(typeof(Prefix[$-2](Key.init)), fullBits - bitSizeOf!(Prefix[$-1]))
- );
- }
-}
-
-//============================================================================
-
-@safe pure int comparePropertyName(Char1, Char2)(const(Char1)[] a, const(Char2)[] b)
-if (is(Char1 : dchar) && is(Char2 : dchar))
-{
- import std.algorithm.comparison : cmp;
- import std.algorithm.iteration : map, filter;
- import std.ascii : toLower;
- static bool pred(dchar c) {return !c.isWhite && c != '-' && c != '_';}
- return cmp(
- a.map!toLower.filter!pred,
- b.map!toLower.filter!pred);
-}
-
-@safe pure unittest
-{
- assert(!comparePropertyName("foo-bar", "fooBar"));
-}
-
-bool propertyNameLess(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) @safe pure
-if (is(Char1 : dchar) && is(Char2 : dchar))
-{
- return comparePropertyName(a, b) < 0;
-}
-
-//============================================================================
-// Utilities for compression of Unicode code point sets
-//============================================================================
-
-@safe void compressTo(uint val, ref ubyte[] arr) pure nothrow
-{
- // not optimized as usually done 1 time (and not public interface)
- if (val < 128)
- arr ~= cast(ubyte) val;
- else if (val < (1 << 13))
- {
- arr ~= (0b1_00 << 5) | cast(ubyte)(val >> 8);
- arr ~= val & 0xFF;
- }
- else
- {
- assert(val < (1 << 21));
- arr ~= (0b1_01 << 5) | cast(ubyte)(val >> 16);
- arr ~= (val >> 8) & 0xFF;
- arr ~= val & 0xFF;
- }
-}
-
-@safe uint decompressFrom(const(ubyte)[] arr, ref size_t idx) pure
-{
- import std.exception : enforce;
- immutable first = arr[idx++];
- if (!(first & 0x80)) // no top bit -> [0 .. 127]
- return first;
- immutable extra = ((first >> 5) & 1) + 1; // [1, 2]
- uint val = (first & 0x1F);
- enforce(idx + extra <= arr.length, "bad code point interval encoding");
- foreach (j; 0 .. extra)
- val = (val << 8) | arr[idx+j];
- idx += extra;
- return val;
-}
-
-
-package ubyte[] compressIntervals(Range)(Range intervals)
-if (isInputRange!Range && isIntegralPair!(ElementType!Range))
-{
- ubyte[] storage;
- uint base = 0;
- // RLE encode
- foreach (val; intervals)
- {
- compressTo(val[0]-base, storage);
- base = val[0];
- if (val[1] != lastDchar+1) // till the end of the domain so don't store it
- {
- compressTo(val[1]-base, storage);
- base = val[1];
- }
- }
- return storage;
-}
-
-@safe pure unittest
-{
- import std.algorithm.comparison : equal;
- import std.typecons : tuple;
-
- auto run = [tuple(80, 127), tuple(128, (1 << 10)+128)];
- ubyte[] enc = [cast(ubyte) 80, 47, 1, (0b1_00 << 5) | (1 << 2), 0];
- assert(compressIntervals(run) == enc);
- auto run2 = [tuple(0, (1 << 20)+512+1), tuple((1 << 20)+512+4, lastDchar+1)];
- ubyte[] enc2 = [cast(ubyte) 0, (0b1_01 << 5) | (1 << 4), 2, 1, 3]; // odd length-ed
- assert(compressIntervals(run2) == enc2);
- size_t idx = 0;
- assert(decompressFrom(enc, idx) == 80);
- assert(decompressFrom(enc, idx) == 47);
- assert(decompressFrom(enc, idx) == 1);
- assert(decompressFrom(enc, idx) == (1 << 10));
- idx = 0;
- assert(decompressFrom(enc2, idx) == 0);
- assert(decompressFrom(enc2, idx) == (1 << 20)+512+1);
- assert(equal(decompressIntervals(compressIntervals(run)), run));
- assert(equal(decompressIntervals(compressIntervals(run2)), run2));
-}
-
-// Creates a range of $(D CodepointInterval) that lazily decodes compressed data.
-@safe package auto decompressIntervals(const(ubyte)[] data) pure
-{
- return DecompressedIntervals(data);
-}
-
-@safe struct DecompressedIntervals
-{
-pure:
- const(ubyte)[] _stream;
- size_t _idx;
- CodepointInterval _front;
-
- this(const(ubyte)[] stream)
- {
- _stream = stream;
- popFront();
- }
-
- @property CodepointInterval front()
- {
- assert(!empty);
- return _front;
- }
-
- void popFront()
- {
- if (_idx == _stream.length)
- {
- _idx = size_t.max;
- return;
- }
- uint base = _front[1];
- _front[0] = base + decompressFrom(_stream, _idx);
- if (_idx == _stream.length)// odd length ---> till the end
- _front[1] = lastDchar+1;
- else
- {
- base = _front[0];
- _front[1] = base + decompressFrom(_stream, _idx);
- }
- }
-
- @property bool empty() const
- {
- return _idx == size_t.max;
- }
-
- @property DecompressedIntervals save() { return this; }
-}
-
-static assert(isInputRange!DecompressedIntervals);
-static assert(isForwardRange!DecompressedIntervals);
-//============================================================================
-
-version (std_uni_bootstrap){}
-else
-{
-
-// helper for looking up code point sets
-@trusted ptrdiff_t findUnicodeSet(alias table, C)(in C[] name) pure
-{
- import std.algorithm.iteration : map;
- import std.range : assumeSorted;
- auto range = assumeSorted!((a,b) => propertyNameLess(a,b))
- (table.map!"a.name"());
- size_t idx = range.lowerBound(name).length;
- if (idx < range.length && comparePropertyName(range[idx], name) == 0)
- return idx;
- return -1;
-}
-
-// another one that loads it
-@trusted bool loadUnicodeSet(alias table, Set, C)(in C[] name, ref Set dest) pure
-{
- auto idx = findUnicodeSet!table(name);
- if (idx >= 0)
- {
- dest = Set(asSet(table[idx].compressed));
- return true;
- }
- return false;
-}
-
-@trusted bool loadProperty(Set=CodepointSet, C)
- (in C[] name, ref Set target) pure
-{
- import std.internal.unicode_tables : uniProps; // generated file
- alias ucmp = comparePropertyName;
- // conjure cumulative properties by hand
- if (ucmp(name, "L") == 0 || ucmp(name, "Letter") == 0)
- {
- target = asSet(uniProps.Lu);
- target |= asSet(uniProps.Ll);
- target |= asSet(uniProps.Lt);
- target |= asSet(uniProps.Lo);
- target |= asSet(uniProps.Lm);
- }
- else if (ucmp(name,"LC") == 0 || ucmp(name,"Cased Letter")==0)
- {
- target = asSet(uniProps.Ll);
- target |= asSet(uniProps.Lu);
- target |= asSet(uniProps.Lt);// Title case
- }
- else if (ucmp(name, "M") == 0 || ucmp(name, "Mark") == 0)
- {
- target = asSet(uniProps.Mn);
- target |= asSet(uniProps.Mc);
- target |= asSet(uniProps.Me);
- }
- else if (ucmp(name, "N") == 0 || ucmp(name, "Number") == 0)
- {
- target = asSet(uniProps.Nd);
- target |= asSet(uniProps.Nl);
- target |= asSet(uniProps.No);
- }
- else if (ucmp(name, "P") == 0 || ucmp(name, "Punctuation") == 0)
- {
- target = asSet(uniProps.Pc);
- target |= asSet(uniProps.Pd);
- target |= asSet(uniProps.Ps);
- target |= asSet(uniProps.Pe);
- target |= asSet(uniProps.Pi);
- target |= asSet(uniProps.Pf);
- target |= asSet(uniProps.Po);
- }
- else if (ucmp(name, "S") == 0 || ucmp(name, "Symbol") == 0)
- {
- target = asSet(uniProps.Sm);
- target |= asSet(uniProps.Sc);
- target |= asSet(uniProps.Sk);
- target |= asSet(uniProps.So);
- }
- else if (ucmp(name, "Z") == 0 || ucmp(name, "Separator") == 0)
- {
- target = asSet(uniProps.Zs);
- target |= asSet(uniProps.Zl);
- target |= asSet(uniProps.Zp);
- }
- else if (ucmp(name, "C") == 0 || ucmp(name, "Other") == 0)
- {
- target = asSet(uniProps.Co);
- target |= asSet(uniProps.Lo);
- target |= asSet(uniProps.No);
- target |= asSet(uniProps.So);
- target |= asSet(uniProps.Po);
- }
- else if (ucmp(name, "graphical") == 0)
- {
- target = asSet(uniProps.Alphabetic);
-
- target |= asSet(uniProps.Mn);
- target |= asSet(uniProps.Mc);
- target |= asSet(uniProps.Me);
-
- target |= asSet(uniProps.Nd);
- target |= asSet(uniProps.Nl);
- target |= asSet(uniProps.No);
-
- target |= asSet(uniProps.Pc);
- target |= asSet(uniProps.Pd);
- target |= asSet(uniProps.Ps);
- target |= asSet(uniProps.Pe);
- target |= asSet(uniProps.Pi);
- target |= asSet(uniProps.Pf);
- target |= asSet(uniProps.Po);
-
- target |= asSet(uniProps.Zs);
-
- target |= asSet(uniProps.Sm);
- target |= asSet(uniProps.Sc);
- target |= asSet(uniProps.Sk);
- target |= asSet(uniProps.So);
- }
- else if (ucmp(name, "any") == 0)
- target = Set.fromIntervals(0, 0x110000);
- else if (ucmp(name, "ascii") == 0)
- target = Set.fromIntervals(0, 0x80);
- else
- return loadUnicodeSet!(uniProps.tab)(name, target);
- return true;
-}
-
-// CTFE-only helper for checking property names at compile-time
-@safe bool isPrettyPropertyName(C)(in C[] name)
-{
- import std.algorithm.searching : find;
- auto names = [
- "L", "Letter",
- "LC", "Cased Letter",
- "M", "Mark",
- "N", "Number",
- "P", "Punctuation",
- "S", "Symbol",
- "Z", "Separator",
- "Graphical",
- "any",
- "ascii"
- ];
- auto x = find!(x => comparePropertyName(x, name) == 0)(names);
- return !x.empty;
-}
-
-// ditto, CTFE-only, not optimized
-@safe private static bool findSetName(alias table, C)(in C[] name)
-{
- return findUnicodeSet!table(name) >= 0;
-}
-
-template SetSearcher(alias table, string kind)
-{
- /// Run-time checked search.
- static auto opCall(C)(in C[] name)
- if (is(C : dchar))
- {
- import std.conv : to;
- CodepointSet set;
- if (loadUnicodeSet!table(name, set))
- return set;
- throw new Exception("No unicode set for "~kind~" by name "
- ~name.to!string()~" was found.");
- }
- /// Compile-time checked search.
- static @property auto opDispatch(string name)()
- {
- static if (findSetName!table(name))
- {
- CodepointSet set;
- loadUnicodeSet!table(name, set);
- return set;
- }
- else
- static assert(false, "No unicode set for "~kind~" by name "
- ~name~" was found.");
- }
-}
-
-/**
- A single entry point to lookup Unicode $(CODEPOINT) sets by name or alias of
- a block, script or general category.
-
- It uses well defined standard rules of property name lookup.
- This includes fuzzy matching of names, so that
- 'White_Space', 'white-SpAce' and 'whitespace' are all considered equal
- and yield the same set of white space $(CHARACTERS).
-*/
-@safe public struct unicode
-{
- /**
- Performs the lookup of set of $(CODEPOINTS)
- with compile-time correctness checking.
- This short-cut version combines 3 searches:
- across blocks, scripts, and common binary properties.
-
- Note that since scripts and blocks overlap the
- usual trick to disambiguate is used - to get a block use
- $(D unicode.InBlockName), to search a script
- use $(D unicode.ScriptName).
-
- See_Also: $(LREF block), $(LREF script)
- and (not included in this search) $(LREF hangulSyllableType).
- */
-
- static @property auto opDispatch(string name)() pure
- {
- static if (findAny(name))
- return loadAny(name);
- else
- static assert(false, "No unicode set by name "~name~" was found.");
- }
-
- ///
- @safe unittest
- {
- import std.exception : collectException;
- auto ascii = unicode.ASCII;
- assert(ascii['A']);
- assert(ascii['~']);
- assert(!ascii['\u00e0']);
- // matching is case-insensitive
- assert(ascii == unicode.ascII);
- assert(!ascii['à']);
- // underscores, '-' and whitespace in names are ignored too
- auto latin = unicode.in_latin1_Supplement;
- assert(latin['à']);
- assert(!latin['$']);
- // BTW Latin 1 Supplement is a block, hence "In" prefix
- assert(latin == unicode("In Latin 1 Supplement"));
- // run-time look up throws if no such set is found
- assert(collectException(unicode("InCyrilliac")));
- }
-
- /**
- The same lookup across blocks, scripts, or binary properties,
- but performed at run-time.
- This version is provided for cases where $(D name)
- is not known beforehand; otherwise compile-time
- checked $(LREF opDispatch) is typically a better choice.
-
- See the $(S_LINK Unicode properties, table of properties) for available
- sets.
- */
- static auto opCall(C)(in C[] name)
- if (is(C : dchar))
- {
- return loadAny(name);
- }
-
- /**
- Narrows down the search for sets of $(CODEPOINTS) to all Unicode blocks.
-
- Note:
- Here block names are unambiguous as no scripts are searched
- and thus to search use simply $(D unicode.block.BlockName) notation.
-
- See $(S_LINK Unicode properties, table of properties) for available sets.
- See_Also: $(S_LINK Unicode properties, table of properties).
- */
- struct block
- {
- import std.internal.unicode_tables : blocks; // generated file
- mixin SetSearcher!(blocks.tab, "block");
- }
-
- ///
- @safe unittest
- {
- // use .block for explicitness
- assert(unicode.block.Greek_and_Coptic == unicode.InGreek_and_Coptic);
- }
-
- /**
- Narrows down the search for sets of $(CODEPOINTS) to all Unicode scripts.
-
- See the $(S_LINK Unicode properties, table of properties) for available
- sets.
- */
- struct script
- {
- import std.internal.unicode_tables : scripts; // generated file
- mixin SetSearcher!(scripts.tab, "script");
- }
-
- ///
- @safe unittest
- {
- auto arabicScript = unicode.script.arabic;
- auto arabicBlock = unicode.block.arabic;
- // there is an intersection between script and block
- assert(arabicBlock['Ø']);
- assert(arabicScript['Ø']);
- // but they are different
- assert(arabicBlock != arabicScript);
- assert(arabicBlock == unicode.inArabic);
- assert(arabicScript == unicode.arabic);
- }
-
- /**
- Fetch a set of $(CODEPOINTS) that have the given hangul syllable type.
-
- Other non-binary properties (once supported) follow the same
- notation - $(D unicode.propertyName.propertyValue) for compile-time
- checked access and $(D unicode.propertyName(propertyValue))
- for run-time checked one.
-
- See the $(S_LINK Unicode properties, table of properties) for available
- sets.
- */
- struct hangulSyllableType
- {
- import std.internal.unicode_tables : hangul; // generated file
- mixin SetSearcher!(hangul.tab, "hangul syllable type");
- }
-
- ///
- @safe unittest
- {
- // L here is syllable type not Letter as in unicode.L short-cut
- auto leadingVowel = unicode.hangulSyllableType("L");
- // check that some leading vowels are present
- foreach (vowel; '\u1110'..'\u115F')
- assert(leadingVowel[vowel]);
- assert(leadingVowel == unicode.hangulSyllableType.L);
- }
-
-private:
- alias ucmp = comparePropertyName;
-
- static bool findAny(string name)
- {
- import std.internal.unicode_tables : blocks, scripts, uniProps; // generated file
- return isPrettyPropertyName(name)
- || findSetName!(uniProps.tab)(name) || findSetName!(scripts.tab)(name)
- || (ucmp(name[0 .. 2],"In") == 0 && findSetName!(blocks.tab)(name[2..$]));
- }
-
- static auto loadAny(Set=CodepointSet, C)(in C[] name) pure
- {
- import std.conv : to;
- import std.internal.unicode_tables : blocks, scripts; // generated file
- Set set;
- immutable loaded = loadProperty(name, set) || loadUnicodeSet!(scripts.tab)(name, set)
- || (name.length > 2 && ucmp(name[0 .. 2],"In") == 0
- && loadUnicodeSet!(blocks.tab)(name[2..$], set));
- if (loaded)
- return set;
- throw new Exception("No unicode set by name "~name.to!string()~" was found.");
- }
-
- // FIXME: re-disable once the compiler is fixed
- // Disabled to prevent the mistake of creating instances of this pseudo-struct.
- //@disable ~this();
-}
-
-@safe unittest
-{
- import std.internal.unicode_tables : blocks, uniProps; // generated file
- assert(unicode("InHebrew") == asSet(blocks.Hebrew));
- assert(unicode("separator") == (asSet(uniProps.Zs) | asSet(uniProps.Zl) | asSet(uniProps.Zp)));
- assert(unicode("In-Kharoshthi") == asSet(blocks.Kharoshthi));
-}
-
-enum EMPTY_CASE_TRIE = ushort.max;// from what gen_uni uses internally
-
-// control - '\r'
-enum controlSwitch = `
- case '\u0000':..case '\u0008':case '\u000E':..case '\u001F':case '\u007F':..
- case '\u0084':case '\u0086':..case '\u009F': case '\u0009':..case '\u000C': case '\u0085':
-`;
-// TODO: redo the most of hangul stuff algorithmically in case of Graphemes too
-// kill unrolled switches
-
-private static bool isRegionalIndicator(dchar ch) @safe pure @nogc nothrow
-{
- return ch >= '\U0001F1E6' && ch <= '\U0001F1FF';
-}
-
-template genericDecodeGrapheme(bool getValue)
-{
- alias graphemeExtend = graphemeExtendTrie;
- alias spacingMark = mcTrie;
- static if (getValue)
- alias Value = Grapheme;
- else
- alias Value = void;
-
- Value genericDecodeGrapheme(Input)(ref Input range)
- {
- import std.internal.unicode_tables : isHangL, isHangT, isHangV; // generated file
- enum GraphemeState {
- Start,
- CR,
- RI,
- L,
- V,
- LVT
- }
- static if (getValue)
- Grapheme grapheme;
- auto state = GraphemeState.Start;
- enum eat = q{
- static if (getValue)
- grapheme ~= ch;
- range.popFront();
- };
-
- dchar ch;
- assert(!range.empty, "Attempting to decode grapheme from an empty " ~ Input.stringof);
- while (!range.empty)
- {
- ch = range.front;
- final switch (state) with(GraphemeState)
- {
- case Start:
- mixin(eat);
- if (ch == '\r')
- state = CR;
- else if (isRegionalIndicator(ch))
- state = RI;
- else if (isHangL(ch))
- state = L;
- else if (hangLV[ch] || isHangV(ch))
- state = V;
- else if (hangLVT[ch])
- state = LVT;
- else if (isHangT(ch))
- state = LVT;
- else
- {
- switch (ch)
- {
- mixin(controlSwitch);
- goto L_End;
- default:
- goto L_End_Extend;
- }
- }
- break;
- case CR:
- if (ch == '\n')
- mixin(eat);
- goto L_End_Extend;
- case RI:
- if (isRegionalIndicator(ch))
- mixin(eat);
- else
- goto L_End_Extend;
- break;
- case L:
- if (isHangL(ch))
- mixin(eat);
- else if (isHangV(ch) || hangLV[ch])
- {
- state = V;
- mixin(eat);
- }
- else if (hangLVT[ch])
- {
- state = LVT;
- mixin(eat);
- }
- else
- goto L_End_Extend;
- break;
- case V:
- if (isHangV(ch))
- mixin(eat);
- else if (isHangT(ch))
- {
- state = LVT;
- mixin(eat);
- }
- else
- goto L_End_Extend;
- break;
- case LVT:
- if (isHangT(ch))
- {
- mixin(eat);
- }
- else
- goto L_End_Extend;
- break;
- }
- }
- L_End_Extend:
- while (!range.empty)
- {
- ch = range.front;
- // extend & spacing marks
- if (!graphemeExtend[ch] && !spacingMark[ch])
- break;
- mixin(eat);
- }
- L_End:
- static if (getValue)
- return grapheme;
- }
-
-}
-
-public: // Public API continues
-
-/++
- Computes the length of grapheme cluster starting at $(D index).
- Both the resulting length and the $(D index) are measured
- in $(S_LINK Code unit, code units).
-
- Params:
- C = type that is implicitly convertible to $(D dchars)
- input = array of grapheme clusters
- index = starting index into $(D input[])
-
- Returns:
- length of grapheme cluster
-+/
-size_t graphemeStride(C)(in C[] input, size_t index)
-if (is(C : dchar))
-{
- auto src = input[index..$];
- auto n = src.length;
- genericDecodeGrapheme!(false)(src);
- return n - src.length;
-}
-
-///
-@safe unittest
-{
- assert(graphemeStride(" ", 1) == 1);
- // A + combing ring above
- string city = "A\u030Arhus";
- size_t first = graphemeStride(city, 0);
- assert(first == 3); //\u030A has 2 UTF-8 code units
- assert(city[0 .. first] == "A\u030A");
- assert(city[first..$] == "rhus");
-}
-
-/++
- Reads one full grapheme cluster from an input range of dchar $(D inp).
-
- For examples see the $(LREF Grapheme) below.
-
- Note:
- This function modifies $(D inp) and thus $(D inp)
- must be an L-value.
-+/
-Grapheme decodeGrapheme(Input)(ref Input inp)
-if (isInputRange!Input && is(Unqual!(ElementType!Input) == dchar))
-{
- return genericDecodeGrapheme!true(inp);
-}
-
-@system unittest
-{
- import std.algorithm.comparison : equal;
-
- Grapheme gr;
- string s = " \u0020\u0308 ";
- gr = decodeGrapheme(s);
- assert(gr.length == 1 && gr[0] == ' ');
- gr = decodeGrapheme(s);
- assert(gr.length == 2 && equal(gr[0 .. 2], " \u0308"));
- s = "\u0300\u0308\u1100";
- assert(equal(decodeGrapheme(s)[], "\u0300\u0308"));
- assert(equal(decodeGrapheme(s)[], "\u1100"));
- s = "\u11A8\u0308\uAC01";
- assert(equal(decodeGrapheme(s)[], "\u11A8\u0308"));
- assert(equal(decodeGrapheme(s)[], "\uAC01"));
-}
-
-/++
- $(P Iterate a string by grapheme.)
-
- $(P Useful for doing string manipulation that needs to be aware
- of graphemes.)
-
- See_Also:
- $(LREF byCodePoint)
-+/
-auto byGrapheme(Range)(Range range)
-if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar))
-{
- // TODO: Bidirectional access
- static struct Result(R)
- {
- private R _range;
- private Grapheme _front;
-
- bool empty() @property
- {
- return _front.length == 0;
- }
-
- Grapheme front() @property
- {
- return _front;
- }
-
- void popFront()
- {
- _front = _range.empty ? Grapheme.init : _range.decodeGrapheme();
- }
-
- static if (isForwardRange!R)
- {
- Result save() @property
- {
- return Result(_range.save, _front);
- }
- }
- }
-
- auto result = Result!(Range)(range);
- result.popFront();
- return result;
-}
-
-///
-@safe unittest
-{
- import std.algorithm.comparison : equal;
- import std.range.primitives : walkLength;
- import std.range : take, drop;
- auto text = "noe\u0308l"; // noël using e + combining diaeresis
- assert(text.walkLength == 5); // 5 code points
-
- auto gText = text.byGrapheme;
- assert(gText.walkLength == 4); // 4 graphemes
-
- assert(gText.take(3).equal("noe\u0308".byGrapheme));
- assert(gText.drop(3).equal("l".byGrapheme));
-}
-
-// For testing non-forward-range input ranges
-version (unittest)
-private static struct InputRangeString
-{
- private string s;
-
- bool empty() @property { return s.empty; }
- dchar front() @property { return s.front; }
- void popFront() { s.popFront(); }
-}
-
-@system unittest
-{
- import std.algorithm.comparison : equal;
- import std.array : array;
- import std.range : retro;
- import std.range.primitives : walkLength;
- assert("".byGrapheme.walkLength == 0);
-
- auto reverse = "le\u0308on";
- assert(reverse.walkLength == 5);
-
- auto gReverse = reverse.byGrapheme;
- assert(gReverse.walkLength == 4);
-
- foreach (text; AliasSeq!("noe\u0308l"c, "noe\u0308l"w, "noe\u0308l"d))
- {
- assert(text.walkLength == 5);
- static assert(isForwardRange!(typeof(text)));
-
- auto gText = text.byGrapheme;
- static assert(isForwardRange!(typeof(gText)));
- assert(gText.walkLength == 4);
- assert(gText.array.retro.equal(gReverse));
- }
-
- auto nonForwardRange = InputRangeString("noe\u0308l").byGrapheme;
- static assert(!isForwardRange!(typeof(nonForwardRange)));
- assert(nonForwardRange.walkLength == 4);
-}
-
-/++
- $(P Lazily transform a range of $(LREF Grapheme)s to a range of code points.)
-
- $(P Useful for converting the result to a string after doing operations
- on graphemes.)
-
- $(P Acts as the identity function when given a range of code points.)
-+/
-auto byCodePoint(Range)(Range range)
-if (isInputRange!Range && is(Unqual!(ElementType!Range) == Grapheme))
-{
- // TODO: Propagate bidirectional access
- static struct Result
- {
- private Range _range;
- private size_t i = 0;
-
- bool empty() @property
- {
- return _range.empty;
- }
-
- dchar front() @property
- {
- return _range.front[i];
- }
-
- void popFront()
- {
- ++i;
-
- if (i >= _range.front.length)
- {
- _range.popFront();
- i = 0;
- }
- }
-
- static if (isForwardRange!Range)
- {
- Result save() @property
- {
- return Result(_range.save, i);
- }
- }
- }
-
- return Result(range);
-}
-
-/// Ditto
-Range byCodePoint(Range)(Range range)
-if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar))
-{
- return range;
-}
-
-///
-@safe unittest
-{
- import std.array : array;
- import std.conv : text;
- import std.range : retro;
-
- string s = "noe\u0308l"; // noël
-
- // reverse it and convert the result to a string
- string reverse = s.byGrapheme
- .array
- .retro
- .byCodePoint
- .text;
-
- assert(reverse == "le\u0308on"); // lëon
-}
-
-@system unittest
-{
- import std.algorithm.comparison : equal;
- import std.range.primitives : walkLength;
- assert("".byGrapheme.byCodePoint.equal(""));
-
- string text = "noe\u0308l";
- static assert(is(typeof(text.byCodePoint) == string));
-
- auto gText = InputRangeString(text).byGrapheme;
- static assert(!isForwardRange!(typeof(gText)));
-
- auto cpText = gText.byCodePoint;
- static assert(!isForwardRange!(typeof(cpText)));
-
- assert(cpText.walkLength == text.walkLength);
-}
-
-@trusted:
-
-/++
- $(P A structure designed to effectively pack $(CHARACTERS)
- of a $(CLUSTER).
- )
-
- $(P $(D Grapheme) has value semantics so 2 copies of a $(D Grapheme)
- always refer to distinct objects. In most actual scenarios a $(D Grapheme)
- fits on the stack and avoids memory allocation overhead for all but quite
- long clusters.
- )
-
- See_Also: $(LREF decodeGrapheme), $(LREF graphemeStride)
-+/
-@trusted struct Grapheme
-{
- import std.traits : isDynamicArray;
-
-public:
- /// Ctor
- this(C)(in C[] chars...)
- if (is(C : dchar))
- {
- this ~= chars;
- }
-
- ///ditto
- this(Input)(Input seq)
- if (!isDynamicArray!Input
- && isInputRange!Input && is(ElementType!Input : dchar))
- {
- this ~= seq;
- }
-
- /// Gets a $(CODEPOINT) at the given index in this cluster.
- dchar opIndex(size_t index) const pure nothrow @nogc
- {
- assert(index < length);
- return read24(isBig ? ptr_ : small_.ptr, index);
- }
-
- /++
- Writes a $(CODEPOINT) $(D ch) at given index in this cluster.
-
- Warning:
- Use of this facility may invalidate grapheme cluster,
- see also $(LREF Grapheme.valid).
- +/
- void opIndexAssign(dchar ch, size_t index) pure nothrow @nogc
- {
- assert(index < length);
- write24(isBig ? ptr_ : small_.ptr, ch, index);
- }
-
- ///
- @safe unittest
- {
- auto g = Grapheme("A\u0302");
- assert(g[0] == 'A');
- assert(g.valid);
- g[1] = '~'; // ASCII tilda is not a combining mark
- assert(g[1] == '~');
- assert(!g.valid);
- }
-
- /++
- Random-access range over Grapheme's $(CHARACTERS).
-
- Warning: Invalidates when this Grapheme leaves the scope,
- attempts to use it then would lead to memory corruption.
- +/
- @system SliceOverIndexed!Grapheme opSlice(size_t a, size_t b) pure nothrow @nogc
- {
- return sliceOverIndexed(a, b, &this);
- }
-
- /// ditto
- @system SliceOverIndexed!Grapheme opSlice() pure nothrow @nogc
- {
- return sliceOverIndexed(0, length, &this);
- }
-
- /// Grapheme cluster length in $(CODEPOINTS).
- @property size_t length() const pure nothrow @nogc
- {
- return isBig ? len_ : slen_ & 0x7F;
- }
-
- /++
- Append $(CHARACTER) $(D ch) to this grapheme.
- Warning:
- Use of this facility may invalidate grapheme cluster,
- see also $(D valid).
-
- See_Also: $(LREF Grapheme.valid)
- +/
- ref opOpAssign(string op)(dchar ch)
- {
- static if (op == "~")
- {
- if (!isBig)
- {
- if (slen_ == small_cap)
- convertToBig();// & fallthrough to "big" branch
- else
- {
- write24(small_.ptr, ch, smallLength);
- slen_++;
- return this;
- }
- }
-
- assert(isBig);
- if (len_ == cap_)
- {
- import core.checkedint : addu, mulu;
- bool overflow;
- cap_ = addu(cap_, grow, overflow);
- auto nelems = mulu(3, addu(cap_, 1, overflow), overflow);
- if (overflow) assert(0);
- ptr_ = cast(ubyte*) pureRealloc(ptr_, nelems);
- if (ptr_ is null) onOutOfMemoryError();
- }
- write24(ptr_, ch, len_++);
- return this;
- }
- else
- static assert(false, "No operation "~op~" defined for Grapheme");
- }
-
- ///
- @system unittest
- {
- import std.algorithm.comparison : equal;
- auto g = Grapheme("A");
- assert(g.valid);
- g ~= '\u0301';
- assert(g[].equal("A\u0301"));
- assert(g.valid);
- g ~= "B";
- // not a valid grapheme cluster anymore
- assert(!g.valid);
- // still could be useful though
- assert(g[].equal("A\u0301B"));
- }
-
- /// Append all $(CHARACTERS) from the input range $(D inp) to this Grapheme.
- ref opOpAssign(string op, Input)(Input inp)
- if (isInputRange!Input && is(ElementType!Input : dchar))
- {
- static if (op == "~")
- {
- foreach (dchar ch; inp)
- this ~= ch;
- return this;
- }
- else
- static assert(false, "No operation "~op~" defined for Grapheme");
- }
-
- /++
- True if this object contains valid extended grapheme cluster.
- Decoding primitives of this module always return a valid $(D Grapheme).
-
- Appending to and direct manipulation of grapheme's $(CHARACTERS) may
- render it no longer valid. Certain applications may chose to use
- Grapheme as a "small string" of any $(CODEPOINTS) and ignore this property
- entirely.
- +/
- @property bool valid()() /*const*/
- {
- auto r = this[];
- genericDecodeGrapheme!false(r);
- return r.length == 0;
- }
-
- this(this) pure @nogc nothrow
- {
- if (isBig)
- {// dup it
- import core.checkedint : addu, mulu;
- bool overflow;
- auto raw_cap = mulu(3, addu(cap_, 1, overflow), overflow);
- if (overflow) assert(0);
-
- auto p = cast(ubyte*) pureMalloc(raw_cap);
- if (p is null) onOutOfMemoryError();
- p[0 .. raw_cap] = ptr_[0 .. raw_cap];
- ptr_ = p;
- }
- }
-
- ~this() pure @nogc nothrow
- {
- if (isBig)
- {
- pureFree(ptr_);
- }
- }
-
-
-private:
- enum small_bytes = ((ubyte*).sizeof+3*size_t.sizeof-1);
- // "out of the blue" grow rate, needs testing
- // (though graphemes are typically small < 9)
- enum grow = 20;
- enum small_cap = small_bytes/3;
- enum small_flag = 0x80, small_mask = 0x7F;
- // 16 bytes in 32bits, should be enough for the majority of cases
- union
- {
- struct
- {
- ubyte* ptr_;
- size_t cap_;
- size_t len_;
- size_t padding_;
- }
- struct
- {
- ubyte[small_bytes] small_;
- ubyte slen_;
- }
- }
-
- void convertToBig() pure @nogc nothrow
- {
- static assert(grow.max / 3 - 1 >= grow);
- enum nbytes = 3 * (grow + 1);
- size_t k = smallLength;
- ubyte* p = cast(ubyte*) pureMalloc(nbytes);
- if (p is null) onOutOfMemoryError();
- for (int i=0; i<k; i++)
- write24(p, read24(small_.ptr, i), i);
- // now we can overwrite small array data
- ptr_ = p;
- len_ = slen_;
- assert(grow > len_);
- cap_ = grow;
- setBig();
- }
-
- void setBig() pure nothrow @nogc { slen_ |= small_flag; }
-
- @property size_t smallLength() const pure nothrow @nogc
- {
- return slen_ & small_mask;
- }
- @property ubyte isBig() const pure nothrow @nogc
- {
- return slen_ & small_flag;
- }
-}
-
-static assert(Grapheme.sizeof == size_t.sizeof*4);
-
-
-@system pure /*nothrow @nogc*/ unittest // TODO: string .front is GC and throw
-{
- import std.algorithm.comparison : equal;
- Grapheme[3] data = [Grapheme("Ю"), Grapheme("У"), Grapheme("З")];
- assert(byGrapheme("ЮУЗ").equal(data[]));
-}
-
-///
-@system unittest
-{
- import std.algorithm.comparison : equal;
- import std.algorithm.iteration : filter;
- import std.range : isRandomAccessRange;
-
- string bold = "ku\u0308hn";
-
- // note that decodeGrapheme takes parameter by ref
- auto first = decodeGrapheme(bold);
-
- assert(first.length == 1);
- assert(first[0] == 'k');
-
- // the next grapheme is 2 characters long
- auto wideOne = decodeGrapheme(bold);
- // slicing a grapheme yields a random-access range of dchar
- assert(wideOne[].equal("u\u0308"));
- assert(wideOne.length == 2);
- static assert(isRandomAccessRange!(typeof(wideOne[])));
-
- // all of the usual range manipulation is possible
- assert(wideOne[].filter!isMark().equal("\u0308"));
-
- auto g = Grapheme("A");
- assert(g.valid);
- g ~= '\u0301';
- assert(g[].equal("A\u0301"));
- assert(g.valid);
- g ~= "B";
- // not a valid grapheme cluster anymore
- assert(!g.valid);
- // still could be useful though
- assert(g[].equal("A\u0301B"));
-}
-
-@safe unittest
-{
- auto g = Grapheme("A\u0302");
- assert(g[0] == 'A');
- assert(g.valid);
- g[1] = '~'; // ASCII tilda is not a combining mark
- assert(g[1] == '~');
- assert(!g.valid);
-}
-
-@system unittest
-{
- import std.algorithm.comparison : equal;
- import std.algorithm.iteration : map;
- import std.conv : text;
- import std.range : iota;
-
- // not valid clusters (but it just a test)
- auto g = Grapheme('a', 'b', 'c', 'd', 'e');
- assert(g[0] == 'a');
- assert(g[1] == 'b');
- assert(g[2] == 'c');
- assert(g[3] == 'd');
- assert(g[4] == 'e');
- g[3] = 'Й';
- assert(g[2] == 'c');
- assert(g[3] == 'Й', text(g[3], " vs ", 'Й'));
- assert(g[4] == 'e');
- assert(!g.valid);
-
- g ~= 'ц';
- g ~= '~';
- assert(g[0] == 'a');
- assert(g[1] == 'b');
- assert(g[2] == 'c');
- assert(g[3] == 'Й');
- assert(g[4] == 'e');
- assert(g[5] == 'ц');
- assert(g[6] == '~');
- assert(!g.valid);
-
- Grapheme copy = g;
- copy[0] = 'X';
- copy[1] = '-';
- assert(g[0] == 'a' && copy[0] == 'X');
- assert(g[1] == 'b' && copy[1] == '-');
- assert(equal(g[2 .. g.length], copy[2 .. copy.length]));
- copy = Grapheme("ÐБВГДЕÐЖЗИКЛМ");
- assert(equal(copy[0 .. 8], "ÐБВГДЕÐЖ"), text(copy[0 .. 8]));
- copy ~= "xyz";
- assert(equal(copy[13 .. 15], "xy"), text(copy[13 .. 15]));
- assert(!copy.valid);
-
- Grapheme h;
- foreach (dchar v; iota(cast(int)'A', cast(int)'Z'+1).map!"cast(dchar)a"())
- h ~= v;
- assert(equal(h[], iota(cast(int)'A', cast(int)'Z'+1)));
-}
-
-/++
- $(P Does basic case-insensitive comparison of $(D r1) and $(D r2).
- This function uses simpler comparison rule thus achieving better performance
- than $(LREF icmp). However keep in mind the warning below.)
-
- Params:
- r1 = an input range of characters
- r2 = an input range of characters
-
- Returns:
- An $(D int) that is 0 if the strings match,
- &lt;0 if $(D r1) is lexicographically "less" than $(D r2),
- &gt;0 if $(D r1) is lexicographically "greater" than $(D r2)
-
- Warning:
- This function only handles 1:1 $(CODEPOINT) mapping
- and thus is not sufficient for certain alphabets
- like German, Greek and few others.
-
- See_Also:
- $(LREF icmp)
- $(REF cmp, std,algorithm,comparison)
-+/
-int sicmp(S1, S2)(S1 r1, S2 r2)
-if (isInputRange!S1 && isSomeChar!(ElementEncodingType!S1)
- && isInputRange!S2 && isSomeChar!(ElementEncodingType!S2))
-{
- import std.internal.unicode_tables : sTable = simpleCaseTable; // generated file
- import std.utf : byDchar;
-
- auto str1 = r1.byDchar;
- auto str2 = r2.byDchar;
-
- foreach (immutable lhs; str1)
- {
- if (str2.empty)
- return 1;
- immutable rhs = str2.front;
- str2.popFront();
- int diff = lhs - rhs;
- if (!diff)
- continue;
- size_t idx = simpleCaseTrie[lhs];
- size_t idx2 = simpleCaseTrie[rhs];
- // simpleCaseTrie is packed index table
- if (idx != EMPTY_CASE_TRIE)
- {
- if (idx2 != EMPTY_CASE_TRIE)
- {// both cased chars
- // adjust idx --> start of bucket
- idx = idx - sTable[idx].n;
- idx2 = idx2 - sTable[idx2].n;
- if (idx == idx2)// one bucket, equivalent chars
- continue;
- else// not the same bucket
- diff = sTable[idx].ch - sTable[idx2].ch;
- }
- else
- diff = sTable[idx - sTable[idx].n].ch - rhs;
- }
- else if (idx2 != EMPTY_CASE_TRIE)
- {
- diff = lhs - sTable[idx2 - sTable[idx2].n].ch;
- }
- // one of chars is not cased at all
- return diff;
- }
- return str2.empty ? 0 : -1;
-}
-
-///
-@safe @nogc pure nothrow unittest
-{
- assert(sicmp("ÐвгуÑÑ‚", "авгуÑТ") == 0);
- // Greek also works as long as there is no 1:M mapping in sight
- assert(sicmp("ΌΎ", "ÏŒÏ") == 0);
- // things like the following won't get matched as equal
- // Greek small letter iota with dialytika and tonos
- assert(sicmp("Î", "\u03B9\u0308\u0301") != 0);
-
- // while icmp has no problem with that
- assert(icmp("Î", "\u03B9\u0308\u0301") == 0);
- assert(icmp("ΌΎ", "ÏŒÏ") == 0);
-}
-
-// overloads for the most common cases to reduce compile time
-@safe @nogc pure nothrow
-{
- int sicmp(const(char)[] str1, const(char)[] str2)
- { return sicmp!(const(char)[], const(char)[])(str1, str2); }
- int sicmp(const(wchar)[] str1, const(wchar)[] str2)
- { return sicmp!(const(wchar)[], const(wchar)[])(str1, str2); }
- int sicmp(const(dchar)[] str1, const(dchar)[] str2)
- { return sicmp!(const(dchar)[], const(dchar)[])(str1, str2); }
-}
-
-private int fullCasedCmp(Range)(dchar lhs, dchar rhs, ref Range rtail)
-{
- import std.algorithm.searching : skipOver;
- import std.internal.unicode_tables : fullCaseTable; // generated file
- alias fTable = fullCaseTable;
- size_t idx = fullCaseTrie[lhs];
- // fullCaseTrie is packed index table
- if (idx == EMPTY_CASE_TRIE)
- return lhs;
- immutable start = idx - fTable[idx].n;
- immutable end = fTable[idx].size + start;
- assert(fTable[start].entry_len == 1);
- for (idx=start; idx<end; idx++)
- {
- auto entryLen = fTable[idx].entry_len;
- if (entryLen == 1)
- {
- if (fTable[idx].seq[0] == rhs)
- {
- return 0;
- }
- }
- else
- {// OK it's a long chunk, like 'ss' for German
- dstring seq = fTable[idx].seq[0 .. entryLen];
- if (rhs == seq[0]
- && rtail.skipOver(seq[1..$]))
- {
- // note that this path modifies rtail
- // iff we managed to get there
- return 0;
- }
- }
- }
- return fTable[start].seq[0]; // new remapped character for accurate diffs
-}
-
-/++
- Does case insensitive comparison of `r1` and `r2`.
- Follows the rules of full case-folding mapping.
- This includes matching as equal german ß with "ss" and
- other 1:M $(CODEPOINT) mappings unlike $(LREF sicmp).
- The cost of `icmp` being pedantically correct is
- slightly worse performance.
-
- Params:
- r1 = a forward range of characters
- r2 = a forward range of characters
-
- Returns:
- An $(D int) that is 0 if the strings match,
- &lt;0 if $(D str1) is lexicographically "less" than $(D str2),
- &gt;0 if $(D str1) is lexicographically "greater" than $(D str2)
-
- See_Also:
- $(LREF sicmp)
- $(REF cmp, std,algorithm,comparison)
-+/
-int icmp(S1, S2)(S1 r1, S2 r2)
-if (isForwardRange!S1 && isSomeChar!(ElementEncodingType!S1)
- && isForwardRange!S2 && isSomeChar!(ElementEncodingType!S2))
-{
- import std.utf : byDchar;
-
- auto str1 = r1.byDchar;
- auto str2 = r2.byDchar;
-
- for (;;)
- {
- if (str1.empty)
- return str2.empty ? 0 : -1;
- immutable lhs = str1.front;
- if (str2.empty)
- return 1;
- immutable rhs = str2.front;
- str1.popFront();
- str2.popFront();
- if (!(lhs - rhs))
- continue;
- // first try to match lhs to <rhs,right-tail> sequence
- immutable cmpLR = fullCasedCmp(lhs, rhs, str2);
- if (!cmpLR)
- continue;
- // then rhs to <lhs,left-tail> sequence
- immutable cmpRL = fullCasedCmp(rhs, lhs, str1);
- if (!cmpRL)
- continue;
- // cmpXX contain remapped codepoints
- // to obtain stable ordering of icmp
- return cmpLR - cmpRL;
- }
-}
-
-///
-@safe @nogc pure nothrow unittest
-{
- assert(icmp("Rußland", "Russland") == 0);
- assert(icmp("ᾩ -> \u1F70\u03B9", "\u1F61\u03B9 -> ᾲ") == 0);
-}
-
-/**
- * By using $(REF byUTF, std,utf) and its aliases, GC allocations via auto-decoding
- * and thrown exceptions can be avoided, making `icmp` `@safe @nogc nothrow pure`.
- */
-@safe @nogc nothrow pure unittest
-{
- import std.utf : byDchar;
-
- assert(icmp("Rußland".byDchar, "Russland".byDchar) == 0);
- assert(icmp("ᾩ -> \u1F70\u03B9".byDchar, "\u1F61\u03B9 -> ᾲ".byDchar) == 0);
-}
-
-// test different character types
-@safe unittest
-{
- assert(icmp("Rußland", "Russland") == 0);
- assert(icmp("Rußland"w, "Russland") == 0);
- assert(icmp("Rußland", "Russland"w) == 0);
- assert(icmp("Rußland"w, "Russland"w) == 0);
- assert(icmp("Rußland"d, "Russland"w) == 0);
- assert(icmp("Rußland"w, "Russland"d) == 0);
-}
-
-// overloads for the most common cases to reduce compile time
-@safe @nogc pure nothrow
-{
- int icmp(const(char)[] str1, const(char)[] str2)
- { return icmp!(const(char)[], const(char)[])(str1, str2); }
- int icmp(const(wchar)[] str1, const(wchar)[] str2)
- { return icmp!(const(wchar)[], const(wchar)[])(str1, str2); }
- int icmp(const(dchar)[] str1, const(dchar)[] str2)
- { return icmp!(const(dchar)[], const(dchar)[])(str1, str2); }
-}
-
-@safe unittest
-{
- import std.algorithm.sorting : sort;
- import std.conv : to;
- import std.exception : assertCTFEable;
- assertCTFEable!(
- {
- foreach (cfunc; AliasSeq!(icmp, sicmp))
- {
- foreach (S1; AliasSeq!(string, wstring, dstring))
- foreach (S2; AliasSeq!(string, wstring, dstring))
- (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
- assert(cfunc("".to!S1(), "".to!S2()) == 0);
- assert(cfunc("A".to!S1(), "".to!S2()) > 0);
- assert(cfunc("".to!S1(), "0".to!S2()) < 0);
- assert(cfunc("abc".to!S1(), "abc".to!S2()) == 0);
- assert(cfunc("abcd".to!S1(), "abc".to!S2()) > 0);
- assert(cfunc("abc".to!S1(), "abcd".to!S2()) < 0);
- assert(cfunc("Abc".to!S1(), "aBc".to!S2()) == 0);
- assert(cfunc("авГуÑÑ‚".to!S1(), "ÐВгУСТ".to!S2()) == 0);
- // Check example:
- assert(cfunc("ÐвгуÑÑ‚".to!S1(), "авгуÑТ".to!S2()) == 0);
- assert(cfunc("ΌΎ".to!S1(), "ÏŒÏ".to!S2()) == 0);
- }();
- // check that the order is properly agnostic to the case
- auto strs = [ "Apple", "ORANGE", "orAcle", "amp", "banana"];
- sort!((a,b) => cfunc(a,b) < 0)(strs);
- assert(strs == ["amp", "Apple", "banana", "orAcle", "ORANGE"]);
- }
- assert(icmp("ßb", "ssa") > 0);
- // Check example:
- assert(icmp("Russland", "Rußland") == 0);
- assert(icmp("ᾩ -> \u1F70\u03B9", "\u1F61\u03B9 -> ᾲ") == 0);
- assert(icmp("Î"w, "\u03B9\u0308\u0301") == 0);
- assert(sicmp("Î", "\u03B9\u0308\u0301") != 0);
- //bugzilla 11057
- assert( icmp("K", "L") < 0 );
- });
-}
-
-// issue 17372
-@safe pure unittest
-{
- import std.algorithm.iteration : joiner, map;
- import std.algorithm.sorting : sort;
- import std.array : array;
- auto a = [["foo", "bar"], ["baz"]].map!(line => line.joiner(" ")).array.sort!((a, b) => icmp(a, b) < 0);
-}
-
-// This is package for the moment to be used as a support tool for std.regex
-// It needs a better API
-/*
- Return a range of all $(CODEPOINTS) that casefold to
- and from this $(D ch).
-*/
-package auto simpleCaseFoldings(dchar ch) @safe
-{
- import std.internal.unicode_tables : simpleCaseTable; // generated file
- alias sTable = simpleCaseTable;
- static struct Range
- {
- @safe pure nothrow:
- uint idx; //if == uint.max, then read c.
- union
- {
- dchar c; // == 0 - empty range
- uint len;
- }
- @property bool isSmall() const { return idx == uint.max; }
-
- this(dchar ch)
- {
- idx = uint.max;
- c = ch;
- }
-
- this(uint start, uint size)
- {
- idx = start;
- len = size;
- }
-
- @property dchar front() const
- {
- assert(!empty);
- if (isSmall)
- {
- return c;
- }
- auto ch = sTable[idx].ch;
- return ch;
- }
-
- @property bool empty() const
- {
- if (isSmall)
- {
- return c == 0;
- }
- return len == 0;
- }
-
- @property size_t length() const
- {
- if (isSmall)
- {
- return c == 0 ? 0 : 1;
- }
- return len;
- }
-
- void popFront()
- {
- if (isSmall)
- c = 0;
- else
- {
- idx++;
- len--;
- }
- }
- }
- immutable idx = simpleCaseTrie[ch];
- if (idx == EMPTY_CASE_TRIE)
- return Range(ch);
- auto entry = sTable[idx];
- immutable start = idx - entry.n;
- return Range(start, entry.size);
-}
-
-@system unittest
-{
- import std.algorithm.comparison : equal;
- import std.algorithm.searching : canFind;
- import std.array : array;
- import std.exception : assertCTFEable;
- assertCTFEable!((){
- auto r = simpleCaseFoldings('Э').array;
- assert(r.length == 2);
- assert(r.canFind('Ñ') && r.canFind('Э'));
- auto sr = simpleCaseFoldings('~');
- assert(sr.equal("~"));
- //A with ring above - casefolds to the same bucket as Angstrom sign
- sr = simpleCaseFoldings('Ã…');
- assert(sr.length == 3);
- assert(sr.canFind('Ã¥') && sr.canFind('Ã…') && sr.canFind('\u212B'));
- });
-}
-
-/++
- $(P Returns the $(S_LINK Combining class, combining class) of $(D ch).)
-+/
-ubyte combiningClass(dchar ch) @safe pure nothrow @nogc
-{
- return combiningClassTrie[ch];
-}
-
-///
-@safe unittest
-{
- // shorten the code
- alias CC = combiningClass;
-
- // combining tilda
- assert(CC('\u0303') == 230);
- // combining ring below
- assert(CC('\u0325') == 220);
- // the simple consequence is that "tilda" should be
- // placed after a "ring below" in a sequence
-}
-
-@safe pure nothrow @nogc unittest
-{
- foreach (ch; 0 .. 0x80)
- assert(combiningClass(ch) == 0);
- assert(combiningClass('\u05BD') == 22);
- assert(combiningClass('\u0300') == 230);
- assert(combiningClass('\u0317') == 220);
- assert(combiningClass('\u1939') == 222);
-}
-
-/// Unicode character decomposition type.
-enum UnicodeDecomposition {
- /// Canonical decomposition. The result is canonically equivalent sequence.
- Canonical,
- /**
- Compatibility decomposition. The result is compatibility equivalent sequence.
- Note: Compatibility decomposition is a $(B lossy) conversion,
- typically suitable only for fuzzy matching and internal processing.
- */
- Compatibility
-}
-
-/**
- Shorthand aliases for character decomposition type, passed as a
- template parameter to $(LREF decompose).
-*/
-enum {
- Canonical = UnicodeDecomposition.Canonical,
- Compatibility = UnicodeDecomposition.Compatibility
-}
-
-/++
- Try to canonically compose 2 $(CHARACTERS).
- Returns the composed $(CHARACTER) if they do compose and dchar.init otherwise.
-
- The assumption is that $(D first) comes before $(D second) in the original text,
- usually meaning that the first is a starter.
-
- Note: Hangul syllables are not covered by this function.
- See $(D composeJamo) below.
-+/
-public dchar compose(dchar first, dchar second) pure nothrow @safe
-{
- import std.algorithm.iteration : map;
- import std.internal.unicode_comp : compositionTable, composeCntShift, composeIdxMask;
- import std.range : assumeSorted;
- immutable packed = compositionJumpTrie[first];
- if (packed == ushort.max)
- return dchar.init;
- // unpack offset and length
- immutable idx = packed & composeIdxMask, cnt = packed >> composeCntShift;
- // TODO: optimize this micro binary search (no more then 4-5 steps)
- auto r = compositionTable[idx .. idx+cnt].map!"a.rhs"().assumeSorted();
- immutable target = r.lowerBound(second).length;
- if (target == cnt)
- return dchar.init;
- immutable entry = compositionTable[idx+target];
- if (entry.rhs != second)
- return dchar.init;
- return entry.composed;
-}
-
-///
-@safe unittest
-{
- assert(compose('A','\u0308') == '\u00C4');
- assert(compose('A', 'B') == dchar.init);
- assert(compose('C', '\u0301') == '\u0106');
- // note that the starter is the first one
- // thus the following doesn't compose
- assert(compose('\u0308', 'A') == dchar.init);
-}
-
-/++
- Returns a full $(S_LINK Canonical decomposition, Canonical)
- (by default) or $(S_LINK Compatibility decomposition, Compatibility)
- decomposition of $(CHARACTER) $(D ch).
- If no decomposition is available returns a $(LREF Grapheme)
- with the $(D ch) itself.
-
- Note:
- This function also decomposes hangul syllables
- as prescribed by the standard.
-
- See_Also: $(LREF decomposeHangul) for a restricted version
- that takes into account only hangul syllables but
- no other decompositions.
-+/
-public Grapheme decompose(UnicodeDecomposition decompType=Canonical)(dchar ch) @safe
-{
- import std.algorithm.searching : until;
- import std.internal.unicode_decomp : decompCompatTable, decompCanonTable;
- static if (decompType == Canonical)
- {
- alias table = decompCanonTable;
- alias mapping = canonMappingTrie;
- }
- else static if (decompType == Compatibility)
- {
- alias table = decompCompatTable;
- alias mapping = compatMappingTrie;
- }
- immutable idx = mapping[ch];
- if (!idx) // not found, check hangul arithmetic decomposition
- return decomposeHangul(ch);
- auto decomp = table[idx..$].until(0);
- return Grapheme(decomp);
-}
-
-///
-@system unittest
-{
- import std.algorithm.comparison : equal;
-
- assert(compose('A','\u0308') == '\u00C4');
- assert(compose('A', 'B') == dchar.init);
- assert(compose('C', '\u0301') == '\u0106');
- // note that the starter is the first one
- // thus the following doesn't compose
- assert(compose('\u0308', 'A') == dchar.init);
-
- assert(decompose('Ĉ')[].equal("C\u0302"));
- assert(decompose('D')[].equal("D"));
- assert(decompose('\uD4DC')[].equal("\u1111\u1171\u11B7"));
- assert(decompose!Compatibility('¹')[].equal("1"));
-}
-
-//----------------------------------------------------------------------------
-// Hangul specific composition/decomposition
-enum jamoSBase = 0xAC00;
-enum jamoLBase = 0x1100;
-enum jamoVBase = 0x1161;
-enum jamoTBase = 0x11A7;
-enum jamoLCount = 19, jamoVCount = 21, jamoTCount = 28;
-enum jamoNCount = jamoVCount * jamoTCount;
-enum jamoSCount = jamoLCount * jamoNCount;
-
-// Tests if $(D ch) is a Hangul leading consonant jamo.
-bool isJamoL(dchar ch) pure nothrow @nogc @safe
-{
- // first cmp rejects ~ 1M code points above leading jamo range
- return ch < jamoLBase+jamoLCount && ch >= jamoLBase;
-}
-
-// Tests if $(D ch) is a Hangul vowel jamo.
-bool isJamoT(dchar ch) pure nothrow @nogc @safe
-{
- // first cmp rejects ~ 1M code points above trailing jamo range
- // Note: ch == jamoTBase doesn't indicate trailing jamo (TIndex must be > 0)
- return ch < jamoTBase+jamoTCount && ch > jamoTBase;
-}
-
-// Tests if $(D ch) is a Hangul trailnig consonant jamo.
-bool isJamoV(dchar ch) pure nothrow @nogc @safe
-{
- // first cmp rejects ~ 1M code points above vowel range
- return ch < jamoVBase+jamoVCount && ch >= jamoVBase;
-}
-
-int hangulSyllableIndex(dchar ch) pure nothrow @nogc @safe
-{
- int idxS = cast(int) ch - jamoSBase;
- return idxS >= 0 && idxS < jamoSCount ? idxS : -1;
-}
-
-// internal helper: compose hangul syllables leaving dchar.init in holes
-void hangulRecompose(dchar[] seq) pure nothrow @nogc @safe
-{
- for (size_t idx = 0; idx + 1 < seq.length; )
- {
- if (isJamoL(seq[idx]) && isJamoV(seq[idx+1]))
- {
- immutable int indexL = seq[idx] - jamoLBase;
- immutable int indexV = seq[idx+1] - jamoVBase;
- immutable int indexLV = indexL * jamoNCount + indexV * jamoTCount;
- if (idx + 2 < seq.length && isJamoT(seq[idx+2]))
- {
- seq[idx] = jamoSBase + indexLV + seq[idx+2] - jamoTBase;
- seq[idx+1] = dchar.init;
- seq[idx+2] = dchar.init;
- idx += 3;
- }
- else
- {
- seq[idx] = jamoSBase + indexLV;
- seq[idx+1] = dchar.init;
- idx += 2;
- }
- }
- else
- idx++;
- }
-}
-
-//----------------------------------------------------------------------------
-public:
-
-/**
- Decomposes a Hangul syllable. If $(D ch) is not a composed syllable
- then this function returns $(LREF Grapheme) containing only $(D ch) as is.
-*/
-Grapheme decomposeHangul(dchar ch) @safe
-{
- immutable idxS = cast(int) ch - jamoSBase;
- if (idxS < 0 || idxS >= jamoSCount) return Grapheme(ch);
- immutable idxL = idxS / jamoNCount;
- immutable idxV = (idxS % jamoNCount) / jamoTCount;
- immutable idxT = idxS % jamoTCount;
-
- immutable partL = jamoLBase + idxL;
- immutable partV = jamoVBase + idxV;
- if (idxT > 0) // there is a trailling consonant (T); <L,V,T> decomposition
- return Grapheme(partL, partV, jamoTBase + idxT);
- else // <L, V> decomposition
- return Grapheme(partL, partV);
-}
-
-///
-@system unittest
-{
- import std.algorithm.comparison : equal;
- assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6"));
-}
-
-/++
- Try to compose hangul syllable out of a leading consonant ($(D lead)),
- a $(D vowel) and optional $(D trailing) consonant jamos.
-
- On success returns the composed LV or LVT hangul syllable.
-
- If any of $(D lead) and $(D vowel) are not a valid hangul jamo
- of the respective $(CHARACTER) class returns dchar.init.
-+/
-dchar composeJamo(dchar lead, dchar vowel, dchar trailing=dchar.init) pure nothrow @nogc @safe
-{
- if (!isJamoL(lead))
- return dchar.init;
- immutable indexL = lead - jamoLBase;
- if (!isJamoV(vowel))
- return dchar.init;
- immutable indexV = vowel - jamoVBase;
- immutable indexLV = indexL * jamoNCount + indexV * jamoTCount;
- immutable dchar syllable = jamoSBase + indexLV;
- return isJamoT(trailing) ? syllable + (trailing - jamoTBase) : syllable;
-}
-
-///
-@safe unittest
-{
- assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB');
- // leaving out T-vowel, or passing any codepoint
- // that is not trailing consonant composes an LV-syllable
- assert(composeJamo('\u1111', '\u1171') == '\uD4CC');
- assert(composeJamo('\u1111', '\u1171', ' ') == '\uD4CC');
- assert(composeJamo('\u1111', 'A') == dchar.init);
- assert(composeJamo('A', '\u1171') == dchar.init);
-}
-
-@system unittest
-{
- import std.algorithm.comparison : equal;
- import std.conv : text;
-
- static void testDecomp(UnicodeDecomposition T)(dchar ch, string r)
- {
- Grapheme g = decompose!T(ch);
- assert(equal(g[], r), text(g[], " vs ", r));
- }
- testDecomp!Canonical('\u1FF4', "\u03C9\u0301\u0345");
- testDecomp!Canonical('\uF907', "\u9F9C");
- testDecomp!Compatibility('\u33FF', "\u0067\u0061\u006C");
- testDecomp!Compatibility('\uA7F9', "\u0153");
-
- // check examples
- assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6"));
- assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB');
- assert(composeJamo('\u1111', '\u1171') == '\uD4CC'); // leave out T-vowel
- assert(composeJamo('\u1111', '\u1171', ' ') == '\uD4CC');
- assert(composeJamo('\u1111', 'A') == dchar.init);
- assert(composeJamo('A', '\u1171') == dchar.init);
-}
-
-/**
- Enumeration type for normalization forms,
- passed as template parameter for functions like $(LREF normalize).
-*/
-enum NormalizationForm {
- NFC,
- NFD,
- NFKC,
- NFKD
-}
-
-
-enum {
- /**
- Shorthand aliases from values indicating normalization forms.
- */
- NFC = NormalizationForm.NFC,
- ///ditto
- NFD = NormalizationForm.NFD,
- ///ditto
- NFKC = NormalizationForm.NFKC,
- ///ditto
- NFKD = NormalizationForm.NFKD
-}
-
-/++
- Returns $(D input) string normalized to the chosen form.
- Form C is used by default.
-
- For more information on normalization forms see
- the $(S_LINK Normalization, normalization section).
-
- Note:
- In cases where the string in question is already normalized,
- it is returned unmodified and no memory allocation happens.
-+/
-inout(C)[] normalize(NormalizationForm norm=NFC, C)(inout(C)[] input)
-{
- import std.algorithm.mutation : SwapStrategy;
- import std.algorithm.sorting : sort;
- import std.array : appender;
- import std.range : zip;
-
- auto anchors = splitNormalized!norm(input);
- if (anchors[0] == input.length && anchors[1] == input.length)
- return input;
- dchar[] decomposed;
- decomposed.reserve(31);
- ubyte[] ccc;
- ccc.reserve(31);
- auto app = appender!(C[])();
- do
- {
- app.put(input[0 .. anchors[0]]);
- foreach (dchar ch; input[anchors[0]..anchors[1]])
- static if (norm == NFD || norm == NFC)
- {
- foreach (dchar c; decompose!Canonical(ch)[])
- decomposed ~= c;
- }
- else // NFKD & NFKC
- {
- foreach (dchar c; decompose!Compatibility(ch)[])
- decomposed ~= c;
- }
- ccc.length = decomposed.length;
- size_t firstNonStable = 0;
- ubyte lastClazz = 0;
-
- foreach (idx, dchar ch; decomposed)
- {
- immutable clazz = combiningClass(ch);
- ccc[idx] = clazz;
- if (clazz == 0 && lastClazz != 0)
- {
- // found a stable code point after unstable ones
- sort!("a[0] < b[0]", SwapStrategy.stable)
- (zip(ccc[firstNonStable .. idx], decomposed[firstNonStable .. idx]));
- firstNonStable = decomposed.length;
- }
- else if (clazz != 0 && lastClazz == 0)
- {
- // found first unstable code point after stable ones
- firstNonStable = idx;
- }
- lastClazz = clazz;
- }
- sort!("a[0] < b[0]", SwapStrategy.stable)
- (zip(ccc[firstNonStable..$], decomposed[firstNonStable..$]));
- static if (norm == NFC || norm == NFKC)
- {
- import std.algorithm.searching : countUntil;
- auto first = countUntil(ccc, 0);
- if (first >= 0) // no starters?? no recomposition
- {
- for (;;)
- {
- immutable second = recompose(first, decomposed, ccc);
- if (second == decomposed.length)
- break;
- first = second;
- }
- // 2nd pass for hangul syllables
- hangulRecompose(decomposed);
- }
- }
- static if (norm == NFD || norm == NFKD)
- app.put(decomposed);
- else
- {
- import std.algorithm.mutation : remove;
- auto clean = remove!("a == dchar.init", SwapStrategy.stable)(decomposed);
- app.put(decomposed[0 .. clean.length]);
- }
- // reset variables
- decomposed.length = 0;
- decomposed.assumeSafeAppend();
- ccc.length = 0;
- ccc.assumeSafeAppend();
- input = input[anchors[1]..$];
- // and move on
- anchors = splitNormalized!norm(input);
- }while (anchors[0] != input.length);
- app.put(input[0 .. anchors[0]]);
- return cast(inout(C)[])app.data;
-}
-
-///
-@safe unittest
-{
- // any encoding works
- wstring greet = "Hello world";
- assert(normalize(greet) is greet); // the same exact slice
-
- // An example of a character with all 4 forms being different:
- // Greek upsilon with acute and hook symbol (code point 0x03D3)
- assert(normalize!NFC("Ï“") == "\u03D3");
- assert(normalize!NFD("Ï“") == "\u03D2\u0301");
- assert(normalize!NFKC("Ï“") == "\u038E");
- assert(normalize!NFKD("Ï“") == "\u03A5\u0301");
-}
-
-@safe unittest
-{
- import std.conv : text;
-
- assert(normalize!NFD("abc\uF904def") == "abc\u6ED1def", text(normalize!NFD("abc\uF904def")));
- assert(normalize!NFKD("2¹â°") == "210", normalize!NFKD("2¹â°"));
- assert(normalize!NFD("Äffin") == "A\u0308ffin");
-
- // check example
-
- // any encoding works
- wstring greet = "Hello world";
- assert(normalize(greet) is greet); // the same exact slice
-
- // An example of a character with all 4 forms being different:
- // Greek upsilon with acute and hook symbol (code point 0x03D3)
- assert(normalize!NFC("Ï“") == "\u03D3");
- assert(normalize!NFD("Ï“") == "\u03D2\u0301");
- assert(normalize!NFKC("Ï“") == "\u038E");
- assert(normalize!NFKD("Ï“") == "\u03A5\u0301");
-}
-
-// canonically recompose given slice of code points, works in-place and mutates data
-private size_t recompose(size_t start, dchar[] input, ubyte[] ccc) pure nothrow @safe
-{
- assert(input.length == ccc.length);
- int accumCC = -1;// so that it's out of 0 .. 255 range
- // writefln("recomposing %( %04x %)", input);
- // first one is always a starter thus we start at i == 1
- size_t i = start+1;
- for (; ; )
- {
- if (i == input.length)
- break;
- immutable curCC = ccc[i];
- // In any character sequence beginning with a starter S
- // a character C is blocked from S if and only if there
- // is some character B between S and C, and either B
- // is a starter or it has the same or higher combining class as C.
- //------------------------
- // Applying to our case:
- // S is input[0]
- // accumCC is the maximum CCC of characters between C and S,
- // as ccc are sorted
- // C is input[i]
-
- if (curCC > accumCC)
- {
- immutable comp = compose(input[start], input[i]);
- if (comp != dchar.init)
- {
- input[start] = comp;
- input[i] = dchar.init;// put a sentinel
- // current was merged so its CCC shouldn't affect
- // composing with the next one
- }
- else
- {
- // if it was a starter then accumCC is now 0, end of loop
- accumCC = curCC;
- if (accumCC == 0)
- break;
- }
- }
- else
- {
- // ditto here
- accumCC = curCC;
- if (accumCC == 0)
- break;
- }
- i++;
- }
- return i;
-}
-
-// returns tuple of 2 indexes that delimit:
-// normalized text, piece that needs normalization and
-// the rest of input starting with stable code point
-private auto splitNormalized(NormalizationForm norm, C)(const(C)[] input)
-{
- import std.typecons : tuple;
- ubyte lastCC = 0;
-
- foreach (idx, dchar ch; input)
- {
- static if (norm == NFC)
- if (ch < 0x0300)
- {
- lastCC = 0;
- continue;
- }
- immutable ubyte CC = combiningClass(ch);
- if (lastCC > CC && CC != 0)
- {
- return seekStable!norm(idx, input);
- }
-
- if (notAllowedIn!norm(ch))
- {
- return seekStable!norm(idx, input);
- }
- lastCC = CC;
- }
- return tuple(input.length, input.length);
-}
-
-private auto seekStable(NormalizationForm norm, C)(size_t idx, in C[] input)
-{
- import std.typecons : tuple;
- import std.utf : codeLength;
-
- auto br = input[0 .. idx];
- size_t region_start = 0;// default
- for (;;)
- {
- if (br.empty)// start is 0
- break;
- dchar ch = br.back;
- if (combiningClass(ch) == 0 && allowedIn!norm(ch))
- {
- region_start = br.length - codeLength!C(ch);
- break;
- }
- br.popFront();
- }
- ///@@@BUG@@@ can't use find: " find is a nested function and can't be used..."
- size_t region_end=input.length;// end is $ by default
- foreach (i, dchar ch; input[idx..$])
- {
- if (combiningClass(ch) == 0 && allowedIn!norm(ch))
- {
- region_end = i+idx;
- break;
- }
- }
- // writeln("Region to normalize: ", input[region_start .. region_end]);
- return tuple(region_start, region_end);
-}
-
-/**
- Tests if dchar $(D ch) is always allowed (Quick_Check=YES) in normalization
- form $(D norm).
-*/
-public bool allowedIn(NormalizationForm norm)(dchar ch)
-{
- return !notAllowedIn!norm(ch);
-}
-
-///
-@safe unittest
-{
- // e.g. Cyrillic is always allowed, so is ASCII
- assert(allowedIn!NFC('Ñ'));
- assert(allowedIn!NFD('Ñ'));
- assert(allowedIn!NFKC('Ñ'));
- assert(allowedIn!NFKD('Ñ'));
- assert(allowedIn!NFC('Z'));
-}
-
-// not user friendly name but more direct
-private bool notAllowedIn(NormalizationForm norm)(dchar ch)
-{
- static if (norm == NFC)
- alias qcTrie = nfcQCTrie;
- else static if (norm == NFD)
- alias qcTrie = nfdQCTrie;
- else static if (norm == NFKC)
- alias qcTrie = nfkcQCTrie;
- else static if (norm == NFKD)
- alias qcTrie = nfkdQCTrie;
- else
- static assert("Unknown normalization form "~norm);
- return qcTrie[ch];
-}
-
-@safe unittest
-{
- assert(allowedIn!NFC('Ñ'));
- assert(allowedIn!NFD('Ñ'));
- assert(allowedIn!NFKC('Ñ'));
- assert(allowedIn!NFKD('Ñ'));
- assert(allowedIn!NFC('Z'));
-}
-
-}
-
-version (std_uni_bootstrap)
-{
- // old version used for bootstrapping of gen_uni.d that generates
- // up to date optimal versions of all of isXXX functions
- @safe pure nothrow @nogc public bool isWhite(dchar c)
- {
- import std.ascii : isWhite;
- return isWhite(c) ||
- c == lineSep || c == paraSep ||
- c == '\u0085' || c == '\u00A0' || c == '\u1680' || c == '\u180E' ||
- (c >= '\u2000' && c <= '\u200A') ||
- c == '\u202F' || c == '\u205F' || c == '\u3000';
- }
-}
-else
-{
-
-// trusted -> avoid bounds check
-@trusted pure nothrow @nogc private
-{
- import std.internal.unicode_tables; // : toLowerTable, toTitleTable, toUpperTable; // generated file
-
- // hide template instances behind functions (Bugzilla 13232)
- ushort toLowerIndex(dchar c) { return toLowerIndexTrie[c]; }
- ushort toLowerSimpleIndex(dchar c) { return toLowerSimpleIndexTrie[c]; }
- dchar toLowerTab(size_t idx) { return toLowerTable[idx]; }
-
- ushort toTitleIndex(dchar c) { return toTitleIndexTrie[c]; }
- ushort toTitleSimpleIndex(dchar c) { return toTitleSimpleIndexTrie[c]; }
- dchar toTitleTab(size_t idx) { return toTitleTable[idx]; }
-
- ushort toUpperIndex(dchar c) { return toUpperIndexTrie[c]; }
- ushort toUpperSimpleIndex(dchar c) { return toUpperSimpleIndexTrie[c]; }
- dchar toUpperTab(size_t idx) { return toUpperTable[idx]; }
-}
-
-public:
-
-/++
- Whether or not $(D c) is a Unicode whitespace $(CHARACTER).
- (general Unicode category: Part of C0(tab, vertical tab, form feed,
- carriage return, and linefeed characters), Zs, Zl, Zp, and NEL(U+0085))
-+/
-@safe pure nothrow @nogc
-public bool isWhite(dchar c)
-{
- import std.internal.unicode_tables : isWhiteGen; // generated file
- return isWhiteGen(c); // call pregenerated binary search
-}
-
-/++
- Return whether $(D c) is a Unicode lowercase $(CHARACTER).
-+/
-@safe pure nothrow @nogc
-bool isLower(dchar c)
-{
- import std.ascii : isLower, isASCII;
- if (isASCII(c))
- return isLower(c);
- return lowerCaseTrie[c];
-}
-
-@safe unittest
-{
- import std.ascii : isLower;
- foreach (v; 0 .. 0x80)
- assert(isLower(v) == .isLower(v));
- assert(.isLower('Ñ'));
- assert(.isLower('й'));
- assert(!.isLower('Ж'));
- // Greek HETA
- assert(!.isLower('\u0370'));
- assert(.isLower('\u0371'));
- assert(!.isLower('\u039C')); // capital MU
- assert(.isLower('\u03B2')); // beta
- // from extended Greek
- assert(!.isLower('\u1F18'));
- assert(.isLower('\u1F00'));
- foreach (v; unicode.lowerCase.byCodepoint)
- assert(.isLower(v) && !isUpper(v));
-}
-
-
-/++
- Return whether $(D c) is a Unicode uppercase $(CHARACTER).
-+/
-@safe pure nothrow @nogc
-bool isUpper(dchar c)
-{
- import std.ascii : isUpper, isASCII;
- if (isASCII(c))
- return isUpper(c);
- return upperCaseTrie[c];
-}
-
-@safe unittest
-{
- import std.ascii : isLower;
- foreach (v; 0 .. 0x80)
- assert(isLower(v) == .isLower(v));
- assert(!isUpper('й'));
- assert(isUpper('Ж'));
- // Greek HETA
- assert(isUpper('\u0370'));
- assert(!isUpper('\u0371'));
- assert(isUpper('\u039C')); // capital MU
- assert(!isUpper('\u03B2')); // beta
- // from extended Greek
- assert(!isUpper('\u1F00'));
- assert(isUpper('\u1F18'));
- foreach (v; unicode.upperCase.byCodepoint)
- assert(isUpper(v) && !.isLower(v));
-}
-
-
-//TODO: Hidden for now, needs better API.
-//Other transforms could use better API as well, but this one is a new primitive.
-@safe pure nothrow @nogc
-private dchar toTitlecase(dchar c)
-{
- // optimize ASCII case
- if (c < 0xAA)
- {
- if (c < 'a')
- return c;
- if (c <= 'z')
- return c - 32;
- return c;
- }
- size_t idx = toTitleSimpleIndex(c);
- if (idx != ushort.max)
- {
- return toTitleTab(idx);
- }
- return c;
-}
-
-private alias UpperTriple = AliasSeq!(toUpperIndex, MAX_SIMPLE_UPPER, toUpperTab);
-private alias LowerTriple = AliasSeq!(toLowerIndex, MAX_SIMPLE_LOWER, toLowerTab);
-
-// generic toUpper/toLower on whole string, creates new or returns as is
-private S toCase(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, S)(S s) @trusted pure
-if (isSomeString!S)
-{
- import std.array : appender;
- import std.ascii : isASCII;
-
- foreach (i, dchar cOuter; s)
- {
- ushort idx = indexFn(cOuter);
- if (idx == ushort.max)
- continue;
- auto result = appender!S(s[0 .. i]);
- result.reserve(s.length);
- foreach (dchar c; s[i .. $])
- {
- if (c.isASCII)
- {
- result.put(asciiConvert(c));
- }
- else
- {
- idx = indexFn(c);
- if (idx == ushort.max)
- result.put(c);
- else if (idx < maxIdx)
- {
- c = tableFn(idx);
- result.put(c);
- }
- else
- {
- auto val = tableFn(idx);
- // unpack length + codepoint
- immutable uint len = val >> 24;
- result.put(cast(dchar)(val & 0xFF_FFFF));
- foreach (j; idx+1 .. idx+len)
- result.put(tableFn(j));
- }
- }
- }
- return result.data;
- }
- return s;
-}
-
-@safe unittest //12428
-{
- import std.array : replicate;
- auto s = "abcdefghij".replicate(300);
- s = s[0 .. 10];
-
- toUpper(s);
-
- assert(s == "abcdefghij");
-}
-
-
-// generic toUpper/toLower on whole range, returns range
-private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, Range)(Range str)
- // Accept range of dchar's
-if (isInputRange!Range &&
- isSomeChar!(ElementEncodingType!Range) &&
- ElementEncodingType!Range.sizeof == dchar.sizeof)
-{
- static struct ToCaserImpl
- {
- @property bool empty()
- {
- return !nLeft && r.empty;
- }
-
- @property auto front()
- {
- import std.ascii : isASCII;
-
- if (!nLeft)
- {
- dchar c = r.front;
- if (c.isASCII)
- {
- buf[0] = asciiConvert(c);
- nLeft = 1;
- }
- else
- {
- const idx = indexFn(c);
- if (idx == ushort.max)
- {
- buf[0] = c;
- nLeft = 1;
- }
- else if (idx < maxIdx)
- {
- buf[0] = tableFn(idx);
- nLeft = 1;
- }
- else
- {
- immutable val = tableFn(idx);
- // unpack length + codepoint
- nLeft = val >> 24;
- if (nLeft == 0)
- nLeft = 1;
- assert(nLeft <= buf.length);
- buf[nLeft - 1] = cast(dchar)(val & 0xFF_FFFF);
- foreach (j; 1 .. nLeft)
- buf[nLeft - j - 1] = tableFn(idx + j);
- }
- }
- }
- return buf[nLeft - 1];
- }
-
- void popFront()
- {
- if (!nLeft)
- front;
- assert(nLeft);
- --nLeft;
- if (!nLeft)
- r.popFront();
- }
-
- static if (isForwardRange!Range)
- {
- @property auto save()
- {
- auto ret = this;
- ret.r = r.save;
- return ret;
- }
- }
-
- private:
- Range r;
- uint nLeft;
- dchar[3] buf = void;
- }
-
- return ToCaserImpl(str);
-}
-
-/*********************
- * Convert input range or string to upper or lower case.
- *
- * Does not allocate memory.
- * Characters in UTF-8 or UTF-16 format that cannot be decoded
- * are treated as $(REF replacementDchar, std,utf).
- *
- * Params:
- * str = string or range of characters
- *
- * Returns:
- * an InputRange of dchars
- *
- * See_Also:
- * $(LREF toUpper), $(LREF toLower)
- */
-
-auto asLowerCase(Range)(Range str)
-if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
- !isConvertibleToString!Range)
-{
- static if (ElementEncodingType!Range.sizeof < dchar.sizeof)
- {
- import std.utf : byDchar;
-
- // Decode first
- return asLowerCase(str.byDchar);
- }
- else
- {
- static import std.ascii;
- return toCaser!(LowerTriple, std.ascii.toLower)(str);
- }
-}
-
-/// ditto
-auto asUpperCase(Range)(Range str)
-if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
- !isConvertibleToString!Range)
-{
- static if (ElementEncodingType!Range.sizeof < dchar.sizeof)
- {
- import std.utf : byDchar;
-
- // Decode first
- return asUpperCase(str.byDchar);
- }
- else
- {
- static import std.ascii;
- return toCaser!(UpperTriple, std.ascii.toUpper)(str);
- }
-}
-
-///
-@safe pure unittest
-{
- import std.algorithm.comparison : equal;
-
- assert("hEllo".asUpperCase.equal("HELLO"));
-}
-
-// explicitly undocumented
-auto asLowerCase(Range)(auto ref Range str)
-if (isConvertibleToString!Range)
-{
- import std.traits : StringTypeOf;
- return asLowerCase!(StringTypeOf!Range)(str);
-}
-
-// explicitly undocumented
-auto asUpperCase(Range)(auto ref Range str)
-if (isConvertibleToString!Range)
-{
- import std.traits : StringTypeOf;
- return asUpperCase!(StringTypeOf!Range)(str);
-}
-
-@safe unittest
-{
- assert(testAliasedString!asLowerCase("hEllo"));
- assert(testAliasedString!asUpperCase("hEllo"));
-}
-
-@safe unittest
-{
- import std.array : array;
-
- auto a = "HELLo".asLowerCase;
- auto savea = a.save;
- auto s = a.array;
- assert(s == "hello");
- s = savea.array;
- assert(s == "hello");
-
- string[] lower = ["123", "abcфеж", "\u0131\u023f\u03c9", "i\u0307\u1Fe2"];
- string[] upper = ["123", "ABCФЕЖ", "I\u2c7e\u2126", "\u0130\u03A5\u0308\u0300"];
-
- foreach (i, slwr; lower)
- {
- import std.utf : byChar;
-
- auto sx = slwr.asUpperCase.byChar.array;
- assert(sx == toUpper(slwr));
- auto sy = upper[i].asLowerCase.byChar.array;
- assert(sy == toLower(upper[i]));
- }
-
- // Not necessary to call r.front
- for (auto r = lower[3].asUpperCase; !r.empty; r.popFront())
- {
- }
-
- import std.algorithm.comparison : equal;
-
- "HELLo"w.asLowerCase.equal("hello"d);
- "HELLo"w.asUpperCase.equal("HELLO"d);
- "HELLo"d.asLowerCase.equal("hello"d);
- "HELLo"d.asUpperCase.equal("HELLO"d);
-
- import std.utf : byChar;
- assert(toLower("\u1Fe2") == asLowerCase("\u1Fe2").byChar.array);
-}
-
-// generic capitalizer on whole range, returns range
-private auto toCapitalizer(alias indexFnUpper, uint maxIdxUpper, alias tableFnUpper,
- Range)(Range str)
- // Accept range of dchar's
-if (isInputRange!Range &&
- isSomeChar!(ElementEncodingType!Range) &&
- ElementEncodingType!Range.sizeof == dchar.sizeof)
-{
- static struct ToCapitalizerImpl
- {
- @property bool empty()
- {
- return lower ? lwr.empty : !nLeft && r.empty;
- }
-
- @property auto front()
- {
- if (lower)
- return lwr.front;
-
- if (!nLeft)
- {
- immutable dchar c = r.front;
- const idx = indexFnUpper(c);
- if (idx == ushort.max)
- {
- buf[0] = c;
- nLeft = 1;
- }
- else if (idx < maxIdxUpper)
- {
- buf[0] = tableFnUpper(idx);
- nLeft = 1;
- }
- else
- {
- immutable val = tableFnUpper(idx);
- // unpack length + codepoint
- nLeft = val >> 24;
- if (nLeft == 0)
- nLeft = 1;
- assert(nLeft <= buf.length);
- buf[nLeft - 1] = cast(dchar)(val & 0xFF_FFFF);
- foreach (j; 1 .. nLeft)
- buf[nLeft - j - 1] = tableFnUpper(idx + j);
- }
- }
- return buf[nLeft - 1];
- }
-
- void popFront()
- {
- if (lower)
- lwr.popFront();
- else
- {
- if (!nLeft)
- front;
- assert(nLeft);
- --nLeft;
- if (!nLeft)
- {
- r.popFront();
- lwr = r.asLowerCase();
- lower = true;
- }
- }
- }
-
- static if (isForwardRange!Range)
- {
- @property auto save()
- {
- auto ret = this;
- ret.r = r.save;
- ret.lwr = lwr.save;
- return ret;
- }
- }
-
- private:
- Range r;
- typeof(r.asLowerCase) lwr; // range representing the lower case rest of string
- bool lower = false; // false for first character, true for rest of string
- dchar[3] buf = void;
- uint nLeft = 0;
- }
-
- return ToCapitalizerImpl(str);
-}
-
-/*********************
- * Capitalize input range or string, meaning convert the first
- * character to upper case and subsequent characters to lower case.
- *
- * Does not allocate memory.
- * Characters in UTF-8 or UTF-16 format that cannot be decoded
- * are treated as $(REF replacementDchar, std,utf).
- *
- * Params:
- * str = string or range of characters
- *
- * Returns:
- * an InputRange of dchars
- *
- * See_Also:
- * $(LREF toUpper), $(LREF toLower)
- * $(LREF asUpperCase), $(LREF asLowerCase)
- */
-
-auto asCapitalized(Range)(Range str)
-if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
- !isConvertibleToString!Range)
-{
- static if (ElementEncodingType!Range.sizeof < dchar.sizeof)
- {
- import std.utf : byDchar;
-
- // Decode first
- return toCapitalizer!UpperTriple(str.byDchar);
- }
- else
- {
- return toCapitalizer!UpperTriple(str);
- }
-}
-
-///
-@safe pure unittest
-{
- import std.algorithm.comparison : equal;
-
- assert("hEllo".asCapitalized.equal("Hello"));
-}
-
-auto asCapitalized(Range)(auto ref Range str)
-if (isConvertibleToString!Range)
-{
- import std.traits : StringTypeOf;
- return asCapitalized!(StringTypeOf!Range)(str);
-}
-
-@safe unittest
-{
- assert(testAliasedString!asCapitalized("hEllo"));
-}
-
-@safe pure nothrow @nogc unittest
-{
- auto r = "hEllo".asCapitalized();
- assert(r.front == 'H');
-}
-
-@safe unittest
-{
- import std.array : array;
-
- auto a = "hELLo".asCapitalized;
- auto savea = a.save;
- auto s = a.array;
- assert(s == "Hello");
- s = savea.array;
- assert(s == "Hello");
-
- string[2][] cases =
- [
- ["", ""],
- ["h", "H"],
- ["H", "H"],
- ["3", "3"],
- ["123", "123"],
- ["h123A", "H123a"],
- ["феж", "Феж"],
- ["\u1Fe2", "\u03a5\u0308\u0300"],
- ];
-
- foreach (i; 0 .. cases.length)
- {
- import std.utf : byChar;
-
- auto r = cases[i][0].asCapitalized.byChar.array;
- auto result = cases[i][1];
- assert(r == result);
- }
-
- // Don't call r.front
- for (auto r = "\u1Fe2".asCapitalized; !r.empty; r.popFront())
- {
- }
-
- import std.algorithm.comparison : equal;
-
- "HELLo"w.asCapitalized.equal("Hello"d);
- "hElLO"w.asCapitalized.equal("Hello"d);
- "hello"d.asCapitalized.equal("Hello"d);
- "HELLO"d.asCapitalized.equal("Hello"d);
-
- import std.utf : byChar;
- assert(asCapitalized("\u0130").byChar.array == asUpperCase("\u0130").byChar.array);
-}
-
-// TODO: helper, I wish std.utf was more flexible (and stright)
-private size_t encodeTo(scope char[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc
-{
- if (c <= 0x7F)
- {
- buf[idx] = cast(char) c;
- idx++;
- }
- else if (c <= 0x7FF)
- {
- buf[idx] = cast(char)(0xC0 | (c >> 6));
- buf[idx+1] = cast(char)(0x80 | (c & 0x3F));
- idx += 2;
- }
- else if (c <= 0xFFFF)
- {
- buf[idx] = cast(char)(0xE0 | (c >> 12));
- buf[idx+1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
- buf[idx+2] = cast(char)(0x80 | (c & 0x3F));
- idx += 3;
- }
- else if (c <= 0x10FFFF)
- {
- buf[idx] = cast(char)(0xF0 | (c >> 18));
- buf[idx+1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
- buf[idx+2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
- buf[idx+3] = cast(char)(0x80 | (c & 0x3F));
- idx += 4;
- }
- else
- assert(0);
- return idx;
-}
-
-@safe unittest
-{
- char[] s = "abcd".dup;
- size_t i = 0;
- i = encodeTo(s, i, 'X');
- assert(s == "Xbcd");
-
- i = encodeTo(s, i, cast(dchar)'\u00A9');
- assert(s == "X\xC2\xA9d");
-}
-
-// TODO: helper, I wish std.utf was more flexible (and stright)
-private size_t encodeTo(scope wchar[] buf, size_t idx, dchar c) @trusted pure
-{
- import std.utf : UTFException;
- if (c <= 0xFFFF)
- {
- if (0xD800 <= c && c <= 0xDFFF)
- throw (new UTFException("Encoding an isolated surrogate code point in UTF-16")).setSequence(c);
- buf[idx] = cast(wchar) c;
- idx++;
- }
- else if (c <= 0x10FFFF)
- {
- buf[idx] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
- buf[idx+1] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00);
- idx += 2;
- }
- else
- assert(0);
- return idx;
-}
-
-private size_t encodeTo(scope dchar[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc
-{
- buf[idx] = c;
- idx++;
- return idx;
-}
-
-private void toCaseInPlace(alias indexFn, uint maxIdx, alias tableFn, C)(ref C[] s) @trusted pure
-if (is(C == char) || is(C == wchar) || is(C == dchar))
-{
- import std.utf : decode, codeLength;
- size_t curIdx = 0;
- size_t destIdx = 0;
- alias slowToCase = toCaseInPlaceAlloc!(indexFn, maxIdx, tableFn);
- size_t lastUnchanged = 0;
- // in-buffer move of bytes to a new start index
- // the trick is that it may not need to copy at all
- static size_t moveTo(C[] str, size_t dest, size_t from, size_t to)
- {
- // Interestingly we may just bump pointer for a while
- // then have to copy if a re-cased char was smaller the original
- // later we may regain pace with char that got bigger
- // In the end it sometimes flip-flops between the 2 cases below
- if (dest == from)
- return to;
- // got to copy
- foreach (C c; str[from .. to])
- str[dest++] = c;
- return dest;
- }
- while (curIdx != s.length)
- {
- size_t startIdx = curIdx;
- immutable ch = decode(s, curIdx);
- // TODO: special case for ASCII
- immutable caseIndex = indexFn(ch);
- if (caseIndex == ushort.max) // unchanged, skip over
- {
- continue;
- }
- else if (caseIndex < maxIdx) // 1:1 codepoint mapping
- {
- // previous cased chars had the same length as uncased ones
- // thus can just adjust pointer
- destIdx = moveTo(s, destIdx, lastUnchanged, startIdx);
- lastUnchanged = curIdx;
- immutable cased = tableFn(caseIndex);
- immutable casedLen = codeLength!C(cased);
- if (casedLen + destIdx > curIdx) // no place to fit cased char
- {
- // switch to slow codepath, where we allocate
- return slowToCase(s, startIdx, destIdx);
- }
- else
- {
- destIdx = encodeTo(s, destIdx, cased);
- }
- }
- else // 1:m codepoint mapping, slow codepath
- {
- destIdx = moveTo(s, destIdx, lastUnchanged, startIdx);
- lastUnchanged = curIdx;
- return slowToCase(s, startIdx, destIdx);
- }
- assert(destIdx <= curIdx);
- }
- if (lastUnchanged != s.length)
- {
- destIdx = moveTo(s, destIdx, lastUnchanged, s.length);
- }
- s = s[0 .. destIdx];
-}
-
-// helper to precalculate size of case-converted string
-private template toCaseLength(alias indexFn, uint maxIdx, alias tableFn)
-{
- size_t toCaseLength(C)(in C[] str)
- {
- import std.utf : decode, codeLength;
- size_t codeLen = 0;
- size_t lastNonTrivial = 0;
- size_t curIdx = 0;
- while (curIdx != str.length)
- {
- immutable startIdx = curIdx;
- immutable ch = decode(str, curIdx);
- immutable ushort caseIndex = indexFn(ch);
- if (caseIndex == ushort.max)
- continue;
- else if (caseIndex < maxIdx)
- {
- codeLen += startIdx - lastNonTrivial;
- lastNonTrivial = curIdx;
- immutable cased = tableFn(caseIndex);
- codeLen += codeLength!C(cased);
- }
- else
- {
- codeLen += startIdx - lastNonTrivial;
- lastNonTrivial = curIdx;
- immutable val = tableFn(caseIndex);
- immutable len = val >> 24;
- immutable dchar cased = val & 0xFF_FFFF;
- codeLen += codeLength!C(cased);
- foreach (j; caseIndex+1 .. caseIndex+len)
- codeLen += codeLength!C(tableFn(j));
- }
- }
- if (lastNonTrivial != str.length)
- codeLen += str.length - lastNonTrivial;
- return codeLen;
- }
-}
-
-@safe unittest
-{
- alias toLowerLength = toCaseLength!(LowerTriple);
- assert(toLowerLength("abcd") == 4);
- assert(toLowerLength("аБВгд456") == 10+3);
-}
-
-// slower code path that preallocates and then copies
-// case-converted stuf to the new string
-private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn)
-{
- void toCaseInPlaceAlloc(C)(ref C[] s, size_t curIdx,
- size_t destIdx) @trusted pure
- if (is(C == char) || is(C == wchar) || is(C == dchar))
- {
- import std.utf : decode;
- alias caseLength = toCaseLength!(indexFn, maxIdx, tableFn);
- auto trueLength = destIdx + caseLength(s[curIdx..$]);
- C[] ns = new C[trueLength];
- ns[0 .. destIdx] = s[0 .. destIdx];
- size_t lastUnchanged = curIdx;
- while (curIdx != s.length)
- {
- immutable startIdx = curIdx; // start of current codepoint
- immutable ch = decode(s, curIdx);
- immutable caseIndex = indexFn(ch);
- if (caseIndex == ushort.max) // skip over
- {
- continue;
- }
- else if (caseIndex < maxIdx) // 1:1 codepoint mapping
- {
- immutable cased = tableFn(caseIndex);
- auto toCopy = startIdx - lastUnchanged;
- ns[destIdx .. destIdx+toCopy] = s[lastUnchanged .. startIdx];
- lastUnchanged = curIdx;
- destIdx += toCopy;
- destIdx = encodeTo(ns, destIdx, cased);
- }
- else // 1:m codepoint mapping, slow codepath
- {
- auto toCopy = startIdx - lastUnchanged;
- ns[destIdx .. destIdx+toCopy] = s[lastUnchanged .. startIdx];
- lastUnchanged = curIdx;
- destIdx += toCopy;
- auto val = tableFn(caseIndex);
- // unpack length + codepoint
- immutable uint len = val >> 24;
- destIdx = encodeTo(ns, destIdx, cast(dchar)(val & 0xFF_FFFF));
- foreach (j; caseIndex+1 .. caseIndex+len)
- destIdx = encodeTo(ns, destIdx, tableFn(j));
- }
- }
- if (lastUnchanged != s.length)
- {
- auto toCopy = s.length - lastUnchanged;
- ns[destIdx .. destIdx+toCopy] = s[lastUnchanged..$];
- destIdx += toCopy;
- }
- assert(ns.length == destIdx);
- s = ns;
- }
-}
-
-/++
- Converts $(D s) to lowercase (by performing Unicode lowercase mapping) in place.
- For a few characters string length may increase after the transformation,
- in such a case the function reallocates exactly once.
- If $(D s) does not have any uppercase characters, then $(D s) is unaltered.
-+/
-void toLowerInPlace(C)(ref C[] s) @trusted pure
-if (is(C == char) || is(C == wchar) || is(C == dchar))
-{
- toCaseInPlace!(LowerTriple)(s);
-}
-// overloads for the most common cases to reduce compile time
-@safe pure /*TODO nothrow*/
-{
- void toLowerInPlace(ref char[] s)
- { toLowerInPlace!char(s); }
- void toLowerInPlace(ref wchar[] s)
- { toLowerInPlace!wchar(s); }
- void toLowerInPlace(ref dchar[] s)
- { toLowerInPlace!dchar(s); }
-}
-
-/++
- Converts $(D s) to uppercase (by performing Unicode uppercase mapping) in place.
- For a few characters string length may increase after the transformation,
- in such a case the function reallocates exactly once.
- If $(D s) does not have any lowercase characters, then $(D s) is unaltered.
-+/
-void toUpperInPlace(C)(ref C[] s) @trusted pure
-if (is(C == char) || is(C == wchar) || is(C == dchar))
-{
- toCaseInPlace!(UpperTriple)(s);
-}
-// overloads for the most common cases to reduce compile time/code size
-@safe pure /*TODO nothrow*/
-{
- void toUpperInPlace(ref char[] s)
- { toUpperInPlace!char(s); }
- void toUpperInPlace(ref wchar[] s)
- { toUpperInPlace!wchar(s); }
- void toUpperInPlace(ref dchar[] s)
- { toUpperInPlace!dchar(s); }
-}
-
-/++
- If $(D c) is a Unicode uppercase $(CHARACTER), then its lowercase equivalent
- is returned. Otherwise $(D c) is returned.
-
- Warning: certain alphabets like German and Greek have no 1:1
- upper-lower mapping. Use overload of toLower which takes full string instead.
-+/
-@safe pure nothrow @nogc
-dchar toLower(dchar c)
-{
- // optimize ASCII case
- if (c < 0xAA)
- {
- if (c < 'A')
- return c;
- if (c <= 'Z')
- return c + 32;
- return c;
- }
- size_t idx = toLowerSimpleIndex(c);
- if (idx != ushort.max)
- {
- return toLowerTab(idx);
- }
- return c;
-}
-
-/++
- Returns a string which is identical to $(D s) except that all of its
- characters are converted to lowercase (by preforming Unicode lowercase mapping).
- If none of $(D s) characters were affected, then $(D s) itself is returned.
-+/
-S toLower(S)(S s) @trusted pure
-if (isSomeString!S)
-{
- static import std.ascii;
- return toCase!(LowerTriple, std.ascii.toLower)(s);
-}
-// overloads for the most common cases to reduce compile time
-@safe pure /*TODO nothrow*/
-{
- string toLower(string s)
- { return toLower!string(s); }
- wstring toLower(wstring s)
- { return toLower!wstring(s); }
- dstring toLower(dstring s)
- { return toLower!dstring(s); }
-
- @safe unittest
- {
- // https://issues.dlang.org/show_bug.cgi?id=16663
-
- static struct String
- {
- string data;
- alias data this;
- }
-
- void foo()
- {
- auto u = toLower(String(""));
- }
- }
-}
-
-
-@system unittest //@@@BUG std.format is not @safe
-{
- static import std.ascii;
- import std.format : format;
- foreach (ch; 0 .. 0x80)
- assert(std.ascii.toLower(ch) == toLower(ch));
- assert(toLower('Я') == 'Ñ');
- assert(toLower('Δ') == 'δ');
- foreach (ch; unicode.upperCase.byCodepoint)
- {
- dchar low = ch.toLower();
- assert(low == ch || isLower(low), format("%s -> %s", ch, low));
- }
- assert(toLower("ÐЯ") == "аÑ");
-
- assert("\u1E9E".toLower == "\u00df");
- assert("\u00df".toUpper == "SS");
-}
-
-//bugzilla 9629
-@safe unittest
-{
- wchar[] test = "hello þ world"w.dup;
- auto piece = test[6 .. 7];
- toUpperInPlace(piece);
- assert(test == "hello Þ world");
-}
-
-
-@safe unittest
-{
- import std.algorithm.comparison : cmp;
- string s1 = "FoL";
- string s2 = toLower(s1);
- assert(cmp(s2, "fol") == 0, s2);
- assert(s2 != s1);
-
- char[] s3 = s1.dup;
- toLowerInPlace(s3);
- assert(s3 == s2);
-
- s1 = "A\u0100B\u0101d";
- s2 = toLower(s1);
- s3 = s1.dup;
- assert(cmp(s2, "a\u0101b\u0101d") == 0);
- assert(s2 !is s1);
- toLowerInPlace(s3);
- assert(s3 == s2);
-
- s1 = "A\u0460B\u0461d";
- s2 = toLower(s1);
- s3 = s1.dup;
- assert(cmp(s2, "a\u0461b\u0461d") == 0);
- assert(s2 !is s1);
- toLowerInPlace(s3);
- assert(s3 == s2);
-
- s1 = "\u0130";
- s2 = toLower(s1);
- s3 = s1.dup;
- assert(s2 == "i\u0307");
- assert(s2 !is s1);
- toLowerInPlace(s3);
- assert(s3 == s2);
-
- // Test on wchar and dchar strings.
- assert(toLower("Some String"w) == "some string"w);
- assert(toLower("Some String"d) == "some string"d);
-
- // bugzilla 12455
- dchar c = 'Ä°'; // '\U0130' LATIN CAPITAL LETTER I WITH DOT ABOVE
- assert(isUpper(c));
- assert(toLower(c) == 'i');
- // extend on 12455 reprot - check simple-case toUpper too
- c = '\u1f87';
- assert(isLower(c));
- assert(toUpper(c) == '\u1F8F');
-}
-
-
-/++
- If $(D c) is a Unicode lowercase $(CHARACTER), then its uppercase equivalent
- is returned. Otherwise $(D c) is returned.
-
- Warning:
- Certain alphabets like German and Greek have no 1:1
- upper-lower mapping. Use overload of toUpper which takes full string instead.
-
- toUpper can be used as an argument to $(REF map, std,algorithm,iteration)
- to produce an algorithm that can convert a range of characters to upper case
- without allocating memory.
- A string can then be produced by using $(REF copy, std,algorithm,mutation)
- to send it to an $(REF appender, std,array).
-+/
-@safe pure nothrow @nogc
-dchar toUpper(dchar c)
-{
- // optimize ASCII case
- if (c < 0xAA)
- {
- if (c < 'a')
- return c;
- if (c <= 'z')
- return c - 32;
- return c;
- }
- size_t idx = toUpperSimpleIndex(c);
- if (idx != ushort.max)
- {
- return toUpperTab(idx);
- }
- return c;
-}
-
-///
-@system unittest
-{
- import std.algorithm.iteration : map;
- import std.algorithm.mutation : copy;
- import std.array : appender;
-
- auto abuf = appender!(char[])();
- "hello".map!toUpper.copy(&abuf);
- assert(abuf.data == "HELLO");
-}
-
-@safe unittest
-{
- static import std.ascii;
- import std.format : format;
- foreach (ch; 0 .. 0x80)
- assert(std.ascii.toUpper(ch) == toUpper(ch));
- assert(toUpper('Ñ') == 'Я');
- assert(toUpper('δ') == 'Δ');
- auto title = unicode.Titlecase_Letter;
- foreach (ch; unicode.lowerCase.byCodepoint)
- {
- dchar up = ch.toUpper();
- assert(up == ch || isUpper(up) || title[up],
- format("%x -> %x", ch, up));
- }
-}
-
-/++
- Returns a string which is identical to $(D s) except that all of its
- characters are converted to uppercase (by preforming Unicode uppercase mapping).
- If none of $(D s) characters were affected, then $(D s) itself is returned.
-+/
-S toUpper(S)(S s) @trusted pure
-if (isSomeString!S)
-{
- static import std.ascii;
- return toCase!(UpperTriple, std.ascii.toUpper)(s);
-}
-// overloads for the most common cases to reduce compile time
-@safe pure /*TODO nothrow*/
-{
- string toUpper(string s)
- { return toUpper!string(s); }
- wstring toUpper(wstring s)
- { return toUpper!wstring(s); }
- dstring toUpper(dstring s)
- { return toUpper!dstring(s); }
-
- @safe unittest
- {
- // https://issues.dlang.org/show_bug.cgi?id=16663
-
- static struct String
- {
- string data;
- alias data this;
- }
-
- void foo()
- {
- auto u = toUpper(String(""));
- }
- }
-}
-
-@safe unittest
-{
- import std.algorithm.comparison : cmp;
-
- string s1 = "FoL";
- string s2;
- char[] s3;
-
- s2 = toUpper(s1);
- s3 = s1.dup; toUpperInPlace(s3);
- assert(s3 == s2, s3);
- assert(cmp(s2, "FOL") == 0);
- assert(s2 !is s1);
-
- s1 = "a\u0100B\u0101d";
- s2 = toUpper(s1);
- s3 = s1.dup; toUpperInPlace(s3);
- assert(s3 == s2);
- assert(cmp(s2, "A\u0100B\u0100D") == 0);
- assert(s2 !is s1);
-
- s1 = "a\u0460B\u0461d";
- s2 = toUpper(s1);
- s3 = s1.dup; toUpperInPlace(s3);
- assert(s3 == s2);
- assert(cmp(s2, "A\u0460B\u0460D") == 0);
- assert(s2 !is s1);
-}
-
-@system unittest
-{
- static void doTest(C)(const(C)[] s, const(C)[] trueUp, const(C)[] trueLow)
- {
- import std.format : format;
- string diff = "src: %( %x %)\nres: %( %x %)\ntru: %( %x %)";
- auto low = s.toLower() , up = s.toUpper();
- auto lowInp = s.dup, upInp = s.dup;
- lowInp.toLowerInPlace();
- upInp.toUpperInPlace();
- assert(low == trueLow, format(diff, low, trueLow));
- assert(up == trueUp, format(diff, up, trueUp));
- assert(lowInp == trueLow,
- format(diff, cast(ubyte[]) s, cast(ubyte[]) lowInp, cast(ubyte[]) trueLow));
- assert(upInp == trueUp,
- format(diff, cast(ubyte[]) s, cast(ubyte[]) upInp, cast(ubyte[]) trueUp));
- }
- foreach (S; AliasSeq!(dstring, wstring, string))
- {
-
- S easy = "123";
- S good = "abCФеж";
- S awful = "\u0131\u023f\u2126";
- S wicked = "\u0130\u1FE2";
- auto options = [easy, good, awful, wicked];
- S[] lower = ["123", "abcфеж", "\u0131\u023f\u03c9", "i\u0307\u1Fe2"];
- S[] upper = ["123", "ABCФЕЖ", "I\u2c7e\u2126", "\u0130\u03A5\u0308\u0300"];
-
- foreach (val; AliasSeq!(easy, good))
- {
- auto e = val.dup;
- auto g = e;
- e.toUpperInPlace();
- assert(e is g);
- e.toLowerInPlace();
- assert(e is g);
- }
- foreach (i, v; options)
- {
- doTest(v, upper[i], lower[i]);
- }
-
- // a few combinatorial runs
- foreach (i; 0 .. options.length)
- foreach (j; i .. options.length)
- foreach (k; j .. options.length)
- {
- auto sample = options[i] ~ options[j] ~ options[k];
- auto sample2 = options[k] ~ options[j] ~ options[i];
- doTest(sample, upper[i] ~ upper[j] ~ upper[k],
- lower[i] ~ lower[j] ~ lower[k]);
- doTest(sample2, upper[k] ~ upper[j] ~ upper[i],
- lower[k] ~ lower[j] ~ lower[i]);
- }
- }
-}
-
-
-/++
- Returns whether $(D c) is a Unicode alphabetic $(CHARACTER)
- (general Unicode category: Alphabetic).
-+/
-@safe pure nothrow @nogc
-bool isAlpha(dchar c)
-{
- // optimization
- if (c < 0xAA)
- {
- size_t x = c - 'A';
- if (x <= 'Z' - 'A')
- return true;
- else
- {
- x = c - 'a';
- if (x <= 'z'-'a')
- return true;
- }
- return false;
- }
-
- return alphaTrie[c];
-}
-
-@safe unittest
-{
- auto alpha = unicode("Alphabetic");
- foreach (ch; alpha.byCodepoint)
- assert(isAlpha(ch));
- foreach (ch; 0 .. 0x4000)
- assert((ch in alpha) == isAlpha(ch));
-}
-
-
-/++
- Returns whether $(D c) is a Unicode mark
- (general Unicode category: Mn, Me, Mc).
-+/
-@safe pure nothrow @nogc
-bool isMark(dchar c)
-{
- return markTrie[c];
-}
-
-@safe unittest
-{
- auto mark = unicode("Mark");
- foreach (ch; mark.byCodepoint)
- assert(isMark(ch));
- foreach (ch; 0 .. 0x4000)
- assert((ch in mark) == isMark(ch));
-}
-
-/++
- Returns whether $(D c) is a Unicode numerical $(CHARACTER)
- (general Unicode category: Nd, Nl, No).
-+/
-@safe pure nothrow @nogc
-bool isNumber(dchar c)
-{
- // optimization for ascii case
- if (c <= 0x7F)
- {
- return c >= '0' && c <= '9';
- }
- else
- {
- return numberTrie[c];
- }
-}
-
-@safe unittest
-{
- auto n = unicode("N");
- foreach (ch; n.byCodepoint)
- assert(isNumber(ch));
- foreach (ch; 0 .. 0x4000)
- assert((ch in n) == isNumber(ch));
-}
-
-/++
- Returns whether $(D c) is a Unicode alphabetic $(CHARACTER) or number.
- (general Unicode category: Alphabetic, Nd, Nl, No).
-
- Params:
- c = any Unicode character
- Returns:
- `true` if the character is in the Alphabetic, Nd, Nl, or No Unicode
- categories
-+/
-@safe pure nothrow @nogc
-bool isAlphaNum(dchar c)
-{
- static import std.ascii;
-
- // optimization for ascii case
- if (std.ascii.isASCII(c))
- {
- return std.ascii.isAlphaNum(c);
- }
- else
- {
- return isAlpha(c) || isNumber(c);
- }
-}
-
-@safe unittest
-{
- auto n = unicode("N");
- auto alpha = unicode("Alphabetic");
-
- foreach (ch; n.byCodepoint)
- assert(isAlphaNum(ch));
-
- foreach (ch; alpha.byCodepoint)
- assert(isAlphaNum(ch));
-
- foreach (ch; 0 .. 0x4000)
- {
- assert(((ch in n) || (ch in alpha)) == isAlphaNum(ch));
- }
-}
-
-/++
- Returns whether $(D c) is a Unicode punctuation $(CHARACTER)
- (general Unicode category: Pd, Ps, Pe, Pc, Po, Pi, Pf).
-+/
-@safe pure nothrow @nogc
-bool isPunctuation(dchar c)
-{
- static import std.ascii;
-
- // optimization for ascii case
- if (c <= 0x7F)
- {
- return std.ascii.isPunctuation(c);
- }
- else
- {
- return punctuationTrie[c];
- }
-}
-
-@safe unittest
-{
- assert(isPunctuation('\u0021'));
- assert(isPunctuation('\u0028'));
- assert(isPunctuation('\u0029'));
- assert(isPunctuation('\u002D'));
- assert(isPunctuation('\u005F'));
- assert(isPunctuation('\u00AB'));
- assert(isPunctuation('\u00BB'));
- foreach (ch; unicode("P").byCodepoint)
- assert(isPunctuation(ch));
-}
-
-/++
- Returns whether $(D c) is a Unicode symbol $(CHARACTER)
- (general Unicode category: Sm, Sc, Sk, So).
-+/
-@safe pure nothrow @nogc
-bool isSymbol(dchar c)
-{
- return symbolTrie[c];
-}
-
-@safe unittest
-{
- import std.format : format;
- assert(isSymbol('\u0024'));
- assert(isSymbol('\u002B'));
- assert(isSymbol('\u005E'));
- assert(isSymbol('\u00A6'));
- foreach (ch; unicode("S").byCodepoint)
- assert(isSymbol(ch), format("%04x", ch));
-}
-
-/++
- Returns whether $(D c) is a Unicode space $(CHARACTER)
- (general Unicode category: Zs)
- Note: This doesn't include '\n', '\r', \t' and other non-space $(CHARACTER).
- For commonly used less strict semantics see $(LREF isWhite).
-+/
-@safe pure nothrow @nogc
-bool isSpace(dchar c)
-{
- import std.internal.unicode_tables : isSpaceGen; // generated file
- return isSpaceGen(c);
-}
-
-@safe unittest
-{
- assert(isSpace('\u0020'));
- auto space = unicode.Zs;
- foreach (ch; space.byCodepoint)
- assert(isSpace(ch));
- foreach (ch; 0 .. 0x1000)
- assert(isSpace(ch) == space[ch]);
-}
-
-
-/++
- Returns whether $(D c) is a Unicode graphical $(CHARACTER)
- (general Unicode category: L, M, N, P, S, Zs).
-
-+/
-@safe pure nothrow @nogc
-bool isGraphical(dchar c)
-{
- return graphicalTrie[c];
-}
-
-
-@safe unittest
-{
- auto set = unicode("Graphical");
- import std.format : format;
- foreach (ch; set.byCodepoint)
- assert(isGraphical(ch), format("%4x", ch));
- foreach (ch; 0 .. 0x4000)
- assert((ch in set) == isGraphical(ch));
-}
-
-
-/++
- Returns whether $(D c) is a Unicode control $(CHARACTER)
- (general Unicode category: Cc).
-+/
-@safe pure nothrow @nogc
-bool isControl(dchar c)
-{
- import std.internal.unicode_tables : isControlGen; // generated file
- return isControlGen(c);
-}
-
-@safe unittest
-{
- assert(isControl('\u0000'));
- assert(isControl('\u0081'));
- assert(!isControl('\u0100'));
- auto cc = unicode.Cc;
- foreach (ch; cc.byCodepoint)
- assert(isControl(ch));
- foreach (ch; 0 .. 0x1000)
- assert(isControl(ch) == cc[ch]);
-}
-
-
-/++
- Returns whether $(D c) is a Unicode formatting $(CHARACTER)
- (general Unicode category: Cf).
-+/
-@safe pure nothrow @nogc
-bool isFormat(dchar c)
-{
- import std.internal.unicode_tables : isFormatGen; // generated file
- return isFormatGen(c);
-}
-
-
-@safe unittest
-{
- assert(isFormat('\u00AD'));
- foreach (ch; unicode("Format").byCodepoint)
- assert(isFormat(ch));
-}
-
-// code points for private use, surrogates are not likely to change in near feature
-// if need be they can be generated from unicode data as well
-
-/++
- Returns whether $(D c) is a Unicode Private Use $(CODEPOINT)
- (general Unicode category: Co).
-+/
-@safe pure nothrow @nogc
-bool isPrivateUse(dchar c)
-{
- return (0x00_E000 <= c && c <= 0x00_F8FF)
- || (0x0F_0000 <= c && c <= 0x0F_FFFD)
- || (0x10_0000 <= c && c <= 0x10_FFFD);
-}
-
-/++
- Returns whether $(D c) is a Unicode surrogate $(CODEPOINT)
- (general Unicode category: Cs).
-+/
-@safe pure nothrow @nogc
-bool isSurrogate(dchar c)
-{
- return (0xD800 <= c && c <= 0xDFFF);
-}
-
-/++
- Returns whether $(D c) is a Unicode high surrogate (lead surrogate).
-+/
-@safe pure nothrow @nogc
-bool isSurrogateHi(dchar c)
-{
- return (0xD800 <= c && c <= 0xDBFF);
-}
-
-/++
- Returns whether $(D c) is a Unicode low surrogate (trail surrogate).
-+/
-@safe pure nothrow @nogc
-bool isSurrogateLo(dchar c)
-{
- return (0xDC00 <= c && c <= 0xDFFF);
-}
-
-/++
- Returns whether $(D c) is a Unicode non-character i.e.
- a $(CODEPOINT) with no assigned abstract character.
- (general Unicode category: Cn)
-+/
-@safe pure nothrow @nogc
-bool isNonCharacter(dchar c)
-{
- return nonCharacterTrie[c];
-}
-
-@safe unittest
-{
- auto set = unicode("Cn");
- foreach (ch; set.byCodepoint)
- assert(isNonCharacter(ch));
-}
-
-private:
-// load static data from pre-generated tables into usable datastructures
-
-
-@safe auto asSet(const (ubyte)[] compressed) pure
-{
- return CodepointSet.fromIntervals(decompressIntervals(compressed));
-}
-
-@safe pure nothrow auto asTrie(T...)(in TrieEntry!T e)
-{
- return const(CodepointTrie!T)(e.offsets, e.sizes, e.data);
-}
-
-@safe pure nothrow @nogc @property
-{
- import std.internal.unicode_tables; // generated file
-
- // It's important to use auto return here, so that the compiler
- // only runs semantic on the return type if the function gets
- // used. Also these are functions rather than templates to not
- // increase the object size of the caller.
- auto lowerCaseTrie() { static immutable res = asTrie(lowerCaseTrieEntries); return res; }
- auto upperCaseTrie() { static immutable res = asTrie(upperCaseTrieEntries); return res; }
- auto simpleCaseTrie() { static immutable res = asTrie(simpleCaseTrieEntries); return res; }
- auto fullCaseTrie() { static immutable res = asTrie(fullCaseTrieEntries); return res; }
- auto alphaTrie() { static immutable res = asTrie(alphaTrieEntries); return res; }
- auto markTrie() { static immutable res = asTrie(markTrieEntries); return res; }
- auto numberTrie() { static immutable res = asTrie(numberTrieEntries); return res; }
- auto punctuationTrie() { static immutable res = asTrie(punctuationTrieEntries); return res; }
- auto symbolTrie() { static immutable res = asTrie(symbolTrieEntries); return res; }
- auto graphicalTrie() { static immutable res = asTrie(graphicalTrieEntries); return res; }
- auto nonCharacterTrie() { static immutable res = asTrie(nonCharacterTrieEntries); return res; }
-
- //normalization quick-check tables
- auto nfcQCTrie()
- {
- import std.internal.unicode_norm : nfcQCTrieEntries;
- static immutable res = asTrie(nfcQCTrieEntries);
- return res;
- }
-
- auto nfdQCTrie()
- {
- import std.internal.unicode_norm : nfdQCTrieEntries;
- static immutable res = asTrie(nfdQCTrieEntries);
- return res;
- }
-
- auto nfkcQCTrie()
- {
- import std.internal.unicode_norm : nfkcQCTrieEntries;
- static immutable res = asTrie(nfkcQCTrieEntries);
- return res;
- }
-
- auto nfkdQCTrie()
- {
- import std.internal.unicode_norm : nfkdQCTrieEntries;
- static immutable res = asTrie(nfkdQCTrieEntries);
- return res;
- }
-
- //grapheme breaking algorithm tables
- auto mcTrie()
- {
- import std.internal.unicode_grapheme : mcTrieEntries;
- static immutable res = asTrie(mcTrieEntries);
- return res;
- }
-
- auto graphemeExtendTrie()
- {
- import std.internal.unicode_grapheme : graphemeExtendTrieEntries;
- static immutable res = asTrie(graphemeExtendTrieEntries);
- return res;
- }
-
- auto hangLV()
- {
- import std.internal.unicode_grapheme : hangulLVTrieEntries;
- static immutable res = asTrie(hangulLVTrieEntries);
- return res;
- }
-
- auto hangLVT()
- {
- import std.internal.unicode_grapheme : hangulLVTTrieEntries;
- static immutable res = asTrie(hangulLVTTrieEntries);
- return res;
- }
-
- // tables below are used for composition/decomposition
- auto combiningClassTrie()
- {
- import std.internal.unicode_comp : combiningClassTrieEntries;
- static immutable res = asTrie(combiningClassTrieEntries);
- return res;
- }
-
- auto compatMappingTrie()
- {
- import std.internal.unicode_decomp : compatMappingTrieEntries;
- static immutable res = asTrie(compatMappingTrieEntries);
- return res;
- }
-
- auto canonMappingTrie()
- {
- import std.internal.unicode_decomp : canonMappingTrieEntries;
- static immutable res = asTrie(canonMappingTrieEntries);
- return res;
- }
-
- auto compositionJumpTrie()
- {
- import std.internal.unicode_comp : compositionJumpTrieEntries;
- static immutable res = asTrie(compositionJumpTrieEntries);
- return res;
- }
-
- //case conversion tables
- auto toUpperIndexTrie() { static immutable res = asTrie(toUpperIndexTrieEntries); return res; }
- auto toLowerIndexTrie() { static immutable res = asTrie(toLowerIndexTrieEntries); return res; }
- auto toTitleIndexTrie() { static immutable res = asTrie(toTitleIndexTrieEntries); return res; }
- //simple case conversion tables
- auto toUpperSimpleIndexTrie() { static immutable res = asTrie(toUpperSimpleIndexTrieEntries); return res; }
- auto toLowerSimpleIndexTrie() { static immutable res = asTrie(toLowerSimpleIndexTrieEntries); return res; }
- auto toTitleSimpleIndexTrie() { static immutable res = asTrie(toTitleSimpleIndexTrieEntries); return res; }
-
-}
-
-}// version (!std_uni_bootstrap)
diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d
new file mode 100644
index 00000000000..318bcb32a6f
--- /dev/null
+++ b/libphobos/src/std/uni/package.d
@@ -0,0 +1,10637 @@
+// Written in the D programming language.
+
+/++
+ $(P The `std.uni` module provides an implementation
+ of fundamental Unicode algorithms and data structures.
+ This doesn't include UTF encoding and decoding primitives,
+ see $(REF decode, std,_utf) and $(REF encode, std,_utf) in $(MREF std, utf)
+ for this functionality. )
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Decode) $(TD
+ $(LREF byCodePoint)
+ $(LREF byGrapheme)
+ $(LREF decodeGrapheme)
+ $(LREF graphemeStride)
+))
+$(TR $(TD Comparison) $(TD
+ $(LREF icmp)
+ $(LREF sicmp)
+))
+$(TR $(TD Classification) $(TD
+ $(LREF isAlpha)
+ $(LREF isAlphaNum)
+ $(LREF isCodepointSet)
+ $(LREF isControl)
+ $(LREF isFormat)
+ $(LREF isGraphical)
+ $(LREF isIntegralPair)
+ $(LREF isMark)
+ $(LREF isNonCharacter)
+ $(LREF isNumber)
+ $(LREF isPrivateUse)
+ $(LREF isPunctuation)
+ $(LREF isSpace)
+ $(LREF isSurrogate)
+ $(LREF isSurrogateHi)
+ $(LREF isSurrogateLo)
+ $(LREF isSymbol)
+ $(LREF isWhite)
+))
+$(TR $(TD Normalization) $(TD
+ $(LREF NFC)
+ $(LREF NFD)
+ $(LREF NFKD)
+ $(LREF NormalizationForm)
+ $(LREF normalize)
+))
+$(TR $(TD Decompose) $(TD
+ $(LREF decompose)
+ $(LREF decomposeHangul)
+ $(LREF UnicodeDecomposition)
+))
+$(TR $(TD Compose) $(TD
+ $(LREF compose)
+ $(LREF composeJamo)
+))
+$(TR $(TD Sets) $(TD
+ $(LREF CodepointInterval)
+ $(LREF CodepointSet)
+ $(LREF InversionList)
+ $(LREF unicode)
+))
+$(TR $(TD Trie) $(TD
+ $(LREF codepointSetTrie)
+ $(LREF CodepointSetTrie)
+ $(LREF codepointTrie)
+ $(LREF CodepointTrie)
+ $(LREF toTrie)
+ $(LREF toDelegate)
+))
+$(TR $(TD Casing) $(TD
+ $(LREF asCapitalized)
+ $(LREF asLowerCase)
+ $(LREF asUpperCase)
+ $(LREF isLower)
+ $(LREF isUpper)
+ $(LREF toLower)
+ $(LREF toLowerInPlace)
+ $(LREF toUpper)
+ $(LREF toUpperInPlace)
+))
+$(TR $(TD Utf8Matcher) $(TD
+ $(LREF isUtfMatcher)
+ $(LREF MatcherConcept)
+ $(LREF utfMatcher)
+))
+$(TR $(TD Separators) $(TD
+ $(LREF lineSep)
+ $(LREF nelSep)
+ $(LREF paraSep)
+))
+$(TR $(TD Building blocks) $(TD
+ $(LREF allowedIn)
+ $(LREF combiningClass)
+ $(LREF Grapheme)
+))
+))
+
+ $(P All primitives listed operate on Unicode characters and
+ sets of characters. For functions which operate on ASCII characters
+ and ignore Unicode $(CHARACTERS), see $(MREF std, ascii).
+ For definitions of Unicode $(CHARACTER), $(CODEPOINT) and other terms
+ used throughout this module see the $(S_LINK Terminology, terminology) section
+ below.
+ )
+ $(P The focus of this module is the core needs of developing Unicode-aware
+ applications. To that effect it provides the following optimized primitives:
+ )
+ $(UL
+ $(LI Character classification by category and common properties:
+ $(LREF isAlpha), $(LREF isWhite) and others.
+ )
+ $(LI
+ Case-insensitive string comparison ($(LREF sicmp), $(LREF icmp)).
+ )
+ $(LI
+ Converting text to any of the four normalization forms via $(LREF normalize).
+ )
+ $(LI
+ Decoding ($(LREF decodeGrapheme)) and iteration ($(LREF byGrapheme), $(LREF graphemeStride))
+ by user-perceived characters, that is by $(LREF Grapheme) clusters.
+ )
+ $(LI
+ Decomposing and composing of individual character(s) according to canonical
+ or compatibility rules, see $(LREF compose) and $(LREF decompose),
+ including the specific version for Hangul syllables $(LREF composeJamo)
+ and $(LREF decomposeHangul).
+ )
+ )
+ $(P It's recognized that an application may need further enhancements
+ and extensions, such as less commonly known algorithms,
+ or tailoring existing ones for region specific needs. To help users
+ with building any extra functionality beyond the core primitives,
+ the module provides:
+ )
+ $(UL
+ $(LI
+ $(LREF CodepointSet), a type for easy manipulation of sets of characters.
+ Besides the typical set algebra it provides an unusual feature:
+ a D source code generator for detection of $(CODEPOINTS) in this set.
+ This is a boon for meta-programming parser frameworks,
+ and is used internally to power classification in small
+ sets like $(LREF isWhite).
+ )
+ $(LI
+ A way to construct optimal packed multi-stage tables also known as a
+ special case of $(LINK2 https://en.wikipedia.org/wiki/Trie, Trie).
+ The functions $(LREF codepointTrie), $(LREF codepointSetTrie)
+ construct custom tries that map dchar to value.
+ The end result is a fast and predictable $(BIGOH 1) lookup that powers
+ functions like $(LREF isAlpha) and $(LREF combiningClass),
+ but for user-defined data sets.
+ )
+ $(LI
+ A useful technique for Unicode-aware parsers that perform
+ character classification of encoded $(CODEPOINTS)
+ is to avoid unnecassary decoding at all costs.
+ $(LREF utfMatcher) provides an improvement over the usual workflow
+ of decode-classify-process, combining the decoding and classification
+ steps. By extracting necessary bits directly from encoded
+ $(S_LINK Code unit, code units) matchers achieve
+ significant performance improvements. See $(LREF MatcherConcept) for
+ the common interface of UTF matchers.
+ )
+ $(LI
+ Generally useful building blocks for customized normalization:
+ $(LREF combiningClass) for querying combining class
+ and $(LREF allowedIn) for testing the Quick_Check
+ property of a given normalization form.
+ )
+ $(LI
+ Access to a large selection of commonly used sets of $(CODEPOINTS).
+ $(S_LINK Unicode properties, Supported sets) include Script,
+ Block and General Category. The exact contents of a set can be
+ observed in the CLDR utility, on the
+ $(HTTP www.unicode.org/cldr/utility/properties.jsp, property index) page
+ of the Unicode website.
+ See $(LREF unicode) for easy and (optionally) compile-time checked set
+ queries.
+ )
+ )
+ $(SECTION Synopsis)
+ ---
+ import std.uni;
+ void main()
+ {
+ // initialize code point sets using script/block or property name
+ // now 'set' contains code points from both scripts.
+ auto set = unicode("Cyrillic") | unicode("Armenian");
+ // same thing but simpler and checked at compile-time
+ auto ascii = unicode.ASCII;
+ auto currency = unicode.Currency_Symbol;
+
+ // easy set ops
+ auto a = set & ascii;
+ assert(a.empty); // as it has no intersection with ascii
+ a = set | ascii;
+ auto b = currency - a; // subtract all ASCII, Cyrillic and Armenian
+
+ // some properties of code point sets
+ assert(b.length > 45); // 46 items in Unicode 6.1, even more in 6.2
+ // testing presence of a code point in a set
+ // is just fine, it is O(logN)
+ assert(!b['$']);
+ assert(!b['\u058F']); // Armenian dram sign
+ assert(b['Â¥']);
+
+ // building fast lookup tables, these guarantee O(1) complexity
+ // 1-level Trie lookup table essentially a huge bit-set ~262Kb
+ auto oneTrie = toTrie!1(b);
+ // 2-level far more compact but typically slightly slower
+ auto twoTrie = toTrie!2(b);
+ // 3-level even smaller, and a bit slower yet
+ auto threeTrie = toTrie!3(b);
+ assert(oneTrie['£']);
+ assert(twoTrie['£']);
+ assert(threeTrie['£']);
+
+ // build the trie with the most sensible trie level
+ // and bind it as a functor
+ auto cyrillicOrArmenian = toDelegate(set);
+ auto balance = find!(cyrillicOrArmenian)("Hello Õ¨Õ¶Õ¯Õ¥Ö€!");
+ assert(balance == "Õ¨Õ¶Õ¯Õ¥Ö€!");
+ // compatible with bool delegate(dchar)
+ bool delegate(dchar) bindIt = cyrillicOrArmenian;
+
+ // Normalization
+ string s = "Plain ascii (and not only), is always normalized!";
+ assert(s is normalize(s));// is the same string
+
+ string nonS = "A\u0308ffin"; // A ligature
+ auto nS = normalize(nonS); // to NFC, the W3C endorsed standard
+ assert(nS == "Äffin");
+ assert(nS != nonS);
+ string composed = "Äffin";
+
+ assert(normalize!NFD(composed) == "A\u0308ffin");
+ // to NFKD, compatibility decomposition useful for fuzzy matching/searching
+ assert(normalize!NFKD("2¹â°") == "210");
+ }
+ ---
+ $(SECTION Terminology)
+ $(P The following is a list of important Unicode notions
+ and definitions. Any conventions used specifically in this
+ module alone are marked as such. The descriptions are based on the formal
+ definition as found in $(HTTP www.unicode.org/versions/Unicode6.2.0/ch03.pdf,
+ chapter three of The Unicode Standard Core Specification.)
+ )
+ $(P $(DEF Abstract character) A unit of information used for the organization,
+ control, or representation of textual data.
+ Note that:
+ $(UL
+ $(LI When representing data, the nature of that data
+ is generally symbolic as opposed to some other
+ kind of data (for example, visual).
+ )
+ $(LI An abstract character has no concrete form
+ and should not be confused with a $(S_LINK Glyph, glyph).
+ )
+ $(LI An abstract character does not necessarily
+ correspond to what a user thinks of as a “characterâ€
+ and should not be confused with a $(LREF Grapheme).
+ )
+ $(LI The abstract characters encoded (see Encoded character)
+ are known as Unicode abstract characters.
+ )
+ $(LI Abstract characters not directly
+ encoded by the Unicode Standard can often be
+ represented by the use of combining character sequences.
+ )
+ )
+ )
+ $(P $(DEF Canonical decomposition)
+ The decomposition of a character or character sequence
+ that results from recursively applying the canonical
+ mappings found in the Unicode Character Database
+ and these described in Conjoining Jamo Behavior
+ (section 12 of
+ $(HTTP www.unicode.org/uni2book/ch03.pdf, Unicode Conformance)).
+ )
+ $(P $(DEF Canonical composition)
+ The precise definition of the Canonical composition
+ is the algorithm as specified in $(HTTP www.unicode.org/uni2book/ch03.pdf,
+ Unicode Conformance) section 11.
+ Informally it's the process that does the reverse of the canonical
+ decomposition with the addition of certain rules
+ that e.g. prevent legacy characters from appearing in the composed result.
+ )
+ $(P $(DEF Canonical equivalent)
+ Two character sequences are said to be canonical equivalents if
+ their full canonical decompositions are identical.
+ )
+ $(P $(DEF Character) Typically differs by context.
+ For the purpose of this documentation the term $(I character)
+ implies $(I encoded character), that is, a code point having
+ an assigned abstract character (a symbolic meaning).
+ )
+ $(P $(DEF Code point) Any value in the Unicode codespace;
+ that is, the range of integers from 0 to 10FFFF (hex).
+ Not all code points are assigned to encoded characters.
+ )
+ $(P $(DEF Code unit) The minimal bit combination that can represent
+ a unit of encoded text for processing or interchange.
+ Depending on the encoding this could be:
+ 8-bit code units in the UTF-8 (`char`),
+ 16-bit code units in the UTF-16 (`wchar`),
+ and 32-bit code units in the UTF-32 (`dchar`).
+ $(I Note that in UTF-32, a code unit is a code point
+ and is represented by the D `dchar` type.)
+ )
+ $(P $(DEF Combining character) A character with the General Category
+ of Combining Mark(M).
+ $(UL
+ $(LI All characters with non-zero canonical combining class
+ are combining characters, but the reverse is not the case:
+ there are combining characters with a zero combining class.
+ )
+ $(LI These characters are not normally used in isolation
+ unless they are being described. They include such characters
+ as accents, diacritics, Hebrew points, Arabic vowel signs,
+ and Indic matras.
+ )
+ )
+ )
+ $(P $(DEF Combining class)
+ A numerical value used by the Unicode Canonical Ordering Algorithm
+ to determine which sequences of combining marks are to be
+ considered canonically equivalent and which are not.
+ )
+ $(P $(DEF Compatibility decomposition)
+ The decomposition of a character or character sequence that results
+ from recursively applying both the compatibility mappings and
+ the canonical mappings found in the Unicode Character Database, and those
+ described in Conjoining Jamo Behavior no characters
+ can be further decomposed.
+ )
+ $(P $(DEF Compatibility equivalent)
+ Two character sequences are said to be compatibility
+ equivalents if their full compatibility decompositions are identical.
+ )
+ $(P $(DEF Encoded character) An association (or mapping)
+ between an abstract character and a code point.
+ )
+ $(P $(DEF Glyph) The actual, concrete image of a glyph representation
+ having been rasterized or otherwise imaged onto some display surface.
+ )
+ $(P $(DEF Grapheme base) A character with the property
+ Grapheme_Base, or any standard Korean syllable block.
+ )
+ $(P $(DEF Grapheme cluster) Defined as the text between
+ grapheme boundaries as specified by Unicode Standard Annex #29,
+ $(HTTP www.unicode.org/reports/tr29/, Unicode text segmentation).
+ Important general properties of a grapheme:
+ $(UL
+ $(LI The grapheme cluster represents a horizontally segmentable
+ unit of text, consisting of some grapheme base (which may
+ consist of a Korean syllable) together with any number of
+ nonspacing marks applied to it.
+ )
+ $(LI A grapheme cluster typically starts with a grapheme base
+ and then extends across any subsequent sequence of nonspacing marks.
+ A grapheme cluster is most directly relevant to text rendering and
+ processes such as cursor placement and text selection in editing,
+ but may also be relevant to comparison and searching.
+ )
+ $(LI For many processes, a grapheme cluster behaves as if it was a
+ single character with the same properties as its grapheme base.
+ Effectively, nonspacing marks apply $(I graphically) to the base,
+ but do not change its properties.
+ )
+ )
+ $(P This module defines a number of primitives that work with graphemes:
+ $(LREF Grapheme), $(LREF decodeGrapheme) and $(LREF graphemeStride).
+ All of them are using $(I extended grapheme) boundaries
+ as defined in the aforementioned standard annex.
+ )
+ )
+ $(P $(DEF Nonspacing mark) A combining character with the
+ General Category of Nonspacing Mark (Mn) or Enclosing Mark (Me).
+ )
+ $(P $(DEF Spacing mark) A combining character that is not a nonspacing mark.
+ )
+ $(SECTION Normalization)
+ $(P The concepts of $(S_LINK Canonical equivalent, canonical equivalent)
+ or $(S_LINK Compatibility equivalent, compatibility equivalent)
+ characters in the Unicode Standard make it necessary to have a full, formal
+ definition of equivalence for Unicode strings.
+ String equivalence is determined by a process called normalization,
+ whereby strings are converted into forms which are compared
+ directly for identity. This is the primary goal of the normalization process,
+ see the function $(LREF normalize) to convert into any of
+ the four defined forms.
+ )
+ $(P A very important attribute of the Unicode Normalization Forms
+ is that they must remain stable between versions of the Unicode Standard.
+ A Unicode string normalized to a particular Unicode Normalization Form
+ in one version of the standard is guaranteed to remain in that Normalization
+ Form for implementations of future versions of the standard.
+ )
+ $(P The Unicode Standard specifies four normalization forms.
+ Informally, two of these forms are defined by maximal decomposition
+ of equivalent sequences, and two of these forms are defined
+ by maximal $(I composition) of equivalent sequences.
+ $(UL
+ $(LI Normalization Form D (NFD): The $(S_LINK Canonical decomposition,
+ canonical decomposition) of a character sequence.)
+ $(LI Normalization Form KD (NFKD): The $(S_LINK Compatibility decomposition,
+ compatibility decomposition) of a character sequence.)
+ $(LI Normalization Form C (NFC): The canonical composition of the
+ $(S_LINK Canonical decomposition, canonical decomposition)
+ of a coded character sequence.)
+ $(LI Normalization Form KC (NFKC): The canonical composition
+ of the $(S_LINK Compatibility decomposition,
+ compatibility decomposition) of a character sequence)
+ )
+ )
+ $(P The choice of the normalization form depends on the particular use case.
+ NFC is the best form for general text, since it's more compatible with
+ strings converted from legacy encodings. NFKC is the preferred form for
+ identifiers, especially where there are security concerns. NFD and NFKD
+ are the most useful for internal processing.
+ )
+ $(SECTION Construction of lookup tables)
+ $(P The Unicode standard describes a set of algorithms that
+ depend on having the ability to quickly look up various properties
+ of a code point. Given the the codespace of about 1 million $(CODEPOINTS),
+ it is not a trivial task to provide a space-efficient solution for
+ the multitude of properties.
+ )
+ $(P Common approaches such as hash-tables or binary search over
+ sorted code point intervals (as in $(LREF InversionList)) are insufficient.
+ Hash-tables have enormous memory footprint and binary search
+ over intervals is not fast enough for some heavy-duty algorithms.
+ )
+ $(P The recommended solution (see Unicode Implementation Guidelines)
+ is using multi-stage tables that are an implementation of the
+ $(HTTP en.wikipedia.org/wiki/Trie, Trie) data structure with integer
+ keys and a fixed number of stages. For the remainder of the section
+ this will be called a fixed trie. The following describes a particular
+ implementation that is aimed for the speed of access at the expense
+ of ideal size savings.
+ )
+ $(P Taking a 2-level Trie as an example the principle of operation is as follows.
+ Split the number of bits in a key (code point, 21 bits) into 2 components
+ (e.g. 15 and 8). The first is the number of bits in the index of the trie
+ and the other is number of bits in each page of the trie.
+ The layout of the trie is then an array of size 2^^bits-of-index followed
+ an array of memory chunks of size 2^^bits-of-page/bits-per-element.
+ )
+ $(P The number of pages is variable (but not less then 1)
+ unlike the number of entries in the index. The slots of the index
+ all have to contain a number of a page that is present. The lookup is then
+ just a couple of operations - slice the upper bits,
+ lookup an index for these, take a page at this index and use
+ the lower bits as an offset within this page.
+
+ Assuming that pages are laid out consequently
+ in one array at `pages`, the pseudo-code is:
+ )
+ ---
+ auto elemsPerPage = (2 ^^ bits_per_page) / Value.sizeOfInBits;
+ pages[index[n >> bits_per_page]][n & (elemsPerPage - 1)];
+ ---
+ $(P Where if `elemsPerPage` is a power of 2 the whole process is
+ a handful of simple instructions and 2 array reads. Subsequent levels
+ of the trie are introduced by recursing on this notion - the index array
+ is treated as values. The number of bits in index is then again
+ split into 2 parts, with pages over 'current-index' and the new 'upper-index'.
+ )
+
+ $(P For completeness a level 1 trie is simply an array.
+ The current implementation takes advantage of bit-packing values
+ when the range is known to be limited in advance (such as `bool`).
+ See also $(LREF BitPacked) for enforcing it manually.
+ The major size advantage however comes from the fact
+ that multiple $(B identical pages on every level are merged) by construction.
+ )
+ $(P The process of constructing a trie is more involved and is hidden from
+ the user in a form of the convenience functions $(LREF codepointTrie),
+ $(LREF codepointSetTrie) and the even more convenient $(LREF toTrie).
+ In general a set or built-in AA with `dchar` type
+ can be turned into a trie. The trie object in this module
+ is read-only (immutable); it's effectively frozen after construction.
+ )
+ $(SECTION Unicode properties)
+ $(P This is a full list of Unicode properties accessible through $(LREF unicode)
+ with specific helpers per category nested within. Consult the
+ $(HTTP www.unicode.org/cldr/utility/properties.jsp, CLDR utility)
+ when in doubt about the contents of a particular set.
+ )
+ $(P General category sets listed below are only accessible with the
+ $(LREF unicode) shorthand accessor.)
+ $(BOOKTABLE $(B General category ),
+ $(TR $(TH Abb.) $(TH Long form)
+ $(TH Abb.) $(TH Long form)$(TH Abb.) $(TH Long form))
+ $(TR $(TD L) $(TD Letter)
+ $(TD Cn) $(TD Unassigned) $(TD Po) $(TD Other_Punctuation))
+ $(TR $(TD Ll) $(TD Lowercase_Letter)
+ $(TD Co) $(TD Private_Use) $(TD Ps) $(TD Open_Punctuation))
+ $(TR $(TD Lm) $(TD Modifier_Letter)
+ $(TD Cs) $(TD Surrogate) $(TD S) $(TD Symbol))
+ $(TR $(TD Lo) $(TD Other_Letter)
+ $(TD N) $(TD Number) $(TD Sc) $(TD Currency_Symbol))
+ $(TR $(TD Lt) $(TD Titlecase_Letter)
+ $(TD Nd) $(TD Decimal_Number) $(TD Sk) $(TD Modifier_Symbol))
+ $(TR $(TD Lu) $(TD Uppercase_Letter)
+ $(TD Nl) $(TD Letter_Number) $(TD Sm) $(TD Math_Symbol))
+ $(TR $(TD M) $(TD Mark)
+ $(TD No) $(TD Other_Number) $(TD So) $(TD Other_Symbol))
+ $(TR $(TD Mc) $(TD Spacing_Mark)
+ $(TD P) $(TD Punctuation) $(TD Z) $(TD Separator))
+ $(TR $(TD Me) $(TD Enclosing_Mark)
+ $(TD Pc) $(TD Connector_Punctuation) $(TD Zl) $(TD Line_Separator))
+ $(TR $(TD Mn) $(TD Nonspacing_Mark)
+ $(TD Pd) $(TD Dash_Punctuation) $(TD Zp) $(TD Paragraph_Separator))
+ $(TR $(TD C) $(TD Other)
+ $(TD Pe) $(TD Close_Punctuation) $(TD Zs) $(TD Space_Separator))
+ $(TR $(TD Cc) $(TD Control) $(TD Pf)
+ $(TD Final_Punctuation) $(TD -) $(TD Any))
+ $(TR $(TD Cf) $(TD Format)
+ $(TD Pi) $(TD Initial_Punctuation) $(TD -) $(TD ASCII))
+ )
+ $(P Sets for other commonly useful properties that are
+ accessible with $(LREF unicode):)
+ $(BOOKTABLE $(B Common binary properties),
+ $(TR $(TH Name) $(TH Name) $(TH Name))
+ $(TR $(TD Alphabetic) $(TD Ideographic) $(TD Other_Uppercase))
+ $(TR $(TD ASCII_Hex_Digit) $(TD IDS_Binary_Operator) $(TD Pattern_Syntax))
+ $(TR $(TD Bidi_Control) $(TD ID_Start) $(TD Pattern_White_Space))
+ $(TR $(TD Cased) $(TD IDS_Trinary_Operator) $(TD Quotation_Mark))
+ $(TR $(TD Case_Ignorable) $(TD Join_Control) $(TD Radical))
+ $(TR $(TD Dash) $(TD Logical_Order_Exception) $(TD Soft_Dotted))
+ $(TR $(TD Default_Ignorable_Code_Point) $(TD Lowercase) $(TD STerm))
+ $(TR $(TD Deprecated) $(TD Math) $(TD Terminal_Punctuation))
+ $(TR $(TD Diacritic) $(TD Noncharacter_Code_Point) $(TD Unified_Ideograph))
+ $(TR $(TD Extender) $(TD Other_Alphabetic) $(TD Uppercase))
+ $(TR $(TD Grapheme_Base) $(TD Other_Default_Ignorable_Code_Point) $(TD Variation_Selector))
+ $(TR $(TD Grapheme_Extend) $(TD Other_Grapheme_Extend) $(TD White_Space))
+ $(TR $(TD Grapheme_Link) $(TD Other_ID_Continue) $(TD XID_Continue))
+ $(TR $(TD Hex_Digit) $(TD Other_ID_Start) $(TD XID_Start))
+ $(TR $(TD Hyphen) $(TD Other_Lowercase) )
+ $(TR $(TD ID_Continue) $(TD Other_Math) )
+ )
+ $(P Below is the table with block names accepted by $(LREF unicode.block).
+ Note that the shorthand version $(LREF unicode) requires "In"
+ to be prepended to the names of blocks so as to disambiguate
+ scripts and blocks.
+ )
+ $(BOOKTABLE $(B Blocks),
+ $(TR $(TD Aegean Numbers) $(TD Ethiopic Extended) $(TD Mongolian))
+ $(TR $(TD Alchemical Symbols) $(TD Ethiopic Extended-A) $(TD Musical Symbols))
+ $(TR $(TD Alphabetic Presentation Forms) $(TD Ethiopic Supplement) $(TD Myanmar))
+ $(TR $(TD Ancient Greek Musical Notation) $(TD General Punctuation) $(TD Myanmar Extended-A))
+ $(TR $(TD Ancient Greek Numbers) $(TD Geometric Shapes) $(TD New Tai Lue))
+ $(TR $(TD Ancient Symbols) $(TD Georgian) $(TD NKo))
+ $(TR $(TD Arabic) $(TD Georgian Supplement) $(TD Number Forms))
+ $(TR $(TD Arabic Extended-A) $(TD Glagolitic) $(TD Ogham))
+ $(TR $(TD Arabic Mathematical Alphabetic Symbols) $(TD Gothic) $(TD Ol Chiki))
+ $(TR $(TD Arabic Presentation Forms-A) $(TD Greek and Coptic) $(TD Old Italic))
+ $(TR $(TD Arabic Presentation Forms-B) $(TD Greek Extended) $(TD Old Persian))
+ $(TR $(TD Arabic Supplement) $(TD Gujarati) $(TD Old South Arabian))
+ $(TR $(TD Armenian) $(TD Gurmukhi) $(TD Old Turkic))
+ $(TR $(TD Arrows) $(TD Halfwidth and Fullwidth Forms) $(TD Optical Character Recognition))
+ $(TR $(TD Avestan) $(TD Hangul Compatibility Jamo) $(TD Oriya))
+ $(TR $(TD Balinese) $(TD Hangul Jamo) $(TD Osmanya))
+ $(TR $(TD Bamum) $(TD Hangul Jamo Extended-A) $(TD Phags-pa))
+ $(TR $(TD Bamum Supplement) $(TD Hangul Jamo Extended-B) $(TD Phaistos Disc))
+ $(TR $(TD Basic Latin) $(TD Hangul Syllables) $(TD Phoenician))
+ $(TR $(TD Batak) $(TD Hanunoo) $(TD Phonetic Extensions))
+ $(TR $(TD Bengali) $(TD Hebrew) $(TD Phonetic Extensions Supplement))
+ $(TR $(TD Block Elements) $(TD High Private Use Surrogates) $(TD Playing Cards))
+ $(TR $(TD Bopomofo) $(TD High Surrogates) $(TD Private Use Area))
+ $(TR $(TD Bopomofo Extended) $(TD Hiragana) $(TD Rejang))
+ $(TR $(TD Box Drawing) $(TD Ideographic Description Characters) $(TD Rumi Numeral Symbols))
+ $(TR $(TD Brahmi) $(TD Imperial Aramaic) $(TD Runic))
+ $(TR $(TD Braille Patterns) $(TD Inscriptional Pahlavi) $(TD Samaritan))
+ $(TR $(TD Buginese) $(TD Inscriptional Parthian) $(TD Saurashtra))
+ $(TR $(TD Buhid) $(TD IPA Extensions) $(TD Sharada))
+ $(TR $(TD Byzantine Musical Symbols) $(TD Javanese) $(TD Shavian))
+ $(TR $(TD Carian) $(TD Kaithi) $(TD Sinhala))
+ $(TR $(TD Chakma) $(TD Kana Supplement) $(TD Small Form Variants))
+ $(TR $(TD Cham) $(TD Kanbun) $(TD Sora Sompeng))
+ $(TR $(TD Cherokee) $(TD Kangxi Radicals) $(TD Spacing Modifier Letters))
+ $(TR $(TD CJK Compatibility) $(TD Kannada) $(TD Specials))
+ $(TR $(TD CJK Compatibility Forms) $(TD Katakana) $(TD Sundanese))
+ $(TR $(TD CJK Compatibility Ideographs) $(TD Katakana Phonetic Extensions) $(TD Sundanese Supplement))
+ $(TR $(TD CJK Compatibility Ideographs Supplement) $(TD Kayah Li) $(TD Superscripts and Subscripts))
+ $(TR $(TD CJK Radicals Supplement) $(TD Kharoshthi) $(TD Supplemental Arrows-A))
+ $(TR $(TD CJK Strokes) $(TD Khmer) $(TD Supplemental Arrows-B))
+ $(TR $(TD CJK Symbols and Punctuation) $(TD Khmer Symbols) $(TD Supplemental Mathematical Operators))
+ $(TR $(TD CJK Unified Ideographs) $(TD Lao) $(TD Supplemental Punctuation))
+ $(TR $(TD CJK Unified Ideographs Extension A) $(TD Latin-1 Supplement) $(TD Supplementary Private Use Area-A))
+ $(TR $(TD CJK Unified Ideographs Extension B) $(TD Latin Extended-A) $(TD Supplementary Private Use Area-B))
+ $(TR $(TD CJK Unified Ideographs Extension C) $(TD Latin Extended Additional) $(TD Syloti Nagri))
+ $(TR $(TD CJK Unified Ideographs Extension D) $(TD Latin Extended-B) $(TD Syriac))
+ $(TR $(TD Combining Diacritical Marks) $(TD Latin Extended-C) $(TD Tagalog))
+ $(TR $(TD Combining Diacritical Marks for Symbols) $(TD Latin Extended-D) $(TD Tagbanwa))
+ $(TR $(TD Combining Diacritical Marks Supplement) $(TD Lepcha) $(TD Tags))
+ $(TR $(TD Combining Half Marks) $(TD Letterlike Symbols) $(TD Tai Le))
+ $(TR $(TD Common Indic Number Forms) $(TD Limbu) $(TD Tai Tham))
+ $(TR $(TD Control Pictures) $(TD Linear B Ideograms) $(TD Tai Viet))
+ $(TR $(TD Coptic) $(TD Linear B Syllabary) $(TD Tai Xuan Jing Symbols))
+ $(TR $(TD Counting Rod Numerals) $(TD Lisu) $(TD Takri))
+ $(TR $(TD Cuneiform) $(TD Low Surrogates) $(TD Tamil))
+ $(TR $(TD Cuneiform Numbers and Punctuation) $(TD Lycian) $(TD Telugu))
+ $(TR $(TD Currency Symbols) $(TD Lydian) $(TD Thaana))
+ $(TR $(TD Cypriot Syllabary) $(TD Mahjong Tiles) $(TD Thai))
+ $(TR $(TD Cyrillic) $(TD Malayalam) $(TD Tibetan))
+ $(TR $(TD Cyrillic Extended-A) $(TD Mandaic) $(TD Tifinagh))
+ $(TR $(TD Cyrillic Extended-B) $(TD Mathematical Alphanumeric Symbols) $(TD Transport And Map Symbols))
+ $(TR $(TD Cyrillic Supplement) $(TD Mathematical Operators) $(TD Ugaritic))
+ $(TR $(TD Deseret) $(TD Meetei Mayek) $(TD Unified Canadian Aboriginal Syllabics))
+ $(TR $(TD Devanagari) $(TD Meetei Mayek Extensions) $(TD Unified Canadian Aboriginal Syllabics Extended))
+ $(TR $(TD Devanagari Extended) $(TD Meroitic Cursive) $(TD Vai))
+ $(TR $(TD Dingbats) $(TD Meroitic Hieroglyphs) $(TD Variation Selectors))
+ $(TR $(TD Domino Tiles) $(TD Miao) $(TD Variation Selectors Supplement))
+ $(TR $(TD Egyptian Hieroglyphs) $(TD Miscellaneous Mathematical Symbols-A) $(TD Vedic Extensions))
+ $(TR $(TD Emoticons) $(TD Miscellaneous Mathematical Symbols-B) $(TD Vertical Forms))
+ $(TR $(TD Enclosed Alphanumerics) $(TD Miscellaneous Symbols) $(TD Yijing Hexagram Symbols))
+ $(TR $(TD Enclosed Alphanumeric Supplement) $(TD Miscellaneous Symbols and Arrows) $(TD Yi Radicals))
+ $(TR $(TD Enclosed CJK Letters and Months) $(TD Miscellaneous Symbols And Pictographs) $(TD Yi Syllables))
+ $(TR $(TD Enclosed Ideographic Supplement) $(TD Miscellaneous Technical) )
+ $(TR $(TD Ethiopic) $(TD Modifier Tone Letters) )
+ )
+ $(P Below is the table with script names accepted by $(LREF unicode.script)
+ and by the shorthand version $(LREF unicode):)
+ $(BOOKTABLE $(B Scripts),
+ $(TR $(TD Arabic) $(TD Hanunoo) $(TD Old_Italic))
+ $(TR $(TD Armenian) $(TD Hebrew) $(TD Old_Persian))
+ $(TR $(TD Avestan) $(TD Hiragana) $(TD Old_South_Arabian))
+ $(TR $(TD Balinese) $(TD Imperial_Aramaic) $(TD Old_Turkic))
+ $(TR $(TD Bamum) $(TD Inherited) $(TD Oriya))
+ $(TR $(TD Batak) $(TD Inscriptional_Pahlavi) $(TD Osmanya))
+ $(TR $(TD Bengali) $(TD Inscriptional_Parthian) $(TD Phags_Pa))
+ $(TR $(TD Bopomofo) $(TD Javanese) $(TD Phoenician))
+ $(TR $(TD Brahmi) $(TD Kaithi) $(TD Rejang))
+ $(TR $(TD Braille) $(TD Kannada) $(TD Runic))
+ $(TR $(TD Buginese) $(TD Katakana) $(TD Samaritan))
+ $(TR $(TD Buhid) $(TD Kayah_Li) $(TD Saurashtra))
+ $(TR $(TD Canadian_Aboriginal) $(TD Kharoshthi) $(TD Sharada))
+ $(TR $(TD Carian) $(TD Khmer) $(TD Shavian))
+ $(TR $(TD Chakma) $(TD Lao) $(TD Sinhala))
+ $(TR $(TD Cham) $(TD Latin) $(TD Sora_Sompeng))
+ $(TR $(TD Cherokee) $(TD Lepcha) $(TD Sundanese))
+ $(TR $(TD Common) $(TD Limbu) $(TD Syloti_Nagri))
+ $(TR $(TD Coptic) $(TD Linear_B) $(TD Syriac))
+ $(TR $(TD Cuneiform) $(TD Lisu) $(TD Tagalog))
+ $(TR $(TD Cypriot) $(TD Lycian) $(TD Tagbanwa))
+ $(TR $(TD Cyrillic) $(TD Lydian) $(TD Tai_Le))
+ $(TR $(TD Deseret) $(TD Malayalam) $(TD Tai_Tham))
+ $(TR $(TD Devanagari) $(TD Mandaic) $(TD Tai_Viet))
+ $(TR $(TD Egyptian_Hieroglyphs) $(TD Meetei_Mayek) $(TD Takri))
+ $(TR $(TD Ethiopic) $(TD Meroitic_Cursive) $(TD Tamil))
+ $(TR $(TD Georgian) $(TD Meroitic_Hieroglyphs) $(TD Telugu))
+ $(TR $(TD Glagolitic) $(TD Miao) $(TD Thaana))
+ $(TR $(TD Gothic) $(TD Mongolian) $(TD Thai))
+ $(TR $(TD Greek) $(TD Myanmar) $(TD Tibetan))
+ $(TR $(TD Gujarati) $(TD New_Tai_Lue) $(TD Tifinagh))
+ $(TR $(TD Gurmukhi) $(TD Nko) $(TD Ugaritic))
+ $(TR $(TD Han) $(TD Ogham) $(TD Vai))
+ $(TR $(TD Hangul) $(TD Ol_Chiki) $(TD Yi))
+ )
+ $(P Below is the table of names accepted by $(LREF unicode.hangulSyllableType).)
+ $(BOOKTABLE $(B Hangul syllable type),
+ $(TR $(TH Abb.) $(TH Long form))
+ $(TR $(TD L) $(TD Leading_Jamo))
+ $(TR $(TD LV) $(TD LV_Syllable))
+ $(TR $(TD LVT) $(TD LVT_Syllable) )
+ $(TR $(TD T) $(TD Trailing_Jamo))
+ $(TR $(TD V) $(TD Vowel_Jamo))
+ )
+ References:
+ $(HTTP www.digitalmars.com/d/ascii-table.html, ASCII Table),
+ $(HTTP en.wikipedia.org/wiki/Unicode, Wikipedia),
+ $(HTTP www.unicode.org, The Unicode Consortium),
+ $(HTTP www.unicode.org/reports/tr15/, Unicode normalization forms),
+ $(HTTP www.unicode.org/reports/tr29/, Unicode text segmentation)
+ $(HTTP www.unicode.org/uni2book/ch05.pdf,
+ Unicode Implementation Guidelines)
+ $(HTTP www.unicode.org/uni2book/ch03.pdf,
+ Unicode Conformance)
+ Trademarks:
+ Unicode(tm) is a trademark of Unicode, Inc.
+
+ Copyright: Copyright 2013 -
+ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ Authors: Dmitry Olshansky
+ Source: $(PHOBOSSRC std/uni/package.d)
+ Standards: $(HTTP www.unicode.org/versions/Unicode6.2.0/, Unicode v6.2)
+
+Macros:
+
+SECTION = <h3><a id="$1">$0</a></h3>
+DEF = <div><a id="$1"><i>$0</i></a></div>
+S_LINK = <a href="#$1">$+</a>
+CODEPOINT = $(S_LINK Code point, code point)
+CODEPOINTS = $(S_LINK Code point, code points)
+CHARACTER = $(S_LINK Character, character)
+CHARACTERS = $(S_LINK Character, characters)
+CLUSTER = $(S_LINK Grapheme cluster, grapheme cluster)
++/
+module std.uni;
+
+import std.meta : AliasSeq;
+import std.range.primitives : back, ElementEncodingType, ElementType, empty,
+ front, hasLength, hasSlicing, isForwardRange, isInputRange,
+ isRandomAccessRange, popFront, put, save;
+import std.traits : isConvertibleToString, isIntegral, isSomeChar,
+ isSomeString, Unqual, isDynamicArray;
+// debug = std_uni;
+
+debug(std_uni) import std.stdio; // writefln, writeln
+
+private:
+
+
+void copyBackwards(T,U)(T[] src, U[] dest)
+{
+ assert(src.length == dest.length);
+ for (size_t i=src.length; i-- > 0; )
+ dest[i] = src[i];
+}
+
+void copyForward(T,U)(T[] src, U[] dest)
+{
+ assert(src.length == dest.length);
+ for (size_t i=0; i<src.length; i++)
+ dest[i] = src[i];
+}
+
+// TODO: update to reflect all major CPUs supporting unaligned reads
+version (X86)
+ enum hasUnalignedReads = true;
+else version (X86_64)
+ enum hasUnalignedReads = true;
+else version (SystemZ)
+ enum hasUnalignedReads = true;
+else
+ enum hasUnalignedReads = false; // better be safe then sorry
+
+public enum dchar lineSep = '\u2028'; /// Constant $(CODEPOINT) (0x2028) - line separator.
+public enum dchar paraSep = '\u2029'; /// Constant $(CODEPOINT) (0x2029) - paragraph separator.
+public enum dchar nelSep = '\u0085'; /// Constant $(CODEPOINT) (0x0085) - next line.
+
+// test the intro example
+@safe unittest
+{
+ import std.algorithm.searching : find;
+ // initialize code point sets using script/block or property name
+ // set contains code points from both scripts.
+ auto set = unicode("Cyrillic") | unicode("Armenian");
+ // or simpler and statically-checked look
+ auto ascii = unicode.ASCII;
+ auto currency = unicode.Currency_Symbol;
+
+ // easy set ops
+ auto a = set & ascii;
+ assert(a.empty); // as it has no intersection with ascii
+ a = set | ascii;
+ auto b = currency - a; // subtract all ASCII, Cyrillic and Armenian
+
+ // some properties of code point sets
+ assert(b.length > 45); // 46 items in Unicode 6.1, even more in 6.2
+ // testing presence of a code point in a set
+ // is just fine, it is O(logN)
+ assert(!b['$']);
+ assert(!b['\u058F']); // Armenian dram sign
+ assert(b['Â¥']);
+
+ // building fast lookup tables, these guarantee O(1) complexity
+ // 1-level Trie lookup table essentially a huge bit-set ~262Kb
+ auto oneTrie = toTrie!1(b);
+ // 2-level far more compact but typically slightly slower
+ auto twoTrie = toTrie!2(b);
+ // 3-level even smaller, and a bit slower yet
+ auto threeTrie = toTrie!3(b);
+ assert(oneTrie['£']);
+ assert(twoTrie['£']);
+ assert(threeTrie['£']);
+
+ // build the trie with the most sensible trie level
+ // and bind it as a functor
+ auto cyrillicOrArmenian = toDelegate(set);
+ auto balance = find!(cyrillicOrArmenian)("Hello Õ¨Õ¶Õ¯Õ¥Ö€!");
+ assert(balance == "Õ¨Õ¶Õ¯Õ¥Ö€!");
+ // compatible with bool delegate(dchar)
+ bool delegate(dchar) bindIt = cyrillicOrArmenian;
+
+ // Normalization
+ string s = "Plain ascii (and not only), is always normalized!";
+ assert(s is normalize(s));// is the same string
+
+ string nonS = "A\u0308ffin"; // A ligature
+ auto nS = normalize(nonS); // to NFC, the W3C endorsed standard
+ assert(nS == "Äffin");
+ assert(nS != nonS);
+ string composed = "Äffin";
+
+ assert(normalize!NFD(composed) == "A\u0308ffin");
+ // to NFKD, compatibility decomposition useful for fuzzy matching/searching
+ assert(normalize!NFKD("2¹â°") == "210");
+}
+
+enum lastDchar = 0x10FFFF;
+
+auto force(T, F)(F from)
+if (isIntegral!T && !is(T == F))
+{
+ assert(from <= T.max && from >= T.min);
+ return cast(T) from;
+}
+
+auto force(T, F)(F from)
+if (isBitPacked!T && !is(T == F))
+{
+ assert(from <= 2^^bitSizeOf!T-1);
+ return T(cast(TypeOfBitPacked!T) from);
+}
+
+auto force(T, F)(F from)
+if (is(T == F))
+{
+ return from;
+}
+
+// repeat X times the bit-pattern in val assuming it's length is 'bits'
+size_t replicateBits(size_t times, size_t bits)(size_t val) @safe pure nothrow @nogc
+{
+ static if (times == 1)
+ return val;
+ else static if (bits == 1)
+ {
+ static if (times == size_t.sizeof*8)
+ return val ? size_t.max : 0;
+ else
+ return val ? (1 << times)-1 : 0;
+ }
+ else static if (times % 2)
+ return (replicateBits!(times-1, bits)(val)<<bits) | val;
+ else
+ return replicateBits!(times/2, bits*2)((val << bits) | val);
+}
+
+@safe pure nothrow @nogc unittest // for replicate
+{
+ import std.algorithm.iteration : sum, map;
+ import std.range : iota;
+ size_t m = 0b111;
+ size_t m2 = 0b01;
+ static foreach (i; AliasSeq!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
+ {
+ assert(replicateBits!(i, 3)(m)+1 == (1<<(3*i)));
+ assert(replicateBits!(i, 2)(m2) == iota(0, i).map!"2^^(2*a)"().sum());
+ }
+}
+
+// multiple arrays squashed into one memory block
+struct MultiArray(Types...)
+{
+ import std.range.primitives : isOutputRange;
+ this(size_t[] sizes...) @safe pure nothrow
+ {
+ assert(dim == sizes.length);
+ size_t full_size;
+ foreach (i, v; Types)
+ {
+ full_size += spaceFor!(bitSizeOf!v)(sizes[i]);
+ sz[i] = sizes[i];
+ static if (i >= 1)
+ offsets[i] = offsets[i-1] +
+ spaceFor!(bitSizeOf!(Types[i-1]))(sizes[i-1]);
+ }
+
+ storage = new size_t[full_size];
+ }
+
+ this(const(size_t)[] raw_offsets,
+ const(size_t)[] raw_sizes,
+ return scope const(size_t)[] data) return scope const @safe pure nothrow @nogc
+ {
+ offsets[] = raw_offsets[];
+ sz[] = raw_sizes[];
+ storage = data;
+ }
+
+ @property auto slice(size_t n)()inout pure nothrow @nogc
+ {
+ auto ptr = raw_ptr!n;
+ return packedArrayView!(Types[n])(ptr, sz[n]);
+ }
+
+ @property auto ptr(size_t n)()inout pure nothrow @nogc
+ {
+ auto ptr = raw_ptr!n;
+ return inout(PackedPtr!(Types[n]))(ptr);
+ }
+
+ template length(size_t n)
+ {
+ @property size_t length()const @safe pure nothrow @nogc{ return sz[n]; }
+
+ @property void length(size_t new_size)
+ {
+ if (new_size > sz[n])
+ {// extend
+ size_t delta = (new_size - sz[n]);
+ sz[n] += delta;
+ delta = spaceFor!(bitSizeOf!(Types[n]))(delta);
+ storage.length += delta;// extend space at end
+ // raw_slice!x must follow resize as it could be moved!
+ // next stmts move all data past this array, last-one-goes-first
+ static if (n != dim-1)
+ {
+ auto start = raw_ptr!(n+1);
+ // len includes delta
+ size_t len = (storage.ptr+storage.length-start);
+
+ copyBackwards(start[0 .. len-delta], start[delta .. len]);
+
+ start[0 .. delta] = 0;
+ // offsets are used for raw_slice, ptr etc.
+ foreach (i; n+1 .. dim)
+ offsets[i] += delta;
+ }
+ }
+ else if (new_size < sz[n])
+ {// shrink
+ size_t delta = (sz[n] - new_size);
+ sz[n] -= delta;
+ delta = spaceFor!(bitSizeOf!(Types[n]))(delta);
+ // move all data past this array, forward direction
+ static if (n != dim-1)
+ {
+ auto start = raw_ptr!(n+1);
+ size_t len = (storage.ptr+storage.length-start);
+ copyForward(start[0 .. len-delta], start[delta .. len]);
+
+ // adjust offsets last, they affect raw_slice
+ foreach (i; n+1 .. dim)
+ offsets[i] -= delta;
+ }
+ storage.length -= delta;
+ }
+ // else - NOP
+ }
+ }
+
+ @property size_t bytes(size_t n=size_t.max)() const @safe
+ {
+ static if (n == size_t.max)
+ return storage.length*size_t.sizeof;
+ else static if (n != Types.length-1)
+ return (raw_ptr!(n+1)-raw_ptr!n)*size_t.sizeof;
+ else
+ return (storage.ptr+storage.length - raw_ptr!n)*size_t.sizeof;
+ }
+
+ void store(OutRange)(scope OutRange sink) const
+ if (isOutputRange!(OutRange, char))
+ {
+ import std.format.write : formattedWrite;
+ formattedWrite(sink, "[%( 0x%x, %)]", offsets[]);
+ formattedWrite(sink, ", [%( 0x%x, %)]", sz[]);
+ formattedWrite(sink, ", [%( 0x%x, %)]", storage);
+ }
+
+private:
+ import std.meta : staticMap;
+ @property auto raw_ptr(size_t n)()inout pure nothrow @nogc
+ {
+ static if (n == 0)
+ return storage.ptr;
+ else
+ {
+ return storage.ptr+offsets[n];
+ }
+ }
+ enum dim = Types.length;
+ size_t[dim] offsets;// offset for level x
+ size_t[dim] sz;// size of level x
+ alias bitWidth = staticMap!(bitSizeOf, Types);
+ size_t[] storage;
+}
+
+@system unittest
+{
+ import std.conv : text;
+ enum dg = (){
+ // sizes are:
+ // lvl0: 3, lvl1 : 2, lvl2: 1
+ auto m = MultiArray!(int, ubyte, int)(3,2,1);
+
+ static void check(size_t k, T)(ref T m, int n)
+ {
+ foreach (i; 0 .. n)
+ assert(m.slice!(k)[i] == i+1, text("level:",i," : ",m.slice!(k)[0 .. n]));
+ }
+
+ static void checkB(size_t k, T)(ref T m, int n)
+ {
+ foreach (i; 0 .. n)
+ assert(m.slice!(k)[i] == n-i, text("level:",i," : ",m.slice!(k)[0 .. n]));
+ }
+
+ static void fill(size_t k, T)(ref T m, int n)
+ {
+ foreach (i; 0 .. n)
+ m.slice!(k)[i] = force!ubyte(i+1);
+ }
+
+ static void fillB(size_t k, T)(ref T m, int n)
+ {
+ foreach (i; 0 .. n)
+ m.slice!(k)[i] = force!ubyte(n-i);
+ }
+
+ m.length!1 = 100;
+ fill!1(m, 100);
+ check!1(m, 100);
+
+ m.length!0 = 220;
+ fill!0(m, 220);
+ check!1(m, 100);
+ check!0(m, 220);
+
+ m.length!2 = 17;
+ fillB!2(m, 17);
+ checkB!2(m, 17);
+ check!0(m, 220);
+ check!1(m, 100);
+
+ m.length!2 = 33;
+ checkB!2(m, 17);
+ fillB!2(m, 33);
+ checkB!2(m, 33);
+ check!0(m, 220);
+ check!1(m, 100);
+
+ m.length!1 = 195;
+ fillB!1(m, 195);
+ checkB!1(m, 195);
+ checkB!2(m, 33);
+ check!0(m, 220);
+
+ auto marr = MultiArray!(BitPacked!(uint, 4), BitPacked!(uint, 6))(20, 10);
+ marr.length!0 = 15;
+ marr.length!1 = 30;
+ fill!1(marr, 30);
+ fill!0(marr, 15);
+ check!1(marr, 30);
+ check!0(marr, 15);
+ return 0;
+ };
+ enum ct = dg();
+ auto rt = dg();
+}
+
+@system unittest
+{// more bitpacking tests
+ import std.conv : text;
+
+ alias Bitty =
+ MultiArray!(BitPacked!(size_t, 3)
+ , BitPacked!(size_t, 4)
+ , BitPacked!(size_t, 3)
+ , BitPacked!(size_t, 6)
+ , bool);
+ alias fn1 = sliceBits!(13, 16);
+ alias fn2 = sliceBits!( 9, 13);
+ alias fn3 = sliceBits!( 6, 9);
+ alias fn4 = sliceBits!( 0, 6);
+ static void check(size_t lvl, MA)(ref MA arr){
+ for (size_t i = 0; i< arr.length!lvl; i++)
+ assert(arr.slice!(lvl)[i] == i, text("Mismatch on lvl ", lvl, " idx ", i, " value: ", arr.slice!(lvl)[i]));
+ }
+
+ static void fillIdx(size_t lvl, MA)(ref MA arr){
+ for (size_t i = 0; i< arr.length!lvl; i++)
+ arr.slice!(lvl)[i] = i;
+ }
+ Bitty m1;
+
+ m1.length!4 = 10;
+ m1.length!3 = 2^^6;
+ m1.length!2 = 2^^3;
+ m1.length!1 = 2^^4;
+ m1.length!0 = 2^^3;
+
+ m1.length!4 = 2^^16;
+
+ for (size_t i = 0; i< m1.length!4; i++)
+ m1.slice!(4)[i] = i % 2;
+
+ fillIdx!1(m1);
+ check!1(m1);
+ fillIdx!2(m1);
+ check!2(m1);
+ fillIdx!3(m1);
+ check!3(m1);
+ fillIdx!0(m1);
+ check!0(m1);
+ check!3(m1);
+ check!2(m1);
+ check!1(m1);
+ for (size_t i=0; i < 2^^16; i++)
+ {
+ m1.slice!(4)[i] = i % 2;
+ m1.slice!(0)[fn1(i)] = fn1(i);
+ m1.slice!(1)[fn2(i)] = fn2(i);
+ m1.slice!(2)[fn3(i)] = fn3(i);
+ m1.slice!(3)[fn4(i)] = fn4(i);
+ }
+ for (size_t i=0; i < 2^^16; i++)
+ {
+ assert(m1.slice!(4)[i] == i % 2);
+ assert(m1.slice!(0)[fn1(i)] == fn1(i));
+ assert(m1.slice!(1)[fn2(i)] == fn2(i));
+ assert(m1.slice!(2)[fn3(i)] == fn3(i));
+ assert(m1.slice!(3)[fn4(i)] == fn4(i));
+ }
+}
+
+size_t spaceFor(size_t _bits)(size_t new_len) @safe pure nothrow @nogc
+{
+ import std.math.algebraic : nextPow2;
+ enum bits = _bits == 1 ? 1 : nextPow2(_bits - 1);// see PackedArrayView
+ static if (bits > 8*size_t.sizeof)
+ {
+ static assert(bits % (size_t.sizeof*8) == 0);
+ return new_len * bits/(8*size_t.sizeof);
+ }
+ else
+ {
+ enum factor = size_t.sizeof*8/bits;
+ return (new_len+factor-1)/factor; // rounded up
+ }
+}
+
+template isBitPackableType(T)
+{
+ enum isBitPackableType = isBitPacked!T
+ || isIntegral!T || is(T == bool) || isSomeChar!T;
+}
+
+//============================================================================
+template PackedArrayView(T)
+if ((is(T dummy == BitPacked!(U, sz), U, size_t sz)
+ && isBitPackableType!U) || isBitPackableType!T)
+{
+ import std.math.algebraic : nextPow2;
+ private enum bits = bitSizeOf!T;
+ alias PackedArrayView = PackedArrayViewImpl!(T, bits > 1 ? nextPow2(bits - 1) : 1);
+}
+
+//unsafe and fast access to a chunk of RAM as if it contains packed values
+template PackedPtr(T)
+if ((is(T dummy == BitPacked!(U, sz), U, size_t sz)
+ && isBitPackableType!U) || isBitPackableType!T)
+{
+ import std.math.algebraic : nextPow2;
+ private enum bits = bitSizeOf!T;
+ alias PackedPtr = PackedPtrImpl!(T, bits > 1 ? nextPow2(bits - 1) : 1);
+}
+
+struct PackedPtrImpl(T, size_t bits)
+{
+pure nothrow:
+ static assert(isPow2OrZero(bits));
+
+ this(inout(size_t)* ptr)inout @safe @nogc
+ {
+ origin = ptr;
+ }
+
+ private T simpleIndex(size_t n) inout
+ {
+ immutable q = n / factor;
+ immutable r = n % factor;
+ return cast(T)((origin[q] >> bits*r) & mask);
+ }
+
+ private void simpleWrite(TypeOfBitPacked!T val, size_t n)
+ in
+ {
+ static if (isIntegral!T)
+ assert(val <= mask);
+ }
+ do
+ {
+ immutable q = n / factor;
+ immutable r = n % factor;
+ immutable tgt_shift = bits*r;
+ immutable word = origin[q];
+ origin[q] = (word & ~(mask << tgt_shift))
+ | (cast(size_t) val << tgt_shift);
+ }
+
+ static if (factor == bytesPerWord// can safely pack by byte
+ || factor == 1 // a whole word at a time
+ || ((factor == bytesPerWord/2 || factor == bytesPerWord/4)
+ && hasUnalignedReads)) // this needs unaligned reads
+ {
+ static if (factor == bytesPerWord)
+ alias U = ubyte;
+ else static if (factor == bytesPerWord/2)
+ alias U = ushort;
+ else static if (factor == bytesPerWord/4)
+ alias U = uint;
+ else static if (size_t.sizeof == 8 && factor == bytesPerWord/8)
+ alias U = ulong;
+
+ T opIndex(size_t idx) inout
+ {
+ T ret;
+ version (LittleEndian)
+ ret = __ctfe ? simpleIndex(idx) :
+ cast(inout(T))(cast(U*) origin)[idx];
+ else
+ ret = simpleIndex(idx);
+ return ret;
+ }
+
+ static if (isBitPacked!T) // lack of user-defined implicit conversion
+ {
+ void opIndexAssign(T val, size_t idx)
+ {
+ return opIndexAssign(cast(TypeOfBitPacked!T) val, idx);
+ }
+ }
+
+ void opIndexAssign(TypeOfBitPacked!T val, size_t idx)
+ {
+ version (LittleEndian)
+ {
+ if (__ctfe)
+ simpleWrite(val, idx);
+ else
+ (cast(U*) origin)[idx] = cast(U) val;
+ }
+ else
+ simpleWrite(val, idx);
+ }
+ }
+ else
+ {
+ T opIndex(size_t n) inout
+ {
+ return simpleIndex(n);
+ }
+
+ static if (isBitPacked!T) // lack of user-defined implicit conversion
+ {
+ void opIndexAssign(T val, size_t idx)
+ {
+ return opIndexAssign(cast(TypeOfBitPacked!T) val, idx);
+ }
+ }
+
+ void opIndexAssign(TypeOfBitPacked!T val, size_t n)
+ {
+ return simpleWrite(val, n);
+ }
+ }
+
+private:
+ // factor - number of elements in one machine word
+ enum factor = size_t.sizeof*8/bits, mask = 2^^bits-1;
+ enum bytesPerWord = size_t.sizeof;
+ size_t* origin;
+}
+
+// data is packed only by power of two sized packs per word,
+// thus avoiding mul/div overhead at the cost of ultimate packing
+// this construct doesn't own memory, only provides access, see MultiArray for usage
+struct PackedArrayViewImpl(T, size_t bits)
+{
+pure nothrow:
+
+ this(inout(size_t)* origin, size_t offset, size_t items) inout @safe
+ {
+ ptr = inout(PackedPtr!(T))(origin);
+ ofs = offset;
+ limit = items;
+ }
+
+ bool zeros(size_t s, size_t e)
+ in
+ {
+ assert(s <= e);
+ }
+ do
+ {
+ s += ofs;
+ e += ofs;
+ immutable pad_s = roundUp(s);
+ if ( s >= e)
+ {
+ foreach (i; s .. e)
+ if (ptr[i])
+ return false;
+ return true;
+ }
+ immutable pad_e = roundDown(e);
+ size_t i;
+ for (i=s; i<pad_s; i++)
+ if (ptr[i])
+ return false;
+ // all in between is x*factor elements
+ for (size_t j=i/factor; i<pad_e; i+=factor, j++)
+ if (ptr.origin[j])
+ return false;
+ for (; i<e; i++)
+ if (ptr[i])
+ return false;
+ return true;
+ }
+
+ T opIndex(size_t idx) inout
+ in
+ {
+ assert(idx < limit);
+ }
+ do
+ {
+ return ptr[ofs + idx];
+ }
+
+ static if (isBitPacked!T) // lack of user-defined implicit conversion
+ {
+ void opIndexAssign(T val, size_t idx)
+ {
+ return opIndexAssign(cast(TypeOfBitPacked!T) val, idx);
+ }
+ }
+
+ void opIndexAssign(TypeOfBitPacked!T val, size_t idx)
+ in
+ {
+ assert(idx < limit);
+ }
+ do
+ {
+ ptr[ofs + idx] = val;
+ }
+
+ static if (isBitPacked!T) // lack of user-defined implicit conversions
+ {
+ void opSliceAssign(T val, size_t start, size_t end)
+ {
+ opSliceAssign(cast(TypeOfBitPacked!T) val, start, end);
+ }
+ }
+
+ void opSliceAssign(TypeOfBitPacked!T val, size_t start, size_t end)
+ in
+ {
+ assert(start <= end);
+ assert(end <= limit);
+ }
+ do
+ {
+ // account for ofsetted view
+ start += ofs;
+ end += ofs;
+ // rounded to factor granularity
+ immutable pad_start = roundUp(start);// rounded up
+ if (pad_start >= end) //rounded up >= then end of slice
+ {
+ //nothing to gain, use per element assignment
+ foreach (i; start .. end)
+ ptr[i] = val;
+ return;
+ }
+ immutable pad_end = roundDown(end); // rounded down
+ size_t i;
+ for (i=start; i<pad_start; i++)
+ ptr[i] = val;
+ // all in between is x*factor elements
+ if (pad_start != pad_end)
+ {
+ immutable repval = replicateBits!(factor, bits)(val);
+ for (size_t j=i/factor; i<pad_end; i+=factor, j++)
+ ptr.origin[j] = repval;// so speed it up by factor
+ }
+ for (; i<end; i++)
+ ptr[i] = val;
+ }
+
+ auto opSlice(size_t from, size_t to)inout
+ in
+ {
+ assert(from <= to);
+ assert(ofs + to <= limit);
+ }
+ do
+ {
+ return typeof(this)(ptr.origin, ofs + from, to - from);
+ }
+
+ auto opSlice(){ return opSlice(0, length); }
+
+ bool opEquals(T)(auto ref T arr) const
+ {
+ if (limit != arr.limit)
+ return false;
+ size_t s1 = ofs, s2 = arr.ofs;
+ size_t e1 = s1 + limit, e2 = s2 + limit;
+ if (s1 % factor == 0 && s2 % factor == 0 && length % factor == 0)
+ {
+ return ptr.origin[s1/factor .. e1/factor]
+ == arr.ptr.origin[s2/factor .. e2/factor];
+ }
+ for (size_t i=0;i<limit; i++)
+ if (this[i] != arr[i])
+ return false;
+ return true;
+ }
+
+ @property size_t length()const{ return limit; }
+
+private:
+ auto roundUp()(size_t val){ return (val+factor-1)/factor*factor; }
+ auto roundDown()(size_t val){ return val/factor*factor; }
+ // factor - number of elements in one machine word
+ enum factor = size_t.sizeof*8/bits;
+ PackedPtr!(T) ptr;
+ size_t ofs, limit;
+}
+
+
+private struct SliceOverIndexed(T)
+{
+ enum assignableIndex = is(typeof((){ T.init[0] = Item.init; }));
+ enum assignableSlice = is(typeof((){ T.init[0 .. 0] = Item.init; }));
+ auto opIndex(size_t idx)const
+ in
+ {
+ assert(idx < to - from);
+ }
+ do
+ {
+ return (*arr)[from+idx];
+ }
+
+ static if (assignableIndex)
+ void opIndexAssign(Item val, size_t idx)
+ in
+ {
+ assert(idx < to - from);
+ }
+ do
+ {
+ (*arr)[from+idx] = val;
+ }
+
+ auto opSlice(size_t a, size_t b)
+ {
+ return typeof(this)(from+a, from+b, arr);
+ }
+
+ // static if (assignableSlice)
+ void opSliceAssign(T)(T val, size_t start, size_t end)
+ {
+ (*arr)[start+from .. end+from] = val;
+ }
+
+ auto opSlice()
+ {
+ return typeof(this)(from, to, arr);
+ }
+
+ @property size_t length()const { return to-from;}
+
+ alias opDollar = length;
+
+ @property bool empty()const { return from == to; }
+
+ @property auto front()const { return (*arr)[from]; }
+
+ static if (assignableIndex)
+ @property void front(Item val) { (*arr)[from] = val; }
+
+ @property auto back()const { return (*arr)[to-1]; }
+
+ static if (assignableIndex)
+ @property void back(Item val) { (*arr)[to-1] = val; }
+
+ @property auto save() inout { return this; }
+
+ void popFront() { from++; }
+
+ void popBack() { to--; }
+
+ bool opEquals(T)(auto ref T arr) const
+ {
+ if (arr.length != length)
+ return false;
+ for (size_t i=0; i <length; i++)
+ if (this[i] != arr[i])
+ return false;
+ return true;
+ }
+private:
+ alias Item = typeof(T.init[0]);
+ size_t from, to;
+ T* arr;
+}
+
+@safe pure nothrow @nogc unittest
+{
+ static assert(isRandomAccessRange!(SliceOverIndexed!(int[])));
+}
+
+SliceOverIndexed!(const(T)) sliceOverIndexed(T)(size_t a, size_t b, const(T)* x)
+if (is(Unqual!T == T))
+{
+ return SliceOverIndexed!(const(T))(a, b, x);
+}
+
+// BUG? inout is out of reach
+//...SliceOverIndexed.arr only parameters or stack based variables can be inout
+SliceOverIndexed!T sliceOverIndexed(T)(size_t a, size_t b, T* x)
+if (is(Unqual!T == T))
+{
+ return SliceOverIndexed!T(a, b, x);
+}
+
+@system unittest
+{
+ int[] idxArray = [2, 3, 5, 8, 13];
+ auto sliced = sliceOverIndexed(0, idxArray.length, &idxArray);
+
+ assert(!sliced.empty);
+ assert(sliced.front == 2);
+ sliced.front = 1;
+ assert(sliced.front == 1);
+ assert(sliced.back == 13);
+ sliced.popFront();
+ assert(sliced.front == 3);
+ assert(sliced.back == 13);
+ sliced.back = 11;
+ assert(sliced.back == 11);
+ sliced.popBack();
+
+ assert(sliced.front == 3);
+ assert(sliced[$-1] == 8);
+ sliced = sliced[];
+ assert(sliced[0] == 3);
+ assert(sliced.back == 8);
+ sliced = sliced[1..$];
+ assert(sliced.front == 5);
+ sliced = sliced[0..$-1];
+ assert(sliced[$-1] == 5);
+
+ int[] other = [2, 5];
+ assert(sliced[] == sliceOverIndexed(1, 2, &other));
+ sliceOverIndexed(0, 2, &idxArray)[0 .. 2] = -1;
+ assert(idxArray[0 .. 2] == [-1, -1]);
+ uint[] nullArr = null;
+ auto nullSlice = sliceOverIndexed(0, 0, &idxArray);
+ assert(nullSlice.empty);
+}
+
+private inout(PackedArrayView!T) packedArrayView(T)(inout(size_t)* ptr, size_t items)
+{
+ return inout(PackedArrayView!T)(ptr, 0, items);
+}
+
+
+//============================================================================
+// Partially unrolled binary search using Shar's method
+//============================================================================
+
+string genUnrolledSwitchSearch(size_t size) @safe pure nothrow
+{
+ import core.bitop : bsr;
+ import std.array : replace;
+ import std.conv : to;
+ assert(isPow2OrZero(size));
+ string code = `
+ import core.bitop : bsr;
+ auto power = bsr(m)+1;
+ switch (power){`;
+ size_t i = bsr(size);
+ foreach_reverse (val; 0 .. bsr(size))
+ {
+ auto v = 2^^val;
+ code ~= `
+ case pow:
+ if (pred(range[idx+m], needle))
+ idx += m;
+ goto case;
+ `.replace("m", to!string(v))
+ .replace("pow", to!string(i));
+ i--;
+ }
+ code ~= `
+ case 0:
+ if (pred(range[idx], needle))
+ idx += 1;
+ goto default;
+ `;
+ code ~= `
+ default:
+ }`;
+ return code;
+}
+
+bool isPow2OrZero(size_t sz) @safe pure nothrow @nogc
+{
+ // See also: std.math.isPowerOf2()
+ return (sz & (sz-1)) == 0;
+}
+
+size_t uniformLowerBound(alias pred, Range, T)(Range range, T needle)
+if (is(T : ElementType!Range))
+{
+ assert(isPow2OrZero(range.length));
+ size_t idx = 0, m = range.length/2;
+ while (m != 0)
+ {
+ if (pred(range[idx+m], needle))
+ idx += m;
+ m /= 2;
+ }
+ if (pred(range[idx], needle))
+ idx += 1;
+ return idx;
+}
+
+size_t switchUniformLowerBound(alias pred, Range, T)(Range range, T needle)
+if (is(T : ElementType!Range))
+{
+ assert(isPow2OrZero(range.length));
+ size_t idx = 0, m = range.length/2;
+ enum max = 1 << 10;
+ while (m >= max)
+ {
+ if (pred(range[idx+m], needle))
+ idx += m;
+ m /= 2;
+ }
+ mixin(genUnrolledSwitchSearch(max));
+ return idx;
+}
+
+template sharMethod(alias uniLowerBound)
+{
+ size_t sharMethod(alias _pred="a<b", Range, T)(Range range, T needle)
+ if (is(T : ElementType!Range))
+ {
+ import std.functional : binaryFun;
+ import std.math.algebraic : nextPow2, truncPow2;
+ alias pred = binaryFun!_pred;
+ if (range.length == 0)
+ return 0;
+ if (isPow2OrZero(range.length))
+ return uniLowerBound!pred(range, needle);
+ size_t n = truncPow2(range.length);
+ if (pred(range[n-1], needle))
+ {// search in another 2^^k area that fully covers the tail of range
+ size_t k = nextPow2(range.length - n + 1);
+ return range.length - k + uniLowerBound!pred(range[$-k..$], needle);
+ }
+ else
+ return uniLowerBound!pred(range[0 .. n], needle);
+ }
+}
+
+alias sharLowerBound = sharMethod!uniformLowerBound;
+alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound;
+
+@safe unittest
+{
+ import std.array : array;
+ import std.range : assumeSorted, iota;
+
+ auto stdLowerBound(T)(T[] range, T needle)
+ {
+ return assumeSorted(range).lowerBound(needle).length;
+ }
+ immutable MAX = 5*1173;
+ auto arr = array(iota(5, MAX, 5));
+ assert(arr.length == MAX/5-1);
+ foreach (i; 0 .. MAX+5)
+ {
+ auto st = stdLowerBound(arr, i);
+ assert(st == sharLowerBound(arr, i));
+ assert(st == sharSwitchLowerBound(arr, i));
+ }
+ arr = [];
+ auto st = stdLowerBound(arr, 33);
+ assert(st == sharLowerBound(arr, 33));
+ assert(st == sharSwitchLowerBound(arr, 33));
+}
+//============================================================================
+
+@safe
+{
+// hope to see simillar stuff in public interface... once Allocators are out
+//@@@BUG moveFront and friends? dunno, for now it's POD-only
+
+@trusted size_t genericReplace(Policy=void, T, Range)
+ (ref T dest, size_t from, size_t to, Range stuff)
+{
+ import std.algorithm.mutation : copy;
+ size_t delta = to - from;
+ size_t stuff_end = from+stuff.length;
+ if (stuff.length > delta)
+ {// replace increases length
+ delta = stuff.length - delta;// now, new is > old by delta
+ static if (is(Policy == void))
+ dest.length = dest.length+delta;//@@@BUG lame @property
+ else
+ dest = Policy.realloc(dest, dest.length+delta);
+ copyBackwards(dest[to .. dest.length-delta],
+ dest[to+delta .. dest.length]);
+ copyForward(stuff, dest[from .. stuff_end]);
+ }
+ else if (stuff.length == delta)
+ {
+ copy(stuff, dest[from .. to]);
+ }
+ else
+ {// replace decreases length by delta
+ delta = delta - stuff.length;
+ copy(stuff, dest[from .. stuff_end]);
+ copyForward(dest[to .. dest.length],
+ dest[stuff_end .. dest.length-delta]);
+ static if (is(Policy == void))
+ dest.length = dest.length - delta;//@@@BUG lame @property
+ else
+ dest = Policy.realloc(dest, dest.length-delta);
+ }
+ return stuff_end;
+}
+
+
+// Simple storage manipulation policy
+@safe private struct GcPolicy
+{
+ import std.traits : isDynamicArray;
+
+ static T[] dup(T)(const T[] arr)
+ {
+ return arr.dup;
+ }
+
+ static T[] alloc(T)(size_t size)
+ {
+ return new T[size];
+ }
+
+ static T[] realloc(T)(T[] arr, size_t sz)
+ {
+ arr.length = sz;
+ return arr;
+ }
+
+ static void replaceImpl(T, Range)(ref T[] dest, size_t from, size_t to, Range stuff)
+ {
+ replaceInPlace(dest, from, to, stuff);
+ }
+
+ static void append(T, V)(ref T[] arr, V value)
+ if (!isInputRange!V)
+ {
+ arr ~= force!T(value);
+ }
+
+ static void append(T, V)(ref T[] arr, V value)
+ if (isInputRange!V)
+ {
+ insertInPlace(arr, arr.length, value);
+ }
+
+ static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000
+ if (isDynamicArray!T && is(Unqual!T == T))
+ {
+ debug
+ {
+ arr[] = cast(typeof(T.init[0]))(0xdead_beef);
+ }
+ arr = null;
+ }
+
+ static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000
+ if (isDynamicArray!T && !is(Unqual!T == T))
+ {
+ arr = null;
+ }
+}
+
+// ditto
+@safe struct ReallocPolicy
+{
+ import std.range.primitives : hasLength;
+
+ static T[] dup(T)(const T[] arr)
+ {
+ auto result = alloc!T(arr.length);
+ result[] = arr[];
+ return result;
+ }
+
+ static T[] alloc(T)(size_t size) @trusted
+ {
+ import std.internal.memory : enforceMalloc;
+
+ import core.checkedint : mulu;
+ bool overflow;
+ size_t nbytes = mulu(size, T.sizeof, overflow);
+ if (overflow) assert(0);
+
+ auto ptr = cast(T*) enforceMalloc(nbytes);
+ return ptr[0 .. size];
+ }
+
+ static T[] realloc(T)(return scope T[] arr, size_t size) @trusted
+ {
+ import std.internal.memory : enforceRealloc;
+ if (!size)
+ {
+ destroy(arr);
+ return null;
+ }
+
+ import core.checkedint : mulu;
+ bool overflow;
+ size_t nbytes = mulu(size, T.sizeof, overflow);
+ if (overflow) assert(0);
+
+ auto ptr = cast(T*) enforceRealloc(arr.ptr, nbytes);
+ return ptr[0 .. size];
+ }
+
+ static void replaceImpl(T, Range)(ref T[] dest, size_t from, size_t to, Range stuff)
+ {
+ genericReplace!(ReallocPolicy)(dest, from, to, stuff);
+ }
+
+ static void append(T, V)(ref T[] arr, V value)
+ if (!isInputRange!V)
+ {
+ if (arr.length == size_t.max) assert(0);
+ arr = realloc(arr, arr.length+1);
+ arr[$-1] = force!T(value);
+ }
+
+ pure @safe unittest
+ {
+ int[] arr;
+ ReallocPolicy.append(arr, 3);
+
+ import std.algorithm.comparison : equal;
+ assert(equal(arr, [3]));
+ }
+
+ static void append(T, V)(ref T[] arr, V value)
+ if (isInputRange!V && hasLength!V)
+ {
+ import core.checkedint : addu;
+ bool overflow;
+ size_t nelems = addu(arr.length, value.length, overflow);
+ if (overflow) assert(0);
+
+ arr = realloc(arr, nelems);
+
+ import std.algorithm.mutation : copy;
+ copy(value, arr[$-value.length..$]);
+ }
+
+ pure @safe unittest
+ {
+ int[] arr;
+ ReallocPolicy.append(arr, [1,2,3]);
+
+ import std.algorithm.comparison : equal;
+ assert(equal(arr, [1,2,3]));
+ }
+
+ static void destroy(T)(scope ref T[] arr) @trusted
+ {
+ import core.memory : pureFree;
+ if (arr.ptr)
+ pureFree(arr.ptr);
+ arr = null;
+ }
+}
+
+//build hack
+alias _RealArray = CowArray!ReallocPolicy;
+
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ with(ReallocPolicy)
+ {
+ bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result,
+ string file = __FILE__, size_t line = __LINE__)
+ {
+ {
+ replaceImpl(orig, from, to, toReplace);
+ scope(exit) destroy(orig);
+ if (!equal(orig, result))
+ return false;
+ }
+ return true;
+ }
+ static T[] arr(T)(T[] args... )
+ {
+ return dup(args);
+ }
+
+ assert(test(arr([1, 2, 3, 4]), 0, 0, [5, 6, 7], [5, 6, 7, 1, 2, 3, 4]));
+ assert(test(arr([1, 2, 3, 4]), 0, 2, cast(int[])[], [3, 4]));
+ assert(test(arr([1, 2, 3, 4]), 0, 4, [5, 6, 7], [5, 6, 7]));
+ assert(test(arr([1, 2, 3, 4]), 0, 2, [5, 6, 7], [5, 6, 7, 3, 4]));
+ assert(test(arr([1, 2, 3, 4]), 2, 3, [5, 6, 7], [1, 2, 5, 6, 7, 4]));
+ }
+}
+
+/**
+ Tests if T is some kind a set of code points. Intended for template constraints.
+*/
+public template isCodepointSet(T)
+{
+ static if (is(T dummy == InversionList!(Args), Args...))
+ enum isCodepointSet = true;
+ else
+ enum isCodepointSet = false;
+}
+
+/**
+ Tests if `T` is a pair of integers that implicitly convert to `V`.
+ The following code must compile for any pair `T`:
+ ---
+ (T x){ V a = x[0]; V b = x[1];}
+ ---
+ The following must not compile:
+ ---
+ (T x){ V c = x[2];}
+ ---
+*/
+public template isIntegralPair(T, V=uint)
+{
+ enum isIntegralPair = is(typeof((T x){ V a = x[0]; V b = x[1];}))
+ && !is(typeof((T x){ V c = x[2]; }));
+}
+
+
+/**
+ The recommended default type for set of $(CODEPOINTS).
+ For details, see the current implementation: $(LREF InversionList).
+*/
+public alias CodepointSet = InversionList!GcPolicy;
+
+
+//@@@BUG: std.typecons tuples depend on std.format to produce fields mixin
+// which relies on std.uni.isGraphical and this chain blows up with Forward reference error
+// hence below doesn't seem to work
+// public alias CodepointInterval = Tuple!(uint, "a", uint, "b");
+
+/**
+ The recommended type of $(REF Tuple, std,_typecons)
+ to represent [a, b$(RPAREN) intervals of $(CODEPOINTS). As used in $(LREF InversionList).
+ Any interval type should pass $(LREF isIntegralPair) trait.
+*/
+public struct CodepointInterval
+{
+pure:
+ uint[2] _tuple;
+ alias _tuple this;
+
+@safe pure nothrow @nogc:
+
+ this(uint low, uint high)
+ {
+ _tuple[0] = low;
+ _tuple[1] = high;
+ }
+ bool opEquals(T)(T val) const
+ {
+ return this[0] == val[0] && this[1] == val[1];
+ }
+ @property ref inout(uint) a() inout { return _tuple[0]; }
+ @property ref inout(uint) b() inout { return _tuple[1]; }
+}
+
+/**
+ $(P
+ `InversionList` is a set of $(CODEPOINTS)
+ represented as an array of open-right [a, b$(RPAREN)
+ intervals (see $(LREF CodepointInterval) above).
+ The name comes from the way the representation reads left to right.
+ For instance a set of all values [10, 50$(RPAREN), [80, 90$(RPAREN),
+ plus a singular value 60 looks like this:
+ )
+ ---
+ 10, 50, 60, 61, 80, 90
+ ---
+ $(P
+ The way to read this is: start with negative meaning that all numbers
+ smaller then the next one are not present in this set (and positive -
+ the contrary). Then switch positive/negative after each
+ number passed from left to right.
+ )
+ $(P This way negative spans until 10, then positive until 50,
+ then negative until 60, then positive until 61, and so on.
+ As seen this provides a space-efficient storage of highly redundant data
+ that comes in long runs. A description which Unicode $(CHARACTER)
+ properties fit nicely. The technique itself could be seen as a variation
+ on $(LINK2 https://en.wikipedia.org/wiki/Run-length_encoding, RLE encoding).
+ )
+
+ $(P Sets are value types (just like `int` is) thus they
+ are never aliased.
+ )
+ Example:
+ ---
+ auto a = CodepointSet('a', 'z'+1);
+ auto b = CodepointSet('A', 'Z'+1);
+ auto c = a;
+ a = a | b;
+ assert(a == CodepointSet('A', 'Z'+1, 'a', 'z'+1));
+ assert(a != c);
+ ---
+ $(P See also $(LREF unicode) for simpler construction of sets
+ from predefined ones.
+ )
+
+ $(P Memory usage is 8 bytes per each contiguous interval in a set.
+ The value semantics are achieved by using the
+ $(HTTP en.wikipedia.org/wiki/Copy-on-write, COW) technique
+ and thus it's $(RED not) safe to cast this type to $(D_KEYWORD shared).
+ )
+
+ Note:
+ $(P It's not recommended to rely on the template parameters
+ or the exact type of a current $(CODEPOINT) set in `std.uni`.
+ The type and parameters may change when the standard
+ allocators design is finalized.
+ Use $(LREF isCodepointSet) with templates or just stick with the default
+ alias $(LREF CodepointSet) throughout the whole code base.
+ )
+*/
+public struct InversionList(SP=GcPolicy)
+{
+ import std.range : assumeSorted;
+
+ /**
+ Construct from another code point set of any type.
+ */
+ this(Set)(Set set) pure
+ if (isCodepointSet!Set)
+ {
+ uint[] arr;
+ foreach (v; set.byInterval)
+ {
+ arr ~= v.a;
+ arr ~= v.b;
+ }
+ data = CowArray!(SP).reuse(arr);
+ }
+
+ /**
+ Construct a set from a forward range of code point intervals.
+ */
+ this(Range)(Range intervals) pure
+ if (isForwardRange!Range && isIntegralPair!(ElementType!Range))
+ {
+ uint[] arr;
+ foreach (v; intervals)
+ {
+ SP.append(arr, v.a);
+ SP.append(arr, v.b);
+ }
+ data = CowArray!(SP).reuse(arr);
+ sanitize(); //enforce invariant: sort intervals etc.
+ }
+
+ //helper function that avoids sanity check to be CTFE-friendly
+ private static fromIntervals(Range)(Range intervals) pure
+ {
+ import std.algorithm.iteration : map;
+ import std.range : roundRobin;
+ auto flattened = roundRobin(intervals.save.map!"a[0]"(),
+ intervals.save.map!"a[1]"());
+ InversionList set;
+ set.data = CowArray!(SP)(flattened);
+ return set;
+ }
+ //ditto untill sort is CTFE-able
+ private static fromIntervals()(uint[] intervals...) pure
+ in
+ {
+ import std.conv : text;
+ assert(intervals.length % 2 == 0, "Odd number of interval bounds [a, b)!");
+ for (uint i = 0; i < intervals.length; i += 2)
+ {
+ auto a = intervals[i], b = intervals[i+1];
+ assert(a < b, text("illegal interval [a, b): ", a, " > ", b));
+ }
+ }
+ do
+ {
+ InversionList set;
+ set.data = CowArray!(SP)(intervals);
+ return set;
+ }
+
+ /**
+ Construct a set from plain values of code point intervals.
+ */
+ this()(uint[] intervals...)
+ in
+ {
+ import std.conv : text;
+ assert(intervals.length % 2 == 0, "Odd number of interval bounds [a, b)!");
+ for (uint i = 0; i < intervals.length; i += 2)
+ {
+ auto a = intervals[i], b = intervals[i+1];
+ assert(a < b, text("illegal interval [a, b): ", a, " > ", b));
+ }
+ }
+ do
+ {
+ data = CowArray!(SP)(intervals);
+ sanitize(); //enforce invariant: sort intervals etc.
+ }
+
+ ///
+ pure @safe unittest
+ {
+ import std.algorithm.comparison : equal;
+
+ auto set = CodepointSet('a', 'z'+1, 'а', 'Ñ'+1);
+ foreach (v; 'a'..'z'+1)
+ assert(set[v]);
+ // Cyrillic lowercase interval
+ foreach (v; 'а'..'Ñ'+1)
+ assert(set[v]);
+ //specific order is not required, intervals may interesect
+ auto set2 = CodepointSet('а', 'Ñ'+1, 'a', 'd', 'b', 'z'+1);
+ //the same end result
+ assert(set2.byInterval.equal(set.byInterval));
+ // test constructor this(Range)(Range intervals)
+ auto chessPiecesWhite = CodepointInterval(9812, 9818);
+ auto chessPiecesBlack = CodepointInterval(9818, 9824);
+ auto set3 = CodepointSet([chessPiecesWhite, chessPiecesBlack]);
+ foreach (v; '♔'..'♟'+1)
+ assert(set3[v]);
+ }
+
+ /**
+ Get range that spans all of the $(CODEPOINT) intervals in this $(LREF InversionList).
+ */
+ @property auto byInterval() scope
+ {
+ // TODO: change this to data[] once the -dip1000 errors have been fixed
+ // see e.g. https://github.com/dlang/phobos/pull/6638
+ import std.array : array;
+ return Intervals!(typeof(data.array))(data.array);
+ }
+
+ @safe unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ auto set = CodepointSet('A', 'D'+1, 'a', 'd'+1);
+
+ assert(set.byInterval.equal([tuple('A','E'), tuple('a','e')]));
+ }
+
+ package(std) @property const(CodepointInterval)[] intervals() const
+ {
+ import std.array : array;
+ return Intervals!(typeof(data[]))(data[]).array;
+ }
+
+ /**
+ Tests the presence of code point `val` in this set.
+ */
+ bool opIndex(uint val) const
+ {
+ // the <= ensures that searching in interval of [a, b) for 'a' you get .length == 1
+ // return assumeSorted!((a,b) => a <= b)(data[]).lowerBound(val).length & 1;
+ return sharSwitchLowerBound!"a <= b"(data[], val) & 1;
+ }
+
+ ///
+ pure @safe unittest
+ {
+ auto gothic = unicode.Gothic;
+ // Gothic letter ahsa
+ assert(gothic['\U00010330']);
+ // no ascii in Gothic obviously
+ assert(!gothic['$']);
+ }
+
+
+ // Linear scan for `ch`. Useful only for small sets.
+ // TODO:
+ // used internally in std.regex
+ // should be properly exposed in a public API ?
+ package(std) auto scanFor()(dchar ch) const
+ {
+ immutable len = data.length;
+ for (size_t i = 0; i < len; i++)
+ if (ch < data[i])
+ return i & 1;
+ return 0;
+ }
+
+ /// Number of $(CODEPOINTS) in this set
+ @property size_t length()
+ {
+ size_t sum = 0;
+ foreach (iv; byInterval)
+ {
+ sum += iv.b - iv.a;
+ }
+ return sum;
+ }
+
+// bootstrap full set operations from 4 primitives (suitable as a template mixin):
+// addInterval, skipUpTo, dropUpTo & byInterval iteration
+//============================================================================
+public:
+ /**
+ $(P Sets support natural syntax for set algebra, namely: )
+ $(BOOKTABLE ,
+ $(TR $(TH Operator) $(TH Math notation) $(TH Description) )
+ $(TR $(TD &) $(TD a ∩ b) $(TD intersection) )
+ $(TR $(TD |) $(TD a ∪ b) $(TD union) )
+ $(TR $(TD -) $(TD a ∖ b) $(TD subtraction) )
+ $(TR $(TD ~) $(TD a ~ b) $(TD symmetric set difference i.e. (a ∪ b) \ (a ∩ b)) )
+ )
+ */
+ This opBinary(string op, U)(U rhs)
+ if (isCodepointSet!U || is(U:dchar))
+ {
+ static if (op == "&" || op == "|" || op == "~")
+ {// symmetric ops thus can swap arguments to reuse r-value
+ static if (is(U:dchar))
+ {
+ auto tmp = this;
+ mixin("tmp "~op~"= rhs; ");
+ return tmp;
+ }
+ else
+ {
+ static if (is(Unqual!U == U))
+ {
+ // try hard to reuse r-value
+ mixin("rhs "~op~"= this;");
+ return rhs;
+ }
+ else
+ {
+ auto tmp = this;
+ mixin("tmp "~op~"= rhs;");
+ return tmp;
+ }
+ }
+ }
+ else static if (op == "-") // anti-symmetric
+ {
+ auto tmp = this;
+ tmp -= rhs;
+ return tmp;
+ }
+ else
+ static assert(0, "no operator "~op~" defined for Set");
+ }
+
+ ///
+ pure @safe unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ auto lower = unicode.LowerCase;
+ auto upper = unicode.UpperCase;
+ auto ascii = unicode.ASCII;
+
+ assert((lower & upper).empty); // no intersection
+ auto lowerASCII = lower & ascii;
+ assert(lowerASCII.byCodepoint.equal(iota('a', 'z'+1)));
+ // throw away all of the lowercase ASCII
+ assert((ascii - lower).length == 128 - 26);
+
+ auto onlyOneOf = lower ~ ascii;
+ assert(!onlyOneOf['Δ']); // not ASCII and not lowercase
+ assert(onlyOneOf['$']); // ASCII and not lowercase
+ assert(!onlyOneOf['a']); // ASCII and lowercase
+ assert(onlyOneOf['Ñ']); // not ASCII but lowercase
+
+ // throw away all cased letters from ASCII
+ auto noLetters = ascii - (lower | upper);
+ assert(noLetters.length == 128 - 26*2);
+ }
+
+ /// The 'op=' versions of the above overloaded operators.
+ ref This opOpAssign(string op, U)(U rhs)
+ if (isCodepointSet!U || is(U:dchar))
+ {
+ static if (op == "|") // union
+ {
+ static if (is(U:dchar))
+ {
+ this.addInterval(rhs, rhs+1);
+ return this;
+ }
+ else
+ return this.add(rhs);
+ }
+ else static if (op == "&") // intersection
+ return this.intersect(rhs);// overloaded
+ else static if (op == "-") // set difference
+ return this.sub(rhs);// overloaded
+ else static if (op == "~") // symmetric set difference
+ {
+ auto copy = this & rhs;
+ this |= rhs;
+ this -= copy;
+ return this;
+ }
+ else
+ static assert(0, "no operator "~op~" defined for Set");
+ }
+
+ /**
+ Tests the presence of codepoint `ch` in this set,
+ the same as $(LREF opIndex).
+ */
+ bool opBinaryRight(string op: "in", U)(U ch) const
+ if (is(U : dchar))
+ {
+ return this[ch];
+ }
+
+ ///
+ pure @safe unittest
+ {
+ assert('Ñ' in unicode.Cyrillic);
+ assert(!('z' in unicode.Cyrillic));
+ }
+
+
+
+ /**
+ * Obtains a set that is the inversion of this set.
+ *
+ * See_Also: $(LREF inverted)
+ */
+ auto opUnary(string op: "!")()
+ {
+ return this.inverted;
+ }
+
+ /**
+ A range that spans each $(CODEPOINT) in this set.
+ */
+ @property auto byCodepoint()
+ {
+ static struct CodepointRange
+ {
+ this(This set)
+ {
+ r = set.byInterval;
+ if (!r.empty)
+ cur = r.front.a;
+ }
+
+ @property dchar front() const
+ {
+ return cast(dchar) cur;
+ }
+
+ @property bool empty() const
+ {
+ return r.empty;
+ }
+
+ void popFront()
+ {
+ cur++;
+ while (cur >= r.front.b)
+ {
+ r.popFront();
+ if (r.empty)
+ break;
+ cur = r.front.a;
+ }
+ }
+ private:
+ uint cur;
+ typeof(This.init.byInterval) r;
+ }
+
+ return CodepointRange(this);
+ }
+
+ ///
+ pure @safe unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ auto set = unicode.ASCII;
+ set.byCodepoint.equal(iota(0, 0x80));
+ }
+
+ /**
+ $(P Obtain textual representation of this set in from of
+ open-right intervals and feed it to `sink`.
+ )
+ $(P Used by various standard formatting facilities such as
+ $(REF formattedWrite, std,format), $(REF write, std,stdio),
+ $(REF writef, std,stdio), $(REF to, std,conv) and others.
+ )
+ Example:
+ ---
+ import std.conv;
+ assert(unicode.ASCII.to!string == "[0..128$(RPAREN)");
+ ---
+ */
+
+ private import std.format.spec : FormatSpec;
+
+ /***************************************
+ * Obtain a textual representation of this InversionList
+ * in form of open-right intervals.
+ *
+ * The formatting flag is applied individually to each value, for example:
+ * $(LI $(B %s) and $(B %d) format the intervals as a [low .. high$(RPAREN) range of integrals)
+ * $(LI $(B %x) formats the intervals as a [low .. high$(RPAREN) range of lowercase hex characters)
+ * $(LI $(B %X) formats the intervals as a [low .. high$(RPAREN) range of uppercase hex characters)
+ */
+ void toString(Writer)(scope Writer sink, scope const ref FormatSpec!char fmt) /* const */
+ {
+ import std.format.write : formatValue;
+ auto range = byInterval;
+ if (range.empty)
+ return;
+
+ while (1)
+ {
+ auto i = range.front;
+ range.popFront();
+
+ put(sink, "[");
+ formatValue(sink, i.a, fmt);
+ put(sink, "..");
+ formatValue(sink, i.b, fmt);
+ put(sink, ")");
+ if (range.empty) return;
+ put(sink, " ");
+ }
+ }
+
+ ///
+ pure @safe unittest
+ {
+ import std.conv : to;
+ import std.format : format;
+ import std.uni : unicode;
+
+ assert(unicode.Cyrillic.to!string ==
+ "[1024..1157) [1159..1320) [7467..7468) [7544..7545) [11744..11776) [42560..42648) [42655..42656)");
+
+ // The specs '%s' and '%d' are equivalent to the to!string call above.
+ assert(format("%d", unicode.Cyrillic) == unicode.Cyrillic.to!string);
+
+ assert(format("%#x", unicode.Cyrillic) ==
+ "[0x400..0x485) [0x487..0x528) [0x1d2b..0x1d2c) [0x1d78..0x1d79) [0x2de0..0x2e00) "
+ ~"[0xa640..0xa698) [0xa69f..0xa6a0)");
+
+ assert(format("%#X", unicode.Cyrillic) ==
+ "[0X400..0X485) [0X487..0X528) [0X1D2B..0X1D2C) [0X1D78..0X1D79) [0X2DE0..0X2E00) "
+ ~"[0XA640..0XA698) [0XA69F..0XA6A0)");
+ }
+
+ pure @safe unittest
+ {
+ import std.exception : assertThrown;
+ import std.format : format, FormatException;
+ assertThrown!FormatException(format("%z", unicode.ASCII));
+ }
+
+
+ /**
+ Add an interval [a, b$(RPAREN) to this set.
+ */
+ ref add()(uint a, uint b)
+ {
+ addInterval(a, b);
+ return this;
+ }
+
+ ///
+ pure @safe unittest
+ {
+ CodepointSet someSet;
+ someSet.add('0', '5').add('A','Z'+1);
+ someSet.add('5', '9'+1);
+ assert(someSet['0']);
+ assert(someSet['5']);
+ assert(someSet['9']);
+ assert(someSet['Z']);
+ }
+
+private:
+
+ package(std) // used from: std.regex.internal.parser
+ ref intersect(U)(U rhs)
+ if (isCodepointSet!U)
+ {
+ Marker mark;
+ foreach ( i; rhs.byInterval)
+ {
+ mark = this.dropUpTo(i.a, mark);
+ mark = this.skipUpTo(i.b, mark);
+ }
+ this.dropUpTo(uint.max, mark);
+ return this;
+ }
+
+ ref intersect()(dchar ch)
+ {
+ foreach (i; byInterval)
+ if (i.a <= ch && ch < i.b)
+ return this = This.init.add(ch, ch+1);
+ this = This.init;
+ return this;
+ }
+
+ pure @safe unittest
+ {
+ assert(unicode.Cyrillic.intersect('-').byInterval.empty);
+ }
+
+ ref sub()(dchar ch)
+ {
+ return subChar(ch);
+ }
+
+ // same as the above except that skip & drop parts are swapped
+ package(std) // used from: std.regex.internal.parser
+ ref sub(U)(U rhs)
+ if (isCodepointSet!U)
+ {
+ Marker mark;
+ foreach (i; rhs.byInterval)
+ {
+ mark = this.skipUpTo(i.a, mark);
+ mark = this.dropUpTo(i.b, mark);
+ }
+ return this;
+ }
+
+ package(std) // used from: std.regex.internal.parse
+ ref add(U)(U rhs)
+ if (isCodepointSet!U)
+ {
+ Marker start;
+ foreach (i; rhs.byInterval)
+ {
+ start = addInterval(i.a, i.b, start);
+ }
+ return this;
+ }
+
+// end of mixin-able part
+//============================================================================
+public:
+ /**
+ Obtains a set that is the inversion of this set.
+
+ See the '!' $(LREF opUnary) for the same but using operators.
+ */
+ @property auto inverted()
+ {
+ InversionList inversion = this;
+ if (inversion.data.length == 0)
+ {
+ inversion.addInterval(0, lastDchar+1);
+ return inversion;
+ }
+ if (inversion.data[0] != 0)
+ genericReplace(inversion.data, 0, 0, [0]);
+ else
+ genericReplace(inversion.data, 0, 1, cast(uint[]) null);
+ if (data[data.length-1] != lastDchar+1)
+ genericReplace(inversion.data,
+ inversion.data.length, inversion.data.length, [lastDchar+1]);
+ else
+ genericReplace(inversion.data,
+ inversion.data.length-1, inversion.data.length, cast(uint[]) null);
+
+ return inversion;
+ }
+
+ ///
+ pure @safe unittest
+ {
+ auto set = unicode.ASCII;
+ // union with the inverse gets all of the code points in the Unicode
+ assert((set | set.inverted).length == 0x110000);
+ // no intersection with the inverse
+ assert((set & set.inverted).empty);
+ }
+
+ package(std) static string toSourceCode(const(CodepointInterval)[] range, string funcName)
+ {
+ import std.algorithm.searching : countUntil;
+ import std.format : format;
+ enum maxBinary = 3;
+ static string linearScope(R)(R ivals, string indent)
+ {
+ string result = indent~"{\n";
+ string deeper = indent~" ";
+ foreach (ival; ivals)
+ {
+ immutable span = ival[1] - ival[0];
+ assert(span != 0);
+ if (span == 1)
+ {
+ result ~= format("%sif (ch == %s) return true;\n", deeper, ival[0]);
+ }
+ else if (span == 2)
+ {
+ result ~= format("%sif (ch == %s || ch == %s) return true;\n",
+ deeper, ival[0], ival[0]+1);
+ }
+ else
+ {
+ if (ival[0] != 0) // dchar is unsigned and < 0 is useless
+ result ~= format("%sif (ch < %s) return false;\n", deeper, ival[0]);
+ result ~= format("%sif (ch < %s) return true;\n", deeper, ival[1]);
+ }
+ }
+ result ~= format("%sreturn false;\n%s}\n", deeper, indent); // including empty range of intervals
+ return result;
+ }
+
+ static string binaryScope(R)(R ivals, string indent) @safe
+ {
+ // time to do unrolled comparisons?
+ if (ivals.length < maxBinary)
+ return linearScope(ivals, indent);
+ else
+ return bisect(ivals, ivals.length/2, indent);
+ }
+
+ // not used yet if/elsebinary search is far better with DMD as of 2.061
+ // and GDC is doing fine job either way
+ static string switchScope(R)(R ivals, string indent)
+ {
+ string result = indent~"switch (ch){\n";
+ string deeper = indent~" ";
+ foreach (ival; ivals)
+ {
+ if (ival[0]+1 == ival[1])
+ {
+ result ~= format("%scase %s: return true;\n",
+ deeper, ival[0]);
+ }
+ else
+ {
+ result ~= format("%scase %s: .. case %s: return true;\n",
+ deeper, ival[0], ival[1]-1);
+ }
+ }
+ result ~= deeper~"default: return false;\n"~indent~"}\n";
+ return result;
+ }
+
+ static string bisect(R)(R range, size_t idx, string indent)
+ {
+ string deeper = indent ~ " ";
+ // bisect on one [a, b) interval at idx
+ string result = indent~"{\n";
+ // less branch, < a
+ result ~= format("%sif (ch < %s)\n%s",
+ deeper, range[idx][0], binaryScope(range[0 .. idx], deeper));
+ // middle point, >= a && < b
+ result ~= format("%selse if (ch < %s) return true;\n",
+ deeper, range[idx][1]);
+ // greater or equal branch, >= b
+ result ~= format("%selse\n%s",
+ deeper, binaryScope(range[idx+1..$], deeper));
+ return result~indent~"}\n";
+ }
+
+ string code = format("bool %s(dchar ch) @safe pure nothrow @nogc\n",
+ funcName.empty ? "function" : funcName);
+ // special case first bisection to be on ASCII vs beyond
+ auto tillAscii = countUntil!"a[0] > 0x80"(range);
+ if (tillAscii <= 0) // everything is ASCII or nothing is ascii (-1 & 0)
+ code ~= binaryScope(range, "");
+ else
+ code ~= bisect(range, tillAscii, "");
+ return code;
+ }
+
+ /**
+ Generates string with D source code of unary function with name of
+ `funcName` taking a single `dchar` argument. If `funcName` is empty
+ the code is adjusted to be a lambda function.
+
+ The function generated tests if the $(CODEPOINT) passed
+ belongs to this set or not. The result is to be used with string mixin.
+ The intended usage area is aggressive optimization via meta programming
+ in parser generators and the like.
+
+ Note: Use with care for relatively small or regular sets. It
+ could end up being slower then just using multi-staged tables.
+
+ Example:
+ ---
+ import std.stdio;
+
+ // construct set directly from [a, b$RPAREN intervals
+ auto set = CodepointSet(10, 12, 45, 65, 100, 200);
+ writeln(set);
+ writeln(set.toSourceCode("func"));
+ ---
+
+ The above outputs something along the lines of:
+ ---
+ bool func(dchar ch) @safe pure nothrow @nogc
+ {
+ if (ch < 45)
+ {
+ if (ch == 10 || ch == 11) return true;
+ return false;
+ }
+ else if (ch < 65) return true;
+ else
+ {
+ if (ch < 100) return false;
+ if (ch < 200) return true;
+ return false;
+ }
+ }
+ ---
+ */
+ string toSourceCode(string funcName="")
+ {
+ import std.array : array;
+ auto range = byInterval.array();
+ return toSourceCode(range, funcName);
+ }
+
+ /**
+ True if this set doesn't contain any $(CODEPOINTS).
+ */
+ @property bool empty() const
+ {
+ return data.length == 0;
+ }
+
+ ///
+ pure @safe unittest
+ {
+ CodepointSet emptySet;
+ assert(emptySet.length == 0);
+ assert(emptySet.empty);
+ }
+
+private:
+ alias This = typeof(this);
+ alias Marker = size_t;
+
+ // a random-access range of integral pairs
+ static struct Intervals(Range)
+ {
+ import std.range.primitives : hasAssignableElements;
+
+ this(Range sp) scope
+ {
+ slice = sp;
+ start = 0;
+ end = sp.length;
+ }
+
+ this(Range sp, size_t s, size_t e) scope
+ {
+ slice = sp;
+ start = s;
+ end = e;
+ }
+
+ @property auto front()const
+ {
+ immutable a = slice[start];
+ immutable b = slice[start+1];
+ return CodepointInterval(a, b);
+ }
+
+ //may break sorted property - but we need std.sort to access it
+ //hence package(std) protection attribute
+ static if (hasAssignableElements!Range)
+ package(std) @property void front(CodepointInterval val)
+ {
+ slice[start] = val.a;
+ slice[start+1] = val.b;
+ }
+
+ @property auto back()const
+ {
+ immutable a = slice[end-2];
+ immutable b = slice[end-1];
+ return CodepointInterval(a, b);
+ }
+
+ //ditto about package
+ static if (hasAssignableElements!Range)
+ package(std) @property void back(CodepointInterval val)
+ {
+ slice[end-2] = val.a;
+ slice[end-1] = val.b;
+ }
+
+ void popFront()
+ {
+ start += 2;
+ }
+
+ void popBack()
+ {
+ end -= 2;
+ }
+
+ auto opIndex(size_t idx) const
+ {
+ immutable a = slice[start+idx*2];
+ immutable b = slice[start+idx*2+1];
+ return CodepointInterval(a, b);
+ }
+
+ //ditto about package
+ static if (hasAssignableElements!Range)
+ package(std) void opIndexAssign(CodepointInterval val, size_t idx)
+ {
+ slice[start+idx*2] = val.a;
+ slice[start+idx*2+1] = val.b;
+ }
+
+ auto opSlice(size_t s, size_t e)
+ {
+ return Intervals(slice, s*2+start, e*2+start);
+ }
+
+ @property size_t length()const { return slice.length/2; }
+
+ @property bool empty()const { return start == end; }
+
+ @property auto save(){ return this; }
+ private:
+ size_t start, end;
+ Range slice;
+ }
+
+ // called after construction from intervals
+ // to make sure invariants hold
+ void sanitize()
+ {
+ import std.algorithm.comparison : max;
+ import std.algorithm.mutation : SwapStrategy;
+ import std.algorithm.sorting : sort;
+ if (data.length == 0)
+ return;
+ alias Ival = CodepointInterval;
+ //intervals wrapper for a _range_ over packed array
+ auto ivals = Intervals!(typeof(data[]))(data[]);
+ //@@@BUG@@@ can't use "a.a < b.a" see
+ // https://issues.dlang.org/show_bug.cgi?id=12265
+ sort!((a,b) => a.a < b.a, SwapStrategy.stable)(ivals);
+ // what follows is a variation on stable remove
+ // differences:
+ // - predicate is binary, and is tested against
+ // the last kept element (at 'i').
+ // - predicate mutates lhs (merges rhs into lhs)
+ size_t len = ivals.length;
+ size_t i = 0;
+ size_t j = 1;
+ while (j < len)
+ {
+ if (ivals[i].b >= ivals[j].a)
+ {
+ ivals[i] = Ival(ivals[i].a, max(ivals[i].b, ivals[j].b));
+ j++;
+ }
+ else //unmergable
+ {
+ // check if there is a hole after merges
+ // (in the best case we do 0 writes to ivals)
+ if (j != i+1)
+ ivals[i+1] = ivals[j]; //copy over
+ i++;
+ j++;
+ }
+ }
+ len = i + 1;
+ for (size_t k=0; k + 1 < len; k++)
+ {
+ assert(ivals[k].a < ivals[k].b);
+ assert(ivals[k].b < ivals[k+1].a);
+ }
+ data.length = len * 2;
+ }
+
+ // special case for normal InversionList
+ ref subChar(dchar ch)
+ {
+ auto mark = skipUpTo(ch);
+ if (mark != data.length
+ && data[mark] == ch && data[mark-1] == ch)
+ {
+ // it has split, meaning that ch happens to be in one of intervals
+ data[mark] = data[mark]+1;
+ }
+ return this;
+ }
+
+ //
+ Marker addInterval(int a, int b, Marker hint=Marker.init) scope
+ in
+ {
+ assert(a <= b);
+ }
+ do
+ {
+ import std.range : assumeSorted, SearchPolicy;
+ auto range = assumeSorted(data[]);
+ size_t pos;
+ size_t a_idx = hint + range[hint..$].lowerBound!(SearchPolicy.gallop)(a).length;
+ if (a_idx == range.length)
+ {
+ // [---+++----++++----++++++]
+ // [ a b]
+ data.append(a, b);
+ return data.length-1;
+ }
+ size_t b_idx = range[a_idx .. range.length].lowerBound!(SearchPolicy.gallop)(b).length+a_idx;
+ uint[3] buf = void;
+ uint to_insert;
+ debug(std_uni)
+ {
+ writefln("a_idx=%d; b_idx=%d;", a_idx, b_idx);
+ }
+ if (b_idx == range.length)
+ {
+ // [-------++++++++----++++++-]
+ // [ s a b]
+ if (a_idx & 1)// a in positive
+ {
+ buf[0] = b;
+ to_insert = 1;
+ }
+ else// a in negative
+ {
+ buf[0] = a;
+ buf[1] = b;
+ to_insert = 2;
+ }
+ pos = genericReplace(data, a_idx, b_idx, buf[0 .. to_insert]);
+ return pos - 1;
+ }
+
+ uint top = data[b_idx];
+
+ debug(std_uni)
+ {
+ writefln("a_idx=%d; b_idx=%d;", a_idx, b_idx);
+ writefln("a=%s; b=%s; top=%s;", a, b, top);
+ }
+ if (a_idx & 1)
+ {// a in positive
+ if (b_idx & 1)// b in positive
+ {
+ // [-------++++++++----++++++-]
+ // [ s a b ]
+ buf[0] = top;
+ to_insert = 1;
+ }
+ else // b in negative
+ {
+ // [-------++++++++----++++++-]
+ // [ s a b ]
+ if (top == b)
+ {
+ assert(b_idx+1 < data.length);
+ buf[0] = data[b_idx+1];
+ pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 1]);
+ return pos - 1;
+ }
+ buf[0] = b;
+ buf[1] = top;
+ to_insert = 2;
+ }
+ }
+ else
+ { // a in negative
+ if (b_idx & 1) // b in positive
+ {
+ // [----------+++++----++++++-]
+ // [ a b ]
+ buf[0] = a;
+ buf[1] = top;
+ to_insert = 2;
+ }
+ else// b in negative
+ {
+ // [----------+++++----++++++-]
+ // [ a s b ]
+ if (top == b)
+ {
+ assert(b_idx+1 < data.length);
+ buf[0] = a;
+ buf[1] = data[b_idx+1];
+ pos = genericReplace(data, a_idx, b_idx+2, buf[0 .. 2]);
+ return pos - 1;
+ }
+ buf[0] = a;
+ buf[1] = b;
+ buf[2] = top;
+ to_insert = 3;
+ }
+ }
+ pos = genericReplace(data, a_idx, b_idx+1, buf[0 .. to_insert]);
+ debug(std_uni)
+ {
+ writefln("marker idx: %d; length=%d", pos, data[pos], data.length);
+ writeln("inserting ", buf[0 .. to_insert]);
+ }
+ return pos - 1;
+ }
+
+ //
+ Marker dropUpTo(uint a, Marker pos=Marker.init)
+ in
+ {
+ assert(pos % 2 == 0); // at start of interval
+ }
+ do
+ {
+ auto range = assumeSorted!"a <= b"(data[pos .. data.length]);
+ if (range.empty)
+ return pos;
+ size_t idx = pos;
+ idx += range.lowerBound(a).length;
+
+ debug(std_uni)
+ {
+ writeln("dropUpTo full length=", data.length);
+ writeln(pos,"~~~", idx);
+ }
+ if (idx == data.length)
+ return genericReplace(data, pos, idx, cast(uint[])[]);
+ if (idx & 1)
+ { // a in positive
+ //[--+++----++++++----+++++++------...]
+ // |<---si s a t
+ genericReplace(data, pos, idx, [a]);
+ }
+ else
+ { // a in negative
+ //[--+++----++++++----+++++++-------+++...]
+ // |<---si s a t
+ genericReplace(data, pos, idx, cast(uint[])[]);
+ }
+ return pos;
+ }
+
+ //
+ Marker skipUpTo(uint a, Marker pos=Marker.init)
+ out(result)
+ {
+ assert(result % 2 == 0);// always start of interval
+ //(may be 0-width after-split)
+ }
+ do
+ {
+ assert(data.length % 2 == 0);
+ auto range = assumeSorted!"a <= b"(data[pos .. data.length]);
+ size_t idx = pos+range.lowerBound(a).length;
+
+ if (idx >= data.length) // could have Marker point to recently removed stuff
+ return data.length;
+
+ if (idx & 1)// inside of interval, check for split
+ {
+
+ immutable top = data[idx];
+ if (top == a)// no need to split, it's end
+ return idx+1;
+ immutable start = data[idx-1];
+ if (a == start)
+ return idx-1;
+ // split it up
+ genericReplace(data, idx, idx+1, [a, a, top]);
+ return idx+1; // avoid odd index
+ }
+ return idx;
+ }
+
+ CowArray!SP data;
+}
+
+pure @system unittest
+{
+ import std.conv : to;
+ assert(unicode.ASCII.to!string() == "[0..128)");
+}
+
+// pedantic version for ctfe, and aligned-access only architectures
+@system private uint safeRead24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc
+{
+ idx *= 3;
+ version (LittleEndian)
+ return ptr[idx] + (cast(uint) ptr[idx+1]<<8)
+ + (cast(uint) ptr[idx+2]<<16);
+ else
+ return (cast(uint) ptr[idx]<<16) + (cast(uint) ptr[idx+1]<<8)
+ + ptr[idx+2];
+}
+
+// ditto
+@system private void safeWrite24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc
+{
+ idx *= 3;
+ version (LittleEndian)
+ {
+ ptr[idx] = val & 0xFF;
+ ptr[idx+1] = (val >> 8) & 0xFF;
+ ptr[idx+2] = (val >> 16) & 0xFF;
+ }
+ else
+ {
+ ptr[idx] = (val >> 16) & 0xFF;
+ ptr[idx+1] = (val >> 8) & 0xFF;
+ ptr[idx+2] = val & 0xFF;
+ }
+}
+
+// unaligned x86-like read/write functions
+@system private uint unalignedRead24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc
+{
+ uint* src = cast(uint*)(ptr+3*idx);
+ version (LittleEndian)
+ return *src & 0xFF_FFFF;
+ else
+ return *src >> 8;
+}
+
+// ditto
+@system private void unalignedWrite24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc
+{
+ uint* dest = cast(uint*)(cast(ubyte*) ptr + 3*idx);
+ version (LittleEndian)
+ *dest = val | (*dest & 0xFF00_0000);
+ else
+ *dest = (val << 8) | (*dest & 0xFF);
+}
+
+@system private uint read24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc
+{
+ static if (hasUnalignedReads)
+ return __ctfe ? safeRead24(ptr, idx) : unalignedRead24(ptr, idx);
+ else
+ return safeRead24(ptr, idx);
+}
+
+@system private void write24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc
+{
+ static if (hasUnalignedReads)
+ return __ctfe ? safeWrite24(ptr, val, idx) : unalignedWrite24(ptr, val, idx);
+ else
+ return safeWrite24(ptr, val, idx);
+}
+
+struct CowArray(SP=GcPolicy)
+{
+ import std.range.primitives : hasLength;
+
+ @safe:
+ static auto reuse(uint[] arr)
+ {
+ CowArray cow;
+ cow.data = arr;
+ SP.append(cow.data, 1);
+ assert(cow.refCount == 1);
+ assert(cow.length == arr.length);
+ return cow;
+ }
+
+ this(Range)(Range range)
+ if (isInputRange!Range && hasLength!Range)
+ {
+ import std.algorithm.mutation : copy;
+ length = range.length;
+ copy(range, data[0..$-1]);
+ }
+
+ this(Range)(Range range)
+ if (isForwardRange!Range && !hasLength!Range)
+ {
+ import std.algorithm.mutation : copy;
+ import std.range.primitives : walkLength;
+ immutable len = walkLength(range.save);
+ length = len;
+ copy(range, data[0..$-1]);
+ }
+
+ this(this)
+ {
+ if (!empty)
+ {
+ refCount = refCount + 1;
+ }
+ }
+
+ ~this()
+ {
+ if (!empty)
+ {
+ immutable cnt = refCount;
+ if (cnt == 1)
+ SP.destroy(data);
+ else
+ refCount = cnt - 1;
+ }
+ }
+
+ // no ref-count for empty U24 array
+ @property bool empty() const { return data.length == 0; }
+
+ // report one less then actual size
+ @property size_t length() const
+ {
+ return data.length ? data.length - 1 : 0;
+ }
+
+ //+ an extra slot for ref-count
+ @property void length(size_t len)
+ {
+ import std.algorithm.comparison : min;
+ import std.algorithm.mutation : copy;
+ if (len == 0)
+ {
+ if (!empty)
+ freeThisReference();
+ return;
+ }
+ immutable total = len + 1; // including ref-count
+ if (empty)
+ {
+ data = SP.alloc!uint(total);
+ refCount = 1;
+ return;
+ }
+ immutable cur_cnt = refCount;
+ if (cur_cnt != 1) // have more references to this memory
+ {
+ refCount = cur_cnt - 1;
+ auto new_data = SP.alloc!uint(total);
+ // take shrinking into account
+ auto to_copy = min(total, data.length) - 1;
+ copy(data[0 .. to_copy], new_data[0 .. to_copy]);
+ data = new_data; // before setting refCount!
+ refCount = 1;
+ }
+ else // 'this' is the only reference
+ {
+ // use the realloc (hopefully in-place operation)
+ data = SP.realloc(data, total);
+ refCount = 1; // setup a ref-count in the new end of the array
+ }
+ }
+
+ alias opDollar = length;
+
+ uint opIndex()(size_t idx)const
+ {
+ return data[idx];
+ }
+
+ void opIndexAssign(uint val, size_t idx)
+ {
+ auto cnt = refCount;
+ if (cnt != 1)
+ dupThisReference(cnt);
+ data[idx] = val;
+ }
+
+ //
+ auto opSlice(size_t from, size_t to)
+ {
+ if (!empty)
+ {
+ auto cnt = refCount;
+ if (cnt != 1)
+ dupThisReference(cnt);
+ }
+ return data[from .. to];
+
+ }
+
+ //
+ auto opSlice(size_t from, size_t to) const
+ {
+ return data[from .. to];
+ }
+
+ // length slices before the ref count
+ auto opSlice()
+ {
+ return opSlice(0, length);
+ }
+
+ // ditto
+ auto opSlice() const
+ {
+ return opSlice(0, length);
+ }
+
+ void append(Range)(Range range)
+ if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint))
+ {
+ size_t nl = length + range.length;
+ length = nl;
+ copy(range, this[nl-range.length .. nl]);
+ }
+
+ void append()(uint[] val...)
+ {
+ length = length + val.length;
+ data[$-val.length-1 .. $-1] = val[];
+ }
+
+ bool opEquals()(auto const ref CowArray rhs)const
+ {
+ if (empty ^ rhs.empty)
+ return false; // one is empty and the other isn't
+ return empty || data[0..$-1] == rhs.data[0..$-1];
+ }
+
+private:
+ // ref-count is right after the data
+ @property uint refCount() const
+ {
+ return data[$-1];
+ }
+
+ @property void refCount(uint cnt)
+ {
+ data[$-1] = cnt;
+ }
+
+ void freeThisReference()
+ {
+ immutable count = refCount;
+ if (count != 1) // have more references to this memory
+ {
+ // dec shared ref-count
+ refCount = count - 1;
+ data = [];
+ }
+ else
+ SP.destroy(data);
+ assert(!data.ptr);
+ }
+
+ void dupThisReference(uint count)
+ in
+ {
+ assert(!empty && count != 1 && count == refCount);
+ }
+ do
+ {
+ import std.algorithm.mutation : copy;
+ // dec shared ref-count
+ refCount = count - 1;
+ // copy to the new chunk of RAM
+ auto new_data = SP.alloc!uint(data.length);
+ // bit-blit old stuff except the counter
+ copy(data[0..$-1], new_data[0..$-1]);
+ data = new_data; // before setting refCount!
+ refCount = 1; // so that this updates the right one
+ }
+
+ uint[] data;
+}
+
+pure @safe unittest// Uint24 tests
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.mutation : copy;
+ import std.conv : text;
+ import std.range : iota, chain;
+ import std.range.primitives : isBidirectionalRange, isOutputRange;
+ void funcRef(T)(ref T u24)
+ {
+ u24.length = 2;
+ u24[1] = 1024;
+ T u24_c = u24;
+ assert(u24[1] == 1024);
+ u24.length = 0;
+ assert(u24.empty);
+ u24.append([1, 2]);
+ assert(equal(u24[], [1, 2]));
+ u24.append(111);
+ assert(equal(u24[], [1, 2, 111]));
+ assert(!u24_c.empty && u24_c[1] == 1024);
+ u24.length = 3;
+ copy(iota(0, 3), u24[]);
+ assert(equal(u24[], iota(0, 3)));
+ assert(u24_c[1] == 1024);
+ }
+
+ void func2(T)(T u24)
+ {
+ T u24_2 = u24;
+ T u24_3;
+ u24_3 = u24_2;
+ assert(u24_2 == u24_3);
+ assert(equal(u24[], u24_2[]));
+ assert(equal(u24_2[], u24_3[]));
+ funcRef(u24_3);
+
+ assert(equal(u24_3[], iota(0, 3)));
+ assert(!equal(u24_2[], u24_3[]));
+ assert(equal(u24_2[], u24[]));
+ u24_2 = u24_3;
+ assert(equal(u24_2[], iota(0, 3)));
+ // to test that passed arg is intact outside
+ // plus try out opEquals
+ u24 = u24_3;
+ u24 = T.init;
+ u24_3 = T.init;
+ assert(u24.empty);
+ assert(u24 == u24_3);
+ assert(u24 != u24_2);
+ }
+
+ static foreach (Policy; AliasSeq!(GcPolicy, ReallocPolicy))
+ {{
+ alias Range = typeof(CowArray!Policy.init[]);
+ alias U24A = CowArray!Policy;
+ static assert(isForwardRange!Range);
+ static assert(isBidirectionalRange!Range);
+ static assert(isOutputRange!(Range, uint));
+ static assert(isRandomAccessRange!(Range));
+
+ auto arr = U24A([42u, 36, 100]);
+ assert(arr[0] == 42);
+ assert(arr[1] == 36);
+ arr[0] = 72;
+ arr[1] = 0xFE_FEFE;
+ assert(arr[0] == 72);
+ assert(arr[1] == 0xFE_FEFE);
+ assert(arr[2] == 100);
+ U24A arr2 = arr;
+ assert(arr2[0] == 72);
+ arr2[0] = 11;
+ // test COW-ness
+ assert(arr[0] == 72);
+ assert(arr2[0] == 11);
+ // set this to about 100M to stress-test COW memory management
+ foreach (v; 0 .. 10_000)
+ func2(arr);
+ assert(equal(arr[], [72, 0xFE_FEFE, 100]));
+
+ auto r2 = U24A(iota(0, 100));
+ assert(equal(r2[], iota(0, 100)), text(r2[]));
+ copy(iota(10, 170, 2), r2[10 .. 90]);
+ assert(equal(r2[], chain(iota(0, 10), iota(10, 170, 2), iota(90, 100)))
+ , text(r2[]));
+ }}
+}
+
+pure @safe unittest// core set primitives test
+{
+ import std.conv : text;
+ alias AllSets = AliasSeq!(InversionList!GcPolicy, InversionList!ReallocPolicy);
+ foreach (CodeList; AllSets)
+ {
+ CodeList a;
+ //"plug a hole" test
+ a.add(10, 20).add(25, 30).add(15, 27);
+ assert(a == CodeList(10, 30), text(a));
+
+ auto x = CodeList.init;
+ x.add(10, 20).add(30, 40).add(50, 60);
+
+ a = x;
+ a.add(20, 49);//[10, 49) [50, 60)
+ assert(a == CodeList(10, 49, 50 ,60));
+
+ a = x;
+ a.add(20, 50);
+ assert(a == CodeList(10, 60), text(a));
+
+ // simple unions, mostly edge effects
+ x = CodeList.init;
+ x.add(10, 20).add(40, 60);
+
+ a = x;
+ a.add(10, 25); //[10, 25) [40, 60)
+ assert(a == CodeList(10, 25, 40, 60));
+
+ a = x;
+ a.add(5, 15); //[5, 20) [40, 60)
+ assert(a == CodeList(5, 20, 40, 60));
+
+ a = x;
+ a.add(0, 10); // [0, 20) [40, 60)
+ assert(a == CodeList(0, 20, 40, 60));
+
+ a = x;
+ a.add(0, 5); // prepand
+ assert(a == CodeList(0, 5, 10, 20, 40, 60), text(a));
+
+ a = x;
+ a.add(5, 20);
+ assert(a == CodeList(5, 20, 40, 60));
+
+ a = x;
+ a.add(3, 37);
+ assert(a == CodeList(3, 37, 40, 60));
+
+ a = x;
+ a.add(37, 65);
+ assert(a == CodeList(10, 20, 37, 65));
+
+ // some tests on helpers for set intersection
+ x = CodeList.init.add(10, 20).add(40, 60).add(100, 120);
+ a = x;
+
+ auto m = a.skipUpTo(60);
+ a.dropUpTo(110, m);
+ assert(a == CodeList(10, 20, 40, 60, 110, 120), text(a.data[]));
+
+ a = x;
+ a.dropUpTo(100);
+ assert(a == CodeList(100, 120), text(a.data[]));
+
+ a = x;
+ m = a.skipUpTo(50);
+ a.dropUpTo(140, m);
+ assert(a == CodeList(10, 20, 40, 50), text(a.data[]));
+ a = x;
+ a.dropUpTo(60);
+ assert(a == CodeList(100, 120), text(a.data[]));
+ }
+}
+
+
+//test constructor to work with any order of intervals
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : text, to;
+ import std.range : chain, iota;
+ import std.typecons : tuple;
+ //ensure constructor handles bad ordering and overlap
+ auto c1 = CodepointSet('а', 'Ñ'+1, 'Ð','Я'+1);
+ foreach (ch; chain(iota('а', 'Ñ'+1), iota('Ð','Я'+1)))
+ assert(ch in c1, to!string(ch));
+
+ //contiguos
+ assert(CodepointSet(1000, 1006, 1006, 1009)
+ .byInterval.equal([tuple(1000, 1009)]));
+ //contains
+ assert(CodepointSet(900, 1200, 1000, 1100)
+ .byInterval.equal([tuple(900, 1200)]));
+ //intersect left
+ assert(CodepointSet(900, 1100, 1000, 1200)
+ .byInterval.equal([tuple(900, 1200)]));
+ //intersect right
+ assert(CodepointSet(1000, 1200, 900, 1100)
+ .byInterval.equal([tuple(900, 1200)]));
+
+ //ditto with extra items at end
+ assert(CodepointSet(1000, 1200, 900, 1100, 800, 850)
+ .byInterval.equal([tuple(800, 850), tuple(900, 1200)]));
+ assert(CodepointSet(900, 1100, 1000, 1200, 800, 850)
+ .byInterval.equal([tuple(800, 850), tuple(900, 1200)]));
+
+ //"plug a hole" test
+ auto c2 = CodepointSet(20, 40,
+ 60, 80, 100, 140, 150, 200,
+ 40, 60, 80, 100, 140, 150
+ );
+ assert(c2.byInterval.equal([tuple(20, 200)]));
+
+ auto c3 = CodepointSet(
+ 20, 40, 60, 80, 100, 140, 150, 200,
+ 0, 10, 15, 100, 10, 20, 200, 220);
+ assert(c3.byInterval.equal([tuple(0, 140), tuple(150, 220)]));
+}
+
+
+pure @safe unittest
+{ // full set operations
+ import std.conv : text;
+ alias AllSets = AliasSeq!(InversionList!GcPolicy, InversionList!ReallocPolicy);
+ foreach (CodeList; AllSets)
+ {
+ CodeList a, b, c, d;
+
+ //"plug a hole"
+ a.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b.add(40, 60).add(80, 100).add(140, 150);
+ c = a | b;
+ d = b | a;
+ assert(c == CodeList(20, 200), text(CodeList.stringof," ", c));
+ assert(c == d, text(c," vs ", d));
+
+ b = CodeList.init.add(25, 45).add(65, 85).add(95,110).add(150, 210);
+ c = a | b; //[20,45) [60, 85) [95, 140) [150, 210)
+ d = b | a;
+ assert(c == CodeList(20, 45, 60, 85, 95, 140, 150, 210), text(c));
+ assert(c == d, text(c," vs ", d));
+
+ b = CodeList.init.add(10, 20).add(30,100).add(145,200);
+ c = a | b;//[10, 140) [145, 200)
+ d = b | a;
+ assert(c == CodeList(10, 140, 145, 200));
+ assert(c == d, text(c," vs ", d));
+
+ b = CodeList.init.add(0, 10).add(15, 100).add(10, 20).add(200, 220);
+ c = a | b;//[0, 140) [150, 220)
+ d = b | a;
+ assert(c == CodeList(0, 140, 150, 220));
+ assert(c == d, text(c," vs ", d));
+
+
+ a = CodeList.init.add(20, 40).add(60, 80);
+ b = CodeList.init.add(25, 35).add(65, 75);
+ c = a & b;
+ d = b & a;
+ assert(c == CodeList(25, 35, 65, 75), text(c));
+ assert(c == d, text(c," vs ", d));
+
+ a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(25, 35).add(65, 75).add(110, 130).add(160, 180);
+ c = a & b;
+ d = b & a;
+ assert(c == CodeList(25, 35, 65, 75, 110, 130, 160, 180), text(c));
+ assert(c == d, text(c," vs ", d));
+
+ a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(10, 30).add(60, 120).add(135, 160);
+ c = a & b;//[20, 30)[60, 80) [100, 120) [135, 140) [150, 160)
+ d = b & a;
+
+ assert(c == CodeList(20, 30, 60, 80, 100, 120, 135, 140, 150, 160),text(c));
+ assert(c == d, text(c, " vs ",d));
+ assert((c & a) == c);
+ assert((d & b) == d);
+ assert((c & d) == d);
+
+ b = CodeList.init.add(40, 60).add(80, 100).add(140, 200);
+ c = a & b;
+ d = b & a;
+ assert(c == CodeList(150, 200), text(c));
+ assert(c == d, text(c, " vs ",d));
+ assert((c & a) == c);
+ assert((d & b) == d);
+ assert((c & d) == d);
+
+ assert((a & a) == a);
+ assert((b & b) == b);
+
+ a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(30, 60).add(75, 120).add(190, 300);
+ c = a - b;// [30, 40) [60, 75) [120, 140) [150, 190)
+ d = b - a;// [40, 60) [80, 100) [200, 300)
+ assert(c == CodeList(20, 30, 60, 75, 120, 140, 150, 190), text(c));
+ assert(d == CodeList(40, 60, 80, 100, 200, 300), text(d));
+ assert(c - d == c, text(c-d, " vs ", c));
+ assert(d - c == d, text(d-c, " vs ", d));
+ assert(c - c == CodeList.init);
+ assert(d - d == CodeList.init);
+
+ a = CodeList.init.add(20, 40).add( 60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(10, 50).add(60, 160).add(190, 300);
+ c = a - b;// [160, 190)
+ d = b - a;// [10, 20) [40, 50) [80, 100) [140, 150) [200, 300)
+ assert(c == CodeList(160, 190), text(c));
+ assert(d == CodeList(10, 20, 40, 50, 80, 100, 140, 150, 200, 300), text(d));
+ assert(c - d == c, text(c-d, " vs ", c));
+ assert(d - c == d, text(d-c, " vs ", d));
+ assert(c - c == CodeList.init);
+ assert(d - d == CodeList.init);
+
+ a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(10, 30).add(45, 100).add(130, 190);
+ c = a ~ b; // [10, 20) [30, 40) [45, 60) [80, 130) [140, 150) [190, 200)
+ d = b ~ a;
+ assert(c == CodeList(10, 20, 30, 40, 45, 60, 80, 130, 140, 150, 190, 200),
+ text(c));
+ assert(c == d, text(c, " vs ", d));
+ }
+}
+
+}
+
+pure @safe unittest// vs single dchar
+{
+ import std.conv : text;
+ CodepointSet a = CodepointSet(10, 100, 120, 200);
+ assert(a - 'A' == CodepointSet(10, 65, 66, 100, 120, 200), text(a - 'A'));
+ assert((a & 'B') == CodepointSet(66, 67));
+}
+
+pure @safe unittest// iteration & opIndex
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : text;
+ import std.typecons : tuple, Tuple;
+
+ static foreach (CodeList; AliasSeq!(InversionList!(ReallocPolicy)))
+ {{
+ auto arr = "ABCDEFGHIJKLMabcdefghijklm"d;
+ auto a = CodeList('A','N','a', 'n');
+ assert(equal(a.byInterval,
+ [tuple(cast(uint)'A', cast(uint)'N'), tuple(cast(uint)'a', cast(uint)'n')]
+ ), text(a.byInterval));
+
+ // same @@@BUG as in https://issues.dlang.org/show_bug.cgi?id=8949 ?
+ version (bug8949)
+ {
+ import std.range : retro;
+ assert(equal(retro(a.byInterval),
+ [tuple(cast(uint)'a', cast(uint)'n'), tuple(cast(uint)'A', cast(uint)'N')]
+ ), text(retro(a.byInterval)));
+ }
+ auto achr = a.byCodepoint;
+ assert(equal(achr, arr), text(a.byCodepoint));
+ foreach (ch; a.byCodepoint)
+ assert(a[ch]);
+ auto x = CodeList(100, 500, 600, 900, 1200, 1500);
+ assert(equal(x.byInterval, [ tuple(100, 500), tuple(600, 900), tuple(1200, 1500)]), text(x.byInterval));
+ foreach (ch; x.byCodepoint)
+ assert(x[ch]);
+ static if (is(CodeList == CodepointSet))
+ {
+ auto y = CodeList(x.byInterval);
+ assert(equal(x.byInterval, y.byInterval));
+ }
+ assert(equal(CodepointSet.init.byInterval, cast(Tuple!(uint, uint)[])[]));
+ assert(equal(CodepointSet.init.byCodepoint, cast(dchar[])[]));
+ }}
+}
+
+//============================================================================
+// Generic Trie template and various ways to build it
+//============================================================================
+
+// debug helper to get a shortened array dump
+auto arrayRepr(T)(T x)
+{
+ import std.conv : text;
+ if (x.length > 32)
+ {
+ return text(x[0 .. 16],"~...~", x[x.length-16 .. x.length]);
+ }
+ else
+ return text(x);
+}
+
+/**
+ Maps `Key` to a suitable integer index within the range of `size_t`.
+ The mapping is constructed by applying predicates from `Prefix` left to right
+ and concatenating the resulting bits.
+
+ The first (leftmost) predicate defines the most significant bits of
+ the resulting index.
+ */
+template mapTrieIndex(Prefix...)
+{
+ size_t mapTrieIndex(Key)(Key key)
+ if (isValidPrefixForTrie!(Key, Prefix))
+ {
+ alias p = Prefix;
+ size_t idx;
+ foreach (i, v; p[0..$-1])
+ {
+ idx |= p[i](key);
+ idx <<= p[i+1].bitSize;
+ }
+ idx |= p[$-1](key);
+ return idx;
+ }
+}
+
+/*
+ `TrieBuilder` is a type used for incremental construction
+ of $(LREF Trie)s.
+
+ See $(LREF buildTrie) for generic helpers built on top of it.
+*/
+@trusted private struct TrieBuilder(Value, Key, Args...)
+if (isBitPackableType!Value && isValidArgsForTrie!(Key, Args))
+{
+ import std.exception : enforce;
+
+private:
+ // last index is not stored in table, it is used as an offset to values in a block.
+ static if (is(Value == bool))// always pack bool
+ alias V = BitPacked!(Value, 1);
+ else
+ alias V = Value;
+ static auto deduceMaxIndex(Preds...)()
+ {
+ size_t idx = 1;
+ foreach (v; Preds)
+ idx *= 2^^v.bitSize;
+ return idx;
+ }
+
+ static if (is(typeof(Args[0]) : Key)) // Args start with upper bound on Key
+ {
+ alias Prefix = Args[1..$];
+ enum lastPageSize = 2^^Prefix[$-1].bitSize;
+ enum translatedMaxIndex = mapTrieIndex!(Prefix)(Args[0]);
+ enum roughedMaxIndex =
+ (translatedMaxIndex + lastPageSize-1)/lastPageSize*lastPageSize;
+ // check warp around - if wrapped, use the default deduction rule
+ enum maxIndex = roughedMaxIndex < translatedMaxIndex ?
+ deduceMaxIndex!(Prefix)() : roughedMaxIndex;
+ }
+ else
+ {
+ alias Prefix = Args;
+ enum maxIndex = deduceMaxIndex!(Prefix)();
+ }
+
+ alias getIndex = mapTrieIndex!(Prefix);
+
+ enum lastLevel = Prefix.length-1;
+ struct ConstructState
+ {
+ size_t idx_zeros, idx_ones;
+ }
+ // iteration over levels of Trie, each indexes its own level and thus a shortened domain
+ size_t[Prefix.length] indices;
+ // default filler value to use
+ Value defValue;
+ // this is a full-width index of next item
+ size_t curIndex;
+ // all-zeros page index, all-ones page index (+ indicator if there is such a page)
+ ConstructState[Prefix.length] state;
+ // the table being constructed
+ MultiArray!(idxTypes!(Key, fullBitSize!(Prefix), Prefix[0..$]), V) table;
+
+ @disable this();
+
+ //shortcut for index variable at level 'level'
+ @property ref idx(size_t level)(){ return indices[level]; }
+
+ // this function assumes no holes in the input so
+ // indices are going one by one
+ void addValue(size_t level, T)(T val, size_t numVals)
+ {
+ alias j = idx!level;
+ enum pageSize = 1 << Prefix[level].bitSize;
+ if (numVals == 0)
+ return;
+ auto ptr = table.slice!(level);
+ if (numVals == 1)
+ {
+ static if (level == Prefix.length-1)
+ ptr[j] = val;
+ else
+ {// can incur narrowing conversion
+ assert(j < ptr.length);
+ ptr[j] = force!(typeof(ptr[j]))(val);
+ }
+ j++;
+ if (j % pageSize == 0)
+ spillToNextPage!level(ptr);
+ return;
+ }
+ // longer row of values
+ // get to the next page boundary
+ immutable nextPB = (j + pageSize) & ~(pageSize-1);
+ immutable n = nextPB - j;// can fill right in this page
+ if (numVals < n) //fits in current page
+ {
+ ptr[j .. j+numVals] = val;
+ j += numVals;
+ return;
+ }
+ static if (level != 0)//on the first level it always fits
+ {
+ numVals -= n;
+ //write till the end of current page
+ ptr[j .. j+n] = val;
+ j += n;
+ //spill to the next page
+ spillToNextPage!level(ptr);
+ // page at once loop
+ if (state[level].idx_zeros != size_t.max && val == T.init)
+ {
+ alias NextIdx = typeof(table.slice!(level-1)[0]);
+ addValue!(level-1)(force!NextIdx(state[level].idx_zeros),
+ numVals/pageSize);
+ ptr = table.slice!level; //table structure might have changed
+ numVals %= pageSize;
+ }
+ else
+ {
+ while (numVals >= pageSize)
+ {
+ numVals -= pageSize;
+ ptr[j .. j+pageSize] = val;
+ j += pageSize;
+ spillToNextPage!level(ptr);
+ }
+ }
+ if (numVals)
+ {
+ // the leftovers, an incomplete page
+ ptr[j .. j+numVals] = val;
+ j += numVals;
+ }
+ }
+ }
+
+ void spillToNextPage(size_t level, Slice)(ref Slice ptr)
+ {
+ // last level (i.e. topmost) has 1 "page"
+ // thus it need not to add a new page on upper level
+ static if (level != 0)
+ spillToNextPageImpl!(level)(ptr);
+ }
+
+ // this can re-use the current page if duplicate or allocate a new one
+ // it also makes sure that previous levels point to the correct page in this level
+ void spillToNextPageImpl(size_t level, Slice)(ref Slice ptr)
+ {
+ alias NextIdx = typeof(table.slice!(level-1)[0]);
+ NextIdx next_lvl_index;
+ enum pageSize = 1 << Prefix[level].bitSize;
+ assert(idx!level % pageSize == 0);
+ immutable last = idx!level-pageSize;
+ const slice = ptr[idx!level - pageSize .. idx!level];
+ size_t j;
+ for (j=0; j<last; j+=pageSize)
+ {
+ if (ptr[j .. j+pageSize] == slice)
+ {
+ // get index to it, reuse ptr space for the next block
+ next_lvl_index = force!NextIdx(j/pageSize);
+ version (none)
+ {
+ import std.stdio : writefln, writeln;
+ writefln("LEVEL(%s) page mapped idx: %s: 0..%s ---> [%s..%s]"
+ ,level
+ ,indices[level-1], pageSize, j, j+pageSize);
+ writeln("LEVEL(", level
+ , ") mapped page is: ", slice, ": ", arrayRepr(ptr[j .. j+pageSize]));
+ writeln("LEVEL(", level
+ , ") src page is :", ptr, ": ", arrayRepr(slice[0 .. pageSize]));
+ }
+ idx!level -= pageSize; // reuse this page, it is duplicate
+ break;
+ }
+ }
+ if (j == last)
+ {
+ L_allocate_page:
+ next_lvl_index = force!NextIdx(idx!level/pageSize - 1);
+ if (state[level].idx_zeros == size_t.max && ptr.zeros(j, j+pageSize))
+ {
+ state[level].idx_zeros = next_lvl_index;
+ }
+ // allocate next page
+ version (none)
+ {
+ import std.stdio : writefln;
+ writefln("LEVEL(%s) page allocated: %s"
+ , level, arrayRepr(slice[0 .. pageSize]));
+ writefln("LEVEL(%s) index: %s ; page at this index %s"
+ , level
+ , next_lvl_index
+ , arrayRepr(
+ table.slice!(level)
+ [pageSize*next_lvl_index..(next_lvl_index+1)*pageSize]
+ ));
+ }
+ table.length!level = table.length!level + pageSize;
+ }
+ L_know_index:
+ // for the previous level, values are indices to the pages in the current level
+ addValue!(level-1)(next_lvl_index, 1);
+ ptr = table.slice!level; //re-load the slice after moves
+ }
+
+ // idx - full-width index to fill with v (full-width index != key)
+ // fills everything in the range of [curIndex, idx) with filler
+ void putAt(size_t idx, Value v)
+ {
+ assert(idx >= curIndex);
+ immutable numFillers = idx - curIndex;
+ addValue!lastLevel(defValue, numFillers);
+ addValue!lastLevel(v, 1);
+ curIndex = idx + 1;
+ }
+
+ // ditto, but sets the range of [idxA, idxB) to v
+ void putRangeAt(size_t idxA, size_t idxB, Value v)
+ {
+ assert(idxA >= curIndex);
+ assert(idxB >= idxA);
+ size_t numFillers = idxA - curIndex;
+ addValue!lastLevel(defValue, numFillers);
+ addValue!lastLevel(v, idxB - idxA);
+ curIndex = idxB; // open-right
+ }
+
+ enum errMsg = "non-monotonic prefix function(s), an unsorted range or "~
+ "duplicate key->value mapping";
+
+public:
+ /**
+ Construct a builder, where `filler` is a value
+ to indicate empty slots (or "not found" condition).
+ */
+ this(Value filler)
+ {
+ curIndex = 0;
+ defValue = filler;
+ // zeros-page index, ones-page index
+ foreach (ref v; state)
+ v = ConstructState(size_t.max, size_t.max);
+ table = typeof(table)(indices);
+ // one page per level is a bootstrap minimum
+ foreach (i, Pred; Prefix)
+ table.length!i = (1 << Pred.bitSize);
+ }
+
+ /**
+ Put a value `v` into interval as
+ mapped by keys from `a` to `b`.
+ All slots prior to `a` are filled with
+ the default filler.
+ */
+ void putRange(Key a, Key b, Value v)
+ {
+ auto idxA = getIndex(a), idxB = getIndex(b);
+ // indexes of key should always grow
+ enforce(idxB >= idxA && idxA >= curIndex, errMsg);
+ putRangeAt(idxA, idxB, v);
+ }
+
+ /**
+ Put a value `v` into slot mapped by `key`.
+ All slots prior to `key` are filled with the
+ default filler.
+ */
+ void putValue(Key key, Value v)
+ {
+ auto idx = getIndex(key);
+ enforce(idx >= curIndex, errMsg);
+ putAt(idx, v);
+ }
+
+ /// Finishes construction of Trie, yielding an immutable Trie instance.
+ auto build()
+ {
+ static if (maxIndex != 0) // doesn't cover full range of size_t
+ {
+ assert(curIndex <= maxIndex);
+ addValue!lastLevel(defValue, maxIndex - curIndex);
+ }
+ else
+ {
+ if (curIndex != 0 // couldn't wrap around
+ || (Prefix.length != 1 && indices[lastLevel] == 0)) // can be just empty
+ {
+ addValue!lastLevel(defValue, size_t.max - curIndex);
+ addValue!lastLevel(defValue, 1);
+ }
+ // else curIndex already completed the full range of size_t by wrapping around
+ }
+ return Trie!(V, Key, maxIndex, Prefix)(table);
+ }
+}
+
+/**
+ $(P A generic Trie data-structure for a fixed number of stages.
+ The design goal is optimal speed with smallest footprint size.
+ )
+ $(P It's intentionally read-only and doesn't provide constructors.
+ To construct one use a special builder,
+ see $(LREF TrieBuilder) and $(LREF buildTrie).
+ )
+
+*/
+@trusted private struct Trie(Value, Key, Args...)
+if (isValidPrefixForTrie!(Key, Args)
+ || (isValidPrefixForTrie!(Key, Args[1..$])
+ && is(typeof(Args[0]) : size_t)))
+{
+ import std.range.primitives : isOutputRange;
+ static if (is(typeof(Args[0]) : size_t))
+ {
+ private enum maxIndex = Args[0];
+ private enum hasBoundsCheck = true;
+ private alias Prefix = Args[1..$];
+ }
+ else
+ {
+ private enum hasBoundsCheck = false;
+ private alias Prefix = Args;
+ }
+
+ private this()(typeof(_table) table)
+ {
+ _table = table;
+ }
+
+ // only for constant Tries constructed from precompiled tables
+ private this()(const(size_t)[] offsets, const(size_t)[] sizes,
+ const(size_t)[] data) const
+ {
+ _table = typeof(_table)(offsets, sizes, data);
+ }
+
+ /**
+ $(P Lookup the `key` in this `Trie`. )
+
+ $(P The lookup always succeeds if key fits the domain
+ provided during construction. The whole domain defined
+ is covered so instead of not found condition
+ the sentinel (filler) value could be used. )
+
+ $(P See $(LREF buildTrie), $(LREF TrieBuilder) for how to
+ define a domain of `Trie` keys and the sentinel value. )
+
+ Note:
+ Domain range-checking is only enabled in debug builds
+ and results in assertion failure.
+ */
+ TypeOfBitPacked!Value opIndex()(Key key) const
+ {
+ static if (hasBoundsCheck)
+ assert(mapTrieIndex!Prefix(key) < maxIndex);
+ size_t idx;
+ alias p = Prefix;
+ idx = cast(size_t) p[0](key);
+ foreach (i, v; p[0..$-1])
+ idx = cast(size_t)((_table.ptr!i[idx]<<p[i+1].bitSize) + p[i+1](key));
+ return _table.ptr!(p.length-1)[idx];
+ }
+
+ ///
+ @property size_t bytes(size_t n=size_t.max)() const
+ {
+ return _table.bytes!n;
+ }
+
+ ///
+ @property size_t pages(size_t n)() const
+ {
+ return (bytes!n+2^^(Prefix[n].bitSize-1))
+ /2^^Prefix[n].bitSize;
+ }
+
+ ///
+ void store(OutRange)(scope OutRange sink) const
+ if (isOutputRange!(OutRange, char))
+ {
+ _table.store(sink);
+ }
+
+private:
+ MultiArray!(idxTypes!(Key, fullBitSize!(Prefix), Prefix[0..$]), Value) _table;
+}
+
+// create a tuple of 'sliceBits' that slice the 'top' of bits into pieces of sizes 'sizes'
+// left-to-right, the most significant bits first
+template GetBitSlicing(size_t top, sizes...)
+{
+ static if (sizes.length > 0)
+ alias GetBitSlicing =
+ AliasSeq!(sliceBits!(top - sizes[0], top),
+ GetBitSlicing!(top - sizes[0], sizes[1..$]));
+ else
+ alias GetBitSlicing = AliasSeq!();
+}
+
+template callableWith(T)
+{
+ template callableWith(alias Pred)
+ {
+ static if (!is(typeof(Pred(T.init))))
+ enum callableWith = false;
+ else
+ {
+ alias Result = typeof(Pred(T.init));
+ enum callableWith = isBitPackableType!(TypeOfBitPacked!(Result));
+ }
+ }
+}
+
+/*
+ Check if `Prefix` is a valid set of predicates
+ for `Trie` template having `Key` as the type of keys.
+ This requires all predicates to be callable, take
+ single argument of type `Key` and return unsigned value.
+*/
+template isValidPrefixForTrie(Key, Prefix...)
+{
+ import std.meta : allSatisfy;
+ enum isValidPrefixForTrie = allSatisfy!(callableWith!Key, Prefix); // TODO: tighten the screws
+}
+
+/*
+ Check if `Args` is a set of maximum key value followed by valid predicates
+ for `Trie` template having `Key` as the type of keys.
+*/
+template isValidArgsForTrie(Key, Args...)
+{
+ static if (Args.length > 1)
+ {
+ enum isValidArgsForTrie = isValidPrefixForTrie!(Key, Args)
+ || (isValidPrefixForTrie!(Key, Args[1..$]) && is(typeof(Args[0]) : Key));
+ }
+ else
+ enum isValidArgsForTrie = isValidPrefixForTrie!Args;
+}
+
+@property size_t sumOfIntegerTuple(ints...)()
+{
+ size_t count=0;
+ foreach (v; ints)
+ count += v;
+ return count;
+}
+
+/**
+ A shorthand for creating a custom multi-level fixed Trie
+ from a `CodepointSet`. `sizes` are numbers of bits per level,
+ with the most significant bits used first.
+
+ Note: The sum of `sizes` must be equal 21.
+
+ See_Also: $(LREF toTrie), which is even simpler.
+
+ Example:
+ ---
+ {
+ import std.stdio;
+ auto set = unicode("Number");
+ auto trie = codepointSetTrie!(8, 5, 8)(set);
+ writeln("Input code points to test:");
+ foreach (line; stdin.byLine)
+ {
+ int count=0;
+ foreach (dchar ch; line)
+ if (trie[ch])// is number
+ count++;
+ writefln("Contains %d number code points.", count);
+ }
+ }
+ ---
+*/
+public template codepointSetTrie(sizes...)
+if (sumOfIntegerTuple!sizes == 21)
+{
+ auto codepointSetTrie(Set)(Set set)
+ if (isCodepointSet!Set)
+ {
+ auto builder = TrieBuilder!(bool, dchar, lastDchar+1, GetBitSlicing!(21, sizes))(false);
+ foreach (ival; set.byInterval)
+ builder.putRange(ival[0], ival[1], true);
+ return builder.build();
+ }
+}
+
+/// Type of Trie generated by codepointSetTrie function.
+public template CodepointSetTrie(sizes...)
+if (sumOfIntegerTuple!sizes == 21)
+{
+ alias Prefix = GetBitSlicing!(21, sizes);
+ alias CodepointSetTrie = typeof(TrieBuilder!(bool, dchar, lastDchar+1, Prefix)(false).build());
+}
+
+/**
+ A slightly more general tool for building fixed `Trie`
+ for the Unicode data.
+
+ Specifically unlike `codepointSetTrie` it's allows creating mappings
+ of `dchar` to an arbitrary type `T`.
+
+ Note: Overload taking `CodepointSet`s will naturally convert
+ only to bool mapping `Trie`s.
+
+ CodepointTrie is the type of Trie as generated by codepointTrie function.
+*/
+public template codepointTrie(T, sizes...)
+if (sumOfIntegerTuple!sizes == 21)
+{
+ alias Prefix = GetBitSlicing!(21, sizes);
+
+ static if (is(TypeOfBitPacked!T == bool))
+ {
+ auto codepointTrie(Set)(const scope Set set)
+ if (isCodepointSet!Set)
+ {
+ return codepointSetTrie(set);
+ }
+ }
+
+ ///
+ auto codepointTrie()(T[dchar] map, T defValue=T.init)
+ {
+ return buildTrie!(T, dchar, Prefix)(map, defValue);
+ }
+
+ // unsorted range of pairs
+ ///
+ auto codepointTrie(R)(R range, T defValue=T.init)
+ if (isInputRange!R
+ && is(typeof(ElementType!R.init[0]) : T)
+ && is(typeof(ElementType!R.init[1]) : dchar))
+ {
+ // build from unsorted array of pairs
+ // TODO: expose index sorting functions for Trie
+ return buildTrie!(T, dchar, Prefix)(range, defValue, true);
+ }
+}
+
+@system pure unittest
+{
+ import std.algorithm.comparison : max;
+ import std.algorithm.searching : count;
+
+ // pick characters from the Greek script
+ auto set = unicode.Greek;
+
+ // a user-defined property (or an expensive function)
+ // that we want to look up
+ static uint luckFactor(dchar ch)
+ {
+ // here we consider a character lucky
+ // if its code point has a lot of identical hex-digits
+ // e.g. arabic letter DDAL (\u0688) has a "luck factor" of 2
+ ubyte[6] nibbles; // 6 4-bit chunks of code point
+ uint value = ch;
+ foreach (i; 0 .. 6)
+ {
+ nibbles[i] = value & 0xF;
+ value >>= 4;
+ }
+ uint luck;
+ foreach (n; nibbles)
+ luck = cast(uint) max(luck, count(nibbles[], n));
+ return luck;
+ }
+
+ // only unsigned built-ins are supported at the moment
+ alias LuckFactor = BitPacked!(uint, 3);
+
+ // create a temporary associative array (AA)
+ LuckFactor[dchar] map;
+ foreach (ch; set.byCodepoint)
+ map[ch] = LuckFactor(luckFactor(ch));
+
+ // bits per stage are chosen randomly, fell free to optimize
+ auto trie = codepointTrie!(LuckFactor, 8, 5, 8)(map);
+
+ // from now on the AA is not needed
+ foreach (ch; set.byCodepoint)
+ assert(trie[ch] == luckFactor(ch)); // verify
+ // CJK is not Greek, thus it has the default value
+ assert(trie['\u4444'] == 0);
+ // and here is a couple of quite lucky Greek characters:
+ // Greek small letter epsilon with dasia
+ assert(trie['\u1F11'] == 3);
+ // Ancient Greek metretes sign
+ assert(trie['\U00010181'] == 3);
+
+}
+
+/// ditto
+public template CodepointTrie(T, sizes...)
+if (sumOfIntegerTuple!sizes == 21)
+{
+ alias Prefix = GetBitSlicing!(21, sizes);
+ alias CodepointTrie = typeof(TrieBuilder!(T, dchar, lastDchar+1, Prefix)(T.init).build());
+}
+
+package(std) template cmpK0(alias Pred)
+{
+ import std.typecons : Tuple;
+ static bool cmpK0(Value, Key)
+ (Tuple!(Value, Key) a, Tuple!(Value, Key) b)
+ {
+ return Pred(a[1]) < Pred(b[1]);
+ }
+}
+
+/**
+ The most general utility for construction of `Trie`s
+ short of using `TrieBuilder` directly.
+
+ Provides a number of convenience overloads.
+ `Args` is tuple of maximum key value followed by
+ predicates to construct index from key.
+
+ Alternatively if the first argument is not a value convertible to `Key`
+ then the whole tuple of `Args` is treated as predicates
+ and the maximum Key is deduced from predicates.
+*/
+private template buildTrie(Value, Key, Args...)
+if (isValidArgsForTrie!(Key, Args))
+{
+ static if (is(typeof(Args[0]) : Key)) // prefix starts with upper bound on Key
+ {
+ alias Prefix = Args[1..$];
+ }
+ else
+ alias Prefix = Args;
+
+ alias getIndex = mapTrieIndex!(Prefix);
+
+ // for multi-sort
+ template GetComparators(size_t n)
+ {
+ static if (n > 0)
+ alias GetComparators =
+ AliasSeq!(GetComparators!(n-1), cmpK0!(Prefix[n-1]));
+ else
+ alias GetComparators = AliasSeq!();
+ }
+
+ /*
+ Build `Trie` from a range of a Key-Value pairs,
+ assuming it is sorted by Key as defined by the following lambda:
+ ------
+ (a, b) => mapTrieIndex!(Prefix)(a) < mapTrieIndex!(Prefix)(b)
+ ------
+ Exception is thrown if it's detected that the above order doesn't hold.
+
+ In other words $(LREF mapTrieIndex) should be a
+ monotonically increasing function that maps `Key` to an integer.
+
+ See_Also: $(REF sort, std,_algorithm),
+ $(REF SortedRange, std,range),
+ $(REF setUnion, std,_algorithm).
+ */
+ auto buildTrie(Range)(Range range, Value filler=Value.init)
+ if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value)
+ && is(typeof(Range.init.front[1]) : Key))
+ {
+ auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
+ foreach (v; range)
+ builder.putValue(v[1], v[0]);
+ return builder.build();
+ }
+
+ /*
+ If `Value` is bool (or BitPacked!(bool, x)) then it's possible
+ to build `Trie` from a range of open-right intervals of `Key`s.
+ The requirement on the ordering of keys (and the behavior on the
+ violation of it) is the same as for Key-Value range overload.
+
+ Intervals denote ranges of !`filler` i.e. the opposite of filler.
+ If no filler provided keys inside of the intervals map to true,
+ and `filler` is false.
+ */
+ auto buildTrie(Range)(Range range, Value filler=Value.init)
+ if (is(TypeOfBitPacked!Value == bool)
+ && isInputRange!Range && is(typeof(Range.init.front[0]) : Key)
+ && is(typeof(Range.init.front[1]) : Key))
+ {
+ auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
+ foreach (ival; range)
+ builder.putRange(ival[0], ival[1], !filler);
+ return builder.build();
+ }
+
+ auto buildTrie(Range)(Range range, Value filler, bool unsorted)
+ if (isInputRange!Range
+ && is(typeof(Range.init.front[0]) : Value)
+ && is(typeof(Range.init.front[1]) : Key))
+ {
+ import std.algorithm.sorting : multiSort;
+ alias Comps = GetComparators!(Prefix.length);
+ if (unsorted)
+ multiSort!(Comps)(range);
+ return buildTrie(range, filler);
+ }
+
+ /*
+ If `Value` is bool (or BitPacked!(bool, x)) then it's possible
+ to build `Trie` simply from an input range of `Key`s.
+ The requirement on the ordering of keys (and the behavior on the
+ violation of it) is the same as for Key-Value range overload.
+
+ Keys found in range denote !`filler` i.e. the opposite of filler.
+ If no filler provided keys map to true, and `filler` is false.
+ */
+ auto buildTrie(Range)(Range range, Value filler=Value.init)
+ if (is(TypeOfBitPacked!Value == bool)
+ && isInputRange!Range && is(typeof(Range.init.front) : Key))
+ {
+ auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
+ foreach (v; range)
+ builder.putValue(v, !filler);
+ return builder.build();
+ }
+
+ /*
+ If `Key` is unsigned integer `Trie` could be constructed from array
+ of values where array index serves as key.
+ */
+ auto buildTrie()(Value[] array, Value filler=Value.init)
+ if (isUnsigned!Key)
+ {
+ auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
+ foreach (idx, v; array)
+ builder.putValue(idx, v);
+ return builder.build();
+ }
+
+ /*
+ Builds `Trie` from associative array.
+ */
+ auto buildTrie(Key, Value)(Value[Key] map, Value filler=Value.init)
+ {
+ import std.array : array;
+ import std.range : zip;
+ auto range = array(zip(map.values, map.keys));
+ return buildTrie(range, filler, true); // sort it
+ }
+}
+
+// helper in place of assumeSize to
+//reduce mangled name & help DMD inline Trie functors
+struct clamp(size_t bits)
+{
+ static size_t opCall(T)(T arg){ return arg; }
+ enum bitSize = bits;
+}
+
+struct clampIdx(size_t idx, size_t bits)
+{
+ static size_t opCall(T)(T arg){ return arg[idx]; }
+ enum bitSize = bits;
+}
+
+/**
+ Conceptual type that outlines the common properties of all UTF Matchers.
+
+ Note: For illustration purposes only, every method
+ call results in assertion failure.
+ Use $(LREF utfMatcher) to obtain a concrete matcher
+ for UTF-8 or UTF-16 encodings.
+*/
+public struct MatcherConcept
+{
+ /**
+ $(P Perform a semantic equivalent 2 operations:
+ decoding a $(CODEPOINT) at front of `inp` and testing if
+ it belongs to the set of $(CODEPOINTS) of this matcher. )
+
+ $(P The effect on `inp` depends on the kind of function called:)
+
+ $(P Match. If the codepoint is found in the set then range `inp`
+ is advanced by its size in $(S_LINK Code unit, code units),
+ otherwise the range is not modifed.)
+
+ $(P Skip. The range is always advanced by the size
+ of the tested $(CODEPOINT) regardless of the result of test.)
+
+ $(P Test. The range is left unaffected regardless
+ of the result of test.)
+ */
+ public bool match(Range)(ref Range inp)
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ {
+ assert(false);
+ }
+
+ ///ditto
+ public bool skip(Range)(ref Range inp)
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ {
+ assert(false);
+ }
+
+ ///ditto
+ public bool test(Range)(ref Range inp)
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ {
+ assert(false);
+ }
+ ///
+ pure @safe unittest
+ {
+ string truth = "2² = 4";
+ auto m = utfMatcher!char(unicode.Number);
+ assert(m.match(truth)); // '2' is a number all right
+ assert(truth == "² = 4"); // skips on match
+ assert(m.match(truth)); // so is the superscript '2'
+ assert(!m.match(truth)); // space is not a number
+ assert(truth == " = 4"); // unaffected on no match
+ assert(!m.skip(truth)); // same test ...
+ assert(truth == "= 4"); // but skips a codepoint regardless
+ assert(!m.test(truth)); // '=' is not a number
+ assert(truth == "= 4"); // test never affects argument
+ }
+
+ /**
+ Advanced feature - provide direct access to a subset of matcher based a
+ set of known encoding lengths. Lengths are provided in
+ $(S_LINK Code unit, code units). The sub-matcher then may do less
+ operations per any `test`/`match`.
+
+ Use with care as the sub-matcher won't match
+ any $(CODEPOINTS) that have encoded length that doesn't belong
+ to the selected set of lengths. Also the sub-matcher object references
+ the parent matcher and must not be used past the liftetime
+ of the latter.
+
+ Another caveat of using sub-matcher is that skip is not available
+ preciesly because sub-matcher doesn't detect all lengths.
+ */
+ @property auto subMatcher(Lengths...)()
+ {
+ assert(0);
+ return this;
+ }
+
+ pure @safe unittest
+ {
+ auto m = utfMatcher!char(unicode.Number);
+ string square = "2²";
+ // about sub-matchers
+ assert(!m.subMatcher!(2,3,4).test(square)); // ASCII no covered
+ assert(m.subMatcher!1.match(square)); // ASCII-only, works
+ assert(!m.subMatcher!1.test(square)); // unicode '²'
+ assert(m.subMatcher!(2,3,4).match(square)); //
+ assert(square == "");
+ wstring wsquare = "2²";
+ auto m16 = utfMatcher!wchar(unicode.Number);
+ // may keep ref, but the orignal (m16) must be kept alive
+ auto bmp = m16.subMatcher!1;
+ assert(bmp.match(wsquare)); // Okay, in basic multilingual plan
+ assert(bmp.match(wsquare)); // And '²' too
+ }
+}
+
+/**
+ Test if `M` is an UTF Matcher for ranges of `Char`.
+*/
+public enum isUtfMatcher(M, C) = __traits(compiles, (){
+ C[] s;
+ auto d = s.decoder;
+ M m;
+ assert(is(typeof(m.match(d)) == bool));
+ assert(is(typeof(m.test(d)) == bool));
+ static if (is(typeof(m.skip(d))))
+ {
+ assert(is(typeof(m.skip(d)) == bool));
+ assert(is(typeof(m.skip(s)) == bool));
+ }
+ assert(is(typeof(m.match(s)) == bool));
+ assert(is(typeof(m.test(s)) == bool));
+});
+
+pure @safe unittest
+{
+ alias CharMatcher = typeof(utfMatcher!char(CodepointSet.init));
+ alias WcharMatcher = typeof(utfMatcher!wchar(CodepointSet.init));
+ static assert(isUtfMatcher!(CharMatcher, char));
+ static assert(isUtfMatcher!(CharMatcher, immutable(char)));
+ static assert(isUtfMatcher!(WcharMatcher, wchar));
+ static assert(isUtfMatcher!(WcharMatcher, immutable(wchar)));
+}
+
+enum Mode {
+ alwaysSkip,
+ neverSkip,
+ skipOnMatch
+}
+
+mixin template ForwardStrings()
+{
+ private bool fwdStr(string fn, C)(ref C[] str) const @trusted
+ {
+ import std.utf : byCodeUnit;
+ alias type = typeof(byCodeUnit(str));
+ return mixin(fn~"(*cast(type*)&str)");
+ }
+}
+
+template Utf8Matcher()
+{
+ enum validSize(int sz) = sz >= 1 && sz <= 4;
+
+ void badEncoding() pure @safe
+ {
+ import std.utf : UTFException;
+ throw new UTFException("Invalid UTF-8 sequence");
+ }
+
+ //for 1-stage ASCII
+ alias AsciiSpec = AliasSeq!(bool, char, clamp!7);
+ //for 2-stage lookup of 2 byte UTF-8 sequences
+ alias Utf8Spec2 = AliasSeq!(bool, char[2],
+ clampIdx!(0, 5), clampIdx!(1, 6));
+ //ditto for 3 byte
+ alias Utf8Spec3 = AliasSeq!(bool, char[3],
+ clampIdx!(0, 4),
+ clampIdx!(1, 6),
+ clampIdx!(2, 6)
+ );
+ //ditto for 4 byte
+ alias Utf8Spec4 = AliasSeq!(bool, char[4],
+ clampIdx!(0, 3), clampIdx!(1, 6),
+ clampIdx!(2, 6), clampIdx!(3, 6)
+ );
+ alias Tables = AliasSeq!(
+ typeof(TrieBuilder!(AsciiSpec)(false).build()),
+ typeof(TrieBuilder!(Utf8Spec2)(false).build()),
+ typeof(TrieBuilder!(Utf8Spec3)(false).build()),
+ typeof(TrieBuilder!(Utf8Spec4)(false).build())
+ );
+ alias Table(int size) = Tables[size-1];
+
+ enum leadMask(size_t size) = (cast(size_t) 1<<(7 - size))-1;
+ enum encMask(size_t size) = ((1 << size)-1)<<(8-size);
+
+ char truncate()(char ch) pure @safe
+ {
+ ch -= 0x80;
+ if (ch < 0x40)
+ {
+ return ch;
+ }
+ else
+ {
+ badEncoding();
+ return cast(char) 0;
+ }
+ }
+
+ static auto encode(size_t sz)(dchar ch)
+ if (sz > 1)
+ {
+ import std.utf : encodeUTF = encode;
+ char[4] buf;
+ encodeUTF(buf, ch);
+ char[sz] ret;
+ buf[0] &= leadMask!sz;
+ foreach (n; 1 .. sz)
+ buf[n] = buf[n] & 0x3f; //keep 6 lower bits
+ ret[] = buf[0 .. sz];
+ return ret;
+ }
+
+ auto build(Set)(Set set)
+ {
+ import std.algorithm.iteration : map;
+ auto ascii = set & unicode.ASCII;
+ auto utf8_2 = set & CodepointSet(0x80, 0x800);
+ auto utf8_3 = set & CodepointSet(0x800, 0x1_0000);
+ auto utf8_4 = set & CodepointSet(0x1_0000, lastDchar+1);
+ auto asciiT = ascii.byCodepoint.map!(x=>cast(char) x).buildTrie!(AsciiSpec);
+ auto utf8_2T = utf8_2.byCodepoint.map!(x=>encode!2(x)).buildTrie!(Utf8Spec2);
+ auto utf8_3T = utf8_3.byCodepoint.map!(x=>encode!3(x)).buildTrie!(Utf8Spec3);
+ auto utf8_4T = utf8_4.byCodepoint.map!(x=>encode!4(x)).buildTrie!(Utf8Spec4);
+ alias Ret = Impl!(1,2,3,4);
+ return Ret(asciiT, utf8_2T, utf8_3T, utf8_4T);
+ }
+
+ // Bootstrap UTF-8 static matcher interface
+ // from 3 primitives: tab!(size), lookup and Sizes
+ mixin template DefMatcher()
+ {
+ import std.format : format;
+ import std.meta : Erase, staticIndexOf;
+ enum hasASCII = staticIndexOf!(1, Sizes) >= 0;
+ alias UniSizes = Erase!(1, Sizes);
+
+ //generate dispatch code sequence for unicode parts
+ static auto genDispatch()
+ {
+ string code;
+ foreach (size; UniSizes)
+ code ~= format(q{
+ if ((ch & ~leadMask!%d) == encMask!(%d))
+ return lookup!(%d, mode)(inp);
+ else
+ }, size, size, size);
+ static if (Sizes.length == 4) //covers all code unit cases
+ code ~= "{ badEncoding(); return false; }";
+ else
+ code ~= "return false;"; //may be just fine but not covered
+ return code;
+ }
+ enum dispatch = genDispatch();
+
+ public bool match(Range)(ref Range inp) const
+ if (isRandomAccessRange!Range && is(ElementType!Range : char) &&
+ !isDynamicArray!Range)
+ {
+ enum mode = Mode.skipOnMatch;
+ assert(!inp.empty);
+ immutable ch = inp[0];
+ static if (hasASCII)
+ {
+ if (ch < 0x80)
+ {
+ immutable r = tab!1[ch];
+ if (r)
+ inp.popFront();
+ return r;
+ }
+ else
+ mixin(dispatch);
+ }
+ else
+ mixin(dispatch);
+ }
+
+ static if (Sizes.length == 4) // can skip iff can detect all encodings
+ {
+ public bool skip(Range)(ref Range inp) const
+ if (isRandomAccessRange!Range && is(ElementType!Range : char) &&
+ !isDynamicArray!Range)
+ {
+ enum mode = Mode.alwaysSkip;
+ assert(!inp.empty);
+ auto ch = inp[0];
+ static if (hasASCII)
+ {
+ if (ch < 0x80)
+ {
+ inp.popFront();
+ return tab!1[ch];
+ }
+ else
+ mixin(dispatch);
+ }
+ else
+ mixin(dispatch);
+ }
+ }
+
+ public bool test(Range)(ref Range inp) const
+ if (isRandomAccessRange!Range && is(ElementType!Range : char) &&
+ !isDynamicArray!Range)
+ {
+ enum mode = Mode.neverSkip;
+ assert(!inp.empty);
+ auto ch = inp[0];
+ static if (hasASCII)
+ {
+ if (ch < 0x80)
+ return tab!1[ch];
+ else
+ mixin(dispatch);
+ }
+ else
+ mixin(dispatch);
+ }
+
+ bool match(C)(ref C[] str) const
+ if (isSomeChar!C)
+ {
+ return fwdStr!"match"(str);
+ }
+
+ bool skip(C)(ref C[] str) const
+ if (isSomeChar!C)
+ {
+ return fwdStr!"skip"(str);
+ }
+
+ bool test(C)(ref C[] str) const
+ if (isSomeChar!C)
+ {
+ return fwdStr!"test"(str);
+ }
+
+ mixin ForwardStrings;
+ }
+
+ struct Impl(Sizes...)
+ {
+ import std.meta : allSatisfy, staticMap;
+ static assert(allSatisfy!(validSize, Sizes),
+ "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8");
+ private:
+ //pick tables for chosen sizes
+ alias OurTabs = staticMap!(Table, Sizes);
+ OurTabs tables;
+ mixin DefMatcher;
+ //static disptach helper UTF size ==> table
+ alias tab(int i) = tables[i - 1];
+
+ package(std) @property CherryPick!(Impl, SizesToPick) subMatcher(SizesToPick...)()
+ {
+ return CherryPick!(Impl, SizesToPick)(&this);
+ }
+
+ bool lookup(int size, Mode mode, Range)(ref Range inp) const
+ {
+ import std.range : popFrontN;
+ if (inp.length < size)
+ {
+ badEncoding();
+ return false;
+ }
+ char[size] needle = void;
+ needle[0] = leadMask!size & inp[0];
+ static foreach (i; 1 .. size)
+ {
+ needle[i] = truncate(inp[i]);
+ }
+ //overlong encoding checks
+ static if (size == 2)
+ {
+ //0x80-0x7FF
+ //got 6 bits in needle[1], must use at least 8 bits
+ //must use at least 2 bits in needle[1]
+ if (needle[0] < 2) badEncoding();
+ }
+ else static if (size == 3)
+ {
+ //0x800-0xFFFF
+ //got 6 bits in needle[2], must use at least 12bits
+ //must use 6 bits in needle[1] or anything in needle[0]
+ if (needle[0] == 0 && needle[1] < 0x20) badEncoding();
+ }
+ else static if (size == 4)
+ {
+ //0x800-0xFFFF
+ //got 2x6=12 bits in needle[2 .. 3] must use at least 17bits
+ //must use 5 bits (or above) in needle[1] or anything in needle[0]
+ if (needle[0] == 0 && needle[1] < 0x10) badEncoding();
+ }
+ static if (mode == Mode.alwaysSkip)
+ {
+ inp.popFrontN(size);
+ return tab!size[needle];
+ }
+ else static if (mode == Mode.neverSkip)
+ {
+ return tab!size[needle];
+ }
+ else
+ {
+ static assert(mode == Mode.skipOnMatch);
+ if (tab!size[needle])
+ {
+ inp.popFrontN(size);
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+ }
+
+ struct CherryPick(I, Sizes...)
+ {
+ import std.meta : allSatisfy;
+ static assert(allSatisfy!(validSize, Sizes),
+ "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8");
+ private:
+ I* m;
+ @property auto tab(int i)() const { return m.tables[i - 1]; }
+ bool lookup(int size, Mode mode, Range)(ref Range inp) const
+ {
+ return m.lookup!(size, mode)(inp);
+ }
+ mixin DefMatcher;
+ }
+}
+
+template Utf16Matcher()
+{
+ enum validSize(int sz) = sz >= 1 && sz <= 2;
+
+ void badEncoding() pure @safe
+ {
+ import std.utf : UTFException;
+ throw new UTFException("Invalid UTF-16 sequence");
+ }
+
+ // 1-stage ASCII
+ alias AsciiSpec = AliasSeq!(bool, wchar, clamp!7);
+ //2-stage BMP
+ alias BmpSpec = AliasSeq!(bool, wchar, sliceBits!(7, 16), sliceBits!(0, 7));
+ //4-stage - full Unicode
+ //assume that 0xD800 & 0xDC00 bits are cleared
+ //thus leaving 10 bit per wchar to worry about
+ alias UniSpec = AliasSeq!(bool, wchar[2],
+ assumeSize!(x=>x[0]>>4, 6), assumeSize!(x=>x[0]&0xf, 4),
+ assumeSize!(x=>x[1]>>6, 4), assumeSize!(x=>x[1]&0x3f, 6),
+ );
+ alias Ascii = typeof(TrieBuilder!(AsciiSpec)(false).build());
+ alias Bmp = typeof(TrieBuilder!(BmpSpec)(false).build());
+ alias Uni = typeof(TrieBuilder!(UniSpec)(false).build());
+
+ auto encode2(dchar ch)
+ {
+ ch -= 0x1_0000;
+ assert(ch <= 0xF_FFFF);
+ wchar[2] ret;
+ //do not put surrogate bits, they are sliced off
+ ret[0] = cast(wchar)(ch >> 10);
+ ret[1] = (ch & 0xFFF);
+ return ret;
+ }
+
+ auto build(Set)(Set set)
+ {
+ import std.algorithm.iteration : map;
+ auto ascii = set & unicode.ASCII;
+ auto bmp = (set & CodepointSet.fromIntervals(0x80, 0xFFFF+1))
+ - CodepointSet.fromIntervals(0xD800, 0xDFFF+1);
+ auto other = set - (bmp | ascii);
+ auto asciiT = ascii.byCodepoint.map!(x=>cast(char) x).buildTrie!(AsciiSpec);
+ auto bmpT = bmp.byCodepoint.map!(x=>cast(wchar) x).buildTrie!(BmpSpec);
+ auto otherT = other.byCodepoint.map!(x=>encode2(x)).buildTrie!(UniSpec);
+ alias Ret = Impl!(1,2);
+ return Ret(asciiT, bmpT, otherT);
+ }
+
+ //bootstrap full UTF-16 matcher interace from
+ //sizeFlags, lookupUni and ascii
+ mixin template DefMatcher()
+ {
+ public bool match(Range)(ref Range inp) const
+ if (isRandomAccessRange!Range && is(ElementType!Range : wchar) &&
+ !isDynamicArray!Range)
+ {
+ enum mode = Mode.skipOnMatch;
+ assert(!inp.empty);
+ immutable ch = inp[0];
+ static if (sizeFlags & 1)
+ {
+ if (ch < 0x80)
+ {
+ if (ascii[ch])
+ {
+ inp.popFront();
+ return true;
+ }
+ else
+ return false;
+ }
+ return lookupUni!mode(inp);
+ }
+ else
+ return lookupUni!mode(inp);
+ }
+
+ static if (Sizes.length == 2)
+ {
+ public bool skip(Range)(ref Range inp) const
+ if (isRandomAccessRange!Range && is(ElementType!Range : wchar) &&
+ !isDynamicArray!Range)
+ {
+ enum mode = Mode.alwaysSkip;
+ assert(!inp.empty);
+ immutable ch = inp[0];
+ static if (sizeFlags & 1)
+ {
+ if (ch < 0x80)
+ {
+ inp.popFront();
+ return ascii[ch];
+ }
+ else
+ return lookupUni!mode(inp);
+ }
+ else
+ return lookupUni!mode(inp);
+ }
+ }
+
+ public bool test(Range)(ref Range inp) const
+ if (isRandomAccessRange!Range && is(ElementType!Range : wchar) &&
+ !isDynamicArray!Range)
+ {
+ enum mode = Mode.neverSkip;
+ assert(!inp.empty);
+ auto ch = inp[0];
+ static if (sizeFlags & 1)
+ return ch < 0x80 ? ascii[ch] : lookupUni!mode(inp);
+ else
+ return lookupUni!mode(inp);
+ }
+
+ bool match(C)(ref C[] str) const
+ if (isSomeChar!C)
+ {
+ return fwdStr!"match"(str);
+ }
+
+ bool skip(C)(ref C[] str) const
+ if (isSomeChar!C)
+ {
+ return fwdStr!"skip"(str);
+ }
+
+ bool test(C)(ref C[] str) const
+ if (isSomeChar!C)
+ {
+ return fwdStr!"test"(str);
+ }
+
+ mixin ForwardStrings; //dispatch strings to range versions
+ }
+
+ struct Impl(Sizes...)
+ if (Sizes.length >= 1 && Sizes.length <= 2)
+ {
+ private:
+ import std.meta : allSatisfy;
+ static assert(allSatisfy!(validSize, Sizes),
+ "Only lengths of 1 and 2 code units are possible in UTF-16");
+ static if (Sizes.length > 1)
+ enum sizeFlags = Sizes[0] | Sizes[1];
+ else
+ enum sizeFlags = Sizes[0];
+
+ static if (sizeFlags & 1)
+ {
+ Ascii ascii;
+ Bmp bmp;
+ }
+ static if (sizeFlags & 2)
+ {
+ Uni uni;
+ }
+ mixin DefMatcher;
+
+ package(std) @property CherryPick!(Impl, SizesToPick) subMatcher(SizesToPick...)()
+ {
+ return CherryPick!(Impl, SizesToPick)(&this);
+ }
+
+ bool lookupUni(Mode mode, Range)(ref Range inp) const
+ {
+ wchar x = cast(wchar)(inp[0] - 0xD800);
+ //not a high surrogate
+ if (x > 0x3FF)
+ {
+ //low surrogate
+ if (x <= 0x7FF) badEncoding();
+ static if (sizeFlags & 1)
+ {
+ auto ch = inp[0];
+ static if (mode == Mode.alwaysSkip)
+ inp.popFront();
+ static if (mode == Mode.skipOnMatch)
+ {
+ if (bmp[ch])
+ {
+ inp.popFront();
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ return bmp[ch];
+ }
+ else //skip is not available for sub-matchers, so just false
+ return false;
+ }
+ else
+ {
+ import std.range : popFrontN;
+ static if (sizeFlags & 2)
+ {
+ if (inp.length < 2)
+ badEncoding();
+ wchar y = cast(wchar)(inp[1] - 0xDC00);
+ //not a low surrogate
+ if (y > 0x3FF)
+ badEncoding();
+ wchar[2] needle = [inp[0] & 0x3ff, inp[1] & 0x3ff];
+ static if (mode == Mode.alwaysSkip)
+ inp.popFrontN(2);
+ static if (mode == Mode.skipOnMatch)
+ {
+ if (uni[needle])
+ {
+ inp.popFrontN(2);
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ return uni[needle];
+ }
+ else //ditto
+ return false;
+ }
+ }
+ }
+
+ struct CherryPick(I, Sizes...)
+ if (Sizes.length >= 1 && Sizes.length <= 2)
+ {
+ private:
+ import std.meta : allSatisfy;
+ I* m;
+ enum sizeFlags = I.sizeFlags;
+
+ static if (sizeFlags & 1)
+ {
+ @property auto ascii()() const { return m.ascii; }
+ }
+
+ bool lookupUni(Mode mode, Range)(ref Range inp) const
+ {
+ return m.lookupUni!mode(inp);
+ }
+ mixin DefMatcher;
+ static assert(allSatisfy!(validSize, Sizes),
+ "Only lengths of 1 and 2 code units are possible in UTF-16");
+ }
+}
+
+private auto utf8Matcher(Set)(Set set)
+{
+ return Utf8Matcher!().build(set);
+}
+
+private auto utf16Matcher(Set)(Set set)
+{
+ return Utf16Matcher!().build(set);
+}
+
+/**
+ Constructs a matcher object
+ to classify $(CODEPOINTS) from the `set` for encoding
+ that has `Char` as code unit.
+
+ See $(LREF MatcherConcept) for API outline.
+*/
+public auto utfMatcher(Char, Set)(Set set)
+if (isCodepointSet!Set)
+{
+ static if (is(Char : char))
+ return utf8Matcher(set);
+ else static if (is(Char : wchar))
+ return utf16Matcher(set);
+ else static if (is(Char : dchar))
+ static assert(false, "UTF-32 needs no decoding,
+ and thus not supported by utfMatcher");
+ else
+ static assert(false, "Only character types 'char' and 'wchar' are allowed");
+}
+
+
+//a range of code units, packed with index to speed up forward iteration
+package(std) auto decoder(C)(C[] s, size_t offset=0)
+if (is(C : wchar) || is(C : char))
+{
+ static struct Decoder
+ {
+ pure nothrow:
+ C[] str;
+ size_t idx;
+ @property C front(){ return str[idx]; }
+ @property C back(){ return str[$-1]; }
+ void popFront(){ idx++; }
+ void popBack(){ str = str[0..$-1]; }
+ void popFrontN(size_t n){ idx += n; }
+ @property bool empty(){ return idx == str.length; }
+ @property auto save(){ return this; }
+ auto opIndex(size_t i){ return str[idx+i]; }
+ @property size_t length(){ return str.length - idx; }
+ alias opDollar = length;
+ auto opSlice(size_t a, size_t b){ return Decoder(str[0 .. idx+b], idx+a); }
+ }
+ static assert(isRandomAccessRange!Decoder);
+ static assert(is(ElementType!Decoder : C));
+ return Decoder(s, offset);
+}
+
+pure @safe unittest
+{
+ string rs = "hi! ネемног砀 текÑта";
+ auto codec = rs.decoder;
+ auto utf8 = utf8Matcher(unicode.Letter);
+ auto asc = utf8.subMatcher!(1);
+ auto uni = utf8.subMatcher!(2,3,4);
+ assert(asc.test(codec));
+ assert(!uni.match(codec));
+ assert(utf8.skip(codec));
+ assert(codec.idx == 1);
+
+ assert(!uni.match(codec));
+ assert(asc.test(codec));
+ assert(utf8.skip(codec));
+ assert(codec.idx == 2);
+ assert(!asc.match(codec));
+
+ assert(!utf8.test(codec));
+ assert(!utf8.skip(codec));
+
+ assert(!asc.test(codec));
+ assert(!utf8.test(codec));
+ assert(!utf8.skip(codec));
+ assert(utf8.test(codec));
+ foreach (i; 0 .. 7)
+ {
+ assert(!asc.test(codec));
+ assert(uni.test(codec));
+ assert(utf8.skip(codec));
+ }
+ assert(!utf8.test(codec));
+ assert(!utf8.skip(codec));
+ //the same with match where applicable
+ codec = rs.decoder;
+ assert(utf8.match(codec));
+ assert(codec.idx == 1);
+ assert(utf8.match(codec));
+ assert(codec.idx == 2);
+ assert(!utf8.match(codec));
+ assert(codec.idx == 2);
+ assert(!utf8.skip(codec));
+ assert(!utf8.skip(codec));
+
+ foreach (i; 0 .. 7)
+ {
+ assert(!asc.test(codec));
+ assert(utf8.test(codec));
+ assert(utf8.match(codec));
+ }
+ auto i = codec.idx;
+ assert(!utf8.match(codec));
+ assert(codec.idx == i);
+}
+
+pure @safe unittest
+{
+ import std.range : stride;
+ static bool testAll(Matcher, Range)(scope ref Matcher m, ref Range r)
+ {
+ bool t = m.test(r);
+ auto save = r.idx;
+ assert(t == m.match(r));
+ assert(r.idx == save || t); //ether no change or was match
+ r.idx = save;
+ static if (is(typeof(m.skip(r))))
+ {
+ assert(t == m.skip(r));
+ assert(r.idx != save); //always changed
+ r.idx = save;
+ }
+ return t;
+ }
+ auto utf16 = utfMatcher!wchar(unicode.L);
+ auto bmp = utf16.subMatcher!1;
+ auto nonBmp = utf16.subMatcher!1;
+ auto utf8 = utfMatcher!char(unicode.L);
+ auto ascii = utf8.subMatcher!1;
+ auto uni2 = utf8.subMatcher!2;
+ auto uni3 = utf8.subMatcher!3;
+ auto uni24 = utf8.subMatcher!(2,4);
+ foreach (ch; unicode.L.byCodepoint.stride(3))
+ {
+ import std.utf : encode;
+ char[4] buf;
+ wchar[2] buf16;
+ auto len = encode(buf, ch);
+ auto len16 = encode(buf16, ch);
+ auto c8 = buf[0 .. len].decoder;
+ auto c16 = buf16[0 .. len16].decoder;
+ assert(testAll(utf16, c16));
+ assert(testAll(bmp, c16) || len16 != 1);
+ assert(testAll(nonBmp, c16) || len16 != 2);
+
+ assert(testAll(utf8, c8));
+
+ //submatchers return false on out of their domain
+ assert(testAll(ascii, c8) || len != 1);
+ assert(testAll(uni2, c8) || len != 2);
+ assert(testAll(uni3, c8) || len != 3);
+ assert(testAll(uni24, c8) || (len != 2 && len != 4));
+ }
+}
+
+// cover decode fail cases of Matcher
+pure @system unittest
+{
+ import std.algorithm.iteration : map;
+ import std.exception : collectException;
+ import std.format : format;
+ auto utf16 = utfMatcher!wchar(unicode.L);
+ auto utf8 = utfMatcher!char(unicode.L);
+ //decode failure cases UTF-8
+ alias fails8 = AliasSeq!("\xC1", "\x80\x00","\xC0\x00", "\xCF\x79",
+ "\xFF\x00\0x00\0x00\x00", "\xC0\0x80\0x80\x80", "\x80\0x00\0x00\x00",
+ "\xCF\x00\0x00\0x00\x00");
+ foreach (msg; fails8)
+ {
+ assert(collectException((){
+ auto s = msg;
+ size_t idx = 0;
+ utf8.test(s);
+ }()), format("%( %2x %)", cast(ubyte[]) msg));
+ }
+ //decode failure cases UTF-16
+ alias fails16 = AliasSeq!([0xD811], [0xDC02]);
+ foreach (msg; fails16)
+ {
+ assert(collectException((){
+ auto s = msg.map!(x => cast(wchar) x);
+ utf16.test(s);
+ }()));
+ }
+}
+
+/++
+ Convenience function to construct optimal configurations for
+ packed Trie from any `set` of $(CODEPOINTS).
+
+ The parameter `level` indicates the number of trie levels to use,
+ allowed values are: 1, 2, 3 or 4. Levels represent different trade-offs
+ speed-size wise.
+
+ $(P Level 1 is fastest and the most memory hungry (a bit array). )
+ $(P Level 4 is the slowest and has the smallest footprint. )
+
+ See the $(S_LINK Synopsis, Synopsis) section for example.
+
+ Note:
+ Level 4 stays very practical (being faster and more predictable)
+ compared to using direct lookup on the `set` itself.
+
+
++/
+public auto toTrie(size_t level, Set)(Set set)
+if (isCodepointSet!Set)
+{
+ static if (level == 1)
+ return codepointSetTrie!(21)(set);
+ else static if (level == 2)
+ return codepointSetTrie!(10, 11)(set);
+ else static if (level == 3)
+ return codepointSetTrie!(8, 5, 8)(set);
+ else static if (level == 4)
+ return codepointSetTrie!(6, 4, 4, 7)(set);
+ else
+ static assert(false,
+ "Sorry, toTrie doesn't support levels > 4, use codepointSetTrie directly");
+}
+
+/**
+ $(P Builds a `Trie` with typically optimal speed-size trade-off
+ and wraps it into a delegate of the following type:
+ $(D bool delegate(dchar ch)). )
+
+ $(P Effectively this creates a 'tester' lambda suitable
+ for algorithms like std.algorithm.find that take unary predicates. )
+
+ See the $(S_LINK Synopsis, Synopsis) section for example.
+*/
+public auto toDelegate(Set)(Set set)
+if (isCodepointSet!Set)
+{
+ // 3 is very small and is almost as fast as 2-level (due to CPU caches?)
+ auto t = toTrie!3(set);
+ return (dchar ch) => t[ch];
+}
+
+/**
+ $(P Opaque wrapper around unsigned built-in integers and
+ code unit (char/wchar/dchar) types.
+ Parameter `sz` indicates that the value is confined
+ to the range of [0, 2^^sz$(RPAREN). With this knowledge it can be
+ packed more tightly when stored in certain
+ data-structures like trie. )
+
+ Note:
+ $(P The $(D BitPacked!(T, sz)) is implicitly convertible to `T`
+ but not vise-versa. Users have to ensure the value fits in
+ the range required and use the `cast`
+ operator to perform the conversion.)
+*/
+struct BitPacked(T, size_t sz)
+if (isIntegral!T || is(T:dchar))
+{
+ enum bitSize = sz;
+ T _value;
+ alias _value this;
+}
+
+/*
+ Depending on the form of the passed argument `bitSizeOf` returns
+ the amount of bits required to represent a given type
+ or a return type of a given functor.
+*/
+template bitSizeOf(Args...)
+if (Args.length == 1)
+{
+ import std.traits : ReturnType;
+ alias T = Args[0];
+ static if (__traits(compiles, { size_t val = T.bitSize; })) //(is(typeof(T.bitSize) : size_t))
+ {
+ enum bitSizeOf = T.bitSize;
+ }
+ else static if (is(ReturnType!T dummy == BitPacked!(U, bits), U, size_t bits))
+ {
+ enum bitSizeOf = bitSizeOf!(ReturnType!T);
+ }
+ else
+ {
+ enum bitSizeOf = T.sizeof*8;
+ }
+}
+
+/**
+ Tests if `T` is some instantiation of $(LREF BitPacked)!(U, x)
+ and thus suitable for packing.
+*/
+template isBitPacked(T)
+{
+ static if (is(T dummy == BitPacked!(U, bits), U, size_t bits))
+ enum isBitPacked = true;
+ else
+ enum isBitPacked = false;
+}
+
+/**
+ Gives the type `U` from $(LREF BitPacked)!(U, x)
+ or `T` itself for every other type.
+*/
+template TypeOfBitPacked(T)
+{
+ static if (is(T dummy == BitPacked!(U, bits), U, size_t bits))
+ alias TypeOfBitPacked = U;
+ else
+ alias TypeOfBitPacked = T;
+}
+
+/*
+ Wrapper, used in definition of custom data structures from `Trie` template.
+ Applying it to a unary lambda function indicates that the returned value always
+ fits within `bits` of bits.
+*/
+struct assumeSize(alias Fn, size_t bits)
+{
+ enum bitSize = bits;
+ static auto ref opCall(T)(auto ref T arg)
+ {
+ return Fn(arg);
+ }
+}
+
+/*
+ A helper for defining lambda function that yields a slice
+ of certain bits from an unsigned integral value.
+ The resulting lambda is wrapped in assumeSize and can be used directly
+ with `Trie` template.
+*/
+struct sliceBits(size_t from, size_t to)
+{
+ //for now bypass assumeSize, DMD has trouble inlining it
+ enum bitSize = to-from;
+ static auto opCall(T)(T x)
+ out(result)
+ {
+ assert(result < (1 << to-from));
+ }
+ do
+ {
+ static assert(from < to);
+ static if (from == 0)
+ return x & ((1 << to)-1);
+ else
+ return (x >> from) & ((1<<(to-from))-1);
+ }
+}
+
+@safe pure nothrow @nogc uint low_8(uint x) { return x&0xFF; }
+@safe pure nothrow @nogc uint midlow_8(uint x){ return (x&0xFF00)>>8; }
+alias lo8 = assumeSize!(low_8, 8);
+alias mlo8 = assumeSize!(midlow_8, 8);
+
+@safe pure nothrow @nogc unittest
+{
+ static assert(bitSizeOf!lo8 == 8);
+ static assert(bitSizeOf!(sliceBits!(4, 7)) == 3);
+ static assert(bitSizeOf!(BitPacked!(uint, 2)) == 2);
+}
+
+template Sequence(size_t start, size_t end)
+{
+ static if (start < end)
+ alias Sequence = AliasSeq!(start, Sequence!(start+1, end));
+ else
+ alias Sequence = AliasSeq!();
+}
+
+//---- TRIE TESTS ----
+@system unittest
+{
+ import std.algorithm.iteration : map;
+ import std.algorithm.sorting : sort;
+ import std.array : array;
+ import std.conv : text, to;
+ import std.range : iota;
+ static trieStats(TRIE)(TRIE t)
+ {
+ version (std_uni_stats)
+ {
+ import std.stdio : writefln, writeln;
+ writeln("---TRIE FOOTPRINT STATS---");
+ static foreach (i; 0 .. t.table.dim)
+ {
+ writefln("lvl%s = %s bytes; %s pages"
+ , i, t.bytes!i, t.pages!i);
+ }
+ writefln("TOTAL: %s bytes", t.bytes);
+ version (none)
+ {
+ writeln("INDEX (excluding value level):");
+ static foreach (i; 0 .. t.table.dim-1)
+ writeln(t.table.slice!(i)[0 .. t.table.length!i]);
+ }
+ writeln("---------------------------");
+ }
+ }
+ //@@@BUG link failure, lambdas not found by linker somehow (in case of trie2)
+ // alias lo8 = assumeSize!(8, function (uint x) { return x&0xFF; });
+ // alias next8 = assumeSize!(7, function (uint x) { return (x&0x7F00)>>8; });
+ alias Set = CodepointSet;
+ auto set = Set('A','Z','a','z');
+ auto trie = buildTrie!(bool, uint, 256, lo8)(set.byInterval);// simple bool array
+ for (int a='a'; a<'z';a++)
+ assert(trie[a]);
+ for (int a='A'; a<'Z';a++)
+ assert(trie[a]);
+ for (int a=0; a<'A'; a++)
+ assert(!trie[a]);
+ for (int a ='Z'; a<'a'; a++)
+ assert(!trie[a]);
+ trieStats(trie);
+
+ auto redundant2 = Set(
+ 1, 18, 256+2, 256+111, 512+1, 512+18, 768+2, 768+111);
+ auto trie2 = buildTrie!(bool, uint, 1024, mlo8, lo8)(redundant2.byInterval);
+ trieStats(trie2);
+ foreach (e; redundant2.byCodepoint)
+ assert(trie2[e], text(cast(uint) e, " - ", trie2[e]));
+ foreach (i; 0 .. 1024)
+ {
+ assert(trie2[i] == (i in redundant2));
+ }
+
+
+ auto redundant3 = Set(
+ 2, 4, 6, 8, 16,
+ 2+16, 4+16, 16+6, 16+8, 16+16,
+ 2+32, 4+32, 32+6, 32+8,
+ );
+
+ enum max3 = 256;
+ // sliceBits
+ auto trie3 = buildTrie!(bool, uint, max3,
+ sliceBits!(6,8), sliceBits!(4,6), sliceBits!(0,4)
+ )(redundant3.byInterval);
+ trieStats(trie3);
+ foreach (i; 0 .. max3)
+ assert(trie3[i] == (i in redundant3), text(cast(uint) i));
+
+ auto redundant4 = Set(
+ 10, 64, 64+10, 128, 128+10, 256, 256+10, 512,
+ 1000, 2000, 3000, 4000, 5000, 6000
+ );
+ enum max4 = 2^^16;
+ auto trie4 = buildTrie!(bool, size_t, max4,
+ sliceBits!(13, 16), sliceBits!(9, 13), sliceBits!(6, 9) , sliceBits!(0, 6)
+ )(redundant4.byInterval);
+ foreach (i; 0 .. max4)
+ {
+ if (i in redundant4)
+ assert(trie4[i], text(cast(uint) i));
+ }
+ trieStats(trie4);
+
+ alias mapToS = mapTrieIndex!(useItemAt!(0, char));
+ string[] redundantS = ["tea", "start", "orange"];
+ redundantS.sort!((a,b) => mapToS(a) < mapToS(b))();
+ auto strie = buildTrie!(bool, string, useItemAt!(0, char))(redundantS);
+ // using first char only
+ assert(redundantS == ["orange", "start", "tea"]);
+ assert(strie["test"], text(strie["test"]));
+ assert(!strie["aea"]);
+ assert(strie["s"]);
+
+ // a bit size test
+ auto a = array(map!(x => to!ubyte(x))(iota(0, 256)));
+ auto bt = buildTrie!(bool, ubyte, sliceBits!(7, 8), sliceBits!(5, 7), sliceBits!(0, 5))(a);
+ trieStats(bt);
+ foreach (i; 0 .. 256)
+ assert(bt[cast(ubyte) i]);
+}
+
+template useItemAt(size_t idx, T)
+if (isIntegral!T || is(T: dchar))
+{
+ size_t impl(const scope T[] arr){ return arr[idx]; }
+ alias useItemAt = assumeSize!(impl, 8*T.sizeof);
+}
+
+template useLastItem(T)
+{
+ size_t impl(const scope T[] arr){ return arr[$-1]; }
+ alias useLastItem = assumeSize!(impl, 8*T.sizeof);
+}
+
+template fullBitSize(Prefix...)
+{
+ static if (Prefix.length > 0)
+ enum fullBitSize = bitSizeOf!(Prefix[0])+fullBitSize!(Prefix[1..$]);
+ else
+ enum fullBitSize = 0;
+}
+
+template idxTypes(Key, size_t fullBits, Prefix...)
+{
+ static if (Prefix.length == 1)
+ {// the last level is value level, so no index once reduced to 1-level
+ alias idxTypes = AliasSeq!();
+ }
+ else
+ {
+ // Important note on bit packing
+ // Each level has to hold enough of bits to address the next one
+ // The bottom level is known to hold full bit width
+ // thus it's size in pages is full_bit_width - size_of_last_prefix
+ // Recourse on this notion
+ alias idxTypes =
+ AliasSeq!(
+ idxTypes!(Key, fullBits - bitSizeOf!(Prefix[$-1]), Prefix[0..$-1]),
+ BitPacked!(typeof(Prefix[$-2](Key.init)), fullBits - bitSizeOf!(Prefix[$-1]))
+ );
+ }
+}
+
+//============================================================================
+
+@safe pure int comparePropertyName(Char1, Char2)(const(Char1)[] a, const(Char2)[] b)
+if (is(Char1 : dchar) && is(Char2 : dchar))
+{
+ import std.algorithm.comparison : cmp;
+ import std.algorithm.iteration : map, filter;
+ import std.ascii : toLower;
+ static bool pred(dchar c) {return !c.isWhite && c != '-' && c != '_';}
+ return cmp(
+ a.map!toLower.filter!pred,
+ b.map!toLower.filter!pred);
+}
+
+@safe pure unittest
+{
+ assert(!comparePropertyName("foo-bar", "fooBar"));
+}
+
+bool propertyNameLess(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) @safe pure
+if (is(Char1 : dchar) && is(Char2 : dchar))
+{
+ return comparePropertyName(a, b) < 0;
+}
+
+//============================================================================
+// Utilities for compression of Unicode code point sets
+//============================================================================
+
+@safe void compressTo(uint val, ref ubyte[] arr) pure nothrow
+{
+ // not optimized as usually done 1 time (and not public interface)
+ if (val < 128)
+ arr ~= cast(ubyte) val;
+ else if (val < (1 << 13))
+ {
+ arr ~= (0b1_00 << 5) | cast(ubyte)(val >> 8);
+ arr ~= val & 0xFF;
+ }
+ else
+ {
+ assert(val < (1 << 21));
+ arr ~= (0b1_01 << 5) | cast(ubyte)(val >> 16);
+ arr ~= (val >> 8) & 0xFF;
+ arr ~= val & 0xFF;
+ }
+}
+
+@safe uint decompressFrom(const(ubyte)[] arr, ref size_t idx) pure
+{
+ import std.exception : enforce;
+ immutable first = arr[idx++];
+ if (!(first & 0x80)) // no top bit -> [0 .. 127]
+ return first;
+ immutable extra = ((first >> 5) & 1) + 1; // [1, 2]
+ uint val = (first & 0x1F);
+ enforce(idx + extra <= arr.length, "bad code point interval encoding");
+ foreach (j; 0 .. extra)
+ val = (val << 8) | arr[idx+j];
+ idx += extra;
+ return val;
+}
+
+
+package(std) ubyte[] compressIntervals(Range)(Range intervals)
+if (isInputRange!Range && isIntegralPair!(ElementType!Range))
+{
+ ubyte[] storage;
+ uint base = 0;
+ // RLE encode
+ foreach (val; intervals)
+ {
+ compressTo(val[0]-base, storage);
+ base = val[0];
+ if (val[1] != lastDchar+1) // till the end of the domain so don't store it
+ {
+ compressTo(val[1]-base, storage);
+ base = val[1];
+ }
+ }
+ return storage;
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ auto run = [tuple(80, 127), tuple(128, (1 << 10)+128)];
+ ubyte[] enc = [cast(ubyte) 80, 47, 1, (0b1_00 << 5) | (1 << 2), 0];
+ assert(compressIntervals(run) == enc);
+ auto run2 = [tuple(0, (1 << 20)+512+1), tuple((1 << 20)+512+4, lastDchar+1)];
+ ubyte[] enc2 = [cast(ubyte) 0, (0b1_01 << 5) | (1 << 4), 2, 1, 3]; // odd length-ed
+ assert(compressIntervals(run2) == enc2);
+ size_t idx = 0;
+ assert(decompressFrom(enc, idx) == 80);
+ assert(decompressFrom(enc, idx) == 47);
+ assert(decompressFrom(enc, idx) == 1);
+ assert(decompressFrom(enc, idx) == (1 << 10));
+ idx = 0;
+ assert(decompressFrom(enc2, idx) == 0);
+ assert(decompressFrom(enc2, idx) == (1 << 20)+512+1);
+ assert(equal(decompressIntervals(compressIntervals(run)), run));
+ assert(equal(decompressIntervals(compressIntervals(run2)), run2));
+}
+
+// Creates a range of `CodepointInterval` that lazily decodes compressed data.
+@safe package(std) auto decompressIntervals(const(ubyte)[] data) pure
+{
+ return DecompressedIntervals(data);
+}
+
+@safe struct DecompressedIntervals
+{
+pure:
+ const(ubyte)[] _stream;
+ size_t _idx;
+ CodepointInterval _front;
+
+ this(const(ubyte)[] stream)
+ {
+ _stream = stream;
+ popFront();
+ }
+
+ @property CodepointInterval front()
+ {
+ assert(!empty);
+ return _front;
+ }
+
+ void popFront()
+ {
+ if (_idx == _stream.length)
+ {
+ _idx = size_t.max;
+ return;
+ }
+ uint base = _front[1];
+ _front[0] = base + decompressFrom(_stream, _idx);
+ if (_idx == _stream.length)// odd length ---> till the end
+ _front[1] = lastDchar+1;
+ else
+ {
+ base = _front[0];
+ _front[1] = base + decompressFrom(_stream, _idx);
+ }
+ }
+
+ @property bool empty() const
+ {
+ return _idx == size_t.max;
+ }
+
+ @property DecompressedIntervals save() return scope { return this; }
+}
+
+@safe pure nothrow @nogc unittest
+{
+ static assert(isInputRange!DecompressedIntervals);
+ static assert(isForwardRange!DecompressedIntervals);
+}
+
+//============================================================================
+
+version (std_uni_bootstrap){}
+else
+{
+
+// helper for looking up code point sets
+ptrdiff_t findUnicodeSet(alias table, C)(const scope C[] name)
+{
+ import std.algorithm.iteration : map;
+ import std.range : assumeSorted;
+ auto range = assumeSorted!((a,b) => propertyNameLess(a,b))
+ (table.map!"a.name"());
+ size_t idx = range.lowerBound(name).length;
+ if (idx < range.length && comparePropertyName(range[idx], name) == 0)
+ return idx;
+ return -1;
+}
+
+// another one that loads it
+bool loadUnicodeSet(alias table, Set, C)(const scope C[] name, ref Set dest)
+{
+ auto idx = findUnicodeSet!table(name);
+ if (idx >= 0)
+ {
+ dest = Set(asSet(table[idx].compressed));
+ return true;
+ }
+ return false;
+}
+
+bool loadProperty(Set=CodepointSet, C)
+ (const scope C[] name, ref Set target) pure
+{
+ import std.internal.unicode_tables : uniProps; // generated file
+ alias ucmp = comparePropertyName;
+ // conjure cumulative properties by hand
+ if (ucmp(name, "L") == 0 || ucmp(name, "Letter") == 0)
+ {
+ target = asSet(uniProps.Lu);
+ target |= asSet(uniProps.Ll);
+ target |= asSet(uniProps.Lt);
+ target |= asSet(uniProps.Lo);
+ target |= asSet(uniProps.Lm);
+ }
+ else if (ucmp(name,"LC") == 0 || ucmp(name,"Cased Letter")==0)
+ {
+ target = asSet(uniProps.Ll);
+ target |= asSet(uniProps.Lu);
+ target |= asSet(uniProps.Lt);// Title case
+ }
+ else if (ucmp(name, "M") == 0 || ucmp(name, "Mark") == 0)
+ {
+ target = asSet(uniProps.Mn);
+ target |= asSet(uniProps.Mc);
+ target |= asSet(uniProps.Me);
+ }
+ else if (ucmp(name, "N") == 0 || ucmp(name, "Number") == 0)
+ {
+ target = asSet(uniProps.Nd);
+ target |= asSet(uniProps.Nl);
+ target |= asSet(uniProps.No);
+ }
+ else if (ucmp(name, "P") == 0 || ucmp(name, "Punctuation") == 0)
+ {
+ target = asSet(uniProps.Pc);
+ target |= asSet(uniProps.Pd);
+ target |= asSet(uniProps.Ps);
+ target |= asSet(uniProps.Pe);
+ target |= asSet(uniProps.Pi);
+ target |= asSet(uniProps.Pf);
+ target |= asSet(uniProps.Po);
+ }
+ else if (ucmp(name, "S") == 0 || ucmp(name, "Symbol") == 0)
+ {
+ target = asSet(uniProps.Sm);
+ target |= asSet(uniProps.Sc);
+ target |= asSet(uniProps.Sk);
+ target |= asSet(uniProps.So);
+ }
+ else if (ucmp(name, "Z") == 0 || ucmp(name, "Separator") == 0)
+ {
+ target = asSet(uniProps.Zs);
+ target |= asSet(uniProps.Zl);
+ target |= asSet(uniProps.Zp);
+ }
+ else if (ucmp(name, "C") == 0 || ucmp(name, "Other") == 0)
+ {
+ target = asSet(uniProps.Co);
+ target |= asSet(uniProps.Lo);
+ target |= asSet(uniProps.No);
+ target |= asSet(uniProps.So);
+ target |= asSet(uniProps.Po);
+ }
+ else if (ucmp(name, "graphical") == 0)
+ {
+ target = asSet(uniProps.Alphabetic);
+
+ target |= asSet(uniProps.Mn);
+ target |= asSet(uniProps.Mc);
+ target |= asSet(uniProps.Me);
+
+ target |= asSet(uniProps.Nd);
+ target |= asSet(uniProps.Nl);
+ target |= asSet(uniProps.No);
+
+ target |= asSet(uniProps.Pc);
+ target |= asSet(uniProps.Pd);
+ target |= asSet(uniProps.Ps);
+ target |= asSet(uniProps.Pe);
+ target |= asSet(uniProps.Pi);
+ target |= asSet(uniProps.Pf);
+ target |= asSet(uniProps.Po);
+
+ target |= asSet(uniProps.Zs);
+
+ target |= asSet(uniProps.Sm);
+ target |= asSet(uniProps.Sc);
+ target |= asSet(uniProps.Sk);
+ target |= asSet(uniProps.So);
+ }
+ else if (ucmp(name, "any") == 0)
+ target = Set.fromIntervals(0, 0x110000);
+ else if (ucmp(name, "ascii") == 0)
+ target = Set.fromIntervals(0, 0x80);
+ else
+ return loadUnicodeSet!(uniProps.tab)(name, target);
+ return true;
+}
+
+// CTFE-only helper for checking property names at compile-time
+@safe bool isPrettyPropertyName(C)(const scope C[] name)
+{
+ import std.algorithm.searching : find;
+ auto names = [
+ "L", "Letter",
+ "LC", "Cased Letter",
+ "M", "Mark",
+ "N", "Number",
+ "P", "Punctuation",
+ "S", "Symbol",
+ "Z", "Separator",
+ "Graphical",
+ "any",
+ "ascii"
+ ];
+ auto x = find!(x => comparePropertyName(x, name) == 0)(names);
+ return !x.empty;
+}
+
+// ditto, CTFE-only, not optimized
+@safe private static bool findSetName(alias table, C)(const scope C[] name)
+{
+ return findUnicodeSet!table(name) >= 0;
+}
+
+template SetSearcher(alias table, string kind)
+{
+ /// Run-time checked search.
+ static auto opCall(C)(const scope C[] name)
+ if (is(C : dchar))
+ {
+ import std.conv : to;
+ CodepointSet set;
+ if (loadUnicodeSet!table(name, set))
+ return set;
+ throw new Exception("No unicode set for "~kind~" by name "
+ ~name.to!string()~" was found.");
+ }
+ /// Compile-time checked search.
+ static @property auto opDispatch(string name)()
+ {
+ static if (findSetName!table(name))
+ {
+ CodepointSet set;
+ loadUnicodeSet!table(name, set);
+ return set;
+ }
+ else
+ static assert(false, "No unicode set for "~kind~" by name "
+ ~name~" was found.");
+ }
+}
+
+// Characters that need escaping in string posed as regular expressions
+package(std) alias Escapables = AliasSeq!('[', ']', '\\', '^', '$', '.', '|', '?', ',', '-',
+ ';', ':', '#', '&', '%', '/', '<', '>', '`', '*', '+', '(', ')', '{', '}', '~');
+
+package(std) CodepointSet memoizeExpr(string expr)()
+{
+ if (__ctfe)
+ return mixin(expr);
+ alias T = typeof(mixin(expr));
+ static T slot;
+ static bool initialized;
+ if (!initialized)
+ {
+ slot = mixin(expr);
+ initialized = true;
+ }
+ return slot;
+}
+
+//property for \w character class
+package(std) @property CodepointSet wordCharacter() @safe
+{
+ return memoizeExpr!("unicode.Alphabetic | unicode.Mn | unicode.Mc
+ | unicode.Me | unicode.Nd | unicode.Pc")();
+}
+
+//basic stack, just in case it gets used anywhere else then Parser
+package(std) struct Stack(T)
+{
+@safe:
+ T[] data;
+ @property bool empty(){ return data.empty; }
+
+ @property size_t length(){ return data.length; }
+
+ void push(T val){ data ~= val; }
+
+ @trusted T pop()
+ {
+ assert(!empty);
+ auto val = data[$ - 1];
+ data = data[0 .. $ - 1];
+ if (!__ctfe)
+ cast(void) data.assumeSafeAppend();
+ return val;
+ }
+
+ @property ref T top()
+ {
+ assert(!empty);
+ return data[$ - 1];
+ }
+}
+
+//test if a given string starts with hex number of maxDigit that's a valid codepoint
+//returns it's value and skips these maxDigit chars on success, throws on failure
+package(std) dchar parseUniHex(Range)(ref Range str, size_t maxDigit)
+{
+ import std.exception : enforce;
+ //std.conv.parse is both @system and bogus
+ uint val;
+ for (int k = 0; k < maxDigit; k++)
+ {
+ enforce(!str.empty, "incomplete escape sequence");
+ //accepts ascii only, so it's OK to index directly
+ immutable current = str.front;
+ if ('0' <= current && current <= '9')
+ val = val * 16 + current - '0';
+ else if ('a' <= current && current <= 'f')
+ val = val * 16 + current -'a' + 10;
+ else if ('A' <= current && current <= 'F')
+ val = val * 16 + current - 'A' + 10;
+ else
+ throw new Exception("invalid escape sequence");
+ str.popFront();
+ }
+ enforce(val <= 0x10FFFF, "invalid codepoint");
+ return val;
+}
+
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.exception : collectException;
+ string[] non_hex = [ "000j", "000z", "FffG", "0Z"];
+ string[] hex = [ "01", "ff", "00af", "10FFFF" ];
+ int[] value = [ 1, 0xFF, 0xAF, 0x10FFFF ];
+ foreach (v; non_hex)
+ assert(collectException(parseUniHex(v, v.length)).msg
+ .canFind("invalid escape sequence"));
+ foreach (i, v; hex)
+ assert(parseUniHex(v, v.length) == value[i]);
+ string over = "0011FFFF";
+ assert(collectException(parseUniHex(over, over.length)).msg
+ .canFind("invalid codepoint"));
+}
+
+auto caseEnclose(CodepointSet set)
+{
+ auto cased = set & unicode.LC;
+ foreach (dchar ch; cased.byCodepoint)
+ {
+ foreach (c; simpleCaseFoldings(ch))
+ set |= c;
+ }
+ return set;
+}
+
+/+
+ fetch codepoint set corresponding to a name (InBlock or binary property)
++/
+CodepointSet getUnicodeSet(const scope char[] name, bool negated, bool casefold) @safe
+{
+ CodepointSet s = unicode(name);
+ //FIXME: caseEnclose for new uni as Set | CaseEnclose(SET && LC)
+ if (casefold)
+ s = caseEnclose(s);
+ if (negated)
+ s = s.inverted;
+ return s;
+}
+
+struct UnicodeSetParser(Range)
+{
+ import std.exception : enforce;
+ import std.typecons : tuple, Tuple;
+ Range range;
+ bool casefold_;
+
+ @property bool empty(){ return range.empty; }
+ @property dchar front(){ return range.front; }
+ void popFront(){ range.popFront(); }
+
+ //CodepointSet operations relatively in order of priority
+ enum Operator:uint {
+ Open = 0, Negate, Difference, SymDifference, Intersection, Union, None
+ }
+
+ //parse unit of CodepointSet spec, most notably escape sequences and char ranges
+ //also fetches next set operation
+ Tuple!(CodepointSet,Operator) parseCharTerm()
+ {
+ import std.range : drop;
+ enum privateUseStart = '\U000F0000', privateUseEnd ='\U000FFFFD';
+ enum State{ Start, Char, Escape, CharDash, CharDashEscape,
+ PotentialTwinSymbolOperator }
+ Operator op = Operator.None;
+ dchar last;
+ CodepointSet set;
+ State state = State.Start;
+
+ void addWithFlags(ref CodepointSet set, uint ch)
+ {
+ if (casefold_)
+ {
+ auto range = simpleCaseFoldings(ch);
+ foreach (v; range)
+ set |= v;
+ }
+ else
+ set |= ch;
+ }
+
+ static Operator twinSymbolOperator(dchar symbol)
+ {
+ switch (symbol)
+ {
+ case '|':
+ return Operator.Union;
+ case '-':
+ return Operator.Difference;
+ case '~':
+ return Operator.SymDifference;
+ case '&':
+ return Operator.Intersection;
+ default:
+ assert(false);
+ }
+ }
+
+ L_CharTermLoop:
+ for (;;)
+ {
+ final switch (state)
+ {
+ case State.Start:
+ switch (front)
+ {
+ case '|':
+ case '-':
+ case '~':
+ case '&':
+ state = State.PotentialTwinSymbolOperator;
+ last = front;
+ break;
+ case '[':
+ op = Operator.Union;
+ goto case;
+ case ']':
+ break L_CharTermLoop;
+ case '\\':
+ state = State.Escape;
+ break;
+ default:
+ state = State.Char;
+ last = front;
+ }
+ break;
+ case State.Char:
+ // xxx last front xxx
+ switch (front)
+ {
+ case '|':
+ case '~':
+ case '&':
+ // then last is treated as normal char and added as implicit union
+ state = State.PotentialTwinSymbolOperator;
+ addWithFlags(set, last);
+ last = front;
+ break;
+ case '-': // still need more info
+ state = State.CharDash;
+ break;
+ case '\\':
+ set |= last;
+ state = State.Escape;
+ break;
+ case '[':
+ op = Operator.Union;
+ goto case;
+ case ']':
+ addWithFlags(set, last);
+ break L_CharTermLoop;
+ default:
+ state = State.Char;
+ addWithFlags(set, last);
+ last = front;
+ }
+ break;
+ case State.PotentialTwinSymbolOperator:
+ // xxx last front xxxx
+ // where last = [|-&~]
+ if (front == last)
+ {
+ op = twinSymbolOperator(last);
+ popFront();//skip second twin char
+ break L_CharTermLoop;
+ }
+ goto case State.Char;
+ case State.Escape:
+ // xxx \ front xxx
+ switch (front)
+ {
+ case 'f':
+ last = '\f';
+ state = State.Char;
+ break;
+ case 'n':
+ last = '\n';
+ state = State.Char;
+ break;
+ case 'r':
+ last = '\r';
+ state = State.Char;
+ break;
+ case 't':
+ last = '\t';
+ state = State.Char;
+ break;
+ case 'v':
+ last = '\v';
+ state = State.Char;
+ break;
+ case 'c':
+ last = unicode.parseControlCode(this);
+ state = State.Char;
+ break;
+ foreach (val; Escapables)
+ {
+ case val:
+ }
+ last = front;
+ state = State.Char;
+ break;
+ case 'p':
+ set.add(unicode.parsePropertySpec(this, false, casefold_));
+ state = State.Start;
+ continue L_CharTermLoop; //next char already fetched
+ case 'P':
+ set.add(unicode.parsePropertySpec(this, true, casefold_));
+ state = State.Start;
+ continue L_CharTermLoop; //next char already fetched
+ case 'x':
+ popFront();
+ last = parseUniHex(this, 2);
+ state = State.Char;
+ continue L_CharTermLoop;
+ case 'u':
+ popFront();
+ last = parseUniHex(this, 4);
+ state = State.Char;
+ continue L_CharTermLoop;
+ case 'U':
+ popFront();
+ last = parseUniHex(this, 8);
+ state = State.Char;
+ continue L_CharTermLoop;
+ case 'd':
+ set.add(unicode.Nd);
+ state = State.Start;
+ break;
+ case 'D':
+ set.add(unicode.Nd.inverted);
+ state = State.Start;
+ break;
+ case 's':
+ set.add(unicode.White_Space);
+ state = State.Start;
+ break;
+ case 'S':
+ set.add(unicode.White_Space.inverted);
+ state = State.Start;
+ break;
+ case 'w':
+ set.add(wordCharacter);
+ state = State.Start;
+ break;
+ case 'W':
+ set.add(wordCharacter.inverted);
+ state = State.Start;
+ break;
+ default:
+ if (front >= privateUseStart && front <= privateUseEnd)
+ enforce(false, "no matching ']' found while parsing character class");
+ enforce(false, "invalid escape sequence");
+ }
+ break;
+ case State.CharDash:
+ // xxx last - front xxx
+ switch (front)
+ {
+ case '[':
+ op = Operator.Union;
+ goto case;
+ case ']':
+ //means dash is a single char not an interval specifier
+ addWithFlags(set, last);
+ addWithFlags(set, '-');
+ break L_CharTermLoop;
+ case '-'://set Difference again
+ addWithFlags(set, last);
+ op = Operator.Difference;
+ popFront();//skip '-'
+ break L_CharTermLoop;
+ case '\\':
+ state = State.CharDashEscape;
+ break;
+ default:
+ enforce(last <= front, "inverted range");
+ if (casefold_)
+ {
+ for (uint ch = last; ch <= front; ch++)
+ addWithFlags(set, ch);
+ }
+ else
+ set.add(last, front + 1);
+ state = State.Start;
+ }
+ break;
+ case State.CharDashEscape:
+ //xxx last - \ front xxx
+ uint end;
+ switch (front)
+ {
+ case 'f':
+ end = '\f';
+ break;
+ case 'n':
+ end = '\n';
+ break;
+ case 'r':
+ end = '\r';
+ break;
+ case 't':
+ end = '\t';
+ break;
+ case 'v':
+ end = '\v';
+ break;
+ foreach (val; Escapables)
+ {
+ case val:
+ }
+ end = front;
+ break;
+ case 'c':
+ end = unicode.parseControlCode(this);
+ break;
+ case 'x':
+ popFront();
+ end = parseUniHex(this, 2);
+ enforce(last <= end,"inverted range");
+ set.add(last, end + 1);
+ state = State.Start;
+ continue L_CharTermLoop;
+ case 'u':
+ popFront();
+ end = parseUniHex(this, 4);
+ enforce(last <= end,"inverted range");
+ set.add(last, end + 1);
+ state = State.Start;
+ continue L_CharTermLoop;
+ case 'U':
+ popFront();
+ end = parseUniHex(this, 8);
+ enforce(last <= end,"inverted range");
+ set.add(last, end + 1);
+ state = State.Start;
+ continue L_CharTermLoop;
+ default:
+ if (front >= privateUseStart && front <= privateUseEnd)
+ enforce(false, "no matching ']' found while parsing character class");
+ enforce(false, "invalid escape sequence");
+ }
+ // Lookahead to check if it's a \T
+ // where T is sub-pattern terminator in multi-pattern scheme
+ auto lookahead = range.save.drop(1);
+ if (end == '\\' && !lookahead.empty)
+ {
+ if (lookahead.front >= privateUseStart && lookahead.front <= privateUseEnd)
+ enforce(false, "no matching ']' found while parsing character class");
+ }
+ enforce(last <= end,"inverted range");
+ set.add(last, end + 1);
+ state = State.Start;
+ break;
+ }
+ popFront();
+ enforce(!empty, "unexpected end of CodepointSet");
+ }
+ return tuple(set, op);
+ }
+
+ alias ValStack = Stack!(CodepointSet);
+ alias OpStack = Stack!(Operator);
+
+ CodepointSet parseSet()
+ {
+ ValStack vstack;
+ OpStack opstack;
+ import std.functional : unaryFun;
+ enforce(!empty, "unexpected end of input");
+ enforce(front == '[', "expected '[' at the start of unicode set");
+ //
+ static bool apply(Operator op, ref ValStack stack)
+ {
+ switch (op)
+ {
+ case Operator.Negate:
+ enforce(!stack.empty, "no operand for '^'");
+ stack.top = stack.top.inverted;
+ break;
+ case Operator.Union:
+ auto s = stack.pop();//2nd operand
+ enforce(!stack.empty, "no operand for '||'");
+ stack.top.add(s);
+ break;
+ case Operator.Difference:
+ auto s = stack.pop();//2nd operand
+ enforce(!stack.empty, "no operand for '--'");
+ stack.top.sub(s);
+ break;
+ case Operator.SymDifference:
+ auto s = stack.pop();//2nd operand
+ enforce(!stack.empty, "no operand for '~~'");
+ stack.top ~= s;
+ break;
+ case Operator.Intersection:
+ auto s = stack.pop();//2nd operand
+ enforce(!stack.empty, "no operand for '&&'");
+ stack.top.intersect(s);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ static bool unrollWhile(alias cond)(ref ValStack vstack, ref OpStack opstack)
+ {
+ while (cond(opstack.top))
+ {
+ if (!apply(opstack.pop(),vstack))
+ return false;//syntax error
+ if (opstack.empty)
+ return false;
+ }
+ return true;
+ }
+
+ L_CharsetLoop:
+ do
+ {
+ switch (front)
+ {
+ case '[':
+ opstack.push(Operator.Open);
+ popFront();
+ enforce(!empty, "unexpected end of character class");
+ if (front == '^')
+ {
+ opstack.push(Operator.Negate);
+ popFront();
+ enforce(!empty, "unexpected end of character class");
+ }
+ else if (front == ']') // []...] is special cased
+ {
+ popFront();
+ enforce(!empty, "wrong character set");
+ auto pair = parseCharTerm();
+ pair[0].add(']', ']'+1);
+ if (pair[1] != Operator.None)
+ {
+ if (opstack.top == Operator.Union)
+ unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack);
+ opstack.push(pair[1]);
+ }
+ vstack.push(pair[0]);
+ }
+ break;
+ case ']':
+ enforce(unrollWhile!(unaryFun!"a != a.Open")(vstack, opstack),
+ "character class syntax error");
+ enforce(!opstack.empty, "unmatched ']'");
+ opstack.pop();
+ popFront();
+ if (opstack.empty)
+ break L_CharsetLoop;
+ auto pair = parseCharTerm();
+ if (!pair[0].empty)//not only operator e.g. -- or ~~
+ {
+ vstack.top.add(pair[0]);//apply union
+ }
+ if (pair[1] != Operator.None)
+ {
+ if (opstack.top == Operator.Union)
+ unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack);
+ opstack.push(pair[1]);
+ }
+ break;
+ //
+ default://yet another pair of term(op)?
+ auto pair = parseCharTerm();
+ if (pair[1] != Operator.None)
+ {
+ if (opstack.top == Operator.Union)
+ unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack);
+ opstack.push(pair[1]);
+ }
+ vstack.push(pair[0]);
+ }
+
+ }while (!empty || !opstack.empty);
+ while (!opstack.empty)
+ apply(opstack.pop(),vstack);
+ assert(vstack.length == 1);
+ return vstack.top;
+ }
+}
+
+/**
+ A single entry point to lookup Unicode $(CODEPOINT) sets by name or alias of
+ a block, script or general category.
+
+ It uses well defined standard rules of property name lookup.
+ This includes fuzzy matching of names, so that
+ 'White_Space', 'white-SpAce' and 'whitespace' are all considered equal
+ and yield the same set of white space $(CHARACTERS).
+*/
+@safe public struct unicode
+{
+ import std.exception : enforce;
+ /**
+ Performs the lookup of set of $(CODEPOINTS)
+ with compile-time correctness checking.
+ This short-cut version combines 3 searches:
+ across blocks, scripts, and common binary properties.
+
+ Note that since scripts and blocks overlap the
+ usual trick to disambiguate is used - to get a block use
+ `unicode.InBlockName`, to search a script
+ use `unicode.ScriptName`.
+
+ See_Also: $(LREF block), $(LREF script)
+ and (not included in this search) $(LREF hangulSyllableType).
+ */
+
+ static @property auto opDispatch(string name)() pure
+ {
+ static if (findAny(name))
+ return loadAny(name);
+ else
+ static assert(false, "No unicode set by name "~name~" was found.");
+ }
+
+ ///
+ @safe unittest
+ {
+ import std.exception : collectException;
+ auto ascii = unicode.ASCII;
+ assert(ascii['A']);
+ assert(ascii['~']);
+ assert(!ascii['\u00e0']);
+ // matching is case-insensitive
+ assert(ascii == unicode.ascII);
+ assert(!ascii['à']);
+ // underscores, '-' and whitespace in names are ignored too
+ auto latin = unicode.in_latin1_Supplement;
+ assert(latin['à']);
+ assert(!latin['$']);
+ // BTW Latin 1 Supplement is a block, hence "In" prefix
+ assert(latin == unicode("In Latin 1 Supplement"));
+ // run-time look up throws if no such set is found
+ assert(collectException(unicode("InCyrilliac")));
+ }
+
+ /**
+ The same lookup across blocks, scripts, or binary properties,
+ but performed at run-time.
+ This version is provided for cases where `name`
+ is not known beforehand; otherwise compile-time
+ checked $(LREF opDispatch) is typically a better choice.
+
+ See the $(S_LINK Unicode properties, table of properties) for available
+ sets.
+ */
+ static auto opCall(C)(const scope C[] name)
+ if (is(C : dchar))
+ {
+ return loadAny(name);
+ }
+
+ /**
+ Narrows down the search for sets of $(CODEPOINTS) to all Unicode blocks.
+
+ Note:
+ Here block names are unambiguous as no scripts are searched
+ and thus to search use simply `unicode.block.BlockName` notation.
+
+ See $(S_LINK Unicode properties, table of properties) for available sets.
+ See_Also: $(S_LINK Unicode properties, table of properties).
+ */
+ struct block
+ {
+ import std.internal.unicode_tables : blocks; // generated file
+ mixin SetSearcher!(blocks.tab, "block");
+ }
+
+ ///
+ @safe unittest
+ {
+ // use .block for explicitness
+ assert(unicode.block.Greek_and_Coptic == unicode.InGreek_and_Coptic);
+ }
+
+ /**
+ Narrows down the search for sets of $(CODEPOINTS) to all Unicode scripts.
+
+ See the $(S_LINK Unicode properties, table of properties) for available
+ sets.
+ */
+ struct script
+ {
+ import std.internal.unicode_tables : scripts; // generated file
+ mixin SetSearcher!(scripts.tab, "script");
+ }
+
+ ///
+ @safe unittest
+ {
+ auto arabicScript = unicode.script.arabic;
+ auto arabicBlock = unicode.block.arabic;
+ // there is an intersection between script and block
+ assert(arabicBlock['Ø']);
+ assert(arabicScript['Ø']);
+ // but they are different
+ assert(arabicBlock != arabicScript);
+ assert(arabicBlock == unicode.inArabic);
+ assert(arabicScript == unicode.arabic);
+ }
+
+ /**
+ Fetch a set of $(CODEPOINTS) that have the given hangul syllable type.
+
+ Other non-binary properties (once supported) follow the same
+ notation - `unicode.propertyName.propertyValue` for compile-time
+ checked access and `unicode.propertyName(propertyValue)`
+ for run-time checked one.
+
+ See the $(S_LINK Unicode properties, table of properties) for available
+ sets.
+ */
+ struct hangulSyllableType
+ {
+ import std.internal.unicode_tables : hangul; // generated file
+ mixin SetSearcher!(hangul.tab, "hangul syllable type");
+ }
+
+ ///
+ @safe unittest
+ {
+ // L here is syllable type not Letter as in unicode.L short-cut
+ auto leadingVowel = unicode.hangulSyllableType("L");
+ // check that some leading vowels are present
+ foreach (vowel; '\u1110'..'\u115F')
+ assert(leadingVowel[vowel]);
+ assert(leadingVowel == unicode.hangulSyllableType.L);
+ }
+
+ //parse control code of form \cXXX, c assumed to be the current symbol
+ static package(std) dchar parseControlCode(Parser)(ref Parser p)
+ {
+ with(p)
+ {
+ popFront();
+ enforce(!empty, "Unfinished escape sequence");
+ enforce(('a' <= front && front <= 'z')
+ || ('A' <= front && front <= 'Z'),
+ "Only letters are allowed after \\c");
+ return front & 0x1f;
+ }
+ }
+
+ //parse and return a CodepointSet for \p{...Property...} and \P{...Property..},
+ //\ - assumed to be processed, p - is current
+ static package(std) CodepointSet parsePropertySpec(Range)(ref Range p,
+ bool negated, bool casefold)
+ {
+ static import std.ascii;
+ with(p)
+ {
+ enum MAX_PROPERTY = 128;
+ char[MAX_PROPERTY] result;
+ uint k = 0;
+ popFront();
+ enforce(!empty, "eof parsing unicode property spec");
+ if (front == '{')
+ {
+ popFront();
+ while (k < MAX_PROPERTY && !empty && front !='}'
+ && front !=':')
+ {
+ if (front != '-' && front != ' ' && front != '_')
+ result[k++] = cast(char) std.ascii.toLower(front);
+ popFront();
+ }
+ enforce(k != MAX_PROPERTY, "invalid property name");
+ enforce(front == '}', "} expected ");
+ }
+ else
+ {//single char properties e.g.: \pL, \pN ...
+ enforce(front < 0x80, "invalid property name");
+ result[k++] = cast(char) front;
+ }
+ auto s = getUnicodeSet(result[0 .. k], negated, casefold);
+ enforce(!s.empty, "unrecognized unicode property spec");
+ popFront();
+ return s;
+ }
+ }
+
+ /**
+ Parse unicode codepoint set from given `range` using standard regex
+ syntax '[...]'. The range is advanced skiping over regex set definition.
+ `casefold` parameter determines if the set should be casefolded - that is
+ include both lower and upper case versions for any letters in the set.
+ */
+ static CodepointSet parseSet(Range)(ref Range range, bool casefold=false)
+ if (isInputRange!Range && is(ElementType!Range : dchar))
+ {
+ auto usParser = UnicodeSetParser!Range(range, casefold);
+ auto set = usParser.parseSet();
+ range = usParser.range;
+ return set;
+ }
+
+ ///
+ @safe unittest
+ {
+ import std.uni : unicode;
+ string pat = "[a-zA-Z0-9]hello";
+ auto set = unicode.parseSet(pat);
+ // check some of the codepoints
+ assert(set['a'] && set['A'] && set['9']);
+ assert(pat == "hello");
+ }
+
+private:
+ alias ucmp = comparePropertyName;
+
+ static bool findAny(string name)
+ {
+ import std.internal.unicode_tables : blocks, scripts, uniProps; // generated file
+ return isPrettyPropertyName(name)
+ || findSetName!(uniProps.tab)(name) || findSetName!(scripts.tab)(name)
+ || (ucmp(name[0 .. 2],"In") == 0 && findSetName!(blocks.tab)(name[2..$]));
+ }
+
+ static auto loadAny(Set=CodepointSet, C)(const scope C[] name) pure
+ {
+ import std.conv : to;
+ import std.internal.unicode_tables : blocks, scripts; // generated file
+ Set set;
+ immutable loaded = loadProperty(name, set) || loadUnicodeSet!(scripts.tab)(name, set)
+ || (name.length > 2 && ucmp(name[0 .. 2],"In") == 0
+ && loadUnicodeSet!(blocks.tab)(name[2..$], set));
+ if (loaded)
+ return set;
+ throw new Exception("No unicode set by name "~name.to!string()~" was found.");
+ }
+
+ // FIXME: re-disable once the compiler is fixed
+ // Disabled to prevent the mistake of creating instances of this pseudo-struct.
+ //@disable ~this();
+}
+
+@safe unittest
+{
+ import std.internal.unicode_tables : blocks, uniProps; // generated file
+ assert(unicode("InHebrew") == asSet(blocks.Hebrew));
+ assert(unicode("separator") == (asSet(uniProps.Zs) | asSet(uniProps.Zl) | asSet(uniProps.Zp)));
+ assert(unicode("In-Kharoshthi") == asSet(blocks.Kharoshthi));
+}
+
+enum EMPTY_CASE_TRIE = ushort.max;// from what gen_uni uses internally
+
+// control - '\r'
+enum controlSwitch = `
+ case '\u0000':..case '\u0008':case '\u000E':..case '\u001F':case '\u007F':..
+ case '\u0084':case '\u0086':..case '\u009F': case '\u0009':..case '\u000C': case '\u0085':
+`;
+// TODO: redo the most of hangul stuff algorithmically in case of Graphemes too
+// kill unrolled switches
+
+private static bool isRegionalIndicator(dchar ch) @safe pure @nogc nothrow
+{
+ return ch >= '\U0001F1E6' && ch <= '\U0001F1FF';
+}
+
+template genericDecodeGrapheme(bool getValue)
+{
+ alias graphemeExtend = graphemeExtendTrie;
+ alias spacingMark = mcTrie;
+ static if (getValue)
+ alias Value = Grapheme;
+ else
+ alias Value = void;
+
+ Value genericDecodeGrapheme(Input)(ref Input range)
+ {
+ import std.internal.unicode_tables : isHangL, isHangT, isHangV; // generated file
+ enum GraphemeState {
+ Start,
+ CR,
+ RI,
+ L,
+ V,
+ LVT
+ }
+ static if (getValue)
+ Grapheme grapheme;
+ auto state = GraphemeState.Start;
+ enum eat = q{
+ static if (getValue)
+ grapheme ~= ch;
+ range.popFront();
+ };
+
+ dchar ch;
+ assert(!range.empty, "Attempting to decode grapheme from an empty " ~ Input.stringof);
+ while (!range.empty)
+ {
+ ch = range.front;
+ final switch (state) with(GraphemeState)
+ {
+ case Start:
+ mixin(eat);
+ if (ch == '\r')
+ state = CR;
+ else if (isRegionalIndicator(ch))
+ state = RI;
+ else if (isHangL(ch))
+ state = L;
+ else if (hangLV[ch] || isHangV(ch))
+ state = V;
+ else if (hangLVT[ch])
+ state = LVT;
+ else if (isHangT(ch))
+ state = LVT;
+ else
+ {
+ switch (ch)
+ {
+ mixin(controlSwitch);
+ goto L_End;
+ default:
+ goto L_End_Extend;
+ }
+ }
+ break;
+ case CR:
+ if (ch == '\n')
+ mixin(eat);
+ goto L_End_Extend;
+ case RI:
+ if (isRegionalIndicator(ch))
+ mixin(eat);
+ else
+ goto L_End_Extend;
+ break;
+ case L:
+ if (isHangL(ch))
+ mixin(eat);
+ else if (isHangV(ch) || hangLV[ch])
+ {
+ state = V;
+ mixin(eat);
+ }
+ else if (hangLVT[ch])
+ {
+ state = LVT;
+ mixin(eat);
+ }
+ else
+ goto L_End_Extend;
+ break;
+ case V:
+ if (isHangV(ch))
+ mixin(eat);
+ else if (isHangT(ch))
+ {
+ state = LVT;
+ mixin(eat);
+ }
+ else
+ goto L_End_Extend;
+ break;
+ case LVT:
+ if (isHangT(ch))
+ {
+ mixin(eat);
+ }
+ else
+ goto L_End_Extend;
+ break;
+ }
+ }
+ L_End_Extend:
+ while (!range.empty)
+ {
+ ch = range.front;
+ // extend & spacing marks
+ if (!graphemeExtend[ch] && !spacingMark[ch])
+ break;
+ mixin(eat);
+ }
+ L_End:
+ static if (getValue)
+ return grapheme;
+ }
+
+}
+
+public: // Public API continues
+
+/++
+ Computes the length of grapheme cluster starting at `index`.
+ Both the resulting length and the `index` are measured
+ in $(S_LINK Code unit, code units).
+
+ Params:
+ C = type that is implicitly convertible to `dchars`
+ input = array of grapheme clusters
+ index = starting index into `input[]`
+
+ Returns:
+ length of grapheme cluster
++/
+size_t graphemeStride(C)(const scope C[] input, size_t index) @safe pure
+if (is(C : dchar))
+{
+ auto src = input[index..$];
+ auto n = src.length;
+ genericDecodeGrapheme!(false)(src);
+ return n - src.length;
+}
+
+///
+@safe unittest
+{
+ assert(graphemeStride(" ", 1) == 1);
+ // A + combing ring above
+ string city = "A\u030Arhus";
+ size_t first = graphemeStride(city, 0);
+ assert(first == 3); //\u030A has 2 UTF-8 code units
+ assert(city[0 .. first] == "A\u030A");
+ assert(city[first..$] == "rhus");
+}
+
+@safe unittest
+{
+ // Ensure that graphemeStride is usable from CTFE.
+ enum c1 = graphemeStride("A", 0);
+ static assert(c1 == 1);
+
+ enum c2 = graphemeStride("A\u0301", 0);
+ static assert(c2 == 3); // \u0301 has 2 UTF-8 code units
+}
+
+/++
+ Reads one full grapheme cluster from an
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of dchar `inp`.
+
+ For examples see the $(LREF Grapheme) below.
+
+ Note:
+ This function modifies `inp` and thus `inp`
+ must be an L-value.
++/
+Grapheme decodeGrapheme(Input)(ref Input inp)
+if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar))
+{
+ return genericDecodeGrapheme!true(inp);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ Grapheme gr;
+ string s = " \u0020\u0308 ";
+ gr = decodeGrapheme(s);
+ assert(gr.length == 1 && gr[0] == ' ');
+ gr = decodeGrapheme(s);
+ assert(gr.length == 2 && equal(gr[0 .. 2], " \u0308"));
+ s = "\u0300\u0308\u1100";
+ assert(equal(decodeGrapheme(s)[], "\u0300\u0308"));
+ assert(equal(decodeGrapheme(s)[], "\u1100"));
+ s = "\u11A8\u0308\uAC01";
+ assert(equal(decodeGrapheme(s)[], "\u11A8\u0308"));
+ assert(equal(decodeGrapheme(s)[], "\uAC01"));
+}
+
+/++
+ $(P Iterate a string by $(LREF Grapheme).)
+
+ $(P Useful for doing string manipulation that needs to be aware
+ of graphemes.)
+
+ See_Also:
+ $(LREF byCodePoint)
++/
+auto byGrapheme(Range)(Range range)
+if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar))
+{
+ // TODO: Bidirectional access
+ static struct Result(R)
+ {
+ private R _range;
+ private Grapheme _front;
+
+ bool empty() @property
+ {
+ return _front.length == 0;
+ }
+
+ Grapheme front() @property
+ {
+ return _front;
+ }
+
+ void popFront()
+ {
+ _front = _range.empty ? Grapheme.init : _range.decodeGrapheme();
+ }
+
+ static if (isForwardRange!R)
+ {
+ Result save() @property
+ {
+ return Result(_range.save, _front);
+ }
+ }
+ }
+
+ auto result = Result!(Range)(range);
+ result.popFront();
+ return result;
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : walkLength;
+ import std.range : take, drop;
+ auto text = "noe\u0308l"; // noël using e + combining diaeresis
+ assert(text.walkLength == 5); // 5 code points
+
+ auto gText = text.byGrapheme;
+ assert(gText.walkLength == 4); // 4 graphemes
+
+ assert(gText.take(3).equal("noe\u0308".byGrapheme));
+ assert(gText.drop(3).equal("l".byGrapheme));
+}
+
+// For testing non-forward-range input ranges
+version (StdUnittest)
+private static struct InputRangeString
+{
+ private string s;
+
+ bool empty() @property { return s.empty; }
+ dchar front() @property { return s.front; }
+ void popFront() { s.popFront(); }
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.array : array;
+ import std.range : retro;
+ import std.range.primitives : walkLength;
+ assert("".byGrapheme.walkLength == 0);
+
+ auto reverse = "le\u0308on";
+ assert(reverse.walkLength == 5);
+
+ auto gReverse = reverse.byGrapheme;
+ assert(gReverse.walkLength == 4);
+
+ static foreach (text; AliasSeq!("noe\u0308l"c, "noe\u0308l"w, "noe\u0308l"d))
+ {{
+ assert(text.walkLength == 5);
+ static assert(isForwardRange!(typeof(text)));
+
+ auto gText = text.byGrapheme;
+ static assert(isForwardRange!(typeof(gText)));
+ assert(gText.walkLength == 4);
+ assert(gText.array.retro.equal(gReverse));
+ }}
+
+ auto nonForwardRange = InputRangeString("noe\u0308l").byGrapheme;
+ static assert(!isForwardRange!(typeof(nonForwardRange)));
+ assert(nonForwardRange.walkLength == 4);
+}
+
+/++
+ $(P Lazily transform a range of $(LREF Grapheme)s to a range of code points.)
+
+ $(P Useful for converting the result to a string after doing operations
+ on graphemes.)
+
+ $(P If passed in a range of code points, returns a range with equivalent capabilities.)
++/
+auto byCodePoint(Range)(Range range)
+if (isInputRange!Range && is(immutable ElementType!Range == immutable Grapheme))
+{
+ // TODO: Propagate bidirectional access
+ static struct Result
+ {
+ private Range _range;
+ private size_t i = 0;
+
+ bool empty() @property
+ {
+ return _range.empty;
+ }
+
+ dchar front() @property
+ {
+ return _range.front[i];
+ }
+
+ void popFront()
+ {
+ ++i;
+
+ if (i >= _range.front.length)
+ {
+ _range.popFront();
+ i = 0;
+ }
+ }
+
+ static if (isForwardRange!Range)
+ {
+ Result save() @property
+ {
+ return Result(_range.save, i);
+ }
+ }
+ }
+
+ return Result(range);
+}
+
+/// Ditto
+auto byCodePoint(Range)(Range range)
+if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar))
+{
+ import std.range.primitives : isBidirectionalRange, popBack;
+ import std.traits : isNarrowString;
+ static if (isNarrowString!Range)
+ {
+ static struct Result
+ {
+ private Range _range;
+ @property bool empty() { return _range.empty; }
+ @property dchar front(){ return _range.front; }
+ void popFront(){ _range.popFront; }
+ @property auto save() { return Result(_range.save); }
+ @property dchar back(){ return _range.back; }
+ void popBack(){ _range.popBack; }
+ }
+ static assert(isBidirectionalRange!(Result));
+ return Result(range);
+ }
+ else
+ return range;
+}
+
+///
+@safe unittest
+{
+ import std.array : array;
+ import std.conv : text;
+ import std.range : retro;
+
+ string s = "noe\u0308l"; // noël
+
+ // reverse it and convert the result to a string
+ string reverse = s.byGrapheme
+ .array
+ .retro
+ .byCodePoint
+ .text;
+
+ assert(reverse == "le\u0308on"); // lëon
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : walkLength;
+ import std.range : retro;
+ assert("".byGrapheme.byCodePoint.equal(""));
+
+ string text = "noe\u0308l";
+ static assert(!__traits(compiles, "noe\u0308l".byCodePoint.length));
+
+ auto gText = InputRangeString(text).byGrapheme;
+ static assert(!isForwardRange!(typeof(gText)));
+
+ auto cpText = gText.byCodePoint;
+ static assert(!isForwardRange!(typeof(cpText)));
+
+ assert(cpText.walkLength == text.walkLength);
+
+ auto plainCp = text.byCodePoint;
+ static assert(isForwardRange!(typeof(plainCp)));
+ assert(equal(plainCp, text));
+ assert(equal(retro(plainCp.save), retro(text.save)));
+ // Check that we still have length for dstring
+ assert("абвгд"d.byCodePoint.length == 5);
+}
+
+/++
+ $(P A structure designed to effectively pack $(CHARACTERS)
+ of a $(CLUSTER).
+ )
+
+ $(P `Grapheme` has value semantics so 2 copies of a `Grapheme`
+ always refer to distinct objects. In most actual scenarios a `Grapheme`
+ fits on the stack and avoids memory allocation overhead for all but quite
+ long clusters.
+ )
+
+ See_Also: $(LREF decodeGrapheme), $(LREF graphemeStride)
++/
+@safe struct Grapheme
+{
+ import std.exception : enforce;
+ import std.traits : isDynamicArray;
+
+public:
+ /// Ctor
+ this(C)(const scope C[] chars...)
+ if (is(C : dchar))
+ {
+ this ~= chars;
+ }
+
+ ///ditto
+ this(Input)(Input seq)
+ if (!isDynamicArray!Input
+ && isInputRange!Input && is(ElementType!Input : dchar))
+ {
+ this ~= seq;
+ }
+
+ /// Gets a $(CODEPOINT) at the given index in this cluster.
+ dchar opIndex(size_t index) const @nogc nothrow pure @trusted
+ {
+ assert(index < length);
+ return read24(isBig ? ptr_ : small_.ptr, index);
+ }
+
+ /++
+ Writes a $(CODEPOINT) `ch` at given index in this cluster.
+
+ Warning:
+ Use of this facility may invalidate grapheme cluster,
+ see also $(LREF Grapheme.valid).
+ +/
+ void opIndexAssign(dchar ch, size_t index) @nogc nothrow pure @trusted
+ {
+ assert(index < length);
+ write24(isBig ? ptr_ : small_.ptr, ch, index);
+ }
+
+ ///
+ @safe unittest
+ {
+ auto g = Grapheme("A\u0302");
+ assert(g[0] == 'A');
+ assert(g.valid);
+ g[1] = '~'; // ASCII tilda is not a combining mark
+ assert(g[1] == '~');
+ assert(!g.valid);
+ }
+
+ /++
+ Random-access range over Grapheme's $(CHARACTERS).
+
+ Warning: Invalidates when this Grapheme leaves the scope,
+ attempts to use it then would lead to memory corruption.
+ +/
+ SliceOverIndexed!Grapheme opSlice(size_t a, size_t b) @nogc nothrow pure return
+ {
+ return sliceOverIndexed(a, b, &this);
+ }
+
+ /// ditto
+ SliceOverIndexed!Grapheme opSlice() @nogc nothrow pure return
+ {
+ return sliceOverIndexed(0, length, &this);
+ }
+
+ /// Grapheme cluster length in $(CODEPOINTS).
+ @property size_t length() const @nogc nothrow pure
+ {
+ return isBig ? len_ : slen_ & 0x7F;
+ }
+
+ /++
+ Append $(CHARACTER) `ch` to this grapheme.
+ Warning:
+ Use of this facility may invalidate grapheme cluster,
+ see also `valid`.
+
+ See_Also: $(LREF Grapheme.valid)
+ +/
+ ref opOpAssign(string op)(dchar ch) @trusted
+ {
+ static if (op == "~")
+ {
+ import std.internal.memory : enforceRealloc;
+ if (!isBig)
+ {
+ if (slen_ == small_cap)
+ convertToBig();// & fallthrough to "big" branch
+ else
+ {
+ write24(small_.ptr, ch, smallLength);
+ slen_++;
+ return this;
+ }
+ }
+
+ assert(isBig);
+ if (len_ == cap_)
+ {
+ import core.checkedint : addu, mulu;
+ bool overflow;
+ cap_ = addu(cap_, grow, overflow);
+ auto nelems = mulu(3, addu(cap_, 1, overflow), overflow);
+ if (overflow) assert(0);
+ ptr_ = cast(ubyte*) enforceRealloc(ptr_, nelems);
+ }
+ write24(ptr_, ch, len_++);
+ return this;
+ }
+ else
+ static assert(false, "No operation "~op~" defined for Grapheme");
+ }
+
+ ///
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+ auto g = Grapheme("A");
+ assert(g.valid);
+ g ~= '\u0301';
+ assert(g[].equal("A\u0301"));
+ assert(g.valid);
+ g ~= "B";
+ // not a valid grapheme cluster anymore
+ assert(!g.valid);
+ // still could be useful though
+ assert(g[].equal("A\u0301B"));
+ }
+
+ /// Append all $(CHARACTERS) from the input range `inp` to this Grapheme.
+ ref opOpAssign(string op, Input)(scope Input inp)
+ if (isInputRange!Input && is(ElementType!Input : dchar))
+ {
+ static if (op == "~")
+ {
+ foreach (dchar ch; inp)
+ this ~= ch;
+ return this;
+ }
+ else
+ static assert(false, "No operation "~op~" defined for Grapheme");
+ }
+
+ /++
+ True if this object contains valid extended grapheme cluster.
+ Decoding primitives of this module always return a valid `Grapheme`.
+
+ Appending to and direct manipulation of grapheme's $(CHARACTERS) may
+ render it no longer valid. Certain applications may chose to use
+ Grapheme as a "small string" of any $(CODEPOINTS) and ignore this property
+ entirely.
+ +/
+ @property bool valid()() /*const*/
+ {
+ auto r = this[];
+ genericDecodeGrapheme!false(r);
+ return r.length == 0;
+ }
+
+ this(this) @nogc nothrow pure @trusted
+ {
+ import std.internal.memory : enforceMalloc;
+ if (isBig)
+ {// dup it
+ import core.checkedint : addu, mulu;
+ bool overflow;
+ auto raw_cap = mulu(3, addu(cap_, 1, overflow), overflow);
+ if (overflow) assert(0);
+
+ auto p = cast(ubyte*) enforceMalloc(raw_cap);
+ p[0 .. raw_cap] = ptr_[0 .. raw_cap];
+ ptr_ = p;
+ }
+ }
+
+ ~this() @nogc nothrow pure @trusted
+ {
+ import core.memory : pureFree;
+ if (isBig)
+ {
+ pureFree(ptr_);
+ }
+ }
+
+
+private:
+ enum small_bytes = ((ubyte*).sizeof+3*size_t.sizeof-1);
+ // "out of the blue" grow rate, needs testing
+ // (though graphemes are typically small < 9)
+ enum grow = 20;
+ enum small_cap = small_bytes/3;
+ enum small_flag = 0x80, small_mask = 0x7F;
+ // 16 bytes in 32bits, should be enough for the majority of cases
+ union
+ {
+ struct
+ {
+ ubyte* ptr_;
+ size_t cap_;
+ size_t len_;
+ size_t padding_;
+ }
+ struct
+ {
+ ubyte[small_bytes] small_;
+ ubyte slen_;
+ }
+ }
+
+ void convertToBig() @nogc nothrow pure @trusted
+ {
+ import std.internal.memory : enforceMalloc;
+ static assert(grow.max / 3 - 1 >= grow);
+ enum nbytes = 3 * (grow + 1);
+ size_t k = smallLength;
+ ubyte* p = cast(ubyte*) enforceMalloc(nbytes);
+ for (int i=0; i<k; i++)
+ write24(p, read24(small_.ptr, i), i);
+ // now we can overwrite small array data
+ ptr_ = p;
+ len_ = slen_;
+ assert(grow > len_);
+ cap_ = grow;
+ setBig();
+ }
+
+ void setBig() @nogc nothrow pure { slen_ |= small_flag; }
+
+ @property size_t smallLength() const @nogc nothrow pure
+ {
+ return slen_ & small_mask;
+ }
+ @property ubyte isBig() const @nogc nothrow pure
+ {
+ return slen_ & small_flag;
+ }
+}
+
+static assert(Grapheme.sizeof == size_t.sizeof*4);
+
+
+@system pure /*nothrow @nogc*/ unittest // TODO: string .front is GC and throw
+{
+ import std.algorithm.comparison : equal;
+ Grapheme[3] data = [Grapheme("Ю"), Grapheme("У"), Grapheme("З")];
+ assert(byGrapheme("ЮУЗ").equal(data[]));
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+ import std.range : isRandomAccessRange;
+
+ string bold = "ku\u0308hn";
+
+ // note that decodeGrapheme takes parameter by ref
+ auto first = decodeGrapheme(bold);
+
+ assert(first.length == 1);
+ assert(first[0] == 'k');
+
+ // the next grapheme is 2 characters long
+ auto wideOne = decodeGrapheme(bold);
+ // slicing a grapheme yields a random-access range of dchar
+ assert(wideOne[].equal("u\u0308"));
+ assert(wideOne.length == 2);
+ static assert(isRandomAccessRange!(typeof(wideOne[])));
+
+ // all of the usual range manipulation is possible
+ assert(wideOne[].filter!isMark().equal("\u0308"));
+
+ auto g = Grapheme("A");
+ assert(g.valid);
+ g ~= '\u0301';
+ assert(g[].equal("A\u0301"));
+ assert(g.valid);
+ g ~= "B";
+ // not a valid grapheme cluster anymore
+ assert(!g.valid);
+ // still could be useful though
+ assert(g[].equal("A\u0301B"));
+}
+
+@safe unittest
+{
+ auto g = Grapheme("A\u0302");
+ assert(g[0] == 'A');
+ assert(g.valid);
+ g[1] = '~'; // ASCII tilda is not a combining mark
+ assert(g[1] == '~');
+ assert(!g.valid);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ import std.conv : text;
+ import std.range : iota;
+
+ // not valid clusters (but it just a test)
+ auto g = Grapheme('a', 'b', 'c', 'd', 'e');
+ assert(g[0] == 'a');
+ assert(g[1] == 'b');
+ assert(g[2] == 'c');
+ assert(g[3] == 'd');
+ assert(g[4] == 'e');
+ g[3] = 'Й';
+ assert(g[2] == 'c');
+ assert(g[3] == 'Й', text(g[3], " vs ", 'Й'));
+ assert(g[4] == 'e');
+ assert(!g.valid);
+
+ g ~= 'ц';
+ g ~= '~';
+ assert(g[0] == 'a');
+ assert(g[1] == 'b');
+ assert(g[2] == 'c');
+ assert(g[3] == 'Й');
+ assert(g[4] == 'e');
+ assert(g[5] == 'ц');
+ assert(g[6] == '~');
+ assert(!g.valid);
+
+ Grapheme copy = g;
+ copy[0] = 'X';
+ copy[1] = '-';
+ assert(g[0] == 'a' && copy[0] == 'X');
+ assert(g[1] == 'b' && copy[1] == '-');
+ assert(equal(g[2 .. g.length], copy[2 .. copy.length]));
+ copy = Grapheme("ÐБВГДЕÐЖЗИКЛМ");
+ assert(equal(copy[0 .. 8], "ÐБВГДЕÐЖ"), text(copy[0 .. 8]));
+ copy ~= "xyz";
+ assert(equal(copy[13 .. 15], "xy"), text(copy[13 .. 15]));
+ assert(!copy.valid);
+
+ Grapheme h;
+ foreach (dchar v; iota(cast(int)'A', cast(int)'Z'+1).map!"cast(dchar)a"())
+ h ~= v;
+ assert(equal(h[], iota(cast(int)'A', cast(int)'Z'+1)));
+}
+
+/++
+ $(P Does basic case-insensitive comparison of `r1` and `r2`.
+ This function uses simpler comparison rule thus achieving better performance
+ than $(LREF icmp). However keep in mind the warning below.)
+
+ Params:
+ r1 = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of characters
+ r2 = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of characters
+
+ Returns:
+ An `int` that is 0 if the strings match,
+ &lt;0 if `r1` is lexicographically "less" than `r2`,
+ &gt;0 if `r1` is lexicographically "greater" than `r2`
+
+ Warning:
+ This function only handles 1:1 $(CODEPOINT) mapping
+ and thus is not sufficient for certain alphabets
+ like German, Greek and few others.
+
+ See_Also:
+ $(LREF icmp)
+ $(REF cmp, std,algorithm,comparison)
++/
+int sicmp(S1, S2)(scope S1 r1, scope S2 r2)
+if (isInputRange!S1 && isSomeChar!(ElementEncodingType!S1)
+ && isInputRange!S2 && isSomeChar!(ElementEncodingType!S2))
+{
+ import std.internal.unicode_tables : sTable = simpleCaseTable; // generated file
+ import std.range.primitives : isInfinite;
+ import std.utf : decodeFront;
+ import std.traits : isDynamicArray;
+ import std.typecons : Yes;
+ static import std.ascii;
+
+ static if ((isDynamicArray!S1 || isRandomAccessRange!S1)
+ && (isDynamicArray!S2 || isRandomAccessRange!S2)
+ && !(isInfinite!S1 && isInfinite!S2)
+ && __traits(compiles,
+ {
+ size_t s = size_t.sizeof / 2;
+ r1 = r1[s .. $];
+ r2 = r2[s .. $];
+ }))
+ {{
+ // ASCII optimization for dynamic arrays & similar.
+ size_t i = 0;
+ static if (isInfinite!S1)
+ immutable end = r2.length;
+ else static if (isInfinite!S2)
+ immutable end = r1.length;
+ else
+ immutable end = r1.length > r2.length ? r2.length : r1.length;
+ for (; i < end; ++i)
+ {
+ auto lhs = r1[i];
+ auto rhs = r2[i];
+ if ((lhs | rhs) >= 0x80) goto NonAsciiPath;
+ if (lhs == rhs) continue;
+ auto lowDiff = std.ascii.toLower(lhs) - std.ascii.toLower(rhs);
+ if (lowDiff) return lowDiff;
+ }
+ static if (isInfinite!S1)
+ return 1;
+ else static if (isInfinite!S2)
+ return -1;
+ else
+ return (r1.length > r2.length) - (r2.length > r1.length);
+
+ NonAsciiPath:
+ r1 = r1[i .. $];
+ r2 = r2[i .. $];
+ // Fall through to standard case.
+ }}
+
+ while (!r1.empty)
+ {
+ immutable lhs = decodeFront!(Yes.useReplacementDchar)(r1);
+ if (r2.empty)
+ return 1;
+ immutable rhs = decodeFront!(Yes.useReplacementDchar)(r2);
+ int diff = lhs - rhs;
+ if (!diff)
+ continue;
+ if ((lhs | rhs) < 0x80)
+ {
+ immutable d = std.ascii.toLower(lhs) - std.ascii.toLower(rhs);
+ if (!d) continue;
+ return d;
+ }
+ size_t idx = simpleCaseTrie[lhs];
+ size_t idx2 = simpleCaseTrie[rhs];
+ // simpleCaseTrie is packed index table
+ if (idx != EMPTY_CASE_TRIE)
+ {
+ if (idx2 != EMPTY_CASE_TRIE)
+ {// both cased chars
+ // adjust idx --> start of bucket
+ idx = idx - sTable[idx].n;
+ idx2 = idx2 - sTable[idx2].n;
+ if (idx == idx2)// one bucket, equivalent chars
+ continue;
+ else// not the same bucket
+ diff = sTable[idx].ch - sTable[idx2].ch;
+ }
+ else
+ diff = sTable[idx - sTable[idx].n].ch - rhs;
+ }
+ else if (idx2 != EMPTY_CASE_TRIE)
+ {
+ diff = lhs - sTable[idx2 - sTable[idx2].n].ch;
+ }
+ // one of chars is not cased at all
+ return diff;
+ }
+ return int(r2.empty) - 1;
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ assert(sicmp("ÐвгуÑÑ‚", "авгуÑТ") == 0);
+ // Greek also works as long as there is no 1:M mapping in sight
+ assert(sicmp("ΌΎ", "ÏŒÏ") == 0);
+ // things like the following won't get matched as equal
+ // Greek small letter iota with dialytika and tonos
+ assert(sicmp("Î", "\u03B9\u0308\u0301") != 0);
+
+ // while icmp has no problem with that
+ assert(icmp("Î", "\u03B9\u0308\u0301") == 0);
+ assert(icmp("ΌΎ", "ÏŒÏ") == 0);
+}
+
+// overloads for the most common cases to reduce compile time
+@safe @nogc pure nothrow
+{
+ int sicmp(scope const(char)[] str1, scope const(char)[] str2)
+ { return sicmp!(const(char)[], const(char)[])(str1, str2); }
+
+ int sicmp(scope const(wchar)[] str1, scope const(wchar)[] str2)
+ { return sicmp!(const(wchar)[], const(wchar)[])(str1, str2); }
+
+ int sicmp(scope const(dchar)[] str1, scope const(dchar)[] str2)
+ { return sicmp!(const(dchar)[], const(dchar)[])(str1, str2); }
+}
+
+private int fullCasedCmp(Range)(dchar lhs, dchar rhs, ref Range rtail)
+{
+ import std.algorithm.searching : skipOver;
+ import std.internal.unicode_tables : fullCaseTable; // generated file
+ alias fTable = fullCaseTable;
+ size_t idx = fullCaseTrie[lhs];
+ // fullCaseTrie is packed index table
+ if (idx == EMPTY_CASE_TRIE)
+ return lhs;
+ immutable start = idx - fTable[idx].n;
+ immutable end = fTable[idx].size + start;
+ assert(fTable[start].entry_len == 1);
+ for (idx=start; idx<end; idx++)
+ {
+ auto entryLen = fTable[idx].entry_len;
+ if (entryLen == 1)
+ {
+ if (fTable[idx].seq[0] == rhs)
+ {
+ return 0;
+ }
+ }
+ else
+ {// OK it's a long chunk, like 'ss' for German
+ dstring seq = fTable[idx].seq[0 .. entryLen];
+ if (rhs == seq[0]
+ && rtail.skipOver(seq[1..$]))
+ {
+ // note that this path modifies rtail
+ // iff we managed to get there
+ return 0;
+ }
+ }
+ }
+ return fTable[start].seq[0]; // new remapped character for accurate diffs
+}
+
+/++
+ Does case insensitive comparison of `r1` and `r2`.
+ Follows the rules of full case-folding mapping.
+ This includes matching as equal german ß with "ss" and
+ other 1:M $(CODEPOINT) mappings unlike $(LREF sicmp).
+ The cost of `icmp` being pedantically correct is
+ slightly worse performance.
+
+ Params:
+ r1 = a forward range of characters
+ r2 = a forward range of characters
+
+ Returns:
+ An `int` that is 0 if the strings match,
+ &lt;0 if `str1` is lexicographically "less" than `str2`,
+ &gt;0 if `str1` is lexicographically "greater" than `str2`
+
+ See_Also:
+ $(LREF sicmp)
+ $(REF cmp, std,algorithm,comparison)
++/
+int icmp(S1, S2)(S1 r1, S2 r2)
+if (isForwardRange!S1 && isSomeChar!(ElementEncodingType!S1)
+ && isForwardRange!S2 && isSomeChar!(ElementEncodingType!S2))
+{
+ import std.range.primitives : isInfinite;
+ import std.traits : isDynamicArray;
+ import std.utf : byDchar;
+ static import std.ascii;
+
+ static if ((isDynamicArray!S1 || isRandomAccessRange!S1)
+ && (isDynamicArray!S2 || isRandomAccessRange!S2)
+ && !(isInfinite!S1 && isInfinite!S2)
+ && __traits(compiles,
+ {
+ size_t s = size_t.max / 2;
+ r1 = r1[s .. $];
+ r2 = r2[s .. $];
+ }))
+ {{
+ // ASCII optimization for dynamic arrays & similar.
+ size_t i = 0;
+ static if (isInfinite!S1)
+ immutable end = r2.length;
+ else static if (isInfinite!S2)
+ immutable end = r1.length;
+ else
+ immutable end = r1.length > r2.length ? r2.length : r1.length;
+ for (; i < end; ++i)
+ {
+ auto lhs = r1[i];
+ auto rhs = r2[i];
+ if ((lhs | rhs) >= 0x80) goto NonAsciiPath;
+ if (lhs == rhs) continue;
+ auto lowDiff = std.ascii.toLower(lhs) - std.ascii.toLower(rhs);
+ if (lowDiff) return lowDiff;
+ }
+ static if (isInfinite!S1)
+ return 1;
+ else static if (isInfinite!S2)
+ return -1;
+ else
+ return (r1.length > r2.length) - (r2.length > r1.length);
+
+ NonAsciiPath:
+ r1 = r1[i .. $];
+ r2 = r2[i .. $];
+ // Fall through to standard case.
+ }}
+
+ auto str1 = r1.byDchar;
+ auto str2 = r2.byDchar;
+
+ for (;;)
+ {
+ if (str1.empty)
+ return str2.empty ? 0 : -1;
+ immutable lhs = str1.front;
+ if (str2.empty)
+ return 1;
+ immutable rhs = str2.front;
+ str1.popFront();
+ str2.popFront();
+ if (!(lhs - rhs))
+ continue;
+ // first try to match lhs to <rhs,right-tail> sequence
+ immutable cmpLR = fullCasedCmp(lhs, rhs, str2);
+ if (!cmpLR)
+ continue;
+ // then rhs to <lhs,left-tail> sequence
+ immutable cmpRL = fullCasedCmp(rhs, lhs, str1);
+ if (!cmpRL)
+ continue;
+ // cmpXX contain remapped codepoints
+ // to obtain stable ordering of icmp
+ return cmpLR - cmpRL;
+ }
+}
+
+///
+@safe @nogc pure nothrow unittest
+{
+ assert(icmp("Rußland", "Russland") == 0);
+ assert(icmp("ᾩ -> \u1F70\u03B9", "\u1F61\u03B9 -> ᾲ") == 0);
+}
+
+/**
+ * By using $(REF byUTF, std,utf) and its aliases, GC allocations via auto-decoding
+ * and thrown exceptions can be avoided, making `icmp` `@safe @nogc nothrow pure`.
+ */
+@safe @nogc nothrow pure unittest
+{
+ import std.utf : byDchar;
+
+ assert(icmp("Rußland".byDchar, "Russland".byDchar) == 0);
+ assert(icmp("ᾩ -> \u1F70\u03B9".byDchar, "\u1F61\u03B9 -> ᾲ".byDchar) == 0);
+}
+
+// test different character types
+@safe unittest
+{
+ assert(icmp("Rußland", "Russland") == 0);
+ assert(icmp("Rußland"w, "Russland") == 0);
+ assert(icmp("Rußland", "Russland"w) == 0);
+ assert(icmp("Rußland"w, "Russland"w) == 0);
+ assert(icmp("Rußland"d, "Russland"w) == 0);
+ assert(icmp("Rußland"w, "Russland"d) == 0);
+}
+
+// overloads for the most common cases to reduce compile time
+@safe @nogc pure nothrow
+{
+ int icmp(const(char)[] str1, const(char)[] str2)
+ { return icmp!(const(char)[], const(char)[])(str1, str2); }
+ int icmp(const(wchar)[] str1, const(wchar)[] str2)
+ { return icmp!(const(wchar)[], const(wchar)[])(str1, str2); }
+ int icmp(const(dchar)[] str1, const(dchar)[] str2)
+ { return icmp!(const(dchar)[], const(dchar)[])(str1, str2); }
+}
+
+@safe unittest
+{
+ import std.algorithm.sorting : sort;
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+ assertCTFEable!(
+ {
+ static foreach (cfunc; AliasSeq!(icmp, sicmp))
+ {{
+ static foreach (S1; AliasSeq!(string, wstring, dstring))
+ static foreach (S2; AliasSeq!(string, wstring, dstring))
+ {
+ assert(cfunc("".to!S1(), "".to!S2()) == 0);
+ assert(cfunc("A".to!S1(), "".to!S2()) > 0);
+ assert(cfunc("".to!S1(), "0".to!S2()) < 0);
+ assert(cfunc("abc".to!S1(), "abc".to!S2()) == 0);
+ assert(cfunc("abcd".to!S1(), "abc".to!S2()) > 0);
+ assert(cfunc("abc".to!S1(), "abcd".to!S2()) < 0);
+ assert(cfunc("Abc".to!S1(), "aBc".to!S2()) == 0);
+ assert(cfunc("авГуÑÑ‚".to!S1(), "ÐВгУСТ".to!S2()) == 0);
+ // Check example:
+ assert(cfunc("ÐвгуÑÑ‚".to!S1(), "авгуÑТ".to!S2()) == 0);
+ assert(cfunc("ΌΎ".to!S1(), "ÏŒÏ".to!S2()) == 0);
+ }
+ // check that the order is properly agnostic to the case
+ auto strs = [ "Apple", "ORANGE", "orAcle", "amp", "banana"];
+ sort!((a,b) => cfunc(a,b) < 0)(strs);
+ assert(strs == ["amp", "Apple", "banana", "orAcle", "ORANGE"]);
+ }}
+ assert(icmp("ßb", "ssa") > 0);
+ // Check example:
+ assert(icmp("Russland", "Rußland") == 0);
+ assert(icmp("ᾩ -> \u1F70\u03B9", "\u1F61\u03B9 -> ᾲ") == 0);
+ assert(icmp("Î"w, "\u03B9\u0308\u0301") == 0);
+ assert(sicmp("Î", "\u03B9\u0308\u0301") != 0);
+ // https://issues.dlang.org/show_bug.cgi?id=11057
+ assert( icmp("K", "L") < 0 );
+ });
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17372
+@safe pure unittest
+{
+ import std.algorithm.iteration : joiner, map;
+ import std.algorithm.sorting : sort;
+ import std.array : array;
+ auto a = [["foo", "bar"], ["baz"]].map!(line => line.joiner(" ")).array.sort!((a, b) => icmp(a, b) < 0);
+}
+
+// This is package(std) for the moment to be used as a support tool for std.regex
+// It needs a better API
+/*
+ Return a range of all $(CODEPOINTS) that casefold to
+ and from this `ch`.
+*/
+package(std) auto simpleCaseFoldings(dchar ch) @safe
+{
+ import std.internal.unicode_tables : simpleCaseTable; // generated file
+ alias sTable = simpleCaseTable;
+ static struct Range
+ {
+ @safe pure nothrow:
+ uint idx; //if == uint.max, then read c.
+ union
+ {
+ dchar c; // == 0 - empty range
+ uint len;
+ }
+ @property bool isSmall() const { return idx == uint.max; }
+
+ this(dchar ch)
+ {
+ idx = uint.max;
+ c = ch;
+ }
+
+ this(uint start, uint size)
+ {
+ idx = start;
+ len = size;
+ }
+
+ @property dchar front() const
+ {
+ assert(!empty);
+ if (isSmall)
+ {
+ return c;
+ }
+ auto ch = sTable[idx].ch;
+ return ch;
+ }
+
+ @property bool empty() const
+ {
+ if (isSmall)
+ {
+ return c == 0;
+ }
+ return len == 0;
+ }
+
+ @property size_t length() const
+ {
+ if (isSmall)
+ {
+ return c == 0 ? 0 : 1;
+ }
+ return len;
+ }
+
+ void popFront()
+ {
+ if (isSmall)
+ c = 0;
+ else
+ {
+ idx++;
+ len--;
+ }
+ }
+ }
+ immutable idx = simpleCaseTrie[ch];
+ if (idx == EMPTY_CASE_TRIE)
+ return Range(ch);
+ auto entry = sTable[idx];
+ immutable start = idx - entry.n;
+ return Range(start, entry.size);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.searching : canFind;
+ import std.array : array;
+ import std.exception : assertCTFEable;
+ assertCTFEable!((){
+ auto r = simpleCaseFoldings('Э').array;
+ assert(r.length == 2);
+ assert(r.canFind('Ñ') && r.canFind('Э'));
+ auto sr = simpleCaseFoldings('~');
+ assert(sr.equal("~"));
+ //A with ring above - casefolds to the same bucket as Angstrom sign
+ sr = simpleCaseFoldings('Ã…');
+ assert(sr.length == 3);
+ assert(sr.canFind('Ã¥') && sr.canFind('Ã…') && sr.canFind('\u212B'));
+ });
+}
+
+/++
+ $(P Returns the $(S_LINK Combining class, combining class) of `ch`.)
++/
+ubyte combiningClass(dchar ch) @safe pure nothrow @nogc
+{
+ return combiningClassTrie[ch];
+}
+
+///
+@safe unittest
+{
+ // shorten the code
+ alias CC = combiningClass;
+
+ // combining tilda
+ assert(CC('\u0303') == 230);
+ // combining ring below
+ assert(CC('\u0325') == 220);
+ // the simple consequence is that "tilda" should be
+ // placed after a "ring below" in a sequence
+}
+
+@safe pure nothrow @nogc unittest
+{
+ foreach (ch; 0 .. 0x80)
+ assert(combiningClass(ch) == 0);
+ assert(combiningClass('\u05BD') == 22);
+ assert(combiningClass('\u0300') == 230);
+ assert(combiningClass('\u0317') == 220);
+ assert(combiningClass('\u1939') == 222);
+}
+
+/// Unicode character decomposition type.
+enum UnicodeDecomposition {
+ /// Canonical decomposition. The result is canonically equivalent sequence.
+ Canonical,
+ /**
+ Compatibility decomposition. The result is compatibility equivalent sequence.
+ Note: Compatibility decomposition is a $(B lossy) conversion,
+ typically suitable only for fuzzy matching and internal processing.
+ */
+ Compatibility
+}
+
+/**
+ Shorthand aliases for character decomposition type, passed as a
+ template parameter to $(LREF decompose).
+*/
+enum {
+ Canonical = UnicodeDecomposition.Canonical,
+ Compatibility = UnicodeDecomposition.Compatibility
+}
+
+/++
+ Try to canonically compose 2 $(CHARACTERS).
+ Returns the composed $(CHARACTER) if they do compose and dchar.init otherwise.
+
+ The assumption is that `first` comes before `second` in the original text,
+ usually meaning that the first is a starter.
+
+ Note: Hangul syllables are not covered by this function.
+ See `composeJamo` below.
++/
+public dchar compose(dchar first, dchar second) pure nothrow @safe
+{
+ import std.algorithm.iteration : map;
+ import std.internal.unicode_comp : compositionTable, composeCntShift, composeIdxMask;
+ import std.range : assumeSorted;
+ immutable packed = compositionJumpTrie[first];
+ if (packed == ushort.max)
+ return dchar.init;
+ // unpack offset and length
+ immutable idx = packed & composeIdxMask, cnt = packed >> composeCntShift;
+ // TODO: optimize this micro binary search (no more then 4-5 steps)
+ auto r = compositionTable[idx .. idx+cnt].map!"a.rhs"().assumeSorted();
+ immutable target = r.lowerBound(second).length;
+ if (target == cnt)
+ return dchar.init;
+ immutable entry = compositionTable[idx+target];
+ if (entry.rhs != second)
+ return dchar.init;
+ return entry.composed;
+}
+
+///
+@safe unittest
+{
+ assert(compose('A','\u0308') == '\u00C4');
+ assert(compose('A', 'B') == dchar.init);
+ assert(compose('C', '\u0301') == '\u0106');
+ // note that the starter is the first one
+ // thus the following doesn't compose
+ assert(compose('\u0308', 'A') == dchar.init);
+}
+
+/++
+ Returns a full $(S_LINK Canonical decomposition, Canonical)
+ (by default) or $(S_LINK Compatibility decomposition, Compatibility)
+ decomposition of $(CHARACTER) `ch`.
+ If no decomposition is available returns a $(LREF Grapheme)
+ with the `ch` itself.
+
+ Note:
+ This function also decomposes hangul syllables
+ as prescribed by the standard.
+
+ See_Also: $(LREF decomposeHangul) for a restricted version
+ that takes into account only hangul syllables but
+ no other decompositions.
++/
+public Grapheme decompose(UnicodeDecomposition decompType=Canonical)(dchar ch) @safe
+{
+ import std.algorithm.searching : until;
+ import std.internal.unicode_decomp : decompCompatTable, decompCanonTable;
+ static if (decompType == Canonical)
+ {
+ alias table = decompCanonTable;
+ alias mapping = canonMappingTrie;
+ }
+ else static if (decompType == Compatibility)
+ {
+ alias table = decompCompatTable;
+ alias mapping = compatMappingTrie;
+ }
+ immutable idx = mapping[ch];
+ if (!idx) // not found, check hangul arithmetic decomposition
+ return decomposeHangul(ch);
+ auto decomp = table[idx..$].until(0);
+ return Grapheme(decomp);
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(compose('A','\u0308') == '\u00C4');
+ assert(compose('A', 'B') == dchar.init);
+ assert(compose('C', '\u0301') == '\u0106');
+ // note that the starter is the first one
+ // thus the following doesn't compose
+ assert(compose('\u0308', 'A') == dchar.init);
+
+ assert(decompose('Ĉ')[].equal("C\u0302"));
+ assert(decompose('D')[].equal("D"));
+ assert(decompose('\uD4DC')[].equal("\u1111\u1171\u11B7"));
+ assert(decompose!Compatibility('¹')[].equal("1"));
+}
+
+//----------------------------------------------------------------------------
+// Hangul specific composition/decomposition
+enum jamoSBase = 0xAC00;
+enum jamoLBase = 0x1100;
+enum jamoVBase = 0x1161;
+enum jamoTBase = 0x11A7;
+enum jamoLCount = 19, jamoVCount = 21, jamoTCount = 28;
+enum jamoNCount = jamoVCount * jamoTCount;
+enum jamoSCount = jamoLCount * jamoNCount;
+
+// Tests if `ch` is a Hangul leading consonant jamo.
+bool isJamoL(dchar ch) pure nothrow @nogc @safe
+{
+ // first cmp rejects ~ 1M code points above leading jamo range
+ return ch < jamoLBase+jamoLCount && ch >= jamoLBase;
+}
+
+// Tests if `ch` is a Hangul vowel jamo.
+bool isJamoT(dchar ch) pure nothrow @nogc @safe
+{
+ // first cmp rejects ~ 1M code points above trailing jamo range
+ // Note: ch == jamoTBase doesn't indicate trailing jamo (TIndex must be > 0)
+ return ch < jamoTBase+jamoTCount && ch > jamoTBase;
+}
+
+// Tests if `ch` is a Hangul trailnig consonant jamo.
+bool isJamoV(dchar ch) pure nothrow @nogc @safe
+{
+ // first cmp rejects ~ 1M code points above vowel range
+ return ch < jamoVBase+jamoVCount && ch >= jamoVBase;
+}
+
+int hangulSyllableIndex(dchar ch) pure nothrow @nogc @safe
+{
+ int idxS = cast(int) ch - jamoSBase;
+ return idxS >= 0 && idxS < jamoSCount ? idxS : -1;
+}
+
+// internal helper: compose hangul syllables leaving dchar.init in holes
+void hangulRecompose(dchar[] seq) pure nothrow @nogc @safe
+{
+ for (size_t idx = 0; idx + 1 < seq.length; )
+ {
+ if (isJamoL(seq[idx]) && isJamoV(seq[idx+1]))
+ {
+ immutable int indexL = seq[idx] - jamoLBase;
+ immutable int indexV = seq[idx+1] - jamoVBase;
+ immutable int indexLV = indexL * jamoNCount + indexV * jamoTCount;
+ if (idx + 2 < seq.length && isJamoT(seq[idx+2]))
+ {
+ seq[idx] = jamoSBase + indexLV + seq[idx+2] - jamoTBase;
+ seq[idx+1] = dchar.init;
+ seq[idx+2] = dchar.init;
+ idx += 3;
+ }
+ else
+ {
+ seq[idx] = jamoSBase + indexLV;
+ seq[idx+1] = dchar.init;
+ idx += 2;
+ }
+ }
+ else
+ idx++;
+ }
+}
+
+//----------------------------------------------------------------------------
+public:
+
+/**
+ Decomposes a Hangul syllable. If `ch` is not a composed syllable
+ then this function returns $(LREF Grapheme) containing only `ch` as is.
+*/
+Grapheme decomposeHangul(dchar ch) @safe
+{
+ immutable idxS = cast(int) ch - jamoSBase;
+ if (idxS < 0 || idxS >= jamoSCount) return Grapheme(ch);
+ immutable idxL = idxS / jamoNCount;
+ immutable idxV = (idxS % jamoNCount) / jamoTCount;
+ immutable idxT = idxS % jamoTCount;
+
+ immutable partL = jamoLBase + idxL;
+ immutable partV = jamoVBase + idxV;
+ if (idxT > 0) // there is a trailling consonant (T); <L,V,T> decomposition
+ return Grapheme(partL, partV, jamoTBase + idxT);
+ else // <L, V> decomposition
+ return Grapheme(partL, partV);
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6"));
+}
+
+/++
+ Try to compose hangul syllable out of a leading consonant (`lead`),
+ a `vowel` and optional `trailing` consonant jamos.
+
+ On success returns the composed LV or LVT hangul syllable.
+
+ If any of `lead` and `vowel` are not a valid hangul jamo
+ of the respective $(CHARACTER) class returns dchar.init.
++/
+dchar composeJamo(dchar lead, dchar vowel, dchar trailing=dchar.init) pure nothrow @nogc @safe
+{
+ if (!isJamoL(lead))
+ return dchar.init;
+ immutable indexL = lead - jamoLBase;
+ if (!isJamoV(vowel))
+ return dchar.init;
+ immutable indexV = vowel - jamoVBase;
+ immutable indexLV = indexL * jamoNCount + indexV * jamoTCount;
+ immutable dchar syllable = jamoSBase + indexLV;
+ return isJamoT(trailing) ? syllable + (trailing - jamoTBase) : syllable;
+}
+
+///
+@safe unittest
+{
+ assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB');
+ // leaving out T-vowel, or passing any codepoint
+ // that is not trailing consonant composes an LV-syllable
+ assert(composeJamo('\u1111', '\u1171') == '\uD4CC');
+ assert(composeJamo('\u1111', '\u1171', ' ') == '\uD4CC');
+ assert(composeJamo('\u1111', 'A') == dchar.init);
+ assert(composeJamo('A', '\u1171') == dchar.init);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : text;
+
+ static void testDecomp(UnicodeDecomposition T)(dchar ch, string r)
+ {
+ Grapheme g = decompose!T(ch);
+ assert(equal(g[], r), text(g[], " vs ", r));
+ }
+ testDecomp!Canonical('\u1FF4', "\u03C9\u0301\u0345");
+ testDecomp!Canonical('\uF907', "\u9F9C");
+ testDecomp!Compatibility('\u33FF', "\u0067\u0061\u006C");
+ testDecomp!Compatibility('\uA7F9', "\u0153");
+
+ // check examples
+ assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6"));
+ assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB');
+ assert(composeJamo('\u1111', '\u1171') == '\uD4CC'); // leave out T-vowel
+ assert(composeJamo('\u1111', '\u1171', ' ') == '\uD4CC');
+ assert(composeJamo('\u1111', 'A') == dchar.init);
+ assert(composeJamo('A', '\u1171') == dchar.init);
+}
+
+/**
+ Enumeration type for normalization forms,
+ passed as template parameter for functions like $(LREF normalize).
+*/
+enum NormalizationForm {
+ NFC,
+ NFD,
+ NFKC,
+ NFKD
+}
+
+
+enum {
+ /**
+ Shorthand aliases from values indicating normalization forms.
+ */
+ NFC = NormalizationForm.NFC,
+ ///ditto
+ NFD = NormalizationForm.NFD,
+ ///ditto
+ NFKC = NormalizationForm.NFKC,
+ ///ditto
+ NFKD = NormalizationForm.NFKD
+}
+
+/++
+ Returns `input` string normalized to the chosen form.
+ Form C is used by default.
+
+ For more information on normalization forms see
+ the $(S_LINK Normalization, normalization section).
+
+ Note:
+ In cases where the string in question is already normalized,
+ it is returned unmodified and no memory allocation happens.
++/
+inout(C)[] normalize(NormalizationForm norm=NFC, C)(return scope inout(C)[] input)
+{
+ import std.algorithm.mutation : SwapStrategy;
+ import std.algorithm.sorting : sort;
+ import std.array : appender;
+ import std.range : zip;
+
+ auto anchors = splitNormalized!norm(input);
+ if (anchors[0] == input.length && anchors[1] == input.length)
+ return input;
+ dchar[] decomposed;
+ decomposed.reserve(31);
+ ubyte[] ccc;
+ ccc.reserve(31);
+ auto app = appender!(C[])();
+ do
+ {
+ app.put(input[0 .. anchors[0]]);
+ foreach (dchar ch; input[anchors[0]..anchors[1]])
+ static if (norm == NFD || norm == NFC)
+ {
+ foreach (dchar c; decompose!Canonical(ch)[])
+ decomposed ~= c;
+ }
+ else // NFKD & NFKC
+ {
+ foreach (dchar c; decompose!Compatibility(ch)[])
+ decomposed ~= c;
+ }
+ ccc.length = decomposed.length;
+ size_t firstNonStable = 0;
+ ubyte lastClazz = 0;
+
+ foreach (idx, dchar ch; decomposed)
+ {
+ immutable clazz = combiningClass(ch);
+ ccc[idx] = clazz;
+ if (clazz == 0 && lastClazz != 0)
+ {
+ // found a stable code point after unstable ones
+ sort!("a[0] < b[0]", SwapStrategy.stable)
+ (zip(ccc[firstNonStable .. idx], decomposed[firstNonStable .. idx]));
+ firstNonStable = decomposed.length;
+ }
+ else if (clazz != 0 && lastClazz == 0)
+ {
+ // found first unstable code point after stable ones
+ firstNonStable = idx;
+ }
+ lastClazz = clazz;
+ }
+ sort!("a[0] < b[0]", SwapStrategy.stable)
+ (zip(ccc[firstNonStable..$], decomposed[firstNonStable..$]));
+ static if (norm == NFC || norm == NFKC)
+ {
+ import std.algorithm.searching : countUntil;
+ auto first = countUntil(ccc, 0);
+ if (first >= 0) // no starters?? no recomposition
+ {
+ for (;;)
+ {
+ immutable second = recompose(first, decomposed, ccc);
+ if (second == decomposed.length)
+ break;
+ first = second;
+ }
+ // 2nd pass for hangul syllables
+ hangulRecompose(decomposed);
+ }
+ }
+ static if (norm == NFD || norm == NFKD)
+ app.put(decomposed);
+ else
+ {
+ import std.algorithm.mutation : remove;
+ auto clean = remove!("a == dchar.init", SwapStrategy.stable)(decomposed);
+ app.put(decomposed[0 .. clean.length]);
+ }
+ // reset variables
+ decomposed.length = 0;
+ () @trusted {
+ decomposed.assumeSafeAppend();
+ ccc.length = 0;
+ ccc.assumeSafeAppend();
+ } ();
+ input = input[anchors[1]..$];
+ // and move on
+ anchors = splitNormalized!norm(input);
+ }while (anchors[0] != input.length);
+ app.put(input[0 .. anchors[0]]);
+ return () @trusted inout { return cast(inout(C)[]) app.data; } ();
+}
+
+///
+@safe unittest
+{
+ // any encoding works
+ wstring greet = "Hello world";
+ assert(normalize(greet) is greet); // the same exact slice
+
+ // An example of a character with all 4 forms being different:
+ // Greek upsilon with acute and hook symbol (code point 0x03D3)
+ assert(normalize!NFC("Ï“") == "\u03D3");
+ assert(normalize!NFD("Ï“") == "\u03D2\u0301");
+ assert(normalize!NFKC("Ï“") == "\u038E");
+ assert(normalize!NFKD("Ï“") == "\u03A5\u0301");
+}
+
+@safe unittest
+{
+ import std.conv : text;
+
+ assert(normalize!NFD("abc\uF904def") == "abc\u6ED1def", text(normalize!NFD("abc\uF904def")));
+ assert(normalize!NFKD("2¹â°") == "210", normalize!NFKD("2¹â°"));
+ assert(normalize!NFD("Äffin") == "A\u0308ffin");
+
+ // check example
+
+ // any encoding works
+ wstring greet = "Hello world";
+ assert(normalize(greet) is greet); // the same exact slice
+
+ // An example of a character with all 4 forms being different:
+ // Greek upsilon with acute and hook symbol (code point 0x03D3)
+ assert(normalize!NFC("Ï“") == "\u03D3");
+ assert(normalize!NFD("Ï“") == "\u03D2\u0301");
+ assert(normalize!NFKC("Ï“") == "\u038E");
+ assert(normalize!NFKD("Ï“") == "\u03A5\u0301");
+}
+
+// canonically recompose given slice of code points, works in-place and mutates data
+private size_t recompose(size_t start, dchar[] input, ubyte[] ccc) pure nothrow @safe
+{
+ assert(input.length == ccc.length);
+ int accumCC = -1;// so that it's out of 0 .. 255 range
+ // writefln("recomposing %( %04x %)", input);
+ // first one is always a starter thus we start at i == 1
+ size_t i = start+1;
+ for (; ; )
+ {
+ if (i == input.length)
+ break;
+ immutable curCC = ccc[i];
+ // In any character sequence beginning with a starter S
+ // a character C is blocked from S if and only if there
+ // is some character B between S and C, and either B
+ // is a starter or it has the same or higher combining class as C.
+ //------------------------
+ // Applying to our case:
+ // S is input[0]
+ // accumCC is the maximum CCC of characters between C and S,
+ // as ccc are sorted
+ // C is input[i]
+
+ if (curCC > accumCC)
+ {
+ immutable comp = compose(input[start], input[i]);
+ if (comp != dchar.init)
+ {
+ input[start] = comp;
+ input[i] = dchar.init;// put a sentinel
+ // current was merged so its CCC shouldn't affect
+ // composing with the next one
+ }
+ else
+ {
+ // if it was a starter then accumCC is now 0, end of loop
+ accumCC = curCC;
+ if (accumCC == 0)
+ break;
+ }
+ }
+ else
+ {
+ // ditto here
+ accumCC = curCC;
+ if (accumCC == 0)
+ break;
+ }
+ i++;
+ }
+ return i;
+}
+
+// returns tuple of 2 indexes that delimit:
+// normalized text, piece that needs normalization and
+// the rest of input starting with stable code point
+private auto splitNormalized(NormalizationForm norm, C)(scope const(C)[] input)
+{
+ import std.typecons : tuple;
+ ubyte lastCC = 0;
+
+ foreach (idx, dchar ch; input)
+ {
+ static if (norm == NFC)
+ if (ch < 0x0300)
+ {
+ lastCC = 0;
+ continue;
+ }
+ immutable ubyte CC = combiningClass(ch);
+ if (lastCC > CC && CC != 0)
+ {
+ return seekStable!norm(idx, input);
+ }
+
+ if (notAllowedIn!norm(ch))
+ {
+ return seekStable!norm(idx, input);
+ }
+ lastCC = CC;
+ }
+ return tuple(input.length, input.length);
+}
+
+private auto seekStable(NormalizationForm norm, C)(size_t idx, const scope C[] input)
+{
+ import std.typecons : tuple;
+ import std.utf : codeLength;
+
+ auto br = input[0 .. idx];
+ size_t region_start = 0;// default
+ for (;;)
+ {
+ if (br.empty)// start is 0
+ break;
+ dchar ch = br.back;
+ if (combiningClass(ch) == 0 && allowedIn!norm(ch))
+ {
+ region_start = br.length - codeLength!C(ch);
+ break;
+ }
+ br.popFront();
+ }
+ ///@@@BUG@@@ can't use find: " find is a nested function and can't be used..."
+ size_t region_end=input.length;// end is $ by default
+ foreach (i, dchar ch; input[idx..$])
+ {
+ if (combiningClass(ch) == 0 && allowedIn!norm(ch))
+ {
+ region_end = i+idx;
+ break;
+ }
+ }
+ // writeln("Region to normalize: ", input[region_start .. region_end]);
+ return tuple(region_start, region_end);
+}
+
+/**
+ Tests if dchar `ch` is always allowed (Quick_Check=YES) in normalization
+ form `norm`.
+*/
+public bool allowedIn(NormalizationForm norm)(dchar ch)
+{
+ return !notAllowedIn!norm(ch);
+}
+
+///
+@safe unittest
+{
+ // e.g. Cyrillic is always allowed, so is ASCII
+ assert(allowedIn!NFC('Ñ'));
+ assert(allowedIn!NFD('Ñ'));
+ assert(allowedIn!NFKC('Ñ'));
+ assert(allowedIn!NFKD('Ñ'));
+ assert(allowedIn!NFC('Z'));
+}
+
+// not user friendly name but more direct
+private bool notAllowedIn(NormalizationForm norm)(dchar ch)
+{
+ static if (norm == NFC)
+ alias qcTrie = nfcQCTrie;
+ else static if (norm == NFD)
+ alias qcTrie = nfdQCTrie;
+ else static if (norm == NFKC)
+ alias qcTrie = nfkcQCTrie;
+ else static if (norm == NFKD)
+ alias qcTrie = nfkdQCTrie;
+ else
+ static assert("Unknown normalization form "~norm);
+ return qcTrie[ch];
+}
+
+@safe unittest
+{
+ assert(allowedIn!NFC('Ñ'));
+ assert(allowedIn!NFD('Ñ'));
+ assert(allowedIn!NFKC('Ñ'));
+ assert(allowedIn!NFKD('Ñ'));
+ assert(allowedIn!NFC('Z'));
+}
+
+}
+
+version (std_uni_bootstrap)
+{
+ // old version used for bootstrapping of gen_uni.d that generates
+ // up to date optimal versions of all of isXXX functions
+ @safe pure nothrow @nogc public bool isWhite(dchar c)
+ {
+ import std.ascii : isWhite;
+ return isWhite(c) ||
+ c == lineSep || c == paraSep ||
+ c == '\u0085' || c == '\u00A0' || c == '\u1680' || c == '\u180E' ||
+ (c >= '\u2000' && c <= '\u200A') ||
+ c == '\u202F' || c == '\u205F' || c == '\u3000';
+ }
+}
+else
+{
+
+// trusted -> avoid bounds check
+@trusted pure nothrow @nogc private
+{
+ import std.internal.unicode_tables; // : toLowerTable, toTitleTable, toUpperTable; // generated file
+
+ // hide template instances behind functions
+ // https://issues.dlang.org/show_bug.cgi?id=13232
+ ushort toLowerIndex(dchar c) { return toLowerIndexTrie[c]; }
+ ushort toLowerSimpleIndex(dchar c) { return toLowerSimpleIndexTrie[c]; }
+ dchar toLowerTab(size_t idx) { return toLowerTable[idx]; }
+
+ ushort toTitleIndex(dchar c) { return toTitleIndexTrie[c]; }
+ ushort toTitleSimpleIndex(dchar c) { return toTitleSimpleIndexTrie[c]; }
+ dchar toTitleTab(size_t idx) { return toTitleTable[idx]; }
+
+ ushort toUpperIndex(dchar c) { return toUpperIndexTrie[c]; }
+ ushort toUpperSimpleIndex(dchar c) { return toUpperSimpleIndexTrie[c]; }
+ dchar toUpperTab(size_t idx) { return toUpperTable[idx]; }
+}
+
+public:
+
+/++
+ Whether or not `c` is a Unicode whitespace $(CHARACTER).
+ (general Unicode category: Part of C0(tab, vertical tab, form feed,
+ carriage return, and linefeed characters), Zs, Zl, Zp, and NEL(U+0085))
++/
+@safe pure nothrow @nogc
+public bool isWhite(dchar c)
+{
+ import std.internal.unicode_tables : isWhiteGen; // generated file
+ return isWhiteGen(c); // call pregenerated binary search
+}
+
+/++
+ Return whether `c` is a Unicode lowercase $(CHARACTER).
++/
+@safe pure nothrow @nogc
+bool isLower(dchar c)
+{
+ import std.ascii : isLower, isASCII;
+ if (isASCII(c))
+ return isLower(c);
+ return lowerCaseTrie[c];
+}
+
+@safe unittest
+{
+ import std.ascii : isLower;
+ foreach (v; 0 .. 0x80)
+ assert(isLower(v) == .isLower(v));
+ assert(.isLower('Ñ'));
+ assert(.isLower('й'));
+ assert(!.isLower('Ж'));
+ // Greek HETA
+ assert(!.isLower('\u0370'));
+ assert(.isLower('\u0371'));
+ assert(!.isLower('\u039C')); // capital MU
+ assert(.isLower('\u03B2')); // beta
+ // from extended Greek
+ assert(!.isLower('\u1F18'));
+ assert(.isLower('\u1F00'));
+ foreach (v; unicode.lowerCase.byCodepoint)
+ assert(.isLower(v) && !isUpper(v));
+}
+
+
+/++
+ Return whether `c` is a Unicode uppercase $(CHARACTER).
++/
+@safe pure nothrow @nogc
+bool isUpper(dchar c)
+{
+ import std.ascii : isUpper, isASCII;
+ if (isASCII(c))
+ return isUpper(c);
+ return upperCaseTrie[c];
+}
+
+@safe unittest
+{
+ import std.ascii : isLower;
+ foreach (v; 0 .. 0x80)
+ assert(isLower(v) == .isLower(v));
+ assert(!isUpper('й'));
+ assert(isUpper('Ж'));
+ // Greek HETA
+ assert(isUpper('\u0370'));
+ assert(!isUpper('\u0371'));
+ assert(isUpper('\u039C')); // capital MU
+ assert(!isUpper('\u03B2')); // beta
+ // from extended Greek
+ assert(!isUpper('\u1F00'));
+ assert(isUpper('\u1F18'));
+ foreach (v; unicode.upperCase.byCodepoint)
+ assert(isUpper(v) && !.isLower(v));
+}
+
+
+//TODO: Hidden for now, needs better API.
+//Other transforms could use better API as well, but this one is a new primitive.
+@safe pure nothrow @nogc
+private dchar toTitlecase(dchar c)
+{
+ // optimize ASCII case
+ if (c < 0xAA)
+ {
+ if (c < 'a')
+ return c;
+ if (c <= 'z')
+ return c - 32;
+ return c;
+ }
+ size_t idx = toTitleSimpleIndex(c);
+ if (idx != ushort.max)
+ {
+ return toTitleTab(idx);
+ }
+ return c;
+}
+
+private alias UpperTriple = AliasSeq!(toUpperIndex, MAX_SIMPLE_UPPER, toUpperTab);
+private alias LowerTriple = AliasSeq!(toLowerIndex, MAX_SIMPLE_LOWER, toLowerTab);
+
+// generic toUpper/toLower on whole string, creates new or returns as is
+private ElementEncodingType!S[] toCase(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, S)(S s)
+if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S)))
+{
+ import std.array : appender, array;
+ import std.ascii : isASCII;
+ import std.utf : byDchar, codeLength;
+
+ alias C = ElementEncodingType!S;
+
+ auto r = s.byDchar;
+ for (size_t i; !r.empty; i += r.front.codeLength!C , r.popFront())
+ {
+ auto cOuter = r.front;
+ ushort idx = indexFn(cOuter);
+ if (idx == ushort.max)
+ continue;
+ auto result = appender!(C[])();
+ result.reserve(s.length);
+ result.put(s[0 .. i]);
+ foreach (dchar c; s[i .. $].byDchar)
+ {
+ if (c.isASCII)
+ {
+ result.put(asciiConvert(c));
+ }
+ else
+ {
+ idx = indexFn(c);
+ if (idx == ushort.max)
+ result.put(c);
+ else if (idx < maxIdx)
+ {
+ c = tableFn(idx);
+ result.put(c);
+ }
+ else
+ {
+ auto val = tableFn(idx);
+ // unpack length + codepoint
+ immutable uint len = val >> 24;
+ result.put(cast(dchar)(val & 0xFF_FFFF));
+ foreach (j; idx+1 .. idx+len)
+ result.put(tableFn(j));
+ }
+ }
+ }
+ return result.data;
+ }
+
+ static if (isSomeString!S)
+ return s;
+ else
+ return s.array;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=12428
+@safe unittest
+{
+ import std.array : replicate;
+ auto s = "abcdefghij".replicate(300);
+ s = s[0 .. 10];
+
+ toUpper(s);
+
+ assert(s == "abcdefghij");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=18993
+@safe unittest
+{
+ static assert(`몬스터/A`.toLower.length == `몬스터/a`.toLower.length);
+}
+
+
+// generic toUpper/toLower on whole range, returns range
+private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, Range)(Range str)
+ // Accept range of dchar's
+if (isInputRange!Range &&
+ isSomeChar!(ElementEncodingType!Range) &&
+ ElementEncodingType!Range.sizeof == dchar.sizeof)
+{
+ static struct ToCaserImpl
+ {
+ @property bool empty()
+ {
+ return !nLeft && r.empty;
+ }
+
+ @property auto front()
+ {
+ import std.ascii : isASCII;
+
+ if (!nLeft)
+ {
+ dchar c = r.front;
+ if (c.isASCII)
+ {
+ buf[0] = asciiConvert(c);
+ nLeft = 1;
+ }
+ else
+ {
+ const idx = indexFn(c);
+ if (idx == ushort.max)
+ {
+ buf[0] = c;
+ nLeft = 1;
+ }
+ else if (idx < maxIdx)
+ {
+ buf[0] = tableFn(idx);
+ nLeft = 1;
+ }
+ else
+ {
+ immutable val = tableFn(idx);
+ // unpack length + codepoint
+ nLeft = val >> 24;
+ if (nLeft == 0)
+ nLeft = 1;
+ assert(nLeft <= buf.length);
+ buf[nLeft - 1] = cast(dchar)(val & 0xFF_FFFF);
+ foreach (j; 1 .. nLeft)
+ buf[nLeft - j - 1] = tableFn(idx + j);
+ }
+ }
+ }
+ return buf[nLeft - 1];
+ }
+
+ void popFront()
+ {
+ if (!nLeft)
+ front;
+ assert(nLeft);
+ --nLeft;
+ if (!nLeft)
+ r.popFront();
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property auto save()
+ {
+ auto ret = this;
+ ret.r = r.save;
+ return ret;
+ }
+ }
+
+ private:
+ Range r;
+ uint nLeft;
+ dchar[3] buf = void;
+ }
+
+ return ToCaserImpl(str);
+}
+
+/*********************
+ * Convert an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * or a string to upper or lower case.
+ *
+ * Does not allocate memory.
+ * Characters in UTF-8 or UTF-16 format that cannot be decoded
+ * are treated as $(REF replacementDchar, std,utf).
+ *
+ * Params:
+ * str = string or range of characters
+ *
+ * Returns:
+ * an input range of `dchar`s
+ *
+ * See_Also:
+ * $(LREF toUpper), $(LREF toLower)
+ */
+
+auto asLowerCase(Range)(Range str)
+if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ !isConvertibleToString!Range)
+{
+ static if (ElementEncodingType!Range.sizeof < dchar.sizeof)
+ {
+ import std.utf : byDchar;
+
+ // Decode first
+ return asLowerCase(str.byDchar);
+ }
+ else
+ {
+ static import std.ascii;
+ return toCaser!(LowerTriple, std.ascii.toLower)(str);
+ }
+}
+
+/// ditto
+auto asUpperCase(Range)(Range str)
+if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ !isConvertibleToString!Range)
+{
+ static if (ElementEncodingType!Range.sizeof < dchar.sizeof)
+ {
+ import std.utf : byDchar;
+
+ // Decode first
+ return asUpperCase(str.byDchar);
+ }
+ else
+ {
+ static import std.ascii;
+ return toCaser!(UpperTriple, std.ascii.toUpper)(str);
+ }
+}
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert("hEllo".asUpperCase.equal("HELLO"));
+}
+
+// explicitly undocumented
+auto asLowerCase(Range)(auto ref Range str)
+if (isConvertibleToString!Range)
+{
+ import std.traits : StringTypeOf;
+ return asLowerCase!(StringTypeOf!Range)(str);
+}
+
+// explicitly undocumented
+auto asUpperCase(Range)(auto ref Range str)
+if (isConvertibleToString!Range)
+{
+ import std.traits : StringTypeOf;
+ return asUpperCase!(StringTypeOf!Range)(str);
+}
+
+@safe unittest
+{
+ static struct TestAliasedString
+ {
+ string get() @safe @nogc pure nothrow { return _s; }
+ alias get this;
+ @disable this(this);
+ string _s;
+ }
+
+ static bool testAliasedString(alias func, Args...)(string s, Args args)
+ {
+ import std.algorithm.comparison : equal;
+ auto a = func(TestAliasedString(s), args);
+ auto b = func(s, args);
+ static if (is(typeof(equal(a, b))))
+ {
+ // For ranges, compare contents instead of object identity.
+ return equal(a, b);
+ }
+ else
+ {
+ return a == b;
+ }
+ }
+ assert(testAliasedString!asLowerCase("hEllo"));
+ assert(testAliasedString!asUpperCase("hEllo"));
+ assert(testAliasedString!asCapitalized("hEllo"));
+}
+
+@safe unittest
+{
+ import std.array : array;
+
+ auto a = "HELLo".asLowerCase;
+ auto savea = a.save;
+ auto s = a.array;
+ assert(s == "hello");
+ s = savea.array;
+ assert(s == "hello");
+
+ string[] lower = ["123", "abcфеж", "\u0131\u023f\u03c9", "i\u0307\u1Fe2"];
+ string[] upper = ["123", "ABCФЕЖ", "I\u2c7e\u2126", "\u0130\u03A5\u0308\u0300"];
+
+ foreach (i, slwr; lower)
+ {
+ import std.utf : byChar;
+
+ auto sx = slwr.asUpperCase.byChar.array;
+ assert(sx == toUpper(slwr));
+ auto sy = upper[i].asLowerCase.byChar.array;
+ assert(sy == toLower(upper[i]));
+ }
+
+ // Not necessary to call r.front
+ for (auto r = lower[3].asUpperCase; !r.empty; r.popFront())
+ {
+ }
+
+ import std.algorithm.comparison : equal;
+
+ "HELLo"w.asLowerCase.equal("hello"d);
+ "HELLo"w.asUpperCase.equal("HELLO"d);
+ "HELLo"d.asLowerCase.equal("hello"d);
+ "HELLo"d.asUpperCase.equal("HELLO"d);
+
+ import std.utf : byChar;
+ assert(toLower("\u1Fe2") == asLowerCase("\u1Fe2").byChar.array);
+}
+
+// generic capitalizer on whole range, returns range
+private auto toCapitalizer(alias indexFnUpper, uint maxIdxUpper, alias tableFnUpper,
+ Range)(Range str)
+ // Accept range of dchar's
+if (isInputRange!Range &&
+ isSomeChar!(ElementEncodingType!Range) &&
+ ElementEncodingType!Range.sizeof == dchar.sizeof)
+{
+ static struct ToCapitalizerImpl
+ {
+ @property bool empty()
+ {
+ return lower ? lwr.empty : !nLeft && r.empty;
+ }
+
+ @property auto front()
+ {
+ if (lower)
+ return lwr.front;
+
+ if (!nLeft)
+ {
+ immutable dchar c = r.front;
+ const idx = indexFnUpper(c);
+ if (idx == ushort.max)
+ {
+ buf[0] = c;
+ nLeft = 1;
+ }
+ else if (idx < maxIdxUpper)
+ {
+ buf[0] = tableFnUpper(idx);
+ nLeft = 1;
+ }
+ else
+ {
+ immutable val = tableFnUpper(idx);
+ // unpack length + codepoint
+ nLeft = val >> 24;
+ if (nLeft == 0)
+ nLeft = 1;
+ assert(nLeft <= buf.length);
+ buf[nLeft - 1] = cast(dchar)(val & 0xFF_FFFF);
+ foreach (j; 1 .. nLeft)
+ buf[nLeft - j - 1] = tableFnUpper(idx + j);
+ }
+ }
+ return buf[nLeft - 1];
+ }
+
+ void popFront()
+ {
+ if (lower)
+ lwr.popFront();
+ else
+ {
+ if (!nLeft)
+ front;
+ assert(nLeft);
+ --nLeft;
+ if (!nLeft)
+ {
+ r.popFront();
+ lwr = r.asLowerCase();
+ lower = true;
+ }
+ }
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property auto save()
+ {
+ auto ret = this;
+ ret.r = r.save;
+ ret.lwr = lwr.save;
+ return ret;
+ }
+ }
+
+ private:
+ Range r;
+ typeof(r.asLowerCase) lwr; // range representing the lower case rest of string
+ bool lower = false; // false for first character, true for rest of string
+ dchar[3] buf = void;
+ uint nLeft = 0;
+ }
+
+ return ToCapitalizerImpl(str);
+}
+
+/*********************
+ * Capitalize an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * or string, meaning convert the first
+ * character to upper case and subsequent characters to lower case.
+ *
+ * Does not allocate memory.
+ * Characters in UTF-8 or UTF-16 format that cannot be decoded
+ * are treated as $(REF replacementDchar, std,utf).
+ *
+ * Params:
+ * str = string or range of characters
+ *
+ * Returns:
+ * an InputRange of dchars
+ *
+ * See_Also:
+ * $(LREF toUpper), $(LREF toLower)
+ * $(LREF asUpperCase), $(LREF asLowerCase)
+ */
+
+auto asCapitalized(Range)(Range str)
+if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ !isConvertibleToString!Range)
+{
+ static if (ElementEncodingType!Range.sizeof < dchar.sizeof)
+ {
+ import std.utf : byDchar;
+
+ // Decode first
+ return toCapitalizer!UpperTriple(str.byDchar);
+ }
+ else
+ {
+ return toCapitalizer!UpperTriple(str);
+ }
+}
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert("hEllo".asCapitalized.equal("Hello"));
+}
+
+auto asCapitalized(Range)(auto ref Range str)
+if (isConvertibleToString!Range)
+{
+ import std.traits : StringTypeOf;
+ return asCapitalized!(StringTypeOf!Range)(str);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ auto r = "hEllo".asCapitalized();
+ assert(r.front == 'H');
+}
+
+@safe unittest
+{
+ import std.array : array;
+
+ auto a = "hELLo".asCapitalized;
+ auto savea = a.save;
+ auto s = a.array;
+ assert(s == "Hello");
+ s = savea.array;
+ assert(s == "Hello");
+
+ string[2][] cases =
+ [
+ ["", ""],
+ ["h", "H"],
+ ["H", "H"],
+ ["3", "3"],
+ ["123", "123"],
+ ["h123A", "H123a"],
+ ["феж", "Феж"],
+ ["\u1Fe2", "\u03a5\u0308\u0300"],
+ ];
+
+ foreach (i; 0 .. cases.length)
+ {
+ import std.utf : byChar;
+
+ auto r = cases[i][0].asCapitalized.byChar.array;
+ auto result = cases[i][1];
+ assert(r == result);
+ }
+
+ // Don't call r.front
+ for (auto r = "\u1Fe2".asCapitalized; !r.empty; r.popFront())
+ {
+ }
+
+ import std.algorithm.comparison : equal;
+
+ "HELLo"w.asCapitalized.equal("Hello"d);
+ "hElLO"w.asCapitalized.equal("Hello"d);
+ "hello"d.asCapitalized.equal("Hello"d);
+ "HELLO"d.asCapitalized.equal("Hello"d);
+
+ import std.utf : byChar;
+ assert(asCapitalized("\u0130").byChar.array == asUpperCase("\u0130").byChar.array);
+}
+
+// TODO: helper, I wish std.utf was more flexible (and stright)
+private size_t encodeTo(scope char[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc
+{
+ if (c <= 0x7F)
+ {
+ buf[idx] = cast(char) c;
+ idx++;
+ }
+ else if (c <= 0x7FF)
+ {
+ buf[idx] = cast(char)(0xC0 | (c >> 6));
+ buf[idx+1] = cast(char)(0x80 | (c & 0x3F));
+ idx += 2;
+ }
+ else if (c <= 0xFFFF)
+ {
+ buf[idx] = cast(char)(0xE0 | (c >> 12));
+ buf[idx+1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[idx+2] = cast(char)(0x80 | (c & 0x3F));
+ idx += 3;
+ }
+ else if (c <= 0x10FFFF)
+ {
+ buf[idx] = cast(char)(0xF0 | (c >> 18));
+ buf[idx+1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
+ buf[idx+2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[idx+3] = cast(char)(0x80 | (c & 0x3F));
+ idx += 4;
+ }
+ else
+ assert(0);
+ return idx;
+}
+
+@safe unittest
+{
+ char[] s = "abcd".dup;
+ size_t i = 0;
+ i = encodeTo(s, i, 'X');
+ assert(s == "Xbcd");
+
+ i = encodeTo(s, i, cast(dchar)'\u00A9');
+ assert(s == "X\xC2\xA9d");
+}
+
+// TODO: helper, I wish std.utf was more flexible (and stright)
+private size_t encodeTo(scope wchar[] buf, size_t idx, dchar c) @trusted pure
+{
+ import std.utf : UTFException;
+ if (c <= 0xFFFF)
+ {
+ if (0xD800 <= c && c <= 0xDFFF)
+ throw (new UTFException("Encoding an isolated surrogate code point in UTF-16")).setSequence(c);
+ buf[idx] = cast(wchar) c;
+ idx++;
+ }
+ else if (c <= 0x10FFFF)
+ {
+ buf[idx] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ buf[idx+1] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00);
+ idx += 2;
+ }
+ else
+ assert(0);
+ return idx;
+}
+
+private size_t encodeTo(scope dchar[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc
+{
+ buf[idx] = c;
+ idx++;
+ return idx;
+}
+
+private void toCaseInPlace(alias indexFn, uint maxIdx, alias tableFn, C)(ref C[] s) @trusted pure
+if (is(C == char) || is(C == wchar) || is(C == dchar))
+{
+ import std.utf : decode, codeLength;
+ size_t curIdx = 0;
+ size_t destIdx = 0;
+ alias slowToCase = toCaseInPlaceAlloc!(indexFn, maxIdx, tableFn);
+ size_t lastUnchanged = 0;
+ // in-buffer move of bytes to a new start index
+ // the trick is that it may not need to copy at all
+ static size_t moveTo(C[] str, size_t dest, size_t from, size_t to)
+ {
+ // Interestingly we may just bump pointer for a while
+ // then have to copy if a re-cased char was smaller the original
+ // later we may regain pace with char that got bigger
+ // In the end it sometimes flip-flops between the 2 cases below
+ if (dest == from)
+ return to;
+ // got to copy
+ foreach (C c; str[from .. to])
+ str[dest++] = c;
+ return dest;
+ }
+ while (curIdx != s.length)
+ {
+ size_t startIdx = curIdx;
+ immutable ch = decode(s, curIdx);
+ // TODO: special case for ASCII
+ immutable caseIndex = indexFn(ch);
+ if (caseIndex == ushort.max) // unchanged, skip over
+ {
+ continue;
+ }
+ else if (caseIndex < maxIdx) // 1:1 codepoint mapping
+ {
+ // previous cased chars had the same length as uncased ones
+ // thus can just adjust pointer
+ destIdx = moveTo(s, destIdx, lastUnchanged, startIdx);
+ lastUnchanged = curIdx;
+ immutable cased = tableFn(caseIndex);
+ immutable casedLen = codeLength!C(cased);
+ if (casedLen + destIdx > curIdx) // no place to fit cased char
+ {
+ // switch to slow codepath, where we allocate
+ return slowToCase(s, startIdx, destIdx);
+ }
+ else
+ {
+ destIdx = encodeTo(s, destIdx, cased);
+ }
+ }
+ else // 1:m codepoint mapping, slow codepath
+ {
+ destIdx = moveTo(s, destIdx, lastUnchanged, startIdx);
+ lastUnchanged = curIdx;
+ return slowToCase(s, startIdx, destIdx);
+ }
+ assert(destIdx <= curIdx);
+ }
+ if (lastUnchanged != s.length)
+ {
+ destIdx = moveTo(s, destIdx, lastUnchanged, s.length);
+ }
+ s = s[0 .. destIdx];
+}
+
+// helper to precalculate size of case-converted string
+private template toCaseLength(alias indexFn, uint maxIdx, alias tableFn)
+{
+ size_t toCaseLength(C)(const scope C[] str)
+ {
+ import std.utf : decode, codeLength;
+ size_t codeLen = 0;
+ size_t lastNonTrivial = 0;
+ size_t curIdx = 0;
+ while (curIdx != str.length)
+ {
+ immutable startIdx = curIdx;
+ immutable ch = decode(str, curIdx);
+ immutable ushort caseIndex = indexFn(ch);
+ if (caseIndex == ushort.max)
+ continue;
+ else if (caseIndex < maxIdx)
+ {
+ codeLen += startIdx - lastNonTrivial;
+ lastNonTrivial = curIdx;
+ immutable cased = tableFn(caseIndex);
+ codeLen += codeLength!C(cased);
+ }
+ else
+ {
+ codeLen += startIdx - lastNonTrivial;
+ lastNonTrivial = curIdx;
+ immutable val = tableFn(caseIndex);
+ immutable len = val >> 24;
+ immutable dchar cased = val & 0xFF_FFFF;
+ codeLen += codeLength!C(cased);
+ foreach (j; caseIndex+1 .. caseIndex+len)
+ codeLen += codeLength!C(tableFn(j));
+ }
+ }
+ if (lastNonTrivial != str.length)
+ codeLen += str.length - lastNonTrivial;
+ return codeLen;
+ }
+}
+
+@safe unittest
+{
+ alias toLowerLength = toCaseLength!(LowerTriple);
+ assert(toLowerLength("abcd") == 4);
+ assert(toLowerLength("аБВгд456") == 10+3);
+}
+
+// slower code path that preallocates and then copies
+// case-converted stuf to the new string
+private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn)
+{
+ void toCaseInPlaceAlloc(C)(ref C[] s, size_t curIdx,
+ size_t destIdx) @trusted pure
+ if (is(C == char) || is(C == wchar) || is(C == dchar))
+ {
+ import std.utf : decode;
+ alias caseLength = toCaseLength!(indexFn, maxIdx, tableFn);
+ auto trueLength = destIdx + caseLength(s[curIdx..$]);
+ C[] ns = new C[trueLength];
+ ns[0 .. destIdx] = s[0 .. destIdx];
+ size_t lastUnchanged = curIdx;
+ while (curIdx != s.length)
+ {
+ immutable startIdx = curIdx; // start of current codepoint
+ immutable ch = decode(s, curIdx);
+ immutable caseIndex = indexFn(ch);
+ if (caseIndex == ushort.max) // skip over
+ {
+ continue;
+ }
+ else if (caseIndex < maxIdx) // 1:1 codepoint mapping
+ {
+ immutable cased = tableFn(caseIndex);
+ auto toCopy = startIdx - lastUnchanged;
+ ns[destIdx .. destIdx+toCopy] = s[lastUnchanged .. startIdx];
+ lastUnchanged = curIdx;
+ destIdx += toCopy;
+ destIdx = encodeTo(ns, destIdx, cased);
+ }
+ else // 1:m codepoint mapping, slow codepath
+ {
+ auto toCopy = startIdx - lastUnchanged;
+ ns[destIdx .. destIdx+toCopy] = s[lastUnchanged .. startIdx];
+ lastUnchanged = curIdx;
+ destIdx += toCopy;
+ auto val = tableFn(caseIndex);
+ // unpack length + codepoint
+ immutable uint len = val >> 24;
+ destIdx = encodeTo(ns, destIdx, cast(dchar)(val & 0xFF_FFFF));
+ foreach (j; caseIndex+1 .. caseIndex+len)
+ destIdx = encodeTo(ns, destIdx, tableFn(j));
+ }
+ }
+ if (lastUnchanged != s.length)
+ {
+ auto toCopy = s.length - lastUnchanged;
+ ns[destIdx .. destIdx+toCopy] = s[lastUnchanged..$];
+ destIdx += toCopy;
+ }
+ assert(ns.length == destIdx);
+ s = ns;
+ }
+}
+
+/++
+ Converts `s` to lowercase (by performing Unicode lowercase mapping) in place.
+ For a few characters string length may increase after the transformation,
+ in such a case the function reallocates exactly once.
+ If `s` does not have any uppercase characters, then `s` is unaltered.
++/
+void toLowerInPlace(C)(ref C[] s) @trusted pure
+if (is(C == char) || is(C == wchar) || is(C == dchar))
+{
+ toCaseInPlace!(LowerTriple)(s);
+}
+// overloads for the most common cases to reduce compile time
+@safe pure /*TODO nothrow*/
+{
+ void toLowerInPlace(ref char[] s)
+ { toLowerInPlace!char(s); }
+ void toLowerInPlace(ref wchar[] s)
+ { toLowerInPlace!wchar(s); }
+ void toLowerInPlace(ref dchar[] s)
+ { toLowerInPlace!dchar(s); }
+}
+
+/++
+ Converts `s` to uppercase (by performing Unicode uppercase mapping) in place.
+ For a few characters string length may increase after the transformation,
+ in such a case the function reallocates exactly once.
+ If `s` does not have any lowercase characters, then `s` is unaltered.
++/
+void toUpperInPlace(C)(ref C[] s) @trusted pure
+if (is(C == char) || is(C == wchar) || is(C == dchar))
+{
+ toCaseInPlace!(UpperTriple)(s);
+}
+// overloads for the most common cases to reduce compile time/code size
+@safe pure /*TODO nothrow*/
+{
+ void toUpperInPlace(ref char[] s)
+ { toUpperInPlace!char(s); }
+ void toUpperInPlace(ref wchar[] s)
+ { toUpperInPlace!wchar(s); }
+ void toUpperInPlace(ref dchar[] s)
+ { toUpperInPlace!dchar(s); }
+}
+
+/++
+ If `c` is a Unicode uppercase $(CHARACTER), then its lowercase equivalent
+ is returned. Otherwise `c` is returned.
+
+ Warning: certain alphabets like German and Greek have no 1:1
+ upper-lower mapping. Use overload of toLower which takes full string instead.
++/
+@safe pure nothrow @nogc
+dchar toLower(dchar c)
+{
+ // optimize ASCII case
+ if (c < 0xAA)
+ {
+ if (c < 'A')
+ return c;
+ if (c <= 'Z')
+ return c + 32;
+ return c;
+ }
+ size_t idx = toLowerSimpleIndex(c);
+ if (idx != ushort.max)
+ {
+ return toLowerTab(idx);
+ }
+ return c;
+}
+
+/++
+ Creates a new array which is identical to `s` except that all of its
+ characters are converted to lowercase (by preforming Unicode lowercase mapping).
+ If none of `s` characters were affected, then `s` itself is returned if `s` is a
+ `string`-like type.
+
+ Params:
+ s = A $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives)
+ of characters
+ Returns:
+ An array with the same element type as `s`.
++/
+ElementEncodingType!S[] toLower(S)(S s)
+if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S)))
+{
+ static import std.ascii;
+
+ static if (isSomeString!S)
+ return () @trusted { return toCase!(LowerTriple, std.ascii.toLower)(s); } ();
+ else
+ return toCase!(LowerTriple, std.ascii.toLower)(s);
+}
+
+// overloads for the most common cases to reduce compile time
+@safe pure /*TODO nothrow*/
+{
+ string toLower(string s)
+ { return toLower!string(s); }
+ wstring toLower(wstring s)
+ { return toLower!wstring(s); }
+ dstring toLower(dstring s)
+ { return toLower!dstring(s); }
+
+ @safe unittest
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=16663
+
+ static struct String
+ {
+ string data;
+ alias data this;
+ }
+
+ void foo()
+ {
+ auto u = toLower(String(""));
+ }
+ }
+}
+
+
+@safe unittest
+{
+ static import std.ascii;
+ import std.format : format;
+ foreach (ch; 0 .. 0x80)
+ assert(std.ascii.toLower(ch) == toLower(ch));
+ assert(toLower('Я') == 'Ñ');
+ assert(toLower('Δ') == 'δ');
+ foreach (ch; unicode.upperCase.byCodepoint)
+ {
+ dchar low = ch.toLower();
+ assert(low == ch || isLower(low), format("%s -> %s", ch, low));
+ }
+ assert(toLower("ÐЯ") == "аÑ");
+
+ assert("\u1E9E".toLower == "\u00df");
+ assert("\u00df".toUpper == "SS");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=9629
+@safe unittest
+{
+ wchar[] test = "hello þ world"w.dup;
+ auto piece = test[6 .. 7];
+ toUpperInPlace(piece);
+ assert(test == "hello Þ world");
+}
+
+
+@safe unittest
+{
+ import std.algorithm.comparison : cmp;
+ string s1 = "FoL";
+ string s2 = toLower(s1);
+ assert(cmp(s2, "fol") == 0, s2);
+ assert(s2 != s1);
+
+ char[] s3 = s1.dup;
+ toLowerInPlace(s3);
+ assert(s3 == s2);
+
+ s1 = "A\u0100B\u0101d";
+ s2 = toLower(s1);
+ s3 = s1.dup;
+ assert(cmp(s2, "a\u0101b\u0101d") == 0);
+ assert(s2 !is s1);
+ toLowerInPlace(s3);
+ assert(s3 == s2);
+
+ s1 = "A\u0460B\u0461d";
+ s2 = toLower(s1);
+ s3 = s1.dup;
+ assert(cmp(s2, "a\u0461b\u0461d") == 0);
+ assert(s2 !is s1);
+ toLowerInPlace(s3);
+ assert(s3 == s2);
+
+ s1 = "\u0130";
+ s2 = toLower(s1);
+ s3 = s1.dup;
+ assert(s2 == "i\u0307");
+ assert(s2 !is s1);
+ toLowerInPlace(s3);
+ assert(s3 == s2);
+
+ // Test on wchar and dchar strings.
+ assert(toLower("Some String"w) == "some string"w);
+ assert(toLower("Some String"d) == "some string"d);
+
+ // https://issues.dlang.org/show_bug.cgi?id=12455
+ dchar c = 'Ä°'; // '\U0130' LATIN CAPITAL LETTER I WITH DOT ABOVE
+ assert(isUpper(c));
+ assert(toLower(c) == 'i');
+ // extends on https://issues.dlang.org/show_bug.cgi?id=12455 report
+ // check simple-case toUpper too
+ c = '\u1f87';
+ assert(isLower(c));
+ assert(toUpper(c) == '\u1F8F');
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : cmp, equal;
+ import std.utf : byCodeUnit;
+ auto r1 = "FoL".byCodeUnit;
+ assert(r1.toLower.cmp("fol") == 0);
+ auto r2 = "A\u0460B\u0461d".byCodeUnit;
+ assert(r2.toLower.cmp("a\u0461b\u0461d") == 0);
+}
+
+/++
+ If `c` is a Unicode lowercase $(CHARACTER), then its uppercase equivalent
+ is returned. Otherwise `c` is returned.
+
+ Warning:
+ Certain alphabets like German and Greek have no 1:1
+ upper-lower mapping. Use overload of toUpper which takes full string instead.
+
+ toUpper can be used as an argument to $(REF map, std,algorithm,iteration)
+ to produce an algorithm that can convert a range of characters to upper case
+ without allocating memory.
+ A string can then be produced by using $(REF copy, std,algorithm,mutation)
+ to send it to an $(REF appender, std,array).
++/
+@safe pure nothrow @nogc
+dchar toUpper(dchar c)
+{
+ // optimize ASCII case
+ if (c < 0xAA)
+ {
+ if (c < 'a')
+ return c;
+ if (c <= 'z')
+ return c - 32;
+ return c;
+ }
+ size_t idx = toUpperSimpleIndex(c);
+ if (idx != ushort.max)
+ {
+ return toUpperTab(idx);
+ }
+ return c;
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.algorithm.mutation : copy;
+ import std.array : appender;
+
+ auto abuf = appender!(char[])();
+ "hello".map!toUpper.copy(abuf);
+ assert(abuf.data == "HELLO");
+}
+
+@safe unittest
+{
+ static import std.ascii;
+ import std.format : format;
+ foreach (ch; 0 .. 0x80)
+ assert(std.ascii.toUpper(ch) == toUpper(ch));
+ assert(toUpper('Ñ') == 'Я');
+ assert(toUpper('δ') == 'Δ');
+ auto title = unicode.Titlecase_Letter;
+ foreach (ch; unicode.lowerCase.byCodepoint)
+ {
+ dchar up = ch.toUpper();
+ assert(up == ch || isUpper(up) || title[up],
+ format("%x -> %x", ch, up));
+ }
+}
+
+/++
+ Allocates a new array which is identical to `s` except that all of its
+ characters are converted to uppercase (by preforming Unicode uppercase mapping).
+ If none of `s` characters were affected, then `s` itself is returned if `s`
+ is a `string`-like type.
+
+ Params:
+ s = A $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives)
+ of characters
+ Returns:
+ An new array with the same element type as `s`.
++/
+ElementEncodingType!S[] toUpper(S)(S s)
+if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S)))
+{
+ static import std.ascii;
+
+ static if (isSomeString!S)
+ return () @trusted { return toCase!(UpperTriple, std.ascii.toUpper)(s); } ();
+ else
+ return toCase!(UpperTriple, std.ascii.toUpper)(s);
+}
+
+// overloads for the most common cases to reduce compile time
+@safe pure /*TODO nothrow*/
+{
+ string toUpper(string s)
+ { return toUpper!string(s); }
+ wstring toUpper(wstring s)
+ { return toUpper!wstring(s); }
+ dstring toUpper(dstring s)
+ { return toUpper!dstring(s); }
+
+ @safe unittest
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=16663
+
+ static struct String
+ {
+ string data;
+ alias data this;
+ }
+
+ void foo()
+ {
+ auto u = toUpper(String(""));
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : cmp;
+
+ string s1 = "FoL";
+ string s2;
+ char[] s3;
+
+ s2 = toUpper(s1);
+ s3 = s1.dup; toUpperInPlace(s3);
+ assert(s3 == s2, s3);
+ assert(cmp(s2, "FOL") == 0);
+ assert(s2 !is s1);
+
+ s1 = "a\u0100B\u0101d";
+ s2 = toUpper(s1);
+ s3 = s1.dup; toUpperInPlace(s3);
+ assert(s3 == s2);
+ assert(cmp(s2, "A\u0100B\u0100D") == 0);
+ assert(s2 !is s1);
+
+ s1 = "a\u0460B\u0461d";
+ s2 = toUpper(s1);
+ s3 = s1.dup; toUpperInPlace(s3);
+ assert(s3 == s2);
+ assert(cmp(s2, "A\u0460B\u0460D") == 0);
+ assert(s2 !is s1);
+}
+
+@system unittest
+{
+ static void doTest(C)(const(C)[] s, const(C)[] trueUp, const(C)[] trueLow)
+ {
+ import std.format : format;
+ string diff = "src: %( %x %)\nres: %( %x %)\ntru: %( %x %)";
+ auto low = s.toLower() , up = s.toUpper();
+ auto lowInp = s.dup, upInp = s.dup;
+ lowInp.toLowerInPlace();
+ upInp.toUpperInPlace();
+ assert(low == trueLow, format(diff, low, trueLow));
+ assert(up == trueUp, format(diff, up, trueUp));
+ assert(lowInp == trueLow,
+ format(diff, cast(ubyte[]) s, cast(ubyte[]) lowInp, cast(ubyte[]) trueLow));
+ assert(upInp == trueUp,
+ format(diff, cast(ubyte[]) s, cast(ubyte[]) upInp, cast(ubyte[]) trueUp));
+ }
+ static foreach (S; AliasSeq!(dstring, wstring, string))
+ {{
+
+ S easy = "123";
+ S good = "abCФеж";
+ S awful = "\u0131\u023f\u2126";
+ S wicked = "\u0130\u1FE2";
+ auto options = [easy, good, awful, wicked];
+ S[] lower = ["123", "abcфеж", "\u0131\u023f\u03c9", "i\u0307\u1Fe2"];
+ S[] upper = ["123", "ABCФЕЖ", "I\u2c7e\u2126", "\u0130\u03A5\u0308\u0300"];
+
+ foreach (val; [easy, good])
+ {
+ auto e = val.dup;
+ auto g = e;
+ e.toUpperInPlace();
+ assert(e is g);
+ e.toLowerInPlace();
+ assert(e is g);
+ }
+ foreach (i, v; options)
+ {
+ doTest(v, upper[i], lower[i]);
+ }
+
+ // a few combinatorial runs
+ foreach (i; 0 .. options.length)
+ foreach (j; i .. options.length)
+ foreach (k; j .. options.length)
+ {
+ auto sample = options[i] ~ options[j] ~ options[k];
+ auto sample2 = options[k] ~ options[j] ~ options[i];
+ doTest(sample, upper[i] ~ upper[j] ~ upper[k],
+ lower[i] ~ lower[j] ~ lower[k]);
+ doTest(sample2, upper[k] ~ upper[j] ~ upper[i],
+ lower[k] ~ lower[j] ~ lower[i]);
+ }
+ }}
+}
+
+// test random access ranges
+@safe pure unittest
+{
+ import std.algorithm.comparison : cmp;
+ import std.utf : byCodeUnit;
+ auto s1 = "FoL".byCodeUnit;
+ assert(s1.toUpper.cmp("FOL") == 0);
+ auto s2 = "a\u0460B\u0461d".byCodeUnit;
+ assert(s2.toUpper.cmp("A\u0460B\u0460D") == 0);
+}
+
+/++
+ Returns whether `c` is a Unicode alphabetic $(CHARACTER)
+ (general Unicode category: Alphabetic).
++/
+@safe pure nothrow @nogc
+bool isAlpha(dchar c)
+{
+ // optimization
+ if (c < 0xAA)
+ {
+ size_t x = c - 'A';
+ if (x <= 'Z' - 'A')
+ return true;
+ else
+ {
+ x = c - 'a';
+ if (x <= 'z'-'a')
+ return true;
+ }
+ return false;
+ }
+
+ return alphaTrie[c];
+}
+
+@safe unittest
+{
+ auto alpha = unicode("Alphabetic");
+ foreach (ch; alpha.byCodepoint)
+ assert(isAlpha(ch));
+ foreach (ch; 0 .. 0x4000)
+ assert((ch in alpha) == isAlpha(ch));
+}
+
+
+/++
+ Returns whether `c` is a Unicode mark
+ (general Unicode category: Mn, Me, Mc).
++/
+@safe pure nothrow @nogc
+bool isMark(dchar c)
+{
+ return markTrie[c];
+}
+
+@safe unittest
+{
+ auto mark = unicode("Mark");
+ foreach (ch; mark.byCodepoint)
+ assert(isMark(ch));
+ foreach (ch; 0 .. 0x4000)
+ assert((ch in mark) == isMark(ch));
+}
+
+/++
+ Returns whether `c` is a Unicode numerical $(CHARACTER)
+ (general Unicode category: Nd, Nl, No).
++/
+@safe pure nothrow @nogc
+bool isNumber(dchar c)
+{
+ // optimization for ascii case
+ if (c <= 0x7F)
+ {
+ return c >= '0' && c <= '9';
+ }
+ else
+ {
+ return numberTrie[c];
+ }
+}
+
+@safe unittest
+{
+ auto n = unicode("N");
+ foreach (ch; n.byCodepoint)
+ assert(isNumber(ch));
+ foreach (ch; 0 .. 0x4000)
+ assert((ch in n) == isNumber(ch));
+}
+
+/++
+ Returns whether `c` is a Unicode alphabetic $(CHARACTER) or number.
+ (general Unicode category: Alphabetic, Nd, Nl, No).
+
+ Params:
+ c = any Unicode character
+ Returns:
+ `true` if the character is in the Alphabetic, Nd, Nl, or No Unicode
+ categories
++/
+@safe pure nothrow @nogc
+bool isAlphaNum(dchar c)
+{
+ static import std.ascii;
+
+ // optimization for ascii case
+ if (std.ascii.isASCII(c))
+ {
+ return std.ascii.isAlphaNum(c);
+ }
+ else
+ {
+ return isAlpha(c) || isNumber(c);
+ }
+}
+
+@safe unittest
+{
+ auto n = unicode("N");
+ auto alpha = unicode("Alphabetic");
+
+ foreach (ch; n.byCodepoint)
+ assert(isAlphaNum(ch));
+
+ foreach (ch; alpha.byCodepoint)
+ assert(isAlphaNum(ch));
+
+ foreach (ch; 0 .. 0x4000)
+ {
+ assert(((ch in n) || (ch in alpha)) == isAlphaNum(ch));
+ }
+}
+
+/++
+ Returns whether `c` is a Unicode punctuation $(CHARACTER)
+ (general Unicode category: Pd, Ps, Pe, Pc, Po, Pi, Pf).
++/
+@safe pure nothrow @nogc
+bool isPunctuation(dchar c)
+{
+ static import std.ascii;
+
+ // optimization for ascii case
+ if (c <= 0x7F)
+ {
+ return std.ascii.isPunctuation(c);
+ }
+ else
+ {
+ return punctuationTrie[c];
+ }
+}
+
+@safe unittest
+{
+ assert(isPunctuation('\u0021'));
+ assert(isPunctuation('\u0028'));
+ assert(isPunctuation('\u0029'));
+ assert(isPunctuation('\u002D'));
+ assert(isPunctuation('\u005F'));
+ assert(isPunctuation('\u00AB'));
+ assert(isPunctuation('\u00BB'));
+ foreach (ch; unicode("P").byCodepoint)
+ assert(isPunctuation(ch));
+}
+
+/++
+ Returns whether `c` is a Unicode symbol $(CHARACTER)
+ (general Unicode category: Sm, Sc, Sk, So).
++/
+@safe pure nothrow @nogc
+bool isSymbol(dchar c)
+{
+ return symbolTrie[c];
+}
+
+@safe unittest
+{
+ import std.format : format;
+ assert(isSymbol('\u0024'));
+ assert(isSymbol('\u002B'));
+ assert(isSymbol('\u005E'));
+ assert(isSymbol('\u00A6'));
+ foreach (ch; unicode("S").byCodepoint)
+ assert(isSymbol(ch), format("%04x", ch));
+}
+
+/++
+ Returns whether `c` is a Unicode space $(CHARACTER)
+ (general Unicode category: Zs)
+ Note: This doesn't include '\n', '\r', \t' and other non-space $(CHARACTER).
+ For commonly used less strict semantics see $(LREF isWhite).
++/
+@safe pure nothrow @nogc
+bool isSpace(dchar c)
+{
+ import std.internal.unicode_tables : isSpaceGen; // generated file
+ return isSpaceGen(c);
+}
+
+@safe unittest
+{
+ assert(isSpace('\u0020'));
+ auto space = unicode.Zs;
+ foreach (ch; space.byCodepoint)
+ assert(isSpace(ch));
+ foreach (ch; 0 .. 0x1000)
+ assert(isSpace(ch) == space[ch]);
+}
+
+
+/++
+ Returns whether `c` is a Unicode graphical $(CHARACTER)
+ (general Unicode category: L, M, N, P, S, Zs).
+
++/
+@safe pure nothrow @nogc
+bool isGraphical(dchar c)
+{
+ return graphicalTrie[c];
+}
+
+
+@safe unittest
+{
+ auto set = unicode("Graphical");
+ import std.format : format;
+ foreach (ch; set.byCodepoint)
+ assert(isGraphical(ch), format("%4x", ch));
+ foreach (ch; 0 .. 0x4000)
+ assert((ch in set) == isGraphical(ch));
+}
+
+
+/++
+ Returns whether `c` is a Unicode control $(CHARACTER)
+ (general Unicode category: Cc).
++/
+@safe pure nothrow @nogc
+bool isControl(dchar c)
+{
+ import std.internal.unicode_tables : isControlGen; // generated file
+ return isControlGen(c);
+}
+
+@safe unittest
+{
+ assert(isControl('\u0000'));
+ assert(isControl('\u0081'));
+ assert(!isControl('\u0100'));
+ auto cc = unicode.Cc;
+ foreach (ch; cc.byCodepoint)
+ assert(isControl(ch));
+ foreach (ch; 0 .. 0x1000)
+ assert(isControl(ch) == cc[ch]);
+}
+
+
+/++
+ Returns whether `c` is a Unicode formatting $(CHARACTER)
+ (general Unicode category: Cf).
++/
+@safe pure nothrow @nogc
+bool isFormat(dchar c)
+{
+ import std.internal.unicode_tables : isFormatGen; // generated file
+ return isFormatGen(c);
+}
+
+
+@safe unittest
+{
+ assert(isFormat('\u00AD'));
+ foreach (ch; unicode("Format").byCodepoint)
+ assert(isFormat(ch));
+}
+
+// code points for private use, surrogates are not likely to change in near feature
+// if need be they can be generated from unicode data as well
+
+/++
+ Returns whether `c` is a Unicode Private Use $(CODEPOINT)
+ (general Unicode category: Co).
++/
+@safe pure nothrow @nogc
+bool isPrivateUse(dchar c)
+{
+ return (0x00_E000 <= c && c <= 0x00_F8FF)
+ || (0x0F_0000 <= c && c <= 0x0F_FFFD)
+ || (0x10_0000 <= c && c <= 0x10_FFFD);
+}
+
+/++
+ Returns whether `c` is a Unicode surrogate $(CODEPOINT)
+ (general Unicode category: Cs).
++/
+@safe pure nothrow @nogc
+bool isSurrogate(dchar c)
+{
+ return (0xD800 <= c && c <= 0xDFFF);
+}
+
+/++
+ Returns whether `c` is a Unicode high surrogate (lead surrogate).
++/
+@safe pure nothrow @nogc
+bool isSurrogateHi(dchar c)
+{
+ return (0xD800 <= c && c <= 0xDBFF);
+}
+
+/++
+ Returns whether `c` is a Unicode low surrogate (trail surrogate).
++/
+@safe pure nothrow @nogc
+bool isSurrogateLo(dchar c)
+{
+ return (0xDC00 <= c && c <= 0xDFFF);
+}
+
+/++
+ Returns whether `c` is a Unicode non-character i.e.
+ a $(CODEPOINT) with no assigned abstract character.
+ (general Unicode category: Cn)
++/
+@safe pure nothrow @nogc
+bool isNonCharacter(dchar c)
+{
+ return nonCharacterTrie[c];
+}
+
+@safe unittest
+{
+ auto set = unicode("Cn");
+ foreach (ch; set.byCodepoint)
+ assert(isNonCharacter(ch));
+}
+
+private:
+// load static data from pre-generated tables into usable datastructures
+
+
+@safe auto asSet(const (ubyte)[] compressed) pure
+{
+ return CodepointSet.fromIntervals(decompressIntervals(compressed));
+}
+
+@safe pure nothrow auto asTrie(T...)(const scope TrieEntry!T e)
+{
+ return const(CodepointTrie!T)(e.offsets, e.sizes, e.data);
+}
+
+@safe pure nothrow @nogc @property
+{
+ import std.internal.unicode_tables; // generated file
+
+ // It's important to use auto return here, so that the compiler
+ // only runs semantic on the return type if the function gets
+ // used. Also these are functions rather than templates to not
+ // increase the object size of the caller.
+ auto lowerCaseTrie() { static immutable res = asTrie(lowerCaseTrieEntries); return res; }
+ auto upperCaseTrie() { static immutable res = asTrie(upperCaseTrieEntries); return res; }
+ auto simpleCaseTrie() { static immutable res = asTrie(simpleCaseTrieEntries); return res; }
+ auto fullCaseTrie() { static immutable res = asTrie(fullCaseTrieEntries); return res; }
+ auto alphaTrie() { static immutable res = asTrie(alphaTrieEntries); return res; }
+ auto markTrie() { static immutable res = asTrie(markTrieEntries); return res; }
+ auto numberTrie() { static immutable res = asTrie(numberTrieEntries); return res; }
+ auto punctuationTrie() { static immutable res = asTrie(punctuationTrieEntries); return res; }
+ auto symbolTrie() { static immutable res = asTrie(symbolTrieEntries); return res; }
+ auto graphicalTrie() { static immutable res = asTrie(graphicalTrieEntries); return res; }
+ auto nonCharacterTrie() { static immutable res = asTrie(nonCharacterTrieEntries); return res; }
+
+ //normalization quick-check tables
+ auto nfcQCTrie()
+ {
+ import std.internal.unicode_norm : nfcQCTrieEntries;
+ static immutable res = asTrie(nfcQCTrieEntries);
+ return res;
+ }
+
+ auto nfdQCTrie()
+ {
+ import std.internal.unicode_norm : nfdQCTrieEntries;
+ static immutable res = asTrie(nfdQCTrieEntries);
+ return res;
+ }
+
+ auto nfkcQCTrie()
+ {
+ import std.internal.unicode_norm : nfkcQCTrieEntries;
+ static immutable res = asTrie(nfkcQCTrieEntries);
+ return res;
+ }
+
+ auto nfkdQCTrie()
+ {
+ import std.internal.unicode_norm : nfkdQCTrieEntries;
+ static immutable res = asTrie(nfkdQCTrieEntries);
+ return res;
+ }
+
+ //grapheme breaking algorithm tables
+ auto mcTrie()
+ {
+ import std.internal.unicode_grapheme : mcTrieEntries;
+ static immutable res = asTrie(mcTrieEntries);
+ return res;
+ }
+
+ auto graphemeExtendTrie()
+ {
+ import std.internal.unicode_grapheme : graphemeExtendTrieEntries;
+ static immutable res = asTrie(graphemeExtendTrieEntries);
+ return res;
+ }
+
+ auto hangLV()
+ {
+ import std.internal.unicode_grapheme : hangulLVTrieEntries;
+ static immutable res = asTrie(hangulLVTrieEntries);
+ return res;
+ }
+
+ auto hangLVT()
+ {
+ import std.internal.unicode_grapheme : hangulLVTTrieEntries;
+ static immutable res = asTrie(hangulLVTTrieEntries);
+ return res;
+ }
+
+ // tables below are used for composition/decomposition
+ auto combiningClassTrie()
+ {
+ import std.internal.unicode_comp : combiningClassTrieEntries;
+ static immutable res = asTrie(combiningClassTrieEntries);
+ return res;
+ }
+
+ auto compatMappingTrie()
+ {
+ import std.internal.unicode_decomp : compatMappingTrieEntries;
+ static immutable res = asTrie(compatMappingTrieEntries);
+ return res;
+ }
+
+ auto canonMappingTrie()
+ {
+ import std.internal.unicode_decomp : canonMappingTrieEntries;
+ static immutable res = asTrie(canonMappingTrieEntries);
+ return res;
+ }
+
+ auto compositionJumpTrie()
+ {
+ import std.internal.unicode_comp : compositionJumpTrieEntries;
+ static immutable res = asTrie(compositionJumpTrieEntries);
+ return res;
+ }
+
+ //case conversion tables
+ auto toUpperIndexTrie() { static immutable res = asTrie(toUpperIndexTrieEntries); return res; }
+ auto toLowerIndexTrie() { static immutable res = asTrie(toLowerIndexTrieEntries); return res; }
+ auto toTitleIndexTrie() { static immutable res = asTrie(toTitleIndexTrieEntries); return res; }
+ //simple case conversion tables
+ auto toUpperSimpleIndexTrie() { static immutable res = asTrie(toUpperSimpleIndexTrieEntries); return res; }
+ auto toLowerSimpleIndexTrie() { static immutable res = asTrie(toLowerSimpleIndexTrieEntries); return res; }
+ auto toTitleSimpleIndexTrie() { static immutable res = asTrie(toTitleSimpleIndexTrieEntries); return res; }
+
+}
+
+}// version (!std_uni_bootstrap)
diff --git a/libphobos/src/std/uri.d b/libphobos/src/std/uri.d
index fcc902c8236..bf7cbc06438 100644
--- a/libphobos/src/std/uri.d
+++ b/libphobos/src/std/uri.d
@@ -9,14 +9,14 @@
* Escape sequences consist of $(B %) followed by two hex digits.
*
* See_Also:
- * $(LINK2 http://www.ietf.org/rfc/rfc3986.txt, RFC 3986)<br>
+ * $(LINK2 https://www.ietf.org/rfc/rfc3986.txt, RFC 3986)<br>
* $(LINK2 http://en.wikipedia.org/wiki/Uniform_resource_identifier, Wikipedia)
- * Copyright: Copyright Digital Mars 2000 - 2009.
+ * Copyright: Copyright The D Language Foundation 2000 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright)
- * Source: $(PHOBOSSRC std/_uri.d)
+ * Source: $(PHOBOSSRC std/uri.d)
*/
-/* Copyright Digital Mars 2000 - 2009.
+/* Copyright The D Language Foundation 2000 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -36,6 +36,13 @@ class URIException : Exception
mixin basicExceptionCtors;
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ assertThrown!URIException("%ab".decode);
+}
+
private enum
{
URI_Alpha = 1,
@@ -65,11 +72,8 @@ private immutable ubyte[128] uri_flags = // indexed by character
return uflags;
})();
-private string URI_Encode(dstring str, uint unescapedSet)
+private string URI_Encode(dstring str, uint unescapedSet) @safe pure
{
- import core.exception : OutOfMemoryError;
- import core.stdc.stdlib : alloca;
-
uint j;
uint k;
dchar V;
@@ -77,13 +81,13 @@ private string URI_Encode(dstring str, uint unescapedSet)
// result buffer
char[50] buffer = void;
- char* R;
+ char[] R;
uint Rlen;
uint Rsize; // alloc'd size
immutable len = str.length;
- R = buffer.ptr;
+ R = buffer[];
Rsize = buffer.length;
Rlen = 0;
@@ -95,19 +99,10 @@ private string URI_Encode(dstring str, uint unescapedSet)
{
if (Rlen == Rsize)
{
- char* R2;
+ char[] R2;
Rsize *= 2;
- if (Rsize > 1024)
- {
- R2 = (new char[Rsize]).ptr;
- }
- else
- {
- R2 = cast(char *) alloca(Rsize * char.sizeof);
- if (!R2)
- throw new OutOfMemoryError("Alloca failure");
- }
+ R2 = new char[Rsize];
R2[0 .. Rlen] = R[0 .. Rlen];
R = R2;
}
@@ -155,19 +150,10 @@ private string URI_Encode(dstring str, uint unescapedSet)
if (Rlen + L * 3 > Rsize)
{
- char *R2;
+ char[] R2;
Rsize = 2 * (Rlen + L * 3);
- if (Rsize > 1024)
- {
- R2 = (new char[Rsize]).ptr;
- }
- else
- {
- R2 = cast(char *) alloca(Rsize * char.sizeof);
- if (!R2)
- throw new OutOfMemoryError("Alloca failure");
- }
+ R2 = new char[Rsize];
R2[0 .. Rlen] = R[0 .. Rlen];
R = R2;
}
@@ -186,6 +172,18 @@ private string URI_Encode(dstring str, uint unescapedSet)
return R[0 .. Rlen].idup;
}
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+
+ assert(URI_Encode("", 0) == "");
+ assert(URI_Encode(URI_Decode("%F0%BF%BF%BF", 0), 0) == "%F0%BF%BF%BF");
+ dstring a;
+ a ~= cast(dchar) 0xFFFFFFFF;
+ assertThrown(URI_Encode(a, 0));
+ assert(URI_Encode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0).length == 3 * 60);
+}
+
private uint ascii2hex(dchar c) @nogc @safe pure nothrow
{
return (c <= '9') ? c - '0' :
@@ -193,11 +191,9 @@ private uint ascii2hex(dchar c) @nogc @safe pure nothrow
c - 'a' + 10;
}
-private dstring URI_Decode(Char)(in Char[] uri, uint reservedSet)
+private dstring URI_Decode(Char)(scope const(Char)[] uri, uint reservedSet)
if (isSomeChar!Char)
{
- import core.exception : OutOfMemoryError;
- import core.stdc.stdlib : alloca;
import std.ascii : isHexDigit;
uint j;
@@ -205,25 +201,12 @@ if (isSomeChar!Char)
uint V;
dchar C;
- // Result array, allocated on stack
- dchar* R;
uint Rlen;
-
immutable len = uri.length;
- auto s = uri.ptr;
+ auto s = uri;
- // Preallocate result buffer R guaranteed to be large enough for result
auto Rsize = len;
- if (Rsize > 1024 / dchar.sizeof)
- {
- R = (new dchar[Rsize]).ptr;
- }
- else
- {
- R = cast(dchar *) alloca(Rsize * dchar.sizeof);
- if (!R)
- throw new OutOfMemoryError("Alloca failure");
- }
+ dchar[] R = new dchar[Rsize];
Rlen = 0;
for (k = 0; k != len; k++)
@@ -307,13 +290,28 @@ if (isSomeChar!Char)
return R[0 .. Rlen].idup;
}
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+
+ assert(URI_Decode("", 0) == "");
+ assertThrown!URIException(URI_Decode("%", 0));
+ assertThrown!URIException(URI_Decode("%xx", 0));
+ assertThrown!URIException(URI_Decode("%FF", 0));
+ assertThrown!URIException(URI_Decode("%C0", 0));
+ assertThrown!URIException(URI_Decode("%C0000000", 0));
+ assertThrown!URIException(URI_Decode("%C0%xx0000", 0));
+ assertThrown!URIException(URI_Decode("%C0%C00000", 0));
+ assertThrown!URIException(URI_Decode("%F7%BF%BF%BF", 0));
+ assert(URI_Decode("%23", URI_Hash) == "%23");
+}
+
/*************************************
* Decodes the URI string encodedURI into a UTF-8 string and returns it.
* Escape sequences that resolve to reserved URI characters are not replaced.
* Escape sequences that resolve to the '#' character are not replaced.
*/
-
-string decode(Char)(in Char[] encodedURI)
+string decode(Char)(scope const(Char)[] encodedURI)
if (isSomeChar!Char)
{
import std.algorithm.iteration : each;
@@ -324,12 +322,20 @@ if (isSomeChar!Char)
return r;
}
+///
+@safe unittest
+{
+ assert("foo%20bar".decode == "foo bar");
+ assert("%3C%3E.@.%E2%84%A2".decode == "<>.@.â„¢");
+ assert("foo&/".decode == "foo&/");
+ assert("!@#$&*(".decode == "!@#$&*(");
+}
+
/*******************************
* Decodes the URI string encodedURI into a UTF-8 string and returns it. All
* escape sequences are decoded.
*/
-
-string decodeComponent(Char)(in Char[] encodedURIComponent)
+string decodeComponent(Char)(scope const(Char)[] encodedURIComponent)
if (isSomeChar!Char)
{
import std.algorithm.iteration : each;
@@ -340,12 +346,19 @@ if (isSomeChar!Char)
return r;
}
+///
+@safe unittest
+{
+ assert("foo%2F%26".decodeComponent == "foo/&");
+ assert("dl%C3%A4ng%20r%C3%B6cks".decodeComponent == "dläng röcks");
+ assert("!%40%23%24%25%5E%26*(".decodeComponent == "!@#$%^&*(");
+}
+
/*****************************
* Encodes the UTF-8 string uri into a URI and returns that URI. Any character
* not a valid URI character is escaped. The '#' character is not escaped.
*/
-
-string encode(Char)(in Char[] uri)
+string encode(Char)(scope const(Char)[] uri)
if (isSomeChar!Char)
{
import std.utf : toUTF32;
@@ -353,12 +366,21 @@ if (isSomeChar!Char)
return URI_Encode(s, URI_Reserved | URI_Hash | URI_Alpha | URI_Digit | URI_Mark);
}
+///
+@safe unittest
+{
+ assert("foo bar".encode == "foo%20bar");
+ assert("<>.@.â„¢".encode == "%3C%3E.@.%E2%84%A2");
+ assert("foo/#?a=1&b=2".encode == "foo/#?a=1&b=2");
+ assert("dlang+rocks!".encode == "dlang+rocks!");
+ assert("!@#$%^&*(".encode == "!@#$%25%5E&*(");
+}
+
/********************************
* Encodes the UTF-8 string uriComponent into a URI and returns that URI.
* Any character not a letter, digit, or one of -_.!~*'() is escaped.
*/
-
-string encodeComponent(Char)(in Char[] uriComponent)
+string encodeComponent(Char)(scope const(Char)[] uriComponent)
if (isSomeChar!Char)
{
import std.utf : toUTF32;
@@ -366,6 +388,16 @@ if (isSomeChar!Char)
return URI_Encode(s, URI_Alpha | URI_Digit | URI_Mark);
}
+///
+@safe unittest
+{
+ assert("!@#$%^&*(".encodeComponent == "!%40%23%24%25%5E%26*(");
+ assert("<>.@.â„¢".encodeComponent == "%3C%3E.%40.%E2%84%A2");
+ assert("foo/&".encodeComponent == "foo%2F%26");
+ assert("dläng röcks".encodeComponent == "dl%C3%A4ng%20r%C3%B6cks");
+ assert("dlang+rocks!".encodeComponent == "dlang%2Brocks!");
+}
+
/* Encode associative array using www-form-urlencoding
*
* Params:
@@ -374,13 +406,13 @@ if (isSomeChar!Char)
* Returns:
* A string encoded using www-form-urlencoding.
*/
-package string urlEncode(in string[string] values)
+package string urlEncode(scope string[string] values) @safe pure
{
if (values.length == 0)
return "";
import std.array : Appender;
- import std.format : formattedWrite;
+ import std.format.write : formattedWrite;
Appender!string enc;
enc.reserve(values.length * 128);
@@ -396,7 +428,7 @@ package string urlEncode(in string[string] values)
return enc.data;
}
-@system unittest
+@safe pure unittest
{
// @system because urlEncode -> encodeComponent -> URI_Encode
// URI_Encode uses alloca and pointer slicing
@@ -414,7 +446,7 @@ package string urlEncode(in string[string] values)
* len it does, and s[0 .. len] is the slice of s[] that is that URL
*/
-ptrdiff_t uriLength(Char)(in Char[] s)
+ptrdiff_t uriLength(Char)(scope const(Char)[] s)
if (isSomeChar!Char)
{
/* Must start with one of:
@@ -467,7 +499,7 @@ if (isSomeChar!Char)
}
///
-@safe unittest
+@safe pure unittest
{
string s1 = "http://www.digitalmars.com/~fred/fredsRX.html#foo end!";
assert(uriLength(s1) == 49);
@@ -476,6 +508,11 @@ if (isSomeChar!Char)
assert(uriLength("issue 14924") < 0);
}
+@safe pure nothrow @nogc unittest
+{
+ assert(uriLength("") == -1);
+ assert(uriLength("https://www") == -1);
+}
/***************************
* Does string s[] start with an email address?
@@ -485,13 +522,16 @@ if (isSomeChar!Char)
* References:
* RFC2822
*/
-ptrdiff_t emailLength(Char)(in Char[] s)
+ptrdiff_t emailLength(Char)(scope const(Char)[] s)
if (isSomeChar!Char)
{
import std.ascii : isAlpha, isAlphaNum;
ptrdiff_t i;
+ if (s.length == 0)
+ return -1;
+
if (!isAlpha(s[0]))
return -1;
@@ -534,7 +574,7 @@ if (isSomeChar!Char)
}
///
-@safe unittest
+@safe pure unittest
{
string s1 = "my.e-mail@www.example-domain.com with garbage added";
assert(emailLength(s1) == 32);
@@ -543,8 +583,7 @@ if (isSomeChar!Char)
assert(emailLength("issue 14924") < 0);
}
-
-@system unittest
+@safe pure unittest
{
//@system because of encode -> URI_Encode
debug(uri) writeln("uri.encodeURI.unittest");
@@ -575,8 +614,8 @@ if (isSomeChar!Char)
debug(uri) writeln(result);
import std.meta : AliasSeq;
- foreach (StringType; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
- {
+ static foreach (StringType; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {{
import std.conv : to;
StringType decoded1 = source.to!StringType;
string encoded1 = encode(decoded1);
@@ -589,5 +628,15 @@ if (isSomeChar!Char)
assert(encoded2 == target.to!StringType); // check that `encoded2` wasn't changed
assert(decoded2 == source);
assert(encoded2 == encode(decoded2).to!StringType);
- }
+ }}
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(emailLength("") == -1);
+ assert(emailLength("@") == -1);
+ assert(emailLength("abcd") == -1);
+ assert(emailLength("blah@blub") == -1);
+ assert(emailLength("blah@blub.") == -1);
+ assert(emailLength("blah@blub.domain") == -1);
}
diff --git a/libphobos/src/std/utf.d b/libphobos/src/std/utf.d
index beb4d8fc4a3..be60e7a8f3a 100644
--- a/libphobos/src/std/utf.d
+++ b/libphobos/src/std/utf.d
@@ -7,6 +7,7 @@
$(D '\u0000' &lt;= character &lt;= '\U0010FFFF').
$(SCRIPT inhibitQuickIndex = 1;)
+$(DIVC quickindex,
$(BOOKTABLE,
$(TR $(TH Category) $(TH Functions))
$(TR $(TD Decode) $(TD
@@ -40,6 +41,7 @@ $(TR $(TD Index) $(TD
))
$(TR $(TD Validation) $(TD
$(LREF isValidDchar)
+ $(LREF isValidCodepoint)
$(LREF validate)
))
$(TR $(TD Miscellaneous) $(TD
@@ -47,29 +49,32 @@ $(TR $(TD Miscellaneous) $(TD
$(LREF UseReplacementDchar)
$(LREF UTFException)
))
-)
+))
See_Also:
$(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)<br>
$(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)<br>
$(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335)
- Copyright: Copyright Digital Mars 2000 - 2012.
+ Copyright: Copyright The D Language Foundation 2000 - 2012.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- Authors: $(HTTP digitalmars.com, Walter Bright) and Jonathan M Davis
- Source: $(PHOBOSSRC std/_utf.d)
+ Authors: $(HTTP digitalmars.com, Walter Bright) and
+ $(HTTP jmdavisprog.com, Jonathan M Davis)
+ Source: $(PHOBOSSRC std/utf.d)
+/
module std.utf;
-import std.exception; // basicExceptionCtors
-import std.meta; // AliasSeq
+import std.exception : basicExceptionCtors;
+import core.exception : UnicodeException;
+import std.meta : AliasSeq;
import std.range.primitives;
-import std.traits; // isSomeChar, isSomeString
-import std.typecons; // Flag, Yes, No
+import std.traits : isAutodecodableString, isPointer, isSomeChar,
+ isSomeString, isStaticArray, Unqual, isConvertibleToString;
+import std.typecons : Flag, Yes, No;
/++
Exception thrown on errors in std.utf functions.
+/
-class UTFException : Exception
+class UTFException : UnicodeException
{
import core.internal.string : unsignedToTempString, UnsignedStringBuf;
@@ -77,7 +82,7 @@ class UTFException : Exception
size_t len;
@safe pure nothrow @nogc
- UTFException setSequence(scope uint[] data...)
+ UTFException setSequence(scope uint[] data...) return
{
assert(data.length <= 4);
@@ -87,23 +92,30 @@ class UTFException : Exception
return this;
}
- // FIXME: Use std.exception.basicExceptionCtors here once bug #11500 is fixed
+ // FIXME: Use std.exception.basicExceptionCtors here once
+ // https://issues.dlang.org/show_bug.cgi?id=11500 is fixed
+ /**
+ Standard exception constructors.
+ */
this(string msg, string file = __FILE__, size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow
{
- super(msg, file, line, next);
+ super(msg, 0, file, line, next);
}
-
+ /// ditto
this(string msg, size_t index, string file = __FILE__,
size_t line = __LINE__, Throwable next = null) @safe pure nothrow
{
UnsignedStringBuf buf = void;
- msg ~= " (at index " ~ unsignedToTempString(index, buf, 10) ~ ")";
- super(msg, file, line, next);
+ msg ~= " (at index " ~ unsignedToTempString(index, buf) ~ ")";
+ super(msg, index, file, line, next);
}
-
+ /**
+ Returns:
+ A `string` detailing the invalid UTF sequence.
+ */
override string toString() const
{
if (len == 0)
@@ -122,7 +134,7 @@ class UTFException : Exception
{
UnsignedStringBuf buf = void;
result ~= ' ';
- auto h = unsignedToTempString(i, buf, 16);
+ auto h = unsignedToTempString!16(i, buf);
if (h.length == 1)
result ~= '0';
result ~= h;
@@ -139,6 +151,19 @@ class UTFException : Exception
}
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+
+ char[4] buf;
+ assertThrown!UTFException(encode(buf, cast(dchar) 0xD800));
+ assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF));
+ assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00));
+ assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF));
+ assertThrown!UTFException(encode(buf, cast(dchar) 0x110000));
+}
+
/*
Provide array of invalidly encoded UTF strings. Useful for testing.
@@ -240,10 +265,10 @@ if (isSomeChar!Char)
c = code point to check
Returns:
- $(D true) iff $(D c) is a valid Unicode code point
+ `true` if and only if `c` is a valid Unicode code point
Note:
- $(D '\uFFFE') and $(D '\uFFFF') are considered valid by $(D isValidDchar),
+ `'\uFFFE'` and `'\uFFFF'` are considered valid by `isValidDchar`,
as they are permitted for internal use by an application, but they are
not allowed for interchange by the Unicode standard.
+/
@@ -252,6 +277,15 @@ bool isValidDchar(dchar c) pure nothrow @safe @nogc
return c < 0xD800 || (c > 0xDFFF && c <= 0x10FFFF);
}
+///
+@safe @nogc pure nothrow unittest
+{
+ assert( isValidDchar(cast(dchar) 0x41));
+ assert( isValidDchar(cast(dchar) 0x00));
+ assert(!isValidDchar(cast(dchar) 0xD800));
+ assert(!isValidDchar(cast(dchar) 0x11FFFF));
+}
+
pure nothrow @safe @nogc unittest
{
import std.exception;
@@ -273,15 +307,63 @@ pure nothrow @safe @nogc unittest
});
}
+/**
+Checks if a single character forms a valid code point.
+
+When standing alone, some characters are invalid code points. For
+example the `wchar` `0xD800` is a so called high surrogate, which can
+only be interpreted together with a low surrogate following it. As a
+standalone character it is considered invalid.
+
+See $(LINK2 http://www.unicode.org/versions/Unicode13.0.0/,
+Unicode Standard, D90, D91 and D92) for more details.
+
+Params:
+ c = character to test
+ Char = character type of `c`
+
+Returns:
+ `true`, if `c` forms a valid code point.
+ */
+bool isValidCodepoint(Char)(Char c)
+if (isSomeChar!Char)
+{
+ alias UChar = Unqual!Char;
+ static if (is(UChar == char))
+ {
+ return c <= 0x7F;
+ }
+ else static if (is(UChar == wchar))
+ {
+ return c <= 0xD7FF || c >= 0xE000;
+ }
+ else static if (is(UChar == dchar))
+ {
+ return isValidDchar(c);
+ }
+ else
+ static assert(false, "unknown character type: `" ~ Char.stringof ~ "`");
+}
+
+///
+@safe pure nothrow unittest
+{
+ assert( isValidCodepoint(cast(char) 0x40));
+ assert(!isValidCodepoint(cast(char) 0x80));
+ assert( isValidCodepoint(cast(wchar) 0x1234));
+ assert(!isValidCodepoint(cast(wchar) 0xD800));
+ assert( isValidCodepoint(cast(dchar) 0x0010FFFF));
+ assert(!isValidCodepoint(cast(dchar) 0x12345678));
+}
/++
- Calculate the length of the UTF sequence starting at $(D index)
- in $(D str).
+ Calculate the length of the UTF sequence starting at `index`
+ in `str`.
Params:
- str = input range of UTF code units. Must be random access if
- $(D index) is passed
- index = starting index of UTF sequence (default: $(D 0))
+ str = $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ of UTF code units. Must be random access if `index` is passed
+ index = starting index of UTF sequence (default: `0`)
Returns:
The number of code units in the UTF sequence. For UTF-8, this is a
@@ -289,18 +371,18 @@ pure nothrow @safe @nogc unittest
For UTF-16, it is either 1 or 2. For UTF-32, it is always 1.
Throws:
- May throw a $(D UTFException) if $(D str[index]) is not the start of a
+ May throw a `UTFException` if `str[index]` is not the start of a
valid UTF sequence.
Note:
- $(D stride) will only analyze the first $(D str[index]) element. It
+ `stride` will only analyze the first `str[index]` element. It
will not fully verify the validity of the UTF sequence, nor even verify
the presence of the sequence: it will not actually guarantee that
$(D index + stride(str, index) <= str.length).
+/
uint stride(S)(auto ref S str, size_t index)
if (is(S : const char[]) ||
- (isRandomAccessRange!S && is(Unqual!(ElementType!S) == char)))
+ (isRandomAccessRange!S && is(immutable ElementType!S == immutable char)))
{
static if (is(typeof(str.length) : ulong))
assert(index < str.length, "Past the end of the UTF-8 sequence");
@@ -315,7 +397,7 @@ if (is(S : const char[]) ||
/// Ditto
uint stride(S)(auto ref S str)
if (is(S : const char[]) ||
- (isInputRange!S && is(Unqual!(ElementType!S) == char)))
+ (isInputRange!S && is(immutable ElementType!S == immutable char)))
{
static if (is(S : const char[]))
immutable c = str[0];
@@ -328,23 +410,13 @@ if (is(S : const char[]) ||
return strideImpl(c, 0);
}
-private uint strideImpl(char c, size_t index) @trusted pure
-in { assert(c & 0x80); }
-body
-{
- import core.bitop : bsr;
- immutable msbs = 7 - bsr((~uint(c)) & 0xFF);
- if (c == 0xFF || msbs < 2 || msbs > 4)
- throw new UTFException("Invalid UTF-8 sequence", index);
- return msbs;
-}
-
@system unittest
{
import core.exception : AssertError;
import std.conv : to;
import std.exception;
import std.string : format;
+ import std.traits : FunctionAttribute, functionAttributes, isSafe;
static void test(string s, dchar c, size_t i = 0, size_t line = __LINE__)
{
enforce(stride(s, i) == codeLength!char(c),
@@ -423,7 +495,7 @@ body
/// Ditto
uint stride(S)(auto ref S str, size_t index)
if (is(S : const wchar[]) ||
- (isRandomAccessRange!S && is(Unqual!(ElementType!S) == wchar)))
+ (isRandomAccessRange!S && is(immutable ElementType!S == immutable wchar)))
{
static if (is(typeof(str.length) : ulong))
assert(index < str.length, "Past the end of the UTF-16 sequence");
@@ -440,7 +512,8 @@ if (is(S : const wchar[]))
/// Ditto
uint stride(S)(auto ref S str)
-if (isInputRange!S && is(Unqual!(ElementType!S) == wchar))
+if (isInputRange!S && is(immutable ElementType!S == immutable wchar) &&
+ !is(S : const wchar[]))
{
assert(!str.empty, "UTF-16 sequence is empty");
immutable uint u = str.front;
@@ -453,6 +526,7 @@ if (isInputRange!S && is(Unqual!(ElementType!S) == wchar))
import std.conv : to;
import std.exception;
import std.string : format;
+ import std.traits : FunctionAttribute, functionAttributes, isSafe;
static void test(wstring s, dchar c, size_t i = 0, size_t line = __LINE__)
{
enforce(stride(s, i) == codeLength!wchar(c),
@@ -517,7 +591,7 @@ if (isInputRange!S && is(Unqual!(ElementType!S) == wchar))
/// Ditto
uint stride(S)(auto ref S str, size_t index = 0)
if (is(S : const dchar[]) ||
- (isInputRange!S && is(Unqual!(ElementEncodingType!S) == dchar)))
+ (isInputRange!S && is(immutable ElementEncodingType!S == immutable dchar)))
{
static if (is(typeof(str.length) : ulong))
assert(index < str.length, "Past the end of the UTF-32 sequence");
@@ -526,12 +600,23 @@ if (is(S : const dchar[]) ||
return 1;
}
+///
+@safe unittest
+{
+ assert("a".stride == 1);
+ assert("λ".stride == 2);
+ assert("aλ".stride == 1);
+ assert("aλ".stride(1) == 2);
+ assert("ð·".stride == 4);
+}
+
@system unittest
{
import core.exception : AssertError;
import std.conv : to;
import std.exception;
import std.string : format;
+ import std.traits : FunctionAttribute, functionAttributes, isSafe;
static void test(dstring s, dchar c, size_t i = 0, size_t line = __LINE__)
{
enforce(stride(s, i) == codeLength!dchar(c),
@@ -593,14 +678,25 @@ if (is(S : const dchar[]) ||
});
}
+private uint strideImpl(char c, size_t index) @trusted pure
+in { assert(c & 0x80); }
+do
+{
+ import core.bitop : bsr;
+ immutable msbs = 7 - bsr((~uint(c)) & 0xFF);
+ if (c == 0xFF || msbs < 2 || msbs > 4)
+ throw new UTFException("Invalid UTF-8 sequence", index);
+ return msbs;
+}
+
/++
Calculate the length of the UTF sequence ending one code unit before
- $(D index) in $(D str).
+ `index` in `str`.
Params:
str = bidirectional range of UTF code units. Must be random access if
- $(D index) is passed
- index = index one past end of UTF sequence (default: $(D str.length))
+ `index` is passed
+ index = index one past end of UTF sequence (default: `str.length`)
Returns:
The number of code units in the UTF sequence. For UTF-8, this is a
@@ -608,18 +704,18 @@ if (is(S : const dchar[]) ||
For UTF-16, it is either 1 or 2. For UTF-32, it is always 1.
Throws:
- May throw a $(D UTFException) if $(D str[index]) is not one past the
+ May throw a `UTFException` if `str[index]` is not one past the
end of a valid UTF sequence.
Note:
- $(D strideBack) will only analyze the element at $(D str[index - 1])
+ `strideBack` will only analyze the element at $(D str[index - 1])
element. It will not fully verify the validity of the UTF sequence, nor
even verify the presence of the sequence: it will not actually
guarantee that $(D strideBack(str, index) <= index).
+/
uint strideBack(S)(auto ref S str, size_t index)
if (is(S : const char[]) ||
- (isRandomAccessRange!S && is(Unqual!(ElementType!S) == char)))
+ (isRandomAccessRange!S && is(immutable ElementType!S == immutable char)))
{
static if (is(typeof(str.length) : ulong))
assert(index <= str.length, "Past the end of the UTF-8 sequence");
@@ -630,7 +726,7 @@ if (is(S : const char[]) ||
if (index >= 4) //single verification for most common case
{
- foreach (i; AliasSeq!(2, 3, 4))
+ static foreach (i; 2 .. 5)
{
if ((str[index-i] & 0b1100_0000) != 0b1000_0000)
return i;
@@ -638,7 +734,7 @@ if (is(S : const char[]) ||
}
else
{
- foreach (i; AliasSeq!(2, 3))
+ static foreach (i; 2 .. 4)
{
if (index >= i && (str[index-i] & 0b1100_0000) != 0b1000_0000)
return i;
@@ -650,14 +746,14 @@ if (is(S : const char[]) ||
/// Ditto
uint strideBack(S)(auto ref S str)
if (is(S : const char[]) ||
- (isRandomAccessRange!S && hasLength!S && is(Unqual!(ElementType!S) == char)))
+ (isRandomAccessRange!S && hasLength!S && is(immutable ElementType!S == immutable char)))
{
return strideBack(str, str.length);
}
/// Ditto
uint strideBack(S)(auto ref S str)
-if (isBidirectionalRange!S && is(Unqual!(ElementType!S) == char) && !isRandomAccessRange!S)
+if (isBidirectionalRange!S && is(immutable ElementType!S == immutable char) && !isRandomAccessRange!S)
{
assert(!str.empty, "Past the end of the UTF-8 sequence");
auto temp = str.save;
@@ -678,6 +774,7 @@ if (isBidirectionalRange!S && is(Unqual!(ElementType!S) == char) && !isRandomAcc
import std.conv : to;
import std.exception;
import std.string : format;
+ import std.traits : FunctionAttribute, functionAttributes, isSafe;
static void test(string s, dchar c, size_t i = size_t.max, size_t line = __LINE__)
{
enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!char(c),
@@ -744,7 +841,7 @@ if (isBidirectionalRange!S && is(Unqual!(ElementType!S) == char) && !isRandomAcc
/// Ditto
uint strideBack(S)(auto ref S str, size_t index)
if (is(S : const wchar[]) ||
- (isRandomAccessRange!S && is(Unqual!(ElementType!S) == wchar)))
+ (isRandomAccessRange!S && is(immutable ElementType!S == immutable wchar)))
{
static if (is(typeof(str.length) : ulong))
assert(index <= str.length, "Past the end of the UTF-16 sequence");
@@ -757,7 +854,7 @@ if (is(S : const wchar[]) ||
/// Ditto
uint strideBack(S)(auto ref S str)
if (is(S : const wchar[]) ||
- (isBidirectionalRange!S && is(Unqual!(ElementType!S) == wchar)))
+ (isBidirectionalRange!S && is(immutable ElementType!S == immutable wchar)))
{
assert(!str.empty, "UTF-16 sequence is empty");
@@ -775,6 +872,7 @@ if (is(S : const wchar[]) ||
import std.conv : to;
import std.exception;
import std.string : format;
+ import std.traits : FunctionAttribute, functionAttributes, isSafe;
static void test(wstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__)
{
enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!wchar(c),
@@ -838,7 +936,7 @@ if (is(S : const wchar[]) ||
/// Ditto
uint strideBack(S)(auto ref S str, size_t index)
-if (isRandomAccessRange!S && is(Unqual!(ElementEncodingType!S) == dchar))
+if (isRandomAccessRange!S && is(immutable ElementEncodingType!S == immutable dchar))
{
static if (is(typeof(str.length) : ulong))
assert(index <= str.length, "Past the end of the UTF-32 sequence");
@@ -848,18 +946,29 @@ if (isRandomAccessRange!S && is(Unqual!(ElementEncodingType!S) == dchar))
/// Ditto
uint strideBack(S)(auto ref S str)
-if (isBidirectionalRange!S && is(Unqual!(ElementEncodingType!S) == dchar))
+if (isBidirectionalRange!S && is(immutable ElementEncodingType!S == immutable dchar))
{
assert(!str.empty, "Empty UTF-32 sequence");
return 1;
}
+///
+@safe unittest
+{
+ assert("a".strideBack == 1);
+ assert("λ".strideBack == 2);
+ assert("aλ".strideBack == 2);
+ assert("aλ".strideBack(1) == 1);
+ assert("ð·".strideBack == 4);
+}
+
@system unittest
{
import core.exception : AssertError;
import std.conv : to;
import std.exception;
import std.string : format;
+ import std.traits : FunctionAttribute, functionAttributes, isSafe;
static void test(dstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__)
{
enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!dchar(c),
@@ -923,16 +1032,16 @@ if (isBidirectionalRange!S && is(Unqual!(ElementEncodingType!S) == dchar))
/++
- Given $(D index) into $(D str) and assuming that $(D index) is at the start
- of a UTF sequence, $(D toUCSindex) determines the number of UCS characters
- up to $(D index). So, $(D index) is the index of a code unit at the
+ Given `index` into `str` and assuming that `index` is at the start
+ of a UTF sequence, `toUCSindex` determines the number of UCS characters
+ up to `index`. So, `index` is the index of a code unit at the
beginning of a code point, and the return value is how many code points into
the string that that code point is.
+/
size_t toUCSindex(C)(const(C)[] str, size_t index) @safe pure
if (isSomeChar!C)
{
- static if (is(Unqual!C == dchar))
+ static if (is(immutable C == immutable dchar))
return index;
else
{
@@ -944,7 +1053,7 @@ if (isSomeChar!C)
if (j > index)
{
- static if (is(Unqual!C == char))
+ static if (is(immutable C == immutable char))
throw new UTFException("Invalid UTF-8 sequence", index);
else
throw new UTFException("Invalid UTF-16 sequence", index);
@@ -972,14 +1081,14 @@ if (isSomeChar!C)
/++
- Given a UCS index $(D n) into $(D str), returns the UTF index.
- So, $(D n) is how many code points into the string the code point is, and
+ Given a UCS index `n` into `str`, returns the UTF index.
+ So, `n` is how many code points into the string the code point is, and
the array index of the code unit is returned.
+/
size_t toUTFindex(C)(const(C)[] str, size_t n) @safe pure
if (isSomeChar!C)
{
- static if (is(Unqual!C == dchar))
+ static if (is(immutable C == immutable dchar))
{
return n;
}
@@ -1017,9 +1126,9 @@ if (isSomeChar!C)
alias UseReplacementDchar = Flag!"useReplacementDchar";
/++
- Decodes and returns the code point starting at $(D str[index]). $(D index)
+ Decodes and returns the code point starting at `str[index]`. `index`
is advanced to one past the decoded code point. If the code point is not
- well-formed, then a $(D UTFException) is thrown and $(D index) remains
+ well-formed, then a `UTFException` is thrown and `index` remains
unchanged.
decode will only work with strings and random access ranges of code units
@@ -1035,8 +1144,8 @@ alias UseReplacementDchar = Flag!"useReplacementDchar";
decoded character
Throws:
- $(LREF UTFException) if $(D str[index]) is not the start of a valid UTF
- sequence and useReplacementDchar is $(D No.useReplacementDchar)
+ $(LREF UTFException) if `str[index]` is not the start of a valid UTF
+ sequence and useReplacementDchar is `No.useReplacementDchar`
+/
dchar decode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(auto ref S str, ref size_t index)
if (!isSomeString!S &&
@@ -1049,7 +1158,7 @@ out (result)
{
assert(isValidDchar(result));
}
-body
+do
{
if (str[index] < codeUnitLimit!S)
return str[index++];
@@ -1057,6 +1166,7 @@ body
return decodeImpl!(true, useReplacementDchar)(str, index);
}
+/// ditto
dchar decode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(
auto ref S str, ref size_t index) @trusted pure
if (isSomeString!S)
@@ -1068,20 +1178,44 @@ out (result)
{
assert(isValidDchar(result));
}
-body
+do
{
if (str[index] < codeUnitLimit!S)
return str[index++];
- else
- return decodeImpl!(true, useReplacementDchar)(str, index);
+ else static if (is(immutable S == immutable C[], C))
+ return decodeImpl!(true, useReplacementDchar)(cast(const(C)[]) str, index);
+}
+
+///
+@safe pure unittest
+{
+ size_t i;
+
+ assert("a".decode(i) == 'a' && i == 1);
+ i = 0;
+ assert("Ã¥".decode(i) == 'Ã¥' && i == 2);
+ i = 1;
+ assert("aå".decode(i) == 'å' && i == 3);
+ i = 0;
+ assert("Ã¥"w.decode(i) == 'Ã¥' && i == 1);
+
+ // ë as a multi-code point grapheme
+ i = 0;
+ assert("e\u0308".decode(i) == 'e' && i == 1);
+ // ë as a single code point grapheme
+ i = 0;
+ assert("ë".decode(i) == 'ë' && i == 2);
+ i = 0;
+ assert("ë"w.decode(i) == 'ë' && i == 1);
}
/++
- $(D decodeFront) is a variant of $(LREF decode) which specifically decodes
- the first code point. Unlike $(LREF decode), $(D decodeFront) accepts any
- input range of code units (rather than just a string or random access
- range). It also takes the range by $(D ref) and pops off the elements as it
- decodes them. If $(D numCodeUnits) is passed in, it gets set to the number
+ `decodeFront` is a variant of $(LREF decode) which specifically decodes
+ the first code point. Unlike $(LREF decode), `decodeFront` accepts any
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ of code units (rather than just a string or random access
+ range). It also takes the range by `ref` and pops off the elements as it
+ decodes them. If `numCodeUnits` is passed in, it gets set to the number
of code units which were in the code point which was decoded.
Params:
@@ -1093,7 +1227,7 @@ body
decoded character
Throws:
- $(LREF UTFException) if $(D str.front) is not the start of a valid UTF
+ $(LREF UTFException) if `str.front` is not the start of a valid UTF
sequence. If an exception is thrown, then there is no guarantee as to
the number of code units which were popped off, as it depends on the
type of range being used and how many code units had to be popped off
@@ -1110,7 +1244,7 @@ out (result)
{
assert(isValidDchar(result));
}
-body
+do
{
immutable fst = str.front;
@@ -1122,10 +1256,12 @@ body
}
else
{
- //@@@BUG@@@ 14447 forces canIndex to be done outside of decodeImpl, which
- //is undesirable, since not all overloads of decodeImpl need it. So, it
- //should be moved back into decodeImpl once bug# 8521 has been fixed.
- enum canIndex = isRandomAccessRange!S && hasSlicing!S && hasLength!S;
+ // https://issues.dlang.org/show_bug.cgi?id=14447 forces canIndex to be
+ // done outside of decodeImpl, which is undesirable, since not all
+ // overloads of decodeImpl need it. So, it should be moved back into
+ // decodeImpl once https://issues.dlang.org/show_bug.cgi?id=8521
+ // has been fixed.
+ enum canIndex = is(S : const char[]) || isRandomAccessRange!S && hasSlicing!S && hasLength!S;
immutable retval = decodeImpl!(canIndex, useReplacementDchar)(str, numCodeUnits);
// The other range types were already popped by decodeImpl.
@@ -1136,6 +1272,7 @@ body
}
}
+/// ditto
dchar decodeFront(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(
ref S str, out size_t numCodeUnits) @trusted pure
if (isSomeString!S)
@@ -1147,7 +1284,7 @@ out (result)
{
assert(isValidDchar(result));
}
-body
+do
{
if (str[0] < codeUnitLimit!S)
{
@@ -1156,9 +1293,9 @@ body
str = str[1 .. $];
return retval;
}
- else
+ else static if (is(immutable S == immutable C[], C))
{
- immutable retval = decodeImpl!(true, useReplacementDchar)(str, numCodeUnits);
+ immutable retval = decodeImpl!(true, useReplacementDchar)(cast(const(C)[]) str, numCodeUnits);
str = str[numCodeUnits .. $];
return retval;
}
@@ -1172,12 +1309,26 @@ if (isInputRange!S && isSomeChar!(ElementType!S))
return decodeFront!useReplacementDchar(str, numCodeUnits);
}
+///
+@safe pure unittest
+{
+ import std.range.primitives;
+ string str = "Hello, World!";
+
+ assert(str.decodeFront == 'H' && str == "ello, World!");
+ str = "Ã¥";
+ assert(str.decodeFront == 'Ã¥' && str.empty);
+ str = "Ã¥";
+ size_t i;
+ assert(str.decodeFront(i) == 'Ã¥' && i == 2 && str.empty);
+}
+
/++
- $(D decodeBack) is a variant of $(LREF decode) which specifically decodes
- the last code point. Unlike $(LREF decode), $(D decodeBack) accepts any
+ `decodeBack` is a variant of $(LREF decode) which specifically decodes
+ the last code point. Unlike $(LREF decode), `decodeBack` accepts any
bidirectional range of code units (rather than just a string or random access
- range). It also takes the range by $(D ref) and pops off the elements as it
- decodes them. If $(D numCodeUnits) is passed in, it gets set to the number
+ range). It also takes the range by `ref` and pops off the elements as it
+ decodes them. If `numCodeUnits` is passed in, it gets set to the number
of code units which were in the code point which was decoded.
Params:
@@ -1189,9 +1340,9 @@ if (isInputRange!S && isSomeChar!(ElementType!S))
A decoded UTF character.
Throws:
- $(LREF UTFException) if $(D str.back) is not the end of a valid UTF
- sequence. If an exception is thrown, the $(D str) itself remains unchanged,
- but there is no guarantee as to the value of $(D numCodeUnits) (when passed).
+ $(LREF UTFException) if `str.back` is not the end of a valid UTF
+ sequence. If an exception is thrown, the `str` itself remains unchanged,
+ but there is no guarantee as to the value of `numCodeUnits` (when passed).
+/
dchar decodeBack(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(
ref S str, out size_t numCodeUnits)
@@ -1204,7 +1355,7 @@ out (result)
{
assert(isValidDchar(result));
}
-body
+do
{
if (str[$ - 1] < codeUnitLimit!S)
{
@@ -1213,12 +1364,12 @@ body
str = str[0 .. $ - 1];
return retval;
}
- else
+ else static if (is(immutable S == immutable C[], C))
{
numCodeUnits = strideBack(str);
immutable newLength = str.length - numCodeUnits;
size_t index = newLength;
- immutable retval = decodeImpl!(true, useReplacementDchar)(str, index);
+ immutable retval = decodeImpl!(true, useReplacementDchar)(cast(const(C)[]) str, index);
str = str[0 .. newLength];
return retval;
}
@@ -1237,7 +1388,7 @@ out (result)
{
assert(isValidDchar(result));
}
-body
+do
{
if (str.back < codeUnitLimit!S)
{
@@ -1288,19 +1439,34 @@ out (result)
{
assert(isValidDchar(result));
}
-body
+do
{
size_t numCodeUnits;
return decodeBack!useReplacementDchar(str, numCodeUnits);
}
-// Gives the maximum value that a code unit for the given range type can hold.
+///
+@system pure unittest
+{
+ import std.range.primitives;
+ string str = "Hello, World!";
+
+ assert(str.decodeBack == '!' && str == "Hello, World");
+ str = "Ã¥";
+ assert(str.decodeBack == 'Ã¥' && str.empty);
+ str = "Ã¥";
+ size_t i;
+ assert(str.decodeBack(i) == 'Ã¥' && i == 2 && str.empty);
+}
+
+// For the given range, code unit values less than this
+// are guaranteed to be valid single-codepoint encodings.
package template codeUnitLimit(S)
if (isSomeChar!(ElementEncodingType!S))
{
- static if (is(Unqual!(ElementEncodingType!S) == char))
+ static if (is(immutable ElementEncodingType!S == immutable char))
enum char codeUnitLimit = 0x80;
- else static if (is(Unqual!(ElementEncodingType!S) == wchar))
+ else static if (is(immutable ElementEncodingType!S == immutable wchar))
enum wchar codeUnitLimit = 0xD800;
else
enum dchar codeUnitLimit = 0xD800;
@@ -1326,7 +1492,7 @@ if (isSomeChar!(ElementEncodingType!S))
private dchar decodeImpl(bool canIndex, UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(
auto ref S str, ref size_t index)
if (
- is(S : const char[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S) == char)))
+ is(S : const char[]) || (isInputRange!S && is(immutable ElementEncodingType!S == immutable char)))
{
/* The following encodings are valid, except for the 5 and 6 byte
* combinations:
@@ -1349,7 +1515,8 @@ if (
else
alias pstr = str;
- //@@@BUG@@@ 14447 forces this to be done outside of decodeImpl
+ // https://issues.dlang.org/show_bug.cgi?id=14447 forces this to be done
+ // outside of decodeImpl
//enum canIndex = is(S : const char[]) || (isRandomAccessRange!S && hasSlicing!S && hasLength!S);
static if (canIndex)
@@ -1556,7 +1723,7 @@ unittest
private dchar decodeImpl(bool canIndex, UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)
(auto ref S str, ref size_t index)
-if (is(S : const wchar[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S) == wchar)))
+if (is(S : const wchar[]) || (isInputRange!S && is(immutable ElementEncodingType!S == immutable wchar)))
{
static if (is(S : const wchar[]))
auto pstr = str.ptr + index;
@@ -1565,7 +1732,8 @@ if (is(S : const wchar[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S
else
alias pstr = str;
- //@@@BUG@@@ 14447 forces this to be done outside of decodeImpl
+ // https://issues.dlang.org/show_bug.cgi?id=14447 forces this to be done
+ // outside of decodeImpl
//enum canIndex = is(S : const wchar[]) || (isRandomAccessRange!S && hasSlicing!S && hasLength!S);
static if (canIndex)
@@ -1673,7 +1841,7 @@ unittest
private dchar decodeImpl(bool canIndex, UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(
auto ref S str, ref size_t index)
-if (is(S : const dchar[]) || (isInputRange!S && is(Unqual!(ElementEncodingType!S) == dchar)))
+if (is(S : const dchar[]) || (isInputRange!S && is(immutable ElementEncodingType!S == immutable dchar)))
{
static if (is(S : const dchar[]))
auto pstr = str.ptr;
@@ -1736,19 +1904,21 @@ unittest
}
-version (unittest) private void testDecode(R)(R range,
+version (StdUnittest) private void testDecode(R)(R range,
size_t index,
dchar expectedChar,
size_t expectedIndex,
size_t line = __LINE__)
{
import core.exception : AssertError;
+ import std.exception : enforce;
import std.string : format;
+ import std.traits : isNarrowString;
static if (hasLength!R)
immutable lenBefore = range.length;
- static if (isRandomAccessRange!R)
+ static if (isRandomAccessRange!R && !isNarrowString!R)
{
{
immutable result = decode(range, index);
@@ -1765,12 +1935,13 @@ version (unittest) private void testDecode(R)(R range,
}
}
-version (unittest) private void testDecodeFront(R)(ref R range,
+version (StdUnittest) private void testDecodeFront(R)(ref R range,
dchar expectedChar,
size_t expectedNumCodeUnits,
size_t line = __LINE__)
{
import core.exception : AssertError;
+ import std.exception : enforce;
import std.string : format;
static if (hasLength!R)
@@ -1790,7 +1961,7 @@ version (unittest) private void testDecodeFront(R)(ref R range,
}
}
-version (unittest) private void testDecodeBack(R)(ref R range,
+version (StdUnittest) private void testDecodeBack(R)(ref R range,
dchar expectedChar,
size_t expectedNumCodeUnits,
size_t line = __LINE__)
@@ -1801,6 +1972,7 @@ version (unittest) private void testDecodeBack(R)(ref R range,
else
{
import core.exception : AssertError;
+ import std.exception : enforce;
import std.string : format;
static if (hasLength!R)
@@ -1821,7 +1993,7 @@ version (unittest) private void testDecodeBack(R)(ref R range,
}
}
-version (unittest) private void testAllDecode(R)(R range,
+version (StdUnittest) private void testAllDecode(R)(R range,
dchar expectedChar,
size_t expectedIndex,
size_t line = __LINE__)
@@ -1835,9 +2007,10 @@ version (unittest) private void testAllDecode(R)(R range,
testDecodeFront(range, expectedChar, expectedIndex, line);
}
-version (unittest) private void testBadDecode(R)(R range, size_t index, size_t line = __LINE__)
+version (StdUnittest) private void testBadDecode(R)(R range, size_t index, size_t line = __LINE__)
{
import core.exception : AssertError;
+ import std.exception : assertThrown, enforce;
import std.string : format;
immutable initialIndex = index;
@@ -1861,7 +2034,7 @@ version (unittest) private void testBadDecode(R)(R range, size_t index, size_t l
assertThrown!UTFException(decodeFront(range, index), null, __FILE__, line);
}
-version (unittest) private void testBadDecodeBack(R)(R range, size_t line = __LINE__)
+version (StdUnittest) private void testBadDecodeBack(R)(R range, size_t line = __LINE__)
{
// This condition is to allow unit testing all `decode` functions together
static if (!isBidirectionalRange!R)
@@ -1869,6 +2042,7 @@ version (unittest) private void testBadDecodeBack(R)(R range, size_t line = __LI
else
{
import core.exception : AssertError;
+ import std.exception : assertThrown, enforce;
import std.string : format;
static if (hasLength!R)
@@ -1972,11 +2146,10 @@ version (unittest) private void testBadDecodeBack(R)(R range, size_t line = __LI
@system unittest
{
- import std.conv : to;
import std.exception;
assertCTFEable!(
{
- foreach (S; AliasSeq!(to!wstring, InputCU!wchar, RandomCU!wchar,
+ foreach (S; AliasSeq!((wstring s) => s, InputCU!wchar, RandomCU!wchar,
(wstring s) => new RefBidirCU!wchar(s),
(wstring s) => new RefRandomCU!wchar(s)))
{
@@ -2011,7 +2184,7 @@ version (unittest) private void testBadDecodeBack(R)(R range, size_t line = __LI
}
}
- foreach (S; AliasSeq!(to!wstring, RandomCU!wchar, (wstring s) => new RefRandomCU!wchar(s)))
+ foreach (S; AliasSeq!((wchar[] s) => s.idup, RandomCU!wchar, (wstring s) => new RefRandomCU!wchar(s)))
{
auto str = S([cast(wchar) 0xD800, cast(wchar) 0xDC00,
cast(wchar) 0x1400,
@@ -2028,11 +2201,10 @@ version (unittest) private void testBadDecodeBack(R)(R range, size_t line = __LI
@system unittest
{
- import std.conv : to;
import std.exception;
assertCTFEable!(
{
- foreach (S; AliasSeq!(to!dstring, RandomCU!dchar, InputCU!dchar,
+ foreach (S; AliasSeq!((dstring s) => s, RandomCU!dchar, InputCU!dchar,
(dstring s) => new RefBidirCU!dchar(s),
(dstring s) => new RefRandomCU!dchar(s)))
{
@@ -2069,7 +2241,7 @@ version (unittest) private void testBadDecodeBack(R)(R range, size_t line = __LI
}
}
- foreach (S; AliasSeq!(to!dstring, RandomCU!dchar, (dstring s) => new RefRandomCU!dchar(s)))
+ foreach (S; AliasSeq!((dchar[] s) => s.idup, RandomCU!dchar, (dstring s) => new RefRandomCU!dchar(s)))
{
auto str = S([cast(dchar) 0x10000, cast(dchar) 0x1400, cast(dchar) 0xB9DDE]);
testDecode(str, 0, 0x10000, 1);
@@ -2085,6 +2257,7 @@ version (unittest) private void testBadDecodeBack(R)(R range, size_t line = __LI
@safe unittest
{
import std.exception;
+ import std.traits : FunctionAttribute, functionAttributes, isSafe;
assertCTFEable!(
{
foreach (S; AliasSeq!( char[], const( char)[], string,
@@ -2129,13 +2302,13 @@ private dchar _utfException(UseReplacementDchar useReplacementDchar)(string msg,
}
/++
- Encodes $(D c) into the static array, $(D buf), and returns the actual
- length of the encoded character (a number between $(D 1) and $(D 4) for
- $(D char[4]) buffers and a number between $(D 1) and $(D 2) for
- $(D wchar[2]) buffers).
+ Encodes `c` into the static array, `buf`, and returns the actual
+ length of the encoded character (a number between `1` and `4` for
+ `char[4]` buffers and a number between `1` and `2` for
+ `wchar[2]` buffers).
Throws:
- $(D UTFException) if $(D c) is not a valid UTF code point.
+ `UTFException` if `c` is not a valid UTF code point.
+/
size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
out char[4] buf, dchar c) @safe pure
@@ -2180,6 +2353,64 @@ size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
goto L3;
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.typecons : Yes;
+
+ char[4] buf;
+
+ assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000");
+ assert(encode(buf, '\u007F') == 1 && buf[0 .. 1] == "\u007F");
+ assert(encode(buf, '\u0080') == 2 && buf[0 .. 2] == "\u0080");
+ assert(encode(buf, '\uE000') == 3 && buf[0 .. 3] == "\uE000");
+ assert(encode(buf, 0xFFFE) == 3 && buf[0 .. 3] == "\xEF\xBF\xBE");
+ assertThrown!UTFException(encode(buf, cast(dchar) 0x110000));
+
+ encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
+ auto slice = buf[];
+ assert(slice.decodeFront == replacementDchar);
+}
+
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.typecons : Yes;
+
+ wchar[2] buf;
+
+ assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000");
+ assert(encode(buf, '\uD7FF') == 1 && buf[0 .. 1] == "\uD7FF");
+ assert(encode(buf, '\uE000') == 1 && buf[0 .. 1] == "\uE000");
+ assert(encode(buf, '\U00010000') == 2 && buf[0 .. 2] == "\U00010000");
+ assert(encode(buf, '\U0010FFFF') == 2 && buf[0 .. 2] == "\U0010FFFF");
+ assertThrown!UTFException(encode(buf, cast(dchar) 0xD800));
+
+ encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
+ auto slice = buf[];
+ assert(slice.decodeFront == replacementDchar);
+}
+
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.typecons : Yes;
+
+ dchar[1] buf;
+
+ assert(encode(buf, '\u0000') == 1 && buf[0] == '\u0000');
+ assert(encode(buf, '\uD7FF') == 1 && buf[0] == '\uD7FF');
+ assert(encode(buf, '\uE000') == 1 && buf[0] == '\uE000');
+ assert(encode(buf, '\U0010FFFF') == 1 && buf[0] == '\U0010FFFF');
+ assertThrown!UTFException(encode(buf, cast(dchar) 0xD800));
+
+ encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
+ assert(buf[0] == replacementDchar);
+}
+
@safe unittest
{
import std.exception;
@@ -2206,7 +2437,8 @@ size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
assertThrown!UTFException(encode(buf, cast(dchar) 0x110000));
assert(encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000) == buf.stride);
- assert(buf.front == replacementDchar);
+ enum replacementDcharString = "\uFFFD";
+ assert(buf[0 .. replacementDcharString.length] == replacementDcharString);
});
}
@@ -2303,10 +2535,10 @@ size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
/++
- Encodes $(D c) in $(D str)'s encoding and appends it to $(D str).
+ Encodes `c` in `str`'s encoding and appends it to `str`.
Throws:
- $(D UTFException) if $(D c) is not a valid UTF code point.
+ `UTFException` if `c` is not a valid UTF code point.
+/
void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
ref char[] str, dchar c) @safe pure
@@ -2362,6 +2594,21 @@ void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
str = r;
}
+///
+@safe unittest
+{
+ char[] s = "abcd".dup;
+ dchar d1 = 'a';
+ dchar d2 = 'ø';
+
+ encode(s, d1);
+ assert(s.length == 5);
+ assert(s == "abcda");
+ encode(s, d2);
+ assert(s.length == 7);
+ assert(s == "abcdaø");
+}
+
@safe unittest
{
import std.exception;
@@ -2409,9 +2656,11 @@ void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF));
assertThrown!UTFException(encode(buf, cast(dchar) 0x110000));
- assert(buf.back != replacementDchar);
+ enum replacementDcharString = "\uFFFD";
+ enum rdcslen = replacementDcharString.length;
+ assert(buf[$ - rdcslen .. $] != replacementDcharString);
encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
- assert(buf.back == replacementDchar);
+ assert(buf[$ - rdcslen .. $] == replacementDcharString);
});
}
@@ -2516,7 +2765,7 @@ void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
/++
Returns the number of code units that are required to encode the code point
- $(D c) when $(D C) is the character type used to encode it.
+ `c` when `C` is the character type used to encode it.
+/
ubyte codeLength(C)(dchar c) @safe pure nothrow @nogc
if (isSomeChar!C)
@@ -2554,19 +2803,20 @@ if (isSomeChar!C)
/++
- Returns the number of code units that are required to encode $(D str)
- in a string whose character type is $(D C). This is particularly useful
+ Returns the number of code units that are required to encode `str`
+ in a string whose character type is `C`. This is particularly useful
when slicing one string with the length of another and the two string
types use different character types.
Params:
C = the character type to get the encoding length for
- input = the input range to calculate the encoding length from
+ input = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ to calculate the encoding length from
Returns:
The number of code units in `input` when encoded to `C`
+/
size_t codeLength(C, InputRange)(InputRange input)
-if (isInputRange!InputRange && !isInfinite!InputRange && is(ElementType!InputRange : dchar))
+if (isInputRange!InputRange && !isInfinite!InputRange && isSomeChar!(ElementType!InputRange))
{
alias EncType = Unqual!(ElementEncodingType!InputRange);
static if (isSomeString!InputRange && is(EncType == C) && is(typeof(input.length)))
@@ -2575,7 +2825,7 @@ if (isInputRange!InputRange && !isInfinite!InputRange && is(ElementType!InputRan
{
size_t total = 0;
- foreach (dchar c; input)
+ foreach (c; input.byDchar)
total += codeLength!C(c);
return total;
@@ -2585,20 +2835,19 @@ if (isInputRange!InputRange && !isInfinite!InputRange && is(ElementType!InputRan
///
@safe unittest
{
- import std.conv : to;
assert(codeLength!char("hello world") ==
- to!string("hello world").length);
+ "hello world".length);
assert(codeLength!wchar("hello world") ==
- to!wstring("hello world").length);
+ "hello world"w.length);
assert(codeLength!dchar("hello world") ==
- to!dstring("hello world").length);
+ "hello world"d.length);
assert(codeLength!char(`プログラミング`) ==
- to!string(`プログラミング`).length);
+ `プログラミング`.length);
assert(codeLength!wchar(`プログラミング`) ==
- to!wstring(`プログラミング`).length);
+ `プログラミング`w.length);
assert(codeLength!dchar(`プログラミング`) ==
- to!dstring(`プログラミング`).length);
+ `プログラミング`d.length);
string haystack = `Être sans la verité, ça, ce ne serait pas bien.`;
wstring needle = `Être sans la verité`;
@@ -2634,11 +2883,11 @@ if (isInputRange!InputRange && !isInfinite!InputRange && is(ElementType!InputRan
/+
Internal helper function:
-Returns true if it is safe to search for the Codepoint $(D c) inside
+Returns true if it is safe to search for the Codepoint `c` inside
code units, without decoding.
This is a runtime check that is used an optimization in various functions,
-particularly, in $(D std.string).
+particularly, in `std.string`.
+/
package bool canSearchInCodeUnits(C)(dchar c)
if (isSomeChar!C)
@@ -2674,10 +2923,10 @@ if (isSomeChar!C)
/* =================== Validation ======================= */
/++
- Checks to see if $(D str) is well-formed unicode or not.
+ Checks to see if `str` is well-formed unicode or not.
Throws:
- $(D UTFException) if $(D str) is not well-formed.
+ `UTFException` if `str` is not well-formed.
+/
void validate(S)(in S str) @safe pure
if (isSomeString!S)
@@ -2689,8 +2938,16 @@ if (isSomeString!S)
}
}
+///
+@safe unittest
+{
+ import std.exception : assertThrown;
+ char[] a = [167, 133, 175];
+ assertThrown!UTFException(validate(a));
+}
-@safe unittest // bugzilla 12923
+// https://issues.dlang.org/show_bug.cgi?id=12923
+@safe unittest
{
import std.exception;
assertThrown((){
@@ -2733,8 +2990,9 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S))
import std.algorithm.comparison : equal;
import std.internal.test.dummyrange : ReferenceInputRange;
- auto r1 = new ReferenceInputRange!dchar("Hellø");
- auto r2 = new ReferenceInputRange!dchar("ð·");
+ alias RT = ReferenceInputRange!(ElementType!(string));
+ auto r1 = new RT("Hellø");
+ auto r2 = new RT("ð·");
assert(r1.toUTF8.equal(['H', 'e', 'l', 'l', 0xC3, 0xB8]));
assert(r2.toUTF8.equal([0xF0, 0x90, 0x90, 0xB7]));
@@ -2775,8 +3033,9 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S))
import std.algorithm.comparison : equal;
import std.internal.test.dummyrange : ReferenceInputRange;
- auto r1 = new ReferenceInputRange!dchar("𤭢");
- auto r2 = new ReferenceInputRange!dchar("ð·");
+ alias RT = ReferenceInputRange!(ElementType!(string));
+ auto r1 = new RT("𤭢");
+ auto r2 = new RT("ð·");
assert(r1.toUTF16.equal([0xD852, 0xDF62]));
assert(r2.toUTF16.equal([0xD801, 0xDC37]));
@@ -2794,13 +3053,26 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S))
* See_Also:
* For a lazy, non-allocating version of these functions, see $(LREF byUTF).
*/
-dstring toUTF32(S)(S s)
+dstring toUTF32(S)(scope S s)
if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S))
{
return toUTFImpl!dstring(s);
}
-private T toUTFImpl(T, S)(S s)
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // these graphemes are two code units in UTF-16 and one in UTF-32
+ assert("𤭢"w.length == 2);
+ assert("ð·"w.length == 2);
+
+ assert("𤭢"w.toUTF32.equal([0x00024B62]));
+ assert("ð·"w.toUTF32.equal([0x00010437]));
+}
+
+private T toUTFImpl(T, S)(scope S s)
{
static if (is(S : T))
{
@@ -2811,7 +3083,7 @@ private T toUTFImpl(T, S)(S s)
import std.array : appender;
auto app = appender!T();
- static if (hasLength!S || isSomeString!S)
+ static if (is(S == C[], C) || hasLength!S)
app.reserve(s.length);
foreach (c; s.byUTF!(Unqual!(ElementEncodingType!T)))
@@ -2824,36 +3096,36 @@ private T toUTFImpl(T, S)(S s)
/* =================== toUTFz ======================= */
/++
- Returns a C-style zero-terminated string equivalent to $(D str). $(D str)
- must not contain embedded $(D '\0')'s as any C function will treat the first
- $(D '\0') that it sees as the end of the string. If $(D str.empty) is
- $(D true), then a string containing only $(D '\0') is returned.
+ Returns a C-style zero-terminated string equivalent to `str`. `str`
+ must not contain embedded `'\0'`'s as any C function will treat the first
+ `'\0'` that it sees as the end of the string. If `str.empty` is
+ `true`, then a string containing only `'\0'` is returned.
- $(D toUTFz) accepts any type of string and is templated on the type of
+ `toUTFz` accepts any type of string and is templated on the type of
character pointer that you wish to convert to. It will avoid allocating a
new string if it can, but there's a decent chance that it will end up having
to allocate a new string - particularly when dealing with character types
- other than $(D char).
+ other than `char`.
- $(RED Warning 1:) If the result of $(D toUTFz) equals $(D str.ptr), then if
- anything alters the character one past the end of $(D str) (which is the
- $(D '\0') character terminating the string), then the string won't be
+ $(RED Warning 1:) If the result of `toUTFz` equals `str.ptr`, then if
+ anything alters the character one past the end of `str` (which is the
+ `'\0'` character terminating the string), then the string won't be
zero-terminated anymore. The most likely scenarios for that are if you
- append to $(D str) and no reallocation takes place or when $(D str) is a
+ append to `str` and no reallocation takes place or when `str` is a
slice of a larger array, and you alter the character in the larger array
- which is one character past the end of $(D str). Another case where it could
+ which is one character past the end of `str`. Another case where it could
occur would be if you had a mutable character array immediately after
- $(D str) in memory (for example, if they're member variables in a
+ `str` in memory (for example, if they're member variables in a
user-defined type with one declared right after the other) and that
- character array happened to start with $(D '\0'). Such scenarios will never
+ character array happened to start with `'\0'`. Such scenarios will never
occur if you immediately use the zero-terminated string after calling
- $(D toUTFz) and the C function using it doesn't keep a reference to it.
+ `toUTFz` and the C function using it doesn't keep a reference to it.
Also, they are unlikely to occur even if you save the zero-terminated string
(the cases above would be among the few examples of where it could happen).
However, if you save the zero-terminate string and want to be absolutely
certain that the string stays zero-terminated, then simply append a
- $(D '\0') to the string and use its $(D ptr) property rather than calling
- $(D toUTFz).
+ `'\0'` to the string and use its `ptr` property rather than calling
+ `toUTFz`.
$(RED Warning 2:) When passing a character pointer to a C function, and the
C function keeps it around for any reason, make sure that you keep a
@@ -2861,8 +3133,10 @@ private T toUTFImpl(T, S)(S s)
collection cycle and cause a nasty bug when the C code tries to use it.
+/
template toUTFz(P)
+if (isPointer!P && isSomeChar!(typeof(*P.init)))
{
P toUTFz(S)(S str) @safe pure
+ if (isSomeString!S)
{
return toUTFzImpl!(P, S)(str);
}
@@ -2879,10 +3153,8 @@ template toUTFz(P)
auto p6 = toUTFz!(immutable(dchar)*)("hello world"w);
}
-private P toUTFzImpl(P, S)(S str) @safe pure
-if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) &&
- is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S)) &&
- is(immutable(Unqual!(ElementEncodingType!S)) == ElementEncodingType!S))
+private P toUTFzImpl(P, S)(return scope S str) @safe pure
+if (is(immutable typeof(*P.init) == typeof(str[0])))
//immutable(C)[] -> C*, const(C)*, or immutable(C)*
{
if (str.empty)
@@ -2924,13 +3196,11 @@ if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) &&
}
}
-private P toUTFzImpl(P, S)(S str) @safe pure
-if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) &&
- is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S)) &&
- !is(immutable(Unqual!(ElementEncodingType!S)) == ElementEncodingType!S))
+private P toUTFzImpl(P, S)(return scope S str) @safe pure
+if (is(typeof(str[0]) C) && is(immutable typeof(*P.init) == immutable C) && !is(C == immutable))
//C[] or const(C)[] -> C*, const(C)*, or immutable(C)*
{
- alias InChar = ElementEncodingType!S;
+ alias InChar = typeof(str[0]);
alias OutChar = typeof(*P.init);
//const(C)[] -> const(C)* or
@@ -2965,8 +3235,7 @@ if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) &&
}
private P toUTFzImpl(P, S)(S str) @safe pure
-if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) &&
- !is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S)))
+if (!is(immutable typeof(*P.init) == immutable typeof(str[0])))
//C1[], const(C1)[], or immutable(C1)[] -> C2*, const(C2)*, or immutable(C2)*
{
import std.array : appender;
@@ -3068,11 +3337,11 @@ if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) &&
/++
- $(D toUTF16z) is a convenience function for $(D toUTFz!(const(wchar)*)).
+ `toUTF16z` is a convenience function for `toUTFz!(const(wchar)*)`.
- Encodes string $(D s) into UTF-16 and returns the encoded string.
- $(D toUTF16z) is suitable for calling the 'W' functions in the Win32 API
- that take an $(D LPWSTR) or $(D LPCWSTR) argument.
+ Encodes string `s` into UTF-16 and returns the encoded string.
+ `toUTF16z` is suitable for calling the 'W' functions in the Win32 API
+ that take an `LPCWSTR` argument.
+/
const(wchar)* toUTF16z(C)(const(C)[] str) @safe pure
if (isSomeChar!C)
@@ -3080,6 +3349,14 @@ if (isSomeChar!C)
return toUTFz!(const(wchar)*)(str);
}
+///
+@system unittest
+{
+ string str = "Hello, World!";
+ const(wchar)* p = str.toUTF16z;
+ assert(p[str.length] == '\0');
+}
+
@safe pure unittest
{
import std.conv : to;
@@ -3123,19 +3400,28 @@ if (isSomeChar!C)
/++
- Returns the total number of code points encoded in $(D str).
+ Returns the total number of code points encoded in `str`.
Supercedes: This function supercedes $(LREF toUCSindex).
Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252
Throws:
- $(D UTFException) if $(D str) is not well-formed.
+ `UTFException` if `str` is not well-formed.
+/
-size_t count(C)(const(C)[] str) @trusted pure nothrow @nogc
+size_t count(C)(const(C)[] str) @safe pure nothrow @nogc
if (isSomeChar!C)
{
- return walkLength(str);
+ return walkLength(str.byDchar);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(count("") == 0);
+ assert(count("a") == 1);
+ assert(count("abc") == 3);
+ assert(count("\u20AC100") == 4);
}
@safe pure nothrow @nogc unittest
@@ -3152,8 +3438,9 @@ if (isSomeChar!C)
// Ranges of code units for testing.
-version (unittest)
+version (StdUnittest)
{
+private:
struct InputCU(C)
{
import std.conv : to;
@@ -3277,12 +3564,12 @@ enum dchar replacementDchar = '\uFFFD';
* one while iterating over the resulting range will give nonsensical results.
*
* Params:
- * r = an input range of characters (including strings) or a type that
- * implicitly converts to a string type.
+ * r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * of characters (including strings) or a type that implicitly converts to a string type.
* Returns:
- * If `r` is not an auto-decodable string (i.e. a narrow string or a
- * user-defined type that implicits converts to a string type), then `r`
- * is returned.
+ * If `r` is not an auto-decodable string (i.e. a narrow string or a
+ * user-defined type that implicits converts to a string type), then `r`
+ * is returned.
*
* Otherwise, `r` is converted to its corresponding string type (if it's
* not already a string) and wrapped in a random-access range where the
@@ -3293,6 +3580,11 @@ enum dchar replacementDchar = '\uFFFD';
* of characters on its own (i.e. it has the input range API as member
* functions), $(I and) it's implicitly convertible to a string type, then
* `r` is returned, and no implicit conversion takes place.
+ *
+ * If `r` is wrapped in a new range, then that range has a `source`
+ * property for returning the string that's currently contained within that
+ * range.
+ *
* See_Also:
* Refer to the $(MREF std, uni) docs for a reference on Unicode
* terminology.
@@ -3301,12 +3593,11 @@ enum dchar replacementDchar = '\uFFFD';
* $(REF byGrapheme, std,uni).
*/
auto byCodeUnit(R)(R r)
-if (isAutodecodableString!R ||
- isInputRange!R && isSomeChar!(ElementEncodingType!R) ||
- (is(R : const dchar[]) && !isStaticArray!R))
+if ((isConvertibleToString!R && !isStaticArray!R) ||
+ (isInputRange!R && isSomeChar!(ElementEncodingType!R)))
{
- static if (isNarrowString!R ||
- // This would be cleaner if we had a way to check whether a type
+ import std.traits : StringTypeOf;
+ static if (// This would be cleaner if we had a way to check whether a type
// was a range without any implicit conversions.
(isAutodecodableString!R && !__traits(hasMember, R, "empty") &&
!__traits(hasMember, R, "front") && !__traits(hasMember, R, "popFront")))
@@ -3315,31 +3606,31 @@ if (isAutodecodableString!R ||
{
@safe pure nothrow @nogc:
- @property bool empty() const { return str.length == 0; }
- @property auto ref front() inout { return str[0]; }
- void popFront() { str = str[1 .. $]; }
+ @property bool empty() const { return source.length == 0; }
+ @property auto ref front() inout { return source[0]; }
+ void popFront() { source = source[1 .. $]; }
- @property auto save() { return ByCodeUnitImpl(str.save); }
+ @property auto save() { return ByCodeUnitImpl(source.save); }
- @property auto ref back() inout { return str[$ - 1]; }
- void popBack() { str = str[0 .. $-1]; }
+ @property auto ref back() inout { return source[$ - 1]; }
+ void popBack() { source = source[0 .. $-1]; }
- auto ref opIndex(size_t index) inout { return str[index]; }
- auto opSlice(size_t lower, size_t upper) { return ByCodeUnitImpl(str[lower .. upper]); }
+ auto ref opIndex(size_t index) inout { return source[index]; }
+ auto opSlice(size_t lower, size_t upper) { return ByCodeUnitImpl(source[lower .. upper]); }
- @property size_t length() const { return str.length; }
+ @property size_t length() const { return source.length; }
alias opDollar = length;
- private:
- StringTypeOf!R str;
+ StringTypeOf!R source;
}
static assert(isRandomAccessRange!ByCodeUnitImpl);
return ByCodeUnitImpl(r);
}
- else static if (is(R : const dchar[]) && !__traits(hasMember, R, "empty") &&
- !__traits(hasMember, R, "front") && !__traits(hasMember, R, "popFront"))
+ else static if (!isInputRange!R ||
+ (is(R : const dchar[]) && !__traits(hasMember, R, "empty") &&
+ !__traits(hasMember, R, "front") && !__traits(hasMember, R, "popFront")))
{
return cast(StringTypeOf!R) r;
}
@@ -3354,6 +3645,7 @@ if (isAutodecodableString!R ||
@safe unittest
{
import std.range.primitives;
+ import std.traits : isAutodecodableString;
auto r = "Hello, World!".byCodeUnit();
static assert(hasLength!(typeof(r)));
@@ -3361,14 +3653,27 @@ if (isAutodecodableString!R ||
static assert(isRandomAccessRange!(typeof(r)));
static assert(is(ElementType!(typeof(r)) == immutable char));
- // contrast with the range capabilities of standard strings
+ // contrast with the range capabilities of standard strings (with or
+ // without autodecoding enabled).
auto s = "Hello, World!";
static assert(isBidirectionalRange!(typeof(r)));
- static assert(is(ElementType!(typeof(s)) == dchar));
-
- static assert(!isRandomAccessRange!(typeof(s)));
- static assert(!hasSlicing!(typeof(s)));
- static assert(!hasLength!(typeof(s)));
+ static if (isAutodecodableString!(typeof(s)))
+ {
+ // with autodecoding enabled, strings are non-random-access ranges of
+ // dchar.
+ static assert(is(ElementType!(typeof(s)) == dchar));
+ static assert(!isRandomAccessRange!(typeof(s)));
+ static assert(!hasSlicing!(typeof(s)));
+ static assert(!hasLength!(typeof(s)));
+ }
+ else
+ {
+ // without autodecoding, strings are normal arrays.
+ static assert(is(ElementType!(typeof(s)) == immutable char));
+ static assert(isRandomAccessRange!(typeof(s)));
+ static assert(hasSlicing!(typeof(s)));
+ static assert(hasLength!(typeof(s)));
+ }
}
/// `byCodeUnit` does no Unicode decoding
@@ -3384,6 +3689,29 @@ if (isAutodecodableString!R ||
assert(noel2.byCodeUnit[2] != 'ë');
}
+/// `byCodeUnit` exposes a `source` property when wrapping narrow strings.
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : popFrontN;
+ import std.traits : isAutodecodableString;
+ {
+ auto range = byCodeUnit("hello world");
+ range.popFrontN(3);
+ assert(equal(range.save, "lo world"));
+ static if (isAutodecodableString!string) // only enabled with autodecoding
+ {
+ string str = range.source;
+ assert(str == "lo world");
+ }
+ }
+ // source only exists if the range was wrapped
+ {
+ auto range = byCodeUnit("hello world"d);
+ static assert(!__traits(compiles, range.source));
+ }
+}
+
@safe pure nothrow @nogc unittest
{
import std.range;
@@ -3434,7 +3762,7 @@ if (isAutodecodableString!R ||
{
auto bcu = "hello".byCodeUnit().byCodeUnit();
static assert(isForwardRange!(typeof(bcu)));
- static assert(is(typeof(bcu) == struct));
+ static assert(is(typeof(bcu) == struct) == isAutodecodableString!string);
auto s = bcu.save;
bcu.popFront();
assert(s.front == 'h');
@@ -3443,7 +3771,7 @@ if (isAutodecodableString!R ||
auto bcu = "hello".byCodeUnit();
static assert(hasSlicing!(typeof(bcu)));
static assert(isBidirectionalRange!(typeof(bcu)));
- static assert(is(typeof(bcu) == struct));
+ static assert(is(typeof(bcu) == struct) == isAutodecodableString!string);
static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit())));
auto ret = bcu.retro;
assert(ret.front == 'o');
@@ -3454,7 +3782,7 @@ if (isAutodecodableString!R ||
auto bcu = "κόσμε"w.byCodeUnit();
static assert(hasSlicing!(typeof(bcu)));
static assert(isBidirectionalRange!(typeof(bcu)));
- static assert(is(typeof(bcu) == struct));
+ static assert(is(typeof(bcu) == struct) == isAutodecodableString!wstring);
static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit())));
auto ret = bcu.retro;
assert(ret.front == 'ε');
@@ -3471,7 +3799,7 @@ if (isAutodecodableString!R ||
auto orig = Stringish("\U0010fff8 ðŠ foo ð‚“");
auto bcu = orig.byCodeUnit();
static assert(is(typeof(bcu) == struct));
- static assert(!is(typeof(bcu) == Stringish));
+ static assert(!is(typeof(bcu) == Stringish) == isAutodecodableString!Stringish);
static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit())));
static assert(is(ElementType!(typeof(bcu)) == immutable char));
assert(bcu.front == cast(char) 244);
@@ -3486,7 +3814,7 @@ if (isAutodecodableString!R ||
auto orig = WStringish("\U0010fff8 ðŠ foo ð‚“"w);
auto bcu = orig.byCodeUnit();
static assert(is(typeof(bcu) == struct));
- static assert(!is(typeof(bcu) == WStringish));
+ static assert(!is(typeof(bcu) == WStringish) == isAutodecodableString!WStringish);
static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit())));
static assert(is(ElementType!(typeof(bcu)) == immutable wchar));
assert(bcu.front == cast(wchar) 56319);
@@ -3515,7 +3843,10 @@ if (isAutodecodableString!R ||
auto orig = FuncStringish("\U0010fff8 ðŠ foo ð‚“");
auto bcu = orig.byCodeUnit();
- static assert(is(typeof(bcu) == struct));
+ static if (isAutodecodableString!FuncStringish)
+ static assert(is(typeof(bcu) == struct));
+ else
+ static assert(is(typeof(bcu) == string));
static assert(!is(typeof(bcu) == FuncStringish));
static assert(is(typeof(bcu) == typeof(bcu.byCodeUnit())));
static assert(is(ElementType!(typeof(bcu)) == immutable char));
@@ -3632,7 +3963,10 @@ if (isAutodecodableString!R ||
auto orig = Enum.a;
auto bcu = orig.byCodeUnit();
static assert(!is(typeof(bcu) == Enum));
- static assert(is(typeof(bcu) == struct));
+ static if (isAutodecodableString!Enum)
+ static assert(is(typeof(bcu) == struct));
+ else
+ static assert(is(typeof(bcu) == string));
static assert(is(ElementType!(typeof(bcu)) == immutable char));
assert(bcu.front == 't');
}
@@ -3642,7 +3976,10 @@ if (isAutodecodableString!R ||
auto orig = WEnum.a;
auto bcu = orig.byCodeUnit();
static assert(!is(typeof(bcu) == WEnum));
- static assert(is(typeof(bcu) == struct));
+ static if (isAutodecodableString!WEnum)
+ static assert(is(typeof(bcu) == struct));
+ else
+ static assert(is(typeof(bcu) == wstring));
static assert(is(ElementType!(typeof(bcu)) == immutable wchar));
assert(bcu.front == 't');
}
@@ -3656,8 +3993,16 @@ if (isAutodecodableString!R ||
assert(bcu.front == 't');
}
- static assert(!is(typeof(byCodeUnit("hello")) == string));
- static assert(!is(typeof(byCodeUnit("hello"w)) == wstring));
+ static if (autodecodeStrings)
+ {
+ static assert(!is(typeof(byCodeUnit("hello")) == string));
+ static assert(!is(typeof(byCodeUnit("hello"w)) == wstring));
+ }
+ else
+ {
+ static assert(is(typeof(byCodeUnit("hello")) == string));
+ static assert(is(typeof(byCodeUnit("hello"w)) == wstring));
+ }
static assert(is(typeof(byCodeUnit("hello"d)) == dstring));
static assert(!__traits(compiles, byCodeUnit((char[5]).init)));
@@ -3674,7 +4019,8 @@ if (isAutodecodableString!R ||
}
/****************************
- * Iterate an input range of characters by char, wchar, or dchar.
+ * Iterate an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * of characters by char, wchar, or dchar.
* These aliases simply forward to $(LREF byUTF) with the
* corresponding C argument.
*
@@ -3883,8 +4229,8 @@ pure @safe nothrow @nogc unittest
foreach (c; s[].byDchar()) { }
}
-version (unittest)
-int impureVariable;
+version (StdUnittest)
+private int impureVariable;
@system unittest
{
@@ -3909,23 +4255,33 @@ int impureVariable;
}
/****************************
- * Iterate an input range of characters by char type `C` by
- * encoding the elements of the range.
+ * Iterate an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ * of characters by char type `C` by encoding the elements of the range.
*
- * UTF sequences that cannot be converted to the specified encoding are
+ * UTF sequences that cannot be converted to the specified encoding are either
* replaced by U+FFFD per "5.22 Best Practice for U+FFFD Substitution"
- * of the Unicode Standard 6.2. Hence byUTF is not symmetric.
+ * of the Unicode Standard 6.2 or result in a thrown UTFException.
+ * Hence byUTF is not symmetric.
* This algorithm is lazy, and does not allocate memory.
* `@nogc`, `pure`-ity, `nothrow`, and `@safe`-ty are inferred from the
* `r` parameter.
*
* Params:
* C = `char`, `wchar`, or `dchar`
+ * useReplacementDchar = UseReplacementDchar.yes means replace invalid UTF with `replacementDchar`,
+ * UseReplacementDchar.no means throw `UTFException` for invalid UTF
+ *
+ * Throws:
+ * `UTFException` if invalid UTF sequence and `useReplacementDchar` is set to `UseReplacementDchar.yes`
+ *
+ * GC:
+ * Does not use GC if `useReplacementDchar` is set to `UseReplacementDchar.no`
*
* Returns:
- * A forward range if `R` is a range and not auto-decodable, as defined by
- * $(REF isAutodecodableString, std, traits), and if the base range is
- * also a forward range.
+ * A bidirectional range if `R` is a bidirectional range and not auto-decodable,
+ * as defined by $(REF isAutodecodableString, std, traits).
+ *
+ * A forward range if `R` is a forward range and not auto-decodable.
*
* Or, if `R` is a range and it is auto-decodable and
* `is(ElementEncodingType!typeof(r) == C)`, then the range is passed
@@ -3933,11 +4289,11 @@ int impureVariable;
*
* Otherwise, an input range of characters.
*/
-template byUTF(C)
+template byUTF(C, UseReplacementDchar useReplacementDchar = Yes.useReplacementDchar)
if (isSomeChar!C)
{
- static if (!is(Unqual!C == C))
- alias byUTF = byUTF!(Unqual!C);
+ static if (is(immutable C == immutable UC, UC) && !is(C == UC))
+ alias byUTF = byUTF!UC;
else:
auto ref byUTF(R)(R r)
@@ -3949,19 +4305,170 @@ if (isSomeChar!C)
auto ref byUTF(R)(R r)
if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
{
- alias RC = Unqual!(ElementEncodingType!R);
-
- static if (is(RC == C))
+ static if (is(immutable ElementEncodingType!R == immutable RC, RC) && is(RC == C))
{
return r.byCodeUnit();
}
+ else static if (is(C == dchar))
+ {
+ static struct Result
+ {
+ enum Empty = uint.max; // range is empty or just constructed
+
+ this(return R r)
+ {
+ this.r = r;
+ }
+
+ this(return R r, uint buff)
+ {
+ this.r = r;
+ this.buff = buff;
+ }
+
+ static if (isBidirectionalRange!R)
+ {
+ this(return R r, uint frontBuff, uint backBuff)
+ {
+ this.r = r;
+ this.buff = frontBuff;
+ this.backBuff = backBuff;
+ }
+ }
+
+ @property bool empty()
+ {
+ static if (isBidirectionalRange!R)
+ return buff == Empty && backBuff == Empty && r.empty;
+ else
+ return buff == Empty && r.empty;
+ }
+
+ @property dchar front() scope // 'scope' required by call to decodeFront() below
+ {
+ if (buff == Empty)
+ {
+ auto c = r.front;
+
+ static if (is(RC == wchar))
+ enum firstMulti = 0xD800; // First high surrogate.
+ else
+ enum firstMulti = 0x80; // First non-ASCII.
+ if (c < firstMulti)
+ {
+ r.popFront;
+ buff = cast(dchar) c;
+ }
+ else
+ {
+ buff = () @trusted { return decodeFront!(useReplacementDchar)(r); }();
+ }
+ }
+ return cast(dchar) buff;
+ }
+
+ void popFront()
+ {
+ if (buff == Empty)
+ front();
+ buff = Empty;
+ }
+
+ static if (isForwardRange!R)
+ {
+ @property auto save()
+ {
+ static if (isBidirectionalRange!R)
+ {
+ return Result(r.save, buff, backBuff);
+ }
+ else
+ {
+ return Result(r.save, buff);
+ }
+ }
+ }
+
+ static if (isBidirectionalRange!R)
+ {
+ @property dchar back() scope // 'scope' required by call to decodeBack() below
+ {
+ if (backBuff != Empty)
+ return cast(dchar) backBuff;
+
+ auto c = r.back;
+ static if (is(RC == wchar))
+ enum firstMulti = 0xD800; // First high surrogate.
+ else
+ enum firstMulti = 0x80; // First non-ASCII.
+ if (c < firstMulti)
+ {
+ r.popBack;
+ backBuff = cast(dchar) c;
+ }
+ else
+ {
+ backBuff = () @trusted { return decodeBack!useReplacementDchar(r); }();
+ }
+ return cast(dchar) backBuff;
+
+ }
+
+ void popBack()
+ {
+ if (backBuff == Empty)
+ back();
+ backBuff = Empty;
+ }
+ }
+
+ private:
+
+ R r;
+ uint buff = Empty; // one character lookahead buffer
+ static if (isBidirectionalRange!R)
+ uint backBuff = Empty;
+ }
+
+ return Result(r);
+ }
else
{
static struct Result
{
+ this(return R r)
+ {
+ this.r = r;
+ }
+
+ this(return R r, ushort pos, ushort fill, C[4 / C.sizeof] buf)
+ {
+ this.r = r;
+ this.pos = pos;
+ this.fill = fill;
+ this.buf = buf;
+ }
+
+ static if (isBidirectionalRange!R)
+ {
+ this(return R r, ushort frontPos, ushort frontFill,
+ ushort backPos, ushort backFill, C[4 / C.sizeof] buf)
+ {
+ this.r = r;
+ this.pos = frontPos;
+ this.fill = frontFill;
+ this.backPos = backPos;
+ this.backFill = backFill;
+ this.buf = buf;
+ }
+ }
+
@property bool empty()
{
- return pos == fill && r.empty;
+ static if (isBidirectionalRange!R)
+ return pos == fill && backPos == backFill && r.empty;
+ else
+ return pos == fill && r.empty;
}
@property auto front() scope // 'scope' required by call to decodeFront() below
@@ -3971,7 +4478,11 @@ if (isSomeChar!C)
pos = 0;
auto c = r.front;
- if (c <= 0x7F)
+ static if (C.sizeof >= 2 && RC.sizeof >= 2)
+ enum firstMulti = 0xD800; // First high surrogate.
+ else
+ enum firstMulti = 0x80; // First non-ASCII.
+ if (c < firstMulti)
{
fill = 1;
r.popFront;
@@ -3985,8 +4496,8 @@ if (isSomeChar!C)
dchar dc = c;
}
else
- dchar dc = () @trusted { return decodeFront!(Yes.useReplacementDchar)(r); }();
- fill = cast(ushort) encode!(Yes.useReplacementDchar)(buf, dc);
+ dchar dc = () @trusted { return decodeFront!(useReplacementDchar)(r); }();
+ fill = cast(ushort) encode!(useReplacementDchar)(buf, dc);
}
}
return buf[pos];
@@ -4001,22 +4512,67 @@ if (isSomeChar!C)
static if (isForwardRange!R)
{
- @property auto save() return scope
- /* `return scope` cannot be inferred because compiler does not
- * track it backwards from assignment to local `ret`
- */
+ @property auto save()
+ {
+ static if (isBidirectionalRange!R)
+ {
+ return Result(r.save, pos, fill, backPos, backFill, buf);
+ }
+ else
+ {
+ return Result(r.save, pos, fill, buf);
+ }
+ }
+ }
+
+ static if (isBidirectionalRange!R)
+ {
+ @property auto back() scope // 'scope' required by call to decodeBack() below
+ {
+ if (backPos != backFill)
+ return buf[cast(ushort) (backFill - backPos - 1)];
+
+ backPos = 0;
+ auto c = r.back;
+ static if (C.sizeof >= 2 && RC.sizeof >= 2)
+ enum firstMulti = 0xD800; // First high surrogate.
+ else
+ enum firstMulti = 0x80; // First non-ASCII.
+ if (c < firstMulti)
+ {
+ backFill = 1;
+ r.popBack;
+ buf[cast(ushort) (backFill - backPos - 1)] = cast(C) c;
+ }
+ else
+ {
+ static if (is(RC == dchar))
+ {
+ r.popBack;
+ dchar dc = c;
+ }
+ else
+ dchar dc = () @trusted { return decodeBack!(useReplacementDchar)(r); }();
+ backFill = cast(ushort) encode!(useReplacementDchar)(buf, dc);
+ }
+ return buf[cast(ushort) (backFill - backPos - 1)];
+ }
+
+ void popBack()
{
- auto ret = this;
- ret.r = r.save;
- return ret;
+ if (backPos == backFill)
+ back;
+ ++backPos;
}
}
private:
R r;
- C[4 / C.sizeof] buf = void;
ushort pos, fill;
+ static if (isBidirectionalRange!R)
+ ushort backPos, backFill;
+ C[4 / C.sizeof] buf = void;
}
return Result(r);
@@ -4030,13 +4586,140 @@ if (isSomeChar!C)
import std.algorithm.comparison : equal;
// hellö as a range of `char`s, which are UTF-8
- "hell\u00F6".byUTF!char().equal(['h', 'e', 'l', 'l', 0xC3, 0xB6]);
+ assert("hell\u00F6".byUTF!char().equal(['h', 'e', 'l', 'l', 0xC3, 0xB6]));
// `wchar`s are able to hold the ö in a single element (UTF-16 code unit)
- "hell\u00F6".byUTF!wchar().equal(['h', 'e', 'l', 'l', 'ö']);
+ assert("hell\u00F6".byUTF!wchar().equal(['h', 'e', 'l', 'l', 'ö']));
// ð· is four code units in UTF-8, two in UTF-16, and one in UTF-32
- "ð·".byUTF!char().equal([0xF0, 0x90, 0x90, 0xB7]);
- "ð·".byUTF!wchar().equal([0xD801, 0xDC37]);
- "ð·".byUTF!dchar().equal([0x00010437]);
+ assert("ð·".byUTF!char().equal([0xF0, 0x90, 0x90, 0xB7]));
+ assert("ð·".byUTF!wchar().equal([0xD801, 0xDC37]));
+ assert("ð·".byUTF!dchar().equal([0x00010437]));
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.exception : assertThrown;
+
+ assert("hello\xF0betty".byChar.byUTF!(dchar, UseReplacementDchar.yes).equal("hello\uFFFDetty"));
+ assertThrown!UTFException("hello\xF0betty".byChar.byUTF!(dchar, UseReplacementDchar.no).equal("hello betty"));
+}
+
+@safe unittest
+{
+ {
+ wchar[] s = ['a', 'b', 0x219];
+ auto r = s.byUTF!char;
+ assert(isBidirectionalRange!(typeof(r)));
+ assert(r.back == 0x99);
+ r.popBack;
+ assert(r.back == 0xc8);
+ r.popBack;
+ assert(r.back == 'b');
+
+ }
+
+ {
+ wchar[] s = ['a', 'b', 0x219];
+ auto r = s.byUTF!wchar;
+ uint i;
+ assert(isBidirectionalRange!(typeof(r)));
+ assert(r.back == 0x219);
+ r.popBack;
+ assert(r.back == 'b');
+ }
+
+ {
+ wchar[] s = ['a', 'b', 0x219];
+ auto r = s.byUTF!dchar;
+ assert(isBidirectionalRange!(typeof(r)));
+ assert(r.back == 0x219);
+ r.popBack;
+ assert(r.back == 'b');
+ }
+
+ {
+ dchar[] s = ['ð·', 'ðŸ˜'];
+ auto r = s.byUTF!wchar;
+ assert(r.back == 0xde01);
+ r.popBack;
+ assert(r.back == 0xd83d);
+ r.popBack;
+ assert(r.back == 0xdc37);
+ r.popBack;
+ assert(r.back == 0xd801);
+ }
+
+ {
+ dchar[] s = ['ð·', 'ðŸ˜'];
+ auto r = s.byUTF!char;
+ char[] res;
+ while (!r.empty)
+ {
+ res ~= r.back;
+ r.popBack;
+ }
+ import std.algorithm.comparison : equal;
+ assert(res.equal([0x81, 0x98, 0x9f, 0xf0, 0xb7, 0x90, 0x90, 0xf0]));
+ }
+
+ {
+ dchar[] res;
+ auto r = ['a', 'b', 'c', 'd', 'e'].byUTF!dchar;
+ while (!r.empty)
+ {
+ res ~= r.back;
+ r.popBack;
+ }
+ import std.algorithm.comparison : equal;
+ assert(res.equal(['e', 'd', 'c', 'b', 'a']));
+ }
+
+ {
+ //testing the save() function
+ wchar[] s = ['Ä‚','È›'];
+
+ auto rc = s.byUTF!char;
+ rc.popBack;
+ auto rcCopy = rc.save;
+ assert(rc.back == rcCopy.back);
+ assert(rcCopy.back == 0xc8);
+
+ auto rd = s.byUTF!dchar;
+ rd.popBack;
+ auto rdCopy = rd.save;
+ assert(rd.back == rdCopy.back);
+ assert(rdCopy.back == 'Ä‚');
+ }
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.range.primitives;
+ wchar[] s = ['ă', 'î'];
+
+ auto rc = s.byUTF!char;
+ static assert(isBidirectionalRange!(typeof(rc)));
+ assert(rc.back == 0xae);
+ rc.popBack;
+ assert(rc.back == 0xc3);
+ rc.popBack;
+ assert(rc.back == 0x83);
+ rc.popBack;
+ assert(rc.back == 0xc4);
+
+ auto rw = s.byUTF!wchar;
+ static assert(isBidirectionalRange!(typeof(rw)));
+ assert(rw.back == 'î');
+ rw.popBack;
+ assert(rw.back == 'ă');
+
+ auto rd = s.byUTF!dchar;
+ static assert(isBidirectionalRange!(typeof(rd)));
+ assert(rd.back == 'î');
+ rd.popBack;
+ assert(rd.back == 'ă');
}
diff --git a/libphobos/src/std/uuid.d b/libphobos/src/std/uuid.d
index c804e8eb7e8..dec2a1c276d 100644
--- a/libphobos/src/std/uuid.d
+++ b/libphobos/src/std/uuid.d
@@ -68,11 +68,11 @@ $(TR $(TDNW UUID namespaces)
*
* For efficiency, UUID is implemented as a struct. UUIDs are therefore empty if not explicitly
* initialized. An UUID is empty if $(MYREF3 UUID.empty, empty) is true. Empty UUIDs are equal to
- * $(D UUID.init), which is a UUID with all 16 bytes set to 0.
+ * `UUID.init`, which is a UUID with all 16 bytes set to 0.
* Use UUID's constructors or the UUID generator functions to get an initialized UUID.
*
* This is a port of $(LINK2 http://www.boost.org/doc/libs/1_42_0/libs/uuid/uuid.html,
- * boost._uuid) from the Boost project with some minor additions and API
+ * boost.uuid) from the Boost project with some minor additions and API
* changes for a more D-like API.
*
* Standards:
@@ -84,11 +84,11 @@ $(TR $(TDNW UUID namespaces)
* Copyright: Copyright Johannes Pfau 2011 - .
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Johannes Pfau
- * Source: $(PHOBOSSRC std/_uuid.d)
+ * Source: $(PHOBOSSRC std/uuid.d)
*
* Macros:
* MYREF2 = <a href="#$2">$(TT $1)</a>&nbsp;
- * MYREF3 = <a href="#$2">$(D $1)</a>
+ * MYREF3 = <a href="#$2">`$1`</a>
*/
/* Copyright Johannes Pfau 2011 - 2012.
* Distributed under the Boost Software License, Version 1.0.
@@ -178,7 +178,7 @@ public struct UUID
*
* Note:
* All of these UUID versions can be read and processed by
- * $(D std.uuid), but only version 3, 4 and 5 UUIDs can be generated.
+ * `std.uuid`, but only version 3, 4 and 5 UUIDs can be generated.
*/
enum Version
{
@@ -249,12 +249,12 @@ public struct UUID
* Construct a UUID struct from the 16 byte representation
* of a UUID.
*/
- @safe pure nothrow @nogc this(ref in ubyte[16] uuidData)
+ @safe pure nothrow @nogc this(ref const scope ubyte[16] uuidData)
{
data = uuidData;
}
/// ditto
- @safe pure nothrow @nogc this(in ubyte[16] uuidData)
+ @safe pure nothrow @nogc this(const ubyte[16] uuidData)
{
data = uuidData;
}
@@ -331,7 +331,7 @@ public struct UUID
*
* For a less strict parser, see $(LREF parseUUID)
*/
- this(T)(in T[] uuid) if (isSomeChar!(Unqual!T))
+ this(T)(in T[] uuid) if (isSomeChar!T)
{
import std.conv : to, parse;
if (uuid.length < 36)
@@ -404,13 +404,13 @@ public struct UUID
{
import std.conv : to;
import std.exception;
- import std.meta;
+ import std.meta : AliasSeq;
- foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
+ static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[],
immutable(char[]), immutable(wchar[]), immutable(dchar[])))
- {
+ {{
//Test valid, working cases
assert(UUID(to!S("00000000-0000-0000-0000-000000000000")).empty);
@@ -456,7 +456,7 @@ public struct UUID
== UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01,
0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]));
}
- }
+ }}
/**
* Returns true if and only if the UUID is equal
@@ -664,7 +664,7 @@ public struct UUID
* All of the standard numeric operators are defined for
* the UUID struct.
*/
- @safe pure nothrow @nogc bool opEquals(in UUID s) const
+ @safe pure nothrow @nogc bool opEquals(const UUID s) const
{
return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1];
}
@@ -693,7 +693,7 @@ public struct UUID
/**
* ditto
*/
- @safe pure nothrow @nogc bool opEquals(ref in UUID s) const
+ @safe pure nothrow @nogc bool opEquals(ref const scope UUID s) const
{
return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1];
}
@@ -701,7 +701,7 @@ public struct UUID
/**
* ditto
*/
- @safe pure nothrow @nogc int opCmp(in UUID s) const
+ @safe pure nothrow @nogc int opCmp(const UUID s) const
{
import std.algorithm.comparison : cmp;
return cmp(this.data[], s.data[]);
@@ -710,7 +710,7 @@ public struct UUID
/**
* ditto
*/
- @safe pure nothrow @nogc int opCmp(ref in UUID s) const
+ @safe pure nothrow @nogc int opCmp(ref const scope UUID s) const
{
import std.algorithm.comparison : cmp;
return cmp(this.data[], s.data[]);
@@ -719,7 +719,7 @@ public struct UUID
/**
* ditto
*/
- @safe pure nothrow @nogc UUID opAssign(in UUID s)
+ @safe pure nothrow @nogc UUID opAssign(const UUID s)
{
ulongs[0] = s.ulongs[0];
ulongs[1] = s.ulongs[1];
@@ -729,7 +729,7 @@ public struct UUID
/**
* ditto
*/
- @safe pure nothrow @nogc UUID opAssign(ref in UUID s)
+ @safe pure nothrow @nogc UUID opAssign(ref const scope UUID s)
{
ulongs[0] = s.ulongs[0];
ulongs[1] = s.ulongs[1];
@@ -880,13 +880,15 @@ public struct UUID
const uint lo = (entry) & 0x0F;
result[pos+1] = toChar!char(lo);
}
- foreach (i, c; result)
+ static if (!__traits(compiles, put(sink, result[])) || isSomeString!Writer)
{
- static if (__traits(compiles, put(sink, c)))
- put(sink, c);
- else
+ foreach (i, c; result)
sink[i] = cast(typeof(sink[i]))c;
}
+ else
+ {
+ put(sink, result[]);
+ }
}
/**
@@ -911,8 +913,8 @@ public struct UUID
@safe pure nothrow @nogc unittest
{
import std.meta : AliasSeq;
- foreach (Char; AliasSeq!(char, wchar, dchar))
- {
+ static foreach (Char; AliasSeq!(char, wchar, dchar))
+ {{
alias String = immutable(Char)[];
//CTFE
enum String s = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
@@ -926,7 +928,7 @@ public struct UUID
Char[36] str;
id.toString(str[]);
assert(str == s);
- }
+ }}
}
@system pure nothrow @nogc unittest
@@ -952,7 +954,7 @@ public struct UUID
assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
char[] buf;
- void sink(const(char)[] data)
+ void sink(scope const(char)[] data)
{
buf ~= data;
}
@@ -961,10 +963,23 @@ public struct UUID
}
}
+///
+@safe unittest
+{
+ UUID id;
+ assert(id.empty);
+
+ id = randomUUID;
+ assert(!id.empty);
+
+ id = UUID(cast(ubyte[16]) [138, 179, 6, 14, 44, 186, 79,
+ 35, 183, 76, 181, 45, 179, 189, 251, 70]);
+ assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+}
/**
* This function generates a name based (Version 3) UUID from a namespace UUID and a name.
- * If no namespace UUID was passed, the empty UUID $(D UUID.init) is used.
+ * If no namespace UUID was passed, the empty UUID `UUID.init` is used.
*
* Note:
* The default namespaces ($(LREF dnsNamespace), ...) defined by
@@ -980,8 +995,8 @@ public struct UUID
* RFC 4122 isn't very clear on how UUIDs should be generated from names.
* It is possible that different implementations return different UUIDs
* for the same input, so be warned. The implementation for UTF-8 strings
- * and byte arrays used by $(D std.uuid) is compatible with Boost's implementation.
- * $(D std.uuid) guarantees that the same input to this function will generate
+ * and byte arrays used by `std.uuid` is compatible with Boost's implementation.
+ * `std.uuid` guarantees that the same input to this function will generate
* the same output at any time, on any system (this especially means endianness
* doesn't matter).
*
@@ -1078,7 +1093,7 @@ public struct UUID
/**
* This function generates a name based (Version 5) UUID from a namespace
* UUID and a name.
- * If no namespace UUID was passed, the empty UUID $(D UUID.init) is used.
+ * If no namespace UUID was passed, the empty UUID `UUID.init` is used.
*
* Note:
* The default namespaces ($(LREF dnsNamespace), ...) defined by
@@ -1091,8 +1106,8 @@ public struct UUID
* RFC 4122 isn't very clear on how UUIDs should be generated from names.
* It is possible that different implementations return different UUIDs
* for the same input, so be warned. The implementation for UTF-8 strings
- * and byte arrays used by $(D std.uuid) is compatible with Boost's implementation.
- * $(D std.uuid) guarantees that the same input to this function will generate
+ * and byte arrays used by `std.uuid` is compatible with Boost's implementation.
+ * `std.uuid` guarantees that the same input to this function will generate
* the same output at any time, on any system (this especially means endianness
* doesn't matter).
*
@@ -1104,13 +1119,13 @@ public struct UUID
* for strings and wstrings. It's always possible to pass wstrings and dstrings
* by using the ubyte[] function overload (but be aware of endianness issues!).
*/
-@safe pure nothrow @nogc UUID sha1UUID(in char[] name, const UUID namespace = UUID.init)
+@safe pure nothrow @nogc UUID sha1UUID(scope const(char)[] name, scope const UUID namespace = UUID.init)
{
return sha1UUID(cast(const(ubyte[]))name, namespace);
}
/// ditto
-@safe pure nothrow @nogc UUID sha1UUID(in ubyte[] data, const UUID namespace = UUID.init)
+@safe pure nothrow @nogc UUID sha1UUID(scope const(ubyte)[] data, scope const UUID namespace = UUID.init)
{
import std.digest.sha : SHA1;
@@ -1195,7 +1210,25 @@ public struct UUID
@safe UUID randomUUID()
{
import std.random : rndGen;
- return randomUUID(rndGen);
+ // A PRNG with fewer than `n` bytes of state cannot produce
+ // every distinct `n` byte sequence.
+ static if (typeof(rndGen).sizeof >= UUID.sizeof)
+ {
+ return randomUUID(rndGen);
+ }
+ else
+ {
+ import std.random : unpredictableSeed, Xorshift192;
+ static assert(Xorshift192.sizeof >= UUID.sizeof);
+ static Xorshift192 rng;
+ static bool initialized;
+ if (!initialized)
+ {
+ rng.seed(unpredictableSeed);
+ initialized = true;
+ }
+ return randomUUID(rng);
+ }
}
/// ditto
@@ -1245,18 +1278,6 @@ if (isInputRange!RNG && isIntegral!(ElementType!RNG))
auto uuid3 = randomUUID(gen);
}
-/*
- * Original boost.uuid used Mt19937, we don't want
- * to use anything worse than that. If Random is changed
- * to something else, this assert and the randomUUID function
- * have to be updated.
- */
-@safe unittest
-{
- import std.random : rndGen, Mt19937;
- static assert(is(typeof(rndGen) == Mt19937));
-}
-
@safe unittest
{
import std.random : Xorshift192, unpredictableSeed;
@@ -1311,8 +1332,7 @@ if (isSomeString!T)
///ditto
UUID parseUUID(Range)(ref Range uuidRange)
-if (isInputRange!Range
- && is(Unqual!(ElementType!Range) == dchar))
+if (isInputRange!Range && isSomeChar!(ElementType!Range))
{
import std.ascii : isHexDigit;
import std.conv : ConvException, parse;
@@ -1527,12 +1547,12 @@ if (isInputRange!Range
return parseUUID(to!T(input));
}
- foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
+ static foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
wchar[], const(wchar)[], immutable(wchar)[],
dchar[], const(dchar)[], immutable(dchar)[],
immutable(char[]), immutable(wchar[]), immutable(dchar[]),
TestForwardRange, TestInputRange))
- {
+ {{
//Verify examples.
auto id = parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
//no dashes
@@ -1608,6 +1628,13 @@ if (isInputRange!Range
//multiple trailing/leading characters
assert(parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||")
== parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+ }}
+
+ // Test input range with non-dchar element type.
+ {
+ import std.utf : byCodeUnit;
+ auto range = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46".byCodeUnit;
+ assert(parseUUID(range).data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]);
}
}
diff --git a/libphobos/src/std/variant.d b/libphobos/src/std/variant.d
index 574e2c5a375..953d6eafaee 100644
--- a/libphobos/src/std/variant.d
+++ b/libphobos/src/std/variant.d
@@ -2,7 +2,7 @@
/**
This module implements a
-$(HTTP erdani.org/publications/cuj-04-2002.html,discriminated union)
+$(HTTP erdani.org/publications/cuj-04-2002.php.html,discriminated union)
type (a.k.a.
$(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union),
$(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)).
@@ -13,8 +13,8 @@ languages, and comfortable exploratory programming.
A $(LREF Variant) object can hold a value of any type, with very few
restrictions (such as `shared` types and noncopyable types). Setting the value
is as immediate as assigning to the `Variant` object. To read back the value of
-the appropriate type `T`, use the $(LREF get!T) call. To query whether a
-`Variant` currently holds a value of type `T`, use $(LREF peek!T). To fetch the
+the appropriate type `T`, use the $(LREF get) method. To query whether a
+`Variant` currently holds a value of type `T`, use $(LREF peek). To fetch the
exact type currently held, call $(LREF type), which returns the `TypeInfo` of
the current value.
@@ -23,13 +23,16 @@ type constructor. Unlike `Variant`, `Algebraic` only allows a finite set of
types, which are specified in the instantiation (e.g. $(D Algebraic!(int,
string)) may only hold an `int` or a `string`).
+$(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new
+code. Instead, use $(REF SumType, std,sumtype).)
+
Credits: Reviewed by Brad Roberts. Daniel Keep provided a detailed code review
prompting the following improvements: (1) better support for arrays; (2) support
for associative arrays; (3) friendlier behavior towards the garbage collector.
Copyright: Copyright Andrei Alexandrescu 2007 - 2015.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.org, Andrei Alexandrescu)
-Source: $(PHOBOSSRC std/_variant.d)
+Source: $(PHOBOSSRC std/variant.d)
*/
module std.variant;
@@ -73,59 +76,98 @@ import std.meta, std.traits, std.typecons;
}
/++
- Gives the $(D sizeof) the largest type given.
+ Gives the `sizeof` the largest type given.
+
+ See_Also: https://forum.dlang.org/thread/wbpnncxepehgcswhuazl@forum.dlang.org?page=1
+/
-template maxSize(T...)
+template maxSize(Ts...)
{
- static if (T.length == 1)
- {
- enum size_t maxSize = T[0].sizeof;
- }
- else
+ align(1) union Impl
{
- import std.algorithm.comparison : max;
- enum size_t maxSize = max(T[0].sizeof, maxSize!(T[1 .. $]));
+ static foreach (i, T; Ts)
+ {
+ static if (!is(T == void))
+ mixin("T _field_", i, ";");
+ }
}
+ enum maxSize = Impl.sizeof;
}
///
@safe unittest
{
+ struct Cat { int a, b, c; }
+
+ align(1) struct S
+ {
+ long l;
+ ubyte b;
+ }
+
+ align(1) struct T
+ {
+ ubyte b;
+ long l;
+ }
+
static assert(maxSize!(int, long) == 8);
static assert(maxSize!(bool, byte) == 1);
-
- struct Cat { int a, b, c; }
static assert(maxSize!(bool, Cat) == 12);
+ static assert(maxSize!(char) == 1);
+ static assert(maxSize!(char, short, ubyte) == 2);
+ static assert(maxSize!(char, long, ubyte) == 8);
+ import std.algorithm.comparison : max;
+ static assert(maxSize!(long, S) == max(long.sizeof, S.sizeof));
+ static assert(maxSize!(S, T) == max(S.sizeof, T.sizeof));
+ static assert(maxSize!(int, ubyte[7]) == 7);
+ static assert(maxSize!(int, ubyte[3]) == 4);
+ static assert(maxSize!(int, int, ubyte[3]) == 4);
+ static assert(maxSize!(void, int, ubyte[3]) == 4);
+ static assert(maxSize!(void) == 1);
}
struct This;
-private alias This2Variant(V, T...) = AliasSeq!(ReplaceType!(This, V, T));
+private alias This2Variant(V, T...) = AliasSeq!(ReplaceTypeUnless!(isAlgebraic, This, V, T));
+
+// We can't just use maxAlignment because no types might be specified
+// to VariantN, so handle that here and then pass along the rest.
+private template maxVariantAlignment(U...)
+if (isTypeTuple!U)
+{
+ static if (U.length == 0)
+ {
+ import std.algorithm.comparison : max;
+ enum maxVariantAlignment = max(real.alignof, size_t.alignof);
+ }
+ else
+ enum maxVariantAlignment = maxAlignment!(U);
+}
/**
* Back-end type seldom used directly by user
- * code. Two commonly-used types using $(D VariantN) are:
+ * code. Two commonly-used types using `VariantN` are:
*
* $(OL $(LI $(LREF Algebraic): A closed discriminated union with a
* limited type universe (e.g., $(D Algebraic!(int, double,
* string)) only accepts these three types and rejects anything
* else).) $(LI $(LREF Variant): An open discriminated union allowing an
- * unbounded set of types. If any of the types in the $(D Variant)
+ * unbounded set of types. If any of the types in the `Variant`
* are larger than the largest built-in type, they will automatically
* be boxed. This means that even large types will only be the size
- * of a pointer within the $(D Variant), but this also implies some
- * overhead. $(D Variant) can accommodate all primitive types and
+ * of a pointer within the `Variant`, but this also implies some
+ * overhead. `Variant` can accommodate all primitive types and
* all user-defined types.))
*
- * Both $(D Algebraic) and $(D Variant) share $(D
+ * Both `Algebraic` and `Variant` share $(D
* VariantN)'s interface. (See their respective documentations below.)
*
- * $(D VariantN) is a discriminated union type parameterized
- * with the largest size of the types stored ($(D maxDataSize))
- * and with the list of allowed types ($(D AllowedTypes)). If
+ * `VariantN` is a discriminated union type parameterized
+ * with the largest size of the types stored (`maxDataSize`)
+ * and with the list of allowed types (`AllowedTypes`). If
* the list is empty, then any type up of size up to $(D
* maxDataSize) (rounded up for alignment) can be stored in a
- * $(D VariantN) object without being boxed (types larger
+ * `VariantN` object without being boxed (types larger
* than this will be boxed).
*
*/
@@ -145,9 +187,9 @@ private:
}
enum size = SizeChecker.sizeof - (int function()).sizeof;
- /** Tells whether a type $(D T) is statically _allowed for
- * storage inside a $(D VariantN) object by looking
- * $(D T) up in $(D AllowedTypes).
+ /** Tells whether a type `T` is statically _allowed for
+ * storage inside a `VariantN` object by looking
+ * `T` up in `AllowedTypes`.
*/
public template allowed(T)
{
@@ -165,15 +207,15 @@ private:
apply, postblit, destruct }
// state
- ptrdiff_t function(OpID selector, ubyte[size]* store, void* data) fptr
- = &handler!(void);
union
{
- ubyte[size] store;
+ align(maxVariantAlignment!(AllowedTypes)) ubyte[size] store;
// conservatively mark the region as pointers
static if (size >= (void*).sizeof)
void*[size / (void*).sizeof] p;
}
+ ptrdiff_t function(OpID selector, ubyte[size]* store, void* data) fptr
+ = &handler!(void);
// internals
// Handler for an uninitialized value
@@ -235,25 +277,35 @@ private:
{
static if (is(typeof(*rhsPA == *zis)))
{
- if (*rhsPA == *zis)
+ enum isEmptyStructWithoutOpEquals = is(A == struct) && A.tupleof.length == 0 &&
+ !__traits(hasMember, A, "opEquals");
+ static if (isEmptyStructWithoutOpEquals)
{
+ // The check above will always succeed if A is an empty struct.
+ // Don't generate unreachable code as seen in
+ // https://issues.dlang.org/show_bug.cgi?id=21231
return 0;
}
- static if (is(typeof(*zis < *rhsPA)))
+ else
{
- // Many types (such as any using the default Object opCmp)
- // will throw on an invalid opCmp, so do it only
- // if the caller requests it.
- if (selector == OpID.compare)
- return *zis < *rhsPA ? -1 : 1;
+ if (*rhsPA == *zis)
+ return 0;
+ static if (is(typeof(*zis < *rhsPA)))
+ {
+ // Many types (such as any using the default Object opCmp)
+ // will throw on an invalid opCmp, so do it only
+ // if the caller requests it.
+ if (selector == OpID.compare)
+ return *zis < *rhsPA ? -1 : 1;
+ else
+ return ptrdiff_t.min;
+ }
else
+ {
+ // Not equal, and type does not support ordering
+ // comparisons.
return ptrdiff_t.min;
- }
- else
- {
- // Not equal, and type does not support ordering
- // comparisons.
- return ptrdiff_t.min;
+ }
}
}
else
@@ -271,7 +323,14 @@ private:
static bool tryPutting(A* src, TypeInfo targetType, void* target)
{
alias UA = Unqual!A;
- alias MutaTypes = AliasSeq!(UA, ImplicitConversionTargets!UA);
+ static if (isStaticArray!A && is(typeof(UA.init[0])))
+ {
+ alias MutaTypes = AliasSeq!(UA, typeof(UA.init[0])[], AllImplicitConversionTargets!UA);
+ }
+ else
+ {
+ alias MutaTypes = AliasSeq!(UA, AllImplicitConversionTargets!UA);
+ }
alias ConstTypes = staticMap!(ConstOf, MutaTypes);
alias SharedTypes = staticMap!(SharedOf, MutaTypes);
alias SharedConstTypes = staticMap!(SharedConstOf, MutaTypes);
@@ -299,13 +358,20 @@ private:
if (targetType != typeid(T))
continue;
- static if (is(typeof(*cast(T*) target = *src)) ||
+ // SPECIAL NOTE: variant only will ever create a new value with
+ // tryPutting (effectively), and T is ALWAYS the same type of
+ // A, but with different modifiers (and a limited set of
+ // implicit targets). So this checks to see if we can construct
+ // a T from A, knowing that prerequisite. This handles issues
+ // where the type contains some constant data aside from the
+ // modifiers on the type itself.
+ static if (is(typeof(delegate T() {return *src;})) ||
is(T == const(U), U) ||
is(T == shared(U), U) ||
is(T == shared const(U), U) ||
is(T == immutable(U), U))
{
- import std.conv : emplaceRef;
+ import core.internal.lifetime : emplaceRef;
auto zat = cast(T*) target;
if (src)
@@ -313,7 +379,15 @@ private:
static if (T.sizeof > 0)
assert(target, "target must be non-null");
- emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src);
+ static if (isStaticArray!A && isDynamicArray!T)
+ {
+ auto this_ = (*src)[];
+ emplaceRef(*cast(Unqual!T*) zat, cast(Unqual!T) this_);
+ }
+ else
+ {
+ emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src);
+ }
}
}
else
@@ -339,7 +413,17 @@ private:
static if (target.size < A.sizeof)
{
if (target.type.tsize < A.sizeof)
- *cast(A**)&target.store = new A;
+ {
+ static if (is(A == U[n], U, size_t n))
+ {
+ A* p = cast(A*)(new U[n]).ptr;
+ }
+ else
+ {
+ A* p = new A;
+ }
+ *cast(A**)&target.store = p;
+ }
}
tryPutting(zis, typeid(A), cast(void*) getPtr(&target.store))
|| assert(false);
@@ -411,7 +495,7 @@ private:
case OpID.index:
auto result = cast(Variant*) parm;
- static if (isArray!(A) && !is(Unqual!(typeof(A.init[0])) == void))
+ static if (isArray!(A) && !is(immutable typeof(A.init[0]) == immutable void))
{
// array type; input and output are the same VariantN
size_t index = result.convertsTo!(int)
@@ -439,7 +523,7 @@ private:
(*zis)[index] = args[0].get!(typeof((*zis)[0]));
break;
}
- else static if (isAssociativeArray!(A))
+ else static if (isAssociativeArray!(A) && is(typeof((*zis)[A.init.keys[0]] = A.init.values[0])))
{
(*zis)[args[1].get!(typeof(A.init.keys[0]))]
= args[0].get!(typeof(A.init.values[0]));
@@ -451,7 +535,8 @@ private:
}
case OpID.catAssign:
- static if (!is(Unqual!(typeof((*zis)[0])) == void) && is(typeof((*zis)[0])) && is(typeof((*zis) ~= *zis)))
+ static if (!is(immutable typeof((*zis)[0]) == immutable void) &&
+ is(typeof((*zis)[0])) && is(typeof(*zis ~= *zis)))
{
// array type; parm is the element to append
auto arg = cast(Variant*) parm;
@@ -529,14 +614,14 @@ private:
case OpID.postblit:
static if (hasElaborateCopyConstructor!A)
{
- typeid(A).postblit(zis);
+ zis.__xpostblit();
}
break;
case OpID.destruct:
static if (hasElaborateDestructor!A)
{
- typeid(A).destroy(zis);
+ zis.__xdtor();
}
break;
@@ -545,10 +630,8 @@ private:
return 0;
}
- enum doUnittest = is(VariantN == Variant);
-
public:
- /** Constructs a $(D VariantN) value given an argument of a
+ /** Constructs a `VariantN` value given an argument of a
* generic type. Statically rejects disallowed types.
*/
@@ -578,16 +661,23 @@ public:
{
~this()
{
- fptr(OpID.destruct, &store, null);
+ // Infer the safety of the provided types
+ static if (AllowedTypes.length)
+ {
+ if (0)
+ {
+ AllowedTypes var;
+ }
+ }
+ (() @trusted => fptr(OpID.destruct, &store, null))();
}
}
- /** Assigns a $(D VariantN) from a generic
+ /** Assigns a `VariantN` from a generic
* argument. Statically rejects disallowed types. */
VariantN opAssign(T)(T rhs)
{
- //writeln(typeid(rhs));
static assert(allowed!(T), "Cannot store a " ~ T.stringof
~ " in a " ~ VariantN.stringof ~ ". Valid types are "
~ AllowedTypes.stringof);
@@ -604,6 +694,8 @@ public:
}
else
{
+ import core.lifetime : copyEmplace;
+
static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes))
{
// Assignment should destruct previous value
@@ -611,37 +703,17 @@ public:
}
static if (T.sizeof <= size)
- {
- import core.stdc.string : memcpy;
- // If T is a class we're only copying the reference, so it
- // should be safe to cast away shared so the memcpy will work.
- //
- // TODO: If a shared class has an atomic reference then using
- // an atomic load may be more correct. Just make sure
- // to use the fastest approach for the load op.
- static if (is(T == class) && is(T == shared))
- memcpy(&store, cast(const(void*)) &rhs, rhs.sizeof);
- else
- memcpy(&store, &rhs, rhs.sizeof);
- static if (hasElaborateCopyConstructor!T)
- {
- typeid(T).postblit(&store);
- }
- }
+ copyEmplace(rhs, *cast(T*) &store);
else
{
- import core.stdc.string : memcpy;
- static if (__traits(compiles, {new T(T.init);}))
- {
- auto p = new T(rhs);
- }
+ static if (is(T == U[n], U, size_t n))
+ auto p = cast(T*) (new U[n]).ptr;
else
- {
auto p = new T;
- *p = rhs;
- }
- memcpy(&store, &p, p.sizeof);
+ copyEmplace(rhs, *p);
+ *(cast(T**) &store) = p;
}
+
fptr = &handler!(T);
}
return this;
@@ -671,7 +743,7 @@ public:
return pack[0];
}
- /** Returns true if and only if the $(D VariantN) object
+ /** Returns true if and only if the `VariantN` object
* holds a valid value (has been initialized with, or assigned
* from, a valid value).
*/
@@ -682,7 +754,7 @@ public:
}
///
- static if (doUnittest)
+ version (StdDdoc)
@system unittest
{
Variant a;
@@ -695,10 +767,10 @@ public:
}
/**
- * If the $(D VariantN) object holds a value of the
- * $(I exact) type $(D T), returns a pointer to that
- * value. Otherwise, returns $(D null). In cases
- * where $(D T) is statically disallowed, $(D
+ * If the `VariantN` object holds a value of the
+ * $(I exact) type `T`, returns a pointer to that
+ * value. Otherwise, returns `null`. In cases
+ * where `T` is statically disallowed, $(D
* peek) will not compile.
*/
@property inout(T)* peek(T)() inout
@@ -715,7 +787,7 @@ public:
}
///
- static if (doUnittest)
+ version (StdDdoc)
@system unittest
{
Variant a = 5;
@@ -726,7 +798,7 @@ public:
}
/**
- * Returns the $(D typeid) of the currently held value.
+ * Returns the `typeid` of the currently held value.
*/
@property TypeInfo type() const nothrow @trusted
@@ -739,10 +811,10 @@ public:
}
/**
- * Returns $(D true) if and only if the $(D VariantN)
+ * Returns `true` if and only if the `VariantN`
* object holds an object implicitly convertible to type `T`.
* Implicit convertibility is defined as per
- * $(REF_ALTTEXT ImplicitConversionTargets, ImplicitConversionTargets, std,traits).
+ * $(REF_ALTTEXT AllImplicitConversionTargets, AllImplicitConversionTargets, std,traits).
*/
@property bool convertsTo(T)() const
@@ -790,11 +862,11 @@ public:
}
/**
- * Returns the value stored in the $(D VariantN) object,
+ * Returns the value stored in the `VariantN` object,
* explicitly converted (coerced) to the requested type $(D
- * T). If $(D T) is a string type, the value is formatted as
- * a string. If the $(D VariantN) object is a string, a
- * parse of the string to type $(D T) is attempted. If a
+ * T). If `T` is a string type, the value is formatted as
+ * a string. If the `VariantN` object is a string, a
+ * parse of the string to type `T` is attempted. If a
* conversion is not possible, throws a $(D
* VariantException).
*/
@@ -862,9 +934,9 @@ public:
// returns 1 if the two are equal
bool opEquals(T)(auto ref T rhs) const
- if (allowed!T || is(Unqual!T == VariantN))
+ if (allowed!T || is(immutable T == immutable VariantN))
{
- static if (is(Unqual!T == VariantN))
+ static if (is(immutable T == immutable VariantN))
alias temp = rhs;
else
auto temp = VariantN(rhs);
@@ -881,7 +953,7 @@ public:
/**
* Ordering comparison used by the "<", "<=", ">", and ">="
* operators. In case comparison is not sensible between the held
- * value and $(D rhs), an exception is thrown.
+ * value and `rhs`, an exception is thrown.
*/
int opCmp(T)(T rhs)
@@ -985,79 +1057,42 @@ public:
}
/**
- * Arithmetic between $(D VariantN) objects and numeric
- * values. All arithmetic operations return a $(D VariantN)
+ * Arithmetic between `VariantN` objects and numeric
+ * values. All arithmetic operations return a `VariantN`
* object typed depending on the types of both values
* involved. The conversion rules mimic D's built-in rules for
* arithmetic conversions.
*/
-
- // Adapted from http://www.prowiki.org/wiki4d/wiki.cgi?DanielKeep/Variant
- // arithmetic
- VariantN opAdd(T)(T rhs) { return opArithmetic!(T, "+")(rhs); }
- ///ditto
- VariantN opSub(T)(T rhs) { return opArithmetic!(T, "-")(rhs); }
-
- // Commenteed all _r versions for now because of ambiguities
- // arising when two Variants are used
-
- // ///ditto
- // VariantN opSub_r(T)(T lhs)
- // {
- // return VariantN(lhs).opArithmetic!(VariantN, "-")(this);
- // }
- ///ditto
- VariantN opMul(T)(T rhs) { return opArithmetic!(T, "*")(rhs); }
- ///ditto
- VariantN opDiv(T)(T rhs) { return opArithmetic!(T, "/")(rhs); }
- // ///ditto
- // VariantN opDiv_r(T)(T lhs)
- // {
- // return VariantN(lhs).opArithmetic!(VariantN, "/")(this);
- // }
- ///ditto
- VariantN opMod(T)(T rhs) { return opArithmetic!(T, "%")(rhs); }
- // ///ditto
- // VariantN opMod_r(T)(T lhs)
- // {
- // return VariantN(lhs).opArithmetic!(VariantN, "%")(this);
- // }
- ///ditto
- VariantN opAnd(T)(T rhs) { return opLogic!(T, "&")(rhs); }
+ VariantN opBinary(string op, T)(T rhs)
+ if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "^^" || op == "%") &&
+ is(typeof(opArithmetic!(T, op)(rhs))))
+ { return opArithmetic!(T, op)(rhs); }
///ditto
- VariantN opOr(T)(T rhs) { return opLogic!(T, "|")(rhs); }
+ VariantN opBinary(string op, T)(T rhs)
+ if ((op == "&" || op == "|" || op == "^" || op == ">>" || op == "<<" || op == ">>>") &&
+ is(typeof(opLogic!(T, op)(rhs))))
+ { return opLogic!(T, op)(rhs); }
///ditto
- VariantN opXor(T)(T rhs) { return opLogic!(T, "^")(rhs); }
+ VariantN opBinaryRight(string op, T)(T lhs)
+ if ((op == "+" || op == "*") &&
+ is(typeof(opArithmetic!(T, op)(lhs))))
+ { return opArithmetic!(T, op)(lhs); }
///ditto
- VariantN opShl(T)(T rhs) { return opLogic!(T, "<<")(rhs); }
- // ///ditto
- // VariantN opShl_r(T)(T lhs)
- // {
- // return VariantN(lhs).opLogic!(VariantN, "<<")(this);
- // }
- ///ditto
- VariantN opShr(T)(T rhs) { return opLogic!(T, ">>")(rhs); }
- // ///ditto
- // VariantN opShr_r(T)(T lhs)
- // {
- // return VariantN(lhs).opLogic!(VariantN, ">>")(this);
- // }
- ///ditto
- VariantN opUShr(T)(T rhs) { return opLogic!(T, ">>>")(rhs); }
- // ///ditto
- // VariantN opUShr_r(T)(T lhs)
- // {
- // return VariantN(lhs).opLogic!(VariantN, ">>>")(this);
- // }
+ VariantN opBinaryRight(string op, T)(T lhs)
+ if ((op == "&" || op == "|" || op == "^") &&
+ is(typeof(opLogic!(T, op)(lhs))))
+ { return opLogic!(T, op)(lhs); }
///ditto
- VariantN opCat(T)(T rhs)
+ VariantN opBinary(string op, T)(T rhs)
+ if (op == "~")
{
auto temp = this;
temp ~= rhs;
return temp;
}
// ///ditto
- // VariantN opCat_r(T)(T rhs)
+ // VariantN opBinaryRight(string op, T)(T rhs)
+ // if (op == "~")
// {
// VariantN temp = rhs;
// temp ~= this;
@@ -1065,33 +1100,18 @@ public:
// }
///ditto
- VariantN opAddAssign(T)(T rhs) { return this = this + rhs; }
- ///ditto
- VariantN opSubAssign(T)(T rhs) { return this = this - rhs; }
- ///ditto
- VariantN opMulAssign(T)(T rhs) { return this = this * rhs; }
- ///ditto
- VariantN opDivAssign(T)(T rhs) { return this = this / rhs; }
- ///ditto
- VariantN opModAssign(T)(T rhs) { return this = this % rhs; }
- ///ditto
- VariantN opAndAssign(T)(T rhs) { return this = this & rhs; }
- ///ditto
- VariantN opOrAssign(T)(T rhs) { return this = this | rhs; }
- ///ditto
- VariantN opXorAssign(T)(T rhs) { return this = this ^ rhs; }
- ///ditto
- VariantN opShlAssign(T)(T rhs) { return this = this << rhs; }
- ///ditto
- VariantN opShrAssign(T)(T rhs) { return this = this >> rhs; }
- ///ditto
- VariantN opUShrAssign(T)(T rhs) { return this = this >>> rhs; }
- ///ditto
- VariantN opCatAssign(T)(T rhs)
+ VariantN opOpAssign(string op, T)(T rhs)
{
- auto toAppend = Variant(rhs);
- fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false);
- return this;
+ static if (op != "~")
+ {
+ mixin("return this = this" ~ op ~ "rhs;");
+ }
+ else
+ {
+ auto toAppend = Variant(rhs);
+ fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false);
+ return this;
+ }
}
/**
@@ -1107,7 +1127,7 @@ public:
}
///
- static if (doUnittest)
+ version (StdDdoc)
@system unittest
{
Variant a = new int[10];
@@ -1144,7 +1164,7 @@ public:
return opIndexAssign(mixin(`opIndex(i)` ~ op ~ `value`), i);
}
- /** If the $(D VariantN) contains an (associative) array,
+ /** If the `VariantN` contains an (associative) array,
* returns the _length of that array. Otherwise, throws an
* exception.
*/
@@ -1154,7 +1174,7 @@ public:
}
/**
- If the $(D VariantN) contains an array, applies $(D dg) to each
+ If the `VariantN` contains an array, applies `dg` to each
element of the array in turn. Otherwise, throws an exception.
*/
int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate))
@@ -1192,6 +1212,63 @@ public:
}
}
+///
+@system unittest
+{
+ alias Var = VariantN!(maxSize!(int, double, string));
+
+ Var a; // Must assign before use, otherwise exception ensues
+ // Initialize with an integer; make the type int
+ Var b = 42;
+ assert(b.type == typeid(int));
+ // Peek at the value
+ assert(b.peek!(int) !is null && *b.peek!(int) == 42);
+ // Automatically convert per language rules
+ auto x = b.get!(real);
+
+ // Assign any other type, including other variants
+ a = b;
+ a = 3.14;
+ assert(a.type == typeid(double));
+ // Implicit conversions work just as with built-in types
+ assert(a < b);
+ // Check for convertibility
+ assert(!a.convertsTo!(int)); // double not convertible to int
+ // Strings and all other arrays are supported
+ a = "now I'm a string";
+ assert(a == "now I'm a string");
+}
+
+/// can also assign arrays
+@system unittest
+{
+ alias Var = VariantN!(maxSize!(int[]));
+
+ Var a = new int[42];
+ assert(a.length == 42);
+ a[5] = 7;
+ assert(a[5] == 7);
+}
+
+@safe unittest
+{
+ alias V = VariantN!24;
+ const alignMask = V.alignof - 1;
+ assert(V.sizeof == ((24 + (void*).sizeof + alignMask) & ~alignMask));
+}
+
+/// Can also assign class values
+@system unittest
+{
+ alias Var = VariantN!(maxSize!(int*)); // classes are pointers
+ Var a;
+
+ class Foo {}
+ auto foo = new Foo;
+ a = foo;
+ assert(*a.peek!(Foo) == foo); // and full type information is preserved
+}
+
@system unittest
{
import std.conv : to;
@@ -1214,7 +1291,7 @@ public:
assert(v[42] == 5);
}
-// opIndex with static arrays, issue 12771
+// opIndex with static arrays, https://issues.dlang.org/show_bug.cgi?id=12771
@system unittest
{
int[4] elements = [0, 1, 2, 3];
@@ -1245,7 +1322,33 @@ public:
assertThrown!VariantException(v[1] = Variant(null));
}
-//Issue# 8195
+// https://issues.dlang.org/show_bug.cgi?id=10879
+@system unittest
+{
+ int[10] arr = [1,2,3,4,5,6,7,8,9,10];
+ Variant v1 = arr;
+ Variant v2;
+ v2 = arr;
+ assert(v1 == arr);
+ assert(v2 == arr);
+ foreach (i, e; arr)
+ {
+ assert(v1[i] == e);
+ assert(v2[i] == e);
+ }
+ static struct LargeStruct
+ {
+ int[100] data;
+ }
+ LargeStruct ls;
+ ls.data[] = 4;
+ v1 = ls;
+ Variant v3 = ls;
+ assert(v1 == ls);
+ assert(v3 == ls);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=8195
@system unittest
{
struct S
@@ -1265,7 +1368,7 @@ public:
assert(v == S.init);
}
-// Issue #10961
+// https://issues.dlang.org/show_bug.cgi?id=10961
@system unittest
{
// Primarily test that we can assign a void[] to a Variant.
@@ -1275,7 +1378,7 @@ public:
assert(returned == elements);
}
-// Issue #13352
+// https://issues.dlang.org/show_bug.cgi?id=13352
@system unittest
{
alias TP = Algebraic!(long);
@@ -1291,7 +1394,7 @@ public:
assert(a + c == 4L);
}
-// Issue #13354
+// https://issues.dlang.org/show_bug.cgi?id=13354
@system unittest
{
alias A = Algebraic!(string[]);
@@ -1309,14 +1412,14 @@ public:
assert(aa["b"] == 3);
}
-// Issue #14198
+// https://issues.dlang.org/show_bug.cgi?id=14198
@system unittest
{
Variant a = true;
assert(a.type == typeid(bool));
}
-// Issue #14233
+// https://issues.dlang.org/show_bug.cgi?id=14233
@system unittest
{
alias Atom = Algebraic!(string, This[]);
@@ -1333,7 +1436,7 @@ pure nothrow @nogc
a = 1.0;
}
-// Issue 14457
+// https://issues.dlang.org/show_bug.cgi?id=14457
@system unittest
{
alias A = Algebraic!(int, float, double);
@@ -1347,7 +1450,7 @@ pure nothrow @nogc
assert(a.get!float == 6f);
}
-// Issue 14585
+// https://issues.dlang.org/show_bug.cgi?id=14585
@system unittest
{
static struct S
@@ -1358,7 +1461,7 @@ pure nothrow @nogc
Variant(S()).get!S;
}
-// Issue 14586
+// https://issues.dlang.org/show_bug.cgi?id=14586
@system unittest
{
const Variant v = new immutable Object;
@@ -1375,6 +1478,135 @@ pure nothrow @nogc
v.get!S;
}
+// https://issues.dlang.org/show_bug.cgi?id=13262
+@system unittest
+{
+ static void fun(T)(Variant v){
+ T x;
+ v = x;
+ auto r = v.get!(T);
+ }
+ Variant v;
+ fun!(shared(int))(v);
+ fun!(shared(int)[])(v);
+
+ static struct S1
+ {
+ int c;
+ string a;
+ }
+
+ static struct S2
+ {
+ string a;
+ shared int[] b;
+ }
+
+ static struct S3
+ {
+ string a;
+ shared int[] b;
+ int c;
+ }
+
+ fun!(S1)(v);
+ fun!(shared(S1))(v);
+ fun!(S2)(v);
+ fun!(shared(S2))(v);
+ fun!(S3)(v);
+ fun!(shared(S3))(v);
+
+ // ensure structs that are shared, but don't have shared postblits
+ // can't be used.
+ static struct S4
+ {
+ int x;
+ this(this) {x = 0;}
+ }
+
+ fun!(S4)(v);
+ static assert(!is(typeof(fun!(shared(S4))(v))));
+}
+
+@safe unittest
+{
+ Algebraic!(int) x;
+
+ static struct SafeS
+ {
+ @safe ~this() {}
+ }
+
+ Algebraic!(SafeS) y;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19986
+@system unittest
+{
+ VariantN!32 v;
+ v = const(ubyte[33]).init;
+
+ struct S
+ {
+ ubyte[33] s;
+ }
+
+ VariantN!32 v2;
+ v2 = const(S).init;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21021
+@system unittest
+{
+ static struct S
+ {
+ int h;
+ int[5] array;
+ alias h this;
+ }
+
+ S msg;
+ msg.array[] = 3;
+ Variant a = msg;
+ auto other = a.get!S;
+ assert(msg.array[0] == 3);
+ assert(other.array[0] == 3);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21231
+// Compatibility with -preview=fieldwise
+@system unittest
+{
+ static struct Empty
+ {
+ bool opCmp(const scope ref Empty) const
+ { return false; }
+ }
+
+ Empty a, b;
+ assert(a == b);
+ assert(!(a < b));
+
+ VariantN!(4, Empty) v = a;
+ assert(v == b);
+ assert(!(v < b));
+}
+
+// Compatibility with -preview=fieldwise
+@system unittest
+{
+ static struct Empty
+ {
+ bool opEquals(const scope ref Empty) const
+ { return false; }
+ }
+
+ Empty a, b;
+ assert(a != b);
+
+ VariantN!(4, Empty) v = a;
+ assert(v != b);
+}
/**
_Algebraic data type restricted to a closed set of possible
@@ -1384,6 +1616,8 @@ useful when it is desirable to restrict what a discriminated type
could hold to the end of defining simpler and more efficient
manipulation.
+$(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new
+code. Instead, use $(REF SumType, std,sumtype).)
*/
template Algebraic(T...)
{
@@ -1435,20 +1669,70 @@ be arbitrarily complex.
assert(obj.get!2["customer"] == "John");
}
+private struct FakeComplexReal
+{
+ real re, im;
+}
+
/**
Alias for $(LREF VariantN) instantiated with the largest size of `creal`,
`char[]`, and `void delegate()`. This ensures that `Variant` is large enough
to hold all of D's predefined types unboxed, including all numeric types,
pointers, delegates, and class references. You may want to use
-$(D VariantN) directly with a different maximum size either for
+`VariantN` directly with a different maximum size either for
storing larger types unboxed, or for saving memory.
*/
-alias Variant = VariantN!(maxSize!(creal, char[], void delegate()));
+alias Variant = VariantN!(maxSize!(FakeComplexReal, char[], void delegate()));
+
+///
+@system unittest
+{
+ Variant a; // Must assign before use, otherwise exception ensues
+ // Initialize with an integer; make the type int
+ Variant b = 42;
+ assert(b.type == typeid(int));
+ // Peek at the value
+ assert(b.peek!(int) !is null && *b.peek!(int) == 42);
+ // Automatically convert per language rules
+ auto x = b.get!(real);
+
+ // Assign any other type, including other variants
+ a = b;
+ a = 3.14;
+ assert(a.type == typeid(double));
+ // Implicit conversions work just as with built-in types
+ assert(a < b);
+ // Check for convertibility
+ assert(!a.convertsTo!(int)); // double not convertible to int
+ // Strings and all other arrays are supported
+ a = "now I'm a string";
+ assert(a == "now I'm a string");
+}
+
+/// can also assign arrays
+@system unittest
+{
+ Variant a = new int[42];
+ assert(a.length == 42);
+ a[5] = 7;
+ assert(a[5] == 7);
+}
+
+/// Can also assign class values
+@system unittest
+{
+ Variant a;
+
+ class Foo {}
+ auto foo = new Foo;
+ a = foo;
+ assert(*a.peek!(Foo) == foo); // and full type information is preserved
+}
/**
- * Returns an array of variants constructed from $(D args).
+ * Returns an array of variants constructed from `args`.
*
- * This is by design. During construction the $(D Variant) needs
+ * This is by design. During construction the `Variant` needs
* static type information about the type being held, so as to store a
* pointer to function for fast retrieval.
*/
@@ -1475,9 +1759,9 @@ Variant[] variantArray(T...)(T args)
* Thrown in three cases:
*
* $(OL $(LI An uninitialized `Variant` is used in any way except
- * assignment and $(D hasValue);) $(LI A $(D get) or
- * $(D coerce) is attempted with an incompatible target type;)
- * $(LI A comparison between $(D Variant) objects of
+ * assignment and `hasValue`;) $(LI A `get` or
+ * `coerce` is attempted with an incompatible target type;)
+ * $(LI A comparison between `Variant` objects of
* incompatible types is attempted.))
*
*/
@@ -1503,6 +1787,24 @@ static class VariantException : Exception
}
}
+///
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ Variant v;
+
+ // uninitialized use
+ assertThrown!VariantException(v + 1);
+ assertThrown!VariantException(v.length);
+
+ // .get with an incompatible target type
+ assertThrown!VariantException(Variant("a").get!int);
+
+ // comparison between incompatible types
+ assertThrown!VariantException(Variant(3) < Variant("a"));
+}
+
@system unittest
{
alias W1 = This2Variant!(char, int, This[int]);
@@ -1660,7 +1962,7 @@ static class VariantException : Exception
assert( v.peek!(double) );
assert( v.convertsTo!(real) );
//@@@ BUG IN COMPILER: DOUBLE SHOULD NOT IMPLICITLY CONVERT TO FLOAT
- assert( !v.convertsTo!(float) );
+ assert( v.convertsTo!(float) );
assert( *v.peek!(double) == 3.1413 );
auto u = Variant(v);
@@ -1721,16 +2023,13 @@ static class VariantException : Exception
{
auto v1 = Variant(42);
auto v2 = Variant("foo");
- auto v3 = Variant(1+2.0i);
int[Variant] hash;
hash[v1] = 0;
hash[v2] = 1;
- hash[v3] = 2;
assert( hash[v1] == 0 );
assert( hash[v2] == 1 );
- assert( hash[v3] == 2 );
}
{
@@ -1759,9 +2058,9 @@ static class VariantException : Exception
static assert(!__traits(compiles, {v > null;}));
}
+// https://issues.dlang.org/show_bug.cgi?id=1558
@system unittest
{
- // bug 1558
Variant va=1;
Variant vb=-2;
assert((va+vb).get!(int) == -1);
@@ -1826,7 +2125,7 @@ static class VariantException : Exception
assert(v.convertsTo!(char[]));
}
-// http://d.puremagic.com/issues/show_bug.cgi?id=5424
+// https://issues.dlang.org/show_bug.cgi?id=5424
@system unittest
{
interface A {
@@ -1842,14 +2141,14 @@ static class VariantException : Exception
Variant b = Variant(a);
}
+// https://issues.dlang.org/show_bug.cgi?id=7070
@system unittest
{
- // bug 7070
Variant v;
v = null;
}
-// Class and interface opEquals, issue 12157
+// Class and interface opEquals, https://issues.dlang.org/show_bug.cgi?id=12157
@system unittest
{
class Foo { }
@@ -1867,7 +2166,7 @@ static class VariantException : Exception
assert(v2 == f2);
}
-// Const parameters with opCall, issue 11361.
+// Const parameters with opCall, https://issues.dlang.org/show_bug.cgi?id=11361
@system unittest
{
static string t1(string c) {
@@ -1898,7 +2197,7 @@ static class VariantException : Exception
assert(v3(4).type == typeid(char[]));
}
-// issue 12071
+// https://issues.dlang.org/show_bug.cgi?id=12071
@system unittest
{
static struct Structure { int data; }
@@ -1915,7 +2214,8 @@ static class VariantException : Exception
assert(called);
}
-// Ordering comparisons of incompatible types, e.g. issue 7990.
+// Ordering comparisons of incompatible types
+// e.g. https://issues.dlang.org/show_bug.cgi?id=7990
@system unittest
{
import std.exception : assertThrown;
@@ -1927,7 +2227,8 @@ static class VariantException : Exception
assertThrown!VariantException(Variant(3) < Variant.init);
}
-// Handling of unordered types, e.g. issue 9043.
+// Handling of unordered types
+// https://issues.dlang.org/show_bug.cgi?id=9043
@system unittest
{
import std.exception : assertThrown;
@@ -1940,7 +2241,8 @@ static class VariantException : Exception
assertThrown!VariantException(Variant(A(3)) < Variant(A(4)));
}
-// Handling of empty types and arrays, e.g. issue 10958
+// Handling of empty types and arrays
+// https://issues.dlang.org/show_bug.cgi?id=10958
@system unittest
{
class EmptyClass { }
@@ -1971,7 +2273,8 @@ static class VariantException : Exception
assert(a.get!EmptyArray == arr);
}
-// Handling of void function pointers / delegates, e.g. issue 11360
+// Handling of void function pointers / delegates
+// https://issues.dlang.org/show_bug.cgi?id=11360
@system unittest
{
static void t1() { }
@@ -1983,7 +2286,8 @@ static class VariantException : Exception
assert(v2() == 3);
}
-// Using peek for large structs, issue 8580
+// Using peek for large structs
+// https://issues.dlang.org/show_bug.cgi?id=8580
@system unittest
{
struct TestStruct(bool pad)
@@ -2014,16 +2318,25 @@ static class VariantException : Exception
testPeekWith!(TestStruct!true)();
}
+// https://issues.dlang.org/show_bug.cgi?id=18780
+@system unittest
+{
+ int x = 7;
+ Variant a = x;
+ assert(a.convertsTo!ulong);
+ assert(a.convertsTo!uint);
+}
+
/**
* Applies a delegate or function to the given $(LREF Algebraic) depending on the held type,
* ensuring that all types are handled by the visiting functions.
*
* The delegate or function having the currently held value as parameter is called
- * with $(D variant)'s current value. Visiting handlers are passed
+ * with `variant`'s current value. Visiting handlers are passed
* in the template parameter list.
* It is statically ensured that all held types of
- * $(D variant) are handled across all handlers.
- * $(D visit) allows delegates and static functions to be passed
+ * `variant` are handled across all handlers.
+ * `visit` allows delegates and static functions to be passed
* as parameters.
*
* If a function with an untyped parameter is specified, this function is called
@@ -2164,11 +2477,11 @@ if (Handlers.length > 0)
Algebraic!(int, string) maybenumber = 2;
// ok, x ~ "a" valid for string, x + 1 valid for int, only 1 generic
- static assert( __traits(compiles, number.visit!((string x) => x ~ "a", x => x + 1)));
+ static assert( __traits(compiles, maybenumber.visit!((string x) => x ~ "a", x => "foobar"[0 .. x + 1])));
// bad, x ~ "a" valid for string but not int
- static assert(!__traits(compiles, number.visit!(x => x ~ "a")));
+ static assert(!__traits(compiles, maybenumber.visit!(x => x ~ "a")));
// bad, two generics, each only applies in one case
- static assert(!__traits(compiles, number.visit!(x => x + 1, x => x ~ "a")));
+ static assert(!__traits(compiles, maybenumber.visit!(x => x + 1, x => x ~ "a")));
}
/**
@@ -2176,7 +2489,7 @@ if (Handlers.length > 0)
* by the visiting functions.
*
* If a parameter-less function is specified it is called when
- * either $(D variant) doesn't hold a value or holds a type
+ * either `variant` doesn't hold a value or holds a type
* which isn't handled by the visiting functions.
*
* Returns: The return type of tryVisit is deduced from the visiting functions and must be
@@ -2260,11 +2573,11 @@ if (isAlgebraic!VariantType && Handler.length > 0)
/**
- * Returns: Struct where $(D indices) is an array which
+ * Returns: Struct where `indices` is an array which
* contains at the n-th position the index in Handler which takes the
* n-th type of AllowedTypes. If an Handler doesn't match an
* AllowedType, -1 is set. If a function in the delegates doesn't
- * have parameters, the field $(D exceptionFuncIdx) is set;
+ * have parameters, the field `exceptionFuncIdx` is set;
* otherwise it's -1.
*/
auto visitGetOverloadMap()
@@ -2277,6 +2590,24 @@ if (isAlgebraic!VariantType && Handler.length > 0)
Result result;
+ enum int nonmatch = ()
+ {
+ foreach (int dgidx, dg; Handler)
+ {
+ bool found = false;
+ foreach (T; AllowedTypes)
+ {
+ found |= __traits(compiles, { static assert(isSomeFunction!(dg!T)); });
+ found |= __traits(compiles, (T t) { dg(t); });
+ found |= __traits(compiles, dg());
+ }
+ if (!found) return dgidx;
+ }
+ return -1;
+ }();
+ static assert(nonmatch == -1, "No match for visit handler #"~
+ nonmatch.stringof~" ("~Handler[nonmatch].stringof~")");
+
foreach (tidx, T; AllowedTypes)
{
bool added = false;
@@ -2308,7 +2639,7 @@ if (isAlgebraic!VariantType && Handler.length > 0)
result.indices[tidx] = dgidx;
}
}
- else static if (isSomeFunction!(dg!T))
+ else static if (__traits(compiles, { static assert(isSomeFunction!(dg!T)); }))
{
assert(result.generalFuncIdx == -1 ||
result.generalFuncIdx == dgidx,
@@ -2316,10 +2647,6 @@ if (isAlgebraic!VariantType && Handler.length > 0)
result.generalFuncIdx = dgidx;
}
// Handle composite visitors with opCall overloads
- else
- {
- static assert(false, dg.stringof ~ " is not a function or delegate");
- }
}
if (!added)
@@ -2373,6 +2700,19 @@ if (isAlgebraic!VariantType && Handler.length > 0)
assert(false);
}
+// https://issues.dlang.org/show_bug.cgi?id=21253
+@system unittest
+{
+ static struct A { int n; }
+ static struct B { }
+
+ auto a = Algebraic!(A, B)(B());
+ assert(a.visit!(
+ (B _) => 42,
+ (a ) => a.n
+ ) == 42);
+}
+
@system unittest
{
// validate that visit can be called with a const type
@@ -2389,9 +2729,9 @@ if (isAlgebraic!VariantType && Handler.length > 0)
assert(depth(fb) == 3);
}
+// https://issues.dlang.org/show_bug.cgi?id=16383
@system unittest
{
- // https://issues.dlang.org/show_bug.cgi?id=16383
class Foo {this() immutable {}}
alias V = Algebraic!(immutable Foo);
@@ -2401,9 +2741,9 @@ if (isAlgebraic!VariantType && Handler.length > 0)
assert(x == 3);
}
+// https://issues.dlang.org/show_bug.cgi?id=5310
@system unittest
{
- // http://d.puremagic.com/issues/show_bug.cgi?id=5310
const Variant a;
assert(a == a);
Variant b;
@@ -2417,9 +2757,9 @@ if (isAlgebraic!VariantType && Handler.length > 0)
assert(a[0] == 2);
}
+// https://issues.dlang.org/show_bug.cgi?id=10017
@system unittest
{
- // http://d.puremagic.com/issues/show_bug.cgi?id=10017
static struct S
{
ubyte[Variant.size + 1] s;
@@ -2430,32 +2770,33 @@ if (isAlgebraic!VariantType && Handler.length > 0)
v2 = v1; // AssertError: target must be non-null
assert(v1 == v2);
}
+
+// https://issues.dlang.org/show_bug.cgi?id=7069
@system unittest
{
import std.exception : assertThrown;
- // http://d.puremagic.com/issues/show_bug.cgi?id=7069
Variant v;
int i = 10;
v = i;
- foreach (qual; AliasSeq!(MutableOf, ConstOf))
+ static foreach (qual; AliasSeq!(Alias, ConstOf))
{
assert(v.get!(qual!int) == 10);
assert(v.get!(qual!float) == 10.0f);
}
- foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
+ static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
{
assertThrown!VariantException(v.get!(qual!int));
}
const(int) ci = 20;
v = ci;
- foreach (qual; AliasSeq!(ConstOf))
+ static foreach (qual; AliasSeq!(ConstOf))
{
assert(v.get!(qual!int) == 20);
assert(v.get!(qual!float) == 20.0f);
}
- foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf))
+ static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf))
{
assertThrown!VariantException(v.get!(qual!int));
assertThrown!VariantException(v.get!(qual!float));
@@ -2463,12 +2804,12 @@ if (isAlgebraic!VariantType && Handler.length > 0)
immutable(int) ii = ci;
v = ii;
- foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf))
+ static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf))
{
assert(v.get!(qual!int) == 20);
assert(v.get!(qual!float) == 20.0f);
}
- foreach (qual; AliasSeq!(MutableOf, SharedOf))
+ static foreach (qual; AliasSeq!(Alias, SharedOf))
{
assertThrown!VariantException(v.get!(qual!int));
assertThrown!VariantException(v.get!(qual!float));
@@ -2476,12 +2817,12 @@ if (isAlgebraic!VariantType && Handler.length > 0)
int[] ai = [1,2,3];
v = ai;
- foreach (qual; AliasSeq!(MutableOf, ConstOf))
+ static foreach (qual; AliasSeq!(Alias, ConstOf))
{
assert(v.get!(qual!(int[])) == [1,2,3]);
assert(v.get!(qual!(int)[]) == [1,2,3]);
}
- foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
+ static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
{
assertThrown!VariantException(v.get!(qual!(int[])));
assertThrown!VariantException(v.get!(qual!(int)[]));
@@ -2489,12 +2830,12 @@ if (isAlgebraic!VariantType && Handler.length > 0)
const(int[]) cai = [4,5,6];
v = cai;
- foreach (qual; AliasSeq!(ConstOf))
+ static foreach (qual; AliasSeq!(ConstOf))
{
assert(v.get!(qual!(int[])) == [4,5,6]);
assert(v.get!(qual!(int)[]) == [4,5,6]);
}
- foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf))
+ static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf))
{
assertThrown!VariantException(v.get!(qual!(int[])));
assertThrown!VariantException(v.get!(qual!(int)[]));
@@ -2508,7 +2849,7 @@ if (isAlgebraic!VariantType && Handler.length > 0)
assert(v.get!(const(int)[]) == [7,8,9]);
//assert(v.get!(shared(const(int[]))) == cast(shared const)[7,8,9]); // Bug ??? runtime error
//assert(v.get!(shared(const(int))[]) == cast(shared const)[7,8,9]); // Bug ??? runtime error
- foreach (qual; AliasSeq!(MutableOf))
+ static foreach (qual; AliasSeq!(Alias))
{
assertThrown!VariantException(v.get!(qual!(int[])));
assertThrown!VariantException(v.get!(qual!(int)[]));
@@ -2518,13 +2859,13 @@ if (isAlgebraic!VariantType && Handler.length > 0)
class B : A {}
B b = new B();
v = b;
- foreach (qual; AliasSeq!(MutableOf, ConstOf))
+ static foreach (qual; AliasSeq!(Alias, ConstOf))
{
assert(v.get!(qual!B) is b);
assert(v.get!(qual!A) is b);
assert(v.get!(qual!Object) is b);
}
- foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
+ static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
{
assertThrown!VariantException(v.get!(qual!B));
assertThrown!VariantException(v.get!(qual!A));
@@ -2533,13 +2874,13 @@ if (isAlgebraic!VariantType && Handler.length > 0)
const(B) cb = new B();
v = cb;
- foreach (qual; AliasSeq!(ConstOf))
+ static foreach (qual; AliasSeq!(ConstOf))
{
assert(v.get!(qual!B) is cb);
assert(v.get!(qual!A) is cb);
assert(v.get!(qual!Object) is cb);
}
- foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf))
+ static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf))
{
assertThrown!VariantException(v.get!(qual!B));
assertThrown!VariantException(v.get!(qual!A));
@@ -2548,13 +2889,13 @@ if (isAlgebraic!VariantType && Handler.length > 0)
immutable(B) ib = new immutable(B)();
v = ib;
- foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf))
+ static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf))
{
assert(v.get!(qual!B) is ib);
assert(v.get!(qual!A) is ib);
assert(v.get!(qual!Object) is ib);
}
- foreach (qual; AliasSeq!(MutableOf, SharedOf))
+ static foreach (qual; AliasSeq!(Alias, SharedOf))
{
assertThrown!VariantException(v.get!(qual!B));
assertThrown!VariantException(v.get!(qual!A));
@@ -2563,13 +2904,13 @@ if (isAlgebraic!VariantType && Handler.length > 0)
shared(B) sb = new shared B();
v = sb;
- foreach (qual; AliasSeq!(SharedOf, SharedConstOf))
+ static foreach (qual; AliasSeq!(SharedOf, SharedConstOf))
{
assert(v.get!(qual!B) is sb);
assert(v.get!(qual!A) is sb);
assert(v.get!(qual!Object) is sb);
}
- foreach (qual; AliasSeq!(MutableOf, ImmutableOf, ConstOf))
+ static foreach (qual; AliasSeq!(Alias, ImmutableOf, ConstOf))
{
assertThrown!VariantException(v.get!(qual!B));
assertThrown!VariantException(v.get!(qual!A));
@@ -2578,13 +2919,13 @@ if (isAlgebraic!VariantType && Handler.length > 0)
shared(const(B)) scb = new shared const B();
v = scb;
- foreach (qual; AliasSeq!(SharedConstOf))
+ static foreach (qual; AliasSeq!(SharedConstOf))
{
assert(v.get!(qual!B) is scb);
assert(v.get!(qual!A) is scb);
assert(v.get!(qual!Object) is scb);
}
- foreach (qual; AliasSeq!(MutableOf, ConstOf, ImmutableOf, SharedOf))
+ static foreach (qual; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf))
{
assertThrown!VariantException(v.get!(qual!B));
assertThrown!VariantException(v.get!(qual!A));
@@ -2592,11 +2933,11 @@ if (isAlgebraic!VariantType && Handler.length > 0)
}
}
+// https://issues.dlang.org/show_bug.cgi?id=12540
@system unittest
{
static struct DummyScope
{
- // https://d.puremagic.com/issues/show_bug.cgi?id=12540
alias Alias12540 = Algebraic!Class12540;
static class Class12540
@@ -2654,7 +2995,7 @@ if (isAlgebraic!VariantType && Handler.length > 0)
@system unittest
{
- // Bugzilla 13300
+ // https://issues.dlang.org/show_bug.cgi?id=13300
static struct S
{
this(this) {}
@@ -2682,9 +3023,9 @@ if (isAlgebraic!VariantType && Handler.length > 0)
auto a = appender!(T[]);
}
+// https://issues.dlang.org/show_bug.cgi?id=13871
@system unittest
{
- // Bugzilla 13871
alias A = Algebraic!(int, typeof(null));
static struct B { A value; }
alias C = std.variant.Algebraic!B;
@@ -2721,9 +3062,9 @@ if (isAlgebraic!VariantType && Handler.length > 0)
assertThrown!VariantException(v.length);
}
+// https://issues.dlang.org/show_bug.cgi?id=13534
@system unittest
{
- // Bugzilla 13534
static assert(!__traits(compiles, () @safe {
auto foo() @system { return 3; }
auto v = Variant(&foo);
@@ -2731,9 +3072,9 @@ if (isAlgebraic!VariantType && Handler.length > 0)
}));
}
+// https://issues.dlang.org/show_bug.cgi?id=15039
@system unittest
{
- // Bugzilla 15039
import std.typecons;
import std.variant;
@@ -2749,9 +3090,9 @@ if (isAlgebraic!VariantType && Handler.length > 0)
);
}
+// https://issues.dlang.org/show_bug.cgi?id=15791
@system unittest
{
- // Bugzilla 15791
int n = 3;
struct NS1 { int foo() { return n + 10; } }
struct NS2 { int foo() { return n * 10; } }
@@ -2763,9 +3104,103 @@ if (isAlgebraic!VariantType && Handler.length > 0)
assert(v.get!NS2.foo() == 30);
}
+// https://issues.dlang.org/show_bug.cgi?id=15827
@system unittest
{
- // Bugzilla 15827
static struct Foo15827 { Variant v; this(Foo15827 v) {} }
Variant v = Foo15827.init;
}
+
+// https://issues.dlang.org/show_bug.cgi?id=18934
+@system unittest
+{
+ static struct S
+ {
+ const int x;
+ }
+
+ auto s = S(42);
+ Variant v = s;
+ auto s2 = v.get!S;
+ assert(s2.x == 42);
+ Variant v2 = v; // support copying from one variant to the other
+ v2 = S(2);
+ v = v2;
+ assert(v.get!S.x == 2);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19200
+@system unittest
+{
+ static struct S
+ {
+ static int opBinaryRight(string op : "|", T)(T rhs)
+ {
+ return 3;
+ }
+ }
+
+ S s;
+ Variant v;
+ auto b = v | s;
+ assert(b == 3);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=11061
+@system unittest
+{
+ int[4] el = [0, 1, 2, 3];
+ int[3] nl = [0, 1, 2];
+ Variant v1 = el;
+ assert(v1 == el); // Compare Var(static) to static
+ assert(v1 != nl); // Compare static arrays of different length
+ assert(v1 == [0, 1, 2, 3]); // Compare Var(static) to dynamic.
+ assert(v1 != [0, 1, 2]);
+ int[] dyn = [0, 1, 2, 3];
+ v1 = dyn;
+ assert(v1 == el); // Compare Var(dynamic) to static.
+ assert(v1 == [0, 1] ~ [2, 3]); // Compare Var(dynamic) to dynamic
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15940
+@system unittest
+{
+ class C { }
+ struct S
+ {
+ C a;
+ alias a this;
+ }
+ S s = S(new C());
+ auto v = Variant(s); // compile error
+}
+
+@system unittest
+{
+ // Test if we don't have scoping issues.
+ Variant createVariant(int[] input)
+ {
+ int[2] el = [input[0], input[1]];
+ Variant v = el;
+ return v;
+ }
+ Variant v = createVariant([0, 1]);
+ createVariant([2, 3]);
+ assert(v == [0,1]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19994
+@safe unittest
+{
+ alias Inner = Algebraic!(This*);
+ alias Outer = Algebraic!(Inner, This*);
+
+ static assert(is(Outer.AllowedTypes == AliasSeq!(Inner, Outer*)));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=21296
+@system unittest
+{
+ immutable aa = ["0": 0];
+ auto v = Variant(aa); // compile error
+}
diff --git a/libphobos/src/std/windows/charset.d b/libphobos/src/std/windows/charset.d
index ee7211d446b..69626b553fa 100644
--- a/libphobos/src/std/windows/charset.d
+++ b/libphobos/src/std/windows/charset.d
@@ -3,11 +3,11 @@
/**
* Support UTF-8 on Windows 95, 98 and ME systems.
*
- * Copyright: Copyright Digital Mars 2005 - 2009.
+ * Copyright: Copyright The D Language Foundation" 2005 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright)
*/
-/* Copyright Digital Mars 2005 - 2009.
+/* Copyright The D Language Foundation" 2005 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -30,7 +30,7 @@ version (StdDdoc)
* Authors:
* yaneurao, Walter Bright, Stewart Gordon
*/
- const(char)* toMBSz(in char[] s, uint codePage = 0);
+ const(char)* toMBSz(scope const(char)[] s, uint codePage = 0);
/**********************************************
* Converts the null-terminated string s from a Windows 8-bit character set
@@ -50,14 +50,14 @@ else:
version (Windows):
-import core.sys.windows.windows;
+import core.sys.windows.winbase, core.sys.windows.winnls;
import std.conv;
import std.string;
import std.windows.syserror;
import std.internal.cstring;
-const(char)* toMBSz(in char[] s, uint codePage = 0)
+const(char)* toMBSz(scope const(char)[] s, uint codePage = 0)
{
// Only need to do this if any chars have the high bit set
foreach (char c; s)
@@ -88,7 +88,7 @@ const(char)* toMBSz(in char[] s, uint codePage = 0)
return std.string.toStringz(s);
}
-string fromMBSz(immutable(char)* s, int codePage = 0)
+string fromMBSz(return scope immutable(char)* s, int codePage = 0)
{
const(char)* c;
diff --git a/libphobos/src/std/windows/registry.d b/libphobos/src/std/windows/registry.d
index 7293d2dad51..cdf37c11cf6 100644
--- a/libphobos/src/std/windows/registry.d
+++ b/libphobos/src/std/windows/registry.d
@@ -12,7 +12,7 @@
Created 15th March 2003,
Updated 25th April 2004,
- Source: $(PHOBOSSRC std/windows/_registry.d)
+ Source: $(PHOBOSSRC std/windows/registry.d)
*/
/* /////////////////////////////////////////////////////////////////////////////
*
@@ -38,7 +38,7 @@
module std.windows.registry;
version (Windows):
-import core.sys.windows.windows;
+import core.sys.windows.winbase, core.sys.windows.windef, core.sys.windows.winreg;
import std.array;
import std.conv;
import std.exception;
@@ -81,7 +81,7 @@ class Win32Exception : WindowsException
@property int error() { return super.code; }
}
-version (unittest) import std.string : startsWith, endsWith;
+version (StdUnittest) import std.string : startsWith, endsWith;
@safe unittest
{
@@ -274,7 +274,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
/* No need to attempt to close any of the standard hive keys.
* Although it's documented that calling RegCloseKey() on any of
@@ -309,7 +309,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
immutable res = RegFlushKey(hkey);
enforceSucc(res, "Key cannot be flushed");
@@ -322,7 +322,7 @@ in
assert(hkey !is null);
assert(subKey !is null);
}
-body
+do
{
HKEY hkeyResult;
enforceSucc(RegCreateKeyExW(
@@ -340,7 +340,7 @@ in
assert(hkey !is null);
assert(subKey !is null);
}
-body
+do
{
LONG res;
if (haveWoW64Job(samDesired))
@@ -361,7 +361,7 @@ in
assert(hkey !is null);
assert(valueName !is null);
}
-body
+do
{
enforceSucc(RegDeleteValueW(hkey, valueName.tempCStringW()),
"Value cannot be deleted: \"" ~ valueName ~ "\"");
@@ -372,7 +372,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
/* Can't duplicate standard keys, but don't need to, so can just return */
if (cast(uint) hkey & 0x80000000)
@@ -422,7 +422,7 @@ out(res)
{
assert(res != ERROR_MORE_DATA);
}
-body
+do
{
// The Registry API lies about the lengths of a very few sub-key lengths
// so we have to test to see if it whinges about more data, and provide
@@ -447,7 +447,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
for (;;)
{
@@ -467,7 +467,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
return RegQueryInfoKeyW(hkey, null, null, null, &cSubKeys,
&cchSubKeyMaxLen, null, null, null, null, null, null);
@@ -478,7 +478,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
return RegQueryInfoKeyW(hkey, null, null, null, null, null, null,
&cValues, &cchValueMaxLen, null, null, null);
@@ -489,7 +489,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
REG_VALUE_TYPE type;
enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, null, null),
@@ -504,7 +504,7 @@ in
assert(hkey !is null);
assert(subKey !is null);
}
-body
+do
{
HKEY hkeyResult;
enforceSucc(RegOpenKeyExW(hkey, subKey.tempCStringW(), 0, compatibleRegsam(samDesired), &hkeyResult),
@@ -518,13 +518,13 @@ in
{
assert(hkey !is null);
}
-body
+do
{
import core.bitop : bswap;
REG_VALUE_TYPE type;
- // See bugzilla 961 on this
+ // See https://issues.dlang.org/show_bug.cgi?id=961 on this
union U
{
uint dw;
@@ -589,7 +589,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
REG_VALUE_TYPE type;
@@ -631,7 +631,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
import core.bitop : bswap;
@@ -669,7 +669,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
REG_VALUE_TYPE type;
@@ -694,7 +694,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
REG_VALUE_TYPE type;
@@ -729,7 +729,7 @@ in
{
assert(hkey !is null);
}
-body
+do
{
enforceSucc(RegSetValueExW(hkey, subKey.tempCStringW(), 0, type, cast(BYTE*) lpData, cbData),
"Value cannot be set: \"" ~ subKey ~ "\"");
@@ -801,7 +801,7 @@ private:
{
assert(hkey !is null);
}
- body
+ do
{
m_hkey = hkey;
m_name = name;
@@ -886,11 +886,11 @@ public:
Returns the named sub-key of this key.
Params:
- name = The name of the subkey to create. May not be $(D null).
+ name = The name of the subkey to create. May not be `null`.
Returns:
The created key.
Throws:
- $(D RegistryException) is thrown if the key cannot be created.
+ `RegistryException` is thrown if the key cannot be created.
*/
Key createKey(string name, REGSAM access = REGSAM.KEY_ALL_ACCESS)
{
@@ -926,12 +926,12 @@ public:
Params:
name = The name of the subkey to aquire. If name is the empty
string, then the called key is duplicated.
- access = The desired access; one of the $(D REGSAM) enumeration.
+ access = The desired access; one of the `REGSAM` enumeration.
Returns:
The aquired key.
Throws:
- This function never returns $(D null). If a key corresponding to
- the requested name is not found, $(D RegistryException) is thrown.
+ This function never returns `null`. If a key corresponding to
+ the requested name is not found, `RegistryException` is thrown.
*/
Key getKey(string name, REGSAM access = REGSAM.KEY_READ)
{
@@ -965,7 +965,7 @@ public:
Deletes the named key.
Params:
- name = The name of the key to delete. May not be $(D null).
+ name = The name of the key to delete. May not be `null`.
*/
void deleteKey(string name, REGSAM access = cast(REGSAM) 0)
{
@@ -976,11 +976,11 @@ public:
/**
Returns the named value.
- If $(D name) is the empty string, then the default value is returned.
+ If `name` is the empty string, then the default value is returned.
Returns:
- This function never returns $(D null). If a value corresponding
- to the requested name is not found, $(D RegistryException) is thrown.
+ This function never returns `null`. If a value corresponding
+ to the requested name is not found, `RegistryException` is thrown.
*/
Value getValue(string name)
{
@@ -996,7 +996,7 @@ public:
value = The 32-bit unsigned value to set.
Throws:
If a value corresponding to the requested name is not found,
- $(D RegistryException) is thrown.
+ `RegistryException` is thrown.
*/
void setValue(string name, uint value)
{
@@ -1011,10 +1011,10 @@ public:
name = The name of the value to set. If it is the empty string,
sets the default value.
value = The 32-bit unsigned value to set.
- endian = Can be $(D Endian.BigEndian) or $(D Endian.LittleEndian).
+ endian = Can be `Endian.BigEndian` or `Endian.LittleEndian`.
Throws:
If a value corresponding to the requested name is not found,
- $(D RegistryException) is thrown.
+ `RegistryException` is thrown.
*/
void setValue(string name, uint value, Endian endian)
{
@@ -1035,7 +1035,7 @@ public:
value = The 64-bit unsigned value to set.
Throws:
If a value corresponding to the requested name is not found,
- $(D RegistryException) is thrown.
+ `RegistryException` is thrown.
*/
void setValue(string name, ulong value)
{
@@ -1051,7 +1051,7 @@ public:
value = The string value to set.
Throws:
If a value corresponding to the requested name is not found,
- $(D RegistryException) is thrown.
+ `RegistryException` is thrown.
*/
void setValue(string name, string value)
{
@@ -1065,11 +1065,11 @@ public:
name = The name of the value to set. If it is the empty string,
sets the default value.
value = The string value to set.
- asEXPAND_SZ = If $(D true), the value will be stored as an
+ asEXPAND_SZ = If `true`, the value will be stored as an
expandable environment string, otherwise as a normal string.
Throws:
If a value corresponding to the requested name is not found,
- $(D RegistryException) is thrown.
+ `RegistryException` is thrown.
*/
void setValue(string name, string value, bool asEXPAND_SZ)
{
@@ -1092,7 +1092,7 @@ public:
value = The multiple-strings value to set.
Throws:
If a value corresponding to the requested name is not found,
- $(D RegistryException) is thrown.
+ `RegistryException` is thrown.
*/
void setValue(string name, string[] value)
{
@@ -1116,7 +1116,7 @@ public:
value = The binary value to set.
Throws:
If a value corresponding to the requested name is not found,
- $(D RegistryException) is thrown.
+ `RegistryException` is thrown.
*/
void setValue(string name, byte[] value)
{
@@ -1127,10 +1127,10 @@ public:
Deletes the named value.
Params:
- name = The name of the value to delete. May not be $(D null).
+ name = The name of the value to delete. May not be `null`.
Throws:
If a value of the requested name is not found,
- $(D RegistryException) is thrown.
+ `RegistryException` is thrown.
*/
void deleteValue(string name)
{
@@ -1168,7 +1168,7 @@ private:
{
assert(null !is key);
}
- body
+ do
{
m_key = key;
m_type = type;
@@ -1197,12 +1197,12 @@ public:
/**
Obtains the current value of the value as a string.
If the value's type is REG_EXPAND_SZ the returned value is <b>not</b>
- expanded; $(D value_EXPAND_SZ) should be called
+ expanded; `value_EXPAND_SZ` should be called
Returns:
The contents of the value.
Throws:
- $(D RegistryException) if the type of the value is not REG_SZ,
+ `RegistryException` if the type of the value is not REG_SZ,
REG_EXPAND_SZ, REG_DWORD, or REG_QWORD.
*/
@property string value_SZ() const
@@ -1217,7 +1217,7 @@ public:
/**
Obtains the current value as a string, within which any environment
variables have undergone expansion.
- This function works with the same value-types as $(D value_SZ).
+ This function works with the same value-types as `value_SZ`.
Returns:
The contents of the value.
@@ -1245,7 +1245,7 @@ public:
Returns:
The contents of the value.
Throws:
- $(D RegistryException) if the type of the value is not REG_MULTI_SZ.
+ `RegistryException` if the type of the value is not REG_MULTI_SZ.
*/
@property string[] value_MULTI_SZ() const
{
@@ -1263,7 +1263,7 @@ public:
Returns:
The contents of the value.
Throws:
- $(D RegistryException) is thrown for all types other than
+ `RegistryException` is thrown for all types other than
REG_DWORD, REG_DWORD_LITTLE_ENDIAN and REG_DWORD_BIG_ENDIAN.
*/
@property uint value_DWORD() const
@@ -1282,7 +1282,7 @@ public:
Returns:
The contents of the value.
Throws:
- $(D RegistryException) if the type of the value is not REG_QWORD.
+ `RegistryException` if the type of the value is not REG_QWORD.
*/
@property ulong value_QWORD() const
{
@@ -1299,7 +1299,7 @@ public:
Returns:
The contents of the value.
Throws:
- $(D RegistryException) if the type of the value is not REG_BINARY.
+ `RegistryException` if the type of the value is not REG_BINARY.
*/
@property byte[] value_BINARY() const
{
@@ -1322,7 +1322,7 @@ private:
final class Registry
{
private:
- @disable this() { }
+ @disable this();
public:
/// Returns the root key for the HKEY_CLASSES_ROOT hive
@@ -1405,7 +1405,7 @@ public:
Returns:
The name of the key corresponding to the given index.
Throws:
- $(D RegistryException) if no corresponding key is retrieved.
+ `RegistryException` if no corresponding key is retrieved.
*/
string opIndex(size_t index)
{
@@ -1482,7 +1482,7 @@ public:
Returns:
The key corresponding to the given index.
Throws:
- $(D RegistryException) if no corresponding key is retrieved.
+ `RegistryException` if no corresponding key is retrieved.
*/
Key getKey(size_t index)
{
@@ -1502,7 +1502,7 @@ public:
Returns:
The key corresponding to the given index.
Throws:
- $(D RegistryException) if no corresponding key is retrieved.
+ `RegistryException` if no corresponding key is retrieved.
*/
Key opIndex(size_t index)
{
@@ -1591,7 +1591,7 @@ public:
Returns:
The name of the value corresponding to the given index.
Throws:
- $(D RegistryException) if no corresponding value is retrieved.
+ `RegistryException` if no corresponding value is retrieved.
*/
string getValueName(size_t index)
{
@@ -1611,7 +1611,7 @@ public:
Returns:
The name of the value corresponding to the given index.
Throws:
- $(D RegistryException) if no corresponding value is retrieved.
+ `RegistryException` if no corresponding value is retrieved.
*/
string opIndex(size_t index)
{
@@ -1678,14 +1678,14 @@ public:
}
/**
- The value at the given $(D index).
+ The value at the given `index`.
Params:
index = The 0-based index of the value to retrieve
Returns:
The value corresponding to the given index.
Throws:
- $(D RegistryException) if no corresponding value is retrieved
+ `RegistryException` if no corresponding value is retrieved
*/
Value getValue(size_t index)
{
@@ -1698,14 +1698,14 @@ public:
}
/**
- The value at the given $(D index).
+ The value at the given `index`.
Params:
index = The 0-based index of the value to retrieve.
Returns:
The value corresponding to the given index.
Throws:
- $(D RegistryException) if no corresponding value is retrieved.
+ `RegistryException` if no corresponding value is retrieved.
*/
Value opIndex(size_t index)
{
diff --git a/libphobos/src/std/windows/syserror.d b/libphobos/src/std/windows/syserror.d
index 73863607dd1..94f8ee59d50 100644
--- a/libphobos/src/std/windows/syserror.d
+++ b/libphobos/src/std/windows/syserror.d
@@ -3,12 +3,12 @@
/**
* Convert Win32 error code to string.
*
- * Copyright: Copyright Digital Mars 2006 - 2013.
+ * Copyright: Copyright The D Language Foundation" 2006 - 2013.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright)
* Credits: Based on code written by Regan Heath
*/
-/* Copyright Digital Mars 2006 - 2013.
+/* Copyright The D Language Foundation" 2006 - 2013.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -26,7 +26,7 @@ version (StdDdoc)
/** Query the text for a Windows error code, as returned by
$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx,
- $(D GetLastError)), as a D string.
+ `GetLastError`), as a D string.
*/
string sysErrorString(
DWORD errCode,
@@ -37,20 +37,20 @@ version (StdDdoc)
/*********************
Thrown if errors that set
$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360.aspx,
- $(D GetLastError)) occur.
+ `GetLastError`) occur.
*/
class WindowsException : Exception
{
private alias DWORD = int;
- final @property DWORD code(); /// $(D GetLastError)'s return value.
+ final @property DWORD code(); /// `GetLastError`'s return value.
this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted;
}
/++
- If $(D !!value) is true, $(D value) is returned. Otherwise,
+ If `!!value` is true, `value` is returned. Otherwise,
$(D new WindowsException(GetLastError(), msg)) is thrown.
- $(D WindowsException) assumes that the last operation set
- $(D GetLastError()) appropriately.
+ `WindowsException` assumes that the last operation set
+ `GetLastError()` appropriately.
Example:
--------------------
@@ -65,10 +65,10 @@ else:
version (Windows):
-import core.sys.windows.windows;
+import core.sys.windows.winbase, core.sys.windows.winnt;
import std.array : appender;
import std.conv : to;
-import std.format : formattedWrite;
+import std.format.write : formattedWrite;
import std.windows.charset;
string sysErrorString(
@@ -117,9 +117,9 @@ bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0)
class WindowsException : Exception
{
- import core.sys.windows.windows : DWORD;
+ import core.sys.windows.windef : DWORD;
- final @property DWORD code() { return _code; } /// $(D GetLastError)'s return value.
+ final @property DWORD code() { return _code; } /// `GetLastError`'s return value.
private DWORD _code;
this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted
diff --git a/libphobos/src/std/xml.d b/libphobos/src/std/xml.d
index 13241f53613..37fab6db038 100644
--- a/libphobos/src/std/xml.d
+++ b/libphobos/src/std/xml.d
@@ -2,9 +2,11 @@
/**
$(RED Warning: This module is considered out-dated and not up to Phobos'
- current standards. It will remain until we have a suitable replacement,
- but be aware that it will not remain long term.)
+ current standards. It will be removed from Phobos in 2.101.0.
+ If you still need it, go to $(LINK https://github.com/DigitalMars/undeaD))
+ */
+/*
Classes and functions for creating and parsing XML
The basic architecture of this module is that there are standalone functions,
@@ -115,7 +117,7 @@ void main()
Copyright: Copyright Janice Caron 2008 - 2009.
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: Janice Caron
-Source: $(PHOBOSSRC std/_xml.d)
+Source: $(PHOBOSSRC std/xml.d)
*/
/*
Copyright Janice Caron 2008 - 2009.
@@ -123,11 +125,12 @@ Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
*/
+deprecated("Will be removed from Phobos in 2.101.0. If you still need it, go to https://github.com/DigitalMars/undeaD")
module std.xml;
enum cdata = "<![CDATA[";
-/**
+/*
* Returns true if the character is a character according to the XML standard
*
* Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
@@ -189,7 +192,7 @@ bool isChar(dchar c) @safe @nogc pure nothrow // rule 2
}
}
-/**
+/*
* Returns true if the character is whitespace according to the XML standard
*
* Only the following characters are considered whitespace in XML - space, tab,
@@ -205,7 +208,7 @@ bool isSpace(dchar c) @safe @nogc pure nothrow
return c == '\u0020' || c == '\u0009' || c == '\u000A' || c == '\u000D';
}
-/**
+/*
* Returns true if the character is a digit according to the XML standard
*
* Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
@@ -230,7 +233,7 @@ bool isDigit(dchar c) @safe @nogc pure nothrow
}
}
-/**
+/*
* Returns true if the character is a letter according to the XML standard
*
* Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
@@ -243,7 +246,7 @@ bool isLetter(dchar c) @safe @nogc nothrow pure // rule 84
return isIdeographic(c) || isBaseChar(c);
}
-/**
+/*
* Returns true if the character is an ideographic character according to the
* XML standard
*
@@ -278,7 +281,7 @@ bool isIdeographic(dchar c) @safe @nogc nothrow pure
}
}
-/**
+/*
* Returns true if the character is a base character according to the XML
* standard
*
@@ -292,7 +295,7 @@ bool isBaseChar(dchar c) @safe @nogc nothrow pure
return lookup(BaseCharTable,c);
}
-/**
+/*
* Returns true if the character is a combining character according to the
* XML standard
*
@@ -306,7 +309,7 @@ bool isCombiningChar(dchar c) @safe @nogc nothrow pure
return lookup(CombiningCharTable,c);
}
-/**
+/*
* Returns true if the character is an extender according to the XML standard
*
* Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
@@ -319,7 +322,7 @@ bool isExtender(dchar c) @safe @nogc nothrow pure
return lookup(ExtenderTable,c);
}
-/**
+/*
* Encodes a string by replacing all characters which need to be escaped with
* appropriate predefined XML entities.
*
@@ -384,7 +387,7 @@ S encode(S)(S s)
assert(encode("cat & dog") == "cat &amp; dog");
}
-/**
+/*
* Mode to use for decoding.
*
* $(DDOC_ENUM_MEMBERS NONE) Do not decode
@@ -396,7 +399,7 @@ enum DecodeMode
NONE, LOOSE, STRICT
}
-/**
+/*
* Decodes a string by unescaping all predefined XML entities.
*
* encode() escapes certain characters (ampersand, quote, apostrophe, less-than
@@ -430,7 +433,7 @@ enum DecodeMode
* writefln(decode("a &gt; b")); // writes "a > b"
* --------------
*/
-string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @safe pure
+string decode(return scope string s, DecodeMode mode=DecodeMode.LOOSE) @safe pure
{
import std.algorithm.searching : startsWith;
@@ -521,7 +524,7 @@ string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @safe pure
assertNot("&#x2G;");
}
-/**
+/*
* Class representing an XML document.
*
* Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
@@ -529,18 +532,18 @@ string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @safe pure
*/
class Document : Element
{
- /**
+ /*
* Contains all text which occurs before the root element.
* Defaults to &lt;?xml version="1.0"?&gt;
*/
string prolog = "<?xml version=\"1.0\"?>";
- /**
+ /*
* Contains all text which occurs after the root element.
* Defaults to the empty string
*/
string epilog;
- /**
+ /*
* Constructs a Document by parsing XML text.
*
* This function creates a complete DOM (Document Object Model) tree.
@@ -556,7 +559,7 @@ class Document : Element
{
assert(s.length != 0);
}
- body
+ do
{
auto xml = new DocumentParser(s);
string tagString = xml.tag.tagString;
@@ -567,7 +570,7 @@ class Document : Element
epilog = *xml.s;
}
- /**
+ /*
* Constructs a Document from a Tag.
*
* Params:
@@ -580,7 +583,7 @@ class Document : Element
const
{
- /**
+ /*
* Compares two Documents for equality
*
* Example:
@@ -597,7 +600,7 @@ class Document : Element
&& epilog == doc.epilog;
}
- /**
+ /*
* Compares two Documents
*
* You should rarely need to call this function. It exists so that
@@ -621,7 +624,7 @@ class Document : Element
return 0;
}
- /**
+ /*
* Returns the hash of a Document
*
* You should rarely need to call this function. It exists so that
@@ -632,7 +635,7 @@ class Document : Element
return hash(prolog, hash(epilog, (cast() this).Element.toHash()));
}
- /**
+ /*
* Returns the string representation of a Document. (That is, the
* complete XML of a document).
*/
@@ -661,22 +664,22 @@ class Document : Element
assert(b > a);
}
-/**
+/*
* Class representing an XML element.
*
* Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
*/
class Element : Item
{
- Tag tag; /// The start tag of the element
- Item[] items; /// The element's items
- Text[] texts; /// The element's text items
- CData[] cdatas; /// The element's CData items
- Comment[] comments; /// The element's comments
- ProcessingInstruction[] pis; /// The element's processing instructions
- Element[] elements; /// The element's child elements
-
- /**
+ Tag tag; // The start tag of the element
+ Item[] items; // The element's items
+ Text[] texts; // The element's text items
+ CData[] cdatas; // The element's CData items
+ Comment[] comments; // The element's comments
+ ProcessingInstruction[] pis; // The element's processing instructions
+ Element[] elements; // The element's child elements
+
+ /*
* Constructs an Element given a name and a string to be used as a Text
* interior.
*
@@ -693,10 +696,10 @@ class Element : Item
this(string name, string interior=null) @safe pure
{
this(new Tag(name));
- if (interior.length != 0) opCatAssign(new Text(interior));
+ if (interior.length != 0) opOpAssign!("~")(new Text(interior));
}
- /**
+ /*
* Constructs an Element from a Tag.
*
* Params:
@@ -710,7 +713,7 @@ class Element : Item
tag.tagString = tag_.tagString;
}
- /**
+ /*
* Append a text item to the interior of this element
*
* Params:
@@ -722,13 +725,14 @@ class Element : Item
* element ~= new Text("hello");
* --------------
*/
- void opCatAssign(Text item) @safe pure
+ void opOpAssign(string op)(Text item) @safe pure
+ if (op == "~")
{
texts ~= item;
appendItem(item);
}
- /**
+ /*
* Append a CData item to the interior of this element
*
* Params:
@@ -740,13 +744,14 @@ class Element : Item
* element ~= new CData("hello");
* --------------
*/
- void opCatAssign(CData item) @safe pure
+ void opOpAssign(string op)(CData item) @safe pure
+ if (op == "~")
{
cdatas ~= item;
appendItem(item);
}
- /**
+ /*
* Append a comment to the interior of this element
*
* Params:
@@ -758,13 +763,14 @@ class Element : Item
* element ~= new Comment("hello");
* --------------
*/
- void opCatAssign(Comment item) @safe pure
+ void opOpAssign(string op)(Comment item) @safe pure
+ if (op == "~")
{
comments ~= item;
appendItem(item);
}
- /**
+ /*
* Append a processing instruction to the interior of this element
*
* Params:
@@ -776,13 +782,14 @@ class Element : Item
* element ~= new ProcessingInstruction("hello");
* --------------
*/
- void opCatAssign(ProcessingInstruction item) @safe pure
+ void opOpAssign(string op)(ProcessingInstruction item) @safe pure
+ if (op == "~")
{
pis ~= item;
appendItem(item);
}
- /**
+ /*
* Append a complete element to the interior of this element
*
* Params:
@@ -796,7 +803,8 @@ class Element : Item
* // appends element representing <br />
* --------------
*/
- void opCatAssign(Element item) @safe pure
+ void opOpAssign(string op)(Element item) @safe pure
+ if (op == "~")
{
elements ~= item;
appendItem(item);
@@ -811,22 +819,22 @@ class Element : Item
private void parse(ElementParser xml)
{
- xml.onText = (string s) { opCatAssign(new Text(s)); };
- xml.onCData = (string s) { opCatAssign(new CData(s)); };
- xml.onComment = (string s) { opCatAssign(new Comment(s)); };
- xml.onPI = (string s) { opCatAssign(new ProcessingInstruction(s)); };
+ xml.onText = (string s) { opOpAssign!("~")(new Text(s)); };
+ xml.onCData = (string s) { opOpAssign!("~")(new CData(s)); };
+ xml.onComment = (string s) { opOpAssign!("~")(new Comment(s)); };
+ xml.onPI = (string s) { opOpAssign!("~")(new ProcessingInstruction(s)); };
xml.onStartTag[null] = (ElementParser xml)
{
auto e = new Element(xml.tag);
e.parse(xml);
- opCatAssign(e);
+ opOpAssign!("~")(e);
};
xml.parse();
}
- /**
+ /*
* Compares two Elements for equality
*
* Example:
@@ -847,7 +855,7 @@ class Element : Item
return true;
}
- /**
+ /*
* Compares two Elements
*
* You should rarely need to call this function. It exists so that Elements
@@ -872,7 +880,7 @@ class Element : Item
}
}
- /**
+ /*
* Returns the hash of an Element
*
* You should rarely need to call this function. It exists so that Elements
@@ -887,7 +895,7 @@ class Element : Item
const
{
- /**
+ /*
* Returns the decoded interior of an element.
*
* The element is assumed to contain text <i>only</i>. So, for
@@ -911,7 +919,7 @@ class Element : Item
return buffer;
}
- /**
+ /*
* Returns an indented string representation of this item
*
* Params:
@@ -947,7 +955,7 @@ class Element : Item
return a;
}
- /**
+ /*
* Returns the string representation of an Element
*
* Example:
@@ -970,7 +978,7 @@ class Element : Item
}
}
-/**
+/*
* Tag types.
*
* $(DDOC_ENUM_MEMBERS START) Used for start tags
@@ -980,7 +988,7 @@ class Element : Item
*/
enum TagType { START, END, EMPTY }
-/**
+/*
* Class representing an XML tag.
*
* Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
@@ -994,9 +1002,9 @@ enum TagType { START, END, EMPTY }
*/
class Tag
{
- TagType type = TagType.START; /// Type of tag
- string name; /// Tag name
- string[string] attr; /// Associative array of attributes
+ TagType type = TagType.START; // Type of tag
+ string name; // Tag name
+ string[string] attr; // Associative array of attributes
private string tagString;
invariant()
@@ -1017,11 +1025,11 @@ class Tag
s = k;
try { checkName(s,t); }
catch (Err e)
- { assert(false,"Invalid atrribute name:" ~ e.toString()); }
+ { assert(false,"Invalid attribute name:" ~ e.toString()); }
}
}
- /**
+ /*
* Constructs an instance of Tag with a specified name and type
*
* The constructor does not initialize the attributes. To initialize the
@@ -1111,7 +1119,7 @@ class Tag
const
{
- /**
+ /*
* Compares two Tags for equality
*
* You should rarely need to call this function. It exists so that Tags
@@ -1133,7 +1141,7 @@ class Tag
true )));
}
- /**
+ /*
* Compares two Tags
*
* Example:
@@ -1153,7 +1161,7 @@ class Tag
0 )));
}
- /**
+ /*
* Returns the hash of a Tag
*
* You should rarely need to call this function. It exists so that Tags
@@ -1161,10 +1169,10 @@ class Tag
*/
override size_t toHash()
{
- return typeid(name).getHash(&name);
+ return .hashOf(name);
}
- /**
+ /*
* Returns the string representation of a Tag
*
* Example:
@@ -1198,7 +1206,7 @@ class Tag
string toEmptyString() @safe { return toNonEndString() ~ " />"; }
}
- /**
+ /*
* Returns true if the Tag is a start tag
*
* Example:
@@ -1208,7 +1216,7 @@ class Tag
*/
@property bool isStart() @safe @nogc pure nothrow { return type == TagType.START; }
- /**
+ /*
* Returns true if the Tag is an end tag
*
* Example:
@@ -1218,7 +1226,7 @@ class Tag
*/
@property bool isEnd() @safe @nogc pure nothrow { return type == TagType.END; }
- /**
+ /*
* Returns true if the Tag is an empty tag
*
* Example:
@@ -1230,14 +1238,14 @@ class Tag
}
}
-/**
+/*
* Class representing a comment
*/
class Comment : Item
{
private string content;
- /**
+ /*
* Construct a comment
*
* Params:
@@ -1261,7 +1269,7 @@ class Comment : Item
this.content = content;
}
- /**
+ /*
* Compares two comments for equality
*
* Example:
@@ -1277,7 +1285,7 @@ class Comment : Item
return t !is null && content == t.content;
}
- /**
+ /*
* Compares two comments
*
* You should rarely need to call this function. It exists so that Comments
@@ -1297,7 +1305,7 @@ class Comment : Item
? (content < t.content ? -1 : 1 ) : 0 );
}
- /**
+ /*
* Returns the hash of a Comment
*
* You should rarely need to call this function. It exists so that Comments
@@ -1305,15 +1313,16 @@ class Comment : Item
*/
override size_t toHash() scope const nothrow { return hash(content); }
- /**
+ /*
* Returns a string representation of this comment
*/
override string toString() scope const @safe pure nothrow { return "<!--" ~ content ~ "-->"; }
- override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } /// Returns false always
+ override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } // Returns false always
}
-@safe unittest // issue 16241
+// https://issues.dlang.org/show_bug.cgi?id=16241
+@safe unittest
{
import std.exception : assertThrown;
auto c = new Comment("==");
@@ -1321,14 +1330,14 @@ class Comment : Item
assertThrown!CommentException(new Comment("--"));
}
-/**
+/*
* Class representing a Character Data section
*/
class CData : Item
{
private string content;
- /**
+ /*
* Construct a character data section
*
* Params:
@@ -1349,7 +1358,7 @@ class CData : Item
this.content = content;
}
- /**
+ /*
* Compares two CDatas for equality
*
* Example:
@@ -1365,7 +1374,7 @@ class CData : Item
return t !is null && content == t.content;
}
- /**
+ /*
* Compares two CDatas
*
* You should rarely need to call this function. It exists so that CDatas
@@ -1385,7 +1394,7 @@ class CData : Item
? (content < t.content ? -1 : 1 ) : 0 );
}
- /**
+ /*
* Returns the hash of a CData
*
* You should rarely need to call this function. It exists so that CDatas
@@ -1393,22 +1402,22 @@ class CData : Item
*/
override size_t toHash() scope const nothrow { return hash(content); }
- /**
+ /*
* Returns a string representation of this CData section
*/
override string toString() scope const @safe pure nothrow { return cdata ~ content ~ "]]>"; }
- override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } /// Returns false always
+ override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } // Returns false always
}
-/**
+/*
* Class representing a text (aka Parsed Character Data) section
*/
class Text : Item
{
private string content;
- /**
+ /*
* Construct a text (aka PCData) section
*
* Params:
@@ -1426,7 +1435,7 @@ class Text : Item
this.content = encode(content);
}
- /**
+ /*
* Compares two text sections for equality
*
* Example:
@@ -1442,7 +1451,7 @@ class Text : Item
return t !is null && content == t.content;
}
- /**
+ /*
* Compares two text sections
*
* You should rarely need to call this function. It exists so that Texts
@@ -1462,7 +1471,7 @@ class Text : Item
&& (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
}
- /**
+ /*
* Returns the hash of a text section
*
* You should rarely need to call this function. It exists so that Texts
@@ -1470,25 +1479,25 @@ class Text : Item
*/
override size_t toHash() scope const nothrow { return hash(content); }
- /**
+ /*
* Returns a string representation of this Text section
*/
override string toString() scope const @safe @nogc pure nothrow { return content; }
- /**
+ /*
* Returns true if the content is the empty string
*/
override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return content.length == 0; }
}
-/**
+/*
* Class representing an XML Instruction section
*/
class XMLInstruction : Item
{
private string content;
- /**
+ /*
* Construct an XML Instruction section
*
* Params:
@@ -1509,7 +1518,7 @@ class XMLInstruction : Item
this.content = content;
}
- /**
+ /*
* Compares two XML instructions for equality
*
* Example:
@@ -1525,7 +1534,7 @@ class XMLInstruction : Item
return t !is null && content == t.content;
}
- /**
+ /*
* Compares two XML instructions
*
* You should rarely need to call this function. It exists so that
@@ -1545,7 +1554,7 @@ class XMLInstruction : Item
&& (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
}
- /**
+ /*
* Returns the hash of an XMLInstruction
*
* You should rarely need to call this function. It exists so that
@@ -1553,22 +1562,22 @@ class XMLInstruction : Item
*/
override size_t toHash() scope const nothrow { return hash(content); }
- /**
+ /*
* Returns a string representation of this XmlInstruction
*/
override string toString() scope const @safe pure nothrow { return "<!" ~ content ~ ">"; }
- override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } /// Returns false always
+ override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } // Returns false always
}
-/**
+/*
* Class representing a Processing Instruction section
*/
class ProcessingInstruction : Item
{
private string content;
- /**
+ /*
* Construct a Processing Instruction section
*
* Params:
@@ -1589,7 +1598,7 @@ class ProcessingInstruction : Item
this.content = content;
}
- /**
+ /*
* Compares two processing instructions for equality
*
* Example:
@@ -1605,7 +1614,7 @@ class ProcessingInstruction : Item
return t !is null && content == t.content;
}
- /**
+ /*
* Compares two processing instructions
*
* You should rarely need to call this function. It exists so that
@@ -1625,7 +1634,7 @@ class ProcessingInstruction : Item
&& (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
}
- /**
+ /*
* Returns the hash of a ProcessingInstruction
*
* You should rarely need to call this function. It exists so that
@@ -1633,32 +1642,32 @@ class ProcessingInstruction : Item
*/
override size_t toHash() scope const nothrow { return hash(content); }
- /**
+ /*
* Returns a string representation of this ProcessingInstruction
*/
override string toString() scope const @safe pure nothrow { return "<?" ~ content ~ "?>"; }
- override @property @safe @nogc pure nothrow bool isEmptyXML() scope const { return false; } /// Returns false always
+ override @property @safe @nogc pure nothrow bool isEmptyXML() scope const { return false; } // Returns false always
}
-/**
+/*
* Abstract base class for XML items
*/
abstract class Item
{
- /// Compares with another Item of same type for equality
+ // Compares with another Item of same type for equality
abstract override bool opEquals(scope const Object o) @safe const;
- /// Compares with another Item of same type
+ // Compares with another Item of same type
abstract override int opCmp(scope const Object o) @safe const;
- /// Returns the hash of this item
+ // Returns the hash of this item
abstract override size_t toHash() @safe scope const;
- /// Returns a string representation of this item
+ // Returns a string representation of this item
abstract override string toString() @safe scope const;
- /**
+ /*
* Returns an indented string representation of this item
*
* Params:
@@ -1671,11 +1680,11 @@ abstract class Item
return s.length == 0 ? [] : [ s ];
}
- /// Returns true if the item represents empty XML text
+ // Returns true if the item represents empty XML text
abstract @property @safe @nogc pure nothrow bool isEmptyXML() scope const;
}
-/**
+/*
* Class for parsing an XML Document.
*
* This is a subclass of ElementParser. Most of the useful functions are
@@ -1693,7 +1702,7 @@ class DocumentParser : ElementParser
{
string xmlText;
- /**
+ /*
* Constructs a DocumentParser.
*
* The input to this function MUST be valid XML.
@@ -1718,7 +1727,7 @@ class DocumentParser : ElementParser
assert(false, "\n" ~ e.toString());
}
}
- body
+ do
{
xmlText = xmlText_;
s = &xmlText;
@@ -1735,7 +1744,7 @@ class DocumentParser : ElementParser
assert(doc.items == doc.elements);
}
-/**
+/*
* Class for parsing an XML element.
*
* Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
@@ -1782,13 +1791,13 @@ class ElementParser
}
}
- /**
+ /*
* The Tag at the start of the element being parsed. You can read this to
* determine the tag's name and attributes.
*/
@property @safe @nogc pure nothrow const(Tag) tag() const { return tag_; }
- /**
+ /*
* Register a handler which will be called whenever a start tag is
* encountered which matches the specified name. You can also pass null as
* the name, in which case the handler will be called for any unmatched
@@ -1824,7 +1833,7 @@ class ElementParser
*/
ParserHandler[string] onStartTag;
- /**
+ /*
* Register a handler which will be called whenever an end tag is
* encountered which matches the specified name. You can also pass null as
* the name, in which case the handler will be called for any unmatched
@@ -1860,7 +1869,7 @@ class ElementParser
elementStart = *s;
}
- /**
+ /*
* Register a handler which will be called whenever text is encountered.
*
* Example:
@@ -1880,7 +1889,7 @@ class ElementParser
*/
@property @safe @nogc pure nothrow void onText(Handler handler) { textHandler = handler; }
- /**
+ /*
* Register an alternative handler which will be called whenever text
* is encountered. This differs from onText in that onText will decode
* the text, whereas onTextRaw will not. This allows you to make design
@@ -1906,7 +1915,7 @@ class ElementParser
*/
@safe @nogc pure nothrow void onTextRaw(Handler handler) { rawTextHandler = handler; }
- /**
+ /*
* Register a handler which will be called whenever a character data
* segment is encountered.
*
@@ -1927,7 +1936,7 @@ class ElementParser
*/
@property @safe @nogc pure nothrow void onCData(Handler handler) { cdataHandler = handler; }
- /**
+ /*
* Register a handler which will be called whenever a comment is
* encountered.
*
@@ -1948,7 +1957,7 @@ class ElementParser
*/
@property @safe @nogc pure nothrow void onComment(Handler handler) { commentHandler = handler; }
- /**
+ /*
* Register a handler which will be called whenever a processing
* instruction is encountered.
*
@@ -1969,7 +1978,7 @@ class ElementParser
*/
@property @safe @nogc pure nothrow void onPI(Handler handler) { piHandler = handler; }
- /**
+ /*
* Register a handler which will be called whenever an XML instruction is
* encountered.
*
@@ -1992,7 +2001,7 @@ class ElementParser
*/
@property @safe @nogc pure nothrow void onXI(Handler handler) { xiHandler = handler; }
- /**
+ /*
* Parse an XML element.
*
* Parsing will continue until the end of the current element. Any items
@@ -2091,8 +2100,8 @@ class ElementParser
{
Tag startTag = new Tag(tag_.name);
- // FIX by hed010gy, for bug 2979
- // http://d.puremagic.com/issues/show_bug.cgi?id=2979
+ // FIX by hed010gy
+ // https://issues.dlang.org/show_bug.cgi?id=2979
if (tag_.attr.length > 0)
foreach (tn,tv; tag_.attr) startTag.attr[tn]=tv;
// END FIX
@@ -2130,7 +2139,7 @@ class ElementParser
}
}
- /**
+ /*
* Returns that part of the element which has already been parsed
*/
override string toString() const @nogc @safe pure nothrow
@@ -2716,7 +2725,7 @@ private
}
}
-/**
+/*
* Check an entire XML document for well-formedness
*
* Params:
@@ -2858,57 +2867,57 @@ EOS";
assert(doc.toString() == s);
}
-/** The base class for exceptions thrown by this module */
+/* The base class for exceptions thrown by this module */
class XMLException : Exception { this(string msg) @safe pure { super(msg); } }
// Other exceptions
-/// Thrown during Comment constructor
+// Thrown during Comment constructor
class CommentException : XMLException
{ private this(string msg) @safe pure { super(msg); } }
-/// Thrown during CData constructor
+// Thrown during CData constructor
class CDataException : XMLException
{ private this(string msg) @safe pure { super(msg); } }
-/// Thrown during XMLInstruction constructor
+// Thrown during XMLInstruction constructor
class XIException : XMLException
{ private this(string msg) @safe pure { super(msg); } }
-/// Thrown during ProcessingInstruction constructor
+// Thrown during ProcessingInstruction constructor
class PIException : XMLException
{ private this(string msg) @safe pure { super(msg); } }
-/// Thrown during Text constructor
+// Thrown during Text constructor
class TextException : XMLException
{ private this(string msg) @safe pure { super(msg); } }
-/// Thrown during decode()
+// Thrown during decode()
class DecodeException : XMLException
{ private this(string msg) @safe pure { super(msg); } }
-/// Thrown if comparing with wrong type
+// Thrown if comparing with wrong type
class InvalidTypeException : XMLException
{ private this(string msg) @safe pure { super(msg); } }
-/// Thrown when parsing for Tags
+// Thrown when parsing for Tags
class TagException : XMLException
{ private this(string msg) @safe pure { super(msg); } }
-/**
+/*
* Thrown during check()
*/
class CheckException : XMLException
{
- CheckException err; /// Parent in hierarchy
+ CheckException err; // Parent in hierarchy
private string tail;
- /**
+ /*
* Name of production rule which failed to parse,
* or specific error message
*/
string msg;
- size_t line = 0; /// Line number at which parse failure occurred
- size_t column = 0; /// Column number at which parse failure occurred
+ size_t line = 0; // Line number at which parse failure occurred
+ size_t column = 0; // Column number at which parse failure occurred
private this(string tail,string msg,Err err=null) @safe pure
{
@@ -2950,7 +2959,7 @@ private alias Err = CheckException;
private
{
- inout(T) toType(T)(inout Object o)
+ inout(T) toType(T)(inout return scope Object o)
{
T t = cast(T)(o);
if (t is null)
@@ -2993,10 +3002,7 @@ private
return ch;
}
- size_t hash(string s,size_t h=0) @trusted nothrow
- {
- return typeid(s).getHash(&s) + h;
- }
+ alias hash = .hashOf;
// Definitions from the XML specification
immutable CharTable=[0x9,0x9,0xA,0xA,0xD,0xD,0x20,0xD7FF,0xE000,0xFFFD,
diff --git a/libphobos/src/std/zip.d b/libphobos/src/std/zip.d
index 9e55d199a9b..4d7422bdd1c 100644
--- a/libphobos/src/std/zip.d
+++ b/libphobos/src/std/zip.d
@@ -1,114 +1,169 @@
// Written in the D programming language.
/**
- * Read/write data in the $(LINK2 http://www.info-zip.org, _zip archive) format.
- * Makes use of the etc.c.zlib compression library.
- *
- * Bugs:
- * $(UL
- * $(LI Multi-disk zips not supported.)
- * $(LI Only Zip version 20 formats are supported.)
- * $(LI Only supports compression modes 0 (no compression) and 8 (deflate).)
- * $(LI Does not support encryption.)
- * $(LI $(BUGZILLA 592))
- * $(LI $(BUGZILLA 2137))
- * )
- *
- * Example:
- * ---
-// Read existing zip file.
-import std.digest.crc, std.file, std.stdio, std.zip;
+Read and write data in the
+$(LINK2 https://en.wikipedia.org/wiki/Zip_%28file_format%29, zip archive)
+format.
+
+Standards:
+
+The current implementation mostly conforms to
+$(LINK2 https://www.iso.org/standard/60101.html, ISO/IEC 21320-1:2015),
+which means,
+$(UL
+$(LI that files can only be stored uncompressed or using the deflate mechanism,)
+$(LI that encryption features are not used,)
+$(LI that digital signature features are not used,)
+$(LI that patched data features are not used, and)
+$(LI that archives may not span multiple volumes.)
+)
+
+Additionally, archives are checked for malware attacks and rejected if detected.
+This includes
+$(UL
+$(LI $(LINK2 https://news.ycombinator.com/item?id=20352439, zip bombs) which
+ generate gigantic amounts of unpacked data)
+$(LI zip archives that contain overlapping records)
+$(LI chameleon zip archives which generate different unpacked data, depending
+ on the implementation of the unpack algorithm)
+)
+
+The current implementation makes use of the zlib compression library.
+
+Usage:
+
+There are two main ways of usage: Extracting files from a zip archive
+and storing files into a zip archive. These can be mixed though (e.g.
+read an archive, remove some files, add others and write the new
+archive).
+
+Examples:
+
+Example for reading an existing zip archive:
+---
+import std.stdio : writeln, writefln;
+import std.file : read;
+import std.zip;
void main(string[] args)
{
// read a zip file into memory
auto zip = new ZipArchive(read(args[1]));
- writeln("Archive: ", args[1]);
- writefln("%-10s %-8s Name", "Length", "CRC-32");
+
// iterate over all zip members
+ writefln("%-10s %-8s Name", "Length", "CRC-32");
foreach (name, am; zip.directory)
{
// print some data about each member
writefln("%10s %08x %s", am.expandedSize, am.crc32, name);
assert(am.expandedData.length == 0);
+
// decompress the archive member
zip.expand(am);
assert(am.expandedData.length == am.expandedSize);
}
}
+---
-// Create and write new zip file.
+Example for writing files into a zip archive:
+---
import std.file : write;
import std.string : representation;
+import std.zip;
void main()
{
- char[] data = "Test data.\n".dup;
- // Create an ArchiveMember for the test file.
- ArchiveMember am = new ArchiveMember();
- am.name = "test.txt";
- am.expandedData(data.representation);
+ // Create an ArchiveMembers for each file.
+ ArchiveMember file1 = new ArchiveMember();
+ file1.name = "test1.txt";
+ file1.expandedData("Test data.\n".dup.representation);
+ file1.compressionMethod = CompressionMethod.none; // don't compress
+
+ ArchiveMember file2 = new ArchiveMember();
+ file2.name = "test2.txt";
+ file2.expandedData("More test data.\n".dup.representation);
+ file2.compressionMethod = CompressionMethod.deflate; // compress
+
// Create an archive and add the member.
ZipArchive zip = new ZipArchive();
- zip.addMember(am);
+
+ // add ArchiveMembers
+ zip.addMember(file1);
+ zip.addMember(file2);
+
// Build the archive
void[] compressed_data = zip.build();
+
// Write to a file
write("test.zip", compressed_data);
}
- * ---
- *
- * Copyright: Copyright Digital Mars 2000 - 2009.
+---
+
+ * Copyright: Copyright The D Language Foundation 2000 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright)
- * Source: $(PHOBOSSRC std/_zip.d)
+ * Source: $(PHOBOSSRC std/zip.d)
*/
-/* Copyright Digital Mars 2000 - 2009.
+/* Copyright The D Language Foundation 2000 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
module std.zip;
+import std.exception : enforce;
+
+// Non-Android/Apple ARM POSIX-only, because we can't rely on the unzip
+// command being available on Android, Apple ARM or Windows
+version (Android) {}
+else version (iOS) {}
+else version (TVOS) {}
+else version (WatchOS) {}
+else version (Posix)
+ version = HasUnzip;
+
//debug=print;
-/** Thrown on error.
- */
+/// Thrown on error.
class ZipException : Exception
{
- this(string msg) @safe
- {
- super("ZipException: " ~ msg);
- }
+ import std.exception : basicExceptionCtors;
+ ///
+ mixin basicExceptionCtors;
}
-/**
- * Compression method used by ArchiveMember
- */
+/// Compression method used by `ArchiveMember`.
enum CompressionMethod : ushort
{
- none = 0, /// No compression, just archiving
- deflate = 8 /// Deflate algorithm. Use zlib library to compress
+ none = 0, /// No compression, just archiving.
+ deflate = 8 /// Deflate algorithm. Use zlib library to compress.
}
-/**
- * A member of the ZipArchive.
- */
+/// A single file or directory inside the archive.
final class ArchiveMember
{
import std.conv : to, octal;
import std.datetime.systime : DosFileTime, SysTime, SysTimeToDosFileTime;
/**
- * Read/Write: Usually the file name of the archive member; it is used to
- * index the archive directory for the member. Each member must have a unique
- * name[]. Do not change without removing member from the directory first.
+ * The name of the archive member; it is used to index the
+ * archive directory for the member. Each member must have a
+ * unique name. Do not change without removing member from the
+ * directory first.
*/
string name;
- ubyte[] extra; /// Read/Write: extra data for this member.
- string comment; /// Read/Write: comment associated with this member.
+ /**
+ * The content of the extra data field for this member. See
+ * $(LINK2 https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT,
+ * original documentation)
+ * for a description of the general format of this data. May contain
+ * undocumented 3rd-party data.
+ */
+ ubyte[] extra;
+
+ string comment; /// Comment associated with this member.
private ubyte[] _compressedData;
private ubyte[] _expandedData;
@@ -119,32 +174,80 @@ final class ArchiveMember
private CompressionMethod _compressionMethod;
private ushort _madeVersion = 20;
private ushort _extractVersion = 20;
- private ushort _diskNumber;
private uint _externalAttributes;
private DosFileTime _time;
// by default, no explicit order goes after explicit order
private uint _index = uint.max;
- ushort flags; /// Read/Write: normally set to 0
- ushort internalAttributes; /// Read/Write
+ /**
+ * Contains some information on how to extract this archive. See
+ * $(LINK2 https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT,
+ * original documentation)
+ * for details.
+ */
+ ushort flags;
+
+ /**
+ * Internal attributes. Bit 1 is set, if the member is apparently in binary format
+ * and bit 2 is set, if each record is preceded by the length of the record.
+ */
+ ushort internalAttributes;
+
+ /**
+ * The zip file format version needed to extract this member.
+ *
+ * Returns: Format version needed to extract this member.
+ */
+ @property @safe pure nothrow @nogc ushort extractVersion() const { return _extractVersion; }
- @property ushort extractVersion() { return _extractVersion; } /// Read Only
- @property uint crc32() { return _crc32; } /// Read Only: cyclic redundancy check (CRC) value
+ /**
+ * Cyclic redundancy check (CRC) value.
+ *
+ * Returns: CRC32 value.
+ */
+ @property @safe pure nothrow @nogc uint crc32() const { return _crc32; }
+
+ /**
+ * Size of data of member in compressed form.
+ *
+ * Returns: Size of the compressed archive.
+ */
+ @property @safe pure nothrow @nogc uint compressedSize() const { return _compressedSize; }
- /// Read Only: size of data of member in compressed form.
- @property uint compressedSize() { return _compressedSize; }
+ /**
+ * Size of data of member in uncompressed form.
+ *
+ * Returns: Size of uncompressed archive.
+ */
+ @property @safe pure nothrow @nogc uint expandedSize() const { return _expandedSize; }
- /// Read Only: size of data of member in expanded form.
- @property uint expandedSize() { return _expandedSize; }
- @property ushort diskNumber() { return _diskNumber; } /// Read Only: should be 0.
+ /**
+ * Should be 0.
+ *
+ * Returns: The number of the disk where this member can be found.
+ */
+ deprecated("Multidisk not supported; will be removed in 2.099.0")
+ @property @safe pure nothrow @nogc ushort diskNumber() const { return 0; }
- /// Read Only: data of member in compressed form.
- @property ubyte[] compressedData() { return _compressedData; }
+ /**
+ * Data of member in compressed form.
+ *
+ * Returns: The file data in compressed form.
+ */
+ @property @safe pure nothrow @nogc ubyte[] compressedData() { return _compressedData; }
- /// Read data of member in uncompressed form.
- @property ubyte[] expandedData() { return _expandedData; }
+ /**
+ * Get or set data of member in uncompressed form. When an existing archive is
+ * read `ZipArchive.expand` needs to be called before this can be accessed.
+ *
+ * Params:
+ * ed = Expanded Data.
+ *
+ * Returns: The file data.
+ */
+ @property @safe pure nothrow @nogc ubyte[] expandedData() { return _expandedData; }
- /// Write data of member in uncompressed form.
+ /// ditto
@property @safe void expandedData(ubyte[] ed)
{
_expandedData = ed;
@@ -156,8 +259,14 @@ final class ArchiveMember
}
/**
- * Set the OS specific file attributes, as obtained by
- * $(REF getAttributes, std,file) or $(REF DirEntry.attributes, std,file), for this archive member.
+ * Get or set the OS specific file attributes for this archive member.
+ *
+ * Params:
+ * attr = Attributes as obtained by $(REF getAttributes, std,file) or
+ * $(REF DirEntry.attributes, std,file).
+ *
+ * Returns: The file attributes or 0 if the file attributes were
+ * encoded for an incompatible OS (Windows vs. POSIX).
*/
@property @safe void fileAttributes(uint attr)
{
@@ -186,14 +295,8 @@ final class ArchiveMember
assert((am._madeVersion & 0xFF00) == 0x0300);
}
- /**
- * Get the OS specific file attributes for the archive member.
- *
- * Returns: The file attributes or 0 if the file attributes were
- * encoded for an incompatible OS (Windows vs. Posix).
- *
- */
- @property uint fileAttributes() const
+ /// ditto
+ @property @nogc nothrow uint fileAttributes() const
{
version (Posix)
{
@@ -213,51 +316,66 @@ final class ArchiveMember
}
}
- /// Set the last modification time for this member.
- @property void time(SysTime time)
+ /**
+ * Get or set the last modification time for this member.
+ *
+ * Params:
+ * time = Time to set (will be saved as DosFileTime, which is less accurate).
+ *
+ * Returns:
+ * The last modification time in DosFileFormat.
+ */
+ @property DosFileTime time() const @safe pure nothrow @nogc
{
- _time = SysTimeToDosFileTime(time);
+ return _time;
}
/// ditto
- @property void time(DosFileTime time)
+ @property void time(SysTime time)
{
- _time = time;
+ _time = SysTimeToDosFileTime(time);
}
- /// Get the last modification time for this member.
- @property DosFileTime time() const
+ /// ditto
+ @property void time(DosFileTime time) @safe pure nothrow @nogc
{
- return _time;
+ _time = time;
}
/**
- * Read compression method used for this member
+ * Get or set compression method used for this member.
+ *
+ * Params:
+ * cm = Compression method.
+ *
+ * Returns: Compression method.
+ *
* See_Also:
- * CompressionMethod
+ * $(LREF CompressionMethod)
**/
- @property @safe CompressionMethod compressionMethod() { return _compressionMethod; }
+ @property @safe @nogc pure nothrow CompressionMethod compressionMethod() const { return _compressionMethod; }
- /**
- * Write compression method used for this member
- * See_Also:
- * CompressionMethod
- **/
- @property void compressionMethod(CompressionMethod cm)
+ /// ditto
+ @property @safe pure void compressionMethod(CompressionMethod cm)
{
if (cm == _compressionMethod) return;
- if (_compressedSize > 0)
- throw new ZipException("Can't change compression method for a compressed element");
+ enforce!ZipException(_compressedSize == 0, "Can't change compression method for a compressed element");
_compressionMethod = cm;
}
/**
- * The index of this archive member within the archive.
- */
- @property uint index() const pure nothrow @nogc { return _index; }
- @property uint index(uint value) pure nothrow @nogc { return _index = value; }
+ * The index of this archive member within the archive. Set this to a
+ * different value for reordering the members of an archive.
+ *
+ * Params:
+ * value = Index value to set.
+ *
+ * Returns: The index.
+ */
+ @property uint index(uint value) @safe pure nothrow @nogc { return _index = value; }
+ @property uint index() const @safe pure nothrow @nogc { return _index; } /// ditto
debug(print)
{
@@ -280,6 +398,21 @@ final class ArchiveMember
}
}
+@safe pure unittest
+{
+ import std.exception : assertThrown, assertNotThrown;
+
+ auto am = new ArchiveMember();
+
+ assertNotThrown(am.compressionMethod(CompressionMethod.deflate));
+ assertNotThrown(am.compressionMethod(CompressionMethod.none));
+
+ am._compressedData = [0x65]; // not strictly necessary, but for consistency
+ am._compressedSize = 1;
+
+ assertThrown!ZipException(am.compressionMethod(CompressionMethod.deflate));
+}
+
/**
* Object representing the entire archive.
* ZipArchives are collections of ArchiveMembers.
@@ -291,42 +424,93 @@ final class ZipArchive
import std.conv : to;
import std.datetime.systime : DosFileTime;
- string comment; /// Read/Write: the archive comment. Must be less than 65536 bytes in length.
+private:
+ // names are taken directly from the specification
+ // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
+ static immutable ubyte[] centralFileHeaderSignature = [ 0x50, 0x4b, 0x01, 0x02 ];
+ static immutable ubyte[] localFileHeaderSignature = [ 0x50, 0x4b, 0x03, 0x04 ];
+ static immutable ubyte[] endOfCentralDirSignature = [ 0x50, 0x4b, 0x05, 0x06 ];
+ static immutable ubyte[] archiveExtraDataSignature = [ 0x50, 0x4b, 0x06, 0x08 ];
+ static immutable ubyte[] digitalSignatureSignature = [ 0x50, 0x4b, 0x05, 0x05 ];
+ static immutable ubyte[] zip64EndOfCentralDirSignature = [ 0x50, 0x4b, 0x06, 0x06 ];
+ static immutable ubyte[] zip64EndOfCentralDirLocatorSignature = [ 0x50, 0x4b, 0x06, 0x07 ];
+
+ enum centralFileHeaderLength = 46;
+ enum localFileHeaderLength = 30;
+ enum endOfCentralDirLength = 22;
+ enum archiveExtraDataLength = 8;
+ enum digitalSignatureLength = 6;
+ enum zip64EndOfCentralDirLength = 56;
+ enum zip64EndOfCentralDirLocatorLength = 20;
+ enum dataDescriptorLength = 12;
+
+public:
+ string comment; /// The archive comment. Must be less than 65536 bytes in length.
private ubyte[] _data;
- private uint endrecOffset;
- private uint _diskNumber;
- private uint _diskStartDir;
- private uint _numEntries;
- private uint _totalEntries;
private bool _isZip64;
static const ushort zip64ExtractVersion = 45;
+
+ deprecated("Use digitalSignatureLength instead; will be removed in 2.098.0")
static const int digiSignLength = 6;
+ deprecated("Use zip64EndOfCentralDirLocatorLength instead; will be removed in 2.098.0")
static const int eocd64LocLength = 20;
+ deprecated("Use zip64EndOfCentralDirLength instead; will be removed in 2.098.0")
static const int eocd64Length = 56;
- /// Read Only: array representing the entire contents of the archive.
- @property @safe ubyte[] data() { return _data; }
+ private Segment[] _segs;
- /// Read Only: 0 since multi-disk zip archives are not supported.
- @property @safe uint diskNumber() { return _diskNumber; }
+ /**
+ * Array representing the entire contents of the archive.
+ *
+ * Returns: Data of the entire contents of the archive.
+ */
+ @property @safe @nogc pure nothrow ubyte[] data() { return _data; }
- /// Read Only: 0 since multi-disk zip archives are not supported
- @property @safe uint diskStartDir() { return _diskStartDir; }
+ /**
+ * 0 since multi-disk zip archives are not supported.
+ *
+ * Returns: Number of this disk.
+ */
+ deprecated("Multidisk not supported; will be removed in 2.099.0")
+ @property @safe @nogc pure nothrow uint diskNumber() const { return 0; }
- /// Read Only: number of ArchiveMembers in the directory.
- @property @safe uint numEntries() { return _numEntries; }
- @property @safe uint totalEntries() { return _totalEntries; } /// ditto
+ /**
+ * 0 since multi-disk zip archives are not supported.
+ *
+ * Returns: Number of the disk, where the central directory starts.
+ */
+ deprecated("Multidisk not supported; will be removed in 2.099.0")
+ @property @safe @nogc pure nothrow uint diskStartDir() const { return 0; }
- /// True when the archive is in Zip64 format.
- @property @safe bool isZip64() { return _isZip64; }
+ /**
+ * Number of ArchiveMembers in the directory.
+ *
+ * Returns: The number of files in this archive.
+ */
+ deprecated("Use totalEntries instead; will be removed in 2.099.0")
+ @property @safe @nogc pure nothrow uint numEntries() const { return cast(uint) _directory.length; }
+ @property @safe @nogc pure nothrow uint totalEntries() const { return cast(uint) _directory.length; } /// ditto
- /// Set this to true to force building a Zip64 archive.
- @property @safe void isZip64(bool value) { _isZip64 = value; }
/**
- * Read Only: array indexed by the name of each member of the archive.
- * All the members of the archive can be accessed with a foreach loop:
+ * True when the archive is in Zip64 format. Set this to true to force building a Zip64 archive.
+ *
+ * Params:
+ * value = True, when the archive is forced to be build in Zip64 format.
+ *
+ * Returns: True, when the archive is in Zip64 format.
+ */
+ @property @safe @nogc pure nothrow bool isZip64() const { return _isZip64; }
+
+ /// ditto
+ @property @safe @nogc pure nothrow void isZip64(bool value) { _isZip64 = value; }
+
+ /**
+ * Associative array indexed by the name of each member of the archive.
+ *
+ * All the members of the archive can be accessed with a foreach loop:
+ *
* Example:
* --------------------
* ZipArchive archive = new ZipArchive(data);
@@ -335,8 +519,10 @@ final class ZipArchive
* writefln("member name is '%s'", am.name);
* }
* --------------------
+ *
+ * Returns: Associative array with all archive members.
*/
- @property @safe ArchiveMember[string] directory() { return _directory; }
+ @property @safe @nogc pure nothrow ArchiveMember[string] directory() { return _directory; }
private ArchiveMember[string] _directory;
@@ -354,13 +540,21 @@ final class ZipArchive
/* ============ Creating a new archive =================== */
- /** Constructor to use when creating a new archive.
+ /**
+ * Constructor to use when creating a new archive.
*/
- this() @safe
+ this() @safe @nogc pure nothrow
{
}
- /** Add de to the archive. The file is compressed on the fly.
+ /**
+ * Add a member to the archive. The file is compressed on the fly.
+ *
+ * Params:
+ * de = Member to be added.
+ *
+ * Throws: ZipException when an unsupported compression method is used or when
+ * compression failed.
*/
@safe void addMember(ArchiveMember de)
{
@@ -390,61 +584,101 @@ final class ZipArchive
import std.zlib : crc32;
() @trusted { de._crc32 = crc32(0, cast(void[]) de._expandedData); }();
}
- assert(de._compressedData.length == de._compressedSize);
+ assert(de._compressedData.length == de._compressedSize, "Archive member compressed failed.");
+ }
+
+ @safe unittest
+ {
+ import std.exception : assertThrown;
+
+ ArchiveMember am = new ArchiveMember();
+ am.compressionMethod = cast(CompressionMethod) 3;
+
+ ZipArchive zip = new ZipArchive();
+
+ assertThrown!ZipException(zip.addMember(am));
}
- /** Delete de from the archive.
+ /**
+ * Delete member `de` from the archive. Uses the name of the member
+ * to detect which element to delete.
+ *
+ * Params:
+ * de = Member to be deleted.
*/
@safe void deleteMember(ArchiveMember de)
{
_directory.remove(de.name);
}
+ // https://issues.dlang.org/show_bug.cgi?id=20398
+ @safe unittest
+ {
+ import std.string : representation;
+
+ ArchiveMember file1 = new ArchiveMember();
+ file1.name = "test1.txt";
+ file1.expandedData("Test data.\n".dup.representation);
+
+ ZipArchive zip = new ZipArchive();
+
+ zip.addMember(file1);
+ assert(zip.totalEntries == 1);
+
+ zip.deleteMember(file1);
+ assert(zip.totalEntries == 0);
+ }
+
/**
- * Construct an archive out of the current members of the archive.
+ * Construct the entire contents of the current members of the archive.
*
- * Fills in the properties data[], diskNumber, diskStartDir, numEntries,
- * totalEntries, and directory[].
+ * Fills in the properties data[], totalEntries, and directory[].
* For each ArchiveMember, fills in properties crc32, compressedSize,
* compressedData[].
*
- * Returns: array representing the entire archive.
+ * Returns: Array representing the entire archive.
+ *
+ * Throws: ZipException when the archive could not be build.
*/
- void[] build()
+ void[] build() @safe pure
{
+ import std.array : array, uninitializedArray;
import std.algorithm.sorting : sort;
+ import std.string : representation;
+
uint i;
uint directoryOffset;
- if (comment.length > 0xFFFF)
- throw new ZipException("archive comment longer than 65535");
+ enforce!ZipException(comment.length <= 0xFFFF, "archive comment longer than 65535");
// Compress each member; compute size
uint archiveSize = 0;
uint directorySize = 0;
- auto directory = _directory.values().sort!((x, y) => x.index < y.index).release;
+ auto directory = _directory.byValue.array.sort!((x, y) => x.index < y.index).release;
foreach (ArchiveMember de; directory)
{
- if (to!ulong(archiveSize) + 30 + de.name.length + de.extra.length + de.compressedSize
- + directorySize + 46 + de.name.length + de.extra.length + de.comment.length
- + 22 + comment.length + eocd64LocLength + eocd64Length > uint.max)
- throw new ZipException("zip files bigger than 4 GB are unsupported");
-
- archiveSize += 30 + de.name.length +
+ enforce!ZipException(to!ulong(archiveSize) + localFileHeaderLength + de.name.length
+ + de.extra.length + de.compressedSize + directorySize
+ + centralFileHeaderLength + de.name.length + de.extra.length
+ + de.comment.length + endOfCentralDirLength + comment.length
+ + zip64EndOfCentralDirLocatorLength + zip64EndOfCentralDirLength <= uint.max,
+ "zip files bigger than 4 GB are unsupported");
+
+ archiveSize += localFileHeaderLength + de.name.length +
de.extra.length +
de.compressedSize;
- directorySize += 46 + de.name.length +
+ directorySize += centralFileHeaderLength + de.name.length +
de.extra.length +
de.comment.length;
}
if (!isZip64 && _directory.length > ushort.max)
_isZip64 = true;
- uint dataSize = archiveSize + directorySize + 22 + cast(uint) comment.length;
+ uint dataSize = archiveSize + directorySize + endOfCentralDirLength + cast(uint) comment.length;
if (isZip64)
- dataSize += eocd64LocLength + eocd64Length;
+ dataSize += zip64EndOfCentralDirLocatorLength + zip64EndOfCentralDirLength;
- _data = new ubyte[dataSize];
+ _data = uninitializedArray!(ubyte[])(dataSize);
// Populate the data[]
@@ -453,7 +687,7 @@ final class ZipArchive
foreach (ArchiveMember de; directory)
{
de.offset = i;
- _data[i .. i + 4] = cast(ubyte[])"PK\x03\x04";
+ _data[i .. i + 4] = localFileHeaderSignature;
putUshort(i + 4, de.extractVersion);
putUshort(i + 6, de.flags);
putUshort(i + 8, de._compressionMethod);
@@ -463,9 +697,9 @@ final class ZipArchive
putUint (i + 22, to!uint(de.expandedSize));
putUshort(i + 26, cast(ushort) de.name.length);
putUshort(i + 28, cast(ushort) de.extra.length);
- i += 30;
+ i += localFileHeaderLength;
- _data[i .. i + de.name.length] = (cast(ubyte[]) de.name)[];
+ _data[i .. i + de.name.length] = (de.name.representation)[];
i += de.name.length;
_data[i .. i + de.extra.length] = (cast(ubyte[]) de.extra)[];
i += de.extra.length;
@@ -475,10 +709,9 @@ final class ZipArchive
// Write directory
directoryOffset = i;
- _numEntries = 0;
foreach (ArchiveMember de; directory)
{
- _data[i .. i + 4] = cast(ubyte[])"PK\x01\x02";
+ _data[i .. i + 4] = centralFileHeaderSignature;
putUshort(i + 4, de._madeVersion);
putUshort(i + 6, de.extractVersion);
putUshort(i + 8, de.flags);
@@ -490,180 +723,175 @@ final class ZipArchive
putUshort(i + 28, cast(ushort) de.name.length);
putUshort(i + 30, cast(ushort) de.extra.length);
putUshort(i + 32, cast(ushort) de.comment.length);
- putUshort(i + 34, de.diskNumber);
+ putUshort(i + 34, cast(ushort) 0);
putUshort(i + 36, de.internalAttributes);
putUint (i + 38, de._externalAttributes);
putUint (i + 42, de.offset);
- i += 46;
+ i += centralFileHeaderLength;
- _data[i .. i + de.name.length] = (cast(ubyte[]) de.name)[];
+ _data[i .. i + de.name.length] = (de.name.representation)[];
i += de.name.length;
_data[i .. i + de.extra.length] = (cast(ubyte[]) de.extra)[];
i += de.extra.length;
- _data[i .. i + de.comment.length] = (cast(ubyte[]) de.comment)[];
+ _data[i .. i + de.comment.length] = (de.comment.representation)[];
i += de.comment.length;
- _numEntries++;
}
- _totalEntries = numEntries;
if (isZip64)
{
// Write zip64 end of central directory record
uint eocd64Offset = i;
- _data[i .. i + 4] = cast(ubyte[])"PK\x06\x06";
- putUlong (i + 4, eocd64Length - 12);
+ _data[i .. i + 4] = zip64EndOfCentralDirSignature;
+ putUlong (i + 4, zip64EndOfCentralDirLength - 12);
putUshort(i + 12, zip64ExtractVersion);
putUshort(i + 14, zip64ExtractVersion);
- putUint (i + 16, diskNumber);
- putUint (i + 20, diskStartDir);
- putUlong (i + 24, numEntries);
- putUlong (i + 32, totalEntries);
+ putUint (i + 16, cast(ushort) 0);
+ putUint (i + 20, cast(ushort) 0);
+ putUlong (i + 24, directory.length);
+ putUlong (i + 32, directory.length);
putUlong (i + 40, directorySize);
putUlong (i + 48, directoryOffset);
- i += eocd64Length;
+ i += zip64EndOfCentralDirLength;
// Write zip64 end of central directory record locator
- _data[i .. i + 4] = cast(ubyte[])"PK\x06\x07";
- putUint (i + 4, diskNumber);
+ _data[i .. i + 4] = zip64EndOfCentralDirLocatorSignature;
+ putUint (i + 4, cast(ushort) 0);
putUlong (i + 8, eocd64Offset);
putUint (i + 16, 1);
- i += eocd64LocLength;
+ i += zip64EndOfCentralDirLocatorLength;
}
// Write end record
- endrecOffset = i;
- _data[i .. i + 4] = cast(ubyte[])"PK\x05\x06";
- putUshort(i + 4, cast(ushort) diskNumber);
- putUshort(i + 6, cast(ushort) diskStartDir);
- putUshort(i + 8, (numEntries > ushort.max ? ushort.max : cast(ushort) numEntries));
+ _data[i .. i + 4] = endOfCentralDirSignature;
+ putUshort(i + 4, cast(ushort) 0);
+ putUshort(i + 6, cast(ushort) 0);
+ putUshort(i + 8, (totalEntries > ushort.max ? ushort.max : cast(ushort) totalEntries));
putUshort(i + 10, (totalEntries > ushort.max ? ushort.max : cast(ushort) totalEntries));
putUint (i + 12, directorySize);
putUint (i + 16, directoryOffset);
putUshort(i + 20, cast(ushort) comment.length);
- i += 22;
+ i += endOfCentralDirLength;
// Write archive comment
- assert(i + comment.length == data.length);
- _data[i .. data.length] = (cast(ubyte[]) comment)[];
+ assert(i + comment.length == data.length, "Writing the archive comment failed.");
+ _data[i .. data.length] = (comment.representation)[];
return cast(void[]) data;
}
+ @safe pure unittest
+ {
+ import std.exception : assertNotThrown;
+
+ ZipArchive zip = new ZipArchive();
+ zip.comment = "A";
+ assertNotThrown(zip.build());
+ }
+
+ @safe pure unittest
+ {
+ import std.range : repeat, array;
+ import std.exception : assertThrown;
+
+ ZipArchive zip = new ZipArchive();
+ zip.comment = 'A'.repeat(70_000).array;
+ assertThrown!ZipException(zip.build());
+ }
+
/* ============ Reading an existing archive =================== */
/**
* Constructor to use when reading an existing archive.
*
- * Fills in the properties data[], diskNumber, diskStartDir, numEntries,
- * totalEntries, comment[], and directory[].
+ * Fills in the properties data[], totalEntries, comment[], and directory[].
* For each ArchiveMember, fills in
* properties madeVersion, extractVersion, flags, compressionMethod, time,
- * crc32, compressedSize, expandedSize, compressedData[], diskNumber,
+ * crc32, compressedSize, expandedSize, compressedData[],
* internalAttributes, externalAttributes, name[], extra[], comment[].
* Use expand() to get the expanded data for each ArchiveMember.
*
* Params:
- * buffer = the entire contents of the archive.
+ * buffer = The entire contents of the archive.
+ *
+ * Throws: ZipException when the archive was invalid or when malware was detected.
*/
-
this(void[] buffer)
- { uint iend;
- uint i;
- int endcommentlength;
- uint directorySize;
- uint directoryOffset;
-
+ {
this._data = cast(ubyte[]) buffer;
- if (data.length > uint.max - 2)
- throw new ZipException("zip files bigger than 4 GB are unsupported");
+ enforce!ZipException(data.length <= uint.max - 2, "zip files bigger than 4 GB are unsupported");
+
+ _segs = [Segment(0, cast(uint) data.length)];
+
+ uint i = findEndOfCentralDirRecord();
- // Find 'end record index' by searching backwards for signature
- iend = (data.length > 66_000 ? to!uint(data.length - 66_000) : 0);
- for (i = to!uint(data.length) - 22; 1; i--)
+ int endCommentLength = getUshort(i + 20);
+ comment = cast(string)(_data[i + endOfCentralDirLength .. i + endOfCentralDirLength + endCommentLength]);
+
+ // end of central dir record
+ removeSegment(i, i + endOfCentralDirLength + endCommentLength);
+
+ uint k = i - zip64EndOfCentralDirLocatorLength;
+ if (k < i && _data[k .. k + 4] == zip64EndOfCentralDirLocatorSignature)
{
- if (i < iend || i >= data.length)
- throw new ZipException("no end record");
+ _isZip64 = true;
+ i = k;
- if (_data[i .. i + 4] == cast(ubyte[])"PK\x05\x06")
- {
- endcommentlength = getUshort(i + 20);
- if (i + 22 + endcommentlength > data.length
- || i + 22 + endcommentlength < i)
- continue;
- comment = cast(string)(_data[i + 22 .. i + 22 + endcommentlength]);
- endrecOffset = i;
-
- uint k = i - eocd64LocLength;
- if (k < i && _data[k .. k + 4] == cast(ubyte[])"PK\x06\x07")
- {
- _isZip64 = true;
- i = k;
- }
-
- break;
- }
+ // zip64 end of central dir record locator
+ removeSegment(k, k + zip64EndOfCentralDirLocatorLength);
}
+ uint directorySize;
+ uint directoryOffset;
+ uint directoryCount;
+
if (isZip64)
{
// Read Zip64 record data
ulong eocdOffset = getUlong(i + 8);
- if (eocdOffset + eocd64Length > _data.length)
- throw new ZipException("corrupted directory");
+ enforce!ZipException(eocdOffset + zip64EndOfCentralDirLength <= _data.length,
+ "corrupted directory");
i = to!uint(eocdOffset);
- if (_data[i .. i + 4] != cast(ubyte[])"PK\x06\x06")
- throw new ZipException("invalid Zip EOCD64 signature");
+ enforce!ZipException(_data[i .. i + 4] == zip64EndOfCentralDirSignature,
+ "invalid Zip EOCD64 signature");
ulong eocd64Size = getUlong(i + 4);
- if (eocd64Size + i - 12 > data.length)
- throw new ZipException("invalid Zip EOCD64 size");
+ enforce!ZipException(eocd64Size + i - 12 <= data.length,
+ "invalid Zip EOCD64 size");
- _diskNumber = getUint(i + 16);
- _diskStartDir = getUint(i + 20);
+ // zip64 end of central dir record
+ removeSegment(i, cast(uint) (i + 12 + eocd64Size));
ulong numEntriesUlong = getUlong(i + 24);
ulong totalEntriesUlong = getUlong(i + 32);
ulong directorySizeUlong = getUlong(i + 40);
ulong directoryOffsetUlong = getUlong(i + 48);
- if (numEntriesUlong > uint.max)
- throw new ZipException("supposedly more than 4294967296 files in archive");
+ enforce!ZipException(numEntriesUlong <= uint.max,
+ "supposedly more than 4294967296 files in archive");
- if (numEntriesUlong != totalEntriesUlong)
- throw new ZipException("multiple disk zips not supported");
+ enforce!ZipException(numEntriesUlong == totalEntriesUlong,
+ "multiple disk zips not supported");
- if (directorySizeUlong > i || directoryOffsetUlong > i
- || directorySizeUlong + directoryOffsetUlong > i)
- throw new ZipException("corrupted directory");
+ enforce!ZipException(directorySizeUlong <= i && directoryOffsetUlong <= i
+ && directorySizeUlong + directoryOffsetUlong <= i,
+ "corrupted directory");
- _numEntries = to!uint(numEntriesUlong);
- _totalEntries = to!uint(totalEntriesUlong);
+ directoryCount = to!uint(totalEntriesUlong);
directorySize = to!uint(directorySizeUlong);
directoryOffset = to!uint(directoryOffsetUlong);
}
else
{
- // Read end record data
- _diskNumber = getUshort(i + 4);
- _diskStartDir = getUshort(i + 6);
-
- _numEntries = getUshort(i + 8);
- _totalEntries = getUshort(i + 10);
-
- if (numEntries != totalEntries)
- throw new ZipException("multiple disk zips not supported");
-
- directorySize = getUint(i + 12);
- directoryOffset = getUint(i + 16);
-
- if (directoryOffset + directorySize > i)
- throw new ZipException("corrupted directory");
+ // Read end record data
+ directoryCount = getUshort(i + 10);
+ directorySize = getUint(i + 12);
+ directoryOffset = getUint(i + 16);
}
i = directoryOffset;
- for (int n = 0; n < numEntries; n++)
+ for (int n = 0; n < directoryCount; n++)
{
/* The format of an entry is:
* 'PK' 1, 2
@@ -677,8 +905,8 @@ final class ZipArchive
uint extralen;
uint commentlen;
- if (_data[i .. i + 4] != cast(ubyte[])"PK\x01\x02")
- throw new ZipException("invalid directory entry 1");
+ enforce!ZipException(_data[i .. i + 4] == centralFileHeaderSignature,
+ "wrong central file header signature found");
ArchiveMember de = new ArchiveMember();
de._index = n;
de._madeVersion = getUshort(i + 4);
@@ -692,14 +920,17 @@ final class ZipArchive
namelen = getUshort(i + 28);
extralen = getUshort(i + 30);
commentlen = getUshort(i + 32);
- de._diskNumber = getUshort(i + 34);
de.internalAttributes = getUshort(i + 36);
de._externalAttributes = getUint(i + 38);
de.offset = getUint(i + 42);
- i += 46;
- if (i + namelen + extralen + commentlen > directoryOffset + directorySize)
- throw new ZipException("invalid directory entry 2");
+ // central file header
+ removeSegment(i, i + centralFileHeaderLength + namelen + extralen + commentlen);
+
+ i += centralFileHeaderLength;
+
+ enforce!ZipException(i + namelen + extralen + commentlen <= directoryOffset + directorySize,
+ "invalid field lengths in file header found");
de.name = cast(string)(_data[i .. i + namelen]);
i += namelen;
@@ -708,31 +939,288 @@ final class ZipArchive
de.comment = cast(string)(_data[i .. i + commentlen]);
i += commentlen;
- immutable uint dataOffset = de.offset + 30 + namelen + extralen;
- if (dataOffset + de.compressedSize > endrecOffset)
- throw new ZipException("Invalid directory entry offset or size.");
+ auto localFileHeaderNamelen = getUshort(de.offset + 26);
+ auto localFileHeaderExtralen = getUshort(de.offset + 28);
+
+ // file data
+ removeSegment(de.offset, de.offset + localFileHeaderLength + localFileHeaderNamelen
+ + localFileHeaderExtralen + de._compressedSize);
+
+ immutable uint dataOffset = de.offset + localFileHeaderLength
+ + localFileHeaderNamelen + localFileHeaderExtralen;
de._compressedData = _data[dataOffset .. dataOffset + de.compressedSize];
_directory[de.name] = de;
+ }
+
+ enforce!ZipException(i == directoryOffset + directorySize, "invalid directory entry 3");
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // contains wrong directorySize (extra byte 0xff)
+ auto file =
+ "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~
+ "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~
+ "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~
+ "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~
+ "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~
+ "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~
+ "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~
+ "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~
+ "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\xff\x50\x4b\x05"~
+ "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4b\x00\x00\x00\x43\x00\x00"~
+ "\x00\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // wrong eocdOffset
+ auto file =
+ "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~
+ "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~
+ "\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // wrong signature of zip64 end of central directory
+ auto file =
+ "\x50\x4b\x06\x07\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~
+ "\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // wrong size of zip64 end of central directory
+ auto file =
+ "\x50\x4b\x06\x06\xff\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~
+ "\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // too many entries in zip64 end of central directory
+ auto file =
+ "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~
+ "\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // zip64: numEntries and totalEntries differ
+ auto file =
+ "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~
+ "\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // zip64: directorySize too large
+ auto file =
+ "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~
+ "\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+
+ // zip64: directoryOffset too large
+ file =
+ "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\xff\xff\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~
+ "\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+
+ // zip64: directorySize + directoryOffset too large
+ // we need to add a useless byte at the beginning to avoid that one of the other two checks allready fires
+ file =
+ "\x00\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"~
+ "\x01\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~
+ "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~
+ "\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // wrong central file header signature
+ auto file =
+ "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~
+ "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~
+ "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~
+ "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~
+ "\x6c\x6c\x6f\x50\x4b\x01\x03\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~
+ "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~
+ "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~
+ "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~
+ "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~
+ "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~
+ "\x00\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // invalid field lengths in file header
+ auto file =
+ "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~
+ "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~
+ "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~
+ "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~
+ "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~
+ "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~
+ "\x00\x18\x00\x01\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~
+ "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~
+ "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\xff\x50\x4b\x05"~
+ "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~
+ "\x00\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+ }
+
+ private uint findEndOfCentralDirRecord()
+ {
+ // end of central dir record can be followed by a comment of up to 2^^16-1 bytes
+ // therefore we have to scan 2^^16 positions
+
+ uint endrecOffset = to!uint(data.length);
+ foreach (i; 0 .. 2 ^^ 16)
+ {
+ if (endOfCentralDirLength + i > data.length) break;
+ uint start = to!uint(data.length) - endOfCentralDirLength - i;
+
+ if (data[start .. start + 4] != endOfCentralDirSignature) continue;
+
+ auto numberOfThisDisc = getUshort(start + 4);
+ if (numberOfThisDisc != 0) continue; // no support for multiple volumes yet
+
+ auto numberOfStartOfCentralDirectory = getUshort(start + 6);
+ if (numberOfStartOfCentralDirectory != 0) continue; // dito
+
+ if (numberOfThisDisc < numberOfStartOfCentralDirectory) continue;
+
+ uint k = start - zip64EndOfCentralDirLocatorLength;
+ auto maybeZip64 = k < start && _data[k .. k + 4] == zip64EndOfCentralDirLocatorSignature;
+
+ auto totalNumberOfEntriesOnThisDisk = getUshort(start + 8);
+ auto totalNumberOfEntriesInCentralDir = getUshort(start + 10);
+
+ if (totalNumberOfEntriesOnThisDisk > totalNumberOfEntriesInCentralDir &&
+ (!maybeZip64 || totalNumberOfEntriesOnThisDisk < 0xffff)) continue;
+
+ auto sizeOfCentralDirectory = getUint(start + 12);
+ if (sizeOfCentralDirectory > start &&
+ (!maybeZip64 || sizeOfCentralDirectory < 0xffff)) continue;
+ auto offsetOfCentralDirectory = getUint(start + 16);
+ if (offsetOfCentralDirectory > start - sizeOfCentralDirectory &&
+ (!maybeZip64 || offsetOfCentralDirectory < 0xffff)) continue;
+
+ auto zipfileCommentLength = getUshort(start + 20);
+ if (start + zipfileCommentLength + endOfCentralDirLength != data.length) continue;
+
+ enforce!ZipException(endrecOffset == to!uint(data.length),
+ "found more than one valid 'end of central dir record'");
+
+ endrecOffset = start;
}
- if (i != directoryOffset + directorySize)
- throw new ZipException("invalid directory entry 3");
+
+ enforce!ZipException(endrecOffset != to!uint(data.length),
+ "found no valid 'end of central dir record'");
+
+ return endrecOffset;
}
- /*****
- * Decompress the contents of archive member de and return the expanded
- * data.
+ /**
+ * Decompress the contents of a member.
*
* Fills in properties extractVersion, flags, compressionMethod, time,
* crc32, compressedSize, expandedSize, expandedData[], name[], extra[].
+ *
+ * Params:
+ * de = Member to be decompressed.
+ *
+ * Returns: The expanded data.
+ *
+ * Throws: ZipException when the entry is invalid or the compression method is not supported.
*/
ubyte[] expand(ArchiveMember de)
- { uint namelen;
+ {
+ import std.string : representation;
+
+ uint namelen;
uint extralen;
- if (_data[de.offset .. de.offset + 4] != cast(ubyte[])"PK\x03\x04")
- throw new ZipException("invalid directory entry 4");
+ enforce!ZipException(_data[de.offset .. de.offset + 4] == localFileHeaderSignature,
+ "wrong local file header signature found");
// These values should match what is in the main zip archive directory
de._extractVersion = getUshort(de.offset + 4);
@@ -753,16 +1241,7 @@ final class ZipArchive
printf("\t\textralen = %d\n", extralen);
}
- if (de.flags & 1)
- throw new ZipException("encryption not supported");
-
- int i;
- i = de.offset + 30 + namelen + extralen;
- if (i + de.compressedSize > endrecOffset)
- throw new ZipException("invalid directory entry 5");
-
- de._compressedData = _data[i .. i + de.compressedSize];
- debug(print) arrayPrint(de.compressedData);
+ enforce!ZipException((de.flags & 1) == 0, "encryption not supported");
switch (de.compressionMethod)
{
@@ -783,40 +1262,183 @@ final class ZipArchive
}
}
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // check for correct local file header signature
+ auto file =
+ "\x50\x4b\x04\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~
+ "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~
+ "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~
+ "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~
+ "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~
+ "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~
+ "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~
+ "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~
+ "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~
+ "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~
+ "\x00\x00\x00";
+
+ auto za = new ZipArchive(cast(void[]) file);
+
+ assertThrown!ZipException(za.expand(za._directory["file"]));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // check for encryption flag
+ auto file =
+ "\x50\x4b\x03\x04\x0a\x00\x01\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~
+ "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~
+ "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~
+ "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~
+ "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~
+ "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~
+ "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~
+ "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~
+ "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~
+ "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~
+ "\x00\x00\x00";
+
+ auto za = new ZipArchive(cast(void[]) file);
+
+ assertThrown!ZipException(za.expand(za._directory["file"]));
+ }
+
+ @system unittest
+ {
+ import std.exception : assertThrown;
+
+ // check for invalid compression method
+ auto file =
+ "\x50\x4b\x03\x04\x0a\x00\x00\x00\x03\x00\x8f\x72\x4a\x4f\x86\xa6"~
+ "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~
+ "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~
+ "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~
+ "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~
+ "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~
+ "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~
+ "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~
+ "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~
+ "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~
+ "\x00\x00\x00";
+
+ auto za = new ZipArchive(cast(void[]) file);
+
+ assertThrown!ZipException(za.expand(za._directory["file"]));
+ }
+
/* ============ Utility =================== */
- @safe ushort getUshort(int i)
+ @safe @nogc pure nothrow ushort getUshort(uint i)
{
ubyte[2] result = data[i .. i + 2];
return littleEndianToNative!ushort(result);
}
- @safe uint getUint(int i)
+ @safe @nogc pure nothrow uint getUint(uint i)
{
ubyte[4] result = data[i .. i + 4];
return littleEndianToNative!uint(result);
}
- @safe ulong getUlong(int i)
+ @safe @nogc pure nothrow ulong getUlong(uint i)
{
ubyte[8] result = data[i .. i + 8];
return littleEndianToNative!ulong(result);
}
- @safe void putUshort(int i, ushort us)
+ @safe @nogc pure nothrow void putUshort(uint i, ushort us)
{
data[i .. i + 2] = nativeToLittleEndian(us);
}
- @safe void putUint(int i, uint ui)
+ @safe @nogc pure nothrow void putUint(uint i, uint ui)
{
data[i .. i + 4] = nativeToLittleEndian(ui);
}
- @safe void putUlong(int i, ulong ul)
+ @safe @nogc pure nothrow void putUlong(uint i, ulong ul)
{
data[i .. i + 8] = nativeToLittleEndian(ul);
}
+
+ /* ============== for detecting overlaps =============== */
+
+private:
+
+ // defines a segment of the zip file, including start, excluding end
+ struct Segment
+ {
+ uint start;
+ uint end;
+ }
+
+ // removes Segment start .. end from _segs
+ // throws zipException if start .. end is not completely available in _segs;
+ void removeSegment(uint start, uint end) pure @safe
+ in (start < end, "segment invalid")
+ {
+ auto found = false;
+ size_t pos;
+ foreach (i,seg;_segs)
+ if (seg.start <= start && seg.end >= end
+ && (!found || seg.start > _segs[pos].start))
+ {
+ found = true;
+ pos = i;
+ }
+
+ enforce!ZipException(found, "overlapping data detected");
+
+ if (start>_segs[pos].start)
+ _segs ~= Segment(_segs[pos].start, start);
+ if (end<_segs[pos].end)
+ _segs ~= Segment(end, _segs[pos].end);
+ _segs = _segs[0 .. pos] ~ _segs[pos + 1 .. $];
+ }
+
+ pure @safe unittest
+ {
+ with (new ZipArchive())
+ {
+ _segs = [Segment(0,100)];
+ removeSegment(10,20);
+ assert(_segs == [Segment(0,10),Segment(20,100)]);
+
+ _segs = [Segment(0,100)];
+ removeSegment(0,20);
+ assert(_segs == [Segment(20,100)]);
+
+ _segs = [Segment(0,100)];
+ removeSegment(10,100);
+ assert(_segs == [Segment(0,10)]);
+
+ _segs = [Segment(0,100), Segment(200,300), Segment(400,500)];
+ removeSegment(220,230);
+ assert(_segs == [Segment(0,100),Segment(400,500),Segment(200,220),Segment(230,300)]);
+
+ _segs = [Segment(200,300), Segment(0,100), Segment(400,500)];
+ removeSegment(20,30);
+ assert(_segs == [Segment(200,300),Segment(400,500),Segment(0,20),Segment(30,100)]);
+
+ import std.exception : assertThrown;
+
+ _segs = [Segment(0,100), Segment(200,300), Segment(400,500)];
+ assertThrown(removeSegment(120,230));
+
+ _segs = [Segment(0,100), Segment(200,300), Segment(400,500)];
+ removeSegment(0,100);
+ assertThrown(removeSegment(0,100));
+
+ _segs = [Segment(0,100)];
+ removeSegment(0,100);
+ assertThrown(removeSegment(0,100));
+ }
+ }
}
debug(print)
@@ -963,10 +1585,135 @@ the quick brown fox jumps over the lazy dog\r
assert(amAfter.time == am.time);
}
-// Non-Android Posix-only, because we can't rely on the unzip command being
-// available on Android or Windows
-version (Android) {} else
-version (Posix) @system unittest
+@system unittest
+{
+ // invalid format of end of central directory entry
+ import std.exception : assertThrown;
+ assertThrown!ZipException(new ZipArchive(cast(void[]) "\x50\x4B\x05\x06aaaaaaaaaaaaaaaaaaaa"));
+}
+
+@system unittest
+{
+ // minimum (empty) archive should pass
+ auto za = new ZipArchive(cast(void[]) "\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
+ assert(za.directory.length == 0);
+
+ // one byte too short or too long should not pass
+ import std.exception : assertThrown;
+ assertThrown!ZipException(new ZipArchive(cast(void[]) "\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"));
+ assertThrown!ZipException(new ZipArchive(cast(void[]) "\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"));
+}
+
+@system unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=20239
+ // chameleon file, containing two valid end of central directory entries
+ auto file =
+ "\x50\x4B\x03\x04\x0A\x00\x00\x00\x00\x00\x89\x36\x39\x4F\x04\x6A\xB3\xA3\x01\x00"~
+ "\x00\x00\x01\x00\x00\x00\x0D\x00\x1C\x00\x62\x65\x73\x74\x5F\x6C\x61\x6E\x67\x75"~
+ "\x61\x67\x65\x55\x54\x09\x00\x03\x82\xF2\x8A\x5D\x82\xF2\x8A\x5D\x75\x78\x0B\x00"~
+ "\x01\x04\xEB\x03\x00\x00\x04\xEB\x03\x00\x00\x44\x50\x4B\x01\x02\x1E\x03\x0A\x00"~
+ "\x00\x00\x00\x00\x89\x36\x39\x4F\x04\x6A\xB3\xA3\x01\x00\x00\x00\x01\x00\x00\x00"~
+ "\x0D\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xB0\x81\x00\x00\x00\x00\x62\x65"~
+ "\x73\x74\x5F\x6C\x61\x6E\x67\x75\x61\x67\x65\x55\x54\x05\x00\x03\x82\xF2\x8A\x5D"~
+ "\x75\x78\x0B\x00\x01\x04\xEB\x03\x00\x00\x04\xEB\x03\x00\x00\x50\x4B\x05\x06\x00"~
+ "\x00\x00\x00\x01\x00\x01\x00\x53\x00\x00\x00\x48\x00\x00\x00\xB7\x00\x50\x4B\x03"~
+ "\x04\x0A\x00\x00\x00\x00\x00\x94\x36\x39\x4F\xD7\xCB\x3B\x55\x07\x00\x00\x00\x07"~
+ "\x00\x00\x00\x0D\x00\x1C\x00\x62\x65\x73\x74\x5F\x6C\x61\x6E\x67\x75\x61\x67\x65"~
+ "\x55\x54\x09\x00\x03\x97\xF2\x8A\x5D\x8C\xF2\x8A\x5D\x75\x78\x0B\x00\x01\x04\xEB"~
+ "\x03\x00\x00\x04\xEB\x03\x00\x00\x46\x4F\x52\x54\x52\x41\x4E\x50\x4B\x01\x02\x1E"~
+ "\x03\x0A\x00\x00\x00\x00\x00\x94\x36\x39\x4F\xD7\xCB\x3B\x55\x07\x00\x00\x00\x07"~
+ "\x00\x00\x00\x0D\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xB0\x81\xB1\x00\x00"~
+ "\x00\x62\x65\x73\x74\x5F\x6C\x61\x6E\x67\x75\x61\x67\x65\x55\x54\x05\x00\x03\x97"~
+ "\xF2\x8A\x5D\x75\x78\x0B\x00\x01\x04\xEB\x03\x00\x00\x04\xEB\x03\x00\x00\x50\x4B"~
+ "\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x53\x00\x00\x00\xFF\x00\x00\x00\x00\x00";
+
+ import std.exception : assertThrown;
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+}
+
+@system unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=20287
+ // check for correct compressed data
+ auto file =
+ "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~
+ "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~
+ "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~
+ "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~
+ "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~
+ "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~
+ "\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~
+ "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~
+ "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~
+ "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~
+ "\x00\x00\x00";
+
+ auto za = new ZipArchive(cast(void[]) file);
+ assert(za.directory["file"].compressedData == [104, 101, 108, 108, 111]);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20027
+@system unittest
+{
+ // central file header overlaps end of central directory
+ auto file =
+ // lfh
+ "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~
+ "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1c\x00\x66\x69"~
+ "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~
+ "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~
+ "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~
+ "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~
+ "\x00\x18\x00\x04\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~
+ "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~
+ "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~
+ "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~
+ "\x00\x00\x00";
+
+ import std.exception : assertThrown;
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+
+ // local file header and file data overlap second local file header and file data
+ file =
+ "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x8f\x72\x4a\x4f\x86\xa6"~
+ "\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04\x00\x1e\x00\x66\x69"~
+ "\x6c\x65\x55\x54\x09\x00\x03\x0d\x22\x9f\x5d\x12\x22\x9f\x5d\x75"~
+ "\x78\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x68\x65"~
+ "\x6c\x6c\x6f\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\x8f"~
+ "\x72\x4a\x4f\x86\xa6\x10\x36\x05\x00\x00\x00\x05\x00\x00\x00\x04"~
+ "\x00\x18\x00\x04\x00\x00\x00\x01\x00\x00\x00\xb0\x81\x00\x00\x00"~
+ "\x00\x66\x69\x6c\x65\x55\x54\x05\x00\x03\x0d\x22\x9f\x5d\x75\x78"~
+ "\x0b\x00\x01\x04\xf0\x03\x00\x00\x04\xf0\x03\x00\x00\x50\x4b\x05"~
+ "\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4a\x00\x00\x00\x43\x00\x00"~
+ "\x00\x00\x00";
+
+ assertThrown!ZipException(new ZipArchive(cast(void[]) file));
+}
+
+@system unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=20295
+ // zip64 with 0xff bytes in end of central dir record do not work
+ // minimum (empty zip64) archive should pass
+ auto file =
+ "\x50\x4b\x06\x06\x2c\x00\x00\x00\x00\x00\x00\x00\x1e\x03\x2d\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4b\x06\x07\x00\x00\x00\x00"~
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x4B\x05\x06"~
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"~
+ "\x00\x00";
+
+ auto za = new ZipArchive(cast(void[]) file);
+ assert(za.directory.length == 0);
+}
+
+version (HasUnzip)
+@system unittest
{
import std.datetime, std.file, std.format, std.path, std.process, std.stdio;
diff --git a/libphobos/src/std/zlib.d b/libphobos/src/std/zlib.d
index e6cce240fd5..beb280f891d 100644
--- a/libphobos/src/std/zlib.d
+++ b/libphobos/src/std/zlib.d
@@ -1,7 +1,7 @@
// Written in the D programming language.
/**
- * Compress/decompress data using the $(HTTP www._zlib.net, _zlib library).
+ * Compress/decompress data using the $(HTTP www.zlib.net, zlib library).
*
* Examples:
*
@@ -43,12 +43,12 @@
* References:
* $(HTTP en.wikipedia.org/wiki/Zlib, Wikipedia)
*
- * Copyright: Copyright Digital Mars 2000 - 2011.
+ * Copyright: Copyright The D Language Foundation 2000 - 2011.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright)
- * Source: $(PHOBOSSRC std/_zlib.d)
+ * Source: $(PHOBOSSRC std/zlib.d)
*/
-/* Copyright Digital Mars 2000 - 2011.
+/* Copyright The D Language Foundation 2000 - 2011.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
@@ -75,9 +75,9 @@ enum
class ZlibException : Exception
{
- this(int errnum)
- { string msg;
-
+ private static string getmsg(int errnum) nothrow @nogc pure @safe
+ {
+ string msg;
switch (errnum)
{
case Z_STREAM_END: msg = "stream end"; break;
@@ -90,7 +90,12 @@ class ZlibException : Exception
case Z_VERSION_ERROR: msg = "version error"; break;
default: msg = "unknown error"; break;
}
- super(msg);
+ return msg;
+ }
+
+ this(int errnum)
+ {
+ super(getmsg(errnum));
}
}
@@ -104,7 +109,7 @@ class ZlibException : Exception
* buf = buffer containing input data
*
* Returns:
- * A $(D uint) checksum for the provided input data and starting checksum
+ * A `uint` checksum for the provided input data and starting checksum
*
* See_Also:
* $(LINK http://en.wikipedia.org/wiki/Adler-32)
@@ -147,7 +152,7 @@ uint adler32(uint adler, const(void)[] buf)
* buf = buffer containing input data
*
* Returns:
- * A $(D uint) checksum for the provided input data and starting checksum
+ * A `uint` checksum for the provided input data and starting checksum
*
* See_Also:
* $(LINK http://en.wikipedia.org/wiki/Cyclic_redundancy_check)
@@ -191,13 +196,14 @@ uint crc32(uint crc, const(void)[] buf)
ubyte[] compress(const(void)[] srcbuf, int level)
in
{
- assert(-1 <= level && level <= 9);
+ assert(-1 <= level && level <= 9, "Compression level needs to be within [-1, 9].");
}
-body
+do
{
import core.memory : GC;
+ import std.array : uninitializedArray;
auto destlen = srcbuf.length + ((srcbuf.length + 1023) / 1024) + 12;
- auto destbuf = new ubyte[destlen];
+ auto destbuf = uninitializedArray!(ubyte[])(destlen);
auto err = etc.c.zlib.compress2(destbuf.ptr, &destlen, cast(ubyte *) srcbuf.ptr, srcbuf.length, level);
if (err)
{
@@ -276,7 +282,7 @@ void[] uncompress(const(void)[] srcbuf, size_t destlen = 0u, int winbits = 15)
throw new ZlibException(err);
}
}
- assert(0);
+ assert(0, "Unreachable code");
}
@system unittest
@@ -370,9 +376,9 @@ class Compress
this(int level, HeaderFormat header = HeaderFormat.deflate)
in
{
- assert(1 <= level && level <= 9);
+ assert(1 <= level && level <= 9, "Legal compression level are in [1, 9].");
}
- body
+ do
{
this.level = level;
this.gzip = header == HeaderFormat.gzip;
@@ -406,6 +412,7 @@ class Compress
const(void)[] compress(const(void)[] buf)
{
import core.memory : GC;
+ import std.array : uninitializedArray;
int err;
ubyte[] destbuf;
@@ -420,7 +427,7 @@ class Compress
inited = 1;
}
- destbuf = new ubyte[zs.avail_in + buf.length];
+ destbuf = uninitializedArray!(ubyte[])(zs.avail_in + buf.length);
zs.next_out = destbuf.ptr;
zs.avail_out = to!uint(destbuf.length);
@@ -461,9 +468,10 @@ class Compress
void[] flush(int mode = Z_FINISH)
in
{
- assert(mode == Z_FINISH || mode == Z_SYNC_FLUSH || mode == Z_FULL_FLUSH);
+ assert(mode == Z_FINISH || mode == Z_SYNC_FLUSH || mode == Z_FULL_FLUSH,
+ "Mode must be either Z_FINISH, Z_SYNC_FLUSH or Z_FULL_FLUSH.");
}
- body
+ do
{
import core.memory : GC;
ubyte[] destbuf;
@@ -523,6 +531,7 @@ class UnCompress
z_stream zs;
int inited;
int done;
+ bool inputEnded;
size_t destbufsize;
HeaderFormat format;
@@ -571,16 +580,16 @@ class UnCompress
const(void)[] uncompress(const(void)[] buf)
in
{
- assert(!done);
+ assert(!done, "Buffer has been flushed.");
}
- body
+ do
{
+ if (inputEnded || !buf.length)
+ return null;
+
import core.memory : GC;
+ import std.array : uninitializedArray;
int err;
- ubyte[] destbuf;
-
- if (buf.length == 0)
- return null;
if (!inited)
{
@@ -598,26 +607,153 @@ class UnCompress
if (!destbufsize)
destbufsize = to!uint(buf.length) * 2;
- destbuf = new ubyte[zs.avail_in * 2 + destbufsize];
- zs.next_out = destbuf.ptr;
- zs.avail_out = to!uint(destbuf.length);
-
- if (zs.avail_in)
- buf = zs.next_in[0 .. zs.avail_in] ~ cast(ubyte[]) buf;
+ auto destbuf = uninitializedArray!(ubyte[])(destbufsize);
+ size_t destFill;
zs.next_in = cast(ubyte*) buf.ptr;
zs.avail_in = to!uint(buf.length);
- err = inflate(&zs, Z_NO_FLUSH);
- if (err != Z_STREAM_END && err != Z_OK)
+ while (true)
{
- GC.free(destbuf.ptr);
- error(err);
+ auto oldAvailIn = zs.avail_in;
+
+ zs.next_out = destbuf[destFill .. $].ptr;
+ zs.avail_out = to!uint(destbuf.length - destFill);
+
+ err = inflate(&zs, Z_NO_FLUSH);
+ if (err == Z_STREAM_END)
+ {
+ inputEnded = true;
+ break;
+ }
+ else if (err != Z_OK)
+ {
+ GC.free(destbuf.ptr);
+ error(err);
+ }
+ else if (!zs.avail_in)
+ break;
+
+ /*
+ According to the zlib manual inflate() stops when either there's
+ no more data to uncompress or the output buffer is full
+ So at this point, the output buffer is too full
+ */
+
+ destFill = destbuf.length;
+
+ if (destbuf.capacity)
+ {
+ if (destbuf.length < destbuf.capacity)
+ destbuf.length = destbuf.capacity;
+ else
+ {
+ auto newLength = GC.extend(destbuf.ptr, destbufsize, destbufsize);
+
+ if (newLength && destbuf.length < destbuf.capacity)
+ destbuf.length = destbuf.capacity;
+ else
+ destbuf.length += destbufsize;
+ }
+ }
+ else
+ destbuf.length += destbufsize;
}
+
destbuf.length = destbuf.length - zs.avail_out;
return destbuf;
}
+ // Test for https://issues.dlang.org/show_bug.cgi?id=3191 and
+ // https://issues.dlang.org/show_bug.cgi?id=9505
+ @system unittest
+ {
+ import std.algorithm.comparison;
+ import std.array;
+ import std.file;
+ import std.zlib;
+
+ // Data that can be easily compressed
+ ubyte[1024] originalData;
+
+ // This should yield a compression ratio of at least 1/2
+ auto compressedData = compress(originalData, 9);
+ assert(compressedData.length < originalData.length / 2,
+ "The compression ratio is too low to accurately test this situation");
+
+ auto chunkSize = compressedData.length / 4;
+ assert(chunkSize < compressedData.length,
+ "The length of the compressed data is too small to accurately test this situation");
+
+ auto decompressor = new UnCompress();
+ ubyte[originalData.length] uncompressedData;
+ ubyte[] reusedBuf;
+ int progress;
+
+ reusedBuf.length = chunkSize;
+
+ for (int i = 0; i < compressedData.length; i += chunkSize)
+ {
+ auto len = min(chunkSize, compressedData.length - i);
+ // simulate reading from a stream in small chunks
+ reusedBuf[0 .. len] = compressedData[i .. i + len];
+
+ // decompress using same input buffer
+ auto chunk = decompressor.uncompress(reusedBuf);
+ assert(progress + chunk.length <= originalData.length,
+ "The uncompressed result is bigger than the original data");
+
+ uncompressedData[progress .. progress + chunk.length] = cast(const ubyte[]) chunk[];
+ progress += chunk.length;
+ }
+
+ auto chunk = decompressor.flush();
+ assert(progress + chunk.length <= originalData.length,
+ "The uncompressed result is bigger than the original data");
+
+ uncompressedData[progress .. progress + chunk.length] = cast(const ubyte[]) chunk[];
+ progress += chunk.length;
+
+ assert(progress == originalData.length,
+ "The uncompressed and the original data sizes differ");
+ assert(originalData[] == uncompressedData[],
+ "The uncompressed and the original data differ");
+ }
+
+ @system unittest
+ {
+ ubyte[1024] invalidData;
+ auto decompressor = new UnCompress();
+
+ try
+ {
+ auto uncompressedData = decompressor.uncompress(invalidData);
+ }
+ catch (ZlibException e)
+ {
+ assert(e.msg == "data error");
+ return;
+ }
+
+ assert(false, "Corrupted data didn't result in an error");
+ }
+
+ @system unittest
+ {
+ ubyte[2014] originalData = void;
+ auto compressedData = compress(originalData, 9);
+
+ auto decompressor = new UnCompress();
+ auto uncompressedData = decompressor.uncompress(compressedData ~ cast(ubyte[]) "whatever");
+
+ assert(originalData.length == uncompressedData.length,
+ "The uncompressed and the original data sizes differ");
+ assert(originalData[] == uncompressedData[],
+ "The uncompressed and the original data differ");
+ assert(!decompressor.uncompress("whatever").length,
+ "Compression continued after the end");
+ }
+
/**
* Decompress and return any remaining data.
* The returned data should be appended to that returned by uncompress().
@@ -626,49 +762,40 @@ class UnCompress
void[] flush()
in
{
- assert(!done);
+ assert(!done, "Buffer has been flushed before.");
}
out
{
- assert(done);
+ assert(done, "Flushing failed.");
}
- body
+ do
{
- import core.memory : GC;
- ubyte[] extra;
- ubyte[] destbuf;
- int err;
-
done = 1;
- if (!inited)
- return null;
+ return null;
+ }
- L1:
- destbuf = new ubyte[zs.avail_in * 2 + 100];
- zs.next_out = destbuf.ptr;
- zs.avail_out = to!uint(destbuf.length);
+ /// Returns true if all input data has been decompressed and no further data
+ /// can be decompressed (inflate() returned Z_STREAM_END)
+ @property bool empty() const
+ {
+ return inputEnded;
+ }
- err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH);
- if (err == Z_OK && zs.avail_out == 0)
- {
- extra ~= destbuf;
- goto L1;
- }
- if (err != Z_STREAM_END)
- {
- GC.free(destbuf.ptr);
- if (err == Z_OK)
- err = Z_BUF_ERROR;
- error(err);
- }
- destbuf = destbuf.ptr[0 .. zs.next_out - destbuf.ptr];
- err = etc.c.zlib.inflateEnd(&zs);
- inited = 0;
- if (err)
- error(err);
- if (extra.length)
- destbuf = extra ~ destbuf;
- return destbuf;
+ ///
+ @system unittest
+ {
+ // some random data
+ ubyte[1024] originalData = void;
+
+ // append garbage data (or don't, this works in both cases)
+ auto compressedData = cast(ubyte[]) compress(originalData) ~ cast(ubyte[]) "whatever";
+
+ auto decompressor = new UnCompress();
+ auto uncompressedData = decompressor.uncompress(compressedData);
+
+ assert(uncompressedData[] == originalData[],
+ "The uncompressed and the original data differ");
+ assert(decompressor.empty, "The UnCompressor reports not being done");
}
}
@@ -754,7 +881,8 @@ import std.stdio;
assert( output[] == input[] );
}
+// https://issues.dlang.org/show_bug.cgi?id=15457
@system unittest
{
- static assert(__traits(compiles, etc.c.zlib.gzclose(null))); // bugzilla 15457
+ static assert(__traits(compiles, etc.c.zlib.gzclose(null)));
}
diff --git a/libphobos/testsuite/lib/libphobos.exp b/libphobos/testsuite/lib/libphobos.exp
index 2af430a0e45..66e3e80105f 100644
--- a/libphobos/testsuite/lib/libphobos.exp
+++ b/libphobos/testsuite/lib/libphobos.exp
@@ -54,6 +54,10 @@ proc libphobos-dg-test { prog do_what extra_tool_flags } {
# Set up the compiler flags, based on what we're going to do.
switch $do_what {
+ "compile" {
+ set compile_type "assembly"
+ set output_file "[file rootname [file tail $prog]].s"
+ }
"run" {
set compile_type "executable"
# FIXME: "./" is to cope with "." not being in $PATH.
@@ -89,8 +93,52 @@ proc libphobos-dg-test { prog do_what extra_tool_flags } {
return [list $comp_output $output_file]
}
+# Override the DejaGnu dg-test in order to clear flags after a test, as
+# is done for compiler tests in gcc-dg.exp.
+
+if { [info procs saved-dg-test] == [list] } {
+ rename dg-test saved-dg-test
+
+ proc dg-test { args } {
+ global additional_prunes
+ global errorInfo
+ global testname_with_flags
+ global shouldfail
+
+ if { [ catch { eval saved-dg-test $args } errmsg ] } {
+ set saved_info $errorInfo
+ set additional_prunes ""
+ set shouldfail 0
+ if [info exists testname_with_flags] {
+ unset testname_with_flags
+ }
+ unset_timeout_vars
+ error $errmsg $saved_info
+ }
+ set additional_prunes ""
+ set shouldfail 0
+ unset_timeout_vars
+ if [info exists testname_with_flags] {
+ unset testname_with_flags
+ }
+ }
+}
+
+# Prune messages from gdc that aren't useful.
+
+set additional_prunes ""
+
proc libphobos-dg-prune { system text } {
+ global additional_prunes
+
+ foreach p $additional_prunes {
+ if { [string length $p] > 0 } {
+ # Following regexp matches a complete line containing $p.
+ regsub -all "(^|\n)\[^\n\]*$p\[^\n\]*" $text "" text
+ }
+ }
+
# Ignore harmless warnings from Xcode.
regsub -all "(^|\n)\[^\n\]*ld: warning: could not create compact unwind for\[^\n\]*" $text "" text
@@ -281,6 +329,18 @@ proc libphobos_skipped_test_p { test } {
return "skipped test"
}
+# Prune any messages matching ARGS[1] (a regexp) from test output.
+proc dg-prune-output { args } {
+ global additional_prunes
+
+ if { [llength $args] != 2 } {
+ error "[lindex $args 1]: need one argument"
+ return
+ }
+
+ lappend additional_prunes [lindex $args 1]
+}
+
# Return true if the curl library is supported on the target.
proc check_effective_target_libcurl_available { } {
return [check_no_compiler_messages libcurl_available executable {
diff --git a/libphobos/testsuite/libphobos.aa/test_aa.d b/libphobos/testsuite/libphobos.aa/test_aa.d
index d6222b13175..11ad2f90ff1 100644
--- a/libphobos/testsuite/libphobos.aa/test_aa.d
+++ b/libphobos/testsuite/libphobos.aa/test_aa.d
@@ -30,6 +30,8 @@ void main()
issue15367();
issue16974();
issue18071();
+ issue20440();
+ issue21442();
testIterationWithConst();
testStructArrayKey();
miscTests1();
@@ -286,21 +288,20 @@ void testUpdate2()
assert(updated);
}
-void testByKey1()
+void testByKey1() @safe
{
- static assert(!__traits(compiles,
- () @safe {
- struct BadValue
- {
- int x;
- this(this) @safe { *(cast(ubyte*)(null) + 100000) = 5; } // not @safe
- alias x this;
- }
+ static struct BadValue
+ {
+ int x;
+ this(this) @system { *(cast(ubyte*)(null) + 100000) = 5; } // not @safe
+ alias x this;
+ }
- BadValue[int] aa;
- () @safe { auto x = aa.byKey.front; } ();
- }
- ));
+ BadValue[int] aa;
+
+ // FIXME: Should be @system because of the postblit
+ if (false)
+ auto x = aa.byKey.front;
}
void testByKey2() nothrow pure
@@ -690,6 +691,58 @@ void issue18071()
() @safe { assert(f.byKey.empty); }();
}
+/// Test that `require` works even with types whose opAssign
+/// doesn't return a reference to the receiver.
+/// https://issues.dlang.org/show_bug.cgi?id=20440
+void issue20440() @safe
+{
+ static struct S
+ {
+ int value;
+ auto opAssign(S s) {
+ this.value = s.value;
+ return this;
+ }
+ }
+ S[S] aa;
+ assert(aa.require(S(1), S(2)) == S(2));
+ assert(aa[S(1)] == S(2));
+}
+
+///
+void issue21442()
+{
+ import core.memory;
+
+ size_t[size_t] glob;
+
+ class Foo
+ {
+ size_t count;
+
+ this (size_t entries) @safe
+ {
+ this.count = entries;
+ foreach (idx; 0 .. entries)
+ glob[idx] = idx;
+ }
+
+ ~this () @safe
+ {
+ foreach (idx; 0 .. this.count)
+ glob.remove(idx);
+ }
+ }
+
+ void bar () @safe
+ {
+ Foo f = new Foo(16);
+ }
+
+ bar();
+ GC.collect(); // Needs to happen from a GC collection
+}
+
/// Verify iteration with const.
void testIterationWithConst()
{
diff --git a/libphobos/testsuite/libphobos.allocations/alloc_from_assert.d b/libphobos/testsuite/libphobos.allocations/alloc_from_assert.d
new file mode 100644
index 00000000000..a377cd9139d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.allocations/alloc_from_assert.d
@@ -0,0 +1,25 @@
+import core.exception;
+import core.memory;
+
+class FailFinalization
+{
+ int magic;
+
+ ~this () @nogc nothrow
+ {
+ try
+ assert(this.magic == 42);
+ catch (AssertError) {}
+ }
+}
+
+void foo ()
+{
+ auto dangling = new FailFinalization();
+}
+
+void main()
+{
+ foo();
+ GC.collect();
+}
diff --git a/libphobos/testsuite/libphobos.betterc/betterc.exp b/libphobos/testsuite/libphobos.betterc/betterc.exp
new file mode 100644
index 00000000000..e5e9b84829d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/betterc.exp
@@ -0,0 +1,27 @@
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Initialize dg.
+dg-init
+
+# Gather a list of all tests.
+set tests [lsort [find $srcdir/$subdir *.d]]
+
+# Main loop.
+dg-runtest $tests "-fno-druntime" $DEFAULT_DFLAGS
+
+# All done.
+dg-finish
diff --git a/libphobos/testsuite/libphobos.betterc/test18828.d b/libphobos/testsuite/libphobos.betterc/test18828.d
new file mode 100644
index 00000000000..db0530dd13c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test18828.d
@@ -0,0 +1,10 @@
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=18828
+
+struct S18828 { }
+
+extern(C) void main()
+{
+ S18828 s;
+ destroy(s);
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test19416.d b/libphobos/testsuite/libphobos.betterc/test19416.d
new file mode 100644
index 00000000000..aff93d3aa5f
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test19416.d
@@ -0,0 +1,14 @@
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19416
+
+import core.stdc.stdlib : malloc, free;
+import core.exception : onOutOfMemoryError;
+
+extern(C) void main()
+{
+ auto m = malloc(1);
+ if (!m)
+ onOutOfMemoryError();
+ else
+ free(m);
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test19421.d b/libphobos/testsuite/libphobos.betterc/test19421.d
new file mode 100644
index 00000000000..2427c5e9a0c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test19421.d
@@ -0,0 +1,13 @@
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19421
+
+import core.memory;
+
+extern(C) void main() @nogc nothrow pure
+{
+ auto p = pureMalloc(1);
+ p = pureRealloc(p, 2);
+ if (p) pureFree(p);
+ p = pureCalloc(1, 1);
+ if (p) pureFree(p);
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test19561.d b/libphobos/testsuite/libphobos.betterc/test19561.d
new file mode 100644
index 00000000000..96ecec51ae4
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test19561.d
@@ -0,0 +1,16 @@
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19561
+
+import core.memory;
+
+extern(C) void main() @nogc nothrow pure
+{
+ int[3] a, b;
+ a[] = 0;
+ a[] = b[];
+ //FIXME: Next line requires compiler change.
+ //a[] = 1; // error: undefined reference to '_memset32'
+ a[] += 1;
+ a[] += b[];
+ int[3] c = a[] + b[];
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test19924.d b/libphobos/testsuite/libphobos.betterc/test19924.d
new file mode 100644
index 00000000000..e9a93cad0ac
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test19924.d
@@ -0,0 +1,15 @@
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=19924
+
+import core.bitop;
+
+extern(C) void main()
+{
+ uint a = 0x01_23_45_67;
+ a = bswap(a);
+ assert(a == 0x67_45_23_01);
+
+ ulong b = 0x01_23_45_67_89_ab_cd_ef;
+ b = bswap(b);
+ assert(b == 0xef_cd_ab_89_67_45_23_01);
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test20088.d b/libphobos/testsuite/libphobos.betterc/test20088.d
new file mode 100644
index 00000000000..a809041c877
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test20088.d
@@ -0,0 +1,14 @@
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20088
+
+struct S {
+ int i;
+}
+
+extern(C) int main() @nogc nothrow pure
+{
+ S[2] s = [S(1),S(2)];
+ void[] v = cast(void[])s;
+ S[] p = cast(S[])v; // cast of void[] to S[] triggers __ArrayCast template function
+ return 0;
+}
diff --git a/libphobos/testsuite/libphobos.betterc/test20613.d b/libphobos/testsuite/libphobos.betterc/test20613.d
new file mode 100644
index 00000000000..b03e2d17b62
--- /dev/null
+++ b/libphobos/testsuite/libphobos.betterc/test20613.d
@@ -0,0 +1,18 @@
+/*******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=20613
+
+extern(C) int main() @nogc nothrow pure
+{
+ auto s = "F";
+ final switch(s)
+ {
+ case "A": break;
+ case "B": break;
+ case "C": break;
+ case "D": break;
+ case "E": break;
+ case "F": break;
+ case "G": break;
+ }
+ return 0;
+}
diff --git a/libphobos/testsuite/libphobos.config/config.exp b/libphobos/testsuite/libphobos.config/config.exp
new file mode 100644
index 00000000000..e8f4d943ff3
--- /dev/null
+++ b/libphobos/testsuite/libphobos.config/config.exp
@@ -0,0 +1,46 @@
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+load_lib libphobos-dg.exp
+
+set dg-output-text [list]
+
+# Test, arguments to pass to the test program, and return code.
+set config_test_list [list \
+ { test19433 "--DRT-dont-eat-me" 0 } \
+ { test20459 "foo bar -- --DRT-gcopts=profile:1" 0 } \
+]
+
+# Initialize dg.
+dg-init
+
+# Main loop.
+foreach config_test $config_test_list {
+ set test "$srcdir/$subdir/[lindex $config_test 0].d"
+
+ set libphobos_run_args "[lindex $config_test 1]"
+ set shouldfail [lindex $config_test 2]
+ set libphobos_test_name "[dg-trim-dirname $srcdir $test] $libphobos_run_args"
+
+ dg-runtest $test "" $DEFAULT_DFLAGS
+
+ set libphobos_test_name ""
+ set shouldfail 0
+ set libphobos_run_args ""
+}
+
+# All done.
+dg-finish
diff --git a/libphobos/testsuite/libphobos.config/test19433.d b/libphobos/testsuite/libphobos.config/test19433.d
new file mode 100644
index 00000000000..1c58145103e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.config/test19433.d
@@ -0,0 +1,7 @@
+extern(C) __gshared bool rt_cmdline_enabled = false;
+
+void main(string[] args)
+{
+ assert(args.length == 2);
+ assert(args[1] == "--DRT-dont-eat-me");
+}
diff --git a/libphobos/testsuite/libphobos.config/test20459.d b/libphobos/testsuite/libphobos.config/test20459.d
new file mode 100644
index 00000000000..248720d8f30
--- /dev/null
+++ b/libphobos/testsuite/libphobos.config/test20459.d
@@ -0,0 +1,5 @@
+void main (string[] args)
+{
+ assert(args.length == 5);
+ assert(args[1 .. $] == [ "foo", "bar", "--", "--DRT-gcopts=profile:1" ]);
+}
diff --git a/libphobos/testsuite/libphobos.druntime/druntime.exp b/libphobos/testsuite/libphobos.druntime/druntime.exp
index daedfd71ebb..5342d45386f 100644
--- a/libphobos/testsuite/libphobos.druntime/druntime.exp
+++ b/libphobos/testsuite/libphobos.druntime/druntime.exp
@@ -22,7 +22,7 @@ if { ![isnative] } {
# Gather a list of all tests.
set tests [lsort [filter_libphobos_unittests [find $srcdir/../libdruntime "*.d"]]]
-set version_flags ""
+set version_flags "-fversion=CoreUnittest"
if { [is-effective-target linux_pre_2639] } {
lappend version_flags "-fversion=Linux_Pre_2639"
diff --git a/libphobos/testsuite/libphobos.druntime_shared/druntime_shared.exp b/libphobos/testsuite/libphobos.druntime_shared/druntime_shared.exp
index 51f9c2cf1d6..67edab95cff 100644
--- a/libphobos/testsuite/libphobos.druntime_shared/druntime_shared.exp
+++ b/libphobos/testsuite/libphobos.druntime_shared/druntime_shared.exp
@@ -22,7 +22,7 @@ if { ![isnative] || ![is-effective-target shared] } {
# Gather a list of all tests.
set tests [lsort [filter_libphobos_unittests [find $srcdir/../libdruntime "*.d"]]]
-set version_flags ""
+set version_flags "-fversion=CoreUnittest -fversion=Shared"
if { [is-effective-target linux_pre_2639] } {
lappend version_flags "-fversion=Linux_Pre_2639"
diff --git a/libphobos/testsuite/libphobos.exceptions/assert_fail.d b/libphobos/testsuite/libphobos.exceptions/assert_fail.d
new file mode 100644
index 00000000000..79b3cb8139e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/assert_fail.d
@@ -0,0 +1,564 @@
+import core.stdc.stdio : fprintf, stderr;
+import core.internal.dassert : _d_assert_fail;
+
+void test(string comp = "==", A, B)(A a, B b, string msg, size_t line = __LINE__)
+{
+ test(_d_assert_fail!(A)(comp, a, b), msg, line);
+}
+
+void test(const string actual, const string expected, size_t line = __LINE__)
+{
+ import core.exception : AssertError;
+
+ if (actual != expected)
+ {
+ const msg = "Mismatch!\nExpected: <" ~ expected ~ ">\nActual: <" ~ actual ~ '>';
+ throw new AssertError(msg, __FILE__, line);
+ }
+}
+
+void testIntegers()
+{
+ test(1, 2, "1 != 2");
+ test(-10, 8, "-10 != 8");
+ test(byte.min, byte.max, "-128 != 127");
+ test(ubyte.min, ubyte.max, "0 != 255");
+ test(short.min, short.max, "-32768 != 32767");
+ test(ushort.min, ushort.max, "0 != 65535");
+ test(int.min, int.max, "-2147483648 != 2147483647");
+ test(uint.min, uint.max, "0 != 4294967295");
+ test(long.min, long.max, "-9223372036854775808 != 9223372036854775807");
+ test(ulong.min, ulong.max, "0 != 18446744073709551615");
+ test(shared(ulong).min, shared(ulong).max, "0 != 18446744073709551615");
+
+ int testFun() { return 1; }
+ test(testFun(), 2, "1 != 2");
+}
+
+void testIntegerComparisons()
+{
+ test!"!="(2, 2, "2 == 2");
+ test!"<"(2, 1, "2 >= 1");
+ test!"<="(2, 1, "2 > 1");
+ test!">"(1, 2, "1 <= 2");
+ test!">="(1, 2, "1 < 2");
+}
+
+void testFloatingPoint()
+{
+ if (__ctfe)
+ {
+ test(float.max, -float.max, "<float not supported> != <float not supported>");
+ test(double.max, -double.max, "<double not supported> != <double not supported>");
+ test(real(1), real(-1), "<real not supported> != <real not supported>");
+ }
+ else
+ {
+ test(1.5, 2.5, "1.5 != 2.5");
+ test(float.max, -float.max, "3.40282e+38 != -3.40282e+38");
+ test(double.max, -double.max, "1.79769e+308 != -1.79769e+308");
+ test(real(1), real(-1), "1 != -1");
+ }
+}
+
+void testPointers()
+{
+ static struct S
+ {
+ string toString() const { return "S(...)"; }
+ }
+
+ static if ((void*).sizeof == 4)
+ enum ptr = "0x12345670";
+ else
+ enum ptr = "0x123456789abcdef0";
+
+ int* p = cast(int*) mixin(ptr);
+ test(cast(S*) p, p, ptr ~ " != " ~ ptr);
+}
+
+void testStrings()
+{
+ test("foo", "bar", `"foo" != "bar"`);
+ test("", "bar", `"" != "bar"`);
+
+ char[] dlang = "dlang".dup;
+ const(char)[] rust = "rust";
+ test(dlang, rust, `"dlang" != "rust"`);
+
+ // https://issues.dlang.org/show_bug.cgi?id=20322
+ test("left"w, "right"w, `"left" != "right"`);
+ test("left"d, "right"d, `"left" != "right"`);
+
+ test('A', 'B', "'A' != 'B'");
+ test(wchar('â¤'), wchar('∑'), "'â¤' != '∑'");
+ test(dchar('â¤'), dchar('∑'), "'â¤' != '∑'");
+
+ // Detect invalid code points
+ test(char(255), 'B', "cast(char) 255 != 'B'");
+ test(wchar(0xD888), wchar('∑'), "cast(wchar) 55432 != '∑'");
+ test(dchar(0xDDDD), dchar('∑'), "cast(dchar) 56797 != '∑'");
+}
+
+void testToString()
+{
+ class Foo
+ {
+ this(string payload) {
+ this.payload = payload;
+ }
+
+ string payload;
+ override string toString() {
+ return "Foo(" ~ payload ~ ")";
+ }
+ }
+ test(new Foo("a"), new Foo("b"), "Foo(a) != Foo(b)");
+
+ scope f = cast(shared) new Foo("a");
+ if (!__ctfe) // Ref somehow get's lost in CTFE
+ test!"!="(f, f, "Foo(a) == Foo(a)");
+
+ // Verifiy that the const toString is selected if present
+ static struct Overloaded
+ {
+ string toString()
+ {
+ return "Mutable";
+ }
+
+ string toString() const
+ {
+ return "Const";
+ }
+ }
+
+ test!"!="(Overloaded(), Overloaded(), "Const == Const");
+
+ Foo fnull = null;
+ test!"!is"(fnull, fnull, "`null` is `null`");
+}
+
+
+void testArray()
+{
+ test([1], [0], "[1] != [0]");
+ test([1, 2, 3], [0], "[1, 2, 3] != [0]");
+
+ // test with long arrays
+ int[] arr;
+ foreach (i; 0 .. 100)
+ arr ~= i;
+ test(arr, [0], "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ...] != [0]");
+
+ // Ignore fake arrays
+ static struct S
+ {
+ int[2] arr;
+ int[] get() return { return arr[]; }
+ alias get this;
+ }
+
+ const a = S([1, 2]);
+ test(a, S([3, 4]), "S([1, 2]) != S([3, 4])");
+}
+
+void testStruct()
+{
+ struct S { int s; }
+ struct T { T[] t; }
+ test(S(0), S(1), "S(0) != S(1)");
+ test(T([T(null)]), T(null), "T([T([])]) != T([])");
+
+ // https://issues.dlang.org/show_bug.cgi?id=20323
+ static struct NoCopy
+ {
+ @disable this(this);
+ }
+
+ NoCopy n;
+ test(_d_assert_fail!(typeof(n))("!=", n, n), "NoCopy() == NoCopy()");
+
+ shared NoCopy sn;
+ test(_d_assert_fail!(typeof(sn))("!=", sn, sn), "NoCopy() == NoCopy()");
+}
+
+void testAA()
+{
+ test([1:"one"], [2: "two"], `[1: "one"] != [2: "two"]`);
+ test!"in"(1, [2: 3], "1 !in [2: 3]");
+ test!"in"("foo", ["bar": true], `"foo" !in ["bar": true]`);
+}
+
+void testAttributes() @safe pure @nogc nothrow
+{
+ int a;
+ string s = _d_assert_fail!(int, char)("==", a, 'c', 1, 'd');
+ assert(s == `(0, 'c') != (1, 'd')`);
+
+ string s2 = _d_assert_fail!int("", a);
+ assert(s2 == `0 != true`);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=20066
+void testVoidArray()
+{
+ test!"!is"([], null, (__ctfe ? "<void[] not supported>" : "[]") ~ " is `null`");
+ test!"!is"(null, null, "`null` is `null`");
+ test([1], null, "[1] != `null`");
+ test("s", null, "\"s\" != `null`");
+ test(['c'], null, "\"c\" != `null`");
+ test!"!="(null, null, "`null` == `null`");
+
+ const void[] chunk = [byte(1), byte(2), byte(3)];
+ test(chunk, null, (__ctfe ? "<void[] not supported>" : "[1, 2, 3]") ~ " != `null`");
+}
+
+void testTemporary()
+{
+ static struct Bad
+ {
+ ~this() @system {}
+ }
+
+ test!"!="(Bad(), Bad(), "Bad() == Bad()");
+}
+
+void testEnum()
+{
+ static struct UUID {
+ union
+ {
+ ubyte[] data = [1];
+ }
+ }
+
+ ubyte[] data;
+ enum ctfe = UUID();
+ test(_d_assert_fail!(ubyte[])("==", ctfe.data, data), "[1] != []");
+}
+
+void testUnary()
+{
+ test(_d_assert_fail!int("", 9), "9 != true");
+ test(_d_assert_fail!(int[])("!", [1, 2, 3]), "[1, 2, 3] == true");
+}
+
+void testTuple()
+{
+ test(_d_assert_fail("=="), "() != ()");
+ test(_d_assert_fail("!="), "() == ()");
+ test(_d_assert_fail(">="), "() < ()");
+}
+
+void testStructEquals()
+{
+ struct T {
+ bool b;
+ int i;
+ float f1 = 2.5;
+ float f2 = 0;
+ string s1 = "bar";
+ string s2;
+ }
+
+ T t1;
+ test!"!="(t1, t1, `T(false, 0, 2.5, 0, "bar", "") == T(false, 0, 2.5, 0, "bar", "")`);
+ T t2 = {s1: "bari"};
+ test(t1, t2, `T(false, 0, 2.5, 0, "bar", "") != T(false, 0, 2.5, 0, "bari", "")`);
+}
+
+void testStructEquals2()
+{
+ struct T {
+ bool b;
+ int i;
+ float f1 = 2.5;
+ float f2 = 0;
+ }
+
+ T t1;
+ test!"!="(t1, t1, `T(false, 0, 2.5, 0) == T(false, 0, 2.5, 0)`);
+ T t2 = {i: 2};
+ test(t1, t2, `T(false, 0, 2.5, 0) != T(false, 2, 2.5, 0)`);
+}
+
+void testStructEquals3()
+{
+ struct T {
+ bool b;
+ int i;
+ string s1 = "bar";
+ string s2;
+ }
+
+ T t1;
+ test!"!="(t1, t1, `T(false, 0, "bar", "") == T(false, 0, "bar", "")`);
+ T t2 = {s1: "bari"};
+ test(t1, t2, `T(false, 0, "bar", "") != T(false, 0, "bari", "")`);
+}
+
+void testStructEquals4()
+{
+ struct T {
+ float f1 = 2.5;
+ float f2 = 0;
+ string s1 = "bar";
+ string s2;
+ }
+
+ T t1;
+ test!"!="(t1, t1, `T(2.5, 0, "bar", "") == T(2.5, 0, "bar", "")`);
+ T t2 = {s1: "bari"};
+ test(t1, t2, `T(2.5, 0, "bar", "") != T(2.5, 0, "bari", "")`);
+}
+
+void testStructEquals5()
+{
+ struct T {
+ bool b;
+ int i;
+ float f2 = 0;
+ string s2;
+ }
+
+ T t1;
+ test!"!="(t1, t1, `T(false, 0, 0, "") == T(false, 0, 0, "")`);
+ T t2 = {b: true};
+ test(t1, t2, `T(false, 0, 0, "") != T(true, 0, 0, "")`);
+}
+
+void testStructEquals6()
+{
+ class C { override string toString() { return "C()"; }}
+ struct T {
+ bool b;
+ int i;
+ float f2 = 0;
+ string s2;
+ int[] arr;
+ C c;
+ }
+
+ T t1;
+ test!"!="(t1, t1, "T(false, 0, 0, \"\", [], `null`) == T(false, 0, 0, \"\", [], `null`)");
+ T t2 = {arr: [1]};
+ test(t1, t2, "T(false, 0, 0, \"\", [], `null`) != T(false, 0, 0, \"\", [1], `null`)");
+ T t3 = {c: new C()};
+ test(t1, t3, "T(false, 0, 0, \"\", [], `null`) != T(false, 0, 0, \"\", [], C())");
+}
+
+void testContextPointer()
+{
+ int i;
+ struct T
+ {
+ int j;
+ int get()
+ {
+ return i * j;
+ }
+ }
+ T t = T(1);
+ t.tupleof[$-1] = cast(void*) 0xABCD; // Deterministic context pointer
+ test(t, t, `T(1, <context>: 0xabcd) != T(1, <context>: 0xabcd)`);
+}
+
+void testExternClasses()
+{
+ {
+ extern(C++) static class Cpp
+ {
+ int a;
+ this(int a) { this.a = a; }
+ }
+ scope a = new Cpp(1);
+ scope b = new Cpp(2);
+ test(a, b, "Cpp(1) != Cpp(2)");
+ test(a, Cpp.init, "Cpp(1) != null");
+ }
+ {
+ extern(C++) static class CppToString
+ {
+ int a;
+ this(int a) { this.a = a; }
+ extern(D) string toString() const { return a == 0 ? "hello" : "world"; }
+ }
+ scope a = new CppToString(0);
+ scope b = new CppToString(1);
+ test(a, b, "hello != world");
+ }
+ if (!__ctfe)
+ {
+ extern(C++) static class Opaque;
+ Opaque null_ = null;
+ Opaque notNull = cast(Opaque) &null_;
+ test(null_, notNull, "null != <Opaque>");
+ }
+ {
+ extern(C++) static interface Stuff {}
+ scope Stuff stuff = new class Stuff {};
+ test(stuff, Stuff.init, "Stuff() != null");
+ }
+}
+
+void testShared()
+{
+ static struct Small
+ {
+ int i;
+ }
+
+ auto s1 = shared Small(1);
+ const s2 = shared Small(2);
+ test(s1, s2, "Small(1) != Small(2)");
+
+ static struct Big
+ {
+ long[10] l;
+ }
+
+ auto b1 = shared Big([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+ const b2 = shared Big();
+ test(b1, b2, "Big([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) != Big([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])");
+
+ // Sanity check: Big shouldn't be supported by atomicLoad
+ import core.atomic : atomicLoad;
+ static assert( __traits(compiles, atomicLoad(s1)));
+ static assert(!__traits(compiles, atomicLoad(b1)));
+}
+
+void testException()
+{
+ static struct MayThrow
+ {
+ int i;
+ string toString()
+ {
+ if (i == 1)
+ throw new Exception("Error");
+ return "Some message";
+ }
+ }
+
+ test(MayThrow(0), MayThrow(1), `Some message != <toString() failed: "Error", called on MayThrow(1)>`);
+}
+
+void testOverlappingFields()
+{
+ static struct S
+ {
+ union
+ {
+ double num;
+ immutable(char)[] name;
+ }
+ }
+
+ test(S(1.0), S(2.0), "S(<overlapped field>, <overlapped field>) != S(<overlapped field>, <overlapped field>)");
+
+ static struct S2
+ {
+ int valid;
+ union
+ {
+ double num;
+ immutable(char)[] name;
+ }
+ }
+
+ test(S2(4, 1.0), S2(5, 2.0), "S2(4, <overlapped field>, <overlapped field>) != S2(5, <overlapped field>, <overlapped field>)");
+
+ static struct S3
+ {
+ union
+ {
+ double num;
+ immutable(char)[] name;
+ }
+ int valid;
+ }
+ S3 a = {
+ num: 1.0,
+ valid: 8
+ };
+
+ S3 b = {
+ num: 1.0,
+ valid: 8
+ };
+ test(a, b, "S3(<overlapped field>, <overlapped field>, 8) != S3(<overlapped field>, <overlapped field>, 8)");
+}
+
+void testDestruction()
+{
+ static class Test
+ {
+ __gshared string unary, binary;
+ __gshared bool run;
+
+ ~this()
+ {
+ run = true;
+ unary = _d_assert_fail!int("", 1);
+ binary = _d_assert_fail!int("==", 1, 2);
+ }
+ }
+
+ static void createGarbage()
+ {
+ new Test();
+ new long[100];
+ }
+
+ import core.memory : GC;
+ createGarbage();
+ GC.collect();
+
+ assert(Test.run);
+ assert(Test.unary == "Assertion failed (rich formatting is disabled in finalizers)");
+ assert(Test.binary == "Assertion failed (rich formatting is disabled in finalizers)");
+}
+
+int main()
+{
+ testIntegers();
+ testIntegerComparisons();
+ testFloatingPoint();
+ testPointers();
+ testStrings();
+ testToString();
+ testArray();
+ testStruct();
+ testAA();
+ testAttributes();
+ testVoidArray();
+ testTemporary();
+ testEnum();
+ testUnary();
+ testTuple();
+ if (!__ctfe)
+ testStructEquals();
+ if (!__ctfe)
+ testStructEquals2();
+ testStructEquals3();
+ if (!__ctfe)
+ testStructEquals4();
+ if (!__ctfe)
+ testStructEquals5();
+ if (!__ctfe)
+ testStructEquals6();
+ testContextPointer();
+ testExternClasses();
+ testShared();
+ testException();
+ testOverlappingFields();
+ if (!__ctfe)
+ testDestruction();
+
+ if (!__ctfe)
+ fprintf(stderr, "success.\n");
+ return 0;
+}
+
+enum forceCTFE = main();
diff --git a/libphobos/testsuite/libphobos.exceptions/catch_in_finally.d b/libphobos/testsuite/libphobos.exceptions/catch_in_finally.d
new file mode 100644
index 00000000000..88bd73957dd
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/catch_in_finally.d
@@ -0,0 +1,191 @@
+import core.stdc.stdio : fprintf, stderr;
+
+class MyException : Exception
+{
+ this() { super(typeof(this).stringof); }
+}
+
+void throw_catch()
+{
+ try
+ {
+ throw new MyException;
+ }
+ catch (MyException)
+ {
+ }
+ catch (Exception)
+ {
+ assert(false);
+ }
+}
+
+// Test that exceptions that are entirely thrown and caught in finally blocks don't affect exception handling.
+void test1()
+{
+ try
+ {
+ try
+ {
+ throw new Exception("p");
+ }
+ finally
+ {
+ throw_catch();
+ }
+ }
+ catch (Exception e)
+ {
+ assert(e.msg == "p");
+ }
+}
+
+// Test that exceptions that are entirely thrown and caught in finally blocks don't interfere with chaining.
+void test2()
+{
+ try
+ {
+ try
+ {
+ try
+ {
+ throw new Exception("p");
+ }
+ finally
+ {
+ throw new Exception("q");
+ }
+ }
+ finally
+ {
+ throw_catch();
+ }
+ }
+ catch(Exception e)
+ {
+ assert(e.msg == "p");
+ assert(e.next.msg == "q");
+ assert(!e.next.next);
+ }
+}
+
+void test3()
+{
+ try
+ {
+ try
+ {
+ try
+ {
+ throw new Exception("p");
+ }
+ finally
+ {
+ throw_catch();
+ }
+ }
+ finally
+ {
+ throw new Exception("q");
+ }
+ }
+ catch(Exception e)
+ {
+ assert(e.msg == "p");
+ assert(e.next.msg == "q");
+ assert(!e.next.next);
+ }
+}
+
+// Test order of exception handler operations.
+void test4()
+{
+ string result;
+ void throw_catch()
+ {
+ pragma(inline, false);
+ try
+ {
+ result ~= "b";
+ throw new MyException;
+ }
+ catch (MyException)
+ {
+ result ~= "c";
+ }
+ catch (Exception)
+ {
+ assert(false);
+ }
+ }
+ try
+ {
+ try
+ {
+ result ~= "a";
+ throw new Exception("");
+ }
+ finally
+ {
+ throw_catch();
+ }
+ }
+ catch(Exception e)
+ {
+ result ~= "d";
+ }
+ assert(result == "abcd");
+}
+
+void test5()
+{
+ string result;
+ void fail()
+ {
+ result ~= "b";
+ throw new Exception("a");
+ }
+
+ void throw_catch()
+ {
+ pragma(inline, false);
+ try
+ {
+ fail();
+ }
+ catch(Exception e)
+ {
+ assert(e.msg == "a");
+ assert(!e.next);
+ result ~= "c";
+ }
+ }
+ try
+ {
+ try
+ {
+ result ~= "a";
+ throw new Exception("x");
+ }
+ finally
+ {
+ throw_catch();
+ }
+ }
+ catch (Exception e)
+ {
+ assert(e.msg == "x");
+ assert(!e.next);
+ result ~= "d";
+ }
+ assert(result == "abcd");
+}
+
+void main() {
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ fprintf(stderr, "success.\n");
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/future_message.d b/libphobos/testsuite/libphobos.exceptions/future_message.d
new file mode 100644
index 00000000000..61b10348287
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/future_message.d
@@ -0,0 +1,71 @@
+// { dg-options "-Wno-deprecated" }
+import core.stdc.stdio;
+
+// Make sure basic stuff works with future Throwable.message
+class NoMessage : Throwable
+{
+ @nogc @safe pure nothrow this(string msg, Throwable next = null)
+ {
+ super(msg, next);
+ }
+}
+
+class WithMessage : Throwable
+{
+ @nogc @safe pure nothrow this(string msg, Throwable next = null)
+ {
+ super(msg, next);
+ }
+
+ override const(char)[] message() const
+ {
+ return "I have a custom message.";
+ }
+}
+
+class WithMessageNoOverride : Throwable
+{
+ @nogc @safe pure nothrow this(string msg, Throwable next = null)
+ {
+ super(msg, next);
+ }
+
+ const(char)[] message() const
+ {
+ return "I have a custom message and no override.";
+ }
+}
+
+class WithMessageNoOverrideAndDifferentSignature : Throwable
+{
+ @nogc @safe pure nothrow this(string msg, Throwable next = null)
+ {
+ super(msg, next);
+ }
+
+ immutable(char)[] message()
+ {
+ return "I have a custom message and I'm nothing like Throwable.message.";
+ }
+}
+
+void test(Throwable t)
+{
+ try
+ {
+ throw t;
+ }
+ catch (Throwable e)
+ {
+ fprintf(stderr, "%.*s ", cast(int)e.message.length, e.message.ptr);
+ }
+}
+
+void main()
+{
+ test(new NoMessage("exception"));
+ test(new WithMessage("exception"));
+ test(new WithMessageNoOverride("exception"));
+ test(new WithMessageNoOverrideAndDifferentSignature("exception"));
+ fprintf(stderr, "\n");
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/long_backtrace_trunc.d b/libphobos/testsuite/libphobos.exceptions/long_backtrace_trunc.d
new file mode 100644
index 00000000000..3ff45e55c87
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/long_backtrace_trunc.d
@@ -0,0 +1,37 @@
+struct AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA(T) {
+struct BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB {
+struct CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC {
+struct DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD {
+struct EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE {
+struct FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF {
+struct GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG {
+struct HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH {
+ T tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt(int x) {
+ throw new Exception("test");
+ }
+}
+}
+}
+}
+}
+}
+}
+}
+
+void main() {
+ try {
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!int.
+ BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.
+ CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.
+ DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD.
+ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE.
+ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.
+ GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.
+ HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH x;
+ x.tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt(1);
+ } catch (Exception e) {
+ import core.stdc.stdio;
+ auto str = e.toString();
+ printf("%.*s\n", cast(int)str.length, str.ptr);
+ }
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/refcounted.d b/libphobos/testsuite/libphobos.exceptions/refcounted.d
new file mode 100644
index 00000000000..2b7e79bbf39
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/refcounted.d
@@ -0,0 +1,96 @@
+// { dg-options "-fpreview=dip1008" }
+class E : Exception
+{
+ static int instances;
+ this(string msg = "")
+ {
+ super(msg);
+ instances++;
+ }
+
+ ~this()
+ {
+ instances--;
+ }
+}
+
+void main()
+{
+ alias chain = Exception.chainTogether;
+
+ assert(chain(null, null) is null);
+
+ try
+ {
+ throw new E();
+ }
+ catch (E e)
+ {
+ assert(E.instances == 1);
+ assert(e.refcount == 2);
+ }
+
+ assert(E.instances == 0);
+
+ try
+ {
+ throw new E();
+ }
+ catch (E e)
+ {
+ assert(chain(null, e) is e);
+ assert(e.refcount == 2); // "Owned by e" + 1
+ }
+
+ assert(E.instances == 0);
+
+ try
+ {
+ throw new E();
+ }
+ catch (E e)
+ {
+ assert(chain(e, null) is e);
+ assert(e.refcount == 2); // "Owned by e" + 1
+ }
+
+ assert(E.instances == 0);
+
+ try
+ {
+ throw new E("first");
+ }
+ catch (E first)
+ {
+ try
+ {
+ throw new E("second");
+ }
+ catch (E second)
+ {
+ try
+ {
+ throw new E("third");
+ }
+ catch (E third)
+ {
+ assert(chain(first, second) is first);
+ assert(first.next is second);
+ assert(second.next is null);
+
+ assert(chain(first, third) is first);
+ assert(first.next is second);
+ assert(second.next is third);
+ assert(third.next is null);
+
+ assert(first.refcount == 2);
+ assert(second.refcount == 3);
+ assert(third.refcount == 3);
+ }
+ }
+
+ assert(E.instances == 3);
+ }
+
+ assert(E.instances == 0);
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions.d b/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions.d
new file mode 100644
index 00000000000..bd0c227e0f6
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions.d
@@ -0,0 +1,15 @@
+// { dg-shouldfail "uncaught exception" }
+// { dg-output "gcc.deh.*: uncaught exception" }
+// Code adapted from
+// http://arsdnet.net/this-week-in-d/2016-aug-07.html
+extern extern(C) __gshared bool rt_trapExceptions;
+extern extern(C) int _d_run_main(int, char**, void*) @system;
+
+extern(C) int main(int argc, char** argv) {
+ rt_trapExceptions = false;
+ return _d_run_main(argc, argv, &_main);
+}
+
+int _main() {
+ throw new Exception("this will abort");
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions_drt.d b/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions_drt.d
new file mode 100644
index 00000000000..fc4448cf0bf
--- /dev/null
+++ b/libphobos/testsuite/libphobos.exceptions/rt_trap_exceptions_drt.d
@@ -0,0 +1,11 @@
+// { dg-shouldfail "uncaught exception" }
+void test()
+{
+ int innerLocal = 20;
+ throw new Exception("foo");
+}
+void main(string[] args)
+{
+ string myLocal = "bar";
+ test();
+}
diff --git a/libphobos/testsuite/libphobos.exceptions/unknown_gc.d b/libphobos/testsuite/libphobos.exceptions/unknown_gc.d
index eb95aeed036..183c0f2969e 100644
--- a/libphobos/testsuite/libphobos.exceptions/unknown_gc.d
+++ b/libphobos/testsuite/libphobos.exceptions/unknown_gc.d
@@ -2,8 +2,12 @@
// { dg-options "-shared-libphobos" }
// { dg-shouldfail "unknowngc" }
// { dg-output "No GC was initialized, please recheck the name of the selected GC \\('unknowngc'\\)." }
+import core.memory;
+
extern(C) __gshared string[] rt_options = [ "gcopt=gc:unknowngc" ];
void main()
{
+ // GC initialized upon first call -> Unknown GC error is thrown
+ GC.enable();
}
diff --git a/libphobos/testsuite/libphobos.gc/attributes.d b/libphobos/testsuite/libphobos.gc/attributes.d
new file mode 100644
index 00000000000..a7acd6ce550
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/attributes.d
@@ -0,0 +1,30 @@
+import core.memory;
+
+// TODO: The following should work, but L10 (second assert) fails.
+version(none) void dotest(T) (T* ptr)
+{
+ GC.clrAttr(ptr, uint.max);
+ assert(GC.getAttr(ptr) == 0);
+
+ GC.setAttr(ptr, GC.BlkAttr.NO_MOVE);
+ assert(GC.getAttr(ptr) == GC.BlkAttr.NO_MOVE);
+
+ GC.clrAttr(ptr, GC.BlkAttr.NO_MOVE);
+ assert(GC.getAttr(ptr) == 0);
+ GC.clrAttr(ptr, GC.BlkAttr.NO_MOVE);
+ assert(GC.getAttr(ptr) == 0);
+}
+else void dotest(T) (T* ptr)
+{
+ // https://issues.dlang.org/show_bug.cgi?id=21484
+ GC.clrAttr(ptr, uint.max);
+ GC.setAttr(ptr, GC.BlkAttr.NO_MOVE);
+ GC.getAttr(ptr);
+}
+
+void main ()
+{
+ auto ptr = new int;
+ dotest!(const(int))(ptr);
+ dotest!(int)(ptr);
+}
diff --git a/libphobos/testsuite/libphobos.gc/forkgc.d b/libphobos/testsuite/libphobos.gc/forkgc.d
new file mode 100644
index 00000000000..9c18dc296f0
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/forkgc.d
@@ -0,0 +1,36 @@
+import core.memory;
+import core.stdc.stdio;
+import core.sys.posix.sys.wait;
+import core.sys.posix.unistd;
+
+void main()
+{
+ printf("[parent] Creating garbage...\n");
+ foreach (n; 0 .. 1_000)
+ new uint[10_000];
+ printf("[parent] Collecting garbage...\n");
+ GC.collect();
+ printf("[parent] Forking...\n");
+ auto i = fork();
+ if (i < 0)
+ assert(false, "Fork failed");
+ if (i == 0)
+ {
+ printf("[child] In fork.\n");
+ printf("[child] Creating garbage...\n");
+ foreach (n; 0 .. 1_000)
+ new uint[10_000];
+ printf("[child] Collecting garbage...\n");
+ GC.collect();
+ printf("[child] Exiting fork.\n");
+ }
+ else
+ {
+ printf("[parent] Waiting for fork (PID %d).\n", i);
+ int status;
+ i = waitpid(i, &status, 0);
+ printf("[parent] Fork %d exited (%d).\n", i, status);
+ if (status != 0)
+ assert(false, "child had errors");
+ }
+}
diff --git a/libphobos/testsuite/libphobos.gc/forkgc2.d b/libphobos/testsuite/libphobos.gc/forkgc2.d
new file mode 100644
index 00000000000..de7796ced72
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/forkgc2.d
@@ -0,0 +1,22 @@
+import core.stdc.stdlib : exit;
+import core.sys.posix.sys.wait : waitpid;
+import core.sys.posix.unistd : fork;
+import core.thread : Thread;
+
+void main()
+{
+ foreach (t; 0 .. 10)
+ new Thread({
+ foreach (n; 0 .. 100)
+ {
+ foreach (x; 0 .. 100)
+ new ubyte[x];
+ auto f = fork();
+ assert(f >= 0);
+ if (f == 0)
+ exit(0);
+ else
+ waitpid(f, null, 0);
+ }
+ }).start();
+}
diff --git a/libphobos/testsuite/libphobos.gc/gc.exp b/libphobos/testsuite/libphobos.gc/gc.exp
new file mode 100644
index 00000000000..cb785382f51
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/gc.exp
@@ -0,0 +1,27 @@
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Initialize dg.
+dg-init
+
+# Gather a list of all tests.
+set tests [lsort [find $srcdir/$subdir *.d]]
+
+# Main loop.
+dg-runtest $tests "" $DEFAULT_DFLAGS
+
+# All done.
+dg-finish
diff --git a/libphobos/testsuite/libphobos.gc/nocollect.d b/libphobos/testsuite/libphobos.gc/nocollect.d
new file mode 100644
index 00000000000..5df1483a284
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/nocollect.d
@@ -0,0 +1,15 @@
+// https://issues.dlang.org/show_bug.cgi?id=20567
+
+import core.memory;
+
+void main()
+{
+ auto stats = GC.profileStats();
+ assert(stats.numCollections == 0);
+
+ char[] sbuf = new char[256]; // small pool
+ char[] lbuf = new char[2049]; // large pool
+
+ stats = GC.profileStats();
+ assert(stats.numCollections == 0);
+} \ No newline at end of file
diff --git a/libphobos/testsuite/libphobos.gc/precisegc.d b/libphobos/testsuite/libphobos.gc/precisegc.d
new file mode 100644
index 00000000000..9bcaf3f4faa
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/precisegc.d
@@ -0,0 +1,126 @@
+// precise GC related:
+// https://issues.dlang.org/show_bug.cgi?id=3463
+// https://issues.dlang.org/show_bug.cgi?id=4358
+// https://issues.dlang.org/show_bug.cgi?id=9094
+// https://issues.dlang.org/show_bug.cgi?id=13801
+// https://issues.dlang.org/show_bug.cgi?id=18900
+module testgc;
+
+import core.memory;
+import core.stdc.stdio;
+
+class C
+{
+ __gshared int dtors;
+ ~this() { dtors++; }
+
+ C next;
+ size_t val;
+}
+
+struct S
+{
+ __gshared int dtors;
+ ~this() { dtors++; }
+
+ size_t val;
+ S* next;
+}
+
+struct L
+{
+ __gshared int dtors;
+ ~this() { dtors++; }
+
+ size_t[1000] data;
+ S* node;
+}
+
+struct Roots
+{
+ C c;
+ S *s;
+ L *l;
+};
+
+Roots* roots;
+size_t iroots;
+
+void init()
+{
+ roots = new Roots;
+ roots.c = new C;
+ roots.c.next = new C;
+
+ roots.s = new S;
+ roots.s.next = new S;
+
+ roots.l = new L;
+ roots.l.node = new S;
+}
+
+void verifyPointers()
+{
+ assert(C.dtors == 0);
+ assert(S.dtors == 0);
+ assert(L.dtors == 0);
+}
+
+// compiling with -gx should help eliminating false pointers on the stack
+Roots makeFalsePointers()
+{
+ roots.c.val = cast(size_t) cast(void*) roots.c.next;
+ roots.c.next = null;
+ roots.s.val = cast(size_t) cast(void*) roots.s.next;
+ roots.s.next = null;
+ roots.l.data[7] = cast(size_t) cast(void*) roots.l.node;
+ roots.l.node = null;
+
+ return Roots(null, null, null); // try to spill register contents
+}
+
+Roots moveRoot()
+{
+ iroots = cast(size_t)roots;
+ roots = null;
+
+ return Roots(null, null, null); // try to spill register contents
+}
+
+// compiling with -gx should help eliminating false pointers on the stack
+void verifyFalsePointers()
+{
+ assert(C.dtors <= 1);
+ if (C.dtors < 1) printf ("False pointers? C.dtors = %d, 1 expected\n", C.dtors);
+ assert(S.dtors <= 2);
+ if (S.dtors < 2) printf ("False pointers? S.dtors = %d, 2 expected\n", S.dtors);
+ assert(L.dtors == 0);
+}
+
+extern(C) __gshared string[] rt_options = [ "gcopt=gc:precise", "scanDataSeg=precise" ];
+
+void main()
+{
+ GC.collect(); // cleanup from unittests
+
+ init();
+ GC.collect(); // should collect nothing
+ verifyPointers();
+
+ makeFalsePointers();
+ GC.collect(); // should collect roots.c.next, roots.s.next and roots.l.node
+ verifyFalsePointers();
+
+ moveRoot();
+ GC.collect(); // should collect all
+
+ version(Windows) // precise DATA scanning only implemented on Windows
+ {
+ assert(C.dtors <= 2);
+ if (C.dtors < 2) printf ("False DATA pointers? C.dtors = %d, 2 expected\n", C.dtors);
+ assert(S.dtors <= 3);
+ if (S.dtors < 3) printf ("False DATA pointers? S.dtors = %d, 2 expected\n", S.dtors);
+ assert(L.dtors <= 1);
+ if (L.dtors < 1) printf ("False DATA pointers? L.dtors = %d, 1 expected\n", L.dtors);
+ }
+}
diff --git a/libphobos/testsuite/libphobos.gc/recoverfree.d b/libphobos/testsuite/libphobos.gc/recoverfree.d
new file mode 100644
index 00000000000..59c3b4ab597
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/recoverfree.d
@@ -0,0 +1,13 @@
+// https://issues.dlang.org/show_bug.cgi?id=20438
+import core.stdc.stdio;
+import core.memory;
+
+void main()
+{
+ auto used0 = GC.stats.usedSize;
+ void* z = GC.malloc(100);
+ GC.free(z);
+ GC.collect();
+ auto used1 = GC.stats.usedSize;
+ used1 <= used0 || assert(false);
+}
diff --git a/libphobos/testsuite/libphobos.gc/sigmaskgc.d b/libphobos/testsuite/libphobos.gc/sigmaskgc.d
new file mode 100644
index 00000000000..eb46316d18f
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/sigmaskgc.d
@@ -0,0 +1,42 @@
+
+// https://issues.dlang.org/show_bug.cgi?id=20256
+
+extern(C) __gshared string[] rt_options = [ "gcopt=parallel:1" ];
+
+void main()
+{
+ version (Posix)
+ {
+ import core.sys.posix.signal;
+ import core.sys.posix.unistd;
+ import core.thread;
+ import core.memory;
+
+ sigset_t m;
+ sigemptyset(&m);
+ sigaddset(&m, SIGHUP);
+
+ auto x = new int[](10000);
+ foreach (i; 0 .. 10000)
+ {
+ x ~= i;
+ }
+ GC.collect(); // GC create thread
+
+ sigprocmask(SIG_BLOCK, &m, null); // block SIGHUP from delivery to main thread
+
+ auto parent_pid = getpid();
+ auto child_pid = fork();
+ assert(child_pid >= 0);
+ if (child_pid == 0)
+ {
+ kill(parent_pid, SIGHUP); // send signal to parent
+ _exit(0);
+ }
+ // parent
+ Thread.sleep(100.msecs);
+ // if we are here, then GC threads didn't receive SIGHUP,
+ // otherwise whole process killed
+ _exit(0);
+ }
+}
diff --git a/libphobos/testsuite/libphobos.gc/startbackgc.d b/libphobos/testsuite/libphobos.gc/startbackgc.d
new file mode 100644
index 00000000000..bf51fbdbefe
--- /dev/null
+++ b/libphobos/testsuite/libphobos.gc/startbackgc.d
@@ -0,0 +1,22 @@
+// https://issues.dlang.org/show_bug.cgi?id=20270
+import core.sys.posix.sys.wait : waitpid;
+import core.sys.posix.unistd : fork, _exit;
+import core.thread : Thread;
+
+void main()
+{
+ foreach (t; 0 .. 10)
+ new Thread({
+ foreach (n; 0 .. 100)
+ {
+ foreach (x; 0 .. 100)
+ new ubyte[x];
+ auto f = fork();
+ assert(f >= 0);
+ if (f == 0)
+ _exit(0);
+ else
+ waitpid(f, null, 0);
+ }
+ }).start();
+}
diff --git a/libphobos/testsuite/libphobos.hash/test_hash.d b/libphobos/testsuite/libphobos.hash/test_hash.d
index b40626659b7..d0a8e5fb809 100644
--- a/libphobos/testsuite/libphobos.hash/test_hash.d
+++ b/libphobos/testsuite/libphobos.hash/test_hash.d
@@ -1,3 +1,5 @@
+// { dg-prune-output "Warning: struct HasNonConstToHash has method toHash" }
+// { dg-prune-output "HasNonConstToHash.toHash defined here:" }
void main()
{
issue19562();
@@ -8,8 +10,14 @@ void main()
issue19005();
issue19204();
issue19262();
+ issue19282();
+ issue19332(); // Support might be removed in the future!
issue19568();
issue19582();
+ issue20034();
+ issue21642();
+ issue22024();
+ issue22076();
testTypeInfoArrayGetHash1();
testTypeInfoArrayGetHash2();
pr2243();
@@ -130,6 +138,30 @@ void issue19262() nothrow
h = hashOf(aa, h);
}
+extern(C++) class Issue19282CppClass {}
+
+/// test that hashOf doesn't crash for non-null C++ objects.
+void issue19282()
+{
+ Issue19282CppClass c = new Issue19282CppClass();
+ size_t h = hashOf(c);
+ h = hashOf(c, h);
+}
+
+/// Ensure hashOf works for const struct that has non-const toHash & has all
+/// fields bitwise-hashable. (Support might be removed in the future!)
+void issue19332()
+{
+ static struct HasNonConstToHash
+ {
+ int a;
+ size_t toHash() { return a; }
+ }
+ const HasNonConstToHash val;
+ size_t h = hashOf(val);
+ h = hashOf!(const HasNonConstToHash)(val); // Ensure doesn't match more than one overload.
+}
+
/// hashOf should not unnecessarily call a struct's fields' postblits & dtors in CTFE
void issue19568()
{
@@ -189,11 +221,99 @@ void issue19582()
}
}
enum b2 = () {
- S[10] a;
- return ((const S[] a) @nogc nothrow pure @safe => toUbyte(a))(a);
+ return ((const S[] a) @nogc nothrow pure @safe => toUbyte(a))(new S[10]);
}();
}
+/// Check core.internal.hash.hashOf works with enums of non-scalar values
+void issue20034()
+{
+ enum E
+ {
+ a = "foo"
+ }
+ // should compile
+ assert(hashOf(E.a, 1));
+}
+
+/// [REG 2.084] hashOf will fail to compile for some structs/unions that recursively contain shared enums
+void issue21642() @safe nothrow pure
+{
+ enum C : char { _ = 1, }
+ union U { C c; void[0] _; }
+ shared union V { U u; }
+ cast(void) hashOf(V.init);
+ // Also test the underlying reason the above was failing.
+ import core.internal.convert : toUbyte;
+ shared C c;
+ assert(toUbyte(c) == [ubyte(1)]);
+}
+
+/// Accept enum type whose ultimate base type is a SIMD vector.
+void issue22024() @nogc nothrow pure @safe
+{
+ static if (is(__vector(float[2])))
+ {
+ enum E2 : __vector(float[2]) { a = __vector(float[2]).init, }
+ enum F2 : E2 { a = E2.init, }
+ assert(hashOf(E2.init) == hashOf(F2.init));
+ assert(hashOf(E2.init, 1) == hashOf(F2.init, 1));
+ }
+ static if (is(__vector(float[4])))
+ {
+ enum E4 : __vector(float[4]) { a = __vector(float[4]).init, }
+ enum F4 : E4 { a = E4.init, }
+ assert(hashOf(E4.init) == hashOf(F4.init));
+ assert(hashOf(E4.init, 1) == hashOf(F4.init, 1));
+ }
+}
+
+/// hashOf(S) can segfault if S.toHash is forwarded via `alias this` to a
+/// receiver which may be null.
+void issue22076()
+{
+ static struct S0 { Object a; alias a this; }
+
+ static struct S1
+ {
+ S0 a;
+ inout(S0)* b() inout nothrow { return &a; }
+ alias b this;
+ }
+
+ static struct S2
+ {
+ S0 a;
+ S1 b;
+ }
+
+ extern(C++) static class C0
+ {
+ int foo() { return 0; } // Need at least one function in vtable.
+ S0 a; alias a this;
+ }
+
+ extern(C++) static class C1
+ {
+ S1 a;
+ inout(S1)* b() inout nothrow { return &a; }
+ alias b this;
+ }
+
+ cast(void) hashOf(S0.init);
+ cast(void) hashOf(S0.init, 0);
+ cast(void) hashOf(S1.init);
+ cast(void) hashOf(S1.init, 0);
+ cast(void) hashOf(S2.init);
+ cast(void) hashOf(S2.init, 0);
+ auto c0 = new C0();
+ cast(void) hashOf(c0);
+ cast(void) hashOf(c0, 0);
+ auto c1 = new C1();
+ cast(void) hashOf(c1);
+ cast(void) hashOf(c1, 0);
+}
+
/// Tests ensure TypeInfo_Array.getHash uses element hash functions instead
/// of hashing array data.
void testTypeInfoArrayGetHash1()
@@ -300,11 +420,9 @@ void pr2243()
enum Bar vsexpr = Bar();
enum int[int] aaexpr = [99:2, 12:6, 45:4];
enum Gun eexpr = Gun.A;
- enum cdouble cexpr = 7+4i;
enum Foo[] staexpr = [Foo(), Foo(), Foo()];
enum Bar[] vsaexpr = [Bar(), Bar(), Bar()];
enum realexpr = 7.88;
- enum raexpr = [8.99L+86i, 3.12L+99i, 5.66L+12i];
enum nullexpr = null;
enum plstr = Plain();
enum plarrstr = [Plain(), Plain(), Plain()];
@@ -328,7 +446,6 @@ void pr2243()
enum h10 = vsexpr.hashOf();
enum h11 = aaexpr.hashOf();
enum h12 = eexpr.hashOf();
- enum h13 = cexpr.hashOf();
enum h14 = hashOf(new Boo);
enum h15 = staexpr.hashOf();
enum h16 = hashOf([new Boo, new Boo, new Boo]);
@@ -349,7 +466,6 @@ void pr2243()
auto h27 = ptrexpr.hashOf();
enum h28 = realexpr.hashOf();
- enum h29 = raexpr.hashOf();
enum h30 = nullexpr.hashOf();
enum h31 = plstr.hashOf();
enum h32 = plarrstr.hashOf();
@@ -367,7 +483,6 @@ void pr2243()
auto v10 = vsexpr;
auto v11 = aaexpr;
auto v12 = eexpr;
- auto v13 = cexpr;
auto v14 = new Boo;
auto v15 = staexpr;
auto v16 = [new Boo, new Boo, new Boo];
@@ -389,7 +504,6 @@ void pr2243()
auto v26 = dgexpr;
auto v27 = ptrexpr;
auto v28 = realexpr;
- auto v29 = raexpr;
//runtime hashes
auto rth1 = hashOf(v1);
@@ -404,7 +518,6 @@ void pr2243()
auto rth10 = hashOf(v10);
auto rth11 = hashOf(v11);
auto rth12 = hashOf(v12);
- auto rth13 = hashOf(v13);
auto rth14 = hashOf(v14);
auto rth15 = hashOf(v15);
auto rth16 = hashOf(v16);
@@ -422,7 +535,6 @@ void pr2243()
auto rth26 = hashOf(v26);
auto rth27 = hashOf(v27);
auto rth28 = hashOf(v28);
- auto rth29 = hashOf(v29);
auto rth31 = hashOf(v31);
auto rth32 = hashOf(v32);
@@ -440,7 +552,6 @@ void pr2243()
assert(h10 == rth10);
assert(h11 == rth11);
assert(h12 == rth12);
- assert(h13 == rth13);
assert(h14 == rth14);
assert(h15 == rth15);
assert(h16 == rth16);
@@ -455,8 +566,7 @@ void pr2243()
assert(h25 == rth25);
assert(h26 == rth26);
assert(h27 == rth27);
- assert(h28 == rth28);
- assert(h29 == rth29);*/
+ assert(h28 == rth28);*/
assert(h30 == rth30);
assert(h31 == rth31);
assert(h32 == rth32);
@@ -482,7 +592,6 @@ void pr2243()
auto tih10 = tiHashOf(v10);
auto tih11 = tiHashOf(v11);
auto tih12 = tiHashOf(v12);
- auto tih13 = tiHashOf(v13);
auto tih14 = tiHashOf(v14);
auto tih15 = tiHashOf(v15);
auto tih16 = tiHashOf(v16);
@@ -498,7 +607,6 @@ void pr2243()
auto tih26 = tiHashOf(v26);
auto tih27 = tiHashOf(v27);
auto tih28 = tiHashOf(v28);
- auto tih29 = tiHashOf(v29);
auto tih30 = tiHashOf(v30);
auto tih31 = tiHashOf(v31);
auto tih32 = tiHashOf(v32);
@@ -516,7 +624,6 @@ void pr2243()
//assert(tih10 == rth10); // need compiler-generated __xtoHash changes
assert(tih11 == rth11);
assert(tih12 == rth12);
- assert(tih13 == rth13);
assert(tih14 == rth14);
assert(tih15 == rth15);
assert(tih16 == rth16);
@@ -532,7 +639,6 @@ void pr2243()
assert(tih26 == rth26);
assert(tih27 == rth27);
assert(tih28 == rth28);
- assert(tih29 == rth29);
assert(tih30 == rth30);
assert(tih31 == rth31);
assert(tih32 == rth32);
diff --git a/libphobos/testsuite/libphobos.imports/bug18193.d b/libphobos/testsuite/libphobos.imports/bug18193.d
new file mode 100644
index 00000000000..fc8f5ca6882
--- /dev/null
+++ b/libphobos/testsuite/libphobos.imports/bug18193.d
@@ -0,0 +1,4 @@
+// { dg-options "-fversion=Shared" }
+// { dg-do compile }
+import core.runtime;
+import core.thread;
diff --git a/libphobos/testsuite/libphobos.imports/imports.exp b/libphobos/testsuite/libphobos.imports/imports.exp
new file mode 100644
index 00000000000..344e415fa53
--- /dev/null
+++ b/libphobos/testsuite/libphobos.imports/imports.exp
@@ -0,0 +1,29 @@
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+load_lib libphobos-dg.exp
+
+# Initialize dg.
+dg-init
+
+# Gather a list of all tests.
+set tests [lsort [find $srcdir/$subdir *.d]]
+
+# Main loop.
+dg-runtest $tests "" $DEFAULT_DFLAGS
+
+# All done.
+dg-finish
diff --git a/libphobos/testsuite/libphobos.init_fini/custom_gc.d b/libphobos/testsuite/libphobos.init_fini/custom_gc.d
new file mode 100644
index 00000000000..a5e2bf40356
--- /dev/null
+++ b/libphobos/testsuite/libphobos.init_fini/custom_gc.d
@@ -0,0 +1,203 @@
+import core.gc.registry;
+import core.gc.gcinterface;
+import core.stdc.stdlib;
+
+static import core.memory;
+
+extern (C) __gshared string[] rt_options = ["gcopt=gc:malloc"];
+
+extern (C) pragma(crt_constructor) void register_mygc()
+{
+ registerGCFactory("malloc", &MallocGC.initialize);
+}
+
+extern (C) void register_default_gcs()
+{
+ // remove default GCs
+}
+
+/** Simple GC that requires any pointers passed to it's API
+ to point to start of the allocation.
+ */
+class MallocGC : GC
+{
+nothrow @nogc:
+ static GC initialize()
+ {
+ import core.stdc.string : memcpy;
+
+ __gshared ubyte[__traits(classInstanceSize, MallocGC)] buf;
+
+ auto init = typeid(MallocGC).initializer();
+ assert(init.length == buf.length);
+ auto instance = cast(MallocGC) memcpy(buf.ptr, init.ptr, init.length);
+ instance.__ctor();
+ return instance;
+ }
+
+ this()
+ {
+ }
+
+ void Dtor()
+ {
+ }
+
+ void enable()
+ {
+ }
+
+ void disable()
+ {
+ }
+
+ void collect() nothrow
+ {
+ }
+
+ void collectNoStack() nothrow
+ {
+ }
+
+ void minimize() nothrow
+ {
+ }
+
+ uint getAttr(void* p) nothrow
+ {
+ return 0;
+ }
+
+ uint setAttr(void* p, uint mask) nothrow
+ {
+ return mask;
+ }
+
+ uint clrAttr(void* p, uint mask) nothrow
+ {
+ return mask;
+ }
+
+ void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow
+ {
+ return sentinelAdd(.malloc(size + sentinelSize), size);
+ }
+
+ BlkInfo qalloc(size_t size, uint bits, const scope TypeInfo ti) nothrow
+ {
+ return BlkInfo(malloc(size, bits, ti), size);
+ }
+
+ void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow
+ {
+ return sentinelAdd(.calloc(1, size + sentinelSize), size);
+ }
+
+ void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow
+ {
+ return sentinelAdd(.realloc(p - sentinelSize, size + sentinelSize), size);
+ }
+
+ size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow
+ {
+ return 0;
+ }
+
+ size_t reserve(size_t size) nothrow
+ {
+ return 0;
+ }
+
+ void free(void* p) nothrow
+ {
+ free(p - sentinelSize);
+ }
+
+ void* addrOf(void* p) nothrow
+ {
+ return p;
+ }
+
+ size_t sizeOf(void* p) nothrow
+ {
+ return query(p).size;
+ }
+
+ BlkInfo query(void* p) nothrow
+ {
+ return p ? BlkInfo(p, sentinelGet(p)) : BlkInfo.init;
+ }
+
+ core.memory.GC.Stats stats() nothrow
+ {
+ return core.memory.GC.Stats.init;
+ }
+
+ core.memory.GC.ProfileStats profileStats() nothrow
+ {
+ return typeof(return).init;
+ }
+
+ void addRoot(void* p) nothrow @nogc
+ {
+ }
+
+ void removeRoot(void* p) nothrow @nogc
+ {
+ }
+
+ @property RootIterator rootIter() @nogc
+ {
+ return null;
+ }
+
+ void addRange(void* p, size_t sz, const TypeInfo ti) nothrow @nogc
+ {
+ }
+
+ void removeRange(void* p) nothrow @nogc
+ {
+ }
+
+ @property RangeIterator rangeIter() @nogc
+ {
+ return null;
+ }
+
+ void runFinalizers(const scope void[] segment) nothrow
+ {
+ }
+
+ bool inFinalizer() nothrow
+ {
+ return false;
+ }
+
+ ulong allocatedInCurrentThread() nothrow
+ {
+ return stats().allocatedInCurrentThread;
+ }
+
+private:
+ // doesn't care for alignment
+ static void* sentinelAdd(void* p, size_t value)
+ {
+ *cast(size_t*) p = value;
+ return p + sentinelSize;
+ }
+
+ static size_t sentinelGet(void* p)
+ {
+ return *cast(size_t*)(p - sentinelSize);
+ }
+
+ enum sentinelSize = size_t.sizeof;
+}
+
+void main()
+{
+ // test array append cache
+ char[] s;
+ foreach (char c; char.min .. char.max + 1)
+ s ~= c;
+}
diff --git a/libphobos/testsuite/libphobos.init_fini/test18996.d b/libphobos/testsuite/libphobos.init_fini/test18996.d
new file mode 100644
index 00000000000..01d514cd563
--- /dev/null
+++ b/libphobos/testsuite/libphobos.init_fini/test18996.d
@@ -0,0 +1,13 @@
+// Issue https://issues.dlang.org/show_bug.cgi?id=18996
+// Array!string calls removeRange without first adding the range, but never
+// initializes the GC. The behavior of the default GC is to ignore removing
+// ranges when the range wasn't added. The ProtoGC originally would crash when
+// this happened.
+
+import core.memory;
+
+void main()
+{
+ GC.removeRange(null);
+ GC.removeRoot(null);
+}
diff --git a/libphobos/testsuite/libphobos.lifetime/large_aggregate_destroy_21097.d b/libphobos/testsuite/libphobos.lifetime/large_aggregate_destroy_21097.d
new file mode 100644
index 00000000000..bc0695ed57d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.lifetime/large_aggregate_destroy_21097.d
@@ -0,0 +1,78 @@
+// https://issues.dlang.org/show_bug.cgi?id=21097
+
+// The crucial part of the test cases is testing `destroy`. At the same time, we test
+// `core.internal.lifetime.emplaceInitializer` (which is currently called by `destroy`).
+
+enum SIZE = 10_000_000; // 10 MB should exhaust the stack on most if not all test systems.
+
+import core.internal.lifetime;
+
+void test_largestruct()
+{
+ static struct LargeStruct
+ {
+ int[SIZE/2] a1;
+ int b = 42;
+ int[SIZE/2] a2;
+ }
+ static LargeStruct s = void;
+ emplaceInitializer(s);
+ assert(s.b == 42);
+ s.b = 101;
+ destroy(s);
+ assert(s.b == 42);
+}
+
+void test_largestruct_w_opassign()
+{
+ static struct LargeStructOpAssign
+ {
+ int[SIZE/2] a1;
+ int b = 420; // non-zero init
+ int[SIZE/2] a2;
+
+ void opAssign(typeof(this)) {} // hasElaborateAssign == true
+ }
+ static LargeStructOpAssign s = void;
+ emplaceInitializer(s);
+ assert(s.b == 420);
+ s.b = 101;
+ destroy(s);
+ assert(s.b == 420);
+}
+
+void test_largearray() {
+ static struct NonZero
+ {
+ int i = 123;
+ }
+ static NonZero[SIZE] s = void;
+ emplaceInitializer(s);
+ assert(s[SIZE/2] == NonZero.init);
+ s[10] = NonZero(101);
+ destroy(s);
+ assert(s[10] == NonZero.init);
+}
+
+void test_largearray_w_opassign() {
+ static struct NonZeroWithOpAssign
+ {
+ int i = 123;
+ void opAssign(typeof(this)) {} // hasElaborateAssign == true
+ }
+ static NonZeroWithOpAssign[SIZE] s = void;
+ emplaceInitializer(s);
+ assert(s[SIZE/2] == NonZeroWithOpAssign.init);
+ s[10] = NonZeroWithOpAssign(101);
+ destroy(s);
+ assert(s[10] == NonZeroWithOpAssign.init);
+}
+
+int main()
+{
+ test_largestruct();
+ test_largestruct_w_opassign();
+ test_largearray();
+ test_largearray_w_opassign();
+ return 0;
+}
diff --git a/libphobos/testsuite/libphobos.lifetime/lifetime.exp b/libphobos/testsuite/libphobos.lifetime/lifetime.exp
new file mode 100644
index 00000000000..cb785382f51
--- /dev/null
+++ b/libphobos/testsuite/libphobos.lifetime/lifetime.exp
@@ -0,0 +1,27 @@
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Initialize dg.
+dg-init
+
+# Gather a list of all tests.
+set tests [lsort [find $srcdir/$subdir *.d]]
+
+# Main loop.
+dg-runtest $tests "" $DEFAULT_DFLAGS
+
+# All done.
+dg-finish
diff --git a/libphobos/testsuite/libphobos.phobos/phobos.exp b/libphobos/testsuite/libphobos.phobos/phobos.exp
index 937849eebd7..84e989837fe 100644
--- a/libphobos/testsuite/libphobos.phobos/phobos.exp
+++ b/libphobos/testsuite/libphobos.phobos/phobos.exp
@@ -27,7 +27,7 @@ if { ![is-effective-target d_runtime_has_std_library] } {
# Gather a list of all tests.
set tests [lsort [filter_libphobos_unittests [find $srcdir/../src "*.d"]]]
-set version_flags ""
+set version_flags "-fversion=StdUnittest"
if { [is-effective-target linux_pre_2639] } {
lappend version_flags "-fversion=Linux_Pre_2639"
diff --git a/libphobos/testsuite/libphobos.phobos_shared/phobos_shared.exp b/libphobos/testsuite/libphobos.phobos_shared/phobos_shared.exp
index 8498522d4d4..b8f8e42f612 100644
--- a/libphobos/testsuite/libphobos.phobos_shared/phobos_shared.exp
+++ b/libphobos/testsuite/libphobos.phobos_shared/phobos_shared.exp
@@ -27,7 +27,7 @@ if { ![is-effective-target d_runtime_has_std_library] } {
# Gather a list of all tests.
set tests [lsort [filter_libphobos_unittests [find $srcdir/../src "*.d"]]]
-set version_flags ""
+set version_flags "-fversion=StdUnittest"
if { [is-effective-target linux_pre_2639] } {
lappend version_flags "-fversion=Linux_Pre_2639"
diff --git a/libphobos/testsuite/libphobos.shared/host.c b/libphobos/testsuite/libphobos.shared/host.c
index 81e896aa3d8..395ad0c3f55 100644
--- a/libphobos/testsuite/libphobos.shared/host.c
+++ b/libphobos/testsuite/libphobos.shared/host.c
@@ -10,6 +10,11 @@ int main(int argc, char* argv[])
void *druntime = dlopen(argv[1], RTLD_LAZY); // load druntime
assert(druntime);
#endif
+#if defined(__DragonFly__)
+ // workaround for Bugzilla 14824
+ void *druntime = dlopen(argv[1], RTLD_LAZY); // load druntime
+ assert(druntime);
+#endif
const size_t pathlen = strrchr(argv[0], '/') - argv[0] + 1;
char *name = malloc(pathlen + sizeof("plugin1.so"));
@@ -56,5 +61,8 @@ int main(int argc, char* argv[])
#if defined(__FreeBSD__)
dlclose(druntime);
#endif
+#if defined(__DragonFly__)
+ dlclose(druntime);
+#endif
return EXIT_SUCCESS;
}
diff --git a/libphobos/testsuite/libphobos.shared/link_mod_collision.d b/libphobos/testsuite/libphobos.shared/link_mod_collision.d
deleted file mode 100644
index 9c3d1c7b235..00000000000
--- a/libphobos/testsuite/libphobos.shared/link_mod_collision.d
+++ /dev/null
@@ -1,5 +0,0 @@
-module lib; // module collides with lib.so
-
-void main()
-{
-}
diff --git a/libphobos/testsuite/libphobos.shared/load.d b/libphobos/testsuite/libphobos.shared/load.d
index 5a2dd01b778..0d3ffa65d6e 100644
--- a/libphobos/testsuite/libphobos.shared/load.d
+++ b/libphobos/testsuite/libphobos.shared/load.d
@@ -2,7 +2,6 @@ import core.runtime;
import core.stdc.stdio;
import core.stdc.string;
import core.thread;
-
import core.sys.posix.dlfcn;
version (DragonFlyBSD) import core.sys.dragonflybsd.dlfcn : RTLD_NOLOAD;
diff --git a/libphobos/testsuite/libphobos.shared/load_13414.d b/libphobos/testsuite/libphobos.shared/load_13414.d
index f7cbf45190a..047d5079937 100644
--- a/libphobos/testsuite/libphobos.shared/load_13414.d
+++ b/libphobos/testsuite/libphobos.shared/load_13414.d
@@ -16,8 +16,17 @@ void runTest(string name)
*cast(void function()*).dlsym(h, "_D9lib_1341420sharedStaticDtorHookOPFZv") = &sharedStaticDtorHook;
Runtime.unloadLibrary(h);
- assert(tlsDtor == 1);
- assert(dtor == 1);
+ version (CRuntime_Musl)
+ {
+ // On Musl, unloadLibrary is a no-op because dlclose is a no-op
+ assert(tlsDtor == 0);
+ assert(dtor == 0);
+ }
+ else
+ {
+ assert(tlsDtor == 1);
+ assert(dtor == 1);
+ }
}
void main(string[] args)
diff --git a/libphobos/testsuite/libphobos.shared/load_mod_collision.d b/libphobos/testsuite/libphobos.shared/load_mod_collision.d
deleted file mode 100644
index 64243d4b7bb..00000000000
--- a/libphobos/testsuite/libphobos.shared/load_mod_collision.d
+++ /dev/null
@@ -1,14 +0,0 @@
-module lib; // module collides with lib.so
-
-import core.runtime;
-import core.stdc.stdio;
-import core.stdc.string;
-import core.sys.posix.dlfcn;
-
-void main(string[] args)
-{
- auto name = args[0] ~ '\0';
- const pathlen = strrchr(name.ptr, '/') - name.ptr + 1;
- name = name[0 .. pathlen] ~ "lib.so";
- auto lib = Runtime.loadLibrary(name);
-}
diff --git a/libphobos/testsuite/libphobos.thread/external_threads.d b/libphobos/testsuite/libphobos.thread/external_threads.d
new file mode 100644
index 00000000000..9c98a3fa13d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.thread/external_threads.d
@@ -0,0 +1,50 @@
+import core.sys.posix.pthread;
+import core.memory;
+import core.thread;
+
+extern (C) void rt_moduleTlsCtor();
+extern (C) void rt_moduleTlsDtor();
+
+extern(C)
+void* entry_point1(void*)
+{
+ // try collecting - GC must ignore this call because this thread
+ // is not registered in runtime
+ GC.collect();
+ return null;
+}
+
+extern(C)
+void* entry_point2(void*)
+{
+ // This thread gets registered in druntime, does some work and gets
+ // unregistered to be cleaned up manually
+ thread_attachThis();
+ rt_moduleTlsCtor();
+
+ auto x = new int[10];
+
+ rt_moduleTlsDtor();
+ thread_detachThis();
+ return null;
+}
+
+void main()
+{
+ // allocate some garbage
+ auto x = new int[1000];
+
+ {
+ pthread_t thread;
+ auto status = pthread_create(&thread, null, &entry_point1, null);
+ assert(status == 0);
+ pthread_join(thread, null);
+ }
+
+ {
+ pthread_t thread;
+ auto status = pthread_create(&thread, null, &entry_point2, null);
+ assert(status == 0);
+ pthread_join(thread, null);
+ }
+}
diff --git a/libphobos/testsuite/libphobos.thread/fiber_guard_page.d b/libphobos/testsuite/libphobos.thread/fiber_guard_page.d
index ca54a19857f..dbdd0f9d08d 100644
--- a/libphobos/testsuite/libphobos.thread/fiber_guard_page.d
+++ b/libphobos/testsuite/libphobos.thread/fiber_guard_page.d
@@ -4,6 +4,9 @@ import core.thread;
import core.sys.posix.signal;
import core.sys.posix.sys.mman;
+version (LDC) import ldc.attributes;
+else struct optStrategy { string a; }
+
// this should be true for most architectures
// (taken from core.thread)
version (GNU_StackGrowsDown)
@@ -12,6 +15,7 @@ version (GNU_StackGrowsDown)
enum stackSize = MINSIGSTKSZ;
// Simple method that causes a stack overflow
+@optStrategy("none")
void stackMethod()
{
// Over the stack size, so it overflows the stack
diff --git a/libphobos/testsuite/libphobos.thread/join_detach.d b/libphobos/testsuite/libphobos.thread/join_detach.d
new file mode 100644
index 00000000000..f1515190171
--- /dev/null
+++ b/libphobos/testsuite/libphobos.thread/join_detach.d
@@ -0,0 +1,20 @@
+import core.thread;
+import core.sync.semaphore;
+
+__gshared Semaphore sem;
+
+void thread_main ()
+{
+ sem.notify();
+}
+
+void main()
+{
+ auto th = new Thread(&thread_main);
+ sem = new Semaphore();
+ th.start();
+ sem.wait();
+ while (th.isRunning()) {}
+ destroy(th); // force detach
+ th.join();
+}
diff --git a/libphobos/testsuite/libphobos.thread/test_import.d b/libphobos/testsuite/libphobos.thread/test_import.d
new file mode 100644
index 00000000000..dfa0487d916
--- /dev/null
+++ b/libphobos/testsuite/libphobos.thread/test_import.d
@@ -0,0 +1,7 @@
+// https://issues.dlang.org/show_bug.cgi?id=20447
+void main()
+{
+ import core.thread;
+ int[] x;
+ auto b = x.dup;
+}
diff --git a/libphobos/testsuite/libphobos.thread/tlsgc_sections.d b/libphobos/testsuite/libphobos.thread/tlsgc_sections.d
index 1421d926a38..1bd3f26cffc 100644
--- a/libphobos/testsuite/libphobos.thread/tlsgc_sections.d
+++ b/libphobos/testsuite/libphobos.thread/tlsgc_sections.d
@@ -1,39 +1,70 @@
-final class Class
+import core.memory;
+import core.sync.condition;
+import core.sync.mutex;
+import core.thread;
+
+__gshared Condition g_cond;
+__gshared Mutex g_mutex;
+__gshared int g_step = 0;
+
+class C
{
- // This gets triggered although the instance always stays referenced.
~this()
{
import core.stdc.stdlib;
- abort();
+ abort(); // this gets triggered although the instance always stays referenced
}
}
-Class obj;
+C c;
static this()
{
- obj = new Class;
+ c = new C;
}
static ~this()
{
- // Free without destruction to avoid triggering abort()
import core.memory;
- GC.free(cast(void*)obj);
+ GC.free(cast(void*)c); // free without destruction to avoid triggering abort()
}
-void doit()
+void test()
{
- foreach (i; 0 .. 10_000)
- new ubyte[](100_000);
+ assert(c !is null);
+
+ // notify the main thread of the finished initialization
+ synchronized (g_mutex) g_step = 1;
+ g_cond.notifyAll();
+
+ // wait until the GC collection is done
+ synchronized (g_mutex) {
+ while (g_step != 2)
+ g_cond.wait();
+ }
}
+
void main()
{
- import core.thread;
- auto t = new Thread(&doit);
- t.start();
+ g_mutex = new Mutex;
+ g_cond = new Condition(g_mutex);
+
+ auto th = new Thread(&test);
+ th.start();
+
+ // wait for thread to be fully initialized
+ synchronized (g_mutex) {
+ while (g_step != 1)
+ g_cond.wait();
+ }
+
+ // this causes the other thread's C instance to be reaped with the bug present
+ GC.collect();
+
+ // allow the thread to shut down
+ synchronized (g_mutex) g_step = 2;
+ g_cond.notifyAll();
- // This triggers the GC that frees the still referenced Class instance.
- doit();
+ th.join();
}
diff --git a/libphobos/testsuite/libphobos.thread/tlsstack.d b/libphobos/testsuite/libphobos.thread/tlsstack.d
new file mode 100644
index 00000000000..dbd93213bfe
--- /dev/null
+++ b/libphobos/testsuite/libphobos.thread/tlsstack.d
@@ -0,0 +1,38 @@
+module core.thread.test; // needs access to getStackTop()/getStackBottom()
+
+import core.stdc.stdio;
+import core.thread;
+
+ubyte[16384] data;
+
+void showThreadInfo() nothrow
+{
+ try
+ {
+ auto top = getStackTop();
+ auto bottom = getStackBottom();
+ printf("tlsdata: %p\n", data.ptr);
+ printf("stack top: %p\n", getStackTop());
+ printf("stack bottom:%p\n", getStackBottom());
+ printf("used stack: %lld\n", cast(ulong)(bottom - top));
+ }
+ catch(Exception e)
+ {
+ assert(false, e.msg);
+ }
+}
+
+void main()
+{
+ printf("### main\n");
+ showThreadInfo();
+
+ printf("### thread\n");
+ auto th = new Thread(&showThreadInfo, 16384);
+ th.start();
+ th.join();
+
+ printf("### lowlevel thread\n");
+ auto llth = createLowLevelThread(() { showThreadInfo(); });
+ joinLowLevelThread(llth);
+}
diff --git a/libphobos/testsuite/libphobos.typeinfo/enum_.d b/libphobos/testsuite/libphobos.typeinfo/enum_.d
new file mode 100644
index 00000000000..58cfbe34d60
--- /dev/null
+++ b/libphobos/testsuite/libphobos.typeinfo/enum_.d
@@ -0,0 +1,21 @@
+// https://issues.dlang.org/show_bug.cgi?id=21441
+
+int dtorCount;
+int postblitCount;
+
+struct S
+{
+ this(this) { ++postblitCount; }
+ ~this() { ++dtorCount; }
+}
+
+enum E : S { _ = S.init }
+
+void main()
+{
+ E e;
+ typeid(e).destroy(&e);
+ assert(dtorCount == 1);
+ typeid(e).postblit(&e);
+ assert(postblitCount == 1);
+}
diff --git a/libphobos/testsuite/libphobos.typeinfo/isbaseof.d b/libphobos/testsuite/libphobos.typeinfo/isbaseof.d
new file mode 100644
index 00000000000..26dd5a66e3e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.typeinfo/isbaseof.d
@@ -0,0 +1,46 @@
+// https://issues.dlang.org/show_bug.cgi?id=20178
+
+interface I {}
+interface J : I {}
+interface K(T) {}
+class C1 : I {}
+class C2 : C1 {}
+class C3 : J {}
+class C4(T) : C3, K!T {}
+class C5(T) : C4!T {}
+
+void main() @nogc nothrow pure @safe
+{
+ assert(typeid(C1).isBaseOf(typeid(C1)));
+ assert(typeid(C1).isBaseOf(typeid(C2)));
+
+ assert(!typeid(C2).isBaseOf(typeid(C1)));
+ assert(typeid(C2).isBaseOf(typeid(C2)));
+
+ assert(!typeid(C1).isBaseOf(typeid(Object)));
+ assert(!typeid(C2).isBaseOf(typeid(Object)));
+ assert(typeid(Object).isBaseOf(typeid(C1)));
+ assert(typeid(Object).isBaseOf(typeid(C2)));
+
+ assert(typeid(I).isBaseOf(typeid(I)));
+ assert(typeid(I).isBaseOf(typeid(J)));
+ assert(typeid(I).isBaseOf(typeid(C1)));
+ assert(typeid(I).isBaseOf(typeid(C2)));
+ assert(typeid(I).isBaseOf(typeid(C3)));
+ assert(!typeid(I).isBaseOf(typeid(Object)));
+
+ assert(!typeid(J).isBaseOf(typeid(I)));
+ assert(typeid(J).isBaseOf(typeid(J)));
+ assert(!typeid(J).isBaseOf(typeid(C1)));
+ assert(!typeid(J).isBaseOf(typeid(C2)));
+ assert(typeid(J).isBaseOf(typeid(C3)));
+ assert(!typeid(J).isBaseOf(typeid(Object)));
+
+ assert(typeid(C4!int).isBaseOf(typeid(C5!int)));
+ assert(typeid(K!int).isBaseOf(typeid(C5!int)));
+ assert(!typeid(C4!Object).isBaseOf(typeid(C5!int)));
+ assert(!typeid(K!Object).isBaseOf(typeid(C5!int)));
+
+ static assert(!__traits(compiles, TypeInfo.init.isBaseOf(typeid(C1))));
+ static assert(!__traits(compiles, typeid(C1).isBaseOf(TypeInfo.init)));
+}
diff --git a/libphobos/testsuite/libphobos.unittest/customhandler.d b/libphobos/testsuite/libphobos.unittest/customhandler.d
new file mode 100644
index 00000000000..f5a04350d9c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.unittest/customhandler.d
@@ -0,0 +1,21 @@
+import core.runtime;
+
+UnitTestResult customModuleUnitTester()
+{
+ version(GoodTests) return UnitTestResult(100, 100, false, true);
+ version(FailedTests) return UnitTestResult(100, 0, false, true);
+ version(NoTests) return UnitTestResult(0, 0, true, false);
+ version(FailNoPrintout) return UnitTestResult(100, 0, false, false);
+ version(PassNoPrintout) return UnitTestResult(100, 100, false, false);
+}
+
+shared static this()
+{
+ Runtime.extendedModuleUnitTester = &customModuleUnitTester;
+}
+
+void main()
+{
+ import core.stdc.stdio;
+ fprintf(stderr, "main\n");
+}
diff --git a/libphobos/testsuite/libphobos.unittest/unittest.exp b/libphobos/testsuite/libphobos.unittest/unittest.exp
new file mode 100644
index 00000000000..ba2fc6e32af
--- /dev/null
+++ b/libphobos/testsuite/libphobos.unittest/unittest.exp
@@ -0,0 +1,53 @@
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+load_lib libphobos-dg.exp
+
+set dg-output-text [list]
+
+# Arguments to pass to the compiler, expected output, and return code.
+set unit_test_list [list \
+ { "-fversion=PassNoPrintout" 0 } \
+ { "-fversion=GoodTests" 0 } \
+ { "-fversion=FailNoPrintout" 1 } \
+ { "-fversion=FailedTests" 1 } \
+ { "-fversion=NoTests" 0 } \
+]
+
+# Initialize dg.
+dg-init
+
+# Gather a list of all tests.
+set tests [lsort [find $srcdir/$subdir *.d]]
+
+# Main loop.
+foreach unit_test $unit_test_list {
+ # The version flags to build the program with.
+ set test_flags [lindex $unit_test 0]
+
+ # Whether the program is expected to fail.
+ set expected_fail [lindex $unit_test 1]
+
+ foreach test $tests {
+ set shouldfail $expected_fail
+ dg-test $test "" $test_flags
+ }
+
+ set shouldfail 0
+}
+
+# All done.
+dg-finish
diff --git a/libphobos/testsuite/testsuite_flags.in b/libphobos/testsuite/testsuite_flags.in
index bafd5ad4502..93bf7cbfba2 100755
--- a/libphobos/testsuite/testsuite_flags.in
+++ b/libphobos/testsuite/testsuite_flags.in
@@ -29,7 +29,7 @@ case ${query} in
--gdcflags)
GDCFLAGS_default="-fmessage-length=0 -fno-show-column"
GDCFLAGS_config="@WARN_DFLAGS@ @GDCFLAGS@ @CET_DFLAGS@
- @phobos_compiler_shared_flag@ -fno-release -funittest"
+ @phobos_compiler_shared_flag@ -fpreview=dip1000 -fno-release -funittest"
echo ${GDCFLAGS_default} ${GDCFLAGS_config}
;;
--gdcpaths)